2021-12-16 12:08:10 -07:00
|
|
|
// SPDX-License-Identifier: GPL-2.0
|
|
|
|
//
|
|
|
|
// Copyright (c) 2021 MediaTek Inc.
|
|
|
|
|
|
|
|
#include <linux/clk.h>
|
|
|
|
#include <linux/iopoll.h>
|
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/of.h>
|
|
|
|
#include <linux/platform_device.h>
|
|
|
|
#include <linux/property.h>
|
|
|
|
#include <linux/spmi.h>
|
|
|
|
|
|
|
|
#define SWINF_IDLE 0x00
|
|
|
|
#define SWINF_WFVLDCLR 0x06
|
|
|
|
|
|
|
|
#define GET_SWINF(x) (((x) >> 1) & 0x7)
|
|
|
|
|
|
|
|
#define PMIF_CMD_REG_0 0
|
|
|
|
#define PMIF_CMD_REG 1
|
|
|
|
#define PMIF_CMD_EXT_REG 2
|
|
|
|
#define PMIF_CMD_EXT_REG_LONG 3
|
|
|
|
|
|
|
|
#define PMIF_DELAY_US 10
|
|
|
|
#define PMIF_TIMEOUT_US (10 * 1000)
|
|
|
|
|
|
|
|
#define PMIF_CHAN_OFFSET 0x5
|
|
|
|
|
|
|
|
#define PMIF_MAX_CLKS 3
|
|
|
|
|
|
|
|
#define SPMI_OP_ST_BUSY 1
|
|
|
|
|
|
|
|
struct ch_reg {
|
|
|
|
u32 ch_sta;
|
|
|
|
u32 wdata;
|
|
|
|
u32 rdata;
|
|
|
|
u32 ch_send;
|
|
|
|
u32 ch_rdy;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct pmif_data {
|
|
|
|
const u32 *regs;
|
|
|
|
const u32 *spmimst_regs;
|
|
|
|
u32 soc_chan;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct pmif {
|
|
|
|
void __iomem *base;
|
|
|
|
void __iomem *spmimst_base;
|
|
|
|
struct ch_reg chan;
|
|
|
|
struct clk_bulk_data clks[PMIF_MAX_CLKS];
|
|
|
|
size_t nclks;
|
|
|
|
const struct pmif_data *data;
|
spmi: mtk-pmif: Serialize PMIF status check and command submission
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 <nfraprado@collabora.com>
Link: https://lore.kernel.org/r/20230724154739.493724-1-nfraprado@collabora.com
Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
Reviewed-by: Alexandre Mergnat <amergnat@baylibre.com>
Signed-off-by: Stephen Boyd <sboyd@kernel.org>
Link: https://lore.kernel.org/r/20231206231733.4031901-2-sboyd@kernel.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2023-12-06 16:17:24 -07:00
|
|
|
raw_spinlock_t lock;
|
2021-12-16 12:08:10 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
static const char * const pmif_clock_names[] = {
|
|
|
|
"pmif_sys_ck", "pmif_tmr_ck", "spmimst_clk_mux",
|
|
|
|
};
|
|
|
|
|
|
|
|
enum pmif_regs {
|
|
|
|
PMIF_INIT_DONE,
|
|
|
|
PMIF_INF_EN,
|
|
|
|
PMIF_ARB_EN,
|
|
|
|
PMIF_CMDISSUE_EN,
|
|
|
|
PMIF_TIMER_CTRL,
|
|
|
|
PMIF_SPI_MODE_CTRL,
|
|
|
|
PMIF_IRQ_EVENT_EN_0,
|
|
|
|
PMIF_IRQ_FLAG_0,
|
|
|
|
PMIF_IRQ_CLR_0,
|
|
|
|
PMIF_IRQ_EVENT_EN_1,
|
|
|
|
PMIF_IRQ_FLAG_1,
|
|
|
|
PMIF_IRQ_CLR_1,
|
|
|
|
PMIF_IRQ_EVENT_EN_2,
|
|
|
|
PMIF_IRQ_FLAG_2,
|
|
|
|
PMIF_IRQ_CLR_2,
|
|
|
|
PMIF_IRQ_EVENT_EN_3,
|
|
|
|
PMIF_IRQ_FLAG_3,
|
|
|
|
PMIF_IRQ_CLR_3,
|
|
|
|
PMIF_IRQ_EVENT_EN_4,
|
|
|
|
PMIF_IRQ_FLAG_4,
|
|
|
|
PMIF_IRQ_CLR_4,
|
|
|
|
PMIF_WDT_EVENT_EN_0,
|
|
|
|
PMIF_WDT_FLAG_0,
|
|
|
|
PMIF_WDT_EVENT_EN_1,
|
|
|
|
PMIF_WDT_FLAG_1,
|
|
|
|
PMIF_SWINF_0_STA,
|
|
|
|
PMIF_SWINF_0_WDATA_31_0,
|
|
|
|
PMIF_SWINF_0_RDATA_31_0,
|
|
|
|
PMIF_SWINF_0_ACC,
|
|
|
|
PMIF_SWINF_0_VLD_CLR,
|
|
|
|
PMIF_SWINF_1_STA,
|
|
|
|
PMIF_SWINF_1_WDATA_31_0,
|
|
|
|
PMIF_SWINF_1_RDATA_31_0,
|
|
|
|
PMIF_SWINF_1_ACC,
|
|
|
|
PMIF_SWINF_1_VLD_CLR,
|
|
|
|
PMIF_SWINF_2_STA,
|
|
|
|
PMIF_SWINF_2_WDATA_31_0,
|
|
|
|
PMIF_SWINF_2_RDATA_31_0,
|
|
|
|
PMIF_SWINF_2_ACC,
|
|
|
|
PMIF_SWINF_2_VLD_CLR,
|
|
|
|
PMIF_SWINF_3_STA,
|
|
|
|
PMIF_SWINF_3_WDATA_31_0,
|
|
|
|
PMIF_SWINF_3_RDATA_31_0,
|
|
|
|
PMIF_SWINF_3_ACC,
|
|
|
|
PMIF_SWINF_3_VLD_CLR,
|
|
|
|
};
|
|
|
|
|
|
|
|
static const u32 mt6873_regs[] = {
|
|
|
|
[PMIF_INIT_DONE] = 0x0000,
|
|
|
|
[PMIF_INF_EN] = 0x0024,
|
|
|
|
[PMIF_ARB_EN] = 0x0150,
|
|
|
|
[PMIF_CMDISSUE_EN] = 0x03B4,
|
|
|
|
[PMIF_TIMER_CTRL] = 0x03E0,
|
|
|
|
[PMIF_SPI_MODE_CTRL] = 0x0400,
|
|
|
|
[PMIF_IRQ_EVENT_EN_0] = 0x0418,
|
|
|
|
[PMIF_IRQ_FLAG_0] = 0x0420,
|
|
|
|
[PMIF_IRQ_CLR_0] = 0x0424,
|
|
|
|
[PMIF_IRQ_EVENT_EN_1] = 0x0428,
|
|
|
|
[PMIF_IRQ_FLAG_1] = 0x0430,
|
|
|
|
[PMIF_IRQ_CLR_1] = 0x0434,
|
|
|
|
[PMIF_IRQ_EVENT_EN_2] = 0x0438,
|
|
|
|
[PMIF_IRQ_FLAG_2] = 0x0440,
|
|
|
|
[PMIF_IRQ_CLR_2] = 0x0444,
|
|
|
|
[PMIF_IRQ_EVENT_EN_3] = 0x0448,
|
|
|
|
[PMIF_IRQ_FLAG_3] = 0x0450,
|
|
|
|
[PMIF_IRQ_CLR_3] = 0x0454,
|
|
|
|
[PMIF_IRQ_EVENT_EN_4] = 0x0458,
|
|
|
|
[PMIF_IRQ_FLAG_4] = 0x0460,
|
|
|
|
[PMIF_IRQ_CLR_4] = 0x0464,
|
|
|
|
[PMIF_WDT_EVENT_EN_0] = 0x046C,
|
|
|
|
[PMIF_WDT_FLAG_0] = 0x0470,
|
|
|
|
[PMIF_WDT_EVENT_EN_1] = 0x0474,
|
|
|
|
[PMIF_WDT_FLAG_1] = 0x0478,
|
|
|
|
[PMIF_SWINF_0_ACC] = 0x0C00,
|
|
|
|
[PMIF_SWINF_0_WDATA_31_0] = 0x0C04,
|
|
|
|
[PMIF_SWINF_0_RDATA_31_0] = 0x0C14,
|
|
|
|
[PMIF_SWINF_0_VLD_CLR] = 0x0C24,
|
|
|
|
[PMIF_SWINF_0_STA] = 0x0C28,
|
|
|
|
[PMIF_SWINF_1_ACC] = 0x0C40,
|
|
|
|
[PMIF_SWINF_1_WDATA_31_0] = 0x0C44,
|
|
|
|
[PMIF_SWINF_1_RDATA_31_0] = 0x0C54,
|
|
|
|
[PMIF_SWINF_1_VLD_CLR] = 0x0C64,
|
|
|
|
[PMIF_SWINF_1_STA] = 0x0C68,
|
|
|
|
[PMIF_SWINF_2_ACC] = 0x0C80,
|
|
|
|
[PMIF_SWINF_2_WDATA_31_0] = 0x0C84,
|
|
|
|
[PMIF_SWINF_2_RDATA_31_0] = 0x0C94,
|
|
|
|
[PMIF_SWINF_2_VLD_CLR] = 0x0CA4,
|
|
|
|
[PMIF_SWINF_2_STA] = 0x0CA8,
|
|
|
|
[PMIF_SWINF_3_ACC] = 0x0CC0,
|
|
|
|
[PMIF_SWINF_3_WDATA_31_0] = 0x0CC4,
|
|
|
|
[PMIF_SWINF_3_RDATA_31_0] = 0x0CD4,
|
|
|
|
[PMIF_SWINF_3_VLD_CLR] = 0x0CE4,
|
|
|
|
[PMIF_SWINF_3_STA] = 0x0CE8,
|
|
|
|
};
|
|
|
|
|
2021-12-16 12:08:11 -07:00
|
|
|
static const u32 mt8195_regs[] = {
|
|
|
|
[PMIF_INIT_DONE] = 0x0000,
|
|
|
|
[PMIF_INF_EN] = 0x0024,
|
|
|
|
[PMIF_ARB_EN] = 0x0150,
|
|
|
|
[PMIF_CMDISSUE_EN] = 0x03B8,
|
|
|
|
[PMIF_TIMER_CTRL] = 0x03E4,
|
|
|
|
[PMIF_SPI_MODE_CTRL] = 0x0408,
|
|
|
|
[PMIF_IRQ_EVENT_EN_0] = 0x0420,
|
|
|
|
[PMIF_IRQ_FLAG_0] = 0x0428,
|
|
|
|
[PMIF_IRQ_CLR_0] = 0x042C,
|
|
|
|
[PMIF_IRQ_EVENT_EN_1] = 0x0430,
|
|
|
|
[PMIF_IRQ_FLAG_1] = 0x0438,
|
|
|
|
[PMIF_IRQ_CLR_1] = 0x043C,
|
|
|
|
[PMIF_IRQ_EVENT_EN_2] = 0x0440,
|
|
|
|
[PMIF_IRQ_FLAG_2] = 0x0448,
|
|
|
|
[PMIF_IRQ_CLR_2] = 0x044C,
|
|
|
|
[PMIF_IRQ_EVENT_EN_3] = 0x0450,
|
|
|
|
[PMIF_IRQ_FLAG_3] = 0x0458,
|
|
|
|
[PMIF_IRQ_CLR_3] = 0x045C,
|
|
|
|
[PMIF_IRQ_EVENT_EN_4] = 0x0460,
|
|
|
|
[PMIF_IRQ_FLAG_4] = 0x0468,
|
|
|
|
[PMIF_IRQ_CLR_4] = 0x046C,
|
|
|
|
[PMIF_WDT_EVENT_EN_0] = 0x0474,
|
|
|
|
[PMIF_WDT_FLAG_0] = 0x0478,
|
|
|
|
[PMIF_WDT_EVENT_EN_1] = 0x047C,
|
|
|
|
[PMIF_WDT_FLAG_1] = 0x0480,
|
|
|
|
[PMIF_SWINF_0_ACC] = 0x0800,
|
|
|
|
[PMIF_SWINF_0_WDATA_31_0] = 0x0804,
|
|
|
|
[PMIF_SWINF_0_RDATA_31_0] = 0x0814,
|
|
|
|
[PMIF_SWINF_0_VLD_CLR] = 0x0824,
|
|
|
|
[PMIF_SWINF_0_STA] = 0x0828,
|
|
|
|
[PMIF_SWINF_1_ACC] = 0x0840,
|
|
|
|
[PMIF_SWINF_1_WDATA_31_0] = 0x0844,
|
|
|
|
[PMIF_SWINF_1_RDATA_31_0] = 0x0854,
|
|
|
|
[PMIF_SWINF_1_VLD_CLR] = 0x0864,
|
|
|
|
[PMIF_SWINF_1_STA] = 0x0868,
|
|
|
|
[PMIF_SWINF_2_ACC] = 0x0880,
|
|
|
|
[PMIF_SWINF_2_WDATA_31_0] = 0x0884,
|
|
|
|
[PMIF_SWINF_2_RDATA_31_0] = 0x0894,
|
|
|
|
[PMIF_SWINF_2_VLD_CLR] = 0x08A4,
|
|
|
|
[PMIF_SWINF_2_STA] = 0x08A8,
|
|
|
|
[PMIF_SWINF_3_ACC] = 0x08C0,
|
|
|
|
[PMIF_SWINF_3_WDATA_31_0] = 0x08C4,
|
|
|
|
[PMIF_SWINF_3_RDATA_31_0] = 0x08D4,
|
|
|
|
[PMIF_SWINF_3_VLD_CLR] = 0x08E4,
|
|
|
|
[PMIF_SWINF_3_STA] = 0x08E8,
|
|
|
|
};
|
|
|
|
|
2021-12-16 12:08:10 -07:00
|
|
|
enum spmi_regs {
|
|
|
|
SPMI_OP_ST_CTRL,
|
|
|
|
SPMI_GRP_ID_EN,
|
|
|
|
SPMI_OP_ST_STA,
|
|
|
|
SPMI_MST_SAMPL,
|
|
|
|
SPMI_MST_REQ_EN,
|
|
|
|
SPMI_REC_CTRL,
|
|
|
|
SPMI_REC0,
|
|
|
|
SPMI_REC1,
|
|
|
|
SPMI_REC2,
|
|
|
|
SPMI_REC3,
|
|
|
|
SPMI_REC4,
|
|
|
|
SPMI_MST_DBG,
|
2021-12-16 12:08:11 -07:00
|
|
|
|
|
|
|
/* MT8195 spmi regs */
|
|
|
|
SPMI_MST_RCS_CTRL,
|
|
|
|
SPMI_SLV_3_0_EINT,
|
|
|
|
SPMI_SLV_7_4_EINT,
|
|
|
|
SPMI_SLV_B_8_EINT,
|
|
|
|
SPMI_SLV_F_C_EINT,
|
|
|
|
SPMI_REC_CMD_DEC,
|
|
|
|
SPMI_DEC_DBG,
|
2021-12-16 12:08:10 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
static const u32 mt6873_spmi_regs[] = {
|
|
|
|
[SPMI_OP_ST_CTRL] = 0x0000,
|
|
|
|
[SPMI_GRP_ID_EN] = 0x0004,
|
|
|
|
[SPMI_OP_ST_STA] = 0x0008,
|
|
|
|
[SPMI_MST_SAMPL] = 0x000c,
|
|
|
|
[SPMI_MST_REQ_EN] = 0x0010,
|
|
|
|
[SPMI_REC_CTRL] = 0x0040,
|
|
|
|
[SPMI_REC0] = 0x0044,
|
|
|
|
[SPMI_REC1] = 0x0048,
|
|
|
|
[SPMI_REC2] = 0x004c,
|
|
|
|
[SPMI_REC3] = 0x0050,
|
|
|
|
[SPMI_REC4] = 0x0054,
|
|
|
|
[SPMI_MST_DBG] = 0x00fc,
|
|
|
|
};
|
|
|
|
|
2021-12-16 12:08:11 -07:00
|
|
|
static const u32 mt8195_spmi_regs[] = {
|
|
|
|
[SPMI_OP_ST_CTRL] = 0x0000,
|
|
|
|
[SPMI_GRP_ID_EN] = 0x0004,
|
|
|
|
[SPMI_OP_ST_STA] = 0x0008,
|
|
|
|
[SPMI_MST_SAMPL] = 0x000C,
|
|
|
|
[SPMI_MST_REQ_EN] = 0x0010,
|
|
|
|
[SPMI_MST_RCS_CTRL] = 0x0014,
|
|
|
|
[SPMI_SLV_3_0_EINT] = 0x0020,
|
|
|
|
[SPMI_SLV_7_4_EINT] = 0x0024,
|
|
|
|
[SPMI_SLV_B_8_EINT] = 0x0028,
|
|
|
|
[SPMI_SLV_F_C_EINT] = 0x002C,
|
|
|
|
[SPMI_REC_CTRL] = 0x0040,
|
|
|
|
[SPMI_REC0] = 0x0044,
|
|
|
|
[SPMI_REC1] = 0x0048,
|
|
|
|
[SPMI_REC2] = 0x004C,
|
|
|
|
[SPMI_REC3] = 0x0050,
|
|
|
|
[SPMI_REC4] = 0x0054,
|
|
|
|
[SPMI_REC_CMD_DEC] = 0x005C,
|
|
|
|
[SPMI_DEC_DBG] = 0x00F8,
|
|
|
|
[SPMI_MST_DBG] = 0x00FC,
|
|
|
|
};
|
|
|
|
|
2021-12-16 12:08:10 -07:00
|
|
|
static u32 pmif_readl(struct pmif *arb, enum pmif_regs reg)
|
|
|
|
{
|
|
|
|
return readl(arb->base + arb->data->regs[reg]);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void pmif_writel(struct pmif *arb, u32 val, enum pmif_regs reg)
|
|
|
|
{
|
|
|
|
writel(val, arb->base + arb->data->regs[reg]);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void mtk_spmi_writel(struct pmif *arb, u32 val, enum spmi_regs reg)
|
|
|
|
{
|
|
|
|
writel(val, arb->spmimst_base + arb->data->spmimst_regs[reg]);
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool pmif_is_fsm_vldclr(struct pmif *arb)
|
|
|
|
{
|
|
|
|
u32 reg_rdata;
|
|
|
|
|
|
|
|
reg_rdata = pmif_readl(arb, arb->chan.ch_sta);
|
|
|
|
|
|
|
|
return GET_SWINF(reg_rdata) == SWINF_WFVLDCLR;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int pmif_arb_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid)
|
|
|
|
{
|
|
|
|
struct pmif *arb = spmi_controller_get_drvdata(ctrl);
|
|
|
|
u32 rdata, cmd;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
/* Check the opcode */
|
|
|
|
if (opc < SPMI_CMD_RESET || opc > SPMI_CMD_WAKEUP)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
cmd = opc - SPMI_CMD_RESET;
|
|
|
|
|
|
|
|
mtk_spmi_writel(arb, (cmd << 0x4) | sid, SPMI_OP_ST_CTRL);
|
|
|
|
ret = readl_poll_timeout_atomic(arb->spmimst_base + arb->data->spmimst_regs[SPMI_OP_ST_STA],
|
|
|
|
rdata, (rdata & SPMI_OP_ST_BUSY) == SPMI_OP_ST_BUSY,
|
|
|
|
PMIF_DELAY_US, PMIF_TIMEOUT_US);
|
|
|
|
if (ret < 0)
|
|
|
|
dev_err(&ctrl->dev, "timeout, err = %d\n", ret);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int pmif_spmi_read_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,
|
|
|
|
u16 addr, u8 *buf, size_t len)
|
|
|
|
{
|
|
|
|
struct pmif *arb = spmi_controller_get_drvdata(ctrl);
|
|
|
|
struct ch_reg *inf_reg;
|
|
|
|
int ret;
|
|
|
|
u32 data, cmd;
|
spmi: mtk-pmif: Serialize PMIF status check and command submission
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 <nfraprado@collabora.com>
Link: https://lore.kernel.org/r/20230724154739.493724-1-nfraprado@collabora.com
Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
Reviewed-by: Alexandre Mergnat <amergnat@baylibre.com>
Signed-off-by: Stephen Boyd <sboyd@kernel.org>
Link: https://lore.kernel.org/r/20231206231733.4031901-2-sboyd@kernel.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2023-12-06 16:17:24 -07:00
|
|
|
unsigned long flags;
|
2021-12-16 12:08:10 -07:00
|
|
|
|
|
|
|
/* Check for argument validation. */
|
|
|
|
if (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);
|
|
|
|
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (opc >= 0x60 && opc <= 0x7f)
|
|
|
|
opc = PMIF_CMD_REG;
|
|
|
|
else if ((opc >= 0x20 && opc <= 0x2f) || (opc >= 0x38 && opc <= 0x3f))
|
|
|
|
opc = PMIF_CMD_EXT_REG_LONG;
|
|
|
|
else
|
|
|
|
return -EINVAL;
|
|
|
|
|
spmi: mtk-pmif: Serialize PMIF status check and command submission
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 <nfraprado@collabora.com>
Link: https://lore.kernel.org/r/20230724154739.493724-1-nfraprado@collabora.com
Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
Reviewed-by: Alexandre Mergnat <amergnat@baylibre.com>
Signed-off-by: Stephen Boyd <sboyd@kernel.org>
Link: https://lore.kernel.org/r/20231206231733.4031901-2-sboyd@kernel.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2023-12-06 16:17:24 -07:00
|
|
|
raw_spin_lock_irqsave(&arb->lock, flags);
|
2021-12-16 12:08:10 -07:00
|
|
|
/* 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],
|
|
|
|
data, GET_SWINF(data) == SWINF_IDLE,
|
|
|
|
PMIF_DELAY_US, PMIF_TIMEOUT_US);
|
|
|
|
if (ret < 0) {
|
|
|
|
/* set channel ready if the data has transferred */
|
|
|
|
if (pmif_is_fsm_vldclr(arb))
|
|
|
|
pmif_writel(arb, 1, inf_reg->ch_rdy);
|
spmi: mtk-pmif: Serialize PMIF status check and command submission
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 <nfraprado@collabora.com>
Link: https://lore.kernel.org/r/20230724154739.493724-1-nfraprado@collabora.com
Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
Reviewed-by: Alexandre Mergnat <amergnat@baylibre.com>
Signed-off-by: Stephen Boyd <sboyd@kernel.org>
Link: https://lore.kernel.org/r/20231206231733.4031901-2-sboyd@kernel.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2023-12-06 16:17:24 -07:00
|
|
|
raw_spin_unlock_irqrestore(&arb->lock, flags);
|
2021-12-16 12:08:10 -07:00
|
|
|
dev_err(&ctrl->dev, "failed to wait for SWINF_IDLE\n");
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Send the command. */
|
|
|
|
cmd = (opc << 30) | (sid << 24) | ((len - 1) << 16) | addr;
|
|
|
|
pmif_writel(arb, cmd, inf_reg->ch_send);
|
spmi: mtk-pmif: Serialize PMIF status check and command submission
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 <nfraprado@collabora.com>
Link: https://lore.kernel.org/r/20230724154739.493724-1-nfraprado@collabora.com
Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
Reviewed-by: Alexandre Mergnat <amergnat@baylibre.com>
Signed-off-by: Stephen Boyd <sboyd@kernel.org>
Link: https://lore.kernel.org/r/20231206231733.4031901-2-sboyd@kernel.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2023-12-06 16:17:24 -07:00
|
|
|
raw_spin_unlock_irqrestore(&arb->lock, flags);
|
2021-12-16 12:08:10 -07:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Wait for Software Interface FSM state to be WFVLDCLR,
|
|
|
|
* read the data and clear the valid flag.
|
|
|
|
*/
|
|
|
|
ret = readl_poll_timeout_atomic(arb->base + arb->data->regs[inf_reg->ch_sta],
|
|
|
|
data, GET_SWINF(data) == SWINF_WFVLDCLR,
|
|
|
|
PMIF_DELAY_US, PMIF_TIMEOUT_US);
|
|
|
|
if (ret < 0) {
|
|
|
|
dev_err(&ctrl->dev, "failed to wait for SWINF_WFVLDCLR\n");
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
data = pmif_readl(arb, inf_reg->rdata);
|
|
|
|
memcpy(buf, &data, len);
|
|
|
|
pmif_writel(arb, 1, inf_reg->ch_rdy);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int pmif_spmi_write_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,
|
|
|
|
u16 addr, const u8 *buf, size_t len)
|
|
|
|
{
|
|
|
|
struct pmif *arb = spmi_controller_get_drvdata(ctrl);
|
|
|
|
struct ch_reg *inf_reg;
|
|
|
|
int ret;
|
spmi: mtk-pmif: Serialize PMIF status check and command submission
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 <nfraprado@collabora.com>
Link: https://lore.kernel.org/r/20230724154739.493724-1-nfraprado@collabora.com
Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
Reviewed-by: Alexandre Mergnat <amergnat@baylibre.com>
Signed-off-by: Stephen Boyd <sboyd@kernel.org>
Link: https://lore.kernel.org/r/20231206231733.4031901-2-sboyd@kernel.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2023-12-06 16:17:24 -07:00
|
|
|
u32 data, wdata, cmd;
|
|
|
|
unsigned long flags;
|
2021-12-16 12:08:10 -07:00
|
|
|
|
2023-12-06 16:17:31 -07:00
|
|
|
/* Check for argument validation. */
|
|
|
|
if (unlikely(sid & ~0xf)) {
|
|
|
|
dev_err(&ctrl->dev, "exceed the max slv id\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2021-12-16 12:08:10 -07:00
|
|
|
if (len > 4) {
|
|
|
|
dev_err(&ctrl->dev, "pmif supports 1..4 bytes per trans, but:%zu requested", len);
|
|
|
|
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check the opcode */
|
|
|
|
if (opc >= 0x40 && opc <= 0x5F)
|
|
|
|
opc = PMIF_CMD_REG;
|
|
|
|
else if ((opc <= 0xF) || (opc >= 0x30 && opc <= 0x37))
|
|
|
|
opc = PMIF_CMD_EXT_REG_LONG;
|
|
|
|
else if (opc >= 0x80)
|
|
|
|
opc = PMIF_CMD_REG_0;
|
|
|
|
else
|
|
|
|
return -EINVAL;
|
|
|
|
|
spmi: mtk-pmif: Serialize PMIF status check and command submission
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 <nfraprado@collabora.com>
Link: https://lore.kernel.org/r/20230724154739.493724-1-nfraprado@collabora.com
Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
Reviewed-by: Alexandre Mergnat <amergnat@baylibre.com>
Signed-off-by: Stephen Boyd <sboyd@kernel.org>
Link: https://lore.kernel.org/r/20231206231733.4031901-2-sboyd@kernel.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2023-12-06 16:17:24 -07:00
|
|
|
/* Set the write data. */
|
|
|
|
memcpy(&wdata, buf, len);
|
|
|
|
|
|
|
|
raw_spin_lock_irqsave(&arb->lock, flags);
|
2021-12-16 12:08:10 -07:00
|
|
|
/* 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],
|
|
|
|
data, GET_SWINF(data) == SWINF_IDLE,
|
|
|
|
PMIF_DELAY_US, PMIF_TIMEOUT_US);
|
|
|
|
if (ret < 0) {
|
|
|
|
/* set channel ready if the data has transferred */
|
|
|
|
if (pmif_is_fsm_vldclr(arb))
|
|
|
|
pmif_writel(arb, 1, inf_reg->ch_rdy);
|
spmi: mtk-pmif: Serialize PMIF status check and command submission
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 <nfraprado@collabora.com>
Link: https://lore.kernel.org/r/20230724154739.493724-1-nfraprado@collabora.com
Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
Reviewed-by: Alexandre Mergnat <amergnat@baylibre.com>
Signed-off-by: Stephen Boyd <sboyd@kernel.org>
Link: https://lore.kernel.org/r/20231206231733.4031901-2-sboyd@kernel.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2023-12-06 16:17:24 -07:00
|
|
|
raw_spin_unlock_irqrestore(&arb->lock, flags);
|
2021-12-16 12:08:10 -07:00
|
|
|
dev_err(&ctrl->dev, "failed to wait for SWINF_IDLE\n");
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
spmi: mtk-pmif: Serialize PMIF status check and command submission
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 <nfraprado@collabora.com>
Link: https://lore.kernel.org/r/20230724154739.493724-1-nfraprado@collabora.com
Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
Reviewed-by: Alexandre Mergnat <amergnat@baylibre.com>
Signed-off-by: Stephen Boyd <sboyd@kernel.org>
Link: https://lore.kernel.org/r/20231206231733.4031901-2-sboyd@kernel.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2023-12-06 16:17:24 -07:00
|
|
|
pmif_writel(arb, wdata, inf_reg->wdata);
|
2021-12-16 12:08:10 -07:00
|
|
|
|
|
|
|
/* Send the command. */
|
|
|
|
cmd = (opc << 30) | BIT(29) | (sid << 24) | ((len - 1) << 16) | addr;
|
|
|
|
pmif_writel(arb, cmd, inf_reg->ch_send);
|
spmi: mtk-pmif: Serialize PMIF status check and command submission
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 <nfraprado@collabora.com>
Link: https://lore.kernel.org/r/20230724154739.493724-1-nfraprado@collabora.com
Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
Reviewed-by: Alexandre Mergnat <amergnat@baylibre.com>
Signed-off-by: Stephen Boyd <sboyd@kernel.org>
Link: https://lore.kernel.org/r/20231206231733.4031901-2-sboyd@kernel.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2023-12-06 16:17:24 -07:00
|
|
|
raw_spin_unlock_irqrestore(&arb->lock, flags);
|
2021-12-16 12:08:10 -07:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct pmif_data mt6873_pmif_arb = {
|
|
|
|
.regs = mt6873_regs,
|
|
|
|
.spmimst_regs = mt6873_spmi_regs,
|
|
|
|
.soc_chan = 2,
|
|
|
|
};
|
|
|
|
|
2021-12-16 12:08:11 -07:00
|
|
|
static const struct pmif_data mt8195_pmif_arb = {
|
|
|
|
.regs = mt8195_regs,
|
|
|
|
.spmimst_regs = mt8195_spmi_regs,
|
|
|
|
.soc_chan = 2,
|
|
|
|
};
|
|
|
|
|
2021-12-16 12:08:10 -07:00
|
|
|
static int mtk_spmi_probe(struct platform_device *pdev)
|
|
|
|
{
|
|
|
|
struct pmif *arb;
|
|
|
|
struct spmi_controller *ctrl;
|
|
|
|
int err, i;
|
|
|
|
u32 chan_offset;
|
|
|
|
|
2023-12-06 16:17:27 -07:00
|
|
|
ctrl = devm_spmi_controller_alloc(&pdev->dev, sizeof(*arb));
|
|
|
|
if (IS_ERR(ctrl))
|
|
|
|
return PTR_ERR(ctrl);
|
2021-12-16 12:08:10 -07:00
|
|
|
|
|
|
|
arb = spmi_controller_get_drvdata(ctrl);
|
|
|
|
arb->data = device_get_match_data(&pdev->dev);
|
|
|
|
if (!arb->data) {
|
|
|
|
dev_err(&pdev->dev, "Cannot get drv_data\n");
|
2023-12-06 16:17:27 -07:00
|
|
|
return -EINVAL;
|
2021-12-16 12:08:10 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
arb->base = devm_platform_ioremap_resource_byname(pdev, "pmif");
|
2023-12-06 16:17:27 -07:00
|
|
|
if (IS_ERR(arb->base))
|
|
|
|
return PTR_ERR(arb->base);
|
2021-12-16 12:08:10 -07:00
|
|
|
|
|
|
|
arb->spmimst_base = devm_platform_ioremap_resource_byname(pdev, "spmimst");
|
2023-12-06 16:17:27 -07:00
|
|
|
if (IS_ERR(arb->spmimst_base))
|
|
|
|
return PTR_ERR(arb->spmimst_base);
|
2021-12-16 12:08:10 -07:00
|
|
|
|
|
|
|
arb->nclks = ARRAY_SIZE(pmif_clock_names);
|
|
|
|
for (i = 0; i < arb->nclks; i++)
|
|
|
|
arb->clks[i].id = pmif_clock_names[i];
|
|
|
|
|
2023-12-06 16:17:25 -07:00
|
|
|
err = clk_bulk_get(&pdev->dev, arb->nclks, arb->clks);
|
2021-12-16 12:08:10 -07:00
|
|
|
if (err) {
|
|
|
|
dev_err(&pdev->dev, "Failed to get clocks: %d\n", err);
|
2023-12-06 16:17:27 -07:00
|
|
|
return err;
|
2021-12-16 12:08:10 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
err = clk_bulk_prepare_enable(arb->nclks, arb->clks);
|
|
|
|
if (err) {
|
|
|
|
dev_err(&pdev->dev, "Failed to enable clocks: %d\n", err);
|
2023-12-06 16:17:25 -07:00
|
|
|
goto err_put_clks;
|
2021-12-16 12:08:10 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
ctrl->cmd = pmif_arb_cmd;
|
|
|
|
ctrl->read_cmd = pmif_spmi_read_cmd;
|
|
|
|
ctrl->write_cmd = pmif_spmi_write_cmd;
|
|
|
|
|
|
|
|
chan_offset = PMIF_CHAN_OFFSET * arb->data->soc_chan;
|
|
|
|
arb->chan.ch_sta = PMIF_SWINF_0_STA + chan_offset;
|
|
|
|
arb->chan.wdata = PMIF_SWINF_0_WDATA_31_0 + chan_offset;
|
|
|
|
arb->chan.rdata = PMIF_SWINF_0_RDATA_31_0 + chan_offset;
|
|
|
|
arb->chan.ch_send = PMIF_SWINF_0_ACC + chan_offset;
|
|
|
|
arb->chan.ch_rdy = PMIF_SWINF_0_VLD_CLR + chan_offset;
|
|
|
|
|
spmi: mtk-pmif: Serialize PMIF status check and command submission
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 <nfraprado@collabora.com>
Link: https://lore.kernel.org/r/20230724154739.493724-1-nfraprado@collabora.com
Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
Reviewed-by: Alexandre Mergnat <amergnat@baylibre.com>
Signed-off-by: Stephen Boyd <sboyd@kernel.org>
Link: https://lore.kernel.org/r/20231206231733.4031901-2-sboyd@kernel.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2023-12-06 16:17:24 -07:00
|
|
|
raw_spin_lock_init(&arb->lock);
|
|
|
|
|
2021-12-16 12:08:10 -07:00
|
|
|
platform_set_drvdata(pdev, ctrl);
|
|
|
|
|
|
|
|
err = spmi_controller_add(ctrl);
|
|
|
|
if (err)
|
|
|
|
goto err_domain_remove;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
err_domain_remove:
|
|
|
|
clk_bulk_disable_unprepare(arb->nclks, arb->clks);
|
2023-12-06 16:17:25 -07:00
|
|
|
err_put_clks:
|
|
|
|
clk_bulk_put(arb->nclks, arb->clks);
|
2021-12-16 12:08:10 -07:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2023-04-13 15:38:30 -07:00
|
|
|
static void mtk_spmi_remove(struct platform_device *pdev)
|
2021-12-16 12:08:10 -07:00
|
|
|
{
|
|
|
|
struct spmi_controller *ctrl = platform_get_drvdata(pdev);
|
|
|
|
struct pmif *arb = spmi_controller_get_drvdata(ctrl);
|
|
|
|
|
2023-12-06 16:17:28 -07:00
|
|
|
spmi_controller_remove(ctrl);
|
2021-12-16 12:08:10 -07:00
|
|
|
clk_bulk_disable_unprepare(arb->nclks, arb->clks);
|
2023-12-06 16:17:25 -07:00
|
|
|
clk_bulk_put(arb->nclks, arb->clks);
|
2021-12-16 12:08:10 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
static const struct of_device_id mtk_spmi_match_table[] = {
|
|
|
|
{
|
|
|
|
.compatible = "mediatek,mt6873-spmi",
|
|
|
|
.data = &mt6873_pmif_arb,
|
2021-12-16 12:08:11 -07:00
|
|
|
}, {
|
|
|
|
.compatible = "mediatek,mt8195-spmi",
|
|
|
|
.data = &mt8195_pmif_arb,
|
2021-12-16 12:08:10 -07:00
|
|
|
}, {
|
|
|
|
/* sentinel */
|
|
|
|
},
|
|
|
|
};
|
|
|
|
MODULE_DEVICE_TABLE(of, mtk_spmi_match_table);
|
|
|
|
|
|
|
|
static struct platform_driver mtk_spmi_driver = {
|
|
|
|
.driver = {
|
|
|
|
.name = "spmi-mtk",
|
2023-04-13 15:38:32 -07:00
|
|
|
.of_match_table = mtk_spmi_match_table,
|
2021-12-16 12:08:10 -07:00
|
|
|
},
|
|
|
|
.probe = mtk_spmi_probe,
|
2023-04-13 15:38:30 -07:00
|
|
|
.remove_new = mtk_spmi_remove,
|
2021-12-16 12:08:10 -07:00
|
|
|
};
|
|
|
|
module_platform_driver(mtk_spmi_driver);
|
|
|
|
|
|
|
|
MODULE_AUTHOR("Hsin-Hsiung Wang <hsin-hsiung.wang@mediatek.com>");
|
|
|
|
MODULE_DESCRIPTION("MediaTek SPMI Driver");
|
|
|
|
MODULE_LICENSE("GPL");
|