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.4-rc5 209 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 = resource_size(res_mem); 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 ehci_reset(ehci); 159 160 ret = usb_add_hcd(hcd, irq, IRQF_SHARED); 161 if (ret) { 162 dev_dbg(&pdev->dev, "failed to add hcd with err %d\n", ret); 163 goto err3; 164 } 165 166 platform_set_drvdata(pdev, hcd); 167 168 /* root ports should always stay powered */ 169 ehci_port_power(ehci, 1); 170 171 return 0; 172err3: 173 ehci_octeon_stop(); 174 175 iounmap(hcd->regs); 176err2: 177 release_mem_region(hcd->rsrc_start, hcd->rsrc_len); 178err1: 179 usb_put_hcd(hcd); 180 return ret; 181} 182 183static int ehci_octeon_drv_remove(struct platform_device *pdev) 184{ 185 struct usb_hcd *hcd = platform_get_drvdata(pdev); 186 187 usb_remove_hcd(hcd); 188 189 ehci_octeon_stop(); 190 iounmap(hcd->regs); 191 release_mem_region(hcd->rsrc_start, hcd->rsrc_len); 192 usb_put_hcd(hcd); 193 194 platform_set_drvdata(pdev, NULL); 195 196 return 0; 197} 198 199static struct platform_driver ehci_octeon_driver = { 200 .probe = ehci_octeon_drv_probe, 201 .remove = ehci_octeon_drv_remove, 202 .shutdown = usb_hcd_platform_shutdown, 203 .driver = { 204 .name = OCTEON_EHCI_HCD_NAME, 205 .owner = THIS_MODULE, 206 } 207}; 208 209MODULE_ALIAS("platform:" OCTEON_EHCI_HCD_NAME);