Linux Audio

Check our new training course

Real-Time Linux with PREEMPT_RT training

Feb 18-20, 2025
Register
Loading...
Note: File does not exist in v5.9.
  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");