linux/drivers/usb/musb/jz4740.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Ingenic JZ4740 "glue layer"
   4 *
   5 * Copyright (C) 2013, Apelete Seketeli <apelete@seketeli.net>
   6 */
   7
   8#include <linux/clk.h>
   9#include <linux/dma-mapping.h>
  10#include <linux/errno.h>
  11#include <linux/kernel.h>
  12#include <linux/module.h>
  13#include <linux/of_device.h>
  14#include <linux/platform_device.h>
  15#include <linux/usb/usb_phy_generic.h>
  16
  17#include "musb_core.h"
  18
  19struct jz4740_glue {
  20        struct device           *dev;
  21        struct platform_device  *musb;
  22        struct clk              *clk;
  23};
  24
  25static irqreturn_t jz4740_musb_interrupt(int irq, void *__hci)
  26{
  27        unsigned long   flags;
  28        irqreturn_t     retval = IRQ_NONE;
  29        struct musb     *musb = __hci;
  30
  31        spin_lock_irqsave(&musb->lock, flags);
  32
  33        musb->int_usb = musb_readb(musb->mregs, MUSB_INTRUSB);
  34        musb->int_tx = musb_readw(musb->mregs, MUSB_INTRTX);
  35        musb->int_rx = musb_readw(musb->mregs, MUSB_INTRRX);
  36
  37        /*
  38         * The controller is gadget only, the state of the host mode IRQ bits is
  39         * undefined. Mask them to make sure that the musb driver core will
  40         * never see them set
  41         */
  42        musb->int_usb &= MUSB_INTR_SUSPEND | MUSB_INTR_RESUME |
  43            MUSB_INTR_RESET | MUSB_INTR_SOF;
  44
  45        if (musb->int_usb || musb->int_tx || musb->int_rx)
  46                retval = musb_interrupt(musb);
  47
  48        spin_unlock_irqrestore(&musb->lock, flags);
  49
  50        return retval;
  51}
  52
  53static struct musb_fifo_cfg jz4740_musb_fifo_cfg[] = {
  54{ .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, },
  55{ .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, },
  56{ .hw_ep_num = 2, .style = FIFO_TX, .maxpacket = 64, },
  57};
  58
  59static const struct musb_hdrc_config jz4740_musb_config = {
  60        /* Silicon does not implement USB OTG. */
  61        .multipoint = 0,
  62        /* Max EPs scanned, driver will decide which EP can be used. */
  63        .num_eps    = 4,
  64        /* RAMbits needed to configure EPs from table */
  65        .ram_bits   = 9,
  66        .fifo_cfg = jz4740_musb_fifo_cfg,
  67        .fifo_cfg_size = ARRAY_SIZE(jz4740_musb_fifo_cfg),
  68};
  69
  70static struct musb_hdrc_platform_data jz4740_musb_platform_data = {
  71        .mode   = MUSB_PERIPHERAL,
  72        .config = &jz4740_musb_config,
  73};
  74
  75static int jz4740_musb_init(struct musb *musb)
  76{
  77        struct device *dev = musb->controller->parent;
  78
  79        if (dev->of_node)
  80                musb->xceiv = devm_usb_get_phy_by_phandle(dev, "phys", 0);
  81        else
  82                musb->xceiv = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2);
  83        if (IS_ERR(musb->xceiv)) {
  84                dev_err(dev, "No transceiver configured\n");
  85                return PTR_ERR(musb->xceiv);
  86        }
  87
  88        /* Silicon does not implement ConfigData register.
  89         * Set dyn_fifo to avoid reading EP config from hardware.
  90         */
  91        musb->dyn_fifo = true;
  92
  93        musb->isr = jz4740_musb_interrupt;
  94
  95        return 0;
  96}
  97
  98/*
  99 * DMA has not been confirmed to work with CONFIG_USB_INVENTRA_DMA,
 100 * so let's not set up the dma function pointers yet.
 101 */
 102static const struct musb_platform_ops jz4740_musb_ops = {
 103        .quirks         = MUSB_DMA_INVENTRA | MUSB_INDEXED_EP,
 104        .fifo_mode      = 2,
 105        .init           = jz4740_musb_init,
 106};
 107
 108static int jz4740_probe(struct platform_device *pdev)
 109{
 110        struct musb_hdrc_platform_data  *pdata = &jz4740_musb_platform_data;
 111        struct platform_device          *musb;
 112        struct jz4740_glue              *glue;
 113        struct clk                      *clk;
 114        int                             ret;
 115
 116        glue = devm_kzalloc(&pdev->dev, sizeof(*glue), GFP_KERNEL);
 117        if (!glue)
 118                return -ENOMEM;
 119
 120        musb = platform_device_alloc("musb-hdrc", PLATFORM_DEVID_AUTO);
 121        if (!musb) {
 122                dev_err(&pdev->dev, "failed to allocate musb device\n");
 123                return -ENOMEM;
 124        }
 125
 126        clk = devm_clk_get(&pdev->dev, "udc");
 127        if (IS_ERR(clk)) {
 128                dev_err(&pdev->dev, "failed to get clock\n");
 129                ret = PTR_ERR(clk);
 130                goto err_platform_device_put;
 131        }
 132
 133        ret = clk_prepare_enable(clk);
 134        if (ret) {
 135                dev_err(&pdev->dev, "failed to enable clock\n");
 136                goto err_platform_device_put;
 137        }
 138
 139        musb->dev.parent                = &pdev->dev;
 140
 141        glue->dev                       = &pdev->dev;
 142        glue->musb                      = musb;
 143        glue->clk                       = clk;
 144
 145        pdata->platform_ops             = &jz4740_musb_ops;
 146
 147        platform_set_drvdata(pdev, glue);
 148
 149        ret = platform_device_add_resources(musb, pdev->resource,
 150                                            pdev->num_resources);
 151        if (ret) {
 152                dev_err(&pdev->dev, "failed to add resources\n");
 153                goto err_clk_disable;
 154        }
 155
 156        ret = platform_device_add_data(musb, pdata, sizeof(*pdata));
 157        if (ret) {
 158                dev_err(&pdev->dev, "failed to add platform_data\n");
 159                goto err_clk_disable;
 160        }
 161
 162        ret = platform_device_add(musb);
 163        if (ret) {
 164                dev_err(&pdev->dev, "failed to register musb device\n");
 165                goto err_clk_disable;
 166        }
 167
 168        return 0;
 169
 170err_clk_disable:
 171        clk_disable_unprepare(clk);
 172err_platform_device_put:
 173        platform_device_put(musb);
 174        return ret;
 175}
 176
 177static int jz4740_remove(struct platform_device *pdev)
 178{
 179        struct jz4740_glue      *glue = platform_get_drvdata(pdev);
 180
 181        platform_device_unregister(glue->musb);
 182        clk_disable_unprepare(glue->clk);
 183
 184        return 0;
 185}
 186
 187#ifdef CONFIG_OF
 188static const struct of_device_id jz4740_musb_of_match[] = {
 189        { .compatible = "ingenic,jz4740-musb" },
 190        {},
 191};
 192MODULE_DEVICE_TABLE(of, jz4740_musb_of_match);
 193#endif
 194
 195static struct platform_driver jz4740_driver = {
 196        .probe          = jz4740_probe,
 197        .remove         = jz4740_remove,
 198        .driver         = {
 199                .name   = "musb-jz4740",
 200                .of_match_table = of_match_ptr(jz4740_musb_of_match),
 201        },
 202};
 203
 204MODULE_DESCRIPTION("JZ4740 MUSB Glue Layer");
 205MODULE_AUTHOR("Apelete Seketeli <apelete@seketeli.net>");
 206MODULE_LICENSE("GPL v2");
 207module_platform_driver(jz4740_driver);
 208