From: "Sharma, Rahul" <r-sharma3@ti.com>
To: Peter Rosin <peda@lysator.liu.se>
Cc: robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org,
devicetree@vger.kernel.org, linux-kernel@vger.kernel.org,
vigneshr@ti.com, "Alvin Šipraga" <alvin.sipraga@analog.com>
Subject: Re: [EXTERNAL] Re: [PATCH 2/2] mux-controller: ti: add driver for event mux router
Date: Fri, 4 Sep 2026 00:46:48 +0530 [thread overview]
Message-ID: <bb67e942-ff5e-421c-8556-18a9eb18be22@ti.com> (raw)
In-Reply-To: <apL3StZuqeV-gaIa@gryt>
Hi Peter,
Thanks for detailed review.
On 8/29/2026 8:44 PM, Peter Rosin wrote:
> Hi Rahul, I believe you posted something like this as an RFC about half
> a year ago? Please include a pointer to the previous posting along with
> some notes about what changed since the last posting when you update a
> patch series. Thanks! And
>
>
> Hi Rahul,
>
> I believe you posted something like this as an RFC about half a year
> ago? Please include a pointer to the previous posting along with some
> notes about what changed since the last posting when you update a
> patch series. Thanks!
I will follow this with v2. For now I am adding the RFC link below,
https://lore.kernel.org/lkml/20260313060437.3704592-1-r-sharma3@ti.com
Not much has changed since RFC was posted first. In the binding doc
there was a term "syscon" which got added mistakenly, is now removed.
Event mux router is a complete IP block and not a syscon node, rest of
the content is same.
Due to lack of comments in RFC, this time I posted as fresh patch
series. That was anyhow the original plan.
>
> And sorry for being so very slow with the review. I intend to be more
> responsive going forward...
>
> Den Fri, Aug 28, 2026 at 03:36:15PM +0530, skrev Rahul Sharma:
>> The driver supports event muxing routers like gpio mux router and timesync
>> router. This driver is adaptation of original reg-mux driver, along with
>> changes specific to support TI's mux router.
>>
>> The idle states this driver supports are only 2 which active(represented
>> by 1 in dt-node) and in-active(represented by 0 in dt-node).
>
> I don't see the point of using 1 as idle state. Why would anyone
> do that?
>
>>
>> Signed-off-by: Rahul Sharma <r-sharma3@ti.com>
>> ---
>> drivers/mux/Kconfig | 15 +++
>> drivers/mux/Makefile | 2 +
>> drivers/mux/ti-k3-event-mux.c | 235 ++++++++++++++++++++++++++++++++++
>> 3 files changed, 252 insertions(+)
>> create mode 100644 drivers/mux/ti-k3-event-mux.c
>>
>> diff --git a/drivers/mux/Kconfig b/drivers/mux/Kconfig
>> index eb34457beaab..58739a01f0d5 100644
>> --- a/drivers/mux/Kconfig
>> +++ b/drivers/mux/Kconfig
>> @@ -83,6 +83,21 @@ config MUX_RZV2H_VBENCTL
>> To compile the driver as a module, choose M here: the module will
>> be called mux-rzv2h-vbenctl.
>>
>> +config MUX_TI_K3_EVENT_ROUTER
>> + tristate "TI Event Mux Router using MMIO registers"
>> + depends on OF && (REGMAP_MMIO || COMPILE_TEST)
>> + help
>> + This is extension of MMIO mux for timesync router and gpiomux
>> + routers on TI K3 SoCs. This driver supports the 3-field format for
>> + mux control: <register-offset mask value>.
>
> The first sentence has some grammar issues, and the second talks
> about "the 3-field format" as if that is some well established format.
I just mentioned because it is different from standard Key-Value pairs :) .
> How about:
>
> This is a mux for timesync and gpiomux routers on TI K3 SoCs.
> The driver supports a 3-field format for mux control:
> <register-offset mask value>.
>
>> +
>> + The driver allows configuration of hardware mux routers using
>> + memory-mapped registers. It's based on the mmio-mux driver but
>> + supports the extended 3-field format for more precise control.
>
> The person reading this would not care about the code ancestry, that
> info belongs elsewhere, and the 3-field format has already been
> mentioned. Thus, the second sentence can be dropped.
>
>> +
>> + To compile the driver as a module, choose M here: the module will
>> + be called mux-ti-k3-event.
>> +
>> endmenu
>>
>> endif # MULTIPLEXER
>> diff --git a/drivers/mux/Makefile b/drivers/mux/Makefile
>> index 0854c04613b9..114abf88b75e 100644
>> --- a/drivers/mux/Makefile
>> +++ b/drivers/mux/Makefile
>> @@ -9,6 +9,7 @@ mux-adgs1408-objs := adgs1408.o
>> mux-gpio-objs := gpio.o
>> mux-mmio-objs := mmio.o
>> mux-rzv2h-vbenctl-objs := rzv2h-vbenctl.o
>> +mux-ti-k3-event-objs := ti-k3-event-mux.o
>>
>> obj-$(CONFIG_MULTIPLEXER) += mux-core.o
>> obj-$(CONFIG_MUX_ADG792A) += mux-adg792a.o
>> @@ -16,3 +17,4 @@ obj-$(CONFIG_MUX_ADGS1408) += mux-adgs1408.o
>> obj-$(CONFIG_MUX_GPIO) += mux-gpio.o
>> obj-$(CONFIG_MUX_MMIO) += mux-mmio.o
>> obj-$(CONFIG_MUX_RZV2H_VBENCTL) += mux-rzv2h-vbenctl.o
>> +obj-$(CONFIG_MUX_TI_K3_EVENT_ROUTER) += mux-ti-k3-event.o
>> diff --git a/drivers/mux/ti-k3-event-mux.c b/drivers/mux/ti-k3-event-mux.c
>> new file mode 100644
>> index 000000000000..2469500d1b48
>> --- /dev/null
>> +++ b/drivers/mux/ti-k3-event-mux.c
>> @@ -0,0 +1,235 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * MMIO register bit-field controlled multiplexer driver
>
> This is some left-over I presume?
>
>> + *
>> + * Copyright (C) 2026 Texas Instruments Incorporated - https://www.ti.com
>> + *
>> + * Based on drivers/mux/mmio.c by Philipp Zabel <kernel@pengutronix.de>
>
> So, why did you drop the Pengutronix copyright?
>
>> + * Modified to support 3-field format: reg-offset, mask & value
>> + *
>> + * Author: Rahul Sharma <r-sharma3@ti.com>
>> + */
>> +
>> +#include <linux/bitops.h>
>> +#include <linux/err.h>
>> +#include <linux/mfd/syscon.h>
>> +#include <linux/module.h>
>> +#include <linux/mux/driver.h>
>> +#include <linux/of.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/property.h>
>> +#include <linux/regmap.h>
>> +
>> +#define MUX_ENABLE_INTR BIT(16)
>> +
>> +struct mux_ti_k3_event {
>> + struct regmap *regmap;
>
> I think this can be a single pointer in the below chip struct
> instead of "wasting" one pointer for each field, no?
>
>> + u32 reg;
>> + u32 mask;
>> + u32 value;
>> +};
>> +
>> +struct mux_ti_k3_event_chip {
>> + struct mux_chip *mux_chip;
>
> I do not see the need for this back-pointer to the mux chip.
>
>> + struct mux_ti_k3_event *fields;
>> + int num_fields;
>
> This is redundant. The mux-control count for a chip is available
> as mux_chip->controllers.
>
>> + u32 *saved_states;
>> +};
>> +
>> +static int mux_ti_k3_event_suspend(struct device *dev)
>> +{
>> + struct mux_ti_k3_event_chip *chip = dev_get_drvdata(dev);
>> + int i, ret;
>> +
>> + if (!chip->saved_states) {
>> + chip->saved_states = devm_kcalloc(dev, chip->num_fields,
>> + sizeof(u32), GFP_KERNEL);
>
> Why not allocate this up-front during probe? Or, on second thought, why
> not just add a saved_state (non-array) member to struct mux_ti_k3_event?
>
>> + if (!chip->saved_states)
>> + return -ENOMEM;
>> + }
>> +
>> + for (i = 0; i < chip->num_fields; i++) {
>> + struct mux_ti_k3_event *field = &chip->fields[i];
>> +
>> + ret = regmap_read(field->regmap, field->reg,
>> + &chip->saved_states[i]);
>> + if (ret)
>> + return ret;
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +static int mux_ti_k3_event_resume(struct device *dev)
>> +{
>> + struct mux_ti_k3_event_chip *chip = dev_get_drvdata(dev);
>> + int i, ret;
>> +
>> + if (!chip->saved_states)
>> + return 0;
>> +
>> + for (i = 0; i < chip->num_fields; i++) {
>> + struct mux_ti_k3_event *field = &chip->fields[i];
>> +
>> + ret = regmap_write(field->regmap, field->reg,
>> + chip->saved_states[i]);
>
> Should you not apply the mask here?
>
>> + if (ret)
>> + return ret;
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +static DEFINE_SIMPLE_DEV_PM_OPS(mux_ti_k3_event_pm_ops,
>> + mux_ti_k3_event_suspend,
>> + mux_ti_k3_event_resume);
>> +
>> +/*
>> + * State behavior:
>> + * - state 0: Clears the mask bits in the target register (inactive state)
>> + * - state 1: Sets both the value bits and enable bit (bit 16) in the register
>> + */
>> +static int mux_ti_k3_event_set(struct mux_control *mux, int state)
>> +{
>> + struct mux_ti_k3_event *fields = mux_chip_priv(mux->chip);
>> + struct mux_ti_k3_event *field = &fields[mux_control_get_index(mux)];
>> +
>> + if (!state)
>> + return regmap_update_bits(field->regmap, field->reg, field->mask, 0);
>
> This is confusing to me. Why do you elect to leave the enable bit as-is
> and write only the zero value? Would it not be saner to clear out the
> enable bit as well?
>
> You should handle state == MUX_IDLE_DISCONNECT here in the .set function,
> see below for rationale.
>
>> +
>> + return regmap_update_bits(field->regmap, field->reg, field->mask | MUX_ENABLE_INTR,
>> + field->value | MUX_ENABLE_INTR);
>> +}
>> +
>> +static const struct mux_control_ops mux_ti_k3_event_ops = {
>> + .set = mux_ti_k3_event_set,
>> +};
>> +
>> +static const struct regmap_config mux_ti_k3_event_regmap_cfg = {
>> + .reg_bits = 32,
>> + .val_bits = 32,
>> + .reg_stride = 4,
>> +};
>> +
>> +static int mux_ti_k3_event_probe(struct platform_device *pdev)
>> +{
>> + struct device *dev = &pdev->dev;
>> + struct device_node *np = dev->of_node;
>> + struct mux_ti_k3_event_chip *chip;
>> + struct mux_ti_k3_event *fields;
>> + struct mux_chip *mux_chip;
>> + struct regmap *regmap;
>> + void __iomem *base;
>> + int num_fields;
>> + int ret;
>> + int i;
>> +
>> + chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
>> + if (!chip)
>> + return -ENOMEM;
>> +
>> + base = devm_platform_ioremap_resource(pdev, 0);
>> + if (IS_ERR(base)) {
>> + return dev_err_probe(dev, -ENODEV,
>> + "failed to get base address\n");
>> + } else {
>
> Drop the else + indentation when the if-block always returns.
>
>> + regmap = devm_regmap_init_mmio(dev, base, &mux_ti_k3_event_regmap_cfg);
>> + }
>> + if (IS_ERR(regmap)) {
>> + iounmap(base);
>
> Why do you need to explicitely unmap base?
>
>> + return dev_err_probe(dev, PTR_ERR(regmap),
>> + "failed to get regmap\n");
>> + }
>> +
>> + ret = of_property_count_u32_elems(np, "ti,reg-mask-val");
>> + if (!ret || ret % 3) {
>> + ret = -EINVAL;
>> + dev_err(dev, "ti,reg-mask-val property missing or invalid: %d\n",
>> + ret);
>> + return ret;
>> + }
>> +
>> + num_fields = ret / 3;
>> + mux_chip = devm_mux_chip_alloc(dev, num_fields, num_fields *
>> + sizeof(*fields));
>> + if (IS_ERR(mux_chip))
>> + return PTR_ERR(mux_chip);
>> +
>> + fields = mux_chip_priv(mux_chip);
>> + chip->mux_chip = mux_chip;
>
> This feels backwards to me. Should not "chip" be what is returned
> from mux_chip_priv()? I.e. something like this:
>
> mux_chip = devm_mux_chip_alloc(dev, num_fields, sizeof(*chip));
> chip = mux_chip_priv(mux_chip);
> chip->fields = devm_kcalloc(dev, num_fields, sizeof...
>
> (error checking omitted)
>
>> + chip->fields = fields;
>> + chip->num_fields = num_fields;
>> +
>> + platform_set_drvdata(pdev, chip);
>
> I think you should use mux_chip as drvdata. I assume this is why you
> needed the back pointer to the mux_chip at some point.
>
>> +
>> + for (i = 0; i < num_fields; i++) {
>> + struct mux_control *mux = &mux_chip->mux[i];
>> + s32 idle_state = MUX_IDLE_AS_IS;
>> + u32 reg, mask, value;
>> +
>> + ret = of_property_read_u32_index(np, "ti,reg-mask-val",
>> + 3 * i, ®);
>> + if (!ret)
>> + ret = of_property_read_u32_index(np, "ti,reg-mask-val",
>> + 3 * i + 1, &mask);
>> + if (!ret)
>> + ret = of_property_read_u32_index(np, "ti,reg-mask-val",
>> + 3 * i + 2, &value);
>> + if (ret < 0) {
>> + dev_err(dev, "field %d: failed to read ti,reg-mask-val property: %d\n",
>> + i, ret);
>> + return ret;
>> + }
>> +
>> + /* Validate that value bits are within mask */
>> + if (value & ~mask) {
>
> This is broken and only works as expected if "mask" is a bitfield
> based at the lsb. You should keep the limitation from the mmio
> driver that "mask" has to be a proper field (without holes)
> and you should shift things such that "value" is what will be
> written to that field, and not what will be written to the whole
> register.
>
>> + dev_err(dev, "field %d: value 0x%x has bits outside mask 0x%x\n",
>> + i, value, mask);
>> + return -EINVAL;
>> + }
>
> I think you should also check that the mask does not clobber
> the enable bit.
>
>> +
>> + fields[i].regmap = regmap;
>> + fields[i].reg = reg;
>> + fields[i].mask = mask;
>> + fields[i].value = value;
>> +
>> + /* This driver supports binary mux (2 states: 0 and active) */
>> + mux->states = 2;
>
> This is weird. You apparently only have one leg on these muxes,
> and need to turn them on/off with a MUX_IDLE_DISCONNECT idle
> state instead of abusing an extra state that can never be used
> as an actual valid state.
>
> I.e. these things are not really muxes at all, they are more
> like gates, methinks.
>
> And all this indicate that the idle state handling below is
> completely bogus. The only sane idle-state with the current
> patch is zero. So, why require the user to fill that in?
> Why not force it instead? But see above, the idle state
> should not be forced to zero but to MUX_IDLE_DISCONNECT and
> state zero should be the only state and the state that "opens
> the gate" when selected.
>
I will work on the idle state improvements and masks usage, thanks again.
> With all that said, I worry about what happens if you write
> other values in the reg-field? Since you have added this as
> a mux driver when you really have implemented gates, I have
> this feeling that the hw spec calls these registers muxes
> and that they can be used to wire vastly different things
> together. If so, what if you need some of these other values
> in the register? I don't know where to look and have not gone
> trawling the TI site for details, do you perhaps have some
> reference for how these registers work?
>
The TRM for this IP block is having better block diagrams. I can't paste
them here but below is the github link that shows ASCII equivalent block
diagram for event-mux-router IP block, this is the best I could get.
https://github.com/lucifer-9852/linux/commit/aed1746e411a9f0ad7824f4e4a7121e6f56a4ef2
For detailed view of IP you can refer Section 10.2 and 10.2.1 of TRM
https://www.ti.com/lit/pdf/sprujb4
So, from the above diagrams you can see that it is indeed a mux block
instead of a gate, why ? Because there are mux registers corresponding
to each output lines, and they do the job of selection lines in the mux.
Here, the mux type is not many-to-one but instead many-to-few. The mux
register holds the index value of input lines, which are fixed in count.
BR,
Rahul
> Cheers,
> Peter
>
>> +
>> + of_property_read_u32_index(np, "idle-states", i,
>> + (u32 *)&idle_state);
>> + if (idle_state != MUX_IDLE_AS_IS) {
>> + if (idle_state < 0 || idle_state >= mux->states) {
>> + dev_err(dev, "field: %d: out of range idle state %d\n",
>> + i, idle_state);
>> + return -EINVAL;
>> + }
>> +
>> + mux->idle_state = idle_state;
>> + }
>> + }
>> +
>> + mux_chip->ops = &mux_ti_k3_event_ops;
>> +
>> + return devm_mux_chip_register(dev, mux_chip);
>> +}
>> +
>> +static const struct of_device_id mux_ti_k3_event_dt_ids[] = {
>> + { .compatible = "ti,am62l-event-mux-router", },
>> + { /* sentinel */ }
>> +};
>> +MODULE_DEVICE_TABLE(of, mux_ti_k3_event_dt_ids);
>> +
>> +static struct platform_driver mux_ti_k3_event_driver = {
>> + .driver = {
>> + .name = "ti-k3-event-mux",
>> + .of_match_table = mux_ti_k3_event_dt_ids,
>> + .pm = &mux_ti_k3_event_pm_ops,
>> + },
>> + .probe = mux_ti_k3_event_probe,
>> +};
>> +module_platform_driver(mux_ti_k3_event_driver);
>> +
>> +MODULE_DESCRIPTION("TI K3 Bit-field Controlled Event Multiplexer driver");
>> +MODULE_AUTHOR("Rahul Sharma <r-sharma3@ti.com>");
>> +MODULE_LICENSE("GPL");
>> --
>> 2.34.1
>>
>
prev parent reply other threads:[~2026-09-03 19:17 UTC|newest]
Thread overview: 10+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-28 10:06 [PATCH 0/2] Add TI's event mux router driver and build Rahul Sharma
2026-08-28 10:06 ` [PATCH 1/2] dt-bindings: mux-controller: ti: add binding for event mux router Rahul Sharma
2026-08-28 10:13 ` sashiko-bot
2026-08-28 11:50 ` Alvin Šipraga
2026-09-02 12:25 ` [EXTERNAL] " rahul sharma
2026-08-28 16:27 ` Conor Dooley
2026-08-28 10:06 ` [PATCH 2/2] mux-controller: ti: add driver " Rahul Sharma
2026-08-28 10:20 ` sashiko-bot
2026-08-29 15:14 ` Peter Rosin
2026-09-03 19:16 ` Sharma, Rahul [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=bb67e942-ff5e-421c-8556-18a9eb18be22@ti.com \
--to=r-sharma3@ti.com \
--cc=alvin.sipraga@analog.com \
--cc=conor+dt@kernel.org \
--cc=devicetree@vger.kernel.org \
--cc=krzk+dt@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=peda@lysator.liu.se \
--cc=robh@kernel.org \
--cc=vigneshr@ti.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox