ehci-ppc-of.c (5336B)
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 25static 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) 78static int 79ppc44x_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 94static 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 == NO_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 } 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_ioremap; 173 174 device_wakeup_enable(hcd->self.controller); 175 return 0; 176 177err_ioremap: 178 irq_dispose_mapping(irq); 179err_irq: 180 usb_put_hcd(hcd); 181 182 return rv; 183} 184 185 186static int ehci_hcd_ppc_of_remove(struct platform_device *op) 187{ 188 struct usb_hcd *hcd = platform_get_drvdata(op); 189 struct ehci_hcd *ehci = hcd_to_ehci(hcd); 190 191 struct device_node *np; 192 struct resource res; 193 194 dev_dbg(&op->dev, "stopping PPC-OF USB Controller\n"); 195 196 usb_remove_hcd(hcd); 197 198 irq_dispose_mapping(hcd->irq); 199 200 /* use request_mem_region to test if the ohci driver is loaded. if so 201 * ensure the ohci core is operational. 202 */ 203 if (ehci->has_amcc_usb23) { 204 np = of_find_compatible_node(NULL, NULL, "ibm,usb-ohci-440epx"); 205 if (np != NULL) { 206 if (!of_address_to_resource(np, 0, &res)) 207 if (!request_mem_region(res.start, 208 0x4, hcd_name)) 209 set_ohci_hcfs(ehci, 1); 210 else 211 release_mem_region(res.start, 0x4); 212 else 213 pr_debug("%s: no ohci offset in fdt\n", __FILE__); 214 of_node_put(np); 215 } 216 } 217 usb_put_hcd(hcd); 218 219 return 0; 220} 221 222 223static const struct of_device_id ehci_hcd_ppc_of_match[] = { 224 { 225 .compatible = "usb-ehci", 226 }, 227 {}, 228}; 229MODULE_DEVICE_TABLE(of, ehci_hcd_ppc_of_match); 230 231 232static struct platform_driver ehci_hcd_ppc_of_driver = { 233 .probe = ehci_hcd_ppc_of_probe, 234 .remove = ehci_hcd_ppc_of_remove, 235 .shutdown = usb_hcd_platform_shutdown, 236 .driver = { 237 .name = "ppc-of-ehci", 238 .of_match_table = ehci_hcd_ppc_of_match, 239 }, 240};