summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/ABI/testing/sysfs-bus-fsi8
-rw-r--r--Documentation/admin-guide/kernel-parameters.txt3
-rw-r--r--Documentation/devicetree/bindings/bmc/npcm7xx-lpc-bpc.txt26
-rw-r--r--Documentation/devicetree/bindings/bmc/npcm7xx-pci-mbox.txt19
-rw-r--r--Documentation/devicetree/bindings/edac/npcm7xx-sdram-edac.txt17
-rw-r--r--Documentation/devicetree/bindings/fsi/fsi-master-aspeed.txt12
-rw-r--r--Documentation/devicetree/bindings/fsi/ibm,fsi2spi.yaml10
-rw-r--r--Documentation/devicetree/bindings/fsi/ibm,p9-occ.txt12
-rw-r--r--Documentation/devicetree/bindings/hwmon/pmbus/max31785.txt158
-rw-r--r--Documentation/devicetree/bindings/mfd/intel,peci-client.yaml67
-rw-r--r--Documentation/devicetree/bindings/net/nuvoton,npcm7xx-emc.txt36
-rw-r--r--Documentation/devicetree/bindings/peci/peci-aspeed.yaml124
-rw-r--r--Documentation/devicetree/bindings/peci/peci-bus.yaml129
-rw-r--r--Documentation/devicetree/bindings/peci/peci-client.yaml54
-rw-r--r--Documentation/devicetree/bindings/peci/peci-npcm.yaml102
-rw-r--r--Documentation/devicetree/bindings/soc/aspeed/xdma.yaml103
-rw-r--r--Documentation/hwmon/index.rst2
-rw-r--r--Documentation/hwmon/peci-cputemp.rst95
-rw-r--r--Documentation/hwmon/peci-dimmtemp.rst60
-rw-r--r--Documentation/userspace-api/ioctl/ioctl-number.rst2
-rw-r--r--MAINTAINERS14
-rw-r--r--arch/arm/boot/dts/Makefile6
-rw-r--r--arch/arm/boot/dts/aspeed-ast2600-evb.dts20
-rw-r--r--arch/arm/boot/dts/aspeed-bmc-amd-ethanolx.dts219
-rw-r--r--arch/arm/boot/dts/aspeed-bmc-facebook-cmm.dts1231
-rw-r--r--arch/arm/boot/dts/aspeed-bmc-facebook-wedge40.dts42
-rw-r--r--arch/arm/boot/dts/aspeed-bmc-ibm-rainier.dts589
-rw-r--r--arch/arm/boot/dts/aspeed-bmc-opp-mihawk.dts152
-rw-r--r--arch/arm/boot/dts/aspeed-bmc-opp-tacoma.dts84
-rw-r--r--arch/arm/boot/dts/aspeed-bmc-opp-witherspoon-128.dts65
-rw-r--r--arch/arm/boot/dts/aspeed-bmc-opp-witherspoon.dts68
-rw-r--r--arch/arm/boot/dts/aspeed-g4.dtsi112
-rw-r--r--arch/arm/boot/dts/aspeed-g5.dtsi170
-rw-r--r--arch/arm/boot/dts/aspeed-g6.dtsi44
-rw-r--r--arch/arm/boot/dts/nuvoton-common-npcm7xx.dtsi1150
-rw-r--r--arch/arm/boot/dts/nuvoton-npcm730-gsj-gpio.dtsi477
-rw-r--r--arch/arm/boot/dts/nuvoton-npcm730-gsj.dts523
-rw-r--r--arch/arm/boot/dts/nuvoton-npcm730.dtsi57
-rw-r--r--arch/arm/boot/dts/nuvoton-npcm750-evb.dts526
-rw-r--r--arch/arm/boot/dts/nuvoton-npcm750-gpio.dtsi2021
-rw-r--r--arch/arm/boot/dts/nuvoton-npcm750-runbmc-olympus-pincfg.dtsi517
-rw-r--r--arch/arm/boot/dts/nuvoton-npcm750-runbmc-olympus.dts1162
-rw-r--r--arch/arm/boot/dts/nuvoton-npcm750.dtsi140
-rw-r--r--arch/arm/configs/aspeed_g4_defconfig18
-rw-r--r--arch/arm/configs/aspeed_g5_defconfig59
-rw-r--r--arch/arm/configs/npcm7xx_defconfig127
-rw-r--r--drivers/Kconfig3
-rw-r--r--drivers/Makefile1
-rw-r--r--drivers/char/Kconfig9
-rw-r--r--drivers/char/ipmi/bt-bmc.c1
-rw-r--r--drivers/char/ipmi/kcs_bmc_aspeed.c1
-rw-r--r--drivers/char/mem.c11
-rw-r--r--drivers/clk/clk-ast2600.c2
-rw-r--r--drivers/edac/Kconfig7
-rw-r--r--drivers/edac/Makefile1
-rw-r--r--drivers/edac/npcm7xx_edac.c421
-rw-r--r--drivers/fsi/fsi-core.c31
-rw-r--r--drivers/fsi/fsi-master-aspeed.c134
-rw-r--r--drivers/fsi/fsi-master-ast-cf.c7
-rw-r--r--drivers/fsi/fsi-master-gpio.c5
-rw-r--r--drivers/fsi/fsi-master-hub.c15
-rw-r--r--drivers/fsi/fsi-master.h3
-rw-r--r--drivers/fsi/fsi-occ.c128
-rw-r--r--drivers/fsi/fsi-sbefifo.c12
-rw-r--r--drivers/fsi/fsi-scom.c2
-rw-r--r--drivers/hwmon/Kconfig28
-rw-r--r--drivers/hwmon/Makefile2
-rw-r--r--drivers/hwmon/occ/common.c65
-rw-r--r--drivers/hwmon/peci-cputemp.c472
-rw-r--r--drivers/hwmon/peci-dimmtemp.c425
-rw-r--r--drivers/hwmon/peci-hwmon.h48
-rw-r--r--drivers/hwmon/pmbus/max31785.c497
-rw-r--r--drivers/hwmon/pmbus/pmbus_core.c22
-rw-r--r--drivers/i2c/busses/i2c-fsi.c7
-rw-r--r--drivers/leds/leds-pca955x.c8
-rw-r--r--drivers/mfd/Kconfig17
-rw-r--r--drivers/mfd/Makefile1
-rw-r--r--drivers/mfd/intel-peci-client.c148
-rw-r--r--drivers/misc/Kconfig22
-rw-r--r--drivers/misc/Makefile3
-rw-r--r--drivers/misc/eeprom/at25.c94
-rw-r--r--drivers/misc/mctp-lpc.c443
-rw-r--r--drivers/misc/npcm7xx-lpc-bpc.c394
-rw-r--r--drivers/misc/npcm7xx-pci-mbox.c288
-rw-r--r--drivers/mtd/spi-nor/controllers/aspeed-smc.c588
-rw-r--r--drivers/mtd/spi-nor/sfdp.c1
-rw-r--r--drivers/mtd/spi-nor/spi-nor.c5155
-rw-r--r--drivers/mtd/spi-nor/winbond.c2
-rw-r--r--drivers/net/ethernet/Kconfig1
-rw-r--r--drivers/net/ethernet/Makefile1
-rw-r--r--drivers/net/ethernet/nuvoton/Kconfig30
-rw-r--r--drivers/net/ethernet/nuvoton/Makefile6
-rw-r--r--drivers/net/ethernet/nuvoton/npcm7xx_emc.c2090
-rw-r--r--drivers/peci/Kconfig37
-rw-r--r--drivers/peci/Makefile11
-rw-r--r--drivers/peci/busses/Kconfig34
-rw-r--r--drivers/peci/busses/Makefile7
-rw-r--r--drivers/peci/busses/peci-aspeed.c484
-rw-r--r--drivers/peci/busses/peci-npcm.c406
-rw-r--r--drivers/peci/peci-core.c2089
-rw-r--r--drivers/peci/peci-dev.c348
-rw-r--r--drivers/pinctrl/aspeed/pinctrl-aspeed-g6.c7
-rw-r--r--drivers/pinctrl/aspeed/pinctrl-aspeed.c25
-rw-r--r--drivers/reset/reset-simple.c1
-rw-r--r--drivers/soc/aspeed/Kconfig16
-rw-r--r--drivers/soc/aspeed/Makefile2
-rw-r--r--drivers/soc/aspeed/aspeed-bmc-misc.c190
-rw-r--r--drivers/soc/aspeed/aspeed-lpc-ctrl.c31
-rw-r--r--drivers/soc/aspeed/aspeed-lpc-snoop.c2
-rw-r--r--drivers/soc/aspeed/aspeed-xdma.c1205
-rw-r--r--drivers/spi/spi-fsi.c139
-rw-r--r--include/linux/mfd/intel-peci-client.h119
-rw-r--r--include/linux/peci.h150
-rw-r--r--include/uapi/linux/aspeed-xdma.h42
-rw-r--r--include/uapi/linux/peci-ioctl.h661
115 files changed, 27905 insertions, 385 deletions
diff --git a/Documentation/ABI/testing/sysfs-bus-fsi b/Documentation/ABI/testing/sysfs-bus-fsi
index 320697bdf41d..95090a8b1b17 100644
--- a/Documentation/ABI/testing/sysfs-bus-fsi
+++ b/Documentation/ABI/testing/sysfs-bus-fsi
@@ -36,3 +36,11 @@ Contact: linux-fsi@lists.ozlabs.org
Description:
Provides a means of reading/writing a 32 bit value from/to a
specified FSI bus address.
+
+What: /sys/bus/platform/devices/../cfam_reset
+Date: June 2020
+KernelVersion: 5.9
+Contact: linux-fsi@lists.ozlabs.org
+Description:
+ Provides a means of resetting the cfam that is attached to the
+ FSI device.
diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
index fb95fad81c79..fd1bc986483d 100644
--- a/Documentation/admin-guide/kernel-parameters.txt
+++ b/Documentation/admin-guide/kernel-parameters.txt
@@ -2750,6 +2750,9 @@
deep - Suspend-To-RAM or equivalent (if supported)
See Documentation/admin-guide/pm/sleep-states.rst.
+ mem.devmem= Activate the /dev/mem device
+ Format: <bool> (1/Y/y=enable, 0/N/n=disable)
+
meye.*= [HW] Set MotionEye Camera parameters
See Documentation/admin-guide/media/meye.rst.
diff --git a/Documentation/devicetree/bindings/bmc/npcm7xx-lpc-bpc.txt b/Documentation/devicetree/bindings/bmc/npcm7xx-lpc-bpc.txt
new file mode 100644
index 000000000000..0832c9cbea32
--- /dev/null
+++ b/Documentation/devicetree/bindings/bmc/npcm7xx-lpc-bpc.txt
@@ -0,0 +1,26 @@
+Nuvoton NPCM7xx LPC BPC interface
+
+Nuvoton BMC NPCM7xx BIOS Post Code (BPC) monitoring two
+configurable I/O addresses written by the host on the
+Low Pin Count (LPC) bus, the capure data stored in 128-word FIFO.
+
+NPCM7xx BPC supports capture double words, when using capture
+double word only I/O address 1 is monitored.
+
+Required properties for lpc_bpc node
+- compatible : "nuvoton,npcm750-lpc-bpc" for Poleg NPCM7XX.
+- reg : specifies physical base address and size of the registers.
+- interrupts : contain the LPC BPC with flags for falling edge.
+- monitor-ports : contain monitor I/O addresses, at least one monitor I/O
+ address required
+
+Optional property for lpc_bpc node
+- bpc-en-dwcapture : enable capture double words support.
+
+Example:
+ lpc_bpc: lpc-bpc@f0007040 {
+ compatible = "nuvoton,npcm7xx-lpc-bpc";
+ reg = <0xf0007040 0x14>;
+ monitor-ports = <0x80>;
+ interrupts = <GIC_SPI 9 IRQ_TYPE_LEVEL_HIGH>;
+ };
diff --git a/Documentation/devicetree/bindings/bmc/npcm7xx-pci-mbox.txt b/Documentation/devicetree/bindings/bmc/npcm7xx-pci-mbox.txt
new file mode 100644
index 000000000000..e5585f38041b
--- /dev/null
+++ b/Documentation/devicetree/bindings/bmc/npcm7xx-pci-mbox.txt
@@ -0,0 +1,19 @@
+Nuvoton NPCM7xx PCI mail box interface
+
+Nuvoton BMC NPCM7xx PCI mail box, The mailbox is a high-bandwidth
+communication module between the BMC CPU and host CPU.
+
+Required properties for lpc_bpc node
+- compatible : "nuvoton,npcm750-pci-mbox" for Poleg NPCM7XX.
+- reg : specifies two address space
+ 1. physical base address and size of the registers.
+ 2. physical base address and size of the dual-ported RAM.
+- interrupts : contain the PCI mail box interrupt with flags for falling edge.
+
+Example:
+ pcimbox: pcimbox@f0848000 {
+ compatible = "nuvoton,npcm750-pci-mbox", "simple-mfd", "syscon";
+ reg = <0xf084C000 0x8
+ 0xf0848000 0x3F00>;
+ interrupts = <GIC_SPI 8 IRQ_TYPE_LEVEL_HIGH>;
+ };
diff --git a/Documentation/devicetree/bindings/edac/npcm7xx-sdram-edac.txt b/Documentation/devicetree/bindings/edac/npcm7xx-sdram-edac.txt
new file mode 100644
index 000000000000..dd4dac59a5bd
--- /dev/null
+++ b/Documentation/devicetree/bindings/edac/npcm7xx-sdram-edac.txt
@@ -0,0 +1,17 @@
+Nuvoton NPCM7xx SoC EDAC device driver
+
+The Nuvoton NPCM7xx SoC supports DDR4 memory with/without ECC and the driver
+uses the EDAC framework to implement the ECC detection and corrtection.
+
+Required properties:
+- compatible: should be "nuvoton,npcm7xx-sdram-edac"
+- reg: Memory controller register set should be <0xf0824000 0x1000>
+- interrupts: should be MC interrupt #25
+
+Example:
+
+ mc: memory-controller@f0824000 {
+ compatible = "nuvoton,npcm7xx-sdram-edac";
+ reg = <0xf0824000 0x1000>;
+ interrupts = <0 25 4>;
+ };
diff --git a/Documentation/devicetree/bindings/fsi/fsi-master-aspeed.txt b/Documentation/devicetree/bindings/fsi/fsi-master-aspeed.txt
index b758f91914f7..a513e65ec0c9 100644
--- a/Documentation/devicetree/bindings/fsi/fsi-master-aspeed.txt
+++ b/Documentation/devicetree/bindings/fsi/fsi-master-aspeed.txt
@@ -12,6 +12,13 @@ Required properties:
- pinctrl-0: phandle to pinctrl node
- pinctrl-names: pinctrl state
+Optional properties:
+ - cfam-reset-gpios: GPIO for CFAM reset
+
+ - fsi-routing-gpios: GPIO for setting the FSI mux (internal or cabled)
+ - fsi-mux-gpios: GPIO for detecting the desired FSI mux state
+
+
Examples:
fsi-master {
@@ -21,4 +28,9 @@ Examples:
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_fsi1_default>;
clocks = <&syscon ASPEED_CLK_GATE_FSICLK>;
+
+ fsi-routing-gpios = <&gpio0 ASPEED_GPIO(Q, 7) GPIO_ACTIVE_HIGH>;
+ fsi-mux-gpios = <&gpio0 ASPEED_GPIO(B, 0) GPIO_ACTIVE_HIGH>;
+
+ cfam-reset-gpios = <&gpio0 ASPEED_GPIO(Q, 0) GPIO_ACTIVE_LOW>;
};
diff --git a/Documentation/devicetree/bindings/fsi/ibm,fsi2spi.yaml b/Documentation/devicetree/bindings/fsi/ibm,fsi2spi.yaml
index 893d81e54caa..ff16797061ec 100644
--- a/Documentation/devicetree/bindings/fsi/ibm,fsi2spi.yaml
+++ b/Documentation/devicetree/bindings/fsi/ibm,fsi2spi.yaml
@@ -24,6 +24,16 @@ properties:
items:
- description: FSI slave address
+patternProperties:
+ "^spi(@.*|-[0-9a-f])*$":
+ type: object
+
+ properties:
+ fsi2spi,restricted:
+ description: indicates the controller should not use looping in the
+ sequencer and therefore has a smaller maximum transfer size
+ type: boolean
+
required:
- compatible
- reg
diff --git a/Documentation/devicetree/bindings/fsi/ibm,p9-occ.txt b/Documentation/devicetree/bindings/fsi/ibm,p9-occ.txt
index 99ca9862a586..e73358075a90 100644
--- a/Documentation/devicetree/bindings/fsi/ibm,p9-occ.txt
+++ b/Documentation/devicetree/bindings/fsi/ibm,p9-occ.txt
@@ -1,13 +1,13 @@
-Device-tree bindings for FSI-attached POWER9 On-Chip Controller (OCC)
----------------------------------------------------------------------
+Device-tree bindings for FSI-attached POWER9/POWER10 On-Chip Controller (OCC)
+-----------------------------------------------------------------------------
-This is the binding for the P9 On-Chip Controller accessed over FSI from a
-service processor. See fsi.txt for details on bindings for FSI slave and CFAM
+This is the binding for the P9 or P10 On-Chip Controller accessed over FSI from
+a service processor. See fsi.txt for details on bindings for FSI slave and CFAM
nodes. The OCC is not an FSI slave device itself, rather it is accessed
-through the SBE fifo.
+through the SBE FIFO.
Required properties:
- - compatible = "ibm,p9-occ"
+ - compatible = "ibm,p9-occ" or "ibm,p10-occ"
Examples:
diff --git a/Documentation/devicetree/bindings/hwmon/pmbus/max31785.txt b/Documentation/devicetree/bindings/hwmon/pmbus/max31785.txt
new file mode 100644
index 000000000000..af9578e7742c
--- /dev/null
+++ b/Documentation/devicetree/bindings/hwmon/pmbus/max31785.txt
@@ -0,0 +1,158 @@
+Bindings for the Maxim MAX31785 Intelligent Fan Controller
+==========================================================
+
+Reference:
+
+https://datasheets.maximintegrated.com/en/ds/MAX31785.pdf
+
+Required properties:
+- compatible : One of "maxim,max31785" or "maxim,max31785a"
+- reg : I2C address, one of 0x52, 0x53, 0x54, 0x55.
+- #address-cells : Must be 1
+- #size-cells : Must be 0
+- #thermal-sensor-cells : Should be 1. The device supports:
+ - One internal sensor
+ - Four external I2C digital sensors
+ - Six external thermal diodes
+
+Optional properties:
+- use-stored-presence : Do not treat the devicetree description as canon for
+ fan presence (the 'installed' bit of FAN_CONFIG_*).
+ Instead, rely on the on the default value store of
+ the device to populate it.
+
+Capabilities are configured through subnodes of the controller's node.
+
+Fans
+----
+
+Only fans with subnodes present will be considered as installed. If
+use-stored-presence is present in the parent node, then only fans that are both
+defined in the devicetree and have their installed bit set are considered
+installed.
+
+Required subnode properties:
+- compatible : Must be "pmbus-fan"
+- reg : The PMBus page the properties apply to.
+- #cooling-cells : Should be 2. See the thermal bindings at [1].
+- maxim,fan-rotor-input : The type of rotor measurement provided to the
+ controller. Must be either "tach" for tachometer
+ pulses or "lock" for a locked-rotor signal.
+- maxim,fan-lock-polarity: Required iff maxim,fan-rotor-input is "lock". Valid
+ values are "low" for active low, "high" for active
+ high.
+
+Optional subnode properties:
+- fan-mode : "rpm" or "pwm". Default value is "pwm".
+- tach-pulses : Tachometer pulses per revolution. Valid values are
+ 1, 2, 3 or 4. The default is 1.
+- cooling-min-level : Smallest cooling state accepted. See [1].
+- cooling-max-level : Largest cooling state accepted. See [1].
+- maxim,fan-no-fault-ramp: Do not ramp the fan to 100% PWM duty on detecting a
+ fan fault
+- maxim,fan-startup : The number of rotations required before taking
+ emergency action for an unresponsive fan and driving
+ it with 100% or 0% PWM duty, depending on the state
+ of maxim,fan-no-fault-ramp. Valid values are 0
+ (automatic spin-up disabled), 2, 4, or 8. Default
+ value is 0.
+- maxim,fan-health : Enable automated fan health check
+- maxim,fan-ramp : Configures how fast the device ramps the PWM duty
+ cycle from one value to another. Valid values are 0
+ to 7 inclusive, with values 0 - 2 configuring a
+ 1000ms update rate and 1 - 3% duty respective duty
+ increase, and 3 - 7 a 200ms update rate with a 1 -
+ 5% respective duty increase. Default value is 0.
+- maxim,fan-no-watchdog : Do not ramp fan to 100% PWM duty on failure to
+ update desired fan rate inside 10s. This implies
+ maxim,tmp-no-fault-ramp
+- maxim,tmp-no-fault-ramp: Do not ramp fan to 100% PWM duty on temperature
+ sensor fault detection. This implies
+ maxim,fan-no-watchdog
+- maxim,tmp-hysteresis : The temperature hysteresis used to determine
+ transitions to lower fan speed bands in the
+ temperature/fan rate lookup table. Valid values are
+ 2, 4, 6 or 8 (degrees celcius). Default value is 2.
+- maxim,fan-dual-tach : Enable dual tachometer functionality
+- maxim,fan-pwm-freq : The PWM frequency. Valid values are 30, 50, 100, 150
+ and 25000 (Hz). Default value is 30Hz.
+- maxim,fan-lookup-table : A 16-element cell array of alternating temperature
+ and rate values representing the look up table. The
+ rate units are set through the fan-mode property.
+- maxim,fan-fault-pin-mon: Ramp fans to 100% PWM duty when the FAULT pin is
+ asserted
+
+Temperature
+-----------
+
+Required subnode properties:
+- compatible : Must be "pmbus-temperature"
+- reg : The PMBus page the properties apply to.
+
+Optional subnode properties:
+- maxim,tmp-offset : Valid values are 0 - 30 (degrees celcius) inclusive.
+ Default value is 0.
+- maxim,tmp-fans : An array of phandles to fans controlled by the
+ current temperature sensor.
+
+[1] Documentation/devicetree/bindings/thermal/thermal.txt
+
+Example:
+ fan-max31785: max31785@52 {
+ reg = <0x52>;
+ compatible = "maxim,max31785";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ #thermal-sensor-cells = <1>;
+
+ fan@0 {
+ compatible = "pmbus-fan";
+ reg = <0>;
+ mode = "rpm";
+ tach-pulses = <1>;
+
+ #cooling-cells = <2>;
+ cooling-min-level = <0>;
+ cooling-max-level = <9>;
+
+ maxim,fan-rotor-input = "tach";
+ maxim,fan-dual-tach;
+ };
+
+ /*
+ * Hardware controlled fan: Fan speed is controlled by a
+ * temperature sensor feeding values into the lookup table. The
+ * fan association is done in the temperature sensor node. One
+ * sensor can drive multiple fans.
+ */
+ cpu_fan: fan@1 {
+ compatible = "pmbus-fan";
+ reg = <1>;
+ mode = "rpm";
+ tach-pulses = <1>;
+
+ #cooling-cells = <2>;
+
+ maxim,fan-rotor-input = "tach";
+ maxim,tmp-hysteresis = <2>;
+ maxim,fan-lookup-table = <
+ /* Temperature RPM */
+ 0 1000
+ 10 2000
+ 20 3000
+ 30 4000
+ 40 5000
+ 50 6000
+ 60 7000
+ 70 8000
+ >;
+ };
+
+ cpu_temp: sensor@6 {
+ compatible = "pmbus-temperature";
+ reg = <6>;
+
+ maxim,tmp-offset = <0>;
+ maxim,tmp-fans = <&cpu_fan>;
+ };
+ };
diff --git a/Documentation/devicetree/bindings/mfd/intel,peci-client.yaml b/Documentation/devicetree/bindings/mfd/intel,peci-client.yaml
new file mode 100644
index 000000000000..7baddce0a92c
--- /dev/null
+++ b/Documentation/devicetree/bindings/mfd/intel,peci-client.yaml
@@ -0,0 +1,67 @@
+# SPDX-License-Identifier: GPL-2.0
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/mfd/intel,peci-client.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Intel PECI Client Device Tree Bindings
+
+maintainers:
+ - Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
+
+description: |
+ PECI (Platform Environment Control Interface) is a one-wire bus interface
+ that provides a communication channel from PECI clients in Intel processors
+ and chipset components to external monitoring or control devices. PECI is
+ designed to support the following sideband functions:
+ - Processor and DRAM thermal management
+ - Platform Manageability
+ - Processor Interface Tuning and Diagnostics
+ - Failure Analysis
+
+properties:
+ compatible:
+ const: intel,peci-client
+
+ reg:
+ description: |
+ Address of a client CPU. According to the PECI specification, client
+ addresses start from 0x30.
+ maxItems: 1
+
+required:
+ - compatible
+ - reg
+
+examples:
+ - |
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+ #include <dt-bindings/clock/ast2600-clock.h>
+ peci: bus@1e78b000 {
+ compatible = "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges = <0x0 0x1e78b000 0x60>;
+
+ peci0: peci-bus@0 {
+ compatible = "aspeed,ast2600-peci";
+ reg = <0x0 0x100>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ interrupts = <GIC_SPI 38 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&syscon ASPEED_CLK_GATE_REF0CLK>;
+ resets = <&syscon ASPEED_RESET_PECI>;
+ clock-frequency = <24000000>;
+
+ peci-client@30 {
+ compatible = "intel,peci-client";
+ reg = <0x30>;
+ };
+
+ peci-client@31 {
+ compatible = "intel,peci-client";
+ reg = <0x31>;
+ };
+ };
+ };
+...
diff --git a/Documentation/devicetree/bindings/net/nuvoton,npcm7xx-emc.txt b/Documentation/devicetree/bindings/net/nuvoton,npcm7xx-emc.txt
new file mode 100644
index 000000000000..4227597401f5
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/nuvoton,npcm7xx-emc.txt
@@ -0,0 +1,36 @@
+Nuvoton NPCM7XX 10/100 Ethernet MAC Controller (EMC)
+
+The NPCM750x provides two identical Ethernet MAC Controllers
+for WAN/LAN applications
+
+Required properties:
+- device_type : Should be "network"
+- compatible : "nuvoton,npcm750-emc" for Poleg NPCM750.
+- reg : Offset and length of the register set for the device.
+- interrupts : Contain the emc interrupts with flags for falling edge.
+ first interrupt dedicated to Txirq
+ second interrupt dedicated to Rxirq
+- phy-mode : Should be "rmii" (see ethernet.txt in the same directory)
+- clocks : phandle of emc reference clock.
+- use-ncsi : Use the NC-SI stack instead of an MDIO PHY
+
+Example:
+
+emc0: eth@f0825000 {
+ device_type = "network";
+ compatible = "nuvoton,npcm750-emc";
+ reg = <0xf0825000 0x1000>;
+ interrupts = <GIC_SPI 16 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 15 IRQ_TYPE_LEVEL_HIGH>;
+ phy-mode = "rmii";
+ clocks = <&clk NPCM7XX_CLK_AHB>;
+
+ #use-ncsi; /* add this to support ncsi */
+
+ clock-names = "clk_emc";
+ pinctrl-names = "default";
+ pinctrl-0 = <&r1_pins
+ &r1err_pins
+ &r1md_pins>;
+ status = "okay";
+}; \ No newline at end of file
diff --git a/Documentation/devicetree/bindings/peci/peci-aspeed.yaml b/Documentation/devicetree/bindings/peci/peci-aspeed.yaml
new file mode 100644
index 000000000000..0f5c2993fe9b
--- /dev/null
+++ b/Documentation/devicetree/bindings/peci/peci-aspeed.yaml
@@ -0,0 +1,124 @@
+# SPDX-License-Identifier: GPL-2.0
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/peci/peci-aspeed.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Aspeed PECI Bus Device Tree Bindings
+
+maintainers:
+ - Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
+
+properties:
+ compatible:
+ enum:
+ - aspeed,ast2400-peci
+ - aspeed,ast2500-peci
+ - aspeed,ast2600-peci
+
+ reg:
+ maxItems: 1
+
+ "#address-cells":
+ # Required to define a client address.
+ const: 1
+
+ "#size-cells":
+ # Required to define a client address.
+ const: 0
+
+ interrupts:
+ maxItems: 1
+
+ clocks:
+ description: |
+ Clock source for PECI controller. Should reference the external
+ oscillator clock.
+ maxItems: 1
+
+ resets:
+ maxItems: 1
+
+ clock-frequency:
+ # Operation frequency of PECI controller in units of Hz.
+ minimum: 187500
+ maximum: 24000000
+
+ msg-timing:
+ description: |
+ Message timing negotiation period. This value will determine the period
+ of message timing negotiation to be issued by PECI controller. The unit
+ of the programmed value is four times of PECI clock period.
+ allOf:
+ - $ref: /schemas/types.yaml#/definitions/uint32
+ - minimum: 0
+ maximum: 255
+ default: 1
+
+ addr-timing:
+ description: |
+ Address timing negotiation period. This value will determine the period
+ of address timing negotiation to be issued by PECI controller. The unit
+ of the programmed value is four times of PECI clock period.
+ allOf:
+ - $ref: /schemas/types.yaml#/definitions/uint32
+ - minimum: 0
+ maximum: 255
+ default: 1
+
+ rd-sampling-point:
+ description: |
+ Read sampling point selection. The whole period of a bit time will be
+ divided into 16 time frames. This value will determine the time frame
+ in which the controller will sample PECI signal for data read back.
+ Usually in the middle of a bit time is the best.
+ allOf:
+ - $ref: /schemas/types.yaml#/definitions/uint32
+ - minimum: 0
+ maximum: 15
+ default: 8
+
+ cmd-timeout-ms:
+ # Command timeout in units of ms.
+ allOf:
+ - $ref: /schemas/types.yaml#/definitions/uint32
+ - minimum: 1
+ maximum: 60000
+ default: 1000
+
+required:
+ - compatible
+ - reg
+ - "#address-cells"
+ - "#size-cells"
+ - interrupts
+ - clocks
+ - resets
+ - clock-frequency
+
+examples:
+ - |
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+ #include <dt-bindings/clock/ast2600-clock.h>
+ peci: bus@1e78b000 {
+ compatible = "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges = <0x0 0x1e78b000 0x60>;
+
+ peci0: peci-bus@0 {
+ compatible = "aspeed,ast2600-peci";
+ reg = <0x0 0x100>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ interrupts = <GIC_SPI 38 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&syscon ASPEED_CLK_GATE_REF0CLK>;
+ resets = <&syscon ASPEED_RESET_PECI>;
+ clock-frequency = <24000000>;
+ msg-timing = <1>;
+ addr-timing = <1>;
+ rd-sampling-point = <8>;
+ cmd-timeout-ms = <1000>;
+ };
+ };
+...
diff --git a/Documentation/devicetree/bindings/peci/peci-bus.yaml b/Documentation/devicetree/bindings/peci/peci-bus.yaml
new file mode 100644
index 000000000000..b085e67089cf
--- /dev/null
+++ b/Documentation/devicetree/bindings/peci/peci-bus.yaml
@@ -0,0 +1,129 @@
+# SPDX-License-Identifier: GPL-2.0
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/peci/peci-bus.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Generic Device Tree Bindings for PECI bus
+
+maintainers:
+ - Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
+
+description: |
+ PECI (Platform Environment Control Interface) is a one-wire bus interface that
+ provides a communication channel from Intel processors and chipset components
+ to external monitoring or control devices. PECI is designed to support the
+ following sideband functions:
+
+ * Processor and DRAM thermal management
+ - Processor fan speed control is managed by comparing Digital Thermal
+ Sensor (DTS) thermal readings acquired via PECI against the
+ processor-specific fan speed control reference point, or TCONTROL. Both
+ TCONTROL and DTS thermal readings are accessible via the processor PECI
+ client. These variables are referenced to a common temperature, the TCC
+ activation point, and are both defined as negative offsets from that
+ reference.
+ - PECI based access to the processor package configuration space provides
+ a means for Baseboard Management Controllers (BMC) or other platform
+ management devices to actively manage the processor and memory power
+ and thermal features.
+
+ * Platform Manageability
+ - Platform manageability functions including thermal, power, and error
+ monitoring. Note that platform 'power' management includes monitoring
+ and control for both the processor and DRAM subsystem to assist with
+ data center power limiting.
+ - PECI allows read access to certain error registers in the processor MSR
+ space and status monitoring registers in the PCI configuration space
+ within the processor and downstream devices.
+ - PECI permits writes to certain registers in the processor PCI
+ configuration space.
+
+ * Processor Interface Tuning and Diagnostics
+ - Processor interface tuning and diagnostics capabilities
+ (Intel Interconnect BIST). The processors Intel Interconnect Built In
+ Self Test (Intel IBIST) allows for infield diagnostic capabilities in
+ the Intel UPI and memory controller interfaces. PECI provides a port to
+ execute these diagnostics via its PCI Configuration read and write
+ capabilities.
+
+ * Failure Analysis
+ - Output the state of the processor after a failure for analysis via
+ Crashdump.
+
+ PECI uses a single wire for self-clocking and data transfer. The bus
+ requires no additional control lines. The physical layer is a self-clocked
+ one-wire bus that begins each bit with a driven, rising edge from an idle
+ level near zero volts. The duration of the signal driven high depends on
+ whether the bit value is a logic '0' or logic '1'. PECI also includes
+ variable data transfer rate established with every message. In this way, it
+ is highly flexible even though underlying logic is simple.
+
+ The interface design was optimized for interfacing between an Intel
+ processor and chipset components in both single processor and multiple
+ processor environments. The single wire interface provides low board
+ routing overhead for the multiple load connections in the congested routing
+ area near the processor and chipset components. Bus speed, error checking,
+ and low protocol overhead provides adequate link bandwidth and reliability
+ to transfer critical device operating conditions and configuration
+ information.
+
+ PECI subsystem provides single or multiple bus nodes support so each bus can
+ have one adapter node and multiple device specific client nodes that can be
+ attached to the PECI bus so each processor client's features can be supported
+ by the client node through an adapter connection in the bus.
+
+properties:
+ compatible:
+ const: simple-bus
+
+ "#address-cells":
+ # Required to define bus device control resource address.
+ const: 1
+
+ "#size-cells":
+ # Required to define bus device control resource address.
+ const: 1
+
+ ranges: true
+
+required:
+ - compatible
+ - "#address-cells"
+ - "#size-cells"
+ - ranges
+
+examples:
+ - |
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+ #include <dt-bindings/clock/ast2600-clock.h>
+ peci: bus@1e78b000 {
+ compatible = "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges = <0x0 0x1e78b000 0x200>;
+
+ peci0: peci-bus@0 {
+ compatible = "aspeed,ast2600-peci";
+ reg = <0x0 0x100>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ interrupts = <GIC_SPI 38 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&syscon ASPEED_CLK_GATE_REF0CLK>;
+ resets = <&syscon ASPEED_RESET_PECI>;
+ clock-frequency = <24000000>;
+ };
+
+ // Just an example. ast2600 doesn't have a second PECI module actually.
+ peci1: peci-bus@100 {
+ compatible = "aspeed,ast2600-peci";
+ reg = <0x100 0x100>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ interrupts = <GIC_SPI 39 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&syscon ASPEED_CLK_GATE_REF0CLK>;
+ resets = <&syscon ASPEED_RESET_PECI>;
+ clock-frequency = <24000000>;
+ };
+ };
+...
diff --git a/Documentation/devicetree/bindings/peci/peci-client.yaml b/Documentation/devicetree/bindings/peci/peci-client.yaml
new file mode 100644
index 000000000000..fc7c4110e929
--- /dev/null
+++ b/Documentation/devicetree/bindings/peci/peci-client.yaml
@@ -0,0 +1,54 @@
+# SPDX-License-Identifier: GPL-2.0
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/peci/peci-client.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Generic Device Tree Bindings for PECI clients
+
+maintainers:
+ - Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
+
+properties:
+ compatible:
+ enum:
+ - intel,peci-client
+
+ reg:
+ description: |
+ Address of a client CPU. According to the PECI specification, client
+ addresses start from 0x30.
+ maxItems: 1
+
+examples:
+ - |
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+ #include <dt-bindings/clock/ast2600-clock.h>
+ peci: bus@1e78b000 {
+ compatible = "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges = <0x0 0x1e78b000 0x60>;
+
+ peci0: peci-bus@0 {
+ compatible = "aspeed,ast2600-peci";
+ reg = <0x0 0x100>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ interrupts = <GIC_SPI 38 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&syscon ASPEED_CLK_GATE_REF0CLK>;
+ resets = <&syscon ASPEED_RESET_PECI>;
+ clock-frequency = <24000000>;
+
+ peci-client@30 {
+ compatible = "intel,peci-client";
+ reg = <0x30>;
+ };
+
+ peci-client@31 {
+ compatible = "intel,peci-client";
+ reg = <0x31>;
+ };
+ };
+ };
+...
diff --git a/Documentation/devicetree/bindings/peci/peci-npcm.yaml b/Documentation/devicetree/bindings/peci/peci-npcm.yaml
new file mode 100644
index 000000000000..bcd5626e68e7
--- /dev/null
+++ b/Documentation/devicetree/bindings/peci/peci-npcm.yaml
@@ -0,0 +1,102 @@
+# SPDX-License-Identifier: GPL-2.0
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/peci/peci-npcm.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Nuvoton NPCM PECI Bus Device Tree Bindings
+
+maintainers:
+ - Tomer Maimon <tmaimon77@gmail.com>
+
+properties:
+ compatible:
+ const: nuvoton,npcm750-peci # for the NPCM7XX BMC.
+
+ reg:
+ maxItems: 1
+
+ "#address-cells":
+ # Required to define a client address.
+ const: 1
+
+ "#size-cells":
+ # Required to define a client address.
+ const: 0
+
+ interrupts:
+ maxItems: 1
+
+ clocks:
+ # PECI reference clock.
+ maxItems: 1
+
+ cmd-timeout-ms:
+ # Command timeout in units of ms.
+ allOf:
+ - $ref: /schemas/types.yaml#/definitions/uint32
+ - minimum: 1
+ maximum: 60000
+ default: 1000
+
+ pull-down:
+ description: |
+ Defines the PECI I/O internal pull down operation.
+ 0: pull down always enable
+ 1: pull down only during transactions.
+ 2: pull down always disable.
+ allOf:
+ - $ref: /schemas/types.yaml#/definitions/uint32
+ - minimum: 0
+ maximum: 2
+ default: 0
+
+ host-neg-bit-rate:
+ description: |
+ Define host negotiation bit rate divider.
+ the host negotiation bit rate calculate with formula:
+ clock frequency[Hz] / [4 x {host-neg-bit-rate + 1}]
+ allOf:
+ - $ref: /schemas/types.yaml#/definitions/uint32
+ - minimum: 7
+ maximum: 31
+ default: 15
+
+ high-volt-range:
+ description: |
+ Adapts PECI I/O interface to voltage range.
+ 0: PECI I/O interface voltage range of 0.8-1.06V (default)
+ 1: PECI I/O interface voltage range of 0.95-1.26V
+ type: boolean
+
+required:
+ - compatible
+ - reg
+ - "#address-cells"
+ - "#size-cells"
+ - interrupts
+ - clocks
+
+examples:
+ - |
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+ #include <dt-bindings/clock/nuvoton,npcm7xx-clock.h>
+ peci: bus@100000 {
+ compatible = "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges = <0x0 0x100000 0x200>;
+
+ peci0: peci-bus@0 {
+ compatible = "nuvoton,npcm750-peci";
+ reg = <0x0 0x200>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ interrupts = <GIC_SPI 6 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&clk NPCM7XX_CLK_APB3>;
+ cmd-timeout-ms = <1000>;
+ pull-down = <0>;
+ host-neg-bit-rate = <15>;
+ };
+ };
+...
diff --git a/Documentation/devicetree/bindings/soc/aspeed/xdma.yaml b/Documentation/devicetree/bindings/soc/aspeed/xdma.yaml
new file mode 100644
index 000000000000..495743581e2d
--- /dev/null
+++ b/Documentation/devicetree/bindings/soc/aspeed/xdma.yaml
@@ -0,0 +1,103 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/soc/aspeed/xdma.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Aspeed AST25XX and AST26XX XDMA Engine
+
+maintainers:
+ - Eddie James <eajames@linux.ibm.com>
+
+description: |
+ This binding describes the XDMA Engine embedded in the AST2500 and AST2600
+ SOCs. The XDMA engine can perform automatic DMA operations over PCI between
+ the SOC (acting as a BMC) and a host processor.
+
+properties:
+ compatible:
+ enum:
+ - aspeed,ast2500-xdma
+ - aspeed,ast2600-xdma
+
+ reg:
+ maxItems: 1
+
+ clocks:
+ maxItems: 1
+
+ resets:
+ minItems: 1
+ maxItems: 2
+
+ reset-names:
+ maxItems: 2
+ items:
+ - const: device
+ - const: root-complex
+
+ interrupts:
+ maxItems: 2
+ items:
+ - description: global interrupt for the XDMA engine
+ - description: PCI-E reset or PERST interrupt
+
+ aspeed,scu:
+ description: a reference to the System Control Unit node of the Aspeed SOC.
+ allOf:
+ - $ref: /schemas/types.yaml#/definitions/phandle
+
+ aspeed,pcie-device:
+ description: describes which PCI-E device the XDMA engine should use
+ allOf:
+ - $ref: /schemas/types.yaml#/definitions/string
+ - enum: [ bmc, vga ]
+
+required:
+ - compatible
+ - reg
+ - clocks
+ - resets
+ - interrupts-extended
+ - aspeed,scu
+ - memory-region
+
+if:
+ properties:
+ compatible:
+ contains:
+ const: aspeed,ast2600-xdma
+then:
+ required:
+ - reset-names
+
+examples:
+ - |
+ #include <dt-bindings/clock/ast2600-clock.h>
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+ #include <dt-bindings/interrupt-controller/aspeed-scu-ic.h>
+ syscon: syscon@1e6e2000 {
+ reg = <0x1e6e2000 0x1000>;
+ ranges = <0 0x1e6e2000 0x1000>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ #clock-cells = <1>;
+ #reset-cells = <1>;
+ scu_ic0: interrupt-controller@560 {
+ reg = <0x560 0x4>;
+ interrupt-controller;
+ #interrupt-cells = <1>;
+ };
+ };
+ xdma@1e6e7000 {
+ compatible = "aspeed,ast2600-xdma";
+ reg = <0x1e6e7000 0x100>;
+ clocks = <&syscon ASPEED_CLK_GATE_BCLK>;
+ resets = <&syscon ASPEED_RESET_DEV_XDMA>, <&syscon ASPEED_RESET_RC_XDMA>;
+ reset-names = "device", "root-complex";
+ interrupts-extended = <&gic GIC_SPI 6 IRQ_TYPE_LEVEL_HIGH>,
+ <&scu_ic0 ASPEED_AST2600_SCU_IC0_PCIE_PERST_LO_TO_HI>;
+ aspeed,scu = <&syscon>;
+ aspeed,pcie-device = "bmc";
+ memory-region = <&vga_memory>;
+ };
diff --git a/Documentation/hwmon/index.rst b/Documentation/hwmon/index.rst
index 55ff4b7c5349..89e1a824021f 100644
--- a/Documentation/hwmon/index.rst
+++ b/Documentation/hwmon/index.rst
@@ -140,6 +140,8 @@ Hardware Monitoring Kernel Drivers
pc87360
pc87427
pcf8591
+ peci-cputemp
+ peci-dimmtemp
pmbus
powr1220
pxe1610
diff --git a/Documentation/hwmon/peci-cputemp.rst b/Documentation/hwmon/peci-cputemp.rst
new file mode 100644
index 000000000000..bf08e16dd989
--- /dev/null
+++ b/Documentation/hwmon/peci-cputemp.rst
@@ -0,0 +1,95 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+Kernel driver peci-cputemp
+==========================
+
+:Copyright: |copy| 2018-2019 Intel Corporation
+
+Supported chips:
+ One of Intel server CPUs listed below which is connected to a PECI bus.
+ * Intel Xeon E5/E7 v3 server processors
+ Intel Xeon E5-14xx v3 family
+ Intel Xeon E5-24xx v3 family
+ Intel Xeon E5-16xx v3 family
+ Intel Xeon E5-26xx v3 family
+ Intel Xeon E5-46xx v3 family
+ Intel Xeon E7-48xx v3 family
+ Intel Xeon E7-88xx v3 family
+ * Intel Xeon E5/E7 v4 server processors
+ Intel Xeon E5-16xx v4 family
+ Intel Xeon E5-26xx v4 family
+ Intel Xeon E5-46xx v4 family
+ Intel Xeon E7-48xx v4 family
+ Intel Xeon E7-88xx v4 family
+ * Intel Xeon Scalable server processors
+ Intel Xeon D family
+ Intel Xeon Bronze family
+ Intel Xeon Silver family
+ Intel Xeon Gold family
+ Intel Xeon Platinum family
+
+ Addresses scanned: PECI client address 0x30 - 0x37
+ Datasheet: Available from http://www.intel.com/design/literature.htm
+
+Author:
+ Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
+
+Description
+-----------
+
+This driver implements a generic PECI hwmon feature which provides Digital
+Thermal Sensor (DTS) thermal readings of the CPU package and CPU cores that are
+accessible using the PECI Client Command Suite via the processor PECI client.
+
+All temperature values are given in millidegree Celsius and will be measurable
+only when the target CPU is powered on.
+
+``sysfs`` interface
+-------------------
+======================= =======================================================
+temp1_label "Die"
+temp1_input Provides current die temperature of the CPU package.
+temp1_max Provides thermal control temperature of the CPU package
+ which is also known as Tcontrol.
+temp1_crit Provides shutdown temperature of the CPU package which
+ is also known as the maximum processor junction
+ temperature, Tjmax or Tprochot.
+temp1_crit_hyst Provides the hysteresis value from Tcontrol to Tjmax of
+ the CPU package.
+
+temp2_label "DTS"
+temp2_input Provides current DTS temperature of the CPU package.
+temp2_max Provides thermal control temperature of the CPU package
+ which is also known as Tcontrol.
+temp2_crit Provides shutdown temperature of the CPU package which
+ is also known as the maximum processor junction
+ temperature, Tjmax or Tprochot.
+temp2_crit_hyst Provides the hysteresis value from Tcontrol to Tjmax of
+ the CPU package.
+
+temp3_label "Tcontrol"
+temp3_input Provides current Tcontrol temperature of the CPU
+ package which is also known as Fan Temperature target.
+ Indicates the relative value from thermal monitor trip
+ temperature at which fans should be engaged.
+temp3_crit Provides Tcontrol critical value of the CPU package
+ which is same to Tjmax.
+
+temp4_label "Tthrottle"
+temp4_input Provides current Tthrottle temperature of the CPU
+ package. Used for throttling temperature. If this value
+ is allowed and lower than Tjmax - the throttle will
+ occur and reported at lower than Tjmax.
+
+temp5_label "Tjmax"
+temp5_input Provides the maximum junction temperature, Tjmax of the
+ CPU package.
+
+temp[6-N]_label Provides string "Core X", where X is resolved core
+ number.
+temp[6-N]_input Provides current temperature of each core.
+temp[6-N]_max Provides thermal control temperature of the core.
+temp[6-N]_crit Provides shutdown temperature of the core.
+temp[6-N]_crit_hyst Provides the hysteresis value from Tcontrol to Tjmax of
+ the core.
+======================= ======================================================= \ No newline at end of file
diff --git a/Documentation/hwmon/peci-dimmtemp.rst b/Documentation/hwmon/peci-dimmtemp.rst
new file mode 100644
index 000000000000..e3581811de2d
--- /dev/null
+++ b/Documentation/hwmon/peci-dimmtemp.rst
@@ -0,0 +1,60 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+Kernel driver peci-dimmtemp
+===========================
+
+:Copyright: |copy| 2018-2019 Intel Corporation
+
+Supported chips:
+ One of Intel server CPUs listed below which is connected to a PECI bus.
+ * Intel Xeon E5/E7 v3 server processors
+ Intel Xeon E5-14xx v3 family
+ Intel Xeon E5-24xx v3 family
+ Intel Xeon E5-16xx v3 family
+ Intel Xeon E5-26xx v3 family
+ Intel Xeon E5-46xx v3 family
+ Intel Xeon E7-48xx v3 family
+ Intel Xeon E7-88xx v3 family
+ * Intel Xeon E5/E7 v4 server processors
+ Intel Xeon E5-16xx v4 family
+ Intel Xeon E5-26xx v4 family
+ Intel Xeon E5-46xx v4 family
+ Intel Xeon E7-48xx v4 family
+ Intel Xeon E7-88xx v4 family
+ * Intel Xeon Scalable server processors
+ Intel Xeon D family
+ Intel Xeon Bronze family
+ Intel Xeon Silver family
+ Intel Xeon Gold family
+ Intel Xeon Platinum family
+
+ Addresses scanned: PECI client address 0x30 - 0x37
+ Datasheet: Available from http://www.intel.com/design/literature.htm
+
+Author:
+ Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
+
+Description
+-----------
+
+This driver implements a generic PECI hwmon feature which provides Digital
+Thermal Sensor (DTS) thermal readings of DIMM components that are accessible
+using the PECI Client Command Suite via the processor PECI client.
+
+All temperature values are given in millidegree Celsius and will be measurable
+only when the target CPU is powered on.
+
+``sysfs`` interface
+-------------------
+======================= =======================================================
+
+temp[N]_label Provides string "DIMM CI", where C is DIMM channel and
+ I is DIMM index of the populated DIMM.
+temp[N]_input Provides current temperature of the populated DIMM.
+temp[N]_max Provides thermal control temperature of the DIMM.
+temp[N]_crit Provides shutdown temperature of the DIMM.
+======================= =======================================================
+
+Note:
+ DIMM temperature attributes will appear when the client CPU's BIOS
+ completes memory training and testing.
diff --git a/Documentation/userspace-api/ioctl/ioctl-number.rst b/Documentation/userspace-api/ioctl/ioctl-number.rst
index 59472cd6a11d..d1d628347906 100644
--- a/Documentation/userspace-api/ioctl/ioctl-number.rst
+++ b/Documentation/userspace-api/ioctl/ioctl-number.rst
@@ -339,6 +339,8 @@ Code Seq# Include File Comments
0xB4 00-0F linux/gpio.h <mailto:linux-gpio@vger.kernel.org>
0xB5 00-0F uapi/linux/rpmsg.h <mailto:linux-remoteproc@vger.kernel.org>
0xB6 all linux/fpga-dfl.h
+0xB7 all uapi/linux/peci-ioctl.h PECI subsystem
+ <mailto:jae.hyun.yoo@linux.intel.com>
0xC0 00-0F linux/usb/iowarrior.h
0xCA 00-0F uapi/misc/cxl.h
0xCA 10-2F uapi/misc/ocxl.h
diff --git a/MAINTAINERS b/MAINTAINERS
index 4e2698cc7e23..caffdd17b36e 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2808,6 +2808,14 @@ S: Maintained
F: Documentation/devicetree/bindings/media/aspeed-video.txt
F: drivers/media/platform/aspeed-video.c
+ASPEED XDMA ENGINE DRIVER
+M: Eddie James <eajames@linux.ibm.com>
+L: linux-aspeed@lists.ozlabs.org (moderated for non-subscribers)
+S: Maintained
+F: Documentation/devicetree/bindings/soc/aspeed/xdma.yaml
+F: drivers/soc/aspeed/aspeed-xdma.c
+F: include/uapi/linux/aspeed-xdma.h
+
ASUS NOTEBOOKS AND EEEPC ACPI/WMI EXTRAS DRIVERS
M: Corentin Chary <corentin.chary@gmail.com>
L: acpi4asus-user@lists.sourceforge.net
@@ -6208,6 +6216,12 @@ L: linux-edac@vger.kernel.org
S: Maintained
F: drivers/edac/mpc85xx_edac.[ch]
+EDAC-NPCM7XX
+M: George Hung <george.hung@quantatw.com>
+S: Maintained
+F: drivers/edac/npcm7xx_edac.c
+F: Documentation/devicetree/bindings/edac/npcm7xx-sdram-edac.txt
+
EDAC-PASEMI
M: Egor Martovetsky <egor@pasemi.com>
L: linux-edac@vger.kernel.org
diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile
index e6a1cac0bfc7..56fd8c8a2ba9 100644
--- a/arch/arm/boot/dts/Makefile
+++ b/arch/arm/boot/dts/Makefile
@@ -337,7 +337,9 @@ dtb-$(CONFIG_ARCH_LPC32XX) += \
lpc3250-ea3250.dtb \
lpc3250-phy3250.dtb
dtb-$(CONFIG_ARCH_NPCM7XX) += \
- nuvoton-npcm750-evb.dtb
+ nuvoton-npcm730-gsj.dtb \
+ nuvoton-npcm750-evb.dtb \
+ nuvoton-npcm750-runbmc-olympus.dtb
dtb-$(CONFIG_MACH_MESON6) += \
meson6-atv1200.dtb
dtb-$(CONFIG_MACH_MESON8) += \
@@ -1346,6 +1348,7 @@ dtb-$(CONFIG_ARCH_ZX) += zx296702-ad1.dtb
dtb-$(CONFIG_ARCH_ASPEED) += \
aspeed-ast2500-evb.dtb \
aspeed-ast2600-evb.dtb \
+ aspeed-bmc-amd-ethanolx.dtb \
aspeed-bmc-arm-centriq2400-rep.dtb \
aspeed-bmc-arm-stardragon4800-rep2.dtb \
aspeed-bmc-facebook-cmm.dtb \
@@ -1370,6 +1373,7 @@ dtb-$(CONFIG_ARCH_ASPEED) += \
aspeed-bmc-opp-tacoma.dtb \
aspeed-bmc-opp-vesnin.dtb \
aspeed-bmc-opp-witherspoon.dtb \
+ aspeed-bmc-opp-witherspoon-128.dtb \
aspeed-bmc-opp-zaius.dtb \
aspeed-bmc-portwell-neptune.dtb \
aspeed-bmc-quanta-q71l.dtb
diff --git a/arch/arm/boot/dts/aspeed-ast2600-evb.dts b/arch/arm/boot/dts/aspeed-ast2600-evb.dts
index 8d0f4656aa05..89be13197780 100644
--- a/arch/arm/boot/dts/aspeed-ast2600-evb.dts
+++ b/arch/arm/boot/dts/aspeed-ast2600-evb.dts
@@ -23,6 +23,15 @@
};
};
+&mdio0 {
+ status = "okay";
+
+ ethphy0: ethernet-phy@0 {
+ compatible = "ethernet-phy-ieee802.3-c22";
+ reg = <0>;
+ };
+};
+
&mdio1 {
status = "okay";
@@ -50,6 +59,17 @@
};
};
+&mac0 {
+ status = "okay";
+
+ phy-mode = "rgmii";
+ phy-handle = <&ethphy0>;
+
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_rgmii1_default>;
+};
+
+
&mac1 {
status = "okay";
diff --git a/arch/arm/boot/dts/aspeed-bmc-amd-ethanolx.dts b/arch/arm/boot/dts/aspeed-bmc-amd-ethanolx.dts
new file mode 100644
index 000000000000..60ba86f3e5bc
--- /dev/null
+++ b/arch/arm/boot/dts/aspeed-bmc-amd-ethanolx.dts
@@ -0,0 +1,219 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2020 AMD Inc.
+// Author: Supreeth Venkatesh <supreeth.venkatesh@amd.com>
+/dts-v1/;
+
+#include "aspeed-g5.dtsi"
+#include <dt-bindings/gpio/aspeed-gpio.h>
+
+/ {
+ model = "AMD EthanolX BMC";
+ compatible = "amd,ethanolx-bmc", "aspeed,ast2500";
+
+ memory@80000000 {
+ reg = <0x80000000 0x20000000>;
+ };
+ aliases {
+ serial0 = &uart1;
+ serial4 = &uart5;
+ };
+ chosen {
+ stdout-path = &uart5;
+ bootargs = "console=ttyS4,115200 earlyprintk";
+ };
+ leds {
+ compatible = "gpio-leds";
+
+ fault {
+ gpios = <&gpio ASPEED_GPIO(A, 2) GPIO_ACTIVE_LOW>;
+ };
+
+ identify {
+ gpios = <&gpio ASPEED_GPIO(A, 3) GPIO_ACTIVE_LOW>;
+ };
+ };
+ iio-hwmon {
+ compatible = "iio-hwmon";
+ io-channels = <&adc 0>, <&adc 1>, <&adc 2>, <&adc 3>, <&adc 4>;
+ };
+};
+
+&fmc {
+ status = "okay";
+ flash@0 {
+ status = "okay";
+ m25p,fast-read;
+ #include "openbmc-flash-layout.dtsi"
+ };
+};
+
+
+&mac0 {
+ status = "okay";
+
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_rmii1_default>;
+ clocks = <&syscon ASPEED_CLK_GATE_MAC1CLK>,
+ <&syscon ASPEED_CLK_MAC1RCLK>;
+ clock-names = "MACCLK", "RCLK";
+};
+
+&uart1 {
+ //Host Console
+ status = "okay";
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_txd1_default
+ &pinctrl_rxd1_default>;
+};
+
+&uart5 {
+ //BMC Console
+ status = "okay";
+};
+
+&adc {
+ status = "okay";
+
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_adc0_default
+ &pinctrl_adc1_default
+ &pinctrl_adc2_default
+ &pinctrl_adc3_default
+ &pinctrl_adc4_default>;
+};
+
+//APML for P0
+&i2c0 {
+ status = "okay";
+};
+
+//APML for P1
+&i2c1 {
+ status = "okay";
+};
+
+// Thermal Sensors
+&i2c7 {
+ status = "okay";
+
+ lm75a@48 {
+ compatible = "national,lm75a";
+ reg = <0x48>;
+ };
+
+ lm75a@49 {
+ compatible = "national,lm75a";
+ reg = <0x49>;
+ };
+
+ lm75a@4a {
+ compatible = "national,lm75a";
+ reg = <0x4a>;
+ };
+
+ lm75a@4b {
+ compatible = "national,lm75a";
+ reg = <0x4b>;
+ };
+
+ lm75a@4c {
+ compatible = "national,lm75a";
+ reg = <0x4c>;
+ };
+
+ lm75a@4d {
+ compatible = "national,lm75a";
+ reg = <0x4d>;
+ };
+
+ lm75a@4e {
+ compatible = "national,lm75a";
+ reg = <0x4e>;
+ };
+
+ lm75a@4f {
+ compatible = "national,lm75a";
+ reg = <0x4f>;
+ };
+};
+
+&kcs1 {
+ status = "okay";
+ kcs_addr = <0x60>;
+};
+
+&kcs2 {
+ status = "okay";
+ kcs_addr = <0x62>;
+};
+
+&kcs4 {
+ status = "okay";
+ kcs_addr = <0x97DE>;
+};
+
+&lpc_snoop {
+ status = "okay";
+ snoop-ports = <0x80>;
+};
+
+&lpc_ctrl {
+ //Enable lpc clock
+ status = "okay";
+};
+
+&pwm_tacho {
+ status = "okay";
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_pwm0_default
+ &pinctrl_pwm1_default
+ &pinctrl_pwm2_default
+ &pinctrl_pwm3_default
+ &pinctrl_pwm4_default
+ &pinctrl_pwm5_default
+ &pinctrl_pwm6_default
+ &pinctrl_pwm7_default>;
+
+ fan@0 {
+ reg = <0x00>;
+ aspeed,fan-tach-ch = /bits/ 8 <0x00>;
+ };
+
+ fan@1 {
+ reg = <0x01>;
+ aspeed,fan-tach-ch = /bits/ 8 <0x01>;
+ };
+
+ fan@2 {
+ reg = <0x02>;
+ aspeed,fan-tach-ch = /bits/ 8 <0x02>;
+ };
+
+ fan@3 {
+ reg = <0x03>;
+ aspeed,fan-tach-ch = /bits/ 8 <0x03>;
+ };
+
+ fan@4 {
+ reg = <0x04>;
+ aspeed,fan-tach-ch = /bits/ 8 <0x04>;
+ };
+
+ fan@5 {
+ reg = <0x05>;
+ aspeed,fan-tach-ch = /bits/ 8 <0x05>;
+ };
+
+ fan@6 {
+ reg = <0x06>;
+ aspeed,fan-tach-ch = /bits/ 8 <0x06>;
+ };
+
+ fan@7 {
+ reg = <0x07>;
+ aspeed,fan-tach-ch = /bits/ 8 <0x07>;
+ };
+};
+
+
+
diff --git a/arch/arm/boot/dts/aspeed-bmc-facebook-cmm.dts b/arch/arm/boot/dts/aspeed-bmc-facebook-cmm.dts
index 016bbcb99bb6..7bc7df7ed428 100644
--- a/arch/arm/boot/dts/aspeed-bmc-facebook-cmm.dts
+++ b/arch/arm/boot/dts/aspeed-bmc-facebook-cmm.dts
@@ -19,8 +19,8 @@
serial3 = &uart4;
/*
- * Hardcode the bus number of i2c switches' channels to
- * avoid breaking the legacy applications.
+ * PCA9548 (1-0077) provides 8 channels for connecting to
+ * 4 Line Cards and 4 Fabric Cards.
*/
i2c16 = &imux16;
i2c17 = &imux17;
@@ -30,6 +30,11 @@
i2c21 = &imux21;
i2c22 = &imux22;
i2c23 = &imux23;
+
+ /*
+ * PCA9548 (2-0071) provides 8 channels for connecting to
+ * Power Distribution Board.
+ */
i2c24 = &imux24;
i2c25 = &imux25;
i2c26 = &imux26;
@@ -38,6 +43,11 @@
i2c29 = &imux29;
i2c30 = &imux30;
i2c31 = &imux31;
+
+ /*
+ * PCA9548 (8-0077) provides 8 channels and the first 4
+ * channels are connecting to 4 Fan Control Boards.
+ */
i2c32 = &imux32;
i2c33 = &imux33;
i2c34 = &imux34;
@@ -46,6 +56,226 @@
i2c37 = &imux37;
i2c38 = &imux38;
i2c39 = &imux39;
+
+ /*
+ * 2 PCA9548 (18-0070 & 18-0073), 16 channels connecting
+ * to Line Card #1.
+ */
+ i2c40 = &imux40;
+ i2c41 = &imux41;
+ i2c42 = &imux42;
+ i2c43 = &imux43;
+ i2c44 = &imux44;
+ i2c45 = &imux45;
+ i2c46 = &imux46;
+ i2c47 = &imux47;
+ i2c48 = &imux48;
+ i2c49 = &imux49;
+ i2c50 = &imux50;
+ i2c51 = &imux51;
+ i2c52 = &imux52;
+ i2c53 = &imux53;
+ i2c54 = &imux54;
+ i2c55 = &imux55;
+
+ /*
+ * 2 PCA9548 (19-0070 & 19-0073), 16 channels connecting
+ * to Line Card #2.
+ */
+ i2c56 = &imux56;
+ i2c57 = &imux57;
+ i2c58 = &imux58;
+ i2c59 = &imux59;
+ i2c60 = &imux60;
+ i2c61 = &imux61;
+ i2c62 = &imux62;
+ i2c63 = &imux63;
+ i2c64 = &imux64;
+ i2c65 = &imux65;
+ i2c66 = &imux66;
+ i2c67 = &imux67;
+ i2c68 = &imux68;
+ i2c69 = &imux69;
+ i2c70 = &imux70;
+ i2c71 = &imux71;
+
+ /*
+ * 2 PCA9548 (20-0070 & 20-0073), 16 channels connecting
+ * to Line Card #3.
+ */
+ i2c72 = &imux72;
+ i2c73 = &imux73;
+ i2c74 = &imux74;
+ i2c75 = &imux75;
+ i2c76 = &imux76;
+ i2c77 = &imux77;
+ i2c78 = &imux78;
+ i2c79 = &imux79;
+ i2c80 = &imux80;
+ i2c81 = &imux81;
+ i2c82 = &imux82;
+ i2c83 = &imux83;
+ i2c84 = &imux84;
+ i2c85 = &imux85;
+ i2c86 = &imux86;
+ i2c87 = &imux87;
+
+ /*
+ * 2 PCA9548 (21-0070 & 21-0073), 16 channels connecting
+ * to Line Card #4.
+ */
+ i2c88 = &imux88;
+ i2c89 = &imux89;
+ i2c90 = &imux90;
+ i2c91 = &imux91;
+ i2c92 = &imux92;
+ i2c93 = &imux93;
+ i2c94 = &imux94;
+ i2c95 = &imux95;
+ i2c96 = &imux96;
+ i2c97 = &imux97;
+ i2c98 = &imux98;
+ i2c99 = &imux99;
+ i2c100 = &imux100;
+ i2c101 = &imux101;
+ i2c102 = &imux102;
+ i2c103 = &imux103;
+
+ /*
+ * 2 PCA9548 (16-0070 & 16-0073), 16 channels connecting
+ * to Fabric Card #1.
+ */
+ i2c104 = &imux104;
+ i2c105 = &imux105;
+ i2c106 = &imux106;
+ i2c107 = &imux107;
+ i2c108 = &imux108;
+ i2c109 = &imux109;
+ i2c110 = &imux110;
+ i2c111 = &imux111;
+ i2c112 = &imux112;
+ i2c113 = &imux113;
+ i2c114 = &imux114;
+ i2c115 = &imux115;
+ i2c116 = &imux116;
+ i2c117 = &imux117;
+ i2c118 = &imux118;
+ i2c119 = &imux119;
+
+ /*
+ * 2 PCA9548 (17-0070 & 17-0073), 16 channels connecting
+ * to Fabric Card #2.
+ */
+ i2c120 = &imux120;
+ i2c121 = &imux121;
+ i2c122 = &imux122;
+ i2c123 = &imux123;
+ i2c124 = &imux124;
+ i2c125 = &imux125;
+ i2c126 = &imux126;
+ i2c127 = &imux127;
+ i2c128 = &imux128;
+ i2c129 = &imux129;
+ i2c130 = &imux130;
+ i2c131 = &imux131;
+ i2c132 = &imux132;
+ i2c133 = &imux133;
+ i2c134 = &imux134;
+ i2c135 = &imux135;
+
+ /*
+ * 2 PCA9548 (22-0070 & 22-0073), 16 channels connecting
+ * to Fabric Card #3.
+ */
+ i2c136 = &imux136;
+ i2c137 = &imux137;
+ i2c138 = &imux138;
+ i2c139 = &imux139;
+ i2c140 = &imux140;
+ i2c141 = &imux141;
+ i2c142 = &imux142;
+ i2c143 = &imux143;
+ i2c144 = &imux144;
+ i2c145 = &imux145;
+ i2c146 = &imux146;
+ i2c147 = &imux147;
+ i2c148 = &imux148;
+ i2c149 = &imux149;
+ i2c150 = &imux150;
+ i2c151 = &imux151;
+
+ /*
+ * 2 PCA9548 (23-0070 & 23-0073), 16 channels connecting
+ * to Fabric Card #4.
+ */
+ i2c152 = &imux152;
+ i2c153 = &imux153;
+ i2c154 = &imux154;
+ i2c155 = &imux155;
+ i2c156 = &imux156;
+ i2c157 = &imux157;
+ i2c158 = &imux158;
+ i2c159 = &imux159;
+ i2c160 = &imux160;
+ i2c161 = &imux161;
+ i2c162 = &imux162;
+ i2c163 = &imux163;
+ i2c164 = &imux164;
+ i2c165 = &imux165;
+ i2c166 = &imux166;
+ i2c167 = &imux167;
+
+ /*
+ * PCA9548 (32-0070), 8 channels connecting to Fan Control
+ # Board #1.
+ */
+ i2c168 = &imux168;
+ i2c169 = &imux169;
+ i2c170 = &imux170;
+ i2c171 = &imux171;
+ i2c172 = &imux172;
+ i2c173 = &imux173;
+ i2c174 = &imux174;
+ i2c175 = &imux175;
+
+ /*
+ * PCA9548 (33-0070), 8 channels connecting to Fan Control
+ # Board #2.
+ */
+ i2c176 = &imux176;
+ i2c177 = &imux177;
+ i2c178 = &imux178;
+ i2c179 = &imux179;
+ i2c180 = &imux180;
+ i2c181 = &imux181;
+ i2c182 = &imux182;
+ i2c183 = &imux183;
+
+ /*
+ * PCA9548 (34-0070), 8 channels connecting to Fan Control
+ # Board #3.
+ */
+ i2c184 = &imux184;
+ i2c185 = &imux185;
+ i2c186 = &imux186;
+ i2c187 = &imux187;
+ i2c188 = &imux188;
+ i2c189 = &imux189;
+ i2c190 = &imux190;
+ i2c191 = &imux191;
+
+ /*
+ * PCA9548 (35-0070), 8 channels connecting to Fan Control
+ # Board #4.
+ */
+ i2c192 = &imux192;
+ i2c193 = &imux193;
+ i2c194 = &imux194;
+ i2c195 = &imux195;
+ i2c196 = &imux196;
+ i2c197 = &imux197;
+ i2c198 = &imux198;
+ i2c199 = &imux199;
};
chosen {
@@ -103,53 +333,846 @@
#address-cells = <1>;
#size-cells = <0>;
reg = <0x77>;
+ i2c-mux-idle-disconnect;
+ /* To Fabric Card #1 */
imux16: i2c@0 {
#address-cells = <1>;
#size-cells = <0>;
reg = <0>;
+
+ i2c-switch@70 {
+ compatible = "nxp,pca9548";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0x70>;
+ i2c-mux-idle-disconnect;
+
+ imux104: i2c@0 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0>;
+ };
+ imux105: i2c@1 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <1>;
+ };
+ imux106: i2c@2 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <2>;
+ };
+ imux107: i2c@3 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <3>;
+ };
+ imux108: i2c@4 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <4>;
+ };
+ imux109: i2c@5 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <5>;
+ };
+ imux110: i2c@6 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <6>;
+ };
+ imux111: i2c@7 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <7>;
+ };
+ };
+
+ i2c-switch@73 {
+ compatible = "nxp,pca9548";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0x73>;
+ i2c-mux-idle-disconnect;
+
+ imux112: i2c@0 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0>;
+ };
+ imux113: i2c@1 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <1>;
+ };
+ imux114: i2c@2 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <2>;
+ };
+ imux115: i2c@3 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <3>;
+ };
+ imux116: i2c@4 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <4>;
+ };
+ imux117: i2c@5 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <5>;
+ };
+ imux118: i2c@6 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <6>;
+ };
+ imux119: i2c@7 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <7>;
+ };
+ };
};
+ /* To Fabric Card #2 */
imux17: i2c@1 {
#address-cells = <1>;
#size-cells = <0>;
reg = <1>;
+
+ i2c-switch@70 {
+ compatible = "nxp,pca9548";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0x70>;
+ i2c-mux-idle-disconnect;
+
+ imux120: i2c@0 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0>;
+ };
+ imux121: i2c@1 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <1>;
+ };
+ imux122: i2c@2 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <2>;
+ };
+ imux123: i2c@3 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <3>;
+ };
+ imux124: i2c@4 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <4>;
+ };
+ imux125: i2c@5 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <5>;
+ };
+ imux126: i2c@6 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <6>;
+ };
+ imux127: i2c@7 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <7>;
+ };
+ };
+
+ i2c-switch@73 {
+ compatible = "nxp,pca9548";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0x73>;
+ i2c-mux-idle-disconnect;
+
+ imux128: i2c@0 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0>;
+ };
+ imux129: i2c@1 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <1>;
+ };
+ imux130: i2c@2 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <2>;
+ };
+ imux131: i2c@3 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <3>;
+ };
+ imux132: i2c@4 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <4>;
+ };
+ imux133: i2c@5 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <5>;
+ };
+ imux134: i2c@6 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <6>;
+ };
+ imux135: i2c@7 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <7>;
+ };
+ };
};
+ /* To Line Card #1 */
imux18: i2c@2 {
#address-cells = <1>;
#size-cells = <0>;
reg = <2>;
+
+ i2c-switch@70 {
+ compatible = "nxp,pca9548";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0x70>;
+ i2c-mux-idle-disconnect;
+
+ imux40: i2c@0 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0>;
+ };
+ imux41: i2c@1 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <1>;
+ };
+ imux42: i2c@2 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <2>;
+ };
+ imux43: i2c@3 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <3>;
+ };
+ imux44: i2c@4 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <4>;
+ };
+ imux45: i2c@5 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <5>;
+ };
+ imux46: i2c@6 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <6>;
+ };
+ imux47: i2c@7 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <7>;
+ };
+ };
+
+ i2c-switch@73 {
+ compatible = "nxp,pca9548";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0x73>;
+ i2c-mux-idle-disconnect;
+
+ imux48: i2c@0 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0>;
+ };
+ imux49: i2c@1 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <1>;
+ };
+ imux50: i2c@2 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <2>;
+ };
+ imux51: i2c@3 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <3>;
+ };
+ imux52: i2c@4 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <4>;
+ };
+ imux53: i2c@5 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <5>;
+ };
+ imux54: i2c@6 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <6>;
+ };
+ imux55: i2c@7 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <7>;
+ };
+ };
};
+ /* To Line Card #2 */
imux19: i2c@3 {
#address-cells = <1>;
#size-cells = <0>;
reg = <3>;
+
+ i2c-switch@70 {
+ compatible = "nxp,pca9548";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0x70>;
+ i2c-mux-idle-disconnect;
+
+ imux56: i2c@0 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0>;
+ };
+ imux57: i2c@1 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <1>;
+ };
+ imux58: i2c@2 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <2>;
+ };
+ imux59: i2c@3 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <3>;
+ };
+ imux60: i2c@4 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <4>;
+ };
+ imux61: i2c@5 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <5>;
+ };
+ imux62: i2c@6 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <6>;
+ };
+ imux63: i2c@7 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <7>;
+ };
+ };
+
+ i2c-switch@73 {
+ compatible = "nxp,pca9548";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0x73>;
+ i2c-mux-idle-disconnect;
+
+ imux64: i2c@0 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0>;
+ };
+ imux65: i2c@1 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <1>;
+ };
+ imux66: i2c@2 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <2>;
+ };
+ imux67: i2c@3 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <3>;
+ };
+ imux68: i2c@4 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <4>;
+ };
+ imux69: i2c@5 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <5>;
+ };
+ imux70: i2c@6 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <6>;
+ };
+ imux71: i2c@7 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <7>;
+ };
+ };
};
+ /* To LC3 SCM */
imux20: i2c@4 {
#address-cells = <1>;
#size-cells = <0>;
reg = <4>;
+
+ i2c-switch@70 {
+ compatible = "nxp,pca9548";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0x70>;
+ i2c-mux-idle-disconnect;
+
+ imux72: i2c@0 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0>;
+ };
+ imux73: i2c@1 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <1>;
+ };
+ imux74: i2c@2 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <2>;
+ };
+ imux75: i2c@3 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <3>;
+ };
+ imux76: i2c@4 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <4>;
+ };
+ imux77: i2c@5 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <5>;
+ };
+ imux78: i2c@6 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <6>;
+ };
+ imux79: i2c@7 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <7>;
+ };
+ };
+
+ i2c-switch@73 {
+ compatible = "nxp,pca9548";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0x73>;
+ i2c-mux-idle-disconnect;
+
+ imux80: i2c@0 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0>;
+ };
+ imux81: i2c@1 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <1>;
+ };
+ imux82: i2c@2 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <2>;
+ };
+ imux83: i2c@3 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <3>;
+ };
+ imux84: i2c@4 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <4>;
+ };
+ imux85: i2c@5 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <5>;
+ };
+ imux86: i2c@6 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <6>;
+ };
+ imux87: i2c@7 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <7>;
+ };
+ };
};
+ /* To Line Card #4 */
imux21: i2c@5 {
#address-cells = <1>;
#size-cells = <0>;
reg = <5>;
+
+ i2c-switch@70 {
+ compatible = "nxp,pca9548";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0x70>;
+ i2c-mux-idle-disconnect;
+
+ imux88: i2c@0 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0>;
+ };
+ imux89: i2c@1 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <1>;
+ };
+ imux90: i2c@2 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <2>;
+ };
+ imux91: i2c@3 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <3>;
+ };
+ imux92: i2c@4 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <4>;
+ };
+ imux93: i2c@5 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <5>;
+ };
+ imux94: i2c@6 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <6>;
+ };
+ imux95: i2c@7 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <7>;
+ };
+ };
+
+ i2c-switch@73 {
+ compatible = "nxp,pca9548";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0x73>;
+ i2c-mux-idle-disconnect;
+
+ imux96: i2c@0 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0>;
+ };
+ imux97: i2c@1 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <1>;
+ };
+ imux98: i2c@2 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <2>;
+ };
+ imux99: i2c@3 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <3>;
+ };
+ imux100: i2c@4 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <4>;
+ };
+ imux101: i2c@5 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <5>;
+ };
+ imux102: i2c@6 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <6>;
+ };
+ imux103: i2c@7 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <7>;
+ };
+ };
};
+ /* To Fabric Card #3 */
imux22: i2c@6 {
#address-cells = <1>;
#size-cells = <0>;
reg = <6>;
+
+ i2c-switch@70 {
+ compatible = "nxp,pca9548";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0x70>;
+ i2c-mux-idle-disconnect;
+
+ imux136: i2c@0 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0>;
+ };
+ imux137: i2c@1 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <1>;
+ };
+ imux138: i2c@2 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <2>;
+ };
+ imux139: i2c@3 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <3>;
+ };
+ imux140: i2c@4 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <4>;
+ };
+ imux141: i2c@5 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <5>;
+ };
+ imux142: i2c@6 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <6>;
+ };
+ imux143: i2c@7 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <7>;
+ };
+ };
+
+ i2c-switch@73 {
+ compatible = "nxp,pca9548";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0x73>;
+ i2c-mux-idle-disconnect;
+
+ imux144: i2c@0 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0>;
+ };
+ imux145: i2c@1 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <1>;
+ };
+ imux146: i2c@2 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <2>;
+ };
+ imux147: i2c@3 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <3>;
+ };
+ imux148: i2c@4 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <4>;
+ };
+ imux149: i2c@5 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <5>;
+ };
+ imux150: i2c@6 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <6>;
+ };
+ imux151: i2c@7 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <7>;
+ };
+ };
};
+ /* To Fabric Card #4 */
imux23: i2c@7 {
#address-cells = <1>;
#size-cells = <0>;
reg = <7>;
+
+ i2c-switch@70 {
+ compatible = "nxp,pca9548";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0x70>;
+ i2c-mux-idle-disconnect;
+
+ imux152: i2c@0 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0>;
+ };
+ imux153: i2c@1 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <1>;
+ };
+ imux154: i2c@2 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <2>;
+ };
+ imux155: i2c@3 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <3>;
+ };
+ imux156: i2c@4 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <4>;
+ };
+ imux157: i2c@5 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <5>;
+ };
+ imux158: i2c@6 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <6>;
+ };
+ imux159: i2c@7 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <7>;
+ };
+ };
+
+ i2c-switch@73 {
+ compatible = "nxp,pca9548";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0x73>;
+ i2c-mux-idle-disconnect;
+
+ imux160: i2c@0 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0>;
+ };
+ imux161: i2c@1 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <1>;
+ };
+ imux162: i2c@2 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <2>;
+ };
+ imux163: i2c@3 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <3>;
+ };
+ imux164: i2c@4 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <4>;
+ };
+ imux165: i2c@5 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <5>;
+ };
+ imux166: i2c@6 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <6>;
+ };
+ imux167: i2c@7 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <7>;
+ };
+ };
};
};
};
@@ -165,6 +1188,7 @@
#address-cells = <1>;
#size-cells = <0>;
reg = <0x71>;
+ i2c-mux-idle-disconnect;
imux24: i2c@0 {
#address-cells = <1>;
@@ -252,7 +1276,7 @@
};
/*
- * I2C bus to Fan Control Board.
+ * I2C bus to Fan Control Boards.
*/
&i2c8 {
status = "okay";
@@ -262,29 +1286,230 @@
#address-cells = <1>;
#size-cells = <0>;
reg = <0x77>;
+ i2c-mux-idle-disconnect;
+ /* To Fan Control Board #1 */
imux32: i2c@0 {
#address-cells = <1>;
#size-cells = <0>;
reg = <0>;
+
+ i2c-switch@70 {
+ compatible = "nxp,pca9548";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0x70>;
+ i2c-mux-idle-disconnect;
+
+ imux168: i2c@0 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0>;
+ };
+ imux169: i2c@1 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <1>;
+ };
+ imux170: i2c@2 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <2>;
+ };
+ imux171: i2c@3 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <3>;
+ };
+ imux172: i2c@4 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <4>;
+ };
+ imux173: i2c@5 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <5>;
+ };
+ imux174: i2c@6 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <6>;
+ };
+ imux175: i2c@7 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <7>;
+ };
+ };
};
+ /* To Fan Control Board #2 */
imux33: i2c@1 {
#address-cells = <1>;
#size-cells = <0>;
reg = <1>;
+
+ i2c-switch@70 {
+ compatible = "nxp,pca9548";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0x70>;
+ i2c-mux-idle-disconnect;
+
+ imux176: i2c@0 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0>;
+ };
+ imux177: i2c@1 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <1>;
+ };
+ imux178: i2c@2 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <2>;
+ };
+ imux179: i2c@3 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <3>;
+ };
+ imux180: i2c@4 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <4>;
+ };
+ imux181: i2c@5 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <5>;
+ };
+ imux182: i2c@6 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <6>;
+ };
+ imux183: i2c@7 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <7>;
+ };
+ };
};
+ /* To Fan Control Board #3 */
imux34: i2c@2 {
#address-cells = <1>;
#size-cells = <0>;
reg = <2>;
+
+ i2c-switch@70 {
+ compatible = "nxp,pca9548";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0x70>;
+ i2c-mux-idle-disconnect;
+
+ imux184: i2c@0 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0>;
+ };
+ imux185: i2c@1 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <1>;
+ };
+ imux186: i2c@2 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <2>;
+ };
+ imux187: i2c@3 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <3>;
+ };
+ imux188: i2c@4 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <4>;
+ };
+ imux189: i2c@5 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <5>;
+ };
+ imux190: i2c@6 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <6>;
+ };
+ imux191: i2c@7 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <7>;
+ };
+ };
};
+ /* To Fan Control Board #4 */
imux35: i2c@3 {
#address-cells = <1>;
#size-cells = <0>;
reg = <3>;
+
+ i2c-switch@70 {
+ compatible = "nxp,pca9548";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0x70>;
+ i2c-mux-idle-disconnect;
+
+ imux192: i2c@0 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0>;
+ };
+ imux193: i2c@1 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <1>;
+ };
+ imux194: i2c@2 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <2>;
+ };
+ imux195: i2c@3 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <3>;
+ };
+ imux196: i2c@4 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <4>;
+ };
+ imux197: i2c@5 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <5>;
+ };
+ imux198: i2c@6 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <6>;
+ };
+ imux199: i2c@7 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <7>;
+ };
+ };
};
imux36: i2c@4 {
diff --git a/arch/arm/boot/dts/aspeed-bmc-facebook-wedge40.dts b/arch/arm/boot/dts/aspeed-bmc-facebook-wedge40.dts
index 54e508530dce..8ac23ff6b09e 100644
--- a/arch/arm/boot/dts/aspeed-bmc-facebook-wedge40.dts
+++ b/arch/arm/boot/dts/aspeed-bmc-facebook-wedge40.dts
@@ -27,6 +27,11 @@
memory@40000000 {
reg = <0x40000000 0x20000000>;
};
+
+ ast-adc-hwmon {
+ compatible = "iio-hwmon";
+ io-channels = <&adc 5>, <&adc 6>, <&adc 7>, <&adc 8>, <&adc 9>;
+ };
};
&wdt1 {
@@ -115,26 +120,47 @@
status = "okay";
};
-&i2c9 {
+&i2c11 {
status = "okay";
};
-&i2c10 {
+&i2c12 {
status = "okay";
};
-&i2c11 {
+&vhub {
status = "okay";
};
-&i2c12 {
+&adc {
status = "okay";
};
-&i2c13 {
+&pwm_tacho {
status = "okay";
-};
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_pwm0_default
+ &pinctrl_pwm1_default
+ &pinctrl_pwm6_default
+ &pinctrl_pwm7_default>;
+
+ fan@0 {
+ reg = <0x00>;
+ aspeed,fan-tach-ch = /bits/ 8 <0x00 0x01>;
+ };
-&vhub {
- status = "okay";
+ fan@1 {
+ reg = <0x01>;
+ aspeed,fan-tach-ch = /bits/ 8 <0x02 0x03>;
+ };
+
+ fan@6 {
+ reg = <0x06>;
+ aspeed,fan-tach-ch = /bits/ 8 <0x04 0x05>;
+ };
+
+ fan@7 {
+ reg = <0x07>;
+ aspeed,fan-tach-ch = /bits/ 8 <0x06 0x07>;
+ };
};
diff --git a/arch/arm/boot/dts/aspeed-bmc-ibm-rainier.dts b/arch/arm/boot/dts/aspeed-bmc-ibm-rainier.dts
index bdfe342bf7c5..cbc64a1d14d1 100644
--- a/arch/arm/boot/dts/aspeed-bmc-ibm-rainier.dts
+++ b/arch/arm/boot/dts/aspeed-bmc-ibm-rainier.dts
@@ -12,6 +12,23 @@
aliases {
serial4 = &uart5;
+ i2c16 = &i2c2mux0;
+ i2c17 = &i2c2mux1;
+ i2c18 = &i2c2mux2;
+ i2c19 = &i2c2mux3;
+
+ spi10 = &cfam0_spi0;
+ spi11 = &cfam0_spi1;
+ spi12 = &cfam0_spi2;
+ spi13 = &cfam0_spi3;
+ spi20 = &cfam1_spi0;
+ spi21 = &cfam1_spi1;
+ spi22 = &cfam1_spi2;
+ spi23 = &cfam1_spi3;
+ spi30 = &cfam2_spi0;
+ spi31 = &cfam2_spi1;
+ spi32 = &cfam2_spi2;
+ spi33 = &cfam2_spi3;
};
chosen {
@@ -35,9 +52,10 @@
};
vga_memory: region@bf000000 {
- no-map;
- reg = <0xbf000000 0x01000000>; /* 16M */
- };
+ no-map;
+ compatible = "shared-dma-pool";
+ reg = <0xbf000000 0x01000000>; /* 16M */
+ };
};
gpio-keys {
@@ -68,26 +86,117 @@
};
};
+ i2c2mux: i2cmux {
+ compatible = "i2c-mux-gpio";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ status = "okay";
+
+ i2c-parent = <&i2c2>;
+ mux-gpios = <&gpio0 ASPEED_GPIO(G, 4) GPIO_ACTIVE_HIGH>,
+ <&gpio0 ASPEED_GPIO(G, 5) GPIO_ACTIVE_HIGH>;
+ idle-state = <0>;
+
+ i2c2mux0: i2c@0 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0>;
+ };
+
+ i2c2mux1: i2c@1 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <1>;
+ };
+
+ i2c2mux2: i2c@2 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <2>;
+ };
+
+ i2c2mux3: i2c@3 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <3>;
+ };
+ };
+
+ leds {
+ compatible = "gpio-leds";
+
+ /* BMC Card fault LED at the back */
+ bmc-ingraham0 {
+ gpios = <&gpio0 ASPEED_GPIO(H, 1) GPIO_ACTIVE_LOW>;
+ };
+
+ /* Enclosure ID LED at the back */
+ rear-enc-id0 {
+ gpios = <&gpio0 ASPEED_GPIO(H, 2) GPIO_ACTIVE_LOW>;
+ };
+
+ /* Enclosure fault LED at the back */
+ rear-enc-fault0 {
+ gpios = <&gpio0 ASPEED_GPIO(H, 3) GPIO_ACTIVE_LOW>;
+ };
+
+ /* PCIE slot power LED */
+ pcieslot-power {
+ gpios = <&gpio0 ASPEED_GPIO(P, 4) GPIO_ACTIVE_LOW>;
+ };
+
+ /* System ID LED that is at front on Op Panel */
+ front-sys-id0 {
+ retain-state-shutdown;
+ default-state = "keep";
+ gpios = <&pca_oppanel 0 GPIO_ACTIVE_LOW>;
+ };
+
+ /* System Attention Indicator ID LED that is at front on Op Panel */
+ front-check-log0 {
+ retain-state-shutdown;
+ default-state = "keep";
+ gpios = <&pca_oppanel 1 GPIO_ACTIVE_LOW>;
+ };
+
+ /* Enclosure Fault LED that is at front on Op Panel */
+ front-enc-fault1 {
+ retain-state-shutdown;
+ default-state = "keep";
+ gpios = <&pca_oppanel 2 GPIO_ACTIVE_LOW>;
+ };
+
+ /* System PowerOn LED that is at front on Op Panel */
+ front-sys-pwron0 {
+ retain-state-shutdown;
+ default-state = "keep";
+ gpios = <&pca_oppanel 3 GPIO_ACTIVE_LOW>;
+ };
+ };
+};
+
+&ehci1 {
+ status = "okay";
};
&gpio0 {
gpio-line-names =
/*A0-A7*/ "","","","","","","","",
- /*B0-B7*/ "","","","","","","","",
+ /*B0-B7*/ "","","","","","","checkstop","",
/*C0-C7*/ "","","","","","","","",
/*D0-D7*/ "","","","","","","","",
/*E0-E7*/ "","","","","","","","",
/*F0-F7*/ "","","","","","","","",
/*G0-G7*/ "","","","","","","","",
- /*H0-H7*/ "","","","","","","","",
+ /*H0-H7*/ "","bmc-ingraham0","rear-enc-id0","rear-enc-fault0","","","","",
/*I0-I7*/ "","","","","","","","",
/*J0-J7*/ "","","","","","","","",
/*K0-K7*/ "","","","","","","","",
/*L0-L7*/ "","","","","","","","",
/*M0-M7*/ "","","","","","","","",
/*N0-N7*/ "","","","","","","","",
- /*O0-O7*/ "","","","","","","","",
- /*P0-P7*/ "","","","","","","","",
+ /*O0-O7*/ "","","","usb-power","","","","",
+ /*P0-P7*/ "","","","","pcieslot-power","","","",
/*Q0-Q7*/ "cfam-reset","","","","","","","",
/*R0-R7*/ "","","","","","","","",
/*S0-S7*/ "presence-ps0","presence-ps1","presence-ps2","presence-ps3",
@@ -102,6 +211,20 @@
/*AA0-AA7*/ "","","","","","","","",
/*AB0-AB7*/ "","","","","","","","",
/*AC0-AC7*/ "","","","","","","","";
+
+ pin_mclr_vpp {
+ gpio-hog;
+ gpios = <ASPEED_GPIO(P, 7) GPIO_OPEN_DRAIN>;
+ output-high;
+ line-name = "mclr_vpp";
+ };
+
+ i2c3_mux_oe_n {
+ gpio-hog;
+ gpios = <ASPEED_GPIO(G, 6) GPIO_ACTIVE_LOW>;
+ output-high;
+ line-name = "I2C3_MUX_OE_N";
+ };
};
&emmc_controller {
@@ -118,6 +241,12 @@
#address-cells = <2>;
#size-cells = <0>;
+ /*
+ * CFAM Reset is supposed to be active low but pass1 hardware is wired
+ * active high.
+ */
+ cfam-reset-gpios = <&gpio0 ASPEED_GPIO(Q, 0) GPIO_ACTIVE_HIGH>;
+
cfam@0,0 {
reg = <0 0>;
#address-cells = <1>;
@@ -129,6 +258,84 @@
reg = <0x1000 0x400>;
};
+ i2c@1800 {
+ compatible = "ibm,fsi-i2c-master";
+ reg = <0x1800 0x400>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ };
+
+ fsi2spi@1c00 {
+ compatible = "ibm,fsi2spi";
+ reg = <0x1c00 0x400>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ cfam0_spi0: spi@0 {
+ reg = <0x0>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ eeprom@0 {
+ at25,byte-len = <0x80000>;
+ at25,addr-mode = <4>;
+ at25,page-size = <256>;
+
+ compatible = "atmel,at25";
+ reg = <0>;
+ spi-max-frequency = <1000000>;
+ };
+ };
+
+ cfam0_spi1: spi@20 {
+ reg = <0x20>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ eeprom@0 {
+ at25,byte-len = <0x80000>;
+ at25,addr-mode = <4>;
+ at25,page-size = <256>;
+
+ compatible = "atmel,at25";
+ reg = <0>;
+ spi-max-frequency = <1000000>;
+ };
+ };
+
+ cfam0_spi2: spi@40 {
+ reg = <0x40>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ eeprom@0 {
+ at25,byte-len = <0x80000>;
+ at25,addr-mode = <4>;
+ at25,page-size = <256>;
+
+ compatible = "atmel,at25";
+ reg = <0>;
+ spi-max-frequency = <1000000>;
+ };
+ };
+
+ cfam0_spi3: spi@60 {
+ reg = <0x60>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ eeprom@0 {
+ at25,byte-len = <0x80000>;
+ at25,addr-mode = <4>;
+ at25,page-size = <256>;
+
+ compatible = "atmel,at25";
+ reg = <0>;
+ spi-max-frequency = <1000000>;
+ };
+ };
+ };
+
sbefifo@2400 {
compatible = "ibm,p9-sbefifo";
reg = <0x2400 0x400>;
@@ -136,7 +343,7 @@
#size-cells = <0>;
fsi_occ0: occ {
- compatible = "ibm,p9-occ";
+ compatible = "ibm,p10-occ";
};
};
@@ -163,6 +370,84 @@
reg = <0x1000 0x400>;
};
+ i2c@1800 {
+ compatible = "ibm,fsi-i2c-master";
+ reg = <0x1800 0x400>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ };
+
+ fsi2spi@1c00 {
+ compatible = "ibm,fsi2spi";
+ reg = <0x1c00 0x400>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ cfam1_spi0: spi@0 {
+ reg = <0x0>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ eeprom@0 {
+ at25,byte-len = <0x80000>;
+ at25,addr-mode = <4>;
+ at25,page-size = <256>;
+
+ compatible = "atmel,at25";
+ reg = <0>;
+ spi-max-frequency = <1000000>;
+ };
+ };
+
+ cfam1_spi1: spi@20 {
+ reg = <0x20>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ eeprom@0 {
+ at25,byte-len = <0x80000>;
+ at25,addr-mode = <4>;
+ at25,page-size = <256>;
+
+ compatible = "atmel,at25";
+ reg = <0>;
+ spi-max-frequency = <1000000>;
+ };
+ };
+
+ cfam1_spi2: spi@40 {
+ reg = <0x40>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ eeprom@0 {
+ at25,byte-len = <0x80000>;
+ at25,addr-mode = <4>;
+ at25,page-size = <256>;
+
+ compatible = "atmel,at25";
+ reg = <0>;
+ spi-max-frequency = <1000000>;
+ };
+ };
+
+ cfam1_spi3: spi@60 {
+ reg = <0x60>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ eeprom@0 {
+ at25,byte-len = <0x80000>;
+ at25,addr-mode = <4>;
+ at25,page-size = <256>;
+
+ compatible = "atmel,at25";
+ reg = <0>;
+ spi-max-frequency = <1000000>;
+ };
+ };
+ };
+
sbefifo@2400 {
compatible = "ibm,p9-sbefifo";
reg = <0x2400 0x400>;
@@ -170,7 +455,7 @@
#size-cells = <0>;
fsi_occ1: occ {
- compatible = "ibm,p9-occ";
+ compatible = "ibm,p10-occ";
};
};
@@ -183,6 +468,116 @@
no-scan-on-init;
};
};
+
+ cfam@2,0 {
+ reg = <2 0>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ chip-id = <2>;
+
+ scom@1000 {
+ compatible = "ibm,fsi2pib";
+ reg = <0x1000 0x400>;
+ };
+
+ i2c@1800 {
+ compatible = "ibm,fsi-i2c-master";
+ reg = <0x1800 0x400>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ };
+
+ fsi2spi@1c00 {
+ compatible = "ibm,fsi2spi";
+ reg = <0x1c00 0x400>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ cfam2_spi0: spi@0 {
+ reg = <0x0>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ eeprom@0 {
+ at25,byte-len = <0x80000>;
+ at25,addr-mode = <4>;
+ at25,page-size = <256>;
+
+ compatible = "atmel,at25";
+ reg = <0>;
+ spi-max-frequency = <1000000>;
+ };
+ };
+
+ cfam2_spi1: spi@20 {
+ reg = <0x20>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ eeprom@0 {
+ at25,byte-len = <0x80000>;
+ at25,addr-mode = <4>;
+ at25,page-size = <256>;
+
+ compatible = "atmel,at25";
+ reg = <0>;
+ spi-max-frequency = <1000000>;
+ };
+ };
+
+ cfam2_spi2: spi@40 {
+ reg = <0x40>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ eeprom@0 {
+ at25,byte-len = <0x80000>;
+ at25,addr-mode = <4>;
+ at25,page-size = <256>;
+
+ compatible = "atmel,at25";
+ reg = <0>;
+ spi-max-frequency = <1000000>;
+ };
+ };
+
+ cfam2_spi3: spi@60 {
+ reg = <0x60>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ eeprom@0 {
+ at25,byte-len = <0x80000>;
+ at25,addr-mode = <4>;
+ at25,page-size = <256>;
+
+ compatible = "atmel,at25";
+ reg = <0>;
+ spi-max-frequency = <1000000>;
+ };
+ };
+ };
+
+ sbefifo@2400 {
+ compatible = "ibm,p9-sbefifo";
+ reg = <0x2400 0x400>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ fsi_occ2: occ {
+ compatible = "ibm,p10-occ";
+ };
+ };
+
+ fsi_hub2: hub@3400 {
+ compatible = "fsi-master-hub";
+ reg = <0x3400 0x400>;
+ #address-cells = <2>;
+ #size-cells = <0>;
+
+ no-scan-on-init;
+ };
+ };
};
/* Legacy OCC numbering (to get rid of when userspace is fixed) */
@@ -194,6 +589,10 @@
reg = <2>;
};
+&fsi_occ2 {
+ reg = <3>;
+};
+
&ibt {
status = "okay";
};
@@ -205,6 +604,21 @@
compatible = "atmel,24c64";
reg = <0x51>;
};
+
+ tca9554@40 {
+ compatible = "ti,tca9554";
+ reg = <0x40>;
+ gpio-controller;
+ #gpio-cells = <2>;
+
+ smbus0 {
+ gpio-hog;
+ gpios = <4 GPIO_ACTIVE_HIGH>;
+ output-high;
+ line-name = "smbus0";
+ };
+ };
+
};
&i2c1 {
@@ -470,6 +884,56 @@
};
};
+ pca_oppanel: pca9551@60 {
+ compatible = "nxp,pca9551";
+ reg = <0x60>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ gpio-controller;
+ #gpio-cells = <2>;
+
+ gpio@0 {
+ reg = <0>;
+ type = <PCA955X_TYPE_GPIO>;
+ };
+
+ gpio@1 {
+ reg = <1>;
+ type = <PCA955X_TYPE_GPIO>;
+ };
+
+ gpio@2 {
+ reg = <2>;
+ type = <PCA955X_TYPE_GPIO>;
+ };
+
+ gpio@3 {
+ reg = <3>;
+ type = <PCA955X_TYPE_GPIO>;
+ };
+
+ gpio@4 {
+ reg = <4>;
+ type = <PCA955X_TYPE_GPIO>;
+ };
+
+ gpio@5 {
+ reg = <5>;
+ type = <PCA955X_TYPE_GPIO>;
+ };
+
+ gpio@6 {
+ reg = <6>;
+ type = <PCA955X_TYPE_GPIO>;
+ };
+
+ gpio@7 {
+ reg = <7>;
+ type = <PCA955X_TYPE_GPIO>;
+ };
+ };
+
dps: dps310@76 {
compatible = "infineon,dps310";
reg = <0x76>;
@@ -519,6 +983,96 @@
compatible = "atmel,24c64";
reg = <0x51>;
};
+
+ pca1: pca9552@61 {
+ compatible = "nxp,pca9552";
+ reg = <0x61>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ gpio-controller;
+ #gpio-cells = <2>;
+
+ gpio@0 {
+ reg = <0>;
+ type = <PCA955X_TYPE_GPIO>;
+ };
+
+ gpio@1 {
+ reg = <1>;
+ type = <PCA955X_TYPE_GPIO>;
+ };
+
+ gpio@2 {
+ reg = <2>;
+ type = <PCA955X_TYPE_GPIO>;
+ };
+
+ gpio@3 {
+ reg = <3>;
+ type = <PCA955X_TYPE_GPIO>;
+ };
+
+ gpio@4 {
+ reg = <4>;
+ type = <PCA955X_TYPE_GPIO>;
+ };
+
+ gpio@5 {
+ reg = <5>;
+ type = <PCA955X_TYPE_GPIO>;
+ };
+
+ gpio@6 {
+ reg = <6>;
+ type = <PCA955X_TYPE_GPIO>;
+ };
+
+ gpio@7 {
+ reg = <7>;
+ type = <PCA955X_TYPE_GPIO>;
+ };
+
+ gpio@8 {
+ reg = <8>;
+ type = <PCA955X_TYPE_GPIO>;
+ };
+
+ gpio@9 {
+ reg = <9>;
+ type = <PCA955X_TYPE_GPIO>;
+ };
+
+ gpio@10 {
+ reg = <10>;
+ type = <PCA955X_TYPE_GPIO>;
+ };
+
+ gpio@11 {
+ reg = <11>;
+ type = <PCA955X_TYPE_GPIO>;
+ };
+
+ gpio@12 {
+ reg = <12>;
+ type = <PCA955X_TYPE_GPIO>;
+ };
+
+ gpio@13 {
+ reg = <13>;
+ type = <PCA955X_TYPE_GPIO>;
+ };
+
+ gpio@14 {
+ reg = <14>;
+ type = <PCA955X_TYPE_GPIO>;
+ };
+
+ gpio@15 {
+ reg = <15>;
+ type = <PCA955X_TYPE_GPIO>;
+ };
+ };
+
};
&i2c9 {
@@ -627,6 +1181,11 @@
memory-region = <&flash_memory>;
};
+&kcs4 {
+ compatible = "openbmc,mctp-lpc";
+ status = "okay";
+};
+
&mac2 {
status = "okay";
pinctrl-names = "default";
@@ -656,13 +1215,6 @@
spi-max-frequency = <50000000>;
#include "openbmc-flash-layout-128.dtsi"
};
-
- flash@1 {
- status = "okay";
- m25p,fast-read;
- label = "alt-bmc";
- spi-max-frequency = <50000000>;
- };
};
&spi1 {
@@ -677,3 +1229,8 @@
spi-max-frequency = <100000000>;
};
};
+
+&xdma {
+ status = "okay";
+ memory-region = <&vga_memory>;
+};
diff --git a/arch/arm/boot/dts/aspeed-bmc-opp-mihawk.dts b/arch/arm/boot/dts/aspeed-bmc-opp-mihawk.dts
index 60e545b6396f..cb85168f6761 100644
--- a/arch/arm/boot/dts/aspeed-bmc-opp-mihawk.dts
+++ b/arch/arm/boot/dts/aspeed-bmc-opp-mihawk.dts
@@ -820,12 +820,50 @@
#address-cells = <1>;
#size-cells = <0>;
reg = <0>;
+
+ tca9554@39 {
+ compatible = "ti,tca9554";
+ reg = <0x39>;
+ gpio-controller;
+ #gpio-cells = <2>;
+
+ smbus0 {
+ gpio-hog;
+ gpios = <4 GPIO_ACTIVE_HIGH>;
+ output-high;
+ line-name = "smbus0";
+ };
+ };
+
+ tmp431@4c {
+ compatible = "ti,tmp401";
+ reg = <0x4c>;
+ };
};
bus9_mux232: i2c@1 {
#address-cells = <1>;
#size-cells = <0>;
reg = <1>;
+
+ tca9554@39 {
+ compatible = "ti,tca9554";
+ reg = <0x39>;
+ gpio-controller;
+ #gpio-cells = <2>;
+
+ smbus1 {
+ gpio-hog;
+ gpios = <4 GPIO_ACTIVE_HIGH>;
+ output-high;
+ line-name = "smbus1";
+ };
+ };
+
+ tmp431@4c {
+ compatible = "ti,tmp401";
+ reg = <0x4c>;
+ };
};
bus9_mux233: i2c@2 {
@@ -855,12 +893,50 @@
#address-cells = <1>;
#size-cells = <0>;
reg = <0>;
+
+ tca9554@39 {
+ compatible = "ti,tca9554";
+ reg = <0x39>;
+ gpio-controller;
+ #gpio-cells = <2>;
+
+ smbus2 {
+ gpio-hog;
+ gpios = <4 GPIO_ACTIVE_HIGH>;
+ output-high;
+ line-name = "smbus2";
+ };
+ };
+
+ tmp431@4c {
+ compatible = "ti,tmp401";
+ reg = <0x4c>;
+ };
};
bus9_mux236: i2c@1 {
#address-cells = <1>;
#size-cells = <0>;
reg = <1>;
+
+ tca9554@39 {
+ compatible = "ti,tca9554";
+ reg = <0x39>;
+ gpio-controller;
+ #gpio-cells = <2>;
+
+ smbus3 {
+ gpio-hog;
+ gpios = <4 GPIO_ACTIVE_HIGH>;
+ output-high;
+ line-name = "smbus3";
+ };
+ };
+
+ tmp431@4c {
+ compatible = "ti,tmp401";
+ reg = <0x4c>;
+ };
};
bus9_mux237: i2c@2 {
@@ -909,12 +985,50 @@
#address-cells = <1>;
#size-cells = <0>;
reg = <0>;
+
+ tca9554@39 {
+ compatible = "ti,tca9554";
+ reg = <0x39>;
+ gpio-controller;
+ #gpio-cells = <2>;
+
+ smbus4 {
+ gpio-hog;
+ gpios = <4 GPIO_ACTIVE_HIGH>;
+ output-high;
+ line-name = "smbus4";
+ };
+ };
+
+ tmp431@4c {
+ compatible = "ti,tmp401";
+ reg = <0x4c>;
+ };
};
bus10_mux240: i2c@1 {
#address-cells = <1>;
#size-cells = <0>;
reg = <1>;
+
+ tca9554@39 {
+ compatible = "ti,tca9554";
+ reg = <0x39>;
+ gpio-controller;
+ #gpio-cells = <2>;
+
+ smbus5 {
+ gpio-hog;
+ gpios = <4 GPIO_ACTIVE_HIGH>;
+ output-high;
+ line-name = "smbus5";
+ };
+ };
+
+ tmp431@4c {
+ compatible = "ti,tmp401";
+ reg = <0x4c>;
+ };
};
bus10_mux241: i2c@2 {
@@ -944,12 +1058,50 @@
#address-cells = <1>;
#size-cells = <0>;
reg = <0>;
+
+ tca9554@39 {
+ compatible = "ti,tca9554";
+ reg = <0x39>;
+ gpio-controller;
+ #gpio-cells = <2>;
+
+ smbus6 {
+ gpio-hog;
+ gpios = <4 GPIO_ACTIVE_HIGH>;
+ output-high;
+ line-name = "smbus6";
+ };
+ };
+
+ tmp431@4c {
+ compatible = "ti,tmp401";
+ reg = <0x4c>;
+ };
};
bus10_mux244: i2c@1 {
#address-cells = <1>;
#size-cells = <0>;
reg = <1>;
+
+ tca9554@39 {
+ compatible = "ti,tca9554";
+ reg = <0x39>;
+ gpio-controller;
+ #gpio-cells = <2>;
+
+ smbus7 {
+ gpio-hog;
+ gpios = <4 GPIO_ACTIVE_HIGH>;
+ output-high;
+ line-name = "smbus7";
+ };
+ };
+
+ tmp431@4c {
+ compatible = "ti,tmp401";
+ reg = <0x4c>;
+ };
};
bus10_mux245: i2c@2 {
diff --git a/arch/arm/boot/dts/aspeed-bmc-opp-tacoma.dts b/arch/arm/boot/dts/aspeed-bmc-opp-tacoma.dts
index 13c4aa02f4de..094766eca3ad 100644
--- a/arch/arm/boot/dts/aspeed-bmc-opp-tacoma.dts
+++ b/arch/arm/boot/dts/aspeed-bmc-opp-tacoma.dts
@@ -29,76 +29,17 @@
no-map;
reg = <0xb8000000 0x4000000>; /* 64M */
};
- };
-
- gpio-keys {
- compatible = "gpio-keys";
-
- checkstop {
- label = "checkstop";
- gpios = <&gpio0 ASPEED_GPIO(E, 3) GPIO_ACTIVE_LOW>;
- linux,code = <ASPEED_GPIO(E, 3)>;
- };
-
- ps0-presence {
- label = "ps0-presence";
- gpios = <&gpio0 ASPEED_GPIO(H, 3) GPIO_ACTIVE_LOW>;
- linux,code = <ASPEED_GPIO(H, 3)>;
- };
-
- ps1-presence {
- label = "ps1-presence";
- gpios = <&gpio0 ASPEED_GPIO(E, 5) GPIO_ACTIVE_LOW>;
- linux,code = <ASPEED_GPIO(E, 5)>;
- };
- };
-
- gpio-keys-polled {
- compatible = "gpio-keys-polled";
- #address-cells = <1>;
- #size-cells = <0>;
- poll-interval = <1000>;
-
- fan0-presence {
- label = "fan0-presence";
- gpios = <&pca0 4 GPIO_ACTIVE_LOW>;
- linux,code = <4>;
- };
- fan1-presence {
- label = "fan1-presence";
- gpios = <&pca0 5 GPIO_ACTIVE_LOW>;
- linux,code = <5>;
- };
-
- fan2-presence {
- label = "fan2-presence";
- gpios = <&pca0 6 GPIO_ACTIVE_LOW>;
- linux,code = <6>;
- };
-
- fan3-presence {
- label = "fan3-presence";
- gpios = <&pca0 7 GPIO_ACTIVE_LOW>;
- linux,code = <7>;
+ vga_memory: region@bf000000 {
+ no-map;
+ compatible = "shared-dma-pool";
+ reg = <0xbf000000 0x01000000>; /* 16M */
};
};
gpio-keys {
compatible = "gpio-keys";
- air-water {
- label = "air-water";
- gpios = <&gpio0 ASPEED_GPIO(Q, 7) GPIO_ACTIVE_LOW>;
- linux,code = <ASPEED_GPIO(Q, 7)>;
- };
-
- checkstop {
- label = "checkstop";
- gpios = <&gpio0 ASPEED_GPIO(E, 3) GPIO_ACTIVE_LOW>;
- linux,code = <ASPEED_GPIO(E, 3)>;
- };
-
ps0-presence {
label = "ps0-presence";
gpios = <&gpio0 ASPEED_GPIO(H, 3) GPIO_ACTIVE_LOW>;
@@ -154,6 +95,10 @@
};
};
+&ehci1 {
+ status = "okay";
+};
+
&gpio0 {
gpio-line-names =
/*A0-A7*/ "","","","","","","","",
@@ -170,7 +115,7 @@
/*L0-L7*/ "","","","","","","","",
/*M0-M7*/ "","","","","","","","",
/*N0-N7*/ "","","","","","","","",
- /*O0-O7*/ "led-rear-power","led-rear-id","","","","","","",
+ /*O0-O7*/ "led-rear-power","led-rear-id","","usb-power","","","","",
/*P0-P7*/ "","","","","","","","",
/*Q0-Q7*/ "cfam-reset","","","","","","","fsi-routing",
/*R0-R7*/ "","","","","","","","",
@@ -244,6 +189,7 @@
fsi-routing-gpios = <&gpio0 ASPEED_GPIO(Q, 7) GPIO_ACTIVE_HIGH>;
fsi-mux-gpios = <&gpio0 ASPEED_GPIO(B, 0) GPIO_ACTIVE_HIGH>;
+ cfam-reset-gpios = <&gpio0 ASPEED_GPIO(Q, 0) GPIO_ACTIVE_LOW>;
cfam@0,0 {
reg = <0 0>;
@@ -912,3 +858,13 @@
pinctrl-0 = <&pinctrl_lpc_default>,
<&pinctrl_lsirq_default>;
};
+
+&xdma {
+ status = "okay";
+ memory-region = <&vga_memory>;
+};
+
+&kcs4 {
+ compatible = "openbmc,mctp-lpc";
+ status = "okay";
+};
diff --git a/arch/arm/boot/dts/aspeed-bmc-opp-witherspoon-128.dts b/arch/arm/boot/dts/aspeed-bmc-opp-witherspoon-128.dts
new file mode 100644
index 000000000000..f4d575ed1541
--- /dev/null
+++ b/arch/arm/boot/dts/aspeed-bmc-opp-witherspoon-128.dts
@@ -0,0 +1,65 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+// Copyright 2019 IBM Corp.
+/dts-v1/;
+
+#include "aspeed-bmc-opp-witherspoon.dts"
+
+/ {
+ gpio-keys {
+ /delete-node/ checkstop;
+ };
+};
+
+&fmc {
+ status = "okay";
+
+ flash@0 {
+ status = "okay";
+ label = "bmc";
+ m25p,fast-read;
+ spi-max-frequency = <100000000>;
+
+ partitions {
+ #address-cells = < 1 >;
+ #size-cells = < 1 >;
+ compatible = "fixed-partitions";
+ u-boot@0 {
+ reg = < 0 0x60000 >;
+ label = "u-boot";
+ };
+ u-boot-env@60000 {
+ reg = < 0x60000 0x20000 >;
+ label = "u-boot-env";
+ };
+ obmc-ubi@80000 {
+ reg = < 0x80000 0x7F80000>;
+ label = "obmc-ubi";
+ };
+ };
+ };
+
+ flash@1 {
+ status = "okay";
+ label = "alt-bmc";
+ m25p,fast-read;
+ spi-max-frequency = <100000000>;
+
+ partitions {
+ #address-cells = < 1 >;
+ #size-cells = < 1 >;
+ compatible = "fixed-partitions";
+ u-boot@0 {
+ reg = < 0 0x60000 >;
+ label = "alt-u-boot";
+ };
+ u-boot-env@60000 {
+ reg = < 0x60000 0x20000 >;
+ label = "alt-u-boot-env";
+ };
+ obmc-ubi@80000 {
+ reg = < 0x80000 0x7F80000>;
+ label = "alt-obmc-ubi";
+ };
+ };
+ };
+};
diff --git a/arch/arm/boot/dts/aspeed-bmc-opp-witherspoon.dts b/arch/arm/boot/dts/aspeed-bmc-opp-witherspoon.dts
index a0f99e34ac8e..81bf93c07ff9 100644
--- a/arch/arm/boot/dts/aspeed-bmc-opp-witherspoon.dts
+++ b/arch/arm/boot/dts/aspeed-bmc-opp-witherspoon.dts
@@ -27,6 +27,12 @@
reg = <0x98000000 0x04000000>; /* 64M */
};
+ vga_memory: region@9f000000 {
+ no-map;
+ compatible = "shared-dma-pool";
+ reg = <0x9f000000 0x01000000>; /* 16M */
+ };
+
gfx_memory: framebuffer {
size = <0x01000000>;
alignment = <0x01000000>;
@@ -323,6 +329,11 @@
flash = <&spi1>;
};
+&kcs4 {
+ compatible = "openbmc,mctp-lpc";
+ status = "okay";
+};
+
&mac0 {
status = "okay";
pinctrl-names = "default";
@@ -356,6 +367,58 @@
reg = <0x52>;
#address-cells = <1>;
#size-cells = <0>;
+
+ fan@0 {
+ compatible = "pmbus-fan";
+ reg = <0>;
+ tach-pulses = <2>;
+ maxim,fan-rotor-input = "tach";
+ maxim,fan-pwm-freq = <25000>;
+ maxim,fan-dual-tach;
+ maxim,fan-no-watchdog;
+ maxim,fan-no-fault-ramp;
+ maxim,fan-ramp = <2>;
+ maxim,fan-fault-pin-mon;
+ };
+
+ fan@1 {
+ compatible = "pmbus-fan";
+ reg = <1>;
+ tach-pulses = <2>;
+ maxim,fan-rotor-input = "tach";
+ maxim,fan-pwm-freq = <25000>;
+ maxim,fan-dual-tach;
+ maxim,fan-no-watchdog;
+ maxim,fan-no-fault-ramp;
+ maxim,fan-ramp = <2>;
+ maxim,fan-fault-pin-mon;
+ };
+
+ fan@2 {
+ compatible = "pmbus-fan";
+ reg = <2>;
+ tach-pulses = <2>;
+ maxim,fan-rotor-input = "tach";
+ maxim,fan-pwm-freq = <25000>;
+ maxim,fan-dual-tach;
+ maxim,fan-no-watchdog;
+ maxim,fan-no-fault-ramp;
+ maxim,fan-ramp = <2>;
+ maxim,fan-fault-pin-mon;
+ };
+
+ fan@3 {
+ compatible = "pmbus-fan";
+ reg = <3>;
+ tach-pulses = <2>;
+ maxim,fan-rotor-input = "tach";
+ maxim,fan-pwm-freq = <25000>;
+ maxim,fan-dual-tach;
+ maxim,fan-no-watchdog;
+ maxim,fan-no-fault-ramp;
+ maxim,fan-ramp = <2>;
+ maxim,fan-fault-pin-mon;
+ };
};
dps: dps310@76 {
@@ -690,4 +753,9 @@
memory-region = <&video_engine_memory>;
};
+&xdma {
+ status = "okay";
+ memory-region = <&vga_memory>;
+};
+
#include "ibm-power9-dual.dtsi"
diff --git a/arch/arm/boot/dts/aspeed-g4.dtsi b/arch/arm/boot/dts/aspeed-g4.dtsi
index 82f0213e3a3c..f606fc01ff13 100644
--- a/arch/arm/boot/dts/aspeed-g4.dtsi
+++ b/arch/arm/boot/dts/aspeed-g4.dtsi
@@ -390,9 +390,20 @@
interrupts = <8>;
status = "disabled";
};
+
+ sio_regs: regs {
+ compatible = "aspeed,bmc-misc";
+ };
};
};
+ peci: bus@1e78b000 {
+ compatible = "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges = <0x0 0x1e78b000 0x60>;
+ };
+
uart2: serial@1e78d000 {
compatible = "ns16550a";
reg = <0x1e78d000 0x20>;
@@ -436,6 +447,24 @@
};
};
+&peci {
+ peci0: peci-bus@0 {
+ compatible = "aspeed,ast2400-peci";
+ reg = <0x0 0x60>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ interrupts = <15>;
+ clocks = <&syscon ASPEED_CLK_GATE_REFCLK>;
+ resets = <&syscon ASPEED_RESET_PECI>;
+ clock-frequency = <24000000>;
+ msg-timing = <1>;
+ addr-timing = <1>;
+ rd-sampling-point = <8>;
+ cmd-timeout-ms = <1000>;
+ status = "disabled";
+ };
+};
+
&i2c {
i2c_ic: interrupt-controller@0 {
#interrupt-cells = <1>;
@@ -1433,3 +1462,86 @@
groups = "WDTRST2";
};
};
+
+&sio_regs {
+ sio_2b {
+ offset = <0xf0>;
+ bit-mask = <0xff>;
+ bit-shift = <24>;
+ };
+ sio_2a {
+ offset = <0xf0>;
+ bit-mask = <0xff>;
+ bit-shift = <16>;
+ };
+ sio_29 {
+ offset = <0xf0>;
+ bit-mask = <0xff>;
+ bit-shift = <8>;
+ };
+ sio_28 {
+ offset = <0xf0>;
+ bit-mask = <0xff>;
+ bit-shift = <0>;
+ };
+ sio_2f {
+ offset = <0xf4>;
+ bit-mask = <0xff>;
+ bit-shift = <24>;
+ };
+ sio_2e {
+ offset = <0xf4>;
+ bit-mask = <0xff>;
+ bit-shift = <16>;
+ };
+ sio_2d {
+ offset = <0xf4>;
+ bit-mask = <0xff>;
+ bit-shift = <8>;
+ };
+ sio_2c {
+ offset = <0xf4>;
+ bit-mask = <0xff>;
+ bit-shift = <0>;
+ };
+ sio_23 {
+ offset = <0xf8>;
+ bit-mask = <0xff>;
+ bit-shift = <24>;
+ };
+ sio_22 {
+ offset = <0xf8>;
+ bit-mask = <0xff>;
+ bit-shift = <16>;
+ };
+ sio_21 {
+ offset = <0xf8>;
+ bit-mask = <0xff>;
+ bit-shift = <8>;
+ };
+ sio_20 {
+ offset = <0xf8>;
+ bit-mask = <0xff>;
+ bit-shift = <0>;
+ };
+ sio_27 {
+ offset = <0xfc>;
+ bit-mask = <0xff>;
+ bit-shift = <24>;
+ };
+ sio_26 {
+ offset = <0xfc>;
+ bit-mask = <0xff>;
+ bit-shift = <16>;
+ };
+ sio_25 {
+ offset = <0xfc>;
+ bit-mask = <0xff>;
+ bit-shift = <8>;
+ };
+ sio_24 {
+ offset = <0xfc>;
+ bit-mask = <0xff>;
+ bit-shift = <0>;
+ };
+};
diff --git a/arch/arm/boot/dts/aspeed-g5.dtsi b/arch/arm/boot/dts/aspeed-g5.dtsi
index de7fd80b022a..19288495f41a 100644
--- a/arch/arm/boot/dts/aspeed-g5.dtsi
+++ b/arch/arm/boot/dts/aspeed-g5.dtsi
@@ -1,5 +1,6 @@
// SPDX-License-Identifier: GPL-2.0+
#include <dt-bindings/clock/aspeed-clock.h>
+#include <dt-bindings/interrupt-controller/aspeed-scu-ic.h>
/ {
model = "Aspeed BMC";
@@ -243,6 +244,10 @@
reg = <0x80 0x18>, <0xa0 0x10>;
aspeed,external-nodes = <&gfx>, <&lhc>;
};
+
+ vga_scratch: scratch {
+ compatible = "aspeed,bmc-misc";
+ };
};
rng: hwrng@1e6e2078 {
@@ -267,8 +272,8 @@
reg = <0x1e6e7000 0x100>;
clocks = <&syscon ASPEED_CLK_GATE_BCLK>;
resets = <&syscon ASPEED_RESET_XDMA>;
- interrupts-extended = <&vic 6>, <&scu_ic 2>;
- pcie-device = "bmc";
+ interrupts-extended = <&vic 6>, <&scu_ic ASPEED_AST2500_SCU_IC_PCIE_RESET_LO_TO_HI>;
+ aspeed,pcie-device = "bmc";
aspeed,scu = <&syscon>;
status = "disabled";
};
@@ -424,7 +429,6 @@
interrupts = <8>;
clocks = <&syscon ASPEED_CLK_APB>;
no-loopback-test;
- aspeed,sirq-polarity-sense = <&syscon 0x70 25>;
status = "disabled";
};
@@ -513,9 +517,20 @@
interrupts = <8>;
status = "disabled";
};
+
+ sio_regs: regs {
+ compatible = "aspeed,bmc-misc";
+ };
};
};
+ peci: bus@1e78b000 {
+ compatible = "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges = <0x0 0x1e78b000 0x60>;
+ };
+
uart2: serial@1e78d000 {
compatible = "ns16550a";
reg = <0x1e78d000 0x20>;
@@ -559,6 +574,24 @@
};
};
+&peci {
+ peci0: peci-bus@0 {
+ compatible = "aspeed,ast2500-peci";
+ reg = <0x0 0x60>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ interrupts = <15>;
+ clocks = <&syscon ASPEED_CLK_GATE_REFCLK>;
+ resets = <&syscon ASPEED_RESET_PECI>;
+ clock-frequency = <24000000>;
+ msg-timing = <1>;
+ addr-timing = <1>;
+ rd-sampling-point = <8>;
+ cmd-timeout-ms = <1000>;
+ status = "disabled";
+ };
+};
+
&i2c {
i2c_ic: interrupt-controller@0 {
#interrupt-cells = <1>;
@@ -1636,3 +1669,134 @@
groups = "WDTRST2";
};
};
+
+&vga_scratch {
+ dac_mux {
+ offset = <0x2c>;
+ bit-mask = <0x3>;
+ bit-shift = <16>;
+ };
+ vga0 {
+ offset = <0x50>;
+ bit-mask = <0xffffffff>;
+ bit-shift = <0>;
+ };
+ vga1 {
+ offset = <0x54>;
+ bit-mask = <0xffffffff>;
+ bit-shift = <0>;
+ };
+ vga2 {
+ offset = <0x58>;
+ bit-mask = <0xffffffff>;
+ bit-shift = <0>;
+ };
+ vga3 {
+ offset = <0x5c>;
+ bit-mask = <0xffffffff>;
+ bit-shift = <0>;
+ };
+ vga4 {
+ offset = <0x60>;
+ bit-mask = <0xffffffff>;
+ bit-shift = <0>;
+ };
+ vga5 {
+ offset = <0x64>;
+ bit-mask = <0xffffffff>;
+ bit-shift = <0>;
+ };
+ vga6 {
+ offset = <0x68>;
+ bit-mask = <0xffffffff>;
+ bit-shift = <0>;
+ };
+ vga7 {
+ offset = <0x6c>;
+ bit-mask = <0xffffffff>;
+ bit-shift = <0>;
+ };
+};
+
+&sio_regs {
+ sio_2b {
+ offset = <0xf0>;
+ bit-mask = <0xff>;
+ bit-shift = <24>;
+ };
+ sio_2a {
+ offset = <0xf0>;
+ bit-mask = <0xff>;
+ bit-shift = <16>;
+ };
+ sio_29 {
+ offset = <0xf0>;
+ bit-mask = <0xff>;
+ bit-shift = <8>;
+ };
+ sio_28 {
+ offset = <0xf0>;
+ bit-mask = <0xff>;
+ bit-shift = <0>;
+ };
+ sio_2f {
+ offset = <0xf4>;
+ bit-mask = <0xff>;
+ bit-shift = <24>;
+ };
+ sio_2e {
+ offset = <0xf4>;
+ bit-mask = <0xff>;
+ bit-shift = <16>;
+ };
+ sio_2d {
+ offset = <0xf4>;
+ bit-mask = <0xff>;
+ bit-shift = <8>;
+ };
+ sio_2c {
+ offset = <0xf4>;
+ bit-mask = <0xff>;
+ bit-shift = <0>;
+ };
+ sio_23 {
+ offset = <0xf8>;
+ bit-mask = <0xff>;
+ bit-shift = <24>;
+ };
+ sio_22 {
+ offset = <0xf8>;
+ bit-mask = <0xff>;
+ bit-shift = <16>;
+ };
+ sio_21 {
+ offset = <0xf8>;
+ bit-mask = <0xff>;
+ bit-shift = <8>;
+ };
+ sio_20 {
+ offset = <0xf8>;
+ bit-mask = <0xff>;
+ bit-shift = <0>;
+ };
+ sio_27 {
+ offset = <0xfc>;
+ bit-mask = <0xff>;
+ bit-shift = <24>;
+ };
+ sio_26 {
+ offset = <0xfc>;
+ bit-mask = <0xff>;
+ bit-shift = <16>;
+ };
+ sio_25 {
+ offset = <0xfc>;
+ bit-mask = <0xff>;
+ bit-shift = <8>;
+ };
+ sio_24 {
+ offset = <0xfc>;
+ bit-mask = <0xff>;
+ bit-shift = <0>;
+ };
+};
diff --git a/arch/arm/boot/dts/aspeed-g6.dtsi b/arch/arm/boot/dts/aspeed-g6.dtsi
index 9d8d8e18bc90..97ca743363d7 100644
--- a/arch/arm/boot/dts/aspeed-g6.dtsi
+++ b/arch/arm/boot/dts/aspeed-g6.dtsi
@@ -2,6 +2,7 @@
// Copyright 2019 IBM Corp.
#include <dt-bindings/interrupt-controller/arm-gic.h>
+#include <dt-bindings/interrupt-controller/aspeed-scu-ic.h>
#include <dt-bindings/clock/ast2600-clock.h>
/ {
@@ -346,22 +347,12 @@
resets = <&syscon ASPEED_RESET_DEV_XDMA>, <&syscon ASPEED_RESET_RC_XDMA>;
reset-names = "device", "root-complex";
interrupts-extended = <&gic GIC_SPI 6 IRQ_TYPE_LEVEL_HIGH>,
- <&scu_ic0 2>;
- pcie-device = "bmc";
+ <&scu_ic0 ASPEED_AST2600_SCU_IC0_PCIE_PERST_LO_TO_HI>;
+ aspeed,pcie-device = "bmc";
aspeed,scu = <&syscon>;
status = "disabled";
};
- video: video@1e700000 {
- compatible = "aspeed,ast2600-video-engine";
- reg = <0x1e700000 0x1000>;
- clocks = <&syscon ASPEED_CLK_GATE_VCLK>,
- <&syscon ASPEED_CLK_GATE_ECLK>;
- clock-names = "vclk", "eclk";
- interrupts = <GIC_SPI 7 IRQ_TYPE_LEVEL_HIGH>;
- status = "disabled";
- };
-
gpio0: gpio@1e780000 {
#gpio-cells = <2>;
gpio-controller;
@@ -457,6 +448,13 @@
status = "disabled";
};
+ peci: bus@1e78b000 {
+ compatible = "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges = <0x0 0x1e78b000 0x100>;
+ };
+
lpc: lpc@1e789000 {
compatible = "aspeed,ast2600-lpc", "simple-mfd";
reg = <0x1e789000 0x1000>;
@@ -542,6 +540,10 @@
interrupts = <GIC_SPI 143 IRQ_TYPE_LEVEL_HIGH>;
status = "disabled";
};
+
+ sio_regs: regs {
+ compatible = "aspeed,bmc-misc";
+ };
};
};
@@ -687,6 +689,24 @@
#include "aspeed-g6-pinctrl.dtsi"
+&peci {
+ peci0: peci-bus@0 {
+ compatible = "aspeed,ast2600-peci";
+ reg = <0x0 0x100>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ interrupts = <GIC_SPI 38 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&syscon ASPEED_CLK_GATE_REF0CLK>;
+ resets = <&syscon ASPEED_RESET_PECI>;
+ clock-frequency = <24000000>;
+ msg-timing = <1>;
+ addr-timing = <1>;
+ rd-sampling-point = <8>;
+ cmd-timeout-ms = <1000>;
+ status = "disabled";
+ };
+};
+
&i2c {
i2c0: i2c-bus@80 {
#address-cells = <1>;
diff --git a/arch/arm/boot/dts/nuvoton-common-npcm7xx.dtsi b/arch/arm/boot/dts/nuvoton-common-npcm7xx.dtsi
index d2d0761295a4..58899ccba505 100644
--- a/arch/arm/boot/dts/nuvoton-common-npcm7xx.dtsi
+++ b/arch/arm/boot/dts/nuvoton-common-npcm7xx.dtsi
@@ -3,14 +3,21 @@
// Copyright 2018 Google, Inc.
#include <dt-bindings/interrupt-controller/arm-gic.h>
+#include <dt-bindings/clock/nuvoton,npcm7xx-clock.h>
+#include <dt-bindings/gpio/gpio.h>
/ {
#address-cells = <1>;
#size-cells = <1>;
interrupt-parent = <&gic>;
+ memory {
+ device_type = "memory";
+ reg = <0 0>;
+ };
+
/* external reference clock */
- clk_refclk: clk_refclk {
+ clk_refclk: clk-refclk {
compatible = "fixed-clock";
#clock-cells = <0>;
clock-frequency = <25000000>;
@@ -18,7 +25,7 @@
};
/* external reference clock for cpu. float in normal operation */
- clk_sysbypck: clk_sysbypck {
+ clk_sysbypck: clk-sysbypck {
compatible = "fixed-clock";
#clock-cells = <0>;
clock-frequency = <800000000>;
@@ -26,7 +33,7 @@
};
/* external reference clock for MC. float in normal operation */
- clk_mcbypck: clk_mcbypck {
+ clk_mcbypck: clk-mcbypck {
compatible = "fixed-clock";
#clock-cells = <0>;
clock-frequency = <800000000>;
@@ -34,7 +41,7 @@
};
/* external clock signal rg1refck, supplied by the phy */
- clk_rg1refck: clk_rg1refck {
+ clk_rg1refck: clk-rg1refck {
compatible = "fixed-clock";
#clock-cells = <0>;
clock-frequency = <125000000>;
@@ -42,14 +49,14 @@
};
/* external clock signal rg2refck, supplied by the phy */
- clk_rg2refck: clk_rg2refck {
+ clk_rg2refck: clk-rg2refck {
compatible = "fixed-clock";
#clock-cells = <0>;
clock-frequency = <125000000>;
clock-output-names = "clk_rg2refck";
};
- clk_xin: clk_xin {
+ clk_xin: clk-xin {
compatible = "fixed-clock";
#clock-cells = <0>;
clock-frequency = <50000000>;
@@ -69,6 +76,12 @@
reg = <0x800000 0x1000>;
};
+ rst: rst@f0801000 {
+ compatible = "nuvoton,npcm750-rst", "syscon",
+ "simple-mfd";
+ reg = <0x801000 0x6C>;
+ };
+
scu: scu@3fe000 {
compatible = "arm,cortex-a9-scu";
reg = <0x3fe000 0x1000>;
@@ -80,7 +93,7 @@
interrupts = <GIC_SPI 21 IRQ_TYPE_LEVEL_HIGH>;
cache-unified;
cache-level = <2>;
- clocks = <&clk 10>;
+ clocks = <&clk NPCM7XX_CLK_AXI>;
arm,shared-override;
};
@@ -109,6 +122,177 @@
clocks = <&clk_refclk>, <&clk_sysbypck>, <&clk_mcbypck>;
};
+ gmac0: eth@f0802000 {
+ device_type = "network";
+ compatible = "snps,dwmac";
+ reg = <0xf0802000 0x2000>;
+ interrupts = <GIC_SPI 14 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "macirq";
+ ethernet = <0>;
+ clocks = <&clk_rg1refck>, <&clk NPCM7XX_CLK_AHB>;
+ clock-names = "stmmaceth", "clk_gmac";
+ pinctrl-names = "default";
+ pinctrl-0 = <&rg1_pins
+ &rg1mdio_pins>;
+ status = "disabled";
+ };
+
+ mc: memory-controller@f0824000 {
+ compatible = "nuvoton,npcm7xx-sdram-edac";
+ reg = <0xf0824000 0x1000>;
+ interrupts = <GIC_SPI 25 IRQ_TYPE_LEVEL_HIGH>;
+ status = "disabled";
+ };
+
+ emc0: eth@f0825000 {
+ device_type = "network";
+ compatible = "nuvoton,npcm750-emc";
+ reg = <0xf0825000 0x1000>;
+ interrupts = <GIC_SPI 16 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 15 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&clk NPCM7XX_CLK_AHB>;
+ clock-names = "clk_emc";
+ pinctrl-names = "default";
+ pinctrl-0 = <&r1_pins
+ &r1err_pins
+ &r1md_pins>;
+ status = "disabled";
+ };
+
+ ehci1:usb@f0806000 {
+ compatible = "nuvoton,npcm750-ehci";
+ reg = <0xf0806000 0x1000>;
+ interrupts = <GIC_SPI 61 IRQ_TYPE_LEVEL_HIGH>;
+ status = "disabled";
+ };
+
+ ohci1: ohci@f0807000 {
+ compatible = "nuvoton,npcm750-ohci";
+ reg = <0xf0807000 0x1000>;
+ interrupts = <GIC_SPI 62 IRQ_TYPE_LEVEL_HIGH>;
+ status = "disabled";
+ };
+
+ sdhci0: sdhci@f0842000 {
+ compatible = "nuvoton,npcm750-sdhci";
+ status = "disabled";
+ reg = <0xf0842000 0x200>;
+ interrupts = <GIC_SPI 26 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&clk NPCM7XX_CLK_AHB>; /*, <&clk_xin>;*/
+ clock-names = "clk_mmc"; /* ,"clk_xin"; */
+ pinctrl-names = "default";
+ pinctrl-0 = <&mmc8_pins
+ &mmc_pins>;
+ };
+
+ sdhci1: sdhci@f0840000 {
+ compatible = "nuvoton,npcm750-sdhci";
+ status = "disabled";
+ reg = <0xf0840000 0x200>;
+ interrupts = <GIC_SPI 27 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&clk NPCM7XX_CLK_AHB>; /*, <&clk_xin>;*/
+ clock-names = "clk_sdhc"; /* ,"clk_xin"; */
+ pinctrl-names = "default";
+ pinctrl-0 = <&sd1_pins>;
+ };
+
+ aes:aes@f0858000 {
+ compatible = "nuvoton,npcm750-aes";
+ reg = <0xf0858000 0x1000>;
+ status = "disabled";
+ clocks = <&clk NPCM7XX_CLK_AHB>;
+ clock-names = "clk_ahb";
+ };
+
+ sha:sha@f085a000 {
+ compatible = "nuvoton,npcm750-sha";
+ reg = <0xf085a000 0x1000>;
+ status = "disabled";
+ clocks = <&clk NPCM7XX_CLK_AHB>;
+ clock-names = "clk_ahb";
+ };
+
+ copr: copr@0 {
+ compatible = "nuvoton,npcm750-copr";
+ interrupts = <GIC_SPI 1 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&clk NPCM7XX_CLK_AHB>;
+ clock-names = "clk_ahb";
+ };
+
+ vdma: vdma@e0800000 {
+ compatible = "nuvoton,npcm750-vdm";
+ reg = <0xe0800000 0x1000
+ 0xf0822000 0x1000>;
+ interrupts = <GIC_SPI 29 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ fiu0: spi@fb000000 {
+ compatible = "nuvoton,npcm750-fiu";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0xfb000000 0x1000>, <0x80000000 0x10000000>;
+ reg-names = "control", "memory";
+ clocks = <&clk NPCM7XX_CLK_AHB>;
+ clock-names = "clk_ahb";
+ status = "disabled";
+ };
+
+ fiu3: spi@c0000000 {
+ compatible = "nuvoton,npcm750-fiu";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0xc0000000 0x1000>, <0xA0000000 0x20000000>;
+ reg-names = "control", "memory";
+ clocks = <&clk NPCM7XX_CLK_AHB>;
+ clock-names = "clk_ahb";
+ pinctrl-names = "default";
+ pinctrl-0 = <&spi3_pins>;
+ status = "disabled";
+ };
+
+ fiux: spi@fb001000 {
+ compatible = "nuvoton,npcm750-fiu";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0xfb001000 0x1000>, <0xf8000000 0x2000000>;
+ reg-names = "control", "memory";
+ clocks = <&clk NPCM7XX_CLK_AHB>;
+ clock-names = "clk_ahb";
+ status = "disabled";
+ };
+
+ dvc: dvc@f0808000 {
+ compatible = "nuvoton,npcm750-dvc";
+ reg = <0xf0808000 0x1000>;
+ interrupts = <0 23 4>;
+ };
+
+ vcd: vcd@f0810000 {
+ compatible = "nuvoton,npcm750-vcd";
+ reg = <0xf0810000 0x10000>;
+ mem-addr = <0x3e200000>;
+ mem-size = <0x600000>;
+ interrupts = <GIC_SPI 22 IRQ_TYPE_LEVEL_HIGH>;
+ status = "disabled";
+ };
+
+ ece: ece@f0820000 {
+ compatible = "nuvoton,npcm750-ece";
+ reg = <0xf0820000 0x2000>;
+ mem-addr = <0x3e800000>;
+ mem-size = <0x600000>;
+ interrupts = <GIC_SPI 24 IRQ_TYPE_LEVEL_HIGH>;
+ status = "disabled";
+ };
+
+ pcimbox: pcimbox@f0848000 {
+ compatible = "nuvoton,npcm750-pci-mbox",
+ "simple-mfd", "syscon";
+ reg = <0xf084C000 0x8
+ 0xf0848000 0x3F00>;
+ interrupts = <GIC_SPI 8 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
apb {
#address-cells = <1>;
#size-cells = <1>;
@@ -116,11 +300,107 @@
interrupt-parent = <&gic>;
ranges = <0x0 0xf0000000 0x00300000>;
+ lpc_kcs: lpc_kcs@7000 {
+ compatible = "nuvoton,npcm750-lpc-kcs",
+ "simple-mfd", "syscon";
+ reg = <0x7000 0x40>;
+ reg-io-width = <1>;
+
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges = <0x0 0x7000 0x40>;
+
+ kcs1: kcs1@0 {
+ compatible = "nuvoton,npcm750-kcs-bmc";
+ reg = <0x0 0x40>;
+ interrupts = <GIC_SPI 9 IRQ_TYPE_LEVEL_HIGH>;
+ kcs_chan = <1>;
+ status = "disabled";
+ };
+
+ kcs2: kcs2@0 {
+ compatible = "nuvoton,npcm750-kcs-bmc";
+ reg = <0x0 0x40>;
+ interrupts = <GIC_SPI 9 IRQ_TYPE_LEVEL_HIGH>;
+ kcs_chan = <2>;
+ status = "disabled";
+ };
+
+ kcs3: kcs3@0 {
+ compatible = "nuvoton,npcm750-kcs-bmc";
+ reg = <0x0 0x40>;
+ interrupts = <GIC_SPI 9 IRQ_TYPE_LEVEL_HIGH>;
+ kcs_chan = <3>;
+ status = "disabled";
+ };
+ };
+
+ lpc_host: lpc_host@7000 {
+ compatible = "nuvoton,npcm750-lpc-host",
+ "simple-mfd", "syscon";
+ reg = <0x7000 0x60>;
+
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges = <0x0 0x7000 0x60>;
+
+ lpc_bpc: lpc_bpc@40 {
+ compatible = "nuvoton,npcm750-lpc-bpc";
+ reg = <0x40 0x20>;
+ interrupts = <GIC_SPI 9 IRQ_TYPE_LEVEL_HIGH>;
+ status = "disabled";
+ };
+ };
+
+ peci: peci-bus@100000 {
+ compatible = "nuvoton,npcm750-peci";
+ reg = <0x100000 0x200>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges = <0x0 0x100000 0x200>;
+
+ peci0: peci-bus@0 {
+ compatible = "nuvoton,npcm750-peci";
+ reg = <0x0 0x200>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ interrupts = <GIC_SPI 6 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&clk NPCM7XX_CLK_APB3>;
+ status = "disabled";
+ };
+ };
+
+ spi0: spi@200000 {
+ compatible = "nuvoton,npcm750-pspi";
+ reg = <0x200000 0x1000>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pspi1_pins>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ interrupts = <GIC_SPI 31 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&clk NPCM7XX_CLK_APB5>;
+ clock-names = "clk_apb5";
+ status = "disabled";
+ };
+
+ spi1: spi@201000 {
+ compatible = "nuvoton,npcm750-pspi";
+ reg = <0x201000 0x1000>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pspi2_pins>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ interrupts = <GIC_SPI 28 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&clk NPCM7XX_CLK_APB5>;
+ clock-names = "clk_apb5";
+ status = "disabled";
+ };
+
timer0: timer@8000 {
compatible = "nuvoton,npcm750-timer";
interrupts = <GIC_SPI 32 IRQ_TYPE_LEVEL_HIGH>;
- reg = <0x8000 0x50>;
- clocks = <&clk 5>;
+ reg = <0x8000 0x1C>;
+ clocks = <&clk NPCM7XX_CLK_TIMER>;
};
watchdog0: watchdog@801C {
@@ -128,7 +408,7 @@
interrupts = <GIC_SPI 47 IRQ_TYPE_LEVEL_HIGH>;
reg = <0x801C 0x4>;
status = "disabled";
- clocks = <&clk 5>;
+ clocks = <&clk NPCM7XX_CLK_TIMER>;
};
watchdog1: watchdog@901C {
@@ -136,7 +416,7 @@
interrupts = <GIC_SPI 48 IRQ_TYPE_LEVEL_HIGH>;
reg = <0x901C 0x4>;
status = "disabled";
- clocks = <&clk 5>;
+ clocks = <&clk NPCM7XX_CLK_TIMER>;
};
watchdog2: watchdog@a01C {
@@ -144,13 +424,13 @@
interrupts = <GIC_SPI 49 IRQ_TYPE_LEVEL_HIGH>;
reg = <0xa01C 0x4>;
status = "disabled";
- clocks = <&clk 5>;
+ clocks = <&clk NPCM7XX_CLK_TIMER>;
};
serial0: serial@1000 {
compatible = "nuvoton,npcm750-uart";
reg = <0x1000 0x1000>;
- clocks = <&clk 6>;
+ clocks = <&clk NPCM7XX_CLK_UART>;
interrupts = <GIC_SPI 2 IRQ_TYPE_LEVEL_HIGH>;
reg-shift = <2>;
status = "disabled";
@@ -159,7 +439,7 @@
serial1: serial@2000 {
compatible = "nuvoton,npcm750-uart";
reg = <0x2000 0x1000>;
- clocks = <&clk 6>;
+ clocks = <&clk NPCM7XX_CLK_UART>;
interrupts = <GIC_SPI 3 IRQ_TYPE_LEVEL_HIGH>;
reg-shift = <2>;
status = "disabled";
@@ -168,7 +448,7 @@
serial2: serial@3000 {
compatible = "nuvoton,npcm750-uart";
reg = <0x3000 0x1000>;
- clocks = <&clk 6>;
+ clocks = <&clk NPCM7XX_CLK_UART>;
interrupts = <GIC_SPI 4 IRQ_TYPE_LEVEL_HIGH>;
reg-shift = <2>;
status = "disabled";
@@ -177,11 +457,849 @@
serial3: serial@4000 {
compatible = "nuvoton,npcm750-uart";
reg = <0x4000 0x1000>;
- clocks = <&clk 6>;
+ clocks = <&clk NPCM7XX_CLK_UART>;
interrupts = <GIC_SPI 5 IRQ_TYPE_LEVEL_HIGH>;
reg-shift = <2>;
status = "disabled";
};
+
+ rng: rng@b000 {
+ compatible = "nuvoton,npcm750-rng";
+ reg = <0xb000 0x1000>;
+ clocks = <&clk NPCM7XX_CLK_APB1>;
+ clock-names = "clk_apb1";
+ status = "disabled";
+ };
+
+ adc: adc@c000 {
+ compatible = "nuvoton,npcm750-adc";
+ reg = <0xc000 0x8>;
+ interrupts = <GIC_SPI 0 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&clk NPCM7XX_CLK_ADC>;
+ status = "disabled";
+ };
+
+ otp:otp@189000 {
+ compatible = "nuvoton,npcm750-otp";
+ reg = <0x189000 0x1000
+ 0x18a000 0x1000>;
+ status = "disabled";
+ clocks = <&clk NPCM7XX_CLK_APB4>;
+ clock-names = "clk_apb4";
+ };
+
+ pwm_fan:pwm-fan-controller@103000 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "nuvoton,npcm750-pwm-fan";
+ reg = <0x103000 0x2000>,
+ <0x180000 0x8000>;
+ reg-names = "pwm", "fan";
+ clocks = <&clk NPCM7XX_CLK_APB3>,
+ <&clk NPCM7XX_CLK_APB4>;
+ clock-names = "pwm","fan";
+ interrupts = <GIC_SPI 96 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 97 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 98 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 99 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 100 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 101 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 102 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 103 IRQ_TYPE_LEVEL_HIGH>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pwm0_pins &pwm1_pins
+ &pwm2_pins &pwm3_pins
+ &pwm4_pins &pwm5_pins
+ &pwm6_pins &pwm7_pins
+ &fanin0_pins &fanin1_pins
+ &fanin2_pins &fanin3_pins
+ &fanin4_pins &fanin5_pins
+ &fanin6_pins &fanin7_pins
+ &fanin8_pins &fanin9_pins
+ &fanin10_pins &fanin11_pins
+ &fanin12_pins &fanin13_pins
+ &fanin14_pins &fanin15_pins>;
+ status = "disabled";
+ };
+
+ i2c0: i2c@80000 {
+ reg = <0x80000 0x1000>;
+ compatible = "nuvoton,npcm750-i2c";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ clocks = <&clk NPCM7XX_CLK_APB2>;
+ bus-frequency = <100000>;
+ interrupts = <GIC_SPI 64 IRQ_TYPE_LEVEL_HIGH>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&smb0_pins>;
+ status = "disabled";
+ };
+
+ i2c1: i2c@81000 {
+ reg = <0x81000 0x1000>;
+ compatible = "nuvoton,npcm750-i2c";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ clocks = <&clk NPCM7XX_CLK_APB2>;
+ bus-frequency = <100000>;
+ interrupts = <GIC_SPI 65 IRQ_TYPE_LEVEL_HIGH>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&smb1_pins>;
+ status = "disabled";
+ };
+
+ i2c2: i2c@82000 {
+ reg = <0x82000 0x1000>;
+ compatible = "nuvoton,npcm750-i2c";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ clocks = <&clk NPCM7XX_CLK_APB2>;
+ bus-frequency = <100000>;
+ interrupts = <GIC_SPI 66 IRQ_TYPE_LEVEL_HIGH>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&smb2_pins>;
+ status = "disabled";
+ };
+
+ i2c3: i2c@83000 {
+ reg = <0x83000 0x1000>;
+ compatible = "nuvoton,npcm750-i2c";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ clocks = <&clk NPCM7XX_CLK_APB2>;
+ bus-frequency = <100000>;
+ interrupts = <GIC_SPI 67 IRQ_TYPE_LEVEL_HIGH>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&smb3_pins>;
+ status = "disabled";
+ };
+
+ i2c4: i2c@84000 {
+ reg = <0x84000 0x1000>;
+ compatible = "nuvoton,npcm750-i2c";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ clocks = <&clk NPCM7XX_CLK_APB2>;
+ bus-frequency = <100000>;
+ interrupts = <GIC_SPI 68 IRQ_TYPE_LEVEL_HIGH>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&smb4_pins>;
+ status = "disabled";
+ };
+
+ i2c5: i2c@85000 {
+ reg = <0x85000 0x1000>;
+ compatible = "nuvoton,npcm750-i2c";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ clocks = <&clk NPCM7XX_CLK_APB2>;
+ bus-frequency = <100000>;
+ interrupts = <GIC_SPI 69 IRQ_TYPE_LEVEL_HIGH>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&smb5_pins>;
+ status = "disabled";
+ };
+
+ i2c6: i2c@86000 {
+ reg = <0x86000 0x1000>;
+ compatible = "nuvoton,npcm750-i2c";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ clocks = <&clk NPCM7XX_CLK_APB2>;
+ bus-frequency = <100000>;
+ interrupts = <GIC_SPI 70 IRQ_TYPE_LEVEL_HIGH>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&smb6_pins>;
+ status = "disabled";
+ };
+
+ i2c7: i2c@87000 {
+ reg = <0x87000 0x1000>;
+ compatible = "nuvoton,npcm750-i2c";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ clocks = <&clk NPCM7XX_CLK_APB2>;
+ bus-frequency = <100000>;
+ interrupts = <GIC_SPI 71 IRQ_TYPE_LEVEL_HIGH>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&smb7_pins>;
+ status = "disabled";
+ };
+
+ i2c8: i2c@88000 {
+ reg = <0x88000 0x1000>;
+ compatible = "nuvoton,npcm750-i2c";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ clocks = <&clk NPCM7XX_CLK_APB2>;
+ bus-frequency = <100000>;
+ interrupts = <GIC_SPI 72 IRQ_TYPE_LEVEL_HIGH>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&smb8_pins>;
+ status = "disabled";
+ };
+
+ i2c9: i2c@89000 {
+ reg = <0x89000 0x1000>;
+ compatible = "nuvoton,npcm750-i2c";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ clocks = <&clk NPCM7XX_CLK_APB2>;
+ bus-frequency = <100000>;
+ interrupts = <GIC_SPI 73 IRQ_TYPE_LEVEL_HIGH>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&smb9_pins>;
+ status = "disabled";
+ };
+
+ i2c10: i2c@8a000 {
+ reg = <0x8a000 0x1000>;
+ compatible = "nuvoton,npcm750-i2c";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ clocks = <&clk NPCM7XX_CLK_APB2>;
+ bus-frequency = <100000>;
+ interrupts = <GIC_SPI 74 IRQ_TYPE_LEVEL_HIGH>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&smb10_pins>;
+ status = "disabled";
+ };
+
+ i2c11: i2c@8b000 {
+ reg = <0x8b000 0x1000>;
+ compatible = "nuvoton,npcm750-i2c";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ clocks = <&clk NPCM7XX_CLK_APB2>;
+ bus-frequency = <100000>;
+ interrupts = <GIC_SPI 75 IRQ_TYPE_LEVEL_HIGH>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&smb11_pins>;
+ status = "disabled";
+ };
+
+ i2c12: i2c@8c000 {
+ reg = <0x8c000 0x1000>;
+ compatible = "nuvoton,npcm750-i2c";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ clocks = <&clk NPCM7XX_CLK_APB2>;
+ bus-frequency = <100000>;
+ interrupts = <GIC_SPI 76 IRQ_TYPE_LEVEL_HIGH>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&smb12_pins>;
+ status = "disabled";
+ };
+
+ i2c13: i2c@8d000 {
+ reg = <0x8d000 0x1000>;
+ compatible = "nuvoton,npcm750-i2c";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ clocks = <&clk NPCM7XX_CLK_APB2>;
+ bus-frequency = <100000>;
+ interrupts = <GIC_SPI 77 IRQ_TYPE_LEVEL_HIGH>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&smb13_pins>;
+ status = "disabled";
+ };
+
+ i2c14: i2c@8e000 {
+ reg = <0x8e000 0x1000>;
+ compatible = "nuvoton,npcm750-i2c";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ clocks = <&clk NPCM7XX_CLK_APB2>;
+ bus-frequency = <100000>;
+ interrupts = <GIC_SPI 78 IRQ_TYPE_LEVEL_HIGH>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&smb14_pins>;
+ status = "disabled";
+ };
+
+ i2c15: i2c@8f000 {
+ reg = <0x8f000 0x1000>;
+ compatible = "nuvoton,npcm750-i2c";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ clocks = <&clk NPCM7XX_CLK_APB2>;
+ bus-frequency = <100000>;
+ interrupts = <GIC_SPI 79 IRQ_TYPE_LEVEL_HIGH>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&smb15_pins>;
+ status = "disabled";
+ };
+
+ gfxi: gfxi@f000e000 {
+ compatible = "nuvoton,npcm750-gfxi", "syscon", "simple-mfd";
+ reg = <0xf000e000 0x100>;
+ };
+
+ };
+ };
+
+ pinctrl: pinctrl@f0800000 {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "nuvoton,npcm750-pinctrl", "syscon", "simple-mfd";
+ ranges = <0 0xf0010000 0x8000>;
+ status = "okay";
+ gpio0: gpio@f0010000 {
+ gpio-controller;
+ #gpio-cells = <2>;
+ reg = <0x0 0x80>;
+ interrupts = <GIC_SPI 116 IRQ_TYPE_LEVEL_HIGH>;
+ gpio-ranges = <&pinctrl 0 0 32>;
+ };
+ gpio1: gpio@f0011000 {
+ gpio-controller;
+ #gpio-cells = <2>;
+ reg = <0x1000 0x80>;
+ interrupts = <GIC_SPI 117 IRQ_TYPE_LEVEL_HIGH>;
+ gpio-ranges = <&pinctrl 0 32 32>;
+ };
+ gpio2: gpio@f0012000 {
+ gpio-controller;
+ #gpio-cells = <2>;
+ reg = <0x2000 0x80>;
+ interrupts = <GIC_SPI 118 IRQ_TYPE_LEVEL_HIGH>;
+ gpio-ranges = <&pinctrl 0 64 32>;
+ };
+ gpio3: gpio@f0013000 {
+ gpio-controller;
+ #gpio-cells = <2>;
+ reg = <0x3000 0x80>;
+ interrupts = <GIC_SPI 119 IRQ_TYPE_LEVEL_HIGH>;
+ gpio-ranges = <&pinctrl 0 96 32>;
+ };
+ gpio4: gpio@f0014000 {
+ gpio-controller;
+ #gpio-cells = <2>;
+ reg = <0x4000 0x80>;
+ interrupts = <GIC_SPI 120 IRQ_TYPE_LEVEL_HIGH>;
+ gpio-ranges = <&pinctrl 0 128 32>;
+ };
+ gpio5: gpio@f0015000 {
+ gpio-controller;
+ #gpio-cells = <2>;
+ reg = <0x5000 0x80>;
+ interrupts = <GIC_SPI 121 IRQ_TYPE_LEVEL_HIGH>;
+ gpio-ranges = <&pinctrl 0 160 32>;
+ };
+ gpio6: gpio@f0016000 {
+ gpio-controller;
+ #gpio-cells = <2>;
+ reg = <0x6000 0x80>;
+ interrupts = <GIC_SPI 122 IRQ_TYPE_LEVEL_HIGH>;
+ gpio-ranges = <&pinctrl 0 192 32>;
+ };
+ gpio7: gpio@f0017000 {
+ gpio-controller;
+ #gpio-cells = <2>;
+ reg = <0x7000 0x80>;
+ interrupts = <GIC_SPI 123 IRQ_TYPE_LEVEL_HIGH>;
+ gpio-ranges = <&pinctrl 0 224 32>;
+ };
+
+ iox1_pins: iox1-pins {
+ groups = "iox1";
+ function = "iox1";
+ };
+ iox2_pins: iox2-pins {
+ groups = "iox2";
+ function = "iox2";
+ };
+ smb1d_pins: smb1d-pins {
+ groups = "smb1d";
+ function = "smb1d";
+ };
+ smb2d_pins: smb2d-pins {
+ groups = "smb2d";
+ function = "smb2d";
+ };
+ lkgpo1_pins: lkgpo1-pins {
+ groups = "lkgpo1";
+ function = "lkgpo1";
+ };
+ lkgpo2_pins: lkgpo2-pins {
+ groups = "lkgpo2";
+ function = "lkgpo2";
+ };
+ ioxh_pins: ioxh-pins {
+ groups = "ioxh";
+ function = "ioxh";
+ };
+ gspi_pins: gspi-pins {
+ groups = "gspi";
+ function = "gspi";
+ };
+ smb5b_pins: smb5b-pins {
+ groups = "smb5b";
+ function = "smb5b";
+ };
+ smb5c_pins: smb5c-pins {
+ groups = "smb5c";
+ function = "smb5c";
+ };
+ lkgpo0_pins: lkgpo0-pins {
+ groups = "lkgpo0";
+ function = "lkgpo0";
+ };
+ pspi2_pins: pspi2-pins {
+ groups = "pspi2";
+ function = "pspi2";
+ };
+ smb4den_pins: smb4den-pins {
+ groups = "smb4den";
+ function = "smb4den";
+ };
+ smb4b_pins: smb4b-pins {
+ groups = "smb4b";
+ function = "smb4b";
+ };
+ smb4c_pins: smb4c-pins {
+ groups = "smb4c";
+ function = "smb4c";
+ };
+ smb15_pins: smb15-pins {
+ groups = "smb15";
+ function = "smb15";
+ };
+ smb4d_pins: smb4d-pins {
+ groups = "smb4d";
+ function = "smb4d";
+ };
+ smb14_pins: smb14-pins {
+ groups = "smb14";
+ function = "smb14";
+ };
+ smb5_pins: smb5-pins {
+ groups = "smb5";
+ function = "smb5";
+ };
+ smb4_pins: smb4-pins {
+ groups = "smb4";
+ function = "smb4";
+ };
+ smb3_pins: smb3-pins {
+ groups = "smb3";
+ function = "smb3";
+ };
+ spi0cs1_pins: spi0cs1-pins {
+ groups = "spi0cs1";
+ function = "spi0cs1";
+ };
+ spi0cs2_pins: spi0cs2-pins {
+ groups = "spi0cs2";
+ function = "spi0cs2";
+ };
+ spi0cs3_pins: spi0cs3-pins {
+ groups = "spi0cs3";
+ function = "spi0cs3";
+ };
+ smb3c_pins: smb3c-pins {
+ groups = "smb3c";
+ function = "smb3c";
+ };
+ smb3b_pins: smb3b-pins {
+ groups = "smb3b";
+ function = "smb3b";
+ };
+ bmcuart0a_pins: bmcuart0a-pins {
+ groups = "bmcuart0a";
+ function = "bmcuart0a";
+ };
+ uart1_pins: uart1-pins {
+ groups = "uart1";
+ function = "uart1";
+ };
+ jtag2_pins: jtag2-pins {
+ groups = "jtag2";
+ function = "jtag2";
+ };
+ bmcuart1_pins: bmcuart1-pins {
+ groups = "bmcuart1";
+ function = "bmcuart1";
+ };
+ uart2_pins: uart2-pins {
+ groups = "uart2";
+ function = "uart2";
+ };
+ bmcuart0b_pins: bmcuart0b-pins {
+ groups = "bmcuart0b";
+ function = "bmcuart0b";
+ };
+ r1err_pins: r1err-pins {
+ groups = "r1err";
+ function = "r1err";
+ };
+ r1md_pins: r1md-pins {
+ groups = "r1md";
+ function = "r1md";
+ };
+ smb3d_pins: smb3d-pins {
+ groups = "smb3d";
+ function = "smb3d";
+ };
+ fanin0_pins: fanin0-pins {
+ groups = "fanin0";
+ function = "fanin0";
+ };
+ fanin1_pins: fanin1-pins {
+ groups = "fanin1";
+ function = "fanin1";
+ };
+ fanin2_pins: fanin2-pins {
+ groups = "fanin2";
+ function = "fanin2";
+ };
+ fanin3_pins: fanin3-pins {
+ groups = "fanin3";
+ function = "fanin3";
+ };
+ fanin4_pins: fanin4-pins {
+ groups = "fanin4";
+ function = "fanin4";
+ };
+ fanin5_pins: fanin5-pins {
+ groups = "fanin5";
+ function = "fanin5";
+ };
+ fanin6_pins: fanin6-pins {
+ groups = "fanin6";
+ function = "fanin6";
+ };
+ fanin7_pins: fanin7-pins {
+ groups = "fanin7";
+ function = "fanin7";
+ };
+ fanin8_pins: fanin8-pins {
+ groups = "fanin8";
+ function = "fanin8";
+ };
+ fanin9_pins: fanin9-pins {
+ groups = "fanin9";
+ function = "fanin9";
+ };
+ fanin10_pins: fanin10-pins {
+ groups = "fanin10";
+ function = "fanin10";
+ };
+ fanin11_pins: fanin11-pins {
+ groups = "fanin11";
+ function = "fanin11";
+ };
+ fanin12_pins: fanin12-pins {
+ groups = "fanin12";
+ function = "fanin12";
+ };
+ fanin13_pins: fanin13-pins {
+ groups = "fanin13";
+ function = "fanin13";
+ };
+ fanin14_pins: fanin14-pins {
+ groups = "fanin14";
+ function = "fanin14";
+ };
+ fanin15_pins: fanin15-pins {
+ groups = "fanin15";
+ function = "fanin15";
+ };
+ pwm0_pins: pwm0-pins {
+ groups = "pwm0";
+ function = "pwm0";
+ };
+ pwm1_pins: pwm1-pins {
+ groups = "pwm1";
+ function = "pwm1";
+ };
+ pwm2_pins: pwm2-pins {
+ groups = "pwm2";
+ function = "pwm2";
+ };
+ pwm3_pins: pwm3-pins {
+ groups = "pwm3";
+ function = "pwm3";
+ };
+ r2_pins: r2-pins {
+ groups = "r2";
+ function = "r2";
+ };
+ r2err_pins: r2err-pins {
+ groups = "r2err";
+ function = "r2err";
+ };
+ r2md_pins: r2md-pins {
+ groups = "r2md";
+ function = "r2md";
+ };
+ ga20kbc_pins: ga20kbc-pins {
+ groups = "ga20kbc";
+ function = "ga20kbc";
+ };
+ smb5d_pins: smb5d-pins {
+ groups = "smb5d";
+ function = "smb5d";
+ };
+ lpc_pins: lpc-pins {
+ groups = "lpc";
+ function = "lpc";
+ };
+ espi_pins: espi-pins {
+ groups = "espi";
+ function = "espi";
+ };
+ rg1_pins: rg1-pins {
+ groups = "rg1";
+ function = "rg1";
+ };
+ rg1mdio_pins: rg1mdio-pins {
+ groups = "rg1mdio";
+ function = "rg1mdio";
+ };
+ rg2_pins: rg2-pins {
+ groups = "rg2";
+ function = "rg2";
+ };
+ ddr_pins: ddr-pins {
+ groups = "ddr";
+ function = "ddr";
+ };
+ smb0_pins: smb0-pins {
+ groups = "smb0";
+ function = "smb0";
+ };
+ smb1_pins: smb1-pins {
+ groups = "smb1";
+ function = "smb1";
+ };
+ smb2_pins: smb2-pins {
+ groups = "smb2";
+ function = "smb2";
+ };
+ smb2c_pins: smb2c-pins {
+ groups = "smb2c";
+ function = "smb2c";
+ };
+ smb2b_pins: smb2b-pins {
+ groups = "smb2b";
+ function = "smb2b";
+ };
+ smb1c_pins: smb1c-pins {
+ groups = "smb1c";
+ function = "smb1c";
+ };
+ smb1b_pins: smb1b-pins {
+ groups = "smb1b";
+ function = "smb1b";
+ };
+ smb8_pins: smb8-pins {
+ groups = "smb8";
+ function = "smb8";
+ };
+ smb9_pins: smb9-pins {
+ groups = "smb9";
+ function = "smb9";
+ };
+ smb10_pins: smb10-pins {
+ groups = "smb10";
+ function = "smb10";
+ };
+ smb11_pins: smb11-pins {
+ groups = "smb11";
+ function = "smb11";
+ };
+ sd1_pins: sd1-pins {
+ groups = "sd1";
+ function = "sd1";
+ };
+ sd1pwr_pins: sd1pwr-pins {
+ groups = "sd1pwr";
+ function = "sd1pwr";
+ };
+ pwm4_pins: pwm4-pins {
+ groups = "pwm4";
+ function = "pwm4";
+ };
+ pwm5_pins: pwm5-pins {
+ groups = "pwm5";
+ function = "pwm5";
+ };
+ pwm6_pins: pwm6-pins {
+ groups = "pwm6";
+ function = "pwm6";
+ };
+ pwm7_pins: pwm7-pins {
+ groups = "pwm7";
+ function = "pwm7";
+ };
+ mmc8_pins: mmc8-pins {
+ groups = "mmc8";
+ function = "mmc8";
+ };
+ mmc_pins: mmc-pins {
+ groups = "mmc";
+ function = "mmc";
+ };
+ mmcwp_pins: mmcwp-pins {
+ groups = "mmcwp";
+ function = "mmcwp";
+ };
+ mmccd_pins: mmccd-pins {
+ groups = "mmccd";
+ function = "mmccd";
+ };
+ mmcrst_pins: mmcrst-pins {
+ groups = "mmcrst";
+ function = "mmcrst";
+ };
+ clkout_pins: clkout-pins {
+ groups = "clkout";
+ function = "clkout";
+ };
+ serirq_pins: serirq-pins {
+ groups = "serirq";
+ function = "serirq";
+ };
+ lpcclk_pins: lpcclk-pins {
+ groups = "lpcclk";
+ function = "lpcclk";
+ };
+ scipme_pins: scipme-pins {
+ groups = "scipme";
+ function = "scipme";
+ };
+ sci_pins: sci-pins {
+ groups = "sci";
+ function = "sci";
+ };
+ smb6_pins: smb6-pins {
+ groups = "smb6";
+ function = "smb6";
+ };
+ smb7_pins: smb7-pins {
+ groups = "smb7";
+ function = "smb7";
+ };
+ pspi1_pins: pspi1-pins {
+ groups = "pspi1";
+ function = "pspi1";
+ };
+ faninx_pins: faninx-pins {
+ groups = "faninx";
+ function = "faninx";
+ };
+ r1_pins: r1-pins {
+ groups = "r1";
+ function = "r1";
+ };
+ spi3_pins: spi3-pins {
+ groups = "spi3";
+ function = "spi3";
+ };
+ spi3cs1_pins: spi3cs1-pins {
+ groups = "spi3cs1";
+ function = "spi3cs1";
+ };
+ spi3quad_pins: spi3quad-pins {
+ groups = "spi3quad";
+ function = "spi3quad";
+ };
+ spi3cs2_pins: spi3cs2-pins {
+ groups = "spi3cs2";
+ function = "spi3cs2";
+ };
+ spi3cs3_pins: spi3cs3-pins {
+ groups = "spi3cs3";
+ function = "spi3cs3";
+ };
+ nprd_smi_pins: nprd-smi-pins {
+ groups = "nprd_smi";
+ function = "nprd_smi";
+ };
+ smb0b_pins: smb0b-pins {
+ groups = "smb0b";
+ function = "smb0b";
+ };
+ smb0c_pins: smb0c-pins {
+ groups = "smb0c";
+ function = "smb0c";
+ };
+ smb0den_pins: smb0den-pins {
+ groups = "smb0den";
+ function = "smb0den";
+ };
+ smb0d_pins: smb0d-pins {
+ groups = "smb0d";
+ function = "smb0d";
+ };
+ ddc_pins: ddc-pins {
+ groups = "ddc";
+ function = "ddc";
+ };
+ rg2mdio_pins: rg2mdio-pins {
+ groups = "rg2mdio";
+ function = "rg2mdio";
+ };
+ wdog1_pins: wdog1-pins {
+ groups = "wdog1";
+ function = "wdog1";
+ };
+ wdog2_pins: wdog2-pins {
+ groups = "wdog2";
+ function = "wdog2";
+ };
+ smb12_pins: smb12-pins {
+ groups = "smb12";
+ function = "smb12";
+ };
+ smb13_pins: smb13-pins {
+ groups = "smb13";
+ function = "smb13";
+ };
+ spix_pins: spix-pins {
+ groups = "spix";
+ function = "spix";
+ };
+ spixcs1_pins: spixcs1-pins {
+ groups = "spixcs1";
+ function = "spixcs1";
+ };
+ clkreq_pins: clkreq-pins {
+ groups = "clkreq";
+ function = "clkreq";
+ };
+ hgpio0_pins: hgpio0-pins {
+ groups = "hgpio0";
+ function = "hgpio0";
+ };
+ hgpio1_pins: hgpio1-pins {
+ groups = "hgpio1";
+ function = "hgpio1";
+ };
+ hgpio2_pins: hgpio2-pins {
+ groups = "hgpio2";
+ function = "hgpio2";
+ };
+ hgpio3_pins: hgpio3-pins {
+ groups = "hgpio3";
+ function = "hgpio3";
+ };
+ hgpio4_pins: hgpio4-pins {
+ groups = "hgpio4";
+ function = "hgpio4";
+ };
+ hgpio5_pins: hgpio5-pins {
+ groups = "hgpio5";
+ function = "hgpio5";
+ };
+ hgpio6_pins: hgpio6-pins {
+ groups = "hgpio6";
+ function = "hgpio6";
+ };
+ hgpio7_pins: hgpio7-pins {
+ groups = "hgpio7";
+ function = "hgpio7";
};
};
};
diff --git a/arch/arm/boot/dts/nuvoton-npcm730-gsj-gpio.dtsi b/arch/arm/boot/dts/nuvoton-npcm730-gsj-gpio.dtsi
new file mode 100644
index 000000000000..53cfd15fa03f
--- /dev/null
+++ b/arch/arm/boot/dts/nuvoton-npcm730-gsj-gpio.dtsi
@@ -0,0 +1,477 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2018 Nuvoton Technology tomer.maimon@nuvoton.com
+
+/ {
+ pinctrl: pinctrl@f0800000 {
+ gpio0pp_pins: gpio0pp-pins {
+ pins = "GPIO0/IOX1DI";
+ bias-disable;
+ drive-push-pull;
+ };
+ gpio1pp_pins: gpio1pp-pins {
+ pins = "GPIO1/IOX1LD";
+ bias-disable;
+ drive-push-pull;
+ };
+ gpio2pp_pins: gpio2pp-pins {
+ pins = "GPIO2/IOX1CK";
+ bias-disable;
+ drive-push-pull;
+ };
+ gpio3pp_pins: gpio3pp-pins {
+ pins = "GPIO3/IOX1D0";
+ bias-disable;
+ drive-push-pull;
+ };
+ gpio4pp_pins: gpio4pp-pins {
+ pins = "GPIO4/IOX2DI/SMB1DSDA";
+ bias-disable;
+ drive-push-pull;
+ };
+ gpio5pp_pins: gpio5pp-pins {
+ pins = "GPIO5/IOX2LD/SMB1DSCL";
+ bias-disable;
+ drive-push-pull;
+ };
+ gpio6pp_pins: gpio6pp-pins {
+ pins = "GPIO6/IOX2CK/SMB2DSDA";
+ bias-disable;
+ drive-push-pull;
+ };
+ gpio7pp_pins: gpio7pp-pins {
+ pins = "GPIO7/IOX2D0/SMB2DSCL";
+ bias-disable;
+ drive-push-pull;
+ };
+ gpio8_pins: gpio8-pins {
+ pins = "GPIO8/LKGPO1";
+ bias-disable;
+ input-enable;
+ };
+ gpio9_pins: gpio9-pins {
+ pins = "GPIO9/LKGPO2";
+ bias-disable;
+ input-enable;
+ };
+ gpio10pp_pins: gpio10pp-pins {
+ pins = "GPIO10/IOXHLD";
+ bias-disable;
+ drive-push-pull;
+ };
+ gpio11pp_pins: gpio11pp-pins {
+ pins = "GPIO11/IOXHCK";
+ bias-disable;
+ drive-push-pull;
+ };
+ gpio12_pins: gpio12-pins {
+ pins = "GPIO12/GSPICK/SMB5BSCL";
+ bias-disable;
+ input-enable;
+ };
+ gpio13_pins: gpio13-pins {
+ pins = "GPIO13/GSPIDO/SMB5BSDA";
+ bias-disable;
+ input-enable;
+ };
+ gpio14_pins: gpio14-pins {
+ pins = "GPIO14/GSPIDI/SMB5CSCL";
+ bias-disable;
+ input-enable;
+ };
+ gpio15od_pins: gpio15od-pins {
+ pins = "GPIO15/GSPICS/SMB5CSDA";
+ bias-disable;
+ drive-open-drain;
+ };
+ gpio17pp_pins: gpio17pp-pins {
+ pins = "GPIO17/PSPI2DI/SMB4DEN";
+ bias-disable;
+ drive-push-pull;
+ };
+ gpio18pp_pins: gpio18pp-pins {
+ pins = "GPIO18/PSPI2D0/SMB4BSDA";
+ bias-disable;
+ drive-push-pull;
+ };
+ gpio19pp_pins: gpio19pp-pins {
+ pins = "GPIO19/PSPI2CK/SMB4BSCL";
+ bias-disable;
+ drive-push-pull;
+ };
+ gpio24pp_pins: gpio24pp-pins {
+ pins = "GPIO24/IOXHDO";
+ bias-disable;
+ drive-push-pull;
+ };
+ gpio25pp_pins: gpio25pp-pins {
+ pins = "GPIO25/IOXHDI";
+ bias-disable;
+ drive-push-pull;
+ };
+ gpio37od_pins: gpio37od-pins {
+ pins = "GPIO37/SMB3CSDA";
+ bias-disable;
+ drive-open-drain;
+ };
+ gpio59pp_pins: gpio59pp-pins {
+ pins = "GPIO59/SMB3DSDA";
+ bias-disable;
+ drive-push-pull;
+ };
+ gpio60_pins: gpio60-pins {
+ pins = "GPIO60/SMB3DSCL";
+ bias-disable;
+ input-enable;
+ };
+ gpio72od_pins: gpio72od-pins {
+ pins = "GPIO72/FANIN8";
+ bias-disable;
+ drive-open-drain;
+ };
+ gpio73od_pins: gpio73od-pins {
+ pins = "GPIO73/FANIN9";
+ bias-disable;
+ drive-open-drain;
+ };
+ gpio74od_pins: gpio74od-pins {
+ pins = "GPIO74/FANIN10";
+ bias-disable;
+ drive-open-drain;
+ };
+ gpio75od_pins: gpio75od-pins {
+ pins = "GPIO75/FANIN11";
+ bias-disable;
+ drive-open-drain;
+ };
+ gpio76od_pins: gpio76od-pins {
+ pins = "GPIO76/FANIN12";
+ bias-disable;
+ drive-open-drain;
+ };
+ gpio77od_pins: gpio77od-pins {
+ pins = "GPIO77/FANIN13";
+ bias-disable;
+ drive-open-drain;
+ };
+ gpio78od_pins: gpio78od-pins {
+ pins = "GPIO78/FANIN14";
+ bias-disable;
+ drive-open-drain;
+ };
+ gpio79od_pins: gpio79od-pins {
+ pins = "GPIO79/FANIN15";
+ bias-disable;
+ drive-open-drain;
+ };
+ gpio83_pins: gpio83-pins {
+ pins = "GPIO83/PWM3";
+ bias-disable;
+ input-enable;
+ };
+ gpio84pp_pins: gpio84pp-pins {
+ pins = "GPIO84/R2TXD0";
+ bias-disable;
+ drive-push-pull;
+ };
+ gpio85pp_pins: gpio85pp-pins {
+ pins = "GPIO85/R2TXD1";
+ bias-disable;
+ drive-push-pull;
+ };
+ gpio86pp_pins: gpio86pp-pins {
+ pins = "GPIO86/R2TXEN";
+ bias-disable;
+ drive-push-pull;
+ };
+ gpio87pp_pins: gpio87pp-pins {
+ pins = "GPIO87/R2RXD0";
+ bias-disable;
+ drive-push-pull;
+ };
+ gpio88pp_pins: gpio88pp-pins {
+ pins = "GPIO88/R2RXD1";
+ bias-disable;
+ drive-push-pull;
+ };
+ gpio89pp_pins: gpio89pp-pins {
+ pins = "GPIO89/R2CRSDV";
+ bias-disable;
+ drive-push-pull;
+ };
+ gpio90pp_pins: gpio90pp-pins {
+ pins = "GPIO90/R2RXERR";
+ bias-disable;
+ drive-push-pull;
+ };
+ gpio91_pins: gpio91-pins {
+ pins = "GPIO91/R2MDC";
+ bias-disable;
+ input-enable;
+ };
+ gpio92_pins: gpio92-pins {
+ pins = "GPIO92/R2MDIO";
+ bias-disable;
+ input-enable;
+ };
+ gpio93pp_pins: gpio93pp-pins {
+ pins = "GPIO93/GA20/SMB5DSCL";
+ bias-disable;
+ drive-push-pull;
+ };
+ gpio94pp_pins: gpio94pp-pins {
+ pins = "GPIO94/nKBRST/SMB5DSDA";
+ bias-disable;
+ drive-push-pull;
+ };
+ gpio95_pins: gpio95-pins {
+ pins = "GPIO95/nLRESET/nESPIRST";
+ bias-disable;
+ input-enable;
+ };
+ gpio125pp_pins: gpio125pp-pins {
+ pins = "GPIO125/SMB1CSCL";
+ bias-disable;
+ drive-push-pull;
+ };
+ gpio126od_pins: gpio126od-pins {
+ pins = "GPIO126/SMB1BSDA";
+ bias-disable;
+ drive-open-drain;
+ };
+ gpio127od_pins: gpio127od-pins {
+ pins = "GPIO127/SMB1BSCL";
+ bias-disable;
+ drive-open-drain;
+ };
+ gpio136_pins: gpio136-pins {
+ pins = "GPIO136/SD1DT0";
+ bias-disable;
+ input-enable;
+ };
+ gpio137_pins: gpio137-pins {
+ pins = "GPIO137/SD1DT1";
+ bias-disable;
+ input-enable;
+ };
+ gpio141_pins: gpio141-pins {
+ pins = "GPIO141/SD1WP";
+ bias-disable;
+ input-enable;
+ };
+ gpio142od_pins: gpio142od-pins {
+ pins = "GPIO142/SD1CMD";
+ bias-disable;
+ drive-open-drain;
+ };
+ gpio143ol_pins: gpio143ol-pins {
+ pins = "GPIO143/SD1CD/SD1PWR";
+ bias-disable;
+ output-low;
+ };
+ gpio144_pins: gpio144-pins {
+ pins = "GPIO144/PWM4";
+ bias-disable;
+ input-enable;
+ };
+ gpio145_pins: gpio145-pins {
+ pins = "GPIO145/PWM5";
+ bias-disable;
+ input-enable;
+ };
+ gpio146_pins: gpio146-pins {
+ pins = "GPIO146/PWM6";
+ bias-disable;
+ input-enable;
+ };
+ gpio147_pins: gpio147-pins {
+ pins = "GPIO147/PWM7";
+ bias-disable;
+ input-enable;
+ };
+ gpio148_pins: gpio148-pins {
+ pins = "GPIO148/MMCDT4";
+ bias-disable;
+ input-enable;
+ };
+ gpio149_pins: gpio149-pins {
+ pins = "GPIO149/MMCDT5";
+ bias-disable;
+ input-enable;
+ };
+ gpio150_pins: gpio150-pins {
+ pins = "GPIO150/MMCDT6";
+ bias-disable;
+ input-enable;
+ };
+ gpio151_pins: gpio151-pins {
+ pins = "GPIO151/MMCDT7";
+ bias-disable;
+ input-enable;
+ };
+ gpio152_pins: gpio152-pins {
+ pins = "GPIO152/MMCCLK";
+ bias-disable;
+ input-enable;
+ };
+ gpio153_pins: gpio153-pins {
+ pins = "GPIO153/MMCWP";
+ bias-disable;
+ input-enable;
+ };
+ gpio154_pins: gpio154-pins {
+ pins = "GPIO154/MMCCMD";
+ bias-disable;
+ input-enable;
+ };
+ gpio155_pins: gpio155-pins {
+ pins = "GPIO155/nMMCCD/nMMCRST";
+ bias-disable;
+ input-enable;
+ };
+ gpio156_pins: gpio156-pins {
+ pins = "GPIO156/MMCDT0";
+ bias-disable;
+ input-enable;
+ };
+ gpio157_pins: gpio157-pins {
+ pins = "GPIO157/MMCDT1";
+ bias-disable;
+ input-enable;
+ };
+ gpio158_pins: gpio158-pins {
+ pins = "GPIO158/MMCDT2";
+ bias-disable;
+ input-enable;
+ };
+ gpio159_pins: gpio159-pins {
+ pins = "GPIO159/MMCDT3";
+ bias-disable;
+ input-enable;
+ };
+ gpio161_pins: gpio161-pins {
+ pins = "GPIO161/nLFRAME/nESPICS";
+ bias-disable;
+ input-enable;
+ };
+ gpio162_pins: gpio162-pins {
+ pins = "GPIO162/SERIRQ";
+ bias-disable;
+ input-enable;
+ };
+ gpio163_pins: gpio163-pins {
+ pins = "GPIO163/LCLK/ESPICLK";
+ bias-disable;
+ input-enable;
+ };
+ gpio164_pins: gpio164-pins {
+ pins = "GPIO164/LAD0/ESPI_IO0";
+ bias-disable;
+ input-enable;
+ };
+ gpio165_pins: gpio165-pins {
+ pins = "GPIO165/LAD1/ESPI_IO1";
+ bias-disable;
+ input-enable;
+ };
+ gpio166_pins: gpio166-pins {
+ pins = "GPIO166/LAD2/ESPI_IO2";
+ bias-disable;
+ input-enable;
+ };
+ gpio167_pins: gpio167-pins {
+ pins = "GPIO167/LAD3/ESPI_IO3";
+ bias-disable;
+ input-enable;
+ };
+ gpio168_pins: gpio168-pins {
+ pins = "GPIO168/nCLKRUN/nESPIALERT";
+ bias-disable;
+ input-enable;
+ };
+ gpio169_pins: gpio169-pins {
+ pins = "GPIO169/nSCIPME";
+ bias-disable;
+ input-enable;
+ };
+ gpio170_pins: gpio170-pins {
+ pins = "GPIO170/nSMI";
+ bias-disable;
+ input-enable;
+ };
+ gpio175od_pins: gpio175od-pins {
+ pins = "GPIO175/PSPI1CK/FANIN19";
+ bias-disable;
+ drive-open-drain;
+ };
+ gpio176od_pins: gpio176od-pins {
+ pins = "GPIO176/PSPI1DO/FANIN18";
+ bias-disable;
+ drive-open-drain;
+ };
+ gpio177_pins: gpio177-pins {
+ pins = "GPIO177/PSPI1DI/FANIN17";
+ bias-disable;
+ input-enable;
+ };
+ gpio190od_pins: gpio190od-pins {
+ pins = "GPIO190/nPRD_SMI";
+ bias-disable;
+ drive-open-drain;
+ };
+ gpio191_pins: gpio191-pins {
+ pins = "GPIO191";
+ bias-disable;
+ input-enable;
+ };
+ gpio192_pins: gpio192-pins {
+ pins = "GPIO192";
+ bias-disable;
+ input-enable;
+ };
+ gpio194pp_pins: gpio194pp-pins {
+ pins = "GPIO194/SMB0BSCL";
+ bias-disable;
+ drive-push-pull;
+ };
+ gpio195od_pins: gpio195od-pins {
+ pins = "GPIO195/SMB0BSDA";
+ bias-disable;
+ drive-open-drain;
+ };
+ gpio196od_pins: gpio196od-pins {
+ pins = "GPIO196/SMB0CSCL";
+ bias-disable;
+ drive-open-drain;
+ };
+ gpio197od_pins: gpio197od-pins {
+ pins = "GPIO197/SMB0DEN";
+ bias-disable;
+ drive-open-drain;
+ };
+ gpio198od_pins: gpio198od-pins {
+ pins = "GPIO198/SMB0DSDA";
+ bias-disable;
+ drive-open-drain;
+ };
+ gpio199od_pins: gpio199od-pins {
+ pins = "GPIO199/SMB0DSCL";
+ bias-disable;
+ drive-open-drain;
+ };
+ gpio200pp_pins: gpio200pp-pins {
+ pins = "GPIO200/R2CK";
+ bias-disable;
+ drive-push-pull;
+ };
+ gpio202od_pins: gpio202od-pins {
+ pins = "GPIO202/SMB0CSDA";
+ bias-disable;
+ drive-open-drain;
+ };
+ gpio203_pins: gpio203-pins {
+ pins = "GPIO203/FANIN16";
+ bias-disable;
+ input-enable;
+ };
+ };
+};
diff --git a/arch/arm/boot/dts/nuvoton-npcm730-gsj.dts b/arch/arm/boot/dts/nuvoton-npcm730-gsj.dts
new file mode 100644
index 000000000000..da7619eb8a0d
--- /dev/null
+++ b/arch/arm/boot/dts/nuvoton-npcm730-gsj.dts
@@ -0,0 +1,523 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2019 Quanta Computer lnc. Fran.Hsu@quantatw.com
+
+/dts-v1/;
+#include "nuvoton-npcm730.dtsi"
+#include "nuvoton-npcm730-gsj-gpio.dtsi"
+/ {
+ model = "Quanta GSJ Board (Device Tree v12)";
+ compatible = "nuvoton,npcm750";
+
+ aliases {
+ ethernet0 = &emc0;
+ ethernet1 = &gmac0;
+ serial3 = &serial3;
+ udc9 = &udc9;
+ i2c1 = &i2c1;
+ i2c2 = &i2c2;
+ i2c3 = &i2c3;
+ i2c4 = &i2c4;
+ i2c8 = &i2c8;
+ i2c9 = &i2c9;
+ i2c10 = &i2c10;
+ i2c11 = &i2c11;
+ i2c12 = &i2c12;
+ i2c15 = &i2c15;
+ fiu0 = &fiu0;
+ };
+
+ chosen {
+ stdout-path = &serial3;
+ };
+
+ memory {
+ reg = <0 0x40000000>;
+ };
+
+ leds {
+ compatible = "gpio-leds";
+
+ led-bmc-live {
+ gpios = <&gpio4 15 GPIO_ACTIVE_HIGH>;
+ linux,default-trigger = "heartbeat";
+ };
+
+ LED_U2_0_LOCATE {
+ gpios = <&gpio0 0 GPIO_ACTIVE_HIGH>;
+ default-state = "off";
+ };
+
+ LED_U2_1_LOCATE {
+ gpios = <&gpio0 1 GPIO_ACTIVE_HIGH>;
+ default-state = "off";
+ };
+
+ LED_U2_2_LOCATE {
+ gpios = <&gpio0 2 GPIO_ACTIVE_HIGH>;
+ default-state = "off";
+ };
+
+ LED_U2_3_LOCATE {
+ gpios = <&gpio0 3 GPIO_ACTIVE_HIGH>;
+ default-state = "off";
+ };
+
+ LED_U2_4_LOCATE {
+ gpios = <&gpio0 10 GPIO_ACTIVE_HIGH>;
+ default-state = "off";
+ };
+
+ LED_U2_5_LOCATE {
+ gpios = <&gpio0 11 GPIO_ACTIVE_HIGH>;
+ default-state = "off";
+ };
+
+ LED_BMC_TRAY_PWRGD {
+ gpios = <&gpio0 19 GPIO_ACTIVE_HIGH>;
+ default-state = "off";
+ };
+
+ LED_U2_7_FAULT {
+ gpios = <&gpio6 8 GPIO_ACTIVE_HIGH>;
+ default-state = "off";
+ };
+
+ LED_U2_6_LOCATE {
+ gpios = <&gpio0 24 GPIO_ACTIVE_HIGH>;
+ default-state = "off";
+ };
+
+ LED_U2_7_LOCATE {
+ gpios = <&gpio0 25 GPIO_ACTIVE_HIGH>;
+ default-state = "off";
+ };
+
+ LED_U2_0_FAULT {
+ gpios = <&gpio2 20 GPIO_ACTIVE_HIGH>;
+ default-state = "off";
+ };
+
+ LED_U2_1_FAULT {
+ gpios = <&gpio2 21 GPIO_ACTIVE_HIGH>;
+ default-state = "off";
+ };
+
+ LED_U2_2_FAULT {
+ gpios = <&gpio2 22 GPIO_ACTIVE_HIGH>;
+ default-state = "off";
+ };
+
+ LED_U2_3_FAULT {
+ gpios = <&gpio2 23 GPIO_ACTIVE_HIGH>;
+ default-state = "off";
+ };
+
+ LED_U2_4_FAULT {
+ gpios = <&gpio2 24 GPIO_ACTIVE_HIGH>;
+ default-state = "off";
+ };
+
+ LED_U2_5_FAULT {
+ gpios = <&gpio2 25 GPIO_ACTIVE_HIGH>;
+ default-state = "off";
+ };
+
+ LED_U2_6_FAULT {
+ gpios = <&gpio2 26 GPIO_ACTIVE_HIGH>;
+ default-state = "off";
+ };
+ };
+};
+
+&fiu0 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&spi0cs1_pins>;
+ status = "okay";
+
+ spi-nor@0 {
+ compatible = "jedec,spi-nor";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ reg = <0>;
+ spi-rx-bus-width = <2>;
+
+ partitions@80000000 {
+ compatible = "fixed-partitions";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ bmc@0{
+ label = "bmc";
+ reg = <0x000000 0x2000000>;
+ };
+ u-boot@0 {
+ label = "u-boot";
+ reg = <0x0000000 0x80000>;
+ read-only;
+ };
+ u-boot-env@100000{
+ label = "u-boot-env";
+ reg = <0x00100000 0x40000>;
+ };
+ kernel@200000 {
+ label = "kernel";
+ reg = <0x0200000 0x600000>;
+ };
+ rofs@800000 {
+ label = "rofs";
+ reg = <0x800000 0x1400000>;
+ };
+ rwfs@1c00000 {
+ label = "rwfs";
+ reg = <0x1c00000 0x300000>;
+ };
+ reserved@1f00000 {
+ label = "reserved";
+ reg = <0x1f00000 0x100000>;
+ };
+ };
+ };
+};
+
+&gmac0 {
+ phy-mode = "rgmii-id";
+ status = "okay";
+};
+
+&mc {
+ status = "okay";
+};
+
+&emc0 {
+ phy-mode = "rmii";
+ use-ncsi;
+ status = "okay";
+};
+
+&ehci1 {
+ status = "okay";
+};
+
+&ohci1 {
+ status = "okay";
+};
+
+&aes {
+ status = "okay";
+};
+
+&sha {
+ status = "okay";
+};
+
+&pcimbox {
+ status = "okay";
+};
+
+&udc9 {
+ status = "okay";
+};
+
+&watchdog1 {
+ status = "okay";
+};
+
+&rng {
+ status = "okay";
+};
+
+&serial0 {
+ status = "okay";
+};
+
+&serial1 {
+ status = "okay";
+};
+
+&serial2 {
+ status = "okay";
+};
+
+&serial3 {
+ status = "okay";
+};
+
+&adc {
+ status = "okay";
+};
+
+&otp {
+ status = "okay";
+};
+
+&i2c1 {
+ status = "okay";
+
+ lm75@5c {
+ compatible = "maxim,max31725";
+ reg = <0x5c>;
+ status = "okay";
+ };
+};
+
+&i2c2 {
+ status = "okay";
+
+ lm75@5c {
+ compatible = "maxim,max31725";
+ reg = <0x5c>;
+ status = "okay";
+ };
+};
+
+&i2c3 {
+ status = "okay";
+
+ lm75@5c {
+ compatible = "maxim,max31725";
+ reg = <0x5c>;
+ };
+};
+
+&i2c4 {
+ status = "okay";
+
+ lm75@5c {
+ compatible = "maxim,max31725";
+ reg = <0x5c>;
+ };
+};
+
+&i2c8 {
+ status = "okay";
+};
+
+&i2c9 {
+ status = "okay";
+
+ eeprom@55 {
+ compatible = "atmel,24c64";
+ reg = <0x55>;
+ };
+};
+
+&i2c10 {
+ status = "okay";
+
+ eeprom@55 {
+ compatible = "atmel,24c64";
+ reg = <0x55>;
+ };
+};
+
+&i2c11 {
+ status = "okay";
+
+ /* P12V Quarter Brick DC/DC Power Module Q54SH12050 @60 */
+ power-brick@36 {
+ compatible = "delta,dps800";
+ reg = <0x36>;
+ };
+
+ hotswap@15 {
+ compatible = "ti,lm5066i";
+ reg = <0x15>;
+ };
+};
+
+&i2c12 {
+ status = "okay";
+
+ ucd90160@6b {
+ compatible = "ti,ucd90160";
+ reg = <0x6b>;
+ };
+};
+
+&i2c15 {
+ status = "okay";
+
+ i2c-switch@75 {
+ compatible = "nxp,pca9548";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0x75>;
+ i2c-mux-idle-disconnect;
+
+ i2c_u20: i2c@0 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0>;
+ };
+
+ i2c_u21: i2c@1 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <1>;
+ };
+
+ i2c_u22: i2c@2 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <2>;
+ };
+
+ i2c_u23: i2c@3 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <3>;
+ };
+
+ i2c_u24: i2c@4 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <4>;
+ };
+
+ i2c_u25: i2c@5 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <5>;
+ };
+
+ i2c_u26: i2c@6 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <6>;
+ };
+
+ i2c_u27: i2c@7 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <7>;
+ };
+ };
+};
+
+&pwm_fan {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pwm0_pins &pwm1_pins &pwm2_pins
+ &fanin0_pins &fanin1_pins
+ &fanin2_pins &fanin3_pins
+ &fanin4_pins &fanin5_pins>;
+ status = "okay";
+
+ fan@0 {
+ reg = <0x00>;
+ fan-tach-ch = /bits/ 8 <0x00 0x01>;
+ cooling-levels = <127 255>;
+ };
+
+ fan@1 {
+ reg = <0x01>;
+ fan-tach-ch = /bits/ 8 <0x02 0x03>;
+ cooling-levels = /bits/ 8 <127 255>;
+ };
+
+ fan@2 {
+ reg = <0x02>;
+ fan-tach-ch = /bits/ 8 <0x04 0x05>;
+ cooling-levels = /bits/ 8 <127 255>;
+ };
+};
+
+&pinctrl {
+ pinctrl-names = "default";
+ pinctrl-0 = <
+ /* GPI pins*/
+ &gpio8_pins
+ &gpio9_pins
+ &gpio12_pins
+ &gpio13_pins
+ &gpio14_pins
+ &gpio60_pins
+ &gpio83_pins
+ &gpio91_pins
+ &gpio92_pins
+ &gpio95_pins
+ &gpio136_pins
+ &gpio137_pins
+ &gpio141_pins
+ &gpio144_pins
+ &gpio145_pins
+ &gpio146_pins
+ &gpio147_pins
+ &gpio148_pins
+ &gpio149_pins
+ &gpio150_pins
+ &gpio151_pins
+ &gpio152_pins
+ &gpio153_pins
+ &gpio154_pins
+ &gpio155_pins
+ &gpio156_pins
+ &gpio157_pins
+ &gpio158_pins
+ &gpio159_pins
+ &gpio161_pins
+ &gpio162_pins
+ &gpio163_pins
+ &gpio164_pins
+ &gpio165_pins
+ &gpio166_pins
+ &gpio167_pins
+ &gpio168_pins
+ &gpio169_pins
+ &gpio170_pins
+ &gpio177_pins
+ &gpio191_pins
+ &gpio192_pins
+ &gpio203_pins
+ /* GPO pins*/
+ &gpio0pp_pins
+ &gpio1pp_pins
+ &gpio2pp_pins
+ &gpio3pp_pins
+ &gpio4pp_pins
+ &gpio5pp_pins
+ &gpio6pp_pins
+ &gpio7pp_pins
+ &gpio10pp_pins
+ &gpio11pp_pins
+ &gpio15od_pins
+ &gpio17pp_pins
+ &gpio18pp_pins
+ &gpio19pp_pins
+ &gpio24pp_pins
+ &gpio25pp_pins
+ &gpio37od_pins
+ &gpio59pp_pins
+ &gpio72od_pins
+ &gpio73od_pins
+ &gpio74od_pins
+ &gpio75od_pins
+ &gpio76od_pins
+ &gpio77od_pins
+ &gpio78od_pins
+ &gpio79od_pins
+ &gpio84pp_pins
+ &gpio85pp_pins
+ &gpio86pp_pins
+ &gpio87pp_pins
+ &gpio88pp_pins
+ &gpio89pp_pins
+ &gpio90pp_pins
+ &gpio93pp_pins
+ &gpio94pp_pins
+ &gpio125pp_pins
+ &gpio126od_pins
+ &gpio127od_pins
+ &gpio142od_pins
+ &gpio143ol_pins
+ &gpio175od_pins
+ &gpio176od_pins
+ &gpio190od_pins
+ &gpio194pp_pins
+ &gpio195od_pins
+ &gpio196od_pins
+ &gpio197od_pins
+ &gpio198od_pins
+ &gpio199od_pins
+ &gpio200pp_pins
+ &gpio202od_pins
+ >;
+};
diff --git a/arch/arm/boot/dts/nuvoton-npcm730.dtsi b/arch/arm/boot/dts/nuvoton-npcm730.dtsi
new file mode 100644
index 000000000000..20e13489b993
--- /dev/null
+++ b/arch/arm/boot/dts/nuvoton-npcm730.dtsi
@@ -0,0 +1,57 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2018 Nuvoton Technology tomer.maimon@nuvoton.com
+// Copyright 2018 Google, Inc.
+
+#include "nuvoton-common-npcm7xx.dtsi"
+
+/ {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ interrupt-parent = <&gic>;
+
+ cpus {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ enable-method = "nuvoton,npcm750-smp";
+
+ cpu@0 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a9";
+ clocks = <&clk NPCM7XX_CLK_CPU>;
+ clock-names = "clk_cpu";
+ reg = <0>;
+ next-level-cache = <&l2>;
+ };
+
+ cpu@1 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a9";
+ clocks = <&clk NPCM7XX_CLK_CPU>;
+ clock-names = "clk_cpu";
+ reg = <1>;
+ next-level-cache = <&l2>;
+ };
+ };
+
+ soc {
+ timer@3fe600 {
+ compatible = "arm,cortex-a9-twd-timer";
+ reg = <0x3fe600 0x20>;
+ interrupts = <GIC_PPI 13 (GIC_CPU_MASK_SIMPLE(2) |
+ IRQ_TYPE_LEVEL_HIGH)>;
+ clocks = <&clk NPCM7XX_CLK_AHB>;
+ };
+ };
+
+ ahb {
+ udc9:udc@f0839000 {
+ compatible = "nuvoton,npcm750-udc";
+ reg = <0xf0839000 0x1000
+ 0xfffd0000 0x800>;
+ interrupts = <GIC_SPI 60 IRQ_TYPE_LEVEL_HIGH>;
+ status = "disabled";
+ clocks = <&clk NPCM7XX_CLK_SU>;
+ clock-names = "clk_usb_bridge";
+ };
+ };
+};
diff --git a/arch/arm/boot/dts/nuvoton-npcm750-evb.dts b/arch/arm/boot/dts/nuvoton-npcm750-evb.dts
index 15f744f1beea..bd1eb6ee380f 100644
--- a/arch/arm/boot/dts/nuvoton-npcm750-evb.dts
+++ b/arch/arm/boot/dts/nuvoton-npcm750-evb.dts
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: GPL-2.0
-// Copyright (c) 2018 Nuvoton Technology tomer.maimon@nuvoton.com
+// Copyright (c) 2018 Nuvoton Technology <tomer.maimon@nuvoton.com>
// Copyright 2018 Google, Inc.
/dts-v1/;
@@ -9,6 +9,50 @@
model = "Nuvoton npcm750 Development Board (Device Tree)";
compatible = "nuvoton,npcm750";
+ aliases {
+ ethernet0 = &emc0;
+ ethernet1 = &emc1;
+ ethernet2 = &gmac0;
+ ethernet3 = &gmac1;
+ serial0 = &serial0;
+ serial1 = &serial1;
+ serial2 = &serial2;
+ serial3 = &serial3;
+ udc0 = &udc0;
+ udc1 = &udc1;
+ udc2 = &udc2;
+ udc3 = &udc3;
+ udc4 = &udc4;
+ udc5 = &udc5;
+ udc6 = &udc6;
+ udc7 = &udc7;
+ udc8 = &udc8;
+ udc9 = &udc9;
+ emmc0 = &sdhci0;
+ emmc1 = &sdhci1;
+ i2c0 = &i2c0;
+ i2c1 = &i2c1;
+ i2c2 = &i2c2;
+ i2c3 = &i2c3;
+ i2c4 = &i2c4;
+ i2c5 = &i2c5;
+ i2c6 = &i2c6;
+ i2c7 = &i2c7;
+ i2c8 = &i2c8;
+ i2c9 = &i2c9;
+ i2c10 = &i2c10;
+ i2c11 = &i2c11;
+ i2c12 = &i2c12;
+ i2c13 = &i2c13;
+ i2c14 = &i2c14;
+ i2c15 = &i2c15;
+ spi0 = &spi0;
+ spi1 = &spi1;
+ fiu0 = &fiu0;
+ fiu1 = &fiu3;
+ fiu2 = &fiux;
+ };
+
chosen {
stdout-path = &serial3;
};
@@ -16,12 +60,219 @@
memory {
reg = <0 0x40000000>;
};
+
+ regulators {
+ compatible = "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ reg_vref1_2: regulator@0 {
+ compatible = "regulator-fixed";
+ reg = <0>;
+ regulator-name = "vref_1_2v";
+ regulator-min-microvolt = <1200000>;
+ regulator-max-microvolt = <1200000>;
+ };
+ reg_vref3_3: regulator@1 {
+ compatible = "regulator-fixed";
+ reg = <0>;
+ regulator-name = "vref_3_3v";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ };
+ };
+};
+
+&gmac0 {
+ phy-mode = "rgmii-id";
+ status = "okay";
+};
+
+&gmac1 {
+ phy-mode = "rgmii-id";
+ status = "okay";
+};
+
+&emc0 {
+ phy-mode = "rmii";
+ status = "okay";
+};
+
+&emc1 {
+ phy-mode = "rmii";
+ status = "okay";
+};
+
+&ehci1 {
+ status = "okay";
+};
+
+&ohci1 {
+ status = "okay";
+};
+
+&udc0 {
+ status = "okay";
+};
+
+&udc1 {
+ status = "okay";
+};
+
+&udc2 {
+ status = "okay";
+};
+
+&udc3 {
+ status = "okay";
+};
+
+&udc4 {
+ status = "okay";
+};
+
+&udc5 {
+ status = "okay";
+};
+
+&udc6 {
+ status = "okay";
+};
+
+&udc7 {
+ status = "okay";
+};
+
+&udc8 {
+ status = "okay";
+};
+
+&udc9 {
+ status = "okay";
+};
+
+&aes {
+ status = "okay";
+};
+
+&sha {
+ status = "okay";
+};
+
+&fiu0 {
+ status = "okay";
+
+ spi-nor@0 {
+ compatible = "jedec,spi-nor";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ spi-rx-bus-width = <2>;
+ reg = <0>;
+ partitions@80000000 {
+ compatible = "fixed-partitions";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ bbuboot1@0 {
+ label = "bb-uboot-1";
+ reg = <0x0000000 0x80000>;
+ read-only;
+ };
+ bbuboot2@80000 {
+ label = "bb-uboot-2";
+ reg = <0x0080000 0x80000>;
+ read-only;
+ };
+ envparam@100000 {
+ label = "env-param";
+ reg = <0x0100000 0x40000>;
+ read-only;
+ };
+ spare@140000 {
+ label = "spare";
+ reg = <0x0140000 0xC0000>;
+ };
+ kernel@200000 {
+ label = "kernel";
+ reg = <0x0200000 0x400000>;
+ };
+ rootfs@600000 {
+ label = "rootfs";
+ reg = <0x0600000 0x700000>;
+ };
+ spare1@D00000 {
+ label = "spare1";
+ reg = <0x0D00000 0x200000>;
+ };
+ spare2@0F00000 {
+ label = "spare2";
+ reg = <0x0F00000 0x200000>;
+ };
+ spare3@1100000 {
+ label = "spare3";
+ reg = <0x1100000 0x200000>;
+ };
+ spare4@1300000 {
+ label = "spare4";
+ reg = <0x1300000 0x0>;
+ };
+ };
+ };
+};
+
+&fiu3 {
+ pinctrl-0 = <&spi3_pins>, <&spi3quad_pins>;
+ status = "okay";
+
+ spi-nor@0 {
+ compatible = "jedec,spi-nor";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ spi-rx-bus-width = <2>;
+ reg = <0>;
+ partitions@A0000000 {
+ compatible = "fixed-partitions";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ system1@0 {
+ label = "spi3-system1";
+ reg = <0x0 0x0>;
+ };
+ };
+ };
+};
+
+&fiux {
+ spix-mode;
+};
+
+&sdhci0 {
+ status = "okay";
+};
+
+&sdhci1 {
+ status = "okay";
+};
+
+&pcimbox {
+ status = "okay";
+};
+
+&vcd {
+ status = "okay";
+};
+
+&ece {
+ status = "okay";
};
&watchdog1 {
status = "okay";
};
+&rng {
+ status = "okay";
+};
+
&serial0 {
status = "okay";
};
@@ -37,3 +288,276 @@
&serial3 {
status = "okay";
};
+
+&adc {
+ /* enable external vref */
+ /*vref-supply = <&reg_vref1_2>;*/
+ status = "okay";
+};
+
+&otp {
+ status = "okay";
+};
+
+&kcs1 {
+ status = "okay";
+};
+
+&kcs2 {
+ status = "okay";
+};
+
+&kcs3 {
+ status = "okay";
+};
+
+
+&lpc_bpc {
+ monitor-ports = <0x80>;
+ status = "okay";
+};
+
+&i2c0 {
+ status = "okay";
+
+ /* lm75 on SVB */
+ lm75@48 {
+ compatible = "lm75";
+ reg = <0x48>;
+ };
+};
+
+&i2c1 {
+ status = "okay";
+
+ /* lm75 on EB */
+ lm75@48 {
+ compatible = "lm75";
+ reg = <0x48>;
+ };
+};
+
+&i2c2 {
+ status = "okay";
+
+ /* tmp100 on EB */
+ tmp100@48 {
+ compatible = "tmp100";
+ reg = <0x48>;
+ };
+};
+
+&i2c6 {
+ status = "okay";
+
+ /* tmp100 on SVB */
+ tmp100@48 {
+ compatible = "tmp100";
+ reg = <0x48>;
+ };
+};
+
+&i2c3 {
+ status = "okay";
+};
+
+&i2c5 {
+ status = "okay";
+};
+
+&i2c7 {
+ status = "okay";
+};
+
+&i2c8 {
+ status = "okay";
+};
+
+&i2c9 {
+ status = "okay";
+};
+
+&i2c10 {
+ status = "okay";
+};
+
+&i2c11 {
+ status = "okay";
+};
+
+&i2c14 {
+ status = "okay";
+};
+
+&i2c15 {
+ /* SVB conflict with pspi2 cs gpio20o_pins */
+ status = "disabled";
+};
+
+&pwm_fan {
+ status = "okay";
+
+ fan@0 {
+ reg = <0x00>;
+ fan-tach-ch = /bits/ 8 <0x00 0x01>;
+ cooling-levels = <127 255>;
+ };
+ fan@1 {
+ reg = <0x01>;
+ fan-tach-ch = /bits/ 8 <0x02 0x03>;
+ cooling-levels = /bits/ 8 <127 255>;
+ };
+ fan@2 {
+ reg = <0x02>;
+ fan-tach-ch = /bits/ 8 <0x04 0x05>;
+ cooling-levels = /bits/ 8 <127 255>;
+ };
+ fan@3 {
+ reg = <0x03>;
+ fan-tach-ch = /bits/ 8 <0x06 0x07>;
+ cooling-levels = /bits/ 8 <127 255>;
+ };
+ fan@4 {
+ reg = <0x04>;
+ fan-tach-ch = /bits/ 8 <0x08 0x09>;
+ cooling-levels = /bits/ 8 <127 255>;
+ };
+ fan@5 {
+ reg = <0x05>;
+ fan-tach-ch = /bits/ 8 <0x0A 0x0B>;
+ cooling-levels = /bits/ 8 <127 255>;
+ };
+ fan@6 {
+ reg = <0x06>;
+ fan-tach-ch = /bits/ 8 <0x0C 0x0D>;
+ cooling-levels = /bits/ 8 <127 255>;
+ };
+ fan@7 {
+ reg = <0x07>;
+ fan-tach-ch = /bits/ 8 <0x0E 0x0F>;
+ cooling-levels = /bits/ 8 <127 255>;
+ };
+};
+
+&spi0 {
+ cs-gpios = <&gpio6 11 GPIO_ACTIVE_LOW>;
+ status = "okay";
+
+ flash@0 {
+ compatible = "winbond,w25q128",
+ "jedec,spi-nor";
+ reg = <0x0>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ spi-max-frequency = <5000000>;
+
+ partition@0 {
+ label = "spi0_spare1";
+ reg = <0x0000000 0x800000>;
+ };
+ partition@1 {
+ label = "spi0_spare2";
+ reg = <0x800000 0x0>;
+ };
+ };
+};
+
+&spi1 {
+ cs-gpios = <&gpio0 20 GPIO_ACTIVE_LOW>;
+ status = "okay";
+
+ flash@0 {
+ compatible = "winbond,w25q128fw",
+ "jedec,spi-nor";
+ reg = <0x0>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ spi-max-frequency = <5000000>;
+
+ partition@0 {
+ label = "spi1_spare1";
+ reg = <0x0000000 0x800000>;
+ };
+ partition@1 {
+ label = "spi1_spare2";
+ reg = <0x800000 0x0>;
+ };
+ };
+};
+
+&gcr {
+ serial_port_mux: mux-controller {
+ compatible = "mmio-mux";
+ #mux-control-cells = <1>;
+
+ mux-reg-masks = <0x38 0x07>;
+ idle-states = <2>; /* Serial port mode 3 (takeover) */
+ };
+};
+
+&peci0 {
+ cmd-timeout-ms = <1000>;
+ pull-down = <0>;
+ host-neg-bit-rate = <15>;
+ status = "okay";
+
+ intel-peci-dimmtemp@30 {
+ compatible = "intel,peci-client";
+ reg = <0x30>;
+ status = "okay";
+ };
+};
+
+&pinctrl {
+ pinctrl-names = "default";
+ pinctrl-0 = < &iox1_pins
+ &gpio8_pins
+ &gpio9o_pins
+ &gpio10_pins
+ &gpio11o_pins
+ &gpio16_pins
+ &gpio24o_pins
+ &gpio25ol_pins
+ &gpio32o_pins
+ &jtag2_pins
+ &gpio61o_pins
+ &gpio62o_pins
+ &gpio63o_pins
+ &gpio64o_pins /* SVB pspi1 enable */
+ &gpio80_pins
+ &gpio81_pins
+ &gpio82_pins
+ &gpio83_pins
+ &lpc_pins
+ &gpio132_pins
+ &gpio133_pins
+ &gpio134_pins
+ &gpio135_pins
+ &gpio144_pins
+ &gpio145_pins
+ &gpio146_pins
+ &gpio147_pins
+ &gpio160_pins
+ &gpio162_pins
+ &gpio168_pins
+ &gpio169_pins
+ &gpio170_pins
+ &gpio187o_pins
+ &gpio190_pins
+ &gpio191o_pins
+ &gpio192o_pins
+ &gpio197ol_pins
+ &ddc_pins
+ &gpio218_pins
+ &gpio219ol_pins
+ &gpio220ol_pins
+ &gpio221o_pins
+ &gpio222_pins
+ &gpio223ol_pins
+ &spix_pins
+ &gpio228ol_pins
+ &gpio231o_pins
+ &gpio255_pins>;
+};
+
+
diff --git a/arch/arm/boot/dts/nuvoton-npcm750-gpio.dtsi b/arch/arm/boot/dts/nuvoton-npcm750-gpio.dtsi
new file mode 100644
index 000000000000..a912910bc7ec
--- /dev/null
+++ b/arch/arm/boot/dts/nuvoton-npcm750-gpio.dtsi
@@ -0,0 +1,2021 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2018 Nuvoton Technology tomer.maimon@nuvoton.com
+
+/ {
+ pinctrl: pinctrl@f0800000 {
+ gpio0o_pins: gpio0o-pins {
+ pins = "GPIO0/IOX1DI";
+ bias-disable;
+ output-high;
+ };
+ gpio1_pins: gpio1-pins {
+ pins = "GPIO1/IOX1LD";
+ bias-disable;
+ input-enable;
+ };
+ gpio2_pins: gpio2-pins {
+ pins = "GPIO2/IOX1CK";
+ bias-disable;
+ input-enable;
+ };
+ gpio2o_pins: gpio2o-pins {
+ pins = "GPIO2/IOX1CK";
+ bias-disable;
+ output_high;
+ };
+ gpio3_pins: gpio3-pins {
+ pins = "GPIO3/IOX1D0";
+ bias-disable;
+ input-enable;
+ };
+ gpio3o_pins: gpio3o-pins {
+ pins = "GPIO3/IOX1D0";
+ bias-disable;
+ output-high;
+ };
+ gpio4_pins: gpio4-pins {
+ pins = "GPIO4/IOX2DI/SMB1DSDA";
+ bias-disable;
+ input-enable;
+ };
+ gpio5_pins: gpio5-pins {
+ pins = "GPIO5/IOX2LD/SMB1DSCL";
+ bias-disable;
+ input-enable;
+ };
+ gpio6_pins: gpio6-pins {
+ pins = "GPIO6/IOX2CK/SMB2DSDA";
+ bias-disable;
+ input-enable;
+ };
+ gpio6o_pins: gpio6o-pins {
+ pins = "GPIO6/IOX2CK/SMB2DSDA";
+ bias-disable;
+ output-high;
+ };
+ gpio6ol_pins: gpio6ol-pins {
+ pins = "GPIO6/IOX2CK/SMB2DSDA";
+ bias-disable;
+ output-low;
+ };
+ gpio7_pins: gpio7-pins {
+ pins = "GPIO7/IOX2D0/SMB2DSCL";
+ bias-disable;
+ input-enable;
+ };
+ gpio7o_pins: gpio7o-pins {
+ pins = "GPIO7/IOX2D0/SMB2DSCL";
+ bias-disable;
+ output-high;
+ };
+ gpio7ol_pins: gpio7ol-pins {
+ pins = "GPIO7/IOX2D0/SMB2DSCL";
+ bias-disable;
+ output-low;
+ };
+ gpio8_pins: gpio8-pins {
+ pins = "GPIO8/LKGPO1";
+ bias-disable;
+ input-enable;
+ };
+ gpio8ol_pins: gpio8ol-pins {
+ pins = "GPIO8/LKGPO1";
+ bias-disable;
+ output-low;
+ };
+ gpio9_pins: gpio9-pins {
+ pins = "GPIO9/LKGPO2";
+ bias-disable;
+ input-enable;
+ };
+ gpio9o_pins: gpio9o-pins {
+ pins = "GPIO9/LKGPO2";
+ bias-disable;
+ output-high;
+ };
+ gpio9ol_pins: gpio9ol-pins {
+ pins = "GPIO9/LKGPO2";
+ bias-disable;
+ output-low;
+ };
+ gpio10_pins: gpio10-pins {
+ pins = "GPIO10/IOXHLD";
+ bias-disable;
+ input-enable;
+ };
+ gpio10ol_pins: gpio10ol-pins {
+ pins = "GPIO10/IOXHLD";
+ bias-disable;
+ output-low;
+ };
+ gpio11_pins: gpio11-pins {
+ pins = "GPIO11/IOXHCK";
+ bias-disable;
+ input-enable;
+ };
+ gpio11o_pins: gpio11o-pins {
+ pins = "GPIO11/IOXHCK";
+ bias-disable;
+ output-high;
+ };
+ gpio11ol_pins: gpio11ol-pins {
+ pins = "GPIO11/IOXHCK";
+ bias-disable;
+ output-low;
+ };
+ gpio12_pins: gpio12-pins {
+ pins = "GPIO12/GSPICK/SMB5BSCL";
+ bias-disable;
+ input-enable;
+ };
+ gpio12o_pins: gpio12o-pins {
+ pins = "GPIO12/GSPICK/SMB5BSCL";
+ bias-disable;
+ output-high;
+ };
+ gpio12ol_pins: gpio12ol-pins {
+ pins = "GPIO12/GSPICK/SMB5BSCL";
+ bias-disable;
+ output-low;
+ };
+ gpio13_pins: gpio13-pins {
+ pins = "GPIO13/GSPIDO/SMB5BSDA";
+ bias-disable;
+ input-enable;
+ };
+ gpio13ol_pins: gpio13ol-pins {
+ pins = "GPIO13/GSPIDO/SMB5BSDA";
+ bias-disable;
+ output-low;
+ };
+ gpio14_pins: gpio14-pins {
+ pins = "GPIO14/GSPIDI/SMB5CSCL";
+ bias-disable;
+ input-enable;
+ };
+ gpio14ol_pins: gpio14ol-pins {
+ pins = "GPIO14/GSPIDI/SMB5CSCL";
+ bias-disable;
+ output-low;
+ };
+ gpio15_pins: gpio15-pins {
+ pins = "GPIO15/GSPICS/SMB5CSDA";
+ bias-disable;
+ input-enable;
+ };
+ gpio15o_pins: gpio15o-pins {
+ pins = "GPIO15/GSPICS/SMB5CSDA";
+ bias-disable;
+ output-high;
+ };
+ gpio16_pins: gpio16-pins {
+ pins = "GPIO16/LKGPO0";
+ bias-disable;
+ input-enable;
+ };
+ gpio16o_pins: gpio16o-pins {
+ pins = "GPIO16/LKGPO0";
+ bias-disable;
+ output-high;
+ };
+ gpio16ol_pins: gpio16ol-pins {
+ pins = "GPIO16/LKGPO0";
+ bias-disable;
+ output-low;
+ };
+ gpio17_pins: gpio17-pins {
+ pins = "GPIO17/PSPI2DI/SMB4DEN";
+ bias-disable;
+ input-enable;
+ };
+ gpio17o_pins: gpio17o-pins {
+ pins = "GPIO17/PSPI2DI/SMB4DEN";
+ bias-disable;
+ output-high;
+ };
+ gpio17ol_pins: gpio17ol-pins {
+ pins = "GPIO17/PSPI2DI/SMB4DEN";
+ bias-disable;
+ output-low;
+ };
+ gpio18_pins: gpio18-pins {
+ pins = "GPIO18/PSPI2D0/SMB4BSDA";
+ bias-disable;
+ input-enable;
+ };
+ gpio18ol_pins: gpio18ol-pins {
+ pins = "GPIO18/PSPI2D0/SMB4BSDA";
+ bias-disable;
+ output-low;
+ };
+ gpio19_pins: gpio19-pins {
+ pins = "GPIO19/PSPI2CK/SMB4BSCL";
+ bias-disable;
+ input-enable;
+ };
+ gpio19ol_pins: gpio19ol-pins {
+ pins = "GPIO19/PSPI2CK/SMB4BSCL";
+ bias-disable;
+ output-low;
+ };
+ gpio20_pins: gpio20-pins {
+ pins = "GPIO20/SMB4CSDA/SMB15SDA";
+ bias-disable;
+ input-enable;
+ };
+ gpio20o_pins: gpio20o-pins {
+ pins = "GPIO20/SMB4CSDA/SMB15SDA";
+ bias-disable;
+ output-high;
+ };
+ gpio20ol_pins: gpio20ol-pins {
+ pins = "GPIO20/SMB4CSDA/SMB15SDA";
+ bias-disable;
+ output-low;
+ };
+ gpio21_pins: gpio21-pins {
+ pins = "GPIO21/SMB4CSCL/SMB15SCL";
+ bias-disable;
+ input-enable;
+ };
+ gpio21ol_pins: gpio21ol-pins {
+ pins = "GPIO21/SMB4CSCL/SMB15SCL";
+ bias-disable;
+ output-low;
+ };
+ gpio22_pins: gpio22-pins {
+ pins = "GPIO22/SMB4DSDA/SMB14SDA";
+ bias-disable;
+ input-enable;
+ };
+ gpio22ol_pins: gpio22ol-pins {
+ pins = "GPIO22/SMB4DSDA/SMB14SDA";
+ bias-disable;
+ output-low;
+ };
+ gpio23_pins: gpio23-pins {
+ pins = "GPIO23/SMB4DSCL/SMB14SCL";
+ bias-disable;
+ input-enable;
+ };
+ gpio23ol_pins: gpio23ol-pins {
+ pins = "GPIO23/SMB4DSCL/SMB14SCL";
+ bias-disable;
+ output-low;
+ };
+ gpio24_pins: gpio24-pins {
+ pins = "GPIO24/IOXHDO";
+ bias-disable;
+ input-enable;
+ };
+ gpio24o_pins: gpio24o-pins {
+ pins = "GPIO24/IOXHDO";
+ bias-disable;
+ output-high;
+ };
+ gpio24ol_pins: gpio24ol-pins {
+ pins = "GPIO24/IOXHDO";
+ bias-disable;
+ output-low;
+ };
+ gpio25_pins: gpio25-pins {
+ pins = "GPIO25/IOXHDI";
+ bias-disable;
+ input-enable;
+ };
+ gpio25o_pins: gpio25o-pins {
+ pins = "GPIO25/IOXHDI";
+ bias-disable;
+ output-high;
+ };
+ gpio25ol_pins: gpio25ol-pins {
+ pins = "GPIO25/IOXHDI";
+ bias-disable;
+ output-low;
+ };
+ gpio26_pins: gpio26-pins {
+ pins = "GPIO26/SMB5SDA";
+ bias-disable;
+ input-enable;
+ };
+ gpio27_pins: gpio27-pins {
+ pins = "GPIO27/SMB5SCL";
+ bias-disable;
+ input-enable;
+ };
+ gpio32_pins: gpio32-pins {
+ pins = "GPIO32/nSPI0CS1";
+ bias-disable;
+ input-enable;
+ };
+ gpio32o_pins: gpio32o-pins {
+ pins = "GPIO32/nSPI0CS1";
+ bias-disable;
+ output-high;
+ };
+ gpio32ol_pins: gpio32ol-pins {
+ pins = "GPIO32/nSPI0CS1";
+ bias-disable;
+ output-low;
+ };
+ gpio37_pins: gpio37-pins {
+ pins = "GPIO37/SMB3CSDA";
+ bias-disable;
+ input-enable;
+ };
+ gpio37o_pins: gpio37o-pins {
+ pins = "GPIO37/SMB3CSDA";
+ bias-disable;
+ output-high;
+ };
+ gpio37ol_pins: gpio37ol-pins {
+ pins = "GPIO37/SMB3CSDA";
+ bias-disable;
+ output-low;
+ };
+ gpio38_pins: gpio38-pins {
+ pins = "GPIO38/SMB3CSCL";
+ bias-disable;
+ input-enable;
+ };
+ gpio38o_pins: gpio38o-pins {
+ pins = "GPIO38/SMB3CSCL";
+ bias-disable;
+ output-high;
+ };
+ gpio38ol_pins: gpio38ol-pins {
+ pins = "GPIO38/SMB3CSCL";
+ bias-disable;
+ output-low;
+ };
+ gpio39_pins: gpio39-pins {
+ pins = "GPIO39/SMB3BSDA";
+ bias-disable;
+ input-enable;
+ };
+ gpio39o_pins: gpio39o-pins {
+ pins = "GPIO39/SMB3BSDA";
+ bias-disable;
+ output-high;
+ };
+ gpio39ol_pins: gpio39ol-pins {
+ pins = "GPIO39/SMB3BSDA";
+ bias-disable;
+ output-low;
+ };
+ gpio40_pins: gpio40-pins {
+ pins = "GPIO40/SMB3BSCL";
+ bias-disable;
+ input-enable;
+ };
+ gpio40o_pins: gpio40o-pins {
+ pins = "GPIO40/SMB3BSCL";
+ bias-disable;
+ output-high;
+ };
+ gpio40ol_pins: gpio40ol-pins {
+ pins = "GPIO40/SMB3BSCL";
+ bias-disable;
+ output-low;
+ };
+ gpio41_pins: gpio41-pins {
+ pins = "GPIO41/BSPRXD";
+ input-enable;
+ };
+ gpio42_pins: gpio42-pins {
+ pins = "GPO42/BSPTXD/STRAP11";
+ bias-disable;
+ input-enable;
+ };
+ gpio43_pins: gpio43-pins {
+ pins = "GPIO43/RXD1/JTMS2/BU1RXD";
+ bias-disable;
+ input-enable;
+ };
+ gpio44_pins: gpio44-pins {
+ pins = "GPIO44/nCTS1/JTDI2/BU1CTS";
+ bias-disable;
+ input-enable;
+ };
+ gpio45_pins: gpio45-pins {
+ pins = "GPIO45/nDCD1/JTDO2";
+ bias-disable;
+ input-enable;
+ };
+ gpio46_pins: gpio46-pins {
+ pins = "GPIO46/nDSR1/JTCK2";
+ bias-disable;
+ input-enable;
+ };
+ gpio47_pins: gpio47-pins {
+ pins = "GPIO47/nRI1/JCP_RDY2";
+ bias-disable;
+ input-enable;
+ };
+ gpio48_pins: gpio48-pins {
+ pins = "GPIO48/TXD2/BSPTXD";
+ bias-disable;
+ input-enable;
+ };
+ gpio49_pins: gpio49-pins {
+ pins = "GPIO49/RXD2/BSPRXD";
+ bias-disable;
+ input-enable;
+ };
+ gpio50_pins: gpio50-pins {
+ pins = "GPIO50/nCTS2";
+ bias-disable;
+ input-enable;
+ };
+ gpio50ol_pins: gpio50ol-pins {
+ pins = "GPIO50/nCTS2";
+ bias-disable;
+ output-low;
+ };
+ gpio51_pins: gpio51-pins {
+ pins = "GPO51/nRTS2/STRAP2";
+ bias-disable;
+ input-enable;
+ };
+ gpio51o_pins: gpio51o-pins {
+ pins = "GPO51/nRTS2/STRAP2";
+ bias-disable;
+ output-high;
+ };
+ gpio52_pins: gpio52-pins {
+ pins = "GPIO52/nDCD2";
+ bias-disable;
+ input-enable;
+ };
+ gpio52ol_pins: gpio52ol-pins {
+ pins = "GPIO52/nDCD2";
+ bias-disable;
+ output-low;
+ };
+ gpio53_pins: gpio53-pins {
+ pins = "GPO53/nDTR2_BOUT2/STRAP1";
+ bias-disable;
+ input-enable;
+ };
+ gpio53o_pins: gpio53o-pins {
+ pins = "GPO53/nDTR2_BOUT2/STRAP1";
+ bias-disable;
+ output-high;
+ };
+ gpio54_pins: gpio54-pins {
+ pins = "GPIO54/nDSR2";
+ bias-disable;
+ input-enable;
+ };
+ gpio54ol_pins: gpio54ol-pins {
+ pins = "GPIO54/nDSR2";
+ bias-disable;
+ output-low;
+ };
+ gpio55_pins: gpio55-pins {
+ pins = "GPIO55/nRI2";
+ bias-disable;
+ input-enable;
+ };
+ gpio55ol_pins: gpio55ol-pins {
+ pins = "GPIO55/nRI2";
+ bias-disable;
+ output-low;
+ };
+ gpio56_pins: gpio56-pins {
+ pins = "GPIO56/R1RXERR";
+ bias-disable;
+ input-enable;
+ };
+ gpio57_pins: gpio57-pins {
+ pins = "GPIO57/R1MDC";
+ bias-disable;
+ input-enable;
+ };
+ gpio57ol_pins: gpio57ol-pins {
+ pins = "GPIO57/R1MDC";
+ bias-disable;
+ output-low;
+ };
+ gpio58_pins: gpio58-pins {
+ pins = "GPIO58/R1MDIO";
+ bias-disable;
+ input-enable;
+ };
+ gpio58ol_pins: gpio58ol-pins {
+ pins = "GPIO58/R1MDIO";
+ bias-disable;
+ output-low;
+ };
+ gpio59_pins: gpio59-pins {
+ pins = "GPIO59/SMB3DSDA";
+ bias-disable;
+ input-enable;
+ };
+ gpio59o_pins: gpio59o-pins {
+ pins = "GPIO59/SMB3DSDA";
+ bias-disable;
+ output-high;
+ };
+ gpio59ol_pins: gpio59ol-pins {
+ pins = "GPIO59/SMB3DSDA";
+ bias-disable;
+ output-low;
+ };
+ gpio60_pins: gpio60-pins {
+ pins = "GPIO60/SMB3DSCL";
+ bias-disable;
+ input-enable;
+ };
+ gpio60o_pins: gpio60o-pins {
+ pins = "GPIO60/SMB3DSCL";
+ bias-disable;
+ output-high;
+ };
+ gpio60ol_pins: gpio60ol-pins {
+ pins = "GPIO60/SMB3DSCL";
+ bias-disable;
+ output-low;
+ };
+ gpio61_pins: gpio61-pins {
+ pins = "GPO61/nDTR1_BOUT1/STRAP6";
+ bias-disable;
+ input-enable;
+ };
+ gpio61o_pins: gpio61o-pins {
+ pins = "GPO61/nDTR1_BOUT1/STRAP6";
+ bias-disable;
+ output-high;
+ };
+ gpio62_pins: gpio62-pins {
+ pins = "GPO62/nRTST1/STRAP5";
+ bias-disable;
+ input-enable;
+ };
+ gpio62o_pins: gpio62o-pins {
+ pins = "GPO62/nRTST1/STRAP5";
+ bias-disable;
+ output-high;
+ };
+ gpio63_pins: gpio63-pins {
+ pins = "GPO63/TXD1/STRAP4";
+ bias-disable;
+ input-enable;
+ };
+ gpio63o_pins: gpio63o-pins {
+ pins = "GPO63/TXD1/STRAP4";
+ bias-disable;
+ output-high;
+ };
+ gpio64_pins: gpio64-pins {
+ pins = "GPIO64/FANIN0";
+ bias-disable;
+ input-enable;
+ };
+ gpio64o_pins: gpio64o-pins {
+ pins = "GPIO64/FANIN0";
+ bias-disable;
+ output-high;
+ };
+ gpio65_pins: gpio65-pins {
+ pins = "GPIO65/FANIN1";
+ bias-disable;
+ input-enable;
+ };
+ gpio66_pins: gpio66-pins {
+ pins = "GPIO66/FANIN2";
+ bias-disable;
+ input-enable;
+ };
+ gpio67_pins: gpio67-pins {
+ pins = "GPIO67/FANIN3";
+ bias-disable;
+ input-enable;
+ };
+ gpio68_pins: gpio68-pins {
+ pins = "GPIO68/FANIN4";
+ bias-disable;
+ input-enable;
+ };
+ gpio69_pins: gpio69-pins {
+ pins = "GPIO69/FANIN5";
+ bias-disable;
+ input-enable;
+ };
+ gpio69ol_pins: gpio69ol-pins {
+ pins = "GPIO69/FANIN5";
+ bias-disable;
+ output-low;
+ };
+ gpio70_pins: gpio70-pins {
+ pins = "GPIO70/FANIN6";
+ bias-disable;
+ input-enable;
+ };
+ gpio71_pins: gpio71-pins {
+ pins = "GPIO71/FANIN7";
+ bias-disable;
+ input-enable;
+ };
+ gpio72_pins: gpio72-pins {
+ pins = "GPIO72/FANIN8";
+ bias-disable;
+ input-enable;
+ };
+ gpio72ol_pins: gpio72ol-pins {
+ pins = "GPIO72/FANIN8";
+ bias-disable;
+ output-low;
+ };
+ gpio73_pins: gpio73-pins {
+ pins = "GPIO73/FANIN9";
+ bias-disable;
+ input-enable;
+ };
+ gpio73ol_pins: gpio73ol-pins {
+ pins = "GPIO73/FANIN9";
+ bias-disable;
+ output-low;
+ };
+ gpio74_pins: gpio74-pins {
+ pins = "GPIO74/FANIN10";
+ bias-disable;
+ input-enable;
+ };
+ gpio74ol_pins: gpio74ol-pins {
+ pins = "GPIO74/FANIN10";
+ bias-disable;
+ output-low;
+ };
+ gpio75_pins: gpio75-pins {
+ pins = "GPIO75/FANIN11";
+ bias-disable;
+ input-enable;
+ };
+ gpio75ol_pins: gpio75ol-pins {
+ pins = "GPIO75/FANIN11";
+ bias-disable;
+ output-low;
+ };
+ gpio76_pins: gpio76-pins {
+ pins = "GPIO76/FANIN12";
+ bias-disable;
+ input-enable;
+ };
+ gpio76ol_pins: gpio76ol-pins {
+ pins = "GPIO76/FANIN12";
+ bias-disable;
+ output-low;
+ };
+ gpio77_pins: gpio77-pins {
+ pins = "GPIO77/FANIN13";
+ bias-disable;
+ input-enable;
+ };
+ gpio77ol_pins: gpio77ol-pins {
+ pins = "GPIO77/FANIN13";
+ bias-disable;
+ output-low;
+ };
+ gpio78_pins: gpio78-pins {
+ pins = "GPIO78/FANIN14";
+ bias-disable;
+ input-enable;
+ };
+ gpio78ol_pins: gpio78ol-pins {
+ pins = "GPIO78/FANIN14";
+ bias-disable;
+ output-low;
+ };
+ gpio79_pins: gpio79-pins {
+ pins = "GPIO79/FANIN15";
+ bias-disable;
+ input-enable;
+ };
+ gpio79ol_pins: gpio79ol-pins {
+ pins = "GPIO79/FANIN15";
+ bias-disable;
+ output-low;
+ };
+ gpio80_pins: gpio80-pins {
+ pins = "GPIO80/PWM0";
+ bias-disable;
+ input-enable;
+ };
+ gpio81_pins: gpio81-pins {
+ pins = "GPIO81/PWM1";
+ bias-disable;
+ input-enable;
+ };
+ gpio82_pins: gpio82-pins {
+ pins = "GPIO82/PWM2";
+ bias-disable;
+ input-enable;
+ };
+ gpio83_pins: gpio83-pins {
+ pins = "GPIO83/PWM3";
+ bias-disable;
+ input-enable;
+ };
+ gpio84_pins: gpio84-pins {
+ pins = "GPIO84/R2TXD0";
+ bias-disable;
+ input-enable;
+ };
+ gpio84o_pins: gpio84ol-pins {
+ pins = "GPIO84/R2TXD0";
+ bias-disable;
+ output-high;
+ };
+ gpio85_pins: gpio85-pins {
+ pins = "GPIO85/R2TXD1";
+ bias-disable;
+ input-enable;
+ };
+ gpio85o_pins: gpio85o-pins {
+ pins = "GPIO85/R2TXD1";
+ bias-disable;
+ output-high;
+ };
+ gpio86_pins: gpio86-pins {
+ pins = "GPIO86/R2TXEN";
+ bias-disable;
+ input-enable;
+ };
+ gpio86o_pins: gpio86o-pins {
+ pins = "GPIO86/R2TXEN";
+ bias-disable;
+ output-high;
+ };
+ gpio87_pins: gpio87-pins {
+ pins = "GPIO87/R2RXD0";
+ bias-disable;
+ input-enable;
+ };
+ gpio87o_pins: gpio87o-pins {
+ pins = "GPIO87/R2RXD0";
+ bias-disable;
+ output-high;
+ };
+ gpio88_pins: gpio88-pins {
+ pins = "GPIO88/R2RXD1";
+ bias-disable;
+ input-enable;
+ };
+ gpio88ol_pins: gpio88ol-pins {
+ pins = "GPIO88/R2RXD1";
+ bias-disable;
+ output-low;
+ };
+ gpio89_pins: gpio89-pins {
+ pins = "GPIO89/R2CRSDV";
+ bias-disable;
+ input-enable;
+ };
+ gpio89ol_pins: gpio89ol-pins {
+ pins = "GPIO89/R2CRSDV";
+ bias-disable;
+ output-low;
+ };
+ gpio90_pins: gpio90-pins {
+ pins = "GPIO90/R2RXERR";
+ bias-disable;
+ input-enable;
+ };
+ gpio90o_pins: gpio90o0-pins {
+ pins = "GPIO90/R2RXERR";
+ bias-disable;
+ output-high;
+ };
+ gpio90ol_pins: gpio90ol-pins {
+ pins = "GPIO90/R2RXERR";
+ bias-disable;
+ output-low;
+ };
+ gpio91_pins: gpio91-pins {
+ pins = "GPIO91/R2MDC";
+ bias-disable;
+ input-enable;
+ };
+ gpio91o_pins: gpio91o-pins {
+ pins = "GPIO91/R2MDC";
+ bias-disable;
+ output-high;
+ };
+ gpio91ol_pins: gpio91ol-pins {
+ pins = "GPIO91/R2MDC";
+ bias-disable;
+ output-low;
+ };
+ gpio92_pins: gpio92-pins {
+ pins = "GPIO92/R2MDIO";
+ bias-disable;
+ input-enable;
+ };
+ gpio92o_pins: gpio92o-pins {
+ pins = "GPIO92/R2MDIO";
+ bias-disable;
+ output-high;
+ };
+ gpio92ol_pins: gpio92ol-pins {
+ pins = "GPIO92/R2MDIO";
+ bias-disable;
+ output-low;
+ };
+ gpio93_pins: gpio93-pins {
+ pins = "GPIO93/GA20/SMB5DSCL";
+ bias-disable;
+ input-enable;
+ };
+ gpio93ol_pins: gpio93ol-pins {
+ pins = "GPIO93/GA20/SMB5DSCL";
+ bias-disable;
+ output-low;
+ };
+ gpio94_pins: gpio94-pins {
+ pins = "GPIO94/nKBRST/SMB5DSDA";
+ bias-disable;
+ input-enable;
+ };
+ gpio94o_pins: gpio94o-pins {
+ pins = "GPIO94/nKBRST/SMB5DSDA";
+ bias-disable;
+ output-high;
+ };
+ gpio95_pins: gpio95-pins {
+ pins = "GPIO95/nLRESET/nESPIRST";
+ bias-disable;
+ input-enable;
+ };
+ gpio96_pins: gpio96-pins {
+ pins = "GPIO96/RG1TXD0";
+ bias-disable;
+ input-enable;
+ };
+ gpio96ol_pins: gpio96ol-pins {
+ pins = "GPIO96/RG1TXD0";
+ bias-disable;
+ output-low;
+ };
+ gpio97_pins: gpio97-pins {
+ pins = "GPIO97/RG1TXD1";
+ bias-disable;
+ input-enable;
+ };
+ gpio97ol_pins: gpio97ol-pins {
+ pins = "GPIO97/RG1TXD1";
+ bias-disable;
+ output-low;
+ };
+ gpio98_pins: gpio98-pins {
+ pins = "GPIO98/RG1TXD2";
+ bias-disable;
+ input-enable;
+ };
+ gpio98ol_pins: gpio98ol-pins {
+ pins = "GPIO98/RG1TXD2";
+ bias-disable;
+ output-low;
+ };
+ gpio99_pins: gpio99-pins {
+ pins = "GPIO99/RG1TXD3";
+ bias-disable;
+ input-enable;
+ };
+ gpio99ol_pins: gpio99ol-pins {
+ pins = "GPIO99/RG1TXD3";
+ bias-disable;
+ output-low;
+ };
+ gpio100_pins: gpio100-pins {
+ pins = "GPIO100/RG1TXC";
+ bias-disable;
+ input-enable;
+ };
+ gpio100ol_pins: gpio100ol-pins {
+ pins = "GPIO100/RG1TXC";
+ bias-disable;
+ output-low;
+ };
+ gpio101_pins: gpio101-pins {
+ pins = "GPIO101/RG1TXCTL";
+ bias-disable;
+ input-enable;
+ };
+ gpio101ol_pins: gpio101ol-pins {
+ pins = "GPIO101/RG1TXCTL";
+ bias-disable;
+ output-low;
+ };
+ gpio102_pins: gpio102-pins {
+ pins = "GPIO102/RG1RXD0";
+ bias-disable;
+ input-enable;
+ };
+ gpio102ol_pins: gpio102ol-pins {
+ pins = "GPIO102/RG1RXD0";
+ bias-disable;
+ output-low;
+ };
+ gpio103_pins: gpio103-pins {
+ pins = "GPIO103/RG1RXD1";
+ bias-disable;
+ input-enable;
+ };
+ gpio103ol_pins: gpio103ol-pins {
+ pins = "GPIO103/RG1RXD1";
+ bias-disable;
+ output-low;
+ };
+ gpio104_pins: gpio104-pins {
+ pins = "GPIO104/RG1RXD2";
+ bias-disable;
+ input-enable;
+ };
+ gpio104ol_pins: gpio104ol-pins {
+ pins = "GPIO104/RG1RXD2";
+ bias-disable;
+ output-low;
+ };
+ gpio105_pins: gpio105-pins {
+ pins = "GPIO105/RG1RXD3";
+ bias-disable;
+ input-enable;
+ };
+ gpio105ol_pins: gpio105ol-pins {
+ pins = "GPIO105/RG1RXD3";
+ bias-disable;
+ output-low;
+ };
+ gpio106_pins: gpio106-pins {
+ pins = "GPIO106/RG1RXC";
+ bias-disable;
+ input-enable;
+ };
+ gpio106ol_pins: gpio106ol-pins {
+ pins = "GPIO106/RG1RXC";
+ bias-disable;
+ output-low;
+ };
+ gpio107_pins: gpio107-pins {
+ pins = "GPIO107/RG1RXCTL";
+ bias-disable;
+ input-enable;
+ };
+ gpio107ol_pins: gpio107ol-pins {
+ pins = "GPIO107/RG1RXCTL";
+ bias-disable;
+ output-low;
+ };
+ gpio108_pins: gpio108-pins {
+ pins = "GPIO108/RG1MDC";
+ bias-disable;
+ input-enable;
+ };
+ gpio108ol_pins: gpio108ol-pins {
+ pins = "GPIO108/RG1MDC";
+ bias-disable;
+ output-low;
+ };
+ gpio109_pins: gpio109-pins {
+ pins = "GPIO109/RG1MDIO";
+ bias-disable;
+ input-enable;
+ };
+ gpio109ol_pins: gpio109ol-pins {
+ pins = "GPIO109/RG1MDIO";
+ bias-disable;
+ output-low;
+ };
+ gpio110_pins: gpio110-pins {
+ pins = "GPIO110/RG2TXD0/DDRV0";
+ bias-disable;
+ input-enable;
+ };
+ gpio110ol_pins: gpio110ol-pins {
+ pins = "GPIO110/RG2TXD0/DDRV0";
+ bias-disable;
+ output-low;
+ };
+ gpio111_pins: gpio111-pins {
+ pins = "GPIO111/RG2TXD1/DDRV1";
+ bias-disable;
+ input-enable;
+ };
+ gpio111ol_pins: gpio111ol-pins {
+ pins = "GPIO111/RG2TXD1/DDRV1";
+ bias-disable;
+ output-low;
+ };
+ gpio112_pins: gpio112-pins {
+ pins = "GPIO112/RG2TXD2/DDRV2";
+ bias-disable;
+ input-enable;
+ };
+ gpio112ol_pins: gpio112ol-pins {
+ pins = "GPIO112/RG2TXD2/DDRV2";
+ bias-disable;
+ output-low;
+ };
+ gpio113_pins: gpio113-pins {
+ pins = "GPIO113/RG2TXD3/DDRV3";
+ bias-disable;
+ input-enable;
+ };
+ gpio113ol_pins: gpio113ol-pins {
+ pins = "GPIO113/RG2TXD3/DDRV3";
+ bias-disable;
+ output-low;
+ };
+ gpio118_pins: gpio118-pins {
+ pins = "GPIO118/SMB2SCL";
+ bias-disable;
+ input-enable;
+ };
+ gpio119_pins: gpio119-pins {
+ pins = "GPIO119/SMB2SDA";
+ bias-disable;
+ input-enable;
+ };
+ gpio120_pins: gpio120-pins {
+ pins = "GPIO120/SMB2CSDA";
+ bias-disable;
+ input-enable;
+ };
+ gpio121_pins: gpio121-pins {
+ pins = "GPIO121/SMB2CSCL";
+ bias-disable;
+ input-enable;
+ };
+ gpio122_pins: gpio122-pins {
+ pins = "GPIO122/SMB2BSDA";
+ bias-disable;
+ input-enable;
+ };
+ gpio123_pins: gpio123-pins {
+ pins = "GPIO123/SMB2BSCL";
+ bias-disable;
+ input-enable;
+ };
+ gpio123_pins: gpio123-pins {
+ pins = "GPIO123/SMB2BSCL";
+ bias-disable;
+ input-enable;
+ };
+ gpio124_pins: gpio124-pins {
+ pins = "GPIO124/SMB1CSDA";
+ bias-disable;
+ input-enable;
+ };
+ gpio125_pins: gpio125-pins {
+ pins = "GPIO125/SMB1CSCL";
+ bias-disable;
+ input-enable;
+ };
+ gpio126_pins: gpio126-pins {
+ pins = "GPIO126/SMB1BSDA";
+ bias-disable;
+ input-enable;
+ };
+ gpio127_pins: gpio127-pins {
+ pins = "GPIO127/SMB1BSCL";
+ bias-disable;
+ input-enable;
+ };
+ gpio128o_pins: gpio128o-pins {
+ pins = "GPIO128/SMB8SCL";
+ bias-disable;
+ output-high;
+ };
+ gpio130_pins: gpio130-pins {
+ pins = "GPIO130/SMB9SCL";
+ bias-disable;
+ input-enable;
+ };
+ gpio131_pins: gpio131-pins {
+ pins = "GPIO131/SMB9SDA";
+ bias-disable;
+ input-enable;
+ };
+ gpio132_pins: gpio132-pins {
+ pins = "GPIO132/SMB10SCL";
+ bias-disable;
+ input-enable;
+ };
+ gpio133_pins: gpio133-pins {
+ pins = "GPIO133/SMB10SDA";
+ bias-disable;
+ input-enable;
+ };
+ gpio134_pins: gpio134-pins {
+ pins = "GPIO134/SMB11SCL";
+ bias-disable;
+ input-enable;
+ };
+ gpio135_pins: gpio135-pins {
+ pins = "GPIO135/SMB11SDA";
+ bias-disable;
+ input-enable;
+ };
+ gpio136_pins: gpio136-pins {
+ pins = "GPIO136/SD1DT0";
+ bias-disable;
+ input-enable;
+ };
+ gpio136o_pins: gpio136o-pins {
+ pins = "GPIO136/SD1DT0";
+ bias-disable;
+ output-high;
+ };
+ gpio137_pins: gpio137-pins {
+ pins = "GPIO137/SD1DT1";
+ bias-disable;
+ input-enable;
+ };
+ gpio137o_pins: gpio137o-pins {
+ pins = "GPIO137/SD1DT1";
+ bias-disable;
+ output-high;
+ };
+ gpio138_pins: gpio138-pins {
+ pins = "GPIO138/SD1DT2";
+ bias-disable;
+ input-enable;
+ };
+ gpio138o_pins: gpio138o-pins {
+ pins = "GPIO138/SD1DT2";
+ bias-disable;
+ output-high;
+ };
+ gpio139_pins: gpio139-pins {
+ pins = "GPIO139/SD1DT3";
+ bias-disable;
+ input-enable;
+ };
+ gpio139o_pins: gpio139o-pins {
+ pins = "GPIO139/SD1DT3";
+ bias-disable;
+ output-high;
+ };
+ gpio140_pins: gpio140-pins {
+ pins = "GPIO140/SD1CLK";
+ bias-disable;
+ input-enable;
+ };
+ gpio140o_pins: gpio140o-pins {
+ pins = "GPIO140/SD1CLK";
+ bias-disable;
+ output-high;
+ };
+ gpio141_pins: gpio141-pins {
+ pins = "GPIO141/SD1WP";
+ bias-disable;
+ input-enable;
+ };
+ gpio141o_pins: gpio141o-pins {
+ pins = "GPIO141/SD1WP";
+ bias-disable;
+ output-high;
+ };
+ gpio142_pins: gpio142-pins {
+ pins = "GPIO142/SD1CMD";
+ bias-disable;
+ input-enable;
+ };
+ gpio142o_pins: gpio142o-pins {
+ pins = "GPIO142/SD1CMD";
+ bias-disable;
+ output-high;
+ };
+ gpio143_pins: gpio143-pins {
+ pins = "GPIO143/SD1CD/SD1PWR";
+ bias-disable;
+ input-enable;
+ };
+ gpio143o_pins: gpio143o-pins {
+ pins = "GPIO143/SD1CD/SD1PWR";
+ bias-disable;
+ output-high;
+ };
+ gpio143ol_pins: gpio143ol-pins {
+ pins = "GPIO143/SD1CD/SD1PWR";
+ bias-disable;
+ output-low;
+ };
+ gpio144_pins: gpio144-pins {
+ pins = "GPIO144/PWM4";
+ bias-disable;
+ input-enable;
+ };
+ gpio145_pins: gpio145-pins {
+ pins = "GPIO145/PWM5";
+ bias-disable;
+ input-enable;
+ };
+ gpio146_pins: gpio146-pins {
+ pins = "GPIO146/PWM6";
+ bias-disable;
+ input-enable;
+ };
+ gpio147_pins: gpio147-pins {
+ pins = "GPIO147/PWM7";
+ bias-disable;
+ input-enable;
+ };
+ gpio148_pins: gpio148-pins {
+ pins = "GPIO148/MMCDT4";
+ bias-disable;
+ input-enable;
+ };
+ gpio148o_pins: gpio148o-pins {
+ pins = "GPIO148/MMCDT4";
+ bias-disable;
+ output-high;
+ };
+ gpio148ol_pins: gpio148ol_pins {
+ pins = "GPIO148/MMCDT4";
+ bias-disable;
+ output-low;
+ };
+ gpio149_pins: gpio149-pins {
+ pins = "GPIO149/MMCDT5";
+ bias-disable;
+ input-enable;
+ };
+ gpio149o_pins: gpio149o-pins {
+ pins = "GPIO149/MMCDT5";
+ bias-disable;
+ output-high;
+ };
+ gpio149ol_pins: gpio149ol-pins {
+ pins = "GPIO149/MMCDT5";
+ bias-disable;
+ output-low;
+ };
+ gpio150_pins: gpio150-pins {
+ pins = "GPIO150/MMCDT6";
+ bias-disable;
+ input-enable;
+ };
+ gpio150o_pins: gpio150o-pins {
+ pins = "GPIO150/MMCDT6";
+ bias-disable;
+ output-high;
+ };
+ gpio150ol_pins: gpio150ol-pins {
+ pins = "GPIO150/MMCDT6";
+ bias-disable;
+ output-low;
+ };
+ gpio151_pins: gpio151-pins {
+ pins = "GPIO151/MMCDT7";
+ bias-disable;
+ input-enable;
+ };
+ gpio151o_pins: gpio151o-pins {
+ pins = "GPIO151/MMCDT7";
+ bias-disable;
+ output-high;
+ };
+ gpio151ol_pins: gpio151ol-pins {
+ pins = "GPIO151/MMCDT7";
+ bias-disable;
+ output-low;
+ };
+ gpio152_pins: gpio152-pins {
+ pins = "GPIO152/MMCCLK";
+ bias-disable;
+ input-enable;
+ };
+ gpio152o_pins: gpio152o-pins {
+ pins = "GPIO152/MMCCLK";
+ bias-disable;
+ output-high;
+ };
+ gpio152ol_pins: gpio152ol-pins {
+ pins = "GPIO152/MMCCLK";
+ bias-disable;
+ output-low;
+ };
+ gpio153_pins: gpio153-pins {
+ pins = "GPIO153/MMCWP";
+ bias-disable;
+ input-enable;
+ };
+ gpio153ol_pins: gpio153ol-pins {
+ pins = "GPIO153/MMCWP";
+ bias-disable;
+ output-low;
+ };
+ gpio154_pins: gpio154-pins {
+ pins = "GPIO154/MMCCMD";
+ bias-disable;
+ input-enable;
+ };
+ gpio154ol_pins: gpio154ol-pins {
+ pins = "GPIO154/MMCCMD";
+ bias-disable;
+ output-low;
+ };
+ gpio155_pins: gpio155-pins {
+ pins = "GPIO155/nMMCCD/nMMCRST";
+ bias-disable;
+ input-enable;
+ };
+ gpio155ol_pins: gpio155ol-pins {
+ pins = "GPIO155/nMMCCD/nMMCRST";
+ bias-disable;
+ output-low;
+ };
+ gpio156_pins: gpio156-pins {
+ pins = "GPIO156/MMCDT0";
+ bias-disable;
+ input-enable;
+ };
+ gpio156ol_pins: gpio156ol-pins {
+ pins = "GPIO156/MMCDT0";
+ bias-disable;
+ output-low;
+ };
+ gpio157_pins: gpio157-pins {
+ pins = "GPIO157/MMCDT1";
+ bias-disable;
+ input-enable;
+ };
+ gpio157o_pins: gpio157o-pins {
+ pins = "GPIO157/MMCDT1";
+ bias-disable;
+ output-high;
+ };
+ gpio157ol_pins: gpio157ol-pins {
+ pins = "GPIO157/MMCDT1";
+ bias-disable;
+ output-low;
+ };
+ gpio158_pins: gpio158-pins {
+ pins = "GPIO158/MMCDT2";
+ bias-disable;
+ input-enable;
+ };
+ gpio158o_pins: gpio158o-pins {
+ pins = "GPIO158/MMCDT2";
+ bias-disable;
+ output-high;
+ };
+ gpio158ol_pins: gpio158ol-pins {
+ pins = "GPIO158/MMCDT2";
+ bias-disable;
+ output-low;
+ };
+ gpio159_pins: gpio159-pins {
+ pins = "GPIO159/MMCDT3";
+ bias-disable;
+ input-enable;
+ };
+ gpio159o_pins: gpio159o-pins {
+ pins = "GPIO159/MMCDT3";
+ bias-disable;
+ output-high;
+ };
+ gpio159ol_pins: gpio159ol-pins {
+ pins = "GPIO159/MMCDT3";
+ bias-disable;
+ output-low;
+ };
+ gpio160_pins: gpio160-pins {
+ pins = "GPIO160/CLKOUT/RNGOSCOUT";
+ bias-disable;
+ input-enable;
+ };
+ gpio160o_pins: gpio160o-pins {
+ pins = "GPIO160/CLKOUT/RNGOSCOUT";
+ bias-disable;
+ output-high;
+ };
+ gpio160ol_pins: gpio160ol-pins {
+ pins = "GPIO160/CLKOUT/RNGOSCOUT";
+ bias-disable;
+ output-low;
+ };
+ gpio161_pins: gpio161-pins {
+ pins = "GPIO161/nLFRAME/nESPICS";
+ bias-disable;
+ input-enable;
+ };
+ gpio162_pins: gpio162-pins {
+ pins = "GPIO162/SERIRQ";
+ bias-disable;
+ input-enable;
+ };
+ gpio163_pins: gpio163-pins {
+ pins = "GPIO163/LCLK/ESPICLK";
+ bias-disable;
+ input-enable;
+ };
+ gpio164_pins: gpio164-pins {
+ pins = "GPIO164/LAD0/ESPI_IO0";
+ bias-disable;
+ input-enable;
+ };
+ gpio165_pins: gpio165-pins {
+ pins = "GPIO165/LAD1/ESPI_IO1";
+ bias-disable;
+ input-enable;
+ };
+ gpio166_pins: gpio166-pins {
+ pins = "GPIO166/LAD2/ESPI_IO2";
+ bias-disable;
+ input-enable;
+ };
+ gpio167_pins: gpio167-pins {
+ pins = "GPIO167/LAD3/ESPI_IO3";
+ bias-disable;
+ input-enable;
+ };
+ gpio168_pins: gpio168-pins {
+ pins = "GPIO168/nCLKRUN/nESPIALERT";
+ bias-disable;
+ input-enable;
+ };
+ gpio168ol_pins: gpio168ol-pins {
+ pins = "GPIO168/nCLKRUN/nESPIALERT";
+ bias-disable;
+ output-low;
+ };
+ gpio169_pins: gpio169-pins {
+ pins = "GPIO169/nSCIPME";
+ bias-disable;
+ input-enable;
+ };
+ gpio169o_pins: gpio169o-pins {
+ pins = "GPIO169/nSCIPME";
+ bias-disable;
+ output-high;
+ };
+ gpio169ol_pins: gpio169ol-pins {
+ pins = "GPIO169/nSCIPME";
+ bias-disable;
+ output-low;
+ };
+ gpio170_pins: gpio170-pins {
+ pins = "GPIO170/nSMI";
+ bias-disable;
+ input-enable;
+ };
+ gpio170ol_pins: gpio170ol-pins {
+ pins = "GPIO170/nSMI";
+ bias-disable;
+ output-low;
+ };
+ gpio173o_pins: gpio173o-pins {
+ pins = "GPIO173/SMB7SCL";
+ bias-disable;
+ output-high;
+ };
+ gpio173ol_pins: gpio173ol-pins {
+ pins = "GPIO173/SMB7SCL";
+ bias-disable;
+ output-low;
+ };
+ gpio174_pins: gpio174-pins {
+ pins = "GPIO174/SMB7SDA";
+ bias-disable;
+ input-enable;
+ };
+ gpio175_pins: gpio175-pins {
+ pins = "GPIO175/PSPI1CK/FANIN19";
+ bias-disable;
+ input-enable;
+ };
+ gpio175o_pins: gpio175o-pins {
+ pins = "GPIO175/PSPI1CK/FANIN19";
+ bias-disable;
+ output-high;
+ };
+ gpio175ol_pins: gpio175ol-pins {
+ pins = "GPIO175/PSPI1CK/FANIN19";
+ bias-disable;
+ output-low;
+ };
+ gpio176_pins: gpio176-pins {
+ pins = "GPIO176/PSPI1DO/FANIN18";
+ bias-disable;
+ input-enable;
+ };
+ gpio176o_pins: gpio176o-pins {
+ pins = "GPIO176/PSPI1DO/FANIN18";
+ bias-disable;
+ output-high;
+ };
+ gpio176ol_pins: gpio176ol-pins {
+ pins = "GPIO176/PSPI1DO/FANIN18";
+ bias-disable;
+ output-low;
+ };
+ gpio177_pins: gpio177-pins {
+ pins = "GPIO177/PSPI1DI/FANIN17";
+ bias-disable;
+ input-enable;
+ };
+ gpio177o_pins: gpio177o-pins {
+ pins = "GPIO177/PSPI1DI/FANIN17";
+ bias-disable;
+ output-high;
+ };
+ gpio177ol_pins: gpio177ol-pins {
+ pins = "GPIO177/PSPI1DI/FANIN17";
+ bias-disable;
+ output-low;
+ };
+ gpio187_pins: gpio187-pins {
+ pins = "GPIO187/nSPI3CS1";
+ bias-disable;
+ input-enable;
+ };
+ gpio187o_pins: gpio187o-pins {
+ pins = "GPIO187/nSPI3CS1";
+ bias-disable;
+ output-high;
+ };
+ gpio187ol_pins: gpio187ol-pins {
+ pins = "GPIO187/nSPI3CS1";
+ bias-disable;
+ output-low;
+ };
+ gpio188_pins: gpio188-pins {
+ pins = "GPIO188/SPI3D2/nSPI3CS2";
+ bias-disable;
+ input-enable;
+ };
+ gpio188o_pins: gpio188o-pins {
+ pins = "GPIO188/SPI3D2/nSPI3CS2";
+ bias-disable;
+ output-high;
+ };
+ gpio189o_pins: gpio189o-pins {
+ pins = "GPIO189/SPI3D3/nSPI3CS3";
+ bias-disable;
+ output-high;
+ };
+ gpio190_pins: gpio190-pins {
+ pins = "GPIO190/nPRD_SMI";
+ bias-disable;
+ input-enable;
+ };
+ gpio190o_pins: gpio190o-pins {
+ pins = "GPIO190/nPRD_SMI";
+ bias-disable;
+ output-high;
+ };
+ gpio190ol_pins: gpio190ol-pins {
+ pins = "GPIO190/nPRD_SMI";
+ bias-disable;
+ output-low;
+ };
+ gpio191o_pins: gpio191o-pins {
+ pins = "GPIO191";
+ bias-disable;
+ output-high;
+ };
+ gpio191ol_pins: gpio191ol-pins {
+ pins = "GPIO191";
+ bias-disable;
+ output-low;
+ };
+ gpio192_pins: gpio192-pins {
+ pins = "GPIO192";
+ bias-disable;
+ input-enable;
+ };
+ gpio192o_pins: gpio192o-pins {
+ pins = "GPIO192";
+ bias-disable;
+ output-high;
+ };
+ gpio192ol_pins: gpio192ol-pins {
+ pins = "GPIO192";
+ bias-disable;
+ output-low;
+ };
+ gpio194_pins: gpio194-pins {
+ pins = "GPIO194/SMB0BSCL";
+ bias-disable;
+ input-enable;
+ };
+ gpio194o_pins: gpio194o-pins {
+ pins = "GPIO194/SMB0BSCL";
+ bias-disable;
+ output-high;
+ };
+ gpio195_pins: gpio195-pins {
+ pins = "GPIO195/SMB0BSDA";
+ bias-disable;
+ input-enable;
+ };
+ gpio196_pins: gpio196-pins {
+ pins = "GPIO196/SMB0CSCL";
+ bias-disable;
+ input-enable;
+ };
+ gpio197_pins: gpio197-pins {
+ pins = "GPIO197/SMB0DEN";
+ bias-disable;
+ input-enable;
+ };
+ gpio197o_pins: gpio197o-pins {
+ pins = "GPIO197/SMB0DEN";
+ bias-disable;
+ output-high;
+ };
+ gpio197ol_pins: gpio197ol-pins {
+ pins = "GPIO197/SMB0DEN";
+ bias-disable;
+ output-low;
+ };
+ gpio198o_pins: gpio198o-pins {
+ pins = "GPIO198/SMB0DSDA";
+ bias-disable;
+ output-high;
+ };
+ gpio198ol_pins: gpio198ol-pins {
+ pins = "GPIO198/SMB0DSDA";
+ bias-disable;
+ output-low;
+ };
+ gpio199_pins: gpio199-pins {
+ pins = "GPIO199/SMB0DSCL";
+ bias-disable;
+ input-enable;
+ };
+ gpio200_pins: gpio200-pins {
+ pins = "GPIO200/R2CK";
+ input-enable;
+ bias-disable;
+ };
+ gpio200ol_pins: gpio200ol-pins {
+ pins = "GPIO200/R2CK";
+ bias-disable;
+ output-low;
+ };
+ gpio201ol_pins: gpio201ol-pins {
+ pins = "GPIO200/R2CK";
+ bias-disable;
+ output-low;
+ };
+ gpio202_pins: gpio202-pins {
+ pins = "GPIO202/SMB0CSDA";
+ bias-disable;
+ input-enable;
+ };
+ gpio203_pins: gpio203-pins {
+ pins = "GPIO203/FANIN16";
+ bias-disable;
+ input-enable;
+ };
+ gpio203o_pins: gpio203o-pins {
+ pins = "GPIO203/FANIN16";
+ bias-disable;
+ output-high;
+ };
+ gpio203ol_pins: gpio203ol-pins {
+ pins = "GPIO203/FANIN16";
+ bias-disable;
+ output-low;
+ };
+ gpio204_pins: gpio204-pins {
+ pins = "GPIO204/DDC2SCL";
+ bias-disable;
+ input-enable;
+ };
+ gpio204o_pins: gpio204o-pins {
+ pins = "GPIO204/DDC2SCL";
+ bias-disable;
+ output-high;
+ };
+ gpio204ol_pins: gpio204ol-pins {
+ pins = "GPIO204/DDC2SCL";
+ bias-disable;
+ output-low;
+ };
+ gpio205_pins: gpio205-pins {
+ pins = "GPIO205/DDC2SDA";
+ bias-disable;
+ input-enable;
+ };
+ gpio205o_pins: gpio205o-pins {
+ pins = "GPIO205/DDC2SDA";
+ bias-disable;
+ output-high;
+ };
+ gpio205ol_pins: gpio205ol-pins {
+ pins = "GPIO205/DDC2SDA";
+ bias-disable;
+ output-low;
+ };
+ gpio206_pins: gpio206-pins {
+ pins = "GPIO206/HSYNC2";
+ bias-disable;
+ input-enable;
+ };
+ gpio206o_pins: gpio206o-pins {
+ pins = "GPIO206/HSYNC2";
+ bias-disable;
+ output-high;
+ };
+ gpio206ol_pins: gpio206ol-pins {
+ pins = "GPIO206/HSYNC2";
+ bias-disable;
+ output-low;
+ };
+ gpio207_pins: gpio207-pins {
+ pins = "GPIO207/VSYNC2";
+ bias-disable;
+ input-enable;
+ };
+ gpio207o_pins: gpio207o-pins {
+ pins = "GPIO207/VSYNC2";
+ bias-disable;
+ output-high;
+ };
+ gpio207ol_pins: gpio207ol-pins {
+ pins = "GPIO207/VSYNC2";
+ bias-disable;
+ output-low;
+ };
+ gpio208_pins: gpio208-pins {
+ pins = "GPIO208/RG2TXC/DVCK";
+ bias-disable;
+ input-enable;
+ };
+ gpio208o_pins: gpio208o-pins {
+ pins = "GPIO208/RG2TXC/DVCK";
+ bias-disable;
+ output-high;
+ };
+ gpio208ol_pins: gpio208ol-pins {
+ pins = "GPIO208/RG2TXC/DVCK";
+ bias-disable;
+ output-low;
+ };
+ gpio209_pins: gpio209-pins {
+ pins = "GPIO209/RG2TXCTL/DDRV4";
+ bias-disable;
+ input-enable;
+ };
+ gpio209ol_pins: gpio209ol-pins {
+ pins = "GPIO209/RG2TXCTL/DDRV4";
+ bias-disable;
+ output-low;
+ };
+ gpio210_pins: gpio210-pins {
+ pins = "GPIO210/RG2RXD0/DDRV5";
+ bias-disable;
+ input-enable;
+ };
+ gpio210o_pins: gpio210o-pins {
+ pins = "GPIO210/RG2RXD0/DDRV5";
+ bias-disable;
+ output-high;
+ };
+ gpio210ol_pins: gpio210ol-pins {
+ pins = "GPIO210/RG2RXD0/DDRV5";
+ bias-disable;
+ output-low;
+ };
+ gpio211_pins: gpio211-pins {
+ pins = "GPIO211/RG2RXD1/DDRV6";
+ bias-disable;
+ input-enable;
+ };
+ gpio211o_pins: gpio211o-pins {
+ pins = "GPIO211/RG2RXD1/DDRV6";
+ bias-disable;
+ output-high;
+ };
+ gpio211ol_pins: gpio211ol-pins {
+ pins = "GPIO211/RG2RXD1/DDRV6";
+ bias-disable;
+ output-low;
+ };
+ gpio212_pins: gpio212-pins {
+ pins = "GPIO212/RG2RXD2/DDRV7";
+ bias-disable;
+ input-enable;
+ };
+ gpio212o_pins: gpio212o-pins {
+ pins = "GPIO212/RG2RXD2/DDRV7";
+ bias-disable;
+ output-high;
+ };
+ gpio212ol_pins: gpio212ol-pins {
+ pins = "GPIO212/RG2RXD2/DDRV7";
+ bias-disable;
+ output-low;
+ };
+ gpio213_pins: gpio213-pins {
+ pins = "GPIO213/RG2RXD3/DDRV8";
+ bias-disable;
+ input-enable;
+ };
+ gpio213o_pins: gpio213o-pins {
+ pins = "GPIO213/RG2RXD3/DDRV8";
+ bias-disable;
+ output-high;
+ };
+ gpio213ol_pins: gpio213ol-pins {
+ pins = "GPIO213/RG2RXD3/DDRV8";
+ bias-disable;
+ output-low;
+ };
+ gpio214_pins: gpio214-pins {
+ pins = "GPIO214/RG2RXC/DDRV9";
+ bias-disable;
+ input-enable;
+ };
+ gpio214ol_pins: gpio214ol-pins {
+ pins = "GPIO214/RG2RXC/DDRV9";
+ bias-disable;
+ output-low;
+ };
+ gpio215_pins: gpio215-pins {
+ pins = "GPIO215/RG2RXCTL/DDRV10";
+ bias-disable;
+ input-enable;
+ };
+ gpio215ol_pins: gpio215ol-pins {
+ pins = "GPIO215/RG2RXCTL/DDRV10";
+ bias-disable;
+ output-low;
+ };
+ gpio216_pins: gpio216-pins {
+ pins = "GPIO216/RG2MDC/DDRV11";
+ bias-disable;
+ input-enable;
+ };
+ gpio216ol_pins: gpio216ol-pins {
+ pins = "GPIO216/RG2MDC/DDRV11";
+ bias-disable;
+ output-low;
+ };
+ gpio217_pins: gpio217-pins {
+ pins = "GPIO217/RG2MDIO/DVHSYNC";
+ bias-disable;
+ input-enable;
+ };
+ gpio217ol_pins: gpio217ol-pins {
+ pins = "GPIO217/RG2MDIO/DVHSYNC";
+ bias-disable;
+ output-low;
+ };
+ gpio218_pins: gpio218-pins {
+ pins = "GPIO218/nWDO1";
+ bias-disable;
+ input-enable;
+ };
+ gpio218ol_pins: gpio218ol-pins {
+ pins = "GPIO218/nWDO1";
+ bias-disable;
+ output-low;
+ };
+ gpio219_pins: gpio219-pins {
+ pins = "GPIO219/nWDO2";
+ bias-disable;
+ input-enable;
+ };
+ gpio219ol_pins: gpio219ol-pins {
+ pins = "GPIO219/nWDO2";
+ bias-disable;
+ output-low;
+ };
+ gpio220ol_pins: gpio220ol-pins {
+ pins = "GPIO220/SMB12SCL";
+ bias-disable;
+ output-low;
+ };
+ gpio221o_pins: gpio221o-pins {
+ pins = "GPIO221/SMB12SDA";
+ bias-disable;
+ output-high;
+ };
+ gpio222_pins: gpio222-pins {
+ pins = "GPIO222/SMB13SCL";
+ bias-disable;
+ input-enable;
+ };
+ gpio222o_pins: gpio222o-pins {
+ pins = "GPIO222/SMB13SCL";
+ bias-disable;
+ output-high;
+ };
+ gpio223_pins: gpio223-pins {
+ pins = "GPIO223/SMB13SDA";
+ bias-disable;
+ input-enable;
+ };
+ gpio223ol_pins: gpio223ol-pins {
+ pins = "GPIO223/SMB13SDA";
+ bias-disable;
+ output-low;
+ };
+ gpio224_pins: gpio224-pins {
+ pins = "GPIO224/SPIXCK";
+ bias-disable;
+ input-enable;
+ };
+ gpio224o_pins: gpio224o-pins {
+ pins = "GPIO224/SPIXCK";
+ bias-disable;
+ output-high;
+ };
+ gpio224ol_pins: gpio224ol-pins {
+ pins = "GPIO224/SPIXCK";
+ bias-disable;
+ output-low;
+ };
+ gpio225_pins: gpio225-pins {
+ pins = "GPO225/SPIXD0/STRAP12";
+ bias-disable;
+ input-enable;
+ };
+ gpio225o_pins: gpio225o-pins {
+ pins = "GPO225/SPIXD0/STRAP12";
+ bias-disable;
+ output-high;
+ };
+ gpio226_pins: gpio226-pins {
+ pins = "GPO226/SPIXD1/STRAP13";
+ bias-disable;
+ input-enable;
+ };
+ gpio226o_pins: gpio226o-pins {
+ pins = "GPO226/SPIXD1/STRAP13";
+ bias-disable;
+ output-high;
+ };
+ gpio227_pins: gpio227-pins {
+ pins = "GPIO227/nSPIXCS0";
+ bias-disable;
+ input-enable;
+ };
+ gpio227o_pins: gpio227o-pins {
+ pins = "GPIO227/nSPIXCS0";
+ bias-disable;
+ output-high;
+ };
+ gpio227ol_pins: gpio227ol-pins {
+ pins = "GPIO227/nSPIXCS0";
+ bias-disable;
+ output-low;
+ };
+ gpio228_pins: gpio228-pins {
+ pins = "GPIO228/nSPIXCS1";
+ bias-disable;
+ input-enable;
+ };
+ gpio228ol_pins: gpio228ol-pins {
+ pins = "GPIO228/nSPIXCS1";
+ bias-disable;
+ output-low;
+ };
+ gpio229_pins: gpio229-pins {
+ pins = "GPO229/SPIXD2/STRAP3";
+ bias-disable;
+ input-enable;
+ };
+ gpio229o_pins: gpio229o-pins {
+ pins = "GPO229/SPIXD2/STRAP3";
+ bias-disable;
+ output-high;
+ };
+ gpio230_pins: gpio230-pins {
+ pins = "GPIO230/SPIXD3";
+ bias-disable;
+ input-enable;
+ };
+ gpio230o_pins: gpio230o-pins {
+ pins = "GPIO230/SPIXD3";
+ bias-disable;
+ output-high;
+ };
+ gpio230ol_pins: gpio230ol-pins {
+ pins = "GPIO230/SPIXD3";
+ bias-disable;
+ output-low;
+ };
+ gpio231_pins: gpio231-pins {
+ pins = "GPIO231/nCLKREQ";
+ bias-disable;
+ input-enable;
+ };
+ gpio231o_pins: gpio231o-pins {
+ pins = "GPIO231/nCLKREQ";
+ bias-disable;
+ output-high;
+ };
+ gpio255_pins: gpio255-pins {
+ pins = "GPI255/DACOSEL";
+ bias-disable;
+ input-enable;
+ };
+ };
+};
diff --git a/arch/arm/boot/dts/nuvoton-npcm750-runbmc-olympus-pincfg.dtsi b/arch/arm/boot/dts/nuvoton-npcm750-runbmc-olympus-pincfg.dtsi
new file mode 100644
index 000000000000..230cb344b2e1
--- /dev/null
+++ b/arch/arm/boot/dts/nuvoton-npcm750-runbmc-olympus-pincfg.dtsi
@@ -0,0 +1,517 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2019 Quanta Computer Inc. Samuel.Jiang@quantatw.com
+
+/ {
+ pinctrl: pinctrl@f0800000 {
+ gpio0ol_pins: gpio0ol-pins {
+ pins = "GPIO0/IOX1DI";
+ bias-disable;
+ output-low;
+ };
+ gpio1ol_pins: gpio1ol-pins {
+ pins = "GPIO1/IOX1LD";
+ bias-disable;
+ output-low;
+ };
+ gpio2ol_pins: gpio2ol-pins {
+ pins = "GPIO2/IOX1CK";
+ bias-disable;
+ output-low;
+ };
+ gpio3ol_pins: gpio3ol-pins {
+ pins = "GPIO3/IOX1D0";
+ bias-disable;
+ output-low;
+ };
+ gpio5_pins: gpio5-pins {
+ pins = "GPIO5/IOX2LD/SMB1DSCL";
+ bias-disable;
+ input-enable;
+ };
+ gpio6_pins: gpio6-pins {
+ pins = "GPIO6/IOX2CK/SMB2DSDA";
+ bias-disable;
+ input-enable;
+ };
+ gpio7_pins: gpio7-pins {
+ pins = "GPIO7/IOX2D0/SMB2DSCL";
+ bias-disable;
+ input-enable;
+ };
+ gpio8o_pins: gpio8o-pins {
+ pins = "GPIO8/LKGPO1";
+ bias-disable;
+ output-high;
+ };
+ gpio9ol_pins: gpio9ol-pins {
+ pins = "GPIO9/LKGPO2";
+ bias-disable;
+ output-low;
+ };
+ gpio10_pins: gpio10-pins {
+ pins = "GPIO10/IOXHLD";
+ bias-disable;
+ input-enable;
+ };
+ gpio11_pins: gpio11-pins {
+ pins = "GPIO11/IOXHCK";
+ bias-disable;
+ input-enable;
+ };
+ gpio12ol_pins: gpio12ol-pins {
+ pins = "GPIO12/GSPICK/SMB5BSCL";
+ bias-disable;
+ output-low;
+ };
+ gpio13ol_pins: gpio13ol-pins {
+ pins = "GPIO13/GSPIDO/SMB5BSDA";
+ bias-disable;
+ output-low;
+ };
+ gpio14ol_pins: gpio14ol-pins {
+ pins = "GPIO14/GSPIDI/SMB5CSCL";
+ bias-disable;
+ output-low;
+ };
+ gpio15ol_pins: gpio15ol-pins {
+ pins = "GPIO15/GSPICS/SMB5CSDA";
+ bias-disable;
+ output-low;
+ };
+ gpio20_pins: gpio20-pins {
+ pins = "GPIO20/SMB4CSDA/SMB15SDA";
+ bias-disable;
+ input-enable;
+ };
+ gpio21_pins: gpio21-pins {
+ pins = "GPIO21/SMB4CSCL/SMB15SCL";
+ bias-disable;
+ input-enable;
+ };
+ gpio22o_pins: gpio22o-pins {
+ pins = "GPIO22/SMB4DSDA/SMB14SDA";
+ bias-disable;
+ output-high;
+ };
+ gpio23_pins: gpio23-pins {
+ pins = "GPIO23/SMB4DSCL/SMB14SCL";
+ bias-disable;
+ input-enable;
+ };
+ gpio24_pins: gpio24-pins {
+ pins = "GPIO24/IOXHDO";
+ bias-disable;
+ input-enable;
+ };
+ gpio25_pins: gpio25-pins {
+ pins = "GPIO25/IOXHDI";
+ bias-disable;
+ input-enable;
+ };
+ gpio30_pins: gpio30-pins {
+ pins = "GPIO30/SMB3SDA";
+ bias-disable;
+ input-enable;
+ };
+ gpio31_pins: gpio31-pins {
+ pins = "GPIO31/SMB3SCL";
+ bias-disable;
+ input-enable;
+ };
+ gpio37o_pins: gpio37o-pins {
+ pins = "GPIO37/SMB3CSDA";
+ bias-disable;
+ output-high;
+ };
+ gpio38_pins: gpio38-pins {
+ pins = "GPIO38/SMB3CSCL";
+ bias-disable;
+ input-enable;
+ };
+ gpio39_pins: gpio39-pins {
+ pins = "GPIO39/SMB3BSDA";
+ bias-disable;
+ input-enable;
+ };
+ gpio40o_pins: gpio40o-pins {
+ pins = "GPIO40/SMB3BSCL";
+ bias-disable;
+ output-high;
+ };
+ gpio59_pins: gpio59-pins {
+ pins = "GPIO59/SMB3DSDA";
+ bias-disable;
+ input-enable;
+ };
+ gpio76_pins: gpio76-pins {
+ pins = "GPIO76/FANIN12";
+ bias-disable;
+ input-enable;
+ };
+ gpio77_pins: gpio77-pins {
+ pins = "GPIO77/FANIN13";
+ bias-disable;
+ input-enable;
+ };
+ gpio78o_pins: gpio78o-pins {
+ pins = "GPIO78/FANIN14";
+ bias-disable;
+ output-high;
+ };
+ gpio79_pins: gpio79-pins {
+ pins = "GPIO79/FANIN15";
+ bias-disable;
+ input-enable;
+ };
+ gpio82_pins: gpio82-pins {
+ pins = "GPIO82/PWM2";
+ bias-disable;
+ input-enable;
+ };
+ gpio83_pins: gpio83-pins {
+ pins = "GPIO83/PWM3";
+ bias-disable;
+ input-enable;
+ };
+ gpio84_pins: gpio84-pins {
+ pins = "GPIO84/R2TXD0";
+ bias-disable;
+ input-enable;
+ };
+ gpio85o_pins: gpio85o-pins {
+ pins = "GPIO85/R2TXD1";
+ bias-disable;
+ output-high;
+ };
+ gpio86ol_pins: gpio86ol-pins {
+ pins = "GPIO86/R2TXEN";
+ bias-disable;
+ output-low;
+ };
+ gpio87_pins: gpio87-pins {
+ pins = "GPIO87/R2RXD0";
+ bias-disable;
+ input-enable;
+ };
+ gpio88_pins: gpio88-pins {
+ pins = "GPIO88/R2RXD1";
+ bias-disable;
+ input-enable;
+ };
+ gpio89_pins: gpio89-pins {
+ pins = "GPIO89/R2CRSDV";
+ bias-disable;
+ input-enable;
+ };
+ gpio90_pins: gpio90-pins {
+ pins = "GPIO90/R2RXERR";
+ bias-disable;
+ input-enable;
+ };
+ gpio93_pins: gpio93-pins {
+ pins = "GPIO93/GA20/SMB5DSCL";
+ bias-disable;
+ input-enable;
+ };
+ gpio94ol_pins: gpio94ol-pins {
+ pins = "GPIO94/nKBRST/SMB5DSDA";
+ bias-disable;
+ output-low;
+ };
+ gpio108ol_pins: gpio108ol-pins {
+ pins = "GPIO108/RG1MDC";
+ bias-disable;
+ output-low;
+ };
+ gpio109ol_pins: gpio109ol-pins {
+ pins = "GPIO109/RG1MDIO";
+ bias-disable;
+ output-low;
+ };
+ gpio110ol_pins: gpio110ol-pins {
+ pins = "GPIO110/RG2TXD0/DDRV0";
+ bias-disable;
+ output-low;
+ };
+ gpio111ol_pins: gpio111ol-pins {
+ pins = "GPIO111/RG2TXD1/DDRV1";
+ bias-disable;
+ output-low;
+ };
+ gpio112ol_pins: gpio112ol-pins {
+ pins = "GPIO112/RG2TXD2/DDRV2";
+ bias-disable;
+ output-low;
+ };
+ gpio113ol_pins: gpio113ol-pins {
+ pins = "GPIO113/RG2TXD3/DDRV3";
+ bias-disable;
+ output-low;
+ };
+ gpio114o_pins: gpio114o-pins {
+ pins = "GPIO114/SMB0SCL";
+ bias-disable;
+ output-high;
+ };
+ gpio115_pins: gpio115-pins {
+ pins = "GPIO115/SMB0SDA";
+ bias-disable;
+ input-enable;
+ };
+ gpio120_pins: gpio120-pins {
+ pins = "GPIO120/SMB2CSDA";
+ bias-disable;
+ input-enable;
+ };
+ gpio121_pins: gpio121-pins {
+ pins = "GPIO121/SMB2CSCL";
+ bias-disable;
+ input-enable;
+ };
+ gpio122_pins: gpio122-pins {
+ pins = "GPIO122/SMB2BSDA";
+ bias-disable;
+ input-enable;
+ };
+ gpio123_pins: gpio123-pins {
+ pins = "GPIO123/SMB2BSCL";
+ bias-disable;
+ input-enable;
+ };
+ gpio124_pins: gpio124-pins {
+ pins = "GPIO124/SMB1CSDA";
+ bias-disable;
+ input-enable;
+ };
+ gpio125_pins: gpio125-pins {
+ pins = "GPIO125/SMB1CSCL";
+ bias-disable;
+ input-enable;
+ };
+ gpio126_pins: gpio126-pins {
+ pins = "GPIO126/SMB1BSDA";
+ bias-disable;
+ input-enable;
+ };
+ gpio127o_pins: gpio127o-pins {
+ pins = "GPIO127/SMB1BSCL";
+ bias-disable;
+ output-high;
+ };
+ gpio136_pins: gpio136-pins {
+ pins = "GPIO136/SD1DT0";
+ bias-disable;
+ input-enable;
+ };
+ gpio137_pins: gpio137-pins {
+ pins = "GPIO137/SD1DT1";
+ bias-disable;
+ input-enable;
+ };
+ gpio138_pins: gpio138-pins {
+ pins = "GPIO138/SD1DT2";
+ bias-disable;
+ input-enable;
+ };
+ gpio139_pins: gpio139-pins {
+ pins = "GPIO139/SD1DT3";
+ bias-disable;
+ input-enable;
+ };
+ gpio140_pins: gpio140-pins {
+ pins = "GPIO140/SD1CLK";
+ bias-disable;
+ input-enable;
+ };
+ gpio141_pins: gpio141-pins {
+ pins = "GPIO141/SD1WP";
+ bias-disable;
+ input-enable;
+ };
+ gpio142_pins: gpio142-pins {
+ pins = "GPIO142/SD1CMD";
+ bias-disable;
+ input-enable;
+ };
+ gpio143_pins: gpio143-pins {
+ pins = "GPIO143/SD1CD/SD1PWR";
+ bias-disable;
+ input-enable;
+ };
+ gpio144_pins: gpio144-pins {
+ pins = "GPIO144/PWM4";
+ bias-disable;
+ input-enable;
+ };
+ gpio145_pins: gpio145-pins {
+ pins = "GPIO145/PWM5";
+ bias-disable;
+ input-enable;
+ };
+ gpio146_pins: gpio146-pins {
+ pins = "GPIO146/PWM6";
+ bias-disable;
+ input-enable;
+ };
+ gpio147_pins: gpio147-pins {
+ pins = "GPIO147/PWM7";
+ bias-disable;
+ input-enable;
+ };
+ gpio153o_pins: gpio153o-pins {
+ pins = "GPIO153/MMCWP";
+ bias-disable;
+ output-high;
+ };
+ gpio155_pins: gpio155-pins {
+ pins = "GPIO155/nMMCCD/nMMCRST";
+ bias-disable;
+ input-enable;
+ };
+ gpio160o_pins: gpio160o-pins {
+ pins = "GPIO160/CLKOUT/RNGOSCOUT";
+ bias-disable;
+ output-high;
+ };
+ gpio169o_pins: gpio169o-pins {
+ pins = "GPIO169/nSCIPME";
+ bias-disable;
+ output-high;
+ };
+ gpio188o_pins: gpio188o-pins {
+ pins = "GPIO188/SPI3D2/nSPI3CS2";
+ bias-disable;
+ output-high;
+ };
+ gpio189_pins: gpio189-pins {
+ pins = "GPIO189/SPI3D3/nSPI3CS3";
+ bias-disable;
+ input-enable;
+ };
+ gpio196_pins: gpio196-pins {
+ pins = "GPIO196/SMB0CSCL";
+ bias-disable;
+ input-enable;
+ };
+ gpio197_pins: gpio197-pins {
+ pins = "GPIO197/SMB0DEN";
+ bias-disable;
+ input-enable;
+ };
+ gpio198o_pins: gpio198o-pins {
+ pins = "GPIO198/SMB0DSDA";
+ bias-disable;
+ output-high;
+ };
+ gpio199o_pins: gpio199o-pins {
+ pins = "GPIO199/SMB0DSCL";
+ bias-disable;
+ output-high;
+ };
+ gpio200_pins: gpio200-pins {
+ pins = "GPIO200/R2CK";
+ input-enable;
+ bias-disable;
+ };
+ gpio202_pins: gpio202-pins {
+ pins = "GPIO202/SMB0CSDA";
+ bias-disable;
+ input-enable;
+ };
+ gpio203o_pins: gpio203o-pins {
+ pins = "GPIO203/FANIN16";
+ bias-disable;
+ output-high;
+ };
+ gpio208_pins: gpio208-pins {
+ pins = "GPIO208/RG2TXC/DVCK";
+ bias-disable;
+ input-enable;
+ };
+ gpio209ol_pins: gpio209ol-pins {
+ pins = "GPIO209/RG2TXCTL/DDRV4";
+ bias-disable;
+ output-low;
+ };
+ gpio210ol_pins: gpio210ol-pins {
+ pins = "GPIO210/RG2RXD0/DDRV5";
+ bias-disable;
+ output-low;
+ };
+ gpio211ol_pins: gpio211ol-pins {
+ pins = "GPIO211/RG2RXD1/DDRV6";
+ bias-disable;
+ output-low;
+ };
+ gpio212ol_pins: gpio212ol-pins {
+ pins = "GPIO212/RG2RXD2/DDRV7";
+ bias-disable;
+ output-low;
+ };
+ gpio213ol_pins: gpio213ol-pins {
+ pins = "GPIO213/RG2RXD3/DDRV8";
+ bias-disable;
+ output-low;
+ };
+ gpio214ol_pins: gpio214ol-pins {
+ pins = "GPIO214/RG2RXC/DDRV9";
+ bias-disable;
+ output-low;
+ };
+ gpio215ol_pins: gpio215ol-pins {
+ pins = "GPIO215/RG2RXCTL/DDRV10";
+ bias-disable;
+ output-low;
+ };
+ gpio216ol_pins: gpio216ol-pins {
+ pins = "GPIO216/RG2MDC/DDRV11";
+ bias-disable;
+ output-low;
+ };
+ gpio217ol_pins: gpio217ol-pins {
+ pins = "GPIO217/RG2MDIO/DVHSYNC";
+ bias-disable;
+ output-low;
+ };
+ gpio224_pins: gpio224-pins {
+ pins = "GPIO224/SPIXCK";
+ bias-disable;
+ input-enable;
+ };
+ gpio225ol_pins: gpio225ol-pins {
+ pins = "GPO225/SPIXD0/STRAP12";
+ bias-disable;
+ output-low;
+ };
+ gpio226ol_pins: gpio226ol-pins {
+ pins = "GPO226/SPIXD1/STRAP13";
+ bias-disable;
+ output-low;
+ };
+ gpio227ol_pins: gpio227ol-pins {
+ pins = "GPIO227/nSPIXCS0";
+ bias-disable;
+ output-low;
+ };
+ gpio228o_pins: gpio228ol-pins {
+ pins = "GPIO228/nSPIXCS1";
+ bias-disable;
+ output-high;
+ };
+ gpio229o_pins: gpio229o-pins {
+ pins = "GPO229/SPIXD2/STRAP3";
+ bias-disable;
+ output-high;
+ };
+ gpio230_pins: gpio230-pins {
+ pins = "GPIO230/SPIXD3";
+ bias-disable;
+ input-enable;
+ };
+ gpio231o_pins: gpio231o-pins {
+ pins = "GPIO231/nCLKREQ";
+ bias-disable;
+ output-high;
+ };
+ };
+};
diff --git a/arch/arm/boot/dts/nuvoton-npcm750-runbmc-olympus.dts b/arch/arm/boot/dts/nuvoton-npcm750-runbmc-olympus.dts
new file mode 100644
index 000000000000..1692bb7314c5
--- /dev/null
+++ b/arch/arm/boot/dts/nuvoton-npcm750-runbmc-olympus.dts
@@ -0,0 +1,1162 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2019 Nuvoton Technology <kwliu@nuvoton.com>
+// Copyright (c) 2019 Quanta Computer Inc. <Samuel.Jiang@quantatw.com>
+
+/dts-v1/;
+#include "nuvoton-npcm750.dtsi"
+#include "nuvoton-npcm750-runbmc-olympus-pincfg.dtsi"
+#include <dt-bindings/i2c/i2c.h>
+
+/ {
+ model = "Nuvoton npcm750 RunBMC Olympus";
+ compatible = "nuvoton,npcm750";
+
+ aliases {
+ ethernet0 = &emc0;
+ ethernet1 = &gmac0;
+ serial0 = &serial0;
+ serial1 = &serial1;
+ serial2 = &serial2;
+ serial3 = &serial3;
+ udc0 = &udc0;
+ udc1 = &udc1;
+ udc2 = &udc2;
+ udc3 = &udc3;
+ udc4 = &udc4;
+ udc5 = &udc5;
+ udc6 = &udc6;
+ udc7 = &udc7;
+ udc8 = &udc8;
+ udc9 = &udc9;
+ emmc0 = &sdhci0;
+ i2c0 = &i2c0;
+ i2c1 = &i2c1;
+ i2c2 = &i2c2;
+ i2c3 = &i2c3;
+ i2c4 = &i2c4;
+ i2c5 = &i2c5;
+ i2c6 = &i2c6;
+ i2c7 = &i2c7;
+ i2c8 = &i2c8;
+ i2c9 = &i2c9;
+ i2c10 = &i2c10;
+ i2c11 = &i2c11;
+ i2c12 = &i2c12;
+ i2c13 = &i2c13;
+ spi0 = &spi0;
+ spi1 = &spi1;
+ fiu0 = &fiu0;
+ fiu1 = &fiu3;
+ };
+
+ chosen {
+ stdout-path = &serial3;
+ };
+
+ memory {
+ reg = <0 0x40000000>;
+ };
+
+ iio-hwmon {
+ compatible = "iio-hwmon";
+ io-channels = <&adc 0>, <&adc 1>, <&adc 2>, <&adc 3>,
+ <&adc 4>, <&adc 5>, <&adc 6>, <&adc 7>;
+ };
+
+ leds {
+ compatible = "gpio-leds";
+ heartbeat {
+ label = "heartbeat";
+ gpios = <&gpio3 14 1>;
+ };
+
+ identify {
+ label = "identify";
+ gpios = <&gpio3 15 1>;
+ };
+ };
+
+ jtag {
+ compatible = "nuvoton,npcm750-jtag";
+ enable_pspi_jtag = <1>;
+ pspi-index = <2>;
+ tck {
+ label = "tck";
+ gpios = <&gpio0 19 0>; /* gpio19 */
+ regbase = <0xf0010000 0x1000>;
+ };
+
+ tdi {
+ label = "tdi";
+ gpios = <&gpio0 18 0>; /* gpio18 */
+ regbase = <0xf0010000 0x1000>;
+ };
+
+ tdo {
+ label = "tdo";
+ gpios = <&gpio0 17 0>; /* gpio17 */
+ regbase = <0xf0010000 0x1000>;
+ };
+ tms {
+ label = "tms";
+ gpios = <&gpio0 16 0>; /* gpio16 */
+ regbase = <0xf0010000 0x1000>;
+ };
+ };
+};
+
+&fiu0 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&spi0cs1_pins>;
+ status = "okay";
+
+ spi-nor@0 {
+ compatible = "jedec,spi-nor";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ reg = <0>;
+ spi-rx-bus-width = <2>;
+
+ partitions@80000000 {
+ compatible = "fixed-partitions";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ bmc@0{
+ label = "bmc";
+ reg = <0x000000 0x2000000>;
+ };
+ u-boot@0 {
+ label = "u-boot";
+ reg = <0x0000000 0x80000>;
+ read-only;
+ };
+ u-boot-env@100000{
+ label = "u-boot-env";
+ reg = <0x00100000 0x40000>;
+ };
+ kernel@200000 {
+ label = "kernel";
+ reg = <0x0200000 0x600000>;
+ };
+ rofs@800000 {
+ label = "rofs";
+ reg = <0x800000 0x1500000>;
+ };
+ rwfs@1d00000 {
+ label = "rwfs";
+ reg = <0x1d00000 0x300000>;
+ };
+ };
+ };
+
+ spi-nor@1 {
+ compatible = "jedec,spi-nor";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ reg = <1>;
+ npcm,fiu-rx-bus-width = <2>;
+
+ partitions@88000000 {
+ compatible = "fixed-partitions";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ spare1@0 {
+ label = "spi0-cs1-spare1";
+ reg = <0x0 0x800000>;
+ };
+ spare2@800000 {
+ label = "spi0-cs1-spare2";
+ reg = <0x800000 0x0>;
+ };
+ };
+ };
+};
+
+&fiu3 {
+ pinctrl-0 = <&spi3_pins>;
+ status = "okay";
+
+ spi-nor@0 {
+ compatible = "jedec,spi-nor";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ reg = <0>;
+ spi-rx-bus-width = <2>;
+
+ partitions@A0000000 {
+ compatible = "fixed-partitions";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ system1@0 {
+ label = "spi3-system1";
+ reg = <0x0 0x800000>;
+ };
+ system2@800000 {
+ label = "spi3-system2";
+ reg = <0x800000 0x0>;
+ };
+ };
+ };
+};
+
+&gcr {
+ mux-controller {
+ compatible = "mmio-mux";
+ #mux-control-cells = <1>;
+
+ mux-reg-masks = <0x38 0x07>;
+ idle-states = <6>;
+ };
+};
+
+&gmac0 {
+ phy-mode = "rgmii-id";
+ snps,eee-force-disable;
+ status = "okay";
+};
+
+&emc0 {
+ phy-mode = "rmii";
+ use-ncsi;
+ status = "okay";
+};
+
+&i2c1 {
+ status = "okay";
+
+ i2c-switch@70 {
+ compatible = "nxp,pca9548";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0x70>;
+ i2c-mux-idle-disconnect;
+
+ i2c_slot1a: i2c-bus@0 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0>;
+ };
+
+ i2c_slot1b: i2c-bus@1 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <1>;
+ };
+
+ i2c_slot2a: i2c-bus@2 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <2>;
+ };
+
+ i2c_slot2b: i2c-bus@3 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <3>;
+ };
+
+ i2c_slot3: i2c-bus@4 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <4>;
+ };
+
+ i2c_slot4: i2c-bus@5 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <5>;
+ };
+
+ i2c_slot5: i2c-bus@6 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <6>;
+ };
+ };
+
+ i2c-switch@71 {
+ compatible = "nxp,pca9546";
+ reg = <0x71>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ i2c-mux-idle-disconnect;
+
+ i2c_m2_s1: i2c-bus@0 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0>;
+ };
+
+ i2c_m2_s2: i2c-bus@1 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <1>;
+ };
+ i2c_m2_s3: i2c-bus@2 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <2>;
+ };
+
+ i2c_m2_s4: i2c-bus@3 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <3>;
+ };
+ };
+};
+
+&i2c2 {
+ status = "okay";
+
+ tmp421@4c {
+ compatible = "ti,tmp421";
+ reg = <0x4c>;
+ };
+
+ power-supply@58 {
+ compatible = "delta,dps800";
+ reg = <0x58>;
+ };
+};
+
+&i2c3 {
+ status = "okay";
+};
+
+&i2c4 {
+ status = "okay";
+
+ eeprom@54 {
+ compatible = "atmel,24c64";
+ reg = <0x54>;
+ };
+};
+
+&i2c5 {
+ status = "okay";
+
+ i2c-slave-mqueue@10 {
+ compatible = "i2c-slave-mqueue";
+ reg = <(I2C_OWN_SLAVE_ADDRESS | 0x10)>;
+ };
+};
+
+&i2c6 {
+ status = "okay";
+
+ ina219@40 {
+ compatible = "ti,ina219";
+ reg = <0x40>;
+ };
+ ina219@41 {
+ compatible = "ti,ina219";
+ reg = <0x41>;
+ };
+ ina219@44 {
+ compatible = "ti,ina219";
+ reg = <0x44>;
+ };
+ ina219@45 {
+ compatible = "ti,ina219";
+ reg = <0x45>;
+ };
+ tps53679@60 {
+ compatible = "ti,tps53679";
+ reg = <0x60>;
+ };
+ tps53659@62 {
+ compatible = "ti,tps53659";
+ reg = <0x62>;
+ };
+ tps53659@64 {
+ compatible = "ti,tps53659";
+ reg = <0x64>;
+ };
+ tps53622@67 {
+ compatible = "ti,tps53622";
+ reg = <0x67>;
+ };
+ tps53622@69 {
+ compatible = "ti,tps53622";
+ reg = <0x69>;
+ };
+ tps53679@70 {
+ compatible = "ti,tps53679";
+ reg = <0x70>;
+ };
+ tps53659@72 {
+ compatible = "ti,tps53659";
+ reg = <0x72>;
+ };
+ tps53659@74 {
+ compatible = "ti,tps53659";
+ reg = <0x74>;
+ };
+ tps53622@77 {
+ compatible = "ti,tps53622";
+ reg = <0x77>;
+ };
+};
+
+&i2c7 {
+ status = "okay";
+
+ tmp421@4c {
+ compatible = "ti,tmp421";
+ reg = <0x4c>;
+ };
+};
+
+&i2c8 {
+ status = "okay";
+
+ adm1278@11 {
+ compatible = "adm1278";
+ reg = <0x11>;
+ Rsense = <500>;
+ };
+};
+
+&i2c9 {
+ status = "okay";
+};
+
+&i2c10 {
+ status = "okay";
+
+ gpio: pca9555@27 {
+ compatible = "nxp,pca9555";
+ reg = <0x27>;
+
+ gpio-controller;
+ #gpio-cells = <2>;
+ };
+};
+
+&i2c11 {
+ status = "okay";
+
+ pca9539_g1a: pca9539-g1a@74 {
+ compatible = "nxp,pca9539";
+ reg = <0x74>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ reset-gpios = <&gpio7 4 GPIO_ACTIVE_LOW>;
+ G1A_P0_0 {
+ gpio-hog;
+ gpios = <0 0>;
+ output-high;
+ line-name = "TPM_BMC_ALERT_N";
+ };
+ G1A_P0_1 {
+ gpio-hog;
+ gpios = <1 0>;
+ input;
+ line-name = "FM_BIOS_TOP_SWAP";
+ };
+ G1A_P0_2 {
+ gpio-hog;
+ gpios = <2 0>;
+ input;
+ line-name = "FM_BIOS_PREFRB2_GOOD";
+ };
+ G1A_P0_3 {
+ gpio-hog;
+ gpios = <3 0>;
+ input;
+ line-name = "BMC_SATAXPCIE_0TO3_SEL";
+ };
+ G1A_P0_4 {
+ gpio-hog;
+ gpios = <4 0>;
+ input;
+ line-name = "BMC_SATAXPCIE_4TO7_SEL";
+ };
+ G1A_P0_5 {
+ gpio-hog;
+ gpios = <5 0>;
+ output-low;
+ line-name = "FM_UV_ADR_TRIGGER_EN_N";
+ };
+ G1A_P0_6 {
+ gpio-hog;
+ gpios = <6 0>;
+ input;
+ line-name = "RM_THROTTLE_EN_N";
+ };
+ G1A_P1_0 {
+ gpio-hog;
+ gpios = <8 0>;
+ input;
+ line-name = "FM_BMC_TPM_PRES_N";
+ };
+ G1A_P1_1 {
+ gpio-hog;
+ gpios = <9 0>;
+ input;
+ line-name = "FM_CPU0_SKTOCC_LVT3_N";
+ };
+ G1A_P1_2 {
+ gpio-hog;
+ gpios = <10 0>;
+ input;
+ line-name = "FM_CPU1_SKTOCC_LVT3_N";
+ };
+ G1A_P1_3 {
+ gpio-hog;
+ gpios = <11 0>;
+ input;
+ line-name = "PSU1_ALERT_N";
+ };
+ G1A_P1_4 {
+ gpio-hog;
+ gpios = <12 0>;
+ input;
+ line-name = "PSU2_ALERT_N";
+ };
+ G1A_P1_5 {
+ gpio-hog;
+ gpios = <13 0>;
+ input;
+ line-name = "H_CPU0_FAST_WAKE_LVT3_N";
+ };
+ G1A_P1_6 {
+ gpio-hog;
+ gpios = <14 0>;
+ output-high;
+ line-name = "I2C_MUX1_RESET_N";
+ };
+ G1A_P1_7 {
+ gpio-hog;
+ gpios = <15 0>;
+ input;
+ line-name = "FM_CPU_CATERR_LVT3_N";
+ };
+ };
+
+ pca9539_g1b: pca9539-g1b@75 {
+ compatible = "nxp,pca9539";
+ reg = <0x75>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ G1B_P0_0 {
+ gpio-hog;
+ gpios = <0 0>;
+ input;
+ line-name = "PVDDQ_ABC_PINALERT_N";
+ };
+ G1B_P0_1 {
+ gpio-hog;
+ gpios = <1 0>;
+ input;
+ line-name = "PVDDQ_DEF_PINALERT_N";
+ };
+ G1B_P0_2 {
+ gpio-hog;
+ gpios = <2 0>;
+ input;
+ line-name = "PVDDQ_GHJ_PINALERT_N";
+ };
+ G1B_P0_3 {
+ gpio-hog;
+ gpios = <3 0>;
+ input;
+ line-name = "PVDDQ_KLM_PINALERT_N";
+ };
+ G1B_P0_5 {
+ gpio-hog;
+ gpios = <5 0>;
+ input;
+ line-name = "FM_BOARD_REV_ID0";
+ };
+ G1B_P0_6 {
+ gpio-hog;
+ gpios = <6 0>;
+ input;
+ line-name = "FM_BOARD_REV_ID1";
+ };
+ G1B_P0_7 {
+ gpio-hog;
+ gpios = <7 0>;
+ input;
+ line-name = "FM_BOARD_REV_ID2";
+ };
+ G1B_P1_0 {
+ gpio-hog;
+ gpios = <8 0>;
+ input;
+ line-name = "FM_OC_DETECT_EN_N";
+ };
+ G1B_P1_1 {
+ gpio-hog;
+ gpios = <9 0>;
+ input;
+ line-name = "FM_FLASH_DESC_OVERRIDE";
+ };
+ G1B_P1_2 {
+ gpio-hog;
+ gpios = <10 0>;
+ output-low;
+ line-name = "FP_PWR_ID_LED_N";
+ };
+ G1B_P1_3 {
+ gpio-hog;
+ gpios = <11 0>;
+ output-low;
+ line-name = "BMC_LED_PWR_GRN";
+ };
+ G1B_P1_4 {
+ gpio-hog;
+ gpios = <12 0>;
+ output-low;
+ line-name = "BMC_LED_PWR_AMBER";
+ };
+ G1B_P1_5 {
+ gpio-hog;
+ gpios = <13 0>;
+ output-high;
+ line-name = "FM_BMC_FAULT_LED_N";
+ };
+ G1B_P1_6 {
+ gpio-hog;
+ gpios = <14 0>;
+ output-high;
+ line-name = "FM_CPLD_BMC_PWRDN_N";
+ };
+ G1B_P1_7 {
+ gpio-hog;
+ gpios = <15 0>;
+ output-high;
+ line-name = "BMC_LED_CATERR_N";
+ };
+ };
+};
+
+&i2c12 {
+ status = "okay";
+
+ pca9539_g2a: pca9539-g2a@74 {
+ compatible = "nxp,pca9539";
+ reg = <0x74>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ reset-gpios = <&gpio5 28 GPIO_ACTIVE_LOW>;
+ G2A_P0_0 {
+ gpio-hog;
+ gpios = <0 0>;
+ output-high;
+ line-name = "BMC_PON_RST_REQ_N";
+ };
+ G2A_P0_1 {
+ gpio-hog;
+ gpios = <1 0>;
+ output-high;
+ line-name = "BMC_RST_IND_REQ_N";
+ };
+ G2A_P0_2 {
+ gpio-hog;
+ gpios = <2 0>;
+ input;
+ line-name = "RST_BMC_RTCRST";
+ };
+ G2A_P0_3 {
+ gpio-hog;
+ gpios = <3 0>;
+ output-high;
+ line-name = "FM_BMC_PWRBTN_OUT_N";
+ };
+ G2A_P0_4 {
+ gpio-hog;
+ gpios = <4 0>;
+ output-high;
+ line-name = "RST_BMC_SYSRST_BTN_OUT_N";
+ };
+ G2A_P0_5 {
+ gpio-hog;
+ gpios = <5 0>;
+ output-high;
+ line-name = "FM_BATTERY_SENSE_EN_N";
+ };
+ G2A_P0_6 {
+ gpio-hog;
+ gpios = <6 0>;
+ output-high;
+ line-name = "FM_BMC_READY_N";
+ };
+ G2A_P0_7 {
+ gpio-hog;
+ gpios = <7 0>;
+ input;
+ line-name = "IRQ_BMC_PCH_SMI_LPC_N";
+ };
+ G2A_P1_0 {
+ gpio-hog;
+ gpios = <8 0>;
+ input;
+ line-name = "FM_SLOT4_CFG0";
+ };
+ G2A_P1_1 {
+ gpio-hog;
+ gpios = <9 0>;
+ input;
+ line-name = "FM_SLOT4_CFG1";
+ };
+ G2A_P1_2 {
+ gpio-hog;
+ gpios = <10 0>;
+ input;
+ line-name = "FM_NVDIMM_EVENT_N";
+ };
+ G2A_P1_3 {
+ gpio-hog;
+ gpios = <11 0>;
+ input;
+ line-name = "PSU1_BLADE_EN_N";
+ };
+ G2A_P1_4 {
+ gpio-hog;
+ gpios = <12 0>;
+ input;
+ line-name = "BMC_PCH_FNM";
+ };
+ G2A_P1_5 {
+ gpio-hog;
+ gpios = <13 0>;
+ input;
+ line-name = "FM_SOL_UART_CH_SEL";
+ };
+ G2A_P1_6 {
+ gpio-hog;
+ gpios = <14 0>;
+ input;
+ line-name = "FM_BIOS_POST_CMPLT_N";
+ };
+ };
+
+ pca9539_g2b: pca9539-g2b@75 {
+ compatible = "nxp,pca9539";
+ reg = <0x75>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ G2B_P0_0 {
+ gpio-hog;
+ gpios = <0 0>;
+ input;
+ line-name = "FM_CPU_MSMI_LVT3_N";
+ };
+ G2B_P0_1 {
+ gpio-hog;
+ gpios = <1 0>;
+ input;
+ line-name = "FM_BIOS_MRC_DEBUG_MSG_DIS";
+ };
+ G2B_P0_2 {
+ gpio-hog;
+ gpios = <2 0>;
+ input;
+ line-name = "FM_CPU1_DISABLE_BMC_N";
+ };
+ G2B_P0_3 {
+ gpio-hog;
+ gpios = <3 0>;
+ output-low;
+ line-name = "BMC_JTAG_SELECT";
+ };
+ G2B_P0_4 {
+ gpio-hog;
+ gpios = <4 0>;
+ output-high;
+ line-name = "PECI_MUX_SELECT";
+ };
+ G2B_P0_5 {
+ gpio-hog;
+ gpios = <5 0>;
+ output-high;
+ line-name = "I2C_MUX2_RESET_N";
+ };
+ G2B_P0_6 {
+ gpio-hog;
+ gpios = <6 0>;
+ input;
+ line-name = "FM_BMC_CPLD_PSU2_ON";
+ };
+ G2B_P0_7 {
+ gpio-hog;
+ gpios = <7 0>;
+ output-high;
+ line-name = "PSU2_ALERT_EN_N";
+ };
+ G2B_P1_0 {
+ gpio-hog;
+ gpios = <8 0>;
+ output-high;
+ line-name = "FM_CPU_BMC_INIT";
+ };
+ G2B_P1_1 {
+ gpio-hog;
+ gpios = <9 0>;
+ output-high;
+ line-name = "IRQ_BMC_PCH_SCI_LPC_N";
+ };
+ G2B_P1_2 {
+ gpio-hog;
+ gpios = <10 0>;
+ output-low;
+ line-name = "PMB_ALERT_EN_N";
+ };
+ G2B_P1_3 {
+ gpio-hog;
+ gpios = <11 0>;
+ output-high;
+ line-name = "FM_FAST_PROCHOT_EN_N";
+ };
+ G2B_P1_4 {
+ gpio-hog;
+ gpios = <12 0>;
+ output-high;
+ line-name = "BMC_NVDIMM_PRSNT_N";
+ };
+ G2B_P1_5 {
+ gpio-hog;
+ gpios = <13 0>;
+ output-low;
+ line-name = "FM_BACKUP_BIOS_SEL_H_BMC";
+ };
+ G2B_P1_6 {
+ gpio-hog;
+ gpios = <14 0>;
+ output-high;
+ line-name = "FM_PWRBRK_N";
+ };
+ };
+};
+
+&i2c13 {
+ status = "okay";
+
+ tmp75@4a {
+ compatible = "ti,tmp75";
+ reg = <0x4a>;
+ status = "okay";
+ };
+ m24128_fru@51 {
+ compatible = "atmel,24c128";
+ reg = <0x51>;
+ pagesize = <64>;
+ status = "okay";
+ };
+};
+
+&pwm_fan {
+ pinctrl-names = "default";
+ pinctrl-0 = < &pwm0_pins &pwm1_pins
+ &fanin0_pins &fanin1_pins
+ &fanin2_pins &fanin3_pins
+ &fanin4_pins &fanin5_pins
+ &fanin6_pins &fanin7_pins
+ &fanin8_pins &fanin9_pins
+ &fanin10_pins &fanin11_pins>;
+ status = "okay";
+
+ fan@0 {
+ reg = <0x00>;
+ fan-tach-ch = /bits/ 8 <0x00 0x01>;
+ cooling-levels = <127 255>;
+ };
+ fan@1 {
+ reg = <0x01>;
+ fan-tach-ch = /bits/ 8 <0x02 0x03>;
+ cooling-levels = /bits/ 8 <127 255>;
+ };
+ fan@2 {
+ reg = <0x02>;
+ fan-tach-ch = /bits/ 8 <0x04 0x05>;
+ cooling-levels = /bits/ 8 <127 255>;
+ };
+ fan@3 {
+ reg = <0x03>;
+ fan-tach-ch = /bits/ 8 <0x06 0x07>;
+ cooling-levels = /bits/ 8 <127 255>;
+ };
+ fan@4 {
+ reg = <0x04>;
+ fan-tach-ch = /bits/ 8 <0x08 0x09>;
+ cooling-levels = /bits/ 8 <127 255>;
+ };
+ fan@5 {
+ reg = <0x05>;
+ fan-tach-ch = /bits/ 8 <0x0A 0x0B>;
+ cooling-levels = /bits/ 8 <127 255>;
+ };
+ fan@6 {
+ reg = <0x06>;
+ fan-tach-ch = /bits/ 8 <0x0C 0x0D>;
+ cooling-levels = /bits/ 8 <127 255>;
+ };
+ fan@7 {
+ reg = <0x07>;
+ fan-tach-ch = /bits/ 8 <0x0E 0x0F>;
+ cooling-levels = /bits/ 8 <127 255>;
+ };
+};
+
+&peci0 {
+ cmd-timeout-ms = <1000>;
+ pull-down = <0>;
+ host-neg-bit-rate = <15>;
+ status = "okay";
+
+ intel-peci-dimmtemp@30 {
+ compatible = "intel,peci-client";
+ reg = <0x30>;
+ };
+ intel-peci-dimmtemp@31 {
+ compatible = "intel,peci-client";
+ reg = <0x31>;
+ };
+};
+
+&ehci1 {
+ status = "okay";
+};
+
+&ohci1 {
+ status = "okay";
+};
+
+&udc0 {
+ status = "okay";
+};
+
+&udc1 {
+ status = "okay";
+};
+
+&udc2 {
+ status = "okay";
+};
+
+&udc3 {
+ status = "okay";
+};
+
+&udc4 {
+ status = "okay";
+};
+
+&udc5 {
+ status = "okay";
+};
+
+&udc6 {
+ status = "okay";
+};
+
+&udc7 {
+ status = "okay";
+};
+
+&udc8 {
+ status = "okay";
+};
+
+&udc9 {
+ status = "okay";
+};
+
+&aes {
+ status = "okay";
+};
+
+&sha {
+ status = "okay";
+};
+
+
+&sdhci0 {
+ status = "okay";
+};
+
+&pcimbox {
+ status = "okay";
+};
+
+&vcd {
+ status = "okay";
+};
+
+&ece {
+ status = "okay";
+};
+
+&watchdog1 {
+ status = "okay";
+};
+
+&rng {
+ status = "okay";
+};
+
+&serial0 {
+ status = "okay";
+};
+
+&serial1 {
+ status = "okay";
+};
+
+&serial2 {
+ status = "okay";
+};
+
+&serial3 {
+ status = "okay";
+};
+
+&adc {
+ #io-channel-cells = <1>;
+ status = "okay";
+};
+
+&otp {
+ status = "okay";
+};
+
+&kcs1 {
+ status = "okay";
+};
+
+&kcs2 {
+ status = "okay";
+};
+
+&kcs3 {
+ status = "okay";
+};
+
+&lpc_bpc {
+ monitor-ports = <0x80>;
+ status = "okay";
+};
+
+&spi0 {
+ cs-gpios = <&gpio6 11 GPIO_ACTIVE_LOW>;
+ status = "okay";
+};
+
+&spi1 {
+ status = "okay";
+};
+
+&pinctrl {
+ pinctrl-names = "default";
+ pinctrl-0 = <
+ /******* RunBMC inside Module pins *******/
+ &gpio0ol_pins
+ &gpio1ol_pins
+ &gpio2ol_pins
+ &gpio3ol_pins
+ &gpio8o_pins
+ &gpio9ol_pins
+ &gpio12ol_pins
+ &gpio13ol_pins
+ &gpio14ol_pins
+ &gpio15ol_pins
+ &gpio37o_pins
+ &gpio38_pins
+ &gpio39_pins
+ &gpio94ol_pins
+ &gpio108ol_pins
+ &gpio109ol_pins
+ &gpio111ol_pins
+ &gpio112ol_pins
+ &gpio113ol_pins
+ &gpio208_pins
+ &gpio209ol_pins
+ &gpio210ol_pins
+ &gpio211ol_pins
+ &gpio212ol_pins
+ &gpio213ol_pins
+ &gpio214ol_pins
+ &gpio215ol_pins
+ &gpio216ol_pins
+ &gpio217ol_pins
+ /******* RunBMC outside Connector pins *******/
+ &gpio5_pins
+ &gpio6_pins
+ &gpio7_pins
+ &gpio10_pins
+ &gpio11_pins
+ &gpio20_pins
+ &gpio21_pins
+ &gpio22o_pins
+ &gpio23_pins
+ &gpio24_pins
+ &gpio25_pins
+ &gpio30_pins
+ &gpio31_pins
+ &gpio40o_pins
+ &gpio59_pins
+ &gpio76_pins
+ &gpio77_pins
+ &gpio78o_pins
+ &gpio79_pins
+ &gpio82_pins
+ &gpio83_pins
+ &gpio84_pins
+ &gpio85o_pins
+ &gpio86ol_pins
+ &gpio87_pins
+ &gpio88_pins
+ &gpio89_pins
+ &gpio90_pins
+ &gpio93_pins
+ &gpio114o_pins
+ &gpio115_pins
+ &gpio120_pins
+ &gpio121_pins
+ &gpio122_pins
+ &gpio123_pins
+ &gpio124_pins
+ &gpio125_pins
+ &gpio126_pins
+ &gpio127o_pins
+ &gpio136_pins
+ &gpio137_pins
+ &gpio138_pins
+ &gpio139_pins
+ &gpio140_pins
+ &gpio141_pins
+ &gpio142_pins
+ &gpio143_pins
+ &gpio144_pins
+ &gpio146_pins
+ &gpio145_pins
+ &gpio147_pins
+ &gpio153o_pins
+ &gpio155_pins
+ &gpio160o_pins
+ &gpio169o_pins
+ &gpio188o_pins
+ &gpio189_pins
+ &gpio196_pins
+ &gpio197_pins
+ &gpio198o_pins
+ &gpio199o_pins
+ &gpio200_pins
+ &gpio202_pins
+ &gpio203o_pins
+ &gpio224_pins
+ &gpio225ol_pins
+ &gpio226ol_pins
+ &gpio227ol_pins
+ &gpio228o_pins
+ &gpio229o_pins
+ &gpio230_pins
+ &gpio231o_pins
+ &ddc_pins
+ &wdog1_pins
+ &wdog2_pins
+ >;
+};
diff --git a/arch/arm/boot/dts/nuvoton-npcm750.dtsi b/arch/arm/boot/dts/nuvoton-npcm750.dtsi
index 6ac340533587..14b3d5b1206f 100644
--- a/arch/arm/boot/dts/nuvoton-npcm750.dtsi
+++ b/arch/arm/boot/dts/nuvoton-npcm750.dtsi
@@ -3,6 +3,7 @@
// Copyright 2018 Google, Inc.
#include "nuvoton-common-npcm7xx.dtsi"
+#include "nuvoton-npcm750-gpio.dtsi"
/ {
#address-cells = <1>;
@@ -17,7 +18,7 @@
cpu@0 {
device_type = "cpu";
compatible = "arm,cortex-a9";
- clocks = <&clk 0>;
+ clocks = <&clk NPCM7XX_CLK_CPU>;
clock-names = "clk_cpu";
reg = <0>;
next-level-cache = <&l2>;
@@ -26,19 +27,152 @@
cpu@1 {
device_type = "cpu";
compatible = "arm,cortex-a9";
- clocks = <&clk 0>;
+ clocks = <&clk NPCM7XX_CLK_CPU>;
clock-names = "clk_cpu";
reg = <1>;
next-level-cache = <&l2>;
};
};
+
soc {
timer@3fe600 {
compatible = "arm,cortex-a9-twd-timer";
reg = <0x3fe600 0x20>;
interrupts = <GIC_PPI 13 (GIC_CPU_MASK_SIMPLE(2) |
IRQ_TYPE_LEVEL_HIGH)>;
- clocks = <&clk 5>;
+ clocks = <&clk NPCM7XX_CLK_AHB>;
+ };
+ };
+
+ ahb {
+ gmac1: eth@f0804000 {
+ device_type = "network";
+ compatible = "snps,dwmac";
+ reg = <0xf0804000 0x2000>;
+ interrupts = <GIC_SPI 17 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "macirq";
+ ethernet = <1>;
+ clocks = <&clk_rg2refck>, <&clk NPCM7XX_CLK_AHB>;
+ clock-names = "stmmaceth", "clk_gmac";
+ pinctrl-names = "default";
+ pinctrl-0 = <&rg2_pins
+ &rg2mdio_pins>;
+ status = "disabled";
+ };
+
+ emc1: eth@f0826000 {
+ device_type = "network";
+ compatible = "nuvoton,npcm750-emc";
+ reg = <0xf0826000 0x1000>;
+ interrupts = <GIC_SPI 115 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 114 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&clk NPCM7XX_CLK_AHB>;
+ clock-names = "clk_emc";
+ pinctrl-names = "default";
+ pinctrl-0 = <&r2_pins
+ &r2err_pins
+ &r2md_pins>;
+ status = "disabled";
+ };
+
+ udc0:udc@f0830000 {
+ compatible = "nuvoton,npcm750-udc";
+ reg = <0xf0830000 0x1000
+ 0xfffd0000 0x800>;
+ interrupts = <GIC_SPI 51 IRQ_TYPE_LEVEL_HIGH>;
+ status = "disabled";
+ clocks = <&clk NPCM7XX_CLK_SU>;
+ clock-names = "clk_usb_bridge";
+ };
+
+ udc1:udc@f0831000 {
+ compatible = "nuvoton,npcm750-udc";
+ reg = <0xf0831000 0x1000
+ 0xfffd0800 0x800>;
+ interrupts = <GIC_SPI 52 IRQ_TYPE_LEVEL_HIGH>;
+ status = "disabled";
+ clocks = <&clk NPCM7XX_CLK_SU>;
+ clock-names = "clk_usb_bridge";
+ };
+
+ udc2:udc@f0832000 {
+ compatible = "nuvoton,npcm750-udc";
+ reg = <0xf0832000 0x1000
+ 0xfffd1000 0x800>;
+ interrupts = <GIC_SPI 53 IRQ_TYPE_LEVEL_HIGH>;
+ status = "disabled";
+ clocks = <&clk NPCM7XX_CLK_SU>;
+ clock-names = "clk_usb_bridge";
+ };
+
+ udc3:udc@f0833000 {
+ compatible = "nuvoton,npcm750-udc";
+ reg = <0xf0833000 0x1000
+ 0xfffd1800 0x800>;
+ interrupts = <GIC_SPI 54 IRQ_TYPE_LEVEL_HIGH>;
+ status = "disabled";
+ clocks = <&clk NPCM7XX_CLK_SU>;
+ clock-names = "clk_usb_bridge";
+ };
+
+ udc4:udc@f0834000 {
+ compatible = "nuvoton,npcm750-udc";
+ reg = <0xf0834000 0x1000
+ 0xfffd2000 0x800>;
+ interrupts = <GIC_SPI 55 IRQ_TYPE_LEVEL_HIGH>;
+ status = "disabled";
+ clocks = <&clk NPCM7XX_CLK_SU>;
+ clock-names = "clk_usb_bridge";
+ };
+
+ udc5:udc@f0835000 {
+ compatible = "nuvoton,npcm750-udc";
+ reg = <0xf0835000 0x1000
+ 0xfffd2800 0x800>;
+ interrupts = <GIC_SPI 56 IRQ_TYPE_LEVEL_HIGH>;
+ status = "disabled";
+ clocks = <&clk NPCM7XX_CLK_SU>;
+ clock-names = "clk_usb_bridge";
+ };
+
+ udc6:udc@f0836000 {
+ compatible = "nuvoton,npcm750-udc";
+ reg = <0xf0836000 0x1000
+ 0xfffd3000 0x800>;
+ interrupts = <GIC_SPI 57 IRQ_TYPE_LEVEL_HIGH>;
+ status = "disabled";
+ clocks = <&clk NPCM7XX_CLK_SU>;
+ clock-names = "clk_usb_bridge";
+ };
+
+ udc7:udc@f0837000 {
+ compatible = "nuvoton,npcm750-udc";
+ reg = <0xf0837000 0x1000
+ 0xfffd3800 0x800>;
+ interrupts = <GIC_SPI 58 IRQ_TYPE_LEVEL_HIGH>;
+ status = "disabled";
+ clocks = <&clk NPCM7XX_CLK_SU>;
+ clock-names = "clk_usb_bridge";
+ };
+
+ udc8:udc@f0838000 {
+ compatible = "nuvoton,npcm750-udc";
+ reg = <0xf0838000 0x1000
+ 0xfffd4000 0x800>;
+ interrupts = <GIC_SPI 59 IRQ_TYPE_LEVEL_HIGH>;
+ status = "disabled";
+ clocks = <&clk NPCM7XX_CLK_SU>;
+ clock-names = "clk_usb_bridge";
+ };
+
+ udc9:udc@f0839000 {
+ compatible = "nuvoton,npcm750-udc";
+ reg = <0xf0839000 0x1000
+ 0xfffd4800 0x800>;
+ interrupts = <GIC_SPI 60 IRQ_TYPE_LEVEL_HIGH>;
+ status = "disabled";
+ clocks = <&clk NPCM7XX_CLK_SU>;
+ clock-names = "clk_usb_bridge";
};
};
};
diff --git a/arch/arm/configs/aspeed_g4_defconfig b/arch/arm/configs/aspeed_g4_defconfig
index 303f75a3baec..99ab15135acc 100644
--- a/arch/arm/configs/aspeed_g4_defconfig
+++ b/arch/arm/configs/aspeed_g4_defconfig
@@ -3,6 +3,8 @@ CONFIG_KERNEL_XZ=y
CONFIG_SYSVIPC=y
CONFIG_NO_HZ_IDLE=y
CONFIG_HIGH_RES_TIMERS=y
+CONFIG_PSI=y
+CONFIG_PSI_DEFAULT_DISABLED=y
CONFIG_IKCONFIG=y
CONFIG_IKCONFIG_PROC=y
CONFIG_LOG_BUF_SHIFT=16
@@ -30,14 +32,11 @@ CONFIG_SECCOMP=y
# CONFIG_ATAGS is not set
CONFIG_ZBOOT_ROM_TEXT=0x0
CONFIG_ZBOOT_ROM_BSS=0x0
-CONFIG_ARM_APPENDED_DTB=y
-CONFIG_ARM_ATAG_DTB_COMPAT=y
CONFIG_KEXEC=y
CONFIG_FIRMWARE_MEMMAP=y
CONFIG_JUMP_LABEL=y
CONFIG_STRICT_KERNEL_RWX=y
# CONFIG_BLK_DEV_BSG is not set
-# CONFIG_BLK_DEBUG_FS is not set
# CONFIG_MQ_IOSCHED_DEADLINE is not set
# CONFIG_MQ_IOSCHED_KYBER is not set
# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
@@ -49,8 +48,16 @@ CONFIG_UNIX=y
CONFIG_UNIX_DIAG=y
CONFIG_INET=y
CONFIG_IP_MULTICAST=y
+CONFIG_IP_ADVANCED_ROUTER=y
+CONFIG_IP_MULTIPLE_TABLES=y
+CONFIG_IP_ROUTE_MULTIPATH=y
+CONFIG_IP_ROUTE_VERBOSE=y
CONFIG_SYN_COOKIES=y
# CONFIG_INET_DIAG is not set
+CONFIG_IPV6_ROUTER_PREF=y
+CONFIG_IPV6_ROUTE_INFO=y
+CONFIG_IPV6_OPTIMISTIC_DAD=y
+CONFIG_IPV6_MULTIPLE_TABLES=y
CONFIG_NETFILTER=y
# CONFIG_NETFILTER_ADVANCED is not set
CONFIG_VLAN_8021Q=y
@@ -69,6 +76,7 @@ CONFIG_MTD_UBI=y
CONFIG_MTD_UBI_FASTMAP=y
CONFIG_MTD_UBI_BLOCK=y
CONFIG_BLK_DEV_LOOP=y
+CONFIG_BLK_DEV_NBD=y
CONFIG_EEPROM_AT24=y
CONFIG_NETDEVICES=y
CONFIG_NETCONSOLE=y
@@ -90,6 +98,7 @@ CONFIG_FTGMAC100=y
# CONFIG_NET_VENDOR_MARVELL is not set
# CONFIG_NET_VENDOR_MELLANOX is not set
# CONFIG_NET_VENDOR_MICREL is not set
+# CONFIG_NET_VENDOR_MICROCHIP is not set
# CONFIG_NET_VENDOR_MICROSEMI is not set
# CONFIG_NET_VENDOR_NATSEMI is not set
# CONFIG_NET_VENDOR_NETRONOME is not set
@@ -148,6 +157,7 @@ CONFIG_SENSORS_ASPEED=y
CONFIG_SENSORS_IIO_HWMON=y
CONFIG_SENSORS_LM75=y
CONFIG_SENSORS_NCT7904=y
+CONFIG_SENSORS_OCC_P8_I2C=y
CONFIG_PMBUS=y
CONFIG_SENSORS_ADM1275=y
CONFIG_SENSORS_IBM_CFFPS=y
@@ -164,6 +174,7 @@ CONFIG_MEDIA_CAMERA_SUPPORT=y
CONFIG_V4L_PLATFORM_DRIVERS=y
CONFIG_VIDEO_ASPEED=y
CONFIG_DRM=y
+CONFIG_DRM_ASPEED_GFX=y
CONFIG_USB=y
CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
CONFIG_USB_DYNAMIC_MINORS=y
@@ -253,6 +264,7 @@ CONFIG_DEBUG_INFO_REDUCED=y
CONFIG_DEBUG_INFO_DWARF4=y
CONFIG_GDB_SCRIPTS=y
CONFIG_STRIP_ASM_SYMS=y
+CONFIG_DEBUG_FS=y
CONFIG_SCHED_STACK_END_CHECK=y
CONFIG_PANIC_ON_OOPS=y
CONFIG_PANIC_TIMEOUT=-1
diff --git a/arch/arm/configs/aspeed_g5_defconfig b/arch/arm/configs/aspeed_g5_defconfig
index b0d056d49abe..0f3f73f97e3a 100644
--- a/arch/arm/configs/aspeed_g5_defconfig
+++ b/arch/arm/configs/aspeed_g5_defconfig
@@ -3,10 +3,15 @@ CONFIG_KERNEL_XZ=y
CONFIG_SYSVIPC=y
CONFIG_NO_HZ_IDLE=y
CONFIG_HIGH_RES_TIMERS=y
+CONFIG_PSI=y
+CONFIG_PSI_DEFAULT_DISABLED=y
CONFIG_IKCONFIG=y
CONFIG_IKCONFIG_PROC=y
CONFIG_LOG_BUF_SHIFT=16
CONFIG_CGROUPS=y
+CONFIG_CGROUP_BPF=y
+CONFIG_NAMESPACES=y
+CONFIG_USER_NS=y
CONFIG_BLK_DEV_INITRD=y
# CONFIG_RD_BZIP2 is not set
# CONFIG_RD_LZO is not set
@@ -42,7 +47,6 @@ CONFIG_KERNEL_MODE_NEON=y
CONFIG_FIRMWARE_MEMMAP=y
CONFIG_JUMP_LABEL=y
# CONFIG_BLK_DEV_BSG is not set
-# CONFIG_BLK_DEBUG_FS is not set
# CONFIG_MQ_IOSCHED_DEADLINE is not set
# CONFIG_MQ_IOSCHED_KYBER is not set
# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
@@ -82,7 +86,13 @@ CONFIG_MTD_UBI=y
CONFIG_MTD_UBI_FASTMAP=y
CONFIG_MTD_UBI_BLOCK=y
CONFIG_BLK_DEV_LOOP=y
+CONFIG_BLK_DEV_NBD=y
+CONFIG_MCTP_LPC=y
CONFIG_EEPROM_AT24=y
+CONFIG_EEPROM_AT25=y
+CONFIG_MD=y
+CONFIG_BLK_DEV_DM=y
+CONFIG_DM_VERITY=y
CONFIG_NETDEVICES=y
CONFIG_NETCONSOLE=y
# CONFIG_NET_VENDOR_ALACRITECH is not set
@@ -103,6 +113,7 @@ CONFIG_FTGMAC100=y
# CONFIG_NET_VENDOR_MARVELL is not set
# CONFIG_NET_VENDOR_MELLANOX is not set
# CONFIG_NET_VENDOR_MICREL is not set
+# CONFIG_NET_VENDOR_MICROCHIP is not set
# CONFIG_NET_VENDOR_MICROSEMI is not set
# CONFIG_NET_VENDOR_NATSEMI is not set
# CONFIG_NET_VENDOR_NETRONOME is not set
@@ -147,26 +158,37 @@ CONFIG_HW_RANDOM_TIMERIOMEM=y
# CONFIG_I2C_COMPAT is not set
CONFIG_I2C_CHARDEV=y
CONFIG_I2C_MUX=y
+CONFIG_I2C_MUX_GPIO=y
CONFIG_I2C_MUX_PCA9541=y
CONFIG_I2C_MUX_PCA954x=y
CONFIG_I2C_ASPEED=y
CONFIG_I2C_FSI=y
CONFIG_SPI=y
+CONFIG_SPI_FSI=y
CONFIG_GPIOLIB=y
CONFIG_GPIO_SYSFS=y
CONFIG_GPIO_ASPEED=y
CONFIG_GPIO_ASPEED_SGPIO=y
+CONFIG_GPIO_PCA953X=y
+CONFIG_GPIO_PCA953X_IRQ=y
CONFIG_W1=y
CONFIG_W1_MASTER_GPIO=y
CONFIG_W1_SLAVE_THERM=y
+CONFIG_SENSORS_ADT7475=y
CONFIG_SENSORS_ASPEED=y
CONFIG_SENSORS_IIO_HWMON=y
CONFIG_SENSORS_LM75=y
CONFIG_SENSORS_NCT7904=y
+CONFIG_SENSORS_OCC_P8_I2C=y
+CONFIG_SENSORS_OCC_P9_SBE=y
+CONFIG_SENSORS_PECI_CPUTEMP=y
+CONFIG_SENSORS_PECI_DIMMTEMP=y
CONFIG_PMBUS=y
CONFIG_SENSORS_ADM1275=y
CONFIG_SENSORS_IBM_CFFPS=y
CONFIG_SENSORS_IR35221=y
+CONFIG_SENSORS_IR38064=y
+CONFIG_SENSORS_ISL68137=y
CONFIG_SENSORS_LM25066=y
CONFIG_SENSORS_MAX31785=y
CONFIG_SENSORS_UCD9000=y
@@ -175,7 +197,8 @@ CONFIG_SENSORS_TMP421=y
CONFIG_SENSORS_W83773G=y
CONFIG_WATCHDOG_SYSFS=y
CONFIG_MEDIA_SUPPORT=y
-CONFIG_MEDIA_CAMERA_SUPPORT=y
+CONFIG_MEDIA_SUPPORT_FILTER=y
+CONFIG_MEDIA_PLATFORM_SUPPORT=y
CONFIG_V4L_PLATFORM_DRIVERS=y
CONFIG_VIDEO_ASPEED=y
CONFIG_DRM=y
@@ -187,22 +210,10 @@ CONFIG_USB_EHCI_HCD=y
CONFIG_USB_EHCI_ROOT_HUB_TT=y
CONFIG_USB_EHCI_HCD_PLATFORM=y
CONFIG_USB_GADGET=y
-CONFIG_U_SERIAL_CONSOLE=y
CONFIG_USB_ASPEED_VHUB=y
CONFIG_USB_CONFIGFS=y
-CONFIG_USB_CONFIGFS_SERIAL=y
-CONFIG_USB_CONFIGFS_ACM=y
-CONFIG_USB_CONFIGFS_OBEX=y
-CONFIG_USB_CONFIGFS_NCM=y
-CONFIG_USB_CONFIGFS_ECM=y
-CONFIG_USB_CONFIGFS_ECM_SUBSET=y
-CONFIG_USB_CONFIGFS_RNDIS=y
-CONFIG_USB_CONFIGFS_EEM=y
CONFIG_USB_CONFIGFS_MASS_STORAGE=y
-CONFIG_USB_CONFIGFS_F_LB_SS=y
-CONFIG_USB_CONFIGFS_F_FS=y
CONFIG_USB_CONFIGFS_F_HID=y
-CONFIG_USB_CONFIGFS_F_PRINTER=y
CONFIG_MMC=y
CONFIG_MMC_SDHCI=y
CONFIG_MMC_SDHCI_PLTFM=y
@@ -218,6 +229,7 @@ CONFIG_LEDS_TRIGGER_TIMER=y
CONFIG_LEDS_TRIGGER_HEARTBEAT=y
CONFIG_LEDS_TRIGGER_DEFAULT_ON=y
CONFIG_EDAC=y
+# CONFIG_EDAC_LEGACY_SYSFS is not set
CONFIG_EDAC_ASPEED=y
CONFIG_RTC_CLASS=y
CONFIG_RTC_DRV_DS1307=y
@@ -229,10 +241,12 @@ CONFIG_RTC_DRV_ASPEED=y
CONFIG_ASPEED_LPC_CTRL=y
CONFIG_ASPEED_LPC_SNOOP=y
CONFIG_ASPEED_P2A_CTRL=y
+CONFIG_ASPEED_XDMA=y
CONFIG_IIO=y
CONFIG_ASPEED_ADC=y
CONFIG_MAX1363=y
CONFIG_BMP280=y
+CONFIG_DPS310=y
CONFIG_RAS=y
CONFIG_FSI=y
CONFIG_FSI_MASTER_GPIO=y
@@ -242,6 +256,10 @@ CONFIG_FSI_MASTER_ASPEED=y
CONFIG_FSI_SCOM=y
CONFIG_FSI_SBEFIFO=y
CONFIG_FSI_OCC=y
+CONFIG_PECI=y
+CONFIG_PECI_CHARDEV=y
+CONFIG_PECI_ASPEED=y
+CONFIG_EXT4_FS=y
CONFIG_FANOTIFY=y
CONFIG_OVERLAY_FS=y
CONFIG_TMPFS=y
@@ -271,14 +289,17 @@ CONFIG_DEBUG_INFO_REDUCED=y
CONFIG_DEBUG_INFO_DWARF4=y
CONFIG_GDB_SCRIPTS=y
CONFIG_STRIP_ASM_SYMS=y
-CONFIG_SOFTLOCKUP_DETECTOR=y
-# CONFIG_DETECT_HUNG_TASK is not set
-CONFIG_WQ_WATCHDOG=y
+CONFIG_DEBUG_FS=y
+CONFIG_SCHED_STACK_END_CHECK=y
CONFIG_PANIC_ON_OOPS=y
CONFIG_PANIC_TIMEOUT=-1
+CONFIG_SOFTLOCKUP_DETECTOR=y
+CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC=y
+CONFIG_BOOTPARAM_HUNG_TASK_PANIC=y
+CONFIG_WQ_WATCHDOG=y
# CONFIG_SCHED_DEBUG is not set
-CONFIG_SCHED_STACK_END_CHECK=y
+CONFIG_DEBUG_LIST=y
CONFIG_FUNCTION_TRACER=y
-# CONFIG_RUNTIME_TESTING_MENU is not set
CONFIG_DEBUG_WX=y
CONFIG_DEBUG_USER=y
+# CONFIG_RUNTIME_TESTING_MENU is not set
diff --git a/arch/arm/configs/npcm7xx_defconfig b/arch/arm/configs/npcm7xx_defconfig
new file mode 100644
index 000000000000..62aaffdba4dd
--- /dev/null
+++ b/arch/arm/configs/npcm7xx_defconfig
@@ -0,0 +1,127 @@
+CONFIG_KERNEL_XZ=y
+CONFIG_SYSVIPC=y
+CONFIG_BSD_PROCESS_ACCT=y
+CONFIG_LOG_BUF_SHIFT=21
+CONFIG_CGROUPS=y
+CONFIG_SYSFS_DEPRECATED=y
+CONFIG_SYSFS_DEPRECATED_V2=y
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_SYSCTL_SYSCALL=y
+CONFIG_EMBEDDED=y
+CONFIG_SLAB=y
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+CONFIG_MODVERSIONS=y
+CONFIG_PARTITION_ADVANCED=y
+CONFIG_DEFAULT_DEADLINE=y
+CONFIG_ARCH_NPCM=y
+CONFIG_ARCH_NPCM7XX=y
+CONFIG_SMP=y
+CONFIG_VMSPLIT_3G_OPT=y
+CONFIG_ZBOOT_ROM_BSS=0x0
+CONFIG_BINFMT_MISC=y
+CONFIG_NET=y
+CONFIG_PACKET=y
+CONFIG_UNIX=y
+CONFIG_INET=y
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+CONFIG_IP_PNP_BOOTP=y
+CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+CONFIG_DEVTMPFS=y
+CONFIG_MTD=y
+CONFIG_MTD_BLOCK=y
+CONFIG_MTD_RAM=y
+CONFIG_MTD_COMPLEX_MAPPINGS=y
+CONFIG_MTD_M25P80=y
+CONFIG_MTD_SPI_NOR=y
+CONFIG_SPI_NPCM_FIU=y
+CONFIG_OF_OVERLAY=y
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_BLK_DEV_NBD=y
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_COUNT=1
+CONFIG_BLK_DEV_RAM_SIZE=8192
+CONFIG_NPCM7XX_LPC_BPC=y
+CONFIG_NPCM7XX_PCI_MBOX=y
+CONFIG_SCSI=y
+CONFIG_BLK_DEV_SD=y
+CONFIG_NETDEVICES=y
+CONFIG_NPCM7XX_EMC_ETH=y
+CONFIG_STMMAC_ETH=y
+CONFIG_BROADCOM_PHY=y
+CONFIG_SERIAL_8250=y
+CONFIG_SERIAL_8250_CONSOLE=y
+CONFIG_SERIAL_OF_PLATFORM=y
+CONFIG_NPCM750_OTP=y
+CONFIG_NPCM750_OTP_WRITE_ENABLE=y
+CONFIG_NPCM7XX_KCS_IPMI_BMC=y
+CONFIG_HW_RANDOM=y
+CONFIG_I2C=y
+CONFIG_I2C_CHARDEV=y
+CONFIG_I2C_NPCM7XX=y
+CONFIG_I2C_SLAVE_EEPROM=y
+CONFIG_SPI=y
+CONFIG_SPI_NPCM_PSPI=y
+CONFIG_GPIO_SYSFS=y
+CONFIG_GPIO_GENERIC_PLATFORM=y
+CONFIG_SENSORS_LM75=y
+CONFIG_SENSORS_NPCM7XX=y
+CONFIG_SENSORS_TMP102=y
+CONFIG_WATCHDOG=y
+CONFIG_REGULATOR=y
+CONFIG_REGULATOR_FIXED_VOLTAGE=y
+CONFIG_NPCM750_VCD=y
+CONFIG_NPCM750_ECE=y
+CONFIG_USB_HIDDEV=y
+CONFIG_USB=y
+CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
+CONFIG_USB_EHCI_HCD=y
+CONFIG_USB_EHCI_ROOT_HUB_TT=y
+CONFIG_USB_OHCI_HCD=y
+CONFIG_USB_STORAGE=y
+CONFIG_USB_CHIPIDEA=y
+CONFIG_USB_CHIPIDEA_UDC=y
+CONFIG_USB_GADGET=y
+CONFIG_USB_GADGET_NPCMX50_USB2=y
+CONFIG_USB_CONFIGFS=y
+CONFIG_USB_CONFIGFS_F_HID=y
+CONFIG_USB_MASS_STORAGE=m
+CONFIG_USB_EDM_KBD_MOUSE=m
+CONFIG_MMC=y
+CONFIG_MMC_SDHCI=y
+CONFIG_MMC_SDHCI_NPCM750=y
+CONFIG_IIO=y
+CONFIG_NPCM_ADC=y
+CONFIG_IIO_MUX=y
+CONFIG_MUX_MMIO=y
+CONFIG_MSDOS_FS=y
+CONFIG_VFAT_FS=y
+CONFIG_TMPFS=y
+CONFIG_ROMFS_FS=y
+CONFIG_NFS_FS=y
+CONFIG_ROOT_NFS=y
+CONFIG_CIFS=y
+CONFIG_CIFS_XATTR=y
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_ASCII=y
+CONFIG_NLS_ISO8859_1=y
+CONFIG_DYNAMIC_DEBUG=y
+CONFIG_DEBUG_INFO=y
+CONFIG_DEBUG_INFO_REDUCED=y
+CONFIG_READABLE_ASM=y
+CONFIG_DEBUG_SECTION_MISMATCH=y
+CONFIG_MAGIC_SYSRQ=y
+CONFIG_FUNCTION_TRACER=y
+CONFIG_CRYPTO_CBC=y
+CONFIG_CRYPTO_CRC32C=y
+CONFIG_CRYPTO_DEFLATE=y
+CONFIG_CRYPTO_LZO=y
+CONFIG_CRYPTO_USER_API_SKCIPHER=y
+CONFIG_CRYPTO_DEV_NPCMX50=y
+CONFIG_ARM_CRYPTO=y
+CONFIG_PECI=y
+CONFIG_PECI_NPCM=y
+CONFIG_MFD_INTEL_PECI_CLIENT=y
+CONFIG_SENSORS_PECI_CPUTEMP=y
+CONFIG_SENSORS_PECI_DIMMTEMP=y
diff --git a/drivers/Kconfig b/drivers/Kconfig
index dcecc9f6e33f..1bd21494d4cd 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -235,4 +235,7 @@ source "drivers/interconnect/Kconfig"
source "drivers/counter/Kconfig"
source "drivers/most/Kconfig"
+
+source "drivers/peci/Kconfig"
+
endmenu
diff --git a/drivers/Makefile b/drivers/Makefile
index c0cd1b9075e3..bbc6d661b9de 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -188,3 +188,4 @@ obj-$(CONFIG_GNSS) += gnss/
obj-$(CONFIG_INTERCONNECT) += interconnect/
obj-$(CONFIG_COUNTER) += counter/
obj-$(CONFIG_MOST) += most/
+obj-$(CONFIG_PECI) += peci/
diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
index 98c3a5d8003e..98d2e04ac24f 100644
--- a/drivers/char/Kconfig
+++ b/drivers/char/Kconfig
@@ -333,6 +333,15 @@ config DEVMEM
memory.
When in doubt, say "Y".
+config DEVMEM_BOOTPARAM
+ bool "mem.devmem boot parameter"
+ depends on DEVMEM
+ default n
+ help
+ This option adds a 'mem.devmem' kernel parameter which activates
+ the /dev/mem device when enabled.
+ When in doubt, say "N".
+
config DEVKMEM
bool "/dev/kmem virtual device support"
# On arm64, VMALLOC_START < PAGE_OFFSET, which confuses kmem read/write
diff --git a/drivers/char/ipmi/bt-bmc.c b/drivers/char/ipmi/bt-bmc.c
index a395e2e70dc5..a514f531cb34 100644
--- a/drivers/char/ipmi/bt-bmc.c
+++ b/drivers/char/ipmi/bt-bmc.c
@@ -508,6 +508,7 @@ static int bt_bmc_remove(struct platform_device *pdev)
static const struct of_device_id bt_bmc_match[] = {
{ .compatible = "aspeed,ast2400-ibt-bmc" },
{ .compatible = "aspeed,ast2500-ibt-bmc" },
+ { .compatible = "aspeed,ast2600-ibt-bmc" },
{ },
};
diff --git a/drivers/char/ipmi/kcs_bmc_aspeed.c b/drivers/char/ipmi/kcs_bmc_aspeed.c
index a140203c079b..2a2508d54e2a 100644
--- a/drivers/char/ipmi/kcs_bmc_aspeed.c
+++ b/drivers/char/ipmi/kcs_bmc_aspeed.c
@@ -400,6 +400,7 @@ static const struct of_device_id ast_kcs_bmc_match[] = {
{ .compatible = "aspeed,ast2500-kcs-bmc" },
{ .compatible = "aspeed,ast2400-kcs-bmc-v2" },
{ .compatible = "aspeed,ast2500-kcs-bmc-v2" },
+ { .compatible = "aspeed,ast2600-kcs-bmc" },
{ }
};
MODULE_DEVICE_TABLE(of, ast_kcs_bmc_match);
diff --git a/drivers/char/mem.c b/drivers/char/mem.c
index 687d4af6945d..0050a401ae46 100644
--- a/drivers/char/mem.c
+++ b/drivers/char/mem.c
@@ -10,6 +10,7 @@
*/
#include <linux/mm.h>
+#include <linux/moduleparam.h>
#include <linux/miscdevice.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
@@ -994,6 +995,12 @@ static char *mem_devnode(struct device *dev, umode_t *mode)
return NULL;
}
+#ifdef CONFIG_DEVMEM_BOOTPARAM
+static bool devmem;
+module_param(devmem, bool, 0444);
+MODULE_PARM_DESC(devmem, "kernel parameter to activate /dev/mem");
+#endif
+
static struct class *mem_class;
static int devmem_fs_init_fs_context(struct fs_context *fc)
@@ -1054,6 +1061,10 @@ static int __init chr_dev_init(void)
if (!devlist[minor].name)
continue;
+#ifdef CONFIG_DEVMEM_BOOTPARAM
+ if (minor == DEVMEM_MINOR && !devmem)
+ continue;
+#endif
/*
* Create /dev/port?
*/
diff --git a/drivers/clk/clk-ast2600.c b/drivers/clk/clk-ast2600.c
index 177368cac6dd..bbacaccad554 100644
--- a/drivers/clk/clk-ast2600.c
+++ b/drivers/clk/clk-ast2600.c
@@ -64,7 +64,7 @@ static const struct aspeed_gate_data aspeed_g6_gates[] = {
[ASPEED_CLK_GATE_GCLK] = { 2, 7, "gclk-gate", NULL, 0 }, /* 2D engine */
/* vclk parent - dclk/d1clk/hclk/mclk */
[ASPEED_CLK_GATE_VCLK] = { 3, 6, "vclk-gate", NULL, 0 }, /* Video Capture */
- [ASPEED_CLK_GATE_BCLK] = { 4, 8, "bclk-gate", "bclk", 0 }, /* PCIe/PCI */
+ [ASPEED_CLK_GATE_BCLK] = { 4, 8, "bclk-gate", "bclk", CLK_IS_CRITICAL }, /* PCIe/PCI */
/* From dpll */
[ASPEED_CLK_GATE_DCLK] = { 5, -1, "dclk-gate", NULL, CLK_IS_CRITICAL }, /* DAC */
[ASPEED_CLK_GATE_REF0CLK] = { 6, -1, "ref0clk-gate", "clkin", CLK_IS_CRITICAL },
diff --git a/drivers/edac/Kconfig b/drivers/edac/Kconfig
index 7b6ec3014ba2..fc30f2ef9782 100644
--- a/drivers/edac/Kconfig
+++ b/drivers/edac/Kconfig
@@ -530,4 +530,11 @@ config EDAC_DMC520
Support for error detection and correction on the
SoCs with ARM DMC-520 DRAM controller.
+config EDAC_NPCM7XX
+ tristate "Nuvoton NPCM7xx DDR Memory Controller"
+ depends on ARCH_NPCM7XX
+ help
+ Support for error detection and correction on the
+ Nuvoton NPCM7xx DDR memory controller.
+
endif # EDAC
diff --git a/drivers/edac/Makefile b/drivers/edac/Makefile
index 269e15118cea..25c77763d666 100644
--- a/drivers/edac/Makefile
+++ b/drivers/edac/Makefile
@@ -88,3 +88,4 @@ obj-$(CONFIG_EDAC_QCOM) += qcom_edac.o
obj-$(CONFIG_EDAC_ASPEED) += aspeed_edac.o
obj-$(CONFIG_EDAC_BLUEFIELD) += bluefield_edac.o
obj-$(CONFIG_EDAC_DMC520) += dmc520_edac.o
+obj-$(CONFIG_EDAC_NPCM7XX) += npcm7xx_edac.o
diff --git a/drivers/edac/npcm7xx_edac.c b/drivers/edac/npcm7xx_edac.c
new file mode 100644
index 000000000000..c60761766ec1
--- /dev/null
+++ b/drivers/edac/npcm7xx_edac.c
@@ -0,0 +1,421 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2019 Quanta Computer lnc.
+ */
+
+#include <linux/edac.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+
+#include "edac_module.h"
+
+#define ECC_ENABLE BIT(24)
+#define ECC_EN_INT_MASK 0x7fffff87
+
+#define INT_STATUS_ADDR 116
+#define INT_ACK_ADDR 117
+#define INT_MASK_ADDR 118
+
+#define ECC_EN_ADDR 93
+#define ECC_C_ADDR_ADDR 98
+#define ECC_C_DATA_ADDR 100
+#define ECC_C_ID_ADDR 101
+#define ECC_C_SYND_ADDR 99
+#define ECC_U_ADDR_ADDR 95
+#define ECC_U_DATA_ADDR 97
+#define ECC_U_ID_ADDR 101
+#define ECC_U_SYND_ADDR 96
+
+#define ECC_ERROR -1
+#define EDAC_MSG_SIZE 256
+#define EDAC_MOD_NAME "npcm7xx-edac"
+
+struct ecc_error_signature_info {
+ u32 ecc_addr;
+ u32 ecc_data;
+ u32 ecc_id;
+ u32 ecc_synd;
+};
+
+struct npcm7xx_ecc_int_status {
+ u32 int_mask;
+ u32 int_status;
+ u32 int_ack;
+ u32 ce_cnt;
+ u32 ue_cnt;
+ struct ecc_error_signature_info ceinfo;
+ struct ecc_error_signature_info ueinfo;
+};
+
+struct npcm7xx_edac_priv {
+ void __iomem *baseaddr;
+ char message[EDAC_MSG_SIZE];
+ struct npcm7xx_ecc_int_status stat;
+};
+
+/**
+ * npcm7xx_edac_get_ecc_syndrom - Get the current ecc error info
+ * @base: Pointer to the base address of the ddr memory controller
+ * @p: Pointer to the Nuvoton ecc status structure
+ *
+ * Determines there is any ecc error or not
+ *
+ * Return: ECC detection status
+ */
+static int npcm7xx_edac_get_ecc_syndrom(void __iomem *base,
+ struct npcm7xx_ecc_int_status *p)
+{
+ int status = 0;
+ u32 int_status = 0;
+
+ int_status = readl(base + 4*INT_STATUS_ADDR);
+ writel(int_status, base + 4*INT_ACK_ADDR);
+ edac_dbg(3, "int_status: %#08x\n", int_status);
+
+ if ((int_status & (1 << 6)) == (1 << 6)) {
+ edac_dbg(3, "6-Mult uncorrectable detected.\n");
+ p->ue_cnt++;
+ status = ECC_ERROR;
+ }
+
+ if ((int_status & (1 << 5)) == (1 << 5)) {
+ edac_dbg(3, "5-An uncorrectable detected\n");
+ p->ue_cnt++;
+ status = ECC_ERROR;
+ }
+
+ if ((int_status & (1 << 4)) == (1 << 4)) {
+ edac_dbg(3, "4-mult correctable detected.\n");
+ p->ce_cnt++;
+ status = ECC_ERROR;
+ }
+
+ if ((int_status & (1 << 3)) == (1 << 3)) {
+ edac_dbg(3, "3-A correctable detected.\n");
+ p->ce_cnt++;
+ status = ECC_ERROR;
+ }
+
+ if (status == ECC_ERROR) {
+ u32 ecc_id;
+
+ p->ceinfo.ecc_addr = readl(base + 4*ECC_C_ADDR_ADDR);
+ p->ceinfo.ecc_data = readl(base + 4*ECC_C_DATA_ADDR);
+ p->ceinfo.ecc_synd = readl(base + 4*ECC_C_SYND_ADDR);
+
+ p->ueinfo.ecc_addr = readl(base + 4*ECC_U_ADDR_ADDR);
+ p->ueinfo.ecc_data = readl(base + 4*ECC_U_DATA_ADDR);
+ p->ueinfo.ecc_synd = readl(base + 4*ECC_U_SYND_ADDR);
+
+ /* ECC_C_ID_ADDR has same value as ECC_U_ID_ADDR */
+ ecc_id = readl(base + 4*ECC_C_ID_ADDR);
+ p->ueinfo.ecc_id = ecc_id & 0xffff;
+ p->ceinfo.ecc_id = ecc_id >> 16;
+ }
+
+ return status;
+}
+
+/**
+ * npcm7xx_edac_handle_error - Handle controller error types CE and UE
+ * @mci: Pointer to the edac memory controller instance
+ * @p: Pointer to the Nuvoton ecc status structure
+ *
+ * Handles the controller ECC correctable and un correctable error.
+ */
+static void npcm7xx_edac_handle_error(struct mem_ctl_info *mci,
+ struct npcm7xx_ecc_int_status *p)
+{
+ struct npcm7xx_edac_priv *priv = mci->pvt_info;
+ u32 page, offset;
+
+ if (p->ce_cnt) {
+ snprintf(priv->message, EDAC_MSG_SIZE,
+ "DDR ECC: synd=%#08x addr=%#08x data=%#08x source_id=%#08x ",
+ p->ceinfo.ecc_synd, p->ceinfo.ecc_addr,
+ p->ceinfo.ecc_data, p->ceinfo.ecc_id);
+
+ page = p->ceinfo.ecc_addr >> PAGE_SHIFT;
+ offset = p->ceinfo.ecc_addr & ~PAGE_MASK;
+ edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci,
+ p->ce_cnt, page, offset,
+ p->ceinfo.ecc_synd,
+ 0, 0, -1,
+ priv->message, "");
+ }
+
+ if (p->ue_cnt) {
+ snprintf(priv->message, EDAC_MSG_SIZE,
+ "DDR ECC: synd=%#08x addr=%#08x data=%#08x source_id=%#08x ",
+ p->ueinfo.ecc_synd, p->ueinfo.ecc_addr,
+ p->ueinfo.ecc_data, p->ueinfo.ecc_id);
+
+ page = p->ueinfo.ecc_addr >> PAGE_SHIFT;
+ offset = p->ueinfo.ecc_addr & ~PAGE_MASK;
+ edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci,
+ p->ue_cnt, page, offset,
+ p->ueinfo.ecc_synd,
+ 0, 0, -1,
+ priv->message, "");
+ }
+
+ memset(p, 0, sizeof(*p));
+}
+
+/**
+ * npcm7xx_edac_check - Check controller for ECC errors
+ * @mci: Pointer to the edac memory controller instance
+ *
+ * This routine is used to check and post ECC errors and is called by
+ * this driver's CE and UE interrupt handler.
+ */
+static void npcm7xx_edac_check(struct mem_ctl_info *mci)
+{
+ struct npcm7xx_edac_priv *priv = mci->pvt_info;
+ int status = 0;
+
+ status = npcm7xx_edac_get_ecc_syndrom(priv->baseaddr, &priv->stat);
+ if (status != ECC_ERROR)
+ return;
+
+ npcm7xx_edac_handle_error(mci, &priv->stat);
+}
+
+/**
+ * npcm7xx_edac_isr - CE/UE interrupt service routine
+ * @irq: The virtual interrupt number being serviced.
+ * @dev_id: A pointer to the EDAC memory controller instance
+ * associated with the interrupt being handled.
+ *
+ * This routine implements the interrupt handler for both correctable
+ * (CE) and uncorrectable (UE) ECC errors for the Nuvoton Cadence DDR
+ * controller. It simply calls through to the routine used to check,
+ * report and clear the ECC status.
+ *
+ * Unconditionally returns IRQ_HANDLED.
+ */
+static irqreturn_t npcm7xx_edac_isr(int irq, void *dev_id)
+{
+ struct mem_ctl_info *mci = dev_id;
+
+ npcm7xx_edac_check(mci);
+
+ return IRQ_HANDLED;
+}
+
+static int npcm7xx_edac_register_irq(struct mem_ctl_info *mci,
+ struct platform_device *pdev)
+{
+ int status = 0;
+ int mc_irq;
+ struct npcm7xx_edac_priv *priv = mci->pvt_info;
+
+ /* Only enable MC interrupts with ECC - clear int_mask[6:3] */
+ writel(ECC_EN_INT_MASK, priv->baseaddr + 4*INT_MASK_ADDR);
+
+ mc_irq = platform_get_irq(pdev, 0);
+
+ if (!mc_irq) {
+ edac_printk(KERN_ERR, EDAC_MC, "Unable to map interrupts.\n");
+ status = -ENODEV;
+ goto fail;
+ }
+
+ status = devm_request_irq(&pdev->dev, mc_irq, npcm7xx_edac_isr, 0,
+ "npcm-memory-controller", mci);
+
+ if (status < 0) {
+ edac_printk(KERN_ERR, EDAC_MC,
+ "Unable to request irq %d for ECC",
+ mc_irq);
+ status = -ENODEV;
+ goto fail;
+ }
+
+ return 0;
+
+fail:
+ return status;
+}
+
+static const struct of_device_id npcm7xx_edac_of_match[] = {
+ { .compatible = "nuvoton,npcm7xx-sdram-edac"},
+ { /* end of table */ }
+};
+
+MODULE_DEVICE_TABLE(of, npcm7xx_edac_of_match);
+
+/**
+ * npcm7xx_edac_mc_init - Initialize driver instance
+ * @mci: Pointer to the edac memory controller instance
+ * @pdev: Pointer to the platform_device struct
+ *
+ * Performs initialization of the EDAC memory controller instance and
+ * related driver-private data associated with the memory controller the
+ * instance is bound to.
+ *
+ * Returns 0 if OK; otherwise, < 0 on error.
+ */
+static int npcm7xx_edac_mc_init(struct mem_ctl_info *mci,
+ struct platform_device *pdev)
+{
+ const struct of_device_id *id;
+
+ id = of_match_device(npcm7xx_edac_of_match, &pdev->dev);
+ if (!id)
+ return -ENODEV;
+
+ /* Initialize controller capabilities and configuration */
+ mci->mtype_cap = MEM_FLAG_DDR4;
+ mci->edac_ctl_cap = EDAC_FLAG_SECDED;
+ mci->edac_cap = EDAC_FLAG_SECDED;
+ mci->scrub_cap = SCRUB_FLAG_HW_SRC;
+ mci->scrub_mode = SCRUB_HW_SRC;
+ mci->ctl_name = id->compatible;
+ mci->dev_name = dev_name(&pdev->dev);
+ mci->mod_name = EDAC_MOD_NAME;
+
+ edac_op_state = EDAC_OPSTATE_INT;
+
+ return 0;
+}
+
+/**
+ * npcm7xx_edac_get_eccstate - Return the controller ecc enable/disable status
+ * @base: Pointer to the ddr memory controller base address
+ *
+ * Get the ECC enable/disable status for the controller
+ *
+ * Return: a ecc status boolean i.e true/false - enabled/disabled.
+ */
+static bool npcm7xx_edac_get_eccstate(void __iomem *base)
+{
+ u32 ecc_en;
+ bool state = false;
+
+ ecc_en = readl(base + 4*ECC_EN_ADDR);
+ if (ecc_en & ECC_ENABLE) {
+ edac_printk(KERN_INFO, EDAC_MC, "ECC reporting and correcting on. ");
+ state = true;
+ }
+
+ return state;
+}
+
+/**
+ * npcm7xx_edac_mc_probe - Check controller and bind driver
+ * @pdev: Pointer to the platform_device struct
+ *
+ * Probes a specific controller instance for binding with the driver.
+ *
+ * Return: 0 if the controller instance was successfully bound to the
+ * driver; otherwise, < 0 on error.
+ */
+static int npcm7xx_edac_mc_probe(struct platform_device *pdev)
+{
+ struct mem_ctl_info *mci;
+ struct edac_mc_layer layers[1];
+ struct npcm7xx_edac_priv *priv;
+ struct resource *res;
+ void __iomem *baseaddr;
+ int rc;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ baseaddr = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(baseaddr)) {
+ edac_printk(KERN_ERR, EDAC_MOD_NAME,
+ "DDR controller regs not defined\n");
+ return PTR_ERR(baseaddr);
+ }
+
+ /*
+ * Check if ECC is enabled.
+ * If not, there is no useful monitoring that can be done
+ * for this controller.
+ */
+ if (!npcm7xx_edac_get_eccstate(baseaddr)) {
+ edac_printk(KERN_INFO, EDAC_MC, "ECC disabled\n");
+ return -ENXIO;
+ }
+
+ /*
+ * Allocate an EDA controller instance and perform the appropriate
+ * initialization.
+ */
+ layers[0].type = EDAC_MC_LAYER_ALL_MEM;
+ layers[0].size = 1;
+
+ mci = edac_mc_alloc(0, ARRAY_SIZE(layers), layers,
+ sizeof(struct npcm7xx_edac_priv));
+ if (!mci) {
+ edac_printk(KERN_ERR, EDAC_MC,
+ "Failed memory allocation for mc instance\n");
+ return -ENOMEM;
+ }
+
+ mci->pdev = &pdev->dev;
+ priv = mci->pvt_info;
+ priv->baseaddr = baseaddr;
+ platform_set_drvdata(pdev, mci);
+
+ rc = npcm7xx_edac_mc_init(mci, pdev);
+ if (rc) {
+ edac_printk(KERN_ERR, EDAC_MC,
+ "Failed to initialize instance\n");
+ goto free_edac_mc;
+ }
+
+ /* Attempt to register it with the EDAC subsystem */
+ rc = edac_mc_add_mc(mci);
+ if (rc) {
+ edac_printk(KERN_ERR, EDAC_MC,
+ "Failed to register with EDAC core\n");
+ goto free_edac_mc;
+ }
+
+ /* Register interrupts */
+ rc = npcm7xx_edac_register_irq(mci, pdev);
+ if (rc)
+ goto free_edac_mc;
+
+ return 0;
+
+free_edac_mc:
+ edac_mc_free(mci);
+
+ return rc;
+}
+
+/**
+ * npcm7xx_edac_mc_remove - Unbind driver from controller
+ * @pdev: Pointer to the platform_device struct
+ *
+ * Return: Unconditionally 0
+ */
+static int npcm7xx_edac_mc_remove(struct platform_device *pdev)
+{
+ struct mem_ctl_info *mci = platform_get_drvdata(pdev);
+
+ edac_mc_del_mc(&pdev->dev);
+ edac_mc_free(mci);
+
+ return 0;
+}
+
+static struct platform_driver npcm7xx_edac_driver = {
+ .probe = npcm7xx_edac_mc_probe,
+ .remove = npcm7xx_edac_mc_remove,
+ .driver = {
+ .name = EDAC_MOD_NAME,
+ .of_match_table = npcm7xx_edac_of_match,
+ },
+};
+
+module_platform_driver(npcm7xx_edac_driver);
+
+MODULE_AUTHOR("Quanta Computer Inc.");
+MODULE_DESCRIPTION("Nuvoton NPCM7xx EDAC Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/fsi/fsi-core.c b/drivers/fsi/fsi-core.c
index 8244da8a7241..4e60e84cd17a 100644
--- a/drivers/fsi/fsi-core.c
+++ b/drivers/fsi/fsi-core.c
@@ -50,6 +50,7 @@ static const int engine_page_size = 0x400;
#define FSI_SMODE 0x0 /* R/W: Mode register */
#define FSI_SISC 0x8 /* R/W: Interrupt condition */
#define FSI_SSTAT 0x14 /* R : Slave status */
+#define FSI_SLBUS 0x30 /* W : LBUS Ownership */
#define FSI_LLMODE 0x100 /* R/W: Link layer mode register */
/*
@@ -67,6 +68,11 @@ static const int engine_page_size = 0x400;
#define FSI_SMODE_LBCRR_MASK 0xf /* Clk ratio mask */
/*
+ * SLBUS fields
+ */
+#define FSI_SLBUS_FORCE 0x80000000 /* Force LBUS ownership */
+
+/*
* LLMODE fields
*/
#define FSI_LLMODE_ASYNC 0x1
@@ -981,7 +987,7 @@ static int fsi_slave_init(struct fsi_master *master, int link, uint8_t id)
uint32_t cfam_id;
struct fsi_slave *slave;
uint8_t crc;
- __be32 data, llmode;
+ __be32 data, llmode, slbus;
int rc;
/* Currently, we only support single slaves on a link, and use the
@@ -1052,6 +1058,14 @@ static int fsi_slave_init(struct fsi_master *master, int link, uint8_t id)
}
+ slbus = cpu_to_be32(FSI_SLBUS_FORCE);
+ rc = fsi_master_write(master, link, id, FSI_SLAVE_BASE + FSI_SLBUS,
+ &slbus, sizeof(slbus));
+ if (rc)
+ dev_warn(&master->dev,
+ "can't set slbus on slave:%02x:%02x %d\n", link, id,
+ rc);
+
rc = fsi_slave_set_smode(slave);
if (rc) {
dev_warn(&master->dev,
@@ -1154,10 +1168,18 @@ static int fsi_master_write(struct fsi_master *master, int link,
return rc;
}
+static int fsi_master_link_disable(struct fsi_master *master, int link)
+{
+ if (master->link_enable)
+ return master->link_enable(master, link, false);
+
+ return 0;
+}
+
static int fsi_master_link_enable(struct fsi_master *master, int link)
{
if (master->link_enable)
- return master->link_enable(master, link);
+ return master->link_enable(master, link, true);
return 0;
}
@@ -1192,12 +1214,15 @@ static int fsi_master_scan(struct fsi_master *master)
}
rc = fsi_master_break(master, link);
if (rc) {
+ fsi_master_link_disable(master, link);
dev_dbg(&master->dev,
"break to link %d failed: %d\n", link, rc);
continue;
}
- fsi_slave_init(master, link, 0);
+ rc = fsi_slave_init(master, link, 0);
+ if (rc)
+ fsi_master_link_disable(master, link);
}
return 0;
diff --git a/drivers/fsi/fsi-master-aspeed.c b/drivers/fsi/fsi-master-aspeed.c
index f49742b310c2..c006ec008a1a 100644
--- a/drivers/fsi/fsi-master-aspeed.c
+++ b/drivers/fsi/fsi-master-aspeed.c
@@ -13,6 +13,7 @@
#include <linux/regmap.h>
#include <linux/slab.h>
#include <linux/iopoll.h>
+#include <linux/gpio/consumer.h>
#include "fsi-master.h"
@@ -21,6 +22,7 @@ struct fsi_master_aspeed {
struct device *dev;
void __iomem *base;
struct clk *clk;
+ struct gpio_desc *cfam_reset_gpio;
};
#define to_fsi_master_aspeed(m) \
@@ -82,7 +84,12 @@ static const u32 fsi_base = 0xa0000000;
#define FSI_LINK_ENABLE_SETUP_TIME 10 /* in mS */
-#define DEFAULT_DIVISOR 14
+/* Run the bus at maximum speed by default */
+#define FSI_DIVISOR_DEFAULT 1
+#define FSI_DIVISOR_CABLED 2
+static u16 aspeed_fsi_divisor = FSI_DIVISOR_DEFAULT;
+module_param_named(bus_div,aspeed_fsi_divisor, ushort, 0);
+
#define OPB_POLL_TIMEOUT 10000
static int __opb_write(struct fsi_master_aspeed *aspeed, u32 addr,
@@ -241,9 +248,10 @@ static int aspeed_master_read(struct fsi_master *master, int link,
struct fsi_master_aspeed *aspeed = to_fsi_master_aspeed(master);
int ret;
- if (id != 0)
+ if (id > 0x3)
return -EINVAL;
+ addr |= id << 21;
addr += link * FSI_HUB_LINK_SIZE;
switch (size) {
@@ -273,9 +281,10 @@ static int aspeed_master_write(struct fsi_master *master, int link,
struct fsi_master_aspeed *aspeed = to_fsi_master_aspeed(master);
int ret;
- if (id != 0)
+ if (id > 0x3)
return -EINVAL;
+ addr |= id << 21;
addr += link * FSI_HUB_LINK_SIZE;
switch (size) {
@@ -299,32 +308,28 @@ static int aspeed_master_write(struct fsi_master *master, int link,
return 0;
}
-static int aspeed_master_link_enable(struct fsi_master *master, int link)
+static int aspeed_master_link_enable(struct fsi_master *master, int link,
+ bool enable)
{
struct fsi_master_aspeed *aspeed = to_fsi_master_aspeed(master);
int idx, bit, ret;
- __be32 reg, result;
+ __be32 reg;
idx = link / 32;
bit = link % 32;
reg = cpu_to_be32(0x80000000 >> bit);
+ if (!enable)
+ return opb_writel(aspeed, ctrl_base + FSI_MCENP0 + (4 * idx),
+ reg);
+
ret = opb_writel(aspeed, ctrl_base + FSI_MSENP0 + (4 * idx), reg);
if (ret)
return ret;
mdelay(FSI_LINK_ENABLE_SETUP_TIME);
- ret = opb_readl(aspeed, ctrl_base + FSI_MENP0 + (4 * idx), &result);
- if (ret)
- return ret;
-
- if (result != reg) {
- dev_err(aspeed->dev, "%s failed: %08x\n", __func__, result);
- return -EIO;
- }
-
return 0;
}
@@ -386,9 +391,11 @@ static int aspeed_master_init(struct fsi_master_aspeed *aspeed)
opb_writel(aspeed, ctrl_base + FSI_MECTRL, reg);
reg = cpu_to_be32(FSI_MMODE_ECRC | FSI_MMODE_EPC | FSI_MMODE_RELA
- | fsi_mmode_crs0(DEFAULT_DIVISOR)
- | fsi_mmode_crs1(DEFAULT_DIVISOR)
+ | fsi_mmode_crs0(aspeed_fsi_divisor)
+ | fsi_mmode_crs1(aspeed_fsi_divisor)
| FSI_MMODE_P8_TO_LSB);
+ dev_info(aspeed->dev, "mmode set to %08x (divisor %d)\n",
+ be32_to_cpu(reg), aspeed_fsi_divisor);
opb_writel(aspeed, ctrl_base + FSI_MMODE, reg);
reg = cpu_to_be32(0xffff0000);
@@ -419,6 +426,90 @@ static int aspeed_master_init(struct fsi_master_aspeed *aspeed)
return 0;
}
+static ssize_t cfam_reset_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct fsi_master_aspeed *aspeed = dev_get_drvdata(dev);
+
+ gpiod_set_value(aspeed->cfam_reset_gpio, 1);
+ usleep_range(900, 1000);
+ gpiod_set_value(aspeed->cfam_reset_gpio, 0);
+
+ return count;
+}
+
+static DEVICE_ATTR(cfam_reset, 0200, NULL, cfam_reset_store);
+
+static int setup_cfam_reset(struct fsi_master_aspeed *aspeed)
+{
+ struct device *dev = aspeed->dev;
+ struct gpio_desc *gpio;
+ int rc;
+
+ gpio = devm_gpiod_get_optional(dev, "cfam-reset", GPIOD_OUT_LOW);
+ if (IS_ERR(gpio))
+ return PTR_ERR(gpio);
+ if (!gpio)
+ return 0;
+
+ aspeed->cfam_reset_gpio = gpio;
+
+ rc = device_create_file(dev, &dev_attr_cfam_reset);
+ if (rc) {
+ devm_gpiod_put(dev, gpio);
+ return rc;
+ }
+
+ return 0;
+}
+
+static int tacoma_cabled_fsi_fixup(struct device *dev)
+{
+ struct gpio_desc *routing_gpio, *mux_gpio;
+ int gpio;
+
+ /*
+ * The routing GPIO is a jumper indicating we should mux for the
+ * externally connected FSI cable.
+ */
+ routing_gpio = devm_gpiod_get_optional(dev, "fsi-routing",
+ GPIOD_IN | GPIOD_FLAGS_BIT_NONEXCLUSIVE);
+ if (IS_ERR(routing_gpio))
+ return PTR_ERR(routing_gpio);
+ if (!routing_gpio)
+ return 0;
+
+ mux_gpio = devm_gpiod_get_optional(dev, "fsi-mux", GPIOD_ASIS);
+ if (IS_ERR(mux_gpio))
+ return PTR_ERR(mux_gpio);
+ if (!mux_gpio)
+ return 0;
+
+ gpio = gpiod_get_value(routing_gpio);
+ if (gpio < 0)
+ return gpio;
+
+ /* If the routing GPIO is high we should set the mux to low. */
+ if (gpio) {
+ /*
+ * Cable signal integrity means we should run the bus
+ * slightly slower. Do not override if a kernel param
+ * has already overridden.
+ */
+ if (aspeed_fsi_divisor == FSI_DIVISOR_DEFAULT)
+ aspeed_fsi_divisor = FSI_DIVISOR_CABLED;
+
+ gpiod_direction_output(mux_gpio, 0);
+ dev_info(dev, "FSI configured for external cable\n");
+ } else {
+ gpiod_direction_output(mux_gpio, 1);
+ }
+
+ devm_gpiod_put(dev, routing_gpio);
+
+ return 0;
+}
+
static int fsi_master_aspeed_probe(struct platform_device *pdev)
{
struct fsi_master_aspeed *aspeed;
@@ -426,6 +517,12 @@ static int fsi_master_aspeed_probe(struct platform_device *pdev)
int rc, links, reg;
__be32 raw;
+ rc = tacoma_cabled_fsi_fixup(&pdev->dev);
+ if (rc) {
+ dev_err(&pdev->dev, "Tacoma FSI cable fixup failed\n");
+ return rc;
+ }
+
aspeed = devm_kzalloc(&pdev->dev, sizeof(*aspeed), GFP_KERNEL);
if (!aspeed)
return -ENOMEM;
@@ -448,6 +545,11 @@ static int fsi_master_aspeed_probe(struct platform_device *pdev)
return rc;
}
+ rc = setup_cfam_reset(aspeed);
+ if (rc) {
+ dev_err(&pdev->dev, "CFAM reset GPIO setup failed\n");
+ }
+
writel(0x1, aspeed->base + OPB_CLK_SYNC);
writel(OPB1_XFER_ACK_EN | OPB0_XFER_ACK_EN,
aspeed->base + OPB_IRQ_MASK);
diff --git a/drivers/fsi/fsi-master-ast-cf.c b/drivers/fsi/fsi-master-ast-cf.c
index 04d10ea8d343..57a779a89b07 100644
--- a/drivers/fsi/fsi-master-ast-cf.c
+++ b/drivers/fsi/fsi-master-ast-cf.c
@@ -838,7 +838,7 @@ static int load_copro_firmware(struct fsi_master_acf *master)
rc = request_firmware(&fw, FW_FILE_NAME, master->dev);
if (rc) {
dev_err(
- master->dev, "Error %d to load firwmare '%s' !\n",
+ master->dev, "Error %d to load firmware '%s' !\n",
rc, FW_FILE_NAME);
return rc;
}
@@ -1039,7 +1039,8 @@ static void fsi_master_acf_setup_external(struct fsi_master_acf *master)
gpiod_direction_input(master->gpio_data);
}
-static int fsi_master_acf_link_enable(struct fsi_master *_master, int link)
+static int fsi_master_acf_link_enable(struct fsi_master *_master, int link,
+ bool enable)
{
struct fsi_master_acf *master = to_fsi_master_acf(_master);
int rc = -EBUSY;
@@ -1049,7 +1050,7 @@ static int fsi_master_acf_link_enable(struct fsi_master *_master, int link)
mutex_lock(&master->lock);
if (!master->external_mode) {
- gpiod_set_value(master->gpio_enable, 1);
+ gpiod_set_value(master->gpio_enable, enable ? 1 : 0);
rc = 0;
}
mutex_unlock(&master->lock);
diff --git a/drivers/fsi/fsi-master-gpio.c b/drivers/fsi/fsi-master-gpio.c
index 4dcce17f243f..aa97c4a250cb 100644
--- a/drivers/fsi/fsi-master-gpio.c
+++ b/drivers/fsi/fsi-master-gpio.c
@@ -678,7 +678,8 @@ static void fsi_master_gpio_init_external(struct fsi_master_gpio *master)
gpiod_direction_input(master->gpio_data);
}
-static int fsi_master_gpio_link_enable(struct fsi_master *_master, int link)
+static int fsi_master_gpio_link_enable(struct fsi_master *_master, int link,
+ bool enable)
{
struct fsi_master_gpio *master = to_fsi_master_gpio(_master);
int rc = -EBUSY;
@@ -688,7 +689,7 @@ static int fsi_master_gpio_link_enable(struct fsi_master *_master, int link)
mutex_lock(&master->cmd_lock);
if (!master->external_mode) {
- gpiod_set_value(master->gpio_enable, 1);
+ gpiod_set_value(master->gpio_enable, enable ? 1 : 0);
rc = 0;
}
mutex_unlock(&master->cmd_lock);
diff --git a/drivers/fsi/fsi-master-hub.c b/drivers/fsi/fsi-master-hub.c
index def35cf92571..01f0a796111e 100644
--- a/drivers/fsi/fsi-master-hub.c
+++ b/drivers/fsi/fsi-master-hub.c
@@ -77,7 +77,8 @@ static int hub_master_break(struct fsi_master *master, int link)
return hub_master_write(master, link, 0, addr, &cmd, sizeof(cmd));
}
-static int hub_master_link_enable(struct fsi_master *master, int link)
+static int hub_master_link_enable(struct fsi_master *master, int link,
+ bool enable)
{
struct fsi_master_hub *hub = to_fsi_master_hub(master);
int idx, bit;
@@ -89,13 +90,17 @@ static int hub_master_link_enable(struct fsi_master *master, int link)
reg = cpu_to_be32(0x80000000 >> bit);
+ if (!enable)
+ return fsi_device_write(hub->upstream, FSI_MCENP0 + (4 * idx),
+ &reg, 4);
+
rc = fsi_device_write(hub->upstream, FSI_MSENP0 + (4 * idx), &reg, 4);
+ if (rc)
+ return rc;
mdelay(FSI_LINK_ENABLE_SETUP_TIME);
- fsi_device_read(hub->upstream, FSI_MENP0 + (4 * idx), &reg, 4);
-
- return rc;
+ return 0;
}
static void hub_master_release(struct device *dev)
@@ -271,7 +276,7 @@ static int hub_master_remove(struct device *dev)
return 0;
}
-static struct fsi_device_id hub_master_ids[] = {
+static const struct fsi_device_id hub_master_ids[] = {
{
.engine_type = FSI_ENGID_HUB_MASTER,
.version = FSI_VERSION_ANY,
diff --git a/drivers/fsi/fsi-master.h b/drivers/fsi/fsi-master.h
index 6e8d4d4d5149..cd6bee5e12a7 100644
--- a/drivers/fsi/fsi-master.h
+++ b/drivers/fsi/fsi-master.h
@@ -130,7 +130,8 @@ struct fsi_master {
uint32_t addr, const void *val, size_t size);
int (*term)(struct fsi_master *, int link, uint8_t id);
int (*send_break)(struct fsi_master *, int link);
- int (*link_enable)(struct fsi_master *, int link);
+ int (*link_enable)(struct fsi_master *, int link,
+ bool enable);
int (*link_config)(struct fsi_master *, int link,
u8 t_send_delay, u8 t_echo_delay);
};
diff --git a/drivers/fsi/fsi-occ.c b/drivers/fsi/fsi-occ.c
index 7da9c81759ac..d92462222b68 100644
--- a/drivers/fsi/fsi-occ.c
+++ b/drivers/fsi/fsi-occ.c
@@ -14,6 +14,7 @@
#include <linux/mutex.h>
#include <linux/fsi-occ.h>
#include <linux/of.h>
+#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/sched.h>
#include <linux/slab.h>
@@ -24,8 +25,13 @@
#define OCC_CMD_DATA_BYTES 4090
#define OCC_RESP_DATA_BYTES 4089
-#define OCC_SRAM_CMD_ADDR 0xFFFBE000
-#define OCC_SRAM_RSP_ADDR 0xFFFBF000
+#define OCC_P9_SRAM_CMD_ADDR 0xFFFBE000
+#define OCC_P9_SRAM_RSP_ADDR 0xFFFBF000
+
+#define OCC_P10_SRAM_CMD_ADDR 0xFFFFD000
+#define OCC_P10_SRAM_RSP_ADDR 0xFFFFE000
+
+#define OCC_P10_SRAM_MODE 0x58 /* Normal mode, OCB channel 2 */
/*
* Assume we don't have much FFDC, if we do we'll overflow and
@@ -37,11 +43,14 @@
#define OCC_TIMEOUT_MS 1000
#define OCC_CMD_IN_PRG_WAIT_MS 50
+enum versions { occ_p9, occ_p10 };
+
struct occ {
struct device *dev;
struct device *sbefifo;
char name[32];
int idx;
+ enum versions version;
struct miscdevice mdev;
struct mutex occ_lock;
};
@@ -235,29 +244,43 @@ static int occ_verify_checksum(struct occ_response *resp, u16 data_length)
return 0;
}
-static int occ_getsram(struct occ *occ, u32 address, void *data, ssize_t len)
+static int occ_getsram(struct occ *occ, u32 offset, void *data, ssize_t len)
{
u32 data_len = ((len + 7) / 8) * 8; /* must be multiples of 8 B */
- size_t resp_len, resp_data_len;
- __be32 *resp, cmd[5];
- int rc;
+ size_t cmd_len, resp_len, resp_data_len;
+ __be32 *resp, cmd[6];
+ int idx = 0, rc;
/*
* Magic sequence to do SBE getsram command. SBE will fetch data from
* specified SRAM address.
*/
- cmd[0] = cpu_to_be32(0x5);
+ switch (occ->version) {
+ default:
+ case occ_p9:
+ cmd_len = 5;
+ cmd[2] = cpu_to_be32(1); /* Normal mode */
+ cmd[3] = cpu_to_be32(OCC_P9_SRAM_RSP_ADDR + offset);
+ break;
+ case occ_p10:
+ idx = 1;
+ cmd_len = 6;
+ cmd[2] = cpu_to_be32(OCC_P10_SRAM_MODE);
+ cmd[3] = 0;
+ cmd[4] = cpu_to_be32(OCC_P10_SRAM_RSP_ADDR + offset);
+ break;
+ }
+
+ cmd[0] = cpu_to_be32(cmd_len);
cmd[1] = cpu_to_be32(SBEFIFO_CMD_GET_OCC_SRAM);
- cmd[2] = cpu_to_be32(1);
- cmd[3] = cpu_to_be32(address);
- cmd[4] = cpu_to_be32(data_len);
+ cmd[4 + idx] = cpu_to_be32(data_len);
resp_len = (data_len >> 2) + OCC_SBE_STATUS_WORDS;
resp = kzalloc(resp_len << 2, GFP_KERNEL);
if (!resp)
return -ENOMEM;
- rc = sbefifo_submit(occ->sbefifo, cmd, 5, resp, &resp_len);
+ rc = sbefifo_submit(occ->sbefifo, cmd, cmd_len, resp, &resp_len);
if (rc)
goto free;
@@ -287,20 +310,21 @@ free:
return rc;
}
-static int occ_putsram(struct occ *occ, u32 address, const void *data,
- ssize_t len)
+static int occ_putsram(struct occ *occ, const void *data, ssize_t len)
{
size_t cmd_len, buf_len, resp_len, resp_data_len;
u32 data_len = ((len + 7) / 8) * 8; /* must be multiples of 8 B */
__be32 *buf;
- int rc;
+ int idx = 0, rc;
+
+ cmd_len = (occ->version == occ_p10) ? 6 : 5;
/*
* We use the same buffer for command and response, make
* sure it's big enough
*/
resp_len = OCC_SBE_STATUS_WORDS;
- cmd_len = (data_len >> 2) + 5;
+ cmd_len += data_len >> 2;
buf_len = max(cmd_len, resp_len);
buf = kzalloc(buf_len << 2, GFP_KERNEL);
if (!buf)
@@ -312,11 +336,23 @@ static int occ_putsram(struct occ *occ, u32 address, const void *data,
*/
buf[0] = cpu_to_be32(cmd_len);
buf[1] = cpu_to_be32(SBEFIFO_CMD_PUT_OCC_SRAM);
- buf[2] = cpu_to_be32(1);
- buf[3] = cpu_to_be32(address);
- buf[4] = cpu_to_be32(data_len);
- memcpy(&buf[5], data, len);
+ switch (occ->version) {
+ default:
+ case occ_p9:
+ buf[2] = cpu_to_be32(1); /* Normal mode */
+ buf[3] = cpu_to_be32(OCC_P9_SRAM_CMD_ADDR);
+ break;
+ case occ_p10:
+ idx = 1;
+ buf[2] = cpu_to_be32(OCC_P10_SRAM_MODE);
+ buf[3] = 0;
+ buf[4] = cpu_to_be32(OCC_P10_SRAM_CMD_ADDR);
+ break;
+ }
+
+ buf[4 + idx] = cpu_to_be32(data_len);
+ memcpy(&buf[5 + idx], data, len);
rc = sbefifo_submit(occ->sbefifo, buf, cmd_len, buf, &resp_len);
if (rc)
@@ -356,21 +392,35 @@ free:
static int occ_trigger_attn(struct occ *occ)
{
__be32 buf[OCC_SBE_STATUS_WORDS];
- size_t resp_len, resp_data_len;
- int rc;
+ size_t cmd_len, resp_len, resp_data_len;
+ int idx = 0, rc;
- BUILD_BUG_ON(OCC_SBE_STATUS_WORDS < 7);
+ BUILD_BUG_ON(OCC_SBE_STATUS_WORDS < 8);
resp_len = OCC_SBE_STATUS_WORDS;
- buf[0] = cpu_to_be32(0x5 + 0x2); /* Chip-op length in words */
+ switch (occ->version) {
+ default:
+ case occ_p9:
+ cmd_len = 7;
+ buf[2] = cpu_to_be32(3); /* Circular mode */
+ buf[3] = 0;
+ break;
+ case occ_p10:
+ idx = 1;
+ cmd_len = 8;
+ buf[2] = cpu_to_be32(0xd0); /* Circular mode, OCB Channel 1 */
+ buf[3] = 0;
+ buf[4] = 0;
+ break;
+ }
+
+ buf[0] = cpu_to_be32(cmd_len); /* Chip-op length in words */
buf[1] = cpu_to_be32(SBEFIFO_CMD_PUT_OCC_SRAM);
- buf[2] = cpu_to_be32(0x3); /* Mode: Circular */
- buf[3] = cpu_to_be32(0x0); /* Address: ignore in mode 3 */
- buf[4] = cpu_to_be32(0x8); /* Data length in bytes */
- buf[5] = cpu_to_be32(0x20010000); /* Trigger OCC attention */
- buf[6] = 0;
+ buf[4 + idx] = cpu_to_be32(8); /* Data length in bytes */
+ buf[5 + idx] = cpu_to_be32(0x20010000); /* Trigger OCC attention */
+ buf[6 + idx] = 0;
- rc = sbefifo_submit(occ->sbefifo, buf, 7, buf, &resp_len);
+ rc = sbefifo_submit(occ->sbefifo, buf, cmd_len, buf, &resp_len);
if (rc)
goto error;
@@ -429,7 +479,7 @@ int fsi_occ_submit(struct device *dev, const void *request, size_t req_len,
/* Extract the seq_no from the command (first byte) */
seq_no = *(const u8 *)request;
- rc = occ_putsram(occ, OCC_SRAM_CMD_ADDR, request, req_len);
+ rc = occ_putsram(occ, request, req_len);
if (rc)
goto done;
@@ -440,7 +490,7 @@ int fsi_occ_submit(struct device *dev, const void *request, size_t req_len,
/* Read occ response header */
start = jiffies;
do {
- rc = occ_getsram(occ, OCC_SRAM_RSP_ADDR, resp, 8);
+ rc = occ_getsram(occ, 0, resp, 8);
if (rc)
goto done;
@@ -476,8 +526,7 @@ int fsi_occ_submit(struct device *dev, const void *request, size_t req_len,
/* Grab the rest */
if (resp_data_length > 1) {
/* already got 3 bytes resp, also need 2 bytes checksum */
- rc = occ_getsram(occ, OCC_SRAM_RSP_ADDR + 8,
- &resp->data[3], resp_data_length - 1);
+ rc = occ_getsram(occ, 8, &resp->data[3], resp_data_length - 1);
if (rc)
goto done;
}
@@ -508,6 +557,7 @@ static int occ_probe(struct platform_device *pdev)
struct occ *occ;
struct platform_device *hwmon_dev;
struct device *dev = &pdev->dev;
+ const void *md = of_device_get_match_data(dev);
struct platform_device_info hwmon_dev_info = {
.parent = dev,
.name = "occ-hwmon",
@@ -517,6 +567,7 @@ static int occ_probe(struct platform_device *pdev)
if (!occ)
return -ENOMEM;
+ occ->version = (enum versions)md;
occ->dev = dev;
occ->sbefifo = dev->parent;
mutex_init(&occ->occ_lock);
@@ -555,7 +606,7 @@ static int occ_probe(struct platform_device *pdev)
hwmon_dev_info.id = occ->idx;
hwmon_dev = platform_device_register_full(&hwmon_dev_info);
- if (!hwmon_dev)
+ if (IS_ERR(hwmon_dev))
dev_warn(dev, "failed to create hwmon device\n");
return 0;
@@ -575,7 +626,14 @@ static int occ_remove(struct platform_device *pdev)
}
static const struct of_device_id occ_match[] = {
- { .compatible = "ibm,p9-occ" },
+ {
+ .compatible = "ibm,p9-occ",
+ .data = (void *)occ_p9
+ },
+ {
+ .compatible = "ibm,p10-occ",
+ .data = (void *)occ_p10
+ },
{ },
};
diff --git a/drivers/fsi/fsi-sbefifo.c b/drivers/fsi/fsi-sbefifo.c
index f54df9ebc8b3..84cb965bfed5 100644
--- a/drivers/fsi/fsi-sbefifo.c
+++ b/drivers/fsi/fsi-sbefifo.c
@@ -325,7 +325,8 @@ static int sbefifo_up_write(struct sbefifo *sbefifo, __be32 word)
static int sbefifo_request_reset(struct sbefifo *sbefifo)
{
struct device *dev = &sbefifo->fsi_dev->dev;
- u32 status, timeout;
+ unsigned long end_time;
+ u32 status;
int rc;
dev_dbg(dev, "Requesting FIFO reset\n");
@@ -341,7 +342,8 @@ static int sbefifo_request_reset(struct sbefifo *sbefifo)
}
/* Wait for it to complete */
- for (timeout = 0; timeout < SBEFIFO_RESET_TIMEOUT; timeout++) {
+ end_time = jiffies + msecs_to_jiffies(SBEFIFO_RESET_TIMEOUT);
+ while (!time_after(jiffies, end_time)) {
rc = sbefifo_regr(sbefifo, SBEFIFO_UP | SBEFIFO_STS, &status);
if (rc) {
dev_err(dev, "Failed to read UP fifo status during reset"
@@ -355,7 +357,7 @@ static int sbefifo_request_reset(struct sbefifo *sbefifo)
return 0;
}
- msleep(1);
+ cond_resched();
}
dev_err(dev, "FIFO reset timed out\n");
@@ -400,7 +402,7 @@ static int sbefifo_cleanup_hw(struct sbefifo *sbefifo)
/* The FIFO already contains a reset request from the SBE ? */
if (down_status & SBEFIFO_STS_RESET_REQ) {
dev_info(dev, "Cleanup: FIFO reset request set, resetting\n");
- rc = sbefifo_regw(sbefifo, SBEFIFO_UP, SBEFIFO_PERFORM_RESET);
+ rc = sbefifo_regw(sbefifo, SBEFIFO_DOWN, SBEFIFO_PERFORM_RESET);
if (rc) {
sbefifo->broken = true;
dev_err(dev, "Cleanup: Reset reg write failed, rc=%d\n", rc);
@@ -1028,7 +1030,7 @@ static int sbefifo_remove(struct device *dev)
return 0;
}
-static struct fsi_device_id sbefifo_ids[] = {
+static const struct fsi_device_id sbefifo_ids[] = {
{
.engine_type = FSI_ENGID_SBE,
.version = FSI_VERSION_ANY,
diff --git a/drivers/fsi/fsi-scom.c b/drivers/fsi/fsi-scom.c
index 004dc03ccf09..b45bfab7b7f5 100644
--- a/drivers/fsi/fsi-scom.c
+++ b/drivers/fsi/fsi-scom.c
@@ -627,7 +627,7 @@ static int scom_remove(struct device *dev)
return 0;
}
-static struct fsi_device_id scom_ids[] = {
+static const struct fsi_device_id scom_ids[] = {
{
.engine_type = FSI_ENGID_SCOM,
.version = FSI_VERSION_ANY,
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 288ae9f63588..9aa89d7d4193 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -1435,6 +1435,34 @@ config SENSORS_PCF8591
These devices are hard to detect and rarely found on mainstream
hardware. If unsure, say N.
+config SENSORS_PECI_CPUTEMP
+ tristate "PECI CPU temperature monitoring client"
+ depends on PECI
+ select MFD_INTEL_PECI_CLIENT
+ help
+ If you say yes here you get support for the generic Intel PECI
+ cputemp driver which provides Digital Thermal Sensor (DTS) thermal
+ readings of the CPU package and CPU cores that are accessible using
+ the PECI Client Command Suite via the processor PECI client.
+ Check <file:Documentation/hwmon/peci-cputemp.rst> for details.
+
+ This driver can also be built as a module. If so, the module
+ will be called peci-cputemp.
+
+config SENSORS_PECI_DIMMTEMP
+ tristate "PECI DIMM temperature monitoring client"
+ depends on PECI
+ select MFD_INTEL_PECI_CLIENT
+ help
+ If you say yes here you get support for the generic Intel PECI hwmon
+ driver which provides Digital Thermal Sensor (DTS) thermal readings of
+ DIMM components that are accessible using the PECI Client Command
+ Suite via the processor PECI client.
+ Check <file:Documentation/hwmon/peci-dimmtemp.rst> for details.
+
+ This driver can also be built as a module. If so, the module
+ will be called peci-dimmtemp.
+
source "drivers/hwmon/pmbus/Kconfig"
config SENSORS_PWM_FAN
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index 3e32c21f5efe..ae41ee71a71b 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -151,6 +151,8 @@ obj-$(CONFIG_SENSORS_NTC_THERMISTOR) += ntc_thermistor.o
obj-$(CONFIG_SENSORS_PC87360) += pc87360.o
obj-$(CONFIG_SENSORS_PC87427) += pc87427.o
obj-$(CONFIG_SENSORS_PCF8591) += pcf8591.o
+obj-$(CONFIG_SENSORS_PECI_CPUTEMP) += peci-cputemp.o
+obj-$(CONFIG_SENSORS_PECI_DIMMTEMP) += peci-dimmtemp.o
obj-$(CONFIG_SENSORS_POWR1220) += powr1220.o
obj-$(CONFIG_SENSORS_PWM_FAN) += pwm-fan.o
obj-$(CONFIG_SENSORS_RASPBERRYPI_HWMON) += raspberrypi-hwmon.o
diff --git a/drivers/hwmon/occ/common.c b/drivers/hwmon/occ/common.c
index 30e18eb60da7..3e580a83ae61 100644
--- a/drivers/hwmon/occ/common.c
+++ b/drivers/hwmon/occ/common.c
@@ -41,6 +41,14 @@ struct temp_sensor_2 {
u8 value;
} __packed;
+struct temp_sensor_10 {
+ u32 sensor_id;
+ u8 fru_type;
+ u8 value;
+ u8 throttle;
+ u8 reserved;
+} __packed;
+
struct freq_sensor_1 {
u16 sensor_id;
u16 value;
@@ -307,6 +315,60 @@ static ssize_t occ_show_temp_2(struct device *dev,
return snprintf(buf, PAGE_SIZE - 1, "%u\n", val);
}
+static ssize_t occ_show_temp_10(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int rc;
+ u32 val = 0;
+ struct temp_sensor_10 *temp;
+ struct occ *occ = dev_get_drvdata(dev);
+ struct occ_sensors *sensors = &occ->sensors;
+ struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
+
+ rc = occ_update_response(occ);
+ if (rc)
+ return rc;
+
+ temp = ((struct temp_sensor_10 *)sensors->temp.data) + sattr->index;
+
+ switch (sattr->nr) {
+ case 0:
+ val = get_unaligned_be32(&temp->sensor_id);
+ break;
+ case 1:
+ val = temp->value;
+ if (val == OCC_TEMP_SENSOR_FAULT)
+ return -EREMOTEIO;
+
+ /*
+ * VRM doesn't return temperature, only alarm bit. This
+ * attribute maps to tempX_alarm instead of tempX_input for
+ * VRM
+ */
+ if (temp->fru_type != OCC_FRU_TYPE_VRM) {
+ /* sensor not ready */
+ if (val == 0)
+ return -EAGAIN;
+
+ val *= 1000;
+ }
+ break;
+ case 2:
+ val = temp->fru_type;
+ break;
+ case 3:
+ val = temp->value == OCC_TEMP_SENSOR_FAULT;
+ break;
+ case 4:
+ val = temp->throttle * 1000;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return snprintf(buf, PAGE_SIZE - 1, "%u\n", val);
+}
+
static ssize_t occ_show_freq_1(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -745,6 +807,9 @@ static int occ_setup_sensor_attrs(struct occ *occ)
num_attrs += (sensors->temp.num_sensors * 4);
show_temp = occ_show_temp_2;
break;
+ case 0x10:
+ show_temp = occ_show_temp_10;
+ break;
default:
sensors->temp.num_sensors = 0;
}
diff --git a/drivers/hwmon/peci-cputemp.c b/drivers/hwmon/peci-cputemp.c
new file mode 100644
index 000000000000..b9fe91281d58
--- /dev/null
+++ b/drivers/hwmon/peci-cputemp.c
@@ -0,0 +1,472 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2018-2019 Intel Corporation
+
+#include <linux/hwmon.h>
+#include <linux/jiffies.h>
+#include <linux/mfd/intel-peci-client.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include "peci-hwmon.h"
+
+#define DEFAULT_CHANNEL_NUMS 5
+#define CORETEMP_CHANNEL_NUMS CORE_NUMS_MAX
+#define CPUTEMP_CHANNEL_NUMS (DEFAULT_CHANNEL_NUMS + CORETEMP_CHANNEL_NUMS)
+
+struct temp_group {
+ struct peci_sensor_data die;
+ struct peci_sensor_data dts;
+ struct peci_sensor_data tcontrol;
+ struct peci_sensor_data tthrottle;
+ struct peci_sensor_data tjmax;
+ struct peci_sensor_data core[CORETEMP_CHANNEL_NUMS];
+};
+
+struct peci_cputemp {
+ struct peci_client_manager *mgr;
+ struct device *dev;
+ char name[PECI_NAME_SIZE];
+ const struct cpu_gen_info *gen_info;
+ struct temp_group temp;
+ u64 core_mask;
+ u32 temp_config[CPUTEMP_CHANNEL_NUMS + 1];
+ uint config_idx;
+ struct hwmon_channel_info temp_info;
+ const struct hwmon_channel_info *info[2];
+ struct hwmon_chip_info chip;
+ char **coretemp_label;
+};
+
+enum cputemp_channels {
+ channel_die,
+ channel_dts,
+ channel_tcontrol,
+ channel_tthrottle,
+ channel_tjmax,
+ channel_core,
+};
+
+static const u32 config_table[DEFAULT_CHANNEL_NUMS + 1] = {
+ /* Die temperature */
+ HWMON_T_LABEL | HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT |
+ HWMON_T_CRIT_HYST,
+
+ /* DTS margin */
+ HWMON_T_LABEL | HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT |
+ HWMON_T_CRIT_HYST,
+
+ /* Tcontrol temperature */
+ HWMON_T_LABEL | HWMON_T_INPUT | HWMON_T_CRIT,
+
+ /* Tthrottle temperature */
+ HWMON_T_LABEL | HWMON_T_INPUT,
+
+ /* Tjmax temperature */
+ HWMON_T_LABEL | HWMON_T_INPUT,
+
+ /* Core temperature - for all core channels */
+ HWMON_T_LABEL | HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT |
+ HWMON_T_CRIT_HYST,
+};
+
+static const char *cputemp_label[DEFAULT_CHANNEL_NUMS] = {
+ "Die",
+ "DTS",
+ "Tcontrol",
+ "Tthrottle",
+ "Tjmax"
+};
+
+static s32 ten_dot_six_to_millidegree(s32 val)
+{
+ return ((val ^ 0x8000) - 0x8000) * 1000 / 64;
+}
+
+static int get_temp_targets(struct peci_cputemp *priv)
+{
+ s32 tthrottle_offset;
+ s32 tcontrol_margin;
+ u8 pkg_cfg[4];
+ int ret;
+
+ /*
+ * Just use only the tcontrol marker to determine if target values need
+ * update.
+ */
+ if (!peci_sensor_need_update(&priv->temp.tcontrol))
+ return 0;
+
+ ret = peci_client_read_package_config(priv->mgr,
+ PECI_MBX_INDEX_TEMP_TARGET, 0,
+ pkg_cfg);
+ if (ret)
+ return ret;
+
+ priv->temp.tjmax.value = pkg_cfg[2] * 1000;
+
+ tcontrol_margin = pkg_cfg[1];
+ tcontrol_margin = ((tcontrol_margin ^ 0x80) - 0x80) * 1000;
+ priv->temp.tcontrol.value = priv->temp.tjmax.value - tcontrol_margin;
+
+ tthrottle_offset = (pkg_cfg[3] & 0x2f) * 1000;
+ priv->temp.tthrottle.value = priv->temp.tjmax.value - tthrottle_offset;
+
+ peci_sensor_mark_updated(&priv->temp.tcontrol);
+
+ return 0;
+}
+
+static int get_die_temp(struct peci_cputemp *priv)
+{
+ struct peci_get_temp_msg msg;
+ int ret;
+
+ if (!peci_sensor_need_update(&priv->temp.die))
+ return 0;
+
+ msg.addr = priv->mgr->client->addr;
+
+ ret = peci_command(priv->mgr->client->adapter, PECI_CMD_GET_TEMP, &msg);
+ if (ret)
+ return ret;
+
+ /* Note that the tjmax should be available before calling it */
+ priv->temp.die.value = priv->temp.tjmax.value +
+ (msg.temp_raw * 1000 / 64);
+
+ peci_sensor_mark_updated(&priv->temp.die);
+
+ return 0;
+}
+
+static int get_dts(struct peci_cputemp *priv)
+{
+ s32 dts_margin;
+ u8 pkg_cfg[4];
+ int ret;
+
+ if (!peci_sensor_need_update(&priv->temp.dts))
+ return 0;
+
+ ret = peci_client_read_package_config(priv->mgr,
+ PECI_MBX_INDEX_DTS_MARGIN, 0,
+ pkg_cfg);
+
+ if (ret)
+ return ret;
+
+ dts_margin = le16_to_cpup((__le16 *)pkg_cfg);
+
+ /**
+ * Processors return a value of DTS reading in 10.6 format
+ * (10 bits signed decimal, 6 bits fractional).
+ * Error codes:
+ * 0x8000: General sensor error
+ * 0x8001: Reserved
+ * 0x8002: Underflow on reading value
+ * 0x8003-0x81ff: Reserved
+ */
+ if (dts_margin >= 0x8000 && dts_margin <= 0x81ff)
+ return -EIO;
+
+ dts_margin = ten_dot_six_to_millidegree(dts_margin);
+
+ /* Note that the tcontrol should be available before calling it */
+ priv->temp.dts.value = priv->temp.tcontrol.value - dts_margin;
+
+ peci_sensor_mark_updated(&priv->temp.dts);
+
+ return 0;
+}
+
+static int get_core_temp(struct peci_cputemp *priv, int core_index)
+{
+ s32 core_dts_margin;
+ u8 pkg_cfg[4];
+ int ret;
+
+ if (!peci_sensor_need_update(&priv->temp.core[core_index]))
+ return 0;
+
+ ret = peci_client_read_package_config(priv->mgr,
+ PECI_MBX_INDEX_PER_CORE_DTS_TEMP,
+ core_index, pkg_cfg);
+ if (ret)
+ return ret;
+
+ core_dts_margin = le16_to_cpup((__le16 *)pkg_cfg);
+
+ /*
+ * Processors return a value of the core DTS reading in 10.6 format
+ * (10 bits signed decimal, 6 bits fractional).
+ * Error codes:
+ * 0x8000: General sensor error
+ * 0x8001: Reserved
+ * 0x8002: Underflow on reading value
+ * 0x8003-0x81ff: Reserved
+ */
+ if (core_dts_margin >= 0x8000 && core_dts_margin <= 0x81ff)
+ return -EIO;
+
+ core_dts_margin = ten_dot_six_to_millidegree(core_dts_margin);
+
+ /* Note that the tjmax should be available before calling it */
+ priv->temp.core[core_index].value = priv->temp.tjmax.value +
+ core_dts_margin;
+
+ peci_sensor_mark_updated(&priv->temp.core[core_index]);
+
+ return 0;
+}
+
+static int cputemp_read_string(struct device *dev,
+ enum hwmon_sensor_types type,
+ u32 attr, int channel, const char **str)
+{
+ struct peci_cputemp *priv = dev_get_drvdata(dev);
+
+ if (attr != hwmon_temp_label)
+ return -EOPNOTSUPP;
+
+ *str = (channel < DEFAULT_CHANNEL_NUMS) ?
+ cputemp_label[channel] :
+ (const char *)priv->coretemp_label[channel -
+ DEFAULT_CHANNEL_NUMS];
+
+ return 0;
+}
+
+static int cputemp_read(struct device *dev,
+ enum hwmon_sensor_types type,
+ u32 attr, int channel, long *val)
+{
+ struct peci_cputemp *priv = dev_get_drvdata(dev);
+ int ret, core_index;
+
+ if (channel >= CPUTEMP_CHANNEL_NUMS ||
+ !(priv->temp_config[channel] & BIT(attr)))
+ return -EOPNOTSUPP;
+
+ ret = get_temp_targets(priv);
+ if (ret)
+ return ret;
+
+ switch (attr) {
+ case hwmon_temp_input:
+ switch (channel) {
+ case channel_die:
+ ret = get_die_temp(priv);
+ if (ret)
+ break;
+
+ *val = priv->temp.die.value;
+ break;
+ case channel_dts:
+ ret = get_dts(priv);
+ if (ret)
+ break;
+
+ *val = priv->temp.dts.value;
+ break;
+ case channel_tcontrol:
+ *val = priv->temp.tcontrol.value;
+ break;
+ case channel_tthrottle:
+ *val = priv->temp.tthrottle.value;
+ break;
+ case channel_tjmax:
+ *val = priv->temp.tjmax.value;
+ break;
+ default:
+ core_index = channel - DEFAULT_CHANNEL_NUMS;
+ ret = get_core_temp(priv, core_index);
+ if (ret)
+ break;
+
+ *val = priv->temp.core[core_index].value;
+ break;
+ }
+ break;
+ case hwmon_temp_max:
+ *val = priv->temp.tcontrol.value;
+ break;
+ case hwmon_temp_crit:
+ *val = priv->temp.tjmax.value;
+ break;
+ case hwmon_temp_crit_hyst:
+ *val = priv->temp.tjmax.value - priv->temp.tcontrol.value;
+ break;
+ default:
+ ret = -EOPNOTSUPP;
+ break;
+ }
+
+ return ret;
+}
+
+static umode_t cputemp_is_visible(const void *data,
+ enum hwmon_sensor_types type,
+ u32 attr, int channel)
+{
+ const struct peci_cputemp *priv = data;
+
+ if (channel < ARRAY_SIZE(priv->temp_config) &&
+ (priv->temp_config[channel] & BIT(attr)) &&
+ (channel < DEFAULT_CHANNEL_NUMS ||
+ (channel >= DEFAULT_CHANNEL_NUMS &&
+ (priv->core_mask & BIT(channel - DEFAULT_CHANNEL_NUMS)))))
+ return 0444;
+
+ return 0;
+}
+
+static const struct hwmon_ops cputemp_ops = {
+ .is_visible = cputemp_is_visible,
+ .read_string = cputemp_read_string,
+ .read = cputemp_read,
+};
+
+static int check_resolved_cores(struct peci_cputemp *priv)
+{
+ struct peci_rd_pci_cfg_local_msg msg;
+ int ret;
+
+ /* Get the RESOLVED_CORES register value */
+ msg.addr = priv->mgr->client->addr;
+ msg.device = 30;
+ msg.function = 3;
+ msg.rx_len = 4;
+ msg.bus = 1;
+ msg.reg = 0xb4;
+
+ ret = peci_command(priv->mgr->client->adapter,
+ PECI_CMD_RD_PCI_CFG_LOCAL, &msg);
+ if (msg.cc != PECI_DEV_CC_SUCCESS)
+ ret = -EAGAIN;
+ if (ret)
+ return ret;
+
+ priv->core_mask = le32_to_cpup((__le32 *)msg.pci_config);
+ if (!priv->core_mask)
+ return -EAGAIN;
+
+ dev_dbg(priv->dev, "Scanned resolved cores: 0x%llx\n", priv->core_mask);
+
+ return 0;
+}
+
+static int create_core_temp_label(struct peci_cputemp *priv, int idx)
+{
+ priv->coretemp_label[idx] = devm_kzalloc(priv->dev,
+ PECI_HWMON_LABEL_STR_LEN,
+ GFP_KERNEL);
+ if (!priv->coretemp_label[idx])
+ return -ENOMEM;
+
+ sprintf(priv->coretemp_label[idx], "Core %d", idx + 1);
+
+ return 0;
+}
+
+static int create_core_temp_info(struct peci_cputemp *priv)
+{
+ int ret, i;
+
+ ret = check_resolved_cores(priv);
+ if (ret)
+ return ret;
+
+ priv->coretemp_label = devm_kzalloc(priv->dev,
+ priv->gen_info->core_max *
+ sizeof(char *),
+ GFP_KERNEL);
+ if (!priv->coretemp_label)
+ return -ENOMEM;
+
+ for (i = 0; i < priv->gen_info->core_max; i++)
+ if (priv->core_mask & BIT(i)) {
+ while (priv->config_idx <= i + DEFAULT_CHANNEL_NUMS)
+ priv->temp_config[priv->config_idx++] =
+ config_table[channel_core];
+
+ ret = create_core_temp_label(priv, i);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int peci_cputemp_probe(struct platform_device *pdev)
+{
+ struct peci_client_manager *mgr = dev_get_drvdata(pdev->dev.parent);
+ struct device *dev = &pdev->dev;
+ struct peci_cputemp *priv;
+ struct device *hwmon_dev;
+ int ret;
+
+ if ((mgr->client->adapter->cmd_mask &
+ (BIT(PECI_CMD_GET_TEMP) | BIT(PECI_CMD_RD_PKG_CFG))) !=
+ (BIT(PECI_CMD_GET_TEMP) | BIT(PECI_CMD_RD_PKG_CFG)))
+ return -ENODEV;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, priv);
+ priv->mgr = mgr;
+ priv->dev = dev;
+ priv->gen_info = mgr->gen_info;
+
+ snprintf(priv->name, PECI_NAME_SIZE, "peci_cputemp.cpu%d",
+ mgr->client->addr - PECI_BASE_ADDR);
+
+ priv->temp_config[priv->config_idx++] = config_table[channel_die];
+ priv->temp_config[priv->config_idx++] = config_table[channel_dts];
+ priv->temp_config[priv->config_idx++] = config_table[channel_tcontrol];
+ priv->temp_config[priv->config_idx++] = config_table[channel_tthrottle];
+ priv->temp_config[priv->config_idx++] = config_table[channel_tjmax];
+
+ ret = create_core_temp_info(priv);
+ if (ret)
+ dev_dbg(dev, "Skipped creating core temp info\n");
+
+ priv->chip.ops = &cputemp_ops;
+ priv->chip.info = priv->info;
+
+ priv->info[0] = &priv->temp_info;
+
+ priv->temp_info.type = hwmon_temp;
+ priv->temp_info.config = priv->temp_config;
+
+ hwmon_dev = devm_hwmon_device_register_with_info(priv->dev,
+ priv->name,
+ priv,
+ &priv->chip,
+ NULL);
+
+ if (IS_ERR(hwmon_dev))
+ return PTR_ERR(hwmon_dev);
+
+ dev_dbg(dev, "%s: sensor '%s'\n", dev_name(hwmon_dev), priv->name);
+
+ return 0;
+}
+
+static const struct platform_device_id peci_cputemp_ids[] = {
+ { .name = "peci-cputemp", .driver_data = 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(platform, peci_cputemp_ids);
+
+static struct platform_driver peci_cputemp_driver = {
+ .probe = peci_cputemp_probe,
+ .id_table = peci_cputemp_ids,
+ .driver = { .name = KBUILD_MODNAME, },
+};
+module_platform_driver(peci_cputemp_driver);
+
+MODULE_AUTHOR("Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>");
+MODULE_DESCRIPTION("PECI cputemp driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/hwmon/peci-dimmtemp.c b/drivers/hwmon/peci-dimmtemp.c
new file mode 100644
index 000000000000..1555bfdefabd
--- /dev/null
+++ b/drivers/hwmon/peci-dimmtemp.c
@@ -0,0 +1,425 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2018-2019 Intel Corporation
+
+#include <linux/hwmon.h>
+#include <linux/jiffies.h>
+#include <linux/mfd/intel-peci-client.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/workqueue.h>
+#include "peci-hwmon.h"
+
+#define DIMM_MASK_CHECK_DELAY_JIFFIES msecs_to_jiffies(5000)
+#define DIMM_MASK_CHECK_RETRY_MAX 60 /* 60 x 5 secs = 5 minutes */
+
+struct peci_dimmtemp {
+ struct peci_client_manager *mgr;
+ struct device *dev;
+ char name[PECI_NAME_SIZE];
+ const struct cpu_gen_info *gen_info;
+ struct workqueue_struct *work_queue;
+ struct delayed_work work_handler;
+ struct peci_sensor_data temp[DIMM_NUMS_MAX];
+ long temp_max[DIMM_NUMS_MAX];
+ long temp_crit[DIMM_NUMS_MAX];
+ u32 dimm_mask;
+ int retry_count;
+ u32 temp_config[DIMM_NUMS_MAX + 1];
+ struct hwmon_channel_info temp_info;
+ const struct hwmon_channel_info *info[2];
+ struct hwmon_chip_info chip;
+ char **dimmtemp_label;
+};
+
+static const u8 support_model[4] = {
+ INTEL_FAM6_HASWELL_X,
+ INTEL_FAM6_BROADWELL_X,
+ INTEL_FAM6_SKYLAKE_X,
+ INTEL_FAM6_SKYLAKE_XD,
+};
+
+static inline int read_ddr_dimm_temp_config(struct peci_dimmtemp *priv,
+ int chan_rank,
+ u8 *cfg_data)
+{
+ return peci_client_read_package_config(priv->mgr,
+ PECI_MBX_INDEX_DDR_DIMM_TEMP,
+ chan_rank, cfg_data);
+}
+
+static int get_dimm_temp(struct peci_dimmtemp *priv, int dimm_no)
+{
+ int dimm_order = dimm_no % priv->gen_info->dimm_idx_max;
+ int chan_rank = dimm_no / priv->gen_info->dimm_idx_max;
+ struct peci_rd_pci_cfg_local_msg rp_msg;
+ u8 cfg_data[4];
+ int ret;
+
+ if (!peci_sensor_need_update(&priv->temp[dimm_no]))
+ return 0;
+
+ ret = read_ddr_dimm_temp_config(priv, chan_rank, cfg_data);
+ if (ret)
+ return ret;
+
+ priv->temp[dimm_no].value = cfg_data[dimm_order] * 1000;
+
+ switch (priv->gen_info->model) {
+ case INTEL_FAM6_SKYLAKE_X:
+ rp_msg.addr = priv->mgr->client->addr;
+ rp_msg.bus = 2;
+ /*
+ * Device 10, Function 2: IMC 0 channel 0 -> rank 0
+ * Device 10, Function 6: IMC 0 channel 1 -> rank 1
+ * Device 11, Function 2: IMC 0 channel 2 -> rank 2
+ * Device 12, Function 2: IMC 1 channel 0 -> rank 3
+ * Device 12, Function 6: IMC 1 channel 1 -> rank 4
+ * Device 13, Function 2: IMC 1 channel 2 -> rank 5
+ */
+ rp_msg.device = 10 + chan_rank / 3 * 2 +
+ (chan_rank % 3 == 2 ? 1 : 0);
+ rp_msg.function = chan_rank % 3 == 1 ? 6 : 2;
+ rp_msg.reg = 0x120 + dimm_order * 4;
+ rp_msg.rx_len = 4;
+
+ ret = peci_command(priv->mgr->client->adapter,
+ PECI_CMD_RD_PCI_CFG_LOCAL, &rp_msg);
+ if (rp_msg.cc != PECI_DEV_CC_SUCCESS)
+ ret = -EAGAIN;
+ if (ret)
+ return ret;
+
+ priv->temp_max[dimm_no] = rp_msg.pci_config[1] * 1000;
+ priv->temp_crit[dimm_no] = rp_msg.pci_config[2] * 1000;
+ break;
+ case INTEL_FAM6_SKYLAKE_XD:
+ rp_msg.addr = priv->mgr->client->addr;
+ rp_msg.bus = 2;
+ /*
+ * Device 10, Function 2: IMC 0 channel 0 -> rank 0
+ * Device 10, Function 6: IMC 0 channel 1 -> rank 1
+ * Device 12, Function 2: IMC 1 channel 0 -> rank 2
+ * Device 12, Function 6: IMC 1 channel 1 -> rank 3
+ */
+ rp_msg.device = 10 + chan_rank / 2 * 2;
+ rp_msg.function = (chan_rank % 2) ? 6 : 2;
+ rp_msg.reg = 0x120 + dimm_order * 4;
+ rp_msg.rx_len = 4;
+
+ ret = peci_command(priv->mgr->client->adapter,
+ PECI_CMD_RD_PCI_CFG_LOCAL, &rp_msg);
+ if (rp_msg.cc != PECI_DEV_CC_SUCCESS)
+ ret = -EAGAIN;
+ if (ret)
+ return ret;
+
+ priv->temp_max[dimm_no] = rp_msg.pci_config[1] * 1000;
+ priv->temp_crit[dimm_no] = rp_msg.pci_config[2] * 1000;
+ break;
+ case INTEL_FAM6_HASWELL_X:
+ case INTEL_FAM6_BROADWELL_X:
+ rp_msg.addr = priv->mgr->client->addr;
+ rp_msg.bus = 1;
+ /*
+ * Device 20, Function 0: IMC 0 channel 0 -> rank 0
+ * Device 20, Function 1: IMC 0 channel 1 -> rank 1
+ * Device 21, Function 0: IMC 0 channel 2 -> rank 2
+ * Device 21, Function 1: IMC 0 channel 3 -> rank 3
+ * Device 23, Function 0: IMC 1 channel 0 -> rank 4
+ * Device 23, Function 1: IMC 1 channel 1 -> rank 5
+ * Device 24, Function 0: IMC 1 channel 2 -> rank 6
+ * Device 24, Function 1: IMC 1 channel 3 -> rank 7
+ */
+ rp_msg.device = 20 + chan_rank / 2 + chan_rank / 4;
+ rp_msg.function = chan_rank % 2;
+ rp_msg.reg = 0x120 + dimm_order * 4;
+ rp_msg.rx_len = 4;
+
+ ret = peci_command(priv->mgr->client->adapter,
+ PECI_CMD_RD_PCI_CFG_LOCAL, &rp_msg);
+ if (rp_msg.cc != PECI_DEV_CC_SUCCESS)
+ ret = -EAGAIN;
+ if (ret)
+ return ret;
+
+ priv->temp_max[dimm_no] = rp_msg.pci_config[1] * 1000;
+ priv->temp_crit[dimm_no] = rp_msg.pci_config[2] * 1000;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ peci_sensor_mark_updated(&priv->temp[dimm_no]);
+
+ return 0;
+}
+
+static int dimmtemp_read_string(struct device *dev,
+ enum hwmon_sensor_types type,
+ u32 attr, int channel, const char **str)
+{
+ struct peci_dimmtemp *priv = dev_get_drvdata(dev);
+
+ if (attr != hwmon_temp_label)
+ return -EOPNOTSUPP;
+
+ *str = (const char *)priv->dimmtemp_label[channel];
+
+ return 0;
+}
+
+static int dimmtemp_read(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long *val)
+{
+ struct peci_dimmtemp *priv = dev_get_drvdata(dev);
+ int ret;
+
+ ret = get_dimm_temp(priv, channel);
+ if (ret)
+ return ret;
+
+ switch (attr) {
+ case hwmon_temp_input:
+ *val = priv->temp[channel].value;
+ break;
+ case hwmon_temp_max:
+ *val = priv->temp_max[channel];
+ break;
+ case hwmon_temp_crit:
+ *val = priv->temp_crit[channel];
+ break;
+ default:
+ ret = -EOPNOTSUPP;
+ break;
+ }
+
+ return ret;
+}
+
+static umode_t dimmtemp_is_visible(const void *data,
+ enum hwmon_sensor_types type,
+ u32 attr, int channel)
+{
+ const struct peci_dimmtemp *priv = data;
+
+ if (priv->temp_config[channel] & BIT(attr) &&
+ priv->dimm_mask & BIT(channel))
+ return 0444;
+
+ return 0;
+}
+
+static const struct hwmon_ops dimmtemp_ops = {
+ .is_visible = dimmtemp_is_visible,
+ .read_string = dimmtemp_read_string,
+ .read = dimmtemp_read,
+};
+
+static int check_populated_dimms(struct peci_dimmtemp *priv)
+{
+ u32 chan_rank_max = priv->gen_info->chan_rank_max;
+ u32 dimm_idx_max = priv->gen_info->dimm_idx_max;
+ int chan_rank, dimm_idx;
+ u8 cfg_data[4];
+
+ for (chan_rank = 0; chan_rank < chan_rank_max; chan_rank++) {
+ int ret;
+
+ ret = read_ddr_dimm_temp_config(priv, chan_rank, cfg_data);
+ if (ret) {
+ priv->dimm_mask = 0;
+ return ret;
+ }
+
+ for (dimm_idx = 0; dimm_idx < dimm_idx_max; dimm_idx++)
+ if (cfg_data[dimm_idx])
+ priv->dimm_mask |= BIT(chan_rank *
+ dimm_idx_max +
+ dimm_idx);
+ }
+
+ if (!priv->dimm_mask)
+ return -EAGAIN;
+
+ dev_dbg(priv->dev, "Scanned populated DIMMs: 0x%x\n", priv->dimm_mask);
+
+ return 0;
+}
+
+static int create_dimm_temp_label(struct peci_dimmtemp *priv, int chan)
+{
+ int rank, idx;
+
+ priv->dimmtemp_label[chan] = devm_kzalloc(priv->dev,
+ PECI_HWMON_LABEL_STR_LEN,
+ GFP_KERNEL);
+ if (!priv->dimmtemp_label[chan])
+ return -ENOMEM;
+
+ rank = chan / priv->gen_info->dimm_idx_max;
+ idx = chan % priv->gen_info->dimm_idx_max;
+
+ sprintf(priv->dimmtemp_label[chan], "DIMM %c%d", 'A' + rank, idx + 1);
+
+ return 0;
+}
+
+static int create_dimm_temp_info(struct peci_dimmtemp *priv)
+{
+ int ret, i, config_idx, channels;
+ struct device *dev;
+
+ ret = check_populated_dimms(priv);
+ if (ret) {
+ if (ret == -EAGAIN) {
+ if (priv->retry_count < DIMM_MASK_CHECK_RETRY_MAX) {
+ queue_delayed_work(priv->work_queue,
+ &priv->work_handler,
+ DIMM_MASK_CHECK_DELAY_JIFFIES);
+ priv->retry_count++;
+ dev_dbg(priv->dev,
+ "Deferred DIMM temp info creation\n");
+ } else {
+ dev_err(priv->dev,
+ "Timeout DIMM temp info creation\n");
+ ret = -ETIMEDOUT;
+ }
+ }
+
+ return ret;
+ }
+
+ channels = priv->gen_info->chan_rank_max *
+ priv->gen_info->dimm_idx_max;
+
+ priv->dimmtemp_label = devm_kzalloc(priv->dev,
+ channels * sizeof(char *),
+ GFP_KERNEL);
+ if (!priv->dimmtemp_label)
+ return -ENOMEM;
+
+ for (i = 0, config_idx = 0; i < channels; i++)
+ if (priv->dimm_mask & BIT(i)) {
+ while (i >= config_idx)
+ priv->temp_config[config_idx++] =
+ HWMON_T_LABEL | HWMON_T_INPUT |
+ HWMON_T_MAX | HWMON_T_CRIT;
+
+ ret = create_dimm_temp_label(priv, i);
+ if (ret)
+ return ret;
+ }
+
+ priv->chip.ops = &dimmtemp_ops;
+ priv->chip.info = priv->info;
+
+ priv->info[0] = &priv->temp_info;
+
+ priv->temp_info.type = hwmon_temp;
+ priv->temp_info.config = priv->temp_config;
+
+ dev = devm_hwmon_device_register_with_info(priv->dev,
+ priv->name,
+ priv,
+ &priv->chip,
+ NULL);
+ if (IS_ERR(dev)) {
+ dev_err(priv->dev, "Failed to register hwmon device\n");
+ return PTR_ERR(dev);
+ }
+
+ dev_dbg(priv->dev, "%s: sensor '%s'\n", dev_name(dev), priv->name);
+
+ return 0;
+}
+
+static void create_dimm_temp_info_delayed(struct work_struct *work)
+{
+ struct delayed_work *dwork = to_delayed_work(work);
+ struct peci_dimmtemp *priv = container_of(dwork, struct peci_dimmtemp,
+ work_handler);
+ int ret;
+
+ ret = create_dimm_temp_info(priv);
+ if (ret && ret != -EAGAIN)
+ dev_dbg(priv->dev, "Failed to create DIMM temp info\n");
+}
+
+static int peci_dimmtemp_probe(struct platform_device *pdev)
+{
+ struct peci_client_manager *mgr = dev_get_drvdata(pdev->dev.parent);
+ struct device *dev = &pdev->dev;
+ struct peci_dimmtemp *priv;
+ int ret, i;
+
+ if ((mgr->client->adapter->cmd_mask &
+ (BIT(PECI_CMD_GET_TEMP) | BIT(PECI_CMD_RD_PKG_CFG))) !=
+ (BIT(PECI_CMD_GET_TEMP) | BIT(PECI_CMD_RD_PKG_CFG)))
+ return -ENODEV;
+
+ for (i = 0; i < ARRAY_SIZE(support_model); i++) {
+ if (mgr->gen_info->model == support_model[i])
+ break;
+ }
+ if (i == ARRAY_SIZE(support_model))
+ return -ENODEV;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, priv);
+ priv->mgr = mgr;
+ priv->dev = dev;
+ priv->gen_info = mgr->gen_info;
+
+ snprintf(priv->name, PECI_NAME_SIZE, "peci_dimmtemp.cpu%d",
+ priv->mgr->client->addr - PECI_BASE_ADDR);
+
+ priv->work_queue = alloc_ordered_workqueue(priv->name, 0);
+ if (!priv->work_queue)
+ return -ENOMEM;
+
+ INIT_DELAYED_WORK(&priv->work_handler, create_dimm_temp_info_delayed);
+
+ ret = create_dimm_temp_info(priv);
+ if (ret && ret != -EAGAIN) {
+ dev_dbg(dev, "Failed to create DIMM temp info\n");
+ goto err_free_wq;
+ }
+
+ return 0;
+
+err_free_wq:
+ destroy_workqueue(priv->work_queue);
+ return ret;
+}
+
+static int peci_dimmtemp_remove(struct platform_device *pdev)
+{
+ struct peci_dimmtemp *priv = dev_get_drvdata(&pdev->dev);
+
+ cancel_delayed_work_sync(&priv->work_handler);
+ destroy_workqueue(priv->work_queue);
+
+ return 0;
+}
+
+static const struct platform_device_id peci_dimmtemp_ids[] = {
+ { .name = "peci-dimmtemp", .driver_data = 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(platform, peci_dimmtemp_ids);
+
+static struct platform_driver peci_dimmtemp_driver = {
+ .probe = peci_dimmtemp_probe,
+ .remove = peci_dimmtemp_remove,
+ .id_table = peci_dimmtemp_ids,
+ .driver = { .name = KBUILD_MODNAME, },
+};
+module_platform_driver(peci_dimmtemp_driver);
+
+MODULE_AUTHOR("Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>");
+MODULE_DESCRIPTION("PECI dimmtemp driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/hwmon/peci-hwmon.h b/drivers/hwmon/peci-hwmon.h
new file mode 100644
index 000000000000..4d78c528c4c8
--- /dev/null
+++ b/drivers/hwmon/peci-hwmon.h
@@ -0,0 +1,48 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (c) 2018-2019 Intel Corporation */
+
+#ifndef __PECI_HWMON_H
+#define __PECI_HWMON_H
+
+#include <linux/peci.h>
+
+#define TEMP_TYPE_PECI 6 /* Sensor type 6: Intel PECI */
+#define UPDATE_INTERVAL HZ
+
+#define PECI_HWMON_LABEL_STR_LEN 10
+
+/**
+ * struct peci_sensor_data - PECI sensor information
+ * @valid: flag to indicate the sensor value is valid
+ * @value: sensor value in millidegree Celsius
+ * @last_updated: time of the last update in jiffies
+ */
+struct peci_sensor_data {
+ uint valid;
+ s32 value;
+ ulong last_updated;
+};
+
+/**
+ * peci_sensor_need_update - check whether sensor update is needed or not
+ * @sensor: pointer to sensor data struct
+ *
+ * Return: true if update is needed, false if not.
+ */
+static inline bool peci_sensor_need_update(struct peci_sensor_data *sensor)
+{
+ return !sensor->valid ||
+ time_after(jiffies, sensor->last_updated + UPDATE_INTERVAL);
+}
+
+/**
+ * peci_sensor_mark_updated - mark the sensor is updated
+ * @sensor: pointer to sensor data struct
+ */
+static inline void peci_sensor_mark_updated(struct peci_sensor_data *sensor)
+{
+ sensor->valid = 1;
+ sensor->last_updated = jiffies;
+}
+
+#endif /* __PECI_HWMON_H */
diff --git a/drivers/hwmon/pmbus/max31785.c b/drivers/hwmon/pmbus/max31785.c
index d9aa5c873d21..cbcd0b2301f4 100644
--- a/drivers/hwmon/pmbus/max31785.c
+++ b/drivers/hwmon/pmbus/max31785.c
@@ -12,40 +12,126 @@
enum max31785_regs {
MFR_REVISION = 0x9b,
+ MFR_FAULT_RESPONSE = 0xd9,
+ MFR_TEMP_SENSOR_CONFIG = 0xf0,
MFR_FAN_CONFIG = 0xf1,
+ MFR_FAN_FAULT_LIMIT = 0xf5,
};
#define MAX31785 0x3030
#define MAX31785A 0x3040
#define MFR_FAN_CONFIG_DUAL_TACH BIT(12)
+#define MFR_FAN_CONFIG_TSFO BIT(9)
+#define MFR_FAN_CONFIG_TACHO BIT(8)
+#define MFR_FAN_CONFIG_HEALTH BIT(4)
+#define MFR_FAN_CONFIG_ROTOR_HI_LO BIT(3)
+#define MFR_FAN_CONFIG_ROTOR BIT(2)
+
+#define MFR_FAULT_RESPONSE_MONITOR BIT(0)
#define MAX31785_NR_PAGES 23
#define MAX31785_NR_FAN_PAGES 6
+/*
+ * MAX31785 dragons ahead
+ *
+ * We see weird issues where some transfers fail. There doesn't appear to be
+ * any pattern to the problem, so below we wrap all the read/write calls with a
+ * retry. The device provides no indication of this besides NACK'ing master
+ * Txs; no bits are set in STATUS_BYTE to suggest anything has gone wrong.
+ */
+
+#define max31785_retry(_func, ...) ({ \
+ /* All relevant functions return int, sue me */ \
+ int _ret = _func(__VA_ARGS__); \
+ if (_ret == -EIO) \
+ _ret = _func(__VA_ARGS__); \
+ _ret; \
+})
+
+static int max31785_i2c_smbus_read_byte_data(struct i2c_client *client,
+ int command)
+{
+ return max31785_retry(i2c_smbus_read_byte_data, client, command);
+}
+
+
+static int max31785_i2c_smbus_write_byte_data(struct i2c_client *client,
+ int command, u16 data)
+{
+ return max31785_retry(i2c_smbus_write_byte_data, client, command, data);
+}
+
+static int max31785_i2c_smbus_read_word_data(struct i2c_client *client,
+ int command)
+{
+ return max31785_retry(i2c_smbus_read_word_data, client, command);
+}
+
+static int max31785_i2c_smbus_write_word_data(struct i2c_client *client,
+ int command, u16 data)
+{
+ return max31785_retry(i2c_smbus_write_word_data, client, command, data);
+}
+
+static int max31785_pmbus_write_byte(struct i2c_client *client, int page,
+ u8 value)
+{
+ return max31785_retry(pmbus_write_byte, client, page, value);
+}
+
+static int max31785_pmbus_read_byte_data(struct i2c_client *client, int page,
+ int command)
+{
+ return max31785_retry(pmbus_read_byte_data, client, page, command);
+}
+
+static int max31785_pmbus_write_byte_data(struct i2c_client *client, int page,
+ int command, u16 data)
+{
+ return max31785_retry(pmbus_write_byte_data, client, page, command,
+ data);
+}
+
+static int max31785_pmbus_read_word_data(struct i2c_client *client, int page,
+ int phase, int command)
+{
+ return max31785_retry(pmbus_read_word_data, client, page, phase, command);
+}
+
+static int max31785_pmbus_write_word_data(struct i2c_client *client, int page,
+ int command, u16 data)
+{
+ return max31785_retry(pmbus_write_word_data, client, page, command,
+ data);
+}
+
static int max31785_read_byte_data(struct i2c_client *client, int page,
int reg)
{
- if (page < MAX31785_NR_PAGES)
- return -ENODATA;
-
switch (reg) {
case PMBUS_VOUT_MODE:
- return -ENOTSUPP;
+ if (page >= MAX31785_NR_PAGES)
+ return -ENOTSUPP;
+ break;
case PMBUS_FAN_CONFIG_12:
- return pmbus_read_byte_data(client, page - MAX31785_NR_PAGES,
- reg);
+ if (page >= MAX31785_NR_PAGES)
+ return max31785_pmbus_read_byte_data(client,
+ page - MAX31785_NR_PAGES,
+ reg);
+ break;
}
- return -ENODATA;
+ return max31785_pmbus_read_byte_data(client, page, reg);
}
static int max31785_write_byte(struct i2c_client *client, int page, u8 value)
{
- if (page < MAX31785_NR_PAGES)
- return -ENODATA;
+ if (page >= MAX31785_NR_PAGES)
+ return -ENOTSUPP;
- return -ENOTSUPP;
+ return max31785_pmbus_write_byte(client, page, value);
}
static int max31785_read_long_data(struct i2c_client *client, int page,
@@ -106,11 +192,13 @@ static int max31785_get_pwm_mode(struct i2c_client *client, int page)
int config;
int command;
- config = pmbus_read_byte_data(client, page, PMBUS_FAN_CONFIG_12);
+ config = max31785_pmbus_read_byte_data(client, page,
+ PMBUS_FAN_CONFIG_12);
if (config < 0)
return config;
- command = pmbus_read_word_data(client, page, 0xff, PMBUS_FAN_COMMAND_1);
+ command = max31785_pmbus_read_word_data(client, page, 0xff,
+ PMBUS_FAN_COMMAND_1);
if (command < 0)
return command;
@@ -134,15 +222,14 @@ static int max31785_read_word_data(struct i2c_client *client, int page,
switch (reg) {
case PMBUS_READ_FAN_SPEED_1:
if (page < MAX31785_NR_PAGES)
- return -ENODATA;
+ return max31785_pmbus_read_word_data(client, page, 0xff, reg);
rv = max31785_read_long_data(client, page - MAX31785_NR_PAGES,
reg, &val);
if (rv < 0)
return rv;
- rv = (val >> 16) & 0xffff;
- break;
+ return (val >> 16) & 0xffff;
case PMBUS_FAN_COMMAND_1:
/*
* PMBUS_FAN_COMMAND_x is probed to judge whether or not to
@@ -150,20 +237,28 @@ static int max31785_read_word_data(struct i2c_client *client, int page,
*
* Don't expose fan_target attribute for virtual pages.
*/
- rv = (page >= MAX31785_NR_PAGES) ? -ENOTSUPP : -ENODATA;
+ if (page >= MAX31785_NR_PAGES)
+ return -ENOTSUPP;
break;
+ case PMBUS_VIRT_FAN_TARGET_1:
+ if (page >= MAX31785_NR_PAGES)
+ return -ENOTSUPP;
+
+ return -ENODATA;
case PMBUS_VIRT_PWM_1:
- rv = max31785_get_pwm(client, page);
- break;
+ return max31785_get_pwm(client, page);
case PMBUS_VIRT_PWM_ENABLE_1:
- rv = max31785_get_pwm_mode(client, page);
- break;
+ return max31785_get_pwm_mode(client, page);
default:
- rv = -ENODATA;
+ if (page >= MAX31785_NR_PAGES)
+ return -ENXIO;
break;
}
- return rv;
+ if (reg >= PMBUS_VIRT_BASE)
+ return -ENXIO;
+
+ return max31785_pmbus_read_word_data(client, page, 0xff, reg);
}
static inline u32 max31785_scale_pwm(u32 sensor_val)
@@ -187,6 +282,31 @@ static inline u32 max31785_scale_pwm(u32 sensor_val)
return (sensor_val * 100) / 255;
}
+static int max31785_update_fan(struct i2c_client *client, int page,
+ u8 config, u8 mask, u16 command)
+{
+ int from, rv;
+ u8 to;
+
+ from = max31785_pmbus_read_byte_data(client, page, PMBUS_FAN_CONFIG_12);
+ if (from < 0)
+ return from;
+
+ to = (from & ~mask) | (config & mask);
+
+ if (to != from) {
+ rv = max31785_pmbus_write_byte_data(client, page,
+ PMBUS_FAN_CONFIG_12, to);
+ if (rv < 0)
+ return rv;
+ }
+
+ rv = max31785_pmbus_write_word_data(client, page, PMBUS_FAN_COMMAND_1,
+ command);
+
+ return rv;
+}
+
static int max31785_pwm_enable(struct i2c_client *client, int page,
u16 word)
{
@@ -216,15 +336,18 @@ static int max31785_pwm_enable(struct i2c_client *client, int page,
return -EINVAL;
}
- return pmbus_update_fan(client, page, 0, config, PB_FAN_1_RPM, rate);
+ return max31785_update_fan(client, page, config, PB_FAN_1_RPM, rate);
}
static int max31785_write_word_data(struct i2c_client *client, int page,
int reg, u16 word)
{
switch (reg) {
+ case PMBUS_VIRT_FAN_TARGET_1:
+ return max31785_update_fan(client, page, PB_FAN_1_RPM,
+ PB_FAN_1_RPM, word);
case PMBUS_VIRT_PWM_1:
- return pmbus_update_fan(client, page, 0, 0, PB_FAN_1_RPM,
+ return max31785_update_fan(client, page, 0, PB_FAN_1_RPM,
max31785_scale_pwm(word));
case PMBUS_VIRT_PWM_ENABLE_1:
return max31785_pwm_enable(client, page, word);
@@ -232,7 +355,279 @@ static int max31785_write_word_data(struct i2c_client *client, int page,
break;
}
- return -ENODATA;
+ if (reg < PMBUS_VIRT_BASE)
+ return max31785_pmbus_write_word_data(client, page, reg, word);
+
+ return -ENXIO;
+}
+
+/*
+ * Returns negative error codes if an unrecoverable problem is detected, 0 if a
+ * recoverable problem is detected, or a positive value on success.
+ */
+static int max31785_of_fan_config(struct i2c_client *client,
+ struct pmbus_driver_info *info,
+ struct device_node *child)
+{
+ int mfr_cfg = 0, mfr_fault_resp = 0, pb_cfg;
+ struct device *dev = &client->dev;
+ char *lock_polarity = NULL;
+ const char *sval;
+ u32 page;
+ u32 uval;
+ int ret;
+
+ if (!of_device_is_compatible(child, "pmbus-fan"))
+ return 0;
+
+ ret = of_property_read_u32(child, "reg", &page);
+ if (ret < 0) {
+ dev_err(&client->dev, "Missing valid reg property\n");
+ return ret;
+ }
+
+ if (!(info->func[page] & PMBUS_HAVE_FAN12)) {
+ dev_err(dev, "Page %d does not have fan capabilities\n", page);
+ return -ENXIO;
+ }
+
+ ret = max31785_i2c_smbus_write_byte_data(client, PMBUS_PAGE, page);
+ if (ret < 0)
+ return ret;
+
+ pb_cfg = max31785_i2c_smbus_read_byte_data(client, PMBUS_FAN_CONFIG_12);
+ if (pb_cfg < 0)
+ return pb_cfg;
+
+ if (of_property_read_bool(child->parent, "use-stored-presence")) {
+ if (!(pb_cfg & PB_FAN_1_INSTALLED))
+ dev_info(dev, "Fan %d is configured but not installed\n",
+ page);
+ } else {
+ pb_cfg |= PB_FAN_1_INSTALLED;
+ }
+
+ ret = of_property_read_string(child, "maxim,fan-rotor-input", &sval);
+ if (ret < 0) {
+ dev_err(dev, "Missing valid maxim,fan-rotor-input property for fan %d\n",
+ page);
+ return ret;
+ }
+
+ if (strcmp("tach", sval) && strcmp("lock", sval)) {
+ dev_err(dev, "maxim,fan-rotor-input has invalid value for fan %d: %s\n",
+ page, sval);
+ return -EINVAL;
+ } else if (!strcmp("lock", sval)) {
+ mfr_cfg |= MFR_FAN_CONFIG_ROTOR;
+
+ ret = max31785_i2c_smbus_write_word_data(client,
+ MFR_FAN_FAULT_LIMIT,
+ 1);
+ if (ret < 0)
+ return ret;
+
+ ret = of_property_read_string(child, "maxim,fan-lock-polarity",
+ &sval);
+ if (ret < 0) {
+ dev_err(dev, "Missing valid maxim,fan-lock-polarity property for fan %d\n",
+ page);
+ return ret;
+ }
+
+ if (strcmp("low", sval) && strcmp("high", sval)) {
+ dev_err(dev, "maxim,fan-lock-polarity has invalid value for fan %d: %s\n",
+ page, lock_polarity);
+ return -EINVAL;
+ } else if (!strcmp("high", sval))
+ mfr_cfg |= MFR_FAN_CONFIG_ROTOR_HI_LO;
+ }
+
+ if (!of_property_read_string(child, "fan-mode", &sval)) {
+ if (!strcmp("rpm", sval))
+ pb_cfg |= PB_FAN_1_RPM;
+ else if (!strcmp("pwm", sval))
+ pb_cfg &= ~PB_FAN_1_RPM;
+ else {
+ dev_err(dev, "fan-mode has invalid value for fan %d: %s\n",
+ page, sval);
+ return -EINVAL;
+ }
+ }
+
+ ret = of_property_read_u32(child, "tach-pulses", &uval);
+ if (ret < 0) {
+ pb_cfg &= ~PB_FAN_1_PULSE_MASK;
+ } else if (uval && (uval - 1) < 4) {
+ pb_cfg = ((pb_cfg & ~PB_FAN_1_PULSE_MASK) | ((uval - 1) << 4));
+ } else {
+ dev_err(dev, "tach-pulses has invalid value for fan %d: %u\n",
+ page, uval);
+ return -EINVAL;
+ }
+
+ if (of_property_read_bool(child, "maxim,fan-health"))
+ mfr_cfg |= MFR_FAN_CONFIG_HEALTH;
+
+ if (of_property_read_bool(child, "maxim,fan-no-watchdog") ||
+ of_property_read_bool(child, "maxim,tmp-no-fault-ramp"))
+ mfr_cfg |= MFR_FAN_CONFIG_TSFO;
+
+ if (of_property_read_bool(child, "maxim,fan-dual-tach"))
+ mfr_cfg |= MFR_FAN_CONFIG_DUAL_TACH;
+
+ if (of_property_read_bool(child, "maxim,fan-no-fault-ramp"))
+ mfr_cfg |= MFR_FAN_CONFIG_TACHO;
+
+ if (!of_property_read_u32(child, "maxim,fan-startup", &uval)) {
+ uval /= 2;
+ if (uval < 5) {
+ mfr_cfg |= uval;
+ } else {
+ dev_err(dev, "maxim,fan-startup has invalid value for fan %d: %u\n",
+ page, uval);
+ return -EINVAL;
+ }
+ }
+
+ if (!of_property_read_u32(child, "maxim,fan-ramp", &uval)) {
+ if (uval < 8) {
+ mfr_cfg |= uval << 5;
+ } else {
+ dev_err(dev, "maxim,fan-ramp has invalid value for fan %d: %u\n",
+ page, uval);
+ return -EINVAL;
+ }
+ }
+
+ if (!of_property_read_u32(child, "maxim,tmp-hysteresis", &uval)) {
+ uval /= 2;
+ uval -= 1;
+ if (uval < 4) {
+ mfr_cfg |= uval << 10;
+ } else {
+ dev_err(dev, "maxim,tmp-hysteresis has invalid value for fan %d, %u\n",
+ page, uval);
+ return -EINVAL;
+ }
+ }
+
+ if (!of_property_read_u32(child, "maxim,fan-pwm-freq", &uval)) {
+ u16 val;
+
+ if (uval == 30) {
+ val = 0;
+ } else if (uval == 50) {
+ val = 1;
+ } else if (uval == 100) {
+ val = 2;
+ } else if (uval == 150) {
+ val = 3;
+ } else if (uval == 25000) {
+ val = 7;
+ } else {
+ dev_err(dev, "maxim,fan-pwm-freq has invalid value for fan %d: %u\n",
+ page, uval);
+ return -EINVAL;
+ }
+
+ mfr_cfg |= val << 13;
+ }
+
+ if (of_property_read_bool(child, "maxim,fan-fault-pin-mon"))
+ mfr_fault_resp |= MFR_FAULT_RESPONSE_MONITOR;
+
+ ret = max31785_i2c_smbus_write_byte_data(client, PMBUS_FAN_CONFIG_12,
+ pb_cfg & ~PB_FAN_1_INSTALLED);
+ if (ret < 0)
+ return ret;
+
+ ret = max31785_i2c_smbus_write_word_data(client, MFR_FAN_CONFIG,
+ mfr_cfg);
+ if (ret < 0)
+ return ret;
+
+ ret = max31785_i2c_smbus_write_byte_data(client, MFR_FAULT_RESPONSE,
+ mfr_fault_resp);
+ if (ret < 0)
+ return ret;
+
+ ret = max31785_i2c_smbus_write_byte_data(client, PMBUS_FAN_CONFIG_12,
+ pb_cfg);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * Fans are on pages 0 - 5. If the page property of a fan node is
+ * greater than 5 we will have errored in checks above out above.
+ * Therefore we don't need to cope with values up to 31, and the int
+ * return type is enough.
+ *
+ * The bit mask return value is used to populate a bitfield of fans
+ * who are both configured in the devicetree _and_ reported as
+ * installed by the hardware. Any fans that are not configured in the
+ * devicetree but are reported as installed by the hardware will have
+ * their hardware configuration updated to unset the installed bit.
+ */
+ return BIT(page);
+}
+
+static int max31785_of_tmp_config(struct i2c_client *client,
+ struct pmbus_driver_info *info,
+ struct device_node *child)
+{
+ struct device *dev = &client->dev;
+ struct device_node *np;
+ u16 mfr_tmp_cfg = 0;
+ u32 page;
+ u32 uval;
+ int ret;
+ int i;
+
+ if (!of_device_is_compatible(child, "pmbus-temperature"))
+ return 0;
+
+ ret = of_property_read_u32(child, "reg", &page);
+ if (ret < 0) {
+ dev_err(&client->dev, "Missing valid reg property\n");
+ return ret;
+ }
+
+ if (!(info->func[page] & PMBUS_HAVE_TEMP)) {
+ dev_err(dev, "Page %d does not have temp capabilities\n", page);
+ return -ENXIO;
+ }
+
+ ret = max31785_i2c_smbus_write_byte_data(client, PMBUS_PAGE, page);
+ if (ret < 0)
+ return ret;
+
+ if (!of_property_read_u32(child, "maxim,tmp-offset", &uval)) {
+ if (uval < 32)
+ mfr_tmp_cfg |= uval << 10;
+ }
+
+ i = 0;
+ while ((np = of_parse_phandle(child, "maxim,tmp-fans", i))) {
+ if (of_property_read_u32(np, "reg", &uval)) {
+ dev_err(&client->dev, "Failed to read fan reg property for phandle index %d\n",
+ i);
+ } else {
+ if (uval < 6)
+ mfr_tmp_cfg |= BIT(uval);
+ else
+ dev_warn(&client->dev, "Invalid fan page: %d\n",
+ uval);
+ }
+ i++;
+ }
+
+ ret = max31785_i2c_smbus_write_word_data(client, MFR_TEMP_SENSOR_CONFIG,
+ mfr_tmp_cfg);
+ if (ret < 0)
+ return ret;
+
+ return 0;
}
#define MAX31785_FAN_FUNCS \
@@ -304,11 +699,11 @@ static int max31785_configure_dual_tach(struct i2c_client *client,
int i;
for (i = 0; i < MAX31785_NR_FAN_PAGES; i++) {
- ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, i);
+ ret = max31785_i2c_smbus_write_byte_data(client, PMBUS_PAGE, i);
if (ret < 0)
return ret;
- ret = i2c_smbus_read_word_data(client, MFR_FAN_CONFIG);
+ ret = max31785_i2c_smbus_read_word_data(client, MFR_FAN_CONFIG);
if (ret < 0)
return ret;
@@ -328,9 +723,12 @@ static int max31785_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct device *dev = &client->dev;
+ struct device_node *child;
struct pmbus_driver_info *info;
bool dual_tach = false;
+ u32 fans;
s64 ret;
+ int i;
if (!i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_BYTE_DATA |
@@ -343,7 +741,7 @@ static int max31785_probe(struct i2c_client *client,
*info = max31785_info;
- ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, 255);
+ ret = max31785_i2c_smbus_write_byte_data(client, PMBUS_PAGE, 255);
if (ret < 0)
return ret;
@@ -360,6 +758,49 @@ static int max31785_probe(struct i2c_client *client,
return -ENODEV;
}
+ fans = 0;
+ for_each_child_of_node(dev->of_node, child) {
+ ret = max31785_of_fan_config(client, info, child);
+ if (ret < 0) {
+ of_node_put(child);
+ return ret;
+ }
+
+ if (ret)
+ fans |= ret;
+
+ ret = max31785_of_tmp_config(client, info, child);
+ if (ret < 0) {
+ of_node_put(child);
+ return ret;
+ }
+ }
+
+ for (i = 0; i < MAX31785_NR_PAGES; i++) {
+ bool have_fan = !!(info->func[i] & PMBUS_HAVE_FAN12);
+ bool fan_configured = !!(fans & BIT(i));
+
+ if (!have_fan || fan_configured)
+ continue;
+
+ ret = max31785_i2c_smbus_write_byte_data(client, PMBUS_PAGE,
+ i);
+ if (ret < 0)
+ return ret;
+
+ ret = max31785_i2c_smbus_read_byte_data(client,
+ PMBUS_FAN_CONFIG_12);
+ if (ret < 0)
+ return ret;
+
+ ret &= ~PB_FAN_1_INSTALLED;
+ ret = max31785_i2c_smbus_write_word_data(client,
+ PMBUS_FAN_CONFIG_12,
+ ret);
+ if (ret < 0)
+ return ret;
+ }
+
if (dual_tach) {
ret = max31785_configure_dual_tach(client, info);
if (ret < 0)
diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c
index 2191575a448b..702c25010369 100644
--- a/drivers/hwmon/pmbus/pmbus_core.c
+++ b/drivers/hwmon/pmbus/pmbus_core.c
@@ -158,9 +158,19 @@ int pmbus_set_page(struct i2c_client *client, int page, int phase)
if (!(data->info->func[page] & PMBUS_PAGE_VIRTUAL) &&
data->info->pages > 1 && page != data->currpage) {
+ dev_dbg(&client->dev, "Want page %u, %u cached\n", page,
+ data->currpage);
+
rv = i2c_smbus_write_byte_data(client, PMBUS_PAGE, page);
- if (rv < 0)
- return rv;
+ if (rv < 0) {
+ rv = i2c_smbus_write_byte_data(client, PMBUS_PAGE,
+ page);
+ dev_dbg(&client->dev,
+ "Failed to set page %u, performed one-shot retry %s: %d\n",
+ page, rv ? "and failed" : "with success", rv);
+ if (rv < 0)
+ return rv;
+ }
rv = i2c_smbus_read_byte_data(client, PMBUS_PAGE);
if (rv < 0)
@@ -451,15 +461,15 @@ static int pmbus_get_fan_rate(struct i2c_client *client, int page, int id,
return s->data;
}
- config = pmbus_read_byte_data(client, page,
- pmbus_fan_config_registers[id]);
+ config = _pmbus_read_byte_data(client, page,
+ pmbus_fan_config_registers[id]);
if (config < 0)
return config;
have_rpm = !!(config & pmbus_fan_rpm_mask[id]);
if (want_rpm == have_rpm)
- return pmbus_read_word_data(client, page, 0xff,
- pmbus_fan_command_registers[id]);
+ return _pmbus_read_word_data(client, page, 0xff,
+ pmbus_fan_command_registers[id]);
/* Can't sensibly map between RPM and PWM, just return zero */
return 0;
diff --git a/drivers/i2c/busses/i2c-fsi.c b/drivers/i2c/busses/i2c-fsi.c
index 977d6f524649..95b6b6bc1d78 100644
--- a/drivers/i2c/busses/i2c-fsi.c
+++ b/drivers/i2c/busses/i2c-fsi.c
@@ -703,7 +703,12 @@ static int fsi_i2c_probe(struct device *dev)
for (port_no = 0; port_no < ports; port_no++) {
np = fsi_i2c_find_port_of_node(dev->of_node, port_no);
- if (np && !of_device_is_available(np))
+ /* Do not add port if it is not described in the device tree */
+ if (!np)
+ continue;
+
+ /* Do not add port if it is described as disabled */
+ if (!of_device_is_available(np))
continue;
port = kzalloc(sizeof(*port), GFP_KERNEL);
diff --git a/drivers/leds/leds-pca955x.c b/drivers/leds/leds-pca955x.c
index 4037c504589c..bf7ead45f66b 100644
--- a/drivers/leds/leds-pca955x.c
+++ b/drivers/leds/leds-pca955x.c
@@ -65,6 +65,7 @@ enum pca955x_type {
pca9550,
pca9551,
pca9552,
+ pca9552_ibm,
pca9553,
};
@@ -90,6 +91,11 @@ static struct pca955x_chipdef pca955x_chipdefs[] = {
.slv_addr = /* 1100xxx */ 0x60,
.slv_addr_shift = 3,
},
+ [pca9552_ibm] = {
+ .bits = 16,
+ .slv_addr = /* 0110xxx */ 0x30,
+ .slv_addr_shift = 3,
+ },
[pca9553] = {
.bits = 4,
.slv_addr = /* 110001x */ 0x62,
@@ -101,6 +107,7 @@ static const struct i2c_device_id pca955x_id[] = {
{ "pca9550", pca9550 },
{ "pca9551", pca9551 },
{ "pca9552", pca9552 },
+ { "pca9552-ibm", pca9552_ibm },
{ "pca9553", pca9553 },
{ }
};
@@ -412,6 +419,7 @@ static const struct of_device_id of_pca955x_match[] = {
{ .compatible = "nxp,pca9550", .data = (void *)pca9550 },
{ .compatible = "nxp,pca9551", .data = (void *)pca9551 },
{ .compatible = "nxp,pca9552", .data = (void *)pca9552 },
+ { .compatible = "nxp,pca9552-ibm", .data = (void *)pca9552_ibm },
{ .compatible = "nxp,pca9553", .data = (void *)pca9553 },
{},
};
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index a37d7d171382..a311541bce2c 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -670,6 +670,23 @@ config MFD_INTEL_PMC_BXT
Register and P-unit access. In addition this creates devices
for iTCO watchdog and telemetry that are part of the PMC.
+config MFD_INTEL_PECI_CLIENT
+ tristate "Intel PECI client"
+ depends on (PECI || COMPILE_TEST)
+ select MFD_CORE
+ help
+ If you say yes to this option, support will be included for the
+ Intel PECI (Platform Environment Control Interface) client. PECI is a
+ one-wire bus interface that provides a communication channel from PECI
+ clients in Intel processors and chipset components to external
+ monitoring or control devices.
+
+ Additional drivers must be enabled in order to use the functionality
+ of the device.
+
+ This driver can also be built as a module. If so, the module
+ will be called intel-peci-client.
+
config MFD_IPAQ_MICRO
bool "Atmel Micro ASIC (iPAQ h3100/h3600/h3700) Support"
depends on SA1100_H3100 || SA1100_H3600
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 9367a92f795a..e288e586b687 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -216,6 +216,7 @@ obj-$(CONFIG_MFD_INTEL_LPSS_PCI) += intel-lpss-pci.o
obj-$(CONFIG_MFD_INTEL_LPSS_ACPI) += intel-lpss-acpi.o
obj-$(CONFIG_MFD_INTEL_MSIC) += intel_msic.o
obj-$(CONFIG_MFD_INTEL_PMC_BXT) += intel_pmc_bxt.o
+obj-$(CONFIG_MFD_INTEL_PECI_CLIENT) += intel-peci-client.o
obj-$(CONFIG_MFD_PALMAS) += palmas.o
obj-$(CONFIG_MFD_VIPERBOARD) += viperboard.o
obj-$(CONFIG_MFD_RC5T583) += rc5t583.o rc5t583-irq.o
diff --git a/drivers/mfd/intel-peci-client.c b/drivers/mfd/intel-peci-client.c
new file mode 100644
index 000000000000..24f15438634c
--- /dev/null
+++ b/drivers/mfd/intel-peci-client.c
@@ -0,0 +1,148 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2018-2019 Intel Corporation
+
+#include <linux/bitfield.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/intel-peci-client.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/peci.h>
+
+#define CPU_ID_MODEL_MASK GENMASK(7, 4)
+#define CPU_ID_FAMILY_MASK GENMASK(11, 8)
+#define CPU_ID_EXT_MODEL_MASK GENMASK(19, 16)
+#define CPU_ID_EXT_FAMILY_MASK GENMASK(27, 20)
+
+#define LOWER_NIBBLE_MASK GENMASK(3, 0)
+#define UPPER_NIBBLE_MASK GENMASK(7, 4)
+#define LOWER_BYTE_MASK GENMASK(7, 0)
+#define UPPER_BYTE_MASK GENMASK(16, 8)
+
+static struct mfd_cell peci_functions[] = {
+ { .name = "peci-cputemp", },
+ { .name = "peci-dimmtemp", },
+};
+
+static const struct cpu_gen_info cpu_gen_info_table[] = {
+ { /* Haswell Xeon */
+ .family = INTEL_FAM6,
+ .model = INTEL_FAM6_HASWELL_X,
+ .core_max = CORE_MAX_ON_HSX,
+ .chan_rank_max = CHAN_RANK_MAX_ON_HSX,
+ .dimm_idx_max = DIMM_IDX_MAX_ON_HSX },
+ { /* Broadwell Xeon */
+ .family = INTEL_FAM6,
+ .model = INTEL_FAM6_BROADWELL_X,
+ .core_max = CORE_MAX_ON_BDX,
+ .chan_rank_max = CHAN_RANK_MAX_ON_BDX,
+ .dimm_idx_max = DIMM_IDX_MAX_ON_BDX },
+ { /* Skylake Xeon */
+ .family = INTEL_FAM6,
+ .model = INTEL_FAM6_SKYLAKE_X,
+ .core_max = CORE_MAX_ON_SKX,
+ .chan_rank_max = CHAN_RANK_MAX_ON_SKX,
+ .dimm_idx_max = DIMM_IDX_MAX_ON_SKX },
+ { /* Skylake Xeon D */
+ .family = INTEL_FAM6,
+ .model = INTEL_FAM6_SKYLAKE_XD,
+ .core_max = CORE_MAX_ON_SKXD,
+ .chan_rank_max = CHAN_RANK_MAX_ON_SKXD,
+ .dimm_idx_max = DIMM_IDX_MAX_ON_SKXD },
+};
+
+static int peci_client_get_cpu_gen_info(struct peci_client_manager *priv)
+{
+ struct device *dev = &priv->client->dev;
+ u32 cpu_id;
+ u16 family;
+ u8 model;
+ int ret;
+ int i;
+
+ ret = peci_get_cpu_id(priv->client->adapter, priv->client->addr,
+ &cpu_id);
+ if (ret)
+ return ret;
+
+ family = FIELD_PREP(LOWER_BYTE_MASK,
+ FIELD_GET(CPU_ID_FAMILY_MASK, cpu_id)) |
+ FIELD_PREP(UPPER_BYTE_MASK,
+ FIELD_GET(CPU_ID_EXT_FAMILY_MASK, cpu_id));
+ model = FIELD_PREP(LOWER_NIBBLE_MASK,
+ FIELD_GET(CPU_ID_MODEL_MASK, cpu_id)) |
+ FIELD_PREP(UPPER_NIBBLE_MASK,
+ FIELD_GET(CPU_ID_EXT_MODEL_MASK, cpu_id));
+
+ for (i = 0; i < ARRAY_SIZE(cpu_gen_info_table); i++) {
+ const struct cpu_gen_info *cpu_info = &cpu_gen_info_table[i];
+
+ if (family == cpu_info->family && model == cpu_info->model) {
+ priv->gen_info = cpu_info;
+ break;
+ }
+ }
+
+ if (!priv->gen_info) {
+ dev_err(dev, "Can't support this CPU: 0x%x\n", cpu_id);
+ ret = -ENODEV;
+ }
+
+ return ret;
+}
+
+static int peci_client_probe(struct peci_client *client)
+{
+ struct device *dev = &client->dev;
+ struct peci_client_manager *priv;
+ uint cpu_no;
+ int ret;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, priv);
+ priv->client = client;
+ cpu_no = client->addr - PECI_BASE_ADDR;
+
+ ret = peci_client_get_cpu_gen_info(priv);
+ if (ret)
+ return ret;
+
+ ret = devm_mfd_add_devices(dev, cpu_no, peci_functions,
+ ARRAY_SIZE(peci_functions), NULL, 0, NULL);
+ if (ret < 0) {
+ dev_err(dev, "Failed to register child devices: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id peci_client_of_table[] = {
+ { .compatible = "intel,peci-client" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, peci_client_of_table);
+#endif
+
+static const struct peci_device_id peci_client_ids[] = {
+ { .name = "peci-client" },
+ { }
+};
+MODULE_DEVICE_TABLE(peci, peci_client_ids);
+
+static struct peci_driver peci_client_driver = {
+ .probe = peci_client_probe,
+ .id_table = peci_client_ids,
+ .driver = {
+ .name = KBUILD_MODNAME,
+ .of_match_table = of_match_ptr(peci_client_of_table),
+ },
+};
+module_peci_driver(peci_client_driver);
+
+MODULE_AUTHOR("Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>");
+MODULE_DESCRIPTION("PECI client driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index e1b1ba5e2b92..d8626a0d3e31 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -444,6 +444,13 @@ config XILINX_SDFEC
If unsure, say N.
+config MCTP_LPC
+ tristate "MCTP LPC binding implementation for ASPEED BMCs"
+ depends on REGMAP
+ help
+ Implements the MCTP LPC binding via KCS LPC IO cycles for control and
+ LPC FWH cycles for data
+
config MISC_RTSX
tristate
default MISC_RTSX_PCI || MISC_RTSX_USB
@@ -456,6 +463,21 @@ config PVPANIC
a paravirtualized device provided by QEMU; it lets a virtual machine
(guest) communicate panic events to the host.
+config NPCM7XX_LPC_BPC
+ tristate "NPCM7xx LPC BIOS Post Code support"
+ depends on (ARCH_NPCM7XX || COMPILE_TEST)
+ help
+ Provides a NPCM7xx driver to control the LPC BIOS Post Code
+ interface which allows the BMC to monitoring and save
+ the data written by the host to an arbitrary LPC I/O port.
+
+config NPCM7XX_PCI_MBOX
+ tristate "NPCM7xx PCI Mailbox Controller"
+ depends on (ARCH_NPCM7XX || COMPILE_TEST) && REGMAP && MFD_SYSCON
+ help
+ Expose the NPCM750/730/715/705 PCI MBOX registers found on
+ Nuvoton SOCs to userspace.
+
source "drivers/misc/c2port/Kconfig"
source "drivers/misc/eeprom/Kconfig"
source "drivers/misc/cb710/Kconfig"
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index c7bd01ac6291..183970192ced 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -57,3 +57,6 @@ obj-$(CONFIG_PVPANIC) += pvpanic.o
obj-$(CONFIG_HABANA_AI) += habanalabs/
obj-$(CONFIG_UACCE) += uacce/
obj-$(CONFIG_XILINX_SDFEC) += xilinx_sdfec.o
+obj-$(CONFIG_NPCM7XX_LPC_BPC) += npcm7xx-lpc-bpc.o
+obj-$(CONFIG_NPCM7XX_PCI_MBOX) += npcm7xx-pci-mbox.o
+obj-$(CONFIG_MCTP_LPC) += mctp-lpc.o
diff --git a/drivers/misc/eeprom/at25.c b/drivers/misc/eeprom/at25.c
index cde9a2fc1325..f3863929f8e3 100644
--- a/drivers/misc/eeprom/at25.c
+++ b/drivers/misc/eeprom/at25.c
@@ -64,12 +64,17 @@ static int at25_ee_read(void *priv, unsigned int offset,
{
struct at25_data *at25 = priv;
char *buf = val;
+ size_t max_chunk = spi_max_transfer_size(at25->spi);
+ size_t num_msgs = DIV_ROUND_UP(count, max_chunk);
+ size_t nr_bytes = 0;
u8 command[EE_MAXADDRLEN + 1];
u8 *cp;
ssize_t status;
struct spi_transfer t[2];
struct spi_message m;
u8 instr;
+ unsigned int msg_offset;
+ size_t msg_count;
if (unlikely(offset >= at25->chip.byte_len))
return -EINVAL;
@@ -78,57 +83,64 @@ static int at25_ee_read(void *priv, unsigned int offset,
if (unlikely(!count))
return -EINVAL;
- cp = command;
-
- instr = AT25_READ;
- if (at25->chip.flags & EE_INSTR_BIT3_IS_ADDR)
- if (offset >= (1U << (at25->addrlen * 8)))
- instr |= AT25_INSTR_BIT3;
- *cp++ = instr;
-
- /* 8/16/24-bit address is written MSB first */
- switch (at25->addrlen) {
- default: /* case 3 */
- *cp++ = offset >> 16;
- /* fall through */
- case 2:
- *cp++ = offset >> 8;
- /* fall through */
- case 1:
- case 0: /* can't happen: for better codegen */
- *cp++ = offset >> 0;
- }
+ msg_offset = (unsigned int)offset;
+ msg_count = min(count, max_chunk);
+ while (num_msgs) {
+ cp = command;
- spi_message_init(&m);
- memset(t, 0, sizeof(t));
+ instr = AT25_READ;
+ if (at25->chip.flags & EE_INSTR_BIT3_IS_ADDR)
+ if (msg_offset >= (1U << (at25->addrlen * 8)))
+ instr |= AT25_INSTR_BIT3;
+ *cp++ = instr;
- t[0].tx_buf = command;
- t[0].len = at25->addrlen + 1;
- spi_message_add_tail(&t[0], &m);
+ /* 8/16/24-bit address is written MSB first */
+ switch (at25->addrlen) {
+ default: /* case 3 */
+ *cp++ = msg_offset >> 16;
+ /* fall through */
+ case 2:
+ *cp++ = msg_offset >> 8;
+ /* fall through */
+ case 1:
+ case 0: /* can't happen: for better codegen */
+ *cp++ = msg_offset >> 0;
+ }
- t[1].rx_buf = buf;
- t[1].len = count;
- spi_message_add_tail(&t[1], &m);
+ spi_message_init(&m);
+ memset(t, 0, sizeof(t));
- mutex_lock(&at25->lock);
+ t[0].tx_buf = command;
+ t[0].len = at25->addrlen + 1;
+ spi_message_add_tail(&t[0], &m);
- /* Read it all at once.
- *
- * REVISIT that's potentially a problem with large chips, if
- * other devices on the bus need to be accessed regularly or
- * this chip is clocked very slowly
- */
- status = spi_sync(at25->spi, &m);
- dev_dbg(&at25->spi->dev, "read %zu bytes at %d --> %zd\n",
- count, offset, status);
+ t[1].rx_buf = buf + nr_bytes;
+ t[1].len = msg_count;
+ spi_message_add_tail(&t[1], &m);
- mutex_unlock(&at25->lock);
- return status;
+ mutex_lock(&at25->lock);
+
+ status = spi_sync(at25->spi, &m);
+
+ mutex_unlock(&at25->lock);
+
+ if (status)
+ return status;
+
+ --num_msgs;
+ msg_offset += msg_count;
+ nr_bytes += msg_count;
+ }
+
+ dev_dbg(&at25->spi->dev, "read %zu bytes at %d\n",
+ count, offset);
+ return 0;
}
static int at25_ee_write(void *priv, unsigned int off, void *val, size_t count)
{
struct at25_data *at25 = priv;
+ size_t maxsz = spi_max_transfer_size(at25->spi);
const char *buf = val;
int status = 0;
unsigned buf_size;
@@ -191,6 +203,8 @@ static int at25_ee_write(void *priv, unsigned int off, void *val, size_t count)
segment = buf_size - (offset % buf_size);
if (segment > count)
segment = count;
+ if (segment > maxsz)
+ segment = maxsz;
memcpy(cp, buf, segment);
status = spi_write(at25->spi, bounce,
segment + at25->addrlen + 1);
diff --git a/drivers/misc/mctp-lpc.c b/drivers/misc/mctp-lpc.c
new file mode 100644
index 000000000000..71fc4ae69de7
--- /dev/null
+++ b/drivers/misc/mctp-lpc.c
@@ -0,0 +1,443 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2019, IBM Corp.
+ */
+
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/mfd/syscon.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/poll.h>
+#include <linux/regmap.h>
+#include <linux/sched/signal.h>
+#include <linux/uaccess.h>
+#include <linux/wait.h>
+
+#define LPC_HICRB 0x080
+#define LPC_HICRB_IBFIF4 BIT(1)
+#define LPC_HICRB_LPC4E BIT(0)
+#define LPC_HICRC 0x084
+#define LPC_KCS4_IRQSEL_MASK GENMASK(7, 4)
+#define LPC_KCS4_IRQSEL_SHIFT 4
+#define LPC_KCS4_IRQTYPE_MASK GENMASK(3, 2)
+#define LPC_KCS4_IRQTYPE_SHIFT 2
+#define LPC_KCS4_IRQTYPE_LOW 0b00
+#define LPC_KCS4_IRQTYPE_HIGH 0b01
+#define LPC_KCS4_IRQTYPE_RSVD 0b10
+#define LPC_KCS4_IRQTYPE_RISING 0b11
+#define LPC_KCS4_OBF4_AUTO_CLR BIT(1)
+#define LPC_KCS4_IRQ_HOST BIT(0)
+#define LPC_LADR4 0x090
+#define LPC_IDR4 0x094
+#define LPC_ODR4 0x098
+#define LPC_STR4 0x09C
+#define STR4_IBF (1 << 1)
+#define STR4_OBF (1 << 0)
+
+#define HOST_ODR 0xca2
+#define HOST_STR 0xca3
+#define HOST_SERIRQ_ID 11
+#define HOST_SERIRQ_TYPE LPC_KCS4_IRQTYPE_LOW
+
+#define RX_BUF_SIZE 1024
+
+struct mctp_lpc {
+ struct miscdevice miscdev;
+ struct regmap *map;
+
+ wait_queue_head_t rx;
+ bool pending;
+ u8 idr;
+};
+
+static irqreturn_t mctp_lpc_irq(int irq, void *data)
+{
+ struct mctp_lpc *priv = data;
+ unsigned long flags;
+ unsigned int hicrb;
+ struct device *dev;
+ unsigned int str;
+ irqreturn_t ret;
+
+ dev = priv->miscdev.this_device;
+
+ spin_lock_irqsave(&priv->rx.lock, flags);
+
+ regmap_read(priv->map, LPC_STR4, &str);
+ regmap_read(priv->map, LPC_HICRB, &hicrb);
+
+ if ((str & STR4_IBF) && (hicrb & LPC_HICRB_IBFIF4)) {
+ unsigned int val;
+
+ if (priv->pending)
+ dev_err(dev, "Storm brewing!");
+
+ /* Mask the IRQ / Enter polling mode */
+ dev_dbg(dev, "Received IRQ %d, disabling to provide back-pressure\n",
+ irq);
+ regmap_update_bits(priv->map, LPC_HICRB, LPC_HICRB_IBFIF4, 0);
+
+ /*
+ * Extract the IDR4 value to ack the IRQ. Reading IDR clears
+ * IBF and allows the host to write another value, however as
+ * we have disabled IRQs the back-pressure is still applied
+ * until userspace starts servicing the interface.
+ */
+ regmap_read(priv->map, LPC_IDR4, &val);
+ priv->idr = val & 0xff;
+ priv->pending = true;
+
+ dev_dbg(dev, "Set pending, waking waiters\n");
+ wake_up_locked(&priv->rx);
+ ret = IRQ_HANDLED;
+ } else {
+ dev_dbg(dev, "LPC IRQ triggered, but not for us (str=0x%x, hicrb=0x%x)\n",
+ str, hicrb);
+ ret = IRQ_NONE;
+ }
+
+ spin_unlock_irqrestore(&priv->rx.lock, flags);
+
+ return ret;
+}
+
+static inline struct mctp_lpc *to_mctp_lpc(struct file *filp)
+{
+ return container_of(filp->private_data, struct mctp_lpc, miscdev);
+}
+
+static ssize_t mctp_lpc_read(struct file *filp, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct mctp_lpc *priv;
+ struct device *dev;
+ size_t remaining;
+ ssize_t rc;
+
+ priv = to_mctp_lpc(filp);
+ dev = priv->miscdev.this_device;
+
+ if (!count)
+ return 0;
+
+ if (count > 2 || *ppos > 1)
+ return -EINVAL;
+
+ remaining = count;
+
+ spin_lock_irq(&priv->rx.lock);
+ if (*ppos == 0) {
+ unsigned int val;
+ u8 str;
+
+ /* YOLO blocking, non-block not supported */
+ dev_dbg(dev, "Waiting for IBF\n");
+ regmap_read(priv->map, LPC_STR4, &val);
+ str = val & 0xff;
+ rc = wait_event_interruptible_locked(priv->rx, (priv->pending || str & STR4_IBF));
+ if (rc < 0)
+ goto out;
+
+ if (signal_pending(current)) {
+ dev_dbg(dev, "Interrupted waiting for IBF\n");
+ rc = -EINTR;
+ goto out;
+ }
+
+ /*
+ * Re-enable IRQs 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");
+ regmap_update_bits(priv->map, LPC_HICRB, LPC_HICRB_IBFIF4,
+ LPC_HICRB_IBFIF4);
+
+ /* Read data out of IDR into internal storage if necessary */
+ if (!priv->pending) {
+ WARN(!(str & STR4_IBF), "Unknown reason for wakeup!");
+
+ /* Extract the IDR4 value to ack the IRQ */
+ regmap_read(priv->map, LPC_IDR4, &val);
+ priv->idr = val & 0xff;
+ }
+
+ /* Copy data from internal storage to userspace */
+ if (copy_to_user(buf, &priv->idr, sizeof(priv->idr))) {
+ rc = -EFAULT;
+ goto out;
+ }
+
+ /* We're done consuming the internally stored value */
+ priv->pending = false;
+
+ remaining--;
+ buf++;
+ }
+
+ if (remaining) {
+ /* Either:
+ *
+ * 1. (count == 1 && *ppos == 1)
+ * 2. (count == 2 && *ppos == 0)
+ */
+ unsigned int val;
+ u8 str;
+
+ regmap_read(priv->map, LPC_STR4, &val);
+ str = val & 0xff;
+ if (*ppos == 0 || priv->pending)
+ /*
+ * 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 |= STR4_IBF;
+
+ dev_dbg(dev, "Read status 0x%x\n", str);
+ if (copy_to_user(buf, &str, sizeof(str))) {
+ rc = -EFAULT;
+ goto out;
+ }
+
+ remaining--;
+ }
+
+ WARN_ON(remaining);
+
+ rc = count;
+
+out:
+ spin_unlock_irq(&priv->rx.lock);
+
+ return rc;
+}
+
+static ssize_t mctp_lpc_write(struct file *filp, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ uint8_t _data[2], *data = &_data[0];
+ struct mctp_lpc *priv;
+ struct device *dev;
+ size_t remaining;
+ unsigned int str;
+
+ priv = to_mctp_lpc(filp);
+ 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;
+
+ remaining = count;
+
+ if (*ppos == 0) {
+ /* Wait until OBF is clear - we don't get an IRQ */
+ dev_dbg(dev, "Waiting for OBF to clear\n");
+ for (;;) {
+ if (signal_pending(current))
+ return -EINTR;
+
+ regmap_read(priv->map, LPC_STR4, &str);
+ if (!(str & STR4_OBF))
+ break;
+
+ msleep(1);
+ }
+
+ dev_dbg(dev, "Writing 0x%x to ODR\n", *data);
+ regmap_write(priv->map, LPC_ODR4, *data);
+ remaining--;
+ data++;
+ }
+
+ if (remaining) {
+ if (!(*data & STR4_OBF))
+ dev_err(dev, "Clearing OBF with status write: 0x%x\n",
+ *data);
+ dev_dbg(dev, "Writing status 0x%x\n", *data);
+ regmap_write(priv->map, LPC_STR4, *data);
+ remaining--;
+ }
+
+ WARN_ON(remaining);
+
+ regmap_read(priv->map, LPC_STR4, &str);
+ dev_dbg(dev, "Triggering SerIRQ (current str=0x%x)\n", str);
+
+ /*
+ * Trigger Host IRQ on ODR write. Do this after any STR write in case
+ * we need to write ODR to indicate an STR update (which we do).
+ */
+ if (*ppos == 0)
+ regmap_update_bits(priv->map, LPC_HICRC, LPC_KCS4_IRQ_HOST,
+ LPC_KCS4_IRQ_HOST);
+
+ return count;
+}
+
+static __poll_t mctp_lpc_poll(struct file *filp, poll_table *wait)
+{
+ struct mctp_lpc *priv;
+ struct device *dev;
+ unsigned int val;
+ bool ibf;
+
+ priv = to_mctp_lpc(filp);
+ dev = priv->miscdev.this_device;
+
+ regmap_read(priv->map, LPC_STR4, &val);
+
+ spin_lock_irq(&priv->rx.lock);
+
+ ibf = priv->pending || val & STR4_IBF;
+
+ if (!ibf) {
+ dev_dbg(dev, "Polling on IBF\n");
+
+ spin_unlock_irq(&priv->rx.lock);
+
+ poll_wait(filp, &priv->rx, wait);
+ if (signal_pending(current)) {
+ dev_dbg(dev, "Polling IBF was interrupted\n");
+ goto out;
+ }
+
+ spin_lock_irq(&priv->rx.lock);
+
+ regmap_read(priv->map, LPC_STR4, &val);
+
+ ibf = priv->pending || val & STR4_IBF;
+ }
+
+ spin_unlock_irq(&priv->rx.lock);
+
+out:
+ dev_dbg(dev, "Polled IBF state: %s\n", ibf ? "set" : "clear");
+
+ return ibf ? EPOLLIN : 0;
+}
+
+static const struct file_operations mctp_lpc_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_seek_end_llseek,
+ .read = mctp_lpc_read,
+ .write = mctp_lpc_write,
+ .poll = mctp_lpc_poll,
+};
+
+static int mctp_lpc_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ unsigned int mask, val;
+ struct mctp_lpc *priv;
+ int irq;
+ int rc;
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->map = syscon_node_to_regmap(dev->parent->of_node);
+ if (IS_ERR(priv->map)) {
+ dev_err(dev, "Couldn't get regmap\n");
+ return -ENODEV;
+ }
+
+ /*
+ * Set the LPC address. Simultaneously, test our MMIO regmap works. All
+ * subsequent accesses are assumed to work
+ */
+ rc = regmap_write(priv->map, LPC_LADR4, ((HOST_STR) << 16) | HOST_ODR);
+ if (rc < 0)
+ return rc;
+
+ /* Set up the SerIRQ */
+ mask = LPC_KCS4_IRQSEL_MASK
+ | LPC_KCS4_IRQTYPE_MASK
+ | LPC_KCS4_OBF4_AUTO_CLR;
+ val = (HOST_SERIRQ_ID << LPC_KCS4_IRQSEL_SHIFT)
+ | (HOST_SERIRQ_TYPE << LPC_KCS4_IRQTYPE_SHIFT);
+ val &= ~LPC_KCS4_OBF4_AUTO_CLR; /* Unnecessary, just documentation */
+ regmap_update_bits(priv->map, LPC_HICRC, mask, val);
+
+ /* Trigger waiters from IRQ */
+ init_waitqueue_head(&priv->rx);
+
+ dev_set_drvdata(dev, priv);
+
+ /* Set up the miscdevice */
+ priv->miscdev.minor = MISC_DYNAMIC_MINOR;
+ priv->miscdev.name = "mctp0";
+ priv->miscdev.fops = &mctp_lpc_fops;
+
+ /* Configure the IRQ handler */
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+
+ rc = devm_request_irq(dev, irq, mctp_lpc_irq, IRQF_SHARED,
+ dev_name(dev), priv);
+ if (rc < 0)
+ return rc;
+
+ /* Register the device */
+ rc = misc_register(&priv->miscdev);
+ if (rc) {
+ dev_err(dev, "Unable to register device\n");
+ return rc;
+ }
+
+ /* Enable the channel */
+ regmap_update_bits(priv->map, LPC_HICRB,
+ LPC_HICRB_IBFIF4 | LPC_HICRB_LPC4E,
+ LPC_HICRB_IBFIF4 | LPC_HICRB_LPC4E);
+
+ return 0;
+}
+
+static int mctp_lpc_remove(struct platform_device *pdev)
+{
+ struct mctp_lpc *ctx = dev_get_drvdata(&pdev->dev);
+
+ misc_deregister(&ctx->miscdev);
+
+ return 0;
+}
+
+static const struct of_device_id mctp_lpc_match[] = {
+ { .compatible = "openbmc,mctp-lpc" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, mctp_lpc_match);
+
+static struct platform_driver mctp_lpc = {
+ .driver = {
+ .name = "mctp-lpc",
+ .of_match_table = mctp_lpc_match,
+ },
+ .probe = mctp_lpc_probe,
+ .remove = mctp_lpc_remove,
+};
+module_platform_driver(mctp_lpc);
+
+MODULE_LICENSE("GPL v2+");
+MODULE_AUTHOR("Andrew Jeffery <andrew@aj.id.au>");
+MODULE_DESCRIPTION("OpenBMC MCTP LPC binding on ASPEED KCS");
diff --git a/drivers/misc/npcm7xx-lpc-bpc.c b/drivers/misc/npcm7xx-lpc-bpc.c
new file mode 100644
index 000000000000..e014e07cd4a4
--- /dev/null
+++ b/drivers/misc/npcm7xx-lpc-bpc.c
@@ -0,0 +1,394 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2014-2018 Nuvoton Technology corporation.
+
+#include <linux/fs.h>
+#include <linux/bitops.h>
+#include <linux/interrupt.h>
+#include <linux/kfifo.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/miscdevice.h>
+#include <linux/poll.h>
+
+#define DEVICE_NAME "npcm7xx-lpc-bpc"
+
+#define NUM_BPC_CHANNELS 2
+#define DW_PAD_SIZE 3
+
+/* BIOS POST Code FIFO Registers */
+#define NPCM7XX_BPCFA2L_REG 0x2 //BIOS POST Code FIFO Address 2 LSB
+#define NPCM7XX_BPCFA2M_REG 0x4 //BIOS POST Code FIFO Address 2 MSB
+#define NPCM7XX_BPCFEN_REG 0x6 //BIOS POST Code FIFO Enable
+#define NPCM7XX_BPCFSTAT_REG 0x8 //BIOS POST Code FIFO Status
+#define NPCM7XX_BPCFDATA_REG 0xA //BIOS POST Code FIFO Data
+#define NPCM7XX_BPCFMSTAT_REG 0xC //BIOS POST Code FIFO Miscellaneous Status
+#define NPCM7XX_BPCFA1L_REG 0x10 //BIOS POST Code FIFO Address 1 LSB
+#define NPCM7XX_BPCFA1M_REG 0x12 //BIOS POST Code FIFO Address 1 MSB
+
+/*BIOS regiser data*/
+#define FIFO_IOADDR1_ENABLE 0x80
+#define FIFO_IOADDR2_ENABLE 0x40
+
+/* BPC interface package and structure definition */
+#define BPC_KFIFO_SIZE 0x400
+
+/*BPC regiser data*/
+#define FIFO_DATA_VALID 0x80
+#define FIFO_OVERFLOW 0x20
+#define FIFO_READY_INT_ENABLE 0x8
+#define FIFO_DWCAPTURE 0x4
+#define FIFO_ADDR_DECODE 0x1
+
+/*Host Reset*/
+#define HOST_RESET_INT_ENABLE 0x10
+#define HOST_RESET_CHANGED 0x40
+
+struct npcm7xx_bpc_channel {
+ struct npcm7xx_bpc *data;
+ struct kfifo fifo;
+ wait_queue_head_t wq;
+ bool host_reset;
+ struct miscdevice miscdev;
+};
+
+struct npcm7xx_bpc {
+ void __iomem *base;
+ int irq;
+ bool en_dwcap;
+ struct npcm7xx_bpc_channel ch[NUM_BPC_CHANNELS];
+};
+
+static struct npcm7xx_bpc_channel *npcm7xx_file_to_ch(struct file *file)
+{
+ return container_of(file->private_data, struct npcm7xx_bpc_channel,
+ miscdev);
+}
+
+static ssize_t npcm7xx_bpc_read(struct file *file, char __user *buffer,
+ size_t count, loff_t *ppos)
+{
+ struct npcm7xx_bpc_channel *chan = npcm7xx_file_to_ch(file);
+ struct npcm7xx_bpc *lpc_bpc = chan->data;
+ unsigned int copied;
+ int ret = 0;
+ int cond_size = 1;
+
+ if (lpc_bpc->en_dwcap)
+ cond_size = 3;
+
+ if (kfifo_len(&chan->fifo) < cond_size) {
+ if (file->f_flags & O_NONBLOCK)
+ return -EAGAIN;
+
+ ret = wait_event_interruptible
+ (chan->wq, kfifo_len(&chan->fifo) > cond_size);
+ if (ret == -ERESTARTSYS)
+ return -EINTR;
+ }
+
+ ret = kfifo_to_user(&chan->fifo, buffer, count, &copied);
+
+ return ret ? ret : copied;
+}
+
+static __poll_t npcm7xx_bpc_poll(struct file *file,
+ struct poll_table_struct *pt)
+{
+ struct npcm7xx_bpc_channel *chan = npcm7xx_file_to_ch(file);
+ __poll_t mask = 0;
+
+ poll_wait(file, &chan->wq, pt);
+ if (!kfifo_is_empty(&chan->fifo))
+ mask |= POLLIN;
+
+ if (chan->host_reset) {
+ mask |= POLLHUP;
+ chan->host_reset = false;
+ }
+
+ return mask;
+}
+
+static const struct file_operations npcm7xx_bpc_fops = {
+ .owner = THIS_MODULE,
+ .read = npcm7xx_bpc_read,
+ .poll = npcm7xx_bpc_poll,
+ .llseek = noop_llseek,
+};
+
+static irqreturn_t npcm7xx_bpc_irq(int irq, void *arg)
+{
+ struct npcm7xx_bpc *lpc_bpc = arg;
+ u8 fifo_st;
+ u8 host_st;
+ u8 addr_index = 0;
+ u8 Data;
+ u8 padzero[3] = {0};
+ u8 last_addr_bit = 0;
+ bool isr_flag = false;
+
+ fifo_st = ioread8(lpc_bpc->base + NPCM7XX_BPCFSTAT_REG);
+ while (FIFO_DATA_VALID & fifo_st) {
+ /* If dwcapture enabled only channel 0 (FIFO 0) used */
+ if (!lpc_bpc->en_dwcap)
+ addr_index = fifo_st & FIFO_ADDR_DECODE;
+ else
+ last_addr_bit = fifo_st & FIFO_ADDR_DECODE;
+
+ /*Read data from FIFO to clear interrupt*/
+ Data = ioread8(lpc_bpc->base + NPCM7XX_BPCFDATA_REG);
+ if (kfifo_is_full(&lpc_bpc->ch[addr_index].fifo))
+ kfifo_skip(&lpc_bpc->ch[addr_index].fifo);
+ kfifo_put(&lpc_bpc->ch[addr_index].fifo, Data);
+ if (fifo_st & FIFO_OVERFLOW)
+ pr_info("BIOS Post Codes FIFO Overflow!!!\n");
+
+ fifo_st = ioread8(lpc_bpc->base + NPCM7XX_BPCFSTAT_REG);
+ if (lpc_bpc->en_dwcap && last_addr_bit) {
+ if ((fifo_st & FIFO_ADDR_DECODE) ||
+ ((FIFO_DATA_VALID & fifo_st) == 0)) {
+ while (kfifo_avail(&lpc_bpc->ch[addr_index].fifo) < DW_PAD_SIZE)
+ kfifo_skip(&lpc_bpc->ch[addr_index].fifo);
+ kfifo_in(&lpc_bpc->ch[addr_index].fifo,
+ padzero, DW_PAD_SIZE);
+ }
+ }
+ isr_flag = true;
+ }
+
+ host_st = ioread8(lpc_bpc->base + NPCM7XX_BPCFMSTAT_REG);
+ if (host_st & HOST_RESET_CHANGED) {
+ iowrite8(HOST_RESET_CHANGED,
+ lpc_bpc->base + NPCM7XX_BPCFMSTAT_REG);
+ lpc_bpc->ch[addr_index].host_reset = true;
+ isr_flag = true;
+ }
+
+ if (isr_flag) {
+ wake_up_interruptible(&lpc_bpc->ch[addr_index].wq);
+ return IRQ_HANDLED;
+ }
+
+ return IRQ_NONE;
+}
+
+static int npcm7xx_bpc_config_irq(struct npcm7xx_bpc *lpc_bpc,
+ struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ int rc;
+
+ lpc_bpc->irq = platform_get_irq(pdev, 0);
+ if (lpc_bpc->irq < 0) {
+ dev_err(dev, "get IRQ failed\n");
+ return lpc_bpc->irq;
+ }
+
+ rc = devm_request_irq(dev, lpc_bpc->irq,
+ npcm7xx_bpc_irq, IRQF_SHARED,
+ DEVICE_NAME, lpc_bpc);
+ if (rc < 0) {
+ dev_warn(dev, "Unable to request IRQ %d\n", lpc_bpc->irq);
+ return rc;
+ }
+
+ return 0;
+}
+
+static int npcm7xx_enable_bpc(struct npcm7xx_bpc *lpc_bpc, struct device *dev,
+ int channel, u16 lpc_port)
+{
+ int rc;
+ u8 addr_en, reg_en;
+
+ init_waitqueue_head(&lpc_bpc->ch[channel].wq);
+
+ rc = kfifo_alloc(&lpc_bpc->ch[channel].fifo,
+ BPC_KFIFO_SIZE, GFP_KERNEL);
+ if (rc)
+ return rc;
+
+ lpc_bpc->ch[channel].miscdev.minor = MISC_DYNAMIC_MINOR;
+ lpc_bpc->ch[channel].miscdev.name =
+ devm_kasprintf(dev, GFP_KERNEL, "%s%d", DEVICE_NAME, channel);
+ lpc_bpc->ch[channel].miscdev.fops = &npcm7xx_bpc_fops;
+ lpc_bpc->ch[channel].miscdev.parent = dev;
+ rc = misc_register(&lpc_bpc->ch[channel].miscdev);
+ if (rc)
+ return rc;
+
+ lpc_bpc->ch[channel].data = lpc_bpc;
+ lpc_bpc->ch[channel].host_reset = false;
+
+ /* Enable LPC snoop channel at requested port */
+ switch (channel) {
+ case 0:
+ addr_en = FIFO_IOADDR1_ENABLE;
+ iowrite8((u8)lpc_port & 0xFF,
+ lpc_bpc->base + NPCM7XX_BPCFA1L_REG);
+ iowrite8((u8)(lpc_port >> 8),
+ lpc_bpc->base + NPCM7XX_BPCFA1M_REG);
+ break;
+ case 1:
+ addr_en = FIFO_IOADDR2_ENABLE;
+ iowrite8((u8)lpc_port & 0xFF,
+ lpc_bpc->base + NPCM7XX_BPCFA2L_REG);
+ iowrite8((u8)(lpc_port >> 8),
+ lpc_bpc->base + NPCM7XX_BPCFA2M_REG);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (lpc_bpc->en_dwcap)
+ addr_en = FIFO_DWCAPTURE;
+
+ /*
+ * Enable FIFO Ready Interrupt, FIFO Capture of I/O addr,
+ * and Host Reset
+ */
+ reg_en = ioread8(lpc_bpc->base + NPCM7XX_BPCFEN_REG);
+ iowrite8(reg_en | addr_en | FIFO_READY_INT_ENABLE |
+ HOST_RESET_INT_ENABLE, lpc_bpc->base + NPCM7XX_BPCFEN_REG);
+
+ return 0;
+}
+
+static void npcm7xx_disable_bpc(struct npcm7xx_bpc *lpc_bpc, int channel)
+{
+ u8 reg_en;
+
+ switch (channel) {
+ case 0:
+ reg_en = ioread8(lpc_bpc->base + NPCM7XX_BPCFEN_REG);
+ if (lpc_bpc->en_dwcap)
+ iowrite8(reg_en & ~FIFO_DWCAPTURE,
+ lpc_bpc->base + NPCM7XX_BPCFEN_REG);
+ else
+ iowrite8(reg_en & ~FIFO_IOADDR1_ENABLE,
+ lpc_bpc->base + NPCM7XX_BPCFEN_REG);
+ break;
+ case 1:
+ reg_en = ioread8(lpc_bpc->base + NPCM7XX_BPCFEN_REG);
+ iowrite8(reg_en & ~FIFO_IOADDR2_ENABLE,
+ lpc_bpc->base + NPCM7XX_BPCFEN_REG);
+ break;
+ default:
+ return;
+ }
+
+ if (!(reg_en & (FIFO_IOADDR1_ENABLE | FIFO_IOADDR2_ENABLE)))
+ iowrite8(reg_en &
+ ~(FIFO_READY_INT_ENABLE | HOST_RESET_INT_ENABLE),
+ lpc_bpc->base + NPCM7XX_BPCFEN_REG);
+
+ kfifo_free(&lpc_bpc->ch[channel].fifo);
+ misc_deregister(&lpc_bpc->ch[channel].miscdev);
+}
+
+static int npcm7xx_bpc_probe(struct platform_device *pdev)
+{
+ struct npcm7xx_bpc *lpc_bpc;
+ struct resource *res;
+ struct device *dev;
+ u32 port;
+ int rc;
+
+ dev = &pdev->dev;
+
+ lpc_bpc = devm_kzalloc(dev, sizeof(*lpc_bpc), GFP_KERNEL);
+ if (!lpc_bpc)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(dev, "BIOS post code reg resource not found\n");
+ return -ENODEV;
+ }
+
+ dev_dbg(dev, "BIOS post code base resource is %pR\n", res);
+ lpc_bpc->base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(lpc_bpc->base))
+ return PTR_ERR(lpc_bpc->base);
+
+ dev_set_drvdata(&pdev->dev, lpc_bpc);
+
+ rc = of_property_read_u32_index(dev->of_node, "monitor-ports", 0,
+ &port);
+ if (rc) {
+ dev_err(dev, "no monitor ports configured\n");
+ return -ENODEV;
+ }
+
+ lpc_bpc->en_dwcap =
+ of_property_read_bool(dev->of_node, "bpc-en-dwcapture");
+
+ rc = npcm7xx_bpc_config_irq(lpc_bpc, pdev);
+ if (rc)
+ return rc;
+
+ rc = npcm7xx_enable_bpc(lpc_bpc, dev, 0, port);
+ if (rc) {
+ dev_err(dev, "Enable BIOS post code I/O port 0 failed\n");
+ return rc;
+ }
+
+ /*
+ * Configuration of second BPC channel port is optional
+ * Double-Word Capture ignoring address 2
+ */
+ if (!lpc_bpc->en_dwcap) {
+ if (of_property_read_u32_index(dev->of_node, "monitor-ports",
+ 1, &port) == 0) {
+ rc = npcm7xx_enable_bpc(lpc_bpc, dev, 1, port);
+ if (rc) {
+ dev_err(dev, "Enable BIOS post code I/O port 1 failed, disable I/O port 0\n");
+ npcm7xx_disable_bpc(lpc_bpc, 0);
+ return rc;
+ }
+ }
+ }
+
+ pr_info("npcm7xx BIOS post code probe\n");
+
+ return rc;
+}
+
+static int npcm7xx_bpc_remove(struct platform_device *pdev)
+{
+ struct npcm7xx_bpc *lpc_bpc = dev_get_drvdata(&pdev->dev);
+ u8 reg_en;
+
+ reg_en = ioread8(lpc_bpc->base + NPCM7XX_BPCFEN_REG);
+
+ if (reg_en & FIFO_IOADDR1_ENABLE)
+ npcm7xx_disable_bpc(lpc_bpc, 0);
+ if (reg_en & FIFO_IOADDR2_ENABLE)
+ npcm7xx_disable_bpc(lpc_bpc, 1);
+
+ return 0;
+}
+
+static const struct of_device_id npcm7xx_bpc_match[] = {
+ { .compatible = "nuvoton,npcm750-lpc-bpc" },
+ { },
+};
+
+static struct platform_driver npcm7xx_bpc_driver = {
+ .driver = {
+ .name = DEVICE_NAME,
+ .of_match_table = npcm7xx_bpc_match,
+ },
+ .probe = npcm7xx_bpc_probe,
+ .remove = npcm7xx_bpc_remove,
+};
+
+module_platform_driver(npcm7xx_bpc_driver);
+
+MODULE_DEVICE_TABLE(of, npcm7xx_bpc_match);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Tomer Maimon <tomer.maimon@nuvoton.com>");
+MODULE_DESCRIPTION("Linux driver to control NPCM7XX LPC BIOS post code monitoring");
diff --git a/drivers/misc/npcm7xx-pci-mbox.c b/drivers/misc/npcm7xx-pci-mbox.c
new file mode 100644
index 000000000000..1a80661a4296
--- /dev/null
+++ b/drivers/misc/npcm7xx-pci-mbox.c
@@ -0,0 +1,288 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2014-2018 Nuvoton Technology corporation.
+
+#include <linux/interrupt.h>
+#include <linux/mfd/syscon.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+#include <linux/poll.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+#define DEVICE_NAME "npcm7xx-pci-mbox"
+
+#define NPCM7XX_MBOX_BMBXSTAT 0x0
+#define NPCM7XX_MBOX_BMBXCTL 0x4
+#define NPCM7XX_MBOX_BMBXCMD 0x8
+
+#define NPCM7XX_MBOX_CIF_0 BIT(0)
+#define NPCM7XX_MBOX_CIE_0 BIT(0)
+#define NPCM7XX_MBOX_HIF_0 BIT(0)
+
+#define NPCM7XX_MBOX_ALL_CIF GENMASK(7, 0)
+#define NPCM7XX_MBOX_ALL_CIE GENMASK(7, 0)
+#define NPCM7XX_MBOX_ALL_HIF GENMASK(7, 0)
+
+struct npcm7xx_mbox {
+ struct miscdevice miscdev;
+ struct regmap *regmap;
+ void __iomem *memory;
+ wait_queue_head_t queue;
+ spinlock_t lock; /* mbox access mutex */
+ bool cif0;
+ u32 max_buf_size;
+};
+
+static atomic_t npcm7xx_mbox_open_count = ATOMIC_INIT(0);
+
+static struct npcm7xx_mbox *file_mbox(struct file *file)
+{
+ return container_of(file->private_data, struct npcm7xx_mbox, miscdev);
+}
+
+static int npcm7xx_mbox_open(struct inode *inode, struct file *file)
+{
+ struct npcm7xx_mbox *mbox = file_mbox(file);
+
+ if (atomic_inc_return(&npcm7xx_mbox_open_count) == 1) {
+ /* enable mailbox interrupt */
+ regmap_update_bits(mbox->regmap, NPCM7XX_MBOX_BMBXCTL,
+ NPCM7XX_MBOX_ALL_CIE, NPCM7XX_MBOX_CIE_0);
+ return 0;
+ }
+
+ atomic_dec(&npcm7xx_mbox_open_count);
+ return -EBUSY;
+}
+
+static ssize_t npcm7xx_mbox_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct npcm7xx_mbox *mbox = file_mbox(file);
+ unsigned long flags;
+
+ if (!access_ok(buf, count))
+ return -EFAULT;
+
+ if ((*ppos + count) > mbox->max_buf_size)
+ return -EINVAL;
+
+ if (file->f_flags & O_NONBLOCK) {
+ if (!mbox->cif0)
+ return -EAGAIN;
+ } else if (wait_event_interruptible(mbox->queue, mbox->cif0)) {
+ return -ERESTARTSYS;
+ }
+
+ spin_lock_irqsave(&mbox->lock, flags);
+
+ if (copy_to_user((void __user *)buf,
+ (const void *)(mbox->memory + *ppos), count)) {
+ spin_unlock_irqrestore(&mbox->lock, flags);
+ return -EFAULT;
+ }
+
+ mbox->cif0 = false;
+ spin_unlock_irqrestore(&mbox->lock, flags);
+ return count;
+}
+
+static ssize_t npcm7xx_mbox_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct npcm7xx_mbox *mbox = file_mbox(file);
+ unsigned long flags;
+
+ if (!access_ok(buf, count))
+ return -EFAULT;
+
+ if ((*ppos + count) > mbox->max_buf_size)
+ return -EINVAL;
+
+ spin_lock_irqsave(&mbox->lock, flags);
+
+ if (copy_from_user((void *)(mbox->memory + *ppos),
+ (void __user *)buf, count)) {
+ spin_unlock_irqrestore(&mbox->lock, flags);
+ return -EFAULT;
+ }
+
+ regmap_update_bits(mbox->regmap, NPCM7XX_MBOX_BMBXCMD,
+ NPCM7XX_MBOX_ALL_HIF, NPCM7XX_MBOX_HIF_0);
+
+ spin_unlock_irqrestore(&mbox->lock, flags);
+ return count;
+}
+
+static unsigned int npcm7xx_mbox_poll(struct file *file, poll_table *wait)
+{
+ struct npcm7xx_mbox *mbox = file_mbox(file);
+ unsigned int mask = 0;
+
+ poll_wait(file, &mbox->queue, wait);
+ if (mbox->cif0)
+ mask |= POLLIN;
+
+ return mask;
+}
+
+static int npcm7xx_mbox_release(struct inode *inode, struct file *file)
+{
+ atomic_dec(&npcm7xx_mbox_open_count);
+ return 0;
+}
+
+static const struct file_operations npcm7xx_mbox_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_seek_end_llseek,
+ .read = npcm7xx_mbox_read,
+ .write = npcm7xx_mbox_write,
+ .open = npcm7xx_mbox_open,
+ .release = npcm7xx_mbox_release,
+ .poll = npcm7xx_mbox_poll,
+};
+
+static irqreturn_t npcm7xx_mbox_irq(int irq, void *arg)
+{
+ struct npcm7xx_mbox *mbox = arg;
+ u32 val;
+
+ regmap_read(mbox->regmap, NPCM7XX_MBOX_BMBXSTAT, &val);
+ if ((val & NPCM7XX_MBOX_CIF_0) != NPCM7XX_MBOX_CIF_0)
+ return IRQ_NONE;
+
+ /*
+ * Leave the status bit set so that we know the data is for us,
+ * clear it once it has been read.
+ */
+ mbox->cif0 = true;
+
+ /* Mask it off, we'll clear it when we the data gets read */
+ regmap_write_bits(mbox->regmap, NPCM7XX_MBOX_BMBXSTAT,
+ NPCM7XX_MBOX_ALL_CIF, NPCM7XX_MBOX_CIF_0);
+
+ wake_up(&mbox->queue);
+
+ return IRQ_HANDLED;
+}
+
+static int npcm7xx_mbox_config_irq(struct npcm7xx_mbox *mbox,
+ struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ int rc, irq;
+ u32 val;
+
+ /* Disable all register based interrupts */
+ regmap_update_bits(mbox->regmap, NPCM7XX_MBOX_BMBXCTL,
+ NPCM7XX_MBOX_ALL_CIE, 0);
+/*
+ * These registers are write one to clear. Clear them.
+ * Per spec, cleared bits should not be re-cleared.
+ * Need to read and clear needed bits only, instead of blindly clearing all.
+ */
+ regmap_read(mbox->regmap, NPCM7XX_MBOX_BMBXSTAT, &val);
+ val &= NPCM7XX_MBOX_ALL_CIF;
+
+ /* If any bit is set, write back to clear */
+ if (val)
+ regmap_write_bits(mbox->regmap, NPCM7XX_MBOX_BMBXSTAT,
+ NPCM7XX_MBOX_ALL_CIF, val);
+
+ irq = irq_of_parse_and_map(dev->of_node, 0);
+ if (!irq)
+ return -ENODEV;
+
+ rc = devm_request_irq(dev, irq, npcm7xx_mbox_irq, 0, DEVICE_NAME, mbox);
+ if (rc < 0) {
+ dev_err(dev, "Unable to request IRQ %d\n", irq);
+ return rc;
+ }
+
+ return 0;
+}
+
+static int npcm7xx_mbox_probe(struct platform_device *pdev)
+{
+ struct npcm7xx_mbox *mbox;
+ struct device *dev;
+ struct resource *res;
+ int rc;
+
+ dev = &pdev->dev;
+
+ mbox = devm_kzalloc(dev, sizeof(*mbox), GFP_KERNEL);
+ if (!mbox)
+ return -ENOMEM;
+
+ dev_set_drvdata(&pdev->dev, mbox);
+
+ mbox->regmap = syscon_node_to_regmap(dev->of_node);
+ if (IS_ERR(mbox->regmap)) {
+ dev_err(dev, "Couldn't get regmap\n");
+ return -ENODEV;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ mbox->memory = devm_ioremap_resource(dev, res);
+ if (IS_ERR(mbox->memory))
+ return PTR_ERR(mbox->memory);
+ mbox->max_buf_size = resource_size(res);
+
+ spin_lock_init(&mbox->lock);
+ init_waitqueue_head(&mbox->queue);
+
+ mbox->miscdev.minor = MISC_DYNAMIC_MINOR;
+ mbox->miscdev.name = DEVICE_NAME;
+ mbox->miscdev.fops = &npcm7xx_mbox_fops;
+ mbox->miscdev.parent = dev;
+ mbox->cif0 = false;
+ rc = misc_register(&mbox->miscdev);
+ if (rc) {
+ dev_err(dev, "Unable to register device\n");
+ return rc;
+ }
+
+ rc = npcm7xx_mbox_config_irq(mbox, pdev);
+ if (rc) {
+ dev_err(dev, "Failed to configure IRQ\n");
+ misc_deregister(&mbox->miscdev);
+ return rc;
+ }
+
+ pr_info("NPCM7xx PCI Mailbox probed\n");
+
+ return 0;
+}
+
+static int npcm7xx_mbox_remove(struct platform_device *pdev)
+{
+ struct npcm7xx_mbox *mbox = dev_get_drvdata(&pdev->dev);
+
+ misc_deregister(&mbox->miscdev);
+
+ return 0;
+}
+
+static const struct of_device_id npcm7xx_mbox_match[] = {
+ { .compatible = "nuvoton,npcm750-pci-mbox" },
+ { },
+};
+
+static struct platform_driver npcm7xx_mbox_driver = {
+ .driver = {
+ .name = DEVICE_NAME,
+ .of_match_table = npcm7xx_mbox_match,
+ },
+ .probe = npcm7xx_mbox_probe,
+ .remove = npcm7xx_mbox_remove,
+};
+
+module_platform_driver(npcm7xx_mbox_driver);
+
+MODULE_DEVICE_TABLE(of, npcm7xx_mbox_match);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Tomer Maimon <tomer.maimon@nuvoton.com>");
+MODULE_DESCRIPTION("NPCM7XX mailbox device driver");
diff --git a/drivers/mtd/spi-nor/controllers/aspeed-smc.c b/drivers/mtd/spi-nor/controllers/aspeed-smc.c
index 7225870e8b18..a591ce1d24c5 100644
--- a/drivers/mtd/spi-nor/controllers/aspeed-smc.c
+++ b/drivers/mtd/spi-nor/controllers/aspeed-smc.c
@@ -6,6 +6,7 @@
*/
#include <linux/bug.h>
+#include <linux/clk.h>
#include <linux/device.h>
#include <linux/io.h>
#include <linux/module.h>
@@ -16,10 +17,13 @@
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/sizes.h>
+#include <linux/slab.h>
#include <linux/sysfs.h>
#define DEVICE_NAME "aspeed-smc"
+#define SNOR_F_4B_OPCODES BIT(6)
+
/*
* The driver only support SPI flash
*/
@@ -30,6 +34,7 @@ enum aspeed_smc_flash_type {
};
struct aspeed_smc_chip;
+struct aspeed_smc_controller;
struct aspeed_smc_info {
u32 maxsize; /* maximum size of chip window */
@@ -37,12 +42,34 @@ struct aspeed_smc_info {
bool hastype; /* flash type field exists in config reg */
u8 we0; /* shift for write enable bit for CE0 */
u8 ctl0; /* offset in regs of ctl for CE0 */
+ u8 timing; /* offset in regs of timing */
+ u32 hclk_mask; /* clock frequency mask in CEx Control reg */
+ u32 hdiv_max; /* Max HCLK divisor on read timing reg */
void (*set_4b)(struct aspeed_smc_chip *chip);
+ int (*optimize_read)(struct aspeed_smc_chip *chip, u32 max_freq);
+ int (*calibrate)(struct aspeed_smc_chip *chip, u32 hdiv,
+ const u8 *golden_buf, u8 *test_buf);
+
+ u32 (*segment_start)(struct aspeed_smc_controller *controller, u32 reg);
+ u32 (*segment_end)(struct aspeed_smc_controller *controller, u32 reg);
+ u32 (*segment_reg)(struct aspeed_smc_controller *controller,
+ u32 start, u32 end);
};
static void aspeed_smc_chip_set_4b_spi_2400(struct aspeed_smc_chip *chip);
static void aspeed_smc_chip_set_4b(struct aspeed_smc_chip *chip);
+static int aspeed_smc_optimize_read(struct aspeed_smc_chip *chip,
+ u32 max_freq);
+static int aspeed_smc_calibrate_reads(struct aspeed_smc_chip *chip, u32 hdiv,
+ const u8 *golden_buf, u8 *test_buf);
+
+static u32 aspeed_smc_segment_start(
+ struct aspeed_smc_controller *controller, u32 reg);
+static u32 aspeed_smc_segment_end(
+ struct aspeed_smc_controller *controller, u32 reg);
+static u32 aspeed_smc_segment_reg(
+ struct aspeed_smc_controller *controller, u32 start, u32 end);
static const struct aspeed_smc_info fmc_2400_info = {
.maxsize = 64 * 1024 * 1024,
@@ -50,7 +77,15 @@ static const struct aspeed_smc_info fmc_2400_info = {
.hastype = true,
.we0 = 16,
.ctl0 = 0x10,
+ .timing = 0x94,
+ .hclk_mask = 0xfffff0ff,
+ .hdiv_max = 1,
.set_4b = aspeed_smc_chip_set_4b,
+ .optimize_read = aspeed_smc_optimize_read,
+ .calibrate = aspeed_smc_calibrate_reads,
+ .segment_start = aspeed_smc_segment_start,
+ .segment_end = aspeed_smc_segment_end,
+ .segment_reg = aspeed_smc_segment_reg,
};
static const struct aspeed_smc_info spi_2400_info = {
@@ -59,7 +94,13 @@ static const struct aspeed_smc_info spi_2400_info = {
.hastype = false,
.we0 = 0,
.ctl0 = 0x04,
+ .timing = 0x14,
+ .hclk_mask = 0xfffff0ff,
+ .hdiv_max = 1,
.set_4b = aspeed_smc_chip_set_4b_spi_2400,
+ .optimize_read = aspeed_smc_optimize_read,
+ .calibrate = aspeed_smc_calibrate_reads,
+ /* No segment registers */
};
static const struct aspeed_smc_info fmc_2500_info = {
@@ -68,7 +109,15 @@ static const struct aspeed_smc_info fmc_2500_info = {
.hastype = true,
.we0 = 16,
.ctl0 = 0x10,
+ .timing = 0x94,
+ .hclk_mask = 0xfffff0ff,
+ .hdiv_max = 1,
.set_4b = aspeed_smc_chip_set_4b,
+ .optimize_read = aspeed_smc_optimize_read,
+ .calibrate = aspeed_smc_calibrate_reads,
+ .segment_start = aspeed_smc_segment_start,
+ .segment_end = aspeed_smc_segment_end,
+ .segment_reg = aspeed_smc_segment_reg,
};
static const struct aspeed_smc_info spi_2500_info = {
@@ -77,7 +126,59 @@ static const struct aspeed_smc_info spi_2500_info = {
.hastype = false,
.we0 = 16,
.ctl0 = 0x10,
+ .timing = 0x94,
+ .hclk_mask = 0xfffff0ff,
+ .hdiv_max = 1,
+ .set_4b = aspeed_smc_chip_set_4b,
+ .optimize_read = aspeed_smc_optimize_read,
+ .calibrate = aspeed_smc_calibrate_reads,
+ .segment_start = aspeed_smc_segment_start,
+ .segment_end = aspeed_smc_segment_end,
+ .segment_reg = aspeed_smc_segment_reg,
+};
+
+static u32 aspeed_smc_segment_start_ast2600(
+ struct aspeed_smc_controller *controller, u32 reg);
+static u32 aspeed_smc_segment_end_ast2600(
+ struct aspeed_smc_controller *controller, u32 reg);
+static u32 aspeed_smc_segment_reg_ast2600(
+ struct aspeed_smc_controller *controller, u32 start, u32 end);
+
+static int aspeed_smc_calibrate_reads_ast2600(struct aspeed_smc_chip *chip,
+ u32 hdiv, const u8 *golden_buf, u8 *test_buf);
+
+static const struct aspeed_smc_info fmc_2600_info = {
+ .maxsize = 256 * 1024 * 1024,
+ .nce = 3,
+ .hastype = false, /* SPI Only */
+ .we0 = 16,
+ .ctl0 = 0x10,
+ .timing = 0x94,
+ .hclk_mask = 0xf0fff0ff,
+ .hdiv_max = 2,
+ .set_4b = aspeed_smc_chip_set_4b,
+ .optimize_read = aspeed_smc_optimize_read,
+ .calibrate = aspeed_smc_calibrate_reads_ast2600,
+ .segment_start = aspeed_smc_segment_start_ast2600,
+ .segment_end = aspeed_smc_segment_end_ast2600,
+ .segment_reg = aspeed_smc_segment_reg_ast2600,
+};
+
+static const struct aspeed_smc_info spi_2600_info = {
+ .maxsize = 256 * 1024 * 1024,
+ .nce = 2,
+ .hastype = false,
+ .we0 = 16,
+ .ctl0 = 0x10,
+ .timing = 0x94,
+ .hclk_mask = 0xf0fff0ff,
+ .hdiv_max = 2,
.set_4b = aspeed_smc_chip_set_4b,
+ .optimize_read = aspeed_smc_optimize_read,
+ .calibrate = aspeed_smc_calibrate_reads_ast2600,
+ .segment_start = aspeed_smc_segment_start_ast2600,
+ .segment_end = aspeed_smc_segment_end_ast2600,
+ .segment_reg = aspeed_smc_segment_reg_ast2600,
};
enum aspeed_smc_ctl_reg_value {
@@ -98,6 +199,7 @@ struct aspeed_smc_chip {
u32 ctl_val[smc_max]; /* control settings */
enum aspeed_smc_flash_type type; /* what type of flash */
struct spi_nor nor;
+ u32 clk_rate;
};
struct aspeed_smc_controller {
@@ -106,12 +208,17 @@ struct aspeed_smc_controller {
struct mutex mutex; /* controller access mutex */
const struct aspeed_smc_info *info; /* type info of controller */
void __iomem *regs; /* controller registers */
- void __iomem *ahb_base; /* per-chip windows resource */
+ void __iomem *ahb_base; /* per-chip window resource */
+ u32 ahb_base_phy; /* phys addr of AHB window */
u32 ahb_window_size; /* full mapping window size */
+ unsigned long clk_frequency;
+
struct aspeed_smc_chip *chips[]; /* pointers to attached chips */
};
+#define ASPEED_SPI_DEFAULT_FREQ 50000000
+
/*
* SPI Flash Configuration Register (AST2500 SPI)
* or
@@ -181,23 +288,82 @@ struct aspeed_smc_controller {
(CONTROL_AAF_MODE | CONTROL_CE_INACTIVE_MASK | CONTROL_CLK_DIV4 | \
CONTROL_CLOCK_FREQ_SEL_MASK | CONTROL_LSB_FIRST | CONTROL_CLOCK_MODE_3)
-/*
- * The Segment Register uses a 8MB unit to encode the start address
- * and the end address of the mapping window of a flash SPI slave :
- *
- * | byte 1 | byte 2 | byte 3 | byte 4 |
- * +--------+--------+--------+--------+
- * | end | start | 0 | 0 |
- */
#define SEGMENT_ADDR_REG0 0x30
-#define SEGMENT_ADDR_START(_r) ((((_r) >> 16) & 0xFF) << 23)
-#define SEGMENT_ADDR_END(_r) ((((_r) >> 24) & 0xFF) << 23)
-#define SEGMENT_ADDR_VALUE(start, end) \
- (((((start) >> 23) & 0xFF) << 16) | ((((end) >> 23) & 0xFF) << 24))
#define SEGMENT_ADDR_REG(controller, cs) \
((controller)->regs + SEGMENT_ADDR_REG0 + (cs) * 4)
/*
+ * The Segment Registers of the AST2400 and AST2500 have a 8MB
+ * unit. The address range of a flash SPI slave is encoded with
+ * absolute addresses which should be part of the overall controller
+ * window.
+ */
+static u32 aspeed_smc_segment_start(
+ struct aspeed_smc_controller *controller, u32 reg)
+{
+ return ((reg >> 16) & 0xFF) << 23;
+}
+
+static u32 aspeed_smc_segment_end(
+ struct aspeed_smc_controller *controller, u32 reg)
+{
+ return ((reg >> 24) & 0xFF) << 23;
+}
+
+static u32 aspeed_smc_segment_reg(
+ struct aspeed_smc_controller *controller, u32 start, u32 end)
+{
+ return (((start >> 23) & 0xFF) << 16) | (((end >> 23) & 0xFF) << 24);
+}
+
+/*
+ * The Segment Registers of the AST2600 have a 1MB unit. The address
+ * range of a flash SPI slave is encoded with offsets in the overall
+ * controller window. The previous SoC AST2400 and AST2500 used
+ * absolute addresses. Only bits [27:20] are relevant and the end
+ * address is an upper bound limit.
+ */
+
+#define AST2600_SEG_ADDR_MASK 0x0ff00000
+
+static u32 aspeed_smc_segment_start_ast2600(
+ struct aspeed_smc_controller *controller, u32 reg)
+{
+ uint32_t start_offset = (reg << 16) & AST2600_SEG_ADDR_MASK;
+
+ return controller->ahb_base_phy + start_offset;
+}
+
+static u32 aspeed_smc_segment_end_ast2600(
+ struct aspeed_smc_controller *controller, u32 reg)
+{
+ uint32_t end_offset = reg & AST2600_SEG_ADDR_MASK;
+
+ /* segment is disabled */
+ if (!end_offset)
+ return controller->ahb_base_phy;
+
+ return controller->ahb_base_phy + end_offset + 0x100000;
+}
+
+static u32 aspeed_smc_segment_reg_ast2600(
+ struct aspeed_smc_controller *controller, u32 start, u32 end)
+{
+ /* disable zero size segments */
+ if (start == end)
+ return 0;
+
+ return ((start & AST2600_SEG_ADDR_MASK) >> 16) |
+ ((end - 1) & AST2600_SEG_ADDR_MASK);
+}
+
+/*
+ * Switch to turn off read optimisation if needed
+ */
+static bool optimize_read = true;
+module_param(optimize_read, bool, 0644);
+
+/*
* In user mode all data bytes read or written to the chip decode address
* range are transferred to or from the SPI bus. The range is treated as a
* fifo of arbitratry 1, 2, or 4 byte width but each write has to be aligned
@@ -370,18 +536,49 @@ static void aspeed_smc_send_cmd_addr(struct spi_nor *nor, u8 cmd, u32 addr)
}
}
+static int aspeed_smc_get_io_mode(struct aspeed_smc_chip *chip)
+{
+ switch (chip->nor.read_proto) {
+ case SNOR_PROTO_1_1_1:
+ return 0;
+ case SNOR_PROTO_1_1_2:
+ return CONTROL_IO_DUAL_DATA;
+ case SNOR_PROTO_1_2_2:
+ return CONTROL_IO_DUAL_ADDR_DATA;
+ default:
+ dev_err(chip->nor.dev, "unsupported SPI read mode\n");
+ return -EINVAL;
+ }
+}
+
+static void aspeed_smc_set_io_mode(struct aspeed_smc_chip *chip, u32 io_mode)
+{
+ u32 ctl;
+
+ if (io_mode > 0) {
+ ctl = readl(chip->ctl) & ~CONTROL_IO_MODE_MASK;
+ ctl |= io_mode;
+ writel(ctl, chip->ctl);
+ }
+}
+
static ssize_t aspeed_smc_read_user(struct spi_nor *nor, loff_t from,
size_t len, u_char *read_buf)
{
struct aspeed_smc_chip *chip = nor->priv;
int i;
u8 dummy = 0xFF;
+ int io_mode = aspeed_smc_get_io_mode(chip);
aspeed_smc_start_user(nor);
aspeed_smc_send_cmd_addr(nor, nor->read_opcode, from);
for (i = 0; i < chip->nor.read_dummy / 8; i++)
aspeed_smc_write_to_ahb(chip->ahb_base, &dummy, sizeof(dummy));
+ /* Set IO mode only for data */
+ if (io_mode == CONTROL_IO_DUAL_DATA)
+ aspeed_smc_set_io_mode(chip, io_mode);
+
aspeed_smc_read_from_ahb(read_buf, chip->ahb_base, len);
aspeed_smc_stop_user(nor);
return len;
@@ -399,6 +596,31 @@ static ssize_t aspeed_smc_write_user(struct spi_nor *nor, loff_t to,
return len;
}
+static ssize_t aspeed_smc_read(struct spi_nor *nor, loff_t from, size_t len,
+ u_char *read_buf)
+{
+ struct aspeed_smc_chip *chip = nor->priv;
+
+ /*
+ * The AHB window configured for the chip is too small for the
+ * read offset. Use the "User mode" of the controller to
+ * perform the read.
+ */
+ if (from >= chip->ahb_window_size) {
+ aspeed_smc_read_user(nor, from, len, read_buf);
+ goto out;
+ }
+
+ /*
+ * Use the "Command mode" to do a direct read from the AHB
+ * window configured for the chip. This should be the default.
+ */
+ memcpy_fromio(read_buf, chip->ahb_base + from, len);
+
+out:
+ return len;
+}
+
static int aspeed_smc_unregister(struct aspeed_smc_controller *controller)
{
struct aspeed_smc_chip *chip;
@@ -423,6 +645,8 @@ static const struct of_device_id aspeed_smc_matches[] = {
{ .compatible = "aspeed,ast2400-spi", .data = &spi_2400_info },
{ .compatible = "aspeed,ast2500-fmc", .data = &fmc_2500_info },
{ .compatible = "aspeed,ast2500-spi", .data = &spi_2500_info },
+ { .compatible = "aspeed,ast2600-fmc", .data = &fmc_2600_info },
+ { .compatible = "aspeed,ast2600-spi", .data = &spi_2600_info },
{ }
};
MODULE_DEVICE_TABLE(of, aspeed_smc_matches);
@@ -438,36 +662,32 @@ static void __iomem *aspeed_smc_chip_base(struct aspeed_smc_chip *chip,
struct resource *res)
{
struct aspeed_smc_controller *controller = chip->controller;
+ const struct aspeed_smc_info *info = controller->info;
u32 offset = 0;
u32 reg;
- if (controller->info->nce > 1) {
+ if (info->nce > 1) {
reg = readl(SEGMENT_ADDR_REG(controller, chip->cs));
- if (SEGMENT_ADDR_START(reg) >= SEGMENT_ADDR_END(reg))
+ if (info->segment_start(controller, reg) >=
+ info->segment_end(controller, reg)) {
return NULL;
+ }
- offset = SEGMENT_ADDR_START(reg) - res->start;
+ offset = info->segment_start(controller, reg) - res->start;
}
return controller->ahb_base + offset;
}
-static u32 aspeed_smc_ahb_base_phy(struct aspeed_smc_controller *controller)
-{
- u32 seg0_val = readl(SEGMENT_ADDR_REG(controller, 0));
-
- return SEGMENT_ADDR_START(seg0_val);
-}
-
static u32 chip_set_segment(struct aspeed_smc_chip *chip, u32 cs, u32 start,
u32 size)
{
struct aspeed_smc_controller *controller = chip->controller;
+ const struct aspeed_smc_info *info = controller->info;
void __iomem *seg_reg;
- u32 seg_oldval, seg_newval, ahb_base_phy, end;
-
- ahb_base_phy = aspeed_smc_ahb_base_phy(controller);
+ u32 seg_oldval, seg_newval, end;
+ u32 ahb_base_phy = controller->ahb_base_phy;
seg_reg = SEGMENT_ADDR_REG(controller, cs);
seg_oldval = readl(seg_reg);
@@ -477,8 +697,15 @@ static u32 chip_set_segment(struct aspeed_smc_chip *chip, u32 cs, u32 start,
* size, but take into account the possible overlap with the
* previous segment
*/
- if (!size)
- size = SEGMENT_ADDR_END(seg_oldval) - start;
+ if (!size) {
+ end = info->segment_end(controller, seg_oldval);
+
+ /*
+ * Check for disabled segment (AST2600).
+ */
+ if (end != ahb_base_phy)
+ size = end - start;
+ }
/*
* The segment cannot exceed the maximum window size of the
@@ -491,7 +718,7 @@ static u32 chip_set_segment(struct aspeed_smc_chip *chip, u32 cs, u32 start,
}
end = start + size;
- seg_newval = SEGMENT_ADDR_VALUE(start, end);
+ seg_newval = info->segment_reg(controller, start, end);
writel(seg_newval, seg_reg);
/*
@@ -502,13 +729,13 @@ static u32 chip_set_segment(struct aspeed_smc_chip *chip, u32 cs, u32 start,
if (seg_newval != readl(seg_reg)) {
dev_err(chip->nor.dev, "CE%d window invalid", cs);
writel(seg_oldval, seg_reg);
- start = SEGMENT_ADDR_START(seg_oldval);
- end = SEGMENT_ADDR_END(seg_oldval);
+ start = info->segment_start(controller, seg_oldval);
+ end = info->segment_end(controller, seg_oldval);
size = end - start;
}
- dev_info(chip->nor.dev, "CE%d window [ 0x%.8x - 0x%.8x ] %dMB",
- cs, start, end, size >> 20);
+ dev_info(chip->nor.dev, "CE%d window [ 0x%.8x - 0x%.8x ] %dMB%s",
+ cs, start, end, size >> 20, size ? "" : " (disabled)");
return size;
}
@@ -556,7 +783,7 @@ static u32 aspeed_smc_chip_set_segment(struct aspeed_smc_chip *chip)
chip->cs, size >> 20);
}
- ahb_base_phy = aspeed_smc_ahb_base_phy(controller);
+ ahb_base_phy = controller->ahb_base_phy;
/*
* As a start address for the current segment, use the default
@@ -566,7 +793,7 @@ static u32 aspeed_smc_chip_set_segment(struct aspeed_smc_chip *chip)
if (chip->cs) {
u32 prev = readl(SEGMENT_ADDR_REG(controller, chip->cs - 1));
- start = SEGMENT_ADDR_END(prev);
+ start = controller->info->segment_end(controller, prev);
} else {
start = ahb_base_phy;
}
@@ -703,10 +930,258 @@ static int aspeed_smc_chip_setup_init(struct aspeed_smc_chip *chip,
return 0;
}
+
+#define CALIBRATE_BUF_SIZE 16384
+
+static bool aspeed_smc_check_reads(struct aspeed_smc_chip *chip,
+ const u8 *golden_buf, u8 *test_buf)
+{
+ int i;
+
+ for (i = 0; i < 10; i++) {
+ memcpy_fromio(test_buf, chip->ahb_base, CALIBRATE_BUF_SIZE);
+ if (memcmp(test_buf, golden_buf, CALIBRATE_BUF_SIZE) != 0)
+ return false;
+ }
+ return true;
+}
+
+static int aspeed_smc_calibrate_reads(struct aspeed_smc_chip *chip, u32 hdiv,
+ const u8 *golden_buf, u8 *test_buf)
+{
+ struct aspeed_smc_controller *controller = chip->controller;
+ const struct aspeed_smc_info *info = controller->info;
+ int i;
+ int good_pass = -1, pass_count = 0;
+ u32 shift = (hdiv - 1) << 2;
+ u32 mask = ~(0xfu << shift);
+ u32 fread_timing_val = 0;
+
+#define FREAD_TPASS(i) (((i) / 2) | (((i) & 1) ? 0 : 8))
+
+ /* Try HCLK delay 0..5, each one with/without delay and look for a
+ * good pair.
+ */
+ for (i = 0; i < 12; i++) {
+ bool pass;
+
+ fread_timing_val &= mask;
+ fread_timing_val |= FREAD_TPASS(i) << shift;
+
+ writel(fread_timing_val, controller->regs + info->timing);
+ pass = aspeed_smc_check_reads(chip, golden_buf, test_buf);
+ dev_dbg(chip->nor.dev,
+ " * [%08x] %d HCLK delay, %dns DI delay : %s",
+ fread_timing_val, i/2, (i & 1) ? 0 : 4,
+ pass ? "PASS" : "FAIL");
+ if (pass) {
+ pass_count++;
+ if (pass_count == 3) {
+ good_pass = i - 1;
+ break;
+ }
+ } else
+ pass_count = 0;
+ }
+
+ /* No good setting for this frequency */
+ if (good_pass < 0)
+ return -1;
+
+ /* We have at least one pass of margin, let's use first pass */
+ fread_timing_val &= mask;
+ fread_timing_val |= FREAD_TPASS(good_pass) << shift;
+ writel(fread_timing_val, controller->regs + info->timing);
+ dev_dbg(chip->nor.dev, " * -> good is pass %d [0x%08x]",
+ good_pass, fread_timing_val);
+ return 0;
+}
+
+static bool aspeed_smc_check_calib_data(const u8 *test_buf, u32 size)
+{
+ const u32 *tb32 = (const u32 *) test_buf;
+ u32 i, cnt = 0;
+
+ /* We check if we have enough words that are neither all 0
+ * nor all 1's so the calibration can be considered valid.
+ *
+ * I use an arbitrary threshold for now of 64
+ */
+ size >>= 2;
+ for (i = 0; i < size; i++) {
+ if (tb32[i] != 0 && tb32[i] != 0xffffffff)
+ cnt++;
+ }
+ return cnt >= 64;
+}
+
+static const uint32_t aspeed_smc_hclk_divs[] = {
+ 0xf, /* HCLK */
+ 0x7, /* HCLK/2 */
+ 0xe, /* HCLK/3 */
+ 0x6, /* HCLK/4 */
+ 0xd, /* HCLK/5 */
+};
+#define ASPEED_SMC_HCLK_DIV(i) \
+ (aspeed_smc_hclk_divs[(i) - 1] << CONTROL_CLOCK_FREQ_SEL_SHIFT)
+
+static u32 aspeed_smc_default_read(struct aspeed_smc_chip *chip)
+{
+ /*
+ * Keep the 4Byte address mode on the AST2400 SPI controller.
+ * Other controllers set the 4Byte mode in the CE Control
+ * Register
+ */
+ u32 ctl_mask = chip->controller->info == &spi_2400_info ?
+ CONTROL_IO_ADDRESS_4B : 0;
+ u8 cmd = chip->nor.addr_width == 4 ? SPINOR_OP_READ_4B :
+ SPINOR_OP_READ;
+
+ /*
+ * Use the "read command" mode to customize the opcode. In
+ * normal command mode, the value is necessarily READ (0x3) on
+ * the AST2400/2500 SoCs.
+ */
+ return (chip->ctl_val[smc_read] & ctl_mask) |
+ (0x00 << 28) | /* Single bit */
+ (0x00 << 24) | /* CE# max */
+ (cmd << 16) | /* use read mode to support 4B opcode */
+ (0x00 << 8) | /* HCLK/16 */
+ (0x00 << 6) | /* no dummy cycle */
+ (0x01); /* read mode */
+}
+
+static int aspeed_smc_optimize_read(struct aspeed_smc_chip *chip,
+ u32 max_freq)
+{
+ struct aspeed_smc_controller *controller = chip->controller;
+ const struct aspeed_smc_info *info = controller->info;
+ u8 *golden_buf, *test_buf;
+ int i, rc, best_div = -1;
+ u32 save_read_val = chip->ctl_val[smc_read];
+ u32 ahb_freq = chip->controller->clk_frequency;
+
+ dev_dbg(chip->nor.dev, "AHB frequency: %d MHz", ahb_freq / 1000000);
+
+ test_buf = kmalloc(CALIBRATE_BUF_SIZE * 2, GFP_KERNEL);
+ golden_buf = test_buf + CALIBRATE_BUF_SIZE;
+
+ /* We start with the dumbest setting (keep 4Byte bit) and read
+ * some data
+ */
+ chip->ctl_val[smc_read] = aspeed_smc_default_read(chip);
+
+ writel(chip->ctl_val[smc_read], chip->ctl);
+
+ memcpy_fromio(golden_buf, chip->ahb_base, CALIBRATE_BUF_SIZE);
+
+ /* Establish our read mode with freq field set to 0 (HCLK/16) */
+ chip->ctl_val[smc_read] = save_read_val & info->hclk_mask;
+
+ /* Check if calibration data is suitable */
+ if (!aspeed_smc_check_calib_data(golden_buf, CALIBRATE_BUF_SIZE)) {
+ dev_info(chip->nor.dev,
+ "Calibration area too uniform, using low speed");
+ writel(chip->ctl_val[smc_read], chip->ctl);
+ kfree(test_buf);
+ return 0;
+ }
+
+ /* Now we iterate the HCLK dividers until we find our breaking point */
+ for (i = ARRAY_SIZE(aspeed_smc_hclk_divs); i > info->hdiv_max - 1; i--) {
+ u32 tv, freq;
+
+ /* Compare timing to max */
+ freq = ahb_freq / i;
+ if (freq > max_freq)
+ continue;
+
+ /* Set the timing */
+ tv = chip->ctl_val[smc_read] | ASPEED_SMC_HCLK_DIV(i);
+ writel(tv, chip->ctl);
+ dev_dbg(chip->nor.dev, "Trying HCLK/%d [%08x] ...", i, tv);
+ rc = info->calibrate(chip, i, golden_buf, test_buf);
+ if (rc == 0)
+ best_div = i;
+ }
+ kfree(test_buf);
+
+ /* Nothing found ? */
+ if (best_div < 0)
+ dev_warn(chip->nor.dev, "No good frequency, using dumb slow");
+ else {
+ dev_dbg(chip->nor.dev, "Found good read timings at HCLK/%d",
+ best_div);
+ chip->ctl_val[smc_read] |= ASPEED_SMC_HCLK_DIV(best_div);
+ }
+
+ writel(chip->ctl_val[smc_read], chip->ctl);
+ return 0;
+}
+
+#define TIMING_DELAY_DI BIT(3)
+#define TIMING_DELAY_HCYCLE_MAX 5
+#define TIMING_REG_AST2600(chip) \
+ ((chip)->controller->regs + (chip)->controller->info->timing + \
+ (chip)->cs * 4)
+
+static int aspeed_smc_calibrate_reads_ast2600(struct aspeed_smc_chip *chip, u32 hdiv,
+ const u8 *golden_buf, u8 *test_buf)
+{
+ int hcycle;
+ u32 shift = (hdiv - 2) << 3;
+ u32 mask = ~(0xfu << shift);
+ u32 fread_timing_val = 0;
+
+ for (hcycle = 0; hcycle <= TIMING_DELAY_HCYCLE_MAX; hcycle++) {
+ int delay_ns;
+ bool pass = false;
+
+ fread_timing_val &= mask;
+ fread_timing_val |= hcycle << shift;
+
+ /* no DI input delay first */
+ writel(fread_timing_val, TIMING_REG_AST2600(chip));
+ pass = aspeed_smc_check_reads(chip, golden_buf, test_buf);
+ dev_dbg(chip->nor.dev,
+ " * [%08x] %d HCLK delay, DI delay none : %s",
+ fread_timing_val, hcycle, pass ? "PASS" : "FAIL");
+ if (pass)
+ return 0;
+
+ /* Add DI input delays */
+ fread_timing_val &= mask;
+ fread_timing_val |= (TIMING_DELAY_DI | hcycle) << shift;
+
+ for (delay_ns = 0; delay_ns < 0x10; delay_ns++) {
+ fread_timing_val &= ~(0xf << (4 + shift));
+ fread_timing_val |= delay_ns << (4 + shift);
+
+ writel(fread_timing_val, TIMING_REG_AST2600(chip));
+ pass = aspeed_smc_check_reads(chip, golden_buf, test_buf);
+ dev_dbg(chip->nor.dev,
+ " * [%08x] %d HCLK delay, DI delay %d.%dns : %s",
+ fread_timing_val, hcycle, (delay_ns + 1)/2,
+ (delay_ns + 1) & 1 ? 5 : 5, pass ? "PASS" : "FAIL");
+ /*
+ * TODO: This is optimistic. We should look
+ * for a working interval and save the middle
+ * value in the read timing register.
+ */
+ if (pass)
+ return 0;
+ }
+ }
+
+ /* No good setting for this frequency */
+ return -1;
+}
+
static int aspeed_smc_chip_setup_finish(struct aspeed_smc_chip *chip)
{
struct aspeed_smc_controller *controller = chip->controller;
const struct aspeed_smc_info *info = controller->info;
+ int io_mode;
u32 cmd;
if (chip->nor.addr_width == 4 && info->set_4b)
@@ -729,21 +1204,24 @@ static int aspeed_smc_chip_setup_finish(struct aspeed_smc_chip *chip)
* TODO: Adjust clocks if fast read is supported and interpret
* SPI NOR flags to adjust controller settings.
*/
- if (chip->nor.read_proto == SNOR_PROTO_1_1_1) {
- if (chip->nor.read_dummy == 0)
- cmd = CONTROL_COMMAND_MODE_NORMAL;
- else
- cmd = CONTROL_COMMAND_MODE_FREAD;
- } else {
- dev_err(chip->nor.dev, "unsupported SPI read mode\n");
- return -EINVAL;
- }
+ io_mode = aspeed_smc_get_io_mode(chip);
+ if (io_mode < 0)
+ return io_mode;
+
+ if (chip->nor.read_dummy == 0)
+ cmd = CONTROL_COMMAND_MODE_NORMAL;
+ else
+ cmd = CONTROL_COMMAND_MODE_FREAD;
- chip->ctl_val[smc_read] |= cmd |
+ chip->ctl_val[smc_read] |= cmd | io_mode |
+ chip->nor.read_opcode << CONTROL_COMMAND_SHIFT |
CONTROL_IO_DUMMY_SET(chip->nor.read_dummy / 8);
- dev_dbg(controller->dev, "base control register: %08x\n",
+ dev_info(controller->dev, "read control register: %08x\n",
chip->ctl_val[smc_read]);
+
+ if (optimize_read && info->optimize_read)
+ info->optimize_read(chip, chip->clk_rate);
return 0;
}
@@ -752,7 +1230,7 @@ static const struct spi_nor_controller_ops aspeed_smc_controller_ops = {
.unprepare = aspeed_smc_unprep,
.read_reg = aspeed_smc_read_reg,
.write_reg = aspeed_smc_write_reg,
- .read = aspeed_smc_read_user,
+ .read = aspeed_smc_read,
.write = aspeed_smc_write_user,
};
@@ -762,6 +1240,7 @@ static int aspeed_smc_setup_flash(struct aspeed_smc_controller *controller,
const struct spi_nor_hwcaps hwcaps = {
.mask = SNOR_HWCAPS_READ |
SNOR_HWCAPS_READ_FAST |
+ SNOR_HWCAPS_READ_1_1_2 |
SNOR_HWCAPS_PP,
};
const struct aspeed_smc_info *info = controller->info;
@@ -805,6 +1284,13 @@ static int aspeed_smc_setup_flash(struct aspeed_smc_controller *controller,
break;
}
+ if (of_property_read_u32(child, "spi-max-frequency",
+ &chip->clk_rate)) {
+ chip->clk_rate = ASPEED_SPI_DEFAULT_FREQ;
+ }
+ dev_info(dev, "Using %d MHz SPI frequency\n",
+ chip->clk_rate / 1000000);
+
chip->controller = controller;
chip->ctl = controller->regs + info->ctl0 + cs * 4;
chip->cs = cs;
@@ -856,6 +1342,7 @@ static int aspeed_smc_probe(struct platform_device *pdev)
struct aspeed_smc_controller *controller;
const struct of_device_id *match;
const struct aspeed_smc_info *info;
+ struct clk *clk;
struct resource *res;
int ret;
@@ -881,12 +1368,19 @@ static int aspeed_smc_probe(struct platform_device *pdev)
return PTR_ERR(controller->regs);
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ controller->ahb_base_phy = res->start;
controller->ahb_base = devm_ioremap_resource(dev, res);
if (IS_ERR(controller->ahb_base))
return PTR_ERR(controller->ahb_base);
controller->ahb_window_size = resource_size(res);
+ clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+ controller->clk_frequency = clk_get_rate(clk);
+ devm_clk_put(&pdev->dev, clk);
+
ret = aspeed_smc_setup_flash(controller, np, res);
if (ret)
dev_err(dev, "Aspeed SMC probe failed %d\n", ret);
diff --git a/drivers/mtd/spi-nor/sfdp.c b/drivers/mtd/spi-nor/sfdp.c
index 55c0c508464b..9db07182e9c8 100644
--- a/drivers/mtd/spi-nor/sfdp.c
+++ b/drivers/mtd/spi-nor/sfdp.c
@@ -456,7 +456,6 @@ static int spi_nor_parse_bfpt(struct spi_nor *nor,
/* Number of address bytes. */
switch (bfpt.dwords[BFPT_DWORD(1)] & BFPT_DWORD1_ADDRESS_BYTES_MASK) {
case BFPT_DWORD1_ADDRESS_BYTES_3_ONLY:
- case BFPT_DWORD1_ADDRESS_BYTES_3_OR_4:
nor->addr_width = 3;
break;
diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c
new file mode 100644
index 000000000000..99ce971ebc6d
--- /dev/null
+++ b/drivers/mtd/spi-nor/spi-nor.c
@@ -0,0 +1,5155 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Based on m25p80.c, by Mike Lavender (mike@steroidmicros.com), with
+ * influence from lart.c (Abraham Van Der Merwe) and mtd_dataflash.c
+ *
+ * Copyright (C) 2005, Intec Automation Inc.
+ * Copyright (C) 2014, Freescale Semiconductor, Inc.
+ */
+
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/mutex.h>
+#include <linux/math64.h>
+#include <linux/sizes.h>
+#include <linux/slab.h>
+#include <linux/sort.h>
+
+#include <linux/mtd/mtd.h>
+#include <linux/of_platform.h>
+#include <linux/sched/task_stack.h>
+#include <linux/spi/flash.h>
+#include <linux/mtd/spi-nor.h>
+
+/* Define max times to check status register before we give up. */
+
+/*
+ * For everything but full-chip erase; probably could be much smaller, but kept
+ * around for safety for now
+ */
+#define DEFAULT_READY_WAIT_JIFFIES (40UL * HZ)
+
+/*
+ * For full-chip erase, calibrated to a 2MB flash (M25P16); should be scaled up
+ * for larger flash
+ */
+#define CHIP_ERASE_2MB_READY_WAIT_JIFFIES (40UL * HZ)
+
+#define SPI_NOR_MAX_ID_LEN 6
+#define SPI_NOR_MAX_ADDR_WIDTH 4
+
+struct sfdp_parameter_header {
+ u8 id_lsb;
+ u8 minor;
+ u8 major;
+ u8 length; /* in double words */
+ u8 parameter_table_pointer[3]; /* byte address */
+ u8 id_msb;
+};
+
+#define SFDP_PARAM_HEADER_ID(p) (((p)->id_msb << 8) | (p)->id_lsb)
+#define SFDP_PARAM_HEADER_PTP(p) \
+ (((p)->parameter_table_pointer[2] << 16) | \
+ ((p)->parameter_table_pointer[1] << 8) | \
+ ((p)->parameter_table_pointer[0] << 0))
+
+#define SFDP_BFPT_ID 0xff00 /* Basic Flash Parameter Table */
+#define SFDP_SECTOR_MAP_ID 0xff81 /* Sector Map Table */
+#define SFDP_4BAIT_ID 0xff84 /* 4-byte Address Instruction Table */
+
+#define SFDP_SIGNATURE 0x50444653U
+#define SFDP_JESD216_MAJOR 1
+#define SFDP_JESD216_MINOR 0
+#define SFDP_JESD216A_MINOR 5
+#define SFDP_JESD216B_MINOR 6
+
+struct sfdp_header {
+ u32 signature; /* Ox50444653U <=> "SFDP" */
+ u8 minor;
+ u8 major;
+ u8 nph; /* 0-base number of parameter headers */
+ u8 unused;
+
+ /* Basic Flash Parameter Table. */
+ struct sfdp_parameter_header bfpt_header;
+};
+
+/* Basic Flash Parameter Table */
+
+/*
+ * JESD216 rev B defines a Basic Flash Parameter Table of 16 DWORDs.
+ * They are indexed from 1 but C arrays are indexed from 0.
+ */
+#define BFPT_DWORD(i) ((i) - 1)
+#define BFPT_DWORD_MAX 16
+
+/* The first version of JESB216 defined only 9 DWORDs. */
+#define BFPT_DWORD_MAX_JESD216 9
+
+/* 1st DWORD. */
+#define BFPT_DWORD1_FAST_READ_1_1_2 BIT(16)
+#define BFPT_DWORD1_ADDRESS_BYTES_MASK GENMASK(18, 17)
+#define BFPT_DWORD1_ADDRESS_BYTES_3_ONLY (0x0UL << 17)
+#define BFPT_DWORD1_ADDRESS_BYTES_3_OR_4 (0x1UL << 17)
+#define BFPT_DWORD1_ADDRESS_BYTES_4_ONLY (0x2UL << 17)
+#define BFPT_DWORD1_DTR BIT(19)
+#define BFPT_DWORD1_FAST_READ_1_2_2 BIT(20)
+#define BFPT_DWORD1_FAST_READ_1_4_4 BIT(21)
+#define BFPT_DWORD1_FAST_READ_1_1_4 BIT(22)
+
+/* 5th DWORD. */
+#define BFPT_DWORD5_FAST_READ_2_2_2 BIT(0)
+#define BFPT_DWORD5_FAST_READ_4_4_4 BIT(4)
+
+/* 11th DWORD. */
+#define BFPT_DWORD11_PAGE_SIZE_SHIFT 4
+#define BFPT_DWORD11_PAGE_SIZE_MASK GENMASK(7, 4)
+
+/* 15th DWORD. */
+
+/*
+ * (from JESD216 rev B)
+ * Quad Enable Requirements (QER):
+ * - 000b: Device does not have a QE bit. Device detects 1-1-4 and 1-4-4
+ * reads based on instruction. DQ3/HOLD# functions are hold during
+ * instruction phase.
+ * - 001b: QE is bit 1 of status register 2. It is set via Write Status with
+ * two data bytes where bit 1 of the second byte is one.
+ * [...]
+ * Writing only one byte to the status register has the side-effect of
+ * clearing status register 2, including the QE bit. The 100b code is
+ * used if writing one byte to the status register does not modify
+ * status register 2.
+ * - 010b: QE is bit 6 of status register 1. It is set via Write Status with
+ * one data byte where bit 6 is one.
+ * [...]
+ * - 011b: QE is bit 7 of status register 2. It is set via Write status
+ * register 2 instruction 3Eh with one data byte where bit 7 is one.
+ * [...]
+ * The status register 2 is read using instruction 3Fh.
+ * - 100b: QE is bit 1 of status register 2. It is set via Write Status with
+ * two data bytes where bit 1 of the second byte is one.
+ * [...]
+ * In contrast to the 001b code, writing one byte to the status
+ * register does not modify status register 2.
+ * - 101b: QE is bit 1 of status register 2. Status register 1 is read using
+ * Read Status instruction 05h. Status register2 is read using
+ * instruction 35h. QE is set via Write Status instruction 01h with
+ * two data bytes where bit 1 of the second byte is one.
+ * [...]
+ */
+#define BFPT_DWORD15_QER_MASK GENMASK(22, 20)
+#define BFPT_DWORD15_QER_NONE (0x0UL << 20) /* Micron */
+#define BFPT_DWORD15_QER_SR2_BIT1_BUGGY (0x1UL << 20)
+#define BFPT_DWORD15_QER_SR1_BIT6 (0x2UL << 20) /* Macronix */
+#define BFPT_DWORD15_QER_SR2_BIT7 (0x3UL << 20)
+#define BFPT_DWORD15_QER_SR2_BIT1_NO_RD (0x4UL << 20)
+#define BFPT_DWORD15_QER_SR2_BIT1 (0x5UL << 20) /* Spansion */
+
+struct sfdp_bfpt {
+ u32 dwords[BFPT_DWORD_MAX];
+};
+
+/**
+ * struct spi_nor_fixups - SPI NOR fixup hooks
+ * @default_init: called after default flash parameters init. Used to tweak
+ * flash parameters when information provided by the flash_info
+ * table is incomplete or wrong.
+ * @post_bfpt: called after the BFPT table has been parsed
+ * @post_sfdp: called after SFDP has been parsed (is also called for SPI NORs
+ * that do not support RDSFDP). Typically used to tweak various
+ * parameters that could not be extracted by other means (i.e.
+ * when information provided by the SFDP/flash_info tables are
+ * incomplete or wrong).
+ *
+ * Those hooks can be used to tweak the SPI NOR configuration when the SFDP
+ * table is broken or not available.
+ */
+struct spi_nor_fixups {
+ void (*default_init)(struct spi_nor *nor);
+ int (*post_bfpt)(struct spi_nor *nor,
+ const struct sfdp_parameter_header *bfpt_header,
+ const struct sfdp_bfpt *bfpt,
+ struct spi_nor_flash_parameter *params);
+ void (*post_sfdp)(struct spi_nor *nor);
+};
+
+struct flash_info {
+ char *name;
+
+ /*
+ * This array stores the ID bytes.
+ * The first three bytes are the JEDIC ID.
+ * JEDEC ID zero means "no ID" (mostly older chips).
+ */
+ u8 id[SPI_NOR_MAX_ID_LEN];
+ u8 id_len;
+
+ /* The size listed here is what works with SPINOR_OP_SE, which isn't
+ * necessarily called a "sector" by the vendor.
+ */
+ unsigned sector_size;
+ u16 n_sectors;
+
+ u16 page_size;
+ u16 addr_width;
+
+ u16 flags;
+#define SECT_4K BIT(0) /* SPINOR_OP_BE_4K works uniformly */
+#define SPI_NOR_NO_ERASE BIT(1) /* No erase command needed */
+#define SST_WRITE BIT(2) /* use SST byte programming */
+#define SPI_NOR_NO_FR BIT(3) /* Can't do fastread */
+#define SECT_4K_PMC BIT(4) /* SPINOR_OP_BE_4K_PMC works uniformly */
+#define SPI_NOR_DUAL_READ BIT(5) /* Flash supports Dual Read */
+#define SPI_NOR_QUAD_READ BIT(6) /* Flash supports Quad Read */
+#define USE_FSR BIT(7) /* use flag status register */
+#define SPI_NOR_HAS_LOCK BIT(8) /* Flash supports lock/unlock via SR */
+#define SPI_NOR_HAS_TB BIT(9) /*
+ * Flash SR has Top/Bottom (TB) protect
+ * bit. Must be used with
+ * SPI_NOR_HAS_LOCK.
+ */
+#define SPI_NOR_XSR_RDY BIT(10) /*
+ * S3AN flashes have specific opcode to
+ * read the status register.
+ * Flags SPI_NOR_XSR_RDY and SPI_S3AN
+ * use the same bit as one implies the
+ * other, but we will get rid of
+ * SPI_S3AN soon.
+ */
+#define SPI_S3AN BIT(10) /*
+ * Xilinx Spartan 3AN In-System Flash
+ * (MFR cannot be used for probing
+ * because it has the same value as
+ * ATMEL flashes)
+ */
+#define SPI_NOR_4B_OPCODES BIT(11) /*
+ * Use dedicated 4byte address op codes
+ * to support memory size above 128Mib.
+ */
+#define NO_CHIP_ERASE BIT(12) /* Chip does not support chip erase */
+#define SPI_NOR_SKIP_SFDP BIT(13) /* Skip parsing of SFDP tables */
+#define USE_CLSR BIT(14) /* use CLSR command */
+#define SPI_NOR_OCTAL_READ BIT(15) /* Flash supports Octal Read */
+
+ /* Part specific fixup hooks. */
+ const struct spi_nor_fixups *fixups;
+};
+
+#define JEDEC_MFR(info) ((info)->id[0])
+
+/**
+ * spi_nor_spimem_xfer_data() - helper function to read/write data to
+ * flash's memory region
+ * @nor: pointer to 'struct spi_nor'
+ * @op: pointer to 'struct spi_mem_op' template for transfer
+ *
+ * Return: number of bytes transferred on success, -errno otherwise
+ */
+static ssize_t spi_nor_spimem_xfer_data(struct spi_nor *nor,
+ struct spi_mem_op *op)
+{
+ bool usebouncebuf = false;
+ void *rdbuf = NULL;
+ const void *buf;
+ int ret;
+
+ if (op->data.dir == SPI_MEM_DATA_IN)
+ buf = op->data.buf.in;
+ else
+ buf = op->data.buf.out;
+
+ if (object_is_on_stack(buf) || !virt_addr_valid(buf))
+ usebouncebuf = true;
+
+ if (usebouncebuf) {
+ if (op->data.nbytes > nor->bouncebuf_size)
+ op->data.nbytes = nor->bouncebuf_size;
+
+ if (op->data.dir == SPI_MEM_DATA_IN) {
+ rdbuf = op->data.buf.in;
+ op->data.buf.in = nor->bouncebuf;
+ } else {
+ op->data.buf.out = nor->bouncebuf;
+ memcpy(nor->bouncebuf, buf,
+ op->data.nbytes);
+ }
+ }
+
+ ret = spi_mem_adjust_op_size(nor->spimem, op);
+ if (ret)
+ return ret;
+
+ ret = spi_mem_exec_op(nor->spimem, op);
+ if (ret)
+ return ret;
+
+ if (usebouncebuf && op->data.dir == SPI_MEM_DATA_IN)
+ memcpy(rdbuf, nor->bouncebuf, op->data.nbytes);
+
+ return op->data.nbytes;
+}
+
+/**
+ * spi_nor_spimem_read_data() - read data from flash's memory region via
+ * spi-mem
+ * @nor: pointer to 'struct spi_nor'
+ * @from: offset to read from
+ * @len: number of bytes to read
+ * @buf: pointer to dst buffer
+ *
+ * Return: number of bytes read successfully, -errno otherwise
+ */
+static ssize_t spi_nor_spimem_read_data(struct spi_nor *nor, loff_t from,
+ size_t len, u8 *buf)
+{
+ struct spi_mem_op op =
+ SPI_MEM_OP(SPI_MEM_OP_CMD(nor->read_opcode, 1),
+ SPI_MEM_OP_ADDR(nor->addr_width, from, 1),
+ SPI_MEM_OP_DUMMY(nor->read_dummy, 1),
+ SPI_MEM_OP_DATA_IN(len, buf, 1));
+
+ /* get transfer protocols. */
+ op.cmd.buswidth = spi_nor_get_protocol_inst_nbits(nor->read_proto);
+ op.addr.buswidth = spi_nor_get_protocol_addr_nbits(nor->read_proto);
+ op.dummy.buswidth = op.addr.buswidth;
+ op.data.buswidth = spi_nor_get_protocol_data_nbits(nor->read_proto);
+
+ /* convert the dummy cycles to the number of bytes */
+ op.dummy.nbytes = (nor->read_dummy * op.dummy.buswidth) / 8;
+
+ return spi_nor_spimem_xfer_data(nor, &op);
+}
+
+/**
+ * spi_nor_read_data() - read data from flash memory
+ * @nor: pointer to 'struct spi_nor'
+ * @from: offset to read from
+ * @len: number of bytes to read
+ * @buf: pointer to dst buffer
+ *
+ * Return: number of bytes read successfully, -errno otherwise
+ */
+static ssize_t spi_nor_read_data(struct spi_nor *nor, loff_t from, size_t len,
+ u8 *buf)
+{
+ if (nor->spimem)
+ return spi_nor_spimem_read_data(nor, from, len, buf);
+
+ return nor->read(nor, from, len, buf);
+}
+
+/**
+ * spi_nor_spimem_write_data() - write data to flash memory via
+ * spi-mem
+ * @nor: pointer to 'struct spi_nor'
+ * @to: offset to write to
+ * @len: number of bytes to write
+ * @buf: pointer to src buffer
+ *
+ * Return: number of bytes written successfully, -errno otherwise
+ */
+static ssize_t spi_nor_spimem_write_data(struct spi_nor *nor, loff_t to,
+ size_t len, const u8 *buf)
+{
+ struct spi_mem_op op =
+ SPI_MEM_OP(SPI_MEM_OP_CMD(nor->program_opcode, 1),
+ SPI_MEM_OP_ADDR(nor->addr_width, to, 1),
+ SPI_MEM_OP_NO_DUMMY,
+ SPI_MEM_OP_DATA_OUT(len, buf, 1));
+
+ op.cmd.buswidth = spi_nor_get_protocol_inst_nbits(nor->write_proto);
+ op.addr.buswidth = spi_nor_get_protocol_addr_nbits(nor->write_proto);
+ op.data.buswidth = spi_nor_get_protocol_data_nbits(nor->write_proto);
+
+ if (nor->program_opcode == SPINOR_OP_AAI_WP && nor->sst_write_second)
+ op.addr.nbytes = 0;
+
+ return spi_nor_spimem_xfer_data(nor, &op);
+}
+
+/**
+ * spi_nor_write_data() - write data to flash memory
+ * @nor: pointer to 'struct spi_nor'
+ * @to: offset to write to
+ * @len: number of bytes to write
+ * @buf: pointer to src buffer
+ *
+ * Return: number of bytes written successfully, -errno otherwise
+ */
+static ssize_t spi_nor_write_data(struct spi_nor *nor, loff_t to, size_t len,
+ const u8 *buf)
+{
+ if (nor->spimem)
+ return spi_nor_spimem_write_data(nor, to, len, buf);
+
+ return nor->write(nor, to, len, buf);
+}
+
+/*
+ * Read the status register, returning its value in the location
+ * Return the status register value.
+ * Returns negative if error occurred.
+ */
+static int read_sr(struct spi_nor *nor)
+{
+ int ret;
+
+ if (nor->spimem) {
+ struct spi_mem_op op =
+ SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDSR, 1),
+ SPI_MEM_OP_NO_ADDR,
+ SPI_MEM_OP_NO_DUMMY,
+ SPI_MEM_OP_DATA_IN(1, nor->bouncebuf, 1));
+
+ ret = spi_mem_exec_op(nor->spimem, &op);
+ } else {
+ ret = nor->read_reg(nor, SPINOR_OP_RDSR, nor->bouncebuf, 1);
+ }
+
+ if (ret < 0) {
+ pr_err("error %d reading SR\n", (int) ret);
+ return ret;
+ }
+
+ return nor->bouncebuf[0];
+}
+
+/*
+ * Read the flag status register, returning its value in the location
+ * Return the status register value.
+ * Returns negative if error occurred.
+ */
+static int read_fsr(struct spi_nor *nor)
+{
+ int ret;
+
+ if (nor->spimem) {
+ struct spi_mem_op op =
+ SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDFSR, 1),
+ SPI_MEM_OP_NO_ADDR,
+ SPI_MEM_OP_NO_DUMMY,
+ SPI_MEM_OP_DATA_IN(1, nor->bouncebuf, 1));
+
+ ret = spi_mem_exec_op(nor->spimem, &op);
+ } else {
+ ret = nor->read_reg(nor, SPINOR_OP_RDFSR, nor->bouncebuf, 1);
+ }
+
+ if (ret < 0) {
+ pr_err("error %d reading FSR\n", ret);
+ return ret;
+ }
+
+ return nor->bouncebuf[0];
+}
+
+/*
+ * Read configuration register, returning its value in the
+ * location. Return the configuration register value.
+ * Returns negative if error occurred.
+ */
+static int read_cr(struct spi_nor *nor)
+{
+ int ret;
+
+ if (nor->spimem) {
+ struct spi_mem_op op =
+ SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDCR, 1),
+ SPI_MEM_OP_NO_ADDR,
+ SPI_MEM_OP_NO_DUMMY,
+ SPI_MEM_OP_DATA_IN(1, nor->bouncebuf, 1));
+
+ ret = spi_mem_exec_op(nor->spimem, &op);
+ } else {
+ ret = nor->read_reg(nor, SPINOR_OP_RDCR, nor->bouncebuf, 1);
+ }
+
+ if (ret < 0) {
+ dev_err(nor->dev, "error %d reading CR\n", ret);
+ return ret;
+ }
+
+ return nor->bouncebuf[0];
+}
+
+/*
+ * Write status register 1 byte
+ * Returns negative if error occurred.
+ */
+static int write_sr(struct spi_nor *nor, u8 val)
+{
+ nor->bouncebuf[0] = val;
+ if (nor->spimem) {
+ struct spi_mem_op op =
+ SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WRSR, 1),
+ SPI_MEM_OP_NO_ADDR,
+ SPI_MEM_OP_NO_DUMMY,
+ SPI_MEM_OP_DATA_OUT(1, nor->bouncebuf, 1));
+
+ return spi_mem_exec_op(nor->spimem, &op);
+ }
+
+ return nor->write_reg(nor, SPINOR_OP_WRSR, nor->bouncebuf, 1);
+}
+
+/*
+ * Set write enable latch with Write Enable command.
+ * Returns negative if error occurred.
+ */
+static int write_enable(struct spi_nor *nor)
+{
+ if (nor->spimem) {
+ struct spi_mem_op op =
+ SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WREN, 1),
+ SPI_MEM_OP_NO_ADDR,
+ SPI_MEM_OP_NO_DUMMY,
+ SPI_MEM_OP_NO_DATA);
+
+ return spi_mem_exec_op(nor->spimem, &op);
+ }
+
+ return nor->write_reg(nor, SPINOR_OP_WREN, NULL, 0);
+}
+
+/*
+ * Send write disable instruction to the chip.
+ */
+static int write_disable(struct spi_nor *nor)
+{
+ if (nor->spimem) {
+ struct spi_mem_op op =
+ SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WRDI, 1),
+ SPI_MEM_OP_NO_ADDR,
+ SPI_MEM_OP_NO_DUMMY,
+ SPI_MEM_OP_NO_DATA);
+
+ return spi_mem_exec_op(nor->spimem, &op);
+ }
+
+ return nor->write_reg(nor, SPINOR_OP_WRDI, NULL, 0);
+}
+
+static struct spi_nor *mtd_to_spi_nor(struct mtd_info *mtd)
+{
+ return mtd->priv;
+}
+
+
+static u8 spi_nor_convert_opcode(u8 opcode, const u8 table[][2], size_t size)
+{
+ size_t i;
+
+ for (i = 0; i < size; i++)
+ if (table[i][0] == opcode)
+ return table[i][1];
+
+ /* No conversion found, keep input op code. */
+ return opcode;
+}
+
+static u8 spi_nor_convert_3to4_read(u8 opcode)
+{
+ static const u8 spi_nor_3to4_read[][2] = {
+ { SPINOR_OP_READ, SPINOR_OP_READ_4B },
+ { SPINOR_OP_READ_FAST, SPINOR_OP_READ_FAST_4B },
+ { SPINOR_OP_READ_1_1_2, SPINOR_OP_READ_1_1_2_4B },
+ { SPINOR_OP_READ_1_2_2, SPINOR_OP_READ_1_2_2_4B },
+ { SPINOR_OP_READ_1_1_4, SPINOR_OP_READ_1_1_4_4B },
+ { SPINOR_OP_READ_1_4_4, SPINOR_OP_READ_1_4_4_4B },
+ { SPINOR_OP_READ_1_1_8, SPINOR_OP_READ_1_1_8_4B },
+ { SPINOR_OP_READ_1_8_8, SPINOR_OP_READ_1_8_8_4B },
+
+ { SPINOR_OP_READ_1_1_1_DTR, SPINOR_OP_READ_1_1_1_DTR_4B },
+ { SPINOR_OP_READ_1_2_2_DTR, SPINOR_OP_READ_1_2_2_DTR_4B },
+ { SPINOR_OP_READ_1_4_4_DTR, SPINOR_OP_READ_1_4_4_DTR_4B },
+ };
+
+ return spi_nor_convert_opcode(opcode, spi_nor_3to4_read,
+ ARRAY_SIZE(spi_nor_3to4_read));
+}
+
+static u8 spi_nor_convert_3to4_program(u8 opcode)
+{
+ static const u8 spi_nor_3to4_program[][2] = {
+ { SPINOR_OP_PP, SPINOR_OP_PP_4B },
+ { SPINOR_OP_PP_1_1_4, SPINOR_OP_PP_1_1_4_4B },
+ { SPINOR_OP_PP_1_4_4, SPINOR_OP_PP_1_4_4_4B },
+ { SPINOR_OP_PP_1_1_8, SPINOR_OP_PP_1_1_8_4B },
+ { SPINOR_OP_PP_1_8_8, SPINOR_OP_PP_1_8_8_4B },
+ };
+
+ return spi_nor_convert_opcode(opcode, spi_nor_3to4_program,
+ ARRAY_SIZE(spi_nor_3to4_program));
+}
+
+static u8 spi_nor_convert_3to4_erase(u8 opcode)
+{
+ static const u8 spi_nor_3to4_erase[][2] = {
+ { SPINOR_OP_BE_4K, SPINOR_OP_BE_4K_4B },
+ { SPINOR_OP_BE_32K, SPINOR_OP_BE_32K_4B },
+ { SPINOR_OP_SE, SPINOR_OP_SE_4B },
+ };
+
+ return spi_nor_convert_opcode(opcode, spi_nor_3to4_erase,
+ ARRAY_SIZE(spi_nor_3to4_erase));
+}
+
+static void spi_nor_set_4byte_opcodes(struct spi_nor *nor)
+{
+ nor->read_opcode = spi_nor_convert_3to4_read(nor->read_opcode);
+ nor->program_opcode = spi_nor_convert_3to4_program(nor->program_opcode);
+ nor->erase_opcode = spi_nor_convert_3to4_erase(nor->erase_opcode);
+
+ if (!spi_nor_has_uniform_erase(nor)) {
+ struct spi_nor_erase_map *map = &nor->params.erase_map;
+ struct spi_nor_erase_type *erase;
+ int i;
+
+ for (i = 0; i < SNOR_ERASE_TYPE_MAX; i++) {
+ erase = &map->erase_type[i];
+ erase->opcode =
+ spi_nor_convert_3to4_erase(erase->opcode);
+ }
+ }
+}
+
+static int macronix_set_4byte(struct spi_nor *nor, bool enable)
+{
+ if (nor->spimem) {
+ struct spi_mem_op op =
+ SPI_MEM_OP(SPI_MEM_OP_CMD(enable ?
+ SPINOR_OP_EN4B :
+ SPINOR_OP_EX4B,
+ 1),
+ SPI_MEM_OP_NO_ADDR,
+ SPI_MEM_OP_NO_DUMMY,
+ SPI_MEM_OP_NO_DATA);
+
+ return spi_mem_exec_op(nor->spimem, &op);
+ }
+
+ return nor->write_reg(nor, enable ? SPINOR_OP_EN4B : SPINOR_OP_EX4B,
+ NULL, 0);
+}
+
+static int st_micron_set_4byte(struct spi_nor *nor, bool enable)
+{
+ int ret;
+
+ write_enable(nor);
+ ret = macronix_set_4byte(nor, enable);
+ write_disable(nor);
+
+ return ret;
+}
+
+static int spansion_set_4byte(struct spi_nor *nor, bool enable)
+{
+ nor->bouncebuf[0] = enable << 7;
+
+ if (nor->spimem) {
+ struct spi_mem_op op =
+ SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_BRWR, 1),
+ SPI_MEM_OP_NO_ADDR,
+ SPI_MEM_OP_NO_DUMMY,
+ SPI_MEM_OP_DATA_OUT(1, nor->bouncebuf, 1));
+
+ return spi_mem_exec_op(nor->spimem, &op);
+ }
+
+ return nor->write_reg(nor, SPINOR_OP_BRWR, nor->bouncebuf, 1);
+}
+
+static int spi_nor_write_ear(struct spi_nor *nor, u8 ear)
+{
+ nor->bouncebuf[0] = ear;
+
+ if (nor->spimem) {
+ struct spi_mem_op op =
+ SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WREAR, 1),
+ SPI_MEM_OP_NO_ADDR,
+ SPI_MEM_OP_NO_DUMMY,
+ SPI_MEM_OP_DATA_OUT(1, nor->bouncebuf, 1));
+
+ return spi_mem_exec_op(nor->spimem, &op);
+ }
+
+ return nor->write_reg(nor, SPINOR_OP_WREAR, nor->bouncebuf, 1);
+}
+
+static int winbond_set_4byte(struct spi_nor *nor, bool enable)
+{
+ int ret;
+
+ ret = macronix_set_4byte(nor, enable);
+ if (ret || enable)
+ return ret;
+
+ /*
+ * On Winbond W25Q256FV, leaving 4byte mode causes the Extended Address
+ * Register to be set to 1, so all 3-byte-address reads come from the
+ * second 16M. We must clear the register to enable normal behavior.
+ */
+ write_enable(nor);
+ ret = spi_nor_write_ear(nor, 0);
+ write_disable(nor);
+
+ return ret;
+}
+
+static int spi_nor_xread_sr(struct spi_nor *nor, u8 *sr)
+{
+ if (nor->spimem) {
+ struct spi_mem_op op =
+ SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_XRDSR, 1),
+ SPI_MEM_OP_NO_ADDR,
+ SPI_MEM_OP_NO_DUMMY,
+ SPI_MEM_OP_DATA_IN(1, sr, 1));
+
+ return spi_mem_exec_op(nor->spimem, &op);
+ }
+
+ return nor->read_reg(nor, SPINOR_OP_XRDSR, sr, 1);
+}
+
+static int s3an_sr_ready(struct spi_nor *nor)
+{
+ int ret;
+
+ ret = spi_nor_xread_sr(nor, nor->bouncebuf);
+ if (ret < 0) {
+ dev_err(nor->dev, "error %d reading XRDSR\n", (int) ret);
+ return ret;
+ }
+
+ return !!(nor->bouncebuf[0] & XSR_RDY);
+}
+
+static int spi_nor_clear_sr(struct spi_nor *nor)
+{
+ if (nor->spimem) {
+ struct spi_mem_op op =
+ SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_CLSR, 1),
+ SPI_MEM_OP_NO_ADDR,
+ SPI_MEM_OP_NO_DUMMY,
+ SPI_MEM_OP_NO_DATA);
+
+ return spi_mem_exec_op(nor->spimem, &op);
+ }
+
+ return nor->write_reg(nor, SPINOR_OP_CLSR, NULL, 0);
+}
+
+static int spi_nor_sr_ready(struct spi_nor *nor)
+{
+ int sr = read_sr(nor);
+ if (sr < 0)
+ return sr;
+
+ if (nor->flags & SNOR_F_USE_CLSR && sr & (SR_E_ERR | SR_P_ERR)) {
+ if (sr & SR_E_ERR)
+ dev_err(nor->dev, "Erase Error occurred\n");
+ else
+ dev_err(nor->dev, "Programming Error occurred\n");
+
+ spi_nor_clear_sr(nor);
+ return -EIO;
+ }
+
+ return !(sr & SR_WIP);
+}
+
+static int spi_nor_clear_fsr(struct spi_nor *nor)
+{
+ if (nor->spimem) {
+ struct spi_mem_op op =
+ SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_CLFSR, 1),
+ SPI_MEM_OP_NO_ADDR,
+ SPI_MEM_OP_NO_DUMMY,
+ SPI_MEM_OP_NO_DATA);
+
+ return spi_mem_exec_op(nor->spimem, &op);
+ }
+
+ return nor->write_reg(nor, SPINOR_OP_CLFSR, NULL, 0);
+}
+
+static int spi_nor_fsr_ready(struct spi_nor *nor)
+{
+ int fsr = read_fsr(nor);
+ if (fsr < 0)
+ return fsr;
+
+ if (fsr & (FSR_E_ERR | FSR_P_ERR)) {
+ if (fsr & FSR_E_ERR)
+ dev_err(nor->dev, "Erase operation failed.\n");
+ else
+ dev_err(nor->dev, "Program operation failed.\n");
+
+ if (fsr & FSR_PT_ERR)
+ dev_err(nor->dev,
+ "Attempted to modify a protected sector.\n");
+
+ spi_nor_clear_fsr(nor);
+ return -EIO;
+ }
+
+ return fsr & FSR_READY;
+}
+
+static int spi_nor_ready(struct spi_nor *nor)
+{
+ int sr, fsr;
+
+ if (nor->flags & SNOR_F_READY_XSR_RDY)
+ sr = s3an_sr_ready(nor);
+ else
+ sr = spi_nor_sr_ready(nor);
+ if (sr < 0)
+ return sr;
+ fsr = nor->flags & SNOR_F_USE_FSR ? spi_nor_fsr_ready(nor) : 1;
+ if (fsr < 0)
+ return fsr;
+ return sr && fsr;
+}
+
+/*
+ * Service routine to read status register until ready, or timeout occurs.
+ * Returns non-zero if error.
+ */
+static int spi_nor_wait_till_ready_with_timeout(struct spi_nor *nor,
+ unsigned long timeout_jiffies)
+{
+ unsigned long deadline;
+ int timeout = 0, ret;
+
+ deadline = jiffies + timeout_jiffies;
+
+ while (!timeout) {
+ if (time_after_eq(jiffies, deadline))
+ timeout = 1;
+
+ ret = spi_nor_ready(nor);
+ if (ret < 0)
+ return ret;
+ if (ret)
+ return 0;
+
+ cond_resched();
+ }
+
+ dev_err(nor->dev, "flash operation timed out\n");
+
+ return -ETIMEDOUT;
+}
+
+static int spi_nor_wait_till_ready(struct spi_nor *nor)
+{
+ return spi_nor_wait_till_ready_with_timeout(nor,
+ DEFAULT_READY_WAIT_JIFFIES);
+}
+
+/*
+ * Erase the whole flash memory
+ *
+ * Returns 0 if successful, non-zero otherwise.
+ */
+static int erase_chip(struct spi_nor *nor)
+{
+ dev_dbg(nor->dev, " %lldKiB\n", (long long)(nor->mtd.size >> 10));
+
+ if (nor->spimem) {
+ struct spi_mem_op op =
+ SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_CHIP_ERASE, 1),
+ SPI_MEM_OP_NO_ADDR,
+ SPI_MEM_OP_NO_DUMMY,
+ SPI_MEM_OP_NO_DATA);
+
+ return spi_mem_exec_op(nor->spimem, &op);
+ }
+
+ return nor->write_reg(nor, SPINOR_OP_CHIP_ERASE, NULL, 0);
+}
+
+static int spi_nor_lock_and_prep(struct spi_nor *nor, enum spi_nor_ops ops)
+{
+ int ret = 0;
+
+ mutex_lock(&nor->lock);
+
+ if (nor->prepare) {
+ ret = nor->prepare(nor, ops);
+ if (ret) {
+ dev_err(nor->dev, "failed in the preparation.\n");
+ mutex_unlock(&nor->lock);
+ return ret;
+ }
+ }
+ return ret;
+}
+
+static void spi_nor_unlock_and_unprep(struct spi_nor *nor, enum spi_nor_ops ops)
+{
+ if (nor->unprepare)
+ nor->unprepare(nor, ops);
+ mutex_unlock(&nor->lock);
+}
+
+/*
+ * This code converts an address to the Default Address Mode, that has non
+ * power of two page sizes. We must support this mode because it is the default
+ * mode supported by Xilinx tools, it can access the whole flash area and
+ * changing over to the Power-of-two mode is irreversible and corrupts the
+ * original data.
+ * Addr can safely be unsigned int, the biggest S3AN device is smaller than
+ * 4 MiB.
+ */
+static u32 s3an_convert_addr(struct spi_nor *nor, u32 addr)
+{
+ u32 offset, page;
+
+ offset = addr % nor->page_size;
+ page = addr / nor->page_size;
+ page <<= (nor->page_size > 512) ? 10 : 9;
+
+ return page | offset;
+}
+
+static u32 spi_nor_convert_addr(struct spi_nor *nor, loff_t addr)
+{
+ if (!nor->params.convert_addr)
+ return addr;
+
+ return nor->params.convert_addr(nor, addr);
+}
+
+/*
+ * Initiate the erasure of a single sector
+ */
+static int spi_nor_erase_sector(struct spi_nor *nor, u32 addr)
+{
+ int i;
+
+ addr = spi_nor_convert_addr(nor, addr);
+
+ if (nor->erase)
+ return nor->erase(nor, addr);
+
+ if (nor->spimem) {
+ struct spi_mem_op op =
+ SPI_MEM_OP(SPI_MEM_OP_CMD(nor->erase_opcode, 1),
+ SPI_MEM_OP_ADDR(nor->addr_width, addr, 1),
+ SPI_MEM_OP_NO_DUMMY,
+ SPI_MEM_OP_NO_DATA);
+
+ return spi_mem_exec_op(nor->spimem, &op);
+ }
+
+ /*
+ * Default implementation, if driver doesn't have a specialized HW
+ * control
+ */
+ for (i = nor->addr_width - 1; i >= 0; i--) {
+ nor->bouncebuf[i] = addr & 0xff;
+ addr >>= 8;
+ }
+
+ return nor->write_reg(nor, nor->erase_opcode, nor->bouncebuf,
+ nor->addr_width);
+}
+
+/**
+ * spi_nor_div_by_erase_size() - calculate remainder and update new dividend
+ * @erase: pointer to a structure that describes a SPI NOR erase type
+ * @dividend: dividend value
+ * @remainder: pointer to u32 remainder (will be updated)
+ *
+ * Return: the result of the division
+ */
+static u64 spi_nor_div_by_erase_size(const struct spi_nor_erase_type *erase,
+ u64 dividend, u32 *remainder)
+{
+ /* JEDEC JESD216B Standard imposes erase sizes to be power of 2. */
+ *remainder = (u32)dividend & erase->size_mask;
+ return dividend >> erase->size_shift;
+}
+
+/**
+ * spi_nor_find_best_erase_type() - find the best erase type for the given
+ * offset in the serial flash memory and the
+ * number of bytes to erase. The region in
+ * which the address fits is expected to be
+ * provided.
+ * @map: the erase map of the SPI NOR
+ * @region: pointer to a structure that describes a SPI NOR erase region
+ * @addr: offset in the serial flash memory
+ * @len: number of bytes to erase
+ *
+ * Return: a pointer to the best fitted erase type, NULL otherwise.
+ */
+static const struct spi_nor_erase_type *
+spi_nor_find_best_erase_type(const struct spi_nor_erase_map *map,
+ const struct spi_nor_erase_region *region,
+ u64 addr, u32 len)
+{
+ const struct spi_nor_erase_type *erase;
+ u32 rem;
+ int i;
+ u8 erase_mask = region->offset & SNOR_ERASE_TYPE_MASK;
+
+ /*
+ * Erase types are ordered by size, with the smallest erase type at
+ * index 0.
+ */
+ for (i = SNOR_ERASE_TYPE_MAX - 1; i >= 0; i--) {
+ /* Does the erase region support the tested erase type? */
+ if (!(erase_mask & BIT(i)))
+ continue;
+
+ erase = &map->erase_type[i];
+
+ /* Don't erase more than what the user has asked for. */
+ if (erase->size > len)
+ continue;
+
+ /* Alignment is not mandatory for overlaid regions */
+ if (region->offset & SNOR_OVERLAID_REGION)
+ return erase;
+
+ spi_nor_div_by_erase_size(erase, addr, &rem);
+ if (rem)
+ continue;
+ else
+ return erase;
+ }
+
+ return NULL;
+}
+
+/**
+ * spi_nor_region_next() - get the next spi nor region
+ * @region: pointer to a structure that describes a SPI NOR erase region
+ *
+ * Return: the next spi nor region or NULL if last region.
+ */
+static struct spi_nor_erase_region *
+spi_nor_region_next(struct spi_nor_erase_region *region)
+{
+ if (spi_nor_region_is_last(region))
+ return NULL;
+ region++;
+ return region;
+}
+
+/**
+ * spi_nor_find_erase_region() - find the region of the serial flash memory in
+ * which the offset fits
+ * @map: the erase map of the SPI NOR
+ * @addr: offset in the serial flash memory
+ *
+ * Return: a pointer to the spi_nor_erase_region struct, ERR_PTR(-errno)
+ * otherwise.
+ */
+static struct spi_nor_erase_region *
+spi_nor_find_erase_region(const struct spi_nor_erase_map *map, u64 addr)
+{
+ struct spi_nor_erase_region *region = map->regions;
+ u64 region_start = region->offset & ~SNOR_ERASE_FLAGS_MASK;
+ u64 region_end = region_start + region->size;
+
+ while (addr < region_start || addr >= region_end) {
+ region = spi_nor_region_next(region);
+ if (!region)
+ return ERR_PTR(-EINVAL);
+
+ region_start = region->offset & ~SNOR_ERASE_FLAGS_MASK;
+ region_end = region_start + region->size;
+ }
+
+ return region;
+}
+
+/**
+ * spi_nor_init_erase_cmd() - initialize an erase command
+ * @region: pointer to a structure that describes a SPI NOR erase region
+ * @erase: pointer to a structure that describes a SPI NOR erase type
+ *
+ * Return: the pointer to the allocated erase command, ERR_PTR(-errno)
+ * otherwise.
+ */
+static struct spi_nor_erase_command *
+spi_nor_init_erase_cmd(const struct spi_nor_erase_region *region,
+ const struct spi_nor_erase_type *erase)
+{
+ struct spi_nor_erase_command *cmd;
+
+ cmd = kmalloc(sizeof(*cmd), GFP_KERNEL);
+ if (!cmd)
+ return ERR_PTR(-ENOMEM);
+
+ INIT_LIST_HEAD(&cmd->list);
+ cmd->opcode = erase->opcode;
+ cmd->count = 1;
+
+ if (region->offset & SNOR_OVERLAID_REGION)
+ cmd->size = region->size;
+ else
+ cmd->size = erase->size;
+
+ return cmd;
+}
+
+/**
+ * spi_nor_destroy_erase_cmd_list() - destroy erase command list
+ * @erase_list: list of erase commands
+ */
+static void spi_nor_destroy_erase_cmd_list(struct list_head *erase_list)
+{
+ struct spi_nor_erase_command *cmd, *next;
+
+ list_for_each_entry_safe(cmd, next, erase_list, list) {
+ list_del(&cmd->list);
+ kfree(cmd);
+ }
+}
+
+/**
+ * spi_nor_init_erase_cmd_list() - initialize erase command list
+ * @nor: pointer to a 'struct spi_nor'
+ * @erase_list: list of erase commands to be executed once we validate that the
+ * erase can be performed
+ * @addr: offset in the serial flash memory
+ * @len: number of bytes to erase
+ *
+ * Builds the list of best fitted erase commands and verifies if the erase can
+ * be performed.
+ *
+ * Return: 0 on success, -errno otherwise.
+ */
+static int spi_nor_init_erase_cmd_list(struct spi_nor *nor,
+ struct list_head *erase_list,
+ u64 addr, u32 len)
+{
+ const struct spi_nor_erase_map *map = &nor->params.erase_map;
+ const struct spi_nor_erase_type *erase, *prev_erase = NULL;
+ struct spi_nor_erase_region *region;
+ struct spi_nor_erase_command *cmd = NULL;
+ u64 region_end;
+ int ret = -EINVAL;
+
+ region = spi_nor_find_erase_region(map, addr);
+ if (IS_ERR(region))
+ return PTR_ERR(region);
+
+ region_end = spi_nor_region_end(region);
+
+ while (len) {
+ erase = spi_nor_find_best_erase_type(map, region, addr, len);
+ if (!erase)
+ goto destroy_erase_cmd_list;
+
+ if (prev_erase != erase ||
+ region->offset & SNOR_OVERLAID_REGION) {
+ cmd = spi_nor_init_erase_cmd(region, erase);
+ if (IS_ERR(cmd)) {
+ ret = PTR_ERR(cmd);
+ goto destroy_erase_cmd_list;
+ }
+
+ list_add_tail(&cmd->list, erase_list);
+ } else {
+ cmd->count++;
+ }
+
+ addr += cmd->size;
+ len -= cmd->size;
+
+ if (len && addr >= region_end) {
+ region = spi_nor_region_next(region);
+ if (!region)
+ goto destroy_erase_cmd_list;
+ region_end = spi_nor_region_end(region);
+ }
+
+ prev_erase = erase;
+ }
+
+ return 0;
+
+destroy_erase_cmd_list:
+ spi_nor_destroy_erase_cmd_list(erase_list);
+ return ret;
+}
+
+/**
+ * spi_nor_erase_multi_sectors() - perform a non-uniform erase
+ * @nor: pointer to a 'struct spi_nor'
+ * @addr: offset in the serial flash memory
+ * @len: number of bytes to erase
+ *
+ * Build a list of best fitted erase commands and execute it once we validate
+ * that the erase can be performed.
+ *
+ * Return: 0 on success, -errno otherwise.
+ */
+static int spi_nor_erase_multi_sectors(struct spi_nor *nor, u64 addr, u32 len)
+{
+ LIST_HEAD(erase_list);
+ struct spi_nor_erase_command *cmd, *next;
+ int ret;
+
+ ret = spi_nor_init_erase_cmd_list(nor, &erase_list, addr, len);
+ if (ret)
+ return ret;
+
+ list_for_each_entry_safe(cmd, next, &erase_list, list) {
+ nor->erase_opcode = cmd->opcode;
+ while (cmd->count) {
+ write_enable(nor);
+
+ ret = spi_nor_erase_sector(nor, addr);
+ if (ret)
+ goto destroy_erase_cmd_list;
+
+ addr += cmd->size;
+ cmd->count--;
+
+ ret = spi_nor_wait_till_ready(nor);
+ if (ret)
+ goto destroy_erase_cmd_list;
+ }
+ list_del(&cmd->list);
+ kfree(cmd);
+ }
+
+ return 0;
+
+destroy_erase_cmd_list:
+ spi_nor_destroy_erase_cmd_list(&erase_list);
+ return ret;
+}
+
+/*
+ * Erase an address range on the nor chip. The address range may extend
+ * one or more erase sectors. Return an error is there is a problem erasing.
+ */
+static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr)
+{
+ struct spi_nor *nor = mtd_to_spi_nor(mtd);
+ u32 addr, len;
+ uint32_t rem;
+ int ret;
+
+ dev_dbg(nor->dev, "at 0x%llx, len %lld\n", (long long)instr->addr,
+ (long long)instr->len);
+
+ if (spi_nor_has_uniform_erase(nor)) {
+ div_u64_rem(instr->len, mtd->erasesize, &rem);
+ if (rem)
+ return -EINVAL;
+ }
+
+ addr = instr->addr;
+ len = instr->len;
+
+ ret = spi_nor_lock_and_prep(nor, SPI_NOR_OPS_ERASE);
+ if (ret)
+ return ret;
+
+ /* whole-chip erase? */
+ if (len == mtd->size && !(nor->flags & SNOR_F_NO_OP_CHIP_ERASE)) {
+ unsigned long timeout;
+
+ write_enable(nor);
+
+ if (erase_chip(nor)) {
+ ret = -EIO;
+ goto erase_err;
+ }
+
+ /*
+ * Scale the timeout linearly with the size of the flash, with
+ * a minimum calibrated to an old 2MB flash. We could try to
+ * pull these from CFI/SFDP, but these values should be good
+ * enough for now.
+ */
+ timeout = max(CHIP_ERASE_2MB_READY_WAIT_JIFFIES,
+ CHIP_ERASE_2MB_READY_WAIT_JIFFIES *
+ (unsigned long)(mtd->size / SZ_2M));
+ ret = spi_nor_wait_till_ready_with_timeout(nor, timeout);
+ if (ret)
+ goto erase_err;
+
+ /* REVISIT in some cases we could speed up erasing large regions
+ * by using SPINOR_OP_SE instead of SPINOR_OP_BE_4K. We may have set up
+ * to use "small sector erase", but that's not always optimal.
+ */
+
+ /* "sector"-at-a-time erase */
+ } else if (spi_nor_has_uniform_erase(nor)) {
+ while (len) {
+ write_enable(nor);
+
+ ret = spi_nor_erase_sector(nor, addr);
+ if (ret)
+ goto erase_err;
+
+ addr += mtd->erasesize;
+ len -= mtd->erasesize;
+
+ ret = spi_nor_wait_till_ready(nor);
+ if (ret)
+ goto erase_err;
+ }
+
+ /* erase multiple sectors */
+ } else {
+ ret = spi_nor_erase_multi_sectors(nor, addr, len);
+ if (ret)
+ goto erase_err;
+ }
+
+ write_disable(nor);
+
+erase_err:
+ spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_ERASE);
+
+ return ret;
+}
+
+/* Write status register and ensure bits in mask match written values */
+static int write_sr_and_check(struct spi_nor *nor, u8 status_new, u8 mask)
+{
+ int ret;
+
+ write_enable(nor);
+ ret = write_sr(nor, status_new);
+ if (ret)
+ return ret;
+
+ ret = spi_nor_wait_till_ready(nor);
+ if (ret)
+ return ret;
+
+ ret = read_sr(nor);
+ if (ret < 0)
+ return ret;
+
+ return ((ret & mask) != (status_new & mask)) ? -EIO : 0;
+}
+
+static void stm_get_locked_range(struct spi_nor *nor, u8 sr, loff_t *ofs,
+ uint64_t *len)
+{
+ struct mtd_info *mtd = &nor->mtd;
+ u8 mask = SR_BP2 | SR_BP1 | SR_BP0;
+ int shift = ffs(mask) - 1;
+ int pow;
+
+ if (!(sr & mask)) {
+ /* No protection */
+ *ofs = 0;
+ *len = 0;
+ } else {
+ pow = ((sr & mask) ^ mask) >> shift;
+ *len = mtd->size >> pow;
+ if (nor->flags & SNOR_F_HAS_SR_TB && sr & SR_TB)
+ *ofs = 0;
+ else
+ *ofs = mtd->size - *len;
+ }
+}
+
+/*
+ * Return 1 if the entire region is locked (if @locked is true) or unlocked (if
+ * @locked is false); 0 otherwise
+ */
+static int stm_check_lock_status_sr(struct spi_nor *nor, loff_t ofs, uint64_t len,
+ u8 sr, bool locked)
+{
+ loff_t lock_offs;
+ uint64_t lock_len;
+
+ if (!len)
+ return 1;
+
+ stm_get_locked_range(nor, sr, &lock_offs, &lock_len);
+
+ if (locked)
+ /* Requested range is a sub-range of locked range */
+ return (ofs + len <= lock_offs + lock_len) && (ofs >= lock_offs);
+ else
+ /* Requested range does not overlap with locked range */
+ return (ofs >= lock_offs + lock_len) || (ofs + len <= lock_offs);
+}
+
+static int stm_is_locked_sr(struct spi_nor *nor, loff_t ofs, uint64_t len,
+ u8 sr)
+{
+ return stm_check_lock_status_sr(nor, ofs, len, sr, true);
+}
+
+static int stm_is_unlocked_sr(struct spi_nor *nor, loff_t ofs, uint64_t len,
+ u8 sr)
+{
+ return stm_check_lock_status_sr(nor, ofs, len, sr, false);
+}
+
+/*
+ * Lock a region of the flash. Compatible with ST Micro and similar flash.
+ * Supports the block protection bits BP{0,1,2} in the status register
+ * (SR). Does not support these features found in newer SR bitfields:
+ * - SEC: sector/block protect - only handle SEC=0 (block protect)
+ * - CMP: complement protect - only support CMP=0 (range is not complemented)
+ *
+ * Support for the following is provided conditionally for some flash:
+ * - TB: top/bottom protect
+ *
+ * Sample table portion for 8MB flash (Winbond w25q64fw):
+ *
+ * SEC | TB | BP2 | BP1 | BP0 | Prot Length | Protected Portion
+ * --------------------------------------------------------------------------
+ * X | X | 0 | 0 | 0 | NONE | NONE
+ * 0 | 0 | 0 | 0 | 1 | 128 KB | Upper 1/64
+ * 0 | 0 | 0 | 1 | 0 | 256 KB | Upper 1/32
+ * 0 | 0 | 0 | 1 | 1 | 512 KB | Upper 1/16
+ * 0 | 0 | 1 | 0 | 0 | 1 MB | Upper 1/8
+ * 0 | 0 | 1 | 0 | 1 | 2 MB | Upper 1/4
+ * 0 | 0 | 1 | 1 | 0 | 4 MB | Upper 1/2
+ * X | X | 1 | 1 | 1 | 8 MB | ALL
+ * ------|-------|-------|-------|-------|---------------|-------------------
+ * 0 | 1 | 0 | 0 | 1 | 128 KB | Lower 1/64
+ * 0 | 1 | 0 | 1 | 0 | 256 KB | Lower 1/32
+ * 0 | 1 | 0 | 1 | 1 | 512 KB | Lower 1/16
+ * 0 | 1 | 1 | 0 | 0 | 1 MB | Lower 1/8
+ * 0 | 1 | 1 | 0 | 1 | 2 MB | Lower 1/4
+ * 0 | 1 | 1 | 1 | 0 | 4 MB | Lower 1/2
+ *
+ * Returns negative on errors, 0 on success.
+ */
+static int stm_lock(struct spi_nor *nor, loff_t ofs, uint64_t len)
+{
+ struct mtd_info *mtd = &nor->mtd;
+ int status_old, status_new;
+ u8 mask = SR_BP2 | SR_BP1 | SR_BP0;
+ u8 shift = ffs(mask) - 1, pow, val;
+ loff_t lock_len;
+ bool can_be_top = true, can_be_bottom = nor->flags & SNOR_F_HAS_SR_TB;
+ bool use_top;
+
+ status_old = read_sr(nor);
+ if (status_old < 0)
+ return status_old;
+
+ /* If nothing in our range is unlocked, we don't need to do anything */
+ if (stm_is_locked_sr(nor, ofs, len, status_old))
+ return 0;
+
+ /* If anything below us is unlocked, we can't use 'bottom' protection */
+ if (!stm_is_locked_sr(nor, 0, ofs, status_old))
+ can_be_bottom = false;
+
+ /* If anything above us is unlocked, we can't use 'top' protection */
+ if (!stm_is_locked_sr(nor, ofs + len, mtd->size - (ofs + len),
+ status_old))
+ can_be_top = false;
+
+ if (!can_be_bottom && !can_be_top)
+ return -EINVAL;
+
+ /* Prefer top, if both are valid */
+ use_top = can_be_top;
+
+ /* lock_len: length of region that should end up locked */
+ if (use_top)
+ lock_len = mtd->size - ofs;
+ else
+ lock_len = ofs + len;
+
+ /*
+ * Need smallest pow such that:
+ *
+ * 1 / (2^pow) <= (len / size)
+ *
+ * so (assuming power-of-2 size) we do:
+ *
+ * pow = ceil(log2(size / len)) = log2(size) - floor(log2(len))
+ */
+ pow = ilog2(mtd->size) - ilog2(lock_len);
+ val = mask - (pow << shift);
+ if (val & ~mask)
+ return -EINVAL;
+ /* Don't "lock" with no region! */
+ if (!(val & mask))
+ return -EINVAL;
+
+ status_new = (status_old & ~mask & ~SR_TB) | val;
+
+ /* Disallow further writes if WP pin is asserted */
+ status_new |= SR_SRWD;
+
+ if (!use_top)
+ status_new |= SR_TB;
+
+ /* Don't bother if they're the same */
+ if (status_new == status_old)
+ return 0;
+
+ /* Only modify protection if it will not unlock other areas */
+ if ((status_new & mask) < (status_old & mask))
+ return -EINVAL;
+
+ return write_sr_and_check(nor, status_new, mask);
+}
+
+/*
+ * Unlock a region of the flash. See stm_lock() for more info
+ *
+ * Returns negative on errors, 0 on success.
+ */
+static int stm_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len)
+{
+ struct mtd_info *mtd = &nor->mtd;
+ int status_old, status_new;
+ u8 mask = SR_BP2 | SR_BP1 | SR_BP0;
+ u8 shift = ffs(mask) - 1, pow, val;
+ loff_t lock_len;
+ bool can_be_top = true, can_be_bottom = nor->flags & SNOR_F_HAS_SR_TB;
+ bool use_top;
+
+ status_old = read_sr(nor);
+ if (status_old < 0)
+ return status_old;
+
+ /* If nothing in our range is locked, we don't need to do anything */
+ if (stm_is_unlocked_sr(nor, ofs, len, status_old))
+ return 0;
+
+ /* If anything below us is locked, we can't use 'top' protection */
+ if (!stm_is_unlocked_sr(nor, 0, ofs, status_old))
+ can_be_top = false;
+
+ /* If anything above us is locked, we can't use 'bottom' protection */
+ if (!stm_is_unlocked_sr(nor, ofs + len, mtd->size - (ofs + len),
+ status_old))
+ can_be_bottom = false;
+
+ if (!can_be_bottom && !can_be_top)
+ return -EINVAL;
+
+ /* Prefer top, if both are valid */
+ use_top = can_be_top;
+
+ /* lock_len: length of region that should remain locked */
+ if (use_top)
+ lock_len = mtd->size - (ofs + len);
+ else
+ lock_len = ofs;
+
+ /*
+ * Need largest pow such that:
+ *
+ * 1 / (2^pow) >= (len / size)
+ *
+ * so (assuming power-of-2 size) we do:
+ *
+ * pow = floor(log2(size / len)) = log2(size) - ceil(log2(len))
+ */
+ pow = ilog2(mtd->size) - order_base_2(lock_len);
+ if (lock_len == 0) {
+ val = 0; /* fully unlocked */
+ } else {
+ val = mask - (pow << shift);
+ /* Some power-of-two sizes are not supported */
+ if (val & ~mask)
+ return -EINVAL;
+ }
+
+ status_new = (status_old & ~mask & ~SR_TB) | val;
+
+ /* Don't protect status register if we're fully unlocked */
+ if (lock_len == 0)
+ status_new &= ~SR_SRWD;
+
+ if (!use_top)
+ status_new |= SR_TB;
+
+ /* Don't bother if they're the same */
+ if (status_new == status_old)
+ return 0;
+
+ /* Only modify protection if it will not lock other areas */
+ if ((status_new & mask) > (status_old & mask))
+ return -EINVAL;
+
+ return write_sr_and_check(nor, status_new, mask);
+}
+
+/*
+ * Check if a region of the flash is (completely) locked. See stm_lock() for
+ * more info.
+ *
+ * Returns 1 if entire region is locked, 0 if any portion is unlocked, and
+ * negative on errors.
+ */
+static int stm_is_locked(struct spi_nor *nor, loff_t ofs, uint64_t len)
+{
+ int status;
+
+ status = read_sr(nor);
+ if (status < 0)
+ return status;
+
+ return stm_is_locked_sr(nor, ofs, len, status);
+}
+
+static const struct spi_nor_locking_ops stm_locking_ops = {
+ .lock = stm_lock,
+ .unlock = stm_unlock,
+ .is_locked = stm_is_locked,
+};
+
+static int spi_nor_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
+{
+ struct spi_nor *nor = mtd_to_spi_nor(mtd);
+ int ret;
+
+ ret = spi_nor_lock_and_prep(nor, SPI_NOR_OPS_LOCK);
+ if (ret)
+ return ret;
+
+ ret = nor->params.locking_ops->lock(nor, ofs, len);
+
+ spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_UNLOCK);
+ return ret;
+}
+
+static int spi_nor_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
+{
+ struct spi_nor *nor = mtd_to_spi_nor(mtd);
+ int ret;
+
+ ret = spi_nor_lock_and_prep(nor, SPI_NOR_OPS_UNLOCK);
+ if (ret)
+ return ret;
+
+ ret = nor->params.locking_ops->unlock(nor, ofs, len);
+
+ spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_LOCK);
+ return ret;
+}
+
+static int spi_nor_is_locked(struct mtd_info *mtd, loff_t ofs, uint64_t len)
+{
+ struct spi_nor *nor = mtd_to_spi_nor(mtd);
+ int ret;
+
+ ret = spi_nor_lock_and_prep(nor, SPI_NOR_OPS_UNLOCK);
+ if (ret)
+ return ret;
+
+ ret = nor->params.locking_ops->is_locked(nor, ofs, len);
+
+ spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_LOCK);
+ return ret;
+}
+
+/*
+ * Write status Register and configuration register with 2 bytes
+ * The first byte will be written to the status register, while the
+ * second byte will be written to the configuration register.
+ * Return negative if error occurred.
+ */
+static int write_sr_cr(struct spi_nor *nor, u8 *sr_cr)
+{
+ int ret;
+
+ write_enable(nor);
+
+ if (nor->spimem) {
+ struct spi_mem_op op =
+ SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WRSR, 1),
+ SPI_MEM_OP_NO_ADDR,
+ SPI_MEM_OP_NO_DUMMY,
+ SPI_MEM_OP_DATA_OUT(2, sr_cr, 1));
+
+ ret = spi_mem_exec_op(nor->spimem, &op);
+ } else {
+ ret = nor->write_reg(nor, SPINOR_OP_WRSR, sr_cr, 2);
+ }
+
+ if (ret < 0) {
+ dev_err(nor->dev,
+ "error while writing configuration register\n");
+ return -EINVAL;
+ }
+
+ ret = spi_nor_wait_till_ready(nor);
+ if (ret) {
+ dev_err(nor->dev,
+ "timeout while writing configuration register\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+/**
+ * macronix_quad_enable() - set QE bit in Status Register.
+ * @nor: pointer to a 'struct spi_nor'
+ *
+ * Set the Quad Enable (QE) bit in the Status Register.
+ *
+ * bit 6 of the Status Register is the QE bit for Macronix like QSPI memories.
+ *
+ * Return: 0 on success, -errno otherwise.
+ */
+static int macronix_quad_enable(struct spi_nor *nor)
+{
+ int ret, val;
+
+ val = read_sr(nor);
+ if (val < 0)
+ return val;
+ if (val & SR_QUAD_EN_MX)
+ return 0;
+
+ write_enable(nor);
+
+ write_sr(nor, val | SR_QUAD_EN_MX);
+
+ ret = spi_nor_wait_till_ready(nor);
+ if (ret)
+ return ret;
+
+ ret = read_sr(nor);
+ if (!(ret > 0 && (ret & SR_QUAD_EN_MX))) {
+ dev_err(nor->dev, "Macronix Quad bit not set\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/**
+ * spansion_quad_enable() - set QE bit in Configuraiton Register.
+ * @nor: pointer to a 'struct spi_nor'
+ *
+ * Set the Quad Enable (QE) bit in the Configuration Register.
+ * This function is kept for legacy purpose because it has been used for a
+ * long time without anybody complaining but it should be considered as
+ * deprecated and maybe buggy.
+ * First, this function doesn't care about the previous values of the Status
+ * and Configuration Registers when it sets the QE bit (bit 1) in the
+ * Configuration Register: all other bits are cleared, which may have unwanted
+ * side effects like removing some block protections.
+ * Secondly, it uses the Read Configuration Register (35h) instruction though
+ * some very old and few memories don't support this instruction. If a pull-up
+ * resistor is present on the MISO/IO1 line, we might still be able to pass the
+ * "read back" test because the QSPI memory doesn't recognize the command,
+ * so leaves the MISO/IO1 line state unchanged, hence read_cr() returns 0xFF.
+ *
+ * bit 1 of the Configuration Register is the QE bit for Spansion like QSPI
+ * memories.
+ *
+ * Return: 0 on success, -errno otherwise.
+ */
+static int spansion_quad_enable(struct spi_nor *nor)
+{
+ u8 *sr_cr = nor->bouncebuf;
+ int ret;
+
+ sr_cr[0] = 0;
+ sr_cr[1] = CR_QUAD_EN_SPAN;
+ ret = write_sr_cr(nor, sr_cr);
+ if (ret)
+ return ret;
+
+ /* read back and check it */
+ ret = read_cr(nor);
+ if (!(ret > 0 && (ret & CR_QUAD_EN_SPAN))) {
+ dev_err(nor->dev, "Spansion Quad bit not set\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/**
+ * spansion_no_read_cr_quad_enable() - set QE bit in Configuration Register.
+ * @nor: pointer to a 'struct spi_nor'
+ *
+ * Set the Quad Enable (QE) bit in the Configuration Register.
+ * This function should be used with QSPI memories not supporting the Read
+ * Configuration Register (35h) instruction.
+ *
+ * bit 1 of the Configuration Register is the QE bit for Spansion like QSPI
+ * memories.
+ *
+ * Return: 0 on success, -errno otherwise.
+ */
+static int spansion_no_read_cr_quad_enable(struct spi_nor *nor)
+{
+ u8 *sr_cr = nor->bouncebuf;
+ int ret;
+
+ /* Keep the current value of the Status Register. */
+ ret = read_sr(nor);
+ if (ret < 0) {
+ dev_err(nor->dev, "error while reading status register\n");
+ return -EINVAL;
+ }
+ sr_cr[0] = ret;
+ sr_cr[1] = CR_QUAD_EN_SPAN;
+
+ return write_sr_cr(nor, sr_cr);
+}
+
+/**
+ * spansion_read_cr_quad_enable() - set QE bit in Configuration Register.
+ * @nor: pointer to a 'struct spi_nor'
+ *
+ * Set the Quad Enable (QE) bit in the Configuration Register.
+ * This function should be used with QSPI memories supporting the Read
+ * Configuration Register (35h) instruction.
+ *
+ * bit 1 of the Configuration Register is the QE bit for Spansion like QSPI
+ * memories.
+ *
+ * Return: 0 on success, -errno otherwise.
+ */
+static int spansion_read_cr_quad_enable(struct spi_nor *nor)
+{
+ struct device *dev = nor->dev;
+ u8 *sr_cr = nor->bouncebuf;
+ int ret;
+
+ /* Check current Quad Enable bit value. */
+ ret = read_cr(nor);
+ if (ret < 0) {
+ dev_err(dev, "error while reading configuration register\n");
+ return -EINVAL;
+ }
+
+ if (ret & CR_QUAD_EN_SPAN)
+ return 0;
+
+ sr_cr[1] = ret | CR_QUAD_EN_SPAN;
+
+ /* Keep the current value of the Status Register. */
+ ret = read_sr(nor);
+ if (ret < 0) {
+ dev_err(dev, "error while reading status register\n");
+ return -EINVAL;
+ }
+ sr_cr[0] = ret;
+
+ ret = write_sr_cr(nor, sr_cr);
+ if (ret)
+ return ret;
+
+ /* Read back and check it. */
+ ret = read_cr(nor);
+ if (!(ret > 0 && (ret & CR_QUAD_EN_SPAN))) {
+ dev_err(nor->dev, "Spansion Quad bit not set\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int spi_nor_write_sr2(struct spi_nor *nor, u8 *sr2)
+{
+ if (nor->spimem) {
+ struct spi_mem_op op =
+ SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WRSR2, 1),
+ SPI_MEM_OP_NO_ADDR,
+ SPI_MEM_OP_NO_DUMMY,
+ SPI_MEM_OP_DATA_OUT(1, sr2, 1));
+
+ return spi_mem_exec_op(nor->spimem, &op);
+ }
+
+ return nor->write_reg(nor, SPINOR_OP_WRSR2, sr2, 1);
+}
+
+static int spi_nor_read_sr2(struct spi_nor *nor, u8 *sr2)
+{
+ if (nor->spimem) {
+ struct spi_mem_op op =
+ SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDSR2, 1),
+ SPI_MEM_OP_NO_ADDR,
+ SPI_MEM_OP_NO_DUMMY,
+ SPI_MEM_OP_DATA_IN(1, sr2, 1));
+
+ return spi_mem_exec_op(nor->spimem, &op);
+ }
+
+ return nor->read_reg(nor, SPINOR_OP_RDSR2, sr2, 1);
+}
+
+/**
+ * sr2_bit7_quad_enable() - set QE bit in Status Register 2.
+ * @nor: pointer to a 'struct spi_nor'
+ *
+ * Set the Quad Enable (QE) bit in the Status Register 2.
+ *
+ * This is one of the procedures to set the QE bit described in the SFDP
+ * (JESD216 rev B) specification but no manufacturer using this procedure has
+ * been identified yet, hence the name of the function.
+ *
+ * Return: 0 on success, -errno otherwise.
+ */
+static int sr2_bit7_quad_enable(struct spi_nor *nor)
+{
+ u8 *sr2 = nor->bouncebuf;
+ int ret;
+
+ /* Check current Quad Enable bit value. */
+ ret = spi_nor_read_sr2(nor, sr2);
+ if (ret)
+ return ret;
+ if (*sr2 & SR2_QUAD_EN_BIT7)
+ return 0;
+
+ /* Update the Quad Enable bit. */
+ *sr2 |= SR2_QUAD_EN_BIT7;
+
+ write_enable(nor);
+
+ ret = spi_nor_write_sr2(nor, sr2);
+ if (ret < 0) {
+ dev_err(nor->dev, "error while writing status register 2\n");
+ return -EINVAL;
+ }
+
+ ret = spi_nor_wait_till_ready(nor);
+ if (ret < 0) {
+ dev_err(nor->dev, "timeout while writing status register 2\n");
+ return ret;
+ }
+
+ /* Read back and check it. */
+ ret = spi_nor_read_sr2(nor, sr2);
+ if (!(ret > 0 && (*sr2 & SR2_QUAD_EN_BIT7))) {
+ dev_err(nor->dev, "SR2 Quad bit not set\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/**
+ * spi_nor_clear_sr_bp() - clear the Status Register Block Protection bits.
+ * @nor: pointer to a 'struct spi_nor'
+ *
+ * Read-modify-write function that clears the Block Protection bits from the
+ * Status Register without affecting other bits.
+ *
+ * Return: 0 on success, -errno otherwise.
+ */
+static int spi_nor_clear_sr_bp(struct spi_nor *nor)
+{
+ int ret;
+ u8 mask = SR_BP2 | SR_BP1 | SR_BP0;
+
+ ret = read_sr(nor);
+ if (ret < 0) {
+ dev_err(nor->dev, "error while reading status register\n");
+ return ret;
+ }
+
+ write_enable(nor);
+
+ ret = write_sr(nor, ret & ~mask);
+ if (ret) {
+ dev_err(nor->dev, "write to status register failed\n");
+ return ret;
+ }
+
+ ret = spi_nor_wait_till_ready(nor);
+ if (ret)
+ dev_err(nor->dev, "timeout while writing status register\n");
+ return ret;
+}
+
+/**
+ * spi_nor_spansion_clear_sr_bp() - clear the Status Register Block Protection
+ * bits on spansion flashes.
+ * @nor: pointer to a 'struct spi_nor'
+ *
+ * Read-modify-write function that clears the Block Protection bits from the
+ * Status Register without affecting other bits. The function is tightly
+ * coupled with the spansion_quad_enable() function. Both assume that the Write
+ * Register with 16 bits, together with the Read Configuration Register (35h)
+ * instructions are supported.
+ *
+ * Return: 0 on success, -errno otherwise.
+ */
+static int spi_nor_spansion_clear_sr_bp(struct spi_nor *nor)
+{
+ int ret;
+ u8 mask = SR_BP2 | SR_BP1 | SR_BP0;
+ u8 *sr_cr = nor->bouncebuf;
+
+ /* Check current Quad Enable bit value. */
+ ret = read_cr(nor);
+ if (ret < 0) {
+ dev_err(nor->dev,
+ "error while reading configuration register\n");
+ return ret;
+ }
+
+ /*
+ * When the configuration register Quad Enable bit is one, only the
+ * Write Status (01h) command with two data bytes may be used.
+ */
+ if (ret & CR_QUAD_EN_SPAN) {
+ sr_cr[1] = ret;
+
+ ret = read_sr(nor);
+ if (ret < 0) {
+ dev_err(nor->dev,
+ "error while reading status register\n");
+ return ret;
+ }
+ sr_cr[0] = ret & ~mask;
+
+ ret = write_sr_cr(nor, sr_cr);
+ if (ret)
+ dev_err(nor->dev, "16-bit write register failed\n");
+ return ret;
+ }
+
+ /*
+ * If the Quad Enable bit is zero, use the Write Status (01h) command
+ * with one data byte.
+ */
+ return spi_nor_clear_sr_bp(nor);
+}
+
+/* Used when the "_ext_id" is two bytes at most */
+#define INFO(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags) \
+ .id = { \
+ ((_jedec_id) >> 16) & 0xff, \
+ ((_jedec_id) >> 8) & 0xff, \
+ (_jedec_id) & 0xff, \
+ ((_ext_id) >> 8) & 0xff, \
+ (_ext_id) & 0xff, \
+ }, \
+ .id_len = (!(_jedec_id) ? 0 : (3 + ((_ext_id) ? 2 : 0))), \
+ .sector_size = (_sector_size), \
+ .n_sectors = (_n_sectors), \
+ .page_size = 256, \
+ .flags = (_flags),
+
+#define INFO6(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags) \
+ .id = { \
+ ((_jedec_id) >> 16) & 0xff, \
+ ((_jedec_id) >> 8) & 0xff, \
+ (_jedec_id) & 0xff, \
+ ((_ext_id) >> 16) & 0xff, \
+ ((_ext_id) >> 8) & 0xff, \
+ (_ext_id) & 0xff, \
+ }, \
+ .id_len = 6, \
+ .sector_size = (_sector_size), \
+ .n_sectors = (_n_sectors), \
+ .page_size = 256, \
+ .flags = (_flags),
+
+#define CAT25_INFO(_sector_size, _n_sectors, _page_size, _addr_width, _flags) \
+ .sector_size = (_sector_size), \
+ .n_sectors = (_n_sectors), \
+ .page_size = (_page_size), \
+ .addr_width = (_addr_width), \
+ .flags = (_flags),
+
+#define S3AN_INFO(_jedec_id, _n_sectors, _page_size) \
+ .id = { \
+ ((_jedec_id) >> 16) & 0xff, \
+ ((_jedec_id) >> 8) & 0xff, \
+ (_jedec_id) & 0xff \
+ }, \
+ .id_len = 3, \
+ .sector_size = (8*_page_size), \
+ .n_sectors = (_n_sectors), \
+ .page_size = _page_size, \
+ .addr_width = 3, \
+ .flags = SPI_NOR_NO_FR | SPI_S3AN,
+
+static int
+is25lp256_post_bfpt_fixups(struct spi_nor *nor,
+ const struct sfdp_parameter_header *bfpt_header,
+ const struct sfdp_bfpt *bfpt,
+ struct spi_nor_flash_parameter *params)
+{
+ /*
+ * IS25LP256 supports 4B opcodes, but the BFPT advertises a
+ * BFPT_DWORD1_ADDRESS_BYTES_3_ONLY address width.
+ * Overwrite the address width advertised by the BFPT.
+ */
+ if ((bfpt->dwords[BFPT_DWORD(1)] & BFPT_DWORD1_ADDRESS_BYTES_MASK) ==
+ BFPT_DWORD1_ADDRESS_BYTES_3_ONLY)
+ nor->addr_width = 4;
+
+ return 0;
+}
+
+static struct spi_nor_fixups is25lp256_fixups = {
+ .post_bfpt = is25lp256_post_bfpt_fixups,
+};
+
+static int
+mx25l25635_post_bfpt_fixups(struct spi_nor *nor,
+ const struct sfdp_parameter_header *bfpt_header,
+ const struct sfdp_bfpt *bfpt,
+ struct spi_nor_flash_parameter *params)
+{
+ /*
+ * MX25L25635F supports 4B opcodes but MX25L25635E does not.
+ * Unfortunately, Macronix has re-used the same JEDEC ID for both
+ * variants which prevents us from defining a new entry in the parts
+ * table.
+ * We need a way to differentiate MX25L25635E and MX25L25635F, and it
+ * seems that the F version advertises support for Fast Read 4-4-4 in
+ * its BFPT table.
+ */
+ if (bfpt->dwords[BFPT_DWORD(5)] & BFPT_DWORD5_FAST_READ_4_4_4)
+ nor->flags |= SNOR_F_4B_OPCODES;
+
+ return 0;
+}
+
+static struct spi_nor_fixups mx25l25635_fixups = {
+ .post_bfpt = mx25l25635_post_bfpt_fixups,
+};
+
+static void gd25q256_default_init(struct spi_nor *nor)
+{
+ /*
+ * Some manufacturer like GigaDevice may use different
+ * bit to set QE on different memories, so the MFR can't
+ * indicate the quad_enable method for this case, we need
+ * to set it in the default_init fixup hook.
+ */
+ nor->params.quad_enable = macronix_quad_enable;
+}
+
+static struct spi_nor_fixups gd25q256_fixups = {
+ .default_init = gd25q256_default_init,
+};
+
+/* NOTE: double check command sets and memory organization when you add
+ * more nor chips. This current list focusses on newer chips, which
+ * have been converging on command sets which including JEDEC ID.
+ *
+ * All newly added entries should describe *hardware* and should use SECT_4K
+ * (or SECT_4K_PMC) if hardware supports erasing 4 KiB sectors. For usage
+ * scenarios excluding small sectors there is config option that can be
+ * disabled: CONFIG_MTD_SPI_NOR_USE_4K_SECTORS.
+ * For historical (and compatibility) reasons (before we got above config) some
+ * old entries may be missing 4K flag.
+ */
+static const struct flash_info spi_nor_ids[] = {
+ /* Atmel -- some are (confusingly) marketed as "DataFlash" */
+ { "at25fs010", INFO(0x1f6601, 0, 32 * 1024, 4, SECT_4K) },
+ { "at25fs040", INFO(0x1f6604, 0, 64 * 1024, 8, SECT_4K) },
+
+ { "at25df041a", INFO(0x1f4401, 0, 64 * 1024, 8, SECT_4K) },
+ { "at25df321", INFO(0x1f4700, 0, 64 * 1024, 64, SECT_4K) },
+ { "at25df321a", INFO(0x1f4701, 0, 64 * 1024, 64, SECT_4K) },
+ { "at25df641", INFO(0x1f4800, 0, 64 * 1024, 128, SECT_4K) },
+
+ { "at26f004", INFO(0x1f0400, 0, 64 * 1024, 8, SECT_4K) },
+ { "at26df081a", INFO(0x1f4501, 0, 64 * 1024, 16, SECT_4K) },
+ { "at26df161a", INFO(0x1f4601, 0, 64 * 1024, 32, SECT_4K) },
+ { "at26df321", INFO(0x1f4700, 0, 64 * 1024, 64, SECT_4K) },
+
+ { "at45db081d", INFO(0x1f2500, 0, 64 * 1024, 16, SECT_4K) },
+
+ /* EON -- en25xxx */
+ { "en25f32", INFO(0x1c3116, 0, 64 * 1024, 64, SECT_4K) },
+ { "en25p32", INFO(0x1c2016, 0, 64 * 1024, 64, 0) },
+ { "en25q32b", INFO(0x1c3016, 0, 64 * 1024, 64, 0) },
+ { "en25p64", INFO(0x1c2017, 0, 64 * 1024, 128, 0) },
+ { "en25q64", INFO(0x1c3017, 0, 64 * 1024, 128, SECT_4K) },
+ { "en25q80a", INFO(0x1c3014, 0, 64 * 1024, 16,
+ SECT_4K | SPI_NOR_DUAL_READ) },
+ { "en25qh32", INFO(0x1c7016, 0, 64 * 1024, 64, 0) },
+ { "en25qh64", INFO(0x1c7017, 0, 64 * 1024, 128,
+ SECT_4K | SPI_NOR_DUAL_READ) },
+ { "en25qh128", INFO(0x1c7018, 0, 64 * 1024, 256, 0) },
+ { "en25qh256", INFO(0x1c7019, 0, 64 * 1024, 512, 0) },
+ { "en25s64", INFO(0x1c3817, 0, 64 * 1024, 128, SECT_4K) },
+
+ /* ESMT */
+ { "f25l32pa", INFO(0x8c2016, 0, 64 * 1024, 64, SECT_4K | SPI_NOR_HAS_LOCK) },
+ { "f25l32qa", INFO(0x8c4116, 0, 64 * 1024, 64, SECT_4K | SPI_NOR_HAS_LOCK) },
+ { "f25l64qa", INFO(0x8c4117, 0, 64 * 1024, 128, SECT_4K | SPI_NOR_HAS_LOCK) },
+
+ /* Everspin */
+ { "mr25h128", CAT25_INFO( 16 * 1024, 1, 256, 2, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) },
+ { "mr25h256", CAT25_INFO( 32 * 1024, 1, 256, 2, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) },
+ { "mr25h10", CAT25_INFO(128 * 1024, 1, 256, 3, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) },
+ { "mr25h40", CAT25_INFO(512 * 1024, 1, 256, 3, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) },
+
+ /* Fujitsu */
+ { "mb85rs1mt", INFO(0x047f27, 0, 128 * 1024, 1, SPI_NOR_NO_ERASE) },
+
+ /* GigaDevice */
+ {
+ "gd25q16", INFO(0xc84015, 0, 64 * 1024, 32,
+ SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |
+ SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
+ },
+ {
+ "gd25q32", INFO(0xc84016, 0, 64 * 1024, 64,
+ SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |
+ SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
+ },
+ {
+ "gd25lq32", INFO(0xc86016, 0, 64 * 1024, 64,
+ SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |
+ SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
+ },
+ {
+ "gd25q64", INFO(0xc84017, 0, 64 * 1024, 128,
+ SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |
+ SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
+ },
+ {
+ "gd25lq64c", INFO(0xc86017, 0, 64 * 1024, 128,
+ SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |
+ SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
+ },
+ {
+ "gd25q128", INFO(0xc84018, 0, 64 * 1024, 256,
+ SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |
+ SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
+ },
+ {
+ "gd25q256", INFO(0xc84019, 0, 64 * 1024, 512,
+ SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |
+ SPI_NOR_4B_OPCODES | SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
+ .fixups = &gd25q256_fixups,
+ },
+
+ /* Intel/Numonyx -- xxxs33b */
+ { "160s33b", INFO(0x898911, 0, 64 * 1024, 32, 0) },
+ { "320s33b", INFO(0x898912, 0, 64 * 1024, 64, 0) },
+ { "640s33b", INFO(0x898913, 0, 64 * 1024, 128, 0) },
+
+ /* ISSI */
+ { "is25cd512", INFO(0x7f9d20, 0, 32 * 1024, 2, SECT_4K) },
+ { "is25lq040b", INFO(0x9d4013, 0, 64 * 1024, 8,
+ SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
+ { "is25lp016d", INFO(0x9d6015, 0, 64 * 1024, 32,
+ SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
+ { "is25lp080d", INFO(0x9d6014, 0, 64 * 1024, 16,
+ SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
+ { "is25lp032", INFO(0x9d6016, 0, 64 * 1024, 64,
+ SECT_4K | SPI_NOR_DUAL_READ) },
+ { "is25lp064", INFO(0x9d6017, 0, 64 * 1024, 128,
+ SECT_4K | SPI_NOR_DUAL_READ) },
+ { "is25lp128", INFO(0x9d6018, 0, 64 * 1024, 256,
+ SECT_4K | SPI_NOR_DUAL_READ) },
+ { "is25lp256", INFO(0x9d6019, 0, 64 * 1024, 512,
+ SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |
+ SPI_NOR_4B_OPCODES)
+ .fixups = &is25lp256_fixups },
+ { "is25wp032", INFO(0x9d7016, 0, 64 * 1024, 64,
+ SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
+ { "is25wp064", INFO(0x9d7017, 0, 64 * 1024, 128,
+ SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
+ { "is25wp128", INFO(0x9d7018, 0, 64 * 1024, 256,
+ SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
+
+ /* Macronix */
+ { "mx25l512e", INFO(0xc22010, 0, 64 * 1024, 1, SECT_4K) },
+ { "mx25l2005a", INFO(0xc22012, 0, 64 * 1024, 4, SECT_4K) },
+ { "mx25l4005a", INFO(0xc22013, 0, 64 * 1024, 8, SECT_4K) },
+ { "mx25l8005", INFO(0xc22014, 0, 64 * 1024, 16, 0) },
+ { "mx25l1606e", INFO(0xc22015, 0, 64 * 1024, 32, SECT_4K) },
+ { "mx25l3205d", INFO(0xc22016, 0, 64 * 1024, 64, SECT_4K) },
+ { "mx25l3255e", INFO(0xc29e16, 0, 64 * 1024, 64, SECT_4K) },
+ { "mx25l6405d", INFO(0xc22017, 0, 64 * 1024, 128, SECT_4K) },
+ { "mx25u2033e", INFO(0xc22532, 0, 64 * 1024, 4, SECT_4K) },
+ { "mx25u3235f", INFO(0xc22536, 0, 64 * 1024, 64,
+ SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
+ { "mx25u4035", INFO(0xc22533, 0, 64 * 1024, 8, SECT_4K) },
+ { "mx25u8035", INFO(0xc22534, 0, 64 * 1024, 16, SECT_4K) },
+ { "mx25u6435f", INFO(0xc22537, 0, 64 * 1024, 128, SECT_4K) },
+ { "mx25l12805d", INFO(0xc22018, 0, 64 * 1024, 256, 0) },
+ { "mx25l12855e", INFO(0xc22618, 0, 64 * 1024, 256, 0) },
+ { "mx25u12835f", INFO(0xc22538, 0, 64 * 1024, 256,
+ SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
+ { "mx25l25635e", INFO(0xc22019, 0, 64 * 1024, 512,
+ SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ)
+ .fixups = &mx25l25635_fixups },
+ { "mx25u25635f", INFO(0xc22539, 0, 64 * 1024, 512, SECT_4K | SPI_NOR_4B_OPCODES) },
+ { "mx25v8035f", INFO(0xc22314, 0, 64 * 1024, 16,
+ SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
+ { "mx25l25655e", INFO(0xc22619, 0, 64 * 1024, 512, 0) },
+ { "mx66l51235f", INFO(0xc2201a, 0, 64 * 1024, 1024, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
+ { "mx66u51235f", INFO(0xc2253a, 0, 64 * 1024, 1024, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) },
+ { "mx66l1g45g", INFO(0xc2201b, 0, 64 * 1024, 2048, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
+ { "mx66l1g55g", INFO(0xc2261b, 0, 64 * 1024, 2048, SPI_NOR_QUAD_READ) },
+
+ /* Micron <--> ST Micro */
+ { "n25q016a", INFO(0x20bb15, 0, 64 * 1024, 32, SECT_4K | SPI_NOR_QUAD_READ) },
+ { "n25q032", INFO(0x20ba16, 0, 64 * 1024, 64, SPI_NOR_QUAD_READ) },
+ { "n25q032a", INFO(0x20bb16, 0, 64 * 1024, 64, SPI_NOR_QUAD_READ) },
+ { "n25q064", INFO(0x20ba17, 0, 64 * 1024, 128, SECT_4K | SPI_NOR_QUAD_READ) },
+ { "n25q064a", INFO(0x20bb17, 0, 64 * 1024, 128, SECT_4K | SPI_NOR_QUAD_READ) },
+ { "n25q128a11", INFO(0x20bb18, 0, 64 * 1024, 256, SECT_4K | SPI_NOR_QUAD_READ) },
+ { "n25q128a13", INFO(0x20ba18, 0, 64 * 1024, 256, SECT_4K | SPI_NOR_QUAD_READ) },
+ { "n25q256a", INFO(0x20ba19, 0, 64 * 1024, 512, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
+ { "n25q256ax1", INFO(0x20bb19, 0, 64 * 1024, 512, SECT_4K | SPI_NOR_QUAD_READ) },
+ { "n25q512ax3", INFO(0x20ba20, 0, 64 * 1024, 1024, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ) },
+ { "n25q00", INFO(0x20ba21, 0, 64 * 1024, 2048, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ | NO_CHIP_ERASE) },
+ { "n25q00a", INFO(0x20bb21, 0, 64 * 1024, 2048, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ | NO_CHIP_ERASE) },
+ { "mt25ql02g", INFO(0x20ba22, 0, 64 * 1024, 4096,
+ SECT_4K | USE_FSR | SPI_NOR_QUAD_READ |
+ NO_CHIP_ERASE) },
+ { "mt25qu512a (n25q512a)", INFO(0x20bb20, 0, 64 * 1024, 1024,
+ SECT_4K | USE_FSR | SPI_NOR_DUAL_READ |
+ SPI_NOR_QUAD_READ |
+ SPI_NOR_4B_OPCODES) },
+ { "mt25qu02g", INFO(0x20bb22, 0, 64 * 1024, 4096, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ | NO_CHIP_ERASE) },
+
+ /* Micron */
+ {
+ "mt35xu512aba", INFO(0x2c5b1a, 0, 128 * 1024, 512,
+ SECT_4K | USE_FSR | SPI_NOR_OCTAL_READ |
+ SPI_NOR_4B_OPCODES)
+ },
+ { "mt35xu02g", INFO(0x2c5b1c, 0, 128 * 1024, 2048,
+ SECT_4K | USE_FSR | SPI_NOR_OCTAL_READ |
+ SPI_NOR_4B_OPCODES) },
+
+ /* PMC */
+ { "pm25lv512", INFO(0, 0, 32 * 1024, 2, SECT_4K_PMC) },
+ { "pm25lv010", INFO(0, 0, 32 * 1024, 4, SECT_4K_PMC) },
+ { "pm25lq032", INFO(0x7f9d46, 0, 64 * 1024, 64, SECT_4K) },
+
+ /* Spansion/Cypress -- single (large) sector size only, at least
+ * for the chips listed here (without boot sectors).
+ */
+ { "s25sl032p", INFO(0x010215, 0x4d00, 64 * 1024, 64, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
+ { "s25sl064p", INFO(0x010216, 0x4d00, 64 * 1024, 128, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
+ { "s25fl128s0", INFO6(0x012018, 0x4d0080, 256 * 1024, 64,
+ SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | USE_CLSR) },
+ { "s25fl128s1", INFO6(0x012018, 0x4d0180, 64 * 1024, 256,
+ SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | USE_CLSR) },
+ { "s25fl256s0", INFO(0x010219, 0x4d00, 256 * 1024, 128, USE_CLSR) },
+ { "s25fl256s1", INFO(0x010219, 0x4d01, 64 * 1024, 512, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | USE_CLSR) },
+ { "s25fl512s", INFO6(0x010220, 0x4d0080, 256 * 1024, 256,
+ SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |
+ SPI_NOR_HAS_LOCK | USE_CLSR) },
+ { "s25fs512s", INFO6(0x010220, 0x4d0081, 256 * 1024, 256, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | USE_CLSR) },
+ { "s70fl01gs", INFO(0x010221, 0x4d00, 256 * 1024, 256, 0) },
+ { "s25sl12800", INFO(0x012018, 0x0300, 256 * 1024, 64, 0) },
+ { "s25sl12801", INFO(0x012018, 0x0301, 64 * 1024, 256, 0) },
+ { "s25fl129p0", INFO(0x012018, 0x4d00, 256 * 1024, 64, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | USE_CLSR) },
+ { "s25fl129p1", INFO(0x012018, 0x4d01, 64 * 1024, 256, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | USE_CLSR) },
+ { "s25sl004a", INFO(0x010212, 0, 64 * 1024, 8, 0) },
+ { "s25sl008a", INFO(0x010213, 0, 64 * 1024, 16, 0) },
+ { "s25sl016a", INFO(0x010214, 0, 64 * 1024, 32, 0) },
+ { "s25sl032a", INFO(0x010215, 0, 64 * 1024, 64, 0) },
+ { "s25sl064a", INFO(0x010216, 0, 64 * 1024, 128, 0) },
+ { "s25fl004k", INFO(0xef4013, 0, 64 * 1024, 8, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
+ { "s25fl008k", INFO(0xef4014, 0, 64 * 1024, 16, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
+ { "s25fl016k", INFO(0xef4015, 0, 64 * 1024, 32, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
+ { "s25fl064k", INFO(0xef4017, 0, 64 * 1024, 128, SECT_4K) },
+ { "s25fl116k", INFO(0x014015, 0, 64 * 1024, 32, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
+ { "s25fl132k", INFO(0x014016, 0, 64 * 1024, 64, SECT_4K) },
+ { "s25fl164k", INFO(0x014017, 0, 64 * 1024, 128, SECT_4K) },
+ { "s25fl204k", INFO(0x014013, 0, 64 * 1024, 8, SECT_4K | SPI_NOR_DUAL_READ) },
+ { "s25fl208k", INFO(0x014014, 0, 64 * 1024, 16, SECT_4K | SPI_NOR_DUAL_READ) },
+ { "s25fl064l", INFO(0x016017, 0, 64 * 1024, 128, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) },
+ { "s25fl128l", INFO(0x016018, 0, 64 * 1024, 256, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) },
+ { "s25fl256l", INFO(0x016019, 0, 64 * 1024, 512, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) },
+
+ /* SST -- large erase sizes are "overlays", "sectors" are 4K */
+ { "sst25vf040b", INFO(0xbf258d, 0, 64 * 1024, 8, SECT_4K | SST_WRITE) },
+ { "sst25vf080b", INFO(0xbf258e, 0, 64 * 1024, 16, SECT_4K | SST_WRITE) },
+ { "sst25vf016b", INFO(0xbf2541, 0, 64 * 1024, 32, SECT_4K | SST_WRITE) },
+ { "sst25vf032b", INFO(0xbf254a, 0, 64 * 1024, 64, SECT_4K | SST_WRITE) },
+ { "sst25vf064c", INFO(0xbf254b, 0, 64 * 1024, 128, SECT_4K) },
+ { "sst25wf512", INFO(0xbf2501, 0, 64 * 1024, 1, SECT_4K | SST_WRITE) },
+ { "sst25wf010", INFO(0xbf2502, 0, 64 * 1024, 2, SECT_4K | SST_WRITE) },
+ { "sst25wf020", INFO(0xbf2503, 0, 64 * 1024, 4, SECT_4K | SST_WRITE) },
+ { "sst25wf020a", INFO(0x621612, 0, 64 * 1024, 4, SECT_4K) },
+ { "sst25wf040b", INFO(0x621613, 0, 64 * 1024, 8, SECT_4K) },
+ { "sst25wf040", INFO(0xbf2504, 0, 64 * 1024, 8, SECT_4K | SST_WRITE) },
+ { "sst25wf080", INFO(0xbf2505, 0, 64 * 1024, 16, SECT_4K | SST_WRITE) },
+ { "sst26wf016b", INFO(0xbf2651, 0, 64 * 1024, 32, SECT_4K |
+ SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
+ { "sst26vf064b", INFO(0xbf2643, 0, 64 * 1024, 128, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
+
+ /* ST Microelectronics -- newer production may have feature updates */
+ { "m25p05", INFO(0x202010, 0, 32 * 1024, 2, 0) },
+ { "m25p10", INFO(0x202011, 0, 32 * 1024, 4, 0) },
+ { "m25p20", INFO(0x202012, 0, 64 * 1024, 4, 0) },
+ { "m25p40", INFO(0x202013, 0, 64 * 1024, 8, 0) },
+ { "m25p80", INFO(0x202014, 0, 64 * 1024, 16, 0) },
+ { "m25p16", INFO(0x202015, 0, 64 * 1024, 32, 0) },
+ { "m25p32", INFO(0x202016, 0, 64 * 1024, 64, 0) },
+ { "m25p64", INFO(0x202017, 0, 64 * 1024, 128, 0) },
+ { "m25p128", INFO(0x202018, 0, 256 * 1024, 64, 0) },
+
+ { "m25p05-nonjedec", INFO(0, 0, 32 * 1024, 2, 0) },
+ { "m25p10-nonjedec", INFO(0, 0, 32 * 1024, 4, 0) },
+ { "m25p20-nonjedec", INFO(0, 0, 64 * 1024, 4, 0) },
+ { "m25p40-nonjedec", INFO(0, 0, 64 * 1024, 8, 0) },
+ { "m25p80-nonjedec", INFO(0, 0, 64 * 1024, 16, 0) },
+ { "m25p16-nonjedec", INFO(0, 0, 64 * 1024, 32, 0) },
+ { "m25p32-nonjedec", INFO(0, 0, 64 * 1024, 64, 0) },
+ { "m25p64-nonjedec", INFO(0, 0, 64 * 1024, 128, 0) },
+ { "m25p128-nonjedec", INFO(0, 0, 256 * 1024, 64, 0) },
+
+ { "m45pe10", INFO(0x204011, 0, 64 * 1024, 2, 0) },
+ { "m45pe80", INFO(0x204014, 0, 64 * 1024, 16, 0) },
+ { "m45pe16", INFO(0x204015, 0, 64 * 1024, 32, 0) },
+
+ { "m25pe20", INFO(0x208012, 0, 64 * 1024, 4, 0) },
+ { "m25pe80", INFO(0x208014, 0, 64 * 1024, 16, 0) },
+ { "m25pe16", INFO(0x208015, 0, 64 * 1024, 32, SECT_4K) },
+
+ { "m25px16", INFO(0x207115, 0, 64 * 1024, 32, SECT_4K) },
+ { "m25px32", INFO(0x207116, 0, 64 * 1024, 64, SECT_4K) },
+ { "m25px32-s0", INFO(0x207316, 0, 64 * 1024, 64, SECT_4K) },
+ { "m25px32-s1", INFO(0x206316, 0, 64 * 1024, 64, SECT_4K) },
+ { "m25px64", INFO(0x207117, 0, 64 * 1024, 128, 0) },
+ { "m25px80", INFO(0x207114, 0, 64 * 1024, 16, 0) },
+
+ /* Winbond -- w25x "blocks" are 64K, "sectors" are 4KiB */
+ { "w25x05", INFO(0xef3010, 0, 64 * 1024, 1, SECT_4K) },
+ { "w25x10", INFO(0xef3011, 0, 64 * 1024, 2, SECT_4K) },
+ { "w25x20", INFO(0xef3012, 0, 64 * 1024, 4, SECT_4K) },
+ { "w25x40", INFO(0xef3013, 0, 64 * 1024, 8, SECT_4K) },
+ { "w25x80", INFO(0xef3014, 0, 64 * 1024, 16, SECT_4K) },
+ { "w25x16", INFO(0xef3015, 0, 64 * 1024, 32, SECT_4K) },
+ {
+ "w25q16dw", INFO(0xef6015, 0, 64 * 1024, 32,
+ SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |
+ SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
+ },
+ { "w25x32", INFO(0xef3016, 0, 64 * 1024, 64, SECT_4K) },
+ {
+ "w25q16jv-im/jm", INFO(0xef7015, 0, 64 * 1024, 32,
+ SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |
+ SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
+ },
+ { "w25q20cl", INFO(0xef4012, 0, 64 * 1024, 4, SECT_4K) },
+ { "w25q20bw", INFO(0xef5012, 0, 64 * 1024, 4, SECT_4K) },
+ { "w25q20ew", INFO(0xef6012, 0, 64 * 1024, 4, SECT_4K) },
+ { "w25q32", INFO(0xef4016, 0, 64 * 1024, 64, SECT_4K) },
+ {
+ "w25q32dw", INFO(0xef6016, 0, 64 * 1024, 64,
+ SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |
+ SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
+ },
+ {
+ "w25q32jv", INFO(0xef7016, 0, 64 * 1024, 64,
+ SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |
+ SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
+ },
+ { "w25x64", INFO(0xef3017, 0, 64 * 1024, 128, SECT_4K) },
+ { "w25q64", INFO(0xef4017, 0, 64 * 1024, 128, SECT_4K) },
+ {
+ "w25q64dw", INFO(0xef6017, 0, 64 * 1024, 128,
+ SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |
+ SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
+ },
+ {
+ "w25q128fw", INFO(0xef6018, 0, 64 * 1024, 256,
+ SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |
+ SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
+ },
+ {
+ "w25q128jv", INFO(0xef7018, 0, 64 * 1024, 256,
+ SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |
+ SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
+ },
+ { "w25q80", INFO(0xef5014, 0, 64 * 1024, 16, SECT_4K) },
+ { "w25q80bl", INFO(0xef4014, 0, 64 * 1024, 16, SECT_4K) },
+ { "w25q128", INFO(0xef4018, 0, 64 * 1024, 256, SECT_4K) },
+ { "w25q256", INFO(0xef4019, 0, 64 * 1024, 512, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
+ { "w25q256jvm", INFO(0xef7019, 0, 64 * 1024, 512,
+ SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
+ { "w25q512jv", INFO(0xef4020, 0, 64 * 1024, 1024, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |
+ SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) },
+ { "w25m512jv", INFO(0xef7119, 0, 64 * 1024, 1024,
+ SECT_4K | SPI_NOR_QUAD_READ | SPI_NOR_DUAL_READ) },
+
+ /* Catalyst / On Semiconductor -- non-JEDEC */
+ { "cat25c11", CAT25_INFO( 16, 8, 16, 1, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) },
+ { "cat25c03", CAT25_INFO( 32, 8, 16, 2, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) },
+ { "cat25c09", CAT25_INFO( 128, 8, 32, 2, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) },
+ { "cat25c17", CAT25_INFO( 256, 8, 32, 2, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) },
+ { "cat25128", CAT25_INFO(2048, 8, 64, 2, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) },
+
+ /* Xilinx S3AN Internal Flash */
+ { "3S50AN", S3AN_INFO(0x1f2200, 64, 264) },
+ { "3S200AN", S3AN_INFO(0x1f2400, 256, 264) },
+ { "3S400AN", S3AN_INFO(0x1f2400, 256, 264) },
+ { "3S700AN", S3AN_INFO(0x1f2500, 512, 264) },
+ { "3S1400AN", S3AN_INFO(0x1f2600, 512, 528) },
+
+ /* XMC (Wuhan Xinxin Semiconductor Manufacturing Corp.) */
+ { "XM25QH64A", INFO(0x207017, 0, 64 * 1024, 128, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
+ { "XM25QH128A", INFO(0x207018, 0, 64 * 1024, 256, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
+ { },
+};
+
+static const struct flash_info *spi_nor_read_id(struct spi_nor *nor)
+{
+ int tmp;
+ u8 *id = nor->bouncebuf;
+ const struct flash_info *info;
+
+ if (nor->spimem) {
+ struct spi_mem_op op =
+ SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDID, 1),
+ SPI_MEM_OP_NO_ADDR,
+ SPI_MEM_OP_NO_DUMMY,
+ SPI_MEM_OP_DATA_IN(SPI_NOR_MAX_ID_LEN, id, 1));
+
+ tmp = spi_mem_exec_op(nor->spimem, &op);
+ } else {
+ tmp = nor->read_reg(nor, SPINOR_OP_RDID, id,
+ SPI_NOR_MAX_ID_LEN);
+ }
+ if (tmp < 0) {
+ dev_err(nor->dev, "error %d reading JEDEC ID\n", tmp);
+ return ERR_PTR(tmp);
+ }
+
+ for (tmp = 0; tmp < ARRAY_SIZE(spi_nor_ids) - 1; tmp++) {
+ info = &spi_nor_ids[tmp];
+ if (info->id_len) {
+ if (!memcmp(info->id, id, info->id_len))
+ return &spi_nor_ids[tmp];
+ }
+ }
+ dev_err(nor->dev, "unrecognized JEDEC id bytes: %*ph\n",
+ SPI_NOR_MAX_ID_LEN, id);
+ return ERR_PTR(-ENODEV);
+}
+
+static int spi_nor_read(struct mtd_info *mtd, loff_t from, size_t len,
+ size_t *retlen, u_char *buf)
+{
+ struct spi_nor *nor = mtd_to_spi_nor(mtd);
+ int ret;
+
+ dev_dbg(nor->dev, "from 0x%08x, len %zd\n", (u32)from, len);
+
+ ret = spi_nor_lock_and_prep(nor, SPI_NOR_OPS_READ);
+ if (ret)
+ return ret;
+
+ while (len) {
+ loff_t addr = from;
+
+ addr = spi_nor_convert_addr(nor, addr);
+
+ ret = spi_nor_read_data(nor, addr, len, buf);
+ if (ret == 0) {
+ /* We shouldn't see 0-length reads */
+ ret = -EIO;
+ goto read_err;
+ }
+ if (ret < 0)
+ goto read_err;
+
+ WARN_ON(ret > len);
+ *retlen += ret;
+ buf += ret;
+ from += ret;
+ len -= ret;
+ }
+ ret = 0;
+
+read_err:
+ spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_READ);
+ return ret;
+}
+
+static int sst_write(struct mtd_info *mtd, loff_t to, size_t len,
+ size_t *retlen, const u_char *buf)
+{
+ struct spi_nor *nor = mtd_to_spi_nor(mtd);
+ size_t actual;
+ int ret;
+
+ dev_dbg(nor->dev, "to 0x%08x, len %zd\n", (u32)to, len);
+
+ ret = spi_nor_lock_and_prep(nor, SPI_NOR_OPS_WRITE);
+ if (ret)
+ return ret;
+
+ write_enable(nor);
+
+ nor->sst_write_second = false;
+
+ actual = to % 2;
+ /* Start write from odd address. */
+ if (actual) {
+ nor->program_opcode = SPINOR_OP_BP;
+
+ /* write one byte. */
+ ret = spi_nor_write_data(nor, to, 1, buf);
+ if (ret < 0)
+ goto sst_write_err;
+ WARN(ret != 1, "While writing 1 byte written %i bytes\n",
+ (int)ret);
+ ret = spi_nor_wait_till_ready(nor);
+ if (ret)
+ goto sst_write_err;
+ }
+ to += actual;
+
+ /* Write out most of the data here. */
+ for (; actual < len - 1; actual += 2) {
+ nor->program_opcode = SPINOR_OP_AAI_WP;
+
+ /* write two bytes. */
+ ret = spi_nor_write_data(nor, to, 2, buf + actual);
+ if (ret < 0)
+ goto sst_write_err;
+ WARN(ret != 2, "While writing 2 bytes written %i bytes\n",
+ (int)ret);
+ ret = spi_nor_wait_till_ready(nor);
+ if (ret)
+ goto sst_write_err;
+ to += 2;
+ nor->sst_write_second = true;
+ }
+ nor->sst_write_second = false;
+
+ write_disable(nor);
+ ret = spi_nor_wait_till_ready(nor);
+ if (ret)
+ goto sst_write_err;
+
+ /* Write out trailing byte if it exists. */
+ if (actual != len) {
+ write_enable(nor);
+
+ nor->program_opcode = SPINOR_OP_BP;
+ ret = spi_nor_write_data(nor, to, 1, buf + actual);
+ if (ret < 0)
+ goto sst_write_err;
+ WARN(ret != 1, "While writing 1 byte written %i bytes\n",
+ (int)ret);
+ ret = spi_nor_wait_till_ready(nor);
+ if (ret)
+ goto sst_write_err;
+ write_disable(nor);
+ actual += 1;
+ }
+sst_write_err:
+ *retlen += actual;
+ spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_WRITE);
+ return ret;
+}
+
+/*
+ * Write an address range to the nor chip. Data must be written in
+ * FLASH_PAGESIZE chunks. The address range may be any size provided
+ * it is within the physical boundaries.
+ */
+static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len,
+ size_t *retlen, const u_char *buf)
+{
+ struct spi_nor *nor = mtd_to_spi_nor(mtd);
+ size_t page_offset, page_remain, i;
+ ssize_t ret;
+
+ dev_dbg(nor->dev, "to 0x%08x, len %zd\n", (u32)to, len);
+
+ ret = spi_nor_lock_and_prep(nor, SPI_NOR_OPS_WRITE);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < len; ) {
+ ssize_t written;
+ loff_t addr = to + i;
+
+ /*
+ * If page_size is a power of two, the offset can be quickly
+ * calculated with an AND operation. On the other cases we
+ * need to do a modulus operation (more expensive).
+ * Power of two numbers have only one bit set and we can use
+ * the instruction hweight32 to detect if we need to do a
+ * modulus (do_div()) or not.
+ */
+ if (hweight32(nor->page_size) == 1) {
+ page_offset = addr & (nor->page_size - 1);
+ } else {
+ uint64_t aux = addr;
+
+ page_offset = do_div(aux, nor->page_size);
+ }
+ /* the size of data remaining on the first page */
+ page_remain = min_t(size_t,
+ nor->page_size - page_offset, len - i);
+
+ addr = spi_nor_convert_addr(nor, addr);
+
+ write_enable(nor);
+ ret = spi_nor_write_data(nor, addr, page_remain, buf + i);
+ if (ret < 0)
+ goto write_err;
+ written = ret;
+
+ ret = spi_nor_wait_till_ready(nor);
+ if (ret)
+ goto write_err;
+ *retlen += written;
+ i += written;
+ }
+
+write_err:
+ spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_WRITE);
+ return ret;
+}
+
+static int spi_nor_check(struct spi_nor *nor)
+{
+ if (!nor->dev ||
+ (!nor->spimem &&
+ (!nor->read || !nor->write || !nor->read_reg ||
+ !nor->write_reg))) {
+ pr_err("spi-nor: please fill all the necessary fields!\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int s3an_nor_setup(struct spi_nor *nor,
+ const struct spi_nor_hwcaps *hwcaps)
+{
+ int ret;
+
+ ret = spi_nor_xread_sr(nor, nor->bouncebuf);
+ if (ret < 0) {
+ dev_err(nor->dev, "error %d reading XRDSR\n", (int) ret);
+ return ret;
+ }
+
+ nor->erase_opcode = SPINOR_OP_XSE;
+ nor->program_opcode = SPINOR_OP_XPP;
+ nor->read_opcode = SPINOR_OP_READ;
+ nor->flags |= SNOR_F_NO_OP_CHIP_ERASE;
+
+ /*
+ * This flashes have a page size of 264 or 528 bytes (known as
+ * Default addressing mode). It can be changed to a more standard
+ * Power of two mode where the page size is 256/512. This comes
+ * with a price: there is 3% less of space, the data is corrupted
+ * and the page size cannot be changed back to default addressing
+ * mode.
+ *
+ * The current addressing mode can be read from the XRDSR register
+ * and should not be changed, because is a destructive operation.
+ */
+ if (nor->bouncebuf[0] & XSR_PAGESIZE) {
+ /* Flash in Power of 2 mode */
+ nor->page_size = (nor->page_size == 264) ? 256 : 512;
+ nor->mtd.writebufsize = nor->page_size;
+ nor->mtd.size = 8 * nor->page_size * nor->info->n_sectors;
+ nor->mtd.erasesize = 8 * nor->page_size;
+ } else {
+ /* Flash in Default addressing mode */
+ nor->params.convert_addr = s3an_convert_addr;
+ nor->mtd.erasesize = nor->info->sector_size;
+ }
+
+ return 0;
+}
+
+static void
+spi_nor_set_read_settings(struct spi_nor_read_command *read,
+ u8 num_mode_clocks,
+ u8 num_wait_states,
+ u8 opcode,
+ enum spi_nor_protocol proto)
+{
+ read->num_mode_clocks = num_mode_clocks;
+ read->num_wait_states = num_wait_states;
+ read->opcode = opcode;
+ read->proto = proto;
+}
+
+static void
+spi_nor_set_pp_settings(struct spi_nor_pp_command *pp,
+ u8 opcode,
+ enum spi_nor_protocol proto)
+{
+ pp->opcode = opcode;
+ pp->proto = proto;
+}
+
+static int spi_nor_hwcaps2cmd(u32 hwcaps, const int table[][2], size_t size)
+{
+ size_t i;
+
+ for (i = 0; i < size; i++)
+ if (table[i][0] == (int)hwcaps)
+ return table[i][1];
+
+ return -EINVAL;
+}
+
+static int spi_nor_hwcaps_read2cmd(u32 hwcaps)
+{
+ static const int hwcaps_read2cmd[][2] = {
+ { SNOR_HWCAPS_READ, SNOR_CMD_READ },
+ { SNOR_HWCAPS_READ_FAST, SNOR_CMD_READ_FAST },
+ { SNOR_HWCAPS_READ_1_1_1_DTR, SNOR_CMD_READ_1_1_1_DTR },
+ { SNOR_HWCAPS_READ_1_1_2, SNOR_CMD_READ_1_1_2 },
+ { SNOR_HWCAPS_READ_1_2_2, SNOR_CMD_READ_1_2_2 },
+ { SNOR_HWCAPS_READ_2_2_2, SNOR_CMD_READ_2_2_2 },
+ { SNOR_HWCAPS_READ_1_2_2_DTR, SNOR_CMD_READ_1_2_2_DTR },
+ { SNOR_HWCAPS_READ_1_1_4, SNOR_CMD_READ_1_1_4 },
+ { SNOR_HWCAPS_READ_1_4_4, SNOR_CMD_READ_1_4_4 },
+ { SNOR_HWCAPS_READ_4_4_4, SNOR_CMD_READ_4_4_4 },
+ { SNOR_HWCAPS_READ_1_4_4_DTR, SNOR_CMD_READ_1_4_4_DTR },
+ { SNOR_HWCAPS_READ_1_1_8, SNOR_CMD_READ_1_1_8 },
+ { SNOR_HWCAPS_READ_1_8_8, SNOR_CMD_READ_1_8_8 },
+ { SNOR_HWCAPS_READ_8_8_8, SNOR_CMD_READ_8_8_8 },
+ { SNOR_HWCAPS_READ_1_8_8_DTR, SNOR_CMD_READ_1_8_8_DTR },
+ };
+
+ return spi_nor_hwcaps2cmd(hwcaps, hwcaps_read2cmd,
+ ARRAY_SIZE(hwcaps_read2cmd));
+}
+
+static int spi_nor_hwcaps_pp2cmd(u32 hwcaps)
+{
+ static const int hwcaps_pp2cmd[][2] = {
+ { SNOR_HWCAPS_PP, SNOR_CMD_PP },
+ { SNOR_HWCAPS_PP_1_1_4, SNOR_CMD_PP_1_1_4 },
+ { SNOR_HWCAPS_PP_1_4_4, SNOR_CMD_PP_1_4_4 },
+ { SNOR_HWCAPS_PP_4_4_4, SNOR_CMD_PP_4_4_4 },
+ { SNOR_HWCAPS_PP_1_1_8, SNOR_CMD_PP_1_1_8 },
+ { SNOR_HWCAPS_PP_1_8_8, SNOR_CMD_PP_1_8_8 },
+ { SNOR_HWCAPS_PP_8_8_8, SNOR_CMD_PP_8_8_8 },
+ };
+
+ return spi_nor_hwcaps2cmd(hwcaps, hwcaps_pp2cmd,
+ ARRAY_SIZE(hwcaps_pp2cmd));
+}
+
+/*
+ * Serial Flash Discoverable Parameters (SFDP) parsing.
+ */
+
+/**
+ * spi_nor_read_raw() - raw read of serial flash memory. read_opcode,
+ * addr_width and read_dummy members of the struct spi_nor
+ * should be previously
+ * set.
+ * @nor: pointer to a 'struct spi_nor'
+ * @addr: offset in the serial flash memory
+ * @len: number of bytes to read
+ * @buf: buffer where the data is copied into (dma-safe memory)
+ *
+ * Return: 0 on success, -errno otherwise.
+ */
+static int spi_nor_read_raw(struct spi_nor *nor, u32 addr, size_t len, u8 *buf)
+{
+ int ret;
+
+ while (len) {
+ ret = spi_nor_read_data(nor, addr, len, buf);
+ if (ret < 0)
+ return ret;
+ if (!ret || ret > len)
+ return -EIO;
+
+ buf += ret;
+ addr += ret;
+ len -= ret;
+ }
+ return 0;
+}
+
+/**
+ * spi_nor_read_sfdp() - read Serial Flash Discoverable Parameters.
+ * @nor: pointer to a 'struct spi_nor'
+ * @addr: offset in the SFDP area to start reading data from
+ * @len: number of bytes to read
+ * @buf: buffer where the SFDP data are copied into (dma-safe memory)
+ *
+ * Whatever the actual numbers of bytes for address and dummy cycles are
+ * for (Fast) Read commands, the Read SFDP (5Ah) instruction is always
+ * followed by a 3-byte address and 8 dummy clock cycles.
+ *
+ * Return: 0 on success, -errno otherwise.
+ */
+static int spi_nor_read_sfdp(struct spi_nor *nor, u32 addr,
+ size_t len, void *buf)
+{
+ u8 addr_width, read_opcode, read_dummy;
+ int ret;
+
+ read_opcode = nor->read_opcode;
+ addr_width = nor->addr_width;
+ read_dummy = nor->read_dummy;
+
+ nor->read_opcode = SPINOR_OP_RDSFDP;
+ nor->addr_width = 3;
+ nor->read_dummy = 8;
+
+ ret = spi_nor_read_raw(nor, addr, len, buf);
+
+ nor->read_opcode = read_opcode;
+ nor->addr_width = addr_width;
+ nor->read_dummy = read_dummy;
+
+ return ret;
+}
+
+/**
+ * spi_nor_spimem_check_op - check if the operation is supported
+ * by controller
+ *@nor: pointer to a 'struct spi_nor'
+ *@op: pointer to op template to be checked
+ *
+ * Returns 0 if operation is supported, -ENOTSUPP otherwise.
+ */
+static int spi_nor_spimem_check_op(struct spi_nor *nor,
+ struct spi_mem_op *op)
+{
+ /*
+ * First test with 4 address bytes. The opcode itself might
+ * be a 3B addressing opcode but we don't care, because
+ * SPI controller implementation should not check the opcode,
+ * but just the sequence.
+ */
+ op->addr.nbytes = 4;
+ if (!spi_mem_supports_op(nor->spimem, op)) {
+ if (nor->mtd.size > SZ_16M)
+ return -ENOTSUPP;
+
+ /* If flash size <= 16MB, 3 address bytes are sufficient */
+ op->addr.nbytes = 3;
+ if (!spi_mem_supports_op(nor->spimem, op))
+ return -ENOTSUPP;
+ }
+
+ return 0;
+}
+
+/**
+ * spi_nor_spimem_check_readop - check if the read op is supported
+ * by controller
+ *@nor: pointer to a 'struct spi_nor'
+ *@read: pointer to op template to be checked
+ *
+ * Returns 0 if operation is supported, -ENOTSUPP otherwise.
+ */
+static int spi_nor_spimem_check_readop(struct spi_nor *nor,
+ const struct spi_nor_read_command *read)
+{
+ struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(read->opcode, 1),
+ SPI_MEM_OP_ADDR(3, 0, 1),
+ SPI_MEM_OP_DUMMY(0, 1),
+ SPI_MEM_OP_DATA_IN(0, NULL, 1));
+
+ op.cmd.buswidth = spi_nor_get_protocol_inst_nbits(read->proto);
+ op.addr.buswidth = spi_nor_get_protocol_addr_nbits(read->proto);
+ op.data.buswidth = spi_nor_get_protocol_data_nbits(read->proto);
+ op.dummy.buswidth = op.addr.buswidth;
+ op.dummy.nbytes = (read->num_mode_clocks + read->num_wait_states) *
+ op.dummy.buswidth / 8;
+
+ return spi_nor_spimem_check_op(nor, &op);
+}
+
+/**
+ * spi_nor_spimem_check_pp - check if the page program op is supported
+ * by controller
+ *@nor: pointer to a 'struct spi_nor'
+ *@pp: pointer to op template to be checked
+ *
+ * Returns 0 if operation is supported, -ENOTSUPP otherwise.
+ */
+static int spi_nor_spimem_check_pp(struct spi_nor *nor,
+ const struct spi_nor_pp_command *pp)
+{
+ struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(pp->opcode, 1),
+ SPI_MEM_OP_ADDR(3, 0, 1),
+ SPI_MEM_OP_NO_DUMMY,
+ SPI_MEM_OP_DATA_OUT(0, NULL, 1));
+
+ op.cmd.buswidth = spi_nor_get_protocol_inst_nbits(pp->proto);
+ op.addr.buswidth = spi_nor_get_protocol_addr_nbits(pp->proto);
+ op.data.buswidth = spi_nor_get_protocol_data_nbits(pp->proto);
+
+ return spi_nor_spimem_check_op(nor, &op);
+}
+
+/**
+ * spi_nor_spimem_adjust_hwcaps - Find optimal Read/Write protocol
+ * based on SPI controller capabilities
+ * @nor: pointer to a 'struct spi_nor'
+ * @hwcaps: pointer to resulting capabilities after adjusting
+ * according to controller and flash's capability
+ */
+static void
+spi_nor_spimem_adjust_hwcaps(struct spi_nor *nor, u32 *hwcaps)
+{
+ struct spi_nor_flash_parameter *params = &nor->params;
+ unsigned int cap;
+
+ /* DTR modes are not supported yet, mask them all. */
+ *hwcaps &= ~SNOR_HWCAPS_DTR;
+
+ /* X-X-X modes are not supported yet, mask them all. */
+ *hwcaps &= ~SNOR_HWCAPS_X_X_X;
+
+ for (cap = 0; cap < sizeof(*hwcaps) * BITS_PER_BYTE; cap++) {
+ int rdidx, ppidx;
+
+ if (!(*hwcaps & BIT(cap)))
+ continue;
+
+ rdidx = spi_nor_hwcaps_read2cmd(BIT(cap));
+ if (rdidx >= 0 &&
+ spi_nor_spimem_check_readop(nor, &params->reads[rdidx]))
+ *hwcaps &= ~BIT(cap);
+
+ ppidx = spi_nor_hwcaps_pp2cmd(BIT(cap));
+ if (ppidx < 0)
+ continue;
+
+ if (spi_nor_spimem_check_pp(nor,
+ &params->page_programs[ppidx]))
+ *hwcaps &= ~BIT(cap);
+ }
+}
+
+/**
+ * spi_nor_read_sfdp_dma_unsafe() - read Serial Flash Discoverable Parameters.
+ * @nor: pointer to a 'struct spi_nor'
+ * @addr: offset in the SFDP area to start reading data from
+ * @len: number of bytes to read
+ * @buf: buffer where the SFDP data are copied into
+ *
+ * Wrap spi_nor_read_sfdp() using a kmalloc'ed bounce buffer as @buf is now not
+ * guaranteed to be dma-safe.
+ *
+ * Return: -ENOMEM if kmalloc() fails, the return code of spi_nor_read_sfdp()
+ * otherwise.
+ */
+static int spi_nor_read_sfdp_dma_unsafe(struct spi_nor *nor, u32 addr,
+ size_t len, void *buf)
+{
+ void *dma_safe_buf;
+ int ret;
+
+ dma_safe_buf = kmalloc(len, GFP_KERNEL);
+ if (!dma_safe_buf)
+ return -ENOMEM;
+
+ ret = spi_nor_read_sfdp(nor, addr, len, dma_safe_buf);
+ memcpy(buf, dma_safe_buf, len);
+ kfree(dma_safe_buf);
+
+ return ret;
+}
+
+/* Fast Read settings. */
+
+static void
+spi_nor_set_read_settings_from_bfpt(struct spi_nor_read_command *read,
+ u16 half,
+ enum spi_nor_protocol proto)
+{
+ read->num_mode_clocks = (half >> 5) & 0x07;
+ read->num_wait_states = (half >> 0) & 0x1f;
+ read->opcode = (half >> 8) & 0xff;
+ read->proto = proto;
+}
+
+struct sfdp_bfpt_read {
+ /* The Fast Read x-y-z hardware capability in params->hwcaps.mask. */
+ u32 hwcaps;
+
+ /*
+ * The <supported_bit> bit in <supported_dword> BFPT DWORD tells us
+ * whether the Fast Read x-y-z command is supported.
+ */
+ u32 supported_dword;
+ u32 supported_bit;
+
+ /*
+ * The half-word at offset <setting_shift> in <setting_dword> BFPT DWORD
+ * encodes the op code, the number of mode clocks and the number of wait
+ * states to be used by Fast Read x-y-z command.
+ */
+ u32 settings_dword;
+ u32 settings_shift;
+
+ /* The SPI protocol for this Fast Read x-y-z command. */
+ enum spi_nor_protocol proto;
+};
+
+static const struct sfdp_bfpt_read sfdp_bfpt_reads[] = {
+ /* Fast Read 1-1-2 */
+ {
+ SNOR_HWCAPS_READ_1_1_2,
+ BFPT_DWORD(1), BIT(16), /* Supported bit */
+ BFPT_DWORD(4), 0, /* Settings */
+ SNOR_PROTO_1_1_2,
+ },
+
+ /* Fast Read 1-2-2 */
+ {
+ SNOR_HWCAPS_READ_1_2_2,
+ BFPT_DWORD(1), BIT(20), /* Supported bit */
+ BFPT_DWORD(4), 16, /* Settings */
+ SNOR_PROTO_1_2_2,
+ },
+
+ /* Fast Read 2-2-2 */
+ {
+ SNOR_HWCAPS_READ_2_2_2,
+ BFPT_DWORD(5), BIT(0), /* Supported bit */
+ BFPT_DWORD(6), 16, /* Settings */
+ SNOR_PROTO_2_2_2,
+ },
+
+ /* Fast Read 1-1-4 */
+ {
+ SNOR_HWCAPS_READ_1_1_4,
+ BFPT_DWORD(1), BIT(22), /* Supported bit */
+ BFPT_DWORD(3), 16, /* Settings */
+ SNOR_PROTO_1_1_4,
+ },
+
+ /* Fast Read 1-4-4 */
+ {
+ SNOR_HWCAPS_READ_1_4_4,
+ BFPT_DWORD(1), BIT(21), /* Supported bit */
+ BFPT_DWORD(3), 0, /* Settings */
+ SNOR_PROTO_1_4_4,
+ },
+
+ /* Fast Read 4-4-4 */
+ {
+ SNOR_HWCAPS_READ_4_4_4,
+ BFPT_DWORD(5), BIT(4), /* Supported bit */
+ BFPT_DWORD(7), 16, /* Settings */
+ SNOR_PROTO_4_4_4,
+ },
+};
+
+struct sfdp_bfpt_erase {
+ /*
+ * The half-word at offset <shift> in DWORD <dwoard> encodes the
+ * op code and erase sector size to be used by Sector Erase commands.
+ */
+ u32 dword;
+ u32 shift;
+};
+
+static const struct sfdp_bfpt_erase sfdp_bfpt_erases[] = {
+ /* Erase Type 1 in DWORD8 bits[15:0] */
+ {BFPT_DWORD(8), 0},
+
+ /* Erase Type 2 in DWORD8 bits[31:16] */
+ {BFPT_DWORD(8), 16},
+
+ /* Erase Type 3 in DWORD9 bits[15:0] */
+ {BFPT_DWORD(9), 0},
+
+ /* Erase Type 4 in DWORD9 bits[31:16] */
+ {BFPT_DWORD(9), 16},
+};
+
+/**
+ * spi_nor_set_erase_type() - set a SPI NOR erase type
+ * @erase: pointer to a structure that describes a SPI NOR erase type
+ * @size: the size of the sector/block erased by the erase type
+ * @opcode: the SPI command op code to erase the sector/block
+ */
+static void spi_nor_set_erase_type(struct spi_nor_erase_type *erase,
+ u32 size, u8 opcode)
+{
+ erase->size = size;
+ erase->opcode = opcode;
+ /* JEDEC JESD216B Standard imposes erase sizes to be power of 2. */
+ erase->size_shift = ffs(erase->size) - 1;
+ erase->size_mask = (1 << erase->size_shift) - 1;
+}
+
+/**
+ * spi_nor_set_erase_settings_from_bfpt() - set erase type settings from BFPT
+ * @erase: pointer to a structure that describes a SPI NOR erase type
+ * @size: the size of the sector/block erased by the erase type
+ * @opcode: the SPI command op code to erase the sector/block
+ * @i: erase type index as sorted in the Basic Flash Parameter Table
+ *
+ * The supported Erase Types will be sorted at init in ascending order, with
+ * the smallest Erase Type size being the first member in the erase_type array
+ * of the spi_nor_erase_map structure. Save the Erase Type index as sorted in
+ * the Basic Flash Parameter Table since it will be used later on to
+ * synchronize with the supported Erase Types defined in SFDP optional tables.
+ */
+static void
+spi_nor_set_erase_settings_from_bfpt(struct spi_nor_erase_type *erase,
+ u32 size, u8 opcode, u8 i)
+{
+ erase->idx = i;
+ spi_nor_set_erase_type(erase, size, opcode);
+}
+
+/**
+ * spi_nor_map_cmp_erase_type() - compare the map's erase types by size
+ * @l: member in the left half of the map's erase_type array
+ * @r: member in the right half of the map's erase_type array
+ *
+ * Comparison function used in the sort() call to sort in ascending order the
+ * map's erase types, the smallest erase type size being the first member in the
+ * sorted erase_type array.
+ *
+ * Return: the result of @l->size - @r->size
+ */
+static int spi_nor_map_cmp_erase_type(const void *l, const void *r)
+{
+ const struct spi_nor_erase_type *left = l, *right = r;
+
+ return left->size - right->size;
+}
+
+/**
+ * spi_nor_sort_erase_mask() - sort erase mask
+ * @map: the erase map of the SPI NOR
+ * @erase_mask: the erase type mask to be sorted
+ *
+ * Replicate the sort done for the map's erase types in BFPT: sort the erase
+ * mask in ascending order with the smallest erase type size starting from
+ * BIT(0) in the sorted erase mask.
+ *
+ * Return: sorted erase mask.
+ */
+static u8 spi_nor_sort_erase_mask(struct spi_nor_erase_map *map, u8 erase_mask)
+{
+ struct spi_nor_erase_type *erase_type = map->erase_type;
+ int i;
+ u8 sorted_erase_mask = 0;
+
+ if (!erase_mask)
+ return 0;
+
+ /* Replicate the sort done for the map's erase types. */
+ for (i = 0; i < SNOR_ERASE_TYPE_MAX; i++)
+ if (erase_type[i].size && erase_mask & BIT(erase_type[i].idx))
+ sorted_erase_mask |= BIT(i);
+
+ return sorted_erase_mask;
+}
+
+/**
+ * spi_nor_regions_sort_erase_types() - sort erase types in each region
+ * @map: the erase map of the SPI NOR
+ *
+ * Function assumes that the erase types defined in the erase map are already
+ * sorted in ascending order, with the smallest erase type size being the first
+ * member in the erase_type array. It replicates the sort done for the map's
+ * erase types. Each region's erase bitmask will indicate which erase types are
+ * supported from the sorted erase types defined in the erase map.
+ * Sort the all region's erase type at init in order to speed up the process of
+ * finding the best erase command at runtime.
+ */
+static void spi_nor_regions_sort_erase_types(struct spi_nor_erase_map *map)
+{
+ struct spi_nor_erase_region *region = map->regions;
+ u8 region_erase_mask, sorted_erase_mask;
+
+ while (region) {
+ region_erase_mask = region->offset & SNOR_ERASE_TYPE_MASK;
+
+ sorted_erase_mask = spi_nor_sort_erase_mask(map,
+ region_erase_mask);
+
+ /* Overwrite erase mask. */
+ region->offset = (region->offset & ~SNOR_ERASE_TYPE_MASK) |
+ sorted_erase_mask;
+
+ region = spi_nor_region_next(region);
+ }
+}
+
+/**
+ * spi_nor_init_uniform_erase_map() - Initialize uniform erase map
+ * @map: the erase map of the SPI NOR
+ * @erase_mask: bitmask encoding erase types that can erase the entire
+ * flash memory
+ * @flash_size: the spi nor flash memory size
+ */
+static void spi_nor_init_uniform_erase_map(struct spi_nor_erase_map *map,
+ u8 erase_mask, u64 flash_size)
+{
+ /* Offset 0 with erase_mask and SNOR_LAST_REGION bit set */
+ map->uniform_region.offset = (erase_mask & SNOR_ERASE_TYPE_MASK) |
+ SNOR_LAST_REGION;
+ map->uniform_region.size = flash_size;
+ map->regions = &map->uniform_region;
+ map->uniform_erase_type = erase_mask;
+}
+
+static int
+spi_nor_post_bfpt_fixups(struct spi_nor *nor,
+ const struct sfdp_parameter_header *bfpt_header,
+ const struct sfdp_bfpt *bfpt,
+ struct spi_nor_flash_parameter *params)
+{
+ if (nor->info->fixups && nor->info->fixups->post_bfpt)
+ return nor->info->fixups->post_bfpt(nor, bfpt_header, bfpt,
+ params);
+
+ return 0;
+}
+
+/**
+ * spi_nor_parse_bfpt() - read and parse the Basic Flash Parameter Table.
+ * @nor: pointer to a 'struct spi_nor'
+ * @bfpt_header: pointer to the 'struct sfdp_parameter_header' describing
+ * the Basic Flash Parameter Table length and version
+ * @params: pointer to the 'struct spi_nor_flash_parameter' to be
+ * filled
+ *
+ * The Basic Flash Parameter Table is the main and only mandatory table as
+ * defined by the SFDP (JESD216) specification.
+ * It provides us with the total size (memory density) of the data array and
+ * the number of address bytes for Fast Read, Page Program and Sector Erase
+ * commands.
+ * For Fast READ commands, it also gives the number of mode clock cycles and
+ * wait states (regrouped in the number of dummy clock cycles) for each
+ * supported instruction op code.
+ * For Page Program, the page size is now available since JESD216 rev A, however
+ * the supported instruction op codes are still not provided.
+ * For Sector Erase commands, this table stores the supported instruction op
+ * codes and the associated sector sizes.
+ * Finally, the Quad Enable Requirements (QER) are also available since JESD216
+ * rev A. The QER bits encode the manufacturer dependent procedure to be
+ * executed to set the Quad Enable (QE) bit in some internal register of the
+ * Quad SPI memory. Indeed the QE bit, when it exists, must be set before
+ * sending any Quad SPI command to the memory. Actually, setting the QE bit
+ * tells the memory to reassign its WP# and HOLD#/RESET# pins to functions IO2
+ * and IO3 hence enabling 4 (Quad) I/O lines.
+ *
+ * Return: 0 on success, -errno otherwise.
+ */
+static int spi_nor_parse_bfpt(struct spi_nor *nor,
+ const struct sfdp_parameter_header *bfpt_header,
+ struct spi_nor_flash_parameter *params)
+{
+ struct spi_nor_erase_map *map = &params->erase_map;
+ struct spi_nor_erase_type *erase_type = map->erase_type;
+ struct sfdp_bfpt bfpt;
+ size_t len;
+ int i, cmd, err;
+ u32 addr;
+ u16 half;
+ u8 erase_mask;
+
+ /* JESD216 Basic Flash Parameter Table length is at least 9 DWORDs. */
+ if (bfpt_header->length < BFPT_DWORD_MAX_JESD216)
+ return -EINVAL;
+
+ /* Read the Basic Flash Parameter Table. */
+ len = min_t(size_t, sizeof(bfpt),
+ bfpt_header->length * sizeof(u32));
+ addr = SFDP_PARAM_HEADER_PTP(bfpt_header);
+ memset(&bfpt, 0, sizeof(bfpt));
+ err = spi_nor_read_sfdp_dma_unsafe(nor, addr, len, &bfpt);
+ if (err < 0)
+ return err;
+
+ /* Fix endianness of the BFPT DWORDs. */
+ for (i = 0; i < BFPT_DWORD_MAX; i++)
+ bfpt.dwords[i] = le32_to_cpu(bfpt.dwords[i]);
+
+ /* Number of address bytes. */
+ switch (bfpt.dwords[BFPT_DWORD(1)] & BFPT_DWORD1_ADDRESS_BYTES_MASK) {
+ case BFPT_DWORD1_ADDRESS_BYTES_3_ONLY:
+ nor->addr_width = 3;
+ break;
+
+ case BFPT_DWORD1_ADDRESS_BYTES_4_ONLY:
+ nor->addr_width = 4;
+ break;
+
+ default:
+ break;
+ }
+
+ /* Flash Memory Density (in bits). */
+ params->size = bfpt.dwords[BFPT_DWORD(2)];
+ if (params->size & BIT(31)) {
+ params->size &= ~BIT(31);
+
+ /*
+ * Prevent overflows on params->size. Anyway, a NOR of 2^64
+ * bits is unlikely to exist so this error probably means
+ * the BFPT we are reading is corrupted/wrong.
+ */
+ if (params->size > 63)
+ return -EINVAL;
+
+ params->size = 1ULL << params->size;
+ } else {
+ params->size++;
+ }
+ params->size >>= 3; /* Convert to bytes. */
+
+ /* Fast Read settings. */
+ for (i = 0; i < ARRAY_SIZE(sfdp_bfpt_reads); i++) {
+ const struct sfdp_bfpt_read *rd = &sfdp_bfpt_reads[i];
+ struct spi_nor_read_command *read;
+
+ if (!(bfpt.dwords[rd->supported_dword] & rd->supported_bit)) {
+ params->hwcaps.mask &= ~rd->hwcaps;
+ continue;
+ }
+
+ params->hwcaps.mask |= rd->hwcaps;
+ cmd = spi_nor_hwcaps_read2cmd(rd->hwcaps);
+ read = &params->reads[cmd];
+ half = bfpt.dwords[rd->settings_dword] >> rd->settings_shift;
+ spi_nor_set_read_settings_from_bfpt(read, half, rd->proto);
+ }
+
+ /*
+ * Sector Erase settings. Reinitialize the uniform erase map using the
+ * Erase Types defined in the bfpt table.
+ */
+ erase_mask = 0;
+ memset(&params->erase_map, 0, sizeof(params->erase_map));
+ for (i = 0; i < ARRAY_SIZE(sfdp_bfpt_erases); i++) {
+ const struct sfdp_bfpt_erase *er = &sfdp_bfpt_erases[i];
+ u32 erasesize;
+ u8 opcode;
+
+ half = bfpt.dwords[er->dword] >> er->shift;
+ erasesize = half & 0xff;
+
+ /* erasesize == 0 means this Erase Type is not supported. */
+ if (!erasesize)
+ continue;
+
+ erasesize = 1U << erasesize;
+ opcode = (half >> 8) & 0xff;
+ erase_mask |= BIT(i);
+ spi_nor_set_erase_settings_from_bfpt(&erase_type[i], erasesize,
+ opcode, i);
+ }
+ spi_nor_init_uniform_erase_map(map, erase_mask, params->size);
+ /*
+ * Sort all the map's Erase Types in ascending order with the smallest
+ * erase size being the first member in the erase_type array.
+ */
+ sort(erase_type, SNOR_ERASE_TYPE_MAX, sizeof(erase_type[0]),
+ spi_nor_map_cmp_erase_type, NULL);
+ /*
+ * Sort the erase types in the uniform region in order to update the
+ * uniform_erase_type bitmask. The bitmask will be used later on when
+ * selecting the uniform erase.
+ */
+ spi_nor_regions_sort_erase_types(map);
+ map->uniform_erase_type = map->uniform_region.offset &
+ SNOR_ERASE_TYPE_MASK;
+
+ /* Stop here if not JESD216 rev A or later. */
+ if (bfpt_header->length < BFPT_DWORD_MAX)
+ return spi_nor_post_bfpt_fixups(nor, bfpt_header, &bfpt,
+ params);
+
+ /* Page size: this field specifies 'N' so the page size = 2^N bytes. */
+ params->page_size = bfpt.dwords[BFPT_DWORD(11)];
+ params->page_size &= BFPT_DWORD11_PAGE_SIZE_MASK;
+ params->page_size >>= BFPT_DWORD11_PAGE_SIZE_SHIFT;
+ params->page_size = 1U << params->page_size;
+
+ /* Quad Enable Requirements. */
+ switch (bfpt.dwords[BFPT_DWORD(15)] & BFPT_DWORD15_QER_MASK) {
+ case BFPT_DWORD15_QER_NONE:
+ params->quad_enable = NULL;
+ break;
+
+ case BFPT_DWORD15_QER_SR2_BIT1_BUGGY:
+ case BFPT_DWORD15_QER_SR2_BIT1_NO_RD:
+ params->quad_enable = spansion_no_read_cr_quad_enable;
+ break;
+
+ case BFPT_DWORD15_QER_SR1_BIT6:
+ params->quad_enable = macronix_quad_enable;
+ break;
+
+ case BFPT_DWORD15_QER_SR2_BIT7:
+ params->quad_enable = sr2_bit7_quad_enable;
+ break;
+
+ case BFPT_DWORD15_QER_SR2_BIT1:
+ params->quad_enable = spansion_read_cr_quad_enable;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return spi_nor_post_bfpt_fixups(nor, bfpt_header, &bfpt, params);
+}
+
+#define SMPT_CMD_ADDRESS_LEN_MASK GENMASK(23, 22)
+#define SMPT_CMD_ADDRESS_LEN_0 (0x0UL << 22)
+#define SMPT_CMD_ADDRESS_LEN_3 (0x1UL << 22)
+#define SMPT_CMD_ADDRESS_LEN_4 (0x2UL << 22)
+#define SMPT_CMD_ADDRESS_LEN_USE_CURRENT (0x3UL << 22)
+
+#define SMPT_CMD_READ_DUMMY_MASK GENMASK(19, 16)
+#define SMPT_CMD_READ_DUMMY_SHIFT 16
+#define SMPT_CMD_READ_DUMMY(_cmd) \
+ (((_cmd) & SMPT_CMD_READ_DUMMY_MASK) >> SMPT_CMD_READ_DUMMY_SHIFT)
+#define SMPT_CMD_READ_DUMMY_IS_VARIABLE 0xfUL
+
+#define SMPT_CMD_READ_DATA_MASK GENMASK(31, 24)
+#define SMPT_CMD_READ_DATA_SHIFT 24
+#define SMPT_CMD_READ_DATA(_cmd) \
+ (((_cmd) & SMPT_CMD_READ_DATA_MASK) >> SMPT_CMD_READ_DATA_SHIFT)
+
+#define SMPT_CMD_OPCODE_MASK GENMASK(15, 8)
+#define SMPT_CMD_OPCODE_SHIFT 8
+#define SMPT_CMD_OPCODE(_cmd) \
+ (((_cmd) & SMPT_CMD_OPCODE_MASK) >> SMPT_CMD_OPCODE_SHIFT)
+
+#define SMPT_MAP_REGION_COUNT_MASK GENMASK(23, 16)
+#define SMPT_MAP_REGION_COUNT_SHIFT 16
+#define SMPT_MAP_REGION_COUNT(_header) \
+ ((((_header) & SMPT_MAP_REGION_COUNT_MASK) >> \
+ SMPT_MAP_REGION_COUNT_SHIFT) + 1)
+
+#define SMPT_MAP_ID_MASK GENMASK(15, 8)
+#define SMPT_MAP_ID_SHIFT 8
+#define SMPT_MAP_ID(_header) \
+ (((_header) & SMPT_MAP_ID_MASK) >> SMPT_MAP_ID_SHIFT)
+
+#define SMPT_MAP_REGION_SIZE_MASK GENMASK(31, 8)
+#define SMPT_MAP_REGION_SIZE_SHIFT 8
+#define SMPT_MAP_REGION_SIZE(_region) \
+ (((((_region) & SMPT_MAP_REGION_SIZE_MASK) >> \
+ SMPT_MAP_REGION_SIZE_SHIFT) + 1) * 256)
+
+#define SMPT_MAP_REGION_ERASE_TYPE_MASK GENMASK(3, 0)
+#define SMPT_MAP_REGION_ERASE_TYPE(_region) \
+ ((_region) & SMPT_MAP_REGION_ERASE_TYPE_MASK)
+
+#define SMPT_DESC_TYPE_MAP BIT(1)
+#define SMPT_DESC_END BIT(0)
+
+/**
+ * spi_nor_smpt_addr_width() - return the address width used in the
+ * configuration detection command.
+ * @nor: pointer to a 'struct spi_nor'
+ * @settings: configuration detection command descriptor, dword1
+ */
+static u8 spi_nor_smpt_addr_width(const struct spi_nor *nor, const u32 settings)
+{
+ switch (settings & SMPT_CMD_ADDRESS_LEN_MASK) {
+ case SMPT_CMD_ADDRESS_LEN_0:
+ return 0;
+ case SMPT_CMD_ADDRESS_LEN_3:
+ return 3;
+ case SMPT_CMD_ADDRESS_LEN_4:
+ return 4;
+ case SMPT_CMD_ADDRESS_LEN_USE_CURRENT:
+ /* fall through */
+ default:
+ return nor->addr_width;
+ }
+}
+
+/**
+ * spi_nor_smpt_read_dummy() - return the configuration detection command read
+ * latency, in clock cycles.
+ * @nor: pointer to a 'struct spi_nor'
+ * @settings: configuration detection command descriptor, dword1
+ *
+ * Return: the number of dummy cycles for an SMPT read
+ */
+static u8 spi_nor_smpt_read_dummy(const struct spi_nor *nor, const u32 settings)
+{
+ u8 read_dummy = SMPT_CMD_READ_DUMMY(settings);
+
+ if (read_dummy == SMPT_CMD_READ_DUMMY_IS_VARIABLE)
+ return nor->read_dummy;
+ return read_dummy;
+}
+
+/**
+ * spi_nor_get_map_in_use() - get the configuration map in use
+ * @nor: pointer to a 'struct spi_nor'
+ * @smpt: pointer to the sector map parameter table
+ * @smpt_len: sector map parameter table length
+ *
+ * Return: pointer to the map in use, ERR_PTR(-errno) otherwise.
+ */
+static const u32 *spi_nor_get_map_in_use(struct spi_nor *nor, const u32 *smpt,
+ u8 smpt_len)
+{
+ const u32 *ret;
+ u8 *buf;
+ u32 addr;
+ int err;
+ u8 i;
+ u8 addr_width, read_opcode, read_dummy;
+ u8 read_data_mask, map_id;
+
+ /* Use a kmalloc'ed bounce buffer to guarantee it is DMA-able. */
+ buf = kmalloc(sizeof(*buf), GFP_KERNEL);
+ if (!buf)
+ return ERR_PTR(-ENOMEM);
+
+ addr_width = nor->addr_width;
+ read_dummy = nor->read_dummy;
+ read_opcode = nor->read_opcode;
+
+ map_id = 0;
+ /* Determine if there are any optional Detection Command Descriptors */
+ for (i = 0; i < smpt_len; i += 2) {
+ if (smpt[i] & SMPT_DESC_TYPE_MAP)
+ break;
+
+ read_data_mask = SMPT_CMD_READ_DATA(smpt[i]);
+ nor->addr_width = spi_nor_smpt_addr_width(nor, smpt[i]);
+ nor->read_dummy = spi_nor_smpt_read_dummy(nor, smpt[i]);
+ nor->read_opcode = SMPT_CMD_OPCODE(smpt[i]);
+ addr = smpt[i + 1];
+
+ err = spi_nor_read_raw(nor, addr, 1, buf);
+ if (err) {
+ ret = ERR_PTR(err);
+ goto out;
+ }
+
+ /*
+ * Build an index value that is used to select the Sector Map
+ * Configuration that is currently in use.
+ */
+ map_id = map_id << 1 | !!(*buf & read_data_mask);
+ }
+
+ /*
+ * If command descriptors are provided, they always precede map
+ * descriptors in the table. There is no need to start the iteration
+ * over smpt array all over again.
+ *
+ * Find the matching configuration map.
+ */
+ ret = ERR_PTR(-EINVAL);
+ while (i < smpt_len) {
+ if (SMPT_MAP_ID(smpt[i]) == map_id) {
+ ret = smpt + i;
+ break;
+ }
+
+ /*
+ * If there are no more configuration map descriptors and no
+ * configuration ID matched the configuration identifier, the
+ * sector address map is unknown.
+ */
+ if (smpt[i] & SMPT_DESC_END)
+ break;
+
+ /* increment the table index to the next map */
+ i += SMPT_MAP_REGION_COUNT(smpt[i]) + 1;
+ }
+
+ /* fall through */
+out:
+ kfree(buf);
+ nor->addr_width = addr_width;
+ nor->read_dummy = read_dummy;
+ nor->read_opcode = read_opcode;
+ return ret;
+}
+
+/**
+ * spi_nor_region_check_overlay() - set overlay bit when the region is overlaid
+ * @region: pointer to a structure that describes a SPI NOR erase region
+ * @erase: pointer to a structure that describes a SPI NOR erase type
+ * @erase_type: erase type bitmask
+ */
+static void
+spi_nor_region_check_overlay(struct spi_nor_erase_region *region,
+ const struct spi_nor_erase_type *erase,
+ const u8 erase_type)
+{
+ int i;
+
+ for (i = 0; i < SNOR_ERASE_TYPE_MAX; i++) {
+ if (!(erase_type & BIT(i)))
+ continue;
+ if (region->size & erase[i].size_mask) {
+ spi_nor_region_mark_overlay(region);
+ return;
+ }
+ }
+}
+
+/**
+ * spi_nor_init_non_uniform_erase_map() - initialize the non-uniform erase map
+ * @nor: pointer to a 'struct spi_nor'
+ * @params: pointer to a duplicate 'struct spi_nor_flash_parameter' that is
+ * used for storing SFDP parsed data
+ * @smpt: pointer to the sector map parameter table
+ *
+ * Return: 0 on success, -errno otherwise.
+ */
+static int
+spi_nor_init_non_uniform_erase_map(struct spi_nor *nor,
+ struct spi_nor_flash_parameter *params,
+ const u32 *smpt)
+{
+ struct spi_nor_erase_map *map = &params->erase_map;
+ struct spi_nor_erase_type *erase = map->erase_type;
+ struct spi_nor_erase_region *region;
+ u64 offset;
+ u32 region_count;
+ int i, j;
+ u8 uniform_erase_type, save_uniform_erase_type;
+ u8 erase_type, regions_erase_type;
+
+ region_count = SMPT_MAP_REGION_COUNT(*smpt);
+ /*
+ * The regions will be freed when the driver detaches from the
+ * device.
+ */
+ region = devm_kcalloc(nor->dev, region_count, sizeof(*region),
+ GFP_KERNEL);
+ if (!region)
+ return -ENOMEM;
+ map->regions = region;
+
+ uniform_erase_type = 0xff;
+ regions_erase_type = 0;
+ offset = 0;
+ /* Populate regions. */
+ for (i = 0; i < region_count; i++) {
+ j = i + 1; /* index for the region dword */
+ region[i].size = SMPT_MAP_REGION_SIZE(smpt[j]);
+ erase_type = SMPT_MAP_REGION_ERASE_TYPE(smpt[j]);
+ region[i].offset = offset | erase_type;
+
+ spi_nor_region_check_overlay(&region[i], erase, erase_type);
+
+ /*
+ * Save the erase types that are supported in all regions and
+ * can erase the entire flash memory.
+ */
+ uniform_erase_type &= erase_type;
+
+ /*
+ * regions_erase_type mask will indicate all the erase types
+ * supported in this configuration map.
+ */
+ regions_erase_type |= erase_type;
+
+ offset = (region[i].offset & ~SNOR_ERASE_FLAGS_MASK) +
+ region[i].size;
+ }
+
+ save_uniform_erase_type = map->uniform_erase_type;
+ map->uniform_erase_type = spi_nor_sort_erase_mask(map,
+ uniform_erase_type);
+
+ if (!regions_erase_type) {
+ /*
+ * Roll back to the previous uniform_erase_type mask, SMPT is
+ * broken.
+ */
+ map->uniform_erase_type = save_uniform_erase_type;
+ return -EINVAL;
+ }
+
+ /*
+ * BFPT advertises all the erase types supported by all the possible
+ * map configurations. Mask out the erase types that are not supported
+ * by the current map configuration.
+ */
+ for (i = 0; i < SNOR_ERASE_TYPE_MAX; i++)
+ if (!(regions_erase_type & BIT(erase[i].idx)))
+ spi_nor_set_erase_type(&erase[i], 0, 0xFF);
+
+ spi_nor_region_mark_end(&region[i - 1]);
+
+ return 0;
+}
+
+/**
+ * spi_nor_parse_smpt() - parse Sector Map Parameter Table
+ * @nor: pointer to a 'struct spi_nor'
+ * @smpt_header: sector map parameter table header
+ * @params: pointer to a duplicate 'struct spi_nor_flash_parameter'
+ * that is used for storing SFDP parsed data
+ *
+ * This table is optional, but when available, we parse it to identify the
+ * location and size of sectors within the main data array of the flash memory
+ * device and to identify which Erase Types are supported by each sector.
+ *
+ * Return: 0 on success, -errno otherwise.
+ */
+static int spi_nor_parse_smpt(struct spi_nor *nor,
+ const struct sfdp_parameter_header *smpt_header,
+ struct spi_nor_flash_parameter *params)
+{
+ const u32 *sector_map;
+ u32 *smpt;
+ size_t len;
+ u32 addr;
+ int i, ret;
+
+ /* Read the Sector Map Parameter Table. */
+ len = smpt_header->length * sizeof(*smpt);
+ smpt = kmalloc(len, GFP_KERNEL);
+ if (!smpt)
+ return -ENOMEM;
+
+ addr = SFDP_PARAM_HEADER_PTP(smpt_header);
+ ret = spi_nor_read_sfdp(nor, addr, len, smpt);
+ if (ret)
+ goto out;
+
+ /* Fix endianness of the SMPT DWORDs. */
+ for (i = 0; i < smpt_header->length; i++)
+ smpt[i] = le32_to_cpu(smpt[i]);
+
+ sector_map = spi_nor_get_map_in_use(nor, smpt, smpt_header->length);
+ if (IS_ERR(sector_map)) {
+ ret = PTR_ERR(sector_map);
+ goto out;
+ }
+
+ ret = spi_nor_init_non_uniform_erase_map(nor, params, sector_map);
+ if (ret)
+ goto out;
+
+ spi_nor_regions_sort_erase_types(&params->erase_map);
+ /* fall through */
+out:
+ kfree(smpt);
+ return ret;
+}
+
+#define SFDP_4BAIT_DWORD_MAX 2
+
+struct sfdp_4bait {
+ /* The hardware capability. */
+ u32 hwcaps;
+
+ /*
+ * The <supported_bit> bit in DWORD1 of the 4BAIT tells us whether
+ * the associated 4-byte address op code is supported.
+ */
+ u32 supported_bit;
+};
+
+/**
+ * spi_nor_parse_4bait() - parse the 4-Byte Address Instruction Table
+ * @nor: pointer to a 'struct spi_nor'.
+ * @param_header: pointer to the 'struct sfdp_parameter_header' describing
+ * the 4-Byte Address Instruction Table length and version.
+ * @params: pointer to the 'struct spi_nor_flash_parameter' to be.
+ *
+ * Return: 0 on success, -errno otherwise.
+ */
+static int spi_nor_parse_4bait(struct spi_nor *nor,
+ const struct sfdp_parameter_header *param_header,
+ struct spi_nor_flash_parameter *params)
+{
+ static const struct sfdp_4bait reads[] = {
+ { SNOR_HWCAPS_READ, BIT(0) },
+ { SNOR_HWCAPS_READ_FAST, BIT(1) },
+ { SNOR_HWCAPS_READ_1_1_2, BIT(2) },
+ { SNOR_HWCAPS_READ_1_2_2, BIT(3) },
+ { SNOR_HWCAPS_READ_1_1_4, BIT(4) },
+ { SNOR_HWCAPS_READ_1_4_4, BIT(5) },
+ { SNOR_HWCAPS_READ_1_1_1_DTR, BIT(13) },
+ { SNOR_HWCAPS_READ_1_2_2_DTR, BIT(14) },
+ { SNOR_HWCAPS_READ_1_4_4_DTR, BIT(15) },
+ };
+ static const struct sfdp_4bait programs[] = {
+ { SNOR_HWCAPS_PP, BIT(6) },
+ { SNOR_HWCAPS_PP_1_1_4, BIT(7) },
+ { SNOR_HWCAPS_PP_1_4_4, BIT(8) },
+ };
+ static const struct sfdp_4bait erases[SNOR_ERASE_TYPE_MAX] = {
+ { 0u /* not used */, BIT(9) },
+ { 0u /* not used */, BIT(10) },
+ { 0u /* not used */, BIT(11) },
+ { 0u /* not used */, BIT(12) },
+ };
+ struct spi_nor_pp_command *params_pp = params->page_programs;
+ struct spi_nor_erase_map *map = &params->erase_map;
+ struct spi_nor_erase_type *erase_type = map->erase_type;
+ u32 *dwords;
+ size_t len;
+ u32 addr, discard_hwcaps, read_hwcaps, pp_hwcaps, erase_mask;
+ int i, ret;
+
+ if (param_header->major != SFDP_JESD216_MAJOR ||
+ param_header->length < SFDP_4BAIT_DWORD_MAX)
+ return -EINVAL;
+
+ /* Read the 4-byte Address Instruction Table. */
+ len = sizeof(*dwords) * SFDP_4BAIT_DWORD_MAX;
+
+ /* Use a kmalloc'ed bounce buffer to guarantee it is DMA-able. */
+ dwords = kmalloc(len, GFP_KERNEL);
+ if (!dwords)
+ return -ENOMEM;
+
+ addr = SFDP_PARAM_HEADER_PTP(param_header);
+ ret = spi_nor_read_sfdp(nor, addr, len, dwords);
+ if (ret)
+ goto out;
+
+ /* Fix endianness of the 4BAIT DWORDs. */
+ for (i = 0; i < SFDP_4BAIT_DWORD_MAX; i++)
+ dwords[i] = le32_to_cpu(dwords[i]);
+
+ /*
+ * Compute the subset of (Fast) Read commands for which the 4-byte
+ * version is supported.
+ */
+ discard_hwcaps = 0;
+ read_hwcaps = 0;
+ for (i = 0; i < ARRAY_SIZE(reads); i++) {
+ const struct sfdp_4bait *read = &reads[i];
+
+ discard_hwcaps |= read->hwcaps;
+ if ((params->hwcaps.mask & read->hwcaps) &&
+ (dwords[0] & read->supported_bit))
+ read_hwcaps |= read->hwcaps;
+ }
+
+ /*
+ * Compute the subset of Page Program commands for which the 4-byte
+ * version is supported.
+ */
+ pp_hwcaps = 0;
+ for (i = 0; i < ARRAY_SIZE(programs); i++) {
+ const struct sfdp_4bait *program = &programs[i];
+
+ /*
+ * The 4 Byte Address Instruction (Optional) Table is the only
+ * SFDP table that indicates support for Page Program Commands.
+ * Bypass the params->hwcaps.mask and consider 4BAIT the biggest
+ * authority for specifying Page Program support.
+ */
+ discard_hwcaps |= program->hwcaps;
+ if (dwords[0] & program->supported_bit)
+ pp_hwcaps |= program->hwcaps;
+ }
+
+ /*
+ * Compute the subset of Sector Erase commands for which the 4-byte
+ * version is supported.
+ */
+ erase_mask = 0;
+ for (i = 0; i < SNOR_ERASE_TYPE_MAX; i++) {
+ const struct sfdp_4bait *erase = &erases[i];
+
+ if (dwords[0] & erase->supported_bit)
+ erase_mask |= BIT(i);
+ }
+
+ /* Replicate the sort done for the map's erase types in BFPT. */
+ erase_mask = spi_nor_sort_erase_mask(map, erase_mask);
+
+ /*
+ * We need at least one 4-byte op code per read, program and erase
+ * operation; the .read(), .write() and .erase() hooks share the
+ * nor->addr_width value.
+ */
+ if (!read_hwcaps || !pp_hwcaps || !erase_mask)
+ goto out;
+
+ /*
+ * Discard all operations from the 4-byte instruction set which are
+ * not supported by this memory.
+ */
+ params->hwcaps.mask &= ~discard_hwcaps;
+ params->hwcaps.mask |= (read_hwcaps | pp_hwcaps);
+
+ /* Use the 4-byte address instruction set. */
+ for (i = 0; i < SNOR_CMD_READ_MAX; i++) {
+ struct spi_nor_read_command *read_cmd = &params->reads[i];
+
+ read_cmd->opcode = spi_nor_convert_3to4_read(read_cmd->opcode);
+ }
+
+ /* 4BAIT is the only SFDP table that indicates page program support. */
+ if (pp_hwcaps & SNOR_HWCAPS_PP)
+ spi_nor_set_pp_settings(&params_pp[SNOR_CMD_PP],
+ SPINOR_OP_PP_4B, SNOR_PROTO_1_1_1);
+ if (pp_hwcaps & SNOR_HWCAPS_PP_1_1_4)
+ spi_nor_set_pp_settings(&params_pp[SNOR_CMD_PP_1_1_4],
+ SPINOR_OP_PP_1_1_4_4B,
+ SNOR_PROTO_1_1_4);
+ if (pp_hwcaps & SNOR_HWCAPS_PP_1_4_4)
+ spi_nor_set_pp_settings(&params_pp[SNOR_CMD_PP_1_4_4],
+ SPINOR_OP_PP_1_4_4_4B,
+ SNOR_PROTO_1_4_4);
+
+ for (i = 0; i < SNOR_ERASE_TYPE_MAX; i++) {
+ if (erase_mask & BIT(i))
+ erase_type[i].opcode = (dwords[1] >>
+ erase_type[i].idx * 8) & 0xFF;
+ else
+ spi_nor_set_erase_type(&erase_type[i], 0u, 0xFF);
+ }
+
+ /*
+ * We set SNOR_F_HAS_4BAIT in order to skip spi_nor_set_4byte_opcodes()
+ * later because we already did the conversion to 4byte opcodes. Also,
+ * this latest function implements a legacy quirk for the erase size of
+ * Spansion memory. However this quirk is no longer needed with new
+ * SFDP compliant memories.
+ */
+ nor->addr_width = 4;
+ nor->flags |= SNOR_F_4B_OPCODES | SNOR_F_HAS_4BAIT;
+
+ /* fall through */
+out:
+ kfree(dwords);
+ return ret;
+}
+
+/**
+ * spi_nor_parse_sfdp() - parse the Serial Flash Discoverable Parameters.
+ * @nor: pointer to a 'struct spi_nor'
+ * @params: pointer to the 'struct spi_nor_flash_parameter' to be
+ * filled
+ *
+ * The Serial Flash Discoverable Parameters are described by the JEDEC JESD216
+ * specification. This is a standard which tends to supported by almost all
+ * (Q)SPI memory manufacturers. Those hard-coded tables allow us to learn at
+ * runtime the main parameters needed to perform basic SPI flash operations such
+ * as Fast Read, Page Program or Sector Erase commands.
+ *
+ * Return: 0 on success, -errno otherwise.
+ */
+static int spi_nor_parse_sfdp(struct spi_nor *nor,
+ struct spi_nor_flash_parameter *params)
+{
+ const struct sfdp_parameter_header *param_header, *bfpt_header;
+ struct sfdp_parameter_header *param_headers = NULL;
+ struct sfdp_header header;
+ struct device *dev = nor->dev;
+ size_t psize;
+ int i, err;
+
+ /* Get the SFDP header. */
+ err = spi_nor_read_sfdp_dma_unsafe(nor, 0, sizeof(header), &header);
+ if (err < 0)
+ return err;
+
+ /* Check the SFDP header version. */
+ if (le32_to_cpu(header.signature) != SFDP_SIGNATURE ||
+ header.major != SFDP_JESD216_MAJOR)
+ return -EINVAL;
+
+ /*
+ * Verify that the first and only mandatory parameter header is a
+ * Basic Flash Parameter Table header as specified in JESD216.
+ */
+ bfpt_header = &header.bfpt_header;
+ if (SFDP_PARAM_HEADER_ID(bfpt_header) != SFDP_BFPT_ID ||
+ bfpt_header->major != SFDP_JESD216_MAJOR)
+ return -EINVAL;
+
+ /*
+ * Allocate memory then read all parameter headers with a single
+ * Read SFDP command. These parameter headers will actually be parsed
+ * twice: a first time to get the latest revision of the basic flash
+ * parameter table, then a second time to handle the supported optional
+ * tables.
+ * Hence we read the parameter headers once for all to reduce the
+ * processing time. Also we use kmalloc() instead of devm_kmalloc()
+ * because we don't need to keep these parameter headers: the allocated
+ * memory is always released with kfree() before exiting this function.
+ */
+ if (header.nph) {
+ psize = header.nph * sizeof(*param_headers);
+
+ param_headers = kmalloc(psize, GFP_KERNEL);
+ if (!param_headers)
+ return -ENOMEM;
+
+ err = spi_nor_read_sfdp(nor, sizeof(header),
+ psize, param_headers);
+ if (err < 0) {
+ dev_err(dev, "failed to read SFDP parameter headers\n");
+ goto exit;
+ }
+ }
+
+ /*
+ * Check other parameter headers to get the latest revision of
+ * the basic flash parameter table.
+ */
+ for (i = 0; i < header.nph; i++) {
+ param_header = &param_headers[i];
+
+ if (SFDP_PARAM_HEADER_ID(param_header) == SFDP_BFPT_ID &&
+ param_header->major == SFDP_JESD216_MAJOR &&
+ (param_header->minor > bfpt_header->minor ||
+ (param_header->minor == bfpt_header->minor &&
+ param_header->length > bfpt_header->length)))
+ bfpt_header = param_header;
+ }
+
+ err = spi_nor_parse_bfpt(nor, bfpt_header, params);
+ if (err)
+ goto exit;
+
+ /* Parse optional parameter tables. */
+ for (i = 0; i < header.nph; i++) {
+ param_header = &param_headers[i];
+
+ switch (SFDP_PARAM_HEADER_ID(param_header)) {
+ case SFDP_SECTOR_MAP_ID:
+ err = spi_nor_parse_smpt(nor, param_header, params);
+ break;
+
+ case SFDP_4BAIT_ID:
+ err = spi_nor_parse_4bait(nor, param_header, params);
+ break;
+
+ default:
+ break;
+ }
+
+ if (err) {
+ dev_warn(dev, "Failed to parse optional parameter table: %04x\n",
+ SFDP_PARAM_HEADER_ID(param_header));
+ /*
+ * Let's not drop all information we extracted so far
+ * if optional table parsers fail. In case of failing,
+ * each optional parser is responsible to roll back to
+ * the previously known spi_nor data.
+ */
+ err = 0;
+ }
+ }
+
+exit:
+ kfree(param_headers);
+ return err;
+}
+
+static int spi_nor_select_read(struct spi_nor *nor,
+ u32 shared_hwcaps)
+{
+ int cmd, best_match = fls(shared_hwcaps & SNOR_HWCAPS_READ_MASK) - 1;
+ const struct spi_nor_read_command *read;
+
+ if (best_match < 0)
+ return -EINVAL;
+
+ cmd = spi_nor_hwcaps_read2cmd(BIT(best_match));
+ if (cmd < 0)
+ return -EINVAL;
+
+ read = &nor->params.reads[cmd];
+ nor->read_opcode = read->opcode;
+ nor->read_proto = read->proto;
+
+ /*
+ * In the spi-nor framework, we don't need to make the difference
+ * between mode clock cycles and wait state clock cycles.
+ * Indeed, the value of the mode clock cycles is used by a QSPI
+ * flash memory to know whether it should enter or leave its 0-4-4
+ * (Continuous Read / XIP) mode.
+ * eXecution In Place is out of the scope of the mtd sub-system.
+ * Hence we choose to merge both mode and wait state clock cycles
+ * into the so called dummy clock cycles.
+ */
+ nor->read_dummy = read->num_mode_clocks + read->num_wait_states;
+ return 0;
+}
+
+static int spi_nor_select_pp(struct spi_nor *nor,
+ u32 shared_hwcaps)
+{
+ int cmd, best_match = fls(shared_hwcaps & SNOR_HWCAPS_PP_MASK) - 1;
+ const struct spi_nor_pp_command *pp;
+
+ if (best_match < 0)
+ return -EINVAL;
+
+ cmd = spi_nor_hwcaps_pp2cmd(BIT(best_match));
+ if (cmd < 0)
+ return -EINVAL;
+
+ pp = &nor->params.page_programs[cmd];
+ nor->program_opcode = pp->opcode;
+ nor->write_proto = pp->proto;
+ return 0;
+}
+
+/**
+ * spi_nor_select_uniform_erase() - select optimum uniform erase type
+ * @map: the erase map of the SPI NOR
+ * @wanted_size: the erase type size to search for. Contains the value of
+ * info->sector_size or of the "small sector" size in case
+ * CONFIG_MTD_SPI_NOR_USE_4K_SECTORS is defined.
+ *
+ * Once the optimum uniform sector erase command is found, disable all the
+ * other.
+ *
+ * Return: pointer to erase type on success, NULL otherwise.
+ */
+static const struct spi_nor_erase_type *
+spi_nor_select_uniform_erase(struct spi_nor_erase_map *map,
+ const u32 wanted_size)
+{
+ const struct spi_nor_erase_type *tested_erase, *erase = NULL;
+ int i;
+ u8 uniform_erase_type = map->uniform_erase_type;
+
+ for (i = SNOR_ERASE_TYPE_MAX - 1; i >= 0; i--) {
+ if (!(uniform_erase_type & BIT(i)))
+ continue;
+
+ tested_erase = &map->erase_type[i];
+
+ /*
+ * If the current erase size is the one, stop here:
+ * we have found the right uniform Sector Erase command.
+ */
+ if (tested_erase->size == wanted_size) {
+ erase = tested_erase;
+ break;
+ }
+
+ /*
+ * Otherwise, the current erase size is still a valid canditate.
+ * Select the biggest valid candidate.
+ */
+ if (!erase && tested_erase->size)
+ erase = tested_erase;
+ /* keep iterating to find the wanted_size */
+ }
+
+ if (!erase)
+ return NULL;
+
+ /* Disable all other Sector Erase commands. */
+ map->uniform_erase_type &= ~SNOR_ERASE_TYPE_MASK;
+ map->uniform_erase_type |= BIT(erase - map->erase_type);
+ return erase;
+}
+
+static int spi_nor_select_erase(struct spi_nor *nor)
+{
+ struct spi_nor_erase_map *map = &nor->params.erase_map;
+ const struct spi_nor_erase_type *erase = NULL;
+ struct mtd_info *mtd = &nor->mtd;
+ u32 wanted_size = nor->info->sector_size;
+ int i;
+
+ /*
+ * The previous implementation handling Sector Erase commands assumed
+ * that the SPI flash memory has an uniform layout then used only one
+ * of the supported erase sizes for all Sector Erase commands.
+ * So to be backward compatible, the new implementation also tries to
+ * manage the SPI flash memory as uniform with a single erase sector
+ * size, when possible.
+ */
+#ifdef CONFIG_MTD_SPI_NOR_USE_4K_SECTORS
+ /* prefer "small sector" erase if possible */
+ wanted_size = 4096u;
+#endif
+
+ if (spi_nor_has_uniform_erase(nor)) {
+ erase = spi_nor_select_uniform_erase(map, wanted_size);
+ if (!erase)
+ return -EINVAL;
+ nor->erase_opcode = erase->opcode;
+ mtd->erasesize = erase->size;
+ return 0;
+ }
+
+ /*
+ * For non-uniform SPI flash memory, set mtd->erasesize to the
+ * maximum erase sector size. No need to set nor->erase_opcode.
+ */
+ for (i = SNOR_ERASE_TYPE_MAX - 1; i >= 0; i--) {
+ if (map->erase_type[i].size) {
+ erase = &map->erase_type[i];
+ break;
+ }
+ }
+
+ if (!erase)
+ return -EINVAL;
+
+ mtd->erasesize = erase->size;
+ return 0;
+}
+
+static int spi_nor_default_setup(struct spi_nor *nor,
+ const struct spi_nor_hwcaps *hwcaps)
+{
+ struct spi_nor_flash_parameter *params = &nor->params;
+ u32 ignored_mask, shared_mask;
+ int err;
+
+ /*
+ * Keep only the hardware capabilities supported by both the SPI
+ * controller and the SPI flash memory.
+ */
+ shared_mask = hwcaps->mask & params->hwcaps.mask;
+
+ if (nor->spimem) {
+ /*
+ * When called from spi_nor_probe(), all caps are set and we
+ * need to discard some of them based on what the SPI
+ * controller actually supports (using spi_mem_supports_op()).
+ */
+ spi_nor_spimem_adjust_hwcaps(nor, &shared_mask);
+ } else {
+ /*
+ * SPI n-n-n protocols are not supported when the SPI
+ * controller directly implements the spi_nor interface.
+ * Yet another reason to switch to spi-mem.
+ */
+ ignored_mask = SNOR_HWCAPS_X_X_X;
+ if (shared_mask & ignored_mask) {
+ dev_dbg(nor->dev,
+ "SPI n-n-n protocols are not supported.\n");
+ shared_mask &= ~ignored_mask;
+ }
+ }
+
+ /* Select the (Fast) Read command. */
+ err = spi_nor_select_read(nor, shared_mask);
+ if (err) {
+ dev_err(nor->dev,
+ "can't select read settings supported by both the SPI controller and memory.\n");
+ return err;
+ }
+
+ /* Select the Page Program command. */
+ err = spi_nor_select_pp(nor, shared_mask);
+ if (err) {
+ dev_err(nor->dev,
+ "can't select write settings supported by both the SPI controller and memory.\n");
+ return err;
+ }
+
+ /* Select the Sector Erase command. */
+ err = spi_nor_select_erase(nor);
+ if (err) {
+ dev_err(nor->dev,
+ "can't select erase settings supported by both the SPI controller and memory.\n");
+ return err;
+ }
+
+ return 0;
+}
+
+static int spi_nor_setup(struct spi_nor *nor,
+ const struct spi_nor_hwcaps *hwcaps)
+{
+ if (!nor->params.setup)
+ return 0;
+
+ return nor->params.setup(nor, hwcaps);
+}
+
+static void macronix_set_default_init(struct spi_nor *nor)
+{
+ nor->params.quad_enable = macronix_quad_enable;
+ nor->params.set_4byte = macronix_set_4byte;
+}
+
+static void st_micron_set_default_init(struct spi_nor *nor)
+{
+ nor->flags |= SNOR_F_HAS_LOCK;
+ nor->params.quad_enable = NULL;
+ nor->params.set_4byte = st_micron_set_4byte;
+}
+
+static void winbond_set_default_init(struct spi_nor *nor)
+{
+ nor->params.set_4byte = winbond_set_4byte;
+}
+
+/**
+ * spi_nor_manufacturer_init_params() - Initialize the flash's parameters and
+ * settings based on MFR register and ->default_init() hook.
+ * @nor: pointer to a 'struct spi-nor'.
+ */
+static void spi_nor_manufacturer_init_params(struct spi_nor *nor)
+{
+ /* Init flash parameters based on MFR */
+ switch (JEDEC_MFR(nor->info)) {
+ case SNOR_MFR_MACRONIX:
+ macronix_set_default_init(nor);
+ break;
+
+ case SNOR_MFR_ST:
+ case SNOR_MFR_MICRON:
+ st_micron_set_default_init(nor);
+ break;
+
+ case SNOR_MFR_WINBOND:
+ winbond_set_default_init(nor);
+ break;
+
+ default:
+ break;
+ }
+
+ if (nor->info->fixups && nor->info->fixups->default_init)
+ nor->info->fixups->default_init(nor);
+}
+
+/**
+ * spi_nor_sfdp_init_params() - Initialize the flash's parameters and settings
+ * based on JESD216 SFDP standard.
+ * @nor: pointer to a 'struct spi-nor'.
+ *
+ * The method has a roll-back mechanism: in case the SFDP parsing fails, the
+ * legacy flash parameters and settings will be restored.
+ */
+static void spi_nor_sfdp_init_params(struct spi_nor *nor)
+{
+ struct spi_nor_flash_parameter sfdp_params;
+
+ memcpy(&sfdp_params, &nor->params, sizeof(sfdp_params));
+
+ if (spi_nor_parse_sfdp(nor, &sfdp_params)) {
+ nor->addr_width = 0;
+ nor->flags &= ~SNOR_F_4B_OPCODES;
+ } else {
+ memcpy(&nor->params, &sfdp_params, sizeof(nor->params));
+ }
+}
+
+/**
+ * spi_nor_info_init_params() - Initialize the flash's parameters and settings
+ * based on nor->info data.
+ * @nor: pointer to a 'struct spi-nor'.
+ */
+static void spi_nor_info_init_params(struct spi_nor *nor)
+{
+ struct spi_nor_flash_parameter *params = &nor->params;
+ struct spi_nor_erase_map *map = &params->erase_map;
+ const struct flash_info *info = nor->info;
+ struct device_node *np = spi_nor_get_flash_node(nor);
+ u8 i, erase_mask;
+
+ /* Initialize legacy flash parameters and settings. */
+ params->quad_enable = spansion_quad_enable;
+ params->set_4byte = spansion_set_4byte;
+ params->setup = spi_nor_default_setup;
+
+ /* Set SPI NOR sizes. */
+ params->size = (u64)info->sector_size * info->n_sectors;
+ params->page_size = info->page_size;
+
+ if (!(info->flags & SPI_NOR_NO_FR)) {
+ /* Default to Fast Read for DT and non-DT platform devices. */
+ params->hwcaps.mask |= SNOR_HWCAPS_READ_FAST;
+
+ /* Mask out Fast Read if not requested at DT instantiation. */
+ if (np && !of_property_read_bool(np, "m25p,fast-read"))
+ params->hwcaps.mask &= ~SNOR_HWCAPS_READ_FAST;
+ }
+
+ /* (Fast) Read settings. */
+ params->hwcaps.mask |= SNOR_HWCAPS_READ;
+ spi_nor_set_read_settings(&params->reads[SNOR_CMD_READ],
+ 0, 0, SPINOR_OP_READ,
+ SNOR_PROTO_1_1_1);
+
+ if (params->hwcaps.mask & SNOR_HWCAPS_READ_FAST)
+ spi_nor_set_read_settings(&params->reads[SNOR_CMD_READ_FAST],
+ 0, 8, SPINOR_OP_READ_FAST,
+ SNOR_PROTO_1_1_1);
+
+ if (info->flags & SPI_NOR_DUAL_READ) {
+ params->hwcaps.mask |= SNOR_HWCAPS_READ_1_1_2;
+ spi_nor_set_read_settings(&params->reads[SNOR_CMD_READ_1_1_2],
+ 0, 8, SPINOR_OP_READ_1_1_2,
+ SNOR_PROTO_1_1_2);
+ }
+
+ if (info->flags & SPI_NOR_QUAD_READ) {
+ params->hwcaps.mask |= SNOR_HWCAPS_READ_1_1_4;
+ spi_nor_set_read_settings(&params->reads[SNOR_CMD_READ_1_1_4],
+ 0, 8, SPINOR_OP_READ_1_1_4,
+ SNOR_PROTO_1_1_4);
+ }
+
+ if (info->flags & SPI_NOR_OCTAL_READ) {
+ params->hwcaps.mask |= SNOR_HWCAPS_READ_1_1_8;
+ spi_nor_set_read_settings(&params->reads[SNOR_CMD_READ_1_1_8],
+ 0, 8, SPINOR_OP_READ_1_1_8,
+ SNOR_PROTO_1_1_8);
+ }
+
+ /* Page Program settings. */
+ params->hwcaps.mask |= SNOR_HWCAPS_PP;
+ spi_nor_set_pp_settings(&params->page_programs[SNOR_CMD_PP],
+ SPINOR_OP_PP, SNOR_PROTO_1_1_1);
+
+ /*
+ * Sector Erase settings. Sort Erase Types in ascending order, with the
+ * smallest erase size starting at BIT(0).
+ */
+ erase_mask = 0;
+ i = 0;
+ if (info->flags & SECT_4K_PMC) {
+ erase_mask |= BIT(i);
+ spi_nor_set_erase_type(&map->erase_type[i], 4096u,
+ SPINOR_OP_BE_4K_PMC);
+ i++;
+ } else if (info->flags & SECT_4K) {
+ erase_mask |= BIT(i);
+ spi_nor_set_erase_type(&map->erase_type[i], 4096u,
+ SPINOR_OP_BE_4K);
+ i++;
+ }
+ erase_mask |= BIT(i);
+ spi_nor_set_erase_type(&map->erase_type[i], info->sector_size,
+ SPINOR_OP_SE);
+ spi_nor_init_uniform_erase_map(map, erase_mask, params->size);
+}
+
+static void spansion_post_sfdp_fixups(struct spi_nor *nor)
+{
+ struct mtd_info *mtd = &nor->mtd;
+
+ if (mtd->size <= SZ_16M)
+ return;
+
+ nor->flags |= SNOR_F_4B_OPCODES;
+ /* No small sector erase for 4-byte command set */
+ nor->erase_opcode = SPINOR_OP_SE;
+ nor->mtd.erasesize = nor->info->sector_size;
+}
+
+static void s3an_post_sfdp_fixups(struct spi_nor *nor)
+{
+ nor->params.setup = s3an_nor_setup;
+}
+
+/**
+ * spi_nor_post_sfdp_fixups() - Updates the flash's parameters and settings
+ * after SFDP has been parsed (is also called for SPI NORs that do not
+ * support RDSFDP).
+ * @nor: pointer to a 'struct spi_nor'
+ *
+ * Typically used to tweak various parameters that could not be extracted by
+ * other means (i.e. when information provided by the SFDP/flash_info tables
+ * are incomplete or wrong).
+ */
+static void spi_nor_post_sfdp_fixups(struct spi_nor *nor)
+{
+ switch (JEDEC_MFR(nor->info)) {
+ case SNOR_MFR_SPANSION:
+ spansion_post_sfdp_fixups(nor);
+ break;
+
+ default:
+ break;
+ }
+
+ if (nor->info->flags & SPI_S3AN)
+ s3an_post_sfdp_fixups(nor);
+
+ if (nor->info->fixups && nor->info->fixups->post_sfdp)
+ nor->info->fixups->post_sfdp(nor);
+}
+
+/**
+ * spi_nor_late_init_params() - Late initialization of default flash parameters.
+ * @nor: pointer to a 'struct spi_nor'
+ *
+ * Used to set default flash parameters and settings when the ->default_init()
+ * hook or the SFDP parser let voids.
+ */
+static void spi_nor_late_init_params(struct spi_nor *nor)
+{
+ /*
+ * NOR protection support. When locking_ops are not provided, we pick
+ * the default ones.
+ */
+ if (nor->flags & SNOR_F_HAS_LOCK && !nor->params.locking_ops)
+ nor->params.locking_ops = &stm_locking_ops;
+}
+
+/**
+ * spi_nor_init_params() - Initialize the flash's parameters and settings.
+ * @nor: pointer to a 'struct spi-nor'.
+ *
+ * The flash parameters and settings are initialized based on a sequence of
+ * calls that are ordered by priority:
+ *
+ * 1/ Default flash parameters initialization. The initializations are done
+ * based on nor->info data:
+ * spi_nor_info_init_params()
+ *
+ * which can be overwritten by:
+ * 2/ Manufacturer flash parameters initialization. The initializations are
+ * done based on MFR register, or when the decisions can not be done solely
+ * based on MFR, by using specific flash_info tweeks, ->default_init():
+ * spi_nor_manufacturer_init_params()
+ *
+ * which can be overwritten by:
+ * 3/ SFDP flash parameters initialization. JESD216 SFDP is a standard and
+ * should be more accurate that the above.
+ * spi_nor_sfdp_init_params()
+ *
+ * Please note that there is a ->post_bfpt() fixup hook that can overwrite
+ * the flash parameters and settings immediately after parsing the Basic
+ * Flash Parameter Table.
+ *
+ * which can be overwritten by:
+ * 4/ Post SFDP flash parameters initialization. Used to tweak various
+ * parameters that could not be extracted by other means (i.e. when
+ * information provided by the SFDP/flash_info tables are incomplete or
+ * wrong).
+ * spi_nor_post_sfdp_fixups()
+ *
+ * 5/ Late default flash parameters initialization, used when the
+ * ->default_init() hook or the SFDP parser do not set specific params.
+ * spi_nor_late_init_params()
+ */
+static void spi_nor_init_params(struct spi_nor *nor)
+{
+ spi_nor_info_init_params(nor);
+
+ spi_nor_manufacturer_init_params(nor);
+
+ if ((nor->info->flags & (SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ)) &&
+ !(nor->info->flags & SPI_NOR_SKIP_SFDP))
+ spi_nor_sfdp_init_params(nor);
+
+ spi_nor_post_sfdp_fixups(nor);
+
+ spi_nor_late_init_params(nor);
+}
+
+/**
+ * spi_nor_quad_enable() - enable Quad I/O if needed.
+ * @nor: pointer to a 'struct spi_nor'
+ *
+ * Return: 0 on success, -errno otherwise.
+ */
+static int spi_nor_quad_enable(struct spi_nor *nor)
+{
+ if (!nor->params.quad_enable)
+ return 0;
+
+ if (!(spi_nor_get_protocol_width(nor->read_proto) == 4 ||
+ spi_nor_get_protocol_width(nor->write_proto) == 4))
+ return 0;
+
+ return nor->params.quad_enable(nor);
+}
+
+static int spi_nor_init(struct spi_nor *nor)
+{
+ int err;
+
+ if (nor->clear_sr_bp) {
+ if (nor->params.quad_enable == spansion_quad_enable)
+ nor->clear_sr_bp = spi_nor_spansion_clear_sr_bp;
+
+ err = nor->clear_sr_bp(nor);
+ if (err) {
+ dev_err(nor->dev,
+ "fail to clear block protection bits\n");
+ return err;
+ }
+ }
+
+ err = spi_nor_quad_enable(nor);
+ if (err) {
+ dev_err(nor->dev, "quad mode not supported\n");
+ return err;
+ }
+
+ if (nor->addr_width == 4 && !(nor->flags & SNOR_F_4B_OPCODES)) {
+ /*
+ * If the RESET# pin isn't hooked up properly, or the system
+ * otherwise doesn't perform a reset command in the boot
+ * sequence, it's impossible to 100% protect against unexpected
+ * reboots (e.g., crashes). Warn the user (or hopefully, system
+ * designer) that this is bad.
+ */
+ WARN_ONCE(nor->flags & SNOR_F_BROKEN_RESET,
+ "enabling reset hack; may not recover from unexpected reboots\n");
+ nor->params.set_4byte(nor, true);
+ }
+
+ return 0;
+}
+
+/* mtd resume handler */
+static void spi_nor_resume(struct mtd_info *mtd)
+{
+ struct spi_nor *nor = mtd_to_spi_nor(mtd);
+ struct device *dev = nor->dev;
+ int ret;
+
+ /* re-initialize the nor chip */
+ ret = spi_nor_init(nor);
+ if (ret)
+ dev_err(dev, "resume() failed\n");
+}
+
+void spi_nor_restore(struct spi_nor *nor)
+{
+ /* restore the addressing mode */
+ if (nor->addr_width == 4 && !(nor->flags & SNOR_F_4B_OPCODES) &&
+ nor->flags & SNOR_F_BROKEN_RESET)
+ nor->params.set_4byte(nor, false);
+}
+EXPORT_SYMBOL_GPL(spi_nor_restore);
+
+static const struct flash_info *spi_nor_match_id(const char *name)
+{
+ const struct flash_info *id = spi_nor_ids;
+
+ while (id->name) {
+ if (!strcmp(name, id->name))
+ return id;
+ id++;
+ }
+ return NULL;
+}
+
+static int spi_nor_set_addr_width(struct spi_nor *nor)
+{
+ if (nor->addr_width) {
+ /* already configured from SFDP */
+ } else if (nor->info->addr_width) {
+ nor->addr_width = nor->info->addr_width;
+ } else if (nor->mtd.size > 0x1000000) {
+ /* enable 4-byte addressing if the device exceeds 16MiB */
+ nor->addr_width = 4;
+ } else {
+ nor->addr_width = 3;
+ }
+
+ if (nor->addr_width > SPI_NOR_MAX_ADDR_WIDTH) {
+ dev_err(nor->dev, "address width is too large: %u\n",
+ nor->addr_width);
+ return -EINVAL;
+ }
+
+ /* Set 4byte opcodes when possible. */
+ if (nor->addr_width == 4 && nor->flags & SNOR_F_4B_OPCODES &&
+ !(nor->flags & SNOR_F_HAS_4BAIT))
+ spi_nor_set_4byte_opcodes(nor);
+
+ return 0;
+}
+
+static void spi_nor_debugfs_init(struct spi_nor *nor,
+ const struct flash_info *info)
+{
+ struct mtd_info *mtd = &nor->mtd;
+
+ mtd->dbg.partname = info->name;
+ mtd->dbg.partid = devm_kasprintf(nor->dev, GFP_KERNEL, "spi-nor:%*phN",
+ info->id_len, info->id);
+}
+
+static const struct flash_info *spi_nor_get_flash_info(struct spi_nor *nor,
+ const char *name)
+{
+ const struct flash_info *info = NULL;
+
+ if (name)
+ info = spi_nor_match_id(name);
+ /* Try to auto-detect if chip name wasn't specified or not found */
+ if (!info)
+ info = spi_nor_read_id(nor);
+ if (IS_ERR_OR_NULL(info))
+ return ERR_PTR(-ENOENT);
+
+ /*
+ * If caller has specified name of flash model that can normally be
+ * detected using JEDEC, let's verify it.
+ */
+ if (name && info->id_len) {
+ const struct flash_info *jinfo;
+
+ jinfo = spi_nor_read_id(nor);
+ if (IS_ERR(jinfo)) {
+ return jinfo;
+ } else if (jinfo != info) {
+ /*
+ * JEDEC knows better, so overwrite platform ID. We
+ * can't trust partitions any longer, but we'll let
+ * mtd apply them anyway, since some partitions may be
+ * marked read-only, and we don't want to lose that
+ * information, even if it's not 100% accurate.
+ */
+ dev_warn(nor->dev, "found %s, expected %s\n",
+ jinfo->name, info->name);
+ info = jinfo;
+ }
+ }
+
+ return info;
+}
+
+int spi_nor_scan(struct spi_nor *nor, const char *name,
+ const struct spi_nor_hwcaps *hwcaps)
+{
+ const struct flash_info *info;
+ struct device *dev = nor->dev;
+ struct mtd_info *mtd = &nor->mtd;
+ struct device_node *np = spi_nor_get_flash_node(nor);
+ struct spi_nor_flash_parameter *params = &nor->params;
+ int ret;
+ int i;
+
+ ret = spi_nor_check(nor);
+ if (ret)
+ return ret;
+
+ /* Reset SPI protocol for all commands. */
+ nor->reg_proto = SNOR_PROTO_1_1_1;
+ nor->read_proto = SNOR_PROTO_1_1_1;
+ nor->write_proto = SNOR_PROTO_1_1_1;
+
+ /*
+ * We need the bounce buffer early to read/write registers when going
+ * through the spi-mem layer (buffers have to be DMA-able).
+ * For spi-mem drivers, we'll reallocate a new buffer if
+ * nor->page_size turns out to be greater than PAGE_SIZE (which
+ * shouldn't happen before long since NOR pages are usually less
+ * than 1KB) after spi_nor_scan() returns.
+ */
+ nor->bouncebuf_size = PAGE_SIZE;
+ nor->bouncebuf = devm_kmalloc(dev, nor->bouncebuf_size,
+ GFP_KERNEL);
+ if (!nor->bouncebuf)
+ return -ENOMEM;
+
+ info = spi_nor_get_flash_info(nor, name);
+ if (IS_ERR(info))
+ return PTR_ERR(info);
+
+ nor->info = info;
+
+ spi_nor_debugfs_init(nor, info);
+
+ mutex_init(&nor->lock);
+
+ /*
+ * Make sure the XSR_RDY flag is set before calling
+ * spi_nor_wait_till_ready(). Xilinx S3AN share MFR
+ * with Atmel spi-nor
+ */
+ if (info->flags & SPI_NOR_XSR_RDY)
+ nor->flags |= SNOR_F_READY_XSR_RDY;
+
+ if (info->flags & SPI_NOR_HAS_LOCK)
+ nor->flags |= SNOR_F_HAS_LOCK;
+
+ /*
+ * Atmel, SST, Intel/Numonyx, and others serial NOR tend to power up
+ * with the software protection bits set.
+ */
+ if (JEDEC_MFR(nor->info) == SNOR_MFR_ATMEL ||
+ JEDEC_MFR(nor->info) == SNOR_MFR_INTEL ||
+ JEDEC_MFR(nor->info) == SNOR_MFR_SST ||
+ nor->info->flags & SPI_NOR_HAS_LOCK)
+ nor->clear_sr_bp = spi_nor_clear_sr_bp;
+
+ /* Init flash parameters based on flash_info struct and SFDP */
+ spi_nor_init_params(nor);
+
+ if (!mtd->name)
+ mtd->name = dev_name(dev);
+ mtd->priv = nor;
+ mtd->type = MTD_NORFLASH;
+ mtd->writesize = 1;
+ mtd->flags = MTD_CAP_NORFLASH;
+ mtd->size = params->size;
+ mtd->_erase = spi_nor_erase;
+ mtd->_read = spi_nor_read;
+ mtd->_resume = spi_nor_resume;
+
+ if (nor->params.locking_ops) {
+ mtd->_lock = spi_nor_lock;
+ mtd->_unlock = spi_nor_unlock;
+ mtd->_is_locked = spi_nor_is_locked;
+ }
+
+ /* sst nor chips use AAI word program */
+ if (info->flags & SST_WRITE)
+ mtd->_write = sst_write;
+ else
+ mtd->_write = spi_nor_write;
+
+ if (info->flags & USE_FSR)
+ nor->flags |= SNOR_F_USE_FSR;
+ if (info->flags & SPI_NOR_HAS_TB)
+ nor->flags |= SNOR_F_HAS_SR_TB;
+ if (info->flags & NO_CHIP_ERASE)
+ nor->flags |= SNOR_F_NO_OP_CHIP_ERASE;
+ if (info->flags & USE_CLSR)
+ nor->flags |= SNOR_F_USE_CLSR;
+
+ if (info->flags & SPI_NOR_NO_ERASE)
+ mtd->flags |= MTD_NO_ERASE;
+
+ mtd->dev.parent = dev;
+ nor->page_size = params->page_size;
+ mtd->writebufsize = nor->page_size;
+
+ if (of_property_read_bool(np, "broken-flash-reset"))
+ nor->flags |= SNOR_F_BROKEN_RESET;
+
+ /*
+ * Configure the SPI memory:
+ * - select op codes for (Fast) Read, Page Program and Sector Erase.
+ * - set the number of dummy cycles (mode cycles + wait states).
+ * - set the SPI protocols for register and memory accesses.
+ */
+ ret = spi_nor_setup(nor, hwcaps);
+ if (ret)
+ return ret;
+
+ if (info->flags & SPI_NOR_4B_OPCODES)
+ nor->flags |= SNOR_F_4B_OPCODES;
+
+ ret = spi_nor_set_addr_width(nor);
+ if (ret)
+ return ret;
+
+ /* Send all the required SPI flash commands to initialize device */
+ ret = spi_nor_init(nor);
+ if (ret)
+ return ret;
+
+ dev_info(dev, "%s (%lld Kbytes)\n", info->name,
+ (long long)mtd->size >> 10);
+
+ dev_dbg(dev,
+ "mtd .name = %s, .size = 0x%llx (%lldMiB), "
+ ".erasesize = 0x%.8x (%uKiB) .numeraseregions = %d\n",
+ mtd->name, (long long)mtd->size, (long long)(mtd->size >> 20),
+ mtd->erasesize, mtd->erasesize / 1024, mtd->numeraseregions);
+
+ if (mtd->numeraseregions)
+ for (i = 0; i < mtd->numeraseregions; i++)
+ dev_dbg(dev,
+ "mtd.eraseregions[%d] = { .offset = 0x%llx, "
+ ".erasesize = 0x%.8x (%uKiB), "
+ ".numblocks = %d }\n",
+ i, (long long)mtd->eraseregions[i].offset,
+ mtd->eraseregions[i].erasesize,
+ mtd->eraseregions[i].erasesize / 1024,
+ mtd->eraseregions[i].numblocks);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(spi_nor_scan);
+
+static int spi_nor_probe(struct spi_mem *spimem)
+{
+ struct spi_device *spi = spimem->spi;
+ struct flash_platform_data *data = dev_get_platdata(&spi->dev);
+ struct spi_nor *nor;
+ /*
+ * Enable all caps by default. The core will mask them after
+ * checking what's really supported using spi_mem_supports_op().
+ */
+ const struct spi_nor_hwcaps hwcaps = { .mask = SNOR_HWCAPS_ALL };
+ char *flash_name;
+ int ret;
+
+ nor = devm_kzalloc(&spi->dev, sizeof(*nor), GFP_KERNEL);
+ if (!nor)
+ return -ENOMEM;
+
+ nor->spimem = spimem;
+ nor->dev = &spi->dev;
+ spi_nor_set_flash_node(nor, spi->dev.of_node);
+
+ spi_mem_set_drvdata(spimem, nor);
+
+ if (data && data->name)
+ nor->mtd.name = data->name;
+
+ if (!nor->mtd.name)
+ nor->mtd.name = spi_mem_get_name(spimem);
+
+ /*
+ * For some (historical?) reason many platforms provide two different
+ * names in flash_platform_data: "name" and "type". Quite often name is
+ * set to "m25p80" and then "type" provides a real chip name.
+ * If that's the case, respect "type" and ignore a "name".
+ */
+ if (data && data->type)
+ flash_name = data->type;
+ else if (!strcmp(spi->modalias, "spi-nor"))
+ flash_name = NULL; /* auto-detect */
+ else
+ flash_name = spi->modalias;
+
+ ret = spi_nor_scan(nor, flash_name, &hwcaps);
+ if (ret)
+ return ret;
+
+ /*
+ * None of the existing parts have > 512B pages, but let's play safe
+ * and add this logic so that if anyone ever adds support for such
+ * a NOR we don't end up with buffer overflows.
+ */
+ if (nor->page_size > PAGE_SIZE) {
+ nor->bouncebuf_size = nor->page_size;
+ devm_kfree(nor->dev, nor->bouncebuf);
+ nor->bouncebuf = devm_kmalloc(nor->dev,
+ nor->bouncebuf_size,
+ GFP_KERNEL);
+ if (!nor->bouncebuf)
+ return -ENOMEM;
+ }
+
+ return mtd_device_register(&nor->mtd, data ? data->parts : NULL,
+ data ? data->nr_parts : 0);
+}
+
+static int spi_nor_remove(struct spi_mem *spimem)
+{
+ struct spi_nor *nor = spi_mem_get_drvdata(spimem);
+
+ spi_nor_restore(nor);
+
+ /* Clean up MTD stuff. */
+ return mtd_device_unregister(&nor->mtd);
+}
+
+static void spi_nor_shutdown(struct spi_mem *spimem)
+{
+ struct spi_nor *nor = spi_mem_get_drvdata(spimem);
+
+ spi_nor_restore(nor);
+}
+
+/*
+ * Do NOT add to this array without reading the following:
+ *
+ * Historically, many flash devices are bound to this driver by their name. But
+ * since most of these flash are compatible to some extent, and their
+ * differences can often be differentiated by the JEDEC read-ID command, we
+ * encourage new users to add support to the spi-nor library, and simply bind
+ * against a generic string here (e.g., "jedec,spi-nor").
+ *
+ * Many flash names are kept here in this list (as well as in spi-nor.c) to
+ * keep them available as module aliases for existing platforms.
+ */
+static const struct spi_device_id spi_nor_dev_ids[] = {
+ /*
+ * Allow non-DT platform devices to bind to the "spi-nor" modalias, and
+ * hack around the fact that the SPI core does not provide uevent
+ * matching for .of_match_table
+ */
+ {"spi-nor"},
+
+ /*
+ * Entries not used in DTs that should be safe to drop after replacing
+ * them with "spi-nor" in platform data.
+ */
+ {"s25sl064a"}, {"w25x16"}, {"m25p10"}, {"m25px64"},
+
+ /*
+ * Entries that were used in DTs without "jedec,spi-nor" fallback and
+ * should be kept for backward compatibility.
+ */
+ {"at25df321a"}, {"at25df641"}, {"at26df081a"},
+ {"mx25l4005a"}, {"mx25l1606e"}, {"mx25l6405d"}, {"mx25l12805d"},
+ {"mx25l25635e"},{"mx66l51235l"},
+ {"n25q064"}, {"n25q128a11"}, {"n25q128a13"}, {"n25q512a"},
+ {"s25fl256s1"}, {"s25fl512s"}, {"s25sl12801"}, {"s25fl008k"},
+ {"s25fl064k"},
+ {"sst25vf040b"},{"sst25vf016b"},{"sst25vf032b"},{"sst25wf040"},
+ {"m25p40"}, {"m25p80"}, {"m25p16"}, {"m25p32"},
+ {"m25p64"}, {"m25p128"},
+ {"w25x80"}, {"w25x32"}, {"w25q32"}, {"w25q32dw"},
+ {"w25q80bl"}, {"w25q128"}, {"w25q256"},
+
+ /* Flashes that can't be detected using JEDEC */
+ {"m25p05-nonjedec"}, {"m25p10-nonjedec"}, {"m25p20-nonjedec"},
+ {"m25p40-nonjedec"}, {"m25p80-nonjedec"}, {"m25p16-nonjedec"},
+ {"m25p32-nonjedec"}, {"m25p64-nonjedec"}, {"m25p128-nonjedec"},
+
+ /* Everspin MRAMs (non-JEDEC) */
+ { "mr25h128" }, /* 128 Kib, 40 MHz */
+ { "mr25h256" }, /* 256 Kib, 40 MHz */
+ { "mr25h10" }, /* 1 Mib, 40 MHz */
+ { "mr25h40" }, /* 4 Mib, 40 MHz */
+
+ { },
+};
+MODULE_DEVICE_TABLE(spi, spi_nor_dev_ids);
+
+static const struct of_device_id spi_nor_of_table[] = {
+ /*
+ * Generic compatibility for SPI NOR that can be identified by the
+ * JEDEC READ ID opcode (0x9F). Use this, if possible.
+ */
+ { .compatible = "jedec,spi-nor" },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, spi_nor_of_table);
+
+/*
+ * REVISIT: many of these chips have deep power-down modes, which
+ * should clearly be entered on suspend() to minimize power use.
+ * And also when they're otherwise idle...
+ */
+static struct spi_mem_driver spi_nor_driver = {
+ .spidrv = {
+ .driver = {
+ .name = "spi-nor",
+ .of_match_table = spi_nor_of_table,
+ },
+ .id_table = spi_nor_dev_ids,
+ },
+ .probe = spi_nor_probe,
+ .remove = spi_nor_remove,
+ .shutdown = spi_nor_shutdown,
+};
+module_spi_mem_driver(spi_nor_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Huang Shijie <shijie8@gmail.com>");
+MODULE_AUTHOR("Mike Lavender");
+MODULE_DESCRIPTION("framework for SPI NOR");
diff --git a/drivers/mtd/spi-nor/winbond.c b/drivers/mtd/spi-nor/winbond.c
index 5062af10f138..425a478576cc 100644
--- a/drivers/mtd/spi-nor/winbond.c
+++ b/drivers/mtd/spi-nor/winbond.c
@@ -84,6 +84,8 @@ static const struct flash_info winbond_parts[] = {
SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
{ "w25q256jw", INFO(0xef6019, 0, 64 * 1024, 512,
SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
+ { "w25q512jv", INFO(0xef4020, 0, 64 * 1024, 1024, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |
+ SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) },
{ "w25m512jv", INFO(0xef7119, 0, 64 * 1024, 1024,
SECT_4K | SPI_NOR_QUAD_READ | SPI_NOR_DUAL_READ) },
};
diff --git a/drivers/net/ethernet/Kconfig b/drivers/net/ethernet/Kconfig
index de50e8b9e656..4624eb34c193 100644
--- a/drivers/net/ethernet/Kconfig
+++ b/drivers/net/ethernet/Kconfig
@@ -139,6 +139,7 @@ source "drivers/net/ethernet/neterion/Kconfig"
source "drivers/net/ethernet/netronome/Kconfig"
source "drivers/net/ethernet/ni/Kconfig"
source "drivers/net/ethernet/8390/Kconfig"
+source "drivers/net/ethernet/nuvoton/Kconfig"
source "drivers/net/ethernet/nvidia/Kconfig"
source "drivers/net/ethernet/nxp/Kconfig"
source "drivers/net/ethernet/oki-semi/Kconfig"
diff --git a/drivers/net/ethernet/Makefile b/drivers/net/ethernet/Makefile
index f8f38dcb5f8a..c4c3b715c101 100644
--- a/drivers/net/ethernet/Makefile
+++ b/drivers/net/ethernet/Makefile
@@ -63,6 +63,7 @@ obj-$(CONFIG_NET_VENDOR_NATSEMI) += natsemi/
obj-$(CONFIG_NET_VENDOR_NETERION) += neterion/
obj-$(CONFIG_NET_VENDOR_NETRONOME) += netronome/
obj-$(CONFIG_NET_VENDOR_NI) += ni/
+obj-$(CONFIG_NET_VENDOR_NUVOTON) += nuvoton/
obj-$(CONFIG_NET_VENDOR_NVIDIA) += nvidia/
obj-$(CONFIG_LPC_ENET) += nxp/
obj-$(CONFIG_NET_VENDOR_OKI) += oki-semi/
diff --git a/drivers/net/ethernet/nuvoton/Kconfig b/drivers/net/ethernet/nuvoton/Kconfig
new file mode 100644
index 000000000000..e79af5f0ba3c
--- /dev/null
+++ b/drivers/net/ethernet/nuvoton/Kconfig
@@ -0,0 +1,30 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Nuvoton network device configuration
+#
+
+config NET_VENDOR_NUVOTON
+ bool "Nuvoton devices"
+ default y
+ depends on ARM && (ARCH_W90X900 || ARCH_NPCM7XX)
+ help
+ If you have a network (Ethernet) card belonging to this class, say Y.
+
+if NET_VENDOR_NUVOTON
+
+config NPCM7XX_EMC_ETH
+ bool "Nuvoton NPCM7XX Ethernet EMC"
+ depends on ARM && ARCH_NPCM7XX
+ select PHYLIB
+ select MII
+ help
+ Say Y here if you want to use built-in Ethernet MAC
+ on NPCM750 MCU.
+
+config NPCM7XX_EMC_ETH_DEBUG
+ bool "Nuvoton NPCM7XX Ethernet EMC debug"
+ depends on NPCM7XX_EMC_ETH
+ help
+ Say Y here if you want debug info via /proc/driver/npcm7xx_emc.x
+
+endif # NET_VENDOR_NUVOTON
diff --git a/drivers/net/ethernet/nuvoton/Makefile b/drivers/net/ethernet/nuvoton/Makefile
new file mode 100644
index 000000000000..3811daa84be8
--- /dev/null
+++ b/drivers/net/ethernet/nuvoton/Makefile
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Makefile for the Nuvoton network device drivers.
+#
+
+obj-$(CONFIG_NPCM7XX_EMC_ETH) += npcm7xx_emc.o
diff --git a/drivers/net/ethernet/nuvoton/npcm7xx_emc.c b/drivers/net/ethernet/nuvoton/npcm7xx_emc.c
new file mode 100644
index 000000000000..9872da33fa5d
--- /dev/null
+++ b/drivers/net/ethernet/nuvoton/npcm7xx_emc.c
@@ -0,0 +1,2090 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2014-2019 Nuvoton Technology corporation.
+
+#ifdef CONFIG_NPCM7XX_EMC_ETH_DEBUG
+#define DEBUG
+#endif
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/mii.h>
+#include <linux/phy.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/ethtool.h>
+#include <linux/platform_device.h>
+#include <linux/gfp.h>
+#include <linux/kthread.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/ctype.h>
+#include <linux/debugfs.h>
+
+#include <linux/clk.h>
+
+#include <linux/of.h>
+#include <linux/of_net.h>
+#include <linux/of_device.h>
+#include <linux/dma-mapping.h>
+
+#include <linux/regmap.h>
+#include <linux/mfd/syscon.h>
+
+#include <linux/if_ether.h>
+
+#include <net/ip.h>
+#include <net/ncsi.h>
+
+#ifdef CONFIG_DEBUG_FS
+static struct dentry *npcm7xx_fs_dir;
+#endif
+
+#define MFSEL1_OFFSET 0x00C
+#define MFSEL3_OFFSET 0x064
+#define INTCR_OFFSET 0x03C
+
+#define IPSRST1_OFFSET 0x020
+
+#define DRV_MODULE_NAME "npcm7xx-emc"
+#define DRV_MODULE_VERSION "3.90"
+
+/* Ethernet MAC Registers */
+#define REG_CAMCMR 0x00
+#define REG_CAMEN 0x04
+#define REG_CAMM_BASE 0x08
+#define REG_CAML_BASE 0x0c
+#define REG_TXDLSA 0x88
+#define REG_RXDLSA 0x8C
+#define REG_MCMDR 0x90
+#define REG_MIID 0x94
+#define REG_MIIDA 0x98
+#define REG_FFTCR 0x9C
+#define REG_TSDR 0xa0
+#define REG_RSDR 0xa4
+#define REG_DMARFC 0xa8
+#define REG_MIEN 0xac
+#define REG_MISTA 0xb0
+#define REG_MGSTA 0xb4
+#define REG_MPCNT 0xb8
+#define REG_MRPC 0xbc
+#define REG_MRPCC 0xc0
+#define REG_MREPC 0xc4
+#define REG_DMARFS 0xc8
+#define REG_CTXDSA 0xcc
+#define REG_CTXBSA 0xd0
+#define REG_CRXDSA 0xd4
+#define REG_CRXBSA 0xd8
+
+/* EMC Diagnostic Registers */
+#define REG_RXFSM 0x200
+#define REG_TXFSM 0x204
+#define REG_FSM0 0x208
+#define REG_FSM1 0x20c
+#define REG_DCR 0x210
+#define REG_DMMIR 0x214
+#define REG_BISTR 0x300
+
+/* mac controller bit */
+#define MCMDR_RXON BIT(0)
+#define MCMDR_ALP BIT(1)
+#define MCMDR_ACP BIT(3)
+#define MCMDR_SPCRC BIT(5)
+#define MCMDR_TXON BIT(8)
+#define MCMDR_NDEF BIT(9)
+#define MCMDR_FDUP BIT(18)
+#define MCMDR_ENMDC BIT(19)
+#define MCMDR_OPMOD BIT(20)
+#define SWR BIT(24)
+
+/* cam command regiser */
+#define CAMCMR_AUP BIT(0)
+#define CAMCMR_AMP BIT(1)
+#define CAMCMR_ABP BIT(2)
+#define CAMCMR_CCAM BIT(3)
+#define CAMCMR_ECMP BIT(4)
+
+/* cam enable regiser */
+#define CAM0EN BIT(0)
+
+/* mac mii controller bit */
+#define PHYAD BIT(8)
+#define PHYWR BIT(16)
+#define PHYBUSY BIT(17)
+#define PHYPRESP BIT(18)
+#define MDCON BIT(19)
+#define CAM_ENTRY_SIZE 0x08
+
+/* rx and tx status */
+#define TXDS_TXCP BIT(19)
+#define RXDS_CRCE BIT(17)
+#define RXDS_PTLE BIT(19)
+#define RXDS_RXGD BIT(20)
+#define RXDS_ALIE BIT(21)
+#define RXDS_RP BIT(22)
+
+/* mac interrupt status*/
+#define MISTA_RXINTR BIT(0)
+#define MISTA_CRCE BIT(1)
+#define MISTA_RXOV BIT(2)
+#define MISTA_PTLE BIT(3)
+#define MISTA_RXGD BIT(4)
+#define MISTA_ALIE BIT(5)
+#define MISTA_RP BIT(6)
+#define MISTA_MMP BIT(7)
+#define MISTA_DFOI BIT(8)
+#define MISTA_DENI BIT(9)
+#define MISTA_RDU BIT(10)
+#define MISTA_RXBERR BIT(11)
+#define MISTA_CFR BIT(14)
+#define MISTA_TXINTR BIT(16)
+#define MISTA_TXEMP BIT(17)
+#define MISTA_TXCP BIT(18)
+#define MISTA_EXDEF BIT(19)
+#define MISTA_NCS BIT(20)
+#define MISTA_TXABT BIT(21)
+#define MISTA_LC BIT(22)
+#define MISTA_TDU BIT(23)
+#define MISTA_TXBERR BIT(24)
+
+/* Transmit/Receive Start Demand Register */
+#define ENSTART BIT(0)
+
+#define ENRXINTR BIT(0)
+#define ENCRCE BIT(1)
+#define EMRXOV BIT(2)
+#define ENPTLE BIT(3)
+#define ENRXGD BIT(4)
+#define ENALIE BIT(5)
+#define ENRP BIT(6)
+#define ENMMP BIT(7)
+#define ENDFO BIT(8)
+#define ENDENI BIT(9)
+#define ENRDU BIT(10)
+#define ENRXBERR BIT(11)
+#define ENCFR BIT(14)
+#define ENTXINTR BIT(16)
+#define ENTXEMP BIT(17)
+#define ENTXCP BIT(18)
+#define ENTXDEF BIT(19)
+#define ENNCS BIT(20)
+#define ENTXABT BIT(21)
+#define ENLC BIT(22)
+#define ENTDU BIT(23)
+#define ENTXBERR BIT(24)
+
+/* rx and tx owner bit */
+#define RX_OWN_DMA BIT(31)
+#define TX_OWN_DMA BIT(31)
+
+/* tx frame desc controller bit */
+#define MACTXINTEN BIT(2)
+#define CRCMODE BIT(1)
+#define PADDINGMODE BIT(0)
+
+/* fftcr controller bit */
+#define RXTHD (0x03 << 0)
+#define TXTHD (0x02 << 8)
+#define BLENGTH (0x02 << 20)
+
+/* global setting for driver */
+#define RX_QUEUE_LEN 128
+#define TX_QUEUE_LEN 64
+#define MAX_RBUFF_SZ 0x600
+#define MAX_TBUFF_SZ 0x600
+#define TX_TIMEOUT 50
+#define DELAY 1000
+#define CAM0 0x0
+#define RX_POLL_SIZE 16
+
+#ifdef CONFIG_VLAN_8021Q
+#define IS_VLAN 1
+#else
+#define IS_VLAN 0
+#endif
+
+#define MAX_PACKET_SIZE (1514 + (IS_VLAN * 4))
+#define MAX_PACKET_SIZE_W_CRC (MAX_PACKET_SIZE + 4) /* 1518 */
+
+#define MHZ (1000 * 1000)
+#define MII_TIMEOUT 100
+
+struct plat_npcm7xx_emc_data {
+ char *phy_bus_name;
+ int phy_addr;
+ unsigned char mac_addr[ETH_ALEN];
+};
+
+struct npcm7xx_rxbd {
+ __le32 sl;
+ __le32 buffer;
+ __le32 reserved;
+ __le32 next;
+};
+
+struct npcm7xx_txbd {
+ __le32 mode; /* Ownership bit and some other bits */
+ __le32 buffer; /* Transmit Buffer Starting Address */
+ __le32 sl; /* Transmit Byte Count and status bits */
+ __le32 next; /* Next Tx Descriptor Starting Address */
+};
+
+struct npcm7xx_ether {
+ struct sk_buff *rx_skb[RX_QUEUE_LEN];
+ struct sk_buff *tx_skb[TX_QUEUE_LEN];
+ spinlock_t lock; /* lock sk */
+ struct npcm7xx_rxbd *rdesc;
+ struct npcm7xx_txbd *tdesc;
+ dma_addr_t rdesc_phys;
+ dma_addr_t tdesc_phys;
+ struct net_device_stats stats;
+ struct platform_device *pdev;
+ struct net_device *ndev;
+ struct resource *res;
+ unsigned int msg_enable;
+ struct mii_bus *mii_bus;
+ struct phy_device *phy_dev;
+ struct napi_struct napi;
+ struct ncsi_dev *ncsidev;
+ bool use_ncsi;
+ void __iomem *reg;
+ int rxirq;
+ int txirq;
+ unsigned int cur_tx;
+ unsigned int cur_rx;
+ unsigned int finish_tx;
+ unsigned int pending_tx;
+ __le32 start_tx_ptr;
+ __le32 start_rx_ptr;
+ unsigned int rx_berr;
+ unsigned int rx_err;
+ unsigned int rdu;
+ unsigned int rxov;
+ __le32 camcmr;
+ unsigned int rx_stuck;
+ int link;
+ int speed;
+ int duplex;
+ int need_reset;
+ char *dump_buf;
+ struct regmap *rst_regmap;
+
+ /* debug counters */
+ unsigned int max_waiting_rx;
+ unsigned int rx_count_pool;
+ unsigned int count_xmit;
+ unsigned int rx_int_count;
+ unsigned int rx_err_count;
+ unsigned int tx_int_count;
+ unsigned int tx_tdu;
+ unsigned int tx_tdu_i;
+ unsigned int tx_cp_i;
+ unsigned int count_finish;
+
+#ifdef CONFIG_DEBUG_FS
+ struct dentry *dbgfs_dir;
+ struct dentry *dbgfs_status;
+ struct dentry *dbgfs_dma_cap;
+#endif
+};
+
+#if defined CONFIG_NPCM7XX_EMC_ETH_DEBUG || defined CONFIG_DEBUG_FS
+#define REG_PRINT(reg_name) {t = scnprintf(next, size, "%-10s = %08X\n", \
+ #reg_name, readl(ether->reg + reg_name)); size -= t; next += t; }
+#define DUMP_PRINT(f, x...) {t = scnprintf(next, size, f, ## x); size -= t; \
+ next += t; }
+
+static int npcm7xx_info_dump(char *buf, int count, struct net_device *dev)
+{
+ struct npcm7xx_ether *ether = netdev_priv(dev);
+ struct npcm7xx_txbd *txbd;
+ struct npcm7xx_rxbd *rxbd;
+ unsigned long flags;
+ unsigned int i, cur, txd_offset, rxd_offset;
+ char *next = buf;
+ unsigned int size = count;
+ int t;
+ int is_locked = spin_is_locked(&ether->lock);
+
+ if (!is_locked)
+ spin_lock_irqsave(&ether->lock, flags);
+
+ /* ------basic driver information ---- */
+ DUMP_PRINT("NPCM7XX EMC %s driver version: %s\n", dev->name,
+ DRV_MODULE_VERSION);
+
+ REG_PRINT(REG_CAMCMR);
+ REG_PRINT(REG_CAMEN);
+ REG_PRINT(REG_CAMM_BASE);
+ REG_PRINT(REG_CAML_BASE);
+ REG_PRINT(REG_TXDLSA);
+ REG_PRINT(REG_RXDLSA);
+ REG_PRINT(REG_MCMDR);
+ REG_PRINT(REG_MIID);
+ REG_PRINT(REG_MIIDA);
+ REG_PRINT(REG_FFTCR);
+ REG_PRINT(REG_TSDR);
+ REG_PRINT(REG_RSDR);
+ REG_PRINT(REG_DMARFC);
+ REG_PRINT(REG_MIEN);
+ REG_PRINT(REG_MISTA);
+ REG_PRINT(REG_MGSTA);
+ REG_PRINT(REG_MPCNT);
+ writel(0x7FFF, (ether->reg + REG_MPCNT));
+ REG_PRINT(REG_MRPC);
+ REG_PRINT(REG_MRPCC);
+ REG_PRINT(REG_MREPC);
+ REG_PRINT(REG_DMARFS);
+ REG_PRINT(REG_CTXDSA);
+ REG_PRINT(REG_CTXBSA);
+ REG_PRINT(REG_CRXDSA);
+ REG_PRINT(REG_CRXBSA);
+ REG_PRINT(REG_RXFSM);
+ REG_PRINT(REG_TXFSM);
+ REG_PRINT(REG_FSM0);
+ REG_PRINT(REG_FSM1);
+ REG_PRINT(REG_DCR);
+ REG_PRINT(REG_DMMIR);
+ REG_PRINT(REG_BISTR);
+ DUMP_PRINT("\n");
+
+ DUMP_PRINT("netif_queue %s\n\n", netif_queue_stopped(dev) ?
+ "Stopped" : "Running");
+ if (ether->rdesc)
+ DUMP_PRINT("napi is %s\n\n", test_bit(NAPI_STATE_SCHED,
+ &ether->napi.state) ?
+ "scheduled" :
+ "not scheduled");
+
+ txd_offset = (readl((ether->reg + REG_CTXDSA)) -
+ readl((ether->reg + REG_TXDLSA))) /
+ sizeof(struct npcm7xx_txbd);
+ DUMP_PRINT("TXD offset %6d\n", txd_offset);
+ DUMP_PRINT("cur_tx %6d\n", ether->cur_tx);
+ DUMP_PRINT("finish_tx %6d\n", ether->finish_tx);
+ DUMP_PRINT("pending_tx %6d\n", ether->pending_tx);
+ /* debug counters */
+ DUMP_PRINT("tx_tdu %6d\n", ether->tx_tdu);
+ ether->tx_tdu = 0;
+ DUMP_PRINT("tx_tdu_i %6d\n", ether->tx_tdu_i);
+ ether->tx_tdu_i = 0;
+ DUMP_PRINT("tx_cp_i %6d\n", ether->tx_cp_i);
+ ether->tx_cp_i = 0;
+ DUMP_PRINT("tx_int_count %6d\n", ether->tx_int_count);
+ ether->tx_int_count = 0;
+ DUMP_PRINT("count_xmit tx %6d\n", ether->count_xmit);
+ ether->count_xmit = 0;
+ DUMP_PRINT("count_finish %6d\n", ether->count_finish);
+ ether->count_finish = 0;
+ DUMP_PRINT("\n");
+
+ rxd_offset = (readl((ether->reg + REG_CRXDSA)) -
+ readl((ether->reg + REG_RXDLSA)))
+ / sizeof(struct npcm7xx_txbd);
+ DUMP_PRINT("RXD offset %6d\n", rxd_offset);
+ DUMP_PRINT("cur_rx %6d\n", ether->cur_rx);
+ DUMP_PRINT("rx_err %6d\n", ether->rx_err);
+ ether->rx_err = 0;
+ DUMP_PRINT("rx_berr %6d\n", ether->rx_berr);
+ ether->rx_berr = 0;
+ DUMP_PRINT("rx_stuck %6d\n", ether->rx_stuck);
+ ether->rx_stuck = 0;
+ DUMP_PRINT("rdu %6d\n", ether->rdu);
+ ether->rdu = 0;
+ DUMP_PRINT("rxov rx %6d\n", ether->rxov);
+ ether->rxov = 0;
+ /* debug counters */
+ DUMP_PRINT("rx_int_count %6d\n", ether->rx_int_count);
+ ether->rx_int_count = 0;
+ DUMP_PRINT("rx_err_count %6d\n", ether->rx_err_count);
+ ether->rx_err_count = 0;
+ DUMP_PRINT("rx_count_pool %6d\n", ether->rx_count_pool);
+ ether->rx_count_pool = 0;
+ DUMP_PRINT("max_waiting_rx %5d\n", ether->max_waiting_rx);
+ ether->max_waiting_rx = 0;
+ DUMP_PRINT("\n");
+ DUMP_PRINT("need_reset %5d\n", ether->need_reset);
+
+ if (ether->tdesc && ether->rdesc) {
+ cur = ether->finish_tx - 2;
+ for (i = 0; i < 3; i++) {
+ cur = (cur + 1) % TX_QUEUE_LEN;
+ txbd = (ether->tdesc + cur);
+ DUMP_PRINT("finish %3d txbd mode %08X buffer %08X sl %08X next %08X tx_skb %p\n",
+ cur, txbd->mode, txbd->buffer,
+ txbd->sl, txbd->next, ether->tx_skb[cur]);
+ }
+ DUMP_PRINT("\n");
+
+ cur = txd_offset - 2;
+ for (i = 0; i < 3; i++) {
+ cur = (cur + 1) % TX_QUEUE_LEN;
+ txbd = (ether->tdesc + cur);
+ DUMP_PRINT("txd_of %3d txbd mode %08X buffer %08X sl %08X next %08X\n",
+ cur, txbd->mode, txbd->buffer,
+ txbd->sl, txbd->next);
+ }
+ DUMP_PRINT("\n");
+
+ cur = ether->cur_tx - 63;
+ for (i = 0; i < 64; i++) {
+ cur = (cur + 1) % TX_QUEUE_LEN;
+ txbd = (ether->tdesc + cur);
+ DUMP_PRINT("cur_tx %3d txbd mode %08X buffer %08X sl %08X next %08X\n",
+ cur, txbd->mode, txbd->buffer,
+ txbd->sl, txbd->next);
+ }
+ DUMP_PRINT("\n");
+
+ cur = ether->cur_rx - 63;
+ for (i = 0; i < 64; i++) {
+ cur = (cur + 1) % RX_QUEUE_LEN;
+ rxbd = (ether->rdesc + cur);
+ DUMP_PRINT("cur_rx %3d rxbd sl %08X buffer %08X sl %08X next %08X\n",
+ cur, rxbd->sl, rxbd->buffer,
+ rxbd->reserved, rxbd->next);
+ }
+ DUMP_PRINT("\n");
+
+ cur = rxd_offset - 2;
+ for (i = 0; i < 3; i++) {
+ cur = (cur + 1) % RX_QUEUE_LEN;
+ rxbd = (ether->rdesc + cur);
+ DUMP_PRINT("rxd_of %3d rxbd sl %08X buffer %08X sl %08X next %08X\n",
+ cur, rxbd->sl, rxbd->buffer,
+ rxbd->reserved, rxbd->next);
+ }
+ DUMP_PRINT("\n");
+ }
+
+ if (!is_locked)
+ spin_unlock_irqrestore(&ether->lock, flags);
+
+ return count - size;
+}
+#endif
+
+#ifdef CONFIG_NPCM7XX_EMC_ETH_DEBUG
+static void npcm7xx_info_print(struct net_device *dev)
+{
+ char *emc_dump_buf;
+ int count;
+ struct npcm7xx_ether *ether;
+ struct platform_device *pdev;
+ const size_t print_size = 5 * PAGE_SIZE;
+
+ ether = netdev_priv(dev);
+ pdev = ether->pdev;
+
+ emc_dump_buf = kmalloc(print_size, GFP_KERNEL);
+ if (!emc_dump_buf) {
+ dev_err(&pdev->dev, "kmalloc failed\n");
+ } else {
+ char c;
+ char *tmp_buf = emc_dump_buf;
+
+ count = npcm7xx_info_dump(emc_dump_buf, print_size, dev);
+ while (count > 512) {
+ c = tmp_buf[512];
+ tmp_buf[512] = 0;
+ dev_info(&pdev->dev, "%s", tmp_buf);
+ tmp_buf += 512;
+ tmp_buf[0] = c;
+ count -= 512;
+ }
+ dev_info(&pdev->dev, "%s", tmp_buf);
+ kfree(emc_dump_buf);
+ }
+}
+#endif
+
+#ifdef CONFIG_DEBUG_FS
+#include <linux/seq_file.h>
+
+static int npcm7xx_debug_show(struct seq_file *sf, void *v)
+{
+ struct net_device *dev = (struct net_device *)sf->private;
+ struct npcm7xx_ether *ether = netdev_priv(dev);
+ const size_t print_size = 5 * PAGE_SIZE;
+
+ if (!ether->dump_buf) {
+ ether->dump_buf = kmalloc(print_size, GFP_KERNEL);
+ if (!ether->dump_buf)
+ return -1;
+ npcm7xx_info_dump(ether->dump_buf, print_size, dev);
+ }
+
+ seq_printf(sf, "%s", ether->dump_buf);
+ if (sf->count < sf->size) {
+ kfree(ether->dump_buf);
+ ether->dump_buf = NULL;
+ }
+
+ return 0;
+}
+
+static int npcm7xx_debug_show_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, npcm7xx_debug_show, inode->i_private);
+}
+
+static const struct file_operations npcm7xx_debug_show_fops = {
+ .open = npcm7xx_debug_show_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int npcm7xx_debug_reset(struct seq_file *sf, void *v)
+{
+ struct net_device *dev = (struct net_device *)sf->private;
+ struct npcm7xx_ether *ether = netdev_priv(dev);
+ unsigned long flags;
+
+ seq_puts(sf, "Ask to reset the module\n");
+ spin_lock_irqsave(&ether->lock, flags);
+ writel(0, (ether->reg + REG_MIEN));
+ spin_unlock_irqrestore(&ether->lock, flags);
+ ether->need_reset = 1;
+ napi_schedule(&ether->napi);
+
+ return 0;
+}
+
+static int npcm7xx_debug_reset_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, npcm7xx_debug_reset, inode->i_private);
+}
+
+static const struct file_operations npcm7xx_debug_reset_fops = {
+ .owner = THIS_MODULE,
+ .open = npcm7xx_debug_reset_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int npcm7xx_debug_fs(struct npcm7xx_ether *ether)
+{
+ /* Create debugfs main directory if it doesn't exist yet */
+ if (!npcm7xx_fs_dir) {
+ npcm7xx_fs_dir = debugfs_create_dir(DRV_MODULE_NAME, NULL);
+
+ if (!npcm7xx_fs_dir || IS_ERR(npcm7xx_fs_dir)) {
+ dev_err(&ether->pdev->dev, "ERROR %s, debugfs create directory failed\n",
+ DRV_MODULE_NAME);
+ return -ENOMEM;
+ }
+ }
+
+ /* Create per netdev entries */
+ ether->dbgfs_dir = debugfs_create_dir(ether->ndev->name,
+ npcm7xx_fs_dir);
+ if (!ether->dbgfs_dir || IS_ERR(ether->dbgfs_dir)) {
+ dev_err(&ether->pdev->dev, "ERROR failed to create %s directory\n", ether->ndev->name);
+ return -ENOMEM;
+ }
+
+ /* Entry to report DMA RX/TX rings */
+ ether->dbgfs_status =
+ debugfs_create_file("status", 0444,
+ ether->dbgfs_dir, ether->ndev,
+ &npcm7xx_debug_show_fops);
+
+ if (!ether->dbgfs_status || IS_ERR(ether->dbgfs_status)) {
+ dev_err(&ether->pdev->dev, "ERROR creating \'status\' debugfs file\n");
+ debugfs_remove_recursive(ether->dbgfs_dir);
+
+ return -ENOMEM;
+ }
+
+ /* Entry to report the DMA HW features */
+ ether->dbgfs_dma_cap = debugfs_create_file("do_reset", 0444,
+ ether->dbgfs_dir,
+ ether->ndev,
+ &npcm7xx_debug_reset_fops);
+
+ if (!ether->dbgfs_dma_cap || IS_ERR(ether->dbgfs_dma_cap)) {
+ dev_err(&ether->pdev->dev, "ERROR creating stmmac \'do_reset\' debugfs file\n");
+ debugfs_remove_recursive(ether->dbgfs_dir);
+
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+#endif
+
+static void npcm7xx_opmode(struct net_device *dev, int speed, int duplex)
+{
+ __le32 val;
+ struct npcm7xx_ether *ether = netdev_priv(dev);
+
+ val = readl((ether->reg + REG_MCMDR));
+ if (speed == 100)
+ val |= MCMDR_OPMOD;
+ else
+ val &= ~MCMDR_OPMOD;
+
+ if (duplex == DUPLEX_FULL)
+ val |= MCMDR_FDUP;
+ else
+ val &= ~MCMDR_FDUP;
+
+ writel(val, (ether->reg + REG_MCMDR));
+}
+
+static void adjust_link(struct net_device *dev)
+{
+ struct npcm7xx_ether *ether = netdev_priv(dev);
+ struct phy_device *phydev = ether->phy_dev;
+ bool status_change = false;
+ unsigned long flags;
+
+ /* clear GPIO interrupt status whihc indicates PHY statu change? */
+ spin_lock_irqsave(&ether->lock, flags);
+
+ if (phydev->link) {
+ if (ether->speed != phydev->speed ||
+ ether->duplex != phydev->duplex) {
+ ether->speed = phydev->speed;
+ ether->duplex = phydev->duplex;
+ status_change = true;
+ }
+ } else {
+ ether->speed = 0;
+ ether->duplex = -1;
+ }
+
+ if (phydev->link != ether->link) {
+ ether->link = phydev->link;
+ status_change = true;
+ }
+
+ spin_unlock_irqrestore(&ether->lock, flags);
+
+ if (status_change)
+ npcm7xx_opmode(dev, ether->speed, ether->duplex);
+}
+
+static void npcm7xx_write_cam(struct net_device *dev,
+ unsigned int x, unsigned char *pval)
+{
+ struct npcm7xx_ether *ether = netdev_priv(dev);
+ __le32 msw, lsw;
+
+ msw = (pval[0] << 24) | (pval[1] << 16) | (pval[2] << 8) | pval[3];
+
+ lsw = (pval[4] << 24) | (pval[5] << 16);
+
+ writel(lsw, (ether->reg + REG_CAML_BASE) + x * CAM_ENTRY_SIZE);
+ writel(msw, (ether->reg + REG_CAMM_BASE) + x * CAM_ENTRY_SIZE);
+ dev_dbg(&ether->pdev->dev, "REG_CAML_BASE = 0x%08X REG_CAMM_BASE = 0x%08X", lsw, msw);
+}
+
+static struct sk_buff *get_new_skb(struct net_device *dev, u32 i)
+{
+ struct npcm7xx_ether *ether = netdev_priv(dev);
+ struct sk_buff *skb = dev_alloc_skb(roundup(MAX_PACKET_SIZE_W_CRC, 4));
+
+ if (!skb)
+ return NULL;
+
+ /* Do not unmark the following skb_reserve() Receive Buffer Starting
+ * Address must be aligned to 4 bytes and the following line
+ * if unmarked will make it align to 2 and this likely will
+ * hult the RX and crash the linux skb_reserve(skb, NET_IP_ALIGN);
+ */
+ skb->dev = dev;
+ (ether->rdesc + i)->buffer =
+ dma_map_single(&dev->dev, skb->data,
+ roundup(MAX_PACKET_SIZE_W_CRC, 4),
+ DMA_FROM_DEVICE);
+ ether->rx_skb[i] = skb;
+
+ return skb;
+}
+
+static int npcm7xx_init_desc(struct net_device *dev)
+{
+ struct npcm7xx_ether *ether;
+ struct npcm7xx_txbd *tdesc;
+ struct npcm7xx_rxbd *rdesc;
+ struct platform_device *pdev;
+ unsigned int i;
+
+ ether = netdev_priv(dev);
+ pdev = ether->pdev;
+
+ if (!ether->tdesc) {
+ ether->tdesc = (struct npcm7xx_txbd *)
+ dma_alloc_coherent(&pdev->dev,
+ sizeof(struct npcm7xx_txbd) *
+ TX_QUEUE_LEN,
+ &ether->tdesc_phys,
+ GFP_KERNEL);
+
+ if (!ether->tdesc) {
+ dev_err(&pdev->dev, "Failed to allocate memory for tx desc\n");
+ return -ENOMEM;
+ }
+ }
+
+ if (!ether->rdesc) {
+ ether->rdesc = (struct npcm7xx_rxbd *)
+ dma_alloc_coherent(&pdev->dev,
+ sizeof(struct npcm7xx_rxbd) *
+ RX_QUEUE_LEN,
+ &ether->rdesc_phys,
+ GFP_KERNEL);
+
+ if (!ether->rdesc) {
+ dev_err(&pdev->dev, "Failed to allocate memory for rx desc\n");
+ dma_free_coherent(&pdev->dev,
+ sizeof(struct npcm7xx_txbd) *
+ TX_QUEUE_LEN, ether->tdesc,
+ ether->tdesc_phys);
+ ether->tdesc = NULL;
+ return -ENOMEM;
+ }
+ }
+
+ for (i = 0; i < TX_QUEUE_LEN; i++) {
+ unsigned int offset;
+
+ tdesc = (ether->tdesc + i);
+
+ if (i == TX_QUEUE_LEN - 1)
+ offset = 0;
+ else
+ offset = sizeof(struct npcm7xx_txbd) * (i + 1);
+
+ tdesc->next = ether->tdesc_phys + offset;
+ tdesc->buffer = (__le32)NULL;
+ tdesc->sl = 0;
+ tdesc->mode = 0;
+ }
+
+ ether->start_tx_ptr = ether->tdesc_phys;
+
+ for (i = 0; i < RX_QUEUE_LEN; i++) {
+ unsigned int offset;
+
+ rdesc = (ether->rdesc + i);
+
+ if (i == RX_QUEUE_LEN - 1)
+ offset = 0;
+ else
+ offset = sizeof(struct npcm7xx_rxbd) * (i + 1);
+
+ rdesc->next = ether->rdesc_phys + offset;
+ rdesc->sl = RX_OWN_DMA;
+
+ if (!get_new_skb(dev, i)) {
+ dev_err(&pdev->dev, "get_new_skb() failed\n");
+
+ for (; i != 0; i--) {
+ dma_unmap_single(&dev->dev, (dma_addr_t)
+ ((ether->rdesc + i)->buffer),
+ roundup(MAX_PACKET_SIZE_W_CRC,
+ 4), DMA_FROM_DEVICE);
+ dev_kfree_skb_any(ether->rx_skb[i]);
+ ether->rx_skb[i] = NULL;
+ }
+
+ dma_free_coherent(&pdev->dev,
+ sizeof(struct npcm7xx_txbd) *
+ TX_QUEUE_LEN,
+ ether->tdesc, ether->tdesc_phys);
+ dma_free_coherent(&pdev->dev,
+ sizeof(struct npcm7xx_rxbd) *
+ RX_QUEUE_LEN,
+ ether->rdesc, ether->rdesc_phys);
+
+ return -ENOMEM;
+ }
+ }
+
+ ether->start_rx_ptr = ether->rdesc_phys;
+ wmb();
+ for (i = 0; i < TX_QUEUE_LEN; i++)
+ ether->tx_skb[i] = NULL;
+
+ return 0;
+}
+
+/* This API must call with Tx/Rx stopped */
+static void npcm7xx_free_desc(struct net_device *dev,
+ bool free_also_descriptors)
+{
+ struct sk_buff *skb;
+ u32 i;
+ struct npcm7xx_ether *ether = netdev_priv(dev);
+ struct platform_device *pdev = ether->pdev;
+
+ for (i = 0; i < TX_QUEUE_LEN; i++) {
+ skb = ether->tx_skb[i];
+ if (skb) {
+ dma_unmap_single(&dev->dev, (dma_addr_t)((ether->tdesc +
+ i)->buffer),
+ skb->len, DMA_TO_DEVICE);
+ dev_kfree_skb_any(skb);
+ ether->tx_skb[i] = NULL;
+ }
+ }
+
+ for (i = 0; i < RX_QUEUE_LEN; i++) {
+ skb = ether->rx_skb[i];
+ if (skb) {
+ dma_unmap_single(&dev->dev, (dma_addr_t)((ether->rdesc +
+ i)->buffer),
+ roundup(MAX_PACKET_SIZE_W_CRC, 4),
+ DMA_FROM_DEVICE);
+ dev_kfree_skb_any(skb);
+ ether->rx_skb[i] = NULL;
+ }
+ }
+
+ if (free_also_descriptors) {
+ if (ether->tdesc)
+ dma_free_coherent(&pdev->dev,
+ sizeof(struct npcm7xx_txbd) *
+ TX_QUEUE_LEN,
+ ether->tdesc, ether->tdesc_phys);
+ ether->tdesc = NULL;
+
+ if (ether->rdesc)
+ dma_free_coherent(&pdev->dev,
+ sizeof(struct npcm7xx_rxbd) *
+ RX_QUEUE_LEN,
+ ether->rdesc, ether->rdesc_phys);
+ ether->rdesc = NULL;
+ }
+}
+
+static void npcm7xx_set_fifo_threshold(struct net_device *dev)
+{
+ struct npcm7xx_ether *ether = netdev_priv(dev);
+ __le32 val;
+
+ val = RXTHD | TXTHD | BLENGTH;
+ writel(val, (ether->reg + REG_FFTCR));
+}
+
+static void npcm7xx_return_default_idle(struct net_device *dev)
+{
+ struct npcm7xx_ether *ether = netdev_priv(dev);
+ __le32 val;
+ __le32 saved_bits;
+
+ val = readl((ether->reg + REG_MCMDR));
+ saved_bits = val & (MCMDR_FDUP | MCMDR_OPMOD);
+ val |= SWR;
+ writel(val, (ether->reg + REG_MCMDR));
+
+ /* During the EMC reset the AHB will read 0 from all registers,
+ * so in order to see if the reset finished we can't count on
+ * (ether->reg + REG_MCMDR).SWR to become 0, instead we read another
+ * register that its reset value is not 0,
+ * we choose (ether->reg + REG_FFTCR).
+ */
+ do {
+ val = readl((ether->reg + REG_FFTCR));
+ } while (val == 0);
+
+ /*
+ * Now we can verify if (ether->reg + REG_MCMDR).SWR became
+ * 0 (probably it will be 0 on the first read).
+ */
+ do {
+ val = readl((ether->reg + REG_MCMDR));
+ } while (val & SWR);
+
+ /* restore values */
+ writel(saved_bits, (ether->reg + REG_MCMDR));
+}
+
+static void npcm7xx_enable_mac_interrupt(struct net_device *dev)
+{
+ struct npcm7xx_ether *ether = netdev_priv(dev);
+ __le32 val;
+
+ val = ENRXINTR | /* Start of RX interrupts */
+ ENCRCE |
+ EMRXOV |
+ (ENPTLE * (!IS_VLAN)) | /* If we don't support VLAN we want interrupt on long packets */
+ ENRXGD |
+ ENALIE |
+ ENRP |
+ ENMMP |
+ ENDFO |
+ /* ENDENI | */ /* We don't need interrupt on DMA Early Notification */
+ ENRDU | /* We don't need interrupt on Receive Descriptor Unavailable Interrupt */
+ ENRXBERR |
+ /* ENCFR | */
+ ENTXINTR | /* Start of TX interrupts */
+ ENTXEMP |
+ ENTXCP |
+ ENTXDEF |
+ ENNCS |
+ ENTXABT |
+ ENLC |
+ /* ENTDU | */ /* We don't need interrupt on Transmit Descriptor Unavailable at start of operation */
+ ENTXBERR;
+ writel(val, (ether->reg + REG_MIEN));
+}
+
+static void npcm7xx_get_and_clear_int(struct net_device *dev,
+ __le32 *val, __le32 mask)
+{
+ struct npcm7xx_ether *ether = netdev_priv(dev);
+
+ *val = readl((ether->reg + REG_MISTA)) & mask;
+ writel(*val, (ether->reg + REG_MISTA));
+}
+
+static void npcm7xx_set_global_maccmd(struct net_device *dev)
+{
+ struct npcm7xx_ether *ether = netdev_priv(dev);
+ __le32 val;
+
+ val = readl((ether->reg + REG_MCMDR));
+
+ val |= MCMDR_SPCRC | MCMDR_ENMDC | MCMDR_ACP | MCMDR_NDEF;
+ if (IS_VLAN) {
+ /*
+ * we set ALP accept long packets since VLAN packets
+ * are 4 bytes longer than 1518
+ */
+ val |= MCMDR_ALP;
+ /* limit receive length to 1522 bytes due to VLAN */
+ writel(MAX_PACKET_SIZE_W_CRC, (ether->reg + REG_DMARFC));
+ }
+ writel(val, (ether->reg + REG_MCMDR));
+}
+
+static void npcm7xx_enable_cam(struct net_device *dev)
+{
+ struct npcm7xx_ether *ether = netdev_priv(dev);
+ __le32 val;
+
+ npcm7xx_write_cam(dev, CAM0, dev->dev_addr);
+
+ val = readl((ether->reg + REG_CAMEN));
+ val |= CAM0EN;
+ writel(val, (ether->reg + REG_CAMEN));
+}
+
+static void npcm7xx_set_curdest(struct net_device *dev)
+{
+ struct npcm7xx_ether *ether = netdev_priv(dev);
+
+ writel(ether->start_rx_ptr, (ether->reg + REG_RXDLSA));
+ writel(ether->start_tx_ptr, (ether->reg + REG_TXDLSA));
+}
+
+static void npcm7xx_ether_set_rx_mode(struct net_device *dev)
+{
+ struct npcm7xx_ether *ether;
+ __le32 rx_mode;
+
+ ether = netdev_priv(dev);
+
+ dev_dbg(&ether->pdev->dev, "%s CAMCMR_AUP\n",
+ (dev->flags & IFF_PROMISC) ? "Set" : "Clear");
+ if (dev->flags & IFF_PROMISC)
+ rx_mode = CAMCMR_AUP | CAMCMR_AMP | CAMCMR_ABP | CAMCMR_ECMP;
+ else if ((dev->flags & IFF_ALLMULTI) || !netdev_mc_empty(dev))
+ rx_mode = CAMCMR_AMP | CAMCMR_ABP | CAMCMR_ECMP;
+ else
+ rx_mode = CAMCMR_ECMP | CAMCMR_ABP;
+ writel(rx_mode, (ether->reg + REG_CAMCMR));
+ ether->camcmr = rx_mode;
+}
+
+static void npcm7xx_reset_mac(struct net_device *dev, int need_free)
+{
+ struct npcm7xx_ether *ether = netdev_priv(dev);
+
+ netif_tx_lock(dev);
+
+ /* disable RX and TX */
+ writel(readl((ether->reg + REG_MCMDR)) & ~(MCMDR_TXON | MCMDR_RXON),
+ (ether->reg + REG_MCMDR));
+
+ npcm7xx_return_default_idle(dev);
+ npcm7xx_set_fifo_threshold(dev);
+
+ if (need_free)
+ npcm7xx_free_desc(dev, false);
+
+ npcm7xx_init_desc(dev);
+
+ ether->cur_tx = 0x0;
+ ether->finish_tx = 0x0;
+ ether->pending_tx = 0x0;
+ ether->cur_rx = 0x0;
+ ether->tx_tdu = 0;
+ ether->tx_tdu_i = 0;
+ ether->tx_cp_i = 0;
+
+ npcm7xx_set_curdest(dev);
+ npcm7xx_enable_cam(dev);
+ npcm7xx_ether_set_rx_mode(dev);
+ npcm7xx_enable_mac_interrupt(dev);
+ npcm7xx_set_global_maccmd(dev);
+
+ /* enable RX and TX */
+ writel(readl((ether->reg + REG_MCMDR)) | MCMDR_TXON | MCMDR_RXON,
+ (ether->reg + REG_MCMDR));
+
+ /* trigger RX */
+ writel(ENSTART, (ether->reg + REG_RSDR));
+
+ ether->need_reset = 0;
+
+ netif_wake_queue(dev);
+ netif_tx_unlock(dev);
+}
+
+static int npcm7xx_mdio_write(struct mii_bus *bus, int phy_id, int regnum,
+ u16 value)
+{
+ struct npcm7xx_ether *ether = bus->priv;
+ unsigned long timeout = jiffies + msecs_to_jiffies(MII_TIMEOUT * 100);
+
+ writel(value, (ether->reg + REG_MIID));
+ writel((phy_id << 0x08) | regnum | PHYBUSY | PHYWR,
+ (ether->reg + REG_MIIDA));
+
+ /* Wait for completion */
+ while (readl((ether->reg + REG_MIIDA)) & PHYBUSY) {
+ if (time_after(jiffies, timeout)) {
+ dev_dbg(&ether->pdev->dev, "mdio read timed out\n ether->reg = 0x%x phy_id=0x%x REG_MIIDA=0x%x\n",
+ (unsigned int)ether->reg, phy_id
+ , readl((ether->reg + REG_MIIDA)));
+ return -ETIMEDOUT;
+ }
+ cpu_relax();
+ }
+
+ return 0;
+}
+
+static int npcm7xx_mdio_read(struct mii_bus *bus, int phy_id, int regnum)
+{
+ struct npcm7xx_ether *ether = bus->priv;
+ unsigned long timeout = jiffies + msecs_to_jiffies(MII_TIMEOUT * 100);
+
+ writel((phy_id << 0x08) | regnum | PHYBUSY, (ether->reg + REG_MIIDA));
+
+ /* Wait for completion */
+ while (readl((ether->reg + REG_MIIDA)) & PHYBUSY) {
+ if (time_after(jiffies, timeout)) {
+ dev_dbg(&ether->pdev->dev, "mdio read timed out\n ether->reg = 0x%x phy_id=0x%x REG_MIIDA=0x%x\n",
+ (unsigned int)ether->reg, phy_id
+ , readl((ether->reg + REG_MIIDA)));
+ return -ETIMEDOUT;
+ }
+ cpu_relax();
+ }
+
+ return readl((ether->reg + REG_MIID));
+}
+
+static int npcm7xx_mdio_reset(struct mii_bus *bus)
+{
+ /* reset EMAC engine?? */
+ return 0;
+}
+
+static int npcm7xx_set_mac_address(struct net_device *dev, void *addr)
+{
+ struct sockaddr *address = addr;
+
+ if (!is_valid_ether_addr((u8 *)address->sa_data))
+ return -EADDRNOTAVAIL;
+
+ memcpy(dev->dev_addr, address->sa_data, dev->addr_len);
+ npcm7xx_write_cam(dev, CAM0, dev->dev_addr);
+
+ return 0;
+}
+
+static int npcm7xx_ether_close(struct net_device *dev)
+{
+ struct npcm7xx_ether *ether = netdev_priv(dev);
+
+ npcm7xx_return_default_idle(dev);
+
+ if (ether->phy_dev)
+ phy_stop(ether->phy_dev);
+ else if (ether->use_ncsi)
+ ncsi_stop_dev(ether->ncsidev);
+
+ msleep(20);
+
+ free_irq(ether->txirq, dev);
+ free_irq(ether->rxirq, dev);
+
+ netif_stop_queue(dev);
+ napi_disable(&ether->napi);
+
+ npcm7xx_free_desc(dev, true);
+
+ kfree(ether->dump_buf);
+ ether->dump_buf = NULL;
+
+ return 0;
+}
+
+static struct net_device_stats *npcm7xx_ether_stats(struct net_device *dev)
+{
+ struct npcm7xx_ether *ether;
+
+ ether = netdev_priv(dev);
+ return &ether->stats;
+}
+
+static int npcm7xx_clean_tx(struct net_device *dev, bool from_xmit)
+{
+ struct npcm7xx_ether *ether = netdev_priv(dev);
+ struct npcm7xx_txbd *txbd;
+ struct sk_buff *s;
+ dma_addr_t cur_entry, entry;
+ __le32 sl;
+
+ if (ether->pending_tx == 0)
+ return (0);
+
+ cur_entry = readl((ether->reg + REG_CTXDSA));
+
+ /* Release old used buffers */
+ entry = ether->tdesc_phys + sizeof(struct npcm7xx_txbd) *
+ (ether->finish_tx);
+
+ while (entry != cur_entry) {
+ txbd = (ether->tdesc + ether->finish_tx);
+ s = ether->tx_skb[ether->finish_tx];
+ if (!s)
+ break;
+
+ ether->count_finish++;
+
+ dma_unmap_single(&dev->dev, txbd->buffer, s->len,
+ DMA_TO_DEVICE);
+ consume_skb(s);
+ ether->tx_skb[ether->finish_tx] = NULL;
+
+ if (++ether->finish_tx >= TX_QUEUE_LEN)
+ ether->finish_tx = 0;
+ ether->pending_tx--;
+
+ sl = txbd->sl;
+ if (sl & TXDS_TXCP) {
+ ether->stats.tx_packets++;
+ ether->stats.tx_bytes += (sl & 0xFFFF);
+ } else {
+ ether->stats.tx_errors++;
+ }
+
+ entry = ether->tdesc_phys + sizeof(struct npcm7xx_txbd) *
+ (ether->finish_tx);
+ }
+
+ if (!from_xmit && unlikely(netif_queue_stopped(dev) &&
+ (TX_QUEUE_LEN - ether->pending_tx) > 1)) {
+ netif_tx_lock(dev);
+ if (netif_queue_stopped(dev) &&
+ (TX_QUEUE_LEN - ether->pending_tx) > 1) {
+ netif_wake_queue(dev);
+ }
+ netif_tx_unlock(dev);
+ }
+
+ return(0);
+}
+
+static int npcm7xx_ether_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct npcm7xx_ether *ether = netdev_priv(dev);
+ struct npcm7xx_txbd *txbd;
+ unsigned long flags;
+
+ ether->count_xmit++;
+
+ /* Insert new buffer */
+ txbd = (ether->tdesc + ether->cur_tx);
+ txbd->buffer = dma_map_single(&dev->dev, skb->data, skb->len,
+ DMA_TO_DEVICE);
+ ether->tx_skb[ether->cur_tx] = skb;
+ if (skb->len > MAX_PACKET_SIZE)
+ dev_err(&ether->pdev->dev, "skb->len (= %d) > MAX_PACKET_SIZE (= %d)\n",
+ skb->len, MAX_PACKET_SIZE);
+
+ txbd->sl = skb->len > MAX_PACKET_SIZE ? MAX_PACKET_SIZE : skb->len;
+ dma_wmb();
+
+ txbd->mode = TX_OWN_DMA | PADDINGMODE | CRCMODE;
+ wmb();
+
+ /* trigger TX */
+ writel(ENSTART, (ether->reg + REG_TSDR));
+
+ if (++ether->cur_tx >= TX_QUEUE_LEN)
+ ether->cur_tx = 0;
+
+ spin_lock_irqsave(&ether->lock, flags);
+ ether->pending_tx++;
+
+ npcm7xx_clean_tx(dev, true);
+
+ if (ether->pending_tx >= TX_QUEUE_LEN - 1) {
+ __le32 reg_mien;
+ unsigned int index_to_wake = ether->cur_tx +
+ ((TX_QUEUE_LEN * 3) / 4);
+
+ if (index_to_wake >= TX_QUEUE_LEN)
+ index_to_wake -= TX_QUEUE_LEN;
+
+ txbd = (ether->tdesc + index_to_wake);
+ txbd->mode = TX_OWN_DMA | PADDINGMODE | CRCMODE | MACTXINTEN;
+ wmb();
+
+ writel(MISTA_TDU, (ether->reg + REG_MISTA));
+ /* Clear TDU interrupt */
+ reg_mien = readl((ether->reg + REG_MIEN));
+
+ if (reg_mien != 0)
+ /* Enable TDU interrupt */
+ writel(reg_mien | ENTDU, (ether->reg + REG_MIEN));
+
+ ether->tx_tdu++;
+ netif_stop_queue(dev);
+ }
+
+ spin_unlock_irqrestore(&ether->lock, flags);
+
+ return 0;
+}
+
+static irqreturn_t npcm7xx_tx_interrupt(int irq, void *dev_id)
+{
+ struct npcm7xx_ether *ether;
+ struct platform_device *pdev;
+ struct net_device *dev;
+ __le32 status;
+ unsigned long flags;
+
+ dev = dev_id;
+ ether = netdev_priv(dev);
+ pdev = ether->pdev;
+
+ npcm7xx_get_and_clear_int(dev, &status, 0xFFFF0000);
+
+ ether->tx_int_count++;
+
+ if (status & MISTA_EXDEF)
+ dev_err(&pdev->dev, "emc defer exceed interrupt status=0x%08X\n"
+ , status);
+ else if (status & MISTA_TXBERR) {
+ dev_err(&pdev->dev, "emc bus error interrupt status=0x%08X\n",
+ status);
+#ifdef CONFIG_NPCM7XX_EMC_ETH_DEBUG
+ npcm7xx_info_print(dev);
+#endif
+ spin_lock_irqsave(&ether->lock, flags);
+ writel(0, (ether->reg + REG_MIEN)); /* disable any interrupt */
+ spin_unlock_irqrestore(&ether->lock, flags);
+ ether->need_reset = 1;
+ } else if (status & ~(MISTA_TXINTR | MISTA_TXCP | MISTA_TDU))
+ dev_err(&pdev->dev, "emc other error interrupt status=0x%08X\n",
+ status);
+
+ /* if we got MISTA_TXCP | MISTA_TDU remove those interrupt and call napi */
+ if (status & (MISTA_TXCP | MISTA_TDU) &
+ readl((ether->reg + REG_MIEN))) {
+ __le32 reg_mien;
+
+ spin_lock_irqsave(&ether->lock, flags);
+ reg_mien = readl((ether->reg + REG_MIEN));
+ if (reg_mien & ENTDU)
+ /* Disable TDU interrupt */
+ writel(reg_mien & (~ENTDU), (ether->reg + REG_MIEN));
+
+ spin_unlock_irqrestore(&ether->lock, flags);
+
+ if (status & MISTA_TXCP)
+ ether->tx_cp_i++;
+ if (status & MISTA_TDU)
+ ether->tx_tdu_i++;
+ } else {
+ dev_dbg(&pdev->dev, "status=0x%08X\n", status);
+ }
+
+ napi_schedule(&ether->napi);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t npcm7xx_rx_interrupt(int irq, void *dev_id)
+{
+ struct net_device *dev = (struct net_device *)dev_id;
+ struct npcm7xx_ether *ether = netdev_priv(dev);
+ struct platform_device *pdev = ether->pdev;
+ __le32 status;
+ unsigned long flags;
+ unsigned int any_err = 0;
+ __le32 rxfsm;
+
+ npcm7xx_get_and_clear_int(dev, &status, 0xFFFF);
+ ether->rx_int_count++;
+
+ if (unlikely(status & MISTA_RXBERR)) {
+ ether->rx_berr++;
+ dev_err(&pdev->dev, "emc rx bus error status=0x%08X\n", status);
+#ifdef CONFIG_NPCM7XX_EMC_ETH_DEBUG
+ npcm7xx_info_print(dev);
+#endif
+ spin_lock_irqsave(&ether->lock, flags);
+ writel(0, (ether->reg + REG_MIEN)); /* disable any interrupt */
+ spin_unlock_irqrestore(&ether->lock, flags);
+ ether->need_reset = 1;
+ napi_schedule(&ether->napi);
+ return IRQ_HANDLED;
+ }
+
+ if (unlikely(status & (MISTA_RXOV | MISTA_RDU))) {
+ /*
+ * filter out all received packets until we have
+ * enough available buffer descriptors
+ */
+ writel(0, (ether->reg + REG_CAMCMR));
+ any_err = 1;
+ if (status & (MISTA_RXOV))
+ ether->rxov++;
+ if (status & (MISTA_RDU))
+ ether->rdu++;
+
+ /*
+ * workaround Errata 1.36: EMC Hangs on receiving 253-256
+ * byte packet
+ */
+ rxfsm = readl((ether->reg + REG_RXFSM));
+
+ if ((rxfsm & 0xFFFFF000) == 0x08044000) {
+ int i;
+
+ for (i = 0; i < 32; i++) {
+ rxfsm = readl((ether->reg + REG_RXFSM));
+ if ((rxfsm & 0xFFFFF000) != 0x08044000)
+ break;
+ }
+ if (i == 32) {
+ ether->rx_stuck++;
+ spin_lock_irqsave(&ether->lock, flags);
+#ifdef CONFIG_NPCM7XX_EMC_ETH_DEBUG
+ npcm7xx_info_print(dev);
+#endif
+ writel(0, (ether->reg + REG_MIEN));
+ spin_unlock_irqrestore(&ether->lock, flags);
+ ether->need_reset = 1;
+ napi_schedule(&ether->napi);
+ dev_err(&pdev->dev, "stuck on REG_RXFSM = 0x%08X status=%08X doing reset!\n", rxfsm, status);
+ return IRQ_HANDLED;
+ }
+ }
+ }
+
+ /* echo MISTA status on unexpected flags although we don't do anithing with them */
+ if (unlikely(status &
+ (/* MISTA_RXINTR | */ /* Receive - all RX interrupt set this */
+ MISTA_CRCE | /* CRC Error */
+ /* MISTA_RXOV | */ /* Receive FIFO Overflow - we alread handled it */
+ (MISTA_PTLE * !IS_VLAN) | /* Packet Too Long is needed if VLAN is not supported */
+ /* MISTA_RXGD | */ /* Receive Good - this is the common good case */
+ MISTA_ALIE | /* Alignment Error */
+ MISTA_RP | /* Runt Packet */
+ MISTA_MMP | /* More Missed Packet */
+ MISTA_DFOI | /* Maximum Frame Length */
+ /* MISTA_DENI | */ /* DMA Early Notification - every packet get this */
+ /* MISTA_RDU | */ /* Receive Descriptor Unavailable */
+ /* MISTA_RXBERR | */ /* Receive Bus Error Interrupt - we alread handled it */
+ /* MISTA_CFR | */ /* Control Frame Receive - not an error */
+ 0))) {
+ dev_dbg(&pdev->dev, "emc rx MISTA status=0x%08X\n", status);
+ any_err = 1;
+ ether->rx_err++;
+ }
+
+ if (!any_err && ((status & MISTA_RXGD) == 0))
+ dev_err(&pdev->dev, "emc rx MISTA status=0x%08X\n", status);
+
+ spin_lock_irqsave(&ether->lock, flags);
+ writel(readl((ether->reg + REG_MIEN)) & ~ENRXGD,
+ (ether->reg + REG_MIEN));
+ spin_unlock_irqrestore(&ether->lock, flags);
+ napi_schedule(&ether->napi);
+
+ return IRQ_HANDLED;
+}
+
+static int npcm7xx_poll(struct napi_struct *napi, int budget)
+{
+ struct npcm7xx_ether *ether =
+ container_of(napi, struct npcm7xx_ether, napi);
+ struct npcm7xx_rxbd *rxbd;
+ struct net_device *dev = ether->ndev;
+ struct platform_device *pdev = ether->pdev;
+ struct sk_buff *skb, *s;
+ unsigned int length;
+ __le32 status;
+ unsigned long flags;
+ int rx_cnt = 0;
+ int complete = 0;
+ unsigned int rx_offset = (readl((ether->reg + REG_CRXDSA)) -
+ ether->start_rx_ptr) /
+ sizeof(struct npcm7xx_txbd);
+ unsigned int local_count = (rx_offset >= ether->cur_rx) ?
+ rx_offset - ether->cur_rx : rx_offset +
+ RX_QUEUE_LEN - ether->cur_rx;
+
+ if (local_count > ether->max_waiting_rx)
+ ether->max_waiting_rx = local_count;
+
+ if (local_count > (4 * RX_POLL_SIZE))
+ /*
+ * we are porbably in a storm of short packets and we don't
+ * want to get into RDU since short packets in RDU cause
+ * many RXOV which may cause EMC halt, so we filter out all
+ * coming packets
+ */
+ writel(0, (ether->reg + REG_CAMCMR));
+
+ if (local_count <= budget)
+ /* we can restore accepting of packets */
+ writel(ether->camcmr, (ether->reg + REG_CAMCMR));
+
+ spin_lock_irqsave(&ether->lock, flags);
+ npcm7xx_clean_tx(dev, false);
+ spin_unlock_irqrestore(&ether->lock, flags);
+
+ rxbd = (ether->rdesc + ether->cur_rx);
+
+ while (rx_cnt < budget) {
+ status = rxbd->sl;
+ if ((status & RX_OWN_DMA) == RX_OWN_DMA) {
+ complete = 1;
+ break;
+ }
+ /* for debug puposes we save the previous value */
+ rxbd->reserved = status;
+ s = ether->rx_skb[ether->cur_rx];
+ length = status & 0xFFFF;
+
+ /*
+ * If VLAN is not supporte RXDS_PTLE (packet too long) is also
+ * an error
+ */
+ if (likely((status & (RXDS_RXGD | RXDS_CRCE | RXDS_ALIE |
+ RXDS_RP | (IS_VLAN ? 0 : RXDS_PTLE))) ==
+ RXDS_RXGD) && likely(length <= MAX_PACKET_SIZE)) {
+ dma_unmap_single(&dev->dev, (dma_addr_t)rxbd->buffer,
+ roundup(MAX_PACKET_SIZE_W_CRC, 4),
+ DMA_FROM_DEVICE);
+
+ skb_put(s, length);
+ s->protocol = eth_type_trans(s, dev);
+ netif_receive_skb(s);
+ ether->stats.rx_packets++;
+ ether->stats.rx_bytes += length;
+ rx_cnt++;
+ ether->rx_count_pool++;
+
+ /* now we allocate new skb instead if the used one. */
+ skb = dev_alloc_skb(roundup(MAX_PACKET_SIZE_W_CRC, 4));
+ if (!skb) {
+ dev_err(&pdev->dev, "get skb buffer error\n");
+ ether->stats.rx_dropped++;
+ goto rx_out;
+ }
+
+ /* Do not unmark the following skb_reserve() Receive
+ * Buffer Starting Address must be aligned
+ * to 4 bytes and the following line if unmarked
+ * will make it align to 2 and this likely
+ * will hult the RX and crash the linux
+ * skb_reserve(skb, NET_IP_ALIGN);
+ */
+ skb->dev = dev;
+
+ rxbd->buffer = dma_map_single(&dev->dev, skb->data,
+ roundup(MAX_PACKET_SIZE_W_CRC, 4),
+ DMA_FROM_DEVICE);
+ ether->rx_skb[ether->cur_rx] = skb;
+ } else {
+ ether->rx_err_count++;
+ ether->stats.rx_errors++;
+ dev_dbg(&pdev->dev, "rx_errors = %lu status = 0x%08X\n",
+ ether->stats.rx_errors, status);
+
+ if (status & RXDS_RP) {
+ ether->stats.rx_length_errors++;
+ dev_dbg(&pdev->dev, "rx_length_errors = %lu\n",
+ ether->stats.rx_length_errors);
+ } else if (status & RXDS_CRCE) {
+ ether->stats.rx_crc_errors++;
+ dev_dbg(&pdev->dev, "rx_crc_errors = %lu\n",
+ ether->stats.rx_crc_errors);
+ } else if (status & RXDS_ALIE) {
+ ether->stats.rx_frame_errors++;
+ dev_dbg(&pdev->dev, "rx_frame_errors = %lu\n",
+ ether->stats.rx_frame_errors);
+ } else if (((!IS_VLAN) && (status & RXDS_PTLE)) ||
+ length > MAX_PACKET_SIZE) {
+ ether->stats.rx_length_errors++;
+ dev_dbg(&pdev->dev, "rx_length_errors = %lu\n",
+ ether->stats.rx_length_errors);
+ }
+ }
+
+ wmb();
+ rxbd->sl = RX_OWN_DMA;
+ wmb();
+
+ if (++ether->cur_rx >= RX_QUEUE_LEN)
+ ether->cur_rx = 0;
+
+ rxbd = (ether->rdesc + ether->cur_rx);
+ }
+
+ if (complete) {
+ napi_complete(napi);
+
+ if (ether->need_reset) {
+ dev_dbg(&pdev->dev, "Reset\n");
+ npcm7xx_reset_mac(dev, 1);
+ }
+
+ spin_lock_irqsave(&ether->lock, flags);
+ writel(readl((ether->reg + REG_MIEN)) | ENRXGD, (ether->reg +
+ REG_MIEN));
+ spin_unlock_irqrestore(&ether->lock, flags);
+ } else {
+ rx_offset = (readl((ether->reg + REG_CRXDSA)) -
+ ether->start_rx_ptr) / sizeof(struct npcm7xx_txbd);
+ local_count = (rx_offset >= ether->cur_rx) ? rx_offset -
+ ether->cur_rx : rx_offset + RX_QUEUE_LEN -
+ ether->cur_rx;
+
+ if (local_count > ether->max_waiting_rx)
+ ether->max_waiting_rx = local_count;
+
+ if (local_count > (3 * RX_POLL_SIZE))
+ /*
+ * we are porbably in a storm of short packets and
+ * we don't want to get into RDU since short packets in
+ * RDU cause many RXOV which may cause
+ * EMC halt, so we filter out all coming packets
+ */
+ writel(0, (ether->reg + REG_CAMCMR));
+ if (local_count <= RX_POLL_SIZE)
+ /* we can restore accepting of packets */
+ writel(ether->camcmr, (ether->reg + REG_CAMCMR));
+ }
+rx_out:
+
+ /* trigger RX */
+ writel(ENSTART, (ether->reg + REG_RSDR));
+ return rx_cnt;
+}
+
+static int npcm7xx_ether_open(struct net_device *dev)
+{
+ struct npcm7xx_ether *ether;
+ struct platform_device *pdev;
+
+ ether = netdev_priv(dev);
+ pdev = ether->pdev;
+
+ if (ether->use_ncsi) {
+ ether->speed = 100;
+ ether->duplex = DUPLEX_FULL;
+ npcm7xx_opmode(dev, 100, DUPLEX_FULL);
+ }
+ npcm7xx_reset_mac(dev, 0);
+
+ if (request_irq(ether->txirq, npcm7xx_tx_interrupt, 0x0, pdev->name,
+ dev)) {
+ dev_err(&pdev->dev, "register irq tx failed\n");
+ npcm7xx_ether_close(dev);
+ return -EAGAIN;
+ }
+
+ if (request_irq(ether->rxirq, npcm7xx_rx_interrupt, 0x0, pdev->name,
+ dev)) {
+ dev_err(&pdev->dev, "register irq rx failed\n");
+ npcm7xx_ether_close(dev);
+ return -EAGAIN;
+ }
+
+ if (ether->phy_dev)
+ phy_start(ether->phy_dev);
+ else if (ether->use_ncsi)
+ netif_carrier_on(dev);
+
+ netif_start_queue(dev);
+ napi_enable(&ether->napi);
+
+ /* trigger RX */
+ writel(ENSTART, (ether->reg + REG_RSDR));
+
+ /* Start the NCSI device */
+ if (ether->use_ncsi) {
+ int err = ncsi_start_dev(ether->ncsidev);
+
+ if (err) {
+ npcm7xx_ether_close(dev);
+ return err;
+ }
+ }
+
+ dev_info(&pdev->dev, "%s is OPENED\n", dev->name);
+
+ return 0;
+}
+
+static int npcm7xx_ether_ioctl(struct net_device *dev,
+ struct ifreq *ifr, int cmd)
+{
+ struct npcm7xx_ether *ether = netdev_priv(dev);
+ struct phy_device *phydev = ether->phy_dev;
+
+ if (!netif_running(dev))
+ return -EINVAL;
+
+ if (!phydev)
+ return -ENODEV;
+
+ return phy_mii_ioctl(phydev, ifr, cmd);
+}
+
+static void npcm7xx_get_drvinfo(struct net_device *dev,
+ struct ethtool_drvinfo *info)
+{
+ strlcpy(info->driver, DRV_MODULE_NAME, sizeof(info->driver));
+ strlcpy(info->version, DRV_MODULE_VERSION, sizeof(info->version));
+ strlcpy(info->fw_version, "N/A", sizeof(info->fw_version));
+ strlcpy(info->bus_info, "N/A", sizeof(info->bus_info));
+}
+
+static int npcm7xx_get_settings(struct net_device *dev,
+ struct ethtool_link_ksettings *cmd)
+{
+ struct npcm7xx_ether *ether = netdev_priv(dev);
+ struct phy_device *phydev = ether->phy_dev;
+
+ if (!phydev)
+ return -ENODEV;
+
+ dev_info(&ether->pdev->dev, "\n\nnpcm7xx_get_settings\n");
+ phy_ethtool_ksettings_get(phydev, cmd);
+
+ return 0;
+}
+
+static int npcm7xx_set_settings(struct net_device *dev,
+ const struct ethtool_link_ksettings *cmd)
+{
+ struct npcm7xx_ether *ether = netdev_priv(dev);
+ struct phy_device *phydev = ether->phy_dev;
+ int ret;
+
+ if (!phydev)
+ return -ENODEV;
+
+ dev_info(&ether->pdev->dev, "\n\nnpcm7xx_set_settings\n");
+ ret = phy_ethtool_ksettings_set(phydev, cmd);
+
+ return ret;
+}
+
+static u32 npcm7xx_get_msglevel(struct net_device *dev)
+{
+ struct npcm7xx_ether *ether = netdev_priv(dev);
+
+ return ether->msg_enable;
+}
+
+static void npcm7xx_set_msglevel(struct net_device *dev, u32 level)
+{
+ struct npcm7xx_ether *ether = netdev_priv(dev);
+
+ ether->msg_enable = level;
+}
+
+static const struct ethtool_ops npcm7xx_ether_ethtool_ops = {
+ .get_link_ksettings = npcm7xx_get_settings,
+ .set_link_ksettings = npcm7xx_set_settings,
+ .get_drvinfo = npcm7xx_get_drvinfo,
+ .get_msglevel = npcm7xx_get_msglevel,
+ .set_msglevel = npcm7xx_set_msglevel,
+ .get_link = ethtool_op_get_link,
+};
+
+static const struct net_device_ops npcm7xx_ether_netdev_ops = {
+ .ndo_open = npcm7xx_ether_open,
+ .ndo_stop = npcm7xx_ether_close,
+ .ndo_start_xmit = npcm7xx_ether_start_xmit,
+ .ndo_get_stats = npcm7xx_ether_stats,
+ .ndo_set_rx_mode = npcm7xx_ether_set_rx_mode,
+ .ndo_set_mac_address = npcm7xx_set_mac_address,
+ .ndo_do_ioctl = npcm7xx_ether_ioctl,
+ .ndo_validate_addr = eth_validate_addr,
+};
+
+static void get_mac_address(struct net_device *dev)
+{
+ struct npcm7xx_ether *ether = netdev_priv(dev);
+ struct platform_device *pdev = ether->pdev;
+ struct device_node *np = ether->pdev->dev.of_node;
+ const u8 *mac_address = NULL;
+
+ mac_address = of_get_mac_address(np);
+
+ if (mac_address != 0)
+ ether_addr_copy(dev->dev_addr, mac_address);
+
+ if (is_valid_ether_addr(dev->dev_addr)) {
+ dev_info(&pdev->dev, "%s: device MAC address : %pM\n",
+ pdev->name, dev->dev_addr);
+ } else {
+ eth_hw_addr_random(dev);
+ dev_info(&pdev->dev, "%s: device MAC address (random generator) %pM\n",
+ dev->name, dev->dev_addr);
+ }
+}
+
+static int npcm7xx_mii_setup(struct net_device *dev)
+{
+ struct npcm7xx_ether *ether = netdev_priv(dev);
+ struct platform_device *pdev;
+ struct phy_device *phydev = NULL;
+ int i, err = 0;
+
+ pdev = ether->pdev;
+
+ ether->mii_bus = mdiobus_alloc();
+ if (!ether->mii_bus) {
+ err = -ENOMEM;
+ dev_err(&pdev->dev, "mdiobus_alloc() failed\n");
+ goto out0;
+ }
+
+ ether->mii_bus->name = "npcm7xx_rmii";
+ ether->mii_bus->read = &npcm7xx_mdio_read;
+ ether->mii_bus->write = &npcm7xx_mdio_write;
+ ether->mii_bus->reset = &npcm7xx_mdio_reset;
+ snprintf(ether->mii_bus->id, MII_BUS_ID_SIZE, "%s-%x",
+ ether->pdev->name, ether->pdev->id);
+ dev_dbg(&pdev->dev, "%s ether->mii_bus->id=%s\n", __func__,
+ ether->mii_bus->id);
+ ether->mii_bus->priv = ether;
+ ether->mii_bus->parent = &ether->pdev->dev;
+
+ for (i = 0; i < PHY_MAX_ADDR; i++)
+ ether->mii_bus->irq[i] = PHY_POLL;
+
+ platform_set_drvdata(ether->pdev, ether->mii_bus);
+
+ /* Enable MDIO Clock */
+ writel(readl((ether->reg + REG_MCMDR)) | MCMDR_ENMDC,
+ (ether->reg + REG_MCMDR));
+
+ if (mdiobus_register(ether->mii_bus)) {
+ dev_err(&pdev->dev, "mdiobus_register() failed\n");
+ goto out2;
+ }
+
+ phydev = phy_find_first(ether->mii_bus);
+ if (!phydev) {
+ dev_err(&pdev->dev, "phy_find_first() failed\n");
+ goto out3;
+ }
+
+ dev_info(&pdev->dev, " name = %s ETH-Phy-Id = 0x%x\n",
+ phydev_name(phydev), phydev->phy_id);
+
+ phydev = phy_connect(dev, phydev_name(phydev),
+ &adjust_link,
+ PHY_INTERFACE_MODE_RMII);
+
+ dev_info(&pdev->dev, " ETH-Phy-Id = 0x%x name = %s\n",
+ phydev->phy_id, phydev->drv->name);
+
+ if (IS_ERR(phydev)) {
+ err = PTR_ERR(phydev);
+ dev_err(&pdev->dev, "phy_connect() failed - %d\n", err);
+ goto out3;
+ }
+
+ linkmode_and(phydev->supported, phydev->supported, PHY_BASIC_FEATURES);
+ linkmode_copy(phydev->advertising, phydev->supported);
+ ether->phy_dev = phydev;
+
+ return 0;
+
+out3:
+ mdiobus_unregister(ether->mii_bus);
+out2:
+ kfree(ether->mii_bus->irq);
+ mdiobus_free(ether->mii_bus);
+out0:
+
+ return err;
+}
+
+static const struct of_device_id emc_dt_id[] = {
+ { .compatible = "nuvoton,npcm750-emc", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, emc_dt_id);
+
+static void npcm7xx_ncsi_handler(struct ncsi_dev *nd)
+{
+ if (unlikely(nd->state != ncsi_dev_state_functional))
+ return;
+
+ netdev_info(nd->dev, "NCSI interface %s\n",
+ nd->link_up ? "up" : "down");
+}
+
+static int npcm7xx_ether_probe(struct platform_device *pdev)
+{
+ struct npcm7xx_ether *ether;
+ struct net_device *dev;
+ int error;
+
+ struct clk *emc_clk = NULL;
+ struct device_node *np = pdev->dev.of_node;
+
+ pdev->id = of_alias_get_id(np, "ethernet");
+ if (pdev->id < 0)
+ pdev->id = 0;
+
+ emc_clk = devm_clk_get(&pdev->dev, NULL);
+
+ if (IS_ERR(emc_clk))
+ return PTR_ERR(emc_clk);
+
+ /* Enable Clock */
+ clk_prepare_enable(emc_clk);
+
+ error = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
+ if (error)
+ return -ENODEV;
+
+ dev = alloc_etherdev(sizeof(struct npcm7xx_ether));
+ if (!dev)
+ return -ENOMEM;
+
+ ether = netdev_priv(dev);
+
+ ether->rst_regmap =
+ syscon_regmap_lookup_by_compatible("nuvoton,npcm750-rst");
+ if (IS_ERR(ether->rst_regmap)) {
+ dev_err(&pdev->dev, "%s: failed to find nuvoton,npcm750-rst\n", __func__);
+ return IS_ERR(ether->rst_regmap);
+ }
+
+ /* Reset EMC module */
+ if (pdev->id == 0) {
+ regmap_update_bits(ether->rst_regmap, IPSRST1_OFFSET,
+ (0x1 << 6), (0x1 << 6));
+ regmap_update_bits(ether->rst_regmap, IPSRST1_OFFSET,
+ (0x1 << 6), 0);
+ }
+ if (pdev->id == 1) {
+ regmap_update_bits(ether->rst_regmap, IPSRST1_OFFSET,
+ (0x1 << 21), (0x1 << 21));
+ regmap_update_bits(ether->rst_regmap, IPSRST1_OFFSET,
+ (0x1 << 21), 0);
+ }
+
+ ether->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!ether->res) {
+ dev_err(&pdev->dev, "failed to get I/O memory\n");
+ error = -ENXIO;
+ goto failed_free;
+ }
+
+ if (!request_mem_region(ether->res->start,
+ resource_size(ether->res), pdev->name)) {
+ dev_err(&pdev->dev, "failed to request I/O memory\n");
+ error = -EBUSY;
+ goto failed_free;
+ }
+
+ ether->reg = ioremap(ether->res->start, resource_size(ether->res));
+ dev_dbg(&pdev->dev, "%s ether->reg = 0x%x\n", __func__,
+ (unsigned int)ether->reg);
+
+ if (!ether->reg) {
+ dev_err(&pdev->dev, "failed to remap I/O memory\n");
+ error = -ENXIO;
+ goto failed_free_mem;
+ }
+
+ ether->txirq = platform_get_irq(pdev, 0);
+ if (ether->txirq < 0) {
+ dev_err(&pdev->dev, "failed to get ether tx irq\n");
+ error = -ENXIO;
+ goto failed_free_io;
+ }
+
+ ether->rxirq = platform_get_irq(pdev, 1);
+ if (ether->rxirq < 0) {
+ dev_err(&pdev->dev, "failed to get ether rx irq\n");
+ error = -ENXIO;
+ goto failed_free_io;
+ }
+
+ SET_NETDEV_DEV(dev, &pdev->dev);
+ platform_set_drvdata(pdev, dev);
+ ether->ndev = dev;
+
+ ether->pdev = pdev;
+ ether->msg_enable = NETIF_MSG_LINK;
+
+ dev->netdev_ops = &npcm7xx_ether_netdev_ops;
+ dev->ethtool_ops = &npcm7xx_ether_ethtool_ops;
+
+ dev->tx_queue_len = TX_QUEUE_LEN;
+ dev->dma = 0x0;
+ dev->watchdog_timeo = TX_TIMEOUT;
+
+ get_mac_address(dev);
+
+ ether->cur_tx = 0x0;
+ ether->cur_rx = 0x0;
+ ether->finish_tx = 0x0;
+ ether->pending_tx = 0x0;
+ ether->link = 0;
+ ether->speed = 100;
+ ether->duplex = DUPLEX_FULL;
+ ether->need_reset = 0;
+ ether->dump_buf = NULL;
+ ether->rx_berr = 0;
+ ether->rx_err = 0;
+ ether->rdu = 0;
+ ether->rxov = 0;
+ ether->rx_stuck = 0;
+ /* debug counters */
+ ether->max_waiting_rx = 0;
+ ether->rx_count_pool = 0;
+ ether->count_xmit = 0;
+ ether->rx_int_count = 0;
+ ether->rx_err_count = 0;
+ ether->tx_int_count = 0;
+ ether->count_finish = 0;
+ ether->tx_tdu = 0;
+ ether->tx_tdu_i = 0;
+ ether->tx_cp_i = 0;
+
+ spin_lock_init(&ether->lock);
+
+ netif_napi_add(dev, &ether->napi, npcm7xx_poll, RX_POLL_SIZE);
+
+ if (pdev->dev.of_node &&
+ of_get_property(pdev->dev.of_node, "use-ncsi", NULL)) {
+ if (!IS_ENABLED(CONFIG_NET_NCSI)) {
+ dev_err(&pdev->dev, "CONFIG_NET_NCSI not enabled\n");
+ error = -ENODEV;
+ goto failed_free_napi;
+ }
+ dev_info(&pdev->dev, "Using NCSI interface\n");
+ ether->use_ncsi = true;
+ ether->ncsidev = ncsi_register_dev(dev, npcm7xx_ncsi_handler);
+ if (!ether->ncsidev) {
+ error = -ENODEV;
+ goto failed_free_napi;
+ }
+ } else {
+ ether->use_ncsi = false;
+ error = npcm7xx_mii_setup(dev);
+ if (error < 0) {
+ dev_err(&pdev->dev, "npcm7xx_mii_setup err\n");
+ goto failed_free_napi;
+ }
+ }
+
+ error = register_netdev(dev);
+ if (error != 0) {
+ dev_err(&pdev->dev, "register_netdev() failed\n");
+ error = -ENODEV;
+ goto failed_free_napi;
+ }
+
+#ifdef CONFIG_DEBUG_FS
+ npcm7xx_debug_fs(ether);
+#endif
+
+ return 0;
+
+failed_free_napi:
+ netif_napi_del(&ether->napi);
+ platform_set_drvdata(pdev, NULL);
+failed_free_io:
+ iounmap(ether->reg);
+failed_free_mem:
+ release_mem_region(ether->res->start, resource_size(ether->res));
+failed_free:
+ free_netdev(dev);
+
+ return error;
+}
+
+static int npcm7xx_ether_remove(struct platform_device *pdev)
+{
+ struct net_device *dev = platform_get_drvdata(pdev);
+ struct npcm7xx_ether *ether = netdev_priv(dev);
+
+#ifdef CONFIG_DEBUG_FS
+ debugfs_remove_recursive(ether->dbgfs_dir);
+#endif
+
+ unregister_netdev(dev);
+
+ free_irq(ether->txirq, dev);
+ free_irq(ether->rxirq, dev);
+
+ if (ether->phy_dev)
+ phy_disconnect(ether->phy_dev);
+
+ mdiobus_unregister(ether->mii_bus);
+ kfree(ether->mii_bus->irq);
+ mdiobus_free(ether->mii_bus);
+
+ platform_set_drvdata(pdev, NULL);
+
+ free_netdev(dev);
+ return 0;
+}
+
+static struct platform_driver npcm7xx_ether_driver = {
+ .probe = npcm7xx_ether_probe,
+ .remove = npcm7xx_ether_remove,
+ .driver = {
+ .name = DRV_MODULE_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(emc_dt_id),
+ },
+};
+
+module_platform_driver(npcm7xx_ether_driver);
+
+MODULE_AUTHOR("Nuvoton Technology Corp.");
+MODULE_DESCRIPTION("NPCM750 EMC driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:npcm750-emc");
+MODULE_VERSION(DRV_MODULE_VERSION);
diff --git a/drivers/peci/Kconfig b/drivers/peci/Kconfig
new file mode 100644
index 000000000000..a64fed7bb367
--- /dev/null
+++ b/drivers/peci/Kconfig
@@ -0,0 +1,37 @@
+#
+# Platform Environment Control Interface (PECI) subsystem configuration
+#
+
+menu "PECI support"
+
+config PECI
+ tristate "PECI support"
+ select CRC8
+ help
+ The Platform Environment Control Interface (PECI) is a one-wire bus
+ interface that provides a communication channel from Intel processors
+ and chipset components to external monitoring or control devices.
+
+ If you want PECI support, you should say Y here and also to the
+ specific driver for your bus adapter(s) below.
+
+ This support is also available as a module. If so, the module
+ will be called peci-core.
+
+if PECI
+
+config PECI_CHARDEV
+ tristate "PECI device interface"
+ help
+ Say Y here to use peci-* device files, usually found in the /dev
+ directory on your system. They make it possible to have user-space
+ programs use the PECI bus.
+
+ This support is also available as a module. If so, the module
+ will be called peci-dev.
+
+source "drivers/peci/busses/Kconfig"
+
+endif # PECI
+
+endmenu
diff --git a/drivers/peci/Makefile b/drivers/peci/Makefile
new file mode 100644
index 000000000000..da8b0a33fa42
--- /dev/null
+++ b/drivers/peci/Makefile
@@ -0,0 +1,11 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile for the PECI core drivers.
+#
+
+# Core functionality
+obj-$(CONFIG_PECI) += peci-core.o
+obj-$(CONFIG_PECI_CHARDEV) += peci-dev.o
+
+# Hardware specific bus drivers
+obj-y += busses/
diff --git a/drivers/peci/busses/Kconfig b/drivers/peci/busses/Kconfig
new file mode 100644
index 000000000000..4316234db67c
--- /dev/null
+++ b/drivers/peci/busses/Kconfig
@@ -0,0 +1,34 @@
+#
+# PECI hardware bus configuration
+#
+
+menu "PECI Hardware Bus support"
+
+config PECI_ASPEED
+ tristate "ASPEED PECI support"
+ depends on ARCH_ASPEED || COMPILE_TEST
+ depends on OF
+ depends on HAS_IOMEM
+ depends on PECI
+ help
+ Say Y here if you want support for the Platform Environment Control
+ Interface (PECI) bus adapter driver on the ASPEED SoCs.
+
+ This support is also available as a module. If so, the module
+ will be called peci-aspeed.
+
+config PECI_NPCM
+ tristate "Nuvoton NPCM PECI support"
+ select REGMAP_MMIO
+ depends on OF
+ depends on HAS_IOMEM
+ depends on ARCH_NPCM || COMPILE_TEST
+ depends on PECI
+ help
+ Say Y here if you want support for the Platform Environment Control
+ Interface (PECI) bus adapter driver on the Nuvoton NPCM SoCs.
+
+ This support is also available as a module. If so, the module
+ will be called peci-npcm.
+
+endmenu
diff --git a/drivers/peci/busses/Makefile b/drivers/peci/busses/Makefile
new file mode 100644
index 000000000000..aa8ce3ae5947
--- /dev/null
+++ b/drivers/peci/busses/Makefile
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile for the PECI hardware bus drivers.
+#
+
+obj-$(CONFIG_PECI_ASPEED) += peci-aspeed.o
+obj-$(CONFIG_PECI_NPCM) += peci-npcm.o
diff --git a/drivers/peci/busses/peci-aspeed.c b/drivers/peci/busses/peci-aspeed.c
new file mode 100644
index 000000000000..2673d4c4dcf9
--- /dev/null
+++ b/drivers/peci/busses/peci-aspeed.c
@@ -0,0 +1,484 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2012-2017 ASPEED Technology Inc.
+// Copyright (c) 2018-2019 Intel Corporation
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/jiffies.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/peci.h>
+#include <linux/platform_device.h>
+#include <linux/reset.h>
+
+/* ASPEED PECI Registers */
+/* Control Register */
+#define ASPEED_PECI_CTRL 0x00
+#define ASPEED_PECI_CTRL_SAMPLING_MASK GENMASK(19, 16)
+#define ASPEED_PECI_CTRL_READ_MODE_MASK GENMASK(13, 12)
+#define ASPEED_PECI_CTRL_READ_MODE_COUNT BIT(12)
+#define ASPEED_PECI_CTRL_READ_MODE_DBG BIT(13)
+#define ASPEED_PECI_CTRL_CLK_SOURCE_MASK BIT(11)
+#define ASPEED_PECI_CTRL_CLK_DIV_MASK GENMASK(10, 8)
+#define ASPEED_PECI_CTRL_INVERT_OUT BIT(7)
+#define ASPEED_PECI_CTRL_INVERT_IN BIT(6)
+#define ASPEED_PECI_CTRL_BUS_CONTENT_EN BIT(5)
+#define ASPEED_PECI_CTRL_PECI_EN BIT(4)
+#define ASPEED_PECI_CTRL_PECI_CLK_EN BIT(0)
+
+/* Timing Negotiation Register */
+#define ASPEED_PECI_TIMING_NEGOTIATION 0x04
+#define ASPEED_PECI_TIMING_MESSAGE_MASK GENMASK(15, 8)
+#define ASPEED_PECI_TIMING_ADDRESS_MASK GENMASK(7, 0)
+
+/* Command Register */
+#define ASPEED_PECI_CMD 0x08
+#define ASPEED_PECI_CMD_PIN_MON BIT(31)
+#define ASPEED_PECI_CMD_STS_MASK GENMASK(27, 24)
+#define ASPEED_PECI_CMD_IDLE_MASK \
+ (ASPEED_PECI_CMD_STS_MASK | ASPEED_PECI_CMD_PIN_MON)
+#define ASPEED_PECI_CMD_FIRE BIT(0)
+
+/* Read/Write Length Register */
+#define ASPEED_PECI_RW_LENGTH 0x0c
+#define ASPEED_PECI_AW_FCS_EN BIT(31)
+#define ASPEED_PECI_READ_LEN_MASK GENMASK(23, 16)
+#define ASPEED_PECI_WRITE_LEN_MASK GENMASK(15, 8)
+#define ASPEED_PECI_TAGET_ADDR_MASK GENMASK(7, 0)
+
+/* Expected FCS Data Register */
+#define ASPEED_PECI_EXP_FCS 0x10
+#define ASPEED_PECI_EXP_READ_FCS_MASK GENMASK(23, 16)
+#define ASPEED_PECI_EXP_AW_FCS_AUTO_MASK GENMASK(15, 8)
+#define ASPEED_PECI_EXP_WRITE_FCS_MASK GENMASK(7, 0)
+
+/* Captured FCS Data Register */
+#define ASPEED_PECI_CAP_FCS 0x14
+#define ASPEED_PECI_CAP_READ_FCS_MASK GENMASK(23, 16)
+#define ASPEED_PECI_CAP_WRITE_FCS_MASK GENMASK(7, 0)
+
+/* Interrupt Register */
+#define ASPEED_PECI_INT_CTRL 0x18
+#define ASPEED_PECI_TIMING_NEGO_SEL_MASK GENMASK(31, 30)
+#define ASPEED_PECI_1ST_BIT_OF_ADDR_NEGO 0
+#define ASPEED_PECI_2ND_BIT_OF_ADDR_NEGO 1
+#define ASPEED_PECI_MESSAGE_NEGO 2
+#define ASPEED_PECI_INT_MASK GENMASK(4, 0)
+#define ASPEED_PECI_INT_BUS_TIMEOUT BIT(4)
+#define ASPEED_PECI_INT_BUS_CONNECT BIT(3)
+#define ASPEED_PECI_INT_W_FCS_BAD BIT(2)
+#define ASPEED_PECI_INT_W_FCS_ABORT BIT(1)
+#define ASPEED_PECI_INT_CMD_DONE BIT(0)
+
+/* Interrupt Status Register */
+#define ASPEED_PECI_INT_STS 0x1c
+#define ASPEED_PECI_INT_TIMING_RESULT_MASK GENMASK(29, 16)
+ /* bits[4..0]: Same bit fields in the 'Interrupt Register' */
+
+/* Rx/Tx Data Buffer Registers */
+#define ASPEED_PECI_W_DATA0 0x20
+#define ASPEED_PECI_W_DATA1 0x24
+#define ASPEED_PECI_W_DATA2 0x28
+#define ASPEED_PECI_W_DATA3 0x2c
+#define ASPEED_PECI_R_DATA0 0x30
+#define ASPEED_PECI_R_DATA1 0x34
+#define ASPEED_PECI_R_DATA2 0x38
+#define ASPEED_PECI_R_DATA3 0x3c
+#define ASPEED_PECI_W_DATA4 0x40
+#define ASPEED_PECI_W_DATA5 0x44
+#define ASPEED_PECI_W_DATA6 0x48
+#define ASPEED_PECI_W_DATA7 0x4c
+#define ASPEED_PECI_R_DATA4 0x50
+#define ASPEED_PECI_R_DATA5 0x54
+#define ASPEED_PECI_R_DATA6 0x58
+#define ASPEED_PECI_R_DATA7 0x5c
+#define ASPEED_PECI_DATA_BUF_SIZE_MAX 32
+
+/* Timing Negotiation */
+#define ASPEED_PECI_RD_SAMPLING_POINT_DEFAULT 8
+#define ASPEED_PECI_RD_SAMPLING_POINT_MAX 15
+#define ASPEED_PECI_CLK_DIV_DEFAULT 0
+#define ASPEED_PECI_CLK_DIV_MAX 7
+#define ASPEED_PECI_MSG_TIMING_DEFAULT 1
+#define ASPEED_PECI_MSG_TIMING_MAX 255
+#define ASPEED_PECI_ADDR_TIMING_DEFAULT 1
+#define ASPEED_PECI_ADDR_TIMING_MAX 255
+
+/* Timeout */
+#define ASPEED_PECI_IDLE_CHECK_TIMEOUT_USEC 50000
+#define ASPEED_PECI_IDLE_CHECK_INTERVAL_USEC 10000
+#define ASPEED_PECI_CMD_TIMEOUT_MS_DEFAULT 1000
+#define ASPEED_PECI_CMD_TIMEOUT_MS_MAX 60000
+
+struct aspeed_peci {
+ struct peci_adapter *adapter;
+ struct device *dev;
+ void __iomem *base;
+ struct clk *clk;
+ struct reset_control *rst;
+ int irq;
+ spinlock_t lock; /* to sync completion status handling */
+ struct completion xfer_complete;
+ u32 status;
+ u32 cmd_timeout_ms;
+};
+
+static inline int aspeed_peci_check_idle(struct aspeed_peci *priv)
+{
+ u32 cmd_sts;
+
+ return readl_poll_timeout(priv->base + ASPEED_PECI_CMD,
+ cmd_sts,
+ !(cmd_sts & ASPEED_PECI_CMD_IDLE_MASK),
+ ASPEED_PECI_IDLE_CHECK_INTERVAL_USEC,
+ ASPEED_PECI_IDLE_CHECK_TIMEOUT_USEC);
+}
+
+static int aspeed_peci_xfer(struct peci_adapter *adapter,
+ struct peci_xfer_msg *msg)
+{
+ struct aspeed_peci *priv = peci_get_adapdata(adapter);
+ long err, timeout = msecs_to_jiffies(priv->cmd_timeout_ms);
+ u32 peci_head, peci_state, rx_data = 0;
+ ulong flags;
+ int i, ret;
+ uint reg;
+
+ if (msg->tx_len > ASPEED_PECI_DATA_BUF_SIZE_MAX ||
+ msg->rx_len > ASPEED_PECI_DATA_BUF_SIZE_MAX)
+ return -EINVAL;
+
+ /* Check command sts and bus idle state */
+ ret = aspeed_peci_check_idle(priv);
+ if (ret)
+ return ret; /* -ETIMEDOUT */
+
+ spin_lock_irqsave(&priv->lock, flags);
+ reinit_completion(&priv->xfer_complete);
+
+ peci_head = FIELD_PREP(ASPEED_PECI_TAGET_ADDR_MASK, msg->addr) |
+ FIELD_PREP(ASPEED_PECI_WRITE_LEN_MASK, msg->tx_len) |
+ FIELD_PREP(ASPEED_PECI_READ_LEN_MASK, msg->rx_len);
+
+ writel(peci_head, priv->base + ASPEED_PECI_RW_LENGTH);
+
+ for (i = 0; i < msg->tx_len; i += 4) {
+ reg = i < 16 ? ASPEED_PECI_W_DATA0 + i % 16 :
+ ASPEED_PECI_W_DATA4 + i % 16;
+ writel(le32_to_cpup((__le32 *)&msg->tx_buf[i]),
+ priv->base + reg);
+ }
+
+ dev_dbg(priv->dev, "HEAD : 0x%08x\n", peci_head);
+ print_hex_dump_debug("TX : ", DUMP_PREFIX_NONE, 16, 1,
+ msg->tx_buf, msg->tx_len, true);
+
+ priv->status = 0;
+ writel(ASPEED_PECI_CMD_FIRE, priv->base + ASPEED_PECI_CMD);
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ err = wait_for_completion_interruptible_timeout(&priv->xfer_complete,
+ timeout);
+
+ spin_lock_irqsave(&priv->lock, flags);
+ dev_dbg(priv->dev, "INT_STS : 0x%08x\n", priv->status);
+ peci_state = readl(priv->base + ASPEED_PECI_CMD);
+ dev_dbg(priv->dev, "PECI_STATE : 0x%lx\n",
+ FIELD_GET(ASPEED_PECI_CMD_STS_MASK, peci_state));
+
+ writel(0, priv->base + ASPEED_PECI_CMD);
+
+ if (err <= 0 || priv->status != ASPEED_PECI_INT_CMD_DONE) {
+ if (err < 0) { /* -ERESTARTSYS */
+ ret = (int)err;
+ goto err_irqrestore;
+ } else if (err == 0) {
+ dev_dbg(priv->dev, "Timeout waiting for a response!\n");
+ ret = -ETIMEDOUT;
+ goto err_irqrestore;
+ }
+
+ dev_dbg(priv->dev, "No valid response!\n");
+ ret = -EIO;
+ goto err_irqrestore;
+ }
+
+ /*
+ * Note that rx_len and rx_buf size can be an odd number.
+ * Byte handling is more efficient.
+ */
+ for (i = 0; i < msg->rx_len; i++) {
+ u8 byte_offset = i % 4;
+
+ if (byte_offset == 0) {
+ reg = i < 16 ? ASPEED_PECI_R_DATA0 + i % 16 :
+ ASPEED_PECI_R_DATA4 + i % 16;
+ rx_data = readl(priv->base + reg);
+ }
+
+ msg->rx_buf[i] = (u8)(rx_data >> (byte_offset << 3));
+ }
+
+ print_hex_dump_debug("RX : ", DUMP_PREFIX_NONE, 16, 1,
+ msg->rx_buf, msg->rx_len, true);
+
+ peci_state = readl(priv->base + ASPEED_PECI_CMD);
+ dev_dbg(priv->dev, "PECI_STATE : 0x%lx\n",
+ FIELD_GET(ASPEED_PECI_CMD_STS_MASK, peci_state));
+ dev_dbg(priv->dev, "------------------------\n");
+
+err_irqrestore:
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ return ret;
+}
+
+static irqreturn_t aspeed_peci_irq_handler(int irq, void *arg)
+{
+ struct aspeed_peci *priv = arg;
+ u32 status;
+
+ spin_lock(&priv->lock);
+ status = readl(priv->base + ASPEED_PECI_INT_STS);
+ writel(status, priv->base + ASPEED_PECI_INT_STS);
+ priv->status |= (status & ASPEED_PECI_INT_MASK);
+
+ /*
+ * In most cases, interrupt bits will be set one by one but also note
+ * that multiple interrupt bits could be set at the same time.
+ */
+ if (status & ASPEED_PECI_INT_BUS_TIMEOUT)
+ dev_dbg(priv->dev, "ASPEED_PECI_INT_BUS_TIMEOUT\n");
+
+ if (status & ASPEED_PECI_INT_BUS_CONNECT)
+ dev_dbg(priv->dev, "ASPEED_PECI_INT_BUS_CONNECT\n");
+
+ if (status & ASPEED_PECI_INT_W_FCS_BAD)
+ dev_dbg(priv->dev, "ASPEED_PECI_INT_W_FCS_BAD\n");
+
+ if (status & ASPEED_PECI_INT_W_FCS_ABORT)
+ dev_dbg(priv->dev, "ASPEED_PECI_INT_W_FCS_ABORT\n");
+
+ /*
+ * All commands should be ended up with a ASPEED_PECI_INT_CMD_DONE bit
+ * set even in an error case.
+ */
+ if (status & ASPEED_PECI_INT_CMD_DONE) {
+ dev_dbg(priv->dev, "ASPEED_PECI_INT_CMD_DONE\n");
+ complete(&priv->xfer_complete);
+ }
+
+ spin_unlock(&priv->lock);
+
+ return IRQ_HANDLED;
+}
+
+static int aspeed_peci_init_ctrl(struct aspeed_peci *priv)
+{
+ u32 msg_timing, addr_timing, rd_sampling_point;
+ u32 clk_freq, clk_divisor, clk_div_val = 0;
+ int ret;
+
+ priv->clk = devm_clk_get(priv->dev, NULL);
+ if (IS_ERR(priv->clk)) {
+ dev_err(priv->dev, "Failed to get clk source.\n");
+ return PTR_ERR(priv->clk);
+ }
+
+ ret = clk_prepare_enable(priv->clk);
+ if (ret) {
+ dev_err(priv->dev, "Failed to enable clock.\n");
+ return ret;
+ }
+
+ ret = device_property_read_u32(priv->dev, "clock-frequency", &clk_freq);
+ if (ret) {
+ dev_err(priv->dev,
+ "Could not read clock-frequency property.\n");
+ clk_disable_unprepare(priv->clk);
+ return ret;
+ }
+
+ clk_divisor = clk_get_rate(priv->clk) / clk_freq;
+
+ while ((clk_divisor >> 1) && (clk_div_val < ASPEED_PECI_CLK_DIV_MAX))
+ clk_div_val++;
+
+ ret = device_property_read_u32(priv->dev, "msg-timing", &msg_timing);
+ if (ret || msg_timing > ASPEED_PECI_MSG_TIMING_MAX) {
+ if (!ret)
+ dev_warn(priv->dev,
+ "Invalid msg-timing : %u, Use default : %u\n",
+ msg_timing, ASPEED_PECI_MSG_TIMING_DEFAULT);
+ msg_timing = ASPEED_PECI_MSG_TIMING_DEFAULT;
+ }
+
+ ret = device_property_read_u32(priv->dev, "addr-timing", &addr_timing);
+ if (ret || addr_timing > ASPEED_PECI_ADDR_TIMING_MAX) {
+ if (!ret)
+ dev_warn(priv->dev,
+ "Invalid addr-timing : %u, Use default : %u\n",
+ addr_timing, ASPEED_PECI_ADDR_TIMING_DEFAULT);
+ addr_timing = ASPEED_PECI_ADDR_TIMING_DEFAULT;
+ }
+
+ ret = device_property_read_u32(priv->dev, "rd-sampling-point",
+ &rd_sampling_point);
+ if (ret || rd_sampling_point > ASPEED_PECI_RD_SAMPLING_POINT_MAX) {
+ if (!ret)
+ dev_warn(priv->dev,
+ "Invalid rd-sampling-point : %u. Use default : %u\n",
+ rd_sampling_point,
+ ASPEED_PECI_RD_SAMPLING_POINT_DEFAULT);
+ rd_sampling_point = ASPEED_PECI_RD_SAMPLING_POINT_DEFAULT;
+ }
+
+ ret = device_property_read_u32(priv->dev, "cmd-timeout-ms",
+ &priv->cmd_timeout_ms);
+ if (ret || priv->cmd_timeout_ms > ASPEED_PECI_CMD_TIMEOUT_MS_MAX ||
+ priv->cmd_timeout_ms == 0) {
+ if (!ret)
+ dev_warn(priv->dev,
+ "Invalid cmd-timeout-ms : %u. Use default : %u\n",
+ priv->cmd_timeout_ms,
+ ASPEED_PECI_CMD_TIMEOUT_MS_DEFAULT);
+ priv->cmd_timeout_ms = ASPEED_PECI_CMD_TIMEOUT_MS_DEFAULT;
+ }
+
+ writel(FIELD_PREP(ASPEED_PECI_CTRL_CLK_DIV_MASK,
+ ASPEED_PECI_CLK_DIV_DEFAULT) |
+ ASPEED_PECI_CTRL_PECI_CLK_EN, priv->base + ASPEED_PECI_CTRL);
+
+ /*
+ * Timing negotiation period setting.
+ * The unit of the programmed value is 4 times of PECI clock period.
+ */
+ writel(FIELD_PREP(ASPEED_PECI_TIMING_MESSAGE_MASK, msg_timing) |
+ FIELD_PREP(ASPEED_PECI_TIMING_ADDRESS_MASK, addr_timing),
+ priv->base + ASPEED_PECI_TIMING_NEGOTIATION);
+
+ /* Clear interrupts */
+ writel(readl(priv->base + ASPEED_PECI_INT_STS) | ASPEED_PECI_INT_MASK,
+ priv->base + ASPEED_PECI_INT_STS);
+
+ /* Set timing negotiation mode and enable interrupts */
+ writel(FIELD_PREP(ASPEED_PECI_TIMING_NEGO_SEL_MASK,
+ ASPEED_PECI_1ST_BIT_OF_ADDR_NEGO) |
+ ASPEED_PECI_INT_MASK, priv->base + ASPEED_PECI_INT_CTRL);
+
+ /* Read sampling point and clock speed setting */
+ writel(FIELD_PREP(ASPEED_PECI_CTRL_SAMPLING_MASK, rd_sampling_point) |
+ FIELD_PREP(ASPEED_PECI_CTRL_CLK_DIV_MASK, clk_div_val) |
+ ASPEED_PECI_CTRL_PECI_EN | ASPEED_PECI_CTRL_PECI_CLK_EN,
+ priv->base + ASPEED_PECI_CTRL);
+
+ return 0;
+}
+
+static int aspeed_peci_probe(struct platform_device *pdev)
+{
+ struct peci_adapter *adapter;
+ struct aspeed_peci *priv;
+ int ret;
+
+ adapter = peci_alloc_adapter(&pdev->dev, sizeof(*priv));
+ if (!adapter)
+ return -ENOMEM;
+
+ priv = peci_get_adapdata(adapter);
+ priv->adapter = adapter;
+ priv->dev = &pdev->dev;
+ dev_set_drvdata(&pdev->dev, priv);
+
+ priv->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(priv->base)) {
+ ret = PTR_ERR(priv->base);
+ goto err_put_adapter_dev;
+ }
+
+ priv->irq = platform_get_irq(pdev, 0);
+ if (!priv->irq) {
+ ret = -ENODEV;
+ goto err_put_adapter_dev;
+ }
+
+ ret = devm_request_irq(&pdev->dev, priv->irq, aspeed_peci_irq_handler,
+ 0, "peci-aspeed-irq", priv);
+ if (ret)
+ goto err_put_adapter_dev;
+
+ init_completion(&priv->xfer_complete);
+ spin_lock_init(&priv->lock);
+
+ priv->adapter->owner = THIS_MODULE;
+ priv->adapter->dev.of_node = of_node_get(dev_of_node(priv->dev));
+ strlcpy(priv->adapter->name, pdev->name, sizeof(priv->adapter->name));
+ priv->adapter->xfer = aspeed_peci_xfer;
+ priv->adapter->use_dma = false;
+
+ priv->rst = devm_reset_control_get(&pdev->dev, NULL);
+ if (IS_ERR(priv->rst)) {
+ dev_err(&pdev->dev,
+ "missing or invalid reset controller entry\n");
+ ret = PTR_ERR(priv->rst);
+ goto err_put_adapter_dev;
+ }
+ reset_control_deassert(priv->rst);
+
+ ret = aspeed_peci_init_ctrl(priv);
+ if (ret)
+ goto err_put_adapter_dev;
+
+ ret = peci_add_adapter(priv->adapter);
+ if (ret)
+ goto err_put_adapter_dev;
+
+ dev_info(&pdev->dev, "peci bus %d registered, irq %d\n",
+ priv->adapter->nr, priv->irq);
+
+ return 0;
+
+err_put_adapter_dev:
+ put_device(&adapter->dev);
+
+ return ret;
+}
+
+static int aspeed_peci_remove(struct platform_device *pdev)
+{
+ struct aspeed_peci *priv = dev_get_drvdata(&pdev->dev);
+
+ clk_disable_unprepare(priv->clk);
+ reset_control_assert(priv->rst);
+ peci_del_adapter(priv->adapter);
+ of_node_put(priv->adapter->dev.of_node);
+
+ return 0;
+}
+
+static const struct of_device_id aspeed_peci_of_table[] = {
+ { .compatible = "aspeed,ast2400-peci", },
+ { .compatible = "aspeed,ast2500-peci", },
+ { .compatible = "aspeed,ast2600-peci", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, aspeed_peci_of_table);
+
+static struct platform_driver aspeed_peci_driver = {
+ .probe = aspeed_peci_probe,
+ .remove = aspeed_peci_remove,
+ .driver = {
+ .name = KBUILD_MODNAME,
+ .of_match_table = of_match_ptr(aspeed_peci_of_table),
+ },
+};
+module_platform_driver(aspeed_peci_driver);
+
+MODULE_AUTHOR("Ryan Chen <ryan_chen@aspeedtech.com>");
+MODULE_AUTHOR("Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>");
+MODULE_DESCRIPTION("ASPEED PECI driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/peci/busses/peci-npcm.c b/drivers/peci/busses/peci-npcm.c
new file mode 100644
index 000000000000..bdebbf1ec7f1
--- /dev/null
+++ b/drivers/peci/busses/peci-npcm.c
@@ -0,0 +1,406 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2019 Nuvoton Technology corporation.
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/jiffies.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/peci.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/mfd/syscon.h>
+#include <linux/reset.h>
+
+/* NPCM7xx GCR module */
+#define NPCM7XX_INTCR3_OFFSET 0x9C
+#define NPCM7XX_INTCR3_PECIVSEL BIT(19)
+
+/* NPCM PECI Registers */
+#define NPCM_PECI_CTL_STS 0x00
+#define NPCM_PECI_RD_LENGTH 0x04
+#define NPCM_PECI_ADDR 0x08
+#define NPCM_PECI_CMD 0x0C
+#define NPCM_PECI_CTL2 0x10
+#define NPCM_PECI_WR_LENGTH 0x1C
+#define NPCM_PECI_PDDR 0x2C
+#define NPCM_PECI_DAT_INOUT(n) (0x100 + ((n) * 4))
+
+#define NPCM_PECI_MAX_REG 0x200
+
+/* NPCM_PECI_CTL_STS - 0x00 : Control Register */
+#define NPCM_PECI_CTRL_DONE_INT_EN BIT(6)
+#define NPCM_PECI_CTRL_ABRT_ERR BIT(4)
+#define NPCM_PECI_CTRL_CRC_ERR BIT(3)
+#define NPCM_PECI_CTRL_DONE BIT(1)
+#define NPCM_PECI_CTRL_START_BUSY BIT(0)
+
+/* NPCM_PECI_RD_LENGTH - 0x04 : Command Register */
+#define NPCM_PECI_RD_LEN_MASK GENMASK(6, 0)
+
+/* NPCM_PECI_CMD - 0x10 : Command Register */
+#define NPCM_PECI_CTL2_MASK GENMASK(7, 6)
+
+/* NPCM_PECI_WR_LENGTH - 0x1C : Command Register */
+#define NPCM_PECI_WR_LEN_MASK GENMASK(6, 0)
+
+/* NPCM_PECI_PDDR - 0x2C : Command Register */
+#define NPCM_PECI_PDDR_MASK GENMASK(4, 0)
+
+#define NPCM_PECI_INT_MASK \
+ (NPCM_PECI_CTRL_ABRT_ERR | NPCM_PECI_CTRL_CRC_ERR | NPCM_PECI_CTRL_DONE)
+
+#define NPCM_PECI_IDLE_CHECK_TIMEOUT_USEC 50000
+#define NPCM_PECI_IDLE_CHECK_INTERVAL_USEC 10000
+#define NPCM_PECI_CMD_TIMEOUT_MS_DEFAULT 1000
+#define NPCM_PECI_CMD_TIMEOUT_MS_MAX 60000
+#define NPCM_PECI_HOST_NEG_BIT_RATE_MAX 31
+#define NPCM_PECI_HOST_NEG_BIT_RATE_MIN 7
+#define NPCM_PECI_HOST_NEG_BIT_RATE_DEFAULT 15
+#define NPCM_PECI_PULL_DOWN_DEFAULT 0
+#define NPCM_PECI_PULL_DOWN_MAX 2
+
+struct npcm_peci {
+ u32 cmd_timeout_ms;
+ u32 host_bit_rate;
+ struct completion xfer_complete;
+ struct regmap *gcr_regmap;
+ struct peci_adapter *adapter;
+ struct regmap *regmap;
+ u32 status;
+ spinlock_t lock; /* to sync completion status handling */
+ struct device *dev;
+ struct clk *clk;
+ int irq;
+};
+
+static int npcm_peci_xfer_native(struct npcm_peci *priv,
+ struct peci_xfer_msg *msg)
+{
+ long err, timeout = msecs_to_jiffies(priv->cmd_timeout_ms);
+ unsigned long flags;
+ unsigned int msg_rd;
+ u32 cmd_sts;
+ int i, rc;
+
+ /* Check command sts and bus idle state */
+ rc = regmap_read_poll_timeout(priv->regmap, NPCM_PECI_CTL_STS, cmd_sts,
+ !(cmd_sts & NPCM_PECI_CTRL_START_BUSY),
+ NPCM_PECI_IDLE_CHECK_INTERVAL_USEC,
+ NPCM_PECI_IDLE_CHECK_TIMEOUT_USEC);
+ if (rc)
+ return rc; /* -ETIMEDOUT */
+
+ spin_lock_irqsave(&priv->lock, flags);
+ reinit_completion(&priv->xfer_complete);
+
+ regmap_write(priv->regmap, NPCM_PECI_ADDR, msg->addr);
+ regmap_write(priv->regmap, NPCM_PECI_RD_LENGTH,
+ NPCM_PECI_WR_LEN_MASK & msg->rx_len);
+ regmap_write(priv->regmap, NPCM_PECI_WR_LENGTH,
+ NPCM_PECI_WR_LEN_MASK & msg->tx_len);
+
+ if (msg->tx_len) {
+ regmap_write(priv->regmap, NPCM_PECI_CMD, msg->tx_buf[0]);
+
+ for (i = 0; i < (msg->tx_len - 1); i++)
+ regmap_write(priv->regmap, NPCM_PECI_DAT_INOUT(i),
+ msg->tx_buf[i + 1]);
+ }
+
+ priv->status = 0;
+ regmap_update_bits(priv->regmap, NPCM_PECI_CTL_STS,
+ NPCM_PECI_CTRL_START_BUSY,
+ NPCM_PECI_CTRL_START_BUSY);
+
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ err = wait_for_completion_interruptible_timeout(&priv->xfer_complete,
+ timeout);
+
+ spin_lock_irqsave(&priv->lock, flags);
+
+ regmap_write(priv->regmap, NPCM_PECI_CMD, 0);
+
+ if (err <= 0 || priv->status != NPCM_PECI_CTRL_DONE) {
+ if (err < 0) { /* -ERESTARTSYS */
+ rc = (int)err;
+ goto err_irqrestore;
+ } else if (err == 0) {
+ dev_dbg(priv->dev, "Timeout waiting for a response!\n");
+ rc = -ETIMEDOUT;
+ goto err_irqrestore;
+ }
+
+ dev_dbg(priv->dev, "No valid response!\n");
+ rc = -EIO;
+ goto err_irqrestore;
+ }
+
+ for (i = 0; i < msg->rx_len; i++) {
+ regmap_read(priv->regmap, NPCM_PECI_DAT_INOUT(i), &msg_rd);
+ msg->rx_buf[i] = (u8)msg_rd;
+ }
+
+err_irqrestore:
+ spin_unlock_irqrestore(&priv->lock, flags);
+ return rc;
+}
+
+static irqreturn_t npcm_peci_irq_handler(int irq, void *arg)
+{
+ struct npcm_peci *priv = arg;
+ u32 status_ack = 0;
+ u32 status;
+
+ spin_lock(&priv->lock);
+ regmap_read(priv->regmap, NPCM_PECI_CTL_STS, &status);
+ priv->status |= (status & NPCM_PECI_INT_MASK);
+
+ if (status & NPCM_PECI_CTRL_CRC_ERR) {
+ dev_dbg(priv->dev, "PECI_INT_W_FCS_BAD\n");
+ status_ack |= NPCM_PECI_CTRL_CRC_ERR;
+ }
+
+ if (status & NPCM_PECI_CTRL_ABRT_ERR) {
+ dev_dbg(priv->dev, "NPCM_PECI_CTRL_ABRT_ERR\n");
+ status_ack |= NPCM_PECI_CTRL_ABRT_ERR;
+ }
+
+ /*
+ * All commands should be ended up with a NPCM_PECI_CTRL_DONE
+ * bit set even in an error case.
+ */
+ if (status & NPCM_PECI_CTRL_DONE) {
+ dev_dbg(priv->dev, "NPCM_PECI_CTRL_DONE\n");
+ status_ack |= NPCM_PECI_CTRL_DONE;
+ complete(&priv->xfer_complete);
+ }
+
+ regmap_write_bits(priv->regmap, NPCM_PECI_CTL_STS,
+ NPCM_PECI_INT_MASK, status_ack);
+
+ spin_unlock(&priv->lock);
+ return IRQ_HANDLED;
+}
+
+static int npcm_peci_init_ctrl(struct npcm_peci *priv)
+{
+ u32 cmd_sts, host_neg_bit_rate = 0, pull_down = 0;
+ int ret;
+
+ priv->clk = devm_clk_get(priv->dev, NULL);
+ if (IS_ERR(priv->clk)) {
+ dev_err(priv->dev, "Failed to get clk source.\n");
+ return PTR_ERR(priv->clk);
+ }
+
+ ret = clk_prepare_enable(priv->clk);
+ if (ret) {
+ dev_err(priv->dev, "Failed to enable clock.\n");
+ return ret;
+ }
+
+ ret = of_property_read_u32(priv->dev->of_node, "cmd-timeout-ms",
+ &priv->cmd_timeout_ms);
+ if (ret || priv->cmd_timeout_ms > NPCM_PECI_CMD_TIMEOUT_MS_MAX ||
+ priv->cmd_timeout_ms == 0) {
+ if (ret)
+ dev_warn(priv->dev,
+ "cmd-timeout-ms not found, use default : %u\n",
+ NPCM_PECI_CMD_TIMEOUT_MS_DEFAULT);
+ else
+ dev_warn(priv->dev,
+ "Invalid cmd-timeout-ms : %u. Use default : %u\n",
+ priv->cmd_timeout_ms,
+ NPCM_PECI_CMD_TIMEOUT_MS_DEFAULT);
+
+ priv->cmd_timeout_ms = NPCM_PECI_CMD_TIMEOUT_MS_DEFAULT;
+ }
+
+ if (of_device_is_compatible(priv->dev->of_node,
+ "nuvoton,npcm750-peci")) {
+ priv->gcr_regmap = syscon_regmap_lookup_by_compatible
+ ("nuvoton,npcm750-gcr");
+ if (!IS_ERR(priv->gcr_regmap)) {
+ bool volt = of_property_read_bool(priv->dev->of_node,
+ "high-volt-range");
+ if (volt)
+ regmap_update_bits(priv->gcr_regmap,
+ NPCM7XX_INTCR3_OFFSET,
+ NPCM7XX_INTCR3_PECIVSEL,
+ NPCM7XX_INTCR3_PECIVSEL);
+ else
+ regmap_update_bits(priv->gcr_regmap,
+ NPCM7XX_INTCR3_OFFSET,
+ NPCM7XX_INTCR3_PECIVSEL, 0);
+ }
+ }
+
+ ret = of_property_read_u32(priv->dev->of_node, "pull-down",
+ &pull_down);
+ if (ret || pull_down > NPCM_PECI_PULL_DOWN_MAX) {
+ if (ret)
+ dev_warn(priv->dev,
+ "pull-down not found, use default : %u\n",
+ NPCM_PECI_PULL_DOWN_DEFAULT);
+ else
+ dev_warn(priv->dev,
+ "Invalid pull-down : %u. Use default : %u\n",
+ pull_down,
+ NPCM_PECI_PULL_DOWN_DEFAULT);
+ pull_down = NPCM_PECI_PULL_DOWN_DEFAULT;
+ }
+
+ regmap_update_bits(priv->regmap, NPCM_PECI_CTL2, NPCM_PECI_CTL2_MASK,
+ pull_down << 6);
+
+ ret = of_property_read_u32(priv->dev->of_node, "host-neg-bit-rate",
+ &host_neg_bit_rate);
+ if (ret || host_neg_bit_rate > NPCM_PECI_HOST_NEG_BIT_RATE_MAX ||
+ host_neg_bit_rate < NPCM_PECI_HOST_NEG_BIT_RATE_MIN) {
+ if (ret)
+ dev_warn(priv->dev,
+ "host-neg-bit-rate not found, use default : %u\n",
+ NPCM_PECI_HOST_NEG_BIT_RATE_DEFAULT);
+ else
+ dev_warn(priv->dev,
+ "Invalid host-neg-bit-rate : %u. Use default : %u\n",
+ host_neg_bit_rate,
+ NPCM_PECI_HOST_NEG_BIT_RATE_DEFAULT);
+ host_neg_bit_rate = NPCM_PECI_HOST_NEG_BIT_RATE_DEFAULT;
+ }
+
+ regmap_update_bits(priv->regmap, NPCM_PECI_PDDR, NPCM_PECI_PDDR_MASK,
+ host_neg_bit_rate);
+
+ priv->host_bit_rate = clk_get_rate(priv->clk) /
+ (4 * (host_neg_bit_rate + 1));
+
+ ret = regmap_read_poll_timeout(priv->regmap, NPCM_PECI_CTL_STS, cmd_sts,
+ !(cmd_sts & NPCM_PECI_CTRL_START_BUSY),
+ NPCM_PECI_IDLE_CHECK_INTERVAL_USEC,
+ NPCM_PECI_IDLE_CHECK_TIMEOUT_USEC);
+ if (ret)
+ return ret; /* -ETIMEDOUT */
+
+ /* PECI interrupt enable */
+ regmap_update_bits(priv->regmap, NPCM_PECI_CTL_STS,
+ NPCM_PECI_CTRL_DONE_INT_EN,
+ NPCM_PECI_CTRL_DONE_INT_EN);
+
+ return 0;
+}
+
+static const struct regmap_config npcm_peci_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = NPCM_PECI_MAX_REG,
+ .fast_io = true,
+};
+
+static int npcm_peci_xfer(struct peci_adapter *adapter,
+ struct peci_xfer_msg *msg)
+{
+ struct npcm_peci *priv = peci_get_adapdata(adapter);
+
+ return npcm_peci_xfer_native(priv, msg);
+}
+
+static int npcm_peci_probe(struct platform_device *pdev)
+{
+ struct peci_adapter *adapter;
+ struct npcm_peci *priv;
+ void __iomem *base;
+ int ret;
+
+ adapter = peci_alloc_adapter(&pdev->dev, sizeof(*priv));
+ if (!adapter)
+ return -ENOMEM;
+
+ priv = peci_get_adapdata(adapter);
+ priv->adapter = adapter;
+ priv->dev = &pdev->dev;
+ dev_set_drvdata(&pdev->dev, priv);
+
+ base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(base)) {
+ ret = PTR_ERR(base);
+ goto err_put_adapter_dev;
+ }
+
+ priv->regmap = devm_regmap_init_mmio(&pdev->dev, base,
+ &npcm_peci_regmap_config);
+ if (IS_ERR(priv->regmap)) {
+ ret = PTR_ERR(priv->regmap);
+ goto err_put_adapter_dev;
+ }
+
+ priv->irq = platform_get_irq(pdev, 0);
+ if (!priv->irq) {
+ ret = -ENODEV;
+ goto err_put_adapter_dev;
+ }
+
+ ret = devm_request_irq(&pdev->dev, priv->irq, npcm_peci_irq_handler,
+ 0, "peci-npcm-irq", priv);
+ if (ret)
+ goto err_put_adapter_dev;
+
+ init_completion(&priv->xfer_complete);
+ spin_lock_init(&priv->lock);
+
+ priv->adapter->owner = THIS_MODULE;
+ priv->adapter->dev.of_node = of_node_get(dev_of_node(priv->dev));
+ strlcpy(priv->adapter->name, pdev->name, sizeof(priv->adapter->name));
+ priv->adapter->xfer = npcm_peci_xfer;
+
+ ret = npcm_peci_init_ctrl(priv);
+ if (ret)
+ goto err_put_adapter_dev;
+
+ ret = peci_add_adapter(priv->adapter);
+ if (ret)
+ goto err_put_adapter_dev;
+
+ dev_info(&pdev->dev, "peci bus %d registered, host negotiation bit rate %dHz",
+ priv->adapter->nr, priv->host_bit_rate);
+
+ return 0;
+
+err_put_adapter_dev:
+ put_device(&adapter->dev);
+ return ret;
+}
+
+static int npcm_peci_remove(struct platform_device *pdev)
+{
+ struct npcm_peci *priv = dev_get_drvdata(&pdev->dev);
+
+ clk_disable_unprepare(priv->clk);
+ peci_del_adapter(priv->adapter);
+ of_node_put(priv->adapter->dev.of_node);
+
+ return 0;
+}
+
+static const struct of_device_id npcm_peci_of_table[] = {
+ { .compatible = "nuvoton,npcm750-peci", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, npcm_peci_of_table);
+
+static struct platform_driver npcm_peci_driver = {
+ .probe = npcm_peci_probe,
+ .remove = npcm_peci_remove,
+ .driver = {
+ .name = KBUILD_MODNAME,
+ .of_match_table = of_match_ptr(npcm_peci_of_table),
+ },
+};
+module_platform_driver(npcm_peci_driver);
+
+MODULE_AUTHOR("Tomer Maimon <tomer.maimon@nuvoton.com>");
+MODULE_DESCRIPTION("NPCM Platform Environment Control Interface (PECI) driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/peci/peci-core.c b/drivers/peci/peci-core.c
new file mode 100644
index 000000000000..9aedb74710e6
--- /dev/null
+++ b/drivers/peci/peci-core.c
@@ -0,0 +1,2089 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2018-2019 Intel Corporation
+
+#include <linux/bitfield.h>
+#include <linux/crc8.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/peci.h>
+#include <linux/pm_domain.h>
+#include <linux/pm_runtime.h>
+#include <linux/sched/task_stack.h>
+#include <linux/slab.h>
+
+/* Mask for getting minor revision number from DIB */
+#define REVISION_NUM_MASK GENMASK(15, 8)
+
+/* CRC8 table for Assured Write Frame Check */
+#define PECI_CRC8_POLYNOMIAL 0x07
+DECLARE_CRC8_TABLE(peci_crc8_table);
+
+static bool is_registered;
+
+static DEFINE_MUTEX(core_lock);
+static DEFINE_IDR(peci_adapter_idr);
+
+struct peci_adapter *peci_get_adapter(int nr)
+{
+ struct peci_adapter *adapter;
+
+ mutex_lock(&core_lock);
+ adapter = idr_find(&peci_adapter_idr, nr);
+ if (!adapter)
+ goto out_unlock;
+
+ if (try_module_get(adapter->owner))
+ get_device(&adapter->dev);
+ else
+ adapter = NULL;
+
+out_unlock:
+ mutex_unlock(&core_lock);
+
+ return adapter;
+}
+EXPORT_SYMBOL_GPL(peci_get_adapter);
+
+void peci_put_adapter(struct peci_adapter *adapter)
+{
+ if (!adapter)
+ return;
+
+ put_device(&adapter->dev);
+ module_put(adapter->owner);
+}
+EXPORT_SYMBOL_GPL(peci_put_adapter);
+
+static ssize_t name_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ return sprintf(buf, "%s\n", dev->type == &peci_client_type ?
+ to_peci_client(dev)->name : to_peci_adapter(dev)->name);
+}
+static DEVICE_ATTR_RO(name);
+
+static void peci_client_dev_release(struct device *dev)
+{
+ struct peci_client *client = to_peci_client(dev);
+
+ dev_dbg(dev, "%s: %s\n", __func__, client->name);
+ peci_put_adapter(client->adapter);
+ kfree(client);
+}
+
+static struct attribute *peci_device_attrs[] = {
+ &dev_attr_name.attr,
+ NULL
+};
+ATTRIBUTE_GROUPS(peci_device);
+
+struct device_type peci_client_type = {
+ .groups = peci_device_groups,
+ .release = peci_client_dev_release,
+};
+EXPORT_SYMBOL_GPL(peci_client_type);
+
+/**
+ * peci_verify_client - return parameter as peci_client, or NULL
+ * @dev: device, probably from some driver model iterator
+ *
+ * Return: pointer to peci_client on success, else NULL.
+ */
+struct peci_client *peci_verify_client(struct device *dev)
+{
+ return (dev->type == &peci_client_type)
+ ? to_peci_client(dev)
+ : NULL;
+}
+EXPORT_SYMBOL_GPL(peci_verify_client);
+
+/**
+ * peci_get_xfer_msg() - get a DMA safe peci_xfer_msg for the given tx and rx
+ * length
+ * @tx_len: the length of tx_buf. May be 0 if tx_buf isn't needed.
+ * @rx_len: the length of rx_buf. May be 0 if rx_buf isn't needed.
+ *
+ * Return: NULL if a DMA safe buffer was not obtained.
+ * Or a valid pointer to be used with DMA. After use, release it by
+ * calling peci_put_xfer_msg().
+ *
+ * This function must only be called from process context!
+ */
+struct peci_xfer_msg *peci_get_xfer_msg(u8 tx_len, u8 rx_len)
+{
+ struct peci_xfer_msg *msg;
+ u8 *tx_buf, *rx_buf;
+
+ if (tx_len) {
+ tx_buf = kzalloc(tx_len, GFP_KERNEL);
+ if (!tx_buf)
+ return NULL;
+ } else {
+ tx_buf = NULL;
+ }
+
+ if (rx_len) {
+ rx_buf = kzalloc(rx_len, GFP_KERNEL);
+ if (!rx_buf)
+ goto err_free_tx_buf;
+ } else {
+ rx_buf = NULL;
+ }
+
+ msg = kzalloc(sizeof(*msg), GFP_KERNEL);
+ if (!msg)
+ goto err_free_tx_rx_buf;
+
+ msg->tx_len = tx_len;
+ msg->tx_buf = tx_buf;
+ msg->rx_len = rx_len;
+ msg->rx_buf = rx_buf;
+
+ return msg;
+
+err_free_tx_rx_buf:
+ kfree(rx_buf);
+err_free_tx_buf:
+ kfree(tx_buf);
+
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(peci_get_xfer_msg);
+
+/**
+ * peci_put_xfer_msg - release a DMA safe peci_xfer_msg
+ * @msg: the message obtained from peci_get_xfer_msg(). May be NULL.
+ */
+void peci_put_xfer_msg(struct peci_xfer_msg *msg)
+{
+ if (!msg)
+ return;
+
+ kfree(msg->rx_buf);
+ kfree(msg->tx_buf);
+ kfree(msg);
+}
+EXPORT_SYMBOL_GPL(peci_put_xfer_msg);
+
+/* Calculate an Assured Write Frame Check Sequence byte */
+static int peci_aw_fcs(struct peci_xfer_msg *msg, int len, u8 *aw_fcs)
+{
+ u8 *tmp_buf;
+
+ /* Allocate a temporary buffer to use a contiguous byte array */
+ tmp_buf = kmalloc(len, GFP_KERNEL);
+ if (!tmp_buf)
+ return -ENOMEM;
+
+ tmp_buf[0] = msg->addr;
+ tmp_buf[1] = msg->tx_len;
+ tmp_buf[2] = msg->rx_len;
+ memcpy(&tmp_buf[3], msg->tx_buf, len - 3);
+
+ *aw_fcs = crc8(peci_crc8_table, tmp_buf, (size_t)len, 0);
+
+ kfree(tmp_buf);
+
+ return 0;
+}
+
+static int __peci_xfer(struct peci_adapter *adapter, struct peci_xfer_msg *msg,
+ bool do_retry, bool has_aw_fcs)
+{
+ uint interval_ms = PECI_DEV_RETRY_INTERVAL_MIN_MSEC;
+ ulong timeout = jiffies;
+ u8 aw_fcs;
+ int ret;
+
+ /*
+ * In case if adapter uses DMA, check at here whether tx and rx buffers
+ * are DMA capable or not.
+ */
+ if (IS_ENABLED(CONFIG_HAS_DMA) && adapter->use_dma) {
+ if (is_vmalloc_addr(msg->tx_buf) ||
+ is_vmalloc_addr(msg->rx_buf)) {
+ WARN_ONCE(1, "xfer msg is not dma capable\n");
+ return -EAGAIN;
+ } else if (object_is_on_stack(msg->tx_buf) ||
+ object_is_on_stack(msg->rx_buf)) {
+ WARN_ONCE(1, "xfer msg is on stack\n");
+ return -EAGAIN;
+ }
+ }
+
+ /*
+ * For some commands, the PECI originator may need to retry a command if
+ * the processor PECI client responds with a 0x8x completion code. In
+ * each instance, the processor PECI client may have started the
+ * operation but not completed it yet. When the 'retry' bit is set, the
+ * PECI client will ignore a new request if it exactly matches a
+ * previous valid request. For better performance and for reducing
+ * retry traffic, the interval time will be increased exponentially.
+ */
+
+ if (do_retry)
+ timeout += PECI_DEV_RETRY_TIMEOUT;
+
+ for (;;) {
+ ret = adapter->xfer(adapter, msg);
+
+ if (!do_retry || ret || !msg->rx_buf)
+ break;
+
+ /* Retry is needed when completion code is 0x8x */
+ if ((msg->rx_buf[0] & PECI_DEV_CC_RETRY_CHECK_MASK) !=
+ PECI_DEV_CC_NEED_RETRY)
+ break;
+
+ /* Set the retry bit to indicate a retry attempt */
+ msg->tx_buf[1] |= PECI_DEV_RETRY_BIT;
+
+ /* Recalculate the AW FCS if it has one */
+ if (has_aw_fcs) {
+ ret = peci_aw_fcs(msg, 2 + msg->tx_len, &aw_fcs);
+ if (ret)
+ break;
+
+ msg->tx_buf[msg->tx_len - 1] = 0x80 ^ aw_fcs;
+ }
+
+ /* Retry it for 'timeout' before returning an error. */
+ if (time_after(jiffies, timeout)) {
+ dev_dbg(&adapter->dev, "Timeout retrying xfer!\n");
+ ret = -ETIMEDOUT;
+ break;
+ }
+
+ set_current_state(TASK_INTERRUPTIBLE);
+ if (schedule_timeout(msecs_to_jiffies(interval_ms))) {
+ ret = -EINTR;
+ break;
+ }
+
+ interval_ms *= 2;
+ if (interval_ms > PECI_DEV_RETRY_INTERVAL_MAX_MSEC)
+ interval_ms = PECI_DEV_RETRY_INTERVAL_MAX_MSEC;
+ }
+
+ if (ret)
+ dev_dbg(&adapter->dev, "xfer error: %d\n", ret);
+
+ return ret;
+}
+
+static int peci_xfer(struct peci_adapter *adapter, struct peci_xfer_msg *msg)
+{
+ return __peci_xfer(adapter, msg, false, false);
+}
+
+static int peci_xfer_with_retries(struct peci_adapter *adapter,
+ struct peci_xfer_msg *msg,
+ bool has_aw_fcs)
+{
+ return __peci_xfer(adapter, msg, true, has_aw_fcs);
+}
+
+static int peci_scan_cmd_mask(struct peci_adapter *adapter)
+{
+ struct peci_xfer_msg *msg;
+ u8 revision;
+ int ret;
+ u64 dib;
+
+ /* Update command mask just once */
+ if (adapter->cmd_mask & BIT(PECI_CMD_XFER))
+ return 0;
+
+ msg = peci_get_xfer_msg(PECI_GET_DIB_WR_LEN, PECI_GET_DIB_RD_LEN);
+ if (!msg)
+ return -ENOMEM;
+
+ msg->addr = PECI_BASE_ADDR;
+ msg->tx_buf[0] = PECI_GET_DIB_CMD;
+
+ ret = peci_xfer(adapter, msg);
+ if (ret)
+ return ret;
+
+ dib = le64_to_cpup((__le64 *)msg->rx_buf);
+
+ /* Check special case for Get DIB command */
+ if (dib == 0) {
+ dev_dbg(&adapter->dev, "DIB read as 0\n");
+ ret = -EIO;
+ goto out;
+ }
+
+ /*
+ * Setting up the supporting commands based on revision number.
+ * See PECI Spec Table 3-1.
+ */
+ revision = FIELD_GET(REVISION_NUM_MASK, dib);
+ if (revision >= 0x40) { /* Rev. 4.0 */
+ adapter->cmd_mask |= BIT(PECI_CMD_RD_IA_MSREX);
+ adapter->cmd_mask |= BIT(PECI_CMD_RD_END_PT_CFG);
+ adapter->cmd_mask |= BIT(PECI_CMD_WR_END_PT_CFG);
+ adapter->cmd_mask |= BIT(PECI_CMD_CRASHDUMP_DISC);
+ adapter->cmd_mask |= BIT(PECI_CMD_CRASHDUMP_GET_FRAME);
+ }
+ if (revision >= 0x36) /* Rev. 3.6 */
+ adapter->cmd_mask |= BIT(PECI_CMD_WR_IA_MSR);
+ if (revision >= 0x35) /* Rev. 3.5 */
+ adapter->cmd_mask |= BIT(PECI_CMD_WR_PCI_CFG);
+ if (revision >= 0x34) /* Rev. 3.4 */
+ adapter->cmd_mask |= BIT(PECI_CMD_RD_PCI_CFG);
+ if (revision >= 0x33) { /* Rev. 3.3 */
+ adapter->cmd_mask |= BIT(PECI_CMD_RD_PCI_CFG_LOCAL);
+ adapter->cmd_mask |= BIT(PECI_CMD_WR_PCI_CFG_LOCAL);
+ }
+ if (revision >= 0x32) /* Rev. 3.2 */
+ adapter->cmd_mask |= BIT(PECI_CMD_RD_IA_MSR);
+ if (revision >= 0x31) { /* Rev. 3.1 */
+ adapter->cmd_mask |= BIT(PECI_CMD_RD_PKG_CFG);
+ adapter->cmd_mask |= BIT(PECI_CMD_WR_PKG_CFG);
+ }
+
+ adapter->cmd_mask |= BIT(PECI_CMD_XFER);
+ adapter->cmd_mask |= BIT(PECI_CMD_GET_TEMP);
+ adapter->cmd_mask |= BIT(PECI_CMD_GET_DIB);
+ adapter->cmd_mask |= BIT(PECI_CMD_PING);
+
+out:
+ peci_put_xfer_msg(msg);
+
+ return ret;
+}
+
+static int peci_check_cmd_support(struct peci_adapter *adapter,
+ enum peci_cmd cmd)
+{
+ if (!(adapter->cmd_mask & BIT(PECI_CMD_PING)) &&
+ peci_scan_cmd_mask(adapter) < 0) {
+ dev_dbg(&adapter->dev, "Failed to scan command mask\n");
+ return -EIO;
+ }
+
+ if (!(adapter->cmd_mask & BIT(cmd))) {
+ dev_dbg(&adapter->dev, "Command %d is not supported\n", cmd);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int peci_cmd_xfer(struct peci_adapter *adapter, void *vmsg)
+{
+ struct peci_xfer_msg *msg = vmsg;
+ u8 aw_fcs;
+ int ret;
+
+ if (!msg->tx_len) {
+ ret = peci_xfer(adapter, msg);
+ } else {
+ switch (msg->tx_buf[0]) {
+ case PECI_RDPKGCFG_CMD:
+ case PECI_RDIAMSR_CMD:
+ case PECI_RDIAMSREX_CMD:
+ case PECI_RDPCICFG_CMD:
+ case PECI_RDPCICFGLOCAL_CMD:
+ case PECI_RDENDPTCFG_CMD:
+ case PECI_CRASHDUMP_CMD:
+ ret = peci_xfer_with_retries(adapter, msg, false);
+ break;
+ case PECI_WRPKGCFG_CMD:
+ case PECI_WRIAMSR_CMD:
+ case PECI_WRPCICFG_CMD:
+ case PECI_WRPCICFGLOCAL_CMD:
+ case PECI_WRENDPTCFG_CMD:
+ /* Check if the AW FCS byte is already provided */
+ ret = peci_aw_fcs(msg, 2 + msg->tx_len, &aw_fcs);
+ if (ret)
+ break;
+
+ if (msg->tx_buf[msg->tx_len - 1] != (0x80 ^ aw_fcs)) {
+ /*
+ * Add an Assured Write Frame Check Sequence
+ * byte and increment the tx_len to include
+ * the new byte.
+ */
+ msg->tx_len++;
+ ret = peci_aw_fcs(msg, 2 + msg->tx_len,
+ &aw_fcs);
+ if (ret)
+ break;
+
+ msg->tx_buf[msg->tx_len - 1] = 0x80 ^ aw_fcs;
+ }
+
+ ret = peci_xfer_with_retries(adapter, msg, true);
+ break;
+ case PECI_GET_DIB_CMD:
+ case PECI_GET_TEMP_CMD:
+ default:
+ ret = peci_xfer(adapter, msg);
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static int peci_cmd_ping(struct peci_adapter *adapter, void *vmsg)
+{
+ struct peci_ping_msg *umsg = vmsg;
+ struct peci_xfer_msg *msg;
+ int ret;
+
+ msg = peci_get_xfer_msg(0, 0);
+ if (!msg)
+ return -ENOMEM;
+
+ msg->addr = umsg->addr;
+
+ ret = peci_xfer(adapter, msg);
+
+ peci_put_xfer_msg(msg);
+
+ return ret;
+}
+
+static int peci_cmd_get_dib(struct peci_adapter *adapter, void *vmsg)
+{
+ struct peci_get_dib_msg *umsg = vmsg;
+ struct peci_xfer_msg *msg;
+ int ret;
+
+ msg = peci_get_xfer_msg(PECI_GET_DIB_WR_LEN, PECI_GET_DIB_RD_LEN);
+ if (!msg)
+ return -ENOMEM;
+
+ msg->addr = umsg->addr;
+ msg->tx_buf[0] = PECI_GET_DIB_CMD;
+
+ ret = peci_xfer(adapter, msg);
+ if (ret)
+ goto out;
+
+ umsg->dib = le64_to_cpup((__le64 *)msg->rx_buf);
+
+out:
+ peci_put_xfer_msg(msg);
+
+ return ret;
+}
+
+static int peci_cmd_get_temp(struct peci_adapter *adapter, void *vmsg)
+{
+ struct peci_get_temp_msg *umsg = vmsg;
+ struct peci_xfer_msg *msg;
+ int ret;
+
+ msg = peci_get_xfer_msg(PECI_GET_TEMP_WR_LEN, PECI_GET_TEMP_RD_LEN);
+ if (!msg)
+ return -ENOMEM;
+
+ msg->addr = umsg->addr;
+ msg->tx_buf[0] = PECI_GET_TEMP_CMD;
+
+ ret = peci_xfer(adapter, msg);
+ if (ret)
+ goto out;
+
+ umsg->temp_raw = le16_to_cpup((__le16 *)msg->rx_buf);
+
+out:
+ peci_put_xfer_msg(msg);
+
+ return ret;
+}
+
+static int peci_cmd_rd_pkg_cfg(struct peci_adapter *adapter, void *vmsg)
+{
+ struct peci_rd_pkg_cfg_msg *umsg = vmsg;
+ struct peci_xfer_msg *msg;
+ int ret;
+
+ /* Per the PECI spec, the read length must be a byte, word, or dword */
+ if (umsg->rx_len != 1 && umsg->rx_len != 2 && umsg->rx_len != 4) {
+ dev_dbg(&adapter->dev, "Invalid read length, rx_len: %d\n",
+ umsg->rx_len);
+ return -EINVAL;
+ }
+
+ msg = peci_get_xfer_msg(PECI_RDPKGCFG_WRITE_LEN,
+ PECI_RDPKGCFG_READ_LEN_BASE + umsg->rx_len);
+ if (!msg)
+ return -ENOMEM;
+
+ msg->addr = umsg->addr;
+ msg->tx_buf[0] = PECI_RDPKGCFG_CMD;
+ msg->tx_buf[1] = 0; /* request byte for Host ID | Retry bit */
+ /* Host ID is 0 for PECI 3.0 */
+ msg->tx_buf[2] = umsg->index; /* RdPkgConfig index */
+ msg->tx_buf[3] = (u8)umsg->param; /* LSB - Config parameter */
+ msg->tx_buf[4] = (u8)(umsg->param >> 8); /* MSB - Config parameter */
+
+ ret = peci_xfer_with_retries(adapter, msg, false);
+ if (!ret)
+ memcpy(umsg->pkg_config, &msg->rx_buf[1], umsg->rx_len);
+
+ umsg->cc = msg->rx_buf[0];
+ peci_put_xfer_msg(msg);
+
+ return ret;
+}
+
+static int peci_cmd_wr_pkg_cfg(struct peci_adapter *adapter, void *vmsg)
+{
+ struct peci_wr_pkg_cfg_msg *umsg = vmsg;
+ struct peci_xfer_msg *msg;
+ int ret, i;
+ u8 aw_fcs;
+
+ /* Per the PECI spec, the write length must be a dword */
+ if (umsg->tx_len != 4) {
+ dev_dbg(&adapter->dev, "Invalid write length, tx_len: %d\n",
+ umsg->tx_len);
+ return -EINVAL;
+ }
+
+ msg = peci_get_xfer_msg(PECI_WRPKGCFG_WRITE_LEN_BASE + umsg->tx_len,
+ PECI_WRPKGCFG_READ_LEN);
+ if (!msg)
+ return -ENOMEM;
+
+ msg->addr = umsg->addr;
+ msg->tx_buf[0] = PECI_WRPKGCFG_CMD;
+ msg->tx_buf[1] = 0; /* request byte for Host ID | Retry bit */
+ /* Host ID is 0 for PECI 3.0 */
+ msg->tx_buf[2] = umsg->index; /* RdPkgConfig index */
+ msg->tx_buf[3] = (u8)umsg->param; /* LSB - Config parameter */
+ msg->tx_buf[4] = (u8)(umsg->param >> 8); /* MSB - Config parameter */
+ for (i = 0; i < umsg->tx_len; i++)
+ msg->tx_buf[5 + i] = (u8)(umsg->value >> (i << 3));
+
+ /* Add an Assured Write Frame Check Sequence byte */
+ ret = peci_aw_fcs(msg, 8 + umsg->tx_len, &aw_fcs);
+ if (ret)
+ goto out;
+
+ msg->tx_buf[5 + i] = 0x80 ^ aw_fcs;
+
+ ret = peci_xfer_with_retries(adapter, msg, true);
+
+out:
+ umsg->cc = msg->rx_buf[0];
+ peci_put_xfer_msg(msg);
+
+ return ret;
+}
+
+static int peci_cmd_rd_ia_msr(struct peci_adapter *adapter, void *vmsg)
+{
+ struct peci_rd_ia_msr_msg *umsg = vmsg;
+ struct peci_xfer_msg *msg;
+ int ret;
+
+ msg = peci_get_xfer_msg(PECI_RDIAMSR_WRITE_LEN, PECI_RDIAMSR_READ_LEN);
+ if (!msg)
+ return -ENOMEM;
+
+ msg->addr = umsg->addr;
+ msg->tx_buf[0] = PECI_RDIAMSR_CMD;
+ msg->tx_buf[1] = 0;
+ msg->tx_buf[2] = umsg->thread_id;
+ msg->tx_buf[3] = (u8)umsg->address;
+ msg->tx_buf[4] = (u8)(umsg->address >> 8);
+
+ ret = peci_xfer_with_retries(adapter, msg, false);
+ if (!ret)
+ memcpy(&umsg->value, &msg->rx_buf[1], sizeof(uint64_t));
+
+ umsg->cc = msg->rx_buf[0];
+ peci_put_xfer_msg(msg);
+
+ return ret;
+}
+
+static int peci_cmd_rd_ia_msrex(struct peci_adapter *adapter, void *vmsg)
+{
+ struct peci_rd_ia_msrex_msg *umsg = vmsg;
+ struct peci_xfer_msg *msg;
+ int ret;
+
+ msg = peci_get_xfer_msg(PECI_RDIAMSREX_WRITE_LEN,
+ PECI_RDIAMSREX_READ_LEN);
+ if (!msg)
+ return -ENOMEM;
+
+ msg->addr = umsg->addr;
+ msg->tx_buf[0] = PECI_RDIAMSREX_CMD;
+ msg->tx_buf[1] = 0;
+ msg->tx_buf[2] = (u8)umsg->thread_id;
+ msg->tx_buf[3] = (u8)(umsg->thread_id >> 8);
+ msg->tx_buf[4] = (u8)umsg->address;
+ msg->tx_buf[5] = (u8)(umsg->address >> 8);
+
+ ret = peci_xfer_with_retries(adapter, msg, false);
+ if (!ret)
+ memcpy(&umsg->value, &msg->rx_buf[1], sizeof(uint64_t));
+
+ umsg->cc = msg->rx_buf[0];
+ peci_put_xfer_msg(msg);
+
+ return ret;
+}
+
+static int peci_cmd_wr_ia_msr(struct peci_adapter *adapter, void *vmsg)
+{
+ return -ENOSYS; /* Not implemented yet */
+}
+
+static int peci_cmd_rd_pci_cfg(struct peci_adapter *adapter, void *vmsg)
+{
+ struct peci_rd_pci_cfg_msg *umsg = vmsg;
+ struct peci_xfer_msg *msg;
+ u32 address;
+ int ret;
+
+ msg = peci_get_xfer_msg(PECI_RDPCICFG_WRITE_LEN,
+ PECI_RDPCICFG_READ_LEN);
+ if (!msg)
+ return -ENOMEM;
+
+ address = umsg->reg; /* [11:0] - Register */
+ address |= (u32)umsg->function << 12; /* [14:12] - Function */
+ address |= (u32)umsg->device << 15; /* [19:15] - Device */
+ address |= (u32)umsg->bus << 20; /* [27:20] - Bus */
+ /* [31:28] - Reserved */
+ msg->addr = umsg->addr;
+ msg->tx_buf[0] = PECI_RDPCICFG_CMD;
+ msg->tx_buf[1] = 0; /* request byte for Host ID | Retry bit */
+ /* Host ID is 0 for PECI 3.0 */
+ msg->tx_buf[2] = (u8)address; /* LSB - PCI Config Address */
+ msg->tx_buf[3] = (u8)(address >> 8); /* PCI Config Address */
+ msg->tx_buf[4] = (u8)(address >> 16); /* PCI Config Address */
+ msg->tx_buf[5] = (u8)(address >> 24); /* MSB - PCI Config Address */
+
+ ret = peci_xfer_with_retries(adapter, msg, false);
+ if (!ret)
+ memcpy(umsg->pci_config, &msg->rx_buf[1], 4);
+
+ umsg->cc = msg->rx_buf[0];
+ peci_put_xfer_msg(msg);
+
+ return ret;
+}
+
+static int peci_cmd_wr_pci_cfg(struct peci_adapter *adapter, void *vmsg)
+{
+ return -ENOSYS; /* Not implemented yet */
+}
+
+static int peci_cmd_rd_pci_cfg_local(struct peci_adapter *adapter, void *vmsg)
+{
+ struct peci_rd_pci_cfg_local_msg *umsg = vmsg;
+ struct peci_xfer_msg *msg;
+ u32 address;
+ int ret;
+
+ /* Per the PECI spec, the read length must be a byte, word, or dword */
+ if (umsg->rx_len != 1 && umsg->rx_len != 2 && umsg->rx_len != 4) {
+ dev_dbg(&adapter->dev, "Invalid read length, rx_len: %d\n",
+ umsg->rx_len);
+ return -EINVAL;
+ }
+
+ msg = peci_get_xfer_msg(PECI_RDPCICFGLOCAL_WRITE_LEN,
+ PECI_RDPCICFGLOCAL_READ_LEN_BASE +
+ umsg->rx_len);
+ if (!msg)
+ return -ENOMEM;
+
+ address = umsg->reg; /* [11:0] - Register */
+ address |= (u32)umsg->function << 12; /* [14:12] - Function */
+ address |= (u32)umsg->device << 15; /* [19:15] - Device */
+ address |= (u32)umsg->bus << 20; /* [23:20] - Bus */
+
+ msg->addr = umsg->addr;
+ msg->tx_buf[0] = PECI_RDPCICFGLOCAL_CMD;
+ msg->tx_buf[1] = 0; /* request byte for Host ID | Retry bit */
+ /* Host ID is 0 for PECI 3.0 */
+ msg->tx_buf[2] = (u8)address; /* LSB - PCI Configuration Address */
+ msg->tx_buf[3] = (u8)(address >> 8); /* PCI Configuration Address */
+ msg->tx_buf[4] = (u8)(address >> 16); /* PCI Configuration Address */
+
+ ret = peci_xfer_with_retries(adapter, msg, false);
+ if (!ret)
+ memcpy(umsg->pci_config, &msg->rx_buf[1], umsg->rx_len);
+
+ umsg->cc = msg->rx_buf[0];
+ peci_put_xfer_msg(msg);
+
+ return ret;
+}
+
+static int peci_cmd_wr_pci_cfg_local(struct peci_adapter *adapter, void *vmsg)
+{
+ struct peci_wr_pci_cfg_local_msg *umsg = vmsg;
+ struct peci_xfer_msg *msg;
+ u32 address;
+ int ret, i;
+ u8 aw_fcs;
+
+ /* Per the PECI spec, the write length must be a byte, word, or dword */
+ if (umsg->tx_len != 1 && umsg->tx_len != 2 && umsg->tx_len != 4) {
+ dev_dbg(&adapter->dev, "Invalid write length, tx_len: %d\n",
+ umsg->tx_len);
+ return -EINVAL;
+ }
+
+ msg = peci_get_xfer_msg(PECI_WRPCICFGLOCAL_WRITE_LEN_BASE +
+ umsg->tx_len, PECI_WRPCICFGLOCAL_READ_LEN);
+ if (!msg)
+ return -ENOMEM;
+
+ address = umsg->reg; /* [11:0] - Register */
+ address |= (u32)umsg->function << 12; /* [14:12] - Function */
+ address |= (u32)umsg->device << 15; /* [19:15] - Device */
+ address |= (u32)umsg->bus << 20; /* [23:20] - Bus */
+
+ msg->addr = umsg->addr;
+ msg->tx_buf[0] = PECI_WRPCICFGLOCAL_CMD;
+ msg->tx_buf[1] = 0; /* request byte for Host ID | Retry bit */
+ /* Host ID is 0 for PECI 3.0 */
+ msg->tx_buf[2] = (u8)address; /* LSB - PCI Configuration Address */
+ msg->tx_buf[3] = (u8)(address >> 8); /* PCI Configuration Address */
+ msg->tx_buf[4] = (u8)(address >> 16); /* PCI Configuration Address */
+ for (i = 0; i < umsg->tx_len; i++)
+ msg->tx_buf[5 + i] = (u8)(umsg->value >> (i << 3));
+
+ /* Add an Assured Write Frame Check Sequence byte */
+ ret = peci_aw_fcs(msg, 8 + umsg->tx_len, &aw_fcs);
+ if (ret)
+ goto out;
+
+ msg->tx_buf[5 + i] = 0x80 ^ aw_fcs;
+
+ ret = peci_xfer_with_retries(adapter, msg, true);
+
+out:
+ umsg->cc = msg->rx_buf[0];
+ peci_put_xfer_msg(msg);
+
+ return ret;
+}
+
+static int peci_cmd_rd_end_pt_cfg(struct peci_adapter *adapter, void *vmsg)
+{
+ struct peci_rd_end_pt_cfg_msg *umsg = vmsg;
+ struct peci_xfer_msg *msg = NULL;
+ u32 address;
+ u8 tx_size;
+ int ret;
+
+ switch (umsg->msg_type) {
+ case PECI_ENDPTCFG_TYPE_LOCAL_PCI:
+ case PECI_ENDPTCFG_TYPE_PCI:
+ /*
+ * Per the PECI spec, the read length must be a byte, word,
+ * or dword
+ */
+ if (umsg->rx_len != 1 && umsg->rx_len != 2 &&
+ umsg->rx_len != 4) {
+ dev_dbg(&adapter->dev,
+ "Invalid read length, rx_len: %d\n",
+ umsg->rx_len);
+ return -EINVAL;
+ }
+
+ msg = peci_get_xfer_msg(PECI_RDENDPTCFG_PCI_WRITE_LEN,
+ PECI_RDENDPTCFG_READ_LEN_BASE +
+ umsg->rx_len);
+ if (!msg)
+ return -ENOMEM;
+
+ address = umsg->params.pci_cfg.reg; /* [11:0] - Register */
+ address |= (u32)umsg->params.pci_cfg.function
+ << 12; /* [14:12] - Function */
+ address |= (u32)umsg->params.pci_cfg.device
+ << 15; /* [19:15] - Device */
+ address |= (u32)umsg->params.pci_cfg.bus
+ << 20; /* [27:20] - Bus */
+ /* [31:28] - Reserved */
+ msg->addr = umsg->addr;
+ msg->tx_buf[0] = PECI_RDENDPTCFG_CMD;
+ msg->tx_buf[1] = 0x00; /* request byte for Host ID|Retry bit */
+ msg->tx_buf[2] = umsg->msg_type; /* Message Type */
+ msg->tx_buf[3] = 0x00; /* Endpoint ID */
+ msg->tx_buf[4] = 0x00; /* Reserved */
+ msg->tx_buf[5] = 0x00; /* Reserved */
+ msg->tx_buf[6] = PECI_ENDPTCFG_ADDR_TYPE_PCI; /* Addr Type */
+ msg->tx_buf[7] = umsg->params.pci_cfg.seg; /* PCI Segment */
+ msg->tx_buf[8] = (u8)address; /* LSB - PCI Config Address */
+ msg->tx_buf[9] = (u8)(address >> 8); /* PCI Config Address */
+ msg->tx_buf[10] = (u8)(address >> 16); /* PCI Config Address */
+ msg->tx_buf[11] =
+ (u8)(address >> 24); /* MSB - PCI Config Address */
+ break;
+
+ case PECI_ENDPTCFG_TYPE_MMIO:
+ /*
+ * Per the PECI spec, the read length must be a byte, word,
+ * dword, or qword
+ */
+ if (umsg->rx_len != 1 && umsg->rx_len != 2 &&
+ umsg->rx_len != 4 && umsg->rx_len != 8) {
+ dev_dbg(&adapter->dev,
+ "Invalid read length, rx_len: %d\n",
+ umsg->rx_len);
+ return -EINVAL;
+ }
+ /*
+ * Per the PECI spec, the address type must specify either DWORD
+ * or QWORD
+ */
+ if (umsg->params.mmio.addr_type !=
+ PECI_ENDPTCFG_ADDR_TYPE_MMIO_D &&
+ umsg->params.mmio.addr_type !=
+ PECI_ENDPTCFG_ADDR_TYPE_MMIO_Q) {
+ dev_dbg(&adapter->dev,
+ "Invalid address type, addr_type: %d\n",
+ umsg->params.mmio.addr_type);
+ return -EINVAL;
+ }
+
+ if (umsg->params.mmio.addr_type ==
+ PECI_ENDPTCFG_ADDR_TYPE_MMIO_D)
+ tx_size = PECI_RDENDPTCFG_MMIO_D_WRITE_LEN;
+ else
+ tx_size = PECI_RDENDPTCFG_MMIO_Q_WRITE_LEN;
+ msg = peci_get_xfer_msg(tx_size,
+ PECI_RDENDPTCFG_READ_LEN_BASE +
+ umsg->rx_len);
+ if (!msg)
+ return -ENOMEM;
+
+ address = umsg->params.mmio.function; /* [2:0] - Function */
+ address |= (u32)umsg->params.mmio.device
+ << 3; /* [7:3] - Device */
+
+ msg->addr = umsg->addr;
+ msg->tx_buf[0] = PECI_RDENDPTCFG_CMD;
+ msg->tx_buf[1] = 0x00; /* request byte for Host ID|Retry bit */
+ msg->tx_buf[2] = umsg->msg_type; /* Message Type */
+ msg->tx_buf[3] = 0x00; /* Endpoint ID */
+ msg->tx_buf[4] = 0x00; /* Reserved */
+ msg->tx_buf[5] = umsg->params.mmio.bar; /* BAR # */
+ msg->tx_buf[6] = umsg->params.mmio.addr_type; /* Address Type */
+ msg->tx_buf[7] = umsg->params.mmio.seg; /* PCI Segment */
+ msg->tx_buf[8] = (u8)address; /* Function/Device */
+ msg->tx_buf[9] = umsg->params.mmio.bus; /* PCI Bus */
+ msg->tx_buf[10] = (u8)umsg->params.mmio
+ .offset; /* LSB - Register Offset */
+ msg->tx_buf[11] = (u8)(umsg->params.mmio.offset
+ >> 8); /* Register Offset */
+ msg->tx_buf[12] = (u8)(umsg->params.mmio.offset
+ >> 16); /* Register Offset */
+ msg->tx_buf[13] = (u8)(umsg->params.mmio.offset
+ >> 24); /* MSB - DWORD Register Offset */
+ if (umsg->params.mmio.addr_type ==
+ PECI_ENDPTCFG_ADDR_TYPE_MMIO_Q) {
+ msg->tx_buf[14] = (u8)(umsg->params.mmio.offset
+ >> 32); /* Register Offset */
+ msg->tx_buf[15] = (u8)(umsg->params.mmio.offset
+ >> 40); /* Register Offset */
+ msg->tx_buf[16] = (u8)(umsg->params.mmio.offset
+ >> 48); /* Register Offset */
+ msg->tx_buf[17] =
+ (u8)(umsg->params.mmio.offset
+ >> 56); /* MSB - QWORD Register Offset */
+ }
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ ret = peci_xfer_with_retries(adapter, msg, false);
+ if (!ret)
+ memcpy(umsg->data, &msg->rx_buf[1], umsg->rx_len);
+
+ umsg->cc = msg->rx_buf[0];
+ peci_put_xfer_msg(msg);
+
+ return ret;
+}
+
+static int peci_cmd_wr_end_pt_cfg(struct peci_adapter *adapter, void *vmsg)
+{
+ struct peci_wr_end_pt_cfg_msg *umsg = vmsg;
+ struct peci_xfer_msg *msg = NULL;
+ u8 tx_size, aw_fcs;
+ int ret, i, idx;
+ u32 address;
+
+ switch (umsg->msg_type) {
+ case PECI_ENDPTCFG_TYPE_LOCAL_PCI:
+ case PECI_ENDPTCFG_TYPE_PCI:
+ /*
+ * Per the PECI spec, the write length must be a byte, word,
+ * or dword
+ */
+ if (umsg->tx_len != 1 && umsg->tx_len != 2 &&
+ umsg->tx_len != 4) {
+ dev_dbg(&adapter->dev,
+ "Invalid write length, tx_len: %d\n",
+ umsg->tx_len);
+ return -EINVAL;
+ }
+
+ msg = peci_get_xfer_msg(PECI_WRENDPTCFG_PCI_WRITE_LEN_BASE +
+ umsg->tx_len, PECI_WRENDPTCFG_READ_LEN);
+ if (!msg)
+ return -ENOMEM;
+
+ address = umsg->params.pci_cfg.reg; /* [11:0] - Register */
+ address |= (u32)umsg->params.pci_cfg.function
+ << 12; /* [14:12] - Function */
+ address |= (u32)umsg->params.pci_cfg.device
+ << 15; /* [19:15] - Device */
+ address |= (u32)umsg->params.pci_cfg.bus
+ << 20; /* [27:20] - Bus */
+ /* [31:28] - Reserved */
+ msg->addr = umsg->addr;
+ msg->tx_buf[0] = PECI_WRENDPTCFG_CMD;
+ msg->tx_buf[1] = 0x00; /* request byte for Host ID|Retry bit */
+ msg->tx_buf[2] = umsg->msg_type; /* Message Type */
+ msg->tx_buf[3] = 0x00; /* Endpoint ID */
+ msg->tx_buf[4] = 0x00; /* Reserved */
+ msg->tx_buf[5] = 0x00; /* Reserved */
+ msg->tx_buf[6] = PECI_ENDPTCFG_ADDR_TYPE_PCI; /* Addr Type */
+ msg->tx_buf[7] = umsg->params.pci_cfg.seg; /* PCI Segment */
+ msg->tx_buf[8] = (u8)address; /* LSB - PCI Config Address */
+ msg->tx_buf[9] = (u8)(address >> 8); /* PCI Config Address */
+ msg->tx_buf[10] = (u8)(address >> 16); /* PCI Config Address */
+ msg->tx_buf[11] =
+ (u8)(address >> 24); /* MSB - PCI Config Address */
+ for (i = 0; i < umsg->tx_len; i++)
+ msg->tx_buf[12 + i] = (u8)(umsg->value >> (i << 3));
+
+ /* Add an Assured Write Frame Check Sequence byte */
+ ret = peci_aw_fcs(msg, 15 + umsg->tx_len, &aw_fcs);
+ if (ret)
+ goto out;
+
+ msg->tx_buf[12 + i] = 0x80 ^ aw_fcs;
+ break;
+
+ case PECI_ENDPTCFG_TYPE_MMIO:
+ /*
+ * Per the PECI spec, the write length must be a byte, word,
+ * dword, or qword
+ */
+ if (umsg->tx_len != 1 && umsg->tx_len != 2 &&
+ umsg->tx_len != 4 && umsg->tx_len != 8) {
+ dev_dbg(&adapter->dev,
+ "Invalid write length, tx_len: %d\n",
+ umsg->tx_len);
+ return -EINVAL;
+ }
+ /*
+ * Per the PECI spec, the address type must specify either DWORD
+ * or QWORD
+ */
+ if (umsg->params.mmio.addr_type !=
+ PECI_ENDPTCFG_ADDR_TYPE_MMIO_D &&
+ umsg->params.mmio.addr_type !=
+ PECI_ENDPTCFG_ADDR_TYPE_MMIO_Q) {
+ dev_dbg(&adapter->dev,
+ "Invalid address type, addr_type: %d\n",
+ umsg->params.mmio.addr_type);
+ return -EINVAL;
+ }
+
+ if (umsg->params.mmio.addr_type ==
+ PECI_ENDPTCFG_ADDR_TYPE_MMIO_D)
+ tx_size = PECI_WRENDPTCFG_MMIO_D_WRITE_LEN_BASE +
+ umsg->tx_len;
+ else
+ tx_size = PECI_WRENDPTCFG_MMIO_Q_WRITE_LEN_BASE +
+ umsg->tx_len;
+ msg = peci_get_xfer_msg(tx_size, PECI_WRENDPTCFG_READ_LEN);
+ if (!msg)
+ return -ENOMEM;
+
+ address = umsg->params.mmio.function; /* [2:0] - Function */
+ address |= (u32)umsg->params.mmio.device
+ << 3; /* [7:3] - Device */
+
+ msg->addr = umsg->addr;
+ msg->tx_buf[0] = PECI_WRENDPTCFG_CMD;
+ msg->tx_buf[1] = 0x00; /* request byte for Host ID|Retry bit */
+ msg->tx_buf[2] = umsg->msg_type; /* Message Type */
+ msg->tx_buf[3] = 0x00; /* Endpoint ID */
+ msg->tx_buf[4] = 0x00; /* Reserved */
+ msg->tx_buf[5] = umsg->params.mmio.bar; /* BAR # */
+ msg->tx_buf[6] = umsg->params.mmio.addr_type; /* Address Type */
+ msg->tx_buf[7] = umsg->params.mmio.seg; /* PCI Segment */
+ msg->tx_buf[8] = (u8)address; /* Function/Device */
+ msg->tx_buf[9] = umsg->params.mmio.bus; /* PCI Bus */
+ msg->tx_buf[10] = (u8)umsg->params.mmio
+ .offset; /* LSB - Register Offset */
+ msg->tx_buf[11] = (u8)(umsg->params.mmio.offset
+ >> 8); /* Register Offset */
+ msg->tx_buf[12] = (u8)(umsg->params.mmio.offset
+ >> 16); /* Register Offset */
+ msg->tx_buf[13] = (u8)(umsg->params.mmio.offset
+ >> 24); /* MSB - DWORD Register Offset */
+ if (umsg->params.mmio.addr_type ==
+ PECI_ENDPTCFG_ADDR_TYPE_MMIO_Q) {
+ msg->tx_len = PECI_WRENDPTCFG_MMIO_Q_WRITE_LEN_BASE;
+ msg->tx_buf[14] = (u8)(umsg->params.mmio.offset
+ >> 32); /* Register Offset */
+ msg->tx_buf[15] = (u8)(umsg->params.mmio.offset
+ >> 40); /* Register Offset */
+ msg->tx_buf[16] = (u8)(umsg->params.mmio.offset
+ >> 48); /* Register Offset */
+ msg->tx_buf[17] =
+ (u8)(umsg->params.mmio.offset
+ >> 56); /* MSB - QWORD Register Offset */
+ idx = 18;
+ } else {
+ idx = 14;
+ }
+ for (i = 0; i < umsg->tx_len; i++)
+ msg->tx_buf[idx + i] = (u8)(umsg->value >> (i << 3));
+
+ /* Add an Assured Write Frame Check Sequence byte */
+ ret = peci_aw_fcs(msg, idx + 3 + umsg->tx_len, &aw_fcs);
+ if (ret)
+ goto out;
+
+ msg->tx_buf[idx + i] = 0x80 ^ aw_fcs;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ ret = peci_xfer_with_retries(adapter, msg, false);
+
+out:
+ umsg->cc = msg->rx_buf[0];
+ peci_put_xfer_msg(msg);
+
+ return ret;
+}
+
+static int peci_cmd_crashdump_disc(struct peci_adapter *adapter, void *vmsg)
+{
+ struct peci_crashdump_disc_msg *umsg = vmsg;
+ struct peci_xfer_msg *msg;
+ int ret;
+
+ /* Per the EDS, the read length must be a byte, word, or qword */
+ if (umsg->rx_len != 1 && umsg->rx_len != 2 && umsg->rx_len != 8) {
+ dev_dbg(&adapter->dev, "Invalid read length, rx_len: %d\n",
+ umsg->rx_len);
+ return -EINVAL;
+ }
+
+ msg = peci_get_xfer_msg(PECI_CRASHDUMP_DISC_WRITE_LEN,
+ PECI_CRASHDUMP_DISC_READ_LEN_BASE +
+ umsg->rx_len);
+ if (!msg)
+ return -ENOMEM;
+
+ msg->addr = umsg->addr;
+ msg->tx_buf[0] = PECI_CRASHDUMP_CMD;
+ msg->tx_buf[1] = 0x00; /* request byte for Host ID | Retry bit */
+ /* Host ID is 0 for PECI 3.0 */
+ msg->tx_buf[2] = PECI_CRASHDUMP_DISC_VERSION;
+ msg->tx_buf[3] = PECI_CRASHDUMP_DISC_OPCODE;
+ msg->tx_buf[4] = umsg->subopcode;
+ msg->tx_buf[5] = umsg->param0;
+ msg->tx_buf[6] = (u8)umsg->param1;
+ msg->tx_buf[7] = (u8)(umsg->param1 >> 8);
+ msg->tx_buf[8] = umsg->param2;
+
+ ret = peci_xfer_with_retries(adapter, msg, false);
+ if (!ret)
+ memcpy(umsg->data, &msg->rx_buf[1], umsg->rx_len);
+
+ umsg->cc = msg->rx_buf[0];
+ peci_put_xfer_msg(msg);
+
+ return ret;
+}
+
+static int peci_cmd_crashdump_get_frame(struct peci_adapter *adapter,
+ void *vmsg)
+{
+ struct peci_crashdump_get_frame_msg *umsg = vmsg;
+ struct peci_xfer_msg *msg;
+ int ret;
+
+ /* Per the EDS, the read length must be a qword or dqword */
+ if (umsg->rx_len != 8 && umsg->rx_len != 16) {
+ dev_dbg(&adapter->dev, "Invalid read length, rx_len: %d\n",
+ umsg->rx_len);
+ return -EINVAL;
+ }
+
+ msg = peci_get_xfer_msg(PECI_CRASHDUMP_GET_FRAME_WRITE_LEN,
+ PECI_CRASHDUMP_GET_FRAME_READ_LEN_BASE +
+ umsg->rx_len);
+ if (!msg)
+ return -ENOMEM;
+
+ msg->addr = umsg->addr;
+ msg->tx_buf[0] = PECI_CRASHDUMP_CMD;
+ msg->tx_buf[1] = 0x00; /* request byte for Host ID | Retry bit */
+ /* Host ID is 0 for PECI 3.0 */
+ msg->tx_buf[2] = PECI_CRASHDUMP_GET_FRAME_VERSION;
+ msg->tx_buf[3] = PECI_CRASHDUMP_GET_FRAME_OPCODE;
+ msg->tx_buf[4] = (u8)umsg->param0;
+ msg->tx_buf[5] = (u8)(umsg->param0 >> 8);
+ msg->tx_buf[6] = (u8)umsg->param1;
+ msg->tx_buf[7] = (u8)(umsg->param1 >> 8);
+ msg->tx_buf[8] = (u8)umsg->param2;
+ msg->tx_buf[9] = (u8)(umsg->param2 >> 8);
+
+ ret = peci_xfer_with_retries(adapter, msg, false);
+ if (!ret)
+ memcpy(umsg->data, &msg->rx_buf[1], umsg->rx_len);
+
+ umsg->cc = msg->rx_buf[0];
+ peci_put_xfer_msg(msg);
+
+ return ret;
+}
+
+typedef int (*peci_cmd_fn_type)(struct peci_adapter *, void *);
+
+static const peci_cmd_fn_type peci_cmd_fn[PECI_CMD_MAX] = {
+ peci_cmd_xfer,
+ peci_cmd_ping,
+ peci_cmd_get_dib,
+ peci_cmd_get_temp,
+ peci_cmd_rd_pkg_cfg,
+ peci_cmd_wr_pkg_cfg,
+ peci_cmd_rd_ia_msr,
+ peci_cmd_wr_ia_msr,
+ peci_cmd_rd_ia_msrex,
+ peci_cmd_rd_pci_cfg,
+ peci_cmd_wr_pci_cfg,
+ peci_cmd_rd_pci_cfg_local,
+ peci_cmd_wr_pci_cfg_local,
+ peci_cmd_rd_end_pt_cfg,
+ peci_cmd_wr_end_pt_cfg,
+ peci_cmd_crashdump_disc,
+ peci_cmd_crashdump_get_frame,
+};
+
+/**
+ * peci_command - transfer function of a PECI command
+ * @adapter: pointer to peci_adapter
+ * @vmsg: pointer to PECI messages
+ * Context: can sleep
+ *
+ * This performs a transfer of a PECI command using PECI messages parameter
+ * which has various formats on each command.
+ *
+ * Return: zero on success, else a negative error code.
+ */
+int peci_command(struct peci_adapter *adapter, enum peci_cmd cmd, void *vmsg)
+{
+ int ret;
+
+ if (cmd >= PECI_CMD_MAX || cmd < PECI_CMD_XFER)
+ return -ENOTTY;
+
+ dev_dbg(&adapter->dev, "%s, cmd=0x%02x\n", __func__, cmd);
+
+ if (!peci_cmd_fn[cmd])
+ return -EINVAL;
+
+ mutex_lock(&adapter->bus_lock);
+
+ ret = peci_check_cmd_support(adapter, cmd);
+ if (!ret)
+ ret = peci_cmd_fn[cmd](adapter, vmsg);
+
+ mutex_unlock(&adapter->bus_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(peci_command);
+
+static int peci_detect(struct peci_adapter *adapter, u8 addr)
+{
+ struct peci_ping_msg msg;
+
+ msg.addr = addr;
+
+ return peci_command(adapter, PECI_CMD_PING, &msg);
+}
+
+static const struct of_device_id *
+peci_of_match_device(const struct of_device_id *matches,
+ struct peci_client *client)
+{
+#if IS_ENABLED(CONFIG_OF)
+ if (!(client && matches))
+ return NULL;
+
+ return of_match_device(matches, &client->dev);
+#else /* CONFIG_OF */
+ return NULL;
+#endif /* CONFIG_OF */
+}
+
+static const struct peci_device_id *
+peci_match_id(const struct peci_device_id *id, struct peci_client *client)
+{
+ if (!(id && client))
+ return NULL;
+
+ while (id->name[0]) {
+ if (!strncmp(client->name, id->name, PECI_NAME_SIZE))
+ return id;
+ id++;
+ }
+
+ return NULL;
+}
+
+static int peci_device_match(struct device *dev, struct device_driver *drv)
+{
+ struct peci_client *client = peci_verify_client(dev);
+ struct peci_driver *driver;
+
+ /* Attempt an OF style match */
+ if (peci_of_match_device(drv->of_match_table, client))
+ return 1;
+
+ driver = to_peci_driver(drv);
+
+ /* Finally an ID match */
+ if (peci_match_id(driver->id_table, client))
+ return 1;
+
+ return 0;
+}
+
+static int peci_device_probe(struct device *dev)
+{
+ struct peci_client *client = peci_verify_client(dev);
+ struct peci_driver *driver;
+ int status = -EINVAL;
+
+ if (!client)
+ return 0;
+
+ driver = to_peci_driver(dev->driver);
+
+ if (!driver->id_table &&
+ !peci_of_match_device(dev->driver->of_match_table, client))
+ return -ENODEV;
+
+ dev_dbg(dev, "%s: name:%s\n", __func__, client->name);
+
+ status = dev_pm_domain_attach(&client->dev, true);
+ if (status == -EPROBE_DEFER)
+ return status;
+
+ if (driver->probe)
+ status = driver->probe(client);
+ else
+ status = -EINVAL;
+
+ if (status)
+ goto err_detach_pm_domain;
+
+ return 0;
+
+err_detach_pm_domain:
+ dev_pm_domain_detach(&client->dev, true);
+
+ return status;
+}
+
+static int peci_device_remove(struct device *dev)
+{
+ struct peci_client *client = peci_verify_client(dev);
+ struct peci_driver *driver;
+ int status = 0;
+
+ if (!client || !dev->driver)
+ return 0;
+
+ driver = to_peci_driver(dev->driver);
+ if (driver->remove) {
+ dev_dbg(dev, "%s: name:%s\n", __func__, client->name);
+ status = driver->remove(client);
+ }
+
+ dev_pm_domain_detach(&client->dev, true);
+
+ return status;
+}
+
+static void peci_device_shutdown(struct device *dev)
+{
+ struct peci_client *client = peci_verify_client(dev);
+ struct peci_driver *driver;
+
+ if (!client || !dev->driver)
+ return;
+
+ dev_dbg(dev, "%s: name:%s\n", __func__, client->name);
+
+ driver = to_peci_driver(dev->driver);
+ if (driver->shutdown)
+ driver->shutdown(client);
+}
+
+struct bus_type peci_bus_type = {
+ .name = "peci",
+ .match = peci_device_match,
+ .probe = peci_device_probe,
+ .remove = peci_device_remove,
+ .shutdown = peci_device_shutdown,
+};
+EXPORT_SYMBOL_GPL(peci_bus_type);
+
+static int peci_check_addr_validity(u8 addr)
+{
+ if (addr < PECI_BASE_ADDR && addr > PECI_BASE_ADDR + PECI_OFFSET_MAX)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int peci_check_client_busy(struct device *dev, void *client_new_p)
+{
+ struct peci_client *client = peci_verify_client(dev);
+ struct peci_client *client_new = client_new_p;
+
+ if (client && client->addr == client_new->addr)
+ return -EBUSY;
+
+ return 0;
+}
+
+/**
+ * peci_get_cpu_id - read CPU ID from the Package Configuration Space of CPU
+ * @adapter: pointer to peci_adapter
+ * @addr: address of the PECI client CPU
+ * @cpu_id: where the CPU ID will be stored
+ * Context: can sleep
+ *
+ * Return: zero on success, else a negative error code.
+ */
+int peci_get_cpu_id(struct peci_adapter *adapter, u8 addr, u32 *cpu_id)
+{
+ struct peci_rd_pkg_cfg_msg msg;
+ int ret;
+
+ msg.addr = addr;
+ msg.index = PECI_MBX_INDEX_CPU_ID;
+ msg.param = PECI_PKG_ID_CPU_ID;
+ msg.rx_len = 4;
+
+ ret = peci_command(adapter, PECI_CMD_RD_PKG_CFG, &msg);
+ if (msg.cc != PECI_DEV_CC_SUCCESS)
+ ret = -EAGAIN;
+ if (ret)
+ return ret;
+
+ *cpu_id = le32_to_cpup((__le32 *)msg.pkg_config);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(peci_get_cpu_id);
+
+static struct peci_client *peci_new_device(struct peci_adapter *adapter,
+ struct peci_board_info const *info)
+{
+ struct peci_client *client;
+ int ret;
+
+ /* Increase reference count for the adapter assigned */
+ if (!peci_get_adapter(adapter->nr))
+ return NULL;
+
+ client = kzalloc(sizeof(*client), GFP_KERNEL);
+ if (!client)
+ goto err_put_adapter;
+
+ client->adapter = adapter;
+ client->addr = info->addr;
+ strlcpy(client->name, info->type, sizeof(client->name));
+
+ ret = peci_check_addr_validity(client->addr);
+ if (ret) {
+ dev_err(&adapter->dev, "Invalid PECI CPU address 0x%02hx\n",
+ client->addr);
+ goto err_free_client_silent;
+ }
+
+ /* Check online status of client */
+ ret = peci_detect(adapter, client->addr);
+ if (ret)
+ goto err_free_client;
+
+ ret = device_for_each_child(&adapter->dev, client,
+ peci_check_client_busy);
+ if (ret)
+ goto err_free_client;
+
+ client->dev.parent = &client->adapter->dev;
+ client->dev.bus = &peci_bus_type;
+ client->dev.type = &peci_client_type;
+ client->dev.of_node = of_node_get(info->of_node);
+ dev_set_name(&client->dev, "%d-%02x", adapter->nr, client->addr);
+
+ ret = device_register(&client->dev);
+ if (ret)
+ goto err_put_of_node;
+
+ dev_dbg(&adapter->dev, "client [%s] registered with bus id %s\n",
+ client->name, dev_name(&client->dev));
+
+ return client;
+
+err_put_of_node:
+ of_node_put(info->of_node);
+err_free_client:
+ dev_err(&adapter->dev,
+ "Failed to register peci client %s at 0x%02x (%d)\n",
+ client->name, client->addr, ret);
+err_free_client_silent:
+ kfree(client);
+err_put_adapter:
+ peci_put_adapter(adapter);
+
+ return NULL;
+}
+
+static void peci_unregister_device(struct peci_client *client)
+{
+ if (!client)
+ return;
+
+ if (client->dev.of_node) {
+ of_node_clear_flag(client->dev.of_node, OF_POPULATED);
+ of_node_put(client->dev.of_node);
+ }
+
+ device_unregister(&client->dev);
+}
+
+static int peci_unregister_client(struct device *dev, void *dummy)
+{
+ struct peci_client *client = peci_verify_client(dev);
+
+ peci_unregister_device(client);
+
+ return 0;
+}
+
+static void peci_adapter_dev_release(struct device *dev)
+{
+ struct peci_adapter *adapter = to_peci_adapter(dev);
+
+ dev_dbg(dev, "%s: %s\n", __func__, adapter->name);
+ mutex_destroy(&adapter->userspace_clients_lock);
+ mutex_destroy(&adapter->bus_lock);
+ kfree(adapter);
+}
+
+static ssize_t peci_sysfs_new_device(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct peci_adapter *adapter = to_peci_adapter(dev);
+ struct peci_board_info info = {};
+ struct peci_client *client;
+ char *blank, end;
+ short addr;
+ int ret;
+
+ /* Parse device type */
+ blank = strchr(buf, ' ');
+ if (!blank) {
+ dev_err(dev, "%s: Missing parameters\n", "new_device");
+ return -EINVAL;
+ }
+ if (blank - buf > PECI_NAME_SIZE - 1) {
+ dev_err(dev, "%s: Invalid device type\n", "new_device");
+ return -EINVAL;
+ }
+ memcpy(info.type, buf, blank - buf);
+
+ /* Parse remaining parameters, reject extra parameters */
+ ret = sscanf(++blank, "%hi%c", &addr, &end);
+ if (ret < 1) {
+ dev_err(dev, "%s: Can't parse client address\n", "new_device");
+ return -EINVAL;
+ }
+ if (ret > 1 && end != '\n') {
+ dev_err(dev, "%s: Extra parameters\n", "new_device");
+ return -EINVAL;
+ }
+
+ info.addr = (u8)addr;
+ client = peci_new_device(adapter, &info);
+ if (!client)
+ return -EINVAL;
+
+ /* Keep track of the added device */
+ mutex_lock(&adapter->userspace_clients_lock);
+ list_add_tail(&client->detected, &adapter->userspace_clients);
+ mutex_unlock(&adapter->userspace_clients_lock);
+ dev_dbg(dev, "%s: Instantiated device %s at 0x%02hx\n", "new_device",
+ info.type, info.addr);
+
+ return count;
+}
+static DEVICE_ATTR(new_device, 0200, NULL, peci_sysfs_new_device);
+
+static ssize_t peci_sysfs_delete_device(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct peci_adapter *adapter = to_peci_adapter(dev);
+ struct peci_client *client, *next;
+ struct peci_board_info info = {};
+ char *blank, end;
+ short addr;
+ int ret;
+
+ /* Parse device type */
+ blank = strchr(buf, ' ');
+ if (!blank) {
+ dev_err(dev, "%s: Missing parameters\n", "delete_device");
+ return -EINVAL;
+ }
+ if (blank - buf > PECI_NAME_SIZE - 1) {
+ dev_err(dev, "%s: Invalid device type\n", "delete_device");
+ return -EINVAL;
+ }
+ memcpy(info.type, buf, blank - buf);
+
+ /* Parse remaining parameters, reject extra parameters */
+ ret = sscanf(++blank, "%hi%c", &addr, &end);
+ if (ret < 1) {
+ dev_err(dev, "%s: Can't parse client address\n",
+ "delete_device");
+ return -EINVAL;
+ }
+ if (ret > 1 && end != '\n') {
+ dev_err(dev, "%s: Extra parameters\n", "delete_device");
+ return -EINVAL;
+ }
+
+ info.addr = (u8)addr;
+
+ /* Make sure the device was added through sysfs */
+ ret = -ENOENT;
+ mutex_lock(&adapter->userspace_clients_lock);
+ list_for_each_entry_safe(client, next, &adapter->userspace_clients,
+ detected) {
+ if (client->addr == info.addr &&
+ !strncmp(client->name, info.type, PECI_NAME_SIZE)) {
+ dev_dbg(dev, "%s: Deleting device %s at 0x%02hx\n",
+ "delete_device", client->name, client->addr);
+ list_del(&client->detected);
+ peci_unregister_device(client);
+ ret = count;
+ break;
+ }
+ }
+ mutex_unlock(&adapter->userspace_clients_lock);
+
+ if (ret < 0)
+ dev_dbg(dev, "%s: Can't find device in list\n",
+ "delete_device");
+
+ return ret;
+}
+static DEVICE_ATTR_IGNORE_LOCKDEP(delete_device, 0200, NULL,
+ peci_sysfs_delete_device);
+
+static struct attribute *peci_adapter_attrs[] = {
+ &dev_attr_name.attr,
+ &dev_attr_new_device.attr,
+ &dev_attr_delete_device.attr,
+ NULL
+};
+ATTRIBUTE_GROUPS(peci_adapter);
+
+struct device_type peci_adapter_type = {
+ .groups = peci_adapter_groups,
+ .release = peci_adapter_dev_release,
+};
+EXPORT_SYMBOL_GPL(peci_adapter_type);
+
+/**
+ * peci_verify_adapter - return parameter as peci_adapter, or NULL
+ * @dev: device, probably from some driver model iterator
+ *
+ * Return: pointer to peci_adapter on success, else NULL.
+ */
+struct peci_adapter *peci_verify_adapter(struct device *dev)
+{
+ return (dev->type == &peci_adapter_type)
+ ? to_peci_adapter(dev)
+ : NULL;
+}
+EXPORT_SYMBOL_GPL(peci_verify_adapter);
+
+#if IS_ENABLED(CONFIG_OF)
+static struct peci_client *peci_of_register_device(struct peci_adapter *adapter,
+ struct device_node *node)
+{
+ struct peci_board_info info = {};
+ struct peci_client *client;
+ u32 addr;
+ int ret;
+
+ dev_dbg(&adapter->dev, "register %pOF\n", node);
+
+ ret = of_property_read_u32(node, "reg", &addr);
+ if (ret) {
+ dev_err(&adapter->dev, "invalid reg on %pOF\n", node);
+ return ERR_PTR(ret);
+ }
+
+ info.addr = addr;
+ info.of_node = node;
+
+ client = peci_new_device(adapter, &info);
+ if (!client)
+ client = ERR_PTR(-EINVAL);
+
+ return client;
+}
+
+static void peci_of_register_devices(struct peci_adapter *adapter)
+{
+ struct device_node *bus, *node;
+ struct peci_client *client;
+
+ /* Only register child devices if the adapter has a node pointer set */
+ if (!adapter->dev.of_node)
+ return;
+
+ bus = of_get_child_by_name(adapter->dev.of_node, "peci-bus");
+ if (!bus)
+ bus = of_node_get(adapter->dev.of_node);
+
+ for_each_available_child_of_node(bus, node) {
+ if (of_node_test_and_set_flag(node, OF_POPULATED))
+ continue;
+
+ client = peci_of_register_device(adapter, node);
+ if (IS_ERR(client)) {
+ dev_warn(&adapter->dev,
+ "Failed to create PECI device for %pOF\n",
+ node);
+ of_node_clear_flag(node, OF_POPULATED);
+ }
+ }
+
+ of_node_put(bus);
+}
+#else /* CONFIG_OF */
+static void peci_of_register_devices(struct peci_adapter *adapter) { }
+#endif /* CONFIG_OF */
+
+#if IS_ENABLED(CONFIG_OF_DYNAMIC)
+static int peci_of_match_node(struct device *dev, const void *data)
+{
+ return dev->of_node == data;
+}
+
+/* must call put_device() when done with returned peci_client device */
+static struct peci_client *peci_of_find_device(struct device_node *node)
+{
+ struct peci_client *client;
+ struct device *dev;
+
+ dev = bus_find_device(&peci_bus_type, NULL, node, peci_of_match_node);
+ if (!dev)
+ return NULL;
+
+ client = peci_verify_client(dev);
+ if (!client)
+ put_device(dev);
+
+ return client;
+}
+
+/* must call put_device() when done with returned peci_adapter device */
+static struct peci_adapter *peci_of_find_adapter(struct device_node *node)
+{
+ struct peci_adapter *adapter;
+ struct device *dev;
+
+ dev = bus_find_device(&peci_bus_type, NULL, node, peci_of_match_node);
+ if (!dev)
+ return NULL;
+
+ adapter = peci_verify_adapter(dev);
+ if (!adapter)
+ put_device(dev);
+
+ return adapter;
+}
+
+static int peci_of_notify(struct notifier_block *nb, ulong action, void *arg)
+{
+ struct of_reconfig_data *rd = arg;
+ struct peci_adapter *adapter;
+ struct peci_client *client;
+
+ switch (of_reconfig_get_state_change(action, rd)) {
+ case OF_RECONFIG_CHANGE_ADD:
+ adapter = peci_of_find_adapter(rd->dn->parent);
+ if (!adapter)
+ return NOTIFY_OK; /* not for us */
+
+ if (of_node_test_and_set_flag(rd->dn, OF_POPULATED)) {
+ put_device(&adapter->dev);
+ return NOTIFY_OK;
+ }
+
+ client = peci_of_register_device(adapter, rd->dn);
+ put_device(&adapter->dev);
+
+ if (IS_ERR(client)) {
+ dev_err(&adapter->dev,
+ "failed to create client for '%pOF'\n", rd->dn);
+ of_node_clear_flag(rd->dn, OF_POPULATED);
+ return notifier_from_errno(PTR_ERR(client));
+ }
+ break;
+ case OF_RECONFIG_CHANGE_REMOVE:
+ /* already depopulated? */
+ if (!of_node_check_flag(rd->dn, OF_POPULATED))
+ return NOTIFY_OK;
+
+ /* find our device by node */
+ client = peci_of_find_device(rd->dn);
+ if (!client)
+ return NOTIFY_OK; /* no? not meant for us */
+
+ /* unregister takes one ref away */
+ peci_unregister_device(client);
+
+ /* and put the reference of the find */
+ put_device(&client->dev);
+ break;
+ }
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block peci_of_notifier = {
+ .notifier_call = peci_of_notify,
+};
+#else /* CONFIG_OF_DYNAMIC */
+extern struct notifier_block peci_of_notifier;
+#endif /* CONFIG_OF_DYNAMIC */
+
+/**
+ * peci_alloc_adapter - allocate a PECI adapter
+ * @dev: the adapter, possibly using the platform_bus
+ * @size: how much zeroed driver-private data to allocate; the pointer to this
+ * memory is in the driver_data field of the returned device,
+ * accessible with peci_get_adapdata().
+ * Context: can sleep
+ *
+ * This call is used only by PECI adapter drivers, which are the only ones
+ * directly touching chip registers. It's how they allocate a peci_adapter
+ * structure, prior to calling peci_add_adapter().
+ *
+ * This must be called from context that can sleep.
+ *
+ * The caller is responsible for initializing the adapter's methods before
+ * calling peci_add_adapter(); and (after errors while adding the device)
+ * calling put_device() to prevent a memory leak.
+ *
+ * Return: the peci_adapter structure on success, else NULL.
+ */
+struct peci_adapter *peci_alloc_adapter(struct device *dev, uint size)
+{
+ struct peci_adapter *adapter;
+
+ if (!dev)
+ return NULL;
+
+ adapter = kzalloc(size + sizeof(*adapter), GFP_KERNEL);
+ if (!adapter)
+ return NULL;
+
+ device_initialize(&adapter->dev);
+ adapter->dev.parent = dev;
+ adapter->dev.bus = &peci_bus_type;
+ adapter->dev.type = &peci_adapter_type;
+ peci_set_adapdata(adapter, &adapter[1]);
+
+ return adapter;
+}
+EXPORT_SYMBOL_GPL(peci_alloc_adapter);
+
+static int peci_register_adapter(struct peci_adapter *adapter)
+{
+ int ret = -EINVAL;
+
+ /* Can't register until after driver model init */
+ if (WARN_ON(!is_registered))
+ goto err_free_idr;
+
+ if (WARN(!adapter->name[0], "peci adapter has no name"))
+ goto err_free_idr;
+
+ if (WARN(!adapter->xfer, "peci adapter has no xfer function\n"))
+ goto err_free_idr;
+
+ mutex_init(&adapter->bus_lock);
+ mutex_init(&adapter->userspace_clients_lock);
+ INIT_LIST_HEAD(&adapter->userspace_clients);
+
+ dev_set_name(&adapter->dev, "peci-%d", adapter->nr);
+
+ ret = device_add(&adapter->dev);
+ if (ret) {
+ pr_err("adapter '%s': can't add device (%d)\n",
+ adapter->name, ret);
+ goto err_free_idr;
+ }
+
+ dev_dbg(&adapter->dev, "adapter [%s] registered\n", adapter->name);
+
+ pm_runtime_no_callbacks(&adapter->dev);
+ pm_suspend_ignore_children(&adapter->dev, true);
+ pm_runtime_enable(&adapter->dev);
+
+ /* create pre-declared device nodes */
+ peci_of_register_devices(adapter);
+
+ return 0;
+
+err_free_idr:
+ mutex_lock(&core_lock);
+ idr_remove(&peci_adapter_idr, adapter->nr);
+ mutex_unlock(&core_lock);
+ return ret;
+}
+
+static int peci_add_numbered_adapter(struct peci_adapter *adapter)
+{
+ int id;
+
+ mutex_lock(&core_lock);
+ id = idr_alloc(&peci_adapter_idr, adapter,
+ adapter->nr, adapter->nr + 1, GFP_KERNEL);
+ mutex_unlock(&core_lock);
+ if (WARN(id < 0, "couldn't get idr"))
+ return id == -ENOSPC ? -EBUSY : id;
+
+ return peci_register_adapter(adapter);
+}
+
+/**
+ * peci_add_adapter - add a PECI adapter
+ * @adapter: initialized adapter, originally from peci_alloc_adapter()
+ * Context: can sleep
+ *
+ * PECI adapters connect to their drivers using some non-PECI bus,
+ * such as the platform bus. The final stage of probe() in that code
+ * includes calling peci_add_adapter() to hook up to this PECI bus glue.
+ *
+ * This must be called from context that can sleep.
+ *
+ * It returns zero on success, else a negative error code (dropping the
+ * adapter's refcount). After a successful return, the caller is responsible
+ * for calling peci_del_adapter().
+ *
+ * Return: zero on success, else a negative error code.
+ */
+int peci_add_adapter(struct peci_adapter *adapter)
+{
+ struct device *dev = &adapter->dev;
+ int id;
+
+ id = of_alias_get_id(dev->of_node, "peci");
+ if (id >= 0) {
+ adapter->nr = id;
+ return peci_add_numbered_adapter(adapter);
+ }
+
+ mutex_lock(&core_lock);
+ id = idr_alloc(&peci_adapter_idr, adapter, 0, 0, GFP_KERNEL);
+ mutex_unlock(&core_lock);
+ if (WARN(id < 0, "couldn't get idr"))
+ return id;
+
+ adapter->nr = id;
+
+ return peci_register_adapter(adapter);
+}
+EXPORT_SYMBOL_GPL(peci_add_adapter);
+
+/**
+ * peci_del_adapter - delete a PECI adapter
+ * @adapter: the adpater being deleted
+ * Context: can sleep
+ *
+ * This call is used only by PECI adpater drivers, which are the only ones
+ * directly touching chip registers.
+ *
+ * This must be called from context that can sleep.
+ *
+ * Note that this function also drops a reference to the adapter.
+ */
+void peci_del_adapter(struct peci_adapter *adapter)
+{
+ struct peci_client *client, *next;
+ struct peci_adapter *found;
+ int nr;
+
+ /* First make sure that this adapter was ever added */
+ mutex_lock(&core_lock);
+ found = idr_find(&peci_adapter_idr, adapter->nr);
+ mutex_unlock(&core_lock);
+
+ if (found != adapter)
+ return;
+
+ /* Remove devices instantiated from sysfs */
+ mutex_lock(&adapter->userspace_clients_lock);
+ list_for_each_entry_safe(client, next, &adapter->userspace_clients,
+ detected) {
+ dev_dbg(&adapter->dev, "Removing %s at 0x%x\n", client->name,
+ client->addr);
+ list_del(&client->detected);
+ peci_unregister_device(client);
+ }
+ mutex_unlock(&adapter->userspace_clients_lock);
+
+ /*
+ * Detach any active clients. This can't fail, thus we do not
+ * check the returned value.
+ */
+ device_for_each_child(&adapter->dev, NULL, peci_unregister_client);
+
+ /* device name is gone after device_unregister */
+ dev_dbg(&adapter->dev, "adapter [%s] unregistered\n", adapter->name);
+
+ pm_runtime_disable(&adapter->dev);
+ nr = adapter->nr;
+ device_unregister(&adapter->dev);
+
+ /* free bus id */
+ mutex_lock(&core_lock);
+ idr_remove(&peci_adapter_idr, nr);
+ mutex_unlock(&core_lock);
+}
+EXPORT_SYMBOL_GPL(peci_del_adapter);
+
+int peci_for_each_dev(void *data, int (*fn)(struct device *, void *))
+{
+ int ret;
+
+ mutex_lock(&core_lock);
+ ret = bus_for_each_dev(&peci_bus_type, NULL, data, fn);
+ mutex_unlock(&core_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(peci_for_each_dev);
+
+/**
+ * peci_register_driver - register a PECI driver
+ * @owner: owner module of the driver being registered
+ * @driver: the driver being registered
+ * Context: can sleep
+ *
+ * Return: zero on success, else a negative error code.
+ */
+int peci_register_driver(struct module *owner, struct peci_driver *driver)
+{
+ int ret;
+
+ /* Can't register until after driver model init */
+ if (WARN_ON(!is_registered))
+ return -EAGAIN;
+
+ /* add the driver to the list of peci drivers in the driver core */
+ driver->driver.owner = owner;
+ driver->driver.bus = &peci_bus_type;
+
+ /*
+ * When registration returns, the driver core
+ * will have called probe() for all matching-but-unbound devices.
+ */
+ ret = driver_register(&driver->driver);
+ if (ret)
+ return ret;
+
+ pr_debug("driver [%s] registered\n", driver->driver.name);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(peci_register_driver);
+
+/**
+ * peci_del_driver - unregister a PECI driver
+ * @driver: the driver being unregistered
+ * Context: can sleep
+ */
+void peci_del_driver(struct peci_driver *driver)
+{
+ driver_unregister(&driver->driver);
+ pr_debug("driver [%s] unregistered\n", driver->driver.name);
+}
+EXPORT_SYMBOL_GPL(peci_del_driver);
+
+static int __init peci_init(void)
+{
+ int ret;
+
+ ret = bus_register(&peci_bus_type);
+ if (ret < 0) {
+ pr_err("peci: Failed to register PECI bus type!\n");
+ return ret;
+ }
+
+ crc8_populate_msb(peci_crc8_table, PECI_CRC8_POLYNOMIAL);
+
+ if (IS_ENABLED(CONFIG_OF_DYNAMIC))
+ WARN_ON(of_reconfig_notifier_register(&peci_of_notifier));
+
+ is_registered = true;
+
+ return 0;
+}
+
+static void __exit peci_exit(void)
+{
+ if (IS_ENABLED(CONFIG_OF_DYNAMIC))
+ WARN_ON(of_reconfig_notifier_unregister(&peci_of_notifier));
+
+ bus_unregister(&peci_bus_type);
+}
+
+subsys_initcall(peci_init);
+module_exit(peci_exit);
+
+MODULE_AUTHOR("Jason M Biils <jason.m.bills@linux.intel.com>");
+MODULE_AUTHOR("Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>");
+MODULE_DESCRIPTION("PECI bus core module");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/peci/peci-dev.c b/drivers/peci/peci-dev.c
new file mode 100644
index 000000000000..e0fe09467a80
--- /dev/null
+++ b/drivers/peci/peci-dev.c
@@ -0,0 +1,348 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2018-2019 Intel Corporation
+
+#include <linux/cdev.h>
+#include <linux/fs.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/notifier.h>
+#include <linux/peci.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+
+/*
+ * A peci_dev represents an peci_adapter ... an PECI or SMBus master, not a
+ * slave (peci_client) with which messages will be exchanged. It's coupled
+ * with a character special file which is accessed by user mode drivers.
+ *
+ * The list of peci_dev structures is parallel to the peci_adapter lists
+ * maintained by the driver model, and is updated using bus notifications.
+ */
+struct peci_dev {
+ struct list_head list;
+ struct peci_adapter *adapter;
+ struct device *dev;
+ struct cdev cdev;
+};
+
+#define PECI_MINORS MINORMASK
+
+static dev_t peci_devt;
+static LIST_HEAD(peci_dev_list);
+static DEFINE_SPINLOCK(peci_dev_list_lock);
+
+static struct peci_dev *peci_dev_get_by_minor(uint index)
+{
+ struct peci_dev *peci_dev;
+
+ spin_lock(&peci_dev_list_lock);
+ list_for_each_entry(peci_dev, &peci_dev_list, list) {
+ if (peci_dev->adapter->nr == index)
+ goto found;
+ }
+ peci_dev = NULL;
+found:
+ spin_unlock(&peci_dev_list_lock);
+
+ return peci_dev;
+}
+
+static struct peci_dev *peci_dev_alloc(struct peci_adapter *adapter)
+{
+ struct peci_dev *peci_dev;
+
+ if (adapter->nr >= PECI_MINORS) {
+ dev_err(&adapter->dev, "Out of device minors (%d)\n",
+ adapter->nr);
+ return ERR_PTR(-ENODEV);
+ }
+
+ peci_dev = kzalloc(sizeof(*peci_dev), GFP_KERNEL);
+ if (!peci_dev)
+ return ERR_PTR(-ENOMEM);
+ peci_dev->adapter = adapter;
+
+ spin_lock(&peci_dev_list_lock);
+ list_add_tail(&peci_dev->list, &peci_dev_list);
+ spin_unlock(&peci_dev_list_lock);
+
+ return peci_dev;
+}
+
+static void peci_dev_put(struct peci_dev *peci_dev)
+{
+ spin_lock(&peci_dev_list_lock);
+ list_del(&peci_dev->list);
+ spin_unlock(&peci_dev_list_lock);
+ kfree(peci_dev);
+}
+
+static ssize_t name_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct peci_dev *peci_dev = peci_dev_get_by_minor(MINOR(dev->devt));
+
+ if (!peci_dev)
+ return -ENODEV;
+
+ return sprintf(buf, "%s\n", peci_dev->adapter->name);
+}
+static DEVICE_ATTR_RO(name);
+
+static struct attribute *peci_dev_attrs[] = {
+ &dev_attr_name.attr,
+ NULL,
+};
+ATTRIBUTE_GROUPS(peci_dev);
+
+static long peci_dev_ioctl(struct file *file, uint iocmd, ulong arg)
+{
+ struct peci_dev *peci_dev = file->private_data;
+ void __user *umsg = (void __user *)arg;
+ struct peci_xfer_msg *xmsg = NULL;
+ struct peci_xfer_msg uxmsg;
+ enum peci_cmd cmd;
+ u8 *msg = NULL;
+ uint msg_len;
+ int ret;
+
+ cmd = _IOC_NR(iocmd);
+ msg_len = _IOC_SIZE(iocmd);
+
+ switch (cmd) {
+ case PECI_CMD_XFER:
+ if (msg_len != sizeof(struct peci_xfer_msg)) {
+ ret = -EFAULT;
+ break;
+ }
+
+ if (copy_from_user(&uxmsg, umsg, msg_len)) {
+ ret = -EFAULT;
+ break;
+ }
+
+ xmsg = peci_get_xfer_msg(uxmsg.tx_len, uxmsg.rx_len);
+ if (IS_ERR(xmsg)) {
+ ret = PTR_ERR(xmsg);
+ break;
+ }
+
+ if (uxmsg.tx_len &&
+ copy_from_user(xmsg->tx_buf, (__u8 __user *)uxmsg.tx_buf,
+ uxmsg.tx_len)) {
+ ret = -EFAULT;
+ break;
+ }
+
+ xmsg->addr = uxmsg.addr;
+ xmsg->tx_len = uxmsg.tx_len;
+ xmsg->rx_len = uxmsg.rx_len;
+
+ ret = peci_command(peci_dev->adapter, cmd, xmsg);
+ if (!ret && xmsg->rx_len &&
+ copy_to_user((__u8 __user *)uxmsg.rx_buf, xmsg->rx_buf,
+ xmsg->rx_len))
+ ret = -EFAULT;
+
+ break;
+
+ default:
+ msg = memdup_user(umsg, msg_len);
+ if (IS_ERR(msg)) {
+ ret = PTR_ERR(msg);
+ break;
+ }
+
+ ret = peci_command(peci_dev->adapter, cmd, msg);
+ if ((!ret || ret == -ETIMEDOUT) &&
+ copy_to_user(umsg, msg, msg_len))
+ ret = -EFAULT;
+
+ break;
+ }
+
+ peci_put_xfer_msg(xmsg);
+ kfree(msg);
+
+ return (long)ret;
+}
+
+static int peci_dev_open(struct inode *inode, struct file *file)
+{
+ struct peci_adapter *adapter;
+ struct peci_dev *peci_dev;
+
+ peci_dev = peci_dev_get_by_minor(iminor(inode));
+ if (!peci_dev)
+ return -ENODEV;
+
+ adapter = peci_get_adapter(peci_dev->adapter->nr);
+ if (!adapter)
+ return -ENODEV;
+
+ file->private_data = peci_dev;
+
+ return 0;
+}
+
+static int peci_dev_release(struct inode *inode, struct file *file)
+{
+ struct peci_dev *peci_dev = file->private_data;
+
+ peci_put_adapter(peci_dev->adapter);
+ file->private_data = NULL;
+
+ return 0;
+}
+
+static const struct file_operations peci_dev_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = peci_dev_ioctl,
+ .open = peci_dev_open,
+ .release = peci_dev_release,
+ .llseek = no_llseek,
+};
+
+static struct class *peci_dev_class;
+
+static int peci_dev_attach_adapter(struct device *dev, void *dummy)
+{
+ struct peci_adapter *adapter;
+ struct peci_dev *peci_dev;
+ dev_t devt;
+ int ret;
+
+ if (dev->type != &peci_adapter_type)
+ return 0;
+
+ adapter = to_peci_adapter(dev);
+ peci_dev = peci_dev_alloc(adapter);
+ if (IS_ERR(peci_dev))
+ return PTR_ERR(peci_dev);
+
+ cdev_init(&peci_dev->cdev, &peci_dev_fops);
+ peci_dev->cdev.owner = THIS_MODULE;
+ devt = MKDEV(MAJOR(peci_devt), adapter->nr);
+
+ ret = cdev_add(&peci_dev->cdev, devt, 1);
+ if (ret)
+ goto err_put_dev;
+
+ /* register this peci device with the driver core */
+ peci_dev->dev = device_create(peci_dev_class, &adapter->dev, devt, NULL,
+ "peci-%d", adapter->nr);
+ if (IS_ERR(peci_dev->dev)) {
+ ret = PTR_ERR(peci_dev->dev);
+ goto err_del_cdev;
+ }
+
+ dev_info(dev, "cdev of adapter [%s] registered as minor %d\n",
+ adapter->name, adapter->nr);
+
+ return 0;
+
+err_del_cdev:
+ cdev_del(&peci_dev->cdev);
+err_put_dev:
+ peci_dev_put(peci_dev);
+
+ return ret;
+}
+
+static int peci_dev_detach_adapter(struct device *dev, void *dummy)
+{
+ struct peci_adapter *adapter;
+ struct peci_dev *peci_dev;
+ dev_t devt;
+
+ if (dev->type != &peci_adapter_type)
+ return 0;
+
+ adapter = to_peci_adapter(dev);
+ peci_dev = peci_dev_get_by_minor(adapter->nr);
+ if (!peci_dev)
+ return 0;
+
+ cdev_del(&peci_dev->cdev);
+ devt = peci_dev->dev->devt;
+ peci_dev_put(peci_dev);
+ device_destroy(peci_dev_class, devt);
+
+ dev_info(dev, "cdev of adapter [%s] unregistered\n", adapter->name);
+
+ return 0;
+}
+
+static int peci_dev_notifier_call(struct notifier_block *nb, ulong action,
+ void *data)
+{
+ struct device *dev = data;
+
+ switch (action) {
+ case BUS_NOTIFY_ADD_DEVICE:
+ return peci_dev_attach_adapter(dev, NULL);
+ case BUS_NOTIFY_DEL_DEVICE:
+ return peci_dev_detach_adapter(dev, NULL);
+ }
+
+ return 0;
+}
+
+static struct notifier_block peci_dev_notifier = {
+ .notifier_call = peci_dev_notifier_call,
+};
+
+static int __init peci_dev_init(void)
+{
+ int ret;
+
+ pr_debug("peci /dev entries driver\n");
+
+ ret = alloc_chrdev_region(&peci_devt, 0, PECI_MINORS, "peci");
+ if (ret < 0) {
+ pr_err("peci: Failed to allocate chr dev region!\n");
+ bus_unregister(&peci_bus_type);
+ goto err;
+ }
+
+ peci_dev_class = class_create(THIS_MODULE, KBUILD_MODNAME);
+ if (IS_ERR(peci_dev_class)) {
+ ret = PTR_ERR(peci_dev_class);
+ goto err_unreg_chrdev;
+ }
+ peci_dev_class->dev_groups = peci_dev_groups;
+
+ /* Keep track of adapters which will be added or removed later */
+ ret = bus_register_notifier(&peci_bus_type, &peci_dev_notifier);
+ if (ret)
+ goto err_destroy_class;
+
+ /* Bind to already existing adapters right away */
+ peci_for_each_dev(NULL, peci_dev_attach_adapter);
+
+ return 0;
+
+err_destroy_class:
+ class_destroy(peci_dev_class);
+err_unreg_chrdev:
+ unregister_chrdev_region(peci_devt, PECI_MINORS);
+err:
+ pr_err("%s: Driver Initialization failed\n", __FILE__);
+
+ return ret;
+}
+
+static void __exit peci_dev_exit(void)
+{
+ bus_unregister_notifier(&peci_bus_type, &peci_dev_notifier);
+ peci_for_each_dev(NULL, peci_dev_detach_adapter);
+ class_destroy(peci_dev_class);
+ unregister_chrdev_region(peci_devt, PECI_MINORS);
+}
+
+module_init(peci_dev_init);
+module_exit(peci_dev_exit);
+
+MODULE_AUTHOR("Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>");
+MODULE_DESCRIPTION("PECI /dev entries driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/pinctrl/aspeed/pinctrl-aspeed-g6.c b/drivers/pinctrl/aspeed/pinctrl-aspeed-g6.c
index fa32c3e9c9d1..7efe6dbe4398 100644
--- a/drivers/pinctrl/aspeed/pinctrl-aspeed-g6.c
+++ b/drivers/pinctrl/aspeed/pinctrl-aspeed-g6.c
@@ -46,6 +46,7 @@
#define SCU634 0x634 /* Disable GPIO Internal Pull-Down #5 */
#define SCU638 0x638 /* Disable GPIO Internal Pull-Down #6 */
#define SCU694 0x694 /* Multi-function Pin Control #25 */
+#define SCU69C 0x69C /* Multi-function Pin Control #27 */
#define SCUC20 0xC20 /* PCIE configuration Setting Control */
#define ASPEED_G6_NR_PINS 256
@@ -819,11 +820,13 @@ FUNC_DECL_2(PWM14, PWM14G0, PWM14G1);
#define Y23 127
SIG_EXPR_LIST_DECL_SEMG(Y23, PWM15, PWM15G1, PWM15, SIG_DESC_SET(SCU41C, 31));
SIG_EXPR_LIST_DECL_SESG(Y23, THRUOUT3, THRU3, SIG_DESC_SET(SCU4BC, 31));
-PIN_DECL_2(Y23, GPIOP7, PWM15, THRUOUT3);
+SIG_EXPR_LIST_DECL_SESG(Y23, HEARTBEAT, HEARTBEAT, SIG_DESC_SET(SCU69C, 31));
+PIN_DECL_3(Y23, GPIOP7, PWM15, THRUOUT3, HEARTBEAT);
GROUP_DECL(PWM15G1, Y23);
FUNC_DECL_2(PWM15, PWM15G0, PWM15G1);
FUNC_GROUP_DECL(THRU3, AB24, Y23);
+FUNC_GROUP_DECL(HEARTBEAT, Y23);
#define AA25 128
SSSF_PIN_DECL(AA25, GPIOQ0, TACH0, SIG_DESC_SET(SCU430, 0));
@@ -1920,6 +1923,7 @@ static const struct aspeed_pin_group aspeed_g6_groups[] = {
ASPEED_PINCTRL_GROUP(GPIU5),
ASPEED_PINCTRL_GROUP(GPIU6),
ASPEED_PINCTRL_GROUP(GPIU7),
+ ASPEED_PINCTRL_GROUP(HEARTBEAT),
ASPEED_PINCTRL_GROUP(HVI3C3),
ASPEED_PINCTRL_GROUP(HVI3C4),
ASPEED_PINCTRL_GROUP(I2C1),
@@ -2158,6 +2162,7 @@ static const struct aspeed_pin_function aspeed_g6_functions[] = {
ASPEED_PINCTRL_FUNC(GPIU5),
ASPEED_PINCTRL_FUNC(GPIU6),
ASPEED_PINCTRL_FUNC(GPIU7),
+ ASPEED_PINCTRL_FUNC(HEARTBEAT),
ASPEED_PINCTRL_FUNC(I2C1),
ASPEED_PINCTRL_FUNC(I2C10),
ASPEED_PINCTRL_FUNC(I2C11),
diff --git a/drivers/pinctrl/aspeed/pinctrl-aspeed.c b/drivers/pinctrl/aspeed/pinctrl-aspeed.c
index b625a657171e..53f3f8aec695 100644
--- a/drivers/pinctrl/aspeed/pinctrl-aspeed.c
+++ b/drivers/pinctrl/aspeed/pinctrl-aspeed.c
@@ -76,6 +76,9 @@ static int aspeed_sig_expr_enable(struct aspeed_pinmux_data *ctx,
{
int ret;
+ pr_debug("Enabling signal %s for %s\n", expr->signal,
+ expr->function);
+
ret = aspeed_sig_expr_eval(ctx, expr, true);
if (ret < 0)
return ret;
@@ -91,6 +94,9 @@ static int aspeed_sig_expr_disable(struct aspeed_pinmux_data *ctx,
{
int ret;
+ pr_debug("Disabling signal %s for %s\n", expr->signal,
+ expr->function);
+
ret = aspeed_sig_expr_eval(ctx, expr, true);
if (ret < 0)
return ret;
@@ -229,7 +235,7 @@ int aspeed_pinmux_set_mux(struct pinctrl_dev *pctldev, unsigned int function,
const struct aspeed_sig_expr **funcs;
const struct aspeed_sig_expr ***prios;
- pr_debug("Muxing pin %d for %s\n", pin, pfunc->name);
+ pr_debug("Muxing pin %s for %s\n", pdesc->name, pfunc->name);
if (!pdesc)
return -EINVAL;
@@ -269,6 +275,9 @@ int aspeed_pinmux_set_mux(struct pinctrl_dev *pctldev, unsigned int function,
ret = aspeed_sig_expr_enable(&pdata->pinmux, expr);
if (ret)
return ret;
+
+ pr_debug("Muxed pin %s as %s for %s\n", pdesc->name, expr->signal,
+ expr->function);
}
return 0;
@@ -317,6 +326,8 @@ int aspeed_gpio_request_enable(struct pinctrl_dev *pctldev,
if (!prios)
return -ENXIO;
+ pr_debug("Muxing pin %s for GPIO\n", pdesc->name);
+
/* Disable any functions of higher priority than GPIO */
while ((funcs = *prios)) {
if (aspeed_gpio_in_exprs(funcs))
@@ -346,14 +357,22 @@ int aspeed_gpio_request_enable(struct pinctrl_dev *pctldev,
* lowest-priority signal type. As such it has no associated
* expression.
*/
- if (!expr)
+ if (!expr) {
+ pr_debug("Muxed pin %s as GPIO\n", pdesc->name);
return 0;
+ }
/*
* If GPIO is not the lowest priority signal type, assume there is only
* one expression defined to enable the GPIO function
*/
- return aspeed_sig_expr_enable(&pdata->pinmux, expr);
+ ret = aspeed_sig_expr_enable(&pdata->pinmux, expr);
+ if (ret)
+ return ret;
+
+ pr_debug("Muxed pin %s as %s\n", pdesc->name, expr->signal);
+
+ return 0;
}
int aspeed_pinctrl_probe(struct platform_device *pdev,
diff --git a/drivers/reset/reset-simple.c b/drivers/reset/reset-simple.c
index 067e7e7b34f1..795c9063fe7b 100644
--- a/drivers/reset/reset-simple.c
+++ b/drivers/reset/reset-simple.c
@@ -125,6 +125,7 @@ static const struct of_device_id reset_simple_dt_ids[] = {
.data = &reset_simple_active_low },
{ .compatible = "aspeed,ast2400-lpc-reset" },
{ .compatible = "aspeed,ast2500-lpc-reset" },
+ { .compatible = "aspeed,ast2600-lpc-reset" },
{ .compatible = "bitmain,bm1880-reset",
.data = &reset_simple_active_low },
{ .compatible = "snps,dw-high-reset" },
diff --git a/drivers/soc/aspeed/Kconfig b/drivers/soc/aspeed/Kconfig
index c95fa30f1a76..2665ca89bdf8 100644
--- a/drivers/soc/aspeed/Kconfig
+++ b/drivers/soc/aspeed/Kconfig
@@ -5,6 +5,14 @@ config SOC_ASPEED
def_bool y
depends on ARCH_ASPEED || COMPILE_TEST
+config ASPEED_BMC_MISC
+ bool "Miscellaneous ASPEED BMC interfaces"
+ depends on ARCH_ASPEED || COMPILE_TEST
+ default ARCH_ASPEED
+ help
+ Say yes to expose VGA and LPC scratch registers, and other
+ miscellaneous control interfaces specific to the ASPEED BMC SoCs
+
config ASPEED_LPC_CTRL
depends on SOC_ASPEED && REGMAP && MFD_SYSCON
tristate "Aspeed ast2400/2500 HOST LPC to BMC bridge control"
@@ -29,4 +37,12 @@ config ASPEED_P2A_CTRL
ioctl()s, the driver also provides an interface for userspace mappings to
a pre-defined region.
+config ASPEED_XDMA
+ tristate "Aspeed XDMA Engine Driver"
+ depends on SOC_ASPEED && REGMAP && MFD_SYSCON && HAS_DMA
+ help
+ Enable support for the Aspeed XDMA Engine found on the Aspeed AST2XXX
+ SOCs. The XDMA engine can perform automatic PCI DMA operations
+ between the AST2XXX (acting as a BMC) and a host processor.
+
endmenu
diff --git a/drivers/soc/aspeed/Makefile b/drivers/soc/aspeed/Makefile
index b64be47f2b1f..217d876fec25 100644
--- a/drivers/soc/aspeed/Makefile
+++ b/drivers/soc/aspeed/Makefile
@@ -1,4 +1,6 @@
# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_ASPEED_BMC_MISC) += aspeed-bmc-misc.o
obj-$(CONFIG_ASPEED_LPC_CTRL) += aspeed-lpc-ctrl.o
obj-$(CONFIG_ASPEED_LPC_SNOOP) += aspeed-lpc-snoop.o
obj-$(CONFIG_ASPEED_P2A_CTRL) += aspeed-p2a-ctrl.o
+obj-$(CONFIG_ASPEED_XDMA) += aspeed-xdma.o
diff --git a/drivers/soc/aspeed/aspeed-bmc-misc.c b/drivers/soc/aspeed/aspeed-bmc-misc.c
new file mode 100644
index 000000000000..314007bad74f
--- /dev/null
+++ b/drivers/soc/aspeed/aspeed-bmc-misc.c
@@ -0,0 +1,190 @@
+// SPDX-License-Identifier: GPL-2.0+
+// Copyright 2018 IBM Corp.
+
+#include <linux/kobject.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/mfd/syscon.h>
+
+#define DEVICE_NAME "aspeed-bmc-misc"
+
+struct aspeed_bmc_ctrl {
+ const char *name;
+ u32 offset;
+ u32 mask;
+ u32 shift;
+ struct regmap *map;
+ struct kobj_attribute attr;
+};
+
+struct aspeed_bmc_misc {
+ struct device *dev;
+ struct regmap *map;
+ struct aspeed_bmc_ctrl *ctrls;
+ int nr_ctrls;
+};
+
+static int aspeed_bmc_misc_parse_dt_child(struct device_node *child,
+ struct aspeed_bmc_ctrl *ctrl)
+{
+ int rc;
+
+ /* Example child:
+ *
+ * ilpc2ahb {
+ * offset = <0x80>;
+ * bit-mask = <0x1>;
+ * bit-shift = <6>;
+ * label = "foo";
+ * }
+ */
+ if (of_property_read_string(child, "label", &ctrl->name))
+ ctrl->name = child->name;
+
+ rc = of_property_read_u32(child, "offset", &ctrl->offset);
+ if (rc < 0)
+ return rc;
+
+ rc = of_property_read_u32(child, "bit-mask", &ctrl->mask);
+ if (rc < 0)
+ return rc;
+
+ rc = of_property_read_u32(child, "bit-shift", &ctrl->shift);
+ if (rc < 0)
+ return rc;
+
+ ctrl->mask <<= ctrl->shift;
+
+ return 0;
+}
+
+static int aspeed_bmc_misc_parse_dt(struct aspeed_bmc_misc *bmc,
+ struct device_node *parent)
+{
+ struct aspeed_bmc_ctrl *ctrl;
+ struct device_node *child;
+ int rc;
+
+ bmc->nr_ctrls = of_get_child_count(parent);
+ bmc->ctrls = devm_kcalloc(bmc->dev, bmc->nr_ctrls, sizeof(*bmc->ctrls),
+ GFP_KERNEL);
+ if (!bmc->ctrls)
+ return -ENOMEM;
+
+ ctrl = bmc->ctrls;
+ for_each_child_of_node(parent, child) {
+ rc = aspeed_bmc_misc_parse_dt_child(child, ctrl++);
+ if (rc < 0) {
+ of_node_put(child);
+ return rc;
+ }
+ }
+
+ return 0;
+}
+
+static ssize_t aspeed_bmc_misc_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ struct aspeed_bmc_ctrl *ctrl;
+ unsigned int val;
+ int rc;
+
+ ctrl = container_of(attr, struct aspeed_bmc_ctrl, attr);
+ rc = regmap_read(ctrl->map, ctrl->offset, &val);
+ if (rc)
+ return rc;
+
+ val &= ctrl->mask;
+ val >>= ctrl->shift;
+
+ return sprintf(buf, "%u\n", val);
+}
+
+static ssize_t aspeed_bmc_misc_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct aspeed_bmc_ctrl *ctrl;
+ long val;
+ int rc;
+
+ rc = kstrtol(buf, 0, &val);
+ if (rc)
+ return rc;
+
+ ctrl = container_of(attr, struct aspeed_bmc_ctrl, attr);
+ val <<= ctrl->shift;
+ rc = regmap_update_bits(ctrl->map, ctrl->offset, ctrl->mask, val);
+
+ return rc < 0 ? rc : count;
+}
+
+static int aspeed_bmc_misc_add_sysfs_attr(struct aspeed_bmc_misc *bmc,
+ struct aspeed_bmc_ctrl *ctrl)
+{
+ ctrl->map = bmc->map;
+
+ sysfs_attr_init(&ctrl->attr.attr);
+ ctrl->attr.attr.name = ctrl->name;
+ ctrl->attr.attr.mode = 0664;
+ ctrl->attr.show = aspeed_bmc_misc_show;
+ ctrl->attr.store = aspeed_bmc_misc_store;
+
+ return sysfs_create_file(&bmc->dev->kobj, &ctrl->attr.attr);
+}
+
+static int aspeed_bmc_misc_populate_sysfs(struct aspeed_bmc_misc *bmc)
+{
+ int rc;
+ int i;
+
+ for (i = 0; i < bmc->nr_ctrls; i++) {
+ rc = aspeed_bmc_misc_add_sysfs_attr(bmc, &bmc->ctrls[i]);
+ if (rc < 0)
+ return rc;
+ }
+
+ return 0;
+}
+
+static int aspeed_bmc_misc_probe(struct platform_device *pdev)
+{
+ struct aspeed_bmc_misc *bmc;
+ int rc;
+
+ bmc = devm_kzalloc(&pdev->dev, sizeof(*bmc), GFP_KERNEL);
+ if (!bmc)
+ return -ENOMEM;
+
+ bmc->dev = &pdev->dev;
+ bmc->map = syscon_node_to_regmap(pdev->dev.parent->of_node);
+ if (IS_ERR(bmc->map))
+ return PTR_ERR(bmc->map);
+
+ rc = aspeed_bmc_misc_parse_dt(bmc, pdev->dev.of_node);
+ if (rc < 0)
+ return rc;
+
+ return aspeed_bmc_misc_populate_sysfs(bmc);
+}
+
+static const struct of_device_id aspeed_bmc_misc_match[] = {
+ { .compatible = "aspeed,bmc-misc" },
+ { },
+};
+
+static struct platform_driver aspeed_bmc_misc = {
+ .driver = {
+ .name = DEVICE_NAME,
+ .of_match_table = aspeed_bmc_misc_match,
+ },
+ .probe = aspeed_bmc_misc_probe,
+};
+
+module_platform_driver(aspeed_bmc_misc);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Andrew Jeffery <andrew@aj.id.au>");
diff --git a/drivers/soc/aspeed/aspeed-lpc-ctrl.c b/drivers/soc/aspeed/aspeed-lpc-ctrl.c
index 01ed21e8bfee..ee2def4ffda3 100644
--- a/drivers/soc/aspeed/aspeed-lpc-ctrl.c
+++ b/drivers/soc/aspeed/aspeed-lpc-ctrl.c
@@ -4,6 +4,7 @@
*/
#include <linux/clk.h>
+#include <linux/log2.h>
#include <linux/mfd/syscon.h>
#include <linux/miscdevice.h>
#include <linux/mm.h>
@@ -21,6 +22,9 @@
#define HICR5_ENL2H BIT(8)
#define HICR5_ENFWH BIT(10)
+#define HICR6 0x4
+#define SW_FWH2AHB BIT(17)
+
#define HICR7 0x8
#define HICR8 0xc
@@ -32,6 +36,7 @@ struct aspeed_lpc_ctrl {
resource_size_t mem_size;
u32 pnor_size;
u32 pnor_base;
+ bool fwh2ahb;
};
static struct aspeed_lpc_ctrl *file_aspeed_lpc_ctrl(struct file *file)
@@ -177,6 +182,16 @@ static long aspeed_lpc_ctrl_ioctl(struct file *file, unsigned int cmd,
return rc;
/*
+ * Switch to FWH2AHB mode, AST2600 only.
+ *
+ * The other bits in this register are interrupt status bits
+ * that are cleared by writing 1. As we don't want to clear
+ * them, set only the bit of interest.
+ */
+ if (lpc_ctrl->fwh2ahb)
+ regmap_write(lpc_ctrl->regmap, HICR6, SW_FWH2AHB);
+
+ /*
* Enable LPC FHW cycles. This is required for the host to
* access the regions specified.
*/
@@ -241,6 +256,18 @@ static int aspeed_lpc_ctrl_probe(struct platform_device *pdev)
lpc_ctrl->mem_size = resource_size(&resm);
lpc_ctrl->mem_base = resm.start;
+
+ if (!is_power_of_2(lpc_ctrl->mem_size)) {
+ dev_err(dev, "Reserved memory size must be a power of 2, got %u\n",
+ (unsigned int)lpc_ctrl->mem_size);
+ return -EINVAL;
+ }
+
+ if (!IS_ALIGNED(lpc_ctrl->mem_base, lpc_ctrl->mem_size)) {
+ dev_err(dev, "Reserved memory must be naturally aligned for size %u\n",
+ (unsigned int)lpc_ctrl->mem_size);
+ return -EINVAL;
+ }
}
lpc_ctrl->regmap = syscon_node_to_regmap(
@@ -261,6 +288,9 @@ static int aspeed_lpc_ctrl_probe(struct platform_device *pdev)
return rc;
}
+ if (of_device_is_compatible(dev->of_node, "aspeed,ast2600-lpc-ctrl"))
+ lpc_ctrl->fwh2ahb = true;
+
lpc_ctrl->miscdev.minor = MISC_DYNAMIC_MINOR;
lpc_ctrl->miscdev.name = DEVICE_NAME;
lpc_ctrl->miscdev.fops = &aspeed_lpc_ctrl_fops;
@@ -291,6 +321,7 @@ static int aspeed_lpc_ctrl_remove(struct platform_device *pdev)
static const struct of_device_id aspeed_lpc_ctrl_match[] = {
{ .compatible = "aspeed,ast2400-lpc-ctrl" },
{ .compatible = "aspeed,ast2500-lpc-ctrl" },
+ { .compatible = "aspeed,ast2600-lpc-ctrl" },
{ },
};
diff --git a/drivers/soc/aspeed/aspeed-lpc-snoop.c b/drivers/soc/aspeed/aspeed-lpc-snoop.c
index f3d8d53ab84d..682ba0eb4eba 100644
--- a/drivers/soc/aspeed/aspeed-lpc-snoop.c
+++ b/drivers/soc/aspeed/aspeed-lpc-snoop.c
@@ -325,6 +325,8 @@ static const struct of_device_id aspeed_lpc_snoop_match[] = {
.data = &ast2400_model_data },
{ .compatible = "aspeed,ast2500-lpc-snoop",
.data = &ast2500_model_data },
+ { .compatible = "aspeed,ast2600-lpc-snoop",
+ .data = &ast2500_model_data },
{ },
};
diff --git a/drivers/soc/aspeed/aspeed-xdma.c b/drivers/soc/aspeed/aspeed-xdma.c
new file mode 100644
index 000000000000..4d8af9e64471
--- /dev/null
+++ b/drivers/soc/aspeed/aspeed-xdma.c
@@ -0,0 +1,1205 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+// Copyright IBM Corp 2019
+
+#include <linux/aspeed-xdma.h>
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/fs.h>
+#include <linux/genalloc.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/jiffies.h>
+#include <linux/mfd/syscon.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of_device.h>
+#include <linux/of_reserved_mem.h>
+#include <linux/platform_device.h>
+#include <linux/poll.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/string.h>
+#include <linux/uaccess.h>
+#include <linux/wait.h>
+#include <linux/workqueue.h>
+
+#define DEVICE_NAME "aspeed-xdma"
+
+#define SCU_AST2600_MISC_CTRL 0x0c0
+#define SCU_AST2600_MISC_CTRL_XDMA_BMC BIT(8)
+
+#define SCU_AST2500_PCIE_CONF 0x180
+#define SCU_AST2600_PCIE_CONF 0xc20
+#define SCU_PCIE_CONF_VGA_EN BIT(0)
+#define SCU_PCIE_CONF_VGA_EN_MMIO BIT(1)
+#define SCU_PCIE_CONF_VGA_EN_LPC BIT(2)
+#define SCU_PCIE_CONF_VGA_EN_MSI BIT(3)
+#define SCU_PCIE_CONF_VGA_EN_MCTP BIT(4)
+#define SCU_PCIE_CONF_VGA_EN_IRQ BIT(5)
+#define SCU_PCIE_CONF_VGA_EN_DMA BIT(6)
+#define SCU_PCIE_CONF_BMC_EN BIT(8)
+#define SCU_PCIE_CONF_BMC_EN_MMIO BIT(9)
+#define SCU_PCIE_CONF_BMC_EN_MSI BIT(11)
+#define SCU_PCIE_CONF_BMC_EN_MCTP BIT(12)
+#define SCU_PCIE_CONF_BMC_EN_IRQ BIT(13)
+#define SCU_PCIE_CONF_BMC_EN_DMA BIT(14)
+
+#define SCU_AST2500_BMC_CLASS_REV 0x19c
+#define SCU_AST2600_BMC_CLASS_REV 0xc68
+#define SCU_BMC_CLASS_REV_XDMA 0xff000001
+
+#define XDMA_CMDQ_SIZE PAGE_SIZE
+#define XDMA_NUM_CMDS \
+ (XDMA_CMDQ_SIZE / sizeof(struct aspeed_xdma_cmd))
+
+/* Aspeed specification requires 100us after disabling the reset */
+#define XDMA_ENGINE_SETUP_TIME_MAX_US 1000
+#define XDMA_ENGINE_SETUP_TIME_MIN_US 100
+
+#define XDMA_CMD_AST2500_PITCH_SHIFT 3
+#define XDMA_CMD_AST2500_PITCH_BMC GENMASK_ULL(62, 51)
+#define XDMA_CMD_AST2500_PITCH_HOST GENMASK_ULL(46, 35)
+#define XDMA_CMD_AST2500_PITCH_UPSTREAM BIT_ULL(31)
+#define XDMA_CMD_AST2500_PITCH_ADDR GENMASK_ULL(29, 4)
+#define XDMA_CMD_AST2500_PITCH_ID BIT_ULL(0)
+#define XDMA_CMD_AST2500_CMD_IRQ_EN BIT_ULL(31)
+#define XDMA_CMD_AST2500_CMD_LINE_NO GENMASK_ULL(27, 16)
+#define XDMA_CMD_AST2500_CMD_IRQ_BMC BIT_ULL(15)
+#define XDMA_CMD_AST2500_CMD_LINE_SIZE_SHIFT 4
+#define XDMA_CMD_AST2500_CMD_LINE_SIZE \
+ GENMASK_ULL(14, XDMA_CMD_AST2500_CMD_LINE_SIZE_SHIFT)
+#define XDMA_CMD_AST2500_CMD_ID BIT_ULL(1)
+
+#define XDMA_CMD_AST2600_PITCH_BMC GENMASK_ULL(62, 48)
+#define XDMA_CMD_AST2600_PITCH_HOST GENMASK_ULL(46, 32)
+#define XDMA_CMD_AST2600_PITCH_ADDR GENMASK_ULL(30, 0)
+#define XDMA_CMD_AST2600_CMD_64_EN BIT_ULL(40)
+#define XDMA_CMD_AST2600_CMD_IRQ_BMC BIT_ULL(37)
+#define XDMA_CMD_AST2600_CMD_IRQ_HOST BIT_ULL(36)
+#define XDMA_CMD_AST2600_CMD_UPSTREAM BIT_ULL(32)
+#define XDMA_CMD_AST2600_CMD_LINE_NO GENMASK_ULL(27, 16)
+#define XDMA_CMD_AST2600_CMD_LINE_SIZE GENMASK_ULL(14, 0)
+#define XDMA_CMD_AST2600_CMD_MULTILINE_SIZE GENMASK_ULL(14, 12)
+
+#define XDMA_AST2500_QUEUE_ENTRY_SIZE 4
+#define XDMA_AST2500_HOST_CMDQ_ADDR0 0x00
+#define XDMA_AST2500_HOST_CMDQ_ENDP 0x04
+#define XDMA_AST2500_HOST_CMDQ_WRITEP 0x08
+#define XDMA_AST2500_HOST_CMDQ_READP 0x0c
+#define XDMA_AST2500_BMC_CMDQ_ADDR 0x10
+#define XDMA_AST2500_BMC_CMDQ_ENDP 0x14
+#define XDMA_AST2500_BMC_CMDQ_WRITEP 0x18
+#define XDMA_AST2500_BMC_CMDQ_READP 0x1c
+#define XDMA_BMC_CMDQ_READP_RESET 0xee882266
+#define XDMA_AST2500_CTRL 0x20
+#define XDMA_AST2500_CTRL_US_COMP BIT(4)
+#define XDMA_AST2500_CTRL_DS_COMP BIT(5)
+#define XDMA_AST2500_CTRL_DS_DIRTY BIT(6)
+#define XDMA_AST2500_CTRL_DS_SIZE_256 BIT(17)
+#define XDMA_AST2500_CTRL_DS_TIMEOUT BIT(28)
+#define XDMA_AST2500_CTRL_DS_CHECK_ID BIT(29)
+#define XDMA_AST2500_STATUS 0x24
+#define XDMA_AST2500_STATUS_US_COMP BIT(4)
+#define XDMA_AST2500_STATUS_DS_COMP BIT(5)
+#define XDMA_AST2500_STATUS_DS_DIRTY BIT(6)
+#define XDMA_AST2500_INPRG_DS_CMD1 0x38
+#define XDMA_AST2500_INPRG_DS_CMD2 0x3c
+#define XDMA_AST2500_INPRG_US_CMD00 0x40
+#define XDMA_AST2500_INPRG_US_CMD01 0x44
+#define XDMA_AST2500_INPRG_US_CMD10 0x48
+#define XDMA_AST2500_INPRG_US_CMD11 0x4c
+#define XDMA_AST2500_INPRG_US_CMD20 0x50
+#define XDMA_AST2500_INPRG_US_CMD21 0x54
+#define XDMA_AST2500_HOST_CMDQ_ADDR1 0x60
+#define XDMA_AST2500_VGA_CMDQ_ADDR0 0x64
+#define XDMA_AST2500_VGA_CMDQ_ENDP 0x68
+#define XDMA_AST2500_VGA_CMDQ_WRITEP 0x6c
+#define XDMA_AST2500_VGA_CMDQ_READP 0x70
+#define XDMA_AST2500_VGA_CMD_STATUS 0x74
+#define XDMA_AST2500_VGA_CMDQ_ADDR1 0x78
+
+#define XDMA_AST2600_QUEUE_ENTRY_SIZE 2
+#define XDMA_AST2600_HOST_CMDQ_ADDR0 0x00
+#define XDMA_AST2600_HOST_CMDQ_ADDR1 0x04
+#define XDMA_AST2600_HOST_CMDQ_ENDP 0x08
+#define XDMA_AST2600_HOST_CMDQ_WRITEP 0x0c
+#define XDMA_AST2600_HOST_CMDQ_READP 0x10
+#define XDMA_AST2600_BMC_CMDQ_ADDR 0x14
+#define XDMA_AST2600_BMC_CMDQ_ENDP 0x18
+#define XDMA_AST2600_BMC_CMDQ_WRITEP 0x1c
+#define XDMA_AST2600_BMC_CMDQ_READP 0x20
+#define XDMA_AST2600_VGA_CMDQ_ADDR0 0x24
+#define XDMA_AST2600_VGA_CMDQ_ADDR1 0x28
+#define XDMA_AST2600_VGA_CMDQ_ENDP 0x2c
+#define XDMA_AST2600_VGA_CMDQ_WRITEP 0x30
+#define XDMA_AST2600_VGA_CMDQ_READP 0x34
+#define XDMA_AST2600_CTRL 0x38
+#define XDMA_AST2600_CTRL_US_COMP BIT(16)
+#define XDMA_AST2600_CTRL_DS_COMP BIT(17)
+#define XDMA_AST2600_CTRL_DS_DIRTY BIT(18)
+#define XDMA_AST2600_CTRL_DS_SIZE_256 BIT(20)
+#define XDMA_AST2600_STATUS 0x3c
+#define XDMA_AST2600_STATUS_US_COMP BIT(16)
+#define XDMA_AST2600_STATUS_DS_COMP BIT(17)
+#define XDMA_AST2600_STATUS_DS_DIRTY BIT(18)
+#define XDMA_AST2600_INPRG_DS_CMD00 0x40
+#define XDMA_AST2600_INPRG_DS_CMD01 0x44
+#define XDMA_AST2600_INPRG_DS_CMD10 0x48
+#define XDMA_AST2600_INPRG_DS_CMD11 0x4c
+#define XDMA_AST2600_INPRG_DS_CMD20 0x50
+#define XDMA_AST2600_INPRG_DS_CMD21 0x54
+#define XDMA_AST2600_INPRG_US_CMD00 0x60
+#define XDMA_AST2600_INPRG_US_CMD01 0x64
+#define XDMA_AST2600_INPRG_US_CMD10 0x68
+#define XDMA_AST2600_INPRG_US_CMD11 0x6c
+#define XDMA_AST2600_INPRG_US_CMD20 0x70
+#define XDMA_AST2600_INPRG_US_CMD21 0x74
+
+struct aspeed_xdma_cmd {
+ u64 host_addr;
+ u64 pitch;
+ u64 cmd;
+ u64 reserved;
+};
+
+struct aspeed_xdma_regs {
+ u8 bmc_cmdq_addr;
+ u8 bmc_cmdq_endp;
+ u8 bmc_cmdq_writep;
+ u8 bmc_cmdq_readp;
+ u8 control;
+ u8 status;
+};
+
+struct aspeed_xdma_status_bits {
+ u32 us_comp;
+ u32 ds_comp;
+ u32 ds_dirty;
+};
+
+struct aspeed_xdma;
+
+struct aspeed_xdma_chip {
+ u32 control;
+ u32 scu_bmc_class;
+ u32 scu_misc_ctrl;
+ u32 scu_pcie_conf;
+ unsigned int queue_entry_size;
+ struct aspeed_xdma_regs regs;
+ struct aspeed_xdma_status_bits status_bits;
+ unsigned int (*set_cmd)(struct aspeed_xdma *ctx,
+ struct aspeed_xdma_cmd cmds[2],
+ struct aspeed_xdma_op *op, u32 bmc_addr);
+};
+
+struct aspeed_xdma_client;
+
+struct aspeed_xdma {
+ struct kobject kobj;
+ const struct aspeed_xdma_chip *chip;
+
+ int irq;
+ int pcie_irq;
+ struct clk *clock;
+ struct device *dev;
+ void __iomem *base;
+ resource_size_t res_size;
+ resource_size_t res_start;
+ struct reset_control *reset;
+ struct reset_control *reset_rc;
+
+ /* Protects current_client */
+ spinlock_t client_lock;
+ struct aspeed_xdma_client *current_client;
+
+ /* Protects engine configuration */
+ spinlock_t engine_lock;
+ struct aspeed_xdma_cmd *cmdq;
+ unsigned int cmd_idx;
+ bool in_reset;
+ bool upstream;
+
+ /* Queue waiters for idle engine */
+ wait_queue_head_t wait;
+
+ struct work_struct reset_work;
+
+ u32 mem_phys;
+ u32 mem_size;
+ void *mem_virt;
+ dma_addr_t mem_coherent;
+ dma_addr_t cmdq_phys;
+ struct gen_pool *pool;
+
+ struct miscdevice misc;
+};
+
+struct aspeed_xdma_client {
+ struct aspeed_xdma *ctx;
+
+ bool error;
+ bool in_progress;
+ void *virt;
+ dma_addr_t phys;
+ u32 size;
+};
+
+static u32 aspeed_xdma_readl(struct aspeed_xdma *ctx, u8 reg)
+{
+ u32 v = readl(ctx->base + reg);
+
+ dev_dbg(ctx->dev, "read %02x[%08x]\n", reg, v);
+ return v;
+}
+
+static void aspeed_xdma_writel(struct aspeed_xdma *ctx, u8 reg, u32 val)
+{
+ writel(val, ctx->base + reg);
+ dev_dbg(ctx->dev, "write %02x[%08x]\n", reg, val);
+}
+
+static void aspeed_xdma_init_eng(struct aspeed_xdma *ctx)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&ctx->engine_lock, flags);
+ aspeed_xdma_writel(ctx, ctx->chip->regs.bmc_cmdq_endp,
+ ctx->chip->queue_entry_size * XDMA_NUM_CMDS);
+ aspeed_xdma_writel(ctx, ctx->chip->regs.bmc_cmdq_readp,
+ XDMA_BMC_CMDQ_READP_RESET);
+ aspeed_xdma_writel(ctx, ctx->chip->regs.bmc_cmdq_writep, 0);
+ aspeed_xdma_writel(ctx, ctx->chip->regs.control, ctx->chip->control);
+ aspeed_xdma_writel(ctx, ctx->chip->regs.bmc_cmdq_addr, ctx->cmdq_phys);
+
+ ctx->cmd_idx = 0;
+ spin_unlock_irqrestore(&ctx->engine_lock, flags);
+}
+
+static unsigned int aspeed_xdma_ast2500_set_cmd(struct aspeed_xdma *ctx,
+ struct aspeed_xdma_cmd cmds[2],
+ struct aspeed_xdma_op *op,
+ u32 bmc_addr)
+{
+ unsigned int rc = 1;
+ unsigned int pitch = 1;
+ unsigned int line_no = 1;
+ unsigned int line_size = op->len >>
+ XDMA_CMD_AST2500_CMD_LINE_SIZE_SHIFT;
+ u64 cmd = XDMA_CMD_AST2500_CMD_IRQ_EN | XDMA_CMD_AST2500_CMD_IRQ_BMC |
+ XDMA_CMD_AST2500_CMD_ID;
+ u64 cmd_pitch = (op->direction ? XDMA_CMD_AST2500_PITCH_UPSTREAM : 0) |
+ XDMA_CMD_AST2500_PITCH_ID;
+
+ dev_dbg(ctx->dev, "xdma %s ast2500: bmc[%08x] len[%08x] host[%08x]\n",
+ op->direction ? "upstream" : "downstream", bmc_addr, op->len,
+ (u32)op->host_addr);
+
+ if (op->len > XDMA_CMD_AST2500_CMD_LINE_SIZE) {
+ unsigned int rem;
+ unsigned int total;
+
+ line_no = op->len / XDMA_CMD_AST2500_CMD_LINE_SIZE;
+ total = XDMA_CMD_AST2500_CMD_LINE_SIZE * line_no;
+ rem = (op->len - total) >>
+ XDMA_CMD_AST2500_CMD_LINE_SIZE_SHIFT;
+ line_size = XDMA_CMD_AST2500_CMD_LINE_SIZE;
+ pitch = line_size >> XDMA_CMD_AST2500_PITCH_SHIFT;
+ line_size >>= XDMA_CMD_AST2500_CMD_LINE_SIZE_SHIFT;
+
+ if (rem) {
+ u32 rbmc = bmc_addr + total;
+
+ cmds[1].host_addr = op->host_addr + (u64)total;
+ cmds[1].pitch = cmd_pitch |
+ ((u64)rbmc & XDMA_CMD_AST2500_PITCH_ADDR) |
+ FIELD_PREP(XDMA_CMD_AST2500_PITCH_HOST, 1) |
+ FIELD_PREP(XDMA_CMD_AST2500_PITCH_BMC, 1);
+ cmds[1].cmd = cmd |
+ FIELD_PREP(XDMA_CMD_AST2500_CMD_LINE_NO, 1) |
+ FIELD_PREP(XDMA_CMD_AST2500_CMD_LINE_SIZE,
+ rem);
+ cmds[1].reserved = 0ULL;
+
+ print_hex_dump_debug("xdma rem ", DUMP_PREFIX_OFFSET,
+ 16, 1, &cmds[1], sizeof(*cmds),
+ true);
+
+ cmd &= ~(XDMA_CMD_AST2500_CMD_IRQ_EN |
+ XDMA_CMD_AST2500_CMD_IRQ_BMC);
+
+ rc++;
+ }
+ }
+
+ cmds[0].host_addr = op->host_addr;
+ cmds[0].pitch = cmd_pitch |
+ ((u64)bmc_addr & XDMA_CMD_AST2500_PITCH_ADDR) |
+ FIELD_PREP(XDMA_CMD_AST2500_PITCH_HOST, pitch) |
+ FIELD_PREP(XDMA_CMD_AST2500_PITCH_BMC, pitch);
+ cmds[0].cmd = cmd | FIELD_PREP(XDMA_CMD_AST2500_CMD_LINE_NO, line_no) |
+ FIELD_PREP(XDMA_CMD_AST2500_CMD_LINE_SIZE, line_size);
+ cmds[0].reserved = 0ULL;
+
+ print_hex_dump_debug("xdma cmd ", DUMP_PREFIX_OFFSET, 16, 1, cmds,
+ sizeof(*cmds), true);
+
+ return rc;
+}
+
+static unsigned int aspeed_xdma_ast2600_set_cmd(struct aspeed_xdma *ctx,
+ struct aspeed_xdma_cmd cmds[2],
+ struct aspeed_xdma_op *op,
+ u32 bmc_addr)
+{
+ unsigned int rc = 1;
+ unsigned int pitch = 1;
+ unsigned int line_no = 1;
+ unsigned int line_size = op->len;
+ u64 cmd = XDMA_CMD_AST2600_CMD_IRQ_BMC |
+ (op->direction ? XDMA_CMD_AST2600_CMD_UPSTREAM : 0);
+
+ if (op->host_addr & 0xffffffff00000000ULL ||
+ (op->host_addr + (u64)op->len) & 0xffffffff00000000ULL)
+ cmd |= XDMA_CMD_AST2600_CMD_64_EN;
+
+ dev_dbg(ctx->dev, "xdma %s ast2600: bmc[%08x] len[%08x] "
+ "host[%016llx]\n", op->direction ? "upstream" : "downstream",
+ bmc_addr, op->len, op->host_addr);
+
+ if (op->len > XDMA_CMD_AST2600_CMD_LINE_SIZE) {
+ unsigned int rem;
+ unsigned int total;
+
+ line_no = op->len / XDMA_CMD_AST2600_CMD_MULTILINE_SIZE;
+ total = XDMA_CMD_AST2600_CMD_MULTILINE_SIZE * line_no;
+ rem = op->len - total;
+ line_size = XDMA_CMD_AST2600_CMD_MULTILINE_SIZE;
+ pitch = line_size;
+
+ if (rem) {
+ u32 rbmc = bmc_addr + total;
+
+ cmds[1].host_addr = op->host_addr + (u64)total;
+ cmds[1].pitch =
+ ((u64)rbmc & XDMA_CMD_AST2600_PITCH_ADDR) |
+ FIELD_PREP(XDMA_CMD_AST2600_PITCH_HOST, 1) |
+ FIELD_PREP(XDMA_CMD_AST2600_PITCH_BMC, 1);
+ cmds[1].cmd = cmd |
+ FIELD_PREP(XDMA_CMD_AST2600_CMD_LINE_NO, 1) |
+ FIELD_PREP(XDMA_CMD_AST2600_CMD_LINE_SIZE,
+ rem);
+ cmds[1].reserved = 0ULL;
+
+ print_hex_dump_debug("xdma rem ", DUMP_PREFIX_OFFSET,
+ 16, 1, &cmds[1], sizeof(*cmds),
+ true);
+
+ cmd &= ~XDMA_CMD_AST2600_CMD_IRQ_BMC;
+
+ rc++;
+ }
+ }
+
+ cmds[0].host_addr = op->host_addr;
+ cmds[0].pitch = ((u64)bmc_addr & XDMA_CMD_AST2600_PITCH_ADDR) |
+ FIELD_PREP(XDMA_CMD_AST2600_PITCH_HOST, pitch) |
+ FIELD_PREP(XDMA_CMD_AST2600_PITCH_BMC, pitch);
+ cmds[0].cmd = cmd | FIELD_PREP(XDMA_CMD_AST2600_CMD_LINE_NO, line_no) |
+ FIELD_PREP(XDMA_CMD_AST2600_CMD_LINE_SIZE, line_size);
+ cmds[0].reserved = 0ULL;
+
+ print_hex_dump_debug("xdma cmd ", DUMP_PREFIX_OFFSET, 16, 1, cmds,
+ sizeof(*cmds), true);
+
+ return rc;
+}
+
+static int aspeed_xdma_start(struct aspeed_xdma *ctx, unsigned int num_cmds,
+ struct aspeed_xdma_cmd cmds[2], bool upstream,
+ struct aspeed_xdma_client *client)
+{
+ unsigned int i;
+ int rc = -EBUSY;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ctx->engine_lock, flags);
+ if (ctx->in_reset)
+ goto unlock;
+
+ spin_lock(&ctx->client_lock);
+ if (ctx->current_client) {
+ spin_unlock(&ctx->client_lock);
+ goto unlock;
+ }
+
+ client->error = false;
+ client->in_progress = true;
+ ctx->current_client = client;
+ spin_unlock(&ctx->client_lock);
+
+ ctx->upstream = upstream;
+ for (i = 0; i < num_cmds; ++i) {
+ /*
+ * Use memcpy_toio here to get some barriers before starting
+ * the operation. The command(s) need to be in physical memory
+ * before the XDMA engine starts.
+ */
+ memcpy_toio(&ctx->cmdq[ctx->cmd_idx], &cmds[i],
+ sizeof(struct aspeed_xdma_cmd));
+ ctx->cmd_idx = (ctx->cmd_idx + 1) % XDMA_NUM_CMDS;
+ }
+
+ aspeed_xdma_writel(ctx, ctx->chip->regs.bmc_cmdq_writep,
+ ctx->cmd_idx * ctx->chip->queue_entry_size);
+ rc = 0;
+
+unlock:
+ spin_unlock_irqrestore(&ctx->engine_lock, flags);
+ return rc;
+}
+
+static void aspeed_xdma_done(struct aspeed_xdma *ctx, bool error)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&ctx->client_lock, flags);
+ if (ctx->current_client) {
+ ctx->current_client->error = error;
+ ctx->current_client->in_progress = false;
+ ctx->current_client = NULL;
+ }
+ spin_unlock_irqrestore(&ctx->client_lock, flags);
+
+ wake_up_interruptible_all(&ctx->wait);
+}
+
+static irqreturn_t aspeed_xdma_irq(int irq, void *arg)
+{
+ struct aspeed_xdma *ctx = arg;
+ u32 status;
+
+ spin_lock(&ctx->engine_lock);
+ status = aspeed_xdma_readl(ctx, ctx->chip->regs.status);
+
+ if (status & ctx->chip->status_bits.ds_dirty) {
+ aspeed_xdma_done(ctx, true);
+ } else {
+ if (status & ctx->chip->status_bits.us_comp) {
+ if (ctx->upstream)
+ aspeed_xdma_done(ctx, false);
+ }
+
+ if (status & ctx->chip->status_bits.ds_comp) {
+ if (!ctx->upstream)
+ aspeed_xdma_done(ctx, false);
+ }
+ }
+
+ aspeed_xdma_writel(ctx, ctx->chip->regs.status, status);
+ spin_unlock(&ctx->engine_lock);
+
+ return IRQ_HANDLED;
+}
+
+static void aspeed_xdma_reset(struct aspeed_xdma *ctx)
+{
+ unsigned long flags;
+
+ reset_control_assert(ctx->reset);
+ usleep_range(XDMA_ENGINE_SETUP_TIME_MIN_US,
+ XDMA_ENGINE_SETUP_TIME_MAX_US);
+ reset_control_deassert(ctx->reset);
+ usleep_range(XDMA_ENGINE_SETUP_TIME_MIN_US,
+ XDMA_ENGINE_SETUP_TIME_MAX_US);
+
+ aspeed_xdma_init_eng(ctx);
+
+ aspeed_xdma_done(ctx, true);
+
+ spin_lock_irqsave(&ctx->engine_lock, flags);
+ ctx->in_reset = false;
+ spin_unlock_irqrestore(&ctx->engine_lock, flags);
+
+ wake_up_interruptible(&ctx->wait);
+}
+
+static void aspeed_xdma_reset_work(struct work_struct *work)
+{
+ struct aspeed_xdma *ctx = container_of(work, struct aspeed_xdma,
+ reset_work);
+
+ aspeed_xdma_reset(ctx);
+}
+
+static irqreturn_t aspeed_xdma_pcie_irq(int irq, void *arg)
+{
+ struct aspeed_xdma *ctx = arg;
+
+ dev_dbg(ctx->dev, "PCI-E reset requested.\n");
+
+ spin_lock(&ctx->engine_lock);
+ if (ctx->in_reset) {
+ spin_unlock(&ctx->engine_lock);
+ return IRQ_HANDLED;
+ }
+
+ ctx->in_reset = true;
+ spin_unlock(&ctx->engine_lock);
+
+ schedule_work(&ctx->reset_work);
+ return IRQ_HANDLED;
+}
+
+static ssize_t aspeed_xdma_write(struct file *file, const char __user *buf,
+ size_t len, loff_t *offset)
+{
+ int rc;
+ unsigned int num_cmds;
+ struct aspeed_xdma_op op;
+ struct aspeed_xdma_cmd cmds[2];
+ struct aspeed_xdma_client *client = file->private_data;
+ struct aspeed_xdma *ctx = client->ctx;
+
+ if (len != sizeof(op))
+ return -EINVAL;
+
+ rc = copy_from_user(&op, buf, len);
+ if (rc)
+ return rc;
+
+ if (!op.len || op.len > client->size ||
+ op.direction > ASPEED_XDMA_DIRECTION_UPSTREAM)
+ return -EINVAL;
+
+ num_cmds = ctx->chip->set_cmd(ctx, cmds, &op, client->phys);
+ do {
+ rc = aspeed_xdma_start(ctx, num_cmds, cmds, !!op.direction,
+ client);
+ if (!rc)
+ break;
+
+ if ((file->f_flags & O_NONBLOCK) || rc != -EBUSY)
+ return rc;
+
+ rc = wait_event_interruptible(ctx->wait,
+ !(ctx->current_client ||
+ ctx->in_reset));
+ } while (!rc);
+
+ if (rc)
+ return -EINTR;
+
+ if (!(file->f_flags & O_NONBLOCK)) {
+ rc = wait_event_interruptible(ctx->wait, !client->in_progress);
+ if (rc)
+ return -EINTR;
+
+ if (client->error)
+ return -EIO;
+ }
+
+ return len;
+}
+
+static __poll_t aspeed_xdma_poll(struct file *file,
+ struct poll_table_struct *wait)
+{
+ __poll_t mask = 0;
+ __poll_t req = poll_requested_events(wait);
+ struct aspeed_xdma_client *client = file->private_data;
+ struct aspeed_xdma *ctx = client->ctx;
+
+ if (req & (EPOLLIN | EPOLLRDNORM)) {
+ if (READ_ONCE(client->in_progress))
+ poll_wait(file, &ctx->wait, wait);
+
+ if (!READ_ONCE(client->in_progress)) {
+ if (READ_ONCE(client->error))
+ mask |= EPOLLERR;
+ else
+ mask |= EPOLLIN | EPOLLRDNORM;
+ }
+ }
+
+ if (req & (EPOLLOUT | EPOLLWRNORM)) {
+ if (READ_ONCE(ctx->current_client))
+ poll_wait(file, &ctx->wait, wait);
+
+ if (!READ_ONCE(ctx->current_client))
+ mask |= EPOLLOUT | EPOLLWRNORM;
+ }
+
+ return mask;
+}
+
+static long aspeed_xdma_ioctl(struct file *file, unsigned int cmd,
+ unsigned long param)
+{
+ unsigned long flags;
+ struct aspeed_xdma_client *client = file->private_data;
+ struct aspeed_xdma *ctx = client->ctx;
+
+ switch (cmd) {
+ case ASPEED_XDMA_IOCTL_RESET:
+ spin_lock_irqsave(&ctx->engine_lock, flags);
+ if (ctx->in_reset) {
+ spin_unlock_irqrestore(&ctx->engine_lock, flags);
+ return 0;
+ }
+
+ ctx->in_reset = true;
+ spin_unlock_irqrestore(&ctx->engine_lock, flags);
+
+ if (READ_ONCE(ctx->current_client))
+ dev_warn(ctx->dev,
+ "User reset with transfer in progress.\n");
+
+ aspeed_xdma_reset(ctx);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void aspeed_xdma_vma_close(struct vm_area_struct *vma)
+{
+ int rc;
+ struct aspeed_xdma_client *client = vma->vm_private_data;
+
+ rc = wait_event_interruptible(client->ctx->wait, !client->in_progress);
+ if (rc)
+ return;
+
+ gen_pool_free(client->ctx->pool, (unsigned long)client->virt,
+ client->size);
+
+ client->virt = NULL;
+ client->phys = 0;
+ client->size = 0;
+}
+
+static const struct vm_operations_struct aspeed_xdma_vm_ops = {
+ .close = aspeed_xdma_vma_close,
+};
+
+static int aspeed_xdma_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ int rc;
+ struct aspeed_xdma_client *client = file->private_data;
+ struct aspeed_xdma *ctx = client->ctx;
+
+ /* restrict file to one mapping */
+ if (client->size)
+ return -EBUSY;
+
+ client->size = vma->vm_end - vma->vm_start;
+ client->virt = gen_pool_dma_alloc(ctx->pool, client->size,
+ &client->phys);
+ if (!client->virt) {
+ client->phys = 0;
+ client->size = 0;
+ return -ENOMEM;
+ }
+
+ vma->vm_pgoff = (client->phys - ctx->mem_phys) >> PAGE_SHIFT;
+ vma->vm_ops = &aspeed_xdma_vm_ops;
+ vma->vm_private_data = client;
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+
+ rc = io_remap_pfn_range(vma, vma->vm_start, client->phys >> PAGE_SHIFT,
+ client->size, vma->vm_page_prot);
+ if (rc) {
+ dev_warn(ctx->dev, "mmap err: v[%08lx] to p[%08x], s[%08x]\n",
+ vma->vm_start, (u32)client->phys, client->size);
+
+ gen_pool_free(ctx->pool, (unsigned long)client->virt,
+ client->size);
+
+ client->virt = NULL;
+ client->phys = 0;
+ client->size = 0;
+ return rc;
+ }
+
+ dev_dbg(ctx->dev, "mmap: v[%08lx] to p[%08x], s[%08x]\n",
+ vma->vm_start, (u32)client->phys, client->size);
+
+ return 0;
+}
+
+static int aspeed_xdma_open(struct inode *inode, struct file *file)
+{
+ struct miscdevice *misc = file->private_data;
+ struct aspeed_xdma *ctx = container_of(misc, struct aspeed_xdma, misc);
+ struct aspeed_xdma_client *client = kzalloc(sizeof(*client),
+ GFP_KERNEL);
+
+ if (!client)
+ return -ENOMEM;
+
+ kobject_get(&ctx->kobj);
+ client->ctx = ctx;
+ file->private_data = client;
+ return 0;
+}
+
+static int aspeed_xdma_release(struct inode *inode, struct file *file)
+{
+ bool reset = false;
+ unsigned long flags;
+ struct aspeed_xdma_client *client = file->private_data;
+ struct aspeed_xdma *ctx = client->ctx;
+
+ spin_lock_irqsave(&ctx->client_lock, flags);
+ if (client == ctx->current_client) {
+ spin_lock(&ctx->engine_lock);
+ if (ctx->in_reset) {
+ ctx->current_client = NULL;
+ } else {
+ ctx->in_reset = true;
+ reset = true;
+ }
+ spin_unlock(&ctx->engine_lock);
+ }
+ spin_unlock_irqrestore(&ctx->client_lock, flags);
+
+ if (reset)
+ aspeed_xdma_reset(ctx);
+
+ if (client->virt)
+ gen_pool_free(ctx->pool, (unsigned long)client->virt,
+ client->size);
+
+ kfree(client);
+ kobject_put(&ctx->kobj);
+ return 0;
+}
+
+static const struct file_operations aspeed_xdma_fops = {
+ .owner = THIS_MODULE,
+ .write = aspeed_xdma_write,
+ .poll = aspeed_xdma_poll,
+ .unlocked_ioctl = aspeed_xdma_ioctl,
+ .mmap = aspeed_xdma_mmap,
+ .open = aspeed_xdma_open,
+ .release = aspeed_xdma_release,
+};
+
+static int aspeed_xdma_init_scu(struct aspeed_xdma *ctx, struct device *dev)
+{
+ struct regmap *scu = syscon_regmap_lookup_by_phandle(dev->of_node,
+ "aspeed,scu");
+
+ if (!IS_ERR(scu)) {
+ u32 selection;
+ bool pcie_device_bmc = true;
+ const u32 bmc = SCU_PCIE_CONF_BMC_EN |
+ SCU_PCIE_CONF_BMC_EN_MSI | SCU_PCIE_CONF_BMC_EN_IRQ |
+ SCU_PCIE_CONF_BMC_EN_DMA;
+ const u32 vga = SCU_PCIE_CONF_VGA_EN |
+ SCU_PCIE_CONF_VGA_EN_MSI | SCU_PCIE_CONF_VGA_EN_IRQ |
+ SCU_PCIE_CONF_VGA_EN_DMA;
+ const char *pcie = NULL;
+
+ if (!of_property_read_string(dev->of_node,
+ "aspeed,pcie-device", &pcie)) {
+ if (!strcmp(pcie, "vga")) {
+ pcie_device_bmc = false;
+ } else if (strcmp(pcie, "bmc")) {
+ dev_err(dev,
+ "Invalid pcie-device property %s.\n",
+ pcie);
+ return -EINVAL;
+ }
+ }
+
+ if (pcie_device_bmc) {
+ selection = bmc;
+ regmap_write(scu, ctx->chip->scu_bmc_class,
+ SCU_BMC_CLASS_REV_XDMA);
+ } else {
+ selection = vga;
+ }
+
+ regmap_update_bits(scu, ctx->chip->scu_pcie_conf, bmc | vga,
+ selection);
+
+ if (ctx->chip->scu_misc_ctrl)
+ regmap_update_bits(scu, ctx->chip->scu_misc_ctrl,
+ SCU_AST2600_MISC_CTRL_XDMA_BMC,
+ SCU_AST2600_MISC_CTRL_XDMA_BMC);
+ } else {
+ dev_warn(dev, "Unable to configure PCIe: %ld; continuing.\n",
+ PTR_ERR(scu));
+ }
+
+ return 0;
+}
+
+static void aspeed_xdma_kobject_release(struct kobject *kobj)
+{
+ struct aspeed_xdma *ctx = container_of(kobj, struct aspeed_xdma, kobj);
+
+ if (ctx->pcie_irq >= 0)
+ free_irq(ctx->pcie_irq, ctx);
+
+ gen_pool_free(ctx->pool, (unsigned long)ctx->cmdq, XDMA_CMDQ_SIZE);
+
+ gen_pool_destroy(ctx->pool);
+
+ dma_free_coherent(ctx->dev, ctx->mem_size, ctx->mem_virt,
+ ctx->mem_coherent);
+
+ if (ctx->reset_rc)
+ reset_control_put(ctx->reset_rc);
+ reset_control_put(ctx->reset);
+
+ clk_put(ctx->clock);
+
+ free_irq(ctx->irq, ctx);
+
+ iounmap(ctx->base);
+ release_mem_region(ctx->res_start, ctx->res_size);
+
+ kfree(ctx);
+}
+
+static struct kobj_type aspeed_xdma_kobject_type = {
+ .release = aspeed_xdma_kobject_release,
+};
+
+static int aspeed_xdma_iomap(struct aspeed_xdma *ctx,
+ struct platform_device *pdev)
+{
+ resource_size_t size;
+ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ if (!res)
+ return -ENOMEM;
+
+ size = resource_size(res);
+ if (!request_mem_region(res->start, size, dev_name(ctx->dev)))
+ return -ENOMEM;
+
+ ctx->base = ioremap(res->start, size);
+ if (!ctx->base) {
+ release_mem_region(res->start, size);
+ return -ENOMEM;
+ }
+
+ ctx->res_start = res->start;
+ ctx->res_size = size;
+
+ return 0;
+}
+
+static int aspeed_xdma_probe(struct platform_device *pdev)
+{
+ int rc;
+ struct aspeed_xdma *ctx;
+ struct reserved_mem *mem;
+ struct device *dev = &pdev->dev;
+ struct device_node *memory_region;
+ const void *md = of_device_get_match_data(dev);
+
+ if (!md)
+ return -ENODEV;
+
+ ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ ctx->chip = md;
+ ctx->dev = dev;
+ platform_set_drvdata(pdev, ctx);
+ spin_lock_init(&ctx->client_lock);
+ spin_lock_init(&ctx->engine_lock);
+ INIT_WORK(&ctx->reset_work, aspeed_xdma_reset_work);
+ init_waitqueue_head(&ctx->wait);
+
+ rc = aspeed_xdma_iomap(ctx, pdev);
+ if (rc) {
+ dev_err(dev, "Failed to map registers.\n");
+ goto err_nomap;
+ }
+
+ ctx->irq = platform_get_irq(pdev, 0);
+ if (ctx->irq < 0) {
+ dev_err(dev, "Failed to find IRQ.\n");
+ rc = ctx->irq;
+ goto err_noirq;
+ }
+
+ rc = request_irq(ctx->irq, aspeed_xdma_irq, 0, DEVICE_NAME, ctx);
+ if (rc < 0) {
+ dev_err(dev, "Failed to request IRQ %d.\n", ctx->irq);
+ goto err_noirq;
+ }
+
+ ctx->clock = clk_get(dev, NULL);
+ if (IS_ERR(ctx->clock)) {
+ dev_err(dev, "Failed to request clock.\n");
+ rc = PTR_ERR(ctx->clock);
+ goto err_noclk;
+ }
+
+ ctx->reset = reset_control_get_exclusive(dev, NULL);
+ if (IS_ERR(ctx->reset)) {
+ dev_err(dev, "Failed to request reset control.\n");
+ rc = PTR_ERR(ctx->reset);
+ goto err_noreset;
+ }
+
+ ctx->reset_rc = reset_control_get_exclusive(dev, "root-complex");
+ if (IS_ERR(ctx->reset_rc)) {
+ dev_dbg(dev, "Failed to request reset RC control.\n");
+ ctx->reset_rc = NULL;
+ }
+
+ memory_region = of_parse_phandle(dev->of_node, "memory-region", 0);
+ if (!memory_region) {
+ dev_err(dev, "Failed to find memory-region.\n");
+ rc = -ENOMEM;
+ goto err_nomem;
+ }
+
+ mem = of_reserved_mem_lookup(memory_region);
+ of_node_put(memory_region);
+ if (!mem) {
+ dev_err(dev, "Failed to find reserved memory.\n");
+ rc = -ENOMEM;
+ goto err_nomem;
+ }
+
+ ctx->mem_phys = mem->base;
+ ctx->mem_size = mem->size;
+
+ rc = of_reserved_mem_device_init(dev);
+ if (rc) {
+ dev_err(dev, "Failed to init reserved memory.\n");
+ goto err_nomem;
+ }
+
+ rc = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
+ if (rc) {
+ dev_err(dev, "Failed to mask DMA.\n");
+ goto err_nomem;
+ }
+
+ ctx->mem_virt = dma_alloc_coherent(dev, ctx->mem_size,
+ &ctx->mem_coherent, 0);
+ if (!ctx->mem_virt) {
+ dev_err(dev, "Failed to allocate reserved memory.\n");
+ rc = -ENOMEM;
+ goto err_nomem;
+ }
+
+ ctx->pool = gen_pool_create(ilog2(PAGE_SIZE), -1);
+ if (!ctx->pool) {
+ dev_err(dev, "Failed to setup genalloc pool.\n");
+ rc = -ENOMEM;
+ goto err_nopool;
+ }
+
+ rc = gen_pool_add_virt(ctx->pool, (unsigned long)ctx->mem_virt,
+ ctx->mem_phys, ctx->mem_size, -1);
+ if (rc) {
+ dev_err(ctx->dev, "Failed to add memory to genalloc pool.\n");
+ goto err_pool_scu_clk;
+ }
+
+ rc = aspeed_xdma_init_scu(ctx, dev);
+ if (rc)
+ goto err_pool_scu_clk;
+
+ rc = clk_prepare_enable(ctx->clock);
+ if (rc) {
+ dev_err(dev, "Failed to enable the clock.\n");
+ goto err_pool_scu_clk;
+ }
+
+ if (ctx->reset_rc) {
+ rc = reset_control_deassert(ctx->reset_rc);
+ if (rc) {
+ dev_err(dev, "Failed to clear the RC reset.\n");
+ goto err_reset_rc;
+ }
+ usleep_range(XDMA_ENGINE_SETUP_TIME_MIN_US,
+ XDMA_ENGINE_SETUP_TIME_MAX_US);
+ }
+
+ rc = reset_control_deassert(ctx->reset);
+ if (rc) {
+ dev_err(dev, "Failed to clear the reset.\n");
+ goto err_reset;
+ }
+ usleep_range(XDMA_ENGINE_SETUP_TIME_MIN_US,
+ XDMA_ENGINE_SETUP_TIME_MAX_US);
+
+ ctx->cmdq = gen_pool_dma_alloc(ctx->pool, XDMA_CMDQ_SIZE,
+ &ctx->cmdq_phys);
+ if (!ctx->cmdq) {
+ dev_err(ctx->dev, "Failed to genalloc cmdq.\n");
+ rc = -ENOMEM;
+ goto err_pool;
+ }
+
+ aspeed_xdma_init_eng(ctx);
+
+ ctx->misc.minor = MISC_DYNAMIC_MINOR;
+ ctx->misc.fops = &aspeed_xdma_fops;
+ ctx->misc.name = "aspeed-xdma";
+ ctx->misc.parent = dev;
+ rc = misc_register(&ctx->misc);
+ if (rc) {
+ dev_err(dev, "Failed to register xdma miscdevice.\n");
+ goto err_misc;
+ }
+
+ /*
+ * This interrupt could fire immediately so only request it once the
+ * engine and driver are initialized.
+ */
+ ctx->pcie_irq = platform_get_irq(pdev, 1);
+ if (ctx->pcie_irq < 0) {
+ dev_warn(dev, "Failed to find PCI-E IRQ.\n");
+ } else {
+ rc = request_irq(ctx->pcie_irq, aspeed_xdma_pcie_irq,
+ IRQF_SHARED, DEVICE_NAME, ctx);
+ if (rc < 0) {
+ dev_warn(dev, "Failed to request PCI-E IRQ %d.\n", rc);
+ ctx->pcie_irq = -1;
+ }
+ }
+
+ kobject_init(&ctx->kobj, &aspeed_xdma_kobject_type);
+ return 0;
+
+err_misc:
+ gen_pool_free(ctx->pool, (unsigned long)ctx->cmdq, XDMA_CMDQ_SIZE);
+err_pool:
+ reset_control_assert(ctx->reset);
+err_reset:
+ if (ctx->reset_rc)
+ reset_control_assert(ctx->reset_rc);
+err_reset_rc:
+ clk_disable_unprepare(ctx->clock);
+err_pool_scu_clk:
+ gen_pool_destroy(ctx->pool);
+err_nopool:
+ dma_free_coherent(ctx->dev, ctx->mem_size, ctx->mem_virt,
+ ctx->mem_coherent);
+err_nomem:
+ if (ctx->reset_rc)
+ reset_control_put(ctx->reset_rc);
+ reset_control_put(ctx->reset);
+err_noreset:
+ clk_put(ctx->clock);
+err_noclk:
+ free_irq(ctx->irq, ctx);
+err_noirq:
+ iounmap(ctx->base);
+ release_mem_region(ctx->res_start, ctx->res_size);
+err_nomap:
+ kfree(ctx);
+ return rc;
+}
+
+static int aspeed_xdma_remove(struct platform_device *pdev)
+{
+ struct aspeed_xdma *ctx = platform_get_drvdata(pdev);
+
+ reset_control_assert(ctx->reset);
+ if (ctx->reset_rc)
+ reset_control_assert(ctx->reset_rc);
+ clk_disable_unprepare(ctx->clock);
+
+ aspeed_xdma_done(ctx, true);
+
+ misc_deregister(&ctx->misc);
+ kobject_put(&ctx->kobj);
+
+ return 0;
+}
+
+static const struct aspeed_xdma_chip aspeed_ast2500_xdma_chip = {
+ .control = XDMA_AST2500_CTRL_US_COMP | XDMA_AST2500_CTRL_DS_COMP |
+ XDMA_AST2500_CTRL_DS_DIRTY | XDMA_AST2500_CTRL_DS_SIZE_256 |
+ XDMA_AST2500_CTRL_DS_TIMEOUT | XDMA_AST2500_CTRL_DS_CHECK_ID,
+ .scu_bmc_class = SCU_AST2500_BMC_CLASS_REV,
+ .scu_misc_ctrl = 0,
+ .scu_pcie_conf = SCU_AST2500_PCIE_CONF,
+ .queue_entry_size = XDMA_AST2500_QUEUE_ENTRY_SIZE,
+ .regs = {
+ .bmc_cmdq_addr = XDMA_AST2500_BMC_CMDQ_ADDR,
+ .bmc_cmdq_endp = XDMA_AST2500_BMC_CMDQ_ENDP,
+ .bmc_cmdq_writep = XDMA_AST2500_BMC_CMDQ_WRITEP,
+ .bmc_cmdq_readp = XDMA_AST2500_BMC_CMDQ_READP,
+ .control = XDMA_AST2500_CTRL,
+ .status = XDMA_AST2500_STATUS,
+ },
+ .status_bits = {
+ .us_comp = XDMA_AST2500_STATUS_US_COMP,
+ .ds_comp = XDMA_AST2500_STATUS_DS_COMP,
+ .ds_dirty = XDMA_AST2500_STATUS_DS_DIRTY,
+ },
+ .set_cmd = aspeed_xdma_ast2500_set_cmd,
+};
+
+static const struct aspeed_xdma_chip aspeed_ast2600_xdma_chip = {
+ .control = XDMA_AST2600_CTRL_US_COMP | XDMA_AST2600_CTRL_DS_COMP |
+ XDMA_AST2600_CTRL_DS_DIRTY | XDMA_AST2600_CTRL_DS_SIZE_256,
+ .scu_bmc_class = SCU_AST2600_BMC_CLASS_REV,
+ .scu_misc_ctrl = SCU_AST2600_MISC_CTRL,
+ .scu_pcie_conf = SCU_AST2600_PCIE_CONF,
+ .queue_entry_size = XDMA_AST2600_QUEUE_ENTRY_SIZE,
+ .regs = {
+ .bmc_cmdq_addr = XDMA_AST2600_BMC_CMDQ_ADDR,
+ .bmc_cmdq_endp = XDMA_AST2600_BMC_CMDQ_ENDP,
+ .bmc_cmdq_writep = XDMA_AST2600_BMC_CMDQ_WRITEP,
+ .bmc_cmdq_readp = XDMA_AST2600_BMC_CMDQ_READP,
+ .control = XDMA_AST2600_CTRL,
+ .status = XDMA_AST2600_STATUS,
+ },
+ .status_bits = {
+ .us_comp = XDMA_AST2600_STATUS_US_COMP,
+ .ds_comp = XDMA_AST2600_STATUS_DS_COMP,
+ .ds_dirty = XDMA_AST2600_STATUS_DS_DIRTY,
+ },
+ .set_cmd = aspeed_xdma_ast2600_set_cmd,
+};
+
+static const struct of_device_id aspeed_xdma_match[] = {
+ {
+ .compatible = "aspeed,ast2500-xdma",
+ .data = &aspeed_ast2500_xdma_chip,
+ },
+ {
+ .compatible = "aspeed,ast2600-xdma",
+ .data = &aspeed_ast2600_xdma_chip,
+ },
+ { },
+};
+
+static struct platform_driver aspeed_xdma_driver = {
+ .probe = aspeed_xdma_probe,
+ .remove = aspeed_xdma_remove,
+ .driver = {
+ .name = DEVICE_NAME,
+ .of_match_table = aspeed_xdma_match,
+ },
+};
+
+module_platform_driver(aspeed_xdma_driver);
+
+MODULE_AUTHOR("Eddie James");
+MODULE_DESCRIPTION("Aspeed XDMA Engine Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/spi/spi-fsi.c b/drivers/spi/spi-fsi.c
index 37a3e0f8e752..8949a64ec87d 100644
--- a/drivers/spi/spi-fsi.c
+++ b/drivers/spi/spi-fsi.c
@@ -12,6 +12,7 @@
#define FSI_ENGID_SPI 0x23
#define FSI_MBOX_ROOT_CTRL_8 0x2860
+#define FSI_MBOX_ROOT_CTRL_8_SPI 0xf0000000
#define FSI2SPI_DATA0 0x00
#define FSI2SPI_DATA1 0x04
@@ -24,11 +25,16 @@
#define SPI_FSI_BASE 0x70000
#define SPI_FSI_INIT_TIMEOUT_MS 1000
-#define SPI_FSI_MAX_TRANSFER_SIZE 2048
+#define SPI_FSI_MAX_XFR_SIZE 2048
+#define SPI_FSI_MAX_XFR_SIZE_RESTRICTED 32
#define SPI_FSI_ERROR 0x0
#define SPI_FSI_COUNTER_CFG 0x1
#define SPI_FSI_COUNTER_CFG_LOOPS(x) (((u64)(x) & 0xffULL) << 32)
+#define SPI_FSI_COUNTER_CFG_N2_RX BIT_ULL(8)
+#define SPI_FSI_COUNTER_CFG_N2_TX BIT_ULL(9)
+#define SPI_FSI_COUNTER_CFG_N2_IMPLICIT BIT_ULL(10)
+#define SPI_FSI_COUNTER_CFG_N2_RELOAD BIT_ULL(11)
#define SPI_FSI_CFG1 0x2
#define SPI_FSI_CLOCK_CFG 0x3
#define SPI_FSI_CLOCK_CFG_MM_ENABLE BIT_ULL(32)
@@ -61,7 +67,7 @@
#define SPI_FSI_STATUS_RDR_OVERRUN BIT_ULL(62)
#define SPI_FSI_STATUS_RDR_FULL BIT_ULL(63)
#define SPI_FSI_STATUS_ANY_ERROR \
- (SPI_FSI_STATUS_ERROR | SPI_FSI_STATUS_TDR_UNDERRUN | \
+ (SPI_FSI_STATUS_ERROR | \
SPI_FSI_STATUS_TDR_OVERRUN | SPI_FSI_STATUS_RDR_UNDERRUN | \
SPI_FSI_STATUS_RDR_OVERRUN)
#define SPI_FSI_PORT_CTRL 0x9
@@ -70,6 +76,8 @@ struct fsi_spi {
struct device *dev; /* SPI controller device */
struct fsi_device *fsi; /* FSI2SPI CFAM engine device */
u32 base;
+ size_t max_xfr_size;
+ bool restricted;
};
struct fsi_spi_sequence {
@@ -77,6 +85,26 @@ struct fsi_spi_sequence {
u64 data;
};
+static int fsi_spi_check_mux(struct fsi_device *fsi, struct device *dev)
+{
+ int rc;
+ u32 root_ctrl_8;
+ __be32 root_ctrl_8_be;
+
+ rc = fsi_slave_read(fsi->slave, FSI_MBOX_ROOT_CTRL_8, &root_ctrl_8_be,
+ sizeof(root_ctrl_8_be));
+ if (rc)
+ return rc;
+
+ root_ctrl_8 = be32_to_cpu(root_ctrl_8_be);
+ dev_dbg(dev, "Root control register 8: %08x\n", root_ctrl_8);
+ if ((root_ctrl_8 & FSI_MBOX_ROOT_CTRL_8_SPI) ==
+ FSI_MBOX_ROOT_CTRL_8_SPI)
+ return 0;
+
+ return -ENOLINK;
+}
+
static int fsi_spi_check_status(struct fsi_spi *ctx)
{
int rc;
@@ -205,8 +233,12 @@ static int fsi_spi_reset(struct fsi_spi *ctx)
if (rc)
return rc;
- return fsi_spi_write_reg(ctx, SPI_FSI_CLOCK_CFG,
- SPI_FSI_CLOCK_CFG_RESET2);
+ rc = fsi_spi_write_reg(ctx, SPI_FSI_CLOCK_CFG,
+ SPI_FSI_CLOCK_CFG_RESET2);
+ if (rc)
+ return rc;
+
+ return fsi_spi_write_reg(ctx, SPI_FSI_STATUS, 0ULL);
}
static int fsi_spi_sequence_add(struct fsi_spi_sequence *seq, u8 val)
@@ -214,8 +246,8 @@ static int fsi_spi_sequence_add(struct fsi_spi_sequence *seq, u8 val)
/*
* Add the next byte of instruction to the 8-byte sequence register.
* Then decrement the counter so that the next instruction will go in
- * the right place. Return the number of "slots" left in the sequence
- * register.
+ * the right place. Return the index of the slot we just filled in the
+ * sequence register.
*/
seq->data |= (u64)val << seq->bit;
seq->bit -= 8;
@@ -233,40 +265,71 @@ static int fsi_spi_sequence_transfer(struct fsi_spi *ctx,
struct fsi_spi_sequence *seq,
struct spi_transfer *transfer)
{
+ bool docfg = false;
int loops;
int idx;
int rc;
+ u8 val = 0;
u8 len = min(transfer->len, 8U);
u8 rem = transfer->len % len;
+ u64 cfg = 0ULL;
loops = transfer->len / len;
if (transfer->tx_buf) {
- idx = fsi_spi_sequence_add(seq,
- SPI_FSI_SEQUENCE_SHIFT_OUT(len));
+ val = SPI_FSI_SEQUENCE_SHIFT_OUT(len);
+ idx = fsi_spi_sequence_add(seq, val);
+
if (rem)
rem = SPI_FSI_SEQUENCE_SHIFT_OUT(rem);
} else if (transfer->rx_buf) {
- idx = fsi_spi_sequence_add(seq,
- SPI_FSI_SEQUENCE_SHIFT_IN(len));
+ val = SPI_FSI_SEQUENCE_SHIFT_IN(len);
+ idx = fsi_spi_sequence_add(seq, val);
+
if (rem)
rem = SPI_FSI_SEQUENCE_SHIFT_IN(rem);
} else {
return -EINVAL;
}
+ if (ctx->restricted) {
+ const int eidx = rem ? 5 : 6;
+
+ while (loops > 1 && idx <= eidx) {
+ idx = fsi_spi_sequence_add(seq, val);
+ loops--;
+ docfg = true;
+ }
+
+ if (loops > 1) {
+ dev_warn(ctx->dev, "No sequencer slots; aborting.\n");
+ return -EINVAL;
+ }
+ }
+
if (loops > 1) {
fsi_spi_sequence_add(seq, SPI_FSI_SEQUENCE_BRANCH(idx));
+ docfg = true;
+ }
- if (rem)
- fsi_spi_sequence_add(seq, rem);
+ if (docfg) {
+ cfg = SPI_FSI_COUNTER_CFG_LOOPS(loops - 1);
+ if (transfer->rx_buf)
+ cfg |= SPI_FSI_COUNTER_CFG_N2_RX |
+ SPI_FSI_COUNTER_CFG_N2_TX |
+ SPI_FSI_COUNTER_CFG_N2_IMPLICIT |
+ SPI_FSI_COUNTER_CFG_N2_RELOAD;
- rc = fsi_spi_write_reg(ctx, SPI_FSI_COUNTER_CFG,
- SPI_FSI_COUNTER_CFG_LOOPS(loops - 1));
+ rc = fsi_spi_write_reg(ctx, SPI_FSI_COUNTER_CFG, cfg);
if (rc)
return rc;
+ } else {
+ fsi_spi_write_reg(ctx, SPI_FSI_COUNTER_CFG, 0ULL);
}
+ if (rem)
+ fsi_spi_sequence_add(seq, rem);
+
return 0;
}
@@ -275,6 +338,7 @@ static int fsi_spi_transfer_data(struct fsi_spi *ctx,
{
int rc = 0;
u64 status = 0ULL;
+ u64 cfg = 0ULL;
if (transfer->tx_buf) {
int nb;
@@ -312,6 +376,16 @@ static int fsi_spi_transfer_data(struct fsi_spi *ctx,
u64 in = 0ULL;
u8 *rx = transfer->rx_buf;
+ rc = fsi_spi_read_reg(ctx, SPI_FSI_COUNTER_CFG, &cfg);
+ if (rc)
+ return rc;
+
+ if (cfg & SPI_FSI_COUNTER_CFG_N2_IMPLICIT) {
+ rc = fsi_spi_write_reg(ctx, SPI_FSI_DATA_TX, 0);
+ if (rc)
+ return rc;
+ }
+
while (transfer->len > recv) {
do {
rc = fsi_spi_read_reg(ctx, SPI_FSI_STATUS,
@@ -350,7 +424,7 @@ static int fsi_spi_transfer_init(struct fsi_spi *ctx)
u64 status = 0ULL;
u64 wanted_clock_cfg = SPI_FSI_CLOCK_CFG_ECC_DISABLE |
SPI_FSI_CLOCK_CFG_SCK_NO_DEL |
- FIELD_PREP(SPI_FSI_CLOCK_CFG_SCK_DIV, 4);
+ FIELD_PREP(SPI_FSI_CLOCK_CFG_SCK_DIV, 19);
end = jiffies + msecs_to_jiffies(SPI_FSI_INIT_TIMEOUT_MS);
do {
@@ -396,18 +470,22 @@ static int fsi_spi_transfer_init(struct fsi_spi *ctx)
static int fsi_spi_transfer_one_message(struct spi_controller *ctlr,
struct spi_message *mesg)
{
- int rc = 0;
+ int rc;
u8 seq_slave = SPI_FSI_SEQUENCE_SEL_SLAVE(mesg->spi->chip_select + 1);
struct spi_transfer *transfer;
struct fsi_spi *ctx = spi_controller_get_devdata(ctlr);
+ rc = fsi_spi_check_mux(ctx->fsi, ctx->dev);
+ if (rc)
+ return rc;
+
list_for_each_entry(transfer, &mesg->transfers, transfer_list) {
struct fsi_spi_sequence seq;
struct spi_transfer *next = NULL;
/* Sequencer must do shift out (tx) first. */
if (!transfer->tx_buf ||
- transfer->len > SPI_FSI_MAX_TRANSFER_SIZE) {
+ transfer->len > (ctx->max_xfr_size + 8)) {
rc = -EINVAL;
goto error;
}
@@ -431,7 +509,7 @@ static int fsi_spi_transfer_one_message(struct spi_controller *ctlr,
/* Sequencer can only do shift in (rx) after tx. */
if (next->rx_buf) {
- if (next->len > SPI_FSI_MAX_TRANSFER_SIZE) {
+ if (next->len > ctx->max_xfr_size) {
rc = -EINVAL;
goto error;
}
@@ -476,30 +554,21 @@ error:
static size_t fsi_spi_max_transfer_size(struct spi_device *spi)
{
- return SPI_FSI_MAX_TRANSFER_SIZE;
+ struct fsi_spi *ctx = spi_controller_get_devdata(spi->controller);
+
+ return ctx->max_xfr_size;
}
static int fsi_spi_probe(struct device *dev)
{
int rc;
- u32 root_ctrl_8;
struct device_node *np;
int num_controllers_registered = 0;
struct fsi_device *fsi = to_fsi_dev(dev);
- /*
- * Check the SPI mux before attempting to probe. If the mux isn't set
- * then the SPI controllers can't access their slave devices.
- */
- rc = fsi_slave_read(fsi->slave, FSI_MBOX_ROOT_CTRL_8, &root_ctrl_8,
- sizeof(root_ctrl_8));
+ rc = fsi_spi_check_mux(fsi, dev);
if (rc)
- return rc;
-
- if (!root_ctrl_8) {
- dev_dbg(dev, "SPI mux not set, aborting probe.\n");
return -ENODEV;
- }
for_each_available_child_of_node(dev->of_node, np) {
u32 base;
@@ -524,6 +593,14 @@ static int fsi_spi_probe(struct device *dev)
ctx->fsi = fsi;
ctx->base = base + SPI_FSI_BASE;
+ if (of_property_read_bool(np, "fsi2spi,restricted")) {
+ ctx->restricted = true;
+ ctx->max_xfr_size = SPI_FSI_MAX_XFR_SIZE_RESTRICTED;
+ } else {
+ ctx->restricted = false;
+ ctx->max_xfr_size = SPI_FSI_MAX_XFR_SIZE;
+ }
+
rc = devm_spi_register_controller(dev, ctlr);
if (rc)
spi_controller_put(ctlr);
diff --git a/include/linux/mfd/intel-peci-client.h b/include/linux/mfd/intel-peci-client.h
new file mode 100644
index 000000000000..7668d0cfa843
--- /dev/null
+++ b/include/linux/mfd/intel-peci-client.h
@@ -0,0 +1,119 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (c) 2018-2019 Intel Corporation */
+
+#ifndef __LINUX_MFD_INTEL_PECI_CLIENT_H
+#define __LINUX_MFD_INTEL_PECI_CLIENT_H
+
+#include <linux/peci.h>
+
+#if IS_ENABLED(CONFIG_X86)
+#include <asm/intel-family.h>
+#else
+/*
+ * Architectures other than x86 cannot include the header file so define these
+ * at here. These are needed for detecting type of client x86 CPUs behind a PECI
+ * connection.
+ */
+#define INTEL_FAM6_HASWELL_X 0x3F
+#define INTEL_FAM6_BROADWELL_X 0x4F
+#define INTEL_FAM6_SKYLAKE_X 0x55
+#define INTEL_FAM6_SKYLAKE_XD 0x56
+#endif
+
+#define INTEL_FAM6 6 /* P6 (Pentium Pro and later) */
+
+#define CORE_MAX_ON_HSX 18 /* Max number of cores on Haswell */
+#define CHAN_RANK_MAX_ON_HSX 8 /* Max number of channel ranks on Haswell */
+#define DIMM_IDX_MAX_ON_HSX 3 /* Max DIMM index per channel on Haswell */
+
+#define CORE_MAX_ON_BDX 24 /* Max number of cores on Broadwell */
+#define CHAN_RANK_MAX_ON_BDX 4 /* Max number of channel ranks on Broadwell */
+#define DIMM_IDX_MAX_ON_BDX 3 /* Max DIMM index per channel on Broadwell */
+
+#define CORE_MAX_ON_SKX 28 /* Max number of cores on Skylake */
+#define CHAN_RANK_MAX_ON_SKX 6 /* Max number of channel ranks on Skylake */
+#define DIMM_IDX_MAX_ON_SKX 2 /* Max DIMM index per channel on Skylake */
+
+#define CORE_MAX_ON_SKXD 16 /* Max number of cores on Skylake D */
+#define CHAN_RANK_MAX_ON_SKXD 2 /* Max number of channel ranks on Skylake D */
+#define DIMM_IDX_MAX_ON_SKXD 2 /* Max DIMM index per channel on Skylake D */
+
+#define CORE_NUMS_MAX CORE_MAX_ON_SKX
+#define CHAN_RANK_MAX CHAN_RANK_MAX_ON_HSX
+#define DIMM_IDX_MAX DIMM_IDX_MAX_ON_HSX
+#define DIMM_NUMS_MAX (CHAN_RANK_MAX * DIMM_IDX_MAX)
+
+/**
+ * struct cpu_gen_info - CPU generation specific information
+ * @family: CPU family ID
+ * @model: CPU model
+ * @core_max: max number of cores
+ * @chan_rank_max: max number of channel ranks
+ * @dimm_idx_max: max number of DIMM indices
+ *
+ * CPU generation specific information to identify maximum number of cores and
+ * DIMM slots.
+ */
+struct cpu_gen_info {
+ u16 family;
+ u8 model;
+ uint core_max;
+ uint chan_rank_max;
+ uint dimm_idx_max;
+};
+
+/**
+ * struct peci_client_manager - PECI client manager information
+ * @client; pointer to the PECI client
+ * @name: PECI client manager name
+ * @gen_info: CPU generation info of the detected CPU
+ *
+ * PECI client manager information for managing PECI sideband functions on a CPU
+ * client.
+ */
+struct peci_client_manager {
+ struct peci_client *client;
+ char name[PECI_NAME_SIZE];
+ const struct cpu_gen_info *gen_info;
+};
+
+/**
+ * peci_client_read_package_config - read from the Package Configuration Space
+ * @priv: driver private data structure
+ * @index: encoding index for the requested service
+ * @param: parameter to specify the exact data being requested
+ * @data: data buffer to store the result
+ * Context: can sleep
+ *
+ * A generic PECI command that provides read access to the
+ * "Package Configuration Space" that is maintained by the PCU, including
+ * various power and thermal management functions. Typical PCS read services
+ * supported by the processor may include access to temperature data, energy
+ * status, run time information, DIMM temperatures and so on.
+ *
+ * Return: zero on success, else a negative error code.
+ */
+static inline int
+peci_client_read_package_config(struct peci_client_manager *priv,
+ u8 index, u16 param, u8 *data)
+{
+ struct peci_rd_pkg_cfg_msg msg;
+ int ret;
+
+ msg.addr = priv->client->addr;
+ msg.index = index;
+ msg.param = param;
+ msg.rx_len = 4;
+
+ ret = peci_command(priv->client->adapter, PECI_CMD_RD_PKG_CFG, &msg);
+ if (msg.cc != PECI_DEV_CC_SUCCESS)
+ ret = -EAGAIN;
+ if (ret)
+ return ret;
+
+ memcpy(data, msg.pkg_config, 4);
+
+ return 0;
+}
+
+#endif /* __LINUX_MFD_INTEL_PECI_CLIENT_H */
diff --git a/include/linux/peci.h b/include/linux/peci.h
new file mode 100644
index 000000000000..4bc4595c797d
--- /dev/null
+++ b/include/linux/peci.h
@@ -0,0 +1,150 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (c) 2018-2019 Intel Corporation */
+
+#ifndef __LINUX_PECI_H
+#define __LINUX_PECI_H
+
+#include <linux/device.h>
+#include <linux/mutex.h>
+#include <linux/peci-ioctl.h>
+
+#define PECI_NAME_SIZE 32
+
+struct peci_board_info {
+ char type[PECI_NAME_SIZE];
+ u8 addr; /* CPU client address */
+ struct device_node *of_node;
+};
+
+/**
+ * struct peci_adapter - represent a PECI adapter
+ * @owner: owner module of the PECI adpater
+ * @bus_lock: mutex for exclusion of multiple callers
+ * @dev: device interface to this driver
+ * @nr: the bus number to map
+ * @name: name of the adapter
+ * @userspace_clients_lock: mutex for exclusion of clients handling
+ * @userspace_clients: list of registered clients
+ * @xfer: low-level transfer function pointer of the adapter
+ * @cmd_mask: mask for supportable PECI commands
+ * @use_dma: flag for indicating that adapter uses DMA
+ *
+ * Each PECI adapter can communicate with one or more PECI client children.
+ * These make a small bus, sharing a single wired PECI connection.
+ */
+struct peci_adapter {
+ struct module *owner;
+ struct mutex bus_lock; /* mutex for bus locking */
+ struct device dev;
+ int nr;
+ char name[PECI_NAME_SIZE];
+ struct mutex userspace_clients_lock; /* clients list mutex */
+ struct list_head userspace_clients;
+ int (*xfer)(struct peci_adapter *adapter,
+ struct peci_xfer_msg *msg);
+ u32 cmd_mask;
+ bool use_dma;
+};
+
+static inline struct peci_adapter *to_peci_adapter(void *d)
+{
+ return container_of(d, struct peci_adapter, dev);
+}
+
+static inline void *peci_get_adapdata(const struct peci_adapter *adapter)
+{
+ return dev_get_drvdata(&adapter->dev);
+}
+
+static inline void peci_set_adapdata(struct peci_adapter *adapter, void *data)
+{
+ dev_set_drvdata(&adapter->dev, data);
+}
+
+/**
+ * struct peci_client - represent a PECI client device
+ * @dev: driver model device node for the client
+ * @adapter: manages the bus segment hosting this PECI device
+ * @addr: address used on the PECI bus connected to the parent adapter
+ * @name: indicates the type of the device
+ * @detected: detected PECI clients list
+ *
+ * A peci_client identifies a single device (i.e. CPU) connected to a peci bus.
+ * The behaviour exposed to Linux is defined by the driver managing the device.
+ */
+struct peci_client {
+ struct device dev;
+ struct peci_adapter *adapter;
+ u8 addr;
+ char name[PECI_NAME_SIZE];
+ struct list_head detected;
+};
+
+static inline struct peci_client *to_peci_client(void *d)
+{
+ return container_of(d, struct peci_client, dev);
+}
+
+struct peci_device_id {
+ char name[PECI_NAME_SIZE];
+ ulong driver_data; /* Data private to the driver */
+};
+
+/**
+ * struct peci_driver - represent a PECI device driver
+ * @probe: callback for device binding
+ * @remove: callback for device unbinding
+ * @shutdown: callback for device shutdown
+ * @driver: device driver model driver
+ * @id_table: list of PECI devices supported by this driver
+ *
+ * The driver.owner field should be set to the module owner of this driver.
+ * The driver.name field should be set to the name of this driver.
+ */
+struct peci_driver {
+ int (*probe)(struct peci_client *client);
+ int (*remove)(struct peci_client *client);
+ void (*shutdown)(struct peci_client *client);
+ struct device_driver driver;
+ const struct peci_device_id *id_table;
+};
+
+static inline struct peci_driver *to_peci_driver(void *d)
+{
+ return container_of(d, struct peci_driver, driver);
+}
+
+/**
+ * module_peci_driver - Helper macro for registering a modular PECI driver
+ * @__peci_driver: peci_driver struct
+ *
+ * Helper macro for PECI drivers which do not do anything special in module
+ * init/exit. This eliminates a lot of boilerplate. Each module may only
+ * use this macro once, and calling it replaces module_init() and module_exit()
+ */
+#define module_peci_driver(__peci_driver) \
+ module_driver(__peci_driver, peci_add_driver, peci_del_driver)
+
+/* use a define to avoid include chaining to get THIS_MODULE */
+#define peci_add_driver(driver) peci_register_driver(THIS_MODULE, driver)
+
+extern struct bus_type peci_bus_type;
+extern struct device_type peci_adapter_type;
+extern struct device_type peci_client_type;
+
+int peci_register_driver(struct module *owner, struct peci_driver *drv);
+void peci_del_driver(struct peci_driver *driver);
+struct peci_client *peci_verify_client(struct device *dev);
+struct peci_adapter *peci_alloc_adapter(struct device *dev, uint size);
+struct peci_adapter *peci_get_adapter(int nr);
+void peci_put_adapter(struct peci_adapter *adapter);
+int peci_add_adapter(struct peci_adapter *adapter);
+void peci_del_adapter(struct peci_adapter *adapter);
+struct peci_adapter *peci_verify_adapter(struct device *dev);
+int peci_for_each_dev(void *data, int (*fn)(struct device *, void *));
+struct peci_xfer_msg *peci_get_xfer_msg(u8 tx_len, u8 rx_len);
+void peci_put_xfer_msg(struct peci_xfer_msg *msg);
+int peci_command(struct peci_adapter *adpater, enum peci_cmd cmd, void *vmsg);
+int peci_get_cpu_id(struct peci_adapter *adapter, u8 addr, u32 *cpu_id);
+
+#endif /* __LINUX_PECI_H */
diff --git a/include/uapi/linux/aspeed-xdma.h b/include/uapi/linux/aspeed-xdma.h
new file mode 100644
index 000000000000..3a3646fd1e9e
--- /dev/null
+++ b/include/uapi/linux/aspeed-xdma.h
@@ -0,0 +1,42 @@
+/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
+/* Copyright IBM Corp 2019 */
+
+#ifndef _UAPI_LINUX_ASPEED_XDMA_H_
+#define _UAPI_LINUX_ASPEED_XDMA_H_
+
+#include <linux/ioctl.h>
+#include <linux/types.h>
+
+#define __ASPEED_XDMA_IOCTL_MAGIC 0xb7
+#define ASPEED_XDMA_IOCTL_RESET _IO(__ASPEED_XDMA_IOCTL_MAGIC, 0)
+
+/*
+ * aspeed_xdma_direction
+ *
+ * ASPEED_XDMA_DIRECTION_DOWNSTREAM: transfers data from the host to the BMC
+ *
+ * ASPEED_XDMA_DIRECTION_UPSTREAM: transfers data from the BMC to the host
+ */
+enum aspeed_xdma_direction {
+ ASPEED_XDMA_DIRECTION_DOWNSTREAM = 0,
+ ASPEED_XDMA_DIRECTION_UPSTREAM,
+};
+
+/*
+ * aspeed_xdma_op
+ *
+ * host_addr: the DMA address on the host side, typically configured by PCI
+ * subsystem
+ *
+ * len: the size of the transfer in bytes
+ *
+ * direction: an enumerator indicating the direction of the DMA operation; see
+ * enum aspeed_xdma_direction
+ */
+struct aspeed_xdma_op {
+ __u64 host_addr;
+ __u32 len;
+ __u32 direction;
+};
+
+#endif /* _UAPI_LINUX_ASPEED_XDMA_H_ */
diff --git a/include/uapi/linux/peci-ioctl.h b/include/uapi/linux/peci-ioctl.h
new file mode 100644
index 000000000000..c74b3cde52e8
--- /dev/null
+++ b/include/uapi/linux/peci-ioctl.h
@@ -0,0 +1,661 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+/* Copyright (c) 2018-2020 Intel Corporation */
+
+#ifndef __PECI_IOCTL_H
+#define __PECI_IOCTL_H
+
+#include <linux/ioctl.h>
+#include <linux/types.h>
+
+/* The PECI client's default address of 0x30 */
+#define PECI_BASE_ADDR 0x30
+
+/* Max number of CPU clients */
+#define PECI_OFFSET_MAX 8
+
+/* PECI read/write data buffer size max */
+#define PECI_BUFFER_SIZE 255
+
+/* Device Specific Completion Code (CC) Definition */
+#define PECI_DEV_CC_SUCCESS 0x40
+#define PECI_DEV_CC_NEED_RETRY 0x80
+#define PECI_DEV_CC_OUT_OF_RESOURCE 0x81
+#define PECI_DEV_CC_UNAVAIL_RESOURCE 0x82
+#define PECI_DEV_CC_INVALID_REQ 0x90
+#define PECI_DEV_CC_MCA_ERROR 0x91
+#define PECI_DEV_CC_CATASTROPHIC_MCA_ERROR 0x93
+#define PECI_DEV_CC_FATAL_MCA_DETECTED 0x94
+#define PECI_DEV_CC_PARITY_ERROR_ON_GPSB_OR_PMSB 0x98
+#define PECI_DEV_CC_PARITY_ERROR_ON_GPSB_OR_PMSB_IERR 0x9B
+#define PECI_DEV_CC_PARITY_ERROR_ON_GPSB_OR_PMSB_MCA 0x9C
+
+/* Completion Code mask to check retry needs */
+#define PECI_DEV_CC_RETRY_CHECK_MASK 0xf0
+
+#define PECI_DEV_RETRY_TIMEOUT msecs_to_jiffies(700)
+#define PECI_DEV_RETRY_INTERVAL_MIN_MSEC 1
+#define PECI_DEV_RETRY_INTERVAL_MAX_MSEC 128
+#define PECI_DEV_RETRY_BIT 0x01
+
+/**
+ * enum peci_cmd - PECI client commands
+ * @PECI_CMD_XFER: raw PECI transfer
+ * @PECI_CMD_PING: ping, a required message for all PECI devices
+ * @PECI_CMD_GET_DIB: get DIB (Device Info Byte)
+ * @PECI_CMD_GET_TEMP: get maximum die temperature
+ * @PECI_CMD_RD_PKG_CFG: read access to the PCS (Package Configuration Space)
+ * @PECI_CMD_WR_PKG_CFG: write access to the PCS (Package Configuration Space)
+ * @PECI_CMD_RD_IA_MSR: read access to MSRs (Model Specific Registers)
+ * @PECI_CMD_WR_IA_MSR: write access to MSRs (Model Specific Registers)
+ * @PECI_CMD_RD_IA_MSREX: read access to MSRs (Model Specific Registers)
+ * @PECI_CMD_RD_PCI_CFG: sideband read access to the PCI configuration space
+ * maintained in downstream devices external to the processor
+ * @PECI_CMD_WR_PCI_CFG: sideband write access to the PCI configuration space
+ * maintained in downstream devices external to the processor
+ * @PECI_CMD_RD_PCI_CFG_LOCAL: sideband read access to the PCI configuration
+ * space that resides within the processor
+ * @PECI_CMD_WR_PCI_CFG_LOCAL: sideband write access to the PCI configuration
+ * space that resides within the processor
+ *
+ * Available commands depend on client's PECI revision.
+ */
+enum peci_cmd {
+ PECI_CMD_XFER = 0,
+ PECI_CMD_PING,
+ PECI_CMD_GET_DIB,
+ PECI_CMD_GET_TEMP,
+ PECI_CMD_RD_PKG_CFG,
+ PECI_CMD_WR_PKG_CFG,
+ PECI_CMD_RD_IA_MSR,
+ PECI_CMD_WR_IA_MSR,
+ PECI_CMD_RD_IA_MSREX,
+ PECI_CMD_RD_PCI_CFG,
+ PECI_CMD_WR_PCI_CFG,
+ PECI_CMD_RD_PCI_CFG_LOCAL,
+ PECI_CMD_WR_PCI_CFG_LOCAL,
+ PECI_CMD_RD_END_PT_CFG,
+ PECI_CMD_WR_END_PT_CFG,
+ PECI_CMD_CRASHDUMP_DISC,
+ PECI_CMD_CRASHDUMP_GET_FRAME,
+ PECI_CMD_MAX
+};
+
+/**
+ * struct peci_xfer_msg - raw PECI transfer command
+ * @addr; address of the client
+ * @tx_len: number of data to be written in bytes
+ * @rx_len: number of data to be read in bytes
+ * @tx_buf: data to be written, or NULL
+ * @rx_buf: data to be read, or NULL
+ *
+ * raw PECI transfer
+ */
+struct peci_xfer_msg {
+ __u8 addr;
+ __u8 tx_len;
+ __u8 rx_len;
+ __u8 padding;
+ __u8 *tx_buf;
+ __u8 *rx_buf;
+} __attribute__((__packed__));
+
+/**
+ * struct peci_ping_msg - ping command
+ * @addr: address of the client
+ *
+ * Ping() is a required message for all PECI devices. This message is used to
+ * enumerate devices or determine if a device has been removed, been
+ * powered-off, etc.
+ */
+struct peci_ping_msg {
+ __u8 addr;
+ __u8 padding[3];
+} __attribute__((__packed__));
+
+/**
+ * struct peci_get_dib_msg - GetDIB command
+ * @addr: address of the client
+ * @dib: DIB data to be read
+ *
+ * The processor PECI client implementation of GetDIB() includes an 8-byte
+ * response and provides information regarding client revision number and the
+ * number of supported domains. All processor PECI clients support the GetDIB()
+ * command.
+ */
+struct peci_get_dib_msg {
+#define PECI_GET_DIB_WR_LEN 1
+#define PECI_GET_DIB_RD_LEN 8
+#define PECI_GET_DIB_CMD 0xf7
+
+ __u8 addr;
+ __u8 padding[3];
+ __u64 dib;
+} __attribute__((__packed__));
+
+/**
+ * struct peci_get_temp_msg - GetTemp command
+ * @addr: address of the client
+ * @temp_raw: raw temperature data to be read
+ *
+ * The GetTemp() command is used to retrieve the maximum die temperature from a
+ * target PECI address. The temperature is used by the external thermal
+ * management system to regulate the temperature on the die. The data is
+ * returned as a negative value representing the number of degrees centigrade
+ * below the maximum processor junction temperature.
+ */
+struct peci_get_temp_msg {
+#define PECI_GET_TEMP_WR_LEN 1
+#define PECI_GET_TEMP_RD_LEN 2
+#define PECI_GET_TEMP_CMD 0x01
+
+ __u8 addr;
+ __u8 padding;
+ __s16 temp_raw;
+} __attribute__((__packed__));
+
+/**
+ * struct peci_rd_pkg_cfg_msg - RdPkgConfig command
+ * @addr: address of the client
+ * @index: encoding index for the requested service
+ * @param: specific data being requested
+ * @rx_len: number of data to be read in bytes
+ * @cc: completion code
+ * @pkg_config: package config data to be read
+ *
+ * The RdPkgConfig() command provides read access to the Package Configuration
+ * Space (PCS) within the processor, including various power and thermal
+ * management functions. Typical PCS read services supported by the processor
+ * may include access to temperature data, energy status, run time information,
+ * DIMM temperatures and so on.
+ */
+struct peci_rd_pkg_cfg_msg {
+#define PECI_RDPKGCFG_WRITE_LEN 5
+#define PECI_RDPKGCFG_READ_LEN_BASE 1
+#define PECI_RDPKGCFG_CMD 0xa1
+
+ __u8 addr;
+ __u8 index;
+#define PECI_MBX_INDEX_CPU_ID 0 /* Package Identifier Read */
+#define PECI_MBX_INDEX_VR_DEBUG 1 /* VR Debug */
+#define PECI_MBX_INDEX_PKG_TEMP_READ 2 /* Package Temperature Read */
+#define PECI_MBX_INDEX_ENERGY_COUNTER 3 /* Energy counter */
+#define PECI_MBX_INDEX_ENERGY_STATUS 4 /* DDR Energy Status */
+#define PECI_MBX_INDEX_WAKE_MODE_BIT 5 /* "Wake on PECI" Mode bit */
+#define PECI_MBX_INDEX_EPI 6 /* Efficient Performance Indication */
+#define PECI_MBX_INDEX_PKG_RAPL_PERF 8 /* Pkg RAPL Performance Status Read */
+#define PECI_MBX_INDEX_PER_CORE_DTS_TEMP 9 /* Per Core DTS Temperature Read */
+#define PECI_MBX_INDEX_DTS_MARGIN 10 /* DTS thermal margin */
+#define PECI_MBX_INDEX_SKT_PWR_THRTL_DUR 11 /* Socket Power Throttled Duration */
+#define PECI_MBX_INDEX_CFG_TDP_CONTROL 12 /* TDP Config Control */
+#define PECI_MBX_INDEX_CFG_TDP_LEVELS 13 /* TDP Config Levels */
+#define PECI_MBX_INDEX_DDR_DIMM_TEMP 14 /* DDR DIMM Temperature */
+#define PECI_MBX_INDEX_CFG_ICCMAX 15 /* Configurable ICCMAX */
+#define PECI_MBX_INDEX_TEMP_TARGET 16 /* Temperature Target Read */
+#define PECI_MBX_INDEX_CURR_CFG_LIMIT 17 /* Current Config Limit */
+#define PECI_MBX_INDEX_DIMM_TEMP_READ 20 /* Package Thermal Status Read */
+#define PECI_MBX_INDEX_DRAM_IMC_TMP_READ 22 /* DRAM IMC Temperature Read */
+#define PECI_MBX_INDEX_DDR_CH_THERM_STAT 23 /* DDR Channel Thermal Status */
+#define PECI_MBX_INDEX_PKG_POWER_LIMIT1 26 /* Package Power Limit1 */
+#define PECI_MBX_INDEX_PKG_POWER_LIMIT2 27 /* Package Power Limit2 */
+#define PECI_MBX_INDEX_TDP 28 /* Thermal design power minimum */
+#define PECI_MBX_INDEX_TDP_HIGH 29 /* Thermal design power maximum */
+#define PECI_MBX_INDEX_TDP_UNITS 30 /* Units for power/energy registers */
+#define PECI_MBX_INDEX_RUN_TIME 31 /* Accumulated Run Time */
+#define PECI_MBX_INDEX_CONSTRAINED_TIME 32 /* Thermally Constrained Time Read */
+#define PECI_MBX_INDEX_TURBO_RATIO 33 /* Turbo Activation Ratio */
+#define PECI_MBX_INDEX_DDR_RAPL_PL1 34 /* DDR RAPL PL1 */
+#define PECI_MBX_INDEX_DDR_PWR_INFO_HIGH 35 /* DRAM Power Info Read (high) */
+#define PECI_MBX_INDEX_DDR_PWR_INFO_LOW 36 /* DRAM Power Info Read (low) */
+#define PECI_MBX_INDEX_DDR_RAPL_PL2 37 /* DDR RAPL PL2 */
+#define PECI_MBX_INDEX_DDR_RAPL_STATUS 38 /* DDR RAPL Performance Status */
+#define PECI_MBX_INDEX_DDR_HOT_ABSOLUTE 43 /* DDR Hottest Dimm Absolute Temp */
+#define PECI_MBX_INDEX_DDR_HOT_RELATIVE 44 /* DDR Hottest Dimm Relative Temp */
+#define PECI_MBX_INDEX_DDR_THROTTLE_TIME 45 /* DDR Throttle Time */
+#define PECI_MBX_INDEX_DDR_THERM_STATUS 46 /* DDR Thermal Status */
+#define PECI_MBX_INDEX_TIME_AVG_TEMP 47 /* Package time-averaged temperature */
+#define PECI_MBX_INDEX_TURBO_RATIO_LIMIT 49 /* Turbo Ratio Limit Read */
+#define PECI_MBX_INDEX_HWP_AUTO_OOB 53 /* HWP Autonomous Out-of-band */
+#define PECI_MBX_INDEX_DDR_WARM_BUDGET 55 /* DDR Warm Power Budget */
+#define PECI_MBX_INDEX_DDR_HOT_BUDGET 56 /* DDR Hot Power Budget */
+#define PECI_MBX_INDEX_PKG_PSYS_PWR_LIM3 57 /* Package/Psys Power Limit3 */
+#define PECI_MBX_INDEX_PKG_PSYS_PWR_LIM1 58 /* Package/Psys Power Limit1 */
+#define PECI_MBX_INDEX_PKG_PSYS_PWR_LIM2 59 /* Package/Psys Power Limit2 */
+#define PECI_MBX_INDEX_PKG_PSYS_PWR_LIM4 60 /* Package/Psys Power Limit4 */
+#define PECI_MBX_INDEX_PERF_LIMIT_REASON 65 /* Performance Limit Reasons */
+
+ __u16 param;
+/* When index is PECI_MBX_INDEX_CPU_ID */
+#define PECI_PKG_ID_CPU_ID 0x0000 /* CPUID Info */
+#define PECI_PKG_ID_PLATFORM_ID 0x0001 /* Platform ID */
+#define PECI_PKG_ID_UNCORE_ID 0x0002 /* Uncore Device ID */
+#define PECI_PKG_ID_MAX_THREAD_ID 0x0003 /* Max Thread ID */
+#define PECI_PKG_ID_MICROCODE_REV 0x0004 /* CPU Microcode Update Revision */
+#define PECI_PKG_ID_MACHINE_CHECK_STATUS 0x0005 /* Machine Check Status */
+
+ __u8 rx_len;
+ __u8 cc;
+ __u8 padding[2];
+ __u8 pkg_config[4];
+} __attribute__((__packed__));
+
+/**
+ * struct peci_wr_pkg_cfg_msg - WrPkgConfig command
+ * @addr: address of the client
+ * @index: encoding index for the requested service
+ * @param: specific data being requested
+ * @tx_len: number of data to be written in bytes
+ * @cc: completion code
+ * @value: package config data to be written
+ *
+ * The WrPkgConfig() command provides write access to the Package Configuration
+ * Space (PCS) within the processor, including various power and thermal
+ * management functions. Typical PCS write services supported by the processor
+ * may include power limiting, thermal averaging constant programming and so
+ * on.
+ */
+struct peci_wr_pkg_cfg_msg {
+#define PECI_WRPKGCFG_WRITE_LEN_BASE 6
+#define PECI_WRPKGCFG_READ_LEN 1
+#define PECI_WRPKGCFG_CMD 0xa5
+
+ __u8 addr;
+ __u8 index;
+#define PECI_MBX_INDEX_DIMM_AMBIENT 19
+#define PECI_MBX_INDEX_DIMM_TEMP 24
+
+ __u16 param;
+ __u8 tx_len;
+ __u8 cc;
+ __u8 padding[2];
+ __u32 value;
+} __attribute__((__packed__));
+
+/**
+ * struct peci_rd_ia_msr_msg - RdIAMSR command
+ * @addr: address of the client
+ * @thread_id: ID of the specific logical processor
+ * @address: address of MSR to read from
+ * @cc: completion code
+ * @value: data to be read
+ *
+ * The RdIAMSR() PECI command provides read access to Model Specific Registers
+ * (MSRs) defined in the processor's Intel Architecture (IA).
+ */
+struct peci_rd_ia_msr_msg {
+#define PECI_RDIAMSR_WRITE_LEN 5
+#define PECI_RDIAMSR_READ_LEN 9
+#define PECI_RDIAMSR_CMD 0xb1
+
+ __u8 addr;
+ __u8 thread_id;
+ __u16 address;
+ __u8 cc;
+ __u8 padding[3];
+ __u64 value;
+} __attribute__((__packed__));
+
+/**
+ * struct peci_wr_ia_msr_msg - WrIAMSR command
+ * @addr: address of the client
+ * @thread_id: ID of the specific logical processor
+ * @address: address of MSR to write to
+ * @tx_len: number of data to be written in bytes
+ * @cc: completion code
+ * @value: data to be written
+ *
+ * The WrIAMSR() PECI command provides write access to Model Specific Registers
+ * (MSRs) defined in the processor's Intel Architecture (IA).
+ */
+struct peci_wr_ia_msr_msg {
+#define PECI_WRIAMSR_CMD 0xb5
+
+ __u8 addr;
+ __u8 thread_id;
+ __u16 address;
+ __u8 tx_len;
+ __u8 cc;
+ __u8 padding[2];
+ __u64 value;
+} __attribute__((__packed__));
+
+/**
+ * struct peci_rd_ia_msrex_msg - RdIAMSREX command
+ * @addr: address of the client
+ * @thread_id: ID of the specific logical processor
+ * @address: address of MSR to read from
+ * @cc: completion code
+ * @value: data to be read
+ *
+ * The RdIAMSREX() PECI command provides read access to Model Specific
+ * Registers (MSRs) defined in the processor's Intel Architecture (IA).
+ * The differences between RdIAMSREX() and RdIAMSR() are that:
+ * (1)RdIAMSR() can only read MC registers, RdIAMSREX() can read all MSRs
+ * (2)thread_id of RdIAMSR() is u8, thread_id of RdIAMSREX() is u16
+ */
+struct peci_rd_ia_msrex_msg {
+#define PECI_RDIAMSREX_WRITE_LEN 6
+#define PECI_RDIAMSREX_READ_LEN 9
+#define PECI_RDIAMSREX_CMD 0xd1
+
+ __u8 addr;
+ __u8 padding0;
+ __u16 thread_id;
+ __u16 address;
+ __u8 cc;
+ __u8 padding1;
+ __u64 value;
+} __attribute__((__packed__));
+
+/**
+ * struct peci_rd_pci_cfg_msg - RdPCIConfig command
+ * @addr: address of the client
+ * @bus: PCI bus number
+ * @device: PCI device number
+ * @function: specific function to read from
+ * @reg: specific register to read from
+ * @cc: completion code
+ * @pci_config: config data to be read
+ *
+ * The RdPCIConfig() command provides sideband read access to the PCI
+ * configuration space maintained in downstream devices external to the
+ * processor.
+ */
+struct peci_rd_pci_cfg_msg {
+#define PECI_RDPCICFG_WRITE_LEN 6
+#define PECI_RDPCICFG_READ_LEN 5
+#define PECI_RDPCICFG_READ_LEN_MAX 24
+#define PECI_RDPCICFG_CMD 0x61
+
+ __u8 addr;
+ __u8 bus;
+#define PECI_PCI_BUS0_CPU0 0x00
+#define PECI_PCI_BUS0_CPU1 0x80
+#define PECI_PCI_CPUBUSNO_BUS 0x00
+#define PECI_PCI_CPUBUSNO_DEV 0x08
+#define PECI_PCI_CPUBUSNO_FUNC 0x02
+#define PECI_PCI_CPUBUSNO 0xcc
+#define PECI_PCI_CPUBUSNO_1 0xd0
+#define PECI_PCI_CPUBUSNO_VALID 0xd4
+
+ __u8 device;
+ __u8 function;
+ __u16 reg;
+ __u8 cc;
+ __u8 padding[1];
+ __u8 pci_config[4];
+} __attribute__((__packed__));
+
+/**
+ * struct peci_wr_pci_cfg_msg - WrPCIConfig command
+ * @addr: address of the client
+ * @bus: PCI bus number
+ * @device: PCI device number
+ * @function: specific function to write to
+ * @reg: specific register to write to
+ * @tx_len: number of data to be written in bytes
+ * @cc: completion code
+ * @pci_config: config data to be written
+ *
+ * The RdPCIConfig() command provides sideband write access to the PCI
+ * configuration space maintained in downstream devices external to the
+ * processor.
+ */
+struct peci_wr_pci_cfg_msg {
+#define PECI_WRPCICFG_CMD 0x65
+
+ __u8 addr;
+ __u8 bus;
+ __u8 device;
+ __u8 function;
+ __u16 reg;
+ __u8 tx_len;
+ __u8 cc;
+ __u8 pci_config[4];
+} __attribute__((__packed__));
+
+/**
+ * struct peci_rd_pci_cfg_local_msg - RdPCIConfigLocal command
+ * @addr: address of the client
+ * @bus: PCI bus number
+ * @device: PCI device number
+ * @function: specific function to read from
+ * @reg: specific register to read from
+ * @rx_len: number of data to be read in bytes
+ * @cc: completion code
+ * @pci_config: config data to be read
+ *
+ * The RdPCIConfigLocal() command provides sideband read access to the PCI
+ * configuration space that resides within the processor. This includes all
+ * processor IIO and uncore registers within the PCI configuration space.
+ */
+struct peci_rd_pci_cfg_local_msg {
+#define PECI_RDPCICFGLOCAL_WRITE_LEN 5
+#define PECI_RDPCICFGLOCAL_READ_LEN_BASE 1
+#define PECI_RDPCICFGLOCAL_CMD 0xe1
+
+ __u8 addr;
+ __u8 bus;
+ __u8 device;
+ __u8 function;
+ __u16 reg;
+ __u8 rx_len;
+ __u8 cc;
+ __u8 pci_config[4];
+} __attribute__((__packed__));
+
+/**
+ * struct peci_wr_pci_cfg_local_msg - WrPCIConfigLocal command
+ * @addr: address of the client
+ * @bus: PCI bus number
+ * @device: PCI device number
+ * @function: specific function to read from
+ * @reg: specific register to read from
+ * @tx_len: number of data to be written in bytes
+ * @cc: completion code
+ * @value: config data to be written
+ *
+ * The WrPCIConfigLocal() command provides sideband write access to the PCI
+ * configuration space that resides within the processor. PECI originators can
+ * access this space even before BIOS enumeration of the system buses.
+ */
+struct peci_wr_pci_cfg_local_msg {
+#define PECI_WRPCICFGLOCAL_WRITE_LEN_BASE 6
+#define PECI_WRPCICFGLOCAL_READ_LEN 1
+#define PECI_WRPCICFGLOCAL_CMD 0xe5
+
+ __u8 addr;
+ __u8 bus;
+ __u8 device;
+ __u8 function;
+ __u16 reg;
+ __u8 tx_len;
+ __u8 cc;
+ __u32 value;
+} __attribute__((__packed__));
+
+struct peci_rd_end_pt_cfg_msg {
+#define PECI_RDENDPTCFG_PCI_WRITE_LEN 12
+#define PECI_RDENDPTCFG_MMIO_D_WRITE_LEN 14
+#define PECI_RDENDPTCFG_MMIO_Q_WRITE_LEN 18
+#define PECI_RDENDPTCFG_READ_LEN_BASE 1
+#define PECI_RDENDPTCFG_CMD 0xc1
+
+ __u8 addr;
+ __u8 msg_type;
+#define PECI_ENDPTCFG_TYPE_LOCAL_PCI 0x03
+#define PECI_ENDPTCFG_TYPE_PCI 0x04
+#define PECI_ENDPTCFG_TYPE_MMIO 0x05
+
+ union {
+ struct {
+ __u8 seg;
+ __u8 bus;
+ __u8 device;
+ __u8 function;
+ __u16 reg;
+ } pci_cfg;
+ struct {
+ __u8 seg;
+ __u8 bus;
+ __u8 device;
+ __u8 function;
+ __u8 bar;
+ __u8 addr_type;
+#define PECI_ENDPTCFG_ADDR_TYPE_PCI 0x04
+#define PECI_ENDPTCFG_ADDR_TYPE_MMIO_D 0x05
+#define PECI_ENDPTCFG_ADDR_TYPE_MMIO_Q 0x06
+
+ __u64 offset;
+ } mmio;
+ } params;
+ __u8 rx_len;
+ __u8 cc;
+ __u8 padding[2];
+ __u8 data[8];
+} __attribute__((__packed__));
+
+struct peci_wr_end_pt_cfg_msg {
+#define PECI_WRENDPTCFG_PCI_WRITE_LEN_BASE 13
+#define PECI_WRENDPTCFG_MMIO_D_WRITE_LEN_BASE 15
+#define PECI_WRENDPTCFG_MMIO_Q_WRITE_LEN_BASE 19
+#define PECI_WRENDPTCFG_READ_LEN 1
+#define PECI_WRENDPTCFG_CMD 0xc5
+
+ __u8 addr;
+ __u8 msg_type;
+ /* See msg_type in struct peci_rd_end_pt_cfg_msg */
+
+ union {
+ struct {
+ __u8 seg;
+ __u8 bus;
+ __u8 device;
+ __u8 function;
+ __u16 reg;
+ } pci_cfg;
+ struct {
+ __u8 seg;
+ __u8 bus;
+ __u8 device;
+ __u8 function;
+ __u8 bar;
+ __u8 addr_type;
+ /* See addr_type in struct peci_rd_end_pt_cfg_msg */
+
+ __u64 offset;
+ } mmio;
+ } params;
+ __u8 tx_len;
+ __u8 cc;
+ __u8 padding[2];
+ __u64 value;
+} __attribute__((__packed__));
+
+/* Crashdump Agent */
+#define PECI_CRASHDUMP_CORE 0x00
+#define PECI_CRASHDUMP_TOR 0x01
+
+/* Crashdump Agent Param */
+#define PECI_CRASHDUMP_PAYLOAD_SIZE 0x00
+
+/* Crashdump Agent Data Param */
+#define PECI_CRASHDUMP_AGENT_ID 0x00
+#define PECI_CRASHDUMP_AGENT_PARAM 0x01
+
+struct peci_crashdump_disc_msg {
+ __u8 addr;
+ __u8 subopcode;
+#define PECI_CRASHDUMP_ENABLED 0x00
+#define PECI_CRASHDUMP_NUM_AGENTS 0x01
+#define PECI_CRASHDUMP_AGENT_DATA 0x02
+
+ __u8 cc;
+ __u8 param0;
+ __u16 param1;
+ __u8 param2;
+ __u8 rx_len;
+ __u8 data[8];
+} __attribute__((__packed__));
+
+struct peci_crashdump_get_frame_msg {
+#define PECI_CRASHDUMP_DISC_WRITE_LEN 9
+#define PECI_CRASHDUMP_DISC_READ_LEN_BASE 1
+#define PECI_CRASHDUMP_DISC_VERSION 0
+#define PECI_CRASHDUMP_DISC_OPCODE 1
+#define PECI_CRASHDUMP_GET_FRAME_WRITE_LEN 10
+#define PECI_CRASHDUMP_GET_FRAME_READ_LEN_BASE 1
+#define PECI_CRASHDUMP_GET_FRAME_VERSION 0
+#define PECI_CRASHDUMP_GET_FRAME_OPCODE 3
+#define PECI_CRASHDUMP_CMD 0x71
+
+ __u8 addr;
+ __u8 padding0;
+ __u16 param0;
+ __u16 param1;
+ __u16 param2;
+ __u8 rx_len;
+ __u8 cc;
+ __u8 padding1[2];
+ __u8 data[16];
+} __attribute__((__packed__));
+
+#define PECI_IOC_BASE 0xb7
+
+#define PECI_IOC_XFER \
+ _IOWR(PECI_IOC_BASE, PECI_CMD_XFER, struct peci_xfer_msg)
+
+#define PECI_IOC_PING \
+ _IOWR(PECI_IOC_BASE, PECI_CMD_PING, struct peci_ping_msg)
+
+#define PECI_IOC_GET_DIB \
+ _IOWR(PECI_IOC_BASE, PECI_CMD_GET_DIB, struct peci_get_dib_msg)
+
+#define PECI_IOC_GET_TEMP \
+ _IOWR(PECI_IOC_BASE, PECI_CMD_GET_TEMP, struct peci_get_temp_msg)
+
+#define PECI_IOC_RD_PKG_CFG \
+ _IOWR(PECI_IOC_BASE, PECI_CMD_RD_PKG_CFG, struct peci_rd_pkg_cfg_msg)
+
+#define PECI_IOC_WR_PKG_CFG \
+ _IOWR(PECI_IOC_BASE, PECI_CMD_WR_PKG_CFG, struct peci_wr_pkg_cfg_msg)
+
+#define PECI_IOC_RD_IA_MSR \
+ _IOWR(PECI_IOC_BASE, PECI_CMD_RD_IA_MSR, struct peci_rd_ia_msr_msg)
+
+#define PECI_IOC_WR_IA_MSR \
+ _IOWR(PECI_IOC_BASE, PECI_CMD_WR_IA_MSR, struct peci_wr_ia_msr_msg)
+
+#define PECI_IOC_RD_IA_MSREX \
+ _IOWR(PECI_IOC_BASE, PECI_CMD_RD_IA_MSREX, struct peci_rd_ia_msrex_msg)
+
+#define PECI_IOC_RD_PCI_CFG \
+ _IOWR(PECI_IOC_BASE, PECI_CMD_RD_PCI_CFG, struct peci_rd_pci_cfg_msg)
+
+#define PECI_IOC_WR_PCI_CFG \
+ _IOWR(PECI_IOC_BASE, PECI_CMD_WR_PCI_CFG, struct peci_wr_pci_cfg_msg)
+
+#define PECI_IOC_RD_PCI_CFG_LOCAL \
+ _IOWR(PECI_IOC_BASE, PECI_CMD_RD_PCI_CFG_LOCAL, \
+ struct peci_rd_pci_cfg_local_msg)
+
+#define PECI_IOC_WR_PCI_CFG_LOCAL \
+ _IOWR(PECI_IOC_BASE, PECI_CMD_WR_PCI_CFG_LOCAL, \
+ struct peci_wr_pci_cfg_local_msg)
+
+#define PECI_IOC_RD_END_PT_CFG \
+ _IOWR(PECI_IOC_BASE, PECI_CMD_RD_END_PT_CFG, \
+ struct peci_rd_end_pt_cfg_msg)
+
+#define PECI_IOC_WR_END_PT_CFG \
+ _IOWR(PECI_IOC_BASE, PECI_CMD_WR_END_PT_CFG, \
+ struct peci_wr_end_pt_cfg_msg)
+
+#define PECI_IOC_CRASHDUMP_DISC \
+ _IOWR(PECI_IOC_BASE, PECI_CMD_CRASHDUMP_DISC, \
+ struct peci_crashdump_disc_msg)
+
+#define PECI_IOC_CRASHDUMP_GET_FRAME \
+ _IOWR(PECI_IOC_BASE, PECI_CMD_CRASHDUMP_GET_FRAME, \
+ struct peci_crashdump_get_frame_msg)
+
+#endif /* __PECI_IOCTL_H */