linux/drivers/usb/host/ehci-pmcmsp.c
<<
>>
Prefs
   1/*
   2 * PMC MSP EHCI (Host Controller Driver) for USB.
   3 *
   4 * (C) Copyright 2006-2010 PMC-Sierra Inc
   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 */
  11
  12/* includes */
  13#include <linux/platform_device.h>
  14#include <linux/gpio.h>
  15#include <linux/usb.h>
  16#include <msp_usb.h>
  17
  18/* stream disable*/
  19#define USB_CTRL_MODE_STREAM_DISABLE    0x10
  20
  21/* threshold */
  22#define USB_CTRL_FIFO_THRESH            0x00300000
  23
  24/* register offset for usb_mode */
  25#define USB_EHCI_REG_USB_MODE           0x68
  26
  27/* register offset for usb fifo */
  28#define USB_EHCI_REG_USB_FIFO           0x24
  29
  30/* register offset for usb status */
  31#define USB_EHCI_REG_USB_STATUS         0x44
  32
  33/* serial/parallel transceiver */
  34#define USB_EHCI_REG_BIT_STAT_STS       (1<<29)
  35
  36/* TWI USB0 host device pin */
  37#define MSP_PIN_USB0_HOST_DEV           49
  38
  39/* TWI USB1 host device pin */
  40#define MSP_PIN_USB1_HOST_DEV           50
  41
  42
  43static void usb_hcd_tdi_set_mode(struct ehci_hcd *ehci)
  44{
  45        u8 *base;
  46        u8 *statreg;
  47        u8 *fiforeg;
  48        u32 val;
  49        struct ehci_regs *reg_base = ehci->regs;
  50
  51        /* get register base */
  52        base = (u8 *)reg_base + USB_EHCI_REG_USB_MODE;
  53        statreg = (u8 *)reg_base + USB_EHCI_REG_USB_STATUS;
  54        fiforeg = (u8 *)reg_base + USB_EHCI_REG_USB_FIFO;
  55
  56        /* Disable controller mode stream */
  57        val = ehci_readl(ehci, (u32 *)base);
  58        ehci_writel(ehci, (val | USB_CTRL_MODE_STREAM_DISABLE),
  59                        (u32 *)base);
  60
  61        /* clear STS to select parallel transceiver interface */
  62        val = ehci_readl(ehci, (u32 *)statreg);
  63        val = val & ~USB_EHCI_REG_BIT_STAT_STS;
  64        ehci_writel(ehci, val, (u32 *)statreg);
  65
  66        /* write to set the proper fifo threshold */
  67        ehci_writel(ehci, USB_CTRL_FIFO_THRESH, (u32 *)fiforeg);
  68
  69        /* set TWI GPIO USB_HOST_DEV pin high */
  70        gpio_direction_output(MSP_PIN_USB0_HOST_DEV, 1);
  71}
  72
  73/* called during probe() after chip reset completes */
  74static int ehci_msp_setup(struct usb_hcd *hcd)
  75{
  76        struct ehci_hcd         *ehci = hcd_to_ehci(hcd);
  77        int                     retval;
  78
  79        ehci->big_endian_mmio = 1;
  80        ehci->big_endian_desc = 1;
  81
  82        ehci->caps = hcd->regs;
  83        hcd->has_tt = 1;
  84
  85        retval = ehci_setup(hcd);
  86        if (retval)
  87                return retval;
  88
  89        usb_hcd_tdi_set_mode(ehci);
  90
  91        return retval;
  92}
  93
  94
  95/* configure so an HC device and id are always provided
  96 * always called with process context; sleeping is OK
  97 */
  98
  99static int usb_hcd_msp_map_regs(struct mspusb_device *dev)
 100{
 101        struct resource *res;
 102        struct platform_device *pdev = &dev->dev;
 103        u32 res_len;
 104        int retval;
 105
 106        /* MAB register space */
 107        res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
 108        if (res == NULL)
 109                return -ENOMEM;
 110        res_len = resource_size(res);
 111        if (!request_mem_region(res->start, res_len, "mab regs"))
 112                return -EBUSY;
 113
 114        dev->mab_regs = ioremap_nocache(res->start, res_len);
 115        if (dev->mab_regs == NULL) {
 116                retval = -ENOMEM;
 117                goto err1;
 118        }
 119
 120        /* MSP USB register space */
 121        res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
 122        if (res == NULL) {
 123                retval = -ENOMEM;
 124                goto err2;
 125        }
 126        res_len = resource_size(res);
 127        if (!request_mem_region(res->start, res_len, "usbid regs")) {
 128                retval = -EBUSY;
 129                goto err2;
 130        }
 131        dev->usbid_regs = ioremap_nocache(res->start, res_len);
 132        if (dev->usbid_regs == NULL) {
 133                retval = -ENOMEM;
 134                goto err3;
 135        }
 136
 137        return 0;
 138err3:
 139        res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
 140        res_len = resource_size(res);
 141        release_mem_region(res->start, res_len);
 142err2:
 143        iounmap(dev->mab_regs);
 144err1:
 145        res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
 146        res_len = resource_size(res);
 147        release_mem_region(res->start, res_len);
 148        dev_err(&pdev->dev, "Failed to map non-EHCI regs.\n");
 149        return retval;
 150}
 151
 152/**
 153 * usb_hcd_msp_probe - initialize PMC MSP-based HCDs
 154 * Context: !in_interrupt()
 155 *
 156 * Allocates basic resources for this USB host controller, and
 157 * then invokes the start() method for the HCD associated with it
 158 * through the hotplug entry's driver_data.
 159 *
 160 */
 161int usb_hcd_msp_probe(const struct hc_driver *driver,
 162                          struct platform_device *dev)
 163{
 164        int retval;
 165        struct usb_hcd *hcd;
 166        struct resource *res;
 167        struct ehci_hcd         *ehci ;
 168
 169        hcd = usb_create_hcd(driver, &dev->dev, "pmcmsp");
 170        if (!hcd)
 171                return -ENOMEM;
 172
 173        res = platform_get_resource(dev, IORESOURCE_MEM, 0);
 174        if (res == NULL) {
 175                pr_debug("No IOMEM resource info for %s.\n", dev->name);
 176                retval = -ENOMEM;
 177                goto err1;
 178        }
 179        hcd->rsrc_start = res->start;
 180        hcd->rsrc_len = resource_size(res);
 181        if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, dev->name)) {
 182                retval = -EBUSY;
 183                goto err1;
 184        }
 185        hcd->regs = ioremap_nocache(hcd->rsrc_start, hcd->rsrc_len);
 186        if (!hcd->regs) {
 187                pr_debug("ioremap failed");
 188                retval = -ENOMEM;
 189                goto err2;
 190        }
 191
 192        res = platform_get_resource(dev, IORESOURCE_IRQ, 0);
 193        if (res == NULL) {
 194                dev_err(&dev->dev, "No IRQ resource info for %s.\n", dev->name);
 195                retval = -ENOMEM;
 196                goto err3;
 197        }
 198
 199        /* Map non-EHCI register spaces */
 200        retval = usb_hcd_msp_map_regs(to_mspusb_device(dev));
 201        if (retval != 0)
 202                goto err3;
 203
 204        ehci = hcd_to_ehci(hcd);
 205        ehci->big_endian_mmio = 1;
 206        ehci->big_endian_desc = 1;
 207
 208
 209        retval = usb_add_hcd(hcd, res->start, IRQF_SHARED);
 210        if (retval == 0) {
 211                device_wakeup_enable(hcd->self.controller);
 212                return 0;
 213        }
 214
 215        usb_remove_hcd(hcd);
 216err3:
 217        iounmap(hcd->regs);
 218err2:
 219        release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
 220err1:
 221        usb_put_hcd(hcd);
 222
 223        return retval;
 224}
 225
 226
 227
 228/**
 229 * usb_hcd_msp_remove - shutdown processing for PMC MSP-based HCDs
 230 * @dev: USB Host Controller being removed
 231 * Context: !in_interrupt()
 232 *
 233 * Reverses the effect of usb_hcd_msp_probe(), first invoking
 234 * the HCD's stop() method.  It is always called from a thread
 235 * context, normally "rmmod", "apmd", or something similar.
 236 *
 237 * may be called without controller electrically present
 238 * may be called with controller, bus, and devices active
 239 */
 240void usb_hcd_msp_remove(struct usb_hcd *hcd, struct platform_device *dev)
 241{
 242        usb_remove_hcd(hcd);
 243        iounmap(hcd->regs);
 244        release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
 245        usb_put_hcd(hcd);
 246}
 247
 248static const struct hc_driver ehci_msp_hc_driver = {
 249        .description =          hcd_name,
 250        .product_desc =         "PMC MSP EHCI",
 251        .hcd_priv_size =        sizeof(struct ehci_hcd),
 252
 253        /*
 254         * generic hardware linkage
 255         */
 256        .irq =                  ehci_irq,
 257        .flags =                HCD_MEMORY | HCD_USB2 | HCD_BH,
 258
 259        /*
 260         * basic lifecycle operations
 261         */
 262        .reset                  = ehci_msp_setup,
 263        .shutdown               = ehci_shutdown,
 264        .start                  = ehci_run,
 265        .stop                   = ehci_stop,
 266
 267        /*
 268         * managing i/o requests and associated device resources
 269         */
 270        .urb_enqueue            = ehci_urb_enqueue,
 271        .urb_dequeue            = ehci_urb_dequeue,
 272        .endpoint_disable       = ehci_endpoint_disable,
 273        .endpoint_reset         = ehci_endpoint_reset,
 274
 275        /*
 276         * scheduling support
 277         */
 278        .get_frame_number       = ehci_get_frame,
 279
 280        /*
 281         * root hub support
 282         */
 283        .hub_status_data        = ehci_hub_status_data,
 284        .hub_control            = ehci_hub_control,
 285        .bus_suspend            = ehci_bus_suspend,
 286        .bus_resume             = ehci_bus_resume,
 287        .relinquish_port        = ehci_relinquish_port,
 288        .port_handed_over       = ehci_port_handed_over,
 289
 290        .clear_tt_buffer_complete       = ehci_clear_tt_buffer_complete,
 291};
 292
 293static int ehci_hcd_msp_drv_probe(struct platform_device *pdev)
 294{
 295        int ret;
 296
 297        pr_debug("In ehci_hcd_msp_drv_probe");
 298
 299        if (usb_disabled())
 300                return -ENODEV;
 301
 302        gpio_request(MSP_PIN_USB0_HOST_DEV, "USB0_HOST_DEV_GPIO");
 303
 304        ret = usb_hcd_msp_probe(&ehci_msp_hc_driver, pdev);
 305
 306        return ret;
 307}
 308
 309static int ehci_hcd_msp_drv_remove(struct platform_device *pdev)
 310{
 311        struct usb_hcd *hcd = platform_get_drvdata(pdev);
 312
 313        usb_hcd_msp_remove(hcd, pdev);
 314
 315        /* free TWI GPIO USB_HOST_DEV pin */
 316        gpio_free(MSP_PIN_USB0_HOST_DEV);
 317
 318        return 0;
 319}
 320
 321MODULE_ALIAS("pmcmsp-ehci");
 322
 323static struct platform_driver ehci_hcd_msp_driver = {
 324        .probe          = ehci_hcd_msp_drv_probe,
 325        .remove         = ehci_hcd_msp_drv_remove,
 326        .driver         = {
 327                .name   = "pmcmsp-ehci",
 328        },
 329};
 330