1/* 2 * Copyright 2012 Tilera Corporation. All Rights Reserved. 3 * 4 * This program is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU General Public License 6 * as published by the Free Software Foundation, version 2. 7 * 8 * This program is distributed in the hope that it will be useful, but 9 * WITHOUT ANY WARRANTY; without even the implied warranty of 10 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or 11 * NON INFRINGEMENT. See the GNU General Public License for 12 * more details. 13 */ 14 15/* 16 * Tilera TILE-Gx USB OHCI host controller driver. 17 */ 18 19#include <linux/irq.h> 20#include <linux/platform_device.h> 21#include <linux/usb/tilegx.h> 22#include <linux/usb.h> 23 24#include <asm/homecache.h> 25 26#include <gxio/iorpc_usb_host.h> 27#include <gxio/usb_host.h> 28 29static void tilegx_start_ohc(void) 30{ 31} 32 33static void tilegx_stop_ohc(void) 34{ 35} 36 37static int tilegx_ohci_start(struct usb_hcd *hcd) 38{ 39 struct ohci_hcd *ohci = hcd_to_ohci(hcd); 40 int ret; 41 42 ret = ohci_init(ohci); 43 if (ret < 0) 44 return ret; 45 46 ret = ohci_run(ohci); 47 if (ret < 0) { 48 dev_err(hcd->self.controller, "can't start %s\n", 49 hcd->self.bus_name); 50 ohci_stop(hcd); 51 return ret; 52 } 53 54 return 0; 55} 56 57static const struct hc_driver ohci_tilegx_hc_driver = { 58 .description = hcd_name, 59 .product_desc = "Tile-Gx OHCI", 60 .hcd_priv_size = sizeof(struct ohci_hcd), 61 62 /* 63 * Generic hardware linkage. 64 */ 65 .irq = ohci_irq, 66 .flags = HCD_MEMORY | HCD_LOCAL_MEM | HCD_USB11, 67 68 /* 69 * Basic lifecycle operations. 70 */ 71 .start = tilegx_ohci_start, 72 .stop = ohci_stop, 73 .shutdown = ohci_shutdown, 74 75 /* 76 * Managing I/O requests and associated device resources. 77 */ 78 .urb_enqueue = ohci_urb_enqueue, 79 .urb_dequeue = ohci_urb_dequeue, 80 .endpoint_disable = ohci_endpoint_disable, 81 82 /* 83 * Scheduling support. 84 */ 85 .get_frame_number = ohci_get_frame, 86 87 /* 88 * Root hub support. 89 */ 90 .hub_status_data = ohci_hub_status_data, 91 .hub_control = ohci_hub_control, 92 .start_port_reset = ohci_start_port_reset, 93}; 94 95static int ohci_hcd_tilegx_drv_probe(struct platform_device *pdev) 96{ 97 struct usb_hcd *hcd; 98 struct tilegx_usb_platform_data *pdata = dev_get_platdata(&pdev->dev); 99 pte_t pte = { 0 }; 100 int my_cpu = smp_processor_id(); 101 int ret; 102 103 if (usb_disabled()) 104 return -ENODEV; 105 106 /* 107 * Try to initialize our GXIO context; if we can't, the device 108 * doesn't exist. 109 */ 110 if (gxio_usb_host_init(&pdata->usb_ctx, pdata->dev_index, 0) != 0) 111 return -ENXIO; 112 113 hcd = usb_create_hcd(&ohci_tilegx_hc_driver, &pdev->dev, 114 dev_name(&pdev->dev)); 115 if (!hcd) { 116 ret = -ENOMEM; 117 goto err_hcd; 118 } 119 120 /* 121 * We don't use rsrc_start to map in our registers, but seems like 122 * we ought to set it to something, so we use the register VA. 123 */ 124 hcd->rsrc_start = 125 (ulong) gxio_usb_host_get_reg_start(&pdata->usb_ctx); 126 hcd->rsrc_len = gxio_usb_host_get_reg_len(&pdata->usb_ctx); 127 hcd->regs = gxio_usb_host_get_reg_start(&pdata->usb_ctx); 128 129 tilegx_start_ohc(); 130 131 /* Create our IRQs and register them. */ 132 pdata->irq = irq_alloc_hwirq(-1); 133 if (!pdata->irq) { 134 ret = -ENXIO; 135 goto err_no_irq; 136 } 137 138 tile_irq_activate(pdata->irq, TILE_IRQ_PERCPU); 139 140 /* Configure interrupts. */ 141 ret = gxio_usb_host_cfg_interrupt(&pdata->usb_ctx, 142 cpu_x(my_cpu), cpu_y(my_cpu), 143 KERNEL_PL, pdata->irq); 144 if (ret) { 145 ret = -ENXIO; 146 goto err_have_irq; 147 } 148 149 /* Register all of our memory. */ 150 pte = pte_set_home(pte, PAGE_HOME_HASH); 151 ret = gxio_usb_host_register_client_memory(&pdata->usb_ctx, pte, 0); 152 if (ret) { 153 ret = -ENXIO; 154 goto err_have_irq; 155 } 156 157 ohci_hcd_init(hcd_to_ohci(hcd)); 158 159 ret = usb_add_hcd(hcd, pdata->irq, IRQF_SHARED); 160 if (ret == 0) { 161 platform_set_drvdata(pdev, hcd); 162 device_wakeup_enable(hcd->self.controller); 163 return ret; 164 } 165 166err_have_irq: 167 irq_free_hwirq(pdata->irq); 168err_no_irq: 169 tilegx_stop_ohc(); 170 usb_put_hcd(hcd); 171err_hcd: 172 gxio_usb_host_destroy(&pdata->usb_ctx); 173 return ret; 174} 175 176static int ohci_hcd_tilegx_drv_remove(struct platform_device *pdev) 177{ 178 struct usb_hcd *hcd = platform_get_drvdata(pdev); 179 struct tilegx_usb_platform_data *pdata = dev_get_platdata(&pdev->dev); 180 181 usb_remove_hcd(hcd); 182 usb_put_hcd(hcd); 183 tilegx_stop_ohc(); 184 gxio_usb_host_destroy(&pdata->usb_ctx); 185 irq_free_hwirq(pdata->irq); 186 187 return 0; 188} 189 190static void ohci_hcd_tilegx_drv_shutdown(struct platform_device *pdev) 191{ 192 usb_hcd_platform_shutdown(pdev); 193 ohci_hcd_tilegx_drv_remove(pdev); 194} 195 196static struct platform_driver ohci_hcd_tilegx_driver = { 197 .probe = ohci_hcd_tilegx_drv_probe, 198 .remove = ohci_hcd_tilegx_drv_remove, 199 .shutdown = ohci_hcd_tilegx_drv_shutdown, 200 .driver = { 201 .name = "tilegx-ohci", 202 } 203}; 204 205MODULE_ALIAS("platform:tilegx-ohci"); 206