openbmc.lists.ozlabs.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v2 01/21] dt-bindings: aspeed-lpc: Remove LPC partitioning
@ 2021-03-19  6:27 Andrew Jeffery
  2021-03-19  6:27 ` [PATCH v2 02/21] ARM: dts: Remove LPC BMC and Host partitions Andrew Jeffery
                   ` (20 more replies)
  0 siblings, 21 replies; 67+ messages in thread
From: Andrew Jeffery @ 2021-03-19  6:27 UTC (permalink / raw)
  To: openipmi-developer, openbmc, minyard
  Cc: devicetree, ryan_chen, tmaimon77, linux-aspeed, Rob Herring,
	avifishman70, venture, Chia-Wei, Wang, linux-kernel, tali.perry1,
	linux-gpio, robh+dt, lee.jones, linus.walleij, linux-arm-kernel,
	benjaminfair

From: "Chia-Wei, Wang" <chiawei_wang@aspeedtech.com>

The LPC controller has no concept of the BMC and the Host partitions.
This patch fixes the documentation by removing the description on LPC
partitions. The register offsets illustrated in the DTS node examples
are also fixed to adapt to the LPC DTS change.

Signed-off-by: Chia-Wei Wang <chiawei_wang@aspeedtech.com>
Reviewed-by: Andrew Jeffery <andrew@aj.id.au>
Acked-by: Rob Herring <robh@kernel.org>
Acked-by: Lee Jones <lee.jones@linaro.org>
---
 .../devicetree/bindings/mfd/aspeed-lpc.txt    | 100 +++++-------------
 1 file changed, 25 insertions(+), 75 deletions(-)

diff --git a/Documentation/devicetree/bindings/mfd/aspeed-lpc.txt b/Documentation/devicetree/bindings/mfd/aspeed-lpc.txt
index d0a38ba8b9ce..936aa108eab4 100644
--- a/Documentation/devicetree/bindings/mfd/aspeed-lpc.txt
+++ b/Documentation/devicetree/bindings/mfd/aspeed-lpc.txt
@@ -9,13 +9,7 @@ primary use case of the Aspeed LPC controller is as a slave on the bus
 conditions it can also take the role of bus master.
 
 The LPC controller is represented as a multi-function device to account for the
-mix of functionality it provides. The principle split is between the register
-layout at the start of the I/O space which is, to quote the Aspeed datasheet,
-"basically compatible with the [LPC registers from the] popular BMC controller
-H8S/2168[1]", and everything else, where everything else is an eclectic
-collection of functions with a esoteric register layout. "Everything else",
-here labeled the "host" portion of the controller, includes, but is not limited
-to:
+mix of functionality, which includes, but is not limited to:
 
 * An IPMI Block Transfer[2] Controller
 
@@ -44,80 +38,36 @@ Required properties
 ===================
 
 - compatible:	One of:
-		"aspeed,ast2400-lpc", "simple-mfd"
-		"aspeed,ast2500-lpc", "simple-mfd"
-		"aspeed,ast2600-lpc", "simple-mfd"
+		"aspeed,ast2400-lpc-v2", "simple-mfd", "syscon"
+		"aspeed,ast2500-lpc-v2", "simple-mfd", "syscon"
+		"aspeed,ast2600-lpc-v2", "simple-mfd", "syscon"
 
 - reg:		contains the physical address and length values of the Aspeed
                 LPC memory region.
 
 - #address-cells: <1>
 - #size-cells:	<1>
-- ranges: 	Maps 0 to the physical address and length of the LPC memory
-                region
-
-Required LPC Child nodes
-========================
-
-BMC Node
---------
-
-- compatible:	One of:
-		"aspeed,ast2400-lpc-bmc"
-		"aspeed,ast2500-lpc-bmc"
-		"aspeed,ast2600-lpc-bmc"
-
-- reg:		contains the physical address and length values of the
-                H8S/2168-compatible LPC controller memory region
-
-Host Node
----------
-
-- compatible:   One of:
-		"aspeed,ast2400-lpc-host", "simple-mfd", "syscon"
-		"aspeed,ast2500-lpc-host", "simple-mfd", "syscon"
-		"aspeed,ast2600-lpc-host", "simple-mfd", "syscon"
-
-- reg:		contains the address and length values of the host-related
-                register space for the Aspeed LPC controller
-
-- #address-cells: <1>
-- #size-cells:	<1>
-- ranges: 	Maps 0 to the address and length of the host-related LPC memory
+- ranges:	Maps 0 to the physical address and length of the LPC memory
                 region
 
 Example:
 
 lpc: lpc@1e789000 {
-	compatible = "aspeed,ast2500-lpc", "simple-mfd";
+	compatible = "aspeed,ast2500-lpc-v2", "simple-mfd", "syscon";
 	reg = <0x1e789000 0x1000>;
 
 	#address-cells = <1>;
 	#size-cells = <1>;
 	ranges = <0x0 0x1e789000 0x1000>;
 
-	lpc_bmc: lpc-bmc@0 {
-		compatible = "aspeed,ast2500-lpc-bmc";
+	lpc_snoop: lpc-snoop@0 {
+		compatible = "aspeed,ast2600-lpc-snoop";
 		reg = <0x0 0x80>;
-	};
-
-	lpc_host: lpc-host@80 {
-		compatible = "aspeed,ast2500-lpc-host", "simple-mfd", "syscon";
-		reg = <0x80 0x1e0>;
-		reg-io-width = <4>;
-
-		#address-cells = <1>;
-		#size-cells = <1>;
-		ranges = <0x0 0x80 0x1e0>;
+		interrupts = <GIC_SPI 144 IRQ_TYPE_LEVEL_HIGH>;
+		snoop-ports = <0x80>;
 	};
 };
 
-BMC Node Children
-==================
-
-
-Host Node Children
-==================
 
 LPC Host Interface Controller
 -------------------
@@ -149,14 +99,12 @@ Optional properties:
 
 Example:
 
-lpc-host@80 {
-	lpc_ctrl: lpc-ctrl@0 {
-		compatible = "aspeed,ast2500-lpc-ctrl";
-		reg = <0x0 0x80>;
-		clocks = <&syscon ASPEED_CLK_GATE_LCLK>;
-		memory-region = <&flash_memory>;
-		flash = <&spi>;
-	};
+lpc_ctrl: lpc-ctrl@80 {
+	compatible = "aspeed,ast2500-lpc-ctrl";
+	reg = <0x80 0x80>;
+	clocks = <&syscon ASPEED_CLK_GATE_LCLK>;
+	memory-region = <&flash_memory>;
+	flash = <&spi>;
 };
 
 LPC Host Controller
@@ -179,9 +127,9 @@ Required properties:
 
 Example:
 
-lhc: lhc@20 {
+lhc: lhc@a0 {
 	compatible = "aspeed,ast2500-lhc";
-	reg = <0x20 0x24 0x48 0x8>;
+	reg = <0xa0 0x24 0xc8 0x8>;
 };
 
 LPC reset control
@@ -192,16 +140,18 @@ state of the LPC bus. Some systems may chose to modify this configuration.
 
 Required properties:
 
- - compatible:		"aspeed,ast2600-lpc-reset" or
-			"aspeed,ast2500-lpc-reset"
-			"aspeed,ast2400-lpc-reset"
+ - compatible:		One of:
+			"aspeed,ast2600-lpc-reset";
+			"aspeed,ast2500-lpc-reset";
+			"aspeed,ast2400-lpc-reset";
+
  - reg:			offset and length of the IP in the LHC memory region
  - #reset-controller	indicates the number of reset cells expected
 
 Example:
 
-lpc_reset: reset-controller@18 {
+lpc_reset: reset-controller@98 {
         compatible = "aspeed,ast2500-lpc-reset";
-        reg = <0x18 0x4>;
+        reg = <0x98 0x4>;
         #reset-cells = <1>;
 };
-- 
2.27.0


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

* [PATCH v2 02/21] ARM: dts: Remove LPC BMC and Host partitions
  2021-03-19  6:27 [PATCH v2 01/21] dt-bindings: aspeed-lpc: Remove LPC partitioning Andrew Jeffery
@ 2021-03-19  6:27 ` Andrew Jeffery
  2021-03-19  6:27 ` [PATCH v2 03/21] ipmi: kcs: aspeed: Adapt to new LPC DTS layout Andrew Jeffery
                   ` (19 subsequent siblings)
  20 siblings, 0 replies; 67+ messages in thread
From: Andrew Jeffery @ 2021-03-19  6:27 UTC (permalink / raw)
  To: openipmi-developer, openbmc, minyard
  Cc: devicetree, ryan_chen, tmaimon77, linux-aspeed, avifishman70,
	venture, Chia-Wei, Wang, linux-kernel, tali.perry1, linux-gpio,
	robh+dt, lee.jones, linus.walleij, linux-arm-kernel,
	benjaminfair

From: "Chia-Wei, Wang" <chiawei_wang@aspeedtech.com>

The LPC controller has no concept of the BMC and the Host partitions.

A concrete instance is that the HICRB[5:4] are for the I/O port address
configurtaion of KCS channel 1/2. However, the KCS driver cannot access
HICRB for channel 1/2 initialization via syscon regmap interface due to
the parition boundary. (i.e. offset 80h)

In addition, for the HW design backward compatibility, a newly added HW
control bit could be located at any reserved one over the LPC addressing
space. Thereby, this patch removes the lpc-bmc and lpc-host child node
and thus the LPC partitioning.

Note that this change requires the synchronization between device tree
change and the driver change. To prevent the misuse of old devicetrees
with new drivers, or vice versa, the v2 compatible strings are adopted
for the LPC device as listed:

	"aspeed,ast2400-lpc-v2"
	"aspeed,ast2500-lpc-v2"
	"aspeed,ast2600-lpc-v2"

Signed-off-by: Chia-Wei Wang <chiawei_wang@aspeedtech.com>
Reviewed-by: Andrew Jeffery <andrew@aj.id.au>
---
 arch/arm/boot/dts/aspeed-g4.dtsi |  70 +++++++-----------
 arch/arm/boot/dts/aspeed-g5.dtsi | 121 +++++++++++++-----------------
 arch/arm/boot/dts/aspeed-g6.dtsi | 123 +++++++++++++------------------
 3 files changed, 133 insertions(+), 181 deletions(-)

diff --git a/arch/arm/boot/dts/aspeed-g4.dtsi b/arch/arm/boot/dts/aspeed-g4.dtsi
index e7a45ba18fc9..c5aeb3cf3a09 100644
--- a/arch/arm/boot/dts/aspeed-g4.dtsi
+++ b/arch/arm/boot/dts/aspeed-g4.dtsi
@@ -343,59 +343,45 @@ vuart: serial@1e787000 {
 			};
 
 			lpc: lpc@1e789000 {
-				compatible = "aspeed,ast2400-lpc", "simple-mfd";
+				compatible = "aspeed,ast2400-lpc-v2", "simple-mfd", "syscon";
 				reg = <0x1e789000 0x1000>;
+				reg-io-width = <4>;
 
 				#address-cells = <1>;
 				#size-cells = <1>;
 				ranges = <0x0 0x1e789000 0x1000>;
 
-				lpc_bmc: lpc-bmc@0 {
-					compatible = "aspeed,ast2400-lpc-bmc";
-					reg = <0x0 0x80>;
+				lpc_ctrl: lpc-ctrl@80 {
+					compatible = "aspeed,ast2400-lpc-ctrl";
+					reg = <0x80 0x10>;
+					clocks = <&syscon ASPEED_CLK_GATE_LCLK>;
+					status = "disabled";
 				};
 
-				lpc_host: lpc-host@80 {
-					compatible = "aspeed,ast2400-lpc-host", "simple-mfd", "syscon";
-					reg = <0x80 0x1e0>;
-					reg-io-width = <4>;
-
-					#address-cells = <1>;
-					#size-cells = <1>;
-					ranges = <0x0 0x80 0x1e0>;
-
-					lpc_ctrl: lpc-ctrl@0 {
-						compatible = "aspeed,ast2400-lpc-ctrl";
-						reg = <0x0 0x10>;
-						clocks = <&syscon ASPEED_CLK_GATE_LCLK>;
-						status = "disabled";
-					};
-
-					lpc_snoop: lpc-snoop@10 {
-						compatible = "aspeed,ast2400-lpc-snoop";
-						reg = <0x10 0x8>;
-						interrupts = <8>;
-						clocks = <&syscon ASPEED_CLK_GATE_LCLK>;
-						status = "disabled";
-					};
+				lpc_snoop: lpc-snoop@90 {
+					compatible = "aspeed,ast2400-lpc-snoop";
+					reg = <0x90 0x8>;
+					interrupts = <8>;
+					clocks = <&syscon ASPEED_CLK_GATE_LCLK>;
+					status = "disabled";
+				};
 
-					lhc: lhc@20 {
-						compatible = "aspeed,ast2400-lhc";
-						reg = <0x20 0x24 0x48 0x8>;
-					};
+				lhc: lhc@a0 {
+					compatible = "aspeed,ast2400-lhc";
+					reg = <0xa0 0x24 0xc8 0x8>;
+				};
 
-					lpc_reset: reset-controller@18 {
-						compatible = "aspeed,ast2400-lpc-reset";
-						reg = <0x18 0x4>;
-						#reset-cells = <1>;
-					};
+				lpc_reset: reset-controller@98 {
+					compatible = "aspeed,ast2400-lpc-reset";
+					reg = <0x98 0x4>;
+					#reset-cells = <1>;
+				};
 
-					ibt: ibt@c0  {
-						compatible = "aspeed,ast2400-ibt-bmc";
-						reg = <0xc0 0x18>;
-						interrupts = <8>;
-						status = "disabled";
-					};
+				ibt: ibt@140 {
+					compatible = "aspeed,ast2400-ibt-bmc";
+					reg = <0x140 0x18>;
+					interrupts = <8>;
+					status = "disabled";
 				};
 			};
 
diff --git a/arch/arm/boot/dts/aspeed-g5.dtsi b/arch/arm/boot/dts/aspeed-g5.dtsi
index 21930521a986..d733c1f161c1 100644
--- a/arch/arm/boot/dts/aspeed-g5.dtsi
+++ b/arch/arm/boot/dts/aspeed-g5.dtsi
@@ -434,91 +434,74 @@ vuart: serial@1e787000 {
 			};
 
 			lpc: lpc@1e789000 {
-				compatible = "aspeed,ast2500-lpc", "simple-mfd";
+				compatible = "aspeed,ast2500-lpc-v2", "simple-mfd", "syscon";
 				reg = <0x1e789000 0x1000>;
+				reg-io-width = <4>;
 
 				#address-cells = <1>;
 				#size-cells = <1>;
 				ranges = <0x0 0x1e789000 0x1000>;
 
-				lpc_bmc: lpc-bmc@0 {
-					compatible = "aspeed,ast2500-lpc-bmc", "simple-mfd", "syscon";
-					reg = <0x0 0x80>;
-					reg-io-width = <4>;
-
-					#address-cells = <1>;
-					#size-cells = <1>;
-					ranges = <0x0 0x0 0x80>;
-
-					kcs1: kcs@24 {
-						compatible = "aspeed,ast2500-kcs-bmc-v2";
-						reg = <0x24 0x1>, <0x30 0x1>, <0x3c 0x1>;
-						interrupts = <8>;
-						status = "disabled";
-					};
-					kcs2: kcs@28 {
-						compatible = "aspeed,ast2500-kcs-bmc-v2";
-						reg = <0x28 0x1>, <0x34 0x1>, <0x40 0x1>;
-						interrupts = <8>;
-						status = "disabled";
-					};
-					kcs3: kcs@2c {
-						compatible = "aspeed,ast2500-kcs-bmc-v2";
-						reg = <0x2c 0x1>, <0x38 0x1>, <0x44 0x1>;
-						interrupts = <8>;
-						status = "disabled";
-					};
+				kcs1: kcs@24 {
+					compatible = "aspeed,ast2500-kcs-bmc-v2";
+					reg = <0x24 0x1>, <0x30 0x1>, <0x3c 0x1>;
+					interrupts = <8>;
+					status = "disabled";
 				};
 
-				lpc_host: lpc-host@80 {
-					compatible = "aspeed,ast2500-lpc-host", "simple-mfd", "syscon";
-					reg = <0x80 0x1e0>;
-					reg-io-width = <4>;
+				kcs2: kcs@28 {
+					compatible = "aspeed,ast2500-kcs-bmc-v2";
+					reg = <0x28 0x1>, <0x34 0x1>, <0x40 0x1>;
+					interrupts = <8>;
+					status = "disabled";
+				};
 
-					#address-cells = <1>;
-					#size-cells = <1>;
-					ranges = <0x0 0x80 0x1e0>;
+				kcs3: kcs@2c {
+					compatible = "aspeed,ast2500-kcs-bmc-v2";
+					reg = <0x2c 0x1>, <0x38 0x1>, <0x44 0x1>;
+					interrupts = <8>;
+					status = "disabled";
+				};
 
-					kcs4: kcs@94 {
-						compatible = "aspeed,ast2500-kcs-bmc-v2";
-						reg = <0x94 0x1>, <0x98 0x1>, <0x9c 0x1>;
-						interrupts = <8>;
-						status = "disabled";
-					};
+				kcs4: kcs@114 {
+					compatible = "aspeed,ast2500-kcs-bmc-v2";
+					reg = <0x114 0x1>, <0x118 0x1>, <0x11c 0x1>;
+					interrupts = <8>;
+					status = "disabled";
+				};
 
-					lpc_ctrl: lpc-ctrl@0 {
-						compatible = "aspeed,ast2500-lpc-ctrl";
-						reg = <0x0 0x10>;
-						clocks = <&syscon ASPEED_CLK_GATE_LCLK>;
-						status = "disabled";
-					};
+				lpc_ctrl: lpc-ctrl@80 {
+					compatible = "aspeed,ast2500-lpc-ctrl";
+					reg = <0x80 0x10>;
+					clocks = <&syscon ASPEED_CLK_GATE_LCLK>;
+					status = "disabled";
+				};
 
-					lpc_snoop: lpc-snoop@10 {
-						compatible = "aspeed,ast2500-lpc-snoop";
-						reg = <0x10 0x8>;
-						interrupts = <8>;
-						clocks = <&syscon ASPEED_CLK_GATE_LCLK>;
-						status = "disabled";
-					};
+				lpc_snoop: lpc-snoop@90 {
+					compatible = "aspeed,ast2500-lpc-snoop";
+					reg = <0x90 0x8>;
+					interrupts = <8>;
+					clocks = <&syscon ASPEED_CLK_GATE_LCLK>;
+					status = "disabled";
+				};
 
-					lpc_reset: reset-controller@18 {
-						compatible = "aspeed,ast2500-lpc-reset";
-						reg = <0x18 0x4>;
-						#reset-cells = <1>;
-					};
+				lpc_reset: reset-controller@98 {
+					compatible = "aspeed,ast2500-lpc-reset";
+					reg = <0x98 0x4>;
+					#reset-cells = <1>;
+				};
 
-					lhc: lhc@20 {
-						compatible = "aspeed,ast2500-lhc";
-						reg = <0x20 0x24 0x48 0x8>;
-					};
+				lhc: lhc@a0 {
+					compatible = "aspeed,ast2500-lhc";
+					reg = <0xa0 0x24 0xc8 0x8>;
+				};
 
 
-					ibt: ibt@c0 {
-						compatible = "aspeed,ast2500-ibt-bmc";
-						reg = <0xc0 0x18>;
-						interrupts = <8>;
-						status = "disabled";
-					};
+				ibt: ibt@140 {
+					compatible = "aspeed,ast2500-ibt-bmc";
+					reg = <0x140 0x18>;
+					interrupts = <8>;
+					status = "disabled";
 				};
 			};
 
diff --git a/arch/arm/boot/dts/aspeed-g6.dtsi b/arch/arm/boot/dts/aspeed-g6.dtsi
index 3ee470c2b7b5..f96607b7b4e2 100644
--- a/arch/arm/boot/dts/aspeed-g6.dtsi
+++ b/arch/arm/boot/dts/aspeed-g6.dtsi
@@ -460,91 +460,74 @@ wdt4: watchdog@1e7850c0 {
 			};
 
 			lpc: lpc@1e789000 {
-				compatible = "aspeed,ast2600-lpc", "simple-mfd";
+				compatible = "aspeed,ast2600-lpc-v2", "simple-mfd", "syscon";
 				reg = <0x1e789000 0x1000>;
+				reg-io-width = <4>;
 
 				#address-cells = <1>;
 				#size-cells = <1>;
 				ranges = <0x0 0x1e789000 0x1000>;
 
-				lpc_bmc: lpc-bmc@0 {
-					compatible = "aspeed,ast2600-lpc-bmc", "simple-mfd", "syscon";
-					reg = <0x0 0x80>;
-					reg-io-width = <4>;
-
-					#address-cells = <1>;
-					#size-cells = <1>;
-					ranges = <0x0 0x0 0x80>;
-
-					kcs1: kcs@24 {
-						compatible = "aspeed,ast2500-kcs-bmc-v2";
-						reg = <0x24 0x1>, <0x30 0x1>, <0x3c 0x1>;
-						interrupts = <GIC_SPI 138 IRQ_TYPE_LEVEL_HIGH>;
-						kcs_chan = <1>;
-						status = "disabled";
-					};
-					kcs2: kcs@28 {
-						compatible = "aspeed,ast2500-kcs-bmc-v2";
-						reg = <0x28 0x1>, <0x34 0x1>, <0x40 0x1>;
-						interrupts = <GIC_SPI 139 IRQ_TYPE_LEVEL_HIGH>;
-						status = "disabled";
-					};
-					kcs3: kcs@2c {
-						compatible = "aspeed,ast2500-kcs-bmc-v2";
-						reg = <0x2c 0x1>, <0x38 0x1>, <0x44 0x1>;
-						interrupts = <GIC_SPI 140 IRQ_TYPE_LEVEL_HIGH>;
-						status = "disabled";
-					};
+				kcs1: kcs@24 {
+					compatible = "aspeed,ast2500-kcs-bmc-v2";
+					reg = <0x24 0x1>, <0x30 0x1>, <0x3c 0x1>;
+					interrupts = <GIC_SPI 138 IRQ_TYPE_LEVEL_HIGH>;
+					kcs_chan = <1>;
+					status = "disabled";
 				};
 
-				lpc_host: lpc-host@80 {
-					compatible = "aspeed,ast2600-lpc-host", "simple-mfd", "syscon";
-					reg = <0x80 0x1e0>;
-					reg-io-width = <4>;
+				kcs2: kcs@28 {
+					compatible = "aspeed,ast2500-kcs-bmc-v2";
+					reg = <0x28 0x1>, <0x34 0x1>, <0x40 0x1>;
+					interrupts = <GIC_SPI 139 IRQ_TYPE_LEVEL_HIGH>;
+					status = "disabled";
+				};
 
-					#address-cells = <1>;
-					#size-cells = <1>;
-					ranges = <0x0 0x80 0x1e0>;
+				kcs3: kcs@2c {
+					compatible = "aspeed,ast2500-kcs-bmc-v2";
+					reg = <0x2c 0x1>, <0x38 0x1>, <0x44 0x1>;
+					interrupts = <GIC_SPI 140 IRQ_TYPE_LEVEL_HIGH>;
+					status = "disabled";
+				};
 
-					kcs4: kcs@94 {
-						compatible = "aspeed,ast2500-kcs-bmc-v2";
-						reg = <0x94 0x1>, <0x98 0x1>, <0x9c 0x1>;
-						interrupts = <GIC_SPI 141 IRQ_TYPE_LEVEL_HIGH>;
-						status = "disabled";
-					};
+				kcs4: kcs@114 {
+					compatible = "aspeed,ast2500-kcs-bmc-v2";
+					reg = <0x114 0x1>, <0x118 0x1>, <0x11c 0x1>;
+					interrupts = <GIC_SPI 141 IRQ_TYPE_LEVEL_HIGH>;
+					status = "disabled";
+				};
 
-					lpc_ctrl: lpc-ctrl@0 {
-						compatible = "aspeed,ast2600-lpc-ctrl";
-						reg = <0x0 0x80>;
-						clocks = <&syscon ASPEED_CLK_GATE_LCLK>;
-						status = "disabled";
-					};
+				lpc_ctrl: lpc-ctrl@80 {
+					compatible = "aspeed,ast2600-lpc-ctrl";
+					reg = <0x80 0x80>;
+					clocks = <&syscon ASPEED_CLK_GATE_LCLK>;
+					status = "disabled";
+				};
 
-					lpc_snoop: lpc-snoop@0 {
-						compatible = "aspeed,ast2600-lpc-snoop";
-						reg = <0x0 0x80>;
-						interrupts = <GIC_SPI 144 IRQ_TYPE_LEVEL_HIGH>;
-						clocks = <&syscon ASPEED_CLK_GATE_LCLK>;
-						status = "disabled";
-					};
+				lpc_snoop: lpc-snoop@80 {
+					compatible = "aspeed,ast2600-lpc-snoop";
+					reg = <0x80 0x80>;
+					interrupts = <GIC_SPI 144 IRQ_TYPE_LEVEL_HIGH>;
+					clocks = <&syscon ASPEED_CLK_GATE_LCLK>;
+					status = "disabled";
+				};
 
-					lhc: lhc@20 {
-						compatible = "aspeed,ast2600-lhc";
-						reg = <0x20 0x24 0x48 0x8>;
-					};
+				lhc: lhc@a0 {
+					compatible = "aspeed,ast2600-lhc";
+					reg = <0xa0 0x24 0xc8 0x8>;
+				};
 
-					lpc_reset: reset-controller@18 {
-						compatible = "aspeed,ast2600-lpc-reset";
-						reg = <0x18 0x4>;
-						#reset-cells = <1>;
-					};
+				lpc_reset: reset-controller@98 {
+					compatible = "aspeed,ast2600-lpc-reset";
+					reg = <0x98 0x4>;
+					#reset-cells = <1>;
+				};
 
-					ibt: ibt@c0 {
-						compatible = "aspeed,ast2600-ibt-bmc";
-						reg = <0xc0 0x18>;
-						interrupts = <GIC_SPI 143 IRQ_TYPE_LEVEL_HIGH>;
-						status = "disabled";
-					};
+				ibt: ibt@140 {
+					compatible = "aspeed,ast2600-ibt-bmc";
+					reg = <0x140 0x18>;
+					interrupts = <GIC_SPI 143 IRQ_TYPE_LEVEL_HIGH>;
+					status = "disabled";
 				};
 			};
 
-- 
2.27.0


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

* [PATCH v2 03/21] ipmi: kcs: aspeed: Adapt to new LPC DTS layout
  2021-03-19  6:27 [PATCH v2 01/21] dt-bindings: aspeed-lpc: Remove LPC partitioning Andrew Jeffery
  2021-03-19  6:27 ` [PATCH v2 02/21] ARM: dts: Remove LPC BMC and Host partitions Andrew Jeffery
@ 2021-03-19  6:27 ` Andrew Jeffery
  2021-04-09  3:35   ` Joel Stanley
  2021-03-19  6:27 ` [PATCH v2 04/21] pinctrl: aspeed-g5: Adapt to new LPC device tree layout Andrew Jeffery
                   ` (18 subsequent siblings)
  20 siblings, 1 reply; 67+ messages in thread
From: Andrew Jeffery @ 2021-03-19  6:27 UTC (permalink / raw)
  To: openipmi-developer, openbmc, minyard
  Cc: devicetree, Haiyue Wang, ryan_chen, tmaimon77, linux-aspeed,
	avifishman70, venture, Chia-Wei, Wang, linux-kernel, tali.perry1,
	linux-gpio, robh+dt, lee.jones, linus.walleij, linux-arm-kernel,
	benjaminfair

From: "Chia-Wei, Wang" <chiawei_wang@aspeedtech.com>

Add check against LPC device v2 compatible string to
ensure that the fixed device tree layout is adopted.
The LPC register offsets are also fixed accordingly.

Signed-off-by: Chia-Wei Wang <chiawei_wang@aspeedtech.com>
Reviewed-by: Andrew Jeffery <andrew@aj.id.au>
Acked-by: Haiyue Wang <haiyue.wang@linux.intel.com>
---
 drivers/char/ipmi/kcs_bmc_aspeed.c | 27 ++++++++++++++++-----------
 1 file changed, 16 insertions(+), 11 deletions(-)

diff --git a/drivers/char/ipmi/kcs_bmc_aspeed.c b/drivers/char/ipmi/kcs_bmc_aspeed.c
index a140203c079b..eefe362f65f0 100644
--- a/drivers/char/ipmi/kcs_bmc_aspeed.c
+++ b/drivers/char/ipmi/kcs_bmc_aspeed.c
@@ -27,7 +27,6 @@
 
 #define KCS_CHANNEL_MAX     4
 
-/* mapped to lpc-bmc@0 IO space */
 #define LPC_HICR0            0x000
 #define     LPC_HICR0_LPC3E          BIT(7)
 #define     LPC_HICR0_LPC2E          BIT(6)
@@ -52,15 +51,13 @@
 #define LPC_STR1             0x03C
 #define LPC_STR2             0x040
 #define LPC_STR3             0x044
-
-/* mapped to lpc-host@80 IO space */
-#define LPC_HICRB            0x080
+#define LPC_HICRB            0x100
 #define     LPC_HICRB_IBFIF4         BIT(1)
 #define     LPC_HICRB_LPC4E          BIT(0)
-#define LPC_LADR4            0x090
-#define LPC_IDR4             0x094
-#define LPC_ODR4             0x098
-#define LPC_STR4             0x09C
+#define LPC_LADR4            0x110
+#define LPC_IDR4             0x114
+#define LPC_ODR4             0x118
+#define LPC_STR4             0x11C
 
 struct aspeed_kcs_bmc {
 	struct regmap *map;
@@ -348,12 +345,20 @@ static int aspeed_kcs_probe(struct platform_device *pdev)
 	struct device_node *np;
 	int rc;
 
-	np = pdev->dev.of_node;
+	np = dev->of_node->parent;
+	if (!of_device_is_compatible(np, "aspeed,ast2400-lpc-v2") &&
+	    !of_device_is_compatible(np, "aspeed,ast2500-lpc-v2") &&
+	    !of_device_is_compatible(np, "aspeed,ast2600-lpc-v2")) {
+		dev_err(dev, "unsupported LPC device binding\n");
+		return -ENODEV;
+	}
+
+	np = dev->of_node;
 	if (of_device_is_compatible(np, "aspeed,ast2400-kcs-bmc") ||
-			of_device_is_compatible(np, "aspeed,ast2500-kcs-bmc"))
+	    of_device_is_compatible(np, "aspeed,ast2500-kcs-bmc"))
 		kcs_bmc = aspeed_kcs_probe_of_v1(pdev);
 	else if (of_device_is_compatible(np, "aspeed,ast2400-kcs-bmc-v2") ||
-			of_device_is_compatible(np, "aspeed,ast2500-kcs-bmc-v2"))
+		 of_device_is_compatible(np, "aspeed,ast2500-kcs-bmc-v2"))
 		kcs_bmc = aspeed_kcs_probe_of_v2(pdev);
 	else
 		return -EINVAL;
-- 
2.27.0


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

* [PATCH v2 04/21] pinctrl: aspeed-g5: Adapt to new LPC device tree layout
  2021-03-19  6:27 [PATCH v2 01/21] dt-bindings: aspeed-lpc: Remove LPC partitioning Andrew Jeffery
  2021-03-19  6:27 ` [PATCH v2 02/21] ARM: dts: Remove LPC BMC and Host partitions Andrew Jeffery
  2021-03-19  6:27 ` [PATCH v2 03/21] ipmi: kcs: aspeed: Adapt to new LPC DTS layout Andrew Jeffery
@ 2021-03-19  6:27 ` Andrew Jeffery
  2021-04-09  3:36   ` Joel Stanley
  2021-03-19  6:27 ` [PATCH v2 05/21] soc: aspeed: " Andrew Jeffery
                   ` (17 subsequent siblings)
  20 siblings, 1 reply; 67+ messages in thread
From: Andrew Jeffery @ 2021-03-19  6:27 UTC (permalink / raw)
  To: openipmi-developer, openbmc, minyard
  Cc: devicetree, ryan_chen, tmaimon77, linux-aspeed, avifishman70,
	venture, Chia-Wei, Wang, linux-kernel, tali.perry1, linux-gpio,
	robh+dt, lee.jones, linus.walleij, linux-arm-kernel,
	benjaminfair

From: "Chia-Wei, Wang" <chiawei_wang@aspeedtech.com>

Add check against LPC device v2 compatible string to
ensure that the fixed device tree layout is adopted.
The LPC register offsets are also fixed accordingly.

Signed-off-by: Chia-Wei Wang <chiawei_wang@aspeedtech.com>
Reviewed-by: Andrew Jeffery <andrew@aj.id.au>
Acked-by: Linus Walleij <linus.walleij@linaro.org>
---
 drivers/pinctrl/aspeed/pinctrl-aspeed-g5.c | 17 +++++++++++------
 1 file changed, 11 insertions(+), 6 deletions(-)

diff --git a/drivers/pinctrl/aspeed/pinctrl-aspeed-g5.c b/drivers/pinctrl/aspeed/pinctrl-aspeed-g5.c
index 0cab4c2576e2..996ebcba4d38 100644
--- a/drivers/pinctrl/aspeed/pinctrl-aspeed-g5.c
+++ b/drivers/pinctrl/aspeed/pinctrl-aspeed-g5.c
@@ -60,7 +60,7 @@
 #define COND2		{ ASPEED_IP_SCU, SCU94, GENMASK(1, 0), 0, 0 }
 
 /* LHCR0 is offset from the end of the H8S/2168-compatible registers */
-#define LHCR0		0x20
+#define LHCR0		0xa0
 #define GFX064		0x64
 
 #define B14 0
@@ -2648,14 +2648,19 @@ static struct regmap *aspeed_g5_acquire_regmap(struct aspeed_pinmux_data *ctx,
 	}
 
 	if (ip == ASPEED_IP_LPC) {
-		struct device_node *node;
+		struct device_node *np;
 		struct regmap *map;
 
-		node = of_parse_phandle(ctx->dev->of_node,
+		np = of_parse_phandle(ctx->dev->of_node,
 					"aspeed,external-nodes", 1);
-		if (node) {
-			map = syscon_node_to_regmap(node->parent);
-			of_node_put(node);
+		if (np) {
+			if (!of_device_is_compatible(np->parent, "aspeed,ast2400-lpc-v2") &&
+			    !of_device_is_compatible(np->parent, "aspeed,ast2500-lpc-v2") &&
+			    !of_device_is_compatible(np->parent, "aspeed,ast2600-lpc-v2"))
+				return ERR_PTR(-ENODEV);
+
+			map = syscon_node_to_regmap(np->parent);
+			of_node_put(np);
 			if (IS_ERR(map))
 				return map;
 		} else
-- 
2.27.0


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

* [PATCH v2 05/21] soc: aspeed: Adapt to new LPC device tree layout
  2021-03-19  6:27 [PATCH v2 01/21] dt-bindings: aspeed-lpc: Remove LPC partitioning Andrew Jeffery
                   ` (2 preceding siblings ...)
  2021-03-19  6:27 ` [PATCH v2 04/21] pinctrl: aspeed-g5: Adapt to new LPC device tree layout Andrew Jeffery
@ 2021-03-19  6:27 ` Andrew Jeffery
  2021-04-09  3:38   ` Joel Stanley
  2021-03-19  6:27 ` [PATCH v2 06/21] ipmi: kcs_bmc_aspeed: Use of match data to extract KCS properties Andrew Jeffery
                   ` (16 subsequent siblings)
  20 siblings, 1 reply; 67+ messages in thread
From: Andrew Jeffery @ 2021-03-19  6:27 UTC (permalink / raw)
  To: openipmi-developer, openbmc, minyard
  Cc: devicetree, ryan_chen, tmaimon77, linux-aspeed, avifishman70,
	venture, Chia-Wei, Wang, linux-kernel, tali.perry1, linux-gpio,
	robh+dt, lee.jones, linus.walleij, linux-arm-kernel,
	benjaminfair

From: "Chia-Wei, Wang" <chiawei_wang@aspeedtech.com>

Add check against LPC device v2 compatible string to
ensure that the fixed device tree layout is adopted.
The LPC register offsets are also fixed accordingly.

Signed-off-by: Chia-Wei Wang <chiawei_wang@aspeedtech.com>
Reviewed-by: Andrew Jeffery <andrew@aj.id.au>
---
 drivers/soc/aspeed/aspeed-lpc-ctrl.c  | 20 ++++++++++++++------
 drivers/soc/aspeed/aspeed-lpc-snoop.c | 23 +++++++++++++++--------
 2 files changed, 29 insertions(+), 14 deletions(-)

diff --git a/drivers/soc/aspeed/aspeed-lpc-ctrl.c b/drivers/soc/aspeed/aspeed-lpc-ctrl.c
index 439bcd6b8c4a..c557ffd0992c 100644
--- a/drivers/soc/aspeed/aspeed-lpc-ctrl.c
+++ b/drivers/soc/aspeed/aspeed-lpc-ctrl.c
@@ -18,15 +18,15 @@
 
 #define DEVICE_NAME	"aspeed-lpc-ctrl"
 
-#define HICR5 0x0
+#define HICR5 0x80
 #define HICR5_ENL2H	BIT(8)
 #define HICR5_ENFWH	BIT(10)
 
-#define HICR6 0x4
+#define HICR6 0x84
 #define SW_FWH2AHB	BIT(17)
 
-#define HICR7 0x8
-#define HICR8 0xc
+#define HICR7 0x88
+#define HICR8 0x8c
 
 struct aspeed_lpc_ctrl {
 	struct miscdevice	miscdev;
@@ -215,6 +215,7 @@ static int aspeed_lpc_ctrl_probe(struct platform_device *pdev)
 	struct device_node *node;
 	struct resource resm;
 	struct device *dev;
+	struct device_node *np;
 	int rc;
 
 	dev = &pdev->dev;
@@ -270,8 +271,15 @@ static int aspeed_lpc_ctrl_probe(struct platform_device *pdev)
 		}
 	}
 
-	lpc_ctrl->regmap = syscon_node_to_regmap(
-			pdev->dev.parent->of_node);
+	np = pdev->dev.parent->of_node;
+	if (!of_device_is_compatible(np, "aspeed,ast2400-lpc-v2") &&
+	    !of_device_is_compatible(np, "aspeed,ast2500-lpc-v2") &&
+	    !of_device_is_compatible(np, "aspeed,ast2600-lpc-v2")) {
+		dev_err(dev, "unsupported LPC device binding\n");
+		return -ENODEV;
+	}
+
+	lpc_ctrl->regmap = syscon_node_to_regmap(np);
 	if (IS_ERR(lpc_ctrl->regmap)) {
 		dev_err(dev, "Couldn't get regmap\n");
 		return -ENODEV;
diff --git a/drivers/soc/aspeed/aspeed-lpc-snoop.c b/drivers/soc/aspeed/aspeed-lpc-snoop.c
index 20acac6342ef..210455efb321 100644
--- a/drivers/soc/aspeed/aspeed-lpc-snoop.c
+++ b/drivers/soc/aspeed/aspeed-lpc-snoop.c
@@ -29,26 +29,25 @@
 #define NUM_SNOOP_CHANNELS 2
 #define SNOOP_FIFO_SIZE 2048
 
-#define HICR5	0x0
+#define HICR5	0x80
 #define HICR5_EN_SNP0W		BIT(0)
 #define HICR5_ENINT_SNP0W	BIT(1)
 #define HICR5_EN_SNP1W		BIT(2)
 #define HICR5_ENINT_SNP1W	BIT(3)
-
-#define HICR6	0x4
+#define HICR6	0x84
 #define HICR6_STR_SNP0W		BIT(0)
 #define HICR6_STR_SNP1W		BIT(1)
-#define SNPWADR	0x10
+#define SNPWADR	0x90
 #define SNPWADR_CH0_MASK	GENMASK(15, 0)
 #define SNPWADR_CH0_SHIFT	0
 #define SNPWADR_CH1_MASK	GENMASK(31, 16)
 #define SNPWADR_CH1_SHIFT	16
-#define SNPWDR	0x14
+#define SNPWDR	0x94
 #define SNPWDR_CH0_MASK		GENMASK(7, 0)
 #define SNPWDR_CH0_SHIFT	0
 #define SNPWDR_CH1_MASK		GENMASK(15, 8)
 #define SNPWDR_CH1_SHIFT	8
-#define HICRB	0x80
+#define HICRB	0x100
 #define HICRB_ENSNP0D		BIT(14)
 #define HICRB_ENSNP1D		BIT(15)
 
@@ -260,6 +259,7 @@ static int aspeed_lpc_snoop_probe(struct platform_device *pdev)
 {
 	struct aspeed_lpc_snoop *lpc_snoop;
 	struct device *dev;
+	struct device_node *np;
 	u32 port;
 	int rc;
 
@@ -269,8 +269,15 @@ static int aspeed_lpc_snoop_probe(struct platform_device *pdev)
 	if (!lpc_snoop)
 		return -ENOMEM;
 
-	lpc_snoop->regmap = syscon_node_to_regmap(
-			pdev->dev.parent->of_node);
+	np = pdev->dev.parent->of_node;
+	if (!of_device_is_compatible(np, "aspeed,ast2400-lpc-v2") &&
+	    !of_device_is_compatible(np, "aspeed,ast2500-lpc-v2") &&
+	    !of_device_is_compatible(np, "aspeed,ast2600-lpc-v2")) {
+		dev_err(dev, "unsupported LPC device binding\n");
+		return -ENODEV;
+	}
+
+	lpc_snoop->regmap = syscon_node_to_regmap(np);
 	if (IS_ERR(lpc_snoop->regmap)) {
 		dev_err(dev, "Couldn't get regmap\n");
 		return -ENODEV;
-- 
2.27.0


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

* [PATCH v2 06/21] ipmi: kcs_bmc_aspeed: Use of match data to extract KCS properties
  2021-03-19  6:27 [PATCH v2 01/21] dt-bindings: aspeed-lpc: Remove LPC partitioning Andrew Jeffery
                   ` (3 preceding siblings ...)
  2021-03-19  6:27 ` [PATCH v2 05/21] soc: aspeed: " Andrew Jeffery
@ 2021-03-19  6:27 ` Andrew Jeffery
  2021-04-06  6:07   ` ChiaWei Wang
  2021-04-09  3:24   ` Zev Weiss
  2021-03-19  6:27 ` [PATCH v2 07/21] ipmi: kcs_bmc: Make status update atomic Andrew Jeffery
                   ` (15 subsequent siblings)
  20 siblings, 2 replies; 67+ messages in thread
From: Andrew Jeffery @ 2021-03-19  6:27 UTC (permalink / raw)
  To: openipmi-developer, openbmc, minyard
  Cc: devicetree, ryan_chen, tmaimon77, linux-aspeed, avifishman70,
	venture, linus.walleij, linux-kernel, tali.perry1, linux-gpio,
	robh+dt, lee.jones, chiawei_wang, linux-arm-kernel, benjaminfair

Unpack and remove the aspeed_kcs_probe_of_v[12]() functions to aid
rearranging how the private device-driver memory is allocated.

Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
---
 drivers/char/ipmi/kcs_bmc_aspeed.c | 146 ++++++++++++++---------------
 1 file changed, 68 insertions(+), 78 deletions(-)

diff --git a/drivers/char/ipmi/kcs_bmc_aspeed.c b/drivers/char/ipmi/kcs_bmc_aspeed.c
index eefe362f65f0..061f53676206 100644
--- a/drivers/char/ipmi/kcs_bmc_aspeed.c
+++ b/drivers/char/ipmi/kcs_bmc_aspeed.c
@@ -13,6 +13,7 @@
 #include <linux/module.h>
 #include <linux/of.h>
 #include <linux/of_address.h>
+#include <linux/of_device.h>
 #include <linux/platform_device.h>
 #include <linux/poll.h>
 #include <linux/regmap.h>
@@ -63,6 +64,10 @@ struct aspeed_kcs_bmc {
 	struct regmap *map;
 };
 
+struct aspeed_kcs_of_ops {
+	int (*get_channel)(struct platform_device *pdev);
+	int (*get_io_address)(struct platform_device *pdev);
+};
 
 static u8 aspeed_kcs_inb(struct kcs_bmc *kcs_bmc, u32 reg)
 {
@@ -231,13 +236,10 @@ static const struct kcs_ioreg ast_kcs_bmc_ioregs[KCS_CHANNEL_MAX] = {
 	{ .idr = LPC_IDR4, .odr = LPC_ODR4, .str = LPC_STR4 },
 };
 
-static struct kcs_bmc *aspeed_kcs_probe_of_v1(struct platform_device *pdev)
+static int aspeed_kcs_of_v1_get_channel(struct platform_device *pdev)
 {
-	struct aspeed_kcs_bmc *priv;
 	struct device_node *np;
-	struct kcs_bmc *kcs;
 	u32 channel;
-	u32 slave;
 	int rc;
 
 	np = pdev->dev.of_node;
@@ -245,105 +247,78 @@ static struct kcs_bmc *aspeed_kcs_probe_of_v1(struct platform_device *pdev)
 	rc = of_property_read_u32(np, "kcs_chan", &channel);
 	if ((rc != 0) || (channel == 0 || channel > KCS_CHANNEL_MAX)) {
 		dev_err(&pdev->dev, "no valid 'kcs_chan' configured\n");
-		return ERR_PTR(-EINVAL);
+		return -EINVAL;
 	}
 
-	kcs = kcs_bmc_alloc(&pdev->dev, sizeof(struct aspeed_kcs_bmc), channel);
-	if (!kcs)
-		return ERR_PTR(-ENOMEM);
+	return channel;
+}
 
-	priv = kcs_bmc_priv(kcs);
-	priv->map = syscon_node_to_regmap(pdev->dev.parent->of_node);
-	if (IS_ERR(priv->map)) {
-		dev_err(&pdev->dev, "Couldn't get regmap\n");
-		return ERR_PTR(-ENODEV);
-	}
+static int aspeed_kcs_of_v1_get_io_address(struct platform_device *pdev)
+{
+	u32 slave;
+	int rc;
 
-	rc = of_property_read_u32(np, "kcs_addr", &slave);
-	if (rc) {
+	rc = of_property_read_u32(pdev->dev.of_node, "kcs_addr", &slave);
+	if (rc || slave > 0xffff) {
 		dev_err(&pdev->dev, "no valid 'kcs_addr' configured\n");
-		return ERR_PTR(-EINVAL);
+		return -EINVAL;
 	}
 
-	kcs->ioreg = ast_kcs_bmc_ioregs[channel - 1];
-	aspeed_kcs_set_address(kcs, slave);
-
-	return kcs;
-}
-
-static int aspeed_kcs_calculate_channel(const struct kcs_ioreg *regs)
-{
-	int i;
-
-	for (i = 0; i < ARRAY_SIZE(ast_kcs_bmc_ioregs); i++) {
-		if (!memcmp(&ast_kcs_bmc_ioregs[i], regs, sizeof(*regs)))
-			return i + 1;
-	}
-
-	return -EINVAL;
+	return slave;
 }
 
-static struct kcs_bmc *aspeed_kcs_probe_of_v2(struct platform_device *pdev)
+static int aspeed_kcs_of_v2_get_channel(struct platform_device *pdev)
 {
-	struct aspeed_kcs_bmc *priv;
 	struct device_node *np;
 	struct kcs_ioreg ioreg;
-	struct kcs_bmc *kcs;
 	const __be32 *reg;
-	int channel;
-	u32 slave;
-	int rc;
+	int i;
 
 	np = pdev->dev.of_node;
 
 	/* Don't translate addresses, we want offsets for the regmaps */
 	reg = of_get_address(np, 0, NULL, NULL);
 	if (!reg)
-		return ERR_PTR(-EINVAL);
+		return -EINVAL;
 	ioreg.idr = be32_to_cpup(reg);
 
 	reg = of_get_address(np, 1, NULL, NULL);
 	if (!reg)
-		return ERR_PTR(-EINVAL);
+		return -EINVAL;
 	ioreg.odr = be32_to_cpup(reg);
 
 	reg = of_get_address(np, 2, NULL, NULL);
 	if (!reg)
-		return ERR_PTR(-EINVAL);
+		return -EINVAL;
 	ioreg.str = be32_to_cpup(reg);
 
-	channel = aspeed_kcs_calculate_channel(&ioreg);
-	if (channel < 0)
-		return ERR_PTR(channel);
-
-	kcs = kcs_bmc_alloc(&pdev->dev, sizeof(struct aspeed_kcs_bmc), channel);
-	if (!kcs)
-		return ERR_PTR(-ENOMEM);
-
-	kcs->ioreg = ioreg;
-
-	priv = kcs_bmc_priv(kcs);
-	priv->map = syscon_node_to_regmap(pdev->dev.parent->of_node);
-	if (IS_ERR(priv->map)) {
-		dev_err(&pdev->dev, "Couldn't get regmap\n");
-		return ERR_PTR(-ENODEV);
+	for (i = 0; i < ARRAY_SIZE(ast_kcs_bmc_ioregs); i++) {
+		if (!memcmp(&ast_kcs_bmc_ioregs[i], &ioreg, sizeof(ioreg)))
+			return i + 1;
 	}
 
-	rc = of_property_read_u32(np, "aspeed,lpc-io-reg", &slave);
-	if (rc)
-		return ERR_PTR(rc);
+	return -EINVAL;
+}
 
-	aspeed_kcs_set_address(kcs, slave);
+static int aspeed_kcs_of_v2_get_io_address(struct platform_device *pdev)
+{
+	uint32_t slave;
+	int rc;
 
-	return kcs;
+	rc = of_property_read_u32(pdev->dev.of_node, "aspeed,lpc-io-reg", &slave);
+	if (rc || slave > 0xffff)
+		return -EINVAL;
+
+	return slave;
 }
 
 static int aspeed_kcs_probe(struct platform_device *pdev)
 {
+	const struct aspeed_kcs_of_ops *ops;
 	struct device *dev = &pdev->dev;
 	struct kcs_bmc *kcs_bmc;
 	struct device_node *np;
-	int rc;
+	int rc, channel, addr;
 
 	np = dev->of_node->parent;
 	if (!of_device_is_compatible(np, "aspeed,ast2400-lpc-v2") &&
@@ -352,23 +327,28 @@ static int aspeed_kcs_probe(struct platform_device *pdev)
 		dev_err(dev, "unsupported LPC device binding\n");
 		return -ENODEV;
 	}
-
-	np = dev->of_node;
-	if (of_device_is_compatible(np, "aspeed,ast2400-kcs-bmc") ||
-	    of_device_is_compatible(np, "aspeed,ast2500-kcs-bmc"))
-		kcs_bmc = aspeed_kcs_probe_of_v1(pdev);
-	else if (of_device_is_compatible(np, "aspeed,ast2400-kcs-bmc-v2") ||
-		 of_device_is_compatible(np, "aspeed,ast2500-kcs-bmc-v2"))
-		kcs_bmc = aspeed_kcs_probe_of_v2(pdev);
-	else
+	ops = of_device_get_match_data(&pdev->dev);
+	if (!ops)
 		return -EINVAL;
 
-	if (IS_ERR(kcs_bmc))
-		return PTR_ERR(kcs_bmc);
+	channel = ops->get_channel(pdev);
+	if (channel < 0)
+		return channel;
 
+	kcs_bmc = kcs_bmc_alloc(&pdev->dev, sizeof(struct aspeed_kcs_bmc), channel);
+	if (!kcs_bmc)
+		return -ENOMEM;
+
+	kcs_bmc->ioreg = ast_kcs_bmc_ioregs[channel - 1];
 	kcs_bmc->io_inputb = aspeed_kcs_inb;
 	kcs_bmc->io_outputb = aspeed_kcs_outb;
 
+	addr = ops->get_io_address(pdev);
+	if (addr < 0)
+		return addr;
+
+	aspeed_kcs_set_address(kcs_bmc, addr);
+
 	rc = aspeed_kcs_config_irq(kcs_bmc, pdev);
 	if (rc)
 		return rc;
@@ -400,11 +380,21 @@ static int aspeed_kcs_remove(struct platform_device *pdev)
 	return 0;
 }
 
+static const struct aspeed_kcs_of_ops of_v1_ops = {
+	.get_channel = aspeed_kcs_of_v1_get_channel,
+	.get_io_address = aspeed_kcs_of_v1_get_io_address,
+};
+
+static const struct aspeed_kcs_of_ops of_v2_ops = {
+	.get_channel = aspeed_kcs_of_v2_get_channel,
+	.get_io_address = aspeed_kcs_of_v2_get_io_address,
+};
+
 static const struct of_device_id ast_kcs_bmc_match[] = {
-	{ .compatible = "aspeed,ast2400-kcs-bmc" },
-	{ .compatible = "aspeed,ast2500-kcs-bmc" },
-	{ .compatible = "aspeed,ast2400-kcs-bmc-v2" },
-	{ .compatible = "aspeed,ast2500-kcs-bmc-v2" },
+	{ .compatible = "aspeed,ast2400-kcs-bmc", .data = &of_v1_ops },
+	{ .compatible = "aspeed,ast2500-kcs-bmc", .data = &of_v1_ops },
+	{ .compatible = "aspeed,ast2400-kcs-bmc-v2", .data = &of_v2_ops },
+	{ .compatible = "aspeed,ast2500-kcs-bmc-v2", .data = &of_v2_ops },
 	{ }
 };
 MODULE_DEVICE_TABLE(of, ast_kcs_bmc_match);
-- 
2.27.0


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

* [PATCH v2 07/21] ipmi: kcs_bmc: Make status update atomic
  2021-03-19  6:27 [PATCH v2 01/21] dt-bindings: aspeed-lpc: Remove LPC partitioning Andrew Jeffery
                   ` (4 preceding siblings ...)
  2021-03-19  6:27 ` [PATCH v2 06/21] ipmi: kcs_bmc_aspeed: Use of match data to extract KCS properties Andrew Jeffery
@ 2021-03-19  6:27 ` Andrew Jeffery
  2021-04-09  5:32   ` Zev Weiss
  2021-03-19  6:27 ` [PATCH v2 08/21] ipmi: kcs_bmc: Rename {read, write}_{status, data}() functions Andrew Jeffery
                   ` (14 subsequent siblings)
  20 siblings, 1 reply; 67+ messages in thread
From: Andrew Jeffery @ 2021-03-19  6:27 UTC (permalink / raw)
  To: openipmi-developer, openbmc, minyard
  Cc: devicetree, ryan_chen, tmaimon77, linux-aspeed, avifishman70,
	venture, linus.walleij, linux-kernel, tali.perry1, linux-gpio,
	robh+dt, lee.jones, chiawei_wang, linux-arm-kernel, benjaminfair

Enable more efficient implementation of read-modify-write sequences.
Both device drivers for the KCS BMC stack use regmaps. The new callback
allows us to exploit regmap_update_bits().

Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
---
 drivers/char/ipmi/kcs_bmc.c         |  7 +------
 drivers/char/ipmi/kcs_bmc.h         |  1 +
 drivers/char/ipmi/kcs_bmc_aspeed.c  |  9 +++++++++
 drivers/char/ipmi/kcs_bmc_npcm7xx.c | 10 ++++++++++
 4 files changed, 21 insertions(+), 6 deletions(-)

diff --git a/drivers/char/ipmi/kcs_bmc.c b/drivers/char/ipmi/kcs_bmc.c
index f292e74bd4a5..58fb1a7bd50d 100644
--- a/drivers/char/ipmi/kcs_bmc.c
+++ b/drivers/char/ipmi/kcs_bmc.c
@@ -67,12 +67,7 @@ static inline void write_status(struct kcs_bmc *kcs_bmc, u8 data)
 
 static void update_status_bits(struct kcs_bmc *kcs_bmc, u8 mask, u8 val)
 {
-	u8 tmp = read_status(kcs_bmc);
-
-	tmp &= ~mask;
-	tmp |= val & mask;
-
-	write_status(kcs_bmc, tmp);
+	kcs_bmc->io_updateb(kcs_bmc, kcs_bmc->ioreg.str, mask, val);
 }
 
 static inline void set_state(struct kcs_bmc *kcs_bmc, u8 state)
diff --git a/drivers/char/ipmi/kcs_bmc.h b/drivers/char/ipmi/kcs_bmc.h
index eb9ea4ce78b8..970f53892f2d 100644
--- a/drivers/char/ipmi/kcs_bmc.h
+++ b/drivers/char/ipmi/kcs_bmc.h
@@ -76,6 +76,7 @@ struct kcs_bmc {
 	struct kcs_ioreg ioreg;
 	u8 (*io_inputb)(struct kcs_bmc *kcs_bmc, u32 reg);
 	void (*io_outputb)(struct kcs_bmc *kcs_bmc, u32 reg, u8 b);
+	void (*io_updateb)(struct kcs_bmc *kcs_bmc, u32 reg, u8 mask, u8 val);
 
 	enum kcs_phases phase;
 	enum kcs_errors error;
diff --git a/drivers/char/ipmi/kcs_bmc_aspeed.c b/drivers/char/ipmi/kcs_bmc_aspeed.c
index 061f53676206..630cf095560e 100644
--- a/drivers/char/ipmi/kcs_bmc_aspeed.c
+++ b/drivers/char/ipmi/kcs_bmc_aspeed.c
@@ -90,6 +90,14 @@ static void aspeed_kcs_outb(struct kcs_bmc *kcs_bmc, u32 reg, u8 data)
 	WARN(rc != 0, "regmap_write() failed: %d\n", rc);
 }
 
+static void aspeed_kcs_updateb(struct kcs_bmc *kcs_bmc, u32 reg, u8 mask, u8 val)
+{
+	struct aspeed_kcs_bmc *priv = kcs_bmc_priv(kcs_bmc);
+	int rc;
+
+	rc = regmap_update_bits(priv->map, reg, mask, val);
+	WARN(rc != 0, "regmap_update_bits() failed: %d\n", rc);
+}
 
 /*
  * AST_usrGuide_KCS.pdf
@@ -342,6 +350,7 @@ static int aspeed_kcs_probe(struct platform_device *pdev)
 	kcs_bmc->ioreg = ast_kcs_bmc_ioregs[channel - 1];
 	kcs_bmc->io_inputb = aspeed_kcs_inb;
 	kcs_bmc->io_outputb = aspeed_kcs_outb;
+	kcs_bmc->io_updateb = aspeed_kcs_updateb;
 
 	addr = ops->get_io_address(pdev);
 	if (addr < 0)
diff --git a/drivers/char/ipmi/kcs_bmc_npcm7xx.c b/drivers/char/ipmi/kcs_bmc_npcm7xx.c
index 722f7391fe1f..1f44aadec9e8 100644
--- a/drivers/char/ipmi/kcs_bmc_npcm7xx.c
+++ b/drivers/char/ipmi/kcs_bmc_npcm7xx.c
@@ -97,6 +97,15 @@ static void npcm7xx_kcs_outb(struct kcs_bmc *kcs_bmc, u32 reg, u8 data)
 	WARN(rc != 0, "regmap_write() failed: %d\n", rc);
 }
 
+static void npcm7xx_kcs_updateb(struct kcs_bmc *kcs_bmc, u32 reg, u8 mask, u8 data)
+{
+	struct npcm7xx_kcs_bmc *priv = kcs_bmc_priv(kcs_bmc);
+	int rc;
+
+	rc = regmap_update_bits(priv->map, reg, mask, data);
+	WARN(rc != 0, "regmap_update_bits() failed: %d\n", rc);
+}
+
 static void npcm7xx_kcs_enable_channel(struct kcs_bmc *kcs_bmc, bool enable)
 {
 	struct npcm7xx_kcs_bmc *priv = kcs_bmc_priv(kcs_bmc);
@@ -163,6 +172,7 @@ static int npcm7xx_kcs_probe(struct platform_device *pdev)
 	kcs_bmc->ioreg.str = priv->reg->sts;
 	kcs_bmc->io_inputb = npcm7xx_kcs_inb;
 	kcs_bmc->io_outputb = npcm7xx_kcs_outb;
+	kcs_bmc->io_updateb = npcm7xx_kcs_updateb;
 
 	dev_set_drvdata(dev, kcs_bmc);
 
-- 
2.27.0


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

* [PATCH v2 08/21] ipmi: kcs_bmc: Rename {read, write}_{status, data}() functions
  2021-03-19  6:27 [PATCH v2 01/21] dt-bindings: aspeed-lpc: Remove LPC partitioning Andrew Jeffery
                   ` (5 preceding siblings ...)
  2021-03-19  6:27 ` [PATCH v2 07/21] ipmi: kcs_bmc: Make status update atomic Andrew Jeffery
@ 2021-03-19  6:27 ` Andrew Jeffery
  2021-04-09  5:33   ` Zev Weiss
  2021-03-19  6:27 ` [PATCH v2 09/21] ipmi: kcs_bmc: Split out kcs_bmc_cdev_ipmi Andrew Jeffery
                   ` (13 subsequent siblings)
  20 siblings, 1 reply; 67+ messages in thread
From: Andrew Jeffery @ 2021-03-19  6:27 UTC (permalink / raw)
  To: openipmi-developer, openbmc, minyard
  Cc: devicetree, ryan_chen, tmaimon77, linux-aspeed, avifishman70,
	venture, linus.walleij, linux-kernel, tali.perry1, linux-gpio,
	robh+dt, lee.jones, chiawei_wang, linux-arm-kernel, benjaminfair

Rename the functions in preparation for separating the IPMI chardev out
from the KCS BMC core.

Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
---
 drivers/char/ipmi/kcs_bmc.c | 52 ++++++++++++++++++-------------------
 1 file changed, 25 insertions(+), 27 deletions(-)

diff --git a/drivers/char/ipmi/kcs_bmc.c b/drivers/char/ipmi/kcs_bmc.c
index 58fb1a7bd50d..c4336c1f2d6d 100644
--- a/drivers/char/ipmi/kcs_bmc.c
+++ b/drivers/char/ipmi/kcs_bmc.c
@@ -45,42 +45,42 @@ enum kcs_states {
 #define KCS_CMD_WRITE_END         0x62
 #define KCS_CMD_READ_BYTE         0x68
 
-static inline u8 read_data(struct kcs_bmc *kcs_bmc)
+static inline u8 kcs_bmc_read_data(struct kcs_bmc *kcs_bmc)
 {
 	return kcs_bmc->io_inputb(kcs_bmc, kcs_bmc->ioreg.idr);
 }
 
-static inline void write_data(struct kcs_bmc *kcs_bmc, u8 data)
+static inline void kcs_bmc_write_data(struct kcs_bmc *kcs_bmc, u8 data)
 {
 	kcs_bmc->io_outputb(kcs_bmc, kcs_bmc->ioreg.odr, data);
 }
 
-static inline u8 read_status(struct kcs_bmc *kcs_bmc)
+static inline u8 kcs_bmc_read_status(struct kcs_bmc *kcs_bmc)
 {
 	return kcs_bmc->io_inputb(kcs_bmc, kcs_bmc->ioreg.str);
 }
 
-static inline void write_status(struct kcs_bmc *kcs_bmc, u8 data)
+static inline void kcs_bmc_write_status(struct kcs_bmc *kcs_bmc, u8 data)
 {
 	kcs_bmc->io_outputb(kcs_bmc, kcs_bmc->ioreg.str, data);
 }
 
-static void update_status_bits(struct kcs_bmc *kcs_bmc, u8 mask, u8 val)
+static void kcs_bmc_update_status(struct kcs_bmc *kcs_bmc, u8 mask, u8 val)
 {
 	kcs_bmc->io_updateb(kcs_bmc, kcs_bmc->ioreg.str, mask, val);
 }
 
 static inline void set_state(struct kcs_bmc *kcs_bmc, u8 state)
 {
-	update_status_bits(kcs_bmc, KCS_STATUS_STATE_MASK,
+	kcs_bmc_update_status(kcs_bmc, KCS_STATUS_STATE_MASK,
 					KCS_STATUS_STATE(state));
 }
 
 static void kcs_force_abort(struct kcs_bmc *kcs_bmc)
 {
 	set_state(kcs_bmc, ERROR_STATE);
-	read_data(kcs_bmc);
-	write_data(kcs_bmc, KCS_ZERO_DATA);
+	kcs_bmc_read_data(kcs_bmc);
+	kcs_bmc_write_data(kcs_bmc, KCS_ZERO_DATA);
 
 	kcs_bmc->phase = KCS_PHASE_ERROR;
 	kcs_bmc->data_in_avail = false;
@@ -99,9 +99,9 @@ static void kcs_bmc_handle_data(struct kcs_bmc *kcs_bmc)
 	case KCS_PHASE_WRITE_DATA:
 		if (kcs_bmc->data_in_idx < KCS_MSG_BUFSIZ) {
 			set_state(kcs_bmc, WRITE_STATE);
-			write_data(kcs_bmc, KCS_ZERO_DATA);
+			kcs_bmc_write_data(kcs_bmc, KCS_ZERO_DATA);
 			kcs_bmc->data_in[kcs_bmc->data_in_idx++] =
-						read_data(kcs_bmc);
+						kcs_bmc_read_data(kcs_bmc);
 		} else {
 			kcs_force_abort(kcs_bmc);
 			kcs_bmc->error = KCS_LENGTH_ERROR;
@@ -112,7 +112,7 @@ static void kcs_bmc_handle_data(struct kcs_bmc *kcs_bmc)
 		if (kcs_bmc->data_in_idx < KCS_MSG_BUFSIZ) {
 			set_state(kcs_bmc, READ_STATE);
 			kcs_bmc->data_in[kcs_bmc->data_in_idx++] =
-						read_data(kcs_bmc);
+						kcs_bmc_read_data(kcs_bmc);
 			kcs_bmc->phase = KCS_PHASE_WRITE_DONE;
 			kcs_bmc->data_in_avail = true;
 			wake_up_interruptible(&kcs_bmc->queue);
@@ -126,34 +126,34 @@ static void kcs_bmc_handle_data(struct kcs_bmc *kcs_bmc)
 		if (kcs_bmc->data_out_idx == kcs_bmc->data_out_len)
 			set_state(kcs_bmc, IDLE_STATE);
 
-		data = read_data(kcs_bmc);
+		data = kcs_bmc_read_data(kcs_bmc);
 		if (data != KCS_CMD_READ_BYTE) {
 			set_state(kcs_bmc, ERROR_STATE);
-			write_data(kcs_bmc, KCS_ZERO_DATA);
+			kcs_bmc_write_data(kcs_bmc, KCS_ZERO_DATA);
 			break;
 		}
 
 		if (kcs_bmc->data_out_idx == kcs_bmc->data_out_len) {
-			write_data(kcs_bmc, KCS_ZERO_DATA);
+			kcs_bmc_write_data(kcs_bmc, KCS_ZERO_DATA);
 			kcs_bmc->phase = KCS_PHASE_IDLE;
 			break;
 		}
 
-		write_data(kcs_bmc,
+		kcs_bmc_write_data(kcs_bmc,
 			kcs_bmc->data_out[kcs_bmc->data_out_idx++]);
 		break;
 
 	case KCS_PHASE_ABORT_ERROR1:
 		set_state(kcs_bmc, READ_STATE);
-		read_data(kcs_bmc);
-		write_data(kcs_bmc, kcs_bmc->error);
+		kcs_bmc_read_data(kcs_bmc);
+		kcs_bmc_write_data(kcs_bmc, kcs_bmc->error);
 		kcs_bmc->phase = KCS_PHASE_ABORT_ERROR2;
 		break;
 
 	case KCS_PHASE_ABORT_ERROR2:
 		set_state(kcs_bmc, IDLE_STATE);
-		read_data(kcs_bmc);
-		write_data(kcs_bmc, KCS_ZERO_DATA);
+		kcs_bmc_read_data(kcs_bmc);
+		kcs_bmc_write_data(kcs_bmc, KCS_ZERO_DATA);
 		kcs_bmc->phase = KCS_PHASE_IDLE;
 		break;
 
@@ -168,9 +168,9 @@ static void kcs_bmc_handle_cmd(struct kcs_bmc *kcs_bmc)
 	u8 cmd;
 
 	set_state(kcs_bmc, WRITE_STATE);
-	write_data(kcs_bmc, KCS_ZERO_DATA);
+	kcs_bmc_write_data(kcs_bmc, KCS_ZERO_DATA);
 
-	cmd = read_data(kcs_bmc);
+	cmd = kcs_bmc_read_data(kcs_bmc);
 	switch (cmd) {
 	case KCS_CMD_WRITE_START:
 		kcs_bmc->phase = KCS_PHASE_WRITE_START;
@@ -212,7 +212,7 @@ int kcs_bmc_handle_event(struct kcs_bmc *kcs_bmc)
 
 	spin_lock_irqsave(&kcs_bmc->lock, flags);
 
-	status = read_status(kcs_bmc);
+	status = kcs_bmc_read_status(kcs_bmc);
 	if (status & KCS_STATUS_IBF) {
 		if (!kcs_bmc->running)
 			kcs_force_abort(kcs_bmc);
@@ -350,7 +350,7 @@ static ssize_t kcs_bmc_write(struct file *filp, const char __user *buf,
 		kcs_bmc->data_out_idx = 1;
 		kcs_bmc->data_out_len = count;
 		memcpy(kcs_bmc->data_out, kcs_bmc->kbuffer, count);
-		write_data(kcs_bmc, kcs_bmc->data_out[0]);
+		kcs_bmc_write_data(kcs_bmc, kcs_bmc->data_out[0]);
 		ret = count;
 	} else {
 		ret = -EINVAL;
@@ -373,13 +373,11 @@ static long kcs_bmc_ioctl(struct file *filp, unsigned int cmd,
 
 	switch (cmd) {
 	case IPMI_BMC_IOCTL_SET_SMS_ATN:
-		update_status_bits(kcs_bmc, KCS_STATUS_SMS_ATN,
-				   KCS_STATUS_SMS_ATN);
+		kcs_bmc_update_status(kcs_bmc, KCS_STATUS_SMS_ATN, KCS_STATUS_SMS_ATN);
 		break;
 
 	case IPMI_BMC_IOCTL_CLEAR_SMS_ATN:
-		update_status_bits(kcs_bmc, KCS_STATUS_SMS_ATN,
-				   0);
+		kcs_bmc_update_status(kcs_bmc, KCS_STATUS_SMS_ATN, 0);
 		break;
 
 	case IPMI_BMC_IOCTL_FORCE_ABORT:
-- 
2.27.0


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

* [PATCH v2 09/21] ipmi: kcs_bmc: Split out kcs_bmc_cdev_ipmi
  2021-03-19  6:27 [PATCH v2 01/21] dt-bindings: aspeed-lpc: Remove LPC partitioning Andrew Jeffery
                   ` (6 preceding siblings ...)
  2021-03-19  6:27 ` [PATCH v2 08/21] ipmi: kcs_bmc: Rename {read, write}_{status, data}() functions Andrew Jeffery
@ 2021-03-19  6:27 ` Andrew Jeffery
  2021-04-09  3:56   ` Zev Weiss
  2021-03-19  6:27 ` [PATCH v2 10/21] ipmi: kcs_bmc: Turn the driver data-structures inside-out Andrew Jeffery
                   ` (12 subsequent siblings)
  20 siblings, 1 reply; 67+ messages in thread
From: Andrew Jeffery @ 2021-03-19  6:27 UTC (permalink / raw)
  To: openipmi-developer, openbmc, minyard
  Cc: devicetree, ryan_chen, tmaimon77, linux-aspeed, avifishman70,
	venture, linus.walleij, linux-kernel, tali.perry1, linux-gpio,
	robh+dt, lee.jones, chiawei_wang, linux-arm-kernel, benjaminfair

Take steps towards defining a coherent API to separate the KCS device
drivers from the userspace interface. Decreasing the coupling will
improve the separation of concerns and enable the introduction of
alternative userspace interfaces.

For now, simply split the chardev logic out to a separate file. The code
continues to build into the same module.

Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
---
 drivers/char/ipmi/Makefile            |   2 +-
 drivers/char/ipmi/kcs_bmc.c           | 423 +------------------------
 drivers/char/ipmi/kcs_bmc.h           |  10 +-
 drivers/char/ipmi/kcs_bmc_cdev_ipmi.c | 428 ++++++++++++++++++++++++++
 4 files changed, 451 insertions(+), 412 deletions(-)
 create mode 100644 drivers/char/ipmi/kcs_bmc_cdev_ipmi.c

diff --git a/drivers/char/ipmi/Makefile b/drivers/char/ipmi/Makefile
index 0822adc2ec41..a302bc865370 100644
--- a/drivers/char/ipmi/Makefile
+++ b/drivers/char/ipmi/Makefile
@@ -22,7 +22,7 @@ obj-$(CONFIG_IPMI_SSIF) += ipmi_ssif.o
 obj-$(CONFIG_IPMI_POWERNV) += ipmi_powernv.o
 obj-$(CONFIG_IPMI_WATCHDOG) += ipmi_watchdog.o
 obj-$(CONFIG_IPMI_POWEROFF) += ipmi_poweroff.o
-obj-$(CONFIG_IPMI_KCS_BMC) += kcs_bmc.o
+obj-$(CONFIG_IPMI_KCS_BMC) += kcs_bmc.o kcs_bmc_cdev_ipmi.o
 obj-$(CONFIG_ASPEED_BT_IPMI_BMC) += bt-bmc.o
 obj-$(CONFIG_ASPEED_KCS_IPMI_BMC) += kcs_bmc_aspeed.o
 obj-$(CONFIG_NPCM7XX_KCS_IPMI_BMC) += kcs_bmc_npcm7xx.o
diff --git a/drivers/char/ipmi/kcs_bmc.c b/drivers/char/ipmi/kcs_bmc.c
index c4336c1f2d6d..ef5c48ffe74a 100644
--- a/drivers/char/ipmi/kcs_bmc.c
+++ b/drivers/char/ipmi/kcs_bmc.c
@@ -3,446 +3,51 @@
  * Copyright (c) 2015-2018, Intel Corporation.
  */
 
-#define pr_fmt(fmt) "kcs-bmc: " fmt
-
-#include <linux/errno.h>
-#include <linux/io.h>
-#include <linux/ipmi_bmc.h>
 #include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/poll.h>
-#include <linux/sched.h>
-#include <linux/slab.h>
 
 #include "kcs_bmc.h"
 
-#define DEVICE_NAME "ipmi-kcs"
-
-#define KCS_MSG_BUFSIZ    1000
-
-#define KCS_ZERO_DATA     0
-
-
-/* IPMI 2.0 - Table 9-1, KCS Interface Status Register Bits */
-#define KCS_STATUS_STATE(state) (state << 6)
-#define KCS_STATUS_STATE_MASK   GENMASK(7, 6)
-#define KCS_STATUS_CMD_DAT      BIT(3)
-#define KCS_STATUS_SMS_ATN      BIT(2)
-#define KCS_STATUS_IBF          BIT(1)
-#define KCS_STATUS_OBF          BIT(0)
-
-/* IPMI 2.0 - Table 9-2, KCS Interface State Bits */
-enum kcs_states {
-	IDLE_STATE  = 0,
-	READ_STATE  = 1,
-	WRITE_STATE = 2,
-	ERROR_STATE = 3,
-};
-
-/* IPMI 2.0 - Table 9-3, KCS Interface Control Codes */
-#define KCS_CMD_GET_STATUS_ABORT  0x60
-#define KCS_CMD_WRITE_START       0x61
-#define KCS_CMD_WRITE_END         0x62
-#define KCS_CMD_READ_BYTE         0x68
-
-static inline u8 kcs_bmc_read_data(struct kcs_bmc *kcs_bmc)
+u8 kcs_bmc_read_data(struct kcs_bmc *kcs_bmc)
 {
 	return kcs_bmc->io_inputb(kcs_bmc, kcs_bmc->ioreg.idr);
 }
+EXPORT_SYMBOL(kcs_bmc_read_data);
 
-static inline void kcs_bmc_write_data(struct kcs_bmc *kcs_bmc, u8 data)
+void kcs_bmc_write_data(struct kcs_bmc *kcs_bmc, u8 data)
 {
 	kcs_bmc->io_outputb(kcs_bmc, kcs_bmc->ioreg.odr, data);
 }
+EXPORT_SYMBOL(kcs_bmc_write_data);
 
-static inline u8 kcs_bmc_read_status(struct kcs_bmc *kcs_bmc)
+u8 kcs_bmc_read_status(struct kcs_bmc *kcs_bmc)
 {
 	return kcs_bmc->io_inputb(kcs_bmc, kcs_bmc->ioreg.str);
 }
+EXPORT_SYMBOL(kcs_bmc_read_status);
 
-static inline void kcs_bmc_write_status(struct kcs_bmc *kcs_bmc, u8 data)
+void kcs_bmc_write_status(struct kcs_bmc *kcs_bmc, u8 data)
 {
 	kcs_bmc->io_outputb(kcs_bmc, kcs_bmc->ioreg.str, data);
 }
+EXPORT_SYMBOL(kcs_bmc_write_status);
 
-static void kcs_bmc_update_status(struct kcs_bmc *kcs_bmc, u8 mask, u8 val)
+void kcs_bmc_update_status(struct kcs_bmc *kcs_bmc, u8 mask, u8 val)
 {
 	kcs_bmc->io_updateb(kcs_bmc, kcs_bmc->ioreg.str, mask, val);
 }
+EXPORT_SYMBOL(kcs_bmc_update_status);
 
-static inline void set_state(struct kcs_bmc *kcs_bmc, u8 state)
-{
-	kcs_bmc_update_status(kcs_bmc, KCS_STATUS_STATE_MASK,
-					KCS_STATUS_STATE(state));
-}
-
-static void kcs_force_abort(struct kcs_bmc *kcs_bmc)
-{
-	set_state(kcs_bmc, ERROR_STATE);
-	kcs_bmc_read_data(kcs_bmc);
-	kcs_bmc_write_data(kcs_bmc, KCS_ZERO_DATA);
-
-	kcs_bmc->phase = KCS_PHASE_ERROR;
-	kcs_bmc->data_in_avail = false;
-	kcs_bmc->data_in_idx = 0;
-}
-
-static void kcs_bmc_handle_data(struct kcs_bmc *kcs_bmc)
-{
-	u8 data;
-
-	switch (kcs_bmc->phase) {
-	case KCS_PHASE_WRITE_START:
-		kcs_bmc->phase = KCS_PHASE_WRITE_DATA;
-		fallthrough;
-
-	case KCS_PHASE_WRITE_DATA:
-		if (kcs_bmc->data_in_idx < KCS_MSG_BUFSIZ) {
-			set_state(kcs_bmc, WRITE_STATE);
-			kcs_bmc_write_data(kcs_bmc, KCS_ZERO_DATA);
-			kcs_bmc->data_in[kcs_bmc->data_in_idx++] =
-						kcs_bmc_read_data(kcs_bmc);
-		} else {
-			kcs_force_abort(kcs_bmc);
-			kcs_bmc->error = KCS_LENGTH_ERROR;
-		}
-		break;
-
-	case KCS_PHASE_WRITE_END_CMD:
-		if (kcs_bmc->data_in_idx < KCS_MSG_BUFSIZ) {
-			set_state(kcs_bmc, READ_STATE);
-			kcs_bmc->data_in[kcs_bmc->data_in_idx++] =
-						kcs_bmc_read_data(kcs_bmc);
-			kcs_bmc->phase = KCS_PHASE_WRITE_DONE;
-			kcs_bmc->data_in_avail = true;
-			wake_up_interruptible(&kcs_bmc->queue);
-		} else {
-			kcs_force_abort(kcs_bmc);
-			kcs_bmc->error = KCS_LENGTH_ERROR;
-		}
-		break;
-
-	case KCS_PHASE_READ:
-		if (kcs_bmc->data_out_idx == kcs_bmc->data_out_len)
-			set_state(kcs_bmc, IDLE_STATE);
-
-		data = kcs_bmc_read_data(kcs_bmc);
-		if (data != KCS_CMD_READ_BYTE) {
-			set_state(kcs_bmc, ERROR_STATE);
-			kcs_bmc_write_data(kcs_bmc, KCS_ZERO_DATA);
-			break;
-		}
-
-		if (kcs_bmc->data_out_idx == kcs_bmc->data_out_len) {
-			kcs_bmc_write_data(kcs_bmc, KCS_ZERO_DATA);
-			kcs_bmc->phase = KCS_PHASE_IDLE;
-			break;
-		}
-
-		kcs_bmc_write_data(kcs_bmc,
-			kcs_bmc->data_out[kcs_bmc->data_out_idx++]);
-		break;
-
-	case KCS_PHASE_ABORT_ERROR1:
-		set_state(kcs_bmc, READ_STATE);
-		kcs_bmc_read_data(kcs_bmc);
-		kcs_bmc_write_data(kcs_bmc, kcs_bmc->error);
-		kcs_bmc->phase = KCS_PHASE_ABORT_ERROR2;
-		break;
-
-	case KCS_PHASE_ABORT_ERROR2:
-		set_state(kcs_bmc, IDLE_STATE);
-		kcs_bmc_read_data(kcs_bmc);
-		kcs_bmc_write_data(kcs_bmc, KCS_ZERO_DATA);
-		kcs_bmc->phase = KCS_PHASE_IDLE;
-		break;
-
-	default:
-		kcs_force_abort(kcs_bmc);
-		break;
-	}
-}
-
-static void kcs_bmc_handle_cmd(struct kcs_bmc *kcs_bmc)
-{
-	u8 cmd;
-
-	set_state(kcs_bmc, WRITE_STATE);
-	kcs_bmc_write_data(kcs_bmc, KCS_ZERO_DATA);
-
-	cmd = kcs_bmc_read_data(kcs_bmc);
-	switch (cmd) {
-	case KCS_CMD_WRITE_START:
-		kcs_bmc->phase = KCS_PHASE_WRITE_START;
-		kcs_bmc->error = KCS_NO_ERROR;
-		kcs_bmc->data_in_avail = false;
-		kcs_bmc->data_in_idx = 0;
-		break;
-
-	case KCS_CMD_WRITE_END:
-		if (kcs_bmc->phase != KCS_PHASE_WRITE_DATA) {
-			kcs_force_abort(kcs_bmc);
-			break;
-		}
-
-		kcs_bmc->phase = KCS_PHASE_WRITE_END_CMD;
-		break;
-
-	case KCS_CMD_GET_STATUS_ABORT:
-		if (kcs_bmc->error == KCS_NO_ERROR)
-			kcs_bmc->error = KCS_ABORTED_BY_COMMAND;
-
-		kcs_bmc->phase = KCS_PHASE_ABORT_ERROR1;
-		kcs_bmc->data_in_avail = false;
-		kcs_bmc->data_in_idx = 0;
-		break;
-
-	default:
-		kcs_force_abort(kcs_bmc);
-		kcs_bmc->error = KCS_ILLEGAL_CONTROL_CODE;
-		break;
-	}
-}
-
+int kcs_bmc_ipmi_event(struct kcs_bmc *kcs_bmc);
 int kcs_bmc_handle_event(struct kcs_bmc *kcs_bmc)
 {
-	unsigned long flags;
-	int ret = -ENODATA;
-	u8 status;
-
-	spin_lock_irqsave(&kcs_bmc->lock, flags);
-
-	status = kcs_bmc_read_status(kcs_bmc);
-	if (status & KCS_STATUS_IBF) {
-		if (!kcs_bmc->running)
-			kcs_force_abort(kcs_bmc);
-		else if (status & KCS_STATUS_CMD_DAT)
-			kcs_bmc_handle_cmd(kcs_bmc);
-		else
-			kcs_bmc_handle_data(kcs_bmc);
-
-		ret = 0;
-	}
-
-	spin_unlock_irqrestore(&kcs_bmc->lock, flags);
-
-	return ret;
+	return kcs_bmc_ipmi_event(kcs_bmc);
 }
 EXPORT_SYMBOL(kcs_bmc_handle_event);
 
-static inline struct kcs_bmc *to_kcs_bmc(struct file *filp)
-{
-	return container_of(filp->private_data, struct kcs_bmc, miscdev);
-}
-
-static int kcs_bmc_open(struct inode *inode, struct file *filp)
-{
-	struct kcs_bmc *kcs_bmc = to_kcs_bmc(filp);
-	int ret = 0;
-
-	spin_lock_irq(&kcs_bmc->lock);
-	if (!kcs_bmc->running)
-		kcs_bmc->running = 1;
-	else
-		ret = -EBUSY;
-	spin_unlock_irq(&kcs_bmc->lock);
-
-	return ret;
-}
-
-static __poll_t kcs_bmc_poll(struct file *filp, poll_table *wait)
-{
-	struct kcs_bmc *kcs_bmc = to_kcs_bmc(filp);
-	__poll_t mask = 0;
-
-	poll_wait(filp, &kcs_bmc->queue, wait);
-
-	spin_lock_irq(&kcs_bmc->lock);
-	if (kcs_bmc->data_in_avail)
-		mask |= EPOLLIN;
-	spin_unlock_irq(&kcs_bmc->lock);
-
-	return mask;
-}
-
-static ssize_t kcs_bmc_read(struct file *filp, char __user *buf,
-			    size_t count, loff_t *ppos)
-{
-	struct kcs_bmc *kcs_bmc = to_kcs_bmc(filp);
-	bool data_avail;
-	size_t data_len;
-	ssize_t ret;
-
-	if (!(filp->f_flags & O_NONBLOCK))
-		wait_event_interruptible(kcs_bmc->queue,
-					 kcs_bmc->data_in_avail);
-
-	mutex_lock(&kcs_bmc->mutex);
-
-	spin_lock_irq(&kcs_bmc->lock);
-	data_avail = kcs_bmc->data_in_avail;
-	if (data_avail) {
-		data_len = kcs_bmc->data_in_idx;
-		memcpy(kcs_bmc->kbuffer, kcs_bmc->data_in, data_len);
-	}
-	spin_unlock_irq(&kcs_bmc->lock);
-
-	if (!data_avail) {
-		ret = -EAGAIN;
-		goto out_unlock;
-	}
-
-	if (count < data_len) {
-		pr_err("channel=%u with too large data : %zu\n",
-			kcs_bmc->channel, data_len);
-
-		spin_lock_irq(&kcs_bmc->lock);
-		kcs_force_abort(kcs_bmc);
-		spin_unlock_irq(&kcs_bmc->lock);
-
-		ret = -EOVERFLOW;
-		goto out_unlock;
-	}
-
-	if (copy_to_user(buf, kcs_bmc->kbuffer, data_len)) {
-		ret = -EFAULT;
-		goto out_unlock;
-	}
-
-	ret = data_len;
-
-	spin_lock_irq(&kcs_bmc->lock);
-	if (kcs_bmc->phase == KCS_PHASE_WRITE_DONE) {
-		kcs_bmc->phase = KCS_PHASE_WAIT_READ;
-		kcs_bmc->data_in_avail = false;
-		kcs_bmc->data_in_idx = 0;
-	} else {
-		ret = -EAGAIN;
-	}
-	spin_unlock_irq(&kcs_bmc->lock);
-
-out_unlock:
-	mutex_unlock(&kcs_bmc->mutex);
-
-	return ret;
-}
-
-static ssize_t kcs_bmc_write(struct file *filp, const char __user *buf,
-			     size_t count, loff_t *ppos)
-{
-	struct kcs_bmc *kcs_bmc = to_kcs_bmc(filp);
-	ssize_t ret;
-
-	/* a minimum response size '3' : netfn + cmd + ccode */
-	if (count < 3 || count > KCS_MSG_BUFSIZ)
-		return -EINVAL;
-
-	mutex_lock(&kcs_bmc->mutex);
-
-	if (copy_from_user(kcs_bmc->kbuffer, buf, count)) {
-		ret = -EFAULT;
-		goto out_unlock;
-	}
-
-	spin_lock_irq(&kcs_bmc->lock);
-	if (kcs_bmc->phase == KCS_PHASE_WAIT_READ) {
-		kcs_bmc->phase = KCS_PHASE_READ;
-		kcs_bmc->data_out_idx = 1;
-		kcs_bmc->data_out_len = count;
-		memcpy(kcs_bmc->data_out, kcs_bmc->kbuffer, count);
-		kcs_bmc_write_data(kcs_bmc, kcs_bmc->data_out[0]);
-		ret = count;
-	} else {
-		ret = -EINVAL;
-	}
-	spin_unlock_irq(&kcs_bmc->lock);
-
-out_unlock:
-	mutex_unlock(&kcs_bmc->mutex);
-
-	return ret;
-}
-
-static long kcs_bmc_ioctl(struct file *filp, unsigned int cmd,
-			  unsigned long arg)
-{
-	struct kcs_bmc *kcs_bmc = to_kcs_bmc(filp);
-	long ret = 0;
-
-	spin_lock_irq(&kcs_bmc->lock);
-
-	switch (cmd) {
-	case IPMI_BMC_IOCTL_SET_SMS_ATN:
-		kcs_bmc_update_status(kcs_bmc, KCS_STATUS_SMS_ATN, KCS_STATUS_SMS_ATN);
-		break;
-
-	case IPMI_BMC_IOCTL_CLEAR_SMS_ATN:
-		kcs_bmc_update_status(kcs_bmc, KCS_STATUS_SMS_ATN, 0);
-		break;
-
-	case IPMI_BMC_IOCTL_FORCE_ABORT:
-		kcs_force_abort(kcs_bmc);
-		break;
-
-	default:
-		ret = -EINVAL;
-		break;
-	}
-
-	spin_unlock_irq(&kcs_bmc->lock);
-
-	return ret;
-}
-
-static int kcs_bmc_release(struct inode *inode, struct file *filp)
-{
-	struct kcs_bmc *kcs_bmc = to_kcs_bmc(filp);
-
-	spin_lock_irq(&kcs_bmc->lock);
-	kcs_bmc->running = 0;
-	kcs_force_abort(kcs_bmc);
-	spin_unlock_irq(&kcs_bmc->lock);
-
-	return 0;
-}
-
-static const struct file_operations kcs_bmc_fops = {
-	.owner          = THIS_MODULE,
-	.open           = kcs_bmc_open,
-	.read           = kcs_bmc_read,
-	.write          = kcs_bmc_write,
-	.release        = kcs_bmc_release,
-	.poll           = kcs_bmc_poll,
-	.unlocked_ioctl = kcs_bmc_ioctl,
-};
-
+struct kcs_bmc *kcs_bmc_ipmi_alloc(struct device *dev, int sizeof_priv, u32 channel);
 struct kcs_bmc *kcs_bmc_alloc(struct device *dev, int sizeof_priv, u32 channel)
 {
-	struct kcs_bmc *kcs_bmc;
-
-	kcs_bmc = devm_kzalloc(dev, sizeof(*kcs_bmc) + sizeof_priv, GFP_KERNEL);
-	if (!kcs_bmc)
-		return NULL;
-
-	spin_lock_init(&kcs_bmc->lock);
-	kcs_bmc->channel = channel;
-
-	mutex_init(&kcs_bmc->mutex);
-	init_waitqueue_head(&kcs_bmc->queue);
-
-	kcs_bmc->data_in = devm_kmalloc(dev, KCS_MSG_BUFSIZ, GFP_KERNEL);
-	kcs_bmc->data_out = devm_kmalloc(dev, KCS_MSG_BUFSIZ, GFP_KERNEL);
-	kcs_bmc->kbuffer = devm_kmalloc(dev, KCS_MSG_BUFSIZ, GFP_KERNEL);
-
-	kcs_bmc->miscdev.minor = MISC_DYNAMIC_MINOR;
-	kcs_bmc->miscdev.name = devm_kasprintf(dev, GFP_KERNEL, "%s%u",
-					       DEVICE_NAME, channel);
-	if (!kcs_bmc->data_in || !kcs_bmc->data_out || !kcs_bmc->kbuffer ||
-	    !kcs_bmc->miscdev.name)
-		return NULL;
-	kcs_bmc->miscdev.fops = &kcs_bmc_fops;
-
-	return kcs_bmc;
+	return kcs_bmc_ipmi_alloc(dev, sizeof_priv, channel);
 }
 EXPORT_SYMBOL(kcs_bmc_alloc);
 
diff --git a/drivers/char/ipmi/kcs_bmc.h b/drivers/char/ipmi/kcs_bmc.h
index 970f53892f2d..febea0c8deb4 100644
--- a/drivers/char/ipmi/kcs_bmc.h
+++ b/drivers/char/ipmi/kcs_bmc.h
@@ -104,6 +104,12 @@ static inline void *kcs_bmc_priv(struct kcs_bmc *kcs_bmc)
 }
 
 int kcs_bmc_handle_event(struct kcs_bmc *kcs_bmc);
-struct kcs_bmc *kcs_bmc_alloc(struct device *dev, int sizeof_priv,
-					u32 channel);
+struct kcs_bmc *kcs_bmc_alloc(struct device *dev, int sizeof_priv, u32 channel);
+
+u8 kcs_bmc_read_data(struct kcs_bmc *kcs_bmc);
+void kcs_bmc_write_data(struct kcs_bmc *kcs_bmc, u8 data);
+u8 kcs_bmc_read_status(struct kcs_bmc *kcs_bmc);
+void kcs_bmc_write_status(struct kcs_bmc *kcs_bmc, u8 data);
+void kcs_bmc_update_status(struct kcs_bmc *kcs_bmc, u8 mask, u8 val);
+
 #endif /* __KCS_BMC_H__ */
diff --git a/drivers/char/ipmi/kcs_bmc_cdev_ipmi.c b/drivers/char/ipmi/kcs_bmc_cdev_ipmi.c
new file mode 100644
index 000000000000..82c77994e481
--- /dev/null
+++ b/drivers/char/ipmi/kcs_bmc_cdev_ipmi.c
@@ -0,0 +1,428 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2015-2018, Intel Corporation.
+ */
+
+#define pr_fmt(fmt) "kcs-bmc: " fmt
+
+#include <linux/errno.h>
+#include <linux/io.h>
+#include <linux/ipmi_bmc.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/poll.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+
+#include "kcs_bmc.h"
+
+#define DEVICE_NAME "ipmi-kcs"
+
+#define KCS_MSG_BUFSIZ    1000
+
+#define KCS_ZERO_DATA     0
+
+
+/* IPMI 2.0 - Table 9-1, KCS Interface Status Register Bits */
+#define KCS_STATUS_STATE(state) (state << 6)
+#define KCS_STATUS_STATE_MASK   GENMASK(7, 6)
+#define KCS_STATUS_CMD_DAT      BIT(3)
+#define KCS_STATUS_SMS_ATN      BIT(2)
+#define KCS_STATUS_IBF          BIT(1)
+#define KCS_STATUS_OBF          BIT(0)
+
+/* IPMI 2.0 - Table 9-2, KCS Interface State Bits */
+enum kcs_states {
+	IDLE_STATE  = 0,
+	READ_STATE  = 1,
+	WRITE_STATE = 2,
+	ERROR_STATE = 3,
+};
+
+/* IPMI 2.0 - Table 9-3, KCS Interface Control Codes */
+#define KCS_CMD_GET_STATUS_ABORT  0x60
+#define KCS_CMD_WRITE_START       0x61
+#define KCS_CMD_WRITE_END         0x62
+#define KCS_CMD_READ_BYTE         0x68
+
+static inline void set_state(struct kcs_bmc *kcs_bmc, u8 state)
+{
+	kcs_bmc_update_status(kcs_bmc, KCS_STATUS_STATE_MASK,
+					KCS_STATUS_STATE(state));
+}
+
+static void kcs_bmc_ipmi_force_abort(struct kcs_bmc *kcs_bmc)
+{
+	set_state(kcs_bmc, ERROR_STATE);
+	kcs_bmc_read_data(kcs_bmc);
+	kcs_bmc_write_data(kcs_bmc, KCS_ZERO_DATA);
+
+	kcs_bmc->phase = KCS_PHASE_ERROR;
+	kcs_bmc->data_in_avail = false;
+	kcs_bmc->data_in_idx = 0;
+}
+
+static void kcs_bmc_ipmi_handle_data(struct kcs_bmc *kcs_bmc)
+{
+	u8 data;
+
+	switch (kcs_bmc->phase) {
+	case KCS_PHASE_WRITE_START:
+		kcs_bmc->phase = KCS_PHASE_WRITE_DATA;
+		fallthrough;
+
+	case KCS_PHASE_WRITE_DATA:
+		if (kcs_bmc->data_in_idx < KCS_MSG_BUFSIZ) {
+			set_state(kcs_bmc, WRITE_STATE);
+			kcs_bmc_write_data(kcs_bmc, KCS_ZERO_DATA);
+			kcs_bmc->data_in[kcs_bmc->data_in_idx++] =
+						kcs_bmc_read_data(kcs_bmc);
+		} else {
+			kcs_bmc_ipmi_force_abort(kcs_bmc);
+			kcs_bmc->error = KCS_LENGTH_ERROR;
+		}
+		break;
+
+	case KCS_PHASE_WRITE_END_CMD:
+		if (kcs_bmc->data_in_idx < KCS_MSG_BUFSIZ) {
+			set_state(kcs_bmc, READ_STATE);
+			kcs_bmc->data_in[kcs_bmc->data_in_idx++] =
+						kcs_bmc_read_data(kcs_bmc);
+			kcs_bmc->phase = KCS_PHASE_WRITE_DONE;
+			kcs_bmc->data_in_avail = true;
+			wake_up_interruptible(&kcs_bmc->queue);
+		} else {
+			kcs_bmc_ipmi_force_abort(kcs_bmc);
+			kcs_bmc->error = KCS_LENGTH_ERROR;
+		}
+		break;
+
+	case KCS_PHASE_READ:
+		if (kcs_bmc->data_out_idx == kcs_bmc->data_out_len)
+			set_state(kcs_bmc, IDLE_STATE);
+
+		data = kcs_bmc_read_data(kcs_bmc);
+		if (data != KCS_CMD_READ_BYTE) {
+			set_state(kcs_bmc, ERROR_STATE);
+			kcs_bmc_write_data(kcs_bmc, KCS_ZERO_DATA);
+			break;
+		}
+
+		if (kcs_bmc->data_out_idx == kcs_bmc->data_out_len) {
+			kcs_bmc_write_data(kcs_bmc, KCS_ZERO_DATA);
+			kcs_bmc->phase = KCS_PHASE_IDLE;
+			break;
+		}
+
+		kcs_bmc_write_data(kcs_bmc,
+			kcs_bmc->data_out[kcs_bmc->data_out_idx++]);
+		break;
+
+	case KCS_PHASE_ABORT_ERROR1:
+		set_state(kcs_bmc, READ_STATE);
+		kcs_bmc_read_data(kcs_bmc);
+		kcs_bmc_write_data(kcs_bmc, kcs_bmc->error);
+		kcs_bmc->phase = KCS_PHASE_ABORT_ERROR2;
+		break;
+
+	case KCS_PHASE_ABORT_ERROR2:
+		set_state(kcs_bmc, IDLE_STATE);
+		kcs_bmc_read_data(kcs_bmc);
+		kcs_bmc_write_data(kcs_bmc, KCS_ZERO_DATA);
+		kcs_bmc->phase = KCS_PHASE_IDLE;
+		break;
+
+	default:
+		kcs_bmc_ipmi_force_abort(kcs_bmc);
+		break;
+	}
+}
+
+static void kcs_bmc_ipmi_handle_cmd(struct kcs_bmc *kcs_bmc)
+{
+	u8 cmd;
+
+	set_state(kcs_bmc, WRITE_STATE);
+	kcs_bmc_write_data(kcs_bmc, KCS_ZERO_DATA);
+
+	cmd = kcs_bmc_read_data(kcs_bmc);
+	switch (cmd) {
+	case KCS_CMD_WRITE_START:
+		kcs_bmc->phase = KCS_PHASE_WRITE_START;
+		kcs_bmc->error = KCS_NO_ERROR;
+		kcs_bmc->data_in_avail = false;
+		kcs_bmc->data_in_idx = 0;
+		break;
+
+	case KCS_CMD_WRITE_END:
+		if (kcs_bmc->phase != KCS_PHASE_WRITE_DATA) {
+			kcs_bmc_ipmi_force_abort(kcs_bmc);
+			break;
+		}
+
+		kcs_bmc->phase = KCS_PHASE_WRITE_END_CMD;
+		break;
+
+	case KCS_CMD_GET_STATUS_ABORT:
+		if (kcs_bmc->error == KCS_NO_ERROR)
+			kcs_bmc->error = KCS_ABORTED_BY_COMMAND;
+
+		kcs_bmc->phase = KCS_PHASE_ABORT_ERROR1;
+		kcs_bmc->data_in_avail = false;
+		kcs_bmc->data_in_idx = 0;
+		break;
+
+	default:
+		kcs_bmc_ipmi_force_abort(kcs_bmc);
+		kcs_bmc->error = KCS_ILLEGAL_CONTROL_CODE;
+		break;
+	}
+}
+
+int kcs_bmc_ipmi_event(struct kcs_bmc *kcs_bmc);
+int kcs_bmc_ipmi_event(struct kcs_bmc *kcs_bmc)
+{
+	unsigned long flags;
+	int ret = -ENODATA;
+	u8 status;
+
+	spin_lock_irqsave(&kcs_bmc->lock, flags);
+
+	status = kcs_bmc_read_status(kcs_bmc);
+	if (status & KCS_STATUS_IBF) {
+		if (!kcs_bmc->running)
+			kcs_bmc_ipmi_force_abort(kcs_bmc);
+		else if (status & KCS_STATUS_CMD_DAT)
+			kcs_bmc_ipmi_handle_cmd(kcs_bmc);
+		else
+			kcs_bmc_ipmi_handle_data(kcs_bmc);
+
+		ret = 0;
+	}
+
+	spin_unlock_irqrestore(&kcs_bmc->lock, flags);
+
+	return ret;
+}
+EXPORT_SYMBOL(kcs_bmc_ipmi_event);
+
+static inline struct kcs_bmc *to_kcs_bmc(struct file *filp)
+{
+	return container_of(filp->private_data, struct kcs_bmc, miscdev);
+}
+
+static int kcs_bmc_ipmi_open(struct inode *inode, struct file *filp)
+{
+	struct kcs_bmc *kcs_bmc = to_kcs_bmc(filp);
+	int ret = 0;
+
+	spin_lock_irq(&kcs_bmc->lock);
+	if (!kcs_bmc->running)
+		kcs_bmc->running = 1;
+	else
+		ret = -EBUSY;
+	spin_unlock_irq(&kcs_bmc->lock);
+
+	return ret;
+}
+
+static __poll_t kcs_bmc_ipmi_poll(struct file *filp, poll_table *wait)
+{
+	struct kcs_bmc *kcs_bmc = to_kcs_bmc(filp);
+	__poll_t mask = 0;
+
+	poll_wait(filp, &kcs_bmc->queue, wait);
+
+	spin_lock_irq(&kcs_bmc->lock);
+	if (kcs_bmc->data_in_avail)
+		mask |= EPOLLIN;
+	spin_unlock_irq(&kcs_bmc->lock);
+
+	return mask;
+}
+
+static ssize_t kcs_bmc_ipmi_read(struct file *filp, char __user *buf,
+			    size_t count, loff_t *ppos)
+{
+	struct kcs_bmc *kcs_bmc = to_kcs_bmc(filp);
+	bool data_avail;
+	size_t data_len;
+	ssize_t ret;
+
+	if (!(filp->f_flags & O_NONBLOCK))
+		wait_event_interruptible(kcs_bmc->queue,
+					 kcs_bmc->data_in_avail);
+
+	mutex_lock(&kcs_bmc->mutex);
+
+	spin_lock_irq(&kcs_bmc->lock);
+	data_avail = kcs_bmc->data_in_avail;
+	if (data_avail) {
+		data_len = kcs_bmc->data_in_idx;
+		memcpy(kcs_bmc->kbuffer, kcs_bmc->data_in, data_len);
+	}
+	spin_unlock_irq(&kcs_bmc->lock);
+
+	if (!data_avail) {
+		ret = -EAGAIN;
+		goto out_unlock;
+	}
+
+	if (count < data_len) {
+		pr_err("channel=%u with too large data : %zu\n",
+			kcs_bmc->channel, data_len);
+
+		spin_lock_irq(&kcs_bmc->lock);
+		kcs_bmc_ipmi_force_abort(kcs_bmc);
+		spin_unlock_irq(&kcs_bmc->lock);
+
+		ret = -EOVERFLOW;
+		goto out_unlock;
+	}
+
+	if (copy_to_user(buf, kcs_bmc->kbuffer, data_len)) {
+		ret = -EFAULT;
+		goto out_unlock;
+	}
+
+	ret = data_len;
+
+	spin_lock_irq(&kcs_bmc->lock);
+	if (kcs_bmc->phase == KCS_PHASE_WRITE_DONE) {
+		kcs_bmc->phase = KCS_PHASE_WAIT_READ;
+		kcs_bmc->data_in_avail = false;
+		kcs_bmc->data_in_idx = 0;
+	} else {
+		ret = -EAGAIN;
+	}
+	spin_unlock_irq(&kcs_bmc->lock);
+
+out_unlock:
+	mutex_unlock(&kcs_bmc->mutex);
+
+	return ret;
+}
+
+static ssize_t kcs_bmc_ipmi_write(struct file *filp, const char __user *buf,
+			     size_t count, loff_t *ppos)
+{
+	struct kcs_bmc *kcs_bmc = to_kcs_bmc(filp);
+	ssize_t ret;
+
+	/* a minimum response size '3' : netfn + cmd + ccode */
+	if (count < 3 || count > KCS_MSG_BUFSIZ)
+		return -EINVAL;
+
+	mutex_lock(&kcs_bmc->mutex);
+
+	if (copy_from_user(kcs_bmc->kbuffer, buf, count)) {
+		ret = -EFAULT;
+		goto out_unlock;
+	}
+
+	spin_lock_irq(&kcs_bmc->lock);
+	if (kcs_bmc->phase == KCS_PHASE_WAIT_READ) {
+		kcs_bmc->phase = KCS_PHASE_READ;
+		kcs_bmc->data_out_idx = 1;
+		kcs_bmc->data_out_len = count;
+		memcpy(kcs_bmc->data_out, kcs_bmc->kbuffer, count);
+		kcs_bmc_write_data(kcs_bmc, kcs_bmc->data_out[0]);
+		ret = count;
+	} else {
+		ret = -EINVAL;
+	}
+	spin_unlock_irq(&kcs_bmc->lock);
+
+out_unlock:
+	mutex_unlock(&kcs_bmc->mutex);
+
+	return ret;
+}
+
+static long kcs_bmc_ipmi_ioctl(struct file *filp, unsigned int cmd,
+			  unsigned long arg)
+{
+	struct kcs_bmc *kcs_bmc = to_kcs_bmc(filp);
+	long ret = 0;
+
+	spin_lock_irq(&kcs_bmc->lock);
+
+	switch (cmd) {
+	case IPMI_BMC_IOCTL_SET_SMS_ATN:
+		kcs_bmc_update_status(kcs_bmc, KCS_STATUS_SMS_ATN, KCS_STATUS_SMS_ATN);
+		break;
+
+	case IPMI_BMC_IOCTL_CLEAR_SMS_ATN:
+		kcs_bmc_update_status(kcs_bmc, KCS_STATUS_SMS_ATN, 0);
+		break;
+
+	case IPMI_BMC_IOCTL_FORCE_ABORT:
+		kcs_bmc_ipmi_force_abort(kcs_bmc);
+		break;
+
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	spin_unlock_irq(&kcs_bmc->lock);
+
+	return ret;
+}
+
+static int kcs_bmc_ipmi_release(struct inode *inode, struct file *filp)
+{
+	struct kcs_bmc *kcs_bmc = to_kcs_bmc(filp);
+
+	spin_lock_irq(&kcs_bmc->lock);
+	kcs_bmc->running = 0;
+	kcs_bmc_ipmi_force_abort(kcs_bmc);
+	spin_unlock_irq(&kcs_bmc->lock);
+
+	return 0;
+}
+
+static const struct file_operations kcs_bmc_fops = {
+	.owner          = THIS_MODULE,
+	.open           = kcs_bmc_ipmi_open,
+	.read           = kcs_bmc_ipmi_read,
+	.write          = kcs_bmc_ipmi_write,
+	.release        = kcs_bmc_ipmi_release,
+	.poll           = kcs_bmc_ipmi_poll,
+	.unlocked_ioctl = kcs_bmc_ipmi_ioctl,
+};
+
+struct kcs_bmc *kcs_bmc_ipmi_alloc(struct device *dev, int sizeof_priv, u32 channel);
+struct kcs_bmc *kcs_bmc_ipmi_alloc(struct device *dev, int sizeof_priv, u32 channel)
+{
+	struct kcs_bmc *kcs_bmc;
+
+	kcs_bmc = devm_kzalloc(dev, sizeof(*kcs_bmc) + sizeof_priv, GFP_KERNEL);
+	if (!kcs_bmc)
+		return NULL;
+
+	spin_lock_init(&kcs_bmc->lock);
+	kcs_bmc->channel = channel;
+
+	mutex_init(&kcs_bmc->mutex);
+	init_waitqueue_head(&kcs_bmc->queue);
+
+	kcs_bmc->data_in = devm_kmalloc(dev, KCS_MSG_BUFSIZ, GFP_KERNEL);
+	kcs_bmc->data_out = devm_kmalloc(dev, KCS_MSG_BUFSIZ, GFP_KERNEL);
+	kcs_bmc->kbuffer = devm_kmalloc(dev, KCS_MSG_BUFSIZ, GFP_KERNEL);
+
+	kcs_bmc->miscdev.minor = MISC_DYNAMIC_MINOR;
+	kcs_bmc->miscdev.name = devm_kasprintf(dev, GFP_KERNEL, "%s%u",
+					       DEVICE_NAME, channel);
+	if (!kcs_bmc->data_in || !kcs_bmc->data_out || !kcs_bmc->kbuffer ||
+	    !kcs_bmc->miscdev.name)
+		return NULL;
+	kcs_bmc->miscdev.fops = &kcs_bmc_fops;
+
+	return kcs_bmc;
+}
+EXPORT_SYMBOL(kcs_bmc_ipmi_alloc);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Haiyue Wang <haiyue.wang@linux.intel.com>");
+MODULE_DESCRIPTION("KCS BMC to handle the IPMI request from system software");
-- 
2.27.0


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

* [PATCH v2 10/21] ipmi: kcs_bmc: Turn the driver data-structures inside-out
  2021-03-19  6:27 [PATCH v2 01/21] dt-bindings: aspeed-lpc: Remove LPC partitioning Andrew Jeffery
                   ` (7 preceding siblings ...)
  2021-03-19  6:27 ` [PATCH v2 09/21] ipmi: kcs_bmc: Split out kcs_bmc_cdev_ipmi Andrew Jeffery
@ 2021-03-19  6:27 ` Andrew Jeffery
  2021-04-09  3:57   ` Zev Weiss
  2021-03-19  6:27 ` [PATCH v2 11/21] ipmi: kcs_bmc: Split headers into device and client Andrew Jeffery
                   ` (11 subsequent siblings)
  20 siblings, 1 reply; 67+ messages in thread
From: Andrew Jeffery @ 2021-03-19  6:27 UTC (permalink / raw)
  To: openipmi-developer, openbmc, minyard
  Cc: devicetree, ryan_chen, tmaimon77, linux-aspeed, avifishman70,
	venture, linus.walleij, linux-kernel, tali.perry1, linux-gpio,
	robh+dt, lee.jones, chiawei_wang, linux-arm-kernel, benjaminfair

Make the KCS device drivers responsible for allocating their own memory.

Until now the private data for the device driver was allocated internal
to the private data for the chardev interface. This coupling required
the slightly awkward API of passing through the struct size for the
driver private data to the chardev constructor, and then retrieving a
pointer to the driver private data from the allocated chardev memory.

In addition to being awkward, the arrangement prevents the
implementation of alternative userspace interfaces as the device driver
private data is not independent.

Peel a layer off the onion and turn the data-structures inside out by
exploiting container_of() and embedding `struct kcs_device` in the
driver private data.

Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
---
 drivers/char/ipmi/kcs_bmc.c           | 15 +++++--
 drivers/char/ipmi/kcs_bmc.h           | 12 ++----
 drivers/char/ipmi/kcs_bmc_aspeed.c    | 60 ++++++++++++++++-----------
 drivers/char/ipmi/kcs_bmc_cdev_ipmi.c | 60 ++++++++++++++++++---------
 drivers/char/ipmi/kcs_bmc_npcm7xx.c   | 37 ++++++++++-------
 5 files changed, 113 insertions(+), 71 deletions(-)

diff --git a/drivers/char/ipmi/kcs_bmc.c b/drivers/char/ipmi/kcs_bmc.c
index ef5c48ffe74a..709b6bdec165 100644
--- a/drivers/char/ipmi/kcs_bmc.c
+++ b/drivers/char/ipmi/kcs_bmc.c
@@ -44,12 +44,19 @@ int kcs_bmc_handle_event(struct kcs_bmc *kcs_bmc)
 }
 EXPORT_SYMBOL(kcs_bmc_handle_event);
 
-struct kcs_bmc *kcs_bmc_ipmi_alloc(struct device *dev, int sizeof_priv, u32 channel);
-struct kcs_bmc *kcs_bmc_alloc(struct device *dev, int sizeof_priv, u32 channel)
+int kcs_bmc_ipmi_attach_cdev(struct kcs_bmc *kcs_bmc);
+int kcs_bmc_add_device(struct kcs_bmc *kcs_bmc)
 {
-	return kcs_bmc_ipmi_alloc(dev, sizeof_priv, channel);
+	return kcs_bmc_ipmi_attach_cdev(kcs_bmc);
 }
-EXPORT_SYMBOL(kcs_bmc_alloc);
+EXPORT_SYMBOL(kcs_bmc_add_device);
+
+int kcs_bmc_ipmi_detach_cdev(struct kcs_bmc *kcs_bmc);
+int kcs_bmc_remove_device(struct kcs_bmc *kcs_bmc)
+{
+	return kcs_bmc_ipmi_detach_cdev(kcs_bmc);
+}
+EXPORT_SYMBOL(kcs_bmc_remove_device);
 
 MODULE_LICENSE("GPL v2");
 MODULE_AUTHOR("Haiyue Wang <haiyue.wang@linux.intel.com>");
diff --git a/drivers/char/ipmi/kcs_bmc.h b/drivers/char/ipmi/kcs_bmc.h
index febea0c8deb4..bf0ae327997f 100644
--- a/drivers/char/ipmi/kcs_bmc.h
+++ b/drivers/char/ipmi/kcs_bmc.h
@@ -67,6 +67,8 @@ struct kcs_ioreg {
 };
 
 struct kcs_bmc {
+	struct device *dev;
+
 	spinlock_t lock;
 
 	u32 channel;
@@ -94,17 +96,11 @@ struct kcs_bmc {
 	u8 *kbuffer;
 
 	struct miscdevice miscdev;
-
-	unsigned long priv[];
 };
 
-static inline void *kcs_bmc_priv(struct kcs_bmc *kcs_bmc)
-{
-	return kcs_bmc->priv;
-}
-
 int kcs_bmc_handle_event(struct kcs_bmc *kcs_bmc);
-struct kcs_bmc *kcs_bmc_alloc(struct device *dev, int sizeof_priv, u32 channel);
+int kcs_bmc_add_device(struct kcs_bmc *kcs_bmc);
+int kcs_bmc_remove_device(struct kcs_bmc *kcs_bmc);
 
 u8 kcs_bmc_read_data(struct kcs_bmc *kcs_bmc);
 void kcs_bmc_write_data(struct kcs_bmc *kcs_bmc, u8 data);
diff --git a/drivers/char/ipmi/kcs_bmc_aspeed.c b/drivers/char/ipmi/kcs_bmc_aspeed.c
index 630cf095560e..0416ac78ce68 100644
--- a/drivers/char/ipmi/kcs_bmc_aspeed.c
+++ b/drivers/char/ipmi/kcs_bmc_aspeed.c
@@ -61,6 +61,8 @@
 #define LPC_STR4             0x11C
 
 struct aspeed_kcs_bmc {
+	struct kcs_bmc kcs_bmc;
+
 	struct regmap *map;
 };
 
@@ -69,9 +71,14 @@ struct aspeed_kcs_of_ops {
 	int (*get_io_address)(struct platform_device *pdev);
 };
 
+static inline struct aspeed_kcs_bmc *to_aspeed_kcs_bmc(struct kcs_bmc *kcs_bmc)
+{
+	return container_of(kcs_bmc, struct aspeed_kcs_bmc, kcs_bmc);
+}
+
 static u8 aspeed_kcs_inb(struct kcs_bmc *kcs_bmc, u32 reg)
 {
-	struct aspeed_kcs_bmc *priv = kcs_bmc_priv(kcs_bmc);
+	struct aspeed_kcs_bmc *priv = to_aspeed_kcs_bmc(kcs_bmc);
 	u32 val = 0;
 	int rc;
 
@@ -83,7 +90,7 @@ static u8 aspeed_kcs_inb(struct kcs_bmc *kcs_bmc, u32 reg)
 
 static void aspeed_kcs_outb(struct kcs_bmc *kcs_bmc, u32 reg, u8 data)
 {
-	struct aspeed_kcs_bmc *priv = kcs_bmc_priv(kcs_bmc);
+	struct aspeed_kcs_bmc *priv = to_aspeed_kcs_bmc(kcs_bmc);
 	int rc;
 
 	rc = regmap_write(priv->map, reg, data);
@@ -92,7 +99,7 @@ static void aspeed_kcs_outb(struct kcs_bmc *kcs_bmc, u32 reg, u8 data)
 
 static void aspeed_kcs_updateb(struct kcs_bmc *kcs_bmc, u32 reg, u8 mask, u8 val)
 {
-	struct aspeed_kcs_bmc *priv = kcs_bmc_priv(kcs_bmc);
+	struct aspeed_kcs_bmc *priv = to_aspeed_kcs_bmc(kcs_bmc);
 	int rc;
 
 	rc = regmap_update_bits(priv->map, reg, mask, val);
@@ -114,7 +121,7 @@ static void aspeed_kcs_updateb(struct kcs_bmc *kcs_bmc, u32 reg, u8 mask, u8 val
  */
 static void aspeed_kcs_set_address(struct kcs_bmc *kcs_bmc, u16 addr)
 {
-	struct aspeed_kcs_bmc *priv = kcs_bmc_priv(kcs_bmc);
+	struct aspeed_kcs_bmc *priv = to_aspeed_kcs_bmc(kcs_bmc);
 
 	switch (kcs_bmc->channel) {
 	case 1:
@@ -148,7 +155,7 @@ static void aspeed_kcs_set_address(struct kcs_bmc *kcs_bmc, u16 addr)
 
 static void aspeed_kcs_enable_channel(struct kcs_bmc *kcs_bmc, bool enable)
 {
-	struct aspeed_kcs_bmc *priv = kcs_bmc_priv(kcs_bmc);
+	struct aspeed_kcs_bmc *priv = to_aspeed_kcs_bmc(kcs_bmc);
 
 	switch (kcs_bmc->channel) {
 	case 1:
@@ -323,16 +330,16 @@ static int aspeed_kcs_of_v2_get_io_address(struct platform_device *pdev)
 static int aspeed_kcs_probe(struct platform_device *pdev)
 {
 	const struct aspeed_kcs_of_ops *ops;
-	struct device *dev = &pdev->dev;
+	struct aspeed_kcs_bmc *priv;
 	struct kcs_bmc *kcs_bmc;
 	struct device_node *np;
 	int rc, channel, addr;
 
-	np = dev->of_node->parent;
+	np = pdev->dev.of_node->parent;
 	if (!of_device_is_compatible(np, "aspeed,ast2400-lpc-v2") &&
 	    !of_device_is_compatible(np, "aspeed,ast2500-lpc-v2") &&
 	    !of_device_is_compatible(np, "aspeed,ast2600-lpc-v2")) {
-		dev_err(dev, "unsupported LPC device binding\n");
+		dev_err(&pdev->dev, "unsupported LPC device binding\n");
 		return -ENODEV;
 	}
 	ops = of_device_get_match_data(&pdev->dev);
@@ -343,18 +350,27 @@ static int aspeed_kcs_probe(struct platform_device *pdev)
 	if (channel < 0)
 		return channel;
 
-	kcs_bmc = kcs_bmc_alloc(&pdev->dev, sizeof(struct aspeed_kcs_bmc), channel);
-	if (!kcs_bmc)
+	addr = ops->get_io_address(pdev);
+	if (addr < 0)
+		return addr;
+
+	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
 		return -ENOMEM;
 
+	kcs_bmc = &priv->kcs_bmc;
+	kcs_bmc->dev = &pdev->dev;
+	kcs_bmc->channel = channel;
 	kcs_bmc->ioreg = ast_kcs_bmc_ioregs[channel - 1];
 	kcs_bmc->io_inputb = aspeed_kcs_inb;
 	kcs_bmc->io_outputb = aspeed_kcs_outb;
 	kcs_bmc->io_updateb = aspeed_kcs_updateb;
 
-	addr = ops->get_io_address(pdev);
-	if (addr < 0)
-		return addr;
+	priv->map = syscon_node_to_regmap(pdev->dev.parent->of_node);
+	if (IS_ERR(priv->map)) {
+		dev_err(&pdev->dev, "Couldn't get regmap\n");
+		return -ENODEV;
+	}
 
 	aspeed_kcs_set_address(kcs_bmc, addr);
 
@@ -362,29 +378,25 @@ static int aspeed_kcs_probe(struct platform_device *pdev)
 	if (rc)
 		return rc;
 
-	dev_set_drvdata(dev, kcs_bmc);
+	platform_set_drvdata(pdev, priv);
 
 	aspeed_kcs_enable_channel(kcs_bmc, true);
 
-	rc = misc_register(&kcs_bmc->miscdev);
-	if (rc) {
-		dev_err(dev, "Unable to register device\n");
+	rc = kcs_bmc_add_device(&priv->kcs_bmc);
+	if (rc < 0)
 		return rc;
-	}
 
-	dev_dbg(&pdev->dev,
-		"Probed KCS device %d (IDR=0x%x, ODR=0x%x, STR=0x%x)\n",
-		kcs_bmc->channel, kcs_bmc->ioreg.idr, kcs_bmc->ioreg.odr,
-		kcs_bmc->ioreg.str);
+	dev_info(&pdev->dev, "Initialised channel %d at 0x%x\n", kcs_bmc->channel, addr);
 
 	return 0;
 }
 
 static int aspeed_kcs_remove(struct platform_device *pdev)
 {
-	struct kcs_bmc *kcs_bmc = dev_get_drvdata(&pdev->dev);
+	struct aspeed_kcs_bmc *priv = platform_get_drvdata(pdev);
+	struct kcs_bmc *kcs_bmc = &priv->kcs_bmc;
 
-	misc_deregister(&kcs_bmc->miscdev);
+	kcs_bmc_remove_device(kcs_bmc);
 
 	return 0;
 }
diff --git a/drivers/char/ipmi/kcs_bmc_cdev_ipmi.c b/drivers/char/ipmi/kcs_bmc_cdev_ipmi.c
index 82c77994e481..0ca71c135a1a 100644
--- a/drivers/char/ipmi/kcs_bmc_cdev_ipmi.c
+++ b/drivers/char/ipmi/kcs_bmc_cdev_ipmi.c
@@ -382,7 +382,7 @@ static int kcs_bmc_ipmi_release(struct inode *inode, struct file *filp)
 	return 0;
 }
 
-static const struct file_operations kcs_bmc_fops = {
+static const struct file_operations kcs_bmc_ipmi_fops = {
 	.owner          = THIS_MODULE,
 	.open           = kcs_bmc_ipmi_open,
 	.read           = kcs_bmc_ipmi_read,
@@ -392,36 +392,58 @@ static const struct file_operations kcs_bmc_fops = {
 	.unlocked_ioctl = kcs_bmc_ipmi_ioctl,
 };
 
-struct kcs_bmc *kcs_bmc_ipmi_alloc(struct device *dev, int sizeof_priv, u32 channel);
-struct kcs_bmc *kcs_bmc_ipmi_alloc(struct device *dev, int sizeof_priv, u32 channel)
+int kcs_bmc_ipmi_attach_cdev(struct kcs_bmc *kcs_bmc);
+int kcs_bmc_ipmi_attach_cdev(struct kcs_bmc *kcs_bmc)
 {
-	struct kcs_bmc *kcs_bmc;
-
-	kcs_bmc = devm_kzalloc(dev, sizeof(*kcs_bmc) + sizeof_priv, GFP_KERNEL);
-	if (!kcs_bmc)
-		return NULL;
+	int rc;
 
 	spin_lock_init(&kcs_bmc->lock);
-	kcs_bmc->channel = channel;
-
 	mutex_init(&kcs_bmc->mutex);
 	init_waitqueue_head(&kcs_bmc->queue);
 
-	kcs_bmc->data_in = devm_kmalloc(dev, KCS_MSG_BUFSIZ, GFP_KERNEL);
-	kcs_bmc->data_out = devm_kmalloc(dev, KCS_MSG_BUFSIZ, GFP_KERNEL);
-	kcs_bmc->kbuffer = devm_kmalloc(dev, KCS_MSG_BUFSIZ, GFP_KERNEL);
+	kcs_bmc->data_in = devm_kmalloc(kcs_bmc->dev, KCS_MSG_BUFSIZ, GFP_KERNEL);
+	kcs_bmc->data_out = devm_kmalloc(kcs_bmc->dev, KCS_MSG_BUFSIZ, GFP_KERNEL);
+	kcs_bmc->kbuffer = devm_kmalloc(kcs_bmc->dev, KCS_MSG_BUFSIZ, GFP_KERNEL);
 
 	kcs_bmc->miscdev.minor = MISC_DYNAMIC_MINOR;
-	kcs_bmc->miscdev.name = devm_kasprintf(dev, GFP_KERNEL, "%s%u",
-					       DEVICE_NAME, channel);
+	kcs_bmc->miscdev.name = devm_kasprintf(kcs_bmc->dev, GFP_KERNEL, "%s%u",
+					       DEVICE_NAME, kcs_bmc->channel);
 	if (!kcs_bmc->data_in || !kcs_bmc->data_out || !kcs_bmc->kbuffer ||
 	    !kcs_bmc->miscdev.name)
-		return NULL;
-	kcs_bmc->miscdev.fops = &kcs_bmc_fops;
+		return -ENOMEM;
 
-	return kcs_bmc;
+	kcs_bmc->miscdev.fops = &kcs_bmc_ipmi_fops;
+
+	rc = misc_register(&kcs_bmc->miscdev);
+	if (rc) {
+		dev_err(kcs_bmc->dev, "Unable to register device: %d\n", rc);
+		return rc;
+	}
+
+	dev_info(kcs_bmc->dev, "Initialised IPMI client for channel %d", kcs_bmc->channel);
+
+	return 0;
+}
+EXPORT_SYMBOL(kcs_bmc_ipmi_attach_cdev);
+
+int kcs_bmc_ipmi_detach_cdev(struct kcs_bmc *kcs_bmc);
+int kcs_bmc_ipmi_detach_cdev(struct kcs_bmc *kcs_bmc)
+{
+	misc_deregister(&kcs_bmc->miscdev);
+
+	spin_lock_irq(&kcs_bmc->lock);
+	kcs_bmc->running = 0;
+	kcs_bmc_ipmi_force_abort(kcs_bmc);
+	spin_unlock_irq(&kcs_bmc->lock);
+
+	devm_kfree(kcs_bmc->dev, kcs_bmc->kbuffer);
+	devm_kfree(kcs_bmc->dev, kcs_bmc->data_out);
+	devm_kfree(kcs_bmc->dev, kcs_bmc->data_in);
+	devm_kfree(kcs_bmc->dev, kcs_bmc);
+
+	return 0;
 }
-EXPORT_SYMBOL(kcs_bmc_ipmi_alloc);
+EXPORT_SYMBOL(kcs_bmc_ipmi_detach_cdev);
 
 MODULE_LICENSE("GPL v2");
 MODULE_AUTHOR("Haiyue Wang <haiyue.wang@linux.intel.com>");
diff --git a/drivers/char/ipmi/kcs_bmc_npcm7xx.c b/drivers/char/ipmi/kcs_bmc_npcm7xx.c
index 1f44aadec9e8..5d017498dc69 100644
--- a/drivers/char/ipmi/kcs_bmc_npcm7xx.c
+++ b/drivers/char/ipmi/kcs_bmc_npcm7xx.c
@@ -65,6 +65,8 @@ struct npcm7xx_kcs_reg {
 };
 
 struct npcm7xx_kcs_bmc {
+	struct kcs_bmc kcs_bmc;
+
 	struct regmap *map;
 
 	const struct npcm7xx_kcs_reg *reg;
@@ -76,9 +78,14 @@ static const struct npcm7xx_kcs_reg npcm7xx_kcs_reg_tbl[KCS_CHANNEL_MAX] = {
 	{ .sts = KCS3ST, .dob = KCS3DO, .dib = KCS3DI, .ctl = KCS3CTL, .ie = KCS3IE },
 };
 
+static inline struct npcm7xx_kcs_bmc *to_npcm7xx_kcs_bmc(struct kcs_bmc *kcs_bmc)
+{
+	return container_of(kcs_bmc, struct npcm7xx_kcs_bmc, kcs_bmc);
+}
+
 static u8 npcm7xx_kcs_inb(struct kcs_bmc *kcs_bmc, u32 reg)
 {
-	struct npcm7xx_kcs_bmc *priv = kcs_bmc_priv(kcs_bmc);
+	struct npcm7xx_kcs_bmc *priv = to_npcm7xx_kcs_bmc(kcs_bmc);
 	u32 val = 0;
 	int rc;
 
@@ -90,7 +97,7 @@ static u8 npcm7xx_kcs_inb(struct kcs_bmc *kcs_bmc, u32 reg)
 
 static void npcm7xx_kcs_outb(struct kcs_bmc *kcs_bmc, u32 reg, u8 data)
 {
-	struct npcm7xx_kcs_bmc *priv = kcs_bmc_priv(kcs_bmc);
+	struct npcm7xx_kcs_bmc *priv = to_npcm7xx_kcs_bmc(kcs_bmc);
 	int rc;
 
 	rc = regmap_write(priv->map, reg, data);
@@ -99,7 +106,7 @@ static void npcm7xx_kcs_outb(struct kcs_bmc *kcs_bmc, u32 reg, u8 data)
 
 static void npcm7xx_kcs_updateb(struct kcs_bmc *kcs_bmc, u32 reg, u8 mask, u8 data)
 {
-	struct npcm7xx_kcs_bmc *priv = kcs_bmc_priv(kcs_bmc);
+	struct npcm7xx_kcs_bmc *priv = to_npcm7xx_kcs_bmc(kcs_bmc);
 	int rc;
 
 	rc = regmap_update_bits(priv->map, reg, mask, data);
@@ -108,7 +115,7 @@ static void npcm7xx_kcs_updateb(struct kcs_bmc *kcs_bmc, u32 reg, u8 mask, u8 da
 
 static void npcm7xx_kcs_enable_channel(struct kcs_bmc *kcs_bmc, bool enable)
 {
-	struct npcm7xx_kcs_bmc *priv = kcs_bmc_priv(kcs_bmc);
+	struct npcm7xx_kcs_bmc *priv = to_npcm7xx_kcs_bmc(kcs_bmc);
 
 	regmap_update_bits(priv->map, priv->reg->ctl, KCS_CTL_IBFIE,
 			   enable ? KCS_CTL_IBFIE : 0);
@@ -155,11 +162,10 @@ static int npcm7xx_kcs_probe(struct platform_device *pdev)
 		return -ENODEV;
 	}
 
-	kcs_bmc = kcs_bmc_alloc(dev, sizeof(*priv), chan);
-	if (!kcs_bmc)
+	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
 		return -ENOMEM;
 
-	priv = kcs_bmc_priv(kcs_bmc);
 	priv->map = syscon_node_to_regmap(dev->parent->of_node);
 	if (IS_ERR(priv->map)) {
 		dev_err(dev, "Couldn't get regmap\n");
@@ -167,6 +173,9 @@ static int npcm7xx_kcs_probe(struct platform_device *pdev)
 	}
 	priv->reg = &npcm7xx_kcs_reg_tbl[chan - 1];
 
+	kcs_bmc = &priv->kcs_bmc;
+	kcs_bmc->dev = &pdev->dev;
+	kcs_bmc->channel = chan;
 	kcs_bmc->ioreg.idr = priv->reg->dib;
 	kcs_bmc->ioreg.odr = priv->reg->dob;
 	kcs_bmc->ioreg.str = priv->reg->sts;
@@ -174,31 +183,27 @@ static int npcm7xx_kcs_probe(struct platform_device *pdev)
 	kcs_bmc->io_outputb = npcm7xx_kcs_outb;
 	kcs_bmc->io_updateb = npcm7xx_kcs_updateb;
 
-	dev_set_drvdata(dev, kcs_bmc);
+	platform_set_drvdata(pdev, priv);
 
 	npcm7xx_kcs_enable_channel(kcs_bmc, true);
 	rc = npcm7xx_kcs_config_irq(kcs_bmc, pdev);
 	if (rc)
 		return rc;
 
-	rc = misc_register(&kcs_bmc->miscdev);
-	if (rc) {
-		dev_err(dev, "Unable to register device\n");
-		return rc;
-	}
 
 	pr_info("channel=%u idr=0x%x odr=0x%x str=0x%x\n",
 		chan,
 		kcs_bmc->ioreg.idr, kcs_bmc->ioreg.odr, kcs_bmc->ioreg.str);
 
-	return 0;
+	return kcs_bmc_add_device(kcs_bmc);
 }
 
 static int npcm7xx_kcs_remove(struct platform_device *pdev)
 {
-	struct kcs_bmc *kcs_bmc = dev_get_drvdata(&pdev->dev);
+	struct npcm7xx_kcs_bmc *priv = platform_get_drvdata(pdev);
+	struct kcs_bmc *kcs_bmc = &priv->kcs_bmc;
 
-	misc_deregister(&kcs_bmc->miscdev);
+	kcs_bmc_remove_device(kcs_bmc);
 
 	return 0;
 }
-- 
2.27.0


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

* [PATCH v2 11/21] ipmi: kcs_bmc: Split headers into device and client
  2021-03-19  6:27 [PATCH v2 01/21] dt-bindings: aspeed-lpc: Remove LPC partitioning Andrew Jeffery
                   ` (8 preceding siblings ...)
  2021-03-19  6:27 ` [PATCH v2 10/21] ipmi: kcs_bmc: Turn the driver data-structures inside-out Andrew Jeffery
@ 2021-03-19  6:27 ` Andrew Jeffery
  2021-04-09  4:01   ` Zev Weiss
  2021-03-19  6:27 ` [PATCH v2 12/21] ipmi: kcs_bmc: Strip private client data from struct kcs_bmc Andrew Jeffery
                   ` (10 subsequent siblings)
  20 siblings, 1 reply; 67+ messages in thread
From: Andrew Jeffery @ 2021-03-19  6:27 UTC (permalink / raw)
  To: openipmi-developer, openbmc, minyard
  Cc: devicetree, ryan_chen, tmaimon77, linux-aspeed, avifishman70,
	venture, linus.walleij, linux-kernel, tali.perry1, linux-gpio,
	robh+dt, lee.jones, chiawei_wang, linux-arm-kernel, benjaminfair

Strengthen the distinction between code that abstracts the
implementation of the KCS behaviours (device drivers) and code that
exploits KCS behaviours (clients). Neither needs to know about the APIs
required by the other, so provide separate headers.

Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
---
 drivers/char/ipmi/kcs_bmc.c           | 21 ++++++++++-----
 drivers/char/ipmi/kcs_bmc.h           | 30 ++++++++++-----------
 drivers/char/ipmi/kcs_bmc_aspeed.c    | 20 +++++++++-----
 drivers/char/ipmi/kcs_bmc_cdev_ipmi.c | 39 ++++++++++++++++++---------
 drivers/char/ipmi/kcs_bmc_client.h    | 29 ++++++++++++++++++++
 drivers/char/ipmi/kcs_bmc_device.h    | 19 +++++++++++++
 drivers/char/ipmi/kcs_bmc_npcm7xx.c   | 20 +++++++++-----
 7 files changed, 129 insertions(+), 49 deletions(-)
 create mode 100644 drivers/char/ipmi/kcs_bmc_client.h
 create mode 100644 drivers/char/ipmi/kcs_bmc_device.h

diff --git a/drivers/char/ipmi/kcs_bmc.c b/drivers/char/ipmi/kcs_bmc.c
index 709b6bdec165..1046ce2bbefc 100644
--- a/drivers/char/ipmi/kcs_bmc.c
+++ b/drivers/char/ipmi/kcs_bmc.c
@@ -1,46 +1,52 @@
 // SPDX-License-Identifier: GPL-2.0
 /*
  * Copyright (c) 2015-2018, Intel Corporation.
+ * Copyright (c) 2021, IBM Corp.
  */
 
 #include <linux/module.h>
 
 #include "kcs_bmc.h"
 
+/* Implement both the device and client interfaces here */
+#include "kcs_bmc_device.h"
+#include "kcs_bmc_client.h"
+
+/* Consumer data access */
+
 u8 kcs_bmc_read_data(struct kcs_bmc *kcs_bmc)
 {
-	return kcs_bmc->io_inputb(kcs_bmc, kcs_bmc->ioreg.idr);
+	return kcs_bmc->ops->io_inputb(kcs_bmc, kcs_bmc->ioreg.idr);
 }
 EXPORT_SYMBOL(kcs_bmc_read_data);
 
 void kcs_bmc_write_data(struct kcs_bmc *kcs_bmc, u8 data)
 {
-	kcs_bmc->io_outputb(kcs_bmc, kcs_bmc->ioreg.odr, data);
+	kcs_bmc->ops->io_outputb(kcs_bmc, kcs_bmc->ioreg.odr, data);
 }
 EXPORT_SYMBOL(kcs_bmc_write_data);
 
 u8 kcs_bmc_read_status(struct kcs_bmc *kcs_bmc)
 {
-	return kcs_bmc->io_inputb(kcs_bmc, kcs_bmc->ioreg.str);
+	return kcs_bmc->ops->io_inputb(kcs_bmc, kcs_bmc->ioreg.str);
 }
 EXPORT_SYMBOL(kcs_bmc_read_status);
 
 void kcs_bmc_write_status(struct kcs_bmc *kcs_bmc, u8 data)
 {
-	kcs_bmc->io_outputb(kcs_bmc, kcs_bmc->ioreg.str, data);
+	kcs_bmc->ops->io_outputb(kcs_bmc, kcs_bmc->ioreg.str, data);
 }
 EXPORT_SYMBOL(kcs_bmc_write_status);
 
 void kcs_bmc_update_status(struct kcs_bmc *kcs_bmc, u8 mask, u8 val)
 {
-	kcs_bmc->io_updateb(kcs_bmc, kcs_bmc->ioreg.str, mask, val);
+	kcs_bmc->ops->io_updateb(kcs_bmc, kcs_bmc->ioreg.str, mask, val);
 }
 EXPORT_SYMBOL(kcs_bmc_update_status);
 
-int kcs_bmc_ipmi_event(struct kcs_bmc *kcs_bmc);
 int kcs_bmc_handle_event(struct kcs_bmc *kcs_bmc)
 {
-	return kcs_bmc_ipmi_event(kcs_bmc);
+	return kcs_bmc->client.ops->event(&kcs_bmc->client);
 }
 EXPORT_SYMBOL(kcs_bmc_handle_event);
 
@@ -60,4 +66,5 @@ EXPORT_SYMBOL(kcs_bmc_remove_device);
 
 MODULE_LICENSE("GPL v2");
 MODULE_AUTHOR("Haiyue Wang <haiyue.wang@linux.intel.com>");
+MODULE_AUTHOR("Andrew Jeffery <andrew@aj.id.au>");
 MODULE_DESCRIPTION("KCS BMC to handle the IPMI request from system software");
diff --git a/drivers/char/ipmi/kcs_bmc.h b/drivers/char/ipmi/kcs_bmc.h
index bf0ae327997f..a1350e567723 100644
--- a/drivers/char/ipmi/kcs_bmc.h
+++ b/drivers/char/ipmi/kcs_bmc.h
@@ -8,6 +8,15 @@
 
 #include <linux/miscdevice.h>
 
+#include "kcs_bmc_client.h"
+
+#define KCS_BMC_EVENT_NONE	0
+#define KCS_BMC_EVENT_HANDLED	1
+
+#define KCS_BMC_STR_OBF		BIT(0)
+#define KCS_BMC_STR_IBF		BIT(1)
+#define KCS_BMC_STR_CMD_DAT	BIT(3)
+
 /* Different phases of the KCS BMC module.
  *  KCS_PHASE_IDLE:
  *            BMC should not be expecting nor sending any data.
@@ -66,19 +75,21 @@ struct kcs_ioreg {
 	u32 str;
 };
 
+struct kcs_bmc_device_ops;
+
 struct kcs_bmc {
 	struct device *dev;
 
+	const struct kcs_bmc_device_ops *ops;
+
+	struct kcs_bmc_client client;
+
 	spinlock_t lock;
 
 	u32 channel;
 	int running;
 
-	/* Setup by BMC KCS controller driver */
 	struct kcs_ioreg ioreg;
-	u8 (*io_inputb)(struct kcs_bmc *kcs_bmc, u32 reg);
-	void (*io_outputb)(struct kcs_bmc *kcs_bmc, u32 reg, u8 b);
-	void (*io_updateb)(struct kcs_bmc *kcs_bmc, u32 reg, u8 mask, u8 val);
 
 	enum kcs_phases phase;
 	enum kcs_errors error;
@@ -97,15 +108,4 @@ struct kcs_bmc {
 
 	struct miscdevice miscdev;
 };
-
-int kcs_bmc_handle_event(struct kcs_bmc *kcs_bmc);
-int kcs_bmc_add_device(struct kcs_bmc *kcs_bmc);
-int kcs_bmc_remove_device(struct kcs_bmc *kcs_bmc);
-
-u8 kcs_bmc_read_data(struct kcs_bmc *kcs_bmc);
-void kcs_bmc_write_data(struct kcs_bmc *kcs_bmc, u8 data);
-u8 kcs_bmc_read_status(struct kcs_bmc *kcs_bmc);
-void kcs_bmc_write_status(struct kcs_bmc *kcs_bmc, u8 data);
-void kcs_bmc_update_status(struct kcs_bmc *kcs_bmc, u8 mask, u8 val);
-
 #endif /* __KCS_BMC_H__ */
diff --git a/drivers/char/ipmi/kcs_bmc_aspeed.c b/drivers/char/ipmi/kcs_bmc_aspeed.c
index 0416ac78ce68..1b313355b1c8 100644
--- a/drivers/char/ipmi/kcs_bmc_aspeed.c
+++ b/drivers/char/ipmi/kcs_bmc_aspeed.c
@@ -21,7 +21,7 @@
 #include <linux/slab.h>
 #include <linux/timer.h>
 
-#include "kcs_bmc.h"
+#include "kcs_bmc_device.h"
 
 
 #define DEVICE_NAME     "ast-kcs-bmc"
@@ -220,14 +220,22 @@ static void aspeed_kcs_enable_channel(struct kcs_bmc *kcs_bmc, bool enable)
 	}
 }
 
+static const struct kcs_bmc_device_ops aspeed_kcs_ops = {
+	.io_inputb = aspeed_kcs_inb,
+	.io_outputb = aspeed_kcs_outb,
+	.io_updateb = aspeed_kcs_updateb,
+};
+
 static irqreturn_t aspeed_kcs_irq(int irq, void *arg)
 {
 	struct kcs_bmc *kcs_bmc = arg;
+	int rc;
 
-	if (!kcs_bmc_handle_event(kcs_bmc))
-		return IRQ_HANDLED;
+	rc = kcs_bmc_handle_event(kcs_bmc);
+	if (rc < 0)
+		dev_warn(kcs_bmc->dev, "Failed to service irq: %d\n", rc);
 
-	return IRQ_NONE;
+	return rc == KCS_BMC_EVENT_HANDLED ? IRQ_HANDLED : IRQ_NONE;
 }
 
 static int aspeed_kcs_config_irq(struct kcs_bmc *kcs_bmc,
@@ -362,9 +370,7 @@ static int aspeed_kcs_probe(struct platform_device *pdev)
 	kcs_bmc->dev = &pdev->dev;
 	kcs_bmc->channel = channel;
 	kcs_bmc->ioreg = ast_kcs_bmc_ioregs[channel - 1];
-	kcs_bmc->io_inputb = aspeed_kcs_inb;
-	kcs_bmc->io_outputb = aspeed_kcs_outb;
-	kcs_bmc->io_updateb = aspeed_kcs_updateb;
+	kcs_bmc->ops = &aspeed_kcs_ops;
 
 	priv->map = syscon_node_to_regmap(pdev->dev.parent->of_node);
 	if (IS_ERR(priv->map)) {
diff --git a/drivers/char/ipmi/kcs_bmc_cdev_ipmi.c b/drivers/char/ipmi/kcs_bmc_cdev_ipmi.c
index 0ca71c135a1a..fd852d8abe48 100644
--- a/drivers/char/ipmi/kcs_bmc_cdev_ipmi.c
+++ b/drivers/char/ipmi/kcs_bmc_cdev_ipmi.c
@@ -22,7 +22,6 @@
 
 #define KCS_ZERO_DATA     0
 
-
 /* IPMI 2.0 - Table 9-1, KCS Interface Status Register Bits */
 #define KCS_STATUS_STATE(state) (state << 6)
 #define KCS_STATUS_STATE_MASK   GENMASK(7, 6)
@@ -179,12 +178,19 @@ static void kcs_bmc_ipmi_handle_cmd(struct kcs_bmc *kcs_bmc)
 	}
 }
 
-int kcs_bmc_ipmi_event(struct kcs_bmc *kcs_bmc);
-int kcs_bmc_ipmi_event(struct kcs_bmc *kcs_bmc)
+static inline struct kcs_bmc *client_to_kcs_bmc(struct kcs_bmc_client *client)
 {
+	return container_of(client, struct kcs_bmc, client);
+}
+
+static int kcs_bmc_ipmi_event(struct kcs_bmc_client *client)
+{
+	struct kcs_bmc *kcs_bmc;
 	unsigned long flags;
-	int ret = -ENODATA;
 	u8 status;
+	int ret;
+
+	kcs_bmc = client_to_kcs_bmc(client);
 
 	spin_lock_irqsave(&kcs_bmc->lock, flags);
 
@@ -197,23 +203,28 @@ int kcs_bmc_ipmi_event(struct kcs_bmc *kcs_bmc)
 		else
 			kcs_bmc_ipmi_handle_data(kcs_bmc);
 
-		ret = 0;
+		ret = KCS_BMC_EVENT_HANDLED;
+	} else {
+		ret = KCS_BMC_EVENT_NONE;
 	}
 
 	spin_unlock_irqrestore(&kcs_bmc->lock, flags);
 
 	return ret;
 }
-EXPORT_SYMBOL(kcs_bmc_ipmi_event);
 
-static inline struct kcs_bmc *to_kcs_bmc(struct file *filp)
+static const struct kcs_bmc_client_ops kcs_bmc_ipmi_client_ops = {
+	.event = kcs_bmc_ipmi_event,
+};
+
+static inline struct kcs_bmc *file_to_kcs_bmc(struct file *filp)
 {
 	return container_of(filp->private_data, struct kcs_bmc, miscdev);
 }
 
 static int kcs_bmc_ipmi_open(struct inode *inode, struct file *filp)
 {
-	struct kcs_bmc *kcs_bmc = to_kcs_bmc(filp);
+	struct kcs_bmc *kcs_bmc = file_to_kcs_bmc(filp);
 	int ret = 0;
 
 	spin_lock_irq(&kcs_bmc->lock);
@@ -228,7 +239,7 @@ static int kcs_bmc_ipmi_open(struct inode *inode, struct file *filp)
 
 static __poll_t kcs_bmc_ipmi_poll(struct file *filp, poll_table *wait)
 {
-	struct kcs_bmc *kcs_bmc = to_kcs_bmc(filp);
+	struct kcs_bmc *kcs_bmc = file_to_kcs_bmc(filp);
 	__poll_t mask = 0;
 
 	poll_wait(filp, &kcs_bmc->queue, wait);
@@ -244,7 +255,7 @@ static __poll_t kcs_bmc_ipmi_poll(struct file *filp, poll_table *wait)
 static ssize_t kcs_bmc_ipmi_read(struct file *filp, char __user *buf,
 			    size_t count, loff_t *ppos)
 {
-	struct kcs_bmc *kcs_bmc = to_kcs_bmc(filp);
+	struct kcs_bmc *kcs_bmc = file_to_kcs_bmc(filp);
 	bool data_avail;
 	size_t data_len;
 	ssize_t ret;
@@ -306,7 +317,7 @@ static ssize_t kcs_bmc_ipmi_read(struct file *filp, char __user *buf,
 static ssize_t kcs_bmc_ipmi_write(struct file *filp, const char __user *buf,
 			     size_t count, loff_t *ppos)
 {
-	struct kcs_bmc *kcs_bmc = to_kcs_bmc(filp);
+	struct kcs_bmc *kcs_bmc = file_to_kcs_bmc(filp);
 	ssize_t ret;
 
 	/* a minimum response size '3' : netfn + cmd + ccode */
@@ -342,7 +353,7 @@ static ssize_t kcs_bmc_ipmi_write(struct file *filp, const char __user *buf,
 static long kcs_bmc_ipmi_ioctl(struct file *filp, unsigned int cmd,
 			  unsigned long arg)
 {
-	struct kcs_bmc *kcs_bmc = to_kcs_bmc(filp);
+	struct kcs_bmc *kcs_bmc = file_to_kcs_bmc(filp);
 	long ret = 0;
 
 	spin_lock_irq(&kcs_bmc->lock);
@@ -372,7 +383,7 @@ static long kcs_bmc_ipmi_ioctl(struct file *filp, unsigned int cmd,
 
 static int kcs_bmc_ipmi_release(struct inode *inode, struct file *filp)
 {
-	struct kcs_bmc *kcs_bmc = to_kcs_bmc(filp);
+	struct kcs_bmc *kcs_bmc = file_to_kcs_bmc(filp);
 
 	spin_lock_irq(&kcs_bmc->lock);
 	kcs_bmc->running = 0;
@@ -401,6 +412,8 @@ int kcs_bmc_ipmi_attach_cdev(struct kcs_bmc *kcs_bmc)
 	mutex_init(&kcs_bmc->mutex);
 	init_waitqueue_head(&kcs_bmc->queue);
 
+	kcs_bmc->client.dev = kcs_bmc;
+	kcs_bmc->client.ops = &kcs_bmc_ipmi_client_ops;
 	kcs_bmc->data_in = devm_kmalloc(kcs_bmc->dev, KCS_MSG_BUFSIZ, GFP_KERNEL);
 	kcs_bmc->data_out = devm_kmalloc(kcs_bmc->dev, KCS_MSG_BUFSIZ, GFP_KERNEL);
 	kcs_bmc->kbuffer = devm_kmalloc(kcs_bmc->dev, KCS_MSG_BUFSIZ, GFP_KERNEL);
diff --git a/drivers/char/ipmi/kcs_bmc_client.h b/drivers/char/ipmi/kcs_bmc_client.h
new file mode 100644
index 000000000000..140631d157d8
--- /dev/null
+++ b/drivers/char/ipmi/kcs_bmc_client.h
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (c) 2021, IBM Corp. */
+
+#ifndef __KCS_BMC_CONSUMER_H__
+#define __KCS_BMC_CONSUMER_H__
+
+#include <linux/list.h>
+#include <linux/notifier.h>
+#include <stdbool.h>
+
+struct kcs_bmc;
+struct kcs_bmc_client_ops;
+
+struct kcs_bmc_client {
+	const struct kcs_bmc_client_ops *ops;
+
+	struct kcs_bmc *dev;
+};
+
+struct kcs_bmc_client_ops {
+	int (*event)(struct kcs_bmc_client *client);
+};
+
+u8 kcs_bmc_read_data(struct kcs_bmc *kcs_bmc);
+void kcs_bmc_write_data(struct kcs_bmc *kcs_bmc, u8 data);
+u8 kcs_bmc_read_status(struct kcs_bmc *kcs_bmc);
+void kcs_bmc_write_status(struct kcs_bmc *kcs_bmc, u8 data);
+void kcs_bmc_update_status(struct kcs_bmc *kcs_bmc, u8 mask, u8 val);
+#endif
diff --git a/drivers/char/ipmi/kcs_bmc_device.h b/drivers/char/ipmi/kcs_bmc_device.h
new file mode 100644
index 000000000000..33462174516d
--- /dev/null
+++ b/drivers/char/ipmi/kcs_bmc_device.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (c) 2021, IBM Corp. */
+
+#ifndef __KCS_BMC_DEVICE_H__
+#define __KCS_BMC_DEVICE_H__
+
+#include "kcs_bmc.h"
+
+struct kcs_bmc_device_ops {
+	u8 (*io_inputb)(struct kcs_bmc *kcs_bmc, u32 reg);
+	void (*io_outputb)(struct kcs_bmc *kcs_bmc, u32 reg, u8 b);
+	void (*io_updateb)(struct kcs_bmc *kcs_bmc, u32 reg, u8 mask, u8 b);
+};
+
+int kcs_bmc_handle_event(struct kcs_bmc *kcs_bmc);
+int kcs_bmc_add_device(struct kcs_bmc *kcs_bmc);
+int kcs_bmc_remove_device(struct kcs_bmc *kcs_bmc);
+
+#endif
diff --git a/drivers/char/ipmi/kcs_bmc_npcm7xx.c b/drivers/char/ipmi/kcs_bmc_npcm7xx.c
index 5d017498dc69..1d21697fc585 100644
--- a/drivers/char/ipmi/kcs_bmc_npcm7xx.c
+++ b/drivers/char/ipmi/kcs_bmc_npcm7xx.c
@@ -17,7 +17,7 @@
 #include <linux/regmap.h>
 #include <linux/slab.h>
 
-#include "kcs_bmc.h"
+#include "kcs_bmc_device.h"
 
 #define DEVICE_NAME	"npcm-kcs-bmc"
 #define KCS_CHANNEL_MAX	3
@@ -127,11 +127,13 @@ static void npcm7xx_kcs_enable_channel(struct kcs_bmc *kcs_bmc, bool enable)
 static irqreturn_t npcm7xx_kcs_irq(int irq, void *arg)
 {
 	struct kcs_bmc *kcs_bmc = arg;
+	int rc;
 
-	if (!kcs_bmc_handle_event(kcs_bmc))
-		return IRQ_HANDLED;
+	rc = kcs_bmc_handle_event(kcs_bmc);
+	if (rc < 0)
+		dev_warn(kcs_bmc->dev, "Failed to service irq: %d\n", rc);
 
-	return IRQ_NONE;
+	return rc == KCS_BMC_EVENT_HANDLED ? IRQ_HANDLED : IRQ_NONE;
 }
 
 static int npcm7xx_kcs_config_irq(struct kcs_bmc *kcs_bmc,
@@ -148,6 +150,12 @@ static int npcm7xx_kcs_config_irq(struct kcs_bmc *kcs_bmc,
 				dev_name(dev), kcs_bmc);
 }
 
+static const struct kcs_bmc_device_ops npcm7xx_kcs_ops = {
+	.io_inputb = npcm7xx_kcs_inb,
+	.io_outputb = npcm7xx_kcs_outb,
+	.io_updateb = npcm7xx_kcs_updateb,
+};
+
 static int npcm7xx_kcs_probe(struct platform_device *pdev)
 {
 	struct device *dev = &pdev->dev;
@@ -179,9 +187,7 @@ static int npcm7xx_kcs_probe(struct platform_device *pdev)
 	kcs_bmc->ioreg.idr = priv->reg->dib;
 	kcs_bmc->ioreg.odr = priv->reg->dob;
 	kcs_bmc->ioreg.str = priv->reg->sts;
-	kcs_bmc->io_inputb = npcm7xx_kcs_inb;
-	kcs_bmc->io_outputb = npcm7xx_kcs_outb;
-	kcs_bmc->io_updateb = npcm7xx_kcs_updateb;
+	kcs_bmc->ops = &npcm7xx_kcs_ops;
 
 	platform_set_drvdata(pdev, priv);
 
-- 
2.27.0


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

* [PATCH v2 12/21] ipmi: kcs_bmc: Strip private client data from struct kcs_bmc
  2021-03-19  6:27 [PATCH v2 01/21] dt-bindings: aspeed-lpc: Remove LPC partitioning Andrew Jeffery
                   ` (9 preceding siblings ...)
  2021-03-19  6:27 ` [PATCH v2 11/21] ipmi: kcs_bmc: Split headers into device and client Andrew Jeffery
@ 2021-03-19  6:27 ` Andrew Jeffery
  2021-04-09  4:07   ` Zev Weiss
  2021-03-19  6:27 ` [PATCH v2 13/21] ipmi: kcs_bmc: Decouple the IPMI chardev from the core Andrew Jeffery
                   ` (9 subsequent siblings)
  20 siblings, 1 reply; 67+ messages in thread
From: Andrew Jeffery @ 2021-03-19  6:27 UTC (permalink / raw)
  To: openipmi-developer, openbmc, minyard
  Cc: devicetree, ryan_chen, tmaimon77, linux-aspeed, avifishman70,
	venture, linus.walleij, linux-kernel, tali.perry1, linux-gpio,
	robh+dt, lee.jones, chiawei_wang, linux-arm-kernel, benjaminfair

Move all client-private data out of `struct kcs_bmc` into the KCS client
implementation.

With this change the KCS BMC core code now only concerns itself with
abstract `struct kcs_bmc` and `struct kcs_bmc_client` types, achieving
expected separation of concerns. Further, the change clears the path for
implementation of alternative userspace interfaces.

The chardev data-structures are rearranged in the same manner applied to
the KCS device driver data-structures in an earlier patch - `struct
kcs_bmc_client` is embedded in the client's private data and we exploit
container_of() to translate as required.

Finally, now that it is free of client data, `struct kcs_bmc` is renamed
to `struct kcs_bmc_device` to contrast `struct kcs_bmc_client`.

Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
---
 drivers/char/ipmi/kcs_bmc.c           |  68 +++-
 drivers/char/ipmi/kcs_bmc.h           |  86 +-----
 drivers/char/ipmi/kcs_bmc_aspeed.c    |  22 +-
 drivers/char/ipmi/kcs_bmc_cdev_ipmi.c | 428 ++++++++++++++++----------
 drivers/char/ipmi/kcs_bmc_client.h    |  28 +-
 drivers/char/ipmi/kcs_bmc_device.h    |  12 +-
 drivers/char/ipmi/kcs_bmc_npcm7xx.c   |  20 +-
 7 files changed, 368 insertions(+), 296 deletions(-)

diff --git a/drivers/char/ipmi/kcs_bmc.c b/drivers/char/ipmi/kcs_bmc.c
index 1046ce2bbefc..266ebec71d6f 100644
--- a/drivers/char/ipmi/kcs_bmc.c
+++ b/drivers/char/ipmi/kcs_bmc.c
@@ -4,6 +4,7 @@
  * Copyright (c) 2021, IBM Corp.
  */
 
+#include <linux/device.h>
 #include <linux/module.h>
 
 #include "kcs_bmc.h"
@@ -14,51 +15,96 @@
 
 /* Consumer data access */
 
-u8 kcs_bmc_read_data(struct kcs_bmc *kcs_bmc)
+u8 kcs_bmc_read_data(struct kcs_bmc_device *kcs_bmc)
 {
 	return kcs_bmc->ops->io_inputb(kcs_bmc, kcs_bmc->ioreg.idr);
 }
 EXPORT_SYMBOL(kcs_bmc_read_data);
 
-void kcs_bmc_write_data(struct kcs_bmc *kcs_bmc, u8 data)
+void kcs_bmc_write_data(struct kcs_bmc_device *kcs_bmc, u8 data)
 {
 	kcs_bmc->ops->io_outputb(kcs_bmc, kcs_bmc->ioreg.odr, data);
 }
 EXPORT_SYMBOL(kcs_bmc_write_data);
 
-u8 kcs_bmc_read_status(struct kcs_bmc *kcs_bmc)
+u8 kcs_bmc_read_status(struct kcs_bmc_device *kcs_bmc)
 {
 	return kcs_bmc->ops->io_inputb(kcs_bmc, kcs_bmc->ioreg.str);
 }
 EXPORT_SYMBOL(kcs_bmc_read_status);
 
-void kcs_bmc_write_status(struct kcs_bmc *kcs_bmc, u8 data)
+void kcs_bmc_write_status(struct kcs_bmc_device *kcs_bmc, u8 data)
 {
 	kcs_bmc->ops->io_outputb(kcs_bmc, kcs_bmc->ioreg.str, data);
 }
 EXPORT_SYMBOL(kcs_bmc_write_status);
 
-void kcs_bmc_update_status(struct kcs_bmc *kcs_bmc, u8 mask, u8 val)
+void kcs_bmc_update_status(struct kcs_bmc_device *kcs_bmc, u8 mask, u8 val)
 {
 	kcs_bmc->ops->io_updateb(kcs_bmc, kcs_bmc->ioreg.str, mask, val);
 }
 EXPORT_SYMBOL(kcs_bmc_update_status);
 
-int kcs_bmc_handle_event(struct kcs_bmc *kcs_bmc)
+int kcs_bmc_handle_event(struct kcs_bmc_device *kcs_bmc)
 {
-	return kcs_bmc->client.ops->event(&kcs_bmc->client);
+	struct kcs_bmc_client *client;
+	int rc;
+
+	spin_lock(&kcs_bmc->lock);
+	client = kcs_bmc->client;
+	if (client) {
+		rc = client->ops->event(client);
+	} else {
+		u8 status;
+
+		status = kcs_bmc_read_status(kcs_bmc);
+		if (status & KCS_BMC_STR_IBF) {
+			/* Ack the event by reading the data */
+			kcs_bmc_read_data(kcs_bmc);
+			rc = KCS_BMC_EVENT_HANDLED;
+		} else {
+			rc = KCS_BMC_EVENT_NONE;
+		}
+	}
+	spin_unlock(&kcs_bmc->lock);
+
+	return rc;
 }
 EXPORT_SYMBOL(kcs_bmc_handle_event);
 
-int kcs_bmc_ipmi_attach_cdev(struct kcs_bmc *kcs_bmc);
-int kcs_bmc_add_device(struct kcs_bmc *kcs_bmc)
+int kcs_bmc_enable_device(struct kcs_bmc_device *kcs_bmc, struct kcs_bmc_client *client)
+{
+	int rc;
+
+	spin_lock_irq(&kcs_bmc->lock);
+	if (kcs_bmc->client) {
+		rc = -EBUSY;
+	} else {
+		kcs_bmc->client = client;
+		rc = 0;
+	}
+	spin_unlock_irq(&kcs_bmc->lock);
+
+	return rc;
+}
+EXPORT_SYMBOL(kcs_bmc_enable_device);
+
+void kcs_bmc_disable_device(struct kcs_bmc_device *kcs_bmc, struct kcs_bmc_client *client)
+{
+	spin_lock_irq(&kcs_bmc->lock);
+	if (client == kcs_bmc->client)
+		kcs_bmc->client = NULL;
+	spin_unlock_irq(&kcs_bmc->lock);
+}
+EXPORT_SYMBOL(kcs_bmc_disable_device);
+
+int kcs_bmc_add_device(struct kcs_bmc_device *kcs_bmc)
 {
 	return kcs_bmc_ipmi_attach_cdev(kcs_bmc);
 }
 EXPORT_SYMBOL(kcs_bmc_add_device);
 
-int kcs_bmc_ipmi_detach_cdev(struct kcs_bmc *kcs_bmc);
-int kcs_bmc_remove_device(struct kcs_bmc *kcs_bmc)
+int kcs_bmc_remove_device(struct kcs_bmc_device *kcs_bmc)
 {
 	return kcs_bmc_ipmi_detach_cdev(kcs_bmc);
 }
diff --git a/drivers/char/ipmi/kcs_bmc.h b/drivers/char/ipmi/kcs_bmc.h
index a1350e567723..3f266740c759 100644
--- a/drivers/char/ipmi/kcs_bmc.h
+++ b/drivers/char/ipmi/kcs_bmc.h
@@ -6,9 +6,7 @@
 #ifndef __KCS_BMC_H__
 #define __KCS_BMC_H__
 
-#include <linux/miscdevice.h>
-
-#include "kcs_bmc_client.h"
+#include <linux/list.h>
 
 #define KCS_BMC_EVENT_NONE	0
 #define KCS_BMC_EVENT_HANDLED	1
@@ -17,53 +15,6 @@
 #define KCS_BMC_STR_IBF		BIT(1)
 #define KCS_BMC_STR_CMD_DAT	BIT(3)
 
-/* Different phases of the KCS BMC module.
- *  KCS_PHASE_IDLE:
- *            BMC should not be expecting nor sending any data.
- *  KCS_PHASE_WRITE_START:
- *            BMC is receiving a WRITE_START command from system software.
- *  KCS_PHASE_WRITE_DATA:
- *            BMC is receiving a data byte from system software.
- *  KCS_PHASE_WRITE_END_CMD:
- *            BMC is waiting a last data byte from system software.
- *  KCS_PHASE_WRITE_DONE:
- *            BMC has received the whole request from system software.
- *  KCS_PHASE_WAIT_READ:
- *            BMC is waiting the response from the upper IPMI service.
- *  KCS_PHASE_READ:
- *            BMC is transferring the response to system software.
- *  KCS_PHASE_ABORT_ERROR1:
- *            BMC is waiting error status request from system software.
- *  KCS_PHASE_ABORT_ERROR2:
- *            BMC is waiting for idle status afer error from system software.
- *  KCS_PHASE_ERROR:
- *            BMC has detected a protocol violation at the interface level.
- */
-enum kcs_phases {
-	KCS_PHASE_IDLE,
-
-	KCS_PHASE_WRITE_START,
-	KCS_PHASE_WRITE_DATA,
-	KCS_PHASE_WRITE_END_CMD,
-	KCS_PHASE_WRITE_DONE,
-
-	KCS_PHASE_WAIT_READ,
-	KCS_PHASE_READ,
-
-	KCS_PHASE_ABORT_ERROR1,
-	KCS_PHASE_ABORT_ERROR2,
-	KCS_PHASE_ERROR
-};
-
-/* IPMI 2.0 - Table 9-4, KCS Interface Status Codes */
-enum kcs_errors {
-	KCS_NO_ERROR                = 0x00,
-	KCS_ABORTED_BY_COMMAND      = 0x01,
-	KCS_ILLEGAL_CONTROL_CODE    = 0x02,
-	KCS_LENGTH_ERROR            = 0x06,
-	KCS_UNSPECIFIED_ERROR       = 0xFF
-};
-
 /* IPMI 2.0 - 9.5, KCS Interface Registers
  * @idr: Input Data Register
  * @odr: Output Data Register
@@ -76,36 +27,23 @@ struct kcs_ioreg {
 };
 
 struct kcs_bmc_device_ops;
+struct kcs_bmc_client;
+
+struct kcs_bmc_device {
+	struct list_head entry;
 
-struct kcs_bmc {
 	struct device *dev;
-
-	const struct kcs_bmc_device_ops *ops;
-
-	struct kcs_bmc_client client;
-
-	spinlock_t lock;
-
 	u32 channel;
-	int running;
 
 	struct kcs_ioreg ioreg;
 
-	enum kcs_phases phase;
-	enum kcs_errors error;
+	const struct kcs_bmc_device_ops *ops;
 
-	wait_queue_head_t queue;
-	bool data_in_avail;
-	int  data_in_idx;
-	u8  *data_in;
-
-	int  data_out_idx;
-	int  data_out_len;
-	u8  *data_out;
-
-	struct mutex mutex;
-	u8 *kbuffer;
-
-	struct miscdevice miscdev;
+	spinlock_t lock;
+	struct kcs_bmc_client *client;
 };
+
+/* Temporary exports while refactoring */
+int kcs_bmc_ipmi_attach_cdev(struct kcs_bmc_device *kcs_bmc);
+int kcs_bmc_ipmi_detach_cdev(struct kcs_bmc_device *kcs_bmc);
 #endif /* __KCS_BMC_H__ */
diff --git a/drivers/char/ipmi/kcs_bmc_aspeed.c b/drivers/char/ipmi/kcs_bmc_aspeed.c
index 1b313355b1c8..6f26e7366c0b 100644
--- a/drivers/char/ipmi/kcs_bmc_aspeed.c
+++ b/drivers/char/ipmi/kcs_bmc_aspeed.c
@@ -61,7 +61,7 @@
 #define LPC_STR4             0x11C
 
 struct aspeed_kcs_bmc {
-	struct kcs_bmc kcs_bmc;
+	struct kcs_bmc_device kcs_bmc;
 
 	struct regmap *map;
 };
@@ -71,12 +71,12 @@ struct aspeed_kcs_of_ops {
 	int (*get_io_address)(struct platform_device *pdev);
 };
 
-static inline struct aspeed_kcs_bmc *to_aspeed_kcs_bmc(struct kcs_bmc *kcs_bmc)
+static inline struct aspeed_kcs_bmc *to_aspeed_kcs_bmc(struct kcs_bmc_device *kcs_bmc)
 {
 	return container_of(kcs_bmc, struct aspeed_kcs_bmc, kcs_bmc);
 }
 
-static u8 aspeed_kcs_inb(struct kcs_bmc *kcs_bmc, u32 reg)
+static u8 aspeed_kcs_inb(struct kcs_bmc_device *kcs_bmc, u32 reg)
 {
 	struct aspeed_kcs_bmc *priv = to_aspeed_kcs_bmc(kcs_bmc);
 	u32 val = 0;
@@ -88,7 +88,7 @@ static u8 aspeed_kcs_inb(struct kcs_bmc *kcs_bmc, u32 reg)
 	return rc == 0 ? (u8) val : 0;
 }
 
-static void aspeed_kcs_outb(struct kcs_bmc *kcs_bmc, u32 reg, u8 data)
+static void aspeed_kcs_outb(struct kcs_bmc_device *kcs_bmc, u32 reg, u8 data)
 {
 	struct aspeed_kcs_bmc *priv = to_aspeed_kcs_bmc(kcs_bmc);
 	int rc;
@@ -97,7 +97,7 @@ static void aspeed_kcs_outb(struct kcs_bmc *kcs_bmc, u32 reg, u8 data)
 	WARN(rc != 0, "regmap_write() failed: %d\n", rc);
 }
 
-static void aspeed_kcs_updateb(struct kcs_bmc *kcs_bmc, u32 reg, u8 mask, u8 val)
+static void aspeed_kcs_updateb(struct kcs_bmc_device *kcs_bmc, u32 reg, u8 mask, u8 val)
 {
 	struct aspeed_kcs_bmc *priv = to_aspeed_kcs_bmc(kcs_bmc);
 	int rc;
@@ -119,7 +119,7 @@ static void aspeed_kcs_updateb(struct kcs_bmc *kcs_bmc, u32 reg, u8 mask, u8 val
  *     C. KCS4
  *        D / C : CA4h / CA5h
  */
-static void aspeed_kcs_set_address(struct kcs_bmc *kcs_bmc, u16 addr)
+static void aspeed_kcs_set_address(struct kcs_bmc_device *kcs_bmc, u16 addr)
 {
 	struct aspeed_kcs_bmc *priv = to_aspeed_kcs_bmc(kcs_bmc);
 
@@ -153,7 +153,7 @@ static void aspeed_kcs_set_address(struct kcs_bmc *kcs_bmc, u16 addr)
 	}
 }
 
-static void aspeed_kcs_enable_channel(struct kcs_bmc *kcs_bmc, bool enable)
+static void aspeed_kcs_enable_channel(struct kcs_bmc_device *kcs_bmc, bool enable)
 {
 	struct aspeed_kcs_bmc *priv = to_aspeed_kcs_bmc(kcs_bmc);
 
@@ -228,7 +228,7 @@ static const struct kcs_bmc_device_ops aspeed_kcs_ops = {
 
 static irqreturn_t aspeed_kcs_irq(int irq, void *arg)
 {
-	struct kcs_bmc *kcs_bmc = arg;
+	struct kcs_bmc_device *kcs_bmc = arg;
 	int rc;
 
 	rc = kcs_bmc_handle_event(kcs_bmc);
@@ -238,7 +238,7 @@ static irqreturn_t aspeed_kcs_irq(int irq, void *arg)
 	return rc == KCS_BMC_EVENT_HANDLED ? IRQ_HANDLED : IRQ_NONE;
 }
 
-static int aspeed_kcs_config_irq(struct kcs_bmc *kcs_bmc,
+static int aspeed_kcs_config_irq(struct kcs_bmc_device *kcs_bmc,
 			struct platform_device *pdev)
 {
 	struct device *dev = &pdev->dev;
@@ -338,8 +338,8 @@ static int aspeed_kcs_of_v2_get_io_address(struct platform_device *pdev)
 static int aspeed_kcs_probe(struct platform_device *pdev)
 {
 	const struct aspeed_kcs_of_ops *ops;
+	struct kcs_bmc_device *kcs_bmc;
 	struct aspeed_kcs_bmc *priv;
-	struct kcs_bmc *kcs_bmc;
 	struct device_node *np;
 	int rc, channel, addr;
 
@@ -400,7 +400,7 @@ static int aspeed_kcs_probe(struct platform_device *pdev)
 static int aspeed_kcs_remove(struct platform_device *pdev)
 {
 	struct aspeed_kcs_bmc *priv = platform_get_drvdata(pdev);
-	struct kcs_bmc *kcs_bmc = &priv->kcs_bmc;
+	struct kcs_bmc_device *kcs_bmc = &priv->kcs_bmc;
 
 	kcs_bmc_remove_device(kcs_bmc);
 
diff --git a/drivers/char/ipmi/kcs_bmc_cdev_ipmi.c b/drivers/char/ipmi/kcs_bmc_cdev_ipmi.c
index fd852d8abe48..58c42e76483d 100644
--- a/drivers/char/ipmi/kcs_bmc_cdev_ipmi.c
+++ b/drivers/char/ipmi/kcs_bmc_cdev_ipmi.c
@@ -8,13 +8,88 @@
 #include <linux/errno.h>
 #include <linux/io.h>
 #include <linux/ipmi_bmc.h>
+#include <linux/list.h>
+#include <linux/miscdevice.h>
 #include <linux/module.h>
+#include <linux/mutex.h>
 #include <linux/platform_device.h>
 #include <linux/poll.h>
 #include <linux/sched.h>
 #include <linux/slab.h>
 
-#include "kcs_bmc.h"
+#include "kcs_bmc_client.h"
+
+/* Different phases of the KCS BMC module.
+ *  KCS_PHASE_IDLE:
+ *            BMC should not be expecting nor sending any data.
+ *  KCS_PHASE_WRITE_START:
+ *            BMC is receiving a WRITE_START command from system software.
+ *  KCS_PHASE_WRITE_DATA:
+ *            BMC is receiving a data byte from system software.
+ *  KCS_PHASE_WRITE_END_CMD:
+ *            BMC is waiting a last data byte from system software.
+ *  KCS_PHASE_WRITE_DONE:
+ *            BMC has received the whole request from system software.
+ *  KCS_PHASE_WAIT_READ:
+ *            BMC is waiting the response from the upper IPMI service.
+ *  KCS_PHASE_READ:
+ *            BMC is transferring the response to system software.
+ *  KCS_PHASE_ABORT_ERROR1:
+ *            BMC is waiting error status request from system software.
+ *  KCS_PHASE_ABORT_ERROR2:
+ *            BMC is waiting for idle status afer error from system software.
+ *  KCS_PHASE_ERROR:
+ *            BMC has detected a protocol violation at the interface level.
+ */
+enum kcs_ipmi_phases {
+	KCS_PHASE_IDLE,
+
+	KCS_PHASE_WRITE_START,
+	KCS_PHASE_WRITE_DATA,
+	KCS_PHASE_WRITE_END_CMD,
+	KCS_PHASE_WRITE_DONE,
+
+	KCS_PHASE_WAIT_READ,
+	KCS_PHASE_READ,
+
+	KCS_PHASE_ABORT_ERROR1,
+	KCS_PHASE_ABORT_ERROR2,
+	KCS_PHASE_ERROR
+};
+
+/* IPMI 2.0 - Table 9-4, KCS Interface Status Codes */
+enum kcs_ipmi_errors {
+	KCS_NO_ERROR                = 0x00,
+	KCS_ABORTED_BY_COMMAND      = 0x01,
+	KCS_ILLEGAL_CONTROL_CODE    = 0x02,
+	KCS_LENGTH_ERROR            = 0x06,
+	KCS_UNSPECIFIED_ERROR       = 0xFF
+};
+
+struct kcs_bmc_ipmi {
+	struct list_head entry;
+
+	struct kcs_bmc_client client;
+
+	spinlock_t lock;
+
+	enum kcs_ipmi_phases phase;
+	enum kcs_ipmi_errors error;
+
+	wait_queue_head_t queue;
+	bool data_in_avail;
+	int  data_in_idx;
+	u8  *data_in;
+
+	int  data_out_idx;
+	int  data_out_len;
+	u8  *data_out;
+
+	struct mutex mutex;
+	u8 *kbuffer;
+
+	struct miscdevice miscdev;
+};
 
 #define DEVICE_NAME "ipmi-kcs"
 
@@ -44,171 +119,169 @@ enum kcs_states {
 #define KCS_CMD_WRITE_END         0x62
 #define KCS_CMD_READ_BYTE         0x68
 
-static inline void set_state(struct kcs_bmc *kcs_bmc, u8 state)
+static inline void set_state(struct kcs_bmc_ipmi *priv, u8 state)
 {
-	kcs_bmc_update_status(kcs_bmc, KCS_STATUS_STATE_MASK,
-					KCS_STATUS_STATE(state));
+	kcs_bmc_update_status(priv->client.dev, KCS_STATUS_STATE_MASK, KCS_STATUS_STATE(state));
 }
 
-static void kcs_bmc_ipmi_force_abort(struct kcs_bmc *kcs_bmc)
+static void kcs_bmc_ipmi_force_abort(struct kcs_bmc_ipmi *priv)
 {
-	set_state(kcs_bmc, ERROR_STATE);
-	kcs_bmc_read_data(kcs_bmc);
-	kcs_bmc_write_data(kcs_bmc, KCS_ZERO_DATA);
+	set_state(priv, ERROR_STATE);
+	kcs_bmc_read_data(priv->client.dev);
+	kcs_bmc_write_data(priv->client.dev, KCS_ZERO_DATA);
 
-	kcs_bmc->phase = KCS_PHASE_ERROR;
-	kcs_bmc->data_in_avail = false;
-	kcs_bmc->data_in_idx = 0;
+	priv->phase = KCS_PHASE_ERROR;
+	priv->data_in_avail = false;
+	priv->data_in_idx = 0;
 }
 
-static void kcs_bmc_ipmi_handle_data(struct kcs_bmc *kcs_bmc)
+static void kcs_bmc_ipmi_handle_data(struct kcs_bmc_ipmi *priv)
 {
+	struct kcs_bmc_device *dev;
 	u8 data;
 
-	switch (kcs_bmc->phase) {
+	dev = priv->client.dev;
+
+	switch (priv->phase) {
 	case KCS_PHASE_WRITE_START:
-		kcs_bmc->phase = KCS_PHASE_WRITE_DATA;
+		priv->phase = KCS_PHASE_WRITE_DATA;
 		fallthrough;
 
 	case KCS_PHASE_WRITE_DATA:
-		if (kcs_bmc->data_in_idx < KCS_MSG_BUFSIZ) {
-			set_state(kcs_bmc, WRITE_STATE);
-			kcs_bmc_write_data(kcs_bmc, KCS_ZERO_DATA);
-			kcs_bmc->data_in[kcs_bmc->data_in_idx++] =
-						kcs_bmc_read_data(kcs_bmc);
+		if (priv->data_in_idx < KCS_MSG_BUFSIZ) {
+			set_state(priv, WRITE_STATE);
+			kcs_bmc_write_data(dev, KCS_ZERO_DATA);
+			priv->data_in[priv->data_in_idx++] = kcs_bmc_read_data(dev);
 		} else {
-			kcs_bmc_ipmi_force_abort(kcs_bmc);
-			kcs_bmc->error = KCS_LENGTH_ERROR;
+			kcs_bmc_ipmi_force_abort(priv);
+			priv->error = KCS_LENGTH_ERROR;
 		}
 		break;
 
 	case KCS_PHASE_WRITE_END_CMD:
-		if (kcs_bmc->data_in_idx < KCS_MSG_BUFSIZ) {
-			set_state(kcs_bmc, READ_STATE);
-			kcs_bmc->data_in[kcs_bmc->data_in_idx++] =
-						kcs_bmc_read_data(kcs_bmc);
-			kcs_bmc->phase = KCS_PHASE_WRITE_DONE;
-			kcs_bmc->data_in_avail = true;
-			wake_up_interruptible(&kcs_bmc->queue);
+		if (priv->data_in_idx < KCS_MSG_BUFSIZ) {
+			set_state(priv, READ_STATE);
+			priv->data_in[priv->data_in_idx++] = kcs_bmc_read_data(dev);
+			priv->phase = KCS_PHASE_WRITE_DONE;
+			priv->data_in_avail = true;
+			wake_up_interruptible(&priv->queue);
 		} else {
-			kcs_bmc_ipmi_force_abort(kcs_bmc);
-			kcs_bmc->error = KCS_LENGTH_ERROR;
+			kcs_bmc_ipmi_force_abort(priv);
+			priv->error = KCS_LENGTH_ERROR;
 		}
 		break;
 
 	case KCS_PHASE_READ:
-		if (kcs_bmc->data_out_idx == kcs_bmc->data_out_len)
-			set_state(kcs_bmc, IDLE_STATE);
+		if (priv->data_out_idx == priv->data_out_len)
+			set_state(priv, IDLE_STATE);
 
-		data = kcs_bmc_read_data(kcs_bmc);
+		data = kcs_bmc_read_data(dev);
 		if (data != KCS_CMD_READ_BYTE) {
-			set_state(kcs_bmc, ERROR_STATE);
-			kcs_bmc_write_data(kcs_bmc, KCS_ZERO_DATA);
+			set_state(priv, ERROR_STATE);
+			kcs_bmc_write_data(dev, KCS_ZERO_DATA);
 			break;
 		}
 
-		if (kcs_bmc->data_out_idx == kcs_bmc->data_out_len) {
-			kcs_bmc_write_data(kcs_bmc, KCS_ZERO_DATA);
-			kcs_bmc->phase = KCS_PHASE_IDLE;
+		if (priv->data_out_idx == priv->data_out_len) {
+			kcs_bmc_write_data(dev, KCS_ZERO_DATA);
+			priv->phase = KCS_PHASE_IDLE;
 			break;
 		}
 
-		kcs_bmc_write_data(kcs_bmc,
-			kcs_bmc->data_out[kcs_bmc->data_out_idx++]);
+		kcs_bmc_write_data(dev, priv->data_out[priv->data_out_idx++]);
 		break;
 
 	case KCS_PHASE_ABORT_ERROR1:
-		set_state(kcs_bmc, READ_STATE);
-		kcs_bmc_read_data(kcs_bmc);
-		kcs_bmc_write_data(kcs_bmc, kcs_bmc->error);
-		kcs_bmc->phase = KCS_PHASE_ABORT_ERROR2;
+		set_state(priv, READ_STATE);
+		kcs_bmc_read_data(dev);
+		kcs_bmc_write_data(dev, priv->error);
+		priv->phase = KCS_PHASE_ABORT_ERROR2;
 		break;
 
 	case KCS_PHASE_ABORT_ERROR2:
-		set_state(kcs_bmc, IDLE_STATE);
-		kcs_bmc_read_data(kcs_bmc);
-		kcs_bmc_write_data(kcs_bmc, KCS_ZERO_DATA);
-		kcs_bmc->phase = KCS_PHASE_IDLE;
+		set_state(priv, IDLE_STATE);
+		kcs_bmc_read_data(dev);
+		kcs_bmc_write_data(dev, KCS_ZERO_DATA);
+		priv->phase = KCS_PHASE_IDLE;
 		break;
 
 	default:
-		kcs_bmc_ipmi_force_abort(kcs_bmc);
+		kcs_bmc_ipmi_force_abort(priv);
 		break;
 	}
 }
 
-static void kcs_bmc_ipmi_handle_cmd(struct kcs_bmc *kcs_bmc)
+static void kcs_bmc_ipmi_handle_cmd(struct kcs_bmc_ipmi *priv)
 {
 	u8 cmd;
 
-	set_state(kcs_bmc, WRITE_STATE);
-	kcs_bmc_write_data(kcs_bmc, KCS_ZERO_DATA);
+	set_state(priv, WRITE_STATE);
+	kcs_bmc_write_data(priv->client.dev, KCS_ZERO_DATA);
 
-	cmd = kcs_bmc_read_data(kcs_bmc);
+	cmd = kcs_bmc_read_data(priv->client.dev);
 	switch (cmd) {
 	case KCS_CMD_WRITE_START:
-		kcs_bmc->phase = KCS_PHASE_WRITE_START;
-		kcs_bmc->error = KCS_NO_ERROR;
-		kcs_bmc->data_in_avail = false;
-		kcs_bmc->data_in_idx = 0;
+		priv->phase = KCS_PHASE_WRITE_START;
+		priv->error = KCS_NO_ERROR;
+		priv->data_in_avail = false;
+		priv->data_in_idx = 0;
 		break;
 
 	case KCS_CMD_WRITE_END:
-		if (kcs_bmc->phase != KCS_PHASE_WRITE_DATA) {
-			kcs_bmc_ipmi_force_abort(kcs_bmc);
+		if (priv->phase != KCS_PHASE_WRITE_DATA) {
+			kcs_bmc_ipmi_force_abort(priv);
 			break;
 		}
 
-		kcs_bmc->phase = KCS_PHASE_WRITE_END_CMD;
+		priv->phase = KCS_PHASE_WRITE_END_CMD;
 		break;
 
 	case KCS_CMD_GET_STATUS_ABORT:
-		if (kcs_bmc->error == KCS_NO_ERROR)
-			kcs_bmc->error = KCS_ABORTED_BY_COMMAND;
+		if (priv->error == KCS_NO_ERROR)
+			priv->error = KCS_ABORTED_BY_COMMAND;
 
-		kcs_bmc->phase = KCS_PHASE_ABORT_ERROR1;
-		kcs_bmc->data_in_avail = false;
-		kcs_bmc->data_in_idx = 0;
+		priv->phase = KCS_PHASE_ABORT_ERROR1;
+		priv->data_in_avail = false;
+		priv->data_in_idx = 0;
 		break;
 
 	default:
-		kcs_bmc_ipmi_force_abort(kcs_bmc);
-		kcs_bmc->error = KCS_ILLEGAL_CONTROL_CODE;
+		kcs_bmc_ipmi_force_abort(priv);
+		priv->error = KCS_ILLEGAL_CONTROL_CODE;
 		break;
 	}
 }
 
-static inline struct kcs_bmc *client_to_kcs_bmc(struct kcs_bmc_client *client)
+static inline struct kcs_bmc_ipmi *client_to_kcs_bmc_ipmi(struct kcs_bmc_client *client)
 {
-	return container_of(client, struct kcs_bmc, client);
+	return container_of(client, struct kcs_bmc_ipmi, client);
 }
 
 static int kcs_bmc_ipmi_event(struct kcs_bmc_client *client)
 {
-	struct kcs_bmc *kcs_bmc;
-	unsigned long flags;
+	struct kcs_bmc_ipmi *priv;
 	u8 status;
 	int ret;
 
-	kcs_bmc = client_to_kcs_bmc(client);
+	priv = client_to_kcs_bmc_ipmi(client);
+	if (!priv)
+		return KCS_BMC_EVENT_NONE;
 
-	spin_lock_irqsave(&kcs_bmc->lock, flags);
+	spin_lock(&priv->lock);
 
-	status = kcs_bmc_read_status(kcs_bmc);
+	status = kcs_bmc_read_status(client->dev);
 	if (status & KCS_STATUS_IBF) {
-		if (!kcs_bmc->running)
-			kcs_bmc_ipmi_force_abort(kcs_bmc);
-		else if (status & KCS_STATUS_CMD_DAT)
-			kcs_bmc_ipmi_handle_cmd(kcs_bmc);
+		if (status & KCS_STATUS_CMD_DAT)
+			kcs_bmc_ipmi_handle_cmd(priv);
 		else
-			kcs_bmc_ipmi_handle_data(kcs_bmc);
+			kcs_bmc_ipmi_handle_data(priv);
 
 		ret = KCS_BMC_EVENT_HANDLED;
 	} else {
 		ret = KCS_BMC_EVENT_NONE;
 	}
 
-	spin_unlock_irqrestore(&kcs_bmc->lock, flags);
+	spin_unlock(&priv->lock);
 
 	return ret;
 }
@@ -217,37 +290,29 @@ static const struct kcs_bmc_client_ops kcs_bmc_ipmi_client_ops = {
 	.event = kcs_bmc_ipmi_event,
 };
 
-static inline struct kcs_bmc *file_to_kcs_bmc(struct file *filp)
+static inline struct kcs_bmc_ipmi *to_kcs_bmc(struct file *filp)
 {
-	return container_of(filp->private_data, struct kcs_bmc, miscdev);
+	return container_of(filp->private_data, struct kcs_bmc_ipmi, miscdev);
 }
 
 static int kcs_bmc_ipmi_open(struct inode *inode, struct file *filp)
 {
-	struct kcs_bmc *kcs_bmc = file_to_kcs_bmc(filp);
-	int ret = 0;
+	struct kcs_bmc_ipmi *priv = to_kcs_bmc(filp);
 
-	spin_lock_irq(&kcs_bmc->lock);
-	if (!kcs_bmc->running)
-		kcs_bmc->running = 1;
-	else
-		ret = -EBUSY;
-	spin_unlock_irq(&kcs_bmc->lock);
-
-	return ret;
+	return kcs_bmc_enable_device(priv->client.dev, &priv->client);
 }
 
 static __poll_t kcs_bmc_ipmi_poll(struct file *filp, poll_table *wait)
 {
-	struct kcs_bmc *kcs_bmc = file_to_kcs_bmc(filp);
+	struct kcs_bmc_ipmi *priv = to_kcs_bmc(filp);
 	__poll_t mask = 0;
 
-	poll_wait(filp, &kcs_bmc->queue, wait);
+	poll_wait(filp, &priv->queue, wait);
 
-	spin_lock_irq(&kcs_bmc->lock);
-	if (kcs_bmc->data_in_avail)
+	spin_lock_irq(&priv->lock);
+	if (priv->data_in_avail)
 		mask |= EPOLLIN;
-	spin_unlock_irq(&kcs_bmc->lock);
+	spin_unlock_irq(&priv->lock);
 
 	return mask;
 }
@@ -255,24 +320,24 @@ static __poll_t kcs_bmc_ipmi_poll(struct file *filp, poll_table *wait)
 static ssize_t kcs_bmc_ipmi_read(struct file *filp, char __user *buf,
 			    size_t count, loff_t *ppos)
 {
-	struct kcs_bmc *kcs_bmc = file_to_kcs_bmc(filp);
+	struct kcs_bmc_ipmi *priv = to_kcs_bmc(filp);
 	bool data_avail;
 	size_t data_len;
 	ssize_t ret;
 
 	if (!(filp->f_flags & O_NONBLOCK))
-		wait_event_interruptible(kcs_bmc->queue,
-					 kcs_bmc->data_in_avail);
+		wait_event_interruptible(priv->queue,
+					 priv->data_in_avail);
 
-	mutex_lock(&kcs_bmc->mutex);
+	mutex_lock(&priv->mutex);
 
-	spin_lock_irq(&kcs_bmc->lock);
-	data_avail = kcs_bmc->data_in_avail;
+	spin_lock_irq(&priv->lock);
+	data_avail = priv->data_in_avail;
 	if (data_avail) {
-		data_len = kcs_bmc->data_in_idx;
-		memcpy(kcs_bmc->kbuffer, kcs_bmc->data_in, data_len);
+		data_len = priv->data_in_idx;
+		memcpy(priv->kbuffer, priv->data_in, data_len);
 	}
-	spin_unlock_irq(&kcs_bmc->lock);
+	spin_unlock_irq(&priv->lock);
 
 	if (!data_avail) {
 		ret = -EAGAIN;
@@ -281,35 +346,35 @@ static ssize_t kcs_bmc_ipmi_read(struct file *filp, char __user *buf,
 
 	if (count < data_len) {
 		pr_err("channel=%u with too large data : %zu\n",
-			kcs_bmc->channel, data_len);
+			priv->client.dev->channel, data_len);
 
-		spin_lock_irq(&kcs_bmc->lock);
-		kcs_bmc_ipmi_force_abort(kcs_bmc);
-		spin_unlock_irq(&kcs_bmc->lock);
+		spin_lock_irq(&priv->lock);
+		kcs_bmc_ipmi_force_abort(priv);
+		spin_unlock_irq(&priv->lock);
 
 		ret = -EOVERFLOW;
 		goto out_unlock;
 	}
 
-	if (copy_to_user(buf, kcs_bmc->kbuffer, data_len)) {
+	if (copy_to_user(buf, priv->kbuffer, data_len)) {
 		ret = -EFAULT;
 		goto out_unlock;
 	}
 
 	ret = data_len;
 
-	spin_lock_irq(&kcs_bmc->lock);
-	if (kcs_bmc->phase == KCS_PHASE_WRITE_DONE) {
-		kcs_bmc->phase = KCS_PHASE_WAIT_READ;
-		kcs_bmc->data_in_avail = false;
-		kcs_bmc->data_in_idx = 0;
+	spin_lock_irq(&priv->lock);
+	if (priv->phase == KCS_PHASE_WRITE_DONE) {
+		priv->phase = KCS_PHASE_WAIT_READ;
+		priv->data_in_avail = false;
+		priv->data_in_idx = 0;
 	} else {
 		ret = -EAGAIN;
 	}
-	spin_unlock_irq(&kcs_bmc->lock);
+	spin_unlock_irq(&priv->lock);
 
 out_unlock:
-	mutex_unlock(&kcs_bmc->mutex);
+	mutex_unlock(&priv->mutex);
 
 	return ret;
 }
@@ -317,35 +382,35 @@ static ssize_t kcs_bmc_ipmi_read(struct file *filp, char __user *buf,
 static ssize_t kcs_bmc_ipmi_write(struct file *filp, const char __user *buf,
 			     size_t count, loff_t *ppos)
 {
-	struct kcs_bmc *kcs_bmc = file_to_kcs_bmc(filp);
+	struct kcs_bmc_ipmi *priv = to_kcs_bmc(filp);
 	ssize_t ret;
 
 	/* a minimum response size '3' : netfn + cmd + ccode */
 	if (count < 3 || count > KCS_MSG_BUFSIZ)
 		return -EINVAL;
 
-	mutex_lock(&kcs_bmc->mutex);
+	mutex_lock(&priv->mutex);
 
-	if (copy_from_user(kcs_bmc->kbuffer, buf, count)) {
+	if (copy_from_user(priv->kbuffer, buf, count)) {
 		ret = -EFAULT;
 		goto out_unlock;
 	}
 
-	spin_lock_irq(&kcs_bmc->lock);
-	if (kcs_bmc->phase == KCS_PHASE_WAIT_READ) {
-		kcs_bmc->phase = KCS_PHASE_READ;
-		kcs_bmc->data_out_idx = 1;
-		kcs_bmc->data_out_len = count;
-		memcpy(kcs_bmc->data_out, kcs_bmc->kbuffer, count);
-		kcs_bmc_write_data(kcs_bmc, kcs_bmc->data_out[0]);
+	spin_lock_irq(&priv->lock);
+	if (priv->phase == KCS_PHASE_WAIT_READ) {
+		priv->phase = KCS_PHASE_READ;
+		priv->data_out_idx = 1;
+		priv->data_out_len = count;
+		memcpy(priv->data_out, priv->kbuffer, count);
+		kcs_bmc_write_data(priv->client.dev, priv->data_out[0]);
 		ret = count;
 	} else {
 		ret = -EINVAL;
 	}
-	spin_unlock_irq(&kcs_bmc->lock);
+	spin_unlock_irq(&priv->lock);
 
 out_unlock:
-	mutex_unlock(&kcs_bmc->mutex);
+	mutex_unlock(&priv->mutex);
 
 	return ret;
 }
@@ -353,22 +418,22 @@ static ssize_t kcs_bmc_ipmi_write(struct file *filp, const char __user *buf,
 static long kcs_bmc_ipmi_ioctl(struct file *filp, unsigned int cmd,
 			  unsigned long arg)
 {
-	struct kcs_bmc *kcs_bmc = file_to_kcs_bmc(filp);
+	struct kcs_bmc_ipmi *priv = to_kcs_bmc(filp);
 	long ret = 0;
 
-	spin_lock_irq(&kcs_bmc->lock);
+	spin_lock_irq(&priv->lock);
 
 	switch (cmd) {
 	case IPMI_BMC_IOCTL_SET_SMS_ATN:
-		kcs_bmc_update_status(kcs_bmc, KCS_STATUS_SMS_ATN, KCS_STATUS_SMS_ATN);
+		kcs_bmc_update_status(priv->client.dev, KCS_STATUS_SMS_ATN, KCS_STATUS_SMS_ATN);
 		break;
 
 	case IPMI_BMC_IOCTL_CLEAR_SMS_ATN:
-		kcs_bmc_update_status(kcs_bmc, KCS_STATUS_SMS_ATN, 0);
+		kcs_bmc_update_status(priv->client.dev, KCS_STATUS_SMS_ATN, 0);
 		break;
 
 	case IPMI_BMC_IOCTL_FORCE_ABORT:
-		kcs_bmc_ipmi_force_abort(kcs_bmc);
+		kcs_bmc_ipmi_force_abort(priv);
 		break;
 
 	default:
@@ -376,19 +441,17 @@ static long kcs_bmc_ipmi_ioctl(struct file *filp, unsigned int cmd,
 		break;
 	}
 
-	spin_unlock_irq(&kcs_bmc->lock);
+	spin_unlock_irq(&priv->lock);
 
 	return ret;
 }
 
 static int kcs_bmc_ipmi_release(struct inode *inode, struct file *filp)
 {
-	struct kcs_bmc *kcs_bmc = file_to_kcs_bmc(filp);
+	struct kcs_bmc_ipmi *priv = to_kcs_bmc(filp);
 
-	spin_lock_irq(&kcs_bmc->lock);
-	kcs_bmc->running = 0;
-	kcs_bmc_ipmi_force_abort(kcs_bmc);
-	spin_unlock_irq(&kcs_bmc->lock);
+	kcs_bmc_ipmi_force_abort(priv);
+	kcs_bmc_disable_device(priv->client.dev, &priv->client);
 
 	return 0;
 }
@@ -403,56 +466,78 @@ static const struct file_operations kcs_bmc_ipmi_fops = {
 	.unlocked_ioctl = kcs_bmc_ipmi_ioctl,
 };
 
-int kcs_bmc_ipmi_attach_cdev(struct kcs_bmc *kcs_bmc);
-int kcs_bmc_ipmi_attach_cdev(struct kcs_bmc *kcs_bmc)
+static DEFINE_SPINLOCK(kcs_bmc_ipmi_instances_lock);
+static LIST_HEAD(kcs_bmc_ipmi_instances);
+
+int kcs_bmc_ipmi_attach_cdev(struct kcs_bmc_device *kcs_bmc);
+int kcs_bmc_ipmi_attach_cdev(struct kcs_bmc_device *kcs_bmc)
 {
+	struct kcs_bmc_ipmi *priv;
 	int rc;
 
-	spin_lock_init(&kcs_bmc->lock);
-	mutex_init(&kcs_bmc->mutex);
-	init_waitqueue_head(&kcs_bmc->queue);
-
-	kcs_bmc->client.dev = kcs_bmc;
-	kcs_bmc->client.ops = &kcs_bmc_ipmi_client_ops;
-	kcs_bmc->data_in = devm_kmalloc(kcs_bmc->dev, KCS_MSG_BUFSIZ, GFP_KERNEL);
-	kcs_bmc->data_out = devm_kmalloc(kcs_bmc->dev, KCS_MSG_BUFSIZ, GFP_KERNEL);
-	kcs_bmc->kbuffer = devm_kmalloc(kcs_bmc->dev, KCS_MSG_BUFSIZ, GFP_KERNEL);
-
-	kcs_bmc->miscdev.minor = MISC_DYNAMIC_MINOR;
-	kcs_bmc->miscdev.name = devm_kasprintf(kcs_bmc->dev, GFP_KERNEL, "%s%u",
-					       DEVICE_NAME, kcs_bmc->channel);
-	if (!kcs_bmc->data_in || !kcs_bmc->data_out || !kcs_bmc->kbuffer ||
-	    !kcs_bmc->miscdev.name)
+	priv = devm_kzalloc(kcs_bmc->dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
 		return -ENOMEM;
 
-	kcs_bmc->miscdev.fops = &kcs_bmc_ipmi_fops;
+	spin_lock_init(&priv->lock);
+	mutex_init(&priv->mutex);
 
-	rc = misc_register(&kcs_bmc->miscdev);
+	init_waitqueue_head(&priv->queue);
+
+	priv->client.dev = kcs_bmc;
+	priv->client.ops = &kcs_bmc_ipmi_client_ops;
+	priv->data_in = devm_kmalloc(kcs_bmc->dev, KCS_MSG_BUFSIZ, GFP_KERNEL);
+	priv->data_out = devm_kmalloc(kcs_bmc->dev, KCS_MSG_BUFSIZ, GFP_KERNEL);
+	priv->kbuffer = devm_kmalloc(kcs_bmc->dev, KCS_MSG_BUFSIZ, GFP_KERNEL);
+
+	priv->miscdev.minor = MISC_DYNAMIC_MINOR;
+	priv->miscdev.name = devm_kasprintf(kcs_bmc->dev, GFP_KERNEL, "%s%u", DEVICE_NAME,
+					   kcs_bmc->channel);
+	if (!priv->data_in || !priv->data_out || !priv->kbuffer || !priv->miscdev.name)
+		return -EINVAL;
+
+	priv->miscdev.fops = &kcs_bmc_ipmi_fops;
+
+	rc = misc_register(&priv->miscdev);
 	if (rc) {
 		dev_err(kcs_bmc->dev, "Unable to register device: %d\n", rc);
 		return rc;
 	}
 
+	spin_lock_irq(&kcs_bmc_ipmi_instances_lock);
+	list_add(&priv->entry, &kcs_bmc_ipmi_instances);
+	spin_unlock_irq(&kcs_bmc_ipmi_instances_lock);
+
 	dev_info(kcs_bmc->dev, "Initialised IPMI client for channel %d", kcs_bmc->channel);
 
 	return 0;
 }
 EXPORT_SYMBOL(kcs_bmc_ipmi_attach_cdev);
 
-int kcs_bmc_ipmi_detach_cdev(struct kcs_bmc *kcs_bmc);
-int kcs_bmc_ipmi_detach_cdev(struct kcs_bmc *kcs_bmc)
+int kcs_bmc_ipmi_detach_cdev(struct kcs_bmc_device *kcs_bmc);
+int kcs_bmc_ipmi_detach_cdev(struct kcs_bmc_device *kcs_bmc)
 {
-	misc_deregister(&kcs_bmc->miscdev);
+	struct kcs_bmc_ipmi *priv = NULL, *pos;
 
-	spin_lock_irq(&kcs_bmc->lock);
-	kcs_bmc->running = 0;
-	kcs_bmc_ipmi_force_abort(kcs_bmc);
-	spin_unlock_irq(&kcs_bmc->lock);
+	spin_lock_irq(&kcs_bmc_ipmi_instances_lock);
+	list_for_each_entry(pos, &kcs_bmc_ipmi_instances, entry) {
+		if (pos->client.dev == kcs_bmc) {
+			priv = pos;
+			list_del(&pos->entry);
+			break;
+		}
+	}
+	spin_unlock_irq(&kcs_bmc_ipmi_instances_lock);
 
-	devm_kfree(kcs_bmc->dev, kcs_bmc->kbuffer);
-	devm_kfree(kcs_bmc->dev, kcs_bmc->data_out);
-	devm_kfree(kcs_bmc->dev, kcs_bmc->data_in);
-	devm_kfree(kcs_bmc->dev, kcs_bmc);
+	if (!priv)
+		return 0;
+
+	misc_deregister(&priv->miscdev);
+	kcs_bmc_disable_device(priv->client.dev, &priv->client);
+	devm_kfree(kcs_bmc->dev, priv->kbuffer);
+	devm_kfree(kcs_bmc->dev, priv->data_out);
+	devm_kfree(kcs_bmc->dev, priv->data_in);
+	devm_kfree(kcs_bmc->dev, priv);
 
 	return 0;
 }
@@ -460,4 +545,5 @@ EXPORT_SYMBOL(kcs_bmc_ipmi_detach_cdev);
 
 MODULE_LICENSE("GPL v2");
 MODULE_AUTHOR("Haiyue Wang <haiyue.wang@linux.intel.com>");
+MODULE_AUTHOR("Andrew Jeffery <andrew@aj.id.au>");
 MODULE_DESCRIPTION("KCS BMC to handle the IPMI request from system software");
diff --git a/drivers/char/ipmi/kcs_bmc_client.h b/drivers/char/ipmi/kcs_bmc_client.h
index 140631d157d8..2dd710f4b4aa 100644
--- a/drivers/char/ipmi/kcs_bmc_client.h
+++ b/drivers/char/ipmi/kcs_bmc_client.h
@@ -8,22 +8,24 @@
 #include <linux/notifier.h>
 #include <stdbool.h>
 
-struct kcs_bmc;
-struct kcs_bmc_client_ops;
-
-struct kcs_bmc_client {
-	const struct kcs_bmc_client_ops *ops;
-
-	struct kcs_bmc *dev;
-};
+#include "kcs_bmc.h"
 
 struct kcs_bmc_client_ops {
 	int (*event)(struct kcs_bmc_client *client);
 };
 
-u8 kcs_bmc_read_data(struct kcs_bmc *kcs_bmc);
-void kcs_bmc_write_data(struct kcs_bmc *kcs_bmc, u8 data);
-u8 kcs_bmc_read_status(struct kcs_bmc *kcs_bmc);
-void kcs_bmc_write_status(struct kcs_bmc *kcs_bmc, u8 data);
-void kcs_bmc_update_status(struct kcs_bmc *kcs_bmc, u8 mask, u8 val);
+struct kcs_bmc_client {
+	const struct kcs_bmc_client_ops *ops;
+
+	struct kcs_bmc_device *dev;
+};
+
+int kcs_bmc_enable_device(struct kcs_bmc_device *kcs_bmc, struct kcs_bmc_client *client);
+void kcs_bmc_disable_device(struct kcs_bmc_device *kcs_bmc, struct kcs_bmc_client *client);
+
+u8 kcs_bmc_read_data(struct kcs_bmc_device *kcs_bmc);
+void kcs_bmc_write_data(struct kcs_bmc_device *kcs_bmc, u8 data);
+u8 kcs_bmc_read_status(struct kcs_bmc_device *kcs_bmc);
+void kcs_bmc_write_status(struct kcs_bmc_device *kcs_bmc, u8 data);
+void kcs_bmc_update_status(struct kcs_bmc_device *kcs_bmc, u8 mask, u8 val);
 #endif
diff --git a/drivers/char/ipmi/kcs_bmc_device.h b/drivers/char/ipmi/kcs_bmc_device.h
index 33462174516d..57b7174b2bac 100644
--- a/drivers/char/ipmi/kcs_bmc_device.h
+++ b/drivers/char/ipmi/kcs_bmc_device.h
@@ -7,13 +7,13 @@
 #include "kcs_bmc.h"
 
 struct kcs_bmc_device_ops {
-	u8 (*io_inputb)(struct kcs_bmc *kcs_bmc, u32 reg);
-	void (*io_outputb)(struct kcs_bmc *kcs_bmc, u32 reg, u8 b);
-	void (*io_updateb)(struct kcs_bmc *kcs_bmc, u32 reg, u8 mask, u8 b);
+	u8 (*io_inputb)(struct kcs_bmc_device *kcs_bmc, u32 reg);
+	void (*io_outputb)(struct kcs_bmc_device *kcs_bmc, u32 reg, u8 b);
+	void (*io_updateb)(struct kcs_bmc_device *kcs_bmc, u32 reg, u8 mask, u8 b);
 };
 
-int kcs_bmc_handle_event(struct kcs_bmc *kcs_bmc);
-int kcs_bmc_add_device(struct kcs_bmc *kcs_bmc);
-int kcs_bmc_remove_device(struct kcs_bmc *kcs_bmc);
+int kcs_bmc_handle_event(struct kcs_bmc_device *kcs_bmc);
+int kcs_bmc_add_device(struct kcs_bmc_device *kcs_bmc);
+int kcs_bmc_remove_device(struct kcs_bmc_device *kcs_bmc);
 
 #endif
diff --git a/drivers/char/ipmi/kcs_bmc_npcm7xx.c b/drivers/char/ipmi/kcs_bmc_npcm7xx.c
index 1d21697fc585..dce93ec895fc 100644
--- a/drivers/char/ipmi/kcs_bmc_npcm7xx.c
+++ b/drivers/char/ipmi/kcs_bmc_npcm7xx.c
@@ -65,7 +65,7 @@ struct npcm7xx_kcs_reg {
 };
 
 struct npcm7xx_kcs_bmc {
-	struct kcs_bmc kcs_bmc;
+	struct kcs_bmc_device kcs_bmc;
 
 	struct regmap *map;
 
@@ -78,12 +78,12 @@ static const struct npcm7xx_kcs_reg npcm7xx_kcs_reg_tbl[KCS_CHANNEL_MAX] = {
 	{ .sts = KCS3ST, .dob = KCS3DO, .dib = KCS3DI, .ctl = KCS3CTL, .ie = KCS3IE },
 };
 
-static inline struct npcm7xx_kcs_bmc *to_npcm7xx_kcs_bmc(struct kcs_bmc *kcs_bmc)
+static inline struct npcm7xx_kcs_bmc *to_npcm7xx_kcs_bmc(struct kcs_bmc_device *kcs_bmc)
 {
 	return container_of(kcs_bmc, struct npcm7xx_kcs_bmc, kcs_bmc);
 }
 
-static u8 npcm7xx_kcs_inb(struct kcs_bmc *kcs_bmc, u32 reg)
+static u8 npcm7xx_kcs_inb(struct kcs_bmc_device *kcs_bmc, u32 reg)
 {
 	struct npcm7xx_kcs_bmc *priv = to_npcm7xx_kcs_bmc(kcs_bmc);
 	u32 val = 0;
@@ -95,7 +95,7 @@ static u8 npcm7xx_kcs_inb(struct kcs_bmc *kcs_bmc, u32 reg)
 	return rc == 0 ? (u8)val : 0;
 }
 
-static void npcm7xx_kcs_outb(struct kcs_bmc *kcs_bmc, u32 reg, u8 data)
+static void npcm7xx_kcs_outb(struct kcs_bmc_device *kcs_bmc, u32 reg, u8 data)
 {
 	struct npcm7xx_kcs_bmc *priv = to_npcm7xx_kcs_bmc(kcs_bmc);
 	int rc;
@@ -104,7 +104,7 @@ static void npcm7xx_kcs_outb(struct kcs_bmc *kcs_bmc, u32 reg, u8 data)
 	WARN(rc != 0, "regmap_write() failed: %d\n", rc);
 }
 
-static void npcm7xx_kcs_updateb(struct kcs_bmc *kcs_bmc, u32 reg, u8 mask, u8 data)
+static void npcm7xx_kcs_updateb(struct kcs_bmc_device *kcs_bmc, u32 reg, u8 mask, u8 data)
 {
 	struct npcm7xx_kcs_bmc *priv = to_npcm7xx_kcs_bmc(kcs_bmc);
 	int rc;
@@ -113,7 +113,7 @@ static void npcm7xx_kcs_updateb(struct kcs_bmc *kcs_bmc, u32 reg, u8 mask, u8 da
 	WARN(rc != 0, "regmap_update_bits() failed: %d\n", rc);
 }
 
-static void npcm7xx_kcs_enable_channel(struct kcs_bmc *kcs_bmc, bool enable)
+static void npcm7xx_kcs_enable_channel(struct kcs_bmc_device *kcs_bmc, bool enable)
 {
 	struct npcm7xx_kcs_bmc *priv = to_npcm7xx_kcs_bmc(kcs_bmc);
 
@@ -126,7 +126,7 @@ static void npcm7xx_kcs_enable_channel(struct kcs_bmc *kcs_bmc, bool enable)
 
 static irqreturn_t npcm7xx_kcs_irq(int irq, void *arg)
 {
-	struct kcs_bmc *kcs_bmc = arg;
+	struct kcs_bmc_device *kcs_bmc = arg;
 	int rc;
 
 	rc = kcs_bmc_handle_event(kcs_bmc);
@@ -136,7 +136,7 @@ static irqreturn_t npcm7xx_kcs_irq(int irq, void *arg)
 	return rc == KCS_BMC_EVENT_HANDLED ? IRQ_HANDLED : IRQ_NONE;
 }
 
-static int npcm7xx_kcs_config_irq(struct kcs_bmc *kcs_bmc,
+static int npcm7xx_kcs_config_irq(struct kcs_bmc_device *kcs_bmc,
 				  struct platform_device *pdev)
 {
 	struct device *dev = &pdev->dev;
@@ -160,7 +160,7 @@ static int npcm7xx_kcs_probe(struct platform_device *pdev)
 {
 	struct device *dev = &pdev->dev;
 	struct npcm7xx_kcs_bmc *priv;
-	struct kcs_bmc *kcs_bmc;
+	struct kcs_bmc_device *kcs_bmc;
 	u32 chan;
 	int rc;
 
@@ -207,7 +207,7 @@ static int npcm7xx_kcs_probe(struct platform_device *pdev)
 static int npcm7xx_kcs_remove(struct platform_device *pdev)
 {
 	struct npcm7xx_kcs_bmc *priv = platform_get_drvdata(pdev);
-	struct kcs_bmc *kcs_bmc = &priv->kcs_bmc;
+	struct kcs_bmc_device *kcs_bmc = &priv->kcs_bmc;
 
 	kcs_bmc_remove_device(kcs_bmc);
 
-- 
2.27.0


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

* [PATCH v2 13/21] ipmi: kcs_bmc: Decouple the IPMI chardev from the core
  2021-03-19  6:27 [PATCH v2 01/21] dt-bindings: aspeed-lpc: Remove LPC partitioning Andrew Jeffery
                   ` (10 preceding siblings ...)
  2021-03-19  6:27 ` [PATCH v2 12/21] ipmi: kcs_bmc: Strip private client data from struct kcs_bmc Andrew Jeffery
@ 2021-03-19  6:27 ` Andrew Jeffery
  2021-04-06  6:07   ` ChiaWei Wang
  2021-04-09  4:35   ` Zev Weiss
  2021-03-19  6:27 ` [PATCH v2 14/21] ipmi: kcs_bmc: Allow clients to control KCS IRQ state Andrew Jeffery
                   ` (8 subsequent siblings)
  20 siblings, 2 replies; 67+ messages in thread
From: Andrew Jeffery @ 2021-03-19  6:27 UTC (permalink / raw)
  To: openipmi-developer, openbmc, minyard
  Cc: devicetree, ryan_chen, tmaimon77, linux-aspeed, avifishman70,
	venture, linus.walleij, linux-kernel, tali.perry1, linux-gpio,
	robh+dt, lee.jones, chiawei_wang, linux-arm-kernel, benjaminfair

Now that we have untangled the data-structures, split the userspace
interface out into its own module. Userspace interfaces and drivers are
registered to the KCS BMC core to support arbitrary binding of either.

Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
---
 drivers/char/ipmi/Kconfig             | 13 +++++
 drivers/char/ipmi/Makefile            |  3 +-
 drivers/char/ipmi/kcs_bmc.c           | 78 ++++++++++++++++++++++++++-
 drivers/char/ipmi/kcs_bmc.h           |  4 --
 drivers/char/ipmi/kcs_bmc_cdev_ipmi.c | 33 +++++++++---
 drivers/char/ipmi/kcs_bmc_client.h    | 14 +++++
 6 files changed, 132 insertions(+), 13 deletions(-)

diff --git a/drivers/char/ipmi/Kconfig b/drivers/char/ipmi/Kconfig
index 07847d9a459a..bc5f81899b62 100644
--- a/drivers/char/ipmi/Kconfig
+++ b/drivers/char/ipmi/Kconfig
@@ -124,6 +124,19 @@ config NPCM7XX_KCS_IPMI_BMC
 	  This support is also available as a module.  If so, the module
 	  will be called kcs_bmc_npcm7xx.
 
+config IPMI_KCS_BMC_CDEV_IPMI
+	depends on IPMI_KCS_BMC
+	tristate "IPMI character device interface for BMC KCS devices"
+	help
+	  Provides a BMC-side character device implementing IPMI
+	  semantics for KCS IPMI devices.
+
+	  Say YES if you wish to expose KCS devices on the BMC for IPMI
+	  purposes.
+
+	  This support is also available as a module. The module will be
+	  called kcs_bmc_cdev_ipmi.
+
 config ASPEED_BT_IPMI_BMC
 	depends on ARCH_ASPEED || COMPILE_TEST
 	depends on REGMAP && REGMAP_MMIO && MFD_SYSCON
diff --git a/drivers/char/ipmi/Makefile b/drivers/char/ipmi/Makefile
index a302bc865370..fcfa676afddb 100644
--- a/drivers/char/ipmi/Makefile
+++ b/drivers/char/ipmi/Makefile
@@ -22,7 +22,8 @@ obj-$(CONFIG_IPMI_SSIF) += ipmi_ssif.o
 obj-$(CONFIG_IPMI_POWERNV) += ipmi_powernv.o
 obj-$(CONFIG_IPMI_WATCHDOG) += ipmi_watchdog.o
 obj-$(CONFIG_IPMI_POWEROFF) += ipmi_poweroff.o
-obj-$(CONFIG_IPMI_KCS_BMC) += kcs_bmc.o kcs_bmc_cdev_ipmi.o
+obj-$(CONFIG_IPMI_KCS_BMC) += kcs_bmc.o
+obj-$(CONFIG_IPMI_KCS_BMC_CDEV_IPMI) += kcs_bmc_cdev_ipmi.o
 obj-$(CONFIG_ASPEED_BT_IPMI_BMC) += bt-bmc.o
 obj-$(CONFIG_ASPEED_KCS_IPMI_BMC) += kcs_bmc_aspeed.o
 obj-$(CONFIG_NPCM7XX_KCS_IPMI_BMC) += kcs_bmc_npcm7xx.o
diff --git a/drivers/char/ipmi/kcs_bmc.c b/drivers/char/ipmi/kcs_bmc.c
index 266ebec71d6f..694db6ee2a92 100644
--- a/drivers/char/ipmi/kcs_bmc.c
+++ b/drivers/char/ipmi/kcs_bmc.c
@@ -5,7 +5,9 @@
  */
 
 #include <linux/device.h>
+#include <linux/list.h>
 #include <linux/module.h>
+#include <linux/mutex.h>
 
 #include "kcs_bmc.h"
 
@@ -13,6 +15,11 @@
 #include "kcs_bmc_device.h"
 #include "kcs_bmc_client.h"
 
+/* Record probed devices and cdevs */
+static DEFINE_MUTEX(kcs_bmc_lock);
+static LIST_HEAD(kcs_bmc_devices);
+static LIST_HEAD(kcs_bmc_cdevs);
+
 /* Consumer data access */
 
 u8 kcs_bmc_read_data(struct kcs_bmc_device *kcs_bmc)
@@ -100,16 +107,83 @@ EXPORT_SYMBOL(kcs_bmc_disable_device);
 
 int kcs_bmc_add_device(struct kcs_bmc_device *kcs_bmc)
 {
-	return kcs_bmc_ipmi_attach_cdev(kcs_bmc);
+	struct kcs_bmc_cdev *cdev;
+	int rc;
+
+	spin_lock_init(&kcs_bmc->lock);
+	kcs_bmc->client = NULL;
+
+	mutex_lock(&kcs_bmc_lock);
+	list_add(&kcs_bmc->entry, &kcs_bmc_devices);
+	list_for_each_entry(cdev, &kcs_bmc_cdevs, entry) {
+		rc = cdev->ops->add_device(kcs_bmc);
+		if (rc)
+			dev_err(kcs_bmc->dev, "Failed to add chardev for KCS channel %d: %d",
+				kcs_bmc->channel, rc);
+	}
+	mutex_unlock(&kcs_bmc_lock);
+
+	return 0;
 }
 EXPORT_SYMBOL(kcs_bmc_add_device);
 
 int kcs_bmc_remove_device(struct kcs_bmc_device *kcs_bmc)
 {
-	return kcs_bmc_ipmi_detach_cdev(kcs_bmc);
+	struct kcs_bmc_cdev *cdev;
+	int rc;
+
+	mutex_lock(&kcs_bmc_lock);
+	list_del(&kcs_bmc->entry);
+	list_for_each_entry(cdev, &kcs_bmc_cdevs, entry) {
+		rc = cdev->ops->remove_device(kcs_bmc);
+		if (rc)
+			dev_err(kcs_bmc->dev, "Failed to remove chardev for KCS channel %d: %d",
+				kcs_bmc->channel, rc);
+	}
+	mutex_unlock(&kcs_bmc_lock);
+
+	return 0;
 }
 EXPORT_SYMBOL(kcs_bmc_remove_device);
 
+int kcs_bmc_register_cdev(struct kcs_bmc_cdev *cdev)
+{
+	struct kcs_bmc_device *kcs_bmc;
+	int rc;
+
+	mutex_lock(&kcs_bmc_lock);
+	list_add(&cdev->entry, &kcs_bmc_cdevs);
+	list_for_each_entry(kcs_bmc, &kcs_bmc_devices, entry) {
+		rc = cdev->ops->add_device(kcs_bmc);
+		if (rc)
+			dev_err(kcs_bmc->dev, "Failed to add chardev for KCS channel %d: %d",
+				kcs_bmc->channel, rc);
+	}
+	mutex_unlock(&kcs_bmc_lock);
+
+	return 0;
+}
+EXPORT_SYMBOL(kcs_bmc_register_cdev);
+
+int kcs_bmc_unregister_cdev(struct kcs_bmc_cdev *cdev)
+{
+	struct kcs_bmc_device *kcs_bmc;
+	int rc;
+
+	mutex_lock(&kcs_bmc_lock);
+	list_del(&cdev->entry);
+	list_for_each_entry(kcs_bmc, &kcs_bmc_devices, entry) {
+		rc = cdev->ops->remove_device(kcs_bmc);
+		if (rc)
+			dev_err(kcs_bmc->dev, "Failed to add chardev for KCS channel %d: %d",
+				kcs_bmc->channel, rc);
+	}
+	mutex_unlock(&kcs_bmc_lock);
+
+	return rc;
+}
+EXPORT_SYMBOL(kcs_bmc_unregister_cdev);
+
 MODULE_LICENSE("GPL v2");
 MODULE_AUTHOR("Haiyue Wang <haiyue.wang@linux.intel.com>");
 MODULE_AUTHOR("Andrew Jeffery <andrew@aj.id.au>");
diff --git a/drivers/char/ipmi/kcs_bmc.h b/drivers/char/ipmi/kcs_bmc.h
index 3f266740c759..5deb9a0b8e60 100644
--- a/drivers/char/ipmi/kcs_bmc.h
+++ b/drivers/char/ipmi/kcs_bmc.h
@@ -42,8 +42,4 @@ struct kcs_bmc_device {
 	spinlock_t lock;
 	struct kcs_bmc_client *client;
 };
-
-/* Temporary exports while refactoring */
-int kcs_bmc_ipmi_attach_cdev(struct kcs_bmc_device *kcs_bmc);
-int kcs_bmc_ipmi_detach_cdev(struct kcs_bmc_device *kcs_bmc);
 #endif /* __KCS_BMC_H__ */
diff --git a/drivers/char/ipmi/kcs_bmc_cdev_ipmi.c b/drivers/char/ipmi/kcs_bmc_cdev_ipmi.c
index 58c42e76483d..df83d67851ac 100644
--- a/drivers/char/ipmi/kcs_bmc_cdev_ipmi.c
+++ b/drivers/char/ipmi/kcs_bmc_cdev_ipmi.c
@@ -469,8 +469,7 @@ static const struct file_operations kcs_bmc_ipmi_fops = {
 static DEFINE_SPINLOCK(kcs_bmc_ipmi_instances_lock);
 static LIST_HEAD(kcs_bmc_ipmi_instances);
 
-int kcs_bmc_ipmi_attach_cdev(struct kcs_bmc_device *kcs_bmc);
-int kcs_bmc_ipmi_attach_cdev(struct kcs_bmc_device *kcs_bmc)
+static int kcs_bmc_ipmi_attach_cdev(struct kcs_bmc_device *kcs_bmc)
 {
 	struct kcs_bmc_ipmi *priv;
 	int rc;
@@ -512,10 +511,8 @@ int kcs_bmc_ipmi_attach_cdev(struct kcs_bmc_device *kcs_bmc)
 
 	return 0;
 }
-EXPORT_SYMBOL(kcs_bmc_ipmi_attach_cdev);
 
-int kcs_bmc_ipmi_detach_cdev(struct kcs_bmc_device *kcs_bmc);
-int kcs_bmc_ipmi_detach_cdev(struct kcs_bmc_device *kcs_bmc)
+static int kcs_bmc_ipmi_detach_cdev(struct kcs_bmc_device *kcs_bmc)
 {
 	struct kcs_bmc_ipmi *priv = NULL, *pos;
 
@@ -541,7 +538,31 @@ int kcs_bmc_ipmi_detach_cdev(struct kcs_bmc_device *kcs_bmc)
 
 	return 0;
 }
-EXPORT_SYMBOL(kcs_bmc_ipmi_detach_cdev);
+
+static const struct kcs_bmc_cdev_ops kcs_bmc_ipmi_cdev_ops = {
+	.add_device = kcs_bmc_ipmi_attach_cdev,
+	.remove_device = kcs_bmc_ipmi_detach_cdev,
+};
+
+static struct kcs_bmc_cdev kcs_bmc_ipmi_cdev = {
+	.ops = &kcs_bmc_ipmi_cdev_ops,
+};
+
+static int kcs_bmc_ipmi_init(void)
+{
+	return kcs_bmc_register_cdev(&kcs_bmc_ipmi_cdev);
+}
+module_init(kcs_bmc_ipmi_init);
+
+static void kcs_bmc_ipmi_exit(void)
+{
+	int rc;
+
+	rc = kcs_bmc_unregister_cdev(&kcs_bmc_ipmi_cdev);
+	if (rc)
+		pr_warn("Failed to remove KCS BMC client: %d", rc);
+}
+module_exit(kcs_bmc_ipmi_exit);
 
 MODULE_LICENSE("GPL v2");
 MODULE_AUTHOR("Haiyue Wang <haiyue.wang@linux.intel.com>");
diff --git a/drivers/char/ipmi/kcs_bmc_client.h b/drivers/char/ipmi/kcs_bmc_client.h
index 2dd710f4b4aa..d0a7404ff584 100644
--- a/drivers/char/ipmi/kcs_bmc_client.h
+++ b/drivers/char/ipmi/kcs_bmc_client.h
@@ -10,6 +10,17 @@
 
 #include "kcs_bmc.h"
 
+struct kcs_bmc_cdev_ops {
+	int (*add_device)(struct kcs_bmc_device *kcs_bmc);
+	int (*remove_device)(struct kcs_bmc_device *kcs_bmc);
+};
+
+struct kcs_bmc_cdev {
+	struct list_head entry;
+
+	const struct kcs_bmc_cdev_ops *ops;
+};
+
 struct kcs_bmc_client_ops {
 	int (*event)(struct kcs_bmc_client *client);
 };
@@ -20,6 +31,9 @@ struct kcs_bmc_client {
 	struct kcs_bmc_device *dev;
 };
 
+int kcs_bmc_register_cdev(struct kcs_bmc_cdev *cdev);
+int kcs_bmc_unregister_cdev(struct kcs_bmc_cdev *cdev);
+
 int kcs_bmc_enable_device(struct kcs_bmc_device *kcs_bmc, struct kcs_bmc_client *client);
 void kcs_bmc_disable_device(struct kcs_bmc_device *kcs_bmc, struct kcs_bmc_client *client);
 
-- 
2.27.0


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

* [PATCH v2 14/21] ipmi: kcs_bmc: Allow clients to control KCS IRQ state
  2021-03-19  6:27 [PATCH v2 01/21] dt-bindings: aspeed-lpc: Remove LPC partitioning Andrew Jeffery
                   ` (11 preceding siblings ...)
  2021-03-19  6:27 ` [PATCH v2 13/21] ipmi: kcs_bmc: Decouple the IPMI chardev from the core Andrew Jeffery
@ 2021-03-19  6:27 ` Andrew Jeffery
  2021-04-09  4:37   ` Zev Weiss
  2021-03-19  6:27 ` [PATCH v2 15/21] ipmi: kcs_bmc: Don't enforce single-open policy in the kernel Andrew Jeffery
                   ` (7 subsequent siblings)
  20 siblings, 1 reply; 67+ messages in thread
From: Andrew Jeffery @ 2021-03-19  6:27 UTC (permalink / raw)
  To: openipmi-developer, openbmc, minyard
  Cc: devicetree, ryan_chen, tmaimon77, linux-aspeed, avifishman70,
	venture, linus.walleij, linux-kernel, tali.perry1, linux-gpio,
	robh+dt, lee.jones, chiawei_wang, linux-arm-kernel, benjaminfair

Add a mechanism for controlling whether the client associated with a
KCS device will receive Input Buffer Full (IBF) and Output Buffer Empty
(OBE) events. This enables an abstract implementation of poll() for KCS
devices.

A wart in the implementation is that the ASPEED KCS devices don't
support an OBE interrupt for the BMC. Instead we pretend it has one by
polling the status register waiting for the Output Buffer Full (OBF) bit
to clear, and generating an event when OBE is observed.

Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
---
 drivers/char/ipmi/kcs_bmc.c         |   6 ++
 drivers/char/ipmi/kcs_bmc.h         |   3 +
 drivers/char/ipmi/kcs_bmc_aspeed.c  | 150 ++++++++++++++++++----------
 drivers/char/ipmi/kcs_bmc_client.h  |   2 +
 drivers/char/ipmi/kcs_bmc_device.h  |   1 +
 drivers/char/ipmi/kcs_bmc_npcm7xx.c |  25 ++++-
 6 files changed, 130 insertions(+), 57 deletions(-)

diff --git a/drivers/char/ipmi/kcs_bmc.c b/drivers/char/ipmi/kcs_bmc.c
index 694db6ee2a92..05bbb72418b2 100644
--- a/drivers/char/ipmi/kcs_bmc.c
+++ b/drivers/char/ipmi/kcs_bmc.c
@@ -184,6 +184,12 @@ int kcs_bmc_unregister_cdev(struct kcs_bmc_cdev *cdev)
 }
 EXPORT_SYMBOL(kcs_bmc_unregister_cdev);
 
+void kcs_bmc_update_event_mask(struct kcs_bmc_device *kcs_bmc, u8 mask, u8 events)
+{
+	kcs_bmc->ops->irq_mask_update(kcs_bmc, mask, events);
+}
+EXPORT_SYMBOL(kcs_bmc_update_event_mask);
+
 MODULE_LICENSE("GPL v2");
 MODULE_AUTHOR("Haiyue Wang <haiyue.wang@linux.intel.com>");
 MODULE_AUTHOR("Andrew Jeffery <andrew@aj.id.au>");
diff --git a/drivers/char/ipmi/kcs_bmc.h b/drivers/char/ipmi/kcs_bmc.h
index 5deb9a0b8e60..11fff935218c 100644
--- a/drivers/char/ipmi/kcs_bmc.h
+++ b/drivers/char/ipmi/kcs_bmc.h
@@ -11,6 +11,9 @@
 #define KCS_BMC_EVENT_NONE	0
 #define KCS_BMC_EVENT_HANDLED	1
 
+#define KCS_BMC_EVENT_TYPE_OBE	BIT(0)
+#define KCS_BMC_EVENT_TYPE_IBF	BIT(1)
+
 #define KCS_BMC_STR_OBF		BIT(0)
 #define KCS_BMC_STR_IBF		BIT(1)
 #define KCS_BMC_STR_CMD_DAT	BIT(3)
diff --git a/drivers/char/ipmi/kcs_bmc_aspeed.c b/drivers/char/ipmi/kcs_bmc_aspeed.c
index 6f26e7366c0b..5f26471c038c 100644
--- a/drivers/char/ipmi/kcs_bmc_aspeed.c
+++ b/drivers/char/ipmi/kcs_bmc_aspeed.c
@@ -60,10 +60,18 @@
 #define LPC_ODR4             0x118
 #define LPC_STR4             0x11C
 
+#define OBE_POLL_PERIOD	     (HZ / 2)
+
 struct aspeed_kcs_bmc {
 	struct kcs_bmc_device kcs_bmc;
 
 	struct regmap *map;
+
+	struct {
+		spinlock_t lock;
+		bool remove;
+		struct timer_list timer;
+	} obe;
 };
 
 struct aspeed_kcs_of_ops {
@@ -159,68 +167,89 @@ static void aspeed_kcs_enable_channel(struct kcs_bmc_device *kcs_bmc, bool enabl
 
 	switch (kcs_bmc->channel) {
 	case 1:
-		if (enable) {
-			regmap_update_bits(priv->map, LPC_HICR2,
-					LPC_HICR2_IBFIF1, LPC_HICR2_IBFIF1);
-			regmap_update_bits(priv->map, LPC_HICR0,
-					LPC_HICR0_LPC1E, LPC_HICR0_LPC1E);
-		} else {
-			regmap_update_bits(priv->map, LPC_HICR0,
-					LPC_HICR0_LPC1E, 0);
-			regmap_update_bits(priv->map, LPC_HICR2,
-					LPC_HICR2_IBFIF1, 0);
-		}
-		break;
-
+		regmap_update_bits(priv->map, LPC_HICR0, LPC_HICR0_LPC1E, enable * LPC_HICR0_LPC1E);
+		return;
 	case 2:
-		if (enable) {
-			regmap_update_bits(priv->map, LPC_HICR2,
-					LPC_HICR2_IBFIF2, LPC_HICR2_IBFIF2);
-			regmap_update_bits(priv->map, LPC_HICR0,
-					LPC_HICR0_LPC2E, LPC_HICR0_LPC2E);
-		} else {
-			regmap_update_bits(priv->map, LPC_HICR0,
-					LPC_HICR0_LPC2E, 0);
-			regmap_update_bits(priv->map, LPC_HICR2,
-					LPC_HICR2_IBFIF2, 0);
-		}
-		break;
-
+		regmap_update_bits(priv->map, LPC_HICR0, LPC_HICR0_LPC2E, enable * LPC_HICR0_LPC2E);
+		return;
 	case 3:
-		if (enable) {
-			regmap_update_bits(priv->map, LPC_HICR2,
-					LPC_HICR2_IBFIF3, LPC_HICR2_IBFIF3);
-			regmap_update_bits(priv->map, LPC_HICR0,
-					LPC_HICR0_LPC3E, LPC_HICR0_LPC3E);
-			regmap_update_bits(priv->map, LPC_HICR4,
-					LPC_HICR4_KCSENBL, LPC_HICR4_KCSENBL);
-		} else {
-			regmap_update_bits(priv->map, LPC_HICR0,
-					LPC_HICR0_LPC3E, 0);
-			regmap_update_bits(priv->map, LPC_HICR4,
-					LPC_HICR4_KCSENBL, 0);
-			regmap_update_bits(priv->map, LPC_HICR2,
-					LPC_HICR2_IBFIF3, 0);
-		}
-		break;
-
+		regmap_update_bits(priv->map, LPC_HICR0, LPC_HICR0_LPC3E, enable * LPC_HICR0_LPC3E);
+		regmap_update_bits(priv->map, LPC_HICR4,
+				   LPC_HICR4_KCSENBL, enable * LPC_HICR4_KCSENBL);
+		return;
 	case 4:
-		if (enable)
-			regmap_update_bits(priv->map, LPC_HICRB,
-					LPC_HICRB_IBFIF4 | LPC_HICRB_LPC4E,
-					LPC_HICRB_IBFIF4 | LPC_HICRB_LPC4E);
+		regmap_update_bits(priv->map, LPC_HICRB, LPC_HICRB_LPC4E, enable * LPC_HICRB_LPC4E);
+		return;
+	default:
+		pr_warn("%s: Unsupported channel: %d", __func__, kcs_bmc->channel);
+		return;
+	}
+}
+
+static void aspeed_kcs_check_obe(struct timer_list *timer)
+{
+	struct aspeed_kcs_bmc *priv = container_of(timer, struct aspeed_kcs_bmc, obe.timer);
+	unsigned long flags;
+	u8 str;
+
+	spin_lock_irqsave(&priv->obe.lock, flags);
+	if (priv->obe.remove) {
+		spin_unlock_irqrestore(&priv->obe.lock, flags);
+		return;
+	}
+
+	str = aspeed_kcs_inb(&priv->kcs_bmc, priv->kcs_bmc.ioreg.str);
+	if (str & KCS_BMC_STR_OBF) {
+		mod_timer(timer, jiffies + OBE_POLL_PERIOD);
+		spin_unlock_irqrestore(&priv->obe.lock, flags);
+		return;
+	}
+	spin_unlock_irqrestore(&priv->obe.lock, flags);
+
+	kcs_bmc_handle_event(&priv->kcs_bmc);
+}
+
+static void aspeed_kcs_irq_mask_update(struct kcs_bmc_device *kcs_bmc, u8 mask, u8 state)
+{
+	struct aspeed_kcs_bmc *priv = to_aspeed_kcs_bmc(kcs_bmc);
+
+	/* We don't have an OBE IRQ, emulate it */
+	if (KCS_BMC_EVENT_TYPE_OBE & mask) {
+		if (KCS_BMC_EVENT_TYPE_OBE & state)
+			mod_timer(&priv->obe.timer, jiffies + OBE_POLL_PERIOD);
 		else
-			regmap_update_bits(priv->map, LPC_HICRB,
-					LPC_HICRB_IBFIF4 | LPC_HICRB_LPC4E,
-					0);
-		break;
+			del_timer(&priv->obe.timer);
+	}
 
-	default:
-		break;
+	if (KCS_BMC_EVENT_TYPE_IBF & mask) {
+		const bool enable = !!(KCS_BMC_EVENT_TYPE_IBF & state);
+
+		switch (kcs_bmc->channel) {
+		case 1:
+			regmap_update_bits(priv->map, LPC_HICR2, LPC_HICR2_IBFIF1,
+					   enable * LPC_HICR2_IBFIF1);
+			return;
+		case 2:
+			regmap_update_bits(priv->map, LPC_HICR2, LPC_HICR2_IBFIF2,
+					   enable * LPC_HICR2_IBFIF2);
+			return;
+		case 3:
+			regmap_update_bits(priv->map, LPC_HICR2, LPC_HICR2_IBFIF3,
+					   enable * LPC_HICR2_IBFIF3);
+			return;
+		case 4:
+			regmap_update_bits(priv->map, LPC_HICRB, LPC_HICRB_IBFIF4,
+					   enable * LPC_HICRB_IBFIF4);
+			return;
+		default:
+			pr_warn("%s: Unsupported channel: %d", __func__, kcs_bmc->channel);
+			return;
+		}
 	}
 }
 
 static const struct kcs_bmc_device_ops aspeed_kcs_ops = {
+	.irq_mask_update = aspeed_kcs_irq_mask_update,
 	.io_inputb = aspeed_kcs_inb,
 	.io_outputb = aspeed_kcs_outb,
 	.io_updateb = aspeed_kcs_updateb,
@@ -378,6 +407,10 @@ static int aspeed_kcs_probe(struct platform_device *pdev)
 		return -ENODEV;
 	}
 
+	spin_lock_init(&priv->obe.lock);
+	priv->obe.remove = false;
+	timer_setup(&priv->obe.timer, aspeed_kcs_check_obe, 0);
+
 	aspeed_kcs_set_address(kcs_bmc, addr);
 
 	rc = aspeed_kcs_config_irq(kcs_bmc, pdev);
@@ -386,6 +419,8 @@ static int aspeed_kcs_probe(struct platform_device *pdev)
 
 	platform_set_drvdata(pdev, priv);
 
+	aspeed_kcs_irq_mask_update(kcs_bmc, (KCS_BMC_EVENT_TYPE_IBF | KCS_BMC_EVENT_TYPE_OBE),
+				   KCS_BMC_EVENT_TYPE_IBF);
 	aspeed_kcs_enable_channel(kcs_bmc, true);
 
 	rc = kcs_bmc_add_device(&priv->kcs_bmc);
@@ -404,6 +439,15 @@ static int aspeed_kcs_remove(struct platform_device *pdev)
 
 	kcs_bmc_remove_device(kcs_bmc);
 
+	aspeed_kcs_enable_channel(kcs_bmc, false);
+	aspeed_kcs_irq_mask_update(kcs_bmc, (KCS_BMC_EVENT_TYPE_IBF | KCS_BMC_EVENT_TYPE_OBE), 0);
+
+	/* Make sure it's proper dead */
+	spin_lock_irq(&priv->obe.lock);
+	priv->obe.remove = true;
+	spin_unlock_irq(&priv->obe.lock);
+	del_timer_sync(&priv->obe.timer);
+
 	return 0;
 }
 
diff --git a/drivers/char/ipmi/kcs_bmc_client.h b/drivers/char/ipmi/kcs_bmc_client.h
index d0a7404ff584..456796da33de 100644
--- a/drivers/char/ipmi/kcs_bmc_client.h
+++ b/drivers/char/ipmi/kcs_bmc_client.h
@@ -37,6 +37,8 @@ int kcs_bmc_unregister_cdev(struct kcs_bmc_cdev *cdev);
 int kcs_bmc_enable_device(struct kcs_bmc_device *kcs_bmc, struct kcs_bmc_client *client);
 void kcs_bmc_disable_device(struct kcs_bmc_device *kcs_bmc, struct kcs_bmc_client *client);
 
+void kcs_bmc_update_event_mask(struct kcs_bmc_device *kcs_bmc, u8 mask, u8 events);
+
 u8 kcs_bmc_read_data(struct kcs_bmc_device *kcs_bmc);
 void kcs_bmc_write_data(struct kcs_bmc_device *kcs_bmc, u8 data);
 u8 kcs_bmc_read_status(struct kcs_bmc_device *kcs_bmc);
diff --git a/drivers/char/ipmi/kcs_bmc_device.h b/drivers/char/ipmi/kcs_bmc_device.h
index 57b7174b2bac..f1ca8912496a 100644
--- a/drivers/char/ipmi/kcs_bmc_device.h
+++ b/drivers/char/ipmi/kcs_bmc_device.h
@@ -7,6 +7,7 @@
 #include "kcs_bmc.h"
 
 struct kcs_bmc_device_ops {
+	void (*irq_mask_update)(struct kcs_bmc_device *kcs_bmc, u8 mask, u8 enable);
 	u8 (*io_inputb)(struct kcs_bmc_device *kcs_bmc, u32 reg);
 	void (*io_outputb)(struct kcs_bmc_device *kcs_bmc, u32 reg, u8 b);
 	void (*io_updateb)(struct kcs_bmc_device *kcs_bmc, u32 reg, u8 mask, u8 b);
diff --git a/drivers/char/ipmi/kcs_bmc_npcm7xx.c b/drivers/char/ipmi/kcs_bmc_npcm7xx.c
index dce93ec895fc..c2032728a03d 100644
--- a/drivers/char/ipmi/kcs_bmc_npcm7xx.c
+++ b/drivers/char/ipmi/kcs_bmc_npcm7xx.c
@@ -38,6 +38,7 @@
 #define KCS2CTL		0x2A
 #define KCS3CTL		0x3C
 #define    KCS_CTL_IBFIE	BIT(0)
+#define    KCS_CTL_OBEIE	BIT(0)
 
 #define KCS1IE		0x1C
 #define KCS2IE		0x2E
@@ -117,13 +118,23 @@ static void npcm7xx_kcs_enable_channel(struct kcs_bmc_device *kcs_bmc, bool enab
 {
 	struct npcm7xx_kcs_bmc *priv = to_npcm7xx_kcs_bmc(kcs_bmc);
 
-	regmap_update_bits(priv->map, priv->reg->ctl, KCS_CTL_IBFIE,
-			   enable ? KCS_CTL_IBFIE : 0);
-
 	regmap_update_bits(priv->map, priv->reg->ie, KCS_IE_IRQE | KCS_IE_HIRQE,
 			   enable ? KCS_IE_IRQE | KCS_IE_HIRQE : 0);
 }
 
+static void npcm7xx_kcs_irq_mask_update(struct kcs_bmc_device *kcs_bmc, u8 mask, u8 state)
+{
+	struct npcm7xx_kcs_bmc *priv = to_npcm7xx_kcs_bmc(kcs_bmc);
+
+	if (KCS_BMC_EVENT_TYPE_OBE & mask)
+		regmap_update_bits(priv->map, priv->reg->ctl, KCS_CTL_OBEIE,
+				   !!(KCS_BMC_EVENT_TYPE_OBE & state) * KCS_CTL_OBEIE);
+
+	if (KCS_BMC_EVENT_TYPE_IBF & mask)
+		regmap_update_bits(priv->map, priv->reg->ctl, KCS_CTL_IBFIE,
+				   !!(KCS_BMC_EVENT_TYPE_IBF & state) * KCS_CTL_IBFIE);
+}
+
 static irqreturn_t npcm7xx_kcs_irq(int irq, void *arg)
 {
 	struct kcs_bmc_device *kcs_bmc = arg;
@@ -151,6 +162,7 @@ static int npcm7xx_kcs_config_irq(struct kcs_bmc_device *kcs_bmc,
 }
 
 static const struct kcs_bmc_device_ops npcm7xx_kcs_ops = {
+	.irq_mask_update = npcm7xx_kcs_irq_mask_update,
 	.io_inputb = npcm7xx_kcs_inb,
 	.io_outputb = npcm7xx_kcs_outb,
 	.io_updateb = npcm7xx_kcs_updateb,
@@ -191,11 +203,13 @@ static int npcm7xx_kcs_probe(struct platform_device *pdev)
 
 	platform_set_drvdata(pdev, priv);
 
-	npcm7xx_kcs_enable_channel(kcs_bmc, true);
 	rc = npcm7xx_kcs_config_irq(kcs_bmc, pdev);
 	if (rc)
 		return rc;
 
+	npcm7xx_kcs_irq_mask_update(kcs_bmc, (KCS_BMC_EVENT_TYPE_IBF | KCS_BMC_EVENT_TYPE_OBE),
+				    KCS_BMC_EVENT_TYPE_IBF);
+	npcm7xx_kcs_enable_channel(kcs_bmc, true);
 
 	pr_info("channel=%u idr=0x%x odr=0x%x str=0x%x\n",
 		chan,
@@ -211,6 +225,9 @@ static int npcm7xx_kcs_remove(struct platform_device *pdev)
 
 	kcs_bmc_remove_device(kcs_bmc);
 
+	npcm7xx_kcs_enable_channel(kcs_bmc, false);
+	npcm7xx_kcs_irq_mask_update(kcs_bmc, (KCS_BMC_EVENT_TYPE_IBF | KCS_BMC_EVENT_TYPE_OBE), 0);
+
 	return 0;
 }
 
-- 
2.27.0


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

* [PATCH v2 15/21] ipmi: kcs_bmc: Don't enforce single-open policy in the kernel
  2021-03-19  6:27 [PATCH v2 01/21] dt-bindings: aspeed-lpc: Remove LPC partitioning Andrew Jeffery
                   ` (12 preceding siblings ...)
  2021-03-19  6:27 ` [PATCH v2 14/21] ipmi: kcs_bmc: Allow clients to control KCS IRQ state Andrew Jeffery
@ 2021-03-19  6:27 ` Andrew Jeffery
  2021-04-09  5:07   ` Zev Weiss
  2021-03-19  6:27 ` [PATCH v2 16/21] ipmi: kcs_bmc: Add a "raw" character device interface Andrew Jeffery
                   ` (6 subsequent siblings)
  20 siblings, 1 reply; 67+ messages in thread
From: Andrew Jeffery @ 2021-03-19  6:27 UTC (permalink / raw)
  To: openipmi-developer, openbmc, minyard
  Cc: devicetree, ryan_chen, tmaimon77, linux-aspeed, avifishman70,
	venture, linus.walleij, linux-kernel, tali.perry1, linux-gpio,
	robh+dt, lee.jones, chiawei_wang, linux-arm-kernel, benjaminfair

Soon it will be possible for one KCS device to have multiple associated
chardevs exposed to userspace (for IPMI and raw-style access). However,
don't prevent userspace from:

1. Opening more than one chardev at a time, or
2. Opening the same chardev more than once.

System behaviour is undefined for both classes of multiple access, so
userspace must manage itself accordingly.

The implementation delivers IBF and OBF events to the first chardev
client to associate with the KCS device. An open on a related chardev
cannot associate its client with the KCS device and so will not
receive notification of events. However, any fd on any chardev may race
their accesses to the data and status registers.

Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
---
 drivers/char/ipmi/kcs_bmc.c         | 34 ++++++++++-------------------
 drivers/char/ipmi/kcs_bmc_aspeed.c  |  3 +--
 drivers/char/ipmi/kcs_bmc_npcm7xx.c |  3 +--
 3 files changed, 14 insertions(+), 26 deletions(-)

diff --git a/drivers/char/ipmi/kcs_bmc.c b/drivers/char/ipmi/kcs_bmc.c
index 05bbb72418b2..2fafa9541934 100644
--- a/drivers/char/ipmi/kcs_bmc.c
+++ b/drivers/char/ipmi/kcs_bmc.c
@@ -55,24 +55,12 @@ EXPORT_SYMBOL(kcs_bmc_update_status);
 int kcs_bmc_handle_event(struct kcs_bmc_device *kcs_bmc)
 {
 	struct kcs_bmc_client *client;
-	int rc;
+	int rc = KCS_BMC_EVENT_NONE;
 
 	spin_lock(&kcs_bmc->lock);
 	client = kcs_bmc->client;
-	if (client) {
+	if (!WARN_ON_ONCE(!client))
 		rc = client->ops->event(client);
-	} else {
-		u8 status;
-
-		status = kcs_bmc_read_status(kcs_bmc);
-		if (status & KCS_BMC_STR_IBF) {
-			/* Ack the event by reading the data */
-			kcs_bmc_read_data(kcs_bmc);
-			rc = KCS_BMC_EVENT_HANDLED;
-		} else {
-			rc = KCS_BMC_EVENT_NONE;
-		}
-	}
 	spin_unlock(&kcs_bmc->lock);
 
 	return rc;
@@ -81,26 +69,28 @@ EXPORT_SYMBOL(kcs_bmc_handle_event);
 
 int kcs_bmc_enable_device(struct kcs_bmc_device *kcs_bmc, struct kcs_bmc_client *client)
 {
-	int rc;
-
 	spin_lock_irq(&kcs_bmc->lock);
-	if (kcs_bmc->client) {
-		rc = -EBUSY;
-	} else {
+	if (!kcs_bmc->client) {
+		u8 mask = KCS_BMC_EVENT_TYPE_IBF;
+
 		kcs_bmc->client = client;
-		rc = 0;
+		kcs_bmc_update_event_mask(kcs_bmc, mask, mask);
 	}
 	spin_unlock_irq(&kcs_bmc->lock);
 
-	return rc;
+	return 0;
 }
 EXPORT_SYMBOL(kcs_bmc_enable_device);
 
 void kcs_bmc_disable_device(struct kcs_bmc_device *kcs_bmc, struct kcs_bmc_client *client)
 {
 	spin_lock_irq(&kcs_bmc->lock);
-	if (client == kcs_bmc->client)
+	if (client == kcs_bmc->client) {
+		u8 mask = KCS_BMC_EVENT_TYPE_IBF | KCS_BMC_EVENT_TYPE_OBE;
+
+		kcs_bmc_update_event_mask(kcs_bmc, mask, 0);
 		kcs_bmc->client = NULL;
+	}
 	spin_unlock_irq(&kcs_bmc->lock);
 }
 EXPORT_SYMBOL(kcs_bmc_disable_device);
diff --git a/drivers/char/ipmi/kcs_bmc_aspeed.c b/drivers/char/ipmi/kcs_bmc_aspeed.c
index 5f26471c038c..271845eb2e26 100644
--- a/drivers/char/ipmi/kcs_bmc_aspeed.c
+++ b/drivers/char/ipmi/kcs_bmc_aspeed.c
@@ -419,8 +419,7 @@ static int aspeed_kcs_probe(struct platform_device *pdev)
 
 	platform_set_drvdata(pdev, priv);
 
-	aspeed_kcs_irq_mask_update(kcs_bmc, (KCS_BMC_EVENT_TYPE_IBF | KCS_BMC_EVENT_TYPE_OBE),
-				   KCS_BMC_EVENT_TYPE_IBF);
+	aspeed_kcs_irq_mask_update(kcs_bmc, (KCS_BMC_EVENT_TYPE_IBF | KCS_BMC_EVENT_TYPE_OBE), 0);
 	aspeed_kcs_enable_channel(kcs_bmc, true);
 
 	rc = kcs_bmc_add_device(&priv->kcs_bmc);
diff --git a/drivers/char/ipmi/kcs_bmc_npcm7xx.c b/drivers/char/ipmi/kcs_bmc_npcm7xx.c
index c2032728a03d..fdf35cad2eba 100644
--- a/drivers/char/ipmi/kcs_bmc_npcm7xx.c
+++ b/drivers/char/ipmi/kcs_bmc_npcm7xx.c
@@ -207,8 +207,7 @@ static int npcm7xx_kcs_probe(struct platform_device *pdev)
 	if (rc)
 		return rc;
 
-	npcm7xx_kcs_irq_mask_update(kcs_bmc, (KCS_BMC_EVENT_TYPE_IBF | KCS_BMC_EVENT_TYPE_OBE),
-				    KCS_BMC_EVENT_TYPE_IBF);
+	npcm7xx_kcs_irq_mask_update(kcs_bmc, (KCS_BMC_EVENT_TYPE_IBF | KCS_BMC_EVENT_TYPE_OBE), 0);
 	npcm7xx_kcs_enable_channel(kcs_bmc, true);
 
 	pr_info("channel=%u idr=0x%x odr=0x%x str=0x%x\n",
-- 
2.27.0


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

* [PATCH v2 16/21] ipmi: kcs_bmc: Add a "raw" character device interface
  2021-03-19  6:27 [PATCH v2 01/21] dt-bindings: aspeed-lpc: Remove LPC partitioning Andrew Jeffery
                   ` (13 preceding siblings ...)
  2021-03-19  6:27 ` [PATCH v2 15/21] ipmi: kcs_bmc: Don't enforce single-open policy in the kernel Andrew Jeffery
@ 2021-03-19  6:27 ` Andrew Jeffery
  2021-04-09  5:17   ` Zev Weiss
  2021-04-09  7:55   ` Arnd Bergmann
  2021-03-19  6:27 ` [PATCH v2 17/21] dt-bindings: ipmi: Convert ASPEED KCS binding to schema Andrew Jeffery
                   ` (5 subsequent siblings)
  20 siblings, 2 replies; 67+ messages in thread
From: Andrew Jeffery @ 2021-03-19  6:27 UTC (permalink / raw)
  To: openipmi-developer, openbmc, minyard
  Cc: devicetree, ryan_chen, tmaimon77, linux-aspeed, avifishman70,
	venture, linus.walleij, linux-kernel, tali.perry1, linux-gpio,
	robh+dt, lee.jones, chiawei_wang, linux-arm-kernel, benjaminfair

The existing IPMI chardev encodes IPMI behaviours as the name suggests.
However, KCS devices are useful beyond IPMI (or keyboards), as they
provide a means to generate IRQs and exchange arbitrary data between a
BMC and its host system.

Implement a "raw" KCS character device that exposes the IDR, ODR and STR
registers to userspace via read() and write() implemented on a character
device:

+--------+--------+---------+
| Offset | read() | write() |
+--------+--------+---------+
|   0    |   IDR  |   ODR   |
+--------+--------+---------+
|   1    |   STR  |   STR   |
+--------+--------+---------+

This interface allows userspace to implement arbitrary (though somewhat
inefficient) protocols for exchanging information between a BMC and host
firmware. Conceptually the KCS interface can be used as an out-of-band
machanism for interrupt-signaled control messages while bulk data
transfers occur over more appropriate interfaces between the BMC and the
host (which may lack their own interrupt mechanism, e.g. LPC FW cycles).

poll() is provided, which will wait for IBF or OBE conditions for data
reads and writes respectively. Reads of STR on its own never blocks,
though accessing both offsets in the one system call may block if the
data registers are not ready.

Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
---
 Documentation/ABI/testing/dev-raw-kcs |  25 ++
 drivers/char/ipmi/Kconfig             |  17 +
 drivers/char/ipmi/Makefile            |   1 +
 drivers/char/ipmi/kcs_bmc_cdev_raw.c  | 443 ++++++++++++++++++++++++++
 4 files changed, 486 insertions(+)
 create mode 100644 Documentation/ABI/testing/dev-raw-kcs
 create mode 100644 drivers/char/ipmi/kcs_bmc_cdev_raw.c

diff --git a/Documentation/ABI/testing/dev-raw-kcs b/Documentation/ABI/testing/dev-raw-kcs
new file mode 100644
index 000000000000..06e7e2071562
--- /dev/null
+++ b/Documentation/ABI/testing/dev-raw-kcs
@@ -0,0 +1,25 @@
+What:		/dev/raw-kcs*
+Date:		2021-02-15
+KernelVersion:	5.13
+Contact:	openbmc@lists.ozlabs.org
+Contact:	openipmi-developer@lists.sourceforge.net
+Contact:	Andrew Jeffery <andrew@aj.id.au>
+Description:	``/dev/raw-kcs*`` exposes to userspace the data and
+		status registers of Keyboard-Controller-Style (KCS) IPMI
+		interfaces via read() and write() syscalls. Direct
+		exposure of the data and status registers enables
+		inefficient but arbitrary protocols to be implemented
+		over the device. A typical approach is to use KCS
+		devices for out-of-band signalling for bulk data
+		transfers over other interfaces between a Baseboard
+		Management Controller and its host.
+
+		+--------+--------+---------+
+		| Offset | read() | write() |
+		+--------+--------+---------+
+		|   0    |   IDR  |   ODR   |
+		+--------+--------+---------+
+		|   1    |   STR  |   STR   |
+		+--------+--------+---------+
+
+Users:		libmctp: https://github.com/openbmc/libmctp
diff --git a/drivers/char/ipmi/Kconfig b/drivers/char/ipmi/Kconfig
index bc5f81899b62..273ac1a1f870 100644
--- a/drivers/char/ipmi/Kconfig
+++ b/drivers/char/ipmi/Kconfig
@@ -137,6 +137,23 @@ config IPMI_KCS_BMC_CDEV_IPMI
 	  This support is also available as a module. The module will be
 	  called kcs_bmc_cdev_ipmi.
 
+config IPMI_KCS_BMC_CDEV_RAW
+	depends on IPMI_KCS_BMC
+	tristate "Raw character device interface for BMC KCS devices"
+	help
+	  Provides a BMC-side character device directly exposing the
+	  data and status registers of a KCS device to userspace. While
+	  KCS devices are commonly used to implement IPMI message
+	  passing, they provide a general interface for exchange of
+	  interrupts, data and status information between the BMC and
+	  its host.
+
+	  Say YES if you wish to use the KCS devices to implement
+	  protocols that are not IPMI.
+
+	  This support is also available as a module. The module will be
+	  called kcs_bmc_cdev_raw.
+
 config ASPEED_BT_IPMI_BMC
 	depends on ARCH_ASPEED || COMPILE_TEST
 	depends on REGMAP && REGMAP_MMIO && MFD_SYSCON
diff --git a/drivers/char/ipmi/Makefile b/drivers/char/ipmi/Makefile
index fcfa676afddb..c8cc248ddd90 100644
--- a/drivers/char/ipmi/Makefile
+++ b/drivers/char/ipmi/Makefile
@@ -24,6 +24,7 @@ obj-$(CONFIG_IPMI_WATCHDOG) += ipmi_watchdog.o
 obj-$(CONFIG_IPMI_POWEROFF) += ipmi_poweroff.o
 obj-$(CONFIG_IPMI_KCS_BMC) += kcs_bmc.o
 obj-$(CONFIG_IPMI_KCS_BMC_CDEV_IPMI) += kcs_bmc_cdev_ipmi.o
+obj-$(CONFIG_IPMI_KCS_BMC_CDEV_RAW) += kcs_bmc_cdev_raw.o
 obj-$(CONFIG_ASPEED_BT_IPMI_BMC) += bt-bmc.o
 obj-$(CONFIG_ASPEED_KCS_IPMI_BMC) += kcs_bmc_aspeed.o
 obj-$(CONFIG_NPCM7XX_KCS_IPMI_BMC) += kcs_bmc_npcm7xx.o
diff --git a/drivers/char/ipmi/kcs_bmc_cdev_raw.c b/drivers/char/ipmi/kcs_bmc_cdev_raw.c
new file mode 100644
index 000000000000..bdd258648c8e
--- /dev/null
+++ b/drivers/char/ipmi/kcs_bmc_cdev_raw.c
@@ -0,0 +1,443 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* Copyright (c) 2021 IBM Corp. */
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/list.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/poll.h>
+
+#include "kcs_bmc_client.h"
+
+#define DEVICE_NAME "raw-kcs"
+
+struct kcs_bmc_raw {
+	struct list_head entry;
+
+	struct kcs_bmc_client client;
+
+	wait_queue_head_t queue;
+	u8 events;
+	bool writable;
+	bool readable;
+	u8 idr;
+
+	struct miscdevice miscdev;
+};
+
+static inline struct kcs_bmc_raw *client_to_kcs_bmc_raw(struct kcs_bmc_client *client)
+{
+	return container_of(client, struct kcs_bmc_raw, client);
+}
+
+/* Call under priv->queue.lock */
+static void kcs_bmc_raw_update_event_mask(struct kcs_bmc_raw *priv, u8 mask, u8 state)
+{
+	kcs_bmc_update_event_mask(priv->client.dev, mask, state);
+	priv->events &= ~mask;
+	priv->events |= state & mask;
+}
+
+static int kcs_bmc_raw_event(struct kcs_bmc_client *client)
+{
+	struct kcs_bmc_raw *priv;
+	struct device *dev;
+	u8 status, handled;
+
+	priv = client_to_kcs_bmc_raw(client);
+	dev = priv->miscdev.this_device;
+
+	spin_lock(&priv->queue.lock);
+
+	status = kcs_bmc_read_status(client->dev);
+	handled = 0;
+
+	if ((priv->events & KCS_BMC_EVENT_TYPE_IBF) && (status & KCS_BMC_STR_IBF)) {
+		if (priv->readable)
+			dev_err(dev, "Storm brewing!");
+
+		dev_dbg(dev, "Disabling IDR events for back-pressure\n");
+		kcs_bmc_raw_update_event_mask(priv, KCS_BMC_EVENT_TYPE_IBF, 0);
+		priv->idr = kcs_bmc_read_data(client->dev);
+		priv->readable = true;
+
+		dev_dbg(dev, "IDR read, waking waiters\n");
+		wake_up_locked(&priv->queue);
+
+		handled |= KCS_BMC_EVENT_TYPE_IBF;
+	}
+
+	if ((priv->events & KCS_BMC_EVENT_TYPE_OBE) && !(status & KCS_BMC_STR_OBF)) {
+		kcs_bmc_raw_update_event_mask(priv, KCS_BMC_EVENT_TYPE_OBE, 0);
+		priv->writable = true;
+
+		dev_dbg(dev, "ODR writable, waking waiters\n");
+		wake_up_locked(&priv->queue);
+
+		handled |= KCS_BMC_EVENT_TYPE_OBE;
+	}
+
+	spin_unlock(&priv->queue.lock);
+
+	return handled ? KCS_BMC_EVENT_HANDLED : KCS_BMC_EVENT_NONE;
+}
+
+static const struct kcs_bmc_client_ops kcs_bmc_raw_client_ops = {
+	.event = kcs_bmc_raw_event,
+};
+
+static inline struct kcs_bmc_raw *file_to_kcs_bmc_raw(struct file *filp)
+{
+	return container_of(filp->private_data, struct kcs_bmc_raw, miscdev);
+}
+
+static int kcs_bmc_raw_open(struct inode *inode, struct file *filp)
+{
+	struct kcs_bmc_raw *priv = file_to_kcs_bmc_raw(filp);
+
+	return kcs_bmc_enable_device(priv->client.dev, &priv->client);
+}
+
+static bool kcs_bmc_raw_prepare_obe(struct kcs_bmc_raw *priv)
+{
+	bool writable;
+
+	/* Enable the OBE event so we can catch the host clearing OBF */
+	kcs_bmc_raw_update_event_mask(priv, KCS_BMC_EVENT_TYPE_OBE, KCS_BMC_EVENT_TYPE_OBE);
+
+	/* Now that we'll catch an OBE event, check if it's already occurred */
+	writable = !(kcs_bmc_read_status(priv->client.dev) & KCS_BMC_STR_OBF);
+
+	/* If OBF is clear we've missed the OBE event, so disable it */
+	if (writable)
+		kcs_bmc_raw_update_event_mask(priv, KCS_BMC_EVENT_TYPE_OBE, 0);
+
+	return writable;
+}
+
+static __poll_t kcs_bmc_raw_poll(struct file *filp, poll_table *wait)
+{
+	struct kcs_bmc_raw *priv;
+	__poll_t events = 0;
+
+	priv = file_to_kcs_bmc_raw(filp);
+
+	poll_wait(filp, &priv->queue, wait);
+
+	spin_lock_irq(&priv->queue.lock);
+	if (kcs_bmc_raw_prepare_obe(priv))
+		events |= (EPOLLOUT | EPOLLWRNORM);
+
+	if (priv->readable || (kcs_bmc_read_status(priv->client.dev) & KCS_BMC_STR_IBF))
+		events |= (EPOLLIN | EPOLLRDNORM);
+	spin_unlock_irq(&priv->queue.lock);
+
+	return events;
+}
+
+static ssize_t kcs_bmc_raw_read(struct file *filp, char __user *buf,
+			     size_t count, loff_t *ppos)
+{
+	struct kcs_bmc_device *kcs_bmc;
+	struct kcs_bmc_raw *priv;
+	bool read_idr, read_str;
+	struct device *dev;
+	u8 idr, str;
+	ssize_t rc;
+
+	priv = file_to_kcs_bmc_raw(filp);
+	kcs_bmc = priv->client.dev;
+	dev = priv->miscdev.this_device;
+
+	if (!count)
+		return 0;
+
+	if (count > 2 || *ppos > 1)
+		return -EINVAL;
+
+	if (*ppos + count > 2)
+		return -EINVAL;
+
+	read_idr = (*ppos == 0);
+	read_str = (*ppos == 1) || (count == 2);
+
+	spin_lock_irq(&priv->queue.lock);
+	if (read_idr) {
+		dev_dbg(dev, "Waiting for IBF\n");
+		str = kcs_bmc_read_status(kcs_bmc);
+		if ((filp->f_flags & O_NONBLOCK) && (str & KCS_BMC_STR_IBF)) {
+			rc = -EWOULDBLOCK;
+			goto out;
+		}
+
+		rc = wait_event_interruptible_locked(priv->queue,
+						     priv->readable || (str & KCS_BMC_STR_IBF));
+		if (rc < 0)
+			goto out;
+
+		if (signal_pending(current)) {
+			dev_dbg(dev, "Interrupted waiting for IBF\n");
+			rc = -EINTR;
+			goto out;
+		}
+
+		/*
+		 * Re-enable events prior to possible read of IDR (which clears
+		 * IBF) to ensure we receive interrupts for subsequent writes
+		 * to IDR. Writes to IDR by the host should not occur while IBF
+		 * is set.
+		 */
+		dev_dbg(dev, "Woken by IBF, enabling IRQ\n");
+		kcs_bmc_raw_update_event_mask(priv, KCS_BMC_EVENT_TYPE_IBF,
+					      KCS_BMC_EVENT_TYPE_IBF);
+
+		/* Read data out of IDR into internal storage if necessary */
+		if (!priv->readable) {
+			WARN(!(str & KCS_BMC_STR_IBF), "Unknown reason for wakeup!");
+
+			priv->idr = kcs_bmc_read_data(kcs_bmc);
+		}
+
+		/* Copy data from internal storage to userspace */
+		idr = priv->idr;
+
+		/* We're done consuming the internally stored value */
+		priv->readable = false;
+	}
+
+	if (read_str) {
+		str = kcs_bmc_read_status(kcs_bmc);
+		if (*ppos == 0 || priv->readable)
+			/*
+			 * If we got this far with `*ppos == 0` then we've read
+			 * data out of IDR, so set IBF when reporting back to
+			 * userspace so userspace knows the IDR value is valid.
+			 */
+			str |= KCS_BMC_STR_IBF;
+
+		dev_dbg(dev, "Read status 0x%x\n", str);
+
+	}
+
+	rc = count;
+out:
+	spin_unlock_irq(&priv->queue.lock);
+
+	if (rc < 0)
+		return rc;
+
+	/* Now copy the data in to the userspace buffer */
+
+	if (read_idr)
+		if (copy_to_user(buf++, &idr, sizeof(idr)))
+			return -EFAULT;
+
+	if (read_str)
+		if (copy_to_user(buf, &str, sizeof(str)))
+			return -EFAULT;
+
+	return count;
+}
+
+static ssize_t kcs_bmc_raw_write(struct file *filp, const char __user *buf,
+			      size_t count, loff_t *ppos)
+{
+	struct kcs_bmc_device *kcs_bmc;
+	bool write_odr, write_str;
+	struct kcs_bmc_raw *priv;
+	struct device *dev;
+	uint8_t data[2];
+	ssize_t result;
+	u8 str;
+
+	priv = file_to_kcs_bmc_raw(filp);
+	kcs_bmc = priv->client.dev;
+	dev = priv->miscdev.this_device;
+
+	if (!count)
+		return count;
+
+	if (count > 2)
+		return -EINVAL;
+
+	if (*ppos >= 2)
+		return -EINVAL;
+
+	if (*ppos + count > 2)
+		return -EINVAL;
+
+	if (copy_from_user(data, buf, count))
+		return -EFAULT;
+
+	write_odr = (*ppos == 0);
+	write_str = (*ppos == 1) || (count == 2);
+
+	spin_lock_irq(&priv->queue.lock);
+
+	/* Always write status before data, we generate the SerIRQ by writing ODR */
+	if (write_str) {
+		/* The index of STR in the userspace buffer depends on whether ODR is written */
+		str = data[*ppos == 0];
+		if (!(str & KCS_BMC_STR_OBF))
+			dev_warn(dev, "Clearing OBF with status write: 0x%x\n", str);
+		dev_dbg(dev, "Writing status 0x%x\n", str);
+		kcs_bmc_write_status(kcs_bmc, str);
+	}
+
+	if (write_odr) {
+		/* If we're writing ODR it's always the first byte in the buffer */
+		u8 odr = data[0];
+
+		str = kcs_bmc_read_status(kcs_bmc);
+		if (str & KCS_BMC_STR_OBF) {
+			if (filp->f_flags & O_NONBLOCK) {
+				result = -EWOULDBLOCK;
+				goto out;
+			}
+
+			priv->writable = kcs_bmc_raw_prepare_obe(priv);
+
+			/* Now either OBF is already clear, or we'll get an OBE event to wake us */
+			dev_dbg(dev, "Waiting for OBF to clear\n");
+			wait_event_interruptible_locked(priv->queue, priv->writable);
+
+			if (signal_pending(current)) {
+				kcs_bmc_raw_update_event_mask(priv, KCS_BMC_EVENT_TYPE_OBE, 0);
+				result = -EINTR;
+				goto out;
+			}
+
+			WARN_ON(kcs_bmc_read_status(kcs_bmc) & KCS_BMC_STR_OBF);
+		}
+
+		dev_dbg(dev, "Writing 0x%x to ODR\n", odr);
+		kcs_bmc_write_data(kcs_bmc, odr);
+	}
+
+	result = count;
+out:
+	spin_unlock_irq(&priv->queue.lock);
+
+	return result;
+}
+
+static int kcs_bmc_raw_release(struct inode *inode, struct file *filp)
+{
+	struct kcs_bmc_raw *priv = file_to_kcs_bmc_raw(filp);
+
+	kcs_bmc_disable_device(priv->client.dev, &priv->client);
+
+	return 0;
+}
+
+static const struct file_operations kcs_bmc_raw_fops = {
+	.owner          = THIS_MODULE,
+	.open		= kcs_bmc_raw_open,
+	.llseek		= no_seek_end_llseek,
+	.read           = kcs_bmc_raw_read,
+	.write          = kcs_bmc_raw_write,
+	.poll		= kcs_bmc_raw_poll,
+	.release	= kcs_bmc_raw_release,
+};
+
+static DEFINE_SPINLOCK(kcs_bmc_raw_instances_lock);
+static LIST_HEAD(kcs_bmc_raw_instances);
+
+static int kcs_bmc_raw_attach_cdev(struct kcs_bmc_device *kcs_bmc)
+{
+	struct kcs_bmc_raw *priv;
+	int rc;
+
+	priv = devm_kzalloc(kcs_bmc->dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	priv->client.dev = kcs_bmc;
+	priv->client.ops = &kcs_bmc_raw_client_ops;
+
+	init_waitqueue_head(&priv->queue);
+	priv->writable = false;
+	priv->readable = false;
+
+	priv->miscdev.minor = MISC_DYNAMIC_MINOR;
+	priv->miscdev.name = devm_kasprintf(kcs_bmc->dev, GFP_KERNEL, "%s%u", DEVICE_NAME,
+					   kcs_bmc->channel);
+	if (!priv->miscdev.name)
+		return -EINVAL;
+
+	priv->miscdev.fops = &kcs_bmc_raw_fops;
+
+	/* Initialise our expected events. Listen for IBF but ignore OBE until necessary */
+	kcs_bmc_raw_update_event_mask(priv, (KCS_BMC_EVENT_TYPE_IBF | KCS_BMC_EVENT_TYPE_OBE),
+				      KCS_BMC_EVENT_TYPE_IBF);
+
+	rc = misc_register(&priv->miscdev);
+	if (rc) {
+		dev_err(kcs_bmc->dev, "Unable to register device\n");
+		return rc;
+	}
+
+	spin_lock_irq(&kcs_bmc_raw_instances_lock);
+	list_add(&priv->entry, &kcs_bmc_raw_instances);
+	spin_unlock_irq(&kcs_bmc_raw_instances_lock);
+
+	dev_info(kcs_bmc->dev, "Initialised raw client for channel %d", kcs_bmc->channel);
+
+	return 0;
+}
+
+static int kcs_bmc_raw_detach_cdev(struct kcs_bmc_device *kcs_bmc)
+{
+	struct kcs_bmc_raw *priv = NULL, *pos;
+
+	spin_lock_irq(&kcs_bmc_raw_instances_lock);
+	list_for_each_entry(pos, &kcs_bmc_raw_instances, entry) {
+		if (pos->client.dev == kcs_bmc) {
+			priv = pos;
+			list_del(&pos->entry);
+			break;
+		}
+	}
+	spin_unlock_irq(&kcs_bmc_raw_instances_lock);
+
+	if (!priv)
+		return 0;
+
+	misc_deregister(&priv->miscdev);
+	kcs_bmc_disable_device(kcs_bmc, &priv->client);
+	devm_kfree(priv->client.dev->dev, priv);
+
+	return 0;
+}
+
+static const struct kcs_bmc_cdev_ops kcs_bmc_raw_cdev_ops = {
+	.add_device = kcs_bmc_raw_attach_cdev,
+	.remove_device = kcs_bmc_raw_detach_cdev,
+};
+
+static struct kcs_bmc_cdev kcs_bmc_raw_cdev = {
+	.ops = &kcs_bmc_raw_cdev_ops,
+};
+
+static int kcs_bmc_raw_init(void)
+{
+	return kcs_bmc_register_cdev(&kcs_bmc_raw_cdev);
+}
+module_init(kcs_bmc_raw_init);
+
+static void kcs_bmc_raw_exit(void)
+{
+	int rc;
+
+	rc = kcs_bmc_unregister_cdev(&kcs_bmc_raw_cdev);
+	if (rc)
+		pr_warn("Failed to remove KCS BMC client: %d", rc);
+}
+module_exit(kcs_bmc_raw_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Andrew Jeffery <andrew@aj.id.au>");
+MODULE_DESCRIPTION("Character device for raw access to a KCS device");
-- 
2.27.0


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

* [PATCH v2 17/21] dt-bindings: ipmi: Convert ASPEED KCS binding to schema
  2021-03-19  6:27 [PATCH v2 01/21] dt-bindings: aspeed-lpc: Remove LPC partitioning Andrew Jeffery
                   ` (14 preceding siblings ...)
  2021-03-19  6:27 ` [PATCH v2 16/21] ipmi: kcs_bmc: Add a "raw" character device interface Andrew Jeffery
@ 2021-03-19  6:27 ` Andrew Jeffery
  2021-03-26  1:48   ` Rob Herring
  2021-04-09  5:15   ` Zev Weiss
  2021-03-19  6:27 ` [PATCH v2 18/21] dt-bindings: ipmi: Add optional SerIRQ property to ASPEED KCS devices Andrew Jeffery
                   ` (4 subsequent siblings)
  20 siblings, 2 replies; 67+ messages in thread
From: Andrew Jeffery @ 2021-03-19  6:27 UTC (permalink / raw)
  To: openipmi-developer, openbmc, minyard
  Cc: devicetree, ryan_chen, tmaimon77, linux-aspeed, avifishman70,
	venture, linus.walleij, linux-kernel, tali.perry1, linux-gpio,
	robh+dt, lee.jones, chiawei_wang, linux-arm-kernel, benjaminfair

Given the deprecated binding, improve the ability to detect issues in
the platform devicetrees. Further, a subsequent patch will introduce a
new interrupts property for specifying SerIRQ behaviour, so convert
before we do any further additions.

Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
---
 .../bindings/ipmi/aspeed,ast2400-kcs-bmc.yaml | 92 +++++++++++++++++++
 .../bindings/ipmi/aspeed-kcs-bmc.txt          | 33 -------
 2 files changed, 92 insertions(+), 33 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/ipmi/aspeed,ast2400-kcs-bmc.yaml
 delete mode 100644 Documentation/devicetree/bindings/ipmi/aspeed-kcs-bmc.txt

diff --git a/Documentation/devicetree/bindings/ipmi/aspeed,ast2400-kcs-bmc.yaml b/Documentation/devicetree/bindings/ipmi/aspeed,ast2400-kcs-bmc.yaml
new file mode 100644
index 000000000000..697ca575454f
--- /dev/null
+++ b/Documentation/devicetree/bindings/ipmi/aspeed,ast2400-kcs-bmc.yaml
@@ -0,0 +1,92 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/ipmi/aspeed,ast2400-kcs-bmc.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: ASPEED BMC KCS Devices
+
+maintainers:
+  - Andrew Jeffery <andrew@aj.id.au>
+
+description: |
+  The Aspeed BMC SoCs typically use the Keyboard-Controller-Style (KCS)
+  interfaces on the LPC bus for in-band IPMI communication with their host.
+
+properties:
+  compatible:
+    oneOf:
+      - description: Channel ID derived from reg
+        items:
+          enum:
+            - aspeed,ast2400-kcs-bmc-v2
+            - aspeed,ast2500-kcs-bmc-v2
+            - aspeed,ast2600-kcs-bmc
+
+      - description: Old-style with explicit channel ID, no reg
+        deprecated: true
+        items:
+          enum:
+            - aspeed,ast2400-kcs-bmc
+            - aspeed,ast2500-kcs-bmc
+
+  interrupts:
+    maxItems: 1
+
+  reg:
+    # maxItems: 3
+    items:
+      - description: IDR register
+      - description: ODR register
+      - description: STR register
+
+  aspeed,lpc-io-reg:
+    $ref: '/schemas/types.yaml#/definitions/uint32-array'
+    minItems: 1
+    maxItems: 2
+    description: |
+      The host CPU LPC IO data and status addresses for the device. For most
+      channels the status address is derived from the data address, but the
+      status address may be optionally provided.
+
+  kcs_chan:
+    deprecated: true
+    $ref: '/schemas/types.yaml#/definitions/uint32'
+    description: The LPC channel number in the controller
+
+  kcs_addr:
+    deprecated: true
+    $ref: '/schemas/types.yaml#/definitions/uint32'
+    description: The host CPU IO map address
+
+required:
+  - compatible
+  - interrupts
+
+additionalProperties: false
+
+allOf:
+  - if:
+      properties:
+        compatible:
+          contains:
+            enum:
+              - aspeed,ast2400-kcs-bmc
+              - aspeed,ast2500-kcs-bmc
+    then:
+      required:
+        - kcs_chan
+        - kcs_addr
+    else:
+      required:
+        - reg
+        - aspeed,lpc-io-reg
+
+examples:
+  - |
+    kcs3: kcs@24 {
+        compatible = "aspeed,ast2600-kcs-bmc";
+        reg = <0x24 0x1>, <0x30 0x1>, <0x3c 0x1>;
+        aspeed,lpc-io-reg = <0xca2>;
+        interrupts = <8>;
+    };
diff --git a/Documentation/devicetree/bindings/ipmi/aspeed-kcs-bmc.txt b/Documentation/devicetree/bindings/ipmi/aspeed-kcs-bmc.txt
deleted file mode 100644
index 193e71ca96b0..000000000000
--- a/Documentation/devicetree/bindings/ipmi/aspeed-kcs-bmc.txt
+++ /dev/null
@@ -1,33 +0,0 @@
-# Aspeed KCS (Keyboard Controller Style) IPMI interface
-
-The Aspeed SOCs (AST2400 and AST2500) are commonly used as BMCs
-(Baseboard Management Controllers) and the KCS interface can be
-used to perform in-band IPMI communication with their host.
-
-## v1
-Required properties:
-- compatible : should be one of
-    "aspeed,ast2400-kcs-bmc"
-    "aspeed,ast2500-kcs-bmc"
-- interrupts : interrupt generated by the controller
-- kcs_chan : The LPC channel number in the controller
-- kcs_addr : The host CPU IO map address
-
-## v2
-Required properties:
-- compatible : should be one of
-    "aspeed,ast2400-kcs-bmc-v2"
-    "aspeed,ast2500-kcs-bmc-v2"
-- reg : The address and size of the IDR, ODR and STR registers
-- interrupts : interrupt generated by the controller
-- aspeed,lpc-io-reg : The host CPU LPC IO address for the device
-
-Example:
-
-    kcs3: kcs@24 {
-        compatible = "aspeed,ast2500-kcs-bmc-v2";
-        reg = <0x24 0x1>, <0x30 0x1>, <0x3c 0x1>;
-        aspeed,lpc-reg = <0xca2>;
-        interrupts = <8>;
-        status = "okay";
-    };
-- 
2.27.0


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

* [PATCH v2 18/21] dt-bindings: ipmi: Add optional SerIRQ property to ASPEED KCS devices
  2021-03-19  6:27 [PATCH v2 01/21] dt-bindings: aspeed-lpc: Remove LPC partitioning Andrew Jeffery
                   ` (15 preceding siblings ...)
  2021-03-19  6:27 ` [PATCH v2 17/21] dt-bindings: ipmi: Convert ASPEED KCS binding to schema Andrew Jeffery
@ 2021-03-19  6:27 ` Andrew Jeffery
  2021-03-26  1:49   ` Rob Herring
  2021-03-19  6:27 ` [PATCH v2 19/21] ipmi: kcs_bmc_aspeed: Implement KCS SerIRQ configuration Andrew Jeffery
                   ` (3 subsequent siblings)
  20 siblings, 1 reply; 67+ messages in thread
From: Andrew Jeffery @ 2021-03-19  6:27 UTC (permalink / raw)
  To: openipmi-developer, openbmc, minyard
  Cc: devicetree, ryan_chen, tmaimon77, linux-aspeed, avifishman70,
	venture, linus.walleij, linux-kernel, tali.perry1, linux-gpio,
	robh+dt, lee.jones, chiawei_wang, linux-arm-kernel, benjaminfair

Allocating IO and IRQ resources to LPC devices is in-theory an operation
for the host, however ASPEED don't appear to expose this capability
outside the BMC (e.g. SuperIO). Instead, we are left with BMC-internal
registers for managing these resources, so introduce a devicetree
property for KCS devices to describe SerIRQ properties.

Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
---
 .../bindings/ipmi/aspeed,ast2400-kcs-bmc.yaml      | 14 ++++++++++++++
 1 file changed, 14 insertions(+)

diff --git a/Documentation/devicetree/bindings/ipmi/aspeed,ast2400-kcs-bmc.yaml b/Documentation/devicetree/bindings/ipmi/aspeed,ast2400-kcs-bmc.yaml
index 697ca575454f..4ff6fabfcb30 100644
--- a/Documentation/devicetree/bindings/ipmi/aspeed,ast2400-kcs-bmc.yaml
+++ b/Documentation/devicetree/bindings/ipmi/aspeed,ast2400-kcs-bmc.yaml
@@ -49,6 +49,18 @@ properties:
       channels the status address is derived from the data address, but the
       status address may be optionally provided.
 
+  aspeed,lpc-interrupts:
+    $ref: "/schemas/types.yaml#/definitions/uint32-array"
+    minItems: 2
+    maxItems: 2
+    description: |
+      A 2-cell property expressing the LPC SerIRQ number and the interrupt
+      level/sense encoding (specified in the standard fashion).
+
+      Note that the generated interrupt is issued from the BMC to the host, and
+      thus the target interrupt controller is not captured by the BMC's
+      devicetree.
+
   kcs_chan:
     deprecated: true
     $ref: '/schemas/types.yaml#/definitions/uint32'
@@ -84,9 +96,11 @@ allOf:
 
 examples:
   - |
+    #include <dt-bindings/interrupt-controller/irq.h>
     kcs3: kcs@24 {
         compatible = "aspeed,ast2600-kcs-bmc";
         reg = <0x24 0x1>, <0x30 0x1>, <0x3c 0x1>;
         aspeed,lpc-io-reg = <0xca2>;
+        aspeed,lpc-interrupts = <11 IRQ_TYPE_LEVEL_LOW>;
         interrupts = <8>;
     };
-- 
2.27.0


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

* [PATCH v2 19/21] ipmi: kcs_bmc_aspeed: Implement KCS SerIRQ configuration
  2021-03-19  6:27 [PATCH v2 01/21] dt-bindings: aspeed-lpc: Remove LPC partitioning Andrew Jeffery
                   ` (16 preceding siblings ...)
  2021-03-19  6:27 ` [PATCH v2 18/21] dt-bindings: ipmi: Add optional SerIRQ property to ASPEED KCS devices Andrew Jeffery
@ 2021-03-19  6:27 ` Andrew Jeffery
  2021-04-01  9:30   ` [EXTERNAL] " Zev Weiss
  2021-03-19  6:27 ` [PATCH v2 20/21] ipmi: kcs_bmc_aspeed: Fix IBFIE typo from datasheet Andrew Jeffery
                   ` (2 subsequent siblings)
  20 siblings, 1 reply; 67+ messages in thread
From: Andrew Jeffery @ 2021-03-19  6:27 UTC (permalink / raw)
  To: openipmi-developer, openbmc, minyard
  Cc: devicetree, ryan_chen, tmaimon77, linux-aspeed, avifishman70,
	venture, linus.walleij, linux-kernel, tali.perry1, linux-gpio,
	robh+dt, lee.jones, chiawei_wang, linux-arm-kernel, benjaminfair

Apply the SerIRQ ID and level/sense behaviours from the devicetree if
provided.

Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
---
 drivers/char/ipmi/kcs_bmc_aspeed.c | 179 ++++++++++++++++++++++++++++-
 1 file changed, 177 insertions(+), 2 deletions(-)

diff --git a/drivers/char/ipmi/kcs_bmc_aspeed.c b/drivers/char/ipmi/kcs_bmc_aspeed.c
index 271845eb2e26..3782aef4eb73 100644
--- a/drivers/char/ipmi/kcs_bmc_aspeed.c
+++ b/drivers/char/ipmi/kcs_bmc_aspeed.c
@@ -9,6 +9,7 @@
 #include <linux/errno.h>
 #include <linux/interrupt.h>
 #include <linux/io.h>
+#include <linux/irq.h>
 #include <linux/mfd/syscon.h>
 #include <linux/module.h>
 #include <linux/of.h>
@@ -28,6 +29,22 @@
 
 #define KCS_CHANNEL_MAX     4
 
+/*
+ * Field class descriptions
+ *
+ * LPCyE	Enable LPC channel y
+ * IBFIEy	Input Buffer Full IRQ Enable for LPC channel y
+ * IRQxEy	Assert SerIRQ x for LPC channel y (Deprecated, use IDyIRQX, IRQXEy)
+ * IDyIRQX	Use the specified 4-bit SerIRQ for LPC channel y
+ * SELyIRQX	SerIRQ polarity for LPC channel y (low: 0, high: 1)
+ * IRQXEy	Assert the SerIRQ specified in IDyIRQX for LPC channel y
+ */
+
+#define LPC_TYIRQX_LOW       0b00
+#define LPC_TYIRQX_HIGH      0b01
+#define LPC_TYIRQX_RSVD      0b10
+#define LPC_TYIRQX_RISING    0b11
+
 #define LPC_HICR0            0x000
 #define     LPC_HICR0_LPC3E          BIT(7)
 #define     LPC_HICR0_LPC2E          BIT(6)
@@ -39,6 +56,19 @@
 #define LPC_HICR4            0x010
 #define     LPC_HICR4_LADR12AS       BIT(7)
 #define     LPC_HICR4_KCSENBL        BIT(2)
+#define LPC_SIRQCR0	     0x070
+/* IRQ{12,1}E1 are deprecated as of AST2600 A3 but necessary for prior chips */
+#define     LPC_SIRQCR0_IRQ12E1	     BIT(1)
+#define     LPC_SIRQCR0_IRQ1E1	     BIT(0)
+#define LPC_HICR5	     0x080
+#define     LPC_HICR5_ID3IRQX_MASK   GENMASK(23, 20)
+#define     LPC_HICR5_ID3IRQX_SHIFT  20
+#define     LPC_HICR5_ID2IRQX_MASK   GENMASK(19, 16)
+#define     LPC_HICR5_ID2IRQX_SHIFT  16
+#define     LPC_HICR5_SEL3IRQX       BIT(15)
+#define     LPC_HICR5_IRQXE3         BIT(14)
+#define     LPC_HICR5_SEL2IRQX       BIT(13)
+#define     LPC_HICR5_IRQXE2         BIT(12)
 #define LPC_LADR3H           0x014
 #define LPC_LADR3L           0x018
 #define LPC_LADR12H          0x01C
@@ -55,6 +85,13 @@
 #define LPC_HICRB            0x100
 #define     LPC_HICRB_IBFIF4         BIT(1)
 #define     LPC_HICRB_LPC4E          BIT(0)
+#define LPC_HICRC            0x104
+#define     LPC_HICRC_ID4IRQX_MASK   GENMASK(7, 4)
+#define     LPC_HICRC_ID4IRQX_SHIFT  4
+#define     LPC_HICRC_TY4IRQX_MASK   GENMASK(3, 2)
+#define     LPC_HICRC_TY4IRQX_SHIFT  2
+#define     LPC_HICRC_OBF4_AUTO_CLR  BIT(1)
+#define     LPC_HICRC_IRQXE4         BIT(0)
 #define LPC_LADR4            0x110
 #define LPC_IDR4             0x114
 #define LPC_ODR4             0x118
@@ -62,11 +99,21 @@
 
 #define OBE_POLL_PERIOD	     (HZ / 2)
 
+enum aspeed_kcs_irq_mode {
+	aspeed_kcs_irq_none,
+	aspeed_kcs_irq_serirq,
+};
+
 struct aspeed_kcs_bmc {
 	struct kcs_bmc_device kcs_bmc;
 
 	struct regmap *map;
 
+	struct {
+		enum aspeed_kcs_irq_mode mode;
+		int id;
+	} upstream_irq;
+
 	struct {
 		spinlock_t lock;
 		bool remove;
@@ -103,6 +150,49 @@ static void aspeed_kcs_outb(struct kcs_bmc_device *kcs_bmc, u32 reg, u8 data)
 
 	rc = regmap_write(priv->map, reg, data);
 	WARN(rc != 0, "regmap_write() failed: %d\n", rc);
+
+	/* Trigger the upstream IRQ on ODR writes, if enabled */
+
+	switch (reg) {
+	case LPC_ODR1:
+	case LPC_ODR2:
+	case LPC_ODR3:
+	case LPC_ODR4:
+		break;
+	default:
+		return;
+	}
+
+	if (priv->upstream_irq.mode != aspeed_kcs_irq_serirq)
+		return;
+
+	switch (kcs_bmc->channel) {
+	case 1:
+		switch (priv->upstream_irq.id) {
+		case 12:
+			regmap_update_bits(priv->map, LPC_SIRQCR0, LPC_SIRQCR0_IRQ12E1,
+					   LPC_SIRQCR0_IRQ12E1);
+			break;
+		case 1:
+			regmap_update_bits(priv->map, LPC_SIRQCR0, LPC_SIRQCR0_IRQ1E1,
+					   LPC_SIRQCR0_IRQ1E1);
+			break;
+		default:
+			break;
+		}
+		break;
+	case 2:
+		regmap_update_bits(priv->map, LPC_HICR5, LPC_HICR5_IRQXE2, LPC_HICR5_IRQXE2);
+		break;
+	case 3:
+		regmap_update_bits(priv->map, LPC_HICR5, LPC_HICR5_IRQXE3, LPC_HICR5_IRQXE3);
+		break;
+	case 4:
+		regmap_update_bits(priv->map, LPC_HICRC, LPC_HICRC_IRQXE4, LPC_HICRC_IRQXE4);
+		break;
+	default:
+		break;
+	}
 }
 
 static void aspeed_kcs_updateb(struct kcs_bmc_device *kcs_bmc, u32 reg, u8 mask, u8 val)
@@ -161,6 +251,73 @@ static void aspeed_kcs_set_address(struct kcs_bmc_device *kcs_bmc, u16 addr)
 	}
 }
 
+static inline int aspeed_kcs_map_serirq_type(u32 dt_type)
+{
+	switch (dt_type) {
+	case IRQ_TYPE_EDGE_RISING:
+		return LPC_TYIRQX_RISING;
+	case IRQ_TYPE_LEVEL_HIGH:
+		return LPC_TYIRQX_HIGH;
+	case IRQ_TYPE_LEVEL_LOW:
+		return LPC_TYIRQX_LOW;
+	default:
+		return -EINVAL;
+	}
+}
+
+static int aspeed_kcs_config_upstream_irq(struct aspeed_kcs_bmc *priv, u32 id, u32 dt_type)
+{
+	unsigned int mask, val, hw_type;
+
+	if (id > 15)
+		return -EINVAL;
+
+	hw_type = aspeed_kcs_map_serirq_type(dt_type);
+	if (hw_type < 0)
+		return hw_type;
+
+	priv->upstream_irq.mode = aspeed_kcs_irq_serirq;
+	priv->upstream_irq.id = id;
+
+	switch (priv->kcs_bmc.channel) {
+	case 1:
+		/* Needs IRQxE1 rather than (ID1IRQX, SEL1IRQX, IRQXE1) before AST2600 A3 */
+		break;
+	case 2:
+		if (!(hw_type == LPC_TYIRQX_LOW || hw_type == LPC_TYIRQX_HIGH))
+			return -EINVAL;
+
+		mask = LPC_HICR5_SEL2IRQX | LPC_HICR5_ID2IRQX_MASK;
+		val = (id << LPC_HICR5_ID2IRQX_SHIFT);
+		val |= (hw_type == LPC_TYIRQX_HIGH) ? LPC_HICR5_SEL2IRQX : 0;
+		regmap_update_bits(priv->map, LPC_HICR5, mask, val);
+
+		break;
+	case 3:
+		if (!(hw_type == LPC_TYIRQX_LOW || hw_type == LPC_TYIRQX_HIGH))
+			return -EINVAL;
+
+		mask = LPC_HICR5_SEL3IRQX | LPC_HICR5_ID3IRQX_MASK;
+		val = (id << LPC_HICR5_ID3IRQX_SHIFT);
+		val |= (hw_type == LPC_TYIRQX_HIGH) ? LPC_HICR5_SEL3IRQX : 0;
+		regmap_update_bits(priv->map, LPC_HICR5, mask, val);
+
+		break;
+	case 4:
+		mask = LPC_HICRC_ID4IRQX_MASK | LPC_HICRC_TY4IRQX_MASK | LPC_HICRC_OBF4_AUTO_CLR;
+		val = (id << LPC_HICRC_ID4IRQX_SHIFT) | (hw_type << LPC_HICRC_TY4IRQX_SHIFT);
+		regmap_update_bits(priv->map, LPC_HICRC, mask, val);
+		break;
+	default:
+		dev_warn(priv->kcs_bmc.dev,
+			 "SerIRQ configuration not supported on KCS channel %d\n",
+			 priv->kcs_bmc.channel);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
 static void aspeed_kcs_enable_channel(struct kcs_bmc_device *kcs_bmc, bool enable)
 {
 	struct aspeed_kcs_bmc *priv = to_aspeed_kcs_bmc(kcs_bmc);
@@ -267,7 +424,7 @@ static irqreturn_t aspeed_kcs_irq(int irq, void *arg)
 	return rc == KCS_BMC_EVENT_HANDLED ? IRQ_HANDLED : IRQ_NONE;
 }
 
-static int aspeed_kcs_config_irq(struct kcs_bmc_device *kcs_bmc,
+static int aspeed_kcs_config_downstream_irq(struct kcs_bmc_device *kcs_bmc,
 			struct platform_device *pdev)
 {
 	struct device *dev = &pdev->dev;
@@ -371,6 +528,8 @@ static int aspeed_kcs_probe(struct platform_device *pdev)
 	struct aspeed_kcs_bmc *priv;
 	struct device_node *np;
 	int rc, channel, addr;
+	bool have_upstream_irq;
+	u32 upstream_irq[2];
 
 	np = pdev->dev.of_node->parent;
 	if (!of_device_is_compatible(np, "aspeed,ast2400-lpc-v2") &&
@@ -379,6 +538,7 @@ static int aspeed_kcs_probe(struct platform_device *pdev)
 		dev_err(&pdev->dev, "unsupported LPC device binding\n");
 		return -ENODEV;
 	}
+
 	ops = of_device_get_match_data(&pdev->dev);
 	if (!ops)
 		return -EINVAL;
@@ -391,6 +551,13 @@ static int aspeed_kcs_probe(struct platform_device *pdev)
 	if (addr < 0)
 		return addr;
 
+	np = pdev->dev.of_node;
+	rc = of_property_read_u32_array(np, "aspeed,lpc-interrupts", upstream_irq, 2);
+	if ((rc && rc != -EINVAL))
+		return -EINVAL;
+
+	have_upstream_irq = !rc;
+
 	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
 	if (!priv)
 		return -ENOMEM;
@@ -413,10 +580,17 @@ static int aspeed_kcs_probe(struct platform_device *pdev)
 
 	aspeed_kcs_set_address(kcs_bmc, addr);
 
-	rc = aspeed_kcs_config_irq(kcs_bmc, pdev);
+	/* Host to BMC IRQ */
+	rc = aspeed_kcs_config_downstream_irq(kcs_bmc, pdev);
 	if (rc)
 		return rc;
 
+	/* BMC to Host IRQ */
+	if (have_upstream_irq)
+		aspeed_kcs_config_upstream_irq(priv, upstream_irq[0], upstream_irq[1]);
+	else
+		priv->upstream_irq.mode = aspeed_kcs_irq_none;
+
 	platform_set_drvdata(pdev, priv);
 
 	aspeed_kcs_irq_mask_update(kcs_bmc, (KCS_BMC_EVENT_TYPE_IBF | KCS_BMC_EVENT_TYPE_OBE), 0);
@@ -481,4 +655,5 @@ module_platform_driver(ast_kcs_bmc_driver);
 
 MODULE_LICENSE("GPL v2");
 MODULE_AUTHOR("Haiyue Wang <haiyue.wang@linux.intel.com>");
+MODULE_AUTHOR("Andrew Jeffery <andrew@aj.id.au>");
 MODULE_DESCRIPTION("Aspeed device interface to the KCS BMC device");
-- 
2.27.0


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

* [PATCH v2 20/21] ipmi: kcs_bmc_aspeed: Fix IBFIE typo from datasheet
  2021-03-19  6:27 [PATCH v2 01/21] dt-bindings: aspeed-lpc: Remove LPC partitioning Andrew Jeffery
                   ` (17 preceding siblings ...)
  2021-03-19  6:27 ` [PATCH v2 19/21] ipmi: kcs_bmc_aspeed: Implement KCS SerIRQ configuration Andrew Jeffery
@ 2021-03-19  6:27 ` Andrew Jeffery
  2021-04-09  5:40   ` Zev Weiss
  2021-03-19  6:27 ` [PATCH v2 21/21] ipmi: kcs_bmc_aspeed: Optionally apply status address Andrew Jeffery
  2021-04-09  3:18 ` [PATCH v2 01/21] dt-bindings: aspeed-lpc: Remove LPC partitioning Joel Stanley
  20 siblings, 1 reply; 67+ messages in thread
From: Andrew Jeffery @ 2021-03-19  6:27 UTC (permalink / raw)
  To: openipmi-developer, openbmc, minyard
  Cc: devicetree, ryan_chen, tmaimon77, linux-aspeed, avifishman70,
	venture, linus.walleij, linux-kernel, tali.perry1, linux-gpio,
	robh+dt, lee.jones, chiawei_wang, linux-arm-kernel, benjaminfair

Input Buffer Full Interrupt Enable (IBFIE) is typoed as IBFIF for some
registers in the datasheet. Fix the driver to use the sensible acronym.

Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
---
 drivers/char/ipmi/kcs_bmc_aspeed.c | 24 ++++++++++++------------
 1 file changed, 12 insertions(+), 12 deletions(-)

diff --git a/drivers/char/ipmi/kcs_bmc_aspeed.c b/drivers/char/ipmi/kcs_bmc_aspeed.c
index 3782aef4eb73..7334b1f51dcc 100644
--- a/drivers/char/ipmi/kcs_bmc_aspeed.c
+++ b/drivers/char/ipmi/kcs_bmc_aspeed.c
@@ -50,9 +50,9 @@
 #define     LPC_HICR0_LPC2E          BIT(6)
 #define     LPC_HICR0_LPC1E          BIT(5)
 #define LPC_HICR2            0x008
-#define     LPC_HICR2_IBFIF3         BIT(3)
-#define     LPC_HICR2_IBFIF2         BIT(2)
-#define     LPC_HICR2_IBFIF1         BIT(1)
+#define     LPC_HICR2_IBFIE3         BIT(3)
+#define     LPC_HICR2_IBFIE2         BIT(2)
+#define     LPC_HICR2_IBFIE1         BIT(1)
 #define LPC_HICR4            0x010
 #define     LPC_HICR4_LADR12AS       BIT(7)
 #define     LPC_HICR4_KCSENBL        BIT(2)
@@ -83,7 +83,7 @@
 #define LPC_STR2             0x040
 #define LPC_STR3             0x044
 #define LPC_HICRB            0x100
-#define     LPC_HICRB_IBFIF4         BIT(1)
+#define     LPC_HICRB_IBFIE4         BIT(1)
 #define     LPC_HICRB_LPC4E          BIT(0)
 #define LPC_HICRC            0x104
 #define     LPC_HICRC_ID4IRQX_MASK   GENMASK(7, 4)
@@ -383,20 +383,20 @@ static void aspeed_kcs_irq_mask_update(struct kcs_bmc_device *kcs_bmc, u8 mask,
 
 		switch (kcs_bmc->channel) {
 		case 1:
-			regmap_update_bits(priv->map, LPC_HICR2, LPC_HICR2_IBFIF1,
-					   enable * LPC_HICR2_IBFIF1);
+			regmap_update_bits(priv->map, LPC_HICR2, LPC_HICR2_IBFIE1,
+					   enable * LPC_HICR2_IBFIE1);
 			return;
 		case 2:
-			regmap_update_bits(priv->map, LPC_HICR2, LPC_HICR2_IBFIF2,
-					   enable * LPC_HICR2_IBFIF2);
+			regmap_update_bits(priv->map, LPC_HICR2, LPC_HICR2_IBFIE2,
+					   enable * LPC_HICR2_IBFIE2);
 			return;
 		case 3:
-			regmap_update_bits(priv->map, LPC_HICR2, LPC_HICR2_IBFIF3,
-					   enable * LPC_HICR2_IBFIF3);
+			regmap_update_bits(priv->map, LPC_HICR2, LPC_HICR2_IBFIE3,
+					   enable * LPC_HICR2_IBFIE3);
 			return;
 		case 4:
-			regmap_update_bits(priv->map, LPC_HICRB, LPC_HICRB_IBFIF4,
-					   enable * LPC_HICRB_IBFIF4);
+			regmap_update_bits(priv->map, LPC_HICRB, LPC_HICRB_IBFIE4,
+					   enable * LPC_HICRB_IBFIE4);
 			return;
 		default:
 			pr_warn("%s: Unsupported channel: %d", __func__, kcs_bmc->channel);
-- 
2.27.0


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

* [PATCH v2 21/21] ipmi: kcs_bmc_aspeed: Optionally apply status address
  2021-03-19  6:27 [PATCH v2 01/21] dt-bindings: aspeed-lpc: Remove LPC partitioning Andrew Jeffery
                   ` (18 preceding siblings ...)
  2021-03-19  6:27 ` [PATCH v2 20/21] ipmi: kcs_bmc_aspeed: Fix IBFIE typo from datasheet Andrew Jeffery
@ 2021-03-19  6:27 ` Andrew Jeffery
  2021-04-01 18:18   ` Re " Zev Weiss
  2021-04-06  6:09   ` ChiaWei Wang
  2021-04-09  3:18 ` [PATCH v2 01/21] dt-bindings: aspeed-lpc: Remove LPC partitioning Joel Stanley
  20 siblings, 2 replies; 67+ messages in thread
From: Andrew Jeffery @ 2021-03-19  6:27 UTC (permalink / raw)
  To: openipmi-developer, openbmc, minyard
  Cc: devicetree, ryan_chen, tmaimon77, linux-aspeed, avifishman70,
	venture, linus.walleij, linux-kernel, tali.perry1, linux-gpio,
	robh+dt, lee.jones, chiawei_wang, linux-arm-kernel, benjaminfair

Some Aspeed KCS devices can derive the status register address from the
address of the data register. As such, the address of the status
register can be implicit in the configuration if desired. On the other
hand, sometimes address schemes might be requested that are incompatible
with the default addressing scheme. Allow these requests where possible
if the devicetree specifies the status register address.

Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
---
 drivers/char/ipmi/kcs_bmc_aspeed.c | 113 +++++++++++++++++++++--------
 1 file changed, 81 insertions(+), 32 deletions(-)

diff --git a/drivers/char/ipmi/kcs_bmc_aspeed.c b/drivers/char/ipmi/kcs_bmc_aspeed.c
index 7334b1f51dcc..98789b837690 100644
--- a/drivers/char/ipmi/kcs_bmc_aspeed.c
+++ b/drivers/char/ipmi/kcs_bmc_aspeed.c
@@ -83,6 +83,8 @@
 #define LPC_STR2             0x040
 #define LPC_STR3             0x044
 #define LPC_HICRB            0x100
+#define     LPC_HICRB_EN16LADR2      BIT(5)
+#define     LPC_HICRB_EN16LADR1      BIT(4)
 #define     LPC_HICRB_IBFIE4         BIT(1)
 #define     LPC_HICRB_LPC4E          BIT(0)
 #define LPC_HICRC            0x104
@@ -96,6 +98,11 @@
 #define LPC_IDR4             0x114
 #define LPC_ODR4             0x118
 #define LPC_STR4             0x11C
+#define LPC_LSADR12	     0x120
+#define     LPC_LSADR12_LSADR2_MASK  GENMASK(31, 16)
+#define     LPC_LSADR12_LSADR2_SHIFT 16
+#define     LPC_LSADR12_LSADR1_MASK  GENMASK(15, 0)
+#define     LPC_LSADR12_LSADR1_SHIFT 0
 
 #define OBE_POLL_PERIOD	     (HZ / 2)
 
@@ -123,7 +130,7 @@ struct aspeed_kcs_bmc {
 
 struct aspeed_kcs_of_ops {
 	int (*get_channel)(struct platform_device *pdev);
-	int (*get_io_address)(struct platform_device *pdev);
+	int (*get_io_address)(struct platform_device *pdev, u32 addrs[2]);
 };
 
 static inline struct aspeed_kcs_bmc *to_aspeed_kcs_bmc(struct kcs_bmc_device *kcs_bmc)
@@ -217,38 +224,64 @@ static void aspeed_kcs_updateb(struct kcs_bmc_device *kcs_bmc, u32 reg, u8 mask,
  *     C. KCS4
  *        D / C : CA4h / CA5h
  */
-static void aspeed_kcs_set_address(struct kcs_bmc_device *kcs_bmc, u16 addr)
+static int aspeed_kcs_set_address(struct kcs_bmc_device *kcs_bmc, u32 addrs[2], int nr_addrs)
 {
 	struct aspeed_kcs_bmc *priv = to_aspeed_kcs_bmc(kcs_bmc);
 
-	switch (kcs_bmc->channel) {
+	if (WARN_ON(nr_addrs < 1 || nr_addrs > 2))
+		return -EINVAL;
+
+	switch (priv->kcs_bmc.channel) {
 	case 1:
-		regmap_update_bits(priv->map, LPC_HICR4,
-				LPC_HICR4_LADR12AS, 0);
-		regmap_write(priv->map, LPC_LADR12H, addr >> 8);
-		regmap_write(priv->map, LPC_LADR12L, addr & 0xFF);
+		regmap_update_bits(priv->map, LPC_HICR4, LPC_HICR4_LADR12AS, 0);
+		regmap_write(priv->map, LPC_LADR12H, addrs[0] >> 8);
+		regmap_write(priv->map, LPC_LADR12L, addrs[0] & 0xFF);
+		if (nr_addrs == 2) {
+			regmap_update_bits(priv->map, LPC_LSADR12, LPC_LSADR12_LSADR1_MASK,
+					   addrs[1] << LPC_LSADR12_LSADR1_SHIFT);
+
+			regmap_update_bits(priv->map, LPC_HICRB, LPC_HICRB_EN16LADR1,
+					   LPC_HICRB_EN16LADR1);
+		}
 		break;
 
 	case 2:
-		regmap_update_bits(priv->map, LPC_HICR4,
-				LPC_HICR4_LADR12AS, LPC_HICR4_LADR12AS);
-		regmap_write(priv->map, LPC_LADR12H, addr >> 8);
-		regmap_write(priv->map, LPC_LADR12L, addr & 0xFF);
+		regmap_update_bits(priv->map, LPC_HICR4, LPC_HICR4_LADR12AS, LPC_HICR4_LADR12AS);
+		regmap_write(priv->map, LPC_LADR12H, addrs[0] >> 8);
+		regmap_write(priv->map, LPC_LADR12L, addrs[0] & 0xFF);
+		if (nr_addrs == 2) {
+			regmap_update_bits(priv->map, LPC_LSADR12, LPC_LSADR12_LSADR2_MASK,
+					   addrs[1] << LPC_LSADR12_LSADR2_SHIFT);
+
+			regmap_update_bits(priv->map, LPC_HICRB, LPC_HICRB_EN16LADR2,
+					   LPC_HICRB_EN16LADR2);
+		}
 		break;
 
 	case 3:
-		regmap_write(priv->map, LPC_LADR3H, addr >> 8);
-		regmap_write(priv->map, LPC_LADR3L, addr & 0xFF);
+		if (nr_addrs == 2) {
+			dev_err(priv->kcs_bmc.dev,
+				"Channel 3 only supports inferred status IO address\n");
+			return -EINVAL;
+		}
+
+		regmap_write(priv->map, LPC_LADR3H, addrs[0] >> 8);
+		regmap_write(priv->map, LPC_LADR3L, addrs[0] & 0xFF);
 		break;
 
 	case 4:
-		regmap_write(priv->map, LPC_LADR4, ((addr + 1) << 16) |
-			addr);
+		if (nr_addrs == 1)
+			regmap_write(priv->map, LPC_LADR4, ((addrs[0] + 1) << 16) | addrs[0]);
+		else
+			regmap_write(priv->map, LPC_LADR4, (addrs[1] << 16) | addrs[0]);
+
 		break;
 
 	default:
-		break;
+		return -EINVAL;
 	}
+
+	return 0;
 }
 
 static inline int aspeed_kcs_map_serirq_type(u32 dt_type)
@@ -462,18 +495,18 @@ static int aspeed_kcs_of_v1_get_channel(struct platform_device *pdev)
 	return channel;
 }
 
-static int aspeed_kcs_of_v1_get_io_address(struct platform_device *pdev)
+static int
+aspeed_kcs_of_v1_get_io_address(struct platform_device *pdev, u32 addrs[2])
 {
-	u32 slave;
 	int rc;
 
-	rc = of_property_read_u32(pdev->dev.of_node, "kcs_addr", &slave);
-	if (rc || slave > 0xffff) {
+	rc = of_property_read_u32(pdev->dev.of_node, "kcs_addr", addrs);
+	if (rc || addrs[0] > 0xffff) {
 		dev_err(&pdev->dev, "no valid 'kcs_addr' configured\n");
 		return -EINVAL;
 	}
 
-	return slave;
+	return 1;
 }
 
 static int aspeed_kcs_of_v2_get_channel(struct platform_device *pdev)
@@ -509,16 +542,27 @@ static int aspeed_kcs_of_v2_get_channel(struct platform_device *pdev)
 	return -EINVAL;
 }
 
-static int aspeed_kcs_of_v2_get_io_address(struct platform_device *pdev)
+static int
+aspeed_kcs_of_v2_get_io_address(struct platform_device *pdev, u32 addrs[2])
 {
-	uint32_t slave;
 	int rc;
 
-	rc = of_property_read_u32(pdev->dev.of_node, "aspeed,lpc-io-reg", &slave);
-	if (rc || slave > 0xffff)
+	rc = of_property_read_variable_u32_array(pdev->dev.of_node,
+						 "aspeed,lpc-io-reg",
+						 addrs, 1, 2);
+	if (rc < 0)
+		return rc;
+
+	if (WARN_ON(rc == 0))
+		return -EINVAL;
+
+	if (addrs[0] > 0xffff)
+		return -EINVAL;
+
+	if (rc == 2 && addrs[1] > 0xffff)
 		return -EINVAL;
 
-	return slave;
+	return rc;
 }
 
 static int aspeed_kcs_probe(struct platform_device *pdev)
@@ -527,9 +571,11 @@ static int aspeed_kcs_probe(struct platform_device *pdev)
 	struct kcs_bmc_device *kcs_bmc;
 	struct aspeed_kcs_bmc *priv;
 	struct device_node *np;
-	int rc, channel, addr;
 	bool have_upstream_irq;
 	u32 upstream_irq[2];
+	int rc, channel;
+	int nr_addrs;
+	u32 addrs[2];
 
 	np = pdev->dev.of_node->parent;
 	if (!of_device_is_compatible(np, "aspeed,ast2400-lpc-v2") &&
@@ -547,9 +593,9 @@ static int aspeed_kcs_probe(struct platform_device *pdev)
 	if (channel < 0)
 		return channel;
 
-	addr = ops->get_io_address(pdev);
-	if (addr < 0)
-		return addr;
+	nr_addrs = ops->get_io_address(pdev, addrs);
+	if (nr_addrs < 0)
+		return nr_addrs;
 
 	np = pdev->dev.of_node;
 	rc = of_property_read_u32_array(np, "aspeed,lpc-interrupts", upstream_irq, 2);
@@ -578,7 +624,9 @@ static int aspeed_kcs_probe(struct platform_device *pdev)
 	priv->obe.remove = false;
 	timer_setup(&priv->obe.timer, aspeed_kcs_check_obe, 0);
 
-	aspeed_kcs_set_address(kcs_bmc, addr);
+	rc = aspeed_kcs_set_address(kcs_bmc, addrs, nr_addrs);
+	if (rc)
+		return rc;
 
 	/* Host to BMC IRQ */
 	rc = aspeed_kcs_config_downstream_irq(kcs_bmc, pdev);
@@ -600,7 +648,8 @@ static int aspeed_kcs_probe(struct platform_device *pdev)
 	if (rc < 0)
 		return rc;
 
-	dev_info(&pdev->dev, "Initialised channel %d at 0x%x\n", kcs_bmc->channel, addr);
+	dev_info(&pdev->dev, "Initialised channel %d at 0x%x\n",
+			kcs_bmc->channel, addrs[0]);
 
 	return 0;
 }
-- 
2.27.0


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

* Re: [PATCH v2 17/21] dt-bindings: ipmi: Convert ASPEED KCS binding to schema
  2021-03-19  6:27 ` [PATCH v2 17/21] dt-bindings: ipmi: Convert ASPEED KCS binding to schema Andrew Jeffery
@ 2021-03-26  1:48   ` Rob Herring
  2021-04-09  5:15   ` Zev Weiss
  1 sibling, 0 replies; 67+ messages in thread
From: Rob Herring @ 2021-03-26  1:48 UTC (permalink / raw)
  To: Andrew Jeffery
  Cc: minyard, devicetree, linus.walleij, ryan_chen, tmaimon77,
	linux-aspeed, avifishman70, venture, chiawei_wang, linux-kernel,
	tali.perry1, linux-gpio, robh+dt, openipmi-developer, lee.jones,
	openbmc, linux-arm-kernel, benjaminfair

On Fri, 19 Mar 2021 16:57:48 +1030, Andrew Jeffery wrote:
> Given the deprecated binding, improve the ability to detect issues in
> the platform devicetrees. Further, a subsequent patch will introduce a
> new interrupts property for specifying SerIRQ behaviour, so convert
> before we do any further additions.
> 
> Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
> ---
>  .../bindings/ipmi/aspeed,ast2400-kcs-bmc.yaml | 92 +++++++++++++++++++
>  .../bindings/ipmi/aspeed-kcs-bmc.txt          | 33 -------
>  2 files changed, 92 insertions(+), 33 deletions(-)
>  create mode 100644 Documentation/devicetree/bindings/ipmi/aspeed,ast2400-kcs-bmc.yaml
>  delete mode 100644 Documentation/devicetree/bindings/ipmi/aspeed-kcs-bmc.txt
> 

Reviewed-by: Rob Herring <robh@kernel.org>

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

* Re: [PATCH v2 18/21] dt-bindings: ipmi: Add optional SerIRQ property to ASPEED KCS devices
  2021-03-19  6:27 ` [PATCH v2 18/21] dt-bindings: ipmi: Add optional SerIRQ property to ASPEED KCS devices Andrew Jeffery
@ 2021-03-26  1:49   ` Rob Herring
  0 siblings, 0 replies; 67+ messages in thread
From: Rob Herring @ 2021-03-26  1:49 UTC (permalink / raw)
  To: Andrew Jeffery
  Cc: minyard, devicetree, chiawei_wang, ryan_chen, benjaminfair,
	linux-aspeed, avifishman70, venture, openbmc, linux-kernel,
	tali.perry1, linux-gpio, robh+dt, openipmi-developer, lee.jones,
	linus.walleij, linux-arm-kernel, tmaimon77

On Fri, 19 Mar 2021 16:57:49 +1030, Andrew Jeffery wrote:
> Allocating IO and IRQ resources to LPC devices is in-theory an operation
> for the host, however ASPEED don't appear to expose this capability
> outside the BMC (e.g. SuperIO). Instead, we are left with BMC-internal
> registers for managing these resources, so introduce a devicetree
> property for KCS devices to describe SerIRQ properties.
> 
> Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
> ---
>  .../bindings/ipmi/aspeed,ast2400-kcs-bmc.yaml      | 14 ++++++++++++++
>  1 file changed, 14 insertions(+)
> 

Reviewed-by: Rob Herring <robh@kernel.org>

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

* Re: [EXTERNAL] [PATCH v2 19/21] ipmi: kcs_bmc_aspeed: Implement KCS SerIRQ configuration
  2021-03-19  6:27 ` [PATCH v2 19/21] ipmi: kcs_bmc_aspeed: Implement KCS SerIRQ configuration Andrew Jeffery
@ 2021-04-01  9:30   ` Zev Weiss
  0 siblings, 0 replies; 67+ messages in thread
From: Zev Weiss @ 2021-04-01  9:30 UTC (permalink / raw)
  To: Andrew Jeffery
  Cc: devicetree, chiawei_wang, ryan_chen, tmaimon77, minyard,
	avifishman70, venture, openbmc, linux-kernel, tali.perry1,
	linux-gpio, robh+dt, linux-arm-kernel, openipmi-developer,
	lee.jones, linus.walleij, linux-aspeed, benjaminfair

On Fri, Mar 19, 2021 at 01:27:50AM CDT, Andrew Jeffery wrote:
>Apply the SerIRQ ID and level/sense behaviours from the devicetree if
>provided.
>
>Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
>---
> drivers/char/ipmi/kcs_bmc_aspeed.c | 179 ++++++++++++++++++++++++++++-
> 1 file changed, 177 insertions(+), 2 deletions(-)
>
>diff --git a/drivers/char/ipmi/kcs_bmc_aspeed.c b/drivers/char/ipmi/kcs_bmc_aspeed.c
>index 271845eb2e26..3782aef4eb73 100644
>--- a/drivers/char/ipmi/kcs_bmc_aspeed.c
>+++ b/drivers/char/ipmi/kcs_bmc_aspeed.c
>@@ -9,6 +9,7 @@
> #include <linux/errno.h>
> #include <linux/interrupt.h>
> #include <linux/io.h>
>+#include <linux/irq.h>
> #include <linux/mfd/syscon.h>
> #include <linux/module.h>
> #include <linux/of.h>
>@@ -28,6 +29,22 @@
>
> #define KCS_CHANNEL_MAX     4
>
>+/*
>+ * Field class descriptions
>+ *
>+ * LPCyE	Enable LPC channel y
>+ * IBFIEy	Input Buffer Full IRQ Enable for LPC channel y
>+ * IRQxEy	Assert SerIRQ x for LPC channel y (Deprecated, use IDyIRQX, IRQXEy)
>+ * IDyIRQX	Use the specified 4-bit SerIRQ for LPC channel y
>+ * SELyIRQX	SerIRQ polarity for LPC channel y (low: 0, high: 1)
>+ * IRQXEy	Assert the SerIRQ specified in IDyIRQX for LPC channel y
>+ */
>+
>+#define LPC_TYIRQX_LOW       0b00
>+#define LPC_TYIRQX_HIGH      0b01
>+#define LPC_TYIRQX_RSVD      0b10
>+#define LPC_TYIRQX_RISING    0b11
>+
> #define LPC_HICR0            0x000
> #define     LPC_HICR0_LPC3E          BIT(7)
> #define     LPC_HICR0_LPC2E          BIT(6)
>@@ -39,6 +56,19 @@
> #define LPC_HICR4            0x010
> #define     LPC_HICR4_LADR12AS       BIT(7)
> #define     LPC_HICR4_KCSENBL        BIT(2)
>+#define LPC_SIRQCR0	     0x070
>+/* IRQ{12,1}E1 are deprecated as of AST2600 A3 but necessary for prior chips */
>+#define     LPC_SIRQCR0_IRQ12E1	     BIT(1)
>+#define     LPC_SIRQCR0_IRQ1E1	     BIT(0)
>+#define LPC_HICR5	     0x080
>+#define     LPC_HICR5_ID3IRQX_MASK   GENMASK(23, 20)
>+#define     LPC_HICR5_ID3IRQX_SHIFT  20
>+#define     LPC_HICR5_ID2IRQX_MASK   GENMASK(19, 16)
>+#define     LPC_HICR5_ID2IRQX_SHIFT  16
>+#define     LPC_HICR5_SEL3IRQX       BIT(15)
>+#define     LPC_HICR5_IRQXE3         BIT(14)
>+#define     LPC_HICR5_SEL2IRQX       BIT(13)
>+#define     LPC_HICR5_IRQXE2         BIT(12)
> #define LPC_LADR3H           0x014
> #define LPC_LADR3L           0x018
> #define LPC_LADR12H          0x01C
>@@ -55,6 +85,13 @@
> #define LPC_HICRB            0x100
> #define     LPC_HICRB_IBFIF4         BIT(1)
> #define     LPC_HICRB_LPC4E          BIT(0)
>+#define LPC_HICRC            0x104
>+#define     LPC_HICRC_ID4IRQX_MASK   GENMASK(7, 4)
>+#define     LPC_HICRC_ID4IRQX_SHIFT  4
>+#define     LPC_HICRC_TY4IRQX_MASK   GENMASK(3, 2)
>+#define     LPC_HICRC_TY4IRQX_SHIFT  2
>+#define     LPC_HICRC_OBF4_AUTO_CLR  BIT(1)
>+#define     LPC_HICRC_IRQXE4         BIT(0)
> #define LPC_LADR4            0x110
> #define LPC_IDR4             0x114
> #define LPC_ODR4             0x118
>@@ -62,11 +99,21 @@
>
> #define OBE_POLL_PERIOD	     (HZ / 2)
>
>+enum aspeed_kcs_irq_mode {
>+	aspeed_kcs_irq_none,
>+	aspeed_kcs_irq_serirq,
>+};
>+
> struct aspeed_kcs_bmc {
> 	struct kcs_bmc_device kcs_bmc;
>
> 	struct regmap *map;
>
>+	struct {
>+		enum aspeed_kcs_irq_mode mode;
>+		int id;
>+	} upstream_irq;
>+
> 	struct {
> 		spinlock_t lock;
> 		bool remove;
>@@ -103,6 +150,49 @@ static void aspeed_kcs_outb(struct kcs_bmc_device *kcs_bmc, u32 reg, u8 data)
>
> 	rc = regmap_write(priv->map, reg, data);
> 	WARN(rc != 0, "regmap_write() failed: %d\n", rc);
>+
>+	/* Trigger the upstream IRQ on ODR writes, if enabled */
>+
>+	switch (reg) {
>+	case LPC_ODR1:
>+	case LPC_ODR2:
>+	case LPC_ODR3:
>+	case LPC_ODR4:
>+		break;
>+	default:
>+		return;
>+	}
>+
>+	if (priv->upstream_irq.mode != aspeed_kcs_irq_serirq)
>+		return;
>+
>+	switch (kcs_bmc->channel) {
>+	case 1:
>+		switch (priv->upstream_irq.id) {
>+		case 12:
>+			regmap_update_bits(priv->map, LPC_SIRQCR0, LPC_SIRQCR0_IRQ12E1,
>+					   LPC_SIRQCR0_IRQ12E1);
>+			break;
>+		case 1:
>+			regmap_update_bits(priv->map, LPC_SIRQCR0, LPC_SIRQCR0_IRQ1E1,
>+					   LPC_SIRQCR0_IRQ1E1);
>+			break;
>+		default:
>+			break;
>+		}
>+		break;
>+	case 2:
>+		regmap_update_bits(priv->map, LPC_HICR5, LPC_HICR5_IRQXE2, LPC_HICR5_IRQXE2);
>+		break;
>+	case 3:
>+		regmap_update_bits(priv->map, LPC_HICR5, LPC_HICR5_IRQXE3, LPC_HICR5_IRQXE3);
>+		break;
>+	case 4:
>+		regmap_update_bits(priv->map, LPC_HICRC, LPC_HICRC_IRQXE4, LPC_HICRC_IRQXE4);
>+		break;
>+	default:
>+		break;
>+	}
> }
>
> static void aspeed_kcs_updateb(struct kcs_bmc_device *kcs_bmc, u32 reg, u8 mask, u8 val)
>@@ -161,6 +251,73 @@ static void aspeed_kcs_set_address(struct kcs_bmc_device *kcs_bmc, u16 addr)
> 	}
> }
>
>+static inline int aspeed_kcs_map_serirq_type(u32 dt_type)
>+{
>+	switch (dt_type) {
>+	case IRQ_TYPE_EDGE_RISING:
>+		return LPC_TYIRQX_RISING;
>+	case IRQ_TYPE_LEVEL_HIGH:
>+		return LPC_TYIRQX_HIGH;
>+	case IRQ_TYPE_LEVEL_LOW:
>+		return LPC_TYIRQX_LOW;
>+	default:
>+		return -EINVAL;
>+	}
>+}
>+
>+static int aspeed_kcs_config_upstream_irq(struct aspeed_kcs_bmc *priv, u32 id, u32 dt_type)
>+{
>+	unsigned int mask, val, hw_type;
>+
>+	if (id > 15)
>+		return -EINVAL;
>+
>+	hw_type = aspeed_kcs_map_serirq_type(dt_type);
>+	if (hw_type < 0)
>+		return hw_type;
>+
>+	priv->upstream_irq.mode = aspeed_kcs_irq_serirq;
>+	priv->upstream_irq.id = id;
>+
>+	switch (priv->kcs_bmc.channel) {
>+	case 1:
>+		/* Needs IRQxE1 rather than (ID1IRQX, SEL1IRQX, IRQXE1) before AST2600 A3 */
>+		break;
>+	case 2:
>+		if (!(hw_type == LPC_TYIRQX_LOW || hw_type == LPC_TYIRQX_HIGH))
>+			return -EINVAL;
>+
>+		mask = LPC_HICR5_SEL2IRQX | LPC_HICR5_ID2IRQX_MASK;
>+		val = (id << LPC_HICR5_ID2IRQX_SHIFT);
>+		val |= (hw_type == LPC_TYIRQX_HIGH) ? LPC_HICR5_SEL2IRQX : 0;
>+		regmap_update_bits(priv->map, LPC_HICR5, mask, val);
>+
>+		break;
>+	case 3:
>+		if (!(hw_type == LPC_TYIRQX_LOW || hw_type == LPC_TYIRQX_HIGH))
>+			return -EINVAL;
>+
>+		mask = LPC_HICR5_SEL3IRQX | LPC_HICR5_ID3IRQX_MASK;
>+		val = (id << LPC_HICR5_ID3IRQX_SHIFT);
>+		val |= (hw_type == LPC_TYIRQX_HIGH) ? LPC_HICR5_SEL3IRQX : 0;
>+		regmap_update_bits(priv->map, LPC_HICR5, mask, val);
>+
>+		break;
>+	case 4:
>+		mask = LPC_HICRC_ID4IRQX_MASK | LPC_HICRC_TY4IRQX_MASK | LPC_HICRC_OBF4_AUTO_CLR;
>+		val = (id << LPC_HICRC_ID4IRQX_SHIFT) | (hw_type << LPC_HICRC_TY4IRQX_SHIFT);
>+		regmap_update_bits(priv->map, LPC_HICRC, mask, val);
>+		break;
>+	default:
>+		dev_warn(priv->kcs_bmc.dev,
>+			 "SerIRQ configuration not supported on KCS channel %d\n",
>+			 priv->kcs_bmc.channel);
>+		return -EINVAL;
>+	}
>+
>+	return 0;
>+}
>+
> static void aspeed_kcs_enable_channel(struct kcs_bmc_device *kcs_bmc, bool enable)
> {
> 	struct aspeed_kcs_bmc *priv = to_aspeed_kcs_bmc(kcs_bmc);
>@@ -267,7 +424,7 @@ static irqreturn_t aspeed_kcs_irq(int irq, void *arg)
> 	return rc == KCS_BMC_EVENT_HANDLED ? IRQ_HANDLED : IRQ_NONE;
> }
>
>-static int aspeed_kcs_config_irq(struct kcs_bmc_device *kcs_bmc,
>+static int aspeed_kcs_config_downstream_irq(struct kcs_bmc_device *kcs_bmc,
> 			struct platform_device *pdev)
> {
> 	struct device *dev = &pdev->dev;
>@@ -371,6 +528,8 @@ static int aspeed_kcs_probe(struct platform_device *pdev)
> 	struct aspeed_kcs_bmc *priv;
> 	struct device_node *np;
> 	int rc, channel, addr;
>+	bool have_upstream_irq;
>+	u32 upstream_irq[2];
>
> 	np = pdev->dev.of_node->parent;
> 	if (!of_device_is_compatible(np, "aspeed,ast2400-lpc-v2") &&
>@@ -379,6 +538,7 @@ static int aspeed_kcs_probe(struct platform_device *pdev)
> 		dev_err(&pdev->dev, "unsupported LPC device binding\n");
> 		return -ENODEV;
> 	}
>+
> 	ops = of_device_get_match_data(&pdev->dev);
> 	if (!ops)
> 		return -EINVAL;
>@@ -391,6 +551,13 @@ static int aspeed_kcs_probe(struct platform_device *pdev)
> 	if (addr < 0)
> 		return addr;
>
>+	np = pdev->dev.of_node;
>+	rc = of_property_read_u32_array(np, "aspeed,lpc-interrupts", upstream_irq, 2);
>+	if ((rc && rc != -EINVAL))
>+		return -EINVAL;
>+
>+	have_upstream_irq = !rc;
>+
> 	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
> 	if (!priv)
> 		return -ENOMEM;
>@@ -413,10 +580,17 @@ static int aspeed_kcs_probe(struct platform_device *pdev)
>
> 	aspeed_kcs_set_address(kcs_bmc, addr);
>
>-	rc = aspeed_kcs_config_irq(kcs_bmc, pdev);
>+	/* Host to BMC IRQ */
>+	rc = aspeed_kcs_config_downstream_irq(kcs_bmc, pdev);
> 	if (rc)
> 		return rc;
>
>+	/* BMC to Host IRQ */
>+	if (have_upstream_irq)
>+		aspeed_kcs_config_upstream_irq(priv, upstream_irq[0], upstream_irq[1]);

This has a return value that could indicate an error if the DT
properties are misconfigured; do we want to check for that here?

>+	else
>+		priv->upstream_irq.mode = aspeed_kcs_irq_none;
>+
> 	platform_set_drvdata(pdev, priv);
>
> 	aspeed_kcs_irq_mask_update(kcs_bmc, (KCS_BMC_EVENT_TYPE_IBF | KCS_BMC_EVENT_TYPE_OBE), 0);
>@@ -481,4 +655,5 @@ module_platform_driver(ast_kcs_bmc_driver);
>
> MODULE_LICENSE("GPL v2");
> MODULE_AUTHOR("Haiyue Wang <haiyue.wang@linux.intel.com>");
>+MODULE_AUTHOR("Andrew Jeffery <andrew@aj.id.au>");
> MODULE_DESCRIPTION("Aspeed device interface to the KCS BMC device");
>-- 
>2.27.0
>

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

* Re [PATCH v2 21/21] ipmi: kcs_bmc_aspeed: Optionally apply status address
  2021-03-19  6:27 ` [PATCH v2 21/21] ipmi: kcs_bmc_aspeed: Optionally apply status address Andrew Jeffery
@ 2021-04-01 18:18   ` Zev Weiss
  2021-04-06  6:09   ` ChiaWei Wang
  1 sibling, 0 replies; 67+ messages in thread
From: Zev Weiss @ 2021-04-01 18:18 UTC (permalink / raw)
  To: Andrew Jeffery
  Cc: devicetree, chiawei_wang, ryan_chen, tmaimon77, minyard,
	avifishman70, venture, openbmc, linux-kernel, tali.perry1,
	linux-gpio, robh+dt, linux-arm-kernel, openipmi-developer,
	lee.jones, linus.walleij, linux-aspeed, benjaminfair

On Fri, Mar 19, 2021 at 01:27:52AM CDT, Andrew Jeffery wrote:
>Some Aspeed KCS devices can derive the status register address from the
>address of the data register. As such, the address of the status
>register can be implicit in the configuration if desired. On the other
>hand, sometimes address schemes might be requested that are incompatible
>with the default addressing scheme. Allow these requests where possible
>if the devicetree specifies the status register address.
>
>Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
>---
> drivers/char/ipmi/kcs_bmc_aspeed.c | 113 +++++++++++++++++++++--------
> 1 file changed, 81 insertions(+), 32 deletions(-)
>
>diff --git a/drivers/char/ipmi/kcs_bmc_aspeed.c b/drivers/char/ipmi/kcs_bmc_aspeed.c
>index 7334b1f51dcc..98789b837690 100644
>--- a/drivers/char/ipmi/kcs_bmc_aspeed.c
>+++ b/drivers/char/ipmi/kcs_bmc_aspeed.c
>@@ -83,6 +83,8 @@
> #define LPC_STR2             0x040
> #define LPC_STR3             0x044
> #define LPC_HICRB            0x100
>+#define     LPC_HICRB_EN16LADR2      BIT(5)
>+#define     LPC_HICRB_EN16LADR1      BIT(4)
> #define     LPC_HICRB_IBFIE4         BIT(1)
> #define     LPC_HICRB_LPC4E          BIT(0)
> #define LPC_HICRC            0x104
>@@ -96,6 +98,11 @@
> #define LPC_IDR4             0x114
> #define LPC_ODR4             0x118
> #define LPC_STR4             0x11C
>+#define LPC_LSADR12	     0x120
>+#define     LPC_LSADR12_LSADR2_MASK  GENMASK(31, 16)
>+#define     LPC_LSADR12_LSADR2_SHIFT 16
>+#define     LPC_LSADR12_LSADR1_MASK  GENMASK(15, 0)
>+#define     LPC_LSADR12_LSADR1_SHIFT 0
>
> #define OBE_POLL_PERIOD	     (HZ / 2)
>
>@@ -123,7 +130,7 @@ struct aspeed_kcs_bmc {
>
> struct aspeed_kcs_of_ops {
> 	int (*get_channel)(struct platform_device *pdev);
>-	int (*get_io_address)(struct platform_device *pdev);
>+	int (*get_io_address)(struct platform_device *pdev, u32 addrs[2]);
> };
>
> static inline struct aspeed_kcs_bmc *to_aspeed_kcs_bmc(struct kcs_bmc_device *kcs_bmc)
>@@ -217,38 +224,64 @@ static void aspeed_kcs_updateb(struct kcs_bmc_device *kcs_bmc, u32 reg, u8 mask,
>  *     C. KCS4
>  *        D / C : CA4h / CA5h
>  */
>-static void aspeed_kcs_set_address(struct kcs_bmc_device *kcs_bmc, u16 addr)
>+static int aspeed_kcs_set_address(struct kcs_bmc_device *kcs_bmc, u32 addrs[2], int nr_addrs)
> {
> 	struct aspeed_kcs_bmc *priv = to_aspeed_kcs_bmc(kcs_bmc);
>
>-	switch (kcs_bmc->channel) {
>+	if (WARN_ON(nr_addrs < 1 || nr_addrs > 2))
>+		return -EINVAL;
>+
>+	switch (priv->kcs_bmc.channel) {
> 	case 1:
>-		regmap_update_bits(priv->map, LPC_HICR4,
>-				LPC_HICR4_LADR12AS, 0);
>-		regmap_write(priv->map, LPC_LADR12H, addr >> 8);
>-		regmap_write(priv->map, LPC_LADR12L, addr & 0xFF);
>+		regmap_update_bits(priv->map, LPC_HICR4, LPC_HICR4_LADR12AS, 0);
>+		regmap_write(priv->map, LPC_LADR12H, addrs[0] >> 8);
>+		regmap_write(priv->map, LPC_LADR12L, addrs[0] & 0xFF);
>+		if (nr_addrs == 2) {
>+			regmap_update_bits(priv->map, LPC_LSADR12, LPC_LSADR12_LSADR1_MASK,
>+					   addrs[1] << LPC_LSADR12_LSADR1_SHIFT);
>+
>+			regmap_update_bits(priv->map, LPC_HICRB, LPC_HICRB_EN16LADR1,
>+					   LPC_HICRB_EN16LADR1);
>+		}
> 		break;
>
> 	case 2:
>-		regmap_update_bits(priv->map, LPC_HICR4,
>-				LPC_HICR4_LADR12AS, LPC_HICR4_LADR12AS);
>-		regmap_write(priv->map, LPC_LADR12H, addr >> 8);
>-		regmap_write(priv->map, LPC_LADR12L, addr & 0xFF);
>+		regmap_update_bits(priv->map, LPC_HICR4, LPC_HICR4_LADR12AS, LPC_HICR4_LADR12AS);
>+		regmap_write(priv->map, LPC_LADR12H, addrs[0] >> 8);
>+		regmap_write(priv->map, LPC_LADR12L, addrs[0] & 0xFF);
>+		if (nr_addrs == 2) {
>+			regmap_update_bits(priv->map, LPC_LSADR12, LPC_LSADR12_LSADR2_MASK,
>+					   addrs[1] << LPC_LSADR12_LSADR2_SHIFT);
>+
>+			regmap_update_bits(priv->map, LPC_HICRB, LPC_HICRB_EN16LADR2,
>+					   LPC_HICRB_EN16LADR2);
>+		}
> 		break;
>
> 	case 3:
>-		regmap_write(priv->map, LPC_LADR3H, addr >> 8);
>-		regmap_write(priv->map, LPC_LADR3L, addr & 0xFF);
>+		if (nr_addrs == 2) {
>+			dev_err(priv->kcs_bmc.dev,
>+				"Channel 3 only supports inferred status IO address\n");
>+			return -EINVAL;
>+		}
>+
>+		regmap_write(priv->map, LPC_LADR3H, addrs[0] >> 8);
>+		regmap_write(priv->map, LPC_LADR3L, addrs[0] & 0xFF);
> 		break;
>
> 	case 4:
>-		regmap_write(priv->map, LPC_LADR4, ((addr + 1) << 16) |
>-			addr);
>+		if (nr_addrs == 1)
>+			regmap_write(priv->map, LPC_LADR4, ((addrs[0] + 1) << 16) | addrs[0]);
>+		else
>+			regmap_write(priv->map, LPC_LADR4, (addrs[1] << 16) | addrs[0]);
>+
> 		break;
>
> 	default:
>-		break;
>+		return -EINVAL;
> 	}
>+
>+	return 0;
> }
>
> static inline int aspeed_kcs_map_serirq_type(u32 dt_type)
>@@ -462,18 +495,18 @@ static int aspeed_kcs_of_v1_get_channel(struct platform_device *pdev)
> 	return channel;
> }
>
>-static int aspeed_kcs_of_v1_get_io_address(struct platform_device *pdev)
>+static int
>+aspeed_kcs_of_v1_get_io_address(struct platform_device *pdev, u32 addrs[2])
> {
>-	u32 slave;
> 	int rc;
>
>-	rc = of_property_read_u32(pdev->dev.of_node, "kcs_addr", &slave);
>-	if (rc || slave > 0xffff) {
>+	rc = of_property_read_u32(pdev->dev.of_node, "kcs_addr", addrs);
>+	if (rc || addrs[0] > 0xffff) {
> 		dev_err(&pdev->dev, "no valid 'kcs_addr' configured\n");
> 		return -EINVAL;
> 	}
>
>-	return slave;
>+	return 1;
> }
>
> static int aspeed_kcs_of_v2_get_channel(struct platform_device *pdev)
>@@ -509,16 +542,27 @@ static int aspeed_kcs_of_v2_get_channel(struct platform_device *pdev)
> 	return -EINVAL;
> }
>
>-static int aspeed_kcs_of_v2_get_io_address(struct platform_device *pdev)
>+static int
>+aspeed_kcs_of_v2_get_io_address(struct platform_device *pdev, u32 addrs[2])
> {
>-	uint32_t slave;
> 	int rc;
>
>-	rc = of_property_read_u32(pdev->dev.of_node, "aspeed,lpc-io-reg", &slave);
>-	if (rc || slave > 0xffff)
>+	rc = of_property_read_variable_u32_array(pdev->dev.of_node,
>+						 "aspeed,lpc-io-reg",
>+						 addrs, 1, 2);
>+	if (rc < 0)
>+		return rc;
>+
>+	if (WARN_ON(rc == 0))
>+		return -EINVAL;

Is this check necessary?  It looks like
of_property_read_variable_u32_array() should fail in that case given
sz_min==1, so this seems like it should be impossible to trigger.

>+
>+	if (addrs[0] > 0xffff)
>+		return -EINVAL;
>+
>+	if (rc == 2 && addrs[1] > 0xffff)
> 		return -EINVAL;
>
>-	return slave;
>+	return rc;
> }
>
> static int aspeed_kcs_probe(struct platform_device *pdev)
>@@ -527,9 +571,11 @@ static int aspeed_kcs_probe(struct platform_device *pdev)
> 	struct kcs_bmc_device *kcs_bmc;
> 	struct aspeed_kcs_bmc *priv;
> 	struct device_node *np;
>-	int rc, channel, addr;
> 	bool have_upstream_irq;
> 	u32 upstream_irq[2];
>+	int rc, channel;
>+	int nr_addrs;
>+	u32 addrs[2];
>
> 	np = pdev->dev.of_node->parent;
> 	if (!of_device_is_compatible(np, "aspeed,ast2400-lpc-v2") &&
>@@ -547,9 +593,9 @@ static int aspeed_kcs_probe(struct platform_device *pdev)
> 	if (channel < 0)
> 		return channel;
>
>-	addr = ops->get_io_address(pdev);
>-	if (addr < 0)
>-		return addr;
>+	nr_addrs = ops->get_io_address(pdev, addrs);
>+	if (nr_addrs < 0)
>+		return nr_addrs;
>
> 	np = pdev->dev.of_node;
> 	rc = of_property_read_u32_array(np, "aspeed,lpc-interrupts", upstream_irq, 2);
>@@ -578,7 +624,9 @@ static int aspeed_kcs_probe(struct platform_device *pdev)
> 	priv->obe.remove = false;
> 	timer_setup(&priv->obe.timer, aspeed_kcs_check_obe, 0);
>
>-	aspeed_kcs_set_address(kcs_bmc, addr);
>+	rc = aspeed_kcs_set_address(kcs_bmc, addrs, nr_addrs);
>+	if (rc)
>+		return rc;
>
> 	/* Host to BMC IRQ */
> 	rc = aspeed_kcs_config_downstream_irq(kcs_bmc, pdev);
>@@ -600,7 +648,8 @@ static int aspeed_kcs_probe(struct platform_device *pdev)
> 	if (rc < 0)
> 		return rc;
>
>-	dev_info(&pdev->dev, "Initialised channel %d at 0x%x\n", kcs_bmc->channel, addr);
>+	dev_info(&pdev->dev, "Initialised channel %d at 0x%x\n",
>+			kcs_bmc->channel, addrs[0]);
>
> 	return 0;
> }
>-- 
>2.27.0
>

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

* RE: [PATCH v2 06/21] ipmi: kcs_bmc_aspeed: Use of match data to extract KCS properties
  2021-03-19  6:27 ` [PATCH v2 06/21] ipmi: kcs_bmc_aspeed: Use of match data to extract KCS properties Andrew Jeffery
@ 2021-04-06  6:07   ` ChiaWei Wang
  2021-04-09  3:24   ` Zev Weiss
  1 sibling, 0 replies; 67+ messages in thread
From: ChiaWei Wang @ 2021-04-06  6:07 UTC (permalink / raw)
  To: Andrew Jeffery, openipmi-developer, openbmc, minyard
  Cc: devicetree, Ryan Chen, tmaimon77, linux-aspeed, avifishman70,
	venture, linus.walleij, linux-kernel, tali.perry1, linux-gpio,
	robh+dt, lee.jones, linux-arm-kernel, benjaminfair

I have tried this patch on Intel EGS CRB with AST2600 A1 as the BMC.
Chiawei

Tested-by: Chia-Wei Wang <chiawei_wang@aspeedtech.com>

> -----Original Message-----
> From: Andrew Jeffery <andrew@aj.id.au>
> Sent: Friday, March 19, 2021 2:28 PM
> To: openipmi-developer@lists.sourceforge.net; openbmc@lists.ozlabs.org;
> minyard@acm.org
> 
> Unpack and remove the aspeed_kcs_probe_of_v[12]() functions to aid
> rearranging how the private device-driver memory is allocated.
> 
> Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
> ---
>  drivers/char/ipmi/kcs_bmc_aspeed.c | 146 ++++++++++++++---------------
>  1 file changed, 68 insertions(+), 78 deletions(-)
> 
> diff --git a/drivers/char/ipmi/kcs_bmc_aspeed.c
> b/drivers/char/ipmi/kcs_bmc_aspeed.c
> index eefe362f65f0..061f53676206 100644
> --- a/drivers/char/ipmi/kcs_bmc_aspeed.c
> +++ b/drivers/char/ipmi/kcs_bmc_aspeed.c
> @@ -13,6 +13,7 @@
>  #include <linux/module.h>
>  #include <linux/of.h>
>  #include <linux/of_address.h>
> +#include <linux/of_device.h>
>  #include <linux/platform_device.h>
>  #include <linux/poll.h>
>  #include <linux/regmap.h>
> @@ -63,6 +64,10 @@ struct aspeed_kcs_bmc {
>  	struct regmap *map;
>  };
> 
> +struct aspeed_kcs_of_ops {
> +	int (*get_channel)(struct platform_device *pdev);
> +	int (*get_io_address)(struct platform_device *pdev); };
> 
>  static u8 aspeed_kcs_inb(struct kcs_bmc *kcs_bmc, u32 reg)  { @@ -231,13
> +236,10 @@ static const struct kcs_ioreg
> ast_kcs_bmc_ioregs[KCS_CHANNEL_MAX] = {
>  	{ .idr = LPC_IDR4, .odr = LPC_ODR4, .str = LPC_STR4 },  };
> 
> -static struct kcs_bmc *aspeed_kcs_probe_of_v1(struct platform_device
> *pdev)
> +static int aspeed_kcs_of_v1_get_channel(struct platform_device *pdev)
>  {
> -	struct aspeed_kcs_bmc *priv;
>  	struct device_node *np;
> -	struct kcs_bmc *kcs;
>  	u32 channel;
> -	u32 slave;
>  	int rc;
> 
>  	np = pdev->dev.of_node;
> @@ -245,105 +247,78 @@ static struct kcs_bmc
> *aspeed_kcs_probe_of_v1(struct platform_device *pdev)
>  	rc = of_property_read_u32(np, "kcs_chan", &channel);
>  	if ((rc != 0) || (channel == 0 || channel > KCS_CHANNEL_MAX)) {
>  		dev_err(&pdev->dev, "no valid 'kcs_chan' configured\n");
> -		return ERR_PTR(-EINVAL);
> +		return -EINVAL;
>  	}
> 
> -	kcs = kcs_bmc_alloc(&pdev->dev, sizeof(struct aspeed_kcs_bmc),
> channel);
> -	if (!kcs)
> -		return ERR_PTR(-ENOMEM);
> +	return channel;
> +}
> 
> -	priv = kcs_bmc_priv(kcs);
> -	priv->map = syscon_node_to_regmap(pdev->dev.parent->of_node);
> -	if (IS_ERR(priv->map)) {
> -		dev_err(&pdev->dev, "Couldn't get regmap\n");
> -		return ERR_PTR(-ENODEV);
> -	}
> +static int aspeed_kcs_of_v1_get_io_address(struct platform_device
> +*pdev) {
> +	u32 slave;
> +	int rc;
> 
> -	rc = of_property_read_u32(np, "kcs_addr", &slave);
> -	if (rc) {
> +	rc = of_property_read_u32(pdev->dev.of_node, "kcs_addr", &slave);
> +	if (rc || slave > 0xffff) {
>  		dev_err(&pdev->dev, "no valid 'kcs_addr' configured\n");
> -		return ERR_PTR(-EINVAL);
> +		return -EINVAL;
>  	}
> 
> -	kcs->ioreg = ast_kcs_bmc_ioregs[channel - 1];
> -	aspeed_kcs_set_address(kcs, slave);
> -
> -	return kcs;
> -}
> -
> -static int aspeed_kcs_calculate_channel(const struct kcs_ioreg *regs) -{
> -	int i;
> -
> -	for (i = 0; i < ARRAY_SIZE(ast_kcs_bmc_ioregs); i++) {
> -		if (!memcmp(&ast_kcs_bmc_ioregs[i], regs, sizeof(*regs)))
> -			return i + 1;
> -	}
> -
> -	return -EINVAL;
> +	return slave;
>  }
> 
> -static struct kcs_bmc *aspeed_kcs_probe_of_v2(struct platform_device
> *pdev)
> +static int aspeed_kcs_of_v2_get_channel(struct platform_device *pdev)
>  {
> -	struct aspeed_kcs_bmc *priv;
>  	struct device_node *np;
>  	struct kcs_ioreg ioreg;
> -	struct kcs_bmc *kcs;
>  	const __be32 *reg;
> -	int channel;
> -	u32 slave;
> -	int rc;
> +	int i;
> 
>  	np = pdev->dev.of_node;
> 
>  	/* Don't translate addresses, we want offsets for the regmaps */
>  	reg = of_get_address(np, 0, NULL, NULL);
>  	if (!reg)
> -		return ERR_PTR(-EINVAL);
> +		return -EINVAL;
>  	ioreg.idr = be32_to_cpup(reg);
> 
>  	reg = of_get_address(np, 1, NULL, NULL);
>  	if (!reg)
> -		return ERR_PTR(-EINVAL);
> +		return -EINVAL;
>  	ioreg.odr = be32_to_cpup(reg);
> 
>  	reg = of_get_address(np, 2, NULL, NULL);
>  	if (!reg)
> -		return ERR_PTR(-EINVAL);
> +		return -EINVAL;
>  	ioreg.str = be32_to_cpup(reg);
> 
> -	channel = aspeed_kcs_calculate_channel(&ioreg);
> -	if (channel < 0)
> -		return ERR_PTR(channel);
> -
> -	kcs = kcs_bmc_alloc(&pdev->dev, sizeof(struct aspeed_kcs_bmc),
> channel);
> -	if (!kcs)
> -		return ERR_PTR(-ENOMEM);
> -
> -	kcs->ioreg = ioreg;
> -
> -	priv = kcs_bmc_priv(kcs);
> -	priv->map = syscon_node_to_regmap(pdev->dev.parent->of_node);
> -	if (IS_ERR(priv->map)) {
> -		dev_err(&pdev->dev, "Couldn't get regmap\n");
> -		return ERR_PTR(-ENODEV);
> +	for (i = 0; i < ARRAY_SIZE(ast_kcs_bmc_ioregs); i++) {
> +		if (!memcmp(&ast_kcs_bmc_ioregs[i], &ioreg, sizeof(ioreg)))
> +			return i + 1;
>  	}
> 
> -	rc = of_property_read_u32(np, "aspeed,lpc-io-reg", &slave);
> -	if (rc)
> -		return ERR_PTR(rc);
> +	return -EINVAL;
> +}
> 
> -	aspeed_kcs_set_address(kcs, slave);
> +static int aspeed_kcs_of_v2_get_io_address(struct platform_device
> +*pdev) {
> +	uint32_t slave;
> +	int rc;
> 
> -	return kcs;
> +	rc = of_property_read_u32(pdev->dev.of_node, "aspeed,lpc-io-reg",
> &slave);
> +	if (rc || slave > 0xffff)
> +		return -EINVAL;
> +
> +	return slave;
>  }
> 
>  static int aspeed_kcs_probe(struct platform_device *pdev)  {
> +	const struct aspeed_kcs_of_ops *ops;
>  	struct device *dev = &pdev->dev;
>  	struct kcs_bmc *kcs_bmc;
>  	struct device_node *np;
> -	int rc;
> +	int rc, channel, addr;
> 
>  	np = dev->of_node->parent;
>  	if (!of_device_is_compatible(np, "aspeed,ast2400-lpc-v2") && @@
> -352,23 +327,28 @@ static int aspeed_kcs_probe(struct platform_device
> *pdev)
>  		dev_err(dev, "unsupported LPC device binding\n");
>  		return -ENODEV;
>  	}
> -
> -	np = dev->of_node;
> -	if (of_device_is_compatible(np, "aspeed,ast2400-kcs-bmc") ||
> -	    of_device_is_compatible(np, "aspeed,ast2500-kcs-bmc"))
> -		kcs_bmc = aspeed_kcs_probe_of_v1(pdev);
> -	else if (of_device_is_compatible(np, "aspeed,ast2400-kcs-bmc-v2") ||
> -		 of_device_is_compatible(np, "aspeed,ast2500-kcs-bmc-v2"))
> -		kcs_bmc = aspeed_kcs_probe_of_v2(pdev);
> -	else
> +	ops = of_device_get_match_data(&pdev->dev);
> +	if (!ops)
>  		return -EINVAL;
> 
> -	if (IS_ERR(kcs_bmc))
> -		return PTR_ERR(kcs_bmc);
> +	channel = ops->get_channel(pdev);
> +	if (channel < 0)
> +		return channel;
> 
> +	kcs_bmc = kcs_bmc_alloc(&pdev->dev, sizeof(struct aspeed_kcs_bmc),
> channel);
> +	if (!kcs_bmc)
> +		return -ENOMEM;
> +
> +	kcs_bmc->ioreg = ast_kcs_bmc_ioregs[channel - 1];
>  	kcs_bmc->io_inputb = aspeed_kcs_inb;
>  	kcs_bmc->io_outputb = aspeed_kcs_outb;
> 
> +	addr = ops->get_io_address(pdev);
> +	if (addr < 0)
> +		return addr;
> +
> +	aspeed_kcs_set_address(kcs_bmc, addr);
> +
>  	rc = aspeed_kcs_config_irq(kcs_bmc, pdev);
>  	if (rc)
>  		return rc;
> @@ -400,11 +380,21 @@ static int aspeed_kcs_remove(struct
> platform_device *pdev)
>  	return 0;
>  }
> 
> +static const struct aspeed_kcs_of_ops of_v1_ops = {
> +	.get_channel = aspeed_kcs_of_v1_get_channel,
> +	.get_io_address = aspeed_kcs_of_v1_get_io_address, };
> +
> +static const struct aspeed_kcs_of_ops of_v2_ops = {
> +	.get_channel = aspeed_kcs_of_v2_get_channel,
> +	.get_io_address = aspeed_kcs_of_v2_get_io_address, };
> +
>  static const struct of_device_id ast_kcs_bmc_match[] = {
> -	{ .compatible = "aspeed,ast2400-kcs-bmc" },
> -	{ .compatible = "aspeed,ast2500-kcs-bmc" },
> -	{ .compatible = "aspeed,ast2400-kcs-bmc-v2" },
> -	{ .compatible = "aspeed,ast2500-kcs-bmc-v2" },
> +	{ .compatible = "aspeed,ast2400-kcs-bmc", .data = &of_v1_ops },
> +	{ .compatible = "aspeed,ast2500-kcs-bmc", .data = &of_v1_ops },
> +	{ .compatible = "aspeed,ast2400-kcs-bmc-v2", .data = &of_v2_ops },
> +	{ .compatible = "aspeed,ast2500-kcs-bmc-v2", .data = &of_v2_ops },
>  	{ }
>  };
>  MODULE_DEVICE_TABLE(of, ast_kcs_bmc_match);
> --
> 2.27.0


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

* RE: [PATCH v2 13/21] ipmi: kcs_bmc: Decouple the IPMI chardev from the core
  2021-03-19  6:27 ` [PATCH v2 13/21] ipmi: kcs_bmc: Decouple the IPMI chardev from the core Andrew Jeffery
@ 2021-04-06  6:07   ` ChiaWei Wang
  2021-04-09  4:35   ` Zev Weiss
  1 sibling, 0 replies; 67+ messages in thread
From: ChiaWei Wang @ 2021-04-06  6:07 UTC (permalink / raw)
  To: Andrew Jeffery, openipmi-developer, openbmc, minyard
  Cc: devicetree, Ryan Chen, tmaimon77, linux-aspeed, avifishman70,
	venture, linus.walleij, linux-kernel, tali.perry1, linux-gpio,
	robh+dt, lee.jones, linux-arm-kernel, benjaminfair

I have tried this patch on Intel EGS CRB with AST2600 A1 as the BMC.
Chiawei

Tested-by: Chia-Wei Wang <chiawei_wang@aspeedtech.com>

> -----Original Message-----
> From: Andrew Jeffery <andrew@aj.id.au>
> Sent: Friday, March 19, 2021 2:28 PM
> To: openipmi-developer@lists.sourceforge.net; openbmc@lists.ozlabs.org;
> minyard@acm.org
> Subject: [PATCH v2 13/21] ipmi: kcs_bmc: Decouple the IPMI chardev from the
> core
> 
> Now that we have untangled the data-structures, split the userspace interface
> out into its own module. Userspace interfaces and drivers are registered to the
> KCS BMC core to support arbitrary binding of either.
> 
> Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
> ---
>  drivers/char/ipmi/Kconfig             | 13 +++++
>  drivers/char/ipmi/Makefile            |  3 +-
>  drivers/char/ipmi/kcs_bmc.c           | 78
> ++++++++++++++++++++++++++-
>  drivers/char/ipmi/kcs_bmc.h           |  4 --
>  drivers/char/ipmi/kcs_bmc_cdev_ipmi.c | 33 +++++++++---
>  drivers/char/ipmi/kcs_bmc_client.h    | 14 +++++
>  6 files changed, 132 insertions(+), 13 deletions(-)
> 
> diff --git a/drivers/char/ipmi/Kconfig b/drivers/char/ipmi/Kconfig index
> 07847d9a459a..bc5f81899b62 100644
> --- a/drivers/char/ipmi/Kconfig
> +++ b/drivers/char/ipmi/Kconfig
> @@ -124,6 +124,19 @@ config NPCM7XX_KCS_IPMI_BMC
>  	  This support is also available as a module.  If so, the module
>  	  will be called kcs_bmc_npcm7xx.
> 
> +config IPMI_KCS_BMC_CDEV_IPMI
> +	depends on IPMI_KCS_BMC
> +	tristate "IPMI character device interface for BMC KCS devices"
> +	help
> +	  Provides a BMC-side character device implementing IPMI
> +	  semantics for KCS IPMI devices.
> +
> +	  Say YES if you wish to expose KCS devices on the BMC for IPMI
> +	  purposes.
> +
> +	  This support is also available as a module. The module will be
> +	  called kcs_bmc_cdev_ipmi.
> +
>  config ASPEED_BT_IPMI_BMC
>  	depends on ARCH_ASPEED || COMPILE_TEST
>  	depends on REGMAP && REGMAP_MMIO && MFD_SYSCON diff --git
> a/drivers/char/ipmi/Makefile b/drivers/char/ipmi/Makefile index
> a302bc865370..fcfa676afddb 100644
> --- a/drivers/char/ipmi/Makefile
> +++ b/drivers/char/ipmi/Makefile
> @@ -22,7 +22,8 @@ obj-$(CONFIG_IPMI_SSIF) += ipmi_ssif.o
>  obj-$(CONFIG_IPMI_POWERNV) += ipmi_powernv.o
>  obj-$(CONFIG_IPMI_WATCHDOG) += ipmi_watchdog.o
>  obj-$(CONFIG_IPMI_POWEROFF) += ipmi_poweroff.o
> -obj-$(CONFIG_IPMI_KCS_BMC) += kcs_bmc.o kcs_bmc_cdev_ipmi.o
> +obj-$(CONFIG_IPMI_KCS_BMC) += kcs_bmc.o
> +obj-$(CONFIG_IPMI_KCS_BMC_CDEV_IPMI) += kcs_bmc_cdev_ipmi.o
>  obj-$(CONFIG_ASPEED_BT_IPMI_BMC) += bt-bmc.o
>  obj-$(CONFIG_ASPEED_KCS_IPMI_BMC) += kcs_bmc_aspeed.o
>  obj-$(CONFIG_NPCM7XX_KCS_IPMI_BMC) += kcs_bmc_npcm7xx.o diff --git
> a/drivers/char/ipmi/kcs_bmc.c b/drivers/char/ipmi/kcs_bmc.c index
> 266ebec71d6f..694db6ee2a92 100644
> --- a/drivers/char/ipmi/kcs_bmc.c
> +++ b/drivers/char/ipmi/kcs_bmc.c
> @@ -5,7 +5,9 @@
>   */
> 
>  #include <linux/device.h>
> +#include <linux/list.h>
>  #include <linux/module.h>
> +#include <linux/mutex.h>
> 
>  #include "kcs_bmc.h"
> 
> @@ -13,6 +15,11 @@
>  #include "kcs_bmc_device.h"
>  #include "kcs_bmc_client.h"
> 
> +/* Record probed devices and cdevs */
> +static DEFINE_MUTEX(kcs_bmc_lock);
> +static LIST_HEAD(kcs_bmc_devices);
> +static LIST_HEAD(kcs_bmc_cdevs);
> +
>  /* Consumer data access */
> 
>  u8 kcs_bmc_read_data(struct kcs_bmc_device *kcs_bmc) @@ -100,16
> +107,83 @@ EXPORT_SYMBOL(kcs_bmc_disable_device);
> 
>  int kcs_bmc_add_device(struct kcs_bmc_device *kcs_bmc)  {
> -	return kcs_bmc_ipmi_attach_cdev(kcs_bmc);
> +	struct kcs_bmc_cdev *cdev;
> +	int rc;
> +
> +	spin_lock_init(&kcs_bmc->lock);
> +	kcs_bmc->client = NULL;
> +
> +	mutex_lock(&kcs_bmc_lock);
> +	list_add(&kcs_bmc->entry, &kcs_bmc_devices);
> +	list_for_each_entry(cdev, &kcs_bmc_cdevs, entry) {
> +		rc = cdev->ops->add_device(kcs_bmc);
> +		if (rc)
> +			dev_err(kcs_bmc->dev, "Failed to add chardev for KCS channel
> %d: %d",
> +				kcs_bmc->channel, rc);
> +	}
> +	mutex_unlock(&kcs_bmc_lock);
> +
> +	return 0;
>  }
>  EXPORT_SYMBOL(kcs_bmc_add_device);
> 
>  int kcs_bmc_remove_device(struct kcs_bmc_device *kcs_bmc)  {
> -	return kcs_bmc_ipmi_detach_cdev(kcs_bmc);
> +	struct kcs_bmc_cdev *cdev;
> +	int rc;
> +
> +	mutex_lock(&kcs_bmc_lock);
> +	list_del(&kcs_bmc->entry);
> +	list_for_each_entry(cdev, &kcs_bmc_cdevs, entry) {
> +		rc = cdev->ops->remove_device(kcs_bmc);
> +		if (rc)
> +			dev_err(kcs_bmc->dev, "Failed to remove chardev for KCS
> channel %d: %d",
> +				kcs_bmc->channel, rc);
> +	}
> +	mutex_unlock(&kcs_bmc_lock);
> +
> +	return 0;
>  }
>  EXPORT_SYMBOL(kcs_bmc_remove_device);
> 
> +int kcs_bmc_register_cdev(struct kcs_bmc_cdev *cdev) {
> +	struct kcs_bmc_device *kcs_bmc;
> +	int rc;
> +
> +	mutex_lock(&kcs_bmc_lock);
> +	list_add(&cdev->entry, &kcs_bmc_cdevs);
> +	list_for_each_entry(kcs_bmc, &kcs_bmc_devices, entry) {
> +		rc = cdev->ops->add_device(kcs_bmc);
> +		if (rc)
> +			dev_err(kcs_bmc->dev, "Failed to add chardev for KCS channel
> %d: %d",
> +				kcs_bmc->channel, rc);
> +	}
> +	mutex_unlock(&kcs_bmc_lock);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL(kcs_bmc_register_cdev);
> +
> +int kcs_bmc_unregister_cdev(struct kcs_bmc_cdev *cdev) {
> +	struct kcs_bmc_device *kcs_bmc;
> +	int rc;
> +
> +	mutex_lock(&kcs_bmc_lock);
> +	list_del(&cdev->entry);
> +	list_for_each_entry(kcs_bmc, &kcs_bmc_devices, entry) {
> +		rc = cdev->ops->remove_device(kcs_bmc);
> +		if (rc)
> +			dev_err(kcs_bmc->dev, "Failed to add chardev for KCS channel
> %d: %d",
> +				kcs_bmc->channel, rc);
> +	}
> +	mutex_unlock(&kcs_bmc_lock);
> +
> +	return rc;
> +}
> +EXPORT_SYMBOL(kcs_bmc_unregister_cdev);
> +
>  MODULE_LICENSE("GPL v2");
>  MODULE_AUTHOR("Haiyue Wang <haiyue.wang@linux.intel.com>");
> MODULE_AUTHOR("Andrew Jeffery <andrew@aj.id.au>"); diff --git
> a/drivers/char/ipmi/kcs_bmc.h b/drivers/char/ipmi/kcs_bmc.h index
> 3f266740c759..5deb9a0b8e60 100644
> --- a/drivers/char/ipmi/kcs_bmc.h
> +++ b/drivers/char/ipmi/kcs_bmc.h
> @@ -42,8 +42,4 @@ struct kcs_bmc_device {
>  	spinlock_t lock;
>  	struct kcs_bmc_client *client;
>  };
> -
> -/* Temporary exports while refactoring */ -int
> kcs_bmc_ipmi_attach_cdev(struct kcs_bmc_device *kcs_bmc); -int
> kcs_bmc_ipmi_detach_cdev(struct kcs_bmc_device *kcs_bmc);  #endif /*
> __KCS_BMC_H__ */ diff --git a/drivers/char/ipmi/kcs_bmc_cdev_ipmi.c
> b/drivers/char/ipmi/kcs_bmc_cdev_ipmi.c
> index 58c42e76483d..df83d67851ac 100644
> --- a/drivers/char/ipmi/kcs_bmc_cdev_ipmi.c
> +++ b/drivers/char/ipmi/kcs_bmc_cdev_ipmi.c
> @@ -469,8 +469,7 @@ static const struct file_operations kcs_bmc_ipmi_fops
> = {  static DEFINE_SPINLOCK(kcs_bmc_ipmi_instances_lock);
>  static LIST_HEAD(kcs_bmc_ipmi_instances);
> 
> -int kcs_bmc_ipmi_attach_cdev(struct kcs_bmc_device *kcs_bmc); -int
> kcs_bmc_ipmi_attach_cdev(struct kcs_bmc_device *kcs_bmc)
> +static int kcs_bmc_ipmi_attach_cdev(struct kcs_bmc_device *kcs_bmc)
>  {
>  	struct kcs_bmc_ipmi *priv;
>  	int rc;
> @@ -512,10 +511,8 @@ int kcs_bmc_ipmi_attach_cdev(struct
> kcs_bmc_device *kcs_bmc)
> 
>  	return 0;
>  }
> -EXPORT_SYMBOL(kcs_bmc_ipmi_attach_cdev);
> 
> -int kcs_bmc_ipmi_detach_cdev(struct kcs_bmc_device *kcs_bmc); -int
> kcs_bmc_ipmi_detach_cdev(struct kcs_bmc_device *kcs_bmc)
> +static int kcs_bmc_ipmi_detach_cdev(struct kcs_bmc_device *kcs_bmc)
>  {
>  	struct kcs_bmc_ipmi *priv = NULL, *pos;
> 
> @@ -541,7 +538,31 @@ int kcs_bmc_ipmi_detach_cdev(struct
> kcs_bmc_device *kcs_bmc)
> 
>  	return 0;
>  }
> -EXPORT_SYMBOL(kcs_bmc_ipmi_detach_cdev);
> +
> +static const struct kcs_bmc_cdev_ops kcs_bmc_ipmi_cdev_ops = {
> +	.add_device = kcs_bmc_ipmi_attach_cdev,
> +	.remove_device = kcs_bmc_ipmi_detach_cdev, };
> +
> +static struct kcs_bmc_cdev kcs_bmc_ipmi_cdev = {
> +	.ops = &kcs_bmc_ipmi_cdev_ops,
> +};
> +
> +static int kcs_bmc_ipmi_init(void)
> +{
> +	return kcs_bmc_register_cdev(&kcs_bmc_ipmi_cdev);
> +}
> +module_init(kcs_bmc_ipmi_init);
> +
> +static void kcs_bmc_ipmi_exit(void)
> +{
> +	int rc;
> +
> +	rc = kcs_bmc_unregister_cdev(&kcs_bmc_ipmi_cdev);
> +	if (rc)
> +		pr_warn("Failed to remove KCS BMC client: %d", rc); }
> +module_exit(kcs_bmc_ipmi_exit);
> 
>  MODULE_LICENSE("GPL v2");
>  MODULE_AUTHOR("Haiyue Wang <haiyue.wang@linux.intel.com>"); diff
> --git a/drivers/char/ipmi/kcs_bmc_client.h
> b/drivers/char/ipmi/kcs_bmc_client.h
> index 2dd710f4b4aa..d0a7404ff584 100644
> --- a/drivers/char/ipmi/kcs_bmc_client.h
> +++ b/drivers/char/ipmi/kcs_bmc_client.h
> @@ -10,6 +10,17 @@
> 
>  #include "kcs_bmc.h"
> 
> +struct kcs_bmc_cdev_ops {
> +	int (*add_device)(struct kcs_bmc_device *kcs_bmc);
> +	int (*remove_device)(struct kcs_bmc_device *kcs_bmc); };
> +
> +struct kcs_bmc_cdev {
> +	struct list_head entry;
> +
> +	const struct kcs_bmc_cdev_ops *ops;
> +};
> +
>  struct kcs_bmc_client_ops {
>  	int (*event)(struct kcs_bmc_client *client);  }; @@ -20,6 +31,9 @@
> struct kcs_bmc_client {
>  	struct kcs_bmc_device *dev;
>  };
> 
> +int kcs_bmc_register_cdev(struct kcs_bmc_cdev *cdev); int
> +kcs_bmc_unregister_cdev(struct kcs_bmc_cdev *cdev);
> +
>  int kcs_bmc_enable_device(struct kcs_bmc_device *kcs_bmc, struct
> kcs_bmc_client *client);  void kcs_bmc_disable_device(struct
> kcs_bmc_device *kcs_bmc, struct kcs_bmc_client *client);
> 
> --
> 2.27.0


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

* RE: [PATCH v2 21/21] ipmi: kcs_bmc_aspeed: Optionally apply status address
  2021-03-19  6:27 ` [PATCH v2 21/21] ipmi: kcs_bmc_aspeed: Optionally apply status address Andrew Jeffery
  2021-04-01 18:18   ` Re " Zev Weiss
@ 2021-04-06  6:09   ` ChiaWei Wang
  1 sibling, 0 replies; 67+ messages in thread
From: ChiaWei Wang @ 2021-04-06  6:09 UTC (permalink / raw)
  To: Andrew Jeffery, openipmi-developer, openbmc, minyard
  Cc: devicetree, Ryan Chen, tmaimon77, linux-aspeed, avifishman70,
	venture, linus.walleij, linux-kernel, tali.perry1, linux-gpio,
	robh+dt, lee.jones, linux-arm-kernel, benjaminfair

Reviewed-by: Chia-Wei Wang <chiawei_wang@aspeedtech.com>

> -----Original Message-----
> From: Andrew Jeffery <andrew@aj.id.au>
> Sent: Friday, March 19, 2021 2:28 PM
> To: openipmi-developer@lists.sourceforge.net; openbmc@lists.ozlabs.org;
> minyard@acm.org
> Subject: [PATCH v2 21/21] ipmi: kcs_bmc_aspeed: Optionally apply status
> address
> 
> Some Aspeed KCS devices can derive the status register address from the
> address of the data register. As such, the address of the status register can be
> implicit in the configuration if desired. On the other hand, sometimes address
> schemes might be requested that are incompatible with the default addressing
> scheme. Allow these requests where possible if the devicetree specifies the
> status register address.
> 
> Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
> ---
>  drivers/char/ipmi/kcs_bmc_aspeed.c | 113 +++++++++++++++++++++--------
>  1 file changed, 81 insertions(+), 32 deletions(-)
> 
> diff --git a/drivers/char/ipmi/kcs_bmc_aspeed.c
> b/drivers/char/ipmi/kcs_bmc_aspeed.c
> index 7334b1f51dcc..98789b837690 100644
> --- a/drivers/char/ipmi/kcs_bmc_aspeed.c
> +++ b/drivers/char/ipmi/kcs_bmc_aspeed.c
> @@ -83,6 +83,8 @@
>  #define LPC_STR2             0x040
>  #define LPC_STR3             0x044
>  #define LPC_HICRB            0x100
> +#define     LPC_HICRB_EN16LADR2      BIT(5)
> +#define     LPC_HICRB_EN16LADR1      BIT(4)
>  #define     LPC_HICRB_IBFIE4         BIT(1)
>  #define     LPC_HICRB_LPC4E          BIT(0)
>  #define LPC_HICRC            0x104
> @@ -96,6 +98,11 @@
>  #define LPC_IDR4             0x114
>  #define LPC_ODR4             0x118
>  #define LPC_STR4             0x11C
> +#define LPC_LSADR12	     0x120
> +#define     LPC_LSADR12_LSADR2_MASK  GENMASK(31, 16)
> +#define     LPC_LSADR12_LSADR2_SHIFT 16
> +#define     LPC_LSADR12_LSADR1_MASK  GENMASK(15, 0)
> +#define     LPC_LSADR12_LSADR1_SHIFT 0
> 
>  #define OBE_POLL_PERIOD	     (HZ / 2)
> 
> @@ -123,7 +130,7 @@ struct aspeed_kcs_bmc {
> 
>  struct aspeed_kcs_of_ops {
>  	int (*get_channel)(struct platform_device *pdev);
> -	int (*get_io_address)(struct platform_device *pdev);
> +	int (*get_io_address)(struct platform_device *pdev, u32 addrs[2]);
>  };
> 
>  static inline struct aspeed_kcs_bmc *to_aspeed_kcs_bmc(struct
> kcs_bmc_device *kcs_bmc) @@ -217,38 +224,64 @@ static void
> aspeed_kcs_updateb(struct kcs_bmc_device *kcs_bmc, u32 reg, u8 mask,
>   *     C. KCS4
>   *        D / C : CA4h / CA5h
>   */
> -static void aspeed_kcs_set_address(struct kcs_bmc_device *kcs_bmc, u16
> addr)
> +static int aspeed_kcs_set_address(struct kcs_bmc_device *kcs_bmc, u32
> +addrs[2], int nr_addrs)
>  {
>  	struct aspeed_kcs_bmc *priv = to_aspeed_kcs_bmc(kcs_bmc);
> 
> -	switch (kcs_bmc->channel) {
> +	if (WARN_ON(nr_addrs < 1 || nr_addrs > 2))
> +		return -EINVAL;
> +
> +	switch (priv->kcs_bmc.channel) {
>  	case 1:
> -		regmap_update_bits(priv->map, LPC_HICR4,
> -				LPC_HICR4_LADR12AS, 0);
> -		regmap_write(priv->map, LPC_LADR12H, addr >> 8);
> -		regmap_write(priv->map, LPC_LADR12L, addr & 0xFF);
> +		regmap_update_bits(priv->map, LPC_HICR4, LPC_HICR4_LADR12AS,
> 0);
> +		regmap_write(priv->map, LPC_LADR12H, addrs[0] >> 8);
> +		regmap_write(priv->map, LPC_LADR12L, addrs[0] & 0xFF);
> +		if (nr_addrs == 2) {
> +			regmap_update_bits(priv->map, LPC_LSADR12,
> LPC_LSADR12_LSADR1_MASK,
> +					   addrs[1] << LPC_LSADR12_LSADR1_SHIFT);
> +
> +			regmap_update_bits(priv->map, LPC_HICRB,
> LPC_HICRB_EN16LADR1,
> +					   LPC_HICRB_EN16LADR1);
> +		}
>  		break;
> 
>  	case 2:
> -		regmap_update_bits(priv->map, LPC_HICR4,
> -				LPC_HICR4_LADR12AS, LPC_HICR4_LADR12AS);
> -		regmap_write(priv->map, LPC_LADR12H, addr >> 8);
> -		regmap_write(priv->map, LPC_LADR12L, addr & 0xFF);
> +		regmap_update_bits(priv->map, LPC_HICR4, LPC_HICR4_LADR12AS,
> LPC_HICR4_LADR12AS);
> +		regmap_write(priv->map, LPC_LADR12H, addrs[0] >> 8);
> +		regmap_write(priv->map, LPC_LADR12L, addrs[0] & 0xFF);
> +		if (nr_addrs == 2) {
> +			regmap_update_bits(priv->map, LPC_LSADR12,
> LPC_LSADR12_LSADR2_MASK,
> +					   addrs[1] << LPC_LSADR12_LSADR2_SHIFT);
> +
> +			regmap_update_bits(priv->map, LPC_HICRB,
> LPC_HICRB_EN16LADR2,
> +					   LPC_HICRB_EN16LADR2);
> +		}
>  		break;
> 
>  	case 3:
> -		regmap_write(priv->map, LPC_LADR3H, addr >> 8);
> -		regmap_write(priv->map, LPC_LADR3L, addr & 0xFF);
> +		if (nr_addrs == 2) {
> +			dev_err(priv->kcs_bmc.dev,
> +				"Channel 3 only supports inferred status IO address\n");
> +			return -EINVAL;
> +		}
> +
> +		regmap_write(priv->map, LPC_LADR3H, addrs[0] >> 8);
> +		regmap_write(priv->map, LPC_LADR3L, addrs[0] & 0xFF);
>  		break;
> 
>  	case 4:
> -		regmap_write(priv->map, LPC_LADR4, ((addr + 1) << 16) |
> -			addr);
> +		if (nr_addrs == 1)
> +			regmap_write(priv->map, LPC_LADR4, ((addrs[0] + 1) << 16) |
> addrs[0]);
> +		else
> +			regmap_write(priv->map, LPC_LADR4, (addrs[1] << 16) |
> addrs[0]);
> +
>  		break;
> 
>  	default:
> -		break;
> +		return -EINVAL;
>  	}
> +
> +	return 0;
>  }
> 
>  static inline int aspeed_kcs_map_serirq_type(u32 dt_type) @@ -462,18
> +495,18 @@ static int aspeed_kcs_of_v1_get_channel(struct platform_device
> *pdev)
>  	return channel;
>  }
> 
> -static int aspeed_kcs_of_v1_get_io_address(struct platform_device *pdev)
> +static int
> +aspeed_kcs_of_v1_get_io_address(struct platform_device *pdev, u32
> +addrs[2])
>  {
> -	u32 slave;
>  	int rc;
> 
> -	rc = of_property_read_u32(pdev->dev.of_node, "kcs_addr", &slave);
> -	if (rc || slave > 0xffff) {
> +	rc = of_property_read_u32(pdev->dev.of_node, "kcs_addr", addrs);
> +	if (rc || addrs[0] > 0xffff) {
>  		dev_err(&pdev->dev, "no valid 'kcs_addr' configured\n");
>  		return -EINVAL;
>  	}
> 
> -	return slave;
> +	return 1;
>  }
> 
>  static int aspeed_kcs_of_v2_get_channel(struct platform_device *pdev) @@
> -509,16 +542,27 @@ static int aspeed_kcs_of_v2_get_channel(struct
> platform_device *pdev)
>  	return -EINVAL;
>  }
> 
> -static int aspeed_kcs_of_v2_get_io_address(struct platform_device *pdev)
> +static int
> +aspeed_kcs_of_v2_get_io_address(struct platform_device *pdev, u32
> +addrs[2])
>  {
> -	uint32_t slave;
>  	int rc;
> 
> -	rc = of_property_read_u32(pdev->dev.of_node, "aspeed,lpc-io-reg",
> &slave);
> -	if (rc || slave > 0xffff)
> +	rc = of_property_read_variable_u32_array(pdev->dev.of_node,
> +						 "aspeed,lpc-io-reg",
> +						 addrs, 1, 2);
> +	if (rc < 0)
> +		return rc;
> +
> +	if (WARN_ON(rc == 0))
> +		return -EINVAL;
> +
> +	if (addrs[0] > 0xffff)
> +		return -EINVAL;
> +
> +	if (rc == 2 && addrs[1] > 0xffff)
>  		return -EINVAL;
> 
> -	return slave;
> +	return rc;
>  }
> 
>  static int aspeed_kcs_probe(struct platform_device *pdev) @@ -527,9
> +571,11 @@ static int aspeed_kcs_probe(struct platform_device *pdev)
>  	struct kcs_bmc_device *kcs_bmc;
>  	struct aspeed_kcs_bmc *priv;
>  	struct device_node *np;
> -	int rc, channel, addr;
>  	bool have_upstream_irq;
>  	u32 upstream_irq[2];
> +	int rc, channel;
> +	int nr_addrs;
> +	u32 addrs[2];
> 
>  	np = pdev->dev.of_node->parent;
>  	if (!of_device_is_compatible(np, "aspeed,ast2400-lpc-v2") && @@ -547,9
> +593,9 @@ static int aspeed_kcs_probe(struct platform_device *pdev)
>  	if (channel < 0)
>  		return channel;
> 
> -	addr = ops->get_io_address(pdev);
> -	if (addr < 0)
> -		return addr;
> +	nr_addrs = ops->get_io_address(pdev, addrs);
> +	if (nr_addrs < 0)
> +		return nr_addrs;
> 
>  	np = pdev->dev.of_node;
>  	rc = of_property_read_u32_array(np, "aspeed,lpc-interrupts",
> upstream_irq, 2); @@ -578,7 +624,9 @@ static int aspeed_kcs_probe(struct
> platform_device *pdev)
>  	priv->obe.remove = false;
>  	timer_setup(&priv->obe.timer, aspeed_kcs_check_obe, 0);
> 
> -	aspeed_kcs_set_address(kcs_bmc, addr);
> +	rc = aspeed_kcs_set_address(kcs_bmc, addrs, nr_addrs);
> +	if (rc)
> +		return rc;
> 
>  	/* Host to BMC IRQ */
>  	rc = aspeed_kcs_config_downstream_irq(kcs_bmc, pdev); @@ -600,7
> +648,8 @@ static int aspeed_kcs_probe(struct platform_device *pdev)
>  	if (rc < 0)
>  		return rc;
> 
> -	dev_info(&pdev->dev, "Initialised channel %d at 0x%x\n",
> kcs_bmc->channel, addr);
> +	dev_info(&pdev->dev, "Initialised channel %d at 0x%x\n",
> +			kcs_bmc->channel, addrs[0]);
> 
>  	return 0;
>  }
> --
> 2.27.0


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

* Re: [PATCH v2 01/21] dt-bindings: aspeed-lpc: Remove LPC partitioning
  2021-03-19  6:27 [PATCH v2 01/21] dt-bindings: aspeed-lpc: Remove LPC partitioning Andrew Jeffery
                   ` (19 preceding siblings ...)
  2021-03-19  6:27 ` [PATCH v2 21/21] ipmi: kcs_bmc_aspeed: Optionally apply status address Andrew Jeffery
@ 2021-04-09  3:18 ` Joel Stanley
  2021-04-09  5:24   ` Andrew Jeffery
  20 siblings, 1 reply; 67+ messages in thread
From: Joel Stanley @ 2021-04-09  3:18 UTC (permalink / raw)
  To: Andrew Jeffery
  Cc: devicetree, Linus Walleij, Ryan Chen, Tomer Maimon,
	Corey Minyard, Rob Herring, Avi Fishman, Patrick Venture,
	Chia-Wei, Wang, Linux Kernel Mailing List, Tali Perry,
	open list:GPIO SUBSYSTEM, Rob Herring, Linux ARM,
	openipmi-developer, Lee Jones, OpenBMC Maillist, linux-aspeed,
	Benjamin Fair

On Fri, 19 Mar 2021 at 06:28, Andrew Jeffery <andrew@aj.id.au> wrote:
>
> From: "Chia-Wei, Wang" <chiawei_wang@aspeedtech.com>
>
> The LPC controller has no concept of the BMC and the Host partitions.
> This patch fixes the documentation by removing the description on LPC
> partitions. The register offsets illustrated in the DTS node examples
> are also fixed to adapt to the LPC DTS change.

Is this accurate:

 The node examples change their reg address to be an offset from the
LPC HC to be an offset from the base of the LPC region.

Reviewed-by: Joel Stanley <joel@jms.id.au>

>
> Signed-off-by: Chia-Wei Wang <chiawei_wang@aspeedtech.com>
> Reviewed-by: Andrew Jeffery <andrew@aj.id.au>
> Acked-by: Rob Herring <robh@kernel.org>
> Acked-by: Lee Jones <lee.jones@linaro.org>
> ---
>  .../devicetree/bindings/mfd/aspeed-lpc.txt    | 100 +++++-------------
>  1 file changed, 25 insertions(+), 75 deletions(-)
>
> diff --git a/Documentation/devicetree/bindings/mfd/aspeed-lpc.txt b/Documentation/devicetree/bindings/mfd/aspeed-lpc.txt
> index d0a38ba8b9ce..936aa108eab4 100644
> --- a/Documentation/devicetree/bindings/mfd/aspeed-lpc.txt
> +++ b/Documentation/devicetree/bindings/mfd/aspeed-lpc.txt
> @@ -9,13 +9,7 @@ primary use case of the Aspeed LPC controller is as a slave on the bus
>  conditions it can also take the role of bus master.
>
>  The LPC controller is represented as a multi-function device to account for the
> -mix of functionality it provides. The principle split is between the register
> -layout at the start of the I/O space which is, to quote the Aspeed datasheet,
> -"basically compatible with the [LPC registers from the] popular BMC controller
> -H8S/2168[1]", and everything else, where everything else is an eclectic
> -collection of functions with a esoteric register layout. "Everything else",
> -here labeled the "host" portion of the controller, includes, but is not limited
> -to:
> +mix of functionality, which includes, but is not limited to:
>
>  * An IPMI Block Transfer[2] Controller
>
> @@ -44,80 +38,36 @@ Required properties
>  ===================
>
>  - compatible:  One of:
> -               "aspeed,ast2400-lpc", "simple-mfd"
> -               "aspeed,ast2500-lpc", "simple-mfd"
> -               "aspeed,ast2600-lpc", "simple-mfd"
> +               "aspeed,ast2400-lpc-v2", "simple-mfd", "syscon"
> +               "aspeed,ast2500-lpc-v2", "simple-mfd", "syscon"
> +               "aspeed,ast2600-lpc-v2", "simple-mfd", "syscon"
>
>  - reg:         contains the physical address and length values of the Aspeed
>                  LPC memory region.
>
>  - #address-cells: <1>
>  - #size-cells: <1>
> -- ranges:      Maps 0 to the physical address and length of the LPC memory
> -                region
> -
> -Required LPC Child nodes
> -========================
> -
> -BMC Node
> ---------
> -
> -- compatible:  One of:
> -               "aspeed,ast2400-lpc-bmc"
> -               "aspeed,ast2500-lpc-bmc"
> -               "aspeed,ast2600-lpc-bmc"
> -
> -- reg:         contains the physical address and length values of the
> -                H8S/2168-compatible LPC controller memory region
> -
> -Host Node
> ----------
> -
> -- compatible:   One of:
> -               "aspeed,ast2400-lpc-host", "simple-mfd", "syscon"
> -               "aspeed,ast2500-lpc-host", "simple-mfd", "syscon"
> -               "aspeed,ast2600-lpc-host", "simple-mfd", "syscon"
> -
> -- reg:         contains the address and length values of the host-related
> -                register space for the Aspeed LPC controller
> -
> -- #address-cells: <1>
> -- #size-cells: <1>
> -- ranges:      Maps 0 to the address and length of the host-related LPC memory
> +- ranges:      Maps 0 to the physical address and length of the LPC memory
>                  region
>
>  Example:
>
>  lpc: lpc@1e789000 {
> -       compatible = "aspeed,ast2500-lpc", "simple-mfd";
> +       compatible = "aspeed,ast2500-lpc-v2", "simple-mfd", "syscon";
>         reg = <0x1e789000 0x1000>;
>
>         #address-cells = <1>;
>         #size-cells = <1>;
>         ranges = <0x0 0x1e789000 0x1000>;
>
> -       lpc_bmc: lpc-bmc@0 {
> -               compatible = "aspeed,ast2500-lpc-bmc";
> +       lpc_snoop: lpc-snoop@0 {
> +               compatible = "aspeed,ast2600-lpc-snoop";
>                 reg = <0x0 0x80>;
> -       };
> -
> -       lpc_host: lpc-host@80 {
> -               compatible = "aspeed,ast2500-lpc-host", "simple-mfd", "syscon";
> -               reg = <0x80 0x1e0>;
> -               reg-io-width = <4>;
> -
> -               #address-cells = <1>;
> -               #size-cells = <1>;
> -               ranges = <0x0 0x80 0x1e0>;
> +               interrupts = <GIC_SPI 144 IRQ_TYPE_LEVEL_HIGH>;
> +               snoop-ports = <0x80>;
>         };
>  };
>
> -BMC Node Children
> -==================
> -
> -
> -Host Node Children
> -==================
>
>  LPC Host Interface Controller
>  -------------------
> @@ -149,14 +99,12 @@ Optional properties:
>
>  Example:
>
> -lpc-host@80 {
> -       lpc_ctrl: lpc-ctrl@0 {
> -               compatible = "aspeed,ast2500-lpc-ctrl";
> -               reg = <0x0 0x80>;
> -               clocks = <&syscon ASPEED_CLK_GATE_LCLK>;
> -               memory-region = <&flash_memory>;
> -               flash = <&spi>;
> -       };
> +lpc_ctrl: lpc-ctrl@80 {
> +       compatible = "aspeed,ast2500-lpc-ctrl";
> +       reg = <0x80 0x80>;
> +       clocks = <&syscon ASPEED_CLK_GATE_LCLK>;
> +       memory-region = <&flash_memory>;
> +       flash = <&spi>;
>  };
>
>  LPC Host Controller
> @@ -179,9 +127,9 @@ Required properties:
>
>  Example:
>
> -lhc: lhc@20 {
> +lhc: lhc@a0 {
>         compatible = "aspeed,ast2500-lhc";
> -       reg = <0x20 0x24 0x48 0x8>;
> +       reg = <0xa0 0x24 0xc8 0x8>;
>  };
>
>  LPC reset control
> @@ -192,16 +140,18 @@ state of the LPC bus. Some systems may chose to modify this configuration.
>
>  Required properties:
>
> - - compatible:         "aspeed,ast2600-lpc-reset" or
> -                       "aspeed,ast2500-lpc-reset"
> -                       "aspeed,ast2400-lpc-reset"
> + - compatible:         One of:
> +                       "aspeed,ast2600-lpc-reset";
> +                       "aspeed,ast2500-lpc-reset";
> +                       "aspeed,ast2400-lpc-reset";
> +
>   - reg:                        offset and length of the IP in the LHC memory region
>   - #reset-controller   indicates the number of reset cells expected
>
>  Example:
>
> -lpc_reset: reset-controller@18 {
> +lpc_reset: reset-controller@98 {
>          compatible = "aspeed,ast2500-lpc-reset";
> -        reg = <0x18 0x4>;
> +        reg = <0x98 0x4>;
>          #reset-cells = <1>;
>  };
> --
> 2.27.0
>

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

* Re: [PATCH v2 06/21] ipmi: kcs_bmc_aspeed: Use of match data to extract KCS properties
  2021-03-19  6:27 ` [PATCH v2 06/21] ipmi: kcs_bmc_aspeed: Use of match data to extract KCS properties Andrew Jeffery
  2021-04-06  6:07   ` ChiaWei Wang
@ 2021-04-09  3:24   ` Zev Weiss
  1 sibling, 0 replies; 67+ messages in thread
From: Zev Weiss @ 2021-04-09  3:24 UTC (permalink / raw)
  To: Andrew Jeffery
  Cc: devicetree, chiawei_wang, ryan_chen, tmaimon77, minyard,
	avifishman70, venture, openbmc, linux-kernel, tali.perry1,
	linux-gpio, robh+dt, linux-arm-kernel, openipmi-developer,
	lee.jones, linus.walleij, linux-aspeed, benjaminfair

On Fri, Mar 19, 2021 at 01:27:37AM CDT, Andrew Jeffery wrote:
>Unpack and remove the aspeed_kcs_probe_of_v[12]() functions to aid
>rearranging how the private device-driver memory is allocated.
>
>Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
>---
> drivers/char/ipmi/kcs_bmc_aspeed.c | 146 ++++++++++++++---------------
> 1 file changed, 68 insertions(+), 78 deletions(-)
>
>diff --git a/drivers/char/ipmi/kcs_bmc_aspeed.c b/drivers/char/ipmi/kcs_bmc_aspeed.c
>index eefe362f65f0..061f53676206 100644
>--- a/drivers/char/ipmi/kcs_bmc_aspeed.c
>+++ b/drivers/char/ipmi/kcs_bmc_aspeed.c
>@@ -13,6 +13,7 @@
> #include <linux/module.h>
> #include <linux/of.h>
> #include <linux/of_address.h>
>+#include <linux/of_device.h>
> #include <linux/platform_device.h>
> #include <linux/poll.h>
> #include <linux/regmap.h>
>@@ -63,6 +64,10 @@ struct aspeed_kcs_bmc {
> 	struct regmap *map;
> };
>
>+struct aspeed_kcs_of_ops {
>+	int (*get_channel)(struct platform_device *pdev);
>+	int (*get_io_address)(struct platform_device *pdev);
>+};
>
> static u8 aspeed_kcs_inb(struct kcs_bmc *kcs_bmc, u32 reg)
> {
>@@ -231,13 +236,10 @@ static const struct kcs_ioreg ast_kcs_bmc_ioregs[KCS_CHANNEL_MAX] = {
> 	{ .idr = LPC_IDR4, .odr = LPC_ODR4, .str = LPC_STR4 },
> };
>
>-static struct kcs_bmc *aspeed_kcs_probe_of_v1(struct platform_device *pdev)
>+static int aspeed_kcs_of_v1_get_channel(struct platform_device *pdev)
> {
>-	struct aspeed_kcs_bmc *priv;
> 	struct device_node *np;
>-	struct kcs_bmc *kcs;
> 	u32 channel;
>-	u32 slave;
> 	int rc;
>
> 	np = pdev->dev.of_node;
>@@ -245,105 +247,78 @@ static struct kcs_bmc *aspeed_kcs_probe_of_v1(struct platform_device *pdev)
> 	rc = of_property_read_u32(np, "kcs_chan", &channel);
> 	if ((rc != 0) || (channel == 0 || channel > KCS_CHANNEL_MAX)) {
> 		dev_err(&pdev->dev, "no valid 'kcs_chan' configured\n");
>-		return ERR_PTR(-EINVAL);
>+		return -EINVAL;
> 	}
>
>-	kcs = kcs_bmc_alloc(&pdev->dev, sizeof(struct aspeed_kcs_bmc), channel);
>-	if (!kcs)
>-		return ERR_PTR(-ENOMEM);
>+	return channel;
>+}
>
>-	priv = kcs_bmc_priv(kcs);
>-	priv->map = syscon_node_to_regmap(pdev->dev.parent->of_node);
>-	if (IS_ERR(priv->map)) {
>-		dev_err(&pdev->dev, "Couldn't get regmap\n");
>-		return ERR_PTR(-ENODEV);
>-	}
>+static int aspeed_kcs_of_v1_get_io_address(struct platform_device *pdev)
>+{
>+	u32 slave;
>+	int rc;
>
>-	rc = of_property_read_u32(np, "kcs_addr", &slave);
>-	if (rc) {
>+	rc = of_property_read_u32(pdev->dev.of_node, "kcs_addr", &slave);
>+	if (rc || slave > 0xffff) {
> 		dev_err(&pdev->dev, "no valid 'kcs_addr' configured\n");
>-		return ERR_PTR(-EINVAL);
>+		return -EINVAL;
> 	}
>
>-	kcs->ioreg = ast_kcs_bmc_ioregs[channel - 1];
>-	aspeed_kcs_set_address(kcs, slave);
>-
>-	return kcs;
>-}
>-
>-static int aspeed_kcs_calculate_channel(const struct kcs_ioreg *regs)
>-{
>-	int i;
>-
>-	for (i = 0; i < ARRAY_SIZE(ast_kcs_bmc_ioregs); i++) {
>-		if (!memcmp(&ast_kcs_bmc_ioregs[i], regs, sizeof(*regs)))
>-			return i + 1;
>-	}
>-
>-	return -EINVAL;
>+	return slave;
> }
>
>-static struct kcs_bmc *aspeed_kcs_probe_of_v2(struct platform_device *pdev)
>+static int aspeed_kcs_of_v2_get_channel(struct platform_device *pdev)
> {
>-	struct aspeed_kcs_bmc *priv;
> 	struct device_node *np;
> 	struct kcs_ioreg ioreg;
>-	struct kcs_bmc *kcs;
> 	const __be32 *reg;
>-	int channel;
>-	u32 slave;
>-	int rc;
>+	int i;
>
> 	np = pdev->dev.of_node;
>
> 	/* Don't translate addresses, we want offsets for the regmaps */
> 	reg = of_get_address(np, 0, NULL, NULL);
> 	if (!reg)
>-		return ERR_PTR(-EINVAL);
>+		return -EINVAL;
> 	ioreg.idr = be32_to_cpup(reg);
>
> 	reg = of_get_address(np, 1, NULL, NULL);
> 	if (!reg)
>-		return ERR_PTR(-EINVAL);
>+		return -EINVAL;
> 	ioreg.odr = be32_to_cpup(reg);
>
> 	reg = of_get_address(np, 2, NULL, NULL);
> 	if (!reg)
>-		return ERR_PTR(-EINVAL);
>+		return -EINVAL;
> 	ioreg.str = be32_to_cpup(reg);
>
>-	channel = aspeed_kcs_calculate_channel(&ioreg);
>-	if (channel < 0)
>-		return ERR_PTR(channel);
>-
>-	kcs = kcs_bmc_alloc(&pdev->dev, sizeof(struct aspeed_kcs_bmc), channel);
>-	if (!kcs)
>-		return ERR_PTR(-ENOMEM);
>-
>-	kcs->ioreg = ioreg;
>-
>-	priv = kcs_bmc_priv(kcs);
>-	priv->map = syscon_node_to_regmap(pdev->dev.parent->of_node);
>-	if (IS_ERR(priv->map)) {
>-		dev_err(&pdev->dev, "Couldn't get regmap\n");
>-		return ERR_PTR(-ENODEV);
>+	for (i = 0; i < ARRAY_SIZE(ast_kcs_bmc_ioregs); i++) {
>+		if (!memcmp(&ast_kcs_bmc_ioregs[i], &ioreg, sizeof(ioreg)))
>+			return i + 1;

Did some patches perhaps get a bit jumbled during a rebase here or
something?  This patch removes the only initialization of priv->map
I can see, with patch #10 later reintroducing it, but in the interim
it seems like it would be in a fairly broken state (I'm guessing its
removal wasn't really intended to be part of this).

> 	}
>
>-	rc = of_property_read_u32(np, "aspeed,lpc-io-reg", &slave);
>-	if (rc)
>-		return ERR_PTR(rc);
>+	return -EINVAL;
>+}
>
>-	aspeed_kcs_set_address(kcs, slave);
>+static int aspeed_kcs_of_v2_get_io_address(struct platform_device *pdev)
>+{
>+	uint32_t slave;
>+	int rc;
>
>-	return kcs;
>+	rc = of_property_read_u32(pdev->dev.of_node, "aspeed,lpc-io-reg", &slave);
>+	if (rc || slave > 0xffff)
>+		return -EINVAL;
>+
>+	return slave;
> }
>
> static int aspeed_kcs_probe(struct platform_device *pdev)
> {
>+	const struct aspeed_kcs_of_ops *ops;
> 	struct device *dev = &pdev->dev;
> 	struct kcs_bmc *kcs_bmc;
> 	struct device_node *np;
>-	int rc;
>+	int rc, channel, addr;
>
> 	np = dev->of_node->parent;
> 	if (!of_device_is_compatible(np, "aspeed,ast2400-lpc-v2") &&
>@@ -352,23 +327,28 @@ static int aspeed_kcs_probe(struct platform_device *pdev)
> 		dev_err(dev, "unsupported LPC device binding\n");
> 		return -ENODEV;
> 	}
>-
>-	np = dev->of_node;
>-	if (of_device_is_compatible(np, "aspeed,ast2400-kcs-bmc") ||
>-	    of_device_is_compatible(np, "aspeed,ast2500-kcs-bmc"))
>-		kcs_bmc = aspeed_kcs_probe_of_v1(pdev);
>-	else if (of_device_is_compatible(np, "aspeed,ast2400-kcs-bmc-v2") ||
>-		 of_device_is_compatible(np, "aspeed,ast2500-kcs-bmc-v2"))
>-		kcs_bmc = aspeed_kcs_probe_of_v2(pdev);
>-	else
>+	ops = of_device_get_match_data(&pdev->dev);
>+	if (!ops)
> 		return -EINVAL;
>
>-	if (IS_ERR(kcs_bmc))
>-		return PTR_ERR(kcs_bmc);
>+	channel = ops->get_channel(pdev);
>+	if (channel < 0)
>+		return channel;
>
>+	kcs_bmc = kcs_bmc_alloc(&pdev->dev, sizeof(struct aspeed_kcs_bmc), channel);
>+	if (!kcs_bmc)
>+		return -ENOMEM;
>+
>+	kcs_bmc->ioreg = ast_kcs_bmc_ioregs[channel - 1];
> 	kcs_bmc->io_inputb = aspeed_kcs_inb;
> 	kcs_bmc->io_outputb = aspeed_kcs_outb;
>
>+	addr = ops->get_io_address(pdev);
>+	if (addr < 0)
>+		return addr;
>+
>+	aspeed_kcs_set_address(kcs_bmc, addr);
>+
> 	rc = aspeed_kcs_config_irq(kcs_bmc, pdev);
> 	if (rc)
> 		return rc;
>@@ -400,11 +380,21 @@ static int aspeed_kcs_remove(struct platform_device *pdev)
> 	return 0;
> }
>
>+static const struct aspeed_kcs_of_ops of_v1_ops = {
>+	.get_channel = aspeed_kcs_of_v1_get_channel,
>+	.get_io_address = aspeed_kcs_of_v1_get_io_address,
>+};
>+
>+static const struct aspeed_kcs_of_ops of_v2_ops = {
>+	.get_channel = aspeed_kcs_of_v2_get_channel,
>+	.get_io_address = aspeed_kcs_of_v2_get_io_address,
>+};
>+
> static const struct of_device_id ast_kcs_bmc_match[] = {
>-	{ .compatible = "aspeed,ast2400-kcs-bmc" },
>-	{ .compatible = "aspeed,ast2500-kcs-bmc" },
>-	{ .compatible = "aspeed,ast2400-kcs-bmc-v2" },
>-	{ .compatible = "aspeed,ast2500-kcs-bmc-v2" },
>+	{ .compatible = "aspeed,ast2400-kcs-bmc", .data = &of_v1_ops },
>+	{ .compatible = "aspeed,ast2500-kcs-bmc", .data = &of_v1_ops },
>+	{ .compatible = "aspeed,ast2400-kcs-bmc-v2", .data = &of_v2_ops },
>+	{ .compatible = "aspeed,ast2500-kcs-bmc-v2", .data = &of_v2_ops },
> 	{ }
> };
> MODULE_DEVICE_TABLE(of, ast_kcs_bmc_match);
>-- 
>2.27.0
>

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

* Re: [PATCH v2 03/21] ipmi: kcs: aspeed: Adapt to new LPC DTS layout
  2021-03-19  6:27 ` [PATCH v2 03/21] ipmi: kcs: aspeed: Adapt to new LPC DTS layout Andrew Jeffery
@ 2021-04-09  3:35   ` Joel Stanley
  0 siblings, 0 replies; 67+ messages in thread
From: Joel Stanley @ 2021-04-09  3:35 UTC (permalink / raw)
  To: Andrew Jeffery
  Cc: devicetree, Linus Walleij, Haiyue Wang, Ryan Chen, Tomer Maimon,
	Corey Minyard, Avi Fishman, Patrick Venture, Chia-Wei, Wang,
	Linux Kernel Mailing List, Tali Perry, open list:GPIO SUBSYSTEM,
	Rob Herring, Linux ARM, openipmi-developer, Lee Jones,
	OpenBMC Maillist, linux-aspeed, Benjamin Fair

On Fri, 19 Mar 2021 at 06:28, Andrew Jeffery <andrew@aj.id.au> wrote:
>
> From: "Chia-Wei, Wang" <chiawei_wang@aspeedtech.com>
>
> Add check against LPC device v2 compatible string to
> ensure that the fixed device tree layout is adopted.
> The LPC register offsets are also fixed accordingly.
>
> Signed-off-by: Chia-Wei Wang <chiawei_wang@aspeedtech.com>
> Reviewed-by: Andrew Jeffery <andrew@aj.id.au>
> Acked-by: Haiyue Wang <haiyue.wang@linux.intel.com>

Reviewed-by: Joel Stanley <joel@jms.id.au>

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

* Re: [PATCH v2 04/21] pinctrl: aspeed-g5: Adapt to new LPC device tree layout
  2021-03-19  6:27 ` [PATCH v2 04/21] pinctrl: aspeed-g5: Adapt to new LPC device tree layout Andrew Jeffery
@ 2021-04-09  3:36   ` Joel Stanley
  0 siblings, 0 replies; 67+ messages in thread
From: Joel Stanley @ 2021-04-09  3:36 UTC (permalink / raw)
  To: Andrew Jeffery
  Cc: devicetree, Linus Walleij, Ryan Chen, Tomer Maimon,
	Corey Minyard, Avi Fishman, Patrick Venture, Chia-Wei, Wang,
	Linux Kernel Mailing List, Tali Perry, open list:GPIO SUBSYSTEM,
	Rob Herring, Linux ARM, openipmi-developer, Lee Jones,
	OpenBMC Maillist, linux-aspeed, Benjamin Fair

On Fri, 19 Mar 2021 at 06:28, Andrew Jeffery <andrew@aj.id.au> wrote:
>
> From: "Chia-Wei, Wang" <chiawei_wang@aspeedtech.com>
>
> Add check against LPC device v2 compatible string to
> ensure that the fixed device tree layout is adopted.
> The LPC register offsets are also fixed accordingly.
>
> Signed-off-by: Chia-Wei Wang <chiawei_wang@aspeedtech.com>
> Reviewed-by: Andrew Jeffery <andrew@aj.id.au>
> Acked-by: Linus Walleij <linus.walleij@linaro.org>

Reviewed-by: Joel Stanley <joel@jms.id.au>

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

* Re: [PATCH v2 05/21] soc: aspeed: Adapt to new LPC device tree layout
  2021-03-19  6:27 ` [PATCH v2 05/21] soc: aspeed: " Andrew Jeffery
@ 2021-04-09  3:38   ` Joel Stanley
  0 siblings, 0 replies; 67+ messages in thread
From: Joel Stanley @ 2021-04-09  3:38 UTC (permalink / raw)
  To: Andrew Jeffery
  Cc: devicetree, Linus Walleij, Ryan Chen, Tomer Maimon,
	Corey Minyard, Avi Fishman, Patrick Venture, Chia-Wei, Wang,
	Linux Kernel Mailing List, Tali Perry, open list:GPIO SUBSYSTEM,
	Rob Herring, Linux ARM, openipmi-developer, Lee Jones,
	OpenBMC Maillist, linux-aspeed, Benjamin Fair

On Fri, 19 Mar 2021 at 06:28, Andrew Jeffery <andrew@aj.id.au> wrote:
>
> From: "Chia-Wei, Wang" <chiawei_wang@aspeedtech.com>
>
> Add check against LPC device v2 compatible string to
> ensure that the fixed device tree layout is adopted.
> The LPC register offsets are also fixed accordingly.
>
> Signed-off-by: Chia-Wei Wang <chiawei_wang@aspeedtech.com>
> Reviewed-by: Andrew Jeffery <andrew@aj.id.au>

Reviewed-by: Joel Stanley <joel@jms.id.au>

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

* Re: [PATCH v2 09/21] ipmi: kcs_bmc: Split out kcs_bmc_cdev_ipmi
  2021-03-19  6:27 ` [PATCH v2 09/21] ipmi: kcs_bmc: Split out kcs_bmc_cdev_ipmi Andrew Jeffery
@ 2021-04-09  3:56   ` Zev Weiss
  2021-04-09  5:48     ` Andrew Jeffery
  0 siblings, 1 reply; 67+ messages in thread
From: Zev Weiss @ 2021-04-09  3:56 UTC (permalink / raw)
  To: Andrew Jeffery
  Cc: devicetree, chiawei_wang, ryan_chen, tmaimon77, minyard,
	avifishman70, venture, openbmc, linux-kernel, tali.perry1,
	linux-gpio, robh+dt, linux-arm-kernel, openipmi-developer,
	lee.jones, linus.walleij, linux-aspeed, benjaminfair

On Fri, Mar 19, 2021 at 01:27:40AM CDT, Andrew Jeffery wrote:
>Take steps towards defining a coherent API to separate the KCS device
>drivers from the userspace interface. Decreasing the coupling will
>improve the separation of concerns and enable the introduction of
>alternative userspace interfaces.
>
>For now, simply split the chardev logic out to a separate file. The code
>continues to build into the same module.
>
>Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
>---
> drivers/char/ipmi/Makefile            |   2 +-
> drivers/char/ipmi/kcs_bmc.c           | 423 +------------------------
> drivers/char/ipmi/kcs_bmc.h           |  10 +-
> drivers/char/ipmi/kcs_bmc_cdev_ipmi.c | 428 ++++++++++++++++++++++++++
> 4 files changed, 451 insertions(+), 412 deletions(-)
> create mode 100644 drivers/char/ipmi/kcs_bmc_cdev_ipmi.c
>
>diff --git a/drivers/char/ipmi/Makefile b/drivers/char/ipmi/Makefile
>index 0822adc2ec41..a302bc865370 100644
>--- a/drivers/char/ipmi/Makefile
>+++ b/drivers/char/ipmi/Makefile
>@@ -22,7 +22,7 @@ obj-$(CONFIG_IPMI_SSIF) += ipmi_ssif.o
> obj-$(CONFIG_IPMI_POWERNV) += ipmi_powernv.o
> obj-$(CONFIG_IPMI_WATCHDOG) += ipmi_watchdog.o
> obj-$(CONFIG_IPMI_POWEROFF) += ipmi_poweroff.o
>-obj-$(CONFIG_IPMI_KCS_BMC) += kcs_bmc.o
>+obj-$(CONFIG_IPMI_KCS_BMC) += kcs_bmc.o kcs_bmc_cdev_ipmi.o
> obj-$(CONFIG_ASPEED_BT_IPMI_BMC) += bt-bmc.o
> obj-$(CONFIG_ASPEED_KCS_IPMI_BMC) += kcs_bmc_aspeed.o
> obj-$(CONFIG_NPCM7XX_KCS_IPMI_BMC) += kcs_bmc_npcm7xx.o
>diff --git a/drivers/char/ipmi/kcs_bmc.c b/drivers/char/ipmi/kcs_bmc.c
>index c4336c1f2d6d..ef5c48ffe74a 100644
>--- a/drivers/char/ipmi/kcs_bmc.c
>+++ b/drivers/char/ipmi/kcs_bmc.c
>@@ -3,446 +3,51 @@
>  * Copyright (c) 2015-2018, Intel Corporation.
>  */
>
>-#define pr_fmt(fmt) "kcs-bmc: " fmt
>-
>-#include <linux/errno.h>
>-#include <linux/io.h>
>-#include <linux/ipmi_bmc.h>
> #include <linux/module.h>
>-#include <linux/platform_device.h>
>-#include <linux/poll.h>
>-#include <linux/sched.h>
>-#include <linux/slab.h>
>
> #include "kcs_bmc.h"
>
>-#define DEVICE_NAME "ipmi-kcs"
>-
>-#define KCS_MSG_BUFSIZ    1000
>-
>-#define KCS_ZERO_DATA     0
>-
>-
>-/* IPMI 2.0 - Table 9-1, KCS Interface Status Register Bits */
>-#define KCS_STATUS_STATE(state) (state << 6)
>-#define KCS_STATUS_STATE_MASK   GENMASK(7, 6)
>-#define KCS_STATUS_CMD_DAT      BIT(3)
>-#define KCS_STATUS_SMS_ATN      BIT(2)
>-#define KCS_STATUS_IBF          BIT(1)
>-#define KCS_STATUS_OBF          BIT(0)
>-
>-/* IPMI 2.0 - Table 9-2, KCS Interface State Bits */
>-enum kcs_states {
>-	IDLE_STATE  = 0,
>-	READ_STATE  = 1,
>-	WRITE_STATE = 2,
>-	ERROR_STATE = 3,
>-};
>-
>-/* IPMI 2.0 - Table 9-3, KCS Interface Control Codes */
>-#define KCS_CMD_GET_STATUS_ABORT  0x60
>-#define KCS_CMD_WRITE_START       0x61
>-#define KCS_CMD_WRITE_END         0x62
>-#define KCS_CMD_READ_BYTE         0x68
>-
>-static inline u8 kcs_bmc_read_data(struct kcs_bmc *kcs_bmc)
>+u8 kcs_bmc_read_data(struct kcs_bmc *kcs_bmc)
> {
> 	return kcs_bmc->io_inputb(kcs_bmc, kcs_bmc->ioreg.idr);
> }
>+EXPORT_SYMBOL(kcs_bmc_read_data);
>
>-static inline void kcs_bmc_write_data(struct kcs_bmc *kcs_bmc, u8 data)
>+void kcs_bmc_write_data(struct kcs_bmc *kcs_bmc, u8 data)
> {
> 	kcs_bmc->io_outputb(kcs_bmc, kcs_bmc->ioreg.odr, data);
> }
>+EXPORT_SYMBOL(kcs_bmc_write_data);
>
>-static inline u8 kcs_bmc_read_status(struct kcs_bmc *kcs_bmc)
>+u8 kcs_bmc_read_status(struct kcs_bmc *kcs_bmc)
> {
> 	return kcs_bmc->io_inputb(kcs_bmc, kcs_bmc->ioreg.str);
> }
>+EXPORT_SYMBOL(kcs_bmc_read_status);
>
>-static inline void kcs_bmc_write_status(struct kcs_bmc *kcs_bmc, u8 data)
>+void kcs_bmc_write_status(struct kcs_bmc *kcs_bmc, u8 data)
> {
> 	kcs_bmc->io_outputb(kcs_bmc, kcs_bmc->ioreg.str, data);
> }
>+EXPORT_SYMBOL(kcs_bmc_write_status);
>
>-static void kcs_bmc_update_status(struct kcs_bmc *kcs_bmc, u8 mask, u8 val)
>+void kcs_bmc_update_status(struct kcs_bmc *kcs_bmc, u8 mask, u8 val)
> {
> 	kcs_bmc->io_updateb(kcs_bmc, kcs_bmc->ioreg.str, mask, val);
> }
>+EXPORT_SYMBOL(kcs_bmc_update_status);
>
>-static inline void set_state(struct kcs_bmc *kcs_bmc, u8 state)
>-{
>-	kcs_bmc_update_status(kcs_bmc, KCS_STATUS_STATE_MASK,
>-					KCS_STATUS_STATE(state));
>-}
>-
>-static void kcs_force_abort(struct kcs_bmc *kcs_bmc)
>-{
>-	set_state(kcs_bmc, ERROR_STATE);
>-	kcs_bmc_read_data(kcs_bmc);
>-	kcs_bmc_write_data(kcs_bmc, KCS_ZERO_DATA);
>-
>-	kcs_bmc->phase = KCS_PHASE_ERROR;
>-	kcs_bmc->data_in_avail = false;
>-	kcs_bmc->data_in_idx = 0;
>-}
>-
>-static void kcs_bmc_handle_data(struct kcs_bmc *kcs_bmc)
>-{
>-	u8 data;
>-
>-	switch (kcs_bmc->phase) {
>-	case KCS_PHASE_WRITE_START:
>-		kcs_bmc->phase = KCS_PHASE_WRITE_DATA;
>-		fallthrough;
>-
>-	case KCS_PHASE_WRITE_DATA:
>-		if (kcs_bmc->data_in_idx < KCS_MSG_BUFSIZ) {
>-			set_state(kcs_bmc, WRITE_STATE);
>-			kcs_bmc_write_data(kcs_bmc, KCS_ZERO_DATA);
>-			kcs_bmc->data_in[kcs_bmc->data_in_idx++] =
>-						kcs_bmc_read_data(kcs_bmc);
>-		} else {
>-			kcs_force_abort(kcs_bmc);
>-			kcs_bmc->error = KCS_LENGTH_ERROR;
>-		}
>-		break;
>-
>-	case KCS_PHASE_WRITE_END_CMD:
>-		if (kcs_bmc->data_in_idx < KCS_MSG_BUFSIZ) {
>-			set_state(kcs_bmc, READ_STATE);
>-			kcs_bmc->data_in[kcs_bmc->data_in_idx++] =
>-						kcs_bmc_read_data(kcs_bmc);
>-			kcs_bmc->phase = KCS_PHASE_WRITE_DONE;
>-			kcs_bmc->data_in_avail = true;
>-			wake_up_interruptible(&kcs_bmc->queue);
>-		} else {
>-			kcs_force_abort(kcs_bmc);
>-			kcs_bmc->error = KCS_LENGTH_ERROR;
>-		}
>-		break;
>-
>-	case KCS_PHASE_READ:
>-		if (kcs_bmc->data_out_idx == kcs_bmc->data_out_len)
>-			set_state(kcs_bmc, IDLE_STATE);
>-
>-		data = kcs_bmc_read_data(kcs_bmc);
>-		if (data != KCS_CMD_READ_BYTE) {
>-			set_state(kcs_bmc, ERROR_STATE);
>-			kcs_bmc_write_data(kcs_bmc, KCS_ZERO_DATA);
>-			break;
>-		}
>-
>-		if (kcs_bmc->data_out_idx == kcs_bmc->data_out_len) {
>-			kcs_bmc_write_data(kcs_bmc, KCS_ZERO_DATA);
>-			kcs_bmc->phase = KCS_PHASE_IDLE;
>-			break;
>-		}
>-
>-		kcs_bmc_write_data(kcs_bmc,
>-			kcs_bmc->data_out[kcs_bmc->data_out_idx++]);
>-		break;
>-
>-	case KCS_PHASE_ABORT_ERROR1:
>-		set_state(kcs_bmc, READ_STATE);
>-		kcs_bmc_read_data(kcs_bmc);
>-		kcs_bmc_write_data(kcs_bmc, kcs_bmc->error);
>-		kcs_bmc->phase = KCS_PHASE_ABORT_ERROR2;
>-		break;
>-
>-	case KCS_PHASE_ABORT_ERROR2:
>-		set_state(kcs_bmc, IDLE_STATE);
>-		kcs_bmc_read_data(kcs_bmc);
>-		kcs_bmc_write_data(kcs_bmc, KCS_ZERO_DATA);
>-		kcs_bmc->phase = KCS_PHASE_IDLE;
>-		break;
>-
>-	default:
>-		kcs_force_abort(kcs_bmc);
>-		break;
>-	}
>-}
>-
>-static void kcs_bmc_handle_cmd(struct kcs_bmc *kcs_bmc)
>-{
>-	u8 cmd;
>-
>-	set_state(kcs_bmc, WRITE_STATE);
>-	kcs_bmc_write_data(kcs_bmc, KCS_ZERO_DATA);
>-
>-	cmd = kcs_bmc_read_data(kcs_bmc);
>-	switch (cmd) {
>-	case KCS_CMD_WRITE_START:
>-		kcs_bmc->phase = KCS_PHASE_WRITE_START;
>-		kcs_bmc->error = KCS_NO_ERROR;
>-		kcs_bmc->data_in_avail = false;
>-		kcs_bmc->data_in_idx = 0;
>-		break;
>-
>-	case KCS_CMD_WRITE_END:
>-		if (kcs_bmc->phase != KCS_PHASE_WRITE_DATA) {
>-			kcs_force_abort(kcs_bmc);
>-			break;
>-		}
>-
>-		kcs_bmc->phase = KCS_PHASE_WRITE_END_CMD;
>-		break;
>-
>-	case KCS_CMD_GET_STATUS_ABORT:
>-		if (kcs_bmc->error == KCS_NO_ERROR)
>-			kcs_bmc->error = KCS_ABORTED_BY_COMMAND;
>-
>-		kcs_bmc->phase = KCS_PHASE_ABORT_ERROR1;
>-		kcs_bmc->data_in_avail = false;
>-		kcs_bmc->data_in_idx = 0;
>-		break;
>-
>-	default:
>-		kcs_force_abort(kcs_bmc);
>-		kcs_bmc->error = KCS_ILLEGAL_CONTROL_CODE;
>-		break;
>-	}
>-}
>-
>+int kcs_bmc_ipmi_event(struct kcs_bmc *kcs_bmc);

This declaration looks a bit out of place here; should it be in
kcs_bmc.h instead?

> int kcs_bmc_handle_event(struct kcs_bmc *kcs_bmc)
> {
>-	unsigned long flags;
>-	int ret = -ENODATA;
>-	u8 status;
>-
>-	spin_lock_irqsave(&kcs_bmc->lock, flags);
>-
>-	status = kcs_bmc_read_status(kcs_bmc);
>-	if (status & KCS_STATUS_IBF) {
>-		if (!kcs_bmc->running)
>-			kcs_force_abort(kcs_bmc);
>-		else if (status & KCS_STATUS_CMD_DAT)
>-			kcs_bmc_handle_cmd(kcs_bmc);
>-		else
>-			kcs_bmc_handle_data(kcs_bmc);
>-
>-		ret = 0;
>-	}
>-
>-	spin_unlock_irqrestore(&kcs_bmc->lock, flags);
>-
>-	return ret;
>+	return kcs_bmc_ipmi_event(kcs_bmc);
> }
> EXPORT_SYMBOL(kcs_bmc_handle_event);
>
>-static inline struct kcs_bmc *to_kcs_bmc(struct file *filp)
>-{
>-	return container_of(filp->private_data, struct kcs_bmc, miscdev);
>-}
>-
>-static int kcs_bmc_open(struct inode *inode, struct file *filp)
>-{
>-	struct kcs_bmc *kcs_bmc = to_kcs_bmc(filp);
>-	int ret = 0;
>-
>-	spin_lock_irq(&kcs_bmc->lock);
>-	if (!kcs_bmc->running)
>-		kcs_bmc->running = 1;
>-	else
>-		ret = -EBUSY;
>-	spin_unlock_irq(&kcs_bmc->lock);
>-
>-	return ret;
>-}
>-
>-static __poll_t kcs_bmc_poll(struct file *filp, poll_table *wait)
>-{
>-	struct kcs_bmc *kcs_bmc = to_kcs_bmc(filp);
>-	__poll_t mask = 0;
>-
>-	poll_wait(filp, &kcs_bmc->queue, wait);
>-
>-	spin_lock_irq(&kcs_bmc->lock);
>-	if (kcs_bmc->data_in_avail)
>-		mask |= EPOLLIN;
>-	spin_unlock_irq(&kcs_bmc->lock);
>-
>-	return mask;
>-}
>-
>-static ssize_t kcs_bmc_read(struct file *filp, char __user *buf,
>-			    size_t count, loff_t *ppos)
>-{
>-	struct kcs_bmc *kcs_bmc = to_kcs_bmc(filp);
>-	bool data_avail;
>-	size_t data_len;
>-	ssize_t ret;
>-
>-	if (!(filp->f_flags & O_NONBLOCK))
>-		wait_event_interruptible(kcs_bmc->queue,
>-					 kcs_bmc->data_in_avail);
>-
>-	mutex_lock(&kcs_bmc->mutex);
>-
>-	spin_lock_irq(&kcs_bmc->lock);
>-	data_avail = kcs_bmc->data_in_avail;
>-	if (data_avail) {
>-		data_len = kcs_bmc->data_in_idx;
>-		memcpy(kcs_bmc->kbuffer, kcs_bmc->data_in, data_len);
>-	}
>-	spin_unlock_irq(&kcs_bmc->lock);
>-
>-	if (!data_avail) {
>-		ret = -EAGAIN;
>-		goto out_unlock;
>-	}
>-
>-	if (count < data_len) {
>-		pr_err("channel=%u with too large data : %zu\n",
>-			kcs_bmc->channel, data_len);
>-
>-		spin_lock_irq(&kcs_bmc->lock);
>-		kcs_force_abort(kcs_bmc);
>-		spin_unlock_irq(&kcs_bmc->lock);
>-
>-		ret = -EOVERFLOW;
>-		goto out_unlock;
>-	}
>-
>-	if (copy_to_user(buf, kcs_bmc->kbuffer, data_len)) {
>-		ret = -EFAULT;
>-		goto out_unlock;
>-	}
>-
>-	ret = data_len;
>-
>-	spin_lock_irq(&kcs_bmc->lock);
>-	if (kcs_bmc->phase == KCS_PHASE_WRITE_DONE) {
>-		kcs_bmc->phase = KCS_PHASE_WAIT_READ;
>-		kcs_bmc->data_in_avail = false;
>-		kcs_bmc->data_in_idx = 0;
>-	} else {
>-		ret = -EAGAIN;
>-	}
>-	spin_unlock_irq(&kcs_bmc->lock);
>-
>-out_unlock:
>-	mutex_unlock(&kcs_bmc->mutex);
>-
>-	return ret;
>-}
>-
>-static ssize_t kcs_bmc_write(struct file *filp, const char __user *buf,
>-			     size_t count, loff_t *ppos)
>-{
>-	struct kcs_bmc *kcs_bmc = to_kcs_bmc(filp);
>-	ssize_t ret;
>-
>-	/* a minimum response size '3' : netfn + cmd + ccode */
>-	if (count < 3 || count > KCS_MSG_BUFSIZ)
>-		return -EINVAL;
>-
>-	mutex_lock(&kcs_bmc->mutex);
>-
>-	if (copy_from_user(kcs_bmc->kbuffer, buf, count)) {
>-		ret = -EFAULT;
>-		goto out_unlock;
>-	}
>-
>-	spin_lock_irq(&kcs_bmc->lock);
>-	if (kcs_bmc->phase == KCS_PHASE_WAIT_READ) {
>-		kcs_bmc->phase = KCS_PHASE_READ;
>-		kcs_bmc->data_out_idx = 1;
>-		kcs_bmc->data_out_len = count;
>-		memcpy(kcs_bmc->data_out, kcs_bmc->kbuffer, count);
>-		kcs_bmc_write_data(kcs_bmc, kcs_bmc->data_out[0]);
>-		ret = count;
>-	} else {
>-		ret = -EINVAL;
>-	}
>-	spin_unlock_irq(&kcs_bmc->lock);
>-
>-out_unlock:
>-	mutex_unlock(&kcs_bmc->mutex);
>-
>-	return ret;
>-}
>-
>-static long kcs_bmc_ioctl(struct file *filp, unsigned int cmd,
>-			  unsigned long arg)
>-{
>-	struct kcs_bmc *kcs_bmc = to_kcs_bmc(filp);
>-	long ret = 0;
>-
>-	spin_lock_irq(&kcs_bmc->lock);
>-
>-	switch (cmd) {
>-	case IPMI_BMC_IOCTL_SET_SMS_ATN:
>-		kcs_bmc_update_status(kcs_bmc, KCS_STATUS_SMS_ATN, KCS_STATUS_SMS_ATN);
>-		break;
>-
>-	case IPMI_BMC_IOCTL_CLEAR_SMS_ATN:
>-		kcs_bmc_update_status(kcs_bmc, KCS_STATUS_SMS_ATN, 0);
>-		break;
>-
>-	case IPMI_BMC_IOCTL_FORCE_ABORT:
>-		kcs_force_abort(kcs_bmc);
>-		break;
>-
>-	default:
>-		ret = -EINVAL;
>-		break;
>-	}
>-
>-	spin_unlock_irq(&kcs_bmc->lock);
>-
>-	return ret;
>-}
>-
>-static int kcs_bmc_release(struct inode *inode, struct file *filp)
>-{
>-	struct kcs_bmc *kcs_bmc = to_kcs_bmc(filp);
>-
>-	spin_lock_irq(&kcs_bmc->lock);
>-	kcs_bmc->running = 0;
>-	kcs_force_abort(kcs_bmc);
>-	spin_unlock_irq(&kcs_bmc->lock);
>-
>-	return 0;
>-}
>-
>-static const struct file_operations kcs_bmc_fops = {
>-	.owner          = THIS_MODULE,
>-	.open           = kcs_bmc_open,
>-	.read           = kcs_bmc_read,
>-	.write          = kcs_bmc_write,
>-	.release        = kcs_bmc_release,
>-	.poll           = kcs_bmc_poll,
>-	.unlocked_ioctl = kcs_bmc_ioctl,
>-};
>-
>+struct kcs_bmc *kcs_bmc_ipmi_alloc(struct device *dev, int sizeof_priv, u32 channel);

Similarly here.

> struct kcs_bmc *kcs_bmc_alloc(struct device *dev, int sizeof_priv, u32 channel)
> {
>-	struct kcs_bmc *kcs_bmc;
>-
>-	kcs_bmc = devm_kzalloc(dev, sizeof(*kcs_bmc) + sizeof_priv, GFP_KERNEL);
>-	if (!kcs_bmc)
>-		return NULL;
>-
>-	spin_lock_init(&kcs_bmc->lock);
>-	kcs_bmc->channel = channel;
>-
>-	mutex_init(&kcs_bmc->mutex);
>-	init_waitqueue_head(&kcs_bmc->queue);
>-
>-	kcs_bmc->data_in = devm_kmalloc(dev, KCS_MSG_BUFSIZ, GFP_KERNEL);
>-	kcs_bmc->data_out = devm_kmalloc(dev, KCS_MSG_BUFSIZ, GFP_KERNEL);
>-	kcs_bmc->kbuffer = devm_kmalloc(dev, KCS_MSG_BUFSIZ, GFP_KERNEL);
>-
>-	kcs_bmc->miscdev.minor = MISC_DYNAMIC_MINOR;
>-	kcs_bmc->miscdev.name = devm_kasprintf(dev, GFP_KERNEL, "%s%u",
>-					       DEVICE_NAME, channel);
>-	if (!kcs_bmc->data_in || !kcs_bmc->data_out || !kcs_bmc->kbuffer ||
>-	    !kcs_bmc->miscdev.name)
>-		return NULL;
>-	kcs_bmc->miscdev.fops = &kcs_bmc_fops;
>-
>-	return kcs_bmc;
>+	return kcs_bmc_ipmi_alloc(dev, sizeof_priv, channel);
> }
> EXPORT_SYMBOL(kcs_bmc_alloc);
>
>diff --git a/drivers/char/ipmi/kcs_bmc.h b/drivers/char/ipmi/kcs_bmc.h
>index 970f53892f2d..febea0c8deb4 100644
>--- a/drivers/char/ipmi/kcs_bmc.h
>+++ b/drivers/char/ipmi/kcs_bmc.h
>@@ -104,6 +104,12 @@ static inline void *kcs_bmc_priv(struct kcs_bmc *kcs_bmc)
> }
>
> int kcs_bmc_handle_event(struct kcs_bmc *kcs_bmc);
>-struct kcs_bmc *kcs_bmc_alloc(struct device *dev, int sizeof_priv,
>-					u32 channel);
>+struct kcs_bmc *kcs_bmc_alloc(struct device *dev, int sizeof_priv, u32 channel);
>+
>+u8 kcs_bmc_read_data(struct kcs_bmc *kcs_bmc);
>+void kcs_bmc_write_data(struct kcs_bmc *kcs_bmc, u8 data);
>+u8 kcs_bmc_read_status(struct kcs_bmc *kcs_bmc);
>+void kcs_bmc_write_status(struct kcs_bmc *kcs_bmc, u8 data);
>+void kcs_bmc_update_status(struct kcs_bmc *kcs_bmc, u8 mask, u8 val);
>+
> #endif /* __KCS_BMC_H__ */
>diff --git a/drivers/char/ipmi/kcs_bmc_cdev_ipmi.c b/drivers/char/ipmi/kcs_bmc_cdev_ipmi.c
>new file mode 100644
>index 000000000000..82c77994e481
>--- /dev/null
>+++ b/drivers/char/ipmi/kcs_bmc_cdev_ipmi.c
>@@ -0,0 +1,428 @@
>+// SPDX-License-Identifier: GPL-2.0
>+/*
>+ * Copyright (c) 2015-2018, Intel Corporation.
>+ */
>+
>+#define pr_fmt(fmt) "kcs-bmc: " fmt
>+
>+#include <linux/errno.h>
>+#include <linux/io.h>
>+#include <linux/ipmi_bmc.h>
>+#include <linux/module.h>
>+#include <linux/platform_device.h>
>+#include <linux/poll.h>
>+#include <linux/sched.h>
>+#include <linux/slab.h>
>+
>+#include "kcs_bmc.h"
>+
>+#define DEVICE_NAME "ipmi-kcs"
>+
>+#define KCS_MSG_BUFSIZ    1000
>+
>+#define KCS_ZERO_DATA     0
>+
>+
>+/* IPMI 2.0 - Table 9-1, KCS Interface Status Register Bits */
>+#define KCS_STATUS_STATE(state) (state << 6)
>+#define KCS_STATUS_STATE_MASK   GENMASK(7, 6)
>+#define KCS_STATUS_CMD_DAT      BIT(3)
>+#define KCS_STATUS_SMS_ATN      BIT(2)
>+#define KCS_STATUS_IBF          BIT(1)
>+#define KCS_STATUS_OBF          BIT(0)
>+
>+/* IPMI 2.0 - Table 9-2, KCS Interface State Bits */
>+enum kcs_states {
>+	IDLE_STATE  = 0,
>+	READ_STATE  = 1,
>+	WRITE_STATE = 2,
>+	ERROR_STATE = 3,
>+};
>+
>+/* IPMI 2.0 - Table 9-3, KCS Interface Control Codes */
>+#define KCS_CMD_GET_STATUS_ABORT  0x60
>+#define KCS_CMD_WRITE_START       0x61
>+#define KCS_CMD_WRITE_END         0x62
>+#define KCS_CMD_READ_BYTE         0x68
>+
>+static inline void set_state(struct kcs_bmc *kcs_bmc, u8 state)
>+{
>+	kcs_bmc_update_status(kcs_bmc, KCS_STATUS_STATE_MASK,
>+					KCS_STATUS_STATE(state));
>+}
>+
>+static void kcs_bmc_ipmi_force_abort(struct kcs_bmc *kcs_bmc)
>+{
>+	set_state(kcs_bmc, ERROR_STATE);
>+	kcs_bmc_read_data(kcs_bmc);
>+	kcs_bmc_write_data(kcs_bmc, KCS_ZERO_DATA);
>+
>+	kcs_bmc->phase = KCS_PHASE_ERROR;
>+	kcs_bmc->data_in_avail = false;
>+	kcs_bmc->data_in_idx = 0;
>+}
>+
>+static void kcs_bmc_ipmi_handle_data(struct kcs_bmc *kcs_bmc)
>+{
>+	u8 data;
>+
>+	switch (kcs_bmc->phase) {
>+	case KCS_PHASE_WRITE_START:
>+		kcs_bmc->phase = KCS_PHASE_WRITE_DATA;
>+		fallthrough;
>+
>+	case KCS_PHASE_WRITE_DATA:
>+		if (kcs_bmc->data_in_idx < KCS_MSG_BUFSIZ) {
>+			set_state(kcs_bmc, WRITE_STATE);
>+			kcs_bmc_write_data(kcs_bmc, KCS_ZERO_DATA);
>+			kcs_bmc->data_in[kcs_bmc->data_in_idx++] =
>+						kcs_bmc_read_data(kcs_bmc);
>+		} else {
>+			kcs_bmc_ipmi_force_abort(kcs_bmc);
>+			kcs_bmc->error = KCS_LENGTH_ERROR;
>+		}
>+		break;
>+
>+	case KCS_PHASE_WRITE_END_CMD:
>+		if (kcs_bmc->data_in_idx < KCS_MSG_BUFSIZ) {
>+			set_state(kcs_bmc, READ_STATE);
>+			kcs_bmc->data_in[kcs_bmc->data_in_idx++] =
>+						kcs_bmc_read_data(kcs_bmc);
>+			kcs_bmc->phase = KCS_PHASE_WRITE_DONE;
>+			kcs_bmc->data_in_avail = true;
>+			wake_up_interruptible(&kcs_bmc->queue);
>+		} else {
>+			kcs_bmc_ipmi_force_abort(kcs_bmc);
>+			kcs_bmc->error = KCS_LENGTH_ERROR;
>+		}
>+		break;
>+
>+	case KCS_PHASE_READ:
>+		if (kcs_bmc->data_out_idx == kcs_bmc->data_out_len)
>+			set_state(kcs_bmc, IDLE_STATE);
>+
>+		data = kcs_bmc_read_data(kcs_bmc);
>+		if (data != KCS_CMD_READ_BYTE) {
>+			set_state(kcs_bmc, ERROR_STATE);
>+			kcs_bmc_write_data(kcs_bmc, KCS_ZERO_DATA);
>+			break;
>+		}
>+
>+		if (kcs_bmc->data_out_idx == kcs_bmc->data_out_len) {
>+			kcs_bmc_write_data(kcs_bmc, KCS_ZERO_DATA);
>+			kcs_bmc->phase = KCS_PHASE_IDLE;
>+			break;
>+		}
>+
>+		kcs_bmc_write_data(kcs_bmc,
>+			kcs_bmc->data_out[kcs_bmc->data_out_idx++]);
>+		break;
>+
>+	case KCS_PHASE_ABORT_ERROR1:
>+		set_state(kcs_bmc, READ_STATE);
>+		kcs_bmc_read_data(kcs_bmc);
>+		kcs_bmc_write_data(kcs_bmc, kcs_bmc->error);
>+		kcs_bmc->phase = KCS_PHASE_ABORT_ERROR2;
>+		break;
>+
>+	case KCS_PHASE_ABORT_ERROR2:
>+		set_state(kcs_bmc, IDLE_STATE);
>+		kcs_bmc_read_data(kcs_bmc);
>+		kcs_bmc_write_data(kcs_bmc, KCS_ZERO_DATA);
>+		kcs_bmc->phase = KCS_PHASE_IDLE;
>+		break;
>+
>+	default:
>+		kcs_bmc_ipmi_force_abort(kcs_bmc);
>+		break;
>+	}
>+}
>+
>+static void kcs_bmc_ipmi_handle_cmd(struct kcs_bmc *kcs_bmc)
>+{
>+	u8 cmd;
>+
>+	set_state(kcs_bmc, WRITE_STATE);
>+	kcs_bmc_write_data(kcs_bmc, KCS_ZERO_DATA);
>+
>+	cmd = kcs_bmc_read_data(kcs_bmc);
>+	switch (cmd) {
>+	case KCS_CMD_WRITE_START:
>+		kcs_bmc->phase = KCS_PHASE_WRITE_START;
>+		kcs_bmc->error = KCS_NO_ERROR;
>+		kcs_bmc->data_in_avail = false;
>+		kcs_bmc->data_in_idx = 0;
>+		break;
>+
>+	case KCS_CMD_WRITE_END:
>+		if (kcs_bmc->phase != KCS_PHASE_WRITE_DATA) {
>+			kcs_bmc_ipmi_force_abort(kcs_bmc);
>+			break;
>+		}
>+
>+		kcs_bmc->phase = KCS_PHASE_WRITE_END_CMD;
>+		break;
>+
>+	case KCS_CMD_GET_STATUS_ABORT:
>+		if (kcs_bmc->error == KCS_NO_ERROR)
>+			kcs_bmc->error = KCS_ABORTED_BY_COMMAND;
>+
>+		kcs_bmc->phase = KCS_PHASE_ABORT_ERROR1;
>+		kcs_bmc->data_in_avail = false;
>+		kcs_bmc->data_in_idx = 0;
>+		break;
>+
>+	default:
>+		kcs_bmc_ipmi_force_abort(kcs_bmc);
>+		kcs_bmc->error = KCS_ILLEGAL_CONTROL_CODE;
>+		break;
>+	}
>+}
>+
>+int kcs_bmc_ipmi_event(struct kcs_bmc *kcs_bmc);

Another instance...

>+int kcs_bmc_ipmi_event(struct kcs_bmc *kcs_bmc)
>+{
>+	unsigned long flags;
>+	int ret = -ENODATA;
>+	u8 status;
>+
>+	spin_lock_irqsave(&kcs_bmc->lock, flags);
>+
>+	status = kcs_bmc_read_status(kcs_bmc);
>+	if (status & KCS_STATUS_IBF) {
>+		if (!kcs_bmc->running)
>+			kcs_bmc_ipmi_force_abort(kcs_bmc);
>+		else if (status & KCS_STATUS_CMD_DAT)
>+			kcs_bmc_ipmi_handle_cmd(kcs_bmc);
>+		else
>+			kcs_bmc_ipmi_handle_data(kcs_bmc);
>+
>+		ret = 0;
>+	}
>+
>+	spin_unlock_irqrestore(&kcs_bmc->lock, flags);
>+
>+	return ret;
>+}
>+EXPORT_SYMBOL(kcs_bmc_ipmi_event);
>+
>+static inline struct kcs_bmc *to_kcs_bmc(struct file *filp)
>+{
>+	return container_of(filp->private_data, struct kcs_bmc, miscdev);
>+}
>+
>+static int kcs_bmc_ipmi_open(struct inode *inode, struct file *filp)
>+{
>+	struct kcs_bmc *kcs_bmc = to_kcs_bmc(filp);
>+	int ret = 0;
>+
>+	spin_lock_irq(&kcs_bmc->lock);
>+	if (!kcs_bmc->running)
>+		kcs_bmc->running = 1;
>+	else
>+		ret = -EBUSY;
>+	spin_unlock_irq(&kcs_bmc->lock);
>+
>+	return ret;
>+}
>+
>+static __poll_t kcs_bmc_ipmi_poll(struct file *filp, poll_table *wait)
>+{
>+	struct kcs_bmc *kcs_bmc = to_kcs_bmc(filp);
>+	__poll_t mask = 0;
>+
>+	poll_wait(filp, &kcs_bmc->queue, wait);
>+
>+	spin_lock_irq(&kcs_bmc->lock);
>+	if (kcs_bmc->data_in_avail)
>+		mask |= EPOLLIN;
>+	spin_unlock_irq(&kcs_bmc->lock);
>+
>+	return mask;
>+}
>+
>+static ssize_t kcs_bmc_ipmi_read(struct file *filp, char __user *buf,
>+			    size_t count, loff_t *ppos)
>+{
>+	struct kcs_bmc *kcs_bmc = to_kcs_bmc(filp);
>+	bool data_avail;
>+	size_t data_len;
>+	ssize_t ret;
>+
>+	if (!(filp->f_flags & O_NONBLOCK))
>+		wait_event_interruptible(kcs_bmc->queue,
>+					 kcs_bmc->data_in_avail);
>+
>+	mutex_lock(&kcs_bmc->mutex);
>+
>+	spin_lock_irq(&kcs_bmc->lock);
>+	data_avail = kcs_bmc->data_in_avail;
>+	if (data_avail) {
>+		data_len = kcs_bmc->data_in_idx;
>+		memcpy(kcs_bmc->kbuffer, kcs_bmc->data_in, data_len);
>+	}
>+	spin_unlock_irq(&kcs_bmc->lock);
>+
>+	if (!data_avail) {
>+		ret = -EAGAIN;
>+		goto out_unlock;
>+	}
>+
>+	if (count < data_len) {
>+		pr_err("channel=%u with too large data : %zu\n",
>+			kcs_bmc->channel, data_len);
>+
>+		spin_lock_irq(&kcs_bmc->lock);
>+		kcs_bmc_ipmi_force_abort(kcs_bmc);
>+		spin_unlock_irq(&kcs_bmc->lock);
>+
>+		ret = -EOVERFLOW;
>+		goto out_unlock;
>+	}
>+
>+	if (copy_to_user(buf, kcs_bmc->kbuffer, data_len)) {
>+		ret = -EFAULT;
>+		goto out_unlock;
>+	}
>+
>+	ret = data_len;
>+
>+	spin_lock_irq(&kcs_bmc->lock);
>+	if (kcs_bmc->phase == KCS_PHASE_WRITE_DONE) {
>+		kcs_bmc->phase = KCS_PHASE_WAIT_READ;
>+		kcs_bmc->data_in_avail = false;
>+		kcs_bmc->data_in_idx = 0;
>+	} else {
>+		ret = -EAGAIN;
>+	}
>+	spin_unlock_irq(&kcs_bmc->lock);
>+
>+out_unlock:
>+	mutex_unlock(&kcs_bmc->mutex);
>+
>+	return ret;
>+}
>+
>+static ssize_t kcs_bmc_ipmi_write(struct file *filp, const char __user *buf,
>+			     size_t count, loff_t *ppos)
>+{
>+	struct kcs_bmc *kcs_bmc = to_kcs_bmc(filp);
>+	ssize_t ret;
>+
>+	/* a minimum response size '3' : netfn + cmd + ccode */
>+	if (count < 3 || count > KCS_MSG_BUFSIZ)
>+		return -EINVAL;
>+
>+	mutex_lock(&kcs_bmc->mutex);
>+
>+	if (copy_from_user(kcs_bmc->kbuffer, buf, count)) {
>+		ret = -EFAULT;
>+		goto out_unlock;
>+	}
>+
>+	spin_lock_irq(&kcs_bmc->lock);
>+	if (kcs_bmc->phase == KCS_PHASE_WAIT_READ) {
>+		kcs_bmc->phase = KCS_PHASE_READ;
>+		kcs_bmc->data_out_idx = 1;
>+		kcs_bmc->data_out_len = count;
>+		memcpy(kcs_bmc->data_out, kcs_bmc->kbuffer, count);
>+		kcs_bmc_write_data(kcs_bmc, kcs_bmc->data_out[0]);
>+		ret = count;
>+	} else {
>+		ret = -EINVAL;
>+	}
>+	spin_unlock_irq(&kcs_bmc->lock);
>+
>+out_unlock:
>+	mutex_unlock(&kcs_bmc->mutex);
>+
>+	return ret;
>+}
>+
>+static long kcs_bmc_ipmi_ioctl(struct file *filp, unsigned int cmd,
>+			  unsigned long arg)
>+{
>+	struct kcs_bmc *kcs_bmc = to_kcs_bmc(filp);
>+	long ret = 0;
>+
>+	spin_lock_irq(&kcs_bmc->lock);
>+
>+	switch (cmd) {
>+	case IPMI_BMC_IOCTL_SET_SMS_ATN:
>+		kcs_bmc_update_status(kcs_bmc, KCS_STATUS_SMS_ATN, KCS_STATUS_SMS_ATN);
>+		break;
>+
>+	case IPMI_BMC_IOCTL_CLEAR_SMS_ATN:
>+		kcs_bmc_update_status(kcs_bmc, KCS_STATUS_SMS_ATN, 0);
>+		break;
>+
>+	case IPMI_BMC_IOCTL_FORCE_ABORT:
>+		kcs_bmc_ipmi_force_abort(kcs_bmc);
>+		break;
>+
>+	default:
>+		ret = -EINVAL;
>+		break;
>+	}
>+
>+	spin_unlock_irq(&kcs_bmc->lock);
>+
>+	return ret;
>+}
>+
>+static int kcs_bmc_ipmi_release(struct inode *inode, struct file *filp)
>+{
>+	struct kcs_bmc *kcs_bmc = to_kcs_bmc(filp);
>+
>+	spin_lock_irq(&kcs_bmc->lock);
>+	kcs_bmc->running = 0;
>+	kcs_bmc_ipmi_force_abort(kcs_bmc);
>+	spin_unlock_irq(&kcs_bmc->lock);
>+
>+	return 0;
>+}
>+
>+static const struct file_operations kcs_bmc_fops = {
>+	.owner          = THIS_MODULE,
>+	.open           = kcs_bmc_ipmi_open,
>+	.read           = kcs_bmc_ipmi_read,
>+	.write          = kcs_bmc_ipmi_write,
>+	.release        = kcs_bmc_ipmi_release,
>+	.poll           = kcs_bmc_ipmi_poll,
>+	.unlocked_ioctl = kcs_bmc_ipmi_ioctl,
>+};
>+
>+struct kcs_bmc *kcs_bmc_ipmi_alloc(struct device *dev, int sizeof_priv, u32 channel);

Here too.

>+struct kcs_bmc *kcs_bmc_ipmi_alloc(struct device *dev, int sizeof_priv, u32 channel)
>+{
>+	struct kcs_bmc *kcs_bmc;
>+
>+	kcs_bmc = devm_kzalloc(dev, sizeof(*kcs_bmc) + sizeof_priv, GFP_KERNEL);
>+	if (!kcs_bmc)
>+		return NULL;
>+
>+	spin_lock_init(&kcs_bmc->lock);
>+	kcs_bmc->channel = channel;
>+
>+	mutex_init(&kcs_bmc->mutex);
>+	init_waitqueue_head(&kcs_bmc->queue);
>+
>+	kcs_bmc->data_in = devm_kmalloc(dev, KCS_MSG_BUFSIZ, GFP_KERNEL);
>+	kcs_bmc->data_out = devm_kmalloc(dev, KCS_MSG_BUFSIZ, GFP_KERNEL);
>+	kcs_bmc->kbuffer = devm_kmalloc(dev, KCS_MSG_BUFSIZ, GFP_KERNEL);
>+
>+	kcs_bmc->miscdev.minor = MISC_DYNAMIC_MINOR;
>+	kcs_bmc->miscdev.name = devm_kasprintf(dev, GFP_KERNEL, "%s%u",
>+					       DEVICE_NAME, channel);
>+	if (!kcs_bmc->data_in || !kcs_bmc->data_out || !kcs_bmc->kbuffer ||
>+	    !kcs_bmc->miscdev.name)
>+		return NULL;
>+	kcs_bmc->miscdev.fops = &kcs_bmc_fops;
>+
>+	return kcs_bmc;
>+}
>+EXPORT_SYMBOL(kcs_bmc_ipmi_alloc);
>+
>+MODULE_LICENSE("GPL v2");
>+MODULE_AUTHOR("Haiyue Wang <haiyue.wang@linux.intel.com>");
>+MODULE_DESCRIPTION("KCS BMC to handle the IPMI request from system software");
>-- 
>2.27.0
>

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

* Re: [PATCH v2 10/21] ipmi: kcs_bmc: Turn the driver data-structures inside-out
  2021-03-19  6:27 ` [PATCH v2 10/21] ipmi: kcs_bmc: Turn the driver data-structures inside-out Andrew Jeffery
@ 2021-04-09  3:57   ` Zev Weiss
  2021-04-09  5:59     ` Andrew Jeffery
  0 siblings, 1 reply; 67+ messages in thread
From: Zev Weiss @ 2021-04-09  3:57 UTC (permalink / raw)
  To: Andrew Jeffery
  Cc: devicetree, chiawei_wang, ryan_chen, tmaimon77, minyard,
	avifishman70, venture, openbmc, linux-kernel, tali.perry1,
	linux-gpio, robh+dt, linux-arm-kernel, openipmi-developer,
	lee.jones, linus.walleij, linux-aspeed, benjaminfair

On Fri, Mar 19, 2021 at 01:27:41AM CDT, Andrew Jeffery wrote:
>Make the KCS device drivers responsible for allocating their own memory.
>
>Until now the private data for the device driver was allocated internal
>to the private data for the chardev interface. This coupling required
>the slightly awkward API of passing through the struct size for the
>driver private data to the chardev constructor, and then retrieving a
>pointer to the driver private data from the allocated chardev memory.
>
>In addition to being awkward, the arrangement prevents the
>implementation of alternative userspace interfaces as the device driver
>private data is not independent.
>
>Peel a layer off the onion and turn the data-structures inside out by
>exploiting container_of() and embedding `struct kcs_device` in the
>driver private data.
>
>Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
>---
> drivers/char/ipmi/kcs_bmc.c           | 15 +++++--
> drivers/char/ipmi/kcs_bmc.h           | 12 ++----
> drivers/char/ipmi/kcs_bmc_aspeed.c    | 60 ++++++++++++++++-----------
> drivers/char/ipmi/kcs_bmc_cdev_ipmi.c | 60 ++++++++++++++++++---------
> drivers/char/ipmi/kcs_bmc_npcm7xx.c   | 37 ++++++++++-------
> 5 files changed, 113 insertions(+), 71 deletions(-)
>
>diff --git a/drivers/char/ipmi/kcs_bmc.c b/drivers/char/ipmi/kcs_bmc.c
>index ef5c48ffe74a..709b6bdec165 100644
>--- a/drivers/char/ipmi/kcs_bmc.c
>+++ b/drivers/char/ipmi/kcs_bmc.c
>@@ -44,12 +44,19 @@ int kcs_bmc_handle_event(struct kcs_bmc *kcs_bmc)
> }
> EXPORT_SYMBOL(kcs_bmc_handle_event);
>
>-struct kcs_bmc *kcs_bmc_ipmi_alloc(struct device *dev, int sizeof_priv, u32 channel);
>-struct kcs_bmc *kcs_bmc_alloc(struct device *dev, int sizeof_priv, u32 channel)
>+int kcs_bmc_ipmi_attach_cdev(struct kcs_bmc *kcs_bmc);

Another declaration perhaps intended for kcs_bmc.h?

>+int kcs_bmc_add_device(struct kcs_bmc *kcs_bmc)
> {
>-	return kcs_bmc_ipmi_alloc(dev, sizeof_priv, channel);
>+	return kcs_bmc_ipmi_attach_cdev(kcs_bmc);
> }
>-EXPORT_SYMBOL(kcs_bmc_alloc);
>+EXPORT_SYMBOL(kcs_bmc_add_device);
>+
>+int kcs_bmc_ipmi_detach_cdev(struct kcs_bmc *kcs_bmc);

Here too.

>+int kcs_bmc_remove_device(struct kcs_bmc *kcs_bmc)
>+{
>+	return kcs_bmc_ipmi_detach_cdev(kcs_bmc);
>+}
>+EXPORT_SYMBOL(kcs_bmc_remove_device);
>
> MODULE_LICENSE("GPL v2");
> MODULE_AUTHOR("Haiyue Wang <haiyue.wang@linux.intel.com>");
>diff --git a/drivers/char/ipmi/kcs_bmc.h b/drivers/char/ipmi/kcs_bmc.h
>index febea0c8deb4..bf0ae327997f 100644
>--- a/drivers/char/ipmi/kcs_bmc.h
>+++ b/drivers/char/ipmi/kcs_bmc.h
>@@ -67,6 +67,8 @@ struct kcs_ioreg {
> };
>
> struct kcs_bmc {
>+	struct device *dev;
>+
> 	spinlock_t lock;
>
> 	u32 channel;
>@@ -94,17 +96,11 @@ struct kcs_bmc {
> 	u8 *kbuffer;
>
> 	struct miscdevice miscdev;
>-
>-	unsigned long priv[];
> };
>
>-static inline void *kcs_bmc_priv(struct kcs_bmc *kcs_bmc)
>-{
>-	return kcs_bmc->priv;
>-}
>-
> int kcs_bmc_handle_event(struct kcs_bmc *kcs_bmc);
>-struct kcs_bmc *kcs_bmc_alloc(struct device *dev, int sizeof_priv, u32 channel);
>+int kcs_bmc_add_device(struct kcs_bmc *kcs_bmc);
>+int kcs_bmc_remove_device(struct kcs_bmc *kcs_bmc);
>
> u8 kcs_bmc_read_data(struct kcs_bmc *kcs_bmc);
> void kcs_bmc_write_data(struct kcs_bmc *kcs_bmc, u8 data);
>diff --git a/drivers/char/ipmi/kcs_bmc_aspeed.c b/drivers/char/ipmi/kcs_bmc_aspeed.c
>index 630cf095560e..0416ac78ce68 100644
>--- a/drivers/char/ipmi/kcs_bmc_aspeed.c
>+++ b/drivers/char/ipmi/kcs_bmc_aspeed.c
>@@ -61,6 +61,8 @@
> #define LPC_STR4             0x11C
>
> struct aspeed_kcs_bmc {
>+	struct kcs_bmc kcs_bmc;
>+
> 	struct regmap *map;
> };
>
>@@ -69,9 +71,14 @@ struct aspeed_kcs_of_ops {
> 	int (*get_io_address)(struct platform_device *pdev);
> };
>
>+static inline struct aspeed_kcs_bmc *to_aspeed_kcs_bmc(struct kcs_bmc *kcs_bmc)
>+{
>+	return container_of(kcs_bmc, struct aspeed_kcs_bmc, kcs_bmc);
>+}
>+
> static u8 aspeed_kcs_inb(struct kcs_bmc *kcs_bmc, u32 reg)
> {
>-	struct aspeed_kcs_bmc *priv = kcs_bmc_priv(kcs_bmc);
>+	struct aspeed_kcs_bmc *priv = to_aspeed_kcs_bmc(kcs_bmc);
> 	u32 val = 0;
> 	int rc;
>
>@@ -83,7 +90,7 @@ static u8 aspeed_kcs_inb(struct kcs_bmc *kcs_bmc, u32 reg)
>
> static void aspeed_kcs_outb(struct kcs_bmc *kcs_bmc, u32 reg, u8 data)
> {
>-	struct aspeed_kcs_bmc *priv = kcs_bmc_priv(kcs_bmc);
>+	struct aspeed_kcs_bmc *priv = to_aspeed_kcs_bmc(kcs_bmc);
> 	int rc;
>
> 	rc = regmap_write(priv->map, reg, data);
>@@ -92,7 +99,7 @@ static void aspeed_kcs_outb(struct kcs_bmc *kcs_bmc, u32 reg, u8 data)
>
> static void aspeed_kcs_updateb(struct kcs_bmc *kcs_bmc, u32 reg, u8 mask, u8 val)
> {
>-	struct aspeed_kcs_bmc *priv = kcs_bmc_priv(kcs_bmc);
>+	struct aspeed_kcs_bmc *priv = to_aspeed_kcs_bmc(kcs_bmc);
> 	int rc;
>
> 	rc = regmap_update_bits(priv->map, reg, mask, val);
>@@ -114,7 +121,7 @@ static void aspeed_kcs_updateb(struct kcs_bmc *kcs_bmc, u32 reg, u8 mask, u8 val
>  */
> static void aspeed_kcs_set_address(struct kcs_bmc *kcs_bmc, u16 addr)
> {
>-	struct aspeed_kcs_bmc *priv = kcs_bmc_priv(kcs_bmc);
>+	struct aspeed_kcs_bmc *priv = to_aspeed_kcs_bmc(kcs_bmc);
>
> 	switch (kcs_bmc->channel) {
> 	case 1:
>@@ -148,7 +155,7 @@ static void aspeed_kcs_set_address(struct kcs_bmc *kcs_bmc, u16 addr)
>
> static void aspeed_kcs_enable_channel(struct kcs_bmc *kcs_bmc, bool enable)
> {
>-	struct aspeed_kcs_bmc *priv = kcs_bmc_priv(kcs_bmc);
>+	struct aspeed_kcs_bmc *priv = to_aspeed_kcs_bmc(kcs_bmc);
>
> 	switch (kcs_bmc->channel) {
> 	case 1:
>@@ -323,16 +330,16 @@ static int aspeed_kcs_of_v2_get_io_address(struct platform_device *pdev)
> static int aspeed_kcs_probe(struct platform_device *pdev)
> {
> 	const struct aspeed_kcs_of_ops *ops;
>-	struct device *dev = &pdev->dev;
>+	struct aspeed_kcs_bmc *priv;
> 	struct kcs_bmc *kcs_bmc;
> 	struct device_node *np;
> 	int rc, channel, addr;
>
>-	np = dev->of_node->parent;
>+	np = pdev->dev.of_node->parent;
> 	if (!of_device_is_compatible(np, "aspeed,ast2400-lpc-v2") &&
> 	    !of_device_is_compatible(np, "aspeed,ast2500-lpc-v2") &&
> 	    !of_device_is_compatible(np, "aspeed,ast2600-lpc-v2")) {
>-		dev_err(dev, "unsupported LPC device binding\n");
>+		dev_err(&pdev->dev, "unsupported LPC device binding\n");
> 		return -ENODEV;
> 	}
> 	ops = of_device_get_match_data(&pdev->dev);
>@@ -343,18 +350,27 @@ static int aspeed_kcs_probe(struct platform_device *pdev)
> 	if (channel < 0)
> 		return channel;
>
>-	kcs_bmc = kcs_bmc_alloc(&pdev->dev, sizeof(struct aspeed_kcs_bmc), channel);
>-	if (!kcs_bmc)
>+	addr = ops->get_io_address(pdev);
>+	if (addr < 0)
>+		return addr;
>+
>+	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
>+	if (!priv)
> 		return -ENOMEM;
>
>+	kcs_bmc = &priv->kcs_bmc;
>+	kcs_bmc->dev = &pdev->dev;
>+	kcs_bmc->channel = channel;
> 	kcs_bmc->ioreg = ast_kcs_bmc_ioregs[channel - 1];
> 	kcs_bmc->io_inputb = aspeed_kcs_inb;
> 	kcs_bmc->io_outputb = aspeed_kcs_outb;
> 	kcs_bmc->io_updateb = aspeed_kcs_updateb;
>
>-	addr = ops->get_io_address(pdev);
>-	if (addr < 0)
>-		return addr;
>+	priv->map = syscon_node_to_regmap(pdev->dev.parent->of_node);
>+	if (IS_ERR(priv->map)) {
>+		dev_err(&pdev->dev, "Couldn't get regmap\n");
>+		return -ENODEV;
>+	}

The reanimated priv->map initialization I suspect wasn't meant to
have been removed in the first place...

>
> 	aspeed_kcs_set_address(kcs_bmc, addr);
>
>@@ -362,29 +378,25 @@ static int aspeed_kcs_probe(struct platform_device *pdev)
> 	if (rc)
> 		return rc;
>
>-	dev_set_drvdata(dev, kcs_bmc);
>+	platform_set_drvdata(pdev, priv);
>
> 	aspeed_kcs_enable_channel(kcs_bmc, true);
>
>-	rc = misc_register(&kcs_bmc->miscdev);
>-	if (rc) {
>-		dev_err(dev, "Unable to register device\n");
>+	rc = kcs_bmc_add_device(&priv->kcs_bmc);
>+	if (rc < 0)
> 		return rc;
>-	}
>
>-	dev_dbg(&pdev->dev,
>-		"Probed KCS device %d (IDR=0x%x, ODR=0x%x, STR=0x%x)\n",
>-		kcs_bmc->channel, kcs_bmc->ioreg.idr, kcs_bmc->ioreg.odr,
>-		kcs_bmc->ioreg.str);
>+	dev_info(&pdev->dev, "Initialised channel %d at 0x%x\n", kcs_bmc->channel, addr);

Is the dbg->info change here intentional?  (I have no particular
objection if so, but it's often a change I make myself during
testing/debugging and then forget to revert...)

>
> 	return 0;
> }
>
> static int aspeed_kcs_remove(struct platform_device *pdev)
> {
>-	struct kcs_bmc *kcs_bmc = dev_get_drvdata(&pdev->dev);
>+	struct aspeed_kcs_bmc *priv = platform_get_drvdata(pdev);
>+	struct kcs_bmc *kcs_bmc = &priv->kcs_bmc;
>
>-	misc_deregister(&kcs_bmc->miscdev);
>+	kcs_bmc_remove_device(kcs_bmc);

Should we propagate the return value outward here?

>
> 	return 0;
> }
>diff --git a/drivers/char/ipmi/kcs_bmc_cdev_ipmi.c b/drivers/char/ipmi/kcs_bmc_cdev_ipmi.c
>index 82c77994e481..0ca71c135a1a 100644
>--- a/drivers/char/ipmi/kcs_bmc_cdev_ipmi.c
>+++ b/drivers/char/ipmi/kcs_bmc_cdev_ipmi.c
>@@ -382,7 +382,7 @@ static int kcs_bmc_ipmi_release(struct inode *inode, struct file *filp)
> 	return 0;
> }
>
>-static const struct file_operations kcs_bmc_fops = {
>+static const struct file_operations kcs_bmc_ipmi_fops = {
> 	.owner          = THIS_MODULE,
> 	.open           = kcs_bmc_ipmi_open,
> 	.read           = kcs_bmc_ipmi_read,
>@@ -392,36 +392,58 @@ static const struct file_operations kcs_bmc_fops = {
> 	.unlocked_ioctl = kcs_bmc_ipmi_ioctl,
> };
>
>-struct kcs_bmc *kcs_bmc_ipmi_alloc(struct device *dev, int sizeof_priv, u32 channel);
>-struct kcs_bmc *kcs_bmc_ipmi_alloc(struct device *dev, int sizeof_priv, u32 channel)
>+int kcs_bmc_ipmi_attach_cdev(struct kcs_bmc *kcs_bmc);

Errant declaration again?

>+int kcs_bmc_ipmi_attach_cdev(struct kcs_bmc *kcs_bmc)
> {
>-	struct kcs_bmc *kcs_bmc;
>-
>-	kcs_bmc = devm_kzalloc(dev, sizeof(*kcs_bmc) + sizeof_priv, GFP_KERNEL);
>-	if (!kcs_bmc)
>-		return NULL;
>+	int rc;
>
> 	spin_lock_init(&kcs_bmc->lock);
>-	kcs_bmc->channel = channel;
>-
> 	mutex_init(&kcs_bmc->mutex);
> 	init_waitqueue_head(&kcs_bmc->queue);
>
>-	kcs_bmc->data_in = devm_kmalloc(dev, KCS_MSG_BUFSIZ, GFP_KERNEL);
>-	kcs_bmc->data_out = devm_kmalloc(dev, KCS_MSG_BUFSIZ, GFP_KERNEL);
>-	kcs_bmc->kbuffer = devm_kmalloc(dev, KCS_MSG_BUFSIZ, GFP_KERNEL);
>+	kcs_bmc->data_in = devm_kmalloc(kcs_bmc->dev, KCS_MSG_BUFSIZ, GFP_KERNEL);
>+	kcs_bmc->data_out = devm_kmalloc(kcs_bmc->dev, KCS_MSG_BUFSIZ, GFP_KERNEL);
>+	kcs_bmc->kbuffer = devm_kmalloc(kcs_bmc->dev, KCS_MSG_BUFSIZ, GFP_KERNEL);
>
> 	kcs_bmc->miscdev.minor = MISC_DYNAMIC_MINOR;
>-	kcs_bmc->miscdev.name = devm_kasprintf(dev, GFP_KERNEL, "%s%u",
>-					       DEVICE_NAME, channel);
>+	kcs_bmc->miscdev.name = devm_kasprintf(kcs_bmc->dev, GFP_KERNEL, "%s%u",
>+					       DEVICE_NAME, kcs_bmc->channel);
> 	if (!kcs_bmc->data_in || !kcs_bmc->data_out || !kcs_bmc->kbuffer ||
> 	    !kcs_bmc->miscdev.name)
>-		return NULL;
>-	kcs_bmc->miscdev.fops = &kcs_bmc_fops;
>+		return -ENOMEM;
>
>-	return kcs_bmc;
>+	kcs_bmc->miscdev.fops = &kcs_bmc_ipmi_fops;
>+
>+	rc = misc_register(&kcs_bmc->miscdev);
>+	if (rc) {
>+		dev_err(kcs_bmc->dev, "Unable to register device: %d\n", rc);
>+		return rc;
>+	}
>+
>+	dev_info(kcs_bmc->dev, "Initialised IPMI client for channel %d", kcs_bmc->channel);
>+
>+	return 0;
>+}
>+EXPORT_SYMBOL(kcs_bmc_ipmi_attach_cdev);
>+
>+int kcs_bmc_ipmi_detach_cdev(struct kcs_bmc *kcs_bmc);

Same here.

>+int kcs_bmc_ipmi_detach_cdev(struct kcs_bmc *kcs_bmc)
>+{
>+	misc_deregister(&kcs_bmc->miscdev);
>+
>+	spin_lock_irq(&kcs_bmc->lock);
>+	kcs_bmc->running = 0;
>+	kcs_bmc_ipmi_force_abort(kcs_bmc);
>+	spin_unlock_irq(&kcs_bmc->lock);
>+
>+	devm_kfree(kcs_bmc->dev, kcs_bmc->kbuffer);
>+	devm_kfree(kcs_bmc->dev, kcs_bmc->data_out);
>+	devm_kfree(kcs_bmc->dev, kcs_bmc->data_in);
>+	devm_kfree(kcs_bmc->dev, kcs_bmc);
>+
>+	return 0;
> }
>-EXPORT_SYMBOL(kcs_bmc_ipmi_alloc);
>+EXPORT_SYMBOL(kcs_bmc_ipmi_detach_cdev);
>
> MODULE_LICENSE("GPL v2");
> MODULE_AUTHOR("Haiyue Wang <haiyue.wang@linux.intel.com>");
>diff --git a/drivers/char/ipmi/kcs_bmc_npcm7xx.c b/drivers/char/ipmi/kcs_bmc_npcm7xx.c
>index 1f44aadec9e8..5d017498dc69 100644
>--- a/drivers/char/ipmi/kcs_bmc_npcm7xx.c
>+++ b/drivers/char/ipmi/kcs_bmc_npcm7xx.c
>@@ -65,6 +65,8 @@ struct npcm7xx_kcs_reg {
> };
>
> struct npcm7xx_kcs_bmc {
>+	struct kcs_bmc kcs_bmc;
>+
> 	struct regmap *map;
>
> 	const struct npcm7xx_kcs_reg *reg;
>@@ -76,9 +78,14 @@ static const struct npcm7xx_kcs_reg npcm7xx_kcs_reg_tbl[KCS_CHANNEL_MAX] = {
> 	{ .sts = KCS3ST, .dob = KCS3DO, .dib = KCS3DI, .ctl = KCS3CTL, .ie = KCS3IE },
> };
>
>+static inline struct npcm7xx_kcs_bmc *to_npcm7xx_kcs_bmc(struct kcs_bmc *kcs_bmc)
>+{
>+	return container_of(kcs_bmc, struct npcm7xx_kcs_bmc, kcs_bmc);
>+}
>+
> static u8 npcm7xx_kcs_inb(struct kcs_bmc *kcs_bmc, u32 reg)
> {
>-	struct npcm7xx_kcs_bmc *priv = kcs_bmc_priv(kcs_bmc);
>+	struct npcm7xx_kcs_bmc *priv = to_npcm7xx_kcs_bmc(kcs_bmc);
> 	u32 val = 0;
> 	int rc;
>
>@@ -90,7 +97,7 @@ static u8 npcm7xx_kcs_inb(struct kcs_bmc *kcs_bmc, u32 reg)
>
> static void npcm7xx_kcs_outb(struct kcs_bmc *kcs_bmc, u32 reg, u8 data)
> {
>-	struct npcm7xx_kcs_bmc *priv = kcs_bmc_priv(kcs_bmc);
>+	struct npcm7xx_kcs_bmc *priv = to_npcm7xx_kcs_bmc(kcs_bmc);
> 	int rc;
>
> 	rc = regmap_write(priv->map, reg, data);
>@@ -99,7 +106,7 @@ static void npcm7xx_kcs_outb(struct kcs_bmc *kcs_bmc, u32 reg, u8 data)
>
> static void npcm7xx_kcs_updateb(struct kcs_bmc *kcs_bmc, u32 reg, u8 mask, u8 data)
> {
>-	struct npcm7xx_kcs_bmc *priv = kcs_bmc_priv(kcs_bmc);
>+	struct npcm7xx_kcs_bmc *priv = to_npcm7xx_kcs_bmc(kcs_bmc);
> 	int rc;
>
> 	rc = regmap_update_bits(priv->map, reg, mask, data);
>@@ -108,7 +115,7 @@ static void npcm7xx_kcs_updateb(struct kcs_bmc *kcs_bmc, u32 reg, u8 mask, u8 da
>
> static void npcm7xx_kcs_enable_channel(struct kcs_bmc *kcs_bmc, bool enable)
> {
>-	struct npcm7xx_kcs_bmc *priv = kcs_bmc_priv(kcs_bmc);
>+	struct npcm7xx_kcs_bmc *priv = to_npcm7xx_kcs_bmc(kcs_bmc);
>
> 	regmap_update_bits(priv->map, priv->reg->ctl, KCS_CTL_IBFIE,
> 			   enable ? KCS_CTL_IBFIE : 0);
>@@ -155,11 +162,10 @@ static int npcm7xx_kcs_probe(struct platform_device *pdev)
> 		return -ENODEV;
> 	}
>
>-	kcs_bmc = kcs_bmc_alloc(dev, sizeof(*priv), chan);
>-	if (!kcs_bmc)
>+	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
>+	if (!priv)
> 		return -ENOMEM;
>
>-	priv = kcs_bmc_priv(kcs_bmc);
> 	priv->map = syscon_node_to_regmap(dev->parent->of_node);
> 	if (IS_ERR(priv->map)) {
> 		dev_err(dev, "Couldn't get regmap\n");
>@@ -167,6 +173,9 @@ static int npcm7xx_kcs_probe(struct platform_device *pdev)
> 	}
> 	priv->reg = &npcm7xx_kcs_reg_tbl[chan - 1];
>
>+	kcs_bmc = &priv->kcs_bmc;
>+	kcs_bmc->dev = &pdev->dev;
>+	kcs_bmc->channel = chan;
> 	kcs_bmc->ioreg.idr = priv->reg->dib;
> 	kcs_bmc->ioreg.odr = priv->reg->dob;
> 	kcs_bmc->ioreg.str = priv->reg->sts;
>@@ -174,31 +183,27 @@ static int npcm7xx_kcs_probe(struct platform_device *pdev)
> 	kcs_bmc->io_outputb = npcm7xx_kcs_outb;
> 	kcs_bmc->io_updateb = npcm7xx_kcs_updateb;
>
>-	dev_set_drvdata(dev, kcs_bmc);
>+	platform_set_drvdata(pdev, priv);
>
> 	npcm7xx_kcs_enable_channel(kcs_bmc, true);
> 	rc = npcm7xx_kcs_config_irq(kcs_bmc, pdev);
> 	if (rc)
> 		return rc;
>
>-	rc = misc_register(&kcs_bmc->miscdev);
>-	if (rc) {
>-		dev_err(dev, "Unable to register device\n");
>-		return rc;
>-	}
>
> 	pr_info("channel=%u idr=0x%x odr=0x%x str=0x%x\n",
> 		chan,
> 		kcs_bmc->ioreg.idr, kcs_bmc->ioreg.odr, kcs_bmc->ioreg.str);
>
>-	return 0;
>+	return kcs_bmc_add_device(kcs_bmc);
> }
>
> static int npcm7xx_kcs_remove(struct platform_device *pdev)
> {
>-	struct kcs_bmc *kcs_bmc = dev_get_drvdata(&pdev->dev);
>+	struct npcm7xx_kcs_bmc *priv = platform_get_drvdata(pdev);
>+	struct kcs_bmc *kcs_bmc = &priv->kcs_bmc;
>
>-	misc_deregister(&kcs_bmc->miscdev);
>+	kcs_bmc_remove_device(kcs_bmc);

As with the corresponding aspeed code, should we propagate the return
value here?

>
> 	return 0;
> }
>-- 
>2.27.0
>

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

* Re: [PATCH v2 11/21] ipmi: kcs_bmc: Split headers into device and client
  2021-03-19  6:27 ` [PATCH v2 11/21] ipmi: kcs_bmc: Split headers into device and client Andrew Jeffery
@ 2021-04-09  4:01   ` Zev Weiss
  2021-04-09  6:06     ` Andrew Jeffery
  0 siblings, 1 reply; 67+ messages in thread
From: Zev Weiss @ 2021-04-09  4:01 UTC (permalink / raw)
  To: Andrew Jeffery
  Cc: devicetree, chiawei_wang, ryan_chen, tmaimon77, minyard,
	avifishman70, venture, openbmc, linux-kernel, tali.perry1,
	linux-gpio, robh+dt, linux-arm-kernel, openipmi-developer,
	lee.jones, linus.walleij, linux-aspeed, benjaminfair

On Fri, Mar 19, 2021 at 01:27:42AM CDT, Andrew Jeffery wrote:
>Strengthen the distinction between code that abstracts the
>implementation of the KCS behaviours (device drivers) and code that
>exploits KCS behaviours (clients). Neither needs to know about the APIs
>required by the other, so provide separate headers.
>
>Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
>---
> drivers/char/ipmi/kcs_bmc.c           | 21 ++++++++++-----
> drivers/char/ipmi/kcs_bmc.h           | 30 ++++++++++-----------
> drivers/char/ipmi/kcs_bmc_aspeed.c    | 20 +++++++++-----
> drivers/char/ipmi/kcs_bmc_cdev_ipmi.c | 39 ++++++++++++++++++---------
> drivers/char/ipmi/kcs_bmc_client.h    | 29 ++++++++++++++++++++
> drivers/char/ipmi/kcs_bmc_device.h    | 19 +++++++++++++
> drivers/char/ipmi/kcs_bmc_npcm7xx.c   | 20 +++++++++-----
> 7 files changed, 129 insertions(+), 49 deletions(-)
> create mode 100644 drivers/char/ipmi/kcs_bmc_client.h
> create mode 100644 drivers/char/ipmi/kcs_bmc_device.h
>
>diff --git a/drivers/char/ipmi/kcs_bmc.c b/drivers/char/ipmi/kcs_bmc.c
>index 709b6bdec165..1046ce2bbefc 100644
>--- a/drivers/char/ipmi/kcs_bmc.c
>+++ b/drivers/char/ipmi/kcs_bmc.c
>@@ -1,46 +1,52 @@
> // SPDX-License-Identifier: GPL-2.0
> /*
>  * Copyright (c) 2015-2018, Intel Corporation.
>+ * Copyright (c) 2021, IBM Corp.
>  */
>
> #include <linux/module.h>
>
> #include "kcs_bmc.h"
>
>+/* Implement both the device and client interfaces here */
>+#include "kcs_bmc_device.h"
>+#include "kcs_bmc_client.h"
>+
>+/* Consumer data access */
>+
> u8 kcs_bmc_read_data(struct kcs_bmc *kcs_bmc)
> {
>-	return kcs_bmc->io_inputb(kcs_bmc, kcs_bmc->ioreg.idr);
>+	return kcs_bmc->ops->io_inputb(kcs_bmc, kcs_bmc->ioreg.idr);
> }
> EXPORT_SYMBOL(kcs_bmc_read_data);
>
> void kcs_bmc_write_data(struct kcs_bmc *kcs_bmc, u8 data)
> {
>-	kcs_bmc->io_outputb(kcs_bmc, kcs_bmc->ioreg.odr, data);
>+	kcs_bmc->ops->io_outputb(kcs_bmc, kcs_bmc->ioreg.odr, data);
> }
> EXPORT_SYMBOL(kcs_bmc_write_data);
>
> u8 kcs_bmc_read_status(struct kcs_bmc *kcs_bmc)
> {
>-	return kcs_bmc->io_inputb(kcs_bmc, kcs_bmc->ioreg.str);
>+	return kcs_bmc->ops->io_inputb(kcs_bmc, kcs_bmc->ioreg.str);
> }
> EXPORT_SYMBOL(kcs_bmc_read_status);
>
> void kcs_bmc_write_status(struct kcs_bmc *kcs_bmc, u8 data)
> {
>-	kcs_bmc->io_outputb(kcs_bmc, kcs_bmc->ioreg.str, data);
>+	kcs_bmc->ops->io_outputb(kcs_bmc, kcs_bmc->ioreg.str, data);
> }
> EXPORT_SYMBOL(kcs_bmc_write_status);
>
> void kcs_bmc_update_status(struct kcs_bmc *kcs_bmc, u8 mask, u8 val)
> {
>-	kcs_bmc->io_updateb(kcs_bmc, kcs_bmc->ioreg.str, mask, val);
>+	kcs_bmc->ops->io_updateb(kcs_bmc, kcs_bmc->ioreg.str, mask, val);
> }
> EXPORT_SYMBOL(kcs_bmc_update_status);
>
>-int kcs_bmc_ipmi_event(struct kcs_bmc *kcs_bmc);
> int kcs_bmc_handle_event(struct kcs_bmc *kcs_bmc)
> {
>-	return kcs_bmc_ipmi_event(kcs_bmc);
>+	return kcs_bmc->client.ops->event(&kcs_bmc->client);
> }
> EXPORT_SYMBOL(kcs_bmc_handle_event);
>
>@@ -60,4 +66,5 @@ EXPORT_SYMBOL(kcs_bmc_remove_device);
>
> MODULE_LICENSE("GPL v2");
> MODULE_AUTHOR("Haiyue Wang <haiyue.wang@linux.intel.com>");
>+MODULE_AUTHOR("Andrew Jeffery <andrew@aj.id.au>");
> MODULE_DESCRIPTION("KCS BMC to handle the IPMI request from system software");
>diff --git a/drivers/char/ipmi/kcs_bmc.h b/drivers/char/ipmi/kcs_bmc.h
>index bf0ae327997f..a1350e567723 100644
>--- a/drivers/char/ipmi/kcs_bmc.h
>+++ b/drivers/char/ipmi/kcs_bmc.h
>@@ -8,6 +8,15 @@
>
> #include <linux/miscdevice.h>
>
>+#include "kcs_bmc_client.h"
>+
>+#define KCS_BMC_EVENT_NONE	0
>+#define KCS_BMC_EVENT_HANDLED	1

Is there a particular reason we're introducing these macros and using an
int return type for kcs_bmc_client_ops.event instead of just having it
be irqreturn_t?  Other event types or outcomes we're anticipating needing
to handle maybe?

>+
>+#define KCS_BMC_STR_OBF		BIT(0)
>+#define KCS_BMC_STR_IBF		BIT(1)
>+#define KCS_BMC_STR_CMD_DAT	BIT(3)

The first two of these macros are used later in the series, but the third
doesn't end up used at all I think?

>+
> /* Different phases of the KCS BMC module.
>  *  KCS_PHASE_IDLE:
>  *            BMC should not be expecting nor sending any data.
>@@ -66,19 +75,21 @@ struct kcs_ioreg {
> 	u32 str;
> };
>
>+struct kcs_bmc_device_ops;
>+
> struct kcs_bmc {
> 	struct device *dev;
>
>+	const struct kcs_bmc_device_ops *ops;
>+
>+	struct kcs_bmc_client client;
>+
> 	spinlock_t lock;
>
> 	u32 channel;
> 	int running;
>
>-	/* Setup by BMC KCS controller driver */
> 	struct kcs_ioreg ioreg;
>-	u8 (*io_inputb)(struct kcs_bmc *kcs_bmc, u32 reg);
>-	void (*io_outputb)(struct kcs_bmc *kcs_bmc, u32 reg, u8 b);
>-	void (*io_updateb)(struct kcs_bmc *kcs_bmc, u32 reg, u8 mask, u8 val);
>
> 	enum kcs_phases phase;
> 	enum kcs_errors error;
>@@ -97,15 +108,4 @@ struct kcs_bmc {
>
> 	struct miscdevice miscdev;
> };
>-
>-int kcs_bmc_handle_event(struct kcs_bmc *kcs_bmc);
>-int kcs_bmc_add_device(struct kcs_bmc *kcs_bmc);
>-int kcs_bmc_remove_device(struct kcs_bmc *kcs_bmc);
>-
>-u8 kcs_bmc_read_data(struct kcs_bmc *kcs_bmc);
>-void kcs_bmc_write_data(struct kcs_bmc *kcs_bmc, u8 data);
>-u8 kcs_bmc_read_status(struct kcs_bmc *kcs_bmc);
>-void kcs_bmc_write_status(struct kcs_bmc *kcs_bmc, u8 data);
>-void kcs_bmc_update_status(struct kcs_bmc *kcs_bmc, u8 mask, u8 val);
>-
> #endif /* __KCS_BMC_H__ */
>diff --git a/drivers/char/ipmi/kcs_bmc_aspeed.c b/drivers/char/ipmi/kcs_bmc_aspeed.c
>index 0416ac78ce68..1b313355b1c8 100644
>--- a/drivers/char/ipmi/kcs_bmc_aspeed.c
>+++ b/drivers/char/ipmi/kcs_bmc_aspeed.c
>@@ -21,7 +21,7 @@
> #include <linux/slab.h>
> #include <linux/timer.h>
>
>-#include "kcs_bmc.h"
>+#include "kcs_bmc_device.h"
>
>
> #define DEVICE_NAME     "ast-kcs-bmc"
>@@ -220,14 +220,22 @@ static void aspeed_kcs_enable_channel(struct kcs_bmc *kcs_bmc, bool enable)
> 	}
> }
>
>+static const struct kcs_bmc_device_ops aspeed_kcs_ops = {
>+	.io_inputb = aspeed_kcs_inb,
>+	.io_outputb = aspeed_kcs_outb,
>+	.io_updateb = aspeed_kcs_updateb,
>+};
>+
> static irqreturn_t aspeed_kcs_irq(int irq, void *arg)
> {
> 	struct kcs_bmc *kcs_bmc = arg;
>+	int rc;
>
>-	if (!kcs_bmc_handle_event(kcs_bmc))
>-		return IRQ_HANDLED;
>+	rc = kcs_bmc_handle_event(kcs_bmc);
>+	if (rc < 0)
>+		dev_warn(kcs_bmc->dev, "Failed to service irq: %d\n", rc);
>
>-	return IRQ_NONE;
>+	return rc == KCS_BMC_EVENT_HANDLED ? IRQ_HANDLED : IRQ_NONE;
> }
>
> static int aspeed_kcs_config_irq(struct kcs_bmc *kcs_bmc,
>@@ -362,9 +370,7 @@ static int aspeed_kcs_probe(struct platform_device *pdev)
> 	kcs_bmc->dev = &pdev->dev;
> 	kcs_bmc->channel = channel;
> 	kcs_bmc->ioreg = ast_kcs_bmc_ioregs[channel - 1];
>-	kcs_bmc->io_inputb = aspeed_kcs_inb;
>-	kcs_bmc->io_outputb = aspeed_kcs_outb;
>-	kcs_bmc->io_updateb = aspeed_kcs_updateb;
>+	kcs_bmc->ops = &aspeed_kcs_ops;
>
> 	priv->map = syscon_node_to_regmap(pdev->dev.parent->of_node);
> 	if (IS_ERR(priv->map)) {
>diff --git a/drivers/char/ipmi/kcs_bmc_cdev_ipmi.c b/drivers/char/ipmi/kcs_bmc_cdev_ipmi.c
>index 0ca71c135a1a..fd852d8abe48 100644
>--- a/drivers/char/ipmi/kcs_bmc_cdev_ipmi.c
>+++ b/drivers/char/ipmi/kcs_bmc_cdev_ipmi.c
>@@ -22,7 +22,6 @@
>
> #define KCS_ZERO_DATA     0
>
>-
> /* IPMI 2.0 - Table 9-1, KCS Interface Status Register Bits */
> #define KCS_STATUS_STATE(state) (state << 6)
> #define KCS_STATUS_STATE_MASK   GENMASK(7, 6)
>@@ -179,12 +178,19 @@ static void kcs_bmc_ipmi_handle_cmd(struct kcs_bmc *kcs_bmc)
> 	}
> }
>
>-int kcs_bmc_ipmi_event(struct kcs_bmc *kcs_bmc);
>-int kcs_bmc_ipmi_event(struct kcs_bmc *kcs_bmc)
>+static inline struct kcs_bmc *client_to_kcs_bmc(struct kcs_bmc_client *client)
> {
>+	return container_of(client, struct kcs_bmc, client);
>+}
>+
>+static int kcs_bmc_ipmi_event(struct kcs_bmc_client *client)
>+{
>+	struct kcs_bmc *kcs_bmc;
> 	unsigned long flags;
>-	int ret = -ENODATA;
> 	u8 status;
>+	int ret;
>+
>+	kcs_bmc = client_to_kcs_bmc(client);
>
> 	spin_lock_irqsave(&kcs_bmc->lock, flags);
>
>@@ -197,23 +203,28 @@ int kcs_bmc_ipmi_event(struct kcs_bmc *kcs_bmc)
> 		else
> 			kcs_bmc_ipmi_handle_data(kcs_bmc);
>
>-		ret = 0;
>+		ret = KCS_BMC_EVENT_HANDLED;
>+	} else {
>+		ret = KCS_BMC_EVENT_NONE;
> 	}
>
> 	spin_unlock_irqrestore(&kcs_bmc->lock, flags);
>
> 	return ret;
> }
>-EXPORT_SYMBOL(kcs_bmc_ipmi_event);
>
>-static inline struct kcs_bmc *to_kcs_bmc(struct file *filp)
>+static const struct kcs_bmc_client_ops kcs_bmc_ipmi_client_ops = {
>+	.event = kcs_bmc_ipmi_event,
>+};
>+
>+static inline struct kcs_bmc *file_to_kcs_bmc(struct file *filp)
> {
> 	return container_of(filp->private_data, struct kcs_bmc, miscdev);
> }
>
> static int kcs_bmc_ipmi_open(struct inode *inode, struct file *filp)
> {
>-	struct kcs_bmc *kcs_bmc = to_kcs_bmc(filp);
>+	struct kcs_bmc *kcs_bmc = file_to_kcs_bmc(filp);
> 	int ret = 0;
>
> 	spin_lock_irq(&kcs_bmc->lock);
>@@ -228,7 +239,7 @@ static int kcs_bmc_ipmi_open(struct inode *inode, struct file *filp)
>
> static __poll_t kcs_bmc_ipmi_poll(struct file *filp, poll_table *wait)
> {
>-	struct kcs_bmc *kcs_bmc = to_kcs_bmc(filp);
>+	struct kcs_bmc *kcs_bmc = file_to_kcs_bmc(filp);
> 	__poll_t mask = 0;
>
> 	poll_wait(filp, &kcs_bmc->queue, wait);
>@@ -244,7 +255,7 @@ static __poll_t kcs_bmc_ipmi_poll(struct file *filp, poll_table *wait)
> static ssize_t kcs_bmc_ipmi_read(struct file *filp, char __user *buf,
> 			    size_t count, loff_t *ppos)
> {
>-	struct kcs_bmc *kcs_bmc = to_kcs_bmc(filp);
>+	struct kcs_bmc *kcs_bmc = file_to_kcs_bmc(filp);
> 	bool data_avail;
> 	size_t data_len;
> 	ssize_t ret;
>@@ -306,7 +317,7 @@ static ssize_t kcs_bmc_ipmi_read(struct file *filp, char __user *buf,
> static ssize_t kcs_bmc_ipmi_write(struct file *filp, const char __user *buf,
> 			     size_t count, loff_t *ppos)
> {
>-	struct kcs_bmc *kcs_bmc = to_kcs_bmc(filp);
>+	struct kcs_bmc *kcs_bmc = file_to_kcs_bmc(filp);
> 	ssize_t ret;
>
> 	/* a minimum response size '3' : netfn + cmd + ccode */
>@@ -342,7 +353,7 @@ static ssize_t kcs_bmc_ipmi_write(struct file *filp, const char __user *buf,
> static long kcs_bmc_ipmi_ioctl(struct file *filp, unsigned int cmd,
> 			  unsigned long arg)
> {
>-	struct kcs_bmc *kcs_bmc = to_kcs_bmc(filp);
>+	struct kcs_bmc *kcs_bmc = file_to_kcs_bmc(filp);
> 	long ret = 0;
>
> 	spin_lock_irq(&kcs_bmc->lock);
>@@ -372,7 +383,7 @@ static long kcs_bmc_ipmi_ioctl(struct file *filp, unsigned int cmd,
>
> static int kcs_bmc_ipmi_release(struct inode *inode, struct file *filp)
> {
>-	struct kcs_bmc *kcs_bmc = to_kcs_bmc(filp);
>+	struct kcs_bmc *kcs_bmc = file_to_kcs_bmc(filp);
>
> 	spin_lock_irq(&kcs_bmc->lock);
> 	kcs_bmc->running = 0;
>@@ -401,6 +412,8 @@ int kcs_bmc_ipmi_attach_cdev(struct kcs_bmc *kcs_bmc)
> 	mutex_init(&kcs_bmc->mutex);
> 	init_waitqueue_head(&kcs_bmc->queue);
>
>+	kcs_bmc->client.dev = kcs_bmc;
>+	kcs_bmc->client.ops = &kcs_bmc_ipmi_client_ops;
> 	kcs_bmc->data_in = devm_kmalloc(kcs_bmc->dev, KCS_MSG_BUFSIZ, GFP_KERNEL);
> 	kcs_bmc->data_out = devm_kmalloc(kcs_bmc->dev, KCS_MSG_BUFSIZ, GFP_KERNEL);
> 	kcs_bmc->kbuffer = devm_kmalloc(kcs_bmc->dev, KCS_MSG_BUFSIZ, GFP_KERNEL);
>diff --git a/drivers/char/ipmi/kcs_bmc_client.h b/drivers/char/ipmi/kcs_bmc_client.h
>new file mode 100644
>index 000000000000..140631d157d8
>--- /dev/null
>+++ b/drivers/char/ipmi/kcs_bmc_client.h
>@@ -0,0 +1,29 @@
>+/* SPDX-License-Identifier: GPL-2.0 */
>+/* Copyright (c) 2021, IBM Corp. */
>+
>+#ifndef __KCS_BMC_CONSUMER_H__
>+#define __KCS_BMC_CONSUMER_H__
>+
>+#include <linux/list.h>
>+#include <linux/notifier.h>
>+#include <stdbool.h>
>+
>+struct kcs_bmc;
>+struct kcs_bmc_client_ops;
>+
>+struct kcs_bmc_client {
>+	const struct kcs_bmc_client_ops *ops;
>+
>+	struct kcs_bmc *dev;
>+};
>+
>+struct kcs_bmc_client_ops {
>+	int (*event)(struct kcs_bmc_client *client);
>+};
>+
>+u8 kcs_bmc_read_data(struct kcs_bmc *kcs_bmc);
>+void kcs_bmc_write_data(struct kcs_bmc *kcs_bmc, u8 data);
>+u8 kcs_bmc_read_status(struct kcs_bmc *kcs_bmc);
>+void kcs_bmc_write_status(struct kcs_bmc *kcs_bmc, u8 data);
>+void kcs_bmc_update_status(struct kcs_bmc *kcs_bmc, u8 mask, u8 val);
>+#endif
>diff --git a/drivers/char/ipmi/kcs_bmc_device.h b/drivers/char/ipmi/kcs_bmc_device.h
>new file mode 100644
>index 000000000000..33462174516d
>--- /dev/null
>+++ b/drivers/char/ipmi/kcs_bmc_device.h
>@@ -0,0 +1,19 @@
>+/* SPDX-License-Identifier: GPL-2.0 */
>+/* Copyright (c) 2021, IBM Corp. */
>+
>+#ifndef __KCS_BMC_DEVICE_H__
>+#define __KCS_BMC_DEVICE_H__
>+
>+#include "kcs_bmc.h"
>+
>+struct kcs_bmc_device_ops {
>+	u8 (*io_inputb)(struct kcs_bmc *kcs_bmc, u32 reg);
>+	void (*io_outputb)(struct kcs_bmc *kcs_bmc, u32 reg, u8 b);
>+	void (*io_updateb)(struct kcs_bmc *kcs_bmc, u32 reg, u8 mask, u8 b);
>+};
>+
>+int kcs_bmc_handle_event(struct kcs_bmc *kcs_bmc);
>+int kcs_bmc_add_device(struct kcs_bmc *kcs_bmc);
>+int kcs_bmc_remove_device(struct kcs_bmc *kcs_bmc);
>+
>+#endif
>diff --git a/drivers/char/ipmi/kcs_bmc_npcm7xx.c b/drivers/char/ipmi/kcs_bmc_npcm7xx.c
>index 5d017498dc69..1d21697fc585 100644
>--- a/drivers/char/ipmi/kcs_bmc_npcm7xx.c
>+++ b/drivers/char/ipmi/kcs_bmc_npcm7xx.c
>@@ -17,7 +17,7 @@
> #include <linux/regmap.h>
> #include <linux/slab.h>
>
>-#include "kcs_bmc.h"
>+#include "kcs_bmc_device.h"
>
> #define DEVICE_NAME	"npcm-kcs-bmc"
> #define KCS_CHANNEL_MAX	3
>@@ -127,11 +127,13 @@ static void npcm7xx_kcs_enable_channel(struct kcs_bmc *kcs_bmc, bool enable)
> static irqreturn_t npcm7xx_kcs_irq(int irq, void *arg)
> {
> 	struct kcs_bmc *kcs_bmc = arg;
>+	int rc;
>
>-	if (!kcs_bmc_handle_event(kcs_bmc))
>-		return IRQ_HANDLED;
>+	rc = kcs_bmc_handle_event(kcs_bmc);
>+	if (rc < 0)
>+		dev_warn(kcs_bmc->dev, "Failed to service irq: %d\n", rc);
>
>-	return IRQ_NONE;
>+	return rc == KCS_BMC_EVENT_HANDLED ? IRQ_HANDLED : IRQ_NONE;
> }
>
> static int npcm7xx_kcs_config_irq(struct kcs_bmc *kcs_bmc,
>@@ -148,6 +150,12 @@ static int npcm7xx_kcs_config_irq(struct kcs_bmc *kcs_bmc,
> 				dev_name(dev), kcs_bmc);
> }
>
>+static const struct kcs_bmc_device_ops npcm7xx_kcs_ops = {
>+	.io_inputb = npcm7xx_kcs_inb,
>+	.io_outputb = npcm7xx_kcs_outb,
>+	.io_updateb = npcm7xx_kcs_updateb,
>+};
>+
> static int npcm7xx_kcs_probe(struct platform_device *pdev)
> {
> 	struct device *dev = &pdev->dev;
>@@ -179,9 +187,7 @@ static int npcm7xx_kcs_probe(struct platform_device *pdev)
> 	kcs_bmc->ioreg.idr = priv->reg->dib;
> 	kcs_bmc->ioreg.odr = priv->reg->dob;
> 	kcs_bmc->ioreg.str = priv->reg->sts;
>-	kcs_bmc->io_inputb = npcm7xx_kcs_inb;
>-	kcs_bmc->io_outputb = npcm7xx_kcs_outb;
>-	kcs_bmc->io_updateb = npcm7xx_kcs_updateb;
>+	kcs_bmc->ops = &npcm7xx_kcs_ops;
>
> 	platform_set_drvdata(pdev, priv);
>
>-- 
>2.27.0
>

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

* Re: [PATCH v2 12/21] ipmi: kcs_bmc: Strip private client data from struct kcs_bmc
  2021-03-19  6:27 ` [PATCH v2 12/21] ipmi: kcs_bmc: Strip private client data from struct kcs_bmc Andrew Jeffery
@ 2021-04-09  4:07   ` Zev Weiss
  2021-04-09  6:15     ` Andrew Jeffery
  0 siblings, 1 reply; 67+ messages in thread
From: Zev Weiss @ 2021-04-09  4:07 UTC (permalink / raw)
  To: Andrew Jeffery
  Cc: devicetree, chiawei_wang, ryan_chen, tmaimon77, minyard,
	avifishman70, venture, openbmc, linux-kernel, tali.perry1,
	linux-gpio, robh+dt, linux-arm-kernel, openipmi-developer,
	lee.jones, linus.walleij, linux-aspeed, benjaminfair

On Fri, Mar 19, 2021 at 01:27:43AM CDT, Andrew Jeffery wrote:
>Move all client-private data out of `struct kcs_bmc` into the KCS client
>implementation.
>
>With this change the KCS BMC core code now only concerns itself with
>abstract `struct kcs_bmc` and `struct kcs_bmc_client` types, achieving
>expected separation of concerns. Further, the change clears the path for
>implementation of alternative userspace interfaces.
>
>The chardev data-structures are rearranged in the same manner applied to
>the KCS device driver data-structures in an earlier patch - `struct
>kcs_bmc_client` is embedded in the client's private data and we exploit
>container_of() to translate as required.
>
>Finally, now that it is free of client data, `struct kcs_bmc` is renamed
>to `struct kcs_bmc_device` to contrast `struct kcs_bmc_client`.
>
>Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
>---
> drivers/char/ipmi/kcs_bmc.c           |  68 +++-
> drivers/char/ipmi/kcs_bmc.h           |  86 +-----
> drivers/char/ipmi/kcs_bmc_aspeed.c    |  22 +-
> drivers/char/ipmi/kcs_bmc_cdev_ipmi.c | 428 ++++++++++++++++----------
> drivers/char/ipmi/kcs_bmc_client.h    |  28 +-
> drivers/char/ipmi/kcs_bmc_device.h    |  12 +-
> drivers/char/ipmi/kcs_bmc_npcm7xx.c   |  20 +-
> 7 files changed, 368 insertions(+), 296 deletions(-)
>
>diff --git a/drivers/char/ipmi/kcs_bmc.c b/drivers/char/ipmi/kcs_bmc.c
>index 1046ce2bbefc..266ebec71d6f 100644
>--- a/drivers/char/ipmi/kcs_bmc.c
>+++ b/drivers/char/ipmi/kcs_bmc.c
>@@ -4,6 +4,7 @@
>  * Copyright (c) 2021, IBM Corp.
>  */
>
>+#include <linux/device.h>
> #include <linux/module.h>
>
> #include "kcs_bmc.h"
>@@ -14,51 +15,96 @@
>
> /* Consumer data access */
>
>-u8 kcs_bmc_read_data(struct kcs_bmc *kcs_bmc)
>+u8 kcs_bmc_read_data(struct kcs_bmc_device *kcs_bmc)
> {
> 	return kcs_bmc->ops->io_inputb(kcs_bmc, kcs_bmc->ioreg.idr);
> }
> EXPORT_SYMBOL(kcs_bmc_read_data);
>
>-void kcs_bmc_write_data(struct kcs_bmc *kcs_bmc, u8 data)
>+void kcs_bmc_write_data(struct kcs_bmc_device *kcs_bmc, u8 data)
> {
> 	kcs_bmc->ops->io_outputb(kcs_bmc, kcs_bmc->ioreg.odr, data);
> }
> EXPORT_SYMBOL(kcs_bmc_write_data);
>
>-u8 kcs_bmc_read_status(struct kcs_bmc *kcs_bmc)
>+u8 kcs_bmc_read_status(struct kcs_bmc_device *kcs_bmc)
> {
> 	return kcs_bmc->ops->io_inputb(kcs_bmc, kcs_bmc->ioreg.str);
> }
> EXPORT_SYMBOL(kcs_bmc_read_status);
>
>-void kcs_bmc_write_status(struct kcs_bmc *kcs_bmc, u8 data)
>+void kcs_bmc_write_status(struct kcs_bmc_device *kcs_bmc, u8 data)
> {
> 	kcs_bmc->ops->io_outputb(kcs_bmc, kcs_bmc->ioreg.str, data);
> }
> EXPORT_SYMBOL(kcs_bmc_write_status);
>
>-void kcs_bmc_update_status(struct kcs_bmc *kcs_bmc, u8 mask, u8 val)
>+void kcs_bmc_update_status(struct kcs_bmc_device *kcs_bmc, u8 mask, u8 val)
> {
> 	kcs_bmc->ops->io_updateb(kcs_bmc, kcs_bmc->ioreg.str, mask, val);
> }
> EXPORT_SYMBOL(kcs_bmc_update_status);
>
>-int kcs_bmc_handle_event(struct kcs_bmc *kcs_bmc)
>+int kcs_bmc_handle_event(struct kcs_bmc_device *kcs_bmc)
> {
>-	return kcs_bmc->client.ops->event(&kcs_bmc->client);
>+	struct kcs_bmc_client *client;
>+	int rc;
>+
>+	spin_lock(&kcs_bmc->lock);
>+	client = kcs_bmc->client;
>+	if (client) {
>+		rc = client->ops->event(client);
>+	} else {
>+		u8 status;
>+
>+		status = kcs_bmc_read_status(kcs_bmc);
>+		if (status & KCS_BMC_STR_IBF) {
>+			/* Ack the event by reading the data */
>+			kcs_bmc_read_data(kcs_bmc);
>+			rc = KCS_BMC_EVENT_HANDLED;
>+		} else {
>+			rc = KCS_BMC_EVENT_NONE;
>+		}
>+	}
>+	spin_unlock(&kcs_bmc->lock);
>+
>+	return rc;
> }
> EXPORT_SYMBOL(kcs_bmc_handle_event);
>
>-int kcs_bmc_ipmi_attach_cdev(struct kcs_bmc *kcs_bmc);
>-int kcs_bmc_add_device(struct kcs_bmc *kcs_bmc)
>+int kcs_bmc_enable_device(struct kcs_bmc_device *kcs_bmc, struct kcs_bmc_client *client)
>+{
>+	int rc;
>+
>+	spin_lock_irq(&kcs_bmc->lock);
>+	if (kcs_bmc->client) {
>+		rc = -EBUSY;
>+	} else {
>+		kcs_bmc->client = client;
>+		rc = 0;
>+	}
>+	spin_unlock_irq(&kcs_bmc->lock);
>+
>+	return rc;
>+}
>+EXPORT_SYMBOL(kcs_bmc_enable_device);
>+
>+void kcs_bmc_disable_device(struct kcs_bmc_device *kcs_bmc, struct kcs_bmc_client *client)
>+{
>+	spin_lock_irq(&kcs_bmc->lock);
>+	if (client == kcs_bmc->client)
>+		kcs_bmc->client = NULL;

Is there any situation in which a non-matching client could be passed in
here?  Might we consider issuing a warning of some sort or returning an
error to the caller if so?

>+	spin_unlock_irq(&kcs_bmc->lock);
>+}
>+EXPORT_SYMBOL(kcs_bmc_disable_device);
>+
>+int kcs_bmc_add_device(struct kcs_bmc_device *kcs_bmc)
> {
> 	return kcs_bmc_ipmi_attach_cdev(kcs_bmc);
> }
> EXPORT_SYMBOL(kcs_bmc_add_device);
>
>-int kcs_bmc_ipmi_detach_cdev(struct kcs_bmc *kcs_bmc);
>-int kcs_bmc_remove_device(struct kcs_bmc *kcs_bmc)
>+int kcs_bmc_remove_device(struct kcs_bmc_device *kcs_bmc)
> {
> 	return kcs_bmc_ipmi_detach_cdev(kcs_bmc);
> }
>diff --git a/drivers/char/ipmi/kcs_bmc.h b/drivers/char/ipmi/kcs_bmc.h
>index a1350e567723..3f266740c759 100644
>--- a/drivers/char/ipmi/kcs_bmc.h
>+++ b/drivers/char/ipmi/kcs_bmc.h
>@@ -6,9 +6,7 @@
> #ifndef __KCS_BMC_H__
> #define __KCS_BMC_H__
>
>-#include <linux/miscdevice.h>
>-
>-#include "kcs_bmc_client.h"
>+#include <linux/list.h>
>
> #define KCS_BMC_EVENT_NONE	0
> #define KCS_BMC_EVENT_HANDLED	1
>@@ -17,53 +15,6 @@
> #define KCS_BMC_STR_IBF		BIT(1)
> #define KCS_BMC_STR_CMD_DAT	BIT(3)
>
>-/* Different phases of the KCS BMC module.
>- *  KCS_PHASE_IDLE:
>- *            BMC should not be expecting nor sending any data.
>- *  KCS_PHASE_WRITE_START:
>- *            BMC is receiving a WRITE_START command from system software.
>- *  KCS_PHASE_WRITE_DATA:
>- *            BMC is receiving a data byte from system software.
>- *  KCS_PHASE_WRITE_END_CMD:
>- *            BMC is waiting a last data byte from system software.
>- *  KCS_PHASE_WRITE_DONE:
>- *            BMC has received the whole request from system software.
>- *  KCS_PHASE_WAIT_READ:
>- *            BMC is waiting the response from the upper IPMI service.
>- *  KCS_PHASE_READ:
>- *            BMC is transferring the response to system software.
>- *  KCS_PHASE_ABORT_ERROR1:
>- *            BMC is waiting error status request from system software.
>- *  KCS_PHASE_ABORT_ERROR2:
>- *            BMC is waiting for idle status afer error from system software.
>- *  KCS_PHASE_ERROR:
>- *            BMC has detected a protocol violation at the interface level.
>- */
>-enum kcs_phases {
>-	KCS_PHASE_IDLE,
>-
>-	KCS_PHASE_WRITE_START,
>-	KCS_PHASE_WRITE_DATA,
>-	KCS_PHASE_WRITE_END_CMD,
>-	KCS_PHASE_WRITE_DONE,
>-
>-	KCS_PHASE_WAIT_READ,
>-	KCS_PHASE_READ,
>-
>-	KCS_PHASE_ABORT_ERROR1,
>-	KCS_PHASE_ABORT_ERROR2,
>-	KCS_PHASE_ERROR
>-};
>-
>-/* IPMI 2.0 - Table 9-4, KCS Interface Status Codes */
>-enum kcs_errors {
>-	KCS_NO_ERROR                = 0x00,
>-	KCS_ABORTED_BY_COMMAND      = 0x01,
>-	KCS_ILLEGAL_CONTROL_CODE    = 0x02,
>-	KCS_LENGTH_ERROR            = 0x06,
>-	KCS_UNSPECIFIED_ERROR       = 0xFF
>-};
>-
> /* IPMI 2.0 - 9.5, KCS Interface Registers
>  * @idr: Input Data Register
>  * @odr: Output Data Register
>@@ -76,36 +27,23 @@ struct kcs_ioreg {
> };
>
> struct kcs_bmc_device_ops;
>+struct kcs_bmc_client;
>+
>+struct kcs_bmc_device {
>+	struct list_head entry;
>
>-struct kcs_bmc {
> 	struct device *dev;
>-
>-	const struct kcs_bmc_device_ops *ops;
>-
>-	struct kcs_bmc_client client;
>-
>-	spinlock_t lock;
>-
> 	u32 channel;
>-	int running;
>
> 	struct kcs_ioreg ioreg;
>
>-	enum kcs_phases phase;
>-	enum kcs_errors error;
>+	const struct kcs_bmc_device_ops *ops;
>
>-	wait_queue_head_t queue;
>-	bool data_in_avail;
>-	int  data_in_idx;
>-	u8  *data_in;
>-
>-	int  data_out_idx;
>-	int  data_out_len;
>-	u8  *data_out;
>-
>-	struct mutex mutex;
>-	u8 *kbuffer;
>-
>-	struct miscdevice miscdev;
>+	spinlock_t lock;
>+	struct kcs_bmc_client *client;
> };
>+
>+/* Temporary exports while refactoring */
>+int kcs_bmc_ipmi_attach_cdev(struct kcs_bmc_device *kcs_bmc);
>+int kcs_bmc_ipmi_detach_cdev(struct kcs_bmc_device *kcs_bmc);
> #endif /* __KCS_BMC_H__ */
>diff --git a/drivers/char/ipmi/kcs_bmc_aspeed.c b/drivers/char/ipmi/kcs_bmc_aspeed.c
>index 1b313355b1c8..6f26e7366c0b 100644
>--- a/drivers/char/ipmi/kcs_bmc_aspeed.c
>+++ b/drivers/char/ipmi/kcs_bmc_aspeed.c
>@@ -61,7 +61,7 @@
> #define LPC_STR4             0x11C
>
> struct aspeed_kcs_bmc {
>-	struct kcs_bmc kcs_bmc;
>+	struct kcs_bmc_device kcs_bmc;
>
> 	struct regmap *map;
> };
>@@ -71,12 +71,12 @@ struct aspeed_kcs_of_ops {
> 	int (*get_io_address)(struct platform_device *pdev);
> };
>
>-static inline struct aspeed_kcs_bmc *to_aspeed_kcs_bmc(struct kcs_bmc *kcs_bmc)
>+static inline struct aspeed_kcs_bmc *to_aspeed_kcs_bmc(struct kcs_bmc_device *kcs_bmc)
> {
> 	return container_of(kcs_bmc, struct aspeed_kcs_bmc, kcs_bmc);
> }
>
>-static u8 aspeed_kcs_inb(struct kcs_bmc *kcs_bmc, u32 reg)
>+static u8 aspeed_kcs_inb(struct kcs_bmc_device *kcs_bmc, u32 reg)
> {
> 	struct aspeed_kcs_bmc *priv = to_aspeed_kcs_bmc(kcs_bmc);
> 	u32 val = 0;
>@@ -88,7 +88,7 @@ static u8 aspeed_kcs_inb(struct kcs_bmc *kcs_bmc, u32 reg)
> 	return rc == 0 ? (u8) val : 0;
> }
>
>-static void aspeed_kcs_outb(struct kcs_bmc *kcs_bmc, u32 reg, u8 data)
>+static void aspeed_kcs_outb(struct kcs_bmc_device *kcs_bmc, u32 reg, u8 data)
> {
> 	struct aspeed_kcs_bmc *priv = to_aspeed_kcs_bmc(kcs_bmc);
> 	int rc;
>@@ -97,7 +97,7 @@ static void aspeed_kcs_outb(struct kcs_bmc *kcs_bmc, u32 reg, u8 data)
> 	WARN(rc != 0, "regmap_write() failed: %d\n", rc);
> }
>
>-static void aspeed_kcs_updateb(struct kcs_bmc *kcs_bmc, u32 reg, u8 mask, u8 val)
>+static void aspeed_kcs_updateb(struct kcs_bmc_device *kcs_bmc, u32 reg, u8 mask, u8 val)
> {
> 	struct aspeed_kcs_bmc *priv = to_aspeed_kcs_bmc(kcs_bmc);
> 	int rc;
>@@ -119,7 +119,7 @@ static void aspeed_kcs_updateb(struct kcs_bmc *kcs_bmc, u32 reg, u8 mask, u8 val
>  *     C. KCS4
>  *        D / C : CA4h / CA5h
>  */
>-static void aspeed_kcs_set_address(struct kcs_bmc *kcs_bmc, u16 addr)
>+static void aspeed_kcs_set_address(struct kcs_bmc_device *kcs_bmc, u16 addr)
> {
> 	struct aspeed_kcs_bmc *priv = to_aspeed_kcs_bmc(kcs_bmc);
>
>@@ -153,7 +153,7 @@ static void aspeed_kcs_set_address(struct kcs_bmc *kcs_bmc, u16 addr)
> 	}
> }
>
>-static void aspeed_kcs_enable_channel(struct kcs_bmc *kcs_bmc, bool enable)
>+static void aspeed_kcs_enable_channel(struct kcs_bmc_device *kcs_bmc, bool enable)
> {
> 	struct aspeed_kcs_bmc *priv = to_aspeed_kcs_bmc(kcs_bmc);
>
>@@ -228,7 +228,7 @@ static const struct kcs_bmc_device_ops aspeed_kcs_ops = {
>
> static irqreturn_t aspeed_kcs_irq(int irq, void *arg)
> {
>-	struct kcs_bmc *kcs_bmc = arg;
>+	struct kcs_bmc_device *kcs_bmc = arg;
> 	int rc;
>
> 	rc = kcs_bmc_handle_event(kcs_bmc);
>@@ -238,7 +238,7 @@ static irqreturn_t aspeed_kcs_irq(int irq, void *arg)
> 	return rc == KCS_BMC_EVENT_HANDLED ? IRQ_HANDLED : IRQ_NONE;
> }
>
>-static int aspeed_kcs_config_irq(struct kcs_bmc *kcs_bmc,
>+static int aspeed_kcs_config_irq(struct kcs_bmc_device *kcs_bmc,
> 			struct platform_device *pdev)
> {
> 	struct device *dev = &pdev->dev;
>@@ -338,8 +338,8 @@ static int aspeed_kcs_of_v2_get_io_address(struct platform_device *pdev)
> static int aspeed_kcs_probe(struct platform_device *pdev)
> {
> 	const struct aspeed_kcs_of_ops *ops;
>+	struct kcs_bmc_device *kcs_bmc;
> 	struct aspeed_kcs_bmc *priv;
>-	struct kcs_bmc *kcs_bmc;
> 	struct device_node *np;
> 	int rc, channel, addr;
>
>@@ -400,7 +400,7 @@ static int aspeed_kcs_probe(struct platform_device *pdev)
> static int aspeed_kcs_remove(struct platform_device *pdev)
> {
> 	struct aspeed_kcs_bmc *priv = platform_get_drvdata(pdev);
>-	struct kcs_bmc *kcs_bmc = &priv->kcs_bmc;
>+	struct kcs_bmc_device *kcs_bmc = &priv->kcs_bmc;
>
> 	kcs_bmc_remove_device(kcs_bmc);
>
>diff --git a/drivers/char/ipmi/kcs_bmc_cdev_ipmi.c b/drivers/char/ipmi/kcs_bmc_cdev_ipmi.c
>index fd852d8abe48..58c42e76483d 100644
>--- a/drivers/char/ipmi/kcs_bmc_cdev_ipmi.c
>+++ b/drivers/char/ipmi/kcs_bmc_cdev_ipmi.c
>@@ -8,13 +8,88 @@
> #include <linux/errno.h>
> #include <linux/io.h>
> #include <linux/ipmi_bmc.h>
>+#include <linux/list.h>
>+#include <linux/miscdevice.h>
> #include <linux/module.h>
>+#include <linux/mutex.h>
> #include <linux/platform_device.h>
> #include <linux/poll.h>
> #include <linux/sched.h>
> #include <linux/slab.h>
>
>-#include "kcs_bmc.h"
>+#include "kcs_bmc_client.h"
>+
>+/* Different phases of the KCS BMC module.
>+ *  KCS_PHASE_IDLE:
>+ *            BMC should not be expecting nor sending any data.
>+ *  KCS_PHASE_WRITE_START:
>+ *            BMC is receiving a WRITE_START command from system software.
>+ *  KCS_PHASE_WRITE_DATA:
>+ *            BMC is receiving a data byte from system software.
>+ *  KCS_PHASE_WRITE_END_CMD:
>+ *            BMC is waiting a last data byte from system software.
>+ *  KCS_PHASE_WRITE_DONE:
>+ *            BMC has received the whole request from system software.
>+ *  KCS_PHASE_WAIT_READ:
>+ *            BMC is waiting the response from the upper IPMI service.
>+ *  KCS_PHASE_READ:
>+ *            BMC is transferring the response to system software.
>+ *  KCS_PHASE_ABORT_ERROR1:
>+ *            BMC is waiting error status request from system software.
>+ *  KCS_PHASE_ABORT_ERROR2:
>+ *            BMC is waiting for idle status afer error from system software.
>+ *  KCS_PHASE_ERROR:
>+ *            BMC has detected a protocol violation at the interface level.
>+ */
>+enum kcs_ipmi_phases {
>+	KCS_PHASE_IDLE,
>+
>+	KCS_PHASE_WRITE_START,
>+	KCS_PHASE_WRITE_DATA,
>+	KCS_PHASE_WRITE_END_CMD,
>+	KCS_PHASE_WRITE_DONE,
>+
>+	KCS_PHASE_WAIT_READ,
>+	KCS_PHASE_READ,
>+
>+	KCS_PHASE_ABORT_ERROR1,
>+	KCS_PHASE_ABORT_ERROR2,
>+	KCS_PHASE_ERROR
>+};
>+
>+/* IPMI 2.0 - Table 9-4, KCS Interface Status Codes */
>+enum kcs_ipmi_errors {
>+	KCS_NO_ERROR                = 0x00,
>+	KCS_ABORTED_BY_COMMAND      = 0x01,
>+	KCS_ILLEGAL_CONTROL_CODE    = 0x02,
>+	KCS_LENGTH_ERROR            = 0x06,
>+	KCS_UNSPECIFIED_ERROR       = 0xFF
>+};
>+
>+struct kcs_bmc_ipmi {
>+	struct list_head entry;
>+
>+	struct kcs_bmc_client client;
>+
>+	spinlock_t lock;
>+
>+	enum kcs_ipmi_phases phase;
>+	enum kcs_ipmi_errors error;
>+
>+	wait_queue_head_t queue;
>+	bool data_in_avail;
>+	int  data_in_idx;
>+	u8  *data_in;
>+
>+	int  data_out_idx;
>+	int  data_out_len;
>+	u8  *data_out;
>+
>+	struct mutex mutex;
>+	u8 *kbuffer;
>+
>+	struct miscdevice miscdev;
>+};
>
> #define DEVICE_NAME "ipmi-kcs"
>
>@@ -44,171 +119,169 @@ enum kcs_states {
> #define KCS_CMD_WRITE_END         0x62
> #define KCS_CMD_READ_BYTE         0x68
>
>-static inline void set_state(struct kcs_bmc *kcs_bmc, u8 state)
>+static inline void set_state(struct kcs_bmc_ipmi *priv, u8 state)
> {
>-	kcs_bmc_update_status(kcs_bmc, KCS_STATUS_STATE_MASK,
>-					KCS_STATUS_STATE(state));
>+	kcs_bmc_update_status(priv->client.dev, KCS_STATUS_STATE_MASK, KCS_STATUS_STATE(state));
> }
>
>-static void kcs_bmc_ipmi_force_abort(struct kcs_bmc *kcs_bmc)
>+static void kcs_bmc_ipmi_force_abort(struct kcs_bmc_ipmi *priv)
> {
>-	set_state(kcs_bmc, ERROR_STATE);
>-	kcs_bmc_read_data(kcs_bmc);
>-	kcs_bmc_write_data(kcs_bmc, KCS_ZERO_DATA);
>+	set_state(priv, ERROR_STATE);
>+	kcs_bmc_read_data(priv->client.dev);
>+	kcs_bmc_write_data(priv->client.dev, KCS_ZERO_DATA);
>
>-	kcs_bmc->phase = KCS_PHASE_ERROR;
>-	kcs_bmc->data_in_avail = false;
>-	kcs_bmc->data_in_idx = 0;
>+	priv->phase = KCS_PHASE_ERROR;
>+	priv->data_in_avail = false;
>+	priv->data_in_idx = 0;
> }
>
>-static void kcs_bmc_ipmi_handle_data(struct kcs_bmc *kcs_bmc)
>+static void kcs_bmc_ipmi_handle_data(struct kcs_bmc_ipmi *priv)
> {
>+	struct kcs_bmc_device *dev;
> 	u8 data;
>
>-	switch (kcs_bmc->phase) {
>+	dev = priv->client.dev;
>+
>+	switch (priv->phase) {
> 	case KCS_PHASE_WRITE_START:
>-		kcs_bmc->phase = KCS_PHASE_WRITE_DATA;
>+		priv->phase = KCS_PHASE_WRITE_DATA;
> 		fallthrough;
>
> 	case KCS_PHASE_WRITE_DATA:
>-		if (kcs_bmc->data_in_idx < KCS_MSG_BUFSIZ) {
>-			set_state(kcs_bmc, WRITE_STATE);
>-			kcs_bmc_write_data(kcs_bmc, KCS_ZERO_DATA);
>-			kcs_bmc->data_in[kcs_bmc->data_in_idx++] =
>-						kcs_bmc_read_data(kcs_bmc);
>+		if (priv->data_in_idx < KCS_MSG_BUFSIZ) {
>+			set_state(priv, WRITE_STATE);
>+			kcs_bmc_write_data(dev, KCS_ZERO_DATA);
>+			priv->data_in[priv->data_in_idx++] = kcs_bmc_read_data(dev);
> 		} else {
>-			kcs_bmc_ipmi_force_abort(kcs_bmc);
>-			kcs_bmc->error = KCS_LENGTH_ERROR;
>+			kcs_bmc_ipmi_force_abort(priv);
>+			priv->error = KCS_LENGTH_ERROR;
> 		}
> 		break;
>
> 	case KCS_PHASE_WRITE_END_CMD:
>-		if (kcs_bmc->data_in_idx < KCS_MSG_BUFSIZ) {
>-			set_state(kcs_bmc, READ_STATE);
>-			kcs_bmc->data_in[kcs_bmc->data_in_idx++] =
>-						kcs_bmc_read_data(kcs_bmc);
>-			kcs_bmc->phase = KCS_PHASE_WRITE_DONE;
>-			kcs_bmc->data_in_avail = true;
>-			wake_up_interruptible(&kcs_bmc->queue);
>+		if (priv->data_in_idx < KCS_MSG_BUFSIZ) {
>+			set_state(priv, READ_STATE);
>+			priv->data_in[priv->data_in_idx++] = kcs_bmc_read_data(dev);
>+			priv->phase = KCS_PHASE_WRITE_DONE;
>+			priv->data_in_avail = true;
>+			wake_up_interruptible(&priv->queue);
> 		} else {
>-			kcs_bmc_ipmi_force_abort(kcs_bmc);
>-			kcs_bmc->error = KCS_LENGTH_ERROR;
>+			kcs_bmc_ipmi_force_abort(priv);
>+			priv->error = KCS_LENGTH_ERROR;
> 		}
> 		break;
>
> 	case KCS_PHASE_READ:
>-		if (kcs_bmc->data_out_idx == kcs_bmc->data_out_len)
>-			set_state(kcs_bmc, IDLE_STATE);
>+		if (priv->data_out_idx == priv->data_out_len)
>+			set_state(priv, IDLE_STATE);
>
>-		data = kcs_bmc_read_data(kcs_bmc);
>+		data = kcs_bmc_read_data(dev);
> 		if (data != KCS_CMD_READ_BYTE) {
>-			set_state(kcs_bmc, ERROR_STATE);
>-			kcs_bmc_write_data(kcs_bmc, KCS_ZERO_DATA);
>+			set_state(priv, ERROR_STATE);
>+			kcs_bmc_write_data(dev, KCS_ZERO_DATA);
> 			break;
> 		}
>
>-		if (kcs_bmc->data_out_idx == kcs_bmc->data_out_len) {
>-			kcs_bmc_write_data(kcs_bmc, KCS_ZERO_DATA);
>-			kcs_bmc->phase = KCS_PHASE_IDLE;
>+		if (priv->data_out_idx == priv->data_out_len) {
>+			kcs_bmc_write_data(dev, KCS_ZERO_DATA);
>+			priv->phase = KCS_PHASE_IDLE;
> 			break;
> 		}
>
>-		kcs_bmc_write_data(kcs_bmc,
>-			kcs_bmc->data_out[kcs_bmc->data_out_idx++]);
>+		kcs_bmc_write_data(dev, priv->data_out[priv->data_out_idx++]);
> 		break;
>
> 	case KCS_PHASE_ABORT_ERROR1:
>-		set_state(kcs_bmc, READ_STATE);
>-		kcs_bmc_read_data(kcs_bmc);
>-		kcs_bmc_write_data(kcs_bmc, kcs_bmc->error);
>-		kcs_bmc->phase = KCS_PHASE_ABORT_ERROR2;
>+		set_state(priv, READ_STATE);
>+		kcs_bmc_read_data(dev);
>+		kcs_bmc_write_data(dev, priv->error);
>+		priv->phase = KCS_PHASE_ABORT_ERROR2;
> 		break;
>
> 	case KCS_PHASE_ABORT_ERROR2:
>-		set_state(kcs_bmc, IDLE_STATE);
>-		kcs_bmc_read_data(kcs_bmc);
>-		kcs_bmc_write_data(kcs_bmc, KCS_ZERO_DATA);
>-		kcs_bmc->phase = KCS_PHASE_IDLE;
>+		set_state(priv, IDLE_STATE);
>+		kcs_bmc_read_data(dev);
>+		kcs_bmc_write_data(dev, KCS_ZERO_DATA);
>+		priv->phase = KCS_PHASE_IDLE;
> 		break;
>
> 	default:
>-		kcs_bmc_ipmi_force_abort(kcs_bmc);
>+		kcs_bmc_ipmi_force_abort(priv);
> 		break;
> 	}
> }
>
>-static void kcs_bmc_ipmi_handle_cmd(struct kcs_bmc *kcs_bmc)
>+static void kcs_bmc_ipmi_handle_cmd(struct kcs_bmc_ipmi *priv)
> {
> 	u8 cmd;
>
>-	set_state(kcs_bmc, WRITE_STATE);
>-	kcs_bmc_write_data(kcs_bmc, KCS_ZERO_DATA);
>+	set_state(priv, WRITE_STATE);
>+	kcs_bmc_write_data(priv->client.dev, KCS_ZERO_DATA);
>
>-	cmd = kcs_bmc_read_data(kcs_bmc);
>+	cmd = kcs_bmc_read_data(priv->client.dev);
> 	switch (cmd) {
> 	case KCS_CMD_WRITE_START:
>-		kcs_bmc->phase = KCS_PHASE_WRITE_START;
>-		kcs_bmc->error = KCS_NO_ERROR;
>-		kcs_bmc->data_in_avail = false;
>-		kcs_bmc->data_in_idx = 0;
>+		priv->phase = KCS_PHASE_WRITE_START;
>+		priv->error = KCS_NO_ERROR;
>+		priv->data_in_avail = false;
>+		priv->data_in_idx = 0;
> 		break;
>
> 	case KCS_CMD_WRITE_END:
>-		if (kcs_bmc->phase != KCS_PHASE_WRITE_DATA) {
>-			kcs_bmc_ipmi_force_abort(kcs_bmc);
>+		if (priv->phase != KCS_PHASE_WRITE_DATA) {
>+			kcs_bmc_ipmi_force_abort(priv);
> 			break;
> 		}
>
>-		kcs_bmc->phase = KCS_PHASE_WRITE_END_CMD;
>+		priv->phase = KCS_PHASE_WRITE_END_CMD;
> 		break;
>
> 	case KCS_CMD_GET_STATUS_ABORT:
>-		if (kcs_bmc->error == KCS_NO_ERROR)
>-			kcs_bmc->error = KCS_ABORTED_BY_COMMAND;
>+		if (priv->error == KCS_NO_ERROR)
>+			priv->error = KCS_ABORTED_BY_COMMAND;
>
>-		kcs_bmc->phase = KCS_PHASE_ABORT_ERROR1;
>-		kcs_bmc->data_in_avail = false;
>-		kcs_bmc->data_in_idx = 0;
>+		priv->phase = KCS_PHASE_ABORT_ERROR1;
>+		priv->data_in_avail = false;
>+		priv->data_in_idx = 0;
> 		break;
>
> 	default:
>-		kcs_bmc_ipmi_force_abort(kcs_bmc);
>-		kcs_bmc->error = KCS_ILLEGAL_CONTROL_CODE;
>+		kcs_bmc_ipmi_force_abort(priv);
>+		priv->error = KCS_ILLEGAL_CONTROL_CODE;
> 		break;
> 	}
> }
>
>-static inline struct kcs_bmc *client_to_kcs_bmc(struct kcs_bmc_client *client)
>+static inline struct kcs_bmc_ipmi *client_to_kcs_bmc_ipmi(struct kcs_bmc_client *client)
> {
>-	return container_of(client, struct kcs_bmc, client);
>+	return container_of(client, struct kcs_bmc_ipmi, client);
> }
>
> static int kcs_bmc_ipmi_event(struct kcs_bmc_client *client)
> {
>-	struct kcs_bmc *kcs_bmc;
>-	unsigned long flags;
>+	struct kcs_bmc_ipmi *priv;
> 	u8 status;
> 	int ret;
>
>-	kcs_bmc = client_to_kcs_bmc(client);
>+	priv = client_to_kcs_bmc_ipmi(client);
>+	if (!priv)
>+		return KCS_BMC_EVENT_NONE;
>
>-	spin_lock_irqsave(&kcs_bmc->lock, flags);
>+	spin_lock(&priv->lock);
>
>-	status = kcs_bmc_read_status(kcs_bmc);
>+	status = kcs_bmc_read_status(client->dev);
> 	if (status & KCS_STATUS_IBF) {
>-		if (!kcs_bmc->running)
>-			kcs_bmc_ipmi_force_abort(kcs_bmc);
>-		else if (status & KCS_STATUS_CMD_DAT)
>-			kcs_bmc_ipmi_handle_cmd(kcs_bmc);
>+		if (status & KCS_STATUS_CMD_DAT)
>+			kcs_bmc_ipmi_handle_cmd(priv);
> 		else
>-			kcs_bmc_ipmi_handle_data(kcs_bmc);
>+			kcs_bmc_ipmi_handle_data(priv);
>
> 		ret = KCS_BMC_EVENT_HANDLED;
> 	} else {
> 		ret = KCS_BMC_EVENT_NONE;
> 	}
>
>-	spin_unlock_irqrestore(&kcs_bmc->lock, flags);
>+	spin_unlock(&priv->lock);
>
> 	return ret;
> }
>@@ -217,37 +290,29 @@ static const struct kcs_bmc_client_ops kcs_bmc_ipmi_client_ops = {
> 	.event = kcs_bmc_ipmi_event,
> };
>
>-static inline struct kcs_bmc *file_to_kcs_bmc(struct file *filp)
>+static inline struct kcs_bmc_ipmi *to_kcs_bmc(struct file *filp)
> {
>-	return container_of(filp->private_data, struct kcs_bmc, miscdev);
>+	return container_of(filp->private_data, struct kcs_bmc_ipmi, miscdev);
> }
>
> static int kcs_bmc_ipmi_open(struct inode *inode, struct file *filp)
> {
>-	struct kcs_bmc *kcs_bmc = file_to_kcs_bmc(filp);
>-	int ret = 0;
>+	struct kcs_bmc_ipmi *priv = to_kcs_bmc(filp);
>
>-	spin_lock_irq(&kcs_bmc->lock);
>-	if (!kcs_bmc->running)
>-		kcs_bmc->running = 1;
>-	else
>-		ret = -EBUSY;
>-	spin_unlock_irq(&kcs_bmc->lock);
>-
>-	return ret;
>+	return kcs_bmc_enable_device(priv->client.dev, &priv->client);
> }
>
> static __poll_t kcs_bmc_ipmi_poll(struct file *filp, poll_table *wait)
> {
>-	struct kcs_bmc *kcs_bmc = file_to_kcs_bmc(filp);
>+	struct kcs_bmc_ipmi *priv = to_kcs_bmc(filp);
> 	__poll_t mask = 0;
>
>-	poll_wait(filp, &kcs_bmc->queue, wait);
>+	poll_wait(filp, &priv->queue, wait);
>
>-	spin_lock_irq(&kcs_bmc->lock);
>-	if (kcs_bmc->data_in_avail)
>+	spin_lock_irq(&priv->lock);
>+	if (priv->data_in_avail)
> 		mask |= EPOLLIN;
>-	spin_unlock_irq(&kcs_bmc->lock);
>+	spin_unlock_irq(&priv->lock);
>
> 	return mask;
> }
>@@ -255,24 +320,24 @@ static __poll_t kcs_bmc_ipmi_poll(struct file *filp, poll_table *wait)
> static ssize_t kcs_bmc_ipmi_read(struct file *filp, char __user *buf,
> 			    size_t count, loff_t *ppos)
> {
>-	struct kcs_bmc *kcs_bmc = file_to_kcs_bmc(filp);
>+	struct kcs_bmc_ipmi *priv = to_kcs_bmc(filp);
> 	bool data_avail;
> 	size_t data_len;
> 	ssize_t ret;
>
> 	if (!(filp->f_flags & O_NONBLOCK))
>-		wait_event_interruptible(kcs_bmc->queue,
>-					 kcs_bmc->data_in_avail);
>+		wait_event_interruptible(priv->queue,
>+					 priv->data_in_avail);
>
>-	mutex_lock(&kcs_bmc->mutex);
>+	mutex_lock(&priv->mutex);
>
>-	spin_lock_irq(&kcs_bmc->lock);
>-	data_avail = kcs_bmc->data_in_avail;
>+	spin_lock_irq(&priv->lock);
>+	data_avail = priv->data_in_avail;
> 	if (data_avail) {
>-		data_len = kcs_bmc->data_in_idx;
>-		memcpy(kcs_bmc->kbuffer, kcs_bmc->data_in, data_len);
>+		data_len = priv->data_in_idx;
>+		memcpy(priv->kbuffer, priv->data_in, data_len);
> 	}
>-	spin_unlock_irq(&kcs_bmc->lock);
>+	spin_unlock_irq(&priv->lock);
>
> 	if (!data_avail) {
> 		ret = -EAGAIN;
>@@ -281,35 +346,35 @@ static ssize_t kcs_bmc_ipmi_read(struct file *filp, char __user *buf,
>
> 	if (count < data_len) {
> 		pr_err("channel=%u with too large data : %zu\n",
>-			kcs_bmc->channel, data_len);
>+			priv->client.dev->channel, data_len);
>
>-		spin_lock_irq(&kcs_bmc->lock);
>-		kcs_bmc_ipmi_force_abort(kcs_bmc);
>-		spin_unlock_irq(&kcs_bmc->lock);
>+		spin_lock_irq(&priv->lock);
>+		kcs_bmc_ipmi_force_abort(priv);
>+		spin_unlock_irq(&priv->lock);
>
> 		ret = -EOVERFLOW;
> 		goto out_unlock;
> 	}
>
>-	if (copy_to_user(buf, kcs_bmc->kbuffer, data_len)) {
>+	if (copy_to_user(buf, priv->kbuffer, data_len)) {
> 		ret = -EFAULT;
> 		goto out_unlock;
> 	}
>
> 	ret = data_len;
>
>-	spin_lock_irq(&kcs_bmc->lock);
>-	if (kcs_bmc->phase == KCS_PHASE_WRITE_DONE) {
>-		kcs_bmc->phase = KCS_PHASE_WAIT_READ;
>-		kcs_bmc->data_in_avail = false;
>-		kcs_bmc->data_in_idx = 0;
>+	spin_lock_irq(&priv->lock);
>+	if (priv->phase == KCS_PHASE_WRITE_DONE) {
>+		priv->phase = KCS_PHASE_WAIT_READ;
>+		priv->data_in_avail = false;
>+		priv->data_in_idx = 0;
> 	} else {
> 		ret = -EAGAIN;
> 	}
>-	spin_unlock_irq(&kcs_bmc->lock);
>+	spin_unlock_irq(&priv->lock);
>
> out_unlock:
>-	mutex_unlock(&kcs_bmc->mutex);
>+	mutex_unlock(&priv->mutex);
>
> 	return ret;
> }
>@@ -317,35 +382,35 @@ static ssize_t kcs_bmc_ipmi_read(struct file *filp, char __user *buf,
> static ssize_t kcs_bmc_ipmi_write(struct file *filp, const char __user *buf,
> 			     size_t count, loff_t *ppos)
> {
>-	struct kcs_bmc *kcs_bmc = file_to_kcs_bmc(filp);
>+	struct kcs_bmc_ipmi *priv = to_kcs_bmc(filp);
> 	ssize_t ret;
>
> 	/* a minimum response size '3' : netfn + cmd + ccode */
> 	if (count < 3 || count > KCS_MSG_BUFSIZ)
> 		return -EINVAL;
>
>-	mutex_lock(&kcs_bmc->mutex);
>+	mutex_lock(&priv->mutex);
>
>-	if (copy_from_user(kcs_bmc->kbuffer, buf, count)) {
>+	if (copy_from_user(priv->kbuffer, buf, count)) {
> 		ret = -EFAULT;
> 		goto out_unlock;
> 	}
>
>-	spin_lock_irq(&kcs_bmc->lock);
>-	if (kcs_bmc->phase == KCS_PHASE_WAIT_READ) {
>-		kcs_bmc->phase = KCS_PHASE_READ;
>-		kcs_bmc->data_out_idx = 1;
>-		kcs_bmc->data_out_len = count;
>-		memcpy(kcs_bmc->data_out, kcs_bmc->kbuffer, count);
>-		kcs_bmc_write_data(kcs_bmc, kcs_bmc->data_out[0]);
>+	spin_lock_irq(&priv->lock);
>+	if (priv->phase == KCS_PHASE_WAIT_READ) {
>+		priv->phase = KCS_PHASE_READ;
>+		priv->data_out_idx = 1;
>+		priv->data_out_len = count;
>+		memcpy(priv->data_out, priv->kbuffer, count);
>+		kcs_bmc_write_data(priv->client.dev, priv->data_out[0]);
> 		ret = count;
> 	} else {
> 		ret = -EINVAL;
> 	}
>-	spin_unlock_irq(&kcs_bmc->lock);
>+	spin_unlock_irq(&priv->lock);
>
> out_unlock:
>-	mutex_unlock(&kcs_bmc->mutex);
>+	mutex_unlock(&priv->mutex);
>
> 	return ret;
> }
>@@ -353,22 +418,22 @@ static ssize_t kcs_bmc_ipmi_write(struct file *filp, const char __user *buf,
> static long kcs_bmc_ipmi_ioctl(struct file *filp, unsigned int cmd,
> 			  unsigned long arg)
> {
>-	struct kcs_bmc *kcs_bmc = file_to_kcs_bmc(filp);
>+	struct kcs_bmc_ipmi *priv = to_kcs_bmc(filp);
> 	long ret = 0;
>
>-	spin_lock_irq(&kcs_bmc->lock);
>+	spin_lock_irq(&priv->lock);
>
> 	switch (cmd) {
> 	case IPMI_BMC_IOCTL_SET_SMS_ATN:
>-		kcs_bmc_update_status(kcs_bmc, KCS_STATUS_SMS_ATN, KCS_STATUS_SMS_ATN);
>+		kcs_bmc_update_status(priv->client.dev, KCS_STATUS_SMS_ATN, KCS_STATUS_SMS_ATN);
> 		break;
>
> 	case IPMI_BMC_IOCTL_CLEAR_SMS_ATN:
>-		kcs_bmc_update_status(kcs_bmc, KCS_STATUS_SMS_ATN, 0);
>+		kcs_bmc_update_status(priv->client.dev, KCS_STATUS_SMS_ATN, 0);
> 		break;
>
> 	case IPMI_BMC_IOCTL_FORCE_ABORT:
>-		kcs_bmc_ipmi_force_abort(kcs_bmc);
>+		kcs_bmc_ipmi_force_abort(priv);
> 		break;
>
> 	default:
>@@ -376,19 +441,17 @@ static long kcs_bmc_ipmi_ioctl(struct file *filp, unsigned int cmd,
> 		break;
> 	}
>
>-	spin_unlock_irq(&kcs_bmc->lock);
>+	spin_unlock_irq(&priv->lock);
>
> 	return ret;
> }
>
> static int kcs_bmc_ipmi_release(struct inode *inode, struct file *filp)
> {
>-	struct kcs_bmc *kcs_bmc = file_to_kcs_bmc(filp);
>+	struct kcs_bmc_ipmi *priv = to_kcs_bmc(filp);
>
>-	spin_lock_irq(&kcs_bmc->lock);
>-	kcs_bmc->running = 0;
>-	kcs_bmc_ipmi_force_abort(kcs_bmc);
>-	spin_unlock_irq(&kcs_bmc->lock);
>+	kcs_bmc_ipmi_force_abort(priv);
>+	kcs_bmc_disable_device(priv->client.dev, &priv->client);
>
> 	return 0;
> }
>@@ -403,56 +466,78 @@ static const struct file_operations kcs_bmc_ipmi_fops = {
> 	.unlocked_ioctl = kcs_bmc_ipmi_ioctl,
> };
>
>-int kcs_bmc_ipmi_attach_cdev(struct kcs_bmc *kcs_bmc);
>-int kcs_bmc_ipmi_attach_cdev(struct kcs_bmc *kcs_bmc)
>+static DEFINE_SPINLOCK(kcs_bmc_ipmi_instances_lock);
>+static LIST_HEAD(kcs_bmc_ipmi_instances);
>+
>+int kcs_bmc_ipmi_attach_cdev(struct kcs_bmc_device *kcs_bmc);
>+int kcs_bmc_ipmi_attach_cdev(struct kcs_bmc_device *kcs_bmc)
> {
>+	struct kcs_bmc_ipmi *priv;
> 	int rc;
>
>-	spin_lock_init(&kcs_bmc->lock);
>-	mutex_init(&kcs_bmc->mutex);
>-	init_waitqueue_head(&kcs_bmc->queue);
>-
>-	kcs_bmc->client.dev = kcs_bmc;
>-	kcs_bmc->client.ops = &kcs_bmc_ipmi_client_ops;
>-	kcs_bmc->data_in = devm_kmalloc(kcs_bmc->dev, KCS_MSG_BUFSIZ, GFP_KERNEL);
>-	kcs_bmc->data_out = devm_kmalloc(kcs_bmc->dev, KCS_MSG_BUFSIZ, GFP_KERNEL);
>-	kcs_bmc->kbuffer = devm_kmalloc(kcs_bmc->dev, KCS_MSG_BUFSIZ, GFP_KERNEL);
>-
>-	kcs_bmc->miscdev.minor = MISC_DYNAMIC_MINOR;
>-	kcs_bmc->miscdev.name = devm_kasprintf(kcs_bmc->dev, GFP_KERNEL, "%s%u",
>-					       DEVICE_NAME, kcs_bmc->channel);
>-	if (!kcs_bmc->data_in || !kcs_bmc->data_out || !kcs_bmc->kbuffer ||
>-	    !kcs_bmc->miscdev.name)
>+	priv = devm_kzalloc(kcs_bmc->dev, sizeof(*priv), GFP_KERNEL);
>+	if (!priv)
> 		return -ENOMEM;
>
>-	kcs_bmc->miscdev.fops = &kcs_bmc_ipmi_fops;
>+	spin_lock_init(&priv->lock);
>+	mutex_init(&priv->mutex);
>
>-	rc = misc_register(&kcs_bmc->miscdev);
>+	init_waitqueue_head(&priv->queue);
>+
>+	priv->client.dev = kcs_bmc;
>+	priv->client.ops = &kcs_bmc_ipmi_client_ops;
>+	priv->data_in = devm_kmalloc(kcs_bmc->dev, KCS_MSG_BUFSIZ, GFP_KERNEL);
>+	priv->data_out = devm_kmalloc(kcs_bmc->dev, KCS_MSG_BUFSIZ, GFP_KERNEL);
>+	priv->kbuffer = devm_kmalloc(kcs_bmc->dev, KCS_MSG_BUFSIZ, GFP_KERNEL);
>+
>+	priv->miscdev.minor = MISC_DYNAMIC_MINOR;
>+	priv->miscdev.name = devm_kasprintf(kcs_bmc->dev, GFP_KERNEL, "%s%u", DEVICE_NAME,
>+					   kcs_bmc->channel);
>+	if (!priv->data_in || !priv->data_out || !priv->kbuffer || !priv->miscdev.name)
>+		return -EINVAL;
>+
>+	priv->miscdev.fops = &kcs_bmc_ipmi_fops;
>+
>+	rc = misc_register(&priv->miscdev);
> 	if (rc) {
> 		dev_err(kcs_bmc->dev, "Unable to register device: %d\n", rc);
> 		return rc;
> 	}
>
>+	spin_lock_irq(&kcs_bmc_ipmi_instances_lock);
>+	list_add(&priv->entry, &kcs_bmc_ipmi_instances);
>+	spin_unlock_irq(&kcs_bmc_ipmi_instances_lock);
>+
> 	dev_info(kcs_bmc->dev, "Initialised IPMI client for channel %d", kcs_bmc->channel);
>
> 	return 0;
> }
> EXPORT_SYMBOL(kcs_bmc_ipmi_attach_cdev);
>
>-int kcs_bmc_ipmi_detach_cdev(struct kcs_bmc *kcs_bmc);
>-int kcs_bmc_ipmi_detach_cdev(struct kcs_bmc *kcs_bmc)
>+int kcs_bmc_ipmi_detach_cdev(struct kcs_bmc_device *kcs_bmc);
>+int kcs_bmc_ipmi_detach_cdev(struct kcs_bmc_device *kcs_bmc)
> {
>-	misc_deregister(&kcs_bmc->miscdev);
>+	struct kcs_bmc_ipmi *priv = NULL, *pos;
>
>-	spin_lock_irq(&kcs_bmc->lock);
>-	kcs_bmc->running = 0;
>-	kcs_bmc_ipmi_force_abort(kcs_bmc);
>-	spin_unlock_irq(&kcs_bmc->lock);
>+	spin_lock_irq(&kcs_bmc_ipmi_instances_lock);
>+	list_for_each_entry(pos, &kcs_bmc_ipmi_instances, entry) {
>+		if (pos->client.dev == kcs_bmc) {
>+			priv = pos;
>+			list_del(&pos->entry);
>+			break;
>+		}
>+	}
>+	spin_unlock_irq(&kcs_bmc_ipmi_instances_lock);
>
>-	devm_kfree(kcs_bmc->dev, kcs_bmc->kbuffer);
>-	devm_kfree(kcs_bmc->dev, kcs_bmc->data_out);
>-	devm_kfree(kcs_bmc->dev, kcs_bmc->data_in);
>-	devm_kfree(kcs_bmc->dev, kcs_bmc);
>+	if (!priv)
>+		return 0;

Would -ENOENT or something be appropriate here if the thing we're trying
to detach isn't found?

>+
>+	misc_deregister(&priv->miscdev);
>+	kcs_bmc_disable_device(priv->client.dev, &priv->client);
>+	devm_kfree(kcs_bmc->dev, priv->kbuffer);
>+	devm_kfree(kcs_bmc->dev, priv->data_out);
>+	devm_kfree(kcs_bmc->dev, priv->data_in);
>+	devm_kfree(kcs_bmc->dev, priv);
>
> 	return 0;
> }
>@@ -460,4 +545,5 @@ EXPORT_SYMBOL(kcs_bmc_ipmi_detach_cdev);
>
> MODULE_LICENSE("GPL v2");
> MODULE_AUTHOR("Haiyue Wang <haiyue.wang@linux.intel.com>");
>+MODULE_AUTHOR("Andrew Jeffery <andrew@aj.id.au>");
> MODULE_DESCRIPTION("KCS BMC to handle the IPMI request from system software");
>diff --git a/drivers/char/ipmi/kcs_bmc_client.h b/drivers/char/ipmi/kcs_bmc_client.h
>index 140631d157d8..2dd710f4b4aa 100644
>--- a/drivers/char/ipmi/kcs_bmc_client.h
>+++ b/drivers/char/ipmi/kcs_bmc_client.h
>@@ -8,22 +8,24 @@
> #include <linux/notifier.h>
> #include <stdbool.h>
>
>-struct kcs_bmc;
>-struct kcs_bmc_client_ops;
>-
>-struct kcs_bmc_client {
>-	const struct kcs_bmc_client_ops *ops;
>-
>-	struct kcs_bmc *dev;
>-};
>+#include "kcs_bmc.h"
>
> struct kcs_bmc_client_ops {
> 	int (*event)(struct kcs_bmc_client *client);
> };
>
>-u8 kcs_bmc_read_data(struct kcs_bmc *kcs_bmc);
>-void kcs_bmc_write_data(struct kcs_bmc *kcs_bmc, u8 data);
>-u8 kcs_bmc_read_status(struct kcs_bmc *kcs_bmc);
>-void kcs_bmc_write_status(struct kcs_bmc *kcs_bmc, u8 data);
>-void kcs_bmc_update_status(struct kcs_bmc *kcs_bmc, u8 mask, u8 val);
>+struct kcs_bmc_client {
>+	const struct kcs_bmc_client_ops *ops;
>+
>+	struct kcs_bmc_device *dev;
>+};
>+
>+int kcs_bmc_enable_device(struct kcs_bmc_device *kcs_bmc, struct kcs_bmc_client *client);
>+void kcs_bmc_disable_device(struct kcs_bmc_device *kcs_bmc, struct kcs_bmc_client *client);
>+
>+u8 kcs_bmc_read_data(struct kcs_bmc_device *kcs_bmc);
>+void kcs_bmc_write_data(struct kcs_bmc_device *kcs_bmc, u8 data);
>+u8 kcs_bmc_read_status(struct kcs_bmc_device *kcs_bmc);
>+void kcs_bmc_write_status(struct kcs_bmc_device *kcs_bmc, u8 data);
>+void kcs_bmc_update_status(struct kcs_bmc_device *kcs_bmc, u8 mask, u8 val);
> #endif
>diff --git a/drivers/char/ipmi/kcs_bmc_device.h b/drivers/char/ipmi/kcs_bmc_device.h
>index 33462174516d..57b7174b2bac 100644
>--- a/drivers/char/ipmi/kcs_bmc_device.h
>+++ b/drivers/char/ipmi/kcs_bmc_device.h
>@@ -7,13 +7,13 @@
> #include "kcs_bmc.h"
>
> struct kcs_bmc_device_ops {
>-	u8 (*io_inputb)(struct kcs_bmc *kcs_bmc, u32 reg);
>-	void (*io_outputb)(struct kcs_bmc *kcs_bmc, u32 reg, u8 b);
>-	void (*io_updateb)(struct kcs_bmc *kcs_bmc, u32 reg, u8 mask, u8 b);
>+	u8 (*io_inputb)(struct kcs_bmc_device *kcs_bmc, u32 reg);
>+	void (*io_outputb)(struct kcs_bmc_device *kcs_bmc, u32 reg, u8 b);
>+	void (*io_updateb)(struct kcs_bmc_device *kcs_bmc, u32 reg, u8 mask, u8 b);
> };
>
>-int kcs_bmc_handle_event(struct kcs_bmc *kcs_bmc);
>-int kcs_bmc_add_device(struct kcs_bmc *kcs_bmc);
>-int kcs_bmc_remove_device(struct kcs_bmc *kcs_bmc);
>+int kcs_bmc_handle_event(struct kcs_bmc_device *kcs_bmc);
>+int kcs_bmc_add_device(struct kcs_bmc_device *kcs_bmc);
>+int kcs_bmc_remove_device(struct kcs_bmc_device *kcs_bmc);
>
> #endif
>diff --git a/drivers/char/ipmi/kcs_bmc_npcm7xx.c b/drivers/char/ipmi/kcs_bmc_npcm7xx.c
>index 1d21697fc585..dce93ec895fc 100644
>--- a/drivers/char/ipmi/kcs_bmc_npcm7xx.c
>+++ b/drivers/char/ipmi/kcs_bmc_npcm7xx.c
>@@ -65,7 +65,7 @@ struct npcm7xx_kcs_reg {
> };
>
> struct npcm7xx_kcs_bmc {
>-	struct kcs_bmc kcs_bmc;
>+	struct kcs_bmc_device kcs_bmc;
>
> 	struct regmap *map;
>
>@@ -78,12 +78,12 @@ static const struct npcm7xx_kcs_reg npcm7xx_kcs_reg_tbl[KCS_CHANNEL_MAX] = {
> 	{ .sts = KCS3ST, .dob = KCS3DO, .dib = KCS3DI, .ctl = KCS3CTL, .ie = KCS3IE },
> };
>
>-static inline struct npcm7xx_kcs_bmc *to_npcm7xx_kcs_bmc(struct kcs_bmc *kcs_bmc)
>+static inline struct npcm7xx_kcs_bmc *to_npcm7xx_kcs_bmc(struct kcs_bmc_device *kcs_bmc)
> {
> 	return container_of(kcs_bmc, struct npcm7xx_kcs_bmc, kcs_bmc);
> }
>
>-static u8 npcm7xx_kcs_inb(struct kcs_bmc *kcs_bmc, u32 reg)
>+static u8 npcm7xx_kcs_inb(struct kcs_bmc_device *kcs_bmc, u32 reg)
> {
> 	struct npcm7xx_kcs_bmc *priv = to_npcm7xx_kcs_bmc(kcs_bmc);
> 	u32 val = 0;
>@@ -95,7 +95,7 @@ static u8 npcm7xx_kcs_inb(struct kcs_bmc *kcs_bmc, u32 reg)
> 	return rc == 0 ? (u8)val : 0;
> }
>
>-static void npcm7xx_kcs_outb(struct kcs_bmc *kcs_bmc, u32 reg, u8 data)
>+static void npcm7xx_kcs_outb(struct kcs_bmc_device *kcs_bmc, u32 reg, u8 data)
> {
> 	struct npcm7xx_kcs_bmc *priv = to_npcm7xx_kcs_bmc(kcs_bmc);
> 	int rc;
>@@ -104,7 +104,7 @@ static void npcm7xx_kcs_outb(struct kcs_bmc *kcs_bmc, u32 reg, u8 data)
> 	WARN(rc != 0, "regmap_write() failed: %d\n", rc);
> }
>
>-static void npcm7xx_kcs_updateb(struct kcs_bmc *kcs_bmc, u32 reg, u8 mask, u8 data)
>+static void npcm7xx_kcs_updateb(struct kcs_bmc_device *kcs_bmc, u32 reg, u8 mask, u8 data)
> {
> 	struct npcm7xx_kcs_bmc *priv = to_npcm7xx_kcs_bmc(kcs_bmc);
> 	int rc;
>@@ -113,7 +113,7 @@ static void npcm7xx_kcs_updateb(struct kcs_bmc *kcs_bmc, u32 reg, u8 mask, u8 da
> 	WARN(rc != 0, "regmap_update_bits() failed: %d\n", rc);
> }
>
>-static void npcm7xx_kcs_enable_channel(struct kcs_bmc *kcs_bmc, bool enable)
>+static void npcm7xx_kcs_enable_channel(struct kcs_bmc_device *kcs_bmc, bool enable)
> {
> 	struct npcm7xx_kcs_bmc *priv = to_npcm7xx_kcs_bmc(kcs_bmc);
>
>@@ -126,7 +126,7 @@ static void npcm7xx_kcs_enable_channel(struct kcs_bmc *kcs_bmc, bool enable)
>
> static irqreturn_t npcm7xx_kcs_irq(int irq, void *arg)
> {
>-	struct kcs_bmc *kcs_bmc = arg;
>+	struct kcs_bmc_device *kcs_bmc = arg;
> 	int rc;
>
> 	rc = kcs_bmc_handle_event(kcs_bmc);
>@@ -136,7 +136,7 @@ static irqreturn_t npcm7xx_kcs_irq(int irq, void *arg)
> 	return rc == KCS_BMC_EVENT_HANDLED ? IRQ_HANDLED : IRQ_NONE;
> }
>
>-static int npcm7xx_kcs_config_irq(struct kcs_bmc *kcs_bmc,
>+static int npcm7xx_kcs_config_irq(struct kcs_bmc_device *kcs_bmc,
> 				  struct platform_device *pdev)
> {
> 	struct device *dev = &pdev->dev;
>@@ -160,7 +160,7 @@ static int npcm7xx_kcs_probe(struct platform_device *pdev)
> {
> 	struct device *dev = &pdev->dev;
> 	struct npcm7xx_kcs_bmc *priv;
>-	struct kcs_bmc *kcs_bmc;
>+	struct kcs_bmc_device *kcs_bmc;
> 	u32 chan;
> 	int rc;
>
>@@ -207,7 +207,7 @@ static int npcm7xx_kcs_probe(struct platform_device *pdev)
> static int npcm7xx_kcs_remove(struct platform_device *pdev)
> {
> 	struct npcm7xx_kcs_bmc *priv = platform_get_drvdata(pdev);
>-	struct kcs_bmc *kcs_bmc = &priv->kcs_bmc;
>+	struct kcs_bmc_device *kcs_bmc = &priv->kcs_bmc;
>
> 	kcs_bmc_remove_device(kcs_bmc);
>
>-- 
>2.27.0
>

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

* Re: [PATCH v2 13/21] ipmi: kcs_bmc: Decouple the IPMI chardev from the core
  2021-03-19  6:27 ` [PATCH v2 13/21] ipmi: kcs_bmc: Decouple the IPMI chardev from the core Andrew Jeffery
  2021-04-06  6:07   ` ChiaWei Wang
@ 2021-04-09  4:35   ` Zev Weiss
  2021-04-09  6:24     ` Andrew Jeffery
  1 sibling, 1 reply; 67+ messages in thread
From: Zev Weiss @ 2021-04-09  4:35 UTC (permalink / raw)
  To: Andrew Jeffery
  Cc: devicetree, chiawei_wang, ryan_chen, tmaimon77, minyard,
	avifishman70, venture, openbmc, linux-kernel, tali.perry1,
	linux-gpio, robh+dt, linux-arm-kernel, openipmi-developer,
	lee.jones, linus.walleij, linux-aspeed, benjaminfair

On Fri, Mar 19, 2021 at 01:27:44AM CDT, Andrew Jeffery wrote:
>Now that we have untangled the data-structures, split the userspace
>interface out into its own module. Userspace interfaces and drivers are
>registered to the KCS BMC core to support arbitrary binding of either.
>
>Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
>---
> drivers/char/ipmi/Kconfig             | 13 +++++
> drivers/char/ipmi/Makefile            |  3 +-
> drivers/char/ipmi/kcs_bmc.c           | 78 ++++++++++++++++++++++++++-
> drivers/char/ipmi/kcs_bmc.h           |  4 --
> drivers/char/ipmi/kcs_bmc_cdev_ipmi.c | 33 +++++++++---
> drivers/char/ipmi/kcs_bmc_client.h    | 14 +++++
> 6 files changed, 132 insertions(+), 13 deletions(-)
>
>diff --git a/drivers/char/ipmi/Kconfig b/drivers/char/ipmi/Kconfig
>index 07847d9a459a..bc5f81899b62 100644
>--- a/drivers/char/ipmi/Kconfig
>+++ b/drivers/char/ipmi/Kconfig
>@@ -124,6 +124,19 @@ config NPCM7XX_KCS_IPMI_BMC
> 	  This support is also available as a module.  If so, the module
> 	  will be called kcs_bmc_npcm7xx.
>
>+config IPMI_KCS_BMC_CDEV_IPMI
>+	depends on IPMI_KCS_BMC
>+	tristate "IPMI character device interface for BMC KCS devices"
>+	help
>+	  Provides a BMC-side character device implementing IPMI
>+	  semantics for KCS IPMI devices.
>+
>+	  Say YES if you wish to expose KCS devices on the BMC for IPMI
>+	  purposes.
>+
>+	  This support is also available as a module. The module will be
>+	  called kcs_bmc_cdev_ipmi.
>+
> config ASPEED_BT_IPMI_BMC
> 	depends on ARCH_ASPEED || COMPILE_TEST
> 	depends on REGMAP && REGMAP_MMIO && MFD_SYSCON
>diff --git a/drivers/char/ipmi/Makefile b/drivers/char/ipmi/Makefile
>index a302bc865370..fcfa676afddb 100644
>--- a/drivers/char/ipmi/Makefile
>+++ b/drivers/char/ipmi/Makefile
>@@ -22,7 +22,8 @@ obj-$(CONFIG_IPMI_SSIF) += ipmi_ssif.o
> obj-$(CONFIG_IPMI_POWERNV) += ipmi_powernv.o
> obj-$(CONFIG_IPMI_WATCHDOG) += ipmi_watchdog.o
> obj-$(CONFIG_IPMI_POWEROFF) += ipmi_poweroff.o
>-obj-$(CONFIG_IPMI_KCS_BMC) += kcs_bmc.o kcs_bmc_cdev_ipmi.o
>+obj-$(CONFIG_IPMI_KCS_BMC) += kcs_bmc.o
>+obj-$(CONFIG_IPMI_KCS_BMC_CDEV_IPMI) += kcs_bmc_cdev_ipmi.o
> obj-$(CONFIG_ASPEED_BT_IPMI_BMC) += bt-bmc.o
> obj-$(CONFIG_ASPEED_KCS_IPMI_BMC) += kcs_bmc_aspeed.o
> obj-$(CONFIG_NPCM7XX_KCS_IPMI_BMC) += kcs_bmc_npcm7xx.o
>diff --git a/drivers/char/ipmi/kcs_bmc.c b/drivers/char/ipmi/kcs_bmc.c
>index 266ebec71d6f..694db6ee2a92 100644
>--- a/drivers/char/ipmi/kcs_bmc.c
>+++ b/drivers/char/ipmi/kcs_bmc.c
>@@ -5,7 +5,9 @@
>  */
>
> #include <linux/device.h>
>+#include <linux/list.h>
> #include <linux/module.h>
>+#include <linux/mutex.h>
>
> #include "kcs_bmc.h"
>
>@@ -13,6 +15,11 @@
> #include "kcs_bmc_device.h"
> #include "kcs_bmc_client.h"
>
>+/* Record probed devices and cdevs */
>+static DEFINE_MUTEX(kcs_bmc_lock);
>+static LIST_HEAD(kcs_bmc_devices);
>+static LIST_HEAD(kcs_bmc_cdevs);
>+
> /* Consumer data access */
>
> u8 kcs_bmc_read_data(struct kcs_bmc_device *kcs_bmc)
>@@ -100,16 +107,83 @@ EXPORT_SYMBOL(kcs_bmc_disable_device);
>
> int kcs_bmc_add_device(struct kcs_bmc_device *kcs_bmc)
> {
>-	return kcs_bmc_ipmi_attach_cdev(kcs_bmc);
>+	struct kcs_bmc_cdev *cdev;
>+	int rc;
>+
>+	spin_lock_init(&kcs_bmc->lock);
>+	kcs_bmc->client = NULL;
>+
>+	mutex_lock(&kcs_bmc_lock);
>+	list_add(&kcs_bmc->entry, &kcs_bmc_devices);
>+	list_for_each_entry(cdev, &kcs_bmc_cdevs, entry) {
>+		rc = cdev->ops->add_device(kcs_bmc);
>+		if (rc)
>+			dev_err(kcs_bmc->dev, "Failed to add chardev for KCS channel %d: %d",
>+				kcs_bmc->channel, rc);
>+	}
>+	mutex_unlock(&kcs_bmc_lock);
>+
>+	return 0;

We're ignoring failed ->add_device() calls here?

> }
> EXPORT_SYMBOL(kcs_bmc_add_device);
>
> int kcs_bmc_remove_device(struct kcs_bmc_device *kcs_bmc)
> {
>-	return kcs_bmc_ipmi_detach_cdev(kcs_bmc);
>+	struct kcs_bmc_cdev *cdev;
>+	int rc;
>+
>+	mutex_lock(&kcs_bmc_lock);
>+	list_del(&kcs_bmc->entry);
>+	list_for_each_entry(cdev, &kcs_bmc_cdevs, entry) {
>+		rc = cdev->ops->remove_device(kcs_bmc);
>+		if (rc)
>+			dev_err(kcs_bmc->dev, "Failed to remove chardev for KCS channel %d: %d",
>+				kcs_bmc->channel, rc);
>+	}
>+	mutex_unlock(&kcs_bmc_lock);
>+
>+	return 0;

Similarly with the return value here...

> }
> EXPORT_SYMBOL(kcs_bmc_remove_device);
>
>+int kcs_bmc_register_cdev(struct kcs_bmc_cdev *cdev)
>+{
>+	struct kcs_bmc_device *kcs_bmc;
>+	int rc;
>+
>+	mutex_lock(&kcs_bmc_lock);
>+	list_add(&cdev->entry, &kcs_bmc_cdevs);
>+	list_for_each_entry(kcs_bmc, &kcs_bmc_devices, entry) {
>+		rc = cdev->ops->add_device(kcs_bmc);
>+		if (rc)
>+			dev_err(kcs_bmc->dev, "Failed to add chardev for KCS channel %d: %d",
>+				kcs_bmc->channel, rc);
>+	}
>+	mutex_unlock(&kcs_bmc_lock);
>+
>+	return 0;

...return value again here...

>+}
>+EXPORT_SYMBOL(kcs_bmc_register_cdev);
>+
>+int kcs_bmc_unregister_cdev(struct kcs_bmc_cdev *cdev)
>+{
>+	struct kcs_bmc_device *kcs_bmc;
>+	int rc;
>+
>+	mutex_lock(&kcs_bmc_lock);
>+	list_del(&cdev->entry);
>+	list_for_each_entry(kcs_bmc, &kcs_bmc_devices, entry) {
>+		rc = cdev->ops->remove_device(kcs_bmc);
>+		if (rc)
>+			dev_err(kcs_bmc->dev, "Failed to add chardev for KCS channel %d: %d",

s/add/remove/

Might also want to differentiate the *_device() error messages from the
*_cdev() ones a bit more?

>+				kcs_bmc->channel, rc);
>+	}
>+	mutex_unlock(&kcs_bmc_lock);
>+
>+	return rc;

...but this one is a bit incongruous, propagating the return value of
only the last ->remove_device() call.

(I'd have expected this to trigger a warning about returning a
potentially uninitialized 'rc', but in some manual testing it doesn't
seem to do so for me...not certain why.)

>+}
>+EXPORT_SYMBOL(kcs_bmc_unregister_cdev);
>+
> MODULE_LICENSE("GPL v2");
> MODULE_AUTHOR("Haiyue Wang <haiyue.wang@linux.intel.com>");
> MODULE_AUTHOR("Andrew Jeffery <andrew@aj.id.au>");
>diff --git a/drivers/char/ipmi/kcs_bmc.h b/drivers/char/ipmi/kcs_bmc.h
>index 3f266740c759..5deb9a0b8e60 100644
>--- a/drivers/char/ipmi/kcs_bmc.h
>+++ b/drivers/char/ipmi/kcs_bmc.h
>@@ -42,8 +42,4 @@ struct kcs_bmc_device {
> 	spinlock_t lock;
> 	struct kcs_bmc_client *client;
> };
>-
>-/* Temporary exports while refactoring */
>-int kcs_bmc_ipmi_attach_cdev(struct kcs_bmc_device *kcs_bmc);
>-int kcs_bmc_ipmi_detach_cdev(struct kcs_bmc_device *kcs_bmc);
> #endif /* __KCS_BMC_H__ */
>diff --git a/drivers/char/ipmi/kcs_bmc_cdev_ipmi.c b/drivers/char/ipmi/kcs_bmc_cdev_ipmi.c
>index 58c42e76483d..df83d67851ac 100644
>--- a/drivers/char/ipmi/kcs_bmc_cdev_ipmi.c
>+++ b/drivers/char/ipmi/kcs_bmc_cdev_ipmi.c
>@@ -469,8 +469,7 @@ static const struct file_operations kcs_bmc_ipmi_fops = {
> static DEFINE_SPINLOCK(kcs_bmc_ipmi_instances_lock);
> static LIST_HEAD(kcs_bmc_ipmi_instances);
>
>-int kcs_bmc_ipmi_attach_cdev(struct kcs_bmc_device *kcs_bmc);
>-int kcs_bmc_ipmi_attach_cdev(struct kcs_bmc_device *kcs_bmc)
>+static int kcs_bmc_ipmi_attach_cdev(struct kcs_bmc_device *kcs_bmc)
> {
> 	struct kcs_bmc_ipmi *priv;
> 	int rc;
>@@ -512,10 +511,8 @@ int kcs_bmc_ipmi_attach_cdev(struct kcs_bmc_device *kcs_bmc)
>
> 	return 0;
> }
>-EXPORT_SYMBOL(kcs_bmc_ipmi_attach_cdev);
>
>-int kcs_bmc_ipmi_detach_cdev(struct kcs_bmc_device *kcs_bmc);
>-int kcs_bmc_ipmi_detach_cdev(struct kcs_bmc_device *kcs_bmc)
>+static int kcs_bmc_ipmi_detach_cdev(struct kcs_bmc_device *kcs_bmc)
> {
> 	struct kcs_bmc_ipmi *priv = NULL, *pos;
>
>@@ -541,7 +538,31 @@ int kcs_bmc_ipmi_detach_cdev(struct kcs_bmc_device *kcs_bmc)
>
> 	return 0;
> }
>-EXPORT_SYMBOL(kcs_bmc_ipmi_detach_cdev);
>+
>+static const struct kcs_bmc_cdev_ops kcs_bmc_ipmi_cdev_ops = {
>+	.add_device = kcs_bmc_ipmi_attach_cdev,
>+	.remove_device = kcs_bmc_ipmi_detach_cdev,
>+};
>+
>+static struct kcs_bmc_cdev kcs_bmc_ipmi_cdev = {
>+	.ops = &kcs_bmc_ipmi_cdev_ops,
>+};
>+
>+static int kcs_bmc_ipmi_init(void)
>+{
>+	return kcs_bmc_register_cdev(&kcs_bmc_ipmi_cdev);
>+}
>+module_init(kcs_bmc_ipmi_init);
>+
>+static void kcs_bmc_ipmi_exit(void)
>+{
>+	int rc;
>+
>+	rc = kcs_bmc_unregister_cdev(&kcs_bmc_ipmi_cdev);
>+	if (rc)
>+		pr_warn("Failed to remove KCS BMC client: %d", rc);
>+}
>+module_exit(kcs_bmc_ipmi_exit);
>
> MODULE_LICENSE("GPL v2");
> MODULE_AUTHOR("Haiyue Wang <haiyue.wang@linux.intel.com>");
>diff --git a/drivers/char/ipmi/kcs_bmc_client.h b/drivers/char/ipmi/kcs_bmc_client.h
>index 2dd710f4b4aa..d0a7404ff584 100644
>--- a/drivers/char/ipmi/kcs_bmc_client.h
>+++ b/drivers/char/ipmi/kcs_bmc_client.h
>@@ -10,6 +10,17 @@
>
> #include "kcs_bmc.h"
>
>+struct kcs_bmc_cdev_ops {
>+	int (*add_device)(struct kcs_bmc_device *kcs_bmc);
>+	int (*remove_device)(struct kcs_bmc_device *kcs_bmc);
>+};
>+
>+struct kcs_bmc_cdev {
>+	struct list_head entry;
>+
>+	const struct kcs_bmc_cdev_ops *ops;
>+};
>+
> struct kcs_bmc_client_ops {
> 	int (*event)(struct kcs_bmc_client *client);
> };
>@@ -20,6 +31,9 @@ struct kcs_bmc_client {
> 	struct kcs_bmc_device *dev;
> };
>
>+int kcs_bmc_register_cdev(struct kcs_bmc_cdev *cdev);
>+int kcs_bmc_unregister_cdev(struct kcs_bmc_cdev *cdev);
>+
> int kcs_bmc_enable_device(struct kcs_bmc_device *kcs_bmc, struct kcs_bmc_client *client);
> void kcs_bmc_disable_device(struct kcs_bmc_device *kcs_bmc, struct kcs_bmc_client *client);
>
>-- 
>2.27.0
>

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

* Re: [PATCH v2 14/21] ipmi: kcs_bmc: Allow clients to control KCS IRQ state
  2021-03-19  6:27 ` [PATCH v2 14/21] ipmi: kcs_bmc: Allow clients to control KCS IRQ state Andrew Jeffery
@ 2021-04-09  4:37   ` Zev Weiss
  2021-04-09  6:39     ` Andrew Jeffery
  0 siblings, 1 reply; 67+ messages in thread
From: Zev Weiss @ 2021-04-09  4:37 UTC (permalink / raw)
  To: Andrew Jeffery
  Cc: devicetree, chiawei_wang, ryan_chen, tmaimon77, minyard,
	avifishman70, venture, openbmc, linux-kernel, tali.perry1,
	linux-gpio, robh+dt, linux-arm-kernel, openipmi-developer,
	lee.jones, linus.walleij, linux-aspeed, benjaminfair

On Fri, Mar 19, 2021 at 01:27:45AM CDT, Andrew Jeffery wrote:
>Add a mechanism for controlling whether the client associated with a
>KCS device will receive Input Buffer Full (IBF) and Output Buffer Empty
>(OBE) events. This enables an abstract implementation of poll() for KCS
>devices.
>
>A wart in the implementation is that the ASPEED KCS devices don't
>support an OBE interrupt for the BMC. Instead we pretend it has one by
>polling the status register waiting for the Output Buffer Full (OBF) bit
>to clear, and generating an event when OBE is observed.
>
>Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
>---
> drivers/char/ipmi/kcs_bmc.c         |   6 ++
> drivers/char/ipmi/kcs_bmc.h         |   3 +
> drivers/char/ipmi/kcs_bmc_aspeed.c  | 150 ++++++++++++++++++----------
> drivers/char/ipmi/kcs_bmc_client.h  |   2 +
> drivers/char/ipmi/kcs_bmc_device.h  |   1 +
> drivers/char/ipmi/kcs_bmc_npcm7xx.c |  25 ++++-
> 6 files changed, 130 insertions(+), 57 deletions(-)
>
>diff --git a/drivers/char/ipmi/kcs_bmc.c b/drivers/char/ipmi/kcs_bmc.c
>index 694db6ee2a92..05bbb72418b2 100644
>--- a/drivers/char/ipmi/kcs_bmc.c
>+++ b/drivers/char/ipmi/kcs_bmc.c
>@@ -184,6 +184,12 @@ int kcs_bmc_unregister_cdev(struct kcs_bmc_cdev *cdev)
> }
> EXPORT_SYMBOL(kcs_bmc_unregister_cdev);
>
>+void kcs_bmc_update_event_mask(struct kcs_bmc_device *kcs_bmc, u8 mask, u8 events)
>+{
>+	kcs_bmc->ops->irq_mask_update(kcs_bmc, mask, events);
>+}
>+EXPORT_SYMBOL(kcs_bmc_update_event_mask);
>+
> MODULE_LICENSE("GPL v2");
> MODULE_AUTHOR("Haiyue Wang <haiyue.wang@linux.intel.com>");
> MODULE_AUTHOR("Andrew Jeffery <andrew@aj.id.au>");
>diff --git a/drivers/char/ipmi/kcs_bmc.h b/drivers/char/ipmi/kcs_bmc.h
>index 5deb9a0b8e60..11fff935218c 100644
>--- a/drivers/char/ipmi/kcs_bmc.h
>+++ b/drivers/char/ipmi/kcs_bmc.h
>@@ -11,6 +11,9 @@
> #define KCS_BMC_EVENT_NONE	0
> #define KCS_BMC_EVENT_HANDLED	1
>
>+#define KCS_BMC_EVENT_TYPE_OBE	BIT(0)
>+#define KCS_BMC_EVENT_TYPE_IBF	BIT(1)
>+
> #define KCS_BMC_STR_OBF		BIT(0)
> #define KCS_BMC_STR_IBF		BIT(1)
> #define KCS_BMC_STR_CMD_DAT	BIT(3)
>diff --git a/drivers/char/ipmi/kcs_bmc_aspeed.c b/drivers/char/ipmi/kcs_bmc_aspeed.c
>index 6f26e7366c0b..5f26471c038c 100644
>--- a/drivers/char/ipmi/kcs_bmc_aspeed.c
>+++ b/drivers/char/ipmi/kcs_bmc_aspeed.c
>@@ -60,10 +60,18 @@
> #define LPC_ODR4             0x118
> #define LPC_STR4             0x11C
>
>+#define OBE_POLL_PERIOD	     (HZ / 2)
>+
> struct aspeed_kcs_bmc {
> 	struct kcs_bmc_device kcs_bmc;
>
> 	struct regmap *map;
>+
>+	struct {
>+		spinlock_t lock;
>+		bool remove;
>+		struct timer_list timer;
>+	} obe;
> };
>
> struct aspeed_kcs_of_ops {
>@@ -159,68 +167,89 @@ static void aspeed_kcs_enable_channel(struct kcs_bmc_device *kcs_bmc, bool enabl
>
> 	switch (kcs_bmc->channel) {
> 	case 1:
>-		if (enable) {
>-			regmap_update_bits(priv->map, LPC_HICR2,
>-					LPC_HICR2_IBFIF1, LPC_HICR2_IBFIF1);
>-			regmap_update_bits(priv->map, LPC_HICR0,
>-					LPC_HICR0_LPC1E, LPC_HICR0_LPC1E);
>-		} else {
>-			regmap_update_bits(priv->map, LPC_HICR0,
>-					LPC_HICR0_LPC1E, 0);
>-			regmap_update_bits(priv->map, LPC_HICR2,
>-					LPC_HICR2_IBFIF1, 0);
>-		}
>-		break;
>-
>+		regmap_update_bits(priv->map, LPC_HICR0, LPC_HICR0_LPC1E, enable * LPC_HICR0_LPC1E);
>+		return;
> 	case 2:
>-		if (enable) {
>-			regmap_update_bits(priv->map, LPC_HICR2,
>-					LPC_HICR2_IBFIF2, LPC_HICR2_IBFIF2);
>-			regmap_update_bits(priv->map, LPC_HICR0,
>-					LPC_HICR0_LPC2E, LPC_HICR0_LPC2E);
>-		} else {
>-			regmap_update_bits(priv->map, LPC_HICR0,
>-					LPC_HICR0_LPC2E, 0);
>-			regmap_update_bits(priv->map, LPC_HICR2,
>-					LPC_HICR2_IBFIF2, 0);
>-		}
>-		break;
>-
>+		regmap_update_bits(priv->map, LPC_HICR0, LPC_HICR0_LPC2E, enable * LPC_HICR0_LPC2E);
>+		return;
> 	case 3:
>-		if (enable) {
>-			regmap_update_bits(priv->map, LPC_HICR2,
>-					LPC_HICR2_IBFIF3, LPC_HICR2_IBFIF3);
>-			regmap_update_bits(priv->map, LPC_HICR0,
>-					LPC_HICR0_LPC3E, LPC_HICR0_LPC3E);
>-			regmap_update_bits(priv->map, LPC_HICR4,
>-					LPC_HICR4_KCSENBL, LPC_HICR4_KCSENBL);
>-		} else {
>-			regmap_update_bits(priv->map, LPC_HICR0,
>-					LPC_HICR0_LPC3E, 0);
>-			regmap_update_bits(priv->map, LPC_HICR4,
>-					LPC_HICR4_KCSENBL, 0);
>-			regmap_update_bits(priv->map, LPC_HICR2,
>-					LPC_HICR2_IBFIF3, 0);
>-		}
>-		break;
>-
>+		regmap_update_bits(priv->map, LPC_HICR0, LPC_HICR0_LPC3E, enable * LPC_HICR0_LPC3E);
>+		regmap_update_bits(priv->map, LPC_HICR4,
>+				   LPC_HICR4_KCSENBL, enable * LPC_HICR4_KCSENBL);
>+		return;
> 	case 4:
>-		if (enable)
>-			regmap_update_bits(priv->map, LPC_HICRB,
>-					LPC_HICRB_IBFIF4 | LPC_HICRB_LPC4E,
>-					LPC_HICRB_IBFIF4 | LPC_HICRB_LPC4E);
>+		regmap_update_bits(priv->map, LPC_HICRB, LPC_HICRB_LPC4E, enable * LPC_HICRB_LPC4E);
>+		return;
>+	default:
>+		pr_warn("%s: Unsupported channel: %d", __func__, kcs_bmc->channel);
>+		return;
>+	}
>+}
>+
>+static void aspeed_kcs_check_obe(struct timer_list *timer)
>+{
>+	struct aspeed_kcs_bmc *priv = container_of(timer, struct aspeed_kcs_bmc, obe.timer);
>+	unsigned long flags;
>+	u8 str;
>+
>+	spin_lock_irqsave(&priv->obe.lock, flags);
>+	if (priv->obe.remove) {
>+		spin_unlock_irqrestore(&priv->obe.lock, flags);
>+		return;
>+	}
>+
>+	str = aspeed_kcs_inb(&priv->kcs_bmc, priv->kcs_bmc.ioreg.str);
>+	if (str & KCS_BMC_STR_OBF) {
>+		mod_timer(timer, jiffies + OBE_POLL_PERIOD);
>+		spin_unlock_irqrestore(&priv->obe.lock, flags);
>+		return;
>+	}
>+	spin_unlock_irqrestore(&priv->obe.lock, flags);
>+
>+	kcs_bmc_handle_event(&priv->kcs_bmc);
>+}
>+
>+static void aspeed_kcs_irq_mask_update(struct kcs_bmc_device *kcs_bmc, u8 mask, u8 state)
>+{
>+	struct aspeed_kcs_bmc *priv = to_aspeed_kcs_bmc(kcs_bmc);
>+
>+	/* We don't have an OBE IRQ, emulate it */
>+	if (KCS_BMC_EVENT_TYPE_OBE & mask) {
>+		if (KCS_BMC_EVENT_TYPE_OBE & state)
>+			mod_timer(&priv->obe.timer, jiffies + OBE_POLL_PERIOD);
> 		else
>-			regmap_update_bits(priv->map, LPC_HICRB,
>-					LPC_HICRB_IBFIF4 | LPC_HICRB_LPC4E,
>-					0);
>-		break;
>+			del_timer(&priv->obe.timer);
>+	}
>
>-	default:
>-		break;
>+	if (KCS_BMC_EVENT_TYPE_IBF & mask) {
>+		const bool enable = !!(KCS_BMC_EVENT_TYPE_IBF & state);

Totally superficial nitpick: the operand ordering for the bitmask tests
in this function seem a bit inconsistent with what I think is the usual
style of 'variable & BITMASK_MACRO' (reminiscent of "yoda
conditionals").

>+
>+		switch (kcs_bmc->channel) {
>+		case 1:
>+			regmap_update_bits(priv->map, LPC_HICR2, LPC_HICR2_IBFIF1,
>+					   enable * LPC_HICR2_IBFIF1);
>+			return;
>+		case 2:
>+			regmap_update_bits(priv->map, LPC_HICR2, LPC_HICR2_IBFIF2,
>+					   enable * LPC_HICR2_IBFIF2);
>+			return;
>+		case 3:
>+			regmap_update_bits(priv->map, LPC_HICR2, LPC_HICR2_IBFIF3,
>+					   enable * LPC_HICR2_IBFIF3);
>+			return;
>+		case 4:
>+			regmap_update_bits(priv->map, LPC_HICRB, LPC_HICRB_IBFIF4,
>+					   enable * LPC_HICRB_IBFIF4);
>+			return;
>+		default:
>+			pr_warn("%s: Unsupported channel: %d", __func__, kcs_bmc->channel);
>+			return;
>+		}
> 	}
> }
>
> static const struct kcs_bmc_device_ops aspeed_kcs_ops = {
>+	.irq_mask_update = aspeed_kcs_irq_mask_update,
> 	.io_inputb = aspeed_kcs_inb,
> 	.io_outputb = aspeed_kcs_outb,
> 	.io_updateb = aspeed_kcs_updateb,
>@@ -378,6 +407,10 @@ static int aspeed_kcs_probe(struct platform_device *pdev)
> 		return -ENODEV;
> 	}
>
>+	spin_lock_init(&priv->obe.lock);
>+	priv->obe.remove = false;
>+	timer_setup(&priv->obe.timer, aspeed_kcs_check_obe, 0);
>+
> 	aspeed_kcs_set_address(kcs_bmc, addr);
>
> 	rc = aspeed_kcs_config_irq(kcs_bmc, pdev);
>@@ -386,6 +419,8 @@ static int aspeed_kcs_probe(struct platform_device *pdev)
>
> 	platform_set_drvdata(pdev, priv);
>
>+	aspeed_kcs_irq_mask_update(kcs_bmc, (KCS_BMC_EVENT_TYPE_IBF | KCS_BMC_EVENT_TYPE_OBE),
>+				   KCS_BMC_EVENT_TYPE_IBF);
> 	aspeed_kcs_enable_channel(kcs_bmc, true);
>
> 	rc = kcs_bmc_add_device(&priv->kcs_bmc);
>@@ -404,6 +439,15 @@ static int aspeed_kcs_remove(struct platform_device *pdev)
>
> 	kcs_bmc_remove_device(kcs_bmc);
>
>+	aspeed_kcs_enable_channel(kcs_bmc, false);
>+	aspeed_kcs_irq_mask_update(kcs_bmc, (KCS_BMC_EVENT_TYPE_IBF | KCS_BMC_EVENT_TYPE_OBE), 0);
>+
>+	/* Make sure it's proper dead */
>+	spin_lock_irq(&priv->obe.lock);
>+	priv->obe.remove = true;
>+	spin_unlock_irq(&priv->obe.lock);
>+	del_timer_sync(&priv->obe.timer);
>+
> 	return 0;
> }
>
>diff --git a/drivers/char/ipmi/kcs_bmc_client.h b/drivers/char/ipmi/kcs_bmc_client.h
>index d0a7404ff584..456796da33de 100644
>--- a/drivers/char/ipmi/kcs_bmc_client.h
>+++ b/drivers/char/ipmi/kcs_bmc_client.h
>@@ -37,6 +37,8 @@ int kcs_bmc_unregister_cdev(struct kcs_bmc_cdev *cdev);
> int kcs_bmc_enable_device(struct kcs_bmc_device *kcs_bmc, struct kcs_bmc_client *client);
> void kcs_bmc_disable_device(struct kcs_bmc_device *kcs_bmc, struct kcs_bmc_client *client);
>
>+void kcs_bmc_update_event_mask(struct kcs_bmc_device *kcs_bmc, u8 mask, u8 events);
>+
> u8 kcs_bmc_read_data(struct kcs_bmc_device *kcs_bmc);
> void kcs_bmc_write_data(struct kcs_bmc_device *kcs_bmc, u8 data);
> u8 kcs_bmc_read_status(struct kcs_bmc_device *kcs_bmc);
>diff --git a/drivers/char/ipmi/kcs_bmc_device.h b/drivers/char/ipmi/kcs_bmc_device.h
>index 57b7174b2bac..f1ca8912496a 100644
>--- a/drivers/char/ipmi/kcs_bmc_device.h
>+++ b/drivers/char/ipmi/kcs_bmc_device.h
>@@ -7,6 +7,7 @@
> #include "kcs_bmc.h"
>
> struct kcs_bmc_device_ops {
>+	void (*irq_mask_update)(struct kcs_bmc_device *kcs_bmc, u8 mask, u8 enable);
> 	u8 (*io_inputb)(struct kcs_bmc_device *kcs_bmc, u32 reg);
> 	void (*io_outputb)(struct kcs_bmc_device *kcs_bmc, u32 reg, u8 b);
> 	void (*io_updateb)(struct kcs_bmc_device *kcs_bmc, u32 reg, u8 mask, u8 b);
>diff --git a/drivers/char/ipmi/kcs_bmc_npcm7xx.c b/drivers/char/ipmi/kcs_bmc_npcm7xx.c
>index dce93ec895fc..c2032728a03d 100644
>--- a/drivers/char/ipmi/kcs_bmc_npcm7xx.c
>+++ b/drivers/char/ipmi/kcs_bmc_npcm7xx.c
>@@ -38,6 +38,7 @@
> #define KCS2CTL		0x2A
> #define KCS3CTL		0x3C
> #define    KCS_CTL_IBFIE	BIT(0)
>+#define    KCS_CTL_OBEIE	BIT(0)
>
> #define KCS1IE		0x1C
> #define KCS2IE		0x2E
>@@ -117,13 +118,23 @@ static void npcm7xx_kcs_enable_channel(struct kcs_bmc_device *kcs_bmc, bool enab
> {
> 	struct npcm7xx_kcs_bmc *priv = to_npcm7xx_kcs_bmc(kcs_bmc);
>
>-	regmap_update_bits(priv->map, priv->reg->ctl, KCS_CTL_IBFIE,
>-			   enable ? KCS_CTL_IBFIE : 0);
>-
> 	regmap_update_bits(priv->map, priv->reg->ie, KCS_IE_IRQE | KCS_IE_HIRQE,
> 			   enable ? KCS_IE_IRQE | KCS_IE_HIRQE : 0);
> }
>
>+static void npcm7xx_kcs_irq_mask_update(struct kcs_bmc_device *kcs_bmc, u8 mask, u8 state)
>+{
>+	struct npcm7xx_kcs_bmc *priv = to_npcm7xx_kcs_bmc(kcs_bmc);
>+
>+	if (KCS_BMC_EVENT_TYPE_OBE & mask)
>+		regmap_update_bits(priv->map, priv->reg->ctl, KCS_CTL_OBEIE,
>+				   !!(KCS_BMC_EVENT_TYPE_OBE & state) * KCS_CTL_OBEIE);
>+
>+	if (KCS_BMC_EVENT_TYPE_IBF & mask)
>+		regmap_update_bits(priv->map, priv->reg->ctl, KCS_CTL_IBFIE,
>+				   !!(KCS_BMC_EVENT_TYPE_IBF & state) * KCS_CTL_IBFIE);

Same operand ordering thing here...

>+}
>+
> static irqreturn_t npcm7xx_kcs_irq(int irq, void *arg)
> {
> 	struct kcs_bmc_device *kcs_bmc = arg;
>@@ -151,6 +162,7 @@ static int npcm7xx_kcs_config_irq(struct kcs_bmc_device *kcs_bmc,
> }
>
> static const struct kcs_bmc_device_ops npcm7xx_kcs_ops = {
>+	.irq_mask_update = npcm7xx_kcs_irq_mask_update,
> 	.io_inputb = npcm7xx_kcs_inb,
> 	.io_outputb = npcm7xx_kcs_outb,
> 	.io_updateb = npcm7xx_kcs_updateb,
>@@ -191,11 +203,13 @@ static int npcm7xx_kcs_probe(struct platform_device *pdev)
>
> 	platform_set_drvdata(pdev, priv);
>
>-	npcm7xx_kcs_enable_channel(kcs_bmc, true);
> 	rc = npcm7xx_kcs_config_irq(kcs_bmc, pdev);
> 	if (rc)
> 		return rc;
>
>+	npcm7xx_kcs_irq_mask_update(kcs_bmc, (KCS_BMC_EVENT_TYPE_IBF | KCS_BMC_EVENT_TYPE_OBE),
>+				    KCS_BMC_EVENT_TYPE_IBF);
>+	npcm7xx_kcs_enable_channel(kcs_bmc, true);
>
> 	pr_info("channel=%u idr=0x%x odr=0x%x str=0x%x\n",
> 		chan,
>@@ -211,6 +225,9 @@ static int npcm7xx_kcs_remove(struct platform_device *pdev)
>
> 	kcs_bmc_remove_device(kcs_bmc);
>
>+	npcm7xx_kcs_enable_channel(kcs_bmc, false);
>+	npcm7xx_kcs_irq_mask_update(kcs_bmc, (KCS_BMC_EVENT_TYPE_IBF | KCS_BMC_EVENT_TYPE_OBE), 0);
>+
> 	return 0;
> }
>
>-- 
>2.27.0
>

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

* Re: [PATCH v2 15/21] ipmi: kcs_bmc: Don't enforce single-open policy in the kernel
  2021-03-19  6:27 ` [PATCH v2 15/21] ipmi: kcs_bmc: Don't enforce single-open policy in the kernel Andrew Jeffery
@ 2021-04-09  5:07   ` Zev Weiss
  2021-04-09  6:42     ` Andrew Jeffery
  0 siblings, 1 reply; 67+ messages in thread
From: Zev Weiss @ 2021-04-09  5:07 UTC (permalink / raw)
  To: Andrew Jeffery
  Cc: devicetree, chiawei_wang, ryan_chen, tmaimon77, minyard,
	avifishman70, venture, openbmc, linux-kernel, tali.perry1,
	linux-gpio, robh+dt, linux-arm-kernel, openipmi-developer,
	lee.jones, linus.walleij, linux-aspeed, benjaminfair

On Fri, Mar 19, 2021 at 01:27:46AM CDT, Andrew Jeffery wrote:
>Soon it will be possible for one KCS device to have multiple associated
>chardevs exposed to userspace (for IPMI and raw-style access). However,
>don't prevent userspace from:
>
>1. Opening more than one chardev at a time, or
>2. Opening the same chardev more than once.
>
>System behaviour is undefined for both classes of multiple access, so
>userspace must manage itself accordingly.
>
>The implementation delivers IBF and OBF events to the first chardev
>client to associate with the KCS device. An open on a related chardev
>cannot associate its client with the KCS device and so will not
>receive notification of events. However, any fd on any chardev may race
>their accesses to the data and status registers.
>
>Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
>---
> drivers/char/ipmi/kcs_bmc.c         | 34 ++++++++++-------------------
> drivers/char/ipmi/kcs_bmc_aspeed.c  |  3 +--
> drivers/char/ipmi/kcs_bmc_npcm7xx.c |  3 +--
> 3 files changed, 14 insertions(+), 26 deletions(-)
>
>diff --git a/drivers/char/ipmi/kcs_bmc.c b/drivers/char/ipmi/kcs_bmc.c
>index 05bbb72418b2..2fafa9541934 100644
>--- a/drivers/char/ipmi/kcs_bmc.c
>+++ b/drivers/char/ipmi/kcs_bmc.c
>@@ -55,24 +55,12 @@ EXPORT_SYMBOL(kcs_bmc_update_status);
> int kcs_bmc_handle_event(struct kcs_bmc_device *kcs_bmc)
> {
> 	struct kcs_bmc_client *client;
>-	int rc;
>+	int rc = KCS_BMC_EVENT_NONE;
>
> 	spin_lock(&kcs_bmc->lock);
> 	client = kcs_bmc->client;
>-	if (client) {
>+	if (!WARN_ON_ONCE(!client))
> 		rc = client->ops->event(client);

The double-negation split by a macro seems a bit confusing to me
readability-wise; could we simplify to something like

	if (client)
		rc = client->ops->event(client);
	else
		WARN_ONCE();

?

>-	} else {
>-		u8 status;
>-
>-		status = kcs_bmc_read_status(kcs_bmc);
>-		if (status & KCS_BMC_STR_IBF) {
>-			/* Ack the event by reading the data */
>-			kcs_bmc_read_data(kcs_bmc);
>-			rc = KCS_BMC_EVENT_HANDLED;
>-		} else {
>-			rc = KCS_BMC_EVENT_NONE;
>-		}
>-	}
> 	spin_unlock(&kcs_bmc->lock);
>
> 	return rc;
>@@ -81,26 +69,28 @@ EXPORT_SYMBOL(kcs_bmc_handle_event);
>
> int kcs_bmc_enable_device(struct kcs_bmc_device *kcs_bmc, struct kcs_bmc_client *client)
> {
>-	int rc;
>-
> 	spin_lock_irq(&kcs_bmc->lock);
>-	if (kcs_bmc->client) {
>-		rc = -EBUSY;
>-	} else {
>+	if (!kcs_bmc->client) {
>+		u8 mask = KCS_BMC_EVENT_TYPE_IBF;
>+
> 		kcs_bmc->client = client;
>-		rc = 0;
>+		kcs_bmc_update_event_mask(kcs_bmc, mask, mask);
> 	}
> 	spin_unlock_irq(&kcs_bmc->lock);
>
>-	return rc;
>+	return 0;

Since this function appears to be infallible now, should it just return
void?  (Might be more churn than it's worth...shrug.)

> }
> EXPORT_SYMBOL(kcs_bmc_enable_device);
>
> void kcs_bmc_disable_device(struct kcs_bmc_device *kcs_bmc, struct kcs_bmc_client *client)
> {
> 	spin_lock_irq(&kcs_bmc->lock);
>-	if (client == kcs_bmc->client)
>+	if (client == kcs_bmc->client) {
>+		u8 mask = KCS_BMC_EVENT_TYPE_IBF | KCS_BMC_EVENT_TYPE_OBE;
>+
>+		kcs_bmc_update_event_mask(kcs_bmc, mask, 0);
> 		kcs_bmc->client = NULL;
>+	}
> 	spin_unlock_irq(&kcs_bmc->lock);
> }
> EXPORT_SYMBOL(kcs_bmc_disable_device);
>diff --git a/drivers/char/ipmi/kcs_bmc_aspeed.c b/drivers/char/ipmi/kcs_bmc_aspeed.c
>index 5f26471c038c..271845eb2e26 100644
>--- a/drivers/char/ipmi/kcs_bmc_aspeed.c
>+++ b/drivers/char/ipmi/kcs_bmc_aspeed.c
>@@ -419,8 +419,7 @@ static int aspeed_kcs_probe(struct platform_device *pdev)
>
> 	platform_set_drvdata(pdev, priv);
>
>-	aspeed_kcs_irq_mask_update(kcs_bmc, (KCS_BMC_EVENT_TYPE_IBF | KCS_BMC_EVENT_TYPE_OBE),
>-				   KCS_BMC_EVENT_TYPE_IBF);
>+	aspeed_kcs_irq_mask_update(kcs_bmc, (KCS_BMC_EVENT_TYPE_IBF | KCS_BMC_EVENT_TYPE_OBE), 0);
> 	aspeed_kcs_enable_channel(kcs_bmc, true);
>
> 	rc = kcs_bmc_add_device(&priv->kcs_bmc);
>diff --git a/drivers/char/ipmi/kcs_bmc_npcm7xx.c b/drivers/char/ipmi/kcs_bmc_npcm7xx.c
>index c2032728a03d..fdf35cad2eba 100644
>--- a/drivers/char/ipmi/kcs_bmc_npcm7xx.c
>+++ b/drivers/char/ipmi/kcs_bmc_npcm7xx.c
>@@ -207,8 +207,7 @@ static int npcm7xx_kcs_probe(struct platform_device *pdev)
> 	if (rc)
> 		return rc;
>
>-	npcm7xx_kcs_irq_mask_update(kcs_bmc, (KCS_BMC_EVENT_TYPE_IBF | KCS_BMC_EVENT_TYPE_OBE),
>-				    KCS_BMC_EVENT_TYPE_IBF);
>+	npcm7xx_kcs_irq_mask_update(kcs_bmc, (KCS_BMC_EVENT_TYPE_IBF | KCS_BMC_EVENT_TYPE_OBE), 0);
> 	npcm7xx_kcs_enable_channel(kcs_bmc, true);
>
> 	pr_info("channel=%u idr=0x%x odr=0x%x str=0x%x\n",
>-- 
>2.27.0
>

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

* Re: [PATCH v2 17/21] dt-bindings: ipmi: Convert ASPEED KCS binding to schema
  2021-03-19  6:27 ` [PATCH v2 17/21] dt-bindings: ipmi: Convert ASPEED KCS binding to schema Andrew Jeffery
  2021-03-26  1:48   ` Rob Herring
@ 2021-04-09  5:15   ` Zev Weiss
  2021-04-09  5:33     ` Andrew Jeffery
  1 sibling, 1 reply; 67+ messages in thread
From: Zev Weiss @ 2021-04-09  5:15 UTC (permalink / raw)
  To: Andrew Jeffery
  Cc: devicetree, chiawei_wang, ryan_chen, tmaimon77, minyard,
	avifishman70, venture, openbmc, linux-kernel, tali.perry1,
	linux-gpio, robh+dt, linux-arm-kernel, openipmi-developer,
	lee.jones, linus.walleij, linux-aspeed, benjaminfair

On Fri, Mar 19, 2021 at 01:27:48AM CDT, Andrew Jeffery wrote:
>Given the deprecated binding, improve the ability to detect issues in
>the platform devicetrees. Further, a subsequent patch will introduce a
>new interrupts property for specifying SerIRQ behaviour, so convert
>before we do any further additions.
>
>Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
>---
> .../bindings/ipmi/aspeed,ast2400-kcs-bmc.yaml | 92 +++++++++++++++++++
> .../bindings/ipmi/aspeed-kcs-bmc.txt          | 33 -------
> 2 files changed, 92 insertions(+), 33 deletions(-)
> create mode 100644 Documentation/devicetree/bindings/ipmi/aspeed,ast2400-kcs-bmc.yaml
> delete mode 100644 Documentation/devicetree/bindings/ipmi/aspeed-kcs-bmc.txt
>
>diff --git a/Documentation/devicetree/bindings/ipmi/aspeed,ast2400-kcs-bmc.yaml b/Documentation/devicetree/bindings/ipmi/aspeed,ast2400-kcs-bmc.yaml
>new file mode 100644
>index 000000000000..697ca575454f
>--- /dev/null
>+++ b/Documentation/devicetree/bindings/ipmi/aspeed,ast2400-kcs-bmc.yaml
>@@ -0,0 +1,92 @@
>+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
>+%YAML 1.2
>+---
>+$id: http://devicetree.org/schemas/ipmi/aspeed,ast2400-kcs-bmc.yaml
>+$schema: http://devicetree.org/meta-schemas/core.yaml
>+
>+title: ASPEED BMC KCS Devices
>+
>+maintainers:
>+  - Andrew Jeffery <andrew@aj.id.au>
>+
>+description: |
>+  The Aspeed BMC SoCs typically use the Keyboard-Controller-Style (KCS)
>+  interfaces on the LPC bus for in-band IPMI communication with their host.
>+
>+properties:
>+  compatible:
>+    oneOf:
>+      - description: Channel ID derived from reg
>+        items:
>+          enum:
>+            - aspeed,ast2400-kcs-bmc-v2
>+            - aspeed,ast2500-kcs-bmc-v2
>+            - aspeed,ast2600-kcs-bmc

Should this have a "-v2" suffix?

>+
>+      - description: Old-style with explicit channel ID, no reg
>+        deprecated: true
>+        items:
>+          enum:
>+            - aspeed,ast2400-kcs-bmc
>+            - aspeed,ast2500-kcs-bmc
>+
>+  interrupts:
>+    maxItems: 1
>+
>+  reg:
>+    # maxItems: 3
>+    items:
>+      - description: IDR register
>+      - description: ODR register
>+      - description: STR register
>+
>+  aspeed,lpc-io-reg:
>+    $ref: '/schemas/types.yaml#/definitions/uint32-array'
>+    minItems: 1
>+    maxItems: 2
>+    description: |
>+      The host CPU LPC IO data and status addresses for the device. For most
>+      channels the status address is derived from the data address, but the
>+      status address may be optionally provided.
>+
>+  kcs_chan:
>+    deprecated: true
>+    $ref: '/schemas/types.yaml#/definitions/uint32'
>+    description: The LPC channel number in the controller
>+
>+  kcs_addr:
>+    deprecated: true
>+    $ref: '/schemas/types.yaml#/definitions/uint32'
>+    description: The host CPU IO map address
>+
>+required:
>+  - compatible
>+  - interrupts
>+
>+additionalProperties: false
>+
>+allOf:
>+  - if:
>+      properties:
>+        compatible:
>+          contains:
>+            enum:
>+              - aspeed,ast2400-kcs-bmc
>+              - aspeed,ast2500-kcs-bmc
>+    then:
>+      required:
>+        - kcs_chan
>+        - kcs_addr
>+    else:
>+      required:
>+        - reg
>+        - aspeed,lpc-io-reg
>+
>+examples:
>+  - |
>+    kcs3: kcs@24 {
>+        compatible = "aspeed,ast2600-kcs-bmc";

And likewise here.

>+        reg = <0x24 0x1>, <0x30 0x1>, <0x3c 0x1>;
>+        aspeed,lpc-io-reg = <0xca2>;
>+        interrupts = <8>;
>+    };
>diff --git a/Documentation/devicetree/bindings/ipmi/aspeed-kcs-bmc.txt b/Documentation/devicetree/bindings/ipmi/aspeed-kcs-bmc.txt
>deleted file mode 100644
>index 193e71ca96b0..000000000000
>--- a/Documentation/devicetree/bindings/ipmi/aspeed-kcs-bmc.txt
>+++ /dev/null
>@@ -1,33 +0,0 @@
>-# Aspeed KCS (Keyboard Controller Style) IPMI interface
>-
>-The Aspeed SOCs (AST2400 and AST2500) are commonly used as BMCs
>-(Baseboard Management Controllers) and the KCS interface can be
>-used to perform in-band IPMI communication with their host.
>-
>-## v1
>-Required properties:
>-- compatible : should be one of
>-    "aspeed,ast2400-kcs-bmc"
>-    "aspeed,ast2500-kcs-bmc"
>-- interrupts : interrupt generated by the controller
>-- kcs_chan : The LPC channel number in the controller
>-- kcs_addr : The host CPU IO map address
>-
>-## v2
>-Required properties:
>-- compatible : should be one of
>-    "aspeed,ast2400-kcs-bmc-v2"
>-    "aspeed,ast2500-kcs-bmc-v2"
>-- reg : The address and size of the IDR, ODR and STR registers
>-- interrupts : interrupt generated by the controller
>-- aspeed,lpc-io-reg : The host CPU LPC IO address for the device
>-
>-Example:
>-
>-    kcs3: kcs@24 {
>-        compatible = "aspeed,ast2500-kcs-bmc-v2";
>-        reg = <0x24 0x1>, <0x30 0x1>, <0x3c 0x1>;
>-        aspeed,lpc-reg = <0xca2>;
>-        interrupts = <8>;
>-        status = "okay";
>-    };
>-- 
>2.27.0
>

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

* Re: [PATCH v2 16/21] ipmi: kcs_bmc: Add a "raw" character device interface
  2021-03-19  6:27 ` [PATCH v2 16/21] ipmi: kcs_bmc: Add a "raw" character device interface Andrew Jeffery
@ 2021-04-09  5:17   ` Zev Weiss
  2021-04-09  6:46     ` Andrew Jeffery
  2021-04-09  7:55   ` Arnd Bergmann
  1 sibling, 1 reply; 67+ messages in thread
From: Zev Weiss @ 2021-04-09  5:17 UTC (permalink / raw)
  To: Andrew Jeffery
  Cc: devicetree, chiawei_wang, ryan_chen, tmaimon77, minyard,
	avifishman70, venture, openbmc, linux-kernel, tali.perry1,
	linux-gpio, robh+dt, linux-arm-kernel, openipmi-developer,
	lee.jones, linus.walleij, linux-aspeed, benjaminfair

On Fri, Mar 19, 2021 at 01:27:47AM CDT, Andrew Jeffery wrote:
>The existing IPMI chardev encodes IPMI behaviours as the name suggests.
>However, KCS devices are useful beyond IPMI (or keyboards), as they
>provide a means to generate IRQs and exchange arbitrary data between a
>BMC and its host system.
>
>Implement a "raw" KCS character device that exposes the IDR, ODR and STR
>registers to userspace via read() and write() implemented on a character
>device:
>
>+--------+--------+---------+
>| Offset | read() | write() |
>+--------+--------+---------+
>|   0    |   IDR  |   ODR   |
>+--------+--------+---------+
>|   1    |   STR  |   STR   |
>+--------+--------+---------+
>
>This interface allows userspace to implement arbitrary (though somewhat
>inefficient) protocols for exchanging information between a BMC and host
>firmware. Conceptually the KCS interface can be used as an out-of-band
>machanism for interrupt-signaled control messages while bulk data

Typo ("mechanism")

>transfers occur over more appropriate interfaces between the BMC and the
>host (which may lack their own interrupt mechanism, e.g. LPC FW cycles).
>
>poll() is provided, which will wait for IBF or OBE conditions for data
>reads and writes respectively. Reads of STR on its own never blocks,
>though accessing both offsets in the one system call may block if the
>data registers are not ready.
>
>Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
>---
> Documentation/ABI/testing/dev-raw-kcs |  25 ++
> drivers/char/ipmi/Kconfig             |  17 +
> drivers/char/ipmi/Makefile            |   1 +
> drivers/char/ipmi/kcs_bmc_cdev_raw.c  | 443 ++++++++++++++++++++++++++
> 4 files changed, 486 insertions(+)
> create mode 100644 Documentation/ABI/testing/dev-raw-kcs
> create mode 100644 drivers/char/ipmi/kcs_bmc_cdev_raw.c
>
>diff --git a/Documentation/ABI/testing/dev-raw-kcs b/Documentation/ABI/testing/dev-raw-kcs
>new file mode 100644
>index 000000000000..06e7e2071562
>--- /dev/null
>+++ b/Documentation/ABI/testing/dev-raw-kcs
>@@ -0,0 +1,25 @@
>+What:		/dev/raw-kcs*
>+Date:		2021-02-15
>+KernelVersion:	5.13
>+Contact:	openbmc@lists.ozlabs.org
>+Contact:	openipmi-developer@lists.sourceforge.net
>+Contact:	Andrew Jeffery <andrew@aj.id.au>
>+Description:	``/dev/raw-kcs*`` exposes to userspace the data and
>+		status registers of Keyboard-Controller-Style (KCS) IPMI
>+		interfaces via read() and write() syscalls. Direct
>+		exposure of the data and status registers enables
>+		inefficient but arbitrary protocols to be implemented
>+		over the device. A typical approach is to use KCS
>+		devices for out-of-band signalling for bulk data
>+		transfers over other interfaces between a Baseboard
>+		Management Controller and its host.
>+
>+		+--------+--------+---------+
>+		| Offset | read() | write() |
>+		+--------+--------+---------+
>+		|   0    |   IDR  |   ODR   |
>+		+--------+--------+---------+
>+		|   1    |   STR  |   STR   |
>+		+--------+--------+---------+
>+
>+Users:		libmctp: https://github.com/openbmc/libmctp
>diff --git a/drivers/char/ipmi/Kconfig b/drivers/char/ipmi/Kconfig
>index bc5f81899b62..273ac1a1f870 100644
>--- a/drivers/char/ipmi/Kconfig
>+++ b/drivers/char/ipmi/Kconfig
>@@ -137,6 +137,23 @@ config IPMI_KCS_BMC_CDEV_IPMI
> 	  This support is also available as a module. The module will be
> 	  called kcs_bmc_cdev_ipmi.
>
>+config IPMI_KCS_BMC_CDEV_RAW
>+	depends on IPMI_KCS_BMC
>+	tristate "Raw character device interface for BMC KCS devices"
>+	help
>+	  Provides a BMC-side character device directly exposing the
>+	  data and status registers of a KCS device to userspace. While
>+	  KCS devices are commonly used to implement IPMI message
>+	  passing, they provide a general interface for exchange of
>+	  interrupts, data and status information between the BMC and
>+	  its host.
>+
>+	  Say YES if you wish to use the KCS devices to implement
>+	  protocols that are not IPMI.
>+
>+	  This support is also available as a module. The module will be
>+	  called kcs_bmc_cdev_raw.
>+
> config ASPEED_BT_IPMI_BMC
> 	depends on ARCH_ASPEED || COMPILE_TEST
> 	depends on REGMAP && REGMAP_MMIO && MFD_SYSCON
>diff --git a/drivers/char/ipmi/Makefile b/drivers/char/ipmi/Makefile
>index fcfa676afddb..c8cc248ddd90 100644
>--- a/drivers/char/ipmi/Makefile
>+++ b/drivers/char/ipmi/Makefile
>@@ -24,6 +24,7 @@ obj-$(CONFIG_IPMI_WATCHDOG) += ipmi_watchdog.o
> obj-$(CONFIG_IPMI_POWEROFF) += ipmi_poweroff.o
> obj-$(CONFIG_IPMI_KCS_BMC) += kcs_bmc.o
> obj-$(CONFIG_IPMI_KCS_BMC_CDEV_IPMI) += kcs_bmc_cdev_ipmi.o
>+obj-$(CONFIG_IPMI_KCS_BMC_CDEV_RAW) += kcs_bmc_cdev_raw.o
> obj-$(CONFIG_ASPEED_BT_IPMI_BMC) += bt-bmc.o
> obj-$(CONFIG_ASPEED_KCS_IPMI_BMC) += kcs_bmc_aspeed.o
> obj-$(CONFIG_NPCM7XX_KCS_IPMI_BMC) += kcs_bmc_npcm7xx.o
>diff --git a/drivers/char/ipmi/kcs_bmc_cdev_raw.c b/drivers/char/ipmi/kcs_bmc_cdev_raw.c
>new file mode 100644
>index 000000000000..bdd258648c8e
>--- /dev/null
>+++ b/drivers/char/ipmi/kcs_bmc_cdev_raw.c
>@@ -0,0 +1,443 @@
>+// SPDX-License-Identifier: GPL-2.0-or-later
>+/* Copyright (c) 2021 IBM Corp. */
>+
>+#include <linux/delay.h>
>+#include <linux/device.h>
>+#include <linux/errno.h>
>+#include <linux/fs.h>
>+#include <linux/list.h>
>+#include <linux/miscdevice.h>
>+#include <linux/module.h>
>+#include <linux/poll.h>
>+
>+#include "kcs_bmc_client.h"
>+
>+#define DEVICE_NAME "raw-kcs"
>+
>+struct kcs_bmc_raw {
>+	struct list_head entry;
>+
>+	struct kcs_bmc_client client;
>+
>+	wait_queue_head_t queue;
>+	u8 events;
>+	bool writable;
>+	bool readable;
>+	u8 idr;
>+
>+	struct miscdevice miscdev;
>+};
>+
>+static inline struct kcs_bmc_raw *client_to_kcs_bmc_raw(struct kcs_bmc_client *client)
>+{
>+	return container_of(client, struct kcs_bmc_raw, client);
>+}
>+
>+/* Call under priv->queue.lock */
>+static void kcs_bmc_raw_update_event_mask(struct kcs_bmc_raw *priv, u8 mask, u8 state)
>+{
>+	kcs_bmc_update_event_mask(priv->client.dev, mask, state);
>+	priv->events &= ~mask;
>+	priv->events |= state & mask;
>+}
>+
>+static int kcs_bmc_raw_event(struct kcs_bmc_client *client)
>+{
>+	struct kcs_bmc_raw *priv;
>+	struct device *dev;
>+	u8 status, handled;
>+
>+	priv = client_to_kcs_bmc_raw(client);
>+	dev = priv->miscdev.this_device;
>+
>+	spin_lock(&priv->queue.lock);
>+
>+	status = kcs_bmc_read_status(client->dev);
>+	handled = 0;
>+
>+	if ((priv->events & KCS_BMC_EVENT_TYPE_IBF) && (status & KCS_BMC_STR_IBF)) {
>+		if (priv->readable)
>+			dev_err(dev, "Storm brewing!");

That seems a *touch* cryptic...

>+
>+		dev_dbg(dev, "Disabling IDR events for back-pressure\n");
>+		kcs_bmc_raw_update_event_mask(priv, KCS_BMC_EVENT_TYPE_IBF, 0);
>+		priv->idr = kcs_bmc_read_data(client->dev);
>+		priv->readable = true;
>+
>+		dev_dbg(dev, "IDR read, waking waiters\n");
>+		wake_up_locked(&priv->queue);
>+
>+		handled |= KCS_BMC_EVENT_TYPE_IBF;
>+	}
>+
>+	if ((priv->events & KCS_BMC_EVENT_TYPE_OBE) && !(status & KCS_BMC_STR_OBF)) {
>+		kcs_bmc_raw_update_event_mask(priv, KCS_BMC_EVENT_TYPE_OBE, 0);
>+		priv->writable = true;
>+
>+		dev_dbg(dev, "ODR writable, waking waiters\n");
>+		wake_up_locked(&priv->queue);
>+
>+		handled |= KCS_BMC_EVENT_TYPE_OBE;
>+	}
>+
>+	spin_unlock(&priv->queue.lock);
>+
>+	return handled ? KCS_BMC_EVENT_HANDLED : KCS_BMC_EVENT_NONE;

Hm, if we're just treating it as a boolean here, is there any need to
muck around with setting specific bits of 'handled' in the if-blocks
above?

>+}
>+
>+static const struct kcs_bmc_client_ops kcs_bmc_raw_client_ops = {
>+	.event = kcs_bmc_raw_event,
>+};
>+
>+static inline struct kcs_bmc_raw *file_to_kcs_bmc_raw(struct file *filp)
>+{
>+	return container_of(filp->private_data, struct kcs_bmc_raw, miscdev);
>+}
>+
>+static int kcs_bmc_raw_open(struct inode *inode, struct file *filp)
>+{
>+	struct kcs_bmc_raw *priv = file_to_kcs_bmc_raw(filp);
>+
>+	return kcs_bmc_enable_device(priv->client.dev, &priv->client);
>+}
>+
>+static bool kcs_bmc_raw_prepare_obe(struct kcs_bmc_raw *priv)
>+{
>+	bool writable;
>+
>+	/* Enable the OBE event so we can catch the host clearing OBF */
>+	kcs_bmc_raw_update_event_mask(priv, KCS_BMC_EVENT_TYPE_OBE, KCS_BMC_EVENT_TYPE_OBE);
>+
>+	/* Now that we'll catch an OBE event, check if it's already occurred */
>+	writable = !(kcs_bmc_read_status(priv->client.dev) & KCS_BMC_STR_OBF);
>+
>+	/* If OBF is clear we've missed the OBE event, so disable it */
>+	if (writable)
>+		kcs_bmc_raw_update_event_mask(priv, KCS_BMC_EVENT_TYPE_OBE, 0);
>+
>+	return writable;
>+}
>+
>+static __poll_t kcs_bmc_raw_poll(struct file *filp, poll_table *wait)
>+{
>+	struct kcs_bmc_raw *priv;
>+	__poll_t events = 0;
>+
>+	priv = file_to_kcs_bmc_raw(filp);
>+
>+	poll_wait(filp, &priv->queue, wait);
>+
>+	spin_lock_irq(&priv->queue.lock);
>+	if (kcs_bmc_raw_prepare_obe(priv))
>+		events |= (EPOLLOUT | EPOLLWRNORM);
>+
>+	if (priv->readable || (kcs_bmc_read_status(priv->client.dev) & KCS_BMC_STR_IBF))
>+		events |= (EPOLLIN | EPOLLRDNORM);
>+	spin_unlock_irq(&priv->queue.lock);
>+
>+	return events;
>+}
>+
>+static ssize_t kcs_bmc_raw_read(struct file *filp, char __user *buf,
>+			     size_t count, loff_t *ppos)
>+{
>+	struct kcs_bmc_device *kcs_bmc;
>+	struct kcs_bmc_raw *priv;
>+	bool read_idr, read_str;
>+	struct device *dev;
>+	u8 idr, str;
>+	ssize_t rc;
>+
>+	priv = file_to_kcs_bmc_raw(filp);
>+	kcs_bmc = priv->client.dev;
>+	dev = priv->miscdev.this_device;
>+
>+	if (!count)
>+		return 0;
>+
>+	if (count > 2 || *ppos > 1)
>+		return -EINVAL;
>+
>+	if (*ppos + count > 2)
>+		return -EINVAL;
>+
>+	read_idr = (*ppos == 0);
>+	read_str = (*ppos == 1) || (count == 2);
>+
>+	spin_lock_irq(&priv->queue.lock);
>+	if (read_idr) {
>+		dev_dbg(dev, "Waiting for IBF\n");
>+		str = kcs_bmc_read_status(kcs_bmc);
>+		if ((filp->f_flags & O_NONBLOCK) && (str & KCS_BMC_STR_IBF)) {
>+			rc = -EWOULDBLOCK;
>+			goto out;
>+		}
>+
>+		rc = wait_event_interruptible_locked(priv->queue,
>+						     priv->readable || (str & KCS_BMC_STR_IBF));
>+		if (rc < 0)
>+			goto out;
>+
>+		if (signal_pending(current)) {
>+			dev_dbg(dev, "Interrupted waiting for IBF\n");
>+			rc = -EINTR;
>+			goto out;
>+		}
>+
>+		/*
>+		 * Re-enable events prior to possible read of IDR (which clears
>+		 * IBF) to ensure we receive interrupts for subsequent writes
>+		 * to IDR. Writes to IDR by the host should not occur while IBF
>+		 * is set.
>+		 */
>+		dev_dbg(dev, "Woken by IBF, enabling IRQ\n");
>+		kcs_bmc_raw_update_event_mask(priv, KCS_BMC_EVENT_TYPE_IBF,
>+					      KCS_BMC_EVENT_TYPE_IBF);
>+
>+		/* Read data out of IDR into internal storage if necessary */
>+		if (!priv->readable) {
>+			WARN(!(str & KCS_BMC_STR_IBF), "Unknown reason for wakeup!");
>+
>+			priv->idr = kcs_bmc_read_data(kcs_bmc);
>+		}
>+
>+		/* Copy data from internal storage to userspace */
>+		idr = priv->idr;
>+
>+		/* We're done consuming the internally stored value */
>+		priv->readable = false;
>+	}
>+
>+	if (read_str) {
>+		str = kcs_bmc_read_status(kcs_bmc);
>+		if (*ppos == 0 || priv->readable)
>+			/*
>+			 * If we got this far with `*ppos == 0` then we've read
>+			 * data out of IDR, so set IBF when reporting back to
>+			 * userspace so userspace knows the IDR value is valid.
>+			 */
>+			str |= KCS_BMC_STR_IBF;
>+
>+		dev_dbg(dev, "Read status 0x%x\n", str);
>+
>+	}
>+
>+	rc = count;
>+out:
>+	spin_unlock_irq(&priv->queue.lock);
>+
>+	if (rc < 0)
>+		return rc;
>+
>+	/* Now copy the data in to the userspace buffer */
>+
>+	if (read_idr)
>+		if (copy_to_user(buf++, &idr, sizeof(idr)))
>+			return -EFAULT;
>+
>+	if (read_str)
>+		if (copy_to_user(buf, &str, sizeof(str)))
>+			return -EFAULT;
>+
>+	return count;
>+}
>+
>+static ssize_t kcs_bmc_raw_write(struct file *filp, const char __user *buf,
>+			      size_t count, loff_t *ppos)
>+{
>+	struct kcs_bmc_device *kcs_bmc;
>+	bool write_odr, write_str;
>+	struct kcs_bmc_raw *priv;
>+	struct device *dev;
>+	uint8_t data[2];
>+	ssize_t result;
>+	u8 str;
>+
>+	priv = file_to_kcs_bmc_raw(filp);
>+	kcs_bmc = priv->client.dev;
>+	dev = priv->miscdev.this_device;
>+
>+	if (!count)
>+		return count;
>+
>+	if (count > 2)
>+		return -EINVAL;
>+
>+	if (*ppos >= 2)
>+		return -EINVAL;
>+
>+	if (*ppos + count > 2)
>+		return -EINVAL;
>+
>+	if (copy_from_user(data, buf, count))
>+		return -EFAULT;
>+
>+	write_odr = (*ppos == 0);
>+	write_str = (*ppos == 1) || (count == 2);
>+
>+	spin_lock_irq(&priv->queue.lock);
>+
>+	/* Always write status before data, we generate the SerIRQ by writing ODR */
>+	if (write_str) {
>+		/* The index of STR in the userspace buffer depends on whether ODR is written */
>+		str = data[*ppos == 0];
>+		if (!(str & KCS_BMC_STR_OBF))
>+			dev_warn(dev, "Clearing OBF with status write: 0x%x\n", str);
>+		dev_dbg(dev, "Writing status 0x%x\n", str);
>+		kcs_bmc_write_status(kcs_bmc, str);
>+	}
>+
>+	if (write_odr) {
>+		/* If we're writing ODR it's always the first byte in the buffer */
>+		u8 odr = data[0];
>+
>+		str = kcs_bmc_read_status(kcs_bmc);
>+		if (str & KCS_BMC_STR_OBF) {
>+			if (filp->f_flags & O_NONBLOCK) {
>+				result = -EWOULDBLOCK;
>+				goto out;
>+			}
>+
>+			priv->writable = kcs_bmc_raw_prepare_obe(priv);
>+
>+			/* Now either OBF is already clear, or we'll get an OBE event to wake us */
>+			dev_dbg(dev, "Waiting for OBF to clear\n");
>+			wait_event_interruptible_locked(priv->queue, priv->writable);
>+
>+			if (signal_pending(current)) {
>+				kcs_bmc_raw_update_event_mask(priv, KCS_BMC_EVENT_TYPE_OBE, 0);
>+				result = -EINTR;
>+				goto out;
>+			}
>+
>+			WARN_ON(kcs_bmc_read_status(kcs_bmc) & KCS_BMC_STR_OBF);
>+		}
>+
>+		dev_dbg(dev, "Writing 0x%x to ODR\n", odr);
>+		kcs_bmc_write_data(kcs_bmc, odr);
>+	}
>+
>+	result = count;
>+out:
>+	spin_unlock_irq(&priv->queue.lock);
>+
>+	return result;
>+}
>+
>+static int kcs_bmc_raw_release(struct inode *inode, struct file *filp)
>+{
>+	struct kcs_bmc_raw *priv = file_to_kcs_bmc_raw(filp);
>+
>+	kcs_bmc_disable_device(priv->client.dev, &priv->client);
>+
>+	return 0;
>+}
>+
>+static const struct file_operations kcs_bmc_raw_fops = {
>+	.owner          = THIS_MODULE,
>+	.open		= kcs_bmc_raw_open,
>+	.llseek		= no_seek_end_llseek,
>+	.read           = kcs_bmc_raw_read,
>+	.write          = kcs_bmc_raw_write,
>+	.poll		= kcs_bmc_raw_poll,
>+	.release	= kcs_bmc_raw_release,
>+};
>+
>+static DEFINE_SPINLOCK(kcs_bmc_raw_instances_lock);
>+static LIST_HEAD(kcs_bmc_raw_instances);
>+
>+static int kcs_bmc_raw_attach_cdev(struct kcs_bmc_device *kcs_bmc)
>+{
>+	struct kcs_bmc_raw *priv;
>+	int rc;
>+
>+	priv = devm_kzalloc(kcs_bmc->dev, sizeof(*priv), GFP_KERNEL);
>+	if (!priv)
>+		return -ENOMEM;
>+
>+	priv->client.dev = kcs_bmc;
>+	priv->client.ops = &kcs_bmc_raw_client_ops;
>+
>+	init_waitqueue_head(&priv->queue);
>+	priv->writable = false;
>+	priv->readable = false;
>+
>+	priv->miscdev.minor = MISC_DYNAMIC_MINOR;
>+	priv->miscdev.name = devm_kasprintf(kcs_bmc->dev, GFP_KERNEL, "%s%u", DEVICE_NAME,
>+					   kcs_bmc->channel);
>+	if (!priv->miscdev.name)
>+		return -EINVAL;
>+
>+	priv->miscdev.fops = &kcs_bmc_raw_fops;
>+
>+	/* Initialise our expected events. Listen for IBF but ignore OBE until necessary */
>+	kcs_bmc_raw_update_event_mask(priv, (KCS_BMC_EVENT_TYPE_IBF | KCS_BMC_EVENT_TYPE_OBE),
>+				      KCS_BMC_EVENT_TYPE_IBF);
>+
>+	rc = misc_register(&priv->miscdev);
>+	if (rc) {
>+		dev_err(kcs_bmc->dev, "Unable to register device\n");
>+		return rc;
>+	}
>+
>+	spin_lock_irq(&kcs_bmc_raw_instances_lock);
>+	list_add(&priv->entry, &kcs_bmc_raw_instances);
>+	spin_unlock_irq(&kcs_bmc_raw_instances_lock);
>+
>+	dev_info(kcs_bmc->dev, "Initialised raw client for channel %d", kcs_bmc->channel);
>+
>+	return 0;
>+}
>+
>+static int kcs_bmc_raw_detach_cdev(struct kcs_bmc_device *kcs_bmc)
>+{
>+	struct kcs_bmc_raw *priv = NULL, *pos;
>+
>+	spin_lock_irq(&kcs_bmc_raw_instances_lock);
>+	list_for_each_entry(pos, &kcs_bmc_raw_instances, entry) {
>+		if (pos->client.dev == kcs_bmc) {
>+			priv = pos;
>+			list_del(&pos->entry);
>+			break;
>+		}
>+	}
>+	spin_unlock_irq(&kcs_bmc_raw_instances_lock);
>+
>+	if (!priv)
>+		return 0;

Similarly to patch #12, might we want to indicate some sort of failure
here, or is this a normal/expected case?

>+
>+	misc_deregister(&priv->miscdev);
>+	kcs_bmc_disable_device(kcs_bmc, &priv->client);
>+	devm_kfree(priv->client.dev->dev, priv);
>+
>+	return 0;
>+}
>+
>+static const struct kcs_bmc_cdev_ops kcs_bmc_raw_cdev_ops = {
>+	.add_device = kcs_bmc_raw_attach_cdev,
>+	.remove_device = kcs_bmc_raw_detach_cdev,
>+};
>+
>+static struct kcs_bmc_cdev kcs_bmc_raw_cdev = {
>+	.ops = &kcs_bmc_raw_cdev_ops,
>+};
>+
>+static int kcs_bmc_raw_init(void)
>+{
>+	return kcs_bmc_register_cdev(&kcs_bmc_raw_cdev);
>+}
>+module_init(kcs_bmc_raw_init);
>+
>+static void kcs_bmc_raw_exit(void)
>+{
>+	int rc;
>+
>+	rc = kcs_bmc_unregister_cdev(&kcs_bmc_raw_cdev);
>+	if (rc)
>+		pr_warn("Failed to remove KCS BMC client: %d", rc);
>+}
>+module_exit(kcs_bmc_raw_exit);
>+
>+MODULE_LICENSE("GPL v2");
>+MODULE_AUTHOR("Andrew Jeffery <andrew@aj.id.au>");
>+MODULE_DESCRIPTION("Character device for raw access to a KCS device");
>-- 
>2.27.0
>

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

* Re: [PATCH v2 01/21] dt-bindings: aspeed-lpc: Remove LPC partitioning
  2021-04-09  3:18 ` [PATCH v2 01/21] dt-bindings: aspeed-lpc: Remove LPC partitioning Joel Stanley
@ 2021-04-09  5:24   ` Andrew Jeffery
  0 siblings, 0 replies; 67+ messages in thread
From: Andrew Jeffery @ 2021-04-09  5:24 UTC (permalink / raw)
  To: Joel Stanley
  Cc: devicetree, Linus Walleij, Ryan Chen, Tomer Maimon,
	Corey Minyard, Rob Herring, Avi Fishman, Patrick Venture,
	Chia-Wei, Wang, Linux Kernel Mailing List, Tali Perry,
	open list:GPIO SUBSYSTEM, Rob Herring, Linux ARM,
	openipmi-developer, Lee Jones, OpenBMC Maillist, linux-aspeed,
	Benjamin Fair



On Fri, 9 Apr 2021, at 12:48, Joel Stanley wrote:
> On Fri, 19 Mar 2021 at 06:28, Andrew Jeffery <andrew@aj.id.au> wrote:
> >
> > From: "Chia-Wei, Wang" <chiawei_wang@aspeedtech.com>
> >
> > The LPC controller has no concept of the BMC and the Host partitions.
> > This patch fixes the documentation by removing the description on LPC
> > partitions. The register offsets illustrated in the DTS node examples
> > are also fixed to adapt to the LPC DTS change.
> 
> Is this accurate:
> 
>  The node examples change their reg address to be an offset from the
> LPC HC to be an offset from the base of the LPC region.

Everything becomes based from the start of the LPC region, yes.

Andrew

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

* Re: [PATCH v2 07/21] ipmi: kcs_bmc: Make status update atomic
  2021-03-19  6:27 ` [PATCH v2 07/21] ipmi: kcs_bmc: Make status update atomic Andrew Jeffery
@ 2021-04-09  5:32   ` Zev Weiss
  0 siblings, 0 replies; 67+ messages in thread
From: Zev Weiss @ 2021-04-09  5:32 UTC (permalink / raw)
  To: Andrew Jeffery
  Cc: devicetree, chiawei_wang, ryan_chen, tmaimon77, minyard,
	avifishman70, venture, openbmc, linux-kernel, tali.perry1,
	linux-gpio, robh+dt, linux-arm-kernel, openipmi-developer,
	lee.jones, linus.walleij, linux-aspeed, benjaminfair

On Fri, Mar 19, 2021 at 01:27:38AM CDT, Andrew Jeffery wrote:
>Enable more efficient implementation of read-modify-write sequences.
>Both device drivers for the KCS BMC stack use regmaps. The new callback
>allows us to exploit regmap_update_bits().
>
>Signed-off-by: Andrew Jeffery <andrew@aj.id.au>

Reviewed-by: Zev Weiss <zweiss@equinix.com>

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

* Re: [PATCH v2 17/21] dt-bindings: ipmi: Convert ASPEED KCS binding to schema
  2021-04-09  5:15   ` Zev Weiss
@ 2021-04-09  5:33     ` Andrew Jeffery
  2021-04-09  5:44       ` Zev Weiss
  0 siblings, 1 reply; 67+ messages in thread
From: Andrew Jeffery @ 2021-04-09  5:33 UTC (permalink / raw)
  To: Zev Weiss
  Cc: devicetree, Chia-Wei, Wang, Ryan Chen, Tomer Maimon,
	Corey Minyard, Avi Fishman, Patrick Venture, openbmc,
	linux-kernel, Tali Perry, linux-gpio, Rob Herring,
	linux-arm-kernel, openipmi-developer, Lee Jones, Linus Walleij,
	linux-aspeed, Benjamin Fair



On Fri, 9 Apr 2021, at 14:45, Zev Weiss wrote:
> On Fri, Mar 19, 2021 at 01:27:48AM CDT, Andrew Jeffery wrote:
> >Given the deprecated binding, improve the ability to detect issues in
> >the platform devicetrees. Further, a subsequent patch will introduce a
> >new interrupts property for specifying SerIRQ behaviour, so convert
> >before we do any further additions.
> >
> >Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
> >---
> > .../bindings/ipmi/aspeed,ast2400-kcs-bmc.yaml | 92 +++++++++++++++++++
> > .../bindings/ipmi/aspeed-kcs-bmc.txt          | 33 -------
> > 2 files changed, 92 insertions(+), 33 deletions(-)
> > create mode 100644 Documentation/devicetree/bindings/ipmi/aspeed,ast2400-kcs-bmc.yaml
> > delete mode 100644 Documentation/devicetree/bindings/ipmi/aspeed-kcs-bmc.txt
> >
> >diff --git a/Documentation/devicetree/bindings/ipmi/aspeed,ast2400-kcs-bmc.yaml b/Documentation/devicetree/bindings/ipmi/aspeed,ast2400-kcs-bmc.yaml
> >new file mode 100644
> >index 000000000000..697ca575454f
> >--- /dev/null
> >+++ b/Documentation/devicetree/bindings/ipmi/aspeed,ast2400-kcs-bmc.yaml
> >@@ -0,0 +1,92 @@
> >+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> >+%YAML 1.2
> >+---
> >+$id: http://devicetree.org/schemas/ipmi/aspeed,ast2400-kcs-bmc.yaml
> >+$schema: http://devicetree.org/meta-schemas/core.yaml
> >+
> >+title: ASPEED BMC KCS Devices
> >+
> >+maintainers:
> >+  - Andrew Jeffery <andrew@aj.id.au>
> >+
> >+description: |
> >+  The Aspeed BMC SoCs typically use the Keyboard-Controller-Style (KCS)
> >+  interfaces on the LPC bus for in-band IPMI communication with their host.
> >+
> >+properties:
> >+  compatible:
> >+    oneOf:
> >+      - description: Channel ID derived from reg
> >+        items:
> >+          enum:
> >+            - aspeed,ast2400-kcs-bmc-v2
> >+            - aspeed,ast2500-kcs-bmc-v2
> >+            - aspeed,ast2600-kcs-bmc
> 
> Should this have a "-v2" suffix?

Well, that was kind of a matter of perspective. The 2600 compatible was 
added after we'd done the v2 of the binding for the 2400 and 2500 so it 
never needed correcting. But it is a case of "don't use the deprecated 
properties with the 2600 compatible".

I don't think a change is necessary?

Cheers,

Andrew

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

* Re: [PATCH v2 08/21] ipmi: kcs_bmc: Rename {read, write}_{status, data}() functions
  2021-03-19  6:27 ` [PATCH v2 08/21] ipmi: kcs_bmc: Rename {read, write}_{status, data}() functions Andrew Jeffery
@ 2021-04-09  5:33   ` Zev Weiss
  0 siblings, 0 replies; 67+ messages in thread
From: Zev Weiss @ 2021-04-09  5:33 UTC (permalink / raw)
  To: Andrew Jeffery
  Cc: devicetree, chiawei_wang, ryan_chen, tmaimon77, minyard,
	avifishman70, venture, openbmc, linux-kernel, tali.perry1,
	linux-gpio, robh+dt, linux-arm-kernel, openipmi-developer,
	lee.jones, linus.walleij, linux-aspeed, benjaminfair

On Fri, Mar 19, 2021 at 01:27:39AM CDT, Andrew Jeffery wrote:
>Rename the functions in preparation for separating the IPMI chardev out
>from the KCS BMC core.
>
>Signed-off-by: Andrew Jeffery <andrew@aj.id.au>

Reviewed-by: Zev Weiss <zweiss@equinix.com>

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

* Re: [PATCH v2 20/21] ipmi: kcs_bmc_aspeed: Fix IBFIE typo from datasheet
  2021-03-19  6:27 ` [PATCH v2 20/21] ipmi: kcs_bmc_aspeed: Fix IBFIE typo from datasheet Andrew Jeffery
@ 2021-04-09  5:40   ` Zev Weiss
  0 siblings, 0 replies; 67+ messages in thread
From: Zev Weiss @ 2021-04-09  5:40 UTC (permalink / raw)
  To: Andrew Jeffery
  Cc: devicetree, chiawei_wang, ryan_chen, tmaimon77, minyard,
	avifishman70, venture, openbmc, linux-kernel, tali.perry1,
	linux-gpio, robh+dt, linux-arm-kernel, openipmi-developer,
	lee.jones, linus.walleij, linux-aspeed, benjaminfair

On Fri, Mar 19, 2021 at 01:27:51AM CDT, Andrew Jeffery wrote:
>Input Buffer Full Interrupt Enable (IBFIE) is typoed as IBFIF for some
>registers in the datasheet. Fix the driver to use the sensible acronym.
>
>Signed-off-by: Andrew Jeffery <andrew@aj.id.au>

Reviewed-by: Zev Weiss <zweiss@equinix.com>

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

* Re: [PATCH v2 17/21] dt-bindings: ipmi: Convert ASPEED KCS binding to schema
  2021-04-09  5:33     ` Andrew Jeffery
@ 2021-04-09  5:44       ` Zev Weiss
  2021-04-09  8:46         ` Zev Weiss
  0 siblings, 1 reply; 67+ messages in thread
From: Zev Weiss @ 2021-04-09  5:44 UTC (permalink / raw)
  To: Andrew Jeffery, g
  Cc: devicetree, Chia-Wei, Wang, Ryan Chen, Tomer Maimon,
	Corey Minyard, Avi Fishman, Patrick Venture, openbmc,
	linux-kernel, Tali Perry, linux-gpio, Rob Herring,
	linux-arm-kernel, openipmi-developer, Lee Jones, Linus Walleij,
	linux-aspeed, Benjamin Fair

On Fri, Apr 09, 2021 at 12:33:10AM CDT, Andrew Jeffery wrote:
>
>
>On Fri, 9 Apr 2021, at 14:45, Zev Weiss wrote:
>> On Fri, Mar 19, 2021 at 01:27:48AM CDT, Andrew Jeffery wrote:
>> >Given the deprecated binding, improve the ability to detect issues in
>> >the platform devicetrees. Further, a subsequent patch will introduce a
>> >new interrupts property for specifying SerIRQ behaviour, so convert
>> >before we do any further additions.
>> >
>> >Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
>> >---
>> > .../bindings/ipmi/aspeed,ast2400-kcs-bmc.yaml | 92 +++++++++++++++++++
>> > .../bindings/ipmi/aspeed-kcs-bmc.txt          | 33 -------
>> > 2 files changed, 92 insertions(+), 33 deletions(-)
>> > create mode 100644 Documentation/devicetree/bindings/ipmi/aspeed,ast2400-kcs-bmc.yaml
>> > delete mode 100644 Documentation/devicetree/bindings/ipmi/aspeed-kcs-bmc.txt
>> >
>> >diff --git a/Documentation/devicetree/bindings/ipmi/aspeed,ast2400-kcs-bmc.yaml b/Documentation/devicetree/bindings/ipmi/aspeed,ast2400-kcs-bmc.yaml
>> >new file mode 100644
>> >index 000000000000..697ca575454f
>> >--- /dev/null
>> >+++ b/Documentation/devicetree/bindings/ipmi/aspeed,ast2400-kcs-bmc.yaml
>> >@@ -0,0 +1,92 @@
>> >+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
>> >+%YAML 1.2
>> >+---
>> >+$id: http://devicetree.org/schemas/ipmi/aspeed,ast2400-kcs-bmc.yaml
>> >+$schema: http://devicetree.org/meta-schemas/core.yaml
>> >+
>> >+title: ASPEED BMC KCS Devices
>> >+
>> >+maintainers:
>> >+  - Andrew Jeffery <andrew@aj.id.au>
>> >+
>> >+description: |
>> >+  The Aspeed BMC SoCs typically use the Keyboard-Controller-Style (KCS)
>> >+  interfaces on the LPC bus for in-band IPMI communication with their host.
>> >+
>> >+properties:
>> >+  compatible:
>> >+    oneOf:
>> >+      - description: Channel ID derived from reg
>> >+        items:
>> >+          enum:
>> >+            - aspeed,ast2400-kcs-bmc-v2
>> >+            - aspeed,ast2500-kcs-bmc-v2
>> >+            - aspeed,ast2600-kcs-bmc
>>
>> Should this have a "-v2" suffix?
>
>Well, that was kind of a matter of perspective. The 2600 compatible was
>added after we'd done the v2 of the binding for the 2400 and 2500 so it
>never needed correcting. But it is a case of "don't use the deprecated
>properties with the 2600 compatible".
>
>I don't think a change is necessary?
>

It just looked inconsistent with the corresponding string in the
ast_kcs_bmc_match[] table; perhaps that should be changed instead then?


Zev

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

* Re: [PATCH v2 09/21] ipmi: kcs_bmc: Split out kcs_bmc_cdev_ipmi
  2021-04-09  3:56   ` Zev Weiss
@ 2021-04-09  5:48     ` Andrew Jeffery
  2021-04-09 19:21       ` Zev Weiss
  0 siblings, 1 reply; 67+ messages in thread
From: Andrew Jeffery @ 2021-04-09  5:48 UTC (permalink / raw)
  To: Zev Weiss
  Cc: devicetree, Chia-Wei, Wang, Ryan Chen, Tomer Maimon,
	Corey Minyard, Avi Fishman, Patrick Venture, openbmc,
	linux-kernel, Tali Perry, linux-gpio, Rob Herring,
	linux-arm-kernel, openipmi-developer, Lee Jones, Linus Walleij,
	linux-aspeed, Benjamin Fair



On Fri, 9 Apr 2021, at 13:26, Zev Weiss wrote:
> On Fri, Mar 19, 2021 at 01:27:40AM CDT, Andrew Jeffery wrote:
> >Take steps towards defining a coherent API to separate the KCS device
> >drivers from the userspace interface. Decreasing the coupling will
> >improve the separation of concerns and enable the introduction of
> >alternative userspace interfaces.
> >
> >For now, simply split the chardev logic out to a separate file. The code
> >continues to build into the same module.
> >
> >Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
> >---
> > drivers/char/ipmi/Makefile            |   2 +-
> > drivers/char/ipmi/kcs_bmc.c           | 423 +------------------------
> > drivers/char/ipmi/kcs_bmc.h           |  10 +-
> > drivers/char/ipmi/kcs_bmc_cdev_ipmi.c | 428 ++++++++++++++++++++++++++
> > 4 files changed, 451 insertions(+), 412 deletions(-)
> > create mode 100644 drivers/char/ipmi/kcs_bmc_cdev_ipmi.c
> >
> >diff --git a/drivers/char/ipmi/Makefile b/drivers/char/ipmi/Makefile
> >index 0822adc2ec41..a302bc865370 100644
> >--- a/drivers/char/ipmi/Makefile
> >+++ b/drivers/char/ipmi/Makefile
> >@@ -22,7 +22,7 @@ obj-$(CONFIG_IPMI_SSIF) += ipmi_ssif.o
> > obj-$(CONFIG_IPMI_POWERNV) += ipmi_powernv.o
> > obj-$(CONFIG_IPMI_WATCHDOG) += ipmi_watchdog.o
> > obj-$(CONFIG_IPMI_POWEROFF) += ipmi_poweroff.o
> >-obj-$(CONFIG_IPMI_KCS_BMC) += kcs_bmc.o
> >+obj-$(CONFIG_IPMI_KCS_BMC) += kcs_bmc.o kcs_bmc_cdev_ipmi.o
> > obj-$(CONFIG_ASPEED_BT_IPMI_BMC) += bt-bmc.o
> > obj-$(CONFIG_ASPEED_KCS_IPMI_BMC) += kcs_bmc_aspeed.o
> > obj-$(CONFIG_NPCM7XX_KCS_IPMI_BMC) += kcs_bmc_npcm7xx.o
> >diff --git a/drivers/char/ipmi/kcs_bmc.c b/drivers/char/ipmi/kcs_bmc.c
> >index c4336c1f2d6d..ef5c48ffe74a 100644
> >--- a/drivers/char/ipmi/kcs_bmc.c
> >+++ b/drivers/char/ipmi/kcs_bmc.c
> >@@ -3,446 +3,51 @@
> >  * Copyright (c) 2015-2018, Intel Corporation.
> >  */
> >
> >-#define pr_fmt(fmt) "kcs-bmc: " fmt
> >-
> >-#include <linux/errno.h>
> >-#include <linux/io.h>
> >-#include <linux/ipmi_bmc.h>
> > #include <linux/module.h>
> >-#include <linux/platform_device.h>
> >-#include <linux/poll.h>
> >-#include <linux/sched.h>
> >-#include <linux/slab.h>
> >
> > #include "kcs_bmc.h"
> >
> >-#define DEVICE_NAME "ipmi-kcs"
> >-
> >-#define KCS_MSG_BUFSIZ    1000
> >-
> >-#define KCS_ZERO_DATA     0
> >-
> >-
> >-/* IPMI 2.0 - Table 9-1, KCS Interface Status Register Bits */
> >-#define KCS_STATUS_STATE(state) (state << 6)
> >-#define KCS_STATUS_STATE_MASK   GENMASK(7, 6)
> >-#define KCS_STATUS_CMD_DAT      BIT(3)
> >-#define KCS_STATUS_SMS_ATN      BIT(2)
> >-#define KCS_STATUS_IBF          BIT(1)
> >-#define KCS_STATUS_OBF          BIT(0)
> >-
> >-/* IPMI 2.0 - Table 9-2, KCS Interface State Bits */
> >-enum kcs_states {
> >-	IDLE_STATE  = 0,
> >-	READ_STATE  = 1,
> >-	WRITE_STATE = 2,
> >-	ERROR_STATE = 3,
> >-};
> >-
> >-/* IPMI 2.0 - Table 9-3, KCS Interface Control Codes */
> >-#define KCS_CMD_GET_STATUS_ABORT  0x60
> >-#define KCS_CMD_WRITE_START       0x61
> >-#define KCS_CMD_WRITE_END         0x62
> >-#define KCS_CMD_READ_BYTE         0x68
> >-
> >-static inline u8 kcs_bmc_read_data(struct kcs_bmc *kcs_bmc)
> >+u8 kcs_bmc_read_data(struct kcs_bmc *kcs_bmc)
> > {
> > 	return kcs_bmc->io_inputb(kcs_bmc, kcs_bmc->ioreg.idr);
> > }
> >+EXPORT_SYMBOL(kcs_bmc_read_data);
> >
> >-static inline void kcs_bmc_write_data(struct kcs_bmc *kcs_bmc, u8 data)
> >+void kcs_bmc_write_data(struct kcs_bmc *kcs_bmc, u8 data)
> > {
> > 	kcs_bmc->io_outputb(kcs_bmc, kcs_bmc->ioreg.odr, data);
> > }
> >+EXPORT_SYMBOL(kcs_bmc_write_data);
> >
> >-static inline u8 kcs_bmc_read_status(struct kcs_bmc *kcs_bmc)
> >+u8 kcs_bmc_read_status(struct kcs_bmc *kcs_bmc)
> > {
> > 	return kcs_bmc->io_inputb(kcs_bmc, kcs_bmc->ioreg.str);
> > }
> >+EXPORT_SYMBOL(kcs_bmc_read_status);
> >
> >-static inline void kcs_bmc_write_status(struct kcs_bmc *kcs_bmc, u8 data)
> >+void kcs_bmc_write_status(struct kcs_bmc *kcs_bmc, u8 data)
> > {
> > 	kcs_bmc->io_outputb(kcs_bmc, kcs_bmc->ioreg.str, data);
> > }
> >+EXPORT_SYMBOL(kcs_bmc_write_status);
> >
> >-static void kcs_bmc_update_status(struct kcs_bmc *kcs_bmc, u8 mask, u8 val)
> >+void kcs_bmc_update_status(struct kcs_bmc *kcs_bmc, u8 mask, u8 val)
> > {
> > 	kcs_bmc->io_updateb(kcs_bmc, kcs_bmc->ioreg.str, mask, val);
> > }
> >+EXPORT_SYMBOL(kcs_bmc_update_status);
> >
> >-static inline void set_state(struct kcs_bmc *kcs_bmc, u8 state)
> >-{
> >-	kcs_bmc_update_status(kcs_bmc, KCS_STATUS_STATE_MASK,
> >-					KCS_STATUS_STATE(state));
> >-}
> >-
> >-static void kcs_force_abort(struct kcs_bmc *kcs_bmc)
> >-{
> >-	set_state(kcs_bmc, ERROR_STATE);
> >-	kcs_bmc_read_data(kcs_bmc);
> >-	kcs_bmc_write_data(kcs_bmc, KCS_ZERO_DATA);
> >-
> >-	kcs_bmc->phase = KCS_PHASE_ERROR;
> >-	kcs_bmc->data_in_avail = false;
> >-	kcs_bmc->data_in_idx = 0;
> >-}
> >-
> >-static void kcs_bmc_handle_data(struct kcs_bmc *kcs_bmc)
> >-{
> >-	u8 data;
> >-
> >-	switch (kcs_bmc->phase) {
> >-	case KCS_PHASE_WRITE_START:
> >-		kcs_bmc->phase = KCS_PHASE_WRITE_DATA;
> >-		fallthrough;
> >-
> >-	case KCS_PHASE_WRITE_DATA:
> >-		if (kcs_bmc->data_in_idx < KCS_MSG_BUFSIZ) {
> >-			set_state(kcs_bmc, WRITE_STATE);
> >-			kcs_bmc_write_data(kcs_bmc, KCS_ZERO_DATA);
> >-			kcs_bmc->data_in[kcs_bmc->data_in_idx++] =
> >-						kcs_bmc_read_data(kcs_bmc);
> >-		} else {
> >-			kcs_force_abort(kcs_bmc);
> >-			kcs_bmc->error = KCS_LENGTH_ERROR;
> >-		}
> >-		break;
> >-
> >-	case KCS_PHASE_WRITE_END_CMD:
> >-		if (kcs_bmc->data_in_idx < KCS_MSG_BUFSIZ) {
> >-			set_state(kcs_bmc, READ_STATE);
> >-			kcs_bmc->data_in[kcs_bmc->data_in_idx++] =
> >-						kcs_bmc_read_data(kcs_bmc);
> >-			kcs_bmc->phase = KCS_PHASE_WRITE_DONE;
> >-			kcs_bmc->data_in_avail = true;
> >-			wake_up_interruptible(&kcs_bmc->queue);
> >-		} else {
> >-			kcs_force_abort(kcs_bmc);
> >-			kcs_bmc->error = KCS_LENGTH_ERROR;
> >-		}
> >-		break;
> >-
> >-	case KCS_PHASE_READ:
> >-		if (kcs_bmc->data_out_idx == kcs_bmc->data_out_len)
> >-			set_state(kcs_bmc, IDLE_STATE);
> >-
> >-		data = kcs_bmc_read_data(kcs_bmc);
> >-		if (data != KCS_CMD_READ_BYTE) {
> >-			set_state(kcs_bmc, ERROR_STATE);
> >-			kcs_bmc_write_data(kcs_bmc, KCS_ZERO_DATA);
> >-			break;
> >-		}
> >-
> >-		if (kcs_bmc->data_out_idx == kcs_bmc->data_out_len) {
> >-			kcs_bmc_write_data(kcs_bmc, KCS_ZERO_DATA);
> >-			kcs_bmc->phase = KCS_PHASE_IDLE;
> >-			break;
> >-		}
> >-
> >-		kcs_bmc_write_data(kcs_bmc,
> >-			kcs_bmc->data_out[kcs_bmc->data_out_idx++]);
> >-		break;
> >-
> >-	case KCS_PHASE_ABORT_ERROR1:
> >-		set_state(kcs_bmc, READ_STATE);
> >-		kcs_bmc_read_data(kcs_bmc);
> >-		kcs_bmc_write_data(kcs_bmc, kcs_bmc->error);
> >-		kcs_bmc->phase = KCS_PHASE_ABORT_ERROR2;
> >-		break;
> >-
> >-	case KCS_PHASE_ABORT_ERROR2:
> >-		set_state(kcs_bmc, IDLE_STATE);
> >-		kcs_bmc_read_data(kcs_bmc);
> >-		kcs_bmc_write_data(kcs_bmc, KCS_ZERO_DATA);
> >-		kcs_bmc->phase = KCS_PHASE_IDLE;
> >-		break;
> >-
> >-	default:
> >-		kcs_force_abort(kcs_bmc);
> >-		break;
> >-	}
> >-}
> >-
> >-static void kcs_bmc_handle_cmd(struct kcs_bmc *kcs_bmc)
> >-{
> >-	u8 cmd;
> >-
> >-	set_state(kcs_bmc, WRITE_STATE);
> >-	kcs_bmc_write_data(kcs_bmc, KCS_ZERO_DATA);
> >-
> >-	cmd = kcs_bmc_read_data(kcs_bmc);
> >-	switch (cmd) {
> >-	case KCS_CMD_WRITE_START:
> >-		kcs_bmc->phase = KCS_PHASE_WRITE_START;
> >-		kcs_bmc->error = KCS_NO_ERROR;
> >-		kcs_bmc->data_in_avail = false;
> >-		kcs_bmc->data_in_idx = 0;
> >-		break;
> >-
> >-	case KCS_CMD_WRITE_END:
> >-		if (kcs_bmc->phase != KCS_PHASE_WRITE_DATA) {
> >-			kcs_force_abort(kcs_bmc);
> >-			break;
> >-		}
> >-
> >-		kcs_bmc->phase = KCS_PHASE_WRITE_END_CMD;
> >-		break;
> >-
> >-	case KCS_CMD_GET_STATUS_ABORT:
> >-		if (kcs_bmc->error == KCS_NO_ERROR)
> >-			kcs_bmc->error = KCS_ABORTED_BY_COMMAND;
> >-
> >-		kcs_bmc->phase = KCS_PHASE_ABORT_ERROR1;
> >-		kcs_bmc->data_in_avail = false;
> >-		kcs_bmc->data_in_idx = 0;
> >-		break;
> >-
> >-	default:
> >-		kcs_force_abort(kcs_bmc);
> >-		kcs_bmc->error = KCS_ILLEGAL_CONTROL_CODE;
> >-		break;
> >-	}
> >-}
> >-
> >+int kcs_bmc_ipmi_event(struct kcs_bmc *kcs_bmc);
> 
> This declaration looks a bit out of place here; should it be in
> kcs_bmc.h instead?

These are only temporary and get removed later on in the series after 
some shuffling of the code.

Andrew

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

* Re: [PATCH v2 10/21] ipmi: kcs_bmc: Turn the driver data-structures inside-out
  2021-04-09  3:57   ` Zev Weiss
@ 2021-04-09  5:59     ` Andrew Jeffery
  2021-04-09  6:25       ` Zev Weiss
  0 siblings, 1 reply; 67+ messages in thread
From: Andrew Jeffery @ 2021-04-09  5:59 UTC (permalink / raw)
  To: Zev Weiss
  Cc: devicetree, Chia-Wei, Wang, Ryan Chen, Tomer Maimon,
	Corey Minyard, Avi Fishman, Patrick Venture, openbmc,
	linux-kernel, Tali Perry, linux-gpio, Rob Herring,
	linux-arm-kernel, openipmi-developer, Lee Jones, Linus Walleij,
	linux-aspeed, Benjamin Fair



On Fri, 9 Apr 2021, at 13:27, Zev Weiss wrote:
> On Fri, Mar 19, 2021 at 01:27:41AM CDT, Andrew Jeffery wrote:
> >Make the KCS device drivers responsible for allocating their own memory.
> >
> >Until now the private data for the device driver was allocated internal
> >to the private data for the chardev interface. This coupling required
> >the slightly awkward API of passing through the struct size for the
> >driver private data to the chardev constructor, and then retrieving a
> >pointer to the driver private data from the allocated chardev memory.
> >
> >In addition to being awkward, the arrangement prevents the
> >implementation of alternative userspace interfaces as the device driver
> >private data is not independent.
> >
> >Peel a layer off the onion and turn the data-structures inside out by
> >exploiting container_of() and embedding `struct kcs_device` in the
> >driver private data.
> >
> >Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
> >---
> > drivers/char/ipmi/kcs_bmc.c           | 15 +++++--
> > drivers/char/ipmi/kcs_bmc.h           | 12 ++----
> > drivers/char/ipmi/kcs_bmc_aspeed.c    | 60 ++++++++++++++++-----------
> > drivers/char/ipmi/kcs_bmc_cdev_ipmi.c | 60 ++++++++++++++++++---------
> > drivers/char/ipmi/kcs_bmc_npcm7xx.c   | 37 ++++++++++-------
> > 5 files changed, 113 insertions(+), 71 deletions(-)
> >
> >diff --git a/drivers/char/ipmi/kcs_bmc.c b/drivers/char/ipmi/kcs_bmc.c
> >index ef5c48ffe74a..709b6bdec165 100644
> >--- a/drivers/char/ipmi/kcs_bmc.c
> >+++ b/drivers/char/ipmi/kcs_bmc.c
> >@@ -44,12 +44,19 @@ int kcs_bmc_handle_event(struct kcs_bmc *kcs_bmc)
> > }
> > EXPORT_SYMBOL(kcs_bmc_handle_event);
> >
> >-struct kcs_bmc *kcs_bmc_ipmi_alloc(struct device *dev, int sizeof_priv, u32 channel);
> >-struct kcs_bmc *kcs_bmc_alloc(struct device *dev, int sizeof_priv, u32 channel)
> >+int kcs_bmc_ipmi_attach_cdev(struct kcs_bmc *kcs_bmc);
> 
> Another declaration perhaps intended for kcs_bmc.h?

These are temporary while the code gets shuffled around. The symbol 
name is an implementation detail, not a "public" part of the API; after 
some further shuffling these are eventually assigned as callbacks in an 
ops struct.

> 
> >+int kcs_bmc_add_device(struct kcs_bmc *kcs_bmc)
> > {
> >-	return kcs_bmc_ipmi_alloc(dev, sizeof_priv, channel);
> >+	return kcs_bmc_ipmi_attach_cdev(kcs_bmc);
> > }
> >-EXPORT_SYMBOL(kcs_bmc_alloc);
> >+EXPORT_SYMBOL(kcs_bmc_add_device);
> >+
> >+int kcs_bmc_ipmi_detach_cdev(struct kcs_bmc *kcs_bmc);
> 
> Here too.
> 
> >+int kcs_bmc_remove_device(struct kcs_bmc *kcs_bmc)
> >+{
> >+	return kcs_bmc_ipmi_detach_cdev(kcs_bmc);
> >+}
> >+EXPORT_SYMBOL(kcs_bmc_remove_device);
> >
> > MODULE_LICENSE("GPL v2");
> > MODULE_AUTHOR("Haiyue Wang <haiyue.wang@linux.intel.com>");
> >diff --git a/drivers/char/ipmi/kcs_bmc.h b/drivers/char/ipmi/kcs_bmc.h
> >index febea0c8deb4..bf0ae327997f 100644
> >--- a/drivers/char/ipmi/kcs_bmc.h
> >+++ b/drivers/char/ipmi/kcs_bmc.h
> >@@ -67,6 +67,8 @@ struct kcs_ioreg {
> > };
> >
> > struct kcs_bmc {
> >+	struct device *dev;
> >+
> > 	spinlock_t lock;
> >
> > 	u32 channel;
> >@@ -94,17 +96,11 @@ struct kcs_bmc {
> > 	u8 *kbuffer;
> >
> > 	struct miscdevice miscdev;
> >-
> >-	unsigned long priv[];
> > };
> >
> >-static inline void *kcs_bmc_priv(struct kcs_bmc *kcs_bmc)
> >-{
> >-	return kcs_bmc->priv;
> >-}
> >-
> > int kcs_bmc_handle_event(struct kcs_bmc *kcs_bmc);
> >-struct kcs_bmc *kcs_bmc_alloc(struct device *dev, int sizeof_priv, u32 channel);
> >+int kcs_bmc_add_device(struct kcs_bmc *kcs_bmc);
> >+int kcs_bmc_remove_device(struct kcs_bmc *kcs_bmc);
> >
> > u8 kcs_bmc_read_data(struct kcs_bmc *kcs_bmc);
> > void kcs_bmc_write_data(struct kcs_bmc *kcs_bmc, u8 data);
> >diff --git a/drivers/char/ipmi/kcs_bmc_aspeed.c b/drivers/char/ipmi/kcs_bmc_aspeed.c
> >index 630cf095560e..0416ac78ce68 100644
> >--- a/drivers/char/ipmi/kcs_bmc_aspeed.c
> >+++ b/drivers/char/ipmi/kcs_bmc_aspeed.c
> >@@ -61,6 +61,8 @@
> > #define LPC_STR4             0x11C
> >
> > struct aspeed_kcs_bmc {
> >+	struct kcs_bmc kcs_bmc;
> >+
> > 	struct regmap *map;
> > };
> >
> >@@ -69,9 +71,14 @@ struct aspeed_kcs_of_ops {
> > 	int (*get_io_address)(struct platform_device *pdev);
> > };
> >
> >+static inline struct aspeed_kcs_bmc *to_aspeed_kcs_bmc(struct kcs_bmc *kcs_bmc)
> >+{
> >+	return container_of(kcs_bmc, struct aspeed_kcs_bmc, kcs_bmc);
> >+}
> >+
> > static u8 aspeed_kcs_inb(struct kcs_bmc *kcs_bmc, u32 reg)
> > {
> >-	struct aspeed_kcs_bmc *priv = kcs_bmc_priv(kcs_bmc);
> >+	struct aspeed_kcs_bmc *priv = to_aspeed_kcs_bmc(kcs_bmc);
> > 	u32 val = 0;
> > 	int rc;
> >
> >@@ -83,7 +90,7 @@ static u8 aspeed_kcs_inb(struct kcs_bmc *kcs_bmc, u32 reg)
> >
> > static void aspeed_kcs_outb(struct kcs_bmc *kcs_bmc, u32 reg, u8 data)
> > {
> >-	struct aspeed_kcs_bmc *priv = kcs_bmc_priv(kcs_bmc);
> >+	struct aspeed_kcs_bmc *priv = to_aspeed_kcs_bmc(kcs_bmc);
> > 	int rc;
> >
> > 	rc = regmap_write(priv->map, reg, data);
> >@@ -92,7 +99,7 @@ static void aspeed_kcs_outb(struct kcs_bmc *kcs_bmc, u32 reg, u8 data)
> >
> > static void aspeed_kcs_updateb(struct kcs_bmc *kcs_bmc, u32 reg, u8 mask, u8 val)
> > {
> >-	struct aspeed_kcs_bmc *priv = kcs_bmc_priv(kcs_bmc);
> >+	struct aspeed_kcs_bmc *priv = to_aspeed_kcs_bmc(kcs_bmc);
> > 	int rc;
> >
> > 	rc = regmap_update_bits(priv->map, reg, mask, val);
> >@@ -114,7 +121,7 @@ static void aspeed_kcs_updateb(struct kcs_bmc *kcs_bmc, u32 reg, u8 mask, u8 val
> >  */
> > static void aspeed_kcs_set_address(struct kcs_bmc *kcs_bmc, u16 addr)
> > {
> >-	struct aspeed_kcs_bmc *priv = kcs_bmc_priv(kcs_bmc);
> >+	struct aspeed_kcs_bmc *priv = to_aspeed_kcs_bmc(kcs_bmc);
> >
> > 	switch (kcs_bmc->channel) {
> > 	case 1:
> >@@ -148,7 +155,7 @@ static void aspeed_kcs_set_address(struct kcs_bmc *kcs_bmc, u16 addr)
> >
> > static void aspeed_kcs_enable_channel(struct kcs_bmc *kcs_bmc, bool enable)
> > {
> >-	struct aspeed_kcs_bmc *priv = kcs_bmc_priv(kcs_bmc);
> >+	struct aspeed_kcs_bmc *priv = to_aspeed_kcs_bmc(kcs_bmc);
> >
> > 	switch (kcs_bmc->channel) {
> > 	case 1:
> >@@ -323,16 +330,16 @@ static int aspeed_kcs_of_v2_get_io_address(struct platform_device *pdev)
> > static int aspeed_kcs_probe(struct platform_device *pdev)
> > {
> > 	const struct aspeed_kcs_of_ops *ops;
> >-	struct device *dev = &pdev->dev;
> >+	struct aspeed_kcs_bmc *priv;
> > 	struct kcs_bmc *kcs_bmc;
> > 	struct device_node *np;
> > 	int rc, channel, addr;
> >
> >-	np = dev->of_node->parent;
> >+	np = pdev->dev.of_node->parent;
> > 	if (!of_device_is_compatible(np, "aspeed,ast2400-lpc-v2") &&
> > 	    !of_device_is_compatible(np, "aspeed,ast2500-lpc-v2") &&
> > 	    !of_device_is_compatible(np, "aspeed,ast2600-lpc-v2")) {
> >-		dev_err(dev, "unsupported LPC device binding\n");
> >+		dev_err(&pdev->dev, "unsupported LPC device binding\n");
> > 		return -ENODEV;
> > 	}
> > 	ops = of_device_get_match_data(&pdev->dev);
> >@@ -343,18 +350,27 @@ static int aspeed_kcs_probe(struct platform_device *pdev)
> > 	if (channel < 0)
> > 		return channel;
> >
> >-	kcs_bmc = kcs_bmc_alloc(&pdev->dev, sizeof(struct aspeed_kcs_bmc), channel);
> >-	if (!kcs_bmc)
> >+	addr = ops->get_io_address(pdev);
> >+	if (addr < 0)
> >+		return addr;
> >+
> >+	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
> >+	if (!priv)
> > 		return -ENOMEM;
> >
> >+	kcs_bmc = &priv->kcs_bmc;
> >+	kcs_bmc->dev = &pdev->dev;
> >+	kcs_bmc->channel = channel;
> > 	kcs_bmc->ioreg = ast_kcs_bmc_ioregs[channel - 1];
> > 	kcs_bmc->io_inputb = aspeed_kcs_inb;
> > 	kcs_bmc->io_outputb = aspeed_kcs_outb;
> > 	kcs_bmc->io_updateb = aspeed_kcs_updateb;
> >
> >-	addr = ops->get_io_address(pdev);
> >-	if (addr < 0)
> >-		return addr;
> >+	priv->map = syscon_node_to_regmap(pdev->dev.parent->of_node);
> >+	if (IS_ERR(priv->map)) {
> >+		dev_err(&pdev->dev, "Couldn't get regmap\n");
> >+		return -ENODEV;
> >+	}
> 
> The reanimated priv->map initialization I suspect wasn't meant to
> have been removed in the first place...

Yeah, I'll have to go back and figure out what went wrong there!

Thanks for catching that.

> 
> >
> > 	aspeed_kcs_set_address(kcs_bmc, addr);
> >
> >@@ -362,29 +378,25 @@ static int aspeed_kcs_probe(struct platform_device *pdev)
> > 	if (rc)
> > 		return rc;
> >
> >-	dev_set_drvdata(dev, kcs_bmc);
> >+	platform_set_drvdata(pdev, priv);
> >
> > 	aspeed_kcs_enable_channel(kcs_bmc, true);
> >
> >-	rc = misc_register(&kcs_bmc->miscdev);
> >-	if (rc) {
> >-		dev_err(dev, "Unable to register device\n");
> >+	rc = kcs_bmc_add_device(&priv->kcs_bmc);
> >+	if (rc < 0)
> > 		return rc;
> >-	}
> >
> >-	dev_dbg(&pdev->dev,
> >-		"Probed KCS device %d (IDR=0x%x, ODR=0x%x, STR=0x%x)\n",
> >-		kcs_bmc->channel, kcs_bmc->ioreg.idr, kcs_bmc->ioreg.odr,
> >-		kcs_bmc->ioreg.str);
> >+	dev_info(&pdev->dev, "Initialised channel %d at 0x%x\n", kcs_bmc->channel, addr);
> 
> Is the dbg->info change here intentional?  (I have no particular
> objection if so, but it's often a change I make myself during
> testing/debugging and then forget to revert...)

Yeah, it was possibly something I forgot to revert. If others have 
issues with it staying at dev_info() I'll switch it back.

> 
> >
> > 	return 0;
> > }
> >
> > static int aspeed_kcs_remove(struct platform_device *pdev)
> > {
> >-	struct kcs_bmc *kcs_bmc = dev_get_drvdata(&pdev->dev);
> >+	struct aspeed_kcs_bmc *priv = platform_get_drvdata(pdev);
> >+	struct kcs_bmc *kcs_bmc = &priv->kcs_bmc;
> >
> >-	misc_deregister(&kcs_bmc->miscdev);
> >+	kcs_bmc_remove_device(kcs_bmc);
> 
> Should we propagate the return value outward here?

Probably!

> 
> >
> > 	return 0;
> > }
> >diff --git a/drivers/char/ipmi/kcs_bmc_cdev_ipmi.c b/drivers/char/ipmi/kcs_bmc_cdev_ipmi.c
> >index 82c77994e481..0ca71c135a1a 100644
> >--- a/drivers/char/ipmi/kcs_bmc_cdev_ipmi.c
> >+++ b/drivers/char/ipmi/kcs_bmc_cdev_ipmi.c
> >@@ -382,7 +382,7 @@ static int kcs_bmc_ipmi_release(struct inode *inode, struct file *filp)
> > 	return 0;
> > }
> >
> >-static const struct file_operations kcs_bmc_fops = {
> >+static const struct file_operations kcs_bmc_ipmi_fops = {
> > 	.owner          = THIS_MODULE,
> > 	.open           = kcs_bmc_ipmi_open,
> > 	.read           = kcs_bmc_ipmi_read,
> >@@ -392,36 +392,58 @@ static const struct file_operations kcs_bmc_fops = {
> > 	.unlocked_ioctl = kcs_bmc_ipmi_ioctl,
> > };
> >
> >-struct kcs_bmc *kcs_bmc_ipmi_alloc(struct device *dev, int sizeof_priv, u32 channel);
> >-struct kcs_bmc *kcs_bmc_ipmi_alloc(struct device *dev, int sizeof_priv, u32 channel)
> >+int kcs_bmc_ipmi_attach_cdev(struct kcs_bmc *kcs_bmc);
> 
> Errant declaration again?

As previously explained.

> 
> >+int kcs_bmc_ipmi_attach_cdev(struct kcs_bmc *kcs_bmc)
> > {
> >-	struct kcs_bmc *kcs_bmc;
> >-
> >-	kcs_bmc = devm_kzalloc(dev, sizeof(*kcs_bmc) + sizeof_priv, GFP_KERNEL);
> >-	if (!kcs_bmc)
> >-		return NULL;
> >+	int rc;
> >
> > 	spin_lock_init(&kcs_bmc->lock);
> >-	kcs_bmc->channel = channel;
> >-
> > 	mutex_init(&kcs_bmc->mutex);
> > 	init_waitqueue_head(&kcs_bmc->queue);
> >
> >-	kcs_bmc->data_in = devm_kmalloc(dev, KCS_MSG_BUFSIZ, GFP_KERNEL);
> >-	kcs_bmc->data_out = devm_kmalloc(dev, KCS_MSG_BUFSIZ, GFP_KERNEL);
> >-	kcs_bmc->kbuffer = devm_kmalloc(dev, KCS_MSG_BUFSIZ, GFP_KERNEL);
> >+	kcs_bmc->data_in = devm_kmalloc(kcs_bmc->dev, KCS_MSG_BUFSIZ, GFP_KERNEL);
> >+	kcs_bmc->data_out = devm_kmalloc(kcs_bmc->dev, KCS_MSG_BUFSIZ, GFP_KERNEL);
> >+	kcs_bmc->kbuffer = devm_kmalloc(kcs_bmc->dev, KCS_MSG_BUFSIZ, GFP_KERNEL);
> >
> > 	kcs_bmc->miscdev.minor = MISC_DYNAMIC_MINOR;
> >-	kcs_bmc->miscdev.name = devm_kasprintf(dev, GFP_KERNEL, "%s%u",
> >-					       DEVICE_NAME, channel);
> >+	kcs_bmc->miscdev.name = devm_kasprintf(kcs_bmc->dev, GFP_KERNEL, "%s%u",
> >+					       DEVICE_NAME, kcs_bmc->channel);
> > 	if (!kcs_bmc->data_in || !kcs_bmc->data_out || !kcs_bmc->kbuffer ||
> > 	    !kcs_bmc->miscdev.name)
> >-		return NULL;
> >-	kcs_bmc->miscdev.fops = &kcs_bmc_fops;
> >+		return -ENOMEM;
> >
> >-	return kcs_bmc;
> >+	kcs_bmc->miscdev.fops = &kcs_bmc_ipmi_fops;
> >+
> >+	rc = misc_register(&kcs_bmc->miscdev);
> >+	if (rc) {
> >+		dev_err(kcs_bmc->dev, "Unable to register device: %d\n", rc);
> >+		return rc;
> >+	}
> >+
> >+	dev_info(kcs_bmc->dev, "Initialised IPMI client for channel %d", kcs_bmc->channel);
> >+
> >+	return 0;
> >+}
> >+EXPORT_SYMBOL(kcs_bmc_ipmi_attach_cdev);
> >+
> >+int kcs_bmc_ipmi_detach_cdev(struct kcs_bmc *kcs_bmc);
> 
> Same here.

Same explanation.

> 
> >+int kcs_bmc_ipmi_detach_cdev(struct kcs_bmc *kcs_bmc)
> >+{
> >+	misc_deregister(&kcs_bmc->miscdev);
> >+
> >+	spin_lock_irq(&kcs_bmc->lock);
> >+	kcs_bmc->running = 0;
> >+	kcs_bmc_ipmi_force_abort(kcs_bmc);
> >+	spin_unlock_irq(&kcs_bmc->lock);
> >+
> >+	devm_kfree(kcs_bmc->dev, kcs_bmc->kbuffer);
> >+	devm_kfree(kcs_bmc->dev, kcs_bmc->data_out);
> >+	devm_kfree(kcs_bmc->dev, kcs_bmc->data_in);
> >+	devm_kfree(kcs_bmc->dev, kcs_bmc);
> >+
> >+	return 0;
> > }
> >-EXPORT_SYMBOL(kcs_bmc_ipmi_alloc);
> >+EXPORT_SYMBOL(kcs_bmc_ipmi_detach_cdev);
> >
> > MODULE_LICENSE("GPL v2");
> > MODULE_AUTHOR("Haiyue Wang <haiyue.wang@linux.intel.com>");
> >diff --git a/drivers/char/ipmi/kcs_bmc_npcm7xx.c b/drivers/char/ipmi/kcs_bmc_npcm7xx.c
> >index 1f44aadec9e8..5d017498dc69 100644
> >--- a/drivers/char/ipmi/kcs_bmc_npcm7xx.c
> >+++ b/drivers/char/ipmi/kcs_bmc_npcm7xx.c
> >@@ -65,6 +65,8 @@ struct npcm7xx_kcs_reg {
> > };
> >
> > struct npcm7xx_kcs_bmc {
> >+	struct kcs_bmc kcs_bmc;
> >+
> > 	struct regmap *map;
> >
> > 	const struct npcm7xx_kcs_reg *reg;
> >@@ -76,9 +78,14 @@ static const struct npcm7xx_kcs_reg npcm7xx_kcs_reg_tbl[KCS_CHANNEL_MAX] = {
> > 	{ .sts = KCS3ST, .dob = KCS3DO, .dib = KCS3DI, .ctl = KCS3CTL, .ie = KCS3IE },
> > };
> >
> >+static inline struct npcm7xx_kcs_bmc *to_npcm7xx_kcs_bmc(struct kcs_bmc *kcs_bmc)
> >+{
> >+	return container_of(kcs_bmc, struct npcm7xx_kcs_bmc, kcs_bmc);
> >+}
> >+
> > static u8 npcm7xx_kcs_inb(struct kcs_bmc *kcs_bmc, u32 reg)
> > {
> >-	struct npcm7xx_kcs_bmc *priv = kcs_bmc_priv(kcs_bmc);
> >+	struct npcm7xx_kcs_bmc *priv = to_npcm7xx_kcs_bmc(kcs_bmc);
> > 	u32 val = 0;
> > 	int rc;
> >
> >@@ -90,7 +97,7 @@ static u8 npcm7xx_kcs_inb(struct kcs_bmc *kcs_bmc, u32 reg)
> >
> > static void npcm7xx_kcs_outb(struct kcs_bmc *kcs_bmc, u32 reg, u8 data)
> > {
> >-	struct npcm7xx_kcs_bmc *priv = kcs_bmc_priv(kcs_bmc);
> >+	struct npcm7xx_kcs_bmc *priv = to_npcm7xx_kcs_bmc(kcs_bmc);
> > 	int rc;
> >
> > 	rc = regmap_write(priv->map, reg, data);
> >@@ -99,7 +106,7 @@ static void npcm7xx_kcs_outb(struct kcs_bmc *kcs_bmc, u32 reg, u8 data)
> >
> > static void npcm7xx_kcs_updateb(struct kcs_bmc *kcs_bmc, u32 reg, u8 mask, u8 data)
> > {
> >-	struct npcm7xx_kcs_bmc *priv = kcs_bmc_priv(kcs_bmc);
> >+	struct npcm7xx_kcs_bmc *priv = to_npcm7xx_kcs_bmc(kcs_bmc);
> > 	int rc;
> >
> > 	rc = regmap_update_bits(priv->map, reg, mask, data);
> >@@ -108,7 +115,7 @@ static void npcm7xx_kcs_updateb(struct kcs_bmc *kcs_bmc, u32 reg, u8 mask, u8 da
> >
> > static void npcm7xx_kcs_enable_channel(struct kcs_bmc *kcs_bmc, bool enable)
> > {
> >-	struct npcm7xx_kcs_bmc *priv = kcs_bmc_priv(kcs_bmc);
> >+	struct npcm7xx_kcs_bmc *priv = to_npcm7xx_kcs_bmc(kcs_bmc);
> >
> > 	regmap_update_bits(priv->map, priv->reg->ctl, KCS_CTL_IBFIE,
> > 			   enable ? KCS_CTL_IBFIE : 0);
> >@@ -155,11 +162,10 @@ static int npcm7xx_kcs_probe(struct platform_device *pdev)
> > 		return -ENODEV;
> > 	}
> >
> >-	kcs_bmc = kcs_bmc_alloc(dev, sizeof(*priv), chan);
> >-	if (!kcs_bmc)
> >+	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
> >+	if (!priv)
> > 		return -ENOMEM;
> >
> >-	priv = kcs_bmc_priv(kcs_bmc);
> > 	priv->map = syscon_node_to_regmap(dev->parent->of_node);
> > 	if (IS_ERR(priv->map)) {
> > 		dev_err(dev, "Couldn't get regmap\n");
> >@@ -167,6 +173,9 @@ static int npcm7xx_kcs_probe(struct platform_device *pdev)
> > 	}
> > 	priv->reg = &npcm7xx_kcs_reg_tbl[chan - 1];
> >
> >+	kcs_bmc = &priv->kcs_bmc;
> >+	kcs_bmc->dev = &pdev->dev;
> >+	kcs_bmc->channel = chan;
> > 	kcs_bmc->ioreg.idr = priv->reg->dib;
> > 	kcs_bmc->ioreg.odr = priv->reg->dob;
> > 	kcs_bmc->ioreg.str = priv->reg->sts;
> >@@ -174,31 +183,27 @@ static int npcm7xx_kcs_probe(struct platform_device *pdev)
> > 	kcs_bmc->io_outputb = npcm7xx_kcs_outb;
> > 	kcs_bmc->io_updateb = npcm7xx_kcs_updateb;
> >
> >-	dev_set_drvdata(dev, kcs_bmc);
> >+	platform_set_drvdata(pdev, priv);
> >
> > 	npcm7xx_kcs_enable_channel(kcs_bmc, true);
> > 	rc = npcm7xx_kcs_config_irq(kcs_bmc, pdev);
> > 	if (rc)
> > 		return rc;
> >
> >-	rc = misc_register(&kcs_bmc->miscdev);
> >-	if (rc) {
> >-		dev_err(dev, "Unable to register device\n");
> >-		return rc;
> >-	}
> >
> > 	pr_info("channel=%u idr=0x%x odr=0x%x str=0x%x\n",
> > 		chan,
> > 		kcs_bmc->ioreg.idr, kcs_bmc->ioreg.odr, kcs_bmc->ioreg.str);
> >
> >-	return 0;
> >+	return kcs_bmc_add_device(kcs_bmc);
> > }
> >
> > static int npcm7xx_kcs_remove(struct platform_device *pdev)
> > {
> >-	struct kcs_bmc *kcs_bmc = dev_get_drvdata(&pdev->dev);
> >+	struct npcm7xx_kcs_bmc *priv = platform_get_drvdata(pdev);
> >+	struct kcs_bmc *kcs_bmc = &priv->kcs_bmc;
> >
> >-	misc_deregister(&kcs_bmc->miscdev);
> >+	kcs_bmc_remove_device(kcs_bmc);
> 
> As with the corresponding aspeed code, should we propagate the return
> value here?

I'll address this.

Thanks for the review!

Andrew

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

* Re: [PATCH v2 11/21] ipmi: kcs_bmc: Split headers into device and client
  2021-04-09  4:01   ` Zev Weiss
@ 2021-04-09  6:06     ` Andrew Jeffery
  0 siblings, 0 replies; 67+ messages in thread
From: Andrew Jeffery @ 2021-04-09  6:06 UTC (permalink / raw)
  To: Zev Weiss
  Cc: devicetree, Chia-Wei, Wang, Ryan Chen, Tomer Maimon,
	Corey Minyard, Avi Fishman, Patrick Venture, openbmc,
	linux-kernel, Tali Perry, linux-gpio, Rob Herring,
	linux-arm-kernel, openipmi-developer, Lee Jones, Linus Walleij,
	linux-aspeed, Benjamin Fair



On Fri, 9 Apr 2021, at 13:31, Zev Weiss wrote:
> On Fri, Mar 19, 2021 at 01:27:42AM CDT, Andrew Jeffery wrote:
> >Strengthen the distinction between code that abstracts the
> >implementation of the KCS behaviours (device drivers) and code that
> >exploits KCS behaviours (clients). Neither needs to know about the APIs
> >required by the other, so provide separate headers.
> >
> >Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
> >---
> > drivers/char/ipmi/kcs_bmc.c           | 21 ++++++++++-----
> > drivers/char/ipmi/kcs_bmc.h           | 30 ++++++++++-----------
> > drivers/char/ipmi/kcs_bmc_aspeed.c    | 20 +++++++++-----
> > drivers/char/ipmi/kcs_bmc_cdev_ipmi.c | 39 ++++++++++++++++++---------
> > drivers/char/ipmi/kcs_bmc_client.h    | 29 ++++++++++++++++++++
> > drivers/char/ipmi/kcs_bmc_device.h    | 19 +++++++++++++
> > drivers/char/ipmi/kcs_bmc_npcm7xx.c   | 20 +++++++++-----
> > 7 files changed, 129 insertions(+), 49 deletions(-)
> > create mode 100644 drivers/char/ipmi/kcs_bmc_client.h
> > create mode 100644 drivers/char/ipmi/kcs_bmc_device.h
> >
> >diff --git a/drivers/char/ipmi/kcs_bmc.c b/drivers/char/ipmi/kcs_bmc.c
> >index 709b6bdec165..1046ce2bbefc 100644
> >--- a/drivers/char/ipmi/kcs_bmc.c
> >+++ b/drivers/char/ipmi/kcs_bmc.c
> >@@ -1,46 +1,52 @@
> > // SPDX-License-Identifier: GPL-2.0
> > /*
> >  * Copyright (c) 2015-2018, Intel Corporation.
> >+ * Copyright (c) 2021, IBM Corp.
> >  */
> >
> > #include <linux/module.h>
> >
> > #include "kcs_bmc.h"
> >
> >+/* Implement both the device and client interfaces here */
> >+#include "kcs_bmc_device.h"
> >+#include "kcs_bmc_client.h"
> >+
> >+/* Consumer data access */
> >+
> > u8 kcs_bmc_read_data(struct kcs_bmc *kcs_bmc)
> > {
> >-	return kcs_bmc->io_inputb(kcs_bmc, kcs_bmc->ioreg.idr);
> >+	return kcs_bmc->ops->io_inputb(kcs_bmc, kcs_bmc->ioreg.idr);
> > }
> > EXPORT_SYMBOL(kcs_bmc_read_data);
> >
> > void kcs_bmc_write_data(struct kcs_bmc *kcs_bmc, u8 data)
> > {
> >-	kcs_bmc->io_outputb(kcs_bmc, kcs_bmc->ioreg.odr, data);
> >+	kcs_bmc->ops->io_outputb(kcs_bmc, kcs_bmc->ioreg.odr, data);
> > }
> > EXPORT_SYMBOL(kcs_bmc_write_data);
> >
> > u8 kcs_bmc_read_status(struct kcs_bmc *kcs_bmc)
> > {
> >-	return kcs_bmc->io_inputb(kcs_bmc, kcs_bmc->ioreg.str);
> >+	return kcs_bmc->ops->io_inputb(kcs_bmc, kcs_bmc->ioreg.str);
> > }
> > EXPORT_SYMBOL(kcs_bmc_read_status);
> >
> > void kcs_bmc_write_status(struct kcs_bmc *kcs_bmc, u8 data)
> > {
> >-	kcs_bmc->io_outputb(kcs_bmc, kcs_bmc->ioreg.str, data);
> >+	kcs_bmc->ops->io_outputb(kcs_bmc, kcs_bmc->ioreg.str, data);
> > }
> > EXPORT_SYMBOL(kcs_bmc_write_status);
> >
> > void kcs_bmc_update_status(struct kcs_bmc *kcs_bmc, u8 mask, u8 val)
> > {
> >-	kcs_bmc->io_updateb(kcs_bmc, kcs_bmc->ioreg.str, mask, val);
> >+	kcs_bmc->ops->io_updateb(kcs_bmc, kcs_bmc->ioreg.str, mask, val);
> > }
> > EXPORT_SYMBOL(kcs_bmc_update_status);
> >
> >-int kcs_bmc_ipmi_event(struct kcs_bmc *kcs_bmc);
> > int kcs_bmc_handle_event(struct kcs_bmc *kcs_bmc)
> > {
> >-	return kcs_bmc_ipmi_event(kcs_bmc);
> >+	return kcs_bmc->client.ops->event(&kcs_bmc->client);
> > }
> > EXPORT_SYMBOL(kcs_bmc_handle_event);
> >
> >@@ -60,4 +66,5 @@ EXPORT_SYMBOL(kcs_bmc_remove_device);
> >
> > MODULE_LICENSE("GPL v2");
> > MODULE_AUTHOR("Haiyue Wang <haiyue.wang@linux.intel.com>");
> >+MODULE_AUTHOR("Andrew Jeffery <andrew@aj.id.au>");
> > MODULE_DESCRIPTION("KCS BMC to handle the IPMI request from system software");
> >diff --git a/drivers/char/ipmi/kcs_bmc.h b/drivers/char/ipmi/kcs_bmc.h
> >index bf0ae327997f..a1350e567723 100644
> >--- a/drivers/char/ipmi/kcs_bmc.h
> >+++ b/drivers/char/ipmi/kcs_bmc.h
> >@@ -8,6 +8,15 @@
> >
> > #include <linux/miscdevice.h>
> >
> >+#include "kcs_bmc_client.h"
> >+
> >+#define KCS_BMC_EVENT_NONE	0
> >+#define KCS_BMC_EVENT_HANDLED	1
> 
> Is there a particular reason we're introducing these macros and using an
> int return type for kcs_bmc_client_ops.event instead of just having it
> be irqreturn_t?  Other event types or outcomes we're anticipating needing
> to handle maybe?

In earlier iterations of the patches I was doing some extra work in the 
event handling path and felt it was useful at the time. However I've 
refactored things a little and this may have outlived its usefulness.

I'll reasses!

> 
> >+
> >+#define KCS_BMC_STR_OBF		BIT(0)
> >+#define KCS_BMC_STR_IBF		BIT(1)
> >+#define KCS_BMC_STR_CMD_DAT	BIT(3)
> 
> The first two of these macros are used later in the series, but the third
> doesn't end up used at all I think?

I think I just added it as documentation as the hardware-defined bits 
aren't contiguous.

Andrew

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

* Re: [PATCH v2 12/21] ipmi: kcs_bmc: Strip private client data from struct kcs_bmc
  2021-04-09  4:07   ` Zev Weiss
@ 2021-04-09  6:15     ` Andrew Jeffery
  0 siblings, 0 replies; 67+ messages in thread
From: Andrew Jeffery @ 2021-04-09  6:15 UTC (permalink / raw)
  To: Zev Weiss
  Cc: devicetree, Chia-Wei, Wang, Ryan Chen, Tomer Maimon,
	Corey Minyard, Avi Fishman, Patrick Venture, openbmc,
	linux-kernel, Tali Perry, linux-gpio, Rob Herring,
	linux-arm-kernel, openipmi-developer, Lee Jones, Linus Walleij,
	linux-aspeed, Benjamin Fair



On Fri, 9 Apr 2021, at 13:37, Zev Weiss wrote:
> On Fri, Mar 19, 2021 at 01:27:43AM CDT, Andrew Jeffery wrote:
> >Move all client-private data out of `struct kcs_bmc` into the KCS client
> >implementation.
> >
> >With this change the KCS BMC core code now only concerns itself with
> >abstract `struct kcs_bmc` and `struct kcs_bmc_client` types, achieving
> >expected separation of concerns. Further, the change clears the path for
> >implementation of alternative userspace interfaces.
> >
> >The chardev data-structures are rearranged in the same manner applied to
> >the KCS device driver data-structures in an earlier patch - `struct
> >kcs_bmc_client` is embedded in the client's private data and we exploit
> >container_of() to translate as required.
> >
> >Finally, now that it is free of client data, `struct kcs_bmc` is renamed
> >to `struct kcs_bmc_device` to contrast `struct kcs_bmc_client`.
> >
> >Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
> >---
> > drivers/char/ipmi/kcs_bmc.c           |  68 +++-
> > drivers/char/ipmi/kcs_bmc.h           |  86 +-----
> > drivers/char/ipmi/kcs_bmc_aspeed.c    |  22 +-
> > drivers/char/ipmi/kcs_bmc_cdev_ipmi.c | 428 ++++++++++++++++----------
> > drivers/char/ipmi/kcs_bmc_client.h    |  28 +-
> > drivers/char/ipmi/kcs_bmc_device.h    |  12 +-
> > drivers/char/ipmi/kcs_bmc_npcm7xx.c   |  20 +-
> > 7 files changed, 368 insertions(+), 296 deletions(-)
> >
> >diff --git a/drivers/char/ipmi/kcs_bmc.c b/drivers/char/ipmi/kcs_bmc.c
> >index 1046ce2bbefc..266ebec71d6f 100644
> >--- a/drivers/char/ipmi/kcs_bmc.c
> >+++ b/drivers/char/ipmi/kcs_bmc.c
> >@@ -4,6 +4,7 @@
> >  * Copyright (c) 2021, IBM Corp.
> >  */
> >
> >+#include <linux/device.h>
> > #include <linux/module.h>
> >
> > #include "kcs_bmc.h"
> >@@ -14,51 +15,96 @@
> >
> > /* Consumer data access */
> >
> >-u8 kcs_bmc_read_data(struct kcs_bmc *kcs_bmc)
> >+u8 kcs_bmc_read_data(struct kcs_bmc_device *kcs_bmc)
> > {
> > 	return kcs_bmc->ops->io_inputb(kcs_bmc, kcs_bmc->ioreg.idr);
> > }
> > EXPORT_SYMBOL(kcs_bmc_read_data);
> >
> >-void kcs_bmc_write_data(struct kcs_bmc *kcs_bmc, u8 data)
> >+void kcs_bmc_write_data(struct kcs_bmc_device *kcs_bmc, u8 data)
> > {
> > 	kcs_bmc->ops->io_outputb(kcs_bmc, kcs_bmc->ioreg.odr, data);
> > }
> > EXPORT_SYMBOL(kcs_bmc_write_data);
> >
> >-u8 kcs_bmc_read_status(struct kcs_bmc *kcs_bmc)
> >+u8 kcs_bmc_read_status(struct kcs_bmc_device *kcs_bmc)
> > {
> > 	return kcs_bmc->ops->io_inputb(kcs_bmc, kcs_bmc->ioreg.str);
> > }
> > EXPORT_SYMBOL(kcs_bmc_read_status);
> >
> >-void kcs_bmc_write_status(struct kcs_bmc *kcs_bmc, u8 data)
> >+void kcs_bmc_write_status(struct kcs_bmc_device *kcs_bmc, u8 data)
> > {
> > 	kcs_bmc->ops->io_outputb(kcs_bmc, kcs_bmc->ioreg.str, data);
> > }
> > EXPORT_SYMBOL(kcs_bmc_write_status);
> >
> >-void kcs_bmc_update_status(struct kcs_bmc *kcs_bmc, u8 mask, u8 val)
> >+void kcs_bmc_update_status(struct kcs_bmc_device *kcs_bmc, u8 mask, u8 val)
> > {
> > 	kcs_bmc->ops->io_updateb(kcs_bmc, kcs_bmc->ioreg.str, mask, val);
> > }
> > EXPORT_SYMBOL(kcs_bmc_update_status);
> >
> >-int kcs_bmc_handle_event(struct kcs_bmc *kcs_bmc)
> >+int kcs_bmc_handle_event(struct kcs_bmc_device *kcs_bmc)
> > {
> >-	return kcs_bmc->client.ops->event(&kcs_bmc->client);
> >+	struct kcs_bmc_client *client;
> >+	int rc;
> >+
> >+	spin_lock(&kcs_bmc->lock);
> >+	client = kcs_bmc->client;
> >+	if (client) {
> >+		rc = client->ops->event(client);
> >+	} else {
> >+		u8 status;
> >+
> >+		status = kcs_bmc_read_status(kcs_bmc);
> >+		if (status & KCS_BMC_STR_IBF) {
> >+			/* Ack the event by reading the data */
> >+			kcs_bmc_read_data(kcs_bmc);
> >+			rc = KCS_BMC_EVENT_HANDLED;
> >+		} else {
> >+			rc = KCS_BMC_EVENT_NONE;
> >+		}
> >+	}
> >+	spin_unlock(&kcs_bmc->lock);
> >+
> >+	return rc;
> > }
> > EXPORT_SYMBOL(kcs_bmc_handle_event);
> >
> >-int kcs_bmc_ipmi_attach_cdev(struct kcs_bmc *kcs_bmc);
> >-int kcs_bmc_add_device(struct kcs_bmc *kcs_bmc)
> >+int kcs_bmc_enable_device(struct kcs_bmc_device *kcs_bmc, struct kcs_bmc_client *client)
> >+{
> >+	int rc;
> >+
> >+	spin_lock_irq(&kcs_bmc->lock);
> >+	if (kcs_bmc->client) {
> >+		rc = -EBUSY;
> >+	} else {
> >+		kcs_bmc->client = client;
> >+		rc = 0;
> >+	}
> >+	spin_unlock_irq(&kcs_bmc->lock);
> >+
> >+	return rc;
> >+}
> >+EXPORT_SYMBOL(kcs_bmc_enable_device);
> >+
> >+void kcs_bmc_disable_device(struct kcs_bmc_device *kcs_bmc, struct kcs_bmc_client *client)
> >+{
> >+	spin_lock_irq(&kcs_bmc->lock);
> >+	if (client == kcs_bmc->client)
> >+		kcs_bmc->client = NULL;
> 
> Is there any situation in which a non-matching client could be passed in
> here?

Yes; this allows the chardev modules to just unconditionally call 
kcs_bmc_disable_device() without having to duplicate the state tracking 
done here.

>  Might we consider issuing a warning of some sort or returning an
> error to the caller if so?

No, see above.

> 
> >+	spin_unlock_irq(&kcs_bmc->lock);
> >+}
> >+EXPORT_SYMBOL(kcs_bmc_disable_device);
> >+
> >+int kcs_bmc_add_device(struct kcs_bmc_device *kcs_bmc)
> > {
> > 	return kcs_bmc_ipmi_attach_cdev(kcs_bmc);
> > }
> > EXPORT_SYMBOL(kcs_bmc_add_device);
> >
> >-int kcs_bmc_ipmi_detach_cdev(struct kcs_bmc *kcs_bmc);
> >-int kcs_bmc_remove_device(struct kcs_bmc *kcs_bmc)
> >+int kcs_bmc_remove_device(struct kcs_bmc_device *kcs_bmc)
> > {
> > 	return kcs_bmc_ipmi_detach_cdev(kcs_bmc);
> > }
> >diff --git a/drivers/char/ipmi/kcs_bmc.h b/drivers/char/ipmi/kcs_bmc.h
> >index a1350e567723..3f266740c759 100644
> >--- a/drivers/char/ipmi/kcs_bmc.h
> >+++ b/drivers/char/ipmi/kcs_bmc.h
> >@@ -6,9 +6,7 @@
> > #ifndef __KCS_BMC_H__
> > #define __KCS_BMC_H__
> >
> >-#include <linux/miscdevice.h>
> >-
> >-#include "kcs_bmc_client.h"
> >+#include <linux/list.h>
> >
> > #define KCS_BMC_EVENT_NONE	0
> > #define KCS_BMC_EVENT_HANDLED	1
> >@@ -17,53 +15,6 @@
> > #define KCS_BMC_STR_IBF		BIT(1)
> > #define KCS_BMC_STR_CMD_DAT	BIT(3)
> >
> >-/* Different phases of the KCS BMC module.
> >- *  KCS_PHASE_IDLE:
> >- *            BMC should not be expecting nor sending any data.
> >- *  KCS_PHASE_WRITE_START:
> >- *            BMC is receiving a WRITE_START command from system software.
> >- *  KCS_PHASE_WRITE_DATA:
> >- *            BMC is receiving a data byte from system software.
> >- *  KCS_PHASE_WRITE_END_CMD:
> >- *            BMC is waiting a last data byte from system software.
> >- *  KCS_PHASE_WRITE_DONE:
> >- *            BMC has received the whole request from system software.
> >- *  KCS_PHASE_WAIT_READ:
> >- *            BMC is waiting the response from the upper IPMI service.
> >- *  KCS_PHASE_READ:
> >- *            BMC is transferring the response to system software.
> >- *  KCS_PHASE_ABORT_ERROR1:
> >- *            BMC is waiting error status request from system software.
> >- *  KCS_PHASE_ABORT_ERROR2:
> >- *            BMC is waiting for idle status afer error from system software.
> >- *  KCS_PHASE_ERROR:
> >- *            BMC has detected a protocol violation at the interface level.
> >- */
> >-enum kcs_phases {
> >-	KCS_PHASE_IDLE,
> >-
> >-	KCS_PHASE_WRITE_START,
> >-	KCS_PHASE_WRITE_DATA,
> >-	KCS_PHASE_WRITE_END_CMD,
> >-	KCS_PHASE_WRITE_DONE,
> >-
> >-	KCS_PHASE_WAIT_READ,
> >-	KCS_PHASE_READ,
> >-
> >-	KCS_PHASE_ABORT_ERROR1,
> >-	KCS_PHASE_ABORT_ERROR2,
> >-	KCS_PHASE_ERROR
> >-};
> >-
> >-/* IPMI 2.0 - Table 9-4, KCS Interface Status Codes */
> >-enum kcs_errors {
> >-	KCS_NO_ERROR                = 0x00,
> >-	KCS_ABORTED_BY_COMMAND      = 0x01,
> >-	KCS_ILLEGAL_CONTROL_CODE    = 0x02,
> >-	KCS_LENGTH_ERROR            = 0x06,
> >-	KCS_UNSPECIFIED_ERROR       = 0xFF
> >-};
> >-
> > /* IPMI 2.0 - 9.5, KCS Interface Registers
> >  * @idr: Input Data Register
> >  * @odr: Output Data Register
> >@@ -76,36 +27,23 @@ struct kcs_ioreg {
> > };
> >
> > struct kcs_bmc_device_ops;
> >+struct kcs_bmc_client;
> >+
> >+struct kcs_bmc_device {
> >+	struct list_head entry;
> >
> >-struct kcs_bmc {
> > 	struct device *dev;
> >-
> >-	const struct kcs_bmc_device_ops *ops;
> >-
> >-	struct kcs_bmc_client client;
> >-
> >-	spinlock_t lock;
> >-
> > 	u32 channel;
> >-	int running;
> >
> > 	struct kcs_ioreg ioreg;
> >
> >-	enum kcs_phases phase;
> >-	enum kcs_errors error;
> >+	const struct kcs_bmc_device_ops *ops;
> >
> >-	wait_queue_head_t queue;
> >-	bool data_in_avail;
> >-	int  data_in_idx;
> >-	u8  *data_in;
> >-
> >-	int  data_out_idx;
> >-	int  data_out_len;
> >-	u8  *data_out;
> >-
> >-	struct mutex mutex;
> >-	u8 *kbuffer;
> >-
> >-	struct miscdevice miscdev;
> >+	spinlock_t lock;
> >+	struct kcs_bmc_client *client;
> > };
> >+
> >+/* Temporary exports while refactoring */
> >+int kcs_bmc_ipmi_attach_cdev(struct kcs_bmc_device *kcs_bmc);
> >+int kcs_bmc_ipmi_detach_cdev(struct kcs_bmc_device *kcs_bmc);
> > #endif /* __KCS_BMC_H__ */
> >diff --git a/drivers/char/ipmi/kcs_bmc_aspeed.c b/drivers/char/ipmi/kcs_bmc_aspeed.c
> >index 1b313355b1c8..6f26e7366c0b 100644
> >--- a/drivers/char/ipmi/kcs_bmc_aspeed.c
> >+++ b/drivers/char/ipmi/kcs_bmc_aspeed.c
> >@@ -61,7 +61,7 @@
> > #define LPC_STR4             0x11C
> >
> > struct aspeed_kcs_bmc {
> >-	struct kcs_bmc kcs_bmc;
> >+	struct kcs_bmc_device kcs_bmc;
> >
> > 	struct regmap *map;
> > };
> >@@ -71,12 +71,12 @@ struct aspeed_kcs_of_ops {
> > 	int (*get_io_address)(struct platform_device *pdev);
> > };
> >
> >-static inline struct aspeed_kcs_bmc *to_aspeed_kcs_bmc(struct kcs_bmc *kcs_bmc)
> >+static inline struct aspeed_kcs_bmc *to_aspeed_kcs_bmc(struct kcs_bmc_device *kcs_bmc)
> > {
> > 	return container_of(kcs_bmc, struct aspeed_kcs_bmc, kcs_bmc);
> > }
> >
> >-static u8 aspeed_kcs_inb(struct kcs_bmc *kcs_bmc, u32 reg)
> >+static u8 aspeed_kcs_inb(struct kcs_bmc_device *kcs_bmc, u32 reg)
> > {
> > 	struct aspeed_kcs_bmc *priv = to_aspeed_kcs_bmc(kcs_bmc);
> > 	u32 val = 0;
> >@@ -88,7 +88,7 @@ static u8 aspeed_kcs_inb(struct kcs_bmc *kcs_bmc, u32 reg)
> > 	return rc == 0 ? (u8) val : 0;
> > }
> >
> >-static void aspeed_kcs_outb(struct kcs_bmc *kcs_bmc, u32 reg, u8 data)
> >+static void aspeed_kcs_outb(struct kcs_bmc_device *kcs_bmc, u32 reg, u8 data)
> > {
> > 	struct aspeed_kcs_bmc *priv = to_aspeed_kcs_bmc(kcs_bmc);
> > 	int rc;
> >@@ -97,7 +97,7 @@ static void aspeed_kcs_outb(struct kcs_bmc *kcs_bmc, u32 reg, u8 data)
> > 	WARN(rc != 0, "regmap_write() failed: %d\n", rc);
> > }
> >
> >-static void aspeed_kcs_updateb(struct kcs_bmc *kcs_bmc, u32 reg, u8 mask, u8 val)
> >+static void aspeed_kcs_updateb(struct kcs_bmc_device *kcs_bmc, u32 reg, u8 mask, u8 val)
> > {
> > 	struct aspeed_kcs_bmc *priv = to_aspeed_kcs_bmc(kcs_bmc);
> > 	int rc;
> >@@ -119,7 +119,7 @@ static void aspeed_kcs_updateb(struct kcs_bmc *kcs_bmc, u32 reg, u8 mask, u8 val
> >  *     C. KCS4
> >  *        D / C : CA4h / CA5h
> >  */
> >-static void aspeed_kcs_set_address(struct kcs_bmc *kcs_bmc, u16 addr)
> >+static void aspeed_kcs_set_address(struct kcs_bmc_device *kcs_bmc, u16 addr)
> > {
> > 	struct aspeed_kcs_bmc *priv = to_aspeed_kcs_bmc(kcs_bmc);
> >
> >@@ -153,7 +153,7 @@ static void aspeed_kcs_set_address(struct kcs_bmc *kcs_bmc, u16 addr)
> > 	}
> > }
> >
> >-static void aspeed_kcs_enable_channel(struct kcs_bmc *kcs_bmc, bool enable)
> >+static void aspeed_kcs_enable_channel(struct kcs_bmc_device *kcs_bmc, bool enable)
> > {
> > 	struct aspeed_kcs_bmc *priv = to_aspeed_kcs_bmc(kcs_bmc);
> >
> >@@ -228,7 +228,7 @@ static const struct kcs_bmc_device_ops aspeed_kcs_ops = {
> >
> > static irqreturn_t aspeed_kcs_irq(int irq, void *arg)
> > {
> >-	struct kcs_bmc *kcs_bmc = arg;
> >+	struct kcs_bmc_device *kcs_bmc = arg;
> > 	int rc;
> >
> > 	rc = kcs_bmc_handle_event(kcs_bmc);
> >@@ -238,7 +238,7 @@ static irqreturn_t aspeed_kcs_irq(int irq, void *arg)
> > 	return rc == KCS_BMC_EVENT_HANDLED ? IRQ_HANDLED : IRQ_NONE;
> > }
> >
> >-static int aspeed_kcs_config_irq(struct kcs_bmc *kcs_bmc,
> >+static int aspeed_kcs_config_irq(struct kcs_bmc_device *kcs_bmc,
> > 			struct platform_device *pdev)
> > {
> > 	struct device *dev = &pdev->dev;
> >@@ -338,8 +338,8 @@ static int aspeed_kcs_of_v2_get_io_address(struct platform_device *pdev)
> > static int aspeed_kcs_probe(struct platform_device *pdev)
> > {
> > 	const struct aspeed_kcs_of_ops *ops;
> >+	struct kcs_bmc_device *kcs_bmc;
> > 	struct aspeed_kcs_bmc *priv;
> >-	struct kcs_bmc *kcs_bmc;
> > 	struct device_node *np;
> > 	int rc, channel, addr;
> >
> >@@ -400,7 +400,7 @@ static int aspeed_kcs_probe(struct platform_device *pdev)
> > static int aspeed_kcs_remove(struct platform_device *pdev)
> > {
> > 	struct aspeed_kcs_bmc *priv = platform_get_drvdata(pdev);
> >-	struct kcs_bmc *kcs_bmc = &priv->kcs_bmc;
> >+	struct kcs_bmc_device *kcs_bmc = &priv->kcs_bmc;
> >
> > 	kcs_bmc_remove_device(kcs_bmc);
> >
> >diff --git a/drivers/char/ipmi/kcs_bmc_cdev_ipmi.c b/drivers/char/ipmi/kcs_bmc_cdev_ipmi.c
> >index fd852d8abe48..58c42e76483d 100644
> >--- a/drivers/char/ipmi/kcs_bmc_cdev_ipmi.c
> >+++ b/drivers/char/ipmi/kcs_bmc_cdev_ipmi.c
> >@@ -8,13 +8,88 @@
> > #include <linux/errno.h>
> > #include <linux/io.h>
> > #include <linux/ipmi_bmc.h>
> >+#include <linux/list.h>
> >+#include <linux/miscdevice.h>
> > #include <linux/module.h>
> >+#include <linux/mutex.h>
> > #include <linux/platform_device.h>
> > #include <linux/poll.h>
> > #include <linux/sched.h>
> > #include <linux/slab.h>
> >
> >-#include "kcs_bmc.h"
> >+#include "kcs_bmc_client.h"
> >+
> >+/* Different phases of the KCS BMC module.
> >+ *  KCS_PHASE_IDLE:
> >+ *            BMC should not be expecting nor sending any data.
> >+ *  KCS_PHASE_WRITE_START:
> >+ *            BMC is receiving a WRITE_START command from system software.
> >+ *  KCS_PHASE_WRITE_DATA:
> >+ *            BMC is receiving a data byte from system software.
> >+ *  KCS_PHASE_WRITE_END_CMD:
> >+ *            BMC is waiting a last data byte from system software.
> >+ *  KCS_PHASE_WRITE_DONE:
> >+ *            BMC has received the whole request from system software.
> >+ *  KCS_PHASE_WAIT_READ:
> >+ *            BMC is waiting the response from the upper IPMI service.
> >+ *  KCS_PHASE_READ:
> >+ *            BMC is transferring the response to system software.
> >+ *  KCS_PHASE_ABORT_ERROR1:
> >+ *            BMC is waiting error status request from system software.
> >+ *  KCS_PHASE_ABORT_ERROR2:
> >+ *            BMC is waiting for idle status afer error from system software.
> >+ *  KCS_PHASE_ERROR:
> >+ *            BMC has detected a protocol violation at the interface level.
> >+ */
> >+enum kcs_ipmi_phases {
> >+	KCS_PHASE_IDLE,
> >+
> >+	KCS_PHASE_WRITE_START,
> >+	KCS_PHASE_WRITE_DATA,
> >+	KCS_PHASE_WRITE_END_CMD,
> >+	KCS_PHASE_WRITE_DONE,
> >+
> >+	KCS_PHASE_WAIT_READ,
> >+	KCS_PHASE_READ,
> >+
> >+	KCS_PHASE_ABORT_ERROR1,
> >+	KCS_PHASE_ABORT_ERROR2,
> >+	KCS_PHASE_ERROR
> >+};
> >+
> >+/* IPMI 2.0 - Table 9-4, KCS Interface Status Codes */
> >+enum kcs_ipmi_errors {
> >+	KCS_NO_ERROR                = 0x00,
> >+	KCS_ABORTED_BY_COMMAND      = 0x01,
> >+	KCS_ILLEGAL_CONTROL_CODE    = 0x02,
> >+	KCS_LENGTH_ERROR            = 0x06,
> >+	KCS_UNSPECIFIED_ERROR       = 0xFF
> >+};
> >+
> >+struct kcs_bmc_ipmi {
> >+	struct list_head entry;
> >+
> >+	struct kcs_bmc_client client;
> >+
> >+	spinlock_t lock;
> >+
> >+	enum kcs_ipmi_phases phase;
> >+	enum kcs_ipmi_errors error;
> >+
> >+	wait_queue_head_t queue;
> >+	bool data_in_avail;
> >+	int  data_in_idx;
> >+	u8  *data_in;
> >+
> >+	int  data_out_idx;
> >+	int  data_out_len;
> >+	u8  *data_out;
> >+
> >+	struct mutex mutex;
> >+	u8 *kbuffer;
> >+
> >+	struct miscdevice miscdev;
> >+};
> >
> > #define DEVICE_NAME "ipmi-kcs"
> >
> >@@ -44,171 +119,169 @@ enum kcs_states {
> > #define KCS_CMD_WRITE_END         0x62
> > #define KCS_CMD_READ_BYTE         0x68
> >
> >-static inline void set_state(struct kcs_bmc *kcs_bmc, u8 state)
> >+static inline void set_state(struct kcs_bmc_ipmi *priv, u8 state)
> > {
> >-	kcs_bmc_update_status(kcs_bmc, KCS_STATUS_STATE_MASK,
> >-					KCS_STATUS_STATE(state));
> >+	kcs_bmc_update_status(priv->client.dev, KCS_STATUS_STATE_MASK, KCS_STATUS_STATE(state));
> > }
> >
> >-static void kcs_bmc_ipmi_force_abort(struct kcs_bmc *kcs_bmc)
> >+static void kcs_bmc_ipmi_force_abort(struct kcs_bmc_ipmi *priv)
> > {
> >-	set_state(kcs_bmc, ERROR_STATE);
> >-	kcs_bmc_read_data(kcs_bmc);
> >-	kcs_bmc_write_data(kcs_bmc, KCS_ZERO_DATA);
> >+	set_state(priv, ERROR_STATE);
> >+	kcs_bmc_read_data(priv->client.dev);
> >+	kcs_bmc_write_data(priv->client.dev, KCS_ZERO_DATA);
> >
> >-	kcs_bmc->phase = KCS_PHASE_ERROR;
> >-	kcs_bmc->data_in_avail = false;
> >-	kcs_bmc->data_in_idx = 0;
> >+	priv->phase = KCS_PHASE_ERROR;
> >+	priv->data_in_avail = false;
> >+	priv->data_in_idx = 0;
> > }
> >
> >-static void kcs_bmc_ipmi_handle_data(struct kcs_bmc *kcs_bmc)
> >+static void kcs_bmc_ipmi_handle_data(struct kcs_bmc_ipmi *priv)
> > {
> >+	struct kcs_bmc_device *dev;
> > 	u8 data;
> >
> >-	switch (kcs_bmc->phase) {
> >+	dev = priv->client.dev;
> >+
> >+	switch (priv->phase) {
> > 	case KCS_PHASE_WRITE_START:
> >-		kcs_bmc->phase = KCS_PHASE_WRITE_DATA;
> >+		priv->phase = KCS_PHASE_WRITE_DATA;
> > 		fallthrough;
> >
> > 	case KCS_PHASE_WRITE_DATA:
> >-		if (kcs_bmc->data_in_idx < KCS_MSG_BUFSIZ) {
> >-			set_state(kcs_bmc, WRITE_STATE);
> >-			kcs_bmc_write_data(kcs_bmc, KCS_ZERO_DATA);
> >-			kcs_bmc->data_in[kcs_bmc->data_in_idx++] =
> >-						kcs_bmc_read_data(kcs_bmc);
> >+		if (priv->data_in_idx < KCS_MSG_BUFSIZ) {
> >+			set_state(priv, WRITE_STATE);
> >+			kcs_bmc_write_data(dev, KCS_ZERO_DATA);
> >+			priv->data_in[priv->data_in_idx++] = kcs_bmc_read_data(dev);
> > 		} else {
> >-			kcs_bmc_ipmi_force_abort(kcs_bmc);
> >-			kcs_bmc->error = KCS_LENGTH_ERROR;
> >+			kcs_bmc_ipmi_force_abort(priv);
> >+			priv->error = KCS_LENGTH_ERROR;
> > 		}
> > 		break;
> >
> > 	case KCS_PHASE_WRITE_END_CMD:
> >-		if (kcs_bmc->data_in_idx < KCS_MSG_BUFSIZ) {
> >-			set_state(kcs_bmc, READ_STATE);
> >-			kcs_bmc->data_in[kcs_bmc->data_in_idx++] =
> >-						kcs_bmc_read_data(kcs_bmc);
> >-			kcs_bmc->phase = KCS_PHASE_WRITE_DONE;
> >-			kcs_bmc->data_in_avail = true;
> >-			wake_up_interruptible(&kcs_bmc->queue);
> >+		if (priv->data_in_idx < KCS_MSG_BUFSIZ) {
> >+			set_state(priv, READ_STATE);
> >+			priv->data_in[priv->data_in_idx++] = kcs_bmc_read_data(dev);
> >+			priv->phase = KCS_PHASE_WRITE_DONE;
> >+			priv->data_in_avail = true;
> >+			wake_up_interruptible(&priv->queue);
> > 		} else {
> >-			kcs_bmc_ipmi_force_abort(kcs_bmc);
> >-			kcs_bmc->error = KCS_LENGTH_ERROR;
> >+			kcs_bmc_ipmi_force_abort(priv);
> >+			priv->error = KCS_LENGTH_ERROR;
> > 		}
> > 		break;
> >
> > 	case KCS_PHASE_READ:
> >-		if (kcs_bmc->data_out_idx == kcs_bmc->data_out_len)
> >-			set_state(kcs_bmc, IDLE_STATE);
> >+		if (priv->data_out_idx == priv->data_out_len)
> >+			set_state(priv, IDLE_STATE);
> >
> >-		data = kcs_bmc_read_data(kcs_bmc);
> >+		data = kcs_bmc_read_data(dev);
> > 		if (data != KCS_CMD_READ_BYTE) {
> >-			set_state(kcs_bmc, ERROR_STATE);
> >-			kcs_bmc_write_data(kcs_bmc, KCS_ZERO_DATA);
> >+			set_state(priv, ERROR_STATE);
> >+			kcs_bmc_write_data(dev, KCS_ZERO_DATA);
> > 			break;
> > 		}
> >
> >-		if (kcs_bmc->data_out_idx == kcs_bmc->data_out_len) {
> >-			kcs_bmc_write_data(kcs_bmc, KCS_ZERO_DATA);
> >-			kcs_bmc->phase = KCS_PHASE_IDLE;
> >+		if (priv->data_out_idx == priv->data_out_len) {
> >+			kcs_bmc_write_data(dev, KCS_ZERO_DATA);
> >+			priv->phase = KCS_PHASE_IDLE;
> > 			break;
> > 		}
> >
> >-		kcs_bmc_write_data(kcs_bmc,
> >-			kcs_bmc->data_out[kcs_bmc->data_out_idx++]);
> >+		kcs_bmc_write_data(dev, priv->data_out[priv->data_out_idx++]);
> > 		break;
> >
> > 	case KCS_PHASE_ABORT_ERROR1:
> >-		set_state(kcs_bmc, READ_STATE);
> >-		kcs_bmc_read_data(kcs_bmc);
> >-		kcs_bmc_write_data(kcs_bmc, kcs_bmc->error);
> >-		kcs_bmc->phase = KCS_PHASE_ABORT_ERROR2;
> >+		set_state(priv, READ_STATE);
> >+		kcs_bmc_read_data(dev);
> >+		kcs_bmc_write_data(dev, priv->error);
> >+		priv->phase = KCS_PHASE_ABORT_ERROR2;
> > 		break;
> >
> > 	case KCS_PHASE_ABORT_ERROR2:
> >-		set_state(kcs_bmc, IDLE_STATE);
> >-		kcs_bmc_read_data(kcs_bmc);
> >-		kcs_bmc_write_data(kcs_bmc, KCS_ZERO_DATA);
> >-		kcs_bmc->phase = KCS_PHASE_IDLE;
> >+		set_state(priv, IDLE_STATE);
> >+		kcs_bmc_read_data(dev);
> >+		kcs_bmc_write_data(dev, KCS_ZERO_DATA);
> >+		priv->phase = KCS_PHASE_IDLE;
> > 		break;
> >
> > 	default:
> >-		kcs_bmc_ipmi_force_abort(kcs_bmc);
> >+		kcs_bmc_ipmi_force_abort(priv);
> > 		break;
> > 	}
> > }
> >
> >-static void kcs_bmc_ipmi_handle_cmd(struct kcs_bmc *kcs_bmc)
> >+static void kcs_bmc_ipmi_handle_cmd(struct kcs_bmc_ipmi *priv)
> > {
> > 	u8 cmd;
> >
> >-	set_state(kcs_bmc, WRITE_STATE);
> >-	kcs_bmc_write_data(kcs_bmc, KCS_ZERO_DATA);
> >+	set_state(priv, WRITE_STATE);
> >+	kcs_bmc_write_data(priv->client.dev, KCS_ZERO_DATA);
> >
> >-	cmd = kcs_bmc_read_data(kcs_bmc);
> >+	cmd = kcs_bmc_read_data(priv->client.dev);
> > 	switch (cmd) {
> > 	case KCS_CMD_WRITE_START:
> >-		kcs_bmc->phase = KCS_PHASE_WRITE_START;
> >-		kcs_bmc->error = KCS_NO_ERROR;
> >-		kcs_bmc->data_in_avail = false;
> >-		kcs_bmc->data_in_idx = 0;
> >+		priv->phase = KCS_PHASE_WRITE_START;
> >+		priv->error = KCS_NO_ERROR;
> >+		priv->data_in_avail = false;
> >+		priv->data_in_idx = 0;
> > 		break;
> >
> > 	case KCS_CMD_WRITE_END:
> >-		if (kcs_bmc->phase != KCS_PHASE_WRITE_DATA) {
> >-			kcs_bmc_ipmi_force_abort(kcs_bmc);
> >+		if (priv->phase != KCS_PHASE_WRITE_DATA) {
> >+			kcs_bmc_ipmi_force_abort(priv);
> > 			break;
> > 		}
> >
> >-		kcs_bmc->phase = KCS_PHASE_WRITE_END_CMD;
> >+		priv->phase = KCS_PHASE_WRITE_END_CMD;
> > 		break;
> >
> > 	case KCS_CMD_GET_STATUS_ABORT:
> >-		if (kcs_bmc->error == KCS_NO_ERROR)
> >-			kcs_bmc->error = KCS_ABORTED_BY_COMMAND;
> >+		if (priv->error == KCS_NO_ERROR)
> >+			priv->error = KCS_ABORTED_BY_COMMAND;
> >
> >-		kcs_bmc->phase = KCS_PHASE_ABORT_ERROR1;
> >-		kcs_bmc->data_in_avail = false;
> >-		kcs_bmc->data_in_idx = 0;
> >+		priv->phase = KCS_PHASE_ABORT_ERROR1;
> >+		priv->data_in_avail = false;
> >+		priv->data_in_idx = 0;
> > 		break;
> >
> > 	default:
> >-		kcs_bmc_ipmi_force_abort(kcs_bmc);
> >-		kcs_bmc->error = KCS_ILLEGAL_CONTROL_CODE;
> >+		kcs_bmc_ipmi_force_abort(priv);
> >+		priv->error = KCS_ILLEGAL_CONTROL_CODE;
> > 		break;
> > 	}
> > }
> >
> >-static inline struct kcs_bmc *client_to_kcs_bmc(struct kcs_bmc_client *client)
> >+static inline struct kcs_bmc_ipmi *client_to_kcs_bmc_ipmi(struct kcs_bmc_client *client)
> > {
> >-	return container_of(client, struct kcs_bmc, client);
> >+	return container_of(client, struct kcs_bmc_ipmi, client);
> > }
> >
> > static int kcs_bmc_ipmi_event(struct kcs_bmc_client *client)
> > {
> >-	struct kcs_bmc *kcs_bmc;
> >-	unsigned long flags;
> >+	struct kcs_bmc_ipmi *priv;
> > 	u8 status;
> > 	int ret;
> >
> >-	kcs_bmc = client_to_kcs_bmc(client);
> >+	priv = client_to_kcs_bmc_ipmi(client);
> >+	if (!priv)
> >+		return KCS_BMC_EVENT_NONE;
> >
> >-	spin_lock_irqsave(&kcs_bmc->lock, flags);
> >+	spin_lock(&priv->lock);
> >
> >-	status = kcs_bmc_read_status(kcs_bmc);
> >+	status = kcs_bmc_read_status(client->dev);
> > 	if (status & KCS_STATUS_IBF) {
> >-		if (!kcs_bmc->running)
> >-			kcs_bmc_ipmi_force_abort(kcs_bmc);
> >-		else if (status & KCS_STATUS_CMD_DAT)
> >-			kcs_bmc_ipmi_handle_cmd(kcs_bmc);
> >+		if (status & KCS_STATUS_CMD_DAT)
> >+			kcs_bmc_ipmi_handle_cmd(priv);
> > 		else
> >-			kcs_bmc_ipmi_handle_data(kcs_bmc);
> >+			kcs_bmc_ipmi_handle_data(priv);
> >
> > 		ret = KCS_BMC_EVENT_HANDLED;
> > 	} else {
> > 		ret = KCS_BMC_EVENT_NONE;
> > 	}
> >
> >-	spin_unlock_irqrestore(&kcs_bmc->lock, flags);
> >+	spin_unlock(&priv->lock);
> >
> > 	return ret;
> > }
> >@@ -217,37 +290,29 @@ static const struct kcs_bmc_client_ops kcs_bmc_ipmi_client_ops = {
> > 	.event = kcs_bmc_ipmi_event,
> > };
> >
> >-static inline struct kcs_bmc *file_to_kcs_bmc(struct file *filp)
> >+static inline struct kcs_bmc_ipmi *to_kcs_bmc(struct file *filp)
> > {
> >-	return container_of(filp->private_data, struct kcs_bmc, miscdev);
> >+	return container_of(filp->private_data, struct kcs_bmc_ipmi, miscdev);
> > }
> >
> > static int kcs_bmc_ipmi_open(struct inode *inode, struct file *filp)
> > {
> >-	struct kcs_bmc *kcs_bmc = file_to_kcs_bmc(filp);
> >-	int ret = 0;
> >+	struct kcs_bmc_ipmi *priv = to_kcs_bmc(filp);
> >
> >-	spin_lock_irq(&kcs_bmc->lock);
> >-	if (!kcs_bmc->running)
> >-		kcs_bmc->running = 1;
> >-	else
> >-		ret = -EBUSY;
> >-	spin_unlock_irq(&kcs_bmc->lock);
> >-
> >-	return ret;
> >+	return kcs_bmc_enable_device(priv->client.dev, &priv->client);
> > }
> >
> > static __poll_t kcs_bmc_ipmi_poll(struct file *filp, poll_table *wait)
> > {
> >-	struct kcs_bmc *kcs_bmc = file_to_kcs_bmc(filp);
> >+	struct kcs_bmc_ipmi *priv = to_kcs_bmc(filp);
> > 	__poll_t mask = 0;
> >
> >-	poll_wait(filp, &kcs_bmc->queue, wait);
> >+	poll_wait(filp, &priv->queue, wait);
> >
> >-	spin_lock_irq(&kcs_bmc->lock);
> >-	if (kcs_bmc->data_in_avail)
> >+	spin_lock_irq(&priv->lock);
> >+	if (priv->data_in_avail)
> > 		mask |= EPOLLIN;
> >-	spin_unlock_irq(&kcs_bmc->lock);
> >+	spin_unlock_irq(&priv->lock);
> >
> > 	return mask;
> > }
> >@@ -255,24 +320,24 @@ static __poll_t kcs_bmc_ipmi_poll(struct file *filp, poll_table *wait)
> > static ssize_t kcs_bmc_ipmi_read(struct file *filp, char __user *buf,
> > 			    size_t count, loff_t *ppos)
> > {
> >-	struct kcs_bmc *kcs_bmc = file_to_kcs_bmc(filp);
> >+	struct kcs_bmc_ipmi *priv = to_kcs_bmc(filp);
> > 	bool data_avail;
> > 	size_t data_len;
> > 	ssize_t ret;
> >
> > 	if (!(filp->f_flags & O_NONBLOCK))
> >-		wait_event_interruptible(kcs_bmc->queue,
> >-					 kcs_bmc->data_in_avail);
> >+		wait_event_interruptible(priv->queue,
> >+					 priv->data_in_avail);
> >
> >-	mutex_lock(&kcs_bmc->mutex);
> >+	mutex_lock(&priv->mutex);
> >
> >-	spin_lock_irq(&kcs_bmc->lock);
> >-	data_avail = kcs_bmc->data_in_avail;
> >+	spin_lock_irq(&priv->lock);
> >+	data_avail = priv->data_in_avail;
> > 	if (data_avail) {
> >-		data_len = kcs_bmc->data_in_idx;
> >-		memcpy(kcs_bmc->kbuffer, kcs_bmc->data_in, data_len);
> >+		data_len = priv->data_in_idx;
> >+		memcpy(priv->kbuffer, priv->data_in, data_len);
> > 	}
> >-	spin_unlock_irq(&kcs_bmc->lock);
> >+	spin_unlock_irq(&priv->lock);
> >
> > 	if (!data_avail) {
> > 		ret = -EAGAIN;
> >@@ -281,35 +346,35 @@ static ssize_t kcs_bmc_ipmi_read(struct file *filp, char __user *buf,
> >
> > 	if (count < data_len) {
> > 		pr_err("channel=%u with too large data : %zu\n",
> >-			kcs_bmc->channel, data_len);
> >+			priv->client.dev->channel, data_len);
> >
> >-		spin_lock_irq(&kcs_bmc->lock);
> >-		kcs_bmc_ipmi_force_abort(kcs_bmc);
> >-		spin_unlock_irq(&kcs_bmc->lock);
> >+		spin_lock_irq(&priv->lock);
> >+		kcs_bmc_ipmi_force_abort(priv);
> >+		spin_unlock_irq(&priv->lock);
> >
> > 		ret = -EOVERFLOW;
> > 		goto out_unlock;
> > 	}
> >
> >-	if (copy_to_user(buf, kcs_bmc->kbuffer, data_len)) {
> >+	if (copy_to_user(buf, priv->kbuffer, data_len)) {
> > 		ret = -EFAULT;
> > 		goto out_unlock;
> > 	}
> >
> > 	ret = data_len;
> >
> >-	spin_lock_irq(&kcs_bmc->lock);
> >-	if (kcs_bmc->phase == KCS_PHASE_WRITE_DONE) {
> >-		kcs_bmc->phase = KCS_PHASE_WAIT_READ;
> >-		kcs_bmc->data_in_avail = false;
> >-		kcs_bmc->data_in_idx = 0;
> >+	spin_lock_irq(&priv->lock);
> >+	if (priv->phase == KCS_PHASE_WRITE_DONE) {
> >+		priv->phase = KCS_PHASE_WAIT_READ;
> >+		priv->data_in_avail = false;
> >+		priv->data_in_idx = 0;
> > 	} else {
> > 		ret = -EAGAIN;
> > 	}
> >-	spin_unlock_irq(&kcs_bmc->lock);
> >+	spin_unlock_irq(&priv->lock);
> >
> > out_unlock:
> >-	mutex_unlock(&kcs_bmc->mutex);
> >+	mutex_unlock(&priv->mutex);
> >
> > 	return ret;
> > }
> >@@ -317,35 +382,35 @@ static ssize_t kcs_bmc_ipmi_read(struct file *filp, char __user *buf,
> > static ssize_t kcs_bmc_ipmi_write(struct file *filp, const char __user *buf,
> > 			     size_t count, loff_t *ppos)
> > {
> >-	struct kcs_bmc *kcs_bmc = file_to_kcs_bmc(filp);
> >+	struct kcs_bmc_ipmi *priv = to_kcs_bmc(filp);
> > 	ssize_t ret;
> >
> > 	/* a minimum response size '3' : netfn + cmd + ccode */
> > 	if (count < 3 || count > KCS_MSG_BUFSIZ)
> > 		return -EINVAL;
> >
> >-	mutex_lock(&kcs_bmc->mutex);
> >+	mutex_lock(&priv->mutex);
> >
> >-	if (copy_from_user(kcs_bmc->kbuffer, buf, count)) {
> >+	if (copy_from_user(priv->kbuffer, buf, count)) {
> > 		ret = -EFAULT;
> > 		goto out_unlock;
> > 	}
> >
> >-	spin_lock_irq(&kcs_bmc->lock);
> >-	if (kcs_bmc->phase == KCS_PHASE_WAIT_READ) {
> >-		kcs_bmc->phase = KCS_PHASE_READ;
> >-		kcs_bmc->data_out_idx = 1;
> >-		kcs_bmc->data_out_len = count;
> >-		memcpy(kcs_bmc->data_out, kcs_bmc->kbuffer, count);
> >-		kcs_bmc_write_data(kcs_bmc, kcs_bmc->data_out[0]);
> >+	spin_lock_irq(&priv->lock);
> >+	if (priv->phase == KCS_PHASE_WAIT_READ) {
> >+		priv->phase = KCS_PHASE_READ;
> >+		priv->data_out_idx = 1;
> >+		priv->data_out_len = count;
> >+		memcpy(priv->data_out, priv->kbuffer, count);
> >+		kcs_bmc_write_data(priv->client.dev, priv->data_out[0]);
> > 		ret = count;
> > 	} else {
> > 		ret = -EINVAL;
> > 	}
> >-	spin_unlock_irq(&kcs_bmc->lock);
> >+	spin_unlock_irq(&priv->lock);
> >
> > out_unlock:
> >-	mutex_unlock(&kcs_bmc->mutex);
> >+	mutex_unlock(&priv->mutex);
> >
> > 	return ret;
> > }
> >@@ -353,22 +418,22 @@ static ssize_t kcs_bmc_ipmi_write(struct file *filp, const char __user *buf,
> > static long kcs_bmc_ipmi_ioctl(struct file *filp, unsigned int cmd,
> > 			  unsigned long arg)
> > {
> >-	struct kcs_bmc *kcs_bmc = file_to_kcs_bmc(filp);
> >+	struct kcs_bmc_ipmi *priv = to_kcs_bmc(filp);
> > 	long ret = 0;
> >
> >-	spin_lock_irq(&kcs_bmc->lock);
> >+	spin_lock_irq(&priv->lock);
> >
> > 	switch (cmd) {
> > 	case IPMI_BMC_IOCTL_SET_SMS_ATN:
> >-		kcs_bmc_update_status(kcs_bmc, KCS_STATUS_SMS_ATN, KCS_STATUS_SMS_ATN);
> >+		kcs_bmc_update_status(priv->client.dev, KCS_STATUS_SMS_ATN, KCS_STATUS_SMS_ATN);
> > 		break;
> >
> > 	case IPMI_BMC_IOCTL_CLEAR_SMS_ATN:
> >-		kcs_bmc_update_status(kcs_bmc, KCS_STATUS_SMS_ATN, 0);
> >+		kcs_bmc_update_status(priv->client.dev, KCS_STATUS_SMS_ATN, 0);
> > 		break;
> >
> > 	case IPMI_BMC_IOCTL_FORCE_ABORT:
> >-		kcs_bmc_ipmi_force_abort(kcs_bmc);
> >+		kcs_bmc_ipmi_force_abort(priv);
> > 		break;
> >
> > 	default:
> >@@ -376,19 +441,17 @@ static long kcs_bmc_ipmi_ioctl(struct file *filp, unsigned int cmd,
> > 		break;
> > 	}
> >
> >-	spin_unlock_irq(&kcs_bmc->lock);
> >+	spin_unlock_irq(&priv->lock);
> >
> > 	return ret;
> > }
> >
> > static int kcs_bmc_ipmi_release(struct inode *inode, struct file *filp)
> > {
> >-	struct kcs_bmc *kcs_bmc = file_to_kcs_bmc(filp);
> >+	struct kcs_bmc_ipmi *priv = to_kcs_bmc(filp);
> >
> >-	spin_lock_irq(&kcs_bmc->lock);
> >-	kcs_bmc->running = 0;
> >-	kcs_bmc_ipmi_force_abort(kcs_bmc);
> >-	spin_unlock_irq(&kcs_bmc->lock);
> >+	kcs_bmc_ipmi_force_abort(priv);
> >+	kcs_bmc_disable_device(priv->client.dev, &priv->client);
> >
> > 	return 0;
> > }
> >@@ -403,56 +466,78 @@ static const struct file_operations kcs_bmc_ipmi_fops = {
> > 	.unlocked_ioctl = kcs_bmc_ipmi_ioctl,
> > };
> >
> >-int kcs_bmc_ipmi_attach_cdev(struct kcs_bmc *kcs_bmc);
> >-int kcs_bmc_ipmi_attach_cdev(struct kcs_bmc *kcs_bmc)
> >+static DEFINE_SPINLOCK(kcs_bmc_ipmi_instances_lock);
> >+static LIST_HEAD(kcs_bmc_ipmi_instances);
> >+
> >+int kcs_bmc_ipmi_attach_cdev(struct kcs_bmc_device *kcs_bmc);
> >+int kcs_bmc_ipmi_attach_cdev(struct kcs_bmc_device *kcs_bmc)
> > {
> >+	struct kcs_bmc_ipmi *priv;
> > 	int rc;
> >
> >-	spin_lock_init(&kcs_bmc->lock);
> >-	mutex_init(&kcs_bmc->mutex);
> >-	init_waitqueue_head(&kcs_bmc->queue);
> >-
> >-	kcs_bmc->client.dev = kcs_bmc;
> >-	kcs_bmc->client.ops = &kcs_bmc_ipmi_client_ops;
> >-	kcs_bmc->data_in = devm_kmalloc(kcs_bmc->dev, KCS_MSG_BUFSIZ, GFP_KERNEL);
> >-	kcs_bmc->data_out = devm_kmalloc(kcs_bmc->dev, KCS_MSG_BUFSIZ, GFP_KERNEL);
> >-	kcs_bmc->kbuffer = devm_kmalloc(kcs_bmc->dev, KCS_MSG_BUFSIZ, GFP_KERNEL);
> >-
> >-	kcs_bmc->miscdev.minor = MISC_DYNAMIC_MINOR;
> >-	kcs_bmc->miscdev.name = devm_kasprintf(kcs_bmc->dev, GFP_KERNEL, "%s%u",
> >-					       DEVICE_NAME, kcs_bmc->channel);
> >-	if (!kcs_bmc->data_in || !kcs_bmc->data_out || !kcs_bmc->kbuffer ||
> >-	    !kcs_bmc->miscdev.name)
> >+	priv = devm_kzalloc(kcs_bmc->dev, sizeof(*priv), GFP_KERNEL);
> >+	if (!priv)
> > 		return -ENOMEM;
> >
> >-	kcs_bmc->miscdev.fops = &kcs_bmc_ipmi_fops;
> >+	spin_lock_init(&priv->lock);
> >+	mutex_init(&priv->mutex);
> >
> >-	rc = misc_register(&kcs_bmc->miscdev);
> >+	init_waitqueue_head(&priv->queue);
> >+
> >+	priv->client.dev = kcs_bmc;
> >+	priv->client.ops = &kcs_bmc_ipmi_client_ops;
> >+	priv->data_in = devm_kmalloc(kcs_bmc->dev, KCS_MSG_BUFSIZ, GFP_KERNEL);
> >+	priv->data_out = devm_kmalloc(kcs_bmc->dev, KCS_MSG_BUFSIZ, GFP_KERNEL);
> >+	priv->kbuffer = devm_kmalloc(kcs_bmc->dev, KCS_MSG_BUFSIZ, GFP_KERNEL);
> >+
> >+	priv->miscdev.minor = MISC_DYNAMIC_MINOR;
> >+	priv->miscdev.name = devm_kasprintf(kcs_bmc->dev, GFP_KERNEL, "%s%u", DEVICE_NAME,
> >+					   kcs_bmc->channel);
> >+	if (!priv->data_in || !priv->data_out || !priv->kbuffer || !priv->miscdev.name)
> >+		return -EINVAL;
> >+
> >+	priv->miscdev.fops = &kcs_bmc_ipmi_fops;
> >+
> >+	rc = misc_register(&priv->miscdev);
> > 	if (rc) {
> > 		dev_err(kcs_bmc->dev, "Unable to register device: %d\n", rc);
> > 		return rc;
> > 	}
> >
> >+	spin_lock_irq(&kcs_bmc_ipmi_instances_lock);
> >+	list_add(&priv->entry, &kcs_bmc_ipmi_instances);
> >+	spin_unlock_irq(&kcs_bmc_ipmi_instances_lock);
> >+
> > 	dev_info(kcs_bmc->dev, "Initialised IPMI client for channel %d", kcs_bmc->channel);
> >
> > 	return 0;
> > }
> > EXPORT_SYMBOL(kcs_bmc_ipmi_attach_cdev);
> >
> >-int kcs_bmc_ipmi_detach_cdev(struct kcs_bmc *kcs_bmc);
> >-int kcs_bmc_ipmi_detach_cdev(struct kcs_bmc *kcs_bmc)
> >+int kcs_bmc_ipmi_detach_cdev(struct kcs_bmc_device *kcs_bmc);
> >+int kcs_bmc_ipmi_detach_cdev(struct kcs_bmc_device *kcs_bmc)
> > {
> >-	misc_deregister(&kcs_bmc->miscdev);
> >+	struct kcs_bmc_ipmi *priv = NULL, *pos;
> >
> >-	spin_lock_irq(&kcs_bmc->lock);
> >-	kcs_bmc->running = 0;
> >-	kcs_bmc_ipmi_force_abort(kcs_bmc);
> >-	spin_unlock_irq(&kcs_bmc->lock);
> >+	spin_lock_irq(&kcs_bmc_ipmi_instances_lock);
> >+	list_for_each_entry(pos, &kcs_bmc_ipmi_instances, entry) {
> >+		if (pos->client.dev == kcs_bmc) {
> >+			priv = pos;
> >+			list_del(&pos->entry);
> >+			break;
> >+		}
> >+	}
> >+	spin_unlock_irq(&kcs_bmc_ipmi_instances_lock);
> >
> >-	devm_kfree(kcs_bmc->dev, kcs_bmc->kbuffer);
> >-	devm_kfree(kcs_bmc->dev, kcs_bmc->data_out);
> >-	devm_kfree(kcs_bmc->dev, kcs_bmc->data_in);
> >-	devm_kfree(kcs_bmc->dev, kcs_bmc);
> >+	if (!priv)
> >+		return 0;
> 
> Would -ENOENT or something be appropriate here if the thing we're trying
> to detach isn't found?

Maybe -ENODEV. Let me think about this.

Cheers,

Andrew

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

* Re: [PATCH v2 13/21] ipmi: kcs_bmc: Decouple the IPMI chardev from the core
  2021-04-09  4:35   ` Zev Weiss
@ 2021-04-09  6:24     ` Andrew Jeffery
  0 siblings, 0 replies; 67+ messages in thread
From: Andrew Jeffery @ 2021-04-09  6:24 UTC (permalink / raw)
  To: Zev Weiss
  Cc: devicetree, Chia-Wei, Wang, Ryan Chen, Tomer Maimon,
	Corey Minyard, Avi Fishman, Patrick Venture, openbmc,
	linux-kernel, Tali Perry, linux-gpio, Rob Herring,
	linux-arm-kernel, openipmi-developer, Lee Jones, Linus Walleij,
	linux-aspeed, Benjamin Fair



On Fri, 9 Apr 2021, at 14:05, Zev Weiss wrote:
> On Fri, Mar 19, 2021 at 01:27:44AM CDT, Andrew Jeffery wrote:
> >Now that we have untangled the data-structures, split the userspace
> >interface out into its own module. Userspace interfaces and drivers are
> >registered to the KCS BMC core to support arbitrary binding of either.
> >
> >Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
> >---
> > drivers/char/ipmi/Kconfig             | 13 +++++
> > drivers/char/ipmi/Makefile            |  3 +-
> > drivers/char/ipmi/kcs_bmc.c           | 78 ++++++++++++++++++++++++++-
> > drivers/char/ipmi/kcs_bmc.h           |  4 --
> > drivers/char/ipmi/kcs_bmc_cdev_ipmi.c | 33 +++++++++---
> > drivers/char/ipmi/kcs_bmc_client.h    | 14 +++++
> > 6 files changed, 132 insertions(+), 13 deletions(-)
> >
> >diff --git a/drivers/char/ipmi/Kconfig b/drivers/char/ipmi/Kconfig
> >index 07847d9a459a..bc5f81899b62 100644
> >--- a/drivers/char/ipmi/Kconfig
> >+++ b/drivers/char/ipmi/Kconfig
> >@@ -124,6 +124,19 @@ config NPCM7XX_KCS_IPMI_BMC
> > 	  This support is also available as a module.  If so, the module
> > 	  will be called kcs_bmc_npcm7xx.
> >
> >+config IPMI_KCS_BMC_CDEV_IPMI
> >+	depends on IPMI_KCS_BMC
> >+	tristate "IPMI character device interface for BMC KCS devices"
> >+	help
> >+	  Provides a BMC-side character device implementing IPMI
> >+	  semantics for KCS IPMI devices.
> >+
> >+	  Say YES if you wish to expose KCS devices on the BMC for IPMI
> >+	  purposes.
> >+
> >+	  This support is also available as a module. The module will be
> >+	  called kcs_bmc_cdev_ipmi.
> >+
> > config ASPEED_BT_IPMI_BMC
> > 	depends on ARCH_ASPEED || COMPILE_TEST
> > 	depends on REGMAP && REGMAP_MMIO && MFD_SYSCON
> >diff --git a/drivers/char/ipmi/Makefile b/drivers/char/ipmi/Makefile
> >index a302bc865370..fcfa676afddb 100644
> >--- a/drivers/char/ipmi/Makefile
> >+++ b/drivers/char/ipmi/Makefile
> >@@ -22,7 +22,8 @@ obj-$(CONFIG_IPMI_SSIF) += ipmi_ssif.o
> > obj-$(CONFIG_IPMI_POWERNV) += ipmi_powernv.o
> > obj-$(CONFIG_IPMI_WATCHDOG) += ipmi_watchdog.o
> > obj-$(CONFIG_IPMI_POWEROFF) += ipmi_poweroff.o
> >-obj-$(CONFIG_IPMI_KCS_BMC) += kcs_bmc.o kcs_bmc_cdev_ipmi.o
> >+obj-$(CONFIG_IPMI_KCS_BMC) += kcs_bmc.o
> >+obj-$(CONFIG_IPMI_KCS_BMC_CDEV_IPMI) += kcs_bmc_cdev_ipmi.o
> > obj-$(CONFIG_ASPEED_BT_IPMI_BMC) += bt-bmc.o
> > obj-$(CONFIG_ASPEED_KCS_IPMI_BMC) += kcs_bmc_aspeed.o
> > obj-$(CONFIG_NPCM7XX_KCS_IPMI_BMC) += kcs_bmc_npcm7xx.o
> >diff --git a/drivers/char/ipmi/kcs_bmc.c b/drivers/char/ipmi/kcs_bmc.c
> >index 266ebec71d6f..694db6ee2a92 100644
> >--- a/drivers/char/ipmi/kcs_bmc.c
> >+++ b/drivers/char/ipmi/kcs_bmc.c
> >@@ -5,7 +5,9 @@
> >  */
> >
> > #include <linux/device.h>
> >+#include <linux/list.h>
> > #include <linux/module.h>
> >+#include <linux/mutex.h>
> >
> > #include "kcs_bmc.h"
> >
> >@@ -13,6 +15,11 @@
> > #include "kcs_bmc_device.h"
> > #include "kcs_bmc_client.h"
> >
> >+/* Record probed devices and cdevs */
> >+static DEFINE_MUTEX(kcs_bmc_lock);
> >+static LIST_HEAD(kcs_bmc_devices);
> >+static LIST_HEAD(kcs_bmc_cdevs);
> >+
> > /* Consumer data access */
> >
> > u8 kcs_bmc_read_data(struct kcs_bmc_device *kcs_bmc)
> >@@ -100,16 +107,83 @@ EXPORT_SYMBOL(kcs_bmc_disable_device);
> >
> > int kcs_bmc_add_device(struct kcs_bmc_device *kcs_bmc)
> > {
> >-	return kcs_bmc_ipmi_attach_cdev(kcs_bmc);
> >+	struct kcs_bmc_cdev *cdev;
> >+	int rc;
> >+
> >+	spin_lock_init(&kcs_bmc->lock);
> >+	kcs_bmc->client = NULL;
> >+
> >+	mutex_lock(&kcs_bmc_lock);
> >+	list_add(&kcs_bmc->entry, &kcs_bmc_devices);
> >+	list_for_each_entry(cdev, &kcs_bmc_cdevs, entry) {
> >+		rc = cdev->ops->add_device(kcs_bmc);
> >+		if (rc)
> >+			dev_err(kcs_bmc->dev, "Failed to add chardev for KCS channel %d: %d",
> >+				kcs_bmc->channel, rc);
> >+	}
> >+	mutex_unlock(&kcs_bmc_lock);
> >+
> >+	return 0;
> 
> We're ignoring failed ->add_device() calls here?

Yep. If one chardev module is failing to accept new devices we don't 
want to not add them to the remaining chardev modules.

What would the caller do with a error return value? Maybe it should 
just be void.

> 
> > }
> > EXPORT_SYMBOL(kcs_bmc_add_device);
> >
> > int kcs_bmc_remove_device(struct kcs_bmc_device *kcs_bmc)
> > {
> >-	return kcs_bmc_ipmi_detach_cdev(kcs_bmc);
> >+	struct kcs_bmc_cdev *cdev;
> >+	int rc;
> >+
> >+	mutex_lock(&kcs_bmc_lock);
> >+	list_del(&kcs_bmc->entry);
> >+	list_for_each_entry(cdev, &kcs_bmc_cdevs, entry) {
> >+		rc = cdev->ops->remove_device(kcs_bmc);
> >+		if (rc)
> >+			dev_err(kcs_bmc->dev, "Failed to remove chardev for KCS channel %d: %d",
> >+				kcs_bmc->channel, rc);
> >+	}
> >+	mutex_unlock(&kcs_bmc_lock);
> >+
> >+	return 0;
> 
> Similarly with the return value here...

As above.

> 
> > }
> > EXPORT_SYMBOL(kcs_bmc_remove_device);
> >
> >+int kcs_bmc_register_cdev(struct kcs_bmc_cdev *cdev)
> >+{
> >+	struct kcs_bmc_device *kcs_bmc;
> >+	int rc;
> >+
> >+	mutex_lock(&kcs_bmc_lock);
> >+	list_add(&cdev->entry, &kcs_bmc_cdevs);
> >+	list_for_each_entry(kcs_bmc, &kcs_bmc_devices, entry) {
> >+		rc = cdev->ops->add_device(kcs_bmc);
> >+		if (rc)
> >+			dev_err(kcs_bmc->dev, "Failed to add chardev for KCS channel %d: %d",
> >+				kcs_bmc->channel, rc);
> >+	}
> >+	mutex_unlock(&kcs_bmc_lock);
> >+
> >+	return 0;
> 
> ...return value again here...

As above.

> 
> >+}
> >+EXPORT_SYMBOL(kcs_bmc_register_cdev);
> >+
> >+int kcs_bmc_unregister_cdev(struct kcs_bmc_cdev *cdev)
> >+{
> >+	struct kcs_bmc_device *kcs_bmc;
> >+	int rc;
> >+
> >+	mutex_lock(&kcs_bmc_lock);
> >+	list_del(&cdev->entry);
> >+	list_for_each_entry(kcs_bmc, &kcs_bmc_devices, entry) {
> >+		rc = cdev->ops->remove_device(kcs_bmc);
> >+		if (rc)
> >+			dev_err(kcs_bmc->dev, "Failed to add chardev for KCS channel %d: %d",
> 
> s/add/remove/

Thanks.

> 
> Might also want to differentiate the *_device() error messages from the
> *_cdev() ones a bit more?

I'll look into it.

> 
> >+				kcs_bmc->channel, rc);
> >+	}
> >+	mutex_unlock(&kcs_bmc_lock);
> >+
> >+	return rc;
> 
> ...but this one is a bit incongruous, propagating the return value of
> only the last ->remove_device() call.

Hah. good catch.

Andrew

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

* Re: [PATCH v2 10/21] ipmi: kcs_bmc: Turn the driver data-structures inside-out
  2021-04-09  5:59     ` Andrew Jeffery
@ 2021-04-09  6:25       ` Zev Weiss
  2021-04-09 19:26         ` Zev Weiss
  0 siblings, 1 reply; 67+ messages in thread
From: Zev Weiss @ 2021-04-09  6:25 UTC (permalink / raw)
  To: Andrew Jeffery
  Cc: devicetree, Chia-Wei, Wang, Ryan Chen, Tomer Maimon,
	Corey Minyard, Avi Fishman, Patrick Venture, openbmc,
	linux-kernel, Tali Perry, linux-gpio, Rob Herring,
	linux-arm-kernel, openipmi-developer, Lee Jones, Linus Walleij,
	linux-aspeed, Benjamin Fair

On Fri, Apr 09, 2021 at 12:59:09AM CDT, Andrew Jeffery wrote:
>
>
>On Fri, 9 Apr 2021, at 13:27, Zev Weiss wrote:
>> On Fri, Mar 19, 2021 at 01:27:41AM CDT, Andrew Jeffery wrote:
>> >Make the KCS device drivers responsible for allocating their own memory.
>> >
>> >Until now the private data for the device driver was allocated internal
>> >to the private data for the chardev interface. This coupling required
>> >the slightly awkward API of passing through the struct size for the
>> >driver private data to the chardev constructor, and then retrieving a
>> >pointer to the driver private data from the allocated chardev memory.
>> >
>> >In addition to being awkward, the arrangement prevents the
>> >implementation of alternative userspace interfaces as the device driver
>> >private data is not independent.
>> >
>> >Peel a layer off the onion and turn the data-structures inside out by
>> >exploiting container_of() and embedding `struct kcs_device` in the
>> >driver private data.
>> >
>> >Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
>> >---
>> > drivers/char/ipmi/kcs_bmc.c           | 15 +++++--
>> > drivers/char/ipmi/kcs_bmc.h           | 12 ++----
>> > drivers/char/ipmi/kcs_bmc_aspeed.c    | 60 ++++++++++++++++-----------
>> > drivers/char/ipmi/kcs_bmc_cdev_ipmi.c | 60 ++++++++++++++++++---------
>> > drivers/char/ipmi/kcs_bmc_npcm7xx.c   | 37 ++++++++++-------
>> > 5 files changed, 113 insertions(+), 71 deletions(-)
>> >
>> >diff --git a/drivers/char/ipmi/kcs_bmc.c b/drivers/char/ipmi/kcs_bmc.c
>> >index ef5c48ffe74a..709b6bdec165 100644
>> >--- a/drivers/char/ipmi/kcs_bmc.c
>> >+++ b/drivers/char/ipmi/kcs_bmc.c
>> >@@ -44,12 +44,19 @@ int kcs_bmc_handle_event(struct kcs_bmc *kcs_bmc)
>> > }
>> > EXPORT_SYMBOL(kcs_bmc_handle_event);
>> >
>> >-struct kcs_bmc *kcs_bmc_ipmi_alloc(struct device *dev, int sizeof_priv, u32 channel);
>> >-struct kcs_bmc *kcs_bmc_alloc(struct device *dev, int sizeof_priv, u32 channel)
>> >+int kcs_bmc_ipmi_attach_cdev(struct kcs_bmc *kcs_bmc);
>>
>> Another declaration perhaps intended for kcs_bmc.h?
>
>These are temporary while the code gets shuffled around. The symbol
>name is an implementation detail, not a "public" part of the API; after
>some further shuffling these are eventually assigned as callbacks in an
>ops struct.
>

Okay, that makes sense.

>>
>> >+int kcs_bmc_add_device(struct kcs_bmc *kcs_bmc)
>> > {
>> >-	return kcs_bmc_ipmi_alloc(dev, sizeof_priv, channel);
>> >+	return kcs_bmc_ipmi_attach_cdev(kcs_bmc);
>> > }
>> >-EXPORT_SYMBOL(kcs_bmc_alloc);
>> >+EXPORT_SYMBOL(kcs_bmc_add_device);
>> >+
>> >+int kcs_bmc_ipmi_detach_cdev(struct kcs_bmc *kcs_bmc);
>>
>> Here too.
>>
>> >+int kcs_bmc_remove_device(struct kcs_bmc *kcs_bmc)
>> >+{
>> >+	return kcs_bmc_ipmi_detach_cdev(kcs_bmc);
>> >+}
>> >+EXPORT_SYMBOL(kcs_bmc_remove_device);
>> >
>> > MODULE_LICENSE("GPL v2");
>> > MODULE_AUTHOR("Haiyue Wang <haiyue.wang@linux.intel.com>");
>> >diff --git a/drivers/char/ipmi/kcs_bmc.h b/drivers/char/ipmi/kcs_bmc.h
>> >index febea0c8deb4..bf0ae327997f 100644
>> >--- a/drivers/char/ipmi/kcs_bmc.h
>> >+++ b/drivers/char/ipmi/kcs_bmc.h
>> >@@ -67,6 +67,8 @@ struct kcs_ioreg {
>> > };
>> >
>> > struct kcs_bmc {
>> >+	struct device *dev;
>> >+
>> > 	spinlock_t lock;
>> >
>> > 	u32 channel;
>> >@@ -94,17 +96,11 @@ struct kcs_bmc {
>> > 	u8 *kbuffer;
>> >
>> > 	struct miscdevice miscdev;
>> >-
>> >-	unsigned long priv[];
>> > };
>> >
>> >-static inline void *kcs_bmc_priv(struct kcs_bmc *kcs_bmc)
>> >-{
>> >-	return kcs_bmc->priv;
>> >-}
>> >-
>> > int kcs_bmc_handle_event(struct kcs_bmc *kcs_bmc);
>> >-struct kcs_bmc *kcs_bmc_alloc(struct device *dev, int sizeof_priv, u32 channel);
>> >+int kcs_bmc_add_device(struct kcs_bmc *kcs_bmc);
>> >+int kcs_bmc_remove_device(struct kcs_bmc *kcs_bmc);
>> >
>> > u8 kcs_bmc_read_data(struct kcs_bmc *kcs_bmc);
>> > void kcs_bmc_write_data(struct kcs_bmc *kcs_bmc, u8 data);
>> >diff --git a/drivers/char/ipmi/kcs_bmc_aspeed.c b/drivers/char/ipmi/kcs_bmc_aspeed.c
>> >index 630cf095560e..0416ac78ce68 100644
>> >--- a/drivers/char/ipmi/kcs_bmc_aspeed.c
>> >+++ b/drivers/char/ipmi/kcs_bmc_aspeed.c
>> >@@ -61,6 +61,8 @@
>> > #define LPC_STR4             0x11C
>> >
>> > struct aspeed_kcs_bmc {
>> >+	struct kcs_bmc kcs_bmc;
>> >+
>> > 	struct regmap *map;
>> > };
>> >
>> >@@ -69,9 +71,14 @@ struct aspeed_kcs_of_ops {
>> > 	int (*get_io_address)(struct platform_device *pdev);
>> > };
>> >
>> >+static inline struct aspeed_kcs_bmc *to_aspeed_kcs_bmc(struct kcs_bmc *kcs_bmc)
>> >+{
>> >+	return container_of(kcs_bmc, struct aspeed_kcs_bmc, kcs_bmc);
>> >+}
>> >+
>> > static u8 aspeed_kcs_inb(struct kcs_bmc *kcs_bmc, u32 reg)
>> > {
>> >-	struct aspeed_kcs_bmc *priv = kcs_bmc_priv(kcs_bmc);
>> >+	struct aspeed_kcs_bmc *priv = to_aspeed_kcs_bmc(kcs_bmc);
>> > 	u32 val = 0;
>> > 	int rc;
>> >
>> >@@ -83,7 +90,7 @@ static u8 aspeed_kcs_inb(struct kcs_bmc *kcs_bmc, u32 reg)
>> >
>> > static void aspeed_kcs_outb(struct kcs_bmc *kcs_bmc, u32 reg, u8 data)
>> > {
>> >-	struct aspeed_kcs_bmc *priv = kcs_bmc_priv(kcs_bmc);
>> >+	struct aspeed_kcs_bmc *priv = to_aspeed_kcs_bmc(kcs_bmc);
>> > 	int rc;
>> >
>> > 	rc = regmap_write(priv->map, reg, data);
>> >@@ -92,7 +99,7 @@ static void aspeed_kcs_outb(struct kcs_bmc *kcs_bmc, u32 reg, u8 data)
>> >
>> > static void aspeed_kcs_updateb(struct kcs_bmc *kcs_bmc, u32 reg, u8 mask, u8 val)
>> > {
>> >-	struct aspeed_kcs_bmc *priv = kcs_bmc_priv(kcs_bmc);
>> >+	struct aspeed_kcs_bmc *priv = to_aspeed_kcs_bmc(kcs_bmc);
>> > 	int rc;
>> >
>> > 	rc = regmap_update_bits(priv->map, reg, mask, val);
>> >@@ -114,7 +121,7 @@ static void aspeed_kcs_updateb(struct kcs_bmc *kcs_bmc, u32 reg, u8 mask, u8 val
>> >  */
>> > static void aspeed_kcs_set_address(struct kcs_bmc *kcs_bmc, u16 addr)
>> > {
>> >-	struct aspeed_kcs_bmc *priv = kcs_bmc_priv(kcs_bmc);
>> >+	struct aspeed_kcs_bmc *priv = to_aspeed_kcs_bmc(kcs_bmc);
>> >
>> > 	switch (kcs_bmc->channel) {
>> > 	case 1:
>> >@@ -148,7 +155,7 @@ static void aspeed_kcs_set_address(struct kcs_bmc *kcs_bmc, u16 addr)
>> >
>> > static void aspeed_kcs_enable_channel(struct kcs_bmc *kcs_bmc, bool enable)
>> > {
>> >-	struct aspeed_kcs_bmc *priv = kcs_bmc_priv(kcs_bmc);
>> >+	struct aspeed_kcs_bmc *priv = to_aspeed_kcs_bmc(kcs_bmc);
>> >
>> > 	switch (kcs_bmc->channel) {
>> > 	case 1:
>> >@@ -323,16 +330,16 @@ static int aspeed_kcs_of_v2_get_io_address(struct platform_device *pdev)
>> > static int aspeed_kcs_probe(struct platform_device *pdev)
>> > {
>> > 	const struct aspeed_kcs_of_ops *ops;
>> >-	struct device *dev = &pdev->dev;
>> >+	struct aspeed_kcs_bmc *priv;
>> > 	struct kcs_bmc *kcs_bmc;
>> > 	struct device_node *np;
>> > 	int rc, channel, addr;
>> >
>> >-	np = dev->of_node->parent;
>> >+	np = pdev->dev.of_node->parent;
>> > 	if (!of_device_is_compatible(np, "aspeed,ast2400-lpc-v2") &&
>> > 	    !of_device_is_compatible(np, "aspeed,ast2500-lpc-v2") &&
>> > 	    !of_device_is_compatible(np, "aspeed,ast2600-lpc-v2")) {
>> >-		dev_err(dev, "unsupported LPC device binding\n");
>> >+		dev_err(&pdev->dev, "unsupported LPC device binding\n");
>> > 		return -ENODEV;
>> > 	}
>> > 	ops = of_device_get_match_data(&pdev->dev);
>> >@@ -343,18 +350,27 @@ static int aspeed_kcs_probe(struct platform_device *pdev)
>> > 	if (channel < 0)
>> > 		return channel;
>> >
>> >-	kcs_bmc = kcs_bmc_alloc(&pdev->dev, sizeof(struct aspeed_kcs_bmc), channel);
>> >-	if (!kcs_bmc)
>> >+	addr = ops->get_io_address(pdev);
>> >+	if (addr < 0)
>> >+		return addr;
>> >+
>> >+	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
>> >+	if (!priv)
>> > 		return -ENOMEM;
>> >
>> >+	kcs_bmc = &priv->kcs_bmc;
>> >+	kcs_bmc->dev = &pdev->dev;
>> >+	kcs_bmc->channel = channel;
>> > 	kcs_bmc->ioreg = ast_kcs_bmc_ioregs[channel - 1];
>> > 	kcs_bmc->io_inputb = aspeed_kcs_inb;
>> > 	kcs_bmc->io_outputb = aspeed_kcs_outb;
>> > 	kcs_bmc->io_updateb = aspeed_kcs_updateb;
>> >
>> >-	addr = ops->get_io_address(pdev);
>> >-	if (addr < 0)
>> >-		return addr;
>> >+	priv->map = syscon_node_to_regmap(pdev->dev.parent->of_node);
>> >+	if (IS_ERR(priv->map)) {
>> >+		dev_err(&pdev->dev, "Couldn't get regmap\n");
>> >+		return -ENODEV;
>> >+	}
>>
>> The reanimated priv->map initialization I suspect wasn't meant to
>> have been removed in the first place...
>
>Yeah, I'll have to go back and figure out what went wrong there!
>
>Thanks for catching that.
>
>>
>> >
>> > 	aspeed_kcs_set_address(kcs_bmc, addr);
>> >
>> >@@ -362,29 +378,25 @@ static int aspeed_kcs_probe(struct platform_device *pdev)
>> > 	if (rc)
>> > 		return rc;
>> >
>> >-	dev_set_drvdata(dev, kcs_bmc);
>> >+	platform_set_drvdata(pdev, priv);
>> >
>> > 	aspeed_kcs_enable_channel(kcs_bmc, true);
>> >
>> >-	rc = misc_register(&kcs_bmc->miscdev);
>> >-	if (rc) {
>> >-		dev_err(dev, "Unable to register device\n");
>> >+	rc = kcs_bmc_add_device(&priv->kcs_bmc);
>> >+	if (rc < 0)
>> > 		return rc;
>> >-	}
>> >
>> >-	dev_dbg(&pdev->dev,
>> >-		"Probed KCS device %d (IDR=0x%x, ODR=0x%x, STR=0x%x)\n",
>> >-		kcs_bmc->channel, kcs_bmc->ioreg.idr, kcs_bmc->ioreg.odr,
>> >-		kcs_bmc->ioreg.str);
>> >+	dev_info(&pdev->dev, "Initialised channel %d at 0x%x\n", kcs_bmc->channel, addr);
>>
>> Is the dbg->info change here intentional?  (I have no particular
>> objection if so, but it's often a change I make myself during
>> testing/debugging and then forget to revert...)
>
>Yeah, it was possibly something I forgot to revert. If others have
>issues with it staying at dev_info() I'll switch it back.
>
>>
>> >
>> > 	return 0;
>> > }
>> >
>> > static int aspeed_kcs_remove(struct platform_device *pdev)
>> > {
>> >-	struct kcs_bmc *kcs_bmc = dev_get_drvdata(&pdev->dev);
>> >+	struct aspeed_kcs_bmc *priv = platform_get_drvdata(pdev);
>> >+	struct kcs_bmc *kcs_bmc = &priv->kcs_bmc;
>> >
>> >-	misc_deregister(&kcs_bmc->miscdev);
>> >+	kcs_bmc_remove_device(kcs_bmc);
>>
>> Should we propagate the return value outward here?
>
>Probably!
>
>>
>> >
>> > 	return 0;
>> > }
>> >diff --git a/drivers/char/ipmi/kcs_bmc_cdev_ipmi.c b/drivers/char/ipmi/kcs_bmc_cdev_ipmi.c
>> >index 82c77994e481..0ca71c135a1a 100644
>> >--- a/drivers/char/ipmi/kcs_bmc_cdev_ipmi.c
>> >+++ b/drivers/char/ipmi/kcs_bmc_cdev_ipmi.c
>> >@@ -382,7 +382,7 @@ static int kcs_bmc_ipmi_release(struct inode *inode, struct file *filp)
>> > 	return 0;
>> > }
>> >
>> >-static const struct file_operations kcs_bmc_fops = {
>> >+static const struct file_operations kcs_bmc_ipmi_fops = {
>> > 	.owner          = THIS_MODULE,
>> > 	.open           = kcs_bmc_ipmi_open,
>> > 	.read           = kcs_bmc_ipmi_read,
>> >@@ -392,36 +392,58 @@ static const struct file_operations kcs_bmc_fops = {
>> > 	.unlocked_ioctl = kcs_bmc_ipmi_ioctl,
>> > };
>> >
>> >-struct kcs_bmc *kcs_bmc_ipmi_alloc(struct device *dev, int sizeof_priv, u32 channel);
>> >-struct kcs_bmc *kcs_bmc_ipmi_alloc(struct device *dev, int sizeof_priv, u32 channel)
>> >+int kcs_bmc_ipmi_attach_cdev(struct kcs_bmc *kcs_bmc);
>>
>> Errant declaration again?
>
>As previously explained.
>

This one seems like a slightly different category, because...

>>
>> >+int kcs_bmc_ipmi_attach_cdev(struct kcs_bmc *kcs_bmc)

...it's immediately followed by the definition of the very same function
that it just declared, so I can't see how its presence or absence could
make any functional difference to anything.  (So perhaps I should have
said "redundant" instead of "errant...again".)

It's fairly trivial of course given that it's gone by the end of the
series, but as long as there's going to be another iteration anyway it
seems like we might as well tidy it up?

>> > {
>> >-	struct kcs_bmc *kcs_bmc;
>> >-
>> >-	kcs_bmc = devm_kzalloc(dev, sizeof(*kcs_bmc) + sizeof_priv, GFP_KERNEL);
>> >-	if (!kcs_bmc)
>> >-		return NULL;
>> >+	int rc;
>> >
>> > 	spin_lock_init(&kcs_bmc->lock);
>> >-	kcs_bmc->channel = channel;
>> >-
>> > 	mutex_init(&kcs_bmc->mutex);
>> > 	init_waitqueue_head(&kcs_bmc->queue);
>> >
>> >-	kcs_bmc->data_in = devm_kmalloc(dev, KCS_MSG_BUFSIZ, GFP_KERNEL);
>> >-	kcs_bmc->data_out = devm_kmalloc(dev, KCS_MSG_BUFSIZ, GFP_KERNEL);
>> >-	kcs_bmc->kbuffer = devm_kmalloc(dev, KCS_MSG_BUFSIZ, GFP_KERNEL);
>> >+	kcs_bmc->data_in = devm_kmalloc(kcs_bmc->dev, KCS_MSG_BUFSIZ, GFP_KERNEL);
>> >+	kcs_bmc->data_out = devm_kmalloc(kcs_bmc->dev, KCS_MSG_BUFSIZ, GFP_KERNEL);
>> >+	kcs_bmc->kbuffer = devm_kmalloc(kcs_bmc->dev, KCS_MSG_BUFSIZ, GFP_KERNEL);
>> >
>> > 	kcs_bmc->miscdev.minor = MISC_DYNAMIC_MINOR;
>> >-	kcs_bmc->miscdev.name = devm_kasprintf(dev, GFP_KERNEL, "%s%u",
>> >-					       DEVICE_NAME, channel);
>> >+	kcs_bmc->miscdev.name = devm_kasprintf(kcs_bmc->dev, GFP_KERNEL, "%s%u",
>> >+					       DEVICE_NAME, kcs_bmc->channel);
>> > 	if (!kcs_bmc->data_in || !kcs_bmc->data_out || !kcs_bmc->kbuffer ||
>> > 	    !kcs_bmc->miscdev.name)
>> >-		return NULL;
>> >-	kcs_bmc->miscdev.fops = &kcs_bmc_fops;
>> >+		return -ENOMEM;
>> >
>> >-	return kcs_bmc;
>> >+	kcs_bmc->miscdev.fops = &kcs_bmc_ipmi_fops;
>> >+
>> >+	rc = misc_register(&kcs_bmc->miscdev);
>> >+	if (rc) {
>> >+		dev_err(kcs_bmc->dev, "Unable to register device: %d\n", rc);
>> >+		return rc;
>> >+	}
>> >+
>> >+	dev_info(kcs_bmc->dev, "Initialised IPMI client for channel %d", kcs_bmc->channel);
>> >+
>> >+	return 0;
>> >+}
>> >+EXPORT_SYMBOL(kcs_bmc_ipmi_attach_cdev);
>> >+
>> >+int kcs_bmc_ipmi_detach_cdev(struct kcs_bmc *kcs_bmc);
>>
>> Same here.
>
>Same explanation.
>

Also a "category 2" I think.

>>
>> >+int kcs_bmc_ipmi_detach_cdev(struct kcs_bmc *kcs_bmc)
>> >+{
>> >+	misc_deregister(&kcs_bmc->miscdev);
>> >+
>> >+	spin_lock_irq(&kcs_bmc->lock);
>> >+	kcs_bmc->running = 0;
>> >+	kcs_bmc_ipmi_force_abort(kcs_bmc);
>> >+	spin_unlock_irq(&kcs_bmc->lock);
>> >+
>> >+	devm_kfree(kcs_bmc->dev, kcs_bmc->kbuffer);
>> >+	devm_kfree(kcs_bmc->dev, kcs_bmc->data_out);
>> >+	devm_kfree(kcs_bmc->dev, kcs_bmc->data_in);
>> >+	devm_kfree(kcs_bmc->dev, kcs_bmc);
>> >+
>> >+	return 0;
>> > }
>> >-EXPORT_SYMBOL(kcs_bmc_ipmi_alloc);
>> >+EXPORT_SYMBOL(kcs_bmc_ipmi_detach_cdev);
>> >
>> > MODULE_LICENSE("GPL v2");
>> > MODULE_AUTHOR("Haiyue Wang <haiyue.wang@linux.intel.com>");
>> >diff --git a/drivers/char/ipmi/kcs_bmc_npcm7xx.c b/drivers/char/ipmi/kcs_bmc_npcm7xx.c
>> >index 1f44aadec9e8..5d017498dc69 100644
>> >--- a/drivers/char/ipmi/kcs_bmc_npcm7xx.c
>> >+++ b/drivers/char/ipmi/kcs_bmc_npcm7xx.c
>> >@@ -65,6 +65,8 @@ struct npcm7xx_kcs_reg {
>> > };
>> >
>> > struct npcm7xx_kcs_bmc {
>> >+	struct kcs_bmc kcs_bmc;
>> >+
>> > 	struct regmap *map;
>> >
>> > 	const struct npcm7xx_kcs_reg *reg;
>> >@@ -76,9 +78,14 @@ static const struct npcm7xx_kcs_reg npcm7xx_kcs_reg_tbl[KCS_CHANNEL_MAX] = {
>> > 	{ .sts = KCS3ST, .dob = KCS3DO, .dib = KCS3DI, .ctl = KCS3CTL, .ie = KCS3IE },
>> > };
>> >
>> >+static inline struct npcm7xx_kcs_bmc *to_npcm7xx_kcs_bmc(struct kcs_bmc *kcs_bmc)
>> >+{
>> >+	return container_of(kcs_bmc, struct npcm7xx_kcs_bmc, kcs_bmc);
>> >+}
>> >+
>> > static u8 npcm7xx_kcs_inb(struct kcs_bmc *kcs_bmc, u32 reg)
>> > {
>> >-	struct npcm7xx_kcs_bmc *priv = kcs_bmc_priv(kcs_bmc);
>> >+	struct npcm7xx_kcs_bmc *priv = to_npcm7xx_kcs_bmc(kcs_bmc);
>> > 	u32 val = 0;
>> > 	int rc;
>> >
>> >@@ -90,7 +97,7 @@ static u8 npcm7xx_kcs_inb(struct kcs_bmc *kcs_bmc, u32 reg)
>> >
>> > static void npcm7xx_kcs_outb(struct kcs_bmc *kcs_bmc, u32 reg, u8 data)
>> > {
>> >-	struct npcm7xx_kcs_bmc *priv = kcs_bmc_priv(kcs_bmc);
>> >+	struct npcm7xx_kcs_bmc *priv = to_npcm7xx_kcs_bmc(kcs_bmc);
>> > 	int rc;
>> >
>> > 	rc = regmap_write(priv->map, reg, data);
>> >@@ -99,7 +106,7 @@ static void npcm7xx_kcs_outb(struct kcs_bmc *kcs_bmc, u32 reg, u8 data)
>> >
>> > static void npcm7xx_kcs_updateb(struct kcs_bmc *kcs_bmc, u32 reg, u8 mask, u8 data)
>> > {
>> >-	struct npcm7xx_kcs_bmc *priv = kcs_bmc_priv(kcs_bmc);
>> >+	struct npcm7xx_kcs_bmc *priv = to_npcm7xx_kcs_bmc(kcs_bmc);
>> > 	int rc;
>> >
>> > 	rc = regmap_update_bits(priv->map, reg, mask, data);
>> >@@ -108,7 +115,7 @@ static void npcm7xx_kcs_updateb(struct kcs_bmc *kcs_bmc, u32 reg, u8 mask, u8 da
>> >
>> > static void npcm7xx_kcs_enable_channel(struct kcs_bmc *kcs_bmc, bool enable)
>> > {
>> >-	struct npcm7xx_kcs_bmc *priv = kcs_bmc_priv(kcs_bmc);
>> >+	struct npcm7xx_kcs_bmc *priv = to_npcm7xx_kcs_bmc(kcs_bmc);
>> >
>> > 	regmap_update_bits(priv->map, priv->reg->ctl, KCS_CTL_IBFIE,
>> > 			   enable ? KCS_CTL_IBFIE : 0);
>> >@@ -155,11 +162,10 @@ static int npcm7xx_kcs_probe(struct platform_device *pdev)
>> > 		return -ENODEV;
>> > 	}
>> >
>> >-	kcs_bmc = kcs_bmc_alloc(dev, sizeof(*priv), chan);
>> >-	if (!kcs_bmc)
>> >+	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
>> >+	if (!priv)
>> > 		return -ENOMEM;
>> >
>> >-	priv = kcs_bmc_priv(kcs_bmc);
>> > 	priv->map = syscon_node_to_regmap(dev->parent->of_node);
>> > 	if (IS_ERR(priv->map)) {
>> > 		dev_err(dev, "Couldn't get regmap\n");
>> >@@ -167,6 +173,9 @@ static int npcm7xx_kcs_probe(struct platform_device *pdev)
>> > 	}
>> > 	priv->reg = &npcm7xx_kcs_reg_tbl[chan - 1];
>> >
>> >+	kcs_bmc = &priv->kcs_bmc;
>> >+	kcs_bmc->dev = &pdev->dev;
>> >+	kcs_bmc->channel = chan;
>> > 	kcs_bmc->ioreg.idr = priv->reg->dib;
>> > 	kcs_bmc->ioreg.odr = priv->reg->dob;
>> > 	kcs_bmc->ioreg.str = priv->reg->sts;
>> >@@ -174,31 +183,27 @@ static int npcm7xx_kcs_probe(struct platform_device *pdev)
>> > 	kcs_bmc->io_outputb = npcm7xx_kcs_outb;
>> > 	kcs_bmc->io_updateb = npcm7xx_kcs_updateb;
>> >
>> >-	dev_set_drvdata(dev, kcs_bmc);
>> >+	platform_set_drvdata(pdev, priv);
>> >
>> > 	npcm7xx_kcs_enable_channel(kcs_bmc, true);
>> > 	rc = npcm7xx_kcs_config_irq(kcs_bmc, pdev);
>> > 	if (rc)
>> > 		return rc;
>> >
>> >-	rc = misc_register(&kcs_bmc->miscdev);
>> >-	if (rc) {
>> >-		dev_err(dev, "Unable to register device\n");
>> >-		return rc;
>> >-	}
>> >
>> > 	pr_info("channel=%u idr=0x%x odr=0x%x str=0x%x\n",
>> > 		chan,
>> > 		kcs_bmc->ioreg.idr, kcs_bmc->ioreg.odr, kcs_bmc->ioreg.str);
>> >
>> >-	return 0;
>> >+	return kcs_bmc_add_device(kcs_bmc);
>> > }
>> >
>> > static int npcm7xx_kcs_remove(struct platform_device *pdev)
>> > {
>> >-	struct kcs_bmc *kcs_bmc = dev_get_drvdata(&pdev->dev);
>> >+	struct npcm7xx_kcs_bmc *priv = platform_get_drvdata(pdev);
>> >+	struct kcs_bmc *kcs_bmc = &priv->kcs_bmc;
>> >
>> >-	misc_deregister(&kcs_bmc->miscdev);
>> >+	kcs_bmc_remove_device(kcs_bmc);
>>
>> As with the corresponding aspeed code, should we propagate the return
>> value here?
>
>I'll address this.
>
>Thanks for the review!
>
>Andrew

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

* Re: [PATCH v2 14/21] ipmi: kcs_bmc: Allow clients to control KCS IRQ state
  2021-04-09  4:37   ` Zev Weiss
@ 2021-04-09  6:39     ` Andrew Jeffery
  0 siblings, 0 replies; 67+ messages in thread
From: Andrew Jeffery @ 2021-04-09  6:39 UTC (permalink / raw)
  To: Zev Weiss
  Cc: devicetree, Chia-Wei, Wang, Ryan Chen, Tomer Maimon,
	Corey Minyard, Avi Fishman, Patrick Venture, openbmc,
	linux-kernel, Tali Perry, linux-gpio, Rob Herring,
	linux-arm-kernel, openipmi-developer, Lee Jones, Linus Walleij,
	linux-aspeed, Benjamin Fair



On Fri, 9 Apr 2021, at 14:07, Zev Weiss wrote:
> On Fri, Mar 19, 2021 at 01:27:45AM CDT, Andrew Jeffery wrote:
> >Add a mechanism for controlling whether the client associated with a
> >KCS device will receive Input Buffer Full (IBF) and Output Buffer Empty
> >(OBE) events. This enables an abstract implementation of poll() for KCS
> >devices.
> >
> >A wart in the implementation is that the ASPEED KCS devices don't
> >support an OBE interrupt for the BMC. Instead we pretend it has one by
> >polling the status register waiting for the Output Buffer Full (OBF) bit
> >to clear, and generating an event when OBE is observed.
> >
> >Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
> >---
> > drivers/char/ipmi/kcs_bmc.c         |   6 ++
> > drivers/char/ipmi/kcs_bmc.h         |   3 +
> > drivers/char/ipmi/kcs_bmc_aspeed.c  | 150 ++++++++++++++++++----------
> > drivers/char/ipmi/kcs_bmc_client.h  |   2 +
> > drivers/char/ipmi/kcs_bmc_device.h  |   1 +
> > drivers/char/ipmi/kcs_bmc_npcm7xx.c |  25 ++++-
> > 6 files changed, 130 insertions(+), 57 deletions(-)
> >
> >diff --git a/drivers/char/ipmi/kcs_bmc.c b/drivers/char/ipmi/kcs_bmc.c
> >index 694db6ee2a92..05bbb72418b2 100644
> >--- a/drivers/char/ipmi/kcs_bmc.c
> >+++ b/drivers/char/ipmi/kcs_bmc.c
> >@@ -184,6 +184,12 @@ int kcs_bmc_unregister_cdev(struct kcs_bmc_cdev *cdev)
> > }
> > EXPORT_SYMBOL(kcs_bmc_unregister_cdev);
> >
> >+void kcs_bmc_update_event_mask(struct kcs_bmc_device *kcs_bmc, u8 mask, u8 events)
> >+{
> >+	kcs_bmc->ops->irq_mask_update(kcs_bmc, mask, events);
> >+}
> >+EXPORT_SYMBOL(kcs_bmc_update_event_mask);
> >+
> > MODULE_LICENSE("GPL v2");
> > MODULE_AUTHOR("Haiyue Wang <haiyue.wang@linux.intel.com>");
> > MODULE_AUTHOR("Andrew Jeffery <andrew@aj.id.au>");
> >diff --git a/drivers/char/ipmi/kcs_bmc.h b/drivers/char/ipmi/kcs_bmc.h
> >index 5deb9a0b8e60..11fff935218c 100644
> >--- a/drivers/char/ipmi/kcs_bmc.h
> >+++ b/drivers/char/ipmi/kcs_bmc.h
> >@@ -11,6 +11,9 @@
> > #define KCS_BMC_EVENT_NONE	0
> > #define KCS_BMC_EVENT_HANDLED	1
> >
> >+#define KCS_BMC_EVENT_TYPE_OBE	BIT(0)
> >+#define KCS_BMC_EVENT_TYPE_IBF	BIT(1)
> >+
> > #define KCS_BMC_STR_OBF		BIT(0)
> > #define KCS_BMC_STR_IBF		BIT(1)
> > #define KCS_BMC_STR_CMD_DAT	BIT(3)
> >diff --git a/drivers/char/ipmi/kcs_bmc_aspeed.c b/drivers/char/ipmi/kcs_bmc_aspeed.c
> >index 6f26e7366c0b..5f26471c038c 100644
> >--- a/drivers/char/ipmi/kcs_bmc_aspeed.c
> >+++ b/drivers/char/ipmi/kcs_bmc_aspeed.c
> >@@ -60,10 +60,18 @@
> > #define LPC_ODR4             0x118
> > #define LPC_STR4             0x11C
> >
> >+#define OBE_POLL_PERIOD	     (HZ / 2)
> >+
> > struct aspeed_kcs_bmc {
> > 	struct kcs_bmc_device kcs_bmc;
> >
> > 	struct regmap *map;
> >+
> >+	struct {
> >+		spinlock_t lock;
> >+		bool remove;
> >+		struct timer_list timer;
> >+	} obe;
> > };
> >
> > struct aspeed_kcs_of_ops {
> >@@ -159,68 +167,89 @@ static void aspeed_kcs_enable_channel(struct kcs_bmc_device *kcs_bmc, bool enabl
> >
> > 	switch (kcs_bmc->channel) {
> > 	case 1:
> >-		if (enable) {
> >-			regmap_update_bits(priv->map, LPC_HICR2,
> >-					LPC_HICR2_IBFIF1, LPC_HICR2_IBFIF1);
> >-			regmap_update_bits(priv->map, LPC_HICR0,
> >-					LPC_HICR0_LPC1E, LPC_HICR0_LPC1E);
> >-		} else {
> >-			regmap_update_bits(priv->map, LPC_HICR0,
> >-					LPC_HICR0_LPC1E, 0);
> >-			regmap_update_bits(priv->map, LPC_HICR2,
> >-					LPC_HICR2_IBFIF1, 0);
> >-		}
> >-		break;
> >-
> >+		regmap_update_bits(priv->map, LPC_HICR0, LPC_HICR0_LPC1E, enable * LPC_HICR0_LPC1E);
> >+		return;
> > 	case 2:
> >-		if (enable) {
> >-			regmap_update_bits(priv->map, LPC_HICR2,
> >-					LPC_HICR2_IBFIF2, LPC_HICR2_IBFIF2);
> >-			regmap_update_bits(priv->map, LPC_HICR0,
> >-					LPC_HICR0_LPC2E, LPC_HICR0_LPC2E);
> >-		} else {
> >-			regmap_update_bits(priv->map, LPC_HICR0,
> >-					LPC_HICR0_LPC2E, 0);
> >-			regmap_update_bits(priv->map, LPC_HICR2,
> >-					LPC_HICR2_IBFIF2, 0);
> >-		}
> >-		break;
> >-
> >+		regmap_update_bits(priv->map, LPC_HICR0, LPC_HICR0_LPC2E, enable * LPC_HICR0_LPC2E);
> >+		return;
> > 	case 3:
> >-		if (enable) {
> >-			regmap_update_bits(priv->map, LPC_HICR2,
> >-					LPC_HICR2_IBFIF3, LPC_HICR2_IBFIF3);
> >-			regmap_update_bits(priv->map, LPC_HICR0,
> >-					LPC_HICR0_LPC3E, LPC_HICR0_LPC3E);
> >-			regmap_update_bits(priv->map, LPC_HICR4,
> >-					LPC_HICR4_KCSENBL, LPC_HICR4_KCSENBL);
> >-		} else {
> >-			regmap_update_bits(priv->map, LPC_HICR0,
> >-					LPC_HICR0_LPC3E, 0);
> >-			regmap_update_bits(priv->map, LPC_HICR4,
> >-					LPC_HICR4_KCSENBL, 0);
> >-			regmap_update_bits(priv->map, LPC_HICR2,
> >-					LPC_HICR2_IBFIF3, 0);
> >-		}
> >-		break;
> >-
> >+		regmap_update_bits(priv->map, LPC_HICR0, LPC_HICR0_LPC3E, enable * LPC_HICR0_LPC3E);
> >+		regmap_update_bits(priv->map, LPC_HICR4,
> >+				   LPC_HICR4_KCSENBL, enable * LPC_HICR4_KCSENBL);
> >+		return;
> > 	case 4:
> >-		if (enable)
> >-			regmap_update_bits(priv->map, LPC_HICRB,
> >-					LPC_HICRB_IBFIF4 | LPC_HICRB_LPC4E,
> >-					LPC_HICRB_IBFIF4 | LPC_HICRB_LPC4E);
> >+		regmap_update_bits(priv->map, LPC_HICRB, LPC_HICRB_LPC4E, enable * LPC_HICRB_LPC4E);
> >+		return;
> >+	default:
> >+		pr_warn("%s: Unsupported channel: %d", __func__, kcs_bmc->channel);
> >+		return;
> >+	}
> >+}
> >+
> >+static void aspeed_kcs_check_obe(struct timer_list *timer)
> >+{
> >+	struct aspeed_kcs_bmc *priv = container_of(timer, struct aspeed_kcs_bmc, obe.timer);
> >+	unsigned long flags;
> >+	u8 str;
> >+
> >+	spin_lock_irqsave(&priv->obe.lock, flags);
> >+	if (priv->obe.remove) {
> >+		spin_unlock_irqrestore(&priv->obe.lock, flags);
> >+		return;
> >+	}
> >+
> >+	str = aspeed_kcs_inb(&priv->kcs_bmc, priv->kcs_bmc.ioreg.str);
> >+	if (str & KCS_BMC_STR_OBF) {
> >+		mod_timer(timer, jiffies + OBE_POLL_PERIOD);
> >+		spin_unlock_irqrestore(&priv->obe.lock, flags);
> >+		return;
> >+	}
> >+	spin_unlock_irqrestore(&priv->obe.lock, flags);
> >+
> >+	kcs_bmc_handle_event(&priv->kcs_bmc);
> >+}
> >+
> >+static void aspeed_kcs_irq_mask_update(struct kcs_bmc_device *kcs_bmc, u8 mask, u8 state)
> >+{
> >+	struct aspeed_kcs_bmc *priv = to_aspeed_kcs_bmc(kcs_bmc);
> >+
> >+	/* We don't have an OBE IRQ, emulate it */
> >+	if (KCS_BMC_EVENT_TYPE_OBE & mask) {
> >+		if (KCS_BMC_EVENT_TYPE_OBE & state)
> >+			mod_timer(&priv->obe.timer, jiffies + OBE_POLL_PERIOD);
> > 		else
> >-			regmap_update_bits(priv->map, LPC_HICRB,
> >-					LPC_HICRB_IBFIF4 | LPC_HICRB_LPC4E,
> >-					0);
> >-		break;
> >+			del_timer(&priv->obe.timer);
> >+	}
> >
> >-	default:
> >-		break;
> >+	if (KCS_BMC_EVENT_TYPE_IBF & mask) {
> >+		const bool enable = !!(KCS_BMC_EVENT_TYPE_IBF & state);
> 
> Totally superficial nitpick: the operand ordering for the bitmask tests
> in this function seem a bit inconsistent with what I think is the usual
> style of 'variable & BITMASK_MACRO' (reminiscent of "yoda
> conditionals").

Haha, I'm not sure what possessed me here.

> 
> >+
> >+		switch (kcs_bmc->channel) {
> >+		case 1:
> >+			regmap_update_bits(priv->map, LPC_HICR2, LPC_HICR2_IBFIF1,
> >+					   enable * LPC_HICR2_IBFIF1);
> >+			return;
> >+		case 2:
> >+			regmap_update_bits(priv->map, LPC_HICR2, LPC_HICR2_IBFIF2,
> >+					   enable * LPC_HICR2_IBFIF2);
> >+			return;
> >+		case 3:
> >+			regmap_update_bits(priv->map, LPC_HICR2, LPC_HICR2_IBFIF3,
> >+					   enable * LPC_HICR2_IBFIF3);
> >+			return;
> >+		case 4:
> >+			regmap_update_bits(priv->map, LPC_HICRB, LPC_HICRB_IBFIF4,
> >+					   enable * LPC_HICRB_IBFIF4);
> >+			return;
> >+		default:
> >+			pr_warn("%s: Unsupported channel: %d", __func__, kcs_bmc->channel);
> >+			return;
> >+		}
> > 	}
> > }
> >
> > static const struct kcs_bmc_device_ops aspeed_kcs_ops = {
> >+	.irq_mask_update = aspeed_kcs_irq_mask_update,
> > 	.io_inputb = aspeed_kcs_inb,
> > 	.io_outputb = aspeed_kcs_outb,
> > 	.io_updateb = aspeed_kcs_updateb,
> >@@ -378,6 +407,10 @@ static int aspeed_kcs_probe(struct platform_device *pdev)
> > 		return -ENODEV;
> > 	}
> >
> >+	spin_lock_init(&priv->obe.lock);
> >+	priv->obe.remove = false;
> >+	timer_setup(&priv->obe.timer, aspeed_kcs_check_obe, 0);
> >+
> > 	aspeed_kcs_set_address(kcs_bmc, addr);
> >
> > 	rc = aspeed_kcs_config_irq(kcs_bmc, pdev);
> >@@ -386,6 +419,8 @@ static int aspeed_kcs_probe(struct platform_device *pdev)
> >
> > 	platform_set_drvdata(pdev, priv);
> >
> >+	aspeed_kcs_irq_mask_update(kcs_bmc, (KCS_BMC_EVENT_TYPE_IBF | KCS_BMC_EVENT_TYPE_OBE),
> >+				   KCS_BMC_EVENT_TYPE_IBF);
> > 	aspeed_kcs_enable_channel(kcs_bmc, true);
> >
> > 	rc = kcs_bmc_add_device(&priv->kcs_bmc);
> >@@ -404,6 +439,15 @@ static int aspeed_kcs_remove(struct platform_device *pdev)
> >
> > 	kcs_bmc_remove_device(kcs_bmc);
> >
> >+	aspeed_kcs_enable_channel(kcs_bmc, false);
> >+	aspeed_kcs_irq_mask_update(kcs_bmc, (KCS_BMC_EVENT_TYPE_IBF | KCS_BMC_EVENT_TYPE_OBE), 0);
> >+
> >+	/* Make sure it's proper dead */
> >+	spin_lock_irq(&priv->obe.lock);
> >+	priv->obe.remove = true;
> >+	spin_unlock_irq(&priv->obe.lock);
> >+	del_timer_sync(&priv->obe.timer);
> >+
> > 	return 0;
> > }
> >
> >diff --git a/drivers/char/ipmi/kcs_bmc_client.h b/drivers/char/ipmi/kcs_bmc_client.h
> >index d0a7404ff584..456796da33de 100644
> >--- a/drivers/char/ipmi/kcs_bmc_client.h
> >+++ b/drivers/char/ipmi/kcs_bmc_client.h
> >@@ -37,6 +37,8 @@ int kcs_bmc_unregister_cdev(struct kcs_bmc_cdev *cdev);
> > int kcs_bmc_enable_device(struct kcs_bmc_device *kcs_bmc, struct kcs_bmc_client *client);
> > void kcs_bmc_disable_device(struct kcs_bmc_device *kcs_bmc, struct kcs_bmc_client *client);
> >
> >+void kcs_bmc_update_event_mask(struct kcs_bmc_device *kcs_bmc, u8 mask, u8 events);
> >+
> > u8 kcs_bmc_read_data(struct kcs_bmc_device *kcs_bmc);
> > void kcs_bmc_write_data(struct kcs_bmc_device *kcs_bmc, u8 data);
> > u8 kcs_bmc_read_status(struct kcs_bmc_device *kcs_bmc);
> >diff --git a/drivers/char/ipmi/kcs_bmc_device.h b/drivers/char/ipmi/kcs_bmc_device.h
> >index 57b7174b2bac..f1ca8912496a 100644
> >--- a/drivers/char/ipmi/kcs_bmc_device.h
> >+++ b/drivers/char/ipmi/kcs_bmc_device.h
> >@@ -7,6 +7,7 @@
> > #include "kcs_bmc.h"
> >
> > struct kcs_bmc_device_ops {
> >+	void (*irq_mask_update)(struct kcs_bmc_device *kcs_bmc, u8 mask, u8 enable);
> > 	u8 (*io_inputb)(struct kcs_bmc_device *kcs_bmc, u32 reg);
> > 	void (*io_outputb)(struct kcs_bmc_device *kcs_bmc, u32 reg, u8 b);
> > 	void (*io_updateb)(struct kcs_bmc_device *kcs_bmc, u32 reg, u8 mask, u8 b);
> >diff --git a/drivers/char/ipmi/kcs_bmc_npcm7xx.c b/drivers/char/ipmi/kcs_bmc_npcm7xx.c
> >index dce93ec895fc..c2032728a03d 100644
> >--- a/drivers/char/ipmi/kcs_bmc_npcm7xx.c
> >+++ b/drivers/char/ipmi/kcs_bmc_npcm7xx.c
> >@@ -38,6 +38,7 @@
> > #define KCS2CTL		0x2A
> > #define KCS3CTL		0x3C
> > #define    KCS_CTL_IBFIE	BIT(0)
> >+#define    KCS_CTL_OBEIE	BIT(0)
> >
> > #define KCS1IE		0x1C
> > #define KCS2IE		0x2E
> >@@ -117,13 +118,23 @@ static void npcm7xx_kcs_enable_channel(struct kcs_bmc_device *kcs_bmc, bool enab
> > {
> > 	struct npcm7xx_kcs_bmc *priv = to_npcm7xx_kcs_bmc(kcs_bmc);
> >
> >-	regmap_update_bits(priv->map, priv->reg->ctl, KCS_CTL_IBFIE,
> >-			   enable ? KCS_CTL_IBFIE : 0);
> >-
> > 	regmap_update_bits(priv->map, priv->reg->ie, KCS_IE_IRQE | KCS_IE_HIRQE,
> > 			   enable ? KCS_IE_IRQE | KCS_IE_HIRQE : 0);
> > }
> >
> >+static void npcm7xx_kcs_irq_mask_update(struct kcs_bmc_device *kcs_bmc, u8 mask, u8 state)
> >+{
> >+	struct npcm7xx_kcs_bmc *priv = to_npcm7xx_kcs_bmc(kcs_bmc);
> >+
> >+	if (KCS_BMC_EVENT_TYPE_OBE & mask)
> >+		regmap_update_bits(priv->map, priv->reg->ctl, KCS_CTL_OBEIE,
> >+				   !!(KCS_BMC_EVENT_TYPE_OBE & state) * KCS_CTL_OBEIE);
> >+
> >+	if (KCS_BMC_EVENT_TYPE_IBF & mask)
> >+		regmap_update_bits(priv->map, priv->reg->ctl, KCS_CTL_IBFIE,
> >+				   !!(KCS_BMC_EVENT_TYPE_IBF & state) * KCS_CTL_IBFIE);
> 
> Same operand ordering thing here...

Ack.

Andrew

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

* Re: [PATCH v2 15/21] ipmi: kcs_bmc: Don't enforce single-open policy in the kernel
  2021-04-09  5:07   ` Zev Weiss
@ 2021-04-09  6:42     ` Andrew Jeffery
  0 siblings, 0 replies; 67+ messages in thread
From: Andrew Jeffery @ 2021-04-09  6:42 UTC (permalink / raw)
  To: openbmc



On Fri, 9 Apr 2021, at 14:37, Zev Weiss wrote:
> On Fri, Mar 19, 2021 at 01:27:46AM CDT, Andrew Jeffery wrote:
> >Soon it will be possible for one KCS device to have multiple associated
> >chardevs exposed to userspace (for IPMI and raw-style access). However,
> >don't prevent userspace from:
> >
> >1. Opening more than one chardev at a time, or
> >2. Opening the same chardev more than once.
> >
> >System behaviour is undefined for both classes of multiple access, so
> >userspace must manage itself accordingly.
> >
> >The implementation delivers IBF and OBF events to the first chardev
> >client to associate with the KCS device. An open on a related chardev
> >cannot associate its client with the KCS device and so will not
> >receive notification of events. However, any fd on any chardev may race
> >their accesses to the data and status registers.
> >
> >Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
> >---
> > drivers/char/ipmi/kcs_bmc.c         | 34 ++++++++++-------------------
> > drivers/char/ipmi/kcs_bmc_aspeed.c  |  3 +--
> > drivers/char/ipmi/kcs_bmc_npcm7xx.c |  3 +--
> > 3 files changed, 14 insertions(+), 26 deletions(-)
> >
> >diff --git a/drivers/char/ipmi/kcs_bmc.c b/drivers/char/ipmi/kcs_bmc.c
> >index 05bbb72418b2..2fafa9541934 100644
> >--- a/drivers/char/ipmi/kcs_bmc.c
> >+++ b/drivers/char/ipmi/kcs_bmc.c
> >@@ -55,24 +55,12 @@ EXPORT_SYMBOL(kcs_bmc_update_status);
> > int kcs_bmc_handle_event(struct kcs_bmc_device *kcs_bmc)
> > {
> > 	struct kcs_bmc_client *client;
> >-	int rc;
> >+	int rc = KCS_BMC_EVENT_NONE;
> >
> > 	spin_lock(&kcs_bmc->lock);
> > 	client = kcs_bmc->client;
> >-	if (client) {
> >+	if (!WARN_ON_ONCE(!client))
> > 		rc = client->ops->event(client);
> 
> The double-negation split by a macro seems a bit confusing to me
> readability-wise;

I did a poll internally about that and I didn't get any complaints :D

> could we simplify to something like
> 
> 	if (client)
> 		rc = client->ops->event(client);
> 	else
> 		WARN_ONCE();
> 
> ?

I guess.

> 
> >-	} else {
> >-		u8 status;
> >-
> >-		status = kcs_bmc_read_status(kcs_bmc);
> >-		if (status & KCS_BMC_STR_IBF) {
> >-			/* Ack the event by reading the data */
> >-			kcs_bmc_read_data(kcs_bmc);
> >-			rc = KCS_BMC_EVENT_HANDLED;
> >-		} else {
> >-			rc = KCS_BMC_EVENT_NONE;
> >-		}
> >-	}
> > 	spin_unlock(&kcs_bmc->lock);
> >
> > 	return rc;
> >@@ -81,26 +69,28 @@ EXPORT_SYMBOL(kcs_bmc_handle_event);
> >
> > int kcs_bmc_enable_device(struct kcs_bmc_device *kcs_bmc, struct kcs_bmc_client *client)
> > {
> >-	int rc;
> >-
> > 	spin_lock_irq(&kcs_bmc->lock);
> >-	if (kcs_bmc->client) {
> >-		rc = -EBUSY;
> >-	} else {
> >+	if (!kcs_bmc->client) {
> >+		u8 mask = KCS_BMC_EVENT_TYPE_IBF;
> >+
> > 		kcs_bmc->client = client;
> >-		rc = 0;
> >+		kcs_bmc_update_event_mask(kcs_bmc, mask, mask);
> > 	}
> > 	spin_unlock_irq(&kcs_bmc->lock);
> >
> >-	return rc;
> >+	return 0;
> 
> Since this function appears to be infallible now, should it just return
> void?  (Might be more churn than it's worth...shrug.)

Yeah, I think I was being a little lazy here.

Cheers,

Andrew

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

* Re: [PATCH v2 16/21] ipmi: kcs_bmc: Add a "raw" character device interface
  2021-04-09  5:17   ` Zev Weiss
@ 2021-04-09  6:46     ` Andrew Jeffery
  0 siblings, 0 replies; 67+ messages in thread
From: Andrew Jeffery @ 2021-04-09  6:46 UTC (permalink / raw)
  To: Zev Weiss
  Cc: devicetree, Chia-Wei, Wang, Ryan Chen, Tomer Maimon,
	Corey Minyard, Avi Fishman, Patrick Venture, openbmc,
	linux-kernel, Tali Perry, linux-gpio, Rob Herring,
	linux-arm-kernel, openipmi-developer, Lee Jones, Linus Walleij,
	linux-aspeed, Benjamin Fair



On Fri, 9 Apr 2021, at 14:47, Zev Weiss wrote:
> On Fri, Mar 19, 2021 at 01:27:47AM CDT, Andrew Jeffery wrote:
> >The existing IPMI chardev encodes IPMI behaviours as the name suggests.
> >However, KCS devices are useful beyond IPMI (or keyboards), as they
> >provide a means to generate IRQs and exchange arbitrary data between a
> >BMC and its host system.
> >
> >Implement a "raw" KCS character device that exposes the IDR, ODR and STR
> >registers to userspace via read() and write() implemented on a character
> >device:
> >
> >+--------+--------+---------+
> >| Offset | read() | write() |
> >+--------+--------+---------+
> >|   0    |   IDR  |   ODR   |
> >+--------+--------+---------+
> >|   1    |   STR  |   STR   |
> >+--------+--------+---------+
> >
> >This interface allows userspace to implement arbitrary (though somewhat
> >inefficient) protocols for exchanging information between a BMC and host
> >firmware. Conceptually the KCS interface can be used as an out-of-band
> >machanism for interrupt-signaled control messages while bulk data
> 
> Typo ("mechanism")

Ack.

> 
> >transfers occur over more appropriate interfaces between the BMC and the
> >host (which may lack their own interrupt mechanism, e.g. LPC FW cycles).
> >
> >poll() is provided, which will wait for IBF or OBE conditions for data
> >reads and writes respectively. Reads of STR on its own never blocks,
> >though accessing both offsets in the one system call may block if the
> >data registers are not ready.
> >
> >Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
> >---
> > Documentation/ABI/testing/dev-raw-kcs |  25 ++
> > drivers/char/ipmi/Kconfig             |  17 +
> > drivers/char/ipmi/Makefile            |   1 +
> > drivers/char/ipmi/kcs_bmc_cdev_raw.c  | 443 ++++++++++++++++++++++++++
> > 4 files changed, 486 insertions(+)
> > create mode 100644 Documentation/ABI/testing/dev-raw-kcs
> > create mode 100644 drivers/char/ipmi/kcs_bmc_cdev_raw.c
> >
> >diff --git a/Documentation/ABI/testing/dev-raw-kcs b/Documentation/ABI/testing/dev-raw-kcs
> >new file mode 100644
> >index 000000000000..06e7e2071562
> >--- /dev/null
> >+++ b/Documentation/ABI/testing/dev-raw-kcs
> >@@ -0,0 +1,25 @@
> >+What:		/dev/raw-kcs*
> >+Date:		2021-02-15
> >+KernelVersion:	5.13
> >+Contact:	openbmc@lists.ozlabs.org
> >+Contact:	openipmi-developer@lists.sourceforge.net
> >+Contact:	Andrew Jeffery <andrew@aj.id.au>
> >+Description:	``/dev/raw-kcs*`` exposes to userspace the data and
> >+		status registers of Keyboard-Controller-Style (KCS) IPMI
> >+		interfaces via read() and write() syscalls. Direct
> >+		exposure of the data and status registers enables
> >+		inefficient but arbitrary protocols to be implemented
> >+		over the device. A typical approach is to use KCS
> >+		devices for out-of-band signalling for bulk data
> >+		transfers over other interfaces between a Baseboard
> >+		Management Controller and its host.
> >+
> >+		+--------+--------+---------+
> >+		| Offset | read() | write() |
> >+		+--------+--------+---------+
> >+		|   0    |   IDR  |   ODR   |
> >+		+--------+--------+---------+
> >+		|   1    |   STR  |   STR   |
> >+		+--------+--------+---------+
> >+
> >+Users:		libmctp: https://github.com/openbmc/libmctp
> >diff --git a/drivers/char/ipmi/Kconfig b/drivers/char/ipmi/Kconfig
> >index bc5f81899b62..273ac1a1f870 100644
> >--- a/drivers/char/ipmi/Kconfig
> >+++ b/drivers/char/ipmi/Kconfig
> >@@ -137,6 +137,23 @@ config IPMI_KCS_BMC_CDEV_IPMI
> > 	  This support is also available as a module. The module will be
> > 	  called kcs_bmc_cdev_ipmi.
> >
> >+config IPMI_KCS_BMC_CDEV_RAW
> >+	depends on IPMI_KCS_BMC
> >+	tristate "Raw character device interface for BMC KCS devices"
> >+	help
> >+	  Provides a BMC-side character device directly exposing the
> >+	  data and status registers of a KCS device to userspace. While
> >+	  KCS devices are commonly used to implement IPMI message
> >+	  passing, they provide a general interface for exchange of
> >+	  interrupts, data and status information between the BMC and
> >+	  its host.
> >+
> >+	  Say YES if you wish to use the KCS devices to implement
> >+	  protocols that are not IPMI.
> >+
> >+	  This support is also available as a module. The module will be
> >+	  called kcs_bmc_cdev_raw.
> >+
> > config ASPEED_BT_IPMI_BMC
> > 	depends on ARCH_ASPEED || COMPILE_TEST
> > 	depends on REGMAP && REGMAP_MMIO && MFD_SYSCON
> >diff --git a/drivers/char/ipmi/Makefile b/drivers/char/ipmi/Makefile
> >index fcfa676afddb..c8cc248ddd90 100644
> >--- a/drivers/char/ipmi/Makefile
> >+++ b/drivers/char/ipmi/Makefile
> >@@ -24,6 +24,7 @@ obj-$(CONFIG_IPMI_WATCHDOG) += ipmi_watchdog.o
> > obj-$(CONFIG_IPMI_POWEROFF) += ipmi_poweroff.o
> > obj-$(CONFIG_IPMI_KCS_BMC) += kcs_bmc.o
> > obj-$(CONFIG_IPMI_KCS_BMC_CDEV_IPMI) += kcs_bmc_cdev_ipmi.o
> >+obj-$(CONFIG_IPMI_KCS_BMC_CDEV_RAW) += kcs_bmc_cdev_raw.o
> > obj-$(CONFIG_ASPEED_BT_IPMI_BMC) += bt-bmc.o
> > obj-$(CONFIG_ASPEED_KCS_IPMI_BMC) += kcs_bmc_aspeed.o
> > obj-$(CONFIG_NPCM7XX_KCS_IPMI_BMC) += kcs_bmc_npcm7xx.o
> >diff --git a/drivers/char/ipmi/kcs_bmc_cdev_raw.c b/drivers/char/ipmi/kcs_bmc_cdev_raw.c
> >new file mode 100644
> >index 000000000000..bdd258648c8e
> >--- /dev/null
> >+++ b/drivers/char/ipmi/kcs_bmc_cdev_raw.c
> >@@ -0,0 +1,443 @@
> >+// SPDX-License-Identifier: GPL-2.0-or-later
> >+/* Copyright (c) 2021 IBM Corp. */
> >+
> >+#include <linux/delay.h>
> >+#include <linux/device.h>
> >+#include <linux/errno.h>
> >+#include <linux/fs.h>
> >+#include <linux/list.h>
> >+#include <linux/miscdevice.h>
> >+#include <linux/module.h>
> >+#include <linux/poll.h>
> >+
> >+#include "kcs_bmc_client.h"
> >+
> >+#define DEVICE_NAME "raw-kcs"
> >+
> >+struct kcs_bmc_raw {
> >+	struct list_head entry;
> >+
> >+	struct kcs_bmc_client client;
> >+
> >+	wait_queue_head_t queue;
> >+	u8 events;
> >+	bool writable;
> >+	bool readable;
> >+	u8 idr;
> >+
> >+	struct miscdevice miscdev;
> >+};
> >+
> >+static inline struct kcs_bmc_raw *client_to_kcs_bmc_raw(struct kcs_bmc_client *client)
> >+{
> >+	return container_of(client, struct kcs_bmc_raw, client);
> >+}
> >+
> >+/* Call under priv->queue.lock */
> >+static void kcs_bmc_raw_update_event_mask(struct kcs_bmc_raw *priv, u8 mask, u8 state)
> >+{
> >+	kcs_bmc_update_event_mask(priv->client.dev, mask, state);
> >+	priv->events &= ~mask;
> >+	priv->events |= state & mask;
> >+}
> >+
> >+static int kcs_bmc_raw_event(struct kcs_bmc_client *client)
> >+{
> >+	struct kcs_bmc_raw *priv;
> >+	struct device *dev;
> >+	u8 status, handled;
> >+
> >+	priv = client_to_kcs_bmc_raw(client);
> >+	dev = priv->miscdev.this_device;
> >+
> >+	spin_lock(&priv->queue.lock);
> >+
> >+	status = kcs_bmc_read_status(client->dev);
> >+	handled = 0;
> >+
> >+	if ((priv->events & KCS_BMC_EVENT_TYPE_IBF) && (status & KCS_BMC_STR_IBF)) {
> >+		if (priv->readable)
> >+			dev_err(dev, "Storm brewing!");
> 
> That seems a *touch* cryptic...

Uh, yeah. That wasn't meant to be there in that form.

> 
> >+
> >+		dev_dbg(dev, "Disabling IDR events for back-pressure\n");
> >+		kcs_bmc_raw_update_event_mask(priv, KCS_BMC_EVENT_TYPE_IBF, 0);
> >+		priv->idr = kcs_bmc_read_data(client->dev);
> >+		priv->readable = true;
> >+
> >+		dev_dbg(dev, "IDR read, waking waiters\n");
> >+		wake_up_locked(&priv->queue);
> >+
> >+		handled |= KCS_BMC_EVENT_TYPE_IBF;
> >+	}
> >+
> >+	if ((priv->events & KCS_BMC_EVENT_TYPE_OBE) && !(status & KCS_BMC_STR_OBF)) {
> >+		kcs_bmc_raw_update_event_mask(priv, KCS_BMC_EVENT_TYPE_OBE, 0);
> >+		priv->writable = true;
> >+
> >+		dev_dbg(dev, "ODR writable, waking waiters\n");
> >+		wake_up_locked(&priv->queue);
> >+
> >+		handled |= KCS_BMC_EVENT_TYPE_OBE;
> >+	}
> >+
> >+	spin_unlock(&priv->queue.lock);
> >+
> >+	return handled ? KCS_BMC_EVENT_HANDLED : KCS_BMC_EVENT_NONE;
> 
> Hm, if we're just treating it as a boolean here, is there any need to
> muck around with setting specific bits of 'handled' in the if-blocks
> above?

I don't think it matters? If we want to debug we can print the handled bitmask.

> 
> >+}
> >+
> >+static const struct kcs_bmc_client_ops kcs_bmc_raw_client_ops = {
> >+	.event = kcs_bmc_raw_event,
> >+};
> >+
> >+static inline struct kcs_bmc_raw *file_to_kcs_bmc_raw(struct file *filp)
> >+{
> >+	return container_of(filp->private_data, struct kcs_bmc_raw, miscdev);
> >+}
> >+
> >+static int kcs_bmc_raw_open(struct inode *inode, struct file *filp)
> >+{
> >+	struct kcs_bmc_raw *priv = file_to_kcs_bmc_raw(filp);
> >+
> >+	return kcs_bmc_enable_device(priv->client.dev, &priv->client);
> >+}
> >+
> >+static bool kcs_bmc_raw_prepare_obe(struct kcs_bmc_raw *priv)
> >+{
> >+	bool writable;
> >+
> >+	/* Enable the OBE event so we can catch the host clearing OBF */
> >+	kcs_bmc_raw_update_event_mask(priv, KCS_BMC_EVENT_TYPE_OBE, KCS_BMC_EVENT_TYPE_OBE);
> >+
> >+	/* Now that we'll catch an OBE event, check if it's already occurred */
> >+	writable = !(kcs_bmc_read_status(priv->client.dev) & KCS_BMC_STR_OBF);
> >+
> >+	/* If OBF is clear we've missed the OBE event, so disable it */
> >+	if (writable)
> >+		kcs_bmc_raw_update_event_mask(priv, KCS_BMC_EVENT_TYPE_OBE, 0);
> >+
> >+	return writable;
> >+}
> >+
> >+static __poll_t kcs_bmc_raw_poll(struct file *filp, poll_table *wait)
> >+{
> >+	struct kcs_bmc_raw *priv;
> >+	__poll_t events = 0;
> >+
> >+	priv = file_to_kcs_bmc_raw(filp);
> >+
> >+	poll_wait(filp, &priv->queue, wait);
> >+
> >+	spin_lock_irq(&priv->queue.lock);
> >+	if (kcs_bmc_raw_prepare_obe(priv))
> >+		events |= (EPOLLOUT | EPOLLWRNORM);
> >+
> >+	if (priv->readable || (kcs_bmc_read_status(priv->client.dev) & KCS_BMC_STR_IBF))
> >+		events |= (EPOLLIN | EPOLLRDNORM);
> >+	spin_unlock_irq(&priv->queue.lock);
> >+
> >+	return events;
> >+}
> >+
> >+static ssize_t kcs_bmc_raw_read(struct file *filp, char __user *buf,
> >+			     size_t count, loff_t *ppos)
> >+{
> >+	struct kcs_bmc_device *kcs_bmc;
> >+	struct kcs_bmc_raw *priv;
> >+	bool read_idr, read_str;
> >+	struct device *dev;
> >+	u8 idr, str;
> >+	ssize_t rc;
> >+
> >+	priv = file_to_kcs_bmc_raw(filp);
> >+	kcs_bmc = priv->client.dev;
> >+	dev = priv->miscdev.this_device;
> >+
> >+	if (!count)
> >+		return 0;
> >+
> >+	if (count > 2 || *ppos > 1)
> >+		return -EINVAL;
> >+
> >+	if (*ppos + count > 2)
> >+		return -EINVAL;
> >+
> >+	read_idr = (*ppos == 0);
> >+	read_str = (*ppos == 1) || (count == 2);
> >+
> >+	spin_lock_irq(&priv->queue.lock);
> >+	if (read_idr) {
> >+		dev_dbg(dev, "Waiting for IBF\n");
> >+		str = kcs_bmc_read_status(kcs_bmc);
> >+		if ((filp->f_flags & O_NONBLOCK) && (str & KCS_BMC_STR_IBF)) {
> >+			rc = -EWOULDBLOCK;
> >+			goto out;
> >+		}
> >+
> >+		rc = wait_event_interruptible_locked(priv->queue,
> >+						     priv->readable || (str & KCS_BMC_STR_IBF));
> >+		if (rc < 0)
> >+			goto out;
> >+
> >+		if (signal_pending(current)) {
> >+			dev_dbg(dev, "Interrupted waiting for IBF\n");
> >+			rc = -EINTR;
> >+			goto out;
> >+		}
> >+
> >+		/*
> >+		 * Re-enable events prior to possible read of IDR (which clears
> >+		 * IBF) to ensure we receive interrupts for subsequent writes
> >+		 * to IDR. Writes to IDR by the host should not occur while IBF
> >+		 * is set.
> >+		 */
> >+		dev_dbg(dev, "Woken by IBF, enabling IRQ\n");
> >+		kcs_bmc_raw_update_event_mask(priv, KCS_BMC_EVENT_TYPE_IBF,
> >+					      KCS_BMC_EVENT_TYPE_IBF);
> >+
> >+		/* Read data out of IDR into internal storage if necessary */
> >+		if (!priv->readable) {
> >+			WARN(!(str & KCS_BMC_STR_IBF), "Unknown reason for wakeup!");
> >+
> >+			priv->idr = kcs_bmc_read_data(kcs_bmc);
> >+		}
> >+
> >+		/* Copy data from internal storage to userspace */
> >+		idr = priv->idr;
> >+
> >+		/* We're done consuming the internally stored value */
> >+		priv->readable = false;
> >+	}
> >+
> >+	if (read_str) {
> >+		str = kcs_bmc_read_status(kcs_bmc);
> >+		if (*ppos == 0 || priv->readable)
> >+			/*
> >+			 * If we got this far with `*ppos == 0` then we've read
> >+			 * data out of IDR, so set IBF when reporting back to
> >+			 * userspace so userspace knows the IDR value is valid.
> >+			 */
> >+			str |= KCS_BMC_STR_IBF;
> >+
> >+		dev_dbg(dev, "Read status 0x%x\n", str);
> >+
> >+	}
> >+
> >+	rc = count;
> >+out:
> >+	spin_unlock_irq(&priv->queue.lock);
> >+
> >+	if (rc < 0)
> >+		return rc;
> >+
> >+	/* Now copy the data in to the userspace buffer */
> >+
> >+	if (read_idr)
> >+		if (copy_to_user(buf++, &idr, sizeof(idr)))
> >+			return -EFAULT;
> >+
> >+	if (read_str)
> >+		if (copy_to_user(buf, &str, sizeof(str)))
> >+			return -EFAULT;
> >+
> >+	return count;
> >+}
> >+
> >+static ssize_t kcs_bmc_raw_write(struct file *filp, const char __user *buf,
> >+			      size_t count, loff_t *ppos)
> >+{
> >+	struct kcs_bmc_device *kcs_bmc;
> >+	bool write_odr, write_str;
> >+	struct kcs_bmc_raw *priv;
> >+	struct device *dev;
> >+	uint8_t data[2];
> >+	ssize_t result;
> >+	u8 str;
> >+
> >+	priv = file_to_kcs_bmc_raw(filp);
> >+	kcs_bmc = priv->client.dev;
> >+	dev = priv->miscdev.this_device;
> >+
> >+	if (!count)
> >+		return count;
> >+
> >+	if (count > 2)
> >+		return -EINVAL;
> >+
> >+	if (*ppos >= 2)
> >+		return -EINVAL;
> >+
> >+	if (*ppos + count > 2)
> >+		return -EINVAL;
> >+
> >+	if (copy_from_user(data, buf, count))
> >+		return -EFAULT;
> >+
> >+	write_odr = (*ppos == 0);
> >+	write_str = (*ppos == 1) || (count == 2);
> >+
> >+	spin_lock_irq(&priv->queue.lock);
> >+
> >+	/* Always write status before data, we generate the SerIRQ by writing ODR */
> >+	if (write_str) {
> >+		/* The index of STR in the userspace buffer depends on whether ODR is written */
> >+		str = data[*ppos == 0];
> >+		if (!(str & KCS_BMC_STR_OBF))
> >+			dev_warn(dev, "Clearing OBF with status write: 0x%x\n", str);
> >+		dev_dbg(dev, "Writing status 0x%x\n", str);
> >+		kcs_bmc_write_status(kcs_bmc, str);
> >+	}
> >+
> >+	if (write_odr) {
> >+		/* If we're writing ODR it's always the first byte in the buffer */
> >+		u8 odr = data[0];
> >+
> >+		str = kcs_bmc_read_status(kcs_bmc);
> >+		if (str & KCS_BMC_STR_OBF) {
> >+			if (filp->f_flags & O_NONBLOCK) {
> >+				result = -EWOULDBLOCK;
> >+				goto out;
> >+			}
> >+
> >+			priv->writable = kcs_bmc_raw_prepare_obe(priv);
> >+
> >+			/* Now either OBF is already clear, or we'll get an OBE event to wake us */
> >+			dev_dbg(dev, "Waiting for OBF to clear\n");
> >+			wait_event_interruptible_locked(priv->queue, priv->writable);
> >+
> >+			if (signal_pending(current)) {
> >+				kcs_bmc_raw_update_event_mask(priv, KCS_BMC_EVENT_TYPE_OBE, 0);
> >+				result = -EINTR;
> >+				goto out;
> >+			}
> >+
> >+			WARN_ON(kcs_bmc_read_status(kcs_bmc) & KCS_BMC_STR_OBF);
> >+		}
> >+
> >+		dev_dbg(dev, "Writing 0x%x to ODR\n", odr);
> >+		kcs_bmc_write_data(kcs_bmc, odr);
> >+	}
> >+
> >+	result = count;
> >+out:
> >+	spin_unlock_irq(&priv->queue.lock);
> >+
> >+	return result;
> >+}
> >+
> >+static int kcs_bmc_raw_release(struct inode *inode, struct file *filp)
> >+{
> >+	struct kcs_bmc_raw *priv = file_to_kcs_bmc_raw(filp);
> >+
> >+	kcs_bmc_disable_device(priv->client.dev, &priv->client);
> >+
> >+	return 0;
> >+}
> >+
> >+static const struct file_operations kcs_bmc_raw_fops = {
> >+	.owner          = THIS_MODULE,
> >+	.open		= kcs_bmc_raw_open,
> >+	.llseek		= no_seek_end_llseek,
> >+	.read           = kcs_bmc_raw_read,
> >+	.write          = kcs_bmc_raw_write,
> >+	.poll		= kcs_bmc_raw_poll,
> >+	.release	= kcs_bmc_raw_release,
> >+};
> >+
> >+static DEFINE_SPINLOCK(kcs_bmc_raw_instances_lock);
> >+static LIST_HEAD(kcs_bmc_raw_instances);
> >+
> >+static int kcs_bmc_raw_attach_cdev(struct kcs_bmc_device *kcs_bmc)
> >+{
> >+	struct kcs_bmc_raw *priv;
> >+	int rc;
> >+
> >+	priv = devm_kzalloc(kcs_bmc->dev, sizeof(*priv), GFP_KERNEL);
> >+	if (!priv)
> >+		return -ENOMEM;
> >+
> >+	priv->client.dev = kcs_bmc;
> >+	priv->client.ops = &kcs_bmc_raw_client_ops;
> >+
> >+	init_waitqueue_head(&priv->queue);
> >+	priv->writable = false;
> >+	priv->readable = false;
> >+
> >+	priv->miscdev.minor = MISC_DYNAMIC_MINOR;
> >+	priv->miscdev.name = devm_kasprintf(kcs_bmc->dev, GFP_KERNEL, "%s%u", DEVICE_NAME,
> >+					   kcs_bmc->channel);
> >+	if (!priv->miscdev.name)
> >+		return -EINVAL;
> >+
> >+	priv->miscdev.fops = &kcs_bmc_raw_fops;
> >+
> >+	/* Initialise our expected events. Listen for IBF but ignore OBE until necessary */
> >+	kcs_bmc_raw_update_event_mask(priv, (KCS_BMC_EVENT_TYPE_IBF | KCS_BMC_EVENT_TYPE_OBE),
> >+				      KCS_BMC_EVENT_TYPE_IBF);
> >+
> >+	rc = misc_register(&priv->miscdev);
> >+	if (rc) {
> >+		dev_err(kcs_bmc->dev, "Unable to register device\n");
> >+		return rc;
> >+	}
> >+
> >+	spin_lock_irq(&kcs_bmc_raw_instances_lock);
> >+	list_add(&priv->entry, &kcs_bmc_raw_instances);
> >+	spin_unlock_irq(&kcs_bmc_raw_instances_lock);
> >+
> >+	dev_info(kcs_bmc->dev, "Initialised raw client for channel %d", kcs_bmc->channel);
> >+
> >+	return 0;
> >+}
> >+
> >+static int kcs_bmc_raw_detach_cdev(struct kcs_bmc_device *kcs_bmc)
> >+{
> >+	struct kcs_bmc_raw *priv = NULL, *pos;
> >+
> >+	spin_lock_irq(&kcs_bmc_raw_instances_lock);
> >+	list_for_each_entry(pos, &kcs_bmc_raw_instances, entry) {
> >+		if (pos->client.dev == kcs_bmc) {
> >+			priv = pos;
> >+			list_del(&pos->entry);
> >+			break;
> >+		}
> >+	}
> >+	spin_unlock_irq(&kcs_bmc_raw_instances_lock);
> >+
> >+	if (!priv)
> >+		return 0;
> 
> Similarly to patch #12, might we want to indicate some sort of failure
> here, or is this a normal/expected case?

I replied on 12/21, I'll have another think about it.

Cheers,

Andrew

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

* Re: [PATCH v2 16/21] ipmi: kcs_bmc: Add a "raw" character device interface
  2021-03-19  6:27 ` [PATCH v2 16/21] ipmi: kcs_bmc: Add a "raw" character device interface Andrew Jeffery
  2021-04-09  5:17   ` Zev Weiss
@ 2021-04-09  7:55   ` Arnd Bergmann
  2021-04-12  1:33     ` Andrew Jeffery
  1 sibling, 1 reply; 67+ messages in thread
From: Arnd Bergmann @ 2021-04-09  7:55 UTC (permalink / raw)
  To: Andrew Jeffery
  Cc: Linux ARM, DTML, chiawei_wang, Ryan Chen, Tomer Maimon,
	Corey Minyard, Avi Fishman, Patrick Venture, OpenBMC Maillist,
	Linux Kernel Mailing List, Tali Perry, open list:GPIO SUBSYSTEM,
	Rob Herring, openipmi-developer, Lee Jones, Linus Walleij,
	linux-aspeed, Benjamin Fair

On Fri, Mar 19, 2021 at 7:31 AM Andrew Jeffery <andrew@aj.id.au> wrote:
>
> The existing IPMI chardev encodes IPMI behaviours as the name suggests.
> However, KCS devices are useful beyond IPMI (or keyboards), as they
> provide a means to generate IRQs and exchange arbitrary data between a
> BMC and its host system.

I only noticed the series after Joel asked about the DT changes on the arm
side. One question though:

How does this related to the drivers/input/serio/ framework that also talks
to the keyboard controller for things that are not keyboards? Are these
separate communication channels on adjacent I/O ports, or does there
need to be some arbitration?

       Arnd

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

* Re: [PATCH v2 17/21] dt-bindings: ipmi: Convert ASPEED KCS binding to schema
  2021-04-09  5:44       ` Zev Weiss
@ 2021-04-09  8:46         ` Zev Weiss
  0 siblings, 0 replies; 67+ messages in thread
From: Zev Weiss @ 2021-04-09  8:46 UTC (permalink / raw)
  To: Andrew Jeffery, g
  Cc: devicetree, Chia-Wei, Wang, Ryan Chen, Tomer Maimon,
	Corey Minyard, Avi Fishman, Patrick Venture, openbmc,
	linux-kernel, Tali Perry, linux-gpio, Rob Herring,
	linux-arm-kernel, openipmi-developer, Lee Jones, Linus Walleij,
	linux-aspeed, Benjamin Fair

On Fri, Apr 09, 2021 at 12:44:04AM CDT, Zev Weiss wrote:
>On Fri, Apr 09, 2021 at 12:33:10AM CDT, Andrew Jeffery wrote:
>>
>>
>>On Fri, 9 Apr 2021, at 14:45, Zev Weiss wrote:
>>>On Fri, Mar 19, 2021 at 01:27:48AM CDT, Andrew Jeffery wrote:
>>>>Given the deprecated binding, improve the ability to detect issues in
>>>>the platform devicetrees. Further, a subsequent patch will introduce a
>>>>new interrupts property for specifying SerIRQ behaviour, so convert
>>>>before we do any further additions.
>>>>
>>>>Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
>>>>---
>>>> .../bindings/ipmi/aspeed,ast2400-kcs-bmc.yaml | 92 +++++++++++++++++++
>>>> .../bindings/ipmi/aspeed-kcs-bmc.txt          | 33 -------
>>>> 2 files changed, 92 insertions(+), 33 deletions(-)
>>>> create mode 100644 Documentation/devicetree/bindings/ipmi/aspeed,ast2400-kcs-bmc.yaml
>>>> delete mode 100644 Documentation/devicetree/bindings/ipmi/aspeed-kcs-bmc.txt
>>>>
>>>>diff --git a/Documentation/devicetree/bindings/ipmi/aspeed,ast2400-kcs-bmc.yaml b/Documentation/devicetree/bindings/ipmi/aspeed,ast2400-kcs-bmc.yaml
>>>>new file mode 100644
>>>>index 000000000000..697ca575454f
>>>>--- /dev/null
>>>>+++ b/Documentation/devicetree/bindings/ipmi/aspeed,ast2400-kcs-bmc.yaml
>>>>@@ -0,0 +1,92 @@
>>>>+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
>>>>+%YAML 1.2
>>>>+---
>>>>+$id: http://devicetree.org/schemas/ipmi/aspeed,ast2400-kcs-bmc.yaml
>>>>+$schema: http://devicetree.org/meta-schemas/core.yaml
>>>>+
>>>>+title: ASPEED BMC KCS Devices
>>>>+
>>>>+maintainers:
>>>>+  - Andrew Jeffery <andrew@aj.id.au>
>>>>+
>>>>+description: |
>>>>+  The Aspeed BMC SoCs typically use the Keyboard-Controller-Style (KCS)
>>>>+  interfaces on the LPC bus for in-band IPMI communication with their host.
>>>>+
>>>>+properties:
>>>>+  compatible:
>>>>+    oneOf:
>>>>+      - description: Channel ID derived from reg
>>>>+        items:
>>>>+          enum:
>>>>+            - aspeed,ast2400-kcs-bmc-v2
>>>>+            - aspeed,ast2500-kcs-bmc-v2
>>>>+            - aspeed,ast2600-kcs-bmc
>>>
>>>Should this have a "-v2" suffix?
>>
>>Well, that was kind of a matter of perspective. The 2600 compatible was
>>added after we'd done the v2 of the binding for the 2400 and 2500 so it
>>never needed correcting. But it is a case of "don't use the deprecated
>>properties with the 2600 compatible".
>>
>>I don't think a change is necessary?
>>
>
>It just looked inconsistent with the corresponding string in the
>ast_kcs_bmc_match[] table; perhaps that should be changed instead then?
>

...except I realize now I only saw the 2600 v2 string in the match table
because I put it there myself in the process of resolving a conflict
when applying your series to the openbmc dev-5.10 branch for testing
purposes.  So nevermind on this.

Reviewed-by: Zev Weiss <zweiss@equinix.com>

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

* Re: [PATCH v2 09/21] ipmi: kcs_bmc: Split out kcs_bmc_cdev_ipmi
  2021-04-09  5:48     ` Andrew Jeffery
@ 2021-04-09 19:21       ` Zev Weiss
  0 siblings, 0 replies; 67+ messages in thread
From: Zev Weiss @ 2021-04-09 19:21 UTC (permalink / raw)
  To: Andrew Jeffery
  Cc: devicetree, Chia-Wei, Wang, Ryan Chen, Tomer Maimon,
	Corey Minyard, Avi Fishman, Patrick Venture, openbmc,
	linux-kernel, Tali Perry, linux-gpio, Rob Herring,
	linux-arm-kernel, openipmi-developer, Lee Jones, Linus Walleij,
	linux-aspeed, Benjamin Fair

On Fri, Apr 09, 2021 at 12:48:21AM CDT, Andrew Jeffery wrote:
>
>
>On Fri, 9 Apr 2021, at 13:26, Zev Weiss wrote:
>> On Fri, Mar 19, 2021 at 01:27:40AM CDT, Andrew Jeffery wrote:
>> >Take steps towards defining a coherent API to separate the KCS device
>> >drivers from the userspace interface. Decreasing the coupling will
>> >improve the separation of concerns and enable the introduction of
>> >alternative userspace interfaces.
>> >
>> >For now, simply split the chardev logic out to a separate file. The code
>> >continues to build into the same module.
>> >
>> >Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
>> >---
>> > drivers/char/ipmi/Makefile            |   2 +-
>> > drivers/char/ipmi/kcs_bmc.c           | 423 +------------------------
>> > drivers/char/ipmi/kcs_bmc.h           |  10 +-
>> > drivers/char/ipmi/kcs_bmc_cdev_ipmi.c | 428 ++++++++++++++++++++++++++
>> > 4 files changed, 451 insertions(+), 412 deletions(-)
>> > create mode 100644 drivers/char/ipmi/kcs_bmc_cdev_ipmi.c
>> >
>> >diff --git a/drivers/char/ipmi/Makefile b/drivers/char/ipmi/Makefile
>> >index 0822adc2ec41..a302bc865370 100644
>> >--- a/drivers/char/ipmi/Makefile
>> >+++ b/drivers/char/ipmi/Makefile
>> >@@ -22,7 +22,7 @@ obj-$(CONFIG_IPMI_SSIF) += ipmi_ssif.o
>> > obj-$(CONFIG_IPMI_POWERNV) += ipmi_powernv.o
>> > obj-$(CONFIG_IPMI_WATCHDOG) += ipmi_watchdog.o
>> > obj-$(CONFIG_IPMI_POWEROFF) += ipmi_poweroff.o
>> >-obj-$(CONFIG_IPMI_KCS_BMC) += kcs_bmc.o
>> >+obj-$(CONFIG_IPMI_KCS_BMC) += kcs_bmc.o kcs_bmc_cdev_ipmi.o
>> > obj-$(CONFIG_ASPEED_BT_IPMI_BMC) += bt-bmc.o
>> > obj-$(CONFIG_ASPEED_KCS_IPMI_BMC) += kcs_bmc_aspeed.o
>> > obj-$(CONFIG_NPCM7XX_KCS_IPMI_BMC) += kcs_bmc_npcm7xx.o
>> >diff --git a/drivers/char/ipmi/kcs_bmc.c b/drivers/char/ipmi/kcs_bmc.c
>> >index c4336c1f2d6d..ef5c48ffe74a 100644
>> >--- a/drivers/char/ipmi/kcs_bmc.c
>> >+++ b/drivers/char/ipmi/kcs_bmc.c
>> >@@ -3,446 +3,51 @@
>> >  * Copyright (c) 2015-2018, Intel Corporation.
>> >  */
>> >
>> >-#define pr_fmt(fmt) "kcs-bmc: " fmt
>> >-
>> >-#include <linux/errno.h>
>> >-#include <linux/io.h>
>> >-#include <linux/ipmi_bmc.h>
>> > #include <linux/module.h>
>> >-#include <linux/platform_device.h>
>> >-#include <linux/poll.h>
>> >-#include <linux/sched.h>
>> >-#include <linux/slab.h>
>> >
>> > #include "kcs_bmc.h"
>> >
>> >-#define DEVICE_NAME "ipmi-kcs"
>> >-
>> >-#define KCS_MSG_BUFSIZ    1000
>> >-
>> >-#define KCS_ZERO_DATA     0
>> >-
>> >-
>> >-/* IPMI 2.0 - Table 9-1, KCS Interface Status Register Bits */
>> >-#define KCS_STATUS_STATE(state) (state << 6)
>> >-#define KCS_STATUS_STATE_MASK   GENMASK(7, 6)
>> >-#define KCS_STATUS_CMD_DAT      BIT(3)
>> >-#define KCS_STATUS_SMS_ATN      BIT(2)
>> >-#define KCS_STATUS_IBF          BIT(1)
>> >-#define KCS_STATUS_OBF          BIT(0)
>> >-
>> >-/* IPMI 2.0 - Table 9-2, KCS Interface State Bits */
>> >-enum kcs_states {
>> >-	IDLE_STATE  = 0,
>> >-	READ_STATE  = 1,
>> >-	WRITE_STATE = 2,
>> >-	ERROR_STATE = 3,
>> >-};
>> >-
>> >-/* IPMI 2.0 - Table 9-3, KCS Interface Control Codes */
>> >-#define KCS_CMD_GET_STATUS_ABORT  0x60
>> >-#define KCS_CMD_WRITE_START       0x61
>> >-#define KCS_CMD_WRITE_END         0x62
>> >-#define KCS_CMD_READ_BYTE         0x68
>> >-
>> >-static inline u8 kcs_bmc_read_data(struct kcs_bmc *kcs_bmc)
>> >+u8 kcs_bmc_read_data(struct kcs_bmc *kcs_bmc)
>> > {
>> > 	return kcs_bmc->io_inputb(kcs_bmc, kcs_bmc->ioreg.idr);
>> > }
>> >+EXPORT_SYMBOL(kcs_bmc_read_data);
>> >
>> >-static inline void kcs_bmc_write_data(struct kcs_bmc *kcs_bmc, u8 data)
>> >+void kcs_bmc_write_data(struct kcs_bmc *kcs_bmc, u8 data)
>> > {
>> > 	kcs_bmc->io_outputb(kcs_bmc, kcs_bmc->ioreg.odr, data);
>> > }
>> >+EXPORT_SYMBOL(kcs_bmc_write_data);
>> >
>> >-static inline u8 kcs_bmc_read_status(struct kcs_bmc *kcs_bmc)
>> >+u8 kcs_bmc_read_status(struct kcs_bmc *kcs_bmc)
>> > {
>> > 	return kcs_bmc->io_inputb(kcs_bmc, kcs_bmc->ioreg.str);
>> > }
>> >+EXPORT_SYMBOL(kcs_bmc_read_status);
>> >
>> >-static inline void kcs_bmc_write_status(struct kcs_bmc *kcs_bmc, u8 data)
>> >+void kcs_bmc_write_status(struct kcs_bmc *kcs_bmc, u8 data)
>> > {
>> > 	kcs_bmc->io_outputb(kcs_bmc, kcs_bmc->ioreg.str, data);
>> > }
>> >+EXPORT_SYMBOL(kcs_bmc_write_status);
>> >
>> >-static void kcs_bmc_update_status(struct kcs_bmc *kcs_bmc, u8 mask, u8 val)
>> >+void kcs_bmc_update_status(struct kcs_bmc *kcs_bmc, u8 mask, u8 val)
>> > {
>> > 	kcs_bmc->io_updateb(kcs_bmc, kcs_bmc->ioreg.str, mask, val);
>> > }
>> >+EXPORT_SYMBOL(kcs_bmc_update_status);
>> >
>> >-static inline void set_state(struct kcs_bmc *kcs_bmc, u8 state)
>> >-{
>> >-	kcs_bmc_update_status(kcs_bmc, KCS_STATUS_STATE_MASK,
>> >-					KCS_STATUS_STATE(state));
>> >-}
>> >-
>> >-static void kcs_force_abort(struct kcs_bmc *kcs_bmc)
>> >-{
>> >-	set_state(kcs_bmc, ERROR_STATE);
>> >-	kcs_bmc_read_data(kcs_bmc);
>> >-	kcs_bmc_write_data(kcs_bmc, KCS_ZERO_DATA);
>> >-
>> >-	kcs_bmc->phase = KCS_PHASE_ERROR;
>> >-	kcs_bmc->data_in_avail = false;
>> >-	kcs_bmc->data_in_idx = 0;
>> >-}
>> >-
>> >-static void kcs_bmc_handle_data(struct kcs_bmc *kcs_bmc)
>> >-{
>> >-	u8 data;
>> >-
>> >-	switch (kcs_bmc->phase) {
>> >-	case KCS_PHASE_WRITE_START:
>> >-		kcs_bmc->phase = KCS_PHASE_WRITE_DATA;
>> >-		fallthrough;
>> >-
>> >-	case KCS_PHASE_WRITE_DATA:
>> >-		if (kcs_bmc->data_in_idx < KCS_MSG_BUFSIZ) {
>> >-			set_state(kcs_bmc, WRITE_STATE);
>> >-			kcs_bmc_write_data(kcs_bmc, KCS_ZERO_DATA);
>> >-			kcs_bmc->data_in[kcs_bmc->data_in_idx++] =
>> >-						kcs_bmc_read_data(kcs_bmc);
>> >-		} else {
>> >-			kcs_force_abort(kcs_bmc);
>> >-			kcs_bmc->error = KCS_LENGTH_ERROR;
>> >-		}
>> >-		break;
>> >-
>> >-	case KCS_PHASE_WRITE_END_CMD:
>> >-		if (kcs_bmc->data_in_idx < KCS_MSG_BUFSIZ) {
>> >-			set_state(kcs_bmc, READ_STATE);
>> >-			kcs_bmc->data_in[kcs_bmc->data_in_idx++] =
>> >-						kcs_bmc_read_data(kcs_bmc);
>> >-			kcs_bmc->phase = KCS_PHASE_WRITE_DONE;
>> >-			kcs_bmc->data_in_avail = true;
>> >-			wake_up_interruptible(&kcs_bmc->queue);
>> >-		} else {
>> >-			kcs_force_abort(kcs_bmc);
>> >-			kcs_bmc->error = KCS_LENGTH_ERROR;
>> >-		}
>> >-		break;
>> >-
>> >-	case KCS_PHASE_READ:
>> >-		if (kcs_bmc->data_out_idx == kcs_bmc->data_out_len)
>> >-			set_state(kcs_bmc, IDLE_STATE);
>> >-
>> >-		data = kcs_bmc_read_data(kcs_bmc);
>> >-		if (data != KCS_CMD_READ_BYTE) {
>> >-			set_state(kcs_bmc, ERROR_STATE);
>> >-			kcs_bmc_write_data(kcs_bmc, KCS_ZERO_DATA);
>> >-			break;
>> >-		}
>> >-
>> >-		if (kcs_bmc->data_out_idx == kcs_bmc->data_out_len) {
>> >-			kcs_bmc_write_data(kcs_bmc, KCS_ZERO_DATA);
>> >-			kcs_bmc->phase = KCS_PHASE_IDLE;
>> >-			break;
>> >-		}
>> >-
>> >-		kcs_bmc_write_data(kcs_bmc,
>> >-			kcs_bmc->data_out[kcs_bmc->data_out_idx++]);
>> >-		break;
>> >-
>> >-	case KCS_PHASE_ABORT_ERROR1:
>> >-		set_state(kcs_bmc, READ_STATE);
>> >-		kcs_bmc_read_data(kcs_bmc);
>> >-		kcs_bmc_write_data(kcs_bmc, kcs_bmc->error);
>> >-		kcs_bmc->phase = KCS_PHASE_ABORT_ERROR2;
>> >-		break;
>> >-
>> >-	case KCS_PHASE_ABORT_ERROR2:
>> >-		set_state(kcs_bmc, IDLE_STATE);
>> >-		kcs_bmc_read_data(kcs_bmc);
>> >-		kcs_bmc_write_data(kcs_bmc, KCS_ZERO_DATA);
>> >-		kcs_bmc->phase = KCS_PHASE_IDLE;
>> >-		break;
>> >-
>> >-	default:
>> >-		kcs_force_abort(kcs_bmc);
>> >-		break;
>> >-	}
>> >-}
>> >-
>> >-static void kcs_bmc_handle_cmd(struct kcs_bmc *kcs_bmc)
>> >-{
>> >-	u8 cmd;
>> >-
>> >-	set_state(kcs_bmc, WRITE_STATE);
>> >-	kcs_bmc_write_data(kcs_bmc, KCS_ZERO_DATA);
>> >-
>> >-	cmd = kcs_bmc_read_data(kcs_bmc);
>> >-	switch (cmd) {
>> >-	case KCS_CMD_WRITE_START:
>> >-		kcs_bmc->phase = KCS_PHASE_WRITE_START;
>> >-		kcs_bmc->error = KCS_NO_ERROR;
>> >-		kcs_bmc->data_in_avail = false;
>> >-		kcs_bmc->data_in_idx = 0;
>> >-		break;
>> >-
>> >-	case KCS_CMD_WRITE_END:
>> >-		if (kcs_bmc->phase != KCS_PHASE_WRITE_DATA) {
>> >-			kcs_force_abort(kcs_bmc);
>> >-			break;
>> >-		}
>> >-
>> >-		kcs_bmc->phase = KCS_PHASE_WRITE_END_CMD;
>> >-		break;
>> >-
>> >-	case KCS_CMD_GET_STATUS_ABORT:
>> >-		if (kcs_bmc->error == KCS_NO_ERROR)
>> >-			kcs_bmc->error = KCS_ABORTED_BY_COMMAND;
>> >-
>> >-		kcs_bmc->phase = KCS_PHASE_ABORT_ERROR1;
>> >-		kcs_bmc->data_in_avail = false;
>> >-		kcs_bmc->data_in_idx = 0;
>> >-		break;
>> >-
>> >-	default:
>> >-		kcs_force_abort(kcs_bmc);
>> >-		kcs_bmc->error = KCS_ILLEGAL_CONTROL_CODE;
>> >-		break;
>> >-	}
>> >-}
>> >-
>> >+int kcs_bmc_ipmi_event(struct kcs_bmc *kcs_bmc);
>>
>> This declaration looks a bit out of place here; should it be in
>> kcs_bmc.h instead?
>
>These are only temporary and get removed later on in the series after
>some shuffling of the code.
>

Okay -- there were a couple others further down in the patch that I'm
pretty sure were strictly redundant and could perhaps be cleaned up
(kcs_bmc_ipmi_event and kcs_bmc_ipmi_alloc in kcs_bmc_cdev_ipmi.c), but
aside from that:

Reviewed-by: Zev Weiss <zweiss@equinix.com>

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

* Re: [PATCH v2 10/21] ipmi: kcs_bmc: Turn the driver data-structures inside-out
  2021-04-09  6:25       ` Zev Weiss
@ 2021-04-09 19:26         ` Zev Weiss
  2021-04-11 23:00           ` Andrew Jeffery
  0 siblings, 1 reply; 67+ messages in thread
From: Zev Weiss @ 2021-04-09 19:26 UTC (permalink / raw)
  To: Andrew Jeffery
  Cc: devicetree, Chia-Wei, Wang, Ryan Chen, Tomer Maimon,
	Corey Minyard, Avi Fishman, Patrick Venture, openbmc,
	linux-kernel, Tali Perry, linux-gpio, Rob Herring,
	linux-arm-kernel, openipmi-developer, Lee Jones, Linus Walleij,
	linux-aspeed, Benjamin Fair

On Fri, Apr 09, 2021 at 01:25:26AM CDT, Zev Weiss wrote:
>On Fri, Apr 09, 2021 at 12:59:09AM CDT, Andrew Jeffery wrote:
>>
>>
>>On Fri, 9 Apr 2021, at 13:27, Zev Weiss wrote:
>>>On Fri, Mar 19, 2021 at 01:27:41AM CDT, Andrew Jeffery wrote:
>>>>Make the KCS device drivers responsible for allocating their own memory.
>>>>
>>>>Until now the private data for the device driver was allocated internal
>>>>to the private data for the chardev interface. This coupling required
>>>>the slightly awkward API of passing through the struct size for the
>>>>driver private data to the chardev constructor, and then retrieving a
>>>>pointer to the driver private data from the allocated chardev memory.
>>>>
>>>>In addition to being awkward, the arrangement prevents the
>>>>implementation of alternative userspace interfaces as the device driver
>>>>private data is not independent.
>>>>
>>>>Peel a layer off the onion and turn the data-structures inside out by
>>>>exploiting container_of() and embedding `struct kcs_device` in the
>>>>driver private data.
>>>>
>>>>Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
>>>>---
>>>> drivers/char/ipmi/kcs_bmc.c           | 15 +++++--
>>>> drivers/char/ipmi/kcs_bmc.h           | 12 ++----
>>>> drivers/char/ipmi/kcs_bmc_aspeed.c    | 60 ++++++++++++++++-----------
>>>> drivers/char/ipmi/kcs_bmc_cdev_ipmi.c | 60 ++++++++++++++++++---------
>>>> drivers/char/ipmi/kcs_bmc_npcm7xx.c   | 37 ++++++++++-------
>>>> 5 files changed, 113 insertions(+), 71 deletions(-)
>>>>
>>>>diff --git a/drivers/char/ipmi/kcs_bmc.c b/drivers/char/ipmi/kcs_bmc.c
>>>>index ef5c48ffe74a..709b6bdec165 100644
>>>>--- a/drivers/char/ipmi/kcs_bmc.c
>>>>+++ b/drivers/char/ipmi/kcs_bmc.c
>>>>@@ -44,12 +44,19 @@ int kcs_bmc_handle_event(struct kcs_bmc *kcs_bmc)
>>>> }
>>>> EXPORT_SYMBOL(kcs_bmc_handle_event);
>>>>
>>>>-struct kcs_bmc *kcs_bmc_ipmi_alloc(struct device *dev, int sizeof_priv, u32 channel);
>>>>-struct kcs_bmc *kcs_bmc_alloc(struct device *dev, int sizeof_priv, u32 channel)
>>>>+int kcs_bmc_ipmi_attach_cdev(struct kcs_bmc *kcs_bmc);
>>>
>>>Another declaration perhaps intended for kcs_bmc.h?
>>
>>These are temporary while the code gets shuffled around. The symbol
>>name is an implementation detail, not a "public" part of the API; after
>>some further shuffling these are eventually assigned as callbacks in an
>>ops struct.
>>
>
>Okay, that makes sense.
>
>>>
>>>>+int kcs_bmc_add_device(struct kcs_bmc *kcs_bmc)
>>>> {
>>>>-	return kcs_bmc_ipmi_alloc(dev, sizeof_priv, channel);
>>>>+	return kcs_bmc_ipmi_attach_cdev(kcs_bmc);
>>>> }
>>>>-EXPORT_SYMBOL(kcs_bmc_alloc);
>>>>+EXPORT_SYMBOL(kcs_bmc_add_device);
>>>>+
>>>>+int kcs_bmc_ipmi_detach_cdev(struct kcs_bmc *kcs_bmc);
>>>
>>>Here too.
>>>
>>>>+int kcs_bmc_remove_device(struct kcs_bmc *kcs_bmc)
>>>>+{
>>>>+	return kcs_bmc_ipmi_detach_cdev(kcs_bmc);
>>>>+}
>>>>+EXPORT_SYMBOL(kcs_bmc_remove_device);
>>>>
>>>> MODULE_LICENSE("GPL v2");
>>>> MODULE_AUTHOR("Haiyue Wang <haiyue.wang@linux.intel.com>");
>>>>diff --git a/drivers/char/ipmi/kcs_bmc.h b/drivers/char/ipmi/kcs_bmc.h
>>>>index febea0c8deb4..bf0ae327997f 100644
>>>>--- a/drivers/char/ipmi/kcs_bmc.h
>>>>+++ b/drivers/char/ipmi/kcs_bmc.h
>>>>@@ -67,6 +67,8 @@ struct kcs_ioreg {
>>>> };
>>>>
>>>> struct kcs_bmc {
>>>>+	struct device *dev;
>>>>+
>>>> 	spinlock_t lock;
>>>>
>>>> 	u32 channel;
>>>>@@ -94,17 +96,11 @@ struct kcs_bmc {
>>>> 	u8 *kbuffer;
>>>>
>>>> 	struct miscdevice miscdev;
>>>>-
>>>>-	unsigned long priv[];
>>>> };
>>>>
>>>>-static inline void *kcs_bmc_priv(struct kcs_bmc *kcs_bmc)
>>>>-{
>>>>-	return kcs_bmc->priv;
>>>>-}
>>>>-
>>>> int kcs_bmc_handle_event(struct kcs_bmc *kcs_bmc);
>>>>-struct kcs_bmc *kcs_bmc_alloc(struct device *dev, int sizeof_priv, u32 channel);
>>>>+int kcs_bmc_add_device(struct kcs_bmc *kcs_bmc);
>>>>+int kcs_bmc_remove_device(struct kcs_bmc *kcs_bmc);
>>>>
>>>> u8 kcs_bmc_read_data(struct kcs_bmc *kcs_bmc);
>>>> void kcs_bmc_write_data(struct kcs_bmc *kcs_bmc, u8 data);
>>>>diff --git a/drivers/char/ipmi/kcs_bmc_aspeed.c b/drivers/char/ipmi/kcs_bmc_aspeed.c
>>>>index 630cf095560e..0416ac78ce68 100644
>>>>--- a/drivers/char/ipmi/kcs_bmc_aspeed.c
>>>>+++ b/drivers/char/ipmi/kcs_bmc_aspeed.c
>>>>@@ -61,6 +61,8 @@
>>>> #define LPC_STR4             0x11C
>>>>
>>>> struct aspeed_kcs_bmc {
>>>>+	struct kcs_bmc kcs_bmc;
>>>>+
>>>> 	struct regmap *map;
>>>> };
>>>>
>>>>@@ -69,9 +71,14 @@ struct aspeed_kcs_of_ops {
>>>> 	int (*get_io_address)(struct platform_device *pdev);
>>>> };
>>>>
>>>>+static inline struct aspeed_kcs_bmc *to_aspeed_kcs_bmc(struct kcs_bmc *kcs_bmc)
>>>>+{
>>>>+	return container_of(kcs_bmc, struct aspeed_kcs_bmc, kcs_bmc);
>>>>+}
>>>>+
>>>> static u8 aspeed_kcs_inb(struct kcs_bmc *kcs_bmc, u32 reg)
>>>> {
>>>>-	struct aspeed_kcs_bmc *priv = kcs_bmc_priv(kcs_bmc);
>>>>+	struct aspeed_kcs_bmc *priv = to_aspeed_kcs_bmc(kcs_bmc);
>>>> 	u32 val = 0;
>>>> 	int rc;
>>>>
>>>>@@ -83,7 +90,7 @@ static u8 aspeed_kcs_inb(struct kcs_bmc *kcs_bmc, u32 reg)
>>>>
>>>> static void aspeed_kcs_outb(struct kcs_bmc *kcs_bmc, u32 reg, u8 data)
>>>> {
>>>>-	struct aspeed_kcs_bmc *priv = kcs_bmc_priv(kcs_bmc);
>>>>+	struct aspeed_kcs_bmc *priv = to_aspeed_kcs_bmc(kcs_bmc);
>>>> 	int rc;
>>>>
>>>> 	rc = regmap_write(priv->map, reg, data);
>>>>@@ -92,7 +99,7 @@ static void aspeed_kcs_outb(struct kcs_bmc *kcs_bmc, u32 reg, u8 data)
>>>>
>>>> static void aspeed_kcs_updateb(struct kcs_bmc *kcs_bmc, u32 reg, u8 mask, u8 val)
>>>> {
>>>>-	struct aspeed_kcs_bmc *priv = kcs_bmc_priv(kcs_bmc);
>>>>+	struct aspeed_kcs_bmc *priv = to_aspeed_kcs_bmc(kcs_bmc);
>>>> 	int rc;
>>>>
>>>> 	rc = regmap_update_bits(priv->map, reg, mask, val);
>>>>@@ -114,7 +121,7 @@ static void aspeed_kcs_updateb(struct kcs_bmc *kcs_bmc, u32 reg, u8 mask, u8 val
>>>>  */
>>>> static void aspeed_kcs_set_address(struct kcs_bmc *kcs_bmc, u16 addr)
>>>> {
>>>>-	struct aspeed_kcs_bmc *priv = kcs_bmc_priv(kcs_bmc);
>>>>+	struct aspeed_kcs_bmc *priv = to_aspeed_kcs_bmc(kcs_bmc);
>>>>
>>>> 	switch (kcs_bmc->channel) {
>>>> 	case 1:
>>>>@@ -148,7 +155,7 @@ static void aspeed_kcs_set_address(struct kcs_bmc *kcs_bmc, u16 addr)
>>>>
>>>> static void aspeed_kcs_enable_channel(struct kcs_bmc *kcs_bmc, bool enable)
>>>> {
>>>>-	struct aspeed_kcs_bmc *priv = kcs_bmc_priv(kcs_bmc);
>>>>+	struct aspeed_kcs_bmc *priv = to_aspeed_kcs_bmc(kcs_bmc);
>>>>
>>>> 	switch (kcs_bmc->channel) {
>>>> 	case 1:
>>>>@@ -323,16 +330,16 @@ static int aspeed_kcs_of_v2_get_io_address(struct platform_device *pdev)
>>>> static int aspeed_kcs_probe(struct platform_device *pdev)
>>>> {
>>>> 	const struct aspeed_kcs_of_ops *ops;
>>>>-	struct device *dev = &pdev->dev;
>>>>+	struct aspeed_kcs_bmc *priv;
>>>> 	struct kcs_bmc *kcs_bmc;
>>>> 	struct device_node *np;
>>>> 	int rc, channel, addr;
>>>>
>>>>-	np = dev->of_node->parent;
>>>>+	np = pdev->dev.of_node->parent;
>>>> 	if (!of_device_is_compatible(np, "aspeed,ast2400-lpc-v2") &&
>>>> 	    !of_device_is_compatible(np, "aspeed,ast2500-lpc-v2") &&
>>>> 	    !of_device_is_compatible(np, "aspeed,ast2600-lpc-v2")) {
>>>>-		dev_err(dev, "unsupported LPC device binding\n");
>>>>+		dev_err(&pdev->dev, "unsupported LPC device binding\n");
>>>> 		return -ENODEV;
>>>> 	}
>>>> 	ops = of_device_get_match_data(&pdev->dev);
>>>>@@ -343,18 +350,27 @@ static int aspeed_kcs_probe(struct platform_device *pdev)
>>>> 	if (channel < 0)
>>>> 		return channel;
>>>>
>>>>-	kcs_bmc = kcs_bmc_alloc(&pdev->dev, sizeof(struct aspeed_kcs_bmc), channel);
>>>>-	if (!kcs_bmc)
>>>>+	addr = ops->get_io_address(pdev);
>>>>+	if (addr < 0)
>>>>+		return addr;
>>>>+
>>>>+	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
>>>>+	if (!priv)
>>>> 		return -ENOMEM;
>>>>
>>>>+	kcs_bmc = &priv->kcs_bmc;
>>>>+	kcs_bmc->dev = &pdev->dev;
>>>>+	kcs_bmc->channel = channel;
>>>> 	kcs_bmc->ioreg = ast_kcs_bmc_ioregs[channel - 1];
>>>> 	kcs_bmc->io_inputb = aspeed_kcs_inb;
>>>> 	kcs_bmc->io_outputb = aspeed_kcs_outb;
>>>> 	kcs_bmc->io_updateb = aspeed_kcs_updateb;
>>>>
>>>>-	addr = ops->get_io_address(pdev);
>>>>-	if (addr < 0)
>>>>-		return addr;
>>>>+	priv->map = syscon_node_to_regmap(pdev->dev.parent->of_node);
>>>>+	if (IS_ERR(priv->map)) {
>>>>+		dev_err(&pdev->dev, "Couldn't get regmap\n");
>>>>+		return -ENODEV;
>>>>+	}
>>>
>>>The reanimated priv->map initialization I suspect wasn't meant to
>>>have been removed in the first place...
>>
>>Yeah, I'll have to go back and figure out what went wrong there!
>>
>>Thanks for catching that.
>>
>>>
>>>>
>>>> 	aspeed_kcs_set_address(kcs_bmc, addr);
>>>>
>>>>@@ -362,29 +378,25 @@ static int aspeed_kcs_probe(struct platform_device *pdev)
>>>> 	if (rc)
>>>> 		return rc;
>>>>
>>>>-	dev_set_drvdata(dev, kcs_bmc);
>>>>+	platform_set_drvdata(pdev, priv);
>>>>
>>>> 	aspeed_kcs_enable_channel(kcs_bmc, true);
>>>>
>>>>-	rc = misc_register(&kcs_bmc->miscdev);
>>>>-	if (rc) {
>>>>-		dev_err(dev, "Unable to register device\n");
>>>>+	rc = kcs_bmc_add_device(&priv->kcs_bmc);
>>>>+	if (rc < 0)
>>>> 		return rc;
>>>>-	}
>>>>
>>>>-	dev_dbg(&pdev->dev,
>>>>-		"Probed KCS device %d (IDR=0x%x, ODR=0x%x, STR=0x%x)\n",
>>>>-		kcs_bmc->channel, kcs_bmc->ioreg.idr, kcs_bmc->ioreg.odr,
>>>>-		kcs_bmc->ioreg.str);
>>>>+	dev_info(&pdev->dev, "Initialised channel %d at 0x%x\n", kcs_bmc->channel, addr);
>>>
>>>Is the dbg->info change here intentional?  (I have no particular
>>>objection if so, but it's often a change I make myself during
>>>testing/debugging and then forget to revert...)
>>
>>Yeah, it was possibly something I forgot to revert. If others have
>>issues with it staying at dev_info() I'll switch it back.
>>
>>>
>>>>
>>>> 	return 0;
>>>> }
>>>>
>>>> static int aspeed_kcs_remove(struct platform_device *pdev)
>>>> {
>>>>-	struct kcs_bmc *kcs_bmc = dev_get_drvdata(&pdev->dev);
>>>>+	struct aspeed_kcs_bmc *priv = platform_get_drvdata(pdev);
>>>>+	struct kcs_bmc *kcs_bmc = &priv->kcs_bmc;
>>>>
>>>>-	misc_deregister(&kcs_bmc->miscdev);
>>>>+	kcs_bmc_remove_device(kcs_bmc);
>>>
>>>Should we propagate the return value outward here?
>>
>>Probably!
>>
>>>
>>>>
>>>> 	return 0;
>>>> }
>>>>diff --git a/drivers/char/ipmi/kcs_bmc_cdev_ipmi.c b/drivers/char/ipmi/kcs_bmc_cdev_ipmi.c
>>>>index 82c77994e481..0ca71c135a1a 100644
>>>>--- a/drivers/char/ipmi/kcs_bmc_cdev_ipmi.c
>>>>+++ b/drivers/char/ipmi/kcs_bmc_cdev_ipmi.c
>>>>@@ -382,7 +382,7 @@ static int kcs_bmc_ipmi_release(struct inode *inode, struct file *filp)
>>>> 	return 0;
>>>> }
>>>>
>>>>-static const struct file_operations kcs_bmc_fops = {
>>>>+static const struct file_operations kcs_bmc_ipmi_fops = {
>>>> 	.owner          = THIS_MODULE,
>>>> 	.open           = kcs_bmc_ipmi_open,
>>>> 	.read           = kcs_bmc_ipmi_read,
>>>>@@ -392,36 +392,58 @@ static const struct file_operations kcs_bmc_fops = {
>>>> 	.unlocked_ioctl = kcs_bmc_ipmi_ioctl,
>>>> };
>>>>
>>>>-struct kcs_bmc *kcs_bmc_ipmi_alloc(struct device *dev, int sizeof_priv, u32 channel);
>>>>-struct kcs_bmc *kcs_bmc_ipmi_alloc(struct device *dev, int sizeof_priv, u32 channel)
>>>>+int kcs_bmc_ipmi_attach_cdev(struct kcs_bmc *kcs_bmc);
>>>
>>>Errant declaration again?
>>
>>As previously explained.
>>
>
>This one seems like a slightly different category, because...
>
>>>
>>>>+int kcs_bmc_ipmi_attach_cdev(struct kcs_bmc *kcs_bmc)
>
>...it's immediately followed by the definition of the very same function
>that it just declared, so I can't see how its presence or absence could
>make any functional difference to anything.  (So perhaps I should have
>said "redundant" instead of "errant...again".)
>
>It's fairly trivial of course given that it's gone by the end of the
>series, but as long as there's going to be another iteration anyway it
>seems like we might as well tidy it up?
>

Oh, and otherwise:

Reviewed-by: Zev Weiss <zweiss@equinix.com>

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

* Re: [PATCH v2 10/21] ipmi: kcs_bmc: Turn the driver data-structures inside-out
  2021-04-09 19:26         ` Zev Weiss
@ 2021-04-11 23:00           ` Andrew Jeffery
  0 siblings, 0 replies; 67+ messages in thread
From: Andrew Jeffery @ 2021-04-11 23:00 UTC (permalink / raw)
  To: Zev Weiss
  Cc: devicetree, Chia-Wei, Wang, Ryan Chen, Tomer Maimon,
	Corey Minyard, Avi Fishman, Patrick Venture, openbmc,
	linux-kernel, Tali Perry, linux-gpio, Rob Herring,
	linux-arm-kernel, openipmi-developer, Lee Jones, Linus Walleij,
	linux-aspeed, Benjamin Fair



On Sat, 10 Apr 2021, at 04:56, Zev Weiss wrote:
> On Fri, Apr 09, 2021 at 01:25:26AM CDT, Zev Weiss wrote:
> >On Fri, Apr 09, 2021 at 12:59:09AM CDT, Andrew Jeffery wrote:
> >>On Fri, 9 Apr 2021, at 13:27, Zev Weiss wrote:
> >>>On Fri, Mar 19, 2021 at 01:27:41AM CDT, Andrew Jeffery wrote:
> >>>>-struct kcs_bmc *kcs_bmc_ipmi_alloc(struct device *dev, int sizeof_priv, u32 channel);
> >>>>-struct kcs_bmc *kcs_bmc_ipmi_alloc(struct device *dev, int sizeof_priv, u32 channel)
> >>>>+int kcs_bmc_ipmi_attach_cdev(struct kcs_bmc *kcs_bmc);
> >>>
> >>>Errant declaration again?
> >>
> >>As previously explained.
> >>
> >
> >This one seems like a slightly different category, because...
> >
> >>>
> >>>>+int kcs_bmc_ipmi_attach_cdev(struct kcs_bmc *kcs_bmc)
> >
> >...it's immediately followed by the definition of the very same function
> >that it just declared, so I can't see how its presence or absence could
> >make any functional difference to anything.  (So perhaps I should have
> >said "redundant" instead of "errant...again".)

This is is a small hack to fend off warnings from -Wmissing-declarations.

> >
> >It's fairly trivial of course given that it's gone by the end of the
> >series, but as long as there's going to be another iteration anyway it
> >seems like we might as well tidy it up?
> >
> 
> Oh, and otherwise:
> 
> Reviewed-by: Zev Weiss <zweiss@equinix.com>

Thanks.

Andrew

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

* Re: [PATCH v2 16/21] ipmi: kcs_bmc: Add a "raw" character device interface
  2021-04-09  7:55   ` Arnd Bergmann
@ 2021-04-12  1:33     ` Andrew Jeffery
  2021-04-12  8:48       ` Arnd Bergmann
  0 siblings, 1 reply; 67+ messages in thread
From: Andrew Jeffery @ 2021-04-12  1:33 UTC (permalink / raw)
  To: Arnd Bergmann, Dmitry Torokhov
  Cc: Linux ARM, DTML, Chia-Wei, Wang, Ryan Chen, Tomer Maimon,
	Corey Minyard, Avi Fishman, Patrick Venture, OpenBMC Maillist,
	Linux Kernel Mailing List, Tali Perry, open list:GPIO SUBSYSTEM,
	Rob Herring, openipmi-developer, Lee Jones, Linus Walleij,
	linux-aspeed, Benjamin Fair

On Fri, 9 Apr 2021, at 17:25, Arnd Bergmann wrote:
> On Fri, Mar 19, 2021 at 7:31 AM Andrew Jeffery <andrew@aj.id.au> wrote:
> >
> > The existing IPMI chardev encodes IPMI behaviours as the name suggests.
> > However, KCS devices are useful beyond IPMI (or keyboards), as they
> > provide a means to generate IRQs and exchange arbitrary data between a
> > BMC and its host system.
> 
> I only noticed the series after Joel asked about the DT changes on the arm
> side. One question though:
> 
> How does this related to the drivers/input/serio/ framework that also talks
> to the keyboard controller for things that are not keyboards?

I've taken a brief look and I feel they're somewhat closely related.

It's plausible that we could wrangle the code so the Aspeed and Nuvoton 
KCS drivers move under drivers/input/serio. If you squint, the i8042 
serio device driver has similarities with what the Aspeed and Nuvoton 
device drivers are providing to the KCS IPMI stack.

Both the KCS IPMI and raw chardev I've implemented in this patch need 
both read and write access to the status register (STR). serio could 
potentially expose its value through serio_interrupt() using the 
SERIO_OOB_DATA flag, but I haven't put any thought into it beyond this 
sentence. We'd need some extra support for writing STR via the serio 
API. I'm not sure that fits into the abstraction (unless we make 
serio_write() take a flags argument?).

In that vein, the serio_raw interface is close to the functionality 
that the raw chardev provides in this patch, though again serio_raw 
lacks userspace access to STR. Flags are ignored in the ->interrupt() 
callback so all values received via ->interrupt() are exposed as data. 
The result is there's no way to take care of SERIO_OOB_DATA in the 
read() path. Given that, I think we'd have to expose an ioctl() to 
access the STR value after taking care of SERIO_OOB_DATA in 
->interrupt().

I'm not sure where that lands us.

Dmitry, any thoughts here?

> Are these
> separate communication channels on adjacent I/O ports, or does there
> need to be some arbitration?

As it stands there's no arbitration.

Cheers,

Andrew

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

* Re: [PATCH v2 16/21] ipmi: kcs_bmc: Add a "raw" character device interface
  2021-04-12  1:33     ` Andrew Jeffery
@ 2021-04-12  8:48       ` Arnd Bergmann
  2021-04-12 23:45         ` Andrew Jeffery
  0 siblings, 1 reply; 67+ messages in thread
From: Arnd Bergmann @ 2021-04-12  8:48 UTC (permalink / raw)
  To: Andrew Jeffery
  Cc: Linux ARM, DTML, Chia-Wei, Wang, Ryan Chen, Tomer Maimon,
	Corey Minyard, Avi Fishman, Patrick Venture, OpenBMC Maillist,
	Dmitry Torokhov, Linux Kernel Mailing List, Tali Perry,
	open list:GPIO SUBSYSTEM, Rob Herring, openipmi-developer,
	Lee Jones, Linus Walleij, linux-aspeed, Benjamin Fair

On Mon, Apr 12, 2021 at 3:33 AM Andrew Jeffery <andrew@aj.id.au> wrote:
> On Fri, 9 Apr 2021, at 17:25, Arnd Bergmann wrote:
> > On Fri, Mar 19, 2021 at 7:31 AM Andrew Jeffery <andrew@aj.id.au> wrote:
> > >
> > > The existing IPMI chardev encodes IPMI behaviours as the name suggests.
> > > However, KCS devices are useful beyond IPMI (or keyboards), as they
> > > provide a means to generate IRQs and exchange arbitrary data between a
> > > BMC and its host system.
> >
> > I only noticed the series after Joel asked about the DT changes on the arm
> > side. One question though:
> >
> > How does this related to the drivers/input/serio/ framework that also talks
> > to the keyboard controller for things that are not keyboards?
>
> I've taken a brief look and I feel they're somewhat closely related.
>
> It's plausible that we could wrangle the code so the Aspeed and Nuvoton
> KCS drivers move under drivers/input/serio. If you squint, the i8042
> serio device driver has similarities with what the Aspeed and Nuvoton
> device drivers are providing to the KCS IPMI stack.

After looking some more into it, I finally understood that the two are
rather complementary. While the  drivers/char/ipmi/kcs_bmc.c
is the other (bmc) end of drivers/char/ipmi/ipmi_kcs_sm.c, it seems
that the proposed kcs_bmc_cdev_raw.c interface would be
what corresponds to the other side of
drivers/input/serio/i8042.c+userio.c. Then again, these are also on
separate ports (0x60 for the keyboard controller, 0xca2 for the BMC
KCS), so they would never actually talk to one another.

> Both the KCS IPMI and raw chardev I've implemented in this patch need
> both read and write access to the status register (STR). serio could
> potentially expose its value through serio_interrupt() using the
> SERIO_OOB_DATA flag, but I haven't put any thought into it beyond this
> sentence. We'd need some extra support for writing STR via the serio
> API. I'm not sure that fits into the abstraction (unless we make
> serio_write() take a flags argument?).
>
> In that vein, the serio_raw interface is close to the functionality
> that the raw chardev provides in this patch, though again serio_raw
> lacks userspace access to STR. Flags are ignored in the ->interrupt()
> callback so all values received via ->interrupt() are exposed as data.
> The result is there's no way to take care of SERIO_OOB_DATA in the
> read() path. Given that, I think we'd have to expose an ioctl() to
> access the STR value after taking care of SERIO_OOB_DATA in
> ->interrupt().
>
> I'm not sure where that lands us.

Based on what I looked up, I think you can just forget about my original
question. We have two separate interfaces that use an Intel 8042-style
protocol, but they don't really interact.

      Arnd

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

* Re: [PATCH v2 16/21] ipmi: kcs_bmc: Add a "raw" character device interface
  2021-04-12  8:48       ` Arnd Bergmann
@ 2021-04-12 23:45         ` Andrew Jeffery
  2021-04-13  8:22           ` Arnd Bergmann
  0 siblings, 1 reply; 67+ messages in thread
From: Andrew Jeffery @ 2021-04-12 23:45 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: Linux ARM, DTML, Chia-Wei, Wang, Ryan Chen, Tomer Maimon,
	Corey Minyard, Avi Fishman, Patrick Venture, OpenBMC Maillist,
	Dmitry Torokhov, Linux Kernel Mailing List, Tali Perry,
	open list:GPIO SUBSYSTEM, Rob Herring, openipmi-developer,
	Lee Jones, Linus Walleij, linux-aspeed, Benjamin Fair



On Mon, 12 Apr 2021, at 18:18, Arnd Bergmann wrote:
> On Mon, Apr 12, 2021 at 3:33 AM Andrew Jeffery <andrew@aj.id.au> wrote:
> > On Fri, 9 Apr 2021, at 17:25, Arnd Bergmann wrote:
> > > On Fri, Mar 19, 2021 at 7:31 AM Andrew Jeffery <andrew@aj.id.au> wrote:
> > > >
> > > > The existing IPMI chardev encodes IPMI behaviours as the name suggests.
> > > > However, KCS devices are useful beyond IPMI (or keyboards), as they
> > > > provide a means to generate IRQs and exchange arbitrary data between a
> > > > BMC and its host system.
> > >
> > > I only noticed the series after Joel asked about the DT changes on the arm
> > > side. One question though:
> > >
> > > How does this related to the drivers/input/serio/ framework that also talks
> > > to the keyboard controller for things that are not keyboards?
> >
> > I've taken a brief look and I feel they're somewhat closely related.
> >
> > It's plausible that we could wrangle the code so the Aspeed and Nuvoton
> > KCS drivers move under drivers/input/serio. If you squint, the i8042
> > serio device driver has similarities with what the Aspeed and Nuvoton
> > device drivers are providing to the KCS IPMI stack.
> 
> After looking some more into it, I finally understood that the two are
> rather complementary. While the  drivers/char/ipmi/kcs_bmc.c
> is the other (bmc) end of drivers/char/ipmi/ipmi_kcs_sm.c, it seems
> that the proposed kcs_bmc_cdev_raw.c interface would be
> what corresponds to the other side of
> drivers/input/serio/i8042.c+userio.c.

Right. I guess the question is should we be splitting kernel subsystems 
along host/bmc lines? Doesn't feel intuitive, it's all Linux, but maybe 
we can consolidate in the future if it makes sense?

> Then again, these are also on
> separate ports (0x60 for the keyboard controller, 0xca2 for the BMC
> KCS), so they would never actually talk to one another.

Well, sort of I guess. On Power systems we don't use the keyboard 
controller for IPMI or keyboards, so we're just kinda exploiting the 
hardware for our own purposes.

> 
> > Both the KCS IPMI and raw chardev I've implemented in this patch need
> > both read and write access to the status register (STR). serio could
> > potentially expose its value through serio_interrupt() using the
> > SERIO_OOB_DATA flag, but I haven't put any thought into it beyond this
> > sentence. We'd need some extra support for writing STR via the serio
> > API. I'm not sure that fits into the abstraction (unless we make
> > serio_write() take a flags argument?).
> >
> > In that vein, the serio_raw interface is close to the functionality
> > that the raw chardev provides in this patch, though again serio_raw
> > lacks userspace access to STR. Flags are ignored in the ->interrupt()
> > callback so all values received via ->interrupt() are exposed as data.
> > The result is there's no way to take care of SERIO_OOB_DATA in the
> > read() path. Given that, I think we'd have to expose an ioctl() to
> > access the STR value after taking care of SERIO_OOB_DATA in
> > ->interrupt().
> >
> > I'm not sure where that lands us.
> 
> Based on what I looked up, I think you can just forget about my original
> question. We have two separate interfaces that use an Intel 8042-style
> protocol, but they don't really interact.

Right, this is still true given Power doesn't care for keyboards or 
IPMI via the keyboard controllers; the two still don't interact.

Andrew

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

* Re: [PATCH v2 16/21] ipmi: kcs_bmc: Add a "raw" character device interface
  2021-04-12 23:45         ` Andrew Jeffery
@ 2021-04-13  8:22           ` Arnd Bergmann
  2021-04-14  0:30             ` Andrew Jeffery
  0 siblings, 1 reply; 67+ messages in thread
From: Arnd Bergmann @ 2021-04-13  8:22 UTC (permalink / raw)
  To: Andrew Jeffery
  Cc: Linux ARM, DTML, Chia-Wei, Wang, Ryan Chen, Tomer Maimon,
	Corey Minyard, Avi Fishman, Patrick Venture, OpenBMC Maillist,
	Dmitry Torokhov, Linux Kernel Mailing List, Tali Perry,
	open list:GPIO SUBSYSTEM, Rob Herring, openipmi-developer,
	Lee Jones, Linus Walleij, linux-aspeed, Benjamin Fair

On Tue, Apr 13, 2021 at 1:45 AM Andrew Jeffery <andrew@aj.id.au> wrote:
> On Mon, 12 Apr 2021, at 18:18, Arnd Bergmann wrote:
> > On Mon, Apr 12, 2021 at 3:33 AM Andrew Jeffery <andrew@aj.id.au> wrote:
> > > On Fri, 9 Apr 2021, at 17:25, Arnd Bergmann wrote:
> > > > On Fri, Mar 19, 2021 at 7:31 AM Andrew Jeffery <andrew@aj.id.au> wrote:
> > > > >
> > > > > The existing IPMI chardev encodes IPMI behaviours as the name suggests.
> > > > > However, KCS devices are useful beyond IPMI (or keyboards), as they
> > > > > provide a means to generate IRQs and exchange arbitrary data between a
> > > > > BMC and its host system.
> > > >
> > > > I only noticed the series after Joel asked about the DT changes on the arm
> > > > side. One question though:
> > > >
> > > > How does this related to the drivers/input/serio/ framework that also talks
> > > > to the keyboard controller for things that are not keyboards?
> > >
> > > I've taken a brief look and I feel they're somewhat closely related.
> > >
> > > It's plausible that we could wrangle the code so the Aspeed and Nuvoton
> > > KCS drivers move under drivers/input/serio. If you squint, the i8042
> > > serio device driver has similarities with what the Aspeed and Nuvoton
> > > device drivers are providing to the KCS IPMI stack.
> >
> > After looking some more into it, I finally understood that the two are
> > rather complementary. While the  drivers/char/ipmi/kcs_bmc.c
> > is the other (bmc) end of drivers/char/ipmi/ipmi_kcs_sm.c, it seems
> > that the proposed kcs_bmc_cdev_raw.c interface would be
> > what corresponds to the other side of
> > drivers/input/serio/i8042.c+userio.c.
>
> Right. I guess the question is should we be splitting kernel subsystems
> along host/bmc lines? Doesn't feel intuitive, it's all Linux, but maybe
> we can consolidate in the future if it makes sense?

We actually have a number of subsystems with somewhat overlapping
functionality. I brought up serio, because it has an abstraction for multiple
things that communicate over the keyboard controller and I thought
the problem you were trying to solve was also related to the keyboard
controller.
It is also one of multiple abstractions that allow you to connect a device
to a uart (along with serdev and tty_ldisc, probably at least one more that
you can nest above or below these).

Consolidating the kcs_bmc.c interface into something that already
exists would obviously be best, but it's not clear which of these that
should be, that depends on the fundamental properties of the hardware
interface.

> > Then again, these are also on
> > separate ports (0x60 for the keyboard controller, 0xca2 for the BMC
> > KCS), so they would never actually talk to one another.
>
> Well, sort of I guess. On Power systems we don't use the keyboard
> controller for IPMI or keyboards, so we're just kinda exploiting the
> hardware for our own purposes.

Can you describe in an abstract form what the hardware interface
can do here and what you want from it? I wonder if it could be
part of a higher-level interface such as drivers/mailbox/ instead.

         Arnd

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

* Re: [PATCH v2 16/21] ipmi: kcs_bmc: Add a "raw" character device interface
  2021-04-13  8:22           ` Arnd Bergmann
@ 2021-04-14  0:30             ` Andrew Jeffery
  0 siblings, 0 replies; 67+ messages in thread
From: Andrew Jeffery @ 2021-04-14  0:30 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: Linux ARM, DTML, Chia-Wei, Wang, Ryan Chen, Tomer Maimon,
	Corey Minyard, Avi Fishman, Patrick Venture, OpenBMC Maillist,
	Dmitry Torokhov, Linux Kernel Mailing List, Tali Perry,
	open list:GPIO SUBSYSTEM, Rob Herring, openipmi-developer,
	Lee Jones, Linus Walleij, linux-aspeed, Benjamin Fair



On Tue, 13 Apr 2021, at 17:52, Arnd Bergmann wrote:
> On Tue, Apr 13, 2021 at 1:45 AM Andrew Jeffery <andrew@aj.id.au> wrote:
> > On Mon, 12 Apr 2021, at 18:18, Arnd Bergmann wrote:
> > > On Mon, Apr 12, 2021 at 3:33 AM Andrew Jeffery <andrew@aj.id.au> wrote:
> > > > On Fri, 9 Apr 2021, at 17:25, Arnd Bergmann wrote:
> > > > > On Fri, Mar 19, 2021 at 7:31 AM Andrew Jeffery <andrew@aj.id.au> wrote:
> > > > > >
> > > > > > The existing IPMI chardev encodes IPMI behaviours as the name suggests.
> > > > > > However, KCS devices are useful beyond IPMI (or keyboards), as they
> > > > > > provide a means to generate IRQs and exchange arbitrary data between a
> > > > > > BMC and its host system.
> > > > >
> > > > > I only noticed the series after Joel asked about the DT changes on the arm
> > > > > side. One question though:
> > > > >
> > > > > How does this related to the drivers/input/serio/ framework that also talks
> > > > > to the keyboard controller for things that are not keyboards?
> > > >
> > > > I've taken a brief look and I feel they're somewhat closely related.
> > > >
> > > > It's plausible that we could wrangle the code so the Aspeed and Nuvoton
> > > > KCS drivers move under drivers/input/serio. If you squint, the i8042
> > > > serio device driver has similarities with what the Aspeed and Nuvoton
> > > > device drivers are providing to the KCS IPMI stack.
> > >
> > > After looking some more into it, I finally understood that the two are
> > > rather complementary. While the  drivers/char/ipmi/kcs_bmc.c
> > > is the other (bmc) end of drivers/char/ipmi/ipmi_kcs_sm.c, it seems
> > > that the proposed kcs_bmc_cdev_raw.c interface would be
> > > what corresponds to the other side of
> > > drivers/input/serio/i8042.c+userio.c.
> >
> > Right. I guess the question is should we be splitting kernel subsystems
> > along host/bmc lines? Doesn't feel intuitive, it's all Linux, but maybe
> > we can consolidate in the future if it makes sense?
> 
> We actually have a number of subsystems with somewhat overlapping
> functionality. I brought up serio, because it has an abstraction for multiple
> things that communicate over the keyboard controller and I thought
> the problem you were trying to solve was also related to the keyboard
> controller.
> It is also one of multiple abstractions that allow you to connect a device
> to a uart (along with serdev and tty_ldisc, probably at least one more that
> you can nest above or below these).
> 
> Consolidating the kcs_bmc.c interface into something that already
> exists would obviously be best, but it's not clear which of these that
> should be, that depends on the fundamental properties of the hardware
> interface.
> 
> > > Then again, these are also on
> > > separate ports (0x60 for the keyboard controller, 0xca2 for the BMC
> > > KCS), so they would never actually talk to one another.
> >
> > Well, sort of I guess. On Power systems we don't use the keyboard
> > controller for IPMI or keyboards, so we're just kinda exploiting the
> > hardware for our own purposes.
> 
> Can you describe in an abstract form what the hardware interface
> can do here and what you want from it? I wonder if it could be
> part of a higher-level interface such as drivers/mailbox/ instead.

It gives us interrupts each way between the host and BMC when we send 
some (small amount of) data/metadata. Mailbox is possibly a fit for 
this? We're (ab)using the keyboard controllers to implement a vendor 
MCTP binding over LPC[1] and also a simple protocol for the (Power) 
host to trigger BMC debug data capture in the event of issues with 
other (more complex) in-band communication stacks. The MCTP binding is 
what requires access to STR.

It's feasible that we could implement the debug capture protocol with 
the serio_raw interface now that I think about it (as it only makes use 
of data and not status). What's unclear to me right now is what impact 
that has on the Aspeed/Nuvoton KCS drivers we have in the IPMI 
subsystem. If we can do something sensible to service both serio and 
IPMI with the one driver implementation then I can put together a PoC 
for the debug data stuff using serio_raw.

Regarding the MCTP binding, Jeremy Kerr is working in an in-kernel, 
socket-based implementation of MCTP. Eventually this will allow us to 
bury the KCS details in the MCTP subsystem, which removes some of the 
motivation for the raw interface here.

Andrew

[1] https://github.com/openbmc/libmctp/blob/master/docs/bindings/vendor-ibm-astlpc.md

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

end of thread, other threads:[~2021-04-14  0:32 UTC | newest]

Thread overview: 67+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-03-19  6:27 [PATCH v2 01/21] dt-bindings: aspeed-lpc: Remove LPC partitioning Andrew Jeffery
2021-03-19  6:27 ` [PATCH v2 02/21] ARM: dts: Remove LPC BMC and Host partitions Andrew Jeffery
2021-03-19  6:27 ` [PATCH v2 03/21] ipmi: kcs: aspeed: Adapt to new LPC DTS layout Andrew Jeffery
2021-04-09  3:35   ` Joel Stanley
2021-03-19  6:27 ` [PATCH v2 04/21] pinctrl: aspeed-g5: Adapt to new LPC device tree layout Andrew Jeffery
2021-04-09  3:36   ` Joel Stanley
2021-03-19  6:27 ` [PATCH v2 05/21] soc: aspeed: " Andrew Jeffery
2021-04-09  3:38   ` Joel Stanley
2021-03-19  6:27 ` [PATCH v2 06/21] ipmi: kcs_bmc_aspeed: Use of match data to extract KCS properties Andrew Jeffery
2021-04-06  6:07   ` ChiaWei Wang
2021-04-09  3:24   ` Zev Weiss
2021-03-19  6:27 ` [PATCH v2 07/21] ipmi: kcs_bmc: Make status update atomic Andrew Jeffery
2021-04-09  5:32   ` Zev Weiss
2021-03-19  6:27 ` [PATCH v2 08/21] ipmi: kcs_bmc: Rename {read, write}_{status, data}() functions Andrew Jeffery
2021-04-09  5:33   ` Zev Weiss
2021-03-19  6:27 ` [PATCH v2 09/21] ipmi: kcs_bmc: Split out kcs_bmc_cdev_ipmi Andrew Jeffery
2021-04-09  3:56   ` Zev Weiss
2021-04-09  5:48     ` Andrew Jeffery
2021-04-09 19:21       ` Zev Weiss
2021-03-19  6:27 ` [PATCH v2 10/21] ipmi: kcs_bmc: Turn the driver data-structures inside-out Andrew Jeffery
2021-04-09  3:57   ` Zev Weiss
2021-04-09  5:59     ` Andrew Jeffery
2021-04-09  6:25       ` Zev Weiss
2021-04-09 19:26         ` Zev Weiss
2021-04-11 23:00           ` Andrew Jeffery
2021-03-19  6:27 ` [PATCH v2 11/21] ipmi: kcs_bmc: Split headers into device and client Andrew Jeffery
2021-04-09  4:01   ` Zev Weiss
2021-04-09  6:06     ` Andrew Jeffery
2021-03-19  6:27 ` [PATCH v2 12/21] ipmi: kcs_bmc: Strip private client data from struct kcs_bmc Andrew Jeffery
2021-04-09  4:07   ` Zev Weiss
2021-04-09  6:15     ` Andrew Jeffery
2021-03-19  6:27 ` [PATCH v2 13/21] ipmi: kcs_bmc: Decouple the IPMI chardev from the core Andrew Jeffery
2021-04-06  6:07   ` ChiaWei Wang
2021-04-09  4:35   ` Zev Weiss
2021-04-09  6:24     ` Andrew Jeffery
2021-03-19  6:27 ` [PATCH v2 14/21] ipmi: kcs_bmc: Allow clients to control KCS IRQ state Andrew Jeffery
2021-04-09  4:37   ` Zev Weiss
2021-04-09  6:39     ` Andrew Jeffery
2021-03-19  6:27 ` [PATCH v2 15/21] ipmi: kcs_bmc: Don't enforce single-open policy in the kernel Andrew Jeffery
2021-04-09  5:07   ` Zev Weiss
2021-04-09  6:42     ` Andrew Jeffery
2021-03-19  6:27 ` [PATCH v2 16/21] ipmi: kcs_bmc: Add a "raw" character device interface Andrew Jeffery
2021-04-09  5:17   ` Zev Weiss
2021-04-09  6:46     ` Andrew Jeffery
2021-04-09  7:55   ` Arnd Bergmann
2021-04-12  1:33     ` Andrew Jeffery
2021-04-12  8:48       ` Arnd Bergmann
2021-04-12 23:45         ` Andrew Jeffery
2021-04-13  8:22           ` Arnd Bergmann
2021-04-14  0:30             ` Andrew Jeffery
2021-03-19  6:27 ` [PATCH v2 17/21] dt-bindings: ipmi: Convert ASPEED KCS binding to schema Andrew Jeffery
2021-03-26  1:48   ` Rob Herring
2021-04-09  5:15   ` Zev Weiss
2021-04-09  5:33     ` Andrew Jeffery
2021-04-09  5:44       ` Zev Weiss
2021-04-09  8:46         ` Zev Weiss
2021-03-19  6:27 ` [PATCH v2 18/21] dt-bindings: ipmi: Add optional SerIRQ property to ASPEED KCS devices Andrew Jeffery
2021-03-26  1:49   ` Rob Herring
2021-03-19  6:27 ` [PATCH v2 19/21] ipmi: kcs_bmc_aspeed: Implement KCS SerIRQ configuration Andrew Jeffery
2021-04-01  9:30   ` [EXTERNAL] " Zev Weiss
2021-03-19  6:27 ` [PATCH v2 20/21] ipmi: kcs_bmc_aspeed: Fix IBFIE typo from datasheet Andrew Jeffery
2021-04-09  5:40   ` Zev Weiss
2021-03-19  6:27 ` [PATCH v2 21/21] ipmi: kcs_bmc_aspeed: Optionally apply status address Andrew Jeffery
2021-04-01 18:18   ` Re " Zev Weiss
2021-04-06  6:09   ` ChiaWei Wang
2021-04-09  3:18 ` [PATCH v2 01/21] dt-bindings: aspeed-lpc: Remove LPC partitioning Joel Stanley
2021-04-09  5:24   ` Andrew Jeffery

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).