2023-12-06 23:17:48

by Stephen Boyd

[permalink] [raw]
Subject: [PATCH 0/8] SPMI patches for the merge window

Hi Greg,

Here's a collection of SPMI patches that I picked up a bit ago but
didn't send because we were too close to the merge window. The first
patch should probably be applied now though, because we're hanging the
PMIF interface on some mediatek boards when async probing. Otherwise the
patches improve mediatek spmi controllers and introduce some devm
wrappers for spmi controllers to simplify.

Fei Shao (5):
spmi: Introduce device-managed functions
spmi: Use devm_spmi_controller_alloc()
spmi: mtk-pmif: Reorder driver remove sequence
spmi: hisi-spmi-controller: Use devm_spmi_controller_add()
spmi: Return meaningful errors in spmi_controller_alloc()

Nícolas F. R. A. Prado (1):
spmi: mtk-pmif: Serialize PMIF status check and command submission

Sen Chu (1):
spmi: mediatek: add device id check

Yu-Che Cheng (1):
spmi: mediatek: Fix UAF on device remove

drivers/spmi/Makefile | 2 +-
drivers/spmi/hisi-spmi-controller.c | 32 +++--------
drivers/spmi/spmi-devres.c | 64 +++++++++++++++++++++
drivers/spmi/spmi-mtk-pmif.c | 61 ++++++++++++--------
drivers/spmi/spmi-pmic-arb.c | 87 ++++++++++-------------------
drivers/spmi/spmi.c | 6 +-
include/linux/spmi.h | 3 +
7 files changed, 146 insertions(+), 109 deletions(-)
create mode 100644 drivers/spmi/spmi-devres.c


base-commit: 0bb80ecc33a8fb5a682236443c1e740d5c917d1d
--
https://git.kernel.org/pub/scm/linux/kernel/git/clk/linux.git/
https://git.kernel.org/pub/scm/linux/kernel/git/sboyd/spmi.git


2023-12-06 23:17:55

by Stephen Boyd

[permalink] [raw]
Subject: [PATCH 2/8] spmi: mediatek: Fix UAF on device remove

From: Yu-Che Cheng <[email protected]>

The pmif driver data that contains the clocks is allocated along with
spmi_controller.
On device remove, spmi_controller will be freed first, and then devres
, including the clocks, will be cleanup.
This leads to UAF because putting the clocks will access the clocks in
the pmif driver data, which is already freed along with spmi_controller.

This can be reproduced by enabling DEBUG_TEST_DRIVER_REMOVE and
building the kernel with KASAN.

Fix the UAF issue by using unmanaged clk_bulk_get() and putting the
clocks before freeing spmi_controller.

Reported-by: Fei Shao <[email protected]>
Signed-off-by: Yu-Che Cheng <[email protected]>
Link: https://lore.kernel.org/r/20230717173934.1.If004a6e055a189c7f2d0724fa814422c26789839@changeid
Tested-by: Fei Shao <[email protected]>
Reviewed-by: Fei Shao <[email protected]>
Reviewed-by: Chen-Yu Tsai <[email protected]>
Signed-off-by: Stephen Boyd <[email protected]>
---
drivers/spmi/spmi-mtk-pmif.c | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/drivers/spmi/spmi-mtk-pmif.c b/drivers/spmi/spmi-mtk-pmif.c
index 54c35f5535cb..1261f381cae6 100644
--- a/drivers/spmi/spmi-mtk-pmif.c
+++ b/drivers/spmi/spmi-mtk-pmif.c
@@ -475,7 +475,7 @@ static int mtk_spmi_probe(struct platform_device *pdev)
for (i = 0; i < arb->nclks; i++)
arb->clks[i].id = pmif_clock_names[i];

- err = devm_clk_bulk_get(&pdev->dev, arb->nclks, arb->clks);
+ err = clk_bulk_get(&pdev->dev, arb->nclks, arb->clks);
if (err) {
dev_err(&pdev->dev, "Failed to get clocks: %d\n", err);
goto err_put_ctrl;
@@ -484,7 +484,7 @@ static int mtk_spmi_probe(struct platform_device *pdev)
err = clk_bulk_prepare_enable(arb->nclks, arb->clks);
if (err) {
dev_err(&pdev->dev, "Failed to enable clocks: %d\n", err);
- goto err_put_ctrl;
+ goto err_put_clks;
}

ctrl->cmd = pmif_arb_cmd;
@@ -510,6 +510,8 @@ static int mtk_spmi_probe(struct platform_device *pdev)

err_domain_remove:
clk_bulk_disable_unprepare(arb->nclks, arb->clks);
+err_put_clks:
+ clk_bulk_put(arb->nclks, arb->clks);
err_put_ctrl:
spmi_controller_put(ctrl);
return err;
@@ -521,6 +523,7 @@ static void mtk_spmi_remove(struct platform_device *pdev)
struct pmif *arb = spmi_controller_get_drvdata(ctrl);

clk_bulk_disable_unprepare(arb->nclks, arb->clks);
+ clk_bulk_put(arb->nclks, arb->clks);
spmi_controller_remove(ctrl);
spmi_controller_put(ctrl);
}
--
https://git.kernel.org/pub/scm/linux/kernel/git/clk/linux.git/
https://git.kernel.org/pub/scm/linux/kernel/git/sboyd/spmi.git

2023-12-06 23:18:01

by Stephen Boyd

[permalink] [raw]
Subject: [PATCH 1/8] spmi: mtk-pmif: Serialize PMIF status check and command submission

From: Nícolas F. R. A. Prado <[email protected]>

Before writing the read or write command to the SPMI arbiter through the
PMIF interface, the current status of the channel is checked to ensure
it is idle. However, since the status only changes from idle when the
command is written, it is possible for two concurrent calls to determine
that the channel is idle and simultaneously send their commands. At this
point the PMIF interface hangs, with the status register no longer being
updated, and thus causing all subsequent operations to time out.

This was observed on the mt8195-cherry-tomato-r2 machine, particularly
after commit 46600ab142f8 ("regulator: Set PROBE_PREFER_ASYNCHRONOUS for
drivers between 5.10 and 5.15") was applied, since then the two MT6315
devices present on the SPMI bus would probe assynchronously and
sometimes (during probe or at a later point) read the bus
simultaneously, breaking the PMIF interface and consequently slowing
down the whole system.

To fix the issue at its root cause, introduce locking around the channel
status check and the command write, so that both become an atomic
operation, preventing race conditions between two (or more) SPMI bus
read/write operations. A spinlock is used since this is a fast bus, as
indicated by the usage of the atomic variant of readl_poll, and
'.fast_io = true' being used in the mt6315 driver, so spinlocks are
already used for the regmap access.

Fixes: b45b3ccef8c0 ("spmi: mediatek: Add support for MT6873/8192")
Signed-off-by: Nícolas F. R. A. Prado <[email protected]>
Link: https://lore.kernel.org/r/[email protected]
Reviewed-by: AngeloGioacchino Del Regno <[email protected]>
Reviewed-by: Alexandre Mergnat <[email protected]>
Signed-off-by: Stephen Boyd <[email protected]>
---
drivers/spmi/spmi-mtk-pmif.c | 20 ++++++++++++++++----
1 file changed, 16 insertions(+), 4 deletions(-)

diff --git a/drivers/spmi/spmi-mtk-pmif.c b/drivers/spmi/spmi-mtk-pmif.c
index b3c991e1ea40..54c35f5535cb 100644
--- a/drivers/spmi/spmi-mtk-pmif.c
+++ b/drivers/spmi/spmi-mtk-pmif.c
@@ -50,6 +50,7 @@ struct pmif {
struct clk_bulk_data clks[PMIF_MAX_CLKS];
size_t nclks;
const struct pmif_data *data;
+ raw_spinlock_t lock;
};

static const char * const pmif_clock_names[] = {
@@ -314,6 +315,7 @@ static int pmif_spmi_read_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,
struct ch_reg *inf_reg;
int ret;
u32 data, cmd;
+ unsigned long flags;

/* Check for argument validation. */
if (sid & ~0xf) {
@@ -334,6 +336,7 @@ static int pmif_spmi_read_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,
else
return -EINVAL;

+ raw_spin_lock_irqsave(&arb->lock, flags);
/* Wait for Software Interface FSM state to be IDLE. */
inf_reg = &arb->chan;
ret = readl_poll_timeout_atomic(arb->base + arb->data->regs[inf_reg->ch_sta],
@@ -343,6 +346,7 @@ static int pmif_spmi_read_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,
/* set channel ready if the data has transferred */
if (pmif_is_fsm_vldclr(arb))
pmif_writel(arb, 1, inf_reg->ch_rdy);
+ raw_spin_unlock_irqrestore(&arb->lock, flags);
dev_err(&ctrl->dev, "failed to wait for SWINF_IDLE\n");
return ret;
}
@@ -350,6 +354,7 @@ static int pmif_spmi_read_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,
/* Send the command. */
cmd = (opc << 30) | (sid << 24) | ((len - 1) << 16) | addr;
pmif_writel(arb, cmd, inf_reg->ch_send);
+ raw_spin_unlock_irqrestore(&arb->lock, flags);

/*
* Wait for Software Interface FSM state to be WFVLDCLR,
@@ -376,7 +381,8 @@ static int pmif_spmi_write_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,
struct pmif *arb = spmi_controller_get_drvdata(ctrl);
struct ch_reg *inf_reg;
int ret;
- u32 data, cmd;
+ u32 data, wdata, cmd;
+ unsigned long flags;

if (len > 4) {
dev_err(&ctrl->dev, "pmif supports 1..4 bytes per trans, but:%zu requested", len);
@@ -394,6 +400,10 @@ static int pmif_spmi_write_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,
else
return -EINVAL;

+ /* Set the write data. */
+ memcpy(&wdata, buf, len);
+
+ raw_spin_lock_irqsave(&arb->lock, flags);
/* Wait for Software Interface FSM state to be IDLE. */
inf_reg = &arb->chan;
ret = readl_poll_timeout_atomic(arb->base + arb->data->regs[inf_reg->ch_sta],
@@ -403,17 +413,17 @@ static int pmif_spmi_write_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,
/* set channel ready if the data has transferred */
if (pmif_is_fsm_vldclr(arb))
pmif_writel(arb, 1, inf_reg->ch_rdy);
+ raw_spin_unlock_irqrestore(&arb->lock, flags);
dev_err(&ctrl->dev, "failed to wait for SWINF_IDLE\n");
return ret;
}

- /* Set the write data. */
- memcpy(&data, buf, len);
- pmif_writel(arb, data, inf_reg->wdata);
+ pmif_writel(arb, wdata, inf_reg->wdata);

/* Send the command. */
cmd = (opc << 30) | BIT(29) | (sid << 24) | ((len - 1) << 16) | addr;
pmif_writel(arb, cmd, inf_reg->ch_send);
+ raw_spin_unlock_irqrestore(&arb->lock, flags);

return 0;
}
@@ -488,6 +498,8 @@ static int mtk_spmi_probe(struct platform_device *pdev)
arb->chan.ch_send = PMIF_SWINF_0_ACC + chan_offset;
arb->chan.ch_rdy = PMIF_SWINF_0_VLD_CLR + chan_offset;

+ raw_spin_lock_init(&arb->lock);
+
platform_set_drvdata(pdev, ctrl);

err = spmi_controller_add(ctrl);
--
https://git.kernel.org/pub/scm/linux/kernel/git/clk/linux.git/
https://git.kernel.org/pub/scm/linux/kernel/git/sboyd/spmi.git

2023-12-06 23:18:14

by Stephen Boyd

[permalink] [raw]
Subject: [PATCH 6/8] spmi: hisi-spmi-controller: Use devm_spmi_controller_add()

From: Fei Shao <[email protected]>

Convert to the device-managed version of spmi_controller_add() and
delete the unnecessary driver remove callback.

Signed-off-by: Fei Shao <[email protected]>
Link: https://lore.kernel.org/r/[email protected]
Signed-off-by: Stephen Boyd <[email protected]>
---
drivers/spmi/hisi-spmi-controller.c | 10 +---------
1 file changed, 1 insertion(+), 9 deletions(-)

diff --git a/drivers/spmi/hisi-spmi-controller.c b/drivers/spmi/hisi-spmi-controller.c
index a5525902656a..674a350cc676 100644
--- a/drivers/spmi/hisi-spmi-controller.c
+++ b/drivers/spmi/hisi-spmi-controller.c
@@ -308,7 +308,7 @@ static int spmi_controller_probe(struct platform_device *pdev)
ctrl->read_cmd = spmi_read_cmd;
ctrl->write_cmd = spmi_write_cmd;

- ret = spmi_controller_add(ctrl);
+ ret = devm_spmi_controller_add(&pdev->dev, ctrl);
if (ret) {
dev_err(&pdev->dev, "spmi_controller_add failed with error %d!\n", ret);
return ret;
@@ -317,13 +317,6 @@ static int spmi_controller_probe(struct platform_device *pdev)
return 0;
}

-static void spmi_del_controller(struct platform_device *pdev)
-{
- struct spmi_controller *ctrl = platform_get_drvdata(pdev);
-
- spmi_controller_remove(ctrl);
-}
-
static const struct of_device_id spmi_controller_match_table[] = {
{
.compatible = "hisilicon,kirin970-spmi-controller",
@@ -334,7 +327,6 @@ MODULE_DEVICE_TABLE(of, spmi_controller_match_table);

static struct platform_driver spmi_controller_driver = {
.probe = spmi_controller_probe,
- .remove_new = spmi_del_controller,
.driver = {
.name = "hisi_spmi_controller",
.of_match_table = spmi_controller_match_table,
--
https://git.kernel.org/pub/scm/linux/kernel/git/clk/linux.git/
https://git.kernel.org/pub/scm/linux/kernel/git/sboyd/spmi.git

2023-12-06 23:18:14

by Stephen Boyd

[permalink] [raw]
Subject: [PATCH 4/8] spmi: Use devm_spmi_controller_alloc()

From: Fei Shao <[email protected]>

Convert to the device-managed version of spmi_controller_alloc() and
simplify the excess error handling code.

Signed-off-by: Fei Shao <[email protected]>
Link: https://lore.kernel.org/r/[email protected]
Reviewed-by: AngeloGioacchino Del Regno <[email protected]>
Signed-off-by: Stephen Boyd <[email protected]>
---
drivers/spmi/hisi-spmi-controller.c | 22 +++-----
drivers/spmi/spmi-mtk-pmif.c | 26 +++------
drivers/spmi/spmi-pmic-arb.c | 87 ++++++++++-------------------
3 files changed, 46 insertions(+), 89 deletions(-)

diff --git a/drivers/spmi/hisi-spmi-controller.c b/drivers/spmi/hisi-spmi-controller.c
index 9cbd473487cb..a5525902656a 100644
--- a/drivers/spmi/hisi-spmi-controller.c
+++ b/drivers/spmi/hisi-spmi-controller.c
@@ -267,10 +267,10 @@ static int spmi_controller_probe(struct platform_device *pdev)
struct resource *iores;
int ret;

- ctrl = spmi_controller_alloc(&pdev->dev, sizeof(*spmi_controller));
- if (!ctrl) {
+ ctrl = devm_spmi_controller_alloc(&pdev->dev, sizeof(*spmi_controller));
+ if (IS_ERR(ctrl)) {
dev_err(&pdev->dev, "can not allocate spmi_controller data\n");
- return -ENOMEM;
+ return PTR_ERR(ctrl);
}
spmi_controller = spmi_controller_get_drvdata(ctrl);
spmi_controller->controller = ctrl;
@@ -278,24 +278,21 @@ static int spmi_controller_probe(struct platform_device *pdev)
iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!iores) {
dev_err(&pdev->dev, "can not get resource!\n");
- ret = -EINVAL;
- goto err_put_controller;
+ return -EINVAL;
}

spmi_controller->base = devm_ioremap(&pdev->dev, iores->start,
resource_size(iores));
if (!spmi_controller->base) {
dev_err(&pdev->dev, "can not remap base addr!\n");
- ret = -EADDRNOTAVAIL;
- goto err_put_controller;
+ return -EADDRNOTAVAIL;
}

ret = of_property_read_u32(pdev->dev.of_node, "hisilicon,spmi-channel",
&spmi_controller->channel);
if (ret) {
dev_err(&pdev->dev, "can not get channel\n");
- ret = -ENODEV;
- goto err_put_controller;
+ return -ENODEV;
}

platform_set_drvdata(pdev, spmi_controller);
@@ -314,14 +311,10 @@ static int spmi_controller_probe(struct platform_device *pdev)
ret = spmi_controller_add(ctrl);
if (ret) {
dev_err(&pdev->dev, "spmi_controller_add failed with error %d!\n", ret);
- goto err_put_controller;
+ return ret;
}

return 0;
-
-err_put_controller:
- spmi_controller_put(ctrl);
- return ret;
}

static void spmi_del_controller(struct platform_device *pdev)
@@ -329,7 +322,6 @@ static void spmi_del_controller(struct platform_device *pdev)
struct spmi_controller *ctrl = platform_get_drvdata(pdev);

spmi_controller_remove(ctrl);
- spmi_controller_put(ctrl);
}

static const struct of_device_id spmi_controller_match_table[] = {
diff --git a/drivers/spmi/spmi-mtk-pmif.c b/drivers/spmi/spmi-mtk-pmif.c
index 1261f381cae6..6ebc6901490a 100644
--- a/drivers/spmi/spmi-mtk-pmif.c
+++ b/drivers/spmi/spmi-mtk-pmif.c
@@ -447,29 +447,24 @@ static int mtk_spmi_probe(struct platform_device *pdev)
int err, i;
u32 chan_offset;

- ctrl = spmi_controller_alloc(&pdev->dev, sizeof(*arb));
- if (!ctrl)
- return -ENOMEM;
+ ctrl = devm_spmi_controller_alloc(&pdev->dev, sizeof(*arb));
+ if (IS_ERR(ctrl))
+ return PTR_ERR(ctrl);

arb = spmi_controller_get_drvdata(ctrl);
arb->data = device_get_match_data(&pdev->dev);
if (!arb->data) {
- err = -EINVAL;
dev_err(&pdev->dev, "Cannot get drv_data\n");
- goto err_put_ctrl;
+ return -EINVAL;
}

arb->base = devm_platform_ioremap_resource_byname(pdev, "pmif");
- if (IS_ERR(arb->base)) {
- err = PTR_ERR(arb->base);
- goto err_put_ctrl;
- }
+ if (IS_ERR(arb->base))
+ return PTR_ERR(arb->base);

arb->spmimst_base = devm_platform_ioremap_resource_byname(pdev, "spmimst");
- if (IS_ERR(arb->spmimst_base)) {
- err = PTR_ERR(arb->spmimst_base);
- goto err_put_ctrl;
- }
+ if (IS_ERR(arb->spmimst_base))
+ return PTR_ERR(arb->spmimst_base);

arb->nclks = ARRAY_SIZE(pmif_clock_names);
for (i = 0; i < arb->nclks; i++)
@@ -478,7 +473,7 @@ static int mtk_spmi_probe(struct platform_device *pdev)
err = clk_bulk_get(&pdev->dev, arb->nclks, arb->clks);
if (err) {
dev_err(&pdev->dev, "Failed to get clocks: %d\n", err);
- goto err_put_ctrl;
+ return err;
}

err = clk_bulk_prepare_enable(arb->nclks, arb->clks);
@@ -512,8 +507,6 @@ static int mtk_spmi_probe(struct platform_device *pdev)
clk_bulk_disable_unprepare(arb->nclks, arb->clks);
err_put_clks:
clk_bulk_put(arb->nclks, arb->clks);
-err_put_ctrl:
- spmi_controller_put(ctrl);
return err;
}

@@ -525,7 +518,6 @@ static void mtk_spmi_remove(struct platform_device *pdev)
clk_bulk_disable_unprepare(arb->nclks, arb->clks);
clk_bulk_put(arb->nclks, arb->clks);
spmi_controller_remove(ctrl);
- spmi_controller_put(ctrl);
}

static const struct of_device_id mtk_spmi_match_table[] = {
diff --git a/drivers/spmi/spmi-pmic-arb.c b/drivers/spmi/spmi-pmic-arb.c
index dcb675d980d4..9ed1180fe31f 100644
--- a/drivers/spmi/spmi-pmic-arb.c
+++ b/drivers/spmi/spmi-pmic-arb.c
@@ -1443,9 +1443,9 @@ static int spmi_pmic_arb_probe(struct platform_device *pdev)
u32 channel, ee, hw_ver;
int err;

- ctrl = spmi_controller_alloc(&pdev->dev, sizeof(*pmic_arb));
- if (!ctrl)
- return -ENOMEM;
+ ctrl = devm_spmi_controller_alloc(&pdev->dev, sizeof(*pmic_arb));
+ if (IS_ERR(ctrl))
+ return PTR_ERR(ctrl);

pmic_arb = spmi_controller_get_drvdata(ctrl);
pmic_arb->spmic = ctrl;
@@ -1462,20 +1462,16 @@ static int spmi_pmic_arb_probe(struct platform_device *pdev)
*/
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "core");
core = devm_ioremap(&ctrl->dev, res->start, resource_size(res));
- if (IS_ERR(core)) {
- err = PTR_ERR(core);
- goto err_put_ctrl;
- }
+ if (IS_ERR(core))
+ return PTR_ERR(core);

pmic_arb->core_size = resource_size(res);

pmic_arb->ppid_to_apid = devm_kcalloc(&ctrl->dev, PMIC_ARB_MAX_PPID,
sizeof(*pmic_arb->ppid_to_apid),
GFP_KERNEL);
- if (!pmic_arb->ppid_to_apid) {
- err = -ENOMEM;
- goto err_put_ctrl;
- }
+ if (!pmic_arb->ppid_to_apid)
+ return -ENOMEM;

hw_ver = readl_relaxed(core + PMIC_ARB_VERSION);

@@ -1499,19 +1495,15 @@ static int spmi_pmic_arb_probe(struct platform_device *pdev)
"obsrvr");
pmic_arb->rd_base = devm_ioremap(&ctrl->dev, res->start,
resource_size(res));
- if (IS_ERR(pmic_arb->rd_base)) {
- err = PTR_ERR(pmic_arb->rd_base);
- goto err_put_ctrl;
- }
+ if (IS_ERR(pmic_arb->rd_base))
+ return PTR_ERR(pmic_arb->rd_base);

res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
"chnls");
pmic_arb->wr_base = devm_ioremap(&ctrl->dev, res->start,
resource_size(res));
- if (IS_ERR(pmic_arb->wr_base)) {
- err = PTR_ERR(pmic_arb->wr_base);
- goto err_put_ctrl;
- }
+ if (IS_ERR(pmic_arb->wr_base))
+ return PTR_ERR(pmic_arb->wr_base);
}

pmic_arb->max_periphs = PMIC_ARB_MAX_PERIPHS;
@@ -1522,10 +1514,9 @@ static int spmi_pmic_arb_probe(struct platform_device *pdev)
of_property_read_u32(pdev->dev.of_node, "qcom,bus-id",
&pmic_arb->bus_instance);
if (pmic_arb->bus_instance > 1) {
- err = -EINVAL;
dev_err(&pdev->dev, "invalid bus instance (%u) specified\n",
pmic_arb->bus_instance);
- goto err_put_ctrl;
+ return -EINVAL;
}

if (pmic_arb->bus_instance == 0) {
@@ -1543,10 +1534,9 @@ static int spmi_pmic_arb_probe(struct platform_device *pdev)
}

if (pmic_arb->base_apid + pmic_arb->apid_count > pmic_arb->max_periphs) {
- err = -EINVAL;
dev_err(&pdev->dev, "Unsupported APID count %d detected\n",
pmic_arb->base_apid + pmic_arb->apid_count);
- goto err_put_ctrl;
+ return -EINVAL;
}
} else if (hw_ver >= PMIC_ARB_VERSION_V5_MIN) {
pmic_arb->base_apid = 0;
@@ -1554,55 +1544,45 @@ static int spmi_pmic_arb_probe(struct platform_device *pdev)
PMIC_ARB_FEATURES_PERIPH_MASK;

if (pmic_arb->apid_count > pmic_arb->max_periphs) {
- err = -EINVAL;
dev_err(&pdev->dev, "Unsupported APID count %d detected\n",
pmic_arb->apid_count);
- goto err_put_ctrl;
+ return -EINVAL;
}
}

pmic_arb->apid_data = devm_kcalloc(&ctrl->dev, pmic_arb->max_periphs,
sizeof(*pmic_arb->apid_data),
GFP_KERNEL);
- if (!pmic_arb->apid_data) {
- err = -ENOMEM;
- goto err_put_ctrl;
- }
+ if (!pmic_arb->apid_data)
+ return -ENOMEM;

dev_info(&ctrl->dev, "PMIC arbiter version %s (0x%x)\n",
pmic_arb->ver_ops->ver_str, hw_ver);

res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "intr");
pmic_arb->intr = devm_ioremap_resource(&ctrl->dev, res);
- if (IS_ERR(pmic_arb->intr)) {
- err = PTR_ERR(pmic_arb->intr);
- goto err_put_ctrl;
- }
+ if (IS_ERR(pmic_arb->intr))
+ return PTR_ERR(pmic_arb->intr);

res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cnfg");
pmic_arb->cnfg = devm_ioremap_resource(&ctrl->dev, res);
- if (IS_ERR(pmic_arb->cnfg)) {
- err = PTR_ERR(pmic_arb->cnfg);
- goto err_put_ctrl;
- }
+ if (IS_ERR(pmic_arb->cnfg))
+ return PTR_ERR(pmic_arb->cnfg);

pmic_arb->irq = platform_get_irq_byname(pdev, "periph_irq");
- if (pmic_arb->irq < 0) {
- err = pmic_arb->irq;
- goto err_put_ctrl;
- }
+ if (pmic_arb->irq < 0)
+ return pmic_arb->irq;

err = of_property_read_u32(pdev->dev.of_node, "qcom,channel", &channel);
if (err) {
dev_err(&pdev->dev, "channel unspecified.\n");
- goto err_put_ctrl;
+ return err;
}

if (channel > 5) {
dev_err(&pdev->dev, "invalid channel (%u) specified.\n",
channel);
- err = -EINVAL;
- goto err_put_ctrl;
+ return -EINVAL;
}

pmic_arb->channel = channel;
@@ -1610,22 +1590,19 @@ static int spmi_pmic_arb_probe(struct platform_device *pdev)
err = of_property_read_u32(pdev->dev.of_node, "qcom,ee", &ee);
if (err) {
dev_err(&pdev->dev, "EE unspecified.\n");
- goto err_put_ctrl;
+ return err;
}

if (ee > 5) {
dev_err(&pdev->dev, "invalid EE (%u) specified\n", ee);
- err = -EINVAL;
- goto err_put_ctrl;
+ return -EINVAL;
}

pmic_arb->ee = ee;
mapping_table = devm_kcalloc(&ctrl->dev, pmic_arb->max_periphs,
sizeof(*mapping_table), GFP_KERNEL);
- if (!mapping_table) {
- err = -ENOMEM;
- goto err_put_ctrl;
- }
+ if (!mapping_table)
+ return -ENOMEM;

pmic_arb->mapping_table = mapping_table;
/* Initialize max_apid/min_apid to the opposite bounds, during
@@ -1645,7 +1622,7 @@ static int spmi_pmic_arb_probe(struct platform_device *pdev)
if (err) {
dev_err(&pdev->dev, "could not read APID->PPID mapping table, rc= %d\n",
err);
- goto err_put_ctrl;
+ return err;
}
}

@@ -1654,8 +1631,7 @@ static int spmi_pmic_arb_probe(struct platform_device *pdev)
&pmic_arb_irq_domain_ops, pmic_arb);
if (!pmic_arb->domain) {
dev_err(&pdev->dev, "unable to create irq_domain\n");
- err = -ENOMEM;
- goto err_put_ctrl;
+ return -ENOMEM;
}

irq_set_chained_handler_and_data(pmic_arb->irq, pmic_arb_chained_irq,
@@ -1669,8 +1645,6 @@ static int spmi_pmic_arb_probe(struct platform_device *pdev)
err_domain_remove:
irq_set_chained_handler_and_data(pmic_arb->irq, NULL, NULL);
irq_domain_remove(pmic_arb->domain);
-err_put_ctrl:
- spmi_controller_put(ctrl);
return err;
}

@@ -1681,7 +1655,6 @@ static void spmi_pmic_arb_remove(struct platform_device *pdev)
spmi_controller_remove(ctrl);
irq_set_chained_handler_and_data(pmic_arb->irq, NULL, NULL);
irq_domain_remove(pmic_arb->domain);
- spmi_controller_put(ctrl);
}

static const struct of_device_id spmi_pmic_arb_match_table[] = {
--
https://git.kernel.org/pub/scm/linux/kernel/git/clk/linux.git/
https://git.kernel.org/pub/scm/linux/kernel/git/sboyd/spmi.git

2023-12-06 23:18:25

by Stephen Boyd

[permalink] [raw]
Subject: [PATCH 3/8] spmi: Introduce device-managed functions

From: Fei Shao <[email protected]>

Utilize the managed resource (devres) framework and add the following
devm_* helpers for the SPMI driver:

- devm_spmi_controller_alloc()
- devm_spmi_controller_add()

Signed-off-by: Fei Shao <[email protected]>
Link: https://lore.kernel.org/r/[email protected]
Reviewed-by: AngeloGioacchino Del Regno <[email protected]>
[[email protected]: Rename to spmi-devres for module niceness, slap on
GPL module license]
Signed-off-by: Stephen Boyd <[email protected]>
---
drivers/spmi/Makefile | 2 +-
drivers/spmi/spmi-devres.c | 64 ++++++++++++++++++++++++++++++++++++++
include/linux/spmi.h | 3 ++
3 files changed, 68 insertions(+), 1 deletion(-)
create mode 100644 drivers/spmi/spmi-devres.c

diff --git a/drivers/spmi/Makefile b/drivers/spmi/Makefile
index 9d974424c8c1..7f152167bb05 100644
--- a/drivers/spmi/Makefile
+++ b/drivers/spmi/Makefile
@@ -2,7 +2,7 @@
#
# Makefile for kernel SPMI framework.
#
-obj-$(CONFIG_SPMI) += spmi.o
+obj-$(CONFIG_SPMI) += spmi.o spmi-devres.o

obj-$(CONFIG_SPMI_HISI3670) += hisi-spmi-controller.o
obj-$(CONFIG_SPMI_MSM_PMIC_ARB) += spmi-pmic-arb.o
diff --git a/drivers/spmi/spmi-devres.c b/drivers/spmi/spmi-devres.c
new file mode 100644
index 000000000000..7683e6fdb18f
--- /dev/null
+++ b/drivers/spmi/spmi-devres.c
@@ -0,0 +1,64 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2023 Google LLC.
+ */
+
+#include <linux/device.h>
+#include <linux/spmi.h>
+
+static void devm_spmi_controller_release(struct device *parent, void *res)
+{
+ spmi_controller_put(*(struct spmi_controller **)res);
+}
+
+struct spmi_controller *devm_spmi_controller_alloc(struct device *parent, size_t size)
+{
+ struct spmi_controller **ptr, *ctrl;
+
+ ptr = devres_alloc(devm_spmi_controller_release, sizeof(*ptr), GFP_KERNEL);
+ if (!ptr)
+ return ERR_PTR(-ENOMEM);
+
+ ctrl = spmi_controller_alloc(parent, size);
+ if (!ctrl) {
+ devres_free(ptr);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ *ptr = ctrl;
+ devres_add(parent, ptr);
+
+ return ctrl;
+}
+EXPORT_SYMBOL_GPL(devm_spmi_controller_alloc);
+
+static void devm_spmi_controller_remove(struct device *parent, void *res)
+{
+ spmi_controller_remove(*(struct spmi_controller **)res);
+}
+
+int devm_spmi_controller_add(struct device *parent, struct spmi_controller *ctrl)
+{
+ struct spmi_controller **ptr;
+ int ret;
+
+ ptr = devres_alloc(devm_spmi_controller_remove, sizeof(*ptr), GFP_KERNEL);
+ if (!ptr)
+ return -ENOMEM;
+
+ ret = spmi_controller_add(ctrl);
+ if (ret) {
+ devres_free(ptr);
+ return ret;
+ }
+
+ *ptr = ctrl;
+ devres_add(parent, ptr);
+
+ return 0;
+
+}
+EXPORT_SYMBOL_GPL(devm_spmi_controller_add);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("SPMI devres helpers");
diff --git a/include/linux/spmi.h b/include/linux/spmi.h
index eac1956a8727..14597708fdbc 100644
--- a/include/linux/spmi.h
+++ b/include/linux/spmi.h
@@ -120,6 +120,9 @@ static inline void spmi_controller_put(struct spmi_controller *ctrl)
int spmi_controller_add(struct spmi_controller *ctrl);
void spmi_controller_remove(struct spmi_controller *ctrl);

+struct spmi_controller *devm_spmi_controller_alloc(struct device *parent, size_t size);
+int devm_spmi_controller_add(struct device *parent, struct spmi_controller *ctrl);
+
/**
* struct spmi_driver - SPMI slave device driver
* @driver: SPMI device drivers should initialize name and owner field of
--
https://git.kernel.org/pub/scm/linux/kernel/git/clk/linux.git/
https://git.kernel.org/pub/scm/linux/kernel/git/sboyd/spmi.git

2023-12-06 23:18:37

by Stephen Boyd

[permalink] [raw]
Subject: [PATCH 8/8] spmi: mediatek: add device id check

From: Sen Chu <[email protected]>

Add device id check for spmi write API.

Signed-off-by: Sen Chu <[email protected]>
Link: https://lore.kernel.org/r/[email protected]
Reviewed-by: AngeloGioacchino Del Regno <[email protected]>
Signed-off-by: Stephen Boyd <[email protected]>
---
drivers/spmi/spmi-mtk-pmif.c | 6 ++++++
1 file changed, 6 insertions(+)

diff --git a/drivers/spmi/spmi-mtk-pmif.c b/drivers/spmi/spmi-mtk-pmif.c
index cc660da6a037..5079442f8ea1 100644
--- a/drivers/spmi/spmi-mtk-pmif.c
+++ b/drivers/spmi/spmi-mtk-pmif.c
@@ -384,6 +384,12 @@ static int pmif_spmi_write_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,
u32 data, wdata, cmd;
unsigned long flags;

+ /* Check for argument validation. */
+ if (unlikely(sid & ~0xf)) {
+ dev_err(&ctrl->dev, "exceed the max slv id\n");
+ return -EINVAL;
+ }
+
if (len > 4) {
dev_err(&ctrl->dev, "pmif supports 1..4 bytes per trans, but:%zu requested", len);

--
https://git.kernel.org/pub/scm/linux/kernel/git/clk/linux.git/
https://git.kernel.org/pub/scm/linux/kernel/git/sboyd/spmi.git

2023-12-06 23:18:38

by Stephen Boyd

[permalink] [raw]
Subject: [PATCH 7/8] spmi: Return meaningful errors in spmi_controller_alloc()

From: Fei Shao <[email protected]>

spmi_controller_alloc() currently returns NULL to all types of errors,
which can be improved.

Use appropriate error code in returns and pass the errors from used
functions where possible.

Signed-off-by: Fei Shao <[email protected]>
Link: https://lore.kernel.org/r/[email protected]
Signed-off-by: Stephen Boyd <[email protected]>
---
drivers/spmi/spmi-devres.c | 4 ++--
drivers/spmi/spmi.c | 6 +++---
2 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/drivers/spmi/spmi-devres.c b/drivers/spmi/spmi-devres.c
index 7683e6fdb18f..62c4b3f24d06 100644
--- a/drivers/spmi/spmi-devres.c
+++ b/drivers/spmi/spmi-devres.c
@@ -20,9 +20,9 @@ struct spmi_controller *devm_spmi_controller_alloc(struct device *parent, size_t
return ERR_PTR(-ENOMEM);

ctrl = spmi_controller_alloc(parent, size);
- if (!ctrl) {
+ if (IS_ERR(ctrl)) {
devres_free(ptr);
- return ERR_PTR(-ENOMEM);
+ return ctrl;
}

*ptr = ctrl;
diff --git a/drivers/spmi/spmi.c b/drivers/spmi/spmi.c
index 7313d4c18a04..5f11ced3f8b9 100644
--- a/drivers/spmi/spmi.c
+++ b/drivers/spmi/spmi.c
@@ -445,11 +445,11 @@ struct spmi_controller *spmi_controller_alloc(struct device *parent,
int id;

if (WARN_ON(!parent))
- return NULL;
+ return ERR_PTR(-EINVAL);

ctrl = kzalloc(sizeof(*ctrl) + size, GFP_KERNEL);
if (!ctrl)
- return NULL;
+ return ERR_PTR(-ENOMEM);

device_initialize(&ctrl->dev);
ctrl->dev.type = &spmi_ctrl_type;
@@ -463,7 +463,7 @@ struct spmi_controller *spmi_controller_alloc(struct device *parent,
dev_err(parent,
"unable to allocate SPMI controller identifier.\n");
spmi_controller_put(ctrl);
- return NULL;
+ return ERR_PTR(id);
}

ctrl->nr = id;
--
https://git.kernel.org/pub/scm/linux/kernel/git/clk/linux.git/
https://git.kernel.org/pub/scm/linux/kernel/git/sboyd/spmi.git

2023-12-06 23:18:45

by Stephen Boyd

[permalink] [raw]
Subject: [PATCH 5/8] spmi: mtk-pmif: Reorder driver remove sequence

From: Fei Shao <[email protected]>

This driver enables clocks and then adds SPMI controller in probing, so
we expect the reversed sequence in removal.
Fix the order in the remove callback.

Signed-off-by: Fei Shao <[email protected]>
Link: https://lore.kernel.org/r/[email protected]
Reviewed-by: AngeloGioacchino Del Regno <[email protected]>
Signed-off-by: Stephen Boyd <[email protected]>
---
drivers/spmi/spmi-mtk-pmif.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/spmi/spmi-mtk-pmif.c b/drivers/spmi/spmi-mtk-pmif.c
index 6ebc6901490a..cc660da6a037 100644
--- a/drivers/spmi/spmi-mtk-pmif.c
+++ b/drivers/spmi/spmi-mtk-pmif.c
@@ -515,9 +515,9 @@ static void mtk_spmi_remove(struct platform_device *pdev)
struct spmi_controller *ctrl = platform_get_drvdata(pdev);
struct pmif *arb = spmi_controller_get_drvdata(ctrl);

+ spmi_controller_remove(ctrl);
clk_bulk_disable_unprepare(arb->nclks, arb->clks);
clk_bulk_put(arb->nclks, arb->clks);
- spmi_controller_remove(ctrl);
}

static const struct of_device_id mtk_spmi_match_table[] = {
--
https://git.kernel.org/pub/scm/linux/kernel/git/clk/linux.git/
https://git.kernel.org/pub/scm/linux/kernel/git/sboyd/spmi.git

2023-12-15 16:13:06

by Greg Kroah-Hartman

[permalink] [raw]
Subject: Re: [PATCH 1/8] spmi: mtk-pmif: Serialize PMIF status check and command submission

On Wed, Dec 06, 2023 at 03:17:24PM -0800, Stephen Boyd wrote:
> From: N?colas F. R. A. Prado <[email protected]>
>
> Before writing the read or write command to the SPMI arbiter through the
> PMIF interface, the current status of the channel is checked to ensure
> it is idle. However, since the status only changes from idle when the
> command is written, it is possible for two concurrent calls to determine
> that the channel is idle and simultaneously send their commands. At this
> point the PMIF interface hangs, with the status register no longer being
> updated, and thus causing all subsequent operations to time out.
>
> This was observed on the mt8195-cherry-tomato-r2 machine, particularly
> after commit 46600ab142f8 ("regulator: Set PROBE_PREFER_ASYNCHRONOUS for
> drivers between 5.10 and 5.15") was applied, since then the two MT6315
> devices present on the SPMI bus would probe assynchronously and
> sometimes (during probe or at a later point) read the bus
> simultaneously, breaking the PMIF interface and consequently slowing
> down the whole system.
>
> To fix the issue at its root cause, introduce locking around the channel
> status check and the command write, so that both become an atomic
> operation, preventing race conditions between two (or more) SPMI bus
> read/write operations. A spinlock is used since this is a fast bus, as
> indicated by the usage of the atomic variant of readl_poll, and
> '.fast_io = true' being used in the mt6315 driver, so spinlocks are
> already used for the regmap access.
>
> Fixes: b45b3ccef8c0 ("spmi: mediatek: Add support for MT6873/8192")
> Signed-off-by: N?colas F. R. A. Prado <[email protected]>
> Link: https://lore.kernel.org/r/[email protected]
> Reviewed-by: AngeloGioacchino Del Regno <[email protected]>
> Reviewed-by: Alexandre Mergnat <[email protected]>
> Signed-off-by: Stephen Boyd <[email protected]>
> ---
> drivers/spmi/spmi-mtk-pmif.c | 20 ++++++++++++++++----
> 1 file changed, 16 insertions(+), 4 deletions(-)

Hi,

This is the friendly patch-bot of Greg Kroah-Hartman. You have sent him
a patch that has triggered this response. He used to manually respond
to these common problems, but in order to save his sanity (he kept
writing the same thing over and over, yet to different people), I was
created. Hopefully you will not take offence and will fix the problem
in your patch and resubmit it so that it can be accepted into the Linux
kernel tree.

You are receiving this message because of the following common error(s)
as indicated below:

- You have marked a patch with a "Fixes:" tag for a commit that is in an
older released kernel, yet you do not have a cc: stable line in the
signed-off-by area at all, which means that the patch will not be
applied to any older kernel releases. To properly fix this, please
follow the documented rules in the
Documentation/process/stable-kernel-rules.rst file for how to resolve
this.

If you wish to discuss this problem further, or you have questions about
how to resolve this issue, please feel free to respond to this email and
Greg will reply once he has dug out from the pending patches received
from other developers.

thanks,

greg k-h's patch email bot