qcom-pmic-typec.c (6537B)
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", ®); 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");