linux/drivers/phy/phy-lgm-usb.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Intel LGM USB PHY driver
   4 *
   5 * Copyright (C) 2020 Intel Corporation.
   6 */
   7
   8#include <linux/bitfield.h>
   9#include <linux/delay.h>
  10#include <linux/iopoll.h>
  11#include <linux/module.h>
  12#include <linux/of.h>
  13#include <linux/platform_device.h>
  14#include <linux/regulator/consumer.h>
  15#include <linux/reset.h>
  16#include <linux/usb/phy.h>
  17#include <linux/workqueue.h>
  18
  19#define CTRL1_OFFSET            0x14
  20#define SRAM_EXT_LD_DONE        BIT(25)
  21#define SRAM_INIT_DONE          BIT(26)
  22
  23#define TCPC_OFFSET             0x1014
  24#define TCPC_MUX_CTL            GENMASK(1, 0)
  25#define MUX_NC                  0
  26#define MUX_USB                 1
  27#define MUX_DP                  2
  28#define MUX_USBDP               3
  29#define TCPC_FLIPPED            BIT(2)
  30#define TCPC_LOW_POWER_EN       BIT(3)
  31#define TCPC_VALID              BIT(4)
  32#define TCPC_CONN               \
  33        (TCPC_VALID | FIELD_PREP(TCPC_MUX_CTL, MUX_USB))
  34#define TCPC_DISCONN            \
  35        (TCPC_VALID | FIELD_PREP(TCPC_MUX_CTL, MUX_NC) | TCPC_LOW_POWER_EN)
  36
  37static const char *const PHY_RESETS[] = { "phy31", "phy", };
  38static const char *const CTL_RESETS[] = { "apb", "ctrl", };
  39
  40struct tca_apb {
  41        struct reset_control *resets[ARRAY_SIZE(PHY_RESETS)];
  42        struct regulator *vbus;
  43        struct work_struct wk;
  44        struct usb_phy phy;
  45
  46        bool regulator_enabled;
  47        bool phy_initialized;
  48        bool connected;
  49};
  50
  51static int get_flipped(struct tca_apb *ta, bool *flipped)
  52{
  53        union extcon_property_value property;
  54        int ret;
  55
  56        ret = extcon_get_property(ta->phy.edev, EXTCON_USB_HOST,
  57                                  EXTCON_PROP_USB_TYPEC_POLARITY, &property);
  58        if (ret) {
  59                dev_err(ta->phy.dev, "no polarity property from extcon\n");
  60                return ret;
  61        }
  62
  63        *flipped = property.intval;
  64
  65        return 0;
  66}
  67
  68static int phy_init(struct usb_phy *phy)
  69{
  70        struct tca_apb *ta = container_of(phy, struct tca_apb, phy);
  71        void __iomem *ctrl1 = phy->io_priv + CTRL1_OFFSET;
  72        int val, ret, i;
  73
  74        if (ta->phy_initialized)
  75                return 0;
  76
  77        for (i = 0; i < ARRAY_SIZE(PHY_RESETS); i++)
  78                reset_control_deassert(ta->resets[i]);
  79
  80        ret = readl_poll_timeout(ctrl1, val, val & SRAM_INIT_DONE, 10, 10 * 1000);
  81        if (ret) {
  82                dev_err(ta->phy.dev, "SRAM init failed, 0x%x\n", val);
  83                return ret;
  84        }
  85
  86        writel(readl(ctrl1) | SRAM_EXT_LD_DONE, ctrl1);
  87
  88        ta->phy_initialized = true;
  89        if (!ta->phy.edev) {
  90                writel(TCPC_CONN, ta->phy.io_priv + TCPC_OFFSET);
  91                return phy->set_vbus(phy, true);
  92        }
  93
  94        schedule_work(&ta->wk);
  95
  96        return ret;
  97}
  98
  99static void phy_shutdown(struct usb_phy *phy)
 100{
 101        struct tca_apb *ta = container_of(phy, struct tca_apb, phy);
 102        int i;
 103
 104        if (!ta->phy_initialized)
 105                return;
 106
 107        ta->phy_initialized = false;
 108        flush_work(&ta->wk);
 109        ta->phy.set_vbus(&ta->phy, false);
 110
 111        ta->connected = false;
 112        writel(TCPC_DISCONN, ta->phy.io_priv + TCPC_OFFSET);
 113
 114        for (i = 0; i < ARRAY_SIZE(PHY_RESETS); i++)
 115                reset_control_assert(ta->resets[i]);
 116}
 117
 118static int phy_set_vbus(struct usb_phy *phy, int on)
 119{
 120        struct tca_apb *ta = container_of(phy, struct tca_apb, phy);
 121        int ret;
 122
 123        if (!!on == ta->regulator_enabled)
 124                return 0;
 125
 126        if (on)
 127                ret = regulator_enable(ta->vbus);
 128        else
 129                ret = regulator_disable(ta->vbus);
 130
 131        if (!ret)
 132                ta->regulator_enabled = on;
 133
 134        dev_dbg(ta->phy.dev, "set vbus: %d\n", on);
 135        return ret;
 136}
 137
 138static void tca_work(struct work_struct *work)
 139{
 140        struct tca_apb *ta = container_of(work, struct tca_apb, wk);
 141        bool connected;
 142        bool flipped = false;
 143        u32 val;
 144        int ret;
 145
 146        ret = get_flipped(ta, &flipped);
 147        if (ret)
 148                return;
 149
 150        connected = extcon_get_state(ta->phy.edev, EXTCON_USB_HOST);
 151        if (connected == ta->connected)
 152                return;
 153
 154        ta->connected = connected;
 155        if (connected) {
 156                val = TCPC_CONN;
 157                if (flipped)
 158                        val |= TCPC_FLIPPED;
 159                dev_dbg(ta->phy.dev, "connected%s\n", flipped ? " flipped" : "");
 160        } else {
 161                val = TCPC_DISCONN;
 162                dev_dbg(ta->phy.dev, "disconnected\n");
 163        }
 164
 165        writel(val, ta->phy.io_priv + TCPC_OFFSET);
 166
 167        ret = ta->phy.set_vbus(&ta->phy, connected);
 168        if (ret)
 169                dev_err(ta->phy.dev, "failed to set VBUS\n");
 170}
 171
 172static int id_notifier(struct notifier_block *nb, unsigned long event, void *ptr)
 173{
 174        struct tca_apb *ta = container_of(nb, struct tca_apb, phy.id_nb);
 175
 176        if (ta->phy_initialized)
 177                schedule_work(&ta->wk);
 178
 179        return NOTIFY_DONE;
 180}
 181
 182static int vbus_notifier(struct notifier_block *nb, unsigned long evnt, void *ptr)
 183{
 184        return NOTIFY_DONE;
 185}
 186
 187static int phy_probe(struct platform_device *pdev)
 188{
 189        struct reset_control *resets[ARRAY_SIZE(CTL_RESETS)];
 190        struct device *dev = &pdev->dev;
 191        struct usb_phy *phy;
 192        struct tca_apb *ta;
 193        int i;
 194
 195        ta = devm_kzalloc(dev, sizeof(*ta), GFP_KERNEL);
 196        if (!ta)
 197                return -ENOMEM;
 198
 199        platform_set_drvdata(pdev, ta);
 200        INIT_WORK(&ta->wk, tca_work);
 201
 202        phy = &ta->phy;
 203        phy->dev = dev;
 204        phy->label = dev_name(dev);
 205        phy->type = USB_PHY_TYPE_USB3;
 206        phy->init = phy_init;
 207        phy->shutdown = phy_shutdown;
 208        phy->set_vbus = phy_set_vbus;
 209        phy->id_nb.notifier_call = id_notifier;
 210        phy->vbus_nb.notifier_call = vbus_notifier;
 211
 212        phy->io_priv = devm_platform_ioremap_resource(pdev, 0);
 213        if (IS_ERR(phy->io_priv))
 214                return PTR_ERR(phy->io_priv);
 215
 216        ta->vbus = devm_regulator_get(dev, "vbus");
 217        if (IS_ERR(ta->vbus))
 218                return PTR_ERR(ta->vbus);
 219
 220        for (i = 0; i < ARRAY_SIZE(CTL_RESETS); i++) {
 221                resets[i] = devm_reset_control_get_exclusive(dev, CTL_RESETS[i]);
 222                if (IS_ERR(resets[i])) {
 223                        dev_err(dev, "%s reset not found\n", CTL_RESETS[i]);
 224                        return PTR_ERR(resets[i]);
 225                }
 226        }
 227
 228        for (i = 0; i < ARRAY_SIZE(PHY_RESETS); i++) {
 229                ta->resets[i] = devm_reset_control_get_exclusive(dev, PHY_RESETS[i]);
 230                if (IS_ERR(ta->resets[i])) {
 231                        dev_err(dev, "%s reset not found\n", PHY_RESETS[i]);
 232                        return PTR_ERR(ta->resets[i]);
 233                }
 234        }
 235
 236        for (i = 0; i < ARRAY_SIZE(CTL_RESETS); i++)
 237                reset_control_assert(resets[i]);
 238
 239        for (i = 0; i < ARRAY_SIZE(PHY_RESETS); i++)
 240                reset_control_assert(ta->resets[i]);
 241        /*
 242         * Out-of-band reset of the controller after PHY reset will cause
 243         * controller malfunctioning, so we should use in-band controller
 244         * reset only and leave the controller de-asserted here.
 245         */
 246        for (i = 0; i < ARRAY_SIZE(CTL_RESETS); i++)
 247                reset_control_deassert(resets[i]);
 248
 249        /* Need to wait at least 20us after de-assert the controller */
 250        usleep_range(20, 100);
 251
 252        return usb_add_phy_dev(phy);
 253}
 254
 255static int phy_remove(struct platform_device *pdev)
 256{
 257        struct tca_apb *ta = platform_get_drvdata(pdev);
 258
 259        usb_remove_phy(&ta->phy);
 260
 261        return 0;
 262}
 263
 264static const struct of_device_id intel_usb_phy_dt_ids[] = {
 265        { .compatible = "intel,lgm-usb-phy" },
 266        { }
 267};
 268MODULE_DEVICE_TABLE(of, intel_usb_phy_dt_ids);
 269
 270static struct platform_driver lgm_phy_driver = {
 271        .driver = {
 272                .name = "lgm-usb-phy",
 273                .of_match_table = intel_usb_phy_dt_ids,
 274        },
 275        .probe = phy_probe,
 276        .remove = phy_remove,
 277};
 278
 279module_platform_driver(lgm_phy_driver);
 280
 281MODULE_DESCRIPTION("Intel LGM USB PHY driver");
 282MODULE_AUTHOR("Li Yin <yin1.li@intel.com>");
 283MODULE_AUTHOR("Vadivel Murugan R <vadivel.muruganx.ramuthevar@linux.intel.com>");
 284MODULE_LICENSE("GPL v2");
 285