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