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