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_and_coherent(dev->dma_dev, DMA_BIT_MASK(32)))
 167                return -EOPNOTSUPP;
 168
 169        usb_dev = kzalloc(sizeof(struct ssb_hcd_device), GFP_KERNEL);
 170        if (!usb_dev)
 171                return -ENOMEM;
 172
 173        /* We currently always attach SSB_DEV_USB11_HOSTDEV
 174         * as HOST OHCI. If we want to attach it as Client device,
 175         * we must branch here and call into the (yet to
 176         * be written) Client mode driver. Same for remove(). */
 177        usb_dev->enable_flags = ssb_hcd_init_chip(dev);
 178
 179        tmp = ssb_read32(dev, SSB_ADMATCH0);
 180
 181        start = ssb_admatch_base(tmp);
 182        len = (coreid == SSB_DEV_USB20_HOST) ? 0x800 : ssb_admatch_size(tmp);
 183        usb_dev->ohci_dev = ssb_hcd_create_pdev(dev, true, start, len);
 184        if (IS_ERR(usb_dev->ohci_dev)) {
 185                err = PTR_ERR(usb_dev->ohci_dev);
 186                goto err_free_usb_dev;
 187        }
 188
 189        if (coreid == SSB_DEV_USB20_HOST) {
 190                start = ssb_admatch_base(tmp) + 0x800; /* ehci core offset */
 191                usb_dev->ehci_dev = ssb_hcd_create_pdev(dev, false, start, len);
 192                if (IS_ERR(usb_dev->ehci_dev)) {
 193                        err = PTR_ERR(usb_dev->ehci_dev);
 194                        goto err_unregister_ohci_dev;
 195                }
 196        }
 197
 198        ssb_set_drvdata(dev, usb_dev);
 199        return 0;
 200
 201err_unregister_ohci_dev:
 202        platform_device_unregister(usb_dev->ohci_dev);
 203err_free_usb_dev:
 204        kfree(usb_dev);
 205        return err;
 206}
 207
 208static void ssb_hcd_remove(struct ssb_device *dev)
 209{
 210        struct ssb_hcd_device *usb_dev = ssb_get_drvdata(dev);
 211        struct platform_device *ohci_dev = usb_dev->ohci_dev;
 212        struct platform_device *ehci_dev = usb_dev->ehci_dev;
 213
 214        if (ohci_dev)
 215                platform_device_unregister(ohci_dev);
 216        if (ehci_dev)
 217                platform_device_unregister(ehci_dev);
 218
 219        ssb_device_disable(dev, 0);
 220}
 221
 222static void ssb_hcd_shutdown(struct ssb_device *dev)
 223{
 224        ssb_device_disable(dev, 0);
 225}
 226
 227#ifdef CONFIG_PM
 228
 229static int ssb_hcd_suspend(struct ssb_device *dev, pm_message_t state)
 230{
 231        ssb_device_disable(dev, 0);
 232
 233        return 0;
 234}
 235
 236static int ssb_hcd_resume(struct ssb_device *dev)
 237{
 238        struct ssb_hcd_device *usb_dev = ssb_get_drvdata(dev);
 239
 240        ssb_device_enable(dev, usb_dev->enable_flags);
 241
 242        return 0;
 243}
 244
 245#else /* !CONFIG_PM */
 246#define ssb_hcd_suspend NULL
 247#define ssb_hcd_resume  NULL
 248#endif /* CONFIG_PM */
 249
 250static const struct ssb_device_id ssb_hcd_table[] = {
 251        SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_USB11_HOSTDEV, SSB_ANY_REV),
 252        SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_USB11_HOST, SSB_ANY_REV),
 253        SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_USB20_HOST, SSB_ANY_REV),
 254        SSB_DEVTABLE_END
 255};
 256MODULE_DEVICE_TABLE(ssb, ssb_hcd_table);
 257
 258static struct ssb_driver ssb_hcd_driver = {
 259        .name           = KBUILD_MODNAME,
 260        .id_table       = ssb_hcd_table,
 261        .probe          = ssb_hcd_probe,
 262        .remove         = ssb_hcd_remove,
 263        .shutdown       = ssb_hcd_shutdown,
 264        .suspend        = ssb_hcd_suspend,
 265        .resume         = ssb_hcd_resume,
 266};
 267
 268static int __init ssb_hcd_init(void)
 269{
 270        return ssb_driver_register(&ssb_hcd_driver);
 271}
 272module_init(ssb_hcd_init);
 273
 274static void __exit ssb_hcd_exit(void)
 275{
 276        ssb_driver_unregister(&ssb_hcd_driver);
 277}
 278module_exit(ssb_hcd_exit);
 279