1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * Driver for Aeroflex Gaisler GRLIB GRUSBHC EHCI host controller 4 * 5 * GRUSBHC is typically found on LEON/GRLIB SoCs 6 * 7 * (c) Jan Andersson <jan@gaisler.com> 8 * 9 * Based on ehci-ppc-of.c which is: 10 * (c) Valentine Barshak <vbarshak@ru.mvista.com> 11 * and in turn based on "ehci-ppc-soc.c" by Stefan Roese <sr@denx.de> 12 * and "ohci-ppc-of.c" by Sylvain Munaut <tnt@246tNt.com> 13 */ 14 15 #include <linux/err.h> 16 #include <linux/signal.h> 17 18 #include <linux/of_irq.h> 19 #include <linux/of_address.h> 20 #include <linux/of_platform.h> 21 22 #define GRUSBHC_HCIVERSION 0x0100 /* Known value of cap. reg. HCIVERSION */ 23 24 static const struct hc_driver ehci_grlib_hc_driver = { 25 .description = hcd_name, 26 .product_desc = "GRLIB GRUSBHC EHCI", 27 .hcd_priv_size = sizeof(struct ehci_hcd), 28 29 /* 30 * generic hardware linkage 31 */ 32 .irq = ehci_irq, 33 .flags = HCD_MEMORY | HCD_DMA | HCD_USB2 | HCD_BH, 34 35 /* 36 * basic lifecycle operations 37 */ 38 .reset = ehci_setup, 39 .start = ehci_run, 40 .stop = ehci_stop, 41 .shutdown = ehci_shutdown, 42 43 /* 44 * managing i/o requests and associated device resources 45 */ 46 .urb_enqueue = ehci_urb_enqueue, 47 .urb_dequeue = ehci_urb_dequeue, 48 .endpoint_disable = ehci_endpoint_disable, 49 .endpoint_reset = ehci_endpoint_reset, 50 51 /* 52 * scheduling support 53 */ 54 .get_frame_number = ehci_get_frame, 55 56 /* 57 * root hub support 58 */ 59 .hub_status_data = ehci_hub_status_data, 60 .hub_control = ehci_hub_control, 61 #ifdef CONFIG_PM 62 .bus_suspend = ehci_bus_suspend, 63 .bus_resume = ehci_bus_resume, 64 #endif 65 .relinquish_port = ehci_relinquish_port, 66 .port_handed_over = ehci_port_handed_over, 67 68 .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, 69 }; 70 71 72 static int ehci_hcd_grlib_probe(struct platform_device *op) 73 { 74 struct device_node *dn = op->dev.of_node; 75 struct usb_hcd *hcd; 76 struct ehci_hcd *ehci = NULL; 77 struct resource res; 78 u32 hc_capbase; 79 int irq; 80 int rv; 81 82 if (usb_disabled()) 83 return -ENODEV; 84 85 dev_dbg(&op->dev, "initializing GRUSBHC EHCI USB Controller\n"); 86 87 rv = of_address_to_resource(dn, 0, &res); 88 if (rv) 89 return rv; 90 91 /* usb_create_hcd requires dma_mask != NULL */ 92 op->dev.dma_mask = &op->dev.coherent_dma_mask; 93 hcd = usb_create_hcd(&ehci_grlib_hc_driver, &op->dev, 94 "GRUSBHC EHCI USB"); 95 if (!hcd) 96 return -ENOMEM; 97 98 hcd->rsrc_start = res.start; 99 hcd->rsrc_len = resource_size(&res); 100 101 irq = irq_of_parse_and_map(dn, 0); 102 if (!irq) { 103 dev_err(&op->dev, "%s: irq_of_parse_and_map failed\n", 104 __FILE__); 105 rv = -EBUSY; 106 goto err_irq; 107 } 108 109 hcd->regs = devm_ioremap_resource(&op->dev, &res); 110 if (IS_ERR(hcd->regs)) { 111 rv = PTR_ERR(hcd->regs); 112 goto err_ioremap; 113 } 114 115 ehci = hcd_to_ehci(hcd); 116 117 ehci->caps = hcd->regs; 118 119 /* determine endianness of this implementation */ 120 hc_capbase = ehci_readl(ehci, &ehci->caps->hc_capbase); 121 if (HC_VERSION(ehci, hc_capbase) != GRUSBHC_HCIVERSION) { 122 ehci->big_endian_mmio = 1; 123 ehci->big_endian_desc = 1; 124 ehci->big_endian_capbase = 1; 125 } 126 127 rv = usb_add_hcd(hcd, irq, 0); 128 if (rv) 129 goto err_ioremap; 130 131 device_wakeup_enable(hcd->self.controller); 132 return 0; 133 134 err_ioremap: 135 irq_dispose_mapping(irq); 136 err_irq: 137 usb_put_hcd(hcd); 138 139 return rv; 140 } 141 142 143 static int ehci_hcd_grlib_remove(struct platform_device *op) 144 { 145 struct usb_hcd *hcd = platform_get_drvdata(op); 146 147 dev_dbg(&op->dev, "stopping GRLIB GRUSBHC EHCI USB Controller\n"); 148 149 usb_remove_hcd(hcd); 150 151 irq_dispose_mapping(hcd->irq); 152 153 usb_put_hcd(hcd); 154 155 return 0; 156 } 157 158 159 static const struct of_device_id ehci_hcd_grlib_of_match[] = { 160 { 161 .name = "GAISLER_EHCI", 162 }, 163 { 164 .name = "01_026", 165 }, 166 {}, 167 }; 168 MODULE_DEVICE_TABLE(of, ehci_hcd_grlib_of_match); 169 170 171 static struct platform_driver ehci_grlib_driver = { 172 .probe = ehci_hcd_grlib_probe, 173 .remove = ehci_hcd_grlib_remove, 174 .shutdown = usb_hcd_platform_shutdown, 175 .driver = { 176 .name = "grlib-ehci", 177 .of_match_table = ehci_hcd_grlib_of_match, 178 }, 179 }; 180