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