Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

USB host: Adding USB ehci & ohci support for spear platform

This patch adds support for ehci and ohci controller in the SPEAr platform.

Changes since V2:
added clear_tt_buffer_complete in ehci_spear_hc_driver

Signed-off-by: Deepak Sikri <deepak.sikri@st.com>
Signed-off-by: Viresh Kumar <viresh.kumar@st.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>

authored by

Deepak Sikri and committed by
Greg Kroah-Hartman
c8c38de9 ad78acaf

+464
+2
drivers/usb/Kconfig
··· 41 41 default y if MFD_TC6393XB 42 42 default y if ARCH_W90X900 43 43 default y if ARCH_DAVINCI_DA8XX 44 + default y if PLAT_SPEAR 44 45 # PPC: 45 46 default y if STB03xxx 46 47 default y if PPC_MPC52xx ··· 68 67 default y if ARCH_MXC 69 68 default y if ARCH_OMAP3 70 69 default y if ARCH_VT8500 70 + default y if PLAT_SPEAR 71 71 default PCI 72 72 73 73 # ARM SA1111 chips have a non-PCI based "OHCI-compatible" USB host interface.
+5
drivers/usb/host/ehci-hcd.c
··· 1221 1221 #define PLATFORM_DRIVER vt8500_ehci_driver 1222 1222 #endif 1223 1223 1224 + #ifdef CONFIG_PLAT_SPEAR 1225 + #include "ehci-spear.c" 1226 + #define PLATFORM_DRIVER spear_ehci_hcd_driver 1227 + #endif 1228 + 1224 1229 #if !defined(PCI_DRIVER) && !defined(PLATFORM_DRIVER) && \ 1225 1230 !defined(PS3_SYSTEM_BUS_DRIVER) && !defined(OF_PLATFORM_DRIVER) && \ 1226 1231 !defined(XILINX_OF_PLATFORM_DRIVER)
+212
drivers/usb/host/ehci-spear.c
··· 1 + /* 2 + * Driver for EHCI HCD on SPEAR SOC 3 + * 4 + * Copyright (C) 2010 ST Micro Electronics, 5 + * Deepak Sikri <deepak.sikri@st.com> 6 + * 7 + * Based on various ehci-*.c drivers 8 + * 9 + * This file is subject to the terms and conditions of the GNU General Public 10 + * License. See the file COPYING in the main directory of this archive for 11 + * more details. 12 + */ 13 + 14 + #include <linux/platform_device.h> 15 + #include <linux/clk.h> 16 + 17 + struct spear_ehci { 18 + struct ehci_hcd ehci; 19 + struct clk *clk; 20 + }; 21 + 22 + #define to_spear_ehci(hcd) (struct spear_ehci *)hcd_to_ehci(hcd) 23 + 24 + static void spear_start_ehci(struct spear_ehci *ehci) 25 + { 26 + clk_enable(ehci->clk); 27 + } 28 + 29 + static void spear_stop_ehci(struct spear_ehci *ehci) 30 + { 31 + clk_disable(ehci->clk); 32 + } 33 + 34 + static int ehci_spear_setup(struct usb_hcd *hcd) 35 + { 36 + struct ehci_hcd *ehci = hcd_to_ehci(hcd); 37 + int retval = 0; 38 + 39 + /* registers start at offset 0x0 */ 40 + ehci->caps = hcd->regs; 41 + ehci->regs = hcd->regs + HC_LENGTH(ehci_readl(ehci, 42 + &ehci->caps->hc_capbase)); 43 + /* cache this readonly data; minimize chip reads */ 44 + ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); 45 + retval = ehci_halt(ehci); 46 + if (retval) 47 + return retval; 48 + 49 + retval = ehci_init(hcd); 50 + if (retval) 51 + return retval; 52 + 53 + ehci_reset(ehci); 54 + ehci_port_power(ehci, 0); 55 + 56 + return retval; 57 + } 58 + 59 + static const struct hc_driver ehci_spear_hc_driver = { 60 + .description = hcd_name, 61 + .product_desc = "SPEAr EHCI", 62 + .hcd_priv_size = sizeof(struct spear_ehci), 63 + 64 + /* generic hardware linkage */ 65 + .irq = ehci_irq, 66 + .flags = HCD_MEMORY | HCD_USB2, 67 + 68 + /* basic lifecycle operations */ 69 + .reset = ehci_spear_setup, 70 + .start = ehci_run, 71 + .stop = ehci_stop, 72 + .shutdown = ehci_shutdown, 73 + 74 + /* managing i/o requests and associated device resources */ 75 + .urb_enqueue = ehci_urb_enqueue, 76 + .urb_dequeue = ehci_urb_dequeue, 77 + .endpoint_disable = ehci_endpoint_disable, 78 + .endpoint_reset = ehci_endpoint_reset, 79 + 80 + /* scheduling support */ 81 + .get_frame_number = ehci_get_frame, 82 + 83 + /* root hub support */ 84 + .hub_status_data = ehci_hub_status_data, 85 + .hub_control = ehci_hub_control, 86 + .bus_suspend = ehci_bus_suspend, 87 + .bus_resume = ehci_bus_resume, 88 + .relinquish_port = ehci_relinquish_port, 89 + .port_handed_over = ehci_port_handed_over, 90 + .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, 91 + }; 92 + 93 + static int spear_ehci_hcd_drv_probe(struct platform_device *pdev) 94 + { 95 + struct usb_hcd *hcd ; 96 + struct spear_ehci *ehci; 97 + struct resource *res; 98 + struct clk *usbh_clk; 99 + const struct hc_driver *driver = &ehci_spear_hc_driver; 100 + int *pdata = pdev->dev.platform_data; 101 + int irq, retval; 102 + char clk_name[20] = "usbh_clk"; 103 + 104 + if (pdata == NULL) 105 + return -EFAULT; 106 + 107 + if (usb_disabled()) 108 + return -ENODEV; 109 + 110 + irq = platform_get_irq(pdev, 0); 111 + if (irq < 0) { 112 + retval = irq; 113 + goto fail_irq_get; 114 + } 115 + 116 + if (*pdata >= 0) 117 + sprintf(clk_name, "usbh.%01d_clk", *pdata); 118 + 119 + usbh_clk = clk_get(NULL, clk_name); 120 + if (IS_ERR(usbh_clk)) { 121 + dev_err(&pdev->dev, "Error getting interface clock\n"); 122 + retval = PTR_ERR(usbh_clk); 123 + goto fail_get_usbh_clk; 124 + } 125 + 126 + hcd = usb_create_hcd(driver, &pdev->dev, dev_name(&pdev->dev)); 127 + if (!hcd) { 128 + retval = -ENOMEM; 129 + goto fail_create_hcd; 130 + } 131 + 132 + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 133 + if (!res) { 134 + retval = -ENODEV; 135 + goto fail_request_resource; 136 + } 137 + 138 + hcd->rsrc_start = res->start; 139 + hcd->rsrc_len = resource_size(res); 140 + if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, 141 + driver->description)) { 142 + retval = -EBUSY; 143 + goto fail_request_resource; 144 + } 145 + 146 + hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len); 147 + if (hcd->regs == NULL) { 148 + dev_dbg(&pdev->dev, "error mapping memory\n"); 149 + retval = -ENOMEM; 150 + goto fail_ioremap; 151 + } 152 + 153 + ehci = (struct spear_ehci *)hcd_to_ehci(hcd); 154 + ehci->clk = usbh_clk; 155 + 156 + spear_start_ehci(ehci); 157 + retval = usb_add_hcd(hcd, irq, IRQF_SHARED | IRQF_DISABLED); 158 + if (retval) 159 + goto fail_add_hcd; 160 + 161 + return retval; 162 + 163 + fail_add_hcd: 164 + spear_stop_ehci(ehci); 165 + iounmap(hcd->regs); 166 + fail_ioremap: 167 + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); 168 + fail_request_resource: 169 + usb_put_hcd(hcd); 170 + fail_create_hcd: 171 + clk_put(usbh_clk); 172 + fail_get_usbh_clk: 173 + fail_irq_get: 174 + dev_err(&pdev->dev, "init fail, %d\n", retval); 175 + 176 + return retval ; 177 + } 178 + 179 + static int spear_ehci_hcd_drv_remove(struct platform_device *pdev) 180 + { 181 + struct usb_hcd *hcd = platform_get_drvdata(pdev); 182 + struct spear_ehci *ehci_p = to_spear_ehci(hcd); 183 + 184 + if (!hcd) 185 + return 0; 186 + if (in_interrupt()) 187 + BUG(); 188 + usb_remove_hcd(hcd); 189 + 190 + if (ehci_p->clk) 191 + spear_stop_ehci(ehci_p); 192 + iounmap(hcd->regs); 193 + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); 194 + usb_put_hcd(hcd); 195 + 196 + if (ehci_p->clk) 197 + clk_put(ehci_p->clk); 198 + 199 + return 0; 200 + } 201 + 202 + static struct platform_driver spear_ehci_hcd_driver = { 203 + .probe = spear_ehci_hcd_drv_probe, 204 + .remove = spear_ehci_hcd_drv_remove, 205 + .shutdown = usb_hcd_platform_shutdown, 206 + .driver = { 207 + .name = "spear-ehci", 208 + .bus = &platform_bus_type 209 + } 210 + }; 211 + 212 + MODULE_ALIAS("platform:spear-ehci");
+5
drivers/usb/host/ohci-hcd.c
··· 1081 1081 #define OF_PLATFORM_DRIVER ohci_hcd_ppc_of_driver 1082 1082 #endif 1083 1083 1084 + #ifdef CONFIG_PLAT_SPEAR 1085 + #include "ohci-spear.c" 1086 + #define PLATFORM_DRIVER spear_ohci_hcd_driver 1087 + #endif 1088 + 1084 1089 #ifdef CONFIG_PPC_PS3 1085 1090 #include "ohci-ps3.c" 1086 1091 #define PS3_SYSTEM_BUS_DRIVER ps3_ohci_driver
+240
drivers/usb/host/ohci-spear.c
··· 1 + /* 2 + * OHCI HCD (Host Controller Driver) for USB. 3 + * 4 + * Copyright (C) 2010 ST Microelectronics. 5 + * Deepak Sikri<deepak.sikri@st.com> 6 + * 7 + * Based on various ohci-*.c drivers 8 + * 9 + * This file is licensed under the terms of the GNU General Public 10 + * License version 2. This program is licensed "as is" without any 11 + * warranty of any kind, whether express or implied. 12 + */ 13 + 14 + #include <linux/signal.h> 15 + #include <linux/platform_device.h> 16 + #include <linux/clk.h> 17 + 18 + struct spear_ohci { 19 + struct ohci_hcd ohci; 20 + struct clk *clk; 21 + }; 22 + 23 + #define to_spear_ohci(hcd) (struct spear_ohci *)hcd_to_ohci(hcd) 24 + 25 + static void spear_start_ohci(struct spear_ohci *ohci) 26 + { 27 + clk_enable(ohci->clk); 28 + } 29 + 30 + static void spear_stop_ohci(struct spear_ohci *ohci) 31 + { 32 + clk_disable(ohci->clk); 33 + } 34 + 35 + static int __devinit ohci_spear_start(struct usb_hcd *hcd) 36 + { 37 + struct ohci_hcd *ohci = hcd_to_ohci(hcd); 38 + int ret; 39 + 40 + ret = ohci_init(ohci); 41 + if (ret < 0) 42 + return ret; 43 + ohci->regs = hcd->regs; 44 + 45 + ret = ohci_run(ohci); 46 + if (ret < 0) { 47 + dev_err(hcd->self.controller, "can't start\n"); 48 + ohci_stop(hcd); 49 + return ret; 50 + } 51 + 52 + create_debug_files(ohci); 53 + 54 + #ifdef DEBUG 55 + ohci_dump(ohci, 1); 56 + #endif 57 + return 0; 58 + } 59 + 60 + static const struct hc_driver ohci_spear_hc_driver = { 61 + .description = hcd_name, 62 + .product_desc = "SPEAr OHCI", 63 + .hcd_priv_size = sizeof(struct spear_ohci), 64 + 65 + /* generic hardware linkage */ 66 + .irq = ohci_irq, 67 + .flags = HCD_USB11 | HCD_MEMORY, 68 + 69 + /* basic lifecycle operations */ 70 + .start = ohci_spear_start, 71 + .stop = ohci_stop, 72 + .shutdown = ohci_shutdown, 73 + #ifdef CONFIG_PM 74 + .bus_suspend = ohci_bus_suspend, 75 + .bus_resume = ohci_bus_resume, 76 + #endif 77 + 78 + /* managing i/o requests and associated device resources */ 79 + .urb_enqueue = ohci_urb_enqueue, 80 + .urb_dequeue = ohci_urb_dequeue, 81 + .endpoint_disable = ohci_endpoint_disable, 82 + 83 + /* scheduling support */ 84 + .get_frame_number = ohci_get_frame, 85 + 86 + /* root hub support */ 87 + .hub_status_data = ohci_hub_status_data, 88 + .hub_control = ohci_hub_control, 89 + 90 + .start_port_reset = ohci_start_port_reset, 91 + }; 92 + 93 + static int spear_ohci_hcd_drv_probe(struct platform_device *pdev) 94 + { 95 + const struct hc_driver *driver = &ohci_spear_hc_driver; 96 + struct usb_hcd *hcd = NULL; 97 + struct clk *usbh_clk; 98 + struct spear_ohci *ohci_p; 99 + struct resource *res; 100 + int retval, irq; 101 + int *pdata = pdev->dev.platform_data; 102 + char clk_name[20] = "usbh_clk"; 103 + 104 + if (pdata == NULL) 105 + return -EFAULT; 106 + 107 + irq = platform_get_irq(pdev, 0); 108 + if (irq < 0) { 109 + retval = irq; 110 + goto fail_irq_get; 111 + } 112 + 113 + if (*pdata >= 0) 114 + sprintf(clk_name, "usbh.%01d_clk", *pdata); 115 + 116 + usbh_clk = clk_get(NULL, clk_name); 117 + if (IS_ERR(usbh_clk)) { 118 + dev_err(&pdev->dev, "Error getting interface clock\n"); 119 + retval = PTR_ERR(usbh_clk); 120 + goto fail_get_usbh_clk; 121 + } 122 + 123 + hcd = usb_create_hcd(driver, &pdev->dev, dev_name(&pdev->dev)); 124 + if (!hcd) { 125 + retval = -ENOMEM; 126 + goto fail_create_hcd; 127 + } 128 + 129 + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 130 + if (!res) { 131 + retval = -ENODEV; 132 + goto fail_request_resource; 133 + } 134 + 135 + hcd->rsrc_start = pdev->resource[0].start; 136 + hcd->rsrc_len = resource_size(res); 137 + if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) { 138 + dev_dbg(&pdev->dev, "request_mem_region failed\n"); 139 + retval = -EBUSY; 140 + goto fail_request_resource; 141 + } 142 + 143 + hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len); 144 + if (!hcd->regs) { 145 + dev_dbg(&pdev->dev, "ioremap failed\n"); 146 + retval = -ENOMEM; 147 + goto fail_ioremap; 148 + } 149 + 150 + ohci_p = (struct spear_ohci *)hcd_to_ohci(hcd); 151 + ohci_p->clk = usbh_clk; 152 + spear_start_ohci(ohci_p); 153 + ohci_hcd_init(hcd_to_ohci(hcd)); 154 + 155 + retval = usb_add_hcd(hcd, platform_get_irq(pdev, 0), IRQF_DISABLED); 156 + if (retval == 0) 157 + return retval; 158 + 159 + spear_stop_ohci(ohci_p); 160 + iounmap(hcd->regs); 161 + fail_ioremap: 162 + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); 163 + fail_request_resource: 164 + usb_put_hcd(hcd); 165 + fail_create_hcd: 166 + clk_put(usbh_clk); 167 + fail_get_usbh_clk: 168 + fail_irq_get: 169 + dev_err(&pdev->dev, "init fail, %d\n", retval); 170 + 171 + return retval; 172 + } 173 + 174 + static int spear_ohci_hcd_drv_remove(struct platform_device *pdev) 175 + { 176 + struct usb_hcd *hcd = platform_get_drvdata(pdev); 177 + struct spear_ohci *ohci_p = to_spear_ohci(hcd); 178 + 179 + usb_remove_hcd(hcd); 180 + if (ohci_p->clk) 181 + spear_stop_ohci(ohci_p); 182 + 183 + iounmap(hcd->regs); 184 + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); 185 + usb_put_hcd(hcd); 186 + 187 + if (ohci_p->clk) 188 + clk_put(ohci_p->clk); 189 + platform_set_drvdata(pdev, NULL); 190 + return 0; 191 + } 192 + 193 + #if defined(CONFIG_PM) 194 + static int spear_ohci_hcd_drv_suspend(struct platform_device *dev, 195 + pm_message_t message) 196 + { 197 + struct usb_hcd *hcd = platform_get_drvdata(dev); 198 + struct ohci_hcd *ohci = hcd_to_ohci(hcd); 199 + struct spear_ohci *ohci_p = to_spear_ohci(hcd); 200 + 201 + if (time_before(jiffies, ohci->next_statechange)) 202 + msleep(5); 203 + ohci->next_statechange = jiffies; 204 + 205 + spear_stop_ohci(ohci_p); 206 + ohci_to_hcd(ohci)->state = HC_STATE_SUSPENDED; 207 + return 0; 208 + } 209 + 210 + static int spear_ohci_hcd_drv_resume(struct platform_device *dev) 211 + { 212 + struct usb_hcd *hcd = platform_get_drvdata(dev); 213 + struct ohci_hcd *ohci = hcd_to_ohci(hcd); 214 + struct spear_ohci *ohci_p = to_spear_ohci(hcd); 215 + 216 + if (time_before(jiffies, ohci->next_statechange)) 217 + msleep(5); 218 + ohci->next_statechange = jiffies; 219 + 220 + spear_start_ohci(ohci_p); 221 + ohci_finish_controller_resume(hcd); 222 + return 0; 223 + } 224 + #endif 225 + 226 + /* Driver definition to register with the platform bus */ 227 + static struct platform_driver spear_ohci_hcd_driver = { 228 + .probe = spear_ohci_hcd_drv_probe, 229 + .remove = spear_ohci_hcd_drv_remove, 230 + #ifdef CONFIG_PM 231 + .suspend = spear_ohci_hcd_drv_suspend, 232 + .resume = spear_ohci_hcd_drv_resume, 233 + #endif 234 + .driver = { 235 + .owner = THIS_MODULE, 236 + .name = "spear-ohci", 237 + }, 238 + }; 239 + 240 + MODULE_ALIAS("platform:spear-ohci");