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