Devicetree
 help / color / mirror / Atom feed
From: Quentin Freimanis <quentin@q-lab.dev>
To: dumitru.ceclan@analog.com,
	"Tomi Valkeinen" <tomi.valkeinen+renesas@ideasonboard.com>,
	"Mauro Carvalho Chehab" <mchehab@kernel.org>,
	"Sakari Ailus" <sakari.ailus@linux.intel.com>,
	"Laurent Pinchart" <laurent.pinchart@ideasonboard.com>,
	"Julien Massot" <julien.massot@collabora.com>,
	"Rob Herring" <robh@kernel.org>,
	"Niklas Söderlund" <niklas.soderlund@ragnatech.se>,
	"Greg Kroah-Hartman" <gregkh@linuxfoundation.org>
Cc: "Tomi Valkeinen" <tomi.valkeinen@ideasonboard.com>,
	mitrutzceclan@gmail.com, linux-media@vger.kernel.org,
	linux-kernel@vger.kernel.org, devicetree@vger.kernel.org,
	linux-staging@lists.linux.dev, linux-gpio@vger.kernel.org,
	"Niklas Söderlund" <niklas.soderlund+renesas@ragnatech.se>,
	"Martin Hecht" <Martin.Hecht@avnet.eu>,
	"Andrian Suciu" <Adrian.Suciu@analog.com>,
	"Cosmin Tanislav" <demonsingur@gmail.com>
Subject: Re: [PATCH v17 15/22] media: i2c: add Maxim GMSL2/3 deserializer framework
Date: Fri, 11 Sep 2026 20:26:20 -0700	[thread overview]
Message-ID: <76b9381d-dcd6-40d3-84f2-661413f56521@q-lab.dev> (raw)
In-Reply-To: <20260909-gmsl2-3_serdes-v17-15-002499e534e8@analog.com>

Hi Dumitru,

On 2026-09-09 6:27 a.m., Dumitru Ceclan via B4 Relay wrote:
> From: Cosmin Tanislav <demonsingur@gmail.com>
>

<snip>

> +static int max_des_init_link_ser_xlate(struct max_des_priv *priv,
> +				       struct max_des_link *link,
> +				       struct i2c_adapter *adapter,
> +				       u8 power_up_addr, u8 new_addr)
> +{
> +	struct max_des *des = priv->des;
> +	u8 addrs[] = { power_up_addr, new_addr };
> +	u8 current_addr;
> +	int ret;
> +
> +	if (des->ops->select_links) {
> +		ret = des->ops->select_links(des, BIT(link->index));
> +		if (ret)
> +			return ret;
> +	}
> +
> +	ret = max_ser_wait_for_multiple(adapter, addrs, ARRAY_SIZE(addrs),
> +					&current_addr);
> +	if (ret) {
> +		dev_err(priv->dev,
> +			"Failed to wait for serializer at 0x%02x or 0x%02x: %d\n",
> +			power_up_addr, new_addr, ret);
> +		return ret;
> +	}
> +
> +	ret = max_ser_reset(adapter, current_addr);
> +	if (ret) {
> +		dev_err(priv->dev, "Failed to reset serializer: %d\n", ret);
> +		return ret;
> +	}
> +
> +	ret = max_ser_wait(adapter, power_up_addr);
> +	if (ret) {
> +		dev_err(priv->dev,
> +			"Failed to wait for serializer at 0x%02x: %d\n",
> +			power_up_addr, ret);
> +		return ret;
> +	}
> +
> +	ret = max_ser_change_address(adapter, power_up_addr, new_addr);
> +	if (ret) {
> +		dev_err(priv->dev,
> +			"Failed to change serializer from 0x%02x to 0x%02x: %d\n",
> +			power_up_addr, new_addr, ret);
> +		return ret;
> +	}
> +
> +	ret = max_ser_wait(adapter, new_addr);
> +	if (ret) {
> +		dev_err(priv->dev,
> +			"Failed to wait for serializer at 0x%02x: %d\n",
> +			new_addr, ret);
> +		return ret;
> +	}
> +
> +	if (des->info->fix_tx_ids) {
> +		ret = max_ser_fix_tx_ids(adapter, new_addr);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	return ret;
> +}
> +
> +static int max_des_init(struct max_des_priv *priv)
> +{
> +	struct max_des *des = priv->des;
> +	unsigned int i;
> +	int ret;
> +
> +	if (des->ops->init) {
> +		ret = des->ops->init(des);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	if (des->ops->set_enable) {
> +		ret = des->ops->set_enable(des, false);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	for (i = 0; i < des->info->num_phys; i++) {
> +		struct max_des_phy *phy = &des->phys[i];
> +
> +		if (phy->enabled) {
> +			ret = des->ops->init_phy(des, phy);
> +			if (ret)
> +				return ret;
> +		}
> +
> +		ret = des->ops->set_phy_enable(des, phy, phy->enabled);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	for (i = 0; i < des->info->num_pipes; i++) {
> +		struct max_des_pipe *pipe = &des->pipes[i];
> +		struct max_des_link *link = &des->links[pipe->link_id];
> +
> +		ret = des->ops->set_pipe_enable(des, pipe, false);
> +		if (ret)
> +			return ret;
> +
> +		if (des->ops->set_pipe_tunnel_enable) {
> +			ret = des->ops->set_pipe_tunnel_enable(des, pipe, false);
> +			if (ret)
> +				return ret;
> +		}
> +
> +		if (des->ops->set_pipe_stream_id) {
> +			ret = des->ops->set_pipe_stream_id(des, pipe, pipe->stream_id);
> +			if (ret)
> +				return ret;
> +		}
> +
> +		if (des->ops->set_pipe_link) {
> +			ret = des->ops->set_pipe_link(des, pipe, link);
> +			if (ret)
> +				return ret;
> +		}
> +
> +		ret = max_des_set_pipe_remaps(priv, pipe, pipe->remaps,
> +					      pipe->num_remaps);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	if (!des->ops->init_link)
> +		return 0;
> +
> +	for (i = 0; i < des->info->num_links; i++) {
> +		struct max_des_link *link = &des->links[i];
> +
> +		if (!link->enabled)
> +			continue;
> +
> +		ret = des->ops->init_link(des, link);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static void max_des_ser_find_version_range(struct max_des *des, int *min, int *max)
> +{
> +	unsigned int i;
> +
> +	*min = MAX_SERDES_GMSL_MIN;
> +	*max = MAX_SERDES_GMSL_MAX;
> +
> +	if (!des->info->needs_single_link_version)
> +		return;
> +
> +	for (i = 0; i < des->info->num_links; i++) {
> +		struct max_des_link *link = &des->links[i];
> +
> +		if (!link->enabled)
> +			continue;
> +
> +		if (!link->ser_xlate.en)
> +			continue;
> +
> +		*min = *max = link->version;
> +
> +		return;
> +	}
> +}
> +
> +static unsigned int max_des_enabled_links_mask(struct max_des *des)
> +{
> +	unsigned int mask = 0;
> +	unsigned int i;
> +
> +	for (i = 0; i < des->info->num_links; i++) {
> +		struct max_des_link *link = &des->links[i];
> +
> +		if (link->enabled)
> +			mask |= BIT(link->index);
> +	}
> +
> +	return mask;
> +}
> +
> +static int max_des_ser_attach_addr(struct max_des_priv *priv, u32 chan_id,
> +				   u16 addr, u16 alias)
> +{
> +	struct max_des *des = priv->des;
> +	struct max_des_link *link = &des->links[chan_id];
> +	unsigned int mask;
> +	int i, min, max;
> +	int ret = -ENOENT;
> +	int err;
> +
> +	max_des_ser_find_version_range(des, &min, &max);
> +
> +	if (link->ser_xlate.en) {
> +		dev_err(priv->dev, "Serializer for link %u already bound\n",
> +			link->index);
> +		return -EINVAL;
> +	}
> +
> +	for (i = max; i >= min; i--) {
> +		if (!(des->info->versions & BIT(i)))
> +			continue;
> +
> +		if (des->ops->set_link_version) {
> +			ret = des->ops->set_link_version(des, link, i);
> +			if (ret)
> +				goto out_select_links;
> +		}
> +
> +		ret = max_des_init_link_ser_xlate(priv, link, priv->client->adapter,
> +						  addr, alias);
> +		if (!ret)
> +			break;
> +	}
> +
> +	if (ret) {
> +		dev_err(priv->dev, "Cannot find serializer for link %u\n",
> +			link->index);
> +		ret = -ENOENT;
> +		goto out_select_links;
> +	}
> +
> +	link->version = i;
> +	link->ser_xlate.src = alias;
> +	link->ser_xlate.dst = addr;
> +	link->ser_xlate.en = true;
> +
> +out_select_links:
> +	if (!des->ops->select_links)
> +		return ret;
> +
> +	mask = max_des_enabled_links_mask(des);
> +	err = des->ops->select_links(des, mask);
> +	if (err)
> +		dev_warn(priv->dev, "Failed to restore link selection: %d\n",
> +			 err);
> +
> +	return ret;
> +}
> +
> +static int max_des_ser_atr_attach_addr(struct i2c_atr *atr, u32 chan_id,
> +				       u16 addr, u16 alias)
> +{
> +	struct max_des_priv *priv = i2c_atr_get_driver_data(atr);
> +
> +	return max_des_ser_attach_addr(priv, chan_id, addr, alias);
> +}
> +
> +static void max_des_ser_atr_detach_addr(struct i2c_atr *atr, u32 chan_id, u16 addr)
> +{
> +	/* Don't do anything. */
> +}
> +
> +static const struct i2c_atr_ops max_des_i2c_atr_ops = {
> +	.attach_addr = max_des_ser_atr_attach_addr,
> +	.detach_addr = max_des_ser_atr_detach_addr,
> +};
> +
> +static void max_des_i2c_atr_deinit(struct max_des_priv *priv)
> +{
> +	struct max_des *des = priv->des;
> +	unsigned int i;
> +
> +	for (i = 0; i < des->info->num_links; i++) {
> +		struct max_des_link *link = &des->links[i];
> +
> +		/* Deleting adapters that haven't been added does no harm. */
> +		i2c_atr_del_adapter(priv->atr, link->index);
> +	}
> +
> +	i2c_atr_delete(priv->atr);
> +	priv->atr = NULL;
> +}
> +
> +static int max_des_i2c_atr_init(struct max_des_priv *priv)
> +{
> +	struct max_des *des = priv->des;
> +	unsigned int mask = 0;
> +	unsigned int i;
> +	int ret;
> +
> +	if (!i2c_check_functionality(priv->client->adapter,
> +				     I2C_FUNC_SMBUS_WRITE_BYTE_DATA))
> +		return -ENODEV;
> +
> +	priv->atr = i2c_atr_new(priv->client->adapter, priv->dev,
> +				&max_des_i2c_atr_ops, des->info->num_links,
> +				I2C_ATR_F_STATIC | I2C_ATR_F_PASSTHROUGH);
> +	if (IS_ERR(priv->atr))
> +		return PTR_ERR(priv->atr);
> +
> +	i2c_atr_set_driver_data(priv->atr, priv);
> +
> +	for (i = 0; i < des->info->num_links; i++) {
> +		struct max_des_link *link = &des->links[i];
> +		struct i2c_atr_adap_desc desc = {
> +			.chan_id = i,
> +		};
> +
> +		if (!link->enabled)
> +			continue;
> +
> +		ret = i2c_atr_add_adapter(priv->atr, &desc);
> +		if (ret)
> +			goto err_add_adapters;
> +	}
> +
> +	if (des->ops->select_links) {
> +		mask = max_des_enabled_links_mask(des);
> +
> +		ret = des->ops->select_links(des, mask);
> +		if (ret) {
> +			dev_warn(priv->dev, "Failed to select links: %d\n", ret);
> +
> +			goto err_add_adapters;
> +		}
> +	}
> +
> +	return 0;
> +
> +err_add_adapters:
> +	max_des_i2c_atr_deinit(priv);
> +
> +	return ret;
> +}
> +

I was able to create a race condition during driver probe when using i2c 
ATR.

My setup has 1x MAX96724 + 4x MAX96717 + 4x distinct camera sensors

NOTE: I am using this series on an older kernel release, with my own 
non-mainline camera drivers. Although as far as I can tell this can 
still happen on the latest media.git with mainlined camera drivers.

Here is an example I observed on my setup:
+------------------------------+------------------------------+
| Thread 1                     | Thread 2                     |
+------------------------------+------------------------------+
| deser probes                 |                              |
+------------------------------+------------------------------+
| max_des_i2c_atr_init()       |                              |
+------------------------------+------------------------------+
| first iteration of           |                              |
| for (i = 0; i <              |                              |
| des->info->num_links; i++)   |                              |
+------------------------------+------------------------------+
| i2c_atr_add_adapter() called |                              |
| on link 0                    |                              |
+------------------------------+------------------------------+
| i2c_add_adapter() called for |                              |
| link 0                       |                              |
+------------------------------+------------------------------+
| max_des_ser_atr_attach_addr()|                              |
| called for serializer 0      |                              |
+------------------------------+------------------------------+
| all other links are          |                              |
| disabled to change ser 0's   |                              |
| address, then all re-        |                              |
| enabled again in             |                              |
| max_des_init_link_ser_xlate()|                              |
+------------------------------+------------------------------+
| the seralizer is left unbound|                              |
| because the module is not    |                              |
| loaded yet                   |                              |
+------------------------------+------------------------------+
| second iteration of          |                              |
| for (i = 0; i <              |                              |
| des->info->num_links; i++)   |                              |
+------------------------------+------------------------------+
| i2c_atr_add_adapter called   |                              |
| on link 1                    |                              |
+------------------------------+------------------------------+
| i2c_add_adapter called for   |                              |
| link 1                       |                              |
+------------------------------+------------------------------+
| max_des_ser_atr_attach_addr()| max96717.ko loads, and the   |
| called for serializer 1      | driver probes                |
+------------------------------+------------------------------+
| all other links are          |                              |
| disabled in                  |                              |
| max_des_init_link_ser_xlate  |                              |
| to change serializer 1's     |                              |
| address                      |                              |
+------------------------------+------------------------------+
| we write the new i2c         | camera 0 probes (module      |
| address to serializer 1      | already loaded)              |
+------------------------------+------------------------------+
|                              | camera driver requests a     |
|                              | reset gpio on serializer 0   |
+------------------------------+------------------------------+
|                              | max96717.ko does a i2c       |
|                              | write on link 0, which       |
|                              | fails since it was disabled  |
+------------------------------+------------------------------+
|                              | -EIO                         |
+------------------------------+------------------------------+
| all links re-enabled in      |                              |
| max_des_init_link_ser_xlate  |                              |
+------------------------------+------------------------------+
| max96717.ko is loaded so we  |                              |
| probe serializer 1           |                              |
| now                          |                              |
+------------------------------+------------------------------+
| camera 1 probes              |                              |
| (different driver)           |                              |
+------------------------------+------------------------------+

As a quick hack I fixed this in my local tree by modifying i2c-atr.c to 
lock atr->lock in i2c_atr_attach_addr() and i2c_atr_detach_addr().

After writing this out I realized there might also be another way:
In max_des_init_link_ser_xlate() skip disabling links that have already 
had their serializer changed to a new, unique address. This would also 
require changing select_links to not reset all links connected to the 
deseralizer. Not sure if that is possible.

- Quentin

<snip>




  parent reply	other threads:[~2026-09-12  3:26 UTC|newest]

Thread overview: 31+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-09 13:27 [PATCH v17 00/22] media: i2c: add Maxim GMSL2/3 serializer and deserializer drivers Dumitru Ceclan via B4 Relay
2026-09-09 13:27 ` [PATCH v17 01/22] media: mc: Add INTERNAL pad flag Dumitru Ceclan via B4 Relay
2026-09-09 13:27 ` [PATCH v17 02/22] dt-bindings: media: i2c: max96717: add support for I2C ATR Dumitru Ceclan via B4 Relay
2026-09-09 13:27 ` [PATCH v17 03/22] dt-bindings: media: i2c: max96717: add support for pinctrl/pinconf Dumitru Ceclan via B4 Relay
2026-09-09 13:27 ` [PATCH v17 04/22] dt-bindings: media: i2c: max96717: add support for MAX9295A Dumitru Ceclan via B4 Relay
2026-09-09 13:27 ` [PATCH v17 05/22] dt-bindings: media: i2c: max96717: add support for MAX96793 Dumitru Ceclan via B4 Relay
2026-09-09 13:27 ` [PATCH v17 06/22] dt-bindings: media: i2c: max96712: use pattern properties for ports Dumitru Ceclan via B4 Relay
2026-09-09 13:27 ` [PATCH v17 07/22] dt-bindings: media: i2c: max96712: add support for I2C ATR Dumitru Ceclan via B4 Relay
2026-09-09 13:27 ` [PATCH v17 08/22] dt-bindings: media: i2c: max96712: add support for POC supplies Dumitru Ceclan via B4 Relay
2026-09-09 13:27 ` [PATCH v17 09/22] dt-bindings: media: i2c: max96712: add support for MAX96724F/R Dumitru Ceclan via B4 Relay
2026-09-09 13:27 ` [PATCH v17 10/22] dt-bindings: media: i2c: max96712: add control-channel-port property Dumitru Ceclan via B4 Relay
2026-09-09 13:27 ` [PATCH v17 11/22] dt-bindings: media: i2c: max96714: add support for MAX96714R Dumitru Ceclan via B4 Relay
2026-09-09 13:27 ` [PATCH v17 12/22] dt-bindings: media: i2c: add MAX9296A, MAX96716A, MAX96792A Dumitru Ceclan via B4 Relay
2026-09-09 13:38   ` sashiko-bot
2026-09-09 13:27 ` [PATCH v17 13/22] media: i2c: add Maxim GMSL2/3 serializer and deserializer framework Dumitru Ceclan via B4 Relay
2026-09-09 13:27 ` [PATCH v17 14/22] media: i2c: add Maxim GMSL2/3 serializer framework Dumitru Ceclan via B4 Relay
2026-09-09 13:49   ` sashiko-bot
2026-09-09 13:27 ` [PATCH v17 15/22] media: i2c: add Maxim GMSL2/3 deserializer framework Dumitru Ceclan via B4 Relay
2026-09-09 13:50   ` sashiko-bot
2026-09-12  3:26   ` Quentin Freimanis [this message]
2026-09-09 13:27 ` [PATCH v17 16/22] media: i2c: remove MAX96717 driver Dumitru Ceclan via B4 Relay
2026-09-09 13:27 ` [PATCH v17 17/22] media: i2c: maxim-serdes: add " Dumitru Ceclan via B4 Relay
2026-09-09 13:49   ` sashiko-bot
2026-09-09 13:27 ` [PATCH v17 18/22] arm64: defconfig: disable deprecated MAX96712 driver Dumitru Ceclan via B4 Relay
2026-09-09 13:42   ` sashiko-bot
2026-09-09 13:27 ` [PATCH v17 19/22] staging: media: remove " Dumitru Ceclan via B4 Relay
2026-09-09 13:27 ` [PATCH v17 20/22] media: i2c: maxim-serdes: add MAX96724 driver Dumitru Ceclan via B4 Relay
2026-09-09 13:58   ` sashiko-bot
2026-09-09 13:27 ` [PATCH v17 21/22] media: i2c: remove MAX96714 driver Dumitru Ceclan via B4 Relay
2026-09-09 13:27 ` [PATCH v17 22/22] media: i2c: maxim-serdes: add MAX9296A driver Dumitru Ceclan via B4 Relay
2026-09-09 13:53   ` sashiko-bot

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=76b9381d-dcd6-40d3-84f2-661413f56521@q-lab.dev \
    --to=quentin@q-lab.dev \
    --cc=Adrian.Suciu@analog.com \
    --cc=Martin.Hecht@avnet.eu \
    --cc=demonsingur@gmail.com \
    --cc=devicetree@vger.kernel.org \
    --cc=dumitru.ceclan@analog.com \
    --cc=gregkh@linuxfoundation.org \
    --cc=julien.massot@collabora.com \
    --cc=laurent.pinchart@ideasonboard.com \
    --cc=linux-gpio@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-media@vger.kernel.org \
    --cc=linux-staging@lists.linux.dev \
    --cc=mchehab@kernel.org \
    --cc=mitrutzceclan@gmail.com \
    --cc=niklas.soderlund+renesas@ragnatech.se \
    --cc=niklas.soderlund@ragnatech.se \
    --cc=robh@kernel.org \
    --cc=sakari.ailus@linux.intel.com \
    --cc=tomi.valkeinen+renesas@ideasonboard.com \
    --cc=tomi.valkeinen@ideasonboard.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