linux/drivers/usb/host/ehci-ppc-of.c
<<
>>
Prefs
   1/*
   2 * EHCI HCD (Host Controller Driver) for USB.
   3 *
   4 * Bus Glue for PPC On-Chip EHCI driver on the of_platform bus
   5 * Tested on AMCC PPC 440EPx
   6 *
   7 * Valentine Barshak <vbarshak@ru.mvista.com>
   8 *
   9 * Based on "ehci-ppc-soc.c" by Stefan Roese <sr@denx.de>
  10 * and "ohci-ppc-of.c" by Sylvain Munaut <tnt@246tNt.com>
  11 *
  12 * This file is licenced under the GPL.
  13 */
  14
  15#include <linux/err.h>
  16#include <linux/signal.h>
  17
  18#include <linux/of.h>
  19#include <linux/of_platform.h>
  20
  21
  22static const struct hc_driver ehci_ppc_of_hc_driver = {
  23        .description            = hcd_name,
  24        .product_desc           = "OF EHCI",
  25        .hcd_priv_size          = sizeof(struct ehci_hcd),
  26
  27        /*
  28         * generic hardware linkage
  29         */
  30        .irq                    = ehci_irq,
  31        .flags                  = HCD_MEMORY | HCD_USB2,
  32
  33        /*
  34         * basic lifecycle operations
  35         */
  36        .reset                  = ehci_setup,
  37        .start                  = ehci_run,
  38        .stop                   = ehci_stop,
  39        .shutdown               = ehci_shutdown,
  40
  41        /*
  42         * managing i/o requests and associated device resources
  43         */
  44        .urb_enqueue            = ehci_urb_enqueue,
  45        .urb_dequeue            = ehci_urb_dequeue,
  46        .endpoint_disable       = ehci_endpoint_disable,
  47        .endpoint_reset         = ehci_endpoint_reset,
  48
  49        /*
  50         * scheduling support
  51         */
  52        .get_frame_number       = ehci_get_frame,
  53
  54        /*
  55         * root hub support
  56         */
  57        .hub_status_data        = ehci_hub_status_data,
  58        .hub_control            = ehci_hub_control,
  59#ifdef  CONFIG_PM
  60        .bus_suspend            = ehci_bus_suspend,
  61        .bus_resume             = ehci_bus_resume,
  62#endif
  63        .relinquish_port        = ehci_relinquish_port,
  64        .port_handed_over       = ehci_port_handed_over,
  65
  66        .clear_tt_buffer_complete       = ehci_clear_tt_buffer_complete,
  67};
  68
  69
  70/*
  71 * 440EPx Errata USBH_3
  72 * Fix: Enable Break Memory Transfer (BMT) in INSNREG3
  73 */
  74#define PPC440EPX_EHCI0_INSREG_BMT      (0x1 << 0)
  75static int
  76ppc44x_enable_bmt(struct device_node *dn)
  77{
  78        __iomem u32 *insreg_virt;
  79
  80        insreg_virt = of_iomap(dn, 1);
  81        if (!insreg_virt)
  82                return  -EINVAL;
  83
  84        out_be32(insreg_virt + 3, PPC440EPX_EHCI0_INSREG_BMT);
  85
  86        iounmap(insreg_virt);
  87        return 0;
  88}
  89
  90
  91static int ehci_hcd_ppc_of_probe(struct platform_device *op)
  92{
  93        struct device_node *dn = op->dev.of_node;
  94        struct usb_hcd *hcd;
  95        struct ehci_hcd *ehci = NULL;
  96        struct resource res;
  97        int irq;
  98        int rv;
  99
 100        struct device_node *np;
 101
 102        if (usb_disabled())
 103                return -ENODEV;
 104
 105        dev_dbg(&op->dev, "initializing PPC-OF USB Controller\n");
 106
 107        rv = of_address_to_resource(dn, 0, &res);
 108        if (rv)
 109                return rv;
 110
 111        hcd = usb_create_hcd(&ehci_ppc_of_hc_driver, &op->dev, "PPC-OF USB");
 112        if (!hcd)
 113                return -ENOMEM;
 114
 115        hcd->rsrc_start = res.start;
 116        hcd->rsrc_len = resource_size(&res);
 117
 118        irq = irq_of_parse_and_map(dn, 0);
 119        if (irq == NO_IRQ) {
 120                printk(KERN_ERR "%s: irq_of_parse_and_map failed\n", __FILE__);
 121                rv = -EBUSY;
 122                goto err_irq;
 123        }
 124
 125        hcd->regs = devm_ioremap_resource(&op->dev, &res);
 126        if (IS_ERR(hcd->regs)) {
 127                rv = PTR_ERR(hcd->regs);
 128                goto err_ioremap;
 129        }
 130
 131        ehci = hcd_to_ehci(hcd);
 132        np = of_find_compatible_node(NULL, NULL, "ibm,usb-ohci-440epx");
 133        if (np != NULL) {
 134                /* claim we really affected by usb23 erratum */
 135                if (!of_address_to_resource(np, 0, &res))
 136                        ehci->ohci_hcctrl_reg =
 137                                devm_ioremap(&op->dev,
 138                                             res.start + OHCI_HCCTRL_OFFSET,
 139                                             OHCI_HCCTRL_LEN);
 140                else
 141                        pr_debug("%s: no ohci offset in fdt\n", __FILE__);
 142                if (!ehci->ohci_hcctrl_reg) {
 143                        pr_debug("%s: ioremap for ohci hcctrl failed\n", __FILE__);
 144                } else {
 145                        ehci->has_amcc_usb23 = 1;
 146                }
 147        }
 148
 149        if (of_get_property(dn, "big-endian", NULL)) {
 150                ehci->big_endian_mmio = 1;
 151                ehci->big_endian_desc = 1;
 152        }
 153        if (of_get_property(dn, "big-endian-regs", NULL))
 154                ehci->big_endian_mmio = 1;
 155        if (of_get_property(dn, "big-endian-desc", NULL))
 156                ehci->big_endian_desc = 1;
 157
 158        ehci->caps = hcd->regs;
 159
 160        if (of_device_is_compatible(dn, "ibm,usb-ehci-440epx")) {
 161                rv = ppc44x_enable_bmt(dn);
 162                ehci_dbg(ehci, "Break Memory Transfer (BMT) is %senabled!\n",
 163                                rv ? "NOT ": "");
 164        }
 165
 166        rv = usb_add_hcd(hcd, irq, 0);
 167        if (rv)
 168                goto err_ioremap;
 169
 170        return 0;
 171
 172err_ioremap:
 173        irq_dispose_mapping(irq);
 174err_irq:
 175        usb_put_hcd(hcd);
 176
 177        return rv;
 178}
 179
 180
 181static int ehci_hcd_ppc_of_remove(struct platform_device *op)
 182{
 183        struct usb_hcd *hcd = dev_get_drvdata(&op->dev);
 184        struct ehci_hcd *ehci = hcd_to_ehci(hcd);
 185
 186        struct device_node *np;
 187        struct resource res;
 188
 189        dev_set_drvdata(&op->dev, NULL);
 190
 191        dev_dbg(&op->dev, "stopping PPC-OF USB Controller\n");
 192
 193        usb_remove_hcd(hcd);
 194
 195        irq_dispose_mapping(hcd->irq);
 196
 197        /* use request_mem_region to test if the ohci driver is loaded.  if so
 198         * ensure the ohci core is operational.
 199         */
 200        if (ehci->has_amcc_usb23) {
 201                np = of_find_compatible_node(NULL, NULL, "ibm,usb-ohci-440epx");
 202                if (np != NULL) {
 203                        if (!of_address_to_resource(np, 0, &res))
 204                                if (!request_mem_region(res.start,
 205                                                            0x4, hcd_name))
 206                                        set_ohci_hcfs(ehci, 1);
 207                                else
 208                                        release_mem_region(res.start, 0x4);
 209                        else
 210                                pr_debug("%s: no ohci offset in fdt\n", __FILE__);
 211                        of_node_put(np);
 212                }
 213        }
 214        usb_put_hcd(hcd);
 215
 216        return 0;
 217}
 218
 219
 220static void ehci_hcd_ppc_of_shutdown(struct platform_device *op)
 221{
 222        struct usb_hcd *hcd = dev_get_drvdata(&op->dev);
 223
 224        if (hcd->driver->shutdown)
 225                hcd->driver->shutdown(hcd);
 226}
 227
 228
 229static const struct of_device_id ehci_hcd_ppc_of_match[] = {
 230        {
 231                .compatible = "usb-ehci",
 232        },
 233        {},
 234};
 235MODULE_DEVICE_TABLE(of, ehci_hcd_ppc_of_match);
 236
 237
 238static struct platform_driver ehci_hcd_ppc_of_driver = {
 239        .probe          = ehci_hcd_ppc_of_probe,
 240        .remove         = ehci_hcd_ppc_of_remove,
 241        .shutdown       = ehci_hcd_ppc_of_shutdown,
 242        .driver = {
 243                .name = "ppc-of-ehci",
 244                .owner = THIS_MODULE,
 245                .of_match_table = ehci_hcd_ppc_of_match,
 246        },
 247};
 248