diff options
author | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2012-04-22 18:25:26 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2012-04-22 18:25:26 -0400 |
commit | 09091a4d5f2dd378dcf71de50b48cdacc58a8ac0 (patch) | |
tree | d50e37ebb5591fa1e723f32bde077dbdf726b78b /drivers/usb/host/ssb-hcd.c | |
parent | 66f75a5d028beaf67c931435fdc3e7823125730c (diff) | |
parent | 3a1c2a82204f5376f484d82cb18189afc2145c77 (diff) |
Merge 3.4-rc4 into usb-next.
This resolves the conflict in:
drivers/usb/host/ehci-fsl.c
And picks up loads of xhci bugfixes to make it easier for others to test
with.
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/usb/host/ssb-hcd.c')
-rw-r--r-- | drivers/usb/host/ssb-hcd.c | 279 |
1 files changed, 279 insertions, 0 deletions
diff --git a/drivers/usb/host/ssb-hcd.c b/drivers/usb/host/ssb-hcd.c new file mode 100644 index 000000000000..c2e73433b306 --- /dev/null +++ b/drivers/usb/host/ssb-hcd.c | |||
@@ -0,0 +1,279 @@ | |||
1 | /* | ||
2 | * Sonics Silicon Backplane | ||
3 | * Broadcom USB-core driver (SSB bus glue) | ||
4 | * | ||
5 | * Copyright 2011-2012 Hauke Mehrtens <hauke@hauke-m.de> | ||
6 | * | ||
7 | * Based on ssb-ohci driver | ||
8 | * Copyright 2007 Michael Buesch <m@bues.ch> | ||
9 | * | ||
10 | * Derived from the OHCI-PCI driver | ||
11 | * Copyright 1999 Roman Weissgaerber | ||
12 | * Copyright 2000-2002 David Brownell | ||
13 | * Copyright 1999 Linus Torvalds | ||
14 | * Copyright 1999 Gregory P. Smith | ||
15 | * | ||
16 | * Derived from the USBcore related parts of Broadcom-SB | ||
17 | * Copyright 2005-2011 Broadcom Corporation | ||
18 | * | ||
19 | * Licensed under the GNU/GPL. See COPYING for details. | ||
20 | */ | ||
21 | #include <linux/ssb/ssb.h> | ||
22 | #include <linux/delay.h> | ||
23 | #include <linux/platform_device.h> | ||
24 | #include <linux/module.h> | ||
25 | #include <linux/usb/ehci_pdriver.h> | ||
26 | #include <linux/usb/ohci_pdriver.h> | ||
27 | |||
28 | MODULE_AUTHOR("Hauke Mehrtens"); | ||
29 | MODULE_DESCRIPTION("Common USB driver for SSB Bus"); | ||
30 | MODULE_LICENSE("GPL"); | ||
31 | |||
32 | #define SSB_HCD_TMSLOW_HOSTMODE (1 << 29) | ||
33 | |||
34 | struct ssb_hcd_device { | ||
35 | struct platform_device *ehci_dev; | ||
36 | struct platform_device *ohci_dev; | ||
37 | |||
38 | u32 enable_flags; | ||
39 | }; | ||
40 | |||
41 | static void __devinit ssb_hcd_5354wa(struct ssb_device *dev) | ||
42 | { | ||
43 | #ifdef CONFIG_SSB_DRIVER_MIPS | ||
44 | /* Work around for 5354 failures */ | ||
45 | if (dev->id.revision == 2 && dev->bus->chip_id == 0x5354) { | ||
46 | /* Change syn01 reg */ | ||
47 | ssb_write32(dev, 0x894, 0x00fe00fe); | ||
48 | |||
49 | /* Change syn03 reg */ | ||
50 | ssb_write32(dev, 0x89c, ssb_read32(dev, 0x89c) | 0x1); | ||
51 | } | ||
52 | #endif | ||
53 | } | ||
54 | |||
55 | static void __devinit ssb_hcd_usb20wa(struct ssb_device *dev) | ||
56 | { | ||
57 | if (dev->id.coreid == SSB_DEV_USB20_HOST) { | ||
58 | /* | ||
59 | * USB 2.0 special considerations: | ||
60 | * | ||
61 | * In addition to the standard SSB reset sequence, the Host | ||
62 | * Control Register must be programmed to bring the USB core | ||
63 | * and various phy components out of reset. | ||
64 | */ | ||
65 | ssb_write32(dev, 0x200, 0x7ff); | ||
66 | |||
67 | /* Change Flush control reg */ | ||
68 | ssb_write32(dev, 0x400, ssb_read32(dev, 0x400) & ~8); | ||
69 | ssb_read32(dev, 0x400); | ||
70 | |||
71 | /* Change Shim control reg */ | ||
72 | ssb_write32(dev, 0x304, ssb_read32(dev, 0x304) & ~0x100); | ||
73 | ssb_read32(dev, 0x304); | ||
74 | |||
75 | udelay(1); | ||
76 | |||
77 | ssb_hcd_5354wa(dev); | ||
78 | } | ||
79 | } | ||
80 | |||
81 | /* based on arch/mips/brcm-boards/bcm947xx/pcibios.c */ | ||
82 | static u32 __devinit ssb_hcd_init_chip(struct ssb_device *dev) | ||
83 | { | ||
84 | u32 flags = 0; | ||
85 | |||
86 | if (dev->id.coreid == SSB_DEV_USB11_HOSTDEV) | ||
87 | /* Put the device into host-mode. */ | ||
88 | flags |= SSB_HCD_TMSLOW_HOSTMODE; | ||
89 | |||
90 | ssb_device_enable(dev, flags); | ||
91 | |||
92 | ssb_hcd_usb20wa(dev); | ||
93 | |||
94 | return flags; | ||
95 | } | ||
96 | |||
97 | static const struct usb_ehci_pdata ehci_pdata = { | ||
98 | }; | ||
99 | |||
100 | static const struct usb_ohci_pdata ohci_pdata = { | ||
101 | }; | ||
102 | |||
103 | static struct platform_device * __devinit | ||
104 | ssb_hcd_create_pdev(struct ssb_device *dev, bool ohci, u32 addr, u32 len) | ||
105 | { | ||
106 | struct platform_device *hci_dev; | ||
107 | struct resource hci_res[2]; | ||
108 | int ret = -ENOMEM; | ||
109 | |||
110 | memset(hci_res, 0, sizeof(hci_res)); | ||
111 | |||
112 | hci_res[0].start = addr; | ||
113 | hci_res[0].end = hci_res[0].start + len - 1; | ||
114 | hci_res[0].flags = IORESOURCE_MEM; | ||
115 | |||
116 | hci_res[1].start = dev->irq; | ||
117 | hci_res[1].flags = IORESOURCE_IRQ; | ||
118 | |||
119 | hci_dev = platform_device_alloc(ohci ? "ohci-platform" : | ||
120 | "ehci-platform" , 0); | ||
121 | if (!hci_dev) | ||
122 | return NULL; | ||
123 | |||
124 | hci_dev->dev.parent = dev->dev; | ||
125 | hci_dev->dev.dma_mask = &hci_dev->dev.coherent_dma_mask; | ||
126 | |||
127 | ret = platform_device_add_resources(hci_dev, hci_res, | ||
128 | ARRAY_SIZE(hci_res)); | ||
129 | if (ret) | ||
130 | goto err_alloc; | ||
131 | if (ohci) | ||
132 | ret = platform_device_add_data(hci_dev, &ohci_pdata, | ||
133 | sizeof(ohci_pdata)); | ||
134 | else | ||
135 | ret = platform_device_add_data(hci_dev, &ehci_pdata, | ||
136 | sizeof(ehci_pdata)); | ||
137 | if (ret) | ||
138 | goto err_alloc; | ||
139 | ret = platform_device_add(hci_dev); | ||
140 | if (ret) | ||
141 | goto err_alloc; | ||
142 | |||
143 | return hci_dev; | ||
144 | |||
145 | err_alloc: | ||
146 | platform_device_put(hci_dev); | ||
147 | return ERR_PTR(ret); | ||
148 | } | ||
149 | |||
150 | static int __devinit ssb_hcd_probe(struct ssb_device *dev, | ||
151 | const struct ssb_device_id *id) | ||
152 | { | ||
153 | int err, tmp; | ||
154 | int start, len; | ||
155 | u16 chipid_top; | ||
156 | u16 coreid = dev->id.coreid; | ||
157 | struct ssb_hcd_device *usb_dev; | ||
158 | |||
159 | /* USBcores are only connected on embedded devices. */ | ||
160 | chipid_top = (dev->bus->chip_id & 0xFF00); | ||
161 | if (chipid_top != 0x4700 && chipid_top != 0x5300) | ||
162 | return -ENODEV; | ||
163 | |||
164 | /* TODO: Probably need checks here; is the core connected? */ | ||
165 | |||
166 | if (dma_set_mask(dev->dma_dev, DMA_BIT_MASK(32)) || | ||
167 | dma_set_coherent_mask(dev->dma_dev, DMA_BIT_MASK(32))) | ||
168 | return -EOPNOTSUPP; | ||
169 | |||
170 | usb_dev = kzalloc(sizeof(struct ssb_hcd_device), GFP_KERNEL); | ||
171 | if (!usb_dev) | ||
172 | return -ENOMEM; | ||
173 | |||
174 | /* We currently always attach SSB_DEV_USB11_HOSTDEV | ||
175 | * as HOST OHCI. If we want to attach it as Client device, | ||
176 | * we must branch here and call into the (yet to | ||
177 | * be written) Client mode driver. Same for remove(). */ | ||
178 | usb_dev->enable_flags = ssb_hcd_init_chip(dev); | ||
179 | |||
180 | tmp = ssb_read32(dev, SSB_ADMATCH0); | ||
181 | |||
182 | start = ssb_admatch_base(tmp); | ||
183 | len = (coreid == SSB_DEV_USB20_HOST) ? 0x800 : ssb_admatch_size(tmp); | ||
184 | usb_dev->ohci_dev = ssb_hcd_create_pdev(dev, true, start, len); | ||
185 | if (IS_ERR(usb_dev->ohci_dev)) { | ||
186 | err = PTR_ERR(usb_dev->ohci_dev); | ||
187 | goto err_free_usb_dev; | ||
188 | } | ||
189 | |||
190 | if (coreid == SSB_DEV_USB20_HOST) { | ||
191 | start = ssb_admatch_base(tmp) + 0x800; /* ehci core offset */ | ||
192 | usb_dev->ehci_dev = ssb_hcd_create_pdev(dev, false, start, len); | ||
193 | if (IS_ERR(usb_dev->ehci_dev)) { | ||
194 | err = PTR_ERR(usb_dev->ehci_dev); | ||
195 | goto err_unregister_ohci_dev; | ||
196 | } | ||
197 | } | ||
198 | |||
199 | ssb_set_drvdata(dev, usb_dev); | ||
200 | return 0; | ||
201 | |||
202 | err_unregister_ohci_dev: | ||
203 | platform_device_unregister(usb_dev->ohci_dev); | ||
204 | err_free_usb_dev: | ||
205 | kfree(usb_dev); | ||
206 | return err; | ||
207 | } | ||
208 | |||
209 | static void __devexit ssb_hcd_remove(struct ssb_device *dev) | ||
210 | { | ||
211 | struct ssb_hcd_device *usb_dev = ssb_get_drvdata(dev); | ||
212 | struct platform_device *ohci_dev = usb_dev->ohci_dev; | ||
213 | struct platform_device *ehci_dev = usb_dev->ehci_dev; | ||
214 | |||
215 | if (ohci_dev) | ||
216 | platform_device_unregister(ohci_dev); | ||
217 | if (ehci_dev) | ||
218 | platform_device_unregister(ehci_dev); | ||
219 | |||
220 | ssb_device_disable(dev, 0); | ||
221 | } | ||
222 | |||
223 | static void __devexit ssb_hcd_shutdown(struct ssb_device *dev) | ||
224 | { | ||
225 | ssb_device_disable(dev, 0); | ||
226 | } | ||
227 | |||
228 | #ifdef CONFIG_PM | ||
229 | |||
230 | static int ssb_hcd_suspend(struct ssb_device *dev, pm_message_t state) | ||
231 | { | ||
232 | ssb_device_disable(dev, 0); | ||
233 | |||
234 | return 0; | ||
235 | } | ||
236 | |||
237 | static int ssb_hcd_resume(struct ssb_device *dev) | ||
238 | { | ||
239 | struct ssb_hcd_device *usb_dev = ssb_get_drvdata(dev); | ||
240 | |||
241 | ssb_device_enable(dev, usb_dev->enable_flags); | ||
242 | |||
243 | return 0; | ||
244 | } | ||
245 | |||
246 | #else /* !CONFIG_PM */ | ||
247 | #define ssb_hcd_suspend NULL | ||
248 | #define ssb_hcd_resume NULL | ||
249 | #endif /* CONFIG_PM */ | ||
250 | |||
251 | static const struct ssb_device_id ssb_hcd_table[] __devinitconst = { | ||
252 | SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_USB11_HOSTDEV, SSB_ANY_REV), | ||
253 | SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_USB11_HOST, SSB_ANY_REV), | ||
254 | SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_USB20_HOST, SSB_ANY_REV), | ||
255 | SSB_DEVTABLE_END | ||
256 | }; | ||
257 | MODULE_DEVICE_TABLE(ssb, ssb_hcd_table); | ||
258 | |||
259 | static struct ssb_driver ssb_hcd_driver = { | ||
260 | .name = KBUILD_MODNAME, | ||
261 | .id_table = ssb_hcd_table, | ||
262 | .probe = ssb_hcd_probe, | ||
263 | .remove = __devexit_p(ssb_hcd_remove), | ||
264 | .shutdown = ssb_hcd_shutdown, | ||
265 | .suspend = ssb_hcd_suspend, | ||
266 | .resume = ssb_hcd_resume, | ||
267 | }; | ||
268 | |||
269 | static int __init ssb_hcd_init(void) | ||
270 | { | ||
271 | return ssb_driver_register(&ssb_hcd_driver); | ||
272 | } | ||
273 | module_init(ssb_hcd_init); | ||
274 | |||
275 | static void __exit ssb_hcd_exit(void) | ||
276 | { | ||
277 | ssb_driver_unregister(&ssb_hcd_driver); | ||
278 | } | ||
279 | module_exit(ssb_hcd_exit); | ||