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),
> + ¤t_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>
next prev 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