1// SPDX-License-Identifier: GPL-2.0-only
2/*
3 * Copyright (c) 2015-2021, The Linux Foundation. All rights reserved.
4 */
5
6#include <linux/bitops.h>
7#include <linux/err.h>
8#include <linux/interrupt.h>
9#include <linux/io.h>
10#include <linux/iopoll.h>
11#include <linux/kernel.h>
12#include <linux/module.h>
13#include <linux/of.h>
14#include <linux/platform_device.h>
15#include <linux/slab.h>
16#include <linux/sysfs.h>
17#include <linux/usb/role.h>
18
19#define EUD_REG_INT1_EN_MASK	0x0024
20#define EUD_REG_INT_STATUS_1	0x0044
21#define EUD_REG_CTL_OUT_1	0x0074
22#define EUD_REG_VBUS_INT_CLR	0x0080
23#define EUD_REG_CSR_EUD_EN	0x1014
24#define EUD_REG_SW_ATTACH_DET	0x1018
25#define EUD_REG_EUD_EN2		0x0000
26
27#define EUD_ENABLE		BIT(0)
28#define EUD_INT_PET_EUD		BIT(0)
29#define EUD_INT_VBUS		BIT(2)
30#define EUD_INT_SAFE_MODE	BIT(4)
31#define EUD_INT_ALL		(EUD_INT_VBUS | EUD_INT_SAFE_MODE)
32
33struct eud_chip {
34	struct device			*dev;
35	struct usb_role_switch		*role_sw;
36	void __iomem			*base;
37	void __iomem			*mode_mgr;
38	unsigned int			int_status;
39	int				irq;
40	bool				enabled;
41	bool				usb_attached;
42};
43
44static int enable_eud(struct eud_chip *priv)
45{
46	writel(EUD_ENABLE, priv->base + EUD_REG_CSR_EUD_EN);
47	writel(EUD_INT_VBUS | EUD_INT_SAFE_MODE,
48			priv->base + EUD_REG_INT1_EN_MASK);
49	writel(1, priv->mode_mgr + EUD_REG_EUD_EN2);
50
51	return usb_role_switch_set_role(priv->role_sw, USB_ROLE_DEVICE);
52}
53
54static void disable_eud(struct eud_chip *priv)
55{
56	writel(0, priv->base + EUD_REG_CSR_EUD_EN);
57	writel(0, priv->mode_mgr + EUD_REG_EUD_EN2);
58}
59
60static ssize_t enable_show(struct device *dev,
61		struct device_attribute *attr, char *buf)
62{
63	struct eud_chip *chip = dev_get_drvdata(dev);
64
65	return sysfs_emit(buf, "%d\n", chip->enabled);
66}
67
68static ssize_t enable_store(struct device *dev,
69		struct device_attribute *attr,
70		const char *buf, size_t count)
71{
72	struct eud_chip *chip = dev_get_drvdata(dev);
73	bool enable;
74	int ret;
75
76	if (kstrtobool(buf, &enable))
77		return -EINVAL;
78
79	if (enable) {
80		ret = enable_eud(chip);
81		if (!ret)
82			chip->enabled = enable;
83		else
84			disable_eud(chip);
85	} else {
86		disable_eud(chip);
87	}
88
89	return count;
90}
91
92static DEVICE_ATTR_RW(enable);
93
94static struct attribute *eud_attrs[] = {
95	&dev_attr_enable.attr,
96	NULL,
97};
98ATTRIBUTE_GROUPS(eud);
99
100static void usb_attach_detach(struct eud_chip *chip)
101{
102	u32 reg;
103
104	/* read ctl_out_1[4] to find USB attach or detach event */
105	reg = readl(chip->base + EUD_REG_CTL_OUT_1);
106	chip->usb_attached = reg & EUD_INT_SAFE_MODE;
107}
108
109static void pet_eud(struct eud_chip *chip)
110{
111	u32 reg;
112	int ret;
113
114	/* When the EUD_INT_PET_EUD in SW_ATTACH_DET is set, the cable has been
115	 * disconnected and we need to detach the pet to check if EUD is in safe
116	 * mode before attaching again.
117	 */
118	reg = readl(chip->base + EUD_REG_SW_ATTACH_DET);
119	if (reg & EUD_INT_PET_EUD) {
120		/* Detach & Attach pet for EUD */
121		writel(0, chip->base + EUD_REG_SW_ATTACH_DET);
122		/* Delay to make sure detach pet is done before attach pet */
123		ret = readl_poll_timeout(chip->base + EUD_REG_SW_ATTACH_DET,
124					reg, (reg == 0), 1, 100);
125		if (ret) {
126			dev_err(chip->dev, "Detach pet failed\n");
127			return;
128		}
129	}
130	/* Attach pet for EUD */
131	writel(EUD_INT_PET_EUD, chip->base + EUD_REG_SW_ATTACH_DET);
132}
133
134static irqreturn_t handle_eud_irq(int irq, void *data)
135{
136	struct eud_chip *chip = data;
137	u32 reg;
138
139	reg = readl(chip->base + EUD_REG_INT_STATUS_1);
140	switch (reg & EUD_INT_ALL) {
141	case EUD_INT_VBUS:
142		usb_attach_detach(chip);
143		return IRQ_WAKE_THREAD;
144	case EUD_INT_SAFE_MODE:
145		pet_eud(chip);
146		return IRQ_HANDLED;
147	default:
148		return IRQ_NONE;
149	}
150}
151
152static irqreturn_t handle_eud_irq_thread(int irq, void *data)
153{
154	struct eud_chip *chip = data;
155	int ret;
156
157	if (chip->usb_attached)
158		ret = usb_role_switch_set_role(chip->role_sw, USB_ROLE_DEVICE);
159	else
160		ret = usb_role_switch_set_role(chip->role_sw, USB_ROLE_HOST);
161	if (ret)
162		dev_err(chip->dev, "failed to set role switch\n");
163
164	/* set and clear vbus_int_clr[0] to clear interrupt */
165	writel(BIT(0), chip->base + EUD_REG_VBUS_INT_CLR);
166	writel(0, chip->base + EUD_REG_VBUS_INT_CLR);
167
168	return IRQ_HANDLED;
169}
170
171static void eud_role_switch_release(void *data)
172{
173	struct eud_chip *chip = data;
174
175	usb_role_switch_put(chip->role_sw);
176}
177
178static int eud_probe(struct platform_device *pdev)
179{
180	struct eud_chip *chip;
181	int ret;
182
183	chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
184	if (!chip)
185		return -ENOMEM;
186
187	chip->dev = &pdev->dev;
188
189	chip->role_sw = usb_role_switch_get(&pdev->dev);
190	if (IS_ERR(chip->role_sw))
191		return dev_err_probe(chip->dev, PTR_ERR(chip->role_sw),
192					"failed to get role switch\n");
193
194	ret = devm_add_action_or_reset(chip->dev, eud_role_switch_release, chip);
195	if (ret)
196		return dev_err_probe(chip->dev, ret,
197				"failed to add role switch release action\n");
198
199	chip->base = devm_platform_ioremap_resource(pdev, 0);
200	if (IS_ERR(chip->base))
201		return PTR_ERR(chip->base);
202
203	chip->mode_mgr = devm_platform_ioremap_resource(pdev, 1);
204	if (IS_ERR(chip->mode_mgr))
205		return PTR_ERR(chip->mode_mgr);
206
207	chip->irq = platform_get_irq(pdev, 0);
208	if (chip->irq < 0)
209		return chip->irq;
210
211	ret = devm_request_threaded_irq(&pdev->dev, chip->irq, handle_eud_irq,
212			handle_eud_irq_thread, IRQF_ONESHOT, NULL, chip);
213	if (ret)
214		return dev_err_probe(chip->dev, ret, "failed to allocate irq\n");
215
216	enable_irq_wake(chip->irq);
217
218	platform_set_drvdata(pdev, chip);
219
220	return 0;
221}
222
223static void eud_remove(struct platform_device *pdev)
224{
225	struct eud_chip *chip = platform_get_drvdata(pdev);
226
227	if (chip->enabled)
228		disable_eud(chip);
229
230	device_init_wakeup(&pdev->dev, false);
231	disable_irq_wake(chip->irq);
232}
233
234static const struct of_device_id eud_dt_match[] = {
235	{ .compatible = "qcom,sc7280-eud" },
236	{ }
237};
238MODULE_DEVICE_TABLE(of, eud_dt_match);
239
240static struct platform_driver eud_driver = {
241	.probe	= eud_probe,
242	.remove_new = eud_remove,
243	.driver	= {
244		.name = "qcom_eud",
245		.dev_groups = eud_groups,
246		.of_match_table = eud_dt_match,
247	},
248};
249module_platform_driver(eud_driver);
250
251MODULE_DESCRIPTION("QTI EUD driver");
252MODULE_LICENSE("GPL v2");
253