aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/host/ehci-ppc-of.c
diff options
context:
space:
mode:
authorVitaly Bordug <vitb@kernel.crashing.org>2008-11-09 13:43:30 -0500
committerGreg Kroah-Hartman <gregkh@suse.de>2009-01-07 12:59:52 -0500
commit796bcae7361c28cf825780f6f1aac9dd3411394e (patch)
tree3fe4ee89e373bf29f868d70430098b7dc625c16f /drivers/usb/host/ehci-ppc-of.c
parentcd40c4c45eaedc289d2e1cc33b18a49a211f0f82 (diff)
USB: powerpc: Workaround for the PPC440EPX USBH_23 errata [take 3]
A published errata for ppc440epx states, that when running Linux with both EHCI and OHCI modules loaded, the EHCI module experiences a fatal error when a high-speed device is connected to the USB2.0, and functions normally if OHCI module is not loaded. There used to be recommendation to use only hi-speed or full-speed devices with specific conditions, when respective module was unloaded. Later, it was observed that ohci suspend is enough to keep things going, and it was turned into workaround, as explained below. Quote from original descriprion: The 440EPx USB 2.0 Host controller is an EHCI compliant controller. In USB 2.0 Host controllers, each EHCI controller has one or more companion controllers, which may be OHCI or UHCI. An USB 2.0 Host controller will contain one or more ports. For each port, only one of the controllers is connected at any one time. In the 440EPx, there is only one OHCI companion controller, and only one USB 2.0 Host port. All ports on an USB 2.0 controller default to the companion controller. If you load only an ohci driver, it will have control of the ports and any deviceplugged in will operate, although high speed devices will be forced to operate at full speed. When an ehci driver is loaded, it explicitly takes control of the ports. If there is a device connected, and / or every time there is a new device connected, the ehci driver determines if the device is high speed or not. If it is high speed, the driver retains control of the port. If it is not, the driver explicitly gives the companion controller control of the port. The is a software workaround that uses Initial version of the software workaround was posted to linux-usb-devel: http://www.mail-archive.com/linux-usb-devel@lists.sourceforge.net/msg54019.html and later available from amcc.com: http://www.amcc.com/Embedded/Downloads/download.html?cat=1&family=15&ins=2 The patch below is generally based on the latter, but reworked to powerpc/of_device USB drivers, and uses a few devicetree inquiries to get rid of (some) hardcoded defines. Signed-off-by: Vitaly Bordug <vitb@kernel.crashing.org> Signed-off-by: Stefan Roese <sr@denx.de> Cc: David Brownell <dbrownell@users.sourceforge.net> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb/host/ehci-ppc-of.c')
-rw-r--r--drivers/usb/host/ehci-ppc-of.c45
1 files changed, 44 insertions, 1 deletions
diff --git a/drivers/usb/host/ehci-ppc-of.c b/drivers/usb/host/ehci-ppc-of.c
index b018deed2e8f..ef732b704f53 100644
--- a/drivers/usb/host/ehci-ppc-of.c
+++ b/drivers/usb/host/ehci-ppc-of.c
@@ -107,11 +107,13 @@ ehci_hcd_ppc_of_probe(struct of_device *op, const struct of_device_id *match)
107{ 107{
108 struct device_node *dn = op->node; 108 struct device_node *dn = op->node;
109 struct usb_hcd *hcd; 109 struct usb_hcd *hcd;
110 struct ehci_hcd *ehci; 110 struct ehci_hcd *ehci = NULL;
111 struct resource res; 111 struct resource res;
112 int irq; 112 int irq;
113 int rv; 113 int rv;
114 114
115 struct device_node *np;
116
115 if (usb_disabled()) 117 if (usb_disabled())
116 return -ENODEV; 118 return -ENODEV;
117 119
@@ -149,6 +151,20 @@ ehci_hcd_ppc_of_probe(struct of_device *op, const struct of_device_id *match)
149 } 151 }
150 152
151 ehci = hcd_to_ehci(hcd); 153 ehci = hcd_to_ehci(hcd);
154 np = of_find_compatible_node(NULL, NULL, "ibm,usb-ohci-440epx");
155 if (np != NULL) {
156 /* claim we really affected by usb23 erratum */
157 if (!of_address_to_resource(np, 0, &res))
158 ehci->ohci_hcctrl_reg = ioremap(res.start +
159 OHCI_HCCTRL_OFFSET, OHCI_HCCTRL_LEN);
160 else
161 pr_debug(__FILE__ ": no ohci offset in fdt\n");
162 if (!ehci->ohci_hcctrl_reg) {
163 pr_debug(__FILE__ ": ioremap for ohci hcctrl failed\n");
164 } else {
165 ehci->has_amcc_usb23 = 1;
166 }
167 }
152 168
153 if (of_get_property(dn, "big-endian", NULL)) { 169 if (of_get_property(dn, "big-endian", NULL)) {
154 ehci->big_endian_mmio = 1; 170 ehci->big_endian_mmio = 1;
@@ -181,6 +197,9 @@ err_ioremap:
181 irq_dispose_mapping(irq); 197 irq_dispose_mapping(irq);
182err_irq: 198err_irq:
183 release_mem_region(hcd->rsrc_start, hcd->rsrc_len); 199 release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
200
201 if (ehci->has_amcc_usb23)
202 iounmap(ehci->ohci_hcctrl_reg);
184err_rmr: 203err_rmr:
185 usb_put_hcd(hcd); 204 usb_put_hcd(hcd);
186 205
@@ -191,6 +210,11 @@ err_rmr:
191static int ehci_hcd_ppc_of_remove(struct of_device *op) 210static int ehci_hcd_ppc_of_remove(struct of_device *op)
192{ 211{
193 struct usb_hcd *hcd = dev_get_drvdata(&op->dev); 212 struct usb_hcd *hcd = dev_get_drvdata(&op->dev);
213 struct ehci_hcd *ehci = hcd_to_ehci(hcd);
214
215 struct device_node *np;
216 struct resource res;
217
194 dev_set_drvdata(&op->dev, NULL); 218 dev_set_drvdata(&op->dev, NULL);
195 219
196 dev_dbg(&op->dev, "stopping PPC-OF USB Controller\n"); 220 dev_dbg(&op->dev, "stopping PPC-OF USB Controller\n");
@@ -201,6 +225,25 @@ static int ehci_hcd_ppc_of_remove(struct of_device *op)
201 irq_dispose_mapping(hcd->irq); 225 irq_dispose_mapping(hcd->irq);
202 release_mem_region(hcd->rsrc_start, hcd->rsrc_len); 226 release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
203 227
228 /* use request_mem_region to test if the ohci driver is loaded. if so
229 * ensure the ohci core is operational.
230 */
231 if (ehci->has_amcc_usb23) {
232 np = of_find_compatible_node(NULL, NULL, "ibm,usb-ohci-440epx");
233 if (np != NULL) {
234 if (!of_address_to_resource(np, 0, &res))
235 if (!request_mem_region(res.start,
236 0x4, hcd_name))
237 set_ohci_hcfs(ehci, 1);
238 else
239 release_mem_region(res.start, 0x4);
240 else
241 pr_debug(__FILE__ ": no ohci offset in fdt\n");
242 of_node_put(np);
243 }
244
245 iounmap(ehci->ohci_hcctrl_reg);
246 }
204 usb_put_hcd(hcd); 247 usb_put_hcd(hcd);
205 248
206 return 0; 249 return 0;