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.0-rc2 207 lines 4.7 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, 55 56 /* 57 * basic lifecycle operations 58 */ 59 .reset = ehci_init, 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.coherent_dma_mask = DMA_BIT_MASK(32); 120 pdev->dev.dma_mask = &ehci_octeon_dma_mask; 121 122 hcd = usb_create_hcd(&ehci_octeon_hc_driver, &pdev->dev, "octeon"); 123 if (!hcd) 124 return -ENOMEM; 125 126 hcd->rsrc_start = res_mem->start; 127 hcd->rsrc_len = res_mem->end - res_mem->start + 1; 128 129 if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, 130 OCTEON_EHCI_HCD_NAME)) { 131 dev_err(&pdev->dev, "request_mem_region failed\n"); 132 ret = -EBUSY; 133 goto err1; 134 } 135 136 hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len); 137 if (!hcd->regs) { 138 dev_err(&pdev->dev, "ioremap failed\n"); 139 ret = -ENOMEM; 140 goto err2; 141 } 142 143 ehci_octeon_start(); 144 145 ehci = hcd_to_ehci(hcd); 146 147 /* Octeon EHCI matches CPU endianness. */ 148#ifdef __BIG_ENDIAN 149 ehci->big_endian_mmio = 1; 150#endif 151 152 ehci->caps = hcd->regs; 153 ehci->regs = hcd->regs + 154 HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase)); 155 /* cache this readonly data; minimize chip reads */ 156 ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); 157 158 ret = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED); 159 if (ret) { 160 dev_dbg(&pdev->dev, "failed to add hcd with err %d\n", ret); 161 goto err3; 162 } 163 164 platform_set_drvdata(pdev, hcd); 165 166 /* root ports should always stay powered */ 167 ehci_port_power(ehci, 1); 168 169 return 0; 170err3: 171 ehci_octeon_stop(); 172 173 iounmap(hcd->regs); 174err2: 175 release_mem_region(hcd->rsrc_start, hcd->rsrc_len); 176err1: 177 usb_put_hcd(hcd); 178 return ret; 179} 180 181static int ehci_octeon_drv_remove(struct platform_device *pdev) 182{ 183 struct usb_hcd *hcd = platform_get_drvdata(pdev); 184 185 usb_remove_hcd(hcd); 186 187 ehci_octeon_stop(); 188 iounmap(hcd->regs); 189 release_mem_region(hcd->rsrc_start, hcd->rsrc_len); 190 usb_put_hcd(hcd); 191 192 platform_set_drvdata(pdev, NULL); 193 194 return 0; 195} 196 197static struct platform_driver ehci_octeon_driver = { 198 .probe = ehci_octeon_drv_probe, 199 .remove = ehci_octeon_drv_remove, 200 .shutdown = usb_hcd_platform_shutdown, 201 .driver = { 202 .name = OCTEON_EHCI_HCD_NAME, 203 .owner = THIS_MODULE, 204 } 205}; 206 207MODULE_ALIAS("platform:" OCTEON_EHCI_HCD_NAME);