diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2026-10-02 12:17:24 -0700 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2026-10-02 12:17:24 -0700 |
| commit | 3f1fe48a36b0b6722dc3fd421d93512bac138e9a (patch) | |
| tree | b767d7f6e26bc64334d3f14f8422d187239dc45d /drivers/spmi/spmi-apple-controller.c | |
| download | linux-stable-3f1fe48a36b0b6722dc3fd421d93512bac138e9a.tar.gz linux-stable-3f1fe48a36b0b6722dc3fd421d93512bac138e9a.zip | |
Merge tag 'io_uring-7.3-20261002' of git://git.kernel.org/pub/scm/linux/kernel/git/axboe/linuxgrafted
Pull io_uring fixes from Jens Axboe:
- Fix a task_work add use-after-free with SQPOLL.
The sqpoll thread could pop and complete the last request while
io_req_normal_work_add() was still looking at them after the mpscq
push.
Use the same approach as DEFER_TASKRUN to protect from that, holding
an RCU read lock across the add, and have exit wait for an RCU grace
period for SQPOLL rings as well.
- CQE32 ring fixes: correct the free entry check for 32b CQEs, zero the
big_cqe for aux CQEs, and only post the dummy skip CQE on CQE_MIXED
rings
- Mark the source filter table as COW when cloning bpf filters, so
registering another filter on the source doesn't modify the shared
table in place
- Initialize the task context before running the BPF loop
- Requeue zcrx multishot receives stopped by a local resource
- End a TX_TIMESTAMP multishot cmd when the CQ is full (lollipopkit)
* tag 'io_uring-7.3-20261002' of git://git.kernel.org/pub/scm/linux/kernel/git/axboe/linux:
io_uring: fix task_work add use-after-free with SQPOLL
io_uring/cmd_net: end TX_TIMESTAMP multishot when the CQ is full
io_uring/zcrx: requeue multishot receives stopped by a local resource
io_uring: initialize task context before running the BPF loop
io_uring: zero big_cqe for aux CQEs on CQE32 rings
io_uring: fix free entry check for 32b CQEs on CQE32 rings
io_uring: only post the dummy skip CQE on CQE_MIXED rings
io_uring/bpf_filter: mark source as COW when cloning filters
Diffstat (limited to 'drivers/spmi/spmi-apple-controller.c')
| -rw-r--r-- | drivers/spmi/spmi-apple-controller.c | 168 |
1 files changed, 168 insertions, 0 deletions
diff --git a/drivers/spmi/spmi-apple-controller.c b/drivers/spmi/spmi-apple-controller.c new file mode 100644 index 000000000..376cf682c --- /dev/null +++ b/drivers/spmi/spmi-apple-controller.c @@ -0,0 +1,168 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Apple SoC SPMI device driver + * + * Copyright The Asahi Linux Contributors + * + * Inspired by: + * OpenBSD support Copyright (c) 2021 Mark Kettenis <kettenis@openbsd.org> + * Correllium support Copyright (C) 2021 Corellium LLC + * hisi-spmi-controller.c + * spmi-pmic-arb.c Copyright (c) 2021, The Linux Foundation. + */ + +#include <linux/io.h> +#include <linux/iopoll.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/spmi.h> + +/* SPMI Controller Registers */ +#define SPMI_STATUS_REG 0 +#define SPMI_CMD_REG 0x4 +#define SPMI_RSP_REG 0x8 + +#define SPMI_RX_FIFO_EMPTY BIT(24) + +#define REG_POLL_INTERVAL_US 10000 +#define REG_POLL_TIMEOUT_US (REG_POLL_INTERVAL_US * 5) + +struct apple_spmi { + void __iomem *regs; +}; + +#define poll_reg(spmi, reg, val, cond) \ + readl_poll_timeout((spmi)->regs + (reg), (val), (cond), \ + REG_POLL_INTERVAL_US, REG_POLL_TIMEOUT_US) + +static inline u32 apple_spmi_pack_cmd(u8 opc, u8 sid, u16 saddr, size_t len) +{ + return opc | sid << 8 | saddr << 16 | (len - 1) | (1 << 15); +} + +/* Wait for Rx FIFO to have something */ +static int apple_spmi_wait_rx_not_empty(struct spmi_controller *ctrl) +{ + struct apple_spmi *spmi = spmi_controller_get_drvdata(ctrl); + int ret; + u32 status; + + ret = poll_reg(spmi, SPMI_STATUS_REG, status, !(status & SPMI_RX_FIFO_EMPTY)); + if (ret) { + dev_err(&ctrl->dev, + "failed to wait for RX FIFO not empty\n"); + return ret; + } + + return 0; +} + +static int spmi_read_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid, + u16 saddr, u8 *buf, size_t len) +{ + struct apple_spmi *spmi = spmi_controller_get_drvdata(ctrl); + u32 spmi_cmd = apple_spmi_pack_cmd(opc, sid, saddr, len); + u32 rsp; + size_t len_read = 0; + u8 i; + int ret; + + writel(spmi_cmd, spmi->regs + SPMI_CMD_REG); + + ret = apple_spmi_wait_rx_not_empty(ctrl); + if (ret) + return ret; + + /* Discard SPMI reply status */ + readl(spmi->regs + SPMI_RSP_REG); + + /* Read SPMI data reply */ + while (len_read < len) { + rsp = readl(spmi->regs + SPMI_RSP_REG); + i = 0; + while ((len_read < len) && (i < 4)) { + buf[len_read++] = ((0xff << (8 * i)) & rsp) >> (8 * i); + i += 1; + } + } + + return 0; +} + +static int spmi_write_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid, + u16 saddr, const u8 *buf, size_t len) +{ + struct apple_spmi *spmi = spmi_controller_get_drvdata(ctrl); + u32 spmi_cmd = apple_spmi_pack_cmd(opc, sid, saddr, len); + size_t i = 0, j; + int ret; + + writel(spmi_cmd, spmi->regs + SPMI_CMD_REG); + + while (i < len) { + j = 0; + spmi_cmd = 0; + while ((j < 4) & (i < len)) + spmi_cmd |= buf[i++] << (j++ * 8); + + writel(spmi_cmd, spmi->regs + SPMI_CMD_REG); + } + + ret = apple_spmi_wait_rx_not_empty(ctrl); + if (ret) + return ret; + + /* Discard */ + readl(spmi->regs + SPMI_RSP_REG); + + return 0; +} + +static int apple_spmi_probe(struct platform_device *pdev) +{ + struct apple_spmi *spmi; + struct spmi_controller *ctrl; + int ret; + + ctrl = devm_spmi_controller_alloc(&pdev->dev, sizeof(*spmi)); + if (IS_ERR(ctrl)) + return -ENOMEM; + + spmi = spmi_controller_get_drvdata(ctrl); + + spmi->regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(spmi->regs)) + return PTR_ERR(spmi->regs); + + ctrl->dev.of_node = pdev->dev.of_node; + + ctrl->read_cmd = spmi_read_cmd; + ctrl->write_cmd = spmi_write_cmd; + + ret = devm_spmi_controller_add(&pdev->dev, ctrl); + if (ret) + return dev_err_probe(&pdev->dev, ret, + "spmi_controller_add failed\n"); + + return 0; +} + +static const struct of_device_id apple_spmi_match_table[] = { + { .compatible = "apple,t8103-spmi", }, + { .compatible = "apple,spmi", }, + {} +}; +MODULE_DEVICE_TABLE(of, apple_spmi_match_table); + +static struct platform_driver apple_spmi_driver = { + .probe = apple_spmi_probe, + .driver = { + .name = "apple-spmi", + .of_match_table = apple_spmi_match_table, + }, +}; +module_platform_driver(apple_spmi_driver); + +MODULE_AUTHOR("Jean-Francois Bortolotti <jeff@borto.fr>"); +MODULE_DESCRIPTION("Apple SoC SPMI driver"); +MODULE_LICENSE("GPL"); |
