blob: 2c9f233047be3cf44317eb3dfe76995f934ae800 [file] [log] [blame]
Mac Lin760efe62010-11-25 23:58:00 +08001/*
2 * Copyright 2008 Cavium Networks
3 *
4 * This file is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License, Version 2, as
6 * published by the Free Software Foundation.
7 */
8
9#include <linux/platform_device.h>
10#include <linux/atomic.h>
11#include <mach/cns3xxx.h>
12#include <mach/pm.h>
13
14static int __devinit
15cns3xxx_ohci_start(struct usb_hcd *hcd)
16{
17 struct ohci_hcd *ohci = hcd_to_ohci(hcd);
18 int ret;
19
20 /*
21 * EHCI and OHCI share the same clock and power,
22 * resetting twice would cause the 1st controller been reset.
23 * Therefore only do power up at the first up device, and
24 * power down at the last down device.
25 *
26 * Set USB AHB INCR length to 16
27 */
28 if (atomic_inc_return(&usb_pwr_ref) == 1) {
29 cns3xxx_pwr_power_up(1 << PM_PLL_HM_PD_CTRL_REG_OFFSET_PLL_USB);
30 cns3xxx_pwr_clk_en(1 << PM_CLK_GATE_REG_OFFSET_USB_HOST);
31 cns3xxx_pwr_soft_rst(1 << PM_SOFT_RST_REG_OFFST_USB_HOST);
32 __raw_writel((__raw_readl(MISC_CHIP_CONFIG_REG) | (0X2 << 24)),
33 MISC_CHIP_CONFIG_REG);
34 }
35
36 ret = ohci_init(ohci);
37 if (ret < 0)
38 return ret;
39
40 ohci->num_ports = 1;
41
42 ret = ohci_run(ohci);
43 if (ret < 0) {
Greg Kroah-Hartmanbd0c0922012-04-27 11:24:40 -070044 dev_err(hcd->self.controller, "can't start %s\n",
45 hcd->self.bus_name);
Mac Lin760efe62010-11-25 23:58:00 +080046 ohci_stop(hcd);
47 return ret;
48 }
49 return 0;
50}
51
52static const struct hc_driver cns3xxx_ohci_hc_driver = {
53 .description = hcd_name,
54 .product_desc = "CNS3XXX OHCI Host controller",
55 .hcd_priv_size = sizeof(struct ohci_hcd),
56 .irq = ohci_irq,
57 .flags = HCD_USB11 | HCD_MEMORY,
58 .start = cns3xxx_ohci_start,
59 .stop = ohci_stop,
60 .shutdown = ohci_shutdown,
61 .urb_enqueue = ohci_urb_enqueue,
62 .urb_dequeue = ohci_urb_dequeue,
63 .endpoint_disable = ohci_endpoint_disable,
64 .get_frame_number = ohci_get_frame,
65 .hub_status_data = ohci_hub_status_data,
66 .hub_control = ohci_hub_control,
67#ifdef CONFIG_PM
68 .bus_suspend = ohci_bus_suspend,
69 .bus_resume = ohci_bus_resume,
70#endif
71 .start_port_reset = ohci_start_port_reset,
72};
73
74static int cns3xxx_ohci_probe(struct platform_device *pdev)
75{
76 struct device *dev = &pdev->dev;
77 struct usb_hcd *hcd;
78 const struct hc_driver *driver = &cns3xxx_ohci_hc_driver;
79 struct resource *res;
80 int irq;
81 int retval;
82
83 if (usb_disabled())
84 return -ENODEV;
85
86 res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
87 if (!res) {
88 dev_err(dev, "Found HC with no IRQ.\n");
89 return -ENODEV;
90 }
91 irq = res->start;
92
93 hcd = usb_create_hcd(driver, dev, dev_name(dev));
94 if (!hcd)
95 return -ENOMEM;
96
97 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
98 if (!res) {
99 dev_err(dev, "Found HC with no register addr.\n");
100 retval = -ENODEV;
101 goto err1;
102 }
103 hcd->rsrc_start = res->start;
Joe Perches28f65c112011-06-09 09:13:32 -0700104 hcd->rsrc_len = resource_size(res);
Mac Lin760efe62010-11-25 23:58:00 +0800105
106 if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len,
107 driver->description)) {
108 dev_dbg(dev, "controller already in use\n");
109 retval = -EBUSY;
110 goto err1;
111 }
112
113 hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
114 if (!hcd->regs) {
115 dev_dbg(dev, "error mapping memory\n");
116 retval = -EFAULT;
117 goto err2;
118 }
119
120 ohci_hcd_init(hcd_to_ohci(hcd));
121
122 retval = usb_add_hcd(hcd, irq, IRQF_SHARED);
123 if (retval == 0)
124 return retval;
125
126 iounmap(hcd->regs);
127err2:
128 release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
129err1:
130 usb_put_hcd(hcd);
131 return retval;
132}
133
134static int cns3xxx_ohci_remove(struct platform_device *pdev)
135{
136 struct usb_hcd *hcd = platform_get_drvdata(pdev);
137
138 usb_remove_hcd(hcd);
139 iounmap(hcd->regs);
140 release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
141
142 /*
143 * EHCI and OHCI share the same clock and power,
144 * resetting twice would cause the 1st controller been reset.
145 * Therefore only do power up at the first up device, and
146 * power down at the last down device.
147 */
148 if (atomic_dec_return(&usb_pwr_ref) == 0)
149 cns3xxx_pwr_clk_dis(1 << PM_CLK_GATE_REG_OFFSET_USB_HOST);
150
151 usb_put_hcd(hcd);
152
153 platform_set_drvdata(pdev, NULL);
154
155 return 0;
156}
157
158MODULE_ALIAS("platform:cns3xxx-ohci");
159
160static struct platform_driver ohci_hcd_cns3xxx_driver = {
161 .probe = cns3xxx_ohci_probe,
162 .remove = cns3xxx_ohci_remove,
163 .driver = {
164 .name = "cns3xxx-ohci",
165 },
166};