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