FILE
tcpci_mt6370.c
File preview with syntax highlighting.
linux-6.19.14/drivers/usb/typec/tcpm/tcpci_mt6370.c
1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * Copyright (C) 2022 Richtek Technology Corp. 4 * 5 * Author: ChiYuan Huang <cy_huang@richtek.com> 6 */ 7 8#include <linux/bits.h> 9#include <linux/interrupt.h> 10#include <linux/kernel.h> 11#include <linux/mod_devicetable.h> 12#include <linux/module.h> 13#include <linux/platform_device.h> 14#include <linux/pm_wakeirq.h> 15#include <linux/regmap.h> 16#include <linux/regulator/consumer.h> 17#include <linux/usb/tcpci.h> 18#include <linux/usb/tcpm.h> 19 20#define MT6370_REG_SYSCTRL8 0x9B 21 22#define MT6370_AUTOIDLE_MASK BIT(3) 23 24#define MT6370_VENDOR_ID 0x29CF 25#define MT6370_TCPC_DID_A 0x2170 26 27struct mt6370_priv { 28 struct device *dev; 29 struct regulator *vbus; 30 struct tcpci *tcpci; 31 struct tcpci_data tcpci_data; 32}; 33 34static const struct reg_sequence mt6370_reg_init[] = { 35 REG_SEQ(0xA0, 0x1, 1000), 36 REG_SEQ(0x81, 0x38, 0), 37 REG_SEQ(0x82, 0x82, 0), 38 REG_SEQ(0xBA, 0xFC, 0), 39 REG_SEQ(0xBB, 0x50, 0), 40 REG_SEQ(0x9E, 0x8F, 0), 41 REG_SEQ(0xA1, 0x5, 0), 42 REG_SEQ(0xA2, 0x4, 0), 43 REG_SEQ(0xA3, 0x4A, 0), 44 REG_SEQ(0xA4, 0x01, 0), 45 REG_SEQ(0x95, 0x01, 0), 46 REG_SEQ(0x80, 0x71, 0), 47 REG_SEQ(0x9B, 0x3A, 1000), 48}; 49 50static int mt6370_tcpc_init(struct tcpci *tcpci, struct tcpci_data *data) 51{ 52 u16 did; 53 int ret; 54 55 ret = regmap_register_patch(data->regmap, mt6370_reg_init, 56 ARRAY_SIZE(mt6370_reg_init)); 57 if (ret) 58 return ret; 59 60 ret = regmap_raw_read(data->regmap, TCPC_BCD_DEV, &did, sizeof(u16)); 61 if (ret) 62 return ret; 63 64 if (did == MT6370_TCPC_DID_A) 65 return regmap_write(data->regmap, TCPC_FAULT_CTRL, 0x80); 66 67 return 0; 68} 69 70static int mt6370_tcpc_set_vconn(struct tcpci *tcpci, struct tcpci_data *data, 71 bool enable) 72{ 73 return regmap_update_bits(data->regmap, MT6370_REG_SYSCTRL8, 74 MT6370_AUTOIDLE_MASK, 75 enable ? 0 : MT6370_AUTOIDLE_MASK); 76} 77 78static int mt6370_tcpc_set_vbus(struct tcpci *tcpci, struct tcpci_data *data, 79 bool source, bool sink) 80{ 81 struct mt6370_priv *priv = container_of(data, struct mt6370_priv, 82 tcpci_data); 83 int ret; 84 85 ret = regulator_is_enabled(priv->vbus); 86 if (ret < 0) 87 return ret; 88 89 if (ret && !source) 90 return regulator_disable(priv->vbus); 91 92 if (!ret && source) 93 return regulator_enable(priv->vbus); 94 95 return 0; 96} 97 98static irqreturn_t mt6370_irq_handler(int irq, void *dev_id) 99{ 100 struct mt6370_priv *priv = dev_id; 101 102 return tcpci_irq(priv->tcpci); 103} 104 105static int mt6370_check_vendor_info(struct mt6370_priv *priv) 106{ 107 struct regmap *regmap = priv->tcpci_data.regmap; 108 u16 vid; 109 int ret; 110 111 ret = regmap_raw_read(regmap, TCPC_VENDOR_ID, &vid, sizeof(u16)); 112 if (ret) 113 return ret; 114 115 if (vid != MT6370_VENDOR_ID) 116 return dev_err_probe(priv->dev, -ENODEV, 117 "Vendor ID not correct 0x%02x\n", vid); 118 119 return 0; 120} 121 122static void mt6370_unregister_tcpci_port(void *tcpci) 123{ 124 tcpci_unregister_port(tcpci); 125} 126 127static int mt6370_tcpc_probe(struct platform_device *pdev) 128{ 129 struct mt6370_priv *priv; 130 struct device *dev = &pdev->dev; 131 int irq, ret; 132 133 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 134 if (!priv) 135 return -ENOMEM; 136 137 priv->dev = dev; 138 139 priv->tcpci_data.regmap = dev_get_regmap(dev->parent, NULL); 140 if (!priv->tcpci_data.regmap) 141 return dev_err_probe(dev, -ENODEV, "Failed to init regmap\n"); 142 143 ret = mt6370_check_vendor_info(priv); 144 if (ret) 145 return ret; 146 147 irq = platform_get_irq(pdev, 0); 148 if (irq < 0) 149 return irq; 150 151 /* Assign TCPCI feature and ops */ 152 priv->tcpci_data.auto_discharge_disconnect = 1; 153 priv->tcpci_data.init = mt6370_tcpc_init; 154 priv->tcpci_data.set_vconn = mt6370_tcpc_set_vconn; 155 156 priv->vbus = devm_regulator_get_optional(dev, "vbus"); 157 if (!IS_ERR(priv->vbus)) 158 priv->tcpci_data.set_vbus = mt6370_tcpc_set_vbus; 159 160 priv->tcpci = tcpci_register_port(dev, &priv->tcpci_data); 161 if (IS_ERR(priv->tcpci)) 162 return dev_err_probe(dev, PTR_ERR(priv->tcpci), 163 "Failed to register tcpci port\n"); 164 165 ret = devm_add_action_or_reset(dev, mt6370_unregister_tcpci_port, priv->tcpci); 166 if (ret) 167 return ret; 168 169 ret = devm_request_threaded_irq(dev, irq, NULL, mt6370_irq_handler, 170 IRQF_ONESHOT, dev_name(dev), priv); 171 if (ret) 172 return dev_err_probe(dev, ret, "Failed to allocate irq\n"); 173 174 device_init_wakeup(dev, true); 175 dev_pm_set_wake_irq(dev, irq); 176 177 return 0; 178} 179 180static void mt6370_tcpc_remove(struct platform_device *pdev) 181{ 182 dev_pm_clear_wake_irq(&pdev->dev); 183 device_init_wakeup(&pdev->dev, false); 184} 185 186static const struct of_device_id mt6370_tcpc_devid_table[] = { 187 { .compatible = "mediatek,mt6370-tcpc" }, 188 {} 189}; 190MODULE_DEVICE_TABLE(of, mt6370_tcpc_devid_table); 191 192static struct platform_driver mt6370_tcpc_driver = { 193 .driver = { 194 .name = "mt6370-tcpc", 195 .of_match_table = mt6370_tcpc_devid_table, 196 }, 197 .probe = mt6370_tcpc_probe, 198 .remove = mt6370_tcpc_remove, 199}; 200module_platform_driver(mt6370_tcpc_driver); 201 202MODULE_AUTHOR("ChiYuan Huang <cy_huang@richtek.com>"); 203MODULE_DESCRIPTION("MT6370 USB Type-C Port Controller Interface Driver"); 204MODULE_LICENSE("GPL v2");
~/
Read-only file preview.