uboot/drivers/phy/omap-usb2-phy.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * OMAP USB2 PHY LAYER
   4 *
   5 * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com
   6 * Written by Jean-Jacques Hiblot <jjhiblot@ti.com>
   7 */
   8
   9#include <common.h>
  10#include <asm/io.h>
  11#include <dm.h>
  12#include <errno.h>
  13#include <generic-phy.h>
  14#include <regmap.h>
  15#include <soc.h>
  16#include <syscon.h>
  17#include <linux/bitops.h>
  18#include <linux/err.h>
  19
  20#define OMAP_USB2_CALIBRATE_FALSE_DISCONNECT    BIT(0)
  21#define OMAP_USB2_DISABLE_CHG_DET               BIT(1)
  22
  23#define OMAP_DEV_PHY_PD         BIT(0)
  24#define OMAP_USB2_PHY_PD        BIT(28)
  25
  26#define AM437X_USB2_PHY_PD              BIT(0)
  27#define AM437X_USB2_OTG_PD              BIT(1)
  28#define AM437X_USB2_OTGVDET_EN          BIT(19)
  29#define AM437X_USB2_OTGSESSEND_EN       BIT(20)
  30
  31#define USB2PHY_DISCON_BYP_LATCH        BIT(31)
  32#define USB2PHY_ANA_CONFIG1             (0x4c)
  33
  34#define AM654_USB2_OTG_PD               BIT(8)
  35#define AM654_USB2_VBUS_DET_EN          BIT(5)
  36#define AM654_USB2_VBUSVALID_DET_EN     BIT(4)
  37
  38#define USB2PHY_CHRG_DET                 0x14
  39#define USB2PHY_USE_CHG_DET_REG         BIT(29)
  40#define USB2PHY_DIS_CHG_DET             BIT(28)
  41
  42DECLARE_GLOBAL_DATA_PTR;
  43
  44struct omap_usb2_phy {
  45        struct regmap *pwr_regmap;
  46        ulong flags;
  47        void *phy_base;
  48        u32 pwr_reg_offset;
  49};
  50
  51struct usb_phy_data {
  52        const char *label;
  53        u8 flags;
  54        u32 mask;
  55        u32 power_on;
  56        u32 power_off;
  57};
  58
  59static const struct usb_phy_data omap5_usb2_data = {
  60        .label = "omap5_usb2",
  61        .flags = 0,
  62        .mask = OMAP_DEV_PHY_PD,
  63        .power_off = OMAP_DEV_PHY_PD,
  64};
  65
  66static const struct usb_phy_data dra7x_usb2_data = {
  67        .label = "dra7x_usb2",
  68        .flags = OMAP_USB2_CALIBRATE_FALSE_DISCONNECT,
  69        .mask = OMAP_DEV_PHY_PD,
  70        .power_off = OMAP_DEV_PHY_PD,
  71};
  72
  73static const struct usb_phy_data dra7x_usb2_phy2_data = {
  74        .label = "dra7x_usb2_phy2",
  75        .flags = OMAP_USB2_CALIBRATE_FALSE_DISCONNECT,
  76        .mask = OMAP_USB2_PHY_PD,
  77        .power_off = OMAP_USB2_PHY_PD,
  78};
  79
  80static const struct usb_phy_data am437x_usb2_data = {
  81        .label = "am437x_usb2",
  82        .flags =  0,
  83        .mask = AM437X_USB2_PHY_PD | AM437X_USB2_OTG_PD |
  84                AM437X_USB2_OTGVDET_EN | AM437X_USB2_OTGSESSEND_EN,
  85        .power_on = AM437X_USB2_OTGVDET_EN | AM437X_USB2_OTGSESSEND_EN,
  86        .power_off = AM437X_USB2_PHY_PD | AM437X_USB2_OTG_PD,
  87};
  88
  89static const struct usb_phy_data am654_usb2_data = {
  90        .label = "am654_usb2",
  91        .flags = OMAP_USB2_CALIBRATE_FALSE_DISCONNECT,
  92        .mask = AM654_USB2_OTG_PD | AM654_USB2_VBUS_DET_EN |
  93                AM654_USB2_VBUSVALID_DET_EN,
  94        .power_on = AM654_USB2_VBUS_DET_EN | AM654_USB2_VBUSVALID_DET_EN,
  95        .power_off = AM654_USB2_OTG_PD,
  96};
  97
  98static const struct udevice_id omap_usb2_id_table[] = {
  99        {
 100                .compatible = "ti,omap5-usb2",
 101                .data = (ulong)&omap5_usb2_data,
 102        },
 103        {
 104                .compatible = "ti,dra7x-usb2",
 105                .data = (ulong)&dra7x_usb2_data,
 106        },
 107        {
 108                .compatible = "ti,dra7x-usb2-phy2",
 109                .data = (ulong)&dra7x_usb2_phy2_data,
 110        },
 111        {
 112                .compatible = "ti,am437x-usb2",
 113                .data = (ulong)&am437x_usb2_data,
 114        },
 115        {
 116                .compatible = "ti,am654-usb2",
 117                .data = (ulong)&am654_usb2_data,
 118        },
 119        {},
 120};
 121
 122static int omap_usb_phy_power(struct phy *usb_phy, bool on)
 123{
 124        struct udevice *dev = usb_phy->dev;
 125        const struct usb_phy_data *data;
 126        const struct omap_usb2_phy *phy = dev_get_priv(dev);
 127        u32 val;
 128        int rc;
 129
 130        data = (const struct usb_phy_data *)dev_get_driver_data(dev);
 131        if (!data)
 132                return -EINVAL;
 133
 134        rc = regmap_read(phy->pwr_regmap, phy->pwr_reg_offset, &val);
 135        if (rc)
 136                return rc;
 137        val &= ~data->mask;
 138        if (on)
 139                val |= data->power_on;
 140        else
 141                val |= data->power_off;
 142        rc = regmap_write(phy->pwr_regmap, phy->pwr_reg_offset, val);
 143        if (rc)
 144                return rc;
 145
 146        return 0;
 147}
 148
 149static int omap_usb2_phy_init(struct phy *usb_phy)
 150{
 151        struct udevice *dev = usb_phy->dev;
 152        struct omap_usb2_phy *priv = dev_get_priv(dev);
 153        u32 val;
 154
 155        if (priv->flags & OMAP_USB2_CALIBRATE_FALSE_DISCONNECT) {
 156                /*
 157                 *
 158                 * Reduce the sensitivity of internal PHY by enabling the
 159                 * DISCON_BYP_LATCH of the USB2PHY_ANA_CONFIG1 register. This
 160                 * resolves issues with certain devices which can otherwise
 161                 * be prone to false disconnects.
 162                 *
 163                 */
 164                val = readl(priv->phy_base + USB2PHY_ANA_CONFIG1);
 165                val |= USB2PHY_DISCON_BYP_LATCH;
 166                writel(val, priv->phy_base + USB2PHY_ANA_CONFIG1);
 167        }
 168
 169        if (priv->flags & OMAP_USB2_DISABLE_CHG_DET) {
 170                val = readl(priv->phy_base + USB2PHY_CHRG_DET);
 171                val |= USB2PHY_USE_CHG_DET_REG | USB2PHY_DIS_CHG_DET;
 172                writel(val, priv->phy_base + USB2PHY_CHRG_DET);
 173        }
 174
 175        return 0;
 176}
 177
 178static int omap_usb2_phy_power_on(struct phy *usb_phy)
 179{
 180        return omap_usb_phy_power(usb_phy, true);
 181}
 182
 183static int omap_usb2_phy_power_off(struct phy *usb_phy)
 184{
 185        return omap_usb_phy_power(usb_phy, false);
 186}
 187
 188static int omap_usb2_phy_exit(struct phy *usb_phy)
 189{
 190        return omap_usb_phy_power(usb_phy, false);
 191}
 192
 193struct phy_ops omap_usb2_phy_ops = {
 194        .init = omap_usb2_phy_init,
 195        .power_on = omap_usb2_phy_power_on,
 196        .power_off = omap_usb2_phy_power_off,
 197        .exit = omap_usb2_phy_exit,
 198};
 199
 200static const struct soc_attr am65x_sr10_soc_devices[] = {
 201        { .family = "AM65X", .revision = "SR1.0" },
 202        { /* sentinel */ }
 203};
 204
 205int omap_usb2_phy_probe(struct udevice *dev)
 206{
 207        int rc;
 208        struct regmap *regmap;
 209        struct omap_usb2_phy *priv = dev_get_priv(dev);
 210        const struct usb_phy_data *data;
 211        u32 tmp[2];
 212
 213        data = (const struct usb_phy_data *)dev_get_driver_data(dev);
 214        if (!data)
 215                return -EINVAL;
 216
 217        priv->phy_base = dev_read_addr_ptr(dev);
 218
 219        if (!priv->phy_base)
 220                return -EINVAL;
 221
 222        if (data->flags & OMAP_USB2_CALIBRATE_FALSE_DISCONNECT)
 223                priv->flags |= OMAP_USB2_CALIBRATE_FALSE_DISCONNECT;
 224
 225        /*
 226         * AM654x PG1.0 has a silicon bug that D+ is pulled high after
 227         * POR, which could cause enumeration failure with some USB hubs.
 228         * Disabling the USB2_PHY Charger Detect function will put D+
 229         * into the normal state.
 230         *
 231         * Enable this workaround for AM654x PG1.0.
 232         */
 233        if (soc_device_match(am65x_sr10_soc_devices))
 234                priv->flags |= OMAP_USB2_DISABLE_CHG_DET;
 235
 236        regmap = syscon_regmap_lookup_by_phandle(dev, "syscon-phy-power");
 237        if (!IS_ERR(regmap)) {
 238                priv->pwr_regmap = regmap;
 239                rc =  dev_read_u32_array(dev, "syscon-phy-power", tmp, 2);
 240                if (rc) {
 241                        printf("couldn't get power reg. offset (err %d)\n", rc);
 242                        return rc;
 243                }
 244                priv->pwr_reg_offset = tmp[1];
 245                return 0;
 246        }
 247        regmap = syscon_regmap_lookup_by_phandle(dev, "ctrl-module");
 248        if (!IS_ERR(regmap)) {
 249                priv->pwr_regmap = regmap;
 250                priv->pwr_reg_offset = 0;
 251                return 0;
 252        }
 253
 254        printf("can't get regmap (err %ld)\n", PTR_ERR(regmap));
 255        return PTR_ERR(regmap);
 256}
 257
 258U_BOOT_DRIVER(omap_usb2_phy) = {
 259        .name = "omap_usb2_phy",
 260        .id = UCLASS_PHY,
 261        .of_match = omap_usb2_id_table,
 262        .probe = omap_usb2_phy_probe,
 263        .ops = &omap_usb2_phy_ops,
 264        .priv_auto_alloc_size = sizeof(struct omap_usb2_phy),
 265};
 266