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

Configure Feed

Select the types of activity you want to include in your feed.

at v3.13 200 lines 4.4 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_EHCI_HCD_NAME "octeon-ehci" 20 21/* Common clock init code. */ 22void octeon2_usb_clocks_start(void); 23void octeon2_usb_clocks_stop(void); 24 25static void ehci_octeon_start(void) 26{ 27 union cvmx_uctlx_ehci_ctl ehci_ctl; 28 29 octeon2_usb_clocks_start(); 30 31 ehci_ctl.u64 = cvmx_read_csr(CVMX_UCTLX_EHCI_CTL(0)); 32 /* Use 64-bit addressing. */ 33 ehci_ctl.s.ehci_64b_addr_en = 1; 34 ehci_ctl.s.l2c_addr_msb = 0; 35 ehci_ctl.s.l2c_buff_emod = 1; /* Byte swapped. */ 36 ehci_ctl.s.l2c_desc_emod = 1; /* Byte swapped. */ 37 cvmx_write_csr(CVMX_UCTLX_EHCI_CTL(0), ehci_ctl.u64); 38} 39 40static void ehci_octeon_stop(void) 41{ 42 octeon2_usb_clocks_stop(); 43} 44 45static const struct hc_driver ehci_octeon_hc_driver = { 46 .description = hcd_name, 47 .product_desc = "Octeon EHCI", 48 .hcd_priv_size = sizeof(struct ehci_hcd), 49 50 /* 51 * generic hardware linkage 52 */ 53 .irq = ehci_irq, 54 .flags = HCD_MEMORY | HCD_USB2 | HCD_BH, 55 56 /* 57 * basic lifecycle operations 58 */ 59 .reset = ehci_setup, 60 .start = ehci_run, 61 .stop = ehci_stop, 62 .shutdown = ehci_shutdown, 63 64 /* 65 * managing i/o requests and associated device resources 66 */ 67 .urb_enqueue = ehci_urb_enqueue, 68 .urb_dequeue = ehci_urb_dequeue, 69 .endpoint_disable = ehci_endpoint_disable, 70 .endpoint_reset = ehci_endpoint_reset, 71 72 /* 73 * scheduling support 74 */ 75 .get_frame_number = ehci_get_frame, 76 77 /* 78 * root hub support 79 */ 80 .hub_status_data = ehci_hub_status_data, 81 .hub_control = ehci_hub_control, 82 .bus_suspend = ehci_bus_suspend, 83 .bus_resume = ehci_bus_resume, 84 .relinquish_port = ehci_relinquish_port, 85 .port_handed_over = ehci_port_handed_over, 86 87 .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, 88}; 89 90static u64 ehci_octeon_dma_mask = DMA_BIT_MASK(64); 91 92static int ehci_octeon_drv_probe(struct platform_device *pdev) 93{ 94 struct usb_hcd *hcd; 95 struct ehci_hcd *ehci; 96 struct resource *res_mem; 97 int irq; 98 int ret; 99 100 if (usb_disabled()) 101 return -ENODEV; 102 103 irq = platform_get_irq(pdev, 0); 104 if (irq < 0) { 105 dev_err(&pdev->dev, "No irq assigned\n"); 106 return -ENODEV; 107 } 108 109 res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); 110 if (res_mem == NULL) { 111 dev_err(&pdev->dev, "No register space assigned\n"); 112 return -ENODEV; 113 } 114 115 /* 116 * We can DMA from anywhere. But the descriptors must be in 117 * the lower 4GB. 118 */ 119 pdev->dev.dma_mask = &ehci_octeon_dma_mask; 120 ret = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32)); 121 if (ret) 122 return ret; 123 124 hcd = usb_create_hcd(&ehci_octeon_hc_driver, &pdev->dev, "octeon"); 125 if (!hcd) 126 return -ENOMEM; 127 128 hcd->rsrc_start = res_mem->start; 129 hcd->rsrc_len = resource_size(res_mem); 130 131 if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, 132 OCTEON_EHCI_HCD_NAME)) { 133 dev_err(&pdev->dev, "request_mem_region failed\n"); 134 ret = -EBUSY; 135 goto err1; 136 } 137 138 hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len); 139 if (!hcd->regs) { 140 dev_err(&pdev->dev, "ioremap failed\n"); 141 ret = -ENOMEM; 142 goto err2; 143 } 144 145 ehci_octeon_start(); 146 147 ehci = hcd_to_ehci(hcd); 148 149 /* Octeon EHCI matches CPU endianness. */ 150#ifdef __BIG_ENDIAN 151 ehci->big_endian_mmio = 1; 152#endif 153 154 ehci->caps = hcd->regs; 155 156 ret = usb_add_hcd(hcd, irq, IRQF_SHARED); 157 if (ret) { 158 dev_dbg(&pdev->dev, "failed to add hcd with err %d\n", ret); 159 goto err3; 160 } 161 162 platform_set_drvdata(pdev, hcd); 163 164 return 0; 165err3: 166 ehci_octeon_stop(); 167 168 iounmap(hcd->regs); 169err2: 170 release_mem_region(hcd->rsrc_start, hcd->rsrc_len); 171err1: 172 usb_put_hcd(hcd); 173 return ret; 174} 175 176static int ehci_octeon_drv_remove(struct platform_device *pdev) 177{ 178 struct usb_hcd *hcd = platform_get_drvdata(pdev); 179 180 usb_remove_hcd(hcd); 181 182 ehci_octeon_stop(); 183 iounmap(hcd->regs); 184 release_mem_region(hcd->rsrc_start, hcd->rsrc_len); 185 usb_put_hcd(hcd); 186 187 return 0; 188} 189 190static struct platform_driver ehci_octeon_driver = { 191 .probe = ehci_octeon_drv_probe, 192 .remove = ehci_octeon_drv_remove, 193 .shutdown = usb_hcd_platform_shutdown, 194 .driver = { 195 .name = OCTEON_EHCI_HCD_NAME, 196 .owner = THIS_MODULE, 197 } 198}; 199 200MODULE_ALIAS("platform:" OCTEON_EHCI_HCD_NAME);