1760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin/*
2760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin * Copyright 2008 Cavium Networks
3760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin *
4760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin * This file is free software; you can redistribute it and/or modify
5760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin * it under the terms of the GNU General Public License, Version 2, as
6760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin * published by the Free Software Foundation.
7760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin */
8760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin
9760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin#include <linux/platform_device.h>
10760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin#include <linux/atomic.h>
11760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin#include <mach/cns3xxx.h>
12760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin#include <mach/pm.h>
13760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin
14760efe6910d5743084b586d3d0a3b65aea96fb2fMac Linstatic int __devinit
15760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lincns3xxx_ohci_start(struct usb_hcd *hcd)
16760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin{
17760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin	struct ohci_hcd	*ohci = hcd_to_ohci(hcd);
18760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin	int ret;
19760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin
20760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin	/*
21760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin	 * EHCI and OHCI share the same clock and power,
22760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin	 * resetting twice would cause the 1st controller been reset.
23760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin	 * Therefore only do power up  at the first up device, and
24760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin	 * power down at the last down device.
25760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin	 *
26760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin	 * Set USB AHB INCR length to 16
27760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin	 */
28760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin	if (atomic_inc_return(&usb_pwr_ref) == 1) {
29760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin		cns3xxx_pwr_power_up(1 << PM_PLL_HM_PD_CTRL_REG_OFFSET_PLL_USB);
30760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin		cns3xxx_pwr_clk_en(1 << PM_CLK_GATE_REG_OFFSET_USB_HOST);
31760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin		cns3xxx_pwr_soft_rst(1 << PM_SOFT_RST_REG_OFFST_USB_HOST);
32760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin		__raw_writel((__raw_readl(MISC_CHIP_CONFIG_REG) | (0X2 << 24)),
33760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin			MISC_CHIP_CONFIG_REG);
34760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin	}
35760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin
36760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin	ret = ohci_init(ohci);
37760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin	if (ret < 0)
38760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin		return ret;
39760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin
40760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin	ohci->num_ports = 1;
41760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin
42760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin	ret = ohci_run(ohci);
43760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin	if (ret < 0) {
44760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin		err("can't start %s", hcd->self.bus_name);
45760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin		ohci_stop(hcd);
46760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin		return ret;
47760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin	}
48760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin	return 0;
49760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin}
50760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin
51760efe6910d5743084b586d3d0a3b65aea96fb2fMac Linstatic const struct hc_driver cns3xxx_ohci_hc_driver = {
52760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin	.description		= hcd_name,
53760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin	.product_desc		= "CNS3XXX OHCI Host controller",
54760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin	.hcd_priv_size		= sizeof(struct ohci_hcd),
55760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin	.irq			= ohci_irq,
56760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin	.flags			= HCD_USB11 | HCD_MEMORY,
57760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin	.start			= cns3xxx_ohci_start,
58760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin	.stop			= ohci_stop,
59760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin	.shutdown		= ohci_shutdown,
60760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin	.urb_enqueue		= ohci_urb_enqueue,
61760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin	.urb_dequeue		= ohci_urb_dequeue,
62760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin	.endpoint_disable	= ohci_endpoint_disable,
63760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin	.get_frame_number	= ohci_get_frame,
64760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin	.hub_status_data	= ohci_hub_status_data,
65760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin	.hub_control		= ohci_hub_control,
66760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin#ifdef CONFIG_PM
67760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin	.bus_suspend		= ohci_bus_suspend,
68760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin	.bus_resume		= ohci_bus_resume,
69760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin#endif
70760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin	.start_port_reset	= ohci_start_port_reset,
71760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin};
72760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin
73760efe6910d5743084b586d3d0a3b65aea96fb2fMac Linstatic int cns3xxx_ohci_probe(struct platform_device *pdev)
74760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin{
75760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin	struct device *dev = &pdev->dev;
76760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin	struct usb_hcd *hcd;
77760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin	const struct hc_driver *driver = &cns3xxx_ohci_hc_driver;
78760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin	struct resource *res;
79760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin	int irq;
80760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin	int retval;
81760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin
82760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin	if (usb_disabled())
83760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin		return -ENODEV;
84760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin
85760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin	res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
86760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin	if (!res) {
87760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin		dev_err(dev, "Found HC with no IRQ.\n");
88760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin		return -ENODEV;
89760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin	}
90760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin	irq = res->start;
91760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin
92760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin	hcd = usb_create_hcd(driver, dev, dev_name(dev));
93760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin	if (!hcd)
94760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin		return -ENOMEM;
95760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin
96760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
97760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin	if (!res) {
98760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin		dev_err(dev, "Found HC with no register addr.\n");
99760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin		retval = -ENODEV;
100760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin		goto err1;
101760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin	}
102760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin	hcd->rsrc_start = res->start;
10328f65c11f2ffb3957259dece647a24f8ad2e241bJoe Perches	hcd->rsrc_len = resource_size(res);
104760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin
105760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin	if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len,
106760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin			driver->description)) {
107760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin		dev_dbg(dev, "controller already in use\n");
108760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin		retval = -EBUSY;
109760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin		goto err1;
110760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin	}
111760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin
112760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin	hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
113760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin	if (!hcd->regs) {
114760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin		dev_dbg(dev, "error mapping memory\n");
115760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin		retval = -EFAULT;
116760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin		goto err2;
117760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin	}
118760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin
119760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin	ohci_hcd_init(hcd_to_ohci(hcd));
120760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin
121760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin	retval = usb_add_hcd(hcd, irq, IRQF_SHARED);
122760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin	if (retval == 0)
123760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin		return retval;
124760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin
125760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin	iounmap(hcd->regs);
126760efe6910d5743084b586d3d0a3b65aea96fb2fMac Linerr2:
127760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin	release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
128760efe6910d5743084b586d3d0a3b65aea96fb2fMac Linerr1:
129760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin	usb_put_hcd(hcd);
130760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin	return retval;
131760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin}
132760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin
133760efe6910d5743084b586d3d0a3b65aea96fb2fMac Linstatic int cns3xxx_ohci_remove(struct platform_device *pdev)
134760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin{
135760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin	struct usb_hcd *hcd = platform_get_drvdata(pdev);
136760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin
137760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin	usb_remove_hcd(hcd);
138760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin	iounmap(hcd->regs);
139760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin	release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
140760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin
141760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin	/*
142760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin	 * EHCI and OHCI share the same clock and power,
143760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin	 * resetting twice would cause the 1st controller been reset.
144760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin	 * Therefore only do power up  at the first up device, and
145760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin	 * power down at the last down device.
146760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin	 */
147760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin	if (atomic_dec_return(&usb_pwr_ref) == 0)
148760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin		cns3xxx_pwr_clk_dis(1 << PM_CLK_GATE_REG_OFFSET_USB_HOST);
149760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin
150760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin	usb_put_hcd(hcd);
151760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin
152760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin	platform_set_drvdata(pdev, NULL);
153760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin
154760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin	return 0;
155760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin}
156760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin
157760efe6910d5743084b586d3d0a3b65aea96fb2fMac LinMODULE_ALIAS("platform:cns3xxx-ohci");
158760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin
159760efe6910d5743084b586d3d0a3b65aea96fb2fMac Linstatic struct platform_driver ohci_hcd_cns3xxx_driver = {
160760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin	.probe = cns3xxx_ohci_probe,
161760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin	.remove = cns3xxx_ohci_remove,
162760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin	.driver = {
163760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin		.name = "cns3xxx-ohci",
164760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin	},
165760efe6910d5743084b586d3d0a3b65aea96fb2fMac Lin};
166