* [PATCH 1/3] dt-bindings: soc: ti: Document the ti,tda54-mcu-scm-conf binding
2026-10-19 8:07 [PATCH 0/3] soc: ti: Add TDA54 SCM configuration syscon support Moteen Shah
@ 2026-10-19 8:07 ` Moteen Shah
2026-10-20 5:57 ` Krzysztof Kozlowski
2026-10-19 8:07 ` [PATCH 2/3] soc: ti: k3-socinfo: Export SoC registration helper Moteen Shah
2026-10-19 8:07 ` [PATCH 3/3] soc: ti: Add TDA54 SCM configuration syscon support Moteen Shah
2 siblings, 1 reply; 6+ messages in thread
From: Moteen Shah @ 2026-10-19 8:07 UTC (permalink / raw)
To: conor+dt, robh, krzk+dt, linux-kernel, nm, ssantosh, linux-arm-kernel
Cc: u-kumar1, gehariprasath, m-shah, vigneshr, afd, a-limaye,
y-abhilashchandra
TDA54's MCU domain exposes a shared scm_conf syscon region that other
peripherals, including chipid, access into rather than owning a
dedicated register window. Rather than a bare fallback entry in the
generic syscon compatible list, give it a dedicated binding
describing the region and the proxy/claim/lock arbitration scheme
that gates write access to a subset of its registers.
Some of the claimable registers are gate clock controls, so also
document the required "#clock-cells" property that lets the node be
referenced directly as a clock provider for those gates. The clock
specifier selects the ePWM time-base gate by instance number.
Signed-off-by: Moteen Shah <m-shah@ti.com>
Signed-off-by: Aniket Limaye <a-limaye@ti.com>
---
.../soc/ti/ti,tda54-mcu-scm-conf.yaml | 78 +++++++++++++++++++
1 file changed, 78 insertions(+)
create mode 100644 Documentation/devicetree/bindings/soc/ti/ti,tda54-mcu-scm-conf.yaml
diff --git a/Documentation/devicetree/bindings/soc/ti/ti,tda54-mcu-scm-conf.yaml b/Documentation/devicetree/bindings/soc/ti/ti,tda54-mcu-scm-conf.yaml
new file mode 100644
index 0000000000000..111cbb739b5f1
--- /dev/null
+++ b/Documentation/devicetree/bindings/soc/ti/ti,tda54-mcu-scm-conf.yaml
@@ -0,0 +1,78 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/soc/ti/ti,tda54-mcu-scm-conf.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Texas Instruments TDA54 SoC MCU Domain System Control Module Configuration registers
+
+maintainers:
+ - Aniket Limaye <a-limaye@ti.com>
+ - Moteen Shah <m-shah@ti.com>
+
+description: |
+ The TDA54 SoC's MCU domain exposes a System Control Module configuration
+ register region (scm_conf) containing miscellaneous control and status
+ registers for MCU domain peripherals, including the JTAGID/device
+ identification registers, boot configuration and status registers, and
+ device feature registers.
+
+ A subset of these registers is write-protected by a proxy/claim scheme:
+ each register is mirrored across 8 proxy regions, and a host must unlock
+ via LOCK_KICK. Any proxy other than proxy-0 must also claim a register
+ through its CLAIM register before writing through that proxy. Proxy-0 is
+ the default access proxy: its claim bits are tied low, so it needs no
+ claim and can write any register that is not claimed by another proxy.
+ For example: Proxy0's CLAIM0 register sits at offset 0x3100, while the
+ same register mirrored at ProxyN sits at N * 0x4000 + 0x3100.
+
+ Each of the 8 proxy regions also has its own fault status and
+ interrupt registers, but a fault can only be handled through proxy-0.
+ Hence, interrupt/fault handling is not described by this binding.
+
+ Some of the claimable registers are gate clock controls (e.g. the
+ ehrpwm_tbclk gates). This node is the clock provider for those gates;
+ writes to them are still routed through the proxy claim scheme described
+ above.
+
+properties:
+ compatible:
+ const: ti,tda54-mcu-scm-conf
+
+ reg:
+ maxItems: 1
+
+ ti,proxy-id:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ minimum: 0
+ maximum: 7
+ description:
+ Proxy region (0-7) through which this node's clients access
+ claimable registers and their fault status registers.
+
+ "#clock-cells":
+ const: 1
+ description:
+ The cell selects the ePWM time-base clock gate. Values 0 to 12 select
+ ehrpwm_tbclk0 to ehrpwm_tbclk12, i.e. the gate for ePWM instance N is
+ specifier N.
+
+required:
+ - compatible
+ - reg
+ - ti,proxy-id
+ - "#clock-cells"
+
+additionalProperties: false
+
+examples:
+
+ - |
+ scm_conf1: syscon@5e800000 {
+ compatible = "ti,tda54-mcu-scm-conf";
+ reg = <0x5e800000 0x20000>;
+ ti,proxy-id = <0>;
+ #clock-cells = <1>;
+ };
+
+...
--
2.34.1
^ permalink raw reply [flat|nested] 6+ messages in thread* Re: [PATCH 1/3] dt-bindings: soc: ti: Document the ti,tda54-mcu-scm-conf binding
2026-10-19 8:07 ` [PATCH 1/3] dt-bindings: soc: ti: Document the ti,tda54-mcu-scm-conf binding Moteen Shah
@ 2026-10-20 5:57 ` Krzysztof Kozlowski
0 siblings, 0 replies; 6+ messages in thread
From: Krzysztof Kozlowski @ 2026-10-20 5:57 UTC (permalink / raw)
To: Moteen Shah, conor+dt, robh, krzk+dt, linux-kernel, nm, ssantosh,
linux-arm-kernel
Cc: u-kumar1, gehariprasath, vigneshr, afd, a-limaye, y-abhilashchandra
On 19/10/2026 10:07, Moteen Shah wrote:
> TDA54's MCU domain exposes a shared scm_conf syscon region that other
> peripherals, including chipid, access into rather than owning a
> dedicated register window. Rather than a bare fallback entry in the
> generic syscon compatible list, give it a dedicated binding
> describing the region and the proxy/claim/lock arbitration scheme
> that gates write access to a subset of its registers.
>
> Some of the claimable registers are gate clock controls, so also
> document the required "#clock-cells" property that lets the node be
> referenced directly as a clock provider for those gates. The clock
> specifier selects the ePWM time-base gate by instance number.
>
> Signed-off-by: Moteen Shah <m-shah@ti.com>
> Signed-off-by: Aniket Limaye <a-limaye@ti.com>
Incomplete DCO.
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.
Best regards,
Krzysztof
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH 2/3] soc: ti: k3-socinfo: Export SoC registration helper
2026-10-19 8:07 [PATCH 0/3] soc: ti: Add TDA54 SCM configuration syscon support Moteen Shah
2026-10-19 8:07 ` [PATCH 1/3] dt-bindings: soc: ti: Document the ti,tda54-mcu-scm-conf binding Moteen Shah
@ 2026-10-19 8:07 ` Moteen Shah
2026-10-19 17:38 ` Andrew Davis
2026-10-19 8:07 ` [PATCH 3/3] soc: ti: Add TDA54 SCM configuration syscon support Moteen Shah
2 siblings, 1 reply; 6+ messages in thread
From: Moteen Shah @ 2026-10-19 8:07 UTC (permalink / raw)
To: conor+dt, robh, krzk+dt, linux-kernel, nm, ssantosh, linux-arm-kernel
Cc: u-kumar1, gehariprasath, m-shah, vigneshr, afd, a-limaye,
y-abhilashchandra
The k3-socinfo driver reads the JTAG ID through a dedicated MMIO region and
then decodes it and registers the SoC device.
Newer TI SoCs like TDA54 use a different path for accessing the JTAG ID
register, but the decoding of the JTAG ID into the SoC information remains
the same.
Split the decode and registration part out of k3_chipinfo_probe() into
k3_socinfo_register() and export it, so that other drivers can register the
SoC device once they have read the JTAG ID through their own access path.
Since the helper can now be called from modular drivers, it must not leak
the SoC device when the caller goes away. Tie the registration to the
calling device with a devm action, which unregisters the SoC device and
frees its attributes on unbind. Callers need no unregister counterpart and
the helper keeps returning an int. If the soc bus is not up yet, the
attributes are only parked for later registration and there is nothing to
undo, so no action is added in that case.
Signed-off-by: Moteen Shah <m-shah@ti.com>
---
drivers/soc/ti/k3-socinfo.c | 101 +++++++++++++++++++++++-------
include/linux/soc/ti/k3-socinfo.h | 11 ++++
2 files changed, 90 insertions(+), 22 deletions(-)
create mode 100644 include/linux/soc/ti/k3-socinfo.h
diff --git a/drivers/soc/ti/k3-socinfo.c b/drivers/soc/ti/k3-socinfo.c
index 48dbef3acceb4..5fae03004e350 100644
--- a/drivers/soc/ti/k3-socinfo.c
+++ b/drivers/soc/ti/k3-socinfo.c
@@ -5,6 +5,8 @@
* Copyright (C) 2020 Texas Instruments Incorporated - http://www.ti.com
*/
+#include <linux/device.h>
+#include <linux/export.h>
#include <linux/mfd/syscon.h>
#include <linux/nvmem-consumer.h>
#include <linux/of.h>
@@ -12,6 +14,7 @@
#include <linux/regmap.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
+#include <linux/soc/ti/k3-socinfo.h>
#include <linux/string.h>
#include <linux/sys_soc.h>
@@ -152,38 +155,44 @@ k3_chipinfo_variant_to_sr(struct device *dev, unsigned int partno,
return -ENODEV;
}
-static const struct regmap_config k3_chipinfo_regmap_cfg = {
- .reg_bits = 32,
- .val_bits = 32,
- .reg_stride = 4,
+struct k3_socinfo {
+ struct soc_device *soc_dev;
+ struct soc_device_attribute *soc_dev_attr;
};
-static int k3_chipinfo_probe(struct platform_device *pdev)
+static void k3_socinfo_unregister(void *data)
+{
+ struct k3_socinfo *info = data;
+
+ soc_device_unregister(info->soc_dev);
+ /* The attributes must only be freed after the unregister. */
+ kfree(info->soc_dev_attr->revision);
+ kfree(info->soc_dev_attr);
+}
+
+/**
+ * k3_socinfo_register() - Decode a JTAG ID and register the SoC device
+ * @dev: device on whose behalf the SoC device is registered
+ * @jtag_id: raw JTAG ID register value read by the caller
+ *
+ * The registration is managed by @dev: the SoC device and its attributes are
+ * released automatically when @dev is unbound, so callers need no matching
+ * unregister call. If the soc bus is not yet available, the registration is
+ * deferred until it is and cannot be released by @dev.
+ *
+ * Return: 0 on success, negative errno on failure.
+ */
+int k3_socinfo_register(struct device *dev, u32 jtag_id)
{
- struct device_node *node = pdev->dev.of_node;
+ struct device_node *node;
+ struct k3_socinfo *info;
struct soc_device_attribute *soc_dev_attr;
- struct device *dev = &pdev->dev;
struct soc_device *soc_dev;
- struct regmap *regmap;
- void __iomem *base;
u32 partno_id;
u32 variant;
- u32 jtag_id;
u32 mfg;
int ret;
- base = devm_platform_ioremap_resource(pdev, 0);
- if (IS_ERR(base))
- return PTR_ERR(base);
-
- regmap = devm_regmap_init_mmio(dev, base, &k3_chipinfo_regmap_cfg);
- if (IS_ERR(regmap))
- return PTR_ERR(regmap);
-
- ret = regmap_read(regmap, CTRLMMR_WKUP_JTAGID_REG, &jtag_id);
- if (ret < 0)
- return ret;
-
mfg = (jtag_id & CTRLMMR_WKUP_JTAGID_MFG_MASK) >>
CTRLMMR_WKUP_JTAGID_MFG_SHIFT;
@@ -198,6 +207,10 @@ static int k3_chipinfo_probe(struct platform_device *pdev)
partno_id = (jtag_id & CTRLMMR_WKUP_JTAGID_PARTNO_MASK) >>
CTRLMMR_WKUP_JTAGID_PARTNO_SHIFT;
+ info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
soc_dev_attr = kzalloc_obj(*soc_dev_attr);
if (!soc_dev_attr)
return -ENOMEM;
@@ -224,6 +237,20 @@ static int k3_chipinfo_probe(struct platform_device *pdev)
goto err_free_rev;
}
+ /*
+ * A NULL handle means the soc bus is not up yet and the attributes
+ * were parked for registration at soc bus init. They must stay
+ * allocated and cannot be unregistered, so there is nothing to undo.
+ */
+ if (soc_dev) {
+ info->soc_dev = soc_dev;
+ info->soc_dev_attr = soc_dev_attr;
+ ret = devm_add_action_or_reset(dev, k3_socinfo_unregister,
+ info);
+ if (ret)
+ return ret;
+ }
+
dev_info(dev, "Family:%s rev:%s JTAGID[0x%08x] Detected\n",
soc_dev_attr->family,
soc_dev_attr->revision, jtag_id);
@@ -236,6 +263,36 @@ static int k3_chipinfo_probe(struct platform_device *pdev)
kfree(soc_dev_attr);
return ret;
}
+EXPORT_SYMBOL_GPL(k3_socinfo_register);
+
+static const struct regmap_config k3_chipinfo_regmap_cfg = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+};
+
+static int k3_chipinfo_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct regmap *regmap;
+ void __iomem *base;
+ u32 jtag_id;
+ int ret;
+
+ base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ regmap = devm_regmap_init_mmio(dev, base, &k3_chipinfo_regmap_cfg);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ ret = regmap_read(regmap, CTRLMMR_WKUP_JTAGID_REG, &jtag_id);
+ if (ret < 0)
+ return ret;
+
+ return k3_socinfo_register(dev, jtag_id);
+}
static const struct of_device_id k3_chipinfo_of_match[] = {
{ .compatible = "ti,am654-chipid", },
diff --git a/include/linux/soc/ti/k3-socinfo.h b/include/linux/soc/ti/k3-socinfo.h
new file mode 100644
index 0000000000000..487e69d0069d1
--- /dev/null
+++ b/include/linux/soc/ti/k3-socinfo.h
@@ -0,0 +1,11 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __LINUX_SOC_TI_K3_SOCINFO_H__
+#define __LINUX_SOC_TI_K3_SOCINFO_H__
+
+#include <linux/types.h>
+
+struct device;
+
+int k3_socinfo_register(struct device *dev, u32 jtag_id);
+
+#endif /* __LINUX_SOC_TI_K3_SOCINFO_H__ */
--
2.34.1
^ permalink raw reply [flat|nested] 6+ messages in thread* Re: [PATCH 2/3] soc: ti: k3-socinfo: Export SoC registration helper
2026-10-19 8:07 ` [PATCH 2/3] soc: ti: k3-socinfo: Export SoC registration helper Moteen Shah
@ 2026-10-19 17:38 ` Andrew Davis
0 siblings, 0 replies; 6+ messages in thread
From: Andrew Davis @ 2026-10-19 17:38 UTC (permalink / raw)
To: Moteen Shah, conor+dt, robh, krzk+dt, linux-kernel, nm, ssantosh,
linux-arm-kernel
Cc: u-kumar1, gehariprasath, vigneshr, a-limaye, y-abhilashchandra
On 10/19/26 3:07 AM, Moteen Shah wrote:
> The k3-socinfo driver reads the JTAG ID through a dedicated MMIO region and
> then decodes it and registers the SoC device.
>
> Newer TI SoCs like TDA54 use a different path for accessing the JTAG ID
> register, but the decoding of the JTAG ID into the SoC information remains
> the same.
>
> Split the decode and registration part out of k3_chipinfo_probe() into
> k3_socinfo_register() and export it, so that other drivers can register the
> SoC device once they have read the JTAG ID through their own access path.
>
> Since the helper can now be called from modular drivers, it must not leak
> the SoC device when the caller goes away. Tie the registration to the
> calling device with a devm action, which unregisters the SoC device and
> frees its attributes on unbind. Callers need no unregister counterpart and
> the helper keeps returning an int. If the soc bus is not up yet, the
> attributes are only parked for later registration and there is nothing to
> undo, so no action is added in that case.
>
> Signed-off-by: Moteen Shah <m-shah@ti.com>
> ---
> drivers/soc/ti/k3-socinfo.c | 101 +++++++++++++++++++++++-------
> include/linux/soc/ti/k3-socinfo.h | 11 ++++
> 2 files changed, 90 insertions(+), 22 deletions(-)
> create mode 100644 include/linux/soc/ti/k3-socinfo.h
>
> diff --git a/drivers/soc/ti/k3-socinfo.c b/drivers/soc/ti/k3-socinfo.c
> index 48dbef3acceb4..5fae03004e350 100644
> --- a/drivers/soc/ti/k3-socinfo.c
> +++ b/drivers/soc/ti/k3-socinfo.c
> @@ -5,6 +5,8 @@
> * Copyright (C) 2020 Texas Instruments Incorporated - http://www.ti.com
> */
>
> +#include <linux/device.h>
> +#include <linux/export.h>
> #include <linux/mfd/syscon.h>
> #include <linux/nvmem-consumer.h>
> #include <linux/of.h>
> @@ -12,6 +14,7 @@
> #include <linux/regmap.h>
> #include <linux/platform_device.h>
> #include <linux/slab.h>
> +#include <linux/soc/ti/k3-socinfo.h>
> #include <linux/string.h>
> #include <linux/sys_soc.h>
>
> @@ -152,38 +155,44 @@ k3_chipinfo_variant_to_sr(struct device *dev, unsigned int partno,
> return -ENODEV;
> }
>
> -static const struct regmap_config k3_chipinfo_regmap_cfg = {
> - .reg_bits = 32,
> - .val_bits = 32,
> - .reg_stride = 4,
> +struct k3_socinfo {
> + struct soc_device *soc_dev;
> + struct soc_device_attribute *soc_dev_attr;
> };
>
> -static int k3_chipinfo_probe(struct platform_device *pdev)
> +static void k3_socinfo_unregister(void *data)
> +{
> + struct k3_socinfo *info = data;
> +
> + soc_device_unregister(info->soc_dev);
> + /* The attributes must only be freed after the unregister. */
> + kfree(info->soc_dev_attr->revision);
> + kfree(info->soc_dev_attr);
> +}
> +
> +/**
> + * k3_socinfo_register() - Decode a JTAG ID and register the SoC device
> + * @dev: device on whose behalf the SoC device is registered
> + * @jtag_id: raw JTAG ID register value read by the caller
> + *
> + * The registration is managed by @dev: the SoC device and its attributes are
> + * released automatically when @dev is unbound, so callers need no matching
> + * unregister call. If the soc bus is not yet available, the registration is
> + * deferred until it is and cannot be released by @dev.
> + *
> + * Return: 0 on success, negative errno on failure.
> + */
> +int k3_socinfo_register(struct device *dev, u32 jtag_id)
> {
> - struct device_node *node = pdev->dev.of_node;
> + struct device_node *node;
> + struct k3_socinfo *info;
> struct soc_device_attribute *soc_dev_attr;
> - struct device *dev = &pdev->dev;
> struct soc_device *soc_dev;
> - struct regmap *regmap;
> - void __iomem *base;
> u32 partno_id;
> u32 variant;
> - u32 jtag_id;
> u32 mfg;
> int ret;
>
> - base = devm_platform_ioremap_resource(pdev, 0);
> - if (IS_ERR(base))
> - return PTR_ERR(base);
> -
> - regmap = devm_regmap_init_mmio(dev, base, &k3_chipinfo_regmap_cfg);
> - if (IS_ERR(regmap))
> - return PTR_ERR(regmap);
> -
> - ret = regmap_read(regmap, CTRLMMR_WKUP_JTAGID_REG, &jtag_id);
> - if (ret < 0)
> - return ret;
> -
> mfg = (jtag_id & CTRLMMR_WKUP_JTAGID_MFG_MASK) >>
> CTRLMMR_WKUP_JTAGID_MFG_SHIFT;
>
> @@ -198,6 +207,10 @@ static int k3_chipinfo_probe(struct platform_device *pdev)
> partno_id = (jtag_id & CTRLMMR_WKUP_JTAGID_PARTNO_MASK) >>
> CTRLMMR_WKUP_JTAGID_PARTNO_SHIFT;
>
> + info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL);
> + if (!info)
> + return -ENOMEM;
> +
> soc_dev_attr = kzalloc_obj(*soc_dev_attr);
> if (!soc_dev_attr)
> return -ENOMEM;
> @@ -224,6 +237,20 @@ static int k3_chipinfo_probe(struct platform_device *pdev)
> goto err_free_rev;
> }
>
> + /*
> + * A NULL handle means the soc bus is not up yet and the attributes
> + * were parked for registration at soc bus init. They must stay
> + * allocated and cannot be unregistered, so there is nothing to undo.
This seems really messy, the only time drivers need to deal with this
early_soc_dev_attr hackyness is if they can register before the soc bus,
which only happens for drivers using early_initcall(), but that is
not the case for either driver using k3_socinfo_register().
Can you explain why you added all this?
Otherwise just allocate soc_dev_attr using devm, k3_socinfo_unregister()
action can be added unconditionally, and only needs to call
soc_device_unregister().
Andrew
> + */
> + if (soc_dev) {
> + info->soc_dev = soc_dev;
> + info->soc_dev_attr = soc_dev_attr;
> + ret = devm_add_action_or_reset(dev, k3_socinfo_unregister,
> + info);
> + if (ret)
> + return ret;
> + }
> +
> dev_info(dev, "Family:%s rev:%s JTAGID[0x%08x] Detected\n",
> soc_dev_attr->family,
> soc_dev_attr->revision, jtag_id);
> @@ -236,6 +263,36 @@ static int k3_chipinfo_probe(struct platform_device *pdev)
> kfree(soc_dev_attr);
> return ret;
> }
> +EXPORT_SYMBOL_GPL(k3_socinfo_register);
> +
> +static const struct regmap_config k3_chipinfo_regmap_cfg = {
> + .reg_bits = 32,
> + .val_bits = 32,
> + .reg_stride = 4,
> +};
> +
> +static int k3_chipinfo_probe(struct platform_device *pdev)
> +{
> + struct device *dev = &pdev->dev;
> + struct regmap *regmap;
> + void __iomem *base;
> + u32 jtag_id;
> + int ret;
> +
> + base = devm_platform_ioremap_resource(pdev, 0);
> + if (IS_ERR(base))
> + return PTR_ERR(base);
> +
> + regmap = devm_regmap_init_mmio(dev, base, &k3_chipinfo_regmap_cfg);
> + if (IS_ERR(regmap))
> + return PTR_ERR(regmap);
> +
> + ret = regmap_read(regmap, CTRLMMR_WKUP_JTAGID_REG, &jtag_id);
> + if (ret < 0)
> + return ret;
> +
> + return k3_socinfo_register(dev, jtag_id);
> +}
>
> static const struct of_device_id k3_chipinfo_of_match[] = {
> { .compatible = "ti,am654-chipid", },
> diff --git a/include/linux/soc/ti/k3-socinfo.h b/include/linux/soc/ti/k3-socinfo.h
> new file mode 100644
> index 0000000000000..487e69d0069d1
> --- /dev/null
> +++ b/include/linux/soc/ti/k3-socinfo.h
> @@ -0,0 +1,11 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +#ifndef __LINUX_SOC_TI_K3_SOCINFO_H__
> +#define __LINUX_SOC_TI_K3_SOCINFO_H__
> +
> +#include <linux/types.h>
> +
> +struct device;
> +
> +int k3_socinfo_register(struct device *dev, u32 jtag_id);
> +
> +#endif /* __LINUX_SOC_TI_K3_SOCINFO_H__ */
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH 3/3] soc: ti: Add TDA54 SCM configuration syscon support
2026-10-19 8:07 [PATCH 0/3] soc: ti: Add TDA54 SCM configuration syscon support Moteen Shah
2026-10-19 8:07 ` [PATCH 1/3] dt-bindings: soc: ti: Document the ti,tda54-mcu-scm-conf binding Moteen Shah
2026-10-19 8:07 ` [PATCH 2/3] soc: ti: k3-socinfo: Export SoC registration helper Moteen Shah
@ 2026-10-19 8:07 ` Moteen Shah
2 siblings, 0 replies; 6+ messages in thread
From: Moteen Shah @ 2026-10-19 8:07 UTC (permalink / raw)
To: conor+dt, robh, krzk+dt, linux-kernel, nm, ssantosh, linux-arm-kernel
Cc: u-kumar1, gehariprasath, m-shah, vigneshr, afd, a-limaye,
y-abhilashchandra
Add the TDA54 SCM configuration driver providing access to the SCM register
space through regmap/syscon.
The driver handles proxy-based register access and SCM unlock/claim
handling, and registers TDA54 SoC information using the common K3 SoC
info helper exported by k3-socinfo, for which the TDA54 part number is
added to the SoC ID table.
Default the option to m on ARCH_K3, so that the driver gets built for TDA54
boards without requiring a defconfig change.
Signed-off-by: Moteen Shah <m-shah@ti.com>
Signed-off-by: Aniket Limaye <a-limaye@ti.com>
---
drivers/soc/ti/Kconfig | 16 +++
drivers/soc/ti/Makefile | 1 +
drivers/soc/ti/k3-socinfo.c | 2 +
drivers/soc/ti/tda54-mcu-sysconf.c | 216 +++++++++++++++++++++++++++++
4 files changed, 235 insertions(+)
create mode 100644 drivers/soc/ti/tda54-mcu-sysconf.c
diff --git a/drivers/soc/ti/Kconfig b/drivers/soc/ti/Kconfig
index 163aadd589d3a..ea5da6722da29 100644
--- a/drivers/soc/ti/Kconfig
+++ b/drivers/soc/ti/Kconfig
@@ -82,6 +82,22 @@ config TI_PRUSS
processors on various TI SoCs. It's safe to say N here if you're
not interested in the PRU or if you are unsure.
+config TI_TDA54_MCU_SYSCONF
+ tristate "TI TDA54 SCM configuration controller"
+ depends on ARCH_K3 || COMPILE_TEST
+ depends on OF
+ default m if ARCH_K3
+ select MFD_SYSCON
+ select TI_K3_SOCINFO
+ help
+ Support the SCM configuration controller found on TI TDA54.
+ The controller provides a syscon regmap and is also the
+ hardware access path used to obtain and register K3 SoC
+ identification information.
+
+ This driver can be built as a module, in which case the module
+ will be called tda54-mcu-sysconf.
+
endif # SOC_TI
config TI_SCI_INTA_MSI_DOMAIN
diff --git a/drivers/soc/ti/Makefile b/drivers/soc/ti/Makefile
index cb800a745e66d..33f4ddca32ed6 100644
--- a/drivers/soc/ti/Makefile
+++ b/drivers/soc/ti/Makefile
@@ -11,4 +11,5 @@ obj-$(CONFIG_TI_SCI_INTA_MSI_DOMAIN) += ti_sci_inta_msi.o
obj-$(CONFIG_TI_K3_RINGACC) += k3-ringacc.o
obj-$(CONFIG_TI_K3_SOCINFO) += k3-socinfo.o
obj-$(CONFIG_TI_PRUSS) += pruss.o
+obj-$(CONFIG_TI_TDA54_MCU_SYSCONF) += tda54-mcu-sysconf.o
obj-$(CONFIG_POWER_AVS_OMAP) += smartreflex.o
diff --git a/drivers/soc/ti/k3-socinfo.c b/drivers/soc/ti/k3-socinfo.c
index 5fae03004e350..9869dd6358d1f 100644
--- a/drivers/soc/ti/k3-socinfo.c
+++ b/drivers/soc/ti/k3-socinfo.c
@@ -50,6 +50,7 @@
#define JTAG_ID_PARTNO_AM62PX 0xBB9D
#define JTAG_ID_PARTNO_J722S 0xBBA0
#define JTAG_ID_PARTNO_AM62LX 0xBBA7
+#define JTAG_ID_PARTNO_TDA54 0xBBC1
static const struct k3_soc_id {
unsigned int id;
@@ -66,6 +67,7 @@ static const struct k3_soc_id {
{ JTAG_ID_PARTNO_AM62PX, "AM62PX" },
{ JTAG_ID_PARTNO_J722S, "J722S" },
{ JTAG_ID_PARTNO_AM62LX, "AM62LX" },
+ { JTAG_ID_PARTNO_TDA54, "TDA54" },
};
static const char * const j721e_rev_string_map[] = {
diff --git a/drivers/soc/ti/tda54-mcu-sysconf.c b/drivers/soc/ti/tda54-mcu-sysconf.c
new file mode 100644
index 0000000000000..8598080f6c48e
--- /dev/null
+++ b/drivers/soc/ti/tda54-mcu-sysconf.c
@@ -0,0 +1,216 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * TDA54 SCM Configuration syscon driver
+ *
+ * Copyright (C) 2026 Texas Instruments Incorporated - https://www.ti.com/
+ */
+#include <linux/bitfield.h>
+#include <linux/bitops.h>
+#include <linux/io.h>
+#include <linux/mfd/syscon.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/soc/ti/k3-socinfo.h>
+#include <linux/types.h>
+
+/* Size of a single proxy window into the scm_conf register space */
+#define TDA54_SCM_CONF_PROXY_STRIDE 0x4000
+
+#define TDA54_JTAG_ID_REG_OFFSET 0x14
+
+#define TDA54_SCM_CONF_LOCK_KICK0_UNLOCK_VAL 0x68EF3490
+#define TDA54_SCM_CONF_LOCK_KICK1_UNLOCK_VAL 0xD172BC5A
+
+/*
+ * Claim registers start at 0x3100. Claim register i holds the claim bit
+ * for each of the 32 registers in the (i * TDA54_SCM_CONF_CLAIM_BLOCK_SIZE)
+ * .. (i * TDA54_SCM_CONF_CLAIM_BLOCK_SIZE + 0x7c) block, one bit per
+ * register, i.e. bit b of claim register i corresponds to the register at
+ * offset i * TDA54_SCM_CONF_CLAIM_BLOCK_SIZE + b * 4.
+ */
+#define TDA54_SCM_CONF_CLAIM_REG_STRIDE 0x4
+#define TDA54_SCM_CONF_CLAIM_REGS_PER_WORD 32
+#define TDA54_SCM_CONF_CLAIM_BLOCK_SIZE \
+ (TDA54_SCM_CONF_CLAIM_REGS_PER_WORD * 0x4)
+
+/* Per-SoC register offset data (platform data) */
+struct tda54_scm_conf_data {
+ u32 lock_offset;
+ u32 claim_offset;
+ u32 claim_status_offset;
+};
+
+static const struct tda54_scm_conf_data tda54_scm_conf_data = {
+ .lock_offset = 0x3008,
+ .claim_offset = 0x3100,
+ .claim_status_offset = 0x3300,
+};
+
+struct tda54_scm_conf {
+ void __iomem *base;
+ u32 proxy;
+ const struct tda54_scm_conf_data *pdata;
+};
+
+/*
+ * Make @reg writable through this proxy.
+ *
+ * Proxy 0 is the default access proxy for all unclaimed registers. Its claim
+ * bits are tied low, so it cannot claim anything and needs no claim to write;
+ * it can write @reg unless another proxy has claimed it, which is reported by
+ * the claim status.
+ *
+ * Any other proxy has to claim @reg first. Registers claimed by a proxy can no
+ * longer be written through any other proxy's address, until that proxy
+ * releases the claim. The claim status is set once any proxy owns @reg, so
+ * read the claim register back to check that the claim is held by this proxy.
+ */
+static int tda54_scm_conf_claim(struct tda54_scm_conf *scm, unsigned int reg)
+{
+ unsigned int block = reg / TDA54_SCM_CONF_CLAIM_BLOCK_SIZE;
+ unsigned int bit = (reg % TDA54_SCM_CONF_CLAIM_BLOCK_SIZE) /
+ TDA54_SCM_CONF_CLAIM_REG_STRIDE;
+ unsigned int claim_reg = scm->pdata->claim_offset +
+ block * TDA54_SCM_CONF_CLAIM_REG_STRIDE;
+ unsigned int status_reg = scm->pdata->claim_status_offset +
+ block * TDA54_SCM_CONF_CLAIM_REG_STRIDE;
+ u32 val;
+
+ if (!scm->proxy) {
+ if (readl(scm->base + status_reg) & BIT(bit))
+ return -EBUSY;
+
+ return 0;
+ }
+
+ val = readl(scm->base + claim_reg);
+ val |= BIT(bit);
+ writel(val, scm->base + claim_reg);
+
+ if (!(readl(scm->base + claim_reg) & BIT(bit)))
+ return -EBUSY;
+
+ return 0;
+}
+
+static int tda54_scm_conf_reg_read(void *context, unsigned int reg,
+ unsigned int *val)
+{
+ struct tda54_scm_conf *scm = context;
+
+ *val = readl(scm->base + reg);
+
+ return 0;
+}
+
+static int tda54_scm_conf_reg_write(void *context, unsigned int reg,
+ unsigned int val)
+{
+ struct tda54_scm_conf *scm = context;
+ int ret;
+
+ ret = tda54_scm_conf_claim(scm, reg);
+ if (ret)
+ return ret;
+
+ writel(val, scm->base + reg);
+
+ return 0;
+}
+
+static const struct regmap_config tda54_scm_conf_regmap_cfg = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .reg_read = tda54_scm_conf_reg_read,
+ .reg_write = tda54_scm_conf_reg_write,
+};
+
+static int tda54_scm_conf_register_socinfo(struct device *dev,
+ struct regmap *regmap)
+{
+ u32 jtag_id;
+ int ret;
+
+ ret = regmap_read(regmap, TDA54_JTAG_ID_REG_OFFSET, &jtag_id);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to read JTAG ID\n");
+
+ return k3_socinfo_register(dev, jtag_id);
+}
+
+static int tda54_scm_conf_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct tda54_scm_conf *scm;
+ struct regmap *regmap;
+ void __iomem *base;
+ int ret;
+
+ scm = devm_kzalloc(dev, sizeof(*scm), GFP_KERNEL);
+ if (!scm)
+ return -ENOMEM;
+
+ base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ if (of_property_read_u32(dev->of_node, "ti,proxy-id", &scm->proxy))
+ return dev_err_probe(dev, -ENODATA, "missing ti,proxy-id property\n");
+
+ if (scm->proxy >= 8)
+ return dev_err_probe(dev, -EINVAL,
+ "ti,proxy-id must be between 0 and 7\n");
+
+ scm->base = base + scm->proxy * TDA54_SCM_CONF_PROXY_STRIDE;
+
+ /* Get register offsets from driver data (matched by compatible) */
+ scm->pdata = of_device_get_match_data(dev);
+
+ regmap = devm_regmap_init(dev, NULL, scm, &tda54_scm_conf_regmap_cfg);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ writel(TDA54_SCM_CONF_LOCK_KICK0_UNLOCK_VAL, scm->base + scm->pdata->lock_offset);
+ writel(TDA54_SCM_CONF_LOCK_KICK1_UNLOCK_VAL, scm->base + scm->pdata->lock_offset + 4);
+
+ /*
+ * There is no way to unregister a syscon regmap, so publish it only
+ * once nothing else in probe can fail.
+ */
+ ret = of_syscon_register_regmap(dev->of_node, regmap);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to register syscon regmap\n");
+
+ /*
+ * SoC identification is informational, so a failure here must not fail
+ * the probe. It is registered last and released through devres.
+ */
+ ret = tda54_scm_conf_register_socinfo(dev, regmap);
+ if (ret)
+ dev_warn(dev, "SoC info not registered: %d\n", ret);
+
+ return 0;
+}
+
+static const struct of_device_id tda54_scm_conf_of_match[] = {
+ { .compatible = "ti,tda54-mcu-scm-conf", .data = &tda54_scm_conf_data },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, tda54_scm_conf_of_match);
+
+static struct platform_driver tda54_scm_conf_driver = {
+ .probe = tda54_scm_conf_probe,
+ .driver = {
+ .name = "k3-tda54-scm-conf",
+ .of_match_table = tda54_scm_conf_of_match,
+ .suppress_bind_attrs = true,
+ },
+};
+
+module_platform_driver(tda54_scm_conf_driver);
+
+MODULE_AUTHOR("Moteen Shah <m-shah@ti.com>");
+MODULE_DESCRIPTION("TDA54 SCM Configuration syscon driver");
+MODULE_LICENSE("GPL");
--
2.34.1
^ permalink raw reply [flat|nested] 6+ messages in thread