Commit | Line | Data |
---|---|---|
9be03929 JA |
1 | /* |
2 | * Driver for Aeroflex Gaisler GRLIB GRUSBHC EHCI host controller | |
3 | * | |
4 | * GRUSBHC is typically found on LEON/GRLIB SoCs | |
5 | * | |
6 | * (c) Jan Andersson <jan@gaisler.com> | |
7 | * | |
8 | * Based on ehci-ppc-of.c which is: | |
9 | * (c) Valentine Barshak <vbarshak@ru.mvista.com> | |
10 | * and in turn based on "ehci-ppc-soc.c" by Stefan Roese <sr@denx.de> | |
11 | * and "ohci-ppc-of.c" by Sylvain Munaut <tnt@246tNt.com> | |
12 | * | |
13 | * This program is free software; you can redistribute it and/or modify it | |
14 | * under the terms of the GNU General Public License as published by the | |
15 | * Free Software Foundation; either version 2 of the License, or (at your | |
16 | * option) any later version. | |
17 | * | |
18 | * This program is distributed in the hope that it will be useful, but | |
19 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | |
20 | * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
21 | * for more details. | |
22 | * | |
23 | * You should have received a copy of the GNU General Public License | |
24 | * along with this program; if not, write to the Free Software Foundation, | |
25 | * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
26 | */ | |
27 | ||
148e1134 | 28 | #include <linux/err.h> |
9be03929 JA |
29 | #include <linux/signal.h> |
30 | ||
31 | #include <linux/of_irq.h> | |
32 | #include <linux/of_address.h> | |
33 | #include <linux/of_platform.h> | |
34 | ||
35 | #define GRUSBHC_HCIVERSION 0x0100 /* Known value of cap. reg. HCIVERSION */ | |
36 | ||
9be03929 JA |
37 | static const struct hc_driver ehci_grlib_hc_driver = { |
38 | .description = hcd_name, | |
39 | .product_desc = "GRLIB GRUSBHC EHCI", | |
40 | .hcd_priv_size = sizeof(struct ehci_hcd), | |
41 | ||
42 | /* | |
43 | * generic hardware linkage | |
44 | */ | |
45 | .irq = ehci_irq, | |
c04ee4b1 | 46 | .flags = HCD_MEMORY | HCD_USB2 | HCD_BH, |
9be03929 JA |
47 | |
48 | /* | |
49 | * basic lifecycle operations | |
50 | */ | |
c73cee71 | 51 | .reset = ehci_setup, |
9be03929 JA |
52 | .start = ehci_run, |
53 | .stop = ehci_stop, | |
54 | .shutdown = ehci_shutdown, | |
55 | ||
56 | /* | |
57 | * managing i/o requests and associated device resources | |
58 | */ | |
59 | .urb_enqueue = ehci_urb_enqueue, | |
60 | .urb_dequeue = ehci_urb_dequeue, | |
61 | .endpoint_disable = ehci_endpoint_disable, | |
62 | .endpoint_reset = ehci_endpoint_reset, | |
63 | ||
64 | /* | |
65 | * scheduling support | |
66 | */ | |
67 | .get_frame_number = ehci_get_frame, | |
68 | ||
69 | /* | |
70 | * root hub support | |
71 | */ | |
72 | .hub_status_data = ehci_hub_status_data, | |
73 | .hub_control = ehci_hub_control, | |
74 | #ifdef CONFIG_PM | |
75 | .bus_suspend = ehci_bus_suspend, | |
76 | .bus_resume = ehci_bus_resume, | |
77 | #endif | |
78 | .relinquish_port = ehci_relinquish_port, | |
79 | .port_handed_over = ehci_port_handed_over, | |
80 | ||
81 | .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, | |
82 | }; | |
83 | ||
84 | ||
41ac7b3a | 85 | static int ehci_hcd_grlib_probe(struct platform_device *op) |
9be03929 JA |
86 | { |
87 | struct device_node *dn = op->dev.of_node; | |
88 | struct usb_hcd *hcd; | |
89 | struct ehci_hcd *ehci = NULL; | |
90 | struct resource res; | |
91 | u32 hc_capbase; | |
92 | int irq; | |
93 | int rv; | |
94 | ||
95 | if (usb_disabled()) | |
96 | return -ENODEV; | |
97 | ||
98 | dev_dbg(&op->dev, "initializing GRUSBHC EHCI USB Controller\n"); | |
99 | ||
100 | rv = of_address_to_resource(dn, 0, &res); | |
101 | if (rv) | |
102 | return rv; | |
103 | ||
104 | /* usb_create_hcd requires dma_mask != NULL */ | |
105 | op->dev.dma_mask = &op->dev.coherent_dma_mask; | |
106 | hcd = usb_create_hcd(&ehci_grlib_hc_driver, &op->dev, | |
107 | "GRUSBHC EHCI USB"); | |
108 | if (!hcd) | |
109 | return -ENOMEM; | |
110 | ||
111 | hcd->rsrc_start = res.start; | |
28f65c11 | 112 | hcd->rsrc_len = resource_size(&res); |
9be03929 | 113 | |
9be03929 JA |
114 | irq = irq_of_parse_and_map(dn, 0); |
115 | if (irq == NO_IRQ) { | |
fc32f116 JH |
116 | dev_err(&op->dev, "%s: irq_of_parse_and_map failed\n", |
117 | __FILE__); | |
9be03929 JA |
118 | rv = -EBUSY; |
119 | goto err_irq; | |
120 | } | |
121 | ||
148e1134 TR |
122 | hcd->regs = devm_ioremap_resource(&op->dev, &res); |
123 | if (IS_ERR(hcd->regs)) { | |
124 | rv = PTR_ERR(hcd->regs); | |
9be03929 JA |
125 | goto err_ioremap; |
126 | } | |
127 | ||
128 | ehci = hcd_to_ehci(hcd); | |
129 | ||
130 | ehci->caps = hcd->regs; | |
131 | ||
132 | /* determine endianness of this implementation */ | |
133 | hc_capbase = ehci_readl(ehci, &ehci->caps->hc_capbase); | |
134 | if (HC_VERSION(ehci, hc_capbase) != GRUSBHC_HCIVERSION) { | |
135 | ehci->big_endian_mmio = 1; | |
136 | ehci->big_endian_desc = 1; | |
137 | ehci->big_endian_capbase = 1; | |
138 | } | |
139 | ||
9be03929 JA |
140 | rv = usb_add_hcd(hcd, irq, 0); |
141 | if (rv) | |
436f2719 | 142 | goto err_ioremap; |
9be03929 | 143 | |
3c9740a1 | 144 | device_wakeup_enable(hcd->self.controller); |
9be03929 JA |
145 | return 0; |
146 | ||
9be03929 JA |
147 | err_ioremap: |
148 | irq_dispose_mapping(irq); | |
149 | err_irq: | |
9be03929 JA |
150 | usb_put_hcd(hcd); |
151 | ||
152 | return rv; | |
153 | } | |
154 | ||
155 | ||
156 | static int ehci_hcd_grlib_remove(struct platform_device *op) | |
157 | { | |
477527ba | 158 | struct usb_hcd *hcd = platform_get_drvdata(op); |
9be03929 JA |
159 | |
160 | dev_dbg(&op->dev, "stopping GRLIB GRUSBHC EHCI USB Controller\n"); | |
161 | ||
162 | usb_remove_hcd(hcd); | |
163 | ||
9be03929 | 164 | irq_dispose_mapping(hcd->irq); |
9be03929 JA |
165 | |
166 | usb_put_hcd(hcd); | |
167 | ||
168 | return 0; | |
169 | } | |
170 | ||
171 | ||
9be03929 JA |
172 | static const struct of_device_id ehci_hcd_grlib_of_match[] = { |
173 | { | |
174 | .name = "GAISLER_EHCI", | |
175 | }, | |
176 | { | |
177 | .name = "01_026", | |
178 | }, | |
179 | {}, | |
180 | }; | |
181 | MODULE_DEVICE_TABLE(of, ehci_hcd_grlib_of_match); | |
182 | ||
183 | ||
184 | static struct platform_driver ehci_grlib_driver = { | |
185 | .probe = ehci_hcd_grlib_probe, | |
186 | .remove = ehci_hcd_grlib_remove, | |
aaf6b52d | 187 | .shutdown = usb_hcd_platform_shutdown, |
9be03929 JA |
188 | .driver = { |
189 | .name = "grlib-ehci", | |
9be03929 JA |
190 | .of_match_table = ehci_hcd_grlib_of_match, |
191 | }, | |
192 | }; |