linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v2 0/4] Add support for lan966 flexcom multiplexer
@ 2022-05-09  8:49 Kavyasree Kotagiri
  2022-05-09  8:49 ` [PATCH v2 1/4] dt-bindings: mfd: atmel,flexcom: Convert to json-schema Kavyasree Kotagiri
                   ` (3 more replies)
  0 siblings, 4 replies; 21+ messages in thread
From: Kavyasree Kotagiri @ 2022-05-09  8:49 UTC (permalink / raw)
  To: krzysztof.kozlowski+dt, nicolas.ferre, alexandre.belloni,
	claudiu.beznea, peda
  Cc: devicetree, linux-arm-kernel, linux-kernel, lee.jones, linux,
	Manohar.Puri, Kavyasree.Kotagiri, UNGLinuxDriver

This patch series implements driver for lan966 flexcom multiplexer.
Converts atmel-flexcom.txt bindings to yaml format and add new
compatible string for lan966 flexcom.

This patch also adds dt bindings for lan966 flexcom multiplexer.

v1 -> v2:
- addressed comments for atmel,flexcom.yaml.
- added child node and its parameters properly in flexcom bindings.
- added ref to mux-consumer.yaml.
- added ref to mux-controller.yaml in lan966-flx-mux.yaml
- added MODULE() stuff in lan966 mux driver.

Kavyasree Kotagiri (4):
  dt-bindings: mfd: atmel,flexcom: Convert to json-schema
  dt-bindings: mfd: atmel,flexcom: Add lan966 compatible string and mux
    properties
  dt-bindings: mux: Add lan966 flexcom mux controller
  mux: lan966: Add support for flexcom mux controller

 .../bindings/mfd/atmel,flexcom.yaml           | 142 ++++++++++++++++++
 .../devicetree/bindings/mfd/atmel-flexcom.txt |  63 --------
 .../mux/microchip,lan966-flx-mux.yaml         |  51 +++++++
 arch/arm/mach-at91/Kconfig                    |   2 +
 drivers/mfd/atmel-flexcom.c                   |  55 ++++++-
 drivers/mux/Kconfig                           |  12 ++
 drivers/mux/Makefile                          |   2 +
 drivers/mux/lan966-flx.c                      | 121 +++++++++++++++
 8 files changed, 384 insertions(+), 64 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/mfd/atmel,flexcom.yaml
 delete mode 100644 Documentation/devicetree/bindings/mfd/atmel-flexcom.txt
 create mode 100644 Documentation/devicetree/bindings/mux/microchip,lan966-flx-mux.yaml
 create mode 100644 drivers/mux/lan966-flx.c

-- 
2.17.1


^ permalink raw reply	[flat|nested] 21+ messages in thread

* [PATCH v2 1/4] dt-bindings: mfd: atmel,flexcom: Convert to json-schema
  2022-05-09  8:49 [PATCH v2 0/4] Add support for lan966 flexcom multiplexer Kavyasree Kotagiri
@ 2022-05-09  8:49 ` Kavyasree Kotagiri
  2022-05-10 10:32   ` Krzysztof Kozlowski
  2022-05-09  8:49 ` [PATCH v2 2/4] dt-bindings: mfd: atmel,flexcom: Add lan966 compatible string and mux properties Kavyasree Kotagiri
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 21+ messages in thread
From: Kavyasree Kotagiri @ 2022-05-09  8:49 UTC (permalink / raw)
  To: krzysztof.kozlowski+dt, nicolas.ferre, alexandre.belloni,
	claudiu.beznea, peda
  Cc: devicetree, linux-arm-kernel, linux-kernel, lee.jones, linux,
	Manohar.Puri, Kavyasree.Kotagiri, UNGLinuxDriver

Convert the Atmel flexcom device tree bindings to json schema.

Signed-off-by: Kavyasree Kotagiri <kavyasree.kotagiri@microchip.com>
---
 .../bindings/mfd/atmel,flexcom.yaml           | 92 +++++++++++++++++++
 .../devicetree/bindings/mfd/atmel-flexcom.txt | 63 -------------
 2 files changed, 92 insertions(+), 63 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/mfd/atmel,flexcom.yaml
 delete mode 100644 Documentation/devicetree/bindings/mfd/atmel-flexcom.txt

diff --git a/Documentation/devicetree/bindings/mfd/atmel,flexcom.yaml b/Documentation/devicetree/bindings/mfd/atmel,flexcom.yaml
new file mode 100644
index 000000000000..79ec7ebc7055
--- /dev/null
+++ b/Documentation/devicetree/bindings/mfd/atmel,flexcom.yaml
@@ -0,0 +1,92 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/mfd/atmel,flexcom.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Device tree bindings for Atmel Flexcom (Flexible Serial Communication Unit)
+
+maintainers:
+  - Kavyasree Kotagiri <kavyasree.kotagiri@microchip.com>
+
+description:
+  The Atmel Flexcom is just a wrapper which embeds a SPI controller,
+  an I2C controller and an USART. Only one function can be used at a
+  time and is chosen at boot time according to the device tree.
+
+properties:
+  compatible:
+    const: atmel,sama5d2-flexcom
+
+  reg:
+    maxItems: 1
+
+  clocks:
+    maxItems: 1
+
+  "#address-cells":
+    const: 1
+
+  "#size-cells":
+    const: 1
+
+  ranges:
+    description:
+      One range for the full I/O register region. (including USART,
+      TWI and SPI registers).
+    items:
+      maxItems: 3
+
+  atmel,flexcom-mode:
+    description: |
+      Specifies the flexcom mode as follows:
+      1: USART
+      2: SPI
+      3: I2C.
+    $ref: /schemas/types.yaml#/definitions/uint32
+    enum: [1, 2, 3]
+
+required:
+  - compatible
+  - reg
+  - clocks
+  - "#address-cells"
+  - "#size-cells"
+  - ranges
+  - atmel,flexcom-mode
+
+additionalProperties: false
+
+patternProperties:
+  "^serial@[0-9a-f]+$":
+    description: See atmel-usart.txt for details of USART bindings.
+  "^spi@[0-9a-f]+$":
+    description: See ../spi/spi_atmel.txt for details of SPI bindings.
+  "^i2c@[0-9a-f]+$":
+    description: See ../i2c/i2c-at91.txt for details of I2C bindings.
+
+examples:
+  - |
+    flx0: flexcom@f8034000 {
+          compatible = "atmel,sama5d2-flexcom";
+          reg = <0xf8034000 0x200>;
+          clocks = <&flx0_clk>;
+          #address-cells = <1>;
+          #size-cells = <1>;
+          ranges = <0x0 0xf8034000 0x800>;
+          atmel,flexcom-mode = <2>;
+
+          spi0: spi@400 {
+                compatible = "atmel,at91rm9200-spi";
+                reg = <0x400 0x200>;
+                interrupts = <19 4 7>;
+                pinctrl-names = "default";
+                pinctrl-0 = <&pinctrl_flx0_default>;
+                #address-cells = <1>;
+                #size-cells = <0>;
+                clocks = <&flx0_clk>;
+                clock-names = "spi_clk";
+                atmel,fifo-size = <32>;
+          };
+    };
+...
diff --git a/Documentation/devicetree/bindings/mfd/atmel-flexcom.txt b/Documentation/devicetree/bindings/mfd/atmel-flexcom.txt
deleted file mode 100644
index 692300117c64..000000000000
--- a/Documentation/devicetree/bindings/mfd/atmel-flexcom.txt
+++ /dev/null
@@ -1,63 +0,0 @@
-* Device tree bindings for Atmel Flexcom (Flexible Serial Communication Unit)
-
-The Atmel Flexcom is just a wrapper which embeds a SPI controller, an I2C
-controller and an USART. Only one function can be used at a time and is chosen
-at boot time according to the device tree.
-
-Required properties:
-- compatible:		Should be "atmel,sama5d2-flexcom"
-- reg:			Should be the offset/length value for Flexcom dedicated
-			I/O registers (without USART, TWI or SPI registers).
-- clocks:		Should be the Flexcom peripheral clock from PMC.
-- #address-cells:	Should be <1>
-- #size-cells:		Should be <1>
-- ranges:		Should be one range for the full I/O register region
-			(including USART, TWI and SPI registers).
-- atmel,flexcom-mode:	Should be one of the following values:
-			- <1> for USART
-			- <2> for SPI
-			- <3> for I2C
-
-Required child:
-A single available child device of type matching the "atmel,flexcom-mode"
-property.
-
-The phandle provided by the clocks property of the child is the same as one for
-the Flexcom parent.
-
-For other properties, please refer to the documentations of the respective
-device:
-- ../serial/atmel-usart.txt
-- ../spi/spi_atmel.txt
-- ../i2c/i2c-at91.txt
-
-Example:
-
-flexcom@f8034000 {
-	compatible = "atmel,sama5d2-flexcom";
-	reg = <0xf8034000 0x200>;
-	clocks = <&flx0_clk>;
-	#address-cells = <1>;
-	#size-cells = <1>;
-	ranges = <0x0 0xf8034000 0x800>;
-	atmel,flexcom-mode = <2>;
-
-	spi@400 {
-		compatible = "atmel,at91rm9200-spi";
-		reg = <0x400 0x200>;
-		interrupts = <19 IRQ_TYPE_LEVEL_HIGH 7>;
-		pinctrl-names = "default";
-		pinctrl-0 = <&pinctrl_flx0_default>;
-		#address-cells = <1>;
-		#size-cells = <0>;
-		clocks = <&flx0_clk>;
-		clock-names = "spi_clk";
-		atmel,fifo-size = <32>;
-
-		mtd_dataflash@0 {
-			compatible = "atmel,at25f512b";
-			reg = <0>;
-			spi-max-frequency = <20000000>;
-		};
-	};
-};
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v2 2/4] dt-bindings: mfd: atmel,flexcom: Add lan966 compatible string and mux properties
  2022-05-09  8:49 [PATCH v2 0/4] Add support for lan966 flexcom multiplexer Kavyasree Kotagiri
  2022-05-09  8:49 ` [PATCH v2 1/4] dt-bindings: mfd: atmel,flexcom: Convert to json-schema Kavyasree Kotagiri
@ 2022-05-09  8:49 ` Kavyasree Kotagiri
  2022-05-10 10:33   ` Krzysztof Kozlowski
  2022-05-09  8:49 ` [PATCH v2 3/4] dt-bindings: mux: Add lan966 flexcom mux controller Kavyasree Kotagiri
  2022-05-09  8:49 ` [PATCH v2 4/4] mux: lan966: Add support for " Kavyasree Kotagiri
  3 siblings, 1 reply; 21+ messages in thread
From: Kavyasree Kotagiri @ 2022-05-09  8:49 UTC (permalink / raw)
  To: krzysztof.kozlowski+dt, nicolas.ferre, alexandre.belloni,
	claudiu.beznea, peda
  Cc: devicetree, linux-arm-kernel, linux-kernel, lee.jones, linux,
	Manohar.Puri, Kavyasree.Kotagiri, UNGLinuxDriver

Add lan966 flexcom compatible string and flexcom mux
device tree properties.

Signed-off-by: Kavyasree Kotagiri <kavyasree.kotagiri@microchip.com>
---
 .../bindings/mfd/atmel,flexcom.yaml           | 52 ++++++++++++++++++-
 1 file changed, 51 insertions(+), 1 deletion(-)

diff --git a/Documentation/devicetree/bindings/mfd/atmel,flexcom.yaml b/Documentation/devicetree/bindings/mfd/atmel,flexcom.yaml
index 79ec7ebc7055..228c095c84ca 100644
--- a/Documentation/devicetree/bindings/mfd/atmel,flexcom.yaml
+++ b/Documentation/devicetree/bindings/mfd/atmel,flexcom.yaml
@@ -16,7 +16,9 @@ description:
 
 properties:
   compatible:
-    const: atmel,sama5d2-flexcom
+    enum:
+      - atmel,sama5d2-flexcom
+      - microchip,lan966-flexcom
 
   reg:
     maxItems: 1
@@ -57,6 +59,27 @@ required:
 
 additionalProperties: false
 
+allOf:
+  - if:
+      properties:
+        compatible:
+          contains:
+            const: microchip,lan966-flexcom
+
+    then:
+      properties:
+        mux-controls:
+          minItems: 1
+          maxItems: 2
+          $ref: /schemas/types.yaml#/definitions/phandle-array
+
+        mux-control-names:
+          minItems: 1
+          $ref: ../mux/mux-consumer.yaml
+          items:
+            - const: cs0
+            - const: cs1
+
 patternProperties:
   "^serial@[0-9a-f]+$":
     description: See atmel-usart.txt for details of USART bindings.
@@ -89,4 +112,31 @@ examples:
                 atmel,fifo-size = <32>;
           };
     };
+
+  - |
+    flx3: flexcom@e0064000 {
+          compatible = "microchip,lan966-flexcom";
+          reg = <0xe0064000 0x100>;
+          clocks = <&fabric_clk>;
+          #address-cells = <1>;
+          #size-cells = <1>;
+          ranges = <0x0 0xe0064000 0x800>;
+          atmel,flexcom-mode = <2>;
+          mux-controls = <&mux 0>;
+          mux-control-names = "cs0";
+
+          spi3: spi@400 {
+                compatible = "atmel,at91rm9200-spi";
+                reg = <0x400 0x200>;
+                interrupts = <0 51 4>;
+                #address-cells = <1>;
+                #size-cells = <0>;
+                clocks = <&fabric_clk>;
+                clock-names = "spi_clk";
+                pinctrl-0 = <&fc3_b_sck_pins>, <&fc3_b_rxd_pins>,
+                            <&fc3_b_txd_pins>, <&fc_shrd9_pins>;
+                pinctrl-names = "default";
+                atmel,fifo-size = <32>;
+          };
+    };
 ...
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v2 3/4] dt-bindings: mux: Add lan966 flexcom mux controller
  2022-05-09  8:49 [PATCH v2 0/4] Add support for lan966 flexcom multiplexer Kavyasree Kotagiri
  2022-05-09  8:49 ` [PATCH v2 1/4] dt-bindings: mfd: atmel,flexcom: Convert to json-schema Kavyasree Kotagiri
  2022-05-09  8:49 ` [PATCH v2 2/4] dt-bindings: mfd: atmel,flexcom: Add lan966 compatible string and mux properties Kavyasree Kotagiri
@ 2022-05-09  8:49 ` Kavyasree Kotagiri
  2022-05-10 10:37   ` Krzysztof Kozlowski
  2022-05-09  8:49 ` [PATCH v2 4/4] mux: lan966: Add support for " Kavyasree Kotagiri
  3 siblings, 1 reply; 21+ messages in thread
From: Kavyasree Kotagiri @ 2022-05-09  8:49 UTC (permalink / raw)
  To: krzysztof.kozlowski+dt, nicolas.ferre, alexandre.belloni,
	claudiu.beznea, peda
  Cc: devicetree, linux-arm-kernel, linux-kernel, lee.jones, linux,
	Manohar.Puri, Kavyasree.Kotagiri, UNGLinuxDriver

This adds DT bindings documentation for lan966 flexcom
mux controller.

Signed-off-by: Kavyasree Kotagiri <kavyasree.kotagiri@microchip.com>
---
 .../mux/microchip,lan966-flx-mux.yaml         | 51 +++++++++++++++++++
 1 file changed, 51 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/mux/microchip,lan966-flx-mux.yaml

diff --git a/Documentation/devicetree/bindings/mux/microchip,lan966-flx-mux.yaml b/Documentation/devicetree/bindings/mux/microchip,lan966-flx-mux.yaml
new file mode 100644
index 000000000000..63147a2e8f3a
--- /dev/null
+++ b/Documentation/devicetree/bindings/mux/microchip,lan966-flx-mux.yaml
@@ -0,0 +1,51 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/mux/microchip,lan966-flx-mux.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Microchip Lan966 Flexcom multiplexer bindings
+
+maintainers:
+  - Kavyasree Kotagiri <kavyasree.kotagiri@microchip.com>
+
+description: |+
+  The Microchip Lan966 have 5 Flexcoms. Each flexcom has 2 chip-selects
+  when operating in USART and SPI modes.
+  Each chip select of each flexcom can be mapped to 21 flexcom shared pins.
+  Define register offset and pin number to map a flexcom chip-select
+  to flexcom shared pin.
+
+allOf:
+  - $ref: /schemas/mux/mux-controller.yaml#
+
+properties:
+  compatible:
+    const: microchip,lan966-flx-mux
+
+  reg:
+    maxItems: 1
+
+  '#mux-control-cells':
+    const: 1
+
+  mux-offset-pin:
+    description: an array of register offset and flexcom shared pin(0-20).
+
+required:
+  - compatible
+  - reg
+  - '#mux-control-cells'
+  - mux-offset-pin
+
+additionalProperties: false
+
+examples:
+  - |
+    mux: mux-controller@e2004168 {
+        compatible = "microchip,lan966-flx-mux";
+        reg = <0xe2004168 0x8>;
+        #mux-control-cells = <1>;
+        mux-offset-pin = <0x18 9>; /* 0: flx3 cs0 offset, pin-9 */
+    };
+...
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v2 4/4] mux: lan966: Add support for flexcom mux controller
  2022-05-09  8:49 [PATCH v2 0/4] Add support for lan966 flexcom multiplexer Kavyasree Kotagiri
                   ` (2 preceding siblings ...)
  2022-05-09  8:49 ` [PATCH v2 3/4] dt-bindings: mux: Add lan966 flexcom mux controller Kavyasree Kotagiri
@ 2022-05-09  8:49 ` Kavyasree Kotagiri
  2022-05-09 11:37   ` Peter Rosin
                     ` (4 more replies)
  3 siblings, 5 replies; 21+ messages in thread
From: Kavyasree Kotagiri @ 2022-05-09  8:49 UTC (permalink / raw)
  To: krzysztof.kozlowski+dt, nicolas.ferre, alexandre.belloni,
	claudiu.beznea, peda
  Cc: devicetree, linux-arm-kernel, linux-kernel, lee.jones, linux,
	Manohar.Puri, Kavyasree.Kotagiri, UNGLinuxDriver

LAN966 SoC have 5 flexcoms. Each flexcom has 2 chip-selects.
For each chip select of each flexcom there is a configuration
register FLEXCOM_SHARED[0-4]:SS_MASK[0-1]. The width of
configuration register is 21 because there are 21 shared pins
on each of which the chip select can be mapped. Each bit of the
register represents a different FLEXCOM_SHARED pin.

Signed-off-by: Kavyasree Kotagiri <kavyasree.kotagiri@microchip.com>
---
 arch/arm/mach-at91/Kconfig  |   2 +
 drivers/mfd/atmel-flexcom.c |  55 +++++++++++++++-
 drivers/mux/Kconfig         |  12 ++++
 drivers/mux/Makefile        |   2 +
 drivers/mux/lan966-flx.c    | 121 ++++++++++++++++++++++++++++++++++++
 5 files changed, 191 insertions(+), 1 deletion(-)
 create mode 100644 drivers/mux/lan966-flx.c

diff --git a/arch/arm/mach-at91/Kconfig b/arch/arm/mach-at91/Kconfig
index 279810381256..26fb0f4e1b79 100644
--- a/arch/arm/mach-at91/Kconfig
+++ b/arch/arm/mach-at91/Kconfig
@@ -74,6 +74,8 @@ config SOC_LAN966
 	select DW_APB_TIMER_OF
 	select ARM_GIC
 	select MEMORY
+	select MULTIPLEXER
+	select MUX_LAN966
 	help
 	  This enables support for ARMv7 based Microchip LAN966 SoC family.
 
diff --git a/drivers/mfd/atmel-flexcom.c b/drivers/mfd/atmel-flexcom.c
index 559eb4d352b6..7cfd0fc3f4f0 100644
--- a/drivers/mfd/atmel-flexcom.c
+++ b/drivers/mfd/atmel-flexcom.c
@@ -17,6 +17,7 @@
 #include <linux/io.h>
 #include <linux/clk.h>
 #include <dt-bindings/mfd/atmel-flexcom.h>
+#include <linux/mux/consumer.h>
 
 /* I/O register offsets */
 #define FLEX_MR		0x0	/* Mode Register */
@@ -28,6 +29,10 @@
 #define FLEX_MR_OPMODE(opmode)	(((opmode) << FLEX_MR_OPMODE_OFFSET) &	\
 				 FLEX_MR_OPMODE_MASK)
 
+struct atmel_flex_caps {
+	bool has_flx_mux;
+};
+
 struct atmel_flexcom {
 	void __iomem *base;
 	u32 opmode;
@@ -37,6 +42,7 @@ struct atmel_flexcom {
 static int atmel_flexcom_probe(struct platform_device *pdev)
 {
 	struct device_node *np = pdev->dev.of_node;
+	const struct atmel_flex_caps *caps;
 	struct resource *res;
 	struct atmel_flexcom *ddata;
 	int err;
@@ -76,13 +82,60 @@ static int atmel_flexcom_probe(struct platform_device *pdev)
 	 */
 	writel(FLEX_MR_OPMODE(ddata->opmode), ddata->base + FLEX_MR);
 
+	caps = of_device_get_match_data(&pdev->dev);
+	if (!caps) {
+		dev_err(&pdev->dev, "Could not retrieve flexcom caps\n");
+		return -EINVAL;
+	}
+
+	/* Flexcom Mux */
+	if (caps->has_flx_mux && of_property_read_bool(np, "mux-controls")) {
+		struct mux_control *flx_mux;
+		struct of_phandle_args args;
+		int i, count;
+
+		flx_mux = devm_mux_control_get(&pdev->dev, NULL);
+		if (IS_ERR(flx_mux))
+			return PTR_ERR(flx_mux);
+
+		count = of_property_count_strings(np, "mux-control-names");
+		for (i = 0; i < count; i++) {
+			err = of_parse_phandle_with_fixed_args(np, "mux-controls", 1, i, &args);
+			if (err)
+				break;
+
+			err = mux_control_select(flx_mux, args.args[0]);
+			if (!err) {
+				mux_control_deselect(flx_mux);
+			} else {
+				dev_err(&pdev->dev, "Failed to select FLEXCOM mux\n");
+				return err;
+			}
+		}
+	}
+
 	clk_disable_unprepare(ddata->clk);
 
 	return devm_of_platform_populate(&pdev->dev);
 }
 
+static const struct atmel_flex_caps atmel_flexcom_caps = {};
+
+static const struct atmel_flex_caps lan966x_flexcom_caps = {
+	.has_flx_mux = true,
+};
+
 static const struct of_device_id atmel_flexcom_of_match[] = {
-	{ .compatible = "atmel,sama5d2-flexcom" },
+	{
+		.compatible = "atmel,sama5d2-flexcom",
+		.data = &atmel_flexcom_caps,
+	},
+
+	{
+		.compatible = "microchip,lan966-flexcom",
+		.data = &lan966x_flexcom_caps,
+	},
+
 	{ /* sentinel */ }
 };
 MODULE_DEVICE_TABLE(of, atmel_flexcom_of_match);
diff --git a/drivers/mux/Kconfig b/drivers/mux/Kconfig
index e5c571fd232c..ea09f474bc2f 100644
--- a/drivers/mux/Kconfig
+++ b/drivers/mux/Kconfig
@@ -45,6 +45,18 @@ config MUX_GPIO
 	  To compile the driver as a module, choose M here: the module will
 	  be called mux-gpio.
 
+config MUX_LAN966
+	tristate "LAN966 Flexcom multiplexer"
+	depends on OF || COMPILE_TEST
+	help
+	Lan966 Flexcom Multiplexer controller.
+
+	The driver supports mapping 2 chip-selects of each of the lan966
+	flexcoms to 21 flexcom shared pins.
+
+	To compile the driver as a module, choose M here: the module will
+	be called mux-lan966.
+
 config MUX_MMIO
 	tristate "MMIO/Regmap register bitfield-controlled Multiplexer"
 	depends on OF || COMPILE_TEST
diff --git a/drivers/mux/Makefile b/drivers/mux/Makefile
index 6e9fa47daf56..53a9840d96fa 100644
--- a/drivers/mux/Makefile
+++ b/drivers/mux/Makefile
@@ -7,10 +7,12 @@ mux-core-objs			:= core.o
 mux-adg792a-objs		:= adg792a.o
 mux-adgs1408-objs		:= adgs1408.o
 mux-gpio-objs			:= gpio.o
+mux-lan966-objs			:= lan966-flx.o
 mux-mmio-objs			:= mmio.o
 
 obj-$(CONFIG_MULTIPLEXER)	+= mux-core.o
 obj-$(CONFIG_MUX_ADG792A)	+= mux-adg792a.o
 obj-$(CONFIG_MUX_ADGS1408)	+= mux-adgs1408.o
 obj-$(CONFIG_MUX_GPIO)		+= mux-gpio.o
+obj-$(CONFIG_MUX_LAN966)	+= mux-lan966.o
 obj-$(CONFIG_MUX_MMIO)		+= mux-mmio.o
diff --git a/drivers/mux/lan966-flx.c b/drivers/mux/lan966-flx.c
new file mode 100644
index 000000000000..2c7dab616a6a
--- /dev/null
+++ b/drivers/mux/lan966-flx.c
@@ -0,0 +1,121 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * LAN966 Flexcom MUX driver
+ *
+ * Copyright (c) 2022 Microchip Inc.
+ *
+ * Author: Kavyasree Kotagiri <kavyasree.kotagiri@microchip.com>
+ */
+
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/mux/driver.h>
+#include <linux/io.h>
+
+#define FLEX_SHRD_MASK		0x1FFFFF
+#define LAN966_MAX_CS		21
+
+static void __iomem *flx_shared_base;
+struct mux_lan966x {
+	u32 offset;
+	u32 ss_pin;
+};
+
+static int mux_lan966x_set(struct mux_control *mux, int state)
+{
+	struct mux_lan966x *mux_lan966x = mux_chip_priv(mux->chip);
+	u32 val;
+
+	val = ~(1 << mux_lan966x[state].ss_pin) & FLEX_SHRD_MASK;
+	writel(val, flx_shared_base + mux_lan966x[state].offset);
+
+	return 0;
+}
+
+static const struct mux_control_ops mux_lan966x_ops = {
+	.set = mux_lan966x_set,
+};
+
+static const struct of_device_id mux_lan966x_dt_ids[] = {
+	{ .compatible = "microchip,lan966-flx-mux", },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, mux_lan966x_dt_ids);
+
+static int mux_lan966x_probe(struct platform_device *pdev)
+{
+	struct device_node *np = pdev->dev.of_node;
+	struct device *dev = &pdev->dev;
+	struct mux_lan966x *mux_lan966x;
+	struct mux_chip *mux_chip;
+	int ret, num_fields, i;
+
+	ret = of_property_count_u32_elems(np, "mux-offset-pin");
+	if (ret == 0 || ret % 2)
+		ret = -EINVAL;
+	if (ret < 0)
+		return dev_err_probe(dev, ret,
+				     "mux-offset-pin property missing or invalid");
+	num_fields = ret / 2;
+
+	mux_chip = devm_mux_chip_alloc(dev, num_fields, sizeof(*mux_lan966x));
+	if (IS_ERR(mux_chip))
+		return dev_err_probe(dev, PTR_ERR(mux_chip),
+				     "failed to allocate mux_chips\n");
+
+	mux_lan966x = mux_chip_priv(mux_chip);
+
+	flx_shared_base = devm_platform_get_and_ioremap_resource(pdev, 0, NULL);
+	if (IS_ERR(flx_shared_base))
+		return dev_err_probe(dev, PTR_ERR(flx_shared_base),
+				     "failed to get flexcom shared base address\n");
+
+	for (i = 0; i < num_fields; i++) {
+		struct mux_control *mux = &mux_chip->mux[i];
+		u32 offset, shared_pin;
+
+		ret = of_property_read_u32_index(np, "mux-offset-pin",
+						 2 * i, &offset);
+		if (ret == 0)
+			ret = of_property_read_u32_index(np, "mux-offset-pin",
+							 2 * i + 1,
+							 &shared_pin);
+		if (ret < 0)
+			return dev_err_probe(dev, ret,
+					     "failed to read mux-offset-pin property: %d", i);
+
+		if (shared_pin >= LAN966_MAX_CS)
+			return -EINVAL;
+
+		mux_lan966x[i].offset = offset;
+		mux_lan966x[i].ss_pin = shared_pin;
+
+		mux->states = LAN966_MAX_CS;
+	}
+
+	mux_chip->ops = &mux_lan966x_ops;
+
+	ret = devm_mux_chip_register(dev, mux_chip);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static struct platform_driver mux_lan966x_driver = {
+	.driver = {
+		.name = "lan966-mux",
+		.of_match_table	= of_match_ptr(mux_lan966x_dt_ids),
+	},
+	.probe = mux_lan966x_probe,
+};
+
+module_platform_driver(mux_lan966x_driver);
+
+MODULE_DESCRIPTION("LAN966 Flexcom multiplexer driver");
+MODULE_AUTHOR("Kavyasree Kotagiri <kavyasree.kotagiri@microchip.com>");
+MODULE_LICENSE("GPL v2");
+
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* Re: [PATCH v2 4/4] mux: lan966: Add support for flexcom mux controller
  2022-05-09  8:49 ` [PATCH v2 4/4] mux: lan966: Add support for " Kavyasree Kotagiri
@ 2022-05-09 11:37   ` Peter Rosin
  2022-05-09 11:41     ` Peter Rosin
                       ` (2 more replies)
  2022-05-10  1:46   ` kernel test robot
                     ` (3 subsequent siblings)
  4 siblings, 3 replies; 21+ messages in thread
From: Peter Rosin @ 2022-05-09 11:37 UTC (permalink / raw)
  To: Kavyasree Kotagiri, krzysztof.kozlowski+dt, nicolas.ferre,
	alexandre.belloni, claudiu.beznea
  Cc: devicetree, linux-arm-kernel, linux-kernel, lee.jones, linux,
	Manohar.Puri, UNGLinuxDriver

Hi!

2022-05-09 at 10:49, Kavyasree Kotagiri wrote:
> LAN966 SoC have 5 flexcoms. Each flexcom has 2 chip-selects.
> For each chip select of each flexcom there is a configuration
> register FLEXCOM_SHARED[0-4]:SS_MASK[0-1]. The width of
> configuration register is 21 because there are 21 shared pins
> on each of which the chip select can be mapped. Each bit of the
> register represents a different FLEXCOM_SHARED pin.
> 
> Signed-off-by: Kavyasree Kotagiri <kavyasree.kotagiri@microchip.com>
> ---
>  arch/arm/mach-at91/Kconfig  |   2 +
>  drivers/mfd/atmel-flexcom.c |  55 +++++++++++++++-
>  drivers/mux/Kconfig         |  12 ++++
>  drivers/mux/Makefile        |   2 +
>  drivers/mux/lan966-flx.c    | 121 ++++++++++++++++++++++++++++++++++++
>  5 files changed, 191 insertions(+), 1 deletion(-)
>  create mode 100644 drivers/mux/lan966-flx.c
> 
> diff --git a/arch/arm/mach-at91/Kconfig b/arch/arm/mach-at91/Kconfig
> index 279810381256..26fb0f4e1b79 100644
> --- a/arch/arm/mach-at91/Kconfig
> +++ b/arch/arm/mach-at91/Kconfig
> @@ -74,6 +74,8 @@ config SOC_LAN966
>  	select DW_APB_TIMER_OF
>  	select ARM_GIC
>  	select MEMORY
> +	select MULTIPLEXER
> +	select MUX_LAN966
>  	help
>  	  This enables support for ARMv7 based Microchip LAN966 SoC family.
>  
> diff --git a/drivers/mfd/atmel-flexcom.c b/drivers/mfd/atmel-flexcom.c
> index 559eb4d352b6..7cfd0fc3f4f0 100644
> --- a/drivers/mfd/atmel-flexcom.c
> +++ b/drivers/mfd/atmel-flexcom.c
> @@ -17,6 +17,7 @@
>  #include <linux/io.h>
>  #include <linux/clk.h>
>  #include <dt-bindings/mfd/atmel-flexcom.h>
> +#include <linux/mux/consumer.h>
>  
>  /* I/O register offsets */
>  #define FLEX_MR		0x0	/* Mode Register */
> @@ -28,6 +29,10 @@
>  #define FLEX_MR_OPMODE(opmode)	(((opmode) << FLEX_MR_OPMODE_OFFSET) &	\
>  				 FLEX_MR_OPMODE_MASK)
>  
> +struct atmel_flex_caps {
> +	bool has_flx_mux;
> +};
> +
>  struct atmel_flexcom {
>  	void __iomem *base;
>  	u32 opmode;
> @@ -37,6 +42,7 @@ struct atmel_flexcom {
>  static int atmel_flexcom_probe(struct platform_device *pdev)
>  {
>  	struct device_node *np = pdev->dev.of_node;
> +	const struct atmel_flex_caps *caps;
>  	struct resource *res;
>  	struct atmel_flexcom *ddata;
>  	int err;
> @@ -76,13 +82,60 @@ static int atmel_flexcom_probe(struct platform_device *pdev)
>  	 */
>  	writel(FLEX_MR_OPMODE(ddata->opmode), ddata->base + FLEX_MR);
>  
> +	caps = of_device_get_match_data(&pdev->dev);
> +	if (!caps) {
> +		dev_err(&pdev->dev, "Could not retrieve flexcom caps\n");
> +		return -EINVAL;
> +	}
> +
> +	/* Flexcom Mux */
> +	if (caps->has_flx_mux && of_property_read_bool(np, "mux-controls")) {
> +		struct mux_control *flx_mux;
> +		struct of_phandle_args args;
> +		int i, count;
> +
> +		flx_mux = devm_mux_control_get(&pdev->dev, NULL);
> +		if (IS_ERR(flx_mux))
> +			return PTR_ERR(flx_mux);
> +
> +		count = of_property_count_strings(np, "mux-control-names");
> +		for (i = 0; i < count; i++) {
> +			err = of_parse_phandle_with_fixed_args(np, "mux-controls", 1, i, &args);
> +			if (err)
> +				break;
> +
> +			err = mux_control_select(flx_mux, args.args[0]);
> +			if (!err) {
> +				mux_control_deselect(flx_mux);

This is suspect. When you deselect the mux you rely on the mux to be
configured with "as-is" as the idle state. But you do not document that.
This is also fragile in that you silently rely on noone else selecting
the mux to some unwanted state behind your back (mux controls are not
exclusive the way e.g. gpio pins or pwms are). The protocol is that
others may get a ref to the mux control and select it as long as noone
else has selected it.

The only sane thing to do is to keep the mux selected for the whole
duration when you rely on it to be in your desired state.

> +			} else {
> +				dev_err(&pdev->dev, "Failed to select FLEXCOM mux\n");
> +				return err;
> +			}
> +		}
> +	}
> +
>  	clk_disable_unprepare(ddata->clk);
>  
>  	return devm_of_platform_populate(&pdev->dev);
>  }
>  
> +static const struct atmel_flex_caps atmel_flexcom_caps = {};
> +
> +static const struct atmel_flex_caps lan966x_flexcom_caps = {
> +	.has_flx_mux = true,
> +};
> +
>  static const struct of_device_id atmel_flexcom_of_match[] = {
> -	{ .compatible = "atmel,sama5d2-flexcom" },
> +	{
> +		.compatible = "atmel,sama5d2-flexcom",
> +		.data = &atmel_flexcom_caps,
> +	},
> +
> +	{
> +		.compatible = "microchip,lan966-flexcom",
> +		.data = &lan966x_flexcom_caps,
> +	},
> +
>  	{ /* sentinel */ }
>  };
>  MODULE_DEVICE_TABLE(of, atmel_flexcom_of_match);
> diff --git a/drivers/mux/Kconfig b/drivers/mux/Kconfig
> index e5c571fd232c..ea09f474bc2f 100644
> --- a/drivers/mux/Kconfig
> +++ b/drivers/mux/Kconfig
> @@ -45,6 +45,18 @@ config MUX_GPIO
>  	  To compile the driver as a module, choose M here: the module will
>  	  be called mux-gpio.
>  
> +config MUX_LAN966
> +	tristate "LAN966 Flexcom multiplexer"
> +	depends on OF || COMPILE_TEST
> +	help
> +	Lan966 Flexcom Multiplexer controller.
> +
> +	The driver supports mapping 2 chip-selects of each of the lan966
> +	flexcoms to 21 flexcom shared pins.
> +
> +	To compile the driver as a module, choose M here: the module will
> +	be called mux-lan966.
> +
>  config MUX_MMIO
>  	tristate "MMIO/Regmap register bitfield-controlled Multiplexer"
>  	depends on OF || COMPILE_TEST
> diff --git a/drivers/mux/Makefile b/drivers/mux/Makefile
> index 6e9fa47daf56..53a9840d96fa 100644
> --- a/drivers/mux/Makefile
> +++ b/drivers/mux/Makefile
> @@ -7,10 +7,12 @@ mux-core-objs			:= core.o
>  mux-adg792a-objs		:= adg792a.o
>  mux-adgs1408-objs		:= adgs1408.o
>  mux-gpio-objs			:= gpio.o
> +mux-lan966-objs			:= lan966-flx.o
>  mux-mmio-objs			:= mmio.o
>  
>  obj-$(CONFIG_MULTIPLEXER)	+= mux-core.o
>  obj-$(CONFIG_MUX_ADG792A)	+= mux-adg792a.o
>  obj-$(CONFIG_MUX_ADGS1408)	+= mux-adgs1408.o
>  obj-$(CONFIG_MUX_GPIO)		+= mux-gpio.o
> +obj-$(CONFIG_MUX_LAN966)	+= mux-lan966.o
>  obj-$(CONFIG_MUX_MMIO)		+= mux-mmio.o
> diff --git a/drivers/mux/lan966-flx.c b/drivers/mux/lan966-flx.c
> new file mode 100644
> index 000000000000..2c7dab616a6a
> --- /dev/null
> +++ b/drivers/mux/lan966-flx.c
> @@ -0,0 +1,121 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * LAN966 Flexcom MUX driver
> + *
> + * Copyright (c) 2022 Microchip Inc.
> + *
> + * Author: Kavyasree Kotagiri <kavyasree.kotagiri@microchip.com>

Looks like it is based on mmio.c?

> + */
> +
> +#include <linux/err.h>
> +#include <linux/module.h>
> +#include <linux/of_platform.h>
> +#include <linux/platform_device.h>
> +#include <linux/property.h>
> +#include <linux/mux/driver.h>
> +#include <linux/io.h>
> +
> +#define FLEX_SHRD_MASK		0x1FFFFF
> +#define LAN966_MAX_CS		21
> +
> +static void __iomem *flx_shared_base;

I would much prefer to store the combined address (base+offset)
in the mux private data instead of only storing the offset and
then unnecessarily rely on some piece of global state (that
will be clobbered by other instances).

> +struct mux_lan966x {

Why is the file named lan966, but then everything inside lan966x?

> +	u32 offset;
> +	u32 ss_pin;
> +};
> +
> +static int mux_lan966x_set(struct mux_control *mux, int state)
> +{
> +	struct mux_lan966x *mux_lan966x = mux_chip_priv(mux->chip);
> +	u32 val;
> +
> +	val = ~(1 << mux_lan966x[state].ss_pin) & FLEX_SHRD_MASK;
> +	writel(val, flx_shared_base + mux_lan966x[state].offset);

This reads memory you have not allocated, if you select a state
other than zero.

> +
> +	return 0;
> +}
> +
> +static const struct mux_control_ops mux_lan966x_ops = {
> +	.set = mux_lan966x_set,
> +};
> +
> +static const struct of_device_id mux_lan966x_dt_ids[] = {
> +	{ .compatible = "microchip,lan966-flx-mux", },
> +	{ /* sentinel */ }
> +};
> +MODULE_DEVICE_TABLE(of, mux_lan966x_dt_ids);
> +
> +static int mux_lan966x_probe(struct platform_device *pdev)
> +{
> +	struct device_node *np = pdev->dev.of_node;
> +	struct device *dev = &pdev->dev;
> +	struct mux_lan966x *mux_lan966x;
> +	struct mux_chip *mux_chip;
> +	int ret, num_fields, i;
> +
> +	ret = of_property_count_u32_elems(np, "mux-offset-pin");
> +	if (ret == 0 || ret % 2)
> +		ret = -EINVAL;
> +	if (ret < 0)
> +		return dev_err_probe(dev, ret,
> +				     "mux-offset-pin property missing or invalid");
> +	num_fields = ret / 2;
> +
> +	mux_chip = devm_mux_chip_alloc(dev, num_fields, sizeof(*mux_lan966x));

I might be thoroughly mistaken and confused by the code, but it seems
very strange that a subsequenct select is not undoing what a previous
select once did. Each state seems to write to its own register offset,
and there is nothing that restores the first register offset with you
switch states.

Care to explain how muxing works and what you are expected to do to
manipulate the mux? Is there some link to public documentation? I did
a quick search for lan966 but came up with nothing relevant.

> +	if (IS_ERR(mux_chip))
> +		return dev_err_probe(dev, PTR_ERR(mux_chip),
> +				     "failed to allocate mux_chips\n");
> +
> +	mux_lan966x = mux_chip_priv(mux_chip);
> +
> +	flx_shared_base = devm_platform_get_and_ioremap_resource(pdev, 0, NULL);
> +	if (IS_ERR(flx_shared_base))
> +		return dev_err_probe(dev, PTR_ERR(flx_shared_base),
> +				     "failed to get flexcom shared base address\n");
> +
> +	for (i = 0; i < num_fields; i++) {
> +		struct mux_control *mux = &mux_chip->mux[i];
> +		u32 offset, shared_pin;
> +
> +		ret = of_property_read_u32_index(np, "mux-offset-pin",
> +						 2 * i, &offset);
> +		if (ret == 0)
> +			ret = of_property_read_u32_index(np, "mux-offset-pin",
> +							 2 * i + 1,
> +							 &shared_pin);
> +		if (ret < 0)
> +			return dev_err_probe(dev, ret,
> +					     "failed to read mux-offset-pin property: %d", i);
> +
> +		if (shared_pin >= LAN966_MAX_CS)
> +			return -EINVAL;
> +
> +		mux_lan966x[i].offset = offset;
> +		mux_lan966x[i].ss_pin = shared_pin;

This clobbers memory you have not allocated, if num_fields >= 1.

Cheers,
Peter

> +
> +		mux->states = LAN966_MAX_CS;
> +	}
> +
> +	mux_chip->ops = &mux_lan966x_ops;
> +
> +	ret = devm_mux_chip_register(dev, mux_chip);
> +	if (ret < 0)
> +		return ret;
> +
> +	return 0;
> +}
> +
> +static struct platform_driver mux_lan966x_driver = {
> +	.driver = {
> +		.name = "lan966-mux",
> +		.of_match_table	= of_match_ptr(mux_lan966x_dt_ids),
> +	},
> +	.probe = mux_lan966x_probe,
> +};
> +
> +module_platform_driver(mux_lan966x_driver);
> +
> +MODULE_DESCRIPTION("LAN966 Flexcom multiplexer driver");
> +MODULE_AUTHOR("Kavyasree Kotagiri <kavyasree.kotagiri@microchip.com>");
> +MODULE_LICENSE("GPL v2");
> +

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v2 4/4] mux: lan966: Add support for flexcom mux controller
  2022-05-09 11:37   ` Peter Rosin
@ 2022-05-09 11:41     ` Peter Rosin
  2022-05-10 14:59     ` Kavyasree.Kotagiri
  2022-05-17 11:53     ` Michael Walle
  2 siblings, 0 replies; 21+ messages in thread
From: Peter Rosin @ 2022-05-09 11:41 UTC (permalink / raw)
  To: Kavyasree Kotagiri, krzysztof.kozlowski+dt, nicolas.ferre,
	alexandre.belloni, claudiu.beznea
  Cc: devicetree, linux-arm-kernel, linux-kernel, lee.jones, linux,
	Manohar.Puri, UNGLinuxDriver

> This clobbers memory you have not allocated, if num_fields >= 1.

Oops, >= 2

Sorry for the noise.

Cheers,
Peter

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v2 4/4] mux: lan966: Add support for flexcom mux controller
  2022-05-09  8:49 ` [PATCH v2 4/4] mux: lan966: Add support for " Kavyasree Kotagiri
  2022-05-09 11:37   ` Peter Rosin
@ 2022-05-10  1:46   ` kernel test robot
  2022-05-10  3:28   ` kernel test robot
                     ` (2 subsequent siblings)
  4 siblings, 0 replies; 21+ messages in thread
From: kernel test robot @ 2022-05-10  1:46 UTC (permalink / raw)
  To: Kavyasree Kotagiri, krzysztof.kozlowski+dt, nicolas.ferre,
	alexandre.belloni, claudiu.beznea, peda
  Cc: kbuild-all, devicetree, linux-arm-kernel, linux-kernel,
	lee.jones, linux, Manohar.Puri, Kavyasree.Kotagiri,
	UNGLinuxDriver

Hi Kavyasree,

Thank you for the patch! Yet something to improve:

[auto build test ERROR on lee-mfd/for-mfd-next]
[also build test ERROR on robh/for-next]
[cannot apply to soc/for-next linus/master v5.18-rc6 next-20220509]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url:    https://github.com/intel-lab-lkp/linux/commits/Kavyasree-Kotagiri/Add-support-for-lan966-flexcom-multiplexer/20220509-171104
base:   https://git.kernel.org/pub/scm/linux/kernel/git/lee/mfd.git for-mfd-next
config: arm-at91_dt_defconfig (https://download.01.org/0day-ci/archive/20220510/202205100948.56scrQeg-lkp@intel.com/config)
compiler: arm-linux-gnueabi-gcc (GCC) 11.3.0
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # https://github.com/intel-lab-lkp/linux/commit/6b77ec16441906d1aa067b60cf97807111abdd72
        git remote add linux-review https://github.com/intel-lab-lkp/linux
        git fetch --no-tags linux-review Kavyasree-Kotagiri/Add-support-for-lan966-flexcom-multiplexer/20220509-171104
        git checkout 6b77ec16441906d1aa067b60cf97807111abdd72
        # save the config file
        mkdir build_dir && cp config build_dir/.config
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-11.3.0 make.cross W=1 O=build_dir ARCH=arm SHELL=/bin/bash

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>

All errors (new ones prefixed by >>):

   arm-linux-gnueabi-ld: drivers/mfd/atmel-flexcom.o: in function `atmel_flexcom_probe':
   atmel-flexcom.c:(.text+0x160): undefined reference to `devm_mux_control_get'
>> arm-linux-gnueabi-ld: atmel-flexcom.c:(.text+0x1ec): undefined reference to `mux_control_select_delay'
>> arm-linux-gnueabi-ld: atmel-flexcom.c:(.text+0x1fc): undefined reference to `mux_control_deselect'

-- 
0-DAY CI Kernel Test Service
https://01.org/lkp

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v2 4/4] mux: lan966: Add support for flexcom mux controller
  2022-05-09  8:49 ` [PATCH v2 4/4] mux: lan966: Add support for " Kavyasree Kotagiri
  2022-05-09 11:37   ` Peter Rosin
  2022-05-10  1:46   ` kernel test robot
@ 2022-05-10  3:28   ` kernel test robot
  2022-05-11  9:06   ` Claudiu.Beznea
  2022-05-23 14:52   ` Lee Jones
  4 siblings, 0 replies; 21+ messages in thread
From: kernel test robot @ 2022-05-10  3:28 UTC (permalink / raw)
  To: Kavyasree Kotagiri, krzysztof.kozlowski+dt, nicolas.ferre,
	alexandre.belloni, claudiu.beznea, peda
  Cc: kbuild-all, devicetree, linux-arm-kernel, linux-kernel,
	lee.jones, linux, Manohar.Puri, Kavyasree.Kotagiri,
	UNGLinuxDriver

Hi Kavyasree,

Thank you for the patch! Yet something to improve:

[auto build test ERROR on lee-mfd/for-mfd-next]
[also build test ERROR on robh/for-next]
[cannot apply to soc/for-next linus/master v5.18-rc6 next-20220509]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url:    https://github.com/intel-lab-lkp/linux/commits/Kavyasree-Kotagiri/Add-support-for-lan966-flexcom-multiplexer/20220509-171104
base:   https://git.kernel.org/pub/scm/linux/kernel/git/lee/mfd.git for-mfd-next
config: arm64-randconfig-r013-20220509 (https://download.01.org/0day-ci/archive/20220510/202205101102.ozG8p9Tv-lkp@intel.com/config)
compiler: aarch64-linux-gcc (GCC) 11.3.0
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # https://github.com/intel-lab-lkp/linux/commit/6b77ec16441906d1aa067b60cf97807111abdd72
        git remote add linux-review https://github.com/intel-lab-lkp/linux
        git fetch --no-tags linux-review Kavyasree-Kotagiri/Add-support-for-lan966-flexcom-multiplexer/20220509-171104
        git checkout 6b77ec16441906d1aa067b60cf97807111abdd72
        # save the config file
        mkdir build_dir && cp config build_dir/.config
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-11.3.0 make.cross W=1 O=build_dir ARCH=arm64 SHELL=/bin/bash

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>

All errors (new ones prefixed by >>):

   aarch64-linux-ld: Unexpected GOT/PLT entries detected!
   aarch64-linux-ld: Unexpected run-time procedure linkages detected!
   aarch64-linux-ld: drivers/mfd/atmel-flexcom.o: in function `atmel_flexcom_probe':
   atmel-flexcom.c:(.text+0x178): undefined reference to `devm_mux_control_get'
>> aarch64-linux-ld: atmel-flexcom.c:(.text+0x1e0): undefined reference to `mux_control_select_delay'
>> aarch64-linux-ld: atmel-flexcom.c:(.text+0x1f4): undefined reference to `mux_control_deselect'

-- 
0-DAY CI Kernel Test Service
https://01.org/lkp

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v2 1/4] dt-bindings: mfd: atmel,flexcom: Convert to json-schema
  2022-05-09  8:49 ` [PATCH v2 1/4] dt-bindings: mfd: atmel,flexcom: Convert to json-schema Kavyasree Kotagiri
@ 2022-05-10 10:32   ` Krzysztof Kozlowski
  0 siblings, 0 replies; 21+ messages in thread
From: Krzysztof Kozlowski @ 2022-05-10 10:32 UTC (permalink / raw)
  To: Kavyasree Kotagiri, krzysztof.kozlowski+dt, nicolas.ferre,
	alexandre.belloni, claudiu.beznea, peda
  Cc: devicetree, linux-arm-kernel, linux-kernel, lee.jones, linux,
	Manohar.Puri, UNGLinuxDriver

On 09/05/2022 10:49, Kavyasree Kotagiri wrote:
> Convert the Atmel flexcom device tree bindings to json schema.
> 
> Signed-off-by: Kavyasree Kotagiri <kavyasree.kotagiri@microchip.com>
> ---
>  .../bindings/mfd/atmel,flexcom.yaml           | 92 +++++++++++++++++++
>  .../devicetree/bindings/mfd/atmel-flexcom.txt | 63 -------------
>  2 files changed, 92 insertions(+), 63 deletions(-)
>  create mode 100644 Documentation/devicetree/bindings/mfd/atmel,flexcom.yaml
>  delete mode 100644 Documentation/devicetree/bindings/mfd/atmel-flexcom.txt
> 
> diff --git a/Documentation/devicetree/bindings/mfd/atmel,flexcom.yaml b/Documentation/devicetree/bindings/mfd/atmel,flexcom.yaml
> new file mode 100644
> index 000000000000..79ec7ebc7055
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/mfd/atmel,flexcom.yaml
> @@ -0,0 +1,92 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/mfd/atmel,flexcom.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Device tree bindings for Atmel Flexcom (Flexible Serial Communication Unit)

s/Device tree bindings//
> +
> +maintainers:
> +  - Kavyasree Kotagiri <kavyasree.kotagiri@microchip.com>
> +
> +description:
> +  The Atmel Flexcom is just a wrapper which embeds a SPI controller,
> +  an I2C controller and an USART. Only one function can be used at a
> +  time and is chosen at boot time according to the device tree.
> +
> +properties:
> +  compatible:
> +    const: atmel,sama5d2-flexcom
> +
> +  reg:
> +    maxItems: 1
> +
> +  clocks:
> +    maxItems: 1
> +
> +  "#address-cells":
> +    const: 1
> +
> +  "#size-cells":
> +    const: 1
> +
> +  ranges:
> +    description:
> +      One range for the full I/O register region. (including USART,
> +      TWI and SPI registers).
> +    items:
> +      maxItems: 3
> +
> +  atmel,flexcom-mode:
> +    description: |
> +      Specifies the flexcom mode as follows:
> +      1: USART
> +      2: SPI
> +      3: I2C.
> +    $ref: /schemas/types.yaml#/definitions/uint32
> +    enum: [1, 2, 3]
> +
> +required:
> +  - compatible
> +  - reg
> +  - clocks
> +  - "#address-cells"
> +  - "#size-cells"
> +  - ranges
> +  - atmel,flexcom-mode
> +
> +additionalProperties: false
> +
> +patternProperties:

This goes after "properties" (before "required").

> +  "^serial@[0-9a-f]+$":
> +    description: See atmel-usart.txt for details of USART bindings.

type: object

Then a blank line.


> +  "^spi@[0-9a-f]+$":
> +    description: See ../spi/spi_atmel.txt for details of SPI bindings.

type: object

Then a blank line.

> +  "^i2c@[0-9a-f]+$":
> +    description: See ../i2c/i2c-at91.txt for details of I2C bindings.

type: object


> +
> +examples:
> +  - |
> +    flx0: flexcom@f8034000 {
> +          compatible = "atmel,sama5d2-flexcom";
> +          reg = <0xf8034000 0x200>;
> +          clocks = <&flx0_clk>;
> +          #address-cells = <1>;
> +          #size-cells = <1>;
> +          ranges = <0x0 0xf8034000 0x800>;
> +          atmel,flexcom-mode = <2>;
> +

Best regards,
Krzysztof

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v2 2/4] dt-bindings: mfd: atmel,flexcom: Add lan966 compatible string and mux properties
  2022-05-09  8:49 ` [PATCH v2 2/4] dt-bindings: mfd: atmel,flexcom: Add lan966 compatible string and mux properties Kavyasree Kotagiri
@ 2022-05-10 10:33   ` Krzysztof Kozlowski
  0 siblings, 0 replies; 21+ messages in thread
From: Krzysztof Kozlowski @ 2022-05-10 10:33 UTC (permalink / raw)
  To: Kavyasree Kotagiri, krzysztof.kozlowski+dt, nicolas.ferre,
	alexandre.belloni, claudiu.beznea, peda
  Cc: devicetree, linux-arm-kernel, linux-kernel, lee.jones, linux,
	Manohar.Puri, UNGLinuxDriver

On 09/05/2022 10:49, Kavyasree Kotagiri wrote:
> Add lan966 flexcom compatible string and flexcom mux
> device tree properties.
> 
> Signed-off-by: Kavyasree Kotagiri <kavyasree.kotagiri@microchip.com>
> ---
>  .../bindings/mfd/atmel,flexcom.yaml           | 52 ++++++++++++++++++-
>  1 file changed, 51 insertions(+), 1 deletion(-)
> 
> diff --git a/Documentation/devicetree/bindings/mfd/atmel,flexcom.yaml b/Documentation/devicetree/bindings/mfd/atmel,flexcom.yaml
> index 79ec7ebc7055..228c095c84ca 100644
> --- a/Documentation/devicetree/bindings/mfd/atmel,flexcom.yaml
> +++ b/Documentation/devicetree/bindings/mfd/atmel,flexcom.yaml
> @@ -16,7 +16,9 @@ description:
>  
>  properties:
>    compatible:
> -    const: atmel,sama5d2-flexcom
> +    enum:
> +      - atmel,sama5d2-flexcom
> +      - microchip,lan966-flexcom
>  
>    reg:
>      maxItems: 1
> @@ -57,6 +59,27 @@ required:
>  
>  additionalProperties: false
>  
> +allOf:
> +  - if:
> +      properties:
> +        compatible:
> +          contains:
> +            const: microchip,lan966-flexcom
> +
> +    then:
> +      properties:
> +        mux-controls:
> +          minItems: 1
> +          maxItems: 2
> +          $ref: /schemas/types.yaml#/definitions/phandle-array
> +
> +        mux-control-names:
> +          minItems: 1
> +          $ref: ../mux/mux-consumer.yaml

absolute path, so:
/schemas/mux/mux-consumer.yaml


Best regards,
Krzysztof

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v2 3/4] dt-bindings: mux: Add lan966 flexcom mux controller
  2022-05-09  8:49 ` [PATCH v2 3/4] dt-bindings: mux: Add lan966 flexcom mux controller Kavyasree Kotagiri
@ 2022-05-10 10:37   ` Krzysztof Kozlowski
  0 siblings, 0 replies; 21+ messages in thread
From: Krzysztof Kozlowski @ 2022-05-10 10:37 UTC (permalink / raw)
  To: Kavyasree Kotagiri, krzysztof.kozlowski+dt, nicolas.ferre,
	alexandre.belloni, claudiu.beznea, peda
  Cc: devicetree, linux-arm-kernel, linux-kernel, lee.jones, linux,
	Manohar.Puri, UNGLinuxDriver

On 09/05/2022 10:49, Kavyasree Kotagiri wrote:
> This adds DT bindings documentation for lan966 flexcom
> mux controller.
> 
> Signed-off-by: Kavyasree Kotagiri <kavyasree.kotagiri@microchip.com>
> ---
>  .../mux/microchip,lan966-flx-mux.yaml         | 51 +++++++++++++++++++
>  1 file changed, 51 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/mux/microchip,lan966-flx-mux.yaml
> 
> diff --git a/Documentation/devicetree/bindings/mux/microchip,lan966-flx-mux.yaml b/Documentation/devicetree/bindings/mux/microchip,lan966-flx-mux.yaml
> new file mode 100644
> index 000000000000..63147a2e8f3a
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/mux/microchip,lan966-flx-mux.yaml
> @@ -0,0 +1,51 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/mux/microchip,lan966-flx-mux.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Microchip Lan966 Flexcom multiplexer bindings

s/bindings//

> +
> +maintainers:
> +  - Kavyasree Kotagiri <kavyasree.kotagiri@microchip.com>
> +
> +description: |+

No need for |+

> +  The Microchip Lan966 have 5 Flexcoms. Each flexcom has 2 chip-selects
> +  when operating in USART and SPI modes.
> +  Each chip select of each flexcom can be mapped to 21 flexcom shared pins.
> +  Define register offset and pin number to map a flexcom chip-select
> +  to flexcom shared pin.
> +
> +allOf:
> +  - $ref: /schemas/mux/mux-controller.yaml#
> +
> +properties:
> +  compatible:
> +    const: microchip,lan966-flx-mux
> +
> +  reg:
> +    maxItems: 1
> +
> +  '#mux-control-cells':
> +    const: 1
> +
> +  mux-offset-pin:
> +    description: an array of register offset and flexcom shared pin(0-20).

This does not look like generic property, so you need vendor prefix and
type/ref.

> +
> +required:
> +  - compatible
> +  - reg
> +  - '#mux-control-cells'
> +  - mux-offset-pin
> +
> +additionalProperties: false
> +
> +examples:
> +  - |
> +    mux: mux-controller@e2004168 {
> +        compatible = "microchip,lan966-flx-mux";
> +        reg = <0xe2004168 0x8>;
> +        #mux-control-cells = <1>;
> +        mux-offset-pin = <0x18 9>; /* 0: flx3 cs0 offset, pin-9 */
> +    };
> +...


Best regards,
Krzysztof

^ permalink raw reply	[flat|nested] 21+ messages in thread

* RE: [PATCH v2 4/4] mux: lan966: Add support for flexcom mux controller
  2022-05-09 11:37   ` Peter Rosin
  2022-05-09 11:41     ` Peter Rosin
@ 2022-05-10 14:59     ` Kavyasree.Kotagiri
  2022-05-10 19:38       ` Peter Rosin
  2022-05-17 11:53     ` Michael Walle
  2 siblings, 1 reply; 21+ messages in thread
From: Kavyasree.Kotagiri @ 2022-05-10 14:59 UTC (permalink / raw)
  To: peda
  Cc: devicetree, linux-arm-kernel, linux-kernel, lee.jones, linux,
	Manohar.Puri, UNGLinuxDriver, krzysztof.kozlowski+dt,
	Nicolas.Ferre, alexandre.belloni, Claudiu.Beznea

> > LAN966 SoC have 5 flexcoms. Each flexcom has 2 chip-selects.
> > For each chip select of each flexcom there is a configuration
> > register FLEXCOM_SHARED[0-4]:SS_MASK[0-1]. The width of
> > configuration register is 21 because there are 21 shared pins
> > on each of which the chip select can be mapped. Each bit of the
> > register represents a different FLEXCOM_SHARED pin.
> >
> > Signed-off-by: Kavyasree Kotagiri <kavyasree.kotagiri@microchip.com>
> > ---
> >  arch/arm/mach-at91/Kconfig  |   2 +
> >  drivers/mfd/atmel-flexcom.c |  55 +++++++++++++++-
> >  drivers/mux/Kconfig         |  12 ++++
> >  drivers/mux/Makefile        |   2 +
> >  drivers/mux/lan966-flx.c    | 121
> ++++++++++++++++++++++++++++++++++++
> >  5 files changed, 191 insertions(+), 1 deletion(-)
> >  create mode 100644 drivers/mux/lan966-flx.c
> >
> > diff --git a/arch/arm/mach-at91/Kconfig b/arch/arm/mach-at91/Kconfig
> > index 279810381256..26fb0f4e1b79 100644
> > --- a/arch/arm/mach-at91/Kconfig
> > +++ b/arch/arm/mach-at91/Kconfig
> > @@ -74,6 +74,8 @@ config SOC_LAN966
> >       select DW_APB_TIMER_OF
> >       select ARM_GIC
> >       select MEMORY
> > +     select MULTIPLEXER
> > +     select MUX_LAN966
> >       help
> >         This enables support for ARMv7 based Microchip LAN966 SoC family.
> >
> > diff --git a/drivers/mfd/atmel-flexcom.c b/drivers/mfd/atmel-flexcom.c
> > index 559eb4d352b6..7cfd0fc3f4f0 100644
> > --- a/drivers/mfd/atmel-flexcom.c
> > +++ b/drivers/mfd/atmel-flexcom.c
> > @@ -17,6 +17,7 @@
> >  #include <linux/io.h>
> >  #include <linux/clk.h>
> >  #include <dt-bindings/mfd/atmel-flexcom.h>
> > +#include <linux/mux/consumer.h>
> >
> >  /* I/O register offsets */
> >  #define FLEX_MR              0x0     /* Mode Register */
> > @@ -28,6 +29,10 @@
> >  #define FLEX_MR_OPMODE(opmode)       (((opmode) <<
> FLEX_MR_OPMODE_OFFSET) &  \
> >                                FLEX_MR_OPMODE_MASK)
> >
> > +struct atmel_flex_caps {
> > +     bool has_flx_mux;
> > +};
> > +
> >  struct atmel_flexcom {
> >       void __iomem *base;
> >       u32 opmode;
> > @@ -37,6 +42,7 @@ struct atmel_flexcom {
> >  static int atmel_flexcom_probe(struct platform_device *pdev)
> >  {
> >       struct device_node *np = pdev->dev.of_node;
> > +     const struct atmel_flex_caps *caps;
> >       struct resource *res;
> >       struct atmel_flexcom *ddata;
> >       int err;
> > @@ -76,13 +82,60 @@ static int atmel_flexcom_probe(struct
> platform_device *pdev)
> >        */
> >       writel(FLEX_MR_OPMODE(ddata->opmode), ddata->base + FLEX_MR);
> >
> > +     caps = of_device_get_match_data(&pdev->dev);
> > +     if (!caps) {
> > +             dev_err(&pdev->dev, "Could not retrieve flexcom caps\n");
> > +             return -EINVAL;
> > +     }
> > +
> > +     /* Flexcom Mux */
> > +     if (caps->has_flx_mux && of_property_read_bool(np, "mux-controls"))
> {
> > +             struct mux_control *flx_mux;
> > +             struct of_phandle_args args;
> > +             int i, count;
> > +
> > +             flx_mux = devm_mux_control_get(&pdev->dev, NULL);
> > +             if (IS_ERR(flx_mux))
> > +                     return PTR_ERR(flx_mux);
> > +
> > +             count = of_property_count_strings(np, "mux-control-names");
> > +             for (i = 0; i < count; i++) {
> > +                     err = of_parse_phandle_with_fixed_args(np, "mux-controls",
> 1, i, &args);
> > +                     if (err)
> > +                             break;
> > +
> > +                     err = mux_control_select(flx_mux, args.args[0]);
> > +                     if (!err) {
> > +                             mux_control_deselect(flx_mux);
> 
> This is suspect. When you deselect the mux you rely on the mux to be
> configured with "as-is" as the idle state. But you do not document that.
> This is also fragile in that you silently rely on noone else selecting
> the mux to some unwanted state behind your back (mux controls are not
> exclusive the way e.g. gpio pins or pwms are). The protocol is that
> others may get a ref to the mux control and select it as long as noone
> else has selected it.
> 
> The only sane thing to do is to keep the mux selected for the whole
> duration when you rely on it to be in your desired state.
>

Yes, mux is kept selected until configuring register is done. Please see below log where
I added debug prints just for understanding:
# dmesg | grep KK
 [    0.779827] KK: Func: atmel_flexcom_probe ***** [START flx muxing] ********
[    0.779875] KK: Func: atmel_flexcom_probe i = 0 args[0] = 0
[    0.779890] KK: Func: mux_control_select [Entered]
[    0.779902] KK: Func: mux_lan966x_set [Entered] state = 0
[    0.779977] KK: Func: mux_lan966x_set [Read] = 0x1fffef   <<<----- setting mux_lan966x[state].ss_pin "4" which is passed from dts
[    0.779992] KK: Func: mux_lan966x_set [Exit]
[    0.780002] KK: Func: mux_control_select [Exit]
[    0.780011] KK: Func: mux_control_deselect [Entered]
[    0.780021] KK: Func: mux_control_deselect [Exit]


> > +                     } else {
> > +                             dev_err(&pdev->dev, "Failed to select FLEXCOM mux\n");
> > +                             return err;
> > +                     }
> > +             }
> > +     }
> > +
> >       clk_disable_unprepare(ddata->clk);
> >
> >       return devm_of_platform_populate(&pdev->dev);
> >  }
> >
> > +static const struct atmel_flex_caps atmel_flexcom_caps = {};
> > +
> > +static const struct atmel_flex_caps lan966x_flexcom_caps = {
> > +     .has_flx_mux = true,
> > +};
> > +
> >  static const struct of_device_id atmel_flexcom_of_match[] = {
> > -     { .compatible = "atmel,sama5d2-flexcom" },
> > +     {
> > +             .compatible = "atmel,sama5d2-flexcom",
> > +             .data = &atmel_flexcom_caps,
> > +     },
> > +
> > +     {
> > +             .compatible = "microchip,lan966-flexcom",
> > +             .data = &lan966x_flexcom_caps,
> > +     },
> > +
> >       { /* sentinel */ }
> >  };
> >  MODULE_DEVICE_TABLE(of, atmel_flexcom_of_match);
> > diff --git a/drivers/mux/Kconfig b/drivers/mux/Kconfig
> > index e5c571fd232c..ea09f474bc2f 100644
> > --- a/drivers/mux/Kconfig
> > +++ b/drivers/mux/Kconfig
> > @@ -45,6 +45,18 @@ config MUX_GPIO
> >         To compile the driver as a module, choose M here: the module will
> >         be called mux-gpio.
> >
> > +config MUX_LAN966
> > +     tristate "LAN966 Flexcom multiplexer"
> > +     depends on OF || COMPILE_TEST
> > +     help
> > +     Lan966 Flexcom Multiplexer controller.
> > +
> > +     The driver supports mapping 2 chip-selects of each of the lan966
> > +     flexcoms to 21 flexcom shared pins.
> > +
> > +     To compile the driver as a module, choose M here: the module will
> > +     be called mux-lan966.
> > +
> >  config MUX_MMIO
> >       tristate "MMIO/Regmap register bitfield-controlled Multiplexer"
> >       depends on OF || COMPILE_TEST
> > diff --git a/drivers/mux/Makefile b/drivers/mux/Makefile
> > index 6e9fa47daf56..53a9840d96fa 100644
> > --- a/drivers/mux/Makefile
> > +++ b/drivers/mux/Makefile
> > @@ -7,10 +7,12 @@ mux-core-objs                       := core.o
> >  mux-adg792a-objs             := adg792a.o
> >  mux-adgs1408-objs            := adgs1408.o
> >  mux-gpio-objs                        := gpio.o
> > +mux-lan966-objs                      := lan966-flx.o
> >  mux-mmio-objs                        := mmio.o
> >
> >  obj-$(CONFIG_MULTIPLEXER)    += mux-core.o
> >  obj-$(CONFIG_MUX_ADG792A)    += mux-adg792a.o
> >  obj-$(CONFIG_MUX_ADGS1408)   += mux-adgs1408.o
> >  obj-$(CONFIG_MUX_GPIO)               += mux-gpio.o
> > +obj-$(CONFIG_MUX_LAN966)     += mux-lan966.o
> >  obj-$(CONFIG_MUX_MMIO)               += mux-mmio.o
> > diff --git a/drivers/mux/lan966-flx.c b/drivers/mux/lan966-flx.c
> > new file mode 100644
> > index 000000000000..2c7dab616a6a
> > --- /dev/null
> > +++ b/drivers/mux/lan966-flx.c
> > @@ -0,0 +1,121 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * LAN966 Flexcom MUX driver
> > + *
> > + * Copyright (c) 2022 Microchip Inc.
> > + *
> > + * Author: Kavyasree Kotagiri <kavyasree.kotagiri@microchip.com>
> 
> Looks like it is based on mmio.c?
> 
Yes, I took mmio.c  driver as reference.

> > + */
> > +
> > +#include <linux/err.h>
> > +#include <linux/module.h>
> > +#include <linux/of_platform.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/property.h>
> > +#include <linux/mux/driver.h>
> > +#include <linux/io.h>
> > +
> > +#define FLEX_SHRD_MASK               0x1FFFFF
> > +#define LAN966_MAX_CS                21
> > +
> > +static void __iomem *flx_shared_base;
> 
> I would much prefer to store the combined address (base+offset)
> in the mux private data instead of only storing the offset and
> then unnecessarily rely on some piece of global state (that
> will be clobbered by other instances).
> 
Ok. I will try to see if this is relevant and change accordingly.

> > +struct mux_lan966x {
> 
> Why is the file named lan966, but then everything inside lan966x?
> 
> > +     u32 offset;
> > +     u32 ss_pin;
> > +};
> > +
> > +static int mux_lan966x_set(struct mux_control *mux, int state)
> > +{
> > +     struct mux_lan966x *mux_lan966x = mux_chip_priv(mux->chip);
> > +     u32 val;
> > +
> > +     val = ~(1 << mux_lan966x[state].ss_pin) & FLEX_SHRD_MASK;
> > +     writel(val, flx_shared_base + mux_lan966x[state].offset);
> 
> This reads memory you have not allocated, if you select a state
> other than zero.
> 
Ok. I will return error condition in case of trying to access none existing entry.
> > +
> > +     return 0;
> > +}
> > +
> > +static const struct mux_control_ops mux_lan966x_ops = {
> > +     .set = mux_lan966x_set,
> > +};
> > +
> > +static const struct of_device_id mux_lan966x_dt_ids[] = {
> > +     { .compatible = "microchip,lan966-flx-mux", },
> > +     { /* sentinel */ }
> > +};
> > +MODULE_DEVICE_TABLE(of, mux_lan966x_dt_ids);
> > +
> > +static int mux_lan966x_probe(struct platform_device *pdev)
> > +{
> > +     struct device_node *np = pdev->dev.of_node;
> > +     struct device *dev = &pdev->dev;
> > +     struct mux_lan966x *mux_lan966x;
> > +     struct mux_chip *mux_chip;
> > +     int ret, num_fields, i;
> > +
> > +     ret = of_property_count_u32_elems(np, "mux-offset-pin");
> > +     if (ret == 0 || ret % 2)
> > +             ret = -EINVAL;
> > +     if (ret < 0)
> > +             return dev_err_probe(dev, ret,
> > +                                  "mux-offset-pin property missing or invalid");
> > +     num_fields = ret / 2;
> > +
> > +     mux_chip = devm_mux_chip_alloc(dev, num_fields,
> sizeof(*mux_lan966x));
> 
> I might be thoroughly mistaken and confused by the code, but it seems
> very strange that a subsequenct select is not undoing what a previous
> select once did. Each state seems to write to its own register offset,
> and there is nothing that restores the first register offset with you
> switch states.
> 
> Care to explain how muxing works and what you are expected to do to
> manipulate the mux? Is there some link to public documentation? I did
> a quick search for lan966 but came up with nothing relevant.
>
LAN966 has 5 flexcoms(which can be used as USART/SPI/I2C interface).
FLEXCOM has two chip-select I/O lines namely CS0 and CS1
in SPI mode, CTS and RTS in USART mode. These FLEXCOM pins are optional.
These chip-selects can be mapped to flexcom shared pin [0-21] which can be
done by configuring flexcom multiplexer register(FLEXCOM_SHARED[0-4]:SS_MASK[0-1])
Driver explanation:
"flx_shared_base" is used to get base address of Flexcom shared multiplexer
"mux-offset-pin" property is used to get cs0/cs1 offset and flexcom shared pin[0-21] of a flexcom.
state value passed from atmel-flexcom is used to configure respective 
FLEXCOM_SHARED[0-4]:SS_MASK[0-1] register with offset and flexcom shared pin.

> > +     if (IS_ERR(mux_chip))
> > +             return dev_err_probe(dev, PTR_ERR(mux_chip),
> > +                                  "failed to allocate mux_chips\n");
> > +
> > +     mux_lan966x = mux_chip_priv(mux_chip);
> > +
> > +     flx_shared_base = devm_platform_get_and_ioremap_resource(pdev,
> 0, NULL);
> > +     if (IS_ERR(flx_shared_base))
> > +             return dev_err_probe(dev, PTR_ERR(flx_shared_base),
> > +                                  "failed to get flexcom shared base address\n");
> > +
> > +     for (i = 0; i < num_fields; i++) {
> > +             struct mux_control *mux = &mux_chip->mux[i];
> > +             u32 offset, shared_pin;
> > +
> > +             ret = of_property_read_u32_index(np, "mux-offset-pin",
> > +                                              2 * i, &offset);
> > +             if (ret == 0)
> > +                     ret = of_property_read_u32_index(np, "mux-offset-pin",
> > +                                                      2 * i + 1,
> > +                                                      &shared_pin);
> > +             if (ret < 0)
> > +                     return dev_err_probe(dev, ret,
> > +                                          "failed to read mux-offset-pin property: %d", i);
> > +
> > +             if (shared_pin >= LAN966_MAX_CS)
> > +                     return -EINVAL;
> > +
> > +             mux_lan966x[i].offset = offset;
> > +             mux_lan966x[i].ss_pin = shared_pin;
> 
> This clobbers memory you have not allocated, if num_fields >= 1.
> 
> Cheers,
> Peter
> 
> > +
> > +             mux->states = LAN966_MAX_CS;
> > +     }
> > +
> > +     mux_chip->ops = &mux_lan966x_ops;
> > +
> > +     ret = devm_mux_chip_register(dev, mux_chip);
> > +     if (ret < 0)
> > +             return ret;
> > +
> > +     return 0;
> > +}
> > +
> > +static struct platform_driver mux_lan966x_driver = {
> > +     .driver = {
> > +             .name = "lan966-mux",
> > +             .of_match_table = of_match_ptr(mux_lan966x_dt_ids),
> > +     },
> > +     .probe = mux_lan966x_probe,
> > +};
> > +
> > +module_platform_driver(mux_lan966x_driver);
> > +
> > +MODULE_DESCRIPTION("LAN966 Flexcom multiplexer driver");
> > +MODULE_AUTHOR("Kavyasree Kotagiri
> <kavyasree.kotagiri@microchip.com>");
> > +MODULE_LICENSE("GPL v2");
> > +

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v2 4/4] mux: lan966: Add support for flexcom mux controller
  2022-05-10 14:59     ` Kavyasree.Kotagiri
@ 2022-05-10 19:38       ` Peter Rosin
  2022-05-11 14:28         ` Kavyasree.Kotagiri
  0 siblings, 1 reply; 21+ messages in thread
From: Peter Rosin @ 2022-05-10 19:38 UTC (permalink / raw)
  To: Kavyasree.Kotagiri
  Cc: devicetree, linux-arm-kernel, linux-kernel, lee.jones, linux,
	Manohar.Puri, UNGLinuxDriver, krzysztof.kozlowski+dt,
	Nicolas.Ferre, alexandre.belloni, Claudiu.Beznea

Hi!

2022-05-10 at 16:59, Kavyasree.Kotagiri@microchip.com wrote:
>>> LAN966 SoC have 5 flexcoms. Each flexcom has 2 chip-selects.
>>> For each chip select of each flexcom there is a configuration
>>> register FLEXCOM_SHARED[0-4]:SS_MASK[0-1]. The width of
>>> configuration register is 21 because there are 21 shared pins
>>> on each of which the chip select can be mapped. Each bit of the
>>> register represents a different FLEXCOM_SHARED pin.
>>>
>>> Signed-off-by: Kavyasree Kotagiri <kavyasree.kotagiri@microchip.com>
>>> ---
>>>  arch/arm/mach-at91/Kconfig  |   2 +
>>>  drivers/mfd/atmel-flexcom.c |  55 +++++++++++++++-
>>>  drivers/mux/Kconfig         |  12 ++++
>>>  drivers/mux/Makefile        |   2 +
>>>  drivers/mux/lan966-flx.c    | 121
>> ++++++++++++++++++++++++++++++++++++
>>>  5 files changed, 191 insertions(+), 1 deletion(-)
>>>  create mode 100644 drivers/mux/lan966-flx.c
>>>
>>> diff --git a/arch/arm/mach-at91/Kconfig b/arch/arm/mach-at91/Kconfig
>>> index 279810381256..26fb0f4e1b79 100644
>>> --- a/arch/arm/mach-at91/Kconfig
>>> +++ b/arch/arm/mach-at91/Kconfig
>>> @@ -74,6 +74,8 @@ config SOC_LAN966
>>>       select DW_APB_TIMER_OF
>>>       select ARM_GIC
>>>       select MEMORY
>>> +     select MULTIPLEXER
>>> +     select MUX_LAN966
>>>       help
>>>         This enables support for ARMv7 based Microchip LAN966 SoC family.
>>>
>>> diff --git a/drivers/mfd/atmel-flexcom.c b/drivers/mfd/atmel-flexcom.c
>>> index 559eb4d352b6..7cfd0fc3f4f0 100644
>>> --- a/drivers/mfd/atmel-flexcom.c
>>> +++ b/drivers/mfd/atmel-flexcom.c
>>> @@ -17,6 +17,7 @@
>>>  #include <linux/io.h>
>>>  #include <linux/clk.h>
>>>  #include <dt-bindings/mfd/atmel-flexcom.h>
>>> +#include <linux/mux/consumer.h>
>>>
>>>  /* I/O register offsets */
>>>  #define FLEX_MR              0x0     /* Mode Register */
>>> @@ -28,6 +29,10 @@
>>>  #define FLEX_MR_OPMODE(opmode)       (((opmode) <<
>> FLEX_MR_OPMODE_OFFSET) &  \
>>>                                FLEX_MR_OPMODE_MASK)
>>>
>>> +struct atmel_flex_caps {
>>> +     bool has_flx_mux;
>>> +};
>>> +
>>>  struct atmel_flexcom {
>>>       void __iomem *base;
>>>       u32 opmode;
>>> @@ -37,6 +42,7 @@ struct atmel_flexcom {
>>>  static int atmel_flexcom_probe(struct platform_device *pdev)
>>>  {
>>>       struct device_node *np = pdev->dev.of_node;
>>> +     const struct atmel_flex_caps *caps;
>>>       struct resource *res;
>>>       struct atmel_flexcom *ddata;
>>>       int err;
>>> @@ -76,13 +82,60 @@ static int atmel_flexcom_probe(struct
>> platform_device *pdev)
>>>        */
>>>       writel(FLEX_MR_OPMODE(ddata->opmode), ddata->base + FLEX_MR);
>>>
>>> +     caps = of_device_get_match_data(&pdev->dev);
>>> +     if (!caps) {
>>> +             dev_err(&pdev->dev, "Could not retrieve flexcom caps\n");
>>> +             return -EINVAL;
>>> +     }
>>> +
>>> +     /* Flexcom Mux */
>>> +     if (caps->has_flx_mux && of_property_read_bool(np, "mux-controls"))
>> {
>>> +             struct mux_control *flx_mux;
>>> +             struct of_phandle_args args;
>>> +             int i, count;
>>> +
>>> +             flx_mux = devm_mux_control_get(&pdev->dev, NULL);
>>> +             if (IS_ERR(flx_mux))
>>> +                     return PTR_ERR(flx_mux);
>>> +
>>> +             count = of_property_count_strings(np, "mux-control-names");
>>> +             for (i = 0; i < count; i++) {
>>> +                     err = of_parse_phandle_with_fixed_args(np, "mux-controls",
>> 1, i, &args);
>>> +                     if (err)
>>> +                             break;
>>> +
>>> +                     err = mux_control_select(flx_mux, args.args[0]);
>>> +                     if (!err) {
>>> +                             mux_control_deselect(flx_mux);
>>
>> This is suspect. When you deselect the mux you rely on the mux to be
>> configured with "as-is" as the idle state. But you do not document that.
>> This is also fragile in that you silently rely on noone else selecting
>> the mux to some unwanted state behind your back (mux controls are not
>> exclusive the way e.g. gpio pins or pwms are). The protocol is that
>> others may get a ref to the mux control and select it as long as noone
>> else has selected it.
>>
>> The only sane thing to do is to keep the mux selected for the whole
>> duration when you rely on it to be in your desired state.
>>
> 
> Yes, mux is kept selected until configuring register is done. Please see below log where
> I added debug prints just for understanding:
> # dmesg | grep KK
>  [    0.779827] KK: Func: atmel_flexcom_probe ***** [START flx muxing] ********
> [    0.779875] KK: Func: atmel_flexcom_probe i = 0 args[0] = 0
> [    0.779890] KK: Func: mux_control_select [Entered]
> [    0.779902] KK: Func: mux_lan966x_set [Entered] state = 0
> [    0.779977] KK: Func: mux_lan966x_set [Read] = 0x1fffef   <<<----- setting mux_lan966x[state].ss_pin "4" which is passed from dts
> [    0.779992] KK: Func: mux_lan966x_set [Exit]
> [    0.780002] KK: Func: mux_control_select [Exit]
> [    0.780011] KK: Func: mux_control_deselect [Entered]
> [    0.780021] KK: Func: mux_control_deselect [Exit]

You misunderstand. The mux control is only "selected" between the call 
to mux_control_select() and the following call to 
mux_control_deselect().

After that, the mux control is "idle". However, in your case the 
"idle-state" is configured to "as-is" (which is the default), so the 
mux control (naturally) remains in the previously selected state while 
idle. But you are not documenting that limitation, and with this 
implementation you *must* have a mux control that uses "as-is" as its 
idle state. But that is an unexpected and broken limitation, and a 
much better solution is to simply keep the mux control "selected" for 
the complete duration when you rely on it to be in whatever state you 
need it to be in.

>>> +                     } else {
>>> +                             dev_err(&pdev->dev, "Failed to select FLEXCOM mux\n");
>>> +                             return err;
>>> +                     }
>>> +             }
>>> +     }
>>> +
>>>       clk_disable_unprepare(ddata->clk);
>>>
>>>       return devm_of_platform_populate(&pdev->dev);
>>>  }
>>>
>>> +static const struct atmel_flex_caps atmel_flexcom_caps = {};
>>> +
>>> +static const struct atmel_flex_caps lan966x_flexcom_caps = {
>>> +     .has_flx_mux = true,
>>> +};
>>> +
>>>  static const struct of_device_id atmel_flexcom_of_match[] = {
>>> -     { .compatible = "atmel,sama5d2-flexcom" },
>>> +     {
>>> +             .compatible = "atmel,sama5d2-flexcom",
>>> +             .data = &atmel_flexcom_caps,
>>> +     },
>>> +
>>> +     {
>>> +             .compatible = "microchip,lan966-flexcom",
>>> +             .data = &lan966x_flexcom_caps,
>>> +     },
>>> +
>>>       { /* sentinel */ }
>>>  };
>>>  MODULE_DEVICE_TABLE(of, atmel_flexcom_of_match);
>>> diff --git a/drivers/mux/Kconfig b/drivers/mux/Kconfig
>>> index e5c571fd232c..ea09f474bc2f 100644
>>> --- a/drivers/mux/Kconfig
>>> +++ b/drivers/mux/Kconfig
>>> @@ -45,6 +45,18 @@ config MUX_GPIO
>>>         To compile the driver as a module, choose M here: the module will
>>>         be called mux-gpio.
>>>
>>> +config MUX_LAN966
>>> +     tristate "LAN966 Flexcom multiplexer"
>>> +     depends on OF || COMPILE_TEST
>>> +     help
>>> +     Lan966 Flexcom Multiplexer controller.
>>> +
>>> +     The driver supports mapping 2 chip-selects of each of the lan966
>>> +     flexcoms to 21 flexcom shared pins.
>>> +
>>> +     To compile the driver as a module, choose M here: the module will
>>> +     be called mux-lan966.
>>> +
>>>  config MUX_MMIO
>>>       tristate "MMIO/Regmap register bitfield-controlled Multiplexer"
>>>       depends on OF || COMPILE_TEST
>>> diff --git a/drivers/mux/Makefile b/drivers/mux/Makefile
>>> index 6e9fa47daf56..53a9840d96fa 100644
>>> --- a/drivers/mux/Makefile
>>> +++ b/drivers/mux/Makefile
>>> @@ -7,10 +7,12 @@ mux-core-objs                       := core.o
>>>  mux-adg792a-objs             := adg792a.o
>>>  mux-adgs1408-objs            := adgs1408.o
>>>  mux-gpio-objs                        := gpio.o
>>> +mux-lan966-objs                      := lan966-flx.o
>>>  mux-mmio-objs                        := mmio.o
>>>
>>>  obj-$(CONFIG_MULTIPLEXER)    += mux-core.o
>>>  obj-$(CONFIG_MUX_ADG792A)    += mux-adg792a.o
>>>  obj-$(CONFIG_MUX_ADGS1408)   += mux-adgs1408.o
>>>  obj-$(CONFIG_MUX_GPIO)               += mux-gpio.o
>>> +obj-$(CONFIG_MUX_LAN966)     += mux-lan966.o
>>>  obj-$(CONFIG_MUX_MMIO)               += mux-mmio.o
>>> diff --git a/drivers/mux/lan966-flx.c b/drivers/mux/lan966-flx.c
>>> new file mode 100644
>>> index 000000000000..2c7dab616a6a
>>> --- /dev/null
>>> +++ b/drivers/mux/lan966-flx.c
>>> @@ -0,0 +1,121 @@
>>> +// SPDX-License-Identifier: GPL-2.0
>>> +/*
>>> + * LAN966 Flexcom MUX driver
>>> + *
>>> + * Copyright (c) 2022 Microchip Inc.
>>> + *
>>> + * Author: Kavyasree Kotagiri <kavyasree.kotagiri@microchip.com>
>>
>> Looks like it is based on mmio.c?
>>
> Yes, I took mmio.c  driver as reference.
> 

So, then the above copyright and authorship info is not complete.

>>> + */
>>> +
>>> +#include <linux/err.h>
>>> +#include <linux/module.h>
>>> +#include <linux/of_platform.h>
>>> +#include <linux/platform_device.h>
>>> +#include <linux/property.h>
>>> +#include <linux/mux/driver.h>
>>> +#include <linux/io.h>
>>> +
>>> +#define FLEX_SHRD_MASK               0x1FFFFF
>>> +#define LAN966_MAX_CS                21
>>> +
>>> +static void __iomem *flx_shared_base;
>>
>> I would much prefer to store the combined address (base+offset)
>> in the mux private data instead of only storing the offset and
>> then unnecessarily rely on some piece of global state (that
>> will be clobbered by other instances).
>>
> Ok. I will try to see if this is relevant and change accordingly.
> 
>>> +struct mux_lan966x {
>>
>> Why is the file named lan966, but then everything inside lan966x?
>>
>>> +     u32 offset;
>>> +     u32 ss_pin;
>>> +};
>>> +
>>> +static int mux_lan966x_set(struct mux_control *mux, int state)
>>> +{
>>> +     struct mux_lan966x *mux_lan966x = mux_chip_priv(mux->chip);
>>> +     u32 val;
>>> +
>>> +     val = ~(1 << mux_lan966x[state].ss_pin) & FLEX_SHRD_MASK;
>>> +     writel(val, flx_shared_base + mux_lan966x[state].offset);
>>
>> This reads memory you have not allocated, if you select a state
>> other than zero.
>>
> Ok. I will return error condition in case of trying to access none existing entry.
>>> +
>>> +     return 0;
>>> +}
>>> +
>>> +static const struct mux_control_ops mux_lan966x_ops = {
>>> +     .set = mux_lan966x_set,
>>> +};
>>> +
>>> +static const struct of_device_id mux_lan966x_dt_ids[] = {
>>> +     { .compatible = "microchip,lan966-flx-mux", },
>>> +     { /* sentinel */ }
>>> +};
>>> +MODULE_DEVICE_TABLE(of, mux_lan966x_dt_ids);
>>> +
>>> +static int mux_lan966x_probe(struct platform_device *pdev)
>>> +{
>>> +     struct device_node *np = pdev->dev.of_node;
>>> +     struct device *dev = &pdev->dev;
>>> +     struct mux_lan966x *mux_lan966x;
>>> +     struct mux_chip *mux_chip;
>>> +     int ret, num_fields, i;
>>> +
>>> +     ret = of_property_count_u32_elems(np, "mux-offset-pin");
>>> +     if (ret == 0 || ret % 2)
>>> +             ret = -EINVAL;
>>> +     if (ret < 0)
>>> +             return dev_err_probe(dev, ret,
>>> +                                  "mux-offset-pin property missing or invalid");
>>> +     num_fields = ret / 2;
>>> +
>>> +     mux_chip = devm_mux_chip_alloc(dev, num_fields,
>> sizeof(*mux_lan966x));
>>
>> I might be thoroughly mistaken and confused by the code, but it seems
>> very strange that a subsequenct select is not undoing what a previous
>> select once did. Each state seems to write to its own register offset,
>> and there is nothing that restores the first register offset with you
>> switch states.
>>
>> Care to explain how muxing works and what you are expected to do to
>> manipulate the mux? Is there some link to public documentation? I did
>> a quick search for lan966 but came up with nothing relevant.
>>
> LAN966 has 5 flexcoms(which can be used as USART/SPI/I2C interface).
> FLEXCOM has two chip-select I/O lines namely CS0 and CS1
> in SPI mode, CTS and RTS in USART mode. These FLEXCOM pins are optional.
> These chip-selects can be mapped to flexcom shared pin [0-21] which can be
> done by configuring flexcom multiplexer register(FLEXCOM_SHARED[0-4]:SS_MASK[0-1])
> Driver explanation:
> "flx_shared_base" is used to get base address of Flexcom shared multiplexer
> "mux-offset-pin" property is used to get cs0/cs1 offset and flexcom shared pin[0-21] of a flexcom.
> state value passed from atmel-flexcom is used to configure respective 
> FLEXCOM_SHARED[0-4]:SS_MASK[0-1] register with offset and flexcom shared pin.

Ok, let me try to interpret that. You wish enable a "fan out" of these
two chip-selects for any of the 5 flexcoms (in SPI mode?) such that
these 10 internal chip-selects can be connected to any of 21 external
pins?

If that's correct and if you wish to interface with e.g. 20 chips,
then it would be possible to have 20 states for one mux control and
then reach the 20 chips using only CS0 from FLEXCOM0, or it would be
possible to have 2 states for 10 mux controls, one each for CS0/CS1 of
all five flexcoms and also reach 20 chips. Or any wild combo you
imagine is useful.

Is that correctly understood?

Assuming so, then you can have a maximum of 10 mux controls, and for
each mux control you need a variable number of legitimate states. Each
mux control would also need to know at what address/offset its SS_MASK
register is at and what pins/states are valid.

But your code does not implemnent the above. You allocate num_fields
mux controls, which is what confuses the hell out of me. num_fields is
the number of states, not the number of mux controls! And you also
need to specify an individual offset for each state, and that makes no
sense at all, at least not to me.

So, instead, I think you want to have:

struct mux_lan966x {
	u32 ss_mask;
	int num_pins;
	u32 ss_pin[];
};

And then do:

	mux_chip = devm_mux_chip_alloc(dev, 1, sizeof(*mux_lan966x) + num_pins * sizeof(u32));

(or however that size is best spelled, I haven't kept up)

The set operation would be along the lines of:

static int mux_lan966x_set(struct mux_control *mux, int state)
{
	struct mux_lan966x *mux_lan966x = mux_chip_priv(mux->chip);
	u32 val;

	if (state < 0 || state >= mux_lan966x->num_pins)
		return -1;

	val = ~(1 << mux_lan966x->ss_pin[state]) & FLEX_SHRD_MASK;
	writel(val, flx_shared_base + mux_lan966x->ss_mask);

	return 0;
}

Because, one mux control should only ever need to know about one offset,
as it should only ever write to its own SS_MASK register. And you will
need some private space such that you can keep track of which states
are legit.

I would also separate out the SS_MASK offset from the mux-offset-pin
property and have one property for that value and then a straight
array for the valid pin numbers in another property (no longer named
mux-offset-pin of course).

Or something like that and assuming I understand how the FLEXCOMs work
and what you want to do etc.

Cheers,
Peter


>>> +     if (IS_ERR(mux_chip))
>>> +             return dev_err_probe(dev, PTR_ERR(mux_chip),
>>> +                                  "failed to allocate mux_chips\n");
>>> +
>>> +     mux_lan966x = mux_chip_priv(mux_chip);
>>> +
>>> +     flx_shared_base = devm_platform_get_and_ioremap_resource(pdev,
>> 0, NULL);
>>> +     if (IS_ERR(flx_shared_base))
>>> +             return dev_err_probe(dev, PTR_ERR(flx_shared_base),
>>> +                                  "failed to get flexcom shared base address\n");
>>> +
>>> +     for (i = 0; i < num_fields; i++) {
>>> +             struct mux_control *mux = &mux_chip->mux[i];
>>> +             u32 offset, shared_pin;
>>> +
>>> +             ret = of_property_read_u32_index(np, "mux-offset-pin",
>>> +                                              2 * i, &offset);
>>> +             if (ret == 0)
>>> +                     ret = of_property_read_u32_index(np, "mux-offset-pin",
>>> +                                                      2 * i + 1,
>>> +                                                      &shared_pin);
>>> +             if (ret < 0)
>>> +                     return dev_err_probe(dev, ret,
>>> +                                          "failed to read mux-offset-pin property: %d", i);
>>> +
>>> +             if (shared_pin >= LAN966_MAX_CS)
>>> +                     return -EINVAL;
>>> +
>>> +             mux_lan966x[i].offset = offset;
>>> +             mux_lan966x[i].ss_pin = shared_pin;
>>
>> This clobbers memory you have not allocated, if num_fields >= 1.
>>
>> Cheers,
>> Peter
>>
>>> +
>>> +             mux->states = LAN966_MAX_CS;
>>> +     }
>>> +
>>> +     mux_chip->ops = &mux_lan966x_ops;
>>> +
>>> +     ret = devm_mux_chip_register(dev, mux_chip);
>>> +     if (ret < 0)
>>> +             return ret;
>>> +
>>> +     return 0;
>>> +}
>>> +
>>> +static struct platform_driver mux_lan966x_driver = {
>>> +     .driver = {
>>> +             .name = "lan966-mux",
>>> +             .of_match_table = of_match_ptr(mux_lan966x_dt_ids),
>>> +     },
>>> +     .probe = mux_lan966x_probe,
>>> +};
>>> +
>>> +module_platform_driver(mux_lan966x_driver);
>>> +
>>> +MODULE_DESCRIPTION("LAN966 Flexcom multiplexer driver");
>>> +MODULE_AUTHOR("Kavyasree Kotagiri
>> <kavyasree.kotagiri@microchip.com>");
>>> +MODULE_LICENSE("GPL v2");
>>> +

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v2 4/4] mux: lan966: Add support for flexcom mux controller
  2022-05-09  8:49 ` [PATCH v2 4/4] mux: lan966: Add support for " Kavyasree Kotagiri
                     ` (2 preceding siblings ...)
  2022-05-10  3:28   ` kernel test robot
@ 2022-05-11  9:06   ` Claudiu.Beznea
  2022-05-23 14:52   ` Lee Jones
  4 siblings, 0 replies; 21+ messages in thread
From: Claudiu.Beznea @ 2022-05-11  9:06 UTC (permalink / raw)
  To: Kavyasree.Kotagiri, krzysztof.kozlowski+dt, Nicolas.Ferre,
	alexandre.belloni, peda
  Cc: devicetree, linux-arm-kernel, linux-kernel, lee.jones, linux,
	Manohar.Puri, UNGLinuxDriver

Hi, Kavyasree,

On 09.05.2022 11:49, Kavyasree Kotagiri wrote:
> LAN966 SoC have 5 flexcoms. Each flexcom has 2 chip-selects.
> For each chip select of each flexcom there is a configuration
> register FLEXCOM_SHARED[0-4]:SS_MASK[0-1]. The width of
> configuration register is 21 because there are 21 shared pins
> on each of which the chip select can be mapped. Each bit of the
> register represents a different FLEXCOM_SHARED pin.
> 
> Signed-off-by: Kavyasree Kotagiri <kavyasree.kotagiri@microchip.com>
> ---
>  arch/arm/mach-at91/Kconfig  |   2 +
>  drivers/mfd/atmel-flexcom.c |  55 +++++++++++++++-
>  drivers/mux/Kconfig         |  12 ++++
>  drivers/mux/Makefile        |   2 +
>  drivers/mux/lan966-flx.c    | 121 ++++++++++++++++++++++++++++++++++++
>  5 files changed, 191 insertions(+), 1 deletion(-)
>  create mode 100644 drivers/mux/lan966-flx.c
> 
> diff --git a/arch/arm/mach-at91/Kconfig b/arch/arm/mach-at91/Kconfig
> index 279810381256..26fb0f4e1b79 100644
> --- a/arch/arm/mach-at91/Kconfig
> +++ b/arch/arm/mach-at91/Kconfig
> @@ -74,6 +74,8 @@ config SOC_LAN966
>  	select DW_APB_TIMER_OF
>  	select ARM_GIC
>  	select MEMORY
> +	select MULTIPLEXER
> +	select MUX_LAN966
>  	help
>  	  This enables support for ARMv7 based Microchip LAN966 SoC family.
>  
> diff --git a/drivers/mfd/atmel-flexcom.c b/drivers/mfd/atmel-flexcom.c
> index 559eb4d352b6..7cfd0fc3f4f0 100644
> --- a/drivers/mfd/atmel-flexcom.c
> +++ b/drivers/mfd/atmel-flexcom.c
> @@ -17,6 +17,7 @@
>  #include <linux/io.h>
>  #include <linux/clk.h>
>  #include <dt-bindings/mfd/atmel-flexcom.h>
> +#include <linux/mux/consumer.h>
>  
>  /* I/O register offsets */
>  #define FLEX_MR		0x0	/* Mode Register */
> @@ -28,6 +29,10 @@
>  #define FLEX_MR_OPMODE(opmode)	(((opmode) << FLEX_MR_OPMODE_OFFSET) &	\
>  				 FLEX_MR_OPMODE_MASK)
>  
> +struct atmel_flex_caps {
> +	bool has_flx_mux;
> +};
> +
>  struct atmel_flexcom {
>  	void __iomem *base;
>  	u32 opmode;
> @@ -37,6 +42,7 @@ struct atmel_flexcom {
>  static int atmel_flexcom_probe(struct platform_device *pdev)
>  {
>  	struct device_node *np = pdev->dev.of_node;
> +	const struct atmel_flex_caps *caps;
>  	struct resource *res;
>  	struct atmel_flexcom *ddata;
>  	int err;
> @@ -76,13 +82,60 @@ static int atmel_flexcom_probe(struct platform_device *pdev)
>  	 */
>  	writel(FLEX_MR_OPMODE(ddata->opmode), ddata->base + FLEX_MR);
>  
> +	caps = of_device_get_match_data(&pdev->dev);
> +	if (!caps) {
> +		dev_err(&pdev->dev, "Could not retrieve flexcom caps\n");
> +		return -EINVAL;
> +	}
> +
> +	/* Flexcom Mux */
> +	if (caps->has_flx_mux && of_property_read_bool(np, "mux-controls")) {
> +		struct mux_control *flx_mux;
> +		struct of_phandle_args args;
> +		int i, count;
> +
> +		flx_mux = devm_mux_control_get(&pdev->dev, NULL);
> +		if (IS_ERR(flx_mux))
> +			return PTR_ERR(flx_mux);
> +
> +		count = of_property_count_strings(np, "mux-control-names");
> +		for (i = 0; i < count; i++) {
> +			err = of_parse_phandle_with_fixed_args(np, "mux-controls", 1, i, &args);
> +			if (err)
> +				break;
> +
> +			err = mux_control_select(flx_mux, args.args[0]);
> +			if (!err) {
> +				mux_control_deselect(flx_mux);
> +			} else {
> +				dev_err(&pdev->dev, "Failed to select FLEXCOM mux\n");
> +				return err;
> +			}
> +		}
> +	}
> +

Can you move this in a separate function and take care of disabling the
clock on error cases?

>  	clk_disable_unprepare(ddata->clk);
>  
>  	return devm_of_platform_populate(&pdev->dev);
>  }
>  
> +static const struct atmel_flex_caps atmel_flexcom_caps = {};
> +
> +static const struct atmel_flex_caps lan966x_flexcom_caps = {
> +	.has_flx_mux = true,
> +};
> +
>  static const struct of_device_id atmel_flexcom_of_match[] = {
> -	{ .compatible = "atmel,sama5d2-flexcom" },
> +	{
> +		.compatible = "atmel,sama5d2-flexcom",
> +		.data = &atmel_flexcom_caps,
> +	},
> +
> +	{
> +		.compatible = "microchip,lan966-flexcom",
> +		.data = &lan966x_flexcom_caps,
> +	},
> +
>  	{ /* sentinel */ }
>  };
>  MODULE_DEVICE_TABLE(of, atmel_flexcom_of_match);
> diff --git a/drivers/mux/Kconfig b/drivers/mux/Kconfig
> index e5c571fd232c..ea09f474bc2f 100644
> --- a/drivers/mux/Kconfig
> +++ b/drivers/mux/Kconfig
> @@ -45,6 +45,18 @@ config MUX_GPIO
>  	  To compile the driver as a module, choose M here: the module will
>  	  be called mux-gpio.
>  
> +config MUX_LAN966
> +	tristate "LAN966 Flexcom multiplexer"
> +	depends on OF || COMPILE_TEST
> +	help
> +	Lan966 Flexcom Multiplexer controller.
> +
> +	The driver supports mapping 2 chip-selects of each of the lan966
> +	flexcoms to 21 flexcom shared pins.
> +
> +	To compile the driver as a module, choose M here: the module will
> +	be called mux-lan966.

Ussually the help message is aligned 2 spaces on right after help.

> +
>  config MUX_MMIO
>  	tristate "MMIO/Regmap register bitfield-controlled Multiplexer"
>  	depends on OF || COMPILE_TEST
> diff --git a/drivers/mux/Makefile b/drivers/mux/Makefile
> index 6e9fa47daf56..53a9840d96fa 100644
> --- a/drivers/mux/Makefile
> +++ b/drivers/mux/Makefile
> @@ -7,10 +7,12 @@ mux-core-objs			:= core.o
>  mux-adg792a-objs		:= adg792a.o
>  mux-adgs1408-objs		:= adgs1408.o
>  mux-gpio-objs			:= gpio.o
> +mux-lan966-objs			:= lan966-flx.o
>  mux-mmio-objs			:= mmio.o
>  
>  obj-$(CONFIG_MULTIPLEXER)	+= mux-core.o
>  obj-$(CONFIG_MUX_ADG792A)	+= mux-adg792a.o
>  obj-$(CONFIG_MUX_ADGS1408)	+= mux-adgs1408.o
>  obj-$(CONFIG_MUX_GPIO)		+= mux-gpio.o
> +obj-$(CONFIG_MUX_LAN966)	+= mux-lan966.o
>  obj-$(CONFIG_MUX_MMIO)		+= mux-mmio.o
> diff --git a/drivers/mux/lan966-flx.c b/drivers/mux/lan966-flx.c
> new file mode 100644
> index 000000000000..2c7dab616a6a
> --- /dev/null
> +++ b/drivers/mux/lan966-flx.c
> @@ -0,0 +1,121 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * LAN966 Flexcom MUX driver
> + *
> + * Copyright (c) 2022 Microchip Inc.
> + *
> + * Author: Kavyasree Kotagiri <kavyasree.kotagiri@microchip.com>
> + */
> +
> +#include <linux/err.h>
> +#include <linux/module.h>
> +#include <linux/of_platform.h>
> +#include <linux/platform_device.h>
> +#include <linux/property.h>
> +#include <linux/mux/driver.h>
> +#include <linux/io.h>
> +
> +#define FLEX_SHRD_MASK		0x1FFFFF

Or you can use GENMASK()

> +#define LAN966_MAX_CS		21
> +
> +static void __iomem *flx_shared_base;

I agree with Peter's point of view on this.

> +struct mux_lan966x {
> +	u32 offset;
> +	u32 ss_pin;
> +};
> +
> +static int mux_lan966x_set(struct mux_control *mux, int state)
> +{
> +	struct mux_lan966x *mux_lan966x = mux_chip_priv(mux->chip);
> +	u32 val;
> +
> +	val = ~(1 << mux_lan966x[state].ss_pin) & FLEX_SHRD_MASK;
> +	writel(val, flx_shared_base + mux_lan966x[state].offset);

writel_relaxed should be enough here.

> +
> +	return 0;
> +}
> +
> +static const struct mux_control_ops mux_lan966x_ops = {
> +	.set = mux_lan966x_set,
> +};
> +
> +static const struct of_device_id mux_lan966x_dt_ids[] = {
> +	{ .compatible = "microchip,lan966-flx-mux", },
> +	{ /* sentinel */ }
> +};
> +MODULE_DEVICE_TABLE(of, mux_lan966x_dt_ids);
> +
> +static int mux_lan966x_probe(struct platform_device *pdev)
> +{
> +	struct device_node *np = pdev->dev.of_node;
> +	struct device *dev = &pdev->dev;
> +	struct mux_lan966x *mux_lan966x;
> +	struct mux_chip *mux_chip;
> +	int ret, num_fields, i;
> +
> +	ret = of_property_count_u32_elems(np, "mux-offset-pin");
> +	if (ret == 0 || ret % 2)
> +		ret = -EINVAL;
> +	if (ret < 0)
> +		return dev_err_probe(dev, ret,
> +				     "mux-offset-pin property missing or invalid");
> +	num_fields = ret / 2;
> +
> +	mux_chip = devm_mux_chip_alloc(dev, num_fields, sizeof(*mux_lan966x));
> +	if (IS_ERR(mux_chip))
> +		return dev_err_probe(dev, PTR_ERR(mux_chip),
> +				     "failed to allocate mux_chips\n");
> +
> +	mux_lan966x = mux_chip_priv(mux_chip);
> +
> +	flx_shared_base = devm_platform_get_and_ioremap_resource(pdev, 0, NULL);
> +	if (IS_ERR(flx_shared_base))
> +		return dev_err_probe(dev, PTR_ERR(flx_shared_base),
> +				     "failed to get flexcom shared base address\n");
> +
> +	for (i = 0; i < num_fields; i++) {
> +		struct mux_control *mux = &mux_chip->mux[i];
> +		u32 offset, shared_pin;
> +
> +		ret = of_property_read_u32_index(np, "mux-offset-pin",
> +						 2 * i, &offset);
> +		if (ret == 0)
> +			ret = of_property_read_u32_index(np, "mux-offset-pin",
> +							 2 * i + 1,
> +							 &shared_pin);
> +		if (ret < 0)
> +			return dev_err_probe(dev, ret,
> +					     "failed to read mux-offset-pin property: %d", i);
> +
> +		if (shared_pin >= LAN966_MAX_CS)
> +			return -EINVAL;
> +
> +		mux_lan966x[i].offset = offset;
> +		mux_lan966x[i].ss_pin = shared_pin;
> +
> +		mux->states = LAN966_MAX_CS;
> +	}
> +
> +	mux_chip->ops = &mux_lan966x_ops;
> +
> +	ret = devm_mux_chip_register(dev, mux_chip);
> +	if (ret < 0)
> +		return ret;
> +
> +	return 0;

you can just return ret; or return devm_mux_chip_register(dev, mux_chip);

> +}
> +
> +static struct platform_driver mux_lan966x_driver = {
> +	.driver = {
> +		.name = "lan966-mux",
> +		.of_match_table	= of_match_ptr(mux_lan966x_dt_ids),
> +	},
> +	.probe = mux_lan966x_probe,
> +};
> +
> +module_platform_driver(mux_lan966x_driver);
> +
> +MODULE_DESCRIPTION("LAN966 Flexcom multiplexer driver");
> +MODULE_AUTHOR("Kavyasree Kotagiri <kavyasree.kotagiri@microchip.com>");
> +MODULE_LICENSE("GPL v2");

Last time I checked checkpatch.pl complained about using "GPL v2" string
here and suggested to use "GPL".

> +


^ permalink raw reply	[flat|nested] 21+ messages in thread

* RE: [PATCH v2 4/4] mux: lan966: Add support for flexcom mux controller
  2022-05-10 19:38       ` Peter Rosin
@ 2022-05-11 14:28         ` Kavyasree.Kotagiri
  2022-05-11 15:43           ` Peter Rosin
  0 siblings, 1 reply; 21+ messages in thread
From: Kavyasree.Kotagiri @ 2022-05-11 14:28 UTC (permalink / raw)
  To: peda
  Cc: devicetree, linux-arm-kernel, linux-kernel, lee.jones, linux,
	Manohar.Puri, UNGLinuxDriver, krzysztof.kozlowski+dt,
	Nicolas.Ferre, alexandre.belloni, Claudiu.Beznea

> 2022-05-10 at 16:59, Kavyasree.Kotagiri@microchip.com wrote:
> >>> LAN966 SoC have 5 flexcoms. Each flexcom has 2 chip-selects.
> >>> For each chip select of each flexcom there is a configuration
> >>> register FLEXCOM_SHARED[0-4]:SS_MASK[0-1]. The width of
> >>> configuration register is 21 because there are 21 shared pins
> >>> on each of which the chip select can be mapped. Each bit of the
> >>> register represents a different FLEXCOM_SHARED pin.
> >>>
> >>> Signed-off-by: Kavyasree Kotagiri <kavyasree.kotagiri@microchip.com>
> >>> ---
> >>>  arch/arm/mach-at91/Kconfig  |   2 +
> >>>  drivers/mfd/atmel-flexcom.c |  55 +++++++++++++++-
> >>>  drivers/mux/Kconfig         |  12 ++++
> >>>  drivers/mux/Makefile        |   2 +
> >>>  drivers/mux/lan966-flx.c    | 121
> >> ++++++++++++++++++++++++++++++++++++
> >>>  5 files changed, 191 insertions(+), 1 deletion(-)
> >>>  create mode 100644 drivers/mux/lan966-flx.c
> >>>
> >>> diff --git a/arch/arm/mach-at91/Kconfig b/arch/arm/mach-at91/Kconfig
> >>> index 279810381256..26fb0f4e1b79 100644
> >>> --- a/arch/arm/mach-at91/Kconfig
> >>> +++ b/arch/arm/mach-at91/Kconfig
> >>> @@ -74,6 +74,8 @@ config SOC_LAN966
> >>>       select DW_APB_TIMER_OF
> >>>       select ARM_GIC
> >>>       select MEMORY
> >>> +     select MULTIPLEXER
> >>> +     select MUX_LAN966
> >>>       help
> >>>         This enables support for ARMv7 based Microchip LAN966 SoC
> family.
> >>>
> >>> diff --git a/drivers/mfd/atmel-flexcom.c b/drivers/mfd/atmel-flexcom.c
> >>> index 559eb4d352b6..7cfd0fc3f4f0 100644
> >>> --- a/drivers/mfd/atmel-flexcom.c
> >>> +++ b/drivers/mfd/atmel-flexcom.c
> >>> @@ -17,6 +17,7 @@
> >>>  #include <linux/io.h>
> >>>  #include <linux/clk.h>
> >>>  #include <dt-bindings/mfd/atmel-flexcom.h>
> >>> +#include <linux/mux/consumer.h>
> >>>
> >>>  /* I/O register offsets */
> >>>  #define FLEX_MR              0x0     /* Mode Register */
> >>> @@ -28,6 +29,10 @@
> >>>  #define FLEX_MR_OPMODE(opmode)       (((opmode) <<
> >> FLEX_MR_OPMODE_OFFSET) &  \
> >>>                                FLEX_MR_OPMODE_MASK)
> >>>
> >>> +struct atmel_flex_caps {
> >>> +     bool has_flx_mux;
> >>> +};
> >>> +
> >>>  struct atmel_flexcom {
> >>>       void __iomem *base;
> >>>       u32 opmode;
> >>> @@ -37,6 +42,7 @@ struct atmel_flexcom {
> >>>  static int atmel_flexcom_probe(struct platform_device *pdev)
> >>>  {
> >>>       struct device_node *np = pdev->dev.of_node;
> >>> +     const struct atmel_flex_caps *caps;
> >>>       struct resource *res;
> >>>       struct atmel_flexcom *ddata;
> >>>       int err;
> >>> @@ -76,13 +82,60 @@ static int atmel_flexcom_probe(struct
> >> platform_device *pdev)
> >>>        */
> >>>       writel(FLEX_MR_OPMODE(ddata->opmode), ddata->base +
> FLEX_MR);
> >>>
> >>> +     caps = of_device_get_match_data(&pdev->dev);
> >>> +     if (!caps) {
> >>> +             dev_err(&pdev->dev, "Could not retrieve flexcom caps\n");
> >>> +             return -EINVAL;
> >>> +     }
> >>> +
> >>> +     /* Flexcom Mux */
> >>> +     if (caps->has_flx_mux && of_property_read_bool(np, "mux-
> controls"))
> >> {
> >>> +             struct mux_control *flx_mux;
> >>> +             struct of_phandle_args args;
> >>> +             int i, count;
> >>> +
> >>> +             flx_mux = devm_mux_control_get(&pdev->dev, NULL);
> >>> +             if (IS_ERR(flx_mux))
> >>> +                     return PTR_ERR(flx_mux);
> >>> +
> >>> +             count = of_property_count_strings(np, "mux-control-names");
> >>> +             for (i = 0; i < count; i++) {
> >>> +                     err = of_parse_phandle_with_fixed_args(np, "mux-
> controls",
> >> 1, i, &args);
> >>> +                     if (err)
> >>> +                             break;
> >>> +
> >>> +                     err = mux_control_select(flx_mux, args.args[0]);
> >>> +                     if (!err) {
> >>> +                             mux_control_deselect(flx_mux);
> >>
> >> This is suspect. When you deselect the mux you rely on the mux to be
> >> configured with "as-is" as the idle state. But you do not document that.
> >> This is also fragile in that you silently rely on noone else selecting
> >> the mux to some unwanted state behind your back (mux controls are not
> >> exclusive the way e.g. gpio pins or pwms are). The protocol is that
> >> others may get a ref to the mux control and select it as long as noone
> >> else has selected it.
> >>
> >> The only sane thing to do is to keep the mux selected for the whole
> >> duration when you rely on it to be in your desired state.
> >>
> >
> > Yes, mux is kept selected until configuring register is done. Please see
> below log where
> > I added debug prints just for understanding:
> > # dmesg | grep KK
> >  [    0.779827] KK: Func: atmel_flexcom_probe ***** [START flx muxing]
> ********
> > [    0.779875] KK: Func: atmel_flexcom_probe i = 0 args[0] = 0
> > [    0.779890] KK: Func: mux_control_select [Entered]
> > [    0.779902] KK: Func: mux_lan966x_set [Entered] state = 0
> > [    0.779977] KK: Func: mux_lan966x_set [Read] = 0x1fffef   <<<----- setting
> mux_lan966x[state].ss_pin "4" which is passed from dts
> > [    0.779992] KK: Func: mux_lan966x_set [Exit]
> > [    0.780002] KK: Func: mux_control_select [Exit]
> > [    0.780011] KK: Func: mux_control_deselect [Entered]
> > [    0.780021] KK: Func: mux_control_deselect [Exit]
> 
> You misunderstand. The mux control is only "selected" between the call
> to mux_control_select() and the following call to
> mux_control_deselect().
> 
> After that, the mux control is "idle". However, in your case the
> "idle-state" is configured to "as-is" (which is the default), so the
> mux control (naturally) remains in the previously selected state while
> idle. But you are not documenting that limitation, and with this
> implementation you *must* have a mux control that uses "as-is" as its
> idle state. But that is an unexpected and broken limitation, and a
> much better solution is to simply keep the mux control "selected" for
> the complete duration when you rely on it to be in whatever state you
> need it to be in.
> 
I am new to mux drivers.
Let me try to explain why I have mux_control_deselect if there is no err from mux_control_select()
For example, 
1. When I have one only chip-select CS0 for flexcom3 to be mapped to flexcom shared pin 9:
As per previously shared log, FLEXCOM_SHARED[3]:SS_MASK[0] is being configured to expected value 
even before mux_control_deseletc().
2. When I have to map two chip-selects of flx3 - CS0 to flexcom shared 9
						CS1 to flexcom shared pin 7
FLEXCOM_SHARED[3]:SS_MASK[0] is set to expected value 0x1fffef
I see console hangs at mux_control_select() if I don’t call mux_control_deselect 
while mapping second chip-select FLEXCOM_SHARED[3]:SS_MASK[1].
After reading below description from mux_control_select() :
" * On successfully selecting the mux-control state, it will be locked until
 * there is a call to mux_control_deselect()."
Following this help text, I called mux_control_deselect() if there is no error from mux_control_select() 
and then it worked. FLEXCOM_SHARED[3]:SS_MASK[1] is now set to expected value 0x1fffbf.

Please explain me if I am missing something.

> >>> +                     } else {
> >>> +                             dev_err(&pdev->dev, "Failed to select FLEXCOM
> mux\n");
> >>> +                             return err;
> >>> +                     }
> >>> +             }
> >>> +     }
> >>> +
> >>>       clk_disable_unprepare(ddata->clk);
> >>>
> >>>       return devm_of_platform_populate(&pdev->dev);
> >>>  }
> >>>
> >>> +static const struct atmel_flex_caps atmel_flexcom_caps = {};
> >>> +
> >>> +static const struct atmel_flex_caps lan966x_flexcom_caps = {
> >>> +     .has_flx_mux = true,
> >>> +};
> >>> +
> >>>  static const struct of_device_id atmel_flexcom_of_match[] = {
> >>> -     { .compatible = "atmel,sama5d2-flexcom" },
> >>> +     {
> >>> +             .compatible = "atmel,sama5d2-flexcom",
> >>> +             .data = &atmel_flexcom_caps,
> >>> +     },
> >>> +
> >>> +     {
> >>> +             .compatible = "microchip,lan966-flexcom",
> >>> +             .data = &lan966x_flexcom_caps,
> >>> +     },
> >>> +
> >>>       { /* sentinel */ }
> >>>  };
> >>>  MODULE_DEVICE_TABLE(of, atmel_flexcom_of_match);
> >>> diff --git a/drivers/mux/Kconfig b/drivers/mux/Kconfig
> >>> index e5c571fd232c..ea09f474bc2f 100644
> >>> --- a/drivers/mux/Kconfig
> >>> +++ b/drivers/mux/Kconfig
> >>> @@ -45,6 +45,18 @@ config MUX_GPIO
> >>>         To compile the driver as a module, choose M here: the module will
> >>>         be called mux-gpio.
> >>>
> >>> +config MUX_LAN966
> >>> +     tristate "LAN966 Flexcom multiplexer"
> >>> +     depends on OF || COMPILE_TEST
> >>> +     help
> >>> +     Lan966 Flexcom Multiplexer controller.
> >>> +
> >>> +     The driver supports mapping 2 chip-selects of each of the lan966
> >>> +     flexcoms to 21 flexcom shared pins.
> >>> +
> >>> +     To compile the driver as a module, choose M here: the module will
> >>> +     be called mux-lan966.
> >>> +
> >>>  config MUX_MMIO
> >>>       tristate "MMIO/Regmap register bitfield-controlled Multiplexer"
> >>>       depends on OF || COMPILE_TEST
> >>> diff --git a/drivers/mux/Makefile b/drivers/mux/Makefile
> >>> index 6e9fa47daf56..53a9840d96fa 100644
> >>> --- a/drivers/mux/Makefile
> >>> +++ b/drivers/mux/Makefile
> >>> @@ -7,10 +7,12 @@ mux-core-objs                       := core.o
> >>>  mux-adg792a-objs             := adg792a.o
> >>>  mux-adgs1408-objs            := adgs1408.o
> >>>  mux-gpio-objs                        := gpio.o
> >>> +mux-lan966-objs                      := lan966-flx.o
> >>>  mux-mmio-objs                        := mmio.o
> >>>
> >>>  obj-$(CONFIG_MULTIPLEXER)    += mux-core.o
> >>>  obj-$(CONFIG_MUX_ADG792A)    += mux-adg792a.o
> >>>  obj-$(CONFIG_MUX_ADGS1408)   += mux-adgs1408.o
> >>>  obj-$(CONFIG_MUX_GPIO)               += mux-gpio.o
> >>> +obj-$(CONFIG_MUX_LAN966)     += mux-lan966.o
> >>>  obj-$(CONFIG_MUX_MMIO)               += mux-mmio.o
> >>> diff --git a/drivers/mux/lan966-flx.c b/drivers/mux/lan966-flx.c
> >>> new file mode 100644
> >>> index 000000000000..2c7dab616a6a
> >>> --- /dev/null
> >>> +++ b/drivers/mux/lan966-flx.c
> >>> @@ -0,0 +1,121 @@
> >>> +// SPDX-License-Identifier: GPL-2.0
> >>> +/*
> >>> + * LAN966 Flexcom MUX driver
> >>> + *
> >>> + * Copyright (c) 2022 Microchip Inc.
> >>> + *
> >>> + * Author: Kavyasree Kotagiri <kavyasree.kotagiri@microchip.com>
> >>
> >> Looks like it is based on mmio.c?
> >>
> > Yes, I took mmio.c  driver as reference.
> >
> 
> So, then the above copyright and authorship info is not complete.
> 
> >>> + */
> >>> +
> >>> +#include <linux/err.h>
> >>> +#include <linux/module.h>
> >>> +#include <linux/of_platform.h>
> >>> +#include <linux/platform_device.h>
> >>> +#include <linux/property.h>
> >>> +#include <linux/mux/driver.h>
> >>> +#include <linux/io.h>
> >>> +
> >>> +#define FLEX_SHRD_MASK               0x1FFFFF
> >>> +#define LAN966_MAX_CS                21
> >>> +
> >>> +static void __iomem *flx_shared_base;
> >>
> >> I would much prefer to store the combined address (base+offset)
> >> in the mux private data instead of only storing the offset and
> >> then unnecessarily rely on some piece of global state (that
> >> will be clobbered by other instances).
> >>
> > Ok. I will try to see if this is relevant and change accordingly.
> >
> >>> +struct mux_lan966x {
> >>
> >> Why is the file named lan966, but then everything inside lan966x?
> >>
> >>> +     u32 offset;
> >>> +     u32 ss_pin;
> >>> +};
> >>> +
> >>> +static int mux_lan966x_set(struct mux_control *mux, int state)
> >>> +{
> >>> +     struct mux_lan966x *mux_lan966x = mux_chip_priv(mux->chip);
> >>> +     u32 val;
> >>> +
> >>> +     val = ~(1 << mux_lan966x[state].ss_pin) & FLEX_SHRD_MASK;
> >>> +     writel(val, flx_shared_base + mux_lan966x[state].offset);
> >>
> >> This reads memory you have not allocated, if you select a state
> >> other than zero.
> >>
> > Ok. I will return error condition in case of trying to access none existing
> entry.
> >>> +
> >>> +     return 0;
> >>> +}
> >>> +
> >>> +static const struct mux_control_ops mux_lan966x_ops = {
> >>> +     .set = mux_lan966x_set,
> >>> +};
> >>> +
> >>> +static const struct of_device_id mux_lan966x_dt_ids[] = {
> >>> +     { .compatible = "microchip,lan966-flx-mux", },
> >>> +     { /* sentinel */ }
> >>> +};
> >>> +MODULE_DEVICE_TABLE(of, mux_lan966x_dt_ids);
> >>> +
> >>> +static int mux_lan966x_probe(struct platform_device *pdev)
> >>> +{
> >>> +     struct device_node *np = pdev->dev.of_node;
> >>> +     struct device *dev = &pdev->dev;
> >>> +     struct mux_lan966x *mux_lan966x;
> >>> +     struct mux_chip *mux_chip;
> >>> +     int ret, num_fields, i;
> >>> +
> >>> +     ret = of_property_count_u32_elems(np, "mux-offset-pin");
> >>> +     if (ret == 0 || ret % 2)
> >>> +             ret = -EINVAL;
> >>> +     if (ret < 0)
> >>> +             return dev_err_probe(dev, ret,
> >>> +                                  "mux-offset-pin property missing or invalid");
> >>> +     num_fields = ret / 2;
> >>> +
> >>> +     mux_chip = devm_mux_chip_alloc(dev, num_fields,
> >> sizeof(*mux_lan966x));
> >>
> >> I might be thoroughly mistaken and confused by the code, but it seems
> >> very strange that a subsequenct select is not undoing what a previous
> >> select once did. Each state seems to write to its own register offset,
> >> and there is nothing that restores the first register offset with you
> >> switch states.
> >>
> >> Care to explain how muxing works and what you are expected to do to
> >> manipulate the mux? Is there some link to public documentation? I did
> >> a quick search for lan966 but came up with nothing relevant.
> >>
> > LAN966 has 5 flexcoms(which can be used as USART/SPI/I2C interface).
> > FLEXCOM has two chip-select I/O lines namely CS0 and CS1
> > in SPI mode, CTS and RTS in USART mode. These FLEXCOM pins are
> optional.
> > These chip-selects can be mapped to flexcom shared pin [0-21] which can
> be
> > done by configuring flexcom multiplexer register(FLEXCOM_SHARED[0-
> 4]:SS_MASK[0-1])
> > Driver explanation:
> > "flx_shared_base" is used to get base address of Flexcom shared
> multiplexer
> > "mux-offset-pin" property is used to get cs0/cs1 offset and flexcom shared
> pin[0-21] of a flexcom.
> > state value passed from atmel-flexcom is used to configure respective
> > FLEXCOM_SHARED[0-4]:SS_MASK[0-1] register with offset and flexcom
> shared pin.
> 
> Ok, let me try to interpret that. You wish enable a "fan out" of these
> two chip-selects for any of the 5 flexcoms (in SPI mode?) such that
> these 10 internal chip-selects can be connected to any of 21 external
> pins?
> 
> If that's correct and if you wish to interface with e.g. 20 chips,
> then it would be possible to have 20 states for one mux control and
> then reach the 20 chips using only CS0 from FLEXCOM0, or it would be
> possible to have 2 states for 10 mux controls, one each for CS0/CS1 of
> all five flexcoms and also reach 20 chips. Or any wild combo you
> imagine is useful.
> 
> Is that correctly understood?
> 
> Assuming so, then you can have a maximum of 10 mux controls, and for
> each mux control you need a variable number of legitimate states. Each
> mux control would also need to know at what address/offset its SS_MASK
> register is at and what pins/states are valid.
> 
> But your code does not implemnent the above. You allocate num_fields
> mux controls, which is what confuses the hell out of me. num_fields is
> the number of states, not the number of mux controls! And you also
> need to specify an individual offset for each state, and that makes no
> sense at all, at least not to me.
> 
> So, instead, I think you want to have:
> 
> struct mux_lan966x {
>         u32 ss_mask;
>         int num_pins;
>         u32 ss_pin[];
> };
> 
> And then do:
> 
>         mux_chip = devm_mux_chip_alloc(dev, 1, sizeof(*mux_lan966x) +
> num_pins * sizeof(u32));
> 
> (or however that size is best spelled, I haven't kept up)
> 
> The set operation would be along the lines of:
>
> static int mux_lan966x_set(struct mux_control *mux, int state)
> {
>         struct mux_lan966x *mux_lan966x = mux_chip_priv(mux->chip);
>         u32 val;
> 
>         if (state < 0 || state >= mux_lan966x->num_pins)
>                 return -1;
> 
>         val = ~(1 << mux_lan966x->ss_pin[state]) & FLEX_SHRD_MASK;
>         writel(val, flx_shared_base + mux_lan966x->ss_mask);
> 
>         return 0;
> }
> 
> Because, one mux control should only ever need to know about one offset,
> as it should only ever write to its own SS_MASK register. And you will
> need some private space such that you can keep track of which states
> are legit.
> 
> I would also separate out the SS_MASK offset from the mux-offset-pin
> property and have one property for that value and then a straight
> array for the valid pin numbers in another property (no longer named
> mux-offset-pin of course).
> 
> Or something like that and assuming I understand how the FLEXCOMs work
> and what you want to do etc.
> 

Thank you for your comments
I agree, I will change number of mux controls to 1 in devm_mux_chip_alloc()
I would like to use mux-offset-pin property because 
each chip-select must be mapped to a unique flexcom shared pin.
For example, 
mux-offset-pin = <0x18 9>, /* 0: flexcom3 CS0 mapped to pin-9 */
                               <0x1c 7>, /* 1: flexcom3 CS1 mapped to pin-7 */
                               <0x20 4>; /* 2: flexcom4 CS0 mapped to pin-4 */};
I want to use mux-offset-pin property to be clear about which offset is mapped to which
flexcom shared pin. Here, 0, 1, 2.. entries represents state passed from mux_control_select(mux, state).

Please provide your comments.

> Cheers,
> Peter
> 
> 
> >>> +     if (IS_ERR(mux_chip))
> >>> +             return dev_err_probe(dev, PTR_ERR(mux_chip),
> >>> +                                  "failed to allocate mux_chips\n");
> >>> +
> >>> +     mux_lan966x = mux_chip_priv(mux_chip);
> >>> +
> >>> +     flx_shared_base =
> devm_platform_get_and_ioremap_resource(pdev,
> >> 0, NULL);
> >>> +     if (IS_ERR(flx_shared_base))
> >>> +             return dev_err_probe(dev, PTR_ERR(flx_shared_base),
> >>> +                                  "failed to get flexcom shared base address\n");
> >>> +
> >>> +     for (i = 0; i < num_fields; i++) {
> >>> +             struct mux_control *mux = &mux_chip->mux[i];
> >>> +             u32 offset, shared_pin;
> >>> +
> >>> +             ret = of_property_read_u32_index(np, "mux-offset-pin",
> >>> +                                              2 * i, &offset);
> >>> +             if (ret == 0)
> >>> +                     ret = of_property_read_u32_index(np, "mux-offset-pin",
> >>> +                                                      2 * i + 1,
> >>> +                                                      &shared_pin);
> >>> +             if (ret < 0)
> >>> +                     return dev_err_probe(dev, ret,
> >>> +                                          "failed to read mux-offset-pin property: %d", i);
> >>> +
> >>> +             if (shared_pin >= LAN966_MAX_CS)
> >>> +                     return -EINVAL;
> >>> +
> >>> +             mux_lan966x[i].offset = offset;
> >>> +             mux_lan966x[i].ss_pin = shared_pin;
> >>
> >> This clobbers memory you have not allocated, if num_fields >= 1.
> >>
> >> Cheers,
> >> Peter
> >>
> >>> +
> >>> +             mux->states = LAN966_MAX_CS;
> >>> +     }
> >>> +
> >>> +     mux_chip->ops = &mux_lan966x_ops;
> >>> +
> >>> +     ret = devm_mux_chip_register(dev, mux_chip);
> >>> +     if (ret < 0)
> >>> +             return ret;
> >>> +
> >>> +     return 0;
> >>> +}
> >>> +
> >>> +static struct platform_driver mux_lan966x_driver = {
> >>> +     .driver = {
> >>> +             .name = "lan966-mux",
> >>> +             .of_match_table = of_match_ptr(mux_lan966x_dt_ids),
> >>> +     },
> >>> +     .probe = mux_lan966x_probe,
> >>> +};
> >>> +
> >>> +module_platform_driver(mux_lan966x_driver);
> >>> +
> >>> +MODULE_DESCRIPTION("LAN966 Flexcom multiplexer driver");
> >>> +MODULE_AUTHOR("Kavyasree Kotagiri
> >> <kavyasree.kotagiri@microchip.com>");
> >>> +MODULE_LICENSE("GPL v2");
> >>> +

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v2 4/4] mux: lan966: Add support for flexcom mux controller
  2022-05-11 14:28         ` Kavyasree.Kotagiri
@ 2022-05-11 15:43           ` Peter Rosin
  2022-05-27  6:00             ` Kavyasree.Kotagiri
  0 siblings, 1 reply; 21+ messages in thread
From: Peter Rosin @ 2022-05-11 15:43 UTC (permalink / raw)
  To: Kavyasree.Kotagiri
  Cc: devicetree, linux-arm-kernel, linux-kernel, lee.jones, linux,
	Manohar.Puri, UNGLinuxDriver, krzysztof.kozlowski+dt,
	Nicolas.Ferre, alexandre.belloni, Claudiu.Beznea

Hi!

2022-05-11 at 16:28, Kavyasree.Kotagiri@microchip.com wrote:
>> 2022-05-10 at 16:59, Kavyasree.Kotagiri@microchip.com wrote:
>>>>> LAN966 SoC have 5 flexcoms. Each flexcom has 2 chip-selects.
>>>>> For each chip select of each flexcom there is a configuration
>>>>> register FLEXCOM_SHARED[0-4]:SS_MASK[0-1]. The width of
>>>>> configuration register is 21 because there are 21 shared pins
>>>>> on each of which the chip select can be mapped. Each bit of the
>>>>> register represents a different FLEXCOM_SHARED pin.
>>>>>
>>>>> Signed-off-by: Kavyasree Kotagiri <kavyasree.kotagiri@microchip.com>
>>>>> ---
>>>>>  arch/arm/mach-at91/Kconfig  |   2 +
>>>>>  drivers/mfd/atmel-flexcom.c |  55 +++++++++++++++-
>>>>>  drivers/mux/Kconfig         |  12 ++++
>>>>>  drivers/mux/Makefile        |   2 +
>>>>>  drivers/mux/lan966-flx.c    | 121
>>>> ++++++++++++++++++++++++++++++++++++
>>>>>  5 files changed, 191 insertions(+), 1 deletion(-)
>>>>>  create mode 100644 drivers/mux/lan966-flx.c
>>>>>
>>>>> diff --git a/arch/arm/mach-at91/Kconfig b/arch/arm/mach-at91/Kconfig
>>>>> index 279810381256..26fb0f4e1b79 100644
>>>>> --- a/arch/arm/mach-at91/Kconfig
>>>>> +++ b/arch/arm/mach-at91/Kconfig
>>>>> @@ -74,6 +74,8 @@ config SOC_LAN966
>>>>>       select DW_APB_TIMER_OF
>>>>>       select ARM_GIC
>>>>>       select MEMORY
>>>>> +     select MULTIPLEXER
>>>>> +     select MUX_LAN966
>>>>>       help
>>>>>         This enables support for ARMv7 based Microchip LAN966 SoC
>> family.
>>>>>
>>>>> diff --git a/drivers/mfd/atmel-flexcom.c b/drivers/mfd/atmel-flexcom.c
>>>>> index 559eb4d352b6..7cfd0fc3f4f0 100644
>>>>> --- a/drivers/mfd/atmel-flexcom.c
>>>>> +++ b/drivers/mfd/atmel-flexcom.c
>>>>> @@ -17,6 +17,7 @@
>>>>>  #include <linux/io.h>
>>>>>  #include <linux/clk.h>
>>>>>  #include <dt-bindings/mfd/atmel-flexcom.h>
>>>>> +#include <linux/mux/consumer.h>
>>>>>
>>>>>  /* I/O register offsets */
>>>>>  #define FLEX_MR              0x0     /* Mode Register */
>>>>> @@ -28,6 +29,10 @@
>>>>>  #define FLEX_MR_OPMODE(opmode)       (((opmode) <<
>>>> FLEX_MR_OPMODE_OFFSET) &  \
>>>>>                                FLEX_MR_OPMODE_MASK)
>>>>>
>>>>> +struct atmel_flex_caps {
>>>>> +     bool has_flx_mux;
>>>>> +};
>>>>> +
>>>>>  struct atmel_flexcom {
>>>>>       void __iomem *base;
>>>>>       u32 opmode;
>>>>> @@ -37,6 +42,7 @@ struct atmel_flexcom {
>>>>>  static int atmel_flexcom_probe(struct platform_device *pdev)
>>>>>  {
>>>>>       struct device_node *np = pdev->dev.of_node;
>>>>> +     const struct atmel_flex_caps *caps;
>>>>>       struct resource *res;
>>>>>       struct atmel_flexcom *ddata;
>>>>>       int err;
>>>>> @@ -76,13 +82,60 @@ static int atmel_flexcom_probe(struct
>>>> platform_device *pdev)
>>>>>        */
>>>>>       writel(FLEX_MR_OPMODE(ddata->opmode), ddata->base +
>> FLEX_MR);
>>>>>
>>>>> +     caps = of_device_get_match_data(&pdev->dev);
>>>>> +     if (!caps) {
>>>>> +             dev_err(&pdev->dev, "Could not retrieve flexcom caps\n");
>>>>> +             return -EINVAL;
>>>>> +     }
>>>>> +
>>>>> +     /* Flexcom Mux */
>>>>> +     if (caps->has_flx_mux && of_property_read_bool(np, "mux-
>> controls"))
>>>> {
>>>>> +             struct mux_control *flx_mux;
>>>>> +             struct of_phandle_args args;
>>>>> +             int i, count;
>>>>> +
>>>>> +             flx_mux = devm_mux_control_get(&pdev->dev, NULL);
>>>>> +             if (IS_ERR(flx_mux))
>>>>> +                     return PTR_ERR(flx_mux);
>>>>> +
>>>>> +             count = of_property_count_strings(np, "mux-control-names");
>>>>> +             for (i = 0; i < count; i++) {
>>>>> +                     err = of_parse_phandle_with_fixed_args(np, "mux-
>> controls",
>>>> 1, i, &args);
>>>>> +                     if (err)
>>>>> +                             break;
>>>>> +
>>>>> +                     err = mux_control_select(flx_mux, args.args[0]);
>>>>> +                     if (!err) {
>>>>> +                             mux_control_deselect(flx_mux);
>>>>
>>>> This is suspect. When you deselect the mux you rely on the mux to be
>>>> configured with "as-is" as the idle state. But you do not document that.
>>>> This is also fragile in that you silently rely on noone else selecting
>>>> the mux to some unwanted state behind your back (mux controls are not
>>>> exclusive the way e.g. gpio pins or pwms are). The protocol is that
>>>> others may get a ref to the mux control and select it as long as noone
>>>> else has selected it.
>>>>
>>>> The only sane thing to do is to keep the mux selected for the whole
>>>> duration when you rely on it to be in your desired state.
>>>>
>>>
>>> Yes, mux is kept selected until configuring register is done. Please see
>> below log where
>>> I added debug prints just for understanding:
>>> # dmesg | grep KK
>>>  [    0.779827] KK: Func: atmel_flexcom_probe ***** [START flx muxing]
>> ********
>>> [    0.779875] KK: Func: atmel_flexcom_probe i = 0 args[0] = 0
>>> [    0.779890] KK: Func: mux_control_select [Entered]
>>> [    0.779902] KK: Func: mux_lan966x_set [Entered] state = 0
>>> [    0.779977] KK: Func: mux_lan966x_set [Read] = 0x1fffef   <<<----- setting
>> mux_lan966x[state].ss_pin "4" which is passed from dts
>>> [    0.779992] KK: Func: mux_lan966x_set [Exit]
>>> [    0.780002] KK: Func: mux_control_select [Exit]
>>> [    0.780011] KK: Func: mux_control_deselect [Entered]
>>> [    0.780021] KK: Func: mux_control_deselect [Exit]
>>
>> You misunderstand. The mux control is only "selected" between the call
>> to mux_control_select() and the following call to
>> mux_control_deselect().
>>
>> After that, the mux control is "idle". However, in your case the
>> "idle-state" is configured to "as-is" (which is the default), so the
>> mux control (naturally) remains in the previously selected state while
>> idle. But you are not documenting that limitation, and with this
>> implementation you *must* have a mux control that uses "as-is" as its
>> idle state. But that is an unexpected and broken limitation, and a
>> much better solution is to simply keep the mux control "selected" for
>> the complete duration when you rely on it to be in whatever state you
>> need it to be in.
>>
> I am new to mux drivers.
> Let me try to explain why I have mux_control_deselect if there is no err from mux_control_select()
> For example, 
> 1. When I have one only chip-select CS0 for flexcom3 to be mapped to flexcom shared pin 9:
> As per previously shared log, FLEXCOM_SHARED[3]:SS_MASK[0] is being configured to expected value 
> even before mux_control_deseletc().
> 2. When I have to map two chip-selects of flx3 - CS0 to flexcom shared 9
> 						CS1 to flexcom shared pin 7
> FLEXCOM_SHARED[3]:SS_MASK[0] is set to expected value 0x1fffef
> I see console hangs at mux_control_select() if I don’t call mux_control_deselect 
> while mapping second chip-select FLEXCOM_SHARED[3]:SS_MASK[1].
> After reading below description from mux_control_select() :
> " * On successfully selecting the mux-control state, it will be locked until
>  * there is a call to mux_control_deselect()."
> Following this help text, I called mux_control_deselect() if there is no error from mux_control_select() 
> and then it worked. FLEXCOM_SHARED[3]:SS_MASK[1] is now set to expected value 0x1fffbf.
> 
> Please explain me if I am missing something.

You should wait with the deselect until you need to change the state
of the mux. I.e., on init/probe you set some variable to -1, like so:

	priv->mux_error = -1;

and when you later need to set/change the state of the mux, you do:

	if (!priv->mux_error)
		mux_control_deselect(...);
	priv->mux_error = mux_control_select(...);

and then you cleanup at the end of life:

	if (!priv->mux_error)
		mux_control_deselect(...);

Or something like that.

The mux api is obviously not a good fit for the use case of long
lived selects like you appear to have here. The api was designed
for short lived selects, along the lines of:

int
so_something(...)
{
	int err;

	err = mux_control_select(...);
	if (err)
		return err;

	do_it(...);

	mux_control_deselect(...);
}


> 
>>>>> +                     } else {
>>>>> +                             dev_err(&pdev->dev, "Failed to select FLEXCOM
>> mux\n");
>>>>> +                             return err;
>>>>> +                     }
>>>>> +             }
>>>>> +     }
>>>>> +
>>>>>       clk_disable_unprepare(ddata->clk);
>>>>>
>>>>>       return devm_of_platform_populate(&pdev->dev);
>>>>>  }
>>>>>
>>>>> +static const struct atmel_flex_caps atmel_flexcom_caps = {};
>>>>> +
>>>>> +static const struct atmel_flex_caps lan966x_flexcom_caps = {
>>>>> +     .has_flx_mux = true,
>>>>> +};
>>>>> +
>>>>>  static const struct of_device_id atmel_flexcom_of_match[] = {
>>>>> -     { .compatible = "atmel,sama5d2-flexcom" },
>>>>> +     {
>>>>> +             .compatible = "atmel,sama5d2-flexcom",
>>>>> +             .data = &atmel_flexcom_caps,
>>>>> +     },
>>>>> +
>>>>> +     {
>>>>> +             .compatible = "microchip,lan966-flexcom",
>>>>> +             .data = &lan966x_flexcom_caps,
>>>>> +     },
>>>>> +
>>>>>       { /* sentinel */ }
>>>>>  };
>>>>>  MODULE_DEVICE_TABLE(of, atmel_flexcom_of_match);
>>>>> diff --git a/drivers/mux/Kconfig b/drivers/mux/Kconfig
>>>>> index e5c571fd232c..ea09f474bc2f 100644
>>>>> --- a/drivers/mux/Kconfig
>>>>> +++ b/drivers/mux/Kconfig
>>>>> @@ -45,6 +45,18 @@ config MUX_GPIO
>>>>>         To compile the driver as a module, choose M here: the module will
>>>>>         be called mux-gpio.
>>>>>
>>>>> +config MUX_LAN966
>>>>> +     tristate "LAN966 Flexcom multiplexer"
>>>>> +     depends on OF || COMPILE_TEST
>>>>> +     help
>>>>> +     Lan966 Flexcom Multiplexer controller.
>>>>> +
>>>>> +     The driver supports mapping 2 chip-selects of each of the lan966
>>>>> +     flexcoms to 21 flexcom shared pins.
>>>>> +
>>>>> +     To compile the driver as a module, choose M here: the module will
>>>>> +     be called mux-lan966.
>>>>> +
>>>>>  config MUX_MMIO
>>>>>       tristate "MMIO/Regmap register bitfield-controlled Multiplexer"
>>>>>       depends on OF || COMPILE_TEST
>>>>> diff --git a/drivers/mux/Makefile b/drivers/mux/Makefile
>>>>> index 6e9fa47daf56..53a9840d96fa 100644
>>>>> --- a/drivers/mux/Makefile
>>>>> +++ b/drivers/mux/Makefile
>>>>> @@ -7,10 +7,12 @@ mux-core-objs                       := core.o
>>>>>  mux-adg792a-objs             := adg792a.o
>>>>>  mux-adgs1408-objs            := adgs1408.o
>>>>>  mux-gpio-objs                        := gpio.o
>>>>> +mux-lan966-objs                      := lan966-flx.o
>>>>>  mux-mmio-objs                        := mmio.o
>>>>>
>>>>>  obj-$(CONFIG_MULTIPLEXER)    += mux-core.o
>>>>>  obj-$(CONFIG_MUX_ADG792A)    += mux-adg792a.o
>>>>>  obj-$(CONFIG_MUX_ADGS1408)   += mux-adgs1408.o
>>>>>  obj-$(CONFIG_MUX_GPIO)               += mux-gpio.o
>>>>> +obj-$(CONFIG_MUX_LAN966)     += mux-lan966.o
>>>>>  obj-$(CONFIG_MUX_MMIO)               += mux-mmio.o
>>>>> diff --git a/drivers/mux/lan966-flx.c b/drivers/mux/lan966-flx.c
>>>>> new file mode 100644
>>>>> index 000000000000..2c7dab616a6a
>>>>> --- /dev/null
>>>>> +++ b/drivers/mux/lan966-flx.c
>>>>> @@ -0,0 +1,121 @@
>>>>> +// SPDX-License-Identifier: GPL-2.0
>>>>> +/*
>>>>> + * LAN966 Flexcom MUX driver
>>>>> + *
>>>>> + * Copyright (c) 2022 Microchip Inc.
>>>>> + *
>>>>> + * Author: Kavyasree Kotagiri <kavyasree.kotagiri@microchip.com>
>>>>
>>>> Looks like it is based on mmio.c?
>>>>
>>> Yes, I took mmio.c  driver as reference.
>>>
>>
>> So, then the above copyright and authorship info is not complete.
>>
>>>>> + */
>>>>> +
>>>>> +#include <linux/err.h>
>>>>> +#include <linux/module.h>
>>>>> +#include <linux/of_platform.h>
>>>>> +#include <linux/platform_device.h>
>>>>> +#include <linux/property.h>
>>>>> +#include <linux/mux/driver.h>
>>>>> +#include <linux/io.h>
>>>>> +
>>>>> +#define FLEX_SHRD_MASK               0x1FFFFF
>>>>> +#define LAN966_MAX_CS                21
>>>>> +
>>>>> +static void __iomem *flx_shared_base;
>>>>
>>>> I would much prefer to store the combined address (base+offset)
>>>> in the mux private data instead of only storing the offset and
>>>> then unnecessarily rely on some piece of global state (that
>>>> will be clobbered by other instances).
>>>>
>>> Ok. I will try to see if this is relevant and change accordingly.
>>>
>>>>> +struct mux_lan966x {
>>>>
>>>> Why is the file named lan966, but then everything inside lan966x?
>>>>
>>>>> +     u32 offset;
>>>>> +     u32 ss_pin;
>>>>> +};
>>>>> +
>>>>> +static int mux_lan966x_set(struct mux_control *mux, int state)
>>>>> +{
>>>>> +     struct mux_lan966x *mux_lan966x = mux_chip_priv(mux->chip);
>>>>> +     u32 val;
>>>>> +
>>>>> +     val = ~(1 << mux_lan966x[state].ss_pin) & FLEX_SHRD_MASK;
>>>>> +     writel(val, flx_shared_base + mux_lan966x[state].offset);
>>>>
>>>> This reads memory you have not allocated, if you select a state
>>>> other than zero.
>>>>
>>> Ok. I will return error condition in case of trying to access none existing
>> entry.
>>>>> +
>>>>> +     return 0;
>>>>> +}
>>>>> +
>>>>> +static const struct mux_control_ops mux_lan966x_ops = {
>>>>> +     .set = mux_lan966x_set,
>>>>> +};
>>>>> +
>>>>> +static const struct of_device_id mux_lan966x_dt_ids[] = {
>>>>> +     { .compatible = "microchip,lan966-flx-mux", },
>>>>> +     { /* sentinel */ }
>>>>> +};
>>>>> +MODULE_DEVICE_TABLE(of, mux_lan966x_dt_ids);
>>>>> +
>>>>> +static int mux_lan966x_probe(struct platform_device *pdev)
>>>>> +{
>>>>> +     struct device_node *np = pdev->dev.of_node;
>>>>> +     struct device *dev = &pdev->dev;
>>>>> +     struct mux_lan966x *mux_lan966x;
>>>>> +     struct mux_chip *mux_chip;
>>>>> +     int ret, num_fields, i;
>>>>> +
>>>>> +     ret = of_property_count_u32_elems(np, "mux-offset-pin");
>>>>> +     if (ret == 0 || ret % 2)
>>>>> +             ret = -EINVAL;
>>>>> +     if (ret < 0)
>>>>> +             return dev_err_probe(dev, ret,
>>>>> +                                  "mux-offset-pin property missing or invalid");
>>>>> +     num_fields = ret / 2;
>>>>> +
>>>>> +     mux_chip = devm_mux_chip_alloc(dev, num_fields,
>>>> sizeof(*mux_lan966x));
>>>>
>>>> I might be thoroughly mistaken and confused by the code, but it seems
>>>> very strange that a subsequenct select is not undoing what a previous
>>>> select once did. Each state seems to write to its own register offset,
>>>> and there is nothing that restores the first register offset with you
>>>> switch states.
>>>>
>>>> Care to explain how muxing works and what you are expected to do to
>>>> manipulate the mux? Is there some link to public documentation? I did
>>>> a quick search for lan966 but came up with nothing relevant.
>>>>
>>> LAN966 has 5 flexcoms(which can be used as USART/SPI/I2C interface).
>>> FLEXCOM has two chip-select I/O lines namely CS0 and CS1
>>> in SPI mode, CTS and RTS in USART mode. These FLEXCOM pins are
>> optional.
>>> These chip-selects can be mapped to flexcom shared pin [0-21] which can
>> be
>>> done by configuring flexcom multiplexer register(FLEXCOM_SHARED[0-
>> 4]:SS_MASK[0-1])
>>> Driver explanation:
>>> "flx_shared_base" is used to get base address of Flexcom shared
>> multiplexer
>>> "mux-offset-pin" property is used to get cs0/cs1 offset and flexcom shared
>> pin[0-21] of a flexcom.
>>> state value passed from atmel-flexcom is used to configure respective
>>> FLEXCOM_SHARED[0-4]:SS_MASK[0-1] register with offset and flexcom
>> shared pin.
>>
>> Ok, let me try to interpret that. You wish enable a "fan out" of these
>> two chip-selects for any of the 5 flexcoms (in SPI mode?) such that
>> these 10 internal chip-selects can be connected to any of 21 external
>> pins?
>>
>> If that's correct and if you wish to interface with e.g. 20 chips,
>> then it would be possible to have 20 states for one mux control and
>> then reach the 20 chips using only CS0 from FLEXCOM0, or it would be
>> possible to have 2 states for 10 mux controls, one each for CS0/CS1 of
>> all five flexcoms and also reach 20 chips. Or any wild combo you
>> imagine is useful.
>>
>> Is that correctly understood?
>>
>> Assuming so, then you can have a maximum of 10 mux controls, and for
>> each mux control you need a variable number of legitimate states. Each
>> mux control would also need to know at what address/offset its SS_MASK
>> register is at and what pins/states are valid.
>>
>> But your code does not implemnent the above. You allocate num_fields
>> mux controls, which is what confuses the hell out of me. num_fields is
>> the number of states, not the number of mux controls! And you also
>> need to specify an individual offset for each state, and that makes no
>> sense at all, at least not to me.
>>
>> So, instead, I think you want to have:
>>
>> struct mux_lan966x {
>>         u32 ss_mask;
>>         int num_pins;
>>         u32 ss_pin[];
>> };
>>
>> And then do:
>>
>>         mux_chip = devm_mux_chip_alloc(dev, 1, sizeof(*mux_lan966x) +
>> num_pins * sizeof(u32));
>>
>> (or however that size is best spelled, I haven't kept up)
>>
>> The set operation would be along the lines of:
>>
>> static int mux_lan966x_set(struct mux_control *mux, int state)
>> {
>>         struct mux_lan966x *mux_lan966x = mux_chip_priv(mux->chip);
>>         u32 val;
>>
>>         if (state < 0 || state >= mux_lan966x->num_pins)
>>                 return -1;
>>
>>         val = ~(1 << mux_lan966x->ss_pin[state]) & FLEX_SHRD_MASK;
>>         writel(val, flx_shared_base + mux_lan966x->ss_mask);
>>
>>         return 0;
>> }
>>
>> Because, one mux control should only ever need to know about one offset,
>> as it should only ever write to its own SS_MASK register. And you will
>> need some private space such that you can keep track of which states
>> are legit.
>>
>> I would also separate out the SS_MASK offset from the mux-offset-pin
>> property and have one property for that value and then a straight
>> array for the valid pin numbers in another property (no longer named
>> mux-offset-pin of course).
>>
>> Or something like that and assuming I understand how the FLEXCOMs work
>> and what you want to do etc.
>>
> 
> Thank you for your comments
> I agree, I will change number of mux controls to 1 in devm_mux_chip_alloc()
> I would like to use mux-offset-pin property because 
> each chip-select must be mapped to a unique flexcom shared pin.
> For example, 
> mux-offset-pin = <0x18 9>, /* 0: flexcom3 CS0 mapped to pin-9 */
>                                <0x1c 7>, /* 1: flexcom3 CS1 mapped to pin-7 */
>                                <0x20 4>; /* 2: flexcom4 CS0 mapped to pin-4 */};
> I want to use mux-offset-pin property to be clear about which offset is mapped to which
> flexcom shared pin. Here, 0, 1, 2.. entries represents state passed from mux_control_select(mux, state).
> 
> Please provide your comments.

It seems you want to just use the static info from the devicetree
to program your registers once and for all? That's not a problem
that a mux driver is aming at solving. My thought was that if you
have one SPI device with chip select on pin-7, another with chip-
select on pin-9 and a third on pin-4, then you /could/ use them all
from e.g. flexcom3/CS0 by reprogramming that SS_MASK register
at the time you want to access one of the three chips. You could
then of course not access the three chips in parallel, but the
muxing of the accesses to three chips like that is the problem
that the mux subsystem helps with.

I.e. pretty much exactly as is described in

	Documentation/devicetree/bindings/spi/spi-mux.yaml

but with a mux node bringing your new driver into the picture
instead of the "gpio-mux" in that example (and the "CS-X" signal
and the "Mux" block in that picture would be internal to the SoC).

If you are content with just programming a fixed set of values to
a couple of registers depending on how the board is wired, some
extra DT property on some node related to the flexcom seems like
a better fit than a mux driver.

Having said that, the mux solution above would solve this static
case too. You would just need to configure a mux with one state.
It would be a couple of extra nodes in the device tree, but it
would certainly work (and also cover the more complex case when
someone pops up and needs that).

Cheers,
Peter

>> Cheers,
>> Peter
>>
>>
>>>>> +     if (IS_ERR(mux_chip))
>>>>> +             return dev_err_probe(dev, PTR_ERR(mux_chip),
>>>>> +                                  "failed to allocate mux_chips\n");
>>>>> +
>>>>> +     mux_lan966x = mux_chip_priv(mux_chip);
>>>>> +
>>>>> +     flx_shared_base =
>> devm_platform_get_and_ioremap_resource(pdev,
>>>> 0, NULL);
>>>>> +     if (IS_ERR(flx_shared_base))
>>>>> +             return dev_err_probe(dev, PTR_ERR(flx_shared_base),
>>>>> +                                  "failed to get flexcom shared base address\n");
>>>>> +
>>>>> +     for (i = 0; i < num_fields; i++) {
>>>>> +             struct mux_control *mux = &mux_chip->mux[i];
>>>>> +             u32 offset, shared_pin;
>>>>> +
>>>>> +             ret = of_property_read_u32_index(np, "mux-offset-pin",
>>>>> +                                              2 * i, &offset);
>>>>> +             if (ret == 0)
>>>>> +                     ret = of_property_read_u32_index(np, "mux-offset-pin",
>>>>> +                                                      2 * i + 1,
>>>>> +                                                      &shared_pin);
>>>>> +             if (ret < 0)
>>>>> +                     return dev_err_probe(dev, ret,
>>>>> +                                          "failed to read mux-offset-pin property: %d", i);
>>>>> +
>>>>> +             if (shared_pin >= LAN966_MAX_CS)
>>>>> +                     return -EINVAL;
>>>>> +
>>>>> +             mux_lan966x[i].offset = offset;
>>>>> +             mux_lan966x[i].ss_pin = shared_pin;
>>>>
>>>> This clobbers memory you have not allocated, if num_fields >= 1.
>>>>
>>>> Cheers,
>>>> Peter
>>>>
>>>>> +
>>>>> +             mux->states = LAN966_MAX_CS;
>>>>> +     }
>>>>> +
>>>>> +     mux_chip->ops = &mux_lan966x_ops;
>>>>> +
>>>>> +     ret = devm_mux_chip_register(dev, mux_chip);
>>>>> +     if (ret < 0)
>>>>> +             return ret;
>>>>> +
>>>>> +     return 0;
>>>>> +}
>>>>> +
>>>>> +static struct platform_driver mux_lan966x_driver = {
>>>>> +     .driver = {
>>>>> +             .name = "lan966-mux",
>>>>> +             .of_match_table = of_match_ptr(mux_lan966x_dt_ids),
>>>>> +     },
>>>>> +     .probe = mux_lan966x_probe,
>>>>> +};
>>>>> +
>>>>> +module_platform_driver(mux_lan966x_driver);
>>>>> +
>>>>> +MODULE_DESCRIPTION("LAN966 Flexcom multiplexer driver");
>>>>> +MODULE_AUTHOR("Kavyasree Kotagiri
>>>> <kavyasree.kotagiri@microchip.com>");
>>>>> +MODULE_LICENSE("GPL v2");
>>>>> +

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v2 4/4] mux: lan966: Add support for flexcom mux controller
  2022-05-09 11:37   ` Peter Rosin
  2022-05-09 11:41     ` Peter Rosin
  2022-05-10 14:59     ` Kavyasree.Kotagiri
@ 2022-05-17 11:53     ` Michael Walle
  2022-05-17 12:00       ` Peter Rosin
  2 siblings, 1 reply; 21+ messages in thread
From: Michael Walle @ 2022-05-17 11:53 UTC (permalink / raw)
  To: peda
  Cc: Manohar.Puri, UNGLinuxDriver, alexandre.belloni, claudiu.beznea,
	devicetree, kavyasree.kotagiri, krzysztof.kozlowski+dt,
	lee.jones, linux-arm-kernel, linux-kernel, linux, nicolas.ferre,
	Michael Walle

Hi,

>> +struct mux_lan966x {
>
> Why is the file named lan966, but then everything inside lan966x?

So I was about to reply to the bindings but since that question
came up here, too, I'll do it here.

IMHO the name "lan966" is super confusing and if I followed it
correctly, it was just invented because the DT guys don't want to
have a wildcard in the compatibles. But LAN966 isn't a real product,
just LAN9662 and LAN9668 is.

I'd really prefer to have a consistent naming. I've said it once
[1], having "lan966" (over lan966x) feels like cheating and is even
worse, because everyone would assume there is a thing named LAN966.
lan966x might lead the reader to think twice what the 'x' means.

So I'd prefer to have lan966x in the documentation and the drivers
and just "microchip,lan9668" or "microchip,lan9662" in the
compatibles.

-michael

[1] https://lore.kernel.org/linux-devicetree/d18291ff8d81f03a58900935d92115f2@walle.cc/

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v2 4/4] mux: lan966: Add support for flexcom mux controller
  2022-05-17 11:53     ` Michael Walle
@ 2022-05-17 12:00       ` Peter Rosin
  0 siblings, 0 replies; 21+ messages in thread
From: Peter Rosin @ 2022-05-17 12:00 UTC (permalink / raw)
  To: Michael Walle
  Cc: Manohar.Puri, UNGLinuxDriver, alexandre.belloni, claudiu.beznea,
	devicetree, kavyasree.kotagiri, krzysztof.kozlowski+dt,
	lee.jones, linux-arm-kernel, linux-kernel, linux, nicolas.ferre



2022-05-17 at 13:53, Michael Walle wrote:
> Hi,
> 
>>> +struct mux_lan966x {
>>
>> Why is the file named lan966, but then everything inside lan966x?
> 
> So I was about to reply to the bindings but since that question
> came up here, too, I'll do it here.
> 
> IMHO the name "lan966" is super confusing and if I followed it
> correctly, it was just invented because the DT guys don't want to
> have a wildcard in the compatibles. But LAN966 isn't a real product,
> just LAN9662 and LAN9668 is.

No wonder I failed when I searched the web for "lan966"...

So, as you were told in the thread you point at below, you name
stuff after one of them (and not some random thing that doesn't
exist), but then handle both in the same file(s). Like you would
have if one was introduced first and the other came later.

Cheers,
Peter

> 
> I'd really prefer to have a consistent naming. I've said it once
> [1], having "lan966" (over lan966x) feels like cheating and is even
> worse, because everyone would assume there is a thing named LAN966.
> lan966x might lead the reader to think twice what the 'x' means.
> 
> So I'd prefer to have lan966x in the documentation and the drivers
> and just "microchip,lan9668" or "microchip,lan9662" in the
> compatibles.
> 
> -michael
> 
> [1] https://lore.kernel.org/linux-devicetree/d18291ff8d81f03a58900935d92115f2@walle.cc/

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v2 4/4] mux: lan966: Add support for flexcom mux controller
  2022-05-09  8:49 ` [PATCH v2 4/4] mux: lan966: Add support for " Kavyasree Kotagiri
                     ` (3 preceding siblings ...)
  2022-05-11  9:06   ` Claudiu.Beznea
@ 2022-05-23 14:52   ` Lee Jones
  4 siblings, 0 replies; 21+ messages in thread
From: Lee Jones @ 2022-05-23 14:52 UTC (permalink / raw)
  To: Kavyasree Kotagiri
  Cc: krzysztof.kozlowski+dt, nicolas.ferre, alexandre.belloni,
	claudiu.beznea, peda, devicetree, linux-arm-kernel, linux-kernel,
	linux, Manohar.Puri, UNGLinuxDriver

On Mon, 09 May 2022, Kavyasree Kotagiri wrote:

> LAN966 SoC have 5 flexcoms. Each flexcom has 2 chip-selects.
> For each chip select of each flexcom there is a configuration
> register FLEXCOM_SHARED[0-4]:SS_MASK[0-1]. The width of
> configuration register is 21 because there are 21 shared pins
> on each of which the chip select can be mapped. Each bit of the
> register represents a different FLEXCOM_SHARED pin.
> 
> Signed-off-by: Kavyasree Kotagiri <kavyasree.kotagiri@microchip.com>
> ---
>  arch/arm/mach-at91/Kconfig  |   2 +

>  drivers/mfd/atmel-flexcom.c |  55 +++++++++++++++-

Can this be separated into its own patch?

>  drivers/mux/Kconfig         |  12 ++++
>  drivers/mux/Makefile        |   2 +
>  drivers/mux/lan966-flx.c    | 121 ++++++++++++++++++++++++++++++++++++
>  5 files changed, 191 insertions(+), 1 deletion(-)
>  create mode 100644 drivers/mux/lan966-flx.c
> 
> diff --git a/arch/arm/mach-at91/Kconfig b/arch/arm/mach-at91/Kconfig
> index 279810381256..26fb0f4e1b79 100644
> --- a/arch/arm/mach-at91/Kconfig
> +++ b/arch/arm/mach-at91/Kconfig
> @@ -74,6 +74,8 @@ config SOC_LAN966
>  	select DW_APB_TIMER_OF
>  	select ARM_GIC
>  	select MEMORY
> +	select MULTIPLEXER
> +	select MUX_LAN966
>  	help
>  	  This enables support for ARMv7 based Microchip LAN966 SoC family.
>  
> diff --git a/drivers/mfd/atmel-flexcom.c b/drivers/mfd/atmel-flexcom.c
> index 559eb4d352b6..7cfd0fc3f4f0 100644
> --- a/drivers/mfd/atmel-flexcom.c
> +++ b/drivers/mfd/atmel-flexcom.c
> @@ -17,6 +17,7 @@
>  #include <linux/io.h>
>  #include <linux/clk.h>
>  #include <dt-bindings/mfd/atmel-flexcom.h>
> +#include <linux/mux/consumer.h>
>  
>  /* I/O register offsets */
>  #define FLEX_MR		0x0	/* Mode Register */
> @@ -28,6 +29,10 @@
>  #define FLEX_MR_OPMODE(opmode)	(((opmode) << FLEX_MR_OPMODE_OFFSET) &	\
>  				 FLEX_MR_OPMODE_MASK)
>  
> +struct atmel_flex_caps {
> +	bool has_flx_mux;

Why does this need it's own struct?

> +};
> +
>  struct atmel_flexcom {
>  	void __iomem *base;
>  	u32 opmode;
> @@ -37,6 +42,7 @@ struct atmel_flexcom {
>  static int atmel_flexcom_probe(struct platform_device *pdev)
>  {
>  	struct device_node *np = pdev->dev.of_node;
> +	const struct atmel_flex_caps *caps;
>  	struct resource *res;
>  	struct atmel_flexcom *ddata;
>  	int err;
> @@ -76,13 +82,60 @@ static int atmel_flexcom_probe(struct platform_device *pdev)
>  	 */
>  	writel(FLEX_MR_OPMODE(ddata->opmode), ddata->base + FLEX_MR);
>  
> +	caps = of_device_get_match_data(&pdev->dev);
> +	if (!caps) {
> +		dev_err(&pdev->dev, "Could not retrieve flexcom caps\n");

dev_err() already prints out the device name, so you can drop the
"flexcom" part.  Also, please expand 'caps'.  I'm assuming that's
capabilities?

> +		return -EINVAL;
> +	}
> +
> +	/* Flexcom Mux */
> +	if (caps->has_flx_mux && of_property_read_bool(np, "mux-controls")) {
> +		struct mux_control *flx_mux;

What is 'flx'?

> +		struct of_phandle_args args;
> +		int i, count;
> +
> +		flx_mux = devm_mux_control_get(&pdev->dev, NULL);
> +		if (IS_ERR(flx_mux))
> +			return PTR_ERR(flx_mux);
> +
> +		count = of_property_count_strings(np, "mux-control-names");
> +		for (i = 0; i < count; i++) {
> +			err = of_parse_phandle_with_fixed_args(np, "mux-controls", 1, i, &args);
> +			if (err)
> +				break;

No mux_control_deselect() for previous iterations?

> +			err = mux_control_select(flx_mux, args.args[0]);
> +			if (!err) {
> +				mux_control_deselect(flx_mux);

Not sure I see the point in selecting then deselecting.

Is it just a test?

If so, why don't you wait until you need to select it, then apply
normal error handling protocols there instead?

> +			} else {
> +				dev_err(&pdev->dev, "Failed to select FLEXCOM mux\n");
> +				return err;
> +			}
> +		}
> +	}
> +
>  	clk_disable_unprepare(ddata->clk);
>  
>  	return devm_of_platform_populate(&pdev->dev);
>  }
>  
> +static const struct atmel_flex_caps atmel_flexcom_caps = {};

Why not just leave .data as NULL?

> +static const struct atmel_flex_caps lan966x_flexcom_caps = {
> +	.has_flx_mux = true,
> +};
> +
>  static const struct of_device_id atmel_flexcom_of_match[] = {
> -	{ .compatible = "atmel,sama5d2-flexcom" },
> +	{
> +		.compatible = "atmel,sama5d2-flexcom",
> +		.data = &atmel_flexcom_caps,

And this can't be a DT property?

> +	},
> +
> +	{
> +		.compatible = "microchip,lan966-flexcom",
> +		.data = &lan966x_flexcom_caps,
> +	},

This all seems like a lot of hoop-jumping.

Why not just do:

  if (of_device_is_compatible(np, "lan966x_flexcom_caps"))

> +
>  	{ /* sentinel */ }
>  };
>  MODULE_DEVICE_TABLE(of, atmel_flexcom_of_match);

-- 
Lee Jones [李琼斯]
Principal Technical Lead - Developer Services
Linaro.org │ Open source software for Arm SoCs
Follow Linaro: Facebook | Twitter | Blog

^ permalink raw reply	[flat|nested] 21+ messages in thread

* RE: [PATCH v2 4/4] mux: lan966: Add support for flexcom mux controller
  2022-05-11 15:43           ` Peter Rosin
@ 2022-05-27  6:00             ` Kavyasree.Kotagiri
  0 siblings, 0 replies; 21+ messages in thread
From: Kavyasree.Kotagiri @ 2022-05-27  6:00 UTC (permalink / raw)
  To: peda
  Cc: devicetree, linux-arm-kernel, linux-kernel, lee.jones, linux,
	Manohar.Puri, UNGLinuxDriver, krzysztof.kozlowski+dt,
	Nicolas.Ferre, alexandre.belloni, Claudiu.Beznea

Hi Peter,

Thanks for your comments.
> If you are content with just programming a fixed set of values to
> a couple of registers depending on how the board is wired, some
> extra DT property on some node related to the flexcom seems like
> a better fit than a mux driver.
Based on your inputs, I planned to send a new patch with new DT properties
introduced in atmel-flexcom.c driver rather than mux driver.

Thanks,
Kavya

> -----Original Message-----
> From: Peter Rosin [mailto:peda@axentia.se]
> Sent: Wednesday, May 11, 2022 9:14 PM
> To: Kavyasree Kotagiri - I30978 <Kavyasree.Kotagiri@microchip.com>
> Cc: devicetree@vger.kernel.org; linux-arm-kernel@lists.infradead.org; linux-
> kernel@vger.kernel.org; lee.jones@linaro.org; linux@armlinux.org.uk;
> Manohar Puri - I30488 <Manohar.Puri@microchip.com>; UNGLinuxDriver
> <UNGLinuxDriver@microchip.com>; krzysztof.kozlowski+dt@linaro.org;
> Nicolas Ferre - M43238 <Nicolas.Ferre@microchip.com>;
> alexandre.belloni@bootlin.com; Claudiu Beznea - M18063
> <Claudiu.Beznea@microchip.com>
> Subject: Re: [PATCH v2 4/4] mux: lan966: Add support for flexcom mux
> controller
> 
> EXTERNAL EMAIL: Do not click links or open attachments unless you know the
> content is safe
> 
> Hi!
> 
> 2022-05-11 at 16:28, Kavyasree.Kotagiri@microchip.com wrote:
> >> 2022-05-10 at 16:59, Kavyasree.Kotagiri@microchip.com wrote:
> >>>>> LAN966 SoC have 5 flexcoms. Each flexcom has 2 chip-selects.
> >>>>> For each chip select of each flexcom there is a configuration
> >>>>> register FLEXCOM_SHARED[0-4]:SS_MASK[0-1]. The width of
> >>>>> configuration register is 21 because there are 21 shared pins
> >>>>> on each of which the chip select can be mapped. Each bit of the
> >>>>> register represents a different FLEXCOM_SHARED pin.
> >>>>>
> >>>>> Signed-off-by: Kavyasree Kotagiri
> <kavyasree.kotagiri@microchip.com>
> >>>>> ---
> >>>>>  arch/arm/mach-at91/Kconfig  |   2 +
> >>>>>  drivers/mfd/atmel-flexcom.c |  55 +++++++++++++++-
> >>>>>  drivers/mux/Kconfig         |  12 ++++
> >>>>>  drivers/mux/Makefile        |   2 +
> >>>>>  drivers/mux/lan966-flx.c    | 121
> >>>> ++++++++++++++++++++++++++++++++++++
> >>>>>  5 files changed, 191 insertions(+), 1 deletion(-)
> >>>>>  create mode 100644 drivers/mux/lan966-flx.c
> >>>>>
> >>>>> diff --git a/arch/arm/mach-at91/Kconfig b/arch/arm/mach-
> at91/Kconfig
> >>>>> index 279810381256..26fb0f4e1b79 100644
> >>>>> --- a/arch/arm/mach-at91/Kconfig
> >>>>> +++ b/arch/arm/mach-at91/Kconfig
> >>>>> @@ -74,6 +74,8 @@ config SOC_LAN966
> >>>>>       select DW_APB_TIMER_OF
> >>>>>       select ARM_GIC
> >>>>>       select MEMORY
> >>>>> +     select MULTIPLEXER
> >>>>> +     select MUX_LAN966
> >>>>>       help
> >>>>>         This enables support for ARMv7 based Microchip LAN966 SoC
> >> family.
> >>>>>
> >>>>> diff --git a/drivers/mfd/atmel-flexcom.c b/drivers/mfd/atmel-
> flexcom.c
> >>>>> index 559eb4d352b6..7cfd0fc3f4f0 100644
> >>>>> --- a/drivers/mfd/atmel-flexcom.c
> >>>>> +++ b/drivers/mfd/atmel-flexcom.c
> >>>>> @@ -17,6 +17,7 @@
> >>>>>  #include <linux/io.h>
> >>>>>  #include <linux/clk.h>
> >>>>>  #include <dt-bindings/mfd/atmel-flexcom.h>
> >>>>> +#include <linux/mux/consumer.h>
> >>>>>
> >>>>>  /* I/O register offsets */
> >>>>>  #define FLEX_MR              0x0     /* Mode Register */
> >>>>> @@ -28,6 +29,10 @@
> >>>>>  #define FLEX_MR_OPMODE(opmode)       (((opmode) <<
> >>>> FLEX_MR_OPMODE_OFFSET) &  \
> >>>>>                                FLEX_MR_OPMODE_MASK)
> >>>>>
> >>>>> +struct atmel_flex_caps {
> >>>>> +     bool has_flx_mux;
> >>>>> +};
> >>>>> +
> >>>>>  struct atmel_flexcom {
> >>>>>       void __iomem *base;
> >>>>>       u32 opmode;
> >>>>> @@ -37,6 +42,7 @@ struct atmel_flexcom {
> >>>>>  static int atmel_flexcom_probe(struct platform_device *pdev)
> >>>>>  {
> >>>>>       struct device_node *np = pdev->dev.of_node;
> >>>>> +     const struct atmel_flex_caps *caps;
> >>>>>       struct resource *res;
> >>>>>       struct atmel_flexcom *ddata;
> >>>>>       int err;
> >>>>> @@ -76,13 +82,60 @@ static int atmel_flexcom_probe(struct
> >>>> platform_device *pdev)
> >>>>>        */
> >>>>>       writel(FLEX_MR_OPMODE(ddata->opmode), ddata->base +
> >> FLEX_MR);
> >>>>>
> >>>>> +     caps = of_device_get_match_data(&pdev->dev);
> >>>>> +     if (!caps) {
> >>>>> +             dev_err(&pdev->dev, "Could not retrieve flexcom caps\n");
> >>>>> +             return -EINVAL;
> >>>>> +     }
> >>>>> +
> >>>>> +     /* Flexcom Mux */
> >>>>> +     if (caps->has_flx_mux && of_property_read_bool(np, "mux-
> >> controls"))
> >>>> {
> >>>>> +             struct mux_control *flx_mux;
> >>>>> +             struct of_phandle_args args;
> >>>>> +             int i, count;
> >>>>> +
> >>>>> +             flx_mux = devm_mux_control_get(&pdev->dev, NULL);
> >>>>> +             if (IS_ERR(flx_mux))
> >>>>> +                     return PTR_ERR(flx_mux);
> >>>>> +
> >>>>> +             count = of_property_count_strings(np, "mux-control-
> names");
> >>>>> +             for (i = 0; i < count; i++) {
> >>>>> +                     err = of_parse_phandle_with_fixed_args(np, "mux-
> >> controls",
> >>>> 1, i, &args);
> >>>>> +                     if (err)
> >>>>> +                             break;
> >>>>> +
> >>>>> +                     err = mux_control_select(flx_mux, args.args[0]);
> >>>>> +                     if (!err) {
> >>>>> +                             mux_control_deselect(flx_mux);
> >>>>
> >>>> This is suspect. When you deselect the mux you rely on the mux to be
> >>>> configured with "as-is" as the idle state. But you do not document that.
> >>>> This is also fragile in that you silently rely on noone else selecting
> >>>> the mux to some unwanted state behind your back (mux controls are
> not
> >>>> exclusive the way e.g. gpio pins or pwms are). The protocol is that
> >>>> others may get a ref to the mux control and select it as long as noone
> >>>> else has selected it.
> >>>>
> >>>> The only sane thing to do is to keep the mux selected for the whole
> >>>> duration when you rely on it to be in your desired state.
> >>>>
> >>>
> >>> Yes, mux is kept selected until configuring register is done. Please see
> >> below log where
> >>> I added debug prints just for understanding:
> >>> # dmesg | grep KK
> >>>  [    0.779827] KK: Func: atmel_flexcom_probe ***** [START flx muxing]
> >> ********
> >>> [    0.779875] KK: Func: atmel_flexcom_probe i = 0 args[0] = 0
> >>> [    0.779890] KK: Func: mux_control_select [Entered]
> >>> [    0.779902] KK: Func: mux_lan966x_set [Entered] state = 0
> >>> [    0.779977] KK: Func: mux_lan966x_set [Read] = 0x1fffef   <<<-----
> setting
> >> mux_lan966x[state].ss_pin "4" which is passed from dts
> >>> [    0.779992] KK: Func: mux_lan966x_set [Exit]
> >>> [    0.780002] KK: Func: mux_control_select [Exit]
> >>> [    0.780011] KK: Func: mux_control_deselect [Entered]
> >>> [    0.780021] KK: Func: mux_control_deselect [Exit]
> >>
> >> You misunderstand. The mux control is only "selected" between the call
> >> to mux_control_select() and the following call to
> >> mux_control_deselect().
> >>
> >> After that, the mux control is "idle". However, in your case the
> >> "idle-state" is configured to "as-is" (which is the default), so the
> >> mux control (naturally) remains in the previously selected state while
> >> idle. But you are not documenting that limitation, and with this
> >> implementation you *must* have a mux control that uses "as-is" as its
> >> idle state. But that is an unexpected and broken limitation, and a
> >> much better solution is to simply keep the mux control "selected" for
> >> the complete duration when you rely on it to be in whatever state you
> >> need it to be in.
> >>
> > I am new to mux drivers.
> > Let me try to explain why I have mux_control_deselect if there is no err
> from mux_control_select()
> > For example,
> > 1. When I have one only chip-select CS0 for flexcom3 to be mapped to
> flexcom shared pin 9:
> > As per previously shared log, FLEXCOM_SHARED[3]:SS_MASK[0] is being
> configured to expected value
> > even before mux_control_deseletc().
> > 2. When I have to map two chip-selects of flx3 - CS0 to flexcom shared 9
> >                                               CS1 to flexcom shared pin 7
> > FLEXCOM_SHARED[3]:SS_MASK[0] is set to expected value 0x1fffef
> > I see console hangs at mux_control_select() if I don’t call
> mux_control_deselect
> > while mapping second chip-select FLEXCOM_SHARED[3]:SS_MASK[1].
> > After reading below description from mux_control_select() :
> > " * On successfully selecting the mux-control state, it will be locked until
> >  * there is a call to mux_control_deselect()."
> > Following this help text, I called mux_control_deselect() if there is no error
> from mux_control_select()
> > and then it worked. FLEXCOM_SHARED[3]:SS_MASK[1] is now set to
> expected value 0x1fffbf.
> >
> > Please explain me if I am missing something.
> 
> You should wait with the deselect until you need to change the state
> of the mux. I.e., on init/probe you set some variable to -1, like so:
> 
>         priv->mux_error = -1;
> 
> and when you later need to set/change the state of the mux, you do:
> 
>         if (!priv->mux_error)
>                 mux_control_deselect(...);
>         priv->mux_error = mux_control_select(...);
> 
> and then you cleanup at the end of life:
> 
>         if (!priv->mux_error)
>                 mux_control_deselect(...);
> 
> Or something like that.
> 
> The mux api is obviously not a good fit for the use case of long
> lived selects like you appear to have here. The api was designed
> for short lived selects, along the lines of:
> 
> int
> so_something(...)
> {
>         int err;
> 
>         err = mux_control_select(...);
>         if (err)
>                 return err;
> 
>         do_it(...);
> 
>         mux_control_deselect(...);
> }
> 
> 
> >
> >>>>> +                     } else {
> >>>>> +                             dev_err(&pdev->dev, "Failed to select FLEXCOM
> >> mux\n");
> >>>>> +                             return err;
> >>>>> +                     }
> >>>>> +             }
> >>>>> +     }
> >>>>> +
> >>>>>       clk_disable_unprepare(ddata->clk);
> >>>>>
> >>>>>       return devm_of_platform_populate(&pdev->dev);
> >>>>>  }
> >>>>>
> >>>>> +static const struct atmel_flex_caps atmel_flexcom_caps = {};
> >>>>> +
> >>>>> +static const struct atmel_flex_caps lan966x_flexcom_caps = {
> >>>>> +     .has_flx_mux = true,
> >>>>> +};
> >>>>> +
> >>>>>  static const struct of_device_id atmel_flexcom_of_match[] = {
> >>>>> -     { .compatible = "atmel,sama5d2-flexcom" },
> >>>>> +     {
> >>>>> +             .compatible = "atmel,sama5d2-flexcom",
> >>>>> +             .data = &atmel_flexcom_caps,
> >>>>> +     },
> >>>>> +
> >>>>> +     {
> >>>>> +             .compatible = "microchip,lan966-flexcom",
> >>>>> +             .data = &lan966x_flexcom_caps,
> >>>>> +     },
> >>>>> +
> >>>>>       { /* sentinel */ }
> >>>>>  };
> >>>>>  MODULE_DEVICE_TABLE(of, atmel_flexcom_of_match);
> >>>>> diff --git a/drivers/mux/Kconfig b/drivers/mux/Kconfig
> >>>>> index e5c571fd232c..ea09f474bc2f 100644
> >>>>> --- a/drivers/mux/Kconfig
> >>>>> +++ b/drivers/mux/Kconfig
> >>>>> @@ -45,6 +45,18 @@ config MUX_GPIO
> >>>>>         To compile the driver as a module, choose M here: the module
> will
> >>>>>         be called mux-gpio.
> >>>>>
> >>>>> +config MUX_LAN966
> >>>>> +     tristate "LAN966 Flexcom multiplexer"
> >>>>> +     depends on OF || COMPILE_TEST
> >>>>> +     help
> >>>>> +     Lan966 Flexcom Multiplexer controller.
> >>>>> +
> >>>>> +     The driver supports mapping 2 chip-selects of each of the lan966
> >>>>> +     flexcoms to 21 flexcom shared pins.
> >>>>> +
> >>>>> +     To compile the driver as a module, choose M here: the module will
> >>>>> +     be called mux-lan966.
> >>>>> +
> >>>>>  config MUX_MMIO
> >>>>>       tristate "MMIO/Regmap register bitfield-controlled Multiplexer"
> >>>>>       depends on OF || COMPILE_TEST
> >>>>> diff --git a/drivers/mux/Makefile b/drivers/mux/Makefile
> >>>>> index 6e9fa47daf56..53a9840d96fa 100644
> >>>>> --- a/drivers/mux/Makefile
> >>>>> +++ b/drivers/mux/Makefile
> >>>>> @@ -7,10 +7,12 @@ mux-core-objs                       := core.o
> >>>>>  mux-adg792a-objs             := adg792a.o
> >>>>>  mux-adgs1408-objs            := adgs1408.o
> >>>>>  mux-gpio-objs                        := gpio.o
> >>>>> +mux-lan966-objs                      := lan966-flx.o
> >>>>>  mux-mmio-objs                        := mmio.o
> >>>>>
> >>>>>  obj-$(CONFIG_MULTIPLEXER)    += mux-core.o
> >>>>>  obj-$(CONFIG_MUX_ADG792A)    += mux-adg792a.o
> >>>>>  obj-$(CONFIG_MUX_ADGS1408)   += mux-adgs1408.o
> >>>>>  obj-$(CONFIG_MUX_GPIO)               += mux-gpio.o
> >>>>> +obj-$(CONFIG_MUX_LAN966)     += mux-lan966.o
> >>>>>  obj-$(CONFIG_MUX_MMIO)               += mux-mmio.o
> >>>>> diff --git a/drivers/mux/lan966-flx.c b/drivers/mux/lan966-flx.c
> >>>>> new file mode 100644
> >>>>> index 000000000000..2c7dab616a6a
> >>>>> --- /dev/null
> >>>>> +++ b/drivers/mux/lan966-flx.c
> >>>>> @@ -0,0 +1,121 @@
> >>>>> +// SPDX-License-Identifier: GPL-2.0
> >>>>> +/*
> >>>>> + * LAN966 Flexcom MUX driver
> >>>>> + *
> >>>>> + * Copyright (c) 2022 Microchip Inc.
> >>>>> + *
> >>>>> + * Author: Kavyasree Kotagiri <kavyasree.kotagiri@microchip.com>
> >>>>
> >>>> Looks like it is based on mmio.c?
> >>>>
> >>> Yes, I took mmio.c  driver as reference.
> >>>
> >>
> >> So, then the above copyright and authorship info is not complete.
> >>
> >>>>> + */
> >>>>> +
> >>>>> +#include <linux/err.h>
> >>>>> +#include <linux/module.h>
> >>>>> +#include <linux/of_platform.h>
> >>>>> +#include <linux/platform_device.h>
> >>>>> +#include <linux/property.h>
> >>>>> +#include <linux/mux/driver.h>
> >>>>> +#include <linux/io.h>
> >>>>> +
> >>>>> +#define FLEX_SHRD_MASK               0x1FFFFF
> >>>>> +#define LAN966_MAX_CS                21
> >>>>> +
> >>>>> +static void __iomem *flx_shared_base;
> >>>>
> >>>> I would much prefer to store the combined address (base+offset)
> >>>> in the mux private data instead of only storing the offset and
> >>>> then unnecessarily rely on some piece of global state (that
> >>>> will be clobbered by other instances).
> >>>>
> >>> Ok. I will try to see if this is relevant and change accordingly.
> >>>
> >>>>> +struct mux_lan966x {
> >>>>
> >>>> Why is the file named lan966, but then everything inside lan966x?
> >>>>
> >>>>> +     u32 offset;
> >>>>> +     u32 ss_pin;
> >>>>> +};
> >>>>> +
> >>>>> +static int mux_lan966x_set(struct mux_control *mux, int state)
> >>>>> +{
> >>>>> +     struct mux_lan966x *mux_lan966x = mux_chip_priv(mux->chip);
> >>>>> +     u32 val;
> >>>>> +
> >>>>> +     val = ~(1 << mux_lan966x[state].ss_pin) & FLEX_SHRD_MASK;
> >>>>> +     writel(val, flx_shared_base + mux_lan966x[state].offset);
> >>>>
> >>>> This reads memory you have not allocated, if you select a state
> >>>> other than zero.
> >>>>
> >>> Ok. I will return error condition in case of trying to access none existing
> >> entry.
> >>>>> +
> >>>>> +     return 0;
> >>>>> +}
> >>>>> +
> >>>>> +static const struct mux_control_ops mux_lan966x_ops = {
> >>>>> +     .set = mux_lan966x_set,
> >>>>> +};
> >>>>> +
> >>>>> +static const struct of_device_id mux_lan966x_dt_ids[] = {
> >>>>> +     { .compatible = "microchip,lan966-flx-mux", },
> >>>>> +     { /* sentinel */ }
> >>>>> +};
> >>>>> +MODULE_DEVICE_TABLE(of, mux_lan966x_dt_ids);
> >>>>> +
> >>>>> +static int mux_lan966x_probe(struct platform_device *pdev)
> >>>>> +{
> >>>>> +     struct device_node *np = pdev->dev.of_node;
> >>>>> +     struct device *dev = &pdev->dev;
> >>>>> +     struct mux_lan966x *mux_lan966x;
> >>>>> +     struct mux_chip *mux_chip;
> >>>>> +     int ret, num_fields, i;
> >>>>> +
> >>>>> +     ret = of_property_count_u32_elems(np, "mux-offset-pin");
> >>>>> +     if (ret == 0 || ret % 2)
> >>>>> +             ret = -EINVAL;
> >>>>> +     if (ret < 0)
> >>>>> +             return dev_err_probe(dev, ret,
> >>>>> +                                  "mux-offset-pin property missing or invalid");
> >>>>> +     num_fields = ret / 2;
> >>>>> +
> >>>>> +     mux_chip = devm_mux_chip_alloc(dev, num_fields,
> >>>> sizeof(*mux_lan966x));
> >>>>
> >>>> I might be thoroughly mistaken and confused by the code, but it seems
> >>>> very strange that a subsequenct select is not undoing what a previous
> >>>> select once did. Each state seems to write to its own register offset,
> >>>> and there is nothing that restores the first register offset with you
> >>>> switch states.
> >>>>
> >>>> Care to explain how muxing works and what you are expected to do to
> >>>> manipulate the mux? Is there some link to public documentation? I did
> >>>> a quick search for lan966 but came up with nothing relevant.
> >>>>
> >>> LAN966 has 5 flexcoms(which can be used as USART/SPI/I2C interface).
> >>> FLEXCOM has two chip-select I/O lines namely CS0 and CS1
> >>> in SPI mode, CTS and RTS in USART mode. These FLEXCOM pins are
> >> optional.
> >>> These chip-selects can be mapped to flexcom shared pin [0-21] which
> can
> >> be
> >>> done by configuring flexcom multiplexer register(FLEXCOM_SHARED[0-
> >> 4]:SS_MASK[0-1])
> >>> Driver explanation:
> >>> "flx_shared_base" is used to get base address of Flexcom shared
> >> multiplexer
> >>> "mux-offset-pin" property is used to get cs0/cs1 offset and flexcom
> shared
> >> pin[0-21] of a flexcom.
> >>> state value passed from atmel-flexcom is used to configure respective
> >>> FLEXCOM_SHARED[0-4]:SS_MASK[0-1] register with offset and flexcom
> >> shared pin.
> >>
> >> Ok, let me try to interpret that. You wish enable a "fan out" of these
> >> two chip-selects for any of the 5 flexcoms (in SPI mode?) such that
> >> these 10 internal chip-selects can be connected to any of 21 external
> >> pins?
> >>
> >> If that's correct and if you wish to interface with e.g. 20 chips,
> >> then it would be possible to have 20 states for one mux control and
> >> then reach the 20 chips using only CS0 from FLEXCOM0, or it would be
> >> possible to have 2 states for 10 mux controls, one each for CS0/CS1 of
> >> all five flexcoms and also reach 20 chips. Or any wild combo you
> >> imagine is useful.
> >>
> >> Is that correctly understood?
> >>
> >> Assuming so, then you can have a maximum of 10 mux controls, and for
> >> each mux control you need a variable number of legitimate states. Each
> >> mux control would also need to know at what address/offset its SS_MASK
> >> register is at and what pins/states are valid.
> >>
> >> But your code does not implemnent the above. You allocate num_fields
> >> mux controls, which is what confuses the hell out of me. num_fields is
> >> the number of states, not the number of mux controls! And you also
> >> need to specify an individual offset for each state, and that makes no
> >> sense at all, at least not to me.
> >>
> >> So, instead, I think you want to have:
> >>
> >> struct mux_lan966x {
> >>         u32 ss_mask;
> >>         int num_pins;
> >>         u32 ss_pin[];
> >> };
> >>
> >> And then do:
> >>
> >>         mux_chip = devm_mux_chip_alloc(dev, 1, sizeof(*mux_lan966x) +
> >> num_pins * sizeof(u32));
> >>
> >> (or however that size is best spelled, I haven't kept up)
> >>
> >> The set operation would be along the lines of:
> >>
> >> static int mux_lan966x_set(struct mux_control *mux, int state)
> >> {
> >>         struct mux_lan966x *mux_lan966x = mux_chip_priv(mux->chip);
> >>         u32 val;
> >>
> >>         if (state < 0 || state >= mux_lan966x->num_pins)
> >>                 return -1;
> >>
> >>         val = ~(1 << mux_lan966x->ss_pin[state]) & FLEX_SHRD_MASK;
> >>         writel(val, flx_shared_base + mux_lan966x->ss_mask);
> >>
> >>         return 0;
> >> }
> >>
> >> Because, one mux control should only ever need to know about one
> offset,
> >> as it should only ever write to its own SS_MASK register. And you will
> >> need some private space such that you can keep track of which states
> >> are legit.
> >>
> >> I would also separate out the SS_MASK offset from the mux-offset-pin
> >> property and have one property for that value and then a straight
> >> array for the valid pin numbers in another property (no longer named
> >> mux-offset-pin of course).
> >>
> >> Or something like that and assuming I understand how the FLEXCOMs
> work
> >> and what you want to do etc.
> >>
> >
> > Thank you for your comments
> > I agree, I will change number of mux controls to 1 in
> devm_mux_chip_alloc()
> > I would like to use mux-offset-pin property because
> > each chip-select must be mapped to a unique flexcom shared pin.
> > For example,
> > mux-offset-pin = <0x18 9>, /* 0: flexcom3 CS0 mapped to pin-9 */
> >                                <0x1c 7>, /* 1: flexcom3 CS1 mapped to pin-7 */
> >                                <0x20 4>; /* 2: flexcom4 CS0 mapped to pin-4 */};
> > I want to use mux-offset-pin property to be clear about which offset is
> mapped to which
> > flexcom shared pin. Here, 0, 1, 2.. entries represents state passed from
> mux_control_select(mux, state).
> >
> > Please provide your comments.
> 
> It seems you want to just use the static info from the devicetree
> to program your registers once and for all? That's not a problem
> that a mux driver is aming at solving. My thought was that if you
> have one SPI device with chip select on pin-7, another with chip-
> select on pin-9 and a third on pin-4, then you /could/ use them all
> from e.g. flexcom3/CS0 by reprogramming that SS_MASK register
> at the time you want to access one of the three chips. You could
> then of course not access the three chips in parallel, but the
> muxing of the accesses to three chips like that is the problem
> that the mux subsystem helps with.
> 
> I.e. pretty much exactly as is described in
> 
>         Documentation/devicetree/bindings/spi/spi-mux.yaml
> 
> but with a mux node bringing your new driver into the picture
> instead of the "gpio-mux" in that example (and the "CS-X" signal
> and the "Mux" block in that picture would be internal to the SoC).
> 
> If you are content with just programming a fixed set of values to
> a couple of registers depending on how the board is wired, some
> extra DT property on some node related to the flexcom seems like
> a better fit than a mux driver.
> 
> Having said that, the mux solution above would solve this static
> case too. You would just need to configure a mux with one state.
> It would be a couple of extra nodes in the device tree, but it
> would certainly work (and also cover the more complex case when
> someone pops up and needs that).
> 
> Cheers,
> Peter
> 
> >> Cheers,
> >> Peter
> >>
> >>
> >>>>> +     if (IS_ERR(mux_chip))
> >>>>> +             return dev_err_probe(dev, PTR_ERR(mux_chip),
> >>>>> +                                  "failed to allocate mux_chips\n");
> >>>>> +
> >>>>> +     mux_lan966x = mux_chip_priv(mux_chip);
> >>>>> +
> >>>>> +     flx_shared_base =
> >> devm_platform_get_and_ioremap_resource(pdev,
> >>>> 0, NULL);
> >>>>> +     if (IS_ERR(flx_shared_base))
> >>>>> +             return dev_err_probe(dev, PTR_ERR(flx_shared_base),
> >>>>> +                                  "failed to get flexcom shared base address\n");
> >>>>> +
> >>>>> +     for (i = 0; i < num_fields; i++) {
> >>>>> +             struct mux_control *mux = &mux_chip->mux[i];
> >>>>> +             u32 offset, shared_pin;
> >>>>> +
> >>>>> +             ret = of_property_read_u32_index(np, "mux-offset-pin",
> >>>>> +                                              2 * i, &offset);
> >>>>> +             if (ret == 0)
> >>>>> +                     ret = of_property_read_u32_index(np, "mux-offset-pin",
> >>>>> +                                                      2 * i + 1,
> >>>>> +                                                      &shared_pin);
> >>>>> +             if (ret < 0)
> >>>>> +                     return dev_err_probe(dev, ret,
> >>>>> +                                          "failed to read mux-offset-pin property: %d",
> i);
> >>>>> +
> >>>>> +             if (shared_pin >= LAN966_MAX_CS)
> >>>>> +                     return -EINVAL;
> >>>>> +
> >>>>> +             mux_lan966x[i].offset = offset;
> >>>>> +             mux_lan966x[i].ss_pin = shared_pin;
> >>>>
> >>>> This clobbers memory you have not allocated, if num_fields >= 1.
> >>>>
> >>>> Cheers,
> >>>> Peter
> >>>>
> >>>>> +
> >>>>> +             mux->states = LAN966_MAX_CS;
> >>>>> +     }
> >>>>> +
> >>>>> +     mux_chip->ops = &mux_lan966x_ops;
> >>>>> +
> >>>>> +     ret = devm_mux_chip_register(dev, mux_chip);
> >>>>> +     if (ret < 0)
> >>>>> +             return ret;
> >>>>> +
> >>>>> +     return 0;
> >>>>> +}
> >>>>> +
> >>>>> +static struct platform_driver mux_lan966x_driver = {
> >>>>> +     .driver = {
> >>>>> +             .name = "lan966-mux",
> >>>>> +             .of_match_table = of_match_ptr(mux_lan966x_dt_ids),
> >>>>> +     },
> >>>>> +     .probe = mux_lan966x_probe,
> >>>>> +};
> >>>>> +
> >>>>> +module_platform_driver(mux_lan966x_driver);
> >>>>> +
> >>>>> +MODULE_DESCRIPTION("LAN966 Flexcom multiplexer driver");
> >>>>> +MODULE_AUTHOR("Kavyasree Kotagiri
> >>>> <kavyasree.kotagiri@microchip.com>");
> >>>>> +MODULE_LICENSE("GPL v2");
> >>>>> +

^ permalink raw reply	[flat|nested] 21+ messages in thread

end of thread, other threads:[~2022-05-27  6:00 UTC | newest]

Thread overview: 21+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-05-09  8:49 [PATCH v2 0/4] Add support for lan966 flexcom multiplexer Kavyasree Kotagiri
2022-05-09  8:49 ` [PATCH v2 1/4] dt-bindings: mfd: atmel,flexcom: Convert to json-schema Kavyasree Kotagiri
2022-05-10 10:32   ` Krzysztof Kozlowski
2022-05-09  8:49 ` [PATCH v2 2/4] dt-bindings: mfd: atmel,flexcom: Add lan966 compatible string and mux properties Kavyasree Kotagiri
2022-05-10 10:33   ` Krzysztof Kozlowski
2022-05-09  8:49 ` [PATCH v2 3/4] dt-bindings: mux: Add lan966 flexcom mux controller Kavyasree Kotagiri
2022-05-10 10:37   ` Krzysztof Kozlowski
2022-05-09  8:49 ` [PATCH v2 4/4] mux: lan966: Add support for " Kavyasree Kotagiri
2022-05-09 11:37   ` Peter Rosin
2022-05-09 11:41     ` Peter Rosin
2022-05-10 14:59     ` Kavyasree.Kotagiri
2022-05-10 19:38       ` Peter Rosin
2022-05-11 14:28         ` Kavyasree.Kotagiri
2022-05-11 15:43           ` Peter Rosin
2022-05-27  6:00             ` Kavyasree.Kotagiri
2022-05-17 11:53     ` Michael Walle
2022-05-17 12:00       ` Peter Rosin
2022-05-10  1:46   ` kernel test robot
2022-05-10  3:28   ` kernel test robot
2022-05-11  9:06   ` Claudiu.Beznea
2022-05-23 14:52   ` Lee Jones

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).