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