uboot/drivers/phy/rockchip/phy-rockchip-inno-usb2.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * Rockchip USB2.0 PHY with Innosilicon IP block driver
   4 *
   5 * Copyright (C) 2016 Fuzhou Rockchip Electronics Co., Ltd
   6 * Copyright (C) 2020 Amarula Solutions(India)
   7 */
   8
   9#include <common.h>
  10#include <clk.h>
  11#include <dm.h>
  12#include <dm/device_compat.h>
  13#include <dm/lists.h>
  14#include <generic-phy.h>
  15#include <reset.h>
  16#include <syscon.h>
  17#include <asm/gpio.h>
  18#include <asm/io.h>
  19#include <linux/iopoll.h>
  20#include <asm/arch-rockchip/clock.h>
  21
  22DECLARE_GLOBAL_DATA_PTR;
  23
  24#define usleep_range(a, b) udelay((b))
  25#define BIT_WRITEABLE_SHIFT     16
  26
  27enum rockchip_usb2phy_port_id {
  28        USB2PHY_PORT_OTG,
  29        USB2PHY_PORT_HOST,
  30        USB2PHY_NUM_PORTS,
  31};
  32
  33struct usb2phy_reg {
  34        unsigned int    offset;
  35        unsigned int    bitend;
  36        unsigned int    bitstart;
  37        unsigned int    disable;
  38        unsigned int    enable;
  39};
  40
  41struct rockchip_usb2phy_port_cfg {
  42        struct usb2phy_reg      phy_sus;
  43        struct usb2phy_reg      bvalid_det_en;
  44        struct usb2phy_reg      bvalid_det_st;
  45        struct usb2phy_reg      bvalid_det_clr;
  46        struct usb2phy_reg      ls_det_en;
  47        struct usb2phy_reg      ls_det_st;
  48        struct usb2phy_reg      ls_det_clr;
  49        struct usb2phy_reg      utmi_avalid;
  50        struct usb2phy_reg      utmi_bvalid;
  51        struct usb2phy_reg      utmi_ls;
  52        struct usb2phy_reg      utmi_hstdet;
  53};
  54
  55struct rockchip_usb2phy_cfg {
  56        unsigned int reg;
  57        const struct rockchip_usb2phy_port_cfg port_cfgs[USB2PHY_NUM_PORTS];
  58};
  59
  60struct rockchip_usb2phy {
  61        void *reg_base;
  62        struct clk phyclk;
  63        const struct rockchip_usb2phy_cfg *phy_cfg;
  64};
  65
  66static inline int property_enable(void *reg_base,
  67                                  const struct usb2phy_reg *reg, bool en)
  68{
  69        unsigned int val, mask, tmp;
  70
  71        tmp = en ? reg->enable : reg->disable;
  72        mask = GENMASK(reg->bitend, reg->bitstart);
  73        val = (tmp << reg->bitstart) | (mask << BIT_WRITEABLE_SHIFT);
  74
  75        return writel(val, reg_base + reg->offset);
  76}
  77
  78static const
  79struct rockchip_usb2phy_port_cfg *us2phy_get_port(struct phy *phy)
  80{
  81        struct udevice *parent = dev_get_parent(phy->dev);
  82        struct rockchip_usb2phy *priv = dev_get_priv(parent);
  83        const struct rockchip_usb2phy_cfg *phy_cfg = priv->phy_cfg;
  84
  85        return &phy_cfg->port_cfgs[phy->id];
  86}
  87
  88static int rockchip_usb2phy_power_on(struct phy *phy)
  89{
  90        struct udevice *parent = dev_get_parent(phy->dev);
  91        struct rockchip_usb2phy *priv = dev_get_priv(parent);
  92        const struct rockchip_usb2phy_port_cfg *port_cfg = us2phy_get_port(phy);
  93
  94        property_enable(priv->reg_base, &port_cfg->phy_sus, false);
  95
  96        /* waiting for the utmi_clk to become stable */
  97        usleep_range(1500, 2000);
  98
  99        return 0;
 100}
 101
 102static int rockchip_usb2phy_power_off(struct phy *phy)
 103{
 104        struct udevice *parent = dev_get_parent(phy->dev);
 105        struct rockchip_usb2phy *priv = dev_get_priv(parent);
 106        const struct rockchip_usb2phy_port_cfg *port_cfg = us2phy_get_port(phy);
 107
 108        property_enable(priv->reg_base, &port_cfg->phy_sus, true);
 109
 110        return 0;
 111}
 112
 113static int rockchip_usb2phy_init(struct phy *phy)
 114{
 115        struct udevice *parent = dev_get_parent(phy->dev);
 116        struct rockchip_usb2phy *priv = dev_get_priv(parent);
 117        const struct rockchip_usb2phy_port_cfg *port_cfg = us2phy_get_port(phy);
 118        int ret;
 119
 120        ret = clk_enable(&priv->phyclk);
 121        if (ret) {
 122                dev_err(phy->dev, "failed to enable phyclk (ret=%d)\n", ret);
 123                return ret;
 124        }
 125
 126        if (phy->id == USB2PHY_PORT_OTG) {
 127                property_enable(priv->reg_base, &port_cfg->bvalid_det_clr, true);
 128                property_enable(priv->reg_base, &port_cfg->bvalid_det_en, true);
 129        } else if (phy->id == USB2PHY_PORT_HOST) {
 130                property_enable(priv->reg_base, &port_cfg->bvalid_det_clr, true);
 131                property_enable(priv->reg_base, &port_cfg->bvalid_det_en, true);
 132        }
 133
 134        return 0;
 135}
 136
 137static int rockchip_usb2phy_exit(struct phy *phy)
 138{
 139        struct udevice *parent = dev_get_parent(phy->dev);
 140        struct rockchip_usb2phy *priv = dev_get_priv(parent);
 141
 142        clk_disable(&priv->phyclk);
 143
 144        return 0;
 145}
 146
 147static int rockchip_usb2phy_of_xlate(struct phy *phy,
 148                                     struct ofnode_phandle_args *args)
 149{
 150        const char *name = phy->dev->name;
 151
 152        if (!strcasecmp(name, "host-port"))
 153                phy->id = USB2PHY_PORT_HOST;
 154        else if (!strcasecmp(name, "otg-port"))
 155                phy->id = USB2PHY_PORT_OTG;
 156        else
 157                dev_err(phy->dev, "improper %s device\n", name);
 158
 159        return 0;
 160}
 161
 162static struct phy_ops rockchip_usb2phy_ops = {
 163        .init = rockchip_usb2phy_init,
 164        .exit = rockchip_usb2phy_exit,
 165        .power_on = rockchip_usb2phy_power_on,
 166        .power_off = rockchip_usb2phy_power_off,
 167        .of_xlate = rockchip_usb2phy_of_xlate,
 168};
 169
 170static int rockchip_usb2phy_probe(struct udevice *dev)
 171{
 172        struct rockchip_usb2phy *priv = dev_get_priv(dev);
 173        const struct rockchip_usb2phy_cfg *phy_cfgs;
 174        unsigned int reg;
 175        int index, ret;
 176
 177        priv->reg_base = syscon_get_first_range(ROCKCHIP_SYSCON_GRF);
 178        if (IS_ERR(priv->reg_base))
 179                return PTR_ERR(priv->reg_base);
 180
 181        ret = ofnode_read_u32(dev_ofnode(dev), "reg", &reg);
 182        if (ret) {
 183                dev_err(dev, "failed to read reg property (ret = %d)\n", ret);
 184                return ret;
 185        }
 186
 187        phy_cfgs = (const struct rockchip_usb2phy_cfg *)
 188                                        dev_get_driver_data(dev);
 189        if (!phy_cfgs)
 190                return -EINVAL;
 191
 192        /* find out a proper config which can be matched with dt. */
 193        index = 0;
 194        while (phy_cfgs[index].reg) {
 195                if (phy_cfgs[index].reg == reg) {
 196                        priv->phy_cfg = &phy_cfgs[index];
 197                        break;
 198                }
 199
 200                ++index;
 201        }
 202
 203        if (!priv->phy_cfg) {
 204                dev_err(dev, "failed find proper phy-cfg\n");
 205                return -EINVAL;
 206        }
 207
 208        ret = clk_get_by_name(dev, "phyclk", &priv->phyclk);
 209        if (ret) {
 210                dev_err(dev, "failed to get the phyclk (ret=%d)\n", ret);
 211                return ret;
 212        }
 213
 214        return 0;
 215}
 216
 217static int rockchip_usb2phy_bind(struct udevice *dev)
 218{
 219        struct udevice *usb2phy_dev;
 220        ofnode node;
 221        const char *name;
 222        int ret = 0;
 223
 224        dev_for_each_subnode(node, dev) {
 225                if (!ofnode_valid(node)) {
 226                        dev_info(dev, "subnode %s not found\n", dev->name);
 227                        return -ENXIO;
 228                }
 229
 230                name = ofnode_get_name(node);
 231                dev_dbg(dev, "subnode %s\n", name);
 232
 233                ret = device_bind_driver_to_node(dev, "rockchip_usb2phy_port",
 234                                                 name, node, &usb2phy_dev);
 235                if (ret) {
 236                        dev_err(dev,
 237                                "'%s' cannot bind 'rockchip_usb2phy_port'\n", name);
 238                        return ret;
 239                }
 240        }
 241
 242        return ret;
 243}
 244
 245static const struct rockchip_usb2phy_cfg rk3399_usb2phy_cfgs[] = {
 246        {
 247                .reg            = 0xe450,
 248                .port_cfgs      = {
 249                        [USB2PHY_PORT_OTG] = {
 250                                .phy_sus        = { 0xe454, 1, 0, 2, 1 },
 251                                .bvalid_det_en  = { 0xe3c0, 3, 3, 0, 1 },
 252                                .bvalid_det_st  = { 0xe3e0, 3, 3, 0, 1 },
 253                                .bvalid_det_clr = { 0xe3d0, 3, 3, 0, 1 },
 254                                .utmi_avalid    = { 0xe2ac, 7, 7, 0, 1 },
 255                                .utmi_bvalid    = { 0xe2ac, 12, 12, 0, 1 },
 256                        },
 257                        [USB2PHY_PORT_HOST] = {
 258                                .phy_sus        = { 0xe458, 1, 0, 0x2, 0x1 },
 259                                .ls_det_en      = { 0xe3c0, 6, 6, 0, 1 },
 260                                .ls_det_st      = { 0xe3e0, 6, 6, 0, 1 },
 261                                .ls_det_clr     = { 0xe3d0, 6, 6, 0, 1 },
 262                                .utmi_ls        = { 0xe2ac, 22, 21, 0, 1 },
 263                                .utmi_hstdet    = { 0xe2ac, 23, 23, 0, 1 }
 264                        }
 265                },
 266        },
 267        {
 268                .reg            = 0xe460,
 269                .port_cfgs      = {
 270                        [USB2PHY_PORT_OTG] = {
 271                                .phy_sus        = { 0xe464, 1, 0, 2, 1 },
 272                                .bvalid_det_en  = { 0xe3c0, 8, 8, 0, 1 },
 273                                .bvalid_det_st  = { 0xe3e0, 8, 8, 0, 1 },
 274                                .bvalid_det_clr = { 0xe3d0, 8, 8, 0, 1 },
 275                                .utmi_avalid    = { 0xe2ac, 10, 10, 0, 1 },
 276                                .utmi_bvalid    = { 0xe2ac, 16, 16, 0, 1 },
 277                        },
 278                        [USB2PHY_PORT_HOST] = {
 279                                .phy_sus        = { 0xe468, 1, 0, 0x2, 0x1 },
 280                                .ls_det_en      = { 0xe3c0, 11, 11, 0, 1 },
 281                                .ls_det_st      = { 0xe3e0, 11, 11, 0, 1 },
 282                                .ls_det_clr     = { 0xe3d0, 11, 11, 0, 1 },
 283                                .utmi_ls        = { 0xe2ac, 26, 25, 0, 1 },
 284                                .utmi_hstdet    = { 0xe2ac, 27, 27, 0, 1 }
 285                        }
 286                },
 287        },
 288        { /* sentinel */ }
 289};
 290
 291static const struct udevice_id rockchip_usb2phy_ids[] = {
 292        {
 293                .compatible = "rockchip,rk3399-usb2phy",
 294                .data = (ulong)&rk3399_usb2phy_cfgs,
 295        },
 296        { /* sentinel */ }
 297};
 298
 299U_BOOT_DRIVER(rockchip_usb2phy_port) = {
 300        .name           = "rockchip_usb2phy_port",
 301        .id             = UCLASS_PHY,
 302        .ops            = &rockchip_usb2phy_ops,
 303};
 304
 305U_BOOT_DRIVER(rockchip_usb2phy) = {
 306        .name   = "rockchip_usb2phy",
 307        .id     = UCLASS_PHY,
 308        .of_match = rockchip_usb2phy_ids,
 309        .probe = rockchip_usb2phy_probe,
 310        .bind = rockchip_usb2phy_bind,
 311        .priv_auto_alloc_size = sizeof(struct rockchip_usb2phy),
 312};
 313