* [PATCH 5/5] media: synopsys: Add support for i.MX95
From: Guoniu Zhou @ 2026-04-15 3:46 UTC (permalink / raw)
To: Michael Riesch, Mauro Carvalho Chehab, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Heiko Stuebner,
Laurent Pinchart, Frank Li
Cc: linux-media, linux-kernel, devicetree, imx, linux-arm-kernel,
linux-rockchip, Guoniu Zhou
In-Reply-To: <20260415-csi2_imx95-v1-0-7d63f3508719@oss.nxp.com>
Add support for the i.MX95 MIPI CSI-2 receiver. The i.MX95 variant is
nearly identical to i.MX93, with the main difference being the use of
IDI (Image Data Interface) instead of IPI (Image Pixel Interface).
However, the IDI interface is transparent to software, requiring only
a different register map definition while sharing the same PHY control
functions with i.MX93.
Signed-off-by: Guoniu Zhou <guoniu.zhou@oss.nxp.com>
---
drivers/media/platform/synopsys/dw-mipi-csi2rx.c | 22 ++++++++++++++++++++++
1 file changed, 22 insertions(+)
diff --git a/drivers/media/platform/synopsys/dw-mipi-csi2rx.c b/drivers/media/platform/synopsys/dw-mipi-csi2rx.c
index 27e4c1027816..bbb41baf789e 100644
--- a/drivers/media/platform/synopsys/dw-mipi-csi2rx.c
+++ b/drivers/media/platform/synopsys/dw-mipi-csi2rx.c
@@ -154,6 +154,17 @@ static const u32 imx93_regs[DW_MIPI_CSI2RX_MAX] = {
[DW_MIPI_CSI2RX_IPI_SOFTRSTN] = DW_REG(0xa0),
};
+static const u32 imx95_regs[DW_MIPI_CSI2RX_MAX] = {
+ [DW_MIPI_CSI2RX_N_LANES] = DW_REG(0x4),
+ [DW_MIPI_CSI2RX_RESETN] = DW_REG(0x8),
+ [DW_MIPI_CSI2RX_PHY_SHUTDOWNZ] = DW_REG(0x40),
+ [DW_MIPI_CSI2RX_DPHY_RSTZ] = DW_REG(0x44),
+ [DW_MIPI_CSI2RX_PHY_STATE] = DW_REG(0x48),
+ [DW_MIPI_CSI2RX_PHY_STOPSTATE] = DW_REG(0x4c),
+ [DW_MIPI_CSI2RX_PHY_TST_CTRL0] = DW_REG(0x50),
+ [DW_MIPI_CSI2RX_PHY_TST_CTRL1] = DW_REG(0x54),
+};
+
static const struct v4l2_mbus_framefmt default_format = {
.width = 3840,
.height = 2160,
@@ -901,11 +912,22 @@ static const struct dw_mipi_csi2rx_drvdata imx93_drvdata = {
.wait_for_phy_stopstate = imx93_csi2rx_wait_for_phy_stopstate,
};
+static const struct dw_mipi_csi2rx_drvdata imx95_drvdata = {
+ .regs = imx95_regs,
+ .dphy_assert_reset = imx93_csi2rx_dphy_assert_reset,
+ .dphy_deassert_reset = imx93_csi2rx_dphy_deassert_reset,
+ .wait_for_phy_stopstate = imx93_csi2rx_wait_for_phy_stopstate,
+};
+
static const struct of_device_id dw_mipi_csi2rx_of_match[] = {
{
.compatible = "fsl,imx93-mipi-csi2",
.data = &imx93_drvdata,
},
+ {
+ .compatible = "fsl,imx95-mipi-csi2",
+ .data = &imx95_drvdata,
+ },
{
.compatible = "rockchip,rk3568-mipi-csi2",
.data = &rk3568_drvdata,
--
2.34.1
^ permalink raw reply related
* Re: [PATCH v4 4/9] coresight: etm4x: fix inconsistencies with sysfs configuration
From: Jie Gan @ 2026-04-15 4:25 UTC (permalink / raw)
To: Yeoreum Yun, coresight, linux-arm-kernel, linux-kernel
Cc: suzuki.poulose, mike.leach, james.clark, alexander.shishkin,
leo.yan
In-Reply-To: <20260413142003.3549310-5-yeoreum.yun@arm.com>
On 4/13/2026 10:19 PM, Yeoreum Yun wrote:
> The current ETM4x configuration via sysfs can lead to
> several inconsistencies:
>
> - If the configuration is modified via sysfs while a perf session is
> active, the running configuration may differ before a sched-out and
> after a subsequent sched-in.
>
> - If a perf session and a sysfs session enable tracing concurrently,
> the configuration from configfs may become corrupted.
>
> - There is a risk of corrupting drvdata->config if a perf session enables
> tracing while cscfg_csdev_disable_active_config() is being handled in
> etm4_disable_sysfs().
>
> To resolve these issues, separate the configuration into:
>
> - active_config: the configuration applied to the current session
> - config: the configuration set via sysfs
>
> Additionally:
>
> - Apply the configuration from configfs after taking the appropriate mode.
>
> - Since active_config and related fields are accessed only by the local CPU
> in etm4_enable/disable_sysfs_smp_call() (similar to perf enable/disable),
> remove the lock/unlock from the sysfs enable/disable path and
> startup/dying_cpu except when to access config fields.
>
> Signed-off-by: Yeoreum Yun <yeoreum.yun@arm.com>
> ---
> .../hwtracing/coresight/coresight-etm4x-cfg.c | 2 +-
> .../coresight/coresight-etm4x-core.c | 107 ++++++++++--------
> drivers/hwtracing/coresight/coresight-etm4x.h | 2 +
> 3 files changed, 62 insertions(+), 49 deletions(-)
>
> diff --git a/drivers/hwtracing/coresight/coresight-etm4x-cfg.c b/drivers/hwtracing/coresight/coresight-etm4x-cfg.c
> index d14d7c8a23e5..0553771d04e7 100644
> --- a/drivers/hwtracing/coresight/coresight-etm4x-cfg.c
> +++ b/drivers/hwtracing/coresight/coresight-etm4x-cfg.c
> @@ -47,7 +47,7 @@ static int etm4_cfg_map_reg_offset(struct etmv4_drvdata *drvdata,
> struct cscfg_regval_csdev *reg_csdev, u32 offset)
> {
> int err = -EINVAL, idx;
> - struct etmv4_config *drvcfg = &drvdata->config;
> + struct etmv4_config *drvcfg = &drvdata->active_config;
> u32 off_mask;
>
> if (((offset >= TRCEVENTCTL0R) && (offset <= TRCVIPCSSCTLR)) ||
> diff --git a/drivers/hwtracing/coresight/coresight-etm4x-core.c b/drivers/hwtracing/coresight/coresight-etm4x-core.c
> index 1bc9f13e33f7..01099689525b 100644
> --- a/drivers/hwtracing/coresight/coresight-etm4x-core.c
> +++ b/drivers/hwtracing/coresight/coresight-etm4x-core.c
> @@ -245,6 +245,10 @@ void etm4_release_trace_id(struct etmv4_drvdata *drvdata)
>
> struct etm4_enable_arg {
> struct etmv4_drvdata *drvdata;
> + unsigned long cfg_hash;
> + int preset;
> + u8 trace_id;
> + struct etmv4_config config;
> int rc;
> };
>
> @@ -270,10 +274,11 @@ static void etm4x_prohibit_trace(struct etmv4_drvdata *drvdata)
> static u64 etm4x_get_kern_user_filter(struct etmv4_drvdata *drvdata)
> {
> u64 trfcr = drvdata->trfcr;
> + struct etmv4_config *config = &drvdata->active_config;
>
> - if (drvdata->config.mode & ETM_MODE_EXCL_KERN)
> + if (config->mode & ETM_MODE_EXCL_KERN)
> trfcr &= ~TRFCR_EL1_ExTRE;
> - if (drvdata->config.mode & ETM_MODE_EXCL_USER)
> + if (config->mode & ETM_MODE_EXCL_USER)
> trfcr &= ~TRFCR_EL1_E0TRE;
>
> return trfcr;
> @@ -281,7 +286,7 @@ static u64 etm4x_get_kern_user_filter(struct etmv4_drvdata *drvdata)
>
> /*
> * etm4x_allow_trace - Allow CPU tracing in the respective ELs,
> - * as configured by the drvdata->config.mode for the current
> + * as configured by the drvdata->active_config.mode for the current
> * session. Even though we have TRCVICTLR bits to filter the
> * trace in the ELs, it doesn't prevent the ETM from generating
> * a packet (e.g, TraceInfo) that might contain the addresses from
> @@ -292,12 +297,13 @@ static u64 etm4x_get_kern_user_filter(struct etmv4_drvdata *drvdata)
> static void etm4x_allow_trace(struct etmv4_drvdata *drvdata)
> {
> u64 trfcr, guest_trfcr;
> + struct etmv4_config *config = &drvdata->active_config;
>
> /* If the CPU doesn't support FEAT_TRF, nothing to do */
> if (!drvdata->trfcr)
> return;
>
> - if (drvdata->config.mode & ETM_MODE_EXCL_HOST)
> + if (config->mode & ETM_MODE_EXCL_HOST)
> trfcr = drvdata->trfcr & ~(TRFCR_EL1_ExTRE | TRFCR_EL1_E0TRE);
> else
> trfcr = etm4x_get_kern_user_filter(drvdata);
> @@ -305,7 +311,7 @@ static void etm4x_allow_trace(struct etmv4_drvdata *drvdata)
> write_trfcr(trfcr);
>
> /* Set filters for guests and pass to KVM */
> - if (drvdata->config.mode & ETM_MODE_EXCL_GUEST)
> + if (config->mode & ETM_MODE_EXCL_GUEST)
> guest_trfcr = drvdata->trfcr & ~(TRFCR_EL1_ExTRE | TRFCR_EL1_E0TRE);
> else
> guest_trfcr = etm4x_get_kern_user_filter(drvdata);
> @@ -499,7 +505,7 @@ static int etm4_enable_hw(struct etmv4_drvdata *drvdata)
> {
> int i, rc;
> const struct etmv4_caps *caps = &drvdata->caps;
> - struct etmv4_config *config = &drvdata->config;
> + struct etmv4_config *config = &drvdata->active_config;
> struct coresight_device *csdev = drvdata->csdev;
> struct device *etm_dev = &csdev->dev;
> struct csdev_access *csa = &csdev->access;
> @@ -616,23 +622,46 @@ static int etm4_enable_hw(struct etmv4_drvdata *drvdata)
> static void etm4_enable_sysfs_smp_call(void *info)
> {
> struct etm4_enable_arg *arg = info;
> + struct etmv4_drvdata *drvdata;
> struct coresight_device *csdev;
>
> if (WARN_ON(!arg))
> return;
>
> - csdev = arg->drvdata->csdev;
> + drvdata = arg->drvdata;
> + csdev = drvdata->csdev;
> if (!coresight_take_mode(csdev, CS_MODE_SYSFS)) {
> /* Someone is already using the tracer */
> arg->rc = -EBUSY;
> return;
> }
>
> - arg->rc = etm4_enable_hw(arg->drvdata);
> + drvdata->active_config = arg->config;
>
> - /* The tracer didn't start */
> + if (arg->cfg_hash) {
> + arg->rc = cscfg_csdev_enable_active_config(csdev,
> + arg->cfg_hash,
> + arg->preset);
> + if (arg->rc)
> + goto err;
> + }
> +
> + drvdata->trcid = arg->trace_id;
> +
> + /* Tracer will never be paused in sysfs mode */
> + drvdata->paused = false;
> +
> + arg->rc = etm4_enable_hw(drvdata);
> if (arg->rc)
> - coresight_set_mode(csdev, CS_MODE_DISABLED);
> + goto err;
> +
> + drvdata->sticky_enable = true;
> +
> + return;
> +err:
> + /* The tracer didn't start */
> + etm4_release_trace_id(drvdata);
[NIT] better move this error handle to etm4_enable_sysfs.
smp_call_function_single possible return before call
etm4_enable_sysfs_smp_call if the cpu is offline. The error path here
cannot handle this error, breaking previous logic(handle all errors in
etm4_enable_sysfs by releasing trace id).
There is no harm in not releasing the trace id here because ETM can
re-use the allocated trace ID. But it's better to fix the inconsistent
logic.
Thanks,
Jie
> + coresight_set_mode(csdev, CS_MODE_DISABLED);
> }
>
> /*
> @@ -670,7 +699,7 @@ static int etm4_config_timestamp_event(struct etmv4_drvdata *drvdata,
> int ctridx;
> int rselector;
> const struct etmv4_caps *caps = &drvdata->caps;
> - struct etmv4_config *config = &drvdata->config;
> + struct etmv4_config *config = &drvdata->active_config;
>
> /* No point in trying if we don't have at least one counter */
> if (!caps->nr_cntr)
> @@ -754,7 +783,7 @@ static int etm4_parse_event_config(struct coresight_device *csdev,
> int ret = 0;
> struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
> const struct etmv4_caps *caps = &drvdata->caps;
> - struct etmv4_config *config = &drvdata->config;
> + struct etmv4_config *config = &drvdata->active_config;
> struct perf_event_attr max_timestamp = {
> .ATTR_CFG_FLD_timestamp_CFG = U64_MAX,
> };
> @@ -916,38 +945,24 @@ static int etm4_enable_sysfs(struct coresight_device *csdev, struct coresight_pa
>
> /* enable any config activated by configfs */
> cscfg_config_sysfs_get_active_cfg(&cfg_hash, &preset);
> - if (cfg_hash) {
> - ret = cscfg_csdev_enable_active_config(csdev, cfg_hash, preset);
> - if (ret) {
> - etm4_release_trace_id(drvdata);
> - return ret;
> - }
> - }
> -
> - raw_spin_lock(&drvdata->spinlock);
> -
> - drvdata->trcid = path->trace_id;
> -
> - /* Tracer will never be paused in sysfs mode */
> - drvdata->paused = false;
>
> /*
> * Executing etm4_enable_hw on the cpu whose ETM is being enabled
> * ensures that register writes occur when cpu is powered.
> */
> arg.drvdata = drvdata;
> + arg.cfg_hash = cfg_hash;
> + arg.preset = preset;
> + arg.trace_id = path->trace_id;
> +
> + raw_spin_lock(&drvdata->spinlock);
> + arg.config = drvdata->config;
> + raw_spin_unlock(&drvdata->spinlock);
> +
> ret = smp_call_function_single(drvdata->cpu,
> etm4_enable_sysfs_smp_call, &arg, 1);
> if (!ret)
> ret = arg.rc;
> - if (!ret)
> - drvdata->sticky_enable = true;
> -
> - if (ret)
> - etm4_release_trace_id(drvdata);
> -
> - raw_spin_unlock(&drvdata->spinlock);
> -
> if (!ret)
> dev_dbg(&csdev->dev, "ETM tracing enabled\n");
> return ret;
> @@ -1036,7 +1051,7 @@ static void etm4_disable_hw(struct etmv4_drvdata *drvdata)
> {
> u32 control;
> const struct etmv4_caps *caps = &drvdata->caps;
> - struct etmv4_config *config = &drvdata->config;
> + struct etmv4_config *config = &drvdata->active_config;
> struct coresight_device *csdev = drvdata->csdev;
> struct csdev_access *csa = &csdev->access;
> int i;
> @@ -1072,6 +1087,8 @@ static void etm4_disable_sysfs_smp_call(void *info)
>
> etm4_disable_hw(drvdata);
>
> + cscfg_csdev_disable_active_config(drvdata->csdev);
> +
> coresight_set_mode(drvdata->csdev, CS_MODE_DISABLED);
> }
>
> @@ -1122,7 +1139,6 @@ static void etm4_disable_sysfs(struct coresight_device *csdev)
> * DYING hotplug callback is serviced by the ETM driver.
> */
> cpus_read_lock();
> - raw_spin_lock(&drvdata->spinlock);
>
> /*
> * Executing etm4_disable_hw on the cpu whose ETM is being disabled
> @@ -1131,10 +1147,6 @@ static void etm4_disable_sysfs(struct coresight_device *csdev)
> smp_call_function_single(drvdata->cpu, etm4_disable_sysfs_smp_call,
> drvdata, 1);
>
> - raw_spin_unlock(&drvdata->spinlock);
> -
> - cscfg_csdev_disable_active_config(csdev);
> -
> cpus_read_unlock();
>
> /*
> @@ -1377,6 +1389,7 @@ static void etm4_init_arch_data(void *info)
> struct etm4_init_arg *init_arg = info;
> struct etmv4_drvdata *drvdata;
> struct etmv4_caps *caps;
> + struct etmv4_config *config;
> struct csdev_access *csa;
> struct device *dev = init_arg->dev;
> int i;
> @@ -1384,6 +1397,7 @@ static void etm4_init_arch_data(void *info)
> drvdata = dev_get_drvdata(init_arg->dev);
> caps = &drvdata->caps;
> csa = init_arg->csa;
> + config = &drvdata->active_config;
>
> /*
> * If we are unable to detect the access mechanism,
> @@ -1444,7 +1458,7 @@ static void etm4_init_arch_data(void *info)
>
> /* EXLEVEL_S, bits[19:16] Secure state instruction tracing */
> caps->s_ex_level = FIELD_GET(TRCIDR3_EXLEVEL_S_MASK, etmidr3);
> - drvdata->config.s_ex_level = caps->s_ex_level;
> + config->s_ex_level = caps->s_ex_level;
> /* EXLEVEL_NS, bits[23:20] Non-secure state instruction tracing */
> caps->ns_ex_level = FIELD_GET(TRCIDR3_EXLEVEL_NS_MASK, etmidr3);
> /*
> @@ -1689,7 +1703,7 @@ static void etm4_set_default(struct etmv4_config *config)
> static int etm4_get_next_comparator(struct etmv4_drvdata *drvdata, u32 type)
> {
> int nr_comparator, index = 0;
> - struct etmv4_config *config = &drvdata->config;
> + struct etmv4_config *config = &drvdata->active_config;
>
> /*
> * nr_addr_cmp holds the number of comparator _pair_, so time 2
> @@ -1730,7 +1744,7 @@ static int etm4_set_event_filters(struct etmv4_drvdata *drvdata,
> {
> int i, comparator, ret = 0;
> u64 address;
> - struct etmv4_config *config = &drvdata->config;
> + struct etmv4_config *config = &drvdata->active_config;
> struct etm_filters *filters = event->hw.addr_filters;
>
> if (!filters)
> @@ -1848,13 +1862,11 @@ static int etm4_starting_cpu(unsigned int cpu)
> if (!etmdrvdata[cpu])
> return 0;
>
> - raw_spin_lock(&etmdrvdata[cpu]->spinlock);
> if (!etmdrvdata[cpu]->os_unlock)
> etm4_os_unlock(etmdrvdata[cpu]);
>
> if (coresight_get_mode(etmdrvdata[cpu]->csdev))
> etm4_enable_hw(etmdrvdata[cpu]);
> - raw_spin_unlock(&etmdrvdata[cpu]->spinlock);
> return 0;
> }
>
> @@ -1863,10 +1875,8 @@ static int etm4_dying_cpu(unsigned int cpu)
> if (!etmdrvdata[cpu])
> return 0;
>
> - raw_spin_lock(&etmdrvdata[cpu]->spinlock);
> if (coresight_get_mode(etmdrvdata[cpu]->csdev))
> etm4_disable_hw(etmdrvdata[cpu]);
> - raw_spin_unlock(&etmdrvdata[cpu]->spinlock);
> return 0;
> }
>
> @@ -2252,7 +2262,8 @@ static int etm4_add_coresight_dev(struct etm4_init_arg *init_arg)
> if (!desc.name)
> return -ENOMEM;
>
> - etm4_set_default(&drvdata->config);
> + etm4_set_default(&drvdata->active_config);
> + drvdata->config = drvdata->active_config;
>
> pdata = coresight_get_platform_data(dev);
> if (IS_ERR(pdata))
> diff --git a/drivers/hwtracing/coresight/coresight-etm4x.h b/drivers/hwtracing/coresight/coresight-etm4x.h
> index 8864cfb76bad..725e6360a8b9 100644
> --- a/drivers/hwtracing/coresight/coresight-etm4x.h
> +++ b/drivers/hwtracing/coresight/coresight-etm4x.h
> @@ -1071,6 +1071,7 @@ struct etmv4_save_state {
> * allows tracing at all ELs. We don't want to compute this
> * at runtime, due to the additional setting of TRFCR_CX when
> * in EL2. Otherwise, 0.
> + * @active_config: structure holding current applied configuration parameters.
> * @config: structure holding configuration parameters.
> * @save_state: State to be preserved across power loss
> * @paused: Indicates if the trace unit is paused.
> @@ -1091,6 +1092,7 @@ struct etmv4_drvdata {
> bool os_unlock : 1;
> bool paused : 1;
> u64 trfcr;
> + struct etmv4_config active_config;
> struct etmv4_config config;
> struct etmv4_save_state *save_state;
> DECLARE_BITMAP(arch_features, ETM4_IMPDEF_FEATURE_MAX);
^ permalink raw reply
* [PATCH v29 2/4] dt-bindings: i2c: ast2600-i2c.yaml: Add global-regs properties
From: Ryan Chen @ 2026-04-15 5:14 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: <20260415-upstream_i2c-v29-0-317c1a905ae1@aspeedtech.com>
Add the aspeed,global-regs phandle to reference the AST2600 global
registers syscon node, containing the SoC-common I2C register set.
These properties apply only to the AST2600 binding. Legacy DTs remain
unchanged.
Signed-off-by: Ryan Chen <ryan_chen@aspeedtech.com>
---
Changes in v29:
- remove aspeed,enable-dma properties.
Changes in v28:
- update commit message correspond with aspeed,enable-dma.
- remove aspeed,transfer-mode and add aspeed,enable-dma property and
description.
- Fix aspeed,enable-dma description to reflect hardware capability rather
than software behavior
Changes in v27:
- change aspeed,transfer-mode to aspeed,enable-dma.
---
Documentation/devicetree/bindings/i2c/aspeed,ast2600-i2c.yaml | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/Documentation/devicetree/bindings/i2c/aspeed,ast2600-i2c.yaml b/Documentation/devicetree/bindings/i2c/aspeed,ast2600-i2c.yaml
index de2c359037da..0c769efb76a5 100644
--- a/Documentation/devicetree/bindings/i2c/aspeed,ast2600-i2c.yaml
+++ b/Documentation/devicetree/bindings/i2c/aspeed,ast2600-i2c.yaml
@@ -37,6 +37,12 @@ properties:
resets:
maxItems: 1
+ 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 +65,5 @@ examples:
resets = <&syscon ASPEED_RESET_I2C>;
clock-frequency = <100000>;
interrupts = <GIC_SPI 110 IRQ_TYPE_LEVEL_HIGH>;
+ aspeed,global-regs = <&i2c_global>;
};
--
2.34.1
^ permalink raw reply related
* [PATCH v29 0/4] Add ASPEED AST2600 I2C controller driver
From: Ryan Chen @ 2026-04-15 5:14 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
This series adds support for the AST2600 I2C controller “new register
set” implementation.
The AST2600 I2C controller introduces a revised register layout which
separates controller and target functionality into distinct register
blocks, and extends clock divider configuration and packet-based
transfer support compared to the legacy mixed register layout used on
earlier ASPEED SoCs.
The current driver implementation for the AST2600 I2C peripheral is
through the hardware's "compatibility mode", which exposes a register
set that matches the previous generation hardware (AST2500 and earlier).
Instead, add a driver that works in new-register-set mode, to allow the
new features, and will provide support for future hardware that will
not implement compatibility mode.
In order to support the new mode, we need a DT binding change to
reflect the reference to the global register set. Since the binding
still represents the same (AST2600 SoC) physical hardware, we continue
to use the existing compatible string of "aspeed,ast2600-i2c-bus".
However: since we're changing semantics for an existing binding, we
allow backwards compatibility by selecting on presence/absence of the
newly-added properties, and fall back to the old driver (ie., in
compatibility mode) when we detect a DT using the old binding spec.
Specifically:
- ast2600-i2c-bus nodes that provide the `aspeed,global-regs` property
(present in the new binding and absent in the legacy binding) will be
successfully probed by the new driver
- ast2600-i2c-bus nodes without `aspeed,global-regs` continue to use the
existing driver (in legacy register mode), ensuring that platforms
with the current DTBs remain functional
Signed-off-by: Ryan Chen <ryan_chen@aspeedtech.com>
---
Changes in v29:
- 2/4: remove aspeed,enable-dma properties.
- 3/4: update commit message remove transfer mode selection.
- 3/4: remove sysfs file.
- 3/4: remove define I2C_TARGET_MSG_BUF_SIZE and AST2600_I2C_DMA_SIZE.
- 3/4: remove buf_index in struct ast2600_i2c_bus.
- 3/4, 4/4: remove dma/byte mode, use buffer mode only.
- 4/4: fix race between unreg_target and IRQ handler.
- 4/4: move i2cs ier enable from ast2600_i2c_init to probe after master ier enable.
- Link to v28: https://lore.kernel.org/r/20260330-upstream_i2c-v28-0-17bdae39c5cb@aspeedtech.com
Changes in v28:
- 2/4: update commit message correspond with aspeed,enable-dma.
- 2/4: remove aspeed,transfer-mode and add aspeed,enable-dma property
and description.
- 2/4: Fix aspeed,enable-dma description to reflect hardware capability
rather than software behavior.
- 3/4: Separate xfer_mode_store into distinct parse and availability-check
steps by introducing ast2600_i2c_xfer_mode_check().
- 3/4: fix tx dma memcpy source point address.
- 3/4: Use a temporary variable for devm_platform_get_and_ioremap_resource()
to avoid storing an ERR_PTR in i2c_bus->buf_base; drop the redundant
NULL assignment in the error path since i2c_bus is kzalloc()ed.
- 3/4: Add ABI documentation file
Documentation/ABI/testing/sysfs-driver-ast2600-i2c.
- 4/4: fix typo condication -> condition.
- 4/4: fix compile error, when disable CONFIG_I2C_SLAVE.
- Link to v27: https://lore.kernel.org/r/20260324-upstream_i2c-v27-0-f19b511c8c28@aspeedtech.com
Changes in v27:
- 1/4 use aspeed,enable-dma instead aspeed,transfer-mode.
- 2/4 remove aspeed,transfer-mode selection instad aspeed,transfer-mode
- 2/4 add sysfs for xfer mode.
- Link to v26: https://lore.kernel.org/r/20260309-upstream_i2c-v26-0-5fedcff8ffe8@aspeedtech.com
Changes in v26:
- 1/4: binding reworks based on review feedback
- Link to v25: https://lore.kernel.org/r/20260225-upstream_i2c-v25-0-9f4bdd954f3f@aspeedtech.com
Changes in v25:
- Use b4 to send series.
- Rebase on v7.0-rc1.
- Clarify cover letter and commit logs based on review feedback.
- Remove the i2c-aspeed-core multiplexer infrastructure and
implement driver selection via conditional -ENODEV handling
in individual probe() functions.
- 3/4: incorporate review feedback and refactor new driver
- Link to v24: https://lore.kernel.org/r/20251118014034.820988-1-ryan_chen@aspeedtech.com
Changes in v24:
- aspeed,ast2600-i2c.yaml
- fix make dt_binding_check blank warning.
- Link to v23: https://lore.kernel.org/all/20251117025040.3622984-1-ryan_chen@aspeedtech.com/
Changes in v23:
- update typo patch (1/4) commit message.
- aspeed,ast2600-i2c.yaml
- update reg and description.
- i2c-ast2600.c controller
- replace ast2600_select_i2c_clock to ast2600_i2c_ac_timing_config.
- i2c-ast2600.c target
- I2C_TARGET_MSG_BUF_SIZE 256 to 4096
- remove blank line.
- refine Master comment description to controller
- Link to v22: https://lore.kernel.org/all/20251112085649.1903631-1-ryan_chen@aspeedtech.com/
Changes in v22:
- update patch (1/4) commit message add dts example reason.
- aspeed,ast2600-i2c.yaml @patch (1/4)
- rename ast2600-i2c.yaml to aspeed,ast2600-i2c.yaml.
- update reg, clock-frequency description.
- aspeed,ast2600-i2c.yaml @patch (2/4)
- aspeed,transfer-mode, aspeed,transfer-mode add for ast2600.
- i2c-aspeed-core.c,h @patch (3/4)
- add i2c-aspeed-core allow both old and new device trees using the
same compatible string "aspeed,ast2600-i2c-bus".
- Link to v21: https://lore.kernel.org/all/20251027061240.3427875-1-ryan_chen@aspeedtech.com/
Changes in v21:
- update patch (1/4) commit message
- i2c-ast2600.c
- move rst to local variable in ast2600_i2c_probe().
- Link to v20: https://lore.kernel.org/all/20251021013548.2375190-1-ryan_chen@aspeedtech.com/
Changes in v20:
- ast2600-i2c.yaml
- fix warning at make dt_binding_check.
- Link to v19: https://lore.kernel.org/all/20251020013200.1858325-1-ryan_chen@aspeedtech.com/
Changes in v19:
- Split AST2600 binding into its own YAML file
- Removed `aspeed,ast2600-i2c-bus` from `aspeed,i2c.yaml`
- Added `aspeed,global-regs` and `aspeed,transfer-mode` to AST2600 binding
- Link to v18: https://lore.kernel.org/all/20250820051832.3605405-1-ryan_chen@aspeedtech.com/
Changes in v18:
- refine patch (1/3) commit message (reason for commit not list.)
- i2c-ast2600.c
- remove redundant reset_control_deassert in driver probe.
- remove reset_control_assert(i2c_bus->rst) in driver remove.
- Link to v17: https://lore.kernel.org/all/20250814084156.1650432-1-ryan_chen@aspeedtech.com/
Changes in v17:
- move i2c new mode register and feature into driver commit message.
- aspeed,i2c.yaml
- remove multi-master properties.
- use aspeed,transfer-mode properties for aspeed,enable-byte/enable-dma.
-i2c-ast2600.c
- rename dma_safe_buf to controller_dma_safe_buf.
- fix ast2600_i2c_recover_bus return overflow warnings.
- add ast2600_i2c_target_packet_buff_irq unhandle case.
- add parameter "cmd" in ast2600_i2c_setup_dma_rx,
ast2600_i2c_setup_buff_rx, ast2600_i2c_setup_byte_rx
- use reset_control_deassert replace
devm_reset_control_get_shared_deasserted.
- useaspeed,transfer-mode properties for transfer mode setting.
- change compatible = "aspeed,ast2600-i2cv2" to "aspeed,ast2600-i2c-bus".
- Link to v16: https://lore.kernel.org/all/20250224055936.1804279-1-ryan_chen@aspeedtech.com/
Changes in v16:
- aspeed,i2c.yaml: add aspeed,enable-byte properties for force byte mode.
- i2c-ast2600.c
- change include asm/unaligned.h to linux/unaligned.h.
- add reset timeout councter when slave active timeout.
- modify issue i2c_recovery_bus before slave re-enable.
- add aspeed,enable-byte properties.
- Link to v15: https://lore.kernel.org/all/20241007035235.2254138-1-ryan_chen@aspeedtech.com/
Changes in v15:
- i2c-ast2600.c
- add include unaligned.h
- rename all master -> controller, slave -> target.
- keep multi-master to align property.
- remove no used element in ast2600_i2c_bus.
- Link to v14: https://lore.kernel.org/all/20241002070213.1165263-1-ryan_chen@aspeedtech.com/
Changes in v14:
- aspeed,i2c.yaml
- v13 change people reviewed-by tag, v14 fixed to original people tag,
modify to Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
- struct ast2600_i2c_bus layout optimal.
- ast2600_select_i2c_clock refine.
- ast2600_i2c_recover_bus overridden fix.
- dma_mapping_error() returned error code shadowed modify.
- buffer register in a 4-byte aligned simplified
- remove smbus alert
- Link to v13: https://lore.kernel.org/all/20240819092850.1590758-1-ryan_chen@aspeedtech.com/
Changes in v13:
- separate i2c master and slave driver to be two patchs.
- modify include header list, add bits.h include. remove of*.h
- modify (((x) >> 24) & GENMASK(5, 0)) to (((x) & GENMASK(29, 24)) >> 24)
- modify ast2600_select_i2c_clock function implement.
- modify ast2600_i2c_recover_bus function u32 claim to
u32 state = readl(i2c_bus->reg_base + AST2600_I2CC_STS_AND_BUFF);
- Link to v12: https://lore.kernel.org/all/20230714074522.23827-1-ryan_chen@aspeedtech.com/
Changes in v12:
- aspeed,i2c.yaml
- add Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
- i2c-ast2600.c
- update include by alphabetical order
- make just a one TAB and put the last two lines on the single one
- remove no used timing_table structre
- remove enum explicit assinment
- rewritten to avoid this and using loop in ast2600_select_i2c_clock
- use GENMASK for most 0xffff
- remove too many parentheses
- use str_read_write replace read write string
- remove redundant blank line after ast2600_i2c_bus_of_table
- fix wrong multi-line style of the comment
- use macro for i2c standard speeds
- remove useless noise dev_info
- Link to v11: https://lore.kernel.org/all/20230430041712.3247998-1-ryan_chen@aspeedtech.com/
Changes in v11:
- aspeed,i2c.yaml
- no change, the same with v10.
- i2c-ast2600.c
- modify alert_enable from int -> boolean.
- modify dbg string recovery -> recover.
- remove no need to init 0.
- remove new line after break.
- remove unneeded empty line.
- modify dma_alloc_coherent to dmam_alloc_coherent
- modify probe nomem return dev_err_probe
- modify i2c_add_adapter to devm_i2c_adapter
- modify checkpatch: Alignment should match open parenthesis
- modify checkpatch: braces {} should be used on all arms of this statement
- modify checkpatch: Unbalanced braces around else statement
- Link to v10: https://lore.kernel.org/all/20230415012848.1777768-1-ryan_chen@aspeedtech.com/
Changes in v10:
- aspeed,i2c.yaml
- move unevaluatedProperties after allOf.
- remove extra one blank line.
- i2c-ast2600.c
- no change, the same with v8.
- Link to v9: https://lore.kernel.org/all/20230405022825.333246-1-ryan_chen@aspeedtech.com/
Changes in v9:
- aspeed,i2c.yaml
- backoff to v7.
- no fix typo in maintainer's name and email. this would be another patch.
- no remove address-cells, size-cells, this would be another patch.
- use aspeed,enable-dma property instead of aspeed,xfer-mode selection.
- fix allOf and else false properties for aspeed,ast2600-i2cv2.
- i2c-ast2600.c
- no change, the same with v8
- Link to v8: https://lore.kernel.org/all/20230330073259.485606-1-ryan_chen@aspeedtech.com/
Changes in v8:
- aspeed,i2c.yaml
- modify commit message.
- Fix typo in maintainer's name and email.
- remove address-cells, size-cells.
- i2c-ast2600.c
- move "i2c timeout counter" comment description before property_read.
- remove redundant code "return ret" in probe end.
- Link to v7: https://lore.kernel.org/all/20230327092524.3916389-1-ryan_chen@aspeedtech.com/
Changes in v7:
- aspeed,i2c.yaml
- Update ASPEED I2C maintainers email.
- use aspeed,enable-dma property instead of aspeed,xfer-mode selection.
- fix allOf and else false properties for aspeed,ast2600-i2cv2.
- i2c-ast2600.c
- remove aspeed,xfer-mode instead of aspeed,enable-dma mode. buffer mode
is default.
- remove aspeed,timeout instead of i2c-scl-clk-low-timeout-us for
timeout setting.
- Link to v6: https://lore.kernel.org/all/20230226031321.3126756-1-ryan_chen@aspeedtech.com/
Changes in v6:
- remove aspeed,i2cv2.yaml, merge to aspeed,i2c.yaml -add support for
i2cv2 properites.
- i2c-ast2600.c
- fix ast2600_i2c_remove ordering.
- remove ast2600_i2c_probe goto labels, and add dev_err_probe -remove
redundant deb_dbg debug message.
- rename gr_regmap -> global_regs
- Link to v5: https://lore.kernel.org/all/20230220061745.1973981-1-ryan_chen@aspeedtech.com/
Changes in v5:
- remove ast2600-i2c-global.yaml, i2c-ast2600-global.c.
- i2c-ast2600.c
- remove legacy clock divide, all go for new clock divide.
- remove duplicated read isr.
- remove no used driver match
- fix probe return for each labels return.
- global use mfd driver, driver use phandle to regmap read/write.
- rename aspeed,i2c-ast2600.yaml to aspeed,i2cv2.yaml -remove bus-frequency.
- add required aspeed,gr
- add timeout, byte-mode, buff-mode properites.
- Link to v4: https://lore.kernel.org/all/20230201103359.1742140-1-ryan_chen@aspeedtech.com/
Changes in v4:
- fix i2c-ast2600.c driver buffer mode use single buffer conflit in
master slave mode both enable.
- fix kmemleak issue when use dma mode.
- fix typo aspeed,i2c-ast2600.yaml compatible is "aspeed,ast2600-i2c"
- fix typo aspeed,i2c-ast2600.ymal to aspeed,i2c-ast2600.yaml
- Link to v3: https://lore.kernel.org/all/20220516064900.30517-1-ryan_chen@aspeedtech.com/
Changes in v3:
- fix i2c global clock divide default value.
- remove i2c slave no used dev_dbg info.
- Link to v2: https://lore.kernel.org/all/20220413101735.27678-1-ryan_chen@aspeedtech.com/
Changes in v2:
- add i2c global ymal file commit.
- rename file name from new to ast2600.
aspeed-i2c-new-global.c -> i2c-ast2600-global.c
aspeed-i2c-new-global.h -> i2c-ast2600-global.h
i2c-new-aspeed.c -> i2c-ast2600.c
- rename all driver function name to ast2600.
- Link to v1: https://lore.kernel.org/all/20220323004009.943298-1-ryan_chen@aspeedtech.com/
---
Ryan Chen (4):
dt-bindings: i2c: Split AST2600 binding into a new YAML
dt-bindings: i2c: ast2600-i2c.yaml: Add global-regs properties
i2c: ast2600: Add controller driver for AST2600 new register set
i2c: ast2600: Add target mode support
.../bindings/i2c/aspeed,ast2600-i2c.yaml | 69 ++
.../devicetree/bindings/i2c/aspeed,i2c.yaml | 3 +-
drivers/i2c/busses/Makefile | 2 +-
drivers/i2c/busses/i2c-aspeed.c | 5 +
drivers/i2c/busses/i2c-ast2600.c | 1148 ++++++++++++++++++++
5 files changed, 1224 insertions(+), 3 deletions(-)
---
base-commit: 6de23f81a5e08be8fbf5e8d7e9febc72a5b5f27f
change-id: 20260223-upstream_i2c-ebd07f89739c
Best regards,
--
Ryan Chen <ryan_chen@aspeedtech.com>
^ permalink raw reply
* [PATCH v29 1/4] dt-bindings: i2c: Split AST2600 binding into a new YAML
From: Ryan Chen @ 2026-04-15 5:14 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: <20260415-upstream_i2c-v29-0-317c1a905ae1@aspeedtech.com>
The AST2600 I2C controller introduces a completely new register layout
with separate controller and target register blocks, unlike the mixed
register layout used by AST2400/AST2500.
Move AST2600 I2C binding from aspeed,i2c.yaml to a dedicated
aspeed,ast2600-i2c.yaml schema.
Besides the split, this also adjusts for AST2600-specific requirements.
- require two reg regions (controller register block + buffer block)
- use clock-frequency for bus speed description
- interrupts are required on AST2600
- use correct DTS coding style in example
No compatible strings are changed.
Signed-off-by: Ryan Chen <ryan_chen@aspeedtech.com>
---
Changes in v26:
- commit message: include details of changes from original binding
- fix example property ordering to follow DTS coding style
- use consistent "AST2600" naming
---
.../bindings/i2c/aspeed,ast2600-i2c.yaml | 62 ++++++++++++++++++++++
.../devicetree/bindings/i2c/aspeed,i2c.yaml | 3 +-
2 files changed, 63 insertions(+), 2 deletions(-)
diff --git a/Documentation/devicetree/bindings/i2c/aspeed,ast2600-i2c.yaml b/Documentation/devicetree/bindings/i2c/aspeed,ast2600-i2c.yaml
new file mode 100644
index 000000000000..de2c359037da
--- /dev/null
+++ b/Documentation/devicetree/bindings/i2c/aspeed,ast2600-i2c.yaml
@@ -0,0 +1,62 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/i2c/aspeed,ast2600-i2c.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: ASPEED I2C on the AST2600 SoCs
+
+maintainers:
+ - Ryan Chen <ryan_chen@aspeedtech.com>
+
+allOf:
+ - $ref: /schemas/i2c/i2c-controller.yaml#
+
+properties:
+ compatible:
+ enum:
+ - aspeed,ast2600-i2c-bus
+
+ reg:
+ items:
+ - description: controller registers
+ - description: controller buffer space
+
+ interrupts:
+ maxItems: 1
+
+ clocks:
+ maxItems: 1
+
+ clock-frequency:
+ description: Desired operating frequency of the I2C bus in Hz.
+ minimum: 500
+ maximum: 4000000
+ default: 100000
+
+ resets:
+ maxItems: 1
+
+required:
+ - reg
+ - compatible
+ - clocks
+ - resets
+ - interrupts
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/clock/aspeed-clock.h>
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+ i2c@80 {
+ compatible = "aspeed,ast2600-i2c-bus";
+ reg = <0x80 0x80>, <0xc00 0x20>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ clocks = <&syscon ASPEED_CLK_APB>;
+ resets = <&syscon ASPEED_RESET_I2C>;
+ clock-frequency = <100000>;
+ interrupts = <GIC_SPI 110 IRQ_TYPE_LEVEL_HIGH>;
+ };
diff --git a/Documentation/devicetree/bindings/i2c/aspeed,i2c.yaml b/Documentation/devicetree/bindings/i2c/aspeed,i2c.yaml
index 5b9bd2feda3b..d4e4f412feba 100644
--- a/Documentation/devicetree/bindings/i2c/aspeed,i2c.yaml
+++ b/Documentation/devicetree/bindings/i2c/aspeed,i2c.yaml
@@ -4,7 +4,7 @@
$id: http://devicetree.org/schemas/i2c/aspeed,i2c.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
-title: ASPEED I2C on the AST24XX, AST25XX, and AST26XX SoCs
+title: ASPEED I2C on the AST24XX, AST25XX SoCs
maintainers:
- Rayn Chen <rayn_chen@aspeedtech.com>
@@ -17,7 +17,6 @@ properties:
enum:
- aspeed,ast2400-i2c-bus
- aspeed,ast2500-i2c-bus
- - aspeed,ast2600-i2c-bus
reg:
minItems: 1
--
2.34.1
^ permalink raw reply related
* [PATCH v29 4/4] i2c: ast2600: Add target mode support
From: Ryan Chen @ 2026-04-15 5:14 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: <20260415-upstream_i2c-v29-0-317c1a905ae1@aspeedtech.com>
Add target mode support to the AST2600 I2C driver.
Target mode features implemented include:
- Add target interrupt handling
- Address match and response logic
This complements the existing controller-mode support, enabling
dual-role capability.
Signed-off-by: Ryan Chen <ryan_chen@aspeedtech.com>
---
Changes in v29:
- fix race between unreg_target and IRQ handler.
- move i2cs ier enable from ast2600_i2c_init to probe after master ier enable.
- remove dma/byte transfer, use buffer mode only.
Changes in v28:
- fix typo condication -> condition
- fix compile error, when disable CONFIG_I2C_SLAVE
Changes in v26:
- change int to bool target_operate
- rename target_operate to target_active
- use i2c_bus->target replace require IO
- use WRITE_ONCE replace target_operate write.
---
drivers/i2c/busses/i2c-ast2600.c | 340 +++++++++++++++++++++++++++++++++++++++
1 file changed, 340 insertions(+)
diff --git a/drivers/i2c/busses/i2c-ast2600.c b/drivers/i2c/busses/i2c-ast2600.c
index 787ef6bd6244..137aaf7e26b5 100644
--- a/drivers/i2c/busses/i2c-ast2600.c
+++ b/drivers/i2c/busses/i2c-ast2600.c
@@ -254,6 +254,11 @@ struct ast2600_i2c_bus {
size_t buf_size;
bool multi_master;
void __iomem *buf_base;
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+ /* target structure */
+ bool target_active;
+ struct i2c_client *target;
+#endif
};
static void ast2600_i2c_ac_timing_config(struct ast2600_i2c_bus *i2c_bus)
@@ -337,6 +342,243 @@ static int ast2600_i2c_recover_bus(struct ast2600_i2c_bus *i2c_bus)
return ret;
}
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+static void ast2600_i2c_target_packet_buff_irq(struct ast2600_i2c_bus *i2c_bus, u32 sts)
+{
+ int target_rx_len = 0;
+ u32 cmd = 0;
+ u8 value;
+ int i;
+
+ /* due to controller target is common buffer, need force the master stop not issue */
+ if (readl(i2c_bus->reg_base + AST2600_I2CM_CMD_STS) & GENMASK(15, 0)) {
+ writel(0, i2c_bus->reg_base + AST2600_I2CM_CMD_STS);
+ i2c_bus->cmd_err = -EBUSY;
+ writel(0, i2c_bus->reg_base + AST2600_I2CC_BUFF_CTRL);
+ complete(&i2c_bus->cmd_complete);
+ }
+
+ /* Handle i2c target timeout condition */
+ if (AST2600_I2CS_INACTIVE_TO & sts) {
+ /* Reset timeout counter */
+ u32 ac_timing = readl(i2c_bus->reg_base + AST2600_I2CC_AC_TIMING) &
+ AST2600_I2CC_AC_TIMING_MASK;
+
+ writel(ac_timing, i2c_bus->reg_base + AST2600_I2CC_AC_TIMING);
+ ac_timing |= AST2600_I2CC_TTIMEOUT(i2c_bus->timeout);
+ writel(ac_timing, i2c_bus->reg_base + AST2600_I2CC_AC_TIMING);
+ writel(TARGET_TRIGGER_CMD, i2c_bus->reg_base + AST2600_I2CS_CMD_STS);
+ writel(AST2600_I2CS_PKT_DONE, i2c_bus->reg_base + AST2600_I2CS_ISR);
+ i2c_slave_event(i2c_bus->target, I2C_SLAVE_STOP, &value);
+ WRITE_ONCE(i2c_bus->target_active, false);
+ return;
+ }
+
+ sts &= ~(AST2600_I2CS_PKT_DONE | AST2600_I2CS_PKT_ERROR);
+
+ if (sts & AST2600_I2CS_SLAVE_MATCH)
+ WRITE_ONCE(i2c_bus->target_active, true);
+
+ switch (sts) {
+ case AST2600_I2CS_SLAVE_PENDING | AST2600_I2CS_WAIT_RX_DMA |
+ AST2600_I2CS_SLAVE_MATCH | AST2600_I2CS_RX_DONE | AST2600_I2CS_STOP:
+ case AST2600_I2CS_SLAVE_PENDING |
+ AST2600_I2CS_SLAVE_MATCH | AST2600_I2CS_RX_DONE | AST2600_I2CS_STOP:
+ case AST2600_I2CS_SLAVE_PENDING |
+ AST2600_I2CS_SLAVE_MATCH | AST2600_I2CS_STOP:
+ i2c_slave_event(i2c_bus->target, I2C_SLAVE_STOP, &value);
+ fallthrough;
+ case AST2600_I2CS_SLAVE_PENDING |
+ AST2600_I2CS_WAIT_RX_DMA | AST2600_I2CS_SLAVE_MATCH | AST2600_I2CS_RX_DONE:
+ case AST2600_I2CS_WAIT_RX_DMA | AST2600_I2CS_SLAVE_MATCH | AST2600_I2CS_RX_DONE:
+ case AST2600_I2CS_WAIT_RX_DMA | AST2600_I2CS_SLAVE_MATCH:
+ i2c_slave_event(i2c_bus->target, I2C_SLAVE_WRITE_REQUESTED, &value);
+ cmd = TARGET_TRIGGER_CMD;
+ if (sts & AST2600_I2CS_RX_DONE) {
+ target_rx_len = AST2600_I2CC_GET_RX_BUF_LEN(readl(i2c_bus->reg_base +
+ AST2600_I2CC_BUFF_CTRL));
+ for (i = 0; i < target_rx_len; i++) {
+ value = readb(i2c_bus->buf_base + 0x10 + i);
+ i2c_slave_event(i2c_bus->target, I2C_SLAVE_WRITE_RECEIVED, &value);
+ }
+ }
+ if (readl(i2c_bus->reg_base + AST2600_I2CS_CMD_STS) & AST2600_I2CS_RX_BUFF_EN)
+ cmd = 0;
+ else
+ cmd = TARGET_TRIGGER_CMD | AST2600_I2CS_RX_BUFF_EN;
+
+ writel(AST2600_I2CC_SET_RX_BUF_LEN(i2c_bus->buf_size),
+ i2c_bus->reg_base + AST2600_I2CC_BUFF_CTRL);
+ break;
+ case AST2600_I2CS_WAIT_RX_DMA | AST2600_I2CS_RX_DONE:
+ cmd = TARGET_TRIGGER_CMD;
+ target_rx_len = AST2600_I2CC_GET_RX_BUF_LEN(readl(i2c_bus->reg_base +
+ AST2600_I2CC_BUFF_CTRL));
+ for (i = 0; i < target_rx_len; i++) {
+ value = readb(i2c_bus->buf_base + 0x10 + i);
+ i2c_slave_event(i2c_bus->target, I2C_SLAVE_WRITE_RECEIVED, &value);
+ }
+ cmd |= AST2600_I2CS_RX_BUFF_EN;
+ writel(AST2600_I2CC_SET_RX_BUF_LEN(i2c_bus->buf_size),
+ i2c_bus->reg_base + AST2600_I2CC_BUFF_CTRL);
+ break;
+ case AST2600_I2CS_SLAVE_PENDING | AST2600_I2CS_WAIT_RX_DMA |
+ AST2600_I2CS_RX_DONE | AST2600_I2CS_STOP:
+ cmd = TARGET_TRIGGER_CMD;
+ target_rx_len = AST2600_I2CC_GET_RX_BUF_LEN(readl(i2c_bus->reg_base +
+ AST2600_I2CC_BUFF_CTRL));
+ for (i = 0; i < target_rx_len; i++) {
+ value = readb(i2c_bus->buf_base + 0x10 + i);
+ i2c_slave_event(i2c_bus->target, I2C_SLAVE_WRITE_RECEIVED, &value);
+ }
+ i2c_slave_event(i2c_bus->target, I2C_SLAVE_STOP, &value);
+ cmd |= AST2600_I2CS_RX_BUFF_EN;
+ writel(AST2600_I2CC_SET_RX_BUF_LEN(i2c_bus->buf_size),
+ i2c_bus->reg_base + AST2600_I2CC_BUFF_CTRL);
+ break;
+ case AST2600_I2CS_SLAVE_PENDING | AST2600_I2CS_RX_DONE | AST2600_I2CS_STOP:
+ cmd = TARGET_TRIGGER_CMD;
+ target_rx_len = AST2600_I2CC_GET_RX_BUF_LEN(readl(i2c_bus->reg_base +
+ AST2600_I2CC_BUFF_CTRL));
+ for (i = 0; i < target_rx_len; i++) {
+ value = readb(i2c_bus->buf_base + 0x10 + i);
+ i2c_slave_event(i2c_bus->target, I2C_SLAVE_WRITE_RECEIVED, &value);
+ }
+ /* workaround for avoid next start with len != 0 */
+ writel(BIT(0), i2c_bus->reg_base + AST2600_I2CC_BUFF_CTRL);
+ i2c_slave_event(i2c_bus->target, I2C_SLAVE_STOP, &value);
+ break;
+ case AST2600_I2CS_RX_DONE | AST2600_I2CS_STOP:
+ cmd = TARGET_TRIGGER_CMD;
+ target_rx_len = AST2600_I2CC_GET_RX_BUF_LEN(readl(i2c_bus->reg_base +
+ AST2600_I2CC_BUFF_CTRL));
+ for (i = 0; i < target_rx_len; i++) {
+ value = readb(i2c_bus->buf_base + 0x10 + i);
+ i2c_slave_event(i2c_bus->target, I2C_SLAVE_WRITE_RECEIVED, &value);
+ }
+ /* workaround for avoid next start with len != 0 */
+ writel(BIT(0), i2c_bus->reg_base + AST2600_I2CC_BUFF_CTRL);
+ i2c_slave_event(i2c_bus->target, I2C_SLAVE_STOP, &value);
+ break;
+ case AST2600_I2CS_SLAVE_PENDING | AST2600_I2CS_RX_DONE |
+ AST2600_I2CS_WAIT_TX_DMA | AST2600_I2CS_STOP:
+ target_rx_len = AST2600_I2CC_GET_RX_BUF_LEN(readl(i2c_bus->reg_base +
+ AST2600_I2CC_BUFF_CTRL));
+ for (i = 0; i < target_rx_len; i++) {
+ value = readb(i2c_bus->buf_base + 0x10 + i);
+ i2c_slave_event(i2c_bus->target, I2C_SLAVE_WRITE_RECEIVED, &value);
+ }
+ i2c_slave_event(i2c_bus->target, I2C_SLAVE_STOP, &value);
+ i2c_slave_event(i2c_bus->target, I2C_SLAVE_READ_REQUESTED, &value);
+ writeb(value, i2c_bus->buf_base);
+ break;
+ case AST2600_I2CS_WAIT_TX_DMA | AST2600_I2CS_SLAVE_MATCH:
+ i2c_slave_event(i2c_bus->target, I2C_SLAVE_READ_REQUESTED, &value);
+ writeb(value, i2c_bus->buf_base);
+ writel(AST2600_I2CC_SET_TX_BUF_LEN(1),
+ i2c_bus->reg_base + AST2600_I2CC_BUFF_CTRL);
+ cmd = TARGET_TRIGGER_CMD | AST2600_I2CS_TX_BUFF_EN;
+ break;
+ case AST2600_I2CS_SLAVE_PENDING | AST2600_I2CS_STOP |
+ AST2600_I2CS_TX_NAK | AST2600_I2CS_SLAVE_MATCH | AST2600_I2CS_RX_DONE:
+ case AST2600_I2CS_SLAVE_PENDING | AST2600_I2CS_WAIT_RX_DMA | AST2600_I2CS_STOP |
+ AST2600_I2CS_TX_NAK | AST2600_I2CS_SLAVE_MATCH | AST2600_I2CS_RX_DONE:
+ i2c_slave_event(i2c_bus->target, I2C_SLAVE_STOP, &value);
+ i2c_slave_event(i2c_bus->target, I2C_SLAVE_WRITE_REQUESTED, &value);
+ target_rx_len = AST2600_I2CC_GET_RX_BUF_LEN(readl(i2c_bus->reg_base +
+ AST2600_I2CC_BUFF_CTRL));
+ for (i = 0; i < target_rx_len; i++) {
+ value = readb(i2c_bus->buf_base + 0x10 + i);
+ i2c_slave_event(i2c_bus->target, I2C_SLAVE_WRITE_RECEIVED, &value);
+ }
+ writel(AST2600_I2CC_SET_RX_BUF_LEN(i2c_bus->buf_size),
+ i2c_bus->reg_base + AST2600_I2CC_BUFF_CTRL);
+ cmd = TARGET_TRIGGER_CMD | AST2600_I2CS_RX_BUFF_EN;
+ break;
+ case AST2600_I2CS_SLAVE_MATCH | AST2600_I2CS_WAIT_TX_DMA | AST2600_I2CS_RX_DONE:
+ case AST2600_I2CS_WAIT_TX_DMA | AST2600_I2CS_RX_DONE:
+ case AST2600_I2CS_WAIT_TX_DMA:
+ if (sts & AST2600_I2CS_SLAVE_MATCH)
+ i2c_slave_event(i2c_bus->target, I2C_SLAVE_WRITE_REQUESTED, &value);
+
+ if (sts & AST2600_I2CS_RX_DONE) {
+ target_rx_len = AST2600_I2CC_GET_RX_BUF_LEN(readl(i2c_bus->reg_base +
+ AST2600_I2CC_BUFF_CTRL));
+ for (i = 0; i < target_rx_len; i++) {
+ value = readb(i2c_bus->buf_base + 0x10 + i);
+ i2c_slave_event(i2c_bus->target, I2C_SLAVE_WRITE_RECEIVED, &value);
+ }
+ i2c_slave_event(i2c_bus->target, I2C_SLAVE_READ_REQUESTED, &value);
+ } else {
+ i2c_slave_event(i2c_bus->target, I2C_SLAVE_READ_PROCESSED, &value);
+ }
+ writeb(value, i2c_bus->buf_base);
+ writel(AST2600_I2CC_SET_TX_BUF_LEN(1),
+ i2c_bus->reg_base + AST2600_I2CC_BUFF_CTRL);
+ cmd = TARGET_TRIGGER_CMD | AST2600_I2CS_TX_BUFF_EN;
+ break;
+ /* workaround : trigger the cmd twice to fix next state keep 1000000 */
+ case AST2600_I2CS_SLAVE_MATCH | AST2600_I2CS_RX_DONE:
+ i2c_slave_event(i2c_bus->target, I2C_SLAVE_WRITE_REQUESTED, &value);
+ cmd = TARGET_TRIGGER_CMD | AST2600_I2CS_RX_BUFF_EN;
+ writel(cmd, i2c_bus->reg_base + AST2600_I2CS_CMD_STS);
+ break;
+ case AST2600_I2CS_TX_NAK | AST2600_I2CS_STOP:
+ case AST2600_I2CS_STOP:
+ cmd = TARGET_TRIGGER_CMD;
+ i2c_slave_event(i2c_bus->target, I2C_SLAVE_STOP, &value);
+ break;
+ default:
+ dev_dbg(i2c_bus->dev, "unhandled target isr case %x, sts %x\n", sts,
+ readl(i2c_bus->reg_base + AST2600_I2CC_STS_AND_BUFF));
+ break;
+ }
+
+ if (cmd)
+ writel(cmd, i2c_bus->reg_base + AST2600_I2CS_CMD_STS);
+ writel(AST2600_I2CS_PKT_DONE, i2c_bus->reg_base + AST2600_I2CS_ISR);
+ readl(i2c_bus->reg_base + AST2600_I2CS_ISR);
+
+ if ((sts & AST2600_I2CS_STOP) && !(sts & AST2600_I2CS_SLAVE_PENDING))
+ WRITE_ONCE(i2c_bus->target_active, false);
+}
+
+static int ast2600_i2c_target_irq(struct ast2600_i2c_bus *i2c_bus)
+{
+ u32 ier = readl(i2c_bus->reg_base + AST2600_I2CS_IER);
+ u32 isr = readl(i2c_bus->reg_base + AST2600_I2CS_ISR);
+
+ if (!(isr & ier))
+ return 0;
+
+ /*
+ * Target interrupt coming after controller packet done
+ * So need handle controller first.
+ */
+ if (readl(i2c_bus->reg_base + AST2600_I2CM_ISR) & AST2600_I2CM_PKT_DONE)
+ return 0;
+
+ isr &= ~(AST2600_I2CS_ADDR_INDICATE_MASK);
+
+ if (AST2600_I2CS_ADDR1_NAK & isr)
+ isr &= ~AST2600_I2CS_ADDR1_NAK;
+
+ if (AST2600_I2CS_ADDR2_NAK & isr)
+ isr &= ~AST2600_I2CS_ADDR2_NAK;
+
+ if (AST2600_I2CS_ADDR3_NAK & isr)
+ isr &= ~AST2600_I2CS_ADDR3_NAK;
+
+ if (AST2600_I2CS_ADDR_MASK & isr)
+ isr &= ~AST2600_I2CS_ADDR_MASK;
+
+ if (AST2600_I2CS_PKT_DONE & isr)
+ ast2600_i2c_target_packet_buff_irq(i2c_bus, isr);
+
+ return 1;
+}
+#endif
+
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];
@@ -498,6 +740,20 @@ static void ast2600_i2c_controller_packet_irq(struct ast2600_i2c_bus *i2c_bus, u
}
break;
case AST2600_I2CM_RX_DONE:
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+ /*
+ * Workaround for controller/target packet mode enable rx done stuck issue
+ * When controller go for first read (RX_DONE), target mode will also effect
+ * Then controller will send nack, not operate anymore.
+ */
+ if (readl(i2c_bus->reg_base + AST2600_I2CS_CMD_STS) & AST2600_I2CS_PKT_MODE_EN) {
+ u32 target_cmd = readl(i2c_bus->reg_base + AST2600_I2CS_CMD_STS);
+
+ writel(0, i2c_bus->reg_base + AST2600_I2CS_CMD_STS);
+ writel(target_cmd, i2c_bus->reg_base + AST2600_I2CS_CMD_STS);
+ }
+ fallthrough;
+#endif
case AST2600_I2CM_RX_DONE | AST2600_I2CM_NORMAL_STOP:
/* do next rx */
xfer_len = AST2600_I2CC_GET_RX_BUF_LEN(readl(i2c_bus->reg_base +
@@ -584,6 +840,12 @@ static irqreturn_t ast2600_i2c_bus_irq(int irq, void *dev_id)
{
struct ast2600_i2c_bus *i2c_bus = dev_id;
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+ if (i2c_bus->target) {
+ if (ast2600_i2c_target_irq(i2c_bus))
+ return IRQ_HANDLED;
+ }
+#endif
return IRQ_RETVAL(ast2600_i2c_controller_irq(i2c_bus));
}
@@ -600,12 +862,31 @@ static int ast2600_i2c_controller_xfer(struct i2c_adapter *adap, struct i2c_msg
return ret;
}
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+ if (i2c_bus->target_active)
+ return -EBUSY;
+ /*
+ * Controller and target share the same buffer register. A target
+ * transaction can update buffer state asynchronously via IRQ, so block
+ * controller transfers while target is active to avoid buffer corruption.
+ */
+ writel(0, i2c_bus->reg_base + AST2600_I2CS_IER);
+ if (readl(i2c_bus->reg_base + AST2600_I2CS_ISR) || i2c_bus->target_active) {
+ writel(AST2600_I2CS_PKT_DONE, i2c_bus->reg_base + AST2600_I2CS_IER);
+ return -EBUSY;
+ }
+#endif
+
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 IS_ENABLED(CONFIG_I2C_SLAVE)
+ /* avoid race condition target is wait and controller wait 1st target operate */
+ writel(AST2600_I2CS_PKT_DONE, i2c_bus->reg_base + AST2600_I2CS_IER);
+#endif
if (ret)
goto controller_out;
timeout = wait_for_completion_timeout(&i2c_bus->cmd_complete, i2c_bus->adap.timeout);
@@ -624,6 +905,9 @@ static int ast2600_i2c_controller_xfer(struct i2c_adapter *adap, struct i2c_msg
* if the bus is still busy.
*/
if (i2c_bus->multi_master &&
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+ !i2c_bus->target_active &&
+#endif
(readl(i2c_bus->reg_base + AST2600_I2CC_STS_AND_BUFF) &
AST2600_I2CC_BUS_BUSY_STS))
ast2600_i2c_recover_bus(i2c_bus);
@@ -659,8 +943,54 @@ static int ast2600_i2c_init(struct ast2600_i2c_bus *i2c_bus)
/* Clear Interrupt */
writel(GENMASK(27, 0), i2c_bus->reg_base + AST2600_I2CM_ISR);
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+ writel(GENMASK(27, 0), i2c_bus->reg_base + AST2600_I2CS_ISR);
+#endif
+
+ return 0;
+}
+
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+static int ast2600_i2c_reg_target(struct i2c_client *client)
+{
+ struct ast2600_i2c_bus *i2c_bus = i2c_get_adapdata(client->adapter);
+ u32 cmd = TARGET_TRIGGER_CMD;
+
+ if (i2c_bus->target)
+ return -EINVAL;
+
+ dev_dbg(i2c_bus->dev, "target addr %x\n", client->addr);
+
+ writel(0, i2c_bus->reg_base + AST2600_I2CS_ADDR_CTRL);
+ writel(AST2600_I2CC_SLAVE_EN | readl(i2c_bus->reg_base + AST2600_I2CC_FUN_CTRL),
+ i2c_bus->reg_base + AST2600_I2CC_FUN_CTRL);
+
+ writel(cmd, i2c_bus->reg_base + AST2600_I2CS_CMD_STS);
+ i2c_bus->target = client;
+ /* Set target addr. */
+ writel(client->addr | AST2600_I2CS_ADDR1_ENABLE,
+ i2c_bus->reg_base + AST2600_I2CS_ADDR_CTRL);
+
+ return 0;
+}
+
+static int ast2600_i2c_unreg_target(struct i2c_client *client)
+{
+ struct ast2600_i2c_bus *i2c_bus = i2c_get_adapdata(client->adapter);
+ u32 val;
+
+ /* Turn off target mode. */
+ val = readl(i2c_bus->reg_base + AST2600_I2CC_FUN_CTRL);
+ writel(val & ~AST2600_I2CC_SLAVE_EN, i2c_bus->reg_base + AST2600_I2CC_FUN_CTRL);
+ val = readl(i2c_bus->reg_base + AST2600_I2CS_ADDR_CTRL);
+ writel(val & ~AST2600_I2CS_ADDR1_MASK, i2c_bus->reg_base + AST2600_I2CS_ADDR_CTRL);
+
+ synchronize_irq(i2c_bus->irq);
+ i2c_bus->target = NULL;
+
return 0;
}
+#endif
static u32 ast2600_i2c_functionality(struct i2c_adapter *adap)
{
@@ -670,6 +1000,10 @@ static u32 ast2600_i2c_functionality(struct i2c_adapter *adap)
static const struct i2c_algorithm i2c_ast2600_algorithm = {
.xfer = ast2600_i2c_controller_xfer,
.functionality = ast2600_i2c_functionality,
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+ .reg_target = ast2600_i2c_reg_target,
+ .unreg_target = ast2600_i2c_unreg_target,
+#endif
};
static int ast2600_i2c_probe(struct platform_device *pdev)
@@ -708,6 +1042,9 @@ static int ast2600_i2c_probe(struct platform_device *pdev)
regmap_write(i2c_bus->global_regs, AST2600_I2CG_CLK_DIV_CTRL, I2CCG_DIV_CTRL);
}
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+ WRITE_ONCE(i2c_bus->target_active, false);
+#endif
i2c_bus->dev = dev;
i2c_bus->multi_master = device_property_read_bool(dev, "multi-master");
@@ -765,6 +1102,9 @@ static int ast2600_i2c_probe(struct platform_device *pdev)
writel(AST2600_I2CM_PKT_DONE | AST2600_I2CM_BUS_RECOVER,
i2c_bus->reg_base + AST2600_I2CM_IER);
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+ writel(AST2600_I2CS_PKT_DONE, i2c_bus->reg_base + AST2600_I2CS_IER);
+#endif
ret = devm_i2c_add_adapter(dev, &i2c_bus->adap);
if (ret)
--
2.34.1
^ permalink raw reply related
* [PATCH v29 3/4] i2c: ast2600: Add controller driver for AST2600 new register set
From: Ryan Chen @ 2026-04-15 5:14 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: <20260415-upstream_i2c-v29-0-317c1a905ae1@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)
- 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.
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 v29:
- update commit message remove transfer mode selection.
- remove dma/byte transfer, use buffer mode only.
- remove sysfs file.
- remove define I2C_TARGET_MSG_BUF_SIZE and AST2600_I2C_DMA_SIZE.
- remove buf_index in struct ast2600_i2c_bus.
Changes in v28:
- Separate xfer_mode_store into distinct parse and availability-check
steps by introducing ast2600_i2c_xfer_mode_check()
- fix tx dma memcpy source point address.
- Use a temporary variable for devm_platform_get_and_ioremap_resource()
to avoid storing an ERR_PTR in i2c_bus->buf_base; drop the redundant
NULL assignment in the error path since i2c_bus is kzalloc()ed
- Add ABI documentation file
Documentation/ABI/testing/sysfs-driver-ast2600-i2c
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 | 808 +++++++++++++++++++++++++++++++++++++++
3 files changed, 814 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..787ef6bd6244
--- /dev/null
+++ b/drivers/i2c/busses/i2c-ast2600.c
@@ -0,0 +1,808 @@
+// 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/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/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 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
+
+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;
+ u32 apb_clk;
+ u32 timeout;
+ int irq;
+ int cmd_err;
+ int msgs_index;
+ int msgs_count;
+ int controller_xfer_cnt;
+ size_t buf_size;
+ bool multi_master;
+ void __iomem *buf_base;
+};
+
+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_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_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_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);
+
+ i2c_bus->controller_xfer_cnt = 0;
+
+ if (msg->flags & I2C_M_RD)
+ return ast2600_i2c_setup_buff_rx(AST2600_I2CM_START_CMD, i2c_bus);
+
+ return ast2600_i2c_setup_buff_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:
+ xfer_len = AST2600_I2CC_GET_TX_BUF_LEN(readl(i2c_bus->reg_base +
+ AST2600_I2CC_BUFF_CTRL));
+ 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 {
+ ast2600_i2c_setup_buff_tx(0, i2c_bus);
+ }
+ break;
+ case AST2600_I2CM_RX_DONE:
+ case AST2600_I2CM_RX_DONE | AST2600_I2CM_NORMAL_STOP:
+ /* do next rx */
+ 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);
+
+ 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 {
+ ast2600_i2c_setup_buff_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);
+
+ /* 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 int ast2600_i2c_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct ast2600_i2c_bus *i2c_bus;
+ void __iomem *buf_base;
+ 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");
+
+ buf_base = devm_platform_get_and_ioremap_resource(pdev, 1, &res);
+ if (IS_ERR(buf_base))
+ return dev_err_probe(dev, PTR_ERR(buf_base), "Missing buffer resource\n");
+ i2c_bus->buf_base = buf_base;
+ i2c_bus->buf_size = resource_size(res) / 2;
+
+ /*
+ * 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 initialize 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;
+
+ 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);
+
+ /* 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
* Re: [PATCH v4 4/9] coresight: etm4x: fix inconsistencies with sysfs configuration
From: Yeoreum Yun @ 2026-04-15 5:36 UTC (permalink / raw)
To: Jie Gan
Cc: coresight, linux-arm-kernel, linux-kernel, suzuki.poulose,
mike.leach, james.clark, alexander.shishkin, leo.yan
In-Reply-To: <d1b42ecb-3275-4399-8537-b6a4844002cf@oss.qualcomm.com>
[...]
> > @@ -616,23 +622,46 @@ static int etm4_enable_hw(struct etmv4_drvdata *drvdata)
> > static void etm4_enable_sysfs_smp_call(void *info)
> > {
> > struct etm4_enable_arg *arg = info;
> > + struct etmv4_drvdata *drvdata;
> > struct coresight_device *csdev;
> > if (WARN_ON(!arg))
> > return;
> > - csdev = arg->drvdata->csdev;
> > + drvdata = arg->drvdata;
> > + csdev = drvdata->csdev;
> > if (!coresight_take_mode(csdev, CS_MODE_SYSFS)) {
> > /* Someone is already using the tracer */
> > arg->rc = -EBUSY;
> > return;
> > }
> > - arg->rc = etm4_enable_hw(arg->drvdata);
> > + drvdata->active_config = arg->config;
> > - /* The tracer didn't start */
> > + if (arg->cfg_hash) {
> > + arg->rc = cscfg_csdev_enable_active_config(csdev,
> > + arg->cfg_hash,
> > + arg->preset);
> > + if (arg->rc)
> > + goto err;
> > + }
> > +
> > + drvdata->trcid = arg->trace_id;
> > +
> > + /* Tracer will never be paused in sysfs mode */
> > + drvdata->paused = false;
> > +
> > + arg->rc = etm4_enable_hw(drvdata);
> > if (arg->rc)
> > - coresight_set_mode(csdev, CS_MODE_DISABLED);
> > + goto err;
> > +
> > + drvdata->sticky_enable = true;
> > +
> > + return;
> > +err:
> > + /* The tracer didn't start */
> > + etm4_release_trace_id(drvdata);
>
> [NIT] better move this error handle to etm4_enable_sysfs.
>
> smp_call_function_single possible return before call
> etm4_enable_sysfs_smp_call if the cpu is offline. The error path here cannot
> handle this error, breaking previous logic(handle all errors in
> etm4_enable_sysfs by releasing trace id).
>
> There is no harm in not releasing the trace id here because ETM can re-use
> the allocated trace ID. But it's better to fix the inconsistent logic.
Agree. I've missed this!
Thanks ;)
[...]
--
Sincerely,
Yeoreum Yun
^ permalink raw reply
* [PATCH v2 net 2/2] net: enetc: fix NTMP DMA use-after-free issue
From: Wei Fang @ 2026-04-15 6:08 UTC (permalink / raw)
To: claudiu.manoil, vladimir.oltean, xiaoning.wang, andrew+netdev,
davem, edumazet, kuba, pabeni, chleroy
Cc: netdev, linux-kernel, imx, linuxppc-dev, linux-arm-kernel
In-Reply-To: <20260415060833.2303846-1-wei.fang@nxp.com>
The AI-generated review reported a potential DMA use-after-free issue
[1]. If netc_xmit_ntmp_cmd() times out and returns an error, the pending
command is not explicitly aborted, while ntmp_free_data_mem()
unconditionally frees the DMA buffer. If the buffer has already been
reallocated elsewhere, this may lead to silent memory corruption. Because
the hardware eventually processes the pending command and perform a DMA
write of the response to the physical address of the freed buffer.
To resolve this issue, this patch does the following modifications:
1. Convert cbdr->ring_lock from a spinlock to a mutex
The lock was originally a spinlock in case NTMP operations might be
invoked from atomic context. After downstream support for all NTMP
tables, no such usage has materialized. A mutex lock is now required
because the driver now needs to reclaim used BDs and release associated
DMA memory within the lock's context, while dma_free_coherent() might
sleep.
2. Introduce software command BD (struct netc_swcbd)
The hardware write-back overwrites the addr and len fields of the BD,
so the driver cannot rely on the hardware BD to free the associated DMA
memory. The driver now maintains a software shadow BD storing the DMA
buffer pointer, DMA address, and size. And netc_xmit_ntmp_cmd() only
reclaims older BDs when the number of used BDs reaches
NETC_CBDR_CLEAN_WORK (16). The software BD enables correct DMA memory
release. With this, struct ntmp_dma_buf and ntmp_free_data_mem() are no
longer needed and are removed.
3. Require callers to hold ring_lock across netc_xmit_ntmp_cmd()
netc_xmit_ntmp_cmd() releases the ring_lock before the caller finishes
consuming the response. At this point, if a concurrent thread submits
a new command, it may trigger ntmp_clean_cbdr() and free the DMA buffer
while it is still in use. Move ring_lock ownership to the caller to
ensure the response buffer cannot be reclaimed prematurely. So the
helpers ntmp_select_and_lock_cbdr() and ntmp_unlock_cbdr() are added.
These changes eliminate the DMA use-after-free condition and ensure safe
and consistent BD reclamation and DMA buffer lifecycle management.
Fixes: 4701073c3deb ("net: enetc: add initial netc-lib driver to support NTMP")
Link: https://lore.kernel.org/netdev/20260403011729.1795413-1-kuba@kernel.org/ # [1]
Signed-off-by: Wei Fang <wei.fang@nxp.com>
---
drivers/net/ethernet/freescale/enetc/ntmp.c | 214 ++++++++++--------
.../ethernet/freescale/enetc/ntmp_private.h | 8 +-
include/linux/fsl/ntmp.h | 9 +-
3 files changed, 134 insertions(+), 97 deletions(-)
diff --git a/drivers/net/ethernet/freescale/enetc/ntmp.c b/drivers/net/ethernet/freescale/enetc/ntmp.c
index b188eb2d40c0..70bbc5d2d5d4 100644
--- a/drivers/net/ethernet/freescale/enetc/ntmp.c
+++ b/drivers/net/ethernet/freescale/enetc/ntmp.c
@@ -7,6 +7,7 @@
#include <linux/dma-mapping.h>
#include <linux/fsl/netc_global.h>
#include <linux/iopoll.h>
+#include <linux/vmalloc.h>
#include "ntmp_private.h"
@@ -42,6 +43,12 @@ int ntmp_init_cbdr(struct netc_cbdr *cbdr, struct device *dev,
if (!cbdr->addr_base)
return -ENOMEM;
+ cbdr->swcbd = vcalloc(cbd_num, sizeof(struct netc_swcbd));
+ if (!cbdr->swcbd) {
+ dma_free_coherent(dev, size, cbdr->addr_base, cbdr->dma_base);
+ return -ENOMEM;
+ }
+
cbdr->dma_size = size;
cbdr->bd_num = cbd_num;
cbdr->regs = *regs;
@@ -52,7 +59,7 @@ int ntmp_init_cbdr(struct netc_cbdr *cbdr, struct device *dev,
cbdr->addr_base_align = PTR_ALIGN(cbdr->addr_base,
NTMP_BASE_ADDR_ALIGN);
- spin_lock_init(&cbdr->ring_lock);
+ mutex_init(&cbdr->ring_lock);
cbdr->next_to_use = netc_read(cbdr->regs.pir);
cbdr->next_to_clean = netc_read(cbdr->regs.cir) & NETC_CBDRCIR_INDEX;
@@ -71,10 +78,24 @@ int ntmp_init_cbdr(struct netc_cbdr *cbdr, struct device *dev,
}
EXPORT_SYMBOL_GPL(ntmp_init_cbdr);
+static void ntmp_free_data_mem(struct device *dev, struct netc_swcbd *swcbd)
+{
+ if (unlikely(!swcbd->buf))
+ return;
+
+ dma_free_coherent(dev, swcbd->size + NTMP_DATA_ADDR_ALIGN,
+ swcbd->buf, swcbd->dma);
+}
+
void ntmp_free_cbdr(struct netc_cbdr *cbdr)
{
/* Disable the Control BD Ring */
netc_write(cbdr->regs.mr, 0);
+
+ for (int i = 0; i < cbdr->bd_num; i++)
+ ntmp_free_data_mem(cbdr->dev, &cbdr->swcbd[i]);
+
+ vfree(cbdr->swcbd);
dma_free_coherent(cbdr->dev, cbdr->dma_size, cbdr->addr_base,
cbdr->dma_base);
memset(cbdr, 0, sizeof(*cbdr));
@@ -94,40 +115,59 @@ static union netc_cbd *ntmp_get_cbd(struct netc_cbdr *cbdr, int index)
static void ntmp_clean_cbdr(struct netc_cbdr *cbdr)
{
- union netc_cbd *cbd;
- int i;
+ int i = cbdr->next_to_clean;
- i = cbdr->next_to_clean;
while ((netc_read(cbdr->regs.cir) & NETC_CBDRCIR_INDEX) != i) {
- cbd = ntmp_get_cbd(cbdr, i);
+ union netc_cbd *cbd = ntmp_get_cbd(cbdr, i);
+ struct netc_swcbd *swcbd = &cbdr->swcbd[i];
+
+ ntmp_free_data_mem(cbdr->dev, swcbd);
+ memset(swcbd, 0, sizeof(*swcbd));
memset(cbd, 0, sizeof(*cbd));
i = (i + 1) % cbdr->bd_num;
}
+ dma_wmb();
cbdr->next_to_clean = i;
}
-static int netc_xmit_ntmp_cmd(struct ntmp_user *user, union netc_cbd *cbd)
+static void ntmp_select_and_lock_cbdr(struct ntmp_user *user,
+ struct netc_cbdr **cbdr)
+{
+ /* Currently only ENETC is supported, and it has only one command
+ * BD ring.
+ */
+ *cbdr = &user->ring[0];
+
+ mutex_lock(&(*cbdr)->ring_lock);
+}
+
+static void ntmp_unlock_cbdr(struct netc_cbdr *cbdr)
+{
+ mutex_unlock(&cbdr->ring_lock);
+}
+
+static int netc_xmit_ntmp_cmd(struct netc_cbdr *cbdr, union netc_cbd *cbd,
+ struct netc_swcbd *swcbd)
{
union netc_cbd *cur_cbd;
- struct netc_cbdr *cbdr;
- int i, err;
+ int i, err, used_bds;
u16 status;
u32 val;
- /* Currently only i.MX95 ENETC is supported, and it only has one
- * command BD ring
- */
- cbdr = &user->ring[0];
-
- spin_lock_bh(&cbdr->ring_lock);
-
- if (unlikely(!ntmp_get_free_cbd_num(cbdr)))
+ used_bds = cbdr->bd_num - ntmp_get_free_cbd_num(cbdr);
+ if (unlikely(used_bds >= NETC_CBDR_CLEAN_WORK)) {
ntmp_clean_cbdr(cbdr);
+ if (unlikely(!ntmp_get_free_cbd_num(cbdr))) {
+ ntmp_free_data_mem(cbdr->dev, swcbd);
+ return -EBUSY;
+ }
+ }
i = cbdr->next_to_use;
cur_cbd = ntmp_get_cbd(cbdr, i);
*cur_cbd = *cbd;
+ cbdr->swcbd[i] = *swcbd;
dma_wmb();
/* Update producer index of both software and hardware */
@@ -135,17 +175,16 @@ static int netc_xmit_ntmp_cmd(struct ntmp_user *user, union netc_cbd *cbd)
cbdr->next_to_use = i;
netc_write(cbdr->regs.pir, i);
- err = read_poll_timeout_atomic(netc_read, val,
- (val & NETC_CBDRCIR_INDEX) == i,
- NETC_CBDR_DELAY_US, NETC_CBDR_TIMEOUT,
- true, cbdr->regs.cir);
+ err = read_poll_timeout(netc_read, val,
+ (val & NETC_CBDRCIR_INDEX) == i,
+ NETC_CBDR_DELAY_US, NETC_CBDR_TIMEOUT,
+ true, cbdr->regs.cir);
if (unlikely(err))
- goto cbdr_unlock;
+ return err;
if (unlikely(val & NETC_CBDRCIR_SBE)) {
- dev_err(user->dev, "Command BD system bus error\n");
- err = -EIO;
- goto cbdr_unlock;
+ dev_err(cbdr->dev, "Command BD system bus error\n");
+ return -EIO;
}
dma_rmb();
@@ -157,40 +196,29 @@ static int netc_xmit_ntmp_cmd(struct ntmp_user *user, union netc_cbd *cbd)
/* Check the writeback error status */
status = le16_to_cpu(cbd->resp_hdr.error_rr) & NTMP_RESP_ERROR;
if (unlikely(status)) {
- err = -EIO;
- dev_err(user->dev, "Command BD error: 0x%04x\n", status);
+ dev_err(cbdr->dev, "Command BD error: 0x%04x\n", status);
+ return -EIO;
}
- ntmp_clean_cbdr(cbdr);
- dma_wmb();
-
-cbdr_unlock:
- spin_unlock_bh(&cbdr->ring_lock);
-
- return err;
+ return 0;
}
-static int ntmp_alloc_data_mem(struct ntmp_dma_buf *data, void **buf_align)
+static int ntmp_alloc_data_mem(struct device *dev, struct netc_swcbd *swcbd,
+ void **buf_align)
{
void *buf;
- buf = dma_alloc_coherent(data->dev, data->size + NTMP_DATA_ADDR_ALIGN,
- &data->dma, GFP_KERNEL);
+ buf = dma_alloc_coherent(dev, swcbd->size + NTMP_DATA_ADDR_ALIGN,
+ &swcbd->dma, GFP_KERNEL);
if (!buf)
return -ENOMEM;
- data->buf = buf;
+ swcbd->buf = buf;
*buf_align = PTR_ALIGN(buf, NTMP_DATA_ADDR_ALIGN);
return 0;
}
-static void ntmp_free_data_mem(struct ntmp_dma_buf *data)
-{
- dma_free_coherent(data->dev, data->size + NTMP_DATA_ADDR_ALIGN,
- data->buf, data->dma);
-}
-
static void ntmp_fill_request_hdr(union netc_cbd *cbd, dma_addr_t dma,
int len, int table_id, int cmd,
int access_method)
@@ -241,37 +269,39 @@ static int ntmp_delete_entry_by_id(struct ntmp_user *user, int tbl_id,
u8 tbl_ver, u32 entry_id, u32 req_len,
u32 resp_len)
{
- struct ntmp_dma_buf data = {
- .dev = user->dev,
+ struct netc_swcbd swcbd = {
.size = max(req_len, resp_len),
};
struct ntmp_req_by_eid *req;
+ struct netc_cbdr *cbdr;
union netc_cbd cbd;
int err;
- err = ntmp_alloc_data_mem(&data, (void **)&req);
+ err = ntmp_alloc_data_mem(user->dev, &swcbd, (void **)&req);
if (err)
return err;
ntmp_fill_crd_eid(req, tbl_ver, 0, 0, entry_id);
- ntmp_fill_request_hdr(&cbd, data.dma, NTMP_LEN(req_len, resp_len),
+ ntmp_fill_request_hdr(&cbd, swcbd.dma, NTMP_LEN(req_len, resp_len),
tbl_id, NTMP_CMD_DELETE, NTMP_AM_ENTRY_ID);
- err = netc_xmit_ntmp_cmd(user, &cbd);
+ ntmp_select_and_lock_cbdr(user, &cbdr);
+ err = netc_xmit_ntmp_cmd(cbdr, &cbd, &swcbd);
if (err)
dev_err(user->dev,
"Failed to delete entry 0x%x of %s, err: %pe",
entry_id, ntmp_table_name(tbl_id), ERR_PTR(err));
-
- ntmp_free_data_mem(&data);
+ ntmp_unlock_cbdr(cbdr);
return err;
}
-static int ntmp_query_entry_by_id(struct ntmp_user *user, int tbl_id,
- u32 len, struct ntmp_req_by_eid *req,
- dma_addr_t dma, bool compare_eid)
+static int ntmp_query_entry_by_id(struct netc_cbdr *cbdr, int tbl_id,
+ struct ntmp_req_by_eid *req,
+ struct netc_swcbd *swcbd,
+ bool compare_eid)
{
+ u32 len = NTMP_LEN(sizeof(*req), swcbd->size);
struct ntmp_cmn_resp_query *resp;
int cmd = NTMP_CMD_QUERY;
union netc_cbd cbd;
@@ -283,10 +313,11 @@ static int ntmp_query_entry_by_id(struct ntmp_user *user, int tbl_id,
cmd = NTMP_CMD_QU;
/* Request header */
- ntmp_fill_request_hdr(&cbd, dma, len, tbl_id, cmd, NTMP_AM_ENTRY_ID);
- err = netc_xmit_ntmp_cmd(user, &cbd);
+ ntmp_fill_request_hdr(&cbd, swcbd->dma, len, tbl_id, cmd,
+ NTMP_AM_ENTRY_ID);
+ err = netc_xmit_ntmp_cmd(cbdr, &cbd, swcbd);
if (err) {
- dev_err(user->dev,
+ dev_err(cbdr->dev,
"Failed to query entry 0x%x of %s, err: %pe\n",
entry_id, ntmp_table_name(tbl_id), ERR_PTR(err));
return err;
@@ -300,7 +331,7 @@ static int ntmp_query_entry_by_id(struct ntmp_user *user, int tbl_id,
resp = (struct ntmp_cmn_resp_query *)req;
if (unlikely(le32_to_cpu(resp->entry_id) != entry_id)) {
- dev_err(user->dev,
+ dev_err(cbdr->dev,
"%s: query EID 0x%x doesn't match response EID 0x%x\n",
ntmp_table_name(tbl_id), entry_id, le32_to_cpu(resp->entry_id));
return -EIO;
@@ -312,15 +343,15 @@ static int ntmp_query_entry_by_id(struct ntmp_user *user, int tbl_id,
int ntmp_maft_add_entry(struct ntmp_user *user, u32 entry_id,
struct maft_entry_data *maft)
{
- struct ntmp_dma_buf data = {
- .dev = user->dev,
+ struct netc_swcbd swcbd = {
.size = sizeof(struct maft_req_add),
};
struct maft_req_add *req;
+ struct netc_cbdr *cbdr;
union netc_cbd cbd;
int err;
- err = ntmp_alloc_data_mem(&data, (void **)&req);
+ err = ntmp_alloc_data_mem(user->dev, &swcbd, (void **)&req);
if (err)
return err;
@@ -329,14 +360,15 @@ int ntmp_maft_add_entry(struct ntmp_user *user, u32 entry_id,
req->keye = maft->keye;
req->cfge = maft->cfge;
- ntmp_fill_request_hdr(&cbd, data.dma, NTMP_LEN(data.size, 0),
+ ntmp_fill_request_hdr(&cbd, swcbd.dma, NTMP_LEN(swcbd.size, 0),
NTMP_MAFT_ID, NTMP_CMD_ADD, NTMP_AM_ENTRY_ID);
- err = netc_xmit_ntmp_cmd(user, &cbd);
+
+ ntmp_select_and_lock_cbdr(user, &cbdr);
+ err = netc_xmit_ntmp_cmd(cbdr, &cbd, &swcbd);
if (err)
dev_err(user->dev, "Failed to add MAFT entry 0x%x, err: %pe\n",
entry_id, ERR_PTR(err));
-
- ntmp_free_data_mem(&data);
+ ntmp_unlock_cbdr(cbdr);
return err;
}
@@ -345,31 +377,31 @@ EXPORT_SYMBOL_GPL(ntmp_maft_add_entry);
int ntmp_maft_query_entry(struct ntmp_user *user, u32 entry_id,
struct maft_entry_data *maft)
{
- struct ntmp_dma_buf data = {
- .dev = user->dev,
+ struct netc_swcbd swcbd = {
.size = sizeof(struct maft_resp_query),
};
struct maft_resp_query *resp;
struct ntmp_req_by_eid *req;
+ struct netc_cbdr *cbdr;
int err;
- err = ntmp_alloc_data_mem(&data, (void **)&req);
+ err = ntmp_alloc_data_mem(user->dev, &swcbd, (void **)&req);
if (err)
return err;
ntmp_fill_crd_eid(req, user->tbl.maft_ver, 0, 0, entry_id);
- err = ntmp_query_entry_by_id(user, NTMP_MAFT_ID,
- NTMP_LEN(sizeof(*req), data.size),
- req, data.dma, true);
+
+ ntmp_select_and_lock_cbdr(user, &cbdr);
+ err = ntmp_query_entry_by_id(cbdr, NTMP_MAFT_ID, req, &swcbd, true);
if (err)
- goto end;
+ goto unlock_cbdr;
resp = (struct maft_resp_query *)req;
maft->keye = resp->keye;
maft->cfge = resp->cfge;
-end:
- ntmp_free_data_mem(&data);
+unlock_cbdr:
+ ntmp_unlock_cbdr(cbdr);
return err;
}
@@ -385,8 +417,9 @@ EXPORT_SYMBOL_GPL(ntmp_maft_delete_entry);
int ntmp_rsst_update_entry(struct ntmp_user *user, const u32 *table,
int count)
{
- struct ntmp_dma_buf data = {.dev = user->dev};
struct rsst_req_update *req;
+ struct netc_swcbd swcbd;
+ struct netc_cbdr *cbdr;
union netc_cbd cbd;
int err, i;
@@ -394,8 +427,8 @@ int ntmp_rsst_update_entry(struct ntmp_user *user, const u32 *table,
/* HW only takes in a full 64 entry table */
return -EINVAL;
- data.size = struct_size(req, groups, count);
- err = ntmp_alloc_data_mem(&data, (void **)&req);
+ swcbd.size = struct_size(req, groups, count);
+ err = ntmp_alloc_data_mem(user->dev, &swcbd, (void **)&req);
if (err)
return err;
@@ -405,15 +438,15 @@ int ntmp_rsst_update_entry(struct ntmp_user *user, const u32 *table,
for (i = 0; i < count; i++)
req->groups[i] = (u8)(table[i]);
- ntmp_fill_request_hdr(&cbd, data.dma, NTMP_LEN(data.size, 0),
+ ntmp_fill_request_hdr(&cbd, swcbd.dma, NTMP_LEN(swcbd.size, 0),
NTMP_RSST_ID, NTMP_CMD_UPDATE, NTMP_AM_ENTRY_ID);
- err = netc_xmit_ntmp_cmd(user, &cbd);
+ ntmp_select_and_lock_cbdr(user, &cbdr);
+ err = netc_xmit_ntmp_cmd(cbdr, &cbd, &swcbd);
if (err)
dev_err(user->dev, "Failed to update RSST entry, err: %pe\n",
ERR_PTR(err));
-
- ntmp_free_data_mem(&data);
+ ntmp_unlock_cbdr(cbdr);
return err;
}
@@ -421,8 +454,9 @@ EXPORT_SYMBOL_GPL(ntmp_rsst_update_entry);
int ntmp_rsst_query_entry(struct ntmp_user *user, u32 *table, int count)
{
- struct ntmp_dma_buf data = {.dev = user->dev};
struct ntmp_req_by_eid *req;
+ struct netc_swcbd swcbd;
+ struct netc_cbdr *cbdr;
union netc_cbd cbd;
int err, i;
u8 *group;
@@ -431,21 +465,23 @@ int ntmp_rsst_query_entry(struct ntmp_user *user, u32 *table, int count)
/* HW only takes in a full 64 entry table */
return -EINVAL;
- data.size = NTMP_ENTRY_ID_SIZE + RSST_STSE_DATA_SIZE(count) +
- RSST_CFGE_DATA_SIZE(count);
- err = ntmp_alloc_data_mem(&data, (void **)&req);
+ swcbd.size = NTMP_ENTRY_ID_SIZE + RSST_STSE_DATA_SIZE(count) +
+ RSST_CFGE_DATA_SIZE(count);
+ err = ntmp_alloc_data_mem(user->dev, &swcbd, (void **)&req);
if (err)
return err;
/* Set the request data buffer */
ntmp_fill_crd_eid(req, user->tbl.rsst_ver, 0, 0, 0);
- ntmp_fill_request_hdr(&cbd, data.dma, NTMP_LEN(sizeof(*req), data.size),
+ ntmp_fill_request_hdr(&cbd, swcbd.dma, NTMP_LEN(sizeof(*req), swcbd.size),
NTMP_RSST_ID, NTMP_CMD_QUERY, NTMP_AM_ENTRY_ID);
- err = netc_xmit_ntmp_cmd(user, &cbd);
+
+ ntmp_select_and_lock_cbdr(user, &cbdr);
+ err = netc_xmit_ntmp_cmd(cbdr, &cbd, &swcbd);
if (err) {
dev_err(user->dev, "Failed to query RSST entry, err: %pe\n",
ERR_PTR(err));
- goto end;
+ goto unlock_cbdr;
}
group = (u8 *)req;
@@ -453,8 +489,8 @@ int ntmp_rsst_query_entry(struct ntmp_user *user, u32 *table, int count)
for (i = 0; i < count; i++)
table[i] = group[i];
-end:
- ntmp_free_data_mem(&data);
+unlock_cbdr:
+ ntmp_unlock_cbdr(cbdr);
return err;
}
diff --git a/drivers/net/ethernet/freescale/enetc/ntmp_private.h b/drivers/net/ethernet/freescale/enetc/ntmp_private.h
index 3459cc45b610..f8dff3ba2c28 100644
--- a/drivers/net/ethernet/freescale/enetc/ntmp_private.h
+++ b/drivers/net/ethernet/freescale/enetc/ntmp_private.h
@@ -14,6 +14,7 @@
#define NETC_CBDR_BD_NUM 256
#define NETC_CBDRCIR_INDEX GENMASK(9, 0)
#define NETC_CBDRCIR_SBE BIT(31)
+#define NETC_CBDR_CLEAN_WORK 16
union netc_cbd {
struct {
@@ -56,13 +57,6 @@ union netc_cbd {
} resp_hdr; /* NTMP Response Message Header Format */
};
-struct ntmp_dma_buf {
- struct device *dev;
- size_t size;
- void *buf;
- dma_addr_t dma;
-};
-
struct ntmp_cmn_req_data {
__le16 update_act;
u8 dbg_opt;
diff --git a/include/linux/fsl/ntmp.h b/include/linux/fsl/ntmp.h
index 916dc4fe7de3..83a449b4d6ec 100644
--- a/include/linux/fsl/ntmp.h
+++ b/include/linux/fsl/ntmp.h
@@ -31,6 +31,12 @@ struct netc_tbl_vers {
u8 rsst_ver;
};
+struct netc_swcbd {
+ void *buf;
+ dma_addr_t dma;
+ size_t size;
+};
+
struct netc_cbdr {
struct device *dev;
struct netc_cbdr_regs regs;
@@ -44,9 +50,10 @@ struct netc_cbdr {
void *addr_base_align;
dma_addr_t dma_base;
dma_addr_t dma_base_align;
+ struct netc_swcbd *swcbd;
/* Serialize the order of command BD ring */
- spinlock_t ring_lock;
+ struct mutex ring_lock;
};
struct ntmp_user {
--
2.34.1
^ permalink raw reply related
* [PATCH v2 net 1/2] net: enetc: correct the command BD ring consumer index
From: Wei Fang @ 2026-04-15 6:08 UTC (permalink / raw)
To: claudiu.manoil, vladimir.oltean, xiaoning.wang, andrew+netdev,
davem, edumazet, kuba, pabeni, chleroy
Cc: netdev, linux-kernel, imx, linuxppc-dev, linux-arm-kernel
In-Reply-To: <20260415060833.2303846-1-wei.fang@nxp.com>
The command BD ring cousumer index register has the consumer index as
the lower 10 bits, and the bit 31 is SBE, which indicates whether a
system bus error occurred during execution of the CBD command. So if a
system bus error occurs, reading the register will get the SBE bit set.
However, the current implementation directly uses the register value as
the consumer index without masking it. Therefore, if a system bus error
occurs, an incorrect consumer index will be obtained, causing errors in
the processing of the command BD ring. Thus, we need to mask out the
other bits to obtain the correct consumer index.
In addition, this patch adds a check for the SBE bit after the polling
loop and returns an error if the bit is set.
Fixes: 4701073c3deb ("net: enetc: add initial netc-lib driver to support NTMP")
Signed-off-by: Wei Fang <wei.fang@nxp.com>
---
drivers/net/ethernet/freescale/enetc/ntmp.c | 13 ++++++++++---
drivers/net/ethernet/freescale/enetc/ntmp_private.h | 2 ++
2 files changed, 12 insertions(+), 3 deletions(-)
diff --git a/drivers/net/ethernet/freescale/enetc/ntmp.c b/drivers/net/ethernet/freescale/enetc/ntmp.c
index 0c1d343253bf..b188eb2d40c0 100644
--- a/drivers/net/ethernet/freescale/enetc/ntmp.c
+++ b/drivers/net/ethernet/freescale/enetc/ntmp.c
@@ -55,7 +55,7 @@ int ntmp_init_cbdr(struct netc_cbdr *cbdr, struct device *dev,
spin_lock_init(&cbdr->ring_lock);
cbdr->next_to_use = netc_read(cbdr->regs.pir);
- cbdr->next_to_clean = netc_read(cbdr->regs.cir);
+ cbdr->next_to_clean = netc_read(cbdr->regs.cir) & NETC_CBDRCIR_INDEX;
/* Step 1: Configure the base address of the Control BD Ring */
netc_write(cbdr->regs.bar0, lower_32_bits(cbdr->dma_base_align));
@@ -98,7 +98,7 @@ static void ntmp_clean_cbdr(struct netc_cbdr *cbdr)
int i;
i = cbdr->next_to_clean;
- while (netc_read(cbdr->regs.cir) != i) {
+ while ((netc_read(cbdr->regs.cir) & NETC_CBDRCIR_INDEX) != i) {
cbd = ntmp_get_cbd(cbdr, i);
memset(cbd, 0, sizeof(*cbd));
i = (i + 1) % cbdr->bd_num;
@@ -135,12 +135,19 @@ static int netc_xmit_ntmp_cmd(struct ntmp_user *user, union netc_cbd *cbd)
cbdr->next_to_use = i;
netc_write(cbdr->regs.pir, i);
- err = read_poll_timeout_atomic(netc_read, val, val == i,
+ err = read_poll_timeout_atomic(netc_read, val,
+ (val & NETC_CBDRCIR_INDEX) == i,
NETC_CBDR_DELAY_US, NETC_CBDR_TIMEOUT,
true, cbdr->regs.cir);
if (unlikely(err))
goto cbdr_unlock;
+ if (unlikely(val & NETC_CBDRCIR_SBE)) {
+ dev_err(user->dev, "Command BD system bus error\n");
+ err = -EIO;
+ goto cbdr_unlock;
+ }
+
dma_rmb();
/* Get the writeback command BD, because the caller may need
* to check some other fields of the response header.
diff --git a/drivers/net/ethernet/freescale/enetc/ntmp_private.h b/drivers/net/ethernet/freescale/enetc/ntmp_private.h
index 34394e40fddd..3459cc45b610 100644
--- a/drivers/net/ethernet/freescale/enetc/ntmp_private.h
+++ b/drivers/net/ethernet/freescale/enetc/ntmp_private.h
@@ -12,6 +12,8 @@
#define NTMP_EID_REQ_LEN 8
#define NETC_CBDR_BD_NUM 256
+#define NETC_CBDRCIR_INDEX GENMASK(9, 0)
+#define NETC_CBDRCIR_SBE BIT(31)
union netc_cbd {
struct {
--
2.34.1
^ permalink raw reply related
* [PATCH v2 net 0/2] net: enetc: fix command BD ring issues
From: Wei Fang @ 2026-04-15 6:08 UTC (permalink / raw)
To: claudiu.manoil, vladimir.oltean, xiaoning.wang, andrew+netdev,
davem, edumazet, kuba, pabeni, chleroy
Cc: netdev, linux-kernel, imx, linuxppc-dev, linux-arm-kernel
Currently, the implementation of command BD ring has two issues, one is
that the driver may obtain wrong consumer index of the ring, because the
driver does not mask out the SBE bit of the CIR value, so a wrong index
will be obtained when a SBE error ouccrs. The other one is that the DMA
buffer may be used after free. If netc_xmit_ntmp_cmd() times out and
returns an error, the pending command is not explicitly aborted, while
ntmp_free_data_mem() unconditionally frees the DMA buffer. If the buffer
has already been reallocated elsewhere, this may lead to silent memory
corruption. Because the hardware eventually processes the pending command
and perform a DMA write of the response to the physical address of the
freed buffer. So this patch set is to fix these two issues.
---
v2:
1. Check the SBE bit in netc_xmit_ntmp_cmd().
2. Fix DMA buffer leak issue when netc_xmit_ntmp_cmd returns -EBUSY.
3. Check swcbd->buf in ntmp_free_data_mem().
4. Move ring_lock ownership to the caller to ensure the response buffer
cannot be reclaimed prematurely. So add the helpers ntmp_unlock_cbdr()
and ntmp_select_and_lock_cbdr().
---
Wei Fang (2):
net: enetc: correct the command BD ring consumer index
net: enetc: fix NTMP DMA use-after-free issue
drivers/net/ethernet/freescale/enetc/ntmp.c | 217 +++++++++++-------
.../ethernet/freescale/enetc/ntmp_private.h | 10 +-
include/linux/fsl/ntmp.h | 9 +-
3 files changed, 141 insertions(+), 95 deletions(-)
--
2.34.1
^ permalink raw reply
* Re: [PATCH net-next 5/6] net: stmmac: move PHY handling out of __stmmac_open()/release()
From: Alexander Stein @ 2026-04-15 6:08 UTC (permalink / raw)
To: Andrew Lunn, Heiner Kallweit, Russell King (Oracle)
Cc: Alexandre Torgue, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, linux-arm-kernel, linux-stm32, Maxime Coquelin,
netdev, Paolo Abeni
In-Reply-To: <E1v11A3-0000000774G-3PKY@rmk-PC.armlinux.org.uk>
Hi,
Am Dienstag, 23. September 2025, 13:26:19 CEST schrieb Russell King (Oracle):
> Move the PHY attachment/detachment from the network driver out of
> __stmmac_open() and __stmmac_release() into stmmac_open() and
> stmmac_release() where these actions will only happen when the
> interface is administratively brought up or down. It does not make
> sense to detach and re-attach the PHY during a change of MTU.
Sorry for coming up now. But I recently noticed this commit breaks changing
the MTU on i.MX8MP. Once I simply change the MTU I run into some DMA error:
$ ip link set dev end1 mtu 1400
imx-dwmac 30bf0000.ethernet end1: Register MEM_TYPE_PAGE_POOL RxQ-0
imx-dwmac 30bf0000.ethernet end1: Register MEM_TYPE_PAGE_POOL RxQ-1
imx-dwmac 30bf0000.ethernet end1: Register MEM_TYPE_PAGE_POOL RxQ-2
imx-dwmac 30bf0000.ethernet end1: Register MEM_TYPE_PAGE_POOL RxQ-3
imx-dwmac 30bf0000.ethernet end1: Register MEM_TYPE_PAGE_POOL RxQ-4
imx-dwmac 30bf0000.ethernet end1: Link is Down
imx-dwmac 30bf0000.ethernet end1: Failed to reset the dma
imx-dwmac 30bf0000.ethernet end1: stmmac_hw_setup: DMA engine initialization failed
imx-dwmac 30bf0000.ethernet end1: __stmmac_open: Hw setup failed
imx-dwmac 30bf0000.ethernet end1: failed reopening the interface after MTU change
Using the command above bisecting was straight forward.
For some reason detach and re-attaching the PHY seems necessary on this platform.
There already too much changes for simply reverting this commit.
Best regards,
Alexander
>
> Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
> ---
> .../net/ethernet/stmicro/stmmac/stmmac_main.c | 29 +++++++++++--------
> 1 file changed, 17 insertions(+), 12 deletions(-)
>
> diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
> index 4acd180d2da8..4844d563e291 100644
> --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
> +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
> @@ -3937,10 +3937,6 @@ static int __stmmac_open(struct net_device *dev,
> u32 chan;
> int ret;
>
> - ret = stmmac_init_phy(dev);
> - if (ret)
> - return ret;
> -
> for (int i = 0; i < MTL_MAX_TX_QUEUES; i++)
> if (priv->dma_conf.tx_queue[i].tbs & STMMAC_TBS_EN)
> dma_conf->tx_queue[i].tbs = priv->dma_conf.tx_queue[i].tbs;
> @@ -3990,7 +3986,6 @@ static int __stmmac_open(struct net_device *dev,
>
> stmmac_release_ptp(priv);
> init_error:
> - phylink_disconnect_phy(priv->phylink);
> return ret;
> }
>
> @@ -4010,18 +4005,28 @@ static int stmmac_open(struct net_device *dev)
>
> ret = pm_runtime_resume_and_get(priv->device);
> if (ret < 0)
> - goto err;
> + goto err_dma_resources;
> +
> + ret = stmmac_init_phy(dev);
> + if (ret)
> + goto err_runtime_pm;
>
> ret = __stmmac_open(dev, dma_conf);
> - if (ret) {
> - pm_runtime_put(priv->device);
> -err:
> - free_dma_desc_resources(priv, dma_conf);
> - }
> + if (ret)
> + goto err_disconnect_phy;
>
> kfree(dma_conf);
>
> return ret;
> +
> +err_disconnect_phy:
> + phylink_disconnect_phy(priv->phylink);
> +err_runtime_pm:
> + pm_runtime_put(priv->device);
> +err_dma_resources:
> + free_dma_desc_resources(priv, dma_conf);
> + kfree(dma_conf);
> + return ret;
> }
>
> static void __stmmac_release(struct net_device *dev)
> @@ -4038,7 +4043,6 @@ static void __stmmac_release(struct net_device *dev)
>
> /* Stop and disconnect the PHY */
> phylink_stop(priv->phylink);
> - phylink_disconnect_phy(priv->phylink);
>
> stmmac_disable_all_queues(priv);
>
> @@ -4078,6 +4082,7 @@ static int stmmac_release(struct net_device *dev)
>
> __stmmac_release(dev);
>
> + phylink_disconnect_phy(priv->phylink);
> pm_runtime_put(priv->device);
>
> return 0;
>
--
TQ-Systems GmbH | Mühlstraße 2, Gut Delling | 82229 Seefeld, Germany
Amtsgericht München, HRB 105018
Geschäftsführer: Detlef Schneider, Rüdiger Stahl, Stefan Schneider
http://www.tq-group.com/
^ permalink raw reply
* Re: [PATCH 1/8] arm64/hwcap: Generate the KERNEL_HWCAP_ definitions for the hwcaps
From: Alexander Stein @ 2026-04-15 6:24 UTC (permalink / raw)
To: Catalin Marinas, Will Deacon, Jonathan Corbet, Shuah Khan,
linux-arm-kernel
Cc: linux-arm-kernel, linux-kernel, linux-doc, linux-kselftest,
Mark Brown, Mark Brown
In-Reply-To: <20260302-arm64-dpisa-2025-v1-1-0855e7f41689@kernel.org>
Am Montag, 2. März 2026, 23:53:16 CEST schrieb Mark Brown:
> Currently for each hwcap we define both the HWCAPn_NAME definition which is
> exposed to userspace and a kernel internal KERNEL_HWCAP_NAME definition
> which we use internally. This is tedious and repetitive, instead use a
> script to generate the KERNEL_HWCAP_ definitions from the UAPI definitions.
>
> No functional changes intended.
Somehow this change causes to delete and generate kernel-hwcap.h on each
make call. This results in compiling essentially everything each time.
$ make Image
make[1]: Entering directory '/linux/build_arm64'
REMOVE arch/arm64/include/generated/asm/kernel-hwcap.h
GEN arch/arm64/include/generated/asm/kernel-hwcap.h
CC arch/arm64/kernel/asm-offsets.s
CC kernel/sched/rq-offsets.s
^Cmake[3]: *** Deleting file 'kernel/sched/rq-offsets.s'
make[3]: *** [../scripts/Makefile.build:184: kernel/sched/rq-offsets.s] Interrupt
make[2]: *** [/linux/Makefile:1371: prepare0] Interrupt
make[1]: *** [/linux/Makefile:248: __sub-make] Interrupt
make: *** [Makefile:248: __sub-make] Interrupt
make Image
make[1]: Entering directory '/linux/build_arm64'
REMOVE arch/arm64/include/generated/asm/kernel-hwcap.h
GEN arch/arm64/include/generated/asm/kernel-hwcap.h
CC arch/arm64/kernel/asm-offsets.s
CC kernel/sched/rq-offsets.s
CC arch/arm64/kernel/vdso/vgettimeofday.o
LD arch/arm64/kernel/vdso/vdso.so.dbg
VDSOSYM include/generated/vdso-offsets.h
OBJCOPY arch/arm64/kernel/vdso/vdso.so
CC init/version.o
^Cmake[4]: *** [../scripts/Makefile.build:289: init/version.o] Interrupt
make[3]: *** [../scripts/Makefile.build:548: init] Interrupt
make[2]: *** [/linux/Makefile:2140: .] Interrupt
make[1]: *** [/linux/Makefile:248: __sub-make] Interrupt
make: *** [Makefile:248: __sub-make] Interrupt
Best regards,
Alexander
>
> Signed-off-by: Mark Brown <broonie@kernel.org>
> ---
> arch/arm64/include/asm/hwcap.h | 120 +---------------------------------
> arch/arm64/tools/Makefile | 8 ++-
> arch/arm64/tools/gen-kernel-hwcaps.sh | 23 +++++++
> 3 files changed, 32 insertions(+), 119 deletions(-)
>
> diff --git a/arch/arm64/include/asm/hwcap.h b/arch/arm64/include/asm/hwcap.h
> index 72ea4bda79f3..abe8218b2325 100644
> --- a/arch/arm64/include/asm/hwcap.h
> +++ b/arch/arm64/include/asm/hwcap.h
> @@ -60,126 +60,10 @@
> * of KERNEL_HWCAP_{feature}.
> */
> #define __khwcap_feature(x) const_ilog2(HWCAP_ ## x)
> -#define KERNEL_HWCAP_FP __khwcap_feature(FP)
> -#define KERNEL_HWCAP_ASIMD __khwcap_feature(ASIMD)
> -#define KERNEL_HWCAP_EVTSTRM __khwcap_feature(EVTSTRM)
> -#define KERNEL_HWCAP_AES __khwcap_feature(AES)
> -#define KERNEL_HWCAP_PMULL __khwcap_feature(PMULL)
> -#define KERNEL_HWCAP_SHA1 __khwcap_feature(SHA1)
> -#define KERNEL_HWCAP_SHA2 __khwcap_feature(SHA2)
> -#define KERNEL_HWCAP_CRC32 __khwcap_feature(CRC32)
> -#define KERNEL_HWCAP_ATOMICS __khwcap_feature(ATOMICS)
> -#define KERNEL_HWCAP_FPHP __khwcap_feature(FPHP)
> -#define KERNEL_HWCAP_ASIMDHP __khwcap_feature(ASIMDHP)
> -#define KERNEL_HWCAP_CPUID __khwcap_feature(CPUID)
> -#define KERNEL_HWCAP_ASIMDRDM __khwcap_feature(ASIMDRDM)
> -#define KERNEL_HWCAP_JSCVT __khwcap_feature(JSCVT)
> -#define KERNEL_HWCAP_FCMA __khwcap_feature(FCMA)
> -#define KERNEL_HWCAP_LRCPC __khwcap_feature(LRCPC)
> -#define KERNEL_HWCAP_DCPOP __khwcap_feature(DCPOP)
> -#define KERNEL_HWCAP_SHA3 __khwcap_feature(SHA3)
> -#define KERNEL_HWCAP_SM3 __khwcap_feature(SM3)
> -#define KERNEL_HWCAP_SM4 __khwcap_feature(SM4)
> -#define KERNEL_HWCAP_ASIMDDP __khwcap_feature(ASIMDDP)
> -#define KERNEL_HWCAP_SHA512 __khwcap_feature(SHA512)
> -#define KERNEL_HWCAP_SVE __khwcap_feature(SVE)
> -#define KERNEL_HWCAP_ASIMDFHM __khwcap_feature(ASIMDFHM)
> -#define KERNEL_HWCAP_DIT __khwcap_feature(DIT)
> -#define KERNEL_HWCAP_USCAT __khwcap_feature(USCAT)
> -#define KERNEL_HWCAP_ILRCPC __khwcap_feature(ILRCPC)
> -#define KERNEL_HWCAP_FLAGM __khwcap_feature(FLAGM)
> -#define KERNEL_HWCAP_SSBS __khwcap_feature(SSBS)
> -#define KERNEL_HWCAP_SB __khwcap_feature(SB)
> -#define KERNEL_HWCAP_PACA __khwcap_feature(PACA)
> -#define KERNEL_HWCAP_PACG __khwcap_feature(PACG)
> -#define KERNEL_HWCAP_GCS __khwcap_feature(GCS)
> -#define KERNEL_HWCAP_CMPBR __khwcap_feature(CMPBR)
> -#define KERNEL_HWCAP_FPRCVT __khwcap_feature(FPRCVT)
> -#define KERNEL_HWCAP_F8MM8 __khwcap_feature(F8MM8)
> -#define KERNEL_HWCAP_F8MM4 __khwcap_feature(F8MM4)
> -#define KERNEL_HWCAP_SVE_F16MM __khwcap_feature(SVE_F16MM)
> -#define KERNEL_HWCAP_SVE_ELTPERM __khwcap_feature(SVE_ELTPERM)
> -#define KERNEL_HWCAP_SVE_AES2 __khwcap_feature(SVE_AES2)
> -#define KERNEL_HWCAP_SVE_BFSCALE __khwcap_feature(SVE_BFSCALE)
> -#define KERNEL_HWCAP_SVE2P2 __khwcap_feature(SVE2P2)
> -#define KERNEL_HWCAP_SME2P2 __khwcap_feature(SME2P2)
> -#define KERNEL_HWCAP_SME_SBITPERM __khwcap_feature(SME_SBITPERM)
> -#define KERNEL_HWCAP_SME_AES __khwcap_feature(SME_AES)
> -#define KERNEL_HWCAP_SME_SFEXPA __khwcap_feature(SME_SFEXPA)
> -#define KERNEL_HWCAP_SME_STMOP __khwcap_feature(SME_STMOP)
> -#define KERNEL_HWCAP_SME_SMOP4 __khwcap_feature(SME_SMOP4)
> -
> #define __khwcap2_feature(x) (const_ilog2(HWCAP2_ ## x) + 64)
> -#define KERNEL_HWCAP_DCPODP __khwcap2_feature(DCPODP)
> -#define KERNEL_HWCAP_SVE2 __khwcap2_feature(SVE2)
> -#define KERNEL_HWCAP_SVEAES __khwcap2_feature(SVEAES)
> -#define KERNEL_HWCAP_SVEPMULL __khwcap2_feature(SVEPMULL)
> -#define KERNEL_HWCAP_SVEBITPERM __khwcap2_feature(SVEBITPERM)
> -#define KERNEL_HWCAP_SVESHA3 __khwcap2_feature(SVESHA3)
> -#define KERNEL_HWCAP_SVESM4 __khwcap2_feature(SVESM4)
> -#define KERNEL_HWCAP_FLAGM2 __khwcap2_feature(FLAGM2)
> -#define KERNEL_HWCAP_FRINT __khwcap2_feature(FRINT)
> -#define KERNEL_HWCAP_SVEI8MM __khwcap2_feature(SVEI8MM)
> -#define KERNEL_HWCAP_SVEF32MM __khwcap2_feature(SVEF32MM)
> -#define KERNEL_HWCAP_SVEF64MM __khwcap2_feature(SVEF64MM)
> -#define KERNEL_HWCAP_SVEBF16 __khwcap2_feature(SVEBF16)
> -#define KERNEL_HWCAP_I8MM __khwcap2_feature(I8MM)
> -#define KERNEL_HWCAP_BF16 __khwcap2_feature(BF16)
> -#define KERNEL_HWCAP_DGH __khwcap2_feature(DGH)
> -#define KERNEL_HWCAP_RNG __khwcap2_feature(RNG)
> -#define KERNEL_HWCAP_BTI __khwcap2_feature(BTI)
> -#define KERNEL_HWCAP_MTE __khwcap2_feature(MTE)
> -#define KERNEL_HWCAP_ECV __khwcap2_feature(ECV)
> -#define KERNEL_HWCAP_AFP __khwcap2_feature(AFP)
> -#define KERNEL_HWCAP_RPRES __khwcap2_feature(RPRES)
> -#define KERNEL_HWCAP_MTE3 __khwcap2_feature(MTE3)
> -#define KERNEL_HWCAP_SME __khwcap2_feature(SME)
> -#define KERNEL_HWCAP_SME_I16I64 __khwcap2_feature(SME_I16I64)
> -#define KERNEL_HWCAP_SME_F64F64 __khwcap2_feature(SME_F64F64)
> -#define KERNEL_HWCAP_SME_I8I32 __khwcap2_feature(SME_I8I32)
> -#define KERNEL_HWCAP_SME_F16F32 __khwcap2_feature(SME_F16F32)
> -#define KERNEL_HWCAP_SME_B16F32 __khwcap2_feature(SME_B16F32)
> -#define KERNEL_HWCAP_SME_F32F32 __khwcap2_feature(SME_F32F32)
> -#define KERNEL_HWCAP_SME_FA64 __khwcap2_feature(SME_FA64)
> -#define KERNEL_HWCAP_WFXT __khwcap2_feature(WFXT)
> -#define KERNEL_HWCAP_EBF16 __khwcap2_feature(EBF16)
> -#define KERNEL_HWCAP_SVE_EBF16 __khwcap2_feature(SVE_EBF16)
> -#define KERNEL_HWCAP_CSSC __khwcap2_feature(CSSC)
> -#define KERNEL_HWCAP_RPRFM __khwcap2_feature(RPRFM)
> -#define KERNEL_HWCAP_SVE2P1 __khwcap2_feature(SVE2P1)
> -#define KERNEL_HWCAP_SME2 __khwcap2_feature(SME2)
> -#define KERNEL_HWCAP_SME2P1 __khwcap2_feature(SME2P1)
> -#define KERNEL_HWCAP_SME_I16I32 __khwcap2_feature(SME_I16I32)
> -#define KERNEL_HWCAP_SME_BI32I32 __khwcap2_feature(SME_BI32I32)
> -#define KERNEL_HWCAP_SME_B16B16 __khwcap2_feature(SME_B16B16)
> -#define KERNEL_HWCAP_SME_F16F16 __khwcap2_feature(SME_F16F16)
> -#define KERNEL_HWCAP_MOPS __khwcap2_feature(MOPS)
> -#define KERNEL_HWCAP_HBC __khwcap2_feature(HBC)
> -#define KERNEL_HWCAP_SVE_B16B16 __khwcap2_feature(SVE_B16B16)
> -#define KERNEL_HWCAP_LRCPC3 __khwcap2_feature(LRCPC3)
> -#define KERNEL_HWCAP_LSE128 __khwcap2_feature(LSE128)
> -#define KERNEL_HWCAP_FPMR __khwcap2_feature(FPMR)
> -#define KERNEL_HWCAP_LUT __khwcap2_feature(LUT)
> -#define KERNEL_HWCAP_FAMINMAX __khwcap2_feature(FAMINMAX)
> -#define KERNEL_HWCAP_F8CVT __khwcap2_feature(F8CVT)
> -#define KERNEL_HWCAP_F8FMA __khwcap2_feature(F8FMA)
> -#define KERNEL_HWCAP_F8DP4 __khwcap2_feature(F8DP4)
> -#define KERNEL_HWCAP_F8DP2 __khwcap2_feature(F8DP2)
> -#define KERNEL_HWCAP_F8E4M3 __khwcap2_feature(F8E4M3)
> -#define KERNEL_HWCAP_F8E5M2 __khwcap2_feature(F8E5M2)
> -#define KERNEL_HWCAP_SME_LUTV2 __khwcap2_feature(SME_LUTV2)
> -#define KERNEL_HWCAP_SME_F8F16 __khwcap2_feature(SME_F8F16)
> -#define KERNEL_HWCAP_SME_F8F32 __khwcap2_feature(SME_F8F32)
> -#define KERNEL_HWCAP_SME_SF8FMA __khwcap2_feature(SME_SF8FMA)
> -#define KERNEL_HWCAP_SME_SF8DP4 __khwcap2_feature(SME_SF8DP4)
> -#define KERNEL_HWCAP_SME_SF8DP2 __khwcap2_feature(SME_SF8DP2)
> -#define KERNEL_HWCAP_POE __khwcap2_feature(POE)
> -
> #define __khwcap3_feature(x) (const_ilog2(HWCAP3_ ## x) + 128)
> -#define KERNEL_HWCAP_MTE_FAR __khwcap3_feature(MTE_FAR)
> -#define KERNEL_HWCAP_MTE_STORE_ONLY __khwcap3_feature(MTE_STORE_ONLY)
> -#define KERNEL_HWCAP_LSFE __khwcap3_feature(LSFE)
> -#define KERNEL_HWCAP_LS64 __khwcap3_feature(LS64)
> +
> +#include "asm/kernel-hwcap.h"
>
> /*
> * This yields a mask that user programs can use to figure out what
> diff --git a/arch/arm64/tools/Makefile b/arch/arm64/tools/Makefile
> index c2b34e761006..a94b3d9caad6 100644
> --- a/arch/arm64/tools/Makefile
> +++ b/arch/arm64/tools/Makefile
> @@ -3,7 +3,7 @@
> gen := arch/$(ARCH)/include/generated
> kapi := $(gen)/asm
>
> -kapisyshdr-y := cpucap-defs.h sysreg-defs.h
> +kapisyshdr-y := cpucap-defs.h kernel-hwcap.h sysreg-defs.h
>
> kapi-hdrs-y := $(addprefix $(kapi)/, $(kapisyshdr-y))
>
> @@ -18,11 +18,17 @@ kapi: $(kapi-hdrs-y)
> quiet_cmd_gen_cpucaps = GEN $@
> cmd_gen_cpucaps = mkdir -p $(dir $@); $(AWK) -f $(real-prereqs) > $@
>
> +quiet_cmd_gen_kernel_hwcap = GEN $@
> + cmd_gen_kernel_hwcap = mkdir -p $(dir $@); /bin/sh -e $(real-prereqs) > $@
> +
> quiet_cmd_gen_sysreg = GEN $@
> cmd_gen_sysreg = mkdir -p $(dir $@); $(AWK) -f $(real-prereqs) > $@
>
> $(kapi)/cpucap-defs.h: $(src)/gen-cpucaps.awk $(src)/cpucaps FORCE
> $(call if_changed,gen_cpucaps)
>
> +$(kapi)/kernel-hwcap.h: $(src)/gen-kernel-hwcaps.sh $(srctree)/arch/arm64/include/uapi/asm/hwcap.h FORCE
> + $(call if_changed,gen_kernel_hwcap)
> +
> $(kapi)/sysreg-defs.h: $(src)/gen-sysreg.awk $(src)/sysreg FORCE
> $(call if_changed,gen_sysreg)
> diff --git a/arch/arm64/tools/gen-kernel-hwcaps.sh b/arch/arm64/tools/gen-kernel-hwcaps.sh
> new file mode 100644
> index 000000000000..e7cdcf428d91
> --- /dev/null
> +++ b/arch/arm64/tools/gen-kernel-hwcaps.sh
> @@ -0,0 +1,23 @@
> +#!/bin/sh -e
> +# SPDX-License-Identifier: GPL-2.0
> +#
> +# gen-kernel-hwcap.sh - Generate kernel internal hwcap.h definitions
> +#
> +# Copyright 2026 Arm, Ltd.
> +
> +if [ "$1" = "" ]; then
> + echo "$0: no filename specified"
> + exit 1
> +fi
> +
> +echo "#ifndef __ASM_KERNEL_HWCAPS_H"
> +echo "#define __ASM_KERNEL_HWCAPS_H"
> +echo ""
> +echo "/* Generated file - do not edit */"
> +echo ""
> +
> +grep -E '^#define HWCAP[0-9]*_[A-Z0-9_]+' $1 | \
> + sed 's/.*HWCAP\([0-9]*\)_\([A-Z0-9_]\+\).*/#define KERNEL_HWCAP_\2\t__khwcap\1_feature(\2)/'
> +
> +echo ""
> +echo "#endif /* __ASM_KERNEL_HWCAPS_H */"
>
>
--
TQ-Systems GmbH | Mühlstraße 2, Gut Delling | 82229 Seefeld, Germany
Amtsgericht München, HRB 105018
Geschäftsführer: Detlef Schneider, Rüdiger Stahl, Stefan Schneider
http://www.tq-group.com/
^ permalink raw reply
* Re: [patch 32/38] powerpc/spufs: Use mftb() directly
From: Christophe Leroy (CS GROUP) @ 2026-04-15 6:38 UTC (permalink / raw)
To: Thomas Gleixner, LKML
Cc: Michael Ellerman, linuxppc-dev, Arnd Bergmann, x86, Lu Baolu,
iommu, Michael Grzeschik, netdev, linux-wireless, Herbert Xu,
linux-crypto, Vlastimil Babka, linux-mm, David Woodhouse,
Bernie Thompson, linux-fbdev, Theodore Tso, linux-ext4,
Andrew Morton, Uladzislau Rezki, Marco Elver, Dmitry Vyukov,
kasan-dev, Andrey Ryabinin, Thomas Sailer, linux-hams,
Jason A. Donenfeld, Richard Henderson, linux-alpha, Russell King,
linux-arm-kernel, Catalin Marinas, Huacai Chen, loongarch,
Geert Uytterhoeven, linux-m68k, Dinh Nguyen, Jonas Bonn,
linux-openrisc, Helge Deller, linux-parisc, Paul Walmsley,
linux-riscv, Heiko Carstens, linux-s390, David S. Miller,
sparclinux
In-Reply-To: <20260410120319.723429844@kernel.org>
Le 10/04/2026 à 14:21, Thomas Gleixner a écrit :
> There is no reason to indirect via get_cycles(), which is about to be
> removed.
>
> Use mftb() directly.
>
> Signed-off-by: Thomas Gleixner <tglx@kernel.org>
> Cc: Michael Ellerman <mpe@ellerman.id.au>
> Cc: linuxppc-dev@lists.ozlabs.org
Reviewed-by: Christophe Leroy (CS GROUP) <chleroy@kernel.org>
> ---
> arch/powerpc/platforms/cell/spufs/switch.c | 5 +++--
> 1 file changed, 3 insertions(+), 2 deletions(-)
>
> --- a/arch/powerpc/platforms/cell/spufs/switch.c
> +++ b/arch/powerpc/platforms/cell/spufs/switch.c
> @@ -34,6 +34,7 @@
> #include <asm/spu_priv1.h>
> #include <asm/spu_csa.h>
> #include <asm/mmu_context.h>
> +#include <asm/time.h>
>
> #include "spufs.h"
>
> @@ -279,7 +280,7 @@ static inline void save_timebase(struct
> * Read PPE Timebase High and Timebase low registers
> * and save in CSA. TBD.
> */
> - csa->suspend_time = get_cycles();
> + csa->suspend_time = mftb();
> }
>
> static inline void remove_other_spu_access(struct spu_state *csa,
> @@ -1261,7 +1262,7 @@ static inline void setup_decr(struct spu
> * in LSCSA.
> */
> if (csa->priv2.mfc_control_RW & MFC_CNTL_DECREMENTER_RUNNING) {
> - cycles_t resume_time = get_cycles();
> + cycles_t resume_time = mftb();
> cycles_t delta_time = resume_time - csa->suspend_time;
>
> csa->lscsa->decr_status.slot[0] = SPU_DECR_STATUS_RUNNING;
>
>
^ permalink raw reply
* Re: [patch 05/38] treewide: Remove CLOCK_TICK_RATE
From: Christophe Leroy (CS GROUP) @ 2026-04-15 6:40 UTC (permalink / raw)
To: Thomas Gleixner, LKML
Cc: Arnd Bergmann, x86, Lu Baolu, iommu, Michael Grzeschik, netdev,
linux-wireless, Herbert Xu, linux-crypto, Vlastimil Babka,
linux-mm, David Woodhouse, Bernie Thompson, linux-fbdev,
Theodore Tso, linux-ext4, Andrew Morton, Uladzislau Rezki,
Marco Elver, Dmitry Vyukov, kasan-dev, Andrey Ryabinin,
Thomas Sailer, linux-hams, Jason A. Donenfeld, Richard Henderson,
linux-alpha, Russell King, linux-arm-kernel, Catalin Marinas,
Huacai Chen, loongarch, Geert Uytterhoeven, linux-m68k,
Dinh Nguyen, Jonas Bonn, linux-openrisc, Helge Deller,
linux-parisc, Michael Ellerman, linuxppc-dev, Paul Walmsley,
linux-riscv, Heiko Carstens, linux-s390, David S. Miller,
sparclinux
In-Reply-To: <20260410120317.910770161@kernel.org>
Le 10/04/2026 à 14:18, Thomas Gleixner a écrit :
> This has been scheduled for removal more than a decade ago and the comments
> related to it have been dutifully ignored. The last dependencies are gone.
>
> Remove it along with various now empty asm/timex.h files.
>
> Signed-off-by: Thomas Gleixner <tglx@kernel.org>
For powerpc:
Reviewed-by: Christophe Leroy (CS GROUP) <chleroy@kernel.org>
> ---
> arch/alpha/include/asm/timex.h | 4 ----
> arch/arc/include/asm/timex.h | 15 ---------------
> arch/arm/mach-omap1/Kconfig | 2 +-
> arch/hexagon/include/asm/timex.h | 3 ---
> arch/m68k/include/asm/timex.h | 15 ---------------
> arch/microblaze/include/asm/timex.h | 13 -------------
> arch/mips/include/asm/timex.h | 8 --------
> arch/openrisc/include/asm/timex.h | 3 ---
> arch/parisc/include/asm/timex.h | 2 --
> arch/powerpc/include/asm/timex.h | 2 --
> arch/s390/include/asm/timex.h | 2 --
> arch/sh/include/asm/timex.h | 24 ------------------------
> arch/sparc/include/asm/timex.h | 2 +-
> arch/sparc/include/asm/timex_32.h | 14 --------------
> arch/sparc/include/asm/timex_64.h | 2 --
> arch/um/include/asm/timex.h | 9 ---------
> arch/x86/include/asm/timex.h | 3 ---
> 17 files changed, 2 insertions(+), 121 deletions(-)
>
> --- a/arch/alpha/include/asm/timex.h
> +++ b/arch/alpha/include/asm/timex.h
> @@ -7,10 +7,6 @@
> #ifndef _ASMALPHA_TIMEX_H
> #define _ASMALPHA_TIMEX_H
>
> -/* With only one or two oddballs, we use the RTC as the ticker, selecting
> - the 32.768kHz reference clock, which nicely divides down to our HZ. */
> -#define CLOCK_TICK_RATE 32768
> -
> /*
> * Standard way to access the cycle counter.
> * Currently only used on SMP for scheduling.
> --- a/arch/arc/include/asm/timex.h
> +++ /dev/null
> @@ -1,15 +0,0 @@
> -/* SPDX-License-Identifier: GPL-2.0-only */
> -/*
> - * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (https://eur01.safelinks.protection.outlook.com/?url=http%3A%2F%2Fwww.synopsys.com%2F&data=05%7C02%7Cchristophe.leroy%40csgroup.eu%7Cac13d5b928bc4eabd9b708de96fb5935%7C8b87af7d86474dc78df45f69a2011bb5%7C0%7C0%7C639114203455047148%7CUnknown%7CTWFpbGZsb3d8eyJFbXB0eU1hcGkiOnRydWUsIlYiOiIwLjAuMDAwMCIsIlAiOiJXaW4zMiIsIkFOIjoiTWFpbCIsIldUIjoyfQ%3D%3D%7C0%7C%7C%7C&sdata=uCL895qVLUoy3Stzhmgph2DiYmjpd4RPdQIW2dZcJ7w%3D&reserved=0)
> - */
> -
> -#ifndef _ASM_ARC_TIMEX_H
> -#define _ASM_ARC_TIMEX_H
> -
> -#define CLOCK_TICK_RATE 80000000 /* slated to be removed */
> -
> -#include <asm-generic/timex.h>
> -
> -/* XXX: get_cycles() to be implemented with RTSC insn */
> -
> -#endif /* _ASM_ARC_TIMEX_H */
> --- a/arch/arm/mach-omap1/Kconfig
> +++ b/arch/arm/mach-omap1/Kconfig
> @@ -74,7 +74,7 @@ config OMAP_32K_TIMER
> currently only available for OMAP16XX, 24XX, 34XX, OMAP4/5 and DRA7XX.
>
> On OMAP2PLUS this value is only used for CONFIG_HZ and
> - CLOCK_TICK_RATE compile time calculation.
> + timer frequency compile time calculation.
> The actual timer selection is done in the board file
> through the (DT_)MACHINE_START structure.
>
> --- a/arch/hexagon/include/asm/timex.h
> +++ b/arch/hexagon/include/asm/timex.h
> @@ -9,9 +9,6 @@
> #include <asm-generic/timex.h>
> #include <asm/hexagon_vm.h>
>
> -/* Using TCX0 as our clock. CLOCK_TICK_RATE scheduled to be removed. */
> -#define CLOCK_TICK_RATE 19200
> -
> #define ARCH_HAS_READ_CURRENT_TIMER
>
> static inline int read_current_timer(unsigned long *timer_val)
> --- a/arch/m68k/include/asm/timex.h
> +++ b/arch/m68k/include/asm/timex.h
> @@ -7,21 +7,6 @@
> #ifndef _ASMm68K_TIMEX_H
> #define _ASMm68K_TIMEX_H
>
> -#ifdef CONFIG_COLDFIRE
> -/*
> - * CLOCK_TICK_RATE should give the underlying frequency of the tick timer
> - * to make ntp work best. For Coldfires, that's the main clock.
> - */
> -#include <asm/coldfire.h>
> -#define CLOCK_TICK_RATE MCF_CLK
> -#else
> -/*
> - * This default CLOCK_TICK_RATE is probably wrong for many 68k boards
> - * Users of those boards will need to check and modify accordingly
> - */
> -#define CLOCK_TICK_RATE 1193180 /* Underlying HZ */
> -#endif
> -
> typedef unsigned long cycles_t;
>
> static inline cycles_t get_cycles(void)
> --- a/arch/microblaze/include/asm/timex.h
> +++ /dev/null
> @@ -1,13 +0,0 @@
> -/* SPDX-License-Identifier: GPL-2.0 */
> -/*
> - * Copyright (C) 2006 Atmark Techno, Inc.
> - */
> -
> -#ifndef _ASM_MICROBLAZE_TIMEX_H
> -#define _ASM_MICROBLAZE_TIMEX_H
> -
> -#include <asm-generic/timex.h>
> -
> -#define CLOCK_TICK_RATE 1000 /* Timer input freq. */
> -
> -#endif /* _ASM_TIMEX_H */
> --- a/arch/mips/include/asm/timex.h
> +++ b/arch/mips/include/asm/timex.h
> @@ -19,14 +19,6 @@
> #include <asm/cpu-type.h>
>
> /*
> - * This is the clock rate of the i8253 PIT. A MIPS system may not have
> - * a PIT by the symbol is used all over the kernel including some APIs.
> - * So keeping it defined to the number for the PIT is the only sane thing
> - * for now.
> - */
> -#define CLOCK_TICK_RATE 1193182
> -
> -/*
> * Standard way to access the cycle counter.
> * Currently only used on SMP for scheduling.
> *
> --- a/arch/openrisc/include/asm/timex.h
> +++ b/arch/openrisc/include/asm/timex.h
> @@ -25,9 +25,6 @@ static inline cycles_t get_cycles(void)
> }
> #define get_cycles get_cycles
>
> -/* This isn't really used any more */
> -#define CLOCK_TICK_RATE 1000
> -
> #define ARCH_HAS_READ_CURRENT_TIMER
>
> #endif
> --- a/arch/parisc/include/asm/timex.h
> +++ b/arch/parisc/include/asm/timex.h
> @@ -9,8 +9,6 @@
>
> #include <asm/special_insns.h>
>
> -#define CLOCK_TICK_RATE 1193180 /* Underlying HZ */
> -
> typedef unsigned long cycles_t;
>
> static inline cycles_t get_cycles(void)
> --- a/arch/powerpc/include/asm/timex.h
> +++ b/arch/powerpc/include/asm/timex.h
> @@ -11,8 +11,6 @@
> #include <asm/cputable.h>
> #include <asm/vdso/timebase.h>
>
> -#define CLOCK_TICK_RATE 1024000 /* Underlying HZ */
> -
> typedef unsigned long cycles_t;
>
> static inline cycles_t get_cycles(void)
> --- a/arch/s390/include/asm/timex.h
> +++ b/arch/s390/include/asm/timex.h
> @@ -177,8 +177,6 @@ static inline void local_tick_enable(uns
> set_clock_comparator(get_lowcore()->clock_comparator);
> }
>
> -#define CLOCK_TICK_RATE 1193180 /* Underlying HZ */
> -
> typedef unsigned long cycles_t;
>
> static __always_inline unsigned long get_tod_clock(void)
> --- a/arch/sh/include/asm/timex.h
> +++ /dev/null
> @@ -1,24 +0,0 @@
> -/* SPDX-License-Identifier: GPL-2.0 */
> -/*
> - * linux/include/asm-sh/timex.h
> - *
> - * sh architecture timex specifications
> - */
> -#ifndef __ASM_SH_TIMEX_H
> -#define __ASM_SH_TIMEX_H
> -
> -/*
> - * Only parts using the legacy CPG code for their clock framework
> - * implementation need to define their own Pclk value. If provided, this
> - * can be used for accurately setting CLOCK_TICK_RATE, otherwise we
> - * simply fall back on the i8253 PIT value.
> - */
> -#ifdef CONFIG_SH_PCLK_FREQ
> -#define CLOCK_TICK_RATE (CONFIG_SH_PCLK_FREQ / 4) /* Underlying HZ */
> -#else
> -#define CLOCK_TICK_RATE 1193180
> -#endif
> -
> -#include <asm-generic/timex.h>
> -
> -#endif /* __ASM_SH_TIMEX_H */
> --- a/arch/sparc/include/asm/timex.h
> +++ b/arch/sparc/include/asm/timex.h
> @@ -4,6 +4,6 @@
> #if defined(__sparc__) && defined(__arch64__)
> #include <asm/timex_64.h>
> #else
> -#include <asm/timex_32.h>
> +#include <asm-generic/timex.h>
> #endif
> #endif
> --- a/arch/sparc/include/asm/timex_32.h
> +++ /dev/null
> @@ -1,14 +0,0 @@
> -/* SPDX-License-Identifier: GPL-2.0 */
> -/*
> - * linux/include/asm/timex.h
> - *
> - * sparc architecture timex specifications
> - */
> -#ifndef _ASMsparc_TIMEX_H
> -#define _ASMsparc_TIMEX_H
> -
> -#define CLOCK_TICK_RATE 1193180 /* Underlying HZ */
> -
> -#include <asm-generic/timex.h>
> -
> -#endif
> --- a/arch/sparc/include/asm/timex_64.h
> +++ b/arch/sparc/include/asm/timex_64.h
> @@ -9,8 +9,6 @@
>
> #include <asm/timer.h>
>
> -#define CLOCK_TICK_RATE 1193180 /* Underlying HZ */
> -
> /* Getting on the cycle counter on sparc64. */
> typedef unsigned long cycles_t;
> #define get_cycles() tick_ops->get_tick()
> --- a/arch/um/include/asm/timex.h
> +++ /dev/null
> @@ -1,9 +0,0 @@
> -/* SPDX-License-Identifier: GPL-2.0 */
> -#ifndef __UM_TIMEX_H
> -#define __UM_TIMEX_H
> -
> -#define CLOCK_TICK_RATE (HZ)
> -
> -#include <asm-generic/timex.h>
> -
> -#endif
> --- a/arch/x86/include/asm/timex.h
> +++ b/arch/x86/include/asm/timex.h
> @@ -14,9 +14,6 @@ static inline unsigned long random_get_e
> }
> #define random_get_entropy random_get_entropy
>
> -/* Assume we use the PIT time source for the clock tick */
> -#define CLOCK_TICK_RATE PIT_TICK_RATE
> -
> #define ARCH_HAS_READ_CURRENT_TIMER
>
> #endif /* _ASM_X86_TIMEX_H */
>
>
^ permalink raw reply
* Re: [patch 07/38] treewide: Consolidate cycles_t
From: Christophe Leroy (CS GROUP) @ 2026-04-15 6:43 UTC (permalink / raw)
To: Thomas Gleixner, LKML
Cc: Arnd Bergmann, x86, Lu Baolu, iommu, Michael Grzeschik, netdev,
linux-wireless, Herbert Xu, linux-crypto, Vlastimil Babka,
linux-mm, David Woodhouse, Bernie Thompson, linux-fbdev,
Theodore Tso, linux-ext4, Andrew Morton, Uladzislau Rezki,
Marco Elver, Dmitry Vyukov, kasan-dev, Andrey Ryabinin,
Thomas Sailer, linux-hams, Jason A. Donenfeld, Richard Henderson,
linux-alpha, Russell King, linux-arm-kernel, Catalin Marinas,
Huacai Chen, loongarch, Geert Uytterhoeven, linux-m68k,
Dinh Nguyen, Jonas Bonn, linux-openrisc, Helge Deller,
linux-parisc, Michael Ellerman, linuxppc-dev, Paul Walmsley,
linux-riscv, Heiko Carstens, linux-s390, David S. Miller,
sparclinux
In-Reply-To: <20260410120318.045532623@kernel.org>
Le 10/04/2026 à 14:19, Thomas Gleixner a écrit :
> Most architectures define cycles_t as unsigned long execpt:
>
> - x86 requires it to be 64-bit independent of the 32-bit/64-bit build.
>
> - parisc and mips define it as unsigned int
>
> parisc has no real reason to do so as there are only a few usage sites
> which either expand it to a 64-bit value or utilize only the lower
> 32bits.
>
> mips has no real requirement either.
>
> Move the typedef to types.h and provide a config switch to enforce the
> 64-bit type for x86.
>
> Signed-off-by: Thomas Gleixner <tglx@kernel.org>
> ---
> arch/Kconfig | 4 ++++
> arch/alpha/include/asm/timex.h | 3 ---
> arch/arm/include/asm/timex.h | 1 -
> arch/loongarch/include/asm/timex.h | 2 --
> arch/m68k/include/asm/timex.h | 2 --
> arch/mips/include/asm/timex.h | 2 --
> arch/nios2/include/asm/timex.h | 2 --
> arch/parisc/include/asm/timex.h | 2 --
> arch/powerpc/include/asm/timex.h | 4 +---
> arch/riscv/include/asm/timex.h | 2 --
> arch/s390/include/asm/timex.h | 2 --
> arch/sparc/include/asm/timex_64.h | 1 -
> arch/x86/Kconfig | 1 +
> arch/x86/include/asm/tsc.h | 2 --
> include/asm-generic/timex.h | 1 -
> include/linux/types.h | 6 ++++++
> 16 files changed, 12 insertions(+), 25 deletions(-)
>
> --- a/arch/powerpc/include/asm/timex.h
> +++ b/arch/powerpc/include/asm/timex.h
> @@ -11,9 +11,7 @@
> #include <asm/cputable.h>
> #include <asm/vdso/timebase.h>
>
> -typedef unsigned long cycles_t;
> -
> -static inline cycles_t get_cycles(void)
> +ostatic inline cycles_t get_cycles(void)
What is 'ostatic' ?
> {
> return mftb();
> }
^ permalink raw reply
* Re: [PATCH v3 2/8] dt-bindings: arm: Add zx297520v3 board binding
From: Krzysztof Kozlowski @ 2026-04-15 6:45 UTC (permalink / raw)
To: Stefan Dösinger, linux-arm-kernel
Cc: Linus Walleij, Arnd Bergmann, Krzysztof Kozlowski, Rob Herring
In-Reply-To: <20260414211215.152850-3-stefandoesinger@gmail.com>
On 14/04/2026 23:12, Stefan Dösinger wrote:
> Add a compatible for boards based on the ZTE zx297520v3 SoC.
>
> Signed-off-by: Stefan Dösinger <stefandoesinger@gmail.com>
> ---
> .../devicetree/bindings/arm/zx29.yaml | 20 +++++++++++++++++++
Please use scripts/get_maintainers.pl to get a list of necessary people
and lists to CC. It might happen, that command when run on an older
kernel, gives you outdated entries. Therefore please be sure you base
your patches on recent Linux kernel.
Tools like b4 or scripts/get_maintainer.pl provide you proper list of
people, so fix your workflow. Tools might also fail if you work on some
ancient tree (don't, instead use mainline) or work on fork of kernel
(don't, instead use mainline). Just use b4 and everything should be
fine, although remember about `b4 prep --auto-to-cc` if you added new
patches to the patchset.
You missed at least devicetree list (maybe more), so this won't be
tested by automated tooling. Performing review on untested code might be
a waste of time.
Please kindly resend and include all necessary To/Cc entries.
> MAINTAINERS | 1 +
> 2 files changed, 21 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/arm/zx29.yaml
>
> diff --git a/Documentation/devicetree/bindings/arm/zx29.yaml b/Documentation/devicetree/bindings/arm/zx29.yaml
> new file mode 100644
> index 000000000000..717ca6413993
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/arm/zx29.yaml
> @@ -0,0 +1,20 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/arm/zx29.yaml#
Bindings are per vendor, so zte.
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: ZTE zx29
> +
> +maintainers:
> + - Stefan Dösinger <stefandoesinger@gmail.com>
> +
> +properties:
> + $nodename:
> + const: "/"
> + compatible:
> + oneOf:
> + - items:
> + - const: zte,zx297520v3
Incomplete. Neither board nor SoC can be alone.
Best regards,
Krzysztof
^ permalink raw reply
* Re: [PATCH v3] arm64: defconfig: Enable Rockchip video decoder
From: Krzysztof Kozlowski @ 2026-04-15 6:47 UTC (permalink / raw)
To: Detlev Casanova, linux-kernel, linux-arm-kernel, kernel,
linux-rockchip
In-Reply-To: <20260414-rkvdec-add-defconfig-v3-1-a3b49b0ca4a2@collabora.com>
On 14/04/2026 15:37, Detlev Casanova wrote:
> The Rockchip video decoder (rkvdec) provides hardware-accelerated video
> decoding on Rockchip SoCs.
Again, open lore or git log and look what was done here.
Which boards (with socs) need it?
> Enable it as a module so that users of these
> widely used platforms get video decoding support out of the box.
That's not valid reasons. Users use distros and distros have their own
defconfig. Plus this is kind of trivial/obvious. If you looked at
previous discussions you would see what is needed here.
Best regards,
Krzysztof
^ permalink raw reply
* Re: [patch 33/38] powerpc: Select ARCH_HAS_RANDOM_ENTROPY
From: Christophe Leroy (CS GROUP) @ 2026-04-15 6:47 UTC (permalink / raw)
To: Thomas Gleixner, LKML
Cc: Michael Ellerman, linuxppc-dev, Arnd Bergmann, x86, Lu Baolu,
iommu, Michael Grzeschik, netdev, linux-wireless, Herbert Xu,
linux-crypto, Vlastimil Babka, linux-mm, David Woodhouse,
Bernie Thompson, linux-fbdev, Theodore Tso, linux-ext4,
Andrew Morton, Uladzislau Rezki, Marco Elver, Dmitry Vyukov,
kasan-dev, Andrey Ryabinin, Thomas Sailer, linux-hams,
Jason A. Donenfeld, Richard Henderson, linux-alpha, Russell King,
linux-arm-kernel, Catalin Marinas, Huacai Chen, loongarch,
Geert Uytterhoeven, linux-m68k, Dinh Nguyen, Jonas Bonn,
linux-openrisc, Helge Deller, linux-parisc, Paul Walmsley,
linux-riscv, Heiko Carstens, linux-s390, David S. Miller,
sparclinux
In-Reply-To: <20260410120319.789114053@kernel.org>
Le 10/04/2026 à 14:21, Thomas Gleixner a écrit :
> The only remaining usage of get_cycles() is to provide random_get_entropy().
>
> Switch powerpc over to the new scheme of selecting ARCH_HAS_RANDOM_ENTROPY
> and providing random_get_entropy() in asm/random.h.
>
> Remove asm/timex.h as it has no functionality anymore.
>
> Signed-off-by: Thomas Gleixner <tglx@kernel.org>
> Cc: Michael Ellerman <mpe@ellerman.id.au>
> Cc: linuxppc-dev@lists.ozlabs.org
Reviewed-by: Christophe Leroy (CS GROUP) <chleroy@kernel.org>
> ---
> arch/powerpc/Kconfig | 1 +
> arch/powerpc/include/asm/random.h | 13 +++++++++++++
> arch/powerpc/include/asm/timex.h | 21 ---------------------
> 3 files changed, 14 insertions(+), 21 deletions(-)
>
> --- a/arch/powerpc/Kconfig
> +++ b/arch/powerpc/Kconfig
> @@ -150,6 +150,7 @@ config PPC
> select ARCH_HAS_PREEMPT_LAZY
> select ARCH_HAS_PTDUMP
> select ARCH_HAS_PTE_SPECIAL
> + select ARCH_HAS_RANDOM_ENTROPY
> select ARCH_HAS_SCALED_CPUTIME if VIRT_CPU_ACCOUNTING_NATIVE && PPC_BOOK3S_64
> select ARCH_HAS_SET_MEMORY
> select ARCH_HAS_STRICT_KERNEL_RWX if (PPC_BOOK3S || PPC_8xx) && !HIBERNATION
> --- /dev/null
> +++ b/arch/powerpc/include/asm/random.h
> @@ -0,0 +1,13 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +#ifndef _ASM_POWERPC_RANDOM_H
> +#define _ASM_POWERPC_RANDOM_H
> +
> +#include <asm/cputable.h>
> +#include <asm/vdso/timebase.h>
> +
> +static inline unsigned long random_get_entropy(void)
> +{
> + return mftb();
> +}
> +
> +#endif /* _ASM_POWERPC_RANDOM_H */
> --- a/arch/powerpc/include/asm/timex.h
> +++ b/arch/powerpc/include/asm/timex.h
> @@ -1,21 +0,0 @@
> -/* SPDX-License-Identifier: GPL-2.0 */
> -#ifndef _ASM_POWERPC_TIMEX_H
> -#define _ASM_POWERPC_TIMEX_H
> -
> -#ifdef __KERNEL__
> -
> -/*
> - * PowerPC architecture timex specifications
> - */
> -
> -#include <asm/cputable.h>
> -#include <asm/vdso/timebase.h>
> -
> -ostatic inline cycles_t get_cycles(void)
> -{
> - return mftb();
> -}
> -#define get_cycles get_cycles
> -
> -#endif /* __KERNEL__ */
> -#endif /* _ASM_POWERPC_TIMEX_H */
>
>
^ permalink raw reply
* Re: [PATCH v7 2/6] dt-bindings: display: bridge: simple: document the Lontium LT8711UXD DP-to-HDMI bridge
From: Krzysztof Kozlowski @ 2026-04-15 6:57 UTC (permalink / raw)
To: Dennis Gilmore
Cc: Alexey Charkov, Andrew Lunn, Andrzej Hajda, Chaoyi Chen,
Conor Dooley, David Airlie, devicetree, dri-devel, FUKAUMI Naoki,
Heiko Stuebner, Hsun Lai, Jernej Skrabec, Jimmy Hon, John Clark,
Jonas Karlman, Krzysztof Kozlowski, Laurent Pinchart,
linux-arm-kernel, linux-kernel, linux-rockchip, Maarten Lankhorst,
Maxime Ripard, Michael Opdenacker, Michael Riesch, Mykola Kvach,
Neil Armstrong, Peter Robinson, Quentin Schulz, Robert Foss,
Rob Herring, Simona Vetter, Thomas Zimmermann
In-Reply-To: <20260414214104.1363987-3-dennis@ausil.us>
On Tue, Apr 14, 2026 at 04:40:53PM -0500, Dennis Gilmore wrote:
> The Lontium LT8711UXD is a high performance two lane Type-C/DP1.4
> to HDMI2.0 converter, designed to connect a USB Type-C source or
> a DP1.4 source to an HDMI2.0 sink.
>
> Signed-off-by: Dennis Gilmore <dennis@ausil.us>
> ---
> .../devicetree/bindings/display/bridge/simple-bridge.yaml | 1 +
> 1 file changed, 1 insertion(+)
Acked-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
Best regards,
Krzysztof
^ permalink raw reply
* Re: [PATCH v2] pmdomain: imx: Make IMX8M/IMX9 BLK_CTRL tristate
From: Frank Li @ 2026-04-15 6:58 UTC (permalink / raw)
To: Zhipeng Wang
Cc: ulfh, s.hauer, kernel, festevam, linux-pm, imx, linux-arm-kernel,
linux-kernel, xuegang.liu, jindong.yue
In-Reply-To: <20260413053049.3041177-1-zhipeng.wang_1@nxp.com>
On Mon, Apr 13, 2026 at 02:30:49PM +0900, Zhipeng Wang wrote:
> Convert IMX8M_BLK_CTRL and IMX9_BLK_CTRL from bool to tristate
> to allow building as loadable modules.
>
> Add prompt strings to make these options visible and configurable
> in menuconfig, keeping them enabled by default on appropriate platforms.
>
> Also remove the IMX_GPCV2_PM_DOMAINS dependency from IMX9_BLK_CTRL.
> This dependency was incorrect from the beginning - i.MX93 uses a
s/-/because
Reviewed-by: Frank Li <Frank.Li@nxp.com>
> different power domain architecture compared to i.MX8M series:
>
> - i.MX8M uses GPCv2 (General Power Controller v2) for power domain
> management, hence IMX8M_BLK_CTRL correctly depends on it.
>
> - i.MX93 uses BLK_CTRL directly without GPCv2. The hardware doesn't
> have GPCv2 at all.
>
> Signed-off-by: Zhipeng Wang <zhipeng.wang_1@nxp.com>
> ---
> drivers/pmdomain/imx/Kconfig | 11 +++++++----
> 1 file changed, 7 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/pmdomain/imx/Kconfig b/drivers/pmdomain/imx/Kconfig
> index 00203615c65e..9168d183b0c5 100644
> --- a/drivers/pmdomain/imx/Kconfig
> +++ b/drivers/pmdomain/imx/Kconfig
> @@ -10,15 +10,18 @@ config IMX_GPCV2_PM_DOMAINS
> default y if SOC_IMX7D
>
> config IMX8M_BLK_CTRL
> - bool
> - default SOC_IMX8M && IMX_GPCV2_PM_DOMAINS
> + tristate "i.MX8M BLK CTRL driver"
> + depends on SOC_IMX8M
> + depends on IMX_GPCV2_PM_DOMAINS
> depends on PM_GENERIC_DOMAINS
> depends on COMMON_CLK
> + default y
>
> config IMX9_BLK_CTRL
> - bool
> - default SOC_IMX9 && IMX_GPCV2_PM_DOMAINS
> + tristate "i.MX93 BLK CTRL driver"
> + depends on SOC_IMX9
> depends on PM_GENERIC_DOMAINS
> + default y
>
> config IMX_SCU_PD
> bool "IMX SCU Power Domain driver"
> --
> 2.34.1
>
^ permalink raw reply
* [PATCH v14 0/5] Add support for Verisilicon IOMMU used by media codec blocks
From: Benjamin Gaignard @ 2026-04-15 7:23 UTC (permalink / raw)
To: joro, will, robin.murphy, krzk+dt, conor+dt, heiko
Cc: iommu, devicetree, linux-kernel, linux-arm-kernel, linux-rockchip,
kernel, Benjamin Gaignard
Hi all,
This patch series adds support for the Verisilicon IOMMU, which is found in front
of hardware encoder and decoder blocks in several SoCs using Verisilicon IP.
A first implementation of this IOMMU is available on the Rockchip RK3588 SoC.
Rockchip provides a driver for this hardware in their 6.1 kernel branch:
https://github.com/rockchip-linux/kernel/blob/develop-6.1/drivers/iommu/rockchip-iommu-av1d.c
This series includes:
- a new binding for the Verisilicon IOMMU
- a driver implementation
- DT updates for RK3588
The driver was forward-ported from Rockchip’s 6.1 implementation,
the prefix was renamed to vsi for generality, and several fixes were
applied.
AV1 decoding was tested using the stateless VPU driver and Fluster.
The test results show a score of 205/239, which confirms that no
regressions were introduced by this series.
Feedback and testing welcome.
changes in version 14:
- Flush TLB after each map/unmap operations.
- Remove vsi_iommu_restore_ctx() and do not touch Verisilicon stateless
video decoder.
- Allow to build the driver as a module.
changes in version 13:
- On top the driver file explicit why the hardware is different than Rockchip IOMMU
- Document why vsi_iommu_restore_ctx() is needed when using Verisilicon
stateless video decoder.
changes in version 12:
- Remove useless vsi_iommu_flush_tlb_all()
- Merge MAINTAINERS changes in the patch introducing VSI iommu driver
Benjamin Gaignard (5):
dt-bindings: vendor-prefixes: Add Verisilicon
dt-bindings: iommu: verisilicon: Add binding for VSI IOMMU
iommu: Add verisilicon IOMMU driver
arm64: dts: rockchip: Add verisilicon IOMMU node on RK3588
arm64: defconfig: enable Verisilicon IOMMU for Rockchip RK3588
.../bindings/iommu/verisilicon,iommu.yaml | 71 ++
.../devicetree/bindings/vendor-prefixes.yaml | 2 +
MAINTAINERS | 8 +
arch/arm64/boot/dts/rockchip/rk3588-base.dtsi | 11 +
arch/arm64/configs/defconfig | 1 +
drivers/iommu/Kconfig | 11 +
drivers/iommu/Makefile | 1 +
drivers/iommu/vsi-iommu.c | 796 ++++++++++++++++++
8 files changed, 901 insertions(+)
create mode 100644 Documentation/devicetree/bindings/iommu/verisilicon,iommu.yaml
create mode 100644 drivers/iommu/vsi-iommu.c
--
2.43.0
^ permalink raw reply
* [PATCH v14 1/5] dt-bindings: vendor-prefixes: Add Verisilicon
From: Benjamin Gaignard @ 2026-04-15 7:23 UTC (permalink / raw)
To: joro, will, robin.murphy, krzk+dt, conor+dt, heiko
Cc: iommu, devicetree, linux-kernel, linux-arm-kernel, linux-rockchip,
kernel, Benjamin Gaignard, Conor Dooley
In-Reply-To: <20260415072349.44237-1-benjamin.gaignard@collabora.com>
Verisilicon Microelectronics is a company based in Shanghai, China,
developping hardware blocks for SoC.
https://verisilicon.com/
Add their name to the list of vendors.
Signed-off-by: Benjamin Gaignard <benjamin.gaignard@collabora.com>
Acked-by: Conor Dooley <conor.dooley@microchip.com>
---
Documentation/devicetree/bindings/vendor-prefixes.yaml | 2 ++
1 file changed, 2 insertions(+)
diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Documentation/devicetree/bindings/vendor-prefixes.yaml
index ee7fd3cfe203..ebd9072300a8 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.yaml
+++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml
@@ -1761,6 +1761,8 @@ patternProperties:
description: Variscite Ltd.
"^vdl,.*":
description: Van der Laan b.v.
+ "^verisilicon,.*":
+ description: VeriSilicon Microelectronics
"^vertexcom,.*":
description: Vertexcom Technologies, Inc.
"^via,.*":
--
2.43.0
^ permalink raw reply related
* [PATCH v14 5/5] arm64: defconfig: enable Verisilicon IOMMU for Rockchip RK3588
From: Benjamin Gaignard @ 2026-04-15 7:23 UTC (permalink / raw)
To: joro, will, robin.murphy, krzk+dt, conor+dt, heiko
Cc: iommu, devicetree, linux-kernel, linux-arm-kernel, linux-rockchip,
kernel, Benjamin Gaignard, Krzysztof Kozlowski
In-Reply-To: <20260415072349.44237-1-benjamin.gaignard@collabora.com>
Enable Verisilicon IOMMU used by Rockchip RK3588 AV1 hardware codec.
This hardware block could be found in Radxa ROCK 5B board.
Signed-off-by: Benjamin Gaignard <benjamin.gaignard@collabora.com>
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
---
arch/arm64/configs/defconfig | 1 +
1 file changed, 1 insertion(+)
diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig
index b67d5b1fc45b..b97f5008f6be 100644
--- a/arch/arm64/configs/defconfig
+++ b/arch/arm64/configs/defconfig
@@ -1592,6 +1592,7 @@ CONFIG_ARM_SMMU_V3=y
CONFIG_MTK_IOMMU=y
CONFIG_QCOM_IOMMU=y
CONFIG_APPLE_DART=m
+CONFIG_VSI_IOMMU=m
CONFIG_REMOTEPROC=y
CONFIG_IMX_REMOTEPROC=y
CONFIG_MTK_SCP=m
--
2.43.0
^ permalink raw reply related
* [PATCH v14 4/5] arm64: dts: rockchip: Add verisilicon IOMMU node on RK3588
From: Benjamin Gaignard @ 2026-04-15 7:23 UTC (permalink / raw)
To: joro, will, robin.murphy, krzk+dt, conor+dt, heiko
Cc: iommu, devicetree, linux-kernel, linux-arm-kernel, linux-rockchip,
kernel, Benjamin Gaignard
In-Reply-To: <20260415072349.44237-1-benjamin.gaignard@collabora.com>
Add the device tree node for the Verisilicon IOMMU present
in the RK3588 SoC.
This IOMMU handles address translation for the VPU hardware blocks.
Signed-off-by: Benjamin Gaignard <benjamin.gaignard@collabora.com>
---
arch/arm64/boot/dts/rockchip/rk3588-base.dtsi | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/arch/arm64/boot/dts/rockchip/rk3588-base.dtsi b/arch/arm64/boot/dts/rockchip/rk3588-base.dtsi
index 7fe9593d8c19..7fde18feeaf8 100644
--- a/arch/arm64/boot/dts/rockchip/rk3588-base.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk3588-base.dtsi
@@ -1428,6 +1428,17 @@ av1d: video-codec@fdc70000 {
clock-names = "aclk", "hclk";
power-domains = <&power RK3588_PD_AV1>;
resets = <&cru SRST_A_AV1>, <&cru SRST_P_AV1>, <&cru SRST_A_AV1_BIU>, <&cru SRST_P_AV1_BIU>;
+ iommus = <&av1d_mmu>;
+ };
+
+ av1d_mmu: iommu@fdca0000 {
+ compatible = "rockchip,rk3588-av1-iommu", "verisilicon,iommu-1.2";
+ reg = <0x0 0xfdca0000 0x0 0x600>;
+ interrupts = <GIC_SPI 109 IRQ_TYPE_LEVEL_HIGH 0>;
+ clocks = <&cru ACLK_AV1>, <&cru PCLK_AV1>;
+ clock-names = "core", "iface";
+ #iommu-cells = <0>;
+ power-domains = <&power RK3588_PD_AV1>;
};
vop: vop@fdd90000 {
--
2.43.0
^ 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