From patchwork Tue Feb 4 09:29:13 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Romain Gantois X-Patchwork-Id: 861983 Received: from relay4-d.mail.gandi.net (relay4-d.mail.gandi.net [217.70.183.196]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A940D207A27; Tue, 4 Feb 2025 09:29:23 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.70.183.196 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1738661366; cv=none; b=A9hRWB3rv8PCPSdzbbgJtdDGayaaEPLN37zlEHB16g0F32Ihbo3VNDCcETH+iiLCllTfQJbLYF/MlznXX5qTYZ2Sg95AyZOSh6wE83bZvsz03N4DcgvYt0yn0w8O1+CaQ7X/9vNTxqzJHE9YxemBY6/E7BgV3bcOv4OU/1JnWJw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1738661366; c=relaxed/simple; bh=PWVjDlbY+5QXMES1Jg43CGsHf063LAxvEUnul1zh5kw=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=GzRDxGoxtTAKfg6vOg+Ohj+BuHH551ejochcyHO09yHpPrnReucf9rhKSEyEJst0pwPenI/U8f61m4ifC493QSpD/o1pOxRN8Hcvy/s6VqhjK6FOuJdbtMO+lUSilxXaoGAl8vzQRpb+upL0QxKOwYRZSK8aUb7dRwVzk6Uer4Q= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=bootlin.com; spf=pass smtp.mailfrom=bootlin.com; dkim=pass (2048-bit key) header.d=bootlin.com header.i=@bootlin.com header.b=Y85470om; arc=none smtp.client-ip=217.70.183.196 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=bootlin.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=bootlin.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=bootlin.com header.i=@bootlin.com header.b="Y85470om" Received: by mail.gandi.net (Postfix) with ESMTPSA id C808843423; Tue, 4 Feb 2025 09:29:20 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=gm1; t=1738661361; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=vTmcIxBFhs+q9FfJ0TmuNV/3ZVPBqmxE+uUND15pWMY=; b=Y85470omv+PEK8UOJ3mUTZmtHMB2wygbE1Wc7a0qyLFuhXob012694idgZH4CY/FyIBRFB 9d02rPJD+EIssAuPkUIWBfuZfY0NURE+2XRTMj4wHjKGlLHFiwnYjv85KbzGyGYCqc/anj 0C67LnFX8wlXdf+G8Gu/8cnPMifQTDFAUOydmuvob1vqgRXiunXn1Te71e0eJH4/dF1HLn QXUv4nshOZL2wxH6kkASYN9OPPnfWN1SaZpOnbvd9kQ7NFoL2shUotEqBQgbAkVFwreoJJ 22y5em54/2zPjC8QdrNHLgheNj+DHXUvd9CHKcnLmhu9+r/NMydH3HwBNARb2A== From: Romain Gantois Date: Tue, 04 Feb 2025 10:29:13 +0100 Subject: [PATCH v7 2/9] media: i2c: ds90ub960: Replace aliased clients list with address list Precedence: bulk X-Mailing-List: linux-media@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20250204-fpc202-v7-2-78b4b8a35cf1@bootlin.com> References: <20250204-fpc202-v7-0-78b4b8a35cf1@bootlin.com> In-Reply-To: <20250204-fpc202-v7-0-78b4b8a35cf1@bootlin.com> To: Wolfram Sang , Tomi Valkeinen , Luca Ceresoli , Andi Shyti , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Derek Kiernan , Dragan Cvetic , Arnd Bergmann , Greg Kroah-Hartman , Mauro Carvalho Chehab , Linus Walleij , Bartosz Golaszewski Cc: Thomas Petazzoni , Kory Maincent , linux-i2c@vger.kernel.org, linux-kernel@vger.kernel.org, devicetree@vger.kernel.org, linux-media@vger.kernel.org, linux-gpio@vger.kernel.org, Romain Gantois X-Mailer: b4 0.14.2 X-GND-State: clean X-GND-Score: -100 X-GND-Cause: gggruggvucftvghtrhhoucdtuddrgeefvddrtddtgddvtdduhecutefuodetggdotefrodftvfcurfhrohhfihhlvgemucfitefpfffkpdcuggftfghnshhusghstghrihgsvgenuceurghilhhouhhtmecufedtudenucesvcftvggtihhpihgvnhhtshculddquddttddmnecujfgurhephfffufggtgfgkfhfjgfvvefosehtjeertdertdejnecuhfhrohhmpeftohhmrghinhcuifgrnhhtohhishcuoehrohhmrghinhdrghgrnhhtohhishessghoohhtlhhinhdrtghomheqnecuggftrfgrthhtvghrnhepkeelieefteelffeuheevtdetkefhfffhteffkefgtefhkeevudeutdeugfffheegnecukfhppeeltddrkeelrdduieefrdduvdejnecuvehluhhsthgvrhfuihiivgeptdenucfrrghrrghmpehinhgvthepledtrdekledrudeifedruddvjedphhgvlhhopegludelvddrudeikedrtddrudefngdpmhgrihhlfhhrohhmpehrohhmrghinhdrghgrnhhtohhishessghoohhtlhhinhdrtghomhdpnhgspghrtghpthhtohepvddvpdhrtghpthhtoheprghrnhgusegrrhhnuggsrdguvgdprhgtphhtthhopehrohhmrghinhdrghgrnhhtohhishessghoohhtlhhinhdrtghomhdprhgtphhtthhopehthhhomhgrshdrphgvthgriiiiohhnihessghoohhtlhhinhdrtghomhdprhgtphhtthhopeguvghrvghkrdhkihgvrhhnrghnsegrmhgurdgtohhmpdhrtghpthhtohepthhomhhirdhvrghlkhgvihhnvghns ehiuggvrghsohhnsghorghrugdrtghomhdprhgtphhtthhopegurhgrghgrnhdrtghvvghtihgtsegrmhgurdgtohhmpdhrtghpthhtohepuggvvhhitggvthhrvggvsehvghgvrhdrkhgvrhhnvghlrdhorhhgpdhrtghpthhtoheprghnughirdhshhihthhisehkvghrnhgvlhdrohhrgh X-GND-Sasl: romain.gantois@bootlin.com The ds90ub960 driver currently uses a list of i2c_client structs to keep track of used I2C address translator (ATR) alias slots for each RX port. Keeping these i2c_client structs in the alias slot list isn't actually needed, the driver only needs to know the client address for each slot. Convert the aliased_clients list to a list of aliased client addresses. This will allow removing the "client" parameter from the i2c-atr callbacks in a future patch. Signed-off-by: Romain Gantois --- drivers/media/i2c/ds90ub960.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/media/i2c/ds90ub960.c b/drivers/media/i2c/ds90ub960.c index 5dde8452739b64dd5b847a7bc1dac556ea43ca6c..e1d53ef087538f7df2c992612e70ce6a3e24906b 100644 --- a/drivers/media/i2c/ds90ub960.c +++ b/drivers/media/i2c/ds90ub960.c @@ -478,7 +478,7 @@ struct ub960_rxport { }; } eq; - const struct i2c_client *aliased_clients[UB960_MAX_PORT_ALIASES]; + u16 aliased_addrs[UB960_MAX_PORT_ALIASES]; }; struct ub960_asd { @@ -1054,17 +1054,17 @@ static int ub960_atr_attach_client(struct i2c_atr *atr, u32 chan_id, struct device *dev = &priv->client->dev; unsigned int reg_idx; - for (reg_idx = 0; reg_idx < ARRAY_SIZE(rxport->aliased_clients); reg_idx++) { - if (!rxport->aliased_clients[reg_idx]) + for (reg_idx = 0; reg_idx < ARRAY_SIZE(rxport->aliased_addrs); reg_idx++) { + if (!rxport->aliased_addrs[reg_idx]) break; } - if (reg_idx == ARRAY_SIZE(rxport->aliased_clients)) { + if (reg_idx == ARRAY_SIZE(rxport->aliased_addrs)) { dev_err(dev, "rx%u: alias pool exhausted\n", rxport->nport); return -EADDRNOTAVAIL; } - rxport->aliased_clients[reg_idx] = client; + rxport->aliased_addrs[reg_idx] = client->addr; ub960_rxport_write(priv, chan_id, UB960_RR_SLAVE_ID(reg_idx), client->addr << 1); @@ -1085,18 +1085,18 @@ static void ub960_atr_detach_client(struct i2c_atr *atr, u32 chan_id, struct device *dev = &priv->client->dev; unsigned int reg_idx; - for (reg_idx = 0; reg_idx < ARRAY_SIZE(rxport->aliased_clients); reg_idx++) { - if (rxport->aliased_clients[reg_idx] == client) + for (reg_idx = 0; reg_idx < ARRAY_SIZE(rxport->aliased_addrs); reg_idx++) { + if (rxport->aliased_addrs[reg_idx] == client->addr) break; } - if (reg_idx == ARRAY_SIZE(rxport->aliased_clients)) { + if (reg_idx == ARRAY_SIZE(rxport->aliased_addrs)) { dev_err(dev, "rx%u: client 0x%02x is not mapped!\n", rxport->nport, client->addr); return; } - rxport->aliased_clients[reg_idx] = NULL; + rxport->aliased_addrs[reg_idx] = 0; ub960_rxport_write(priv, chan_id, UB960_RR_SLAVE_ALIAS(reg_idx), 0); From patchwork Tue Feb 4 09:29:15 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Romain Gantois X-Patchwork-Id: 861982 Received: from relay4-d.mail.gandi.net (relay4-d.mail.gandi.net [217.70.183.196]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 92350207DFF; Tue, 4 Feb 2025 09:29:25 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.70.183.196 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1738661368; cv=none; b=eYOyNaZfRRqVZLV/UDLl/IJp36i3Td2s9ZsVxSUyoh7kr5kD5NBL5e24pzjPtpS3E+3Gkz2jtzrSlbC5D7RIj+0r8OthhKEmI2Rzg3mggauXyK8flKYVi57pq44d61u6axdFU6H4dbIoseY4sa6a88eNTffpA1HCGVOXuziVkBY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1738661368; c=relaxed/simple; bh=n0DBKAJ9pgKz/m5yLANuJaeG8xY3Quf8nfeXa7JfymI=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=W+mbKG4YEFcP1LUlKFxfo0FU6UFK2iFJWJnL+dHPiQwg4Gq/46ir+gTLa4xlo3VS+Otjsy/5gGwRcckGgCG41OxJ2IHZL9aHkyIkbjLMXxo/5Ip77Fo5ER+kMw8qFMRePzJ+j68vhx2Iq9dVYWDu9LIwlz2beTHPoIJsbk/MAec= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=bootlin.com; spf=pass smtp.mailfrom=bootlin.com; dkim=pass (2048-bit key) header.d=bootlin.com header.i=@bootlin.com header.b=YkXXUwe+; arc=none smtp.client-ip=217.70.183.196 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=bootlin.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=bootlin.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=bootlin.com header.i=@bootlin.com header.b="YkXXUwe+" Received: by mail.gandi.net (Postfix) with ESMTPSA id F334A44360; Tue, 4 Feb 2025 09:29:22 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=gm1; t=1738661363; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=peZy17OXvlBM3hd7zz+egN6FRCg5cULHXylYXdPt8v8=; b=YkXXUwe+FPK78Dv2WZFrEyjiVv6fzHfdi+FZ9IklOXo6QzTtb1A2UchYnbPZsjOqT2rtRt XMjazOEheSCjmxhpDpA+AgPhcXJvZVyoP5nUtWUDRp/an7IJKBPAP3FFldh6v8SS1kSZsz yRXV+QM9KS7N4jCkqBJHG1PhAQoDHK6wm2G/o+V3rO7m0AghTmB/ghqxgVrURqC/8kFhxo IgrdnqR/8F1It6myCeH9qCKjeX9l7xZX9elxJvnE8I7jWwhxrGbkFSoSrhMDf4Z/kIE063 Bes0ygAMjE6vEpOQzZMvcU9aejasw5znoKlb8RrjoLesavao0mqeiDrOYTlwOQ== From: Romain Gantois Date: Tue, 04 Feb 2025 10:29:15 +0100 Subject: [PATCH v7 4/9] i2c: use client addresses directly in ATR interface Precedence: bulk X-Mailing-List: linux-media@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20250204-fpc202-v7-4-78b4b8a35cf1@bootlin.com> References: <20250204-fpc202-v7-0-78b4b8a35cf1@bootlin.com> In-Reply-To: <20250204-fpc202-v7-0-78b4b8a35cf1@bootlin.com> To: Wolfram Sang , Tomi Valkeinen , Luca Ceresoli , Andi Shyti , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Derek Kiernan , Dragan Cvetic , Arnd Bergmann , Greg Kroah-Hartman , Mauro Carvalho Chehab , Linus Walleij , Bartosz Golaszewski Cc: Thomas Petazzoni , Kory Maincent , linux-i2c@vger.kernel.org, linux-kernel@vger.kernel.org, devicetree@vger.kernel.org, linux-media@vger.kernel.org, linux-gpio@vger.kernel.org, Romain Gantois X-Mailer: b4 0.14.2 X-GND-State: clean X-GND-Score: -100 X-GND-Cause: gggruggvucftvghtrhhoucdtuddrgeefvddrtddtgddvtdduhecutefuodetggdotefrodftvfcurfhrohhfihhlvgemucfitefpfffkpdcuggftfghnshhusghstghrihgsvgenuceurghilhhouhhtmecufedtudenucesvcftvggtihhpihgvnhhtshculddquddttddmnecujfgurhephfffufggtgfgkfhfjgfvvefosehtjeertdertdejnecuhfhrohhmpeftohhmrghinhcuifgrnhhtohhishcuoehrohhmrghinhdrghgrnhhtohhishessghoohhtlhhinhdrtghomheqnecuggftrfgrthhtvghrnhepkeelieefteelffeuheevtdetkefhfffhteffkefgtefhkeevudeutdeugfffheegnecukfhppeeltddrkeelrdduieefrdduvdejnecuvehluhhsthgvrhfuihiivgeptdenucfrrghrrghmpehinhgvthepledtrdekledrudeifedruddvjedphhgvlhhopegludelvddrudeikedrtddrudefngdpmhgrihhlfhhrohhmpehrohhmrghinhdrghgrnhhtohhishessghoohhtlhhinhdrtghomhdpnhgspghrtghpthhtohepvddvpdhrtghpthhtoheprghrnhgusegrrhhnuggsrdguvgdprhgtphhtthhopehrohhmrghinhdrghgrnhhtohhishessghoohhtlhhinhdrtghomhdprhgtphhtthhopehthhhomhgrshdrphgvthgriiiiohhnihessghoohhtlhhinhdrtghomhdprhgtphhtthhopeguvghrvghkrdhkihgvrhhnrghnsegrmhgurdgtohhmpdhrtghpthhtohepthhomhhirdhvrghlkhgvihhnvghns ehiuggvrghsohhnsghorghrugdrtghomhdprhgtphhtthhopegurhgrghgrnhdrtghvvghtihgtsegrmhgurdgtohhmpdhrtghpthhtohepuggvvhhitggvthhrvggvsehvghgvrhdrkhgvrhhnvghlrdhorhhgpdhrtghpthhtoheprghnughirdhshhihthhisehkvghrnhgvlhdrohhrgh X-GND-Sasl: romain.gantois@bootlin.com The I2C Address Translator (ATR) module defines mappings from i2c_client structs to aliases. However, only the physical address of each i2c_client struct is actually relevant to the workings of the ATR module. Moreover, some drivers require address translation functionality but do not allocate i2c_client structs, accessing the adapter directly instead. The SFP subsystem is an example of this. Replace the "i2c_client" field of the i2c_atr_alias_pair struct with a u16 "addr" field. Rewrite helper functions and callbacks as needed. Reviewed-by: Tomi Valkeinen Signed-off-by: Romain Gantois --- drivers/i2c/i2c-atr.c | 52 ++++++++++++++++--------------------------- drivers/media/i2c/ds90ub960.c | 24 ++++++++++---------- include/linux/i2c-atr.h | 20 ++++++++--------- 3 files changed, 41 insertions(+), 55 deletions(-) diff --git a/drivers/i2c/i2c-atr.c b/drivers/i2c/i2c-atr.c index 8fe9ddff8e96f6f613f48d91a09c09b326e6cb01..118fd7d764dce51de587e274ef452e031ccff52d 100644 --- a/drivers/i2c/i2c-atr.c +++ b/drivers/i2c/i2c-atr.c @@ -21,16 +21,16 @@ #define ATR_MAX_SYMLINK_LEN 11 /* Longest name is 10 chars: "channel-99" */ /** - * struct i2c_atr_alias_pair - Holds the alias assigned to a client. + * struct i2c_atr_alias_pair - Holds the alias assigned to a client address. * @node: List node - * @client: Pointer to the client on the child bus + * @addr: Address of the client on the child bus. * @alias: I2C alias address assigned by the driver. * This is the address that will be used to issue I2C transactions * on the parent (physical) bus. */ struct i2c_atr_alias_pair { struct list_head node; - const struct i2c_client *client; + u16 addr; u16 alias; }; @@ -97,27 +97,13 @@ struct i2c_atr { struct i2c_adapter *adapter[] __counted_by(max_adapters); }; -static struct i2c_atr_alias_pair * -i2c_atr_find_mapping_by_client(const struct list_head *list, - const struct i2c_client *client) -{ - struct i2c_atr_alias_pair *c2a; - - list_for_each_entry(c2a, list, node) { - if (c2a->client == client) - return c2a; - } - - return NULL; -} - static struct i2c_atr_alias_pair * i2c_atr_find_mapping_by_addr(const struct list_head *list, u16 phys_addr) { struct i2c_atr_alias_pair *c2a; list_for_each_entry(c2a, list, node) { - if (c2a->client->addr == phys_addr) + if (c2a->addr == phys_addr) return c2a; } @@ -313,8 +299,8 @@ static void i2c_atr_release_alias(struct i2c_atr *atr, u16 alias) dev_warn(atr->dev, "Unable to find mapped alias\n"); } -static int i2c_atr_attach_client(struct i2c_adapter *adapter, - const struct i2c_client *client) +static int i2c_atr_attach_addr(struct i2c_adapter *adapter, + u16 addr) { struct i2c_atr_chan *chan = adapter->algo_data; struct i2c_atr *atr = chan->atr; @@ -334,14 +320,14 @@ static int i2c_atr_attach_client(struct i2c_adapter *adapter, goto err_release_alias; } - ret = atr->ops->attach_client(atr, chan->chan_id, client, alias); + ret = atr->ops->attach_addr(atr, chan->chan_id, addr, alias); if (ret) goto err_free; - dev_dbg(atr->dev, "chan%u: client 0x%02x mapped at alias 0x%02x (%s)\n", - chan->chan_id, client->addr, alias, client->name); + dev_dbg(atr->dev, "chan%u: using alias 0x%02x for addr 0x%02x\n", + chan->chan_id, alias, addr); - c2a->client = client; + c2a->addr = addr; c2a->alias = alias; list_add(&c2a->node, &chan->alias_list); @@ -355,16 +341,16 @@ static int i2c_atr_attach_client(struct i2c_adapter *adapter, return ret; } -static void i2c_atr_detach_client(struct i2c_adapter *adapter, - const struct i2c_client *client) +static void i2c_atr_detach_addr(struct i2c_adapter *adapter, + u16 addr) { struct i2c_atr_chan *chan = adapter->algo_data; struct i2c_atr *atr = chan->atr; struct i2c_atr_alias_pair *c2a; - atr->ops->detach_client(atr, chan->chan_id, client); + atr->ops->detach_addr(atr, chan->chan_id, addr); - c2a = i2c_atr_find_mapping_by_client(&chan->alias_list, client); + c2a = i2c_atr_find_mapping_by_addr(&chan->alias_list, addr); if (!c2a) { /* This should never happen */ dev_warn(atr->dev, "Unable to find address mapping\n"); @@ -374,8 +360,8 @@ static void i2c_atr_detach_client(struct i2c_adapter *adapter, i2c_atr_release_alias(atr, c2a->alias); dev_dbg(atr->dev, - "chan%u: client 0x%02x unmapped from alias 0x%02x (%s)\n", - chan->chan_id, client->addr, c2a->alias, client->name); + "chan%u: detached alias 0x%02x from addr 0x%02x\n", + chan->chan_id, c2a->alias, addr); list_del(&c2a->node); kfree(c2a); @@ -405,7 +391,7 @@ static int i2c_atr_bus_notifier_call(struct notifier_block *nb, switch (event) { case BUS_NOTIFY_ADD_DEVICE: - ret = i2c_atr_attach_client(client->adapter, client); + ret = i2c_atr_attach_addr(client->adapter, client->addr); if (ret) dev_err(atr->dev, "Failed to attach remote client '%s': %d\n", @@ -413,7 +399,7 @@ static int i2c_atr_bus_notifier_call(struct notifier_block *nb, break; case BUS_NOTIFY_REMOVED_DEVICE: - i2c_atr_detach_client(client->adapter, client); + i2c_atr_detach_addr(client->adapter, client->addr); break; default: @@ -506,7 +492,7 @@ struct i2c_atr *i2c_atr_new(struct i2c_adapter *parent, struct device *dev, if (max_adapters > ATR_MAX_ADAPTERS) return ERR_PTR(-EINVAL); - if (!ops || !ops->attach_client || !ops->detach_client) + if (!ops || !ops->attach_addr || !ops->detach_addr) return ERR_PTR(-EINVAL); atr = kzalloc(struct_size(atr, adapter, max_adapters), GFP_KERNEL); diff --git a/drivers/media/i2c/ds90ub960.c b/drivers/media/i2c/ds90ub960.c index 5969692480409a1632ac05e43c58df479982bdd5..869e32bd07e8b08f443d141e20eb53eeeb3de864 100644 --- a/drivers/media/i2c/ds90ub960.c +++ b/drivers/media/i2c/ds90ub960.c @@ -1049,8 +1049,8 @@ static int ub960_ind_update_bits(struct ub960_data *priv, u8 block, u8 reg, * I2C-ATR (address translator) */ -static int ub960_atr_attach_client(struct i2c_atr *atr, u32 chan_id, - const struct i2c_client *client, u16 alias) +static int ub960_atr_attach_addr(struct i2c_atr *atr, u32 chan_id, + u16 addr, u16 alias) { struct ub960_data *priv = i2c_atr_get_driver_data(atr); struct ub960_rxport *rxport = priv->rxports[chan_id]; @@ -1069,21 +1069,21 @@ static int ub960_atr_attach_client(struct i2c_atr *atr, u32 chan_id, return -EADDRNOTAVAIL; } - rxport->aliased_addrs[reg_idx] = client->addr; + rxport->aliased_addrs[reg_idx] = addr; ub960_rxport_write(priv, chan_id, UB960_RR_SLAVE_ID(reg_idx), - client->addr << 1); + addr << 1); ub960_rxport_write(priv, chan_id, UB960_RR_SLAVE_ALIAS(reg_idx), alias << 1); dev_dbg(dev, "rx%u: client 0x%02x assigned alias 0x%02x at slot %u\n", - rxport->nport, client->addr, alias, reg_idx); + rxport->nport, addr, alias, reg_idx); return 0; } -static void ub960_atr_detach_client(struct i2c_atr *atr, u32 chan_id, - const struct i2c_client *client) +static void ub960_atr_detach_addr(struct i2c_atr *atr, u32 chan_id, + u16 addr) { struct ub960_data *priv = i2c_atr_get_driver_data(atr); struct ub960_rxport *rxport = priv->rxports[chan_id]; @@ -1093,13 +1093,13 @@ static void ub960_atr_detach_client(struct i2c_atr *atr, u32 chan_id, guard(mutex)(&rxport->aliased_addrs_lock); for (reg_idx = 0; reg_idx < ARRAY_SIZE(rxport->aliased_addrs); reg_idx++) { - if (rxport->aliased_addrs[reg_idx] == client->addr) + if (rxport->aliased_addrs[reg_idx] == addr) break; } if (reg_idx == ARRAY_SIZE(rxport->aliased_addrs)) { dev_err(dev, "rx%u: client 0x%02x is not mapped!\n", - rxport->nport, client->addr); + rxport->nport, addr); return; } @@ -1108,12 +1108,12 @@ static void ub960_atr_detach_client(struct i2c_atr *atr, u32 chan_id, ub960_rxport_write(priv, chan_id, UB960_RR_SLAVE_ALIAS(reg_idx), 0); dev_dbg(dev, "rx%u: client 0x%02x released at slot %u\n", rxport->nport, - client->addr, reg_idx); + addr, reg_idx); } static const struct i2c_atr_ops ub960_atr_ops = { - .attach_client = ub960_atr_attach_client, - .detach_client = ub960_atr_detach_client, + .attach_addr = ub960_atr_attach_addr, + .detach_addr = ub960_atr_detach_addr, }; static int ub960_init_atr(struct ub960_data *priv) diff --git a/include/linux/i2c-atr.h b/include/linux/i2c-atr.h index 4d5da161c22516b3294e73702ace7a4546cdd588..14c1f9175c0db6a8a9c6ef5d771ae68361132a76 100644 --- a/include/linux/i2c-atr.h +++ b/include/linux/i2c-atr.h @@ -20,20 +20,20 @@ struct i2c_atr; /** * struct i2c_atr_ops - Callbacks from ATR to the device driver. - * @attach_client: Notify the driver of a new device connected on a child - * bus, with the alias assigned to it. The driver must - * configure the hardware to use the alias. - * @detach_client: Notify the driver of a device getting disconnected. The - * driver must configure the hardware to stop using the - * alias. + * @attach_addr: Notify the driver of a new device connected on a child + * bus, with the alias assigned to it. The driver must + * configure the hardware to use the alias. + * @detach_addr: Notify the driver of a device getting disconnected. The + * driver must configure the hardware to stop using the + * alias. * * All these functions return 0 on success, a negative error code otherwise. */ struct i2c_atr_ops { - int (*attach_client)(struct i2c_atr *atr, u32 chan_id, - const struct i2c_client *client, u16 alias); - void (*detach_client)(struct i2c_atr *atr, u32 chan_id, - const struct i2c_client *client); + int (*attach_addr)(struct i2c_atr *atr, u32 chan_id, + u16 addr, u16 alias); + void (*detach_addr)(struct i2c_atr *atr, u32 chan_id, + u16 addr); }; /** From patchwork Tue Feb 4 09:29:17 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Romain Gantois X-Patchwork-Id: 861981 Received: from relay4-d.mail.gandi.net (relay4-d.mail.gandi.net [217.70.183.196]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id D701E20A5CA; Tue, 4 Feb 2025 09:29:27 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.70.183.196 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1738661370; cv=none; b=beM+JaSlCKNUpmKfACYmpGl9Varo9fWVkY5F+p+45LoyFfHpJVv9edgDm037274l7c19nm2434bQqi1OZOViz5ny54CBIBT3DX1s+9Tma8HMI40mpHPQAzinnZZSMa1NWVNG112yvAK3XpIyEfrC+wsKF07xELc3GE7cIZvCIyA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1738661370; c=relaxed/simple; bh=u6TT5tLgQSB2LcT8eyCO7h0Vll4qkdcUfOlP4+tYeZA=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=Z87vKxO9xpSI3pWomP1sPkX3zHpGDc0uQqDQEr87G7P6KRO+7YOjjEFsKie0P6KvNMh51WPPJOHtX5o0LtBvUWlv1k4kwSX0EstsCmtLb0+UPSBDfz9kkgpabP7lIWGLnnWOrCaOpSXiKg2Ys9c6hr2Lm9zxWxtST1aj0lkIzks= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=bootlin.com; spf=pass smtp.mailfrom=bootlin.com; dkim=pass (2048-bit key) header.d=bootlin.com header.i=@bootlin.com header.b=IugucDF9; arc=none smtp.client-ip=217.70.183.196 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=bootlin.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=bootlin.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=bootlin.com header.i=@bootlin.com header.b="IugucDF9" Received: by mail.gandi.net (Postfix) with ESMTPSA id 23556433EE; Tue, 4 Feb 2025 09:29:25 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=gm1; t=1738661366; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=v40zWBlUzwQVJicfUqDAPQECoG5UIa9/l4Qp0jQwMv8=; b=IugucDF90pFK4Cge1l2VjzUDk5srWoaXlYNDPfimUSNQVwwAqo8UHf3UnHAMBSrltq3t5m lPbEKzqi0GuBhihrdwnUNDH0nGWLvwYYZzKnfd4wToFZAA0ql7OP4VHCkWhoRF6aTnNE5H SmSCoku0b/RXKjLW7OYOV9LcmT9r64XGPDPKC4gWo8w10zxQFK7liyzm550zmJyY1zXdMc jLwz0nfkTQ+8v7DdceICpteo9LphsN3CVjRRXUTk6eNfnvzw2ypFM7Gxy9AKIg1UV3uDVt cJr36aeKMxms4pFMOBRl9oP8nWfqqo6mH28ZSQl2hJvwf6SjZAjjK23vSIznIg== From: Romain Gantois Date: Tue, 04 Feb 2025 10:29:17 +0100 Subject: [PATCH v7 6/9] i2c: rename field 'alias_list' of struct i2c_atr_chan to 'alias_pairs' Precedence: bulk X-Mailing-List: linux-media@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20250204-fpc202-v7-6-78b4b8a35cf1@bootlin.com> References: <20250204-fpc202-v7-0-78b4b8a35cf1@bootlin.com> In-Reply-To: <20250204-fpc202-v7-0-78b4b8a35cf1@bootlin.com> To: Wolfram Sang , Tomi Valkeinen , Luca Ceresoli , Andi Shyti , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Derek Kiernan , Dragan Cvetic , Arnd Bergmann , Greg Kroah-Hartman , Mauro Carvalho Chehab , Linus Walleij , Bartosz Golaszewski Cc: Thomas Petazzoni , Kory Maincent , linux-i2c@vger.kernel.org, linux-kernel@vger.kernel.org, devicetree@vger.kernel.org, linux-media@vger.kernel.org, linux-gpio@vger.kernel.org, Romain Gantois X-Mailer: b4 0.14.2 X-GND-State: clean X-GND-Score: -100 X-GND-Cause: gggruggvucftvghtrhhoucdtuddrgeefvddrtddtgddvtdduhecutefuodetggdotefrodftvfcurfhrohhfihhlvgemucfitefpfffkpdcuggftfghnshhusghstghrihgsvgenuceurghilhhouhhtmecufedtudenucesvcftvggtihhpihgvnhhtshculddquddttddmnecujfgurhephfffufggtgfgkfhfjgfvvefosehtjeertdertdejnecuhfhrohhmpeftohhmrghinhcuifgrnhhtohhishcuoehrohhmrghinhdrghgrnhhtohhishessghoohhtlhhinhdrtghomheqnecuggftrfgrthhtvghrnhepkeelieefteelffeuheevtdetkefhfffhteffkefgtefhkeevudeutdeugfffheegnecukfhppeeltddrkeelrdduieefrdduvdejnecuvehluhhsthgvrhfuihiivgepfeenucfrrghrrghmpehinhgvthepledtrdekledrudeifedruddvjedphhgvlhhopegludelvddrudeikedrtddrudefngdpmhgrihhlfhhrohhmpehrohhmrghinhdrghgrnhhtohhishessghoohhtlhhinhdrtghomhdpnhgspghrtghpthhtohepvddvpdhrtghpthhtoheprghrnhgusegrrhhnuggsrdguvgdprhgtphhtthhopehrohhmrghinhdrghgrnhhtohhishessghoohhtlhhinhdrtghomhdprhgtphhtthhopehthhhomhgrshdrphgvthgriiiiohhnihessghoohhtlhhinhdrtghomhdprhgtphhtthhopeguvghrvghkrdhkihgvrhhnrghnsegrmhgurdgtohhmpdhrtghpthhtohepthhomhhirdhvrghlkhgvihhnvghns ehiuggvrghsohhnsghorghrugdrtghomhdprhgtphhtthhopegurhgrghgrnhdrtghvvghtihgtsegrmhgurdgtohhmpdhrtghpthhtohepuggvvhhitggvthhrvggvsehvghgvrhdrkhgvrhhnvghlrdhorhhgpdhrtghpthhtoheprghnughirdhshhihthhisehkvghrnhgvlhdrohhrgh X-GND-Sasl: romain.gantois@bootlin.com The "alias_list" field of struct i2c_atr_chan describes translation table entries programmed in the ATR channel. This terminology will become more confusing when per-channel alias pool support is introduced, as struct i2c_atr_chan will gain a new field called "alias_pool", which will describe aliases which are available to the ATR channel. Rename the "alias_list" field to "alias_pairs" to clearly distinguish it from the future "alias_pool" field. No functional change is intended. Signed-off-by: Romain Gantois --- drivers/i2c/i2c-atr.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/i2c/i2c-atr.c b/drivers/i2c/i2c-atr.c index e9c6b85e3e4a4ff84fdf9ad139f05fdceb441120..01f8c6f6e68a0a6012101a201c3fd8c7c0f25b47 100644 --- a/drivers/i2c/i2c-atr.c +++ b/drivers/i2c/i2c-atr.c @@ -56,7 +56,7 @@ struct i2c_atr_alias_pool { * @adap: The &struct i2c_adapter for the channel * @atr: The parent I2C ATR * @chan_id: The ID of this channel - * @alias_list: List of @struct i2c_atr_alias_pair containing the + * @alias_pairs: List of @struct i2c_atr_alias_pair containing the * assigned aliases * @orig_addrs_lock: Mutex protecting @orig_addrs * @orig_addrs: Buffer used to store the original addresses during transmit @@ -67,7 +67,7 @@ struct i2c_atr_chan { struct i2c_atr *atr; u32 chan_id; - struct list_head alias_list; + struct list_head alias_pairs; /* Lock orig_addrs during xfer */ struct mutex orig_addrs_lock; @@ -192,7 +192,7 @@ static int i2c_atr_map_msgs(struct i2c_atr_chan *chan, struct i2c_msg *msgs, for (i = 0; i < num; i++) { chan->orig_addrs[i] = msgs[i].addr; - c2a = i2c_atr_find_mapping_by_addr(&chan->alias_list, + c2a = i2c_atr_find_mapping_by_addr(&chan->alias_pairs, msgs[i].addr); if (!c2a) { dev_err(atr->dev, "client 0x%02x not mapped!\n", @@ -262,7 +262,7 @@ static int i2c_atr_smbus_xfer(struct i2c_adapter *adap, u16 addr, struct i2c_adapter *parent = atr->parent; struct i2c_atr_alias_pair *c2a; - c2a = i2c_atr_find_mapping_by_addr(&chan->alias_list, addr); + c2a = i2c_atr_find_mapping_by_addr(&chan->alias_pairs, addr); if (!c2a) { dev_err(atr->dev, "client 0x%02x not mapped!\n", addr); return -ENXIO; @@ -380,7 +380,7 @@ static int i2c_atr_attach_addr(struct i2c_adapter *adapter, c2a->addr = addr; c2a->alias = alias; - list_add(&c2a->node, &chan->alias_list); + list_add(&c2a->node, &chan->alias_pairs); return 0; @@ -401,7 +401,7 @@ static void i2c_atr_detach_addr(struct i2c_adapter *adapter, atr->ops->detach_addr(atr, chan->chan_id, addr); - c2a = i2c_atr_find_mapping_by_addr(&chan->alias_list, addr); + c2a = i2c_atr_find_mapping_by_addr(&chan->alias_pairs, addr); if (!c2a) { /* This should never happen */ dev_warn(atr->dev, "Unable to find address mapping\n"); @@ -621,7 +621,7 @@ int i2c_atr_add_adapter(struct i2c_atr *atr, u32 chan_id, chan->atr = atr; chan->chan_id = chan_id; - INIT_LIST_HEAD(&chan->alias_list); + INIT_LIST_HEAD(&chan->alias_pairs); mutex_init(&chan->orig_addrs_lock); snprintf(chan->adap.name, sizeof(chan->adap.name), "i2c-%d-atr-%d", From patchwork Tue Feb 4 09:29:19 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Romain Gantois X-Patchwork-Id: 861980 Received: from relay4-d.mail.gandi.net (relay4-d.mail.gandi.net [217.70.183.196]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id C5B5F20C00B; Tue, 4 Feb 2025 09:29:29 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.70.183.196 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1738661372; cv=none; b=BcoPPh4Q+2dhbKyevsf1GsZzyF/kj7WD8+94XND9kcrrIpeJZQd49i0wBdQxkeqqKhzAVjGz/p/dS50AtDSHLOIxgIJ9/L8oaLinQmPzhewSOxuRiXXs0Ii8XGPsO54tBMA85mWadOkNLMeidHW2Xx0BL3lLJ8g4HlqZ+Dnwufc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1738661372; c=relaxed/simple; bh=6u6nId+12BH95CMZTPIoLH/Yo7DF/AjT6qsjLLZOr7s=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=rbsaXwvwKs8uqjwm50AmQJCKdxwt3V7BnA5nQ7598Snq4HA1YHLGqf7iRBVOjmSW+cInfoNIu/MATEIkfi8Px0pjNTAmh3+N0yj0fVDzVSY5trO4fl1xQESdWbQnVvUPcE5K66//5M3D7byiWZux7eWhCqbV8oPbenPx+jRLZuQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=bootlin.com; spf=pass smtp.mailfrom=bootlin.com; dkim=pass (2048-bit key) header.d=bootlin.com header.i=@bootlin.com header.b=ThRxFsYe; arc=none smtp.client-ip=217.70.183.196 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=bootlin.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=bootlin.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=bootlin.com header.i=@bootlin.com header.b="ThRxFsYe" Received: by mail.gandi.net (Postfix) with ESMTPSA id 5C63E433ED; Tue, 4 Feb 2025 09:29:27 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=gm1; t=1738661368; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=CpBR5RLshQdrbdLflI2XooigLD05TtTFhbUTNcnU0nI=; b=ThRxFsYe81gU7UlbprOn9zF0uVgechDyDfUvwmjpYx0Fylm+z6cHqge2EA92EC007P2rAt pRGq9kg0RziS5T92aJsgTCJUsI68ar+0+b9RHzbIBckhILo0UxH8INHBssizoyXkbFqhsx Fhzia0spjFAp4rHZDT8+/uAxhp0qzSIjqDrLmyx8jzEnAukFzJpx6FemADw+NCR2phi0Xe L11qyHdxtDOmhAnprTqOoKWF1fAS1z7WN5Qz7MJJBh9yekkHsm9rWBoLeMxIj6RjCYe1vk U6eWIwyfVQT1x9LD66ixpgjEHKb4qfoV3ae7hB1QxsNL4EsDd6i2rZ7hJtaEUw== From: Romain Gantois Date: Tue, 04 Feb 2025 10:29:19 +0100 Subject: [PATCH v7 8/9] i2c: Support dynamic address translation Precedence: bulk X-Mailing-List: linux-media@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20250204-fpc202-v7-8-78b4b8a35cf1@bootlin.com> References: <20250204-fpc202-v7-0-78b4b8a35cf1@bootlin.com> In-Reply-To: <20250204-fpc202-v7-0-78b4b8a35cf1@bootlin.com> To: Wolfram Sang , Tomi Valkeinen , Luca Ceresoli , Andi Shyti , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Derek Kiernan , Dragan Cvetic , Arnd Bergmann , Greg Kroah-Hartman , Mauro Carvalho Chehab , Linus Walleij , Bartosz Golaszewski Cc: Thomas Petazzoni , Kory Maincent , linux-i2c@vger.kernel.org, linux-kernel@vger.kernel.org, devicetree@vger.kernel.org, linux-media@vger.kernel.org, linux-gpio@vger.kernel.org, Romain Gantois X-Mailer: b4 0.14.2 X-GND-State: clean X-GND-Score: -100 X-GND-Cause: gggruggvucftvghtrhhoucdtuddrgeefvddrtddtgddvtdduhecutefuodetggdotefrodftvfcurfhrohhfihhlvgemucfitefpfffkpdcuggftfghnshhusghstghrihgsvgenuceurghilhhouhhtmecufedtudenucesvcftvggtihhpihgvnhhtshculddquddttddmnecujfgurhephfffufggtgfgkfhfjgfvvefosehtjeertdertdejnecuhfhrohhmpeftohhmrghinhcuifgrnhhtohhishcuoehrohhmrghinhdrghgrnhhtohhishessghoohhtlhhinhdrtghomheqnecuggftrfgrthhtvghrnhepkeelieefteelffeuheevtdetkefhfffhteffkefgtefhkeevudeutdeugfffheegnecukfhppeeltddrkeelrdduieefrdduvdejnecuvehluhhsthgvrhfuihiivgepfeenucfrrghrrghmpehinhgvthepledtrdekledrudeifedruddvjedphhgvlhhopegludelvddrudeikedrtddrudefngdpmhgrihhlfhhrohhmpehrohhmrghinhdrghgrnhhtohhishessghoohhtlhhinhdrtghomhdpnhgspghrtghpthhtohepvddvpdhrtghpthhtoheprghrnhgusegrrhhnuggsrdguvgdprhgtphhtthhopehrohhmrghinhdrghgrnhhtohhishessghoohhtlhhinhdrtghomhdprhgtphhtthhopehthhhomhgrshdrphgvthgriiiiohhnihessghoohhtlhhinhdrtghomhdprhgtphhtthhopeguvghrvghkrdhkihgvrhhnrghnsegrmhgurdgtohhmpdhrtghpthhtohepthhomhhirdhvrghlkhgvihhnvghns ehiuggvrghsohhnsghorghrugdrtghomhdprhgtphhtthhopegurhgrghgrnhdrtghvvghtihgtsegrmhgurdgtohhmpdhrtghpthhtohepuggvvhhitggvthhrvggvsehvghgvrhdrkhgvrhhnvghlrdhorhhgpdhrtghpthhtoheprghnughirdhshhihthhisehkvghrnhgvlhdrohhrgh X-GND-Sasl: romain.gantois@bootlin.com The i2c-atr module keeps a list of associations between I2C client aliases and I2C addresses. This represents the address translation table which is programmed into an ATR channel at any given time. This list is only updated when a new client is bound to the channel. However in some cases, an ATR channel can have more downstream clients than available aliases. One example of this is an SFP module that is bound to an FPC202 port. The FPC202 port can only access up to two logical I2C addresses. However, the SFP module may expose up to three logical I2C addresses: its EEPROM on 7-bit addresses 0x50 and 0x51, and a PHY transceiver on address 0x56. In cases like these, it is necessary to reconfigure the channel's translation table on the fly, so that all three I2C addresses can be accessed when needed. As there are currently no known ATR's which do not support dynamic address translation, this feature can be enabled by default without breaking existing use cases. Modify the i2c-atr module to provide on-the-fly address translation. This is achieved by modifying an ATR channel's translation table whenever an I2C transaction with unmapped clients is requested. Add a mutex to protect alias_list. This prevents i2c_atr_dynamic_attach/detach_addr from racing with the bus notifier handler to modify alias_list. Signed-off-by: Romain Gantois --- drivers/i2c/i2c-atr.c | 249 +++++++++++++++++++++++++++++++++++++------------- 1 file changed, 188 insertions(+), 61 deletions(-) diff --git a/drivers/i2c/i2c-atr.c b/drivers/i2c/i2c-atr.c index 6b4cf979b86e0dcbba3bd9dbb297a8b5e6216dd5..1a6ff47b420020feb9bf4111d49e2f3f1dae46e7 100644 --- a/drivers/i2c/i2c-atr.c +++ b/drivers/i2c/i2c-atr.c @@ -16,6 +16,7 @@ #include #include #include +#include #define ATR_MAX_ADAPTERS 100 /* Just a sanity limit */ #define ATR_MAX_SYMLINK_LEN 11 /* Longest name is 10 chars: "channel-99" */ @@ -27,9 +28,17 @@ * @alias: I2C alias address assigned by the driver. * This is the address that will be used to issue I2C transactions * on the parent (physical) bus. + * @fixed: Alias pair cannot be replaced during dynamic address attachment. + * This flag is necessary for situations where a single I2C transaction + * contains more distinct target addresses than the ATR channel can handle. + * It marks addresses that have already been attached to an alias so + * that their alias pair is not evicted by a subsequent address in the same + * transaction. + * */ struct i2c_atr_alias_pair { struct list_head node; + bool fixed; u16 addr; u16 alias; }; @@ -58,6 +67,7 @@ struct i2c_atr_alias_pool { * @adap: The &struct i2c_adapter for the channel * @atr: The parent I2C ATR * @chan_id: The ID of this channel + * @alias_pairs_lock: Mutex protecting @alias_pairs * @alias_pairs: List of @struct i2c_atr_alias_pair containing the * assigned aliases * @alias_pool: Pool of available client aliases @@ -71,6 +81,8 @@ struct i2c_atr_chan { struct i2c_atr *atr; u32 chan_id; + /* Lock alias_pairs during attach/detach */ + struct mutex alias_pairs_lock; struct list_head alias_pairs; struct i2c_atr_alias_pool *alias_pool; @@ -155,16 +167,132 @@ static void i2c_atr_free_alias_pool(struct i2c_atr_alias_pool *alias_pool) kfree(alias_pool); } +/* Must be called with alias_pairs_lock held */ +static struct i2c_atr_alias_pair *i2c_atr_create_c2a(struct i2c_atr_chan *chan, + u16 alias, u16 addr) +{ + struct i2c_atr_alias_pair *c2a; + + lockdep_assert_held(&chan->alias_pairs_lock); + + c2a = kzalloc(sizeof(*c2a), GFP_KERNEL); + if (!c2a) + return NULL; + + c2a->addr = addr; + c2a->alias = alias; + + list_add(&c2a->node, &chan->alias_pairs); + + return c2a; +} + +/* Must be called with alias_pairs_lock held */ +static void i2c_atr_destroy_c2a(struct i2c_atr_alias_pair **pc2a) +{ + list_del(&(*pc2a)->node); + kfree(*pc2a); + *pc2a = NULL; +} + +static int i2c_atr_reserve_alias(struct i2c_atr_alias_pool *alias_pool) +{ + unsigned long idx; + u16 alias; + + spin_lock(&alias_pool->lock); + + idx = find_first_zero_bit(alias_pool->use_mask, alias_pool->size); + if (idx >= alias_pool->size) { + spin_unlock(&alias_pool->lock); + return -EBUSY; + } + + set_bit(idx, alias_pool->use_mask); + + alias = alias_pool->aliases[idx]; + + spin_unlock(&alias_pool->lock); + return alias; +} + +static void i2c_atr_release_alias(struct i2c_atr_alias_pool *alias_pool, u16 alias) +{ + unsigned int idx; + + spin_lock(&alias_pool->lock); + + for (idx = 0; idx < alias_pool->size; ++idx) { + if (alias_pool->aliases[idx] == alias) { + clear_bit(idx, alias_pool->use_mask); + spin_unlock(&alias_pool->lock); + return; + } + } + + spin_unlock(&alias_pool->lock); +} + +/* Must be called with alias_pairs_lock held */ static struct i2c_atr_alias_pair * -i2c_atr_find_mapping_by_addr(const struct list_head *list, u16 phys_addr) +i2c_atr_find_mapping_by_addr(struct i2c_atr_chan *chan, u16 addr) { + struct i2c_atr *atr = chan->atr; struct i2c_atr_alias_pair *c2a; + struct list_head *alias_pairs; + u16 alias; + int ret; + + lockdep_assert_held(&chan->alias_pairs_lock); - list_for_each_entry(c2a, list, node) { - if (c2a->addr == phys_addr) + alias_pairs = &chan->alias_pairs; + + list_for_each_entry(c2a, alias_pairs, node) { + if (c2a->addr == addr) return c2a; } + ret = i2c_atr_reserve_alias(chan->alias_pool); + if (ret < 0) { + // If no free aliases are left, replace an existing one + if (unlikely(list_empty(alias_pairs))) + return NULL; + + list_for_each_entry_reverse(c2a, alias_pairs, node) + if (!c2a->fixed) + break; + + if (c2a->fixed) + return NULL; + + atr->ops->detach_addr(atr, chan->chan_id, c2a->addr); + c2a->addr = addr; + + // Move updated entry to beginning of list + list_move(&c2a->node, alias_pairs); + + alias = c2a->alias; + } else { + alias = ret; + + c2a = i2c_atr_create_c2a(chan, alias, addr); + if (!c2a) + goto err_release_alias; + } + + ret = atr->ops->attach_addr(atr, chan->chan_id, c2a->addr, c2a->alias); + if (ret) { + dev_err(atr->dev, "failed to attach 0x%02x on channel %d: err %d\n", + addr, chan->chan_id, ret); + goto err_del_c2a; + } + + return c2a; + +err_del_c2a: + i2c_atr_destroy_c2a(&c2a); +err_release_alias: + i2c_atr_release_alias(chan->alias_pool, alias); return NULL; } @@ -180,7 +308,7 @@ static int i2c_atr_map_msgs(struct i2c_atr_chan *chan, struct i2c_msg *msgs, { struct i2c_atr *atr = chan->atr; static struct i2c_atr_alias_pair *c2a; - int i; + int i, ret = 0; /* Ensure we have enough room to save the original addresses */ if (unlikely(chan->orig_addrs_size < num)) { @@ -196,11 +324,13 @@ static int i2c_atr_map_msgs(struct i2c_atr_chan *chan, struct i2c_msg *msgs, chan->orig_addrs_size = num; } + mutex_lock(&chan->alias_pairs_lock); + for (i = 0; i < num; i++) { chan->orig_addrs[i] = msgs[i].addr; - c2a = i2c_atr_find_mapping_by_addr(&chan->alias_pairs, - msgs[i].addr); + c2a = i2c_atr_find_mapping_by_addr(chan, msgs[i].addr); + if (!c2a) { dev_err(atr->dev, "client 0x%02x not mapped!\n", msgs[i].addr); @@ -208,13 +338,19 @@ static int i2c_atr_map_msgs(struct i2c_atr_chan *chan, struct i2c_msg *msgs, while (i--) msgs[i].addr = chan->orig_addrs[i]; - return -ENXIO; + ret = -ENXIO; + goto out_unlock; } + // Prevent c2a from being overwritten by another client in this transaction + c2a->fixed = true; + msgs[i].addr = c2a->alias; } - return 0; +out_unlock: + mutex_unlock(&chan->alias_pairs_lock); + return ret; } /* @@ -227,10 +363,24 @@ static int i2c_atr_map_msgs(struct i2c_atr_chan *chan, struct i2c_msg *msgs, static void i2c_atr_unmap_msgs(struct i2c_atr_chan *chan, struct i2c_msg *msgs, int num) { + struct i2c_atr_alias_pair *c2a; int i; for (i = 0; i < num; i++) msgs[i].addr = chan->orig_addrs[i]; + + mutex_lock(&chan->alias_pairs_lock); + + if (unlikely(list_empty(&chan->alias_pairs))) + goto out_unlock; + + // unfix c2a entries so that subsequent transfers can reuse their aliases + list_for_each_entry(c2a, &chan->alias_pairs, node) { + c2a->fixed = false; + } + +out_unlock: + mutex_unlock(&chan->alias_pairs_lock); } static int i2c_atr_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, @@ -268,14 +418,23 @@ static int i2c_atr_smbus_xfer(struct i2c_adapter *adap, u16 addr, struct i2c_atr *atr = chan->atr; struct i2c_adapter *parent = atr->parent; struct i2c_atr_alias_pair *c2a; + u16 alias; + + mutex_lock(&chan->alias_pairs_lock); + + c2a = i2c_atr_find_mapping_by_addr(chan, addr); - c2a = i2c_atr_find_mapping_by_addr(&chan->alias_pairs, addr); if (!c2a) { dev_err(atr->dev, "client 0x%02x not mapped!\n", addr); + mutex_unlock(&chan->alias_pairs_lock); return -ENXIO; } - return i2c_smbus_xfer(parent, c2a->alias, flags, read_write, command, + alias = c2a->alias; + + mutex_unlock(&chan->alias_pairs_lock); + + return i2c_smbus_xfer(parent, alias, flags, read_write, command, size, data); } @@ -317,44 +476,6 @@ static const struct i2c_lock_operations i2c_atr_lock_ops = { .unlock_bus = i2c_atr_unlock_bus, }; -static int i2c_atr_reserve_alias(struct i2c_atr_alias_pool *alias_pool) -{ - unsigned long idx; - u16 alias; - - spin_lock(&alias_pool->lock); - - idx = find_first_zero_bit(alias_pool->use_mask, alias_pool->size); - if (idx >= alias_pool->size) { - spin_unlock(&alias_pool->lock); - return -EBUSY; - } - - set_bit(idx, alias_pool->use_mask); - - alias = alias_pool->aliases[idx]; - - spin_unlock(&alias_pool->lock); - return alias; -} - -static void i2c_atr_release_alias(struct i2c_atr_alias_pool *alias_pool, u16 alias) -{ - unsigned int idx; - - spin_lock(&alias_pool->lock); - - for (idx = 0; idx < alias_pool->size; ++idx) { - if (alias_pool->aliases[idx] == alias) { - clear_bit(idx, alias_pool->use_mask); - spin_unlock(&alias_pool->lock); - return; - } - } - - spin_unlock(&alias_pool->lock); -} - static int i2c_atr_attach_addr(struct i2c_adapter *adapter, u16 addr) { @@ -372,7 +493,9 @@ static int i2c_atr_attach_addr(struct i2c_adapter *adapter, alias = ret; - c2a = kzalloc(sizeof(*c2a), GFP_KERNEL); + mutex_lock(&chan->alias_pairs_lock); + + c2a = i2c_atr_create_c2a(chan, alias, addr); if (!c2a) { ret = -ENOMEM; goto err_release_alias; @@ -380,22 +503,19 @@ static int i2c_atr_attach_addr(struct i2c_adapter *adapter, ret = atr->ops->attach_addr(atr, chan->chan_id, addr, alias); if (ret) - goto err_free; + goto err_del_c2a; dev_dbg(atr->dev, "chan%u: using alias 0x%02x for addr 0x%02x\n", chan->chan_id, alias, addr); - c2a->addr = addr; - c2a->alias = alias; - list_add(&c2a->node, &chan->alias_pairs); - - return 0; + goto out_unlock; -err_free: - kfree(c2a); +err_del_c2a: + i2c_atr_destroy_c2a(&c2a); err_release_alias: i2c_atr_release_alias(chan->alias_pool, alias); - +out_unlock: + mutex_unlock(&chan->alias_pairs_lock); return ret; } @@ -408,21 +528,25 @@ static void i2c_atr_detach_addr(struct i2c_adapter *adapter, atr->ops->detach_addr(atr, chan->chan_id, addr); - c2a = i2c_atr_find_mapping_by_addr(&chan->alias_pairs, addr); + mutex_lock(&chan->alias_pairs_lock); + + c2a = i2c_atr_find_mapping_by_addr(chan, addr); if (!c2a) { /* This should never happen */ dev_warn(atr->dev, "Unable to find address mapping\n"); + mutex_unlock(&chan->alias_pairs_lock); return; } + mutex_unlock(&chan->alias_pairs_lock); + i2c_atr_release_alias(chan->alias_pool, c2a->alias); dev_dbg(atr->dev, "chan%u: detached alias 0x%02x from addr 0x%02x\n", chan->chan_id, c2a->alias, addr); - list_del(&c2a->node); - kfree(c2a); + i2c_atr_destroy_c2a(&c2a); } static int i2c_atr_bus_notifier_call(struct notifier_block *nb, @@ -633,6 +757,7 @@ int i2c_atr_add_adapter(struct i2c_atr *atr, struct i2c_atr_adap_desc *desc) chan->atr = atr; chan->chan_id = chan_id; INIT_LIST_HEAD(&chan->alias_pairs); + mutex_init(&chan->alias_pairs_lock); mutex_init(&chan->orig_addrs_lock); snprintf(chan->adap.name, sizeof(chan->adap.name), "i2c-%d-atr-%d", @@ -709,6 +834,7 @@ int i2c_atr_add_adapter(struct i2c_atr *atr, struct i2c_atr_adap_desc *desc) err_fwnode_put: fwnode_handle_put(dev_fwnode(&chan->adap.dev)); mutex_destroy(&chan->orig_addrs_lock); + mutex_destroy(&chan->alias_pairs_lock); kfree(chan); return ret; } @@ -745,6 +871,7 @@ void i2c_atr_del_adapter(struct i2c_atr *atr, u32 chan_id) fwnode_handle_put(fwnode); mutex_destroy(&chan->orig_addrs_lock); + mutex_destroy(&chan->alias_pairs_lock); kfree(chan->orig_addrs); kfree(chan); } From patchwork Tue Feb 4 09:29:20 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Romain Gantois X-Patchwork-Id: 861979 Received: from relay4-d.mail.gandi.net (relay4-d.mail.gandi.net [217.70.183.196]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 16F0120CCFD; Tue, 4 Feb 2025 09:29:31 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.70.183.196 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1738661380; cv=none; b=feds/7jKajHvgtBk7hJWGZ81N5Ofb+iKtv02O6OEpagPzsYZmEb8GYZe6KmXq6HHVioKnHzve2C4PP6MMYn/D8nZD34B77VYUDO8ePXqf+VnhEyC/6uGnfJdwx66M6+sav25uTRGl5WeJD7Z86TYRSmG/H+RIaWCGJ76eN013/c= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1738661380; c=relaxed/simple; bh=LwaS+b4K6app9iaYHwpPqauV502DqwowxBGf28dPgg4=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=sRBAI4CpBcBqvSC6pX0lSfDszK0xgo9Uda2F1IUfSbuEiZ02xpoqVy0AMIIIDi8zYzyawxiBqlL5CApt44EKo/rBwtM0F6ZaWdNrcXIQCxos+CNkXMM9lcB0R5CbUO2QEcPwB3Ob9DcrBrDpAVHb4Gl4amsDDaZgxxZrckVI/dg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=bootlin.com; spf=pass smtp.mailfrom=bootlin.com; dkim=pass (2048-bit key) header.d=bootlin.com header.i=@bootlin.com header.b=pdeQimmz; arc=none smtp.client-ip=217.70.183.196 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=bootlin.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=bootlin.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=bootlin.com header.i=@bootlin.com header.b="pdeQimmz" Received: by mail.gandi.net (Postfix) with ESMTPSA id 8467444360; Tue, 4 Feb 2025 09:29:28 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=gm1; t=1738661369; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=H2bA0INaMx9C3B656GQashh98wscRjOmWtNYma2Epn8=; b=pdeQimmzyZG0p5wlNe7MCy1VrMP5BfzeIF80Zctuk4pmr5S+nzP1+5eGMqb+pUSbbMG5+/ NxFxcS1v/sE4l107SMuUPd935cU2D32/wK6P+FixQ0HD1sfstaJU5uubLRXgvIbZvIghKc Cy71u3qA4iJYiA9CIyGenaafRj/8SlPBEJwOEdPlRQbhR3NKVXR4b6Pehpd+N7ZVcTUlYI Cqtrhup9Qck60OyPvRLaFU2dKOjU48P9Gu3gxwkNKV19iGa+hmC8V8G7AmRGozrwjYdemz th7xEoIAGyisOV4YD8mksPNQrx4ADQLnbUivT8Bfvg8Ctd1RRSQ3tzdXksUrhw== From: Romain Gantois Date: Tue, 04 Feb 2025 10:29:20 +0100 Subject: [PATCH v7 9/9] misc: add FPC202 dual port controller driver Precedence: bulk X-Mailing-List: linux-media@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20250204-fpc202-v7-9-78b4b8a35cf1@bootlin.com> References: <20250204-fpc202-v7-0-78b4b8a35cf1@bootlin.com> In-Reply-To: <20250204-fpc202-v7-0-78b4b8a35cf1@bootlin.com> To: Wolfram Sang , Tomi Valkeinen , Luca Ceresoli , Andi Shyti , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Derek Kiernan , Dragan Cvetic , Arnd Bergmann , Greg Kroah-Hartman , Mauro Carvalho Chehab , Linus Walleij , Bartosz Golaszewski Cc: Thomas Petazzoni , Kory Maincent , linux-i2c@vger.kernel.org, linux-kernel@vger.kernel.org, devicetree@vger.kernel.org, linux-media@vger.kernel.org, linux-gpio@vger.kernel.org, Romain Gantois X-Mailer: b4 0.14.2 X-GND-State: clean X-GND-Score: -100 X-GND-Cause: gggruggvucftvghtrhhoucdtuddrgeefvddrtddtgddvtdduhecutefuodetggdotefrodftvfcurfhrohhfihhlvgemucfitefpfffkpdcuggftfghnshhusghstghrihgsvgenuceurghilhhouhhtmecufedtudenucesvcftvggtihhpihgvnhhtshculddquddttddmnecujfgurhephfffufggtgfgkfhfjgfvvefosehtjeertdertdejnecuhfhrohhmpeftohhmrghinhcuifgrnhhtohhishcuoehrohhmrghinhdrghgrnhhtohhishessghoohhtlhhinhdrtghomheqnecuggftrfgrthhtvghrnhepkeelieefteelffeuheevtdetkefhfffhteffkefgtefhkeevudeutdeugfffheegnecukfhppeeltddrkeelrdduieefrdduvdejnecuvehluhhsthgvrhfuihiivgepfeenucfrrghrrghmpehinhgvthepledtrdekledrudeifedruddvjedphhgvlhhopegludelvddrudeikedrtddrudefngdpmhgrihhlfhhrohhmpehrohhmrghinhdrghgrnhhtohhishessghoohhtlhhinhdrtghomhdpnhgspghrtghpthhtohepvddvpdhrtghpthhtoheprghrnhgusegrrhhnuggsrdguvgdprhgtphhtthhopehrohhmrghinhdrghgrnhhtohhishessghoohhtlhhinhdrtghomhdprhgtphhtthhopehthhhomhgrshdrphgvthgriiiiohhnihessghoohhtlhhinhdrtghomhdprhgtphhtthhopeguvghrvghkrdhkihgvrhhnrghnsegrmhgurdgtohhmpdhrtghpthhtohepthhomhhirdhvrghlkhgvihhnvghns ehiuggvrghsohhnsghorghrugdrtghomhdprhgtphhtthhopegurhgrghgrnhdrtghvvghtihgtsegrmhgurdgtohhmpdhrtghpthhtohepuggvvhhitggvthhrvggvsehvghgvrhdrkhgvrhhnvghlrdhorhhgpdhrtghpthhtoheprghnughirdhshhihthhisehkvghrnhgvlhdrohhrgh X-GND-Sasl: romain.gantois@bootlin.com The TI FPC202 dual port controller serves as a low-speed signal aggregator for common port types such as SFP, QSFP, Mini-SAS HD, and others. It aggregates GPIO and I2C signals across two downstream ports, acting as both a GPIO controller and an I2C address translator for up to two logical devices per port. Reviewed-by: Linus Walleij Signed-off-by: Romain Gantois --- MAINTAINERS | 1 + drivers/misc/Kconfig | 11 ++ drivers/misc/Makefile | 1 + drivers/misc/ti_fpc202.c | 438 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 451 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 4bb13de2b8ebad5180e2bde607dac40f35c51782..7d2b08d0b0ad5390eb8528eb7ddc9da8b2729026 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -23714,6 +23714,7 @@ M: Romain Gantois L: linux-kernel@vger.kernel.org S: Maintained F: Documentation/devicetree/bindings/misc/ti,fpc202.yaml +F: drivers/misc/ti_fpc202.c TI FPD-LINK DRIVERS M: Tomi Valkeinen diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 56bc72c7ce4a998f6b9b3ed90a0845b52715f405..e4a95a5e7efee908da5bcaefe7df8877579d3980 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -114,6 +114,17 @@ config RPMB If unsure, select N. +config TI_FPC202 + tristate "TI FPC202 Dual Port Controller" + select GPIOLIB + depends on I2C_ATR + help + If you say yes here you get support for the Texas Instruments FPC202 + Dual Port Controller. + + This driver can also be built as a module. If so, the module will be + called fpc202. + config TIFM_CORE tristate "TI Flash Media interface support" depends on PCI diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 545aad06d088563aaddf7d2f3b82d995e817fde0..2e27d4b7590313fab8da3dc311f492359ef44ee8 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -12,6 +12,7 @@ obj-$(CONFIG_ATMEL_SSC) += atmel-ssc.o obj-$(CONFIG_DUMMY_IRQ) += dummy-irq.o obj-$(CONFIG_ICS932S401) += ics932s401.o obj-$(CONFIG_LKDTM) += lkdtm/ +obj-$(CONFIG_TI_FPC202) += ti_fpc202.o obj-$(CONFIG_TIFM_CORE) += tifm_core.o obj-$(CONFIG_TIFM_7XX1) += tifm_7xx1.o obj-$(CONFIG_PHANTOM) += phantom.o diff --git a/drivers/misc/ti_fpc202.c b/drivers/misc/ti_fpc202.c new file mode 100644 index 0000000000000000000000000000000000000000..b9c9ee4bfc4edba102b3bf7ae33a2b68c19c83ec --- /dev/null +++ b/drivers/misc/ti_fpc202.c @@ -0,0 +1,438 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * ti_fpc202.c - FPC202 Dual Port Controller driver + * + * Copyright (C) 2024 Bootlin + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#define FPC202_NUM_PORTS 2 +#define FPC202_ALIASES_PER_PORT 2 + +/* + * GPIO: port mapping + * + * 0: P0_S0_IN_A + * 1: P0_S1_IN_A + * 2: P1_S0_IN_A + * 3: P1_S1_IN_A + * 4: P0_S0_IN_B + * ... + * 8: P0_S0_IN_C + * ... + * 12: P0_S0_OUT_A + * ... + * 16: P0_S0_OUT_B + * ... + * 19: P1_S1_OUT_B + * + */ + +#define FPC202_GPIO_COUNT 20 +#define FPC202_GPIO_P0_S0_IN_B 4 +#define FPC202_GPIO_P0_S0_OUT_A 12 + +#define FPC202_REG_IN_A_INT 0x6 +#define FPC202_REG_IN_C_IN_B 0x7 +#define FPC202_REG_OUT_A_OUT_B 0x8 + +#define FPC202_REG_OUT_A_OUT_B_VAL 0xa + +#define FPC202_REG_MOD_DEV(port, dev) (0xb4 + ((port) * 4) + (dev)) +#define FPC202_REG_AUX_DEV(port, dev) (0xb6 + ((port) * 4) + (dev)) + +/* + * The FPC202 doesn't support turning off address translation on a single port. + * So just set an invalid I2C address as the translation target when no client + * address is attached. + */ +#define FPC202_REG_DEV_INVALID 0 + +/* Even aliases are assigned to device 0 and odd aliases to device 1 */ +#define fpc202_dev_num_from_alias(alias) ((alias) % 2) + +struct fpc202_priv { + struct i2c_client *client; + struct i2c_atr *atr; + struct gpio_desc *en_gpio; + struct gpio_chip gpio; + + /* Lock REG_MOD/AUX_DEV and addr_caches during attach/detach */ + struct mutex reg_dev_lock; + + /* Cached device addresses for both ports and their devices */ + u8 addr_caches[2][2]; + + /* Keep track of which ports were probed */ + DECLARE_BITMAP(probed_ports, FPC202_NUM_PORTS); +}; + +static void fpc202_fill_alias_table(struct i2c_client *client, u16 *aliases, int port_id) +{ + u16 first_alias; + int i; + + /* + * There is a predefined list of aliases for each FPC202 I2C + * self-address. This allows daisy-chained FPC202 units to + * automatically take on different sets of aliases. + * Each port of an FPC202 unit is assigned two aliases from this list. + */ + first_alias = 0x10 + 4 * port_id + 8 * ((u16)client->addr - 2); + + for (i = 0; i < FPC202_ALIASES_PER_PORT; i++) + aliases[i] = first_alias + i; +} + +static int fpc202_gpio_get_dir(int offset) +{ + return offset < FPC202_GPIO_P0_S0_OUT_A ? GPIO_LINE_DIRECTION_IN : GPIO_LINE_DIRECTION_OUT; +} + +static int fpc202_read(struct fpc202_priv *priv, u8 reg) +{ + int val; + + val = i2c_smbus_read_byte_data(priv->client, reg); + return val; +} + +static int fpc202_write(struct fpc202_priv *priv, u8 reg, u8 value) +{ + return i2c_smbus_write_byte_data(priv->client, reg, value); +} + +static void fpc202_set_enable(struct fpc202_priv *priv, int enable) +{ + if (!priv->en_gpio) + return; + + gpiod_set_value(priv->en_gpio, enable); +} + +static void fpc202_gpio_set(struct gpio_chip *chip, unsigned int offset, + int value) +{ + struct fpc202_priv *priv = gpiochip_get_data(chip); + int ret; + u8 val; + + if (fpc202_gpio_get_dir(offset) == GPIO_LINE_DIRECTION_IN) + return; + + ret = fpc202_read(priv, FPC202_REG_OUT_A_OUT_B_VAL); + if (ret < 0) { + dev_err(&priv->client->dev, "Failed to set GPIO %d value! err %d\n", offset, ret); + return; + } + + val = (u8)ret; + + if (value) + val |= BIT(offset - FPC202_GPIO_P0_S0_OUT_A); + else + val &= ~BIT(offset - FPC202_GPIO_P0_S0_OUT_A); + + fpc202_write(priv, FPC202_REG_OUT_A_OUT_B_VAL, val); +} + +static int fpc202_gpio_get(struct gpio_chip *chip, unsigned int offset) +{ + struct fpc202_priv *priv = gpiochip_get_data(chip); + u8 reg, bit; + int ret; + + if (offset < FPC202_GPIO_P0_S0_IN_B) { + reg = FPC202_REG_IN_A_INT; + bit = BIT(4 + offset); + } else if (offset < FPC202_GPIO_P0_S0_OUT_A) { + reg = FPC202_REG_IN_C_IN_B; + bit = BIT(offset - FPC202_GPIO_P0_S0_IN_B); + } else { + reg = FPC202_REG_OUT_A_OUT_B_VAL; + bit = BIT(offset - FPC202_GPIO_P0_S0_OUT_A); + } + + ret = fpc202_read(priv, reg); + if (ret < 0) + return ret; + + return !!(((u8)ret) & bit); +} + +static int fpc202_gpio_direction_input(struct gpio_chip *chip, unsigned int offset) +{ + if (fpc202_gpio_get_dir(offset) == GPIO_LINE_DIRECTION_OUT) + return -EINVAL; + + return 0; +} + +static int fpc202_gpio_direction_output(struct gpio_chip *chip, unsigned int offset, + int value) +{ + struct fpc202_priv *priv = gpiochip_get_data(chip); + int ret; + u8 val; + + if (fpc202_gpio_get_dir(offset) == GPIO_LINE_DIRECTION_IN) + return -EINVAL; + + fpc202_gpio_set(chip, offset, value); + + ret = fpc202_read(priv, FPC202_REG_OUT_A_OUT_B); + if (ret < 0) + return ret; + + val = (u8)ret | BIT(offset - FPC202_GPIO_P0_S0_OUT_A); + + return fpc202_write(priv, FPC202_REG_OUT_A_OUT_B, val); +} + +/* + * Set the translation table entry associated with a port and device number. + * + * Each downstream port of the FPC202 has two fixed aliases corresponding to + * device numbers 0 and 1. If one of these aliases is found in an incoming I2C + * transfer, it will be translated to the address given by the corresponding + * translation table entry. + */ +static int fpc202_write_dev_addr(struct fpc202_priv *priv, u32 port_id, int dev_num, u16 addr) +{ + int ret, reg_mod, reg_aux; + u8 val; + + guard(mutex)(&priv->reg_dev_lock); + + reg_mod = FPC202_REG_MOD_DEV(port_id, dev_num); + reg_aux = FPC202_REG_AUX_DEV(port_id, dev_num); + val = addr & 0x7f; + + ret = fpc202_write(priv, reg_mod, val); + if (ret) + return ret; + + /* + * The FPC202 datasheet is unclear about the role of the AUX registers. + * Empirically, writing to them as well seems to be necessary for + * address translation to function properly. + */ + ret = fpc202_write(priv, reg_aux, val); + + priv->addr_caches[port_id][dev_num] = val; + + return ret; +} + +static int fpc202_attach_addr(struct i2c_atr *atr, u32 chan_id, + u16 addr, u16 alias) +{ + struct fpc202_priv *priv = i2c_atr_get_driver_data(atr); + + dev_dbg(&priv->client->dev, "attaching address 0x%02x to alias 0x%02x\n", addr, alias); + + return fpc202_write_dev_addr(priv, chan_id, fpc202_dev_num_from_alias(alias), addr); +} + +static void fpc202_detach_addr(struct i2c_atr *atr, u32 chan_id, + u16 addr) +{ + struct fpc202_priv *priv = i2c_atr_get_driver_data(atr); + int dev_num, reg_mod, val; + + for (dev_num = 0; dev_num < 2; dev_num++) { + reg_mod = FPC202_REG_MOD_DEV(chan_id, dev_num); + + mutex_lock(&priv->reg_dev_lock); + + val = priv->addr_caches[chan_id][dev_num]; + + mutex_unlock(&priv->reg_dev_lock); + + if (val < 0) { + dev_err(&priv->client->dev, "failed to read register 0x%x while detaching address 0x%02x\n", + reg_mod, addr); + return; + } + + if (val == (addr & 0x7f)) { + fpc202_write_dev_addr(priv, chan_id, dev_num, FPC202_REG_DEV_INVALID); + return; + } + } +} + +static const struct i2c_atr_ops fpc202_atr_ops = { + .attach_addr = fpc202_attach_addr, + .detach_addr = fpc202_detach_addr, +}; + +static int fpc202_probe_port(struct fpc202_priv *priv, struct device_node *i2c_handle, int port_id) +{ + u16 aliases[FPC202_ALIASES_PER_PORT] = { }; + struct device *dev = &priv->client->dev; + struct i2c_atr_adap_desc desc = { }; + int ret = 0; + + desc.chan_id = port_id; + desc.parent = dev; + desc.bus_handle = of_node_to_fwnode(i2c_handle); + desc.num_aliases = FPC202_ALIASES_PER_PORT; + + fpc202_fill_alias_table(priv->client, aliases, port_id); + desc.aliases = aliases; + + ret = i2c_atr_add_adapter(priv->atr, &desc); + if (ret) + return ret; + + set_bit(port_id, priv->probed_ports); + + ret = fpc202_write_dev_addr(priv, port_id, 0, FPC202_REG_DEV_INVALID); + if (ret) + return ret; + + return fpc202_write_dev_addr(priv, port_id, 1, FPC202_REG_DEV_INVALID); +} + +static void fpc202_remove_port(struct fpc202_priv *priv, int port_id) +{ + i2c_atr_del_adapter(priv->atr, port_id); + clear_bit(port_id, priv->probed_ports); +} + +static int fpc202_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct device_node *i2c_handle; + struct fpc202_priv *priv; + int ret, port_id; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + mutex_init(&priv->reg_dev_lock); + + priv->client = client; + i2c_set_clientdata(client, priv); + + priv->en_gpio = devm_gpiod_get_optional(dev, "enable", GPIOD_OUT_HIGH); + if (IS_ERR(priv->en_gpio)) { + ret = PTR_ERR(priv->en_gpio); + dev_err(dev, "failed to fetch enable GPIO! err %d\n", ret); + goto destroy_mutex; + } + + priv->gpio.label = "gpio-fpc202"; + priv->gpio.base = -1; + priv->gpio.direction_input = fpc202_gpio_direction_input; + priv->gpio.direction_output = fpc202_gpio_direction_output; + priv->gpio.set = fpc202_gpio_set; + priv->gpio.get = fpc202_gpio_get; + priv->gpio.ngpio = FPC202_GPIO_COUNT; + priv->gpio.parent = dev; + priv->gpio.owner = THIS_MODULE; + + ret = gpiochip_add_data(&priv->gpio, priv); + if (ret) { + priv->gpio.parent = NULL; + dev_err(dev, "failed to add gpiochip err %d\n", ret); + goto disable_gpio; + } + + priv->atr = i2c_atr_new(client->adapter, dev, &fpc202_atr_ops, 2); + if (IS_ERR(priv->atr)) { + ret = PTR_ERR(priv->atr); + dev_err(dev, "failed to create i2c atr err %d\n", ret); + goto disable_gpio; + } + + i2c_atr_set_driver_data(priv->atr, priv); + + bitmap_zero(priv->probed_ports, FPC202_NUM_PORTS); + + for_each_child_of_node(dev->of_node, i2c_handle) { + ret = of_property_read_u32(i2c_handle, "reg", &port_id); + if (ret) { + if (ret == -EINVAL) + continue; + + dev_err(dev, "failed to read 'reg' property of child node, err %d\n", ret); + goto unregister_chans; + } + + if (port_id > FPC202_NUM_PORTS) { + dev_err(dev, "port ID %d is out of range!\n", port_id); + ret = -EINVAL; + goto unregister_chans; + } + + ret = fpc202_probe_port(priv, i2c_handle, port_id); + if (ret) { + dev_err(dev, "Failed to probe port %d, err %d\n", port_id, ret); + goto unregister_chans; + } + } + + goto out; + +unregister_chans: + for_each_set_bit(port_id, priv->probed_ports, FPC202_NUM_PORTS) + fpc202_remove_port(priv, port_id); + + i2c_atr_delete(priv->atr); +disable_gpio: + fpc202_set_enable(priv, 0); + gpiochip_remove(&priv->gpio); +destroy_mutex: + mutex_destroy(&priv->reg_dev_lock); +out: + return ret; +} + +static void fpc202_remove(struct i2c_client *client) +{ + struct fpc202_priv *priv = i2c_get_clientdata(client); + int port_id; + + for_each_set_bit(port_id, priv->probed_ports, FPC202_NUM_PORTS) + fpc202_remove_port(priv, port_id); + + mutex_destroy(&priv->reg_dev_lock); + + i2c_atr_delete(priv->atr); + + fpc202_set_enable(priv, 0); + gpiochip_remove(&priv->gpio); +} + +static const struct of_device_id fpc202_of_match[] = { + { .compatible = "ti,fpc202" }, + {} +}; +MODULE_DEVICE_TABLE(of, fpc202_of_match); + +static struct i2c_driver fpc202_driver = { + .driver = { + .name = "fpc202", + .of_match_table = fpc202_of_match, + }, + .probe = fpc202_probe, + .remove = fpc202_remove, +}; + +module_i2c_driver(fpc202_driver); + +MODULE_AUTHOR("Romain Gantois "); +MODULE_DESCRIPTION("TI FPC202 Dual Port Controller driver"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("I2C_ATR");