GNU Linux-libre 5.19-rc6-gnu
[releases.git] / drivers / usb / typec / tcpm / tcpci_mt6360.c
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");