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 <vbarshak@ru.mvista.com> 8 * 9 * Based on "ehci-ppc-soc.c" by Stefan Roese <sr@denx.de> 10 * and "ohci-ppc-of.c" by Sylvain Munaut <tnt@246tNt.com> 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 if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) { 118 printk(KERN_ERR "%s: request_mem_region failed\n", __FILE__); 119 rv = -EBUSY; 120 goto err_rmr; 121 } 122 123 irq = irq_of_parse_and_map(dn, 0); 124 if (irq == NO_IRQ) { 125 printk(KERN_ERR "%s: irq_of_parse_and_map failed\n", __FILE__); 126 rv = -EBUSY; 127 goto err_irq; 128 } 129 130 hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len); 131 if (!hcd->regs) { 132 printk(KERN_ERR "%s: ioremap failed\n", __FILE__); 133 rv = -ENOMEM; 134 goto err_ioremap; 135 } 136 137 ehci = hcd_to_ehci(hcd); 138 np = of_find_compatible_node(NULL, NULL, "ibm,usb-ohci-440epx"); 139 if (np != NULL) { 140 /* claim we really affected by usb23 erratum */ 141 if (!of_address_to_resource(np, 0, &res)) 142 ehci->ohci_hcctrl_reg = ioremap(res.start + 143 OHCI_HCCTRL_OFFSET, OHCI_HCCTRL_LEN); 144 else 145 pr_debug("%s: no ohci offset in fdt\n", __FILE__); 146 if (!ehci->ohci_hcctrl_reg) { 147 pr_debug("%s: ioremap for ohci hcctrl failed\n", __FILE__); 148 } else { 149 ehci->has_amcc_usb23 = 1; 150 } 151 } 152 153 if (of_get_property(dn, "big-endian", NULL)) { 154 ehci->big_endian_mmio = 1; 155 ehci->big_endian_desc = 1; 156 } 157 if (of_get_property(dn, "big-endian-regs", NULL)) 158 ehci->big_endian_mmio = 1; 159 if (of_get_property(dn, "big-endian-desc", NULL)) 160 ehci->big_endian_desc = 1; 161 162 ehci->caps = hcd->regs; 163 164 if (of_device_is_compatible(dn, "ibm,usb-ehci-440epx")) { 165 rv = ppc44x_enable_bmt(dn); 166 ehci_dbg(ehci, "Break Memory Transfer (BMT) is %senabled!\n", 167 rv ? "NOT ": ""); 168 } 169 170 rv = usb_add_hcd(hcd, irq, 0); 171 if (rv) 172 goto err_ehci; 173 174 return 0; 175 176 err_ehci: 177 if (ehci->has_amcc_usb23) 178 iounmap(ehci->ohci_hcctrl_reg); 179 iounmap(hcd->regs); 180 err_ioremap: 181 irq_dispose_mapping(irq); 182 err_irq: 183 release_mem_region(hcd->rsrc_start, hcd->rsrc_len); 184 err_rmr: 185 usb_put_hcd(hcd); 186 187 return rv; 188 } 189 190 191 static int ehci_hcd_ppc_of_remove(struct platform_device *op) 192 { 193 struct usb_hcd *hcd = dev_get_drvdata(&op->dev); 194 struct ehci_hcd *ehci = hcd_to_ehci(hcd); 195 196 struct device_node *np; 197 struct resource res; 198 199 dev_set_drvdata(&op->dev, NULL); 200 201 dev_dbg(&op->dev, "stopping PPC-OF USB Controller\n"); 202 203 usb_remove_hcd(hcd); 204 205 iounmap(hcd->regs); 206 irq_dispose_mapping(hcd->irq); 207 release_mem_region(hcd->rsrc_start, hcd->rsrc_len); 208 209 /* use request_mem_region to test if the ohci driver is loaded. if so 210 * ensure the ohci core is operational. 211 */ 212 if (ehci->has_amcc_usb23) { 213 np = of_find_compatible_node(NULL, NULL, "ibm,usb-ohci-440epx"); 214 if (np != NULL) { 215 if (!of_address_to_resource(np, 0, &res)) 216 if (!request_mem_region(res.start, 217 0x4, hcd_name)) 218 set_ohci_hcfs(ehci, 1); 219 else 220 release_mem_region(res.start, 0x4); 221 else 222 pr_debug("%s: no ohci offset in fdt\n", __FILE__); 223 of_node_put(np); 224 } 225 226 iounmap(ehci->ohci_hcctrl_reg); 227 } 228 usb_put_hcd(hcd); 229 230 return 0; 231 } 232 233 234 static void ehci_hcd_ppc_of_shutdown(struct platform_device *op) 235 { 236 struct usb_hcd *hcd = dev_get_drvdata(&op->dev); 237 238 if (hcd->driver->shutdown) 239 hcd->driver->shutdown(hcd); 240 } 241 242 243 static const struct of_device_id ehci_hcd_ppc_of_match[] = { 244 { 245 .compatible = "usb-ehci", 246 }, 247 {}, 248 }; 249 MODULE_DEVICE_TABLE(of, ehci_hcd_ppc_of_match); 250 251 252 static struct platform_driver ehci_hcd_ppc_of_driver = { 253 .probe = ehci_hcd_ppc_of_probe, 254 .remove = ehci_hcd_ppc_of_remove, 255 .shutdown = ehci_hcd_ppc_of_shutdown, 256 .driver = { 257 .name = "ppc-of-ehci", 258 .owner = THIS_MODULE, 259 .of_match_table = ehci_hcd_ppc_of_match, 260 }, 261 }; 262