Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
at v3.11 212 lines 4.3 kB view raw
1/* 2 * EHCI HCD glue for Cavium Octeon II SOCs. 3 * 4 * Loosely based on ehci-au1xxx.c 5 * 6 * This file is subject to the terms and conditions of the GNU General Public 7 * License. See the file "COPYING" in the main directory of this archive 8 * for more details. 9 * 10 * Copyright (C) 2010 Cavium Networks 11 * 12 */ 13 14#include <linux/platform_device.h> 15 16#include <asm/octeon/octeon.h> 17#include <asm/octeon/cvmx-uctlx-defs.h> 18 19#define OCTEON_OHCI_HCD_NAME "octeon-ohci" 20 21/* Common clock init code. */ 22void octeon2_usb_clocks_start(void); 23void octeon2_usb_clocks_stop(void); 24 25static void ohci_octeon_hw_start(void) 26{ 27 union cvmx_uctlx_ohci_ctl ohci_ctl; 28 29 octeon2_usb_clocks_start(); 30 31 ohci_ctl.u64 = cvmx_read_csr(CVMX_UCTLX_OHCI_CTL(0)); 32 ohci_ctl.s.l2c_addr_msb = 0; 33 ohci_ctl.s.l2c_buff_emod = 1; /* Byte swapped. */ 34 ohci_ctl.s.l2c_desc_emod = 1; /* Byte swapped. */ 35 cvmx_write_csr(CVMX_UCTLX_OHCI_CTL(0), ohci_ctl.u64); 36 37} 38 39static void ohci_octeon_hw_stop(void) 40{ 41 /* Undo ohci_octeon_start() */ 42 octeon2_usb_clocks_stop(); 43} 44 45static int ohci_octeon_start(struct usb_hcd *hcd) 46{ 47 struct ohci_hcd *ohci = hcd_to_ohci(hcd); 48 int ret; 49 50 ret = ohci_init(ohci); 51 52 if (ret < 0) 53 return ret; 54 55 ret = ohci_run(ohci); 56 57 if (ret < 0) { 58 ohci_err(ohci, "can't start %s", hcd->self.bus_name); 59 ohci_stop(hcd); 60 return ret; 61 } 62 63 return 0; 64} 65 66static const struct hc_driver ohci_octeon_hc_driver = { 67 .description = hcd_name, 68 .product_desc = "Octeon OHCI", 69 .hcd_priv_size = sizeof(struct ohci_hcd), 70 71 /* 72 * generic hardware linkage 73 */ 74 .irq = ohci_irq, 75 .flags = HCD_USB11 | HCD_MEMORY, 76 77 /* 78 * basic lifecycle operations 79 */ 80 .start = ohci_octeon_start, 81 .stop = ohci_stop, 82 .shutdown = ohci_shutdown, 83 84 /* 85 * managing i/o requests and associated device resources 86 */ 87 .urb_enqueue = ohci_urb_enqueue, 88 .urb_dequeue = ohci_urb_dequeue, 89 .endpoint_disable = ohci_endpoint_disable, 90 91 /* 92 * scheduling support 93 */ 94 .get_frame_number = ohci_get_frame, 95 96 /* 97 * root hub support 98 */ 99 .hub_status_data = ohci_hub_status_data, 100 .hub_control = ohci_hub_control, 101 102 .start_port_reset = ohci_start_port_reset, 103}; 104 105static int ohci_octeon_drv_probe(struct platform_device *pdev) 106{ 107 struct usb_hcd *hcd; 108 struct ohci_hcd *ohci; 109 void *reg_base; 110 struct resource *res_mem; 111 int irq; 112 int ret; 113 114 if (usb_disabled()) 115 return -ENODEV; 116 117 irq = platform_get_irq(pdev, 0); 118 if (irq < 0) { 119 dev_err(&pdev->dev, "No irq assigned\n"); 120 return -ENODEV; 121 } 122 123 res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); 124 if (res_mem == NULL) { 125 dev_err(&pdev->dev, "No register space assigned\n"); 126 return -ENODEV; 127 } 128 129 /* Ohci is a 32-bit device. */ 130 pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32); 131 pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask; 132 133 hcd = usb_create_hcd(&ohci_octeon_hc_driver, &pdev->dev, "octeon"); 134 if (!hcd) 135 return -ENOMEM; 136 137 hcd->rsrc_start = res_mem->start; 138 hcd->rsrc_len = resource_size(res_mem); 139 140 if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, 141 OCTEON_OHCI_HCD_NAME)) { 142 dev_err(&pdev->dev, "request_mem_region failed\n"); 143 ret = -EBUSY; 144 goto err1; 145 } 146 147 reg_base = ioremap(hcd->rsrc_start, hcd->rsrc_len); 148 if (!reg_base) { 149 dev_err(&pdev->dev, "ioremap failed\n"); 150 ret = -ENOMEM; 151 goto err2; 152 } 153 154 ohci_octeon_hw_start(); 155 156 hcd->regs = reg_base; 157 158 ohci = hcd_to_ohci(hcd); 159 160 /* Octeon OHCI matches CPU endianness. */ 161#ifdef __BIG_ENDIAN 162 ohci->flags |= OHCI_QUIRK_BE_MMIO; 163#endif 164 165 ohci_hcd_init(ohci); 166 167 ret = usb_add_hcd(hcd, irq, IRQF_SHARED); 168 if (ret) { 169 dev_dbg(&pdev->dev, "failed to add hcd with err %d\n", ret); 170 goto err3; 171 } 172 173 platform_set_drvdata(pdev, hcd); 174 175 return 0; 176 177err3: 178 ohci_octeon_hw_stop(); 179 180 iounmap(hcd->regs); 181err2: 182 release_mem_region(hcd->rsrc_start, hcd->rsrc_len); 183err1: 184 usb_put_hcd(hcd); 185 return ret; 186} 187 188static int ohci_octeon_drv_remove(struct platform_device *pdev) 189{ 190 struct usb_hcd *hcd = platform_get_drvdata(pdev); 191 192 usb_remove_hcd(hcd); 193 194 ohci_octeon_hw_stop(); 195 iounmap(hcd->regs); 196 release_mem_region(hcd->rsrc_start, hcd->rsrc_len); 197 usb_put_hcd(hcd); 198 199 return 0; 200} 201 202static struct platform_driver ohci_octeon_driver = { 203 .probe = ohci_octeon_drv_probe, 204 .remove = ohci_octeon_drv_remove, 205 .shutdown = usb_hcd_platform_shutdown, 206 .driver = { 207 .name = OCTEON_OHCI_HCD_NAME, 208 .owner = THIS_MODULE, 209 } 210}; 211 212MODULE_ALIAS("platform:" OCTEON_OHCI_HCD_NAME);