OpenWrt – Blame information for rev 4
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
4 | office | 1 | /* |
2 | * drivers/usb/host/ehci-oxnas.c |
||
3 | * |
||
4 | * Tzachi Perelstein <tzachi@marvell.com> |
||
5 | * |
||
6 | * This file is licensed under the terms of the GNU General Public |
||
7 | * License version 2. This program is licensed "as is" without any |
||
8 | * warranty of any kind, whether express or implied. |
||
9 | */ |
||
10 | |||
11 | #include <linux/kernel.h> |
||
12 | #include <linux/module.h> |
||
13 | #include <linux/platform_device.h> |
||
14 | #include <linux/of.h> |
||
15 | #include <linux/of_address.h> |
||
16 | #include <linux/of_irq.h> |
||
17 | #include <linux/mfd/syscon.h> |
||
18 | #include <linux/usb.h> |
||
19 | #include <linux/usb/hcd.h> |
||
20 | #include <linux/dma-mapping.h> |
||
21 | #include <linux/clk.h> |
||
22 | #include <linux/regmap.h> |
||
23 | #include <linux/reset.h> |
||
24 | |||
25 | #define USBHSMPH_CTRL_REGOFFSET 0x40 |
||
26 | #define USBHSMPH_STAT_REGOFFSET 0x44 |
||
27 | #define REF300_DIV_REGOFFSET 0xF8 |
||
28 | #define USBHSPHY_CTRL_REGOFFSET 0x84 |
||
29 | #define USB_CTRL_REGOFFSET 0x90 |
||
30 | #define PLLB_DIV_CTRL_REGOFFSET 0x1000F8 |
||
31 | #define USBHSPHY_SUSPENDM_MANUAL_ENABLE 16 |
||
32 | #define USBHSPHY_SUSPENDM_MANUAL_STATE 15 |
||
33 | #define USBHSPHY_ATE_ESET 14 |
||
34 | #define USBHSPHY_TEST_DIN 6 |
||
35 | #define USBHSPHY_TEST_ADD 2 |
||
36 | #define USBHSPHY_TEST_DOUT_SEL 1 |
||
37 | #define USBHSPHY_TEST_CLK 0 |
||
38 | |||
39 | #define USB_CTRL_USBAPHY_CKSEL_SHIFT 5 |
||
40 | #define USB_CLK_XTAL0_XTAL1 (0 << USB_CTRL_USBAPHY_CKSEL_SHIFT) |
||
41 | #define USB_CLK_XTAL0 (1 << USB_CTRL_USBAPHY_CKSEL_SHIFT) |
||
42 | #define USB_CLK_INTERNAL (2 << USB_CTRL_USBAPHY_CKSEL_SHIFT) |
||
43 | |||
44 | #define USBAMUX_DEVICE BIT(4) |
||
45 | |||
46 | #define USBPHY_REFCLKDIV_SHIFT 2 |
||
47 | #define USB_PHY_REF_12MHZ (0 << USBPHY_REFCLKDIV_SHIFT) |
||
48 | #define USB_PHY_REF_24MHZ (1 << USBPHY_REFCLKDIV_SHIFT) |
||
49 | #define USB_PHY_REF_48MHZ (2 << USBPHY_REFCLKDIV_SHIFT) |
||
50 | |||
51 | #define USB_CTRL_USB_CKO_SEL_BIT 0 |
||
52 | |||
53 | #define USB_INT_CLK_XTAL 0 |
||
54 | #define USB_INT_CLK_REF300 2 |
||
55 | #define USB_INT_CLK_PLLB 3 |
||
56 | |||
57 | #define REF300_DIV_INT_SHIFT 8 |
||
58 | #define REF300_DIV_FRAC_SHIFT 0 |
||
59 | #define REF300_DIV_INT(val) ((val) << REF300_DIV_INT_SHIFT) |
||
60 | #define REF300_DIV_FRAC(val) ((val) << REF300_DIV_FRAC_SHIFT) |
||
61 | |||
62 | #define PLLB_BYPASS 1 |
||
63 | #define PLLB_ENSAT 3 |
||
64 | #define PLLB_OUTDIV 4 |
||
65 | #define PLLB_REFDIV 8 |
||
66 | #define PLLB_DIV_INT_SHIFT 8 |
||
67 | #define PLLB_DIV_FRAC_SHIFT 0 |
||
68 | #define PLLB_DIV_INT(val) ((val) << PLLB_DIV_INT_SHIFT) |
||
69 | #define PLLB_DIV_FRAC(val) ((val) << PLLB_DIV_FRAC_SHIFT) |
||
70 | |||
71 | #include "ehci.h" |
||
72 | |||
73 | struct oxnas_hcd { |
||
74 | struct clk *clk; |
||
75 | struct clk *refsrc; |
||
76 | struct clk *phyref; |
||
77 | int use_pllb; |
||
78 | int use_phya; |
||
79 | struct reset_control *rst_host; |
||
80 | struct reset_control *rst_phya; |
||
81 | struct reset_control *rst_phyb; |
||
82 | struct regmap *syscon; |
||
83 | }; |
||
84 | |||
85 | #define DRIVER_DESC "Oxnas On-Chip EHCI Host Controller" |
||
86 | |||
87 | static struct hc_driver __read_mostly oxnas_hc_driver; |
||
88 | |||
89 | static void start_oxnas_usb_ehci(struct oxnas_hcd *oxnas) |
||
90 | { |
||
91 | if (oxnas->use_pllb) { |
||
92 | /* enable pllb */ |
||
93 | clk_prepare_enable(oxnas->refsrc); |
||
94 | /* enable ref600 */ |
||
95 | clk_prepare_enable(oxnas->phyref); |
||
96 | /* 600MHz pllb divider for 12MHz */ |
||
97 | regmap_write_bits(oxnas->syscon, PLLB_DIV_CTRL_REGOFFSET, 0xffff, PLLB_DIV_INT(50) | PLLB_DIV_FRAC(0)); |
||
98 | } else { |
||
99 | /* ref 300 divider for 12MHz */ |
||
100 | regmap_write_bits(oxnas->syscon, REF300_DIV_REGOFFSET, 0xffff, REF300_DIV_INT(25) | REF300_DIV_FRAC(0)); |
||
101 | } |
||
102 | |||
103 | /* Ensure the USB block is properly reset */ |
||
104 | reset_control_reset(oxnas->rst_host); |
||
105 | reset_control_reset(oxnas->rst_phya); |
||
106 | reset_control_reset(oxnas->rst_phyb); |
||
107 | |||
108 | /* Force the high speed clock to be generated all the time, via serial |
||
109 | programming of the USB HS PHY */ |
||
110 | regmap_write_bits(oxnas->syscon, USBHSPHY_CTRL_REGOFFSET, 0xffff, |
||
111 | (2UL << USBHSPHY_TEST_ADD) | |
||
112 | (0xe0UL << USBHSPHY_TEST_DIN)); |
||
113 | |||
114 | regmap_write_bits(oxnas->syscon, USBHSPHY_CTRL_REGOFFSET, 0xffff, |
||
115 | (1UL << USBHSPHY_TEST_CLK) | |
||
116 | (2UL << USBHSPHY_TEST_ADD) | |
||
117 | (0xe0UL << USBHSPHY_TEST_DIN)); |
||
118 | |||
119 | regmap_write_bits(oxnas->syscon, USBHSPHY_CTRL_REGOFFSET, 0xffff, |
||
120 | (0xfUL << USBHSPHY_TEST_ADD) | |
||
121 | (0xaaUL << USBHSPHY_TEST_DIN)); |
||
122 | |||
123 | regmap_write_bits(oxnas->syscon, USBHSPHY_CTRL_REGOFFSET, 0xffff, |
||
124 | (1UL << USBHSPHY_TEST_CLK) | |
||
125 | (0xfUL << USBHSPHY_TEST_ADD) | |
||
126 | (0xaaUL << USBHSPHY_TEST_DIN)); |
||
127 | |||
128 | if (oxnas->use_pllb) /* use pllb clock */ |
||
129 | regmap_write_bits(oxnas->syscon, USB_CTRL_REGOFFSET, 0xffff, |
||
130 | USB_CLK_INTERNAL | USB_INT_CLK_PLLB); |
||
131 | else /* use ref300 derived clock */ |
||
132 | regmap_write_bits(oxnas->syscon, USB_CTRL_REGOFFSET, 0xffff, |
||
133 | USB_CLK_INTERNAL | USB_INT_CLK_REF300); |
||
134 | |||
135 | if (oxnas->use_phya) { |
||
136 | /* Configure USB PHYA as a host */ |
||
137 | regmap_update_bits(oxnas->syscon, USB_CTRL_REGOFFSET, USBAMUX_DEVICE, 0); |
||
138 | } |
||
139 | |||
140 | /* Enable the clock to the USB block */ |
||
141 | clk_prepare_enable(oxnas->clk); |
||
142 | } |
||
143 | |||
144 | static void stop_oxnas_usb_ehci(struct oxnas_hcd *oxnas) |
||
145 | { |
||
146 | reset_control_assert(oxnas->rst_host); |
||
147 | reset_control_assert(oxnas->rst_phya); |
||
148 | reset_control_assert(oxnas->rst_phyb); |
||
149 | |||
150 | if (oxnas->use_pllb) { |
||
151 | clk_disable_unprepare(oxnas->phyref); |
||
152 | clk_disable_unprepare(oxnas->refsrc); |
||
153 | } |
||
154 | clk_disable_unprepare(oxnas->clk); |
||
155 | } |
||
156 | |||
157 | static int ehci_oxnas_reset(struct usb_hcd *hcd) |
||
158 | { |
||
159 | #define txttfill_tuning reserved2[0] |
||
160 | |||
161 | struct ehci_hcd *ehci; |
||
162 | u32 tmp; |
||
163 | int retval = ehci_setup(hcd); |
||
164 | if (retval) |
||
165 | return retval; |
||
166 | |||
167 | ehci = hcd_to_ehci(hcd); |
||
168 | tmp = ehci_readl(ehci, &ehci->regs->txfill_tuning); |
||
169 | tmp &= ~0x00ff0000; |
||
170 | tmp |= 0x003f0000; /* set burst pre load count to 0x40 (63 * 4 bytes) */ |
||
171 | tmp |= 0x16; /* set sheduler overhead to 22 * 1.267us (HS) or 22 * 6.33us (FS/LS)*/ |
||
172 | ehci_writel(ehci, tmp, &ehci->regs->txfill_tuning); |
||
173 | |||
174 | tmp = ehci_readl(ehci, &ehci->regs->txttfill_tuning); |
||
175 | tmp |= 0x2; /* set sheduler overhead to 2 * 6.333us */ |
||
176 | ehci_writel(ehci, tmp, &ehci->regs->txttfill_tuning); |
||
177 | |||
178 | return retval; |
||
179 | } |
||
180 | |||
181 | static int ehci_oxnas_drv_probe(struct platform_device *ofdev) |
||
182 | { |
||
183 | struct device_node *np = ofdev->dev.of_node; |
||
184 | struct usb_hcd *hcd; |
||
185 | struct ehci_hcd *ehci; |
||
186 | struct resource res; |
||
187 | struct oxnas_hcd *oxnas; |
||
188 | int irq, err; |
||
189 | struct reset_control *rstc; |
||
190 | |||
191 | if (usb_disabled()) |
||
192 | return -ENODEV; |
||
193 | |||
194 | if (!ofdev->dev.dma_mask) |
||
195 | ofdev->dev.dma_mask = &ofdev->dev.coherent_dma_mask; |
||
196 | if (!ofdev->dev.coherent_dma_mask) |
||
197 | ofdev->dev.coherent_dma_mask = DMA_BIT_MASK(32); |
||
198 | |||
199 | hcd = usb_create_hcd(&oxnas_hc_driver, &ofdev->dev, |
||
200 | dev_name(&ofdev->dev)); |
||
201 | if (!hcd) |
||
202 | return -ENOMEM; |
||
203 | |||
204 | err = of_address_to_resource(np, 0, &res); |
||
205 | if (err) |
||
206 | goto err_res; |
||
207 | |||
208 | hcd->rsrc_start = res.start; |
||
209 | hcd->rsrc_len = resource_size(&res); |
||
210 | |||
211 | hcd->regs = devm_ioremap_resource(&ofdev->dev, &res); |
||
212 | if (IS_ERR(hcd->regs)) { |
||
213 | dev_err(&ofdev->dev, "devm_ioremap_resource failed\n"); |
||
214 | err = PTR_ERR(hcd->regs); |
||
215 | goto err_ioremap; |
||
216 | } |
||
217 | |||
218 | oxnas = (struct oxnas_hcd *)hcd_to_ehci(hcd)->priv; |
||
219 | |||
220 | oxnas->use_pllb = of_property_read_bool(np, "oxsemi,ehci_use_pllb"); |
||
221 | oxnas->use_phya = of_property_read_bool(np, "oxsemi,ehci_use_phya"); |
||
222 | |||
223 | oxnas->syscon = syscon_regmap_lookup_by_phandle(np, "oxsemi,sys-ctrl"); |
||
224 | if (IS_ERR(oxnas->syscon)) { |
||
225 | err = PTR_ERR(oxnas->syscon); |
||
226 | goto err_syscon; |
||
227 | } |
||
228 | |||
229 | oxnas->clk = of_clk_get_by_name(np, "usb"); |
||
230 | if (IS_ERR(oxnas->clk)) { |
||
231 | err = PTR_ERR(oxnas->clk); |
||
232 | goto err_clk; |
||
233 | } |
||
234 | |||
235 | if (oxnas->use_pllb) { |
||
236 | oxnas->refsrc = of_clk_get_by_name(np, "refsrc"); |
||
237 | if (IS_ERR(oxnas->refsrc)) { |
||
238 | err = PTR_ERR(oxnas->refsrc); |
||
239 | goto err_refsrc; |
||
240 | } |
||
241 | oxnas->phyref = of_clk_get_by_name(np, "phyref"); |
||
242 | if (IS_ERR(oxnas->refsrc)) { |
||
243 | err = PTR_ERR(oxnas->refsrc); |
||
244 | goto err_phyref; |
||
245 | } |
||
246 | |||
247 | } else { |
||
248 | oxnas->refsrc = NULL; |
||
249 | oxnas->phyref = NULL; |
||
250 | } |
||
251 | |||
252 | rstc = devm_reset_control_get(&ofdev->dev, "host"); |
||
253 | if (IS_ERR(rstc)) { |
||
254 | err = PTR_ERR(rstc); |
||
255 | goto err_rst; |
||
256 | } |
||
257 | oxnas->rst_host = rstc; |
||
258 | |||
259 | rstc = devm_reset_control_get(&ofdev->dev, "phya"); |
||
260 | if (IS_ERR(rstc)) { |
||
261 | err = PTR_ERR(rstc); |
||
262 | goto err_rst; |
||
263 | } |
||
264 | oxnas->rst_phya = rstc; |
||
265 | |||
266 | rstc = devm_reset_control_get(&ofdev->dev, "phyb"); |
||
267 | if (IS_ERR(rstc)) { |
||
268 | err = PTR_ERR(rstc); |
||
269 | goto err_rst; |
||
270 | } |
||
271 | oxnas->rst_phyb = rstc; |
||
272 | |||
273 | irq = irq_of_parse_and_map(np, 0); |
||
274 | if (!irq) { |
||
275 | dev_err(&ofdev->dev, "irq_of_parse_and_map failed\n"); |
||
276 | err = -EBUSY; |
||
277 | goto err_irq; |
||
278 | } |
||
279 | |||
280 | hcd->has_tt = 1; |
||
281 | ehci = hcd_to_ehci(hcd); |
||
282 | ehci->caps = hcd->regs; |
||
283 | |||
284 | start_oxnas_usb_ehci(oxnas); |
||
285 | |||
286 | err = usb_add_hcd(hcd, irq, IRQF_SHARED); |
||
287 | if (err) |
||
288 | goto err_hcd; |
||
289 | |||
290 | return 0; |
||
291 | |||
292 | err_hcd: |
||
293 | stop_oxnas_usb_ehci(oxnas); |
||
294 | err_irq: |
||
295 | err_rst: |
||
296 | if (oxnas->phyref) |
||
297 | clk_put(oxnas->phyref); |
||
298 | err_phyref: |
||
299 | if (oxnas->refsrc) |
||
300 | clk_put(oxnas->refsrc); |
||
301 | err_refsrc: |
||
302 | clk_put(oxnas->clk); |
||
303 | err_syscon: |
||
304 | err_clk: |
||
305 | err_ioremap: |
||
306 | err_res: |
||
307 | usb_put_hcd(hcd); |
||
308 | |||
309 | return err; |
||
310 | } |
||
311 | |||
312 | static int ehci_oxnas_drv_remove(struct platform_device *pdev) |
||
313 | { |
||
314 | struct usb_hcd *hcd = platform_get_drvdata(pdev); |
||
315 | struct oxnas_hcd *oxnas = (struct oxnas_hcd *)hcd_to_ehci(hcd)->priv; |
||
316 | |||
317 | usb_remove_hcd(hcd); |
||
318 | if (oxnas->use_pllb) { |
||
319 | clk_disable_unprepare(oxnas->phyref); |
||
320 | clk_put(oxnas->phyref); |
||
321 | clk_disable_unprepare(oxnas->refsrc); |
||
322 | clk_put(oxnas->refsrc); |
||
323 | } |
||
324 | clk_disable_unprepare(oxnas->clk); |
||
325 | usb_put_hcd(hcd); |
||
326 | |||
327 | return 0; |
||
328 | } |
||
329 | |||
330 | static const struct of_device_id oxnas_ehci_dt_ids[] = { |
||
331 | { .compatible = "plxtech,nas782x-ehci" }, |
||
332 | { /* sentinel */ } |
||
333 | }; |
||
334 | |||
335 | MODULE_DEVICE_TABLE(of, oxnas_ehci_dt_ids); |
||
336 | |||
337 | static struct platform_driver ehci_oxnas_driver = { |
||
338 | .probe = ehci_oxnas_drv_probe, |
||
339 | .remove = ehci_oxnas_drv_remove, |
||
340 | .shutdown = usb_hcd_platform_shutdown, |
||
341 | .driver.name = "oxnas-ehci", |
||
342 | .driver.of_match_table = oxnas_ehci_dt_ids, |
||
343 | }; |
||
344 | |||
345 | static const struct ehci_driver_overrides oxnas_overrides __initconst = { |
||
346 | .reset = ehci_oxnas_reset, |
||
347 | .extra_priv_size = sizeof(struct oxnas_hcd), |
||
348 | }; |
||
349 | |||
350 | static int __init ehci_oxnas_init(void) |
||
351 | { |
||
352 | if (usb_disabled()) |
||
353 | return -ENODEV; |
||
354 | |||
355 | ehci_init_driver(&oxnas_hc_driver, &oxnas_overrides); |
||
356 | return platform_driver_register(&ehci_oxnas_driver); |
||
357 | } |
||
358 | module_init(ehci_oxnas_init); |
||
359 | |||
360 | static void __exit ehci_oxnas_cleanup(void) |
||
361 | { |
||
362 | platform_driver_unregister(&ehci_oxnas_driver); |
||
363 | } |
||
364 | module_exit(ehci_oxnas_cleanup); |
||
365 | |||
366 | MODULE_DESCRIPTION(DRIVER_DESC); |
||
367 | MODULE_ALIAS("platform:oxnas-ehci"); |
||
368 | MODULE_LICENSE("GPL"); |