All of lore.kernel.org
 help / color / mirror / Atom feed
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, &reg);
>> +		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
>> 
> 


      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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.