Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
ehci-ppc-of.c
Go to the documentation of this file.
1 /*
2  * EHCI HCD (Host Controller Driver) for USB.
3  *
4  * Bus Glue for PPC On-Chip EHCI driver on the of_platform bus
5  * Tested on AMCC PPC 440EPx
6  *
7  * Valentine Barshak <[email protected]>
8  *
9  * Based on "ehci-ppc-soc.c" by Stefan Roese <[email protected]>
10  * and "ohci-ppc-of.c" by Sylvain Munaut <[email protected]>
11  *
12  * This file is licenced under the GPL.
13  */
14 
15 #include <linux/signal.h>
16 
17 #include <linux/of.h>
18 #include <linux/of_platform.h>
19 
20 
21 static const struct hc_driver ehci_ppc_of_hc_driver = {
22  .description = hcd_name,
23  .product_desc = "OF EHCI",
24  .hcd_priv_size = sizeof(struct ehci_hcd),
25 
26  /*
27  * generic hardware linkage
28  */
29  .irq = ehci_irq,
30  .flags = HCD_MEMORY | HCD_USB2,
31 
32  /*
33  * basic lifecycle operations
34  */
35  .reset = ehci_setup,
36  .start = ehci_run,
37  .stop = ehci_stop,
38  .shutdown = ehci_shutdown,
39 
40  /*
41  * managing i/o requests and associated device resources
42  */
43  .urb_enqueue = ehci_urb_enqueue,
44  .urb_dequeue = ehci_urb_dequeue,
45  .endpoint_disable = ehci_endpoint_disable,
46  .endpoint_reset = ehci_endpoint_reset,
47 
48  /*
49  * scheduling support
50  */
51  .get_frame_number = ehci_get_frame,
52 
53  /*
54  * root hub support
55  */
56  .hub_status_data = ehci_hub_status_data,
57  .hub_control = ehci_hub_control,
58 #ifdef CONFIG_PM
59  .bus_suspend = ehci_bus_suspend,
60  .bus_resume = ehci_bus_resume,
61 #endif
62  .relinquish_port = ehci_relinquish_port,
63  .port_handed_over = ehci_port_handed_over,
64 
65  .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete,
66 };
67 
68 
69 /*
70  * 440EPx Errata USBH_3
71  * Fix: Enable Break Memory Transfer (BMT) in INSNREG3
72  */
73 #define PPC440EPX_EHCI0_INSREG_BMT (0x1 << 0)
74 static int __devinit
75 ppc44x_enable_bmt(struct device_node *dn)
76 {
77  __iomem u32 *insreg_virt;
78 
79  insreg_virt = of_iomap(dn, 1);
80  if (!insreg_virt)
81  return -EINVAL;
82 
83  out_be32(insreg_virt + 3, PPC440EPX_EHCI0_INSREG_BMT);
84 
85  iounmap(insreg_virt);
86  return 0;
87 }
88 
89 
90 static int __devinit ehci_hcd_ppc_of_probe(struct platform_device *op)
91 {
92  struct device_node *dn = op->dev.of_node;
93  struct usb_hcd *hcd;
94  struct ehci_hcd *ehci = NULL;
95  struct resource res;
96  int irq;
97  int rv;
98 
99  struct device_node *np;
100 
101  if (usb_disabled())
102  return -ENODEV;
103 
104  dev_dbg(&op->dev, "initializing PPC-OF USB Controller\n");
105 
106  rv = of_address_to_resource(dn, 0, &res);
107  if (rv)
108  return rv;
109 
110  hcd = usb_create_hcd(&ehci_ppc_of_hc_driver, &op->dev, "PPC-OF USB");
111  if (!hcd)
112  return -ENOMEM;
113 
114  hcd->rsrc_start = res.start;
115  hcd->rsrc_len = resource_size(&res);
116 
117  irq = irq_of_parse_and_map(dn, 0);
118  if (irq == NO_IRQ) {
119  printk(KERN_ERR "%s: irq_of_parse_and_map failed\n", __FILE__);
120  rv = -EBUSY;
121  goto err_irq;
122  }
123 
124  hcd->regs = devm_request_and_ioremap(&op->dev, &res);
125  if (!hcd->regs) {
126  pr_err("%s: devm_request_and_ioremap failed\n", __FILE__);
127  rv = -ENOMEM;
128  goto err_ioremap;
129  }
130 
131  ehci = hcd_to_ehci(hcd);
132  np = of_find_compatible_node(NULL, NULL, "ibm,usb-ohci-440epx");
133  if (np != NULL) {
134  /* claim we really affected by usb23 erratum */
135  if (!of_address_to_resource(np, 0, &res))
136  ehci->ohci_hcctrl_reg =
137  devm_ioremap(&op->dev,
138  res.start + OHCI_HCCTRL_OFFSET,
140  else
141  pr_debug("%s: no ohci offset in fdt\n", __FILE__);
142  if (!ehci->ohci_hcctrl_reg) {
143  pr_debug("%s: ioremap for ohci hcctrl failed\n", __FILE__);
144  } else {
145  ehci->has_amcc_usb23 = 1;
146  }
147  }
148 
149  if (of_get_property(dn, "big-endian", NULL)) {
150  ehci->big_endian_mmio = 1;
151  ehci->big_endian_desc = 1;
152  }
153  if (of_get_property(dn, "big-endian-regs", NULL))
154  ehci->big_endian_mmio = 1;
155  if (of_get_property(dn, "big-endian-desc", NULL))
156  ehci->big_endian_desc = 1;
157 
158  ehci->caps = hcd->regs;
159 
160  if (of_device_is_compatible(dn, "ibm,usb-ehci-440epx")) {
161  rv = ppc44x_enable_bmt(dn);
162  ehci_dbg(ehci, "Break Memory Transfer (BMT) is %senabled!\n",
163  rv ? "NOT ": "");
164  }
165 
166  rv = usb_add_hcd(hcd, irq, 0);
167  if (rv)
168  goto err_ioremap;
169 
170  return 0;
171 
172 err_ioremap:
173  irq_dispose_mapping(irq);
174 err_irq:
175  usb_put_hcd(hcd);
176 
177  return rv;
178 }
179 
180 
181 static int ehci_hcd_ppc_of_remove(struct platform_device *op)
182 {
183  struct usb_hcd *hcd = dev_get_drvdata(&op->dev);
184  struct ehci_hcd *ehci = hcd_to_ehci(hcd);
185 
186  struct device_node *np;
187  struct resource res;
188 
189  dev_set_drvdata(&op->dev, NULL);
190 
191  dev_dbg(&op->dev, "stopping PPC-OF USB Controller\n");
192 
193  usb_remove_hcd(hcd);
194 
195  irq_dispose_mapping(hcd->irq);
196 
197  /* use request_mem_region to test if the ohci driver is loaded. if so
198  * ensure the ohci core is operational.
199  */
200  if (ehci->has_amcc_usb23) {
201  np = of_find_compatible_node(NULL, NULL, "ibm,usb-ohci-440epx");
202  if (np != NULL) {
203  if (!of_address_to_resource(np, 0, &res))
204  if (!request_mem_region(res.start,
205  0x4, hcd_name))
206  set_ohci_hcfs(ehci, 1);
207  else
208  release_mem_region(res.start, 0x4);
209  else
210  pr_debug("%s: no ohci offset in fdt\n", __FILE__);
211  of_node_put(np);
212  }
213  }
214  usb_put_hcd(hcd);
215 
216  return 0;
217 }
218 
219 
220 static void ehci_hcd_ppc_of_shutdown(struct platform_device *op)
221 {
222  struct usb_hcd *hcd = dev_get_drvdata(&op->dev);
223 
224  if (hcd->driver->shutdown)
225  hcd->driver->shutdown(hcd);
226 }
227 
228 
229 static const struct of_device_id ehci_hcd_ppc_of_match[] = {
230  {
231  .compatible = "usb-ehci",
232  },
233  {},
234 };
235 MODULE_DEVICE_TABLE(of, ehci_hcd_ppc_of_match);
236 
237 
238 static struct platform_driver ehci_hcd_ppc_of_driver = {
239  .probe = ehci_hcd_ppc_of_probe,
240  .remove = ehci_hcd_ppc_of_remove,
241  .shutdown = ehci_hcd_ppc_of_shutdown,
242  .driver = {
243  .name = "ppc-of-ehci",
244  .owner = THIS_MODULE,
245  .of_match_table = ehci_hcd_ppc_of_match,
246  },
247 };