summaryrefslogtreecommitdiffstats
path: root/drivers/usb/chipidea/ulpi.c
diff options
context:
space:
mode:
authorBreno Leitao <leitao@debian.org>2026-08-05 07:52:36 -0700
committerTejun Heo <tj@kernel.org>2026-08-10 11:47:20 -1000
commita5bde5d8fde8a8cb28e59a672d5ddc5b9c1e7656 (patch)
tree39f5b258386e1127d253d293194e9a2991bf3c34 /drivers/usb/chipidea/ulpi.c
downloadlinux-stable-a5bde5d8fde8a8cb28e59a672d5ddc5b9c1e7656.tar.gz
linux-stable-a5bde5d8fde8a8cb28e59a672d5ddc5b9c1e7656.zip
workqueue: skip the node_nr_active update for non-unbound workqueuesgrafted
apply_wqattrs_commit() updates node_nr_active->max unconditionally. wq->node_nr_active[] is only allocated for unbound workqueues, so guard the call before per-cpu workqueues start using this path. No functional change: only unbound workqueues reach apply_wqattrs_*() today. Signed-off-by: Breno Leitao <leitao@debian.org> Signed-off-by: Tejun Heo <tj@kernel.org>
Diffstat (limited to 'drivers/usb/chipidea/ulpi.c')
-rw-r--r--drivers/usb/chipidea/ulpi.c108
1 files changed, 108 insertions, 0 deletions
diff --git a/drivers/usb/chipidea/ulpi.c b/drivers/usb/chipidea/ulpi.c
new file mode 100644
index 000000000..dfec07e8a
--- /dev/null
+++ b/drivers/usb/chipidea/ulpi.c
@@ -0,0 +1,108 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2016 Linaro Ltd.
+ */
+
+#include <linux/device.h>
+#include <linux/usb/chipidea.h>
+#include <linux/ulpi/interface.h>
+
+#include "ci.h"
+
+#define ULPI_WAKEUP BIT(31)
+#define ULPI_RUN BIT(30)
+#define ULPI_WRITE BIT(29)
+#define ULPI_SYNC_STATE BIT(27)
+#define ULPI_ADDR(n) ((n) << 16)
+#define ULPI_DATA(n) (n)
+
+static int ci_ulpi_wait(struct ci_hdrc *ci, u32 mask)
+{
+ unsigned long usec = 10000;
+
+ while (usec--) {
+ if (!hw_read(ci, OP_ULPI_VIEWPORT, mask))
+ return 0;
+
+ udelay(1);
+ }
+
+ return -ETIMEDOUT;
+}
+
+static int ci_ulpi_read(struct device *dev, u8 addr)
+{
+ struct ci_hdrc *ci = dev_get_drvdata(dev);
+ int ret;
+
+ hw_write(ci, OP_ULPI_VIEWPORT, 0xffffffff, ULPI_WRITE | ULPI_WAKEUP);
+ ret = ci_ulpi_wait(ci, ULPI_WAKEUP);
+ if (ret)
+ return ret;
+
+ hw_write(ci, OP_ULPI_VIEWPORT, 0xffffffff, ULPI_RUN | ULPI_ADDR(addr));
+ ret = ci_ulpi_wait(ci, ULPI_RUN);
+ if (ret)
+ return ret;
+
+ return hw_read(ci, OP_ULPI_VIEWPORT, GENMASK(15, 8)) >> 8;
+}
+
+static int ci_ulpi_write(struct device *dev, u8 addr, u8 val)
+{
+ struct ci_hdrc *ci = dev_get_drvdata(dev);
+ int ret;
+
+ hw_write(ci, OP_ULPI_VIEWPORT, 0xffffffff, ULPI_WRITE | ULPI_WAKEUP);
+ ret = ci_ulpi_wait(ci, ULPI_WAKEUP);
+ if (ret)
+ return ret;
+
+ hw_write(ci, OP_ULPI_VIEWPORT, 0xffffffff,
+ ULPI_RUN | ULPI_WRITE | ULPI_ADDR(addr) | val);
+ return ci_ulpi_wait(ci, ULPI_RUN);
+}
+
+int ci_ulpi_init(struct ci_hdrc *ci)
+{
+ if (ci->platdata->phy_mode != USBPHY_INTERFACE_MODE_ULPI)
+ return 0;
+
+ /*
+ * Set PORTSC correctly so we can read/write ULPI registers for
+ * identification purposes
+ */
+ hw_phymode_configure(ci);
+
+ ci->ulpi_ops.read = ci_ulpi_read;
+ ci->ulpi_ops.write = ci_ulpi_write;
+ ci->ulpi = ulpi_register_interface(ci->dev, &ci->ulpi_ops);
+ if (IS_ERR(ci->ulpi))
+ dev_err(ci->dev, "failed to register ULPI interface");
+
+ return PTR_ERR_OR_ZERO(ci->ulpi);
+}
+
+void ci_ulpi_exit(struct ci_hdrc *ci)
+{
+ if (ci->ulpi) {
+ ulpi_unregister_interface(ci->ulpi);
+ ci->ulpi = NULL;
+ }
+}
+
+int ci_ulpi_resume(struct ci_hdrc *ci)
+{
+ int cnt = 100000;
+
+ if (ci->platdata->phy_mode != USBPHY_INTERFACE_MODE_ULPI)
+ return 0;
+
+ while (cnt-- > 0) {
+ if (hw_read(ci, OP_ULPI_VIEWPORT, ULPI_SYNC_STATE))
+ return 0;
+ udelay(1);
+ }
+
+ return -ETIMEDOUT;
+}