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