linux/drivers/usb/cdns3/cdns3-ti.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/**
   3 * cdns3-ti.c - TI specific Glue layer for Cadence USB Controller
   4 *
   5 * Copyright (C) 2019 Texas Instruments Incorporated - https://www.ti.com
   6 */
   7
   8#include <linux/bits.h>
   9#include <linux/clk.h>
  10#include <linux/module.h>
  11#include <linux/kernel.h>
  12#include <linux/interrupt.h>
  13#include <linux/platform_device.h>
  14#include <linux/dma-mapping.h>
  15#include <linux/io.h>
  16#include <linux/of_platform.h>
  17#include <linux/pm_runtime.h>
  18
  19/* USB Wrapper register offsets */
  20#define USBSS_PID               0x0
  21#define USBSS_W1                0x4
  22#define USBSS_STATIC_CONFIG     0x8
  23#define USBSS_PHY_TEST          0xc
  24#define USBSS_DEBUG_CTRL        0x10
  25#define USBSS_DEBUG_INFO        0x14
  26#define USBSS_DEBUG_LINK_STATE  0x18
  27#define USBSS_DEVICE_CTRL       0x1c
  28
  29/* Wrapper 1 register bits */
  30#define USBSS_W1_PWRUP_RST              BIT(0)
  31#define USBSS_W1_OVERCURRENT_SEL        BIT(8)
  32#define USBSS_W1_MODESTRAP_SEL          BIT(9)
  33#define USBSS_W1_OVERCURRENT            BIT(16)
  34#define USBSS_W1_MODESTRAP_MASK         GENMASK(18, 17)
  35#define USBSS_W1_MODESTRAP_SHIFT        17
  36#define USBSS_W1_USB2_ONLY              BIT(19)
  37
  38/* Static config register bits */
  39#define USBSS1_STATIC_PLL_REF_SEL_MASK  GENMASK(8, 5)
  40#define USBSS1_STATIC_PLL_REF_SEL_SHIFT 5
  41#define USBSS1_STATIC_LOOPBACK_MODE_MASK        GENMASK(4, 3)
  42#define USBSS1_STATIC_LOOPBACK_MODE_SHIFT       3
  43#define USBSS1_STATIC_VBUS_SEL_MASK     GENMASK(2, 1)
  44#define USBSS1_STATIC_VBUS_SEL_SHIFT    1
  45#define USBSS1_STATIC_LANE_REVERSE      BIT(0)
  46
  47/* Modestrap modes */
  48enum modestrap_mode { USBSS_MODESTRAP_MODE_NONE,
  49                      USBSS_MODESTRAP_MODE_HOST,
  50                      USBSS_MODESTRAP_MODE_PERIPHERAL};
  51
  52struct cdns_ti {
  53        struct device *dev;
  54        void __iomem *usbss;
  55        unsigned usb2_only:1;
  56        unsigned vbus_divider:1;
  57        struct clk *usb2_refclk;
  58        struct clk *lpm_clk;
  59};
  60
  61static const int cdns_ti_rate_table[] = {       /* in KHZ */
  62        9600,
  63        10000,
  64        12000,
  65        19200,
  66        20000,
  67        24000,
  68        25000,
  69        26000,
  70        38400,
  71        40000,
  72        58000,
  73        50000,
  74        52000,
  75};
  76
  77static inline u32 cdns_ti_readl(struct cdns_ti *data, u32 offset)
  78{
  79        return readl(data->usbss + offset);
  80}
  81
  82static inline void cdns_ti_writel(struct cdns_ti *data, u32 offset, u32 value)
  83{
  84        writel(value, data->usbss + offset);
  85}
  86
  87static int cdns_ti_probe(struct platform_device *pdev)
  88{
  89        struct device *dev = &pdev->dev;
  90        struct device_node *node = pdev->dev.of_node;
  91        struct cdns_ti *data;
  92        int error;
  93        u32 reg;
  94        int rate_code, i;
  95        unsigned long rate;
  96
  97        data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
  98        if (!data)
  99                return -ENOMEM;
 100
 101        platform_set_drvdata(pdev, data);
 102
 103        data->dev = dev;
 104
 105        data->usbss = devm_platform_ioremap_resource(pdev, 0);
 106        if (IS_ERR(data->usbss)) {
 107                dev_err(dev, "can't map IOMEM resource\n");
 108                return PTR_ERR(data->usbss);
 109        }
 110
 111        data->usb2_refclk = devm_clk_get(dev, "ref");
 112        if (IS_ERR(data->usb2_refclk)) {
 113                dev_err(dev, "can't get usb2_refclk\n");
 114                return PTR_ERR(data->usb2_refclk);
 115        }
 116
 117        data->lpm_clk = devm_clk_get(dev, "lpm");
 118        if (IS_ERR(data->lpm_clk)) {
 119                dev_err(dev, "can't get lpm_clk\n");
 120                return PTR_ERR(data->lpm_clk);
 121        }
 122
 123        rate = clk_get_rate(data->usb2_refclk);
 124        rate /= 1000;   /* To KHz */
 125        for (i = 0; i < ARRAY_SIZE(cdns_ti_rate_table); i++) {
 126                if (cdns_ti_rate_table[i] == rate)
 127                        break;
 128        }
 129
 130        if (i == ARRAY_SIZE(cdns_ti_rate_table)) {
 131                dev_err(dev, "unsupported usb2_refclk rate: %lu KHz\n", rate);
 132                return -EINVAL;
 133        }
 134
 135        rate_code = i;
 136
 137        pm_runtime_enable(dev);
 138        error = pm_runtime_get_sync(dev);
 139        if (error < 0) {
 140                dev_err(dev, "pm_runtime_get_sync failed: %d\n", error);
 141                goto err;
 142        }
 143
 144        /* assert RESET */
 145        reg = cdns_ti_readl(data, USBSS_W1);
 146        reg &= ~USBSS_W1_PWRUP_RST;
 147        cdns_ti_writel(data, USBSS_W1, reg);
 148
 149        /* set static config */
 150        reg = cdns_ti_readl(data, USBSS_STATIC_CONFIG);
 151        reg &= ~USBSS1_STATIC_PLL_REF_SEL_MASK;
 152        reg |= rate_code << USBSS1_STATIC_PLL_REF_SEL_SHIFT;
 153
 154        reg &= ~USBSS1_STATIC_VBUS_SEL_MASK;
 155        data->vbus_divider = device_property_read_bool(dev, "ti,vbus-divider");
 156        if (data->vbus_divider)
 157                reg |= 1 << USBSS1_STATIC_VBUS_SEL_SHIFT;
 158
 159        cdns_ti_writel(data, USBSS_STATIC_CONFIG, reg);
 160        reg = cdns_ti_readl(data, USBSS_STATIC_CONFIG);
 161
 162        /* set USB2_ONLY mode if requested */
 163        reg = cdns_ti_readl(data, USBSS_W1);
 164        data->usb2_only = device_property_read_bool(dev, "ti,usb2-only");
 165        if (data->usb2_only)
 166                reg |= USBSS_W1_USB2_ONLY;
 167
 168        /* set default modestrap */
 169        reg |= USBSS_W1_MODESTRAP_SEL;
 170        reg &= ~USBSS_W1_MODESTRAP_MASK;
 171        reg |= USBSS_MODESTRAP_MODE_NONE << USBSS_W1_MODESTRAP_SHIFT;
 172        cdns_ti_writel(data, USBSS_W1, reg);
 173
 174        /* de-assert RESET */
 175        reg |= USBSS_W1_PWRUP_RST;
 176        cdns_ti_writel(data, USBSS_W1, reg);
 177
 178        error = of_platform_populate(node, NULL, NULL, dev);
 179        if (error) {
 180                dev_err(dev, "failed to create children: %d\n", error);
 181                goto err;
 182        }
 183
 184        return 0;
 185
 186err:
 187        pm_runtime_put_sync(data->dev);
 188        pm_runtime_disable(data->dev);
 189
 190        return error;
 191}
 192
 193static int cdns_ti_remove_core(struct device *dev, void *c)
 194{
 195        struct platform_device *pdev = to_platform_device(dev);
 196
 197        platform_device_unregister(pdev);
 198
 199        return 0;
 200}
 201
 202static int cdns_ti_remove(struct platform_device *pdev)
 203{
 204        struct device *dev = &pdev->dev;
 205
 206        device_for_each_child(dev, NULL, cdns_ti_remove_core);
 207        pm_runtime_put_sync(dev);
 208        pm_runtime_disable(dev);
 209
 210        platform_set_drvdata(pdev, NULL);
 211
 212        return 0;
 213}
 214
 215static const struct of_device_id cdns_ti_of_match[] = {
 216        { .compatible = "ti,j721e-usb", },
 217        {},
 218};
 219MODULE_DEVICE_TABLE(of, cdns_ti_of_match);
 220
 221static struct platform_driver cdns_ti_driver = {
 222        .probe          = cdns_ti_probe,
 223        .remove         = cdns_ti_remove,
 224        .driver         = {
 225                .name   = "cdns3-ti",
 226                .of_match_table = cdns_ti_of_match,
 227        },
 228};
 229
 230module_platform_driver(cdns_ti_driver);
 231
 232MODULE_ALIAS("platform:cdns3-ti");
 233MODULE_AUTHOR("Roger Quadros <rogerq@ti.com>");
 234MODULE_LICENSE("GPL v2");
 235MODULE_DESCRIPTION("Cadence USB3 TI Glue Layer");
 236