* Re: [PATCH v6 39/40] arm_mpam: Quirk CMN-650's CSU NRDY behaviour
From: Gavin Shan @ 2026-03-24 4:21 UTC (permalink / raw)
To: Ben Horgan
Cc: amitsinght, baisheng.gao, baolin.wang, carl, dave.martin, david,
dfustini, fenghuay, james.morse, jonathan.cameron, kobak,
lcherian, linux-arm-kernel, linux-kernel, peternewman,
punit.agrawal, quic_jiles, reinette.chatre, rohit.mathew, scott,
sdonthineni, tan.shaopeng, xhao, catalin.marinas, will, corbet,
maz, oupton, joey.gouly, suzuki.poulose, kvmarm, zengheng4,
linux-doc
In-Reply-To: <20260313144617.3420416-40-ben.horgan@arm.com>
On 3/14/26 12:46 AM, Ben Horgan wrote:
> From: James Morse <james.morse@arm.com>
>
> CMN-650 is afflicted with an erratum where the CSU NRDY bit never clears.
> This tells us the monitor never finishes scanning the cache. The erratum
> document says to wait the maximum time, then ignore the field.
>
> Add a flag to indicate whether this is the final attempt to read the
> counter, and when this quirk is applied, ignore the NRDY field.
>
> This means accesses to this counter will always retry, even if the counter
> was previously programmed to the same values.
>
> The counter value is not expected to be stable, it drifts up and down with
> each allocation and eviction. The CSU register provides the value for a
> point in time.
>
> Reviewed-by: Zeng Heng <zengheng4@huawei.com>
> Signed-off-by: James Morse <james.morse@arm.com>
> Signed-off-by: Ben Horgan <ben.horgan@arm.com>
> ---
> Changes since v3:
> parentheses in macro
> ---
> Documentation/arch/arm64/silicon-errata.rst | 3 +++
> drivers/resctrl/mpam_devices.c | 12 ++++++++++++
> drivers/resctrl/mpam_internal.h | 6 ++++++
> 3 files changed, 21 insertions(+)
>
Reviewed-by: Gavin Shan <gshan@redhat.com>
^ permalink raw reply
* Re: [PATCH v6 38/40] arm_mpam: Add workaround for T241-MPAM-6
From: Gavin Shan @ 2026-03-24 4:20 UTC (permalink / raw)
To: Ben Horgan
Cc: amitsinght, baisheng.gao, baolin.wang, carl, dave.martin, david,
dfustini, fenghuay, james.morse, jonathan.cameron, kobak,
lcherian, linux-arm-kernel, linux-kernel, peternewman,
punit.agrawal, quic_jiles, reinette.chatre, rohit.mathew, scott,
sdonthineni, tan.shaopeng, xhao, catalin.marinas, will, corbet,
maz, oupton, joey.gouly, suzuki.poulose, kvmarm, zengheng4,
linux-doc, Shaopeng Tan
In-Reply-To: <20260313144617.3420416-39-ben.horgan@arm.com>
On 3/14/26 12:46 AM, Ben Horgan wrote:
> From: Shanker Donthineni <sdonthineni@nvidia.com>
>
> The registers MSMON_MBWU_L and MSMON_MBWU return the number of requests
> rather than the number of bytes transferred.
>
> Bandwidth resource monitoring is performed at the last level cache, where
> each request arrive in 64Byte granularity. The current implementation
> returns the number of transactions received at the last level cache but
> does not provide the value in bytes. Scaling by 64 gives an accurate byte
> count to match the MPAM specification for the MSMON_MBWU and MSMON_MBWU_L
> registers. This patch fixes the issue by reporting the actual number of
> bytes instead of the number of transactions from __ris_msmon_read().
>
> Tested-by: Gavin Shan <gshan@redhat.com>
> Tested-by: Shaopeng Tan <tan.shaopeng@jp.fujitsu.com>
> Reviewed-by: Zeng Heng <zengheng4@huawei.com>
> Reviewed-by: Shaopeng Tan <tan.shaopeng@jp.fujitsu.com>
> Signed-off-by: Shanker Donthineni <sdonthineni@nvidia.com>
> Signed-off-by: James Morse <james.morse@arm.com>
> Signed-off-by: Ben Horgan <ben.horgan@arm.com>
> ---
> Changes since rfc:
> MPAM_IIDR_NVIDIA_T421 -> MPAM_IIDR_NVIDIA_T241
> Don't apply workaround to MSMON_MBWU_LWD
> ---
> Documentation/arch/arm64/silicon-errata.rst | 2 ++
> drivers/resctrl/mpam_devices.c | 26 +++++++++++++++++++--
> drivers/resctrl/mpam_internal.h | 1 +
> 3 files changed, 27 insertions(+), 2 deletions(-)
>
Reviewed-by: Gavin Shan <gshan@redhat.com>
^ permalink raw reply
* Re: [PATCH v6 37/40] arm_mpam: Add workaround for T241-MPAM-4
From: Gavin Shan @ 2026-03-24 4:19 UTC (permalink / raw)
To: Ben Horgan
Cc: amitsinght, baisheng.gao, baolin.wang, carl, dave.martin, david,
dfustini, fenghuay, james.morse, jonathan.cameron, kobak,
lcherian, linux-arm-kernel, linux-kernel, peternewman,
punit.agrawal, quic_jiles, reinette.chatre, rohit.mathew, scott,
sdonthineni, tan.shaopeng, xhao, catalin.marinas, will, corbet,
maz, oupton, joey.gouly, suzuki.poulose, kvmarm, zengheng4,
linux-doc, Shaopeng Tan
In-Reply-To: <20260313144617.3420416-38-ben.horgan@arm.com>
On 3/14/26 12:46 AM, Ben Horgan wrote:
> From: Shanker Donthineni <sdonthineni@nvidia.com>
>
> In the T241 implementation of memory-bandwidth partitioning, in the absence
> of contention for bandwidth, the minimum bandwidth setting can affect the
> amount of achieved bandwidth. Specifically, the achieved bandwidth in the
> absence of contention can settle to any value between the values of
> MPAMCFG_MBW_MIN and MPAMCFG_MBW_MAX. Also, if MPAMCFG_MBW_MIN is set
> zero (below 0.78125%), once a core enters a throttled state, it will never
> leave that state.
>
> The first issue is not a concern if the MPAM software allows to program
> MPAMCFG_MBW_MIN through the sysfs interface. This patch ensures program
> MBW_MIN=1 (0.78125%) whenever MPAMCFG_MBW_MIN=0 is programmed.
>
> In the scenario where the resctrl doesn't support the MBW_MIN interface via
> sysfs, to achieve bandwidth closer to MBW_MAX in the absence of contention,
> software should configure a relatively narrow gap between MBW_MIN and
> MBW_MAX. The recommendation is to use a 5% gap to mitigate the problem.
>
> Clear the feature MBW_MIN feature from the class to ensure we don't
> accidentally change behaviour when resctrl adds support for a MBW_MIN
> interface.
>
> Tested-by: Gavin Shan <gshan@redhat.com>
> Tested-by: Shaopeng Tan <tan.shaopeng@jp.fujitsu.com>
> Reviewed-by: Zeng Heng <zengheng4@huawei.com>
> Reviewed-by: Shaopeng Tan <tan.shaopeng@jp.fujitsu.com>
> Reviewed-by: Fenghua Yu <fenghuay@nvidia.com>
> Signed-off-by: Shanker Donthineni <sdonthineni@nvidia.com>
> Signed-off-by: James Morse <james.morse@arm.com>
> Signed-off-by: Ben Horgan <ben.horgan@arm.com>
> ---
> [ morse: Added as second quirk, adapted to use the new intermediate values
> in mpam_extend_config() ]
>
> Changes since rfc:
> MPAM_IIDR_NVIDIA_T421 -> MPAM_IIDR_NVIDIA_T241
> Handling when reset_mbw_min is set
>
> Changes since v3:
> Move the 5% gap policy back here
> Clear mbw_min feature in class
>
> Changes since v5:
> Calculate min from max when resetting
> ---
> Documentation/arch/arm64/silicon-errata.rst | 2 +
> drivers/resctrl/mpam_devices.c | 55 +++++++++++++++++++--
> drivers/resctrl/mpam_internal.h | 1 +
> 3 files changed, 55 insertions(+), 3 deletions(-)
>
Reviewed-by: Gavin Shan <gshan@redhat.com>
^ permalink raw reply
* Re: [PATCH v6 36/40] arm_mpam: Add workaround for T241-MPAM-1
From: Gavin Shan @ 2026-03-24 4:16 UTC (permalink / raw)
To: Ben Horgan
Cc: amitsinght, baisheng.gao, baolin.wang, carl, dave.martin, david,
dfustini, fenghuay, james.morse, jonathan.cameron, kobak,
lcherian, linux-arm-kernel, linux-kernel, peternewman,
punit.agrawal, quic_jiles, reinette.chatre, rohit.mathew, scott,
sdonthineni, tan.shaopeng, xhao, catalin.marinas, will, corbet,
maz, oupton, joey.gouly, suzuki.poulose, kvmarm, zengheng4,
linux-doc, Shaopeng Tan
In-Reply-To: <20260313144617.3420416-37-ben.horgan@arm.com>
Hi Ben,
On 3/14/26 12:46 AM, Ben Horgan wrote:
> From: Shanker Donthineni <sdonthineni@nvidia.com>
>
> The MPAM bandwidth partitioning controls will not be correctly configured,
> and hardware will retain default configuration register values, meaning
> generally that bandwidth will remain unprovisioned.
>
> To address the issue, follow the below steps after updating the MBW_MIN
> and/or MBW_MAX registers.
>
> - Perform 64b reads from all 12 bridge MPAM shadow registers at offsets
> (0x360048 + slice*0x10000 + partid*8). These registers are read-only.
> - Continue iterating until all 12 shadow register values match in a loop.
> pr_warn_once if the values fail to match within the loop count 1000.
> - Perform 64b writes with the value 0x0 to the two spare registers at
> offsets 0x1b0000 and 0x1c0000.
>
> In the hardware, writes to the MPAMCFG_MBW_MAX MPAMCFG_MBW_MIN registers
> are transformed into broadcast writes to the 12 shadow registers. The
> final two writes to the spare registers cause a final rank of downstream
> micro-architectural MPAM registers to be updated from the shadow copies.
> The intervening loop to read the 12 shadow registers helps avoid a race
> condition where writes to the spare registers occur before all shadow
> registers have been updated.
>
> Tested-by: Gavin Shan <gshan@redhat.com>
> Tested-by: Shaopeng Tan <tan.shaopeng@jp.fujitsu.com>
> Reviewed-by: Zeng Heng <zengheng4@huawei.com>
> Reviewed-by: Shaopeng Tan <tan.shaopeng@jp.fujitsu.com>
> Signed-off-by: Shanker Donthineni <sdonthineni@nvidia.com>
> Signed-off-by: James Morse <james.morse@arm.com>
> Signed-off-by: Ben Horgan <ben.horgan@arm.com>
> ---
>
> Changes from James:
> Merged the min/max update into a single
> mpam_quirk_post_config_change() helper. Stashed the t241_id in the msc
> instead of carrying the physical address around. Test the msc quirk bit
> instead of a static key.
>
> Changes since rfc:
> MPAM_IIDR_NVIDIA_T421 -> MPAM_IIDR_NVIDIA_T241
> return err from init
> Be specific about the errata in the init name,
> mpam_enable_quirk_nvidia_t241 -> mpam_enable_quirk_nvidia_t241_1
>
> Changes since v3:
> parentheses
> ---
> Documentation/arch/arm64/silicon-errata.rst | 2 +
> drivers/resctrl/mpam_devices.c | 88 +++++++++++++++++++++
> drivers/resctrl/mpam_internal.h | 9 +++
> 3 files changed, 99 insertions(+)
>
One question below.
Reviewed-by: Gavin Shan <gshan@redhat.com>
> diff --git a/Documentation/arch/arm64/silicon-errata.rst b/Documentation/arch/arm64/silicon-errata.rst
> index 4c300caad901..a65620f98e3a 100644
> --- a/Documentation/arch/arm64/silicon-errata.rst
> +++ b/Documentation/arch/arm64/silicon-errata.rst
> @@ -247,6 +247,8 @@ stable kernels.
> +----------------+-----------------+-----------------+-----------------------------+
> | NVIDIA | T241 GICv3/4.x | T241-FABRIC-4 | N/A |
> +----------------+-----------------+-----------------+-----------------------------+
> +| NVIDIA | T241 MPAM | T241-MPAM-1 | N/A |
> ++----------------+-----------------+-----------------+-----------------------------+
> +----------------+-----------------+-----------------+-----------------------------+
> | Freescale/NXP | LS2080A/LS1043A | A-008585 | FSL_ERRATUM_A008585 |
> +----------------+-----------------+-----------------+-----------------------------+
> diff --git a/drivers/resctrl/mpam_devices.c b/drivers/resctrl/mpam_devices.c
> index e66631f3f732..b1753498f07f 100644
> --- a/drivers/resctrl/mpam_devices.c
> +++ b/drivers/resctrl/mpam_devices.c
> @@ -29,6 +29,16 @@
>
> #include "mpam_internal.h"
>
> +/* Values for the T241 errata workaround */
> +#define T241_CHIPS_MAX 4
> +#define T241_CHIP_NSLICES 12
> +#define T241_SPARE_REG0_OFF 0x1b0000
> +#define T241_SPARE_REG1_OFF 0x1c0000
> +#define T241_CHIP_ID(phys) FIELD_GET(GENMASK_ULL(44, 43), phys)
> +#define T241_SHADOW_REG_OFF(sidx, pid) (0x360048 + (sidx) * 0x10000 + (pid) * 8)
> +#define SMCCC_SOC_ID_T241 0x036b0241
> +static void __iomem *t241_scratch_regs[T241_CHIPS_MAX];
> +
> /*
> * mpam_list_lock protects the SRCU lists when writing. Once the
> * mpam_enabled key is enabled these lists are read-only,
> @@ -630,7 +640,45 @@ static struct mpam_msc_ris *mpam_get_or_create_ris(struct mpam_msc *msc,
> return ERR_PTR(-ENOENT);
> }
>
> +static int mpam_enable_quirk_nvidia_t241_1(struct mpam_msc *msc,
> + const struct mpam_quirk *quirk)
> +{
> + s32 soc_id = arm_smccc_get_soc_id_version();
> + struct resource *r;
> + phys_addr_t phys;
> +
> + /*
> + * A mapping to a device other than the MSC is needed, check
> + * SOC_ID is NVIDIA T241 chip (036b:0241)
> + */
> + if (soc_id < 0 || soc_id != SMCCC_SOC_ID_T241)
> + return -EINVAL;
> +
> + r = platform_get_resource(msc->pdev, IORESOURCE_MEM, 0);
> + if (!r)
> + return -EINVAL;
> +
> + /* Find the internal registers base addr from the CHIP ID */
> + msc->t241_id = T241_CHIP_ID(r->start);
> + phys = FIELD_PREP(GENMASK_ULL(45, 44), msc->t241_id) | 0x19000000ULL;
> +
> + t241_scratch_regs[msc->t241_id] = ioremap(phys, SZ_8M);
> + if (WARN_ON_ONCE(!t241_scratch_regs[msc->t241_id]))
> + return -EINVAL;
Those IO regions aren't unmapped when the MSCs are removed. I guess it would be
something to be improved? :-)
> +
> + pr_info_once("Enabled workaround for NVIDIA T241 erratum T241-MPAM-1\n");
> +
> + return 0;
> +}
> +
> static const struct mpam_quirk mpam_quirks[] = {
> + {
> + /* NVIDIA t241 erratum T241-MPAM-1 */
> + .init = mpam_enable_quirk_nvidia_t241_1,
> + .iidr = MPAM_IIDR_NVIDIA_T241,
> + .iidr_mask = MPAM_IIDR_MATCH_ONE,
> + .workaround = T241_SCRUB_SHADOW_REGS,
Perhaps we need a more leading space for every line in the above block.
> + },
> { NULL } /* Sentinel */
> };
>
> @@ -1378,6 +1426,44 @@ static void mpam_reset_msc_bitmap(struct mpam_msc *msc, u16 reg, u16 wd)
> __mpam_write_reg(msc, reg, bm);
> }
>
> +static void mpam_apply_t241_erratum(struct mpam_msc_ris *ris, u16 partid)
> +{
> + int sidx, i, lcount = 1000;
> + void __iomem *regs;
> + u64 val0, val;
> +
> + regs = t241_scratch_regs[ris->vmsc->msc->t241_id];
> +
> + for (i = 0; i < lcount; i++) {
> + /* Read the shadow register at index 0 */
> + val0 = readq_relaxed(regs + T241_SHADOW_REG_OFF(0, partid));
> +
> + /* Check if all the shadow registers have the same value */
> + for (sidx = 1; sidx < T241_CHIP_NSLICES; sidx++) {
> + val = readq_relaxed(regs +
> + T241_SHADOW_REG_OFF(sidx, partid));
> + if (val != val0)
> + break;
> + }
> + if (sidx == T241_CHIP_NSLICES)
> + break;
> + }
> +
> + if (i == lcount)
> + pr_warn_once("t241: inconsistent values in shadow regs");
> +
> + /* Write a value zero to spare registers to take effect of MBW conf */
> + writeq_relaxed(0, regs + T241_SPARE_REG0_OFF);
> + writeq_relaxed(0, regs + T241_SPARE_REG1_OFF);
> +}
> +
> +static void mpam_quirk_post_config_change(struct mpam_msc_ris *ris, u16 partid,
> + struct mpam_config *cfg)
> +{
> + if (mpam_has_quirk(T241_SCRUB_SHADOW_REGS, ris->vmsc->msc))
> + mpam_apply_t241_erratum(ris, partid);
> +}
> +
> /* Called via IPI. Call while holding an SRCU reference */
> static void mpam_reprogram_ris_partid(struct mpam_msc_ris *ris, u16 partid,
> struct mpam_config *cfg)
> @@ -1457,6 +1543,8 @@ static void mpam_reprogram_ris_partid(struct mpam_msc_ris *ris, u16 partid,
> mpam_write_partsel_reg(msc, PRI, pri_val);
> }
>
> + mpam_quirk_post_config_change(ris, partid, cfg);
> +
> mutex_unlock(&msc->part_sel_lock);
> }
>
> diff --git a/drivers/resctrl/mpam_internal.h b/drivers/resctrl/mpam_internal.h
> index e28a168419d4..e38954a735d8 100644
> --- a/drivers/resctrl/mpam_internal.h
> +++ b/drivers/resctrl/mpam_internal.h
> @@ -130,6 +130,9 @@ struct mpam_msc {
> void __iomem *mapped_hwpage;
> size_t mapped_hwpage_sz;
>
> + /* Values only used on some platforms for quirks */
> + u32 t241_id;
> +
> struct mpam_garbage garbage;
> };
>
> @@ -220,6 +223,7 @@ struct mpam_props {
>
> /* Workaround bits for msc->quirks */
> enum mpam_device_quirks {
> + T241_SCRUB_SHADOW_REGS,
> MPAM_QUIRK_LAST
> };
>
> @@ -240,6 +244,11 @@ struct mpam_quirk {
> FIELD_PREP_CONST(MPAMF_IIDR_REVISION, 0xf) | \
> FIELD_PREP_CONST(MPAMF_IIDR_IMPLEMENTER, 0xfff))
>
> +#define MPAM_IIDR_NVIDIA_T241 (FIELD_PREP_CONST(MPAMF_IIDR_PRODUCTID, 0x241) | \
> + FIELD_PREP_CONST(MPAMF_IIDR_VARIANT, 0) | \
> + FIELD_PREP_CONST(MPAMF_IIDR_REVISION, 0) | \
> + FIELD_PREP_CONST(MPAMF_IIDR_IMPLEMENTER, 0x36b))
> +
> /* The values for MSMON_CFG_MBWU_FLT.RWBW */
> enum mon_filter_options {
> COUNT_BOTH = 0,
Thanks,
Gavin
^ permalink raw reply
* Re: [PATCH 0/2] dt-bindings: thermal: st,thermal-spear1340: convert to dtschema
From: Viresh Kumar @ 2026-03-24 4:07 UTC (permalink / raw)
To: Gopi Krishna Menon
Cc: rafael, daniel.lezcano, rui.zhang, lukasz.luba, robh, krzk+dt,
vireshk, conor+dt, linux-pm, devicetree, linux-kernel,
linux-arm-kernel, soc, daniel.baluta, simona.toaca, d-gole,
m-chawdhry
In-Reply-To: <20260323133814.14152-1-krishnagopi487@gmail.com>
On 23-03-26, 19:08, Gopi Krishna Menon wrote:
> This patch series converts SPEAr Thermal Sensor bindings to DT schema
> and renames thermal_flags property in spear13xx.dtsi to
> st,thermal-flags to fix the unevaluated property warning in
> st/spear1340-evb.dts.
>
> Note:
> * This patch is part of the GSoC2026 application process for device tree bindings conversions
> * https://github.com/LinuxFoundationGSoC/ProjectIdeas/wiki/GSoC-2026-Device-Tree-Bindings
>
>
> Gopi Krishna Menon (2):
> dt-bindings: thermal: st,thermal-spear1340: convert to dtschema
> ARM: dts: st: spear: fix dtbs warning on spear thermal sensor
>
> .../bindings/thermal/spear-thermal.txt | 14 --------
> .../thermal/st,thermal-spear1340.yaml | 36 +++++++++++++++++++
> arch/arm/boot/dts/st/spear13xx.dtsi | 2 +-
> 3 files changed, 37 insertions(+), 15 deletions(-)
> delete mode 100644 Documentation/devicetree/bindings/thermal/spear-thermal.txt
> create mode 100644 Documentation/devicetree/bindings/thermal/st,thermal-spear1340.yaml
Acked-by: Viresh Kumar <viresh.kumar@linaro.org>
--
viresh
^ permalink raw reply
* Re: [PATCH v6 35/40] arm_mpam: Add quirk framework
From: Gavin Shan @ 2026-03-24 3:56 UTC (permalink / raw)
To: Ben Horgan
Cc: amitsinght, baisheng.gao, baolin.wang, carl, dave.martin, david,
dfustini, fenghuay, james.morse, jonathan.cameron, kobak,
lcherian, linux-arm-kernel, linux-kernel, peternewman,
punit.agrawal, quic_jiles, reinette.chatre, rohit.mathew, scott,
sdonthineni, tan.shaopeng, xhao, catalin.marinas, will, corbet,
maz, oupton, joey.gouly, suzuki.poulose, kvmarm, zengheng4,
linux-doc, Shaopeng Tan
In-Reply-To: <20260313144617.3420416-36-ben.horgan@arm.com>
Hi Ben,
On 3/14/26 12:46 AM, Ben Horgan wrote:
> From: Shanker Donthineni <sdonthineni@nvidia.com>
>
> The MPAM specification includes the MPAMF_IIDR, which serves to uniquely
> identify the MSC implementation through a combination of implementer
> details, product ID, variant, and revision. Certain hardware issues/errata
> can be resolved using software workarounds.
>
> Introduce a quirk framework to allow workarounds to be enabled based on the
> MPAMF_IIDR value.
>
> Tested-by: Gavin Shan <gshan@redhat.com>
> Tested-by: Shaopeng Tan <tan.shaopeng@jp.fujitsu.com>
> Tested-by: Zeng Heng <zengheng4@huawei.com>
> Tested-by: Punit Agrawal <punit.agrawal@oss.qualcomm.com>
> Reviewed-by: Zeng Heng <zengheng4@huawei.com>
> Reviewed-by: Shaopeng Tan <tan.shaopeng@jp.fujitsu.com>
> Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
> Signed-off-by: Shanker Donthineni <sdonthineni@nvidia.com>
> Co-developed-by: James Morse <james.morse@arm.com>
> Signed-off-by: James Morse <james.morse@arm.com>
> Signed-off-by: Ben Horgan <ben.horgan@arm.com>
> ---
> Changes by James:
> Stash the IIDR so this doesn't need an IPI, enable quirks only
> once, move the description to the callback so it can be pr_once()d, add an
> enum of workarounds for popular errata. Add macros for making lists of
> product/revision/vendor half readable
>
> Changes since rfc:
> remove trailing commas in last element of enums
> Make mpam_enable_quirks() in charge of mpam_set_quirk() even if there
> is an enable.
>
> Changes since v3:
> Brackets in macro
> ---
> drivers/resctrl/mpam_devices.c | 32 ++++++++++++++++++++++++++++++++
> drivers/resctrl/mpam_internal.h | 25 +++++++++++++++++++++++++
> 2 files changed, 57 insertions(+)
>
With the following nitpicks addressed if another respin is needed. This
looks good to me in either way.
Reviewed-by: Gavin Shan <gshan@redhat.com>
> diff --git a/drivers/resctrl/mpam_devices.c b/drivers/resctrl/mpam_devices.c
> index 37b31a1cf376..e66631f3f732 100644
> --- a/drivers/resctrl/mpam_devices.c
> +++ b/drivers/resctrl/mpam_devices.c
> @@ -630,6 +630,30 @@ static struct mpam_msc_ris *mpam_get_or_create_ris(struct mpam_msc *msc,
> return ERR_PTR(-ENOENT);
> }
>
> +static const struct mpam_quirk mpam_quirks[] = {
> + { NULL } /* Sentinel */
> +};
> +
> +static void mpam_enable_quirks(struct mpam_msc *msc)
> +{
> + const struct mpam_quirk *quirk;
> +
> + for (quirk = &mpam_quirks[0]; quirk->iidr_mask; quirk++) {
> + int err = 0;
The initialization on @err is unnecessary if it's checked only when it's
updated (see below). The variable can be avoided.
> +
> + if (quirk->iidr != (msc->iidr & quirk->iidr_mask))
> + continue;
> +
> + if (quirk->init)
> + err = quirk->init(msc, quirk);
> +
> + if (err)
> + continue;
Since @err is only updated by quirk->init(), the following check would be done
only when it's updated. Something like below, @err isn't needed.
if (quirk->init && quirk->init(msc, quirk)
continue;
> +
> + mpam_set_quirk(quirk->workaround, msc);
> + }
> +}
> +
> /*
> * IHI009A.a has this nugget: "If a monitor does not support automatic behaviour
> * of NRDY, software can use this bit for any purpose" - so hardware might not
> @@ -864,8 +888,11 @@ static int mpam_msc_hw_probe(struct mpam_msc *msc)
> /* Grab an IDR value to find out how many RIS there are */
> mutex_lock(&msc->part_sel_lock);
> idr = mpam_msc_read_idr(msc);
> + msc->iidr = mpam_read_partsel_reg(msc, IIDR);
> mutex_unlock(&msc->part_sel_lock);
>
> + mpam_enable_quirks(msc);
> +
> msc->ris_max = FIELD_GET(MPAMF_IDR_RIS_MAX, idr);
>
> /* Use these values so partid/pmg always starts with a valid value */
> @@ -1972,6 +1999,7 @@ static bool mpam_has_cmax_wd_feature(struct mpam_props *props)
> * resulting safe value must be compatible with both. When merging values in
> * the tree, all the aliasing resources must be handled first.
> * On mismatch, parent is modified.
> + * Quirks on an MSC will apply to all MSC in that class.
> */
> static void __props_mismatch(struct mpam_props *parent,
> struct mpam_props *child, bool alias)
> @@ -2091,6 +2119,7 @@ static void __props_mismatch(struct mpam_props *parent,
> * nobble the class feature, as we can't configure all the resources.
> * e.g. The L3 cache is composed of two resources with 13 and 17 portion
> * bitmaps respectively.
> + * Quirks on an MSC will apply to all MSC in that class.
> */
> static void
> __class_props_mismatch(struct mpam_class *class, struct mpam_vmsc *vmsc)
> @@ -2104,6 +2133,9 @@ __class_props_mismatch(struct mpam_class *class, struct mpam_vmsc *vmsc)
> dev_dbg(dev, "Merging features for class:0x%lx &= vmsc:0x%lx\n",
> (long)cprops->features, (long)vprops->features);
>
> + /* Merge quirks */
> + class->quirks |= vmsc->msc->quirks;
> +
> /* Take the safe value for any common features */
> __props_mismatch(cprops, vprops, false);
> }
> diff --git a/drivers/resctrl/mpam_internal.h b/drivers/resctrl/mpam_internal.h
> index ce9e0e0483fb..e28a168419d4 100644
> --- a/drivers/resctrl/mpam_internal.h
> +++ b/drivers/resctrl/mpam_internal.h
> @@ -85,6 +85,8 @@ struct mpam_msc {
> u8 pmg_max;
> unsigned long ris_idxs;
> u32 ris_max;
> + u32 iidr;
> + u16 quirks;
>
It maybe reasonable to have 'u32 quirks' so that 32 instead of 16 quirks can be
supported to the maximal degree. It's known 16 quirks are enough at the moment,
but it's likely to be extended for more space.
> /*
> * error_irq_lock is taken when registering/unregistering the error
> @@ -216,6 +218,28 @@ struct mpam_props {
> #define mpam_set_feature(_feat, x) __set_bit(_feat, (x)->features)
> #define mpam_clear_feature(_feat, x) __clear_bit(_feat, (x)->features)
>
> +/* Workaround bits for msc->quirks */
> +enum mpam_device_quirks {
> + MPAM_QUIRK_LAST
> +};
> +
> +#define mpam_has_quirk(_quirk, x) ((1 << (_quirk) & (x)->quirks))
> +#define mpam_set_quirk(_quirk, x) ((x)->quirks |= (1 << (_quirk)))
> +
> +struct mpam_quirk {
> + int (*init)(struct mpam_msc *msc, const struct mpam_quirk *quirk);
> +
> + u32 iidr;
> + u32 iidr_mask;
> +
> + enum mpam_device_quirks workaround;
> +};
> +
> +#define MPAM_IIDR_MATCH_ONE (FIELD_PREP_CONST(MPAMF_IIDR_PRODUCTID, 0xfff) | \
> + FIELD_PREP_CONST(MPAMF_IIDR_VARIANT, 0xf) | \
> + FIELD_PREP_CONST(MPAMF_IIDR_REVISION, 0xf) | \
> + FIELD_PREP_CONST(MPAMF_IIDR_IMPLEMENTER, 0xfff))
> +
> /* The values for MSMON_CFG_MBWU_FLT.RWBW */
> enum mon_filter_options {
> COUNT_BOTH = 0,
> @@ -259,6 +283,7 @@ struct mpam_class {
>
> struct mpam_props props;
> u32 nrdy_usec;
> + u16 quirks;
As above, it would be "u32 quirks".
> u8 level;
> enum mpam_class_types type;
>
Thanks,
Gavin
^ permalink raw reply
* Re: [PATCH v6 34/40] arm_mpam: resctrl: Call resctrl_init() on platforms that can support resctrl
From: Gavin Shan @ 2026-03-24 3:43 UTC (permalink / raw)
To: Ben Horgan
Cc: amitsinght, baisheng.gao, baolin.wang, carl, dave.martin, david,
dfustini, fenghuay, james.morse, jonathan.cameron, kobak,
lcherian, linux-arm-kernel, linux-kernel, peternewman,
punit.agrawal, quic_jiles, reinette.chatre, rohit.mathew, scott,
sdonthineni, tan.shaopeng, xhao, catalin.marinas, will, corbet,
maz, oupton, joey.gouly, suzuki.poulose, kvmarm, zengheng4,
linux-doc, Shaopeng Tan
In-Reply-To: <20260313144617.3420416-35-ben.horgan@arm.com>
On 3/14/26 12:46 AM, Ben Horgan wrote:
> From: James Morse <james.morse@arm.com>
>
> Now that MPAM links against resctrl, call resctrl_init() to register the
> filesystem and setup resctrl's structures.
>
> Tested-by: Gavin Shan <gshan@redhat.com>
> Tested-by: Shaopeng Tan <tan.shaopeng@jp.fujitsu.com>
> Tested-by: Peter Newman <peternewman@google.com>
> Tested-by: Zeng Heng <zengheng4@huawei.com>
> Tested-by: Punit Agrawal <punit.agrawal@oss.qualcomm.com>
> Reviewed-by: Zeng Heng <zengheng4@huawei.com>
> Reviewed-by: Shaopeng Tan <tan.shaopeng@jp.fujitsu.com>
> Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
> Signed-off-by: James Morse <james.morse@arm.com>
> Signed-off-by: Ben Horgan <ben.horgan@arm.com>
> ---
> Changes since v2:
> Use for_each_mpam...
> error path tidying
>
> Changes since v3:
> Don't consider abmc/mbwu in teardown
> ---
> drivers/resctrl/mpam_devices.c | 32 ++++++++++++++---
> drivers/resctrl/mpam_internal.h | 4 +++
> drivers/resctrl/mpam_resctrl.c | 63 ++++++++++++++++++++++++++++++++-
> 3 files changed, 94 insertions(+), 5 deletions(-)
>
Reviewed-by: Gavin Shan <gshan@redhat.com>
^ permalink raw reply
* Re: [PATCH v6 33/40] arm64: mpam: Select ARCH_HAS_CPU_RESCTRL
From: Gavin Shan @ 2026-03-24 3:42 UTC (permalink / raw)
To: Ben Horgan
Cc: amitsinght, baisheng.gao, baolin.wang, carl, dave.martin, david,
dfustini, fenghuay, james.morse, jonathan.cameron, kobak,
lcherian, linux-arm-kernel, linux-kernel, peternewman,
punit.agrawal, quic_jiles, reinette.chatre, rohit.mathew, scott,
sdonthineni, tan.shaopeng, xhao, catalin.marinas, will, corbet,
maz, oupton, joey.gouly, suzuki.poulose, kvmarm, zengheng4,
linux-doc, Shaopeng Tan
In-Reply-To: <20260313144617.3420416-34-ben.horgan@arm.com>
On 3/14/26 12:46 AM, Ben Horgan wrote:
> From: James Morse <james.morse@arm.com>
>
> Enough MPAM support is present to enable ARCH_HAS_CPU_RESCTRL. Let it
> rip^Wlink!
>
> ARCH_HAS_CPU_RESCTRL indicates resctrl can be enabled. It is enabled by the
> arch code simply because it has 'arch' in its name.
>
> This removes ARM_CPU_RESCTRL as a mimic of X86_CPU_RESCTRL. While here,
> move the ACPI dependency to the driver's Kconfig file.
>
> Tested-by: Gavin Shan <gshan@redhat.com>
> Tested-by: Shaopeng Tan <tan.shaopeng@jp.fujitsu.com>
> Tested-by: Peter Newman <peternewman@google.com>
> Tested-by: Zeng Heng <zengheng4@huawei.com>
> Tested-by: Punit Agrawal <punit.agrawal@oss.qualcomm.com>
> Reviewed-by: Zeng Heng <zengheng4@huawei.com>
> Reviewed-by: Shaopeng Tan <tan.shaopeng@jp.fujitsu.com>
> Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
> Signed-off-by: James Morse <james.morse@arm.com>
> Acked-by: Catalin Marinas <catalin.marinas@arm.com>
> Signed-off-by: Ben Horgan <ben.horgan@arm.com>
> ---
> arch/arm64/Kconfig | 2 +-
> arch/arm64/include/asm/resctrl.h | 2 ++
> drivers/resctrl/Kconfig | 7 +++++++
> drivers/resctrl/Makefile | 2 +-
> 4 files changed, 11 insertions(+), 2 deletions(-)
> create mode 100644 arch/arm64/include/asm/resctrl.h
>
Reviewed-by: Gavin Shan <gshan@redhat.com>
^ permalink raw reply
* Re: [PATCH v6 32/40] arm_mpam: resctrl: Add empty definitions for assorted resctrl functions
From: Gavin Shan @ 2026-03-24 3:42 UTC (permalink / raw)
To: Ben Horgan
Cc: amitsinght, baisheng.gao, baolin.wang, carl, dave.martin, david,
dfustini, fenghuay, james.morse, jonathan.cameron, kobak,
lcherian, linux-arm-kernel, linux-kernel, peternewman,
punit.agrawal, quic_jiles, reinette.chatre, rohit.mathew, scott,
sdonthineni, tan.shaopeng, xhao, catalin.marinas, will, corbet,
maz, oupton, joey.gouly, suzuki.poulose, kvmarm, zengheng4,
linux-doc, Shaopeng Tan
In-Reply-To: <20260313144617.3420416-33-ben.horgan@arm.com>
On 3/14/26 12:46 AM, Ben Horgan wrote:
> From: James Morse <james.morse@arm.com>
>
> A few resctrl features and hooks need to be provided, but aren't needed or
> supported on MPAM platforms.
>
> resctrl has individual hooks to separately enable and disable the
> closid/partid and rmid/pmg context switching code. For MPAM this is all the
> same thing, as the value in struct task_struct is used to cache the value
> that should be written to hardware. arm64's context switching code is
> enabled once MPAM is usable, but doesn't touch the hardware unless the
> value has changed.
>
> For now event configuration is not supported, and can be turned off by
> returning 'false' from resctrl_arch_is_evt_configurable().
>
> The new io_alloc feature is not supported either, always return false from
> the enable helper to indicate and fail the enable.
>
> Add this, and empty definitions for the other hooks.
>
> Tested-by: Gavin Shan <gshan@redhat.com>
> Tested-by: Shaopeng Tan <tan.shaopeng@jp.fujitsu.com>
> Tested-by: Peter Newman <peternewman@google.com>
> Tested-by: Zeng Heng <zengheng4@huawei.com>
> Tested-by: Punit Agrawal <punit.agrawal@oss.qualcomm.com>
> Reviewed-by: Zeng Heng <zengheng4@huawei.com>
> Reviewed-by: Shaopeng Tan <tan.shaopeng@jp.fujitsu.com>
> Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
> Signed-off-by: James Morse <james.morse@arm.com>
> Signed-off-by: Ben Horgan <ben.horgan@arm.com>
> ---
> Changes since v3:
> Add resctrl_arch_pre_mount() {}
> resctrl_arch_reset_rmid_all() signature update
> add stubs for abmc
> keep empty definitions together
>
> Changes since v5:
> Add resctrl_arch_reset_rmid() since mbwu will always use abmc
> ---
> drivers/resctrl/mpam_resctrl.c | 65 ++++++++++++++++++++++++++++++++++
> include/linux/arm_mpam.h | 9 +++++
> 2 files changed, 74 insertions(+)
>
Reviewed-by: Gavin Shan <gshan@redhat.com>
^ permalink raw reply
* Re: [PATCH 2/5] regulator: dt-bindings: mt6359: Drop bogus vcn33_[12]_* split regulators
From: Chen-Yu Tsai @ 2026-03-24 3:42 UTC (permalink / raw)
To: Krzysztof Kozlowski
Cc: Mark Brown, Liam Girdwood, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Matthias Brugger, AngeloGioacchino Del Regno,
linux-arm-kernel, linux-mediatek, devicetree
In-Reply-To: <e14ae934-e448-433f-987d-77bede5f6d07@kernel.org>
On Mon, Mar 23, 2026 at 4:31 PM Krzysztof Kozlowski <krzk@kernel.org> wrote:
>
> On 23/03/2026 09:26, Chen-Yu Tsai wrote:
> > On Fri, Mar 20, 2026 at 5:54 PM Krzysztof Kozlowski <krzk@kernel.org> wrote:
> >>
> >> On Fri, Mar 20, 2026 at 03:24:35PM +0800, Chen-Yu Tsai wrote:
> >>> vcn33_[12]_bt and vcn33_[12]_wifi refer to the same output. There are
> >>> two enable bits in the registers so that BT and WiFi drivers can toggle
> >>> them separately without any coordination. If either bit is set, then the
> >>> regulator output is enabled.
> >>>
> >>> Unfortunately some of them are already referenced by in-tree device
> >>> trees. To keep backward compatibility with them, keep the vcn33_*_bt
> >>
> >> So you drop "_wifi" regulators breaking the ABI, no?
> >
> > Indeed it is a breakage. I kept the *_bt part to cover all in-tree
> > device trees.
> >
> > But the representation itself is broken, as explained in the commit
> > message.
> >
> > I leave it to the platform maintainers to decide whether this "breakage"
> > to fix the broken representation is acceptable.
> >
> > Note that there is a similar issue with the *_sshub and non _sshub
> > regulators: there is only one actual output for the two. I'm still
> > asking the vendor for hardware behavior specifics.
> >
> >>> regulator. Also combine them for a shorten regular expression pattern.
> >>
> >> Instead the duplicates should be deprecated and driver should still
> >> support the "_wifi" variants.
> >
> > But it doesn't really work in the driver. You cannot set different
> > constraints on the two, especially conflicting constraints, and expect
> > them to work. And if you set the voltage on one of them, the other
> > will change as well.
>
> Of course, but now imagine a DTB with only *_wifi regulators and no *_bt.
>
> >
> > If we want the driver to support both with _sane_ behavior, we would
> > need to introduce some sort of alias lookup for both reading
> > constraints and resolving supplies. The driver registers _just_ one
> > regulator, and both DT nodes resolve to it.
>
> Yes, either alias lookup or adjusting the regulator_desc before
> registering them, depending on the nodes.
>
> If I understood old code correctly, the point is that ABI allowed *_wifi
> without *_bt and this would be a working setup. Now it won't work.
OK. I will drop this part for now and just add the supplies. I'll
resend the cleanup once I figure out how to do it in the regulator
driver.
ChenYu
^ permalink raw reply
* Re: [PATCH v6 31/40] arm_mpam: resctrl: Update the rmid reallocation limit
From: Gavin Shan @ 2026-03-24 3:42 UTC (permalink / raw)
To: Ben Horgan
Cc: amitsinght, baisheng.gao, baolin.wang, carl, dave.martin, david,
dfustini, fenghuay, james.morse, jonathan.cameron, kobak,
lcherian, linux-arm-kernel, linux-kernel, peternewman,
punit.agrawal, quic_jiles, reinette.chatre, rohit.mathew, scott,
sdonthineni, tan.shaopeng, xhao, catalin.marinas, will, corbet,
maz, oupton, joey.gouly, suzuki.poulose, kvmarm, zengheng4,
linux-doc, Shaopeng Tan
In-Reply-To: <20260313144617.3420416-32-ben.horgan@arm.com>
On 3/14/26 12:46 AM, Ben Horgan wrote:
> From: James Morse <james.morse@arm.com>
>
> resctrl's limbo code needs to be told when the data left in a cache is
> small enough for the partid+pmg value to be re-allocated.
>
> x86 uses the cache size divided by the number of rmid users the cache may
> have. Do the same, but for the smallest cache, and with the number of
> partid-and-pmg users.
>
> Tested-by: Gavin Shan <gshan@redhat.com>
> Tested-by: Shaopeng Tan <tan.shaopeng@jp.fujitsu.com>
> Tested-by: Peter Newman <peternewman@google.com>
> Tested-by: Zeng Heng <zengheng4@huawei.com>
> Tested-by: Punit Agrawal <punit.agrawal@oss.qualcomm.com>
> Reviewed-by: Zeng Heng <zengheng4@huawei.com>
> Reviewed-by: Shaopeng Tan <tan.shaopeng@jp.fujitsu.com>
> Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
> Signed-off-by: James Morse <james.morse@arm.com>
> Signed-off-by: Ben Horgan <ben.horgan@arm.com>
> ---
> Changes since v2:
> Move waiting for cache info into it's own patch
>
> Changes since v3:
> Move check class is csu higher (just kept to document intent)
> continue -> break
>
> to squash update rmid limits
> use raw_smp_processor_id()
> ---
> drivers/resctrl/mpam_resctrl.c | 39 ++++++++++++++++++++++++++++++++++
> 1 file changed, 39 insertions(+)
>
Reviewed-by: Gavin Shan <gshan@redhat.com>
^ permalink raw reply
* Re: [PATCH v6 30/40] arm_mpam: resctrl: Add resctrl_arch_rmid_read()
From: Gavin Shan @ 2026-03-24 3:41 UTC (permalink / raw)
To: Ben Horgan
Cc: amitsinght, baisheng.gao, baolin.wang, carl, dave.martin, david,
dfustini, fenghuay, james.morse, jonathan.cameron, kobak,
lcherian, linux-arm-kernel, linux-kernel, peternewman,
punit.agrawal, quic_jiles, reinette.chatre, rohit.mathew, scott,
sdonthineni, tan.shaopeng, xhao, catalin.marinas, will, corbet,
maz, oupton, joey.gouly, suzuki.poulose, kvmarm, zengheng4,
linux-doc, Shaopeng Tan
In-Reply-To: <20260313144617.3420416-31-ben.horgan@arm.com>
On 3/14/26 12:46 AM, Ben Horgan wrote:
> From: James Morse <james.morse@arm.com>
>
> resctrl uses resctrl_arch_rmid_read() to read counters. CDP emulation means
> the counter may need reading in three different ways.
>
> The helpers behind the resctrl_arch_ functions will be re-used for the ABMC
> equivalent functions.
>
> Add the rounding helper for checking monitor values while we're here.
>
> Tested-by: Gavin Shan <gshan@redhat.com>
> Tested-by: Shaopeng Tan <tan.shaopeng@jp.fujitsu.com>
> Tested-by: Peter Newman <peternewman@google.com>
> Tested-by: Zeng Heng <zengheng4@huawei.com>
> Reviewed-by: Shaopeng Tan <tan.shaopeng@jp.fujitsu.com>
> Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
> Signed-off-by: James Morse <james.morse@arm.com>
> Signed-off-by: Ben Horgan <ben.horgan@arm.com>
> ---
> Changes since rfc:
> cfg initialisation style
> code flow at end of read_mon_cdp_safe()
>
> Changes since v2:
> Whitespace changes
>
> Changes since v3:
> Update function signatures
> Remove abmc check
>
> Changes since v5:
> don't read mbwu
> remove reset code as only needed for mbwu
> ---
> drivers/resctrl/mpam_resctrl.c | 82 ++++++++++++++++++++++++++++++++++
> include/linux/arm_mpam.h | 5 +++
> 2 files changed, 87 insertions(+)
>
Reviewed-by: Gavin Shan <gshan@redhat.com>
^ permalink raw reply
* Re: [PATCH v6 29/40] arm_mpam: resctrl: Allow resctrl to allocate monitors
From: Gavin Shan @ 2026-03-24 3:41 UTC (permalink / raw)
To: Ben Horgan
Cc: amitsinght, baisheng.gao, baolin.wang, carl, dave.martin, david,
dfustini, fenghuay, james.morse, jonathan.cameron, kobak,
lcherian, linux-arm-kernel, linux-kernel, peternewman,
punit.agrawal, quic_jiles, reinette.chatre, rohit.mathew, scott,
sdonthineni, tan.shaopeng, xhao, catalin.marinas, will, corbet,
maz, oupton, joey.gouly, suzuki.poulose, kvmarm, zengheng4,
linux-doc, Shaopeng Tan
In-Reply-To: <20260313144617.3420416-30-ben.horgan@arm.com>
On 3/14/26 12:46 AM, Ben Horgan wrote:
> From: James Morse <james.morse@arm.com>
>
> When resctrl wants to read a domain's 'QOS_L3_OCCUP', it needs to allocate
> a monitor on the corresponding resource. Monitors are allocated by class
> instead of component.
>
> Add helpers to allocate a CSU monitor. These helper return an out of range
> value for MBM counters.
>
> Allocating a montitor context is expected to block until hardware resources
> become available. This only makes sense for QOS_L3_OCCUP as unallocated MBM
> counters are losing data.
>
> Tested-by: Gavin Shan <gshan@redhat.com>
> Tested-by: Shaopeng Tan <tan.shaopeng@jp.fujitsu.com>
> Tested-by: Peter Newman <peternewman@google.com>
> Tested-by: Zeng Heng <zengheng4@huawei.com>
> Tested-by: Punit Agrawal <punit.agrawal@oss.qualcomm.com>
> Reviewed-by: Zeng Heng <zengheng4@huawei.com>
> Reviewed-by: Shaopeng Tan <tan.shaopeng@jp.fujitsu.com>
> Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
> Signed-off-by: James Morse <james.morse@arm.com>
> Signed-off-by: Ben Horgan <ben.horgan@arm.com>
> ---
> Changes since rfc:
> USE_RMID_IDX -> USE_PRE_ALLOCATED in comment
> Remove unnecessary arch_mon_ctx = NULL
>
> Changes since v2:
> Add include of resctrl_types.h as dropped from earlier patch
>
> Changes since v3:
> Don't mention ABMC in commit message
>
> Changes since v5:
> Remove MBM free running sentence from commit message
> kmalloc -> kmalloc_obj
> ---
> drivers/resctrl/mpam_internal.h | 14 ++++++-
> drivers/resctrl/mpam_resctrl.c | 67 +++++++++++++++++++++++++++++++++
> include/linux/arm_mpam.h | 5 +++
> 3 files changed, 85 insertions(+), 1 deletion(-)
>
Reviewed-by: Gavin Shan <gshan@redhat.com>
^ permalink raw reply
* Re: [PATCH v6 28/40] arm_mpam: resctrl: Add support for csu counters
From: Gavin Shan @ 2026-03-24 3:40 UTC (permalink / raw)
To: Ben Horgan
Cc: amitsinght, baisheng.gao, baolin.wang, carl, dave.martin, david,
dfustini, fenghuay, james.morse, jonathan.cameron, kobak,
lcherian, linux-arm-kernel, linux-kernel, peternewman,
punit.agrawal, quic_jiles, reinette.chatre, rohit.mathew, scott,
sdonthineni, tan.shaopeng, xhao, catalin.marinas, will, corbet,
maz, oupton, joey.gouly, suzuki.poulose, kvmarm, zengheng4,
linux-doc, Shaopeng Tan
In-Reply-To: <20260313144617.3420416-29-ben.horgan@arm.com>
On 3/14/26 12:46 AM, Ben Horgan wrote:
> From: James Morse <james.morse@arm.com>
>
> resctrl exposes a counter via a file named llc_occupancy. This isn't really
> a counter as its value goes up and down, this is a snapshot of the cache
> storage usage monitor.
>
> Add some picking code which will only find an L3. The resctrl counter
> file is called llc_occupancy but we don't check it is the last one as
> it is already identified as L3.
>
> Tested-by: Shaopeng Tan <tan.shaopeng@jp.fujitsu.com>
> Tested-by: Zeng Heng <zengheng4@huawei.com>
> Tested-by: Punit Agrawal <punit.agrawal@oss.qualcomm.com>
> Reviewed-by: Zeng Heng <zengheng4@huawei.com>
> Reviewed-by: Shaopeng Tan <tan.shaopeng@jp.fujitsu.com>
> Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
> Signed-off-by: James Morse <james.morse@arm.com>
> Co-developed-by: Dave Martin <dave.martin@arm.com>
> Signed-off-by: Dave Martin <dave.martin@arm.com>
> Signed-off-by: Ben Horgan <ben.horgan@arm.com>
> ---
> Changes since rfc:
> Allow csu counters however many partid or pmg there are
> else if -> if
> reduce scope of local variables
> drop has_csu
>
> Changes since v2:
> return -> break so works for mbwu in later patch
> add for_each_mpam_resctrl_mon
> return error from mpam_resctrl_monitor_init(). It may fail when is abmc
> allocation introduced in a later patch.
> Squashed in patch from Dave Martin:
> https://lore.kernel.org/lkml/20250820131621.54983-1-Dave.Martin@arm.com/
>
> Changes since v3:
> resctrl_enable_mon_event() signature update
> Restrict the events considered
> num-rmid update
> Use raw_smp_processor_id()
> Tighten heuristics:
> Make sure it is the L3
> Please shout if this means the counters aren't exposed on any platforms
> Drop tags due to change in policy/rework
>
> Changes since v4:
> Move generic monitor boilerplate to separate patch
> ---
> drivers/resctrl/mpam_resctrl.c | 83 ++++++++++++++++++++++++++++++++++
> 1 file changed, 83 insertions(+)
>
Reviewed-by: Gavin Shan <gshan@redhat.com>
^ permalink raw reply
* Re: [PATCH v6 27/40] arm_mpam: resctrl: Add monitor initialisation and domain boilerplate
From: Gavin Shan @ 2026-03-24 3:40 UTC (permalink / raw)
To: Ben Horgan
Cc: amitsinght, baisheng.gao, baolin.wang, carl, dave.martin, david,
dfustini, fenghuay, james.morse, jonathan.cameron, kobak,
lcherian, linux-arm-kernel, linux-kernel, peternewman,
punit.agrawal, quic_jiles, reinette.chatre, rohit.mathew, scott,
sdonthineni, tan.shaopeng, xhao, catalin.marinas, will, corbet,
maz, oupton, joey.gouly, suzuki.poulose, kvmarm, zengheng4,
linux-doc
In-Reply-To: <20260313144617.3420416-28-ben.horgan@arm.com>
On 3/14/26 12:46 AM, Ben Horgan wrote:
> Add the boilerplate that tells resctrl about the mpam monitors that are
> available. resctrl expects all (non-telemetry) monitors to be on the L3 and
> so advertise them there and invent an L3 resctrl resource if required. The
> L3 cache itself has to exist as the cache ids are used as the domain
> ids.
>
> Bring the resctrl monitor domains online and offline based on the cpus
> they contain.
>
> Support for specific monitor types is left to later.
>
> Tested-by: Punit Agrawal <punit.agrawal@oss.qualcomm.com>
> Reviewed-by: Zeng Heng <zengheng4@huawei.com>
> Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
> Signed-off-by: Ben Horgan <ben.horgan@arm.com>
> ---
> New patch but mostly moved from the existing patches to
> separate the monitors from the controls and the boilerplate
> from the specific counters.
> Use l3->mon_capable in resctrl_arch_mon_capable() as
> resctrl_enable_mon_event() now returns a bool.
>
> Changes since v5:
> Use r->mon_capable instead of resctrl_arch_mon_capable() as specific
> to the resource
> Comment line wrap
> Include offline_ctrl_domain cleanup from controls boilerplate patch
> Include any_mon_comp finding and
> Halve num_rmid when cdp_enabled
> Move mpam_resctrl_get_mon_domain_from_cpu() from boilerplate patch
> ---
> drivers/resctrl/mpam_internal.h | 15 +++
> drivers/resctrl/mpam_resctrl.c | 231 ++++++++++++++++++++++++++++++--
> 2 files changed, 235 insertions(+), 11 deletions(-)
>
Reviewed-by: Gavin Shan <gshan@redhat.com>
^ permalink raw reply
* Re: [PATCH v27 3/4] i2c: ast2600: Add controller driver for AST2600 new register set
From: Jeremy Kerr @ 2026-03-24 3:37 UTC (permalink / raw)
To: Ryan Chen, andriy.shevchenko, Andi Shyti, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Joel Stanley, Andrew Jeffery,
Benjamin Herrenschmidt, Philipp Zabel
Cc: linux-i2c, devicetree, linux-arm-kernel, linux-aspeed,
linux-kernel, openbmc
In-Reply-To: <20260324-upstream_i2c-v27-3-f19b511c8c28@aspeedtech.com>
Hi Ryan,
> +static void ast2600_i2c_set_xfer_mode(struct ast2600_i2c_bus *i2c_bus,
> + enum xfer_mode mode)
> +{
> + i2c_bus->mode = mode;
> +
> + switch (mode) {
> + case DMA_MODE:
> + i2c_bus->setup_tx = ast2600_i2c_setup_dma_tx;
> + i2c_bus->setup_rx = ast2600_i2c_setup_dma_rx;
> + break;
> + case BYTE_MODE:
> + i2c_bus->setup_tx = ast2600_i2c_setup_byte_tx;
> + i2c_bus->setup_rx = ast2600_i2c_setup_byte_rx;
> + break;
> + case BUFF_MODE:
> + default:
> + i2c_bus->setup_tx = ast2600_i2c_setup_buff_tx;
> + i2c_bus->setup_rx = ast2600_i2c_setup_buff_rx;
> + break;
> + }
> +}
> +
> +static int ast2600_i2c_xfer_mode_parse(struct ast2600_i2c_bus *i2c_bus,
> + const char *buf, enum xfer_mode *mode)
> +{
> + if (sysfs_streq(buf, "byte")) {
> + *mode = BYTE_MODE;
> + return 0;
> + }
> +
> + if (sysfs_streq(buf, "buffer")) {
> + if (!i2c_bus->buf_base)
> + return -EINVAL;
> + *mode = BUFF_MODE;
> + return 0;
> + }
> +
> + if (sysfs_streq(buf, "dma")) {
> + if (!i2c_bus->dma_available)
> + return -EINVAL;
> + *mode = DMA_MODE;
> + return 0;
> + }
> +
> + return -EINVAL;
> +}
I would suggest separating the string parsing from the "is the mode
available" logic, more on that below.
> +
> +static const char *ast2600_i2c_xfer_mode_name(enum xfer_mode mode)
> +{
> + switch (mode) {
> + case BYTE_MODE:
> + return "byte";
> + case DMA_MODE:
> + return "dma";
> + case BUFF_MODE:
> + default:
> + return "buffer";
> + }
> +}
> +
> +static ssize_t xfer_mode_show(struct device *dev, struct device_attribute *attr, char *buf)
> +{
> + struct ast2600_i2c_bus *i2c_bus = dev_get_drvdata(dev);
> +
> + return sysfs_emit(buf, "%s\n", ast2600_i2c_xfer_mode_name(i2c_bus->mode));
> +}
> +
> +static ssize_t xfer_mode_store(struct device *dev,
> + struct device_attribute *attr,
> + const char *buf, size_t count)
> +{
> + struct ast2600_i2c_bus *i2c_bus = dev_get_drvdata(dev);
> + enum xfer_mode mode;
> + int ret;
> +
> + ret = ast2600_i2c_xfer_mode_parse(i2c_bus, buf, &mode);
> + if (ret)
> + return ret;
> +
> + i2c_lock_bus(&i2c_bus->adap, I2C_LOCK_ROOT_ADAPTER);
> + ast2600_i2c_set_xfer_mode(i2c_bus, mode);
> + i2c_unlock_bus(&i2c_bus->adap, I2C_LOCK_ROOT_ADAPTER);
> +
> + return count;
> +}
> +
> +static DEVICE_ATTR_RW(xfer_mode);
This will need sysfs ABI documentation.
> +
> +static int ast2600_i2c_probe(struct platform_device *pdev)
> +{
> + struct device *dev = &pdev->dev;
> + struct ast2600_i2c_bus *i2c_bus;
> + struct reset_control *rst;
> + struct resource *res;
> + u32 global_ctrl;
> + int ret;
> +
> + if (!device_property_present(dev, "aspeed,global-regs"))
> + return -ENODEV;
> +
> + i2c_bus = devm_kzalloc(dev, sizeof(*i2c_bus), GFP_KERNEL);
> + if (!i2c_bus)
> + return -ENOMEM;
> +
> + i2c_bus->reg_base = devm_platform_ioremap_resource(pdev, 0);
> + if (IS_ERR(i2c_bus->reg_base))
> + return PTR_ERR(i2c_bus->reg_base);
> +
> + rst = devm_reset_control_get_shared_deasserted(dev, NULL);
> + if (IS_ERR(rst))
> + return dev_err_probe(dev, PTR_ERR(rst), "Missing reset ctrl\n");
> +
> + i2c_bus->global_regs =
> + syscon_regmap_lookup_by_phandle(dev_of_node(dev), "aspeed,global-regs");
> + if (IS_ERR(i2c_bus->global_regs))
> + return PTR_ERR(i2c_bus->global_regs);
> +
> + regmap_read(i2c_bus->global_regs, AST2600_I2CG_CTRL, &global_ctrl);
> + if ((global_ctrl & AST2600_GLOBAL_INIT) != AST2600_GLOBAL_INIT) {
> + regmap_write(i2c_bus->global_regs, AST2600_I2CG_CTRL, AST2600_GLOBAL_INIT);
> + regmap_write(i2c_bus->global_regs, AST2600_I2CG_CLK_DIV_CTRL, I2CCG_DIV_CTRL);
> + }
> +
> + i2c_bus->dev = dev;
> + i2c_bus->multi_master = device_property_read_bool(dev, "multi-master");
> + i2c_bus->dma_available = device_property_read_bool(dev, "aspeed,enable-dma");
> + if (i2c_bus->dma_abailable)
dma_abailable? you didn't even build this? :(
> + i2c_bus->mode = DMA_MODE;
> + else
> + i2c_bus->mode = BUFF_MODE;
> +
> + if (i2c_bus->mode == BUFF_MODE) {
> + i2c_bus->buf_base = devm_platform_get_and_ioremap_resource(pdev, 1, &res);
So you only set ->buf_base if we are in buffer mode during probe.
However, the ->buf_base check in xfer_mode_parse() will fail when trying
to change from any other mode to buffer mode. This means you can never
select buffer mode after probe.
> + if (IS_ERR(i2c_bus->buf_base))
> + i2c_bus->mode = BYTE_MODE;
> + else
> + i2c_bus->buf_size = resource_size(res) / 2;
> + }
> +
> + ast2600_i2c_set_xfer_mode(i2c_bus, i2c_bus->mode);
Minor: set_xfer_mode() sets i2c_bus->mode itself. Maybe you want a
temporary instead, or change the semantics of that?
> +
> + /*
> + * i2c timeout counter: use base clk4 1Mhz,
> + * per unit: 1/(1000/1024) = 1024us
> + */
> + ret = device_property_read_u32(dev, "i2c-scl-clk-low-timeout-us", &i2c_bus->timeout);
> + if (!ret)
> + i2c_bus->timeout = DIV_ROUND_UP(i2c_bus->timeout, 1024);
> +
> + init_completion(&i2c_bus->cmd_complete);
> +
> + i2c_bus->irq = platform_get_irq(pdev, 0);
> + if (i2c_bus->irq < 0)
> + return i2c_bus->irq;
> +
> + platform_set_drvdata(pdev, i2c_bus);
> +
> + i2c_bus->clk = devm_clk_get(i2c_bus->dev, NULL);
> + if (IS_ERR(i2c_bus->clk))
> + return dev_err_probe(i2c_bus->dev, PTR_ERR(i2c_bus->clk), "Can't get clock\n");
> +
> + i2c_bus->apb_clk = clk_get_rate(i2c_bus->clk);
> +
> + i2c_parse_fw_timings(i2c_bus->dev, &i2c_bus->timing_info, true);
> +
> + /* Initialize the I2C adapter */
> + i2c_bus->adap.owner = THIS_MODULE;
> + i2c_bus->adap.algo = &i2c_ast2600_algorithm;
> + i2c_bus->adap.retries = 0;
> + i2c_bus->adap.dev.parent = i2c_bus->dev;
> + device_set_node(&i2c_bus->adap.dev, dev_fwnode(dev));
> + i2c_bus->adap.algo_data = i2c_bus;
> + strscpy(i2c_bus->adap.name, pdev->name);
> + i2c_set_adapdata(&i2c_bus->adap, i2c_bus);
> +
> + ret = ast2600_i2c_init(i2c_bus);
> + if (ret < 0)
> + return dev_err_probe(dev, ret, "Unable to initial i2c %d\n", ret);
Super minor: `initial` is not a verb in this context, you want
`initialise` (Australian) or `initialize` (US) or `init` (keeping
everyone happy)
> +
> + ret = devm_request_irq(dev, i2c_bus->irq, ast2600_i2c_bus_irq, 0,
> + dev_name(dev), i2c_bus);
> + if (ret < 0) {
> + ret = dev_err_probe(dev, ret, "Unable to request irq %d\n",
> + i2c_bus->irq);
> + goto err;
> + }
> +
> + writel(AST2600_I2CM_PKT_DONE | AST2600_I2CM_BUS_RECOVER,
> + i2c_bus->reg_base + AST2600_I2CM_IER);
> +
> + ret = devm_i2c_add_adapter(dev, &i2c_bus->adap);
> + if (ret)
> + goto err;
> +
> + ret = sysfs_create_file(&dev->kobj, &dev_attr_xfer_mode.attr);
> + if (ret)
> + goto err;
This error path will fail the probe but not unregister the i2c adapter.
You probably want to only register the adapter last (and remove the
sysfs file if that fails).
> +
> + return 0;
> +
> +err:
> + writel(0, i2c_bus->reg_base + AST2600_I2CC_FUN_CTRL);
> + writel(0, i2c_bus->reg_base + AST2600_I2CM_IER);
> + return ret;
> +}
> +
> +static void ast2600_i2c_remove(struct platform_device *pdev)
> +{
> + struct ast2600_i2c_bus *i2c_bus = platform_get_drvdata(pdev);
> +
> + sysfs_remove_file(&pdev->dev.kobj, &dev_attr_xfer_mode.attr);
> +
> + /* Disable everything. */
> + writel(0, i2c_bus->reg_base + AST2600_I2CC_FUN_CTRL);
> + writel(0, i2c_bus->reg_base + AST2600_I2CM_IER);
> +}
> +
> +static const struct of_device_id ast2600_i2c_of_match[] = {
> + { .compatible = "aspeed,ast2600-i2c-bus" },
> + { }
> +};
> +MODULE_DEVICE_TABLE(of, ast2600_i2c_of_match);
> +
> +static struct platform_driver ast2600_i2c_driver = {
> + .probe = ast2600_i2c_probe,
> + .remove = ast2600_i2c_remove,
> + .driver = {
> + .name = "ast2600-i2c",
> + .of_match_table = ast2600_i2c_of_match,
> + },
> +};
> +module_platform_driver(ast2600_i2c_driver);
> +
> +MODULE_AUTHOR("Ryan Chen <ryan_chen@aspeedtech.com>");
> +MODULE_DESCRIPTION("ASPEED AST2600 I2C Controller Driver");
> +MODULE_LICENSE("GPL");
Cheers,
Jeremy
^ permalink raw reply
* [PATCH 2/2] [v3] spi: spi-mtk-nor: Modify and optimization the SNFC.
From: Meiker Gao @ 2026-03-24 3:26 UTC (permalink / raw)
To: Mark Brown, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Matthias Brugger, AngeloGioacchino Del Regno
Cc: Bayi Cheng, Project_Global_Chrome_Upstream_Group, sirius.wang,
vince-wl.liu, jh.hsu, linux-spi, devicetree, linux-kernel,
linux-arm-kernel, linux-mediatek, Meiker Gao
In-Reply-To: <20260324032624.1708029-1-ot_meiker.gao@mediatek.com>
Changes in v3:
-this patch is a further optimization for version v2.
Changes in v2:
-Use clk_bulk_xxx related functions to enable/disable clocks.
Changes in v1:
-Add new function mtk_nor_parse_clk() to parse nor clock parameters.
Signed-off-by: Meiker Gao <ot_meiker.gao@mediatek.com>
Change-Id: Ifa13f9f08aebf6feb376ca98b0fd69f379037ff3
---
drivers/spi/spi-mtk-nor.c | 19 ++++++++-----------
1 file changed, 8 insertions(+), 11 deletions(-)
diff --git a/drivers/spi/spi-mtk-nor.c b/drivers/spi/spi-mtk-nor.c
index 702339a6c817..6d24932053e5 100644
--- a/drivers/spi/spi-mtk-nor.c
+++ b/drivers/spi/spi-mtk-nor.c
@@ -100,7 +100,6 @@
#define CLK_TO_US(sp, clkcnt) DIV_ROUND_UP(clkcnt, sp->spi_freq / 1000000)
-#define MAX_CLOCK_CNT 6
struct mtk_nor_caps {
u8 dma_bits;
@@ -119,8 +118,8 @@ struct mtk_nor {
void __iomem *base;
u8 *buffer;
dma_addr_t buffer_dma;
- struct clk_bulk_data clocks[MAX_CLOCK_CNT];
- int clock_cnt;
+ struct clk_bulk_data *clocks;
+ u8 clock_cnt;
unsigned int spi_freq;
bool wbuf_en;
bool has_irq;
@@ -733,19 +732,16 @@ static int mtk_nor_enable_clk(struct mtk_nor *sp)
static int mtk_nor_parse_clk(struct device *dev, struct mtk_nor *sp)
{
struct device_node *np = dev->of_node;
- int ret;
const char *name;
- int cnt,i;
+ int cnt,i,ret;
cnt = of_property_count_strings(np, "clock-names");
if (!cnt || (cnt == -EINVAL)) {
dev_err(dev, "Unable to find clocks\n");
- ret = -EINVAL;
- goto out;
+ return -EINVAL;
} else if (cnt < 0) {
dev_err(dev, "Count clock strings failed, err %d\n", cnt);
- ret = cnt;
- goto out;
+ return cnt;
}
sp->clock_cnt = cnt;
@@ -760,9 +756,10 @@ static int mtk_nor_parse_clk(struct device *dev, struct mtk_nor *sp)
}
ret = devm_clk_bulk_get(dev, sp->clock_cnt, sp->clocks);
+ if (ret)
+ return ret;
-out:
- return ret;
+ return 0;
}
static void mtk_nor_init(struct mtk_nor *sp)
--
2.45.2
^ permalink raw reply related
* [PATCH 1/2] dt-bindings: spi: Fix clock-names definition
From: Meiker Gao @ 2026-03-24 3:26 UTC (permalink / raw)
To: Mark Brown, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Matthias Brugger, AngeloGioacchino Del Regno
Cc: Bayi Cheng, Project_Global_Chrome_Upstream_Group, sirius.wang,
vince-wl.liu, jh.hsu, linux-spi, devicetree, linux-kernel,
linux-arm-kernel, linux-mediatek, Meiker Gao
In-Reply-To: <20260324032624.1708029-1-ot_meiker.gao@mediatek.com>
Update the device tree binding for the Mediatek
MT8196 NOR controller to require that the 'clock-names'
property contains exactly six entries, in the strict order:
"spi", "sf", "axi", "axi_s", "bclk", "27m".
Signed-off-by: Meiker Gao <ot_meiker.gao@mediatek.com>
Change-Id: I0542d6d726f1af34ef76c1a58ba4e4b02ec45fe6
---
.../devicetree/bindings/spi/mediatek,spi-mtk-nor.yaml | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/Documentation/devicetree/bindings/spi/mediatek,spi-mtk-nor.yaml b/Documentation/devicetree/bindings/spi/mediatek,spi-mtk-nor.yaml
index a453996c13f2..f605a36e753c 100644
--- a/Documentation/devicetree/bindings/spi/mediatek,spi-mtk-nor.yaml
+++ b/Documentation/devicetree/bindings/spi/mediatek,spi-mtk-nor.yaml
@@ -56,6 +56,10 @@ properties:
design, so this is optional.
- description: clock used for controller axi slave bus.
this depends on hardware design, so it is optional.
+ - description: clock used for bclk.
+ this depends on hardware design, so it is optional.
+ - description: clock used for 27m.
+ this depends on hardware design, so it is optional.
clock-names:
minItems: 2
@@ -64,6 +68,8 @@ properties:
- const: sf
- const: axi
- const: axi_s
+ - const: bclk
+ - const: 27m
required:
- compatible
--
2.45.2
^ permalink raw reply related
* [PATCH 0/2] *** SUBJECT HERE ***
From: Meiker Gao @ 2026-03-24 3:26 UTC (permalink / raw)
To: Mark Brown, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Matthias Brugger, AngeloGioacchino Del Regno
Cc: Bayi Cheng, Project_Global_Chrome_Upstream_Group, sirius.wang,
vince-wl.liu, jh.hsu, linux-spi, devicetree, linux-kernel,
linux-arm-kernel, linux-mediatek, Meiker Gao
*** BLURB HERE ***
This patch series refactors the clock management for the Mediatek SPI NOR driver,
making it more flexible and compatible with different platforms.
Patch 1 updates the device tree binding to support variable clock-names.
Patch 2 migrates the driver to use clk_bulk API and optimizes error handling.
Changes in v1:
- Dynamically allocate clk_bulk_data array.
- Simplify error handling.
- Code cleanup and style improvements.
Meiker Gao (2):
dt-bindings: spi: Fix clock-names definition
[v3] spi: spi-mtk-nor: Modify and optimization the SNFC.
.../bindings/spi/mediatek,spi-mtk-nor.yaml | 6 ++++++
drivers/spi/spi-mtk-nor.c | 19 ++++++++-----------
2 files changed, 14 insertions(+), 11 deletions(-)
--
2.45.2
^ permalink raw reply
* Re: [PATCH 1/2] arm64: dts: imx8qm-mek: switch Type-C connector power-role to dual
From: Xu Yang @ 2026-03-24 3:15 UTC (permalink / raw)
To: Frank Li
Cc: robh, krzk+dt, conor+dt, s.hauer, kernel, festevam, devicetree,
imx, linux-arm-kernel, linux-kernel, jun.li
In-Reply-To: <acGDbMKECQ3xPdTA@lizhi-Precision-Tower-5810>
On Mon, Mar 23, 2026 at 02:16:12PM -0400, Frank Li wrote:
> On Mon, Mar 23, 2026 at 07:09:22PM +0800, Xu Yang wrote:
> > When attach to PC Type-A port, the USB device controller does not function
> > at all. Because it is configured as source-only and a Type-A port doesn't
> > support PD capability, a data role swap is impossible.
> >
> > Actually, PTN5110THQ is configured for DFP/Source role only at POR, but
> > after POR it can operate as a DRP. By switching the power-role to dual,
> > the port can operate as a sink and enter device mode when attached to
> > Type-A port.
>
> when first use term DFP/DRP,
> DFP (Downstream Facing Port) ...
>
> Except well known term, like POR.
OK.
>
> >
> > Since the board design uses EN_SRC to control the 5V VBUS path and EN_SNK
> > to control the 12V VBUS output,
>
> I remember a hardware rework to fix this problem.
Yes, PTN5110THQ is EOL, then PTN5110NHQ is used as a replacement. Since PTN5110NHQ
works as DRP at POR, then this will bring an issue that PTN5110NHQ may work as
a sink when attached to PC then EN_SNK will be enabled and 12V will be output to
PC and this will damage the PC. A hardware rework is done to change PTN5110NHQ to
DFP/Source role at POR to avoid the issue.
The hardware rework and this patch fix different issues.
>
> > to avoid outputting a higher VBUS when in
> > sink role, we set the operation current limit to 0mA so that SW will not
> > control EN_SNK at all.
> >
> > Signed-off-by: Xu Yang <xu.yang_2@nxp.com>
> > ---
> > arch/arm64/boot/dts/freescale/imx8qm-mek.dts | 6 +++++-
> > 1 file changed, 5 insertions(+), 1 deletion(-)
> >
> > diff --git a/arch/arm64/boot/dts/freescale/imx8qm-mek.dts b/arch/arm64/boot/dts/freescale/imx8qm-mek.dts
> > index dadc136aec6e..8a832a0e105d 100644
> > --- a/arch/arm64/boot/dts/freescale/imx8qm-mek.dts
> > +++ b/arch/arm64/boot/dts/freescale/imx8qm-mek.dts
> > @@ -611,9 +611,13 @@ ptn5110: tcpc@51 {
> > usb_con1: connector {
> > compatible = "usb-c-connector";
> > label = "USB-C";
> > - power-role = "source";
> > + power-role = "dual";
> > data-role = "dual";
> > + try-power-role = "sink";
> > source-pdos = <PDO_FIXED(5000, 3000, PDO_FIXED_USB_COMM)>;
> > + sink-pdos = <PDO_FIXED(5000, 0, PDO_FIXED_USB_COMM)>;
> > + op-sink-microwatt = <0>;
>
> Need comment here.
OK.
Thanks,
Xu Yang
^ permalink raw reply
* Re: [PATCH 1/2] arm64/entry: Fix involuntary preemption exception masking
From: Jinjie Ruan @ 2026-03-24 3:14 UTC (permalink / raw)
To: Mark Rutland, linux-arm-kernel
Cc: vladimir.murzin, peterz, catalin.marinas, linux-kernel, tglx,
luto, will
In-Reply-To: <20260320113026.3219620-2-mark.rutland@arm.com>
On 2026/3/20 19:30, Mark Rutland wrote:
> On arm64, involuntary kernel preemption has been subtly broken since the
> move to the generic irq entry code. When preemption occurs, the new task
> may run with SError and Debug exceptions masked unexpectedly, leading to
> a loss of RAS events, breakpoints, watchpoints, and single-step
> exceptions.
We can also add a check in arch_irqentry_exit_need_resched to prevent
schedule-out when the DA bit is set.
>
> We can fix this relatively simply by moving the preemption logic out of
> irqentry_exit(), which is desirable for a number of other reasons on
> arm64. Context and rationale below:
>
> 1) Architecturally, several groups of exceptions can be masked
> independently, including 'Debug', 'SError', 'IRQ', and 'FIQ', whose
> mask bits can be read/written via the 'DAIF' register.
>
> Other mask bits exist, including 'PM' and 'AllInt', which we will
> need to use in future (e.g. for architectural NMI support).
>
> The entry code needs to manipulate all of these, but the generic
> entry code only knows about interrupts (which means both IRQ and FIQ
> on arm64), and the other exception masks aren't generic.
>
> 2) Architecturally, all maskable exceptions MUST be masked during
> exception entry and exception return.
>
> Upon exception entry, hardware places exception context into
> exception registers (e.g. the PC is saved into ELR_ELx). Upon
> exception return, hardware restores exception context from those
> exception registers (e.g. the PC is restored from ELR_ELx).
>
> To ensure the exception registers aren't clobbered by recursive
> exceptions, all maskable exceptions must be masked early during entry
> and late during exit. Hardware masks all maskable exceptions
> automatically at exception entry. Software must unmask these as
> required, and must mask them prior to exception return.
>
> 3) Architecturally, hardware masks all maskable exceptions upon any
> exception entry. A synchronous exception (e.g. a fault on a memory
> access) can be taken from any context (e.g. where IRQ+FIQ might be
> masked), and the entry code must explicitly 'inherit' the unmasking
> from the original context by reading the exception registers (e.g.
> SPSR_ELx) and writing to DAIF, etc.
>
> 4) When 'pseudo-NMI' is used, Linux masks interrupts via a combination
> of DAIF and the 'PMR' priority mask register. At entry and exit,
> interrupts must be masked via DAIF, but most kernel code will
> mask/unmask regular interrupts using PMR (e.g. in local_irq_save()
> and local_irq_restore()).
>
> This requires more complicated transitions at entry and exit. Early
> during entry or late during return, interrupts are masked via DAIF,
> and kernel code which manipulates PMR to mask/unmask interrupts will
> not function correctly in this state.
>
> This also requires fairly complicated management of DAIF and PMR when
> handling interrupts, and arm64 has special logic to avoid preempting
> from pseudo-NMIs which currently lives in
> arch_irqentry_exit_need_resched().
>
> 5) Most kernel code runs with all exceptions unmasked. When scheduling,
> only interrupts should be masked (by PMR pseudo-NMI is used, and by
> DAIF otherwise).
>
> For most exceptions, arm64's entry code has a sequence similar to that
> of el1_abort(), which is used for faults:
>
> | static void noinstr el1_abort(struct pt_regs *regs, unsigned long esr)
> | {
> | unsigned long far = read_sysreg(far_el1);
> | irqentry_state_t state;
> |
> | state = enter_from_kernel_mode(regs);
> | local_daif_inherit(regs);
> | do_mem_abort(far, esr, regs);
> | local_daif_mask();
> | exit_to_kernel_mode(regs, state);
> | }
>
> ... where enter_from_kernel_mode() and exit_to_kernel_mode() are
> wrappers around irqentry_enter() and irqentry_exit() which perform
> additional arm64-specific entry/exit logic.
>
> Currently, the generic irq entry code will attempt to preempt from any
> exception under irqentry_exit() where interrupts were unmasked in the
> original context. As arm64's entry code will have already masked
> exceptions via DAIF, this results in the problems described above.
>
> Fix this by opting out of preemption in irqentry_exit(), and restoring
> arm64's old behaivour of explicitly preempting when returning from IRQ
> or FIQ, before calling exit_to_kernel_mode() / irqentry_exit(). This
> ensures that preemption occurs when only interrupts are masked, and
> where that masking is compatible with most kernel code (e.g. using PMR
> when pseudo-NMI is in use).
>
> Fixes: 99eb057ccd67 ("arm64: entry: Move arm64_preempt_schedule_irq() into __exit_to_kernel_mode()")
> Reported-by: Ada Couprie Diaz <ada.coupriediaz@arm.com>
> Reported-by: Vladimir Murzin <vladimir.murzin@arm.com>
> Signed-off-by: Mark Rutland <mark.rutland@arm.com>
> Cc: Andy Lutomirski <luto@kernel.org>
> Cc: Catalin Marinas <catalin.marinas@arm.com>
> Cc: Jinjie Ruan <ruanjinjie@huawei.com>
> Cc: Peter Zijlstra <peterz@infradead.org>
> Cc: Thomas Gleixner <tglx@kernel.org>
> Cc: Will Deacon <will@kernel.org>
> ---
> arch/Kconfig | 3 +++
> arch/arm64/Kconfig | 1 +
> arch/arm64/kernel/entry-common.c | 2 ++
> kernel/entry/common.c | 4 +++-
> 4 files changed, 9 insertions(+), 1 deletion(-)
>
> Thomas, Peter, I have a couple of things I'd like to check:
>
> (1) The generic irq entry code will preempt from any exception (e.g. a
> synchronous fault) where interrupts were unmasked in the original
> context. Is that intentional/necessary, or was that just the way the
> x86 code happened to be implemented?
>
> I assume that it'd be fine if arm64 only preempted from true
> interrupts, but if that was intentional/necessary I can go rework
> this.
>
> (2) The generic irq entry code only preempts when RCU was watching in
> the original context. IIUC that's just to avoid preempting from the
> idle thread. Is it functionally necessary to avoid that, or is that
> just an optimization?
>
> I'm asking because historically arm64 didn't check that, and I
> haven't bothered checking here. I don't know whether we have a
> latent functional bug.
>
> Mark.
>
> diff --git a/arch/Kconfig b/arch/Kconfig
> index 102ddbd4298ef..c8c99cd955281 100644
> --- a/arch/Kconfig
> +++ b/arch/Kconfig
> @@ -102,6 +102,9 @@ config HOTPLUG_PARALLEL
> bool
> select HOTPLUG_SPLIT_STARTUP
>
> +config ARCH_HAS_OWN_IRQ_PREEMPTION
> + bool
> +
> config GENERIC_IRQ_ENTRY
> bool
>
> diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
> index 38dba5f7e4d2d..bf0ec8237de45 100644
> --- a/arch/arm64/Kconfig
> +++ b/arch/arm64/Kconfig
> @@ -42,6 +42,7 @@ config ARM64
> select ARCH_HAS_NMI_SAFE_THIS_CPU_OPS
> select ARCH_HAS_NON_OVERLAPPING_ADDRESS_SPACE
> select ARCH_HAS_NONLEAF_PMD_YOUNG if ARM64_HAFT
> + select ARCH_HAS_OWN_IRQ_PREEMPTION
> select ARCH_HAS_PREEMPT_LAZY
> select ARCH_HAS_PTDUMP
> select ARCH_HAS_PTE_SPECIAL
> diff --git a/arch/arm64/kernel/entry-common.c b/arch/arm64/kernel/entry-common.c
> index 3625797e9ee8f..1aedadf09eb4d 100644
> --- a/arch/arm64/kernel/entry-common.c
> +++ b/arch/arm64/kernel/entry-common.c
> @@ -497,6 +497,8 @@ static __always_inline void __el1_irq(struct pt_regs *regs,
> do_interrupt_handler(regs, handler);
> irq_exit_rcu();
>
> + irqentry_exit_cond_resched();
> +
> exit_to_kernel_mode(regs, state);
> }
> static void noinstr el1_interrupt(struct pt_regs *regs,
> diff --git a/kernel/entry/common.c b/kernel/entry/common.c
> index 9ef63e4147913..af9cae1f225e3 100644
> --- a/kernel/entry/common.c
> +++ b/kernel/entry/common.c
> @@ -235,8 +235,10 @@ noinstr void irqentry_exit(struct pt_regs *regs, irqentry_state_t state)
> }
>
> instrumentation_begin();
> - if (IS_ENABLED(CONFIG_PREEMPTION))
> + if (IS_ENABLED(CONFIG_PREEMPTION) &&
> + !IS_ENABLED(CONFIG_ARCH_HAS_OWN_IRQ_PREEMPTION)) {
> irqentry_exit_cond_resched();
> + }
>
> /* Covers both tracing and lockdep */
> trace_hardirqs_on();
^ permalink raw reply
* [PATCH] arm64: dts: imx8mp-debix-model-a: Correct PAD settings for pmicirqgrp
From: Peng Fan (OSS) @ 2026-03-24 3:16 UTC (permalink / raw)
To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Frank Li,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam,
Marco Felsch, Laurent Pinchart, Daniel Scally
Cc: devicetree, imx, linux-arm-kernel, linux-kernel, Peng Fan
From: Peng Fan <peng.fan@nxp.com>
With commit 5d0efaf47ee90 ("regulator: pca9450: Correct interrupt type"),
there is interrupt storm for i.MX8MP DEBIX Model A. Per schematic, there
is no on board PULL-UP resistors for GPIO1_IO03, so need to set PAD
PUE and PU together to make pull up work properly.
Fixes: c86d350aae68e ("arm64: dts: Add device tree for the Debix Model A Board")
Reported-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Closes: https://lore.kernel.org/all/20260323105858.GA2185714@killaraus.ideasonboard.com/
Signed-off-by: Peng Fan <peng.fan@nxp.com>
---
arch/arm64/boot/dts/freescale/imx8mp-debix-model-a.dts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/arm64/boot/dts/freescale/imx8mp-debix-model-a.dts b/arch/arm64/boot/dts/freescale/imx8mp-debix-model-a.dts
index 9422beee30b29c5a551b08476c80fbff96af3439..df7489587e48ed0c678f11291f6f2b77082ade95 100644
--- a/arch/arm64/boot/dts/freescale/imx8mp-debix-model-a.dts
+++ b/arch/arm64/boot/dts/freescale/imx8mp-debix-model-a.dts
@@ -440,7 +440,7 @@ MX8MP_IOMUXC_SAI5_RXC__I2C6_SDA 0x400001c3
pinctrl_pmic: pmicirqgrp {
fsl,pins = <
- MX8MP_IOMUXC_GPIO1_IO03__GPIO1_IO03 0x41
+ MX8MP_IOMUXC_GPIO1_IO03__GPIO1_IO03 0x000001c0
>;
};
---
base-commit: 09c0f7f1bcdbc3c37a5a760cbec76bf18f278406
change-id: 20260324-imx8mp-dts-fix-512530fe4dcd
Best regards,
--
Peng Fan <peng.fan@nxp.com>
^ permalink raw reply related
* Re: [PATCH v27 2/4] dt-bindings: i2c: ast2600-i2c.yaml: Add global-regs and transfer-mode properties
From: Jeremy Kerr @ 2026-03-24 3:11 UTC (permalink / raw)
To: Ryan Chen, andriy.shevchenko, Andi Shyti, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Joel Stanley, Andrew Jeffery,
Benjamin Herrenschmidt, Rayn Chen, Philipp Zabel
Cc: linux-i2c, devicetree, linux-arm-kernel, linux-aspeed,
linux-kernel, openbmc
In-Reply-To: <20260324-upstream_i2c-v27-2-f19b511c8c28@aspeedtech.com>
Hi Ryan,
> The AST2600 I2C controller supports three transfer modes (byte, buffer,
> DMA). Add "aspeed,transfer-mode" so DT can select the preferred transfer
> method per controller instance.
This patch does not add an aspeed,transfer-mode property.
> Also add the "aspeed,global-regs"
> phandle to reference the AST2600 global registers syscon/regmap used by
> the controller.
>
> These properties apply only to the AST2600 binding and are not part of
> the legacy binding, which uses a mixed controller/target register layout
> and does not have the split register blocks or these new configuration
> registers. Legacy DTs remain unchanged.
>
> Signed-off-by: Ryan Chen <ryan_chen@aspeedtech.com>
> ---
> Changes in v27:
> - change aspeed,transfer-mode to aspeed,enable-dma.
What about all the previous changes?
> ---
> .../devicetree/bindings/i2c/aspeed,ast2600-i2c.yaml | 17 +++++++++++++++++
> 1 file changed, 17 insertions(+)
>
> diff --git a/Documentation/devicetree/bindings/i2c/aspeed,ast2600-i2c.yaml b/Documentation/devicetree/bindings/i2c/aspeed,ast2600-i2c.yaml
> index de2c359037da..38da6fc6424f 100644
> --- a/Documentation/devicetree/bindings/i2c/aspeed,ast2600-i2c.yaml
> +++ b/Documentation/devicetree/bindings/i2c/aspeed,ast2600-i2c.yaml
> @@ -37,6 +37,21 @@ properties:
> resets:
> maxItems: 1
>
> + aspeed,enable-dma:
> + type: boolean
> + description: |
> + I2C bus enable dma mode transfer.
> +
> + ASPEED ast2600 platform equipped with 16 I2C controllers that share a
> + single DMA engine. DTS files can specify the data transfer mode to/from
> + the device, either DMA or programmed I/O.
As we had discussed: this does not define the transfer mode, only
whether DMA is available to the peripheral.
Why mention the 16 i2c controllers here?
Please keep this description simple and relevant to the specific purpose
of the property.
> +
> + aspeed,global-regs:
> + $ref: /schemas/types.yaml#/definitions/phandle
> + description:
> + Phandle reference to the i2c global syscon node, containing the
> + SoC-common i2c register set.
> +
> required:
> - reg
> - compatible
> @@ -59,4 +74,6 @@ examples:
> resets = <&syscon ASPEED_RESET_I2C>;
> clock-frequency = <100000>;
> interrupts = <GIC_SPI 110 IRQ_TYPE_LEVEL_HIGH>;
> + aspeed,global-regs = <&i2c_global>;
> + aspeed,transfer-mode = "buffer";
This example does not match the binding.
Cheers,
Jeremy
^ permalink raw reply
* Re: [PATCH] arm64: dts: allwinner: sun55i: Fix r-spi DMA
From: Chen-Yu Tsai @ 2026-03-24 3:08 UTC (permalink / raw)
To: Jernej Skrabec, Samuel Holland, Chen-Yu Tsai
Cc: linux-sunxi, devicetree, linux-arm-kernel, linux-kernel
In-Reply-To: <20260323171927.1256507-1-wens@kernel.org>
On Tue, 24 Mar 2026 01:19:26 +0800, Chen-Yu Tsai wrote:
> r-spi has DRQs for both the main and MCU DMA controllers on the A523 SoC
> family, however it seems it that it is mainly routed to the MCU DMA
> controller, with no obvious way to change it.
>
> Change the DMA channels of r-spi to the MCU so that it works properly.
>
>
> [...]
Applied to sunxi/fixes-for-7.0 in local tree, thanks!
[1/1] arm64: dts: allwinner: sun55i: Fix r-spi DMA
commit: 6896ca5a9d05275fbeb38640c9bbdb95698de188
Best regards,
--
Chen-Yu Tsai <wens@kernel.org>
^ permalink raw reply
* [PATCH v27 3/4] i2c: ast2600: Add controller driver for AST2600 new register set
From: Ryan Chen @ 2026-03-24 3:06 UTC (permalink / raw)
To: jk, andriy.shevchenko, Andi Shyti, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Joel Stanley, Andrew Jeffery,
Benjamin Herrenschmidt, Rayn Chen, Philipp Zabel
Cc: linux-i2c, devicetree, linux-arm-kernel, linux-aspeed,
linux-kernel, openbmc, Ryan Chen
In-Reply-To: <20260324-upstream_i2c-v27-0-f19b511c8c28@aspeedtech.com>
The AST2600 introduces a new I2C controller register layout, selectable
at runtime via global control registers. Compared to the legacy layout
used on AST2400/AST2500, the new layout separates controller (master)
and target (slave) registers and adds support for packet-based transfers
The new register set extends the hardware capabilities with:
- Enhanced clock divider configuration for improved timing precision
- tCKHighMin timing control for SCL high pulse width
- Dual pool buffer mode (separate Tx/Rx buffers)
- Extended DMA support with larger buffer size and alignment handling
- Dedicated DMA buffers for controller and target directions
- Hardware-assisted bus recovery and timeout mechanisms
This patch adds an AST2600-specific I2C controller driver implementing
the new register layout, including support for packet-based transfers
and byte, buffer and DMA transfer modes.
The legacy and new register layouts represent the same AST2600 I2C
controller IP and therefore share the existing compatible string:
"aspeed,ast2600-i2c-bus"
To preserve DT ABI compatibility, driver selection is performed at probe
time based on DT contents. In particular, the new binding requires the
`aspeed,global-regs` phandle, which is absent from legacy DTBs:
- The new driver only probes successfully when `aspeed,global-regs` is
present.
- The existing i2c-aspeed driver returns -ENODEV for AST2600 nodes that
provide `aspeed,global-regs`, allowing the new driver to bind.
Signed-off-by: Ryan Chen <ryan_chen@aspeedtech.com>
---
Changes in v27:
- remove aspeed,transfer-mode selection instead aspeed,dma-mode.
- add sysfs for xfer mode.
Changes in v25:
- Rename AST2600_I2CM_SMBUS_ALT to AST2600_I2CM_SMBUS_ALERT.
- Refactor transfer mode handling using setup_tx/setup_rx helpers.
- Rework DMA handling to use pre-allocated buffers and reduce
mapping overhead in interrupt context.
- Fix IRQ status checks to use consistent (sts & value) style.
- Move device_property_read_bool() to probe().
- Improve probe error handling.
- Handle timeout condition in target_byte_irq().
- Rename "package" to "packet".
- Remove target reset when master wait_for_completion_timeout().
---
drivers/i2c/busses/Makefile | 2 +-
drivers/i2c/busses/i2c-aspeed.c | 5 +
drivers/i2c/busses/i2c-ast2600.c | 1064 ++++++++++++++++++++++++++++++++++++++
3 files changed, 1070 insertions(+), 1 deletion(-)
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index 547123ab351f..ece201a67d41 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -37,7 +37,7 @@ obj-$(CONFIG_I2C_POWERMAC) += i2c-powermac.o
obj-$(CONFIG_I2C_ALTERA) += i2c-altera.o
obj-$(CONFIG_I2C_AMD_MP2) += i2c-amd-mp2-pci.o i2c-amd-mp2-plat.o
obj-$(CONFIG_I2C_AMD_ASF) += i2c-amd-asf-plat.o
-obj-$(CONFIG_I2C_ASPEED) += i2c-aspeed.o
+obj-$(CONFIG_I2C_ASPEED) += i2c-aspeed.o i2c-ast2600.o
obj-$(CONFIG_I2C_AT91) += i2c-at91.o
i2c-at91-y := i2c-at91-core.o i2c-at91-master.o
i2c-at91-$(CONFIG_I2C_AT91_SLAVE_EXPERIMENTAL) += i2c-at91-slave.o
diff --git a/drivers/i2c/busses/i2c-aspeed.c b/drivers/i2c/busses/i2c-aspeed.c
index a26b74c71206..8286fd2cd130 100644
--- a/drivers/i2c/busses/i2c-aspeed.c
+++ b/drivers/i2c/busses/i2c-aspeed.c
@@ -22,6 +22,7 @@
#include <linux/of_irq.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
+#include <linux/property.h>
#include <linux/reset.h>
#include <linux/slab.h>
@@ -1002,6 +1003,10 @@ static int aspeed_i2c_probe_bus(struct platform_device *pdev)
struct clk *parent_clk;
int irq, ret;
+ if (device_is_compatible(&pdev->dev, "aspeed,ast2600-i2c-bus") &&
+ device_property_present(&pdev->dev, "aspeed,global-regs"))
+ return -ENODEV;
+
bus = devm_kzalloc(&pdev->dev, sizeof(*bus), GFP_KERNEL);
if (!bus)
return -ENOMEM;
diff --git a/drivers/i2c/busses/i2c-ast2600.c b/drivers/i2c/busses/i2c-ast2600.c
new file mode 100644
index 000000000000..cf3006eaded9
--- /dev/null
+++ b/drivers/i2c/busses/i2c-ast2600.c
@@ -0,0 +1,1064 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * ASPEED AST2600 new register set I2C controller driver
+ *
+ * Copyright (C) 2026 ASPEED Technology Inc.
+ */
+#include <linux/array_size.h>
+#include <linux/bits.h>
+#include <linux/clk.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/i2c-smbus.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/minmax.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+#include <linux/slab.h>
+#include <linux/string_helpers.h>
+#include <linux/unaligned.h>
+
+#define AST2600_I2CG_ISR 0x00
+#define AST2600_I2CG_SLAVE_ISR 0x04
+#define AST2600_I2CG_OWNER 0x08
+#define AST2600_I2CG_CTRL 0x0C
+#define AST2600_I2CG_CLK_DIV_CTRL 0x10
+
+#define AST2600_I2CG_SLAVE_PKT_NAK BIT(4)
+#define AST2600_I2CG_M_S_SEPARATE_INTR BIT(3)
+#define AST2600_I2CG_CTRL_NEW_REG BIT(2)
+#define AST2600_I2CG_CTRL_NEW_CLK_DIV BIT(1)
+#define AST2600_GLOBAL_INIT \
+ (AST2600_I2CG_CTRL_NEW_REG | AST2600_I2CG_CTRL_NEW_CLK_DIV)
+/*
+ * APB clk : 100Mhz
+ * div : scl : baseclk [APB/((div/2) + 1)] : tBuf [1/bclk * 16]
+ * I2CG10[31:24] base clk4 for i2c auto recovery timeout counter (0xC6)
+ * I2CG10[23:16] base clk3 for Standard-mode (100Khz) min tBuf 4.7us
+ * 0x3c : 100.8Khz : 3.225Mhz : 4.96us
+ * 0x3d : 99.2Khz : 3.174Mhz : 5.04us
+ * 0x3e : 97.65Khz : 3.125Mhz : 5.12us
+ * 0x40 : 97.75Khz : 3.03Mhz : 5.28us
+ * 0x41 : 99.5Khz : 2.98Mhz : 5.36us (default)
+ * I2CG10[15:8] base clk2 for Fast-mode (400Khz) min tBuf 1.3us
+ * 0x12 : 400Khz : 10Mhz : 1.6us
+ * I2CG10[7:0] base clk1 for Fast-mode Plus (1Mhz) min tBuf 0.5us
+ * 0x08 : 1Mhz : 20Mhz : 0.8us
+ */
+#define I2CCG_DIV_CTRL 0xC6411208
+
+/* 0x00 : I2CC Controller/Target Function Control Register */
+#define AST2600_I2CC_FUN_CTRL 0x00
+#define AST2600_I2CC_SLAVE_ADDR_RX_EN BIT(20)
+#define AST2600_I2CC_MASTER_RETRY_MASK GENMASK(19, 18)
+#define AST2600_I2CC_MASTER_RETRY(x) (((x) & GENMASK(1, 0)) << 18)
+#define AST2600_I2CC_BUS_AUTO_RELEASE BIT(17)
+#define AST2600_I2CC_M_SDA_LOCK_EN BIT(16)
+#define AST2600_I2CC_MULTI_MASTER_DIS BIT(15)
+#define AST2600_I2CC_M_SCL_DRIVE_EN BIT(14)
+#define AST2600_I2CC_MSB_STS BIT(9)
+#define AST2600_I2CC_SDA_DRIVE_1T_EN BIT(8)
+#define AST2600_I2CC_M_SDA_DRIVE_1T_EN BIT(7)
+#define AST2600_I2CC_M_HIGH_SPEED_EN BIT(6)
+/* reserver 5 : 2 */
+#define AST2600_I2CC_SLAVE_EN BIT(1)
+#define AST2600_I2CC_MASTER_EN BIT(0)
+
+/* 0x04 : I2CC Controller/Target Clock and AC Timing Control Register #1 */
+#define AST2600_I2CC_AC_TIMING 0x04
+#define AST2600_I2CC_TTIMEOUT(x) (((x) & GENMASK(4, 0)) << 24)
+#define AST2600_I2CC_TCKHIGHMIN(x) (((x) & GENMASK(3, 0)) << 20)
+#define AST2600_I2CC_TCKHIGH(x) (((x) & GENMASK(3, 0)) << 16)
+#define AST2600_I2CC_TCKLOW(x) (((x) & GENMASK(3, 0)) << 12)
+#define AST2600_I2CC_THDDAT(x) (((x) & GENMASK(1, 0)) << 10)
+#define AST2600_I2CC_TOUTBASECLK(x) (((x) & GENMASK(1, 0)) << 8)
+#define AST2600_I2CC_TBASECLK(x) ((x) & GENMASK(3, 0))
+#define AST2600_I2CC_AC_TIMING_MASK GENMASK(23, 0)
+
+/* 0x08 : I2CC Controller/Target Transmit/Receive Byte Buffer Register */
+#define AST2600_I2CC_STS_AND_BUFF 0x08
+#define AST2600_I2CC_TX_DIR_MASK GENMASK(31, 29)
+#define AST2600_I2CC_SDA_OE BIT(28)
+#define AST2600_I2CC_SDA_O BIT(27)
+#define AST2600_I2CC_SCL_OE BIT(26)
+#define AST2600_I2CC_SCL_O BIT(25)
+
+#define AST2600_I2CC_SCL_LINE_STS BIT(18)
+#define AST2600_I2CC_SDA_LINE_STS BIT(17)
+#define AST2600_I2CC_BUS_BUSY_STS BIT(16)
+
+#define AST2600_I2CC_GET_RX_BUFF(x) (((x) >> 8) & GENMASK(7, 0))
+
+/* 0x0C : I2CC Controller/Target Pool Buffer Control Register */
+#define AST2600_I2CC_BUFF_CTRL 0x0C
+#define AST2600_I2CC_GET_RX_BUF_LEN(x) (((x) & GENMASK(29, 24)) >> 24)
+#define AST2600_I2CC_SET_RX_BUF_LEN(x) (((((x) - 1) & GENMASK(4, 0)) << 16) | BIT(0))
+#define AST2600_I2CC_SET_TX_BUF_LEN(x) (((((x) - 1) & GENMASK(4, 0)) << 8) | BIT(0))
+#define AST2600_I2CC_GET_TX_BUF_LEN(x) ((((x) & GENMASK(12, 8)) >> 8) + 1)
+
+/* 0x10 : I2CM Controller Interrupt Control Register */
+#define AST2600_I2CM_IER 0x10
+/* 0x14 : I2CM Controller Interrupt Status Register : WC */
+#define AST2600_I2CM_ISR 0x14
+
+#define AST2600_I2CM_PKT_TIMEOUT BIT(18)
+#define AST2600_I2CM_PKT_ERROR BIT(17)
+#define AST2600_I2CM_PKT_DONE BIT(16)
+
+#define AST2600_I2CM_BUS_RECOVER_FAIL BIT(15)
+#define AST2600_I2CM_SDA_DL_TO BIT(14)
+#define AST2600_I2CM_BUS_RECOVER BIT(13)
+#define AST2600_I2CM_SMBUS_ALERT BIT(12)
+
+#define AST2600_I2CM_SCL_LOW_TO BIT(6)
+#define AST2600_I2CM_ABNORMAL BIT(5)
+#define AST2600_I2CM_NORMAL_STOP BIT(4)
+#define AST2600_I2CM_ARBIT_LOSS BIT(3)
+#define AST2600_I2CM_RX_DONE BIT(2)
+#define AST2600_I2CM_TX_NAK BIT(1)
+#define AST2600_I2CM_TX_ACK BIT(0)
+
+/* 0x18 : I2CM Controller Command/Status Register */
+#define AST2600_I2CM_CMD_STS 0x18
+#define AST2600_I2CM_PKT_ADDR(x) (((x) & GENMASK(6, 0)) << 24)
+#define AST2600_I2CM_PKT_EN BIT(16)
+#define AST2600_I2CM_SDA_OE_OUT_DIR BIT(15)
+#define AST2600_I2CM_SDA_O_OUT_DIR BIT(14)
+#define AST2600_I2CM_SCL_OE_OUT_DIR BIT(13)
+#define AST2600_I2CM_SCL_O_OUT_DIR BIT(12)
+#define AST2600_I2CM_RECOVER_CMD_EN BIT(11)
+
+#define AST2600_I2CM_RX_DMA_EN BIT(9)
+#define AST2600_I2CM_TX_DMA_EN BIT(8)
+/* Command Bit */
+#define AST2600_I2CM_RX_BUFF_EN BIT(7)
+#define AST2600_I2CM_TX_BUFF_EN BIT(6)
+#define AST2600_I2CM_STOP_CMD BIT(5)
+#define AST2600_I2CM_RX_CMD_LAST BIT(4)
+#define AST2600_I2CM_RX_CMD BIT(3)
+
+#define AST2600_I2CM_TX_CMD BIT(1)
+#define AST2600_I2CM_START_CMD BIT(0)
+
+/* 0x1C : I2CM Controller DMA Transfer Length Register */
+#define AST2600_I2CM_DMA_LEN 0x1C
+/* Tx Rx support length 1 ~ 4096 */
+#define AST2600_I2CM_SET_RX_DMA_LEN(x) ((((x) & GENMASK(11, 0)) << 16) | BIT(31))
+#define AST2600_I2CM_SET_TX_DMA_LEN(x) (((x) & GENMASK(11, 0)) | BIT(15))
+
+/* 0x20 : I2CS Target Interrupt Control Register */
+#define AST2600_I2CS_IER 0x20
+/* 0x24 : I2CS Target Interrupt Status Register */
+#define AST2600_I2CS_ISR 0x24
+
+#define AST2600_I2CS_ADDR_INDICATE_MASK GENMASK(31, 30)
+#define AST2600_I2CS_SLAVE_PENDING BIT(29)
+
+#define AST2600_I2CS_WAIT_TX_DMA BIT(25)
+#define AST2600_I2CS_WAIT_RX_DMA BIT(24)
+
+#define AST2600_I2CS_ADDR3_NAK BIT(22)
+#define AST2600_I2CS_ADDR2_NAK BIT(21)
+#define AST2600_I2CS_ADDR1_NAK BIT(20)
+
+#define AST2600_I2CS_ADDR_MASK GENMASK(19, 18)
+#define AST2600_I2CS_PKT_ERROR BIT(17)
+#define AST2600_I2CS_PKT_DONE BIT(16)
+#define AST2600_I2CS_INACTIVE_TO BIT(15)
+
+#define AST2600_I2CS_SLAVE_MATCH BIT(7)
+#define AST2600_I2CS_ABNOR_STOP BIT(5)
+#define AST2600_I2CS_STOP BIT(4)
+#define AST2600_I2CS_RX_DONE_NAK BIT(3)
+#define AST2600_I2CS_RX_DONE BIT(2)
+#define AST2600_I2CS_TX_NAK BIT(1)
+#define AST2600_I2CS_TX_ACK BIT(0)
+
+/* 0x28 : I2CS Target CMD/Status Register */
+#define AST2600_I2CS_CMD_STS 0x28
+#define AST2600_I2CS_ACTIVE_ALL GENMASK(18, 17)
+#define AST2600_I2CS_PKT_MODE_EN BIT(16)
+#define AST2600_I2CS_AUTO_NAK_NOADDR BIT(15)
+#define AST2600_I2CS_AUTO_NAK_EN BIT(14)
+
+#define AST2600_I2CS_ALT_EN BIT(10)
+#define AST2600_I2CS_RX_DMA_EN BIT(9)
+#define AST2600_I2CS_TX_DMA_EN BIT(8)
+#define AST2600_I2CS_RX_BUFF_EN BIT(7)
+#define AST2600_I2CS_TX_BUFF_EN BIT(6)
+#define AST2600_I2CS_RX_CMD_LAST BIT(4)
+
+#define AST2600_I2CS_TX_CMD BIT(2)
+
+#define AST2600_I2CS_DMA_LEN 0x2C
+#define AST2600_I2CS_SET_RX_DMA_LEN(x) (((((x) - 1) & GENMASK(11, 0)) << 16) | BIT(31))
+#define AST2600_I2CS_SET_TX_DMA_LEN(x) ((((x) - 1) & GENMASK(11, 0)) | BIT(15))
+
+/* I2CM Controller DMA Tx Buffer Register */
+#define AST2600_I2CM_TX_DMA 0x30
+/* I2CM Controller DMA Rx Buffer Register */
+#define AST2600_I2CM_RX_DMA 0x34
+/* I2CS Target DMA Tx Buffer Register */
+#define AST2600_I2CS_TX_DMA 0x38
+/* I2CS Target DMA Rx Buffer Register */
+#define AST2600_I2CS_RX_DMA 0x3C
+
+#define AST2600_I2CS_ADDR_CTRL 0x40
+
+#define AST2600_I2CS_ADDR3_MASK GENMASK(22, 16)
+#define AST2600_I2CS_ADDR2_MASK GENMASK(14, 8)
+#define AST2600_I2CS_ADDR1_MASK GENMASK(6, 0)
+
+#define AST2600_I2CM_DMA_LEN_STS 0x48
+#define AST2600_I2CS_DMA_LEN_STS 0x4C
+
+#define AST2600_I2C_GET_TX_DMA_LEN(x) ((x) & GENMASK(12, 0))
+#define AST2600_I2C_GET_RX_DMA_LEN(x) (((x) & GENMASK(28, 16)) >> 16)
+
+/* 0x40 : Target Device Address Register */
+#define AST2600_I2CS_ADDR3_ENABLE BIT(23)
+#define AST2600_I2CS_ADDR3(x) ((x) << 16)
+#define AST2600_I2CS_ADDR2_ENABLE BIT(15)
+#define AST2600_I2CS_ADDR2(x) ((x) << 8)
+#define AST2600_I2CS_ADDR1_ENABLE BIT(7)
+#define AST2600_I2CS_ADDR1(x) (x)
+
+#define I2C_TARGET_MSG_BUF_SIZE 4096
+
+#define AST2600_I2C_DMA_SIZE 4096
+
+#define CONTROLLER_TRIGGER_LAST_STOP (AST2600_I2CM_RX_CMD_LAST | AST2600_I2CM_STOP_CMD)
+#define TARGET_TRIGGER_CMD (AST2600_I2CS_ACTIVE_ALL | AST2600_I2CS_PKT_MODE_EN)
+
+#define AST_I2C_TIMEOUT_CLK 0x1
+
+enum xfer_mode {
+ BYTE_MODE,
+ BUFF_MODE,
+ DMA_MODE,
+};
+
+struct ast2600_i2c_bus {
+ struct i2c_adapter adap;
+ struct device *dev;
+ void __iomem *reg_base;
+ struct regmap *global_regs;
+ struct clk *clk;
+ struct i2c_timings timing_info;
+ struct completion cmd_complete;
+ struct i2c_msg *msgs;
+ u8 *controller_dma_buf;
+ dma_addr_t controller_dma_addr;
+ u32 apb_clk;
+ u32 timeout;
+ int irq;
+ int cmd_err;
+ int msgs_index;
+ int msgs_count;
+ int controller_xfer_cnt;
+ size_t buf_index;
+ size_t buf_size;
+ enum xfer_mode mode;
+ bool dma_available;
+ bool multi_master;
+ /* Buffer mode */
+ void __iomem *buf_base;
+ int (*setup_tx)(u32 cmd, struct ast2600_i2c_bus *i2c_bus);
+ int (*setup_rx)(u32 cmd, struct ast2600_i2c_bus *i2c_bus);
+};
+
+static void ast2600_i2c_ac_timing_config(struct ast2600_i2c_bus *i2c_bus)
+{
+ unsigned long base_clk[16];
+ int baseclk_idx = 0;
+ int divisor = 0;
+ u32 clk_div_reg;
+ u32 scl_low;
+ u32 scl_high;
+ u32 data;
+
+ regmap_read(i2c_bus->global_regs, AST2600_I2CG_CLK_DIV_CTRL, &clk_div_reg);
+
+ for (int i = 0; i < ARRAY_SIZE(base_clk); i++) {
+ if (i == 0)
+ base_clk[i] = i2c_bus->apb_clk;
+ else if (i < 5)
+ base_clk[i] = (i2c_bus->apb_clk * 2) /
+ (((clk_div_reg >> ((i - 1) * 8)) & GENMASK(7, 0)) + 2);
+ else
+ base_clk[i] = base_clk[4] >> (i - 4);
+
+ if ((base_clk[i] / i2c_bus->timing_info.bus_freq_hz) <= 32) {
+ baseclk_idx = i;
+ divisor = DIV_ROUND_UP(base_clk[i], i2c_bus->timing_info.bus_freq_hz);
+ break;
+ }
+ }
+ baseclk_idx = min(baseclk_idx, 15);
+ divisor = min(divisor, 32);
+ scl_low = min(divisor * 9 / 16 - 1, 15);
+ scl_high = (divisor - scl_low - 2) & GENMASK(3, 0);
+ data = (scl_high - 1) << 20 | scl_high << 16 | scl_low << 12 | baseclk_idx;
+ if (i2c_bus->timeout) {
+ data |= AST2600_I2CC_TOUTBASECLK(AST_I2C_TIMEOUT_CLK);
+ data |= AST2600_I2CC_TTIMEOUT(i2c_bus->timeout);
+ }
+
+ writel(data, i2c_bus->reg_base + AST2600_I2CC_AC_TIMING);
+}
+
+static int ast2600_i2c_recover_bus(struct ast2600_i2c_bus *i2c_bus)
+{
+ u32 state = readl(i2c_bus->reg_base + AST2600_I2CC_STS_AND_BUFF);
+ int ret = 0;
+ u32 ctrl;
+ int r;
+
+ dev_dbg(i2c_bus->dev, "%d-bus recovery bus [%x]\n", i2c_bus->adap.nr, state);
+
+ /* reset controller */
+ ctrl = readl(i2c_bus->reg_base + AST2600_I2CC_FUN_CTRL);
+ writel(ctrl & ~AST2600_I2CC_MASTER_EN, i2c_bus->reg_base + AST2600_I2CC_FUN_CTRL);
+ writel(ctrl, i2c_bus->reg_base + AST2600_I2CC_FUN_CTRL);
+
+ reinit_completion(&i2c_bus->cmd_complete);
+ i2c_bus->cmd_err = 0;
+
+ /* Check SDA/SCL status in the status register. */
+ state = readl(i2c_bus->reg_base + AST2600_I2CC_STS_AND_BUFF);
+ if (!(state & AST2600_I2CC_SDA_LINE_STS) && (state & AST2600_I2CC_SCL_LINE_STS)) {
+ writel(AST2600_I2CM_RECOVER_CMD_EN, i2c_bus->reg_base + AST2600_I2CM_CMD_STS);
+ r = wait_for_completion_timeout(&i2c_bus->cmd_complete, i2c_bus->adap.timeout);
+ if (r == 0) {
+ dev_dbg(i2c_bus->dev, "recovery timed out\n");
+ return -ETIMEDOUT;
+ } else if (i2c_bus->cmd_err) {
+ dev_dbg(i2c_bus->dev, "recovery error\n");
+ ret = -EPROTO;
+ }
+ }
+
+ /* Recovery done */
+ state = readl(i2c_bus->reg_base + AST2600_I2CC_STS_AND_BUFF);
+ if (state & AST2600_I2CC_BUS_BUSY_STS) {
+ dev_dbg(i2c_bus->dev, "Can't recover bus [%x]\n", state);
+ ret = -EPROTO;
+ }
+
+ return ret;
+}
+
+static int ast2600_i2c_setup_dma_tx(u32 cmd, struct ast2600_i2c_bus *i2c_bus)
+{
+ struct i2c_msg *msg = &i2c_bus->msgs[i2c_bus->msgs_index];
+ int xfer_len = msg->len - i2c_bus->controller_xfer_cnt;
+
+ cmd |= AST2600_I2CM_PKT_EN;
+
+ if (xfer_len > AST2600_I2C_DMA_SIZE)
+ xfer_len = AST2600_I2C_DMA_SIZE;
+ else if (i2c_bus->msgs_index + 1 == i2c_bus->msgs_count)
+ cmd |= AST2600_I2CM_STOP_CMD;
+
+ if (cmd & AST2600_I2CM_START_CMD)
+ cmd |= AST2600_I2CM_PKT_ADDR(msg->addr);
+
+ if (xfer_len) {
+ memcpy(i2c_bus->controller_dma_buf, msg->buf, xfer_len);
+ cmd |= AST2600_I2CM_TX_DMA_EN | AST2600_I2CM_TX_CMD;
+ writel(AST2600_I2CM_SET_TX_DMA_LEN(xfer_len - 1),
+ i2c_bus->reg_base + AST2600_I2CM_DMA_LEN);
+ }
+
+ writel(cmd, i2c_bus->reg_base + AST2600_I2CM_CMD_STS);
+
+ return 0;
+}
+
+static int ast2600_i2c_setup_buff_tx(u32 cmd, struct ast2600_i2c_bus *i2c_bus)
+{
+ struct i2c_msg *msg = &i2c_bus->msgs[i2c_bus->msgs_index];
+ int xfer_len = msg->len - i2c_bus->controller_xfer_cnt;
+ u32 wbuf_dword;
+ int i;
+
+ cmd |= AST2600_I2CM_PKT_EN;
+
+ if (xfer_len > i2c_bus->buf_size)
+ xfer_len = i2c_bus->buf_size;
+ else if (i2c_bus->msgs_index + 1 == i2c_bus->msgs_count)
+ cmd |= AST2600_I2CM_STOP_CMD;
+
+ if (cmd & AST2600_I2CM_START_CMD)
+ cmd |= AST2600_I2CM_PKT_ADDR(msg->addr);
+
+ if (xfer_len) {
+ cmd |= AST2600_I2CM_TX_BUFF_EN | AST2600_I2CM_TX_CMD;
+ /*
+ * The controller's buffer register supports dword writes only.
+ * Therefore, write dwords to the buffer register in a 4-byte aligned,
+ * and write the remaining unaligned data at the end.
+ */
+ for (i = 0; i < xfer_len; i += 4) {
+ int xfer_cnt = i2c_bus->controller_xfer_cnt + i;
+
+ switch (min(xfer_len - i, 4) % 4) {
+ case 1:
+ wbuf_dword = msg->buf[xfer_cnt];
+ break;
+ case 2:
+ wbuf_dword = get_unaligned_le16(&msg->buf[xfer_cnt]);
+ break;
+ case 3:
+ wbuf_dword = get_unaligned_le24(&msg->buf[xfer_cnt]);
+ break;
+ default:
+ wbuf_dword = get_unaligned_le32(&msg->buf[xfer_cnt]);
+ break;
+ }
+ writel(wbuf_dword, i2c_bus->buf_base + i);
+ }
+ writel(AST2600_I2CC_SET_TX_BUF_LEN(xfer_len),
+ i2c_bus->reg_base + AST2600_I2CC_BUFF_CTRL);
+ }
+
+ writel(cmd, i2c_bus->reg_base + AST2600_I2CM_CMD_STS);
+
+ return 0;
+}
+
+static int ast2600_i2c_setup_byte_tx(u32 cmd, struct ast2600_i2c_bus *i2c_bus)
+{
+ struct i2c_msg *msg = &i2c_bus->msgs[i2c_bus->msgs_index];
+ int xfer_len;
+
+ xfer_len = msg->len - i2c_bus->controller_xfer_cnt;
+
+ cmd |= AST2600_I2CM_PKT_EN;
+
+ if (cmd & AST2600_I2CM_START_CMD)
+ cmd |= AST2600_I2CM_PKT_ADDR(msg->addr);
+
+ if ((i2c_bus->msgs_index + 1 == i2c_bus->msgs_count) &&
+ ((i2c_bus->controller_xfer_cnt + 1) == msg->len))
+ cmd |= AST2600_I2CM_STOP_CMD;
+
+ if (xfer_len) {
+ cmd |= AST2600_I2CM_TX_CMD;
+ writel(msg->buf[i2c_bus->controller_xfer_cnt],
+ i2c_bus->reg_base + AST2600_I2CC_STS_AND_BUFF);
+ }
+
+ writel(cmd, i2c_bus->reg_base + AST2600_I2CM_CMD_STS);
+
+ return 0;
+}
+
+static int ast2600_i2c_setup_dma_rx(u32 cmd, struct ast2600_i2c_bus *i2c_bus)
+{
+ struct i2c_msg *msg = &i2c_bus->msgs[i2c_bus->msgs_index];
+ int xfer_len = msg->len - i2c_bus->controller_xfer_cnt;
+
+ cmd |= AST2600_I2CM_PKT_EN | AST2600_I2CM_RX_DMA_EN | AST2600_I2CM_RX_CMD;
+
+ if (msg->flags & I2C_M_RECV_LEN)
+ xfer_len = 1;
+ else if (xfer_len > AST2600_I2C_DMA_SIZE)
+ xfer_len = AST2600_I2C_DMA_SIZE;
+ else if (i2c_bus->msgs_index + 1 == i2c_bus->msgs_count)
+ cmd |= CONTROLLER_TRIGGER_LAST_STOP;
+
+ writel(AST2600_I2CM_SET_RX_DMA_LEN(xfer_len - 1), i2c_bus->reg_base + AST2600_I2CM_DMA_LEN);
+
+ if (cmd & AST2600_I2CM_START_CMD)
+ cmd |= AST2600_I2CM_PKT_ADDR(msg->addr);
+
+ writel(cmd, i2c_bus->reg_base + AST2600_I2CM_CMD_STS);
+
+ return 0;
+}
+
+static int ast2600_i2c_setup_buff_rx(u32 cmd, struct ast2600_i2c_bus *i2c_bus)
+{
+ struct i2c_msg *msg = &i2c_bus->msgs[i2c_bus->msgs_index];
+ int xfer_len = msg->len - i2c_bus->controller_xfer_cnt;
+
+ cmd |= AST2600_I2CM_PKT_EN | AST2600_I2CM_RX_BUFF_EN | AST2600_I2CM_RX_CMD;
+
+ if (cmd & AST2600_I2CM_START_CMD)
+ cmd |= AST2600_I2CM_PKT_ADDR(msg->addr);
+
+ if (msg->flags & I2C_M_RECV_LEN) {
+ dev_dbg(i2c_bus->dev, "smbus read\n");
+ xfer_len = 1;
+ } else if (xfer_len > i2c_bus->buf_size) {
+ xfer_len = i2c_bus->buf_size;
+ } else if (i2c_bus->msgs_index + 1 == i2c_bus->msgs_count) {
+ cmd |= CONTROLLER_TRIGGER_LAST_STOP;
+ }
+ writel(AST2600_I2CC_SET_RX_BUF_LEN(xfer_len), i2c_bus->reg_base + AST2600_I2CC_BUFF_CTRL);
+
+ writel(cmd, i2c_bus->reg_base + AST2600_I2CM_CMD_STS);
+
+ return 0;
+}
+
+static int ast2600_i2c_setup_byte_rx(u32 cmd, struct ast2600_i2c_bus *i2c_bus)
+{
+ struct i2c_msg *msg = &i2c_bus->msgs[i2c_bus->msgs_index];
+
+ cmd |= AST2600_I2CM_PKT_EN | AST2600_I2CM_RX_CMD;
+
+ if (cmd & AST2600_I2CM_START_CMD)
+ cmd |= AST2600_I2CM_PKT_ADDR(msg->addr);
+
+ if (msg->flags & I2C_M_RECV_LEN) {
+ dev_dbg(i2c_bus->dev, "smbus read\n");
+ } else if ((i2c_bus->msgs_index + 1 == i2c_bus->msgs_count) &&
+ ((i2c_bus->controller_xfer_cnt + 1) == msg->len)) {
+ cmd |= CONTROLLER_TRIGGER_LAST_STOP;
+ }
+
+ writel(cmd, i2c_bus->reg_base + AST2600_I2CM_CMD_STS);
+
+ return 0;
+}
+
+static int ast2600_i2c_do_start(struct ast2600_i2c_bus *i2c_bus)
+{
+ struct i2c_msg *msg = &i2c_bus->msgs[i2c_bus->msgs_index];
+
+ /* send start */
+ dev_dbg(i2c_bus->dev, "[%d] %s %d byte%s %s 0x%02x\n",
+ i2c_bus->msgs_index, str_read_write(msg->flags & I2C_M_RD),
+ msg->len, str_plural(msg->len),
+ msg->flags & I2C_M_RD ? "from" : "to", msg->addr);
+
+ if (!i2c_bus->setup_rx || !i2c_bus->setup_tx)
+ return -EINVAL;
+
+ i2c_bus->controller_xfer_cnt = 0;
+ i2c_bus->buf_index = 0;
+
+ if (msg->flags & I2C_M_RD)
+ return i2c_bus->setup_rx(AST2600_I2CM_START_CMD, i2c_bus);
+
+ return i2c_bus->setup_tx(AST2600_I2CM_START_CMD, i2c_bus);
+}
+
+static int ast2600_i2c_irq_err_to_errno(u32 irq_status)
+{
+ if (irq_status & AST2600_I2CM_ARBIT_LOSS)
+ return -EAGAIN;
+ if (irq_status & (AST2600_I2CM_SDA_DL_TO | AST2600_I2CM_SCL_LOW_TO))
+ return -ETIMEDOUT;
+ if (irq_status & (AST2600_I2CM_ABNORMAL))
+ return -EPROTO;
+
+ return 0;
+}
+
+static void ast2600_i2c_controller_packet_irq(struct ast2600_i2c_bus *i2c_bus, u32 sts)
+{
+ struct i2c_msg *msg = &i2c_bus->msgs[i2c_bus->msgs_index];
+ int xfer_len;
+ int i;
+
+ sts &= ~AST2600_I2CM_PKT_DONE;
+ writel(AST2600_I2CM_PKT_DONE, i2c_bus->reg_base + AST2600_I2CM_ISR);
+ switch (sts) {
+ case AST2600_I2CM_PKT_ERROR:
+ i2c_bus->cmd_err = -EAGAIN;
+ complete(&i2c_bus->cmd_complete);
+ break;
+ case AST2600_I2CM_PKT_ERROR | AST2600_I2CM_TX_NAK: /* a0 fix for issue */
+ fallthrough;
+ case AST2600_I2CM_PKT_ERROR | AST2600_I2CM_TX_NAK | AST2600_I2CM_NORMAL_STOP:
+ i2c_bus->cmd_err = -ENXIO;
+ complete(&i2c_bus->cmd_complete);
+ break;
+ case AST2600_I2CM_NORMAL_STOP:
+ /* write 0 byte only have stop isr */
+ i2c_bus->msgs_index++;
+ if (i2c_bus->msgs_index < i2c_bus->msgs_count) {
+ if (ast2600_i2c_do_start(i2c_bus)) {
+ i2c_bus->cmd_err = -ENOMEM;
+ complete(&i2c_bus->cmd_complete);
+ }
+ } else {
+ i2c_bus->cmd_err = i2c_bus->msgs_index;
+ complete(&i2c_bus->cmd_complete);
+ }
+ break;
+ case AST2600_I2CM_TX_ACK:
+ case AST2600_I2CM_TX_ACK | AST2600_I2CM_NORMAL_STOP:
+ if (i2c_bus->mode == DMA_MODE)
+ xfer_len = AST2600_I2C_GET_TX_DMA_LEN(readl(i2c_bus->reg_base +
+ AST2600_I2CM_DMA_LEN_STS));
+ else if (i2c_bus->mode == BUFF_MODE)
+ xfer_len = AST2600_I2CC_GET_TX_BUF_LEN(readl(i2c_bus->reg_base +
+ AST2600_I2CC_BUFF_CTRL));
+ else
+ xfer_len = 1;
+
+ i2c_bus->controller_xfer_cnt += xfer_len;
+
+ if (i2c_bus->controller_xfer_cnt == msg->len) {
+ i2c_bus->msgs_index++;
+ if (i2c_bus->msgs_index == i2c_bus->msgs_count) {
+ i2c_bus->cmd_err = i2c_bus->msgs_index;
+ complete(&i2c_bus->cmd_complete);
+ } else {
+ if (ast2600_i2c_do_start(i2c_bus)) {
+ i2c_bus->cmd_err = -ENOMEM;
+ complete(&i2c_bus->cmd_complete);
+ }
+ }
+ } else {
+ i2c_bus->setup_tx(0, i2c_bus);
+ }
+ break;
+ case AST2600_I2CM_RX_DONE:
+ case AST2600_I2CM_RX_DONE | AST2600_I2CM_NORMAL_STOP:
+ /* do next rx */
+ if (i2c_bus->mode == DMA_MODE) {
+ xfer_len = AST2600_I2C_GET_RX_DMA_LEN(readl(i2c_bus->reg_base +
+ AST2600_I2CM_DMA_LEN_STS));
+ memcpy(&msg->buf[i2c_bus->controller_xfer_cnt],
+ i2c_bus->controller_dma_buf, xfer_len);
+ } else if (i2c_bus->mode == BUFF_MODE) {
+ xfer_len = AST2600_I2CC_GET_RX_BUF_LEN(readl(i2c_bus->reg_base +
+ AST2600_I2CC_BUFF_CTRL));
+ for (i = 0; i < xfer_len; i++)
+ msg->buf[i2c_bus->controller_xfer_cnt + i] =
+ readb(i2c_bus->buf_base + 0x10 + i);
+ } else {
+ xfer_len = 1;
+ msg->buf[i2c_bus->controller_xfer_cnt] =
+ AST2600_I2CC_GET_RX_BUFF(readl(i2c_bus->reg_base +
+ AST2600_I2CC_STS_AND_BUFF));
+ }
+
+ if (msg->flags & I2C_M_RECV_LEN) {
+ u8 recv_len = AST2600_I2CC_GET_RX_BUFF(readl(i2c_bus->reg_base
+ + AST2600_I2CC_STS_AND_BUFF));
+ msg->len = min_t(unsigned int, recv_len, I2C_SMBUS_BLOCK_MAX);
+ msg->len += ((msg->flags & I2C_CLIENT_PEC) ? 2 : 1);
+ msg->flags &= ~I2C_M_RECV_LEN;
+ if (!recv_len)
+ i2c_bus->controller_xfer_cnt = 0;
+ else
+ i2c_bus->controller_xfer_cnt = 1;
+ } else {
+ i2c_bus->controller_xfer_cnt += xfer_len;
+ }
+
+ if (i2c_bus->controller_xfer_cnt == msg->len) {
+ i2c_bus->msgs_index++;
+ if (i2c_bus->msgs_index == i2c_bus->msgs_count) {
+ i2c_bus->cmd_err = i2c_bus->msgs_index;
+ complete(&i2c_bus->cmd_complete);
+ } else {
+ if (ast2600_i2c_do_start(i2c_bus)) {
+ i2c_bus->cmd_err = -ENOMEM;
+ complete(&i2c_bus->cmd_complete);
+ }
+ }
+ } else {
+ i2c_bus->setup_rx(0, i2c_bus);
+ }
+ break;
+ default:
+ dev_dbg(i2c_bus->dev, "unhandled sts %x\n", sts);
+ break;
+ }
+}
+
+static int ast2600_i2c_controller_irq(struct ast2600_i2c_bus *i2c_bus)
+{
+ u32 sts = readl(i2c_bus->reg_base + AST2600_I2CM_ISR);
+ u32 ctrl;
+
+ sts &= ~AST2600_I2CM_SMBUS_ALERT;
+
+ if (sts & AST2600_I2CM_BUS_RECOVER_FAIL) {
+ writel(AST2600_I2CM_BUS_RECOVER_FAIL, i2c_bus->reg_base + AST2600_I2CM_ISR);
+ ctrl = readl(i2c_bus->reg_base + AST2600_I2CC_FUN_CTRL);
+ writel(0, i2c_bus->reg_base + AST2600_I2CC_FUN_CTRL);
+ writel(ctrl, i2c_bus->reg_base + AST2600_I2CC_FUN_CTRL);
+ i2c_bus->cmd_err = -EPROTO;
+ complete(&i2c_bus->cmd_complete);
+ return 1;
+ }
+
+ if (sts & AST2600_I2CM_BUS_RECOVER) {
+ writel(AST2600_I2CM_BUS_RECOVER, i2c_bus->reg_base + AST2600_I2CM_ISR);
+ i2c_bus->cmd_err = 0;
+ complete(&i2c_bus->cmd_complete);
+ return 1;
+ }
+
+ i2c_bus->cmd_err = ast2600_i2c_irq_err_to_errno(sts);
+ if (i2c_bus->cmd_err) {
+ writel(AST2600_I2CM_PKT_DONE, i2c_bus->reg_base + AST2600_I2CM_ISR);
+ complete(&i2c_bus->cmd_complete);
+ return 1;
+ }
+
+ if (sts & AST2600_I2CM_PKT_DONE) {
+ ast2600_i2c_controller_packet_irq(i2c_bus, sts);
+ return 1;
+ }
+
+ return 0;
+}
+
+static irqreturn_t ast2600_i2c_bus_irq(int irq, void *dev_id)
+{
+ struct ast2600_i2c_bus *i2c_bus = dev_id;
+
+ return IRQ_RETVAL(ast2600_i2c_controller_irq(i2c_bus));
+}
+
+static int ast2600_i2c_controller_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
+{
+ struct ast2600_i2c_bus *i2c_bus = i2c_get_adapdata(adap);
+ unsigned long timeout;
+ int ret;
+
+ if (!i2c_bus->multi_master &&
+ (readl(i2c_bus->reg_base + AST2600_I2CC_STS_AND_BUFF) & AST2600_I2CC_BUS_BUSY_STS)) {
+ ret = ast2600_i2c_recover_bus(i2c_bus);
+ if (ret)
+ return ret;
+ }
+
+ i2c_bus->cmd_err = 0;
+ i2c_bus->msgs = msgs;
+ i2c_bus->msgs_index = 0;
+ i2c_bus->msgs_count = num;
+ reinit_completion(&i2c_bus->cmd_complete);
+ ret = ast2600_i2c_do_start(i2c_bus);
+ if (ret)
+ goto controller_out;
+ timeout = wait_for_completion_timeout(&i2c_bus->cmd_complete, i2c_bus->adap.timeout);
+ if (timeout == 0) {
+ u32 ctrl = readl(i2c_bus->reg_base + AST2600_I2CC_FUN_CTRL);
+
+ dev_dbg(i2c_bus->dev, "timeout isr[%x], sts[%x]\n",
+ readl(i2c_bus->reg_base + AST2600_I2CM_ISR),
+ readl(i2c_bus->reg_base + AST2600_I2CC_STS_AND_BUFF));
+ writel(ctrl & ~AST2600_I2CC_MASTER_EN, i2c_bus->reg_base + AST2600_I2CC_FUN_CTRL);
+ writel(ctrl, i2c_bus->reg_base + AST2600_I2CC_FUN_CTRL);
+
+ /*
+ * A slave holding SCL low can stall the transfer and trigger
+ * a master timeout. In multi-master mode, attempt bus recovery
+ * if the bus is still busy.
+ */
+ if (i2c_bus->multi_master &&
+ (readl(i2c_bus->reg_base + AST2600_I2CC_STS_AND_BUFF) &
+ AST2600_I2CC_BUS_BUSY_STS))
+ ast2600_i2c_recover_bus(i2c_bus);
+ ret = -ETIMEDOUT;
+ } else {
+ ret = i2c_bus->cmd_err;
+ }
+
+ dev_dbg(i2c_bus->dev, "bus%d-m: %d end\n", i2c_bus->adap.nr, i2c_bus->cmd_err);
+
+controller_out:
+ return ret;
+}
+
+static int ast2600_i2c_init(struct ast2600_i2c_bus *i2c_bus)
+{
+ u32 fun_ctrl = AST2600_I2CC_BUS_AUTO_RELEASE | AST2600_I2CC_MASTER_EN;
+
+ /* I2C Reset */
+ writel(0, i2c_bus->reg_base + AST2600_I2CC_FUN_CTRL);
+
+ if (!i2c_bus->multi_master)
+ fun_ctrl |= AST2600_I2CC_MULTI_MASTER_DIS;
+
+ /* Enable Controller Mode */
+ writel(fun_ctrl, i2c_bus->reg_base + AST2600_I2CC_FUN_CTRL);
+ /* disable target address */
+ writel(0, i2c_bus->reg_base + AST2600_I2CS_ADDR_CTRL);
+
+ /* Set AC Timing */
+ ast2600_i2c_ac_timing_config(i2c_bus);
+
+ if (i2c_bus->dma_available) {
+ i2c_bus->controller_dma_buf =
+ dmam_alloc_coherent(i2c_bus->dev, AST2600_I2C_DMA_SIZE,
+ &i2c_bus->controller_dma_addr, GFP_KERNEL);
+ if (!i2c_bus->controller_dma_buf)
+ return -ENOMEM;
+ writel(i2c_bus->controller_dma_addr,
+ i2c_bus->reg_base + AST2600_I2CM_TX_DMA);
+ writel(i2c_bus->controller_dma_addr,
+ i2c_bus->reg_base + AST2600_I2CM_RX_DMA);
+ }
+
+ /* Clear Interrupt */
+ writel(GENMASK(27, 0), i2c_bus->reg_base + AST2600_I2CM_ISR);
+
+ return 0;
+}
+
+static u32 ast2600_i2c_functionality(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_SMBUS_BLOCK_DATA;
+}
+
+static const struct i2c_algorithm i2c_ast2600_algorithm = {
+ .xfer = ast2600_i2c_controller_xfer,
+ .functionality = ast2600_i2c_functionality,
+};
+
+static void ast2600_i2c_set_xfer_mode(struct ast2600_i2c_bus *i2c_bus,
+ enum xfer_mode mode)
+{
+ i2c_bus->mode = mode;
+
+ switch (mode) {
+ case DMA_MODE:
+ i2c_bus->setup_tx = ast2600_i2c_setup_dma_tx;
+ i2c_bus->setup_rx = ast2600_i2c_setup_dma_rx;
+ break;
+ case BYTE_MODE:
+ i2c_bus->setup_tx = ast2600_i2c_setup_byte_tx;
+ i2c_bus->setup_rx = ast2600_i2c_setup_byte_rx;
+ break;
+ case BUFF_MODE:
+ default:
+ i2c_bus->setup_tx = ast2600_i2c_setup_buff_tx;
+ i2c_bus->setup_rx = ast2600_i2c_setup_buff_rx;
+ break;
+ }
+}
+
+static int ast2600_i2c_xfer_mode_parse(struct ast2600_i2c_bus *i2c_bus,
+ const char *buf, enum xfer_mode *mode)
+{
+ if (sysfs_streq(buf, "byte")) {
+ *mode = BYTE_MODE;
+ return 0;
+ }
+
+ if (sysfs_streq(buf, "buffer")) {
+ if (!i2c_bus->buf_base)
+ return -EINVAL;
+ *mode = BUFF_MODE;
+ return 0;
+ }
+
+ if (sysfs_streq(buf, "dma")) {
+ if (!i2c_bus->dma_available)
+ return -EINVAL;
+ *mode = DMA_MODE;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static const char *ast2600_i2c_xfer_mode_name(enum xfer_mode mode)
+{
+ switch (mode) {
+ case BYTE_MODE:
+ return "byte";
+ case DMA_MODE:
+ return "dma";
+ case BUFF_MODE:
+ default:
+ return "buffer";
+ }
+}
+
+static ssize_t xfer_mode_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct ast2600_i2c_bus *i2c_bus = dev_get_drvdata(dev);
+
+ return sysfs_emit(buf, "%s\n", ast2600_i2c_xfer_mode_name(i2c_bus->mode));
+}
+
+static ssize_t xfer_mode_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct ast2600_i2c_bus *i2c_bus = dev_get_drvdata(dev);
+ enum xfer_mode mode;
+ int ret;
+
+ ret = ast2600_i2c_xfer_mode_parse(i2c_bus, buf, &mode);
+ if (ret)
+ return ret;
+
+ i2c_lock_bus(&i2c_bus->adap, I2C_LOCK_ROOT_ADAPTER);
+ ast2600_i2c_set_xfer_mode(i2c_bus, mode);
+ i2c_unlock_bus(&i2c_bus->adap, I2C_LOCK_ROOT_ADAPTER);
+
+ return count;
+}
+
+static DEVICE_ATTR_RW(xfer_mode);
+
+static int ast2600_i2c_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct ast2600_i2c_bus *i2c_bus;
+ struct reset_control *rst;
+ struct resource *res;
+ u32 global_ctrl;
+ int ret;
+
+ if (!device_property_present(dev, "aspeed,global-regs"))
+ return -ENODEV;
+
+ i2c_bus = devm_kzalloc(dev, sizeof(*i2c_bus), GFP_KERNEL);
+ if (!i2c_bus)
+ return -ENOMEM;
+
+ i2c_bus->reg_base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(i2c_bus->reg_base))
+ return PTR_ERR(i2c_bus->reg_base);
+
+ rst = devm_reset_control_get_shared_deasserted(dev, NULL);
+ if (IS_ERR(rst))
+ return dev_err_probe(dev, PTR_ERR(rst), "Missing reset ctrl\n");
+
+ i2c_bus->global_regs =
+ syscon_regmap_lookup_by_phandle(dev_of_node(dev), "aspeed,global-regs");
+ if (IS_ERR(i2c_bus->global_regs))
+ return PTR_ERR(i2c_bus->global_regs);
+
+ regmap_read(i2c_bus->global_regs, AST2600_I2CG_CTRL, &global_ctrl);
+ if ((global_ctrl & AST2600_GLOBAL_INIT) != AST2600_GLOBAL_INIT) {
+ regmap_write(i2c_bus->global_regs, AST2600_I2CG_CTRL, AST2600_GLOBAL_INIT);
+ regmap_write(i2c_bus->global_regs, AST2600_I2CG_CLK_DIV_CTRL, I2CCG_DIV_CTRL);
+ }
+
+ i2c_bus->dev = dev;
+ i2c_bus->multi_master = device_property_read_bool(dev, "multi-master");
+ i2c_bus->dma_available = device_property_read_bool(dev, "aspeed,enable-dma");
+ if (i2c_bus->dma_abailable)
+ i2c_bus->mode = DMA_MODE;
+ else
+ i2c_bus->mode = BUFF_MODE;
+
+ if (i2c_bus->mode == BUFF_MODE) {
+ i2c_bus->buf_base = devm_platform_get_and_ioremap_resource(pdev, 1, &res);
+ if (IS_ERR(i2c_bus->buf_base))
+ i2c_bus->mode = BYTE_MODE;
+ else
+ i2c_bus->buf_size = resource_size(res) / 2;
+ }
+
+ ast2600_i2c_set_xfer_mode(i2c_bus, i2c_bus->mode);
+
+ /*
+ * i2c timeout counter: use base clk4 1Mhz,
+ * per unit: 1/(1000/1024) = 1024us
+ */
+ ret = device_property_read_u32(dev, "i2c-scl-clk-low-timeout-us", &i2c_bus->timeout);
+ if (!ret)
+ i2c_bus->timeout = DIV_ROUND_UP(i2c_bus->timeout, 1024);
+
+ init_completion(&i2c_bus->cmd_complete);
+
+ i2c_bus->irq = platform_get_irq(pdev, 0);
+ if (i2c_bus->irq < 0)
+ return i2c_bus->irq;
+
+ platform_set_drvdata(pdev, i2c_bus);
+
+ i2c_bus->clk = devm_clk_get(i2c_bus->dev, NULL);
+ if (IS_ERR(i2c_bus->clk))
+ return dev_err_probe(i2c_bus->dev, PTR_ERR(i2c_bus->clk), "Can't get clock\n");
+
+ i2c_bus->apb_clk = clk_get_rate(i2c_bus->clk);
+
+ i2c_parse_fw_timings(i2c_bus->dev, &i2c_bus->timing_info, true);
+
+ /* Initialize the I2C adapter */
+ i2c_bus->adap.owner = THIS_MODULE;
+ i2c_bus->adap.algo = &i2c_ast2600_algorithm;
+ i2c_bus->adap.retries = 0;
+ i2c_bus->adap.dev.parent = i2c_bus->dev;
+ device_set_node(&i2c_bus->adap.dev, dev_fwnode(dev));
+ i2c_bus->adap.algo_data = i2c_bus;
+ strscpy(i2c_bus->adap.name, pdev->name);
+ i2c_set_adapdata(&i2c_bus->adap, i2c_bus);
+
+ ret = ast2600_i2c_init(i2c_bus);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Unable to initial i2c %d\n", ret);
+
+ ret = devm_request_irq(dev, i2c_bus->irq, ast2600_i2c_bus_irq, 0,
+ dev_name(dev), i2c_bus);
+ if (ret < 0) {
+ ret = dev_err_probe(dev, ret, "Unable to request irq %d\n",
+ i2c_bus->irq);
+ goto err;
+ }
+
+ writel(AST2600_I2CM_PKT_DONE | AST2600_I2CM_BUS_RECOVER,
+ i2c_bus->reg_base + AST2600_I2CM_IER);
+
+ ret = devm_i2c_add_adapter(dev, &i2c_bus->adap);
+ if (ret)
+ goto err;
+
+ ret = sysfs_create_file(&dev->kobj, &dev_attr_xfer_mode.attr);
+ if (ret)
+ goto err;
+
+ return 0;
+
+err:
+ writel(0, i2c_bus->reg_base + AST2600_I2CC_FUN_CTRL);
+ writel(0, i2c_bus->reg_base + AST2600_I2CM_IER);
+ return ret;
+}
+
+static void ast2600_i2c_remove(struct platform_device *pdev)
+{
+ struct ast2600_i2c_bus *i2c_bus = platform_get_drvdata(pdev);
+
+ sysfs_remove_file(&pdev->dev.kobj, &dev_attr_xfer_mode.attr);
+
+ /* Disable everything. */
+ writel(0, i2c_bus->reg_base + AST2600_I2CC_FUN_CTRL);
+ writel(0, i2c_bus->reg_base + AST2600_I2CM_IER);
+}
+
+static const struct of_device_id ast2600_i2c_of_match[] = {
+ { .compatible = "aspeed,ast2600-i2c-bus" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ast2600_i2c_of_match);
+
+static struct platform_driver ast2600_i2c_driver = {
+ .probe = ast2600_i2c_probe,
+ .remove = ast2600_i2c_remove,
+ .driver = {
+ .name = "ast2600-i2c",
+ .of_match_table = ast2600_i2c_of_match,
+ },
+};
+module_platform_driver(ast2600_i2c_driver);
+
+MODULE_AUTHOR("Ryan Chen <ryan_chen@aspeedtech.com>");
+MODULE_DESCRIPTION("ASPEED AST2600 I2C Controller Driver");
+MODULE_LICENSE("GPL");
--
2.34.1
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox