.. | .. |
---|
11 | 11 | #include <linux/clk.h> |
---|
12 | 12 | #include <linux/err.h> |
---|
13 | 13 | #include <linux/usb/otg.h> |
---|
| 14 | +#include <linux/usb/of.h> |
---|
14 | 15 | #include <linux/platform_data/mv_usb.h> |
---|
| 16 | +#include <linux/io.h> |
---|
| 17 | + |
---|
| 18 | +#include <linux/usb/hcd.h> |
---|
| 19 | + |
---|
| 20 | +#include "ehci.h" |
---|
| 21 | + |
---|
| 22 | +/* registers */ |
---|
| 23 | +#define U2x_CAPREGS_OFFSET 0x100 |
---|
15 | 24 | |
---|
16 | 25 | #define CAPLENGTH_MASK (0xff) |
---|
17 | 26 | |
---|
18 | | -struct ehci_hcd_mv { |
---|
19 | | - struct usb_hcd *hcd; |
---|
| 27 | +#define hcd_to_ehci_hcd_mv(h) ((struct ehci_hcd_mv *)hcd_to_ehci(h)->priv) |
---|
20 | 28 | |
---|
| 29 | +struct ehci_hcd_mv { |
---|
21 | 30 | /* Which mode does this ehci running OTG/Host ? */ |
---|
22 | 31 | int mode; |
---|
23 | 32 | |
---|
24 | | - void __iomem *phy_regs; |
---|
| 33 | + void __iomem *base; |
---|
25 | 34 | void __iomem *cap_regs; |
---|
26 | 35 | void __iomem *op_regs; |
---|
27 | 36 | |
---|
28 | 37 | struct usb_phy *otg; |
---|
29 | | - |
---|
30 | | - struct mv_usb_platform_data *pdata; |
---|
31 | | - |
---|
32 | 38 | struct clk *clk; |
---|
| 39 | + |
---|
| 40 | + struct phy *phy; |
---|
| 41 | + |
---|
| 42 | + int (*set_vbus)(unsigned int vbus); |
---|
33 | 43 | }; |
---|
34 | | - |
---|
35 | | -static void ehci_clock_enable(struct ehci_hcd_mv *ehci_mv) |
---|
36 | | -{ |
---|
37 | | - clk_prepare_enable(ehci_mv->clk); |
---|
38 | | -} |
---|
39 | | - |
---|
40 | | -static void ehci_clock_disable(struct ehci_hcd_mv *ehci_mv) |
---|
41 | | -{ |
---|
42 | | - clk_disable_unprepare(ehci_mv->clk); |
---|
43 | | -} |
---|
44 | 44 | |
---|
45 | 45 | static int mv_ehci_enable(struct ehci_hcd_mv *ehci_mv) |
---|
46 | 46 | { |
---|
47 | 47 | int retval; |
---|
48 | 48 | |
---|
49 | | - ehci_clock_enable(ehci_mv); |
---|
50 | | - if (ehci_mv->pdata->phy_init) { |
---|
51 | | - retval = ehci_mv->pdata->phy_init(ehci_mv->phy_regs); |
---|
52 | | - if (retval) |
---|
53 | | - return retval; |
---|
54 | | - } |
---|
| 49 | + retval = clk_prepare_enable(ehci_mv->clk); |
---|
| 50 | + if (retval) |
---|
| 51 | + return retval; |
---|
55 | 52 | |
---|
56 | | - return 0; |
---|
| 53 | + retval = phy_init(ehci_mv->phy); |
---|
| 54 | + if (retval) |
---|
| 55 | + clk_disable_unprepare(ehci_mv->clk); |
---|
| 56 | + |
---|
| 57 | + return retval; |
---|
57 | 58 | } |
---|
58 | 59 | |
---|
59 | 60 | static void mv_ehci_disable(struct ehci_hcd_mv *ehci_mv) |
---|
60 | 61 | { |
---|
61 | | - if (ehci_mv->pdata->phy_deinit) |
---|
62 | | - ehci_mv->pdata->phy_deinit(ehci_mv->phy_regs); |
---|
63 | | - ehci_clock_disable(ehci_mv); |
---|
| 62 | + phy_exit(ehci_mv->phy); |
---|
| 63 | + clk_disable_unprepare(ehci_mv->clk); |
---|
64 | 64 | } |
---|
65 | 65 | |
---|
66 | 66 | static int mv_ehci_reset(struct usb_hcd *hcd) |
---|
67 | 67 | { |
---|
68 | 68 | struct device *dev = hcd->self.controller; |
---|
69 | | - struct ehci_hcd_mv *ehci_mv = dev_get_drvdata(dev); |
---|
| 69 | + struct ehci_hcd_mv *ehci_mv = hcd_to_ehci_hcd_mv(hcd); |
---|
| 70 | + struct ehci_hcd *ehci = hcd_to_ehci(hcd); |
---|
| 71 | + u32 status; |
---|
70 | 72 | int retval; |
---|
71 | 73 | |
---|
72 | 74 | if (ehci_mv == NULL) { |
---|
.. | .. |
---|
80 | 82 | if (retval) |
---|
81 | 83 | dev_err(dev, "ehci_setup failed %d\n", retval); |
---|
82 | 84 | |
---|
| 85 | + if (of_usb_get_phy_mode(dev->of_node) == USBPHY_INTERFACE_MODE_HSIC) { |
---|
| 86 | + status = ehci_readl(ehci, &ehci->regs->port_status[0]); |
---|
| 87 | + status |= PORT_TEST_FORCE; |
---|
| 88 | + ehci_writel(ehci, status, &ehci->regs->port_status[0]); |
---|
| 89 | + status &= ~PORT_TEST_FORCE; |
---|
| 90 | + ehci_writel(ehci, status, &ehci->regs->port_status[0]); |
---|
| 91 | + } |
---|
| 92 | + |
---|
83 | 93 | return retval; |
---|
84 | 94 | } |
---|
85 | 95 | |
---|
86 | | -static const struct hc_driver mv_ehci_hc_driver = { |
---|
87 | | - .description = hcd_name, |
---|
88 | | - .product_desc = "Marvell EHCI", |
---|
89 | | - .hcd_priv_size = sizeof(struct ehci_hcd), |
---|
| 96 | +static struct hc_driver __read_mostly ehci_platform_hc_driver; |
---|
90 | 97 | |
---|
91 | | - /* |
---|
92 | | - * generic hardware linkage |
---|
93 | | - */ |
---|
94 | | - .irq = ehci_irq, |
---|
95 | | - .flags = HCD_MEMORY | HCD_USB2 | HCD_BH, |
---|
96 | | - |
---|
97 | | - /* |
---|
98 | | - * basic lifecycle operations |
---|
99 | | - */ |
---|
100 | | - .reset = mv_ehci_reset, |
---|
101 | | - .start = ehci_run, |
---|
102 | | - .stop = ehci_stop, |
---|
103 | | - .shutdown = ehci_shutdown, |
---|
104 | | - |
---|
105 | | - /* |
---|
106 | | - * managing i/o requests and associated device resources |
---|
107 | | - */ |
---|
108 | | - .urb_enqueue = ehci_urb_enqueue, |
---|
109 | | - .urb_dequeue = ehci_urb_dequeue, |
---|
110 | | - .endpoint_disable = ehci_endpoint_disable, |
---|
111 | | - .endpoint_reset = ehci_endpoint_reset, |
---|
112 | | - .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, |
---|
113 | | - |
---|
114 | | - /* |
---|
115 | | - * scheduling support |
---|
116 | | - */ |
---|
117 | | - .get_frame_number = ehci_get_frame, |
---|
118 | | - |
---|
119 | | - /* |
---|
120 | | - * root hub support |
---|
121 | | - */ |
---|
122 | | - .hub_status_data = ehci_hub_status_data, |
---|
123 | | - .hub_control = ehci_hub_control, |
---|
124 | | - .bus_suspend = ehci_bus_suspend, |
---|
125 | | - .bus_resume = ehci_bus_resume, |
---|
| 98 | +static const struct ehci_driver_overrides platform_overrides __initconst = { |
---|
| 99 | + .reset = mv_ehci_reset, |
---|
| 100 | + .extra_priv_size = sizeof(struct ehci_hcd_mv), |
---|
126 | 101 | }; |
---|
127 | 102 | |
---|
128 | 103 | static int mv_ehci_probe(struct platform_device *pdev) |
---|
.. | .. |
---|
132 | 107 | struct ehci_hcd *ehci; |
---|
133 | 108 | struct ehci_hcd_mv *ehci_mv; |
---|
134 | 109 | struct resource *r; |
---|
135 | | - int retval = -ENODEV; |
---|
| 110 | + int retval; |
---|
136 | 111 | u32 offset; |
---|
137 | | - |
---|
138 | | - if (!pdata) { |
---|
139 | | - dev_err(&pdev->dev, "missing platform_data\n"); |
---|
140 | | - return -ENODEV; |
---|
141 | | - } |
---|
| 112 | + u32 status; |
---|
142 | 113 | |
---|
143 | 114 | if (usb_disabled()) |
---|
144 | 115 | return -ENODEV; |
---|
145 | 116 | |
---|
146 | | - hcd = usb_create_hcd(&mv_ehci_hc_driver, &pdev->dev, "mv ehci"); |
---|
| 117 | + hcd = usb_create_hcd(&ehci_platform_hc_driver, &pdev->dev, dev_name(&pdev->dev)); |
---|
147 | 118 | if (!hcd) |
---|
148 | 119 | return -ENOMEM; |
---|
149 | 120 | |
---|
150 | | - ehci_mv = devm_kzalloc(&pdev->dev, sizeof(*ehci_mv), GFP_KERNEL); |
---|
151 | | - if (ehci_mv == NULL) { |
---|
152 | | - retval = -ENOMEM; |
---|
153 | | - goto err_put_hcd; |
---|
| 121 | + platform_set_drvdata(pdev, hcd); |
---|
| 122 | + ehci_mv = hcd_to_ehci_hcd_mv(hcd); |
---|
| 123 | + |
---|
| 124 | + ehci_mv->mode = MV_USB_MODE_HOST; |
---|
| 125 | + if (pdata) { |
---|
| 126 | + ehci_mv->mode = pdata->mode; |
---|
| 127 | + ehci_mv->set_vbus = pdata->set_vbus; |
---|
154 | 128 | } |
---|
155 | 129 | |
---|
156 | | - platform_set_drvdata(pdev, ehci_mv); |
---|
157 | | - ehci_mv->pdata = pdata; |
---|
158 | | - ehci_mv->hcd = hcd; |
---|
| 130 | + ehci_mv->phy = devm_phy_optional_get(&pdev->dev, "usb"); |
---|
| 131 | + if (IS_ERR(ehci_mv->phy)) { |
---|
| 132 | + retval = PTR_ERR(ehci_mv->phy); |
---|
| 133 | + if (retval != -EPROBE_DEFER) |
---|
| 134 | + dev_err(&pdev->dev, "Failed to get phy.\n"); |
---|
| 135 | + goto err_put_hcd; |
---|
| 136 | + } |
---|
159 | 137 | |
---|
160 | 138 | ehci_mv->clk = devm_clk_get(&pdev->dev, NULL); |
---|
161 | 139 | if (IS_ERR(ehci_mv->clk)) { |
---|
.. | .. |
---|
164 | 142 | goto err_put_hcd; |
---|
165 | 143 | } |
---|
166 | 144 | |
---|
167 | | - r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phyregs"); |
---|
168 | | - ehci_mv->phy_regs = devm_ioremap_resource(&pdev->dev, r); |
---|
169 | | - if (IS_ERR(ehci_mv->phy_regs)) { |
---|
170 | | - retval = PTR_ERR(ehci_mv->phy_regs); |
---|
171 | | - goto err_put_hcd; |
---|
172 | | - } |
---|
173 | | - |
---|
174 | | - r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "capregs"); |
---|
175 | | - ehci_mv->cap_regs = devm_ioremap_resource(&pdev->dev, r); |
---|
176 | | - if (IS_ERR(ehci_mv->cap_regs)) { |
---|
177 | | - retval = PTR_ERR(ehci_mv->cap_regs); |
---|
| 145 | + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
---|
| 146 | + ehci_mv->base = devm_ioremap_resource(&pdev->dev, r); |
---|
| 147 | + if (IS_ERR(ehci_mv->base)) { |
---|
| 148 | + retval = PTR_ERR(ehci_mv->base); |
---|
178 | 149 | goto err_put_hcd; |
---|
179 | 150 | } |
---|
180 | 151 | |
---|
.. | .. |
---|
184 | 155 | goto err_put_hcd; |
---|
185 | 156 | } |
---|
186 | 157 | |
---|
| 158 | + ehci_mv->cap_regs = |
---|
| 159 | + (void __iomem *) ((unsigned long) ehci_mv->base + U2x_CAPREGS_OFFSET); |
---|
187 | 160 | offset = readl(ehci_mv->cap_regs) & CAPLENGTH_MASK; |
---|
188 | 161 | ehci_mv->op_regs = |
---|
189 | 162 | (void __iomem *) ((unsigned long) ehci_mv->cap_regs + offset); |
---|
.. | .. |
---|
198 | 171 | hcd->irq = retval; |
---|
199 | 172 | |
---|
200 | 173 | ehci = hcd_to_ehci(hcd); |
---|
201 | | - ehci->caps = (struct ehci_caps *) ehci_mv->cap_regs; |
---|
| 174 | + ehci->caps = (struct ehci_caps __iomem *) ehci_mv->cap_regs; |
---|
202 | 175 | |
---|
203 | | - ehci_mv->mode = pdata->mode; |
---|
204 | 176 | if (ehci_mv->mode == MV_USB_MODE_OTG) { |
---|
205 | 177 | ehci_mv->otg = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2); |
---|
206 | 178 | if (IS_ERR(ehci_mv->otg)) { |
---|
.. | .. |
---|
225 | 197 | /* otg will enable clock before use as host */ |
---|
226 | 198 | mv_ehci_disable(ehci_mv); |
---|
227 | 199 | } else { |
---|
228 | | - if (pdata->set_vbus) |
---|
229 | | - pdata->set_vbus(1); |
---|
| 200 | + if (ehci_mv->set_vbus) |
---|
| 201 | + ehci_mv->set_vbus(1); |
---|
230 | 202 | |
---|
231 | 203 | retval = usb_add_hcd(hcd, hcd->irq, IRQF_SHARED); |
---|
232 | 204 | if (retval) { |
---|
.. | .. |
---|
237 | 209 | device_wakeup_enable(hcd->self.controller); |
---|
238 | 210 | } |
---|
239 | 211 | |
---|
240 | | - if (pdata->private_init) |
---|
241 | | - pdata->private_init(ehci_mv->op_regs, ehci_mv->phy_regs); |
---|
| 212 | + if (of_usb_get_phy_mode(pdev->dev.of_node) == USBPHY_INTERFACE_MODE_HSIC) { |
---|
| 213 | + status = ehci_readl(ehci, &ehci->regs->port_status[0]); |
---|
| 214 | + /* These "reserved" bits actually enable HSIC mode. */ |
---|
| 215 | + status |= BIT(25); |
---|
| 216 | + status &= ~GENMASK(31, 30); |
---|
| 217 | + ehci_writel(ehci, status, &ehci->regs->port_status[0]); |
---|
| 218 | + } |
---|
242 | 219 | |
---|
243 | 220 | dev_info(&pdev->dev, |
---|
244 | 221 | "successful find EHCI device with regs 0x%p irq %d" |
---|
.. | .. |
---|
248 | 225 | return 0; |
---|
249 | 226 | |
---|
250 | 227 | err_set_vbus: |
---|
251 | | - if (pdata->set_vbus) |
---|
252 | | - pdata->set_vbus(0); |
---|
| 228 | + if (ehci_mv->set_vbus) |
---|
| 229 | + ehci_mv->set_vbus(0); |
---|
253 | 230 | err_disable_clk: |
---|
254 | 231 | mv_ehci_disable(ehci_mv); |
---|
255 | 232 | err_put_hcd: |
---|
.. | .. |
---|
260 | 237 | |
---|
261 | 238 | static int mv_ehci_remove(struct platform_device *pdev) |
---|
262 | 239 | { |
---|
263 | | - struct ehci_hcd_mv *ehci_mv = platform_get_drvdata(pdev); |
---|
264 | | - struct usb_hcd *hcd = ehci_mv->hcd; |
---|
| 240 | + struct usb_hcd *hcd = platform_get_drvdata(pdev); |
---|
| 241 | + struct ehci_hcd_mv *ehci_mv = hcd_to_ehci_hcd_mv(hcd); |
---|
265 | 242 | |
---|
266 | 243 | if (hcd->rh_registered) |
---|
267 | 244 | usb_remove_hcd(hcd); |
---|
.. | .. |
---|
270 | 247 | otg_set_host(ehci_mv->otg->otg, NULL); |
---|
271 | 248 | |
---|
272 | 249 | if (ehci_mv->mode == MV_USB_MODE_HOST) { |
---|
273 | | - if (ehci_mv->pdata->set_vbus) |
---|
274 | | - ehci_mv->pdata->set_vbus(0); |
---|
| 250 | + if (ehci_mv->set_vbus) |
---|
| 251 | + ehci_mv->set_vbus(0); |
---|
275 | 252 | |
---|
276 | 253 | mv_ehci_disable(ehci_mv); |
---|
277 | 254 | } |
---|
.. | .. |
---|
284 | 261 | MODULE_ALIAS("mv-ehci"); |
---|
285 | 262 | |
---|
286 | 263 | static const struct platform_device_id ehci_id_table[] = { |
---|
287 | | - {"pxa-u2oehci", PXA_U2OEHCI}, |
---|
288 | | - {"pxa-sph", PXA_SPH}, |
---|
289 | | - {"mmp3-hsic", MMP3_HSIC}, |
---|
290 | | - {"mmp3-fsic", MMP3_FSIC}, |
---|
| 264 | + {"pxa-u2oehci", 0}, |
---|
| 265 | + {"pxa-sph", 0}, |
---|
291 | 266 | {}, |
---|
292 | 267 | }; |
---|
293 | 268 | |
---|
294 | 269 | static void mv_ehci_shutdown(struct platform_device *pdev) |
---|
295 | 270 | { |
---|
296 | | - struct ehci_hcd_mv *ehci_mv = platform_get_drvdata(pdev); |
---|
297 | | - struct usb_hcd *hcd = ehci_mv->hcd; |
---|
| 271 | + struct usb_hcd *hcd = platform_get_drvdata(pdev); |
---|
298 | 272 | |
---|
299 | 273 | if (!hcd->rh_registered) |
---|
300 | 274 | return; |
---|
.. | .. |
---|
303 | 277 | hcd->driver->shutdown(hcd); |
---|
304 | 278 | } |
---|
305 | 279 | |
---|
| 280 | +static const struct of_device_id ehci_mv_dt_ids[] = { |
---|
| 281 | + { .compatible = "marvell,pxau2o-ehci", }, |
---|
| 282 | + {}, |
---|
| 283 | +}; |
---|
| 284 | + |
---|
306 | 285 | static struct platform_driver ehci_mv_driver = { |
---|
307 | 286 | .probe = mv_ehci_probe, |
---|
308 | 287 | .remove = mv_ehci_remove, |
---|
309 | 288 | .shutdown = mv_ehci_shutdown, |
---|
310 | 289 | .driver = { |
---|
311 | | - .name = "mv-ehci", |
---|
312 | | - .bus = &platform_bus_type, |
---|
313 | | - }, |
---|
| 290 | + .name = "mv-ehci", |
---|
| 291 | + .bus = &platform_bus_type, |
---|
| 292 | + .of_match_table = ehci_mv_dt_ids, |
---|
| 293 | + }, |
---|
314 | 294 | .id_table = ehci_id_table, |
---|
315 | 295 | }; |
---|
| 296 | + |
---|
| 297 | +static int __init ehci_platform_init(void) |
---|
| 298 | +{ |
---|
| 299 | + if (usb_disabled()) |
---|
| 300 | + return -ENODEV; |
---|
| 301 | + |
---|
| 302 | + ehci_init_driver(&ehci_platform_hc_driver, &platform_overrides); |
---|
| 303 | + return platform_driver_register(&ehci_mv_driver); |
---|
| 304 | +} |
---|
| 305 | +module_init(ehci_platform_init); |
---|
| 306 | + |
---|
| 307 | +static void __exit ehci_platform_cleanup(void) |
---|
| 308 | +{ |
---|
| 309 | + platform_driver_unregister(&ehci_mv_driver); |
---|
| 310 | +} |
---|
| 311 | +module_exit(ehci_platform_cleanup); |
---|
| 312 | + |
---|
| 313 | +MODULE_DESCRIPTION("Marvell EHCI driver"); |
---|
| 314 | +MODULE_AUTHOR("Chao Xie <chao.xie@marvell.com>"); |
---|
| 315 | +MODULE_AUTHOR("Neil Zhang <zhangwm@marvell.com>"); |
---|
| 316 | +MODULE_ALIAS("mv-ehci"); |
---|
| 317 | +MODULE_LICENSE("GPL"); |
---|
| 318 | +MODULE_DEVICE_TABLE(of, ehci_mv_dt_ids); |
---|