From patchwork Thu Mar 6 16:23:22 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Romain Gantois X-Patchwork-Id: 871023 Received: from relay8-d.mail.gandi.net (relay8-d.mail.gandi.net [217.70.183.201]) (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 E33E4212D7D; Thu, 6 Mar 2025 16:23:36 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.70.183.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1741278219; cv=none; b=WwZEmf5A7iRkNIS9STRg+Z5YHq74h+gy8i6LXTnkYm5i0uW4Sh46DxXL5iAijTiuIlnyTyXSbELFTiosQT2y7mmbtqATNtUnjG/3V+vS3ROFlP8dWSC/ILclHNjYElk+pldJITCk906GSXKRYLvh+IJZ/vT4EBnxFA4Bm43vZmw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1741278219; c=relaxed/simple; bh=RWqPCbickcf3NeEvAFuVv/cebJHAevvYYSffWUo/kxs=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=Mdt4pphUVoAlJG5dCLx6JEHaelus6AekFpZmFi9Ao8wu9PqbdC+o0m1yYcJkNQrBfHAk8FkESrwPFk4cS2v/m3lpfaq8DTkm/KhRwJFp97gBwAUVmLovZCz/fWg6luYEzMCMduJ/15OWPsKgXaaZUh5lqpPEPqe1fqffPAfSfmM= 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=I4zUO+hG; arc=none smtp.client-ip=217.70.183.201 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="I4zUO+hG" Received: by mail.gandi.net (Postfix) with ESMTPSA id 1E27B44405; Thu, 6 Mar 2025 16:23:34 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=gm1; t=1741278215; 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=Z9Dw8MEw6E4S3LOI35bfz6vBrtEq+quc3+6ULKl0+OY=; b=I4zUO+hGTe5CQ5tU0DxGmkHBoPFfp55F+XgS4+FriXo9RUIIq7FgHRs2kjvhnEUe1Mc07I ZYiJAfZrJgEoCYTOj7k0ozJeRLfljpYg+YOEZdWCBEtCNpBlhbj/8WC/GC/Z9vasyGBwIJ l10+6ttDLF7o/O/cK5bFpetfiNMhoU8IWp5DeZ/kQu2mvkSzttJa0x6DbDCoW0aLYZAAFA 1mmRTaeI60YTDjt0ugulZII7MqXqAhK8v4fcU1POSkj3jBJypMvsT62DBilApBLYMlvj+P NgAMHeEXclFHI3Qan5YP2BLZ1NsvYdAP2ndU7Hb7MieVFsPFxsDS/jYKcBn8jw== From: Romain Gantois Date: Thu, 06 Mar 2025 17:23:22 +0100 Subject: [PATCH v9 1/9] dt-bindings: misc: Describe TI FPC202 dual port controller Precedence: bulk X-Mailing-List: linux-media@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20250306-fpc202-v9-1-2779af6780f6@bootlin.com> References: <20250306-fpc202-v9-0-2779af6780f6@bootlin.com> In-Reply-To: <20250306-fpc202-v9-0-2779af6780f6@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 , Cosmin Tanislav , 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 , Conor Dooley X-Mailer: b4 0.14.2 X-GND-State: clean X-GND-Score: -100 X-GND-Cause: gggruggvucftvghtrhhoucdtuddrgeefvddrtddtgddutdekvdefucetufdoteggodetrfdotffvucfrrhhofhhilhgvmecuifetpfffkfdpucggtfgfnhhsuhgsshgtrhhisggvnecuuegrihhlohhuthemuceftddunecusecvtfgvtghiphhivghnthhsucdlqddutddtmdenucfjughrpefhfffugggtgffkfhgjvfevofesthejredtredtjeenucfhrhhomheptfhomhgrihhnucfirghnthhoihhsuceorhhomhgrihhnrdhgrghnthhoihhssegsohhothhlihhnrdgtohhmqeenucggtffrrghtthgvrhhnpeeitefhjeeljeeuudelffevjeelfeekieehfffffeekgfettdeivddvtdefteeghfenucffohhmrghinhepuggvvhhitggvthhrvggvrdhorhhgnecukfhppeeltddrkeelrdduieefrdduvdejnecuvehluhhsthgvrhfuihiivgeptdenucfrrghrrghmpehinhgvthepledtrdekledrudeifedruddvjedphhgvlhhopegludelvddrudeikedrtddrudefngdpmhgrihhlfhhrohhmpehrohhmrghinhdrghgrnhhtohhishessghoohhtlhhinhdrtghomhdpnhgspghrtghpthhtohepvdegpdhrtghpthhtoheprghnughirdhshhihthhisehkvghrnhgvlhdrohhrghdprhgtphhtthhopehluhgtrgdrtggvrhgvshholhhisegsohhothhlihhnrdgtohhmpdhrtghpthhtohepthhhohhmrghsrdhpvghtrgiiiihonhhisegsohhothhlihhnrdgtohhmpdhrtghpthhtoheprghrnhgusegrrhhnuggsrdguv gdprhgtphhtthhopeguvghmohhnshhinhhguhhrsehgmhgrihhlrdgtohhmpdhrtghpthhtoheplhhinhhugidqghhpihhosehvghgvrhdrkhgvrhhnvghlrdhorhhgpdhrtghpthhtoheprhhomhgrihhnrdhgrghnthhoihhssegsohhothhlihhnrdgtohhmpdhrtghpthhtohepghhrvghgkhhhsehlihhnuhigfhhouhhnuggrthhiohhnrdhorhhg X-GND-Sasl: romain.gantois@bootlin.com The FPC202 dual port controller serves as a low speed signal aggregator for common port types, notably SFP. It provides access to I2C and low-speed GPIO signals of a downstream device through a single upstream control interface. Up to two logical I2C addresses can be accessed on each of the FPC202's ports. The port controller acts as an I2C translator (ATR). It converts addresses of incoming and outgoing I2C transactions. One use case of this is accessing two SFP modules at logical address 0x50 from the same upstream I2C controller, using two different client aliases. Reviewed-by: Conor Dooley Tested-by: Tomi Valkeinen Signed-off-by: Romain Gantois --- .../devicetree/bindings/misc/ti,fpc202.yaml | 94 ++++++++++++++++++++++ MAINTAINERS | 6 ++ 2 files changed, 100 insertions(+) diff --git a/Documentation/devicetree/bindings/misc/ti,fpc202.yaml b/Documentation/devicetree/bindings/misc/ti,fpc202.yaml new file mode 100644 index 0000000000000000000000000000000000000000..a8cb10f2d0df3fa5224f95f6f87467a8bce253bc --- /dev/null +++ b/Documentation/devicetree/bindings/misc/ti,fpc202.yaml @@ -0,0 +1,94 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/misc/ti,fpc202.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: TI FPC202 dual port controller with expanded IOs + +maintainers: + - Romain Gantois + +allOf: + - $ref: /schemas/i2c/i2c-atr.yaml# + +properties: + compatible: + const: ti,fpc202 + + reg: + maxItems: 1 + + gpio-controller: true + + "#gpio-cells": + const: 2 + + enable-gpios: + description: + Specifier for the GPIO connected to the EN pin. + maxItems: 1 + + '#address-cells': + const: 1 + + '#size-cells': + const: 0 + +patternProperties: + "^i2c@[0-1]$": + $ref: /schemas/i2c/i2c-controller.yaml# + description: Downstream device ports 0 and 1 + + properties: + reg: + maxItems: 1 + description: + Downstream port ID + + required: + - "#address-cells" + - "#size-cells" + - reg + + unevaluatedProperties: false + +required: + - compatible + - reg + - gpio-controller + - "#gpio-cells" + - "#address-cells" + - "#size-cells" + +unevaluatedProperties: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + i2c-atr@f { + compatible = "ti,fpc202"; + reg = <0xf>; + #address-cells = <1>; + #size-cells = <0>; + + gpio-controller; + #gpio-cells = <2>; + + i2c@0 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0>; + }; + + i2c@1 { + #address-cells = <1>; + #size-cells = <0>; + reg = <1>; + }; + }; + }; +... diff --git a/MAINTAINERS b/MAINTAINERS index 896a307fa06545e2861abe46ea7029f9b4d3628e..4bb13de2b8ebad5180e2bde607dac40f35c51782 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -23709,6 +23709,12 @@ F: drivers/misc/tifm* F: drivers/mmc/host/tifm_sd.c F: include/linux/tifm.h +TI FPC202 DUAL PORT CONTROLLER +M: Romain Gantois +L: linux-kernel@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/misc/ti,fpc202.yaml + TI FPD-LINK DRIVERS M: Tomi Valkeinen L: linux-media@vger.kernel.org From patchwork Thu Mar 6 16:23:24 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Romain Gantois X-Patchwork-Id: 871022 Received: from relay8-d.mail.gandi.net (relay8-d.mail.gandi.net [217.70.183.201]) (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 37DCC208973; Thu, 6 Mar 2025 16:23:38 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.70.183.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1741278221; cv=none; b=HYBRI5VGR10YsadXdZjXGV0w67NuyZWOJ4wJan4x2ITTqAG5Y1UCVt5Z4KPrf/EUcYDL5iOOu3nMYTnR1+QMKZGLzvEuq56zddqOrldtkF7FepweyLyRX75xL197J75YlCp7KjxruBMq2poESLdPfZbYPS/V+zgGfbCVpXJuBE4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1741278221; c=relaxed/simple; bh=BBtj3Mwf7zGRNgb0IC5jOBEMA+1tBFrQ51xMHJp6tUs=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=HhDbBTLdkTG34a9+Uyi2dB+uDY4OJOCg4QeY3QvlsA73Mqi3nxeilM6KBR33C1HRf9hoopmsh/ouyQtl5SUgW/lNry88Rl3/8OXSGVJUWosI3dJIFZaVIVW5raI+fGYEv8S16By7Tye0hSzhbQwrpQP8ZoICagJmKo8fqWyLlvY= 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=IMMaOGWt; arc=none smtp.client-ip=217.70.183.201 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="IMMaOGWt" Received: by mail.gandi.net (Postfix) with ESMTPSA id 6EF7244416; Thu, 6 Mar 2025 16:23:36 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=gm1; t=1741278217; 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=aE8uWqg0n8aCFliC3uASiOBweWjYLEMfGVFEkQo2L90=; b=IMMaOGWt9aXnMVd2VJxiftGOE/ICNmBM5kdlYIKi59ocImqH2fhUZD0I8Y/+kCyViTKDF1 Y3TVGRQUvkNYRVLlxtclHVFT95XY1W+F1vIvp3kD8uoWpxTmPEubHolVabyWDexcmUjSKP OA9j32pG++pblC+dvK/x/x4pUSDdyhK+LDXJMs4wopGsEaucAd65U8frXYuFK0qnNowylQ E8UU2r4x+rh6PtqlzvX+f5aIhpj4gzl9OUYWWw7R6NKA7z2S/cHy+2jHyixU7ZSJum0jER wESeOOs6gXbm+V+Zhom23R1dcFNA1Hxod5sDXyLW+b1D8YNzn1MkzzAP8Y/QHw== From: Romain Gantois Date: Thu, 06 Mar 2025 17:23:24 +0100 Subject: [PATCH v9 3/9] media: i2c: ds90ub960: Protect alias_use_mask with a mutex Precedence: bulk X-Mailing-List: linux-media@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20250306-fpc202-v9-3-2779af6780f6@bootlin.com> References: <20250306-fpc202-v9-0-2779af6780f6@bootlin.com> In-Reply-To: <20250306-fpc202-v9-0-2779af6780f6@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 , Cosmin Tanislav , 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: gggruggvucftvghtrhhoucdtuddrgeefvddrtddtgddutdekvdefucetufdoteggodetrfdotffvucfrrhhofhhilhgvmecuifetpfffkfdpucggtfgfnhhsuhgsshgtrhhisggvnecuuegrihhlohhuthemuceftddunecusecvtfgvtghiphhivghnthhsucdlqddutddtmdenucfjughrpefhfffugggtgffkfhgjvfevofesthejredtredtjeenucfhrhhomheptfhomhgrihhnucfirghnthhoihhsuceorhhomhgrihhnrdhgrghnthhoihhssegsohhothhlihhnrdgtohhmqeenucggtffrrghtthgvrhhnpeekleeifeetleffueehvedtteekhfffhfetffekgfethfekveduuedtuefgffehgeenucfkphepledtrdekledrudeifedruddvjeenucevlhhushhtvghrufhiiigvpedtnecurfgrrhgrmhepihhnvghtpeeltddrkeelrdduieefrdduvdejpdhhvghloheplgduledvrdduieekrddtrddufegnpdhmrghilhhfrhhomheprhhomhgrihhnrdhgrghnthhoihhssegsohhothhlihhnrdgtohhmpdhnsggprhgtphhtthhopedvfedprhgtphhtthhopegrnhguihdrshhhhihtiheskhgvrhhnvghlrdhorhhgpdhrtghpthhtoheplhhutggrrdgtvghrvghsohhlihessghoohhtlhhinhdrtghomhdprhgtphhtthhopehthhhomhgrshdrphgvthgriiiiohhnihessghoohhtlhhinhdrtghomhdprhgtphhtthhopegrrhhnugesrghrnhgusgdruggvpdhrtghpthhtohepuggvmhhonhhsihhnghhurhesghhmr ghilhdrtghomhdprhgtphhtthhopehlihhnuhigqdhgphhiohesvhhgvghrrdhkvghrnhgvlhdrohhrghdprhgtphhtthhopehrohhmrghinhdrghgrnhhtohhishessghoohhtlhhinhdrtghomhdprhgtphhtthhopehgrhgvghhkhheslhhinhhugihfohhunhgurghtihhonhdrohhrgh X-GND-Sasl: romain.gantois@bootlin.com The aliased_addrs list represents the occupation of an RX port's hardware alias table. This list and the underlying hardware table are only accessed in the attach/detach_client() callbacks. These functions are only called from a bus notifier handler in i2c-atr.c, which is always called with the notifier chain's semaphore held. This indirectly prevents concurrent access to the aliased_addrs list. However, more explicit and direct locking is preferable. Moreover, with the introduction of dynamic address translation in a future patch, the attach/detach_client() callbacks will be called from outside of the notifier chain's read section. Introduce a mutex to protect access to the aliased_addrs list and its underlying hardware table. Tested-by: Tomi Valkeinen Signed-off-by: Romain Gantois --- drivers/media/i2c/ds90ub960.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/media/i2c/ds90ub960.c b/drivers/media/i2c/ds90ub960.c index e1d53ef087538f7df2c992612e70ce6a3e24906b..5969692480409a1632ac05e43c58df479982bdd5 100644 --- a/drivers/media/i2c/ds90ub960.c +++ b/drivers/media/i2c/ds90ub960.c @@ -27,6 +27,7 @@ */ #include +#include #include #include #include @@ -478,6 +479,8 @@ struct ub960_rxport { }; } eq; + /* lock for aliased_addrs and associated registers */ + struct mutex aliased_addrs_lock; u16 aliased_addrs[UB960_MAX_PORT_ALIASES]; }; @@ -1054,6 +1057,8 @@ static int ub960_atr_attach_client(struct i2c_atr *atr, u32 chan_id, struct device *dev = &priv->client->dev; unsigned int reg_idx; + 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]) break; @@ -1085,6 +1090,8 @@ static void ub960_atr_detach_client(struct i2c_atr *atr, u32 chan_id, struct device *dev = &priv->client->dev; unsigned int reg_idx; + 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) break; @@ -3236,6 +3243,8 @@ static void ub960_rxport_free_ports(struct ub960_data *priv) fwnode_handle_put(rxport->source.ep_fwnode); fwnode_handle_put(rxport->ser.fwnode); + mutex_destroy(&rxport->aliased_addrs_lock); + kfree(rxport); priv->rxports[nport] = NULL; } @@ -3456,6 +3465,8 @@ static int ub960_parse_dt_rxport(struct ub960_data *priv, unsigned int nport, if (ret) goto err_put_remote_fwnode; + mutex_init(&rxport->aliased_addrs_lock); + return 0; err_put_remote_fwnode: From patchwork Thu Mar 6 16:23:26 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Romain Gantois X-Patchwork-Id: 871021 Received: from relay8-d.mail.gandi.net (relay8-d.mail.gandi.net [217.70.183.201]) (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 630D5213250; Thu, 6 Mar 2025 16:23:40 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.70.183.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1741278224; cv=none; b=V+qs0bQovnXlAcVnRPATETkJWD9u/kAB6XTyX5AjifEoRPDBHp4b5jpg7xpr6RurGpcC2XNCW314L2IlqsbIvfRwtW3h5zlZ0r56GdzuK5tQ7YXeR+P0QoFZlSbGk4Rt43HliJ7lfch3hZQhF4IlnJ7MPML6Db9G4v7gKVjo7Ps= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1741278224; c=relaxed/simple; bh=miWNQeLEF+TJJG1hhT06+JSPlwM/q23BQjinziZlgk0=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=El86pT5wBsdDaHxPwyxMwy4SOOd/I9zBEHsVwLvQMW3D7iyOOswB8BIQoltQf9quP26WyPD0l8DoNLRDpis2tuQhJIO+/iLEQEa490/0H/nXZdTb2+KTB08X5+4DPGtfFkY+imufug04b6rjFoIgnEOEqluwZbncuBi1lDrRgd0= 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=jcIzJfTp; arc=none smtp.client-ip=217.70.183.201 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="jcIzJfTp" Received: by mail.gandi.net (Postfix) with ESMTPSA id 8695E44423; Thu, 6 Mar 2025 16:23:38 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=gm1; t=1741278219; 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=fDr6LfQ7VXGfsPittLkmpVPAnoW1OuxOv+TQ6VIURpE=; b=jcIzJfTpZNnY3KZqy7R+51fvNgcLnk6AkcZy6Sp6M9TVxWoKGRzgRDS89FYW+upCma3Fqs 1odtG9z10X32c5OhDwlMZZfkoLSZlQGJBxLA5h/Xtjh/DlhOCXCmOoR3CpH+8AOHaDUqMR GIpG7anDR+T6XXZbXGI3WoMBGk90Ao55w3EDyHay2jqbsdOY5IYmKJcNdGS8nwhSCM2Fo5 s9xKEIdhYTeeA0QoRtbG7nYq3JTBwr95yR5edkUg9g73SQx82BavTiclaDCxHcPS1ogUSf C25Gik3OQCK00OVgqyAchFKD3Lf4tQOVXSezI4CZTK76nM7YRE7FTeWn7RFZfA== From: Romain Gantois Date: Thu, 06 Mar 2025 17:23:26 +0100 Subject: [PATCH v9 5/9] i2c: move ATR alias pool to a separate struct Precedence: bulk X-Mailing-List: linux-media@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20250306-fpc202-v9-5-2779af6780f6@bootlin.com> References: <20250306-fpc202-v9-0-2779af6780f6@bootlin.com> In-Reply-To: <20250306-fpc202-v9-0-2779af6780f6@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 , Cosmin Tanislav , 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: gggruggvucftvghtrhhoucdtuddrgeefvddrtddtgddutdekvdefucetufdoteggodetrfdotffvucfrrhhofhhilhgvmecuifetpfffkfdpucggtfgfnhhsuhgsshgtrhhisggvnecuuegrihhlohhuthemuceftddunecusecvtfgvtghiphhivghnthhsucdlqddutddtmdenucfjughrpefhfffugggtgffkfhgjvfevofesthejredtredtjeenucfhrhhomheptfhomhgrihhnucfirghnthhoihhsuceorhhomhgrihhnrdhgrghnthhoihhssegsohhothhlihhnrdgtohhmqeenucggtffrrghtthgvrhhnpeekleeifeetleffueehvedtteekhfffhfetffekgfethfekveduuedtuefgffehgeenucfkphepledtrdekledrudeifedruddvjeenucevlhhushhtvghrufhiiigvpedtnecurfgrrhgrmhepihhnvghtpeeltddrkeelrdduieefrdduvdejpdhhvghloheplgduledvrdduieekrddtrddufegnpdhmrghilhhfrhhomheprhhomhgrihhnrdhgrghnthhoihhssegsohhothhlihhnrdgtohhmpdhnsggprhgtphhtthhopedvfedprhgtphhtthhopegrnhguihdrshhhhihtiheskhgvrhhnvghlrdhorhhgpdhrtghpthhtoheplhhutggrrdgtvghrvghsohhlihessghoohhtlhhinhdrtghomhdprhgtphhtthhopehthhhomhgrshdrphgvthgriiiiohhnihessghoohhtlhhinhdrtghomhdprhgtphhtthhopegrrhhnugesrghrnhgusgdruggvpdhrtghpthhtohepuggvmhhonhhsihhnghhurhesghhmr ghilhdrtghomhdprhgtphhtthhopehlihhnuhigqdhgphhiohesvhhgvghrrdhkvghrnhgvlhdrohhrghdprhgtphhtthhopehrohhmrghinhdrghgrnhhtohhishessghoohhtlhhinhdrtghomhdprhgtphhtthhopehgrhgvghhkhheslhhinhhugihfohhunhgurghtihhonhdrohhrgh X-GND-Sasl: romain.gantois@bootlin.com Each I2C address translator (ATR) has a pool of client aliases which can be used as translation targets. Some ATRs have a single alias pool shared by all downstream channels, while others have a separate alias pool for each channel. Currently, this alias pool is represented by the "aliases", "num_aliases", and "use_mask" fields of struct i2c_atr. In preparation for adding per-channel alias pool support, move the "aliases", "num_aliases", "use_mask" and associated lock to a new struct called "struct alias_pool". Tested-by: Tomi Valkeinen Signed-off-by: Romain Gantois --- drivers/i2c/i2c-atr.c | 172 +++++++++++++++++++++++++++++++------------------- 1 file changed, 107 insertions(+), 65 deletions(-) diff --git a/drivers/i2c/i2c-atr.c b/drivers/i2c/i2c-atr.c index 118fd7d764dce51de587e274ef452e031ccff52d..e9c6b85e3e4a4ff84fdf9ad139f05fdceb441120 100644 --- a/drivers/i2c/i2c-atr.c +++ b/drivers/i2c/i2c-atr.c @@ -34,6 +34,23 @@ struct i2c_atr_alias_pair { u16 alias; }; +/** + * struct i2c_atr_alias_pool - Pool of client aliases available for an ATR. + * @size: Total number of aliases + * + * @lock: Lock protecting @aliases and @use_mask + * @aliases: Array of aliases, must hold exactly @size elements + * @use_mask: Mask of used aliases + */ +struct i2c_atr_alias_pool { + size_t size; + + /* Protects aliases and use_mask */ + spinlock_t lock; + u16 *aliases; + unsigned long *use_mask; +}; + /** * struct i2c_atr_chan - Data for a channel. * @adap: The &struct i2c_adapter for the channel @@ -67,10 +84,7 @@ struct i2c_atr_chan { * @algo: The &struct i2c_algorithm for adapters * @lock: Lock for the I2C bus segment (see &struct i2c_lock_operations) * @max_adapters: Maximum number of adapters this I2C ATR can have - * @num_aliases: Number of aliases in the aliases array - * @aliases: The aliases array - * @alias_mask_lock: Lock protecting alias_use_mask - * @alias_use_mask: Bitmask for used aliases in aliases array + * @alias_pool: Pool of available client aliases * @i2c_nb: Notifier for remote client add & del events * @adapter: Array of adapters */ @@ -86,17 +100,54 @@ struct i2c_atr { struct mutex lock; int max_adapters; - size_t num_aliases; - const u16 *aliases; - /* Protects alias_use_mask */ - spinlock_t alias_mask_lock; - unsigned long *alias_use_mask; + struct i2c_atr_alias_pool *alias_pool; struct notifier_block i2c_nb; struct i2c_adapter *adapter[] __counted_by(max_adapters); }; +static struct i2c_atr_alias_pool *i2c_atr_alloc_alias_pool(size_t num_aliases) +{ + struct i2c_atr_alias_pool *alias_pool; + int ret; + + alias_pool = kzalloc(sizeof(*alias_pool), GFP_KERNEL); + if (!alias_pool) + return ERR_PTR(-ENOMEM); + + alias_pool->size = num_aliases; + + alias_pool->aliases = kcalloc(num_aliases, sizeof(*alias_pool->aliases), GFP_KERNEL); + if (!alias_pool->aliases) { + ret = -ENOMEM; + goto err_free_alias_pool; + } + + alias_pool->use_mask = bitmap_zalloc(num_aliases, GFP_KERNEL); + if (!alias_pool->use_mask) { + ret = -ENOMEM; + goto err_free_aliases; + } + + spin_lock_init(&alias_pool->lock); + + return alias_pool; + +err_free_aliases: + kfree(alias_pool->aliases); +err_free_alias_pool: + kfree(alias_pool); + return ERR_PTR(ret); +} + +static void i2c_atr_free_alias_pool(struct i2c_atr_alias_pool *alias_pool) +{ + bitmap_free(alias_pool->use_mask); + kfree(alias_pool->aliases); + kfree(alias_pool); +} + static struct i2c_atr_alias_pair * i2c_atr_find_mapping_by_addr(const struct list_head *list, u16 phys_addr) { @@ -259,44 +310,42 @@ 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 *atr) +static int i2c_atr_reserve_alias(struct i2c_atr_alias_pool *alias_pool) { unsigned long idx; + u16 alias; - spin_lock(&atr->alias_mask_lock); + spin_lock(&alias_pool->lock); - idx = find_first_zero_bit(atr->alias_use_mask, atr->num_aliases); - if (idx >= atr->num_aliases) { - spin_unlock(&atr->alias_mask_lock); - dev_err(atr->dev, "failed to find a free alias\n"); + 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, atr->alias_use_mask); + set_bit(idx, alias_pool->use_mask); - spin_unlock(&atr->alias_mask_lock); + alias = alias_pool->aliases[idx]; - return atr->aliases[idx]; + spin_unlock(&alias_pool->lock); + return alias; } -static void i2c_atr_release_alias(struct i2c_atr *atr, u16 alias) +static void i2c_atr_release_alias(struct i2c_atr_alias_pool *alias_pool, u16 alias) { unsigned int idx; - spin_lock(&atr->alias_mask_lock); + spin_lock(&alias_pool->lock); - for (idx = 0; idx < atr->num_aliases; ++idx) { - if (atr->aliases[idx] == alias) { - clear_bit(idx, atr->alias_use_mask); - spin_unlock(&atr->alias_mask_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(&atr->alias_mask_lock); - - /* This should never happen */ - dev_warn(atr->dev, "Unable to find mapped alias\n"); + spin_unlock(&alias_pool->lock); } static int i2c_atr_attach_addr(struct i2c_adapter *adapter, @@ -308,9 +357,11 @@ static int i2c_atr_attach_addr(struct i2c_adapter *adapter, u16 alias; int ret; - ret = i2c_atr_reserve_alias(atr); - if (ret < 0) + ret = i2c_atr_reserve_alias(atr->alias_pool); + if (ret < 0) { + dev_err(atr->dev, "failed to find a free alias\n"); return ret; + } alias = ret; @@ -336,7 +387,7 @@ static int i2c_atr_attach_addr(struct i2c_adapter *adapter, err_free: kfree(c2a); err_release_alias: - i2c_atr_release_alias(atr, alias); + i2c_atr_release_alias(atr->alias_pool, alias); return ret; } @@ -357,7 +408,7 @@ static void i2c_atr_detach_addr(struct i2c_adapter *adapter, return; } - i2c_atr_release_alias(atr, c2a->alias); + i2c_atr_release_alias(atr->alias_pool, c2a->alias); dev_dbg(atr->dev, "chan%u: detached alias 0x%02x from addr 0x%02x\n", @@ -411,12 +462,11 @@ static int i2c_atr_bus_notifier_call(struct notifier_block *nb, static int i2c_atr_parse_alias_pool(struct i2c_atr *atr) { + struct i2c_atr_alias_pool *alias_pool; struct device *dev = atr->dev; - unsigned long *alias_use_mask; size_t num_aliases; unsigned int i; u32 *aliases32; - u16 *aliases16; int ret; ret = fwnode_property_count_u32(dev_fwnode(dev), "i2c-alias-pool"); @@ -428,12 +478,23 @@ static int i2c_atr_parse_alias_pool(struct i2c_atr *atr) num_aliases = ret; - if (!num_aliases) + alias_pool = i2c_atr_alloc_alias_pool(num_aliases); + if (IS_ERR(alias_pool)) { + ret = PTR_ERR(alias_pool); + dev_err(dev, "Failed to allocate alias pool, err %d\n", ret); + return ret; + } + + atr->alias_pool = alias_pool; + + if (!alias_pool->size) return 0; aliases32 = kcalloc(num_aliases, sizeof(*aliases32), GFP_KERNEL); - if (!aliases32) - return -ENOMEM; + if (!aliases32) { + ret = -ENOMEM; + goto err_free_alias_pool; + } ret = fwnode_property_read_u32_array(dev_fwnode(dev), "i2c-alias-pool", aliases32, num_aliases); @@ -443,43 +504,27 @@ static int i2c_atr_parse_alias_pool(struct i2c_atr *atr) goto err_free_aliases32; } - aliases16 = kcalloc(num_aliases, sizeof(*aliases16), GFP_KERNEL); - if (!aliases16) { - ret = -ENOMEM; - goto err_free_aliases32; - } - for (i = 0; i < num_aliases; i++) { if (!(aliases32[i] & 0xffff0000)) { - aliases16[i] = aliases32[i]; + alias_pool->aliases[i] = aliases32[i]; continue; } dev_err(dev, "Failed to parse 'i2c-alias-pool' property: I2C flags are not supported\n"); ret = -EINVAL; - goto err_free_aliases16; - } - - alias_use_mask = bitmap_zalloc(num_aliases, GFP_KERNEL); - if (!alias_use_mask) { - ret = -ENOMEM; - goto err_free_aliases16; + goto err_free_aliases32; } kfree(aliases32); - atr->num_aliases = num_aliases; - atr->aliases = aliases16; - atr->alias_use_mask = alias_use_mask; - - dev_dbg(dev, "i2c-alias-pool has %zu aliases", atr->num_aliases); + dev_dbg(dev, "i2c-alias-pool has %zu aliases\n", alias_pool->size); return 0; -err_free_aliases16: - kfree(aliases16); err_free_aliases32: kfree(aliases32); +err_free_alias_pool: + i2c_atr_free_alias_pool(alias_pool); return ret; } @@ -500,7 +545,6 @@ struct i2c_atr *i2c_atr_new(struct i2c_adapter *parent, struct device *dev, return ERR_PTR(-ENOMEM); mutex_init(&atr->lock); - spin_lock_init(&atr->alias_mask_lock); atr->parent = parent; atr->dev = dev; @@ -520,13 +564,12 @@ struct i2c_atr *i2c_atr_new(struct i2c_adapter *parent, struct device *dev, atr->i2c_nb.notifier_call = i2c_atr_bus_notifier_call; ret = bus_register_notifier(&i2c_bus_type, &atr->i2c_nb); if (ret) - goto err_free_aliases; + goto err_free_alias_pool; return atr; -err_free_aliases: - bitmap_free(atr->alias_use_mask); - kfree(atr->aliases); +err_free_alias_pool: + i2c_atr_free_alias_pool(atr->alias_pool); err_destroy_mutex: mutex_destroy(&atr->lock); kfree(atr); @@ -543,8 +586,7 @@ void i2c_atr_delete(struct i2c_atr *atr) WARN_ON(atr->adapter[i]); bus_unregister_notifier(&i2c_bus_type, &atr->i2c_nb); - bitmap_free(atr->alias_use_mask); - kfree(atr->aliases); + i2c_atr_free_alias_pool(atr->alias_pool); mutex_destroy(&atr->lock); kfree(atr); } From patchwork Thu Mar 6 16:23:28 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Romain Gantois X-Patchwork-Id: 871020 Received: from relay8-d.mail.gandi.net (relay8-d.mail.gandi.net [217.70.183.201]) (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 1E470218ABC; Thu, 6 Mar 2025 16:23:42 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.70.183.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1741278226; cv=none; b=WNzGc7ccst08adOR5XMsQbBSvriqioitFz7s0O28wtFq/pCWtm8sJJEzyqrW5/I88mgnH6HWpQX8KlO3Z2mJDmDHhIwIIr7DGuSAeXUB+bRz+H8uU7TmE4KQ/eqoKli0XN0mV64q1w0ibA8AYB8oIU2CUe2UBINsYgMyUrP18Ks= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1741278226; c=relaxed/simple; bh=byQ1v1WemAOrq7sHY2y8XotMbXGbqAd6duRIAaGgDwc=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=kGIw5VoYOo58byV8hU5GjYuL86x7w/ud9SG+eQgFQtH2xOnO15pV87xy09lGCAf9+mLaEU/XpAhxOxwVb479yUtnxQJ2nWG1B18G/t/N61/gwnwfNjSZsu5p0X3nVOmQo27a8qiGVUY9QrX98c9q+Hpz4igU+9T8MEMkjDVo9OI= 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=omPWLzcw; arc=none smtp.client-ip=217.70.183.201 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="omPWLzcw" Received: by mail.gandi.net (Postfix) with ESMTPSA id A2DBF44405; Thu, 6 Mar 2025 16:23:40 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=gm1; t=1741278221; 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=NgwVBrcimznBgx/J82PmPsppfFhjLBx16YGaKkknw2A=; b=omPWLzcwni25otVQdZ8nukUATqZ3p4j1etyPDrLdo3/VQ1V5QSzjtxQq88cyRyXTBzmAjY m9Gk2/XdfQTZtXOKTA1F2c5YTVgv+ONTbUHjywCxFqqhBS8WbYlhYjCYF302jfilizt1zH onGxU3Dzieq+itneCZZ7UCTmC27thOhdJ/M8qAz3SV4P8bLyBaJYowJ+/cu+Eq0JKm8BGH f15PP0kg8HY5Jh/koR2nXVI0qM49PoWGMhXJ1jxvk7A2PHa7vGttw2bQNZjq3UWL9un7JF ZeYRp0WTHZEUledKUObr0Y6XZ9ZUvz5AP6goFL21/0tt7T+KiDw7r6NMsg9Fng== From: Romain Gantois Date: Thu, 06 Mar 2025 17:23:28 +0100 Subject: [PATCH v9 7/9] i2c: support per-channel ATR alias pools Precedence: bulk X-Mailing-List: linux-media@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20250306-fpc202-v9-7-2779af6780f6@bootlin.com> References: <20250306-fpc202-v9-0-2779af6780f6@bootlin.com> In-Reply-To: <20250306-fpc202-v9-0-2779af6780f6@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 , Cosmin Tanislav , 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: gggruggvucftvghtrhhoucdtuddrgeefvddrtddtgddutdekvdefucetufdoteggodetrfdotffvucfrrhhofhhilhgvmecuifetpfffkfdpucggtfgfnhhsuhgsshgtrhhisggvnecuuegrihhlohhuthemuceftddunecusecvtfgvtghiphhivghnthhsucdlqddutddtmdenucfjughrpefhfffugggtgffkfhgjvfevofesthejredtredtjeenucfhrhhomheptfhomhgrihhnucfirghnthhoihhsuceorhhomhgrihhnrdhgrghnthhoihhssegsohhothhlihhnrdgtohhmqeenucggtffrrghtthgvrhhnpeekleeifeetleffueehvedtteekhfffhfetffekgfethfekveduuedtuefgffehgeenucfkphepledtrdekledrudeifedruddvjeenucevlhhushhtvghrufhiiigvpeehnecurfgrrhgrmhepihhnvghtpeeltddrkeelrdduieefrdduvdejpdhhvghloheplgduledvrdduieekrddtrddufegnpdhmrghilhhfrhhomheprhhomhgrihhnrdhgrghnthhoihhssegsohhothhlihhnrdgtohhmpdhnsggprhgtphhtthhopedvfedprhgtphhtthhopegrnhguihdrshhhhihtiheskhgvrhhnvghlrdhorhhgpdhrtghpthhtoheplhhutggrrdgtvghrvghsohhlihessghoohhtlhhinhdrtghomhdprhgtphhtthhopehthhhomhgrshdrphgvthgriiiiohhnihessghoohhtlhhinhdrtghomhdprhgtphhtthhopegrrhhnugesrghrnhgusgdruggvpdhrtghpthhtohepuggvmhhonhhsihhnghhurhesghhmr ghilhdrtghomhdprhgtphhtthhopehlihhnuhigqdhgphhiohesvhhgvghrrdhkvghrnhgvlhdrohhrghdprhgtphhtthhopehrohhmrghinhdrghgrnhhtohhishessghoohhtlhhinhdrtghomhdprhgtphhtthhopehgrhgvghhkhheslhhinhhugihfohhunhgurghtihhonhdrohhrgh X-GND-Sasl: romain.gantois@bootlin.com Some I2C address translators (ATRs) assign each of their remote peripheral aliases to a specific channel. To properly handle these devices, add support for having separate alias pools for each ATR channel. This is achieved by allowing callers of i2c_atr_add_adapter to pass an optional alias list. If present, this list will be used to populate the channel's alias pool. Otherwise, the common alias pool will be used. Tested-by: Tomi Valkeinen Signed-off-by: Romain Gantois --- drivers/i2c/i2c-atr.c | 74 ++++++++++++++++++++++++++++++------------- drivers/media/i2c/ds90ub913.c | 9 ++++-- drivers/media/i2c/ds90ub953.c | 9 ++++-- include/linux/i2c-atr.h | 34 ++++++++++++++------ 4 files changed, 91 insertions(+), 35 deletions(-) diff --git a/drivers/i2c/i2c-atr.c b/drivers/i2c/i2c-atr.c index 01f8c6f6e68a0a6012101a201c3fd8c7c0f25b47..6b4cf979b86e0dcbba3bd9dbb297a8b5e6216dd5 100644 --- a/drivers/i2c/i2c-atr.c +++ b/drivers/i2c/i2c-atr.c @@ -37,6 +37,7 @@ struct i2c_atr_alias_pair { /** * struct i2c_atr_alias_pool - Pool of client aliases available for an ATR. * @size: Total number of aliases + * @shared: Indicates if this alias pool is shared by multiple channels * * @lock: Lock protecting @aliases and @use_mask * @aliases: Array of aliases, must hold exactly @size elements @@ -44,6 +45,7 @@ struct i2c_atr_alias_pair { */ struct i2c_atr_alias_pool { size_t size; + bool shared; /* Protects aliases and use_mask */ spinlock_t lock; @@ -58,6 +60,8 @@ struct i2c_atr_alias_pool { * @chan_id: The ID of this channel * @alias_pairs: List of @struct i2c_atr_alias_pair containing the * assigned aliases + * @alias_pool: Pool of available client aliases + * * @orig_addrs_lock: Mutex protecting @orig_addrs * @orig_addrs: Buffer used to store the original addresses during transmit * @orig_addrs_size: Size of @orig_addrs @@ -68,6 +72,7 @@ struct i2c_atr_chan { u32 chan_id; struct list_head alias_pairs; + struct i2c_atr_alias_pool *alias_pool; /* Lock orig_addrs during xfer */ struct mutex orig_addrs_lock; @@ -84,7 +89,7 @@ struct i2c_atr_chan { * @algo: The &struct i2c_algorithm for adapters * @lock: Lock for the I2C bus segment (see &struct i2c_lock_operations) * @max_adapters: Maximum number of adapters this I2C ATR can have - * @alias_pool: Pool of available client aliases + * @alias_pool: Optional common pool of available client aliases * @i2c_nb: Notifier for remote client add & del events * @adapter: Array of adapters */ @@ -107,7 +112,7 @@ struct i2c_atr { struct i2c_adapter *adapter[] __counted_by(max_adapters); }; -static struct i2c_atr_alias_pool *i2c_atr_alloc_alias_pool(size_t num_aliases) +static struct i2c_atr_alias_pool *i2c_atr_alloc_alias_pool(size_t num_aliases, bool shared) { struct i2c_atr_alias_pool *alias_pool; int ret; @@ -130,6 +135,8 @@ static struct i2c_atr_alias_pool *i2c_atr_alloc_alias_pool(size_t num_aliases) goto err_free_aliases; } + alias_pool->shared = shared; + spin_lock_init(&alias_pool->lock); return alias_pool; @@ -357,7 +364,7 @@ static int i2c_atr_attach_addr(struct i2c_adapter *adapter, u16 alias; int ret; - ret = i2c_atr_reserve_alias(atr->alias_pool); + ret = i2c_atr_reserve_alias(chan->alias_pool); if (ret < 0) { dev_err(atr->dev, "failed to find a free alias\n"); return ret; @@ -387,7 +394,7 @@ static int i2c_atr_attach_addr(struct i2c_adapter *adapter, err_free: kfree(c2a); err_release_alias: - i2c_atr_release_alias(atr->alias_pool, alias); + i2c_atr_release_alias(chan->alias_pool, alias); return ret; } @@ -408,7 +415,7 @@ static void i2c_atr_detach_addr(struct i2c_adapter *adapter, return; } - i2c_atr_release_alias(atr->alias_pool, c2a->alias); + i2c_atr_release_alias(chan->alias_pool, c2a->alias); dev_dbg(atr->dev, "chan%u: detached alias 0x%02x from addr 0x%02x\n", @@ -469,16 +476,20 @@ static int i2c_atr_parse_alias_pool(struct i2c_atr *atr) u32 *aliases32; int ret; - ret = fwnode_property_count_u32(dev_fwnode(dev), "i2c-alias-pool"); - if (ret < 0) { - dev_err(dev, "Failed to count 'i2c-alias-pool' property: %d\n", - ret); - return ret; - } + if (!fwnode_property_present(dev_fwnode(dev), "i2c-alias-pool")) { + num_aliases = 0; + } else { + ret = fwnode_property_count_u32(dev_fwnode(dev), "i2c-alias-pool"); + if (ret < 0) { + dev_err(dev, "Failed to count 'i2c-alias-pool' property: %d\n", + ret); + return ret; + } - num_aliases = ret; + num_aliases = ret; + } - alias_pool = i2c_atr_alloc_alias_pool(num_aliases); + alias_pool = i2c_atr_alloc_alias_pool(num_aliases, true); if (IS_ERR(alias_pool)) { ret = PTR_ERR(alias_pool); dev_err(dev, "Failed to allocate alias pool, err %d\n", ret); @@ -592,15 +603,15 @@ void i2c_atr_delete(struct i2c_atr *atr) } EXPORT_SYMBOL_NS_GPL(i2c_atr_delete, "I2C_ATR"); -int i2c_atr_add_adapter(struct i2c_atr *atr, u32 chan_id, - struct device *adapter_parent, - struct fwnode_handle *bus_handle) +int i2c_atr_add_adapter(struct i2c_atr *atr, struct i2c_atr_adap_desc *desc) { + struct fwnode_handle *bus_handle = desc->bus_handle; struct i2c_adapter *parent = atr->parent; + char symlink_name[ATR_MAX_SYMLINK_LEN]; struct device *dev = atr->dev; + u32 chan_id = desc->chan_id; struct i2c_atr_chan *chan; - char symlink_name[ATR_MAX_SYMLINK_LEN]; - int ret; + int ret, idx; if (chan_id >= atr->max_adapters) { dev_err(dev, "No room for more i2c-atr adapters\n"); @@ -616,8 +627,8 @@ int i2c_atr_add_adapter(struct i2c_atr *atr, u32 chan_id, if (!chan) return -ENOMEM; - if (!adapter_parent) - adapter_parent = dev; + if (!desc->parent) + desc->parent = dev; chan->atr = atr; chan->chan_id = chan_id; @@ -629,7 +640,7 @@ int i2c_atr_add_adapter(struct i2c_atr *atr, u32 chan_id, chan->adap.owner = THIS_MODULE; chan->adap.algo = &atr->algo; chan->adap.algo_data = chan; - chan->adap.dev.parent = adapter_parent; + chan->adap.dev.parent = desc->parent; chan->adap.retries = parent->retries; chan->adap.timeout = parent->timeout; chan->adap.quirks = parent->quirks; @@ -656,13 +667,26 @@ int i2c_atr_add_adapter(struct i2c_atr *atr, u32 chan_id, fwnode_handle_put(atr_node); } + if (desc->num_aliases > 0) { + chan->alias_pool = i2c_atr_alloc_alias_pool(desc->num_aliases, false); + if (IS_ERR(chan->alias_pool)) { + ret = PTR_ERR(chan->alias_pool); + goto err_fwnode_put; + } + + for (idx = 0; idx < desc->num_aliases; idx++) + chan->alias_pool->aliases[idx] = desc->aliases[idx]; + } else { + chan->alias_pool = atr->alias_pool; + } + atr->adapter[chan_id] = &chan->adap; ret = i2c_add_adapter(&chan->adap); if (ret) { dev_err(dev, "failed to add atr-adapter %u (error=%d)\n", chan_id, ret); - goto err_fwnode_put; + goto err_free_alias_pool; } snprintf(symlink_name, sizeof(symlink_name), "channel-%u", @@ -679,6 +703,9 @@ int i2c_atr_add_adapter(struct i2c_atr *atr, u32 chan_id, return 0; +err_free_alias_pool: + if (!chan->alias_pool->shared) + i2c_atr_free_alias_pool(chan->alias_pool); err_fwnode_put: fwnode_handle_put(dev_fwnode(&chan->adap.dev)); mutex_destroy(&chan->orig_addrs_lock); @@ -711,6 +738,9 @@ void i2c_atr_del_adapter(struct i2c_atr *atr, u32 chan_id) i2c_del_adapter(adap); + if (!chan->alias_pool->shared) + i2c_atr_free_alias_pool(chan->alias_pool); + atr->adapter[chan_id] = NULL; fwnode_handle_put(fwnode); diff --git a/drivers/media/i2c/ds90ub913.c b/drivers/media/i2c/ds90ub913.c index fd2d2d5272bfb688f00d7bf5a109e978f6c322e6..bedfeb5f9273ac48d29b480cc7cee67e55e926a2 100644 --- a/drivers/media/i2c/ds90ub913.c +++ b/drivers/media/i2c/ds90ub913.c @@ -670,6 +670,7 @@ static int ub913_i2c_master_init(struct ub913_data *priv) static int ub913_add_i2c_adapter(struct ub913_data *priv) { struct device *dev = &priv->client->dev; + struct i2c_atr_adap_desc desc = { }; struct fwnode_handle *i2c_handle; int ret; @@ -677,8 +678,12 @@ static int ub913_add_i2c_adapter(struct ub913_data *priv) if (!i2c_handle) return 0; - ret = i2c_atr_add_adapter(priv->plat_data->atr, priv->plat_data->port, - dev, i2c_handle); + desc.chan_id = priv->plat_data->port; + desc.parent = dev; + desc.bus_handle = i2c_handle; + desc.num_aliases = 0; + + ret = i2c_atr_add_adapter(priv->plat_data->atr, &desc); fwnode_handle_put(i2c_handle); diff --git a/drivers/media/i2c/ds90ub953.c b/drivers/media/i2c/ds90ub953.c index 46569381b332de3dfd89a0720ada64cdfa3297de..11d541bdc80e054c83a8c84c2f2b8d400db74a4a 100644 --- a/drivers/media/i2c/ds90ub953.c +++ b/drivers/media/i2c/ds90ub953.c @@ -1122,6 +1122,7 @@ static int ub953_register_clkout(struct ub953_data *priv) static int ub953_add_i2c_adapter(struct ub953_data *priv) { struct device *dev = &priv->client->dev; + struct i2c_atr_adap_desc desc = { }; struct fwnode_handle *i2c_handle; int ret; @@ -1129,8 +1130,12 @@ static int ub953_add_i2c_adapter(struct ub953_data *priv) if (!i2c_handle) return 0; - ret = i2c_atr_add_adapter(priv->plat_data->atr, priv->plat_data->port, - dev, i2c_handle); + desc.chan_id = priv->plat_data->port; + desc.parent = dev; + desc.bus_handle = i2c_handle; + desc.num_aliases = 0; + + ret = i2c_atr_add_adapter(priv->plat_data->atr, &desc); fwnode_handle_put(i2c_handle); diff --git a/include/linux/i2c-atr.h b/include/linux/i2c-atr.h index 14c1f9175c0db6a8a9c6ef5d771ae68361132a76..1c3a5bcd939fc56f4a6ca1b6a5cc0ac2c17083b7 100644 --- a/include/linux/i2c-atr.h +++ b/include/linux/i2c-atr.h @@ -36,6 +36,29 @@ struct i2c_atr_ops { u16 addr); }; +/** + * struct i2c_atr_adap_desc - An ATR downstream bus descriptor + * @chan_id: Index of the new adapter (0 .. max_adapters-1). This value is + * passed to the callbacks in `struct i2c_atr_ops`. + * @parent: The device used as the parent of the new i2c adapter, or NULL + * to use the i2c-atr device as the parent. + * @bus_handle: The fwnode handle that points to the adapter's i2c + * peripherals, or NULL. + * @num_aliases: The number of aliases in this adapter's private alias pool. Set + * to zero if this adapter uses the ATR's global alias pool. + * @aliases: An optional array of private aliases used by the adapter + * instead of the ATR's global pool of aliases. Must contain + * exactly num_aliases entries if num_aliases > 0, is ignored + * otherwise. + */ +struct i2c_atr_adap_desc { + u32 chan_id; + struct device *parent; + struct fwnode_handle *bus_handle; + size_t num_aliases; + u16 *aliases; +}; + /** * i2c_atr_new() - Allocate and initialize an I2C ATR helper. * @parent: The parent (upstream) adapter @@ -65,12 +88,7 @@ void i2c_atr_delete(struct i2c_atr *atr); /** * i2c_atr_add_adapter - Create a child ("downstream") I2C bus. * @atr: The I2C ATR - * @chan_id: Index of the new adapter (0 .. max_adapters-1). This value is - * passed to the callbacks in `struct i2c_atr_ops`. - * @adapter_parent: The device used as the parent of the new i2c adapter, or NULL - * to use the i2c-atr device as the parent. - * @bus_handle: The fwnode handle that points to the adapter's i2c - * peripherals, or NULL. + * @desc: An ATR adapter descriptor * * After calling this function a new i2c bus will appear. Adding and removing * devices on the downstream bus will result in calls to the @@ -85,9 +103,7 @@ void i2c_atr_delete(struct i2c_atr *atr); * * Return: 0 on success, a negative error code otherwise. */ -int i2c_atr_add_adapter(struct i2c_atr *atr, u32 chan_id, - struct device *adapter_parent, - struct fwnode_handle *bus_handle); +int i2c_atr_add_adapter(struct i2c_atr *atr, struct i2c_atr_adap_desc *desc); /** * i2c_atr_del_adapter - Remove a child ("downstream") I2C bus added by From patchwork Thu Mar 6 16:23:29 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Romain Gantois X-Patchwork-Id: 871019 Received: from relay8-d.mail.gandi.net (relay8-d.mail.gandi.net [217.70.183.201]) (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 4330721ABC9; Thu, 6 Mar 2025 16:23:43 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.70.183.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1741278229; cv=none; b=qBnwhdx+OpasQ1ggqr157cjrxOij0rZzUvODZWkYMWlol0hMNrglFtI1cSNmNtiD5KUG8G7svnBXZvHAj+u8+njPqzoFZ8KTp3bhjqerK6qLgUdq4Ld4nzxz6Gjc2apChyJhdC4+eEHsE0jE3uRq03/4x28PnOcSU8IK/k35KEQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1741278229; c=relaxed/simple; bh=y7v/HBJVDuu9q8DusM4qYv5Q2bJxm0Rr2aK8TYfV35w=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=N1snAgE5QQ/uIdr9XgUxyAelCxhdvSPCN/A1KAkCjm+sOjxUpGznALVAPQYGlbAZXSCsgzh85wHGAgakC/YetHyZwAwHKTlU0bdP3bkEft5J7UGRFyEIP+W/Y5FETkOrNeEn8kNPXUrOj5ayIeShXBT97242ZhndUf0duBOAhFM= 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=Sy1rR09+; arc=none smtp.client-ip=217.70.183.201 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="Sy1rR09+" Received: by mail.gandi.net (Postfix) with ESMTPSA id A9C8A44433; Thu, 6 Mar 2025 16:23:41 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=gm1; t=1741278222; 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=Fvg48lHNOepnLZV282EGnJVQDc7pD4oyq+hx9SDh+A8=; b=Sy1rR09+AijucikgxglvOKsphy8e9OqVisdtehuiF+fEzVSrCyai4/woRISCxrnIH3ZXM2 U1P+5nxEsirxNeNKCcWAPgp/AHrUtIX0a5nMyLtu4m4hnhON5+Pa6EVHu+O0JblOE33GUJ LEhvW1FEKHArGqqHUDLooTGc4TSm+nZYHsWhqdMXwD9PZlOJCuknF2ihx6jH82znuW0FEz fBL8j+5wXHB7lJ5wZH6M06ArLtB7Yv/r9ofTQlzbYxtdXpSlmGcFP6PtUClE1otHlIU1/O e482LFs6npfBIk+JfG8V4V0wAQxULdSavb920hCczPCra7CKVVYnQyT9jmkwxQ== From: Romain Gantois Date: Thu, 06 Mar 2025 17:23:29 +0100 Subject: [PATCH v9 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: <20250306-fpc202-v9-8-2779af6780f6@bootlin.com> References: <20250306-fpc202-v9-0-2779af6780f6@bootlin.com> In-Reply-To: <20250306-fpc202-v9-0-2779af6780f6@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 , Cosmin Tanislav , 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: gggruggvucftvghtrhhoucdtuddrgeefvddrtddtgddutdekvdefucetufdoteggodetrfdotffvucfrrhhofhhilhgvmecuifetpfffkfdpucggtfgfnhhsuhgsshgtrhhisggvnecuuegrihhlohhuthemuceftddunecusecvtfgvtghiphhivghnthhsucdlqddutddtmdenucfjughrpefhfffugggtgffkfhgjvfevofesthejredtredtjeenucfhrhhomheptfhomhgrihhnucfirghnthhoihhsuceorhhomhgrihhnrdhgrghnthhoihhssegsohhothhlihhnrdgtohhmqeenucggtffrrghtthgvrhhnpeekleeifeetleffueehvedtteekhfffhfetffekgfethfekveduuedtuefgffehgeenucfkphepledtrdekledrudeifedruddvjeenucevlhhushhtvghrufhiiigvpeehnecurfgrrhgrmhepihhnvghtpeeltddrkeelrdduieefrdduvdejpdhhvghloheplgduledvrdduieekrddtrddufegnpdhmrghilhhfrhhomheprhhomhgrihhnrdhgrghnthhoihhssegsohhothhlihhnrdgtohhmpdhnsggprhgtphhtthhopedvfedprhgtphhtthhopegrnhguihdrshhhhihtiheskhgvrhhnvghlrdhorhhgpdhrtghpthhtoheplhhutggrrdgtvghrvghsohhlihessghoohhtlhhinhdrtghomhdprhgtphhtthhopehthhhomhgrshdrphgvthgriiiiohhnihessghoohhtlhhinhdrtghomhdprhgtphhtthhopegrrhhnugesrghrnhgusgdruggvpdhrtghpthhtohepuggvmhhonhhsihhnghhurhesghhmr ghilhdrtghomhdprhgtphhtthhopehlihhnuhigqdhgphhiohesvhhgvghrrdhkvghrnhgvlhdrohhrghdprhgtphhtthhopehrohhmrghinhdrghgrnhhtohhishessghoohhtlhhinhdrtghomhdprhgtphhtthhopehgrhgvghhkhheslhhinhhugihfohhunhgurghtihhonhdrohhrgh 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. Tested-by: Tomi Valkeinen 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..8a5d9247fd29b9dd638319bdaec0052361720854 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,10 +528,13 @@ 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; } @@ -421,8 +544,9 @@ static void i2c_atr_detach_addr(struct i2c_adapter *adapter, "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); + + mutex_unlock(&chan->alias_pairs_lock); } 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); }