diff options
| author | Breno Leitao <leitao@debian.org> | 2026-08-05 07:52:36 -0700 |
|---|---|---|
| committer | Tejun Heo <tj@kernel.org> | 2026-08-10 11:47:20 -1000 |
| commit | a5bde5d8fde8a8cb28e59a672d5ddc5b9c1e7656 (patch) | |
| tree | 39f5b258386e1127d253d293194e9a2991bf3c34 /drivers/usb/chipidea/ulpi.c | |
| download | linux-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.c | 108 |
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; +} |
