1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (C) 2020 MediaTek Inc. 4 * 5 * Author: ChiYuan Huang <cy_huang@richtek.com> 6 */ 7 8 #include <linux/interrupt.h> 9 #include <linux/kernel.h> 10 #include <linux/module.h> 11 #include <linux/of.h> 12 #include <linux/platform_device.h> 13 #include <linux/regmap.h> 14 #include <linux/usb/tcpm.h> 15 16 #include "tcpci.h" 17 18 #define MT6360_REG_PHYCTRL1 0x80 19 #define MT6360_REG_PHYCTRL3 0x82 20 #define MT6360_REG_PHYCTRL7 0x86 21 #define MT6360_REG_VCONNCTRL1 0x8C 22 #define MT6360_REG_MODECTRL2 0x8F 23 #define MT6360_REG_SWRESET 0xA0 24 #define MT6360_REG_DEBCTRL1 0xA1 25 #define MT6360_REG_DRPCTRL1 0xA2 26 #define MT6360_REG_DRPCTRL2 0xA3 27 #define MT6360_REG_I2CTORST 0xBF 28 #define MT6360_REG_PHYCTRL11 0xCA 29 #define MT6360_REG_RXCTRL1 0xCE 30 #define MT6360_REG_RXCTRL2 0xCF 31 #define MT6360_REG_CTDCTRL2 0xEC 32 33 /* MT6360_REG_VCONNCTRL1 */ 34 #define MT6360_VCONNCL_ENABLE BIT(0) 35 /* MT6360_REG_RXCTRL2 */ 36 #define MT6360_OPEN40M_ENABLE BIT(7) 37 /* MT6360_REG_CTDCTRL2 */ 38 #define MT6360_RPONESHOT_ENABLE BIT(6) 39 40 struct mt6360_tcpc_info { 41 struct tcpci_data tdata; 42 struct tcpci *tcpci; 43 struct device *dev; 44 int irq; 45 }; 46 47 static inline int mt6360_tcpc_read16(struct regmap *regmap, 48 unsigned int reg, u16 *val) 49 { 50 return regmap_raw_read(regmap, reg, val, sizeof(u16)); 51 } 52 53 static inline int mt6360_tcpc_write16(struct regmap *regmap, 54 unsigned int reg, u16 val) 55 { 56 return regmap_raw_write(regmap, reg, &val, sizeof(u16)); 57 } 58 59 static int mt6360_tcpc_init(struct tcpci *tcpci, struct tcpci_data *tdata) 60 { 61 struct regmap *regmap = tdata->regmap; 62 int ret; 63 64 ret = regmap_write(regmap, MT6360_REG_SWRESET, 0x01); 65 if (ret) 66 return ret; 67 68 /* after reset command, wait 1~2ms to wait IC action */ 69 usleep_range(1000, 2000); 70 71 /* write all alert to masked */ 72 ret = mt6360_tcpc_write16(regmap, TCPC_ALERT_MASK, 0); 73 if (ret) 74 return ret; 75 76 /* config I2C timeout reset enable , and timeout to 200ms */ 77 ret = regmap_write(regmap, MT6360_REG_I2CTORST, 0x8F); 78 if (ret) 79 return ret; 80 81 /* config CC Detect Debounce : 26.7*val us */ 82 ret = regmap_write(regmap, MT6360_REG_DEBCTRL1, 0x10); 83 if (ret) 84 return ret; 85 86 /* DRP Toggle Cycle : 51.2 + 6.4*val ms */ 87 ret = regmap_write(regmap, MT6360_REG_DRPCTRL1, 4); 88 if (ret) 89 return ret; 90 91 /* DRP Duyt Ctrl : dcSRC: /1024 */ 92 ret = mt6360_tcpc_write16(regmap, MT6360_REG_DRPCTRL2, 330); 93 if (ret) 94 return ret; 95 96 /* Enable VCONN Current Limit function */ 97 ret = regmap_update_bits(regmap, MT6360_REG_VCONNCTRL1, MT6360_VCONNCL_ENABLE, 98 MT6360_VCONNCL_ENABLE); 99 if (ret) 100 return ret; 101 102 /* Enable cc open 40ms when pmic send vsysuv signal */ 103 ret = regmap_update_bits(regmap, MT6360_REG_RXCTRL2, MT6360_OPEN40M_ENABLE, 104 MT6360_OPEN40M_ENABLE); 105 if (ret) 106 return ret; 107 108 /* Enable Rpdet oneshot detection */ 109 ret = regmap_update_bits(regmap, MT6360_REG_CTDCTRL2, MT6360_RPONESHOT_ENABLE, 110 MT6360_RPONESHOT_ENABLE); 111 if (ret) 112 return ret; 113 114 /* BMC PHY */ 115 ret = mt6360_tcpc_write16(regmap, MT6360_REG_PHYCTRL1, 0x3A70); 116 if (ret) 117 return ret; 118 119 ret = regmap_write(regmap, MT6360_REG_PHYCTRL3, 0x82); 120 if (ret) 121 return ret; 122 123 ret = regmap_write(regmap, MT6360_REG_PHYCTRL7, 0x36); 124 if (ret) 125 return ret; 126 127 ret = mt6360_tcpc_write16(regmap, MT6360_REG_PHYCTRL11, 0x3C60); 128 if (ret) 129 return ret; 130 131 ret = regmap_write(regmap, MT6360_REG_RXCTRL1, 0xE8); 132 if (ret) 133 return ret; 134 135 /* Set shipping mode off, AUTOIDLE on */ 136 return regmap_write(regmap, MT6360_REG_MODECTRL2, 0x7A); 137 } 138 139 static irqreturn_t mt6360_irq(int irq, void *dev_id) 140 { 141 struct mt6360_tcpc_info *mti = dev_id; 142 143 return tcpci_irq(mti->tcpci); 144 } 145 146 static int mt6360_tcpc_probe(struct platform_device *pdev) 147 { 148 struct mt6360_tcpc_info *mti; 149 int ret; 150 151 mti = devm_kzalloc(&pdev->dev, sizeof(*mti), GFP_KERNEL); 152 if (!mti) 153 return -ENOMEM; 154 155 mti->dev = &pdev->dev; 156 157 mti->tdata.regmap = dev_get_regmap(pdev->dev.parent, NULL); 158 if (!mti->tdata.regmap) { 159 dev_err(&pdev->dev, "Failed to get parent regmap\n"); 160 return -ENODEV; 161 } 162 163 mti->irq = platform_get_irq_byname(pdev, "PD_IRQB"); 164 if (mti->irq < 0) 165 return mti->irq; 166 167 mti->tdata.init = mt6360_tcpc_init; 168 mti->tcpci = tcpci_register_port(&pdev->dev, &mti->tdata); 169 if (IS_ERR(mti->tcpci)) { 170 dev_err(&pdev->dev, "Failed to register tcpci port\n"); 171 return PTR_ERR(mti->tcpci); 172 } 173 174 ret = devm_request_threaded_irq(mti->dev, mti->irq, NULL, mt6360_irq, IRQF_ONESHOT, 175 dev_name(&pdev->dev), mti); 176 if (ret) { 177 dev_err(mti->dev, "Failed to register irq\n"); 178 tcpci_unregister_port(mti->tcpci); 179 return ret; 180 } 181 182 device_init_wakeup(&pdev->dev, true); 183 platform_set_drvdata(pdev, mti); 184 185 return 0; 186 } 187 188 static int mt6360_tcpc_remove(struct platform_device *pdev) 189 { 190 struct mt6360_tcpc_info *mti = platform_get_drvdata(pdev); 191 192 disable_irq(mti->irq); 193 tcpci_unregister_port(mti->tcpci); 194 return 0; 195 } 196 197 static int __maybe_unused mt6360_tcpc_suspend(struct device *dev) 198 { 199 struct mt6360_tcpc_info *mti = dev_get_drvdata(dev); 200 201 if (device_may_wakeup(dev)) 202 enable_irq_wake(mti->irq); 203 204 return 0; 205 } 206 207 static int __maybe_unused mt6360_tcpc_resume(struct device *dev) 208 { 209 struct mt6360_tcpc_info *mti = dev_get_drvdata(dev); 210 211 if (device_may_wakeup(dev)) 212 disable_irq_wake(mti->irq); 213 214 return 0; 215 } 216 217 static SIMPLE_DEV_PM_OPS(mt6360_tcpc_pm_ops, mt6360_tcpc_suspend, mt6360_tcpc_resume); 218 219 static const struct of_device_id __maybe_unused mt6360_tcpc_of_id[] = { 220 { .compatible = "mediatek,mt6360-tcpc", }, 221 {}, 222 }; 223 MODULE_DEVICE_TABLE(of, mt6360_tcpc_of_id); 224 225 static struct platform_driver mt6360_tcpc_driver = { 226 .driver = { 227 .name = "mt6360-tcpc", 228 .pm = &mt6360_tcpc_pm_ops, 229 .of_match_table = mt6360_tcpc_of_id, 230 }, 231 .probe = mt6360_tcpc_probe, 232 .remove = mt6360_tcpc_remove, 233 }; 234 module_platform_driver(mt6360_tcpc_driver); 235 236 MODULE_AUTHOR("ChiYuan Huang <cy_huang@richtek.com>"); 237 MODULE_DESCRIPTION("MT6360 USB Type-C Port Controller Interface Driver"); 238 MODULE_LICENSE("GPL v2"); 239