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