diff options
Diffstat (limited to 'meta-openbmc-mods/meta-common/recipes-kernel')
70 files changed, 17866 insertions, 0 deletions
diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0001-arm-dts-add-DTS-for-Intel-ast2500-platforms.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0001-arm-dts-add-DTS-for-Intel-ast2500-platforms.patch new file mode 100644 index 000000000..31061624c --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0001-arm-dts-add-DTS-for-Intel-ast2500-platforms.patch @@ -0,0 +1,492 @@ +From da2fecd5d5b3f69bcc4d07fd1265415bd350e5a6 Mon Sep 17 00:00:00 2001 +From: Yuan Li <yuan.li@linux.intel.com> +Date: Tue, 19 Sep 2017 15:55:39 +0800 +Subject: [PATCH] arm: dts: add DTS for Intel ast2500 platforms + +Add the DTS file for Intel ast2500-based systems. + +Signed-off-by: Yuan Li <yuan.li@linux.intel.com> +Signed-off-by: Yong Li <yong.b.li@linux.intel.com> +Signed-off-by: James Feist <james.feist@linux.intel.com> +Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +Signed-off-by: Jason M. Bills <jason.m.bills@linux.intel.com> +Signed-off-by: Zhu, Yunge <yunge.zhu@linux.intel.com> +Signed-off-by: Qiang XU <qiang.xu@linux.intel.com> +Signed-off-by: Chen Yugang <yugang.chen@linux.intel.com> +Signed-off-by: Zhikui Ren <zhikui.ren@intel.com> +Signed-off-by: jayaprakash Mutyala <mutyalax.jayaprakash@intel.com> +Signed-off-by: AppaRao Puli <apparao.puli@linux.intel.com> +--- + arch/arm/boot/dts/aspeed-bmc-intel-ast2500.dts | 460 +++++++++++++++++++++++++ + 1 file changed, 460 insertions(+) + create mode 100644 arch/arm/boot/dts/aspeed-bmc-intel-ast2500.dts + +diff --git a/arch/arm/boot/dts/aspeed-bmc-intel-ast2500.dts b/arch/arm/boot/dts/aspeed-bmc-intel-ast2500.dts +new file mode 100644 +index 0000000..1fe6240 +--- /dev/null ++++ b/arch/arm/boot/dts/aspeed-bmc-intel-ast2500.dts +@@ -0,0 +1,460 @@ ++/dts-v1/; ++ ++#include "aspeed-g5.dtsi" ++#include <dt-bindings/gpio/aspeed-gpio.h> ++#include <dt-bindings/i2c/i2c.h> ++ ++/ { ++ model = "Intel AST2500 BMC"; ++ compatible = "intel,ast2500-bmc", "aspeed,ast2500"; ++ ++ aliases { ++ serial4 = &uart5; ++ }; ++ ++ chosen { ++ stdout-path = &uart5; ++ bootargs = "console=ttyS4,115200 earlyprintk"; ++ }; ++ ++ memory@80000000 { ++ reg = <0x80000000 0x20000000>; ++ }; ++ ++ reserved-memory { ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ranges; ++ ++ vga_memory: framebuffer@7f000000 { ++ no-map; ++ reg = <0x7f000000 0x01000000>; ++ }; ++ ++ gfx_memory: framebuffer { ++ size = <0x01000000>; ++ alignment = <0x01000000>; ++ compatible = "shared-dma-pool"; ++ reusable; ++ }; ++ ++ video_engine_memory: jpegbuffer { ++ size = <0x02000000>; /* 32M */ ++ alignment = <0x01000000>; ++ compatible = "shared-dma-pool"; ++ reusable; ++ }; ++ ++ ramoops@9eff0000{ ++ compatible = "ramoops"; ++ reg = <0x9eff0000 0x10000>; ++ record-size = <0x2000>; ++ console-size = <0x2000>; ++ }; ++ }; ++ ++ vga-shared-memory { ++ compatible = "aspeed,ast2500-vga-sharedmem"; ++ reg = <0x9ff00000 0x100000>; ++ }; ++ ++ iio-hwmon { ++ compatible = "iio-hwmon"; ++ io-channels = <&adc 0>, <&adc 1>, <&adc 2>, <&adc 3>, ++ <&adc 4>, <&adc 5>, <&adc 6>, <&adc 7>, ++ <&adc 8>, <&adc 9>, <&adc 10>, <&adc 11>, ++ <&adc 12>, <&adc 13>, <&adc 14>, <&adc 15>; ++ }; ++ ++ leds { ++ compatible = "gpio-leds"; ++ ++ identify { ++ default-state = "on"; ++ gpios = <&gpio ASPEED_GPIO(S, 6) GPIO_ACTIVE_LOW>; ++ }; ++ ++ status_amber { ++ default-state = "off"; ++ gpios = <&gpio ASPEED_GPIO(S, 5) GPIO_ACTIVE_LOW>; ++ }; ++ ++ status_green { ++ default-state = "keep"; ++ gpios = <&gpio ASPEED_GPIO(S, 4) GPIO_ACTIVE_LOW>; ++ }; ++ }; ++ ++ beeper { ++ compatible = "pwm-beeper"; ++ pwms = <&timer 6 1000000 0>; ++ }; ++}; ++ ++&fmc { ++ status = "okay"; ++ flash@0 { ++ status = "okay"; ++ m25p,fast-read; ++#include "openbmc-flash-layout-intel-64MB.dtsi" ++ }; ++}; ++ ++&espi { ++ status = "okay"; ++}; ++ ++&jtag { ++ status = "okay"; ++}; ++ ++&peci0 { ++ status = "okay"; ++ gpios = <&gpio ASPEED_GPIO(F, 6) 0>; ++}; ++ ++&syscon { ++ uart-clock-high-speed; ++ status = "okay"; ++ ++ misc_control { ++ compatible = "aspeed,bmc-misc"; ++ uart_port_debug { ++ offset = <0x2c>; ++ bit-mask = <0x1>; ++ bit-shift = <10>; ++ read-only; ++ }; ++ p2a-bridge { ++ offset = <0x180>; ++ bit-mask = <0x1>; ++ bit-shift = <1>; ++ read-only; ++ }; ++ boot-2nd-flash { ++ offset = <0x70>; ++ bit-mask = <0x1>; ++ bit-shift = <17>; ++ read-only; ++ }; ++ }; ++}; ++ ++&adc { ++ status = "okay"; ++}; ++ ++&gpio { ++ status = "okay"; ++ gpio-line-names = ++ /*A0-A7*/ "","","","","","","","", ++ /*B0-B7*/ "FM_BMC_BOARD_SKU_ID0_N","FM_BMC_BOARD_SKU_ID1_N","FM_BMC_BOARD_SKU_ID2_N","FM_BMC_BOARD_SKU_ID3_N","FM_BMC_BOARD_SKU_ID4_N","","","", ++ /*C0-C7*/ "","","","","","","","", ++ /*D0-D7*/ "","","","","","","","", ++ /*E0-E7*/ "RESET_BUTTON","RESET_OUT","POWER_BUTTON","POWER_OUT","","DEBUG_EN_N","","", ++ /*F0-F7*/ "NMI_OUT","","","","CPU_ERR0","CPU_ERR1","PLTRST_N","PRDY_N", ++ /*G0-G7*/ "CPU_ERR2","CPU_CATERR","PCH_BMC_THERMTRIP","LCP_ENTER_BUTTON","LCP_LEFT_BUTTON","FM_BMC_BOARD_SKU_ID5_N","","", ++ /*H0-H7*/ "","","","FM_NODE_ID_1","FM_NODE_ID_2","FM_NODE_ID_3","FM_NODE_ID_4","FM_240VA_STATUS", ++ /*I0-I7*/ "FM_SYS_FAN0_PRSNT_D_N","FM_SYS_FAN1_PRSNT_D_N","FM_SYS_FAN2_PRSNT_D_N","FM_SYS_FAN3_PRSNT_D_N","FM_SYS_FAN4_PRSNT_D_N","FM_SYS_FAN5_PRSNT_D_N","","", ++ /*J0-J7*/ "","","","","","","","", ++ /*K0-K7*/ "","","","","","","","", ++ /*L0-L7*/ "","","","","","","","", ++ /*M0-M7*/ "","","","","","","","", ++ /*N0-N7*/ "","","","","","","","", ++ /*O0-O7*/ "","","","","","","","", ++ /*P0-P7*/ "","","","","","","","", ++ /*Q0-Q7*/ "","","","","","","","PWR_DEBUG_N", ++ /*R0-R7*/ "","XDP_PRST_N","","","","","","CHASSIS_INTRUSION", ++ /*S0-S7*/ "REMOTE_DEBUG_ENABLE","SYSPWROK","RSMRST_N","","","","","", ++ /*T0-T7*/ "","","","","","","","", ++ /*U0-U7*/ "","","","","","","","", ++ /*V0-V7*/ "","","","","","","","", ++ /*W0-W7*/ "","","","","","","","", ++ /*X0-X7*/ "","","","","","","","", ++ /*Y0-Y7*/ "SIO_S3","SIO_S5","","SIO_ONCONTROL","","","","", ++ /*Z0-Z7*/ "","SIO_POWER_GOOD","","","","","","", ++ /*AA0-AA7*/ "P3VBAT_BRIDGE_EN","","","","PREQ_N","TCK_MUX_SEL","SMI","POST_COMPLETE", ++ /*AB0-AB7*/ "","NMI_BUTTON","ID_BUTTON","PS_PWROK","","","","", ++ /*AC0-AC7*/ "","","","","","","",""; ++}; ++ ++&sgpio { ++ status = "okay"; ++ gpio-line-names = ++ /* SGPIO output lines */ ++ /*OA0-OA7*/ "","","","","","","","", ++ /*OB0-OB7*/ "LED_CPU1_CH1_DIMM1_FAULT","LED_CPU1_CH1_DIMM2_FAULT","LED_CPU1_CH2_DIMM1_FAULT","LED_CPU1_CH2_DIMM2_FAULT","LED_CPU1_CH3_DIMM1_FAULT","LED_CPU1_CH3_DIMM2_FAULT","LED_CPU1_CH4_DIMM1_FAULT","LED_CPU1_CH4_DIMM2_FAULT", ++ /*OC0-OC7*/ "LED_CPU1_CH5_DIMM1_FAULT","LED_CPU1_CH5_DIMM2_FAULT","LED_CPU1_CH6_DIMM1_FAULT","LED_CPU1_CH6_DIMM2_FAULT","LED_FAN1_FAULT","LED_FAN2_FAULT","LED_FAN3_FAULT","LED_FAN4_FAULT", ++ /*OD0-OD7*/ "LED_FAN5_FAULT","LED_FAN6_FAULT","LED_FAN7_FAULT","LED_FAN8_FAULT","LED_CPU2_CH1_DIMM1_FAULT","LED_CPU1_CH1_DIMM2_FAULT","LED_CPU2_CH2_DIMM1_FAULT","LED_CPU2_CH2_DIMM2_FAULT", ++ /*OE0-OE7*/ "LED_CPU2_CH3_DIMM1_FAULT","LED_CPU2_CH3_DIMM2_FAULT","LED_CPU2_CH4_DIMM1_FAULT","LED_CPU2_CH4_DIMM2_FAULT","LED_CPU2_CH5_DIMM1_FAULT","LED_CPU2_CH5_DIMM2_FAULT","LED_CPU2_CH6_DIMM1_FAULT","LED_CPU2_CH6_DIMM2_FAULT", ++ /*OF0-OF7*/ "LED_CPU3_CH1_DIMM1_FAULT","LED_CPU3_CH1_DIMM2_FAULT","LED_CPU3_CH2_DIMM1_FAULT","LED_CPU3_CH2_DIMM2_FAULT","LED_CPU3_CH3_DIMM1_FAULT","LED_CPU3_CH3_DIMM2_FAULT","LED_CPU3_CH4_DIMM1_FAULT","LED_CPU3_CH4_DIMM2_FAULT", ++ /*OG0-OG7*/ "LED_CPU3_CH5_DIMM1_FAULT","LED_CPU3_CH5_DIMM2_FAULT","LED_CPU3_CH6_DIMM1_FAULT","LED_CPU3_CH6_DIMM2_FAULT","LED_CPU4_CH1_DIMM1_FAULT","LED_CPU4_CH1_DIMM2_FAULT","LED_CPU4_CH2_DIMM1_FAULT","LED_CPU4_CH2_DIMM2_FAULT", ++ /*OH0-OH7*/ "LED_CPU4_CH3_DIMM1_FAULT","LED_CPU4_CH3_DIMM2_FAULT","LED_CPU4_CH4_DIMM1_FAULT","LED_CPU4_CH4_DIMM2_FAULT","LED_CPU4_CH5_DIMM1_FAULT","LED_CPU4_CH5_DIMM2_FAULT","LED_CPU4_CH6_DIMM1_FAULT","LED_CPU4_CH6_DIMM2_FAULT", ++ /*OI0-OI7*/ "","","","","","","","", ++ /*OJ0-OJ7*/ "","","","","","","","", ++ /*DUMMY*/ "","","","","","","","", ++ /*DUMMY*/ "","","","","","","","", ++ ++ /* SGPIO input lines */ ++ /*IA0-IA7*/ "CPU1_PRESENCE","CPU1_THERMTRIP","CPU1_VRHOT","CPU1_FIVR_FAULT","CPU1_MEM_ABCD_VRHOT","CPU1_MEM_EFGH_VRHOT","","", ++ /*IB0-IB7*/ "CPU1_MISMATCH","CPU1_MEM_THERM_EVENT","CPU2_PRESENCE","CPU2_THERMTRIP","CPU2_VRHOT","CPU2_FIVR_FAULT","CPU2_MEM_ABCD_VRHOT","CPU2_MEM_EFGH_VRHOT", ++ /*IC0-IC7*/ "","","CPU2_MISMATCH","CPU2_MEM_THERM_EVENT","","","","", ++ /*ID0-ID7*/ "","","","","","","","", ++ /*IE0-IE7*/ "","","","","","","","", ++ /*IF0-IF7*/ "","","","","","","","", ++ /*IG0-IG7*/ "","","","","","","","", ++ /*IH0-IH7*/ "","","","","","","","", ++ /*II0-II7*/ "","","","","","","","", ++ /*IJ0-IJ7*/ "","","","","","","",""; ++}; ++ ++&kcs3 { ++ kcs_addr = <0xCA2>; ++ status = "okay"; ++}; ++ ++&kcs4 { ++ kcs_addr = <0xCA4>; ++ status = "okay"; ++}; ++ ++&lpc_sio { ++ status = "okay"; ++}; ++ ++&lpc_snoop { ++ snoop-ports = <0x80>; ++ status = "okay"; ++}; ++ ++&mbox { ++ status = "okay"; ++}; ++ ++&uart_routing { ++ status = "okay"; ++}; ++ ++/** ++ * SAFS through SPI1 is available only on Wilson Point. ++ * These pins are used as fan presence checking gpios in WFP ++ * so commenting it out for now. ++ * &spi1 { ++ * status = "okay"; ++ * ++ * flash@0 { ++ * m25p,fast-read; ++ * status = "okay"; ++ * }; ++ *}; ++ */ ++ ++&uart1 { ++ status = "okay"; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_txd1_default ++ &pinctrl_rxd1_default ++ &pinctrl_nrts1_default ++ &pinctrl_ndtr1_default ++ &pinctrl_ndsr1_default ++ &pinctrl_ncts1_default ++ &pinctrl_ndcd1_default ++ &pinctrl_nri1_default>; ++}; ++ ++&uart2 { ++ status = "okay"; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_txd2_default ++ &pinctrl_rxd2_default ++ &pinctrl_nrts2_default ++ &pinctrl_ndtr2_default ++ &pinctrl_ndsr2_default ++ &pinctrl_ncts2_default ++ &pinctrl_ndcd2_default ++ &pinctrl_nri2_default>; ++}; ++ ++&uart3 { ++ status = "okay"; ++}; ++ ++&uart4 { ++ status = "okay"; ++ pinctrl-names = "default"; ++ pinctrl-0 = <>; ++}; ++ ++&uart5 { ++ status = "okay"; ++}; ++ ++&mac1 { ++ status = "okay"; ++ ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_rgmii2_default &pinctrl_mdio2_default>; ++}; ++ ++&mac0 { ++ status = "okay"; ++ use-ncsi; ++ ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_rmii1_default>; ++}; ++ ++&i2c0 { ++ multi-master; ++ general-call; ++ aspeed,dma-buf-size = <4095>; ++ aspeed,hw-timeout-ms = <300>; ++ status = "okay"; ++}; ++ ++&i2c1 { ++ multi-master; ++ aspeed,dma-buf-size = <4095>; ++ aspeed,hw-timeout-ms = <300>; ++ status = "okay"; ++}; ++ ++&i2c2 { ++ multi-master; ++ aspeed,dma-buf-size = <4095>; ++ aspeed,hw-timeout-ms = <300>; ++ status = "okay"; ++}; ++ ++&i2c3 { ++ multi-master; ++ aspeed,dma-buf-size = <4095>; ++ aspeed,hw-timeout-ms = <300>; ++ status = "okay"; ++}; ++ ++&i2c4 { ++ multi-master; ++ aspeed,dma-buf-size = <4095>; ++ aspeed,hw-timeout-ms = <300>; ++ status = "okay"; ++}; ++ ++&i2c5 { ++ bus-frequency = <1000000>; ++ multi-master; ++ aspeed,dma-buf-size = <4095>; ++ aspeed,hw-timeout-ms = <300>; ++ status = "okay"; ++}; ++ ++&i2c6 { ++ multi-master; ++ aspeed,dma-buf-size = <4095>; ++ aspeed,hw-timeout-ms = <300>; ++ status = "okay"; ++}; ++ ++&i2c7 { ++ multi-master; ++ #retries = <3>; ++ aspeed,dma-buf-size = <4095>; ++ aspeed,hw-timeout-ms = <300>; ++ status = "okay"; ++}; ++ ++&i2c9 { ++ multi-master; ++ aspeed,dma-buf-size = <4095>; ++ aspeed,hw-timeout-ms = <300>; ++ status = "okay"; ++}; ++ ++&i2c11 { ++ multi-master; ++ aspeed,dma-buf-size = <4095>; ++ aspeed,hw-timeout-ms = <300>; ++ status = "okay"; ++}; ++ ++&i2c13 { ++ multi-master; ++ aspeed,dma-buf-size = <4095>; ++ aspeed,hw-timeout-ms = <300>; ++ status = "okay"; ++}; ++ ++&gfx { ++ status = "okay"; ++ memory-region = <&gfx_memory>; ++}; ++ ++&vuart { ++ 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 0x01>; ++ }; ++ fan@1 { ++ reg = <0x01>; ++ aspeed,fan-tach-ch = /bits/ 8 <0x02 0x03>; ++ }; ++ fan@2 { ++ reg = <0x02>; ++ aspeed,fan-tach-ch = /bits/ 8 <0x04 0x05>; ++ }; ++ fan@3 { ++ reg = <0x03>; ++ aspeed,fan-tach-ch = /bits/ 8 <0x06 0x07>; ++ }; ++ fan@4 { ++ reg = <0x04>; ++ aspeed,fan-tach-ch = /bits/ 8 <0x08 0x09>; ++ }; ++ fan@5 { ++ reg = <0x05>; ++ aspeed,fan-tach-ch = /bits/ 8 <0x0A 0x0B>; ++ }; ++ fan@6 { ++ reg = <0x06>; ++ aspeed,fan-tach-ch = /bits/ 8 <0x0C 0x0D>; ++ }; ++ fan@7 { ++ reg = <0x07>; ++ aspeed,fan-tach-ch = /bits/ 8 <0x0E 0x0F>; ++ }; ++ ++}; ++ ++&timer { ++/* ++ * Available settings: ++ * fttmr010,pwm-outputs = <5>, <6>, <7>, <8>; ++ * pinctrl-0 = <&pinctrl_timer5_default &pinctrl_timer6_default ++ * &pinctrl_timer7_default &pinctrl_timer8_default>; ++ */ ++ fttmr010,pwm-outputs = <6>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_timer6_default>; ++ #pwm-cells = <3>; ++ status = "okay"; ++}; ++ ++&video { ++ status = "okay"; ++ memory-region = <&video_engine_memory>; ++}; ++ ++&vhub { ++ status = "okay"; ++}; +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0001-arm-dts-add-DTS-for-Intel-ast2600-platforms.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0001-arm-dts-add-DTS-for-Intel-ast2600-platforms.patch new file mode 100644 index 000000000..885e15e4c --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0001-arm-dts-add-DTS-for-Intel-ast2600-platforms.patch @@ -0,0 +1,525 @@ +From c8c8503401e8593860b4b78340a50d55a1a780b3 Mon Sep 17 00:00:00 2001 +From: Vernon Mauery <vernon.mauery@linux.intel.com> +Date: Tue, 19 Sep 2017 15:55:39 +0800 +Subject: [PATCH] arm: dts: add DTS for Intel ast2600 platforms + +Add the DTS file for Intel ast2600-based systems. + +Signed-off-by: Vernon Mauery <vernon.mauery@linux.intel.com> +Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com> +Signed-off-by: Chen Yugang <yugang.chen@linux.intel.com> +Signed-off-by: Kuiying Wang <kuiying.wang@intel.com> +Signed-off-by: arun-pm <arun.p.m@linux.intel.com> +Signed-off-by: Ayushi Smriti <smriti.ayushi@intel.com> +--- + arch/arm/boot/dts/aspeed-bmc-intel-ast2600.dts | 498 +++++++++++++++++++++++++ + 1 file changed, 498 insertions(+) + create mode 100644 arch/arm/boot/dts/aspeed-bmc-intel-ast2600.dts + +diff --git a/arch/arm/boot/dts/aspeed-bmc-intel-ast2600.dts b/arch/arm/boot/dts/aspeed-bmc-intel-ast2600.dts +new file mode 100644 +index 000000000000..c51ed3c57f3e +--- /dev/null ++++ b/arch/arm/boot/dts/aspeed-bmc-intel-ast2600.dts +@@ -0,0 +1,498 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++/dts-v1/; ++ ++#include "aspeed-g6.dtsi" ++#include <dt-bindings/gpio/aspeed-gpio.h> ++#include <dt-bindings/i2c/i2c.h> ++ ++/ { ++ model = "AST2600 EVB"; ++ compatible = "aspeed,ast2600"; ++ ++ chosen { ++ stdout-path = &uart5; ++ bootargs = "console=tty0 console=ttyS4,115200n8 root=/dev/ram rw init=/linuxrc"; ++ }; ++ ++ memory@80000000 { ++ device_type = "memory"; ++ reg = <0x80000000 0x40000000>; ++ }; ++ ++ reserved-memory { ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ranges; ++ ++ gfx_memory: framebuffer { ++ size = <0x01000000>; ++ alignment = <0x01000000>; ++ compatible = "shared-dma-pool"; ++ reusable; ++ }; ++ ++ video_engine_memory: jpegbuffer { ++ size = <0x02000000>; /* 32M */ ++ alignment = <0x01000000>; ++ compatible = "shared-dma-pool"; ++ reusable; ++ }; ++ }; ++ ++ reserved-memory { ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ranges; ++ video_memory: video { ++ size = <0x04000000>; ++ alignment = <0x01000000>; ++ compatible = "shared-dma-pool"; ++ no-map; ++ }; ++ }; ++ ++ iio-hwmon { ++ compatible = "iio-hwmon"; ++ io-channels = <&adc0 0>, <&adc0 1>, <&adc0 2>, <&adc0 3>, ++ <&adc0 4>, <&adc0 5>, <&adc0 6>, <&adc0 7>, ++ <&adc1 0>, <&adc1 1>, <&adc1 2>, <&adc1 3>, ++ <&adc1 4>, <&adc1 5>, <&adc1 6>, <&adc1 7>; ++ }; ++ ++ leds { ++ compatible = "gpio-leds"; ++ ++ identify { ++ default-state = "off"; ++ gpios = <&gpio1 ASPEED_GPIO(B, 7) GPIO_ACTIVE_LOW>; ++ }; ++ ++ status_amber { ++ default-state = "off"; ++ gpios = <&gpio1 ASPEED_GPIO(G, 3) GPIO_ACTIVE_LOW>; ++ }; ++ ++ status_green { ++ default-state = "keep"; ++ gpios = <&gpio1 ASPEED_GPIO(G, 2) GPIO_ACTIVE_LOW>; ++ }; ++ }; ++/* ++ beeper { ++ compatible = "pwm-beeper"; ++ pwms = <&timer 7 1000000 0>; ++ }; */ ++}; ++ ++&fmc { ++ status = "okay"; ++ flash: m25p80@0 { ++ compatible = "m25p80", "jedec,spi-nor"; ++ reg = <0x0>; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ spi-max-frequency = <40000000>; ++ spi-tx-bus-width = <4>; ++ m25p,fast-read; ++#include "openbmc-flash-layout-intel-64MB.dtsi" ++ }; ++}; ++ ++&espi { ++ status = "okay"; ++}; ++ ++&peci0 { ++ status = "okay"; ++ gpios = <&gpio0 ASPEED_GPIO(F, 6) 0>; ++}; ++ ++&syscon { ++ uart-clock-high-speed; ++ status = "okay"; ++}; ++ ++#if 0 ++ GPIO Alias: (runtime alias -> schematic name) ++ ID_BUTTON -> FP_ID_BTN_N ++ CPU_CATERR -> FM_PLT_BMC_THERMTRIP_N ++ PCH_BMC_THERMTRIP -> FM_PLT_BMC_THERMTRIP_N ++ RESET_BUTTON -> FP_BMC_RST_BTN_N ++ RESET_OUT -> RST_BMC_RSTBTN_OUT_R_N ++ POWER_BUTTON -> FP_BMC_PWR_BTN_R_N ++ POWER_OUT -> FM_BMC_PWR_BTN_N ++ PREQ_N -> DBP_ASD_BMC_PREQ_R_N ++ POST_COMPLETE -> FM_BIOS_POST_CMPLT_BMC_N ++ CPU_ERR0 -> FM_CPU_ERR0_LVT3_N ++ CPU_ERR1 -> FM_CPU_ERR1_LVT3_N ++ CPU_ERR2 -> FM_CPU_ERR2_LVT3_N ++ DEBUG_EN_N -> FM_JTAG_TCK_MUX_SEL_R ++ NMI_OUT -> IRQ_BMC_CPU_NMI_R ++ PLTRST_N -> RST_PLTRST_BMC_N ++ PRDY_N -> DBP_ASD_BMC_PRDY_R_N ++ PWR_DEBUG_N -> ++ XDP_PRST_N -> ++ SYSPWROK -> ++ RSMRST_N -> ++ SIO_S3 -> FM_SLPS3_R_N ++ SIO_S5 -> FM_SLPS4_R_N ++ SIO_ONCONTROL -> FM_BMC_ONCTL_R_N ++ SIO_POWER_GOOD -> PWRGD_CPU0_LVC3_R ++ PS_PWROK -> PWRGD_BMC_PS_PWROK_R ++ P3VBAT_BRIDGE_EN -> ++ TCK_MUX_SEL -> ++ SMI -> IRQ_SMI_ACTIVE_BMC_N ++ NMI_BUTTON -> FP_NMI_BTN_N ++#endif ++&gpio0 { ++ status = "okay"; ++ /* Enable GPIOP0 and GPIOP2 pass-through by default */ ++ /* pinctrl-names = "pass-through"; ++ pinctrl-0 = <&pinctrl_thru0_default ++ &pinctrl_thru1_default>; */ ++ gpio-line-names = ++ /*A0-A7*/ "","","","","SMB_CPU_PIROM_SCL","SMB_CPU_PIROM_SDA","SMB_IPMB_STBY_LVC3_R_SCL","SMB_IPMB_STBY_LVC3_R_SDA", ++ /*B0-B7*/ "FM_1200VA_OC","NMI_OUT","IRQ_SMB3_M2_ALERT_N","","RGMII_BMC_RMM4_LVC3_R_MDC","RGMII_BMC_RMM4_LVC3_R_MDIO","FM_BMC_BMCINIT_R","FP_ID_LED_N", ++ /*C0-C7*/ "FM_FORCE_BMC_UPDATE_N","RST_RGMII_PHYRST_N","FM_TPM_EN_PULSE","FM_BMC_CRASHLOG_TRIG_N","IRQ_BMC_PCH_NMI_R","FM_CPU1_DISABLE_COD_N","FM_4S_8S_N_MODE","FM_STANDALONE_MODE_N", ++ /*D0-D7*/ "CPU_ERR0","CPU_ERR1","CPU_ERR2","PRDY_N","FM_SPD_SWITCH_CTRL_N","","","", ++ /*E0-E7*/ "FM_SKT1_FAULT_LED","FM_SKT0_FAULT_LED","CLK_50M_CKMNG_BMCB","FM_BMC_BOARD_REV_ID2_N","","","","", ++ /*F0-F7*/ "FM_BMC_BOARD_SKU_ID0_N","FM_BMC_BOARD_SKU_ID1_N","FM_BMC_BOARD_SKU_ID2_N","FM_BMC_BOARD_SKU_ID3_N","FM_BMC_BOARD_SKU_ID4_N","FM_BMC_BOARD_SKU_ID5_N","ID_BUTTON","PS_PWROK", ++ /*G0-G7*/ "FM_SMB_BMC_NVME_LVC3_ALERT_N","RST_BMC_I2C_M2_R_N","FP_LED_STATUS_GREEN_N","FP_LED_STATUS_AMBER_N","FM_BMC_BOARD_REV_ID0_N","FM_BMC_BOARD_REV_ID1_N","FM_BMC_CPU_FBRK_OUT_R_N","DBP_PRESENT_IN_R2_N", ++ /*H0-H7*/ "SGPIO_BMC_CLK_R","SGPIO_BMC_LD_R","SGPIO_BMC_DOUT_R","SGPIO_BMC_DIN","PLTRST_N","CPU_CATERR","PCH_BMC_THERMTRIP","", ++ /*I0-I7*/ "JTAG_ASD_NTRST_R_N","JTAG_ASD_TDI_R","JTAG_ASD_TCK_R","JTAG_ASD_TMS_R","JTAG_ASD_TDO","FM_BMC_PWRBTN_OUT_R_N","FM_BMC_PWR_BTN_N","", ++ /*J0-J7*/ "SMB_CHASSENSOR_STBY_LVC3_SCL","SMB_CHASSENSOR_STBY_LVC3_SDA","FM_NODE_ID0","FM_NODE_ID1","","","","", ++ /*K0-K7*/ "SMB_HSBP_STBY_LVC3_R_SCL","SMB_HSBP_STBY_LVC3_R_SDA","SMB_SMLINK0_STBY_LVC3_R2_SCL","SMB_SMLINK0_STBY_LVC3_R2_SDA","SMB_TEMPSENSOR_STBY_LVC3_R_SCL","SMB_TEMPSENSOR_STBY_LVC3_R_SDA","SMB_PMBUS_SML1_STBY_LVC3_R_SCL","SMB_PMBUS_SML1_STBY_LVC3_R_SDA", ++ /*L0-L7*/ "SMB_PCIE_STBY_LVC3_R_SCL","SMB_PCIE_STBY_LVC3_R_SDA","SMB_HOST_STBY_BMC_LVC3_R_SCL","SMB_HOST_STBY_BMC_LVC3_R_SDA","PREQ_N","DEBUG_EN_N","V_BMC_GFX_HSYNC_R","V_BMC_GFX_VSYNC_R", ++ /*M0-M7*/ "SPA_CTS_N","SPA_DCD_N","SPA_DSR_N","PU_SPA_RI_N","SPA_DTR_N","SPA_RTS_N","SPA_SOUT","SPA_SIN", ++ /*N0-N7*/ "SPB_CTS_N","SPB_DCD_N","SPB_DSR_N","PU_SPB_RI_N","SPB_DTR_N","SPB_RTS_N","SPB_SOUT","SPB_SIN", ++ /*O0-O7*/ "FAN_BMC_PWM0","FAN_BMC_PWM1","FAN_BMC_PWM2","FAN_BMC_PWM3","FAN_BMC_PWM4","FAN_BMC_PWM5","NMI_BUTTON","SPEAKER_BMC_R", ++ /*P0-P7*/ "RESET_BUTTON","RESET_OUT","POWER_BUTTON","POWER_OUT","FAN_BMC_PWM6","FAN_BMC_PWM7","FAN_BMC_PWM8","FAN_BMC_PWM9", ++ /*Q0-Q7*/ "FAN_BMC_TACH0","FAN_BMC_TACH1","FAN_BMC_TACH2","FAN_BMC_TACH3","FAN_BMC_TACH4","FAN_BMC_TACH5","FAN_BMC_TACH6","FAN_BMC_TACH7", ++ /*R0-R7*/ "FAN_BMC_TACH8","FAN_BMC_TACH9","","","","","","", ++ /*S0-S7*/ "RST_BMC_PCIE_MUX_N","FM_BMC_EUP_LOT6_N","","","","A_P3V_BAT_SCALED_EN","REMOTE_DEBUG_ENABLE","FM_PCHHOT_N", ++ /*T0-T7*/ "A_P12V_PSU_SCALED","A_P12V_AUX_SCALED","A_P3V3_SCALED","A_P5V_SCALED","A_PVNN_PCH_AUX_SENSOR","A_P1V05_PCH_AUX_SENSOR","A_P1V8_AUX_SENSOR","A_P3V_BAT_SCALED", ++ /*U0-U7*/ "A_PVCCIN_CPU0_SENSOR","A_PVCCIN_CPU1_SENSOR","A_PVCCINFAON_CPU0_SENSOR","A_PVCCINFAON_CPU1_SENSOR","A_PVCCFA_EHV_FIVRA_CPU0_SENSOR","A_PVCCFA_EHV_FIVRA_CPU1_SENSOR","A_PVCCD_HV_CPU0_SENSOR","A_PVCCD_HV_CPU1_SENSOR", ++ /*V0-V7*/ "SIO_S3","SIO_S5","TP_BMC_SIO_PWREQ_N","SIO_ONCONTROL","SIO_POWER_GOOD","LED_BMC_HB_LED_N","FM_BMC_SUSACK_N","", ++ /*W0-W7*/ "LPC_LAD0_ESPI_R_IO0","LPC_LAD1_ESPI_R_IO1","LPC_LAD2_ESPI_R_IO2","LPC_LAD3_ESPI_R_IO3","CLK_24M_66M_LPC0_ESPI_BMC","LPC_LFRAME_N_ESPI_CS0_BMC_N","IRQ_LPC_SERIRQ_ESPI_ALERT_N","RST_LPC_LRST_ESPI_RST_BMC_R_N", ++ /*X0-X7*/ "","SMI","POST_COMPLETE","","","","","", ++ /*Y0-Y7*/ "","IRQ_SML0_ALERT_BMC_R2_N","","IRQ_SML1_PMBUS_BMC_ALERT_N","SPI_BMC_BOOT_R_IO2","SPI_BMC_BOOT_R_IO3","PU_SPI_BMC_BOOT_ABR","PU_SPI_BMC_BOOT_WP_N", ++ /*Z0-Z7*/ "PWRGD_P3V3_RISER1","PWRGD_P3V3_RISER2","","HW_STRAP_5","HW_STRAP_6","HW_STRAP_7","HW_STRAP_2","HW_STRAP_3"; ++}; ++ ++&gpio1 { ++ status = "disabled"; ++ gpio-line-names = /* GPIO18 A-E */ ++ /*A0-A7*/ "","","RST_EMMC_BMC_R_N","FM_SYS_FAN6_PRSNT_D_N","FM_SYS_FAN0_PRSNT_D_N","FM_SYS_FAN1_PRSNT_D_N","FM_SYS_FAN2_PRSNT_D_N","FM_SYS_FAN3_PRSNT_D_N", ++ /*B0-B7*/ "FM_SYS_FAN4_PRSNT_D_N","FM_SYS_FAN5_PRSNT_D_N","","FM_SYS_FAN7_PRSNT_D_N","RGMII_BMC_RMM4_TX_R_CLK","RGMII_BMC_RMM4_TX_R_CTRL","RGMII_BMC_RMM4_R_TXD0","RGMII_BMC_RMM4_R_TXD1", ++ /*C0-C7*/ "RGMII_BMC_RMM4_R_TXD2","RGMII_BMC_RMM4_R_TXD3","RGMII_BMC_RMM4_RX_CLK","RGMII_BMC_RMM4_RX_CTRL","RGMII_BMC_RMM4_RXD0","RGMII_BMC_RMM4_RXD1","RGMII_BMC_RMM4_RXD2","RGMII_BMC_RMM4_RXD3", ++ /*D0-D7*/ "EMMC_BMC_R_CLK","EMMC_BMC_R_CMD","EMMC_BMC_R_DATA0","EMMC_BMC_R_DATA1","EMMC_BMC_R_DATA2","EMMC_BMC_R_DATA3","EMMC_BMC_CD_N","EMMC_BMC_WP_N", ++ /*E0-E3*/ "EMMC_BMC_R_DATA4","EMMC_BMC_R_DATA5","EMMC_BMC_R_DATA6","EMMC_BMC_R_DATA7"; ++}; ++ ++&sgpio { ++ status = "okay"; ++ gpio-line-names = ++ /* SGPIO output lines */ ++ /*OA0-OA7*/ "","","","","","","","", ++ /*OB0-OB7*/ "LED_CPU1_CH1_DIMM1_FAULT","LED_CPU1_CH1_DIMM2_FAULT","LED_CPU1_CH2_DIMM1_FAULT","LED_CPU1_CH2_DIMM2_FAULT","LED_CPU1_CH3_DIMM1_FAULT","LED_CPU1_CH3_DIMM2_FAULT","LED_CPU1_CH4_DIMM1_FAULT","LED_CPU1_CH4_DIMM2_FAULT", ++ /*OC0-OC7*/ "LED_CPU1_CH5_DIMM1_FAULT","LED_CPU1_CH5_DIMM2_FAULT","LED_CPU1_CH6_DIMM1_FAULT","LED_CPU1_CH6_DIMM2_FAULT","LED_FAN1_FAULT","LED_FAN2_FAULT","LED_FAN3_FAULT","LED_FAN4_FAULT", ++ /*OD0-OD7*/ "LED_FAN5_FAULT","LED_FAN6_FAULT","LED_FAN7_FAULT","LED_FAN8_FAULT","LED_CPU2_CH1_DIMM1_FAULT","LED_CPU1_CH1_DIMM2_FAULT","LED_CPU2_CH2_DIMM1_FAULT","LED_CPU2_CH2_DIMM2_FAULT", ++ /*OE0-OE7*/ "LED_CPU2_CH3_DIMM1_FAULT","LED_CPU2_CH3_DIMM2_FAULT","LED_CPU2_CH4_DIMM1_FAULT","LED_CPU2_CH4_DIMM2_FAULT","LED_CPU2_CH5_DIMM1_FAULT","LED_CPU2_CH5_DIMM2_FAULT","LED_CPU2_CH6_DIMM1_FAULT","LED_CPU2_CH6_DIMM2_FAULT", ++ /*OF0-OF7*/ "LED_CPU3_CH1_DIMM1_FAULT","LED_CPU3_CH1_DIMM2_FAULT","LED_CPU3_CH2_DIMM1_FAULT","LED_CPU3_CH2_DIMM2_FAULT","LED_CPU3_CH3_DIMM1_FAULT","LED_CPU3_CH3_DIMM2_FAULT","LED_CPU3_CH4_DIMM1_FAULT","LED_CPU3_CH4_DIMM2_FAULT", ++ /*OG0-OG7*/ "LED_CPU3_CH5_DIMM1_FAULT","LED_CPU3_CH5_DIMM2_FAULT","LED_CPU3_CH6_DIMM1_FAULT","LED_CPU3_CH6_DIMM2_FAULT","LED_CPU4_CH1_DIMM1_FAULT","LED_CPU4_CH1_DIMM2_FAULT","LED_CPU4_CH2_DIMM1_FAULT","LED_CPU4_CH2_DIMM2_FAULT", ++ /*OH0-OH7*/ "LED_CPU4_CH3_DIMM1_FAULT","LED_CPU4_CH3_DIMM2_FAULT","LED_CPU4_CH4_DIMM1_FAULT","LED_CPU4_CH4_DIMM2_FAULT","LED_CPU4_CH5_DIMM1_FAULT","LED_CPU4_CH5_DIMM2_FAULT","LED_CPU4_CH6_DIMM1_FAULT","LED_CPU4_CH6_DIMM2_FAULT", ++ /*OI0-OI7*/ "","","","","","","","", ++ /*OJ0-OJ7*/ "","","","","","","","", ++ /*DUMMY*/ "","","","","","","","", ++ /*DUMMY*/ "","","","","","","","", ++ ++ /* SGPIO input lines */ ++ /* Some lines have been renamed from the net names: ++ CPU1_PRESENCE -> FM_CPU0_SKTOCC_LVT3_N ++ CPU1_THERMTRIP -> H_CPU0_THERMTRIP_LVC1_N ++ CPU1_VRHOT -> IRQ_CPU0_VRHOT_N ++ CPU1_FIVR_FAULT -> H_CPU0_MON_FAIL_PLD_LVC1_N ++ CPU1_MEM_ABCD_VRHOT -> ?? ++ CPU1_MEM_EFGH_VRHOT -> ?? ++ CPU2_PRESENCE -> FM_CPU1_SKTOCC_LVT3_N ++ CPU2_THERMTRIP -> H_CPU1_THERMTRIP_LVC1_N ++ CPU2_VRHOT -> IRQ_CPU1_VRHOT_N ++ CPU2_FIVR_FAULT -> H_CPU1_MON_FAIL_PLD_LVC1_N ++ CPU2_MEM_ABCD_VRHOT -> ?? ++ CPU2_MEM_EFGH_VRHOT -> ?? ++ ++ /*IA0-IA7*/ "CPU1_PRESENCE","CPU1_THERMTRIP","CPU1_VRHOT","CPU1_FIVR_FAULT","IRQ_CPU0_MEM_VRHOT_N","H_CPU0_MEMHOT_OUT_LVC1_N","FM_CPU0_PROC_ID0","FM_CPU0_PROC_ID1", ++ /*IB0-IB7*/ "WCPU_MISMATCH","IRQ_PSYS_CRIT_N","CPU2_PRESENCE","CPU2_THERMTRIP","CPU2_VRHOT","CPU2_FIVR_FAULT","IRQ_CPU1_MEM_VRHOT_N","H_CPU1_MEMHOT_OUT_LVC1_N", ++ /*IC0-IC7*/ "FM_CPU1_PROC_ID0","FM_CPU1_PROC_ID1","","","","","","", ++ /*ID0-ID7*/ "","","","","","","","", ++ /*IE0-IE7*/ "","","","","","","","", ++ /*IF0-IF7*/ "FPGA_REV_TEST_0","FPGA_REV_TEST_1","FPGA_REV_TEST_2","FPGA_REV_TEST_3","FPGA_REV_TEST_4","FPGA_REV_TEST_5","FPGA_REV_TEST_6","FPGA_REV_TEST_7", ++ /*IG0-IG7*/ "FPGA_REV_0","FPGA_REV_1","FPGA_REV_2","FPGA_REV_3","FPGA_REV_4","FPGA_REV_5","FPGA_REV_6","FPGA_REV_7", ++ /*IH0-IH7*/ "","WMEMX_PWR_FLT","WCPUX_MEM_PWR_FLT","PWRGD_P3V3_FF","WPSU_PWR_FLT","","","WPCH_PWR_FLT", ++ /*II0-II7*/ "FM_CPU0_PKGID0","FM_CPU0_PKGID1","FM_CPU0_PKGID2","H_CPU0_MEMTRIP_LVC1_N","FM_CPU1_PKGID0","FM_CPU1_PKGID1","FM_CPU1_PKGID2","H_CPU1_MEMTRIP_LVC1_N", ++ /*IJ0-IJ7*/ "","","","","","","",""; ++}; ++ ++&kcs3 { ++ kcs_addr = <0xCA2>; ++ status = "okay"; ++}; ++ ++&kcs4 { ++ kcs_addr = <0xCA4>; ++ status = "okay"; ++}; ++ ++&lpc_sio { ++ status = "okay"; ++}; ++ ++&lpc_snoop { ++ snoop-ports = <0x80>; ++ status = "okay"; ++}; ++ ++&mbox { ++ status = "okay"; ++}; ++ ++&mdio1 { ++ status = "okay"; ++ ++ ethphy1: ethernet-phy@0 { ++ compatible = "ethernet-phy-ieee802.3-c22"; ++ reg = <0>; ++ }; ++}; ++ ++&mac1 { ++ status = "okay"; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_rgmii2_default>; ++ clocks = <&syscon ASPEED_CLK_GATE_MAC2CLK>, ++ <&syscon ASPEED_CLK_MAC2RCLK>; ++ clock-names = "MACCLK", "RCLK"; ++ phy-mode = "rgmii"; ++ phy-handle = <ðphy1>; ++}; ++ ++&mdio2 { ++ status = "okay"; ++ ++ ethphy2: ethernet-phy@1 { ++ compatible = "ethernet-phy-ieee802.3-c22"; ++ reg = <0>; ++ }; ++}; ++ ++&adc0 { ++ status = "okay"; ++}; ++ ++&adc1 { ++ status = "okay"; ++}; ++ ++&uart1 { ++ status = "okay"; ++ pinctrl-0 = <&pinctrl_txd1_default ++ &pinctrl_rxd1_default ++ &pinctrl_nrts1_default ++ &pinctrl_ndtr1_default ++ &pinctrl_ndsr1_default ++ &pinctrl_ncts1_default ++ &pinctrl_ndcd1_default ++ &pinctrl_nri1_default>; ++}; ++ ++&uart2 { ++ status = "okay"; ++ pinctrl-0 = <&pinctrl_txd2_default ++ &pinctrl_rxd2_default ++ &pinctrl_nrts2_default ++ &pinctrl_ndtr2_default ++ &pinctrl_ndsr2_default ++ &pinctrl_ncts2_default ++ &pinctrl_ndcd2_default ++ &pinctrl_nri2_default>; ++}; ++ ++&uart3 { ++ status = "okay"; ++ pinctrl-0 = <>; ++}; ++ ++&uart4 { ++ status = "okay"; ++ pinctrl-0 = <>; ++}; ++ ++&uart5 { ++ status = "okay"; ++ // Workaround for A0 ++ compatible = "snps,dw-apb-uart"; ++}; ++ ++&uart_routing { ++ status = "okay"; ++}; ++ ++&emmc_controller{ ++ status = "okay"; ++}; ++ ++&emmc { ++ non-removable; ++ bus-width = <4>; ++ max-frequency = <52000000>; ++}; ++ ++&i2c0 { ++ /* SMB_CHASSENSOR_STBY_LVC3 */ ++ multi-master; ++ status = "okay"; ++}; ++ ++&i2c4 { ++ /* SMB_HSBP_STBY_LVC3_R */ ++ multi-master; ++ status = "okay"; ++}; ++ ++&i2c5 { ++ /* SMB_SMLINK0_STBY_LVC3_R2 */ ++ bus-frequency = <1000000>; ++ multi-master; ++ status = "okay"; ++}; ++ ++&i2c6 { ++ /* SMB_TEMPSENSOR_STBY_LVC3_R */ ++ multi-master; ++ status = "okay"; ++}; ++ ++&i2c7 { ++ /* SMB_PMBUS_SML1_STBY_LVC3_R */ ++ multi-master; ++ #retries = <3>; ++ status = "okay"; ++}; ++ ++&i2c8 { ++ /* SMB_PCIE_STBY_LVC3_R */ ++ multi-master; ++ status = "okay"; ++}; ++ ++&i2c9 { ++ /* SMB_HOST_STBY_BMC_LVC3_R */ ++ multi-master; ++ status = "okay"; ++}; ++ ++&i2c12 { ++ /* SMB_CPU_PIROM */ ++ multi-master; ++ status = "okay"; ++}; ++ ++&i2c13 { ++ /* SMB_IPMB_STBY_LVC3_R */ ++ multi-master; ++ status = "okay"; ++}; ++ ++&i3c0 { ++ /* I3C_SPD_DDRABCD_CPU0_BMC ; FIXME: i3c driver hangs kernel on probe...*/ ++ status = "disabled"; ++}; ++ ++&i3c1 { ++ /* I3C_SPD_DDREFGH_CPU0_BMC */ ++ status = "disabled"; ++}; ++ ++&i3c2 { ++ /* I3C_SPD_DDRABCD_CPU1_BMC */ ++ status = "disabled"; ++}; ++ ++&i3c3 { ++ /* I3C_SPD_DDREFGH_CPU1_BMC */ ++ status = "disabled"; ++}; ++ ++&gfx { ++ status = "okay"; ++ memory-region = <&gfx_memory>; ++}; ++ ++&pwm_tacho { ++ status = "disabled"; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_pwm0_default &pinctrl_tach0_default ++ &pinctrl_pwm1_default &pinctrl_tach1_default ++ &pinctrl_pwm2_default &pinctrl_tach2_default ++ &pinctrl_pwm3_default &pinctrl_tach3_default ++ &pinctrl_pwm4_default &pinctrl_tach4_default ++ &pinctrl_pwm5_default &pinctrl_tach5_default ++ &pinctrl_pwm12g1_default &pinctrl_tach6_default ++ &pinctrl_pwm13g1_default &pinctrl_tach7_default ++ &pinctrl_pwm14g1_default &pinctrl_tach8_default ++ &pinctrl_pwm15g1_default &pinctrl_tach9_default>; /*pwm 6-11 used for some other functionality*/ ++ ++ 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>; ++ }; ++ fan@8 { ++ reg = <0x08>; ++ aspeed,fan-tach-ch = /bits/ 8 <0x08>; ++ }; ++ fan@9 { ++ reg = <0x09>; ++ aspeed,fan-tach-ch = /bits/ 8 <0x09>; ++ }; ++}; ++ ++&video { ++ status = "okay"; ++ memory-region = <&video_engine_memory>; ++}; ++ ++&vhub { ++ status = "okay"; ++}; ++ ++&jtag { ++ status = "okay"; ++}; +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0001-arm-dts-base-aspeed-g6-dtsi-fixups.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0001-arm-dts-base-aspeed-g6-dtsi-fixups.patch new file mode 100644 index 000000000..d75229a15 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0001-arm-dts-base-aspeed-g6-dtsi-fixups.patch @@ -0,0 +1,212 @@ +From 5e5758fe5929766a9b4677b86f9343a777526fe8 Mon Sep 17 00:00:00 2001 +From: Vernon Mauery <vernon.mauery@linux.intel.com> +Date: Thu, 12 Sep 2019 15:55:39 +0800 +Subject: [PATCH] arm: dts: base aspeed g6 dtsi fixups + +Additions to the base g6 dtsi file for Aspeed ast2600 systems. +This mostly includes entries for the drivers that are not upstream. + +Signed-off-by: Vernon Mauery <vernon.mauery@linux.intel.com> +--- + arch/arm/boot/dts/aspeed-g6.dtsi | 129 +++++++++++++++++++++++++++++- + include/dt-bindings/clock/ast2600-clock.h | 8 ++ + 2 files changed, 135 insertions(+), 2 deletions(-) + +diff --git a/arch/arm/boot/dts/aspeed-g6.dtsi b/arch/arm/boot/dts/aspeed-g6.dtsi +index 2af9efa1faa1..54add29d8217 100644 +--- a/arch/arm/boot/dts/aspeed-g6.dtsi ++++ b/arch/arm/boot/dts/aspeed-g6.dtsi +@@ -28,6 +28,12 @@ + i2c13 = &i2c13; + i2c14 = &i2c14; + i2c15 = &i2c15; ++ i3c0 = &i3c0; ++ i3c1 = &i3c1; ++ i3c2 = &i3c2; ++ i3c3 = &i3c3; ++ i3c4 = &i3c4; ++ i3c5 = &i3c5; + serial0 = &uart1; + serial1 = &uart2; + serial2 = &uart3; +@@ -297,11 +303,21 @@ + quality = <100>; + }; + ++ adc: adc@1e6e9000 { ++ compatible = "aspeed,ast2500-adc"; ++ reg = <0x1e6e9000 0x100>; ++ clocks = <&syscon ASPEED_CLK_APB2>; ++ interrupts = <GIC_SPI 46 IRQ_TYPE_LEVEL_HIGH>; ++ resets = <&syscon ASPEED_RESET_ADC>; ++ #io-channel-cells = <1>; ++ status = "disabled"; ++ }; ++ + gpio0: gpio@1e780000 { + #gpio-cells = <2>; + gpio-controller; + compatible = "aspeed,ast2600-gpio"; +- reg = <0x1e780000 0x800>; ++ reg = <0x1e780000 0x200>; + interrupts = <GIC_SPI 40 IRQ_TYPE_LEVEL_HIGH>; + gpio-ranges = <&pinctrl 0 0 208>; + ngpios = <208>; +@@ -314,7 +330,7 @@ + #gpio-cells = <2>; + gpio-controller; + compatible = "aspeed,ast2600-gpio"; +- reg = <0x1e780800 0x800>; ++ reg = <0x1e780800 0x200>; + interrupts = <GIC_SPI 11 IRQ_TYPE_LEVEL_HIGH>; + gpio-ranges = <&pinctrl 0 208 36>; + ngpios = <36>; +@@ -398,6 +414,13 @@ + ranges = <0x0 0x1e78b000 0x100>; + }; + ++ i3c: bus@1e7a0000 { ++ compatible = "simple-bus"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ranges = <0 0x1e7a0000 0x8000>; ++ }; ++ + lpc: lpc@1e789000 { + compatible = "aspeed,ast2600-lpc", "simple-mfd"; + reg = <0x1e789000 0x1000>; +@@ -486,6 +509,20 @@ + sio_regs: regs { + compatible = "aspeed,bmc-misc"; + }; ++ ++ lpc_sio: lpc-sio@100 { ++ compatible = "aspeed,ast2500-lpc-sio"; ++ reg = <0x100 0x20>; ++ clocks = <&syscon ASPEED_CLK_GATE_LCLK>; ++ status = "disabled"; ++ }; ++ ++ mbox: mbox@180 { ++ compatible = "aspeed,ast2600-mbox"; ++ reg = <0x180 0x5c>; ++ interrupts = <GIC_SPI 54 IRQ_TYPE_LEVEL_HIGH>; ++ #mbox-cells = <1>; ++ }; + }; + }; + +@@ -871,3 +908,91 @@ + status = "disabled"; + }; + }; ++ ++&i3c { ++ i3c0: i3c0@2000 { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ #interrupt-cells = <1>; ++ reg = <0x2000 0x1000>; ++ compatible = "snps,dw-i3c-master-1.00a"; ++ clocks = <&syscon ASPEED_CLK_APB2>; ++ resets = <&syscon ASPEED_RESET_I3C0>; ++ bus-frequency = <100000>; ++ interrupts = <GIC_SPI 102 IRQ_TYPE_LEVEL_HIGH>; ++ status = "disabled"; ++ }; ++ ++ i3c1: i3c1@3000 { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ #interrupt-cells = <1>; ++ reg = <0x3000 0x1000>; ++ compatible = "snps,dw-i3c-master-1.00a"; ++ clocks = <&syscon ASPEED_CLK_APB2>; ++ resets = <&syscon ASPEED_RESET_I3C1>; ++ bus-frequency = <100000>; ++ interrupts = <GIC_SPI 103 IRQ_TYPE_LEVEL_HIGH>; ++ status = "disabled"; ++ }; ++ ++ i3c2: i3c2@4000 { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ #interrupt-cells = <1>; ++ reg = <0x4000 0x1000>; ++ compatible = "snps,dw-i3c-master-1.00a"; ++ clocks = <&syscon ASPEED_CLK_APB2>; ++ resets = <&syscon ASPEED_RESET_I3C2>; ++ bus-frequency = <100000>; ++ interrupts = <GIC_SPI 104 IRQ_TYPE_LEVEL_HIGH>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_i3c3_default>; ++ status = "disabled"; ++ }; ++ ++ i3c3: i3c3@5000 { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ #interrupt-cells = <1>; ++ reg = <0x5000 0x1000>; ++ compatible = "snps,dw-i3c-master-1.00a"; ++ clocks = <&syscon ASPEED_CLK_APB2>; ++ resets = <&syscon ASPEED_RESET_I3C3>; ++ bus-frequency = <100000>; ++ interrupts = <GIC_SPI 105 IRQ_TYPE_LEVEL_HIGH>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_i3c4_default>; ++ status = "disabled"; ++ }; ++ ++ i3c4: i3c4@6000 { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ #interrupt-cells = <1>; ++ reg = <0x6000 0x1000>; ++ compatible = "snps,dw-i3c-master-1.00a"; ++ clocks = <&syscon ASPEED_CLK_APB2>; ++ resets = <&syscon ASPEED_RESET_I3C4>; ++ bus-frequency = <100000>; ++ interrupts = <GIC_SPI 106 IRQ_TYPE_LEVEL_HIGH>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_i3c5_default>; ++ status = "disabled"; ++ }; ++ ++ i3c5: i3c5@7000 { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ #interrupt-cells = <1>; ++ reg = <0x7000 0x1000>; ++ compatible = "snps,dw-i3c-master-1.00a"; ++ clocks = <&syscon ASPEED_CLK_APB2>; ++ resets = <&syscon ASPEED_RESET_I3C5>; ++ bus-frequency = <100000>; ++ interrupts = <GIC_SPI 107 IRQ_TYPE_LEVEL_HIGH>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_i3c6_default>; ++ status = "disabled"; ++ }; ++}; +diff --git a/include/dt-bindings/clock/ast2600-clock.h b/include/dt-bindings/clock/ast2600-clock.h +index 62b9520a00fd..3d90582a813f 100644 +--- a/include/dt-bindings/clock/ast2600-clock.h ++++ b/include/dt-bindings/clock/ast2600-clock.h +@@ -91,6 +91,14 @@ + /* Only list resets here that are not part of a gate */ + #define ASPEED_RESET_ADC 55 + #define ASPEED_RESET_JTAG_MASTER2 54 ++#define ASPEED_RESET_I3C7 47 ++#define ASPEED_RESET_I3C6 46 ++#define ASPEED_RESET_I3C5 45 ++#define ASPEED_RESET_I3C4 44 ++#define ASPEED_RESET_I3C3 43 ++#define ASPEED_RESET_I3C2 42 ++#define ASPEED_RESET_I3C1 41 ++#define ASPEED_RESET_I3C0 40 + #define ASPEED_RESET_I3C_DMA 39 + #define ASPEED_RESET_PWM 37 + #define ASPEED_RESET_PECI 36 +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0002-Add-Aspeed-fmc-spi-driver.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0002-Add-Aspeed-fmc-spi-driver.patch new file mode 100644 index 000000000..08e350c15 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0002-Add-Aspeed-fmc-spi-driver.patch @@ -0,0 +1,645 @@ +From 1365492683b47f63d470d0666fee258a5c7ca3c3 Mon Sep 17 00:00:00 2001 +From: Vernon Mauery <vernon.mauery@intel.com> +Date: Thu, 12 Sep 2019 15:26:08 -0700 +Subject: [PATCH 04/52] Add Aspeed fmc-spi driver + +Add the Aspeed fmc-spi driver from the Apeed SDK v5.02 + +Signed-off-by: Vernon Mauery <vernon.mauery@linux.intel.com> +--- + arch/arm/boot/dts/aspeed-g6.dtsi | 43 ++- + drivers/spi/Kconfig | 6 + + drivers/spi/Makefile | 1 + + drivers/spi/fmc_spi.c | 530 +++++++++++++++++++++++++++++++ + 4 files changed, 579 insertions(+), 1 deletion(-) + create mode 100644 drivers/spi/fmc_spi.c + +diff --git a/arch/arm/boot/dts/aspeed-g6.dtsi b/arch/arm/boot/dts/aspeed-g6.dtsi +index 42f644ac8111..1aab48fbf49e 100644 +--- a/arch/arm/boot/dts/aspeed-g6.dtsi ++++ b/arch/arm/boot/dts/aspeed-g6.dtsi +@@ -90,7 +90,7 @@ + <0x40464000 0x2000>, + <0x40466000 0x2000>; + }; +- ++#if 1 + fmc: spi@1e620000 { + reg = < 0x1e620000 0xc4 + 0x20000000 0x10000000 >; +@@ -169,6 +169,47 @@ + status = "disabled"; + }; + }; ++#else ++ spi0: spi@1e620000 { ++ /* reg : cs0 : cs1 : cs2 */ ++ reg = <0x1e620000 0x100 ++ 0x20000000 0x40 ++ 0x28000000 0x40>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ compatible = "aspeed,fmc-spi"; ++ clocks = <&syscon ASPEED_CLK_AHB>; ++ status = "disable"; ++ number_of_chip_select = /bits/ 16 <2>; ++ interrupts = <GIC_SPI 39 IRQ_TYPE_LEVEL_HIGH>; ++ }; ++ ++ spi1: spi1@1e630000 { ++ /* reg : cs0 : cs1 */ ++ reg = <0x1e630000 0x100 ++ 0x30000000 0x20 ++ 0x32000000 0x20>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ compatible = "aspeed,fmc-spi"; ++ clocks = <&syscon ASPEED_CLK_AHB>; ++ status = "disable"; ++ number_of_chip_select = /bits/ 16 <2>; ++ }; ++ ++ spi2: spi2@1e631000 { ++ /* reg : cs0 : cs1 */ ++ reg = <0x1e631000 0x100 ++ 0x38000000 0x20 ++ 0x3A000000 0x20>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ compatible = "aspeed,fmc-spi"; ++ clocks = <&syscon ASPEED_CLK_AHB>; ++ status = "disable"; ++ number_of_chip_select = /bits/ 16 <2>; ++ }; ++#endif + + mdio0: mdio@1e650000 { + compatible = "aspeed,ast2600-mdio"; +diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig +index 6ee514fd0920..9f32c31ffa3c 100644 +--- a/drivers/spi/Kconfig ++++ b/drivers/spi/Kconfig +@@ -57,6 +57,12 @@ config SPI_MEM + + comment "SPI Master Controller Drivers" + ++config SPI_FMC ++ tristate "Aspeed FMC SPI Controller" ++ depends on ARCH_ASPEED ++ help ++ This selects a driver for the AST FMC SPI Controller ++ + config SPI_ALTERA + tristate "Altera SPI Controller" + help +diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile +index adbebee93a75..224b9b71e29c 100644 +--- a/drivers/spi/Makefile ++++ b/drivers/spi/Makefile +@@ -13,6 +13,7 @@ obj-$(CONFIG_SPI_SPIDEV) += spidev.o + obj-$(CONFIG_SPI_LOOPBACK_TEST) += spi-loopback-test.o + + # SPI master controller drivers (bus) ++obj-$(CONFIG_SPI_FMC) += fmc_spi.o + obj-$(CONFIG_SPI_ALTERA) += spi-altera.o + obj-$(CONFIG_SPI_ARMADA_3700) += spi-armada-3700.o + obj-$(CONFIG_SPI_ATMEL) += spi-atmel.o +diff --git a/drivers/spi/fmc_spi.c b/drivers/spi/fmc_spi.c +new file mode 100644 +index 000000000000..f21f7a00496e +--- /dev/null ++++ b/drivers/spi/fmc_spi.c +@@ -0,0 +1,530 @@ ++/* ++ * fmc_spi.c - FMC SPI driver for the Aspeed SoC ++ * ++ * Copyright (C) ASPEED Technology Inc. ++ * Ryan Chen <ryan_chen@aspeedtech.com> ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version ++ * 2 of the License, or (at your option) any later version. ++ * ++ */ ++#include <linux/module.h> ++#include <linux/init.h> ++#include <linux/clk.h> ++#include <linux/sched.h> ++#include <linux/kernel.h> ++#include <linux/delay.h> ++#include <linux/interrupt.h> ++#include <linux/ioport.h> ++#include <linux/platform_device.h> ++#include <linux/err.h> ++#include <linux/errno.h> ++#include <linux/wait.h> ++#include <linux/delay.h> ++#include <linux/spi/spi.h> ++#include <linux/spi/flash.h> ++ ++#include <linux/of_address.h> ++#include <linux/of_irq.h> ++#include <linux/of_platform.h> ++ ++/******************************************************************************/ ++/* AST_SPI_CONFIG 0x00 : SPI00 CE Type Setting Register */ ++#define AST_G5_SPI_CONF_CE1_WEN (0x1 << 17) ++#define AST_G5_SPI_CONF_CE0_WEN (0x1 << 16) ++ ++#define SPI_CONF_CE0_WEN (0x1) ++ ++/* Register offsets */ ++#define FMC_SPI_CONFIG 0x00 ++#define FMC_SPI_CTRL 0x04 ++#define FMC_SPI_DMA_STS 0x08 ++ ++#define FMC_SPI_CE0_CTRL 0x10 ++#define FMC_SPI_CE1_CTRL 0x14 ++ ++#define AST_SPI_DMA_CTRL 0x80 ++#define AST_SPI_DMA_FLASH_BASE 0x84 ++#define AST_SPI_DMA_DRAM_BASE 0x88 ++#define AST_SPI_DMA_LENGTH 0x8c ++ ++/* AST_FMC_CONFIG 0x00 : FMC00 CE Type Setting Register */ ++#define FMC_CONF_LAGACY_DIS (0x1 << 31) ++#define FMC_CONF_CE1_WEN (0x1 << 17) ++#define FMC_CONF_CE0_WEN (0x1 << 16) ++#define FMC_CONF_CE1_SPI (0x2 << 2) ++#define FMC_CONF_CE0_SPI (0x2) ++ ++/* FMC_SPI_CTRL : 0x04 : FMC04 CE Control Register */ ++#define FMC_CTRL_CE1_4BYTE_MODE (0x1 << 1) ++#define FMC_CTRL_CE0_4BYTE_MODE (0x1) ++ ++/* FMC_SPI_DMA_STS : 0x08 : FMC08 Interrupt Control and Status Register */ ++#define FMC_STS_DMA_READY 0x0800 ++#define FMC_STS_DMA_CLEAR 0x0800 ++ ++/* FMC_CE0_CTRL for SPI 0x10, 0x14, 0x18, 0x1c, 0x20 */ ++#define SPI_IO_MODE_MASK (3 << 28) ++#define SPI_SINGLE_BIT (0 << 28) ++#define SPI_DUAL_MODE (0x2 << 28) ++#define SPI_DUAL_IO_MODE (0x3 << 28) ++#define SPI_QUAD_MODE (0x4 << 28) ++#define SPI_QUAD_IO_MODE (0x5 << 28) ++ ++#define SPI_CE_WIDTH(x) (x << 24) ++#define SPI_CMD_DATA_MASK (0xff << 16) ++#define SPI_CMD_DATA(x) (x << 16) ++#define SPI_DUMMY_CMD (1 << 15) ++#define SPI_DUMMY_HIGH (1 << 14) ++//#define SPI_CLK_DIV (1 << 13) ?? TODO ask.... ++//#define SPI_ADDR_CYCLE (1 << 13) ?? TODO ask.... ++#define SPI_CMD_MERGE_DIS (1 << 12) ++#define SPI_CLK_DIV(x) (x << 8) ++#define SPI_CLK_DIV_MASK (0xf << 8) ++ ++#define SPI_DUMMY_LOW_MASK (0x3 << 6) ++#define SPI_DUMMY_LOW(x) ((x) << 6) ++#define SPI_LSB_FIRST_CTRL (1 << 5) ++#define SPI_CPOL_1 (1 << 4) ++#define SPI_DUAL_DATA (1 << 3) ++#define SPI_CE_INACTIVE (1 << 2) ++#define SPI_CMD_MODE_MASK (0x3) ++#define SPI_CMD_NORMAL_READ_MODE 0 ++#define SPI_CMD_READ_CMD_MODE 1 ++#define SPI_CMD_WRITE_CMD_MODE 2 ++#define SPI_CMD_USER_MODE 3 ++ ++/* AST_SPI_DMA_CTRL 0x80 */ ++#define FMC_DMA_ENABLE (0x1) ++ ++/******************************************************************************/ ++struct fmc_spi_host { ++ void __iomem *base; ++ void __iomem *ctrl_reg; ++ u32 buff[5]; ++ struct spi_master *master; ++ struct spi_device *spi_dev; ++ struct device *dev; ++ u32 ahb_clk; ++ spinlock_t lock; ++}; ++ ++static u32 ast_spi_calculate_divisor(struct fmc_spi_host *host, ++ u32 max_speed_hz) ++{ ++ // [0] ->15 : HCLK , HCLK/16 ++ u8 SPI_DIV[16] = { ++ 16, 7, 14, 6, 13, 5, 12, 4, 11, 3, 10, 2, 9, 1, 8, 0 ++ }; ++ u32 i, spi_cdvr = 0; ++ ++ for (i = 1; i < 17; i++) { ++ if (max_speed_hz >= (host->ahb_clk / i)) { ++ spi_cdvr = SPI_DIV[i - 1]; ++ break; ++ } ++ } ++ ++ // printk("hclk is %d, divisor is %d, target :%d , cal speed %d\n", host->ahb_clk, spi_cdvr, spi->max_speed_hz, hclk/i); ++ return spi_cdvr; ++} ++ ++/* the spi->mode bits understood by this driver: */ ++#define MODEBITS (SPI_CPOL | SPI_CPHA | SPI_CS_HIGH) ++ ++static int fmc_spi_setup(struct spi_device *spi) ++{ ++ struct fmc_spi_host *host = ++ (struct fmc_spi_host *)spi_master_get_devdata(spi->master); ++ unsigned int bits = spi->bits_per_word; ++ u32 fmc_config = 0; ++ u32 spi_ctrl = 0; ++ u32 divisor; ++ ++ // dev_dbg(host->dev, "fmc_spi_setup() cs: %d, spi->mode %d \n", spi->chip_select, spi->mode); ++ // printk("fmc_spi_setup() cs: %d, spi->mode %d spi->max_speed_hz %d , spi->bits_per_word %d \n", spi->chip_select, spi->mode, spi->max_speed_hz, spi->bits_per_word); ++ ++ switch (spi->chip_select) { ++ case 0: ++ fmc_config |= FMC_CONF_CE0_WEN | FMC_CONF_CE0_SPI; ++ host->ctrl_reg = host->base + FMC_SPI_CE0_CTRL; ++ break; ++ case 1: ++ fmc_config |= FMC_CONF_CE1_WEN | FMC_CONF_CE1_SPI; ++ host->ctrl_reg = host->base + FMC_SPI_CE0_CTRL; ++ break; ++ default: ++ dev_dbg(&spi->dev, ++ "setup: invalid chipselect %u (%u defined)\n", ++ spi->chip_select, spi->master->num_chipselect); ++ return -EINVAL; ++ break; ++ } ++ writel(fmc_config, host->base); ++ ++ if (bits == 0) ++ bits = 8; ++ ++ if (bits < 8 || bits > 16) { ++ dev_dbg(&spi->dev, ++ "setup: invalid bits_per_word %u (8 to 16)\n", bits); ++ return -EINVAL; ++ } ++ ++ if (spi->mode & ~MODEBITS) { ++ dev_dbg(&spi->dev, "setup: unsupported mode bits %x\n", ++ spi->mode & ~MODEBITS); ++ return -EINVAL; ++ } ++ ++ /* see notes above re chipselect */ ++ if ((spi->chip_select == 0) && (spi->mode & SPI_CS_HIGH)) { ++ dev_dbg(&spi->dev, "setup: can't be active-high\n"); ++ return -EINVAL; ++ } ++ ++ /* ++ * Pre-new_1 chips start out at half the peripheral ++ * bus speed. ++ */ ++ ++ if (spi->max_speed_hz) { ++ /* Set the SPI slaves select and characteristic control register */ ++ divisor = ast_spi_calculate_divisor(host, spi->max_speed_hz); ++ } else { ++ /* speed zero means "as slow as possible" */ ++ divisor = 15; ++ } ++ ++ spi_ctrl &= ~SPI_CLK_DIV_MASK; ++ // printk("set div %x \n",divisor); ++ //TODO MASK first ++ spi_ctrl |= SPI_CLK_DIV(divisor); ++ ++ /* only support mode 0 (CPOL=0, CPHA=0) and cannot support mode 1 ~ mode 3 */ ++ ++#if 0 ++ if (SPI_CPHA & spi->mode) ++ cpha = SPI_CPHA_1; ++ else ++ cpha = SPI_CPHA_0; ++#endif ++ ++ // if (SPI_CPOL & spi->mode) ++ // spi_ctrl |= SPI_CPOL_1; ++ // else ++ // spi_ctrl &= ~SPI_CPOL_1; ++ ++ //ISSUE : ast spi ctrl couldn't use mode 3, so fix mode 0 ++ spi_ctrl &= ~SPI_CPOL_1; ++ ++ if (SPI_LSB_FIRST & spi->mode) ++ spi_ctrl |= SPI_LSB_FIRST_CTRL; ++ else ++ spi_ctrl &= ~SPI_LSB_FIRST_CTRL; ++ ++ /* Configure SPI controller */ ++ writel(spi_ctrl, host->ctrl_reg); ++ ++ // printk("ctrl %x, ", spi_ctrl); ++ return 0; ++} ++ ++static int fmc_spi_transfer(struct spi_device *spi, struct spi_message *msg) ++{ ++ struct fmc_spi_host *host = ++ (struct fmc_spi_host *)spi_master_get_devdata(spi->master); ++ struct spi_transfer *xfer; ++ const u8 *tx_buf; ++ u8 *rx_buf; ++ unsigned long flags; ++ ++ int i = 0, j = 0; ++ ++ // dev_dbg(host->dev, "xfer %s \n", dev_name(&spi->dev)); ++ // printk("xfer spi->chip_select %d \n", spi->chip_select); ++ ++ host->spi_dev = spi; ++ spin_lock_irqsave(&host->lock, flags); ++ ++ writel(readl(host->ctrl_reg) | SPI_CMD_USER_MODE, host->ctrl_reg); ++ msg->actual_length = 0; ++ msg->status = 0; ++ ++ list_for_each_entry (xfer, &msg->transfers, transfer_list) { ++ dev_dbg(host->dev, ++ "xfer[%d] %p: width %d, len %u, tx %p/%08x, rx %p/%08x\n", ++ j, xfer, xfer->bits_per_word, xfer->len, xfer->tx_buf, ++ xfer->tx_dma, xfer->rx_buf, xfer->rx_dma); ++ ++ tx_buf = xfer->tx_buf; ++ rx_buf = xfer->rx_buf; ++ ++ if (tx_buf != 0) { ++#if 0 ++ printk("tx : "); ++ if(xfer->len > 10) { ++ for(i=0;i<10;i++) ++ printk("%x ",tx_buf[i]); ++ } else { ++ for(i=0;i<xfer->len;i++) ++ printk("%x ",tx_buf[i]); ++ } ++ printk("\n"); ++#endif ++ for (i = 0; i < xfer->len; i++) { ++ writeb(tx_buf[i], ++ (void *)host->buff ++ [host->spi_dev->chip_select]); ++ } ++ } ++ //Issue need clarify ++ udelay(1); ++ if (rx_buf != 0) { ++ for (i = 0; i < xfer->len; i++) { ++ rx_buf[i] = readb( ++ (void *)host->buff ++ [host->spi_dev->chip_select]); ++ } ++#if 0 ++ printk("rx : "); ++ if(xfer->len > 10) { ++ for(i=0;i<10;i++) ++ printk(" %x",rx_buf[i]); ++ } else { ++ for(i=0;i<xfer->len;i++) ++ printk(" %x",rx_buf[i]); ++ } ++ printk("\n"); ++#endif ++ } ++ dev_dbg(host->dev, "old msg->actual_length %d , +len %d \n", ++ msg->actual_length, xfer->len); ++ msg->actual_length += xfer->len; ++ dev_dbg(host->dev, "new msg->actual_length %d \n", ++ msg->actual_length); ++ // j++; ++ } ++ ++ // writel( SPI_CE_INACTIVE | readl(host->spi_data->ctrl_reg),host->spi_data->ctrl_reg); ++ writel(readl(host->ctrl_reg) & ~SPI_CMD_USER_MODE, host->ctrl_reg); ++ msg->status = 0; ++ ++ msg->complete(msg->context); ++ ++ // spin_unlock(&host->lock); ++ spin_unlock_irqrestore(&host->lock, flags); ++ ++ return 0; ++} ++ ++static void fmc_spi_cleanup(struct spi_device *spi) ++{ ++ struct fmc_spi_host *host = spi_master_get_devdata(spi->master); ++ unsigned long flags; ++ dev_dbg(host->dev, "fmc_spi_cleanup() \n"); ++ ++ spin_lock_irqsave(&host->lock, flags); ++ // if (host->stay == spi) { ++ // host->stay = NULL; ++ // cs_deactivate(host, spi); ++ // } ++ spin_unlock_irqrestore(&host->lock, flags); ++} ++ ++#if 0 ++static int fmc_spi_flash_read(struct spi_device *spi, ++ struct spi_flash_read_message *msg) ++{ ++// struct fmc_spi_host *host = spi_master_get_devdata(spi->master); ++ int ret = 0; ++ ++// printk("read msg->from %x, msg->len %x , msg->buf %x , msg->addr_width %d , msg->dummy_bytes %x , msg->read_opcode %x \n", msg->from, msg->len, msg->buf, msg->addr_width, msg->dummy_bytes, msg->read_opcode); ++ ++// memcpy_fromio(msg->buf, b53spi->mmio_base + msg->from, msg->len); ++ msg->retlen = msg->len; ++ ++ return ret; ++} ++#endif ++ ++static int fmc_spi_probe(struct platform_device *pdev) ++{ ++ struct resource *res; ++ struct fmc_spi_host *host; ++ struct spi_master *master; ++ struct clk *clk; ++ int cs_num = 0; ++ int err = 0; ++ ++ dev_dbg(&pdev->dev, "fmc_spi_probe() \n"); ++ ++ master = spi_alloc_master(&pdev->dev, sizeof(struct fmc_spi_host)); ++ if (NULL == master) { ++ dev_err(&pdev->dev, "No memory for spi_master\n"); ++ err = -ENOMEM; ++ goto err_nomem; ++ } ++ ++ /* the spi->mode bits understood by this driver: */ ++ master->mode_bits = ++ SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_RX_DUAL | SPI_TX_DUAL; ++ master->bits_per_word_mask = SPI_BPW_MASK(8); ++ ++ master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_RX_DUAL; ++ // master->bits_per_word_mask = SPI_BPW_RANGE_MASK(8, 16); ++ master->dev.of_node = pdev->dev.of_node; ++ master->bus_num = pdev->id; ++ // master->num_chipselect = master->dev.of_node ? 0 : 4; ++ platform_set_drvdata(pdev, master); ++ ++ host = spi_master_get_devdata(master); ++ memset(host, 0, sizeof(struct fmc_spi_host)); ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (!res) { ++ dev_err(&pdev->dev, "cannot get IORESOURCE_MEM 0\n"); ++ err = -ENXIO; ++ goto err_no_io_res; ++ } ++ ++ host->base = devm_ioremap_resource(&pdev->dev, res); ++ if (!host->base) { ++ dev_err(&pdev->dev, "cannot remap register\n"); ++ err = -EIO; ++ goto err_no_io_res; ++ } ++ ++ clk = devm_clk_get(&pdev->dev, NULL); ++ if (IS_ERR(clk)) { ++ dev_err(&pdev->dev, "no clock defined\n"); ++ return -ENODEV; ++ } ++ host->ahb_clk = clk_get_rate(clk); ++ ++ dev_dbg(&pdev->dev, "remap phy %x, virt %x \n", (u32)res->start, ++ (u32)host->base); ++ ++ host->master = spi_master_get(master); ++ ++ if (of_property_read_u16(pdev->dev.of_node, "number_of_chip_select", ++ &host->master->num_chipselect)) ++ goto err_register; ++ ++ for (cs_num = 0; cs_num < host->master->num_chipselect; cs_num++) { ++ res = platform_get_resource(pdev, IORESOURCE_MEM, cs_num + 1); ++ if (!res) { ++ dev_err(&pdev->dev, "cannot get IORESOURCE_IO 0\n"); ++ return -ENXIO; ++ } ++ ++ host->buff[cs_num] = ++ (u32)devm_ioremap_resource(&pdev->dev, res); ++ if (!host->buff[cs_num]) { ++ dev_err(&pdev->dev, "cannot remap buffer \n"); ++ err = -EIO; ++ goto err_no_io_res; ++ } ++ ++ dev_dbg(&pdev->dev, "remap io phy %x, virt %x \n", ++ (u32)res->start, (u32)host->buff[cs_num]); ++ } ++ ++ host->master->bus_num = pdev->id; ++ host->dev = &pdev->dev; ++ ++ /* Setup the state for bitbang driver */ ++ host->master->setup = fmc_spi_setup; ++ host->master->transfer = fmc_spi_transfer; ++ host->master->cleanup = fmc_spi_cleanup; ++ // host->master->spi_flash_read = fmc_spi_flash_read; ++ ++ platform_set_drvdata(pdev, host); ++ ++ /* Register our spi controller */ ++ err = devm_spi_register_master(&pdev->dev, host->master); ++ if (err) { ++ dev_err(&pdev->dev, "failed to register SPI master\n"); ++ goto err_register; ++ } ++ ++ dev_dbg(&pdev->dev, "fmc_spi : driver load \n"); ++ ++ return 0; ++ ++err_register: ++ spi_master_put(host->master); ++ iounmap(host->base); ++ for (cs_num = 0; cs_num < host->master->num_chipselect; cs_num++) { ++ iounmap((void *)host->buff[cs_num]); ++ } ++ ++err_no_io_res: ++ kfree(master); ++ kfree(host); ++ ++err_nomem: ++ return err; ++} ++ ++static int fmc_spi_remove(struct platform_device *pdev) ++{ ++ struct resource *res0; ++ struct fmc_spi_host *host = platform_get_drvdata(pdev); ++ ++ dev_dbg(host->dev, "fmc_spi_remove()\n"); ++ ++ if (!host) ++ return -1; ++ ++ res0 = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ release_mem_region(res0->start, res0->end - res0->start + 1); ++ iounmap(host->base); ++ iounmap(host->buff); ++ ++ platform_set_drvdata(pdev, NULL); ++ spi_unregister_master(host->master); ++ spi_master_put(host->master); ++ return 0; ++} ++ ++#ifdef CONFIG_PM ++static int fmc_spi_suspend(struct platform_device *pdev, pm_message_t msg) ++{ ++ return 0; ++} ++ ++static int fmc_spi_resume(struct platform_device *pdev) ++{ ++ return 0; ++} ++#else ++#define fmc_spi_suspend NULL ++#define fmc_spi_resume NULL ++#endif ++ ++static const struct of_device_id fmc_spi_of_match[] = { ++ { .compatible = "aspeed,fmc-spi" }, ++ {}, ++}; ++ ++static struct platform_driver fmc_spi_driver = { ++ .probe = fmc_spi_probe, ++ .remove = fmc_spi_remove, ++#ifdef CONFIG_PM ++ .suspend = fmc_spi_suspend, ++ .resume = fmc_spi_resume, ++#endif ++ .driver = { ++ .name = KBUILD_MODNAME, ++ .of_match_table = fmc_spi_of_match, ++ }, ++}; ++ ++module_platform_driver(fmc_spi_driver); ++ ++MODULE_DESCRIPTION("FMC SPI Driver"); ++MODULE_AUTHOR("Ryan Chen"); ++MODULE_LICENSE("GPL"); +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0002-Enable-pass-through-on-GPIOE1-and-GPIOE3-free.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0002-Enable-pass-through-on-GPIOE1-and-GPIOE3-free.patch new file mode 100644 index 000000000..6123b8701 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0002-Enable-pass-through-on-GPIOE1-and-GPIOE3-free.patch @@ -0,0 +1,173 @@ +From 12ef9a4189cd44212a5a5bd2e1c6fce0756ace9f Mon Sep 17 00:00:00 2001 +From: "Jason M. Bills" <jason.m.bills@linux.intel.com> +Date: Fri, 3 May 2019 16:12:39 -0700 +Subject: [PATCH] Enable pass-through on GPIOE1 and GPIOE3 free + +This change adds a gpio_disable_free() implementation that checks +if the GPIO being freed is GPIOE1 (33) or GPIOE3 (35) and will +re-enable the pass-through mux. + +Tested: +Requested GPIOs 33 and 35 and used devmem to check that pass-through +was disabled. Then freed them and checked that pass-through was +enabled again. + +Signed-off-by: Jason M. Bills <jason.m.bills@linux.intel.com> +--- + drivers/pinctrl/aspeed/pinctrl-aspeed-g5.c | 17 ++++++++++ + drivers/pinctrl/aspeed/pinctrl-aspeed-g6.c | 17 ++++++++++ + drivers/pinctrl/aspeed/pinctrl-aspeed.c | 53 ++++++++++++++++++++++++++++++ + drivers/pinctrl/aspeed/pinctrl-aspeed.h | 3 ++ + 4 files changed, 90 insertions(+) + +diff --git a/drivers/pinctrl/aspeed/pinctrl-aspeed-g5.c b/drivers/pinctrl/aspeed/pinctrl-aspeed-g5.c +index 0cab4c2576e2..c5406e2da320 100644 +--- a/drivers/pinctrl/aspeed/pinctrl-aspeed-g5.c ++++ b/drivers/pinctrl/aspeed/pinctrl-aspeed-g5.c +@@ -2780,6 +2780,22 @@ static int aspeed_g5_sig_expr_set(struct aspeed_pinmux_data *ctx, + return 0; + } + ++#define GPIOE1 33 ++#define GPIOE3 35 ++static void aspeed_g5_gpio_disable_free(struct pinctrl_dev *pctldev, ++ struct pinctrl_gpio_range *range, ++ unsigned int offset) ++{ ++ /* ++ * If we're freeing GPIOE1 (33) or GPIOE3 (35) then re-enable the ++ * pass-through mux setting; otherwise, do nothing. ++ */ ++ if (offset != GPIOE1 && offset != GPIOE3) ++ return; ++ ++ aspeed_gpio_disable_free(pctldev, range, offset); ++} ++ + static const struct aspeed_pin_config_map aspeed_g5_pin_config_map[] = { + { PIN_CONFIG_BIAS_PULL_DOWN, 0, 1, BIT_MASK(0)}, + { PIN_CONFIG_BIAS_PULL_DOWN, -1, 0, BIT_MASK(0)}, +@@ -2815,6 +2831,7 @@ static const struct pinmux_ops aspeed_g5_pinmux_ops = { + .get_function_groups = aspeed_pinmux_get_fn_groups, + .set_mux = aspeed_pinmux_set_mux, + .gpio_request_enable = aspeed_gpio_request_enable, ++ .gpio_disable_free = aspeed_g5_gpio_disable_free, + .strict = true, + }; + +diff --git a/drivers/pinctrl/aspeed/pinctrl-aspeed-g6.c b/drivers/pinctrl/aspeed/pinctrl-aspeed-g6.c +index fb96e8d2e6c8..bcd8c5656265 100644 +--- a/drivers/pinctrl/aspeed/pinctrl-aspeed-g6.c ++++ b/drivers/pinctrl/aspeed/pinctrl-aspeed-g6.c +@@ -2655,6 +2655,22 @@ static int aspeed_g6_sig_expr_set(struct aspeed_pinmux_data *ctx, + return 0; + } + ++#define GPIOP1 121 ++#define GPIOP3 123 ++static void aspeed_g6_gpio_disable_free(struct pinctrl_dev *pctldev, ++ struct pinctrl_gpio_range *range, ++ unsigned int offset) ++{ ++ /* ++ * If we're freeing GPIOP1 (121) or GPIOP3 (123) then re-enable the ++ * pass-through mux setting; otherwise, do nothing. ++ */ ++ if (offset != GPIOP1 && offset != GPIOP3) ++ return; ++ ++ aspeed_gpio_disable_free(pctldev, range, offset); ++} ++ + static const struct aspeed_pin_config_map aspeed_g6_pin_config_map[] = { + { PIN_CONFIG_BIAS_PULL_DOWN, 0, 1, BIT_MASK(0)}, + { PIN_CONFIG_BIAS_PULL_DOWN, -1, 0, BIT_MASK(0)}, +@@ -2695,6 +2711,7 @@ static const struct pinmux_ops aspeed_g6_pinmux_ops = { + .get_function_groups = aspeed_pinmux_get_fn_groups, + .set_mux = aspeed_pinmux_set_mux, + .gpio_request_enable = aspeed_gpio_request_enable, ++ .gpio_disable_free = aspeed_g6_gpio_disable_free, + .strict = true, + }; + +diff --git a/drivers/pinctrl/aspeed/pinctrl-aspeed.c b/drivers/pinctrl/aspeed/pinctrl-aspeed.c +index 53f3f8aec695..ca53f3743d16 100644 +--- a/drivers/pinctrl/aspeed/pinctrl-aspeed.c ++++ b/drivers/pinctrl/aspeed/pinctrl-aspeed.c +@@ -375,6 +375,59 @@ int aspeed_gpio_request_enable(struct pinctrl_dev *pctldev, + return 0; + } + ++void aspeed_gpio_disable_free(struct pinctrl_dev *pctldev, ++ struct pinctrl_gpio_range *range, ++ unsigned int offset) ++{ ++ struct aspeed_pinctrl_data *pdata = pinctrl_dev_get_drvdata(pctldev); ++ const struct aspeed_pin_desc *pdesc = pdata->pins[offset].drv_data; ++ const struct aspeed_sig_expr ***prios, **funcs, *expr; ++ int ret; ++ ++ if (!pdesc) ++ return; ++ ++ dev_dbg(pctldev->dev, ++ "Freeing pass-through pin %s (%d). Re-enabling pass-through.\n", ++ pdesc->name, offset); ++ ++ prios = pdesc->prios; ++ ++ if (!prios) ++ return; ++ ++ /* Disable any functions of higher priority than GPIO just in case */ ++ while ((funcs = *prios)) { ++ if (aspeed_gpio_in_exprs(funcs)) ++ break; ++ ++ ret = aspeed_disable_sig(&pdata->pinmux, funcs); ++ if (ret) ++ return; ++ ++ prios++; ++ } ++ ++ if (!funcs) { ++ char *signals = get_defined_signals(pdesc); ++ ++ pr_warn("No GPIO signal type found on pin %s (%d). Found: %s\n", ++ pdesc->name, offset, signals); ++ kfree(signals); ++ ++ return; ++ } ++ ++ /* ++ * Pass-through should be one priority higher than the GPIO function, ++ * so decrement our prios and enable that function ++ */ ++ prios--; ++ funcs = *prios; ++ expr = *funcs; ++ aspeed_sig_expr_enable(&pdata->pinmux, expr); ++} ++ + int aspeed_pinctrl_probe(struct platform_device *pdev, + struct pinctrl_desc *pdesc, + struct aspeed_pinctrl_data *pdata) +diff --git a/drivers/pinctrl/aspeed/pinctrl-aspeed.h b/drivers/pinctrl/aspeed/pinctrl-aspeed.h +index 4dcde3bc29c8..bd497c20a15f 100644 +--- a/drivers/pinctrl/aspeed/pinctrl-aspeed.h ++++ b/drivers/pinctrl/aspeed/pinctrl-aspeed.h +@@ -101,6 +101,9 @@ int aspeed_pinmux_set_mux(struct pinctrl_dev *pctldev, unsigned int function, + int aspeed_gpio_request_enable(struct pinctrl_dev *pctldev, + struct pinctrl_gpio_range *range, + unsigned int offset); ++void aspeed_gpio_disable_free(struct pinctrl_dev *pctldev, ++ struct pinctrl_gpio_range *range, ++ unsigned int offset); + int aspeed_pinctrl_probe(struct platform_device *pdev, + struct pinctrl_desc *pdesc, + struct aspeed_pinctrl_data *pdata); +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0003-Enable-GPIOE0-and-GPIOE2-pass-through-by-default.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0003-Enable-GPIOE0-and-GPIOE2-pass-through-by-default.patch new file mode 100644 index 000000000..1ee1c68de --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0003-Enable-GPIOE0-and-GPIOE2-pass-through-by-default.patch @@ -0,0 +1,70 @@ +From b34fdedc21c98db698150d5014e12927a017b07f Mon Sep 17 00:00:00 2001 +From: "Jason M. Bills" <jason.m.bills@linux.intel.com> +Date: Mon, 6 May 2019 14:18:27 -0700 +Subject: [PATCH] Enable GPIOE0 and GPIOE2 pass-through by default + +This change sets the gpio DT pinctrl default configuration to +enable GPIOE0 and GPIOE2 pass-through. Since this causes +pinctrl_get_select_default() to be called automatically for +the gpio driver to claim the GPIO pins in those groups, we +also need to call pinctrl_put() to release claim on the +pass-through GPIOs so they can be requested at runtime. + +Tested: +Disabled pass-through in uboot and confirmed that after booting +Linux, pass-through is enabled and 'cat /sys/kernel/debug/pinctrl/ +1e6e2000.syscon\:pinctrl-aspeed-g5-pinctrl/pinmux-pins' shows that +the pass-through GPIOs are UNCLAIMED. + +Signed-off-by: Jason M. Bills <jason.m.bills@linux.intel.com> +--- + arch/arm/boot/dts/aspeed-bmc-intel-ast2500.dts | 4 ++++ + drivers/gpio/gpio-aspeed.c | 10 ++++++++++ + 2 files changed, 14 insertions(+) + +diff --git a/arch/arm/boot/dts/aspeed-bmc-intel-ast2500.dts b/arch/arm/boot/dts/aspeed-bmc-intel-ast2500.dts +index 72bb1b3..ea1d9cd3 100644 +--- a/arch/arm/boot/dts/aspeed-bmc-intel-ast2500.dts ++++ b/arch/arm/boot/dts/aspeed-bmc-intel-ast2500.dts +@@ -134,6 +134,10 @@ + + &gpio { + status = "okay"; ++ /* Enable GPIOE0 and GPIOE2 pass-through by default */ ++ pinctrl-names = "pass-through"; ++ pinctrl-0 = <&pinctrl_gpie0_default ++ &pinctrl_gpie2_default>; + gpio-line-names = + /*A0-A7*/ "","","","","","","","", + /*B0-B7*/ "FM_BMC_BOARD_SKU_ID0_N","FM_BMC_BOARD_SKU_ID1_N","FM_BMC_BOARD_SKU_ID2_N","FM_BMC_BOARD_SKU_ID3_N","FM_BMC_BOARD_SKU_ID4_N","","","", +diff --git a/drivers/gpio/gpio-aspeed.c b/drivers/gpio/gpio-aspeed.c +index 09e53c5..6165b44 100644 +--- a/drivers/gpio/gpio-aspeed.c ++++ b/drivers/gpio/gpio-aspeed.c +@@ -1140,6 +1140,7 @@ static int __init aspeed_gpio_probe(struct platform_device *pdev) + { + const struct of_device_id *gpio_id; + struct aspeed_gpio *gpio; ++ struct pinctrl *pinctrl; + int rc, i, banks, err; + u32 ngpio; + +@@ -1190,6 +1191,15 @@ static int __init aspeed_gpio_probe(struct platform_device *pdev) + return -ENOMEM; + + /* ++ * Select the pass-through pinctrl config to enable the pass-through ++ * mux for GPIOs marked as pass-through. Then call pinctrl_put() to ++ * release claim of the GPIO pins, so they can be requested at runtime. ++ */ ++ pinctrl = pinctrl_get_select(&pdev->dev, "pass-through"); ++ if (!IS_ERR(pinctrl)) ++ pinctrl_put(pinctrl); ++ ++ /* + * Populate it with initial values read from the HW and switch + * all command sources to the ARM by default + */ +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0005-128MB-flashmap-for-PFR.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0005-128MB-flashmap-for-PFR.patch new file mode 100644 index 000000000..ca54df9ee --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0005-128MB-flashmap-for-PFR.patch @@ -0,0 +1,30 @@ +From 9a71adc7aecbfdf066ba54c763c2ecd8fb09d3cd Mon Sep 17 00:00:00 2001 +From: Vikram Bodireddy <vikram.bodireddy@intel.com> +Date: Wed, 6 Feb 2019 15:59:34 +0530 +Subject: [PATCH] Selecting 128MB for PFR + +PFR platforms requires 128MB flash mapping. +This will override the existing 64MB flash map +and loads 128MB flash map. + +Signed-off-by: Vikram Bodireddy <vikram.bodireddy@intel.com> +--- + arch/arm/boot/dts/aspeed-bmc-intel-ast2500.dts | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/arch/arm/boot/dts/aspeed-bmc-intel-ast2500.dts b/arch/arm/boot/dts/aspeed-bmc-intel-ast2500.dts +index 4815104459f1..df02bb1aaf36 100644 +--- a/arch/arm/boot/dts/aspeed-bmc-intel-ast2500.dts ++++ b/arch/arm/boot/dts/aspeed-bmc-intel-ast2500.dts +@@ -89,7 +89,7 @@ + flash@0 { + status = "okay"; + m25p,fast-read; +-#include "openbmc-flash-layout-intel-64MB.dtsi" ++#include "openbmc-flash-layout-intel-128MB.dtsi" + }; + }; + +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0006-Allow-monitoring-of-power-control-input-GPIOs.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0006-Allow-monitoring-of-power-control-input-GPIOs.patch new file mode 100644 index 000000000..b9c14c1ac --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0006-Allow-monitoring-of-power-control-input-GPIOs.patch @@ -0,0 +1,123 @@ +From 5f984831a3f451e299ecaf42d65dee36ff0ffeee Mon Sep 17 00:00:00 2001 +From: "Jason M. Bills" <jason.m.bills@linux.intel.com> +Date: Fri, 24 May 2019 12:42:59 -0700 +Subject: [PATCH] Allow monitoring of power control input GPIOs + +The pass-through input GPIOs cannot be monitored because when +requested, pass-through is disabled which causes a change on the +pass-through output. + +The SIO GPIOs cannot be monitored because when requested, the +request is rejected based on the value of the ACPI strap. + +This change removes the register check condition from the pass- +through and desired SIO GPIOs so they can be requsted and +monitored from power control. + +Tested: +For pass-through, I used gpioset to hold a request on the input +GPIOs and confirmed that pass-through remained enabled. + +For SIO, I used gpioget to confirm that I can successfully request +and read the GPIO value. + +Signed-off-by: Jason M. Bills <jason.m.bills@linux.intel.com> +--- + drivers/pinctrl/aspeed/pinctrl-aspeed-g5.c | 10 +++++----- + drivers/pinctrl/aspeed/pinctrl-aspeed-g6.c | 10 +++++----- + 2 files changed, 10 insertions(+), 10 deletions(-) + +diff --git a/drivers/pinctrl/aspeed/pinctrl-aspeed-g5.c b/drivers/pinctrl/aspeed/pinctrl-aspeed-g5.c +index c5406e2da320..fde74c23ca7f 100644 +--- a/drivers/pinctrl/aspeed/pinctrl-aspeed-g5.c ++++ b/drivers/pinctrl/aspeed/pinctrl-aspeed-g5.c +@@ -279,7 +279,7 @@ FUNC_GROUP_DECL(SD2, F19, E21, F20, D20, D21, E20, G18, C21); + + #define B20 32 + SIG_EXPR_LIST_DECL_SINGLE(B20, NCTS3, NCTS3, SIG_DESC_SET(SCU80, 16)); +-SIG_EXPR_DECL_SINGLE(GPIE0IN, GPIE0, GPIE0_DESC); ++SIG_EXPR_DECL_SINGLE(GPIE0IN, GPIE0); + SIG_EXPR_DECL_SINGLE(GPIE0IN, GPIE, GPIE_DESC); + SIG_EXPR_LIST_DECL_DUAL(B20, GPIE0IN, GPIE0, GPIE); + PIN_DECL_2(B20, GPIOE0, NCTS3, GPIE0IN); +@@ -299,7 +299,7 @@ FUNC_GROUP_DECL(GPIE0, B20, C20); + + #define F18 34 + SIG_EXPR_LIST_DECL_SINGLE(F18, NDSR3, NDSR3, SIG_DESC_SET(SCU80, 18)); +-SIG_EXPR_DECL_SINGLE(GPIE2IN, GPIE2, GPIE2_DESC); ++SIG_EXPR_DECL_SINGLE(GPIE2IN, GPIE2); + SIG_EXPR_DECL_SINGLE(GPIE2IN, GPIE, GPIE_DESC); + SIG_EXPR_LIST_DECL_DUAL(F18, GPIE2IN, GPIE2, GPIE); + PIN_DECL_2(F18, GPIOE2, NDSR3, GPIE2IN); +@@ -1412,7 +1412,7 @@ FUNC_GROUP_DECL(ADC15, H4); + + #define R22 192 + SIG_EXPR_DECL_SINGLE(SIOS3, SIOS3, SIG_DESC_SET(SCUA4, 8)); +-SIG_EXPR_DECL_SINGLE(SIOS3, ACPI, ACPI_DESC); ++SIG_EXPR_DECL_SINGLE(SIOS3, ACPI); + SIG_EXPR_LIST_DECL_DUAL(R22, SIOS3, SIOS3, ACPI); + SIG_EXPR_LIST_DECL_SINGLE(R22, DASHR22, DASHR22, SIG_DESC_SET(SCU94, 10)); + PIN_DECL_2(R22, GPIOY0, SIOS3, DASHR22); +@@ -1420,7 +1420,7 @@ FUNC_GROUP_DECL(SIOS3, R22); + + #define R21 193 + SIG_EXPR_DECL_SINGLE(SIOS5, SIOS5, SIG_DESC_SET(SCUA4, 9)); +-SIG_EXPR_DECL_SINGLE(SIOS5, ACPI, ACPI_DESC); ++SIG_EXPR_DECL_SINGLE(SIOS5, ACPI); + SIG_EXPR_LIST_DECL_DUAL(R21, SIOS5, SIOS5, ACPI); + SIG_EXPR_LIST_DECL_SINGLE(R21, DASHR21, DASHR21, SIG_DESC_SET(SCU94, 10)); + PIN_DECL_2(R21, GPIOY1, SIOS5, DASHR21); +@@ -1436,7 +1436,7 @@ FUNC_GROUP_DECL(SIOPWREQ, P22); + + #define P21 195 + SIG_EXPR_DECL_SINGLE(SIOONCTRL, SIOONCTRL, SIG_DESC_SET(SCUA4, 11)); +-SIG_EXPR_DECL_SINGLE(SIOONCTRL, ACPI, ACPI_DESC); ++SIG_EXPR_DECL_SINGLE(SIOONCTRL, ACPI); + SIG_EXPR_LIST_DECL_DUAL(P21, SIOONCTRL, SIOONCTRL, ACPI); + SIG_EXPR_LIST_DECL_SINGLE(P21, DASHP21, DASHP21, SIG_DESC_SET(SCU94, 11)); + PIN_DECL_2(P21, GPIOY3, SIOONCTRL, DASHP21); +diff --git a/drivers/pinctrl/aspeed/pinctrl-aspeed-g6.c b/drivers/pinctrl/aspeed/pinctrl-aspeed-g6.c +index bcd8c5656265..c18ceb52c289 100644 +--- a/drivers/pinctrl/aspeed/pinctrl-aspeed-g6.c ++++ b/drivers/pinctrl/aspeed/pinctrl-aspeed-g6.c +@@ -762,7 +762,7 @@ SSSF_PIN_DECL(AC23, GPIOO7, PWM7, SIG_DESC_SET(SCU41C, 23)); + + #define AB22 120 + SIG_EXPR_LIST_DECL_SEMG(AB22, PWM8, PWM8G1, PWM8, SIG_DESC_SET(SCU41C, 24)); +-SIG_EXPR_LIST_DECL_SESG(AB22, THRUIN0, THRU0, SIG_DESC_SET(SCU4BC, 24)); ++SIG_EXPR_LIST_DECL_SESG(AB22, THRUIN0, THRU0); + PIN_DECL_2(AB22, GPIOP0, PWM8, THRUIN0); + GROUP_DECL(PWM8G1, AB22); + FUNC_DECL_2(PWM8, PWM8G0, PWM8G1); +@@ -779,7 +779,7 @@ FUNC_DECL_2(PWM9, PWM9G0, PWM9G1); + + #define AA23 122 + SIG_EXPR_LIST_DECL_SEMG(AA23, PWM10, PWM10G1, PWM10, SIG_DESC_SET(SCU41C, 26)); +-SIG_EXPR_LIST_DECL_SESG(AA23, THRUIN1, THRU1, SIG_DESC_SET(SCU4BC, 26)); ++SIG_EXPR_LIST_DECL_SESG(AA23, THRUIN1, THRU1); + PIN_DECL_2(AA23, GPIOP2, PWM10, THRUIN1); + GROUP_DECL(PWM10G1, AA23); + FUNC_DECL_2(PWM10, PWM10G0, PWM10G1); +@@ -1070,16 +1070,16 @@ FUNC_GROUP_DECL(GPIU7, AC17); + FUNC_GROUP_DECL(ADC15, AC17); + + #define AB15 168 +-SSSF_PIN_DECL(AB15, GPIOV0, SIOS3, SIG_DESC_SET(SCU434, 8)); ++SSSF_PIN_DECL(AB15, GPIOV0, SIOS3); + + #define AF14 169 +-SSSF_PIN_DECL(AF14, GPIOV1, SIOS5, SIG_DESC_SET(SCU434, 9)); ++SSSF_PIN_DECL(AF14, GPIOV1, SIOS5); + + #define AD14 170 + SSSF_PIN_DECL(AD14, GPIOV2, SIOPWREQ, SIG_DESC_SET(SCU434, 10)); + + #define AC15 171 +-SSSF_PIN_DECL(AC15, GPIOV3, SIOONCTRL, SIG_DESC_SET(SCU434, 11)); ++SSSF_PIN_DECL(AC15, GPIOV3, SIOONCTRL); + + #define AE15 172 + SSSF_PIN_DECL(AE15, GPIOV4, SIOPWRGD, SIG_DESC_SET(SCU434, 12)); +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0007-aspeed-pwm-tacho-change-default-fan-speed.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0007-aspeed-pwm-tacho-change-default-fan-speed.patch new file mode 100644 index 000000000..476a07043 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0007-aspeed-pwm-tacho-change-default-fan-speed.patch @@ -0,0 +1,28 @@ +From b7e8941cf3b1c1c42330207600c91fb23781a77b Mon Sep 17 00:00:00 2001 +From: James Feist <james.feist@linux.intel.com> +Date: Tue, 2 Jul 2019 10:14:59 -0700 +Subject: [PATCH] aspeed-pwm-tacho: change default fan speed + +Change it from max to 58% + +Signed-off-by: James Feist <james.feist@linux.intel.com> +--- + drivers/hwmon/aspeed-pwm-tacho.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/hwmon/aspeed-pwm-tacho.c b/drivers/hwmon/aspeed-pwm-tacho.c +index 40c489be62ea..ae5771f881b5 100644 +--- a/drivers/hwmon/aspeed-pwm-tacho.c ++++ b/drivers/hwmon/aspeed-pwm-tacho.c +@@ -160,7 +160,7 @@ + */ + #define M_TACH_MODE 0x02 /* 10b */ + #define M_TACH_UNIT 0x0210 +-#define INIT_FAN_CTRL 0xFF ++#define INIT_FAN_CTRL 150 /* 58% */ + + /* How long we sleep in us while waiting for an RPM result. */ + #define ASPEED_RPM_STATUS_SLEEP_USEC 500 +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0014-arm-dts-aspeed-g5-add-espi.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0014-arm-dts-aspeed-g5-add-espi.patch new file mode 100644 index 000000000..993beb9ec --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0014-arm-dts-aspeed-g5-add-espi.patch @@ -0,0 +1,31 @@ +From ea37c85db4d2a593d4f3392e21ec2cc108b111e5 Mon Sep 17 00:00:00 2001 +From: Juston Li <juston.li@intel.com> +Date: Mon, 27 Mar 2017 11:16:00 -0700 +Subject: [PATCH] arm: dts: aspeed-g5: add espi + +Signed-off-by: Juston Li <juston.li@intel.com> +--- + arch/arm/boot/dts/aspeed-g5.dtsi | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/arch/arm/boot/dts/aspeed-g5.dtsi b/arch/arm/boot/dts/aspeed-g5.dtsi +index 6580b232771e..452ec323534f 100644 +--- a/arch/arm/boot/dts/aspeed-g5.dtsi ++++ b/arch/arm/boot/dts/aspeed-g5.dtsi +@@ -408,6 +408,13 @@ + status = "disabled"; + }; + ++ espi: espi@1e6ee000 { ++ compatible = "aspeed,ast2500-espi-slave"; ++ reg = <0x1e6ee000 0x100>; ++ interrupts = <23>; ++ status = "disabled"; ++ }; ++ + lpc: lpc@1e789000 { + compatible = "aspeed,ast2500-lpc", "simple-mfd"; + reg = <0x1e789000 0x1000>; +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0015-New-flash-map-for-intel.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0015-New-flash-map-for-intel.patch new file mode 100644 index 000000000..b9de9f5a9 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0015-New-flash-map-for-intel.patch @@ -0,0 +1,117 @@ +From f57d473a30f208754457bdb63512c307f7499ac8 Mon Sep 17 00:00:00 2001 +From: Vernon Mauery <vernon.mauery@intel.com> +Date: Mon, 4 Jun 2018 13:45:42 -0700 +Subject: [PATCH] New flash map for intel + +Signed-off-by: Vernon Mauery <vernon.mauery@intel.com> +Signed-off-by: Vikram Bodireddy <vikram.bodireddy@intel.com> +--- + .../boot/dts/openbmc-flash-layout-intel-128MB.dtsi | 50 ++++++++++++++++++++++ + .../boot/dts/openbmc-flash-layout-intel-64MB.dtsi | 38 ++++++++++++++++ + 2 files changed, 88 insertions(+) + create mode 100644 arch/arm/boot/dts/openbmc-flash-layout-intel-128MB.dtsi + create mode 100644 arch/arm/boot/dts/openbmc-flash-layout-intel-64MB.dtsi + +diff --git a/arch/arm/boot/dts/openbmc-flash-layout-intel-128MB.dtsi b/arch/arm/boot/dts/openbmc-flash-layout-intel-128MB.dtsi +new file mode 100644 +index 000000000000..0d3794423aed +--- /dev/null ++++ b/arch/arm/boot/dts/openbmc-flash-layout-intel-128MB.dtsi +@@ -0,0 +1,50 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++// 128MB flash layout: PFR (active + tmp1/tmp2 + extra) ++// image with common RW partition ++ ++partitions { ++ compatible = "fixed-partitions"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ++ u-boot@0 { ++ reg = <0x0 0x80000>; ++ label = "u-boot"; ++ }; ++ ++ pfm@80000 { ++ reg = <0x80000 0x20000>; ++ label = "pfm"; ++ }; ++ ++ u-boot-env@a0000 { ++ reg = <0xa0000 0x20000>; ++ label = "u-boot-env"; ++ }; ++ ++ sofs@c0000 { ++ reg = <0xc0000 0x200000>; ++ label = "sofs"; ++ }; ++ ++ rwfs@2c0000 { ++ reg = <0x2c0000 0x840000>; ++ label = "rwfs"; ++ }; ++ ++ fit-image-a@b00000 { ++ reg = <0xb00000 0x1f00000>; ++ label = "image-a"; ++ }; ++ ++ rc-image@2a00000 { ++ reg = <0x2a00000 0x2000000>; ++ label = "rc-image"; ++ }; ++ ++ image-staging@4a00000 { ++ reg = <0x4a00000 0x3600000>; ++ label = "image-stg"; ++ }; ++ ++}; +diff --git a/arch/arm/boot/dts/openbmc-flash-layout-intel-64MB.dtsi b/arch/arm/boot/dts/openbmc-flash-layout-intel-64MB.dtsi +new file mode 100644 +index 000000000000..092708f5021f +--- /dev/null ++++ b/arch/arm/boot/dts/openbmc-flash-layout-intel-64MB.dtsi +@@ -0,0 +1,38 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++// 64MB flash layout: redundant image with common RW partition ++ ++partitions { ++ compatible = "fixed-partitions"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ++ u-boot@0 { ++ reg = <0x0 0x80000>; ++ label = "u-boot"; ++ }; ++ ++ fit-image-a@80000 { ++ reg = <0x80000 0x1b80000>; ++ label = "image-a"; ++ }; ++ ++ sofs@1c00000 { ++ reg = <0x1c00000 0x200000>; ++ label = "sofs"; ++ }; ++ ++ rwfs@1e00000 { ++ reg = <0x1e00000 0x600000>; ++ label = "rwfs"; ++ }; ++ ++ u-boot-env@2400000 { ++ reg = <0x2400000 0x20000>; ++ label = "u-boot-env"; ++ }; ++ ++ fit-image-b@2480000 { ++ reg = <0x2480000 0x1b80000>; ++ label = "image-b"; ++ }; ++}; +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0016-Add-ASPEED-SGPIO-driver.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0016-Add-ASPEED-SGPIO-driver.patch new file mode 100644 index 000000000..950272e1b --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0016-Add-ASPEED-SGPIO-driver.patch @@ -0,0 +1,760 @@ +From a49e262cca260d15dc245fdd1870c89068042063 Mon Sep 17 00:00:00 2001 +From: "Feist, James" <james.feist@intel.com> +Date: Tue, 4 Jun 2019 14:00:39 -0700 +Subject: [PATCH] Add ASPEED SGPIO driver + +Add SGPIO driver support for Aspeed SoCs. + +Signed-off-by: James Feist <james.feist@linux.intel.com> +Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +--- + drivers/gpio/Kconfig | 8 + + drivers/gpio/Makefile | 1 + + drivers/gpio/sgpio-aspeed.c | 704 ++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 713 insertions(+) + create mode 100644 drivers/gpio/sgpio-aspeed.c + +diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig +index 7138290cdd36..0235b20a95f6 100644 +--- a/drivers/gpio/Kconfig ++++ b/drivers/gpio/Kconfig +@@ -128,6 +128,14 @@ config GPIO_ASPEED_SGPIO + help + Say Y here to support Aspeed AST2500 SGPIO functionality. + ++config SGPIO_ASPEED ++ tristate "ASPEED SGPIO support" ++ depends on (ARCH_ASPEED || COMPILE_TEST) && OF_GPIO ++ select GPIO_GENERIC ++ select GPIOLIB_IRQCHIP ++ help ++ Say Y here to support ASPEED SGPIO functionality. ++ + config GPIO_ATH79 + tristate "Atheros AR71XX/AR724X/AR913X GPIO support" + default y if ATH79 +diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile +index e4599f90f702..0e80c8cd5ae7 100644 +--- a/drivers/gpio/Makefile ++++ b/drivers/gpio/Makefile +@@ -33,6 +33,7 @@ obj-$(CONFIG_GPIO_AMDPT) += gpio-amdpt.o + obj-$(CONFIG_GPIO_ARIZONA) += gpio-arizona.o + obj-$(CONFIG_GPIO_ASPEED) += gpio-aspeed.o + obj-$(CONFIG_GPIO_ASPEED_SGPIO) += gpio-aspeed-sgpio.o ++obj-$(CONFIG_SGPIO_ASPEED) += sgpio-aspeed.o + obj-$(CONFIG_GPIO_ATH79) += gpio-ath79.o + obj-$(CONFIG_GPIO_BCM_KONA) += gpio-bcm-kona.o + obj-$(CONFIG_GPIO_BD70528) += gpio-bd70528.o +diff --git a/drivers/gpio/sgpio-aspeed.c b/drivers/gpio/sgpio-aspeed.c +new file mode 100644 +index 000000000000..5028e9144a75 +--- /dev/null ++++ b/drivers/gpio/sgpio-aspeed.c +@@ -0,0 +1,704 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++// Copyright (c) 2019 Intel Corporation ++ ++#include <linux/bitfield.h> ++#include <linux/clk.h> ++#include <linux/gpio/driver.h> ++#include <linux/module.h> ++#include <linux/of.h> ++#include <linux/platform_device.h> ++#include <linux/spinlock.h> ++ ++#define ASPEED_SGPIO_CTRL 0x54 ++#define ASPEED_SGPIO_CLK_DIV_MASK GENMASK(31, 16) ++#define ASPEED_SGPIO_CLK_DIV_MIN 1 ++#define ASPEED_SGPIO_CLK_DIV_MAX 65535 ++#define ASPEED_SGPIO_PINBYTES_MASK GENMASK(9, 6) ++#define ASPEED_SGPIO_PINBYTES_MIN 1 ++#define ASPEED_SGPIO_PINBYTES_MAX 10 ++#define ASPEED_SGPIO_ENABLE BIT(0) ++ ++#define ASPEED_SGPIO_BUS_FREQ_DEFAULT 1000000 ++ ++struct aspeed_bank_props { ++ unsigned int bank; ++ u32 input; ++ u32 output; ++}; ++ ++struct aspeed_sgpio_config { ++ unsigned int nr_pgpios; ++ unsigned int nr_gpios; ++ const struct aspeed_bank_props *props; ++}; ++ ++struct aspeed_sgpio { ++ struct gpio_chip chip; ++ struct irq_chip irqc; ++ spinlock_t lock; ++ void __iomem *base; ++ int irq; ++ const struct aspeed_sgpio_config *config; ++}; ++ ++struct aspeed_sgpio_bank { ++ uint16_t val_reg; ++ uint16_t rdata_reg; ++ uint16_t tolerance_reg; ++ uint16_t irq_regs; ++ bool support_irq; ++ const char names[4][3]; ++}; ++ ++/* ++ * Note: The "val" register returns the input value sampled on the line. ++ * Or, it can be used for writing a value on the line. ++ * ++ * The "rdata" register returns the content of the write latch and thus ++ * can be used to read back what was last written reliably. ++ */ ++ ++static const struct aspeed_sgpio_bank aspeed_sgpio_banks[] = { ++ { ++ .val_reg = 0x0000, ++ .rdata_reg = 0x0070, ++ .tolerance_reg = 0x0018, ++ .irq_regs = 0x0004, ++ .support_irq = false, ++ .names = { "OA", "OB", "OC", "OD" }, ++ }, ++ { ++ .val_reg = 0x001C, ++ .rdata_reg = 0x0074, ++ .tolerance_reg = 0x0034, ++ .irq_regs = 0x0020, ++ .support_irq = false, ++ .names = { "OE", "OF", "OG", "OH" }, ++ }, ++ { ++ .val_reg = 0x0038, ++ .rdata_reg = 0x0078, ++ .tolerance_reg = 0x0050, ++ .irq_regs = 0x003C, ++ .support_irq = false, ++ .names = { "OI", "OJ" }, ++ }, ++ { ++ .val_reg = 0x0000, ++ .rdata_reg = 0x0070, ++ .tolerance_reg = 0x0018, ++ .irq_regs = 0x0004, ++ .support_irq = true, ++ .names = { "IA", "IB", "IC", "ID" }, ++ }, ++ { ++ .val_reg = 0x001C, ++ .rdata_reg = 0x0074, ++ .tolerance_reg = 0x0034, ++ .irq_regs = 0x0020, ++ .support_irq = true, ++ .names = { "IE", "IF", "IG", "IH" }, ++ }, ++ { ++ .val_reg = 0x0038, ++ .rdata_reg = 0x0078, ++ .tolerance_reg = 0x0050, ++ .irq_regs = 0x003C, ++ .support_irq = true, ++ .names = { "II", "IJ" }, ++ }, ++}; ++ ++enum aspeed_sgpio_reg { ++ reg_val, ++ reg_rdata, ++ reg_irq_enable, ++ reg_irq_type0, ++ reg_irq_type1, ++ reg_irq_type2, ++ reg_irq_status, ++ reg_tolerance, ++}; ++ ++#define GPIO_IRQ_ENABLE 0x00 ++#define GPIO_IRQ_TYPE0 0x04 ++#define GPIO_IRQ_TYPE1 0x08 ++#define GPIO_IRQ_TYPE2 0x0c ++#define GPIO_IRQ_STATUS 0x10 ++ ++/* This will be resolved at compile time */ ++static inline void __iomem *bank_reg(struct aspeed_sgpio *gpio, ++ const struct aspeed_sgpio_bank *bank, ++ const enum aspeed_sgpio_reg reg) ++{ ++ switch (reg) { ++ case reg_val: ++ return gpio->base + bank->val_reg; ++ case reg_rdata: ++ return gpio->base + bank->rdata_reg; ++ case reg_irq_enable: ++ return gpio->base + bank->irq_regs + GPIO_IRQ_ENABLE; ++ case reg_irq_type0: ++ return gpio->base + bank->irq_regs + GPIO_IRQ_TYPE0; ++ case reg_irq_type1: ++ return gpio->base + bank->irq_regs + GPIO_IRQ_TYPE1; ++ case reg_irq_type2: ++ return gpio->base + bank->irq_regs + GPIO_IRQ_TYPE2; ++ case reg_irq_status: ++ return gpio->base + bank->irq_regs + GPIO_IRQ_STATUS; ++ case reg_tolerance: ++ return gpio->base + bank->tolerance_reg; ++ default: ++ WARN_ON(1); ++ } ++ ++ return NULL; ++} ++ ++#define GPIO_BANK(x) ((x) >> 5) ++#define GPIO_OFFSET(x) ((x) & 0x1f) ++#define GPIO_BIT(x) BIT(GPIO_OFFSET(x)) ++ ++static const struct aspeed_sgpio_bank *to_bank(unsigned int offset) ++{ ++ unsigned int bank = GPIO_BANK(offset); ++ ++ WARN_ON(bank >= ARRAY_SIZE(aspeed_sgpio_banks)); ++ return &aspeed_sgpio_banks[bank]; ++} ++ ++static inline bool is_bank_props_sentinel(const struct aspeed_bank_props *props) ++{ ++ return !(props->input || props->output); ++} ++ ++static inline const struct aspeed_bank_props *find_bank_props( ++ struct aspeed_sgpio *gpio, unsigned int offset) ++{ ++ const struct aspeed_bank_props *props = gpio->config->props; ++ ++ while (!is_bank_props_sentinel(props)) { ++ if (props->bank == GPIO_BANK(offset)) ++ return props; ++ props++; ++ } ++ ++ return NULL; ++} ++ ++static inline bool have_input(struct aspeed_sgpio *gpio, unsigned int offset) ++{ ++ const struct aspeed_bank_props *props = find_bank_props(gpio, offset); ++ ++ return !props || (props->input & GPIO_BIT(offset)); ++} ++ ++static inline bool have_output(struct aspeed_sgpio *gpio, unsigned int offset) ++{ ++ const struct aspeed_bank_props *props = find_bank_props(gpio, offset); ++ ++ return !props || (props->output & GPIO_BIT(offset)); ++} ++ ++static int aspeed_sgpio_get(struct gpio_chip *gc, unsigned int offset) ++{ ++ struct aspeed_sgpio *gpio = gpiochip_get_data(gc); ++ const struct aspeed_sgpio_bank *bank = to_bank(offset); ++ enum aspeed_sgpio_reg reg; ++ ++ if (have_output(gpio, offset)) ++ reg = reg_rdata; ++ else ++ reg = reg_val; ++ ++ return !!(ioread32(bank_reg(gpio, bank, reg)) & GPIO_BIT(offset)); ++} ++ ++static void aspeed_sgpio_set(struct gpio_chip *gc, unsigned int offset, int val) ++{ ++ const struct aspeed_sgpio_bank *bank = to_bank(offset); ++ struct aspeed_sgpio *gpio = gpiochip_get_data(gc); ++ unsigned long flags; ++ u32 reg; ++ ++ if (!have_output(gpio, offset)) ++ return; ++ ++ spin_lock_irqsave(&gpio->lock, flags); ++ ++ reg = ioread32(bank_reg(gpio, bank, reg_rdata)); ++ ++ if (val) ++ reg |= GPIO_BIT(offset); ++ else ++ reg &= ~GPIO_BIT(offset); ++ ++ iowrite32(reg, bank_reg(gpio, bank, reg_val)); ++ ++ spin_unlock_irqrestore(&gpio->lock, flags); ++} ++ ++static int aspeed_sgpio_dir_in(struct gpio_chip *gc, unsigned int offset) ++{ ++ struct aspeed_sgpio *gpio = gpiochip_get_data(gc); ++ ++ if (!have_input(gpio, offset)) ++ return -ENOTSUPP; ++ ++ return 0; ++} ++ ++static int aspeed_sgpio_dir_out(struct gpio_chip *gc, unsigned int offset, ++ int val) ++{ ++ struct aspeed_sgpio *gpio = gpiochip_get_data(gc); ++ ++ if (!have_output(gpio, offset)) ++ return -ENOTSUPP; ++ ++ aspeed_sgpio_set(gc, offset, val); ++ ++ return 0; ++} ++ ++static int aspeed_sgpio_get_direction(struct gpio_chip *gc, unsigned int offset) ++{ ++ struct aspeed_sgpio *gpio = gpiochip_get_data(gc); ++ ++ if (have_output(gpio, offset)) ++ return 0; ++ else if (have_input(gpio, offset)) ++ return 1; ++ ++ return -ENOTSUPP; ++} ++ ++static inline int ++irqd_to_aspeed_sgpio_data(struct irq_data *d, struct aspeed_sgpio **gpio, ++ const struct aspeed_sgpio_bank **bank, ++ u32 *bit, int *offset) ++{ ++ struct aspeed_sgpio *internal; ++ ++ *offset = irqd_to_hwirq(d); ++ ++ internal = irq_data_get_irq_chip_data(d); ++ ++ *gpio = internal; ++ *bank = to_bank(*offset); ++ *bit = GPIO_BIT(*offset); ++ ++ return 0; ++} ++ ++static void aspeed_sgpio_irq_ack(struct irq_data *d) ++{ ++ const struct aspeed_sgpio_bank *bank; ++ struct aspeed_sgpio *gpio; ++ void __iomem *status_addr; ++ unsigned long flags; ++ int rc, offset; ++ u32 bit; ++ ++ rc = irqd_to_aspeed_sgpio_data(d, &gpio, &bank, &bit, &offset); ++ if (rc) ++ return; ++ ++ status_addr = bank_reg(gpio, bank, reg_irq_status); ++ ++ spin_lock_irqsave(&gpio->lock, flags); ++ ++ iowrite32(bit, status_addr); ++ ++ spin_unlock_irqrestore(&gpio->lock, flags); ++} ++ ++static void aspeed_sgpio_irq_set_mask(struct irq_data *d, bool set) ++{ ++ const struct aspeed_sgpio_bank *bank; ++ struct aspeed_sgpio *gpio; ++ unsigned long flags; ++ u32 reg, bit; ++ void __iomem *addr; ++ int rc, offset; ++ ++ rc = irqd_to_aspeed_sgpio_data(d, &gpio, &bank, &bit, &offset); ++ if (rc) ++ return; ++ ++ if (!bank->support_irq) ++ return; ++ ++ addr = bank_reg(gpio, bank, reg_irq_enable); ++ ++ spin_lock_irqsave(&gpio->lock, flags); ++ ++ reg = ioread32(addr); ++ if (set) ++ reg |= bit; ++ else ++ reg &= ~bit; ++ ++ iowrite32(reg, addr); ++ ++ spin_unlock_irqrestore(&gpio->lock, flags); ++} ++ ++static void aspeed_sgpio_irq_mask(struct irq_data *d) ++{ ++ aspeed_sgpio_irq_set_mask(d, false); ++} ++ ++static void aspeed_sgpio_irq_unmask(struct irq_data *d) ++{ ++ aspeed_sgpio_irq_set_mask(d, true); ++} ++ ++static int aspeed_sgpio_set_type(struct irq_data *d, unsigned int type) ++{ ++ u32 type0 = 0; ++ u32 type1 = 0; ++ u32 type2 = 0; ++ u32 bit, reg; ++ const struct aspeed_sgpio_bank *bank; ++ irq_flow_handler_t handler; ++ struct aspeed_sgpio *gpio; ++ unsigned long flags; ++ void __iomem *addr; ++ int rc, offset; ++ ++ rc = irqd_to_aspeed_sgpio_data(d, &gpio, &bank, &bit, &offset); ++ if (rc) ++ return -EINVAL; ++ ++ if (!bank->support_irq) ++ return -ENOTSUPP; ++ ++ switch (type & IRQ_TYPE_SENSE_MASK) { ++ case IRQ_TYPE_EDGE_BOTH: ++ type2 |= bit; ++ /* fall through */ ++ case IRQ_TYPE_EDGE_RISING: ++ type0 |= bit; ++ /* fall through */ ++ case IRQ_TYPE_EDGE_FALLING: ++ handler = handle_edge_irq; ++ break; ++ case IRQ_TYPE_LEVEL_HIGH: ++ type0 |= bit; ++ /* fall through */ ++ case IRQ_TYPE_LEVEL_LOW: ++ type1 |= bit; ++ handler = handle_level_irq; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ spin_lock_irqsave(&gpio->lock, flags); ++ ++ addr = bank_reg(gpio, bank, reg_irq_type0); ++ reg = ioread32(addr); ++ reg = (reg & ~bit) | type0; ++ iowrite32(reg, addr); ++ ++ addr = bank_reg(gpio, bank, reg_irq_type1); ++ reg = ioread32(addr); ++ reg = (reg & ~bit) | type1; ++ iowrite32(reg, addr); ++ ++ addr = bank_reg(gpio, bank, reg_irq_type2); ++ reg = ioread32(addr); ++ reg = (reg & ~bit) | type2; ++ iowrite32(reg, addr); ++ ++ spin_unlock_irqrestore(&gpio->lock, flags); ++ ++ irq_set_handler_locked(d, handler); ++ ++ return 0; ++} ++ ++static void aspeed_sgpio_irq_handler(struct irq_desc *desc) ++{ ++ struct gpio_chip *gc = irq_desc_get_handler_data(desc); ++ struct aspeed_sgpio *data = gpiochip_get_data(gc); ++ struct irq_chip *ic = irq_desc_get_chip(desc); ++ unsigned int i, p, girq; ++ unsigned long reg; ++ ++ chained_irq_enter(ic, desc); ++ ++ for (i = 0; i < ARRAY_SIZE(aspeed_sgpio_banks); i++) { ++ const struct aspeed_sgpio_bank *bank = &aspeed_sgpio_banks[i]; ++ ++ if (!bank->support_irq) ++ continue; ++ ++ reg = ioread32(bank_reg(data, bank, reg_irq_status)); ++ ++ for_each_set_bit(p, ®, 32) { ++ girq = irq_find_mapping(gc->irq.domain, i * 32 + p); ++ generic_handle_irq(girq); ++ } ++ } ++ ++ chained_irq_exit(ic, desc); ++} ++ ++static void aspeed_sgpio_init_irq_valid_mask(struct gpio_chip *gc, ++ unsigned long *valid_mask, ++ unsigned int ngpios) ++{ ++ struct aspeed_sgpio *gpio = gpiochip_get_data(gc); ++ const struct aspeed_bank_props *props = gpio->config->props; ++ ++ while (!is_bank_props_sentinel(props)) { ++ unsigned int offset; ++ const unsigned long int input = props->input; ++ ++ /* Pretty crummy approach, but similar to GPIO core */ ++ for_each_clear_bit(offset, &input, 32) { ++ unsigned int i = props->bank * 32 + offset; ++ ++ if (i >= gpio->chip.ngpio) ++ break; ++ ++ clear_bit(i, valid_mask); ++ } ++ ++ props++; ++ } ++} ++ ++static int aspeed_sgpio_setup_irqs(struct aspeed_sgpio *gpio, ++ struct platform_device *pdev) ++{ ++ const struct aspeed_sgpio_bank *bank; ++ struct gpio_irq_chip *girq; ++ int rc, i; ++ ++ /* Initialize IRQ and tolerant settings */ ++ for (i = 0; i < ARRAY_SIZE(aspeed_sgpio_banks); i++) { ++ bank = &aspeed_sgpio_banks[i]; ++ ++ /* Value will be reset by WDT reset */ ++ iowrite32(0x00000000, bank_reg(gpio, bank, reg_tolerance)); ++ ++ if (!bank->support_irq) ++ continue; ++ ++ /* disable irq enable bits */ ++ iowrite32(0x00000000, bank_reg(gpio, bank, reg_irq_enable)); ++ /* clear status bits */ ++ iowrite32(0xffffffff, bank_reg(gpio, bank, reg_irq_status)); ++ /* set rising or level-high irq */ ++ iowrite32(0xffffffff, bank_reg(gpio, bank, reg_irq_type0)); ++ /* trigger type is level */ ++ iowrite32(0xffffffff, bank_reg(gpio, bank, reg_irq_type1)); ++ /* single trigger mode */ ++ iowrite32(0x00000000, bank_reg(gpio, bank, reg_irq_type2)); ++ } ++ ++ rc = platform_get_irq(pdev, 0); ++ if (rc < 0) ++ return rc; ++ ++ gpio->irq = rc; ++ girq = &gpio->chip.irq; ++ girq->chip = &gpio->irqc; ++ girq->chip->name = dev_name(&pdev->dev); ++ girq->chip->irq_ack = aspeed_sgpio_irq_ack; ++ girq->chip->irq_mask = aspeed_sgpio_irq_mask; ++ girq->chip->irq_unmask = aspeed_sgpio_irq_unmask; ++ girq->chip->irq_set_type = aspeed_sgpio_set_type; ++ girq->parent_handler = aspeed_sgpio_irq_handler; ++ girq->num_parents = 1; ++ girq->parents = devm_kcalloc(&pdev->dev, 1, ++ sizeof(*girq->parents), ++ GFP_KERNEL); ++ if (!girq->parents) ++ return -ENOMEM; ++ girq->parents[0] = gpio->irq; ++ girq->default_type = IRQ_TYPE_NONE; ++ girq->handler = handle_bad_irq; ++ girq->init_valid_mask = aspeed_sgpio_init_irq_valid_mask; ++ ++ return 0; ++} ++ ++static int aspeed_sgpio_reset_tolerance(struct gpio_chip *chip, ++ unsigned int offset, bool enable) ++{ ++ struct aspeed_sgpio *gpio = gpiochip_get_data(chip); ++ unsigned long flags; ++ void __iomem *treg; ++ u32 val; ++ ++ treg = bank_reg(gpio, to_bank(offset), reg_tolerance); ++ ++ spin_lock_irqsave(&gpio->lock, flags); ++ ++ val = readl(treg); ++ ++ if (enable) ++ val |= GPIO_BIT(offset); ++ else ++ val &= ~GPIO_BIT(offset); ++ ++ writel(val, treg); ++ ++ spin_unlock_irqrestore(&gpio->lock, flags); ++ ++ return 0; ++} ++ ++static int aspeed_sgpio_set_config(struct gpio_chip *chip, unsigned int offset, ++ unsigned long config) ++{ ++ unsigned long param = pinconf_to_config_param(config); ++ u32 arg = pinconf_to_config_argument(config); ++ ++ if (param == PIN_CONFIG_PERSIST_STATE) ++ return aspeed_sgpio_reset_tolerance(chip, offset, arg); ++ ++ return -ENOTSUPP; ++} ++ ++/* ++ * Any banks not specified in a struct aspeed_bank_props array are assumed to ++ * have the properties: ++ * ++ * { .input = 0xffffffff, .output = 0xffffffff } ++ */ ++ ++static const struct aspeed_bank_props ast_sgpio_bank_props[] = { ++ /* input output */ ++ { 0, 0x00000000, 0xffffffff }, /* OA/OB/OC/OD */ ++ { 1, 0x00000000, 0xffffffff }, /* OE/OF/OG/OH */ ++ { 2, 0x00000000, 0x0000ffff }, /* OI/OJ */ ++ { 3, 0xffffffff, 0x00000000 }, /* IA/IB/IC/ID */ ++ { 4, 0xffffffff, 0x00000000 }, /* IE/IF/IG/IH */ ++ { 5, 0x0000ffff, 0x00000000 }, /* II/IJ */ ++ { } ++}; ++ ++/* ++ * This H/W has 80 bidirectional lines so this driver provides total 160 lines ++ * for 80 outputs and 80 inputs. To simplify bank register manipulation, it ++ * uses 96 lines per each input and output set so total 192 lines it has. ++ */ ++static const struct aspeed_sgpio_config ast2400_config = ++ { .nr_pgpios = 224, .nr_gpios = 192, .props = ast_sgpio_bank_props }; ++ ++static const struct aspeed_sgpio_config ast2500_config = ++ { .nr_pgpios = 232, .nr_gpios = 192, .props = ast_sgpio_bank_props }; ++ ++static const struct of_device_id aspeed_sgpio_of_table[] = { ++ { .compatible = "aspeed,ast2400-sgpio", .data = &ast2400_config }, ++ { .compatible = "aspeed,ast2500-sgpio", .data = &ast2500_config }, ++ { .compatible = "aspeed,ast2600-sgpio", .data = &ast2500_config }, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, aspeed_sgpio_of_table); ++ ++static int __init aspeed_sgpio_probe(struct platform_device *pdev) ++{ ++ const struct of_device_id *gpio_id; ++ u32 sgpio_freq, clk_div, nb_gpios; ++ struct aspeed_sgpio *gpio; ++ unsigned long src_freq; ++ struct clk *clk; ++ int rc; ++ ++ gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL); ++ if (!gpio) ++ return -ENOMEM; ++ ++ gpio->base = devm_platform_ioremap_resource(pdev, 0); ++ if (IS_ERR(gpio->base)) ++ return PTR_ERR(gpio->base); ++ ++ spin_lock_init(&gpio->lock); ++ ++ gpio_id = of_match_node(aspeed_sgpio_of_table, pdev->dev.of_node); ++ if (!gpio_id) ++ return -EINVAL; ++ ++ gpio->config = gpio_id->data; ++ ++ rc = device_property_read_u32(&pdev->dev, "bus-frequency", &sgpio_freq); ++ if (rc < 0) { ++ dev_warn(&pdev->dev, "Could not read bus-frequency property. Use default.\n"); ++ sgpio_freq = ASPEED_SGPIO_BUS_FREQ_DEFAULT; ++ } ++ ++ clk = devm_clk_get(&pdev->dev, NULL); ++ if (IS_ERR(clk)) { ++ rc = PTR_ERR(clk); ++ if (rc != -EPROBE_DEFER) ++ dev_err(&pdev->dev, "Failed to get clk source.\n"); ++ return rc; ++ } ++ ++ /* ++ * There is a limitation that SGPIO clock division has to be larger or ++ * equal to 1. And a read back value of clock division is 1-bit left ++ * shifted from the actual value. ++ * ++ * GPIO254[31:16] - Serial GPIO clock division: ++ * Serial GPIO clock period = period of PCLK * 2 * (GPIO254[31:16] + 1) ++ * ++ * SGPIO master controller updates every data input when SGPMLD is low. ++ * For an example, SGPIO clock is 1MHz and number of SGPIO is 80. Each ++ * SGPIO will be updated every 80us. ++ */ ++ src_freq = clk_get_rate(clk); ++ clk_div = src_freq / (2 * sgpio_freq) - 1; ++ if (clk_div < ASPEED_SGPIO_CLK_DIV_MIN) ++ clk_div = ASPEED_SGPIO_CLK_DIV_MIN; ++ else if (clk_div > ASPEED_SGPIO_CLK_DIV_MAX) ++ clk_div = ASPEED_SGPIO_CLK_DIV_MAX; ++ ++ nb_gpios = gpio->config->nr_gpios / 16; ++ if (nb_gpios < ASPEED_SGPIO_PINBYTES_MIN) ++ nb_gpios = ASPEED_SGPIO_PINBYTES_MIN; ++ else if (nb_gpios > ASPEED_SGPIO_PINBYTES_MAX) ++ nb_gpios = ASPEED_SGPIO_PINBYTES_MAX; ++ ++ iowrite32(FIELD_PREP(ASPEED_SGPIO_CLK_DIV_MASK, clk_div) | ++ FIELD_PREP(ASPEED_SGPIO_PINBYTES_MASK, nb_gpios) | ++ ASPEED_SGPIO_ENABLE, ++ gpio->base + ASPEED_SGPIO_CTRL); ++ ++ gpio->chip.parent = &pdev->dev; ++ gpio->chip.ngpio = gpio->config->nr_gpios; ++ ++ gpio->chip.direction_input = aspeed_sgpio_dir_in; ++ gpio->chip.direction_output = aspeed_sgpio_dir_out; ++ gpio->chip.get_direction = aspeed_sgpio_get_direction; ++ gpio->chip.get = aspeed_sgpio_get; ++ gpio->chip.set = aspeed_sgpio_set; ++ gpio->chip.set_config = aspeed_sgpio_set_config; ++ gpio->chip.label = dev_name(&pdev->dev); ++ gpio->chip.base = -1; ++ ++ rc = aspeed_sgpio_setup_irqs(gpio, pdev); ++ if (rc < 0) ++ return rc; ++ ++ return devm_gpiochip_add_data(&pdev->dev, &gpio->chip, gpio); ++} ++ ++static struct platform_driver aspeed_sgpio_driver = { ++ .driver = { ++ .name = KBUILD_MODNAME, ++ .of_match_table = aspeed_sgpio_of_table, ++ }, ++}; ++ ++module_platform_driver_probe(aspeed_sgpio_driver, aspeed_sgpio_probe); ++ ++MODULE_AUTHOR("Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>"); ++MODULE_DESCRIPTION("Aspeed SGPIO Master Driver"); ++MODULE_LICENSE("GPL v2"); +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0017-SGPIO-DT-and-pinctrl-fixup.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0017-SGPIO-DT-and-pinctrl-fixup.patch new file mode 100644 index 000000000..50c31f625 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0017-SGPIO-DT-and-pinctrl-fixup.patch @@ -0,0 +1,227 @@ +From 89b5ebd26fc44b4bf820aa507bc88eec028ba218 Mon Sep 17 00:00:00 2001 +From: Vernon Mauery <vernon.mauery@intel.com> +Date: Wed, 16 May 2018 10:03:14 -0700 +Subject: [PATCH] SGPIO DT and pinctrl fixup + +This commit fixes DT and pinctrl for SGPIO use. + +Signed-off-by: Vernon Mauery <vernon.mauery@intel.com> +Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +--- + arch/arm/boot/dts/aspeed-g4.dtsi | 56 +++++++++++------------------- + arch/arm/boot/dts/aspeed-g5.dtsi | 3 +- + arch/arm/boot/dts/aspeed-g6.dtsi | 15 ++++++++ + drivers/pinctrl/aspeed/pinctrl-aspeed-g4.c | 48 ++++++++++++------------- + 4 files changed, 60 insertions(+), 62 deletions(-) + +diff --git a/arch/arm/boot/dts/aspeed-g4.dtsi b/arch/arm/boot/dts/aspeed-g4.dtsi +index b875c0785833..e9fd66ab3099 100644 +--- a/arch/arm/boot/dts/aspeed-g4.dtsi ++++ b/arch/arm/boot/dts/aspeed-g4.dtsi +@@ -254,6 +254,20 @@ + #interrupt-cells = <2>; + }; + ++ sgpio: sgpio@1e780200 { ++ #gpio-cells = <2>; ++ gpio-controller; ++ compatible = "aspeed,ast2400-sgpio"; ++ reg = <0x1e780200 0x0100>; ++ interrupts = <40>; ++ interrupt-controller; ++ clocks = <&syscon ASPEED_CLK_APB>; ++ bus-frequency = <1000000>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_sgpm_default>; ++ status = "disabled"; ++ }; ++ + timer: timer@1e782000 { + /* This timer is a Faraday FTTMR010 derivative */ + compatible = "aspeed,ast2400-timer"; +@@ -1227,44 +1241,14 @@ + groups = "SD2"; + }; + +- pinctrl_sgpmck_default: sgpmck_default { +- function = "SGPMCK"; +- groups = "SGPMCK"; +- }; +- +- pinctrl_sgpmi_default: sgpmi_default { +- function = "SGPMI"; +- groups = "SGPMI"; +- }; +- +- pinctrl_sgpmld_default: sgpmld_default { +- function = "SGPMLD"; +- groups = "SGPMLD"; +- }; +- +- pinctrl_sgpmo_default: sgpmo_default { +- function = "SGPMO"; +- groups = "SGPMO"; +- }; +- +- pinctrl_sgpsck_default: sgpsck_default { +- function = "SGPSCK"; +- groups = "SGPSCK"; +- }; +- +- pinctrl_sgpsi0_default: sgpsi0_default { +- function = "SGPSI0"; +- groups = "SGPSI0"; +- }; +- +- pinctrl_sgpsi1_default: sgpsi1_default { +- function = "SGPSI1"; +- groups = "SGPSI1"; ++ pinctrl_sgpm_default: sgpm_default { ++ function = "SGPM"; ++ groups = "SGPM"; + }; + +- pinctrl_sgpsld_default: sgpsld_default { +- function = "SGPSLD"; +- groups = "SGPSLD"; ++ pinctrl_sgps_default: sgps_default { ++ function = "SGPS"; ++ groups = "SGPS"; + }; + + pinctrl_sioonctrl_default: sioonctrl_default { +diff --git a/arch/arm/boot/dts/aspeed-g5.dtsi b/arch/arm/boot/dts/aspeed-g5.dtsi +index 452ec323534f..20b2eb8052b7 100644 +--- a/arch/arm/boot/dts/aspeed-g5.dtsi ++++ b/arch/arm/boot/dts/aspeed-g5.dtsi +@@ -326,8 +326,7 @@ + reg = <0x1e780200 0x0100>; + clocks = <&syscon ASPEED_CLK_APB>; + interrupt-controller; +- ngpios = <8>; +- bus-frequency = <12000000>; ++ bus-frequency = <1000000>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_sgpm_default>; + status = "disabled"; +diff --git a/arch/arm/boot/dts/aspeed-g6.dtsi b/arch/arm/boot/dts/aspeed-g6.dtsi +index 54add29d8217..459070693aba 100644 +--- a/arch/arm/boot/dts/aspeed-g6.dtsi ++++ b/arch/arm/boot/dts/aspeed-g6.dtsi +@@ -339,6 +339,21 @@ + #interrupt-cells = <2>; + }; + ++ sgpio: sgpio@1e780500 { ++ #gpio-cells = <2>; ++ gpio-controller; ++ compatible = "aspeed,ast2600-sgpio"; ++ reg = <0x1e780500 0x0100>; ++ #interrupt-cells = <2>; ++ interrupts = <GIC_SPI 51 IRQ_TYPE_LEVEL_HIGH>; ++ interrupt-controller; ++ bus-frequency = <1000000>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_sgpm1_default>; ++ clocks = <&syscon ASPEED_CLK_APB1>; ++ status = "disabled"; ++ }; ++ + rtc: rtc@1e781000 { + compatible = "aspeed,ast2600-rtc"; + reg = <0x1e781000 0x18>; +diff --git a/drivers/pinctrl/aspeed/pinctrl-aspeed-g4.c b/drivers/pinctrl/aspeed/pinctrl-aspeed-g4.c +index bfed0e274643..10dadfd0e6e1 100644 +--- a/drivers/pinctrl/aspeed/pinctrl-aspeed-g4.c ++++ b/drivers/pinctrl/aspeed/pinctrl-aspeed-g4.c +@@ -430,16 +430,22 @@ SSSF_PIN_DECL(E16, GPIOF6, TXD4, SIG_DESC_SET(SCU80, 30)); + SSSF_PIN_DECL(C17, GPIOF7, RXD4, SIG_DESC_SET(SCU80, 31)); + + #define A14 48 +-SSSF_PIN_DECL(A14, GPIOG0, SGPSCK, SIG_DESC_SET(SCU84, 0)); ++SIG_EXPR_LIST_DECL_SINGLE(A14, SGPSCK, SGPS, SIG_DESC_SET(SCU84, 0)); ++PIN_DECL_1(A14, GPIOG0, SGPSCK); + + #define E13 49 +-SSSF_PIN_DECL(E13, GPIOG1, SGPSLD, SIG_DESC_SET(SCU84, 1)); ++SIG_EXPR_LIST_DECL_SINGLE(E13, SGPSLD, SGPS, SIG_DESC_SET(SCU84, 1)); ++PIN_DECL_1(E13, GPIOG1, SGPSLD); + + #define D13 50 +-SSSF_PIN_DECL(D13, GPIOG2, SGPSI0, SIG_DESC_SET(SCU84, 2)); ++SIG_EXPR_LIST_DECL_SINGLE(D13, SGPSIO, SGPS, SIG_DESC_SET(SCU84, 2)); ++PIN_DECL_1(D13, GPIOG2, SGPSIO); + + #define C13 51 +-SSSF_PIN_DECL(C13, GPIOG3, SGPSI1, SIG_DESC_SET(SCU84, 3)); ++SIG_EXPR_LIST_DECL_SINGLE(C13, SGPSI1, SGPS, SIG_DESC_SET(SCU84, 3)); ++PIN_DECL_1(C13, GPIOG3, SGPSI1); ++ ++FUNC_GROUP_DECL(SGPS, A14, E13, D13, C13); + + #define B13 52 + SIG_EXPR_LIST_DECL_SINGLE(B13, OSCCLK, OSCCLK, SIG_DESC_SET(SCU2C, 1)); +@@ -613,16 +619,22 @@ FUNC_GROUP_DECL(SPI1PASSTHRU, C22, G18, D19, C20, B22, G19, C18, E20); + FUNC_GROUP_DECL(VGABIOS_ROM, B22, G19, C18, E20); + + #define J5 72 +-SSSF_PIN_DECL(J5, GPIOJ0, SGPMCK, SIG_DESC_SET(SCU84, 8)); ++SIG_EXPR_LIST_DECL_SINGLE(J5, SGPMCK, SGPM, SIG_DESC_SET(SCU84, 8)); ++PIN_DECL_1(J5, GPIOJ0, SGPMCK); + + #define J4 73 +-SSSF_PIN_DECL(J4, GPIOJ1, SGPMLD, SIG_DESC_SET(SCU84, 9)); ++SIG_EXPR_LIST_DECL_SINGLE(J4, SGPMLD, SGPM, SIG_DESC_SET(SCU84, 9)); ++PIN_DECL_1(J4, GPIOJ1, SGPMLD); + + #define K5 74 +-SSSF_PIN_DECL(K5, GPIOJ2, SGPMO, SIG_DESC_SET(SCU84, 10)); ++SIG_EXPR_LIST_DECL_SINGLE(K5, SGPMO, SGPM, SIG_DESC_SET(SCU84, 10)); ++PIN_DECL_1(K5, GPIOJ2, SGPMO); + + #define J3 75 +-SSSF_PIN_DECL(J3, GPIOJ3, SGPMI, SIG_DESC_SET(SCU84, 11)); ++SIG_EXPR_LIST_DECL_SINGLE(J3, SGPMI, SGPM, SIG_DESC_SET(SCU84, 11)); ++PIN_DECL_1(J3, GPIOJ3, SGPMI); ++ ++FUNC_GROUP_DECL(SGPM, J5, J4, K5, J3); + + #define T4 76 + SSSF_PIN_DECL(T4, GPIOJ4, VGAHS, SIG_DESC_SET(SCU84, 12)); +@@ -2234,14 +2246,8 @@ static const struct aspeed_pin_group aspeed_g4_groups[] = { + ASPEED_PINCTRL_GROUP(SALT4), + ASPEED_PINCTRL_GROUP(SD1), + ASPEED_PINCTRL_GROUP(SD2), +- ASPEED_PINCTRL_GROUP(SGPMCK), +- ASPEED_PINCTRL_GROUP(SGPMI), +- ASPEED_PINCTRL_GROUP(SGPMLD), +- ASPEED_PINCTRL_GROUP(SGPMO), +- ASPEED_PINCTRL_GROUP(SGPSCK), +- ASPEED_PINCTRL_GROUP(SGPSI0), +- ASPEED_PINCTRL_GROUP(SGPSI1), +- ASPEED_PINCTRL_GROUP(SGPSLD), ++ ASPEED_PINCTRL_GROUP(SGPM), ++ ASPEED_PINCTRL_GROUP(SGPS), + ASPEED_PINCTRL_GROUP(SIOONCTRL), + ASPEED_PINCTRL_GROUP(SIOPBI), + ASPEED_PINCTRL_GROUP(SIOPBO), +@@ -2389,14 +2395,8 @@ static const struct aspeed_pin_function aspeed_g4_functions[] = { + ASPEED_PINCTRL_FUNC(SALT4), + ASPEED_PINCTRL_FUNC(SD1), + ASPEED_PINCTRL_FUNC(SD2), +- ASPEED_PINCTRL_FUNC(SGPMCK), +- ASPEED_PINCTRL_FUNC(SGPMI), +- ASPEED_PINCTRL_FUNC(SGPMLD), +- ASPEED_PINCTRL_FUNC(SGPMO), +- ASPEED_PINCTRL_FUNC(SGPSCK), +- ASPEED_PINCTRL_FUNC(SGPSI0), +- ASPEED_PINCTRL_FUNC(SGPSI1), +- ASPEED_PINCTRL_FUNC(SGPSLD), ++ ASPEED_PINCTRL_FUNC(SGPM), ++ ASPEED_PINCTRL_FUNC(SGPS), + ASPEED_PINCTRL_FUNC(SIOONCTRL), + ASPEED_PINCTRL_FUNC(SIOPBI), + ASPEED_PINCTRL_FUNC(SIOPBO), +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0019-Add-I2C-IPMB-support.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0019-Add-I2C-IPMB-support.patch new file mode 100644 index 000000000..128b11ecf --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0019-Add-I2C-IPMB-support.patch @@ -0,0 +1,425 @@ +From f588865f8180a6370ac639bdfc186ffc5b926246 Mon Sep 17 00:00:00 2001 +From: Haiyue Wang <haiyue.wang@linux.intel.com> +Date: Tue, 13 Feb 2018 14:28:12 +0800 +Subject: [PATCH] Add I2C IPMB support + +Some protocols over I2C are designed for bi-directional transferring +messages by using I2C Master Write protocol. Like the MCTP (Management +Component Transport Protocol) and IPMB (Intelligent Platform Management +Bus), they both require that the userspace can receive messages from +I2C dirvers under slave mode. + +This new slave mqueue backend is used to receive and queue messages, it +will exposes these messages to userspace by sysfs bin file. + +Signed-off-by: Haiyue Wang <haiyue.wang@linux.intel.com> +--- + Documentation/i2c/slave-mqueue-backend.rst | 124 +++++++++++++++++ + drivers/i2c/Kconfig | 23 +++ + drivers/i2c/Makefile | 1 + + drivers/i2c/i2c-slave-mqueue.c | 217 +++++++++++++++++++++++++++++ + 4 files changed, 365 insertions(+) + create mode 100644 Documentation/i2c/slave-mqueue-backend.rst + create mode 100644 drivers/i2c/i2c-slave-mqueue.c + +diff --git a/Documentation/i2c/slave-mqueue-backend.rst b/Documentation/i2c/slave-mqueue-backend.rst +new file mode 100644 +index 000000000000..2d0d06d8df9d +--- /dev/null ++++ b/Documentation/i2c/slave-mqueue-backend.rst +@@ -0,0 +1,124 @@ ++.. SPDX-License-Identifier: GPL-2.0 ++ ++===================================== ++Linux I2C slave message queue backend ++===================================== ++ ++:Author: Haiyue Wang <haiyue.wang@linux.intel.com> ++ ++Some protocols over I2C/SMBus are designed for bi-directional transferring ++messages by using I2C Master Write protocol. This requires that both sides ++of the communication have slave addresses. ++ ++Like MCTP (Management Component Transport Protocol) and IPMB (Intelligent ++Platform Management Bus), they both require that the userspace can receive ++messages from i2c dirvers under slave mode. ++ ++This I2C slave mqueue (message queue) backend is used to receive and queue ++messages from the remote i2c intelligent device; and it will add the target ++slave address (with R/W# bit is always 0) into the message at the first byte, ++so that userspace can use this byte to dispatch the messages into different ++handling modules. Also, like IPMB, the address byte is in its message format, ++it needs it to do checksum. ++ ++For messages are time related, so this backend will flush the oldest message ++to queue the newest one. ++ ++Link ++---- ++`Intelligent Platform Management Bus ++Communications Protocol Specification ++<https://www.intel.com/content/dam/www/public/us/en/documents/product-briefs/ipmp-spec-v1.0.pdf>`_ ++ ++`Management Component Transport Protocol (MCTP) ++SMBus/I2C Transport Binding Specification ++<https://www.dmtf.org/sites/default/files/standards/documents/DSP0237_1.1.0.pdf>`_ ++ ++How to use ++---------- ++For example, the I2C5 bus has slave address 0x10, the below command will create ++the related message queue interface: ++ ++ echo slave-mqueue 0x1010 > /sys/bus/i2c/devices/i2c-5/new_device ++ ++Then you can dump the messages like this: ++ ++ hexdump -C /sys/bus/i2c/devices/5-1010/slave-mqueue ++ ++Code Example ++------------ ++*Note: call 'lseek' before 'read', this is a requirement from kernfs' design.* ++ ++:: ++ ++ #include <sys/types.h> ++ #include <sys/stat.h> ++ #include <unistd.h> ++ #include <poll.h> ++ #include <time.h> ++ #include <fcntl.h> ++ #include <stdio.h> ++ ++ int main(int argc, char *argv[]) ++ { ++ int i, r; ++ struct pollfd pfd; ++ struct timespec ts; ++ unsigned char data[256]; ++ ++ pfd.fd = open(argv[1], O_RDONLY | O_NONBLOCK); ++ if (pfd.fd < 0) ++ return -1; ++ ++ pfd.events = POLLPRI; ++ ++ while (1) { ++ r = poll(&pfd, 1, 5000); ++ ++ if (r < 0) ++ break; ++ ++ if (r == 0 || !(pfd.revents & POLLPRI)) ++ continue; ++ ++ lseek(pfd.fd, 0, SEEK_SET); ++ r = read(pfd.fd, data, sizeof(data)); ++ if (r <= 0) ++ continue; ++ ++ clock_gettime(CLOCK_MONOTONIC, &ts); ++ printf("[%ld.%.9ld] :", ts.tv_sec, ts.tv_nsec); ++ for (i = 0; i < r; i++) ++ printf(" %02x", data[i]); ++ printf("\n"); ++ } ++ ++ close(pfd.fd); ++ ++ return 0; ++ } ++ ++Result ++------ ++*./a.out "/sys/bus/i2c/devices/5-1010/slave-mqueue"* ++ ++:: ++ ++ [10183.232500449] : 20 18 c8 2c 78 01 5b ++ [10183.479358348] : 20 18 c8 2c 78 01 5b ++ [10183.726556812] : 20 18 c8 2c 78 01 5b ++ [10183.972605863] : 20 18 c8 2c 78 01 5b ++ [10184.220124772] : 20 18 c8 2c 78 01 5b ++ [10184.467764166] : 20 18 c8 2c 78 01 5b ++ [10193.233421784] : 20 18 c8 2c 7c 01 57 ++ [10193.480273460] : 20 18 c8 2c 7c 01 57 ++ [10193.726788733] : 20 18 c8 2c 7c 01 57 ++ [10193.972781945] : 20 18 c8 2c 7c 01 57 ++ [10194.220487360] : 20 18 c8 2c 7c 01 57 ++ [10194.468089259] : 20 18 c8 2c 7c 01 57 ++ [10203.233433099] : 20 18 c8 2c 80 01 53 ++ [10203.481058715] : 20 18 c8 2c 80 01 53 ++ [10203.727610472] : 20 18 c8 2c 80 01 53 ++ [10203.974044856] : 20 18 c8 2c 80 01 53 ++ [10204.220734634] : 20 18 c8 2c 80 01 53 ++ [10204.468461664] : 20 18 c8 2c 80 01 53 +diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig +index abedd55a1264..49a2379876c6 100644 +--- a/drivers/i2c/Kconfig ++++ b/drivers/i2c/Kconfig +@@ -119,6 +119,29 @@ if I2C_SLAVE + config I2C_SLAVE_EEPROM + tristate "I2C eeprom slave driver" + ++config I2C_SLAVE_MQUEUE_MESSAGE_SIZE ++ int "The message size of I2C mqueue slave" ++ default 120 ++ ++config I2C_SLAVE_MQUEUE_QUEUE_SIZE ++ int "The queue size of I2C mqueue slave" ++ default 32 ++ help ++ This number MUST be power of 2. ++ ++config I2C_SLAVE_MQUEUE ++ tristate "I2C mqueue (message queue) slave driver" ++ help ++ Some protocols over I2C are designed for bi-directional transferring ++ messages by using I2C Master Write protocol. This driver is used to ++ receive and queue messages from the remote I2C device. ++ ++ Userspace can get the messages by reading sysfs file that this driver ++ exposes. ++ ++ This support is also available as a module. If so, the module will be ++ called i2c-slave-mqueue. ++ + endif + + config I2C_DEBUG_CORE +diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile +index bed6ba63c983..9a31bc75a446 100644 +--- a/drivers/i2c/Makefile ++++ b/drivers/i2c/Makefile +@@ -16,5 +16,6 @@ obj-$(CONFIG_I2C_MUX) += i2c-mux.o + obj-y += algos/ busses/ muxes/ + obj-$(CONFIG_I2C_STUB) += i2c-stub.o + obj-$(CONFIG_I2C_SLAVE_EEPROM) += i2c-slave-eeprom.o ++obj-$(CONFIG_I2C_SLAVE_MQUEUE) += i2c-slave-mqueue.o + + ccflags-$(CONFIG_I2C_DEBUG_CORE) := -DDEBUG +diff --git a/drivers/i2c/i2c-slave-mqueue.c b/drivers/i2c/i2c-slave-mqueue.c +new file mode 100644 +index 000000000000..6014bca0ff2a +--- /dev/null ++++ b/drivers/i2c/i2c-slave-mqueue.c +@@ -0,0 +1,217 @@ ++// SPDX-License-Identifier: GPL-2.0 ++// Copyright (c) 2017 - 2018, Intel Corporation. ++ ++#include <linux/i2c.h> ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/of.h> ++#include <linux/slab.h> ++#include <linux/spinlock.h> ++#include <linux/sysfs.h> ++ ++#define MQ_MSGBUF_SIZE CONFIG_I2C_SLAVE_MQUEUE_MESSAGE_SIZE ++#define MQ_QUEUE_SIZE CONFIG_I2C_SLAVE_MQUEUE_QUEUE_SIZE ++#define MQ_QUEUE_NEXT(x) (((x) + 1) & (MQ_QUEUE_SIZE - 1)) ++ ++struct mq_msg { ++ int len; ++ u8 *buf; ++}; ++ ++struct mq_queue { ++ struct bin_attribute bin; ++ struct kernfs_node *kn; ++ ++ spinlock_t lock; /* spinlock for queue index handling */ ++ int in; ++ int out; ++ ++ struct mq_msg *curr; ++ int truncated; /* drop current if truncated */ ++ struct mq_msg *queue; ++}; ++ ++static int i2c_slave_mqueue_callback(struct i2c_client *client, ++ enum i2c_slave_event event, u8 *val) ++{ ++ struct mq_queue *mq = i2c_get_clientdata(client); ++ struct mq_msg *msg = mq->curr; ++ int ret = 0; ++ ++ switch (event) { ++ case I2C_SLAVE_WRITE_REQUESTED: ++ mq->truncated = 0; ++ ++ msg->len = 1; ++ msg->buf[0] = client->addr << 1; ++ break; ++ ++ case I2C_SLAVE_WRITE_RECEIVED: ++ if (msg->len < MQ_MSGBUF_SIZE) { ++ msg->buf[msg->len++] = *val; ++ } else { ++ dev_err(&client->dev, "message is truncated!\n"); ++ mq->truncated = 1; ++ ret = -EINVAL; ++ } ++ break; ++ ++ case I2C_SLAVE_STOP: ++ if (unlikely(mq->truncated || msg->len < 2)) ++ break; ++ ++ spin_lock(&mq->lock); ++ mq->in = MQ_QUEUE_NEXT(mq->in); ++ mq->curr = &mq->queue[mq->in]; ++ mq->curr->len = 0; ++ ++ /* Flush the oldest message */ ++ if (mq->out == mq->in) ++ mq->out = MQ_QUEUE_NEXT(mq->out); ++ spin_unlock(&mq->lock); ++ ++ kernfs_notify(mq->kn); ++ break; ++ ++ default: ++ *val = 0xFF; ++ break; ++ } ++ ++ return ret; ++} ++ ++static ssize_t i2c_slave_mqueue_bin_read(struct file *filp, ++ struct kobject *kobj, ++ struct bin_attribute *attr, ++ char *buf, loff_t pos, size_t count) ++{ ++ struct mq_queue *mq; ++ struct mq_msg *msg; ++ unsigned long flags; ++ bool more = false; ++ ssize_t ret = 0; ++ ++ mq = dev_get_drvdata(container_of(kobj, struct device, kobj)); ++ ++ spin_lock_irqsave(&mq->lock, flags); ++ if (mq->out != mq->in) { ++ msg = &mq->queue[mq->out]; ++ ++ if (msg->len <= count) { ++ ret = msg->len; ++ memcpy(buf, msg->buf, ret); ++ } else { ++ ret = -EOVERFLOW; /* Drop this HUGE one. */ ++ } ++ ++ mq->out = MQ_QUEUE_NEXT(mq->out); ++ if (mq->out != mq->in) ++ more = true; ++ } ++ spin_unlock_irqrestore(&mq->lock, flags); ++ ++ if (more) ++ kernfs_notify(mq->kn); ++ ++ return ret; ++} ++ ++static int i2c_slave_mqueue_probe(struct i2c_client *client, ++ const struct i2c_device_id *id) ++{ ++ struct device *dev = &client->dev; ++ struct mq_queue *mq; ++ int ret, i; ++ void *buf; ++ ++ mq = devm_kzalloc(dev, sizeof(*mq), GFP_KERNEL); ++ if (!mq) ++ return -ENOMEM; ++ ++ BUILD_BUG_ON(!is_power_of_2(MQ_QUEUE_SIZE)); ++ ++ buf = devm_kmalloc_array(dev, MQ_QUEUE_SIZE, MQ_MSGBUF_SIZE, ++ GFP_KERNEL); ++ if (!buf) ++ return -ENOMEM; ++ ++ mq->queue = devm_kzalloc(dev, sizeof(*mq->queue) * MQ_QUEUE_SIZE, ++ GFP_KERNEL); ++ if (!buf) ++ return -ENOMEM; ++ ++ for (i = 0; i < MQ_QUEUE_SIZE; i++) ++ mq->queue[i].buf = buf + i * MQ_MSGBUF_SIZE; ++ ++ i2c_set_clientdata(client, mq); ++ ++ spin_lock_init(&mq->lock); ++ mq->curr = &mq->queue[0]; ++ ++ sysfs_bin_attr_init(&mq->bin); ++ mq->bin.attr.name = "slave-mqueue"; ++ mq->bin.attr.mode = 0400; ++ mq->bin.read = i2c_slave_mqueue_bin_read; ++ mq->bin.size = MQ_MSGBUF_SIZE * MQ_QUEUE_SIZE; ++ ++ ret = sysfs_create_bin_file(&dev->kobj, &mq->bin); ++ if (ret) ++ return ret; ++ ++ mq->kn = kernfs_find_and_get(dev->kobj.sd, mq->bin.attr.name); ++ if (!mq->kn) { ++ sysfs_remove_bin_file(&dev->kobj, &mq->bin); ++ return -EFAULT; ++ } ++ ++ ret = i2c_slave_register(client, i2c_slave_mqueue_callback); ++ if (ret) { ++ kernfs_put(mq->kn); ++ sysfs_remove_bin_file(&dev->kobj, &mq->bin); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int i2c_slave_mqueue_remove(struct i2c_client *client) ++{ ++ struct mq_queue *mq = i2c_get_clientdata(client); ++ ++ i2c_slave_unregister(client); ++ ++ kernfs_put(mq->kn); ++ sysfs_remove_bin_file(&client->dev.kobj, &mq->bin); ++ ++ return 0; ++} ++ ++static const struct i2c_device_id i2c_slave_mqueue_id[] = { ++ { "slave-mqueue", 0 }, ++ { } ++}; ++MODULE_DEVICE_TABLE(i2c, i2c_slave_mqueue_id); ++ ++#if IS_ENABLED(CONFIG_OF) ++static const struct of_device_id i2c_slave_mqueue_of_match[] = { ++ { .compatible = "slave-mqueue", .data = (void *)0 }, ++ { }, ++}; ++MODULE_DEVICE_TABLE(of, i2c_slave_mqueue_of_match); ++#endif ++ ++static struct i2c_driver i2c_slave_mqueue_driver = { ++ .driver = { ++ .name = "i2c-slave-mqueue", ++ .of_match_table = of_match_ptr(i2c_slave_mqueue_of_match), ++ }, ++ .probe = i2c_slave_mqueue_probe, ++ .remove = i2c_slave_mqueue_remove, ++ .id_table = i2c_slave_mqueue_id, ++}; ++module_i2c_driver(i2c_slave_mqueue_driver); ++ ++MODULE_LICENSE("GPL v2"); ++MODULE_AUTHOR("Haiyue Wang <haiyue.wang@linux.intel.com>"); ++MODULE_DESCRIPTION("I2C slave mode for receiving and queuing messages"); +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0020-misc-aspeed-add-lpc-mbox-driver.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0020-misc-aspeed-add-lpc-mbox-driver.patch new file mode 100644 index 000000000..bd468e29d --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0020-misc-aspeed-add-lpc-mbox-driver.patch @@ -0,0 +1,475 @@ +From 5d411ed0d66d3d00232519ed7d4ab6fac45e7c6e Mon Sep 17 00:00:00 2001 +From: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +Date: Wed, 10 Jul 2019 16:19:33 -0700 +Subject: [PATCH] misc: aspeed: add lpc mbox driver + +This commit adds back the lpc mbox driver which was removed from +the openbmc linux dev-5.2 tree. + +This driver should be rewritten later. + +Signed-off-by: Cyril Bur <cyrilbur@gmail.com>" +Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +--- + arch/arm/boot/dts/aspeed-g4.dtsi | 9 + + arch/arm/boot/dts/aspeed-g5.dtsi | 9 + + drivers/soc/aspeed/Kconfig | 7 + + drivers/soc/aspeed/Makefile | 1 + + drivers/soc/aspeed/aspeed-lpc-mbox.c | 376 +++++++++++++++++++++++++++++++++++ + 5 files changed, 402 insertions(+) + create mode 100644 drivers/soc/aspeed/aspeed-lpc-mbox.c + +diff --git a/arch/arm/boot/dts/aspeed-g4.dtsi b/arch/arm/boot/dts/aspeed-g4.dtsi +index e9fd66ab3099..f3edda4ae477 100644 +--- a/arch/arm/boot/dts/aspeed-g4.dtsi ++++ b/arch/arm/boot/dts/aspeed-g4.dtsi +@@ -393,6 +393,15 @@ + sio_regs: regs { + compatible = "aspeed,bmc-misc"; + }; ++ ++ mbox: mbox@180 { ++ compatible = "aspeed,ast2400-mbox"; ++ reg = <0x180 0x5c>; ++ interrupts = <46>; ++ #mbox-cells = <1>; ++ clocks = <&syscon ASPEED_CLK_GATE_LCLK>; ++ status = "disabled"; ++ }; + }; + }; + +diff --git a/arch/arm/boot/dts/aspeed-g5.dtsi b/arch/arm/boot/dts/aspeed-g5.dtsi +index 20b2eb8052b7..bd6d1461e4bd 100644 +--- a/arch/arm/boot/dts/aspeed-g5.dtsi ++++ b/arch/arm/boot/dts/aspeed-g5.dtsi +@@ -502,6 +502,15 @@ + sio_regs: regs { + compatible = "aspeed,bmc-misc"; + }; ++ ++ mbox: mbox@180 { ++ compatible = "aspeed,ast2500-mbox"; ++ reg = <0x180 0x5c>; ++ interrupts = <46>; ++ #mbox-cells = <1>; ++ clocks = <&syscon ASPEED_CLK_GATE_LCLK>; ++ status = "disabled"; ++ }; + }; + }; + +diff --git a/drivers/soc/aspeed/Kconfig b/drivers/soc/aspeed/Kconfig +index 78dd74c49ddb..a4be8e566bc7 100644 +--- a/drivers/soc/aspeed/Kconfig ++++ b/drivers/soc/aspeed/Kconfig +@@ -21,6 +21,13 @@ config ASPEED_LPC_CTRL + ioctl()s, the driver also provides a read/write interface to a BMC ram + region where the host LPC read/write region can be buffered. + ++config ASPEED_LPC_MBOX ++ tristate "Aspeed LPC Mailbox Controller" ++ depends on SOC_ASPEED && REGMAP && MFD_SYSCON ++ ---help--- ++ Expose the ASPEED LPC MBOX registers found on Aspeed SOCs (AST2400 ++ and AST2500) to userspace. ++ + config ASPEED_LPC_SNOOP + tristate "Aspeed ast2500 HOST LPC snoop support" + depends on SOC_ASPEED && REGMAP && MFD_SYSCON +diff --git a/drivers/soc/aspeed/Makefile b/drivers/soc/aspeed/Makefile +index e631b23d519b..f3ff29b874ed 100644 +--- a/drivers/soc/aspeed/Makefile ++++ b/drivers/soc/aspeed/Makefile +@@ -1,5 +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_MBOX) += aspeed-lpc-mbox.o + obj-$(CONFIG_ASPEED_LPC_SNOOP) += aspeed-lpc-snoop.o + obj-$(CONFIG_ASPEED_P2A_CTRL) += aspeed-p2a-ctrl.o +diff --git a/drivers/soc/aspeed/aspeed-lpc-mbox.c b/drivers/soc/aspeed/aspeed-lpc-mbox.c +new file mode 100644 +index 000000000000..795107206022 +--- /dev/null ++++ b/drivers/soc/aspeed/aspeed-lpc-mbox.c +@@ -0,0 +1,376 @@ ++// SPDX-License-Identifier: GPL-2.0-or-later ++// Copyright 2017 IBM Corporation ++// TODO: Rewrite this driver ++ ++#include <linux/clk.h> ++#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 "aspeed-mbox" ++ ++#define MBX_USE_INTERRUPT 0 ++ ++#define ASPEED_MBOX_NUM_REGS 16 ++ ++#define ASPEED_MBOX_DATA_0 0x00 ++#define ASPEED_MBOX_STATUS_0 0x40 ++#define ASPEED_MBOX_STATUS_1 0x44 ++#define ASPEED_MBOX_BMC_CTRL 0x48 ++#define ASPEED_MBOX_CTRL_RECV BIT(7) ++#define ASPEED_MBOX_CTRL_MASK BIT(1) ++#define ASPEED_MBOX_CTRL_SEND BIT(0) ++#define ASPEED_MBOX_HOST_CTRL 0x4c ++#define ASPEED_MBOX_INTERRUPT_0 0x50 ++#define ASPEED_MBOX_INTERRUPT_1 0x54 ++ ++struct aspeed_mbox { ++ struct miscdevice miscdev; ++ struct regmap *regmap; ++ struct clk *clk; ++ unsigned int base; ++ int irq; ++ wait_queue_head_t queue; ++ struct mutex mutex; ++}; ++ ++static atomic_t aspeed_mbox_open_count = ATOMIC_INIT(0); ++ ++static u8 aspeed_mbox_inb(struct aspeed_mbox *mbox, int reg) ++{ ++ /* ++ * The mbox registers are actually only one byte but are addressed ++ * four bytes apart. The other three bytes are marked 'reserved', ++ * they *should* be zero but lets not rely on it. ++ * I am going to rely on the fact we can casually read/write to them... ++ */ ++ unsigned int val = 0xff; /* If regmap throws an error return 0xff */ ++ int rc = regmap_read(mbox->regmap, mbox->base + reg, &val); ++ ++ if (rc) ++ dev_err(mbox->miscdev.parent, "regmap_read() failed with " ++ "%d (reg: 0x%08x)\n", rc, reg); ++ ++ return val & 0xff; ++} ++ ++static void aspeed_mbox_outb(struct aspeed_mbox *mbox, u8 data, int reg) ++{ ++ int rc = regmap_write(mbox->regmap, mbox->base + reg, data); ++ ++ if (rc) ++ dev_err(mbox->miscdev.parent, "regmap_write() failed with " ++ "%d (data: %u reg: 0x%08x)\n", rc, data, reg); ++} ++ ++static struct aspeed_mbox *file_mbox(struct file *file) ++{ ++ return container_of(file->private_data, struct aspeed_mbox, miscdev); ++} ++ ++static int aspeed_mbox_open(struct inode *inode, struct file *file) ++{ ++#if MBX_USE_INTERRUPT ++ struct aspeed_mbox *mbox = file_mbox(file); ++#endif ++ ++ if (atomic_inc_return(&aspeed_mbox_open_count) == 1) { ++#if MBX_USE_INTERRUPT ++ /* ++ * Clear the interrupt status bit if it was left on and unmask ++ * interrupts. ++ * ASPEED_MBOX_CTRL_RECV bit is W1C, this also unmasks in 1 step ++ */ ++ aspeed_mbox_outb(mbox, ASPEED_MBOX_CTRL_RECV, ASPEED_MBOX_BMC_CTRL); ++#endif ++ return 0; ++ } ++ ++ atomic_dec(&aspeed_mbox_open_count); ++ return -EBUSY; ++} ++ ++static ssize_t aspeed_mbox_read(struct file *file, char __user *buf, ++ size_t count, loff_t *ppos) ++{ ++ struct aspeed_mbox *mbox = file_mbox(file); ++ char __user *p = buf; ++ ssize_t ret; ++ int i; ++ ++ if (!access_ok(buf, count)) ++ return -EFAULT; ++ ++ if (count + *ppos > ASPEED_MBOX_NUM_REGS) ++ return -EINVAL; ++ ++#if MBX_USE_INTERRUPT ++ if (file->f_flags & O_NONBLOCK) { ++ if (!(aspeed_mbox_inb(mbox, ASPEED_MBOX_BMC_CTRL) & ++ ASPEED_MBOX_CTRL_RECV)) ++ return -EAGAIN; ++ } else if (wait_event_interruptible(mbox->queue, ++ aspeed_mbox_inb(mbox, ASPEED_MBOX_BMC_CTRL) & ++ ASPEED_MBOX_CTRL_RECV)) { ++ return -ERESTARTSYS; ++ } ++#endif ++ ++ mutex_lock(&mbox->mutex); ++ ++ for (i = *ppos; count > 0 && i < ASPEED_MBOX_NUM_REGS; i++) { ++ uint8_t reg = aspeed_mbox_inb(mbox, ASPEED_MBOX_DATA_0 + (i * 4)); ++ ++ ret = __put_user(reg, p); ++ if (ret) ++ goto out_unlock; ++ ++ p++; ++ count--; ++ } ++ ++#if MBX_USE_INTERRUPT ++ /* ASPEED_MBOX_CTRL_RECV bit is write to clear, this also unmasks in 1 step */ ++ aspeed_mbox_outb(mbox, ASPEED_MBOX_CTRL_RECV, ASPEED_MBOX_BMC_CTRL); ++#endif ++ ret = p - buf; ++ ++out_unlock: ++ mutex_unlock(&mbox->mutex); ++ return ret; ++} ++ ++static ssize_t aspeed_mbox_write(struct file *file, const char __user *buf, ++ size_t count, loff_t *ppos) ++{ ++ struct aspeed_mbox *mbox = file_mbox(file); ++ const char __user *p = buf; ++ ssize_t ret; ++ char c; ++ int i; ++ ++ if (!access_ok(buf, count)) ++ return -EFAULT; ++ ++ if (count + *ppos > ASPEED_MBOX_NUM_REGS) ++ return -EINVAL; ++ ++ mutex_lock(&mbox->mutex); ++ ++ for (i = *ppos; count > 0 && i < ASPEED_MBOX_NUM_REGS; i++) { ++ ret = __get_user(c, p); ++ if (ret) ++ goto out_unlock; ++ ++ aspeed_mbox_outb(mbox, c, ASPEED_MBOX_DATA_0 + (i * 4)); ++ p++; ++ count--; ++ } ++ ++ aspeed_mbox_outb(mbox, 0xff, ASPEED_MBOX_STATUS_0); ++ aspeed_mbox_outb(mbox, 0xff, ASPEED_MBOX_STATUS_1); ++ aspeed_mbox_outb(mbox, ASPEED_MBOX_CTRL_RECV | ASPEED_MBOX_CTRL_MASK | ASPEED_MBOX_CTRL_SEND, ASPEED_MBOX_BMC_CTRL); ++ ret = p - buf; ++ ++out_unlock: ++ mutex_unlock(&mbox->mutex); ++ return ret; ++} ++ ++static unsigned int aspeed_mbox_poll(struct file *file, poll_table *wait) ++{ ++ struct aspeed_mbox *mbox = file_mbox(file); ++ unsigned int mask = 0; ++ ++ poll_wait(file, &mbox->queue, wait); ++ ++#if MBX_USE_INTERRUPT ++ if (aspeed_mbox_inb(mbox, ASPEED_MBOX_BMC_CTRL) & ASPEED_MBOX_CTRL_RECV) ++#endif ++ mask |= POLLIN; ++ ++ return mask; ++} ++ ++static int aspeed_mbox_release(struct inode *inode, struct file *file) ++{ ++ atomic_dec(&aspeed_mbox_open_count); ++ return 0; ++} ++ ++static const struct file_operations aspeed_mbox_fops = { ++ .owner = THIS_MODULE, ++ .llseek = no_seek_end_llseek, ++ .read = aspeed_mbox_read, ++ .write = aspeed_mbox_write, ++ .open = aspeed_mbox_open, ++ .release = aspeed_mbox_release, ++ .poll = aspeed_mbox_poll, ++}; ++ ++static irqreturn_t aspeed_mbox_irq(int irq, void *arg) ++{ ++ struct aspeed_mbox *mbox = arg; ++#if MBX_USE_INTERRUPT ++ int i; ++ ++// if (!(aspeed_mbox_inb(mbox, ASPEED_MBOX_BMC_CTRL) & ASPEED_MBOX_CTRL_RECV)) ++// return IRQ_NONE; ++ ++ printk(KERN_ERR "BMC_CTRL: 0x%02x\n", ++ aspeed_mbox_inb(mbox, ASPEED_MBOX_BMC_CTRL)); ++ printk(KERN_ERR "STATUS_0: 0x%02x\n", ++ aspeed_mbox_inb(mbox, ASPEED_MBOX_STATUS_0)); ++ printk(KERN_ERR "STATUS_1: 0x%02x\n", ++ aspeed_mbox_inb(mbox, ASPEED_MBOX_STATUS_1)); ++ for (i = 0; i < ASPEED_MBOX_NUM_REGS; i++) { ++ printk(KERN_ERR "DATA_%d: 0x%02x\n", i, ++ aspeed_mbox_inb(mbox, ASPEED_MBOX_DATA_0 + (i * 4))); ++ } ++#endif ++ ++ /* Clear interrupt status */ ++ aspeed_mbox_outb(mbox, 0xff, ASPEED_MBOX_STATUS_0); ++ aspeed_mbox_outb(mbox, 0xff, ASPEED_MBOX_STATUS_1); ++ aspeed_mbox_outb(mbox, ASPEED_MBOX_CTRL_RECV, ASPEED_MBOX_BMC_CTRL); ++ ++ wake_up(&mbox->queue); ++ return IRQ_HANDLED; ++} ++ ++static int aspeed_mbox_config_irq(struct aspeed_mbox *mbox, ++ struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ int rc; ++ ++ mbox->irq = platform_get_irq(pdev, 0); ++ if (!mbox->irq) ++ return -ENODEV; ++ ++ rc = devm_request_irq(dev, mbox->irq, aspeed_mbox_irq, ++ IRQF_SHARED, DEVICE_NAME, mbox); ++ if (rc < 0) { ++ dev_err(dev, "Unable to request IRQ %d\n", mbox->irq); ++ return rc; ++ } ++ ++ /* Disable all register based interrupts. */ ++ aspeed_mbox_outb(mbox, 0xff, ASPEED_MBOX_INTERRUPT_0); /* regs 0 - 7 */ ++ aspeed_mbox_outb(mbox, 0xff, ASPEED_MBOX_INTERRUPT_1); /* regs 8 - 15 */ ++ ++ /* These registers are write one to clear. Clear them. */ ++ aspeed_mbox_outb(mbox, 0xff, ASPEED_MBOX_STATUS_0); ++ aspeed_mbox_outb(mbox, 0xff, ASPEED_MBOX_STATUS_1); ++ ++ aspeed_mbox_outb(mbox, ASPEED_MBOX_CTRL_RECV, ASPEED_MBOX_BMC_CTRL); ++ return 0; ++} ++ ++static int aspeed_mbox_probe(struct platform_device *pdev) ++{ ++ struct aspeed_mbox *mbox; ++ struct device *dev; ++ int rc; ++ ++ dev = &pdev->dev; ++ ++ mbox = devm_kzalloc(dev, sizeof(*mbox), GFP_KERNEL); ++ if (!mbox) ++ return -ENOMEM; ++ ++ dev_set_drvdata(&pdev->dev, mbox); ++ ++ rc = of_property_read_u32(dev->of_node, "reg", &mbox->base); ++ if (rc) { ++ dev_err(dev, "Couldn't read reg device-tree property\n"); ++ return rc; ++ } ++ ++ mbox->regmap = syscon_node_to_regmap( ++ pdev->dev.parent->of_node); ++ if (IS_ERR(mbox->regmap)) { ++ dev_err(dev, "Couldn't get regmap\n"); ++ return -ENODEV; ++ } ++ ++ mutex_init(&mbox->mutex); ++ init_waitqueue_head(&mbox->queue); ++ ++ mbox->clk = devm_clk_get(dev, NULL); ++ if (IS_ERR(mbox->clk)) { ++ rc = PTR_ERR(mbox->clk); ++ if (rc != -EPROBE_DEFER) ++ dev_err(dev, "couldn't get clock\n"); ++ return rc; ++ } ++ rc = clk_prepare_enable(mbox->clk); ++ if (rc) { ++ dev_err(dev, "couldn't enable clock\n"); ++ return rc; ++ } ++ ++ mbox->miscdev.minor = MISC_DYNAMIC_MINOR; ++ mbox->miscdev.name = DEVICE_NAME; ++ mbox->miscdev.fops = &aspeed_mbox_fops; ++ mbox->miscdev.parent = dev; ++ rc = misc_register(&mbox->miscdev); ++ if (rc) { ++ dev_err(dev, "Unable to register device\n"); ++ goto err; ++ } ++ ++ rc = aspeed_mbox_config_irq(mbox, pdev); ++ if (rc) { ++ dev_err(dev, "Failed to configure IRQ\n"); ++ misc_deregister(&mbox->miscdev); ++ goto err; ++ } ++ ++ dev_info(&pdev->dev, "LPC mbox registered, irq %d\n", mbox->irq); ++ ++ return 0; ++ ++err: ++ clk_disable_unprepare(mbox->clk); ++ ++ return rc; ++} ++ ++static int aspeed_mbox_remove(struct platform_device *pdev) ++{ ++ struct aspeed_mbox *mbox = dev_get_drvdata(&pdev->dev); ++ ++ misc_deregister(&mbox->miscdev); ++ clk_disable_unprepare(mbox->clk); ++ ++ return 0; ++} ++ ++static const struct of_device_id aspeed_mbox_match[] = { ++ { .compatible = "aspeed,ast2400-mbox" }, ++ { .compatible = "aspeed,ast2500-mbox" }, ++ { }, ++}; ++MODULE_DEVICE_TABLE(of, aspeed_mbox_match); ++ ++static struct platform_driver aspeed_mbox_driver = { ++ .driver = { ++ .name = DEVICE_NAME, ++ .of_match_table = aspeed_mbox_match, ++ }, ++ .probe = aspeed_mbox_probe, ++ .remove = aspeed_mbox_remove, ++}; ++ ++module_platform_driver(aspeed_mbox_driver); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Cyril Bur <cyrilbur@gmail.com>"); ++MODULE_DESCRIPTION("Aspeed mailbox device driver"); +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0021-Initial-Port-of-Aspeed-LPC-SIO-driver.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0021-Initial-Port-of-Aspeed-LPC-SIO-driver.patch new file mode 100644 index 000000000..b4b1bcad8 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0021-Initial-Port-of-Aspeed-LPC-SIO-driver.patch @@ -0,0 +1,580 @@ +From 39f76bd49fd481999cf51fbdfbea3e820efc7227 Mon Sep 17 00:00:00 2001 +From: Yong Li <yong.b.li@intel.com> +Date: Mon, 13 Nov 2017 16:29:44 +0800 +Subject: [PATCH] Aspeed LPC SIO driver + +Add lpc sio device driver for AST2500/2400 + +Signed-off-by: Yong Li <yong.b.li@intel.com> +Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +--- + .../bindings/soc/aspeed/aspeed-lpc-sio.txt | 17 + + arch/arm/boot/dts/aspeed-g4.dtsi | 7 + + arch/arm/boot/dts/aspeed-g5.dtsi | 7 + + drivers/soc/aspeed/Kconfig | 7 + + drivers/soc/aspeed/Makefile | 1 + + drivers/soc/aspeed/aspeed-lpc-sio.c | 410 +++++++++++++++++++++ + include/uapi/linux/aspeed-lpc-sio.h | 45 +++ + 7 files changed, 494 insertions(+) + create mode 100644 Documentation/devicetree/bindings/soc/aspeed/aspeed-lpc-sio.txt + create mode 100644 drivers/soc/aspeed/aspeed-lpc-sio.c + create mode 100644 include/uapi/linux/aspeed-lpc-sio.h + +diff --git a/Documentation/devicetree/bindings/soc/aspeed/aspeed-lpc-sio.txt b/Documentation/devicetree/bindings/soc/aspeed/aspeed-lpc-sio.txt +new file mode 100644 +index 000000000000..c74ea3a4e5ac +--- /dev/null ++++ b/Documentation/devicetree/bindings/soc/aspeed/aspeed-lpc-sio.txt +@@ -0,0 +1,17 @@ ++* Aspeed LPC SIO driver. ++ ++Required properties: ++- compatible : Should be one of: ++ "aspeed,ast2400-lpc-sio" ++ "aspeed,ast2500-lpc-sio" ++- reg : Should contain lpc-sio registers location and length ++- clocks: contains a phandle to the syscon node describing the clocks. ++ There should then be one cell representing the clock to use. ++ ++Example: ++lpc_sio: lpc-sio@100 { ++ compatible = "aspeed,ast2500-lpc-sio"; ++ reg = <0x100 0x20>; ++ clocks = <&syscon ASPEED_CLK_GATE_LCLK>; ++ status = "disabled"; ++}; +diff --git a/arch/arm/boot/dts/aspeed-g4.dtsi b/arch/arm/boot/dts/aspeed-g4.dtsi +index f3edda4ae477..a70bee24d058 100644 +--- a/arch/arm/boot/dts/aspeed-g4.dtsi ++++ b/arch/arm/boot/dts/aspeed-g4.dtsi +@@ -394,6 +394,13 @@ + compatible = "aspeed,bmc-misc"; + }; + ++ lpc_sio: lpc-sio@100 { ++ compatible = "aspeed,ast2400-lpc-sio"; ++ reg = <0x100 0x20>; ++ clocks = <&syscon ASPEED_CLK_GATE_LCLK>; ++ status = "disabled"; ++ }; ++ + mbox: mbox@180 { + compatible = "aspeed,ast2400-mbox"; + reg = <0x180 0x5c>; +diff --git a/arch/arm/boot/dts/aspeed-g5.dtsi b/arch/arm/boot/dts/aspeed-g5.dtsi +index bd6d1461e4bd..c15be82c3a9d 100644 +--- a/arch/arm/boot/dts/aspeed-g5.dtsi ++++ b/arch/arm/boot/dts/aspeed-g5.dtsi +@@ -503,6 +503,13 @@ + compatible = "aspeed,bmc-misc"; + }; + ++ lpc_sio: lpc-sio@100 { ++ compatible = "aspeed,ast2500-lpc-sio"; ++ reg = <0x100 0x20>; ++ clocks = <&syscon ASPEED_CLK_GATE_LCLK>; ++ status = "disabled"; ++ }; ++ + mbox: mbox@180 { + compatible = "aspeed,ast2500-mbox"; + reg = <0x180 0x5c>; +diff --git a/drivers/soc/aspeed/Kconfig b/drivers/soc/aspeed/Kconfig +index a4be8e566bc7..285c19042c65 100644 +--- a/drivers/soc/aspeed/Kconfig ++++ b/drivers/soc/aspeed/Kconfig +@@ -28,6 +28,13 @@ config ASPEED_LPC_MBOX + Expose the ASPEED LPC MBOX registers found on Aspeed SOCs (AST2400 + and AST2500) to userspace. + ++config ASPEED_LPC_SIO ++ tristate "Aspeed ast2400/2500 HOST LPC SIO support" ++ depends on SOC_ASPEED && REGMAP && MFD_SYSCON ++ help ++ Provides a driver to control the LPC SIO interface on ASPEED platform ++ through ioctl()s. ++ + config ASPEED_LPC_SNOOP + tristate "Aspeed ast2500 HOST LPC snoop support" + depends on SOC_ASPEED && REGMAP && MFD_SYSCON +diff --git a/drivers/soc/aspeed/Makefile b/drivers/soc/aspeed/Makefile +index f3ff29b874ed..2e547cc47e62 100644 +--- a/drivers/soc/aspeed/Makefile ++++ b/drivers/soc/aspeed/Makefile +@@ -2,5 +2,6 @@ + obj-$(CONFIG_ASPEED_BMC_MISC) += aspeed-bmc-misc.o + obj-$(CONFIG_ASPEED_LPC_CTRL) += aspeed-lpc-ctrl.o + obj-$(CONFIG_ASPEED_LPC_MBOX) += aspeed-lpc-mbox.o ++obj-$(CONFIG_ASPEED_LPC_SIO) += aspeed-lpc-sio.o + obj-$(CONFIG_ASPEED_LPC_SNOOP) += aspeed-lpc-snoop.o + obj-$(CONFIG_ASPEED_P2A_CTRL) += aspeed-p2a-ctrl.o +diff --git a/drivers/soc/aspeed/aspeed-lpc-sio.c b/drivers/soc/aspeed/aspeed-lpc-sio.c +new file mode 100644 +index 000000000000..d4a4da112ff4 +--- /dev/null ++++ b/drivers/soc/aspeed/aspeed-lpc-sio.c +@@ -0,0 +1,410 @@ ++// SPDX-License-Identifier: GPL-2.0 ++// Copyright (C) 2012-2017 ASPEED Technology Inc. ++// Copyright (c) 2017-2020 Intel Corporation ++ ++#include <linux/aspeed-lpc-sio.h> ++#include <linux/clk.h> ++#include <linux/mfd/syscon.h> ++#include <linux/miscdevice.h> ++#include <linux/mm.h> ++#include <linux/module.h> ++#include <linux/of_address.h> ++#include <linux/platform_device.h> ++#include <linux/poll.h> ++#include <linux/regmap.h> ++ ++#define SOC_NAME "aspeed" ++#define DEVICE_NAME "lpc-sio" ++ ++#define AST_LPC_SWCR0300 0x00 ++#define LPC_PWRGD_STS BIT(30) ++#define LPC_PWRGD_RISING_EVT_STS BIT(29) ++#define LPC_PWRGD_FALLING_EVT_STS BIT(28) ++#define LPC_PWRBTN_STS BIT(27) ++#define LPC_PWRBTN_RISING_EVT_STS BIT(26) ++#define LPC_PWRBTN_FALLING_EVT_STS BIT(25) ++#define LPC_S5N_STS BIT(21) ++#define LPC_S5N_RISING_EVT_STS BIT(20) ++#define LPC_S5N_FALLING_EVT_STS BIT(19) ++#define LPC_S3N_STS BIT(18) ++#define LPC_S3N_RISING_EVT_STS BIT(17) ++#define LPC_S3N_FALLING_EVT_STS BIT(16) ++#define LPC_PWBTO_RAW_STS BIT(15) ++#define LPC_LAST_ONCTL_STS BIT(14) ++#define LPC_WAS_PFAIL_STS BIT(13) ++#define LPC_POWER_UP_FAIL_STS BIT(12) /* Crowbar */ ++#define LPC_PWRBTN_OVERRIDE_STS BIT(11) ++#define LPC_BMC_TRIG_WAKEUP_EVT_STS BIT(8) ++ ++#define AST_LPC_SWCR0704 0x04 ++#define LPC_BMC_TRIG_WAKEUP_EVT_EN BIT(8) ++ ++#define AST_LPC_SWCR0B08 0x08 ++#define LPC_PWREQ_OUTPUT_LEVEL BIT(25) ++#define LPC_PWBTO_OUTPUT_LEVEL BIT(24) ++#define LPC_ONCTL_STS BIT(15) ++#define LPC_ONCTL_GPIO_LEVEL BIT(14) ++#define LPC_ONCTL_EN_GPIO_OUTPUT BIT(13) ++#define LPC_ONCTL_EN_GPIO_MODE BIT(12) ++#define LPC_BMC_TRIG_WAKEUP_EVT BIT(6) ++ ++#define AST_LPC_SWCR0F0C 0x0C ++#define AST_LPC_SWCR1310 0x10 ++#define AST_LPC_SWCR1714 0x14 ++#define AST_LPC_SWCR1B18 0x18 ++#define AST_LPC_SWCR1F1C 0x1C ++#define AST_LPC_ACPIE3E0 0x20 ++#define AST_LPC_ACPIC1C0 0x24 ++ ++#define AST_LPC_ACPIB3B0 0x28 ++#define LPC_BMC_TRIG_SCI_EVT_STS BIT(8) ++ ++#define AST_LPC_ACPIB7B4 0x2C ++#define LPC_BMC_TRIG_SCI_EVT_EN BIT(8) ++ ++struct aspeed_lpc_sio { ++ struct miscdevice miscdev; ++ struct regmap *regmap; ++ struct clk *clk; ++ struct semaphore lock; ++ unsigned int reg_base; ++}; ++ ++static struct aspeed_lpc_sio *file_aspeed_lpc_sio(struct file *file) ++{ ++ return container_of(file->private_data, struct aspeed_lpc_sio, ++ miscdev); ++} ++ ++static int aspeed_lpc_sio_open(struct inode *inode, struct file *filp) ++{ ++ return 0; ++} ++ ++#define LPC_SLP3N5N_EVENT_STATUS (\ ++ LPC_S5N_RISING_EVT_STS | \ ++ LPC_S5N_FALLING_EVT_STS | \ ++ LPC_S3N_RISING_EVT_STS | \ ++ LPC_S3N_FALLING_EVT_STS) ++ ++/* ++ * SLPS3n SLPS5n State ++ * --------------------------------- ++ * 1 1 S12 ++ * 0 1 S3I ++ * x 0 S45 ++ ************************************* ++ */ ++ ++static void sio_get_acpi_state(struct aspeed_lpc_sio *lpc_sio, ++ struct sio_ioctl_data *sio_data) ++{ ++ u32 reg, val; ++ ++ reg = lpc_sio->reg_base + AST_LPC_SWCR0300; ++ regmap_read(lpc_sio->regmap, reg, &val); ++ ++ /* update the ACPI state event status */ ++ if (sio_data->param != 0) { ++ if (val & LPC_SLP3N5N_EVENT_STATUS) { ++ sio_data->param = 1; ++ regmap_write(lpc_sio->regmap, reg, ++ LPC_SLP3N5N_EVENT_STATUS); ++ } else { ++ sio_data->param = 0; ++ } ++ } ++ ++ if ((val & LPC_S3N_STS) && (val & LPC_S5N_STS)) ++ sio_data->data = ACPI_STATE_S12; ++ else if ((val & LPC_S3N_STS) == 0 && (val & LPC_S5N_STS)) ++ sio_data->data = ACPI_STATE_S3I; ++ else ++ sio_data->data = ACPI_STATE_S45; ++} ++ ++#define LPC_PWRGD_EVENT_STATUS ( \ ++ LPC_PWRGD_RISING_EVT_STS | \ ++ LPC_PWRGD_FALLING_EVT_STS) ++ ++static void sio_get_pwrgd_status(struct aspeed_lpc_sio *lpc_sio, ++ struct sio_ioctl_data *sio_data) ++{ ++ u32 reg, val; ++ ++ reg = lpc_sio->reg_base + AST_LPC_SWCR0300; ++ regmap_read(lpc_sio->regmap, reg, &val); ++ ++ /* update the PWRGD event status */ ++ if (sio_data->param != 0) { ++ if (val & LPC_PWRGD_EVENT_STATUS) { ++ sio_data->param = 1; ++ regmap_write(lpc_sio->regmap, reg, ++ LPC_PWRGD_EVENT_STATUS); ++ } else { ++ sio_data->param = 0; ++ } ++ } ++ ++ sio_data->data = (val & LPC_PWRGD_STS) != 0 ? 1 : 0; ++} ++ ++static void sio_get_onctl_status(struct aspeed_lpc_sio *lpc_sio, ++ struct sio_ioctl_data *sio_data) ++{ ++ u32 reg, val; ++ ++ reg = lpc_sio->reg_base + AST_LPC_SWCR0B08; ++ regmap_read(lpc_sio->regmap, reg, &val); ++ ++ sio_data->data = (val & LPC_ONCTL_STS) != 0 ? 1 : 0; ++} ++ ++static void sio_set_onctl_gpio(struct aspeed_lpc_sio *lpc_sio, ++ struct sio_ioctl_data *sio_data) ++{ ++ u32 reg, val; ++ ++ reg = lpc_sio->reg_base + AST_LPC_SWCR0B08; ++ regmap_read(lpc_sio->regmap, reg, &val); ++ ++ /* Enable ONCTL GPIO mode */ ++ if (sio_data->param != 0) { ++ val |= LPC_ONCTL_EN_GPIO_MODE; ++ val |= LPC_ONCTL_EN_GPIO_OUTPUT; ++ ++ if (sio_data->data != 0) ++ val |= LPC_ONCTL_GPIO_LEVEL; ++ else ++ val &= ~LPC_ONCTL_GPIO_LEVEL; ++ ++ regmap_write(lpc_sio->regmap, reg, val); ++ } else { ++ val &= ~LPC_ONCTL_EN_GPIO_MODE; ++ regmap_write(lpc_sio->regmap, reg, val); ++ } ++} ++ ++static void sio_get_pwrbtn_override(struct aspeed_lpc_sio *lpc_sio, ++ struct sio_ioctl_data *sio_data) ++{ ++ u32 reg, val; ++ ++ reg = lpc_sio->reg_base + AST_LPC_SWCR0300; ++ regmap_read(lpc_sio->regmap, reg, &val); ++ ++ /* clear the PWRBTN OVERRIDE status */ ++ if (sio_data->param != 0 && val & LPC_PWRBTN_OVERRIDE_STS) ++ regmap_write(lpc_sio->regmap, reg, LPC_PWRBTN_OVERRIDE_STS); ++ ++ sio_data->data = (val & LPC_PWRBTN_OVERRIDE_STS) != 0 ? 1 : 0; ++} ++ ++static void sio_get_pfail_status(struct aspeed_lpc_sio *lpc_sio, ++ struct sio_ioctl_data *sio_data) ++{ ++ u32 reg, val; ++ ++ reg = lpc_sio->reg_base + AST_LPC_SWCR0300; ++ regmap_read(lpc_sio->regmap, reg, &val); ++ ++ /* [ASPEED]: SWCR_03_00[13] (Was_pfail: default 1) is used to identify ++ * this current booting is from AC loss (not DC loss) if FW cleans this ++ * bit after booting successfully every time. ++ **********************************************************************/ ++ if (val & LPC_WAS_PFAIL_STS) { ++ regmap_write(lpc_sio->regmap, reg, 0); /* W0C */ ++ sio_data->data = 1; ++ } else { ++ sio_data->data = 0; ++ } ++} ++ ++static void sio_set_bmc_sci_event(struct aspeed_lpc_sio *lpc_sio, ++ struct sio_ioctl_data *sio_data) ++{ ++ u32 reg; ++ ++ if (sio_data->param) { ++ reg = lpc_sio->reg_base + AST_LPC_ACPIB7B4; ++ regmap_write_bits(lpc_sio->regmap, reg, ++ LPC_BMC_TRIG_SCI_EVT_EN, ++ LPC_BMC_TRIG_SCI_EVT_EN); ++ ++ reg = lpc_sio->reg_base + AST_LPC_SWCR0704; ++ regmap_write_bits(lpc_sio->regmap, reg, ++ LPC_BMC_TRIG_WAKEUP_EVT_EN, ++ LPC_BMC_TRIG_WAKEUP_EVT_EN); ++ ++ reg = lpc_sio->reg_base + AST_LPC_SWCR0B08; ++ regmap_write_bits(lpc_sio->regmap, reg, ++ LPC_BMC_TRIG_WAKEUP_EVT, ++ LPC_BMC_TRIG_WAKEUP_EVT); ++ } else { ++ reg = lpc_sio->reg_base + AST_LPC_SWCR0300; ++ regmap_write_bits(lpc_sio->regmap, reg, ++ LPC_BMC_TRIG_WAKEUP_EVT_STS, ++ LPC_BMC_TRIG_WAKEUP_EVT_STS); ++ ++ reg = lpc_sio->reg_base + AST_LPC_ACPIB3B0; ++ regmap_write_bits(lpc_sio->regmap, reg, ++ LPC_BMC_TRIG_SCI_EVT_STS, ++ LPC_BMC_TRIG_SCI_EVT_STS); ++ } ++ ++ sio_data->data = sio_data->param; ++} ++ ++typedef void (*sio_cmd_fn) (struct aspeed_lpc_sio *sio_dev, ++ struct sio_ioctl_data *sio_data); ++ ++static sio_cmd_fn sio_cmd_handle[SIO_MAX_CMD] = { ++ [SIO_GET_ACPI_STATE] = sio_get_acpi_state, ++ [SIO_GET_PWRGD_STATUS] = sio_get_pwrgd_status, ++ [SIO_GET_ONCTL_STATUS] = sio_get_onctl_status, ++ [SIO_SET_ONCTL_GPIO] = sio_set_onctl_gpio, ++ [SIO_GET_PWRBTN_OVERRIDE] = sio_get_pwrbtn_override, ++ [SIO_GET_PFAIL_STATUS] = sio_get_pfail_status, ++ [SIO_SET_BMC_SCI_EVENT] = sio_set_bmc_sci_event, ++}; ++ ++static long aspeed_lpc_sio_ioctl(struct file *file, unsigned int cmd, ++ unsigned long param) ++{ ++ struct aspeed_lpc_sio *lpc_sio = file_aspeed_lpc_sio(file); ++ struct sio_ioctl_data sio_data; ++ sio_cmd_fn cmd_fn; ++ long ret; ++ ++ if (copy_from_user(&sio_data, (void __user *)param, sizeof(sio_data))) ++ return -EFAULT; ++ ++ if (cmd != SIO_IOC_COMMAND || sio_data.sio_cmd >= SIO_MAX_CMD) ++ return -EINVAL; ++ ++ cmd_fn = sio_cmd_handle[sio_data.sio_cmd]; ++ if (!cmd_fn) ++ return -EINVAL; ++ ++ if (down_interruptible(&lpc_sio->lock) != 0) ++ return -ERESTARTSYS; ++ ++ cmd_fn(lpc_sio, &sio_data); ++ ret = copy_to_user((void __user *)param, &sio_data, sizeof(sio_data)); ++ ++ up(&lpc_sio->lock); ++ ++ return ret; ++} ++ ++static const struct file_operations aspeed_lpc_sio_fops = { ++ .owner = THIS_MODULE, ++ .open = aspeed_lpc_sio_open, ++ .unlocked_ioctl = aspeed_lpc_sio_ioctl, ++}; ++ ++static int aspeed_lpc_sio_probe(struct platform_device *pdev) ++{ ++ struct aspeed_lpc_sio *lpc_sio; ++ struct device *dev; ++ u32 val; ++ int ret; ++ ++ dev = &pdev->dev; ++ ++ lpc_sio = devm_kzalloc(dev, sizeof(*lpc_sio), GFP_KERNEL); ++ if (!lpc_sio) ++ return -ENOMEM; ++ ++ dev_set_drvdata(&pdev->dev, lpc_sio); ++ ++ ret = of_property_read_u32(dev->of_node, "reg", &lpc_sio->reg_base); ++ if (ret) { ++ dev_err(dev, "Couldn't read reg device-tree property\n"); ++ return ret; ++ } ++ ++ lpc_sio->regmap = syscon_node_to_regmap(pdev->dev.parent->of_node); ++ if (IS_ERR(lpc_sio->regmap)) { ++ dev_err(dev, "Couldn't get regmap\n"); ++ return -ENODEV; ++ } ++ ++ /* ++ * We check that the regmap works on this very first access, ++ * but as this is an MMIO-backed regmap, subsequent regmap ++ * access is not going to fail and we skip error checks from ++ * this point. ++ */ ++ ret = regmap_read(lpc_sio->regmap, AST_LPC_SWCR0300, &val); ++ if (ret) { ++ dev_err(dev, "failed to read regmap\n"); ++ return ret; ++ } ++ ++ sema_init(&lpc_sio->lock, 1); ++ ++ lpc_sio->clk = devm_clk_get(dev, NULL); ++ if (IS_ERR(lpc_sio->clk)) { ++ ret = PTR_ERR(lpc_sio->clk); ++ if (ret != -EPROBE_DEFER) ++ dev_err(dev, "couldn't get clock\n"); ++ return ret; ++ } ++ ret = clk_prepare_enable(lpc_sio->clk); ++ if (ret) { ++ dev_err(dev, "couldn't enable clock\n"); ++ return ret; ++ } ++ ++ lpc_sio->miscdev.minor = MISC_DYNAMIC_MINOR; ++ lpc_sio->miscdev.name = DEVICE_NAME; ++ lpc_sio->miscdev.fops = &aspeed_lpc_sio_fops; ++ lpc_sio->miscdev.parent = dev; ++ ret = misc_register(&lpc_sio->miscdev); ++ if (ret) { ++ dev_err(dev, "Unable to register device\n"); ++ goto err; ++ } ++ ++ dev_info(dev, "Loaded at %pap (0x%08x)\n", &lpc_sio->regmap, ++ lpc_sio->reg_base); ++ ++ return 0; ++ ++err: ++ clk_disable_unprepare(lpc_sio->clk); ++ ++ return ret; ++} ++ ++static int aspeed_lpc_sio_remove(struct platform_device *pdev) ++{ ++ struct aspeed_lpc_sio *lpc_sio = dev_get_drvdata(&pdev->dev); ++ ++ misc_deregister(&lpc_sio->miscdev); ++ clk_disable_unprepare(lpc_sio->clk); ++ ++ return 0; ++} ++ ++static const struct of_device_id aspeed_lpc_sio_match[] = { ++ { .compatible = "aspeed,ast2500-lpc-sio" }, ++ { }, ++}; ++MODULE_DEVICE_TABLE(of, aspeed_lpc_sio_match); ++ ++static struct platform_driver aspeed_lpc_sio_driver = { ++ .driver = { ++ .name = SOC_NAME "-" DEVICE_NAME, ++ .of_match_table = of_match_ptr(aspeed_lpc_sio_match), ++ }, ++ .probe = aspeed_lpc_sio_probe, ++ .remove = aspeed_lpc_sio_remove, ++}; ++module_platform_driver(aspeed_lpc_sio_driver); ++ ++MODULE_LICENSE("GPL v2"); ++MODULE_AUTHOR("Ryan Chen <ryan_chen@aspeedtech.com>"); ++MODULE_AUTHOR("Yong Li <yong.blli@linux.intel.com>"); ++MODULE_DESCRIPTION("ASPEED AST LPC SIO device driver"); +diff --git a/include/uapi/linux/aspeed-lpc-sio.h b/include/uapi/linux/aspeed-lpc-sio.h +new file mode 100644 +index 000000000000..acf89a7d2b4a +--- /dev/null ++++ b/include/uapi/linux/aspeed-lpc-sio.h +@@ -0,0 +1,45 @@ ++/* ++ * Copyright (C) 2012-2020 ASPEED Technology Inc. ++ * Copyright (c) 2017 Intel Corporation ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version ++ * 2 of the License, or (at your option) any later version. ++ * ++ */ ++ ++#ifndef _UAPI_LINUX_ASPEED_LPC_SIO_H ++#define _UAPI_LINUX_ASPEED_LPC_SIO_H ++ ++#include <linux/ioctl.h> ++ ++enum ACPI_SLP_STATE { ++ ACPI_STATE_S12 = 1, ++ ACPI_STATE_S3I, ++ ACPI_STATE_S45 ++}; ++ ++/* SWC & ACPI for SuperIO IOCTL */ ++enum SIO_CMD { ++ SIO_GET_ACPI_STATE = 0, ++ SIO_GET_PWRGD_STATUS, ++ SIO_GET_ONCTL_STATUS, ++ SIO_SET_ONCTL_GPIO, ++ SIO_GET_PWRBTN_OVERRIDE, ++ SIO_GET_PFAIL_STATUS, /* Start from AC Loss */ ++ SIO_SET_BMC_SCI_EVENT, ++ ++ SIO_MAX_CMD ++}; ++ ++struct sio_ioctl_data { ++ unsigned short sio_cmd; ++ unsigned short param; ++ unsigned int data; ++}; ++ ++#define SIO_IOC_BASE 'P' ++#define SIO_IOC_COMMAND _IOWR(SIO_IOC_BASE, 1, struct sio_ioctl_data) ++ ++#endif /* _UAPI_LINUX_ASPEED_LPC_SIO_H */ +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0022-Add-AST2500-eSPI-driver.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0022-Add-AST2500-eSPI-driver.patch new file mode 100644 index 000000000..c409dec50 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0022-Add-AST2500-eSPI-driver.patch @@ -0,0 +1,762 @@ +From e6e186acacc16fd95251b2d0fb024993a66ffa0a Mon Sep 17 00:00:00 2001 +From: Haiyue Wang <haiyue.wang@linux.intel.com> +Date: Sat, 24 Feb 2018 11:12:32 +0800 +Subject: [PATCH] Add AST2500 eSPI driver + +When PCH works under eSPI mode, the PMC (Power Management Controller) in +PCH is waiting for SUS_ACK from BMC after it alerts SUS_WARN. It is in +dead loop if no SUS_ACK assert. This is the basic requirement for the BMC +works as eSPI slave. + +Also for the host power on / off actions, from BMC side, the following VW +(Virtual Wire) messages are done in firmware: +1. SLAVE_BOOT_LOAD_DONE / SLAVE_BOOT_LOAD_STATUS +2. SUS_ACK +3. OOB_RESET_ACK +4. HOST_RESET_ACK + +Also, it provides monitoring interface of PLTRST_N signal through +/dev/espi-pltrstn + +Signed-off-by: Haiyue Wang <haiyue.wang@linux.intel.com> +Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +Signed-off-by: James Feist <james.feist@linux.intel.com> +Signed-off-by: Vernon Mauery <vernon.mauery@intel.com> +--- + .../devicetree/bindings/misc/aspeed,espi-slave.txt | 20 + + Documentation/misc-devices/espi-slave.rst | 118 ++++++ + arch/arm/boot/dts/aspeed-g5.dtsi | 4 + + arch/arm/boot/dts/aspeed-g6.dtsi | 14 + + drivers/misc/Kconfig | 8 + + drivers/misc/Makefile | 1 + + drivers/misc/aspeed-espi-slave.c | 468 +++++++++++++++++++++ + include/dt-bindings/clock/ast2600-clock.h | 1 + + 8 files changed, 634 insertions(+) + create mode 100644 Documentation/devicetree/bindings/misc/aspeed,espi-slave.txt + create mode 100644 Documentation/misc-devices/espi-slave.rst + create mode 100644 drivers/misc/aspeed-espi-slave.c + +diff --git a/Documentation/devicetree/bindings/misc/aspeed,espi-slave.txt b/Documentation/devicetree/bindings/misc/aspeed,espi-slave.txt +new file mode 100644 +index 000000000000..f72d9ae32f3e +--- /dev/null ++++ b/Documentation/devicetree/bindings/misc/aspeed,espi-slave.txt +@@ -0,0 +1,20 @@ ++ASPEED eSPI Slave Controller ++ ++Required properties: ++ - compatible: must be one of: ++ - "aspeed,ast2500-espi-slave" ++ - "aspeed,ast2600-espi-slave" ++ ++ - reg: physical base address of the controller and length of memory mapped ++ region ++ ++ - interrupts: interrupt generated by the controller ++ ++Example: ++ ++ espi: espi@1e6ee000 { ++ compatible = "aspeed,ast2500-espi-slave"; ++ reg = <0x1e6ee000 0x100>; ++ interrupts = <23>; ++ status = "disabled"; ++}; +diff --git a/Documentation/misc-devices/espi-slave.rst b/Documentation/misc-devices/espi-slave.rst +new file mode 100644 +index 000000000000..887a69a7130a +--- /dev/null ++++ b/Documentation/misc-devices/espi-slave.rst +@@ -0,0 +1,118 @@ ++.. SPDX-License-Identifier: GPL-2.0 ++ ++========== ++eSPI Slave ++========== ++ ++:Author: Haiyue Wang <haiyue.wang@linux.intel.com> ++ ++The PCH (**eSPI master**) provides the eSPI to support connection of a ++BMC (**eSPI slave**) to the platform. ++ ++The LPC and eSPI interfaces are mutually exclusive. Both use the same ++pins, but on power-up, a HW strap determines if the eSPI or the LPC bus ++is operational. Once selected, it’s not possible to change to the other ++interface. ++ ++``eSPI Channels and Supported Transactions`` ++ +------+---------------------+----------------------+--------------------+ ++ | CH # | Channel | Posted Cycles | Non-Posted Cycles | ++ +======+=====================+======================+====================+ ++ | 0 | Peripheral | Memory Write, | Memory Read, | ++ | | | Completions | I/O Read/Write | ++ +------+---------------------+----------------------+--------------------+ ++ | 1 | Virtual Wire | Virtual Wire GET/PUT | N/A | ++ +------+---------------------+----------------------+--------------------+ ++ | 2 | Out-of-Band Message | SMBus Packet GET/PUT | N/A | ++ +------+---------------------+----------------------+--------------------+ ++ | 3 | Flash Access | N/A | Flash Read, Write, | ++ | | | | Erase | ++ +------+---------------------+----------------------+--------------------+ ++ | N/A | General | Register Accesses | N/A | ++ +------+---------------------+----------------------+--------------------+ ++ ++Virtual Wire Channel (Channel 1) Overview ++----------------------------------------- ++ ++The Virtual Wire channel uses a standard message format to communicate ++several types of signals between the components on the platform:: ++ ++ - Sideband and GPIO Pins: System events and other dedicated signals ++ between the PCH and eSPI slave. These signals are tunneled between the ++ two components over eSPI. ++ ++ - Serial IRQ Interrupts: Interrupts are tunneled from the eSPI slave to ++ the PCH. Both edge and triggered interrupts are supported. ++ ++When PCH runs on eSPI mode, from BMC side, the following VW messages are ++done in firmware:: ++ ++ 1. SLAVE_BOOT_LOAD_DONE / SLAVE_BOOT_LOAD_STATUS ++ 2. SUS_ACK ++ 3. OOB_RESET_ACK ++ 4. HOST_RESET_ACK ++ ++``eSPI Virtual Wires (VW)`` ++ +----------------------+---------+---------------------------------------+ ++ |Virtual Wire |PCH Pin |Comments | ++ | |Direction| | ++ +======================+=========+=======================================+ ++ |SUS_WARN# |Output |PCH pin is a GPIO when eSPI is enabled.| ++ | | |eSPI controller receives as VW message.| ++ +----------------------+---------+---------------------------------------+ ++ |SUS_ACK# |Input |PCH pin is a GPIO when eSPI is enabled.| ++ | | |eSPI controller receives as VW message.| ++ +----------------------+---------+---------------------------------------+ ++ |SLAVE_BOOT_LOAD_DONE |Input |Sent when the BMC has completed its | ++ | | |boot process as an indication to | ++ | | |eSPI-MC to continue with the G3 to S0 | ++ | | |exit. | ++ | | |The eSPI Master waits for the assertion| ++ | | |of this virtual wire before proceeding | ++ | | |with the SLP_S5# deassertion. | ++ | | |The intent is that it is never changed | ++ | | |except on a G3 exit - it is reset on a | ++ | | |G3 entry. | ++ +----------------------+---------+---------------------------------------+ ++ |SLAVE_BOOT_LOAD_STATUS|Input |Sent upon completion of the Slave Boot | ++ | | |Load from the attached flash. A stat of| ++ | | |1 indicates that the boot code load was| ++ | | |successful and that the integrity of | ++ | | |the image is intact. | ++ +----------------------+---------+---------------------------------------+ ++ |HOST_RESET_WARN |Output |Sent from the MC just before the Host | ++ | | |is about to enter reset. Upon receiving| ++ | | |, the BMC must flush and quiesce its | ++ | | |upstream Peripheral Channel request | ++ | | |queues and assert HOST_RESET_ACK VWire.| ++ | | |The MC subsequently completes any | ++ | | |outstanding posted transactions or | ++ | | |completions and then disables the | ++ | | |Peripheral Channel via a write to | ++ | | |the Slave's Configuration Register. | ++ +----------------------+---------+---------------------------------------+ ++ |HOST_RESET_ACK |Input |ACK for the HOST_RESET_WARN message | ++ +----------------------+---------+---------------------------------------+ ++ |OOB_RESET_WARN |Output |Sent from the MC just before the OOB | ++ | | |processor is about to enter reset. Upon| ++ | | |receiving, the BMC must flush and | ++ | | |quiesce its OOB Channel upstream | ++ | | |request queues and assert OOB_RESET_ACK| ++ | | |VWire. The-MC subsequently completes | ++ | | |any outstanding posted transactions or | ++ | | |completions and then disables the OOB | ++ | | |Channel via a write to the Slave's | ++ | | |Configuration Register. | ++ +----------------------+---------+---------------------------------------+ ++ |OOB_RESET_ACK |Input |ACK for OOB_RESET_WARN message | ++ +----------------------+---------+---------------------------------------+ ++ ++`Intel C620 Series Chipset Platform Controller Hub ++<https://www.intel.com/content/www/us/en/chipsets/c620-series-chipset-datasheet.html>`_ ++ ++ -- 17. Enhanced Serial Peripheral Interface ++ ++ ++`Enhanced Serial Peripheral Interface (eSPI) ++- Interface Base Specification (for Client and Server Platforms) ++<https://www.intel.com/content/dam/support/us/en/documents/software/chipset-software/327432-004_espi_base_specification_rev1.0.pdf>`_ +diff --git a/arch/arm/boot/dts/aspeed-g5.dtsi b/arch/arm/boot/dts/aspeed-g5.dtsi +index 26d885d2bd8a..7b798c49cb95 100644 +--- a/arch/arm/boot/dts/aspeed-g5.dtsi ++++ b/arch/arm/boot/dts/aspeed-g5.dtsi +@@ -336,6 +336,7 @@ + clocks = <&syscon ASPEED_CLK_APB>; + interrupt-controller; + #interrupt-cells = <2>; ++ status = "disabled"; + }; + + sgpio: sgpio@1e780200 { +@@ -432,6 +433,9 @@ + reg = <0x1e6ee000 0x100>; + interrupts = <23>; + status = "disabled"; ++ clocks = <&syscon ASPEED_CLK_GATE_ESPICLK>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_espi_default>; + }; + + lpc: lpc@1e789000 { +diff --git a/arch/arm/boot/dts/aspeed-g6.dtsi b/arch/arm/boot/dts/aspeed-g6.dtsi +index 825e64ce317a..315d35c15208 100644 +--- a/arch/arm/boot/dts/aspeed-g6.dtsi ++++ b/arch/arm/boot/dts/aspeed-g6.dtsi +@@ -4,6 +4,7 @@ + #include <dt-bindings/interrupt-controller/arm-gic.h> + #include <dt-bindings/clock/ast2600-clock.h> + #include <dt-bindings/interrupt-controller/aspeed-scu-ic.h> ++#include <dt-bindings/gpio/aspeed-gpio.h> + + / { + model = "Aspeed BMC"; +@@ -708,6 +709,19 @@ + status = "disabled"; + }; + ++ espi: espi@1e6ee000 { ++ compatible = "aspeed,ast2600-espi-slave"; ++ reg = <0x1e6ee000 0x200>; ++ interrupts-extended = <&gic GIC_SPI 42 IRQ_TYPE_LEVEL_HIGH>, ++ <&gpio0 ASPEED_GPIO(W, 7) IRQ_TYPE_EDGE_FALLING>; ++ status = "disabled"; ++ clocks = <&syscon ASPEED_CLK_GATE_ESPICLK>; ++ resets = <&syscon ASPEED_RESET_ESPI>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_espi_default>, ++ <&pinctrl_espialt_default>; ++ }; ++ + i2c: bus@1e78a000 { + compatible = "simple-bus"; + #address-cells = <1>; +diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig +index 06a2b753cc7c..0f9b5a356c93 100644 +--- a/drivers/misc/Kconfig ++++ b/drivers/misc/Kconfig +@@ -433,6 +433,14 @@ config VEXPRESS_SYSCFG + bus. System Configuration interface is one of the possible means + of generating transactions on this bus. + ++config ASPEED_ESPI_SLAVE ++ depends on ARCH_ASPEED || COMPILE_TEST ++ depends on REGMAP_MMIO ++ tristate "Aspeed ast2500 eSPI slave device driver" ++ ---help--- ++ Control Aspeed ast2500 eSPI slave controller to handle event ++ which needs the firmware's processing. ++ + config PCI_ENDPOINT_TEST + depends on PCI + select CRC32 +diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile +index b9e6d4c3e906..53864687e8fd 100644 +--- a/drivers/misc/Makefile ++++ b/drivers/misc/Makefile +@@ -51,6 +51,7 @@ obj-$(CONFIG_GENWQE) += genwqe/ + obj-$(CONFIG_ECHO) += echo/ + obj-$(CONFIG_VEXPRESS_SYSCFG) += vexpress-syscfg.o + obj-$(CONFIG_CXL_BASE) += cxl/ ++obj-$(CONFIG_ASPEED_ESPI_SLAVE) += aspeed-espi-slave.o + obj-$(CONFIG_PCI_ENDPOINT_TEST) += pci_endpoint_test.o + obj-$(CONFIG_OCXL) += ocxl/ + obj-y += cardreader/ +diff --git a/drivers/misc/aspeed-espi-slave.c b/drivers/misc/aspeed-espi-slave.c +new file mode 100644 +index 000000000000..87bc81948694 +--- /dev/null ++++ b/drivers/misc/aspeed-espi-slave.c +@@ -0,0 +1,468 @@ ++// SPDX-License-Identifier: GPL-2.0 ++// Copyright (c) 2015-2019, Intel Corporation. ++ ++#include <linux/clk.h> ++#include <linux/fs.h> ++#include <linux/interrupt.h> ++#include <linux/io.h> ++#include <linux/miscdevice.h> ++#include <linux/module.h> ++#include <linux/of.h> ++#include <linux/platform_device.h> ++#include <linux/regmap.h> ++#include <linux/reset.h> ++#include <linux/sched/signal.h> ++#include <linux/spinlock.h> ++#include <linux/uaccess.h> ++ ++#define ASPEED_ESPI_CTRL 0x00 ++#define ASPEED_ESPI_CTRL_SW_RESET GENMASK(31, 24) ++#define ASPEED_ESPI_CTRL_OOB_CHRDY BIT(4) ++#define ASPEED_ESPI_INT_STS 0x08 ++#define ASPEED_ESPI_HW_RESET BIT(31) ++#define ASPEED_ESPI_VW_SYSEVT1 BIT(22) ++#define ASPEED_ESPI_VW_SYSEVT BIT(8) ++#define ASPEED_ESPI_INT_EN 0x0C ++#define ASPEED_ESPI_DATA_PORT 0x28 ++#define ASPEED_ESPI_SYSEVT_INT_EN 0x94 ++#define ASPEED_ESPI_SYSEVT 0x98 ++#define ASPEED_ESPI_SYSEVT_HOST_RST_ACK BIT(27) ++#define ASPEED_ESPI_SYSEVT_SLAVE_BOOT_STATUS BIT(23) ++#define ASPEED_ESPI_SYSEVT_SLAVE_BOOT_DONE BIT(20) ++#define ASPEED_ESPI_SYSEVT_OOB_RST_ACK BIT(16) ++#define ASPEED_ESPI_SYSEVT_INT_T0 0x110 ++#define ASPEED_ESPI_SYSEVT_INT_T1 0x114 ++#define ASPEED_ESPI_SYSEVT_INT_T2 0x118 ++#define ASPEED_ESPI_SYSEVT_INT_STS 0x11C ++#define ASPEED_ESPI_SYSEVT_HOST_RST_WARN BIT(8) ++#define ASPEED_ESPI_SYSEVT_OOB_RST_WARN BIT(6) ++#define ASPEED_ESPI_SYSEVT_PLTRSTN BIT(5) ++#define ASPEED_ESPI_SYSEVT1_INT_EN 0x100 ++#define ASPEED_ESPI_SYSEVT1 0x104 ++#define ASPEED_ESPI_SYSEVT1_SUS_ACK BIT(20) ++#define ASPEED_ESPI_SYSEVT1_INT_T0 0x120 ++#define ASPEED_ESPI_SYSEVT1_INT_T1 0x124 ++#define ASPEED_ESPI_SYSEVT1_INT_T2 0x128 ++#define ASPEED_ESPI_SYSEVT1_INT_STS 0x12C ++#define ASPEED_ESPI_SYSEVT1_SUS_WARN BIT(0) ++ ++#define ASPEED_ESPI_INT_MASK \ ++ (ASPEED_ESPI_HW_RESET | \ ++ ASPEED_ESPI_VW_SYSEVT1 | \ ++ ASPEED_ESPI_VW_SYSEVT) ++ ++/* ++ * Setup Interrupt Type / Enable of System Event from Master ++ * T2 T1 T0 ++ * 1) HOST_RST_WARN : Dual Edge 1 0 0 ++ * 2) OOB_RST_WARN : Dual Edge 1 0 0 ++ * 3) PLTRSTN : Dual Edge 1 0 0 ++ */ ++#define ASPEED_ESPI_SYSEVT_INT_T0_MASK 0 ++#define ASPEED_ESPI_SYSEVT_INT_T1_MASK 0 ++#define ASPEED_ESPI_SYSEVT_INT_T2_MASK \ ++ (ASPEED_ESPI_SYSEVT_HOST_RST_WARN | \ ++ ASPEED_ESPI_SYSEVT_OOB_RST_WARN | \ ++ ASPEED_ESPI_SYSEVT_PLTRSTN) ++#define ASPEED_ESPI_SYSEVT_INT_MASK \ ++ (ASPEED_ESPI_SYSEVT_INT_T0_MASK | \ ++ ASPEED_ESPI_SYSEVT_INT_T1_MASK | \ ++ ASPEED_ESPI_SYSEVT_INT_T2_MASK) ++ ++/* ++ * Setup Interrupt Type / Enable of System Event 1 from Master ++ * T2 T1 T0 ++ * 1) SUS_WARN : Rising Edge 0 0 1 ++ */ ++#define ASPEED_ESPI_SYSEVT1_INT_T0_MASK ASPEED_ESPI_SYSEVT1_SUS_WARN ++#define ASPEED_ESPI_SYSEVT1_INT_T1_MASK 0 ++#define ASPEED_ESPI_SYSEVT1_INT_T2_MASK 0 ++#define ASPEED_ESPI_SYSEVT1_INT_MASK \ ++ (ASPEED_ESPI_SYSEVT1_INT_T0_MASK | \ ++ ASPEED_ESPI_SYSEVT1_INT_T1_MASK | \ ++ ASPEED_ESPI_SYSEVT1_INT_T2_MASK) ++ ++struct aspeed_espi { ++ struct regmap *map; ++ struct clk *clk; ++ struct device *dev; ++ struct reset_control *reset; ++ int irq; ++ int rst_irq; ++ ++ /* for PLTRST_N signal monitoring interface */ ++ struct miscdevice pltrstn_miscdev; ++ spinlock_t pltrstn_lock; /* for PLTRST_N signal sampling */ ++ wait_queue_head_t pltrstn_waitq; ++ char pltrstn; ++}; ++ ++static void aspeed_espi_sys_event(struct aspeed_espi *priv) ++{ ++ u32 sts, evt; ++ ++ regmap_read(priv->map, ASPEED_ESPI_SYSEVT_INT_STS, &sts); ++ regmap_read(priv->map, ASPEED_ESPI_SYSEVT, &evt); ++ ++ dev_dbg(priv->dev, "sys: sts = %08x, evt = %08x\n", sts, evt); ++ ++ if (!(evt & ASPEED_ESPI_SYSEVT_SLAVE_BOOT_STATUS)) { ++ regmap_write(priv->map, ASPEED_ESPI_SYSEVT, ++ evt | ASPEED_ESPI_SYSEVT_SLAVE_BOOT_STATUS | ++ ASPEED_ESPI_SYSEVT_SLAVE_BOOT_DONE); ++ dev_dbg(priv->dev, "Setting espi slave boot done\n"); ++ } ++ if (sts & ASPEED_ESPI_SYSEVT_HOST_RST_WARN && ++ evt & ASPEED_ESPI_SYSEVT_HOST_RST_WARN) { ++ regmap_write_bits(priv->map, ASPEED_ESPI_SYSEVT, ++ ASPEED_ESPI_SYSEVT_HOST_RST_ACK, ++ ASPEED_ESPI_SYSEVT_HOST_RST_ACK); ++ dev_dbg(priv->dev, "SYSEVT_HOST_RST_WARN: acked\n"); ++ } ++ if (sts & ASPEED_ESPI_SYSEVT_OOB_RST_WARN && ++ evt & ASPEED_ESPI_SYSEVT_OOB_RST_WARN) { ++ regmap_write_bits(priv->map, ASPEED_ESPI_SYSEVT, ++ ASPEED_ESPI_SYSEVT_OOB_RST_ACK, ++ ASPEED_ESPI_SYSEVT_OOB_RST_ACK); ++ dev_dbg(priv->dev, "SYSEVT_OOB_RST_WARN: acked\n"); ++ } ++ if (sts & ASPEED_ESPI_SYSEVT_PLTRSTN || priv->pltrstn == 'U') { ++ priv->pltrstn = (evt & ASPEED_ESPI_SYSEVT_PLTRSTN) ? '1' : '0'; ++ wake_up_interruptible(&priv->pltrstn_waitq); ++ dev_dbg(priv->dev, "SYSEVT_PLTRSTN: %c\n", priv->pltrstn); ++ } ++ ++ regmap_write(priv->map, ASPEED_ESPI_SYSEVT_INT_STS, sts); ++} ++ ++static void aspeed_espi_sys_event1(struct aspeed_espi *priv) ++{ ++ u32 sts, evt; ++ ++ regmap_read(priv->map, ASPEED_ESPI_SYSEVT1_INT_STS, &sts); ++ regmap_read(priv->map, ASPEED_ESPI_SYSEVT1, &evt); ++ ++ dev_dbg(priv->dev, "sys event1: sts = %08x, evt = %08x\n", sts, evt); ++ ++ if (sts & ASPEED_ESPI_SYSEVT1_SUS_WARN && ++ evt & ASPEED_ESPI_SYSEVT1_SUS_WARN) { ++ regmap_write_bits(priv->map, ASPEED_ESPI_SYSEVT1, ++ ASPEED_ESPI_SYSEVT1_SUS_ACK, ++ ASPEED_ESPI_SYSEVT1_SUS_ACK); ++ dev_dbg(priv->dev, "SYSEVT1_SUS_WARN: acked\n"); ++ } ++ ++ regmap_write(priv->map, ASPEED_ESPI_SYSEVT1_INT_STS, sts); ++} ++ ++static void aspeed_espi_boot_ack(struct aspeed_espi *priv) ++{ ++ u32 evt; ++ ++ regmap_read(priv->map, ASPEED_ESPI_SYSEVT, &evt); ++ if (!(evt & ASPEED_ESPI_SYSEVT_SLAVE_BOOT_STATUS)) { ++ regmap_write(priv->map, ASPEED_ESPI_SYSEVT, ++ evt | ASPEED_ESPI_SYSEVT_SLAVE_BOOT_STATUS | ++ ASPEED_ESPI_SYSEVT_SLAVE_BOOT_DONE); ++ dev_dbg(priv->dev, "Setting espi slave boot done\n"); ++ } ++ ++ regmap_read(priv->map, ASPEED_ESPI_SYSEVT1, &evt); ++ if (evt & ASPEED_ESPI_SYSEVT1_SUS_WARN && ++ !(evt & ASPEED_ESPI_SYSEVT1_SUS_ACK)) { ++ regmap_write(priv->map, ASPEED_ESPI_SYSEVT1, ++ evt | ASPEED_ESPI_SYSEVT1_SUS_ACK); ++ dev_dbg(priv->dev, "Boot SYSEVT1_SUS_WARN: acked\n"); ++ } ++} ++ ++static irqreturn_t aspeed_espi_irq(int irq, void *arg) ++{ ++ struct aspeed_espi *priv = arg; ++ u32 sts, sts_handled = 0; ++ ++ regmap_read(priv->map, ASPEED_ESPI_INT_STS, &sts); ++ ++ dev_dbg(priv->dev, "INT_STS: 0x%08x\n", sts); ++ ++ if (sts & ASPEED_ESPI_VW_SYSEVT) { ++ aspeed_espi_sys_event(priv); ++ sts_handled |= ASPEED_ESPI_VW_SYSEVT; ++ } ++ ++ if (sts & ASPEED_ESPI_VW_SYSEVT1) { ++ aspeed_espi_sys_event1(priv); ++ sts_handled |= ASPEED_ESPI_VW_SYSEVT1; ++ } ++ ++ if (sts & ASPEED_ESPI_HW_RESET) { ++ if (priv->rst_irq < 0) { ++ regmap_write_bits(priv->map, ASPEED_ESPI_CTRL, ++ ASPEED_ESPI_CTRL_SW_RESET, 0); ++ regmap_write_bits(priv->map, ASPEED_ESPI_CTRL, ++ ASPEED_ESPI_CTRL_SW_RESET, ++ ASPEED_ESPI_CTRL_SW_RESET); ++ } ++ ++ regmap_write_bits(priv->map, ASPEED_ESPI_CTRL, ++ ASPEED_ESPI_CTRL_OOB_CHRDY, ++ ASPEED_ESPI_CTRL_OOB_CHRDY); ++ aspeed_espi_boot_ack(priv); ++ sts_handled |= ASPEED_ESPI_HW_RESET; ++ } ++ ++ regmap_write(priv->map, ASPEED_ESPI_INT_STS, sts); ++ ++ return sts != sts_handled ? IRQ_NONE : IRQ_HANDLED; ++} ++ ++static void aspeed_espi_config_irq(struct aspeed_espi *priv) ++{ ++ regmap_write(priv->map, ASPEED_ESPI_SYSEVT_INT_T0, ++ ASPEED_ESPI_SYSEVT_INT_T0_MASK); ++ regmap_write(priv->map, ASPEED_ESPI_SYSEVT_INT_T1, ++ ASPEED_ESPI_SYSEVT_INT_T1_MASK); ++ regmap_write(priv->map, ASPEED_ESPI_SYSEVT_INT_T2, ++ ASPEED_ESPI_SYSEVT_INT_T2_MASK); ++ regmap_write(priv->map, ASPEED_ESPI_SYSEVT_INT_EN, ++ ASPEED_ESPI_SYSEVT_INT_MASK); ++ ++ regmap_write(priv->map, ASPEED_ESPI_SYSEVT1_INT_T0, ++ ASPEED_ESPI_SYSEVT1_INT_T0_MASK); ++ regmap_write(priv->map, ASPEED_ESPI_SYSEVT1_INT_T1, ++ ASPEED_ESPI_SYSEVT1_INT_T1_MASK); ++ regmap_write(priv->map, ASPEED_ESPI_SYSEVT1_INT_T2, ++ ASPEED_ESPI_SYSEVT1_INT_T2_MASK); ++ regmap_write(priv->map, ASPEED_ESPI_SYSEVT1_INT_EN, ++ ASPEED_ESPI_SYSEVT1_INT_MASK); ++ ++ regmap_write(priv->map, ASPEED_ESPI_INT_EN, ASPEED_ESPI_INT_MASK); ++} ++ ++static irqreturn_t aspeed_espi_reset_isr(int irq, void *arg) ++{ ++ struct aspeed_espi *priv = arg; ++ ++ reset_control_assert(priv->reset); ++ reset_control_deassert(priv->reset); ++ ++ regmap_write_bits(priv->map, ASPEED_ESPI_CTRL, ++ ASPEED_ESPI_CTRL_SW_RESET, 0); ++ regmap_write_bits(priv->map, ASPEED_ESPI_CTRL, ++ ASPEED_ESPI_CTRL_SW_RESET, ASPEED_ESPI_CTRL_SW_RESET); ++ ++ regmap_write_bits(priv->map, ASPEED_ESPI_CTRL, ++ ASPEED_ESPI_CTRL_OOB_CHRDY, 0); ++ ++ aspeed_espi_config_irq(priv); ++ ++ return IRQ_HANDLED; ++} ++ ++static inline struct aspeed_espi *to_aspeed_espi(struct file *filp) ++{ ++ return container_of(filp->private_data, struct aspeed_espi, ++ pltrstn_miscdev); ++} ++ ++static int aspeed_espi_pltrstn_open(struct inode *inode, struct file *filp) ++{ ++ if ((filp->f_flags & O_ACCMODE) != O_RDONLY) ++ return -EACCES; ++ ++ return 0; ++} ++ ++static ssize_t aspeed_espi_pltrstn_read(struct file *filp, char __user *buf, ++ size_t count, loff_t *offset) ++{ ++ struct aspeed_espi *priv = to_aspeed_espi(filp); ++ DECLARE_WAITQUEUE(wait, current); ++ unsigned long flags; ++ char old_sample; ++ int ret = 0; ++ ++ spin_lock_irqsave(&priv->pltrstn_lock, flags); ++ ++ add_wait_queue(&priv->pltrstn_waitq, &wait); ++ set_current_state(TASK_INTERRUPTIBLE); ++ ++ old_sample = priv->pltrstn; ++ ++ do { ++ char new_sample = priv->pltrstn; ++ ++ if (filp->f_flags & O_NONBLOCK || old_sample != new_sample) { ++ ret = put_user(new_sample, (unsigned long __user *)buf); ++ if (!ret) ++ ret = sizeof(new_sample); ++ } else if (signal_pending(current)) { ++ ret = -ERESTARTSYS; ++ } ++ ++ if (!ret) { ++ spin_unlock_irqrestore(&priv->pltrstn_lock, flags); ++ schedule(); ++ spin_lock_irqsave(&priv->pltrstn_lock, flags); ++ } ++ } while (!ret); ++ ++ remove_wait_queue(&priv->pltrstn_waitq, &wait); ++ set_current_state(TASK_RUNNING); ++ ++ spin_unlock_irqrestore(&priv->pltrstn_lock, flags); ++ ++ return ret; ++} ++ ++static const struct file_operations aspeed_espi_pltrstn_fops = { ++ .owner = THIS_MODULE, ++ .open = aspeed_espi_pltrstn_open, ++ .read = aspeed_espi_pltrstn_read, ++}; ++ ++static const struct regmap_config aspeed_espi_regmap_cfg = { ++ .reg_bits = 32, ++ .reg_stride = 4, ++ .val_bits = 32, ++ .max_register = ASPEED_ESPI_SYSEVT1_INT_STS, ++}; ++ ++static int aspeed_espi_probe(struct platform_device *pdev) ++{ ++ struct aspeed_espi *priv; ++ struct resource *res; ++ void __iomem *regs; ++ u32 ctrl; ++ int ret; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ regs = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(regs)) ++ return PTR_ERR(regs); ++ ++ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ dev_set_drvdata(&pdev->dev, priv); ++ priv->dev = &pdev->dev; ++ ++ priv->map = devm_regmap_init_mmio(&pdev->dev, regs, ++ &aspeed_espi_regmap_cfg); ++ if (IS_ERR(priv->map)) ++ return PTR_ERR(priv->map); ++ ++ spin_lock_init(&priv->pltrstn_lock); ++ init_waitqueue_head(&priv->pltrstn_waitq); ++ priv->pltrstn = 'U'; /* means it's not reported yet from master */ ++ ++ priv->irq = platform_get_irq(pdev, 0); ++ if (priv->irq < 0) ++ return priv->irq; ++ ++ ret = devm_request_irq(&pdev->dev, priv->irq, aspeed_espi_irq, 0, ++ "aspeed-espi-irq", priv); ++ if (ret) ++ return ret; ++ ++ if (of_device_is_compatible(pdev->dev.of_node, ++ "aspeed,ast2600-espi-slave")) { ++ priv->rst_irq = platform_get_irq(pdev, 1); ++ if (priv->rst_irq < 0) ++ return priv->rst_irq; ++ ++ ret = devm_request_irq(&pdev->dev, priv->rst_irq, ++ aspeed_espi_reset_isr, 0, ++ "aspeed-espi-rst-irq", priv); ++ if (ret) ++ return ret; ++ ++ priv->reset = devm_reset_control_get(&pdev->dev, NULL); ++ if (IS_ERR(priv->reset)) ++ return PTR_ERR(priv->reset); ++ } else { ++ priv->rst_irq = -ENOTSUPP; ++ } ++ ++ priv->clk = devm_clk_get(&pdev->dev, NULL); ++ if (IS_ERR(priv->clk)) { ++ ret = PTR_ERR(priv->clk); ++ if (ret != -EPROBE_DEFER) ++ dev_err(&pdev->dev, "couldn't get clock\n"); ++ return ret; ++ } ++ ret = clk_prepare_enable(priv->clk); ++ if (ret) { ++ dev_err(&pdev->dev, "couldn't enable clock\n"); ++ return ret; ++ } ++ ++ /* ++ * We check that the regmap works on this very first access, but as this ++ * is an MMIO-backed regmap, subsequent regmap access is not going to ++ * fail and we skip error checks from this point. ++ */ ++ ret = regmap_read(priv->map, ASPEED_ESPI_CTRL, &ctrl); ++ if (ret) { ++ dev_err(&pdev->dev, "failed to read ctrl register\n"); ++ goto err_clk_disable_out; ++ } ++ regmap_write(priv->map, ASPEED_ESPI_CTRL, ++ ctrl | ASPEED_ESPI_CTRL_OOB_CHRDY); ++ ++ priv->pltrstn_miscdev.minor = MISC_DYNAMIC_MINOR; ++ priv->pltrstn_miscdev.name = "espi-pltrstn"; ++ priv->pltrstn_miscdev.fops = &aspeed_espi_pltrstn_fops; ++ priv->pltrstn_miscdev.parent = &pdev->dev; ++ ++ ret = misc_register(&priv->pltrstn_miscdev); ++ if (ret) { ++ dev_err(&pdev->dev, "Unable to register device\n"); ++ goto err_clk_disable_out; ++ } ++ ++ aspeed_espi_config_irq(priv); ++ aspeed_espi_boot_ack(priv); ++ ++ dev_info(&pdev->dev, "eSPI registered, irq %d\n", priv->irq); ++ ++ return 0; ++ ++err_clk_disable_out: ++ clk_disable_unprepare(priv->clk); ++ ++ return ret; ++} ++ ++static int aspeed_espi_remove(struct platform_device *pdev) ++{ ++ struct aspeed_espi *priv = dev_get_drvdata(&pdev->dev); ++ ++ misc_deregister(&priv->pltrstn_miscdev); ++ clk_disable_unprepare(priv->clk); ++ ++ return 0; ++} ++ ++static const struct of_device_id of_espi_match_table[] = { ++ { .compatible = "aspeed,ast2500-espi-slave" }, ++ { .compatible = "aspeed,ast2600-espi-slave" }, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, of_espi_match_table); ++ ++static struct platform_driver aspeed_espi_driver = { ++ .driver = { ++ .name = KBUILD_MODNAME, ++ .of_match_table = of_match_ptr(of_espi_match_table), ++ }, ++ .probe = aspeed_espi_probe, ++ .remove = aspeed_espi_remove, ++}; ++module_platform_driver(aspeed_espi_driver); ++ ++MODULE_AUTHOR("Haiyue Wang <haiyue.wang@linux.intel.com>"); ++MODULE_AUTHOR("Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>"); ++MODULE_DESCRIPTION("Aspeed eSPI driver"); ++MODULE_LICENSE("GPL v2"); +diff --git a/include/dt-bindings/clock/ast2600-clock.h b/include/dt-bindings/clock/ast2600-clock.h +index 3d90582a813f..1e18364de03d 100644 +--- a/include/dt-bindings/clock/ast2600-clock.h ++++ b/include/dt-bindings/clock/ast2600-clock.h +@@ -104,6 +104,7 @@ + #define ASPEED_RESET_PECI 36 + #define ASPEED_RESET_MII 35 + #define ASPEED_RESET_I2C 34 ++#define ASPEED_RESET_ESPI 32 + #define ASPEED_RESET_H2X 31 + #define ASPEED_RESET_GP_MCU 30 + #define ASPEED_RESET_DP_MCU 29 +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0028-Add-AST2500-JTAG-driver.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0028-Add-AST2500-JTAG-driver.patch new file mode 100644 index 000000000..45dec5b20 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0028-Add-AST2500-JTAG-driver.patch @@ -0,0 +1,95 @@ +From a4413fe3ba94906243b632e0b9e0e9a91620dd22 Mon Sep 17 00:00:00 2001 +From: "Corona, Ernesto" <ernesto.corona@intel.com> +Date: Fri, 1 Mar 2019 11:46:09 -0700 +Subject: [PATCH] Add AST2500 JTAG driver + +Update AST2500 JTAG driver. Remove Legacy driver but keep headers. + +Signed-off-by: Corona, Ernesto <ernesto.corona@intel.com> +--- + include/uapi/linux/jtag_drv.h | 73 +++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 73 insertions(+) + create mode 100644 include/uapi/linux/jtag_drv.h + +diff --git a/include/uapi/linux/jtag_drv.h b/include/uapi/linux/jtag_drv.h +new file mode 100644 +index 000000000000..4df638f8fa43 +--- /dev/null ++++ b/include/uapi/linux/jtag_drv.h +@@ -0,0 +1,73 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++/* Copyright (C) 2012-2017 ASPEED Technology Inc. */ ++/* Copyright (c) 2018 Intel Corporation */ ++ ++#ifndef __JTAG_DRV_H__ ++#define __JTAG_DRV_H__ ++ ++enum xfer_mode { ++ HW_MODE = 0, ++ SW_MODE ++} xfer_mode; ++ ++struct tck_bitbang { ++ __u8 tms; ++ __u8 tdi; ++ __u8 tdo; ++} __attribute__((__packed__)); ++ ++struct scan_xfer { ++ __u8 mode; ++ __u32 tap_state; ++ __u32 length; ++ __u8 *tdi; ++ __u32 tdi_bytes; ++ __u8 *tdo; ++ __u32 tdo_bytes; ++ __u32 end_tap_state; ++} __attribute__((__packed__)); ++ ++struct set_tck_param { ++ __u8 mode; ++ __u32 tck; ++} __attribute__((__packed__)); ++ ++struct get_tck_param { ++ __u8 mode; ++ __u32 tck; ++} __attribute__((__packed__)); ++ ++struct tap_state_param { ++ __u8 mode; ++ __u32 from_state; ++ __u32 to_state; ++} __attribute__((__packed__)); ++ ++enum jtag_states { ++ jtag_tlr, ++ jtag_rti, ++ jtag_sel_dr, ++ jtag_cap_dr, ++ jtag_shf_dr, ++ jtag_ex1_dr, ++ jtag_pau_dr, ++ jtag_ex2_dr, ++ jtag_upd_dr, ++ jtag_sel_ir, ++ jtag_cap_ir, ++ jtag_shf_ir, ++ jtag_ex1_ir, ++ jtag_pau_ir, ++ jtag_ex2_ir, ++ jtag_upd_ir ++} jtag_states; ++ ++#define JTAGIOC_BASE 'T' ++ ++#define AST_JTAG_SET_TCK _IOW(JTAGIOC_BASE, 3, struct set_tck_param) ++#define AST_JTAG_GET_TCK _IOR(JTAGIOC_BASE, 4, struct get_tck_param) ++#define AST_JTAG_BITBANG _IOWR(JTAGIOC_BASE, 5, struct tck_bitbang) ++#define AST_JTAG_SET_TAPSTATE _IOW(JTAGIOC_BASE, 6, struct tap_state_param) ++#define AST_JTAG_READWRITESCAN _IOWR(JTAGIOC_BASE, 7, struct scan_xfer) ++ ++#endif +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0030-Add-dump-debug-code-into-I2C-drivers.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0030-Add-dump-debug-code-into-I2C-drivers.patch new file mode 100644 index 000000000..c9844e1bd --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0030-Add-dump-debug-code-into-I2C-drivers.patch @@ -0,0 +1,145 @@ +From a57f401058e452f9abb6f3233e7f2776af1e6b8d Mon Sep 17 00:00:00 2001 +From: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +Date: Fri, 29 Jun 2018 11:00:02 -0700 +Subject: [PATCH] Add dump debug code into I2C drivers + +This commit enables dump debug of master and slave I2C drivers. +This is only for downstream debug purpose so it shouldn't go to +the upstream. + +Usage (in case of bus 5 for an example): +echo 5 > /sys/module/i2c_aspeed/parameters/dump_debug_bus_id +echo 1 > /sys/module/i2c_aspeed/parameters/dump_debug +echo 5 > /sys/module/i2c_slave_mqueue/parameters/dump_debug_bus_id +echo 1 > /sys/module/i2c_slave_mqueue/parameters/dump_debug + +Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +--- + drivers/i2c/busses/i2c-aspeed.c | 26 ++++++++++++++++++++++++++ + drivers/i2c/i2c-slave-mqueue.c | 24 ++++++++++++++++++++++++ + 2 files changed, 50 insertions(+) + +diff --git a/drivers/i2c/busses/i2c-aspeed.c b/drivers/i2c/busses/i2c-aspeed.c +index a7be6f24450b..c113ffa8d5df 100644 +--- a/drivers/i2c/busses/i2c-aspeed.c ++++ b/drivers/i2c/busses/i2c-aspeed.c +@@ -169,6 +169,21 @@ struct aspeed_i2c_bus { + #endif /* CONFIG_I2C_SLAVE */ + }; + ++static bool dump_debug __read_mostly; ++static int dump_debug_bus_id __read_mostly; ++ ++#define I2C_HEX_DUMP(bus, addr, flags, buf, len) \ ++ do { \ ++ if (dump_debug && (bus)->adap.nr == dump_debug_bus_id) { \ ++ char dump_info[100] = {0,}; \ ++ snprintf(dump_info, sizeof(dump_info), \ ++ "bus_id:%d, addr:0x%02x, flags:0x%02x: ", \ ++ (bus)->adap.nr, addr, flags); \ ++ print_hex_dump(KERN_ERR, dump_info, DUMP_PREFIX_NONE, \ ++ 16, 1, buf, len, true); \ ++ } \ ++ } while (0) ++ + static int aspeed_i2c_reset(struct aspeed_i2c_bus *bus); + + static int aspeed_i2c_recover_bus(struct aspeed_i2c_bus *bus) +@@ -657,6 +672,7 @@ static int aspeed_i2c_master_xfer(struct i2c_adapter *adap, + { + struct aspeed_i2c_bus *bus = i2c_get_adapdata(adap); + unsigned long time_left, flags; ++ int i; + + spin_lock_irqsave(&bus->lock, flags); + bus->cmd_err = 0; +@@ -708,6 +724,11 @@ static int aspeed_i2c_master_xfer(struct i2c_adapter *adap, + return -ETIMEDOUT; + } + ++ for (i = 0; i < num; i++) { ++ I2C_HEX_DUMP(bus, msgs[i].addr, msgs[i].flags, ++ msgs[i].buf, msgs[i].len); ++ } ++ + return bus->master_xfer_result; + } + +@@ -1079,6 +1100,11 @@ static struct platform_driver aspeed_i2c_bus_driver = { + }; + module_platform_driver(aspeed_i2c_bus_driver); + ++module_param_named(dump_debug, dump_debug, bool, 0644); ++MODULE_PARM_DESC(dump_debug, "debug flag for dump printing"); ++module_param_named(dump_debug_bus_id, dump_debug_bus_id, int, 0644); ++MODULE_PARM_DESC(dump_debug_bus_id, "bus id for dump debug printing"); ++ + MODULE_AUTHOR("Brendan Higgins <brendanhiggins@google.com>"); + MODULE_DESCRIPTION("Aspeed I2C Bus Driver"); + MODULE_LICENSE("GPL v2"); +diff --git a/drivers/i2c/i2c-slave-mqueue.c b/drivers/i2c/i2c-slave-mqueue.c +index 6014bca0ff2a..2c7a6038409c 100644 +--- a/drivers/i2c/i2c-slave-mqueue.c ++++ b/drivers/i2c/i2c-slave-mqueue.c +@@ -21,6 +21,7 @@ struct mq_msg { + struct mq_queue { + struct bin_attribute bin; + struct kernfs_node *kn; ++ struct i2c_client *client; + + spinlock_t lock; /* spinlock for queue index handling */ + int in; +@@ -31,6 +32,21 @@ struct mq_queue { + struct mq_msg *queue; + }; + ++static bool dump_debug __read_mostly; ++static int dump_debug_bus_id __read_mostly; ++ ++#define I2C_HEX_DUMP(client, buf, len) \ ++ do { \ ++ if (dump_debug && \ ++ (client)->adapter->nr == dump_debug_bus_id) { \ ++ char dump_info[100] = {0,}; \ ++ snprintf(dump_info, sizeof(dump_info), \ ++ "bus_id:%d: ", (client)->adapter->nr); \ ++ print_hex_dump(KERN_ERR, dump_info, DUMP_PREFIX_NONE, \ ++ 16, 1, buf, len, true); \ ++ } \ ++ } while (0) ++ + static int i2c_slave_mqueue_callback(struct i2c_client *client, + enum i2c_slave_event event, u8 *val) + { +@@ -101,6 +117,7 @@ static ssize_t i2c_slave_mqueue_bin_read(struct file *filp, + if (msg->len <= count) { + ret = msg->len; + memcpy(buf, msg->buf, ret); ++ I2C_HEX_DUMP(mq->client, buf, ret); + } else { + ret = -EOVERFLOW; /* Drop this HUGE one. */ + } +@@ -131,6 +148,8 @@ static int i2c_slave_mqueue_probe(struct i2c_client *client, + + BUILD_BUG_ON(!is_power_of_2(MQ_QUEUE_SIZE)); + ++ mq->client = client; ++ + buf = devm_kmalloc_array(dev, MQ_QUEUE_SIZE, MQ_MSGBUF_SIZE, + GFP_KERNEL); + if (!buf) +@@ -212,6 +231,11 @@ static struct i2c_driver i2c_slave_mqueue_driver = { + }; + module_i2c_driver(i2c_slave_mqueue_driver); + ++module_param_named(dump_debug, dump_debug, bool, 0644); ++MODULE_PARM_DESC(dump_debug, "debug flag for dump printing"); ++module_param_named(dump_debug_bus_id, dump_debug_bus_id, int, 0644); ++MODULE_PARM_DESC(dump_debug_bus_id, "bus id for dump debug printing"); ++ + MODULE_LICENSE("GPL v2"); + MODULE_AUTHOR("Haiyue Wang <haiyue.wang@linux.intel.com>"); + MODULE_DESCRIPTION("I2C slave mode for receiving and queuing messages"); +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0031-Add-high-speed-baud-rate-support-for-UART.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0031-Add-high-speed-baud-rate-support-for-UART.patch new file mode 100644 index 000000000..5240a05f5 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0031-Add-high-speed-baud-rate-support-for-UART.patch @@ -0,0 +1,135 @@ +From 0bc792097e73c07bc324e2c9b0172fb27b51a087 Mon Sep 17 00:00:00 2001 +From: Yong Li <yong.b.li@linux.intel.com> +Date: Wed, 2 Jan 2019 15:06:43 +0800 +Subject: [PATCH] Add high speed baud rate support for UART + +In order to support high speed baud rate(921600 bps), +the default UART clock(24MHz) needs to be switched +to 192MHz(from USB2.0 port1 PHY). + +Create a new 192M Hz clock and assign it to uart, +based on uart clock source configuration in SCU4C. + +bootloader(u-boot) will set SCU4C based on the environment configuration + +Signed-off-by: Yong Li <yong.b.li@linux.intel.com> +--- + drivers/clk/clk-aspeed.c | 44 +++++++++++++++++++++++++++----- + include/dt-bindings/clock/aspeed-clock.h | 2 ++ + 2 files changed, 39 insertions(+), 7 deletions(-) + +diff --git a/drivers/clk/clk-aspeed.c b/drivers/clk/clk-aspeed.c +index 411ff5fb2c07..5e1c87bc8a99 100644 +--- a/drivers/clk/clk-aspeed.c ++++ b/drivers/clk/clk-aspeed.c +@@ -14,7 +14,9 @@ + + #include "clk-aspeed.h" + +-#define ASPEED_NUM_CLKS 38 ++#define ASPEED_NUM_CLKS ASPEED_CLK_MAX ++#define UART_HIGH_SPEED_CLK 192000000 ++#define UART_LOW_SPEED_CLK 24000000 + + #define ASPEED_RESET2_OFFSET 32 + +@@ -29,6 +31,12 @@ + #define ASPEED_MISC_CTRL 0x2c + #define UART_DIV13_EN BIT(12) + #define ASPEED_MAC_CLK_DLY 0x48 ++#define ASPEED_MISC2_CTRL 0x4c ++#define UART1_HS_CLK_EN BIT(24) ++#define UART2_HS_CLK_EN BIT(25) ++#define UART3_HS_CLK_EN BIT(26) ++#define UART4_HS_CLK_EN BIT(27) ++#define UART5_HS_CLK_EN BIT(28) + #define ASPEED_STRAP 0x70 + #define CLKIN_25MHZ_EN BIT(23) + #define AST2400_CLK_SOURCE_SEL BIT(18) +@@ -386,7 +394,7 @@ static int aspeed_clk_probe(struct platform_device *pdev) + struct aspeed_reset *ar; + struct regmap *map; + struct clk_hw *hw; +- u32 val, rate; ++ u32 val, rate, rate_hi; + int i, ret; + + map = syscon_node_to_regmap(dev->of_node); +@@ -420,16 +428,25 @@ static int aspeed_clk_probe(struct platform_device *pdev) + + /* UART clock div13 setting */ + regmap_read(map, ASPEED_MISC_CTRL, &val); +- if (val & UART_DIV13_EN) +- rate = 24000000 / 13; +- else +- rate = 24000000; ++ if (val & UART_DIV13_EN) { ++ rate = UART_LOW_SPEED_CLK / 13; ++ rate_hi = UART_HIGH_SPEED_CLK / 13; ++ } else { ++ rate = UART_LOW_SPEED_CLK; ++ rate_hi = UART_HIGH_SPEED_CLK; ++ } + /* TODO: Find the parent data for the uart clock */ + hw = clk_hw_register_fixed_rate(dev, "uart", NULL, 0, rate); + if (IS_ERR(hw)) + return PTR_ERR(hw); + aspeed_clk_data->hws[ASPEED_CLK_UART] = hw; + ++ hw = clk_hw_register_fixed_rate(dev, "uart-hs", "usb-port1-gate", 0, ++ rate_hi); ++ if (IS_ERR(hw)) ++ return PTR_ERR(hw); ++ aspeed_clk_data->hws[ASPEED_CLK_UART_HS] = hw; ++ + /* + * Memory controller (M-PLL) PLL. This clock is configured by the + * bootloader, and is exposed to Linux as a read-only clock rate. +@@ -539,9 +556,22 @@ static int aspeed_clk_probe(struct platform_device *pdev) + * UART[1..5] clock source mux + */ + ++ /* Get the uart clock source configuration from SCU4C*/ ++ regmap_read(map, ASPEED_MISC2_CTRL, &val); + for (i = 0; i < ARRAY_SIZE(aspeed_gates); i++) { + const struct aspeed_gate_data *gd = &aspeed_gates[i]; + u32 gate_flags; ++ char *parent_name; ++ ++ /* For uart, needs to adjust the clock based on SCU4C value */ ++ if ((i == ASPEED_CLK_GATE_UART1CLK && (val & UART1_HS_CLK_EN)) || ++ (i == ASPEED_CLK_GATE_UART2CLK && (val & UART2_HS_CLK_EN)) || ++ (i == ASPEED_CLK_GATE_UART5CLK && (val & UART5_HS_CLK_EN)) || ++ (i == ASPEED_CLK_GATE_UART3CLK && (val & UART3_HS_CLK_EN)) || ++ (i == ASPEED_CLK_GATE_UART4CLK && (val & UART4_HS_CLK_EN))) ++ parent_name = "uart-hs"; ++ else ++ parent_name = gd->parent_name; + + /* Special case: the USB port 1 clock (bit 14) is always + * working the opposite way from the other ones. +@@ -549,7 +579,7 @@ static int aspeed_clk_probe(struct platform_device *pdev) + gate_flags = (gd->clock_idx == 14) ? 0 : CLK_GATE_SET_TO_DISABLE; + hw = aspeed_clk_hw_register_gate(dev, + gd->name, +- gd->parent_name, ++ parent_name, + gd->flags, + map, + gd->clock_idx, +diff --git a/include/dt-bindings/clock/aspeed-clock.h b/include/dt-bindings/clock/aspeed-clock.h +index 9ff4f6e4558c..41d531dd0b48 100644 +--- a/include/dt-bindings/clock/aspeed-clock.h ++++ b/include/dt-bindings/clock/aspeed-clock.h +@@ -41,6 +41,8 @@ + #define ASPEED_CLK_24M 35 + #define ASPEED_CLK_MAC1RCLK 36 + #define ASPEED_CLK_MAC2RCLK 37 ++#define ASPEED_CLK_UART_HS 38 ++#define ASPEED_CLK_MAX 39 + + #define ASPEED_RESET_XDMA 0 + #define ASPEED_RESET_MCTP 1 +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0032-misc-aspeed-Add-Aspeed-UART-routing-control-driver.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0032-misc-aspeed-Add-Aspeed-UART-routing-control-driver.patch new file mode 100644 index 000000000..2ba9e5002 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0032-misc-aspeed-Add-Aspeed-UART-routing-control-driver.patch @@ -0,0 +1,559 @@ +From 09aa5d5af94823f100fb515d45b0a04fc8d7ee4f Mon Sep 17 00:00:00 2001 +From: Oskar Senft <osk@google.com> +Date: Wed, 8 Aug 2018 10:15:05 -0400 +Subject: [PATCH] misc: aspeed: Add Aspeed UART routing control driver. + +This driver adds sysfs files that allow the BMC userspace to configure +how UARTs and physical serial I/O ports are routed. + +Tested: Checked correct behavior (both read & write) on TYAN S7106 +board by manually changing routing settings and confirming that bits +flow as expected. Tested for UART1 and UART3 as this board doesn't have +the other UARTs wired up in a testable way. + +Signed-off-by: Oskar Senft <osk@google.com> +Signed-off-by: Yong Li <yong.b.li@linux.intel.com> +Signed-off-by: Vernon Mauery <vernon.mauery@linux.intel.com> +--- + .../ABI/stable/sysfs-driver-aspeed-uart-routing | 14 + + Documentation/misc-devices/aspeed-uart-routing.txt | 49 +++ + arch/arm/boot/dts/aspeed-g5.dtsi | 6 + + arch/arm/boot/dts/aspeed-g6.dtsi | 6 + + drivers/misc/Kconfig | 6 + + drivers/misc/Makefile | 1 + + drivers/misc/aspeed-uart-routing.c | 383 +++++++++++++++++++++ + 7 files changed, 465 insertions(+) + create mode 100644 Documentation/ABI/stable/sysfs-driver-aspeed-uart-routing + create mode 100644 Documentation/misc-devices/aspeed-uart-routing.txt + create mode 100644 drivers/misc/aspeed-uart-routing.c + +diff --git a/Documentation/ABI/stable/sysfs-driver-aspeed-uart-routing b/Documentation/ABI/stable/sysfs-driver-aspeed-uart-routing +new file mode 100644 +index 000000000000..5068737d9c12 +--- /dev/null ++++ b/Documentation/ABI/stable/sysfs-driver-aspeed-uart-routing +@@ -0,0 +1,14 @@ ++What: /sys/bus/platform/drivers/aspeed-uart-routing/*/io* ++Date: August 2018 ++Contact: Oskar Senft <osk@google.com> ++Description: Configures the input source for the specific physical ++ serial I/O port. ++Users: OpenBMC. Proposed changes should be mailed to ++ openbmc@lists.ozlabs.org ++ ++What: /sys/bus/platform/drivers/aspeed-uart-routing/*/uart* ++Date: August 2018 ++Contact: Oskar Senft <osk@google.com> ++Description: Configures the input source for the specific UART. ++Users: OpenBMC. Proposed changes should be mailed to ++ openbmc@lists.ozlabs.org +diff --git a/Documentation/misc-devices/aspeed-uart-routing.txt b/Documentation/misc-devices/aspeed-uart-routing.txt +new file mode 100644 +index 000000000000..cf1c2a466875 +--- /dev/null ++++ b/Documentation/misc-devices/aspeed-uart-routing.txt +@@ -0,0 +1,49 @@ ++Kernel driver aspeed-uart-routing ++================================= ++ ++Supported chips: ++ASPEED AST2500/AST2600 ++ ++Author: ++Google LLC ++ ++Description ++----------- ++ ++The Aspeed AST2500/AST2600 allows to dynamically route the inputs for the ++built-in UARTS and physical serial I/O ports. ++ ++This allows, for example, to connect the output of UART to another UART. ++This can be used to enable host<->BMC communication via UARTs, e.g. to allow ++access to the host's serial console. ++ ++This driver is for the BMC side. The sysfs files allow the BMC userspace ++which owns the system configuration policy, to configure how UARTs and ++physical serial I/O ports are routed. ++ ++The driver provides the following files in sysfs: ++uart1 Configure the input signal to UART1. ++uart2 Configure the input signal to UART2. ++uart3 Configure the input signal to UART3. ++uart4 Configure the input signal to UART4. ++uart5 Configure the input signal to UART5. ++io1 Configure the input signal to physical serial port 1. ++io2 Configure the input signal to physical serial port 2. ++io3 Configure the input signal to physical serial port 3. ++io4 Configure the input signal to physical serial port 4. ++io5 Configure the input signal to physical serial port 5. ++ ++When read, each file shows the list of available options with the currently ++selected option marked by square brackets "[]". The list of available options ++depends on the selected file. ++ ++Example: ++$ cat /sys/bus/platform/drivers/aspeed-uart-routing/*.uart_routing/uart1 ++[io1] io2 io3 io4 uart2 uart3 uart4 io6 ++ ++In this case, UART1 gets its input signal from IO1 (physical serial port 1). ++ ++$ echo -n "uart3" \ ++ >/sys/bus/platform/drivers/aspeed-uart-routing/*.uart_routing/uart1 ++$ cat /sys/bus/platform/drivers/aspeed-uart-routing/*.uart_routing/uart1 ++io1 io2 io3 io4 uart2 [uart3] uart4 io6 +diff --git a/arch/arm/boot/dts/aspeed-g5.dtsi b/arch/arm/boot/dts/aspeed-g5.dtsi +index bd2037e52a94..797013debaa7 100644 +--- a/arch/arm/boot/dts/aspeed-g5.dtsi ++++ b/arch/arm/boot/dts/aspeed-g5.dtsi +@@ -523,6 +523,12 @@ + status = "disabled"; + }; + }; ++ ++ uart_routing: uart_routing@9c { ++ compatible = "aspeed,ast2500-uart-routing"; ++ reg = <0x9c 0x4>; ++ status = "disabled"; ++ }; + }; + + peci: bus@1e78b000 { +diff --git a/arch/arm/boot/dts/aspeed-g6.dtsi b/arch/arm/boot/dts/aspeed-g6.dtsi +index e4c1ab3d274e..8cc978058f16 100644 +--- a/arch/arm/boot/dts/aspeed-g6.dtsi ++++ b/arch/arm/boot/dts/aspeed-g6.dtsi +@@ -291,6 +291,12 @@ + compatible = "aspeed,ast2600-pinctrl"; + }; + ++ uart_routing: uart_routing@9c { ++ compatible = "aspeed,ast2500-uart-routing"; ++ reg = <0x9c 0x4>; ++ status = "disabled"; ++ }; ++ + smp-memram@180 { + compatible = "aspeed,ast2600-smpmem"; + reg = <0x180 0x40>; +diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig +index 0f9b5a356c93..8af817694648 100644 +--- a/drivers/misc/Kconfig ++++ b/drivers/misc/Kconfig +@@ -441,6 +441,12 @@ config ASPEED_ESPI_SLAVE + Control Aspeed ast2500 eSPI slave controller to handle event + which needs the firmware's processing. + ++config ASPEED_UART_ROUTING ++ tristate "Aspeed ast2500 UART routing control" ++ help ++ If you want to configure UART routing on Aspeed BMC platforms, enable ++ this option. ++ + config PCI_ENDPOINT_TEST + depends on PCI + select CRC32 +diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile +index 53864687e8fd..a4372208f4de 100644 +--- a/drivers/misc/Makefile ++++ b/drivers/misc/Makefile +@@ -52,6 +52,7 @@ obj-$(CONFIG_ECHO) += echo/ + obj-$(CONFIG_VEXPRESS_SYSCFG) += vexpress-syscfg.o + obj-$(CONFIG_CXL_BASE) += cxl/ + obj-$(CONFIG_ASPEED_ESPI_SLAVE) += aspeed-espi-slave.o ++obj-$(CONFIG_ASPEED_UART_ROUTING) += aspeed-uart-routing.o + obj-$(CONFIG_PCI_ENDPOINT_TEST) += pci_endpoint_test.o + obj-$(CONFIG_OCXL) += ocxl/ + obj-y += cardreader/ +diff --git a/drivers/misc/aspeed-uart-routing.c b/drivers/misc/aspeed-uart-routing.c +new file mode 100644 +index 000000000000..21ef5d98c317 +--- /dev/null ++++ b/drivers/misc/aspeed-uart-routing.c +@@ -0,0 +1,383 @@ ++/* ++ * UART Routing driver for Aspeed AST2500 ++ * ++ * Copyright (c) 2018 Google LLC ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++#include <linux/device.h> ++#include <linux/module.h> ++#include <linux/of_address.h> ++#include <linux/of_platform.h> ++ ++/* The Aspeed AST2500 allows to dynamically route the inputs for the built-in ++ * UARTS and physical serial I/O ports. ++ * ++ * This allows, for example, to connect the output of UART to another UART. ++ * This can be used to enable host<->BMC communication via UARTs, e.g. to allow ++ * access to the host's serial console. ++ * ++ * This driver is for the BMC side. The sysfs files allow the BMC userspace ++ * which owns the system configuration policy, to configure how UARTs and ++ * physical serial I/O ports are routed. ++ */ ++ ++#define ASPEED_HICRA_IO1 "io1" ++#define ASPEED_HICRA_IO2 "io2" ++#define ASPEED_HICRA_IO3 "io3" ++#define ASPEED_HICRA_IO4 "io4" ++#define ASPEED_HICRA_IO5 "io5" ++#define ASPEED_HICRA_IO6 "io6" ++#define ASPEED_HICRA_UART1 "uart1" ++#define ASPEED_HICRA_UART2 "uart2" ++#define ASPEED_HICRA_UART3 "uart3" ++#define ASPEED_HICRA_UART4 "uart4" ++#define ASPEED_HICRA_UART5 "uart5" ++ ++struct aspeed_uart_routing { ++ struct device *dev; ++ void __iomem *regs; ++ spinlock_t lock; ++}; ++ ++struct aspeed_uart_routing_selector { ++ struct device_attribute dev_attr; ++ int shift; ++ int mask; ++ const char * const options[]; ++}; ++ ++#define to_routing_selector(_dev_attr) \ ++ container_of(_dev_attr, struct aspeed_uart_routing_selector, dev_attr) ++ ++ ++static ssize_t aspeed_uart_routing_show(struct device *dev, ++ struct device_attribute *attr, ++ char *buf); ++ ++static ssize_t aspeed_uart_routing_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t count); ++ ++#define ROUTING_ATTR(_name) { \ ++ .attr = {.name = _name, \ ++ .mode = VERIFY_OCTAL_PERMISSIONS(S_IWUSR | S_IRUGO) }, \ ++ .show = aspeed_uart_routing_show, \ ++ .store = aspeed_uart_routing_store, \ ++} ++ ++static struct aspeed_uart_routing_selector uart5_sel = { ++ .dev_attr = ROUTING_ATTR(ASPEED_HICRA_UART5), ++ .shift = 28, ++ .mask = 0xf, ++ .options = { ++ ASPEED_HICRA_IO5, // 0 ++ ASPEED_HICRA_IO1, // 1 ++ ASPEED_HICRA_IO2, // 2 ++ ASPEED_HICRA_IO3, // 3 ++ ASPEED_HICRA_IO4, // 4 ++ ASPEED_HICRA_UART1, // 5 ++ ASPEED_HICRA_UART2, // 6 ++ ASPEED_HICRA_UART3, // 7 ++ ASPEED_HICRA_UART4, // 8 ++ ASPEED_HICRA_IO6, // 9 ++ NULL, // NULL termination ++ }, ++}; ++ ++static struct aspeed_uart_routing_selector uart4_sel = { ++ .dev_attr = ROUTING_ATTR(ASPEED_HICRA_UART4), ++ .shift = 25, ++ .mask = 0x7, ++ .options = { ++ ASPEED_HICRA_IO4, // 0 ++ ASPEED_HICRA_IO1, // 1 ++ ASPEED_HICRA_IO2, // 2 ++ ASPEED_HICRA_IO3, // 3 ++ ASPEED_HICRA_UART1, // 4 ++ ASPEED_HICRA_UART2, // 5 ++ ASPEED_HICRA_UART3, // 6 ++ ASPEED_HICRA_IO6, // 7 ++ NULL, // NULL termination ++ }, ++}; ++ ++static struct aspeed_uart_routing_selector uart3_sel = { ++ .dev_attr = ROUTING_ATTR(ASPEED_HICRA_UART3), ++ .shift = 22, ++ .mask = 0x7, ++ .options = { ++ ASPEED_HICRA_IO3, // 0 ++ ASPEED_HICRA_IO4, // 1 ++ ASPEED_HICRA_IO1, // 2 ++ ASPEED_HICRA_IO2, // 3 ++ ASPEED_HICRA_UART4, // 4 ++ ASPEED_HICRA_UART1, // 5 ++ ASPEED_HICRA_UART2, // 6 ++ ASPEED_HICRA_IO6, // 7 ++ NULL, // NULL termination ++ }, ++}; ++ ++static struct aspeed_uart_routing_selector uart2_sel = { ++ .dev_attr = ROUTING_ATTR(ASPEED_HICRA_UART2), ++ .shift = 19, ++ .mask = 0x7, ++ .options = { ++ ASPEED_HICRA_IO2, // 0 ++ ASPEED_HICRA_IO3, // 1 ++ ASPEED_HICRA_IO4, // 2 ++ ASPEED_HICRA_IO1, // 3 ++ ASPEED_HICRA_UART3, // 4 ++ ASPEED_HICRA_UART4, // 5 ++ ASPEED_HICRA_UART1, // 6 ++ ASPEED_HICRA_IO6, // 7 ++ NULL, // NULL termination ++ }, ++}; ++ ++static struct aspeed_uart_routing_selector uart1_sel = { ++ .dev_attr = ROUTING_ATTR(ASPEED_HICRA_UART1), ++ .shift = 16, ++ .mask = 0x7, ++ .options = { ++ ASPEED_HICRA_IO1, // 0 ++ ASPEED_HICRA_IO2, // 1 ++ ASPEED_HICRA_IO3, // 2 ++ ASPEED_HICRA_IO4, // 3 ++ ASPEED_HICRA_UART2, // 4 ++ ASPEED_HICRA_UART3, // 5 ++ ASPEED_HICRA_UART4, // 6 ++ ASPEED_HICRA_IO6, // 7 ++ NULL, // NULL termination ++ }, ++}; ++ ++static struct aspeed_uart_routing_selector io5_sel = { ++ .dev_attr = ROUTING_ATTR(ASPEED_HICRA_IO5), ++ .shift = 12, ++ .mask = 0x7, ++ .options = { ++ ASPEED_HICRA_UART5, // 0 ++ ASPEED_HICRA_UART1, // 1 ++ ASPEED_HICRA_UART2, // 2 ++ ASPEED_HICRA_UART3, // 3 ++ ASPEED_HICRA_UART4, // 4 ++ ASPEED_HICRA_IO1, // 5 ++ ASPEED_HICRA_IO3, // 6 ++ ASPEED_HICRA_IO6, // 7 ++ NULL, // NULL termination ++ }, ++}; ++ ++static struct aspeed_uart_routing_selector io4_sel = { ++ .dev_attr = ROUTING_ATTR(ASPEED_HICRA_IO4), ++ .shift = 9, ++ .mask = 0x7, ++ .options = { ++ ASPEED_HICRA_UART4, // 0 ++ ASPEED_HICRA_UART5, // 1 ++ ASPEED_HICRA_UART1, // 2 ++ ASPEED_HICRA_UART2, // 3 ++ ASPEED_HICRA_UART3, // 4 ++ ASPEED_HICRA_IO1, // 5 ++ ASPEED_HICRA_IO2, // 6 ++ ASPEED_HICRA_IO6, // 7 ++ NULL, // NULL termination ++ }, ++}; ++ ++static struct aspeed_uart_routing_selector io3_sel = { ++ .dev_attr = ROUTING_ATTR(ASPEED_HICRA_IO3), ++ .shift = 6, ++ .mask = 0x7, ++ .options = { ++ ASPEED_HICRA_UART3, // 0 ++ ASPEED_HICRA_UART4, // 1 ++ ASPEED_HICRA_UART5, // 2 ++ ASPEED_HICRA_UART1, // 3 ++ ASPEED_HICRA_UART2, // 4 ++ ASPEED_HICRA_IO1, // 5 ++ ASPEED_HICRA_IO2, // 6 ++ ASPEED_HICRA_IO6, // 7 ++ NULL, // NULL termination ++ }, ++}; ++ ++static struct aspeed_uart_routing_selector io2_sel = { ++ .dev_attr = ROUTING_ATTR(ASPEED_HICRA_IO2), ++ .shift = 3, ++ .mask = 0x7, ++ .options = { ++ ASPEED_HICRA_UART2, // 0 ++ ASPEED_HICRA_UART3, // 1 ++ ASPEED_HICRA_UART4, // 2 ++ ASPEED_HICRA_UART5, // 3 ++ ASPEED_HICRA_UART1, // 4 ++ ASPEED_HICRA_IO3, // 5 ++ ASPEED_HICRA_IO4, // 6 ++ ASPEED_HICRA_IO6, // 7 ++ NULL, // NULL termination ++ }, ++}; ++ ++static struct aspeed_uart_routing_selector io1_sel = { ++ .dev_attr = ROUTING_ATTR(ASPEED_HICRA_IO1), ++ .shift = 0, ++ .mask = 0x7, ++ .options = { ++ ASPEED_HICRA_UART1, // 0 ++ ASPEED_HICRA_UART2, // 1 ++ ASPEED_HICRA_UART3, // 2 ++ ASPEED_HICRA_UART4, // 3 ++ ASPEED_HICRA_UART5, // 4 ++ ASPEED_HICRA_IO3, // 5 ++ ASPEED_HICRA_IO4, // 6 ++ ASPEED_HICRA_IO6, // 7 ++ NULL, // NULL termination ++ }, ++}; ++ ++ ++static struct attribute *aspeed_uart_routing_attrs[] = { ++ &uart1_sel.dev_attr.attr, ++ &uart2_sel.dev_attr.attr, ++ &uart3_sel.dev_attr.attr, ++ &uart4_sel.dev_attr.attr, ++ &uart5_sel.dev_attr.attr, ++ &io1_sel.dev_attr.attr, ++ &io2_sel.dev_attr.attr, ++ &io3_sel.dev_attr.attr, ++ &io4_sel.dev_attr.attr, ++ &io5_sel.dev_attr.attr, ++ NULL, ++}; ++ ++static const struct attribute_group aspeed_uart_routing_attr_group = { ++ .attrs = aspeed_uart_routing_attrs, ++}; ++ ++static ssize_t aspeed_uart_routing_show(struct device *dev, ++ struct device_attribute *attr, ++ char *buf) ++{ ++ struct aspeed_uart_routing *uart_routing = dev_get_drvdata(dev); ++ struct aspeed_uart_routing_selector *sel = to_routing_selector(attr); ++ int val, pos, len; ++ ++ val = (readl(uart_routing->regs) >> sel->shift) & sel->mask; ++ ++ len = 0; ++ for (pos = 0; sel->options[pos] != NULL; ++pos) { ++ if (pos == val) { ++ len += snprintf(buf + len, PAGE_SIZE - 1 - len, ++ "[%s] ", sel->options[pos]); ++ } else { ++ len += snprintf(buf + len, PAGE_SIZE - 1 - len, ++ "%s ", sel->options[pos]); ++ } ++ } ++ ++ if (val >= pos) { ++ len += snprintf(buf + len, PAGE_SIZE - 1 - len, ++ "[unknown(%d)]", val); ++ } ++ ++ len += snprintf(buf + len, PAGE_SIZE - 1 - len, "\n"); ++ ++ return len; ++} ++ ++static ssize_t aspeed_uart_routing_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ struct aspeed_uart_routing *uart_routing = dev_get_drvdata(dev); ++ struct aspeed_uart_routing_selector *sel = to_routing_selector(attr); ++ int val; ++ u32 reg; ++ ++ val = match_string(sel->options, -1, buf); ++ if (val < 0) { ++ dev_err(dev, "invalid value \"%s\"\n", buf); ++ return -EINVAL; ++ } ++ ++ spin_lock(&uart_routing->lock); ++ reg = readl(uart_routing->regs); ++ // Zero out existing value in specified bits. ++ reg &= ~(sel->mask << sel->shift); ++ // Set new value in specified bits. ++ reg |= (val & sel->mask) << sel->shift; ++ writel(reg, uart_routing->regs); ++ spin_unlock(&uart_routing->lock); ++ ++ return count; ++} ++ ++static int aspeed_uart_routing_probe(struct platform_device *pdev) ++{ ++ struct aspeed_uart_routing *uart_routing; ++ struct resource *res; ++ int rc; ++ ++ uart_routing = devm_kzalloc(&pdev->dev, ++ sizeof(*uart_routing), ++ GFP_KERNEL); ++ if (!uart_routing) ++ return -ENOMEM; ++ ++ spin_lock_init(&uart_routing->lock); ++ uart_routing->dev = &pdev->dev; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ uart_routing->regs = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(uart_routing->regs)) ++ return PTR_ERR(uart_routing->regs); ++ ++ rc = sysfs_create_group(&uart_routing->dev->kobj, ++ &aspeed_uart_routing_attr_group); ++ if (rc < 0) ++ return rc; ++ ++ platform_set_drvdata(pdev, uart_routing); ++ ++ return 0; ++} ++ ++static int aspeed_uart_routing_remove(struct platform_device *pdev) ++{ ++ struct aspeed_uart_routing *uart_routing = platform_get_drvdata(pdev); ++ ++ sysfs_remove_group(&uart_routing->dev->kobj, ++ &aspeed_uart_routing_attr_group); ++ ++ return 0; ++} ++ ++static const struct of_device_id aspeed_uart_routing_table[] = { ++ { .compatible = "aspeed,ast2500-uart-routing" }, ++ { }, ++}; ++ ++static struct platform_driver aspeed_uart_routing_driver = { ++ .driver = { ++ .name = "aspeed-uart-routing", ++ .of_match_table = aspeed_uart_routing_table, ++ }, ++ .probe = aspeed_uart_routing_probe, ++ .remove = aspeed_uart_routing_remove, ++}; ++ ++module_platform_driver(aspeed_uart_routing_driver); ++ ++MODULE_AUTHOR("Oskar Senft <osk@google.com>"); ++MODULE_LICENSE("GPL v2"); ++MODULE_DESCRIPTION("Driver to configure Aspeed UART routing"); +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0034-arm-dts-aspeed-Swap-the-mac-nodes-numbering.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0034-arm-dts-aspeed-Swap-the-mac-nodes-numbering.patch new file mode 100644 index 000000000..2f0fd3a31 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0034-arm-dts-aspeed-Swap-the-mac-nodes-numbering.patch @@ -0,0 +1,85 @@ +From 65c8090f2b418892aee9f239729fa417bd508a00 Mon Sep 17 00:00:00 2001 +From: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +Date: Wed, 3 Oct 2018 10:17:58 -0700 +Subject: [PATCH] arm: dts: aspeed: Swap the mac nodes numbering + +This patch swaps the numbering of mac0 and mac1 to make a dedicated +nic get assigned the first ethernet device number. + +Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +--- + arch/arm/boot/dts/aspeed-g4.dtsi | 16 ++++++++-------- + arch/arm/boot/dts/aspeed-g5.dtsi | 16 ++++++++-------- + 2 files changed, 16 insertions(+), 16 deletions(-) + +diff --git a/arch/arm/boot/dts/aspeed-g4.dtsi b/arch/arm/boot/dts/aspeed-g4.dtsi +index a70bee24d058..054d97229626 100644 +--- a/arch/arm/boot/dts/aspeed-g4.dtsi ++++ b/arch/arm/boot/dts/aspeed-g4.dtsi +@@ -120,14 +120,6 @@ + reg = <0x1e6c2000 0x80>; + }; + +- mac0: ethernet@1e660000 { +- compatible = "aspeed,ast2400-mac", "faraday,ftgmac100"; +- reg = <0x1e660000 0x180>; +- interrupts = <2>; +- clocks = <&syscon ASPEED_CLK_GATE_MAC1CLK>; +- status = "disabled"; +- }; +- + mac1: ethernet@1e680000 { + compatible = "aspeed,ast2400-mac", "faraday,ftgmac100"; + reg = <0x1e680000 0x180>; +@@ -136,6 +128,14 @@ + status = "disabled"; + }; + ++ mac0: ethernet@1e660000 { ++ compatible = "aspeed,ast2400-mac", "faraday,ftgmac100"; ++ reg = <0x1e660000 0x180>; ++ interrupts = <2>; ++ clocks = <&syscon ASPEED_CLK_GATE_MAC1CLK>; ++ status = "disabled"; ++ }; ++ + ehci0: usb@1e6a1000 { + compatible = "aspeed,ast2400-ehci", "generic-ehci"; + reg = <0x1e6a1000 0x100>; +diff --git a/arch/arm/boot/dts/aspeed-g5.dtsi b/arch/arm/boot/dts/aspeed-g5.dtsi +index 797013debaa7..27ed188296a1 100644 +--- a/arch/arm/boot/dts/aspeed-g5.dtsi ++++ b/arch/arm/boot/dts/aspeed-g5.dtsi +@@ -148,14 +148,6 @@ + reg = <0x1e6c2000 0x80>; + }; + +- mac0: ethernet@1e660000 { +- compatible = "aspeed,ast2500-mac", "faraday,ftgmac100"; +- reg = <0x1e660000 0x180>; +- interrupts = <2>; +- clocks = <&syscon ASPEED_CLK_GATE_MAC1CLK>; +- status = "disabled"; +- }; +- + mac1: ethernet@1e680000 { + compatible = "aspeed,ast2500-mac", "faraday,ftgmac100"; + reg = <0x1e680000 0x180>; +@@ -164,6 +156,14 @@ + status = "disabled"; + }; + ++ mac0: ethernet@1e660000 { ++ compatible = "aspeed,ast2500-mac", "faraday,ftgmac100"; ++ reg = <0x1e660000 0x180>; ++ interrupts = <2>; ++ clocks = <&syscon ASPEED_CLK_GATE_MAC1CLK>; ++ status = "disabled"; ++ }; ++ + ehci0: usb@1e6a1000 { + compatible = "aspeed,ast2500-ehci", "generic-ehci"; + reg = <0x1e6a1000 0x100>; +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0035-Implement-a-memory-driver-share-memory.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0035-Implement-a-memory-driver-share-memory.patch new file mode 100644 index 000000000..741b44462 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0035-Implement-a-memory-driver-share-memory.patch @@ -0,0 +1,245 @@ +From 9be0e8ccde0e096cc494aad6f15e7d7d3de3e46f Mon Sep 17 00:00:00 2001 +From: Cheng C Yang <cheng.c.yang@intel.com> +Date: Fri, 9 Nov 2018 10:24:37 +0800 +Subject: [PATCH] Implement a memory driver share memory + +Implement a memory driver for BMC to access VGA share memory. +The driver is used by MDRV2. In MDRV2 BIOS will send whole +SMBIOS table to VGA memory and BMC can get the table from VGA +memory through this driver. + +Signed-off-by: Cheng C Yang <cheng.c.yang@intel.com> +--- + .../bindings/soc/aspeed/aspeed-vga-sharedmem.txt | 20 +++ + drivers/soc/aspeed/Kconfig | 9 ++ + drivers/soc/aspeed/Makefile | 1 + + drivers/soc/aspeed/aspeed-vga-sharedmem.c | 163 +++++++++++++++++++++ + 4 files changed, 193 insertions(+) + create mode 100644 Documentation/devicetree/bindings/soc/aspeed/aspeed-vga-sharedmem.txt + create mode 100644 drivers/soc/aspeed/aspeed-vga-sharedmem.c + +diff --git a/Documentation/devicetree/bindings/soc/aspeed/aspeed-vga-sharedmem.txt b/Documentation/devicetree/bindings/soc/aspeed/aspeed-vga-sharedmem.txt +new file mode 100644 +index 000000000000..03f57c53e844 +--- /dev/null ++++ b/Documentation/devicetree/bindings/soc/aspeed/aspeed-vga-sharedmem.txt +@@ -0,0 +1,20 @@ ++* Aspeed VGA shared memory driver ++ ++Aspeed VGA shared memory driver allow user to read data from AST2500 ++VGA memory. This driver is required by ManagedDataRegionlV2 ++specification. In the spec, BIOS will transfer whole SMBIOS table to ++VGA memroy and BMC get the table from VGA memory. 0penBMC project do ++not allow to use /dev/mem for security concerns. To get the data in ++VGA shared memory in user space, implement this driver only allowed ++user to mmap limited memory area. ++ ++Required properties: ++- compatible: "aspeed,ast2500-vga-sharedmem" ++ - aspeed,ast2500-vga-sharedmem: Aspeed AST2500 family ++- reg: Should contain VGA shared memory start address and length ++ ++Example: ++vga-shared-memory { ++ compatible = "aspeed,ast2500-vga-sharedmem"; ++ reg = <0x9ff00000 0x100000>; ++}; +diff --git a/drivers/soc/aspeed/Kconfig b/drivers/soc/aspeed/Kconfig +index cb85917b8c6f..13cd9fbabbf1 100644 +--- a/drivers/soc/aspeed/Kconfig ++++ b/drivers/soc/aspeed/Kconfig +@@ -59,4 +59,13 @@ config ASPEED_XDMA + SOCs. The XDMA engine can perform automatic PCI DMA operations + between the AST2XXX (acting as a BMC) and a host processor. + ++config ASPEED_VGA_SHAREDMEM ++ tristate "Aspeed VGA Shared memory" ++ depends on SOC_ASPEED ++ help ++ To access VGA shared memory on Aspeed BMC, enable this option. ++ This driver used by ManagedDataRegionlV2 specification. In the ++ specification, BIOS will transfer whole SMBIOS table to VGA memory, ++ and BMC can get the table from VGA memory through this driver. ++ + endmenu +diff --git a/drivers/soc/aspeed/Makefile b/drivers/soc/aspeed/Makefile +index 56c5de5eeabe..875f0d9e7866 100644 +--- a/drivers/soc/aspeed/Makefile ++++ b/drivers/soc/aspeed/Makefile +@@ -6,3 +6,4 @@ obj-$(CONFIG_ASPEED_LPC_SIO) += aspeed-lpc-sio.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 ++obj-$(CONFIG_ASPEED_VGA_SHAREDMEM) += aspeed-vga-sharedmem.o +diff --git a/drivers/soc/aspeed/aspeed-vga-sharedmem.c b/drivers/soc/aspeed/aspeed-vga-sharedmem.c +new file mode 100644 +index 000000000000..cd1f5431378c +--- /dev/null ++++ b/drivers/soc/aspeed/aspeed-vga-sharedmem.c +@@ -0,0 +1,163 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2018 Intel Corporation ++ * VGA Shared Memory driver for Aspeed AST2500 ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/miscdevice.h> ++#include <linux/mm.h> ++#include <linux/module.h> ++#include <linux/of_platform.h> ++ ++#define SHAREDMEM_NAME "vgasharedmem" ++ ++struct aspeed_vga_sharedmem { ++ struct miscdevice miscdev; ++ unsigned int addr; ++ unsigned int size; ++ bool mmap_enable; ++}; ++ ++static struct aspeed_vga_sharedmem *file_sharemem(struct file *file) ++{ ++ return container_of(file->private_data, ++ struct aspeed_vga_sharedmem, miscdev); ++} ++ ++static int vga_open(struct inode *inode, struct file *file) ++{ ++ struct aspeed_vga_sharedmem *vga_sharedmem = file_sharemem(file); ++ ++ if (!capable(CAP_SYS_ADMIN)) ++ return -EPERM; ++ ++ if (!vga_sharedmem->mmap_enable) ++ return -EPERM; ++ ++ return 0; ++} ++ ++static int vga_mmap(struct file *file, struct vm_area_struct *vma) ++{ ++ struct aspeed_vga_sharedmem *vga_sharedmem = file_sharemem(file); ++ ++ if (!capable(CAP_SYS_ADMIN)) ++ return -EPERM; ++ ++ vma->vm_flags = (vma->vm_flags & (~VM_WRITE)); ++ remap_pfn_range(vma, vma->vm_start, vga_sharedmem->addr >> PAGE_SHIFT, ++ vga_sharedmem->size, vma->vm_page_prot); ++ return 0; ++} ++ ++static ssize_t enable_mmap_show(struct device *dev, ++ struct device_attribute *attr, ++ char *buf) ++{ ++ struct aspeed_vga_sharedmem *vga_sharedmem = dev_get_drvdata(dev); ++ ++ return sprintf(buf, "%u\n", vga_sharedmem->mmap_enable); ++} ++ ++static ssize_t enable_mmap_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ struct aspeed_vga_sharedmem *vga_sharedmem = ++ dev_get_drvdata(dev); ++ bool val; ++ ++ if (kstrtobool(buf, &val)) ++ return -EINVAL; ++ ++ vga_sharedmem->mmap_enable = val; ++ ++ return count; ++} ++static DEVICE_ATTR_RW(enable_mmap); ++ ++static struct attribute *sharedmem_attrs[] = { ++ &dev_attr_enable_mmap.attr, ++ NULL ++}; ++ ++static const struct attribute_group sharedmem_attr_group = { ++ .attrs = sharedmem_attrs, ++}; ++ ++static const struct attribute_group *sharedmem_attr_groups[] = { ++ &sharedmem_attr_group, ++ NULL ++}; ++ ++static const struct file_operations vga_sharedmem_fops = { ++ .owner = THIS_MODULE, ++ .open = vga_open, ++ .mmap = vga_mmap, ++}; ++ ++static struct miscdevice vga_sharedmem_miscdev = { ++ .minor = MISC_DYNAMIC_MINOR, ++ .name = SHAREDMEM_NAME, ++ .fops = &vga_sharedmem_fops, ++ .groups = sharedmem_attr_groups, ++}; ++ ++static int vga_sharedmem_probe(struct platform_device *pdev) ++{ ++ struct aspeed_vga_sharedmem *vga_sharedmem; ++ struct device *dev = &pdev->dev; ++ struct resource *rc; ++ ++ vga_sharedmem = devm_kzalloc(dev, sizeof(*vga_sharedmem), GFP_KERNEL); ++ if (!vga_sharedmem) ++ return -ENOMEM; ++ ++ dev_set_drvdata(&pdev->dev, vga_sharedmem); ++ ++ rc = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (!rc) { ++ dev_err(dev, "Couldn't read size device-tree property\n"); ++ return -ENXIO; ++ } ++ ++ vga_sharedmem->addr = rc->start; ++ vga_sharedmem->size = resource_size(rc); ++ vga_sharedmem->mmap_enable = true; ++ ++ vga_sharedmem->miscdev = vga_sharedmem_miscdev; ++ ++ return misc_register(&vga_sharedmem->miscdev); ++} ++ ++static int vga_sharedmem_remove(struct platform_device *pdev) ++{ ++ struct aspeed_vga_sharedmem *vga_sharedmem = ++ dev_get_drvdata(&pdev->dev); ++ ++ misc_deregister(&vga_sharedmem->miscdev); ++ ++ return 0; ++} ++ ++static const struct of_device_id vga_sharedmem_match[] = { ++ { .compatible = "aspeed,ast2500-vga-sharedmem", }, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, vga_sharedmem_match); ++ ++static struct platform_driver vga_sharedmem_driver = { ++ .driver = { ++ .name = "VGA-SHAREDMEM", ++ .of_match_table = vga_sharedmem_match, ++ }, ++ .probe = vga_sharedmem_probe, ++ .remove = vga_sharedmem_remove, ++}; ++ ++module_platform_driver(vga_sharedmem_driver); ++ ++MODULE_AUTHOR("Yang Cheng <cheng.c.yang@intel.com>"); ++MODULE_DESCRIPTION("Shared VGA memory"); ++MODULE_LICENSE("GPL v2"); +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0039-Add-Aspeed-PWM-driver-which-uses-FTTMR010-timer-IP.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0039-Add-Aspeed-PWM-driver-which-uses-FTTMR010-timer-IP.patch new file mode 100644 index 000000000..0fdd40d77 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0039-Add-Aspeed-PWM-driver-which-uses-FTTMR010-timer-IP.patch @@ -0,0 +1,514 @@ +From b20b1ce81f4451d9e906b84e7edcc22d4e70e7df Mon Sep 17 00:00:00 2001 +From: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +Date: Mon, 11 Feb 2019 17:02:35 -0800 +Subject: [PATCH] Add Aspeed PWM driver which uses FTTMR010 timer IP + +This commit adds Aspeed PWM driver which uses timer pulse output +feature in Aspeed SoCs. The timer IP is derived from Faraday +Technologies FTTMR010 IP but has some customized register +structure changes only for Aspeed SoCs. + +Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +--- + arch/arm/boot/dts/aspeed-g5.dtsi | 2 +- + drivers/pwm/Kconfig | 9 + + drivers/pwm/Makefile | 1 + + drivers/pwm/pwm-fttmr010.c | 441 +++++++++++++++++++++++++++++++++++++++ + 4 files changed, 452 insertions(+), 1 deletion(-) + create mode 100644 drivers/pwm/pwm-fttmr010.c + +diff --git a/arch/arm/boot/dts/aspeed-g5.dtsi b/arch/arm/boot/dts/aspeed-g5.dtsi +index 27ed188296a1..45202bc3d60c 100644 +--- a/arch/arm/boot/dts/aspeed-g5.dtsi ++++ b/arch/arm/boot/dts/aspeed-g5.dtsi +@@ -341,7 +341,7 @@ + + timer: timer@1e782000 { + /* This timer is a Faraday FTTMR010 derivative */ +- compatible = "aspeed,ast2400-timer"; ++ compatible = "aspeed,ast2500-timer"; + reg = <0x1e782000 0x90>; + interrupts = <16 17 18 35 36 37 38 39>; + clocks = <&syscon ASPEED_CLK_APB>; +diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig +index e3a2518503ed..21def8cd5af6 100644 +--- a/drivers/pwm/Kconfig ++++ b/drivers/pwm/Kconfig +@@ -171,6 +171,15 @@ config PWM_FSL_FTM + To compile this driver as a module, choose M here: the module + will be called pwm-fsl-ftm. + ++config PWM_FTTMR010 ++ tristate "Faraday Technology FTTMR010 timer PWM support" ++ help ++ Generic PWM framework driver for Faraday Technology FTTMR010 Timer ++ PWM output ++ ++ To compile this driver as a module, choose M here: the module ++ will be called pwm-fttmr010 ++ + config PWM_HIBVT + tristate "HiSilicon BVT PWM support" + depends on ARCH_HISI || COMPILE_TEST +diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile +index 26326adf71d7..3a9e9840b5ae 100644 +--- a/drivers/pwm/Makefile ++++ b/drivers/pwm/Makefile +@@ -15,6 +15,7 @@ obj-$(CONFIG_PWM_CRC) += pwm-crc.o + obj-$(CONFIG_PWM_CROS_EC) += pwm-cros-ec.o + obj-$(CONFIG_PWM_EP93XX) += pwm-ep93xx.o + obj-$(CONFIG_PWM_FSL_FTM) += pwm-fsl-ftm.o ++obj-$(CONFIG_PWM_FTTMR010) += pwm-fttmr010.o + obj-$(CONFIG_PWM_HIBVT) += pwm-hibvt.o + obj-$(CONFIG_PWM_IMG) += pwm-img.o + obj-$(CONFIG_PWM_IMX1) += pwm-imx1.o +diff --git a/drivers/pwm/pwm-fttmr010.c b/drivers/pwm/pwm-fttmr010.c +new file mode 100644 +index 000000000000..283ded6906f1 +--- /dev/null ++++ b/drivers/pwm/pwm-fttmr010.c +@@ -0,0 +1,441 @@ ++// SPDX-License-Identifier: GPL-2.0 ++// Copyright (c) 2019 Intel Corporation ++ ++#include <linux/clk.h> ++#include <linux/io.h> ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/of.h> ++#include <linux/platform_device.h> ++#include <linux/pwm.h> ++ ++#define TIMER_CR 0x30 ++ ++#define TIMER5_ASPEED_COUNT 0x50 ++#define TIMER5_ASPEED_LOAD 0x54 ++#define TIMER5_ASPEED_MATCH1 0x58 ++#define TIMER5_ASPEED_MATCH2 0x5c ++#define TIMER6_ASPEED_COUNT 0x60 ++#define TIMER6_ASPEED_LOAD 0x64 ++#define TIMER6_ASPEED_MATCH1 0x68 ++#define TIMER6_ASPEED_MATCH2 0x6c ++#define TIMER7_ASPEED_COUNT 0x70 ++#define TIMER7_ASPEED_LOAD 0x74 ++#define TIMER7_ASPEED_MATCH1 0x78 ++#define TIMER7_ASPEED_MATCH2 0x7c ++#define TIMER8_ASPEED_COUNT 0x80 ++#define TIMER8_ASPEED_LOAD 0x84 ++#define TIMER8_ASPEED_MATCH1 0x88 ++#define TIMER8_ASPEED_MATCH2 0x8c ++ ++#define TIMER_5_CR_ASPEED_ENABLE BIT(16) ++#define TIMER_5_CR_ASPEED_CLOCK BIT(17) ++#define TIMER_5_CR_ASPEED_INT BIT(18) ++#define TIMER_5_CR_ASPEED_PULSE_OUT BIT(19) ++#define TIMER_6_CR_ASPEED_ENABLE BIT(20) ++#define TIMER_6_CR_ASPEED_CLOCK BIT(21) ++#define TIMER_6_CR_ASPEED_INT BIT(22) ++#define TIMER_6_CR_ASPEED_PULSE_OUT BIT(23) ++#define TIMER_7_CR_ASPEED_ENABLE BIT(24) ++#define TIMER_7_CR_ASPEED_CLOCK BIT(25) ++#define TIMER_7_CR_ASPEED_INT BIT(26) ++#define TIMER_7_CR_ASPEED_PULSE_OUT BIT(27) ++#define TIMER_8_CR_ASPEED_ENABLE BIT(28) ++#define TIMER_8_CR_ASPEED_CLOCK BIT(29) ++#define TIMER_8_CR_ASPEED_INT BIT(30) ++#define TIMER_8_CR_ASPEED_PULSE_OUT BIT(31) ++ ++/** ++ * struct pwm_fttmr010_variant - variant data depends on SoC ++ * @bits: timer counter resolution ++ * @chan_min: lowest timer channel which has pwm pulse output ++ * @chan_max: highest timer channel which has pwm pulse output ++ * @output_mask: pwm pulse output mask which is defined in device tree ++ */ ++struct pwm_fttmr010_variant { ++ u8 bits; ++ u8 chan_min; ++ u8 chan_max; ++ u8 output_mask; ++}; ++ ++/** ++ * struct pwm_fttmr010_chan - private data of FTTMR010 PWM channel ++ * @period_ns: current period in nanoseconds programmed to the hardware ++ * @duty_ns: current duty time in nanoseconds programmed to the hardware ++ */ ++struct pwm_fttmr010_chan { ++ u32 period_ns; ++ u32 duty_ns; ++}; ++ ++/** ++ * struct pwm_fttmr010 - private data of FTTMR010 PWM ++ * @chip: generic PWM chip ++ * @variant: local copy of hardware variant data ++ * @disabled_mask: disabled status for all channels - one bit per channel ++ * @base: base address of mapped PWM registers ++ * @clk: clock used to drive the timers ++ */ ++struct pwm_fttmr010 { ++ struct pwm_chip chip; ++ struct pwm_fttmr010_variant variant; ++ u8 disabled_mask; ++ void __iomem *base; ++ struct clk *clk; ++ u32 clk_tick_ns; ++}; ++ ++static inline ++struct pwm_fttmr010 *to_pwm_fttmr010(struct pwm_chip *chip) ++{ ++ return container_of(chip, struct pwm_fttmr010, chip); ++} ++ ++static int pwm_fttmr010_request(struct pwm_chip *chip, struct pwm_device *pwm) ++{ ++ struct pwm_fttmr010 *priv = to_pwm_fttmr010(chip); ++ struct pwm_fttmr010_chan *chan; ++ ++ if (!(priv->variant.output_mask & BIT(pwm->hwpwm))) { ++ dev_warn(chip->dev, ++ "tried to request PWM channel %d without output\n", ++ pwm->hwpwm); ++ return -EINVAL; ++ } ++ ++ chan = devm_kzalloc(chip->dev, sizeof(*chan), GFP_KERNEL); ++ if (!chan) ++ return -ENOMEM; ++ ++ pwm_set_chip_data(pwm, chan); ++ ++ return 0; ++} ++ ++static void pwm_fttmr010_free(struct pwm_chip *chip, struct pwm_device *pwm) ++{ ++ devm_kfree(chip->dev, pwm_get_chip_data(pwm)); ++ pwm_set_chip_data(pwm, NULL); ++} ++ ++static int pwm_fttmr010_enable(struct pwm_chip *chip, struct pwm_device *pwm) ++{ ++ struct pwm_fttmr010 *priv = to_pwm_fttmr010(chip); ++ u32 cr; ++ ++ cr = readl(priv->base + TIMER_CR); ++ ++ switch (pwm->hwpwm) { ++ case 5: ++ cr |= (TIMER_5_CR_ASPEED_ENABLE | TIMER_5_CR_ASPEED_PULSE_OUT); ++ break; ++ case 6: ++ cr |= (TIMER_6_CR_ASPEED_ENABLE | TIMER_6_CR_ASPEED_PULSE_OUT); ++ break; ++ case 7: ++ cr |= (TIMER_7_CR_ASPEED_ENABLE | TIMER_7_CR_ASPEED_PULSE_OUT); ++ break; ++ case 8: ++ cr |= (TIMER_8_CR_ASPEED_ENABLE | TIMER_8_CR_ASPEED_PULSE_OUT); ++ break; ++ default: ++ return -ERANGE; ++ } ++ ++ writel(cr, priv->base + TIMER_CR); ++ priv->disabled_mask &= ~BIT(pwm->hwpwm); ++ ++ return 0; ++} ++ ++static void pwm_fttmr010_disable(struct pwm_chip *chip, struct pwm_device *pwm) ++{ ++ struct pwm_fttmr010 *priv = to_pwm_fttmr010(chip); ++ u32 cr; ++ ++ cr = readl(priv->base + TIMER_CR); ++ ++ switch (pwm->hwpwm) { ++ case 5: ++ cr &= ~(TIMER_5_CR_ASPEED_ENABLE | TIMER_5_CR_ASPEED_PULSE_OUT); ++ break; ++ case 6: ++ cr &= ~(TIMER_6_CR_ASPEED_ENABLE | TIMER_6_CR_ASPEED_PULSE_OUT); ++ break; ++ case 7: ++ cr &= ~(TIMER_7_CR_ASPEED_ENABLE | TIMER_7_CR_ASPEED_PULSE_OUT); ++ break; ++ case 8: ++ cr &= ~(TIMER_8_CR_ASPEED_ENABLE | TIMER_8_CR_ASPEED_PULSE_OUT); ++ break; ++ default: ++ return; ++ } ++ ++ writel(cr, priv->base + TIMER_CR); ++ priv->disabled_mask |= BIT(pwm->hwpwm); ++} ++ ++static int pwm_fttmr010_config(struct pwm_chip *chip, struct pwm_device *pwm, ++ int duty_ns, int period_ns) ++{ ++ u32 tload, tmatch, creg_offset, lreg_offset, mreg_offset; ++ struct pwm_fttmr010_chan *chan = pwm_get_chip_data(pwm); ++ struct pwm_fttmr010 *priv = to_pwm_fttmr010(chip); ++ ++ /* ++ * We currently avoid using 64bit arithmetic by using the ++ * fact that anything faster than 1Hz is easily representable ++ * by 32bits. ++ */ ++ if (period_ns > NSEC_PER_SEC) ++ return -ERANGE; ++ ++ /* No need to update */ ++ if (chan->period_ns == period_ns || chan->duty_ns == duty_ns) ++ return 0; ++ ++ tload = period_ns / priv->clk_tick_ns; ++ ++ /* Period is too short */ ++ if (tload <= 1) ++ return -ERANGE; ++ ++ tmatch = duty_ns / priv->clk_tick_ns; ++ ++ /* 0% duty is not available */ ++ if (!tmatch) ++ ++tmatch; ++ ++ tmatch = tload - tmatch; ++ ++ /* Decrement to get tick numbers, instead of tick counts */ ++ --tload; ++ --tmatch; ++ ++ if (tload == 0 || tmatch == 0) ++ return -ERANGE; ++ ++ dev_dbg(priv->chip.dev, "clk_tick_ns:%u, tload:%u, tmatch:%u\n", ++ priv->clk_tick_ns, tload, tmatch); ++ ++ switch (pwm->hwpwm) { ++ case 5: ++ creg_offset = TIMER5_ASPEED_COUNT; ++ lreg_offset = TIMER5_ASPEED_LOAD; ++ mreg_offset = TIMER5_ASPEED_MATCH1; ++ break; ++ case 6: ++ creg_offset = TIMER6_ASPEED_COUNT; ++ lreg_offset = TIMER6_ASPEED_LOAD; ++ mreg_offset = TIMER6_ASPEED_MATCH1; ++ break; ++ case 7: ++ creg_offset = TIMER7_ASPEED_COUNT; ++ lreg_offset = TIMER7_ASPEED_LOAD; ++ mreg_offset = TIMER7_ASPEED_MATCH1; ++ break; ++ case 8: ++ creg_offset = TIMER8_ASPEED_COUNT; ++ lreg_offset = TIMER8_ASPEED_LOAD; ++ mreg_offset = TIMER8_ASPEED_MATCH1; ++ break; ++ default: ++ return -ERANGE; ++ } ++ ++ writel(tload, priv->base + creg_offset); ++ writel(tload, priv->base + lreg_offset); ++ writel(tmatch, priv->base + mreg_offset); ++ ++ chan->period_ns = period_ns; ++ chan->duty_ns = duty_ns; ++ ++ return 0; ++} ++ ++static const struct pwm_ops pwm_fttmr010_ops = { ++ .request = pwm_fttmr010_request, ++ .free = pwm_fttmr010_free, ++ .enable = pwm_fttmr010_enable, ++ .disable = pwm_fttmr010_disable, ++ .config = pwm_fttmr010_config, ++ .owner = THIS_MODULE, ++}; ++ ++#ifdef CONFIG_OF ++static const struct pwm_fttmr010_variant aspeed_variant = { ++ .bits = 32, ++ .chan_min = 5, ++ .chan_max = 8, ++}; ++ ++static const struct of_device_id pwm_fttmr010_matches[] = { ++ { .compatible = "aspeed,ast2400-timer", .data = &aspeed_variant }, ++ { .compatible = "aspeed,ast2500-timer", .data = &aspeed_variant }, ++ { }, ++}; ++MODULE_DEVICE_TABLE(of, pwm_fttmr010_matches); ++ ++static int pwm_fttmr010_parse_dt(struct pwm_fttmr010 *priv) ++{ ++ struct device_node *np = priv->chip.dev->of_node; ++ const struct of_device_id *match; ++ struct property *prop; ++ const __be32 *cur; ++ u32 val; ++ ++ match = of_match_node(pwm_fttmr010_matches, np); ++ if (!match) ++ return -ENODEV; ++ ++ memcpy(&priv->variant, match->data, sizeof(priv->variant)); ++ ++ of_property_for_each_u32(np, "fttmr010,pwm-outputs", prop, cur, val) { ++ if (val < priv->variant.chan_min || ++ val > priv->variant.chan_max) { ++ dev_err(priv->chip.dev, ++ "invalid channel index in fttmr010,pwm-outputs property\n"); ++ continue; ++ } ++ priv->variant.output_mask |= BIT(val); ++ } ++ ++ return 0; ++} ++#else ++static int pwm_fttmr010_parse_dt(struct pwm_fttmr010 *priv) ++{ ++ return -ENODEV; ++} ++#endif ++ ++static int pwm_fttmr010_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct pwm_fttmr010 *priv; ++ struct resource *res; ++ ulong clk_rate; ++ int ret; ++ ++ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ priv->chip.dev = &pdev->dev; ++ priv->chip.ops = &pwm_fttmr010_ops; ++ priv->chip.base = -1; ++ ++ if (IS_ENABLED(CONFIG_OF) && pdev->dev.of_node) { ++ ret = pwm_fttmr010_parse_dt(priv); ++ if (ret) ++ return ret; ++ ++ priv->chip.of_xlate = of_pwm_xlate_with_flags; ++ priv->chip.of_pwm_n_cells = 3; ++ } else { ++ if (!pdev->dev.platform_data) { ++ dev_err(&pdev->dev, "no platform data specified\n"); ++ return -EINVAL; ++ } ++ ++ memcpy(&priv->variant, pdev->dev.platform_data, ++ sizeof(priv->variant)); ++ } ++ ++ priv->chip.npwm = priv->variant.chan_max + 1; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ priv->base = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(priv->base)) ++ return PTR_ERR(priv->base); ++ ++ priv->clk = devm_clk_get(&pdev->dev, "PCLK"); ++ if (IS_ERR(priv->clk)) { ++ dev_err(dev, "failed to get timer base clk\n"); ++ return PTR_ERR(priv->clk); ++ } ++ ++ ret = clk_prepare_enable(priv->clk); ++ if (ret < 0) { ++ dev_err(dev, "failed to enable base clock\n"); ++ return ret; ++ } ++ ++ clk_rate = clk_get_rate(priv->clk); ++ priv->clk_tick_ns = NSEC_PER_SEC / clk_rate; ++ ++ platform_set_drvdata(pdev, priv); ++ ++ ret = pwmchip_add(&priv->chip); ++ if (ret < 0) { ++ dev_err(dev, "failed to register PWM chip\n"); ++ clk_disable_unprepare(priv->clk); ++ return ret; ++ } ++ ++ dev_dbg(dev, "clk at %lu\n", clk_rate); ++ ++ return 0; ++} ++ ++static int pwm_fttmr010_remove(struct platform_device *pdev) ++{ ++ struct pwm_fttmr010 *priv = platform_get_drvdata(pdev); ++ int ret; ++ ++ ret = pwmchip_remove(&priv->chip); ++ if (ret < 0) ++ return ret; ++ ++ clk_disable_unprepare(priv->clk); ++ ++ return 0; ++} ++ ++#ifdef CONFIG_PM_SLEEP ++static int pwm_fttmr010_resume(struct device *dev) ++{ ++ struct pwm_fttmr010 *priv = dev_get_drvdata(dev); ++ struct pwm_chip *chip = &priv->chip; ++ unsigned int i; ++ ++ for (i = priv->variant.chan_min; i < priv->variant.chan_max; i++) { ++ struct pwm_device *pwm = &chip->pwms[i]; ++ struct pwm_fttmr010_chan *chan = pwm_get_chip_data(pwm); ++ ++ if (!chan) ++ continue; ++ ++ if (chan->period_ns) { ++ pwm_fttmr010_config(chip, pwm, chan->duty_ns, ++ chan->period_ns); ++ } ++ ++ if (priv->disabled_mask & BIT(i)) ++ pwm_fttmr010_disable(chip, pwm); ++ else ++ pwm_fttmr010_enable(chip, pwm); ++ } ++ ++ return 0; ++} ++#endif ++ ++static SIMPLE_DEV_PM_OPS(pwm_fttmr010_pm_ops, NULL, pwm_fttmr010_resume); ++ ++static struct platform_driver pwm_fttmr010_driver = { ++ .driver = { ++ .name = "fttmr010-timer-pwm", ++ .pm = &pwm_fttmr010_pm_ops, ++ .of_match_table = of_match_ptr(pwm_fttmr010_matches), ++ }, ++ .probe = pwm_fttmr010_probe, ++ .remove = pwm_fttmr010_remove, ++}; ++module_platform_driver(pwm_fttmr010_driver); ++ ++MODULE_AUTHOR("Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>"); ++MODULE_DESCRIPTION("FTTMR010 PWM Driver for timer pulse outputs"); ++MODULE_LICENSE("GPL v2"); +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0040-i2c-Add-mux-hold-unhold-msg-types.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0040-i2c-Add-mux-hold-unhold-msg-types.patch new file mode 100644 index 000000000..198beaa93 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0040-i2c-Add-mux-hold-unhold-msg-types.patch @@ -0,0 +1,488 @@ +From ee9cee93b96b791f52295d7763985fdb10903e2b Mon Sep 17 00:00:00 2001 +From: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +Date: Fri, 15 Feb 2019 16:05:09 -0800 +Subject: [PATCH] i2c: Add mux hold/unhold msg types + +This commit adds mux hold/unhold message types to support extended +mux control for IPMB and MCTP devices. A hold or an unhold message +can be added at the end of I2C message stream wrapped by +repeated-start, also can be used as a single message independantly. + +This mux hold/unhold message will be delivered throughout all mux +levels in the path. Means that if it goes to multi-level mux path, +all muxes will be held/unheld by this message. + +1. Hold message + struct i2c_msg msg; + uint16_t timeout = 5000; // timeout in ms. 5 secs in this example. + + msg.addr = 0x0; // any value can be used. addr will be ignored in this packet. + msg.flags = I2C_M_HOLD; // set this flag to indicate it's a hold message. + msg.len = sizeof(uint16_t); // timeout value will be delivered using two bytes buffer. + msg.buf = (uint8_t *)&timeout; // set timeout value. + +2. Unhold message + struct i2c_msg msg; + uint16_t timeout = 0; // set 0 for an unhold message. + + msg.addr = 0x0; // any value can be used. addr will be ignored in this packet. + msg.flags = I2C_M_HOLD; // set this flag to indicate it's an unhold message. + msg.len = sizeof(uint16_t); // timeout value will be delivered using two bytes buffer. + msg.buf = (uint8_t *)&timeout; // set timeout value. + + This unhold message can be delivered to a mux adapter even when + a bus is locked so that any holding state can be unheld + immediately by invoking this unhold message. + +This patch would not be welcomed from upstream so it should be kept +in downstream only. + +Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +--- + drivers/i2c/i2c-core-base.c | 70 +++++++++++++++++++++++++-- + drivers/i2c/i2c-core-smbus.c | 22 +++++++-- + drivers/i2c/i2c-mux.c | 109 +++++++++++++++++++++++++++++++++++++++---- + include/linux/i2c-mux.h | 3 ++ + include/linux/i2c.h | 25 ++++++++++ + include/uapi/linux/i2c.h | 1 + + 6 files changed, 214 insertions(+), 16 deletions(-) + +diff --git a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c +index 5f6a4985f2bc..d1cd7afe4068 100644 +--- a/drivers/i2c/i2c-core-base.c ++++ b/drivers/i2c/i2c-core-base.c +@@ -1297,6 +1297,25 @@ int i2c_handle_smbus_host_notify(struct i2c_adapter *adap, unsigned short addr) + } + EXPORT_SYMBOL_GPL(i2c_handle_smbus_host_notify); + ++static void i2c_adapter_hold(struct i2c_adapter *adapter, unsigned long timeout) ++{ ++ mutex_lock(&adapter->hold_lock); ++ mod_timer(&adapter->hold_timer, jiffies + timeout); ++} ++ ++static void i2c_adapter_unhold(struct i2c_adapter *adapter) ++{ ++ del_timer_sync(&adapter->hold_timer); ++ mutex_unlock(&adapter->hold_lock); ++} ++ ++static void i2c_adapter_hold_timer_callback(struct timer_list *t) ++{ ++ struct i2c_adapter *adapter = from_timer(adapter, t, hold_timer); ++ ++ i2c_adapter_unhold(adapter); ++} ++ + static int i2c_register_adapter(struct i2c_adapter *adap) + { + int res = -EINVAL; +@@ -1379,6 +1398,9 @@ static int i2c_register_adapter(struct i2c_adapter *adap) + bus_for_each_drv(&i2c_bus_type, NULL, adap, __process_new_adapter); + mutex_unlock(&core_lock); + ++ mutex_init(&adap->hold_lock); ++ timer_setup(&adap->hold_timer, i2c_adapter_hold_timer_callback, 0); ++ + return 0; + + out_reg: +@@ -1599,6 +1621,8 @@ void i2c_del_adapter(struct i2c_adapter *adap) + idr_remove(&i2c_adapter_idr, adap->nr); + mutex_unlock(&core_lock); + ++ i2c_adapter_unhold(adap); ++ + /* Clear the device structure in case this adapter is ever going to be + added again */ + memset(&adap->dev, 0, sizeof(adap->dev)); +@@ -1948,7 +1972,9 @@ static int i2c_check_for_quirks(struct i2c_adapter *adap, struct i2c_msg *msgs, + */ + int __i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) + { ++ enum i2c_hold_msg_type hold_msg = I2C_HOLD_MSG_NONE; + unsigned long orig_jiffies; ++ unsigned long timeout; + int ret, try; + + if (WARN_ON(!msgs || num < 1)) +@@ -1961,6 +1987,25 @@ int __i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) + if (adap->quirks && i2c_check_for_quirks(adap, msgs, num)) + return -EOPNOTSUPP; + ++ /* Do not deliver a mux hold msg to root bus adapter */ ++ if (!i2c_parent_is_i2c_adapter(adap)) { ++ hold_msg = i2c_check_hold_msg(msgs[num - 1].flags, ++ msgs[num - 1].len, ++ (u16 *)msgs[num - 1].buf); ++ if (hold_msg == I2C_HOLD_MSG_SET) { ++ timeout = msecs_to_jiffies(*(u16 *)msgs[num - 1].buf); ++ i2c_adapter_hold(adap, timeout); ++ ++ if (--num == 0) ++ return 0; ++ } else if (hold_msg == I2C_HOLD_MSG_RESET) { ++ i2c_adapter_unhold(adap); ++ return 0; ++ } else if (hold_msg == I2C_HOLD_MSG_NONE) { ++ mutex_lock(&adap->hold_lock); ++ } ++ } ++ + /* + * i2c_trace_msg_key gets enabled when tracepoint i2c_transfer gets + * enabled. This is an efficient way of keeping the for-loop from +@@ -1997,6 +2042,9 @@ int __i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) + trace_i2c_result(adap, num, ret); + } + ++ if (!i2c_parent_is_i2c_adapter(adap) && hold_msg == I2C_HOLD_MSG_NONE) ++ mutex_unlock(&adap->hold_lock); ++ + return ret; + } + EXPORT_SYMBOL(__i2c_transfer); +@@ -2015,6 +2063,7 @@ EXPORT_SYMBOL(__i2c_transfer); + */ + int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) + { ++ bool do_bus_lock = true; + int ret; + + if (!adap->algo->master_xfer) { +@@ -2038,12 +2087,25 @@ int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) + * one (discarding status on the second message) or errno + * (discarding status on the first one). + */ +- ret = __i2c_lock_bus_helper(adap); +- if (ret) +- return ret; ++ /* ++ * Do not lock a bus for delivering an unhold msg to a mux ++ * adpater. This is just for a single length unhold msg case. ++ */ ++ if (num == 1 && i2c_parent_is_i2c_adapter(adap) && ++ i2c_check_hold_msg(msgs[0].flags, msgs[0].len, ++ (u16 *)msgs[0].buf) == ++ I2C_HOLD_MSG_RESET) ++ do_bus_lock = false; ++ ++ if (do_bus_lock) { ++ ret = __i2c_lock_bus_helper(adap); ++ if (ret) ++ return ret; ++ } + + ret = __i2c_transfer(adap, msgs, num); +- i2c_unlock_bus(adap, I2C_LOCK_SEGMENT); ++ if (do_bus_lock) ++ i2c_unlock_bus(adap, I2C_LOCK_SEGMENT); + + return ret; + } +diff --git a/drivers/i2c/i2c-core-smbus.c b/drivers/i2c/i2c-core-smbus.c +index 3ac426a8ab5a..f7bf95101e34 100644 +--- a/drivers/i2c/i2c-core-smbus.c ++++ b/drivers/i2c/i2c-core-smbus.c +@@ -526,15 +526,29 @@ s32 i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, + unsigned short flags, char read_write, + u8 command, int protocol, union i2c_smbus_data *data) + { ++ bool do_bus_lock = true; + s32 res; + +- res = __i2c_lock_bus_helper(adapter); +- if (res) +- return res; ++ /* ++ * Do not lock a bus for delivering an unhold msg to a mux adpater. ++ * This is just for a single length unhold msg case. ++ */ ++ if (i2c_parent_is_i2c_adapter(adapter) && ++ i2c_check_hold_msg(flags, ++ protocol == I2C_SMBUS_WORD_DATA ? 2 : 0, ++ &data->word) == I2C_HOLD_MSG_RESET) ++ do_bus_lock = false; ++ ++ if (do_bus_lock) { ++ res = __i2c_lock_bus_helper(adapter); ++ if (res) ++ return res; ++ } + + res = __i2c_smbus_xfer(adapter, addr, flags, read_write, + command, protocol, data); +- i2c_unlock_bus(adapter, I2C_LOCK_SEGMENT); ++ if (do_bus_lock) ++ i2c_unlock_bus(adapter, I2C_LOCK_SEGMENT); + + return res; + } +diff --git a/drivers/i2c/i2c-mux.c b/drivers/i2c/i2c-mux.c +index 774507b54b57..c6e433238b22 100644 +--- a/drivers/i2c/i2c-mux.c ++++ b/drivers/i2c/i2c-mux.c +@@ -27,6 +27,7 @@ + #include <linux/of.h> + #include <linux/slab.h> + #include <linux/sysfs.h> ++#include <linux/timer.h> + + /* multiplexer per channel data */ + struct i2c_mux_priv { +@@ -36,21 +37,57 @@ struct i2c_mux_priv { + u32 chan_id; + }; + ++static void i2c_mux_hold(struct i2c_mux_core *muxc, unsigned long timeout) ++{ ++ mutex_lock(&muxc->hold_lock); ++ mod_timer(&muxc->hold_timer, jiffies + timeout); ++} ++ ++static void i2c_mux_unhold(struct i2c_mux_core *muxc) ++{ ++ del_timer_sync(&muxc->hold_timer); ++ mutex_unlock(&muxc->hold_lock); ++} ++ ++static void i2c_mux_hold_timer_callback(struct timer_list *t) ++{ ++ struct i2c_mux_core *muxc = from_timer(muxc, t, hold_timer); ++ ++ i2c_mux_unhold(muxc); ++} ++ + static int __i2c_mux_master_xfer(struct i2c_adapter *adap, + struct i2c_msg msgs[], int num) + { + struct i2c_mux_priv *priv = adap->algo_data; + struct i2c_mux_core *muxc = priv->muxc; + struct i2c_adapter *parent = muxc->parent; ++ enum i2c_hold_msg_type hold_msg; ++ unsigned long timeout; + int ret; + + /* Switch to the right mux port and perform the transfer. */ + ++ hold_msg = i2c_check_hold_msg(msgs[num - 1].flags, ++ msgs[num - 1].len, ++ (u16 *)msgs[num - 1].buf); ++ if (hold_msg == I2C_HOLD_MSG_SET) { ++ timeout = msecs_to_jiffies(*(u16 *)msgs[num - 1].buf); ++ i2c_mux_hold(muxc, timeout); ++ } else if (hold_msg == I2C_HOLD_MSG_NONE) { ++ mutex_lock(&muxc->hold_lock); ++ } + ret = muxc->select(muxc, priv->chan_id); + if (ret >= 0) + ret = __i2c_transfer(parent, msgs, num); +- if (muxc->deselect) +- muxc->deselect(muxc, priv->chan_id); ++ if (hold_msg != I2C_HOLD_MSG_SET) { ++ if (muxc->deselect) ++ muxc->deselect(muxc, priv->chan_id); ++ if (hold_msg == I2C_HOLD_MSG_RESET) ++ i2c_mux_unhold(muxc); ++ else ++ mutex_unlock(&muxc->hold_lock); ++ } + + return ret; + } +@@ -61,15 +98,32 @@ static int i2c_mux_master_xfer(struct i2c_adapter *adap, + struct i2c_mux_priv *priv = adap->algo_data; + struct i2c_mux_core *muxc = priv->muxc; + struct i2c_adapter *parent = muxc->parent; ++ enum i2c_hold_msg_type hold_msg; ++ unsigned long timeout; + int ret; + + /* Switch to the right mux port and perform the transfer. */ + ++ hold_msg = i2c_check_hold_msg(msgs[num - 1].flags, ++ msgs[num - 1].len, ++ (u16 *)msgs[num - 1].buf); ++ if (hold_msg == I2C_HOLD_MSG_SET) { ++ timeout = msecs_to_jiffies(*(u16 *)msgs[num - 1].buf); ++ i2c_mux_hold(muxc, timeout); ++ } else if (hold_msg == I2C_HOLD_MSG_NONE) { ++ mutex_lock(&muxc->hold_lock); ++ } + ret = muxc->select(muxc, priv->chan_id); + if (ret >= 0) + ret = i2c_transfer(parent, msgs, num); +- if (muxc->deselect) +- muxc->deselect(muxc, priv->chan_id); ++ if (hold_msg != I2C_HOLD_MSG_SET) { ++ if (muxc->deselect) ++ muxc->deselect(muxc, priv->chan_id); ++ if (hold_msg == I2C_HOLD_MSG_RESET) ++ i2c_mux_unhold(muxc); ++ else ++ mutex_unlock(&muxc->hold_lock); ++ } + + return ret; + } +@@ -82,16 +136,33 @@ static int __i2c_mux_smbus_xfer(struct i2c_adapter *adap, + struct i2c_mux_priv *priv = adap->algo_data; + struct i2c_mux_core *muxc = priv->muxc; + struct i2c_adapter *parent = muxc->parent; ++ enum i2c_hold_msg_type hold_msg; ++ unsigned long timeout; + int ret; + + /* Select the right mux port and perform the transfer. */ + ++ hold_msg = i2c_check_hold_msg(flags, ++ size == I2C_SMBUS_WORD_DATA ? 2 : 0, ++ &data->word); ++ if (hold_msg == I2C_HOLD_MSG_SET) { ++ timeout = msecs_to_jiffies(data->word); ++ i2c_mux_hold(muxc, timeout); ++ } else if (hold_msg == I2C_HOLD_MSG_NONE) { ++ mutex_lock(&muxc->hold_lock); ++ } + ret = muxc->select(muxc, priv->chan_id); + if (ret >= 0) + ret = __i2c_smbus_xfer(parent, addr, flags, + read_write, command, size, data); +- if (muxc->deselect) +- muxc->deselect(muxc, priv->chan_id); ++ if (hold_msg != I2C_HOLD_MSG_SET) { ++ if (muxc->deselect) ++ muxc->deselect(muxc, priv->chan_id); ++ if (hold_msg == I2C_HOLD_MSG_RESET) ++ i2c_mux_unhold(muxc); ++ else ++ mutex_unlock(&muxc->hold_lock); ++ } + + return ret; + } +@@ -104,16 +175,33 @@ static int i2c_mux_smbus_xfer(struct i2c_adapter *adap, + struct i2c_mux_priv *priv = adap->algo_data; + struct i2c_mux_core *muxc = priv->muxc; + struct i2c_adapter *parent = muxc->parent; ++ enum i2c_hold_msg_type hold_msg; ++ unsigned long timeout; + int ret; + + /* Select the right mux port and perform the transfer. */ + ++ hold_msg = i2c_check_hold_msg(flags, ++ size == I2C_SMBUS_WORD_DATA ? 2 : 0, ++ &data->word); ++ if (hold_msg == I2C_HOLD_MSG_SET) { ++ timeout = msecs_to_jiffies(data->word); ++ i2c_mux_hold(muxc, timeout); ++ } else if (hold_msg == I2C_HOLD_MSG_NONE) { ++ mutex_lock(&muxc->hold_lock); ++ } + ret = muxc->select(muxc, priv->chan_id); + if (ret >= 0) + ret = i2c_smbus_xfer(parent, addr, flags, + read_write, command, size, data); +- if (muxc->deselect) +- muxc->deselect(muxc, priv->chan_id); ++ if (hold_msg != I2C_HOLD_MSG_SET) { ++ if (muxc->deselect) ++ muxc->deselect(muxc, priv->chan_id); ++ if (hold_msg == I2C_HOLD_MSG_RESET) ++ i2c_mux_unhold(muxc); ++ else ++ mutex_unlock(&muxc->hold_lock); ++ } + + return ret; + } +@@ -263,6 +351,9 @@ struct i2c_mux_core *i2c_mux_alloc(struct i2c_adapter *parent, + muxc->deselect = deselect; + muxc->max_adapters = max_adapters; + ++ mutex_init(&muxc->hold_lock); ++ timer_setup(&muxc->hold_timer, i2c_mux_hold_timer_callback, 0); ++ + return muxc; + } + EXPORT_SYMBOL_GPL(i2c_mux_alloc); +@@ -441,6 +532,8 @@ void i2c_mux_del_adapters(struct i2c_mux_core *muxc) + { + char symlink_name[20]; + ++ i2c_mux_unhold(muxc); ++ + while (muxc->num_adapters) { + struct i2c_adapter *adap = muxc->adapter[--muxc->num_adapters]; + struct i2c_mux_priv *priv = adap->algo_data; +diff --git a/include/linux/i2c-mux.h b/include/linux/i2c-mux.h +index c5a977320f82..47f8763d6ed2 100644 +--- a/include/linux/i2c-mux.h ++++ b/include/linux/i2c-mux.h +@@ -27,6 +27,9 @@ struct i2c_mux_core { + int (*select)(struct i2c_mux_core *, u32 chan_id); + int (*deselect)(struct i2c_mux_core *, u32 chan_id); + ++ struct mutex hold_lock; /* mutex for channel holding */ ++ struct timer_list hold_timer; ++ + int num_adapters; + int max_adapters; + struct i2c_adapter *adapter[0]; +diff --git a/include/linux/i2c.h b/include/linux/i2c.h +index 1361637c369d..b4055d133338 100644 +--- a/include/linux/i2c.h ++++ b/include/linux/i2c.h +@@ -711,6 +711,13 @@ struct i2c_adapter { + const struct i2c_adapter_quirks *quirks; + + struct irq_domain *host_notify_domain; ++ ++ /* ++ * These will be used by root adpaters only. For muxes, each mux core ++ * has these individually. ++ */ ++ struct mutex hold_lock; /* mutex for bus holding */ ++ struct timer_list hold_timer; + }; + #define to_i2c_adapter(d) container_of(d, struct i2c_adapter, dev) + +@@ -1005,4 +1012,22 @@ static inline struct i2c_adapter *i2c_acpi_find_adapter_by_handle(acpi_handle ha + } + #endif /* CONFIG_ACPI */ + ++enum i2c_hold_msg_type { ++ I2C_HOLD_MSG_NONE, ++ I2C_HOLD_MSG_SET, ++ I2C_HOLD_MSG_RESET ++}; ++ ++static inline enum i2c_hold_msg_type i2c_check_hold_msg(u16 flags, u16 len, u16 *buf) ++{ ++ if (flags & I2C_M_HOLD && len == sizeof(u16)) { ++ if (*buf) ++ return I2C_HOLD_MSG_SET; ++ ++ return I2C_HOLD_MSG_RESET; ++ } ++ ++ return I2C_HOLD_MSG_NONE; ++} ++ + #endif /* _LINUX_I2C_H */ +diff --git a/include/uapi/linux/i2c.h b/include/uapi/linux/i2c.h +index f71a1751cacf..a1db9b17ed36 100644 +--- a/include/uapi/linux/i2c.h ++++ b/include/uapi/linux/i2c.h +@@ -72,6 +72,7 @@ struct i2c_msg { + #define I2C_M_RD 0x0001 /* read data, from slave to master */ + /* I2C_M_RD is guaranteed to be 0x0001! */ + #define I2C_M_TEN 0x0010 /* this is a ten bit chip address */ ++#define I2C_M_HOLD 0x0100 /* for holding a mux path */ + #define I2C_M_DMA_SAFE 0x0200 /* the buffer of this message is DMA safe */ + /* makes only sense in kernelspace */ + /* userspace buffers are copied anyway */ +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0042-Add-bus-timeout-ms-and-retries-device-tree-propertie.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0042-Add-bus-timeout-ms-and-retries-device-tree-propertie.patch new file mode 100644 index 000000000..5b9d4ef8b --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0042-Add-bus-timeout-ms-and-retries-device-tree-propertie.patch @@ -0,0 +1,105 @@ +From 37e7896bf297edef4f2877998ca7a5c086015591 Mon Sep 17 00:00:00 2001 +From: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +Date: Thu, 7 Mar 2019 15:17:40 -0800 +Subject: [PATCH] Add bus-timeout-ms and #retries device tree properties + +BMC uses I2C bus 7 as a PMBus channel to communicate with PSUs, +also ME uses this bus as SMLink to control PSUs so this bus is +managed by multi-masters. In this use case, some arbitration errors +are expected so we need to add retry logic. And PMBus subsystem +uses I2C bus in kernel internally so retry logic should be +supported in kernel level. + +To support the use case, this commit adds 'bus-timeout-ms' and +'#retries' device tree properties to set the bus specific +parameters at kernel boot time without using any additional ioctls +from user space. + +This patch would not be accepted by I2C maintainer in linux +upstream because he doesn't like adding these legacy properties +into device tree, so keep it only in downstream. + +Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +--- + Documentation/devicetree/bindings/i2c/i2c-aspeed.txt | 3 +++ + Documentation/devicetree/bindings/i2c/i2c.txt | 6 ++++++ + drivers/i2c/busses/i2c-aspeed.c | 1 - + drivers/i2c/i2c-core-base.c | 12 ++++++++++-- + 4 files changed, 19 insertions(+), 3 deletions(-) + +diff --git a/Documentation/devicetree/bindings/i2c/i2c-aspeed.txt b/Documentation/devicetree/bindings/i2c/i2c-aspeed.txt +index 8fbd8633a387..7da7e813b2b0 100644 +--- a/Documentation/devicetree/bindings/i2c/i2c-aspeed.txt ++++ b/Documentation/devicetree/bindings/i2c/i2c-aspeed.txt +@@ -16,6 +16,9 @@ Optional Properties: + - bus-frequency : frequency of the bus clock in Hz defaults to 100 kHz when not + specified + - multi-master : states that there is another master active on this bus. ++- bus-timeout-ms: bus timeout in milliseconds defaults to 1 second when not ++ specified. ++- #retries : Number of retries for master transfer. + + Example: + +diff --git a/Documentation/devicetree/bindings/i2c/i2c.txt b/Documentation/devicetree/bindings/i2c/i2c.txt +index 44efafdfd7f5..e382931cf3d6 100644 +--- a/Documentation/devicetree/bindings/i2c/i2c.txt ++++ b/Documentation/devicetree/bindings/i2c/i2c.txt +@@ -80,6 +80,12 @@ wants to support one of the below features, it should adapt the bindings below. + Names of map programmable addresses. + It can contain any map needing another address than default one. + ++- bus-timeout-ms ++ Bus timeout in milliseconds. ++ ++- #retries ++ Number of retries for master transfer. ++ + Binding may contain optional "interrupts" property, describing interrupts + used by the device. I2C core will assign "irq" interrupt (or the very first + interrupt if not using interrupt names) as primary interrupt for the slave. +diff --git a/drivers/i2c/busses/i2c-aspeed.c b/drivers/i2c/busses/i2c-aspeed.c +index c113ffa8d5df..7becfcd67142 100644 +--- a/drivers/i2c/busses/i2c-aspeed.c ++++ b/drivers/i2c/busses/i2c-aspeed.c +@@ -1032,7 +1032,6 @@ static int aspeed_i2c_probe_bus(struct platform_device *pdev) + spin_lock_init(&bus->lock); + init_completion(&bus->cmd_complete); + bus->adap.owner = THIS_MODULE; +- bus->adap.retries = 0; + bus->adap.algo = &aspeed_i2c_algo; + bus->adap.dev.parent = &pdev->dev; + bus->adap.dev.of_node = pdev->dev.of_node; +diff --git a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c +index d1cd7afe4068..1db991220fae 100644 +--- a/drivers/i2c/i2c-core-base.c ++++ b/drivers/i2c/i2c-core-base.c +@@ -1318,6 +1318,7 @@ static void i2c_adapter_hold_timer_callback(struct timer_list *t) + + static int i2c_register_adapter(struct i2c_adapter *adap) + { ++ u32 bus_timeout_ms = 0; + int res = -EINVAL; + + /* Can't register until after driver model init */ +@@ -1345,8 +1346,15 @@ static int i2c_register_adapter(struct i2c_adapter *adap) + INIT_LIST_HEAD(&adap->userspace_clients); + + /* Set default timeout to 1 second if not already set */ +- if (adap->timeout == 0) +- adap->timeout = HZ; ++ if (adap->timeout == 0) { ++ device_property_read_u32(&adap->dev, "bus-timeout-ms", ++ &bus_timeout_ms); ++ adap->timeout = bus_timeout_ms ? ++ msecs_to_jiffies(bus_timeout_ms) : HZ; ++ } ++ ++ /* Set retries count if it has the property setting */ ++ device_property_read_u32(&adap->dev, "#retries", &adap->retries); + + /* register soft irqs for Host Notify */ + res = i2c_setup_host_notify_irq_domain(adap); +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0043-char-ipmi-Add-clock-control-logic-into-Aspeed-LPC-BT.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0043-char-ipmi-Add-clock-control-logic-into-Aspeed-LPC-BT.patch new file mode 100644 index 000000000..6cc7d1138 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0043-char-ipmi-Add-clock-control-logic-into-Aspeed-LPC-BT.patch @@ -0,0 +1,154 @@ +From 301c1aaba9e59eb593406d878451e3ea4fe355bb Mon Sep 17 00:00:00 2001 +From: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +Date: Wed, 13 Mar 2019 15:04:16 -0700 +Subject: [PATCH] char: ipmi: Add clock control logic into Aspeed LPC BT driver + +If LPC BT driver is registered ahead of lpc-ctrl module, LPC BT +block will be enabled without heart beating of LCLK until lpc-ctrl +enables the LCLK. This issue causes improper handling on host +interrupts when the host sends interrupt in that time frame. Then +kernel eventually forcibly disables the interrupt with dumping +stack and printing a 'nobody cared this irq' message out. + +To prevent this issue, all LPC sub-nodes should enable LCLK +individually so this patch adds clock control logic into the LPC +BT driver. + +Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +Signed-off-by: Vernon Mauery <vernon.mauery@linux.intel.com> +--- + .../bindings/ipmi/aspeed,ast2400-ibt-bmc.txt | 3 +++ + arch/arm/boot/dts/aspeed-g4.dtsi | 1 + + arch/arm/boot/dts/aspeed-g5.dtsi | 1 + + arch/arm/boot/dts/aspeed-g6.dtsi | 1 + + drivers/char/ipmi/bt-bmc.c | 24 +++++++++++++++++++++- + 5 files changed, 29 insertions(+), 1 deletion(-) + +diff --git a/Documentation/devicetree/bindings/ipmi/aspeed,ast2400-ibt-bmc.txt b/Documentation/devicetree/bindings/ipmi/aspeed,ast2400-ibt-bmc.txt +index 028268fd99ee..d13887d60f19 100644 +--- a/Documentation/devicetree/bindings/ipmi/aspeed,ast2400-ibt-bmc.txt ++++ b/Documentation/devicetree/bindings/ipmi/aspeed,ast2400-ibt-bmc.txt +@@ -10,6 +10,8 @@ Required properties: + "aspeed,ast2400-ibt-bmc" + "aspeed,ast2500-ibt-bmc" + - reg: physical address and size of the registers ++- clocks: contains a phandle to the syscon node describing the clocks. ++ There should then be one cell representing the clock to use. + + Optional properties: + +@@ -22,4 +24,5 @@ Example: + compatible = "aspeed,ast2400-ibt-bmc"; + reg = <0x1e789140 0x18>; + interrupts = <8>; ++ clocks = <&syscon ASPEED_CLK_GATE_LCLK>; + }; +diff --git a/arch/arm/boot/dts/aspeed-g4.dtsi b/arch/arm/boot/dts/aspeed-g4.dtsi +index 054d97229626..db962ab435af 100644 +--- a/arch/arm/boot/dts/aspeed-g4.dtsi ++++ b/arch/arm/boot/dts/aspeed-g4.dtsi +@@ -386,6 +386,7 @@ + ibt: ibt@c0 { + compatible = "aspeed,ast2400-ibt-bmc"; + reg = <0xc0 0x18>; ++ clocks = <&syscon ASPEED_CLK_GATE_LCLK>; + interrupts = <8>; + status = "disabled"; + }; +diff --git a/arch/arm/boot/dts/aspeed-g5.dtsi b/arch/arm/boot/dts/aspeed-g5.dtsi +index 45202bc3d60c..c4724ec27041 100644 +--- a/arch/arm/boot/dts/aspeed-g5.dtsi ++++ b/arch/arm/boot/dts/aspeed-g5.dtsi +@@ -499,6 +499,7 @@ + ibt: ibt@c0 { + compatible = "aspeed,ast2500-ibt-bmc"; + reg = <0xc0 0x18>; ++ clocks = <&syscon ASPEED_CLK_GATE_LCLK>; + interrupts = <8>; + status = "disabled"; + }; +diff --git a/arch/arm/boot/dts/aspeed-g6.dtsi b/arch/arm/boot/dts/aspeed-g6.dtsi +index 8cc978058f16..a557a7e6fe8d 100644 +--- a/arch/arm/boot/dts/aspeed-g6.dtsi ++++ b/arch/arm/boot/dts/aspeed-g6.dtsi +@@ -524,6 +524,7 @@ + ibt: ibt@c0 { + compatible = "aspeed,ast2600-ibt-bmc"; + reg = <0xc0 0x18>; ++ clocks = <&syscon ASPEED_CLK_GATE_LCLK>; + interrupts = <GIC_SPI 143 IRQ_TYPE_LEVEL_HIGH>; + status = "disabled"; + }; +diff --git a/drivers/char/ipmi/bt-bmc.c b/drivers/char/ipmi/bt-bmc.c +index 0e600449931b..87bf3ff28542 100644 +--- a/drivers/char/ipmi/bt-bmc.c ++++ b/drivers/char/ipmi/bt-bmc.c +@@ -5,6 +5,7 @@ + + #include <linux/atomic.h> + #include <linux/bt-bmc.h> ++#include <linux/clk.h> + #include <linux/errno.h> + #include <linux/interrupt.h> + #include <linux/io.h> +@@ -60,6 +61,7 @@ struct bt_bmc { + struct device dev; + struct miscdevice miscdev; + struct regmap *map; ++ struct clk *clk; + int offset; + int irq; + wait_queue_head_t queue; +@@ -467,6 +469,19 @@ static int bt_bmc_probe(struct platform_device *pdev) + mutex_init(&bt_bmc->mutex); + init_waitqueue_head(&bt_bmc->queue); + ++ bt_bmc->clk = devm_clk_get(dev, NULL); ++ if (IS_ERR(bt_bmc->clk)) { ++ rc = PTR_ERR(bt_bmc->clk); ++ if (rc != -EPROBE_DEFER) ++ dev_err(dev, "couldn't get clock\n"); ++ return rc; ++ } ++ rc = clk_prepare_enable(bt_bmc->clk); ++ if (rc) { ++ dev_err(dev, "couldn't enable clock\n"); ++ return rc; ++ } ++ + bt_bmc->miscdev.minor = MISC_DYNAMIC_MINOR, + bt_bmc->miscdev.name = DEVICE_NAME, + bt_bmc->miscdev.fops = &bt_bmc_fops, +@@ -474,7 +489,7 @@ static int bt_bmc_probe(struct platform_device *pdev) + rc = misc_register(&bt_bmc->miscdev); + if (rc) { + dev_err(dev, "Unable to register misc device\n"); +- return rc; ++ goto err; + } + + bt_bmc_config_irq(bt_bmc, pdev); +@@ -498,6 +513,11 @@ static int bt_bmc_probe(struct platform_device *pdev) + clr_b_busy(bt_bmc); + + return 0; ++ ++err: ++ clk_disable_unprepare(bt_bmc->clk); ++ ++ return rc; + } + + static int bt_bmc_remove(struct platform_device *pdev) +@@ -507,6 +527,8 @@ static int bt_bmc_remove(struct platform_device *pdev) + misc_deregister(&bt_bmc->miscdev); + if (!bt_bmc->irq) + del_timer_sync(&bt_bmc->poll_timer); ++ clk_disable_unprepare(bt_bmc->clk); ++ + return 0; + } + +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0044-misc-Add-clock-control-logic-into-Aspeed-LPC-SNOOP-d.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0044-misc-Add-clock-control-logic-into-Aspeed-LPC-SNOOP-d.patch new file mode 100644 index 000000000..b3ace73f6 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0044-misc-Add-clock-control-logic-into-Aspeed-LPC-SNOOP-d.patch @@ -0,0 +1,139 @@ +From 404f73827714e324a21250b1a855fdc10fcfaf51 Mon Sep 17 00:00:00 2001 +From: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +Date: Wed, 13 Mar 2019 15:27:48 -0700 +Subject: [PATCH] misc: Add clock control logic into Aspeed LPC SNOOP driver + +If LPC SNOOP driver is registered ahead of lpc-ctrl module, LPC +SNOOP block will be enabled without heart beating of LCLK until +lpc-ctrl enables the LCLK. This issue causes improper handling on +host interrupts when the host sends interrupt in that time frame. +Then kernel eventually forcibly disables the interrupt with +dumping stack and printing a 'nobody cared this irq' message out. + +To prevent this issue, all LPC sub-nodes should enable LCLK +individually so this patch adds clock control logic into the LPC +SNOOP driver. + +Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +Signed-off-by: Vernon Mauery <vernon.mauery@linux.intel.com> +--- + arch/arm/boot/dts/aspeed-g4.dtsi | 1 + + arch/arm/boot/dts/aspeed-g5.dtsi | 1 + + arch/arm/boot/dts/aspeed-g6.dtsi | 1 + + drivers/soc/aspeed/aspeed-lpc-snoop.c | 30 +++++++++++++++++++++++++++--- + 4 files changed, 30 insertions(+), 3 deletions(-) + +diff --git a/arch/arm/boot/dts/aspeed-g4.dtsi b/arch/arm/boot/dts/aspeed-g4.dtsi +index db962ab435af..24a20384b5e8 100644 +--- a/arch/arm/boot/dts/aspeed-g4.dtsi ++++ b/arch/arm/boot/dts/aspeed-g4.dtsi +@@ -369,6 +369,7 @@ + compatible = "aspeed,ast2400-lpc-snoop"; + reg = <0x0 0x80>; + interrupts = <8>; ++ clocks = <&syscon ASPEED_CLK_GATE_LCLK>; + status = "disabled"; + }; + +diff --git a/arch/arm/boot/dts/aspeed-g5.dtsi b/arch/arm/boot/dts/aspeed-g5.dtsi +index c4724ec27041..18d2a465c0ed 100644 +--- a/arch/arm/boot/dts/aspeed-g5.dtsi ++++ b/arch/arm/boot/dts/aspeed-g5.dtsi +@@ -482,6 +482,7 @@ + compatible = "aspeed,ast2500-lpc-snoop"; + reg = <0x0 0x80>; + interrupts = <8>; ++ clocks = <&syscon ASPEED_CLK_GATE_LCLK>; + status = "disabled"; + }; + +diff --git a/arch/arm/boot/dts/aspeed-g6.dtsi b/arch/arm/boot/dts/aspeed-g6.dtsi +index a557a7e6fe8d..4035d7bd647e 100644 +--- a/arch/arm/boot/dts/aspeed-g6.dtsi ++++ b/arch/arm/boot/dts/aspeed-g6.dtsi +@@ -507,6 +507,7 @@ + compatible = "aspeed,ast2600-lpc-snoop"; + reg = <0x0 0x80>; + interrupts = <GIC_SPI 144 IRQ_TYPE_LEVEL_HIGH>; ++ clocks = <&syscon ASPEED_CLK_GATE_LCLK>; + status = "disabled"; + }; + +diff --git a/drivers/soc/aspeed/aspeed-lpc-snoop.c b/drivers/soc/aspeed/aspeed-lpc-snoop.c +index c7b4ac066b40..ed272677fc82 100644 +--- a/drivers/soc/aspeed/aspeed-lpc-snoop.c ++++ b/drivers/soc/aspeed/aspeed-lpc-snoop.c +@@ -11,6 +11,7 @@ + */ + + #include <linux/bitops.h> ++#include <linux/clk.h> + #include <linux/interrupt.h> + #include <linux/fs.h> + #include <linux/kfifo.h> +@@ -67,6 +68,7 @@ struct aspeed_lpc_snoop_channel { + struct aspeed_lpc_snoop { + struct regmap *regmap; + int irq; ++ struct clk *clk; + struct aspeed_lpc_snoop_channel chan[NUM_SNOOP_CHANNELS]; + }; + +@@ -282,22 +284,42 @@ static int aspeed_lpc_snoop_probe(struct platform_device *pdev) + return -ENODEV; + } + ++ lpc_snoop->clk = devm_clk_get(dev, NULL); ++ if (IS_ERR(lpc_snoop->clk)) { ++ rc = PTR_ERR(lpc_snoop->clk); ++ if (rc != -EPROBE_DEFER) ++ dev_err(dev, "couldn't get clock\n"); ++ return rc; ++ } ++ rc = clk_prepare_enable(lpc_snoop->clk); ++ if (rc) { ++ dev_err(dev, "couldn't enable clock\n"); ++ return rc; ++ } ++ + rc = aspeed_lpc_snoop_config_irq(lpc_snoop, pdev); + if (rc) +- return rc; ++ goto err; + + rc = aspeed_lpc_enable_snoop(lpc_snoop, dev, 0, port); + if (rc) +- return rc; ++ goto err; + + /* Configuration of 2nd snoop channel port is optional */ + if (of_property_read_u32_index(dev->of_node, "snoop-ports", + 1, &port) == 0) { + rc = aspeed_lpc_enable_snoop(lpc_snoop, dev, 1, port); +- if (rc) ++ if (rc) { + aspeed_lpc_disable_snoop(lpc_snoop, 0); ++ goto err; ++ } + } + ++ return 0; ++ ++err: ++ clk_disable_unprepare(lpc_snoop->clk); ++ + return rc; + } + +@@ -309,6 +331,8 @@ static int aspeed_lpc_snoop_remove(struct platform_device *pdev) + aspeed_lpc_disable_snoop(lpc_snoop, 0); + aspeed_lpc_disable_snoop(lpc_snoop, 1); + ++ clk_disable_unprepare(lpc_snoop->clk); ++ + return 0; + } + +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0045-char-ipmi-Add-clock-control-logic-into-Aspeed-LPC-KC.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0045-char-ipmi-Add-clock-control-logic-into-Aspeed-LPC-KC.patch new file mode 100644 index 000000000..1c0903d71 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0045-char-ipmi-Add-clock-control-logic-into-Aspeed-LPC-KC.patch @@ -0,0 +1,273 @@ +From cd62ca008a771bd6b7aeb06526c37d8435f86648 Mon Sep 17 00:00:00 2001 +From: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +Date: Wed, 13 Mar 2019 15:36:34 -0700 +Subject: [PATCH] char: ipmi: Add clock control logic into Aspeed LPC KCS + driver + +If LPC KCS driver is registered ahead of lpc-ctrl module, LPC KCS +block will be enabled without heart beating of LCLK until lpc-ctrl +enables the LCLK. This issue causes improper handling on host +interrupts when the host sends interrupt in that time frame. Then +kernel eventually forcibly disables the interrupt with dumping +stack and printing a 'nobody cared this irq' message out. + +To prevent this issue, all LPC sub-nodes should enable LCLK +individually so this patch adds clock control logic into the LPC +KCS driver. + +Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +Signed-off-by: Vernon Mauery <vernon.mauery@linux.intel.com> +--- + .../devicetree/bindings/ipmi/aspeed-kcs-bmc.txt | 3 ++ + arch/arm/boot/dts/aspeed-g4.dtsi | 35 ++++++++++++++++++++ + arch/arm/boot/dts/aspeed-g5.dtsi | 6 +++- + arch/arm/boot/dts/aspeed-g6.dtsi | 6 ++++ + drivers/char/ipmi/kcs_bmc_aspeed.c | 37 ++++++++++++++++++---- + 5 files changed, 79 insertions(+), 8 deletions(-) + +diff --git a/Documentation/devicetree/bindings/ipmi/aspeed-kcs-bmc.txt b/Documentation/devicetree/bindings/ipmi/aspeed-kcs-bmc.txt +index d98a9bf45d6c..3453eb0bf8f2 100644 +--- a/Documentation/devicetree/bindings/ipmi/aspeed-kcs-bmc.txt ++++ b/Documentation/devicetree/bindings/ipmi/aspeed-kcs-bmc.txt +@@ -9,6 +9,8 @@ Required properties: + "aspeed,ast2400-kcs-bmc" + "aspeed,ast2500-kcs-bmc" + - interrupts : interrupt generated by the controller ++- clocks: contains a phandle to the syscon node describing the clocks. ++ There should then be one cell representing the clock to use. + - kcs_chan : The LPC channel number in the controller + - kcs_addr : The host CPU IO map address + +@@ -19,6 +21,7 @@ Example: + compatible = "aspeed,ast2500-kcs-bmc"; + reg = <0x0 0x80>; + interrupts = <8>; ++ clocks = <&syscon ASPEED_CLK_GATE_LCLK>; + kcs_chan = <3>; + kcs_addr = <0xCA2>; + status = "okay"; +diff --git a/arch/arm/boot/dts/aspeed-g4.dtsi b/arch/arm/boot/dts/aspeed-g4.dtsi +index 24a20384b5e8..fc6f4e009db7 100644 +--- a/arch/arm/boot/dts/aspeed-g4.dtsi ++++ b/arch/arm/boot/dts/aspeed-g4.dtsi +@@ -347,6 +347,33 @@ + lpc_bmc: lpc-bmc@0 { + compatible = "aspeed,ast2400-lpc-bmc"; + reg = <0x0 0x80>; ++ reg-io-width = <4>; ++ ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ranges = <0x0 0x0 0x80>; ++ ++ kcs1: kcs1@0 { ++ compatible = "aspeed,ast2400-kcs-bmc"; ++ interrupts = <8>; ++ kcs_chan = <1>; ++ clocks = <&syscon ASPEED_CLK_GATE_LCLK>; ++ status = "disabled"; ++ }; ++ kcs2: kcs2@0 { ++ compatible = "aspeed,ast2400-kcs-bmc"; ++ interrupts = <8>; ++ kcs_chan = <2>; ++ clocks = <&syscon ASPEED_CLK_GATE_LCLK>; ++ status = "disabled"; ++ }; ++ kcs3: kcs3@0 { ++ compatible = "aspeed,ast2400-kcs-bmc"; ++ interrupts = <8>; ++ kcs_chan = <3>; ++ clocks = <&syscon ASPEED_CLK_GATE_LCLK>; ++ status = "disabled"; ++ }; + }; + + lpc_host: lpc-host@80 { +@@ -358,6 +385,14 @@ + #size-cells = <1>; + ranges = <0x0 0x80 0x1e0>; + ++ kcs4: kcs4@0 { ++ compatible = "aspeed,ast2400-kcs-bmc"; ++ interrupts = <8>; ++ kcs_chan = <4>; ++ clocks = <&syscon ASPEED_CLK_GATE_LCLK>; ++ status = "disabled"; ++ }; ++ + lpc_ctrl: lpc-ctrl@0 { + compatible = "aspeed,ast2400-lpc-ctrl"; + reg = <0x0 0x80>; +diff --git a/arch/arm/boot/dts/aspeed-g5.dtsi b/arch/arm/boot/dts/aspeed-g5.dtsi +index 18d2a465c0ed..751a8f0316d6 100644 +--- a/arch/arm/boot/dts/aspeed-g5.dtsi ++++ b/arch/arm/boot/dts/aspeed-g5.dtsi +@@ -134,7 +134,7 @@ + }; + + vic: interrupt-controller@1e6c0080 { +- compatible = "aspeed,ast2400-vic"; ++ compatible = "aspeed,ast2500-vic"; + interrupt-controller; + #interrupt-cells = <1>; + valid-sources = <0xfefff7ff 0x0807ffff>; +@@ -439,18 +439,21 @@ + compatible = "aspeed,ast2500-kcs-bmc"; + interrupts = <8>; + kcs_chan = <1>; ++ clocks = <&syscon ASPEED_CLK_GATE_LCLK>; + status = "disabled"; + }; + kcs2: kcs2@0 { + compatible = "aspeed,ast2500-kcs-bmc"; + interrupts = <8>; + kcs_chan = <2>; ++ clocks = <&syscon ASPEED_CLK_GATE_LCLK>; + status = "disabled"; + }; + kcs3: kcs3@0 { + compatible = "aspeed,ast2500-kcs-bmc"; + interrupts = <8>; + kcs_chan = <3>; ++ clocks = <&syscon ASPEED_CLK_GATE_LCLK>; + status = "disabled"; + }; + }; +@@ -468,6 +471,7 @@ + compatible = "aspeed,ast2500-kcs-bmc"; + interrupts = <8>; + kcs_chan = <4>; ++ clocks = <&syscon ASPEED_CLK_GATE_LCLK>; + status = "disabled"; + }; + +diff --git a/arch/arm/boot/dts/aspeed-g6.dtsi b/arch/arm/boot/dts/aspeed-g6.dtsi +index 4035d7bd647e..0e35c4598df5 100644 +--- a/arch/arm/boot/dts/aspeed-g6.dtsi ++++ b/arch/arm/boot/dts/aspeed-g6.dtsi +@@ -463,18 +463,23 @@ + kcs1: kcs1@0 { + compatible = "aspeed,ast2600-kcs-bmc"; + interrupts = <GIC_SPI 138 IRQ_TYPE_LEVEL_HIGH>; ++ clocks = <&syscon ASPEED_CLK_GATE_LCLK>; + kcs_chan = <1>; + status = "disabled"; + }; ++ + kcs2: kcs2@0 { + compatible = "aspeed,ast2600-kcs-bmc"; + interrupts = <GIC_SPI 139 IRQ_TYPE_LEVEL_HIGH>; ++ clocks = <&syscon ASPEED_CLK_GATE_LCLK>; + kcs_chan = <2>; + status = "disabled"; + }; ++ + kcs3: kcs3@0 { + compatible = "aspeed,ast2600-kcs-bmc"; + interrupts = <GIC_SPI 140 IRQ_TYPE_LEVEL_HIGH>; ++ clocks = <&syscon ASPEED_CLK_GATE_LCLK>; + kcs_chan = <3>; + status = "disabled"; + }; +@@ -492,6 +497,7 @@ + kcs4: kcs4@0 { + compatible = "aspeed,ast2600-kcs-bmc"; + interrupts = <GIC_SPI 141 IRQ_TYPE_LEVEL_HIGH>; ++ clocks = <&syscon ASPEED_CLK_GATE_LCLK>; + kcs_chan = <4>; + status = "disabled"; + }; +diff --git a/drivers/char/ipmi/kcs_bmc_aspeed.c b/drivers/char/ipmi/kcs_bmc_aspeed.c +index a0a8bb89c9b3..94cfb879f520 100644 +--- a/drivers/char/ipmi/kcs_bmc_aspeed.c ++++ b/drivers/char/ipmi/kcs_bmc_aspeed.c +@@ -1,11 +1,10 @@ + // SPDX-License-Identifier: GPL-2.0 +-/* +- * Copyright (c) 2015-2018, Intel Corporation. +- */ ++// Copyright (c) 2015-2019, Intel Corporation. + + #define pr_fmt(fmt) "aspeed-kcs-bmc: " fmt + + #include <linux/atomic.h> ++#include <linux/clk.h> + #include <linux/errno.h> + #include <linux/interrupt.h> + #include <linux/io.h> +@@ -63,6 +62,7 @@ + + struct aspeed_kcs_bmc { + struct regmap *map; ++ struct clk *clk; + }; + + +@@ -264,36 +264,59 @@ static int aspeed_kcs_probe(struct platform_device *pdev) + return -ENODEV; + } + ++ priv->clk = devm_clk_get(dev, NULL); ++ if (IS_ERR(priv->clk)) { ++ rc = PTR_ERR(priv->clk); ++ if (rc != -EPROBE_DEFER) ++ dev_err(dev, "couldn't get clock\n"); ++ return rc; ++ } ++ rc = clk_prepare_enable(priv->clk); ++ if (rc) { ++ dev_err(dev, "couldn't enable clock\n"); ++ return rc; ++ } ++ + kcs_bmc->ioreg = ast_kcs_bmc_ioregs[chan - 1]; + kcs_bmc->io_inputb = aspeed_kcs_inb; + kcs_bmc->io_outputb = aspeed_kcs_outb; + + dev_set_drvdata(dev, kcs_bmc); + +- aspeed_kcs_set_address(kcs_bmc, addr); +- aspeed_kcs_enable_channel(kcs_bmc, true); + rc = aspeed_kcs_config_irq(kcs_bmc, pdev); + if (rc) +- return rc; ++ goto err; + + rc = misc_register(&kcs_bmc->miscdev); + if (rc) { + dev_err(dev, "Unable to register device\n"); +- return rc; ++ goto err; + } + ++ aspeed_kcs_set_address(kcs_bmc, addr); ++ aspeed_kcs_enable_channel(kcs_bmc, true); ++ + pr_info("channel=%u addr=0x%x idr=0x%x odr=0x%x str=0x%x\n", + chan, addr, + kcs_bmc->ioreg.idr, kcs_bmc->ioreg.odr, kcs_bmc->ioreg.str); + + return 0; ++ ++err: ++ aspeed_kcs_enable_channel(kcs_bmc, false); ++ clk_disable_unprepare(priv->clk); ++ ++ return rc; + } + + static int aspeed_kcs_remove(struct platform_device *pdev) + { + struct kcs_bmc *kcs_bmc = dev_get_drvdata(&pdev->dev); ++ struct aspeed_kcs_bmc *priv = kcs_bmc_priv(kcs_bmc); + + misc_deregister(&kcs_bmc->miscdev); ++ aspeed_kcs_enable_channel(kcs_bmc, false); ++ clk_disable_unprepare(priv->clk); + + return 0; + } +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0047-misc-Block-error-printing-on-probe-defer-case-in-Asp.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0047-misc-Block-error-printing-on-probe-defer-case-in-Asp.patch new file mode 100644 index 000000000..8e9c793ba --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0047-misc-Block-error-printing-on-probe-defer-case-in-Asp.patch @@ -0,0 +1,43 @@ +From 5473931df3348b7284c16fac3e7d336c9d0c4294 Mon Sep 17 00:00:00 2001 +From: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +Date: Wed, 13 Mar 2019 15:57:08 -0700 +Subject: [PATCH] misc: Block error printing on probe defer case in Aspeed LPC + ctrl + +This commit adds a checking code when it gets -EPROBE_DEFER while +getting a clock resource. In this case it doesn't need to print +out an error message because the probing will be re-visited. + +Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +--- + drivers/soc/aspeed/aspeed-lpc-ctrl.c | 7 +++++-- + 1 file changed, 5 insertions(+), 2 deletions(-) + +diff --git a/drivers/soc/aspeed/aspeed-lpc-ctrl.c b/drivers/soc/aspeed/aspeed-lpc-ctrl.c +index f4ac14c40518..432128b8db87 100644 +--- a/drivers/soc/aspeed/aspeed-lpc-ctrl.c ++++ b/drivers/soc/aspeed/aspeed-lpc-ctrl.c +@@ -265,8 +265,10 @@ static int aspeed_lpc_ctrl_probe(struct platform_device *pdev) + + lpc_ctrl->clk = devm_clk_get(dev, NULL); + if (IS_ERR(lpc_ctrl->clk)) { +- dev_err(dev, "couldn't get clock\n"); +- return PTR_ERR(lpc_ctrl->clk); ++ rc = PTR_ERR(lpc_ctrl->clk); ++ if (rc != -EPROBE_DEFER) ++ dev_err(dev, "couldn't get clock\n"); ++ return rc; + } + rc = clk_prepare_enable(lpc_ctrl->clk); + if (rc) { +@@ -288,6 +290,7 @@ static int aspeed_lpc_ctrl_probe(struct platform_device *pdev) + + err: + clk_disable_unprepare(lpc_ctrl->clk); ++ + return rc; + } + +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0049-Suppress-excessive-HID-gadget-error-logs.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0049-Suppress-excessive-HID-gadget-error-logs.patch new file mode 100644 index 000000000..4a87f2d76 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0049-Suppress-excessive-HID-gadget-error-logs.patch @@ -0,0 +1,52 @@ +From 5b9ec5081492b461710cb82e7ecc93fd3af8ad34 Mon Sep 17 00:00:00 2001 +From: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +Date: Mon, 18 Mar 2019 14:06:36 -0700 +Subject: [PATCH] Suppress excessive HID gadget error logs + +HID events can be sent even when the host disconnects the HID +device according to the current graphic mode. For an example, if +KVM mouse events are sent when the host is in text mode, queueing +of end point messages will be dropped with this message: + +configfs-gadget gadget: usb_ep_queue error on int endpoint -108 + +This case is very usual case in BMC since BMC can control power +status of the host, so this commit suppress the error printing outs +with making HID gadget driver drop events quietly in the case. + +This should be a downstream only customization. Do not upstream it. + +Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +--- + drivers/usb/gadget/function/f_hid.c | 8 +++++--- + 1 file changed, 5 insertions(+), 3 deletions(-) + +diff --git a/drivers/usb/gadget/function/f_hid.c b/drivers/usb/gadget/function/f_hid.c +index f3816a5c861e..c96c0f6f1df0 100644 +--- a/drivers/usb/gadget/function/f_hid.c ++++ b/drivers/usb/gadget/function/f_hid.c +@@ -320,7 +320,7 @@ static void f_hidg_req_complete(struct usb_ep *ep, struct usb_request *req) + struct f_hidg *hidg = (struct f_hidg *)ep->driver_data; + unsigned long flags; + +- if (req->status != 0) { ++ if (req->status != 0 && req->status != -ESHUTDOWN) { + ERROR(hidg->func.config->cdev, + "End Point Request ERROR: %d\n", req->status); + } +@@ -395,8 +395,10 @@ static ssize_t f_hidg_write(struct file *file, const char __user *buffer, + + status = usb_ep_queue(hidg->in_ep, req, GFP_ATOMIC); + if (status < 0) { +- ERROR(hidg->func.config->cdev, +- "usb_ep_queue error on int endpoint %zd\n", status); ++ if (status != -ESHUTDOWN) ++ ERROR(hidg->func.config->cdev, ++ "usb_ep_queue error on int endpoint %zd\n", ++ status); + goto release_write_pending; + } else { + status = count; +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0051-Add-AST2500-JTAG-device.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0051-Add-AST2500-JTAG-device.patch new file mode 100644 index 000000000..4230c077b --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0051-Add-AST2500-JTAG-device.patch @@ -0,0 +1,56 @@ +From 1bf4e5a8cd7ca069092bfe7ac3fd3d1879b7a87d Mon Sep 17 00:00:00 2001 +From: "Hunt, Bryan" <bryan.hunt@intel.com> +Date: Mon, 6 May 2019 10:02:14 -0700 +Subject: [PATCH] Add AST2500 JTAG device + +Adding aspeed jtag device + +Signed-off-by: Hunt, Bryan <bryan.hunt@intel.com> +--- + arch/arm/boot/dts/aspeed-g5.dtsi | 9 +++++++++ + arch/arm/boot/dts/aspeed-g6.dtsi | 9 +++++++++ + 2 files changed, 18 insertions(+) + +diff --git a/arch/arm/boot/dts/aspeed-g5.dtsi b/arch/arm/boot/dts/aspeed-g5.dtsi +index 751a8f0316d6..eb1f9c9d9cca 100644 +--- a/arch/arm/boot/dts/aspeed-g5.dtsi ++++ b/arch/arm/boot/dts/aspeed-g5.dtsi +@@ -418,6 +418,15 @@ + pinctrl-0 = <&pinctrl_espi_default>; + }; + ++ jtag: jtag@1e6e4000 { ++ compatible = "aspeed,ast2500-jtag"; ++ reg = <0x1e6e4000 0x1c>; ++ clocks = <&syscon ASPEED_CLK_APB>; ++ resets = <&syscon ASPEED_RESET_JTAG_MASTER>; ++ interrupts = <43>; ++ status = "disabled"; ++ }; ++ + lpc: lpc@1e789000 { + compatible = "aspeed,ast2500-lpc", "simple-mfd"; + reg = <0x1e789000 0x1000>; +diff --git a/arch/arm/boot/dts/aspeed-g6.dtsi b/arch/arm/boot/dts/aspeed-g6.dtsi +index bc2ce43827fc..31903b08e731 100644 +--- a/arch/arm/boot/dts/aspeed-g6.dtsi ++++ b/arch/arm/boot/dts/aspeed-g6.dtsi +@@ -333,6 +333,15 @@ + quality = <100>; + }; + ++ jtag: jtag@1e6e4000 { ++ compatible = "aspeed,ast2600-jtag"; ++ reg = <0x1e6e4000 0x40>; ++ clocks = <&syscon ASPEED_CLK_APB1>; ++ resets = <&syscon ASPEED_RESET_JTAG_MASTER>; ++ interrupts = <GIC_SPI 27 IRQ_TYPE_LEVEL_HIGH>; ++ status = "disabled"; ++ }; ++ + adc: adc@1e6e9000 { + compatible = "aspeed,ast2500-adc"; + reg = <0x1e6e9000 0x100>; +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0052-drivers-jtag-Add-JTAG-core-driver.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0052-drivers-jtag-Add-JTAG-core-driver.patch new file mode 100644 index 000000000..484c576b9 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0052-drivers-jtag-Add-JTAG-core-driver.patch @@ -0,0 +1,920 @@ +From ea6fa8fd6e36c776d560b4f69d1ede4a3bbe5f6b Mon Sep 17 00:00:00 2001 +From: "Corona, Ernesto" <ernesto.corona@intel.com> +Date: Fri, 7 Jun 2019 07:37:39 -0800 +Subject: [PATCH v29 1/6] drivers: jtag: Add JTAG core driver + +JTAG class driver provide infrastructure to support hardware/software +JTAG platform drivers. It provide user layer API interface for flashing +and debugging external devices which equipped with JTAG interface +using standard transactions. + +Driver exposes set of IOCTL to user space for: +- XFER: + SIR (Scan Instruction Register, IEEE 1149.1 Data Register scan); + SDR (Scan Data Register, IEEE 1149.1 Instruction Register scan); +- GIOCSTATUS read the current TAPC state of the JTAG controller +- SIOCSTATE Forces the JTAG TAPC to go into a particular state. +- SIOCFREQ/GIOCFREQ for setting and reading JTAG frequency. +- IOCBITBANG for low level control of JTAG signals. + +Driver core provides set of internal APIs for allocation and +registration: +- jtag_register; +- jtag_unregister; +- jtag_alloc; +- jtag_free; + +Platform driver on registration with jtag-core creates the next +entry in dev folder: +/dev/jtagX + +Signed-off-by: Oleksandr Shamray <oleksandrs@mellanox.com> +Signed-off-by: Jiri Pirko <jiri@mellanox.com> +Signed-off-by: Corona, Ernesto <ernesto.corona@intel.com> +Acked-by: Philippe Ombredanne <pombredanne@nexb.com> +Cc: Vadim Pasternak <vadimp@mellanox.com> +Cc: Masahiro Yamada <yamada.masahiro@socionext.com> +Cc: Paul Burton <paul.burton@mips.com> +Cc: Thomas Gleixner <tglx@linutronix.de> +Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +Cc: Arnd Bergmann <arnd@arndb.de> +Cc: Boris Brezillon <bbrezillon@kernel.org> +Cc: Randy Dunlap <rdunlap@infradead.org> +Cc: Johan Hovold <johan@kernel.org> +Cc: Jens Axboe <axboe@kernel.dk> +Cc: Joel Stanley <joel@jms.id.au> +Cc: Palmer Dabbelt <palmer@sifive.com> +Cc: Kees Cook <keescook@chromium.org> +Cc: Steven A Filary <steven.a.filary@intel.com> +Cc: Bryan Hunt <bryan.hunt@intel.com> +--- +v28->v29 +Comments pointed by Steven Filary <steven.a.filary@intel.com> +- Expand bitbang function to accept multiples bitbang operations within a + single JTAG_IOCBITBANG call. It will receive a buffer with TDI and TMS + values and it is expected that driver fills TDO fields with its + corresponding output value for every transaction. +- Always setup JTAG controller to master mode but disable JTAG output when + the driver is not in use to allow other HW to own the JTAG bus. Remove SCU + register accesses. This register controls the JTAG controller mode + (master/slave). +- Fix static analysis issues +- Add support for multichain. Set tap state and xfer operations now include + two tap state arguments: current state and end state. + +v27->v28 +Comments pointed by Steven Filary <steven.a.filary@intel.com> +- Replace JTAG_IOCRUNTEST with JTAG_SIOCSTATE adding support for all TAPC + end states in SW mode using a lookup table to navigate across states. +- Add support for simultaneous READ/WRITE transfers(JTAG_READ_WRITE_XFER). +- Support for switching JTAG controller mode between slave and master + mode. +- Setup JTAG controller mode to master only when the driver is opened, + letting + other HW to own the JTAG bus when it isn't in use. +- Include JTAG bit bang IOCTL for low level JTAG control usage + (JTAG_IOCBITBANG). + +v24->v25 +Comments pointed by Greg KH <gregkh@linuxfoundation.org> +- set values to enums in jtag.h + +v23->v24 +Notifications from kbuild test robot <lkp@intel.com> +- Add include types.h header to jtag.h +- remove unecessary jtag_release + +v22->v23 +Comments pointed by Greg KH <gregkh@linuxfoundation.org> +- remove restriction of allocated JTAG devs- +- add validation fo idle values +- remove unnecessary blank line +- change retcode for xfer +- remove unecessary jtag_release callback +- remove unecessary defined fron jtag.h +- align in one line define JTAG_IOCRUNTEST + +v21->v22 +Comments pointed by Andy Shevchenko <andy.shevchenko@gmail.com> +- Fix 0x0f -> 0x0F in ioctl-number.txt +- Add description to #define MAX_JTAG_NAME_LEN +- Remove unnecessary entry *dev from struct jtag +- Remove redundant parens +- Described mandatory callbacks and removed unnecessary +- Set JTAG_MAX_XFER_DATA_LEN to power of 2 +- rework driver alloc/register to devm_ variant +- increasing line length up to 84 in order to improve readability. + +Comments pointed by Randy Dunlap <rdunlap@infradead.org> +- fix spell in ABI doccumentation + +v20->v21 + Comments pointed by Randy Dunlap <rdunlap@infradead.org> + - Fix JTAG dirver help in Kconfig + +v19->v20 +Comments pointed by Randy Dunlap <rdunlap@infradead.org> +- Fix JTAG dirver help in Kconfig + +Notifications from kbuild test robot <lkp@intel.com> +- fix incompatible type casts + +v18->v19 +Comments pointed by Julia Cartwright <juliac@eso.teric.us> +- Fix memory leak on jtag_alloc exit + +v17->v18 +Comments pointed by Julia Cartwright <juliac@eso.teric.us> +- Change to return -EOPNOTSUPP in case of error in JTAG_GIOCFREQ +- Add ops callbacks check to jtag_alloc +- Add err check for copy_to_user +- Move the kfree() above the if (err) in JTAG_IOCXFER +- remove unnecessary check for error after put_user +- add padding to struct jtag_xfer + +v16->v17 +Comments pointed by Julia Cartwright <juliac@eso.teric.us> +- Fix memory allocation on jtag alloc +- Move out unnecessary form lock on jtag open +- Rework jtag register behavior + +v15->v16 +Comments pointed by Florian Fainelli <f.fainelli@gmail.com> +- move check jtag->ops->* in ioctl before get_user() +- change error type -EINVAL --> -EBUSY on open already opened jtag +- remove unnecessary ARCH_DMA_MINALIGN flag from kzalloc +- remove define ARCH_DMA_MINALIGN + +v14->v15 +v13->v14 +Comments pointed by Philippe Ombredanne <pombredanne@nexb.com> +- Change style of head block comment from /**/ to // + +v12->v13 +Comments pointed by Philippe Ombredanne <pombredanne@nexb.com> +- Change jtag.c licence type to + SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note + and reorder line with license in description + +v11->v12 +Comments pointed by Greg KH <gregkh@linuxfoundation.org> +- Change jtag.h licence type to + SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note + and reorder line with license in description + +Comments pointed by Chip Bilbrey <chip@bilbrey.org> +- Remove Apeed reference from uapi jtag.h header +- Remove access mode from xfer and idle transactions +- Add new ioctl JTAG_SIOCMODE for set hw mode +- Add single open per device blocking + +v10->v11 +Notifications from kbuild test robot <lkp@intel.com> +- Add include types.h header to jtag.h +- fix incompatible type of xfer callback +- remove rdundant class defination +- Fix return order in case of xfer error + +V9->v10 +Comments pointed by Greg KH <gregkh@linuxfoundation.org> +- remove unnecessary alignment for pirv data +- move jtag_copy_to_user and jtag_copy_from_user code just to ioctl +- move int jtag_run_test_idle_op and jtag_xfer_op code + just to ioctl +- change return error codes to more applicable +- add missing error checks +- fix error check order in ioctl +- remove unnecessary blank lines +- add param validation to ioctl +- remove compat_ioctl +- remove only one open per JTAG port blocking. + User will care about this. +- Fix idr memory leak on jtag_exit +- change cdev device type to misc + +V8->v9 +Comments pointed by Arnd Bergmann <arnd@arndb.de> +- use get_user() instead of __get_user(). +- change jtag->open type from int to atomic_t +- remove spinlock on jtg_open +- remove mutex on jtag_register +- add unregister_chrdev_region on jtag_init err +- add unregister_chrdev_region on jtag_exit +- remove unnecessary pointer casts +- add *data parameter to xfer function prototype + +v7->v8 +Comments pointed by Moritz Fischer <moritz.fischer@ettus.com> +- Fix misspelling s/friver/driver + +v6->v7 +Notifications from kbuild test robot <lkp@intel.com> +- Remove include asm/types.h from jtag.h +- Add include <linux/types.h> to jtag.c + +v5->v6 +v4->v5 + +v3->v4 +Comments pointed by Arnd Bergmann <arnd@arndb.de> +- change transaction pointer tdio type to __u64 +- change internal status type from enum to __u32 +- reorder jtag_xfer members to avoid the implied padding +- add __packed attribute to jtag_xfer and jtag_run_test_idle + +v2->v3 +Notifications from kbuild test robot <lkp@intel.com> +- Change include path to <linux/types.h> in jtag.h + +v1->v2 +Comments pointed by Greg KH <gregkh@linuxfoundation.org> +- Change license type from GPLv2/BSD to GPLv2 +- Change type of variables which crossed user/kernel to __type +- Remove "default n" from Kconfig + +Comments pointed by Andrew Lunn <andrew@lunn.ch> +- Change list_add_tail in jtag_unregister to list_del + +Comments pointed by Neil Armstrong <narmstrong@baylibre.com> +- Add SPDX-License-Identifier instead of license text + +Comments pointed by Arnd Bergmann <arnd@arndb.de> +- Change __copy_to_user to memdup_user +- Change __put_user to put_user +- Change type of variables to __type for compatible 32 and 64-bit systems +- Add check for maximum xfer data size +- Change lookup data mechanism to get jtag data from inode +- Add .compat_ioctl to file ops +- Add mem alignment for jtag priv data + +Comments pointed by Tobias Klauser <tklauser@distanz.ch> +- Change function names to avoid match with variable types +- Fix description for jtag_ru_test_idle in uapi jtag.h +- Fix misprints IDEL/IDLE, trough/through +--- + drivers/Kconfig | 1 + + drivers/Makefile | 1 + + drivers/jtag/Kconfig | 17 +++ + drivers/jtag/Makefile | 1 + + drivers/jtag/jtag.c | 321 ++++++++++++++++++++++++++++++++++++++++++++++ + include/linux/jtag.h | 47 +++++++ + include/uapi/linux/jtag.h | 214 +++++++++++++++++++++++++++++++ + 7 files changed, 602 insertions(+) + create mode 100644 drivers/jtag/Kconfig + create mode 100644 drivers/jtag/Makefile + create mode 100644 drivers/jtag/jtag.c + create mode 100644 include/linux/jtag.h + create mode 100644 include/uapi/linux/jtag.h + +diff --git a/drivers/Kconfig b/drivers/Kconfig +index 4322efa37732..e5e4fe21b5d9 100644 +--- a/drivers/Kconfig ++++ b/drivers/Kconfig +@@ -230,4 +230,5 @@ source "drivers/counter/Kconfig" + + source "drivers/peci/Kconfig" + ++source "drivers/jtag/Kconfig" + endmenu +diff --git a/drivers/Makefile b/drivers/Makefile +index 82f78cfedf69..297047d4ed9b 100644 +--- a/drivers/Makefile ++++ b/drivers/Makefile +@@ -187,3 +187,4 @@ obj-$(CONFIG_GNSS) += gnss/ + obj-$(CONFIG_INTERCONNECT) += interconnect/ + obj-$(CONFIG_COUNTER) += counter/ + obj-$(CONFIG_PECI) += peci/ ++obj-$(CONFIG_JTAG_ASPEED) += jtag/ +diff --git a/drivers/jtag/Kconfig b/drivers/jtag/Kconfig +new file mode 100644 +index 000000000000..47771fcd3c5b +--- /dev/null ++++ b/drivers/jtag/Kconfig +@@ -0,0 +1,17 @@ ++menuconfig JTAG ++ tristate "JTAG support" ++ help ++ This provides basic core functionality support for JTAG class devices. ++ Hardware that is equipped with a JTAG microcontroller can be ++ supported by using this driver's interfaces. ++ This driver exposes a set of IOCTLs to the user space for ++ the following commands: ++ SDR: Performs an IEEE 1149.1 Data Register scan ++ SIR: Performs an IEEE 1149.1 Instruction Register scan. ++ RUNTEST: Forces the IEEE 1149.1 bus to a run state for a specified ++ number of clocks or a specified time period. ++ ++ If you want this support, you should say Y here. ++ ++ To compile this driver as a module, choose M here: the module will ++ be called jtag. +diff --git a/drivers/jtag/Makefile b/drivers/jtag/Makefile +new file mode 100644 +index 000000000000..af374939a9e6 +--- /dev/null ++++ b/drivers/jtag/Makefile +@@ -0,0 +1 @@ ++obj-$(CONFIG_JTAG) += jtag.o +diff --git a/drivers/jtag/jtag.c b/drivers/jtag/jtag.c +new file mode 100644 +index 000000000000..39a4d88a9c21 +--- /dev/null ++++ b/drivers/jtag/jtag.c +@@ -0,0 +1,321 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++// Copyright (c) 2018 Mellanox Technologies. All rights reserved. ++// Copyright (c) 2018 Oleksandr Shamray <oleksandrs@mellanox.com> ++// Copyright (c) 2019 Intel Corporation ++ ++#include <linux/cdev.h> ++#include <linux/device.h> ++#include <linux/jtag.h> ++#include <linux/kernel.h> ++#include <linux/list.h> ++#include <linux/miscdevice.h> ++#include <linux/module.h> ++#include <linux/rtnetlink.h> ++#include <linux/spinlock.h> ++#include <linux/types.h> ++#include <uapi/linux/jtag.h> ++ ++struct jtag { ++ struct miscdevice miscdev; ++ const struct jtag_ops *ops; ++ int id; ++ unsigned long priv[0]; ++}; ++ ++static DEFINE_IDA(jtag_ida); ++ ++void *jtag_priv(struct jtag *jtag) ++{ ++ return jtag->priv; ++} ++EXPORT_SYMBOL_GPL(jtag_priv); ++ ++static long jtag_ioctl(struct file *file, unsigned int cmd, unsigned long arg) ++{ ++ struct jtag *jtag = file->private_data; ++ struct jtag_tap_state tapstate; ++ struct jtag_xfer xfer; ++ struct bitbang_packet bitbang; ++ struct tck_bitbang *bitbang_data; ++ struct jtag_mode mode; ++ u8 *xfer_data; ++ u32 data_size; ++ u32 value; ++ int err; ++ ++ if (!arg) ++ return -EINVAL; ++ ++ switch (cmd) { ++ case JTAG_GIOCFREQ: ++ if (!jtag->ops->freq_get) ++ return -EOPNOTSUPP; ++ ++ err = jtag->ops->freq_get(jtag, &value); ++ if (err) ++ break; ++ ++ if (put_user(value, (__u32 __user *)arg)) ++ err = -EFAULT; ++ break; ++ ++ case JTAG_SIOCFREQ: ++ if (!jtag->ops->freq_set) ++ return -EOPNOTSUPP; ++ ++ if (get_user(value, (__u32 __user *)arg)) ++ return -EFAULT; ++ if (value == 0) ++ return -EINVAL; ++ ++ err = jtag->ops->freq_set(jtag, value); ++ break; ++ ++ case JTAG_SIOCSTATE: ++ if (copy_from_user(&tapstate, (const void __user *)arg, ++ sizeof(struct jtag_tap_state))) ++ return -EFAULT; ++ ++ if (tapstate.from > JTAG_STATE_CURRENT) ++ return -EINVAL; ++ ++ if (tapstate.endstate > JTAG_STATE_CURRENT) ++ return -EINVAL; ++ ++ if (tapstate.reset > JTAG_FORCE_RESET) ++ return -EINVAL; ++ ++ err = jtag->ops->status_set(jtag, &tapstate); ++ break; ++ ++ case JTAG_IOCXFER: ++ if (copy_from_user(&xfer, (const void __user *)arg, ++ sizeof(struct jtag_xfer))) ++ return -EFAULT; ++ ++ if (xfer.length >= JTAG_MAX_XFER_DATA_LEN) ++ return -EINVAL; ++ ++ if (xfer.type > JTAG_SDR_XFER) ++ return -EINVAL; ++ ++ if (xfer.direction > JTAG_READ_WRITE_XFER) ++ return -EINVAL; ++ ++ if (xfer.from > JTAG_STATE_CURRENT) ++ return -EINVAL; ++ ++ if (xfer.endstate > JTAG_STATE_CURRENT) ++ return -EINVAL; ++ ++ data_size = DIV_ROUND_UP(xfer.length, BITS_PER_BYTE); ++ xfer_data = memdup_user(u64_to_user_ptr(xfer.tdio), data_size); ++ if (IS_ERR(xfer_data)) ++ return -EFAULT; ++ ++ err = jtag->ops->xfer(jtag, &xfer, xfer_data); ++ if (err) { ++ kfree(xfer_data); ++ return err; ++ } ++ ++ err = copy_to_user(u64_to_user_ptr(xfer.tdio), ++ (void *)xfer_data, data_size); ++ kfree(xfer_data); ++ if (err) ++ return -EFAULT; ++ ++ if (copy_to_user((void __user *)arg, (void *)&xfer, ++ sizeof(struct jtag_xfer))) ++ return -EFAULT; ++ break; ++ ++ case JTAG_GIOCSTATUS: ++ err = jtag->ops->status_get(jtag, &value); ++ if (err) ++ break; ++ ++ err = put_user(value, (__u32 __user *)arg); ++ break; ++ case JTAG_IOCBITBANG: ++ if (copy_from_user(&bitbang, (const void __user *)arg, ++ sizeof(struct bitbang_packet))) ++ return -EFAULT; ++ ++ if (bitbang.length >= JTAG_MAX_XFER_DATA_LEN) ++ return -EINVAL; ++ ++ data_size = bitbang.length * sizeof(struct tck_bitbang); ++ bitbang_data = memdup_user((void __user *)bitbang.data, ++ data_size); ++ if (IS_ERR(bitbang_data)) ++ return -EFAULT; ++ ++ err = jtag->ops->bitbang(jtag, &bitbang, bitbang_data); ++ if (err) { ++ kfree(bitbang_data); ++ return err; ++ } ++ err = copy_to_user((void __user *)bitbang.data, ++ (void *)bitbang_data, data_size); ++ kfree(bitbang_data); ++ if (err) ++ return -EFAULT; ++ break; ++ case JTAG_SIOCMODE: ++ if (!jtag->ops->mode_set) ++ return -EOPNOTSUPP; ++ ++ if (copy_from_user(&mode, (const void __user *)arg, ++ sizeof(struct jtag_mode))) ++ return -EFAULT; ++ ++ err = jtag->ops->mode_set(jtag, &mode); ++ break; ++ ++ default: ++ return -EINVAL; ++ } ++ return err; ++} ++ ++static int jtag_open(struct inode *inode, struct file *file) ++{ ++ struct jtag *jtag = container_of(file->private_data, ++ struct jtag, ++ miscdev); ++ ++ file->private_data = jtag; ++ if (jtag->ops->enable(jtag)) ++ return -EBUSY; ++ return nonseekable_open(inode, file); ++} ++ ++static int jtag_release(struct inode *inode, struct file *file) ++{ ++ struct jtag *jtag = file->private_data; ++ ++ if (jtag->ops->disable(jtag)) ++ return -EBUSY; ++ return 0; ++} ++ ++static const struct file_operations jtag_fops = { ++ .owner = THIS_MODULE, ++ .open = jtag_open, ++ .llseek = noop_llseek, ++ .unlocked_ioctl = jtag_ioctl, ++ .release = jtag_release, ++}; ++ ++struct jtag *jtag_alloc(struct device *host, size_t priv_size, ++ const struct jtag_ops *ops) ++{ ++ struct jtag *jtag; ++ ++ if (!host) ++ return NULL; ++ ++ if (!ops) ++ return NULL; ++ ++ if (!ops->status_set || !ops->status_get || !ops->xfer) ++ return NULL; ++ ++ jtag = kzalloc(sizeof(*jtag) + priv_size, GFP_KERNEL); ++ if (!jtag) ++ return NULL; ++ ++ jtag->ops = ops; ++ jtag->miscdev.parent = host; ++ ++ return jtag; ++} ++EXPORT_SYMBOL_GPL(jtag_alloc); ++ ++void jtag_free(struct jtag *jtag) ++{ ++ kfree(jtag); ++} ++EXPORT_SYMBOL_GPL(jtag_free); ++ ++static int jtag_register(struct jtag *jtag) ++{ ++ struct device *dev = jtag->miscdev.parent; ++ int err; ++ int id; ++ ++ if (!dev) ++ return -ENODEV; ++ ++ id = ida_simple_get(&jtag_ida, 0, 0, GFP_KERNEL); ++ if (id < 0) ++ return id; ++ ++ jtag->id = id; ++ ++ jtag->miscdev.fops = &jtag_fops; ++ jtag->miscdev.minor = MISC_DYNAMIC_MINOR; ++ jtag->miscdev.name = kasprintf(GFP_KERNEL, "jtag%d", id); ++ if (!jtag->miscdev.name) { ++ err = -ENOMEM; ++ goto err_jtag_alloc; ++ } ++ ++ err = misc_register(&jtag->miscdev); ++ if (err) { ++ dev_err(jtag->miscdev.parent, "Unable to register device\n"); ++ goto err_jtag_name; ++ } ++ return 0; ++ ++err_jtag_name: ++ kfree(jtag->miscdev.name); ++err_jtag_alloc: ++ ida_simple_remove(&jtag_ida, id); ++ return err; ++} ++ ++static void jtag_unregister(struct jtag *jtag) ++{ ++ misc_deregister(&jtag->miscdev); ++ kfree(jtag->miscdev.name); ++ ida_simple_remove(&jtag_ida, jtag->id); ++} ++ ++static void devm_jtag_unregister(struct device *dev, void *res) ++{ ++ jtag_unregister(*(struct jtag **)res); ++} ++ ++int devm_jtag_register(struct device *dev, struct jtag *jtag) ++{ ++ struct jtag **ptr; ++ int ret; ++ ++ ptr = devres_alloc(devm_jtag_unregister, sizeof(struct jtag *), ++ GFP_KERNEL); ++ if (!ptr) ++ return -ENOMEM; ++ ++ ret = jtag_register(jtag); ++ if (!ret) { ++ *ptr = jtag; ++ devres_add(dev, ptr); ++ } else { ++ devres_free(ptr); ++ } ++ return ret; ++} ++EXPORT_SYMBOL_GPL(devm_jtag_register); ++ ++static void __exit jtag_exit(void) ++{ ++ ida_destroy(&jtag_ida); ++} ++ ++module_exit(jtag_exit); ++ ++MODULE_AUTHOR("Oleksandr Shamray <oleksandrs@mellanox.com>"); ++MODULE_DESCRIPTION("Generic jtag support"); ++MODULE_LICENSE("GPL v2"); +diff --git a/include/linux/jtag.h b/include/linux/jtag.h +new file mode 100644 +index 000000000000..fab12dc4fc5e +--- /dev/null ++++ b/include/linux/jtag.h +@@ -0,0 +1,47 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++/* Copyright (c) 2018 Mellanox Technologies. All rights reserved. */ ++/* Copyright (c) 2018 Oleksandr Shamray <oleksandrs@mellanox.com> */ ++/* Copyright (c) 2019 Intel Corporation */ ++ ++#ifndef __LINUX_JTAG_H ++#define __LINUX_JTAG_H ++ ++#include <linux/types.h> ++#include <uapi/linux/jtag.h> ++ ++#define JTAG_MAX_XFER_DATA_LEN 65535 ++ ++struct jtag; ++/** ++ * struct jtag_ops - callbacks for JTAG control functions: ++ * ++ * @freq_get: get frequency function. Filled by dev driver ++ * @freq_set: set frequency function. Filled by dev driver ++ * @status_get: get JTAG TAPC state function. Mandatory, Filled by dev driver ++ * @status_set: set JTAG TAPC state function. Mandatory, Filled by dev driver ++ * @xfer: send JTAG xfer function. Mandatory func. Filled by dev driver ++ * @mode_set: set specific work mode for JTAG. Filled by dev driver ++ * @bitbang: set low level bitbang operations. Filled by dev driver ++ * @enable: enables JTAG interface in master mode. Filled by dev driver ++ * @disable: disables JTAG interface master mode. Filled by dev driver ++ */ ++struct jtag_ops { ++ int (*freq_get)(struct jtag *jtag, u32 *freq); ++ int (*freq_set)(struct jtag *jtag, u32 freq); ++ int (*status_get)(struct jtag *jtag, u32 *state); ++ int (*status_set)(struct jtag *jtag, struct jtag_tap_state *endst); ++ int (*xfer)(struct jtag *jtag, struct jtag_xfer *xfer, u8 *xfer_data); ++ int (*mode_set)(struct jtag *jtag, struct jtag_mode *jtag_mode); ++ int (*bitbang)(struct jtag *jtag, struct bitbang_packet *bitbang, ++ struct tck_bitbang *bitbang_data); ++ int (*enable)(struct jtag *jtag); ++ int (*disable)(struct jtag *jtag); ++}; ++ ++void *jtag_priv(struct jtag *jtag); ++int devm_jtag_register(struct device *dev, struct jtag *jtag); ++struct jtag *jtag_alloc(struct device *host, size_t priv_size, ++ const struct jtag_ops *ops); ++void jtag_free(struct jtag *jtag); ++ ++#endif /* __LINUX_JTAG_H */ +diff --git a/include/uapi/linux/jtag.h b/include/uapi/linux/jtag.h +new file mode 100644 +index 000000000000..315e59577a17 +--- /dev/null ++++ b/include/uapi/linux/jtag.h +@@ -0,0 +1,214 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++/* Copyright (c) 2018 Mellanox Technologies. All rights reserved. */ ++/* Copyright (c) 2018 Oleksandr Shamray <oleksandrs@mellanox.com> */ ++/* Copyright (c) 2019 Intel Corporation */ ++ ++#ifndef __UAPI_LINUX_JTAG_H ++#define __UAPI_LINUX_JTAG_H ++ ++/* ++ * JTAG_XFER_MODE: JTAG transfer mode. Used to set JTAG controller transfer mode ++ * This is bitmask for feature param in jtag_mode for ioctl JTAG_SIOCMODE ++ */ ++#define JTAG_XFER_MODE 0 ++/* ++ * JTAG_CONTROL_MODE: JTAG controller mode. Used to set JTAG controller mode ++ * This is bitmask for feature param in jtag_mode for ioctl JTAG_SIOCMODE ++ */ ++#define JTAG_CONTROL_MODE 1 ++/* ++ * JTAG_MASTER_OUTPUT_DISABLE: JTAG master mode output disable, it is used to ++ * enable other devices to own the JTAG bus. ++ * This is bitmask for mode param in jtag_mode for ioctl JTAG_SIOCMODE ++ */ ++#define JTAG_MASTER_OUTPUT_DISABLE 0 ++/* ++ * JTAG_MASTER_MODE: JTAG master mode. Used to set JTAG controller master mode ++ * This is bitmask for mode param in jtag_mode for ioctl JTAG_SIOCMODE ++ */ ++#define JTAG_MASTER_MODE 1 ++/* ++ * JTAG_XFER_HW_MODE: JTAG hardware mode. Used to set HW drived or bitbang ++ * mode. This is bitmask for mode param in jtag_mode for ioctl JTAG_SIOCMODE ++ */ ++#define JTAG_XFER_HW_MODE 1 ++/* ++ * JTAG_XFER_SW_MODE: JTAG software mode. Used to set SW drived or bitbang ++ * mode. This is bitmask for mode param in jtag_mode for ioctl JTAG_SIOCMODE ++ */ ++#define JTAG_XFER_SW_MODE 0 ++ ++/** ++ * enum jtag_tapstate: ++ * ++ * @JTAG_STATE_TLRESET: JTAG state machine Test Logic Reset state ++ * @JTAG_STATE_IDLE: JTAG state machine IDLE state ++ * @JTAG_STATE_SELECTDR: JTAG state machine SELECT_DR state ++ * @JTAG_STATE_CAPTUREDR: JTAG state machine CAPTURE_DR state ++ * @JTAG_STATE_SHIFTDR: JTAG state machine SHIFT_DR state ++ * @JTAG_STATE_EXIT1DR: JTAG state machine EXIT-1 DR state ++ * @JTAG_STATE_PAUSEDR: JTAG state machine PAUSE_DR state ++ * @JTAG_STATE_EXIT2DR: JTAG state machine EXIT-2 DR state ++ * @JTAG_STATE_UPDATEDR: JTAG state machine UPDATE DR state ++ * @JTAG_STATE_SELECTIR: JTAG state machine SELECT_IR state ++ * @JTAG_STATE_CAPTUREIR: JTAG state machine CAPTURE_IR state ++ * @JTAG_STATE_SHIFTIR: JTAG state machine SHIFT_IR state ++ * @JTAG_STATE_EXIT1IR: JTAG state machine EXIT-1 IR state ++ * @JTAG_STATE_PAUSEIR: JTAG state machine PAUSE_IR state ++ * @JTAG_STATE_EXIT2IR: JTAG state machine EXIT-2 IR state ++ * @JTAG_STATE_UPDATEIR: JTAG state machine UPDATE IR state ++ * @JTAG_STATE_CURRENT: JTAG current state, saved by driver ++ */ ++enum jtag_tapstate { ++ JTAG_STATE_TLRESET, ++ JTAG_STATE_IDLE, ++ JTAG_STATE_SELECTDR, ++ JTAG_STATE_CAPTUREDR, ++ JTAG_STATE_SHIFTDR, ++ JTAG_STATE_EXIT1DR, ++ JTAG_STATE_PAUSEDR, ++ JTAG_STATE_EXIT2DR, ++ JTAG_STATE_UPDATEDR, ++ JTAG_STATE_SELECTIR, ++ JTAG_STATE_CAPTUREIR, ++ JTAG_STATE_SHIFTIR, ++ JTAG_STATE_EXIT1IR, ++ JTAG_STATE_PAUSEIR, ++ JTAG_STATE_EXIT2IR, ++ JTAG_STATE_UPDATEIR, ++ JTAG_STATE_CURRENT ++}; ++ ++/** ++ * enum jtag_reset: ++ * ++ * @JTAG_NO_RESET: JTAG run TAP from current state ++ * @JTAG_FORCE_RESET: JTAG force TAP to reset state ++ */ ++enum jtag_reset { ++ JTAG_NO_RESET = 0, ++ JTAG_FORCE_RESET = 1, ++}; ++ ++/** ++ * enum jtag_xfer_type: ++ * ++ * @JTAG_SIR_XFER: SIR transfer ++ * @JTAG_SDR_XFER: SDR transfer ++ */ ++enum jtag_xfer_type { ++ JTAG_SIR_XFER = 0, ++ JTAG_SDR_XFER = 1, ++}; ++ ++/** ++ * enum jtag_xfer_direction: ++ * ++ * @JTAG_READ_XFER: read transfer ++ * @JTAG_WRITE_XFER: write transfer ++ * @JTAG_READ_WRITE_XFER: read & write transfer ++ */ ++enum jtag_xfer_direction { ++ JTAG_READ_XFER = 1, ++ JTAG_WRITE_XFER = 2, ++ JTAG_READ_WRITE_XFER = 3, ++}; ++ ++/** ++ * struct jtag_tap_state - forces JTAG state machine to go into a TAPC ++ * state ++ * ++ * @reset: 0 - run IDLE/PAUSE from current state ++ * 1 - go through TEST_LOGIC/RESET state before IDLE/PAUSE ++ * @end: completion flag ++ * @tck: clock counter ++ * ++ * Structure provide interface to JTAG device for JTAG set state execution. ++ */ ++struct jtag_tap_state { ++ __u8 reset; ++ __u8 from; ++ __u8 endstate; ++ __u8 tck; ++}; ++ ++/** ++ * struct jtag_xfer - jtag xfer: ++ * ++ * @type: transfer type ++ * @direction: xfer direction ++ * @from: xfer current state ++ * @endstate: xfer end state ++ * @padding: xfer padding ++ * @length: xfer bits length ++ * @tdio : xfer data array ++ * ++ * Structure provide interface to JTAG device for JTAG SDR/SIR xfer execution. ++ */ ++struct jtag_xfer { ++ __u8 type; ++ __u8 direction; ++ __u8 from; ++ __u8 endstate; ++ __u8 padding; ++ __u32 length; ++ __u64 tdio; ++}; ++ ++/** ++ * struct bitbang_packet - jtag bitbang array packet: ++ * ++ * @data: JTAG Bitbang struct array pointer(input/output) ++ * @length: array size (input) ++ * ++ * Structure provide interface to JTAG device for JTAG bitbang bundle execution ++ */ ++struct bitbang_packet { ++ struct tck_bitbang *data; ++ __u32 length; ++} __attribute__((__packed__)); ++ ++/** ++ * struct jtag_bitbang - jtag bitbang: ++ * ++ * @tms: JTAG TMS ++ * @tdi: JTAG TDI (input) ++ * @tdo: JTAG TDO (output) ++ * ++ * Structure provide interface to JTAG device for JTAG bitbang execution. ++ */ ++struct tck_bitbang { ++ __u8 tms; ++ __u8 tdi; ++ __u8 tdo; ++} __attribute__((__packed__)); ++ ++/** ++ * struct jtag_mode - jtag mode: ++ * ++ * @feature: 0 - JTAG feature setting selector for JTAG controller HW/SW ++ * 1 - JTAG feature setting selector for controller bus master ++ * mode output (enable / disable). ++ * @mode: (0 - SW / 1 - HW) for JTAG_XFER_MODE feature(0) ++ * (0 - output disable / 1 - output enable) for JTAG_CONTROL_MODE ++ * feature(1) ++ * ++ * Structure provide configuration modes to JTAG device. ++ */ ++struct jtag_mode { ++ __u32 feature; ++ __u32 mode; ++}; ++ ++/* ioctl interface */ ++#define __JTAG_IOCTL_MAGIC 0xb2 ++ ++#define JTAG_SIOCSTATE _IOW(__JTAG_IOCTL_MAGIC, 0, struct jtag_tap_state) ++#define JTAG_SIOCFREQ _IOW(__JTAG_IOCTL_MAGIC, 1, unsigned int) ++#define JTAG_GIOCFREQ _IOR(__JTAG_IOCTL_MAGIC, 2, unsigned int) ++#define JTAG_IOCXFER _IOWR(__JTAG_IOCTL_MAGIC, 3, struct jtag_xfer) ++#define JTAG_GIOCSTATUS _IOWR(__JTAG_IOCTL_MAGIC, 4, enum jtag_tapstate) ++#define JTAG_SIOCMODE _IOW(__JTAG_IOCTL_MAGIC, 5, unsigned int) ++#define JTAG_IOCBITBANG _IOW(__JTAG_IOCTL_MAGIC, 6, unsigned int) ++ ++#endif /* __UAPI_LINUX_JTAG_H */ +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0053-Add-Aspeed-SoC-24xx-and-25xx-families-JTAG.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0053-Add-Aspeed-SoC-24xx-and-25xx-families-JTAG.patch new file mode 100644 index 000000000..69c993d86 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0053-Add-Aspeed-SoC-24xx-and-25xx-families-JTAG.patch @@ -0,0 +1,1294 @@ +From 3b7175753cafcee67cfc13eefc30438a518ad348 Mon Sep 17 00:00:00 2001 +From: "Corona, Ernesto" <ernesto.corona@intel.com> +Date: Mon, 3 Jun 2019 08:22:09 -0800 +Subject: [PATCH] Add Aspeed SoC 24xx and 25xx families JTAG + +Driver adds support of Aspeed 2500/2400 series SOC JTAG master controller. + +Driver implements the following jtag ops: +- freq_get; +- freq_set; +- status_get; +- status_set +- xfer; +- mode_set; +- bitbang; +- enable; +- disable; + +It has been tested on Mellanox system with BMC equipped with +Aspeed 2520 SoC for programming CPLD devices. + +It has also been tested on Intel system using Aspeed 25xx SoC +for JTAG communication. + +Signed-off-by: Oleksandr Shamray <oleksandrs@mellanox.com> +Signed-off-by: Jiri Pirko <jiri@mellanox.com> +Signed-off-by: Corona, Ernesto <ernesto.corona@intel.com> +Acked-by: Arnd Bergmann <arnd@arndb.de> +Acked-by: Philippe Ombredanne <pombredanne@nexb.com> +Acked-by: Joel Stanley <joel@jms.id.au> +Cc: Vadim Pasternak <vadimp@mellanox.com> +Cc: Andrew Jeffery <andrew@aj.id.au> +Cc: Steven A Filary <steven.a.filary@intel.com> +Cc: Bryan Hunt <bryan.hunt@intel.com> +--- +v28->v29 +Comments pointed by Steven Filary <steven.a.filary@intel.com> +- Expand bitbang function to accept multiples bitbang operations within a + single JTAG_IOCBITBANG call. It will receive a buffer with TDI and TMS + values and it is expected that driver fills TDO fields with its + corresponding output value for every transaction. +- Always setup JTAG controller to master mode but disable JTAG output when + the driver is not in use to allow other HW to own the JTAG bus. Remove SCU + register accesses. This register controls the JTAG controller mode + (master/slave). +- Encansulate dev_dgb message into DEBUG_JTAG macros to improve driver's JTAG + performace. +- Add support for multichain. Set tap state and xfer operations now include + two tap state arguments: current state and end state. + +v27->v28 +Comments pointed by Steven Filary <steven.a.filary@intel.com> +- Replace JTAG_IOCRUNTEST with JTAG_SIOCSTATE adding support for all TAPC + end states in SW mode using a lookup table to navigate across states. +- Add support for simultaneous READ/WRITE transfers(JTAG_READ_WRITE_XFER). +- Support for switching JTAG controller mode between slave and master + mode. +- Setup JTAG controller mode to master only when the driver is opened, + letting other HW to own the JTAG bus when it isn't in use. +- Include JTAG bit bang IOCTL for low level JTAG control usage + (JTAG_IOCBITBANG). +- Add debug traces. +- Add support for register polling (default) due it is 3 times faster than + interrupt mode. Define USE_INTERRUPTS macro to enable interrupt usage. +- Remove unnecessary delays for aspeed_jtag_status_set function. It makes + SW mode 4 times faster. +- Clean data buffer on aspeed_jtag_xfer_sw before tdo writes to avoid data + output corruption for read operations in SW mode. +- Correct register settings for HW mode transfer operations. +- Propagate ret codes all the way from low level functions up to + JTAG_IOCXFER call. +- Support for partitioned transfers. Single JTAG transfer through + multiples JTAG_IOCXFER calls. Now end transmission(scan_end) also + evaluates transfer end state. + +v26->v27 +Changes made by Oleksandr Shamray <oleksandrs@mellamnox.com> +- change aspeed_jtag_sw_delay to udelay function in bit-bang operation + +v25->v26 +v24->v25 +Comments pointed by Greg KH <gregkh@linuxfoundation.org> +- reduced debug printouts + +v23->v24 +v22->v23 +v21->v22 +Comments pointed by Andy Shevchenko <andy.shevchenko@gmail.com> +- rearrange ASPEED register defines +- simplified JTAG divider calculation formula +- change delay function in bit-bang operation +- add helper functions for TAP states switching +- remove unnecessary comments +- remove redundant debug messages +- make dines for repetative register bit sets +- fixed indentation +- change checks from negative to positive +- add error check for clk_prepare_enable +- rework driver alloc/register to devm_ variant +- Increasing line length up to 85 in order to improve readability + +v20->v21 +v19->v20 +Notifications from kbuild test robot <lkp@intel.com> +- add static declaration to 'aspeed_jtag_init' and + 'aspeed_jtag_deinit' functions + +v18->v19 +v17->v18 +v16->v17 +v15->v16 +Comments pointed by Joel Stanley <joel.stan@gmail.com> +- Add reset_control on Jtag init/deinit + +v14->v15 +Comments pointed by Joel Stanley <joel.stan@gmail.com> +- Add ARCH_ASPEED || COMPILE_TEST to Kconfig +- remove unused offset variable +- remove "aspeed_jtag" from dev_err and dev_dbg messages +- change clk_prepare_enable initialisation order + +v13->v14 +Comments pointed by Philippe Ombredanne <pombredanne@nexb.com> +- Change style of head block comment from /**/ to // + +v12->v13 +Comments pointed by Philippe Ombredanne <pombredanne@nexb.com> +- Change jtag-aspeed.c licence type to + SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note + and reorder line with license- add reset descriptions in bndings file + in description +Comments pointed by Kun Yi <kunyi@google.com> +- Changed capability check for aspeed,ast2400-jtag/ast200-jtag + +v11->v12 +Comments pointed by Chip Bilbrey <chip@bilbrey.org> +- Remove access mode from xfer and idle transactions +- Add new ioctl JTAG_SIOCMODE for set hw mode + +v10->v11 +v9->v10 +V8->v9 +Comments pointed by Arnd Bergmann <arnd@arndb.de> +- add *data parameter to xfer function prototype + +v7->v8 +Comments pointed by Joel Stanley <joel.stan@gmail.com> +- aspeed_jtag_init replace goto to return; +- change input variables type from __u32 to u32 + in functios freq_get, freq_set, status_get +- change sm_ variables type from char to u8 +- in jatg_init add disable clocks on error case +- remove release_mem_region on error case +- remove devm_free_irq on jtag_deinit +- Fix misspelling Disabe/Disable +- Change compatible string to ast2400 and ast2000 + +v6->v7 +Notifications from kbuild test robot <lkp@intel.com> +- Add include <linux/types.h> to jtag-asapeed.c + +v5->v6 +v4->v5 +Comments pointed by Arnd Bergmann <arnd@arndb.de> +- Added HAS_IOMEM dependence in Kconfig to avoid + "undefined reference to `devm_ioremap_resource'" error, + because in some arch this not supported + +v3->v4 +Comments pointed by Arnd Bergmann <arnd@arndb.de> +- change transaction pointer tdio type to __u64 +- change internal status type from enum to __u32 + +v2->v3 + +v1->v2 +Comments pointed by Greg KH <gregkh@linuxfoundation.org> +- change license type from GPLv2/BSD to GPLv2 + +Comments pointed by Neil Armstrong <narmstrong@baylibre.com> +- Add clk_prepare_enable/clk_disable_unprepare in clock init/deinit +- Change .compatible to soc-specific compatible names + aspeed,aspeed4000-jtag/aspeed5000-jtag +- Added dt-bindings + +Comments pointed by Arnd Bergmann <arnd@arndb.de> +- Reorder functions and removed the forward declarations +- Add static const qualifier to state machine states transitions +- Change .compatible to soc-specific compatible names + aspeed,aspeed4000-jtag/aspeed5000-jtag +- Add dt-bindings + +Comments pointed by Randy Dunlap <rdunlap@infradead.org> +- Change module name jtag-aspeed in description in Kconfig + +Comments pointed by kbuild test robot <lkp@intel.com> +- Remove invalid include <asm/mach-types.h> +- add resource_size instead of calculation +--- + drivers/jtag/Kconfig | 14 + + drivers/jtag/Makefile | 1 + + drivers/jtag/jtag-aspeed.c | 1051 ++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 1066 insertions(+) + create mode 100644 drivers/jtag/jtag-aspeed.c + +diff --git a/drivers/jtag/Kconfig b/drivers/jtag/Kconfig +index 47771fcd3c5b..0cc163f9ad44 100644 +--- a/drivers/jtag/Kconfig ++++ b/drivers/jtag/Kconfig +@@ -15,3 +15,17 @@ menuconfig JTAG + + To compile this driver as a module, choose M here: the module will + be called jtag. ++ ++menuconfig JTAG_ASPEED ++ tristate "Aspeed SoC JTAG controller support" ++ depends on JTAG && HAS_IOMEM ++ depends on ARCH_ASPEED || COMPILE_TEST ++ help ++ This provides a support for Aspeed JTAG device, equipped on ++ Aspeed SoC 24xx and 25xx families. Drivers allows programming ++ of hardware devices, connected to SoC through the JTAG interface. ++ ++ If you want this support, you should say Y here. ++ ++ To compile this driver as a module, choose M here: the module will ++ be called jtag-aspeed. +diff --git a/drivers/jtag/Makefile b/drivers/jtag/Makefile +index af374939a9e6..04a855e2df28 100644 +--- a/drivers/jtag/Makefile ++++ b/drivers/jtag/Makefile +@@ -1 +1,2 @@ + obj-$(CONFIG_JTAG) += jtag.o ++obj-$(CONFIG_JTAG_ASPEED) += jtag-aspeed.o +diff --git a/drivers/jtag/jtag-aspeed.c b/drivers/jtag/jtag-aspeed.c +new file mode 100644 +index 000000000000..0c9da1b8986c +--- /dev/null ++++ b/drivers/jtag/jtag-aspeed.c +@@ -0,0 +1,1051 @@ ++// SPDX-License-Identifier: GPL-2.0 ++// Copyright (c) 2018 Mellanox Technologies. All rights reserved. ++// Copyright (c) 2018 Oleksandr Shamray <oleksandrs@mellanox.com> ++// Copyright (c) 2019 Intel Corporation ++ ++#include <linux/clk.h> ++#include <linux/device.h> ++#include <linux/interrupt.h> ++#include <linux/jtag.h> ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/of_address.h> ++#include <linux/platform_device.h> ++#include <linux/reset.h> ++#include <linux/slab.h> ++#include <linux/types.h> ++#include <linux/delay.h> ++#include <uapi/linux/jtag.h> ++ ++#define ASPEED_SCU_RESET_JTAG BIT(22) ++ ++#define ASPEED_JTAG_DATA 0x00 ++#define ASPEED_JTAG_INST 0x04 ++#define ASPEED_JTAG_CTRL 0x08 ++#define ASPEED_JTAG_ISR 0x0C ++#define ASPEED_JTAG_SW 0x10 ++#define ASPEED_JTAG_TCK 0x14 ++#define ASPEED_JTAG_EC 0x18 ++ ++#define ASPEED_JTAG_DATA_MSB 0x01 ++#define ASPEED_JTAG_DATA_CHUNK_SIZE 0x20 ++ ++/* ASPEED_JTAG_CTRL: Engine Control */ ++#define ASPEED_JTAG_CTL_ENG_EN BIT(31) ++#define ASPEED_JTAG_CTL_ENG_OUT_EN BIT(30) ++#define ASPEED_JTAG_CTL_FORCE_TMS BIT(29) ++#define ASPEED_JTAG_CTL_IR_UPDATE BIT(26) ++#define ASPEED_JTAG_CTL_INST_LEN(x) ((x) << 20) ++#define ASPEED_JTAG_CTL_LASPEED_INST BIT(17) ++#define ASPEED_JTAG_CTL_INST_EN BIT(16) ++#define ASPEED_JTAG_CTL_DR_UPDATE BIT(10) ++#define ASPEED_JTAG_CTL_DATA_LEN(x) ((x) << 4) ++#define ASPEED_JTAG_CTL_LASPEED_DATA BIT(1) ++#define ASPEED_JTAG_CTL_DATA_EN BIT(0) ++ ++/* ASPEED_JTAG_ISR : Interrupt status and enable */ ++#define ASPEED_JTAG_ISR_INST_PAUSE BIT(19) ++#define ASPEED_JTAG_ISR_INST_COMPLETE BIT(18) ++#define ASPEED_JTAG_ISR_DATA_PAUSE BIT(17) ++#define ASPEED_JTAG_ISR_DATA_COMPLETE BIT(16) ++#define ASPEED_JTAG_ISR_INST_PAUSE_EN BIT(3) ++#define ASPEED_JTAG_ISR_INST_COMPLETE_EN BIT(2) ++#define ASPEED_JTAG_ISR_DATA_PAUSE_EN BIT(1) ++#define ASPEED_JTAG_ISR_DATA_COMPLETE_EN BIT(0) ++#define ASPEED_JTAG_ISR_INT_EN_MASK GENMASK(3, 0) ++#define ASPEED_JTAG_ISR_INT_MASK GENMASK(19, 16) ++ ++/* ASPEED_JTAG_SW : Software Mode and Status */ ++#define ASPEED_JTAG_SW_MODE_EN BIT(19) ++#define ASPEED_JTAG_SW_MODE_TCK BIT(18) ++#define ASPEED_JTAG_SW_MODE_TMS BIT(17) ++#define ASPEED_JTAG_SW_MODE_TDIO BIT(16) ++ ++/* ASPEED_JTAG_TCK : TCK Control */ ++#define ASPEED_JTAG_TCK_DIVISOR_MASK GENMASK(10, 0) ++#define ASPEED_JTAG_TCK_GET_DIV(x) ((x) & ASPEED_JTAG_TCK_DIVISOR_MASK) ++ ++/* ASPEED_JTAG_EC : Controller set for go to IDLE */ ++#define ASPEED_JTAG_EC_GO_IDLE BIT(0) ++ ++#define ASPEED_JTAG_IOUT_LEN(len) \ ++ (ASPEED_JTAG_CTL_ENG_EN | \ ++ ASPEED_JTAG_CTL_ENG_OUT_EN | \ ++ ASPEED_JTAG_CTL_INST_LEN(len)) ++ ++#define ASPEED_JTAG_DOUT_LEN(len) \ ++ (ASPEED_JTAG_CTL_ENG_EN | \ ++ ASPEED_JTAG_CTL_ENG_OUT_EN | \ ++ ASPEED_JTAG_CTL_DATA_LEN(len)) ++ ++#define ASPEED_JTAG_SW_TDIO (ASPEED_JTAG_SW_MODE_EN | ASPEED_JTAG_SW_MODE_TDIO) ++ ++#define ASPEED_JTAG_GET_TDI(direction, byte) \ ++ (((direction) & JTAG_WRITE_XFER) ? byte : UINT_MAX) ++ ++#define ASPEED_JTAG_TCK_WAIT 10 ++#define ASPEED_JTAG_RESET_CNTR 10 ++#define WAIT_ITERATIONS 75 ++ ++/*#define USE_INTERRUPTS*/ ++/*#define DEBUG_JTAG*/ ++ ++static const char * const regnames[] = { ++ [ASPEED_JTAG_DATA] = "ASPEED_JTAG_DATA", ++ [ASPEED_JTAG_INST] = "ASPEED_JTAG_INST", ++ [ASPEED_JTAG_CTRL] = "ASPEED_JTAG_CTRL", ++ [ASPEED_JTAG_ISR] = "ASPEED_JTAG_ISR", ++ [ASPEED_JTAG_SW] = "ASPEED_JTAG_SW", ++ [ASPEED_JTAG_TCK] = "ASPEED_JTAG_TCK", ++ [ASPEED_JTAG_EC] = "ASPEED_JTAG_EC", ++}; ++ ++#define ASPEED_JTAG_NAME "jtag-aspeed" ++ ++struct aspeed_jtag { ++ void __iomem *reg_base; ++ struct device *dev; ++ struct clk *pclk; ++ enum jtag_tapstate status; ++ int irq; ++ struct reset_control *rst; ++ u32 flag; ++ wait_queue_head_t jtag_wq; ++ u32 mode; ++}; ++ ++/* ++ * This structure represents a TMS cycle, as expressed in a set of bits and a ++ * count of bits (note: there are no start->end state transitions that require ++ * more than 1 byte of TMS cycles) ++ */ ++struct tms_cycle { ++ unsigned char tmsbits; ++ unsigned char count; ++}; ++ ++/* ++ * This is the complete set TMS cycles for going from any TAP state to any ++ * other TAP state, following a "shortest path" rule. ++ */ ++static const struct tms_cycle _tms_cycle_lookup[][16] = { ++/* TLR RTI SelDR CapDR SDR Ex1DR*/ ++/* TLR */{{0x00, 0}, {0x00, 1}, {0x02, 2}, {0x02, 3}, {0x02, 4}, {0x0a, 4}, ++/* PDR Ex2DR UpdDR SelIR CapIR SIR*/ ++ {0x0a, 5}, {0x2a, 6}, {0x1a, 5}, {0x06, 3}, {0x06, 4}, {0x06, 5}, ++/* Ex1IR PIR Ex2IR UpdIR*/ ++ {0x16, 5}, {0x16, 6}, {0x56, 7}, {0x36, 6} }, ++ ++/* TLR RTI SelDR CapDR SDR Ex1DR*/ ++/* RTI */{{0x07, 3}, {0x00, 0}, {0x01, 1}, {0x01, 2}, {0x01, 3}, {0x05, 3}, ++/* PDR Ex2DR UpdDR SelIR CapIR SIR*/ ++ {0x05, 4}, {0x15, 5}, {0x0d, 4}, {0x03, 2}, {0x03, 3}, {0x03, 4}, ++/* Ex1IR PIR Ex2IR UpdIR*/ ++ {0x0b, 4}, {0x0b, 5}, {0x2b, 6}, {0x1b, 5} }, ++ ++/* TLR RTI SelDR CapDR SDR Ex1DR*/ ++/* SelDR*/{{0x03, 2}, {0x03, 3}, {0x00, 0}, {0x00, 1}, {0x00, 2}, {0x02, 2}, ++/* PDR Ex2DR UpdDR SelIR CapIR SIR*/ ++ {0x02, 3}, {0x0a, 4}, {0x06, 3}, {0x01, 1}, {0x01, 2}, {0x01, 3}, ++/* Ex1IR PIR Ex2IR UpdIR*/ ++ {0x05, 3}, {0x05, 4}, {0x15, 5}, {0x0d, 4} }, ++ ++/* TLR RTI SelDR CapDR SDR Ex1DR*/ ++/* CapDR*/{{0x1f, 5}, {0x03, 3}, {0x07, 3}, {0x00, 0}, {0x00, 1}, {0x01, 1}, ++/* PDR Ex2DR UpdDR SelIR CapIR SIR*/ ++ {0x01, 2}, {0x05, 3}, {0x03, 2}, {0x0f, 4}, {0x0f, 5}, {0x0f, 6}, ++/* Ex1IR PIR Ex2IR UpdIR*/ ++ {0x2f, 6}, {0x2f, 7}, {0xaf, 8}, {0x6f, 7} }, ++ ++/* TLR RTI SelDR CapDR SDR Ex1DR*/ ++/* SDR */{{0x1f, 5}, {0x03, 3}, {0x07, 3}, {0x07, 4}, {0x00, 0}, {0x01, 1}, ++/* PDR Ex2DR UpdDR SelIR CapIR SIR*/ ++ {0x01, 2}, {0x05, 3}, {0x03, 2}, {0x0f, 4}, {0x0f, 5}, {0x0f, 6}, ++/* Ex1IR PIR Ex2IR UpdIR*/ ++ {0x2f, 6}, {0x2f, 7}, {0xaf, 8}, {0x6f, 7} }, ++ ++/* TLR RTI SelDR CapDR SDR Ex1DR*/ ++/* Ex1DR*/{{0x0f, 4}, {0x01, 2}, {0x03, 2}, {0x03, 3}, {0x02, 3}, {0x00, 0}, ++/* PDR Ex2DR UpdDR SelIR CapIR SIR*/ ++ {0x00, 1}, {0x02, 2}, {0x01, 1}, {0x07, 3}, {0x07, 4}, {0x07, 5}, ++/* Ex1IR PIR Ex2IR UpdIR*/ ++ {0x17, 5}, {0x17, 6}, {0x57, 7}, {0x37, 6} }, ++ ++/* TLR RTI SelDR CapDR SDR Ex1DR*/ ++/* PDR */{{0x1f, 5}, {0x03, 3}, {0x07, 3}, {0x07, 4}, {0x01, 2}, {0x05, 3}, ++/* PDR Ex2DR UpdDR SelIR CapIR SIR*/ ++ {0x00, 0}, {0x01, 1}, {0x03, 2}, {0x0f, 4}, {0x0f, 5}, {0x0f, 6}, ++/* Ex1IR PIR Ex2IR UpdIR*/ ++ {0x2f, 6}, {0x2f, 7}, {0xaf, 8}, {0x6f, 7} }, ++ ++/* TLR RTI SelDR CapDR SDR Ex1DR*/ ++/* Ex2DR*/{{0x0f, 4}, {0x01, 2}, {0x03, 2}, {0x03, 3}, {0x00, 1}, {0x02, 2}, ++/* PDR Ex2DR UpdDR SelIR CapIR SIR*/ ++ {0x02, 3}, {0x00, 0}, {0x01, 1}, {0x07, 3}, {0x07, 4}, {0x07, 5}, ++/* Ex1IR PIR Ex2IR UpdIR*/ ++ {0x17, 5}, {0x17, 6}, {0x57, 7}, {0x37, 6} }, ++ ++/* TLR RTI SelDR CapDR SDR Ex1DR*/ ++/* UpdDR*/{{0x07, 3}, {0x00, 1}, {0x01, 1}, {0x01, 2}, {0x01, 3}, {0x05, 3}, ++/* PDR Ex2DR UpdDR SelIR CapIR SIR*/ ++ {0x05, 4}, {0x15, 5}, {0x00, 0}, {0x03, 2}, {0x03, 3}, {0x03, 4}, ++/* Ex1IR PIR Ex2IR UpdIR*/ ++ {0x0b, 4}, {0x0b, 5}, {0x2b, 6}, {0x1b, 5} }, ++ ++/* TLR RTI SelDR CapDR SDR Ex1DR*/ ++/* SelIR*/{{0x01, 1}, {0x01, 2}, {0x05, 3}, {0x05, 4}, {0x05, 5}, {0x15, 5}, ++/* PDR Ex2DR UpdDR SelIR CapIR SIR*/ ++ {0x15, 6}, {0x55, 7}, {0x35, 6}, {0x00, 0}, {0x00, 1}, {0x00, 2}, ++/* Ex1IR PIR Ex2IR UpdIR*/ ++ {0x02, 2}, {0x02, 3}, {0x0a, 4}, {0x06, 3} }, ++ ++/* TLR RTI SelDR CapDR SDR Ex1DR*/ ++/* CapIR*/{{0x1f, 5}, {0x03, 3}, {0x07, 3}, {0x07, 4}, {0x07, 5}, {0x17, 5}, ++/* PDR Ex2DR UpdDR SelIR CapIR SIR*/ ++ {0x17, 6}, {0x57, 7}, {0x37, 6}, {0x0f, 4}, {0x00, 0}, {0x00, 1}, ++/* Ex1IR PIR Ex2IR UpdIR*/ ++ {0x01, 1}, {0x01, 2}, {0x05, 3}, {0x03, 2} }, ++ ++/* TLR RTI SelDR CapDR SDR Ex1DR*/ ++/* SIR */{{0x1f, 5}, {0x03, 3}, {0x07, 3}, {0x07, 4}, {0x07, 5}, {0x17, 5}, ++/* PDR Ex2DR UpdDR SelIR CapIR SIR*/ ++ {0x17, 6}, {0x57, 7}, {0x37, 6}, {0x0f, 4}, {0x0f, 5}, {0x00, 0}, ++/* Ex1IR PIR Ex2IR UpdIR*/ ++ {0x01, 1}, {0x01, 2}, {0x05, 3}, {0x03, 2} }, ++ ++/* TLR RTI SelDR CapDR SDR Ex1DR*/ ++/* Ex1IR*/{{0x0f, 4}, {0x01, 2}, {0x03, 2}, {0x03, 3}, {0x03, 4}, {0x0b, 4}, ++/* PDR Ex2DR UpdDR SelIR CapIR SIR*/ ++ {0x0b, 5}, {0x2b, 6}, {0x1b, 5}, {0x07, 3}, {0x07, 4}, {0x02, 3}, ++/* Ex1IR PIR Ex2IR UpdIR*/ ++ {0x00, 0}, {0x00, 1}, {0x02, 2}, {0x01, 1} }, ++ ++/* TLR RTI SelDR CapDR SDR Ex1DR*/ ++/* PIR */{{0x1f, 5}, {0x03, 3}, {0x07, 3}, {0x07, 4}, {0x07, 5}, {0x17, 5}, ++/* PDR Ex2DR UpdDR SelIR CapIR SIR*/ ++ {0x17, 6}, {0x57, 7}, {0x37, 6}, {0x0f, 4}, {0x0f, 5}, {0x01, 2}, ++/* Ex1IR PIR Ex2IR UpdIR*/ ++ {0x05, 3}, {0x00, 0}, {0x01, 1}, {0x03, 2} }, ++ ++/* TLR RTI SelDR CapDR SDR Ex1DR*/ ++/* Ex2IR*/{{0x0f, 4}, {0x01, 2}, {0x03, 2}, {0x03, 3}, {0x03, 4}, {0x0b, 4}, ++/* PDR Ex2DR UpdDR SelIR CapIR SIR*/ ++ {0x0b, 5}, {0x2b, 6}, {0x1b, 5}, {0x07, 3}, {0x07, 4}, {0x00, 1}, ++/* Ex1IR PIR Ex2IR UpdIR*/ ++ {0x02, 2}, {0x02, 3}, {0x00, 0}, {0x01, 1} }, ++ ++/* TLR RTI SelDR CapDR SDR Ex1DR*/ ++/* UpdIR*/{{0x07, 3}, {0x00, 1}, {0x01, 1}, {0x01, 2}, {0x01, 3}, {0x05, 3}, ++/* PDR Ex2DR UpdDR SelIR CapIR SIR*/ ++ {0x05, 4}, {0x15, 5}, {0x0d, 4}, {0x03, 2}, {0x03, 3}, {0x03, 4}, ++/* Ex1IR PIR Ex2IR UpdIR*/ ++ {0x0b, 4}, {0x0b, 5}, {0x2b, 6}, {0x00, 0} }, ++}; ++ ++#ifdef DEBUG_JTAG ++static char *end_status_str[] = { ++ "tlr", "idle", "selDR", "capDR", "sDR", "ex1DR", "pDR", "ex2DR", ++ "updDR", "selIR", "capIR", "sIR", "ex1IR", "pIR", "ex2IR", "updIR" ++}; ++#endif ++ ++static u32 aspeed_jtag_read(struct aspeed_jtag *aspeed_jtag, u32 reg) ++{ ++ u32 val = readl(aspeed_jtag->reg_base + reg); ++ ++#ifdef DEBUG_JTAG ++ dev_dbg(aspeed_jtag->dev, "read:%s val = 0x%08x\n", regnames[reg], val); ++#endif ++ return val; ++} ++ ++static void ++aspeed_jtag_write(struct aspeed_jtag *aspeed_jtag, u32 val, u32 reg) ++{ ++#ifdef DEBUG_JTAG ++ dev_dbg(aspeed_jtag->dev, "write:%s val = 0x%08x\n", ++ regnames[reg], val); ++#endif ++ writel(val, aspeed_jtag->reg_base + reg); ++} ++ ++static int aspeed_jtag_freq_set(struct jtag *jtag, u32 freq) ++{ ++ struct aspeed_jtag *aspeed_jtag = jtag_priv(jtag); ++ unsigned long apb_frq; ++ u32 tck_val; ++ u16 div; ++ ++ apb_frq = clk_get_rate(aspeed_jtag->pclk); ++ if (!apb_frq) ++ return -ENOTSUPP; ++ ++ div = (apb_frq - 1) / freq; ++ tck_val = aspeed_jtag_read(aspeed_jtag, ASPEED_JTAG_TCK); ++ aspeed_jtag_write(aspeed_jtag, ++ (tck_val & ~ASPEED_JTAG_TCK_DIVISOR_MASK) | div, ++ ASPEED_JTAG_TCK); ++ return 0; ++} ++ ++static int aspeed_jtag_freq_get(struct jtag *jtag, u32 *frq) ++{ ++ struct aspeed_jtag *aspeed_jtag = jtag_priv(jtag); ++ u32 pclk; ++ u32 tck; ++ ++ pclk = clk_get_rate(aspeed_jtag->pclk); ++ tck = aspeed_jtag_read(aspeed_jtag, ASPEED_JTAG_TCK); ++ *frq = pclk / (ASPEED_JTAG_TCK_GET_DIV(tck) + 1); ++ ++ return 0; ++} ++ ++static inline void aspeed_jtag_output_disable(struct aspeed_jtag *aspeed_jtag) ++{ ++ aspeed_jtag_write(aspeed_jtag, 0, ASPEED_JTAG_CTRL); ++} ++ ++static inline void aspeed_jtag_master(struct aspeed_jtag *aspeed_jtag) ++{ ++ aspeed_jtag_write(aspeed_jtag, (ASPEED_JTAG_CTL_ENG_EN | ++ ASPEED_JTAG_CTL_ENG_OUT_EN), ++ ASPEED_JTAG_CTRL); ++ ++ aspeed_jtag_write(aspeed_jtag, ASPEED_JTAG_SW_MODE_EN | ++ ASPEED_JTAG_SW_MODE_TDIO, ++ ASPEED_JTAG_SW); ++ aspeed_jtag_write(aspeed_jtag, ASPEED_JTAG_ISR_INST_PAUSE | ++ ASPEED_JTAG_ISR_INST_COMPLETE | ++ ASPEED_JTAG_ISR_DATA_PAUSE | ++ ASPEED_JTAG_ISR_DATA_COMPLETE | ++ ASPEED_JTAG_ISR_INST_PAUSE_EN | ++ ASPEED_JTAG_ISR_INST_COMPLETE_EN | ++ ASPEED_JTAG_ISR_DATA_PAUSE_EN | ++ ASPEED_JTAG_ISR_DATA_COMPLETE_EN, ++ ASPEED_JTAG_ISR); /* Enable Interrupt */ ++} ++ ++static int aspeed_jtag_mode_set(struct jtag *jtag, struct jtag_mode *jtag_mode) ++{ ++ struct aspeed_jtag *aspeed_jtag = jtag_priv(jtag); ++ ++ switch (jtag_mode->feature) { ++ case JTAG_XFER_MODE: ++ aspeed_jtag->mode = jtag_mode->mode; ++ break; ++ case JTAG_CONTROL_MODE: ++ if (jtag_mode->mode == JTAG_MASTER_OUTPUT_DISABLE) ++ aspeed_jtag_output_disable(aspeed_jtag); ++ else if (jtag_mode->mode == JTAG_MASTER_MODE) ++ aspeed_jtag_master(aspeed_jtag); ++ break; ++ default: ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++static char aspeed_jtag_tck_cycle(struct aspeed_jtag *aspeed_jtag, ++ u8 tms, u8 tdi) ++{ ++ char tdo = 0; ++ ++ /* TCK = 0 */ ++ aspeed_jtag_write(aspeed_jtag, ASPEED_JTAG_SW_MODE_EN | ++ (tms * ASPEED_JTAG_SW_MODE_TMS) | ++ (tdi * ASPEED_JTAG_SW_MODE_TDIO), ASPEED_JTAG_SW); ++ ++ aspeed_jtag_read(aspeed_jtag, ASPEED_JTAG_SW); ++ ++ /* TCK = 1 */ ++ aspeed_jtag_write(aspeed_jtag, ASPEED_JTAG_SW_MODE_EN | ++ ASPEED_JTAG_SW_MODE_TCK | ++ (tms * ASPEED_JTAG_SW_MODE_TMS) | ++ (tdi * ASPEED_JTAG_SW_MODE_TDIO), ASPEED_JTAG_SW); ++ ++ if (aspeed_jtag_read(aspeed_jtag, ASPEED_JTAG_SW) & ++ ASPEED_JTAG_SW_MODE_TDIO) ++ tdo = 1; ++ ++ return tdo; ++} ++ ++static int aspeed_jtag_bitbang(struct jtag *jtag, ++ struct bitbang_packet *bitbang, ++ struct tck_bitbang *bitbang_data) ++{ ++ struct aspeed_jtag *aspeed_jtag = jtag_priv(jtag); ++ int i = 0; ++ ++ for (i = 0; i < bitbang->length; i++) { ++ bitbang_data[i].tdo = ++ aspeed_jtag_tck_cycle(aspeed_jtag, bitbang_data[i].tms, ++ bitbang_data[i].tdi); ++ } ++ return 0; ++} ++ ++static int aspeed_jtag_wait_instruction_pause(struct aspeed_jtag *aspeed_jtag) ++{ ++ int res = 0; ++#ifdef USE_INTERRUPTS ++ res = wait_event_interruptible(aspeed_jtag->jtag_wq, ++ aspeed_jtag->flag & ++ ASPEED_JTAG_ISR_INST_PAUSE); ++ aspeed_jtag->flag &= ~ASPEED_JTAG_ISR_INST_PAUSE; ++#else ++ u32 status = 0; ++ u32 iterations = 0; ++ ++ while ((status & ASPEED_JTAG_ISR_INST_PAUSE) == 0) { ++ status = aspeed_jtag_read(aspeed_jtag, ASPEED_JTAG_ISR); ++#ifdef DEBUG_JTAG ++ dev_dbg(aspeed_jtag->dev, "%s = 0x%08x\n", __func__, status); ++#endif ++ iterations++; ++ if (iterations > WAIT_ITERATIONS) { ++ dev_err(aspeed_jtag->dev, ++ "aspeed_jtag driver timed out waiting for instruction pause complete\n"); ++ res = -EFAULT; ++ break; ++ } ++ if ((status & ASPEED_JTAG_ISR_DATA_COMPLETE) == 0) { ++ if (iterations % 25 == 0) ++ usleep_range(1, 5); ++ else ++ udelay(1); ++ } ++ } ++ aspeed_jtag_write(aspeed_jtag, ASPEED_JTAG_ISR_INST_PAUSE | ++ (status & 0xf), ++ ASPEED_JTAG_ISR); ++#endif ++ return res; ++} ++ ++static int ++aspeed_jtag_wait_instruction_complete(struct aspeed_jtag *aspeed_jtag) ++{ ++ int res = 0; ++#ifdef USE_INTERRUPTS ++ res = wait_event_interruptible(aspeed_jtag->jtag_wq, ++ aspeed_jtag->flag & ++ ASPEED_JTAG_ISR_INST_COMPLETE); ++ aspeed_jtag->flag &= ~ASPEED_JTAG_ISR_INST_COMPLETE; ++#else ++ u32 status = 0; ++ u32 iterations = 0; ++ ++ while ((status & ASPEED_JTAG_ISR_INST_COMPLETE) == 0) { ++ status = aspeed_jtag_read(aspeed_jtag, ASPEED_JTAG_ISR); ++#ifdef DEBUG_JTAG ++ dev_dbg(aspeed_jtag->dev, "%s = 0x%08x\n", __func__, status); ++#endif ++ iterations++; ++ if (iterations > WAIT_ITERATIONS) { ++ dev_err(aspeed_jtag->dev, ++ "aspeed_jtag driver timed out waiting for instruction complete\n"); ++ res = -EFAULT; ++ break; ++ } ++ if ((status & ASPEED_JTAG_ISR_DATA_COMPLETE) == 0) { ++ if (iterations % 25 == 0) ++ usleep_range(1, 5); ++ else ++ udelay(1); ++ } ++ } ++ aspeed_jtag_write(aspeed_jtag, ASPEED_JTAG_ISR_INST_COMPLETE | ++ (status & 0xf), ++ ASPEED_JTAG_ISR); ++#endif ++ return res; ++} ++ ++static int ++aspeed_jtag_wait_data_pause_complete(struct aspeed_jtag *aspeed_jtag) ++{ ++ int res = 0; ++#ifdef USE_INTERRUPTS ++ res = wait_event_interruptible(aspeed_jtag->jtag_wq, ++ aspeed_jtag->flag & ++ ASPEED_JTAG_ISR_DATA_PAUSE); ++ aspeed_jtag->flag &= ~ASPEED_JTAG_ISR_DATA_PAUSE; ++#else ++ u32 status = 0; ++ u32 iterations = 0; ++ ++ while ((status & ASPEED_JTAG_ISR_DATA_PAUSE) == 0) { ++ status = aspeed_jtag_read(aspeed_jtag, ASPEED_JTAG_ISR); ++#ifdef DEBUG_JTAG ++ dev_dbg(aspeed_jtag->dev, "%s = 0x%08x\n", __func__, status); ++#endif ++ iterations++; ++ if (iterations > WAIT_ITERATIONS) { ++ dev_err(aspeed_jtag->dev, ++ "aspeed_jtag driver timed out waiting for data pause complete\n"); ++ res = -EFAULT; ++ break; ++ } ++ if ((status & ASPEED_JTAG_ISR_DATA_COMPLETE) == 0) { ++ if (iterations % 25 == 0) ++ usleep_range(1, 5); ++ else ++ udelay(1); ++ } ++ } ++ aspeed_jtag_write(aspeed_jtag, ASPEED_JTAG_ISR_DATA_PAUSE | ++ (status & 0xf), ASPEED_JTAG_ISR); ++#endif ++ return res; ++} ++ ++static int aspeed_jtag_wait_data_complete(struct aspeed_jtag *aspeed_jtag) ++{ ++ int res = 0; ++#ifdef USE_INTERRUPTS ++ res = wait_event_interruptible(aspeed_jtag->jtag_wq, ++ aspeed_jtag->flag & ++ ASPEED_JTAG_ISR_DATA_COMPLETE); ++ aspeed_jtag->flag &= ~ASPEED_JTAG_ISR_DATA_COMPLETE; ++#else ++ u32 status = 0; ++ u32 iterations = 0; ++ ++ while ((status & ASPEED_JTAG_ISR_DATA_COMPLETE) == 0) { ++ status = aspeed_jtag_read(aspeed_jtag, ASPEED_JTAG_ISR); ++#ifdef DEBUG_JTAG ++ dev_dbg(aspeed_jtag->dev, "%s = 0x%08x\n", __func__, status); ++#endif ++ iterations++; ++ if (iterations > WAIT_ITERATIONS) { ++ dev_err(aspeed_jtag->dev, ++ "ast_jtag driver timed out waiting for data complete\n"); ++ res = -EFAULT; ++ break; ++ } ++ if ((status & ASPEED_JTAG_ISR_DATA_COMPLETE) == 0) { ++ if (iterations % 25 == 0) ++ usleep_range(1, 5); ++ else ++ udelay(1); ++ } ++ } ++ aspeed_jtag_write(aspeed_jtag, ++ ASPEED_JTAG_ISR_DATA_COMPLETE | (status & 0xf), ++ ASPEED_JTAG_ISR); ++#endif ++ return res; ++} ++ ++static void aspeed_jtag_set_tap_state(struct aspeed_jtag *aspeed_jtag, ++ enum jtag_tapstate from_state, ++ enum jtag_tapstate end_state) ++{ ++ int i = 0; ++ enum jtag_tapstate from, to; ++ ++ from = from_state; ++ to = end_state; ++ ++ if (from == JTAG_STATE_CURRENT) ++ from = aspeed_jtag->status; ++ ++ for (i = 0; i < _tms_cycle_lookup[from][to].count; i++) ++ aspeed_jtag_tck_cycle(aspeed_jtag, ++ ((_tms_cycle_lookup[from][to].tmsbits >> i) & 0x1), 0); ++ aspeed_jtag->status = end_state; ++} ++ ++static void aspeed_jtag_set_tap_state_sw(struct aspeed_jtag *aspeed_jtag, ++ struct jtag_tap_state *tapstate) ++{ ++ /* SW mode from curent tap state -> to end_state */ ++ if (tapstate->reset) { ++ int i = 0; ++ ++ for (i = 0; i < ASPEED_JTAG_RESET_CNTR; i++) ++ aspeed_jtag_tck_cycle(aspeed_jtag, 1, 0); ++ aspeed_jtag->status = JTAG_STATE_TLRESET; ++ } ++ ++ aspeed_jtag_set_tap_state(aspeed_jtag, tapstate->from, ++ tapstate->endstate); ++} ++ ++static int aspeed_jtag_status_set(struct jtag *jtag, ++ struct jtag_tap_state *tapstate) ++{ ++ struct aspeed_jtag *aspeed_jtag = jtag_priv(jtag); ++ ++#ifdef DEBUG_JTAG ++ dev_dbg(aspeed_jtag->dev, "Set TAP state: %s\n", ++ end_status_str[tapstate->endstate]); ++#endif ++ ++ if (!(aspeed_jtag->mode & JTAG_XFER_HW_MODE)) { ++ aspeed_jtag_set_tap_state_sw(aspeed_jtag, tapstate); ++ return 0; ++ } ++ ++ /* x TMS high + 1 TMS low */ ++ if (tapstate->reset) { ++ /* Disable sw mode */ ++ aspeed_jtag_write(aspeed_jtag, 0, ASPEED_JTAG_SW); ++ mdelay(1); ++ aspeed_jtag_write(aspeed_jtag, ASPEED_JTAG_CTL_ENG_EN | ++ ASPEED_JTAG_CTL_ENG_OUT_EN | ++ ASPEED_JTAG_CTL_FORCE_TMS, ASPEED_JTAG_CTRL); ++ mdelay(1); ++ aspeed_jtag_write(aspeed_jtag, ++ ASPEED_JTAG_SW_TDIO, ASPEED_JTAG_SW); ++ aspeed_jtag->status = JTAG_STATE_TLRESET; ++ } ++ ++ return 0; ++} ++ ++static void aspeed_jtag_xfer_sw(struct aspeed_jtag *aspeed_jtag, ++ struct jtag_xfer *xfer, u32 *data) ++{ ++ unsigned long remain_xfer = xfer->length; ++ unsigned long shift_bits = 0; ++ unsigned long index = 0; ++ unsigned long tdi; ++ char tdo; ++ ++#ifdef DEBUG_JTAG ++ dev_dbg(aspeed_jtag->dev, "SW JTAG SHIFT %s, length = %d\n", ++ (xfer->type == JTAG_SIR_XFER) ? "IR" : "DR", xfer->length); ++#endif ++ ++ if (xfer->type == JTAG_SIR_XFER) ++ aspeed_jtag_set_tap_state(aspeed_jtag, xfer->from, ++ JTAG_STATE_SHIFTIR); ++ else ++ aspeed_jtag_set_tap_state(aspeed_jtag, xfer->from, ++ JTAG_STATE_SHIFTDR); ++ ++ tdi = ASPEED_JTAG_GET_TDI(xfer->direction, data[index]); ++ data[index] = 0; ++ while (remain_xfer > 1) { ++ tdo = aspeed_jtag_tck_cycle(aspeed_jtag, 0, ++ tdi & ASPEED_JTAG_DATA_MSB); ++ data[index] |= tdo << (shift_bits % ++ ASPEED_JTAG_DATA_CHUNK_SIZE); ++ tdi >>= 1; ++ shift_bits++; ++ remain_xfer--; ++ ++ if (shift_bits % ASPEED_JTAG_DATA_CHUNK_SIZE == 0) { ++ tdo = 0; ++ index++; ++ tdi = ASPEED_JTAG_GET_TDI(xfer->direction, data[index]); ++ data[index] = 0; ++ } ++ } ++ ++ if ((xfer->endstate == (xfer->type == JTAG_SIR_XFER ? ++ JTAG_STATE_SHIFTIR : JTAG_STATE_SHIFTDR))) { ++ /* Stay in Shift IR/DR*/ ++ tdo = aspeed_jtag_tck_cycle(aspeed_jtag, 0, ++ tdi & ASPEED_JTAG_DATA_MSB); ++ data[index] |= tdo << (shift_bits % ++ ASPEED_JTAG_DATA_CHUNK_SIZE); ++ } else { ++ /* Goto end state */ ++ tdo = aspeed_jtag_tck_cycle(aspeed_jtag, 1, ++ tdi & ASPEED_JTAG_DATA_MSB); ++ data[index] |= tdo << (shift_bits % ++ ASPEED_JTAG_DATA_CHUNK_SIZE); ++ aspeed_jtag->status = (xfer->type == JTAG_SIR_XFER) ? ++ JTAG_STATE_EXIT1IR : JTAG_STATE_EXIT1DR; ++ aspeed_jtag_set_tap_state(aspeed_jtag, aspeed_jtag->status, ++ xfer->endstate); ++ } ++} ++ ++static int aspeed_jtag_xfer_push_data(struct aspeed_jtag *aspeed_jtag, ++ enum jtag_xfer_type type, u32 bits_len) ++{ ++ int res = 0; ++ ++ if (type == JTAG_SIR_XFER) { ++ aspeed_jtag_write(aspeed_jtag, ASPEED_JTAG_IOUT_LEN(bits_len), ++ ASPEED_JTAG_CTRL); ++ aspeed_jtag_write(aspeed_jtag, ASPEED_JTAG_IOUT_LEN(bits_len) | ++ ASPEED_JTAG_CTL_INST_EN, ASPEED_JTAG_CTRL); ++ res = aspeed_jtag_wait_instruction_pause(aspeed_jtag); ++ } else { ++ aspeed_jtag_write(aspeed_jtag, ASPEED_JTAG_DOUT_LEN(bits_len), ++ ASPEED_JTAG_CTRL); ++ aspeed_jtag_write(aspeed_jtag, ASPEED_JTAG_DOUT_LEN(bits_len) | ++ ASPEED_JTAG_CTL_DATA_EN, ASPEED_JTAG_CTRL); ++ res = aspeed_jtag_wait_data_pause_complete(aspeed_jtag); ++ } ++ return res; ++} ++ ++static int aspeed_jtag_xfer_push_data_last(struct aspeed_jtag *aspeed_jtag, ++ enum jtag_xfer_type type, ++ u32 shift_bits) ++{ ++ int res = 0; ++ ++ if (type == JTAG_SIR_XFER) { ++ aspeed_jtag_write(aspeed_jtag, ++ ASPEED_JTAG_IOUT_LEN(shift_bits) | ++ ASPEED_JTAG_CTL_LASPEED_INST, ++ ASPEED_JTAG_CTRL); ++ aspeed_jtag_write(aspeed_jtag, ++ ASPEED_JTAG_IOUT_LEN(shift_bits) | ++ ASPEED_JTAG_CTL_LASPEED_INST | ++ ASPEED_JTAG_CTL_INST_EN, ++ ASPEED_JTAG_CTRL); ++ res = aspeed_jtag_wait_instruction_complete(aspeed_jtag); ++ } else { ++ aspeed_jtag_write(aspeed_jtag, ++ ASPEED_JTAG_DOUT_LEN(shift_bits) | ++ ASPEED_JTAG_CTL_LASPEED_DATA, ++ ASPEED_JTAG_CTRL); ++ aspeed_jtag_write(aspeed_jtag, ++ ASPEED_JTAG_DOUT_LEN(shift_bits) | ++ ASPEED_JTAG_CTL_LASPEED_DATA | ++ ASPEED_JTAG_CTL_DATA_EN, ++ ASPEED_JTAG_CTRL); ++ res = aspeed_jtag_wait_data_complete(aspeed_jtag); ++ } ++ return res; ++} ++ ++static int aspeed_jtag_xfer_hw(struct aspeed_jtag *aspeed_jtag, ++ struct jtag_xfer *xfer, u32 *data) ++{ ++ unsigned long remain_xfer = xfer->length; ++ unsigned long index = 0; ++ char shift_bits; ++ u32 data_reg; ++ u32 scan_end; ++ ++#ifdef DEBUG_JTAG ++ dev_dbg(aspeed_jtag->dev, "HW JTAG SHIFT %s, length = %d\n", ++ (xfer->type == JTAG_SIR_XFER) ? "IR" : "DR", xfer->length); ++#endif ++ data_reg = xfer->type == JTAG_SIR_XFER ? ++ ASPEED_JTAG_INST : ASPEED_JTAG_DATA; ++ if (xfer->endstate == JTAG_STATE_SHIFTIR || ++ xfer->endstate == JTAG_STATE_SHIFTDR || ++ xfer->endstate == JTAG_STATE_PAUSEIR || ++ xfer->endstate == JTAG_STATE_PAUSEDR) { ++ scan_end = 0; ++ } else { ++ scan_end = 1; ++ } ++ ++ while (remain_xfer) { ++ if (xfer->direction & JTAG_WRITE_XFER) ++ aspeed_jtag_write(aspeed_jtag, data[index], data_reg); ++ else ++ aspeed_jtag_write(aspeed_jtag, 0, data_reg); ++ ++ if (remain_xfer > ASPEED_JTAG_DATA_CHUNK_SIZE) { ++#ifdef DEBUG_JTAG ++ dev_dbg(aspeed_jtag->dev, ++ "Chunk len=%d chunk_size=%d remain_xfer=%lu\n", ++ xfer->length, ASPEED_JTAG_DATA_CHUNK_SIZE, ++ remain_xfer); ++#endif ++ shift_bits = ASPEED_JTAG_DATA_CHUNK_SIZE; ++ ++ /* ++ * Transmit bytes that were not equals to column length ++ * and after the transfer go to Pause IR/DR. ++ */ ++ if (aspeed_jtag_xfer_push_data(aspeed_jtag, xfer->type, ++ shift_bits) != 0) { ++ return -EFAULT; ++ } ++ } else { ++ /* ++ * Read bytes equals to column length ++ */ ++ shift_bits = remain_xfer; ++ if (scan_end) { ++ /* ++ * If this data is the end of the transmission ++ * send remaining bits and go to endstate ++ */ ++#ifdef DEBUG_JTAG ++ dev_dbg(aspeed_jtag->dev, ++ "Last len=%d chunk_size=%d remain_xfer=%lu\n", ++ xfer->length, ++ ASPEED_JTAG_DATA_CHUNK_SIZE, ++ remain_xfer); ++#endif ++ if (aspeed_jtag_xfer_push_data_last( ++ aspeed_jtag, ++ xfer->type, ++ shift_bits) != 0) { ++ return -EFAULT; ++ } ++ } else { ++ /* ++ * If transmission is waiting for additional ++ * data send remaining bits and then go to ++ * Pause IR/DR. ++ */ ++#ifdef DEBUG_JTAG ++ dev_dbg(aspeed_jtag->dev, ++ "Tail len=%d chunk_size=%d remain_xfer=%lu\n", ++ xfer->length, ++ ASPEED_JTAG_DATA_CHUNK_SIZE, ++ remain_xfer); ++#endif ++ if (aspeed_jtag_xfer_push_data(aspeed_jtag, ++ xfer->type, ++ shift_bits) ++ != 0) { ++ return -EFAULT; ++ } ++ } ++ } ++ ++ if (xfer->direction & JTAG_READ_XFER) { ++ if (shift_bits < ASPEED_JTAG_DATA_CHUNK_SIZE) { ++ data[index] = aspeed_jtag_read(aspeed_jtag, ++ data_reg); ++ ++ data[index] >>= ASPEED_JTAG_DATA_CHUNK_SIZE - ++ shift_bits; ++ } else { ++ data[index] = aspeed_jtag_read(aspeed_jtag, ++ data_reg); ++ } ++ } ++ ++ remain_xfer = remain_xfer - shift_bits; ++ index++; ++ } ++ return 0; ++} ++ ++static int aspeed_jtag_xfer(struct jtag *jtag, struct jtag_xfer *xfer, ++ u8 *xfer_data) ++{ ++ struct aspeed_jtag *aspeed_jtag = jtag_priv(jtag); ++ ++ if (!(aspeed_jtag->mode & JTAG_XFER_HW_MODE)) { ++ /* SW mode */ ++ aspeed_jtag_write(aspeed_jtag, ASPEED_JTAG_SW_TDIO, ++ ASPEED_JTAG_SW); ++ ++ aspeed_jtag_xfer_sw(aspeed_jtag, xfer, (u32 *)xfer_data); ++ } else { ++ /* HW mode */ ++ aspeed_jtag_write(aspeed_jtag, 0, ASPEED_JTAG_SW); ++ if (aspeed_jtag_xfer_hw(aspeed_jtag, xfer, ++ (u32 *)xfer_data) != 0) ++ return -EFAULT; ++ } ++ ++ aspeed_jtag->status = xfer->endstate; ++ return 0; ++} ++ ++static int aspeed_jtag_status_get(struct jtag *jtag, u32 *status) ++{ ++ struct aspeed_jtag *aspeed_jtag = jtag_priv(jtag); ++ ++ *status = aspeed_jtag->status; ++ return 0; ++} ++ ++#ifdef USE_INTERRUPTS ++static irqreturn_t aspeed_jtag_interrupt(s32 this_irq, void *dev_id) ++{ ++ struct aspeed_jtag *aspeed_jtag = dev_id; ++ irqreturn_t ret = IRQ_HANDLED; ++ u32 status; ++ ++ status = aspeed_jtag_read(aspeed_jtag, ASPEED_JTAG_ISR); ++ ++ if (status & ASPEED_JTAG_ISR_INT_MASK) { ++ aspeed_jtag_write(aspeed_jtag, ++ (status & ASPEED_JTAG_ISR_INT_MASK) ++ | (status & ASPEED_JTAG_ISR_INT_EN_MASK), ++ ASPEED_JTAG_ISR); ++ aspeed_jtag->flag |= status & ASPEED_JTAG_ISR_INT_MASK; ++ } ++ ++ if (aspeed_jtag->flag) { ++ wake_up_interruptible(&aspeed_jtag->jtag_wq); ++ ret = IRQ_HANDLED; ++ } else { ++ dev_err(aspeed_jtag->dev, "irq status:%x\n", ++ status); ++ ret = IRQ_NONE; ++ } ++ return ret; ++} ++#endif ++ ++static int aspeed_jtag_enable(struct jtag *jtag) ++{ ++ struct aspeed_jtag *aspeed_jtag = jtag_priv(jtag); ++ ++ aspeed_jtag_master(aspeed_jtag); ++ return 0; ++} ++ ++static int aspeed_jtag_disable(struct jtag *jtag) ++{ ++ struct aspeed_jtag *aspeed_jtag = jtag_priv(jtag); ++ ++ aspeed_jtag_output_disable(aspeed_jtag); ++ return 0; ++} ++ ++static int aspeed_jtag_init(struct platform_device *pdev, ++ struct aspeed_jtag *aspeed_jtag) ++{ ++ struct resource *res; ++#ifdef USE_INTERRUPTS ++ int err; ++#endif ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ aspeed_jtag->reg_base = devm_ioremap_resource(aspeed_jtag->dev, res); ++ if (IS_ERR(aspeed_jtag->reg_base)) ++ return -ENOMEM; ++ ++ aspeed_jtag->pclk = devm_clk_get(aspeed_jtag->dev, NULL); ++ if (IS_ERR(aspeed_jtag->pclk)) { ++ dev_err(aspeed_jtag->dev, "devm_clk_get failed\n"); ++ return PTR_ERR(aspeed_jtag->pclk); ++ } ++ ++#ifdef USE_INTERRUPTS ++ aspeed_jtag->irq = platform_get_irq(pdev, 0); ++ if (aspeed_jtag->irq < 0) { ++ dev_err(aspeed_jtag->dev, "no irq specified\n"); ++ return -ENOENT; ++ } ++#endif ++ ++ if (clk_prepare_enable(aspeed_jtag->pclk)) { ++ dev_err(aspeed_jtag->dev, "no irq specified\n"); ++ return -ENOENT; ++ } ++ ++ aspeed_jtag->rst = devm_reset_control_get_shared(&pdev->dev, NULL); ++ if (IS_ERR(aspeed_jtag->rst)) { ++ dev_err(aspeed_jtag->dev, ++ "missing or invalid reset controller device tree entry"); ++ return PTR_ERR(aspeed_jtag->rst); ++ } ++ reset_control_deassert(aspeed_jtag->rst); ++ ++#ifdef USE_INTERRUPTS ++ err = devm_request_irq(aspeed_jtag->dev, aspeed_jtag->irq, ++ aspeed_jtag_interrupt, 0, ++ "aspeed-jtag", aspeed_jtag); ++ if (err) { ++ dev_err(aspeed_jtag->dev, "unable to get IRQ"); ++ clk_disable_unprepare(aspeed_jtag->pclk); ++ return err; ++ } ++#endif ++ ++ aspeed_jtag_output_disable(aspeed_jtag); ++ ++ aspeed_jtag->flag = 0; ++ aspeed_jtag->mode = 0; ++ init_waitqueue_head(&aspeed_jtag->jtag_wq); ++ return 0; ++} ++ ++static int aspeed_jtag_deinit(struct platform_device *pdev, ++ struct aspeed_jtag *aspeed_jtag) ++{ ++ aspeed_jtag_write(aspeed_jtag, 0, ASPEED_JTAG_ISR); ++ /* Disable clock */ ++ aspeed_jtag_write(aspeed_jtag, 0, ASPEED_JTAG_CTRL); ++ reset_control_assert(aspeed_jtag->rst); ++ clk_disable_unprepare(aspeed_jtag->pclk); ++ return 0; ++} ++ ++static const struct jtag_ops aspeed_jtag_ops = { ++ .freq_get = aspeed_jtag_freq_get, ++ .freq_set = aspeed_jtag_freq_set, ++ .status_get = aspeed_jtag_status_get, ++ .status_set = aspeed_jtag_status_set, ++ .xfer = aspeed_jtag_xfer, ++ .mode_set = aspeed_jtag_mode_set, ++ .bitbang = aspeed_jtag_bitbang, ++ .enable = aspeed_jtag_enable, ++ .disable = aspeed_jtag_disable ++}; ++ ++static int aspeed_jtag_probe(struct platform_device *pdev) ++{ ++ struct aspeed_jtag *aspeed_jtag; ++ struct jtag *jtag; ++ int err; ++ ++ jtag = jtag_alloc(&pdev->dev, sizeof(*aspeed_jtag), &aspeed_jtag_ops); ++ if (!jtag) ++ return -ENOMEM; ++ ++ platform_set_drvdata(pdev, jtag); ++ aspeed_jtag = jtag_priv(jtag); ++ aspeed_jtag->dev = &pdev->dev; ++ ++ /* Initialize device*/ ++ err = aspeed_jtag_init(pdev, aspeed_jtag); ++ if (err) ++ goto err_jtag_init; ++ ++ /* Initialize JTAG core structure*/ ++ err = devm_jtag_register(aspeed_jtag->dev, jtag); ++ if (err) ++ goto err_jtag_register; ++ ++ return 0; ++ ++err_jtag_register: ++ aspeed_jtag_deinit(pdev, aspeed_jtag); ++err_jtag_init: ++ jtag_free(jtag); ++ return err; ++} ++ ++static int aspeed_jtag_remove(struct platform_device *pdev) ++{ ++ struct jtag *jtag = platform_get_drvdata(pdev); ++ ++ aspeed_jtag_deinit(pdev, jtag_priv(jtag)); ++ return 0; ++} ++ ++static const struct of_device_id aspeed_jtag_of_match[] = { ++ { .compatible = "aspeed,ast2400-jtag", }, ++ { .compatible = "aspeed,ast2500-jtag", }, ++ { .compatible = "aspeed,ast2600-jtag", }, ++ {} ++}; ++ ++static struct platform_driver aspeed_jtag_driver = { ++ .probe = aspeed_jtag_probe, ++ .remove = aspeed_jtag_remove, ++ .driver = { ++ .name = ASPEED_JTAG_NAME, ++ .of_match_table = aspeed_jtag_of_match, ++ }, ++}; ++module_platform_driver(aspeed_jtag_driver); ++ ++MODULE_AUTHOR("Oleksandr Shamray <oleksandrs@mellanox.com>"); ++MODULE_DESCRIPTION("ASPEED JTAG driver"); ++MODULE_LICENSE("GPL v2"); +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0054-Documentation-jtag-Add-bindings-for-Aspeed-SoC.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0054-Documentation-jtag-Add-bindings-for-Aspeed-SoC.patch new file mode 100644 index 000000000..12073da02 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0054-Documentation-jtag-Add-bindings-for-Aspeed-SoC.patch @@ -0,0 +1,107 @@ +From 2a22feac440070b7feaf0a6fe7e7e555d57ca19b Mon Sep 17 00:00:00 2001 +From: "Corona, Ernesto" <ernesto.corona@intel.com> +Date: Wed, 10 Mar 2019 11:45:04 -0800 +Subject: [PATCH] Documentation: jtag: Add bindings for Aspeed SoC + +It has been tested on Mellanox system with BMC equipped with +Aspeed 2520 SoC for programming CPLD devices. + +It also has been tested on Intel systems with BMC equipped with +Aspeed 25x0 SoC for JTAG board communication. + +Signed-off-by: Oleksandr Shamray <oleksandrs@mellanox.com> +Signed-off-by: Jiri Pirko <jiri@mellanox.com> +Signed-off-by: Corona, Ernesto <ernesto.corona@intel.com> +Acked-by: Rob Herring <robh@kernel.org> +Cc: Mark Rutland <mark.rutland@arm.com> +Cc: Joel Stanley <joel@jms.id.au> +Cc: Andrew Jeffery <andrew@aj.id.au> +Cc: Steven A Filary <steven.a.filary@intel.com> +Cc: Bryan Hunt <bryan.hunt@intel.com> +--- +v28->v29 +v27->v28 +v26->v27 +v25->v26 +v24->v25 +v23->v24 +v22->v23 +v21->v22 +v20->v21 +v19->v20 +v18->v19 + +v17->v18 +v16->v17 +v15->v16 +Comments pointed by Joel Stanley <joel.stan@gmail.com> +- change clocks = <&clk_apb> to proper clocks = <&syscon ASPEED_CLK_APB> +- add reset descriptions in bindings file + +v14->v15 +v13->v14 +v12->v13 +v11->v12 +v10->v11 +v9->v10 +v8->v9 +v7->v8 +Comments pointed by pointed by Joel Stanley <joel.stan@gmail.com> +- Change compatible string to ast2400 and ast2000 + +V6->v7 +Comments pointed by Tobias Klauser <tklauser@distanz.ch> + - Fix spell "Doccumentation" -> "Documentation" + +v5->v6 +Comments pointed by Tobias Klauser <tklauser@distanz.ch> +- Small nit: s/documentation/Documentation/ + +v4->v5 + +V3->v4 +Comments pointed by Rob Herring <robh@kernel.org> +- delete unnecessary "status" and "reg-shift" descriptions in + bindings file + +v2->v3 +Comments pointed by Rob Herring <robh@kernel.org> +- split Aspeed jtag driver and binding to separate patches +- delete unnecessary "status" and "reg-shift" descriptions in + bindings file +--- + .../devicetree/bindings/jtag/aspeed-jtag.txt | 22 ++++++++++++++++++++++ + 1 file changed, 22 insertions(+) + create mode 100644 Documentation/devicetree/bindings/jtag/aspeed-jtag.txt + +diff --git a/Documentation/devicetree/bindings/jtag/aspeed-jtag.txt b/Documentation/devicetree/bindings/jtag/aspeed-jtag.txt +new file mode 100644 +index 0000000..7c36eb6 +--- /dev/null ++++ b/Documentation/devicetree/bindings/jtag/aspeed-jtag.txt +@@ -0,0 +1,22 @@ ++Aspeed JTAG driver for ast2400 and ast2500 SoC ++ ++Required properties: ++- compatible: Should be one of ++ - "aspeed,ast2400-jtag" ++ - "aspeed,ast2500-jtag" ++- reg contains the offset and length of the JTAG memory ++ region ++- clocks root clock of bus, should reference the APB ++ clock in the second cell ++- resets phandle to reset controller with the reset number in ++ the second cell ++- interrupts should contain JTAG controller interrupt ++ ++Example: ++jtag: jtag@1e6e4000 { ++ compatible = "aspeed,ast2500-jtag"; ++ reg = <0x1e6e4000 0x1c>; ++ clocks = <&syscon ASPEED_CLK_APB>; ++ resets = <&syscon ASPEED_RESET_JTAG_MASTER>; ++ interrupts = <43>; ++}; +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0055-Documentation-jtag-Add-ABI-documentation.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0055-Documentation-jtag-Add-ABI-documentation.patch new file mode 100644 index 000000000..1e4142035 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0055-Documentation-jtag-Add-ABI-documentation.patch @@ -0,0 +1,310 @@ +From d4c55bd4eeb6d9290025fa353d1787f18bca6ade Mon Sep 17 00:00:00 2001 +From: "Corona, Ernesto" <ernesto.corona@intel.com> +Date: Sun, 10 Mar 2019 11:47:40 -0800 +Subject: [PATCH v29 4/6] Documentation: jtag: Add ABI documentation + +Added document that describe the ABI for JTAG class driver + +Signed-off-by: Oleksandr Shamray <oleksandrs@mellanox.com> +Signed-off-by: Corona, Ernesto <ernesto.corona@intel.com> +Acked-by: Arnd Bergmann <arnd@arndb.de> +Cc: Jonathan Corbet <corbet@lwn.net> +Cc: Jiri Pirko <jiri@mellanox.com> +Cc: Vadim Pasternak <vadimp@mellanox.com> +Cc: Steven A Filary <steven.a.filary@intel.com> +Cc: Bryan Hunt <bryan.hunt@intel.com> +--- +v28->v29 +Comments pointed by Steven Filary <steven.a.filary@intel.com> +- Expand bitbang function to accept multiples bitbang operations within a + single JTAG_IOCBITBANG call. It will receive a buffer with TDI and TMS + values and it is expected that driver fills TDO fields with its + corresponding output value for every transaction. + +v27->v28 +Comments pointed by Steven Filary <steven.a.filary@intel.com> +- Replace JTAG_IOCRUNTEST with JTAG_SIOCSTATE adding support for all TAPC + end states in SW mode using a lookup table to navigate across states. +- Add support for simultaneous READ/WRITE transfers(JTAG_READ_WRITE_XFER). +- Support for switching JTAG controller mode between slave and master + mode. +- Setup JTAG controller mode to master only when the driver is opened, + letting other HW to own the JTAG bus when it isn't in use. +- Include JTAG bit bang IOCTL for low level JTAG control usage + (JTAG_IOCBITBANG). + +v26->v27 +v25->v26 +Comments pointed by Randy Dunlap <rdunlap@infradead.org> +- fix spell in ABI documentation + +v24->v25 +Comments pointed by Greg KH <gregkh@linuxfoundation.org> +- Fixed documentation according to new open() behavior + +v23->v24 +v22->v23 +Comments pointed by Randy Dunlap <rdunlap@infradead.org> +- fix spell in ABI doccumentation + +v21->v22 +Comments pointed by Randy Dunlap <rdunlap@infradead.org> +- fix spell in ABI doccumentation + +v20->v21 +Comments pointed by Randy Dunlap <rdunlap@infradead.org> +- Fix JTAG dirver help in Kconfig + +v19->v20 +Comments pointed by Randy Dunlap <rdunlap@infradead.org> +- Fix JTAG doccumentation + +v18->v19 +Pavel Machek <pavel@ucw.cz> +- Added JTAG doccumentation to Documentation/jtag + +v17->v18 +v16->v17 +v15->v16 +v14->v15 +v13->v14 +v12->v13 +v11->v12 Tobias Klauser <tklauser@distanz.ch> +Comments pointed by +- rename /Documentation/ABI/testing/jatg-dev -> jtag-dev +- Typo: s/interfase/interface +v10->v11 +v9->v10 +Fixes added by Oleksandr: +- change jtag-cdev to jtag-dev in documentation +- update KernelVersion and Date in jtag-dev documentation; +v8->v9 +v7->v8 +v6->v7 +Comments pointed by Pavel Machek <pavel@ucw.cz> +- Added jtag-cdev documentation to Documentation/ABI/testing folder +--- + Documentation/ABI/testing/jtag-dev | 23 ++++++ + Documentation/jtag/overview | 27 +++++++ + Documentation/jtag/transactions | 145 +++++++++++++++++++++++++++++++++++++ + 3 files changed, 195 insertions(+) + create mode 100644 Documentation/ABI/testing/jtag-dev + create mode 100644 Documentation/jtag/overview + create mode 100644 Documentation/jtag/transactions + +diff --git a/Documentation/ABI/testing/jtag-dev b/Documentation/ABI/testing/jtag-dev +new file mode 100644 +index 000000000000..423baab18761 +--- /dev/null ++++ b/Documentation/ABI/testing/jtag-dev +@@ -0,0 +1,23 @@ ++What: /dev/jtag[0-9]+ ++Date: July 2018 ++KernelVersion: 4.20 ++Contact: oleksandrs@mellanox.com ++Description: ++ The misc device files /dev/jtag* are the interface ++ between JTAG master interface and userspace. ++ ++ The ioctl(2)-based ABI is defined and documented in ++ [include/uapi]<linux/jtag.h>. ++ ++ The following file operations are supported: ++ ++ open(2) ++ Opens and allocates file descriptor. ++ ++ ioctl(2) ++ Initiate various actions. ++ See the inline documentation in [include/uapi]<linux/jtag.h> ++ for descriptions of all ioctls. ++ ++Users: ++ userspace tools which wants to access to JTAG bus +diff --git a/Documentation/jtag/overview b/Documentation/jtag/overview +new file mode 100644 +index 000000000000..6a5ec335e313 +--- /dev/null ++++ b/Documentation/jtag/overview +@@ -0,0 +1,27 @@ ++Linux kernel JTAG support ++========================= ++ ++JTAG is an industry standard for verifying hardware. JTAG provides access to ++many logic signals of a complex integrated circuit, including the device pins. ++ ++A JTAG interface is a special interface added to a chip. ++Depending on the version of JTAG, two, four, or five pins are added. ++ ++The connector pins are: ++ TDI (Test Data In) ++ TDO (Test Data Out) ++ TCK (Test Clock) ++ TMS (Test Mode Select) ++ TRST (Test Reset) optional ++ ++JTAG interface is designed to have two parts - basic core driver and ++hardware specific driver. The basic driver introduces a general interface ++which is not dependent of specific hardware. It provides communication ++between user space and hardware specific driver. ++Each JTAG device is represented as a char device from (jtag0, jtag1, ...). ++Access to a JTAG device is performed through IOCTL calls. ++ ++Call flow example: ++User: open -> /dev/jatgX -> JTAG core driver -> JTAG hardware specific driver ++User: ioctl -> /dev/jtagX -> JTAG core driver -> JTAG hardware specific driver ++User: close -> /dev/jatgX -> JTAG core driver -> JTAG hardware specific driver +diff --git a/Documentation/jtag/transactions b/Documentation/jtag/transactions +new file mode 100644 +index 000000000000..f5d4a1ded6cf +--- /dev/null ++++ b/Documentation/jtag/transactions +@@ -0,0 +1,145 @@ ++The JTAG API ++============= ++ ++JTAG master devices can be accessed through a character misc-device. ++Each JTAG master interface can be accessed by using /dev/jtagN. ++ ++JTAG system calls set: ++- SIR (Scan Instruction Register, IEEE 1149.1 Instruction Register scan); ++- SDR (Scan Data Register, IEEE 1149.1 Data Register scan); ++- RUNTEST (Forces the IEEE 1149.1 bus to a run state for a specified ++number of clocks. ++ ++open(), close() ++------- ++open() opens JTAG device. ++ ++Open/Close device: ++- jtag_fd = open("/dev/jtag0", O_RDWR); ++- close(jtag_fd); ++ ++ioctl() ++------- ++All access operations to JTAG devices are performed through ioctl interface. ++The IOCTL interface supports these requests: ++ JTAG_SIOCSTATE - Force JTAG state machine to go into a TAPC state ++ JTAG_SIOCFREQ - Set JTAG TCK frequency ++ JTAG_GIOCFREQ - Get JTAG TCK frequency ++ JTAG_IOCXFER - send/receive JTAG data Xfer ++ JTAG_GIOCSTATUS - get current JTAG TAP state ++ JTAG_SIOCMODE - set JTAG mode flags. ++ JTAG_IOCBITBANG - JTAG bitbang low level control. ++ ++JTAG_SIOCFREQ, JTAG_GIOCFREQ ++------ ++Set/Get JTAG clock speed: ++ ++ unsigned int jtag_fd; ++ ioctl(jtag_fd, JTAG_SIOCFREQ, &frq); ++ ioctl(jtag_fd, JTAG_GIOCFREQ, &frq); ++ ++JTAG_SIOCSTATE ++------ ++Force JTAG state machine to go into a TAPC state ++ ++struct jtag_tap_state { ++ __u8 reset; ++ __u8 from; ++ __u8 endstate; ++ __u8 tck; ++}; ++ ++reset: ++ JTAG_NO_RESET - go through selected endstate from current state ++ JTAG_FORCE_RESET - go through TEST_LOGIC/RESET state before selected endstate ++endstate: completion flag ++tck: clock counter ++ ++Example: ++ struct jtag_tap_state tap_state; ++ ++ tap_state.from = JTAG_STATE_TLRESET; ++ tap_state.endstate = JTAG_STATE_IDLE; ++ tap_state.reset = 0; ++ tap_state.tck = data_p->tck; ++ usleep(25 * 1000); ++ ioctl(jtag_fd, JTAG_SIOCSTATE, &tap_state); ++ ++JTAG_GIOCSTATUS ++------ ++Get JTAG TAPC machine state ++ ++ unsigned int jtag_fd; ++ jtag_tapstate tapstate; ++ ioctl(jtag_fd, JTAG_GIOCSTATUS, &tapstate); ++ ++JTAG_IOCXFER ++------ ++Send SDR/SIR transaction ++ ++struct jtag_xfer { ++ __u8 type; ++ __u8 direction; ++ __u8 from; ++ __u8 endstate; ++ __u8 padding; ++ __u32 length; ++ __u64 tdio; ++}; ++ ++type: transfer type - JTAG_SIR_XFER/JTAG_SDR_XFER ++direction: xfer direction - JTAG_READ_XFER/JTAG_WRITE_XFER/JTAG_READ_WRITE_XFER ++length: xfer data length in bits ++tdio : xfer data array ++from: xfer from state can be current JTAG tap state saved by the driver ++ JTAG_STATE_CURRENT or in a multichain environment any state listed in ++ jtag_tapstate struct saved by your multichain controller software. ++endstate: xfer end state after transaction finish ++ can be: any state listed in jtag_tapstate struct ++ ++Example: ++ struct jtag_xfer xfer; ++ static char buf[64]; ++ static unsigned int buf_len = 0; ++ [...] ++ xfer.type = JTAG_SDR_XFER; ++ xfer.tdio = (__u64)buf; ++ xfer.length = buf_len; ++ xfer.from = JTAG_STATE_TLRESET; ++ xfer.endstate = JTAG_STATE_IDLE; ++ ++ if (is_read) ++ xfer.direction = JTAG_READ_XFER; ++ else if (is_write) ++ xfer.direction = JTAG_WRITE_XFER; ++ else ++ xfer.direction = JTAG_READ_WRITE_XFER; ++ ++ ioctl(jtag_fd, JTAG_IOCXFER, &xfer); ++ ++JTAG_SIOCMODE ++------ ++If hardware driver can support different running modes you can change it. ++ ++Example: ++ struct jtag_mode mode; ++ mode.feature = JTAG_XFER_MODE; ++ mode.mode = JTAG_XFER_HW_MODE; ++ ioctl(jtag_fd, JTAG_SIOCMODE, &mode); ++ ++JTAG_IOCBITBANG ++------ ++JTAG Bitbang low level operation. ++ ++Example: ++ struct bitbang_packet bitbang; ++ struct tck_bitbang bitbang_data[2]; ++ bitbang_data[0].tms = 0; ++ bitbang_data[0].tdi = 1; ++ bitbang_data[1].tms = 0; ++ bitbang_data[1].tdi = 1; ++ bitbang.data = bitbang_data; ++ bitbang.length = 2; ++ ioctl(jtag_fd, JTAG_IOCBITBANG, &bitbang); ++ tdo0 = bitbang_data[0].tdo; ++ tdo1 = bitbang_data[1].tdo; +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0056-Documentation-jtag-Add-JTAG-core-driver-ioctl-number.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0056-Documentation-jtag-Add-JTAG-core-driver-ioctl-number.patch new file mode 100644 index 000000000..99cc318ab --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0056-Documentation-jtag-Add-JTAG-core-driver-ioctl-number.patch @@ -0,0 +1,57 @@ +From 8618c2cd920d10681859bacf8af56c71f168b5a2 Mon Sep 17 00:00:00 2001 +From: "Corona, Ernesto" <ernesto.corona@intel.com> +Date: Sun, 10 Mar 2019 11:48:18 -0800 +Subject: [PATCH] Documentation jtag: Add JTAG core driver ioctl number + +JTAG class driver provide infrastructure to support hardware/software +JTAG platform drivers. It provide user layer API interface for flashing +and debugging external devices which equipped with JTAG interface +using standard transactions. + +Driver exposes set of IOCTL to user space for: +- XFER: + SIR (Scan Instruction Register, IEEE 1149.1 Data Register scan); + SDR (Scan Data Register, IEEE 1149.1 Instruction Register scan); +- GIOCSTATUS read the current TAPC state of the JTAG controller +- SIOCSTATE Forces the JTAG TAPC to go into a particular state. +- SIOCFREQ/GIOCFREQ for setting and reading JTAG frequency. +- IOCBITBANG for low level control of JTAG signals. + +Signed-off-by: Oleksandr Shamray <oleksandrs@mellanox.com> +Signed-off-by: Corona, Ernesto <ernesto.corona@intel.com> +Acked-by: Philippe Ombredanne <pombredanne@nexb.com> +Cc: Jiri Pirko <jiri@mellanox.com> +Cc: Vadim Pasternak <vadimp@mellanox.com> +Cc: Jonathan Corbet <corbet@lwn.net> +Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +Cc: Gustavo Pimentel <gustavo.pimentel@synopsys.com> +Cc: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com> +Cc: Kishon Vijay Abraham I <kishon@ti.com> +Cc: Darrick J. Wong <darrick.wong@oracle.com> +Cc: Bryant G. Ly <bryantly@linux.vnet.ibm.com> +Cc: Eric Sandeen <sandeen@redhat.com> +Cc: Randy Dunlap <rdunlap@infradead.org> +Cc: Tomohiro Kusumi <kusumi.tomohiro@gmail.com> +Cc: Arnd Bergmann <arnd@arndb.de> +Cc: Steven A Filary <steven.a.filary@intel.com> +Cc: Bryan Hunt <bryan.hunt@intel.com> +--- + Documentation/ioctl/ioctl-number.rst | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/Documentation/ioctl/ioctl-number.rst b/Documentation/ioctl/ioctl-number.rst +index de23fc0f0fce..339d198c5adf 100644 +--- a/Documentation/ioctl/ioctl-number.rst ++++ b/Documentation/ioctl/ioctl-number.rst +@@ -333,6 +333,8 @@ Code Seq# Include File Comments + <mailto:vgo@ratio.de> + 0xB1 00-1F PPPoX + <mailto:mostrows@styx.uwaterloo.ca> ++0xB2 00-0F linux/jtag.h JTAG driver ++ <mailto:oleksandrs@mellanox.com> + 0xB3 00 linux/mmc/ioctl.h + 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> +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0057-drivers-jtag-Add-JTAG-core-driver-Maintainers.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0057-drivers-jtag-Add-JTAG-core-driver-Maintainers.patch new file mode 100644 index 000000000..9ff92d8ec --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0057-drivers-jtag-Add-JTAG-core-driver-Maintainers.patch @@ -0,0 +1,50 @@ +From fcf8f7fd3cb4e0b08cb1540f9ff9e5988cd285d1 Mon Sep 17 00:00:00 2001 +From: "Corona, Ernesto" <ernesto.corona@intel.com> +Date: Sun, 10 Mar 2019 11:49:37 -0800 +Subject: [PATCH] drivers: jtag: Add JTAG core driver Maintainers + +JTAG class driver provide infrastructure to support hardware/software +JTAG platform drivers. It provide user layer API interface for flashing +and debugging external devices which equipped with JTAG interface +using standard transactions. + +Signed-off-by: Oleksandr Shamray <oleksandrs@mellanox.com> +Signed-off-by: Corona, Ernesto <ernesto.corona@intel.com> +Acked-by: Arnd Bergmann <arnd@arndb.de> +Cc: Jiri Pirko <jiri@mellanox.com> +Cc: Vadim Pasternak <vadimp@mellanox.com> +Cc: Mauro Carvalho Chehab <mchehab+samsung@kernel.org> +Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +Cc: David S. Miller <davem@davemloft.net> +Cc: Nicolas Ferre <nicolas.ferre@microchip.com> +Cc: Steven A Filary <steven.a.filary@intel.com> +Cc: Bryan Hunt <bryan.hunt@intel.com> +--- + MAINTAINERS | 11 +++++++++++ + 1 file changed, 11 insertions(+) + +diff --git a/MAINTAINERS b/MAINTAINERS +index 3bbdae50eb7e..34ab3522801c 100644 +--- a/MAINTAINERS ++++ b/MAINTAINERS +@@ -8819,6 +8819,17 @@ L: linux-serial@vger.kernel.org + S: Orphan + F: drivers/tty/serial/jsm/ + ++JTAG SUBSYSTEM ++M: Oleksandr Shamray <oleksandrs@mellanox.com> ++M: Vadim Pasternak <vadimp@mellanox.com> ++M Ernesto Corona <ernesto.corona@intel.com> ++S: Maintained ++F: include/linux/jtag.h ++F: include/uapi/linux/jtag.h ++F: drivers/jtag/ ++F: Documentation/devicetree/bindings/jtag/ ++F: Documentation/ABI/testing/jtag-dev ++ + K10TEMP HARDWARE MONITORING DRIVER + M: Clemens Ladisch <clemens@ladisch.de> + L: linux-hwmon@vger.kernel.org +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0061-i2c-aspeed-add-buffer-mode-transfer-support.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0061-i2c-aspeed-add-buffer-mode-transfer-support.patch new file mode 100644 index 000000000..fbf422a35 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0061-i2c-aspeed-add-buffer-mode-transfer-support.patch @@ -0,0 +1,1148 @@ +From b8d7d0f3513abdf014345b240a79d23c63409c4c Mon Sep 17 00:00:00 2001 +From: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +Date: Tue, 11 Jun 2019 15:07:08 -0700 +Subject: [PATCH] i2c: aspeed: add buffer mode transfer support + +Byte mode currently this driver uses makes lots of interrupt call +which isn't good for performance and it makes the driver very +timing sensitive. To improve performance of the driver, this commit +adds buffer mode transfer support which uses I2C SRAM buffer +instead of using a single byte buffer. + +AST2400: +It has 2 KBytes (256 Bytes x 8 pages) of I2C SRAM buffer pool from +0x1e78a800 to 0x1e78afff that can be used for all busses with +buffer pool manipulation. To simplify implementation for supporting +both AST2400 and AST2500, it assigns each 128 Bytes per bus without +using buffer pool manipulation so total 1792 Bytes of I2C SRAM +buffer will be used. + +AST2500: +It has 16 Bytes of individual I2C SRAM buffer per each bus and its +range is from 0x1e78a200 to 0x1e78a2df, so it doesn't have 'buffer +page selection' bit field in the Function control register, and +neither 'base address pointer' bit field in the Pool buffer control +register it has. To simplify implementation for supporting both +AST2400 and AST2500, it writes zeros on those register bit fields +but it's okay because it does nothing in AST2500. + +It provides buffer based master and slave data transfer. + +Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +--- + .../devicetree/bindings/i2c/i2c-aspeed.txt | 40 ++- + arch/arm/boot/dts/aspeed-g4.dtsi | 47 +-- + arch/arm/boot/dts/aspeed-g5.dtsi | 47 +-- + arch/arm/boot/dts/aspeed-g6.dtsi | 32 +- + drivers/i2c/busses/i2c-aspeed.c | 376 +++++++++++++++++++-- + 5 files changed, 451 insertions(+), 91 deletions(-) + +diff --git a/Documentation/devicetree/bindings/i2c/i2c-aspeed.txt b/Documentation/devicetree/bindings/i2c/i2c-aspeed.txt +index 7da7e813b2b0..0ff3539cee95 100644 +--- a/Documentation/devicetree/bindings/i2c/i2c-aspeed.txt ++++ b/Documentation/devicetree/bindings/i2c/i2c-aspeed.txt +@@ -3,7 +3,10 @@ Device tree configuration for the I2C busses on the AST24XX and AST25XX SoCs. + Required Properties: + - #address-cells : should be 1 + - #size-cells : should be 0 +-- reg : address offset and range of bus ++- reg : Address offset and range of bus registers. ++ An additional SRAM buffer address offset and range is ++ optional in case of enabling I2C dedicated SRAM for ++ buffer mode transfer support. + - compatible : should be "aspeed,ast2400-i2c-bus" + or "aspeed,ast2500-i2c-bus" + - clocks : root clock of bus, should reference the APB +@@ -28,12 +31,21 @@ i2c { + #size-cells = <1>; + ranges = <0 0x1e78a000 0x1000>; + +- i2c_ic: interrupt-controller@0 { +- #interrupt-cells = <1>; +- compatible = "aspeed,ast2400-i2c-ic"; ++ i2c_gr: i2c-global-regs@0 { ++ compatible = "aspeed,ast2500-i2c-gr", "syscon"; + reg = <0x0 0x40>; +- interrupts = <12>; +- interrupt-controller; ++ ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ranges = <0x0 0x0 0x40>; ++ ++ i2c_ic: interrupt-controller@0 { ++ #interrupt-cells = <1>; ++ compatible = "aspeed,ast2500-i2c-ic"; ++ reg = <0x0 0x4>; ++ interrupts = <12>; ++ interrupt-controller; ++ }; + }; + + i2c0: i2c-bus@40 { +@@ -41,11 +53,25 @@ i2c { + #size-cells = <0>; + #interrupt-cells = <1>; + reg = <0x40 0x40>; +- compatible = "aspeed,ast2400-i2c-bus"; ++ compatible = "aspeed,ast2500-i2c-bus"; + clocks = <&syscon ASPEED_CLK_APB>; + resets = <&syscon ASPEED_RESET_I2C>; + bus-frequency = <100000>; + interrupts = <0>; + interrupt-parent = <&i2c_ic>; + }; ++ ++ /* buffer mode transfer enabled */ ++ i2c1: i2c-bus@80 { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ #interrupt-cells = <1>; ++ reg = <0x80 0x40>, <0x210 0x10>; ++ compatible = "aspeed,ast2500-i2c-bus"; ++ clocks = <&syscon ASPEED_CLK_APB>; ++ resets = <&syscon ASPEED_RESET_I2C>; ++ bus-frequency = <100000>; ++ interrupts = <1>; ++ interrupt-parent = <&i2c_ic>; ++ }; + }; +diff --git a/arch/arm/boot/dts/aspeed-g4.dtsi b/arch/arm/boot/dts/aspeed-g4.dtsi +index fc6f4e009db7..0b30c8546e64 100644 +--- a/arch/arm/boot/dts/aspeed-g4.dtsi ++++ b/arch/arm/boot/dts/aspeed-g4.dtsi +@@ -518,12 +518,21 @@ + }; + + &i2c { +- i2c_ic: interrupt-controller@0 { +- #interrupt-cells = <1>; +- compatible = "aspeed,ast2400-i2c-ic"; ++ i2c_gr: i2c-global-regs@0 { ++ compatible = "aspeed,ast2400-i2c-gr", "syscon"; + reg = <0x0 0x40>; +- interrupts = <12>; +- interrupt-controller; ++ ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ranges = <0x0 0x0 0x40>; ++ ++ i2c_ic: interrupt-controller@0 { ++ #interrupt-cells = <1>; ++ compatible = "aspeed,ast2400-i2c-ic"; ++ reg = <0x0 0x4>; ++ interrupts = <12>; ++ interrupt-controller; ++ }; + }; + + i2c0: i2c-bus@40 { +@@ -531,7 +540,7 @@ + #size-cells = <0>; + #interrupt-cells = <1>; + +- reg = <0x40 0x40>; ++ reg = <0x40 0x40>, <0x800 0x80>; + compatible = "aspeed,ast2400-i2c-bus"; + clocks = <&syscon ASPEED_CLK_APB>; + resets = <&syscon ASPEED_RESET_I2C>; +@@ -547,7 +556,7 @@ + #size-cells = <0>; + #interrupt-cells = <1>; + +- reg = <0x80 0x40>; ++ reg = <0x80 0x40>, <0x880 0x80>; + compatible = "aspeed,ast2400-i2c-bus"; + clocks = <&syscon ASPEED_CLK_APB>; + resets = <&syscon ASPEED_RESET_I2C>; +@@ -563,7 +572,7 @@ + #size-cells = <0>; + #interrupt-cells = <1>; + +- reg = <0xc0 0x40>; ++ reg = <0xc0 0x40>, <0x900 0x80>; + compatible = "aspeed,ast2400-i2c-bus"; + clocks = <&syscon ASPEED_CLK_APB>; + resets = <&syscon ASPEED_RESET_I2C>; +@@ -580,7 +589,7 @@ + #size-cells = <0>; + #interrupt-cells = <1>; + +- reg = <0x100 0x40>; ++ reg = <0x100 0x40>, <0x980 0x80>; + compatible = "aspeed,ast2400-i2c-bus"; + clocks = <&syscon ASPEED_CLK_APB>; + resets = <&syscon ASPEED_RESET_I2C>; +@@ -597,7 +606,7 @@ + #size-cells = <0>; + #interrupt-cells = <1>; + +- reg = <0x140 0x40>; ++ reg = <0x140 0x40>, <0xa00 0x80>; + compatible = "aspeed,ast2400-i2c-bus"; + clocks = <&syscon ASPEED_CLK_APB>; + resets = <&syscon ASPEED_RESET_I2C>; +@@ -614,7 +623,7 @@ + #size-cells = <0>; + #interrupt-cells = <1>; + +- reg = <0x180 0x40>; ++ reg = <0x180 0x40>, <0xa80 0x80>; + compatible = "aspeed,ast2400-i2c-bus"; + clocks = <&syscon ASPEED_CLK_APB>; + resets = <&syscon ASPEED_RESET_I2C>; +@@ -631,7 +640,7 @@ + #size-cells = <0>; + #interrupt-cells = <1>; + +- reg = <0x1c0 0x40>; ++ reg = <0x1c0 0x40>, <0xb00 0x80>; + compatible = "aspeed,ast2400-i2c-bus"; + clocks = <&syscon ASPEED_CLK_APB>; + resets = <&syscon ASPEED_RESET_I2C>; +@@ -648,7 +657,7 @@ + #size-cells = <0>; + #interrupt-cells = <1>; + +- reg = <0x300 0x40>; ++ reg = <0x300 0x40>, <0xb80 0x80>; + compatible = "aspeed,ast2400-i2c-bus"; + clocks = <&syscon ASPEED_CLK_APB>; + resets = <&syscon ASPEED_RESET_I2C>; +@@ -665,7 +674,7 @@ + #size-cells = <0>; + #interrupt-cells = <1>; + +- reg = <0x340 0x40>; ++ reg = <0x340 0x40>, <0xc00 0x80>; + compatible = "aspeed,ast2400-i2c-bus"; + clocks = <&syscon ASPEED_CLK_APB>; + resets = <&syscon ASPEED_RESET_I2C>; +@@ -682,7 +691,7 @@ + #size-cells = <0>; + #interrupt-cells = <1>; + +- reg = <0x380 0x40>; ++ reg = <0x380 0x40>, <0xc80 0x80>; + compatible = "aspeed,ast2400-i2c-bus"; + clocks = <&syscon ASPEED_CLK_APB>; + resets = <&syscon ASPEED_RESET_I2C>; +@@ -699,7 +708,7 @@ + #size-cells = <0>; + #interrupt-cells = <1>; + +- reg = <0x3c0 0x40>; ++ reg = <0x3c0 0x40>, <0xd00 0x80>; + compatible = "aspeed,ast2400-i2c-bus"; + clocks = <&syscon ASPEED_CLK_APB>; + resets = <&syscon ASPEED_RESET_I2C>; +@@ -716,7 +725,7 @@ + #size-cells = <0>; + #interrupt-cells = <1>; + +- reg = <0x400 0x40>; ++ reg = <0x400 0x40>, <0xd80 0x80>; + compatible = "aspeed,ast2400-i2c-bus"; + clocks = <&syscon ASPEED_CLK_APB>; + resets = <&syscon ASPEED_RESET_I2C>; +@@ -733,7 +742,7 @@ + #size-cells = <0>; + #interrupt-cells = <1>; + +- reg = <0x440 0x40>; ++ reg = <0x440 0x40>, <0xe00 0x80>; + compatible = "aspeed,ast2400-i2c-bus"; + clocks = <&syscon ASPEED_CLK_APB>; + resets = <&syscon ASPEED_RESET_I2C>; +@@ -750,7 +759,7 @@ + #size-cells = <0>; + #interrupt-cells = <1>; + +- reg = <0x480 0x40>; ++ reg = <0x480 0x40>, <0xe80 0x80>; + compatible = "aspeed,ast2400-i2c-bus"; + clocks = <&syscon ASPEED_CLK_APB>; + resets = <&syscon ASPEED_RESET_I2C>; +diff --git a/arch/arm/boot/dts/aspeed-g5.dtsi b/arch/arm/boot/dts/aspeed-g5.dtsi +index eb1f9c9d9cca..51593a0a8a23 100644 +--- a/arch/arm/boot/dts/aspeed-g5.dtsi ++++ b/arch/arm/boot/dts/aspeed-g5.dtsi +@@ -615,12 +615,21 @@ + }; + + &i2c { +- i2c_ic: interrupt-controller@0 { +- #interrupt-cells = <1>; +- compatible = "aspeed,ast2500-i2c-ic"; ++ i2c_gr: i2c-global-regs@0 { ++ compatible = "aspeed,ast2500-i2c-gr", "syscon"; + reg = <0x0 0x40>; +- interrupts = <12>; +- interrupt-controller; ++ ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ranges = <0x0 0x0 0x40>; ++ ++ i2c_ic: interrupt-controller@0 { ++ #interrupt-cells = <1>; ++ compatible = "aspeed,ast2500-i2c-ic"; ++ reg = <0x0 0x4>; ++ interrupts = <12>; ++ interrupt-controller; ++ }; + }; + + i2c0: i2c-bus@40 { +@@ -628,7 +637,7 @@ + #size-cells = <0>; + #interrupt-cells = <1>; + +- reg = <0x40 0x40>; ++ reg = <0x40 0x40>, <0x200 0x10>; + compatible = "aspeed,ast2500-i2c-bus"; + clocks = <&syscon ASPEED_CLK_APB>; + resets = <&syscon ASPEED_RESET_I2C>; +@@ -644,7 +653,7 @@ + #size-cells = <0>; + #interrupt-cells = <1>; + +- reg = <0x80 0x40>; ++ reg = <0x80 0x40>, <0x210 0x10>; + compatible = "aspeed,ast2500-i2c-bus"; + clocks = <&syscon ASPEED_CLK_APB>; + resets = <&syscon ASPEED_RESET_I2C>; +@@ -660,7 +669,7 @@ + #size-cells = <0>; + #interrupt-cells = <1>; + +- reg = <0xc0 0x40>; ++ reg = <0xc0 0x40>, <0x220 0x10>; + compatible = "aspeed,ast2500-i2c-bus"; + clocks = <&syscon ASPEED_CLK_APB>; + resets = <&syscon ASPEED_RESET_I2C>; +@@ -677,7 +686,7 @@ + #size-cells = <0>; + #interrupt-cells = <1>; + +- reg = <0x100 0x40>; ++ reg = <0x100 0x40>, <0x230 0x10>; + compatible = "aspeed,ast2500-i2c-bus"; + clocks = <&syscon ASPEED_CLK_APB>; + resets = <&syscon ASPEED_RESET_I2C>; +@@ -694,7 +703,7 @@ + #size-cells = <0>; + #interrupt-cells = <1>; + +- reg = <0x140 0x40>; ++ reg = <0x140 0x40>, <0x240 0x10>; + compatible = "aspeed,ast2500-i2c-bus"; + clocks = <&syscon ASPEED_CLK_APB>; + resets = <&syscon ASPEED_RESET_I2C>; +@@ -711,7 +720,7 @@ + #size-cells = <0>; + #interrupt-cells = <1>; + +- reg = <0x180 0x40>; ++ reg = <0x180 0x40>, <0x250 0x10>; + compatible = "aspeed,ast2500-i2c-bus"; + clocks = <&syscon ASPEED_CLK_APB>; + resets = <&syscon ASPEED_RESET_I2C>; +@@ -728,7 +737,7 @@ + #size-cells = <0>; + #interrupt-cells = <1>; + +- reg = <0x1c0 0x40>; ++ reg = <0x1c0 0x40>, <0x260 0x10>; + compatible = "aspeed,ast2500-i2c-bus"; + clocks = <&syscon ASPEED_CLK_APB>; + resets = <&syscon ASPEED_RESET_I2C>; +@@ -745,7 +754,7 @@ + #size-cells = <0>; + #interrupt-cells = <1>; + +- reg = <0x300 0x40>; ++ reg = <0x300 0x40>, <0x270 0x10>; + compatible = "aspeed,ast2500-i2c-bus"; + clocks = <&syscon ASPEED_CLK_APB>; + resets = <&syscon ASPEED_RESET_I2C>; +@@ -762,7 +771,7 @@ + #size-cells = <0>; + #interrupt-cells = <1>; + +- reg = <0x340 0x40>; ++ reg = <0x340 0x40>, <0x280 0x10>; + compatible = "aspeed,ast2500-i2c-bus"; + clocks = <&syscon ASPEED_CLK_APB>; + resets = <&syscon ASPEED_RESET_I2C>; +@@ -779,7 +788,7 @@ + #size-cells = <0>; + #interrupt-cells = <1>; + +- reg = <0x380 0x40>; ++ reg = <0x380 0x40>, <0x290 0x10>; + compatible = "aspeed,ast2500-i2c-bus"; + clocks = <&syscon ASPEED_CLK_APB>; + resets = <&syscon ASPEED_RESET_I2C>; +@@ -796,7 +805,7 @@ + #size-cells = <0>; + #interrupt-cells = <1>; + +- reg = <0x3c0 0x40>; ++ reg = <0x3c0 0x40>, <0x2a0 0x10>; + compatible = "aspeed,ast2500-i2c-bus"; + clocks = <&syscon ASPEED_CLK_APB>; + resets = <&syscon ASPEED_RESET_I2C>; +@@ -813,7 +822,7 @@ + #size-cells = <0>; + #interrupt-cells = <1>; + +- reg = <0x400 0x40>; ++ reg = <0x400 0x40>, <0x2b0 0x10>; + compatible = "aspeed,ast2500-i2c-bus"; + clocks = <&syscon ASPEED_CLK_APB>; + resets = <&syscon ASPEED_RESET_I2C>; +@@ -830,7 +839,7 @@ + #size-cells = <0>; + #interrupt-cells = <1>; + +- reg = <0x440 0x40>; ++ reg = <0x440 0x40>, <0x2c0 0x10>; + compatible = "aspeed,ast2500-i2c-bus"; + clocks = <&syscon ASPEED_CLK_APB>; + resets = <&syscon ASPEED_RESET_I2C>; +@@ -847,7 +856,7 @@ + #size-cells = <0>; + #interrupt-cells = <1>; + +- reg = <0x480 0x40>; ++ reg = <0x480 0x40>, <0x2d0 0x10>; + compatible = "aspeed,ast2500-i2c-bus"; + clocks = <&syscon ASPEED_CLK_APB>; + resets = <&syscon ASPEED_RESET_I2C>; +diff --git a/arch/arm/boot/dts/aspeed-g6.dtsi b/arch/arm/boot/dts/aspeed-g6.dtsi +index f92a2aa999d1..3ef312543269 100644 +--- a/arch/arm/boot/dts/aspeed-g6.dtsi ++++ b/arch/arm/boot/dts/aspeed-g6.dtsi +@@ -712,7 +712,7 @@ + #address-cells = <1>; + #size-cells = <0>; + #interrupt-cells = <1>; +- reg = <0x80 0x80>; ++ reg = <0x80 0x80>, <0xc00 0x20>; + compatible = "aspeed,ast2600-i2c-bus"; + clocks = <&syscon ASPEED_CLK_APB2>; + resets = <&syscon ASPEED_RESET_I2C>; +@@ -727,7 +727,7 @@ + #address-cells = <1>; + #size-cells = <0>; + #interrupt-cells = <1>; +- reg = <0x100 0x80>; ++ reg = <0x100 0x80>, <0xc20 0x20>; + compatible = "aspeed,ast2600-i2c-bus"; + clocks = <&syscon ASPEED_CLK_APB2>; + resets = <&syscon ASPEED_RESET_I2C>; +@@ -742,7 +742,7 @@ + #address-cells = <1>; + #size-cells = <0>; + #interrupt-cells = <1>; +- reg = <0x180 0x80>; ++ reg = <0x180 0x80>, <0xc40 0x20>; + compatible = "aspeed,ast2600-i2c-bus"; + clocks = <&syscon ASPEED_CLK_APB2>; + resets = <&syscon ASPEED_RESET_I2C>; +@@ -757,7 +757,7 @@ + #address-cells = <1>; + #size-cells = <0>; + #interrupt-cells = <1>; +- reg = <0x200 0x80>; ++ reg = <0x200 0x80>, <0xc60 0x20>; + compatible = "aspeed,ast2600-i2c-bus"; + clocks = <&syscon ASPEED_CLK_APB2>; + resets = <&syscon ASPEED_RESET_I2C>; +@@ -772,7 +772,7 @@ + #address-cells = <1>; + #size-cells = <0>; + #interrupt-cells = <1>; +- reg = <0x280 0x80>; ++ reg = <0x280 0x80>, <0xc80 0x20>; + compatible = "aspeed,ast2600-i2c-bus"; + clocks = <&syscon ASPEED_CLK_APB2>; + resets = <&syscon ASPEED_RESET_I2C>; +@@ -787,7 +787,7 @@ + #address-cells = <1>; + #size-cells = <0>; + #interrupt-cells = <1>; +- reg = <0x300 0x80>; ++ reg = <0x300 0x80>, <0xca0 0x20>; + compatible = "aspeed,ast2600-i2c-bus"; + clocks = <&syscon ASPEED_CLK_APB2>; + resets = <&syscon ASPEED_RESET_I2C>; +@@ -802,7 +802,7 @@ + #address-cells = <1>; + #size-cells = <0>; + #interrupt-cells = <1>; +- reg = <0x380 0x80>; ++ reg = <0x380 0x80>, <0xcc0 0x20>; + compatible = "aspeed,ast2600-i2c-bus"; + clocks = <&syscon ASPEED_CLK_APB2>; + resets = <&syscon ASPEED_RESET_I2C>; +@@ -817,7 +817,7 @@ + #address-cells = <1>; + #size-cells = <0>; + #interrupt-cells = <1>; +- reg = <0x400 0x80>; ++ reg = <0x400 0x80>, <0xce0 0x20>; + compatible = "aspeed,ast2600-i2c-bus"; + clocks = <&syscon ASPEED_CLK_APB2>; + resets = <&syscon ASPEED_RESET_I2C>; +@@ -832,7 +832,7 @@ + #address-cells = <1>; + #size-cells = <0>; + #interrupt-cells = <1>; +- reg = <0x480 0x80>; ++ reg = <0x480 0x80>, <0xd00 0x20>; + compatible = "aspeed,ast2600-i2c-bus"; + clocks = <&syscon ASPEED_CLK_APB2>; + resets = <&syscon ASPEED_RESET_I2C>; +@@ -847,7 +847,7 @@ + #address-cells = <1>; + #size-cells = <0>; + #interrupt-cells = <1>; +- reg = <0x500 0x80>; ++ reg = <0x500 0x80>, <0xd20 0x20>; + compatible = "aspeed,ast2600-i2c-bus"; + clocks = <&syscon ASPEED_CLK_APB2>; + resets = <&syscon ASPEED_RESET_I2C>; +@@ -862,7 +862,7 @@ + #address-cells = <1>; + #size-cells = <0>; + #interrupt-cells = <1>; +- reg = <0x580 0x80>; ++ reg = <0x580 0x80>, <0xd40 0x20>; + compatible = "aspeed,ast2600-i2c-bus"; + clocks = <&syscon ASPEED_CLK_APB2>; + resets = <&syscon ASPEED_RESET_I2C>; +@@ -877,7 +877,7 @@ + #address-cells = <1>; + #size-cells = <0>; + #interrupt-cells = <1>; +- reg = <0x600 0x80>; ++ reg = <0x600 0x80>, <0xd60 0x20>; + compatible = "aspeed,ast2600-i2c-bus"; + clocks = <&syscon ASPEED_CLK_APB2>; + resets = <&syscon ASPEED_RESET_I2C>; +@@ -892,7 +892,7 @@ + #address-cells = <1>; + #size-cells = <0>; + #interrupt-cells = <1>; +- reg = <0x680 0x80>; ++ reg = <0x680 0x80>, <0xd80 0x20>; + compatible = "aspeed,ast2600-i2c-bus"; + clocks = <&syscon ASPEED_CLK_APB2>; + resets = <&syscon ASPEED_RESET_I2C>; +@@ -907,7 +907,7 @@ + #address-cells = <1>; + #size-cells = <0>; + #interrupt-cells = <1>; +- reg = <0x700 0x80>; ++ reg = <0x700 0x80>, <0xda0 0x20>; + compatible = "aspeed,ast2600-i2c-bus"; + clocks = <&syscon ASPEED_CLK_APB2>; + resets = <&syscon ASPEED_RESET_I2C>; +@@ -922,7 +922,7 @@ + #address-cells = <1>; + #size-cells = <0>; + #interrupt-cells = <1>; +- reg = <0x780 0x80>; ++ reg = <0x780 0x80>, <0xdc0 0x20>; + compatible = "aspeed,ast2600-i2c-bus"; + clocks = <&syscon ASPEED_CLK_APB2>; + resets = <&syscon ASPEED_RESET_I2C>; +@@ -937,7 +937,7 @@ + #address-cells = <1>; + #size-cells = <0>; + #interrupt-cells = <1>; +- reg = <0x800 0x80>; ++ reg = <0x800 0x80>, <0xde0 0x20>; + compatible = "aspeed,ast2600-i2c-bus"; + clocks = <&syscon ASPEED_CLK_APB2>; + resets = <&syscon ASPEED_RESET_I2C>; +diff --git a/drivers/i2c/busses/i2c-aspeed.c b/drivers/i2c/busses/i2c-aspeed.c +index 7becfcd67142..bf72e151d11f 100644 +--- a/drivers/i2c/busses/i2c-aspeed.c ++++ b/drivers/i2c/busses/i2c-aspeed.c +@@ -7,6 +7,7 @@ + * Copyright 2017 Google, Inc. + */ + ++#include <linux/bitfield.h> + #include <linux/clk.h> + #include <linux/completion.h> + #include <linux/err.h> +@@ -19,15 +20,24 @@ + #include <linux/irqchip/chained_irq.h> + #include <linux/irqdomain.h> + #include <linux/kernel.h> ++#include <linux/mfd/syscon.h> + #include <linux/module.h> + #include <linux/of_address.h> + #include <linux/of_irq.h> + #include <linux/of_platform.h> + #include <linux/platform_device.h> ++#include <linux/regmap.h> + #include <linux/reset.h> + #include <linux/slab.h> + +-/* I2C Register */ ++/* I2C Global Registers */ ++/* 0x00 : I2CG Interrupt Status Register */ ++/* 0x08 : I2CG Interrupt Target Assignment */ ++/* 0x0c : I2CG Global Control Register (AST2500) */ ++#define ASPEED_I2CG_GLOBAL_CTRL_REG 0x0c ++#define ASPEED_I2CG_SRAM_BUFFER_EN BIT(0) ++ ++/* I2C Bus Registers */ + #define ASPEED_I2C_FUN_CTRL_REG 0x00 + #define ASPEED_I2C_AC_TIMING_REG1 0x04 + #define ASPEED_I2C_AC_TIMING_REG2 0x08 +@@ -35,14 +45,12 @@ + #define ASPEED_I2C_INTR_STS_REG 0x10 + #define ASPEED_I2C_CMD_REG 0x14 + #define ASPEED_I2C_DEV_ADDR_REG 0x18 ++#define ASPEED_I2C_BUF_CTRL_REG 0x1c + #define ASPEED_I2C_BYTE_BUF_REG 0x20 + +-/* Global Register Definition */ +-/* 0x00 : I2C Interrupt Status Register */ +-/* 0x08 : I2C Interrupt Target Assignment */ +- + /* Device Register Definition */ + /* 0x00 : I2CD Function Control Register */ ++#define ASPEED_I2CD_BUFFER_PAGE_SEL_MASK GENMASK(22, 20) + #define ASPEED_I2CD_MULTI_MASTER_DIS BIT(15) + #define ASPEED_I2CD_SDA_DRIVE_1T_EN BIT(8) + #define ASPEED_I2CD_M_SDA_DRIVE_1T_EN BIT(7) +@@ -102,6 +110,8 @@ + #define ASPEED_I2CD_BUS_RECOVER_CMD BIT(11) + + /* Command Bit */ ++#define ASPEED_I2CD_RX_BUFF_ENABLE BIT(7) ++#define ASPEED_I2CD_TX_BUFF_ENABLE BIT(6) + #define ASPEED_I2CD_M_STOP_CMD BIT(5) + #define ASPEED_I2CD_M_S_RX_CMD_LAST BIT(4) + #define ASPEED_I2CD_M_RX_CMD BIT(3) +@@ -118,6 +128,13 @@ + /* 0x18 : I2CD Slave Device Address Register */ + #define ASPEED_I2CD_DEV_ADDR_MASK GENMASK(6, 0) + ++/* 0x1c : I2CD Buffer Control Register */ ++/* Use 8-bits or 6-bits wide bit fileds to support both AST2400 and AST2500 */ ++#define ASPEED_I2CD_BUF_RX_COUNT_MASK GENMASK(31, 24) ++#define ASPEED_I2CD_BUF_RX_SIZE_MASK GENMASK(23, 16) ++#define ASPEED_I2CD_BUF_TX_COUNT_MASK GENMASK(15, 8) ++#define ASPEED_I2CD_BUF_OFFSET_MASK GENMASK(5, 0) ++ + enum aspeed_i2c_master_state { + ASPEED_I2C_MASTER_INACTIVE, + ASPEED_I2C_MASTER_PENDING, +@@ -163,6 +180,11 @@ struct aspeed_i2c_bus { + int master_xfer_result; + /* Multi-master */ + bool multi_master; ++ /* Buffer mode */ ++ void __iomem *buf_base; ++ u8 buf_offset; ++ u8 buf_page; ++ size_t buf_size; + #if IS_ENABLED(CONFIG_I2C_SLAVE) + struct i2c_client *slave; + enum aspeed_i2c_slave_state slave_state; +@@ -255,6 +277,77 @@ static int aspeed_i2c_recover_bus(struct aspeed_i2c_bus *bus) + } + + #if IS_ENABLED(CONFIG_I2C_SLAVE) ++static inline void ++aspeed_i2c_slave_handle_rx_done(struct aspeed_i2c_bus *bus, u32 irq_status, ++ u8 *value) ++{ ++ if (bus->buf_base && ++ bus->slave_state == ASPEED_I2C_SLAVE_WRITE_RECEIVED && ++ !(irq_status & ASPEED_I2CD_INTR_NORMAL_STOP)) ++ *value = readb(bus->buf_base); ++ else ++ *value = readl(bus->base + ASPEED_I2C_BYTE_BUF_REG) >> 8; ++} ++ ++static inline void ++aspeed_i2c_slave_handle_normal_stop(struct aspeed_i2c_bus *bus, u32 irq_status, ++ u8 *value) ++{ ++ int i, len; ++ ++ if (bus->slave_state == ASPEED_I2C_SLAVE_WRITE_RECEIVED && ++ irq_status & ASPEED_I2CD_INTR_RX_DONE) { ++ if (bus->buf_base) { ++ len = FIELD_GET(ASPEED_I2CD_BUF_RX_COUNT_MASK, ++ readl(bus->base + ++ ASPEED_I2C_BUF_CTRL_REG)); ++ for (i = 0; i < len; i++) { ++ *value = readb(bus->buf_base + i); ++ i2c_slave_event(bus->slave, ++ I2C_SLAVE_WRITE_RECEIVED, ++ value); ++ } ++ } ++ } ++} ++ ++static inline void ++aspeed_i2c_slave_handle_write_requested(struct aspeed_i2c_bus *bus, u8 *value) ++{ ++ if (bus->buf_base) { ++ writel(FIELD_PREP(ASPEED_I2CD_BUF_RX_SIZE_MASK, ++ bus->buf_size - 1) | ++ FIELD_PREP(ASPEED_I2CD_BUF_OFFSET_MASK, ++ bus->buf_offset), ++ bus->base + ASPEED_I2C_BUF_CTRL_REG); ++ writel(ASPEED_I2CD_RX_BUFF_ENABLE, ++ bus->base + ASPEED_I2C_CMD_REG); ++ } ++} ++ ++static inline void ++aspeed_i2c_slave_handle_write_received(struct aspeed_i2c_bus *bus, u8 *value) ++{ ++ int i, len; ++ ++ if (bus->buf_base) { ++ len = FIELD_GET(ASPEED_I2CD_BUF_RX_COUNT_MASK, ++ readl(bus->base + ++ ASPEED_I2C_BUF_CTRL_REG)); ++ for (i = 1; i < len; i++) { ++ *value = readb(bus->buf_base + i); ++ i2c_slave_event(bus->slave, I2C_SLAVE_WRITE_RECEIVED, ++ value); ++ } ++ writel(FIELD_PREP(ASPEED_I2CD_BUF_RX_SIZE_MASK, ++ bus->buf_size - 1) | ++ FIELD_PREP(ASPEED_I2CD_BUF_OFFSET_MASK, bus->buf_offset), ++ bus->base + ASPEED_I2C_BUF_CTRL_REG); ++ writel(ASPEED_I2CD_RX_BUFF_ENABLE, ++ bus->base + ASPEED_I2C_CMD_REG); ++ } ++} ++ + static u32 aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus, u32 irq_status) + { + u32 command, irq_handled = 0; +@@ -281,7 +374,7 @@ static u32 aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus, u32 irq_status) + + /* Slave was sent something. */ + if (irq_status & ASPEED_I2CD_INTR_RX_DONE) { +- value = readl(bus->base + ASPEED_I2C_BYTE_BUF_REG) >> 8; ++ aspeed_i2c_slave_handle_rx_done(bus, irq_status, &value); + /* Handle address frame. */ + if (bus->slave_state == ASPEED_I2C_SLAVE_START) { + if (value & 0x1) +@@ -296,9 +389,11 @@ static u32 aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus, u32 irq_status) + + /* Slave was asked to stop. */ + if (irq_status & ASPEED_I2CD_INTR_NORMAL_STOP) { ++ aspeed_i2c_slave_handle_normal_stop(bus, irq_status, &value); + irq_handled |= ASPEED_I2CD_INTR_NORMAL_STOP; + bus->slave_state = ASPEED_I2C_SLAVE_STOP; + } ++ + if (irq_status & ASPEED_I2CD_INTR_TX_NAK && + bus->slave_state == ASPEED_I2C_SLAVE_READ_PROCESSED) { + irq_handled |= ASPEED_I2CD_INTR_TX_NAK; +@@ -328,9 +423,11 @@ static u32 aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus, u32 irq_status) + case ASPEED_I2C_SLAVE_WRITE_REQUESTED: + bus->slave_state = ASPEED_I2C_SLAVE_WRITE_RECEIVED; + i2c_slave_event(slave, I2C_SLAVE_WRITE_REQUESTED, &value); ++ aspeed_i2c_slave_handle_write_requested(bus, &value); + break; + case ASPEED_I2C_SLAVE_WRITE_RECEIVED: + i2c_slave_event(slave, I2C_SLAVE_WRITE_RECEIVED, &value); ++ aspeed_i2c_slave_handle_write_received(bus, &value); + break; + case ASPEED_I2C_SLAVE_STOP: + i2c_slave_event(slave, I2C_SLAVE_STOP, &value); +@@ -350,12 +447,76 @@ static u32 aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus, u32 irq_status) + } + #endif /* CONFIG_I2C_SLAVE */ + ++static inline u32 ++aspeed_i2c_prepare_rx_buf(struct aspeed_i2c_bus *bus, struct i2c_msg *msg) ++{ ++ u32 command = 0; ++ int len; ++ ++ if (msg->len > bus->buf_size) { ++ len = bus->buf_size; ++ } else { ++ len = msg->len; ++ command |= ASPEED_I2CD_M_S_RX_CMD_LAST; ++ } ++ ++ if (bus->buf_base) { ++ command |= ASPEED_I2CD_RX_BUFF_ENABLE; ++ ++ writel(FIELD_PREP(ASPEED_I2CD_BUF_RX_SIZE_MASK, len - 1) | ++ FIELD_PREP(ASPEED_I2CD_BUF_OFFSET_MASK, bus->buf_offset), ++ bus->base + ASPEED_I2C_BUF_CTRL_REG); ++ } ++ ++ return command; ++} ++ ++static inline u32 ++aspeed_i2c_prepare_tx_buf(struct aspeed_i2c_bus *bus, struct i2c_msg *msg) ++{ ++ u8 slave_addr = i2c_8bit_addr_from_msg(msg); ++ u32 command = 0; ++ int len; ++ ++ if (msg->len + 1 > bus->buf_size) ++ len = bus->buf_size; ++ else ++ len = msg->len + 1; ++ ++ if (bus->buf_base) { ++ u8 wbuf[4]; ++ int i; ++ ++ command |= ASPEED_I2CD_TX_BUFF_ENABLE; ++ ++ /* ++ * Yeah, it looks bad but byte writing on remapped I2C SRAM ++ * causes corruption so use this way to make dword writings. ++ */ ++ wbuf[0] = slave_addr; ++ for (i = 1; i < len; i++) { ++ wbuf[i % 4] = msg->buf[i - 1]; ++ if (i % 4 == 3) ++ writel(*(u32 *)wbuf, bus->buf_base + i - 3); ++ } ++ if (--i % 4 != 3) ++ writel(*(u32 *)wbuf, bus->buf_base + i - (i % 4)); ++ ++ writel(FIELD_PREP(ASPEED_I2CD_BUF_TX_COUNT_MASK, len - 1) | ++ FIELD_PREP(ASPEED_I2CD_BUF_OFFSET_MASK, bus->buf_offset), ++ bus->base + ASPEED_I2C_BUF_CTRL_REG); ++ } ++ ++ bus->buf_index = len - 1; ++ ++ return command; ++} ++ + /* precondition: bus.lock has been acquired. */ + static void aspeed_i2c_do_start(struct aspeed_i2c_bus *bus) + { + u32 command = ASPEED_I2CD_M_START_CMD | ASPEED_I2CD_M_TX_CMD; + struct i2c_msg *msg = &bus->msgs[bus->msgs_index]; +- u8 slave_addr = i2c_8bit_addr_from_msg(msg); + + #if IS_ENABLED(CONFIG_I2C_SLAVE) + /* +@@ -374,12 +535,21 @@ static void aspeed_i2c_do_start(struct aspeed_i2c_bus *bus) + + if (msg->flags & I2C_M_RD) { + command |= ASPEED_I2CD_M_RX_CMD; +- /* Need to let the hardware know to NACK after RX. */ +- if (msg->len == 1 && !(msg->flags & I2C_M_RECV_LEN)) +- command |= ASPEED_I2CD_M_S_RX_CMD_LAST; ++ if (!(msg->flags & I2C_M_RECV_LEN)) { ++ if (msg->len && bus->buf_base) ++ command |= aspeed_i2c_prepare_rx_buf(bus, msg); ++ ++ /* Need to let the hardware know to NACK after RX. */ ++ if (msg->len <= 1) ++ command |= ASPEED_I2CD_M_S_RX_CMD_LAST; ++ } ++ } else if (msg->len && bus->buf_base) { ++ command |= aspeed_i2c_prepare_tx_buf(bus, msg); + } + +- writel(slave_addr, bus->base + ASPEED_I2C_BYTE_BUF_REG); ++ if (!(command & ASPEED_I2CD_TX_BUFF_ENABLE)) ++ writel(i2c_8bit_addr_from_msg(msg), ++ bus->base + ASPEED_I2C_BYTE_BUF_REG); + writel(command, bus->base + ASPEED_I2C_CMD_REG); + } + +@@ -414,6 +584,104 @@ static int aspeed_i2c_is_irq_error(u32 irq_status) + return 0; + } + ++static inline u32 ++aspeed_i2c_master_handle_tx_first(struct aspeed_i2c_bus *bus, ++ struct i2c_msg *msg) ++{ ++ u32 command = 0; ++ ++ if (bus->buf_base) { ++ u8 wbuf[4]; ++ int len; ++ int i; ++ ++ if (msg->len - bus->buf_index > bus->buf_size) ++ len = bus->buf_size; ++ else ++ len = msg->len - bus->buf_index; ++ ++ ++ command |= ASPEED_I2CD_TX_BUFF_ENABLE; ++ ++ if (msg->len - bus->buf_index > bus->buf_size) ++ len = bus->buf_size; ++ else ++ len = msg->len - bus->buf_index; ++ ++ for (i = 0; i < len; i++) { ++ wbuf[i % 4] = msg->buf[bus->buf_index + i]; ++ if (i % 4 == 3) ++ writel(*(u32 *)wbuf, ++ bus->buf_base + i - 3); ++ } ++ if (--i % 4 != 3) ++ writel(*(u32 *)wbuf, ++ bus->buf_base + i - (i % 4)); ++ ++ writel(FIELD_PREP(ASPEED_I2CD_BUF_TX_COUNT_MASK, ++ len - 1) | ++ FIELD_PREP(ASPEED_I2CD_BUF_OFFSET_MASK, ++ bus->buf_offset), ++ bus->base + ASPEED_I2C_BUF_CTRL_REG); ++ ++ bus->buf_index += len; ++ } else { ++ writel(msg->buf[bus->buf_index++], ++ bus->base + ASPEED_I2C_BYTE_BUF_REG); ++ } ++ ++ return command; ++} ++ ++static inline void ++aspeed_i2c_master_handle_rx(struct aspeed_i2c_bus *bus, struct i2c_msg *msg) ++{ ++ u8 recv_byte; ++ int len; ++ ++ if (bus->buf_base) { ++ len = FIELD_GET(ASPEED_I2CD_BUF_RX_COUNT_MASK, ++ readl(bus->base + ASPEED_I2C_BUF_CTRL_REG)); ++ memcpy_fromio(msg->buf + bus->buf_index, bus->buf_base, len); ++ bus->buf_index += len; ++ } else { ++ recv_byte = readl(bus->base + ASPEED_I2C_BYTE_BUF_REG) >> 8; ++ msg->buf[bus->buf_index++] = recv_byte; ++ } ++} ++ ++static inline u32 ++aspeed_i2c_master_handle_rx_next(struct aspeed_i2c_bus *bus, ++ struct i2c_msg *msg) ++{ ++ u32 command = 0; ++ ++ if (bus->buf_base) { ++ int len; ++ ++ if (msg->len - bus->buf_index > bus->buf_size) { ++ len = bus->buf_size; ++ } else { ++ len = msg->len - bus->buf_index; ++ command |= ASPEED_I2CD_M_S_RX_CMD_LAST; ++ } ++ ++ command |= ASPEED_I2CD_RX_BUFF_ENABLE; ++ ++ writel(FIELD_PREP(ASPEED_I2CD_BUF_RX_SIZE_MASK, ++ len - 1) | ++ FIELD_PREP(ASPEED_I2CD_BUF_TX_COUNT_MASK, 0) | ++ FIELD_PREP(ASPEED_I2CD_BUF_OFFSET_MASK, ++ bus->buf_offset), ++ bus->base + ASPEED_I2C_BUF_CTRL_REG); ++ } else { ++ if (bus->buf_index + 1 == msg->len) ++ command |= ASPEED_I2CD_M_S_RX_CMD_LAST; ++ } ++ ++ return command; ++} ++ + static u32 aspeed_i2c_master_irq(struct aspeed_i2c_bus *bus, u32 irq_status) + { + u32 irq_handled = 0, command = 0; +@@ -522,11 +790,10 @@ static u32 aspeed_i2c_master_irq(struct aspeed_i2c_bus *bus, u32 irq_status) + /* fall through */ + case ASPEED_I2C_MASTER_TX_FIRST: + if (bus->buf_index < msg->len) { ++ command = ASPEED_I2CD_M_TX_CMD; ++ command |= aspeed_i2c_master_handle_tx_first(bus, msg); ++ writel(command, bus->base + ASPEED_I2C_CMD_REG); + bus->master_state = ASPEED_I2C_MASTER_TX; +- writel(msg->buf[bus->buf_index++], +- bus->base + ASPEED_I2C_BYTE_BUF_REG); +- writel(ASPEED_I2CD_M_TX_CMD, +- bus->base + ASPEED_I2C_CMD_REG); + } else { + aspeed_i2c_next_msg_or_stop(bus); + } +@@ -543,26 +810,26 @@ static u32 aspeed_i2c_master_irq(struct aspeed_i2c_bus *bus, u32 irq_status) + } + irq_handled |= ASPEED_I2CD_INTR_RX_DONE; + +- recv_byte = readl(bus->base + ASPEED_I2C_BYTE_BUF_REG) >> 8; +- msg->buf[bus->buf_index++] = recv_byte; +- + if (msg->flags & I2C_M_RECV_LEN) { ++ recv_byte = readl(bus->base + ++ ASPEED_I2C_BYTE_BUF_REG) >> 8; + if (unlikely(recv_byte > I2C_SMBUS_BLOCK_MAX)) { + bus->cmd_err = -EPROTO; + aspeed_i2c_do_stop(bus); + goto out_no_complete; + } +- msg->len = recv_byte + +- ((msg->flags & I2C_CLIENT_PEC) ? 2 : 1); ++ msg->len = recv_byte + ((msg->flags & I2C_CLIENT_PEC) ? ++ 2 : 1); + msg->flags &= ~I2C_M_RECV_LEN; ++ } else if (msg->len) { ++ aspeed_i2c_master_handle_rx(bus, msg); + } + + if (bus->buf_index < msg->len) { +- bus->master_state = ASPEED_I2C_MASTER_RX; + command = ASPEED_I2CD_M_RX_CMD; +- if (bus->buf_index + 1 == msg->len) +- command |= ASPEED_I2CD_M_S_RX_CMD_LAST; ++ command |= aspeed_i2c_master_handle_rx_next(bus, msg); + writel(command, bus->base + ASPEED_I2C_CMD_REG); ++ bus->master_state = ASPEED_I2C_MASTER_RX; + } else { + aspeed_i2c_next_msg_or_stop(bus); + } +@@ -924,6 +1191,9 @@ static int aspeed_i2c_init(struct aspeed_i2c_bus *bus, + if (ret < 0) + return ret; + ++ fun_ctrl_reg |= FIELD_PREP(ASPEED_I2CD_BUFFER_PAGE_SEL_MASK, ++ bus->buf_page); ++ + if (of_property_read_bool(pdev->dev.of_node, "multi-master")) + bus->multi_master = true; + else +@@ -964,6 +1234,52 @@ static int aspeed_i2c_reset(struct aspeed_i2c_bus *bus) + return ret; + } + ++static void aspeed_i2c_set_xfer_mode(struct aspeed_i2c_bus *bus) ++{ ++ struct platform_device *pdev = to_platform_device(bus->dev); ++ bool sram_enabled = true; ++ int ret; ++ ++ /* ++ * Enable I2C SRAM in case of AST2500. ++ * SRAM is enabled by default in AST2400 and AST2600. ++ */ ++ if (of_device_is_compatible(pdev->dev.of_node, ++ "aspeed,ast2500-i2c-bus")) { ++ struct regmap *gr_regmap = syscon_regmap_lookup_by_compatible("aspeed,ast2500-i2c-gr"); ++ ++ if (IS_ERR(gr_regmap)) ++ ret = PTR_ERR(gr_regmap); ++ else ++ ret = regmap_update_bits(gr_regmap, ++ ASPEED_I2CG_GLOBAL_CTRL_REG, ++ ASPEED_I2CG_SRAM_BUFFER_EN, ++ ASPEED_I2CG_SRAM_BUFFER_EN); ++ ++ if (ret) ++ sram_enabled = false; ++ } ++ ++ if (sram_enabled) { ++ struct resource *res = platform_get_resource(pdev, ++ IORESOURCE_MEM, 1); ++ ++ if (res && resource_size(res) >= 2) ++ bus->buf_base = devm_ioremap_resource(&pdev->dev, res); ++ ++ if (!IS_ERR_OR_NULL(bus->buf_base)) { ++ bus->buf_size = resource_size(res); ++ if (of_device_is_compatible(pdev->dev.of_node, ++ "aspeed,ast2400-i2c-bus")) { ++ bus->buf_page = ((res->start >> 8) & ++ GENMASK(3, 0)) - 8; ++ bus->buf_offset = (res->start >> 2) & ++ ASPEED_I2CD_BUF_OFFSET_MASK; ++ } ++ } ++ } ++} ++ + static const struct of_device_id aspeed_i2c_bus_of_table[] = { + { + .compatible = "aspeed,ast2400-i2c-bus", +@@ -986,18 +1302,18 @@ static int aspeed_i2c_probe_bus(struct platform_device *pdev) + const struct of_device_id *match; + struct aspeed_i2c_bus *bus; + struct clk *parent_clk; +- struct resource *res; + int irq, ret; + + bus = devm_kzalloc(&pdev->dev, sizeof(*bus), GFP_KERNEL); + if (!bus) + return -ENOMEM; + +- res = platform_get_resource(pdev, IORESOURCE_MEM, 0); +- bus->base = devm_ioremap_resource(&pdev->dev, res); ++ bus->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(bus->base)) + return PTR_ERR(bus->base); + ++ bus->dev = &pdev->dev; ++ + parent_clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(parent_clk)) + return PTR_ERR(parent_clk); +@@ -1028,6 +1344,8 @@ static int aspeed_i2c_probe_bus(struct platform_device *pdev) + bus->get_clk_reg_val = (u32 (*)(struct device *, u32)) + match->data; + ++ aspeed_i2c_set_xfer_mode(bus); ++ + /* Initialize the I2C adapter */ + spin_lock_init(&bus->lock); + init_completion(&bus->cmd_complete); +@@ -1038,8 +1356,6 @@ static int aspeed_i2c_probe_bus(struct platform_device *pdev) + strlcpy(bus->adap.name, pdev->name, sizeof(bus->adap.name)); + i2c_set_adapdata(&bus->adap, bus); + +- bus->dev = &pdev->dev; +- + /* Clean up any left over interrupt state. */ + writel(0, bus->base + ASPEED_I2C_INTR_CTRL_REG); + writel(0xffffffff, bus->base + ASPEED_I2C_INTR_STS_REG); +@@ -1063,8 +1379,8 @@ static int aspeed_i2c_probe_bus(struct platform_device *pdev) + + platform_set_drvdata(pdev, bus); + +- dev_info(bus->dev, "i2c bus %d registered, irq %d\n", +- bus->adap.nr, irq); ++ dev_info(bus->dev, "i2c bus %d registered (%s mode), irq %d\n", ++ bus->adap.nr, bus->buf_base ? "buffer" : "byte", irq); + + return 0; + } +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0062-i2c-aspeed-add-DMA-mode-transfer-support.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0062-i2c-aspeed-add-DMA-mode-transfer-support.patch new file mode 100644 index 000000000..994c20b18 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0062-i2c-aspeed-add-DMA-mode-transfer-support.patch @@ -0,0 +1,491 @@ +From 5bf7a202eb0af455675a85a4f1bdad863fd8805d Mon Sep 17 00:00:00 2001 +From: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +Date: Tue, 18 Jun 2019 08:47:50 -0700 +Subject: [PATCH] i2c: aspeed: add DMA mode transfer support + +This commit adds DMA mode transfer support. + +Only AST2500 supports DMA mode under some limitations: +I2C is sharing the DMA H/W with UHCI host controller and MCTP +controller. Since those controllers operate with DMA mode only, I2C +has to use buffer mode or byte mode instead if one of those +controllers is enabled. Also make sure that if SD/eMMC or Port80 +snoop uses DMA mode instead of PIO or FIFO respectively, I2C can't +use DMA mode. + +Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +--- + .../devicetree/bindings/i2c/i2c-aspeed.txt | 25 ++ + drivers/i2c/busses/i2c-aspeed.c | 253 +++++++++++++++++---- + 2 files changed, 234 insertions(+), 44 deletions(-) + +diff --git a/Documentation/devicetree/bindings/i2c/i2c-aspeed.txt b/Documentation/devicetree/bindings/i2c/i2c-aspeed.txt +index 0ff3539cee95..d3f4a39f7ba6 100644 +--- a/Documentation/devicetree/bindings/i2c/i2c-aspeed.txt ++++ b/Documentation/devicetree/bindings/i2c/i2c-aspeed.txt +@@ -22,6 +22,16 @@ Optional Properties: + - bus-timeout-ms: bus timeout in milliseconds defaults to 1 second when not + specified. + - #retries : Number of retries for master transfer. ++- aspeed,dma-buf-size : size of DMA buffer (from 2 to 4095 in case of AST2500) ++ Only AST2500 supports DMA mode under some limitations: ++ I2C is sharing the DMA H/W with UHCI host controller ++ and MCTP controller. Since those controllers operate ++ with DMA mode only, I2C has to use buffer mode or byte ++ mode instead if one of those controllers is enabled. ++ Also make sure that if SD/eMMC or Port80 snoop uses ++ DMA mode instead of PIO or FIFO respectively, I2C ++ can't use DMA mode. IF both DMA and buffer modes are ++ enabled, DMA mode will be selected. + + Example: + +@@ -74,4 +84,19 @@ i2c { + interrupts = <1>; + interrupt-parent = <&i2c_ic>; + }; ++ ++ /* DMA mode transfer enabled */ ++ i2c2: i2c-bus@c0 { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ #interrupt-cells = <1>; ++ reg = <0xc0 0x40>; ++ aspeed,dma-buf-size = <4095>; ++ compatible = "aspeed,ast2500-i2c-bus"; ++ clocks = <&syscon ASPEED_CLK_APB>; ++ resets = <&syscon ASPEED_RESET_I2C>; ++ bus-frequency = <100000>; ++ interrupts = <2>; ++ interrupt-parent = <&i2c_ic>; ++ }; + }; +diff --git a/drivers/i2c/busses/i2c-aspeed.c b/drivers/i2c/busses/i2c-aspeed.c +index bf72e151d11f..3c9e491cbe4e 100644 +--- a/drivers/i2c/busses/i2c-aspeed.c ++++ b/drivers/i2c/busses/i2c-aspeed.c +@@ -10,6 +10,8 @@ + #include <linux/bitfield.h> + #include <linux/clk.h> + #include <linux/completion.h> ++#include <linux/dma-mapping.h> ++#include <linux/dmapool.h> + #include <linux/err.h> + #include <linux/errno.h> + #include <linux/i2c.h> +@@ -47,6 +49,8 @@ + #define ASPEED_I2C_DEV_ADDR_REG 0x18 + #define ASPEED_I2C_BUF_CTRL_REG 0x1c + #define ASPEED_I2C_BYTE_BUF_REG 0x20 ++#define ASPEED_I2C_DMA_ADDR_REG 0x24 ++#define ASPEED_I2C_DMA_LEN_REG 0x28 + + /* Device Register Definition */ + /* 0x00 : I2CD Function Control Register */ +@@ -110,6 +114,8 @@ + #define ASPEED_I2CD_BUS_RECOVER_CMD BIT(11) + + /* Command Bit */ ++#define ASPEED_I2CD_RX_DMA_ENABLE BIT(9) ++#define ASPEED_I2CD_TX_DMA_ENABLE BIT(8) + #define ASPEED_I2CD_RX_BUFF_ENABLE BIT(7) + #define ASPEED_I2CD_TX_BUFF_ENABLE BIT(6) + #define ASPEED_I2CD_M_STOP_CMD BIT(5) +@@ -135,6 +141,14 @@ + #define ASPEED_I2CD_BUF_TX_COUNT_MASK GENMASK(15, 8) + #define ASPEED_I2CD_BUF_OFFSET_MASK GENMASK(5, 0) + ++/* 0x24 : I2CD DMA Mode Buffer Address Register */ ++#define ASPEED_I2CD_DMA_ADDR_MASK GENMASK(31, 2) ++#define ASPEED_I2CD_DMA_ALIGN 4 ++ ++/* 0x28 : I2CD DMA Transfer Length Register */ ++#define ASPEED_I2CD_DMA_LEN_SHIFT 0 ++#define ASPEED_I2CD_DMA_LEN_MASK GENMASK(11, 0) ++ + enum aspeed_i2c_master_state { + ASPEED_I2C_MASTER_INACTIVE, + ASPEED_I2C_MASTER_PENDING, +@@ -184,6 +198,12 @@ struct aspeed_i2c_bus { + void __iomem *buf_base; + u8 buf_offset; + u8 buf_page; ++ /* DMA mode */ ++ struct dma_pool *dma_pool; ++ dma_addr_t dma_handle; ++ u8 *dma_buf; ++ size_t dma_len; ++ /* Buffer/DMA mode */ + size_t buf_size; + #if IS_ENABLED(CONFIG_I2C_SLAVE) + struct i2c_client *slave; +@@ -281,7 +301,11 @@ static inline void + aspeed_i2c_slave_handle_rx_done(struct aspeed_i2c_bus *bus, u32 irq_status, + u8 *value) + { +- if (bus->buf_base && ++ if (bus->dma_buf && ++ bus->slave_state == ASPEED_I2C_SLAVE_WRITE_RECEIVED && ++ !(irq_status & ASPEED_I2CD_INTR_NORMAL_STOP)) ++ *value = bus->dma_buf[0]; ++ else if (bus->buf_base && + bus->slave_state == ASPEED_I2C_SLAVE_WRITE_RECEIVED && + !(irq_status & ASPEED_I2CD_INTR_NORMAL_STOP)) + *value = readb(bus->buf_base); +@@ -297,7 +321,18 @@ aspeed_i2c_slave_handle_normal_stop(struct aspeed_i2c_bus *bus, u32 irq_status, + + if (bus->slave_state == ASPEED_I2C_SLAVE_WRITE_RECEIVED && + irq_status & ASPEED_I2CD_INTR_RX_DONE) { +- if (bus->buf_base) { ++ if (bus->dma_buf) { ++ len = bus->buf_size - ++ FIELD_GET(ASPEED_I2CD_DMA_LEN_MASK, ++ readl(bus->base + ++ ASPEED_I2C_DMA_LEN_REG)); ++ for (i = 0; i < len; i++) { ++ *value = bus->dma_buf[i]; ++ i2c_slave_event(bus->slave, ++ I2C_SLAVE_WRITE_RECEIVED, ++ value); ++ } ++ } else if (bus->buf_base) { + len = FIELD_GET(ASPEED_I2CD_BUF_RX_COUNT_MASK, + readl(bus->base + + ASPEED_I2C_BUF_CTRL_REG)); +@@ -314,7 +349,14 @@ aspeed_i2c_slave_handle_normal_stop(struct aspeed_i2c_bus *bus, u32 irq_status, + static inline void + aspeed_i2c_slave_handle_write_requested(struct aspeed_i2c_bus *bus, u8 *value) + { +- if (bus->buf_base) { ++ if (bus->dma_buf) { ++ writel(bus->dma_handle & ASPEED_I2CD_DMA_ADDR_MASK, ++ bus->base + ASPEED_I2C_DMA_ADDR_REG); ++ writel(FIELD_PREP(ASPEED_I2CD_DMA_LEN_MASK, bus->buf_size), ++ bus->base + ASPEED_I2C_DMA_LEN_REG); ++ writel(ASPEED_I2CD_RX_DMA_ENABLE, ++ bus->base + ASPEED_I2C_CMD_REG); ++ } else if (bus->buf_base) { + writel(FIELD_PREP(ASPEED_I2CD_BUF_RX_SIZE_MASK, + bus->buf_size - 1) | + FIELD_PREP(ASPEED_I2CD_BUF_OFFSET_MASK, +@@ -330,7 +372,23 @@ aspeed_i2c_slave_handle_write_received(struct aspeed_i2c_bus *bus, u8 *value) + { + int i, len; + +- if (bus->buf_base) { ++ if (bus->dma_buf) { ++ len = bus->buf_size - ++ FIELD_GET(ASPEED_I2CD_DMA_LEN_MASK, ++ readl(bus->base + ++ ASPEED_I2C_DMA_LEN_REG)); ++ for (i = 1; i < len; i++) { ++ *value = bus->dma_buf[i]; ++ i2c_slave_event(bus->slave, I2C_SLAVE_WRITE_RECEIVED, ++ value); ++ } ++ writel(bus->dma_handle & ASPEED_I2CD_DMA_ADDR_MASK, ++ bus->base + ASPEED_I2C_DMA_ADDR_REG); ++ writel(FIELD_PREP(ASPEED_I2CD_DMA_LEN_MASK, bus->buf_size), ++ bus->base + ASPEED_I2C_DMA_LEN_REG); ++ writel(ASPEED_I2CD_RX_DMA_ENABLE, ++ bus->base + ASPEED_I2C_CMD_REG); ++ } else if (bus->buf_base) { + len = FIELD_GET(ASPEED_I2CD_BUF_RX_COUNT_MASK, + readl(bus->base + + ASPEED_I2C_BUF_CTRL_REG)); +@@ -460,7 +518,15 @@ aspeed_i2c_prepare_rx_buf(struct aspeed_i2c_bus *bus, struct i2c_msg *msg) + command |= ASPEED_I2CD_M_S_RX_CMD_LAST; + } + +- if (bus->buf_base) { ++ if (bus->dma_buf) { ++ command |= ASPEED_I2CD_RX_DMA_ENABLE; ++ ++ writel(bus->dma_handle & ASPEED_I2CD_DMA_ADDR_MASK, ++ bus->base + ASPEED_I2C_DMA_ADDR_REG); ++ writel(FIELD_PREP(ASPEED_I2CD_DMA_LEN_MASK, len), ++ bus->base + ASPEED_I2C_DMA_LEN_REG); ++ bus->dma_len = len; ++ } else { + command |= ASPEED_I2CD_RX_BUFF_ENABLE; + + writel(FIELD_PREP(ASPEED_I2CD_BUF_RX_SIZE_MASK, len - 1) | +@@ -483,7 +549,18 @@ aspeed_i2c_prepare_tx_buf(struct aspeed_i2c_bus *bus, struct i2c_msg *msg) + else + len = msg->len + 1; + +- if (bus->buf_base) { ++ if (bus->dma_buf) { ++ command |= ASPEED_I2CD_TX_DMA_ENABLE; ++ ++ bus->dma_buf[0] = slave_addr; ++ memcpy(bus->dma_buf + 1, msg->buf, len); ++ ++ writel(bus->dma_handle & ASPEED_I2CD_DMA_ADDR_MASK, ++ bus->base + ASPEED_I2C_DMA_ADDR_REG); ++ writel(FIELD_PREP(ASPEED_I2CD_DMA_LEN_MASK, len), ++ bus->base + ASPEED_I2C_DMA_LEN_REG); ++ bus->dma_len = len; ++ } else { + u8 wbuf[4]; + int i; + +@@ -536,18 +613,19 @@ static void aspeed_i2c_do_start(struct aspeed_i2c_bus *bus) + if (msg->flags & I2C_M_RD) { + command |= ASPEED_I2CD_M_RX_CMD; + if (!(msg->flags & I2C_M_RECV_LEN)) { +- if (msg->len && bus->buf_base) ++ if (msg->len && (bus->dma_buf || bus->buf_base)) + command |= aspeed_i2c_prepare_rx_buf(bus, msg); + + /* Need to let the hardware know to NACK after RX. */ + if (msg->len <= 1) + command |= ASPEED_I2CD_M_S_RX_CMD_LAST; + } +- } else if (msg->len && bus->buf_base) { ++ } else if (msg->len && (bus->dma_buf || bus->buf_base)) { + command |= aspeed_i2c_prepare_tx_buf(bus, msg); + } + +- if (!(command & ASPEED_I2CD_TX_BUFF_ENABLE)) ++ if (!(command & (ASPEED_I2CD_TX_BUFF_ENABLE | ++ ASPEED_I2CD_TX_DMA_ENABLE))) + writel(i2c_8bit_addr_from_msg(msg), + bus->base + ASPEED_I2C_BYTE_BUF_REG); + writel(command, bus->base + ASPEED_I2C_CMD_REG); +@@ -590,39 +668,52 @@ aspeed_i2c_master_handle_tx_first(struct aspeed_i2c_bus *bus, + { + u32 command = 0; + +- if (bus->buf_base) { +- u8 wbuf[4]; ++ if (bus->dma_buf || bus->buf_base) { + int len; +- int i; + + if (msg->len - bus->buf_index > bus->buf_size) + len = bus->buf_size; + else + len = msg->len - bus->buf_index; + ++ if (bus->dma_buf) { ++ command |= ASPEED_I2CD_TX_DMA_ENABLE; + +- command |= ASPEED_I2CD_TX_BUFF_ENABLE; ++ memcpy(bus->dma_buf, msg->buf + bus->buf_index, len); + +- if (msg->len - bus->buf_index > bus->buf_size) +- len = bus->buf_size; +- else +- len = msg->len - bus->buf_index; + +- for (i = 0; i < len; i++) { +- wbuf[i % 4] = msg->buf[bus->buf_index + i]; +- if (i % 4 == 3) ++ writel(bus->dma_handle & ASPEED_I2CD_DMA_ADDR_MASK, ++ bus->base + ASPEED_I2C_DMA_ADDR_REG); ++ writel(FIELD_PREP(ASPEED_I2CD_DMA_LEN_MASK, len), ++ bus->base + ASPEED_I2C_DMA_LEN_REG); ++ bus->dma_len = len; ++ } else { ++ u8 wbuf[4]; ++ int i; ++ ++ command |= ASPEED_I2CD_TX_BUFF_ENABLE; ++ ++ if (msg->len - bus->buf_index > bus->buf_size) ++ len = bus->buf_size; ++ else ++ len = msg->len - bus->buf_index; ++ ++ for (i = 0; i < len; i++) { ++ wbuf[i % 4] = msg->buf[bus->buf_index + i]; ++ if (i % 4 == 3) ++ writel(*(u32 *)wbuf, ++ bus->buf_base + i - 3); ++ } ++ if (--i % 4 != 3) + writel(*(u32 *)wbuf, +- bus->buf_base + i - 3); +- } +- if (--i % 4 != 3) +- writel(*(u32 *)wbuf, +- bus->buf_base + i - (i % 4)); ++ bus->buf_base + i - (i % 4)); + +- writel(FIELD_PREP(ASPEED_I2CD_BUF_TX_COUNT_MASK, +- len - 1) | +- FIELD_PREP(ASPEED_I2CD_BUF_OFFSET_MASK, +- bus->buf_offset), +- bus->base + ASPEED_I2C_BUF_CTRL_REG); ++ writel(FIELD_PREP(ASPEED_I2CD_BUF_TX_COUNT_MASK, ++ len - 1) | ++ FIELD_PREP(ASPEED_I2CD_BUF_OFFSET_MASK, ++ bus->buf_offset), ++ bus->base + ASPEED_I2C_BUF_CTRL_REG); ++ } + + bus->buf_index += len; + } else { +@@ -639,7 +730,14 @@ aspeed_i2c_master_handle_rx(struct aspeed_i2c_bus *bus, struct i2c_msg *msg) + u8 recv_byte; + int len; + +- if (bus->buf_base) { ++ if (bus->dma_buf) { ++ len = bus->dma_len - ++ FIELD_GET(ASPEED_I2CD_DMA_LEN_MASK, ++ readl(bus->base + ASPEED_I2C_DMA_LEN_REG)); ++ ++ memcpy(msg->buf + bus->buf_index, bus->dma_buf, len); ++ bus->buf_index += len; ++ } else if (bus->buf_base) { + len = FIELD_GET(ASPEED_I2CD_BUF_RX_COUNT_MASK, + readl(bus->base + ASPEED_I2C_BUF_CTRL_REG)); + memcpy_fromio(msg->buf + bus->buf_index, bus->buf_base, len); +@@ -656,7 +754,7 @@ aspeed_i2c_master_handle_rx_next(struct aspeed_i2c_bus *bus, + { + u32 command = 0; + +- if (bus->buf_base) { ++ if (bus->dma_buf || bus->buf_base) { + int len; + + if (msg->len - bus->buf_index > bus->buf_size) { +@@ -666,14 +764,24 @@ aspeed_i2c_master_handle_rx_next(struct aspeed_i2c_bus *bus, + command |= ASPEED_I2CD_M_S_RX_CMD_LAST; + } + +- command |= ASPEED_I2CD_RX_BUFF_ENABLE; ++ if (bus->dma_buf) { ++ command |= ASPEED_I2CD_RX_DMA_ENABLE; + +- writel(FIELD_PREP(ASPEED_I2CD_BUF_RX_SIZE_MASK, +- len - 1) | +- FIELD_PREP(ASPEED_I2CD_BUF_TX_COUNT_MASK, 0) | +- FIELD_PREP(ASPEED_I2CD_BUF_OFFSET_MASK, +- bus->buf_offset), +- bus->base + ASPEED_I2C_BUF_CTRL_REG); ++ writel(bus->dma_handle & ASPEED_I2CD_DMA_ADDR_MASK, ++ bus->base + ASPEED_I2C_DMA_ADDR_REG); ++ writel(FIELD_PREP(ASPEED_I2CD_DMA_LEN_MASK, len), ++ bus->base + ASPEED_I2C_DMA_LEN_REG); ++ bus->dma_len = len; ++ } else { ++ command |= ASPEED_I2CD_RX_BUFF_ENABLE; ++ ++ writel(FIELD_PREP(ASPEED_I2CD_BUF_RX_SIZE_MASK, ++ len - 1) | ++ FIELD_PREP(ASPEED_I2CD_BUF_TX_COUNT_MASK, 0) | ++ FIELD_PREP(ASPEED_I2CD_BUF_OFFSET_MASK, ++ bus->buf_offset), ++ bus->base + ASPEED_I2C_BUF_CTRL_REG); ++ } + } else { + if (bus->buf_index + 1 == msg->len) + command |= ASPEED_I2CD_M_S_RX_CMD_LAST; +@@ -1260,7 +1368,51 @@ static void aspeed_i2c_set_xfer_mode(struct aspeed_i2c_bus *bus) + sram_enabled = false; + } + +- if (sram_enabled) { ++ /* ++ * Only AST2500 and AST2600 support DMA mode under some limitations: ++ * I2C is sharing the DMA H/W with UHCI host controller and MCTP ++ * controller. Since those controllers operate with DMA mode only, I2C ++ * has to use buffer mode or byte mode instead if one of those ++ * controllers is enabled. Also make sure that if SD/eMMC or Port80 ++ * snoop uses DMA mode instead of PIO or FIFO respectively, I2C can't ++ * use DMA mode. ++ */ ++ if (sram_enabled && !IS_ENABLED(CONFIG_USB_UHCI_ASPEED) && ++ !of_device_is_compatible(pdev->dev.of_node, ++ "aspeed,ast2400-i2c-bus")) { ++ u32 dma_len_max = ASPEED_I2CD_DMA_LEN_MASK >> ++ ASPEED_I2CD_DMA_LEN_SHIFT; ++ ++ ret = device_property_read_u32(&pdev->dev, ++ "aspeed,dma-buf-size", ++ &bus->buf_size); ++ if (!ret && bus->buf_size > dma_len_max) ++ bus->buf_size = dma_len_max; ++ } ++ ++ if (bus->buf_size) { ++ if (dma_set_mask(&pdev->dev, DMA_BIT_MASK(32))) { ++ dev_warn(&pdev->dev, "No suitable DMA available\n"); ++ } else { ++ bus->dma_pool = dma_pool_create("i2c-aspeed", ++ &pdev->dev, ++ bus->buf_size, ++ ASPEED_I2CD_DMA_ALIGN, ++ 0); ++ if (bus->dma_pool) ++ bus->dma_buf = dma_pool_alloc(bus->dma_pool, ++ GFP_KERNEL, ++ &bus->dma_handle); ++ ++ if (!bus->dma_buf) { ++ dev_warn(&pdev->dev, ++ "Cannot allocate DMA buffer\n"); ++ dma_pool_destroy(bus->dma_pool); ++ } ++ } ++ } ++ ++ if (!bus->dma_buf && sram_enabled) { + struct resource *res = platform_get_resource(pdev, + IORESOURCE_MEM, 1); + +@@ -1365,24 +1517,33 @@ static int aspeed_i2c_probe_bus(struct platform_device *pdev) + */ + ret = aspeed_i2c_init(bus, pdev); + if (ret < 0) +- return ret; ++ goto out_free_dma_buf; + + irq = irq_of_parse_and_map(pdev->dev.of_node, 0); + ret = devm_request_irq(&pdev->dev, irq, aspeed_i2c_bus_irq, + 0, dev_name(&pdev->dev), bus); + if (ret < 0) +- return ret; ++ goto out_free_dma_buf; + + ret = i2c_add_adapter(&bus->adap); + if (ret < 0) +- return ret; ++ goto out_free_dma_buf; + + platform_set_drvdata(pdev, bus); + + dev_info(bus->dev, "i2c bus %d registered (%s mode), irq %d\n", +- bus->adap.nr, bus->buf_base ? "buffer" : "byte", irq); ++ bus->adap.nr, bus->dma_buf ? "dma" : ++ bus->buf_base ? "buffer" : "byte", ++ irq); + + return 0; ++ ++out_free_dma_buf: ++ if (bus->dma_buf) ++ dma_pool_free(bus->dma_pool, bus->dma_buf, bus->dma_handle); ++ dma_pool_destroy(bus->dma_pool); ++ ++ return ret; + } + + static int aspeed_i2c_remove_bus(struct platform_device *pdev) +@@ -1400,6 +1561,10 @@ static int aspeed_i2c_remove_bus(struct platform_device *pdev) + + reset_control_assert(bus->rst); + ++ if (bus->dma_buf) ++ dma_pool_free(bus->dma_pool, bus->dma_buf, bus->dma_handle); ++ dma_pool_destroy(bus->dma_pool); ++ + i2c_del_adapter(&bus->adap); + + return 0; +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0063-i2c-aspeed-add-general-call-support.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0063-i2c-aspeed-add-general-call-support.patch new file mode 100644 index 000000000..899924f61 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0063-i2c-aspeed-add-general-call-support.patch @@ -0,0 +1,180 @@ +From d731b53de05b8d69ea739f02275416126cf5fe4e Mon Sep 17 00:00:00 2001 +From: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +Date: Wed, 1 May 2019 13:27:34 -0700 +Subject: [PATCH] i2c: aspeed: add general call support + +This commit adds general call support into Aspeed I2C driver. +This is downstream only customization so it should not go into +upstream. + +Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +--- + .../devicetree/bindings/i2c/i2c-aspeed.txt | 1 + + drivers/i2c/busses/i2c-aspeed.c | 39 ++++++++++++++++++++++ + drivers/i2c/i2c-slave-mqueue.c | 4 ++- + include/linux/i2c.h | 1 + + 4 files changed, 44 insertions(+), 1 deletion(-) + +diff --git a/Documentation/devicetree/bindings/i2c/i2c-aspeed.txt b/Documentation/devicetree/bindings/i2c/i2c-aspeed.txt +index d3f4a39f7ba6..c1ee99398517 100644 +--- a/Documentation/devicetree/bindings/i2c/i2c-aspeed.txt ++++ b/Documentation/devicetree/bindings/i2c/i2c-aspeed.txt +@@ -19,6 +19,7 @@ Optional Properties: + - bus-frequency : frequency of the bus clock in Hz defaults to 100 kHz when not + specified + - multi-master : states that there is another master active on this bus. ++- general-call : enables general call receiving. + - bus-timeout-ms: bus timeout in milliseconds defaults to 1 second when not + specified. + - #retries : Number of retries for master transfer. +diff --git a/drivers/i2c/busses/i2c-aspeed.c b/drivers/i2c/busses/i2c-aspeed.c +index 3c9e491cbe4e..4420899e2c2d 100644 +--- a/drivers/i2c/busses/i2c-aspeed.c ++++ b/drivers/i2c/busses/i2c-aspeed.c +@@ -59,6 +59,7 @@ + #define ASPEED_I2CD_SDA_DRIVE_1T_EN BIT(8) + #define ASPEED_I2CD_M_SDA_DRIVE_1T_EN BIT(7) + #define ASPEED_I2CD_M_HIGH_SPEED_EN BIT(6) ++#define ASPEED_I2CD_GCALL_EN BIT(2) + #define ASPEED_I2CD_SLAVE_EN BIT(1) + #define ASPEED_I2CD_MASTER_EN BIT(0) + +@@ -83,6 +84,7 @@ + */ + #define ASPEED_I2CD_INTR_SDA_DL_TIMEOUT BIT(14) + #define ASPEED_I2CD_INTR_BUS_RECOVER_DONE BIT(13) ++#define ASPEED_I2CD_INTR_GCALL_ADDR BIT(8) + #define ASPEED_I2CD_INTR_SLAVE_MATCH BIT(7) + #define ASPEED_I2CD_INTR_SCL_TIMEOUT BIT(6) + #define ASPEED_I2CD_INTR_ABNORMAL BIT(5) +@@ -167,6 +169,8 @@ enum aspeed_i2c_slave_state { + ASPEED_I2C_SLAVE_READ_PROCESSED, + ASPEED_I2C_SLAVE_WRITE_REQUESTED, + ASPEED_I2C_SLAVE_WRITE_RECEIVED, ++ ASPEED_I2C_SLAVE_GCALL_START, ++ ASPEED_I2C_SLAVE_GCALL_REQUESTED, + ASPEED_I2C_SLAVE_STOP, + }; + +@@ -208,6 +212,8 @@ struct aspeed_i2c_bus { + #if IS_ENABLED(CONFIG_I2C_SLAVE) + struct i2c_client *slave; + enum aspeed_i2c_slave_state slave_state; ++ /* General call */ ++ bool general_call; + #endif /* CONFIG_I2C_SLAVE */ + }; + +@@ -423,6 +429,12 @@ static u32 aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus, u32 irq_status) + bus->slave_state = ASPEED_I2C_SLAVE_START; + } + ++ /* General call was requested, restart state machine. */ ++ if (irq_status & ASPEED_I2CD_INTR_GCALL_ADDR) { ++ irq_handled |= ASPEED_I2CD_INTR_GCALL_ADDR; ++ bus->slave_state = ASPEED_I2C_SLAVE_GCALL_START; ++ } ++ + /* Slave is not currently active, irq was for someone else. */ + if (bus->slave_state == ASPEED_I2C_SLAVE_INACTIVE) + return irq_handled; +@@ -441,6 +453,21 @@ static u32 aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus, u32 irq_status) + else + bus->slave_state = + ASPEED_I2C_SLAVE_WRITE_REQUESTED; ++ } else if (bus->slave_state == ASPEED_I2C_SLAVE_GCALL_START) { ++ /* ++ * I2C spec defines the second byte meaning like below. ++ * 0x06 : Reset and write programmable part of slave ++ * address by hardware. ++ * 0x04 : Write programmable part of slave address by ++ * hardware. ++ * 0x00 : No allowed. ++ * ++ * But in OpenBMC, we are going to use this ++ * 'General call' feature for IPMB message broadcasting ++ * so it delivers all data as is without any specific ++ * handling of the second byte. ++ */ ++ bus->slave_state = ASPEED_I2C_SLAVE_GCALL_REQUESTED; + } + irq_handled |= ASPEED_I2CD_INTR_RX_DONE; + } +@@ -487,11 +514,16 @@ static u32 aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus, u32 irq_status) + i2c_slave_event(slave, I2C_SLAVE_WRITE_RECEIVED, &value); + aspeed_i2c_slave_handle_write_received(bus, &value); + break; ++ case ASPEED_I2C_SLAVE_GCALL_REQUESTED: ++ bus->slave_state = ASPEED_I2C_SLAVE_WRITE_RECEIVED; ++ i2c_slave_event(slave, I2C_SLAVE_GCALL_REQUESTED, &value); ++ break; + case ASPEED_I2C_SLAVE_STOP: + i2c_slave_event(slave, I2C_SLAVE_STOP, &value); + bus->slave_state = ASPEED_I2C_SLAVE_INACTIVE; + break; + case ASPEED_I2C_SLAVE_START: ++ case ASPEED_I2C_SLAVE_GCALL_START: + /* Slave was just started. Waiting for the next event. */; + break; + default: +@@ -1127,6 +1159,8 @@ static void __aspeed_i2c_reg_slave(struct aspeed_i2c_bus *bus, u16 slave_addr) + /* Turn on slave mode. */ + func_ctrl_reg_val = readl(bus->base + ASPEED_I2C_FUN_CTRL_REG); + func_ctrl_reg_val |= ASPEED_I2CD_SLAVE_EN; ++ if (bus->general_call) ++ func_ctrl_reg_val |= ASPEED_I2CD_GCALL_EN; + writel(func_ctrl_reg_val, bus->base + ASPEED_I2C_FUN_CTRL_REG); + } + +@@ -1165,6 +1199,8 @@ static int aspeed_i2c_unreg_slave(struct i2c_client *client) + /* Turn off slave mode. */ + func_ctrl_reg_val = readl(bus->base + ASPEED_I2C_FUN_CTRL_REG); + func_ctrl_reg_val &= ~ASPEED_I2CD_SLAVE_EN; ++ if (bus->general_call) ++ func_ctrl_reg_val &= ~ASPEED_I2CD_GCALL_EN; + writel(func_ctrl_reg_val, bus->base + ASPEED_I2C_FUN_CTRL_REG); + + bus->slave = NULL; +@@ -1312,6 +1348,9 @@ static int aspeed_i2c_init(struct aspeed_i2c_bus *bus, + bus->base + ASPEED_I2C_FUN_CTRL_REG); + + #if IS_ENABLED(CONFIG_I2C_SLAVE) ++ if (of_property_read_bool(pdev->dev.of_node, "general-call")) ++ bus->general_call = true; ++ + /* If slave has already been registered, re-enable it. */ + if (bus->slave) + __aspeed_i2c_reg_slave(bus, bus->slave->addr); +diff --git a/drivers/i2c/i2c-slave-mqueue.c b/drivers/i2c/i2c-slave-mqueue.c +index 2c7a6038409c..1d4db584b393 100644 +--- a/drivers/i2c/i2c-slave-mqueue.c ++++ b/drivers/i2c/i2c-slave-mqueue.c +@@ -56,10 +56,12 @@ static int i2c_slave_mqueue_callback(struct i2c_client *client, + + switch (event) { + case I2C_SLAVE_WRITE_REQUESTED: ++ case I2C_SLAVE_GCALL_REQUESTED: + mq->truncated = 0; + + msg->len = 1; +- msg->buf[0] = client->addr << 1; ++ msg->buf[0] = event == I2C_SLAVE_GCALL_REQUESTED ? ++ 0 : client->addr << 1; + break; + + case I2C_SLAVE_WRITE_RECEIVED: +diff --git a/include/linux/i2c.h b/include/linux/i2c.h +index b4055d133338..52369ea150b4 100644 +--- a/include/linux/i2c.h ++++ b/include/linux/i2c.h +@@ -365,6 +365,7 @@ enum i2c_slave_event { + I2C_SLAVE_WRITE_REQUESTED, + I2C_SLAVE_READ_PROCESSED, + I2C_SLAVE_WRITE_RECEIVED, ++ I2C_SLAVE_GCALL_REQUESTED, + I2C_SLAVE_STOP, + }; + +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0064-set-idle-disconnect-to-true-in-all-cases.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0064-set-idle-disconnect-to-true-in-all-cases.patch new file mode 100644 index 000000000..925880eff --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0064-set-idle-disconnect-to-true-in-all-cases.patch @@ -0,0 +1,34 @@ +From 7854a5e094ac49bebf9b2bfdd44db2f8cdd37543 Mon Sep 17 00:00:00 2001 +From: James Feist <james.feist@linux.intel.com> +Date: Fri, 31 May 2019 15:05:13 -0700 +Subject: [PATCH] set idle-disconnect to true in all cases + +From sysfs this parameter can't be set. We want the +muxes to clean themselves up if possible. Set this to +true. + +Signed-off-by: James Feist <james.feist@linux.intel.com> +--- + drivers/i2c/muxes/i2c-mux-pca954x.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/drivers/i2c/muxes/i2c-mux-pca954x.c b/drivers/i2c/muxes/i2c-mux-pca954x.c +index 923aa3a5a3dc..084c10951890 100644 +--- a/drivers/i2c/muxes/i2c-mux-pca954x.c ++++ b/drivers/i2c/muxes/i2c-mux-pca954x.c +@@ -474,8 +474,12 @@ static int pca954x_probe(struct i2c_client *client, + data->last_chan = 0; /* force the first selection */ + data->idle_state = MUX_IDLE_AS_IS; + ++#if 1 /* Forcibly set the self-disconnect flag */ ++ idle_disconnect_dt = true; ++#else + idle_disconnect_dt = np && + of_property_read_bool(np, "i2c-mux-idle-disconnect"); ++#endif + if (idle_disconnect_dt) + data->idle_state = MUX_IDLE_DISCONNECT; + +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0068-i2c-aspeed-add-H-W-timeout-support.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0068-i2c-aspeed-add-H-W-timeout-support.patch new file mode 100644 index 000000000..0e8a2b101 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0068-i2c-aspeed-add-H-W-timeout-support.patch @@ -0,0 +1,191 @@ +From b429cf11ecb2f6009dd9e062ea5e053718802d93 Mon Sep 17 00:00:00 2001 +From: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +Date: Thu, 11 Jul 2019 13:53:34 -0700 +Subject: [PATCH] i2c: aspeed: add H/W timeout support + +This commit adds I2C H/W timeout support. + +Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +--- + drivers/i2c/busses/i2c-aspeed.c | 82 ++++++++++++++++++++++++++++++++++++++--- + 1 file changed, 76 insertions(+), 6 deletions(-) + +diff --git a/drivers/i2c/busses/i2c-aspeed.c b/drivers/i2c/busses/i2c-aspeed.c +index 4420899e2c2d..830b3d2cabad 100644 +--- a/drivers/i2c/busses/i2c-aspeed.c ++++ b/drivers/i2c/busses/i2c-aspeed.c +@@ -55,6 +55,7 @@ + /* Device Register Definition */ + /* 0x00 : I2CD Function Control Register */ + #define ASPEED_I2CD_BUFFER_PAGE_SEL_MASK GENMASK(22, 20) ++#define ASPEED_I2CD_BUS_AUTO_RECOVERY_EN BIT(17) + #define ASPEED_I2CD_MULTI_MASTER_DIS BIT(15) + #define ASPEED_I2CD_SDA_DRIVE_1T_EN BIT(8) + #define ASPEED_I2CD_M_SDA_DRIVE_1T_EN BIT(7) +@@ -71,10 +72,14 @@ + #define ASPEED_I2CD_TIME_SCL_HIGH_MASK GENMASK(19, 16) + #define ASPEED_I2CD_TIME_SCL_LOW_SHIFT 12 + #define ASPEED_I2CD_TIME_SCL_LOW_MASK GENMASK(15, 12) ++#define ASPEED_I2CD_TIME_TIMEOUT_BASE_DIVISOR_SHIFT 8 ++#define ASPEED_I2CD_TIME_TIMEOUT_BASE_DIVISOR_MASK GENMASK(9, 8) + #define ASPEED_I2CD_TIME_BASE_DIVISOR_MASK GENMASK(3, 0) + #define ASPEED_I2CD_TIME_SCL_REG_MAX GENMASK(3, 0) ++ + /* 0x08 : I2CD Clock and AC Timing Control Register #2 */ +-#define ASPEED_NO_TIMEOUT_CTRL 0 ++#define ASPEED_I2CD_TIMEOUT_CYCLES_SHIFT 0 ++#define ASPEED_I2CD_TIMEOUT_CYCLES_MASK GENMASK(4, 0) + + /* 0x0c : I2CD Interrupt Control Register & + * 0x10 : I2CD Interrupt Status Register +@@ -82,6 +87,7 @@ + * These share bit definitions, so use the same values for the enable & + * status bits. + */ ++#define ASPEED_I2CD_INTR_SLAVE_INACTIVE_TIMEOUT BIT(15) + #define ASPEED_I2CD_INTR_SDA_DL_TIMEOUT BIT(14) + #define ASPEED_I2CD_INTR_BUS_RECOVER_DONE BIT(13) + #define ASPEED_I2CD_INTR_GCALL_ADDR BIT(8) +@@ -98,8 +104,11 @@ + ASPEED_I2CD_INTR_SCL_TIMEOUT | \ + ASPEED_I2CD_INTR_ABNORMAL | \ + ASPEED_I2CD_INTR_ARBIT_LOSS) ++#define ASPEED_I2CD_INTR_SLAVE_ERRORS \ ++ ASPEED_I2CD_INTR_SLAVE_INACTIVE_TIMEOUT + #define ASPEED_I2CD_INTR_ALL \ +- (ASPEED_I2CD_INTR_SDA_DL_TIMEOUT | \ ++ (ASPEED_I2CD_INTR_SLAVE_INACTIVE_TIMEOUT | \ ++ ASPEED_I2CD_INTR_SDA_DL_TIMEOUT | \ + ASPEED_I2CD_INTR_BUS_RECOVER_DONE | \ + ASPEED_I2CD_INTR_SCL_TIMEOUT | \ + ASPEED_I2CD_INTR_ABNORMAL | \ +@@ -186,6 +195,7 @@ struct aspeed_i2c_bus { + u32 divisor); + unsigned long parent_clk_frequency; + u32 bus_frequency; ++ u32 hw_timeout_ms; + /* Transaction state. */ + enum aspeed_i2c_master_state master_state; + struct i2c_msg *msgs; +@@ -303,6 +313,14 @@ static int aspeed_i2c_recover_bus(struct aspeed_i2c_bus *bus) + } + + #if IS_ENABLED(CONFIG_I2C_SLAVE) ++static int aspeed_i2c_check_slave_error(u32 irq_status) ++{ ++ if (irq_status & ASPEED_I2CD_INTR_SLAVE_INACTIVE_TIMEOUT) ++ return -EIO; ++ ++ return 0; ++} ++ + static inline void + aspeed_i2c_slave_handle_rx_done(struct aspeed_i2c_bus *bus, u32 irq_status, + u8 *value) +@@ -421,6 +439,14 @@ static u32 aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus, u32 irq_status) + if (!slave) + return 0; + ++ if (aspeed_i2c_check_slave_error(irq_status)) { ++ dev_dbg(bus->dev, "received slave error interrupt: 0x%08x\n", ++ irq_status); ++ irq_handled |= (irq_status & ASPEED_I2CD_INTR_SLAVE_ERRORS); ++ bus->slave_state = ASPEED_I2C_SLAVE_INACTIVE; ++ return irq_handled; ++ } ++ + command = readl(bus->base + ASPEED_I2C_CMD_REG); + + /* Slave was requested, restart state machine. */ +@@ -681,7 +707,7 @@ static void aspeed_i2c_next_msg_or_stop(struct aspeed_i2c_bus *bus) + } + } + +-static int aspeed_i2c_is_irq_error(u32 irq_status) ++static int aspeed_i2c_check_master_error(u32 irq_status) + { + if (irq_status & ASPEED_I2CD_INTR_ARBIT_LOSS) + return -EAGAIN; +@@ -840,9 +866,9 @@ static u32 aspeed_i2c_master_irq(struct aspeed_i2c_bus *bus, u32 irq_status) + * should clear the command queue effectively taking us back to the + * INACTIVE state. + */ +- ret = aspeed_i2c_is_irq_error(irq_status); ++ ret = aspeed_i2c_check_master_error(irq_status); + if (ret) { +- dev_dbg(bus->dev, "received error interrupt: 0x%08x\n", ++ dev_dbg(bus->dev, "received master error interrupt: 0x%08x\n", + irq_status); + irq_handled |= (irq_status & ASPEED_I2CD_INTR_MASTER_ERRORS); + if (bus->master_state != ASPEED_I2C_MASTER_INACTIVE) { +@@ -1307,6 +1333,7 @@ static u32 aspeed_i2c_25xx_get_clk_reg_val(struct device *dev, u32 divisor) + /* precondition: bus.lock has been acquired. */ + static int aspeed_i2c_init_clk(struct aspeed_i2c_bus *bus) + { ++ u32 timeout_base_divisor, timeout_tick_us, timeout_cycles; + u32 divisor, clk_reg_val; + + divisor = DIV_ROUND_UP(bus->parent_clk_frequency, bus->bus_frequency); +@@ -1315,8 +1342,46 @@ static int aspeed_i2c_init_clk(struct aspeed_i2c_bus *bus) + ASPEED_I2CD_TIME_THDSTA_MASK | + ASPEED_I2CD_TIME_TACST_MASK); + clk_reg_val |= bus->get_clk_reg_val(bus->dev, divisor); ++ ++ if (bus->hw_timeout_ms) { ++ u8 div_max = ASPEED_I2CD_TIME_TIMEOUT_BASE_DIVISOR_MASK >> ++ ASPEED_I2CD_TIME_TIMEOUT_BASE_DIVISOR_SHIFT; ++ u8 cycles_max = ASPEED_I2CD_TIMEOUT_CYCLES_MASK >> ++ ASPEED_I2CD_TIMEOUT_CYCLES_SHIFT; ++ ++ timeout_base_divisor = 0; ++ ++ do { ++ timeout_tick_us = 1000 * (16384 << ++ (timeout_base_divisor << 1)) / ++ (bus->parent_clk_frequency / 1000); ++ ++ if (timeout_base_divisor == div_max || ++ timeout_tick_us * ASPEED_I2CD_TIMEOUT_CYCLES_MASK >= ++ bus->hw_timeout_ms * 1000) ++ break; ++ } while (timeout_base_divisor++ < div_max); ++ ++ if (timeout_tick_us) { ++ timeout_cycles = DIV_ROUND_UP(bus->hw_timeout_ms * 1000, ++ timeout_tick_us); ++ if (timeout_cycles == 0) ++ timeout_cycles = 1; ++ else if (timeout_cycles > cycles_max) ++ timeout_cycles = cycles_max; ++ } else { ++ timeout_cycles = 0; ++ } ++ } else { ++ timeout_base_divisor = 0; ++ timeout_cycles = 0; ++ } ++ ++ clk_reg_val |= FIELD_PREP(ASPEED_I2CD_TIME_TIMEOUT_BASE_DIVISOR_MASK, ++ timeout_base_divisor); ++ + writel(clk_reg_val, bus->base + ASPEED_I2C_AC_TIMING_REG1); +- writel(ASPEED_NO_TIMEOUT_CTRL, bus->base + ASPEED_I2C_AC_TIMING_REG2); ++ writel(timeout_cycles, bus->base + ASPEED_I2C_AC_TIMING_REG2); + + return 0; + } +@@ -1331,6 +1396,11 @@ static int aspeed_i2c_init(struct aspeed_i2c_bus *bus, + /* Disable everything. */ + writel(0, bus->base + ASPEED_I2C_FUN_CTRL_REG); + ++ device_property_read_u32(&pdev->dev, "aspeed,hw-timeout-ms", ++ &bus->hw_timeout_ms); ++ if (bus->hw_timeout_ms) ++ fun_ctrl_reg |= ASPEED_I2CD_BUS_AUTO_RECOVERY_EN; ++ + ret = aspeed_i2c_init_clk(bus); + if (ret < 0) + return ret; +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0069-i2c-aspeed-add-SLAVE_ADDR_RECEIVED_PENDING-interrupt.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0069-i2c-aspeed-add-SLAVE_ADDR_RECEIVED_PENDING-interrupt.patch new file mode 100644 index 000000000..104279ace --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0069-i2c-aspeed-add-SLAVE_ADDR_RECEIVED_PENDING-interrupt.patch @@ -0,0 +1,50 @@ +From b43bdeb5f3aa297549655e67f0e6a5db5f592e34 Mon Sep 17 00:00:00 2001 +From: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +Date: Thu, 11 Jul 2019 14:04:39 -0700 +Subject: [PATCH] i2c: aspeed: add SLAVE_ADDR_RECEIVED_PENDING interrupt + handling + +If a peer master sends messages too quickly before it processes +previous slave DMA data handling, this indicator will be set. It's +just a indicator and driver can't recover this case so just ignore +it. + +Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +--- + drivers/i2c/busses/i2c-aspeed.c | 13 +++++++++++++ + 1 file changed, 13 insertions(+) + +diff --git a/drivers/i2c/busses/i2c-aspeed.c b/drivers/i2c/busses/i2c-aspeed.c +index 830b3d2cabad..a308d5896632 100644 +--- a/drivers/i2c/busses/i2c-aspeed.c ++++ b/drivers/i2c/busses/i2c-aspeed.c +@@ -87,6 +87,7 @@ + * These share bit definitions, so use the same values for the enable & + * status bits. + */ ++#define ASPEED_I2CD_INTR_SLAVE_ADDR_RECEIVED_PENDING BIT(30) + #define ASPEED_I2CD_INTR_SLAVE_INACTIVE_TIMEOUT BIT(15) + #define ASPEED_I2CD_INTR_SDA_DL_TIMEOUT BIT(14) + #define ASPEED_I2CD_INTR_BUS_RECOVER_DONE BIT(13) +@@ -468,6 +469,18 @@ static u32 aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus, u32 irq_status) + dev_dbg(bus->dev, "slave irq status 0x%08x, cmd 0x%08x\n", + irq_status, command); + ++ /* ++ * If a peer master sends messages too quickly before it processes ++ * previous slave DMA data handling, this indicator will be set. It's ++ * just a indicator and driver can't recover this case so just ignore ++ * it. ++ */ ++ if (unlikely(irq_status & ++ ASPEED_I2CD_INTR_SLAVE_ADDR_RECEIVED_PENDING)) { ++ dev_dbg(bus->dev, "A slave addr match interrupt is pending.\n"); ++ irq_handled |= ASPEED_I2CD_INTR_SLAVE_ADDR_RECEIVED_PENDING; ++ } ++ + /* Slave was sent something. */ + if (irq_status & ASPEED_I2CD_INTR_RX_DONE) { + aspeed_i2c_slave_handle_rx_done(bus, irq_status, &value); +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0070-gpio-aspeed-temporary-fix-for-gpiochip-range-setting.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0070-gpio-aspeed-temporary-fix-for-gpiochip-range-setting.patch new file mode 100644 index 000000000..5c3e35016 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0070-gpio-aspeed-temporary-fix-for-gpiochip-range-setting.patch @@ -0,0 +1,44 @@ +From 0bda96c5fe3064c71f190bc484e2ce59c51d5585 Mon Sep 17 00:00:00 2001 +From: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +Date: Fri, 19 Jul 2019 12:54:38 -0700 +Subject: [PATCH] gpio: aspeed: temporary fix for gpiochip range setting + +Since we are still using fixed indices for gpio line numbers for sysfs +interface, this commit set the gpiochip range as fixed temporariliy +til we replace all index based gpio uses with name based uses. + +Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +--- + drivers/gpio/gpio-aspeed.c | 2 +- + drivers/gpio/sgpio-aspeed.c | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/drivers/gpio/gpio-aspeed.c b/drivers/gpio/gpio-aspeed.c +index 39c50c53dae3..05945470bd89 100644 +--- a/drivers/gpio/gpio-aspeed.c ++++ b/drivers/gpio/gpio-aspeed.c +@@ -1181,7 +1181,7 @@ static int __init aspeed_gpio_probe(struct platform_device *pdev) + gpio->chip.set = aspeed_gpio_set; + gpio->chip.set_config = aspeed_gpio_set_config; + gpio->chip.label = dev_name(&pdev->dev); +- gpio->chip.base = -1; ++ gpio->chip.base = 0; + + /* Allocate a cache of the output registers */ + banks = DIV_ROUND_UP(gpio->chip.ngpio, 32); +diff --git a/drivers/gpio/sgpio-aspeed.c b/drivers/gpio/sgpio-aspeed.c +index 5028e9144a75..850539da43f5 100644 +--- a/drivers/gpio/sgpio-aspeed.c ++++ b/drivers/gpio/sgpio-aspeed.c +@@ -681,7 +681,7 @@ static int __init aspeed_sgpio_probe(struct platform_device *pdev) + gpio->chip.set = aspeed_sgpio_set; + gpio->chip.set_config = aspeed_sgpio_set_config; + gpio->chip.label = dev_name(&pdev->dev); +- gpio->chip.base = -1; ++ gpio->chip.base = gpio->config->nr_pgpios; + + rc = aspeed_sgpio_setup_irqs(gpio, pdev); + if (rc < 0) +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0072-pmbus-add-fault-and-beep-attributes.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0072-pmbus-add-fault-and-beep-attributes.patch new file mode 100644 index 000000000..40a9272e0 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0072-pmbus-add-fault-and-beep-attributes.patch @@ -0,0 +1,88 @@ +From e360a6c2a3f15bfc8900c7262c56f9bcd5e0f16e Mon Sep 17 00:00:00 2001 +From: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +Date: Thu, 8 Aug 2019 10:38:00 -0700 +Subject: [PATCH] pmbus: add 'fault' and 'beep' attributes + +This commit adds two more attirbutes to reflect MFR_SPECIFIC bit in +the STATUS_WORD and 'Unit Off For Insufficient Input Voltage' bit in +the STATUS_INPUT into 'fault' and 'beep' attributes respectively. + +The attributes will be enumerated as 'inX_fault' and 'inX_beep' in +a 'vin' group. + +Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +--- + drivers/hwmon/pmbus/pmbus.h | 1 + + drivers/hwmon/pmbus/pmbus_core.c | 30 ++++++++++++++++++++++++++++++ + 2 files changed, 31 insertions(+) + +diff --git a/drivers/hwmon/pmbus/pmbus.h b/drivers/hwmon/pmbus/pmbus.h +index d198af3a92b6..09e6fe01c304 100644 +--- a/drivers/hwmon/pmbus/pmbus.h ++++ b/drivers/hwmon/pmbus/pmbus.h +@@ -303,6 +303,7 @@ enum pmbus_fan_mode { percent = 0, rpm }; + #define PB_PIN_OP_WARNING BIT(0) + #define PB_IIN_OC_WARNING BIT(1) + #define PB_IIN_OC_FAULT BIT(2) ++#define PB_UNIT_OFF_FOR_INSUF_VIN BIT(3) + + /* + * STATUS_TEMPERATURE +diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c +index 898d7378f4f8..85295a45c3ba 100644 +--- a/drivers/hwmon/pmbus/pmbus_core.c ++++ b/drivers/hwmon/pmbus/pmbus_core.c +@@ -1163,6 +1163,8 @@ struct pmbus_limit_attr { + struct pmbus_sensor_attr { + u16 reg; /* sensor register */ + u16 gbit; /* generic status bit */ ++ u16 gfbit; /* generic fault status bit */ ++ u16 sbbit; /* beep status bit */ + u8 nlimit; /* # of limit registers */ + enum pmbus_sensor_classes class;/* sensor class */ + const char *label; /* sensor label */ +@@ -1264,6 +1266,32 @@ static int pmbus_add_sensor_attrs_one(struct i2c_client *client, + return ret; + } + } ++ /* ++ * Add fault attribute if there is a generic fault bit, and if ++ * the generic status register (word or byte, depending on which global ++ * bit is set) for this page is accessible. ++ */ ++ if (attr->gfbit) { ++ upper = !!(attr->gfbit & 0xff00); /* need to check STATUS_WORD */ ++ if ((!upper || (upper && data->has_status_word)) && ++ pmbus_check_status_register(client, page)) { ++ ret = pmbus_add_boolean(data, name, "fault", index, ++ NULL, NULL, ++ PB_STATUS_BASE + page, ++ attr->gfbit); ++ if (ret) ++ return ret; ++ } ++ } ++ /* Add beep attribute if there is a beep status bit. */ ++ if (attr->sbbit) { ++ ret = pmbus_add_boolean(data, name, "beep", index, ++ NULL, NULL, ++ attr->sbase + page, ++ attr->sbbit); ++ if (ret) ++ return ret; ++ } + return 0; + } + +@@ -1435,6 +1463,8 @@ static const struct pmbus_sensor_attr voltage_attributes[] = { + .gbit = PB_STATUS_VIN_UV, + .limit = vin_limit_attrs, + .nlimit = ARRAY_SIZE(vin_limit_attrs), ++ .gfbit = PB_STATUS_WORD_MFR, ++ .sbbit = PB_UNIT_OFF_FOR_INSUF_VIN, + }, { + .reg = PMBUS_VIRT_READ_VMON, + .class = PSC_VOLTAGE_IN, +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0073-Add-IO-statistics-to-USB-Mass-storage-gadget.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0073-Add-IO-statistics-to-USB-Mass-storage-gadget.patch new file mode 100644 index 000000000..1056b3beb --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0073-Add-IO-statistics-to-USB-Mass-storage-gadget.patch @@ -0,0 +1,155 @@ +From 5c82e0b33f2a373d5e19569635f108cfa096f53e Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Adrian=20Ambro=C5=BCewicz?= <adrian.ambrozewicz@intel.com> +Date: Mon, 29 Jul 2019 10:19:00 +0200 +Subject: [PATCH] Add IO statistics to USB Mass storage gadget +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Introduces new attribute to Mass Storage Gadget ConfigFS : stats. +It's read-only attribute which contains statistics of read/write operations +based on LUN transaction counters (IO number and bytes transferred). + +Goal is to provide a way to observe whether simulated device is actually +used by host. Statistics on hosted file / nbd level are not always viable +due to page cache having severe impact on actual IO statistics. +This attribute should provide information about host IO on USB Gadget as +close to endpoint as possible. + +Attribute is tied completely to configFS implementation and it's lifecycle +is managed by Kernel and user. Driver implements a handler which populates +output buffer on read. + +Tests performed: +- mounted USB Mass Storage gadget, new attribute showed up in gadget tree +- attribute was monitored for changes during IO performed on host machine +- removed device, attribute (along with other device attributes) was gone + +Signed-off-by: Adrian Ambrożewicz <adrian.ambrozewicz@intel.com> +--- + drivers/usb/gadget/function/f_mass_storage.c | 12 ++++++++++++ + drivers/usb/gadget/function/storage_common.c | 9 +++++++++ + drivers/usb/gadget/function/storage_common.h | 29 ++++++++++++++++++++++++++++ + 3 files changed, 50 insertions(+) + +diff --git a/drivers/usb/gadget/function/f_mass_storage.c b/drivers/usb/gadget/function/f_mass_storage.c +index 7c96c4665178..ecc3c68a7882 100644 +--- a/drivers/usb/gadget/function/f_mass_storage.c ++++ b/drivers/usb/gadget/function/f_mass_storage.c +@@ -710,6 +710,8 @@ static int do_read(struct fsg_common *common) + amount_left -= nread; + common->residue -= nread; + ++ fsg_stats_rd_attempt(&curlun->stats, nread); ++ + /* + * Except at the end of the transfer, nread will be + * equal to the buffer size, which is divisible by the +@@ -907,6 +909,8 @@ static int do_write(struct fsg_common *common) + amount_left_to_write -= nwritten; + common->residue -= nwritten; + ++ fsg_stats_wr_attempt(&curlun->stats, nwritten); ++ + /* If an error occurred, report it and its position */ + if (nwritten < amount) { + curlun->sense_data = SS_WRITE_ERROR; +@@ -3122,6 +3126,13 @@ static ssize_t fsg_lun_opts_inquiry_string_store(struct config_item *item, + + CONFIGFS_ATTR(fsg_lun_opts_, inquiry_string); + ++static ssize_t fsg_lun_opts_stats_show(struct config_item *item, char *page) ++{ ++ return fsg_show_stats(to_fsg_lun_opts(item)->lun, page); ++} ++ ++CONFIGFS_ATTR_RO(fsg_lun_opts_, stats); ++ + static struct configfs_attribute *fsg_lun_attrs[] = { + &fsg_lun_opts_attr_file, + &fsg_lun_opts_attr_ro, +@@ -3129,6 +3140,7 @@ static struct configfs_attribute *fsg_lun_attrs[] = { + &fsg_lun_opts_attr_cdrom, + &fsg_lun_opts_attr_nofua, + &fsg_lun_opts_attr_inquiry_string, ++ &fsg_lun_opts_attr_stats, + NULL, + }; + +diff --git a/drivers/usb/gadget/function/storage_common.c b/drivers/usb/gadget/function/storage_common.c +index f7e6c42558eb..2325b97961df 100644 +--- a/drivers/usb/gadget/function/storage_common.c ++++ b/drivers/usb/gadget/function/storage_common.c +@@ -371,6 +371,15 @@ ssize_t fsg_show_inquiry_string(struct fsg_lun *curlun, char *buf) + } + EXPORT_SYMBOL_GPL(fsg_show_inquiry_string); + ++ssize_t fsg_show_stats(struct fsg_lun *curlun, char *buf) ++{ ++ return sprintf(buf, "read cnt: %u\n" "read sum: %llu\n" ++ "write cnt: %u\n" "write sum: %llu\n", ++ curlun->stats.read.count, curlun->stats.read.bytes, ++ curlun->stats.write.count, curlun->stats.write.bytes); ++} ++EXPORT_SYMBOL_GPL(fsg_show_stats); ++ + /* + * The caller must hold fsg->filesem for reading when calling this function. + */ +diff --git a/drivers/usb/gadget/function/storage_common.h b/drivers/usb/gadget/function/storage_common.h +index e5e3a2553aaa..447021ba821a 100644 +--- a/drivers/usb/gadget/function/storage_common.h ++++ b/drivers/usb/gadget/function/storage_common.h +@@ -95,6 +95,32 @@ do { \ + */ + #define INQUIRY_STRING_LEN ((size_t) (8 + 16 + 4 + 1)) + ++struct fsg_stats_cnt { ++ u64 bytes; ++ u32 count; ++}; ++ ++struct fsg_stats { ++ struct fsg_stats_cnt read; ++ struct fsg_stats_cnt write; ++}; ++ ++static inline void fsg_stats_update(struct fsg_stats_cnt *cnt, u64 diff) ++{ ++ cnt->count++; ++ cnt->bytes += diff; ++} ++ ++static inline void fsg_stats_wr_attempt(struct fsg_stats *stats, u64 b_written) ++{ ++ fsg_stats_update(&stats->write, b_written); ++} ++ ++static inline void fsg_stats_rd_attempt(struct fsg_stats *stats, u64 b_read) ++{ ++ fsg_stats_update(&stats->read, b_read); ++} ++ + struct fsg_lun { + struct file *filp; + loff_t file_length; +@@ -120,6 +146,8 @@ struct fsg_lun { + const char *name; /* "lun.name" */ + const char **name_pfx; /* "function.name" */ + char inquiry_string[INQUIRY_STRING_LEN]; ++ ++ struct fsg_stats stats; + }; + + static inline bool fsg_lun_is_open(struct fsg_lun *curlun) +@@ -213,6 +241,7 @@ ssize_t fsg_show_file(struct fsg_lun *curlun, struct rw_semaphore *filesem, + ssize_t fsg_show_inquiry_string(struct fsg_lun *curlun, char *buf); + ssize_t fsg_show_cdrom(struct fsg_lun *curlun, char *buf); + ssize_t fsg_show_removable(struct fsg_lun *curlun, char *buf); ++ssize_t fsg_show_stats(struct fsg_lun *curlun, char *buf); + ssize_t fsg_store_ro(struct fsg_lun *curlun, struct rw_semaphore *filesem, + const char *buf, size_t count); + ssize_t fsg_store_nofua(struct fsg_lun *curlun, const char *buf, size_t count); +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0075-Refine-initialization-flow-in-I2C-driver.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0075-Refine-initialization-flow-in-I2C-driver.patch new file mode 100644 index 000000000..52c3617d6 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0075-Refine-initialization-flow-in-I2C-driver.patch @@ -0,0 +1,64 @@ +From 93ba2aeb9ad3899e2b72877daa47376a6bf192b6 Mon Sep 17 00:00:00 2001 +From: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +Date: Mon, 23 Sep 2019 13:48:49 -0700 +Subject: [PATCH] Refine initialization flow in I2C driver + +Since we enabled I2C busses in u-boot, we need to disable the I2C +bus and clear all garbage interrupts when kernel probes the bus. +This commit refines the initialization flow by adding a bus reset +at the beginning of probe function and by moving bus init function +after interrupt handling setup. + +Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +--- + drivers/i2c/busses/i2c-aspeed.c | 20 +++++++++----------- + 1 file changed, 9 insertions(+), 11 deletions(-) + +diff --git a/drivers/i2c/busses/i2c-aspeed.c b/drivers/i2c/busses/i2c-aspeed.c +index a308d5896632..b32611088c82 100644 +--- a/drivers/i2c/busses/i2c-aspeed.c ++++ b/drivers/i2c/busses/i2c-aspeed.c +@@ -1588,6 +1588,11 @@ static int aspeed_i2c_probe_bus(struct platform_device *pdev) + + bus->dev = &pdev->dev; + ++ /* Disable bus and clean up any left over interrupt state. */ ++ writel(0, bus->base + ASPEED_I2C_FUN_CTRL_REG); ++ writel(0, bus->base + ASPEED_I2C_INTR_CTRL_REG); ++ writel(0xffffffff, bus->base + ASPEED_I2C_INTR_STS_REG); ++ + parent_clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(parent_clk)) + return PTR_ERR(parent_clk); +@@ -1630,17 +1635,6 @@ static int aspeed_i2c_probe_bus(struct platform_device *pdev) + strlcpy(bus->adap.name, pdev->name, sizeof(bus->adap.name)); + i2c_set_adapdata(&bus->adap, bus); + +- /* Clean up any left over interrupt state. */ +- writel(0, bus->base + ASPEED_I2C_INTR_CTRL_REG); +- writel(0xffffffff, bus->base + ASPEED_I2C_INTR_STS_REG); +- /* +- * bus.lock does not need to be held because the interrupt handler has +- * not been enabled yet. +- */ +- ret = aspeed_i2c_init(bus, pdev); +- if (ret < 0) +- goto out_free_dma_buf; +- + irq = irq_of_parse_and_map(pdev->dev.of_node, 0); + ret = devm_request_irq(&pdev->dev, irq, aspeed_i2c_bus_irq, + 0, dev_name(&pdev->dev), bus); +@@ -1653,6 +1647,10 @@ static int aspeed_i2c_probe_bus(struct platform_device *pdev) + + platform_set_drvdata(pdev, bus); + ++ ret = aspeed_i2c_init(bus, pdev); ++ if (ret < 0) ++ goto out_free_dma_buf; ++ + dev_info(bus->dev, "i2c bus %d registered (%s mode), irq %d\n", + bus->adap.nr, bus->dma_buf ? "dma" : + bus->buf_base ? "buffer" : "byte", +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0076-arm-ast2600-add-pwm_tacho-driver-from-aspeed.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0076-arm-ast2600-add-pwm_tacho-driver-from-aspeed.patch new file mode 100644 index 000000000..9515a1a89 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0076-arm-ast2600-add-pwm_tacho-driver-from-aspeed.patch @@ -0,0 +1,1107 @@ +From a17a084c4889dcc7cb43ef5f93032997cfcee4f0 Mon Sep 17 00:00:00 2001 +From: Vernon Mauery <vernon.mauery@intel.com> +Date: Fri, 27 Sep 2019 13:09:48 -0700 +Subject: [PATCH] arm: ast2600: add pwm_tacho driver from aspeed + +Add the pwm_tacho driver from Aspeed to get pwm working until an +upstream PWM/Tacho driver is available. This was copied from the v5.02 +BSP from Aspeed. + +Signed-off-by: Vernon Mauery <vernon.mauery@intel.com> +--- + arch/arm/boot/dts/aspeed-g6.dtsi | 10 + + drivers/hwmon/Kconfig | 11 + + drivers/hwmon/Makefile | 1 + + drivers/hwmon/aspeed-g6-pwm-tacho.c | 1025 +++++++++++++++++++++++++++++++++++ + 4 files changed, 1047 insertions(+) + create mode 100644 drivers/hwmon/aspeed-g6-pwm-tacho.c + +diff --git a/arch/arm/boot/dts/aspeed-g6.dtsi b/arch/arm/boot/dts/aspeed-g6.dtsi +index eeace4b7b725..33fcd89db6b8 100644 +--- a/arch/arm/boot/dts/aspeed-g6.dtsi ++++ b/arch/arm/boot/dts/aspeed-g6.dtsi +@@ -278,6 +278,16 @@ + #size-cells = <1>; + ranges; + ++ pwm_tacho: pwm-tacho-controller@1e610000 { ++ compatible = "aspeed,ast2600-pwm-tacho"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ reg = <0x1e610000 0x100>; ++ clocks = <&syscon ASPEED_CLK_AHB>; ++ resets = <&syscon ASPEED_RESET_PWM>; ++ status = "disabled"; ++ }; ++ + syscon: syscon@1e6e2000 { + compatible = "aspeed,ast2600-scu", "syscon", "simple-mfd"; + reg = <0x1e6e2000 0x1000>; +diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig +index e244a7901392..8312b3798b82 100644 +--- a/drivers/hwmon/Kconfig ++++ b/drivers/hwmon/Kconfig +@@ -371,6 +371,17 @@ config SENSORS_ASPEED + This driver can also be built as a module. If so, the module + will be called aspeed_pwm_tacho. + ++config SENSORS_ASPEED_G6 ++ tristate "ASPEED AST2600 PWM and Fan tach driver" ++ depends on THERMAL || THERMAL=n ++ select REGMAP ++ help ++ This driver provides support for ASPEED AST2600 PWM ++ and Fan Tacho controllers. ++ ++ This driver can also be built as a module. If so, the module ++ will be called aspeed_g6_pwm_tacho. ++ + config SENSORS_ATXP1 + tristate "Attansic ATXP1 VID controller" + depends on I2C +diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile +index 1c7ab361adc7..e74ea925fb56 100644 +--- a/drivers/hwmon/Makefile ++++ b/drivers/hwmon/Makefile +@@ -50,6 +50,7 @@ obj-$(CONFIG_SENSORS_ARM_SCPI) += scpi-hwmon.o + obj-$(CONFIG_SENSORS_AS370) += as370-hwmon.o + obj-$(CONFIG_SENSORS_ASC7621) += asc7621.o + obj-$(CONFIG_SENSORS_ASPEED) += aspeed-pwm-tacho.o ++obj-$(CONFIG_SENSORS_ASPEED_G6) += aspeed-g6-pwm-tacho.o + obj-$(CONFIG_SENSORS_ATXP1) += atxp1.o + obj-$(CONFIG_SENSORS_CORETEMP) += coretemp.o + obj-$(CONFIG_SENSORS_DA9052_ADC)+= da9052-hwmon.o +diff --git a/drivers/hwmon/aspeed-g6-pwm-tacho.c b/drivers/hwmon/aspeed-g6-pwm-tacho.c +new file mode 100644 +index 000000000000..1894f6ad5edb +--- /dev/null ++++ b/drivers/hwmon/aspeed-g6-pwm-tacho.c +@@ -0,0 +1,1025 @@ ++/* ++ * Copyright (C) ASPEED Technology Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 or later as ++ * published by the Free Software Foundation. ++ */ ++ ++#include <linux/clk.h> ++#include <linux/errno.h> ++#include <linux/gpio/consumer.h> ++#include <linux/delay.h> ++#include <linux/hwmon.h> ++#include <linux/hwmon-sysfs.h> ++#include <linux/io.h> ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/of_platform.h> ++#include <linux/of_device.h> ++#include <linux/platform_device.h> ++#include <linux/sysfs.h> ++#include <linux/reset.h> ++#include <linux/regmap.h> ++#include <linux/thermal.h> ++ ++#define ASPEED_PWM_CTRL 0x00 //PWM0 General Register ++#define ASPEED_PWM_CTRL_CH(x) ((x * 0x10) + 0x00) ++#define PWM_LOAD_AS_WDT BIT(19) //load selection as WDT ++#define PWM_DUTY_LOAD_AS_WDT_EN BIT(18) //enable PWM duty load as WDT ++#define PWM_DUTY_SYNC_DIS BIT(17) //disable PWM duty sync ++#define PWM_CLK_ENABLE BIT(16) //enable PWM clock ++#define PWM_LEVEL_OUTPUT BIT(15) //output PWM level ++#define PWM_INVERSE BIT(14) //inverse PWM pin ++#define PWM_OPEN_DRAIN_EN BIT(13) //enable open-drain ++#define PWM_PIN_EN BIT(12) //enable PWM pin ++#define PWM_CLK_DIV_H_MASK (0xf << 8) //PWM clock division H bit [3:0] ++#define PWM_CLK_DIV_L_MASK (0xff) //PWM clock division H bit [3:0] ++ ++/* ++\xregmid {11:8 }{RW}{PWM clock division H bit [3:0]}{ ++ 0: divide 1 \n ++ 1: divide 2 \n ++ 2: divide 4 \n ++ 3: divide 8 \n ++ ... \n ++ F: divide 32768} ++\xregmid {7 :0 }{RW}{PWM clock division L bit [7:0]}{ ++ 00: divide 1 \n ++ 01: divide 2 \n ++ 02: divide 3 \n ++ 03: divide 4 \n ++ ... \n ++ FF: divide 256} ++*/ ++ ++#define ASPEED_PWM_DUTY_CYCLE 0x04 //PWM0 Duty Cycle Register ++#define ASPEED_PWM_DUTY_CYCLE_CH(x) ((x * 0x10) + 0x04) ++#define PWM_LOOP_BIT_MASK (0xf << 24) //loop bit [7:0] ++#define PWM_PERIOD_BIT (24) //pwm period bit [7:0] ++#define PWM_PERIOD_BIT_MASK (0xff << 24) //pwm period bit [7:0] ++#define PWM_RISING_FALLING_AS_WDT_BIT (16) ++#define PWM_RISING_FALLING_AS_WDT_MASK (0xff << 16) //pwm rising/falling point bit [7:0] as WDT ++#define PWM_RISING_FALLING_MASK (0xffff) ++#define PWM_RISING_FALLING_BIT (8) //pwm falling point bit [7:0] ++#define PWM_RISING_RISING_BIT (0) //pwm rising point bit [7:0] ++ ++#define ASPEED_TACHO_CTRL 0x08 //TACH0 General Register ++#define ASPEED_TACHO_CTRL_CH(x) ((x * 0x10) + 0x08) ++#define TACHO_IER BIT(31) //enable tacho interrupt ++#define TACHO_INVERS_LIMIT BIT(30) //inverse tacho limit comparison ++#define TACHO_LOOPBACK BIT(29) //tacho loopback ++#define TACHO_ENABLE BIT(28) //{enable tacho} ++#define TACHO_DEBOUNCE_BIT (26) //{tacho de-bounce} ++#define TACHO_DEBOUNCE_MASK (0x3 << 26) //{tacho de-bounce} ++#define TECHIO_EDGE_MASK (0x3 << 24) //tacho edge} ++#define TECHIO_EDGE_BIT (24) //tacho edge} ++#define TACHO_CLK_DIV_T_MASK (0xf << 20) ++#define TACHO_CLK_DIV_BIT (20) ++#define TACHO_THRESHOLD_MASK (0xfffff) //tacho threshold bit ++/* ++\xregmid {23:20}{RW}{tacho clock division T bit [3:0]}{ ++ 0: divide 1 \n ++ 1: divide 4 \n ++ 2: divide 16 \n ++ 3: divide 64 \n ++ ... \n ++ B: divide 4194304 \n ++ others: reserved} ++\xregmidb{19 :0 }{RW}{tacho threshold bit [19:0]} ++*/ ++ ++#define ASPEED_TACHO_STS 0x0C //TACH0 Status Register ++#define ASPEED_TACHO_STS_CH(x) ((x * 0x10) + 0x0C) ++#define TACHO_ISR BIT(31) //interrupt status and clear ++#define PWM_OUT BIT(25) //{pwm_out} ++#define PWM_OEN BIT(24) //{pwm_oeN} ++#define TACHO_DEB_INPUT BIT(23) //tacho deB input ++#define TACHO_RAW_INPUT BIT(22) //tacho raw input} ++#define TACHO_VALUE_UPDATE BIT(21) //tacho value updated since the last read ++#define TACHO_FULL_MEASUREMENT BIT(20) //{tacho full measurement} ++#define TACHO_VALUE_MASK 0xfffff //tacho value bit [19:0]} ++ ++#define MAX_CDEV_NAME_LEN 16 ++ ++struct aspeed_pwm_channel_params { ++ int load_wdt_rising_falling_pt; ++ int load_wdt_selection; //0: rising , 1: falling ++ int load_wdt_enable; ++ int duty_sync_enable; ++ int invert_pin; ++ u8 divide_h; ++ u8 divide_l; ++ u8 period; ++ u8 rising; ++ u8 falling; ++}; ++ ++static struct aspeed_pwm_channel_params default_pwm_params[] = { ++ [0] = { ++ .load_wdt_rising_falling_pt = 0x10, ++ .load_wdt_selection = 0, ++ .load_wdt_enable = 1, ++ .duty_sync_enable = 0, ++ .invert_pin = 0, ++ .divide_h = 0x5, ++ .divide_l = 0x6, ++ .period = 0x13, //5% ~~ ++ .rising = 0x00, ++ .falling = 0x0a, ++ }, ++ [1] = { ++ .load_wdt_rising_falling_pt = 0x10, ++ .load_wdt_selection = 0, ++ .load_wdt_enable = 0, ++ .duty_sync_enable = 0, ++ .invert_pin = 0, ++ .divide_h = 0x5, ++ .divide_l = 0x6, ++ .period = 0x13, //5% ~~ ++ .rising = 0x00, ++ .falling = 0x0a, ++ }, ++ [2] = { ++ .load_wdt_rising_falling_pt = 0x10, ++ .load_wdt_selection = 0, ++ .load_wdt_enable = 0, ++ .duty_sync_enable = 0, ++ .invert_pin = 0, ++ .divide_h = 0x5, ++ .divide_l = 0x6, ++ .period = 0x13, //5% ~~ ++ .rising = 0x00, ++ .falling = 0x0a, ++ }, ++ [3] = { ++ .load_wdt_rising_falling_pt = 0x10, ++ .load_wdt_selection = 0, ++ .load_wdt_enable = 0, ++ .duty_sync_enable = 0, ++ .invert_pin = 0, ++ .divide_h = 0x5, ++ .divide_l = 0x6, ++ .period = 0x13, //5% ~~ ++ .rising = 0x00, ++ .falling = 0x0a, ++ }, ++ [4] = { ++ .load_wdt_rising_falling_pt = 0x10, ++ .load_wdt_selection = 0, ++ .load_wdt_enable = 0, ++ .duty_sync_enable = 0, ++ .invert_pin = 0, ++ .divide_h = 0x5, ++ .divide_l = 0x6, ++ .period = 0x13, //5% ~~ ++ .rising = 0x00, ++ .falling = 0x0a, ++ }, ++ [5] = { ++ .load_wdt_rising_falling_pt = 0x10, ++ .load_wdt_selection = 0, ++ .load_wdt_enable = 0, ++ .duty_sync_enable = 0, ++ .invert_pin = 0, ++ .divide_h = 0x5, ++ .divide_l = 0x6, ++ .period = 0x13, //5% ~~ ++ .rising = 0x00, ++ .falling = 0x0a, ++ }, ++ [6] = { ++ .load_wdt_rising_falling_pt = 0x10, ++ .load_wdt_selection = 0, ++ .load_wdt_enable = 0, ++ .duty_sync_enable = 0, ++ .invert_pin = 0, ++ .divide_h = 0x5, ++ .divide_l = 0x6, ++ .period = 0x13, //5% ~~ ++ .rising = 0x00, ++ .falling = 0x0a, ++ }, ++ [7] = { ++ .load_wdt_rising_falling_pt = 0x10, ++ .load_wdt_selection = 0, ++ .load_wdt_enable = 0, ++ .duty_sync_enable = 0, ++ .invert_pin = 0, ++ .divide_h = 0x5, ++ .divide_l = 0x6, ++ .period = 0x13, //5% ~~ ++ .rising = 0x00, ++ .falling = 0x0a, ++ }, ++ [8] = { ++ .load_wdt_rising_falling_pt = 0x10, ++ .load_wdt_selection = 0, ++ .load_wdt_enable = 0, ++ .duty_sync_enable = 0, ++ .invert_pin = 0, ++ .divide_h = 0x5, ++ .divide_l = 0x6, ++ .period = 0x13, //5% ~~ ++ .rising = 0x00, ++ .falling = 0x0a, ++ }, ++ [9] = { ++ .load_wdt_rising_falling_pt = 0x10, ++ .load_wdt_selection = 0, ++ .load_wdt_enable = 0, ++ .duty_sync_enable = 0, ++ .invert_pin = 0, ++ .divide_h = 0x5, ++ .divide_l = 0x6, ++ .period = 0x13, //5% ~~ ++ .rising = 0x00, ++ .falling = 0x0a, ++ }, ++ [10] = { ++ .load_wdt_rising_falling_pt = 0x10, ++ .load_wdt_selection = 0, ++ .load_wdt_enable = 0, ++ .duty_sync_enable = 0, ++ .invert_pin = 0, ++ .divide_h = 0x5, ++ .divide_l = 0x6, ++ .period = 0x13, //5% ~~ ++ .rising = 0x00, ++ .falling = 0x0a, ++ }, ++ [11] = { ++ .load_wdt_rising_falling_pt = 0x10, ++ .load_wdt_selection = 0, ++ .load_wdt_enable = 0, ++ .duty_sync_enable = 0, ++ .invert_pin = 0, ++ .divide_h = 0x5, ++ .divide_l = 0x6, ++ .period = 0x13, //5% ~~ ++ .rising = 0x00, ++ .falling = 0x0a, ++ }, ++ [12] = { ++ .load_wdt_rising_falling_pt = 0x10, ++ .load_wdt_selection = 0, ++ .load_wdt_enable = 0, ++ .duty_sync_enable = 0, ++ .invert_pin = 0, ++ .divide_h = 0x5, ++ .divide_l = 0x6, ++ .period = 0x13, //5% ~~ ++ .rising = 0x00, ++ .falling = 0x0a, ++ }, ++ [13] = { ++ .load_wdt_rising_falling_pt = 0x10, ++ .load_wdt_selection = 0, ++ .load_wdt_enable = 0, ++ .duty_sync_enable = 0, ++ .invert_pin = 0, ++ .divide_h = 0x5, ++ .divide_l = 0x6, ++ .period = 0x13, //5% ~~ ++ .rising = 0x00, ++ .falling = 0x0a, ++ }, ++ [14] = { ++ .load_wdt_rising_falling_pt = 0x10, ++ .load_wdt_selection = 0, ++ .load_wdt_enable = 0, ++ .duty_sync_enable = 0, ++ .invert_pin = 0, ++ .divide_h = 0x5, ++ .divide_l = 0x6, ++ .period = 0x13, //5% ~~ ++ .rising = 0x00, ++ .falling = 0x0a, ++ }, ++ [15] = { ++ .load_wdt_rising_falling_pt = 0x10, ++ .load_wdt_selection = 0, ++ .load_wdt_enable = 0, ++ .duty_sync_enable = 0, ++ .invert_pin = 0, ++ .divide_h = 0x5, ++ .divide_l = 0x6, ++ .period = 0x13, //5% ~~ ++ .rising = 0x00, ++ .falling = 0x0a, ++ }, ++}; ++ ++/* ++ * 5:4 fan tach edge mode selection bit: ++ * 00: falling ++ * 01: rising ++ * 10: both ++ * 11: reserved. ++ */ ++ ++struct aspeed_tacho_channel_params { ++ int limited_inverse; ++ u16 threshold; ++ u8 tacho_edge; ++ u8 tacho_debounce; ++ u8 divide; ++}; ++ ++ ++static struct aspeed_tacho_channel_params default_tacho_params[] = { ++ [0] = { ++ .limited_inverse = 0, ++ .threshold = 0, ++ .tacho_edge = 0, ++ .tacho_debounce = 0, ++ .divide = 8, ++ }, ++ [1] = { ++ .limited_inverse = 0, ++ .threshold = 0, ++ .tacho_edge = 0, ++ .tacho_debounce = 0, ++ .divide = 8, ++ }, ++ [2] = { ++ .limited_inverse = 0, ++ .threshold = 0, ++ .tacho_edge = 0, ++ .tacho_debounce = 0, ++ .divide = 8, ++ }, ++ [3] = { ++ .limited_inverse = 0, ++ .threshold = 0, ++ .tacho_edge = 0, ++ .tacho_debounce = 0, ++ .divide = 8, ++ }, ++ [4] = { ++ .limited_inverse = 0, ++ .threshold = 0, ++ .tacho_edge = 0, ++ .tacho_debounce = 0, ++ .divide = 8, ++ }, ++ [5] = { ++ .limited_inverse = 0, ++ .threshold = 0, ++ .tacho_edge = 0, ++ .tacho_debounce = 0, ++ .divide = 8, ++ }, ++ [6] = { ++ .limited_inverse = 0, ++ .threshold = 0, ++ .tacho_edge = 0, ++ .tacho_debounce = 0, ++ .divide = 8, ++ }, ++ [7] = { ++ .limited_inverse = 0, ++ .threshold = 0, ++ .tacho_edge = 0, ++ .tacho_debounce = 0, ++ .divide = 8, ++ }, ++ [8] = { ++ .limited_inverse = 0, ++ .threshold = 0, ++ .tacho_edge = 0, ++ .tacho_debounce = 0, ++ .divide = 8, ++ }, ++ [9] = { ++ .limited_inverse = 0, ++ .threshold = 0, ++ .tacho_edge = 0, ++ .tacho_debounce = 0, ++ .divide = 8, ++ }, ++ [10] = { ++ .limited_inverse = 0, ++ .threshold = 0, ++ .tacho_edge = 0, ++ .tacho_debounce = 0, ++ .divide = 8, ++ }, ++ [11] = { ++ .limited_inverse = 0, ++ .threshold = 0, ++ .tacho_edge = 0, ++ .tacho_debounce = 0, ++ .divide = 8, ++ }, ++ [12] = { ++ .limited_inverse = 0, ++ .threshold = 0, ++ .tacho_edge = 0, ++ .tacho_debounce = 0, ++ .divide = 8, ++ }, ++ [13] = { ++ .limited_inverse = 0, ++ .threshold = 0, ++ .tacho_edge = 0, ++ .tacho_debounce = 0, ++ .divide = 8, ++ }, ++ [14] = { ++ .limited_inverse = 0, ++ .threshold = 0, ++ .tacho_edge = 0, ++ .tacho_debounce = 0, ++ .divide = 8, ++ }, ++ [15] = { ++ .limited_inverse = 0, ++ .threshold = 0, ++ .tacho_edge = 0, ++ .tacho_debounce = 0, ++ .divide = 8, ++ }, ++}; ++ ++struct aspeed_pwm_tachometer_data { ++ struct regmap *regmap; ++ unsigned long clk_freq; ++ struct reset_control *reset; ++ bool pwm_present[16]; ++ bool fan_tach_present[16]; ++ struct aspeed_pwm_channel_params *pwm_channel; ++ struct aspeed_tacho_channel_params *tacho_channel; ++ struct aspeed_cooling_device *cdev[8]; ++ const struct attribute_group *groups[3]; ++}; ++ ++struct aspeed_cooling_device { ++ char name[16]; ++ struct aspeed_pwm_tachometer_data *priv; ++ struct thermal_cooling_device *tcdev; ++ int pwm_channel; ++ u8 *cooling_levels; ++ u8 max_state; ++ u8 cur_state; ++}; ++ ++static int regmap_aspeed_pwm_tachometer_reg_write(void *context, unsigned int reg, ++ unsigned int val) ++{ ++ void __iomem *regs = (void __iomem *)context; ++ ++ writel(val, regs + reg); ++ return 0; ++} ++ ++static int regmap_aspeed_pwm_tachometer_reg_read(void *context, unsigned int reg, ++ unsigned int *val) ++{ ++ void __iomem *regs = (void __iomem *)context; ++ ++ *val = readl(regs + reg); ++ return 0; ++} ++ ++static const struct regmap_config aspeed_pwm_tachometer_regmap_config = { ++ .reg_bits = 32, ++ .val_bits = 32, ++ .reg_stride = 4, ++ .max_register = 0x100, ++ .reg_write = regmap_aspeed_pwm_tachometer_reg_write, ++ .reg_read = regmap_aspeed_pwm_tachometer_reg_read, ++ .fast_io = true, ++}; ++ ++static void aspeed_set_pwm_channel_enable(struct regmap *regmap, u8 pwm_channel, ++ bool enable) ++{ ++ regmap_update_bits(regmap, ASPEED_PWM_CTRL_CH(pwm_channel), (PWM_CLK_ENABLE | PWM_PIN_EN), enable ? (PWM_CLK_ENABLE | PWM_PIN_EN) : 0); ++} ++ ++static void aspeed_set_fan_tach_ch_enable(struct aspeed_pwm_tachometer_data *priv, u8 fan_tach_ch, ++ bool enable) ++{ ++ u32 i = 0, j; ++ u32 divide_val = 0; ++ u32 reg_value = 0; ++ ++ if(enable) { ++ //4 ^ n ++ //check pwm clk and to change tacho devide 25KZ ++ for(i = 0; i < 12; i++) { ++ divide_val = 1; ++ for (j = 1; j <= i; j++) ++ divide_val *= 4; ++// printk("i : %d , priv->clk_freq/divide_val %d ",i, priv->clk_freq/divide_val); ++ if((priv->clk_freq/divide_val) < 250000) ++ break; ++ } ++ i--; ++ divide_val = ((1 << i) * (1 << i)); ++// printk("tacho divide_val %d , i %x max tacho clk %d \n", divide_val, i, priv->clk_freq / divide_val); ++ priv->tacho_channel[fan_tach_ch].divide = i; ++ ++ reg_value = TACHO_ENABLE | ++ (priv->tacho_channel[fan_tach_ch].tacho_edge << TECHIO_EDGE_BIT) | ++ (priv->tacho_channel[fan_tach_ch].divide << TACHO_CLK_DIV_BIT) | ++ (priv->tacho_channel[fan_tach_ch].tacho_debounce << TACHO_DEBOUNCE_BIT); ++ ++ if(priv->tacho_channel[fan_tach_ch].limited_inverse) ++ reg_value |= TACHO_INVERS_LIMIT; ++ ++ if(priv->tacho_channel[fan_tach_ch].threshold) ++ reg_value |= (TACHO_IER | priv->tacho_channel[fan_tach_ch].threshold); ++ ++ regmap_write(priv->regmap, ASPEED_TACHO_CTRL_CH(fan_tach_ch), reg_value); ++ } else ++ regmap_update_bits(priv->regmap, ASPEED_TACHO_CTRL_CH(fan_tach_ch), TACHO_ENABLE, 0); ++} ++ ++static void aspeed_set_pwm_channel_fan_ctrl(struct aspeed_pwm_tachometer_data *priv, ++ u8 index, u8 fan_ctrl) ++{ ++ u32 duty_value, ctrl_value; ++ ++ if (fan_ctrl == 0) { ++ aspeed_set_pwm_channel_enable(priv->regmap, index, false); ++ } else { ++ duty_value = (priv->pwm_channel[index].period << PWM_PERIOD_BIT) | ++ (0 << PWM_RISING_RISING_BIT) | (fan_ctrl << PWM_RISING_FALLING_BIT); ++ ++ ctrl_value = (priv->pwm_channel[index].divide_h << 8) | priv->pwm_channel[index].divide_l; ++ ++ if (priv->pwm_channel[index].load_wdt_enable) { ++ ctrl_value |= PWM_DUTY_LOAD_AS_WDT_EN; ++ if(priv->pwm_channel[index].load_wdt_selection) { ++ ctrl_value |= PWM_LOAD_AS_WDT; ++ duty_value |= (priv->pwm_channel[index].load_wdt_rising_falling_pt << PWM_RISING_FALLING_AS_WDT_BIT); ++ } else { ++ duty_value |= (priv->pwm_channel[index].load_wdt_rising_falling_pt << PWM_RISING_FALLING_AS_WDT_BIT); ++ } ++ } ++ ++ regmap_write(priv->regmap, ASPEED_PWM_DUTY_CYCLE_CH(index), duty_value); ++ ++ regmap_write(priv->regmap, ASPEED_PWM_CTRL_CH(index), ctrl_value); ++// printk("pwm clk is %d \n", priv->clk_freq / (priv->pwm_channel[index].period + 1)); ++ aspeed_set_pwm_channel_enable(priv->regmap, index, true); ++ } ++} ++ ++#define BOTH_EDGES 0x02 /* 10b */ ++ ++static int aspeed_get_fan_tach_ch_rpm(struct aspeed_pwm_tachometer_data *priv, ++ u8 fan_tach_ch) ++{ ++ u32 raw_data, tach_div, clk_source, val; ++ u8 mode, both; ++ int i, retries = 3; ++ ++ for(i = 0; i < retries; i++) { ++ regmap_read(priv->regmap, ASPEED_TACHO_STS_CH(fan_tach_ch), &val); ++ if (TACHO_FULL_MEASUREMENT & val) ++ break; ++ } ++ ++ raw_data = val & TACHO_VALUE_MASK; ++ if(raw_data == 0xfffff) ++ return 0; ++ ++ tach_div = priv->tacho_channel[fan_tach_ch].divide; ++ /* ++ * We need the mode to determine if the raw_data is double (from ++ * counting both edges). ++ */ ++ mode = priv->tacho_channel[fan_tach_ch].tacho_edge; ++ both = (mode & BOTH_EDGES) ? 1 : 0; ++// printk("clk %ld, raw_data %x , tach_div %x both %x \n", priv->clk_freq, raw_data, tach_div, both); ++ ++ tach_div = (tach_div * 2) * (0x1 << both); ++ clk_source = priv->clk_freq; ++ ++ if (raw_data == 0) ++ return 0; ++ ++ return (clk_source * 60) / (2 * raw_data * tach_div); ++ ++} ++ ++static ssize_t set_pwm(struct device *dev, struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); ++ int index = sensor_attr->index; ++ int ret; ++ struct aspeed_pwm_tachometer_data *priv = dev_get_drvdata(dev); ++ long fan_ctrl; ++ ++ ret = kstrtol(buf, 10, &fan_ctrl); ++ if (ret != 0) ++ return ret; ++ ++ if (fan_ctrl < 0 || fan_ctrl > priv->pwm_channel[index].period) ++ return -EINVAL; ++ ++ if (priv->pwm_channel[index].falling == fan_ctrl) ++ return count; ++ ++ priv->pwm_channel[index].falling = fan_ctrl; ++ aspeed_set_pwm_channel_fan_ctrl(priv, index, fan_ctrl); ++ ++ return count; ++} ++ ++static ssize_t show_pwm(struct device *dev, struct device_attribute *attr, ++ char *buf) ++{ ++ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); ++ int index = sensor_attr->index; ++ struct aspeed_pwm_tachometer_data *priv = dev_get_drvdata(dev); ++ ++ return sprintf(buf, "%u\n", priv->pwm_channel[index].falling); ++} ++ ++static ssize_t show_rpm(struct device *dev, struct device_attribute *attr, ++ char *buf) ++{ ++ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); ++ int index = sensor_attr->index; ++ int rpm; ++ struct aspeed_pwm_tachometer_data *priv = dev_get_drvdata(dev); ++ ++ rpm = aspeed_get_fan_tach_ch_rpm(priv, index); ++ if (rpm < 0) ++ return rpm; ++ ++ return sprintf(buf, "%d\n", rpm); ++} ++ ++static umode_t pwm_is_visible(struct kobject *kobj, ++ struct attribute *a, int index) ++{ ++ struct device *dev = container_of(kobj, struct device, kobj); ++ struct aspeed_pwm_tachometer_data *priv = dev_get_drvdata(dev); ++ ++ if (!priv->pwm_present[index]) ++ return 0; ++ return a->mode; ++} ++ ++static umode_t fan_dev_is_visible(struct kobject *kobj, ++ struct attribute *a, int index) ++{ ++ struct device *dev = container_of(kobj, struct device, kobj); ++ struct aspeed_pwm_tachometer_data *priv = dev_get_drvdata(dev); ++ ++ if (!priv->fan_tach_present[index]) ++ return 0; ++ return a->mode; ++} ++ ++static SENSOR_DEVICE_ATTR(pwm0, 0644, ++ show_pwm, set_pwm, 0); ++static SENSOR_DEVICE_ATTR(pwm1, 0644, ++ show_pwm, set_pwm, 1); ++static SENSOR_DEVICE_ATTR(pwm2, 0644, ++ show_pwm, set_pwm, 2); ++static SENSOR_DEVICE_ATTR(pwm3, 0644, ++ show_pwm, set_pwm, 3); ++static SENSOR_DEVICE_ATTR(pwm4, 0644, ++ show_pwm, set_pwm, 4); ++static SENSOR_DEVICE_ATTR(pwm5, 0644, ++ show_pwm, set_pwm, 5); ++static SENSOR_DEVICE_ATTR(pwm6, 0644, ++ show_pwm, set_pwm, 6); ++static SENSOR_DEVICE_ATTR(pwm7, 0644, ++ show_pwm, set_pwm, 7); ++static SENSOR_DEVICE_ATTR(pwm8, 0644, ++ show_pwm, set_pwm, 8); ++static SENSOR_DEVICE_ATTR(pwm9, 0644, ++ show_pwm, set_pwm, 9); ++static SENSOR_DEVICE_ATTR(pwm10, 0644, ++ show_pwm, set_pwm, 10); ++static SENSOR_DEVICE_ATTR(pwm11, 0644, ++ show_pwm, set_pwm, 11); ++static SENSOR_DEVICE_ATTR(pwm12, 0644, ++ show_pwm, set_pwm, 12); ++static SENSOR_DEVICE_ATTR(pwm13, 0644, ++ show_pwm, set_pwm, 13); ++static SENSOR_DEVICE_ATTR(pwm14, 0644, ++ show_pwm, set_pwm, 14); ++static SENSOR_DEVICE_ATTR(pwm15, 0644, ++ show_pwm, set_pwm, 15); ++static struct attribute *pwm_dev_attrs[] = { ++ &sensor_dev_attr_pwm0.dev_attr.attr, ++ &sensor_dev_attr_pwm1.dev_attr.attr, ++ &sensor_dev_attr_pwm2.dev_attr.attr, ++ &sensor_dev_attr_pwm3.dev_attr.attr, ++ &sensor_dev_attr_pwm4.dev_attr.attr, ++ &sensor_dev_attr_pwm5.dev_attr.attr, ++ &sensor_dev_attr_pwm6.dev_attr.attr, ++ &sensor_dev_attr_pwm7.dev_attr.attr, ++ &sensor_dev_attr_pwm8.dev_attr.attr, ++ &sensor_dev_attr_pwm9.dev_attr.attr, ++ &sensor_dev_attr_pwm10.dev_attr.attr, ++ &sensor_dev_attr_pwm11.dev_attr.attr, ++ &sensor_dev_attr_pwm12.dev_attr.attr, ++ &sensor_dev_attr_pwm13.dev_attr.attr, ++ &sensor_dev_attr_pwm14.dev_attr.attr, ++ &sensor_dev_attr_pwm15.dev_attr.attr, ++ NULL, ++}; ++ ++static const struct attribute_group pwm_dev_group = { ++ .attrs = pwm_dev_attrs, ++ .is_visible = pwm_is_visible, ++}; ++ ++static SENSOR_DEVICE_ATTR(fan0_input, 0444, ++ show_rpm, NULL, 0); ++static SENSOR_DEVICE_ATTR(fan1_input, 0444, ++ show_rpm, NULL, 1); ++static SENSOR_DEVICE_ATTR(fan2_input, 0444, ++ show_rpm, NULL, 2); ++static SENSOR_DEVICE_ATTR(fan3_input, 0444, ++ show_rpm, NULL, 3); ++static SENSOR_DEVICE_ATTR(fan4_input, 0444, ++ show_rpm, NULL, 4); ++static SENSOR_DEVICE_ATTR(fan5_input, 0444, ++ show_rpm, NULL, 5); ++static SENSOR_DEVICE_ATTR(fan6_input, 0444, ++ show_rpm, NULL, 6); ++static SENSOR_DEVICE_ATTR(fan7_input, 0444, ++ show_rpm, NULL, 7); ++static SENSOR_DEVICE_ATTR(fan8_input, 0444, ++ show_rpm, NULL, 8); ++static SENSOR_DEVICE_ATTR(fan9_input, 0444, ++ show_rpm, NULL, 9); ++static SENSOR_DEVICE_ATTR(fan10_input, 0444, ++ show_rpm, NULL, 10); ++static SENSOR_DEVICE_ATTR(fan11_input, 0444, ++ show_rpm, NULL, 11); ++static SENSOR_DEVICE_ATTR(fan12_input, 0444, ++ show_rpm, NULL, 12); ++static SENSOR_DEVICE_ATTR(fan13_input, 0444, ++ show_rpm, NULL, 13); ++static SENSOR_DEVICE_ATTR(fan14_input, 0444, ++ show_rpm, NULL, 14); ++static SENSOR_DEVICE_ATTR(fan15_input, 0444, ++ show_rpm, NULL, 15); ++static struct attribute *fan_dev_attrs[] = { ++ &sensor_dev_attr_fan0_input.dev_attr.attr, ++ &sensor_dev_attr_fan1_input.dev_attr.attr, ++ &sensor_dev_attr_fan2_input.dev_attr.attr, ++ &sensor_dev_attr_fan3_input.dev_attr.attr, ++ &sensor_dev_attr_fan4_input.dev_attr.attr, ++ &sensor_dev_attr_fan5_input.dev_attr.attr, ++ &sensor_dev_attr_fan6_input.dev_attr.attr, ++ &sensor_dev_attr_fan7_input.dev_attr.attr, ++ &sensor_dev_attr_fan8_input.dev_attr.attr, ++ &sensor_dev_attr_fan9_input.dev_attr.attr, ++ &sensor_dev_attr_fan10_input.dev_attr.attr, ++ &sensor_dev_attr_fan11_input.dev_attr.attr, ++ &sensor_dev_attr_fan12_input.dev_attr.attr, ++ &sensor_dev_attr_fan13_input.dev_attr.attr, ++ &sensor_dev_attr_fan14_input.dev_attr.attr, ++ &sensor_dev_attr_fan15_input.dev_attr.attr, ++ NULL ++}; ++ ++static const struct attribute_group fan_dev_group = { ++ .attrs = fan_dev_attrs, ++ .is_visible = fan_dev_is_visible, ++}; ++ ++static void aspeed_create_pwm_channel(struct aspeed_pwm_tachometer_data *priv, ++ u8 pwm_channel) ++{ ++ priv->pwm_present[pwm_channel] = true; ++ ++ //use default ++ aspeed_set_pwm_channel_fan_ctrl(priv, pwm_channel, priv->pwm_channel[pwm_channel].falling); ++} ++ ++static void aspeed_create_fan_tach_channel(struct aspeed_pwm_tachometer_data *priv, ++ u8 *fan_tach_ch, ++ int count) ++{ ++ u8 val, index; ++ ++ for (val = 0; val < count; val++) { ++ index = fan_tach_ch[val]; ++ priv->fan_tach_present[index] = true; ++ aspeed_set_fan_tach_ch_enable(priv, index, true); ++ } ++} ++ ++static int ++aspeed_pwm_cz_get_max_state(struct thermal_cooling_device *tcdev, ++ unsigned long *state) ++{ ++ struct aspeed_cooling_device *cdev = tcdev->devdata; ++ ++ *state = cdev->max_state; ++ ++ return 0; ++} ++ ++static int ++aspeed_pwm_cz_get_cur_state(struct thermal_cooling_device *tcdev, ++ unsigned long *state) ++{ ++ struct aspeed_cooling_device *cdev = tcdev->devdata; ++ ++ *state = cdev->cur_state; ++ ++ return 0; ++} ++ ++static int ++aspeed_pwm_cz_set_cur_state(struct thermal_cooling_device *tcdev, ++ unsigned long state) ++{ ++ struct aspeed_cooling_device *cdev = tcdev->devdata; ++ ++ if (state > cdev->max_state) ++ return -EINVAL; ++ ++ cdev->cur_state = state; ++ cdev->priv->pwm_channel[cdev->pwm_channel].falling = ++ cdev->cooling_levels[cdev->cur_state]; ++ aspeed_set_pwm_channel_fan_ctrl(cdev->priv, cdev->pwm_channel, ++ cdev->cooling_levels[cdev->cur_state]); ++ ++ return 0; ++} ++ ++static const struct thermal_cooling_device_ops aspeed_pwm_cool_ops = { ++ .get_max_state = aspeed_pwm_cz_get_max_state, ++ .get_cur_state = aspeed_pwm_cz_get_cur_state, ++ .set_cur_state = aspeed_pwm_cz_set_cur_state, ++}; ++ ++static int aspeed_create_pwm_cooling(struct device *dev, ++ struct device_node *child, ++ struct aspeed_pwm_tachometer_data *priv, ++ u32 pwm_channel, u8 num_levels) ++{ ++ int ret; ++ struct aspeed_cooling_device *cdev; ++ ++ cdev = devm_kzalloc(dev, sizeof(*cdev), GFP_KERNEL); ++ if (!cdev) ++ return -ENOMEM; ++ ++ cdev->cooling_levels = devm_kzalloc(dev, num_levels, GFP_KERNEL); ++ if (!cdev->cooling_levels) ++ return -ENOMEM; ++ ++ cdev->max_state = num_levels - 1; ++ ret = of_property_read_u8_array(child, "cooling-levels", ++ cdev->cooling_levels, ++ num_levels); ++ if (ret) { ++ dev_err(dev, "Property 'cooling-levels' cannot be read.\n"); ++ return ret; ++ } ++ snprintf(cdev->name, MAX_CDEV_NAME_LEN, "%s%d", child->name, pwm_channel); ++ ++ cdev->tcdev = thermal_of_cooling_device_register(child, ++ cdev->name, ++ cdev, ++ &aspeed_pwm_cool_ops); ++ if (IS_ERR(cdev->tcdev)) ++ return PTR_ERR(cdev->tcdev); ++ ++ cdev->priv = priv; ++ cdev->pwm_channel = pwm_channel; ++ ++ priv->cdev[pwm_channel] = cdev; ++ ++ return 0; ++} ++ ++static int aspeed_pwm_create_fan(struct device *dev, ++ struct device_node *child, ++ struct aspeed_pwm_tachometer_data *priv) ++{ ++ u8 *fan_tach_ch; ++ u32 pwm_channel; ++ int ret, count; ++ ++ ret = of_property_read_u32(child, "reg", &pwm_channel); ++ if (ret) ++ return ret; ++ ++ aspeed_create_pwm_channel(priv, (u8)pwm_channel); ++ ++ ret = of_property_count_u8_elems(child, "cooling-levels"); ++ if (ret > 0) { ++ ret = aspeed_create_pwm_cooling(dev, child, priv, pwm_channel, ++ ret); ++ if (ret) ++ return ret; ++ } ++ ++ count = of_property_count_u8_elems(child, "aspeed,fan-tach-ch"); ++ if (count < 1) ++ return -EINVAL; ++ ++ fan_tach_ch = devm_kzalloc(dev, sizeof(*fan_tach_ch) * count, ++ GFP_KERNEL); ++ if (!fan_tach_ch) ++ return -ENOMEM; ++ ret = of_property_read_u8_array(child, "aspeed,fan-tach-ch", ++ fan_tach_ch, count); ++ if (ret) ++ return ret; ++ ++ aspeed_create_fan_tach_channel(priv, fan_tach_ch, count); ++ ++ return 0; ++} ++ ++static int aspeed_pwm_tachometer_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct device_node *np, *child; ++ struct aspeed_pwm_tachometer_data *priv; ++ void __iomem *regs; ++ struct resource *res; ++ struct device *hwmon; ++ struct clk *clk; ++ int ret; ++ ++ np = dev->of_node; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (!res) ++ return -ENOENT; ++ regs = devm_ioremap_resource(dev, res); ++ if (IS_ERR(regs)) ++ return PTR_ERR(regs); ++ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ priv->pwm_channel = default_pwm_params; ++ priv->tacho_channel = default_tacho_params; ++ priv->regmap = devm_regmap_init(dev, NULL, (__force void *)regs, ++ &aspeed_pwm_tachometer_regmap_config); ++ if (IS_ERR(priv->regmap)) ++ return PTR_ERR(priv->regmap); ++ ++ clk = devm_clk_get(dev, NULL); ++ if (IS_ERR(clk)) ++ return -ENODEV; ++ priv->clk_freq = clk_get_rate(clk); ++ ++ priv->reset = devm_reset_control_get(&pdev->dev, NULL); ++ if (IS_ERR(priv->reset)) { ++ dev_err(&pdev->dev, "can't get aspeed_pwm_tacho reset\n"); ++ return PTR_ERR(priv->reset); ++ } ++ ++ //scu init ++ reset_control_assert(priv->reset); ++ reset_control_deassert(priv->reset); ++ ++ for_each_child_of_node(np, child) { ++ ret = aspeed_pwm_create_fan(dev, child, priv); ++ if (ret) { ++ of_node_put(child); ++ return ret; ++ } ++ } ++ ++ priv->groups[0] = &pwm_dev_group; ++ priv->groups[1] = &fan_dev_group; ++ priv->groups[2] = NULL; ++ hwmon = devm_hwmon_device_register_with_groups(dev, ++ "aspeed_g6_pwm_tacho", ++ priv, priv->groups); ++ ++ return PTR_ERR_OR_ZERO(hwmon); ++} ++ ++static const struct of_device_id of_pwm_tachometer_match_table[] = { ++ { .compatible = "aspeed,ast2600-pwm-tacho", }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, of_pwm_tachometer_match_table); ++ ++static struct platform_driver aspeed_pwm_tachometer_driver = { ++ .probe = aspeed_pwm_tachometer_probe, ++ .driver = { ++ .name = "aspeed_g6_pwm_tacho", ++ .of_match_table = of_pwm_tachometer_match_table, ++ }, ++}; ++ ++module_platform_driver(aspeed_pwm_tachometer_driver); ++ ++MODULE_AUTHOR("Ryan Chen <ryan_chen@aspeedtech.com>"); ++MODULE_DESCRIPTION("ASPEED PWM and Fan Tachometer device driver"); ++MODULE_LICENSE("GPL"); +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0077-soc-aspeed-Add-read-only-property-support.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0077-soc-aspeed-Add-read-only-property-support.patch new file mode 100644 index 000000000..7ea00a4a2 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0077-soc-aspeed-Add-read-only-property-support.patch @@ -0,0 +1,47 @@ +From f2d62c90f2ea05b1abf9a1887502122eefb5906d Mon Sep 17 00:00:00 2001 +From: Yong Li <yong.b.li@linux.intel.com> +Date: Wed, 16 Oct 2019 15:11:06 +0800 +Subject: [PATCH] soc: aspeed: Add read-only property support + +Add a read-only property, to support export register bits as read-only. + +Signed-off-by: Yong Li <yong.b.li@linux.intel.com> +--- + drivers/soc/aspeed/aspeed-bmc-misc.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/drivers/soc/aspeed/aspeed-bmc-misc.c b/drivers/soc/aspeed/aspeed-bmc-misc.c +index 314007b..bf88376 100644 +--- a/drivers/soc/aspeed/aspeed-bmc-misc.c ++++ b/drivers/soc/aspeed/aspeed-bmc-misc.c +@@ -15,6 +15,7 @@ struct aspeed_bmc_ctrl { + u32 offset; + u32 mask; + u32 shift; ++ bool read_only; + struct regmap *map; + struct kobj_attribute attr; + }; +@@ -55,6 +56,8 @@ static int aspeed_bmc_misc_parse_dt_child(struct device_node *child, + if (rc < 0) + return rc; + ++ ctrl->read_only = of_property_read_bool(child, "read-only"); ++ + ctrl->mask <<= ctrl->shift; + + return 0; +@@ -116,6 +119,10 @@ static ssize_t aspeed_bmc_misc_store(struct kobject *kobj, + return rc; + + ctrl = container_of(attr, struct aspeed_bmc_ctrl, attr); ++ ++ if (ctrl->read_only) ++ return -EROFS; ++ + val <<= ctrl->shift; + rc = regmap_update_bits(ctrl->map, ctrl->offset, ctrl->mask, val); + +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0078-Fix-NCSI-driver-issue-caused-by-host-shutdown.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0078-Fix-NCSI-driver-issue-caused-by-host-shutdown.patch new file mode 100644 index 000000000..09a957860 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0078-Fix-NCSI-driver-issue-caused-by-host-shutdown.patch @@ -0,0 +1,70 @@ +From 3eaf814c51f85aaad0d181d27ff686ce35b31d6a Mon Sep 17 00:00:00 2001 +From: Kuiying Wang <kuiying.wang@intel.com> +Date: Tue, 29 Oct 2019 11:28:29 +0800 +Subject: [PATCH] Fix NCSI driver issue caused by host shutdown due to + overheated. + +NCSI device cannot be recovered when host shutdown due to overheated. + +Tested: +Heat host till shutdown due to overheated and then +run the ipmi command like power status + +Signed-off-by: Kuiying Wang <kuiying.wang@intel.com> +--- + net/ncsi/ncsi-manage.c | 18 +++++++++++++----- + 1 file changed, 13 insertions(+), 5 deletions(-) + +diff --git a/net/ncsi/ncsi-manage.c b/net/ncsi/ncsi-manage.c +index 70fe02697544..4e16a0015f26 100644 +--- a/net/ncsi/ncsi-manage.c ++++ b/net/ncsi/ncsi-manage.c +@@ -132,18 +132,15 @@ static void ncsi_channel_monitor(struct timer_list *t) + netdev_err(ndp->ndev.dev, "NCSI Channel %d timed out!\n", + nc->id); + ncsi_report_link(ndp, true); +- ndp->flags |= NCSI_DEV_RESHUFFLE; + + ncsi_stop_channel_monitor(nc); + +- ncm = &nc->modes[NCSI_MODE_LINK]; + spin_lock_irqsave(&nc->lock, flags); +- nc->state = NCSI_CHANNEL_INVISIBLE; +- ncm->data[2] &= ~0x1; ++ nc->state = NCSI_CHANNEL_INACTIVE; + spin_unlock_irqrestore(&nc->lock, flags); + + spin_lock_irqsave(&ndp->lock, flags); +- nc->state = NCSI_CHANNEL_ACTIVE; ++ ndp->flags |= NCSI_DEV_RESHUFFLE | NCSI_DEV_RESET; + list_add_tail_rcu(&nc->link, &ndp->channel_queue); + spin_unlock_irqrestore(&ndp->lock, flags); + ncsi_process_next_channel(ndp); +@@ -424,6 +421,7 @@ static void ncsi_request_timeout(struct timer_list *t) + { + struct ncsi_request *nr = from_timer(nr, t, timer); + struct ncsi_dev_priv *ndp = nr->ndp; ++ struct ncsi_dev *nd = &ndp->ndev; + struct ncsi_cmd_pkt *cmd; + struct ncsi_package *np; + struct ncsi_channel *nc; +@@ -438,6 +436,16 @@ static void ncsi_request_timeout(struct timer_list *t) + spin_unlock_irqrestore(&ndp->lock, flags); + return; + } ++ if (nd->state == ncsi_dev_state_suspend || ++ nd->state == ncsi_dev_state_suspend_select || ++ nd->state == ncsi_dev_state_suspend_gls || ++ nd->state == ncsi_dev_state_suspend_dcnt || ++ nd->state == ncsi_dev_state_suspend_dc || ++ nd->state == ncsi_dev_state_suspend_deselect || ++ nd->state == ncsi_dev_state_suspend_done) { ++ ndp->flags |= NCSI_DEV_RESET; ++ nd->state = ncsi_dev_state_suspend_done; ++ } + spin_unlock_irqrestore(&ndp->lock, flags); + + if (nr->flags == NCSI_REQ_FLAG_NETLINK_DRIVEN) { +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0080-i2c-aspeed-filter-garbage-interrupts-out.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0080-i2c-aspeed-filter-garbage-interrupts-out.patch new file mode 100644 index 000000000..89e7b894f --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0080-i2c-aspeed-filter-garbage-interrupts-out.patch @@ -0,0 +1,56 @@ +From a90b8f7dfcfcab2d40d1a575e3948ef3a34f643f Mon Sep 17 00:00:00 2001 +From: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +Date: Fri, 8 Nov 2019 15:57:27 -0800 +Subject: [PATCH] i2c: aspeed: filter garbage interrupts out + +AST2600 makes a garbage interrupt which is decribed as 'reserved' +in datasheet so filter them out. + +Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +--- + drivers/i2c/busses/i2c-aspeed.c | 14 ++++++++++++++ + 1 file changed, 14 insertions(+) + +diff --git a/drivers/i2c/busses/i2c-aspeed.c b/drivers/i2c/busses/i2c-aspeed.c +index 62b803e15ce2..c24cecdcfe89 100644 +--- a/drivers/i2c/busses/i2c-aspeed.c ++++ b/drivers/i2c/busses/i2c-aspeed.c +@@ -87,7 +87,11 @@ + * These share bit definitions, so use the same values for the enable & + * status bits. + */ ++#if defined(CONFIG_MACH_ASPEED_G6) ++#define ASPEED_I2CD_INTR_SLAVE_ADDR_RECEIVED_PENDING BIT(29) ++#else + #define ASPEED_I2CD_INTR_SLAVE_ADDR_RECEIVED_PENDING BIT(30) ++#endif + #define ASPEED_I2CD_INTR_SLAVE_INACTIVE_TIMEOUT BIT(15) + #define ASPEED_I2CD_INTR_SDA_DL_TIMEOUT BIT(14) + #define ASPEED_I2CD_INTR_BUS_RECOVER_DONE BIT(13) +@@ -118,6 +122,11 @@ + ASPEED_I2CD_INTR_RX_DONE | \ + ASPEED_I2CD_INTR_TX_NAK | \ + ASPEED_I2CD_INTR_TX_ACK) ++#define ASPEED_I2CD_INTR_STATUS_MASK \ ++ (ASPEED_I2CD_INTR_SLAVE_ADDR_RECEIVED_PENDING | \ ++ ASPEED_I2CD_INTR_GCALL_ADDR | \ ++ ASPEED_I2CD_INTR_SLAVE_MATCH | \ ++ ASPEED_I2CD_INTR_ALL) + + /* 0x14 : I2CD Command/Status Register */ + #define ASPEED_I2CD_SCL_LINE_STS BIT(18) +@@ -1021,6 +1030,11 @@ static irqreturn_t aspeed_i2c_bus_irq(int irq, void *dev_id) + /* Ack all interrupts except for Rx done */ + writel(irq_received & ~ASPEED_I2CD_INTR_RX_DONE, + bus->base + ASPEED_I2C_INTR_STS_REG); ++ /* ++ * AST2600 makes a garbage interrupt which is decribed as 'reserved' ++ * in datasheet so filter them out. ++ */ ++ irq_received &= ASPEED_I2CD_INTR_STATUS_MASK; + irq_remaining = irq_received; + + #if IS_ENABLED(CONFIG_I2C_SLAVE) +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0084-ARM-dts-aspeed-g6-add-GFX-node.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0084-ARM-dts-aspeed-g6-add-GFX-node.patch new file mode 100644 index 000000000..f578d0f04 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0084-ARM-dts-aspeed-g6-add-GFX-node.patch @@ -0,0 +1,35 @@ +From 523bbaaf9f9d064c9cf1f627d64dd7ba169d175d Mon Sep 17 00:00:00 2001 +From: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +Date: Wed, 20 Nov 2019 15:01:06 -0800 +Subject: [PATCH] ARM: dts: aspeed-g6: add GFX node + +This commit adds GFX node for AST2600 SoC. + +Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +--- + arch/arm/boot/dts/aspeed-g6.dtsi | 9 +++++++++ + 1 file changed, 9 insertions(+) + +diff --git a/arch/arm/boot/dts/aspeed-g6.dtsi b/arch/arm/boot/dts/aspeed-g6.dtsi +index b880e8e8f999..3e2153416e11 100644 +--- a/arch/arm/boot/dts/aspeed-g6.dtsi ++++ b/arch/arm/boot/dts/aspeed-g6.dtsi +@@ -330,6 +330,15 @@ + quality = <100>; + }; + ++ gfx: display@1e6e6000 { ++ compatible = "aspeed,ast2600-gfx", "syscon"; ++ reg = <0x1e6e6000 0x1000>; ++ reg-io-width = <4>; ++ clocks = <&syscon ASPEED_CLK_GATE_D1CLK>; ++ status = "disabled"; ++ interrupts = <GIC_SPI 14 IRQ_TYPE_LEVEL_HIGH>; ++ }; ++ + adc: adc@1e6e9000 { + compatible = "aspeed,ast2500-adc"; + reg = <0x1e6e9000 0x100>; +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0085-drm-add-AST2600-GFX-support.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0085-drm-add-AST2600-GFX-support.patch new file mode 100644 index 000000000..259720e87 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0085-drm-add-AST2600-GFX-support.patch @@ -0,0 +1,105 @@ +From 195cd60ff2fe38b18ebdea90245b908548f4e985 Mon Sep 17 00:00:00 2001 +From: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +Date: Wed, 20 Nov 2019 14:58:24 -0800 +Subject: [PATCH] drm: add AST2600 GFX support + +This commit adds support for AST2600 GFX. + +Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +--- + drivers/gpu/drm/aspeed/aspeed_gfx.h | 4 ++++ + drivers/gpu/drm/aspeed/aspeed_gfx_crtc.c | 6 +++--- + drivers/gpu/drm/aspeed/aspeed_gfx_drv.c | 18 +++++++++++------- + 3 files changed, 18 insertions(+), 10 deletions(-) + +diff --git a/drivers/gpu/drm/aspeed/aspeed_gfx.h b/drivers/gpu/drm/aspeed/aspeed_gfx.h +index a10358bb61ec..eebd72eb1220 100644 +--- a/drivers/gpu/drm/aspeed/aspeed_gfx.h ++++ b/drivers/gpu/drm/aspeed/aspeed_gfx.h +@@ -13,11 +13,15 @@ struct aspeed_gfx { + struct drm_simple_display_pipe pipe; + struct drm_connector connector; + struct drm_fbdev_cma *fbdev; ++ u32 scu_misc_offset; + }; + + int aspeed_gfx_create_pipe(struct drm_device *drm); + int aspeed_gfx_create_output(struct drm_device *drm); + ++#define SCU_MISC_AST2500 0x2c /* SCU Misc of AST2500 */ ++#define SCU_MISC_AST2600 0xc0 /* SCU Misc1 of AST2600 */ ++ + #define CRT_CTRL1 0x60 /* CRT Control I */ + #define CRT_CTRL2 0x64 /* CRT Control II */ + #define CRT_STATUS 0x68 /* CRT Status */ +diff --git a/drivers/gpu/drm/aspeed/aspeed_gfx_crtc.c b/drivers/gpu/drm/aspeed/aspeed_gfx_crtc.c +index 2184b8be6fd4..86359a0fe05f 100644 +--- a/drivers/gpu/drm/aspeed/aspeed_gfx_crtc.c ++++ b/drivers/gpu/drm/aspeed/aspeed_gfx_crtc.c +@@ -59,8 +59,8 @@ static void aspeed_gfx_enable_controller(struct aspeed_gfx *priv) + u32 ctrl1 = readl(priv->base + CRT_CTRL1); + u32 ctrl2 = readl(priv->base + CRT_CTRL2); + +- /* SCU2C: set DAC source for display output to Graphics CRT (GFX) */ +- regmap_update_bits(priv->scu, 0x2c, BIT(16), BIT(16)); ++ /* Set DAC source for display output to Graphics CRT (GFX) */ ++ regmap_update_bits(priv->scu, priv->scu_misc_offset, BIT(16), BIT(16)); + + writel(ctrl1 | CRT_CTRL_EN, priv->base + CRT_CTRL1); + writel(ctrl2 | CRT_CTRL_DAC_EN, priv->base + CRT_CTRL2); +@@ -74,7 +74,7 @@ static void aspeed_gfx_disable_controller(struct aspeed_gfx *priv) + writel(ctrl1 & ~CRT_CTRL_EN, priv->base + CRT_CTRL1); + writel(ctrl2 & ~CRT_CTRL_DAC_EN, priv->base + CRT_CTRL2); + +- regmap_update_bits(priv->scu, 0x2c, BIT(16), 0); ++ regmap_update_bits(priv->scu, priv->scu_misc_offset, BIT(16), 0); + } + + static void aspeed_gfx_crtc_mode_set_nofb(struct aspeed_gfx *priv) +diff --git a/drivers/gpu/drm/aspeed/aspeed_gfx_drv.c b/drivers/gpu/drm/aspeed/aspeed_gfx_drv.c +index ada2f6aca906..92bc9b475e0f 100644 +--- a/drivers/gpu/drm/aspeed/aspeed_gfx_drv.c ++++ b/drivers/gpu/drm/aspeed/aspeed_gfx_drv.c +@@ -112,8 +112,14 @@ static int aspeed_gfx_load(struct drm_device *drm) + + priv->scu = syscon_regmap_lookup_by_compatible("aspeed,ast2500-scu"); + if (IS_ERR(priv->scu)) { +- dev_err(&pdev->dev, "failed to find SCU regmap\n"); +- return PTR_ERR(priv->scu); ++ priv->scu = syscon_regmap_lookup_by_compatible("aspeed,ast2600-scu"); ++ if (IS_ERR(priv->scu)) { ++ dev_err(&pdev->dev, "failed to find SCU regmap\n"); ++ return PTR_ERR(priv->scu); ++ } ++ priv->scu_misc_offset = SCU_MISC_AST2600; ++ } else { ++ priv->scu_misc_offset = SCU_MISC_AST2500; + } + + ret = of_reserved_mem_device_init(drm->dev); +@@ -130,12 +136,9 @@ static int aspeed_gfx_load(struct drm_device *drm) + } + + priv->rst = devm_reset_control_get_exclusive(&pdev->dev, NULL); +- if (IS_ERR(priv->rst)) { +- dev_err(&pdev->dev, +- "missing or invalid reset controller device tree entry"); +- return PTR_ERR(priv->rst); ++ if (!IS_ERR_OR_NULL(priv->rst)) { ++ reset_control_deassert(priv->rst); + } +- reset_control_deassert(priv->rst); + + priv->clk = devm_clk_get(drm->dev, NULL); + if (IS_ERR(priv->clk)) { +@@ -211,6 +214,7 @@ static struct drm_driver aspeed_gfx_driver = { + + static const struct of_device_id aspeed_gfx_match[] = { + { .compatible = "aspeed,ast2500-gfx" }, ++ { .compatible = "aspeed,ast2600-gfx" }, + { } + }; + +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0086-ADC-linux-driver-for-AST2600.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0086-ADC-linux-driver-for-AST2600.patch new file mode 100644 index 000000000..4dde9fb06 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0086-ADC-linux-driver-for-AST2600.patch @@ -0,0 +1,271 @@ +From 278db13a36f128b84f76fc0f0f13860b5bf62588 Mon Sep 17 00:00:00 2001 +From: Chen Yugang <yugang.chen@linux.intel.com> +Date: Tue, 3 Dec 2019 13:41:37 +0800 +Subject: [PATCH] ADC linux driver for AST2600 + +Tested: +it's tested with DC input. + +Signed-off-by: Chen Yugang <yugang.chen@linux.intel.com> +--- + arch/arm/boot/dts/aspeed-g6.dtsi | 14 +++++- + drivers/iio/adc/aspeed_adc.c | 99 +++++++++++++++++++++++++++++++++++----- + 2 files changed, 99 insertions(+), 14 deletions(-) + +diff --git a/arch/arm/boot/dts/aspeed-g6.dtsi b/arch/arm/boot/dts/aspeed-g6.dtsi +index 3e2153416e11..5acc1085526d 100644 +--- a/arch/arm/boot/dts/aspeed-g6.dtsi ++++ b/arch/arm/boot/dts/aspeed-g6.dtsi +@@ -339,12 +339,22 @@ + interrupts = <GIC_SPI 14 IRQ_TYPE_LEVEL_HIGH>; + }; + +- adc: adc@1e6e9000 { +- compatible = "aspeed,ast2500-adc"; ++ adc0: adc@1e6e9000 { ++ compatible = "aspeed,ast2600-adc"; + reg = <0x1e6e9000 0x100>; + clocks = <&syscon ASPEED_CLK_APB2>; ++ resets = <&syscon ASPEED_RESET_ADC>; + interrupts = <GIC_SPI 46 IRQ_TYPE_LEVEL_HIGH>; ++ #io-channel-cells = <1>; ++ status = "disabled"; ++ }; ++ ++ adc1: adc@1e6e9100 { ++ compatible = "aspeed,ast2600-adc"; ++ reg = <0x1e6e9100 0x100>; ++ clocks = <&syscon ASPEED_CLK_APB2>; + resets = <&syscon ASPEED_RESET_ADC>; ++ interrupts = <GIC_SPI 46 IRQ_TYPE_LEVEL_HIGH>; + #io-channel-cells = <1>; + status = "disabled"; + }; +diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c +index d3fc39df535d..1dd5a97a16bc 100644 +--- a/drivers/iio/adc/aspeed_adc.c ++++ b/drivers/iio/adc/aspeed_adc.c +@@ -1,8 +1,12 @@ +-// SPDX-License-Identifier: GPL-2.0-only + /* +- * Aspeed AST2400/2500 ADC ++ * Aspeed AST2400/2500/2600 ADC + * + * Copyright (C) 2017 Google, Inc. ++ * Copyright (C) ASPEED Technology Inc. ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms and conditions of the GNU General Public License, ++ * version 2, as published by the Free Software Foundation. ++ * + */ + + #include <linux/clk.h> +@@ -30,6 +34,14 @@ + #define ASPEED_REG_CLOCK_CONTROL 0x0C + #define ASPEED_REG_MAX 0xC0 + ++/* ast2600 */ ++#define REF_VLOTAGE_2500mV 0 ++#define REF_VLOTAGE_1200mV GENMASK(6, 6) ++#define REF_VLOTAGE_1550mV GENMASK(7, 7) ++#define REF_VLOTAGE_900mV GENMASK(7, 6) ++ ++#define ASPEED_AUTOPENSATING BIT(5) ++ + #define ASPEED_OPERATION_MODE_POWER_DOWN (0x0 << 1) + #define ASPEED_OPERATION_MODE_STANDBY (0x1 << 1) + #define ASPEED_OPERATION_MODE_NORMAL (0x7 << 1) +@@ -45,8 +57,10 @@ struct aspeed_adc_model_data { + const char *model_name; + unsigned int min_sampling_rate; // Hz + unsigned int max_sampling_rate; // Hz +- unsigned int vref_voltage; // mV ++ u32 vref_voltage; // mV + bool wait_init_sequence; ++ struct iio_chan_spec const *channels; ++ int num_channels; + }; + + struct aspeed_adc_data { +@@ -56,6 +70,7 @@ struct aspeed_adc_data { + struct clk_hw *clk_prescaler; + struct clk_hw *clk_scaler; + struct reset_control *rst; ++ int cv; + }; + + #define ASPEED_CHAN(_idx, _data_reg_addr) { \ +@@ -87,6 +102,17 @@ static const struct iio_chan_spec aspeed_adc_iio_channels[] = { + ASPEED_CHAN(15, 0x2E), + }; + ++static const struct iio_chan_spec ast2600_adc_iio_channels[] = { ++ ASPEED_CHAN(0, 0x10), ++ ASPEED_CHAN(1, 0x12), ++ ASPEED_CHAN(2, 0x14), ++ ASPEED_CHAN(3, 0x16), ++ ASPEED_CHAN(4, 0x18), ++ ASPEED_CHAN(5, 0x1A), ++ ASPEED_CHAN(6, 0x1C), ++ ASPEED_CHAN(7, 0x1E), ++}; ++ + static int aspeed_adc_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +@@ -175,7 +201,10 @@ static int aspeed_adc_probe(struct platform_device *pdev) + const struct aspeed_adc_model_data *model_data; + struct resource *res; + const char *clk_parent_name; ++ char prescaler_clk_name[32]; ++ char scaler_clk_name[32]; + int ret; ++ u32 eng_ctrl = 0; + u32 adc_engine_control_reg_val; + + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*data)); +@@ -194,19 +223,21 @@ static int aspeed_adc_probe(struct platform_device *pdev) + spin_lock_init(&data->clk_lock); + clk_parent_name = of_clk_get_parent_name(pdev->dev.of_node, 0); + ++ snprintf(prescaler_clk_name, sizeof(prescaler_clk_name), "prescaler-%s", pdev->name); + data->clk_prescaler = clk_hw_register_divider( +- &pdev->dev, "prescaler", clk_parent_name, 0, ++ &pdev->dev, prescaler_clk_name, clk_parent_name, 0, + data->base + ASPEED_REG_CLOCK_CONTROL, + 17, 15, 0, &data->clk_lock); + if (IS_ERR(data->clk_prescaler)) + return PTR_ERR(data->clk_prescaler); + ++ snprintf(scaler_clk_name, sizeof(scaler_clk_name), "scaler-%s", pdev->name); + /* + * Register ADC clock scaler downstream from the prescaler. Allow rate + * setting to adjust the prescaler as well. + */ + data->clk_scaler = clk_hw_register_divider( +- &pdev->dev, "scaler", "prescaler", ++ &pdev->dev, scaler_clk_name, prescaler_clk_name, + CLK_SET_RATE_PARENT, + data->base + ASPEED_REG_CLOCK_CONTROL, + 0, 10, 0, &data->clk_lock); +@@ -215,7 +246,7 @@ static int aspeed_adc_probe(struct platform_device *pdev) + goto scaler_error; + } + +- data->rst = devm_reset_control_get_exclusive(&pdev->dev, NULL); ++ data->rst = devm_reset_control_get_shared(&pdev->dev, NULL); + if (IS_ERR(data->rst)) { + dev_err(&pdev->dev, + "invalid or missing reset controller device tree entry"); +@@ -225,11 +256,26 @@ static int aspeed_adc_probe(struct platform_device *pdev) + reset_control_deassert(data->rst); + + model_data = of_device_get_match_data(&pdev->dev); ++ if (!of_property_read_u32(pdev->dev.of_node, "ref_voltage", (u32 *)&model_data->vref_voltage)) { ++ if (model_data->vref_voltage == 2500) ++ eng_ctrl = REF_VLOTAGE_2500mV; ++ else if (model_data->vref_voltage == 1200) ++ eng_ctrl = REF_VLOTAGE_1200mV; ++ else if ((model_data->vref_voltage >= 1550) && (model_data->vref_voltage <= 2700)) ++ eng_ctrl = REF_VLOTAGE_1550mV; ++ else if ((model_data->vref_voltage >= 900) && (model_data->vref_voltage <= 1650)) ++ eng_ctrl = REF_VLOTAGE_900mV; ++ else { ++ printk("error ref voltage %d \n", model_data->vref_voltage); ++ eng_ctrl = 0; ++ } ++ } else ++ eng_ctrl = 0; + + if (model_data->wait_init_sequence) { + /* Enable engine in normal mode. */ +- writel(ASPEED_OPERATION_MODE_NORMAL | ASPEED_ENGINE_ENABLE, +- data->base + ASPEED_REG_ENGINE_CONTROL); ++ eng_ctrl |= ASPEED_OPERATION_MODE_NORMAL | ASPEED_ENGINE_ENABLE; ++ writel(eng_ctrl, data->base + ASPEED_REG_ENGINE_CONTROL); + + /* Wait for initial sequence complete. */ + ret = readl_poll_timeout(data->base + ASPEED_REG_ENGINE_CONTROL, +@@ -242,12 +288,26 @@ static int aspeed_adc_probe(struct platform_device *pdev) + goto poll_timeout_error; + } + ++ /* do compensating calculation use ch 0 */ ++ writel(eng_ctrl | ASPEED_OPERATION_MODE_NORMAL | ++ ASPEED_ENGINE_ENABLE | ASPEED_AUTOPENSATING, data->base + ASPEED_REG_ENGINE_CONTROL); ++ ++ writel(eng_ctrl | ASPEED_OPERATION_MODE_NORMAL | BIT(16) | ++ ASPEED_ENGINE_ENABLE | ASPEED_AUTOPENSATING, data->base + ASPEED_REG_ENGINE_CONTROL); ++ mdelay(1); ++ ++ data->cv = 0x200 - (readl(data->base + 0x10) & GENMASK(9, 0)); ++ ++ writel(eng_ctrl | ASPEED_OPERATION_MODE_NORMAL | ++ ASPEED_ENGINE_ENABLE | ASPEED_AUTOPENSATING, data->base + ASPEED_REG_ENGINE_CONTROL); ++ printk(KERN_INFO "aspeed_adc: cv %d \n", data->cv); ++ + /* Start all channels in normal mode. */ + ret = clk_prepare_enable(data->clk_scaler->clk); + if (ret) + goto clk_enable_error; + +- adc_engine_control_reg_val = GENMASK(31, 16) | ++ adc_engine_control_reg_val = eng_ctrl | GENMASK(31, 16) | + ASPEED_OPERATION_MODE_NORMAL | ASPEED_ENGINE_ENABLE; + writel(adc_engine_control_reg_val, + data->base + ASPEED_REG_ENGINE_CONTROL); +@@ -257,8 +317,8 @@ static int aspeed_adc_probe(struct platform_device *pdev) + indio_dev->dev.parent = &pdev->dev; + indio_dev->info = &aspeed_adc_iio_info; + indio_dev->modes = INDIO_DIRECT_MODE; +- indio_dev->channels = aspeed_adc_iio_channels; +- indio_dev->num_channels = ARRAY_SIZE(aspeed_adc_iio_channels); ++ indio_dev->channels = model_data->channels; ++ indio_dev->num_channels = model_data->num_channels; + + ret = iio_device_register(indio_dev); + if (ret) +@@ -301,6 +361,8 @@ static const struct aspeed_adc_model_data ast2400_model_data = { + .vref_voltage = 2500, // mV + .min_sampling_rate = 10000, + .max_sampling_rate = 500000, ++ .channels = aspeed_adc_iio_channels, ++ .num_channels = 16, + }; + + static const struct aspeed_adc_model_data ast2500_model_data = { +@@ -309,11 +371,24 @@ static const struct aspeed_adc_model_data ast2500_model_data = { + .min_sampling_rate = 1, + .max_sampling_rate = 1000000, + .wait_init_sequence = true, ++ .channels = aspeed_adc_iio_channels, ++ .num_channels = 16, ++}; ++ ++static const struct aspeed_adc_model_data ast2600_model_data = { ++ .model_name = "ast2500-adc", ++ .vref_voltage = 1800, /* mV --> can be 1.2v or 2.5 or ext 1.55~2.7v, 0.9v ~1.65v */ ++ .min_sampling_rate = 1, ++ .max_sampling_rate = 1000000, ++ .wait_init_sequence = true, ++ .channels = ast2600_adc_iio_channels, ++ .num_channels = 8, + }; + + static const struct of_device_id aspeed_adc_matches[] = { + { .compatible = "aspeed,ast2400-adc", .data = &ast2400_model_data }, + { .compatible = "aspeed,ast2500-adc", .data = &ast2500_model_data }, ++ { .compatible = "aspeed,ast2600-adc", .data = &ast2600_model_data }, + {}, + }; + MODULE_DEVICE_TABLE(of, aspeed_adc_matches); +@@ -330,5 +405,5 @@ static struct platform_driver aspeed_adc_driver = { + module_platform_driver(aspeed_adc_driver); + + MODULE_AUTHOR("Rick Altherr <raltherr@google.com>"); +-MODULE_DESCRIPTION("Aspeed AST2400/2500 ADC Driver"); ++MODULE_DESCRIPTION("Aspeed AST2400/2500/2600 ADC Driver"); + MODULE_LICENSE("GPL"); +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0086-ARM-dts-aspeed-g6-add-video-node.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0086-ARM-dts-aspeed-g6-add-video-node.patch new file mode 100644 index 000000000..221800720 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0086-ARM-dts-aspeed-g6-add-video-node.patch @@ -0,0 +1,36 @@ +From 90c72bbe5f1ac2c8d62736c44492bd8bbc4fcab0 Mon Sep 17 00:00:00 2001 +From: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +Date: Tue, 26 Nov 2019 16:37:36 -0800 +Subject: [PATCH] ARM: dts: aspeed-g6: add video node + +This commit adds video node. + +Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +--- + arch/arm/boot/dts/aspeed-g6.dtsi | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +diff --git a/arch/arm/boot/dts/aspeed-g6.dtsi b/arch/arm/boot/dts/aspeed-g6.dtsi +index 5acc1085526d..b0283e03e9ff 100644 +--- a/arch/arm/boot/dts/aspeed-g6.dtsi ++++ b/arch/arm/boot/dts/aspeed-g6.dtsi +@@ -359,6 +359,16 @@ + 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; +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0087-media-aspeed-add-aspeed-ast2600-video-engine-compati.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0087-media-aspeed-add-aspeed-ast2600-video-engine-compati.patch new file mode 100644 index 000000000..1d8b722de --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0087-media-aspeed-add-aspeed-ast2600-video-engine-compati.patch @@ -0,0 +1,63 @@ +From 1765cb18bacff9d1f48b833f44da198a09f7d5cb Mon Sep 17 00:00:00 2001 +From: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +Date: Tue, 26 Nov 2019 16:39:16 -0800 +Subject: [PATCH] media: aspeed: add aspeed,ast2600-video-engine compatible + string + +This commit adds "aspeed,ast2600-video-engine" compatible string +to support AST2600 video engine H/W. Also. it adds G6 specific +register handling flow. + +Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +--- + drivers/media/platform/aspeed-video.c | 13 ++++++++++--- + 1 file changed, 10 insertions(+), 3 deletions(-) + +diff --git a/drivers/media/platform/aspeed-video.c b/drivers/media/platform/aspeed-video.c +index db45502774b1..fe70ad408375 100644 +--- a/drivers/media/platform/aspeed-video.c ++++ b/drivers/media/platform/aspeed-video.c +@@ -72,8 +72,8 @@ + #define VE_SEQ_CTRL_CAP_BUSY BIT(16) + #define VE_SEQ_CTRL_COMP_BUSY BIT(18) + +-#ifdef CONFIG_MACH_ASPEED_G5 +-#define VE_SEQ_CTRL_JPEG_MODE BIT(13) /* AST2500 */ ++#if defined(CONFIG_MACH_ASPEED_G5) || defined(CONFIG_MACH_ASPEED_G6) ++#define VE_SEQ_CTRL_JPEG_MODE BIT(13) /* AST2500/2600 */ + #else + #define VE_SEQ_CTRL_JPEG_MODE BIT(8) /* AST2400 */ + #endif /* CONFIG_MACH_ASPEED_G5 */ +@@ -135,6 +135,12 @@ + + #define VE_OFFSET_COMP_STREAM 0x078 + ++#ifdef CONFIG_MACH_ASPEED_G6 ++#define VE_JPEG_COMP_SIZE_READ_BACK 0x084 /* AST2600 */ ++#else ++#define VE_JPEG_COMP_SIZE_READ_BACK VE_OFFSET_COMP_STREAM ++#endif ++ + #define VE_SRC_LR_EDGE_DET 0x090 + #define VE_SRC_LR_EDGE_DET_LEFT GENMASK(11, 0) + #define VE_SRC_LR_EDGE_DET_NO_V BIT(12) +@@ -572,7 +578,7 @@ static irqreturn_t aspeed_video_irq(int irq, void *arg) + if (sts & VE_INTERRUPT_COMP_COMPLETE) { + struct aspeed_video_buffer *buf; + u32 frame_size = aspeed_video_read(video, +- VE_OFFSET_COMP_STREAM); ++ VE_JPEG_COMP_SIZE_READ_BACK); + + spin_lock(&video->lock); + clear_bit(VIDEO_FRAME_INPRG, &video->flags); +@@ -1718,6 +1724,7 @@ static int aspeed_video_remove(struct platform_device *pdev) + static const struct of_device_id aspeed_video_of_match[] = { + { .compatible = "aspeed,ast2400-video-engine" }, + { .compatible = "aspeed,ast2500-video-engine" }, ++ { .compatible = "aspeed,ast2600-video-engine" }, + {} + }; + MODULE_DEVICE_TABLE(of, aspeed_video_of_match); +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0088-clk-ast2600-enable-ESPICLK-always.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0088-clk-ast2600-enable-ESPICLK-always.patch new file mode 100644 index 000000000..34df0882b --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0088-clk-ast2600-enable-ESPICLK-always.patch @@ -0,0 +1,30 @@ +From 37efef00064a228c3e723b0eece22d72f2632705 Mon Sep 17 00:00:00 2001 +From: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +Date: Fri, 6 Dec 2019 13:51:37 -0800 +Subject: [PATCH] clk: ast2600: enable ESPICLK always + +To support continous eSPI H/W handshaking, this patch enables +ESPICLK always so that there discontinuity of eSPI handshaking while +boot BMC. + +Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +--- + drivers/clk/clk-ast2600.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/clk/clk-ast2600.c b/drivers/clk/clk-ast2600.c +index 8201d65..21967bc 100644 +--- a/drivers/clk/clk-ast2600.c ++++ b/drivers/clk/clk-ast2600.c +@@ -87,7 +87,7 @@ static const struct aspeed_gate_data aspeed_g6_gates[] = { + [ASPEED_CLK_GATE_EMMCCLK] = { 27, 16, "emmcclk-gate", NULL, 0 }, /* For card clk */ + /* Reserved 28/29/30 */ + [ASPEED_CLK_GATE_LCLK] = { 32, 32, "lclk-gate", NULL, 0 }, /* LPC */ +- [ASPEED_CLK_GATE_ESPICLK] = { 33, -1, "espiclk-gate", NULL, 0 }, /* eSPI */ ++ [ASPEED_CLK_GATE_ESPICLK] = { 33, -1, "espiclk-gate", NULL, CLK_IS_CRITICAL }, /* eSPI */ + [ASPEED_CLK_GATE_REF1CLK] = { 34, -1, "ref1clk-gate", "clkin", CLK_IS_CRITICAL }, + /* Reserved 35 */ + [ASPEED_CLK_GATE_SDCLK] = { 36, 56, "sdclk-gate", NULL, 0 }, /* SDIO/SD */ +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0089-ast2600-enable-high-speed-uart-in-kernel.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0089-ast2600-enable-high-speed-uart-in-kernel.patch new file mode 100644 index 000000000..962d42e6f --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0089-ast2600-enable-high-speed-uart-in-kernel.patch @@ -0,0 +1,97 @@ +From 054391014d04e5b97ae62e1bf5e6aed005f3c67a Mon Sep 17 00:00:00 2001 +From: Kuiying Wang <kuiying.wang@intel.com> +Date: Fri, 13 Dec 2019 16:15:16 +0800 +Subject: [PATCH] ast2600: enable high speed uart in kernel. + +Tested: +1. Config baud rate to 921600 in BIOS setup page +2. BMC could change env variable "hostserialcfg" to 1. +3. BMC is force to reboot and SPA baud rate is changed to 921600 successfully. +4. It is same for back to 115200. + +Signed-off-by: Kuiying Wang <kuiying.wang@intel.com> +--- + drivers/clk/clk-ast2600.c | 26 +++++++++++++++++++------- + include/dt-bindings/clock/ast2600-clock.h | 2 ++ + 2 files changed, 21 insertions(+), 7 deletions(-) + +diff --git a/drivers/clk/clk-ast2600.c b/drivers/clk/clk-ast2600.c +index c7236e39ef85..af908b2dbeb6 100644 +--- a/drivers/clk/clk-ast2600.c ++++ b/drivers/clk/clk-ast2600.c +@@ -15,7 +15,7 @@ + + #include "clk-aspeed.h" + +-#define ASPEED_G6_NUM_CLKS 71 ++#define ASPEED_G6_NUM_CLKS ASPEED_CLK_MAX + + #define ASPEED_G6_SILICON_REV 0x004 + +@@ -43,6 +43,9 @@ + #define ASPEED_MAC12_CLK_DLY 0x340 + #define ASPEED_MAC34_CLK_DLY 0x350 + ++#define ASPEED_G6_GEN_UART_REF 0x338 ++#define UART_192MHZ_R_N_VALUE 0x3c38e ++ + /* Globally visible clocks */ + static DEFINE_SPINLOCK(aspeed_g6_clk_lock); + +@@ -76,7 +79,7 @@ static const struct aspeed_gate_data aspeed_g6_gates[] = { + /* Reserved 11/12 */ + [ASPEED_CLK_GATE_YCLK] = { 13, 4, "yclk-gate", NULL, 0 }, /* HAC */ + [ASPEED_CLK_GATE_USBPORT1CLK] = { 14, 14, "usb-port1-gate", NULL, 0 }, /* USB2 hub/USB2 host port 1/USB1.1 dev */ +- [ASPEED_CLK_GATE_UART5CLK] = { 15, -1, "uart5clk-gate", "uart", 0 }, /* UART5 */ ++ [ASPEED_CLK_GATE_UART5CLK] = { 15, -1, "uart5clk-gate", "uart5", 0 }, /* UART5 */ + /* Reserved 16/19 */ + [ASPEED_CLK_GATE_MAC1CLK] = { 20, 11, "mac1clk-gate", "mac12", 0 }, /* MAC1 */ + [ASPEED_CLK_GATE_MAC2CLK] = { 21, 12, "mac2clk-gate", "mac12", 0 }, /* MAC2 */ +@@ -437,17 +440,26 @@ static int aspeed_g6_clk_probe(struct platform_device *pdev) + return ret; + } + +- /* UART clock div13 setting */ +- regmap_read(map, ASPEED_G6_MISC_CTRL, &val); +- if (val & UART_DIV13_EN) +- rate = 24000000 / 13; ++ /* UART clock setting */ ++ regmap_read(map, ASPEED_G6_GEN_UART_REF, &val); ++ if (val == UART_192MHZ_R_N_VALUE){ ++ rate = 192000000 / 13; ++ dev_err(dev, "192Mhz baud rate 921600\n"); ++ } + else +- rate = 24000000; ++ rate = 24000000 / 13; + hw = clk_hw_register_fixed_rate(dev, "uart", NULL, 0, rate); + if (IS_ERR(hw)) + return PTR_ERR(hw); + aspeed_g6_clk_data->hws[ASPEED_CLK_UART] = hw; + ++ /* UART5 clock setting */ ++ rate = 24000000 / 13; ++ hw = clk_hw_register_fixed_rate(dev, "uart5", NULL, 0, rate); ++ if (IS_ERR(hw)) ++ return PTR_ERR(hw); ++ aspeed_g6_clk_data->hws[ASPEED_CLK_UART5] = hw; ++ + /* UART6~13 clock div13 setting */ + regmap_read(map, 0x80, &val); + if (val & BIT(31)) +diff --git a/include/dt-bindings/clock/ast2600-clock.h b/include/dt-bindings/clock/ast2600-clock.h +index 3d90582a813f..4952f7683be7 100644 +--- a/include/dt-bindings/clock/ast2600-clock.h ++++ b/include/dt-bindings/clock/ast2600-clock.h +@@ -87,6 +87,8 @@ + #define ASPEED_CLK_MAC2RCLK 68 + #define ASPEED_CLK_MAC3RCLK 69 + #define ASPEED_CLK_MAC4RCLK 70 ++#define ASPEED_CLK_UART5 71 ++#define ASPEED_CLK_MAX 72 + + /* Only list resets here that are not part of a gate */ + #define ASPEED_RESET_ADC 55 +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0090-peci-cpupower-driver-1.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0090-peci-cpupower-driver-1.patch new file mode 100644 index 000000000..475e9611f --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0090-peci-cpupower-driver-1.patch @@ -0,0 +1,405 @@ +From e40f7d10d5306c075e2fc7e538ce1efc70eeb448 Mon Sep 17 00:00:00 2001 +From: ZhikuiRen <zhikui.ren@intel.com> +Date: Thu, 9 Jan 2020 10:48:00 -0800 +Subject: [PATCH] Add peci-cpupower driver + +peci-cpupower reads CPU energy counter through peci +and computes average power in mW since last read. + +Signed-off-by: ZhikuiRen <zhikui.ren@intel.com> +--- + Documentation/hwmon/index.rst | 1 + + Documentation/hwmon/peci-cpupower.rst | 52 ++++++++ + arch/arm/configs/aspeed_g5_defconfig | 1 + + drivers/hwmon/Kconfig | 14 +++ + drivers/hwmon/Makefile | 1 + + drivers/hwmon/peci-cpupower.c | 231 ++++++++++++++++++++++++++++++++++ + drivers/mfd/intel-peci-client.c | 1 + + include/uapi/linux/peci-ioctl.h | 1 + + 8 files changed, 302 insertions(+) + create mode 100644 Documentation/hwmon/peci-cpupower.rst + create mode 100644 drivers/hwmon/peci-cpupower.c + +diff --git a/Documentation/hwmon/index.rst b/Documentation/hwmon/index.rst +index 7d894c9..0bde0ef 100644 +--- a/Documentation/hwmon/index.rst ++++ b/Documentation/hwmon/index.rst +@@ -130,6 +130,7 @@ Hardware Monitoring Kernel Drivers + pcf8591 + peci-cputemp + peci-dimmtemp ++ peci-cpupower + pmbus + powr1220 + pxe1610 +diff --git a/Documentation/hwmon/peci-cpupower.rst b/Documentation/hwmon/peci-cpupower.rst +new file mode 100644 +index 0000000..4d7bd61 +--- /dev/null ++++ b/Documentation/hwmon/peci-cpupower.rst +@@ -0,0 +1,52 @@ ++.. SPDX-License-Identifier: GPL-2.0 ++ ++Kernel driver peci-cpupower ++========================== ++ ++:Copyright: |copy| 2018-2020 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: ++ Zhikui Ren <zhikui.ren@.intel.com> ++ ++Description ++----------- ++ ++This driver implements a generic PECI hwmon feature which provides ++average power consumption readings of the CPU package based on energy counter ++accessible using the PECI Client Command Suite via the processor PECI client. ++ ++Power values are average power since last measure given in milli Watt and ++will be measurable only when the target CPU is powered on. ++ ++``sysfs`` interface ++------------------- ++======================= ======================================================= ++power1_average Provides average power since last read in milli Watt. ++power1_label Provides string "Average Power". ++======================= ======================================================= +diff --git a/arch/arm/configs/aspeed_g5_defconfig b/arch/arm/configs/aspeed_g5_defconfig +index 1e589a0..0e50040 100644 +--- a/arch/arm/configs/aspeed_g5_defconfig ++++ b/arch/arm/configs/aspeed_g5_defconfig +@@ -177,6 +177,7 @@ CONFIG_SENSORS_OCC_P8_I2C=y + CONFIG_SENSORS_OCC_P9_SBE=y + CONFIG_SENSORS_PECI_CPUTEMP=y + CONFIG_SENSORS_PECI_DIMMTEMP=y ++CONFIG_SENSORS_PECI_CPUPOWER=y + CONFIG_PMBUS=y + CONFIG_SENSORS_ADM1275=y + CONFIG_SENSORS_IBM_CFFPS=y +diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig +index 8312b37..07d8826 100644 +--- a/drivers/hwmon/Kconfig ++++ b/drivers/hwmon/Kconfig +@@ -1361,6 +1361,20 @@ config SENSORS_PECI_DIMMTEMP + This driver can also be built as a module. If so, the module + will be called peci-dimmtemp. + ++config SENSORS_PECI_CPUPOWER ++ tristate "PECI CPU power monitoring support" ++ 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 average engergy ++ readings of the CPU package using ++ the PECI Client Command Suite via the processor PECI client. ++ Check Documentation/hwmon/peci-cpupower for details. ++ ++ This driver can also be built as a module. If so, the module ++ will be called peci-cpupower. ++ + source "drivers/hwmon/pmbus/Kconfig" + + config SENSORS_PWM_FAN +diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile +index e74ea92..fab43fd 100644 +--- a/drivers/hwmon/Makefile ++++ b/drivers/hwmon/Makefile +@@ -144,6 +144,7 @@ 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_PECI_CPUPOWER) += peci-cpupower.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/peci-cpupower.c b/drivers/hwmon/peci-cpupower.c +new file mode 100644 +index 0000000..6907696 +--- /dev/null ++++ b/drivers/hwmon/peci-cpupower.c +@@ -0,0 +1,231 @@ ++// SPDX-License-Identifier: GPL-2.0 ++// Copyright (c) 2018-2020 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 POWER_DEFAULT_CHANNEL_NUMS 1 ++ ++struct peci_cpupower { ++ struct peci_client_manager *mgr; ++ struct device *dev; ++ char name[PECI_NAME_SIZE]; ++ const struct cpu_gen_info *gen_info; ++ struct peci_sensor_data energy; ++ long avg_power_val; ++ u64 core_mask; ++ u32 power_config[POWER_DEFAULT_CHANNEL_NUMS + 1]; ++ uint config_idx; ++ struct hwmon_channel_info power_info; ++ const struct hwmon_channel_info *info[2]; ++ struct hwmon_chip_info chip; ++}; ++ ++enum cpupower_channels { ++ average_power, ++}; ++ ++static const u32 config_table[POWER_DEFAULT_CHANNEL_NUMS] = { ++ /* average power */ ++ HWMON_P_LABEL | HWMON_P_AVERAGE, ++}; ++ ++static const char *cpupower_label[POWER_DEFAULT_CHANNEL_NUMS] = { ++ "Average Power", ++}; ++ ++static int get_average_power(struct peci_cpupower *priv) ++{ ++ u8 pkg_cfg[4]; ++ int ret; ++ ++ if (!peci_sensor_need_update(&priv->energy)) ++ return 0; ++ ++ ret = peci_client_read_package_config(priv->mgr, ++ PECI_MBX_INDEX_TDP_UNITS, ++ PECI_PKG_ID_PKG_ENERGY_STATUS, ++ pkg_cfg); ++ ++ u32 power_unit = ((le32_to_cpup((__le32 *)pkg_cfg)) & 0x1f00) >> 8; ++ ++ dev_dbg(priv->dev, "cpupower units %d (1J/pow(2, unit))\n", ++ power_unit); ++ ++ ret = peci_client_read_package_config(priv->mgr, ++ PECI_MBX_INDEX_ENERGY_COUNTER, ++ PECI_PKG_ID_PKG_ENERGY_STATUS, ++ pkg_cfg); ++ if (!ret) { ++ u32 energy_cnt = le32_to_cpup((__le32 *)pkg_cfg); ++ ulong jif = jiffies; ++ ulong elapsed = (jif - priv->energy.last_updated); ++ long power_val = 0; ++ /* ++ * Don't calculate average power for first counter read or ++ * counter wrapped around or last counter read was more than ++ * 60 minutes ago (jiffies did not wrap and power calculation ++ * does not overflow or underflow ++ */ ++ if (priv->energy.last_updated > 0 && ++ energy_cnt > priv->energy.value && ++ (elapsed < (HZ * 3600))) { ++ power_val = (long)(energy_cnt - priv->energy.value) ++ / elapsed * HZ; ++ dev_dbg(priv->dev, "countDiff %d, jiffes elapsed %d, raw powerValue %d scale to %d mW\n", ++ (long)(energy_cnt - priv->energy.value), ++ elapsed, power_val, ++ power_val >> (power_unit - 10)); ++ } else { ++ dev_dbg(priv->dev, "countDiff %d, jiffes elapsed %d, skipping calculate power, try agin\n", ++ (long)(energy_cnt - priv->energy.value), ++ elapsed); ++ ret = -EAGAIN; ++ } ++ ++ priv->energy.value = energy_cnt; ++ priv->avg_power_val = power_val >> ((power_unit - 10)); ++ peci_sensor_mark_updated(&priv->energy); ++ ++ dev_dbg(priv->dev, "energy counter 0x%8x, average power %dmW, jif %u, HZ is %d jiffies\n", ++ priv->energy.value, priv->avg_power_val, ++ jif, HZ); ++ } ++ return ret; ++} ++ ++static int cpupower_read_string(struct device *dev, ++ enum hwmon_sensor_types type, ++ u32 attr, int channel, const char **str) ++{ ++ if (attr != hwmon_power_label) ++ return -EOPNOTSUPP; ++ if (channel >= POWER_DEFAULT_CHANNEL_NUMS) ++ return -EOPNOTSUPP; ++ *str = cpupower_label[channel]; ++ ++ return 0; ++} ++ ++static int cpupower_read(struct device *dev, ++ enum hwmon_sensor_types type, ++ u32 attr, int channel, long *val) ++{ ++ struct peci_cpupower *priv = dev_get_drvdata(dev); ++ int ret; ++ ++ if (channel >= POWER_DEFAULT_CHANNEL_NUMS || ++ !(priv->power_config[channel] & BIT(attr))) ++ return -EOPNOTSUPP; ++ ++ switch (attr) { ++ case hwmon_power_average: ++ switch (channel) { ++ case average_power: ++ ret = get_average_power(priv); ++ if (ret) ++ break; ++ ++ *val = priv->avg_power_val; ++ break; ++ default: ++ break; ++ } ++ break; ++ default: ++ ret = -EOPNOTSUPP; ++ break; ++ } ++ ++ return ret; ++} ++ ++static umode_t cpupower_is_visible(const void *data, ++ enum hwmon_sensor_types type, ++ u32 attr, int channel) ++{ ++ const struct peci_cpupower *priv = data; ++ ++ if (channel < POWER_DEFAULT_CHANNEL_NUMS || ++ (priv->power_config[channel] & BIT(attr))) ++ return 0444; ++ ++ return 0; ++} ++ ++static const struct hwmon_ops cpupower_ops = { ++ .is_visible = cpupower_is_visible, ++ .read_string = cpupower_read_string, ++ .read = cpupower_read, ++}; ++ ++static int peci_cpupower_probe(struct platform_device *pdev) ++{ ++ struct peci_client_manager *mgr = dev_get_drvdata(pdev->dev.parent); ++ struct device *dev = &pdev->dev; ++ struct peci_cpupower *priv; ++ struct device *hwmon_dev; ++ ++ if ((mgr->client->adapter->cmd_mask & ++ (BIT(PECI_CMD_RD_PKG_CFG))) != ++ (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_cpupower.cpu%d", ++ mgr->client->addr - PECI_BASE_ADDR); ++ ++ priv->power_config[priv->config_idx++] = config_table[average_power]; ++ ++ priv->chip.ops = &cpupower_ops; ++ priv->chip.info = priv->info; ++ ++ priv->info[0] = &priv->power_info; ++ ++ priv->power_info.type = hwmon_power; ++ priv->power_info.config = priv->power_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_cpupower_ids[] = { ++ { .name = "peci-cpupower", .driver_data = 0 }, ++ { } ++}; ++MODULE_DEVICE_TABLE(platform, peci_cpupower_ids); ++ ++static struct platform_driver peci_cpupower_driver = { ++ .probe = peci_cpupower_probe, ++ .id_table = peci_cpupower_ids, ++ .driver = { .name = KBUILD_MODNAME, }, ++}; ++module_platform_driver(peci_cpupower_driver); ++ ++MODULE_AUTHOR("Zhikui Ren <zhikui.ren@intel.com>"); ++MODULE_DESCRIPTION("PECI cpupower driver"); ++MODULE_LICENSE("GPL v2"); +diff --git a/drivers/mfd/intel-peci-client.c b/drivers/mfd/intel-peci-client.c +index 9751b04..0c62ad6 100644 +--- a/drivers/mfd/intel-peci-client.c ++++ b/drivers/mfd/intel-peci-client.c +@@ -21,6 +21,7 @@ + static struct mfd_cell peci_functions[] = { + { .name = "peci-cputemp", }, + { .name = "peci-dimmtemp", }, ++ { .name = "peci-cpupower", }, + }; + + static const struct cpu_gen_info cpu_gen_info_table[] = { +diff --git a/include/uapi/linux/peci-ioctl.h b/include/uapi/linux/peci-ioctl.h +index 843930f..d16f7c9 100644 +--- a/include/uapi/linux/peci-ioctl.h ++++ b/include/uapi/linux/peci-ioctl.h +@@ -231,6 +231,7 @@ struct peci_rd_pkg_cfg_msg { + #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 */ ++#define PECI_PKG_ID_PKG_ENERGY_STATUS 0x00ff /* Average Energy */ + + __u8 rx_len; + __u8 cc; +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0092-SPI-Quad-IO-driver-support-AST2600.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0092-SPI-Quad-IO-driver-support-AST2600.patch new file mode 100644 index 000000000..2717d367f --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0092-SPI-Quad-IO-driver-support-AST2600.patch @@ -0,0 +1,136 @@ +From 9f1eea1fb639a0554195e29c488cefb36a04910e Mon Sep 17 00:00:00 2001 +From: arun-pm <arun.p.m@linux.intel.com> +Date: Tue, 3 Dec 2019 17:22:28 +0530 +Subject: [PATCH] SPI Quad IO driver support AST2600 + +This commit adds spi driver quad io support for AST2600 + +Note:- Removed n25q00 Quad I/O support for the time being due to clock issue + with chip 'Micron 8UA15 - rw182 (128MB)' while enabling Quad I/O mode. + +Signed-off-by: arun-pm <arun.p.m@linux.intel.com> +--- + drivers/mtd/spi-nor/aspeed-smc.c | 49 ++++++++++++++++++++++++++++++++++------ + drivers/mtd/spi-nor/spi-nor.c | 7 +++++- + 2 files changed, 48 insertions(+), 8 deletions(-) + +diff --git a/drivers/mtd/spi-nor/aspeed-smc.c b/drivers/mtd/spi-nor/aspeed-smc.c +index 0805dcab8cb1..305c1940e822 100644 +--- a/drivers/mtd/spi-nor/aspeed-smc.c ++++ b/drivers/mtd/spi-nor/aspeed-smc.c +@@ -21,6 +21,11 @@ + #include <linux/sysfs.h> + + #define DEVICE_NAME "aspeed-smc" ++#define AST2600A0 0x05000303 ++#define AST2600A0_MAX_FREQ 50000000 ++#define AST2600A0_SAFE_FREQ 40000000 ++#define AST_MAX_FREQ 100000000 ++#define AST2600_REVISION_ID_SCU 0x1e6e2004 + + /* + * The driver only support SPI flash +@@ -542,6 +547,10 @@ static int aspeed_smc_get_io_mode(struct aspeed_smc_chip *chip) + return CONTROL_IO_DUAL_DATA; + case SNOR_PROTO_1_2_2: + return CONTROL_IO_DUAL_ADDR_DATA; ++ case SNOR_PROTO_1_1_4: ++ return CONTROL_IO_QUAD_DATA; ++ case SNOR_PROTO_1_4_4: ++ return CONTROL_IO_QUAD_ADDR_DATA; + default: + dev_err(chip->nor.dev, "unsupported SPI read mode\n"); + return -EINVAL; +@@ -573,7 +582,7 @@ static ssize_t aspeed_smc_read_user(struct spi_nor *nor, loff_t from, + aspeed_smc_write_to_ahb(chip->ahb_base, &dummy, sizeof(dummy)); + + /* Set IO mode only for data */ +- if (io_mode == CONTROL_IO_DUAL_DATA) ++ if (io_mode == CONTROL_IO_DUAL_DATA || io_mode == CONTROL_IO_QUAD_DATA) + aspeed_smc_set_io_mode(chip, io_mode); + + aspeed_smc_read_from_ahb(read_buf, chip->ahb_base, len); +@@ -1222,20 +1231,35 @@ static int aspeed_smc_chip_setup_finish(struct aspeed_smc_chip *chip) + return 0; + } + ++static void aspeed_allowed_max_freq(struct aspeed_smc_chip *chip) ++{ ++ void __iomem *scu_ast_revision_id = ioremap(AST2600_REVISION_ID_SCU, 4); ++ u32 rev_id = readl(scu_ast_revision_id); ++ ++ /*Limit max spi frequency less than 50MHz on AST2600-A0 due ++ * to FWSPICLK signal quality issue. ++ */ ++ if(rev_id == AST2600A0 && chip->clk_rate >= AST2600A0_MAX_FREQ) ++ chip->clk_rate = AST2600A0_SAFE_FREQ; ++} ++ ++static u32 get_hwcaps(unsigned int tx_width){ ++ if(tx_width == 4) ++ return SNOR_HWCAPS_READ_1_1_4; ++ else ++ return SNOR_HWCAPS_READ_1_1_2; ++} ++ + static int aspeed_smc_setup_flash(struct aspeed_smc_controller *controller, + struct device_node *np, struct resource *r) + { +- const struct spi_nor_hwcaps hwcaps = { +- .mask = SNOR_HWCAPS_READ | +- SNOR_HWCAPS_READ_FAST | +- SNOR_HWCAPS_READ_1_1_2 | +- SNOR_HWCAPS_PP, +- }; ++ struct spi_nor_hwcaps hwcaps; + const struct aspeed_smc_info *info = controller->info; + struct device *dev = controller->dev; + struct device_node *child; + unsigned int cs; + int ret = -ENODEV; ++ unsigned int spi_tx_width; + + for_each_available_child_of_node(np, child) { + struct aspeed_smc_chip *chip; +@@ -1276,9 +1300,20 @@ static int aspeed_smc_setup_flash(struct aspeed_smc_controller *controller, + &chip->clk_rate)) { + chip->clk_rate = ASPEED_SPI_DEFAULT_FREQ; + } ++ aspeed_allowed_max_freq(chip); + dev_info(dev, "Using %d MHz SPI frequency\n", + chip->clk_rate / 1000000); + ++ if (of_property_read_u32(child, "spi-tx-bus-width", ++ &spi_tx_width)) { ++ spi_tx_width = 2; ++ } ++ dev_info(dev, "tx width: %ld\n", spi_tx_width); ++ ++ hwcaps.mask = SNOR_HWCAPS_READ | ++ SNOR_HWCAPS_READ_FAST | ++ get_hwcaps(spi_tx_width) | ++ SNOR_HWCAPS_PP; + chip->controller = controller; + chip->ctl = controller->regs + info->ctl0 + cs * 4; + chip->cs = cs; +diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c +index 3668a862d37d..50904876cb09 100644 +--- a/drivers/mtd/spi-nor/spi-nor.c ++++ b/drivers/mtd/spi-nor/spi-nor.c +@@ -2315,7 +2315,12 @@ static const struct flash_info spi_nor_ids[] = { + SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) }, + { "n25q512a", INFO(0x20bb20, 0, 64 * 1024, 1024, SECT_4K | + SPI_NOR_QUAD_READ) }, +- { "n25q00", INFO(0x20ba21, 0, 64 * 1024, 2048, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ | NO_CHIP_ERASE) }, ++ /* Removed n25q00 Quad I/O support for the time being due to clock issue with chip 'Micron 8UA15 - rw182 (128MB)' ++ * while enabling Quad I/O mode. As this chip is default shipped in platforms, marking it ++ * as Not supported for the time being. Once all chips are replaced with the new model, this can be enabled ++ * back(Note:- Certain other chips having same name(n25q00) but different part number has no issues). ++ */ ++ { "n25q00", INFO(0x20ba21, 0, 64 * 1024, 2048, SECT_4K | USE_FSR | 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 | +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0093-ipmi-ipmb_dev_int-add-quick-fix-for-raw-I2C-type-reg.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0093-ipmi-ipmb_dev_int-add-quick-fix-for-raw-I2C-type-reg.patch new file mode 100644 index 000000000..4b3b6b838 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0093-ipmi-ipmb_dev_int-add-quick-fix-for-raw-I2C-type-reg.patch @@ -0,0 +1,37 @@ +From 8be7c34e78bd09fa95ce9972332c8c2bb950ec03 Mon Sep 17 00:00:00 2001 +From: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +Date: Tue, 28 Jan 2020 16:14:07 -0800 +Subject: [PATCH] ipmi: ipmb_dev_int: add quick fix for raw I2C type + registration + +This commit adds quick fix for cases of run time registration +through sysfs. It should be fixed later by adding parsing code for +ipmb_id instead. + +This is a temporary fix. Do not upstream it. + +Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +--- + drivers/char/ipmi/ipmb_dev_int.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/drivers/char/ipmi/ipmb_dev_int.c b/drivers/char/ipmi/ipmb_dev_int.c +index 382e4a1df510..c5db37266724 100644 +--- a/drivers/char/ipmi/ipmb_dev_int.c ++++ b/drivers/char/ipmi/ipmb_dev_int.c +@@ -328,8 +328,12 @@ static int ipmb_probe(struct i2c_client *client, + if (ret) + return ret; + ++#if 1 /* FIXME: Quick fix. Need to add parsing code for ipmb_id instead */ ++ ipmb_dev->is_i2c_protocol = true; ++#else + ipmb_dev->is_i2c_protocol + = device_property_read_bool(&client->dev, "i2c-protocol"); ++#endif + + ipmb_dev->client = client; + i2c_set_clientdata(client, ipmb_dev); +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0094-Return-link-speed-and-duplex-settings-for-the-NCSI-c.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0094-Return-link-speed-and-duplex-settings-for-the-NCSI-c.patch new file mode 100644 index 000000000..3e426edcd --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0094-Return-link-speed-and-duplex-settings-for-the-NCSI-c.patch @@ -0,0 +1,57 @@ +From f280c15c79e7d13e4a59d413a3091eb21b56dd92 Mon Sep 17 00:00:00 2001 +From: Johnathan Mantey <johnathanx.mantey@intel.com> +Date: Mon, 27 Jan 2020 09:03:56 -0800 +Subject: [PATCH] Return link speed and duplex settings for the NCSI channel + +The ftgmac100_open function initializes state for the NCSI +channel. The get link settings function does not return this +data. This caused the link speed, and the duplex value to be returned +incorrectly by the PHY driver (0 Mbps, and duplex off). + +Update the driver to return either the PHY settings when not using +NCSI, or the NCSI values that were assigned when the driver is opened. + +Signed-off-by: Johnathan Mantey <johnathanx.mantey@intel.com> +--- + drivers/net/ethernet/faraday/ftgmac100.c | 22 +++++++++++++++++++++- + 1 file changed, 21 insertions(+), 1 deletion(-) + +diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/faraday/ftgmac100.c +index 8ed85037f021..a53878eecfc8 100644 +--- a/drivers/net/ethernet/faraday/ftgmac100.c ++++ b/drivers/net/ethernet/faraday/ftgmac100.c +@@ -1218,10 +1218,30 @@ static int ftgmac100_set_pauseparam(struct net_device *netdev, + return 0; + } + ++int ftgmac100_ethtool_get_link_ksettings(struct net_device *netdev, ++ struct ethtool_link_ksettings *cmd) ++{ ++ struct phy_device *phydev = netdev->phydev; ++ struct ftgmac100 *priv = netdev_priv(netdev); ++ int retval = 0; ++ ++ if (phydev) { ++ phy_ethtool_ksettings_get(phydev, cmd); ++ } else if (priv->use_ncsi) { ++ cmd->base.speed = priv->cur_speed; ++ cmd->base.duplex = priv->cur_duplex; ++ cmd->base.autoneg = 0; ++ } else { ++ retval = -ENODEV; ++ } ++ ++ return retval; ++} ++ + static const struct ethtool_ops ftgmac100_ethtool_ops = { + .get_drvinfo = ftgmac100_get_drvinfo, + .get_link = ethtool_op_get_link, +- .get_link_ksettings = phy_ethtool_get_link_ksettings, ++ .get_link_ksettings = ftgmac100_ethtool_get_link_ksettings, + .set_link_ksettings = phy_ethtool_set_link_ksettings, + .nway_reset = phy_ethtool_nway_reset, + .get_ringparam = ftgmac100_get_ringparam, +-- +2.24.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0095-pwm-and-tach-driver-changes-for-ast2600.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0095-pwm-and-tach-driver-changes-for-ast2600.patch new file mode 100644 index 000000000..377ec2b36 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0095-pwm-and-tach-driver-changes-for-ast2600.patch @@ -0,0 +1,783 @@ +From 0301f9b2bc948069aaad2899e15e6c94a07ca818 Mon Sep 17 00:00:00 2001 +From: Ayushi Smriti <smriti.ayushi@intel.com> +Date: Mon, 2 Mar 2020 22:51:46 +0530 +Subject: [PATCH] pwm and tach driver changes for ast2600 + +This commit includes additions/changes in driver code +to support 9 PWM outputs and 9 fan tachometer inputs for +ast2600 + +Signed-off-by: Ayushi Smriti <smriti.ayushi@intel.com> +--- + drivers/hwmon/aspeed-g6-pwm-tacho.c | 328 +++++++++++++++------------- + 1 file changed, 171 insertions(+), 157 deletions(-) + +diff --git a/drivers/hwmon/aspeed-g6-pwm-tacho.c b/drivers/hwmon/aspeed-g6-pwm-tacho.c +index 1894f6ad5edb..1ff788bf1133 100644 +--- a/drivers/hwmon/aspeed-g6-pwm-tacho.c ++++ b/drivers/hwmon/aspeed-g6-pwm-tacho.c +@@ -64,6 +64,8 @@ + #define PWM_RISING_FALLING_BIT (8) //pwm falling point bit [7:0] + #define PWM_RISING_RISING_BIT (0) //pwm rising point bit [7:0] + ++#define DEF_PWM_PERIOD 0xff ++ + #define ASPEED_TACHO_CTRL 0x08 //TACH0 General Register + #define ASPEED_TACHO_CTRL_CH(x) ((x * 0x10) + 0x08) + #define TACHO_IER BIT(31) //enable tacho interrupt +@@ -72,8 +74,7 @@ + #define TACHO_ENABLE BIT(28) //{enable tacho} + #define TACHO_DEBOUNCE_BIT (26) //{tacho de-bounce} + #define TACHO_DEBOUNCE_MASK (0x3 << 26) //{tacho de-bounce} +-#define TECHIO_EDGE_MASK (0x3 << 24) //tacho edge} +-#define TECHIO_EDGE_BIT (24) //tacho edge} ++#define TACHIO_EDGE_BIT (24) /*tacho edge}*/ + #define TACHO_CLK_DIV_T_MASK (0xf << 20) + #define TACHO_CLK_DIV_BIT (20) + #define TACHO_THRESHOLD_MASK (0xfffff) //tacho threshold bit +@@ -102,211 +103,183 @@ + + #define MAX_CDEV_NAME_LEN 16 + ++#define DEFAULT_TARGET_PWM_FREQ 25000 ++#define DEFAULT_MIN_RPM 2900 ++ ++#define PWM_FALLING_VALUE 0xff ++ + struct aspeed_pwm_channel_params { ++ int target_freq; ++ int pwm_freq; + int load_wdt_rising_falling_pt; + int load_wdt_selection; //0: rising , 1: falling + int load_wdt_enable; + int duty_sync_enable; + int invert_pin; +- u8 divide_h; +- u8 divide_l; +- u8 period; + u8 rising; + u8 falling; + }; + + static struct aspeed_pwm_channel_params default_pwm_params[] = { + [0] = { ++ .target_freq = 25000, + .load_wdt_rising_falling_pt = 0x10, + .load_wdt_selection = 0, + .load_wdt_enable = 1, + .duty_sync_enable = 0, + .invert_pin = 0, +- .divide_h = 0x5, +- .divide_l = 0x6, +- .period = 0x13, //5% ~~ + .rising = 0x00, +- .falling = 0x0a, ++ .falling = PWM_FALLING_VALUE, + }, + [1] = { ++ .target_freq = 25000, + .load_wdt_rising_falling_pt = 0x10, + .load_wdt_selection = 0, + .load_wdt_enable = 0, + .duty_sync_enable = 0, + .invert_pin = 0, +- .divide_h = 0x5, +- .divide_l = 0x6, +- .period = 0x13, //5% ~~ + .rising = 0x00, +- .falling = 0x0a, ++ .falling = PWM_FALLING_VALUE, + }, + [2] = { ++ .target_freq = 25000, + .load_wdt_rising_falling_pt = 0x10, + .load_wdt_selection = 0, + .load_wdt_enable = 0, + .duty_sync_enable = 0, + .invert_pin = 0, +- .divide_h = 0x5, +- .divide_l = 0x6, +- .period = 0x13, //5% ~~ + .rising = 0x00, +- .falling = 0x0a, ++ .falling = PWM_FALLING_VALUE, + }, + [3] = { ++ .target_freq = 25000, + .load_wdt_rising_falling_pt = 0x10, + .load_wdt_selection = 0, + .load_wdt_enable = 0, + .duty_sync_enable = 0, + .invert_pin = 0, +- .divide_h = 0x5, +- .divide_l = 0x6, +- .period = 0x13, //5% ~~ + .rising = 0x00, +- .falling = 0x0a, ++ .falling = PWM_FALLING_VALUE, + }, + [4] = { ++ .target_freq = 25000, + .load_wdt_rising_falling_pt = 0x10, + .load_wdt_selection = 0, + .load_wdt_enable = 0, + .duty_sync_enable = 0, + .invert_pin = 0, +- .divide_h = 0x5, +- .divide_l = 0x6, +- .period = 0x13, //5% ~~ + .rising = 0x00, +- .falling = 0x0a, ++ .falling = PWM_FALLING_VALUE, + }, + [5] = { ++ .target_freq = 25000, + .load_wdt_rising_falling_pt = 0x10, + .load_wdt_selection = 0, + .load_wdt_enable = 0, + .duty_sync_enable = 0, + .invert_pin = 0, +- .divide_h = 0x5, +- .divide_l = 0x6, +- .period = 0x13, //5% ~~ + .rising = 0x00, +- .falling = 0x0a, ++ .falling = PWM_FALLING_VALUE, + }, + [6] = { ++ .target_freq = 25000, + .load_wdt_rising_falling_pt = 0x10, + .load_wdt_selection = 0, + .load_wdt_enable = 0, + .duty_sync_enable = 0, + .invert_pin = 0, +- .divide_h = 0x5, +- .divide_l = 0x6, +- .period = 0x13, //5% ~~ + .rising = 0x00, +- .falling = 0x0a, ++ .falling = PWM_FALLING_VALUE, + }, + [7] = { ++ .target_freq = 25000, + .load_wdt_rising_falling_pt = 0x10, + .load_wdt_selection = 0, + .load_wdt_enable = 0, + .duty_sync_enable = 0, + .invert_pin = 0, +- .divide_h = 0x5, +- .divide_l = 0x6, +- .period = 0x13, //5% ~~ + .rising = 0x00, +- .falling = 0x0a, ++ .falling = PWM_FALLING_VALUE, + }, + [8] = { ++ .target_freq = 25000, + .load_wdt_rising_falling_pt = 0x10, + .load_wdt_selection = 0, + .load_wdt_enable = 0, + .duty_sync_enable = 0, + .invert_pin = 0, +- .divide_h = 0x5, +- .divide_l = 0x6, +- .period = 0x13, //5% ~~ + .rising = 0x00, +- .falling = 0x0a, ++ .falling = PWM_FALLING_VALUE, + }, + [9] = { ++ .target_freq = 25000, + .load_wdt_rising_falling_pt = 0x10, + .load_wdt_selection = 0, + .load_wdt_enable = 0, + .duty_sync_enable = 0, + .invert_pin = 0, +- .divide_h = 0x5, +- .divide_l = 0x6, +- .period = 0x13, //5% ~~ + .rising = 0x00, +- .falling = 0x0a, ++ .falling = PWM_FALLING_VALUE, + }, + [10] = { ++ .target_freq = 25000, + .load_wdt_rising_falling_pt = 0x10, + .load_wdt_selection = 0, + .load_wdt_enable = 0, + .duty_sync_enable = 0, + .invert_pin = 0, +- .divide_h = 0x5, +- .divide_l = 0x6, +- .period = 0x13, //5% ~~ + .rising = 0x00, +- .falling = 0x0a, ++ .falling = PWM_FALLING_VALUE, + }, + [11] = { ++ .target_freq = 25000, + .load_wdt_rising_falling_pt = 0x10, + .load_wdt_selection = 0, + .load_wdt_enable = 0, + .duty_sync_enable = 0, + .invert_pin = 0, +- .divide_h = 0x5, +- .divide_l = 0x6, +- .period = 0x13, //5% ~~ + .rising = 0x00, +- .falling = 0x0a, ++ .falling = PWM_FALLING_VALUE, + }, + [12] = { ++ .target_freq = 25000, + .load_wdt_rising_falling_pt = 0x10, + .load_wdt_selection = 0, + .load_wdt_enable = 0, + .duty_sync_enable = 0, + .invert_pin = 0, +- .divide_h = 0x5, +- .divide_l = 0x6, +- .period = 0x13, //5% ~~ + .rising = 0x00, +- .falling = 0x0a, ++ .falling = PWM_FALLING_VALUE, + }, + [13] = { ++ .target_freq = 25000, + .load_wdt_rising_falling_pt = 0x10, + .load_wdt_selection = 0, + .load_wdt_enable = 0, + .duty_sync_enable = 0, + .invert_pin = 0, +- .divide_h = 0x5, +- .divide_l = 0x6, +- .period = 0x13, //5% ~~ + .rising = 0x00, +- .falling = 0x0a, ++ .falling = PWM_FALLING_VALUE, + }, + [14] = { ++ .target_freq = 25000, + .load_wdt_rising_falling_pt = 0x10, + .load_wdt_selection = 0, + .load_wdt_enable = 0, + .duty_sync_enable = 0, + .invert_pin = 0, +- .divide_h = 0x5, +- .divide_l = 0x6, +- .period = 0x13, //5% ~~ + .rising = 0x00, +- .falling = 0x0a, ++ .falling = PWM_FALLING_VALUE, + }, + [15] = { ++ .target_freq = 25000, + .load_wdt_rising_falling_pt = 0x10, + .load_wdt_selection = 0, + .load_wdt_enable = 0, + .duty_sync_enable = 0, + .invert_pin = 0, +- .divide_h = 0x5, +- .divide_l = 0x6, +- .period = 0x13, //5% ~~ + .rising = 0x00, +- .falling = 0x0a, ++ .falling = PWM_FALLING_VALUE, + }, + }; + +@@ -318,125 +291,146 @@ static struct aspeed_pwm_channel_params default_pwm_params[] = { + * 11: reserved. + */ + ++#define F2F_EDGES 0x00 ++#define R2R_EDGES 0x01 ++#define BOTH_EDGES 0x02 ++ + struct aspeed_tacho_channel_params { ++ u32 min_rpm; + int limited_inverse; + u16 threshold; + u8 tacho_edge; + u8 tacho_debounce; +- u8 divide; ++ u32 divide; + }; + + + static struct aspeed_tacho_channel_params default_tacho_params[] = { + [0] = { ++ .min_rpm = 2900, + .limited_inverse = 0, + .threshold = 0, +- .tacho_edge = 0, ++ .tacho_edge = F2F_EDGES, + .tacho_debounce = 0, + .divide = 8, + }, + [1] = { ++ .min_rpm = 2900, + .limited_inverse = 0, + .threshold = 0, +- .tacho_edge = 0, ++ .tacho_edge = F2F_EDGES, + .tacho_debounce = 0, + .divide = 8, + }, + [2] = { ++ .min_rpm = 2900, + .limited_inverse = 0, + .threshold = 0, +- .tacho_edge = 0, ++ .tacho_edge = F2F_EDGES, + .tacho_debounce = 0, + .divide = 8, + }, + [3] = { ++ .min_rpm = 2900, + .limited_inverse = 0, + .threshold = 0, +- .tacho_edge = 0, ++ .tacho_edge = F2F_EDGES, + .tacho_debounce = 0, + .divide = 8, + }, + [4] = { ++ .min_rpm = 2900, + .limited_inverse = 0, + .threshold = 0, +- .tacho_edge = 0, ++ .tacho_edge = F2F_EDGES, + .tacho_debounce = 0, + .divide = 8, + }, + [5] = { ++ .min_rpm = 2900, + .limited_inverse = 0, + .threshold = 0, +- .tacho_edge = 0, ++ .tacho_edge = F2F_EDGES, + .tacho_debounce = 0, + .divide = 8, + }, + [6] = { ++ .min_rpm = 2900, + .limited_inverse = 0, + .threshold = 0, +- .tacho_edge = 0, ++ .tacho_edge = F2F_EDGES, + .tacho_debounce = 0, + .divide = 8, + }, + [7] = { ++ .min_rpm = 2900, + .limited_inverse = 0, + .threshold = 0, +- .tacho_edge = 0, ++ .tacho_edge = F2F_EDGES, + .tacho_debounce = 0, + .divide = 8, + }, + [8] = { ++ .min_rpm = 2900, + .limited_inverse = 0, + .threshold = 0, +- .tacho_edge = 0, ++ .tacho_edge = F2F_EDGES, + .tacho_debounce = 0, + .divide = 8, + }, + [9] = { ++ .min_rpm = 2900, + .limited_inverse = 0, + .threshold = 0, +- .tacho_edge = 0, ++ .tacho_edge = F2F_EDGES, + .tacho_debounce = 0, + .divide = 8, + }, + [10] = { ++ .min_rpm = 2900, + .limited_inverse = 0, + .threshold = 0, +- .tacho_edge = 0, ++ .tacho_edge = F2F_EDGES, + .tacho_debounce = 0, + .divide = 8, + }, + [11] = { ++ .min_rpm = 2900, + .limited_inverse = 0, + .threshold = 0, +- .tacho_edge = 0, ++ .tacho_edge = F2F_EDGES, + .tacho_debounce = 0, + .divide = 8, + }, + [12] = { ++ .min_rpm = 2900, + .limited_inverse = 0, + .threshold = 0, +- .tacho_edge = 0, ++ .tacho_edge = F2F_EDGES, + .tacho_debounce = 0, + .divide = 8, + }, + [13] = { ++ .min_rpm = 2900, + .limited_inverse = 0, + .threshold = 0, +- .tacho_edge = 0, ++ .tacho_edge = F2F_EDGES, + .tacho_debounce = 0, + .divide = 8, + }, + [14] = { ++ .min_rpm = 2900, + .limited_inverse = 0, + .threshold = 0, +- .tacho_edge = 0, ++ .tacho_edge = F2F_EDGES, + .tacho_debounce = 0, + .divide = 8, + }, + [15] = { ++ .min_rpm = 2900, + .limited_inverse = 0, + .threshold = 0, +- .tacho_edge = 0, ++ .tacho_edge = F2F_EDGES, + .tacho_debounce = 0, + .divide = 8, + }, +@@ -501,29 +495,28 @@ static void aspeed_set_pwm_channel_enable(struct regmap *regmap, u8 pwm_channel, + static void aspeed_set_fan_tach_ch_enable(struct aspeed_pwm_tachometer_data *priv, u8 fan_tach_ch, + bool enable) + { +- u32 i = 0, j; ++ u32 i; + u32 divide_val = 0; ++ u32 target_div; + u32 reg_value = 0; + + if(enable) { +- //4 ^ n +- //check pwm clk and to change tacho devide 25KZ +- for(i = 0; i < 12; i++) { ++ /*RPM calculation as per ast2600 datasheet*/ ++ target_div = (priv->clk_freq * 60 / priv->tacho_channel[fan_tach_ch].min_rpm * 2) / (0xfffff + 1); ++ if (target_div) { ++ for (i = 0; i < 12; i++) { ++ divide_val = BIT(i) * BIT(i); ++ if (divide_val > target_div) ++ break; ++ } ++ } else { + divide_val = 1; +- for (j = 1; j <= i; j++) +- divide_val *= 4; +-// printk("i : %d , priv->clk_freq/divide_val %d ",i, priv->clk_freq/divide_val); +- if((priv->clk_freq/divide_val) < 250000) +- break; + } +- i--; +- divide_val = ((1 << i) * (1 << i)); +-// printk("tacho divide_val %d , i %x max tacho clk %d \n", divide_val, i, priv->clk_freq / divide_val); +- priv->tacho_channel[fan_tach_ch].divide = i; ++ priv->tacho_channel[fan_tach_ch].divide = divide_val; + + reg_value = TACHO_ENABLE | +- (priv->tacho_channel[fan_tach_ch].tacho_edge << TECHIO_EDGE_BIT) | +- (priv->tacho_channel[fan_tach_ch].divide << TACHO_CLK_DIV_BIT) | ++ (priv->tacho_channel[fan_tach_ch].tacho_edge << TACHIO_EDGE_BIT) | ++ (i << TACHO_CLK_DIV_BIT) | + (priv->tacho_channel[fan_tach_ch].tacho_debounce << TACHO_DEBOUNCE_BIT); + + if(priv->tacho_channel[fan_tach_ch].limited_inverse) +@@ -541,14 +534,28 @@ static void aspeed_set_pwm_channel_fan_ctrl(struct aspeed_pwm_tachometer_data *p + u8 index, u8 fan_ctrl) + { + u32 duty_value, ctrl_value; ++ u32 div_h, div_l, cal_freq; + + if (fan_ctrl == 0) { + aspeed_set_pwm_channel_enable(priv->regmap, index, false); + } else { +- duty_value = (priv->pwm_channel[index].period << PWM_PERIOD_BIT) | +- (0 << PWM_RISING_RISING_BIT) | (fan_ctrl << PWM_RISING_FALLING_BIT); ++ cal_freq = priv->clk_freq / (DEF_PWM_PERIOD + 1); ++ /*calculate for target frequence*/ ++ for (div_l = 0; div_l < 0x100; div_l++) { ++ for (div_h = 0; div_h < 0x10; div_h++) { ++ if ((cal_freq / (BIT(div_h) * (div_l + 1))) < priv->pwm_channel[index].target_freq) ++ break; ++ } ++ if ((cal_freq / (BIT(div_h) * (div_l + 1))) < priv->pwm_channel[index].target_freq) ++ break; ++ } ++ ++ priv->pwm_channel[index].pwm_freq = cal_freq / (BIT(div_h) * (div_l + 1)); + +- ctrl_value = (priv->pwm_channel[index].divide_h << 8) | priv->pwm_channel[index].divide_l; ++ ctrl_value = (div_h << 8) | div_l; ++ ++ duty_value = (DEF_PWM_PERIOD << PWM_PERIOD_BIT) | ++ (0 << PWM_RISING_RISING_BIT) | (fan_ctrl << PWM_RISING_FALLING_BIT); + + if (priv->pwm_channel[index].load_wdt_enable) { + ctrl_value |= PWM_DUTY_LOAD_AS_WDT_EN; +@@ -568,13 +575,11 @@ static void aspeed_set_pwm_channel_fan_ctrl(struct aspeed_pwm_tachometer_data *p + } + } + +-#define BOTH_EDGES 0x02 /* 10b */ +- + static int aspeed_get_fan_tach_ch_rpm(struct aspeed_pwm_tachometer_data *priv, + u8 fan_tach_ch) + { + u32 raw_data, tach_div, clk_source, val; +- u8 mode, both; ++ u8 multiplier = 2; + int i, retries = 3; + + for(i = 0; i < retries; i++) { +@@ -587,22 +592,15 @@ static int aspeed_get_fan_tach_ch_rpm(struct aspeed_pwm_tachometer_data *priv, + if(raw_data == 0xfffff) + return 0; + +- tach_div = priv->tacho_channel[fan_tach_ch].divide; +- /* +- * We need the mode to determine if the raw_data is double (from +- * counting both edges). +- */ +- mode = priv->tacho_channel[fan_tach_ch].tacho_edge; +- both = (mode & BOTH_EDGES) ? 1 : 0; +-// printk("clk %ld, raw_data %x , tach_div %x both %x \n", priv->clk_freq, raw_data, tach_div, both); ++ raw_data += 1; ++ tach_div = raw_data * (priv->tacho_channel[fan_tach_ch].divide) * (multiplier); + +- tach_div = (tach_div * 2) * (0x1 << both); + clk_source = priv->clk_freq; + + if (raw_data == 0) + return 0; + +- return (clk_source * 60) / (2 * raw_data * tach_div); ++ return (clk_source / tach_div * 60); + + } + +@@ -614,19 +612,27 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *attr, + int ret; + struct aspeed_pwm_tachometer_data *priv = dev_get_drvdata(dev); + long fan_ctrl; ++ u8 org_falling = priv->pwm_channel[index].falling; + + ret = kstrtol(buf, 10, &fan_ctrl); + if (ret != 0) + return ret; + +- if (fan_ctrl < 0 || fan_ctrl > priv->pwm_channel[index].period) ++ if (fan_ctrl < 0 || fan_ctrl > DEF_PWM_PERIOD) + return -EINVAL; + + if (priv->pwm_channel[index].falling == fan_ctrl) + return count; + + priv->pwm_channel[index].falling = fan_ctrl; +- aspeed_set_pwm_channel_fan_ctrl(priv, index, fan_ctrl); ++ ++ if (fan_ctrl == 0) ++ aspeed_set_pwm_channel_enable(priv->regmap, index, false); ++ else ++ regmap_update_bits(priv->regmap, ASPEED_PWM_DUTY_CYCLE_CH(index), GENMASK(15, 8), (fan_ctrl << PWM_RISING_FALLING_BIT)); ++ ++ if (org_falling == 0) ++ aspeed_set_pwm_channel_enable(priv->regmap, index, true); + + return count; + } +@@ -678,40 +684,39 @@ static umode_t fan_dev_is_visible(struct kobject *kobj, + return a->mode; + } + +-static SENSOR_DEVICE_ATTR(pwm0, 0644, +- show_pwm, set_pwm, 0); + static SENSOR_DEVICE_ATTR(pwm1, 0644, +- show_pwm, set_pwm, 1); ++ show_pwm, set_pwm, 0); + static SENSOR_DEVICE_ATTR(pwm2, 0644, +- show_pwm, set_pwm, 2); ++ show_pwm, set_pwm, 1); + static SENSOR_DEVICE_ATTR(pwm3, 0644, +- show_pwm, set_pwm, 3); ++ show_pwm, set_pwm, 2); + static SENSOR_DEVICE_ATTR(pwm4, 0644, +- show_pwm, set_pwm, 4); ++ show_pwm, set_pwm, 3); + static SENSOR_DEVICE_ATTR(pwm5, 0644, +- show_pwm, set_pwm, 5); ++ show_pwm, set_pwm, 4); + static SENSOR_DEVICE_ATTR(pwm6, 0644, +- show_pwm, set_pwm, 6); ++ show_pwm, set_pwm, 5); + static SENSOR_DEVICE_ATTR(pwm7, 0644, +- show_pwm, set_pwm, 7); ++ show_pwm, set_pwm, 6); + static SENSOR_DEVICE_ATTR(pwm8, 0644, +- show_pwm, set_pwm, 8); ++ show_pwm, set_pwm, 7); + static SENSOR_DEVICE_ATTR(pwm9, 0644, +- show_pwm, set_pwm, 9); ++ show_pwm, set_pwm, 8); + static SENSOR_DEVICE_ATTR(pwm10, 0644, +- show_pwm, set_pwm, 10); ++ show_pwm, set_pwm, 9); + static SENSOR_DEVICE_ATTR(pwm11, 0644, +- show_pwm, set_pwm, 11); ++ show_pwm, set_pwm, 10); + static SENSOR_DEVICE_ATTR(pwm12, 0644, +- show_pwm, set_pwm, 12); ++ show_pwm, set_pwm, 11); + static SENSOR_DEVICE_ATTR(pwm13, 0644, +- show_pwm, set_pwm, 13); ++ show_pwm, set_pwm, 12); + static SENSOR_DEVICE_ATTR(pwm14, 0644, +- show_pwm, set_pwm, 14); ++ show_pwm, set_pwm, 13); + static SENSOR_DEVICE_ATTR(pwm15, 0644, ++ show_pwm, set_pwm, 14); ++static SENSOR_DEVICE_ATTR(pwm16, 0644, + show_pwm, set_pwm, 15); + static struct attribute *pwm_dev_attrs[] = { +- &sensor_dev_attr_pwm0.dev_attr.attr, + &sensor_dev_attr_pwm1.dev_attr.attr, + &sensor_dev_attr_pwm2.dev_attr.attr, + &sensor_dev_attr_pwm3.dev_attr.attr, +@@ -727,6 +732,7 @@ static struct attribute *pwm_dev_attrs[] = { + &sensor_dev_attr_pwm13.dev_attr.attr, + &sensor_dev_attr_pwm14.dev_attr.attr, + &sensor_dev_attr_pwm15.dev_attr.attr, ++ &sensor_dev_attr_pwm16.dev_attr.attr, + NULL, + }; + +@@ -735,40 +741,39 @@ static const struct attribute_group pwm_dev_group = { + .is_visible = pwm_is_visible, + }; + +-static SENSOR_DEVICE_ATTR(fan0_input, 0444, +- show_rpm, NULL, 0); + static SENSOR_DEVICE_ATTR(fan1_input, 0444, +- show_rpm, NULL, 1); ++ show_rpm, NULL, 0); + static SENSOR_DEVICE_ATTR(fan2_input, 0444, +- show_rpm, NULL, 2); ++ show_rpm, NULL, 1); + static SENSOR_DEVICE_ATTR(fan3_input, 0444, +- show_rpm, NULL, 3); ++ show_rpm, NULL, 2); + static SENSOR_DEVICE_ATTR(fan4_input, 0444, +- show_rpm, NULL, 4); ++ show_rpm, NULL, 3); + static SENSOR_DEVICE_ATTR(fan5_input, 0444, +- show_rpm, NULL, 5); ++ show_rpm, NULL, 4); + static SENSOR_DEVICE_ATTR(fan6_input, 0444, +- show_rpm, NULL, 6); ++ show_rpm, NULL, 5); + static SENSOR_DEVICE_ATTR(fan7_input, 0444, +- show_rpm, NULL, 7); ++ show_rpm, NULL, 6); + static SENSOR_DEVICE_ATTR(fan8_input, 0444, +- show_rpm, NULL, 8); ++ show_rpm, NULL, 7); + static SENSOR_DEVICE_ATTR(fan9_input, 0444, +- show_rpm, NULL, 9); ++ show_rpm, NULL, 8); + static SENSOR_DEVICE_ATTR(fan10_input, 0444, +- show_rpm, NULL, 10); ++ show_rpm, NULL, 9); + static SENSOR_DEVICE_ATTR(fan11_input, 0444, +- show_rpm, NULL, 11); ++ show_rpm, NULL, 10); + static SENSOR_DEVICE_ATTR(fan12_input, 0444, +- show_rpm, NULL, 12); ++ show_rpm, NULL, 11); + static SENSOR_DEVICE_ATTR(fan13_input, 0444, +- show_rpm, NULL, 13); ++ show_rpm, NULL, 12); + static SENSOR_DEVICE_ATTR(fan14_input, 0444, +- show_rpm, NULL, 14); ++ show_rpm, NULL, 13); + static SENSOR_DEVICE_ATTR(fan15_input, 0444, ++ show_rpm, NULL, 14); ++static SENSOR_DEVICE_ATTR(fan16_input, 0444, + show_rpm, NULL, 15); + static struct attribute *fan_dev_attrs[] = { +- &sensor_dev_attr_fan0_input.dev_attr.attr, + &sensor_dev_attr_fan1_input.dev_attr.attr, + &sensor_dev_attr_fan2_input.dev_attr.attr, + &sensor_dev_attr_fan3_input.dev_attr.attr, +@@ -784,6 +789,7 @@ static struct attribute *fan_dev_attrs[] = { + &sensor_dev_attr_fan13_input.dev_attr.attr, + &sensor_dev_attr_fan14_input.dev_attr.attr, + &sensor_dev_attr_fan15_input.dev_attr.attr, ++ &sensor_dev_attr_fan16_input.dev_attr.attr, + NULL + }; + +@@ -802,14 +808,14 @@ static void aspeed_create_pwm_channel(struct aspeed_pwm_tachometer_data *priv, + } + + static void aspeed_create_fan_tach_channel(struct aspeed_pwm_tachometer_data *priv, +- u8 *fan_tach_ch, +- int count) ++ u8 *fan_tach_ch, int count, u32 min_rpm) + { + u8 val, index; + + for (val = 0; val < count; val++) { + index = fan_tach_ch[val]; + priv->fan_tach_present[index] = true; ++ priv->tacho_channel[index].min_rpm = min_rpm; + aspeed_set_fan_tach_ch_enable(priv, index, true); + } + } +@@ -906,13 +912,19 @@ static int aspeed_pwm_create_fan(struct device *dev, + struct aspeed_pwm_tachometer_data *priv) + { + u8 *fan_tach_ch; ++ u32 fan_min_rpm; + u32 pwm_channel; ++ u32 target_pwm_freq; + int ret, count; + + ret = of_property_read_u32(child, "reg", &pwm_channel); + if (ret) + return ret; + ++ ret = of_property_read_u32(child, "aspeed,target_pwm", &target_pwm_freq); ++ if (ret) ++ target_pwm_freq = DEFAULT_TARGET_PWM_FREQ; ++ + aspeed_create_pwm_channel(priv, (u8)pwm_channel); + + ret = of_property_count_u8_elems(child, "cooling-levels"); +@@ -935,9 +947,11 @@ static int aspeed_pwm_create_fan(struct device *dev, + fan_tach_ch, count); + if (ret) + return ret; ++ ret = of_property_read_u32(child, "aspeed,min_rpm", &fan_min_rpm); ++ if (ret) ++ fan_min_rpm = DEFAULT_MIN_RPM; + +- aspeed_create_fan_tach_channel(priv, fan_tach_ch, count); +- ++ aspeed_create_fan_tach_channel(priv, fan_tach_ch, count, fan_min_rpm); + return 0; + } + +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0096-Fix-truncated-WrEndPointConfig-MMIO-command.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0096-Fix-truncated-WrEndPointConfig-MMIO-command.patch new file mode 100644 index 000000000..e2196c629 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0096-Fix-truncated-WrEndPointConfig-MMIO-command.patch @@ -0,0 +1,40 @@ +From 7555448ab0676ea974ef67ef0c8ebe5d68b73d25 Mon Sep 17 00:00:00 2001 +From: "Jason M. Bills" <jason.m.bills@linux.intel.com> +Date: Tue, 18 Feb 2020 14:17:22 -0800 +Subject: [PATCH] Fix truncated WrEndPointConfig MMIO command + +When address type is 6, the driver is reducing the msg buffer to +the base size and truncating the additional command data. + +This removes the line that sets the msg buffer size to the base +size. The correct buffer size is already set. + +Tested: +Ran this command: +peci_cmds WrEndpointConfigMMIO 6 0 0 0x7e 0x1a 0x0 0x224e0 0x5f5a56 + +and got the full buffer in the driver log: +[ 795.207278] peci_aspeed 1e78b000.peci-bus: HEAD : 0x00011730 +[ 795.212980] TX : c5 00 05 00 00 00 06 00 d0 7e e0 24 02 00 00 00 .........~.$.... +[ 795.220556] TX : 00 00 56 5a 5f 00 a8 ..VZ_.. + +Signed-off-by: Jason M. Bills <jason.m.bills@linux.intel.com> +--- + drivers/peci/peci-core.c | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/drivers/peci/peci-core.c b/drivers/peci/peci-core.c +index 9aedb74..efb73b0 100644 +--- a/drivers/peci/peci-core.c ++++ b/drivers/peci/peci-core.c +@@ -1040,7 +1040,6 @@ static int peci_cmd_wr_end_pt_cfg(struct peci_adapter *adapter, void *vmsg) + >> 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 +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0100-Mailbox-Enabling-interrupt-based-mailbox.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0100-Mailbox-Enabling-interrupt-based-mailbox.patch new file mode 100644 index 000000000..e67adb729 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0100-Mailbox-Enabling-interrupt-based-mailbox.patch @@ -0,0 +1,234 @@ +From 7094f4d8efea8e059c965ba70c73d5226af392cb Mon Sep 17 00:00:00 2001 +From: "Arun P. Mohanan" <arun.p.m@linux.intel.com> +Date: Wed, 11 Mar 2020 17:23:49 +0530 +Subject: [PATCH] [Mailbox] Enabling interrupt based mailbox + +Modifying the mailbox driver to use FIFO queue while using interrupt. + +Signed-off-by: Arun P. Mohanan <arun.p.m@linux.intel.com> +--- + drivers/soc/aspeed/aspeed-lpc-mbox.c | 101 ++++++++++++++++++--------- + 1 file changed, 68 insertions(+), 33 deletions(-) + +diff --git a/drivers/soc/aspeed/aspeed-lpc-mbox.c b/drivers/soc/aspeed/aspeed-lpc-mbox.c +index 795107206022..99f38a4e4550 100644 +--- a/drivers/soc/aspeed/aspeed-lpc-mbox.c ++++ b/drivers/soc/aspeed/aspeed-lpc-mbox.c +@@ -12,10 +12,11 @@ + #include <linux/poll.h> + #include <linux/regmap.h> + #include <linux/slab.h> ++#include <linux/kfifo.h> + + #define DEVICE_NAME "aspeed-mbox" + +-#define MBX_USE_INTERRUPT 0 ++#define MBX_USE_INTERRUPT 1 + + #define ASPEED_MBOX_NUM_REGS 16 + +@@ -29,6 +30,7 @@ + #define ASPEED_MBOX_HOST_CTRL 0x4c + #define ASPEED_MBOX_INTERRUPT_0 0x50 + #define ASPEED_MBOX_INTERRUPT_1 0x54 ++#define MBOX_FIFO_SIZE 64 + + struct aspeed_mbox { + struct miscdevice miscdev; +@@ -38,6 +40,8 @@ struct aspeed_mbox { + int irq; + wait_queue_head_t queue; + struct mutex mutex; ++ struct kfifo fifo; ++ spinlock_t lock; + }; + + static atomic_t aspeed_mbox_open_count = ATOMIC_INIT(0); +@@ -74,20 +78,37 @@ static struct aspeed_mbox *file_mbox(struct file *file) + return container_of(file->private_data, struct aspeed_mbox, miscdev); + } + ++/* Save a byte to a FIFO and discard the oldest byte if FIFO is full */ ++static void put_fifo_with_discard(struct aspeed_mbox *mbox, u8 val) ++{ ++ if (!kfifo_initialized(&mbox->fifo)) ++ return; ++ if (kfifo_is_full(&mbox->fifo)) ++ kfifo_skip(&mbox->fifo); ++ kfifo_put(&mbox->fifo, val); ++} ++ + static int aspeed_mbox_open(struct inode *inode, struct file *file) + { + #if MBX_USE_INTERRUPT + struct aspeed_mbox *mbox = file_mbox(file); ++ int i; + #endif + + if (atomic_inc_return(&aspeed_mbox_open_count) == 1) { + #if MBX_USE_INTERRUPT + /* +- * Clear the interrupt status bit if it was left on and unmask +- * interrupts. +- * ASPEED_MBOX_CTRL_RECV bit is W1C, this also unmasks in 1 step ++ * Reset the FIFO while opening to clear the old cached data ++ * and load the FIFO with latest mailbox register values. + */ +- aspeed_mbox_outb(mbox, ASPEED_MBOX_CTRL_RECV, ASPEED_MBOX_BMC_CTRL); ++ kfifo_reset(&mbox->fifo); ++ spin_lock_irq(&mbox->lock); ++ for (i = 0; i < ASPEED_MBOX_NUM_REGS; i++) { ++ put_fifo_with_discard(mbox, ++ aspeed_mbox_inb(mbox, ASPEED_MBOX_DATA_0 + (i * 4))); ++ } ++ spin_unlock_irq(&mbox->lock); ++ + #endif + return 0; + } +@@ -102,6 +123,8 @@ static ssize_t aspeed_mbox_read(struct file *file, char __user *buf, + struct aspeed_mbox *mbox = file_mbox(file); + char __user *p = buf; + ssize_t ret; ++ unsigned int copied; ++ unsigned long flags; + int i; + + if (!access_ok(buf, count)) +@@ -111,15 +134,29 @@ static ssize_t aspeed_mbox_read(struct file *file, char __user *buf, + return -EINVAL; + + #if MBX_USE_INTERRUPT +- if (file->f_flags & O_NONBLOCK) { +- if (!(aspeed_mbox_inb(mbox, ASPEED_MBOX_BMC_CTRL) & +- ASPEED_MBOX_CTRL_RECV)) ++ /* ++ * Restrict count as per the number of mailbox registers ++ * if interrupt is enabled. ++ */ ++ if (count != ASPEED_MBOX_NUM_REGS) ++ return -EINVAL; ++ ++ if (kfifo_is_empty(&mbox->fifo)) { ++ if (file->f_flags & O_NONBLOCK){ + return -EAGAIN; +- } else if (wait_event_interruptible(mbox->queue, +- aspeed_mbox_inb(mbox, ASPEED_MBOX_BMC_CTRL) & +- ASPEED_MBOX_CTRL_RECV)) { +- return -ERESTARTSYS; ++ } ++ ret = wait_event_interruptible(mbox->queue, ++ !kfifo_is_empty(&mbox->fifo)); ++ if (ret == -ERESTARTSYS){ ++ return -EINTR; ++ } + } ++ ++ spin_lock_irqsave(&mbox->lock, flags); ++ ret = kfifo_to_user(&mbox->fifo, buf, count, &copied); ++ spin_unlock_irqrestore(&mbox->lock, flags); ++ return ret ? ret : copied; ++ + #endif + + mutex_lock(&mbox->mutex); +@@ -134,11 +171,6 @@ static ssize_t aspeed_mbox_read(struct file *file, char __user *buf, + p++; + count--; + } +- +-#if MBX_USE_INTERRUPT +- /* ASPEED_MBOX_CTRL_RECV bit is write to clear, this also unmasks in 1 step */ +- aspeed_mbox_outb(mbox, ASPEED_MBOX_CTRL_RECV, ASPEED_MBOX_BMC_CTRL); +-#endif + ret = p - buf; + + out_unlock: +@@ -186,16 +218,9 @@ static ssize_t aspeed_mbox_write(struct file *file, const char __user *buf, + static unsigned int aspeed_mbox_poll(struct file *file, poll_table *wait) + { + struct aspeed_mbox *mbox = file_mbox(file); +- unsigned int mask = 0; + + poll_wait(file, &mbox->queue, wait); +- +-#if MBX_USE_INTERRUPT +- if (aspeed_mbox_inb(mbox, ASPEED_MBOX_BMC_CTRL) & ASPEED_MBOX_CTRL_RECV) +-#endif +- mask |= POLLIN; +- +- return mask; ++ return !kfifo_is_empty(&mbox->fifo) ? POLLIN : 0; + } + + static int aspeed_mbox_release(struct inode *inode, struct file *file) +@@ -220,19 +245,23 @@ static irqreturn_t aspeed_mbox_irq(int irq, void *arg) + #if MBX_USE_INTERRUPT + int i; + +-// if (!(aspeed_mbox_inb(mbox, ASPEED_MBOX_BMC_CTRL) & ASPEED_MBOX_CTRL_RECV)) +-// return IRQ_NONE; +- +- printk(KERN_ERR "BMC_CTRL: 0x%02x\n", ++ dev_dbg(mbox->miscdev.parent, "BMC_CTRL11: 0x%02x\n", + aspeed_mbox_inb(mbox, ASPEED_MBOX_BMC_CTRL)); +- printk(KERN_ERR "STATUS_0: 0x%02x\n", ++ dev_dbg(mbox->miscdev.parent, "STATUS_0: 0x%02x\n", + aspeed_mbox_inb(mbox, ASPEED_MBOX_STATUS_0)); +- printk(KERN_ERR "STATUS_1: 0x%02x\n", ++ dev_dbg(mbox->miscdev.parent, "STATUS_1: 0x%02x\n", + aspeed_mbox_inb(mbox, ASPEED_MBOX_STATUS_1)); + for (i = 0; i < ASPEED_MBOX_NUM_REGS; i++) { +- printk(KERN_ERR "DATA_%d: 0x%02x\n", i, ++ dev_dbg(mbox->miscdev.parent, "DATA_%d: 0x%02x\n", i, + aspeed_mbox_inb(mbox, ASPEED_MBOX_DATA_0 + (i * 4))); + } ++ ++ spin_lock(&mbox->lock); ++ for (i = 0; i < ASPEED_MBOX_NUM_REGS; i++) { ++ put_fifo_with_discard(mbox, ++ aspeed_mbox_inb(mbox, ASPEED_MBOX_DATA_0 + (i * 4))); ++ } ++ spin_unlock(&mbox->lock); + #endif + + /* Clear interrupt status */ +@@ -249,7 +278,6 @@ static int aspeed_mbox_config_irq(struct aspeed_mbox *mbox, + { + struct device *dev = &pdev->dev; + int rc; +- + mbox->irq = platform_get_irq(pdev, 0); + if (!mbox->irq) + return -ENODEV; +@@ -300,6 +328,7 @@ static int aspeed_mbox_probe(struct platform_device *pdev) + return -ENODEV; + } + ++ spin_lock_init(&mbox->lock); + mutex_init(&mbox->mutex); + init_waitqueue_head(&mbox->queue); + +@@ -316,6 +345,11 @@ static int aspeed_mbox_probe(struct platform_device *pdev) + return rc; + } + ++ /* Create FIFO data structure */ ++ rc = kfifo_alloc(&mbox->fifo, MBOX_FIFO_SIZE, GFP_KERNEL); ++ if (rc) ++ return rc; ++ + mbox->miscdev.minor = MISC_DYNAMIC_MINOR; + mbox->miscdev.name = DEVICE_NAME; + mbox->miscdev.fops = &aspeed_mbox_fops; +@@ -349,6 +383,7 @@ static int aspeed_mbox_remove(struct platform_device *pdev) + + misc_deregister(&mbox->miscdev); + clk_disable_unprepare(mbox->clk); ++ kfifo_free(&mbox->fifo); + + return 0; + } +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0101-Add-poll-fops-in-eSPI-driver.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0101-Add-poll-fops-in-eSPI-driver.patch new file mode 100644 index 000000000..9c9704eff --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0101-Add-poll-fops-in-eSPI-driver.patch @@ -0,0 +1,142 @@ +From 56ea1fc793c97232c12ddc3b4936081fe14c962f Mon Sep 17 00:00:00 2001 +From: "Arun P. Mohanan" <arun.p.m@linux.intel.com> +Date: Wed, 18 Mar 2020 08:34:43 +0530 +Subject: [PATCH] Add poll fops in eSPI driver + +Modify eSPI driver to support poll fops. + +Signed-off-by: Arun P. Mohanan <arun.p.m@linux.intel.com> +--- + drivers/misc/aspeed-espi-slave.c | 83 +++++++++++++++++++++++--------- + 1 file changed, 59 insertions(+), 24 deletions(-) + +diff --git a/drivers/misc/aspeed-espi-slave.c b/drivers/misc/aspeed-espi-slave.c +index 87bc81948694..f4a0d7528414 100644 +--- a/drivers/misc/aspeed-espi-slave.c ++++ b/drivers/misc/aspeed-espi-slave.c +@@ -9,6 +9,7 @@ + #include <linux/module.h> + #include <linux/of.h> + #include <linux/platform_device.h> ++#include <linux/poll.h> + #include <linux/regmap.h> + #include <linux/reset.h> + #include <linux/sched/signal.h> +@@ -95,6 +96,7 @@ struct aspeed_espi { + spinlock_t pltrstn_lock; /* for PLTRST_N signal sampling */ + wait_queue_head_t pltrstn_waitq; + char pltrstn; ++ bool pltrstn_in_avail; + }; + + static void aspeed_espi_sys_event(struct aspeed_espi *priv) +@@ -127,7 +129,10 @@ static void aspeed_espi_sys_event(struct aspeed_espi *priv) + dev_dbg(priv->dev, "SYSEVT_OOB_RST_WARN: acked\n"); + } + if (sts & ASPEED_ESPI_SYSEVT_PLTRSTN || priv->pltrstn == 'U') { ++ spin_lock(&priv->pltrstn_lock); + priv->pltrstn = (evt & ASPEED_ESPI_SYSEVT_PLTRSTN) ? '1' : '0'; ++ priv->pltrstn_in_avail = true; ++ spin_unlock(&priv->pltrstn_lock); + wake_up_interruptible(&priv->pltrstn_waitq); + dev_dbg(priv->dev, "SYSEVT_PLTRSTN: %c\n", priv->pltrstn); + } +@@ -284,41 +289,71 @@ static ssize_t aspeed_espi_pltrstn_read(struct file *filp, char __user *buf, + + spin_lock_irqsave(&priv->pltrstn_lock, flags); + +- add_wait_queue(&priv->pltrstn_waitq, &wait); +- set_current_state(TASK_INTERRUPTIBLE); +- +- old_sample = priv->pltrstn; +- +- do { +- char new_sample = priv->pltrstn; +- +- if (filp->f_flags & O_NONBLOCK || old_sample != new_sample) { +- ret = put_user(new_sample, (unsigned long __user *)buf); +- if (!ret) +- ret = sizeof(new_sample); +- } else if (signal_pending(current)) { +- ret = -ERESTARTSYS; ++ if (filp->f_flags & O_NONBLOCK) { ++ if (!priv->pltrstn_in_avail) { ++ ret = -EAGAIN; ++ goto out_unlock; + } +- +- if (!ret) { +- spin_unlock_irqrestore(&priv->pltrstn_lock, flags); +- schedule(); +- spin_lock_irqsave(&priv->pltrstn_lock, flags); ++ char data = priv->pltrstn; ++ ret = put_user(data, (unsigned long __user *)buf); ++ if (!ret){ ++ ret = sizeof(data); ++ } else{ ++ ret = -EAGAIN; + } +- } while (!ret); +- +- remove_wait_queue(&priv->pltrstn_waitq, &wait); +- set_current_state(TASK_RUNNING); +- ++ priv->pltrstn_in_avail = false; ++ } else { ++ add_wait_queue(&priv->pltrstn_waitq, &wait); ++ set_current_state(TASK_INTERRUPTIBLE); ++ ++ old_sample = priv->pltrstn; ++ ++ do { ++ char new_sample = priv->pltrstn; ++ ++ if (old_sample != new_sample) { ++ ret = put_user(new_sample, ++ (unsigned long __user *)buf); ++ if (!ret) ++ ret = sizeof(new_sample); ++ } else if (signal_pending(current)) { ++ ret = -ERESTARTSYS; ++ } ++ ++ if (!ret) { ++ spin_unlock_irqrestore(&priv->pltrstn_lock, ++ flags); ++ schedule(); ++ spin_lock_irqsave(&priv->pltrstn_lock, flags); ++ } ++ } while (!ret); ++ ++ remove_wait_queue(&priv->pltrstn_waitq, &wait); ++ set_current_state(TASK_RUNNING); ++ } ++out_unlock: + spin_unlock_irqrestore(&priv->pltrstn_lock, flags); + + return ret; + } + ++static unsigned int aspeed_espi_pltrstn_poll(struct file *file, ++ poll_table *wait) ++{ ++ struct aspeed_espi *priv = to_aspeed_espi(file); ++ unsigned int mask = 0; ++ poll_wait(file, &priv->pltrstn_waitq, wait); ++ ++ if (priv->pltrstn_in_avail) ++ mask |= POLLIN; ++ return mask; ++} ++ + static const struct file_operations aspeed_espi_pltrstn_fops = { + .owner = THIS_MODULE, + .open = aspeed_espi_pltrstn_open, + .read = aspeed_espi_pltrstn_read, ++ .poll = aspeed_espi_pltrstn_poll, + }; + + static const struct regmap_config aspeed_espi_regmap_cfg = { +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/debug.cfg b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/debug.cfg new file mode 100644 index 000000000..bbc7fa5b6 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/debug.cfg @@ -0,0 +1,2 @@ +CONFIG_DEVMEM = y +CONFIG_DEVMEM_BOOTPARAM = n diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/intel.cfg b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/intel.cfg new file mode 100644 index 000000000..5f453510a --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/intel.cfg @@ -0,0 +1,89 @@ +CONFIG_BLK_DEV_RAM=y +CONFIG_HWMON=y +CONFIG_SPI=y +CONFIG_SPI_MASTER=y +CONFIG_IIO=y +CONFIG_SENSORS_IIO_HWMON=y +CONFIG_ASPEED_ADC=y +CONFIG_SGPIO_ASPEED=y +CONFIG_CRC8=y +CONFIG_PECI=y +CONFIG_PECI_CHARDEV=y +CONFIG_PECI_ASPEED=y +CONFIG_SENSORS_PECI_CPUTEMP=y +CONFIG_SENSORS_PECI_DIMMTEMP=y +CONFIG_SENSORS_PECI_CPUPOWER=y +CONFIG_CONFIGFS_FS=y +CONFIG_BLK_DEV_RAM_SIZE=49152 +CONFIG_MAGIC_SYSRQ=y +CONFIG_MAGIC_SYSRQ_DEFAULT_ENABLE=0x01 +CONFIG_MAGIC_SYSRQ_SERIAL=y +CONFIG_ASPEED_ESPI_SLAVE=y +CONFIG_ASPEED_KCS_IPMI_BMC=y +CONFIG_I2C_SLAVE=y +CONFIG_I2C_SLAVE_MQUEUE=y +CONFIG_I2C_SLAVE_MQUEUE_MESSAGE_SIZE=256 +CONFIG_I2C_SLAVE_MQUEUE_QUEUE_SIZE=32 +CONFIG_ASPEED_BT_IPMI_BMC=n +CONFIG_ASPEED_LPC_CTRL=n +CONFIG_ASPEED_LPC_MBOX=y +CONFIG_ASPEED_LPC_SIO=y +CONFIG_JTAG=y +CONFIG_JTAG_ASPEED=y +CONFIG_FRAME_VECTOR=y +CONFIG_MEDIA_SUPPORT=y +CONFIG_MEDIA_CAMERA_SUPPORT=y +CONFIG_VIDEO_DEV=y +CONFIG_VIDEO_V4L2=y +CONFIG_V4L_PLATFORM_DRIVERS=y +CONFIG_VIDEO_ASPEED=y +CONFIG_VIDEOBUF2_CORE=y +CONFIG_VIDEOBUF2_V4L2=y +CONFIG_VIDEOBUF2_MEMOPS=y +CONFIG_VIDEOBUF2_DMA_CONTIG=y +CONFIG_MEDIA_SUBDRV_AUTOSELECT=y +CONFIG_USB_GADGET_VBUS_DRAW=2 +CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS=2 +CONFIG_USB_LIBCOMPOSITE=y +CONFIG_USB_F_HID=y +CONFIG_USB_GADGET=y +CONFIG_U_SERIAL_CONSOLE=y +CONFIG_USB_ASPEED_VHUB=y +CONFIG_USB_MASS_STORAGE=y +CONFIG_USB_CONFIGFS=y +CONFIG_USB_CONFIGFS_MASS_STORAGE=y +CONFIG_USB_CONFIGFS_F_FS=y +CONFIG_USB_CONFIGFS_F_HID=y +CONFIG_ASPEED_UART_ROUTING=y +CONFIG_ASPEED_VGA_SHAREDMEM=y +CONFIG_PWM=y +CONFIG_PWM_FTTMR010=y +CONFIG_INPUT_MISC=y +CONFIG_INPUT_PWM_BEEPER=y +CONFIG_VFAT_FS=y +CONFIG_NLS=y +CONFIG_NLS_CODEPAGE_437=y +CONFIG_NLS_ASCII=y +CONFIG_NLS_ISO8859_1=y +CONFIG_NLS_ISO8859_15=y +CONFIG_NLS_UTF8=y +CONFIG_NETWORK_FILESYSTEMS=y +CONFIG_CIFS=y +CONFIG_CIFS_XATTR=y +CONFIG_PSTORE=y +CONFIG_PSTORE_ZLIB_COMPRESS=y +CONFIG_PSTORE_RAM=y +CONFIG_FSI=n +CONFIG_FSI_MASTER_HUB=n +CONFIG_FSI_MASTER_ASPEED=n +CONFIG_FSI_SCOM=n +CONFIG_FSI_SBEFIFO=n +CONFIG_FSI_OCC=n +CONFIG_ASPEED_P2A_CTRL=n +CONFIG_USB=n +CONFIG_USB_ANNOUNCE_NEW_DEVICES=n +CONFIG_USB_DYNAMIC_MINORS=n +CONFIG_USB_EHCI_HCD=n +CONFIG_USB_EHCI_ROOT_HUB_TT=n +CONFIG_USB_EHCI_HCD_PLATFORM=n +CONFIG_IPMB_DEVICE_INTERFACE=y diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed_%.bbappend b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed_%.bbappend new file mode 100644 index 000000000..a829e544b --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed_%.bbappend @@ -0,0 +1,78 @@ +FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:" + +do_compile_prepend(){ + # device tree compiler flags + export DTC_FLAGS=-@ +} + +SRC_URI += " \ + file://intel.cfg \ + file://0001-arm-dts-add-DTS-for-Intel-ast2500-platforms.patch \ + file://0001-arm-dts-add-DTS-for-Intel-ast2600-platforms.patch \ + file://0001-arm-dts-base-aspeed-g6-dtsi-fixups.patch \ + file://0002-Enable-pass-through-on-GPIOE1-and-GPIOE3-free.patch \ + file://0003-Enable-GPIOE0-and-GPIOE2-pass-through-by-default.patch \ + file://0006-Allow-monitoring-of-power-control-input-GPIOs.patch \ + file://0007-aspeed-pwm-tacho-change-default-fan-speed.patch \ + file://0014-arm-dts-aspeed-g5-add-espi.patch \ + file://0015-New-flash-map-for-intel.patch \ + file://0016-Add-ASPEED-SGPIO-driver.patch \ + file://0017-SGPIO-DT-and-pinctrl-fixup.patch \ + file://0019-Add-I2C-IPMB-support.patch \ + file://0020-misc-aspeed-add-lpc-mbox-driver.patch \ + file://0021-Initial-Port-of-Aspeed-LPC-SIO-driver.patch \ + file://0022-Add-AST2500-eSPI-driver.patch \ + file://0028-Add-AST2500-JTAG-driver.patch \ + file://0030-Add-dump-debug-code-into-I2C-drivers.patch \ + file://0031-Add-high-speed-baud-rate-support-for-UART.patch \ + file://0032-misc-aspeed-Add-Aspeed-UART-routing-control-driver.patch \ + file://0034-arm-dts-aspeed-Swap-the-mac-nodes-numbering.patch \ + file://0035-Implement-a-memory-driver-share-memory.patch \ + file://0039-Add-Aspeed-PWM-driver-which-uses-FTTMR010-timer-IP.patch \ + file://0040-i2c-Add-mux-hold-unhold-msg-types.patch \ + file://0042-Add-bus-timeout-ms-and-retries-device-tree-propertie.patch \ + file://0043-char-ipmi-Add-clock-control-logic-into-Aspeed-LPC-BT.patch \ + file://0044-misc-Add-clock-control-logic-into-Aspeed-LPC-SNOOP-d.patch \ + file://0045-char-ipmi-Add-clock-control-logic-into-Aspeed-LPC-KC.patch \ + file://0047-misc-Block-error-printing-on-probe-defer-case-in-Asp.patch \ + file://0049-Suppress-excessive-HID-gadget-error-logs.patch \ + file://0051-Add-AST2500-JTAG-device.patch \ + file://0052-drivers-jtag-Add-JTAG-core-driver.patch \ + file://0053-Add-Aspeed-SoC-24xx-and-25xx-families-JTAG.patch \ + file://0054-Documentation-jtag-Add-bindings-for-Aspeed-SoC.patch \ + file://0055-Documentation-jtag-Add-ABI-documentation.patch \ + file://0056-Documentation-jtag-Add-JTAG-core-driver-ioctl-number.patch \ + file://0057-drivers-jtag-Add-JTAG-core-driver-Maintainers.patch \ + file://0061-i2c-aspeed-add-buffer-mode-transfer-support.patch \ + file://0062-i2c-aspeed-add-DMA-mode-transfer-support.patch \ + file://0063-i2c-aspeed-add-general-call-support.patch \ + file://0064-set-idle-disconnect-to-true-in-all-cases.patch \ + file://0068-i2c-aspeed-add-H-W-timeout-support.patch \ + file://0069-i2c-aspeed-add-SLAVE_ADDR_RECEIVED_PENDING-interrupt.patch \ + file://0070-gpio-aspeed-temporary-fix-for-gpiochip-range-setting.patch \ + file://0072-pmbus-add-fault-and-beep-attributes.patch \ + file://0073-Add-IO-statistics-to-USB-Mass-storage-gadget.patch \ + file://0075-Refine-initialization-flow-in-I2C-driver.patch \ + file://0076-arm-ast2600-add-pwm_tacho-driver-from-aspeed.patch \ + file://0077-soc-aspeed-Add-read-only-property-support.patch \ + file://0078-Fix-NCSI-driver-issue-caused-by-host-shutdown.patch \ + file://0080-i2c-aspeed-filter-garbage-interrupts-out.patch \ + file://0084-ARM-dts-aspeed-g6-add-GFX-node.patch \ + file://0085-drm-add-AST2600-GFX-support.patch \ + file://0086-ADC-linux-driver-for-AST2600.patch \ + file://0086-ARM-dts-aspeed-g6-add-video-node.patch \ + file://0087-media-aspeed-add-aspeed-ast2600-video-engine-compati.patch \ + file://0088-clk-ast2600-enable-ESPICLK-always.patch \ + file://0089-ast2600-enable-high-speed-uart-in-kernel.patch \ + file://0090-peci-cpupower-driver-1.patch \ + file://0092-SPI-Quad-IO-driver-support-AST2600.patch \ + file://0093-ipmi-ipmb_dev_int-add-quick-fix-for-raw-I2C-type-reg.patch \ + file://0094-Return-link-speed-and-duplex-settings-for-the-NCSI-c.patch \ + file://0095-pwm-and-tach-driver-changes-for-ast2600.patch \ + file://0096-Fix-truncated-WrEndPointConfig-MMIO-command.patch \ + file://0100-Mailbox-Enabling-interrupt-based-mailbox.patch \ + file://0101-Add-poll-fops-in-eSPI-driver.patch \ + " + +SRC_URI += "${@bb.utils.contains('IMAGE_FSTYPES', 'intel-pfr', 'file://0005-128MB-flashmap-for-PFR.patch', '', d)}" +SRC_URI += "${@bb.utils.contains('EXTRA_IMAGE_FEATURES', 'debug-tweaks', 'file://debug.cfg', '', d)}" |