linux/drivers/usb/host/ssb-hcd.c
<<
>>
Prefs
   1/*
   2 * Sonics Silicon Backplane
   3 * Broadcom USB-core driver  (SSB bus glue)
   4 *
   5 * Copyright 2011-2012 Hauke Mehrtens <hauke@hauke-m.de>
   6 *
   7 * Based on ssb-ohci driver
   8 * Copyright 2007 Michael Buesch <m@bues.ch>
   9 *
  10 * Derived from the OHCI-PCI driver
  11 * Copyright 1999 Roman Weissgaerber
  12 * Copyright 2000-2002 David Brownell
  13 * Copyright 1999 Linus Torvalds
  14 * Copyright 1999 Gregory P. Smith
  15 *
  16 * Derived from the USBcore related parts of Broadcom-SB
  17 * Copyright 2005-2011 Broadcom Corporation
  18 *
  19 * Licensed under the GNU/GPL. See COPYING for details.
  20 */
  21#include <linux/ssb/ssb.h>
  22#include <linux/delay.h>
  23#include <linux/platform_device.h>
  24#include <linux/module.h>
  25#include <linux/slab.h>
  26#include <linux/usb/ehci_pdriver.h>
  27#include <linux/usb/ohci_pdriver.h>
  28
  29MODULE_AUTHOR("Hauke Mehrtens");
  30MODULE_DESCRIPTION("Common USB driver for SSB Bus");
  31MODULE_LICENSE("GPL");
  32
  33#define SSB_HCD_TMSLOW_HOSTMODE (1 << 29)
  34
  35struct ssb_hcd_device {
  36        struct platform_device *ehci_dev;
  37        struct platform_device *ohci_dev;
  38
  39        u32 enable_flags;
  40};
  41
  42static void ssb_hcd_5354wa(struct ssb_device *dev)
  43{
  44#ifdef CONFIG_SSB_DRIVER_MIPS
  45        /* Work around for 5354 failures */
  46        if (dev->id.revision == 2 && dev->bus->chip_id == 0x5354) {
  47                /* Change syn01 reg */
  48                ssb_write32(dev, 0x894, 0x00fe00fe);
  49
  50                /* Change syn03 reg */
  51                ssb_write32(dev, 0x89c, ssb_read32(dev, 0x89c) | 0x1);
  52        }
  53#endif
  54}
  55
  56static void ssb_hcd_usb20wa(struct ssb_device *dev)
  57{
  58        if (dev->id.coreid == SSB_DEV_USB20_HOST) {
  59                /*
  60                 * USB 2.0 special considerations:
  61                 *
  62                 * In addition to the standard SSB reset sequence, the Host
  63                 * Control Register must be programmed to bring the USB core
  64                 * and various phy components out of reset.
  65                 */
  66                ssb_write32(dev, 0x200, 0x7ff);
  67
  68                /* Change Flush control reg */
  69                ssb_write32(dev, 0x400, ssb_read32(dev, 0x400) & ~8);
  70                ssb_read32(dev, 0x400);
  71
  72                /* Change Shim control reg */
  73                ssb_write32(dev, 0x304, ssb_read32(dev, 0x304) & ~0x100);
  74                ssb_read32(dev, 0x304);
  75
  76                udelay(1);
  77
  78                ssb_hcd_5354wa(dev);
  79        }
  80}
  81
  82/* based on arch/mips/brcm-boards/bcm947xx/pcibios.c */
  83static u32 ssb_hcd_init_chip(struct ssb_device *dev)
  84{
  85        u32 flags = 0;
  86
  87        if (dev->id.coreid == SSB_DEV_USB11_HOSTDEV)
  88                /* Put the device into host-mode. */
  89                flags |= SSB_HCD_TMSLOW_HOSTMODE;
  90
  91        ssb_device_enable(dev, flags);
  92
  93        ssb_hcd_usb20wa(dev);
  94
  95        return flags;
  96}
  97
  98static const struct usb_ehci_pdata ehci_pdata = {
  99};
 100
 101static const struct usb_ohci_pdata ohci_pdata = {
 102};
 103
 104static struct platform_device *ssb_hcd_create_pdev(struct ssb_device *dev, bool ohci, u32 addr, u32 len)
 105{
 106        struct platform_device *hci_dev;
 107        struct resource hci_res[2];
 108        int ret = -ENOMEM;
 109
 110        memset(hci_res, 0, sizeof(hci_res));
 111
 112        hci_res[0].start = addr;
 113        hci_res[0].end = hci_res[0].start + len - 1;
 114        hci_res[0].flags = IORESOURCE_MEM;
 115
 116        hci_res[1].start = dev->irq;
 117        hci_res[1].flags = IORESOURCE_IRQ;
 118
 119        hci_dev = platform_device_alloc(ohci ? "ohci-platform" :
 120                                        "ehci-platform" , 0);
 121        if (!hci_dev)
 122                return NULL;
 123
 124        hci_dev->dev.parent = dev->dev;
 125        hci_dev->dev.dma_mask = &hci_dev->dev.coherent_dma_mask;
 126
 127        ret = platform_device_add_resources(hci_dev, hci_res,
 128                                            ARRAY_SIZE(hci_res));
 129        if (ret)
 130                goto err_alloc;
 131        if (ohci)
 132                ret = platform_device_add_data(hci_dev, &ohci_pdata,
 133                                               sizeof(ohci_pdata));
 134        else
 135                ret = platform_device_add_data(hci_dev, &ehci_pdata,
 136                                               sizeof(ehci_pdata));
 137        if (ret)
 138                goto err_alloc;
 139        ret = platform_device_add(hci_dev);
 140        if (ret)
 141                goto err_alloc;
 142
 143        return hci_dev;
 144
 145err_alloc:
 146        platform_device_put(hci_dev);
 147        return ERR_PTR(ret);
 148}
 149
 150static int ssb_hcd_probe(struct ssb_device *dev,
 151                                   const struct ssb_device_id *id)
 152{
 153        int err, tmp;
 154        int start, len;
 155        u16 chipid_top;
 156        u16 coreid = dev->id.coreid;
 157        struct ssb_hcd_device *usb_dev;
 158
 159        /* USBcores are only connected on embedded devices. */
 160        chipid_top = (dev->bus->chip_id & 0xFF00);
 161        if (chipid_top != 0x4700 && chipid_top != 0x5300)
 162                return -ENODEV;
 163
 164        /* TODO: Probably need checks here; is the core connected? */
 165
 166        if (dma_set_mask(dev->dma_dev, DMA_BIT_MASK(32)) ||
 167            dma_set_coherent_mask(dev->dma_dev, DMA_BIT_MASK(32)))
 168                return -EOPNOTSUPP;
 169
 170        usb_dev = kzalloc(sizeof(struct ssb_hcd_device), GFP_KERNEL);
 171        if (!usb_dev)
 172                return -ENOMEM;
 173
 174        /* We currently always attach SSB_DEV_USB11_HOSTDEV
 175         * as HOST OHCI. If we want to attach it as Client device,
 176         * we must branch here and call into the (yet to
 177         * be written) Client mode driver. Same for remove(). */
 178        usb_dev->enable_flags = ssb_hcd_init_chip(dev);
 179
 180        tmp = ssb_read32(dev, SSB_ADMATCH0);
 181
 182        start = ssb_admatch_base(tmp);
 183        len = (coreid == SSB_DEV_USB20_HOST) ? 0x800 : ssb_admatch_size(tmp);
 184        usb_dev->ohci_dev = ssb_hcd_create_pdev(dev, true, start, len);
 185        if (IS_ERR(usb_dev->ohci_dev)) {
 186                err = PTR_ERR(usb_dev->ohci_dev);
 187                goto err_free_usb_dev;
 188        }
 189
 190        if (coreid == SSB_DEV_USB20_HOST) {
 191                start = ssb_admatch_base(tmp) + 0x800; /* ehci core offset */
 192                usb_dev->ehci_dev = ssb_hcd_create_pdev(dev, false, start, len);
 193                if (IS_ERR(usb_dev->ehci_dev)) {
 194                        err = PTR_ERR(usb_dev->ehci_dev);
 195                        goto err_unregister_ohci_dev;
 196                }
 197        }
 198
 199        ssb_set_drvdata(dev, usb_dev);
 200        return 0;
 201
 202err_unregister_ohci_dev:
 203        platform_device_unregister(usb_dev->ohci_dev);
 204err_free_usb_dev:
 205        kfree(usb_dev);
 206        return err;
 207}
 208
 209static void ssb_hcd_remove(struct ssb_device *dev)
 210{
 211        struct ssb_hcd_device *usb_dev = ssb_get_drvdata(dev);
 212        struct platform_device *ohci_dev = usb_dev->ohci_dev;
 213        struct platform_device *ehci_dev = usb_dev->ehci_dev;
 214
 215        if (ohci_dev)
 216                platform_device_unregister(ohci_dev);
 217        if (ehci_dev)
 218                platform_device_unregister(ehci_dev);
 219
 220        ssb_device_disable(dev, 0);
 221}
 222
 223static void ssb_hcd_shutdown(struct ssb_device *dev)
 224{
 225        ssb_device_disable(dev, 0);
 226}
 227
 228#ifdef CONFIG_PM
 229
 230static int ssb_hcd_suspend(struct ssb_device *dev, pm_message_t state)
 231{
 232        ssb_device_disable(dev, 0);
 233
 234        return 0;
 235}
 236
 237static int ssb_hcd_resume(struct ssb_device *dev)
 238{
 239        struct ssb_hcd_device *usb_dev = ssb_get_drvdata(dev);
 240
 241        ssb_device_enable(dev, usb_dev->enable_flags);
 242
 243        return 0;
 244}
 245
 246#else /* !CONFIG_PM */
 247#define ssb_hcd_suspend NULL
 248#define ssb_hcd_resume  NULL
 249#endif /* CONFIG_PM */
 250
 251static const struct ssb_device_id ssb_hcd_table[] = {
 252        SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_USB11_HOSTDEV, SSB_ANY_REV),
 253        SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_USB11_HOST, SSB_ANY_REV),
 254        SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_USB20_HOST, SSB_ANY_REV),
 255        SSB_DEVTABLE_END
 256};
 257MODULE_DEVICE_TABLE(ssb, ssb_hcd_table);
 258
 259static struct ssb_driver ssb_hcd_driver = {
 260        .name           = KBUILD_MODNAME,
 261        .id_table       = ssb_hcd_table,
 262        .probe          = ssb_hcd_probe,
 263        .remove         = ssb_hcd_remove,
 264        .shutdown       = ssb_hcd_shutdown,
 265        .suspend        = ssb_hcd_suspend,
 266        .resume         = ssb_hcd_resume,
 267};
 268
 269static int __init ssb_hcd_init(void)
 270{
 271        return ssb_driver_register(&ssb_hcd_driver);
 272}
 273module_init(ssb_hcd_init);
 274
 275static void __exit ssb_hcd_exit(void)
 276{
 277        ssb_driver_unregister(&ssb_hcd_driver);
 278}
 279module_exit(ssb_hcd_exit);
 280