linux/drivers/usb/typec/qcom-pmic-typec.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Copyright (c) 2020, The Linux Foundation. All rights reserved.
   4 */
   5
   6#include <linux/err.h>
   7#include <linux/interrupt.h>
   8#include <linux/kernel.h>
   9#include <linux/mod_devicetable.h>
  10#include <linux/module.h>
  11#include <linux/platform_device.h>
  12#include <linux/regmap.h>
  13#include <linux/regulator/consumer.h>
  14#include <linux/slab.h>
  15#include <linux/usb/role.h>
  16#include <linux/usb/typec_mux.h>
  17
  18#define TYPEC_MISC_STATUS               0xb
  19#define CC_ATTACHED                     BIT(0)
  20#define CC_ORIENTATION                  BIT(1)
  21#define SNK_SRC_MODE                    BIT(6)
  22#define TYPEC_MODE_CFG                  0x44
  23#define TYPEC_DISABLE_CMD               BIT(0)
  24#define EN_SNK_ONLY                     BIT(1)
  25#define EN_SRC_ONLY                     BIT(2)
  26#define TYPEC_VCONN_CONTROL             0x46
  27#define VCONN_EN_SRC                    BIT(0)
  28#define VCONN_EN_VAL                    BIT(1)
  29#define TYPEC_EXIT_STATE_CFG            0x50
  30#define SEL_SRC_UPPER_REF               BIT(2)
  31#define TYPEC_INTR_EN_CFG_1             0x5e
  32#define TYPEC_INTR_EN_CFG_1_MASK        GENMASK(7, 0)
  33
  34struct qcom_pmic_typec {
  35        struct device           *dev;
  36        struct regmap           *regmap;
  37        u32                     base;
  38
  39        struct typec_port       *port;
  40        struct usb_role_switch *role_sw;
  41
  42        struct regulator        *vbus_reg;
  43        bool                    vbus_enabled;
  44};
  45
  46static void qcom_pmic_typec_enable_vbus_regulator(struct qcom_pmic_typec
  47                                                        *qcom_usb, bool enable)
  48{
  49        int ret;
  50
  51        if (enable == qcom_usb->vbus_enabled)
  52                return;
  53
  54        if (enable) {
  55                ret = regulator_enable(qcom_usb->vbus_reg);
  56                if (ret)
  57                        return;
  58        } else {
  59                ret = regulator_disable(qcom_usb->vbus_reg);
  60                if (ret)
  61                        return;
  62        }
  63        qcom_usb->vbus_enabled = enable;
  64}
  65
  66static void qcom_pmic_typec_check_connection(struct qcom_pmic_typec *qcom_usb)
  67{
  68        enum typec_orientation orientation;
  69        enum usb_role role;
  70        unsigned int stat;
  71        bool enable_vbus;
  72
  73        regmap_read(qcom_usb->regmap, qcom_usb->base + TYPEC_MISC_STATUS,
  74                    &stat);
  75
  76        if (stat & CC_ATTACHED) {
  77                orientation = (stat & CC_ORIENTATION) ?
  78                                TYPEC_ORIENTATION_REVERSE :
  79                                TYPEC_ORIENTATION_NORMAL;
  80                typec_set_orientation(qcom_usb->port, orientation);
  81
  82                role = (stat & SNK_SRC_MODE) ? USB_ROLE_HOST : USB_ROLE_DEVICE;
  83                if (role == USB_ROLE_HOST)
  84                        enable_vbus = true;
  85                else
  86                        enable_vbus = false;
  87        } else {
  88                role = USB_ROLE_NONE;
  89                enable_vbus = false;
  90        }
  91
  92        qcom_pmic_typec_enable_vbus_regulator(qcom_usb, enable_vbus);
  93        usb_role_switch_set_role(qcom_usb->role_sw, role);
  94}
  95
  96static irqreturn_t qcom_pmic_typec_interrupt(int irq, void *_qcom_usb)
  97{
  98        struct qcom_pmic_typec *qcom_usb = _qcom_usb;
  99
 100        qcom_pmic_typec_check_connection(qcom_usb);
 101        return IRQ_HANDLED;
 102}
 103
 104static void qcom_pmic_typec_typec_hw_init(struct qcom_pmic_typec *qcom_usb,
 105                                          enum typec_port_type type)
 106{
 107        u8 mode = 0;
 108
 109        regmap_update_bits(qcom_usb->regmap,
 110                           qcom_usb->base + TYPEC_INTR_EN_CFG_1,
 111                           TYPEC_INTR_EN_CFG_1_MASK, 0);
 112
 113        if (type == TYPEC_PORT_SRC)
 114                mode = EN_SRC_ONLY;
 115        else if (type == TYPEC_PORT_SNK)
 116                mode = EN_SNK_ONLY;
 117
 118        regmap_update_bits(qcom_usb->regmap, qcom_usb->base + TYPEC_MODE_CFG,
 119                           EN_SNK_ONLY | EN_SRC_ONLY, mode);
 120
 121        regmap_update_bits(qcom_usb->regmap,
 122                           qcom_usb->base + TYPEC_VCONN_CONTROL,
 123                           VCONN_EN_SRC | VCONN_EN_VAL, VCONN_EN_SRC);
 124        regmap_update_bits(qcom_usb->regmap,
 125                           qcom_usb->base + TYPEC_EXIT_STATE_CFG,
 126                           SEL_SRC_UPPER_REF, SEL_SRC_UPPER_REF);
 127}
 128
 129static int qcom_pmic_typec_probe(struct platform_device *pdev)
 130{
 131        struct qcom_pmic_typec *qcom_usb;
 132        struct device *dev = &pdev->dev;
 133        struct fwnode_handle *fwnode;
 134        struct typec_capability cap;
 135        const char *buf;
 136        int ret, irq, role;
 137        u32 reg;
 138
 139        ret = device_property_read_u32(dev, "reg", &reg);
 140        if (ret < 0) {
 141                dev_err(dev, "missing base address\n");
 142                return ret;
 143        }
 144
 145        qcom_usb = devm_kzalloc(dev, sizeof(*qcom_usb), GFP_KERNEL);
 146        if (!qcom_usb)
 147                return -ENOMEM;
 148
 149        qcom_usb->dev = dev;
 150        qcom_usb->base = reg;
 151
 152        qcom_usb->regmap = dev_get_regmap(dev->parent, NULL);
 153        if (!qcom_usb->regmap) {
 154                dev_err(dev, "Failed to get regmap\n");
 155                return -EINVAL;
 156        }
 157
 158        qcom_usb->vbus_reg = devm_regulator_get(qcom_usb->dev, "usb_vbus");
 159        if (IS_ERR(qcom_usb->vbus_reg))
 160                return PTR_ERR(qcom_usb->vbus_reg);
 161
 162        fwnode = device_get_named_child_node(dev, "connector");
 163        if (!fwnode)
 164                return -EINVAL;
 165
 166        ret = fwnode_property_read_string(fwnode, "power-role", &buf);
 167        if (!ret) {
 168                role = typec_find_port_power_role(buf);
 169                if (role < 0)
 170                        role = TYPEC_PORT_SNK;
 171        } else {
 172                role = TYPEC_PORT_SNK;
 173        }
 174        cap.type = role;
 175
 176        ret = fwnode_property_read_string(fwnode, "data-role", &buf);
 177        if (!ret) {
 178                role = typec_find_port_data_role(buf);
 179                if (role < 0)
 180                        role = TYPEC_PORT_UFP;
 181        } else {
 182                role = TYPEC_PORT_UFP;
 183        }
 184        cap.data = role;
 185
 186        cap.prefer_role = TYPEC_NO_PREFERRED_ROLE;
 187        cap.fwnode = fwnode;
 188        qcom_usb->port = typec_register_port(dev, &cap);
 189        if (IS_ERR(qcom_usb->port)) {
 190                ret = PTR_ERR(qcom_usb->port);
 191                dev_err(dev, "Failed to register type c port %d\n", ret);
 192                goto err_put_node;
 193        }
 194        fwnode_handle_put(fwnode);
 195
 196        qcom_usb->role_sw = fwnode_usb_role_switch_get(dev_fwnode(qcom_usb->dev));
 197        if (IS_ERR(qcom_usb->role_sw)) {
 198                if (PTR_ERR(qcom_usb->role_sw) != -EPROBE_DEFER)
 199                        dev_err(dev, "failed to get role switch\n");
 200                ret = PTR_ERR(qcom_usb->role_sw);
 201                goto err_typec_port;
 202        }
 203
 204        irq = platform_get_irq(pdev, 0);
 205        if (irq < 0)
 206                goto err_usb_role_sw;
 207
 208        ret = devm_request_threaded_irq(qcom_usb->dev, irq, NULL,
 209                                        qcom_pmic_typec_interrupt, IRQF_ONESHOT,
 210                                        "qcom-pmic-typec", qcom_usb);
 211        if (ret) {
 212                dev_err(&pdev->dev, "Could not request IRQ\n");
 213                goto err_usb_role_sw;
 214        }
 215
 216        platform_set_drvdata(pdev, qcom_usb);
 217        qcom_pmic_typec_typec_hw_init(qcom_usb, cap.type);
 218        qcom_pmic_typec_check_connection(qcom_usb);
 219
 220        return 0;
 221
 222err_usb_role_sw:
 223        usb_role_switch_put(qcom_usb->role_sw);
 224err_typec_port:
 225        typec_unregister_port(qcom_usb->port);
 226err_put_node:
 227        fwnode_handle_put(fwnode);
 228
 229        return ret;
 230}
 231
 232static int qcom_pmic_typec_remove(struct platform_device *pdev)
 233{
 234        struct qcom_pmic_typec *qcom_usb = platform_get_drvdata(pdev);
 235
 236        usb_role_switch_set_role(qcom_usb->role_sw, USB_ROLE_NONE);
 237        qcom_pmic_typec_enable_vbus_regulator(qcom_usb, 0);
 238
 239        typec_unregister_port(qcom_usb->port);
 240        usb_role_switch_put(qcom_usb->role_sw);
 241
 242        return 0;
 243}
 244
 245static const struct of_device_id qcom_pmic_typec_table[] = {
 246        { .compatible = "qcom,pm8150b-usb-typec" },
 247        { }
 248};
 249MODULE_DEVICE_TABLE(of, qcom_pmic_typec_table);
 250
 251static struct platform_driver qcom_pmic_typec = {
 252        .driver = {
 253                .name = "qcom,pmic-typec",
 254                .of_match_table = qcom_pmic_typec_table,
 255        },
 256        .probe = qcom_pmic_typec_probe,
 257        .remove = qcom_pmic_typec_remove,
 258};
 259module_platform_driver(qcom_pmic_typec);
 260
 261MODULE_DESCRIPTION("QCOM PMIC USB type C driver");
 262MODULE_LICENSE("GPL v2");
 263