* [PATCH v7 3/5] ARM: dts: add Protonic VT7 board
From: Oleksij Rempel @ 2020-05-20 9:29 UTC (permalink / raw)
To: Mark Rutland, Rob Herring, Sascha Hauer, Shawn Guo
Cc: devicetree, Rob Herring, Robin van der Gracht, linux-kernel,
Oleksij Rempel, NXP Linux Team, Pengutronix Kernel Team,
David Jander, Fabio Estevam, linux-arm-kernel
In-Reply-To: <20200520092937.15797-1-o.rempel@pengutronix.de>
The Protonic VT7 is a mid-class ISObus Virtual Terminal with a 7 inch
touchscreen display.
Reviewed-by: Rob Herring <robh@kernel.org>
Signed-off-by: Robin van der Gracht <robin@protonic.nl>
Signed-off-by: David Jander <david@protonic.nl>
Signed-off-by: Oleksij Rempel <o.rempel@pengutronix.de>
---
arch/arm/boot/dts/Makefile | 1 +
arch/arm/boot/dts/imx6dl-prtvt7.dts | 472 ++++++++++++++++++++++++++++
2 files changed, 473 insertions(+)
create mode 100644 arch/arm/boot/dts/imx6dl-prtvt7.dts
diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile
index 8ce744f1cbfc9..66ee89e7929f9 100644
--- a/arch/arm/boot/dts/Makefile
+++ b/arch/arm/boot/dts/Makefile
@@ -450,6 +450,7 @@ dtb-$(CONFIG_SOC_IMX6Q) += \
imx6dl-pico-hobbit.dtb \
imx6dl-pico-nymph.dtb \
imx6dl-pico-pi.dtb \
+ imx6dl-prtvt7.dtb \
imx6dl-rex-basic.dtb \
imx6dl-riotboard.dtb \
imx6dl-sabreauto.dtb \
diff --git a/arch/arm/boot/dts/imx6dl-prtvt7.dts b/arch/arm/boot/dts/imx6dl-prtvt7.dts
new file mode 100644
index 0000000000000..70e80195d648a
--- /dev/null
+++ b/arch/arm/boot/dts/imx6dl-prtvt7.dts
@@ -0,0 +1,472 @@
+// SPDX-License-Identifier: GPL-2.0-or-later OR MIT
+/*
+ * Copyright (c) 2016 Protonic Holland
+ */
+
+/dts-v1/;
+#include "imx6dl.dtsi"
+#include "imx6qdl-prti6q.dtsi"
+#include <dt-bindings/input/input.h>
+#include <dt-bindings/leds/common.h>
+#include <dt-bindings/sound/fsl-imx-audmux.h>
+
+/ {
+ model = "Protonic VT7";
+ compatible = "prt,prtvt7", "fsl,imx6dl";
+
+ memory@10000000 {
+ device_type = "memory";
+ reg = <0x10000000 0x20000000>;
+ };
+
+ backlight_lcd: backlight-lcd {
+ compatible = "pwm-backlight";
+ pwms = <&pwm1 0 500000>;
+ brightness-levels = <0 20 81 248 1000>;
+ default-brightness-level = <20>;
+ num-interpolated-steps = <21>;
+ power-supply = <®_12v_bl>;
+ enable-gpios = <&gpio4 28 GPIO_ACTIVE_HIGH>;
+ };
+
+ display {
+ compatible = "fsl,imx-parallel-display";
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_ipu1_disp>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ interface-pix-fmt = "rgb24";
+ status = "okay";
+
+ port@0 {
+ reg = <0>;
+
+ display_in: endpoint {
+ remote-endpoint = <&ipu1_di0_disp0>;
+ };
+ };
+
+ port@1 {
+ reg = <1>;
+
+ display_out: endpoint {
+ remote-endpoint = <&panel_in>;
+ };
+ };
+ };
+
+ keys {
+ compatible = "gpio-keys";
+ autorepeat;
+
+ esc {
+ label = "GPIO Key ESC";
+ linux,code = <KEY_ESC>;
+ gpios = <&gpio_pca 0 GPIO_ACTIVE_LOW>;
+ };
+
+ up {
+ label = "GPIO Key UP";
+ linux,code = <KEY_UP>;
+ gpios = <&gpio_pca 1 GPIO_ACTIVE_LOW>;
+ };
+
+ down {
+ label = "GPIO Key DOWN";
+ linux,code = <KEY_DOWN>;
+ gpios = <&gpio_pca 4 GPIO_ACTIVE_LOW>;
+ };
+
+ enter {
+ label = "GPIO Key Enter";
+ linux,code = <KEY_ENTER>;
+ gpios = <&gpio_pca 3 GPIO_ACTIVE_LOW>;
+ };
+
+ cycle {
+ label = "GPIO Key CYCLE";
+ linux,code = <KEY_CYCLEWINDOWS>;
+ gpios = <&gpio_pca 2 GPIO_ACTIVE_LOW>;
+ };
+
+ f1 {
+ label = "GPIO Key F1";
+ linux,code = <KEY_F1>;
+ gpios = <&gpio_pca 14 GPIO_ACTIVE_LOW>;
+ };
+
+ f2 {
+ label = "GPIO Key F2";
+ linux,code = <KEY_F2>;
+ gpios = <&gpio_pca 13 GPIO_ACTIVE_LOW>;
+ };
+
+ f3 {
+ label = "GPIO Key F3";
+ linux,code = <KEY_F3>;
+ gpios = <&gpio_pca 12 GPIO_ACTIVE_LOW>;
+ };
+
+ f4 {
+ label = "GPIO Key F4";
+ linux,code = <KEY_F4>;
+ gpios = <&gpio_pca 11 GPIO_ACTIVE_LOW>;
+ };
+
+ f5 {
+ label = "GPIO Key F5";
+ linux,code = <KEY_F5>;
+ gpios = <&gpio_pca 10 GPIO_ACTIVE_LOW>;
+ };
+
+ f6 {
+ label = "GPIO Key F6";
+ linux,code = <KEY_F6>;
+ gpios = <&gpio_pca 5 GPIO_ACTIVE_LOW>;
+ };
+
+ f7 {
+ label = "GPIO Key F7";
+ linux,code = <KEY_F7>;
+ gpios = <&gpio_pca 6 GPIO_ACTIVE_LOW>;
+ };
+
+ f8 {
+ label = "GPIO Key F8";
+ linux,code = <KEY_F8>;
+ gpios = <&gpio_pca 7 GPIO_ACTIVE_LOW>;
+ };
+
+ f9 {
+ label = "GPIO Key F9";
+ linux,code = <KEY_F9>;
+ gpios = <&gpio_pca 8 GPIO_ACTIVE_LOW>;
+ };
+
+ f10 {
+ label = "GPIO Key F10";
+ linux,code = <KEY_F10>;
+ gpios = <&gpio_pca 9 GPIO_ACTIVE_LOW>;
+ };
+ };
+
+ leds {
+ compatible = "gpio-leds";
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_leds>;
+
+ led-debug0 {
+ function = LED_FUNCTION_STATUS;
+ gpios = <&gpio1 8 GPIO_ACTIVE_HIGH>;
+ linux,default-trigger = "heartbeat";
+ };
+ };
+
+ panel {
+ compatible = "innolux,g070y2t0ec";
+ backlight = <&backlight_lcd>;
+
+ port {
+ panel_in: endpoint {
+ remote-endpoint = <&display_out>;
+ };
+ };
+ };
+
+ reg_12v_bl: regulator-bl-12v {
+ compatible = "regulator-fixed";
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_reg_12v_bl>;
+ regulator-name = "12v-bl";
+ regulator-min-microvolt = <12000000>;
+ regulator-max-microvolt = <12000000>;
+ gpio = <&gpio1 7 GPIO_ACTIVE_HIGH>;
+ enable-active-high;
+ };
+
+ reg_1v8: regulator-1v8 {
+ compatible = "regulator-fixed";
+ regulator-name = "1v8";
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ regulator-always-on;
+ };
+
+ sound {
+ compatible = "simple-audio-card";
+ simple-audio-card,name = "prti6q-sgtl5000";
+ simple-audio-card,format = "i2s";
+ simple-audio-card,widgets =
+ "Microphone", "Microphone Jack",
+ "Line", "Line In Jack",
+ "Headphone", "Headphone Jack",
+ "Speaker", "External Speaker";
+ simple-audio-card,routing =
+ "MIC_IN", "Microphone Jack",
+ "LINE_IN", "Line In Jack",
+ "Headphone Jack", "HP_OUT",
+ "External Speaker", "LINE_OUT";
+
+ simple-audio-card,cpu {
+ sound-dai = <&ssi1>;
+ system-clock-frequency = <0>;
+ };
+
+ simple-audio-card,codec {
+ sound-dai = <&sgtl5000>;
+ bitclock-master;
+ frame-master;
+ };
+ };
+};
+
+&clks {
+ assigned-clocks = <&clks IMX6QDL_CLK_LDB_DI0_SEL>;
+ assigned-clock-parents = <&clks IMX6QDL_CLK_PLL5_VIDEO_DIV>;
+};
+
+&ecspi2 {
+ cs-gpios = <&gpio2 26 GPIO_ACTIVE_HIGH>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_ecspi2>;
+ status = "okay";
+
+ tsc@0 {
+ compatible = "ti,tsc2046";
+ reg = <0>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_tsc>;
+ spi-max-frequency = <100000>;
+ interrupts-extended = <&gpio3 20 IRQ_TYPE_EDGE_FALLING>;
+ pendown-gpio = <&gpio3 20 GPIO_ACTIVE_HIGH>;
+ vcc-supply = <®_3v3>;
+
+ ti,vref-delay-usecs = /bits/ 16 <100>;
+
+ ti,x-min = /bits/ 16 <0>;
+ ti,x-max = /bits/ 16 <8000>;
+ ti,y-min = /bits/ 16 <0>;
+ ti,y-max = /bits/ 16 <4800>;
+ ti,x-plate-ohms = /bits/ 16 <800>;
+ ti,y-plate-ohms = /bits/ 16 <300>;
+ ti,pressure-max = /bits/ 16 <4095>;
+
+ ti,skip-samples = <2>;
+ ti,sample-period-msecs = <10>;
+ ti,report-period-msecs = <30>;
+
+ ti,filter-tolerance = <80>;
+ ti,touch-resistance-threshold = <3500>;
+ };
+};
+
+&audmux {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_audmux>;
+ status = "okay";
+
+ mux_ssi1 {
+ fsl,audmux-port = <0>;
+ fsl,port-config = <
+ IMX_AUDMUX_V2_PTCR_SYN 0
+ IMX_AUDMUX_V2_PTCR_TFSEL(2) 0
+ IMX_AUDMUX_V2_PTCR_TCSEL(2) 0
+ IMX_AUDMUX_V2_PTCR_TFSDIR 0
+ IMX_AUDMUX_V2_PTCR_TCLKDIR IMX_AUDMUX_V2_PDCR_RXDSEL(2)
+ >;
+ };
+
+ mux_pins3 {
+ fsl,audmux-port = <2>;
+ fsl,port-config = <
+ IMX_AUDMUX_V2_PTCR_SYN IMX_AUDMUX_V2_PDCR_RXDSEL(0)
+ 0 IMX_AUDMUX_V2_PDCR_TXRXEN
+ >;
+ };
+};
+
+&can1 {
+ pinctrl-0 = <&pinctrl_can1 &pinctrl_can1phy>;
+};
+
+&i2c1 {
+ sgtl5000: codec@a {
+ compatible = "fsl,sgtl5000";
+ reg = <0xa>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_codec>;
+ #sound-dai-cells = <0>;
+ clocks = <&clks 201>;
+ VDDA-supply = <®_3v3>;
+ VDDIO-supply = <®_3v3>;
+ VDDD-supply = <®_1v8>;
+ };
+};
+
+&i2c3 {
+ rtc@51 {
+ compatible = "nxp,pcf8563";
+ reg = <0x51>;
+ };
+
+ gpio_pca: gpio@74 {
+ compatible = "nxp,pca9539";
+ reg = <0x74>;
+ interrupts-extended = <&gpio4 5 IRQ_TYPE_LEVEL_LOW>;
+ #gpio-cells = <2>;
+ gpio-controller;
+ };
+};
+
+&ipu1 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_ipu1_csi0>;
+ status = "okay";
+};
+
+&ipu1_di0_disp0 {
+ remote-endpoint = <&display_in>;
+};
+
+&pwm1 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_pwm1>;
+ status = "okay";
+};
+
+&snvs_poweroff {
+ status = "okay";
+};
+
+&snvs_pwrkey {
+ status = "okay";
+};
+
+&ssi1 {
+ #sound-dai-cells = <0>;
+ fsl,mode = "i2s-slave";
+ status = "okay";
+};
+
+&usbh1 {
+ status = "disabled";
+};
+
+&vpu {
+ status = "disabled";
+};
+
+&iomuxc {
+ pinctrl_audmux: audmuxgrp {
+ fsl,pins = <
+ MX6QDL_PAD_CSI0_MCLK__CCM_CLKO1 0x030b0
+ MX6QDL_PAD_CSI0_DAT7__AUD3_RXD 0x130b0
+ MX6QDL_PAD_CSI0_DAT4__AUD3_TXC 0x130b0
+ MX6QDL_PAD_CSI0_DAT5__AUD3_TXD 0x110b0
+ MX6QDL_PAD_CSI0_DAT6__AUD3_TXFS 0x130b0
+ >;
+ };
+
+ pinctrl_can1phy: can1phy {
+ fsl,pins = <
+ /* CAN1_SR */
+ MX6QDL_PAD_KEY_COL3__GPIO4_IO12 0x13070
+ /* CAN1_TERM */
+ MX6QDL_PAD_GPIO_0__GPIO1_IO00 0x1b0b0
+ >;
+ };
+
+ pinctrl_codec: codecgrp {
+ fsl,pins = <
+ /* AUDIO_nRESET */
+ MX6QDL_PAD_CSI0_VSYNC__GPIO5_IO21 0x1f0b0
+ >;
+ };
+
+ pinctrl_ecspi2: ecspi2grp {
+ fsl,pins = <
+ MX6QDL_PAD_EIM_OE__ECSPI2_MISO 0x100b1
+ MX6QDL_PAD_EIM_CS0__ECSPI2_SCLK 0x100b1
+ MX6QDL_PAD_EIM_CS1__ECSPI2_MOSI 0x100b1
+ MX6QDL_PAD_EIM_RW__GPIO2_IO26 0x000b1
+ >;
+ };
+
+ pinctrl_ipu1_csi0: ipu1csi0grp {
+ fsl,pins = <
+ MX6QDL_PAD_CSI0_DAT12__IPU1_CSI0_DATA12 0x1b0b0
+ MX6QDL_PAD_CSI0_DAT13__IPU1_CSI0_DATA13 0x1b0b0
+ MX6QDL_PAD_CSI0_DAT14__IPU1_CSI0_DATA14 0x1b0b0
+ MX6QDL_PAD_CSI0_DAT15__IPU1_CSI0_DATA15 0x1b0b0
+ MX6QDL_PAD_CSI0_DAT16__IPU1_CSI0_DATA16 0x1b0b0
+ MX6QDL_PAD_CSI0_DAT17__IPU1_CSI0_DATA17 0x1b0b0
+ MX6QDL_PAD_CSI0_DAT18__IPU1_CSI0_DATA18 0x1b0b0
+ MX6QDL_PAD_CSI0_DAT19__IPU1_CSI0_DATA19 0x1b0b0
+ MX6QDL_PAD_CSI0_PIXCLK__IPU1_CSI0_PIXCLK 0x1b0b0
+ /* ITU656_nRESET */
+ MX6QDL_PAD_GPIO_2__GPIO1_IO02 0x1b0b0
+ /* ITU656_nPDN */
+ MX6QDL_PAD_CSI0_DATA_EN__GPIO5_IO20 0x1b0b0
+ >;
+ };
+
+ pinctrl_ipu1_disp: ipudisp1grp {
+ fsl,pins = <
+ MX6QDL_PAD_DI0_DISP_CLK__IPU1_DI0_DISP_CLK 0xb0
+ MX6QDL_PAD_DI0_PIN15__IPU1_DI0_PIN15 0xb0
+
+ MX6QDL_PAD_DISP0_DAT0__IPU1_DISP0_DATA00 0xb0
+ MX6QDL_PAD_DISP0_DAT1__IPU1_DISP0_DATA01 0xb0
+ MX6QDL_PAD_DISP0_DAT2__IPU1_DISP0_DATA02 0xb0
+ MX6QDL_PAD_DISP0_DAT3__IPU1_DISP0_DATA03 0xb0
+ MX6QDL_PAD_DISP0_DAT4__IPU1_DISP0_DATA04 0xb0
+ MX6QDL_PAD_DISP0_DAT5__IPU1_DISP0_DATA05 0xb0
+ MX6QDL_PAD_DISP0_DAT6__IPU1_DISP0_DATA06 0xb0
+ MX6QDL_PAD_DISP0_DAT7__IPU1_DISP0_DATA07 0xb0
+
+ MX6QDL_PAD_DISP0_DAT8__IPU1_DISP0_DATA08 0xb0
+ MX6QDL_PAD_DISP0_DAT9__IPU1_DISP0_DATA09 0xb0
+ MX6QDL_PAD_DISP0_DAT10__IPU1_DISP0_DATA10 0xb0
+ MX6QDL_PAD_DISP0_DAT11__IPU1_DISP0_DATA11 0xb0
+ MX6QDL_PAD_DISP0_DAT12__IPU1_DISP0_DATA12 0xb0
+ MX6QDL_PAD_DISP0_DAT13__IPU1_DISP0_DATA13 0xb0
+ MX6QDL_PAD_DISP0_DAT14__IPU1_DISP0_DATA14 0xb0
+ MX6QDL_PAD_DISP0_DAT15__IPU1_DISP0_DATA15 0xb0
+
+ MX6QDL_PAD_DISP0_DAT16__IPU1_DISP0_DATA16 0xb0
+ MX6QDL_PAD_DISP0_DAT17__IPU1_DISP0_DATA17 0xb0
+ MX6QDL_PAD_DISP0_DAT18__IPU1_DISP0_DATA18 0xb0
+ MX6QDL_PAD_DISP0_DAT19__IPU1_DISP0_DATA19 0xb0
+ MX6QDL_PAD_DISP0_DAT20__IPU1_DISP0_DATA20 0xb0
+ MX6QDL_PAD_DISP0_DAT21__IPU1_DISP0_DATA21 0xb0
+ MX6QDL_PAD_DISP0_DAT22__IPU1_DISP0_DATA22 0xb0
+ MX6QDL_PAD_DISP0_DAT23__IPU1_DISP0_DATA23 0xb0
+ >;
+ };
+
+ pinctrl_leds: ledsgrp {
+ fsl,pins = <
+ MX6QDL_PAD_GPIO_8__GPIO1_IO08 0x1b0b0
+ >;
+ };
+
+ pinctrl_pwm1: pwm1grp {
+ fsl,pins = <
+ MX6QDL_PAD_GPIO_9__PWM1_OUT 0x1b0b0
+ >;
+ };
+
+ pinctrl_reg_12v_bl: 12blgrp {
+ fsl,pins = <
+ MX6QDL_PAD_GPIO_7__GPIO1_IO07 0x1b0b0
+ >;
+ };
+
+ pinctrl_tsc: tscgrp {
+
+ fsl,pins = <
+ MX6QDL_PAD_EIM_D20__GPIO3_IO20 0x1b0b0
+ MX6QDL_PAD_EIM_EB2__GPIO2_IO30 0x1b0b0
+ >;
+ };
+};
--
2.26.2
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* [PATCH v7 2/5] ARM: dts: add Protonic WD2 board
From: Oleksij Rempel @ 2020-05-20 9:29 UTC (permalink / raw)
To: Mark Rutland, Rob Herring, Sascha Hauer, Shawn Guo
Cc: devicetree, Rob Herring, linux-kernel, Oleksij Rempel,
NXP Linux Team, Pengutronix Kernel Team, David Jander,
Fabio Estevam, linux-arm-kernel
In-Reply-To: <20200520092937.15797-1-o.rempel@pengutronix.de>
Add support for the Protonic WD2 board, which is an internal development
platform for low-cost agricultural Virtual Terminals based on COTS tablets
and web applications.
It inherits from the PRTI6Q base class.
Reviewed-by: Rob Herring <robh@kernel.org>
Signed-off-by: David Jander <david@protonic.nl>
Signed-off-by: Oleksij Rempel <o.rempel@pengutronix.de>
---
arch/arm/boot/dts/Makefile | 1 +
arch/arm/boot/dts/imx6q-prtwd2.dts | 188 +++++++++++++++++++++++++++++
2 files changed, 189 insertions(+)
create mode 100644 arch/arm/boot/dts/imx6q-prtwd2.dts
diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile
index 206a36a50575e..8ce744f1cbfc9 100644
--- a/arch/arm/boot/dts/Makefile
+++ b/arch/arm/boot/dts/Makefile
@@ -539,6 +539,7 @@ dtb-$(CONFIG_SOC_IMX6Q) += \
imx6q-pico-pi.dtb \
imx6q-pistachio.dtb \
imx6q-prti6q.dtb \
+ imx6q-prtwd2.dtb \
imx6q-rex-pro.dtb \
imx6q-sabreauto.dtb \
imx6q-sabrelite.dtb \
diff --git a/arch/arm/boot/dts/imx6q-prtwd2.dts b/arch/arm/boot/dts/imx6q-prtwd2.dts
new file mode 100644
index 0000000000000..fd9f457a273e2
--- /dev/null
+++ b/arch/arm/boot/dts/imx6q-prtwd2.dts
@@ -0,0 +1,188 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2018 Protonic Holland
+ */
+
+/dts-v1/;
+#include "imx6q.dtsi"
+#include "imx6qdl-prti6q.dtsi"
+#include <dt-bindings/leds/common.h>
+
+/ {
+ model = "Protonic WD2 board";
+ compatible = "prt,prtwd2", "fsl,imx6q";
+
+ memory@10000000 {
+ device_type = "memory";
+ reg = <0x10000000 0x20000000>;
+ };
+
+ memory@80000000 {
+ device_type = "memory";
+ reg = <0x80000000 0x20000000>;
+ };
+
+ usdhc2_wifi_pwrseq: usdhc2_wifi_pwrseq {
+ compatible = "mmc-pwrseq-simple";
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_wifi_npd>;
+ reset-gpios = <&gpio6 10 GPIO_ACTIVE_LOW>;
+ };
+
+ /* PRTWD2 rev 1 bitbang I2C for Ethernet Switch */
+ i2c@4 {
+ compatible = "i2c-gpio";
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_i2c4>;
+ sda-gpios = <&gpio1 22 (GPIO_ACTIVE_HIGH|GPIO_OPEN_DRAIN)>;
+ scl-gpios = <&gpio1 31 GPIO_ACTIVE_HIGH>;
+ i2c-gpio,delay-us = <20>; /* ~10 kHz */
+ i2c-gpio,scl-output-only;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ };
+};
+
+&can1 {
+ pinctrl-0 = <&pinctrl_can1 &pinctrl_can1phy>;
+};
+
+&fec {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_enet>;
+ phy-mode = "rmii";
+ clocks = <&clks IMX6QDL_CLK_ENET>,
+ <&clks IMX6QDL_CLK_ENET>;
+ clock-names = "ipg", "ahb";
+ status = "okay";
+
+ fixed-link {
+ speed = <100>;
+ pause;
+ full-duplex;
+ };
+};
+
+&i2c3 {
+ adc@49 {
+ compatible = "ti,ads1015";
+ reg = <0x49>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ /* V in */
+ channel@4 {
+ reg = <4>;
+ ti,gain = <1>;
+ ti,datarate = <3>;
+ };
+
+ /* I charge */
+ channel@5 {
+ reg = <5>;
+ ti,gain = <1>;
+ ti,datarate = <3>;
+ };
+
+ /* V bus */
+ channel@6 {
+ reg = <6>;
+ ti,gain = <1>;
+ ti,datarate = <3>;
+ };
+
+ /* nc */
+ channel@7 {
+ reg = <7>;
+ ti,gain = <1>;
+ ti,datarate = <3>;
+ };
+ };
+};
+
+&usdhc2 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_usdhc2>;
+ non-removable;
+ no-1-8-v;
+ non-removable;
+ mmc-pwrseq = <&usdhc2_wifi_pwrseq>;
+ pm-ignore-notify;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ status = "okay";
+
+ wifi@1 {
+ compatible = "brcm,bcm4329-fmac";
+ reg = <1>;
+ };
+};
+
+&iomuxc {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_usb_eth_chg>;
+
+ pinctrl_can1phy: can1phy {
+ fsl,pins = <
+ /* CAN1_SR */
+ MX6QDL_PAD_KEY_COL3__GPIO4_IO12 0x13070
+ >;
+ };
+
+ pinctrl_enet: enetgrp {
+ fsl,pins = <
+ /* MX6QDL_ENET_PINGRP4 */
+ MX6QDL_PAD_ENET_RXD0__ENET_RX_DATA0 0x1b0b0
+ MX6QDL_PAD_ENET_RXD1__ENET_RX_DATA1 0x1b0b0
+ MX6QDL_PAD_ENET_RX_ER__ENET_RX_ER 0x130b0
+ MX6QDL_PAD_ENET_TX_EN__ENET_TX_EN 0x1b0b0
+ MX6QDL_PAD_ENET_TXD0__ENET_TX_DATA0 0x1b0b0
+ MX6QDL_PAD_ENET_TXD1__ENET_TX_DATA1 0x1b0b0
+ MX6QDL_PAD_ENET_CRS_DV__ENET_RX_EN 0x1b0b0
+
+ MX6QDL_PAD_GPIO_16__ENET_REF_CLK 0x1b0b0
+ /* Phy reset */
+ MX6QDL_PAD_CSI0_DAT4__GPIO5_IO22 0x1b0b0
+ /* nINTRP */
+ MX6QDL_PAD_CSI0_DAT5__GPIO5_IO23 0x1b0b0
+
+ MX6QDL_PAD_ENET_MDIO__ENET_MDIO 0x10030
+ MX6QDL_PAD_ENET_MDC__ENET_MDC 0x10030
+ >;
+ };
+
+ pinctrl_i2c4: i2c4grp {
+ fsl,pins = <
+ MX6QDL_PAD_ENET_MDIO__GPIO1_IO22 0x1f8b0
+ MX6QDL_PAD_ENET_MDC__GPIO1_IO31 0x1f8b0
+ >;
+ };
+
+ pinctrl_usb_eth_chg: usbethchggrp {
+ fsl,pins = <
+ /* USB charging control */
+ MX6QDL_PAD_NANDF_CS0__GPIO6_IO11 0x130b0
+ MX6QDL_PAD_NANDF_CS1__GPIO6_IO14 0x130b0
+ MX6QDL_PAD_NANDF_CS2__GPIO6_IO15 0x130b0
+ MX6QDL_PAD_NANDF_CS3__GPIO6_IO16 0x130b0
+ >;
+ };
+
+ pinctrl_usdhc2: usdhc2grp {
+ fsl,pins = <
+ MX6QDL_PAD_SD2_CMD__SD2_CMD 0x170b9
+ MX6QDL_PAD_SD2_CLK__SD2_CLK 0x100b9
+ MX6QDL_PAD_SD2_DAT0__SD2_DATA0 0x170b9
+ MX6QDL_PAD_SD2_DAT1__SD2_DATA1 0x170b9
+ MX6QDL_PAD_SD2_DAT2__SD2_DATA2 0x170b9
+ MX6QDL_PAD_SD2_DAT3__SD2_DATA3 0x170b9
+ >;
+ };
+
+ pinctrl_wifi_npd: wifinpd {
+ fsl,pins = <
+ /* WL_REG_ON */
+ MX6QDL_PAD_NANDF_RB0__GPIO6_IO10 0x13069
+ >;
+ };
+};
--
2.26.2
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* RE: [PATCH V3] dt-bindings: thermal: Convert i.MX to json-schema
From: Aisheng Dong @ 2020-05-20 9:31 UTC (permalink / raw)
To: Anson Huang, rui.zhang@intel.com, daniel.lezcano@linaro.org,
amit.kucheria@verdurent.com, robh+dt@kernel.org,
shawnguo@kernel.org, s.hauer@pengutronix.de,
kernel@pengutronix.de, festevam@gmail.com,
linux-pm@vger.kernel.org, devicetree@vger.kernel.org,
linux-arm-kernel@lists.infradead.org,
linux-kernel@vger.kernel.org
Cc: dl-linux-imx
In-Reply-To: <1589962619-28256-1-git-send-email-Anson.Huang@nxp.com>
> From: Anson Huang <Anson.Huang@nxp.com>
> Sent: Wednesday, May 20, 2020 4:17 PM
>
> Convert the i.MX thermal binding to DT schema format using json-schema
>
> Signed-off-by: Anson Huang <Anson.Huang@nxp.com>
> ---
> Changes since V2:
> - remove unnecessary property in compatible;
> - add detail description for interrupts;
> - add description for each item of nvmem-cells;
> - add more detail for "fsl,tempmon" description.
[...]
> +title: NXP i.MX Thermal Binding
> +
> +maintainers:
> + - Shawn Guo <shawn.guo@linaro.org>
> + - Anson Huang <Anson.Huang@nxp.com>
> +
> +properties:
> + compatible:
> + enum:
> + - fsl,imx6q-tempmon
> + - fsl,imx6sx-tempmon
> + - fsl,imx7d-tempmon
> +
> + interrupts:
> + description: |
> + The interrupt output of the controller, i.MX6Q has IRQ_HIGH which
> + will be triggered when temperature is higher than high threshold,
> + i.MX6SX and i.MX7S/D have two more IRQs than i.MX6Q, one is
> IRQ_LOW
> + and the other is IRQ_PANIC, when temperature is lower than low
> + threshold, IRQ_LOW will be triggered, when temperature is higher
> + than panic threshold, IRQ_PANIC will be triggered, and system can
> + be configured to auto reboot by SRC module for IRQ_PANIC. IRQ_HIGH,
> + IRQ_LOW and IRQ_PANIC share same interrupt output of controller.
> + maxItems: 1
> +
> + nvmem-cells:
> + items:
> + - description: Phandle to the calibration data provided by ocotp
> + - description: Phandle to the temperature grade provided by ocotp
> +
> + nvmem-cell-names:
> + items:
> + - const: calib
> + - const: temp_grade
> +
> + fsl,tempmon:
> + $ref: '/schemas/types.yaml#/definitions/phandle'
> + description: Phandle to the temperature sensor register map node.
This still looks vauge.
How about " Phandle to anatop system controller node"?
Otherwise, you can add my tag:
Reviewed-by: Dong Aisheng <aisheng.dong@nxp.com>
Regards
Aisheng
> +
> + fsl,tempmon-data:
> + $ref: '/schemas/types.yaml#/definitions/phandle'
> + description: |
> + Deprecated property, phandle pointer to fuse controller that contains
> + TEMPMON calibration data, e.g. OCOTP on imx6q. The details about
> + calibration data can be found in SoC Reference Manual.
> + deprecated: true
> +
> + clocks:
> + maxItems: 1
> +
> +required:
> + - compatible
> + - interrupts
> + - fsl,tempmon
> + - nvmem-cells
> + - nvmem-cell-names
> +
> +additionalProperties: false
> +
> +examples:
> + - |
> + #include <dt-bindings/clock/imx6sx-clock.h>
> + #include <dt-bindings/interrupt-controller/arm-gic.h>
> +
> + efuse@21bc000 {
> + #address-cells = <1>;
> + #size-cells = <1>;
> + compatible = "fsl,imx6sx-ocotp", "syscon";
> + reg = <0x021bc000 0x4000>;
> + clocks = <&clks IMX6SX_CLK_OCOTP>;
> +
> + tempmon_calib: calib@38 {
> + reg = <0x38 4>;
> + };
> +
> + tempmon_temp_grade: temp-grade@20 {
> + reg = <0x20 4>;
> + };
> + };
> +
> + anatop@20c8000 {
> + compatible = "fsl,imx6q-anatop", "syscon", "simple-mfd";
> + reg = <0x020c8000 0x1000>;
> + interrupts = <0 49 IRQ_TYPE_LEVEL_HIGH>,
> + <0 54 IRQ_TYPE_LEVEL_HIGH>,
> + <0 127 IRQ_TYPE_LEVEL_HIGH>;
> +
> + tempmon {
> + compatible = "fsl,imx6sx-tempmon";
> + interrupts = <GIC_SPI 49 IRQ_TYPE_LEVEL_HIGH>;
> + fsl,tempmon = <&anatop>;
> + nvmem-cells = <&tempmon_calib>,
> <&tempmon_temp_grade>;
> + nvmem-cell-names = "calib", "temp_grade";
> + clocks = <&clks IMX6SX_CLK_PLL3_USB_OTG>;
> + };
> + };
> --
> 2.7.4
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH] iomm/arm-smmu: Add stall implementation hook
From: Sai Prakash Ranjan @ 2020-05-20 9:32 UTC (permalink / raw)
To: Rob Clark, Will Deacon, Robin Murphy
Cc: linux-arm-msm, Joerg Roedel,
list@263.net:IOMMU DRIVERS , Joerg Roedel <joro@8bytes.org>,,
moderated list:ARM/FREESCALE IMX / MXC ARM ARCHITECTURE,
Linux Kernel Mailing List
In-Reply-To: <CAF6AEGuzBtj+srindmOvhaom5BdS2adLaOF=v_MtguMja14V2w@mail.gmail.com>
On 2020-05-19 20:41, Rob Clark wrote:
> On Tue, May 19, 2020 at 2:26 AM Sai Prakash Ranjan
> <saiprakash.ranjan@codeaurora.org> wrote:
>>
>> Hi Will,
>>
>> On 2020-05-18 21:15, Will Deacon wrote:
>> > On Mon, May 11, 2020 at 11:30:08AM -0600, Jordan Crouse wrote:
>> >> On Fri, May 08, 2020 at 08:40:40AM -0700, Rob Clark wrote:
>> >> > On Fri, May 8, 2020 at 8:32 AM Rob Clark <robdclark@gmail.com> wrote:
>> >> > >
>> >> > > On Thu, May 7, 2020 at 5:54 AM Will Deacon <will@kernel.org> wrote:
>> >> > > >
>> >> > > > On Thu, May 07, 2020 at 11:55:54AM +0100, Robin Murphy wrote:
>> >> > > > > On 2020-05-07 11:14 am, Sai Prakash Ranjan wrote:
>> >> > > > > > On 2020-04-22 01:50, Sai Prakash Ranjan wrote:
>> >> > > > > > > Add stall implementation hook to enable stalling
>> >> > > > > > > faults on QCOM platforms which supports it without
>> >> > > > > > > causing any kind of hardware mishaps. Without this
>> >> > > > > > > on QCOM platforms, GPU faults can cause unrelated
>> >> > > > > > > GPU memory accesses to return zeroes. This has the
>> >> > > > > > > unfortunate result of command-stream reads from CP
>> >> > > > > > > getting invalid data, causing a cascade of fail.
>> >> > > > >
>> >> > > > > I think this came up before, but something about this rationale doesn't add
>> >> > > > > up - we're not *using* stalls at all, we're still terminating faulting
>> >> > > > > transactions unconditionally; we're just using CFCFG to terminate them with
>> >> > > > > a slight delay, rather than immediately. It's really not clear how or why
>> >> > > > > that makes a difference. Is it a GPU bug? Or an SMMU bug? Is this reliable
>> >> > > > > (or even a documented workaround for something), or might things start
>> >> > > > > blowing up again if any other behaviour subtly changes? I'm not dead set
>> >> > > > > against adding this, but I'd *really* like to have a lot more confidence in
>> >> > > > > it.
>> >> > > >
>> >> > > > Rob mentioned something about the "bus returning zeroes" before, but I agree
>> >> > > > that we need more information so that we can reason about this and maintain
>> >> > > > the code as the driver continues to change. That needs to be a comment in
>> >> > > > the driver, and I don't think "but android seems to work" is a good enough
>> >> > > > justification. There was some interaction with HUPCF as well.
>> >> > >
>> >> > > The issue is that there are multiple parallel memory accesses
>> >> > > happening at the same time, for example CP (the cmdstream processor)
>> >> > > will be reading ahead and setting things up for the next draw or
>> >> > > compute grid, in parallel with some memory accesses from the shader
>> >> > > which could trigger a fault. (And with faults triggered by something
>> >> > > in the shader, there are *many* shader threads running in parallel so
>> >> > > those tend to generate a big number of faults at the same time.)
>> >> > >
>> >> > > We need either CFCFG or HUPCF, otherwise what I have observed is that
>> >> > > while the fault happens, CP's memory access will start returning
>> >> > > zero's instead of valid cmdstream data, which triggers a GPU hang. I
>> >> > > can't say whether this is something unique to qcom's implementation of
>> >> > > the smmu spec or not.
>> >> > >
>> >> > > *Often* a fault is the result of the usermode gl/vk/cl driver bug,
>> >> > > although I don't think that is an argument against fixing this in the
>> >> > > smmu driver.. I've been carrying around a local patch to set HUPCF for
>> >> > > *years* because debugging usermode driver issues is so much harder
>> >> > > without. But there are some APIs where faults can be caused by the
>> >> > > user's app on top of the usermode driver.
>> >> > >
>> >> >
>> >> > Also, I'll add to that, a big wish of mine is to have stall with the
>> >> > ability to resume later from a wq context. That would enable me to
>> >> > hook in the gpu crash dump handling for faults, which would make
>> >> > debugging these sorts of issues much easier. I think I posted a
>> >> > prototype of this quite some time back, which would schedule a worker
>> >> > on the first fault (since there are cases where you see 1000's of
>> >> > faults at once), which grabbed some information about the currently
>> >> > executing submit and some gpu registers to indicate *where* in the
>> >> > submit (a single submit could have 100's or 1000's of draws), and then
>> >> > resumed the iommu cb.
>> >> >
>> >> > (This would ofc eventually be useful for svm type things.. I expect
>> >> > we'll eventually care about that too.)
>> >>
>> >> Rob is right about HUPCF. Due to the parallel nature of the command
>> >> processor
>> >> there is always a very good chance that a CP access is somewhere in
>> >> the bus so
>> >> any pagefault is usually a death sentence. The GPU context bank would
>> >> always
>> >> want HUPCF set to 1.
>> >
>> > So this sounds like an erratum to me, and I'm happy to set HUPCF if we
>> > detect the broken implementation. However, it will need an entry in
>> > Documentation/arm64/silicon-errata.rst and a decent comment in the
>> > driver
>> > to explain what we're doing and why.
>> >
>>
>> AFAIK there is no erratum documented internally for this behaviour and
>> this
>> exists from MSM8996 SoC time and errata usually don't survive this
>> long
>> across generation of SoCs and there is no point for us in disguising
>> it.
>
> possibly longer, qcom_iommu sets CFCFG..
>
Oh right, I was still in college when those SoCs were released ;)
>> Is it OK if we clearly mention it as the "design limitation" or some
>> other
>> term which we can agree upon along with the description which Rob and
>> Jordan
>> provided for setting HUPCF in the driver when we add the set_hupcf
>> callback?
>
> I'm not too picky on what we call it, but afaict it has been this way
> since the beginning of time, rather than specific to a certain SoC or
> generation of SoCs. So it doesn't seem like the hw designers consider
> it a bug.
>
> (I'm not sure what the expected behavior is.. nor if any other SMMU
> implementation encounters this sort of situation..)
>
Yes, that was my point as well that its probably not counted as a bug
by the hw designers. So I'm going to post setting HUPCF on QCOM
implementation with clear comments based on yours and Jordan's
description
of this problem, but I wanted to have a way to set this only for GPU
context
bank and not GMU as Jordan mentioned earlier that GMU doesnt need HUPCF
set.
I was checking as to how do we map cb to device, if it was possible then
we can have
a compatibility thing like we did for identity mapping. Any ideas Robin?
Thanks,
Sai
--
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a
member
of Code Aurora Forum, hosted by The Linux Foundation
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH v2 1/2] arm64: vdso: Don't prefix sigreturn trampoline with a BTI C instruction
From: Dave Martin @ 2020-05-20 9:33 UTC (permalink / raw)
To: Will Deacon
Cc: Tamas Zsoldos, Mark Brown, kernel-team, linux-arm-kernel,
Daniel Kiss
In-Reply-To: <20200519162821.16857-2-will@kernel.org>
On Tue, May 19, 2020 at 05:28:20PM +0100, Will Deacon wrote:
> For better or worse, GDB relies on the exact instruction sequence in the
> VDSO sigreturn trampoline in order to unwind from signals correctly.
> Commit c91db232da48 ("arm64: vdso: Convert to modern assembler annotations")
> unfortunately added a BTI C instruction to the start of __kernel_rt_sigreturn,
> which breaks this check. Thankfully, it's also not required, since the
> trampoline is called from a RET instruction when returning from the signal
> handler
>
> Remove the unnecessary BTI C instruction from __kernel_rt_sigreturn,
> and do the same for the 32-bit VDSO as well for good measure.
>
> Cc: Dave Martin <dave.martin@arm.com>
> Cc: Mark Brown <broonie@kernel.org>
> Cc: Daniel Kiss <daniel.kiss@arm.com>
> Cc: Tamas Zsoldos <tamas.zsoldos@arm.com>
> Fixes: c91db232da48 ("arm64: vdso: Convert to modern assembler annotations")
> Signed-off-by: Will Deacon <will@kernel.org>
> ---
> arch/arm64/kernel/vdso/sigreturn.S | 11 +++++++++--
> arch/arm64/kernel/vdso32/sigreturn.S | 16 ++++++++--------
> 2 files changed, 17 insertions(+), 10 deletions(-)
>
> diff --git a/arch/arm64/kernel/vdso/sigreturn.S b/arch/arm64/kernel/vdso/sigreturn.S
> index 3fb13b81f780..0c921130002a 100644
> --- a/arch/arm64/kernel/vdso/sigreturn.S
> +++ b/arch/arm64/kernel/vdso/sigreturn.S
> @@ -15,7 +15,14 @@
> .text
>
> nop
> -SYM_FUNC_START(__kernel_rt_sigreturn)
> +/*
> + * GDB relies on being able to identify the sigreturn instruction sequence to
> + * unwind from signal handlers. We cannot, therefore, use SYM_FUNC_START()
> + * here, as it will emit a BTI C instruction and break the unwinder. Thankfully,
> + * this function is only ever called from a RET and so omitting the landing pad
> + * is perfectly fine.
> + */
Can we cross-reference or duplicate (perhaps abridged) this comment for
vdso32?
Can we also fix the comment by the definition of SYM_FUNC_START()?
SYM_FUNC_START() supersedes ENTRY only for PCS-conformant function entry
points. Any code with a wacky special-case interface should not not be
using this.
[...]
> +SYM_CODE_START(__kernel_rt_sigreturn)
> .cfi_startproc
> .cfi_signal_frame
> .cfi_def_cfa x29, 0
> @@ -24,6 +31,6 @@ SYM_FUNC_START(__kernel_rt_sigreturn)
> mov x8, #__NR_rt_sigreturn
> svc #0
> .cfi_endproc
> -SYM_FUNC_END(__kernel_rt_sigreturn)
> +SYM_CODE_END(__kernel_rt_sigreturn)
>
> emit_aarch64_feature_1_and
> diff --git a/arch/arm64/kernel/vdso32/sigreturn.S b/arch/arm64/kernel/vdso32/sigreturn.S
> index 620524969696..b36d4e2267a3 100644
> --- a/arch/arm64/kernel/vdso32/sigreturn.S
> +++ b/arch/arm64/kernel/vdso32/sigreturn.S
> @@ -17,39 +17,39 @@
> .save {r0-r15}
> .pad #COMPAT_SIGFRAME_REGS_OFFSET
> nop
> -SYM_FUNC_START(__kernel_sigreturn_arm)
> +SYM_CODE_START(__kernel_sigreturn_arm)
...although do we actually need this? 32-bit doesn't have BTI.
But for the reasons given above, this is not a "function" and so
SYM_FUNC_START() is trap for future maintenance even if it makes no
difference now.
[...]
Either way,
Reviewed-by: Dave Martin <Dave.Martin@arm.com>
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH v5 09/19] mtd: spi-nor: sfdp: parse xSPI Profile 1.0 table
From: masonccyang @ 2020-05-20 9:40 UTC (permalink / raw)
To: Pratyush Yadav
Cc: Alexandre Belloni, Vignesh Raghavendra, Tudor Ambarus, juliensu,
Richard Weinberger, Mark Brown, Sekhar Nori, linux-kernel,
linux-spi, Ludovic Desroches, Boris Brezillon, linux-mediatek,
Miquel Raynal, Matthias Brugger, linux-mtd, linux-arm-kernel
In-Reply-To: <20200520085534.yra4f5ww5xs23c4j@ti.com>
Hi Pratyush,
> > > +/**
> > > + * spi_nor_parse_profile1() - parse the xSPI Profile 1.0 table
> > > + * @nor: pointer to a 'struct spi_nor'
> > > + * @param_header: pointer to the 'struct sfdp_parameter_header'
> > describing
> > > + * the 4-Byte Address Instruction Table length and version.
> > > + * @params: pointer to the 'struct spi_nor_flash_parameter' to
be.
> > > + *
> > > + * Return: 0 on success, -errno otherwise.
> > > + */
> > > +static int spi_nor_parse_profile1(struct spi_nor *nor,
> > > + const struct sfdp_parameter_header *profile1_header,
> > > + struct spi_nor_flash_parameter *params)
> > > +{
> > > + u32 *table, opcode, addr;
> > > + size_t len;
> > > + int ret, i;
> > > +
> > > + len = profile1_header->length * sizeof(*table);
> > > + table = kmalloc(len, GFP_KERNEL);
> > > + if (!table)
> > > + return -ENOMEM;
> > > +
> > > + addr = SFDP_PARAM_HEADER_PTP(profile1_header);
> > > + ret = spi_nor_read_sfdp(nor, addr, len, table);
> > > + if (ret)
> > > + goto out;
> > > +
> > > + /* Fix endianness of the table DWORDs. */
> > > + for (i = 0; i < profile1_header->length; i++)
> > > + table[i] = le32_to_cpu(table[i]);
> > > +
> > > + /* Get 8D-8D-8D fast read opcode and dummy cycles. */
> > > + opcode = FIELD_GET(PROFILE1_DWORD1_RD_FAST_CMD, table[0]);
> > > +
> > > + /*
> > > + * Update the fast read settings. We set the default dummy
cycles to
> > 20
> > > + * here. Flashes can change this value if they need to when
enabling
> > > + * octal mode.
> > > + */
> > > + spi_nor_set_read_settings(¶ms->reads[SNOR_CMD_READ_8_8_8_DTR],
> > > + 0, 20, opcode,
> > > + SNOR_PROTO_8_8_8_DTR);
> > > +
> >
> >
> > I thought we have a agreement that only do parse here, no other read
> > parameters setting.
>
> Yes, and I considered it. But it didn't make much sense to me to
> introduce an extra member in struct spi_nor just to make this call in
> some other function later.
>
> Why exactly do you think doing this here is bad? The way I see it, we
> avoid carrying around an extra member in spi_nor and this also allows
> flashes to change the read settings easily in a post-sfdp hook. The
> 4bait parsing function does something similar.
I think it's not a question for good or bad.
4bait parsing function parse the 4-Byte Address Instruction Table
and set up read/pp parameters there for sure.
Here we give the function name spi_nor_parse_profile1() but also
do others setting that has nothing to do with it,
it seems not good for SW module design.
oh, it's my humble opinion.
>
> What are the benefits of doing it otherwise?
For other Octal Flash like mx25*
>
> Note that I did remove HWCAPS selection from here, which did seem like a
> sane idea.
>
> > Driver should get dummy cycles used for various frequencies
> > from 4th and 5th DWORD of xSPI table.[1]
> >
> > [1]
> >
https://patchwork.ozlabs.org/project/linux-mtd/patch/1587451187-6889-3-git-
> send-email-masonccyang@mxic.com.tw/
> >
> >
> > In addition, 20 dummy cycles is for 200MHz but not for 100MHz, 133MHz
and
> > 166MHz
> > in case of read performance concern.
> >
> > Given a correct dummy cycles for a specific device. [2]
> >
> > [2]
> >
https://patchwork.ozlabs.org/project/linux-mtd/patch/1587451187-6889-5-git-
> send-email-masonccyang@mxic.com.tw/
>
> The problem is that we don't know what speed the controller is driving
> the flash at, and whether it is using Data Strobe. BFPT tells us the
> maximum speed of the flash based on if Data Strobe is being used. The
> controller can also drive it slower than the maximum. And it can drive
> it with or without DS.
This is for flash, not every Octal flash could work in 200MHz,
The Max operation speeds for other Octal Flash is 100, 133 , or 166MHz.
If a specific Octal Flash could work in 166MHz(Max), and driver setup the
correct 16 dummy cycles for it rather than 20 dummy cycles.
it's for performance concern.
>
> So, we have to be conservative and just use the dummy cycles for the
> maximum speed so we can at least make sure the flash works, albeit at
> slightly less efficiency. I hard-coded it to 20 but I suppose we can
> find it out from the Profile 1.0 table and use that (though we'd have to
> round it to an even value to avoid tripping up controllers). Will fix in
> next version (or, Tudor if you're fine with fixup! patches, I can send
> that too because I suspect it will be a small change).
>
> >
thanks & best regards,
Mason
CONFIDENTIALITY NOTE:
This e-mail and any attachments may contain confidential information
and/or personal data, which is protected by applicable laws. Please be
reminded that duplication, disclosure, distribution, or use of this e-mail
(and/or its attachments) or any part thereof is prohibited. If you receive
this e-mail in error, please notify us immediately and delete this mail as
well as its attachment(s) from your system. In addition, please be
informed that collection, processing, and/or use of personal data is
prohibited unless expressly permitted by personal data protection laws.
Thank you for your attention and cooperation.
Macronix International Co., Ltd.
=====================================================================
============================================================================
CONFIDENTIALITY NOTE:
This e-mail and any attachments may contain confidential information and/or personal data, which is protected by applicable laws. Please be reminded that duplication, disclosure, distribution, or use of this e-mail (and/or its attachments) or any part thereof is prohibited. If you receive this e-mail in error, please notify us immediately and delete this mail as well as its attachment(s) from your system. In addition, please be informed that collection, processing, and/or use of personal data is prohibited unless expressly permitted by personal data protection laws. Thank you for your attention and cooperation.
Macronix International Co., Ltd.
=====================================================================
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH v2 2/2] arm64: vdso: Fix CFI directives in sigreturn trampoline
From: Dave Martin @ 2020-05-20 9:42 UTC (permalink / raw)
To: Will Deacon
Cc: Tamas Zsoldos, Mark Brown, kernel-team, linux-arm-kernel,
Daniel Kiss
In-Reply-To: <20200519162821.16857-3-will@kernel.org>
On Tue, May 19, 2020 at 05:28:21PM +0100, Will Deacon wrote:
> Daniel reports that the .cfi_startproc is misplaced for the sigreturn
> trampoline, which causes LLVM's unwinder to misbehave:
>
> | I run into this with LLVM’s unwinder.
> | This combination was always broken.
>
> This prompted Dave to question our use of CFI directives more generally,
> and I ended up going down a rabbit hole trying to figure out how this
> very poorly documented stuff gets used.
>
> Move the CFI directives so that the "mysterious NOP" is included in
> the .cfi_{start,end}proc block and add a bunch of comments so that I
> can save myself another headache in future.
>
> Cc: Tamas Zsoldos <tamas.zsoldos@arm.com>
> Reported-by: Dave Martin <dave.martin@arm.com>
> Reported-by: Daniel Kiss <daniel.kiss@arm.com>
> Signed-off-by: Will Deacon <will@kernel.org>
> ---
> arch/arm64/kernel/vdso/sigreturn.S | 40 ++++++++++++++++++++++++------
> 1 file changed, 33 insertions(+), 7 deletions(-)
>
> diff --git a/arch/arm64/kernel/vdso/sigreturn.S b/arch/arm64/kernel/vdso/sigreturn.S
> index 0c921130002a..cb47dfb3bd5a 100644
> --- a/arch/arm64/kernel/vdso/sigreturn.S
> +++ b/arch/arm64/kernel/vdso/sigreturn.S
> @@ -1,7 +1,11 @@
> /* SPDX-License-Identifier: GPL-2.0-only */
> /*
> * Sigreturn trampoline for returning from a signal when the SA_RESTORER
> - * flag is not set.
> + * flag is not set. It serves primarily as a hall of shame for crappy
> + * unwinders and features an exciting but mysterious NOP instruction.
> + *
> + * It's also fragile as hell, so please think twice before changing anything
> + * in here.
> *
> * Copyright (C) 2012 ARM Limited
> *
> @@ -14,7 +18,34 @@
>
> .text
>
> - nop
> +/* Ensure that the mysterious NOP can be associated with a function. */
> + .cfi_startproc
> +
> +/*
> + * .cfi_signal_frame causes the corresponding Frame Description Entry in the
> + * .eh_frame section to be annotated as a signal frame. This allows DWARF
> + * unwinders (e.g. libstdc++) to implement _Unwind_GetIPInfo(), which permits
> + * unwinding out of the signal trampoline without the need for the mysterious
> + * NOP.
> + */
> + .cfi_signal_frame
> +
> +/*
> + * Tell the unwinder where to locate the frame record linking back to the
> + * interrupted context.
> + */
> + .cfi_def_cfa x29, 0
> + .cfi_offset x29, 0 * 8
> + .cfi_offset x29, 1 * 8
We should also give rationale for why we don't describe how to recover
other regs here. At a signal, every reg is potentially live with data
essential to the backtrace, so custom unwind entries further up the
stack may unwind badly after trying to unwind out of the signal handler.
Otherwise, looks reasonable -- it should be easier now to understand
next time!
[...]
Cheers
---Dave
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH] ASoC: fsl: imx-pcm-dma: Don't request dma channel in probe
From: Lucas Stach @ 2020-05-20 9:42 UTC (permalink / raw)
To: Shengjiu Wang
Cc: sumit.semwal, linaro-mm-sig, Linux-ALSA, linuxppc-dev,
linux-kernel, Timur Tabi, Xiubo Li, shawnguo, Shengjiu Wang,
Takashi Iwai, Liam Girdwood, dri-devel, perex, Nicolin Chen,
Mark Brown, linux-imx, kernel, Fabio Estevam, s.hauer,
linux-arm-kernel, linux-media
In-Reply-To: <CAA+D8APhHvA39wmCayeCsAEKmOJ0n7qOQiT1tZmFHr4+yASgTw@mail.gmail.com>
Am Mittwoch, den 20.05.2020, 16:20 +0800 schrieb Shengjiu Wang:
> Hi
>
> On Tue, May 19, 2020 at 6:04 PM Lucas Stach <l.stach@pengutronix.de> wrote:
> > Am Dienstag, den 19.05.2020, 17:41 +0800 schrieb Shengjiu Wang:
> > > There are two requirements that we need to move the request
> > > of dma channel from probe to open.
> >
> > How do you handle -EPROBE_DEFER return code from the channel request if
> > you don't do it in probe?
>
> I use the dma_request_slave_channel or dma_request_channel instead
> of dmaengine_pcm_request_chan_of. so there should be not -EPROBE_DEFER
> return code.
This is a pretty weak argument. The dmaengine device might probe after
you try to get the channel. Using a function to request the channel
that doesn't allow you to handle probe deferral is IMHO a bug and
should be fixed, instead of building even more assumptions on top of
it.
> > > - When dma device binds with power-domains, the power will
> > > be enabled when we request dma channel. If the request of dma
> > > channel happen on probe, then the power-domains will be always
> > > enabled after kernel boot up, which is not good for power
> > > saving, so we need to move the request of dma channel to .open();
> >
> > This is certainly something which could be fixed in the dmaengine
> > driver.
>
> Dma driver always call the pm_runtime_get_sync in
> device_alloc_chan_resources, the device_alloc_chan_resources is
> called when channel is requested. so power is enabled on channel
> request.
So why can't you fix the dmaengine driver to do that RPM call at a
later time when the channel is actually going to be used? This will
allow further power savings with other slave devices than the audio
PCM.
Regards,
Lucas
> > > - With FE-BE case, if the dma channel is requested in probe,
> > > then there will be below issue, which is caused by that the
> > > dma channel will be requested duplicately
> >
> > Why is this requested a second time? Is this just some missing cleanup
> > on a deferred probe path?
>
> Not relate with deferred probe. With DMA1->ASRC->DMA2->ESAI case,
> the DMA1->ASRC->DMA2 is in FE, ESAI is in BE. When ESAI drvier
> probe, DMA3 channel is created with ESAI's "dma:tx" (DMA3 channel
> is not used in this FE-BE case). When FE-BE startup, DMA2
> channel is created, it needs the ESAI's "dma:tx", so below warning
> comes out.
>
> > Regards,
> > Lucas
> >
> > > [ 638.906268] sysfs: cannot create duplicate filename '/devices/soc0/soc/2000000.bus/2000000.spba-bus/2024000.esai/dma:tx'
> > > [ 638.919061] CPU: 1 PID: 673 Comm: aplay Not tainted 5.7.0-rc1-12956-gfc64b2585593 #287
> > > [ 638.927113] Hardware name: Freescale i.MX6 Quad/DualLite (Device Tree)
> > > [ 638.933690] [<c0110dd8>] (unwind_backtrace) from [<c010b8ec>] (show_stack+0x10/0x14)
> > > [ 638.941464] [<c010b8ec>] (show_stack) from [<c0557fc0>] (dump_stack+0xe4/0x118)
> > > [ 638.948808] [<c0557fc0>] (dump_stack) from [<c032aeb4>] (sysfs_warn_dup+0x50/0x64)
> > > [ 638.956406] [<c032aeb4>] (sysfs_warn_dup) from [<c032b1a8>] (sysfs_do_create_link_sd+0xc8/0xd4)
> > > [ 638.965134] [<c032b1a8>] (sysfs_do_create_link_sd) from [<c05dc668>] (dma_request_chan+0xb0/0x210)
> > > [ 638.974120] [<c05dc668>] (dma_request_chan) from [<c05dc7d0>] (dma_request_slave_channel+0x8/0x14)
> > > [ 638.983111] [<c05dc7d0>] (dma_request_slave_channel) from [<c09d5548>] (fsl_asrc_dma_hw_params+0x1e0/0x438)
> > > [ 638.992881] [<c09d5548>] (fsl_asrc_dma_hw_params) from [<c09c1654>] (soc_pcm_hw_params+0x4a0/0x6a8)
> > > [ 639.001952] [<c09c1654>] (soc_pcm_hw_params) from [<c09c39d4>] (dpcm_fe_dai_hw_params+0x70/0xe4)
> > > [ 639.010765] [<c09c39d4>] (dpcm_fe_dai_hw_params) from [<c099b274>] (snd_pcm_hw_params+0x158/0x418)
> > > [ 639.019750] [<c099b274>] (snd_pcm_hw_params) from [<c099c5a0>] (snd_pcm_ioctl+0x734/0x183c)
> > > [ 639.028129] [<c099c5a0>] (snd_pcm_ioctl) from [<c029ff94>] (ksys_ioctl+0x2ac/0xb98)
> > > [ 639.035812] [<c029ff94>] (ksys_ioctl) from [<c0100080>] (ret_fast_syscall+0x0/0x28)
> > > [ 639.043490] Exception stack(0xec529fa8 to 0xec529ff0)
> > > [ 639.048565] 9fa0: bee84650 01321870 00000004 c25c4111 bee84650 0002000f
> > > [ 639.056766] 9fc0: bee84650 01321870 01321820 00000036 00001f40 00000000 0002c2f8 00000003
> > > [ 639.064964] 9fe0: b6f483fc bee8451c b6ee2655 b6e1dcf8
> > > [ 639.070339] fsl-esai-dai 2024000.esai: Cannot create DMA dma:tx symlink
> > >
> > > Signed-off-by: Shengjiu Wang <shengjiu.wang@nxp.com>
> > > ---
> > > sound/soc/fsl/imx-pcm-dma.c | 173 +++++++++++++++++++++++++++++++++---
> > > 1 file changed, 159 insertions(+), 14 deletions(-)
> > >
> > > diff --git a/sound/soc/fsl/imx-pcm-dma.c b/sound/soc/fsl/imx-pcm-dma.c
> > > index 04a9bc749016..dae53b384df4 100644
> > > --- a/sound/soc/fsl/imx-pcm-dma.c
> > > +++ b/sound/soc/fsl/imx-pcm-dma.c
> > > @@ -11,6 +11,7 @@
> > > #include <linux/dmaengine.h>
> > > #include <linux/types.h>
> > > #include <linux/module.h>
> > > +#include <linux/dma-mapping.h>
> > >
> > > #include <sound/core.h>
> > > #include <sound/pcm.h>
> > > @@ -29,24 +30,168 @@ static bool filter(struct dma_chan *chan, void *param)
> > > return true;
> > > }
> > >
> > > -static const struct snd_dmaengine_pcm_config imx_dmaengine_pcm_config = {
> > > - .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config,
> > > - .compat_filter_fn = filter,
> > > -};
> > > +static int imx_pcm_hw_params(struct snd_soc_component *component,
> > > + struct snd_pcm_substream *substream,
> > > + struct snd_pcm_hw_params *params)
> > > +{
> > > + struct snd_pcm_runtime *runtime = substream->runtime;
> > > + struct snd_soc_pcm_runtime *rtd = substream->private_data;
> > > + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
> > > + struct snd_dmaengine_dai_dma_data *dma_data;
> > > + struct dma_slave_config config;
> > > + struct dma_chan *chan;
> > > + int ret = 0;
> > >
> > > -int imx_pcm_dma_init(struct platform_device *pdev, size_t size)
> > > + snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
> > > + runtime->dma_bytes = params_buffer_bytes(params);
> > > +
> > > + chan = snd_dmaengine_pcm_get_chan(substream);
> > > + if (!chan)
> > > + return -EINVAL;
> > > +
> > > + ret = snd_hwparams_to_dma_slave_config(substream, params, &config);
> > > + if (ret)
> > > + return ret;
> > > +
> > > + dma_data = snd_soc_dai_get_dma_data(cpu_dai, substream);
> > > + if (!dma_data)
> > > + return -EINVAL;
> > > +
> > > + snd_dmaengine_pcm_set_config_from_dai_data(substream,
> > > + dma_data,
> > > + &config);
> > > + return dmaengine_slave_config(chan, &config);
> > > +}
> > > +
> > > +static int imx_pcm_hw_free(struct snd_soc_component *component,
> > > + struct snd_pcm_substream *substream)
> > > {
> > > - struct snd_dmaengine_pcm_config *config;
> > > + snd_pcm_set_runtime_buffer(substream, NULL);
> > > + return 0;
> > > +}
> > > +
> > > +static snd_pcm_uframes_t imx_pcm_pointer(struct snd_soc_component *component,
> > > + struct snd_pcm_substream *substream)
> > > +{
> > > + return snd_dmaengine_pcm_pointer(substream);
> > > +}
> > > +
> > > +static int imx_pcm_trigger(struct snd_soc_component *component,
> > > + struct snd_pcm_substream *substream, int cmd)
> > > +{
> > > + return snd_dmaengine_pcm_trigger(substream, cmd);
> > > +}
> > > +
> > > +static int imx_pcm_open(struct snd_soc_component *component,
> > > + struct snd_pcm_substream *substream)
> > > +{
> > > + struct snd_soc_pcm_runtime *rtd = substream->private_data;
> > > + bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
> > > + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
> > > + struct snd_dmaengine_dai_dma_data *dma_data;
> > > + struct device *dev = component->dev;
> > > + struct snd_pcm_hardware hw;
> > > + struct dma_chan *chan;
> > > + int ret;
> > > +
> > > + ret = snd_pcm_hw_constraint_integer(substream->runtime,
> > > + SNDRV_PCM_HW_PARAM_PERIODS);
> > > + if (ret < 0) {
> > > + dev_err(dev, "failed to set pcm hw params periods\n");
> > > + return ret;
> > > + }
> > > +
> > > + dma_data = snd_soc_dai_get_dma_data(cpu_dai, substream);
> > > + if (!dma_data)
> > > + return -EINVAL;
> > > +
> > > + chan = dma_request_slave_channel(cpu_dai->dev, tx ? "tx" : "rx");
> > > + if (!chan) {
> > > + /* Try to request channel using compat_filter_fn */
> > > + chan = snd_dmaengine_pcm_request_channel(filter,
> > > + dma_data->filter_data);
> > > + if (!chan)
> > > + return -ENXIO;
> > > + }
> > >
> > > - config = devm_kzalloc(&pdev->dev,
> > > - sizeof(struct snd_dmaengine_pcm_config), GFP_KERNEL);
> > > - if (!config)
> > > - return -ENOMEM;
> > > - *config = imx_dmaengine_pcm_config;
> > > + ret = snd_dmaengine_pcm_open(substream, chan);
> > > + if (ret)
> > > + goto pcm_open_fail;
> > >
> > > - return devm_snd_dmaengine_pcm_register(&pdev->dev,
> > > - config,
> > > - SND_DMAENGINE_PCM_FLAG_COMPAT);
> > > + memset(&hw, 0, sizeof(hw));
> > > + hw.info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
> > > + SNDRV_PCM_INFO_INTERLEAVED;
> > > + hw.periods_min = 2;
> > > + hw.periods_max = UINT_MAX;
> > > + hw.period_bytes_min = 256;
> > > + hw.period_bytes_max = dma_get_max_seg_size(chan->device->dev);
> > > + hw.buffer_bytes_max = IMX_DEFAULT_DMABUF_SIZE;
> > > + hw.fifo_size = dma_data->fifo_size;
> > > +
> > > + /* Refine the hw according to caps of DMA. */
> > > + ret = snd_dmaengine_pcm_refine_runtime_hwparams(substream,
> > > + dma_data,
> > > + &hw,
> > > + chan);
> > > + if (ret < 0)
> > > + goto refine_runtime_hwparams_fail;
> > > +
> > > + snd_soc_set_runtime_hwparams(substream, &hw);
> > > +
> > > + /* Support allocate memory from IRAM */
> > > + ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV_IRAM,
> > > + chan->device->dev,
> > > + hw.buffer_bytes_max,
> > > + &substream->dma_buffer);
> > > + if (ret < 0)
> > > + goto alloc_pagas_fail;
> > > +
> > > + return 0;
> > > +
> > > +alloc_pagas_fail:
> > > +refine_runtime_hwparams_fail:
> > > + snd_dmaengine_pcm_close(substream);
> > > +pcm_open_fail:
> > > + dma_release_channel(chan);
> > > +
> > > + return ret;
> > > +}
> > > +
> > > +static int imx_pcm_close(struct snd_soc_component *component,
> > > + struct snd_pcm_substream *substream)
> > > +{
> > > + if (substream) {
> > > + snd_dma_free_pages(&substream->dma_buffer);
> > > + substream->dma_buffer.area = NULL;
> > > + substream->dma_buffer.addr = 0;
> > > + }
> > > +
> > > + return snd_dmaengine_pcm_close_release_chan(substream);
> > > +}
> > > +
> > > +static int imx_pcm_new(struct snd_soc_component *component,
> > > + struct snd_soc_pcm_runtime *rtd)
> > > +{
> > > + struct snd_card *card = rtd->card->snd_card;
> > > +
> > > + return dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32));
> > > +}
> > > +
> > > +static const struct snd_soc_component_driver imx_pcm_component = {
> > > + .name = "imx-pcm-dma",
> > > + .pcm_construct = imx_pcm_new,
> > > + .open = imx_pcm_open,
> > > + .close = imx_pcm_close,
> > > + .hw_params = imx_pcm_hw_params,
> > > + .hw_free = imx_pcm_hw_free,
> > > + .trigger = imx_pcm_trigger,
> > > + .pointer = imx_pcm_pointer,
> > > +};
> > > +
> > > +int imx_pcm_dma_init(struct platform_device *pdev, size_t size)
> > > +{
> > > + return devm_snd_soc_register_component(&pdev->dev,
> > > + &imx_pcm_component, NULL, 0);
> > > }
> > > EXPORT_SYMBOL_GPL(imx_pcm_dma_init);
> > >
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH 1/3] arm64: vdso: Don't prefix sigreturn trampoline with a BTI C instruction
From: Dave Martin @ 2020-05-20 9:48 UTC (permalink / raw)
To: Mark Brown
Cc: Tamas Zsoldos, kernel-team, Will Deacon, linux-arm-kernel,
Daniel Kiss
In-Reply-To: <20200519154247.GN4611@sirena.org.uk>
On Tue, May 19, 2020 at 04:42:47PM +0100, Mark Brown wrote:
> On Tue, May 19, 2020 at 03:55:15PM +0100, Dave Martin wrote:
> > On Tue, May 19, 2020 at 03:35:00PM +0100, Mark Brown wrote:
> > > On Tue, May 19, 2020 at 02:25:38PM +0100, Dave Martin wrote:
>
> > > > Rather, the "ret lr" that jumps here is supposed to be authenticated via
> > > > pointer auth in the caller.
>
> > > In which case there was an issue anyway...
>
> > What issue?
>
> None, I was confused.
>
> > > > If BTI {nothing} allows this while disallowing all BR/BLR then we could
> > > > use that (I can't remember what BTI {nothing} is useful for, if anything).
>
> > > > Otherwise, it's less clear what we should have here.
>
> > > I can't remember anything that distinguishes it from an explicit NOP.
>
> > I think it rejects everything other then fallthrough execution
> > (BTYPE==0, which includes RET). I might have misunderstood something
>
> Right, but since BTI only generates an exception when BTYPE != 0 I'm
> having trouble differentiating this from a NOP in practical terms.
The idea would be that if an attacker could fudge some function pointer
to point at __kernel_rt_sigreturn, attempting to do a call via that
pointer would still trigger a BTI trap.
This vulnerability isn't applicable to return addresses, because the
victim is supposed to sign those before storing them to (attackable)
memory, and authenticate between loading back from memory and doing the
RET. So the victim can defend itself from that scenario.
>
> > somewhere, but sort of feels like the right thing here. I never put a
> > lot of effort into trying to understand BTI {nothing} though. It
> > seemed a weird, possibly useless special case.
>
> That was my read too.
And if the gdb doesn't tolerate modification of the exact insn sequence,
we can't do it anyway. I'd really say that's a bug-like rogue heuristic
in gdb and "not our problem". But people will moan about regressions
nonetheless.
I was that interested because of the potential use for BTI {nothing}.
I'd have to actually try it out to be 100% sure it works anyway.
Cheers
---Dave
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH v2 2/2] arm64: vdso: Fix CFI directives in sigreturn trampoline
From: Will Deacon @ 2020-05-20 9:50 UTC (permalink / raw)
To: Dave Martin
Cc: Tamas Zsoldos, Mark Brown, kernel-team, linux-arm-kernel,
Daniel Kiss
In-Reply-To: <20200520094212.GK5031@arm.com>
On Wed, May 20, 2020 at 10:42:13AM +0100, Dave Martin wrote:
> On Tue, May 19, 2020 at 05:28:21PM +0100, Will Deacon wrote:
> > @@ -14,7 +18,34 @@
> >
> > .text
> >
> > - nop
> > +/* Ensure that the mysterious NOP can be associated with a function. */
> > + .cfi_startproc
> > +
> > +/*
> > + * .cfi_signal_frame causes the corresponding Frame Description Entry in the
> > + * .eh_frame section to be annotated as a signal frame. This allows DWARF
> > + * unwinders (e.g. libstdc++) to implement _Unwind_GetIPInfo(), which permits
> > + * unwinding out of the signal trampoline without the need for the mysterious
> > + * NOP.
> > + */
> > + .cfi_signal_frame
> > +
> > +/*
> > + * Tell the unwinder where to locate the frame record linking back to the
> > + * interrupted context.
> > + */
> > + .cfi_def_cfa x29, 0
> > + .cfi_offset x29, 0 * 8
> > + .cfi_offset x29, 1 * 8
>
> We should also give rationale for why we don't describe how to recover
> other regs here. At a signal, every reg is potentially live with data
> essential to the backtrace, so custom unwind entries further up the
> stack may unwind badly after trying to unwind out of the signal handler.
Hmm, I'm not sure I get what you're asking for. We can't recover the other
registers even if we tried, can we? I think the only way to get a reliable
backtrace here is not to clobber the framepointer.
Will
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: BUG: sleeping function called from atomic due to "Balance initial LPI affinity across CPUs"
From: Marc Zyngier @ 2020-05-20 9:51 UTC (permalink / raw)
To: John Garry
Cc: Stephen Rothwell, Linux Kernel Mailing List,
Linux Next Mailing List, Qian Cai, Thomas Gleixner, Linux ARM
In-Reply-To: <81796a6e-718a-aa93-d183-6747e0654c8c@huawei.com>
Hi John,
On 2020-05-20 09:43, John Garry wrote:
> On 19/05/2020 23:09, Qian Cai wrote:
>> Reverted the linux-next commit f068a62c548c ("irqchip/gic-v3-its:
>> Balance initial LPI affinity across CPUs") fixed these warnings during
>> boot,
>
> Thanks for the notice. So we need the following set to see this:
> CONFIG_CPUMASK_OFFSTACK=y
> CONFIG_DEBUG_ATOMIC_SLEEP=y
> CONFIG_DEBUG_PER_CPU_MAPS=y
Ah, thanks for pointing this out.
>> its_select_cpu at drivers/irqchip/irq-gic-v3-its.c:1572
>>
>> [ 332.819381][ T3359] BUG: sleeping function called from invalid
>> context at mm/slab.h:568
>> [ 332.827405][ T3359] in_atomic(): 1, irqs_disabled(): 128,
>> non_block: 0, pid: 3359, name: irqbalance
>> [ 332.836455][ T3359] INFO: lockdep is turned off.
>> [ 332.841076][ T3359] irq event stamp: 0
>> [ 332.844836][ T3359] hardirqs last enabled at (0):
>> [<0000000000000000>] 0x0
>> [ 332.851828][ T3359] hardirqs last disabled at (0):
>> [<ffff9000101ea65c>] copy_process+0x98c/0x1f34
>> [ 332.860710][ T3359] softirqs last enabled at (0):
>> [<ffff9000101ea690>] copy_process+0x9c0/0x1f34
>> [ 332.869586][ T3359] softirqs last disabled at (0):
>> [<0000000000000000>] 0x0
>> [ 332.876560][ T3359] CPU: 155 PID: 3359 Comm: irqbalance Tainted: G
>> W L 5.7.0-rc6-next-20200519 #1
>> [ 332.886563][ T3359] Hardware name: HPE Apollo 70
>> /C01_APACHE_MB , BIOS L50_5.13_1.11 06/18/2019
>> [ 332.897000][ T3359] Call trace:
>> [ 332.900151][ T3359] dump_backtrace+0x0/0x22c
>> [ 332.904514][ T3359] show_stack+0x28/0x34
>> [ 332.908543][ T3359] dump_stack+0x104/0x194
>> [ 332.912738][ T3359] ___might_sleep+0x314/0x328
>> [ 332.917274][ T3359] __might_sleep+0x7c/0xe0
>> [ 332.921563][ T3359] slab_pre_alloc_hook+0x44/0x8c
>> [ 332.926360][ T3359] __kmalloc_node+0xb0/0x618
>> [ 332.930811][ T3359] alloc_cpumask_var_node+0x48/0x94
>
> We could use GFP_ATOMIC flag at the callsite here, but maybe there is
> a better solution.
I don't see one, and I doubt it is worth the hassle to have anything
but GFP_ATOMIC. The default arm64 config is to have on-stack cpumasks,
and only DEBUG_PER_CPU_MAPS allows this to be changed.
I'll stash a patch on top.
Thanks,
M.
--
Jazz is not dead. It just smells funny...
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* [PATCH V4] dt-bindings: thermal: Convert i.MX to json-schema
From: Anson Huang @ 2020-05-20 9:42 UTC (permalink / raw)
To: rui.zhang, daniel.lezcano, amit.kucheria, robh+dt, shawnguo,
s.hauer, kernel, festevam, linux-pm, devicetree, linux-arm-kernel,
linux-kernel
Cc: Linux-imx
Convert the i.MX thermal binding to DT schema format using json-schema
Signed-off-by: Anson Huang <Anson.Huang@nxp.com>
Reviewed-by: Dong Aisheng <aisheng.dong@nxp.com>
---
Changes since V3:
- improve "fsl,tempmon" description.
---
.../devicetree/bindings/thermal/imx-thermal.txt | 61 ------------
.../devicetree/bindings/thermal/imx-thermal.yaml | 102 +++++++++++++++++++++
2 files changed, 102 insertions(+), 61 deletions(-)
delete mode 100644 Documentation/devicetree/bindings/thermal/imx-thermal.txt
create mode 100644 Documentation/devicetree/bindings/thermal/imx-thermal.yaml
diff --git a/Documentation/devicetree/bindings/thermal/imx-thermal.txt b/Documentation/devicetree/bindings/thermal/imx-thermal.txt
deleted file mode 100644
index 823e417..0000000
--- a/Documentation/devicetree/bindings/thermal/imx-thermal.txt
+++ /dev/null
@@ -1,61 +0,0 @@
-* Temperature Monitor (TEMPMON) on Freescale i.MX SoCs
-
-Required properties:
-- compatible : must be one of following:
- - "fsl,imx6q-tempmon" for i.MX6Q,
- - "fsl,imx6sx-tempmon" for i.MX6SX,
- - "fsl,imx7d-tempmon" for i.MX7S/D.
-- interrupts : the interrupt output of the controller:
- i.MX6Q has one IRQ which will be triggered when temperature is higher than high threshold,
- i.MX6SX and i.MX7S/D have two more IRQs than i.MX6Q, one is IRQ_LOW and the other is IRQ_PANIC,
- when temperature is below than low threshold, IRQ_LOW will be triggered, when temperature
- is higher than panic threshold, system will auto reboot by SRC module.
-- fsl,tempmon : phandle pointer to system controller that contains TEMPMON
- control registers, e.g. ANATOP on imx6q.
-- nvmem-cells: A phandle to the calibration cells provided by ocotp.
-- nvmem-cell-names: Should be "calib", "temp_grade".
-
-Deprecated properties:
-- fsl,tempmon-data : phandle pointer to fuse controller that contains TEMPMON
- calibration data, e.g. OCOTP on imx6q. The details about calibration data
- can be found in SoC Reference Manual.
-
-Direct access to OCOTP via fsl,tempmon-data is incorrect on some newer chips
-because it does not handle OCOTP clock requirements.
-
-Optional properties:
-- clocks : thermal sensor's clock source.
-
-Example:
-ocotp: ocotp@21bc000 {
- #address-cells = <1>;
- #size-cells = <1>;
- compatible = "fsl,imx6sx-ocotp", "syscon";
- reg = <0x021bc000 0x4000>;
- clocks = <&clks IMX6SX_CLK_OCOTP>;
-
- tempmon_calib: calib@38 {
- reg = <0x38 4>;
- };
-
- tempmon_temp_grade: temp-grade@20 {
- reg = <0x20 4>;
- };
-};
-
-tempmon: tempmon {
- compatible = "fsl,imx6sx-tempmon", "fsl,imx6q-tempmon";
- interrupts = <GIC_SPI 49 IRQ_TYPE_LEVEL_HIGH>;
- fsl,tempmon = <&anatop>;
- nvmem-cells = <&tempmon_calib>, <&tempmon_temp_grade>;
- nvmem-cell-names = "calib", "temp_grade";
- clocks = <&clks IMX6SX_CLK_PLL3_USB_OTG>;
-};
-
-Legacy method (Deprecated):
-tempmon {
- compatible = "fsl,imx6q-tempmon";
- fsl,tempmon = <&anatop>;
- fsl,tempmon-data = <&ocotp>;
- clocks = <&clks 172>;
-};
diff --git a/Documentation/devicetree/bindings/thermal/imx-thermal.yaml b/Documentation/devicetree/bindings/thermal/imx-thermal.yaml
new file mode 100644
index 0000000..aedac16
--- /dev/null
+++ b/Documentation/devicetree/bindings/thermal/imx-thermal.yaml
@@ -0,0 +1,102 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/thermal/imx-thermal.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: NXP i.MX Thermal Binding
+
+maintainers:
+ - Shawn Guo <shawn.guo@linaro.org>
+ - Anson Huang <Anson.Huang@nxp.com>
+
+properties:
+ compatible:
+ enum:
+ - fsl,imx6q-tempmon
+ - fsl,imx6sx-tempmon
+ - fsl,imx7d-tempmon
+
+ interrupts:
+ description: |
+ The interrupt output of the controller, i.MX6Q has IRQ_HIGH which
+ will be triggered when temperature is higher than high threshold,
+ i.MX6SX and i.MX7S/D have two more IRQs than i.MX6Q, one is IRQ_LOW
+ and the other is IRQ_PANIC, when temperature is lower than low
+ threshold, IRQ_LOW will be triggered, when temperature is higher
+ than panic threshold, IRQ_PANIC will be triggered, and system can
+ be configured to auto reboot by SRC module for IRQ_PANIC. IRQ_HIGH,
+ IRQ_LOW and IRQ_PANIC share same interrupt output of controller.
+ maxItems: 1
+
+ nvmem-cells:
+ items:
+ - description: Phandle to the calibration data provided by ocotp
+ - description: Phandle to the temperature grade provided by ocotp
+
+ nvmem-cell-names:
+ items:
+ - const: calib
+ - const: temp_grade
+
+ fsl,tempmon:
+ $ref: '/schemas/types.yaml#/definitions/phandle'
+ description: Phandle to anatop system controller node.
+
+ fsl,tempmon-data:
+ $ref: '/schemas/types.yaml#/definitions/phandle'
+ description: |
+ Deprecated property, phandle pointer to fuse controller that contains
+ TEMPMON calibration data, e.g. OCOTP on imx6q. The details about
+ calibration data can be found in SoC Reference Manual.
+ deprecated: true
+
+ clocks:
+ maxItems: 1
+
+required:
+ - compatible
+ - interrupts
+ - fsl,tempmon
+ - nvmem-cells
+ - nvmem-cell-names
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/clock/imx6sx-clock.h>
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+
+ efuse@21bc000 {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "fsl,imx6sx-ocotp", "syscon";
+ reg = <0x021bc000 0x4000>;
+ clocks = <&clks IMX6SX_CLK_OCOTP>;
+
+ tempmon_calib: calib@38 {
+ reg = <0x38 4>;
+ };
+
+ tempmon_temp_grade: temp-grade@20 {
+ reg = <0x20 4>;
+ };
+ };
+
+ anatop@20c8000 {
+ compatible = "fsl,imx6q-anatop", "syscon", "simple-mfd";
+ reg = <0x020c8000 0x1000>;
+ interrupts = <0 49 IRQ_TYPE_LEVEL_HIGH>,
+ <0 54 IRQ_TYPE_LEVEL_HIGH>,
+ <0 127 IRQ_TYPE_LEVEL_HIGH>;
+
+ tempmon {
+ compatible = "fsl,imx6sx-tempmon";
+ interrupts = <GIC_SPI 49 IRQ_TYPE_LEVEL_HIGH>;
+ fsl,tempmon = <&anatop>;
+ nvmem-cells = <&tempmon_calib>, <&tempmon_temp_grade>;
+ nvmem-cell-names = "calib", "temp_grade";
+ clocks = <&clks IMX6SX_CLK_PLL3_USB_OTG>;
+ };
+ };
--
2.7.4
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* [PATCH v11 1/3] dt-bindings: i2c: npcm7xx: add NPCM I2C controller
From: Tali Perry @ 2020-05-20 9:51 UTC (permalink / raw)
To: ofery, brendanhiggins, avifishman70, tmaimon77, kfting, venture,
yuenn, benjaminfair, robh+dt, wsa, andriy.shevchenko
Cc: devicetree, openbmc, linux-kernel, Tali Perry, linux-i2c,
linux-arm-kernel
In-Reply-To: <20200520095113.185414-1-tali.perry1@gmail.com>
Added device tree binding documentation for Nuvoton BMC
NPCM I2C controller.
Signed-off-by: Tali Perry <tali.perry1@gmail.com>
---
.../bindings/i2c/nuvoton,npcm7xx-i2c.yaml | 62 +++++++++++++++++++
1 file changed, 62 insertions(+)
create mode 100644 Documentation/devicetree/bindings/i2c/nuvoton,npcm7xx-i2c.yaml
diff --git a/Documentation/devicetree/bindings/i2c/nuvoton,npcm7xx-i2c.yaml b/Documentation/devicetree/bindings/i2c/nuvoton,npcm7xx-i2c.yaml
new file mode 100644
index 000000000000..fb4293f11461
--- /dev/null
+++ b/Documentation/devicetree/bindings/i2c/nuvoton,npcm7xx-i2c.yaml
@@ -0,0 +1,62 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/i2c/nuvoton,npcm7xx-i2c.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: nuvoton NPCM7XX I2C Controller Device Tree Bindings
+
+description: |
+ The NPCM750x includes sixteen I2C bus controllers. All Controllers support
+ both master and slave mode. Each controller can switch between master and slave
+ at run time (i.e. IPMB mode). Each controller has two 16 byte HW FIFO for TX and
+ RX.
+
+maintainers:
+ - Tali Perry <tali.perry1@gmail.com>
+
+properties:
+ compatible:
+ enum:
+ - nuvoton,npcm7xx-i2c
+
+ reg:
+ maxItems: 1
+
+ interrupts:
+ maxItems: 1
+
+ clocks:
+ maxItems: 1
+ description: Reference clock for the I2C bus
+
+ bus-frequency:
+ description: Desired I2C bus clock frequency in Hz. If not specified,
+ the default 100 kHz frequency will be used.
+ possible values are 100000, 400000 and 1000000.
+ default: 100000
+ enum: [100000, 400000, 1000000]
+
+required:
+ - compatible
+ - reg
+ - interrupts
+
+allOf:
+ - $ref: /schemas/i2c/i2c-controller.yaml#
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ i2c0: i2c@80000 {
+ compatible = "nuvoton,npcm750-i2c";
+ reg = <0x80000 0x1000>;
+ clocks = <&clk NPCM7XX_CLK_APB2>;
+ bus-frequency = <100000>;
+ interrupts = <GIC_SPI 64 IRQ_TYPE_LEVEL_HIGH>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&smb0_pins>;
+ };
+
+...
--
2.22.0
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* [PATCH v11 3/3] i2c: npcm7xx: Add support for slave mode for Nuvoton
From: Tali Perry @ 2020-05-20 9:51 UTC (permalink / raw)
To: ofery, brendanhiggins, avifishman70, tmaimon77, kfting, venture,
yuenn, benjaminfair, robh+dt, wsa, andriy.shevchenko
Cc: devicetree, openbmc, linux-kernel, Tali Perry, linux-i2c,
linux-arm-kernel
In-Reply-To: <20200520095113.185414-1-tali.perry1@gmail.com>
Add support for slave mode for Nuvoton
NPCM BMC I2C controller driver.
Signed-off-by: Tali Perry <tali.perry1@gmail.com>
---
drivers/i2c/busses/i2c-npcm7xx.c | 622 +++++++++++++++++++++++++++++++
1 file changed, 622 insertions(+)
diff --git a/drivers/i2c/busses/i2c-npcm7xx.c b/drivers/i2c/busses/i2c-npcm7xx.c
index 62aba7897365..721be2bc3e15 100644
--- a/drivers/i2c/busses/i2c-npcm7xx.c
+++ b/drivers/i2c/busses/i2c-npcm7xx.c
@@ -73,6 +73,24 @@ enum i2c_state {
I2C_STOP_PENDING,
};
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+/* Module supports setting multiple own slave addresses */
+enum i2c_addr {
+ I2C_SLAVE_ADDR1 = 0,
+ I2C_SLAVE_ADDR2,
+ I2C_SLAVE_ADDR3,
+ I2C_SLAVE_ADDR4,
+ I2C_SLAVE_ADDR5,
+ I2C_SLAVE_ADDR6,
+ I2C_SLAVE_ADDR7,
+ I2C_SLAVE_ADDR8,
+ I2C_SLAVE_ADDR9,
+ I2C_SLAVE_ADDR10,
+ I2C_GC_ADDR,
+ I2C_ARP_ADDR,
+};
+#endif
+
/* init register and default value required to enable module */
#define NPCM_I2CSEGCTL 0xE4
#define NPCM_I2CSEGCTL_INIT_VAL 0x0333F000
@@ -100,6 +118,21 @@ enum i2c_state {
#define NPCM_I2CADDR6 0x16
#define NPCM_I2CADDR10 0x17
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+/*
+ * npcm_i2caddr array:
+ * The module supports having multiple own slave addresses.
+ * Since the addr regs are sprinkled all over the address space,
+ * use this array to get the address or each register.
+ */
+#define I2C_NUM_OWN_ADDR 10
+const int npcm_i2caddr[I2C_NUM_OWN_ADDR] = {NPCM_I2CADDR1, NPCM_I2CADDR2,
+ NPCM_I2CADDR3, NPCM_I2CADDR4,
+ NPCM_I2CADDR5, NPCM_I2CADDR6,
+ NPCM_I2CADDR7, NPCM_I2CADDR8,
+ NPCM_I2CADDR9, NPCM_I2CADDR10};
+#endif
+
#define NPCM_I2CCTL4 0x1A
#define NPCM_I2CCTL5 0x1B
#define NPCM_I2CSCLLT 0x1C /* SCL Low Time */
@@ -271,6 +304,16 @@ struct npcm_i2c {
unsigned long int_time_stamp;
unsigned long bus_freq; /* in kHz */
u32 xmits;
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+ u8 own_slave_addr;
+ struct i2c_client *slave;
+ int slv_rd_size;
+ int slv_rd_ind;
+ int slv_wr_size;
+ int slv_wr_ind;
+ u8 slv_rd_buf[I2C_HW_FIFO_SIZE];
+ u8 slv_wr_buf[I2C_HW_FIFO_SIZE];
+#endif
#ifdef CONFIG_DEBUG_FS
struct dentry *debugfs; /* debugfs device directory */
u64 ber_count;
@@ -305,6 +348,10 @@ static void npcm_i2c_init_params(struct npcm_i2c *bus)
bus->int_time_stamp = 0;
bus->PEC_use = false;
bus->PEC_mask = 0;
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+ if (bus->slave)
+ bus->master_or_slave = I2C_SLAVE;
+#endif
}
static inline void npcm_i2c_wr_byte(struct npcm_i2c *bus, u8 data)
@@ -350,6 +397,18 @@ static void npcm_i2c_disable(struct npcm_i2c *bus)
{
u8 i2cctl2;
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+ int i;
+
+ /* select bank 0 for I2C addresses */
+ npcm_i2c_select_bank(bus, I2C_BANK_0);
+
+ /* Slave addresses removal */
+ for (i = I2C_SLAVE_ADDR1; i < I2C_NUM_OWN_ADDR; i++)
+ iowrite8(0, bus->reg + npcm_i2caddr[i]);
+
+ npcm_i2c_select_bank(bus, I2C_BANK_1);
+#endif
/* Disable module */
i2cctl2 = ioread8(bus->reg + NPCM_I2CCTL2);
i2cctl2 = i2cctl2 & ~I2CCTL2_ENABLE;
@@ -513,6 +572,62 @@ static inline void npcm_i2c_nack(struct npcm_i2c *bus)
iowrite8(val, bus->reg + NPCM_I2CCTL1);
}
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+static void npcm_i2c_slave_int_enable(struct npcm_i2c *bus, bool enable)
+{
+ u8 i2cctl1;
+
+ /* enable interrupt on slave match: */
+ i2cctl1 = ioread8(bus->reg + NPCM_I2CCTL1);
+ i2cctl1 &= ~NPCM_I2CCTL1_RWS;
+ if (enable)
+ i2cctl1 |= NPCM_I2CCTL1_NMINTE;
+ else
+ i2cctl1 &= ~NPCM_I2CCTL1_NMINTE;
+ iowrite8(i2cctl1, bus->reg + NPCM_I2CCTL1);
+}
+
+static int npcm_i2c_slave_enable_l(struct npcm_i2c *bus,
+ enum i2c_addr addr_type, u8 addr,
+ bool enable)
+{
+ u8 i2cctl1;
+ u8 i2cctl3;
+ u8 sa_reg;
+
+ sa_reg = (addr & 0x7F) | FIELD_PREP(NPCM_I2CADDR_SAEN, enable);
+ if (addr_type == I2C_GC_ADDR) {
+ i2cctl1 = ioread8(bus->reg + NPCM_I2CCTL1);
+ if (enable)
+ i2cctl1 |= NPCM_I2CCTL1_GCMEN;
+ else
+ i2cctl1 &= ~NPCM_I2CCTL1_GCMEN;
+ iowrite8(i2cctl1, bus->reg + NPCM_I2CCTL1);
+ return 0;
+ }
+ if (addr_type == I2C_ARP_ADDR) {
+ i2cctl3 = ioread8(bus->reg + NPCM_I2CCTL3);
+ if (enable)
+ i2cctl3 |= I2CCTL3_ARPMEN;
+ else
+ i2cctl3 &= ~I2CCTL3_ARPMEN;
+ iowrite8(i2cctl3, bus->reg + NPCM_I2CCTL3);
+ return 0;
+ }
+ if (addr_type >= I2C_ARP_ADDR)
+ return -EFAULT;
+ /* select bank 0 for address 3 to 10 */
+ if (addr_type > I2C_SLAVE_ADDR2)
+ npcm_i2c_select_bank(bus, I2C_BANK_0);
+ /* Set and enable the address */
+ iowrite8(sa_reg, bus->reg + npcm_i2caddr[(int)addr_type]);
+ npcm_i2c_slave_int_enable(bus, enable);
+ if (addr_type > I2C_SLAVE_ADDR2)
+ npcm_i2c_select_bank(bus, I2C_BANK_1);
+ return 0;
+}
+#endif
+
static void npcm_i2c_reset(struct npcm_i2c *bus)
{
/*
@@ -542,6 +657,12 @@ static void npcm_i2c_reset(struct npcm_i2c *bus)
/* Clear all fifo bits: */
iowrite8(NPCM_I2CFIF_CTS_CLR_FIFO, bus->reg + NPCM_I2CFIF_CTS);
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+ if (bus->slave)
+ npcm_i2c_slave_enable_l(bus, I2C_SLAVE_ADDR1, bus->slave->addr,
+ true);
+#endif
+
bus->state = I2C_IDLE;
}
@@ -607,6 +728,10 @@ static void npcm_i2c_callback(struct npcm_i2c *bus,
}
bus->operation = I2C_NO_OPER;
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+ if (bus->slave)
+ bus->master_or_slave = I2C_SLAVE;
+#endif
}
static u8 npcm_i2c_fifo_usage(struct npcm_i2c *bus)
@@ -639,6 +764,35 @@ static void npcm_i2c_write_to_fifo_master(struct npcm_i2c *bus,
}
}
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+static void npcm_i2c_write_to_fifo_slave(struct npcm_i2c *bus,
+ u16 max_bytes_to_send)
+{
+ u8 size_free_fifo;
+ /*
+ * Fill the FIFO, while the FIFO is not full and there are more bytes
+ * to write
+ */
+ npcm_i2c_clear_fifo_int(bus);
+ npcm_i2c_clear_tx_fifo(bus);
+ iowrite8(0, bus->reg + NPCM_I2CTXF_CTL);
+ size_free_fifo = I2C_HW_FIFO_SIZE - npcm_i2c_fifo_usage(bus);
+ while (max_bytes_to_send-- && size_free_fifo) {
+ if (bus->slv_wr_size > 0) {
+ bus->slv_wr_ind = bus->slv_wr_ind % I2C_HW_FIFO_SIZE;
+ npcm_i2c_wr_byte(bus,
+ bus->slv_wr_buf[bus->slv_wr_ind]);
+ bus->slv_wr_ind++;
+ bus->slv_wr_ind = bus->slv_wr_ind % I2C_HW_FIFO_SIZE;
+ bus->slv_wr_size--;
+ size_free_fifo = I2C_HW_FIFO_SIZE -
+ npcm_i2c_fifo_usage(bus);
+ } else {
+ break;
+ }
+ }
+}
+#endif
/*
* npcm_i2c_set_fifo:
* configure the FIFO before using it. If nread is -1 RX FIFO will not be
@@ -697,6 +851,21 @@ static void npcm_i2c_read_from_fifo(struct npcm_i2c *bus, u8 bytes_in_fifo)
if (bus->master_or_slave == I2C_MASTER) {
if (bus->rd_ind < bus->rd_size)
bus->rd_buf[bus->rd_ind++] = data;
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+ } else {
+ if (bus->slave) {
+ bus->slv_rd_ind = bus->slv_rd_ind %
+ I2C_HW_FIFO_SIZE;
+ bus->slv_rd_buf[bus->slv_rd_ind] = data;
+ bus->slv_rd_ind++;
+
+ /* 1st byte is length in block protocol: */
+ if (bus->slv_rd_ind == 1 && bus->read_block_use)
+ bus->slv_rd_size = data +
+ (u8)bus->PEC_use +
+ (u8)bus->read_block_use;
+ }
+#endif
}
}
}
@@ -720,6 +889,435 @@ static void npcm_i2c_master_abort(struct npcm_i2c *bus)
}
}
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+static u8 npcm_i2c_get_slave_addr(struct npcm_i2c *bus, enum i2c_addr addr_type)
+{
+ u8 slave_add;
+
+ /* select bank 0 for address 3 to 10 */
+ if (addr_type > I2C_SLAVE_ADDR2)
+ npcm_i2c_select_bank(bus, I2C_BANK_0);
+
+ slave_add = ioread8(bus->reg + npcm_i2caddr[(int)addr_type]);
+
+ if (addr_type > I2C_SLAVE_ADDR2)
+ npcm_i2c_select_bank(bus, I2C_BANK_1);
+
+ return slave_add;
+}
+
+static int npcm_i2c_remove_slave_addr(struct npcm_i2c *bus, u8 slave_add)
+{
+ int i;
+
+ /* Set the enable bit */
+ slave_add |= 0x80;
+ npcm_i2c_select_bank(bus, I2C_BANK_0);
+ for (i = I2C_SLAVE_ADDR1; i < I2C_NUM_OWN_ADDR; i++) {
+ if (ioread8(bus->reg + npcm_i2caddr[i]) == slave_add)
+ iowrite8(0, bus->reg + npcm_i2caddr[i]);
+ }
+ npcm_i2c_select_bank(bus, I2C_BANK_1);
+ return 0;
+}
+
+static int npcm_i2c_slave_get_wr_buf(struct npcm_i2c *bus)
+{
+ int i;
+ u8 value = 0;
+ int ret = bus->slv_wr_ind;
+
+ /* fill a cyclic buffer */
+ for (i = 0; i < I2C_HW_FIFO_SIZE; i++) {
+ if (bus->slv_wr_size >= I2C_HW_FIFO_SIZE)
+ break;
+ i2c_slave_event(bus->slave, I2C_SLAVE_READ_REQUESTED, &value);
+ bus->slv_wr_buf[(bus->slv_wr_ind + bus->slv_wr_size) %
+ I2C_HW_FIFO_SIZE] = value;
+ bus->slv_wr_size++;
+ i2c_slave_event(bus->slave, I2C_SLAVE_READ_PROCESSED, &value);
+ }
+ return I2C_HW_FIFO_SIZE - ret;
+}
+
+static void npcm_i2c_slave_send_rd_buf(struct npcm_i2c *bus)
+{
+ int i;
+
+ for (i = 0; i < bus->slv_rd_ind; i++)
+ i2c_slave_event(bus->slave, I2C_SLAVE_WRITE_RECEIVED,
+ &bus->slv_rd_buf[i]);
+ /*
+ * once we send bytes up, need to reset the counter of the wr buf
+ * got data from master (new offset in device), ignore wr fifo:
+ */
+ if (bus->slv_rd_ind) {
+ bus->slv_wr_size = 0;
+ bus->slv_wr_ind = 0;
+ }
+
+ bus->slv_rd_ind = 0;
+ bus->slv_rd_size = bus->adap.quirks->max_read_len;
+
+ npcm_i2c_clear_fifo_int(bus);
+ npcm_i2c_clear_rx_fifo(bus);
+}
+
+static bool npcm_i2c_slave_receive(struct npcm_i2c *bus, u16 nread,
+ u8 *read_data)
+{
+ bus->state = I2C_OPER_STARTED;
+ bus->operation = I2C_READ_OPER;
+ bus->slv_rd_size = nread;
+ bus->slv_rd_ind = 0;
+
+ iowrite8(0, bus->reg + NPCM_I2CTXF_CTL);
+ iowrite8(I2C_HW_FIFO_SIZE, bus->reg + NPCM_I2CRXF_CTL);
+
+ npcm_i2c_clear_tx_fifo(bus);
+ npcm_i2c_clear_rx_fifo(bus);
+
+ return true;
+}
+
+static bool npcm_i2c_slave_xmit(struct npcm_i2c *bus, u16 nwrite,
+ u8 *write_data)
+{
+ if (nwrite == 0)
+ return false;
+
+ bus->state = I2C_OPER_STARTED;
+ bus->operation = I2C_WRITE_OPER;
+
+ /* get the next buffer */
+ npcm_i2c_slave_get_wr_buf(bus);
+
+ if (nwrite > 0)
+ npcm_i2c_write_to_fifo_slave(bus, nwrite);
+
+ return true;
+}
+
+/*
+ * npcm_i2c_slave_wr_buf_sync:
+ * currently slave IF only supports single byte operations.
+ * in order to utilyze the npcm HW FIFO, the driver will ask for 16 bytes
+ * at a time, pack them in buffer, and then transmit them all together
+ * to the FIFO and onward to the bus.
+ * NACK on read will be once reached to bus->adap->quirks->max_read_len.
+ * sending a NACK wherever the backend requests for it is not supported.
+ * the next two functions allow reading to local buffer before writing it all
+ * to the HW FIFO.
+ * ret val: number of bytes read from the IF:
+ */
+static int npcm_i2c_slave_wr_buf_sync(struct npcm_i2c *bus)
+{
+ int left_in_fifo;
+
+ left_in_fifo = FIELD_GET(NPCM_I2CTXF_STS_TX_BYTES,
+ ioread8(bus->reg + NPCM_I2CTXF_STS));
+ if (left_in_fifo >= I2C_HW_FIFO_SIZE)
+ return left_in_fifo;
+
+ /* fifo already full: */
+ if (bus->slv_wr_size >= I2C_HW_FIFO_SIZE)
+ return left_in_fifo;
+
+ /* update the wr fifo ind, back to the untransmitted bytes: */
+ bus->slv_wr_ind = bus->slv_wr_ind - left_in_fifo;
+ bus->slv_wr_size = bus->slv_wr_size + left_in_fifo;
+
+ if (bus->slv_wr_ind < 0)
+ bus->slv_wr_ind += I2C_HW_FIFO_SIZE;
+
+ return left_in_fifo;
+}
+
+static void npcm_i2c_slave_rd_wr(struct npcm_i2c *bus)
+{
+ if (FIELD_GET(NPCM_I2CST_XMIT, ioread8(bus->reg + NPCM_I2CST))) {
+ /*
+ * Slave got an address match with direction bit 1 so
+ * it should transmit data
+ * Write till the master will NACK
+ */
+ bus->operation = I2C_WRITE_OPER;
+ npcm_i2c_slave_xmit(bus,
+ bus->adap.quirks->max_write_len,
+ bus->slv_wr_buf);
+ } else {
+ /*
+ * Slave got an address match with direction bit 0
+ * so it should receive data.
+ * this module does not support saying no to bytes.
+ * it will always ACK.
+ */
+ bus->operation = I2C_READ_OPER;
+ npcm_i2c_read_from_fifo(bus, npcm_i2c_fifo_usage(bus));
+ bus->stop_ind = I2C_SLAVE_RCV_IND;
+ npcm_i2c_slave_send_rd_buf(bus);
+ npcm_i2c_slave_receive(bus,
+ bus->adap.quirks->max_read_len,
+ bus->slv_rd_buf);
+ }
+}
+
+static irqreturn_t npcm_i2c_int_slave_handler(struct npcm_i2c *bus)
+{
+ u8 val;
+ irqreturn_t ret = IRQ_NONE;
+ u8 i2cst = ioread8(bus->reg + NPCM_I2CST);
+
+ /* Slave: A NACK has occurred */
+ if (FIELD_GET(NPCM_I2CST_NEGACK, i2cst)) {
+ bus->stop_ind = I2C_NACK_IND;
+ npcm_i2c_slave_wr_buf_sync(bus);
+ if (bus->fifo_use)
+ /* clear the FIFO */
+ iowrite8(NPCM_I2CFIF_CTS_CLR_FIFO,
+ bus->reg + NPCM_I2CFIF_CTS);
+
+ /* In slave write, NACK is OK, otherwise it is a problem */
+ bus->stop_ind = I2C_NO_STATUS_IND;
+ bus->operation = I2C_NO_OPER;
+ bus->own_slave_addr = 0xFF;
+
+ /*
+ * Slave has to wait for I2C_STOP to decide this is the end
+ * of the transaction.
+ * Therefore transaction is not yet considered as done
+ */
+ iowrite8(NPCM_I2CST_NEGACK, bus->reg + NPCM_I2CST);
+
+ ret = IRQ_HANDLED;
+ }
+
+ /* Slave mode: a Bus Error (BER) has been identified */
+ if (FIELD_GET(NPCM_I2CST_BER, i2cst)) {
+ /*
+ * Check whether bus arbitration or Start or Stop during data
+ * xfer bus arbitration problem should not result in recovery
+ */
+ bus->stop_ind = I2C_BUS_ERR_IND;
+
+ /* wait for bus busy before clear fifo */
+ iowrite8(NPCM_I2CFIF_CTS_CLR_FIFO, bus->reg + NPCM_I2CFIF_CTS);
+
+ bus->state = I2C_IDLE;
+
+ /*
+ * in BER case we might get 2 interrupts: one for slave one for
+ * master ( for a channel which is master\slave switching)
+ */
+ if (completion_done(&bus->cmd_complete) == false) {
+ bus->cmd_err = -EIO;
+ complete(&bus->cmd_complete);
+ }
+ bus->own_slave_addr = 0xFF;
+ iowrite8(NPCM_I2CST_BER, bus->reg + NPCM_I2CST);
+ ret = IRQ_HANDLED;
+ }
+
+ /* A Slave Stop Condition has been identified */
+ if (FIELD_GET(NPCM_I2CST_SLVSTP, i2cst)) {
+ u8 bytes_in_fifo = npcm_i2c_fifo_usage(bus);
+
+ bus->stop_ind = I2C_SLAVE_DONE_IND;
+
+ if (bus->operation == I2C_READ_OPER)
+ npcm_i2c_read_from_fifo(bus, bytes_in_fifo);
+
+ /* if the buffer is empty nothing will be sent */
+ npcm_i2c_slave_send_rd_buf(bus);
+
+ /* Slave done transmitting or receiving */
+ bus->stop_ind = I2C_NO_STATUS_IND;
+
+ /*
+ * Note, just because we got here, it doesn't mean we through
+ * away the wr buffer.
+ * we keep it until the next received offset.
+ */
+ bus->operation = I2C_NO_OPER;
+ bus->int_cnt = 0;
+ bus->own_slave_addr = 0xFF;
+ i2c_slave_event(bus->slave, I2C_SLAVE_STOP, 0);
+ iowrite8(NPCM_I2CST_SLVSTP, bus->reg + NPCM_I2CST);
+ if (bus->fifo_use) {
+ npcm_i2c_clear_fifo_int(bus);
+ npcm_i2c_clear_rx_fifo(bus);
+ npcm_i2c_clear_tx_fifo(bus);
+
+ iowrite8(NPCM_I2CFIF_CTS_CLR_FIFO,
+ bus->reg + NPCM_I2CFIF_CTS);
+ }
+ bus->state = I2C_IDLE;
+ ret = IRQ_HANDLED;
+ }
+
+ /* restart condition occurred and Rx-FIFO was not empty */
+ if (bus->fifo_use && FIELD_GET(NPCM_I2CFIF_CTS_SLVRSTR,
+ ioread8(bus->reg + NPCM_I2CFIF_CTS))) {
+ bus->stop_ind = I2C_SLAVE_RESTART_IND;
+ bus->master_or_slave = I2C_SLAVE;
+ if (bus->operation == I2C_READ_OPER)
+ npcm_i2c_read_from_fifo(bus, npcm_i2c_fifo_usage(bus));
+ bus->operation = I2C_WRITE_OPER;
+ iowrite8(0, bus->reg + NPCM_I2CRXF_CTL);
+ val = NPCM_I2CFIF_CTS_CLR_FIFO | NPCM_I2CFIF_CTS_SLVRSTR |
+ NPCM_I2CFIF_CTS_RXF_TXE;
+ iowrite8(val, bus->reg + NPCM_I2CFIF_CTS);
+ npcm_i2c_slave_rd_wr(bus);
+ ret = IRQ_HANDLED;
+ }
+
+ /* A Slave Address Match has been identified */
+ if (FIELD_GET(NPCM_I2CST_NMATCH, i2cst)) {
+ u8 info = 0;
+
+ /* Address match automatically implies slave mode */
+ bus->master_or_slave = I2C_SLAVE;
+ npcm_i2c_clear_fifo_int(bus);
+ npcm_i2c_clear_rx_fifo(bus);
+ npcm_i2c_clear_tx_fifo(bus);
+ iowrite8(0, bus->reg + NPCM_I2CTXF_CTL);
+ iowrite8(I2C_HW_FIFO_SIZE, bus->reg + NPCM_I2CRXF_CTL);
+ if (FIELD_GET(NPCM_I2CST_XMIT, i2cst)) {
+ bus->operation = I2C_WRITE_OPER;
+ } else {
+ i2c_slave_event(bus->slave, I2C_SLAVE_WRITE_REQUESTED,
+ &info);
+ bus->operation = I2C_READ_OPER;
+ }
+ if (bus->own_slave_addr == 0xFF) {
+ /* Check which type of address match */
+ val = ioread8(bus->reg + NPCM_I2CCST);
+ if (!!(NPCM_I2CCST_MATCH & val)) {
+ u16 addr;
+ enum i2c_addr eaddr;
+ u8 i2ccst2;
+ u8 i2ccst3;
+
+ i2ccst3 = ioread8(bus->reg + NPCM_I2CCST3);
+ i2ccst2 = ioread8(bus->reg + NPCM_I2CCST2);
+
+ /*
+ * the i2c module can response to 10 own SA.
+ * check which one was addressed by the master.
+ * repond to the first one.
+ */
+ addr = ((i2ccst3 & 0x07) << 7) |
+ (i2ccst2 & 0x7F);
+ info = ffs(addr);
+ eaddr = (enum i2c_addr)info;
+ addr = npcm_i2c_get_slave_addr(bus, eaddr);
+ addr &= 0x7F;
+ bus->own_slave_addr = addr;
+ if (bus->PEC_mask & BIT(info))
+ bus->PEC_use = true;
+ else
+ bus->PEC_use = false;
+ } else {
+ val = ioread8(bus->reg + NPCM_I2CCST);
+ if (!!(NPCM_I2CCST_GCMATCH & val))
+ bus->own_slave_addr = 0;
+ if (!!(NPCM_I2CCST_ARPMATCH & val))
+ bus->own_slave_addr = 0x61;
+ }
+ } else {
+ /*
+ * Slave match can happen in two options:
+ * 1. Start, SA, read (slave read without further ado)
+ * 2. Start, SA, read, data, restart, SA, read, ...
+ * (slave read in fragmented mode)
+ * 3. Start, SA, write, data, restart, SA, read, ..
+ * (regular write-read mode)
+ */
+ if ((bus->state == I2C_OPER_STARTED &&
+ bus->operation == I2C_READ_OPER &&
+ bus->stop_ind == I2C_SLAVE_XMIT_IND) ||
+ bus->stop_ind == I2C_SLAVE_RCV_IND) {
+ /* slave tx after slave rx w/o STOP */
+ bus->stop_ind = I2C_SLAVE_RESTART_IND;
+ }
+ }
+
+ if (FIELD_GET(NPCM_I2CST_XMIT, i2cst))
+ bus->stop_ind = I2C_SLAVE_XMIT_IND;
+ else
+ bus->stop_ind = I2C_SLAVE_RCV_IND;
+ bus->state = I2C_SLAVE_MATCH;
+ npcm_i2c_slave_rd_wr(bus);
+ iowrite8(NPCM_I2CST_NMATCH, bus->reg + NPCM_I2CST);
+ ret = IRQ_HANDLED;
+ }
+
+ /* Slave SDA status is set - tx or rx */
+ if (FIELD_GET(NPCM_I2CST_SDAST, i2cst) ||
+ (bus->fifo_use &&
+ (npcm_i2c_tx_fifo_empty(bus) || npcm_i2c_rx_fifo_full(bus)))) {
+ npcm_i2c_slave_rd_wr(bus);
+ iowrite8(NPCM_I2CST_SDAST, bus->reg + NPCM_I2CST);
+ ret = IRQ_HANDLED;
+ } /* SDAST */
+
+ return ret;
+}
+
+static int npcm_i2c_reg_slave(struct i2c_client *client)
+{
+ unsigned long lock_flags;
+ struct npcm_i2c *bus = i2c_get_adapdata(client->adapter);
+
+ bus->slave = client;
+
+ if (!bus->slave)
+ return -EINVAL;
+
+ if (client->flags & I2C_CLIENT_TEN)
+ return -EAFNOSUPPORT;
+
+ spin_lock_irqsave(&bus->lock, lock_flags);
+
+ npcm_i2c_init_params(bus);
+ bus->slv_rd_size = 0;
+ bus->slv_wr_size = 0;
+ bus->slv_rd_ind = 0;
+ bus->slv_wr_ind = 0;
+ if (client->flags & I2C_CLIENT_PEC)
+ bus->PEC_use = true;
+
+ dev_info(bus->dev, "i2c%d register slave SA=0x%x, PEC=%d\n", bus->num,
+ client->addr, bus->PEC_use);
+
+ npcm_i2c_slave_enable_l(bus, I2C_SLAVE_ADDR1, client->addr, true);
+ npcm_i2c_clear_fifo_int(bus);
+ npcm_i2c_clear_rx_fifo(bus);
+ npcm_i2c_clear_tx_fifo(bus);
+ npcm_i2c_slave_int_enable(bus, true);
+
+ spin_unlock_irqrestore(&bus->lock, lock_flags);
+ return 0;
+}
+
+static int npcm_i2c_unreg_slave(struct i2c_client *client)
+{
+ struct npcm_i2c *bus = client->adapter->algo_data;
+ unsigned long lock_flags;
+
+ spin_lock_irqsave(&bus->lock, lock_flags);
+ if (!bus->slave) {
+ spin_unlock_irqrestore(&bus->lock, lock_flags);
+ return -EINVAL;
+ }
+ npcm_i2c_slave_int_enable(bus, false);
+ npcm_i2c_remove_slave_addr(bus, client->addr);
+ bus->slave = NULL;
+ spin_unlock_irqrestore(&bus->lock, lock_flags);
+ return 0;
+}
+#endif /* CONFIG_I2C_SLAVE */
+
static void npcm_i2c_master_fifo_read(struct npcm_i2c *bus)
{
int rcount;
@@ -1415,6 +2013,9 @@ static int __npcm_i2c_init(struct npcm_i2c *bus, struct platform_device *pdev)
bus->master_or_slave = I2C_SLAVE;
bus->int_time_stamp = 0;
bus->xmits = 0;
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+ bus->slave = NULL;
+#endif
ret = device_property_read_u32(&pdev->dev, "bus-frequency",
&clk_freq_hz);
@@ -1446,6 +2047,12 @@ static irqreturn_t npcm_i2c_bus_irq(int irq, void *dev_id)
if (!npcm_i2c_int_master_handler(bus))
return IRQ_HANDLED;
}
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+ if (bus->slave) {
+ bus->master_or_slave = I2C_SLAVE;
+ return npcm_i2c_int_slave_handler(bus);
+ }
+#endif
return IRQ_NONE;
}
@@ -1587,6 +2194,11 @@ static int npcm_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
*/
spin_lock_irqsave(&bus->lock, flags);
bus_busy = ioread8(bus->reg + NPCM_I2CCST) & NPCM_I2CCST_BB;
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+ if (!bus_busy && bus->slave)
+ iowrite8((bus->slave->addr & 0x7F),
+ bus->reg + NPCM_I2CADDR1);
+#endif
spin_unlock_irqrestore(&bus->lock, flags);
} while (time_is_after_jiffies(time_left) && bus_busy);
@@ -1638,6 +2250,12 @@ static int npcm_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
if (bus->cmd_err == -EAGAIN)
ret = i2c_recover_bus(adap);
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+ /* reenable slave if it was enabled */
+ if (bus->slave)
+ iowrite8((bus->slave->addr & 0x7F) | NPCM_I2CADDR_SAEN,
+ bus->reg + NPCM_I2CADDR1);
+#endif
return bus->cmd_err;
}
@@ -1660,6 +2278,10 @@ static const struct i2c_adapter_quirks npcm_i2c_quirks = {
static const struct i2c_algorithm npcm_i2c_algo = {
.master_xfer = npcm_i2c_master_xfer,
.functionality = npcm_i2c_functionality,
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+ .reg_slave = npcm_i2c_reg_slave,
+ .unreg_slave = npcm_i2c_unreg_slave,
+#endif
};
#ifdef CONFIG_DEBUG_FS
--
2.22.0
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* Re: [PATCH 06/11] irqchip/gic-v3: Configure SGIs as standard interrupts
From: Sumit Garg @ 2020-05-20 9:52 UTC (permalink / raw)
To: Marc Zyngier
Cc: kernel-team, Russell King, Jason Cooper, Catalin Marinas,
Linux Kernel Mailing List, Thomas Gleixner, Will Deacon,
linux-arm-kernel
In-Reply-To: <20200519161755.209565-7-maz@kernel.org>
Hi Marc,
On Tue, 19 May 2020 at 21:48, Marc Zyngier <maz@kernel.org> wrote:
>
> Change the way we deal with GICv3 SGIs by turning them into proper
> IRQs, and calling into the arch code to register the interrupt range
> instead of a callback.
>
> Signed-off-by: Marc Zyngier <maz@kernel.org>
> ---
> drivers/irqchip/irq-gic-v3.c | 91 +++++++++++++++++++++---------------
> 1 file changed, 53 insertions(+), 38 deletions(-)
>
> diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c
> index 23d7c87da407..d57289057b75 100644
> --- a/drivers/irqchip/irq-gic-v3.c
> +++ b/drivers/irqchip/irq-gic-v3.c
> @@ -36,6 +36,9 @@
> #define FLAGS_WORKAROUND_GICR_WAKER_MSM8996 (1ULL << 0)
> #define FLAGS_WORKAROUND_CAVIUM_ERRATUM_38539 (1ULL << 1)
>
> +#define GIC_IRQ_TYPE_PARTITION (GIC_IRQ_TYPE_LPI + 1)
> +#define GIC_IRQ_TYPE_SGI (GIC_IRQ_TYPE_LPI + 2)
> +
> struct redist_region {
> void __iomem *redist_base;
> phys_addr_t phys_base;
> @@ -657,38 +660,14 @@ static asmlinkage void __exception_irq_entry gic_handle_irq(struct pt_regs *regs
> if ((irqnr >= 1020 && irqnr <= 1023))
> return;
>
> - /* Treat anything but SGIs in a uniform way */
> - if (likely(irqnr > 15)) {
> - int err;
> -
> - if (static_branch_likely(&supports_deactivate_key))
> - gic_write_eoir(irqnr);
> - else
> - isb();
> -
> - err = handle_domain_irq(gic_data.domain, irqnr, regs);
> - if (err) {
> - WARN_ONCE(true, "Unexpected interrupt received!\n");
> - gic_deactivate_unhandled(irqnr);
> - }
> - return;
> - }
> - if (irqnr < 16) {
> + if (static_branch_likely(&supports_deactivate_key))
> gic_write_eoir(irqnr);
> - if (static_branch_likely(&supports_deactivate_key))
> - gic_write_dir(irqnr);
> -#ifdef CONFIG_SMP
> - /*
> - * Unlike GICv2, we don't need an smp_rmb() here.
> - * The control dependency from gic_read_iar to
> - * the ISB in gic_write_eoir is enough to ensure
> - * that any shared data read by handle_IPI will
> - * be read after the ACK.
> - */
> - handle_IPI(irqnr, regs);
> -#else
> - WARN_ONCE(true, "Unexpected SGI received!\n");
> -#endif
> + else
> + isb();
> +
> + if (handle_domain_irq(gic_data.domain, irqnr, regs)) {
> + WARN_ONCE(true, "Unexpected interrupt received!\n");
> + gic_deactivate_unhandled(irqnr);
> }
> }
>
> @@ -1136,11 +1115,11 @@ static void gic_send_sgi(u64 cluster_id, u16 tlist, unsigned int irq)
> gic_write_sgi1r(val);
> }
>
> -static void gic_raise_softirq(const struct cpumask *mask, unsigned int irq)
> +static void gic_ipi_send_mask(struct irq_data *d, const struct cpumask *mask)
> {
> int cpu;
>
> - if (WARN_ON(irq >= 16))
> + if (WARN_ON(d->hwirq >= 16))
> return;
>
> /*
> @@ -1154,7 +1133,7 @@ static void gic_raise_softirq(const struct cpumask *mask, unsigned int irq)
> u16 tlist;
>
> tlist = gic_compute_target_list(&cpu, mask, cluster_id);
> - gic_send_sgi(cluster_id, tlist, irq);
> + gic_send_sgi(cluster_id, tlist, d->hwirq);
> }
>
> /* Force the above writes to ICC_SGI1R_EL1 to be executed */
> @@ -1163,10 +1142,36 @@ static void gic_raise_softirq(const struct cpumask *mask, unsigned int irq)
>
> static void gic_smp_init(void)
> {
> - set_smp_cross_call(gic_raise_softirq);
> + struct irq_fwspec sgi_fwspec = {
> + .fwnode = gic_data.fwnode,
> + };
> + int base_sgi;
> +
> cpuhp_setup_state_nocalls(CPUHP_AP_IRQ_GIC_STARTING,
> "irqchip/arm/gicv3:starting",
> gic_starting_cpu, NULL);
> +
> + if (is_of_node(gic_data.fwnode)) {
> + /* DT */
> + sgi_fwspec.param_count = 3;
> + sgi_fwspec.param[0] = GIC_IRQ_TYPE_SGI;
> + sgi_fwspec.param[1] = 0;
> + sgi_fwspec.param[2] = IRQ_TYPE_EDGE_RISING;
> + } else {
> + /* ACPI */
> + sgi_fwspec.param_count = 2;
> + sgi_fwspec.param[0] = 0;
> + sgi_fwspec.param[1] = IRQ_TYPE_EDGE_RISING;
> + }
> +
> + /* Register all 8 non-secure SGIs */
> + base_sgi = __irq_domain_alloc_irqs(gic_data.domain, -1, 8,
> + NUMA_NO_NODE, &sgi_fwspec,
> + false, NULL);
> + if (WARN_ON(base_sgi <= 0))
> + return;
> +
> + set_smp_ipi_range(base_sgi, 8);
> }
>
> static int gic_set_affinity(struct irq_data *d, const struct cpumask *mask_val,
> @@ -1215,6 +1220,7 @@ static int gic_set_affinity(struct irq_data *d, const struct cpumask *mask_val,
> }
> #else
> #define gic_set_affinity NULL
> +#define gic_ipi_send_mask NULL
> #define gic_smp_init() do { } while(0)
> #endif
>
> @@ -1257,6 +1263,7 @@ static struct irq_chip gic_chip = {
> .irq_set_irqchip_state = gic_irq_set_irqchip_state,
> .irq_nmi_setup = gic_irq_nmi_setup,
> .irq_nmi_teardown = gic_irq_nmi_teardown,
> + .ipi_send_mask = gic_ipi_send_mask,
> .flags = IRQCHIP_SET_TYPE_MASKED |
> IRQCHIP_SKIP_SET_WAKE |
> IRQCHIP_MASK_ON_SUSPEND,
It looks like you missed to update "struct irq_chip gic_eoimode1_chip"
with similar change as follows:
diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c
index 2a09634..ceef63b 100644
--- a/drivers/irqchip/irq-gic-v3.c
+++ b/drivers/irqchip/irq-gic-v3.c
@@ -1291,6 +1291,7 @@ static struct irq_chip gic_eoimode1_chip = {
.irq_set_vcpu_affinity = gic_irq_set_vcpu_affinity,
.irq_nmi_setup = gic_irq_nmi_setup,
.irq_nmi_teardown = gic_irq_nmi_teardown,
+ .ipi_send_mask = gic_ipi_send_mask,
.flags = IRQCHIP_SET_TYPE_MASKED |
IRQCHIP_SKIP_SET_WAKE |
IRQCHIP_MASK_ON_SUSPEND,
After incorporating this change, your patch-set works fine on my
Developerbox machine.
-Sumit
> @@ -1289,6 +1296,13 @@ static int gic_irq_domain_map(struct irq_domain *d, unsigned int irq,
>
> switch (__get_intid_range(hw)) {
> case SGI_RANGE:
> + irq_set_percpu_devid(irq);
> + irq_domain_set_info(d, irq, hw, chip, d->host_data,
> + handle_percpu_devid_fasteoi_ipi,
> + NULL, NULL);
> + irq_set_status_flags(irq, IRQ_NOAUTOEN);
> + break;
> +
> case PPI_RANGE:
> case EPPI_RANGE:
> irq_set_percpu_devid(irq);
> @@ -1319,8 +1333,6 @@ static int gic_irq_domain_map(struct irq_domain *d, unsigned int irq,
> return 0;
> }
>
> -#define GIC_IRQ_TYPE_PARTITION (GIC_IRQ_TYPE_LPI + 1)
> -
> static int gic_irq_domain_translate(struct irq_domain *d,
> struct irq_fwspec *fwspec,
> unsigned long *hwirq,
> @@ -1353,6 +1365,9 @@ static int gic_irq_domain_translate(struct irq_domain *d,
> else
> *hwirq += 16;
> break;
> + case GIC_IRQ_TYPE_SGI:
> + *hwirq = fwspec->param[1];
> + break;
> default:
> return -EINVAL;
> }
> @@ -1657,9 +1672,9 @@ static int __init gic_init_bases(void __iomem *dist_base,
>
> gic_update_rdist_properties();
>
> - gic_smp_init();
> gic_dist_init();
> gic_cpu_init();
> + gic_smp_init();
> gic_cpu_pm_init();
>
> if (gic_dist_supports_lpis()) {
> --
> 2.26.2
>
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* Re: [PATCH v2 1/2] arm64: vdso: Don't prefix sigreturn trampoline with a BTI C instruction
From: Will Deacon @ 2020-05-20 9:53 UTC (permalink / raw)
To: Dave Martin
Cc: Tamas Zsoldos, Mark Brown, kernel-team, linux-arm-kernel,
Daniel Kiss
In-Reply-To: <20200520093354.GJ5031@arm.com>
On Wed, May 20, 2020 at 10:33:55AM +0100, Dave Martin wrote:
> On Tue, May 19, 2020 at 05:28:20PM +0100, Will Deacon wrote:
> > For better or worse, GDB relies on the exact instruction sequence in the
> > VDSO sigreturn trampoline in order to unwind from signals correctly.
> > Commit c91db232da48 ("arm64: vdso: Convert to modern assembler annotations")
> > unfortunately added a BTI C instruction to the start of __kernel_rt_sigreturn,
> > which breaks this check. Thankfully, it's also not required, since the
> > trampoline is called from a RET instruction when returning from the signal
> > handler
> >
> > Remove the unnecessary BTI C instruction from __kernel_rt_sigreturn,
> > and do the same for the 32-bit VDSO as well for good measure.
> >
> > Cc: Dave Martin <dave.martin@arm.com>
> > Cc: Mark Brown <broonie@kernel.org>
> > Cc: Daniel Kiss <daniel.kiss@arm.com>
> > Cc: Tamas Zsoldos <tamas.zsoldos@arm.com>
> > Fixes: c91db232da48 ("arm64: vdso: Convert to modern assembler annotations")
> > Signed-off-by: Will Deacon <will@kernel.org>
> > ---
> > arch/arm64/kernel/vdso/sigreturn.S | 11 +++++++++--
> > arch/arm64/kernel/vdso32/sigreturn.S | 16 ++++++++--------
> > 2 files changed, 17 insertions(+), 10 deletions(-)
> >
> > diff --git a/arch/arm64/kernel/vdso/sigreturn.S b/arch/arm64/kernel/vdso/sigreturn.S
> > index 3fb13b81f780..0c921130002a 100644
> > --- a/arch/arm64/kernel/vdso/sigreturn.S
> > +++ b/arch/arm64/kernel/vdso/sigreturn.S
> > @@ -15,7 +15,14 @@
> > .text
> >
> > nop
> > -SYM_FUNC_START(__kernel_rt_sigreturn)
> > +/*
> > + * GDB relies on being able to identify the sigreturn instruction sequence to
> > + * unwind from signal handlers. We cannot, therefore, use SYM_FUNC_START()
> > + * here, as it will emit a BTI C instruction and break the unwinder. Thankfully,
> > + * this function is only ever called from a RET and so omitting the landing pad
> > + * is perfectly fine.
> > + */
>
> Can we cross-reference or duplicate (perhaps abridged) this comment for
> vdso32?
Yes, that's not a bad idea. I'll add a comment to the top of that file.
> Can we also fix the comment by the definition of SYM_FUNC_START()?
I'll tweak it slightly for v3.
> > diff --git a/arch/arm64/kernel/vdso32/sigreturn.S b/arch/arm64/kernel/vdso32/sigreturn.S
> > index 620524969696..b36d4e2267a3 100644
> > --- a/arch/arm64/kernel/vdso32/sigreturn.S
> > +++ b/arch/arm64/kernel/vdso32/sigreturn.S
> > @@ -17,39 +17,39 @@
> > .save {r0-r15}
> > .pad #COMPAT_SIGFRAME_REGS_OFFSET
> > nop
> > -SYM_FUNC_START(__kernel_sigreturn_arm)
> > +SYM_CODE_START(__kernel_sigreturn_arm)
>
> ...although do we actually need this? 32-bit doesn't have BTI.
>
> But for the reasons given above, this is not a "function" and so
> SYM_FUNC_START() is trap for future maintenance even if it makes no
> difference now.
Right, it's just done for consistency on the 32-bit side.
> Either way,
>
> Reviewed-by: Dave Martin <Dave.Martin@arm.com>
Thanks!
Will
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* [PATCH v11 2/3] i2c: npcm7xx: Add Nuvoton NPCM I2C controller driver
From: Tali Perry @ 2020-05-20 9:51 UTC (permalink / raw)
To: ofery, brendanhiggins, avifishman70, tmaimon77, kfting, venture,
yuenn, benjaminfair, robh+dt, wsa, andriy.shevchenko
Cc: devicetree, openbmc, linux-kernel, Tali Perry, linux-i2c,
linux-arm-kernel
In-Reply-To: <20200520095113.185414-1-tali.perry1@gmail.com>
Add Nuvoton NPCM BMC I2C controller driver.
Signed-off-by: Tali Perry <tali.perry1@gmail.com>
---
drivers/i2c/busses/Kconfig | 9 +
drivers/i2c/busses/Makefile | 1 +
drivers/i2c/busses/i2c-npcm7xx.c | 1858 ++++++++++++++++++++++++++++++
3 files changed, 1868 insertions(+)
create mode 100644 drivers/i2c/busses/i2c-npcm7xx.c
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index 2ddca08f8a76..a2cfc555c284 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -791,6 +791,15 @@ config I2C_NOMADIK
I2C interface from ST-Ericsson's Nomadik and Ux500 architectures,
as well as the STA2X11 PCIe I/O HUB.
+config I2C_NPCM7XX
+ tristate "Nuvoton I2C Controller"
+ depends on ARCH_NPCM7XX || COMPILE_TEST
+ help
+ If you say yes to this option, support will be included for the
+ Nuvoton I2C controller, which is available on the NPCM7xx BMC
+ controller.
+ Driver can also support slave mode (select I2C_SLAVE).
+
config I2C_OCORES
tristate "OpenCores I2C Controller"
help
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index 25d60889713c..8f3dfd376bec 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -81,6 +81,7 @@ obj-$(CONFIG_I2C_MT7621) += i2c-mt7621.o
obj-$(CONFIG_I2C_MV64XXX) += i2c-mv64xxx.o
obj-$(CONFIG_I2C_MXS) += i2c-mxs.o
obj-$(CONFIG_I2C_NOMADIK) += i2c-nomadik.o
+obj-$(CONFIG_I2C_NPCM7XX) += i2c-npcm7xx.o
obj-$(CONFIG_I2C_OCORES) += i2c-ocores.o
obj-$(CONFIG_I2C_OMAP) += i2c-omap.o
obj-$(CONFIG_I2C_OWL) += i2c-owl.o
diff --git a/drivers/i2c/busses/i2c-npcm7xx.c b/drivers/i2c/busses/i2c-npcm7xx.c
new file mode 100644
index 000000000000..62aba7897365
--- /dev/null
+++ b/drivers/i2c/busses/i2c-npcm7xx.c
@@ -0,0 +1,1858 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Nuvoton NPCM7xx I2C Controller driver
+ *
+ * Copyright (C) 2020 Nuvoton Technologies tali.perry@nuvoton.com
+ */
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#ifdef CONFIG_DEBUG_FS
+#include <linux/debugfs.h>
+#endif
+#include <linux/errno.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/iopoll.h>
+#include <linux/irq.h>
+#include <linux/jiffies.h>
+#include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+enum i2c_mode {
+ I2C_MASTER,
+ I2C_SLAVE,
+};
+
+/*
+ * External I2C Interface driver xfer indication values, which indicate status
+ * of the bus.
+ */
+enum i2c_state_ind {
+ I2C_NO_STATUS_IND = 0,
+ I2C_SLAVE_RCV_IND,
+ I2C_SLAVE_XMIT_IND,
+ I2C_SLAVE_XMIT_MISSING_DATA_IND,
+ I2C_SLAVE_RESTART_IND,
+ I2C_SLAVE_DONE_IND,
+ I2C_MASTER_DONE_IND,
+ I2C_NACK_IND,
+ I2C_BUS_ERR_IND,
+ I2C_WAKE_UP_IND,
+ I2C_BLOCK_BYTES_ERR_IND,
+ I2C_SLAVE_RCV_MISSING_DATA_IND,
+};
+
+/*
+ * Operation type values (used to define the operation currently running)
+ * module is interrupt driven, on each interrupt the current operation is
+ * checked to see if the module is currently reading or writing.
+ */
+enum i2c_oper {
+ I2C_NO_OPER = 0,
+ I2C_WRITE_OPER,
+ I2C_READ_OPER,
+};
+
+/* I2C Bank (module had 2 banks of registers) */
+enum i2c_bank {
+ I2C_BANK_0 = 0,
+ I2C_BANK_1,
+};
+
+/* Internal I2C states values (for the I2C module state machine). */
+enum i2c_state {
+ I2C_DISABLE = 0,
+ I2C_IDLE,
+ I2C_MASTER_START,
+ I2C_SLAVE_MATCH,
+ I2C_OPER_STARTED,
+ I2C_STOP_PENDING,
+};
+
+/* init register and default value required to enable module */
+#define NPCM_I2CSEGCTL 0xE4
+#define NPCM_I2CSEGCTL_INIT_VAL 0x0333F000
+
+/* Common regs */
+#define NPCM_I2CSDA 0x00
+#define NPCM_I2CST 0x02
+#define NPCM_I2CCST 0x04
+#define NPCM_I2CCTL1 0x06
+#define NPCM_I2CADDR1 0x08
+#define NPCM_I2CCTL2 0x0A
+#define NPCM_I2CADDR2 0x0C
+#define NPCM_I2CCTL3 0x0E
+#define NPCM_I2CCST2 0x18
+#define NPCM_I2CCST3 0x19
+#define I2C_VER 0x1F
+
+/*BANK0 regs*/
+#define NPCM_I2CADDR3 0x10
+#define NPCM_I2CADDR7 0x11
+#define NPCM_I2CADDR4 0x12
+#define NPCM_I2CADDR8 0x13
+#define NPCM_I2CADDR5 0x14
+#define NPCM_I2CADDR9 0x15
+#define NPCM_I2CADDR6 0x16
+#define NPCM_I2CADDR10 0x17
+
+#define NPCM_I2CCTL4 0x1A
+#define NPCM_I2CCTL5 0x1B
+#define NPCM_I2CSCLLT 0x1C /* SCL Low Time */
+#define NPCM_I2CFIF_CTL 0x1D /* FIFO Control */
+#define NPCM_I2CSCLHT 0x1E /* SCL High Time */
+
+/* BANK 1 regs */
+#define NPCM_I2CFIF_CTS 0x10 /* Both FIFOs Control and Status */
+#define NPCM_I2CTXF_CTL 0x12 /* Tx-FIFO Control */
+#define NPCM_I2CT_OUT 0x14 /* Bus T.O. */
+#define NPCM_I2CPEC 0x16 /* PEC Data */
+#define NPCM_I2CTXF_STS 0x1A /* Tx-FIFO Status */
+#define NPCM_I2CRXF_STS 0x1C /* Rx-FIFO Status */
+#define NPCM_I2CRXF_CTL 0x1E /* Rx-FIFO Control */
+
+/* NPCM_I2CST reg fields */
+#define NPCM_I2CST_XMIT BIT(0)
+#define NPCM_I2CST_MASTER BIT(1)
+#define NPCM_I2CST_NMATCH BIT(2)
+#define NPCM_I2CST_STASTR BIT(3)
+#define NPCM_I2CST_NEGACK BIT(4)
+#define NPCM_I2CST_BER BIT(5)
+#define NPCM_I2CST_SDAST BIT(6)
+#define NPCM_I2CST_SLVSTP BIT(7)
+
+/* NPCM_I2CCST reg fields */
+#define NPCM_I2CCST_BUSY BIT(0)
+#define NPCM_I2CCST_BB BIT(1)
+#define NPCM_I2CCST_MATCH BIT(2)
+#define NPCM_I2CCST_GCMATCH BIT(3)
+#define NPCM_I2CCST_TSDA BIT(4)
+#define NPCM_I2CCST_TGSCL BIT(5)
+#define NPCM_I2CCST_MATCHAF BIT(6)
+#define NPCM_I2CCST_ARPMATCH BIT(7)
+
+/* NPCM_I2CCTL1 reg fields */
+#define NPCM_I2CCTL1_START BIT(0)
+#define NPCM_I2CCTL1_STOP BIT(1)
+#define NPCM_I2CCTL1_INTEN BIT(2)
+#define NPCM_I2CCTL1_EOBINTE BIT(3)
+#define NPCM_I2CCTL1_ACK BIT(4)
+#define NPCM_I2CCTL1_GCMEN BIT(5)
+#define NPCM_I2CCTL1_NMINTE BIT(6)
+#define NPCM_I2CCTL1_STASTRE BIT(7)
+
+/* RW1S fields (inside a RW reg): */
+#define NPCM_I2CCTL1_RWS \
+ (NPCM_I2CCTL1_START | NPCM_I2CCTL1_STOP | NPCM_I2CCTL1_ACK)
+
+/* npcm_i2caddr reg fields */
+#define NPCM_I2CADDR_A GENMASK(6, 0)
+#define NPCM_I2CADDR_SAEN BIT(7)
+
+/* NPCM_I2CCTL2 reg fields */
+#define I2CCTL2_ENABLE BIT(0)
+#define I2CCTL2_SCLFRQ6_0 GENMASK(7, 1)
+
+/* NPCM_I2CCTL3 reg fields */
+#define I2CCTL3_SCLFRQ8_7 GENMASK(1, 0)
+#define I2CCTL3_ARPMEN BIT(2)
+#define I2CCTL3_IDL_START BIT(3)
+#define I2CCTL3_400K_MODE BIT(4)
+#define I2CCTL3_BNK_SEL BIT(5)
+#define I2CCTL3_SDA_LVL BIT(6)
+#define I2CCTL3_SCL_LVL BIT(7)
+
+/* NPCM_I2CCST2 reg fields */
+#define NPCM_I2CCST2_MATCHA1F BIT(0)
+#define NPCM_I2CCST2_MATCHA2F BIT(1)
+#define NPCM_I2CCST2_MATCHA3F BIT(2)
+#define NPCM_I2CCST2_MATCHA4F BIT(3)
+#define NPCM_I2CCST2_MATCHA5F BIT(4)
+#define NPCM_I2CCST2_MATCHA6F BIT(5)
+#define NPCM_I2CCST2_MATCHA7F BIT(5)
+#define NPCM_I2CCST2_INTSTS BIT(7)
+
+/* NPCM_I2CCST3 reg fields */
+#define NPCM_I2CCST3_MATCHA8F BIT(0)
+#define NPCM_I2CCST3_MATCHA9F BIT(1)
+#define NPCM_I2CCST3_MATCHA10F BIT(2)
+#define NPCM_I2CCST3_EO_BUSY BIT(7)
+
+/* NPCM_I2CCTL4 reg fields */
+#define I2CCTL4_HLDT GENMASK(5, 0)
+#define I2CCTL4_LVL_WE BIT(7)
+
+/* NPCM_I2CCTL5 reg fields */
+#define I2CCTL5_DBNCT GENMASK(3, 0)
+
+/* NPCM_I2CFIF_CTS reg fields */
+#define NPCM_I2CFIF_CTS_RXF_TXE BIT(1)
+#define NPCM_I2CFIF_CTS_RFTE_IE BIT(3)
+#define NPCM_I2CFIF_CTS_CLR_FIFO BIT(6)
+#define NPCM_I2CFIF_CTS_SLVRSTR BIT(7)
+
+/* NPCM_I2CTXF_CTL reg fields */
+#define NPCM_I2CTXF_CTL_TX_THR GENMASK(4, 0)
+#define NPCM_I2CTXF_CTL_THR_TXIE BIT(6)
+
+/* NPCM_I2CT_OUT reg fields */
+#define NPCM_I2CT_OUT_TO_CKDIV GENMASK(5, 0)
+#define NPCM_I2CT_OUT_T_OUTIE BIT(6)
+#define NPCM_I2CT_OUT_T_OUTST BIT(7)
+
+/* NPCM_I2CTXF_STS reg fields */
+#define NPCM_I2CTXF_STS_TX_BYTES GENMASK(4, 0)
+#define NPCM_I2CTXF_STS_TX_THST BIT(6)
+
+/* NPCM_I2CRXF_STS reg fields */
+#define NPCM_I2CRXF_STS_RX_BYTES GENMASK(4, 0)
+#define NPCM_I2CRXF_STS_RX_THST BIT(6)
+
+/* NPCM_I2CFIF_CTL reg fields */
+#define NPCM_I2CFIF_CTL_FIFO_EN BIT(4)
+
+/* NPCM_I2CRXF_CTL reg fields */
+#define NPCM_I2CRXF_CTL_RX_THR GENMASK(4, 0)
+#define NPCM_I2CRXF_CTL_LAST_PEC BIT(5)
+#define NPCM_I2CRXF_CTL_THR_RXIE BIT(6)
+
+#define I2C_HW_FIFO_SIZE 16
+
+/* I2C_VER reg fields */
+#define I2C_VER_VERSION GENMASK(6, 0)
+#define I2C_VER_FIFO_EN BIT(7)
+
+/* stall/stuck timeout in us */
+#define DEFAULT_STALL_COUNT 25
+
+/* SCLFRQ field position */
+#define SCLFRQ_0_TO_6 GENMASK(6, 0)
+#define SCLFRQ_7_TO_8 GENMASK(8, 7)
+
+/* supported clk settings. values in Hz. */
+#define I2C_FREQ_MIN_HZ 10000
+#define I2C_FREQ_MAX_HZ I2C_MAX_FAST_MODE_PLUS_FREQ
+
+/* Status of one I2C module */
+struct npcm_i2c {
+ struct i2c_adapter adap;
+ struct device *dev;
+ unsigned char __iomem *reg;
+ spinlock_t lock; /* IRQ synchronization */
+ struct completion cmd_complete;
+ int irq;
+ int cmd_err;
+ struct i2c_msg *msgs;
+ int msgs_num;
+ int num;
+ u32 apb_clk;
+ struct i2c_bus_recovery_info rinfo;
+ enum i2c_state state;
+ enum i2c_oper operation;
+ enum i2c_mode master_or_slave;
+ enum i2c_state_ind stop_ind;
+ u8 dest_addr;
+ u8 *rd_buf;
+ u16 rd_size;
+ u16 rd_ind;
+ u8 *wr_buf;
+ u16 wr_size;
+ u16 wr_ind;
+ bool fifo_use;
+ u16 PEC_mask; /* PEC bit mask per slave address */
+ bool PEC_use;
+ bool read_block_use;
+ u8 int_cnt;
+ u32 clk_period_us;
+ unsigned long int_time_stamp;
+ unsigned long bus_freq; /* in kHz */
+ u32 xmits;
+#ifdef CONFIG_DEBUG_FS
+ struct dentry *debugfs; /* debugfs device directory */
+ u64 ber_count;
+ u64 rec_succ_count;
+ u64 rec_fail_count;
+ u64 nack_count;
+ u64 timeout_count;
+#endif
+};
+
+static inline void npcm_i2c_select_bank(struct npcm_i2c *bus,
+ enum i2c_bank bank)
+{
+ u8 i2cctl3 = ioread8(bus->reg + NPCM_I2CCTL3);
+
+ if (bank == I2C_BANK_0)
+ i2cctl3 = i2cctl3 & ~I2CCTL3_BNK_SEL;
+ else
+ i2cctl3 = i2cctl3 | I2CCTL3_BNK_SEL;
+ iowrite8(i2cctl3, bus->reg + NPCM_I2CCTL3);
+}
+
+static void npcm_i2c_init_params(struct npcm_i2c *bus)
+{
+ bus->stop_ind = I2C_NO_STATUS_IND;
+ bus->rd_size = 0;
+ bus->wr_size = 0;
+ bus->rd_ind = 0;
+ bus->wr_ind = 0;
+ bus->int_cnt = 0;
+ bus->read_block_use = false;
+ bus->int_time_stamp = 0;
+ bus->PEC_use = false;
+ bus->PEC_mask = 0;
+}
+
+static inline void npcm_i2c_wr_byte(struct npcm_i2c *bus, u8 data)
+{
+ iowrite8(data, bus->reg + NPCM_I2CSDA);
+}
+
+static inline u8 npcm_i2c_rd_byte(struct npcm_i2c *bus)
+{
+ return ioread8(bus->reg + NPCM_I2CSDA);
+}
+
+static int npcm_i2c_get_SCL(struct i2c_adapter *_adap)
+{
+ struct npcm_i2c *bus = container_of(_adap, struct npcm_i2c, adap);
+
+ return !!(I2CCTL3_SCL_LVL & ioread32(bus->reg + NPCM_I2CCTL3));
+}
+
+static int npcm_i2c_get_SDA(struct i2c_adapter *_adap)
+{
+ struct npcm_i2c *bus = container_of(_adap, struct npcm_i2c, adap);
+
+ return !!(I2CCTL3_SDA_LVL & ioread32(bus->reg + NPCM_I2CCTL3));
+}
+
+static inline u16 npcm_i2c_get_index(struct npcm_i2c *bus)
+{
+ if (bus->operation == I2C_READ_OPER)
+ return bus->rd_ind;
+ if (bus->operation == I2C_WRITE_OPER)
+ return bus->wr_ind;
+ return 0;
+}
+
+/* quick protocol (just address) */
+static inline bool npcm_i2c_is_quick(struct npcm_i2c *bus)
+{
+ return bus->wr_size == 0 && bus->rd_size == 0;
+}
+
+static void npcm_i2c_disable(struct npcm_i2c *bus)
+{
+ u8 i2cctl2;
+
+ /* Disable module */
+ i2cctl2 = ioread8(bus->reg + NPCM_I2CCTL2);
+ i2cctl2 = i2cctl2 & ~I2CCTL2_ENABLE;
+ iowrite8(i2cctl2, bus->reg + NPCM_I2CCTL2);
+
+ bus->state = I2C_DISABLE;
+}
+
+static void npcm_i2c_enable(struct npcm_i2c *bus)
+{
+ u8 i2cctl2 = ioread8(bus->reg + NPCM_I2CCTL2);
+
+ i2cctl2 = i2cctl2 | I2CCTL2_ENABLE;
+ iowrite8(i2cctl2, bus->reg + NPCM_I2CCTL2);
+ bus->state = I2C_IDLE;
+}
+
+/* enable\disable end of busy (EOB) interrupts */
+static inline void npcm_i2c_eob_int(struct npcm_i2c *bus, bool enable)
+{
+ u8 val;
+
+ /* Clear EO_BUSY pending bit: */
+ val = ioread8(bus->reg + NPCM_I2CCST3);
+ val = val | NPCM_I2CCST3_EO_BUSY;
+ iowrite8(val, bus->reg + NPCM_I2CCST3);
+
+ val = ioread8(bus->reg + NPCM_I2CCTL1);
+ val &= ~NPCM_I2CCTL1_RWS;
+ if (enable)
+ val |= NPCM_I2CCTL1_EOBINTE;
+ else
+ val &= ~NPCM_I2CCTL1_EOBINTE;
+ iowrite8(val, bus->reg + NPCM_I2CCTL1);
+}
+
+static inline bool npcm_i2c_tx_fifo_empty(struct npcm_i2c *bus)
+{
+ u8 tx_fifo_sts;
+
+ tx_fifo_sts = ioread8(bus->reg + NPCM_I2CTXF_STS);
+ /* check if TX FIFO is not empty */
+ if ((tx_fifo_sts & NPCM_I2CTXF_STS_TX_BYTES) == 0)
+ return false;
+
+ /* check if TX FIFO status bit is set: */
+ return !!FIELD_GET(NPCM_I2CTXF_STS_TX_THST, tx_fifo_sts);
+}
+
+static inline bool npcm_i2c_rx_fifo_full(struct npcm_i2c *bus)
+{
+ u8 rx_fifo_sts;
+
+ rx_fifo_sts = ioread8(bus->reg + NPCM_I2CRXF_STS);
+ /* check if RX FIFO is not empty: */
+ if ((rx_fifo_sts & NPCM_I2CRXF_STS_RX_BYTES) == 0)
+ return false;
+
+ /* check if rx fifo full status is set: */
+ return !!FIELD_GET(NPCM_I2CRXF_STS_RX_THST, rx_fifo_sts);
+}
+
+static inline void npcm_i2c_clear_fifo_int(struct npcm_i2c *bus)
+{
+ u8 val;
+
+ val = ioread8(bus->reg + NPCM_I2CFIF_CTS);
+ val = (val & NPCM_I2CFIF_CTS_SLVRSTR) | NPCM_I2CFIF_CTS_RXF_TXE;
+ iowrite8(val, bus->reg + NPCM_I2CFIF_CTS);
+}
+
+static inline void npcm_i2c_clear_tx_fifo(struct npcm_i2c *bus)
+{
+ u8 val;
+
+ val = ioread8(bus->reg + NPCM_I2CTXF_STS);
+ val = val | NPCM_I2CTXF_STS_TX_THST;
+ iowrite8(val, bus->reg + NPCM_I2CTXF_STS);
+}
+
+static inline void npcm_i2c_clear_rx_fifo(struct npcm_i2c *bus)
+{
+ u8 val;
+
+ val = ioread8(bus->reg + NPCM_I2CRXF_STS);
+ val = val | NPCM_I2CRXF_STS_RX_THST;
+ iowrite8(val, bus->reg + NPCM_I2CRXF_STS);
+}
+
+static void npcm_i2c_int_enable(struct npcm_i2c *bus, bool enable)
+{
+ u8 val;
+
+ val = ioread8(bus->reg + NPCM_I2CCTL1);
+ val &= ~NPCM_I2CCTL1_RWS;
+ if (enable)
+ val |= NPCM_I2CCTL1_INTEN;
+ else
+ val &= ~NPCM_I2CCTL1_INTEN;
+ iowrite8(val, bus->reg + NPCM_I2CCTL1);
+}
+
+static inline void npcm_i2c_master_start(struct npcm_i2c *bus)
+{
+ u8 val;
+
+ val = ioread8(bus->reg + NPCM_I2CCTL1);
+ val &= ~(NPCM_I2CCTL1_STOP | NPCM_I2CCTL1_ACK);
+ val |= NPCM_I2CCTL1_START;
+ iowrite8(val, bus->reg + NPCM_I2CCTL1);
+}
+
+static inline void npcm_i2c_master_stop(struct npcm_i2c *bus)
+{
+ u8 val;
+
+ /*
+ * override HW issue: I2C may fail to supply stop condition in Master
+ * Write operation.
+ * Need to delay at least 5 us from the last int, before issueing a stop
+ */
+ udelay(10);
+ val = ioread8(bus->reg + NPCM_I2CCTL1);
+ val &= ~(NPCM_I2CCTL1_START | NPCM_I2CCTL1_ACK);
+ val |= NPCM_I2CCTL1_STOP;
+ iowrite8(val, bus->reg + NPCM_I2CCTL1);
+
+ if (!bus->fifo_use)
+ return;
+
+ npcm_i2c_select_bank(bus, I2C_BANK_1);
+
+ if (bus->operation == I2C_READ_OPER)
+ npcm_i2c_clear_rx_fifo(bus);
+ else
+ npcm_i2c_clear_tx_fifo(bus);
+ npcm_i2c_clear_fifo_int(bus);
+ iowrite8(0, bus->reg + NPCM_I2CTXF_CTL);
+}
+
+static inline void npcm_i2c_stall_after_start(struct npcm_i2c *bus, bool stall)
+{
+ u8 val;
+
+ val = ioread8(bus->reg + NPCM_I2CCTL1);
+ val &= ~NPCM_I2CCTL1_RWS;
+ if (stall)
+ val |= NPCM_I2CCTL1_STASTRE;
+ else
+ val &= ~NPCM_I2CCTL1_STASTRE;
+ iowrite8(val, bus->reg + NPCM_I2CCTL1);
+}
+
+static inline void npcm_i2c_nack(struct npcm_i2c *bus)
+{
+ u8 val;
+
+ val = ioread8(bus->reg + NPCM_I2CCTL1);
+ val &= ~(NPCM_I2CCTL1_STOP | NPCM_I2CCTL1_START);
+ val |= NPCM_I2CCTL1_ACK;
+ iowrite8(val, bus->reg + NPCM_I2CCTL1);
+}
+
+static void npcm_i2c_reset(struct npcm_i2c *bus)
+{
+ /*
+ * Save I2CCTL1 relevant bits. It is being cleared when the module
+ * is disabled.
+ */
+ u8 i2cctl1;
+ u8 i2cctl2;
+
+ i2cctl1 = ioread8(bus->reg + NPCM_I2CCTL1);
+ i2cctl2 = ioread8(bus->reg + NPCM_I2CCTL2);
+
+ npcm_i2c_disable(bus);
+ npcm_i2c_enable(bus);
+
+ /* Restore NPCM_I2CCTL1 Status */
+ i2cctl1 &= ~NPCM_I2CCTL1_RWS;
+ iowrite8(i2cctl1, bus->reg + NPCM_I2CCTL1);
+
+ /* Clear BB (BUS BUSY) bit */
+ iowrite8(NPCM_I2CCST_BB, bus->reg + NPCM_I2CCST);
+ iowrite8(0xFF, bus->reg + NPCM_I2CST);
+
+ /* Clear EOB bit */
+ iowrite8(NPCM_I2CCST3_EO_BUSY, bus->reg + NPCM_I2CCST3);
+
+ /* Clear all fifo bits: */
+ iowrite8(NPCM_I2CFIF_CTS_CLR_FIFO, bus->reg + NPCM_I2CFIF_CTS);
+
+ bus->state = I2C_IDLE;
+}
+
+static inline bool npcm_i2c_is_master(struct npcm_i2c *bus)
+{
+ return !!FIELD_GET(NPCM_I2CST_MASTER, ioread8(bus->reg + NPCM_I2CST));
+}
+
+static void npcm_i2c_callback(struct npcm_i2c *bus,
+ enum i2c_state_ind op_status, u16 info)
+{
+ struct i2c_msg *msgs;
+ int msgs_num;
+
+ msgs = bus->msgs;
+ msgs_num = bus->msgs_num;
+ /*
+ * check that transaction was not timed-out, and msgs still
+ * holds a valid value.
+ */
+ if (!msgs)
+ return;
+
+ if (completion_done(&bus->cmd_complete))
+ return;
+
+ switch (op_status) {
+ case I2C_MASTER_DONE_IND:
+ bus->cmd_err = bus->msgs_num;
+ fallthrough;
+ case I2C_BLOCK_BYTES_ERR_IND:
+ /* Master tx finished and all transmit bytes were sent */
+ if (bus->msgs) {
+ if (msgs[0].flags & I2C_M_RD)
+ msgs[0].len = info;
+ else if (msgs_num == 2 &&
+ msgs[1].flags & I2C_M_RD)
+ msgs[1].len = info;
+ }
+ if (completion_done(&bus->cmd_complete) == false)
+ complete(&bus->cmd_complete);
+ break;
+
+ case I2C_NACK_IND:
+ /* MASTER transmit got a NACK before tx all bytes */
+ bus->cmd_err = -ENXIO;
+ if (bus->master_or_slave == I2C_MASTER)
+ complete(&bus->cmd_complete);
+
+ break;
+ case I2C_BUS_ERR_IND:
+ /* Bus error */
+ bus->cmd_err = -EAGAIN;
+ if (bus->master_or_slave == I2C_MASTER)
+ complete(&bus->cmd_complete);
+
+ break;
+ case I2C_WAKE_UP_IND:
+ /* I2C wake up */
+ break;
+ default:
+ break;
+ }
+
+ bus->operation = I2C_NO_OPER;
+}
+
+static u8 npcm_i2c_fifo_usage(struct npcm_i2c *bus)
+{
+ if (bus->operation == I2C_WRITE_OPER)
+ return FIELD_GET(NPCM_I2CTXF_STS_TX_BYTES,
+ ioread8(bus->reg + NPCM_I2CTXF_STS));
+ if (bus->operation == I2C_READ_OPER)
+ return FIELD_GET(NPCM_I2CRXF_STS_RX_BYTES,
+ ioread8(bus->reg + NPCM_I2CRXF_STS));
+ return 0;
+}
+
+static void npcm_i2c_write_to_fifo_master(struct npcm_i2c *bus,
+ u16 max_bytes_to_send)
+{
+ u8 size_free_fifo;
+
+ /*
+ * Fill the FIFO, while the FIFO is not full and there are more bytes
+ * to write
+ */
+ size_free_fifo = I2C_HW_FIFO_SIZE - npcm_i2c_fifo_usage(bus);
+ while (max_bytes_to_send-- && size_free_fifo) {
+ if (bus->wr_ind < bus->wr_size)
+ npcm_i2c_wr_byte(bus, bus->wr_buf[bus->wr_ind++]);
+ else
+ npcm_i2c_wr_byte(bus, 0xFF);
+ size_free_fifo = I2C_HW_FIFO_SIZE - npcm_i2c_fifo_usage(bus);
+ }
+}
+
+/*
+ * npcm_i2c_set_fifo:
+ * configure the FIFO before using it. If nread is -1 RX FIFO will not be
+ * configured. same for nwrite
+ */
+static void npcm_i2c_set_fifo(struct npcm_i2c *bus, int nread, int nwrite)
+{
+ u8 rxf_ctl = 0;
+
+ if (!bus->fifo_use)
+ return;
+ npcm_i2c_select_bank(bus, I2C_BANK_1);
+ npcm_i2c_clear_tx_fifo(bus);
+ npcm_i2c_clear_rx_fifo(bus);
+
+ /* configure RX FIFO */
+ if (nread > 0) {
+ rxf_ctl = min_t(int, nread, I2C_HW_FIFO_SIZE);
+
+ /* set LAST bit. if LAST is set next FIFO packet is nacked */
+ if (nread <= I2C_HW_FIFO_SIZE)
+ rxf_ctl |= NPCM_I2CRXF_CTL_LAST_PEC;
+
+ /*
+ * if we are about to read the first byte in blk rd mode,
+ * don't NACK it. If slave returns zero size HW can't NACK
+ * it immidiattly, it will read extra byte and then NACK.
+ */
+ if (bus->rd_ind == 0 && bus->read_block_use) {
+ /* set fifo to read one byte, no last: */
+ rxf_ctl = 1;
+ }
+
+ /* set fifo size: */
+ iowrite8(rxf_ctl, bus->reg + NPCM_I2CRXF_CTL);
+ }
+
+ /* configure TX FIFO */
+ if (nwrite > 0) {
+ if (nwrite > I2C_HW_FIFO_SIZE)
+ /* data to send is more then FIFO size. */
+ iowrite8(I2C_HW_FIFO_SIZE, bus->reg + NPCM_I2CTXF_CTL);
+ else
+ iowrite8(nwrite, bus->reg + NPCM_I2CTXF_CTL);
+
+ npcm_i2c_clear_tx_fifo(bus);
+ }
+}
+
+static void npcm_i2c_read_from_fifo(struct npcm_i2c *bus, u8 bytes_in_fifo)
+{
+ u8 data;
+
+ while (bytes_in_fifo--) {
+ data = npcm_i2c_rd_byte(bus);
+ if (bus->master_or_slave == I2C_MASTER) {
+ if (bus->rd_ind < bus->rd_size)
+ bus->rd_buf[bus->rd_ind++] = data;
+ }
+ }
+}
+
+static inline void npcm_i2c_clear_master_status(struct npcm_i2c *bus)
+{
+ u8 val;
+
+ /* Clear NEGACK, STASTR and BER bits */
+ val = NPCM_I2CST_BER | NPCM_I2CST_NEGACK | NPCM_I2CST_STASTR;
+ iowrite8(val, bus->reg + NPCM_I2CST);
+}
+
+static void npcm_i2c_master_abort(struct npcm_i2c *bus)
+{
+ /* Only current master is allowed to issue a stop condition */
+ if (npcm_i2c_is_master(bus)) {
+ npcm_i2c_eob_int(bus, true);
+ npcm_i2c_master_stop(bus);
+ npcm_i2c_clear_master_status(bus);
+ }
+}
+
+static void npcm_i2c_master_fifo_read(struct npcm_i2c *bus)
+{
+ int rcount;
+ int fifo_bytes;
+ enum i2c_state_ind ind = I2C_MASTER_DONE_IND;
+
+ fifo_bytes = npcm_i2c_fifo_usage(bus);
+
+ rcount = bus->rd_size - bus->rd_ind;
+
+ /*
+ * In order not to change the RX_TRH during transaction (we found that
+ * this might be problematic if it takes too much time to read the FIFO)
+ * we read the data in the following way. If the number of bytes to
+ * read == FIFO Size + C (where C < FIFO Size)then first read C bytes
+ * and in the next int we read rest of the data.
+ */
+ if (rcount < (2 * I2C_HW_FIFO_SIZE) && rcount > I2C_HW_FIFO_SIZE)
+ fifo_bytes = rcount - I2C_HW_FIFO_SIZE;
+
+ if (rcount <= fifo_bytes) {
+ /* last bytes are about to be read - end of tx */
+ bus->state = I2C_STOP_PENDING;
+ bus->stop_ind = ind;
+ npcm_i2c_eob_int(bus, true);
+ /* Stop should be set before reading last byte. */
+ npcm_i2c_master_stop(bus);
+ npcm_i2c_read_from_fifo(bus, fifo_bytes);
+ } else {
+ npcm_i2c_read_from_fifo(bus, fifo_bytes);
+ rcount = bus->rd_size - bus->rd_ind;
+ npcm_i2c_set_fifo(bus, rcount, -1);
+ }
+}
+
+static void npcm_i2c_irq_master_handler_write(struct npcm_i2c *bus)
+{
+ u16 wcount;
+
+ if (bus->fifo_use)
+ npcm_i2c_clear_tx_fifo(bus); /* clear the TX fifo status bit */
+
+ /* Master write operation - last byte handling */
+ if (bus->wr_ind == bus->wr_size) {
+ if (bus->fifo_use && npcm_i2c_fifo_usage(bus) > 0)
+ /*
+ * No more bytes to send (to add to the FIFO),
+ * however the FIFO is not empty yet. It is
+ * still in the middle of tx. Currently there's nothing
+ * to do except for waiting to the end of the tx
+ * We will get an int when the FIFO will get empty.
+ */
+ return;
+
+ if (bus->rd_size == 0) {
+ /* all bytes have been written, in wr only operation */
+ npcm_i2c_eob_int(bus, true);
+ bus->state = I2C_STOP_PENDING;
+ bus->stop_ind = I2C_MASTER_DONE_IND;
+ npcm_i2c_master_stop(bus);
+ /* Clear SDA Status bit (by writing dummy byte) */
+ npcm_i2c_wr_byte(bus, 0xFF);
+
+ } else {
+ /* last write-byte written on previous int - restart */
+ npcm_i2c_set_fifo(bus, bus->rd_size, -1);
+ /* Generate repeated start upon next write to SDA */
+ npcm_i2c_master_start(bus);
+ if (bus->rd_size == 1)
+ /*
+ * Receiving one byte only - stall after
+ * successful completion of send
+ * address byte. If we NACK here,
+ * and slave doesn't ACK the address, we
+ * might unintentionally NACK the next
+ * multi-byte read
+ */
+ npcm_i2c_stall_after_start(bus, true);
+
+ /* Next int will occur on read */
+ bus->operation = I2C_READ_OPER;
+ /* send the slave address in read direction */
+ npcm_i2c_wr_byte(bus, bus->dest_addr | 0x1);
+ }
+ } else {
+ /* write next byte not last byte and not slave address */
+ if (!bus->fifo_use || bus->wr_size == 1) {
+ npcm_i2c_wr_byte(bus, bus->wr_buf[bus->wr_ind++]);
+ } else {
+ wcount = bus->wr_size - bus->wr_ind;
+ npcm_i2c_set_fifo(bus, -1, wcount);
+ if (wcount)
+ npcm_i2c_write_to_fifo_master(bus, wcount);
+ }
+ }
+}
+
+static void npcm_i2c_irq_master_handler_read(struct npcm_i2c *bus)
+{
+ u16 block_extra_bytes_size;
+ u8 data;
+
+ /* added bytes to the packet: */
+ block_extra_bytes_size = bus->read_block_use + bus->PEC_use;
+
+ /*
+ * Perform master read, distinguishing between last byte and the rest of
+ * the bytes. The last byte should be read when the clock is stopped
+ */
+ if (bus->rd_ind == 0) { /* first byte handling: */
+ if (bus->read_block_use) {
+ /* first byte in block protocol is the size: */
+ data = npcm_i2c_rd_byte(bus);
+ data = clamp_val(data, 1, I2C_SMBUS_BLOCK_MAX);
+ bus->rd_size = data + block_extra_bytes_size;
+ bus->rd_buf[bus->rd_ind++] = data;
+
+ /* clear RX FIFO interrupt status: */
+ if (bus->fifo_use) {
+ data = ioread8(bus->reg + NPCM_I2CFIF_CTS);
+ data = data | NPCM_I2CFIF_CTS_RXF_TXE;
+ iowrite8(data, bus->reg + NPCM_I2CFIF_CTS);
+ }
+
+ npcm_i2c_set_fifo(bus, bus->rd_size - 1, -1);
+ npcm_i2c_stall_after_start(bus, false);
+ } else {
+ npcm_i2c_clear_tx_fifo(bus);
+ npcm_i2c_master_fifo_read(bus);
+ }
+ } else {
+ if (bus->rd_size == block_extra_bytes_size &&
+ bus->read_block_use) {
+ bus->state = I2C_STOP_PENDING;
+ bus->stop_ind = I2C_BLOCK_BYTES_ERR_IND;
+ bus->cmd_err = -EIO;
+ npcm_i2c_eob_int(bus, true);
+ npcm_i2c_master_stop(bus);
+ npcm_i2c_read_from_fifo(bus, npcm_i2c_fifo_usage(bus));
+ } else {
+ npcm_i2c_master_fifo_read(bus);
+ }
+ }
+}
+
+static void npcm_i2c_irq_handle_nmatch(struct npcm_i2c *bus)
+{
+ iowrite8(NPCM_I2CST_NMATCH, bus->reg + NPCM_I2CST);
+ npcm_i2c_nack(bus);
+ bus->stop_ind = I2C_BUS_ERR_IND;
+ npcm_i2c_callback(bus, bus->stop_ind, npcm_i2c_get_index(bus));
+}
+
+/* A NACK has occurred */
+static void npcm_i2c_irq_handle_nack(struct npcm_i2c *bus)
+{
+ u8 val;
+#ifdef CONFIG_DEBUG_FS
+ if (bus->nack_count == ULLONG_MAX) {
+ dev_dbg(bus->dev, "%s reaches max, reset to 0", "nack_count");
+ bus->nack_count = 0;
+ }
+ bus->nack_count++;
+#endif
+ if (bus->fifo_use) {
+ /*
+ * if there are still untransmitted bytes in TX FIFO
+ * reduce them from wr_ind
+ */
+ if (bus->operation == I2C_WRITE_OPER)
+ bus->wr_ind -= npcm_i2c_fifo_usage(bus);
+
+ /* clear the FIFO */
+ iowrite8(NPCM_I2CFIF_CTS_CLR_FIFO, bus->reg + NPCM_I2CFIF_CTS);
+ }
+
+ /* In master write operation, got unexpected NACK */
+ bus->stop_ind = I2C_NACK_IND;
+ /* Only current master is allowed to issue Stop Condition */
+ if (npcm_i2c_is_master(bus)) {
+ /* stopping in the middle */
+ npcm_i2c_eob_int(bus, false);
+
+ npcm_i2c_master_stop(bus);
+
+ /*
+ * The bus is released from stall only
+ * after the software clears NEGACK bit.
+ * Then a Stop condition is sent.
+ */
+ npcm_i2c_clear_master_status(bus);
+
+ readx_poll_timeout_atomic(ioread8, bus->reg + NPCM_I2CCST, val,
+ !(val & NPCM_I2CCST_BUSY), 10, 200);
+ }
+ bus->state = I2C_IDLE;
+
+ /*
+ * In Master mode, NACK should be cleared only after STOP.
+ * In such case, the bus is released from stall only after the
+ * software clears NACK bit. Then a Stop condition is sent.
+ */
+ npcm_i2c_callback(bus, bus->stop_ind, bus->wr_ind);
+}
+
+ /* Master mode: a Bus Error has been identified */
+static void npcm_i2c_irq_handle_ber(struct npcm_i2c *bus)
+{
+#ifdef CONFIG_DEBUG_FS
+ if (bus->ber_count == ULLONG_MAX) {
+ dev_dbg(bus->dev, "%s reach max, reset", "ber_count");
+ bus->ber_count = 0;
+ }
+ bus->ber_count++;
+#endif
+ bus->stop_ind = I2C_BUS_ERR_IND;
+ if (npcm_i2c_is_master(bus)) {
+ npcm_i2c_master_abort(bus);
+ } else {
+ npcm_i2c_clear_master_status(bus);
+
+ /* Clear BB (BUS BUSY) bit */
+ iowrite8(NPCM_I2CCST_BB, bus->reg + NPCM_I2CCST);
+
+ bus->cmd_err = -EAGAIN;
+ npcm_i2c_callback(bus, bus->stop_ind, npcm_i2c_get_index(bus));
+ }
+ bus->state = I2C_IDLE;
+}
+
+ /* EOB: a master End Of Busy (meaning STOP completed) */
+static void npcm_i2c_irq_handle_eob(struct npcm_i2c *bus)
+{
+ npcm_i2c_eob_int(bus, false);
+ bus->state = I2C_IDLE;
+ npcm_i2c_callback(bus, bus->stop_ind, bus->rd_ind);
+}
+
+/* Address sent and requested stall occurred (Master mode) */
+static void npcm_i2c_irq_handle_stall_after_start(struct npcm_i2c *bus)
+{
+ if (npcm_i2c_is_quick(bus)) {
+ bus->state = I2C_STOP_PENDING;
+ bus->stop_ind = I2C_MASTER_DONE_IND;
+ npcm_i2c_eob_int(bus, true);
+ npcm_i2c_master_stop(bus);
+ } else if ((bus->rd_size == 1) && !bus->read_block_use) {
+ /*
+ * Receiving one byte only - set NACK after ensuring
+ * slave ACKed the address byte.
+ */
+ npcm_i2c_nack(bus);
+ }
+
+ /* Reset stall-after-address-byte */
+ npcm_i2c_stall_after_start(bus, false);
+
+ /* Clear stall only after setting STOP */
+ iowrite8(NPCM_I2CST_STASTR, bus->reg + NPCM_I2CST);
+}
+
+/* SDA status is set - TX or RX, master */
+static void npcm_i2c_irq_handle_sda(struct npcm_i2c *bus, u8 i2cst)
+{
+ u8 fif_cts;
+
+ if (bus->state == I2C_IDLE) {
+ if (npcm_i2c_is_master(bus)) {
+ bus->stop_ind = I2C_WAKE_UP_IND;
+
+ /* test stall on start */
+ if (npcm_i2c_is_quick(bus) ||
+ bus->read_block_use)
+ /*
+ * Need to stall after successful
+ * completion of sending address byte
+ */
+ npcm_i2c_stall_after_start(bus, true);
+ else
+ npcm_i2c_stall_after_start(bus, false);
+
+ /*
+ * Receiving one byte only - stall after
+ * successful completion of sending address byte
+ * If we NACK here, and slave doesn't ACK the
+ * address, we might unintentionally NACK
+ * the next multi-byte read
+ */
+ if (bus->wr_size == 0 && bus->rd_size == 1)
+ npcm_i2c_stall_after_start(bus, true);
+
+ /* Initiate I2C master tx */
+
+ /* select bank 1 for FIFO regs */
+ npcm_i2c_select_bank(bus, I2C_BANK_1);
+
+ fif_cts = ioread8(bus->reg + NPCM_I2CFIF_CTS);
+ fif_cts = fif_cts & ~NPCM_I2CFIF_CTS_SLVRSTR;
+
+ /* clear FIFO and relevant status bits. */
+ fif_cts = fif_cts | NPCM_I2CFIF_CTS_CLR_FIFO;
+ iowrite8(fif_cts, bus->reg + NPCM_I2CFIF_CTS);
+
+ /* re-enable */
+ fif_cts = fif_cts | NPCM_I2CFIF_CTS_RXF_TXE;
+ iowrite8(fif_cts, bus->reg + NPCM_I2CFIF_CTS);
+
+ /*
+ * Configure the FIFO threshold:
+ * according to the needed # of bytes to read.
+ * Note: due to HW limitation can't config the
+ * rx fifo before it got and ACK on the restart.
+ * LAST bit will not be reset unless RX completed.
+ * It will stay set on the next tx.
+ */
+ if (bus->wr_size)
+ npcm_i2c_set_fifo(bus, -1, bus->wr_size);
+ else
+ npcm_i2c_set_fifo(bus, bus->rd_size, -1);
+
+ bus->state = I2C_OPER_STARTED;
+
+ if (npcm_i2c_is_quick(bus) || bus->wr_size)
+ npcm_i2c_wr_byte(bus, bus->dest_addr);
+ else
+ npcm_i2c_wr_byte(bus, bus->dest_addr | BIT(0));
+ }
+
+ /* SDA interrupt, after start\restart */
+ } else {
+ if (NPCM_I2CST_XMIT & i2cst) {
+ bus->operation = I2C_WRITE_OPER;
+ npcm_i2c_irq_master_handler_write(bus);
+ } else {
+ bus->operation = I2C_READ_OPER;
+ npcm_i2c_irq_master_handler_read(bus);
+ }
+ }
+}
+
+static int npcm_i2c_int_master_handler(struct npcm_i2c *bus)
+{
+ u8 i2cst;
+ int ret = -EIO;
+
+ i2cst = ioread8(bus->reg + NPCM_I2CST);
+
+ if (FIELD_GET(NPCM_I2CST_NMATCH, i2cst)) {
+ npcm_i2c_irq_handle_nmatch(bus);
+ return 0;
+ }
+ /* A NACK has occurred */
+ if (FIELD_GET(NPCM_I2CST_NEGACK, i2cst)) {
+ npcm_i2c_irq_handle_nack(bus);
+ return 0;
+ }
+
+ /* Master mode: a Bus Error has been identified */
+ if (FIELD_GET(NPCM_I2CST_BER, i2cst)) {
+ npcm_i2c_irq_handle_ber(bus);
+ return 0;
+ }
+
+ /* EOB: a master End Of Busy (meaning STOP completed) */
+ if ((FIELD_GET(NPCM_I2CCTL1_EOBINTE,
+ ioread8(bus->reg + NPCM_I2CCTL1)) == 1) &&
+ (FIELD_GET(NPCM_I2CCST3_EO_BUSY,
+ ioread8(bus->reg + NPCM_I2CCST3)))) {
+ npcm_i2c_irq_handle_eob(bus);
+ return 0;
+ }
+
+ /* Address sent and requested stall occurred (Master mode) */
+ if (FIELD_GET(NPCM_I2CST_STASTR, i2cst)) {
+ npcm_i2c_irq_handle_stall_after_start(bus);
+ ret = 0;
+ }
+
+ /* SDA status is set - TX or RX, master */
+ if (FIELD_GET(NPCM_I2CST_SDAST, i2cst) ||
+ (bus->fifo_use &&
+ (npcm_i2c_tx_fifo_empty(bus) || npcm_i2c_rx_fifo_full(bus)))) {
+ npcm_i2c_irq_handle_sda(bus, i2cst);
+ ret = 0;
+ }
+
+ return ret;
+}
+
+/* recovery using TGCLK functionality of the module */
+static int npcm_i2c_recovery_tgclk(struct i2c_adapter *_adap)
+{
+ u8 val;
+ u8 fif_cts;
+ bool done = false;
+ int status = -ENOTRECOVERABLE;
+ struct npcm_i2c *bus = container_of(_adap, struct npcm_i2c, adap);
+ /* Allow 3 bytes (27 toggles) to be read from the slave: */
+ int iter = 27;
+
+ if ((npcm_i2c_get_SDA(_adap) == 1) && (npcm_i2c_get_SCL(_adap) == 1)) {
+ dev_dbg(bus->dev, "bus%d recovery skipped, bus not stuck",
+ bus->num);
+ npcm_i2c_reset(bus);
+ return status;
+ }
+
+ npcm_i2c_int_enable(bus, false);
+ npcm_i2c_disable(bus);
+ npcm_i2c_enable(bus);
+ iowrite8(NPCM_I2CCST_BB, bus->reg + NPCM_I2CCST);
+ npcm_i2c_clear_tx_fifo(bus);
+ npcm_i2c_clear_rx_fifo(bus);
+ iowrite8(0, bus->reg + NPCM_I2CRXF_CTL);
+ iowrite8(0, bus->reg + NPCM_I2CTXF_CTL);
+ npcm_i2c_stall_after_start(bus, false);
+
+ /* select bank 1 for FIFO regs */
+ npcm_i2c_select_bank(bus, I2C_BANK_1);
+
+ /* clear FIFO and relevant status bits. */
+ fif_cts = ioread8(bus->reg + NPCM_I2CFIF_CTS);
+ fif_cts &= ~NPCM_I2CFIF_CTS_SLVRSTR;
+ fif_cts |= NPCM_I2CFIF_CTS_CLR_FIFO;
+ iowrite8(fif_cts, bus->reg + NPCM_I2CFIF_CTS);
+ npcm_i2c_set_fifo(bus, -1, 0);
+
+ /* Repeat the following sequence until SDA is released */
+ do {
+ /* Issue a single SCL toggle */
+ iowrite8(NPCM_I2CCST_TGSCL, bus->reg + NPCM_I2CCST);
+ udelay(20);
+ /* If SDA line is inactive (high), stop */
+ if (npcm_i2c_get_SDA(_adap)) {
+ done = true;
+ status = 0;
+ }
+ } while (!done && iter--);
+
+ /* If SDA line is released: send start-addr-stop, to re-sync. */
+ if (npcm_i2c_get_SDA(_adap)) {
+ /* Send an address byte in write direction: */
+ npcm_i2c_wr_byte(bus, bus->dest_addr);
+ npcm_i2c_master_start(bus);
+ /* Wait until START condition is sent */
+ status = readx_poll_timeout(npcm_i2c_get_SCL, _adap, val, !val,
+ 20, 200);
+ /* If START condition was sent */
+ if (npcm_i2c_is_master(bus) > 0) {
+ udelay(20);
+ npcm_i2c_master_stop(bus);
+ udelay(200);
+ }
+ }
+ npcm_i2c_reset(bus);
+ npcm_i2c_int_enable(bus, true);
+
+ if ((npcm_i2c_get_SDA(_adap) == 1) && (npcm_i2c_get_SCL(_adap) == 1))
+ status = 0;
+ else
+ status = -ENOTRECOVERABLE;
+#ifdef CONFIG_DEBUG_FS
+ if (status) {
+ if (bus->rec_fail_count == ULLONG_MAX) {
+ dev_dbg(bus->dev,
+ "rec_fail_count reaches max, reset to 0");
+ bus->rec_fail_count = 0;
+ }
+ bus->rec_fail_count++;
+ } else {
+ if (bus->rec_succ_count == ULLONG_MAX) {
+ dev_dbg(bus->dev,
+ "rec_succ_count reaches max, reset to 0");
+ bus->rec_succ_count = 0;
+ }
+ bus->rec_succ_count++;
+ }
+#endif
+ return status;
+}
+
+/* recovery using bit banging functionality of the module */
+static int npcm_i2c_recovery_init(struct i2c_adapter *_adap)
+{
+ struct npcm_i2c *bus = container_of(_adap, struct npcm_i2c, adap);
+ struct i2c_bus_recovery_info *rinfo = &bus->rinfo;
+
+ rinfo->recover_bus = npcm_i2c_recovery_tgclk;
+ rinfo->prepare_recovery = NULL;
+ rinfo->unprepare_recovery = NULL;
+ rinfo->set_scl = NULL;
+ rinfo->set_sda = NULL;
+
+ dev_dbg(bus->dev, "init i2c recovery using TGCLK\n");
+
+ rinfo->get_scl = npcm_i2c_get_SCL;
+ rinfo->get_sda = npcm_i2c_get_SDA;
+
+ _adap->bus_recovery_info = rinfo;
+
+ return 0;
+}
+
+/* SCLFRQ min/max field values */
+#define SCLFRQ_MIN 10
+#define SCLFRQ_MAX 511
+#define clk_coef(freq, mul) DIV_ROUND_UP((freq) * (mul), 1000000)
+
+/*
+ * npcm_i2c_init_clk: init HW timing parameters.
+ * NPCM7XX i2c module timing parameters are depenent on module core clk (APB)
+ * and bus frequency.
+ * 100kHz bus requires tSCL = 4 * SCLFRQ * tCLK. LT and HT are simetric.
+ * 400kHz bus requires assymetric HT and LT. A different equation is recomended
+ * by the HW designer, given core clock range (equations in comments below).
+ *
+ */
+static int npcm_i2c_init_clk(struct npcm_i2c *bus, u32 bus_freq_hz)
+{
+ u32 k1 = 0;
+ u32 k2 = 0;
+ u8 dbnct = 0;
+ u32 sclfrq = 0;
+ u8 hldt = 7;
+ u8 fast_mode = 0;
+ u32 src_clk_khz;
+ u32 bus_freq_khz;
+
+ src_clk_khz = bus->apb_clk / 1000;
+ bus_freq_khz = bus_freq_hz / 1000;
+ bus->bus_freq = bus_freq_hz;
+
+ /* 100KHz and below: */
+ if (bus_freq_hz <= I2C_MAX_STANDARD_MODE_FREQ) {
+ sclfrq = src_clk_khz / (bus_freq_khz * 4);
+
+ if (sclfrq < SCLFRQ_MIN || sclfrq > SCLFRQ_MAX)
+ return -EDOM;
+
+ if (src_clk_khz >= 40000)
+ hldt = 17;
+ else if (src_clk_khz >= 12500)
+ hldt = 15;
+ else
+ hldt = 7;
+ }
+
+ /* 400KHz: */
+ else if (bus_freq_hz <= I2C_MAX_FAST_MODE_FREQ) {
+ sclfrq = 0;
+ fast_mode = I2CCTL3_400K_MODE;
+
+ if (src_clk_khz < 7500)
+ /* 400KHZ cannot be supported for core clock < 7.5MHz */
+ return -EDOM;
+
+ else if (src_clk_khz >= 50000) {
+ k1 = 80;
+ k2 = 48;
+ hldt = 12;
+ dbnct = 7;
+ }
+
+ /* Master or Slave with frequency > 25MHz */
+ else if (src_clk_khz > 25000) {
+ hldt = clk_coef(src_clk_khz, 300) + 7;
+ k1 = clk_coef(src_clk_khz, 1600);
+ k2 = clk_coef(src_clk_khz, 900);
+ }
+ }
+
+ else if (bus_freq_hz <= I2C_MAX_FAST_MODE_PLUS_FREQ) {
+ sclfrq = 0;
+ fast_mode = I2CCTL3_400K_MODE;
+
+ /* 1MHZ cannot be supported for core clock < 24 MHz */
+ if (src_clk_khz < 24000)
+ return -EDOM;
+
+ k1 = clk_coef(src_clk_khz, 620);
+ k2 = clk_coef(src_clk_khz, 380);
+
+ /* Core clk > 40 MHz */
+ if (src_clk_khz > 40000) {
+ /*
+ * Set HLDT:
+ * SDA hold time: (HLDT-7) * T(CLK) >= 120
+ * HLDT = 120/T(CLK) + 7 = 120 * FREQ(CLK) + 7
+ */
+ hldt = clk_coef(src_clk_khz, 120) + 7;
+ } else {
+ hldt = 7;
+ dbnct = 2;
+ }
+ }
+
+ /* Frequency larger than 1 MHZ is not supported */
+ else
+ return -EINVAL;
+
+ if (bus_freq_hz >= I2C_MAX_FAST_MODE_FREQ) {
+ k1 = round_up(k1, 2);
+ k2 = round_up(k2 + 1, 2);
+ if (k1 < SCLFRQ_MIN || k1 > SCLFRQ_MAX ||
+ k2 < SCLFRQ_MIN || k2 > SCLFRQ_MAX)
+ return -EDOM;
+ }
+
+ /* write sclfrq value. bits [6:0] are in I2CCTL2 reg */
+ iowrite8(FIELD_PREP(I2CCTL2_SCLFRQ6_0, sclfrq & 0x7F),
+ bus->reg + NPCM_I2CCTL2);
+
+ /* bits [8:7] are in I2CCTL3 reg */
+ iowrite8(fast_mode | FIELD_PREP(I2CCTL3_SCLFRQ8_7, (sclfrq >> 7) & 0x3),
+ bus->reg + NPCM_I2CCTL3);
+
+ /* Select Bank 0 to access NPCM_I2CCTL4/NPCM_I2CCTL5 */
+ npcm_i2c_select_bank(bus, I2C_BANK_0);
+
+ if (bus_freq_hz >= I2C_MAX_FAST_MODE_FREQ) {
+ /*
+ * Set SCL Low/High Time:
+ * k1 = 2 * SCLLT7-0 -> Low Time = k1 / 2
+ * k2 = 2 * SCLLT7-0 -> High Time = k2 / 2
+ */
+ iowrite8(k1 / 2, bus->reg + NPCM_I2CSCLLT);
+ iowrite8(k2 / 2, bus->reg + NPCM_I2CSCLHT);
+
+ iowrite8(dbnct, bus->reg + NPCM_I2CCTL5);
+ }
+
+ iowrite8(hldt, bus->reg + NPCM_I2CCTL4);
+
+ /* Return to Bank 1, and stay there by default: */
+ npcm_i2c_select_bank(bus, I2C_BANK_1);
+
+ return 0;
+}
+
+static int npcm_i2c_init_module(struct npcm_i2c *bus, enum i2c_mode mode,
+ u32 bus_freq_hz)
+{
+ u8 val;
+ int ret;
+
+ /* Check whether module already enabled or frequency is out of bounds */
+ if ((bus->state != I2C_DISABLE && bus->state != I2C_IDLE) ||
+ bus_freq_hz < I2C_FREQ_MIN_HZ || bus_freq_hz > I2C_FREQ_MAX_HZ)
+ return -EINVAL;
+
+ npcm_i2c_disable(bus);
+
+ /* Configure FIFO mode : */
+ if (FIELD_GET(I2C_VER_FIFO_EN, ioread8(bus->reg + I2C_VER))) {
+ bus->fifo_use = true;
+ npcm_i2c_select_bank(bus, I2C_BANK_0);
+ val = ioread8(bus->reg + NPCM_I2CFIF_CTL);
+ val |= NPCM_I2CFIF_CTL_FIFO_EN;
+ iowrite8(val, bus->reg + NPCM_I2CFIF_CTL);
+ npcm_i2c_select_bank(bus, I2C_BANK_1);
+ } else {
+ bus->fifo_use = false;
+ }
+
+ /* Configure I2C module clock frequency */
+ ret = npcm_i2c_init_clk(bus, bus_freq_hz);
+ if (ret) {
+ dev_err(bus->dev, "npcm_i2c_init_clk failed\n");
+ return ret;
+ }
+
+ /* Enable module (before configuring CTL1) */
+ npcm_i2c_enable(bus);
+ bus->state = I2C_IDLE;
+ val = ioread8(bus->reg + NPCM_I2CCTL1);
+ val = (val | NPCM_I2CCTL1_NMINTE) & ~NPCM_I2CCTL1_RWS;
+ iowrite8(val, bus->reg + NPCM_I2CCTL1);
+
+ npcm_i2c_int_enable(bus, true);
+
+ npcm_i2c_reset(bus);
+
+ return 0;
+}
+
+static int __npcm_i2c_init(struct npcm_i2c *bus, struct platform_device *pdev)
+{
+ u32 clk_freq_hz;
+ int ret;
+
+ /* Initialize the internal data structures */
+ bus->state = I2C_DISABLE;
+ bus->master_or_slave = I2C_SLAVE;
+ bus->int_time_stamp = 0;
+ bus->xmits = 0;
+
+ ret = device_property_read_u32(&pdev->dev, "bus-frequency",
+ &clk_freq_hz);
+ if (ret) {
+ dev_info(&pdev->dev, "Could not read bus-frequency property\n");
+ clk_freq_hz = I2C_MAX_STANDARD_MODE_FREQ;
+ }
+
+ ret = npcm_i2c_init_module(bus, I2C_MASTER, clk_freq_hz);
+ if (ret) {
+ dev_err(&pdev->dev, "npcm_i2c_init_module failed\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static irqreturn_t npcm_i2c_bus_irq(int irq, void *dev_id)
+{
+ struct npcm_i2c *bus = dev_id;
+
+ bus->int_cnt++;
+ if (npcm_i2c_is_master(bus))
+ bus->master_or_slave = I2C_MASTER;
+
+ // master and slave modes share a single irq.
+ if (bus->master_or_slave == I2C_MASTER) {
+ bus->int_time_stamp = jiffies;
+ if (!npcm_i2c_int_master_handler(bus))
+ return IRQ_HANDLED;
+ }
+ return IRQ_NONE;
+}
+
+static bool npcm_i2c_master_start_xmit(struct npcm_i2c *bus,
+ u8 slave_addr, u16 nwrite, u16 nread,
+ u8 *write_data, u8 *read_data,
+ bool use_PEC, bool use_read_block)
+{
+ if (bus->state != I2C_IDLE) {
+ bus->cmd_err = -EBUSY;
+ return false;
+ }
+ bus->xmits++;
+ bus->dest_addr = slave_addr << 1;
+ bus->wr_buf = write_data;
+ bus->wr_size = nwrite;
+ bus->wr_ind = 0;
+ bus->rd_buf = read_data;
+ bus->rd_size = nread;
+ bus->rd_ind = 0;
+ bus->PEC_use = 0;
+
+ /* for tx PEC is appended to buffer from i2c IF. PEC flag is ignored */
+ if (nread)
+ bus->PEC_use = use_PEC;
+
+ bus->read_block_use = use_read_block;
+ if (nread && !nwrite)
+ bus->operation = I2C_READ_OPER;
+ else
+ bus->operation = I2C_WRITE_OPER;
+ bus->int_cnt = 0;
+ if (bus->fifo_use) {
+ u8 i2cfif_cts;
+
+ npcm_i2c_select_bank(bus, I2C_BANK_1);
+ /* clear FIFO and relevant status bits. */
+ i2cfif_cts = ioread8(bus->reg + NPCM_I2CFIF_CTS);
+ i2cfif_cts &= ~NPCM_I2CFIF_CTS_SLVRSTR;
+ i2cfif_cts |= NPCM_I2CFIF_CTS_CLR_FIFO;
+ iowrite8(i2cfif_cts, bus->reg + NPCM_I2CFIF_CTS);
+ }
+
+ bus->state = I2C_IDLE;
+ npcm_i2c_stall_after_start(bus, true);
+ npcm_i2c_master_start(bus);
+ return true;
+}
+
+static int npcm_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
+ int num)
+{
+ struct npcm_i2c *bus = container_of(adap, struct npcm_i2c, adap);
+ struct i2c_msg *msg0, *msg1;
+ unsigned long time_left, flags;
+ u16 nwrite, nread;
+ u8 *write_data, *read_data;
+ u8 slave_addr;
+ int timeout;
+ int ret = 0;
+ bool read_block = false;
+ bool read_PEC = false;
+ u8 bus_busy;
+ unsigned long timeout_usec;
+
+ if (bus->state == I2C_DISABLE) {
+ dev_err(bus->dev, "I2C%d module is disabled", bus->num);
+ return -EINVAL;
+ }
+
+ if (num > 2 || num < 1) {
+ dev_err(bus->dev, "I2C cmd not supported num of msgs=%d", num);
+ return -EINVAL;
+ }
+
+ msg0 = &msgs[0];
+ slave_addr = msg0->addr;
+ if (msg0->flags & I2C_M_RD) { /* read */
+ if (num == 2) {
+ dev_err(bus->dev, "num=2 but 1st msg rd instead of wr");
+ return -EINVAL;
+ }
+ nwrite = 0;
+ write_data = NULL;
+ read_data = msg0->buf;
+ if (msg0->flags & I2C_M_RECV_LEN) {
+ nread = 1;
+ read_block = true;
+ if (msg0->flags & I2C_CLIENT_PEC)
+ read_PEC = true;
+ } else {
+ nread = msg0->len;
+ }
+ } else { /* write */
+ nwrite = msg0->len;
+ write_data = msg0->buf;
+ nread = 0;
+ read_data = NULL;
+ if (num == 2) {
+ msg1 = &msgs[1];
+ read_data = msg1->buf;
+ if (slave_addr != msg1->addr) {
+ dev_err(bus->dev,
+ "SA==%02x but msg1->addr==%02x\n",
+ slave_addr, msg1->addr);
+ return -EINVAL;
+ }
+ if ((msg1->flags & I2C_M_RD) == 0) {
+ dev_err(bus->dev,
+ "num = 2 but both msg are write.\n");
+ return -EINVAL;
+ }
+ if (msg1->flags & I2C_M_RECV_LEN) {
+ nread = 1;
+ read_block = true;
+ if (msg1->flags & I2C_CLIENT_PEC)
+ read_PEC = true;
+ } else {
+ nread = msg1->len;
+ read_block = false;
+ }
+ }
+ }
+
+ /* Adaptive TimeOut: astimated time in usec + 100% margin */
+ timeout_usec = (2 * 10000 / bus->bus_freq) * (2 + nread + nwrite);
+ timeout = max(msecs_to_jiffies(35), usecs_to_jiffies(timeout_usec));
+ if (nwrite >= 32 * 1024 || nread >= 32 * 1024) {
+ dev_err(bus->dev, "i2c%d buffer too big\n", bus->num);
+ return -EINVAL;
+ }
+
+ time_left = jiffies + msecs_to_jiffies(DEFAULT_STALL_COUNT) + 1;
+ do {
+ /*
+ * we must clear slave address immediately when the bus is not
+ * busy, so we spinlock it, but we don't keep the lock for the
+ * entire while since it is too long.
+ */
+ spin_lock_irqsave(&bus->lock, flags);
+ bus_busy = ioread8(bus->reg + NPCM_I2CCST) & NPCM_I2CCST_BB;
+ spin_unlock_irqrestore(&bus->lock, flags);
+
+ } while (time_is_after_jiffies(time_left) && bus_busy);
+
+ if (bus_busy) {
+ iowrite8(NPCM_I2CCST_BB, bus->reg + NPCM_I2CCST);
+ npcm_i2c_reset(bus);
+ i2c_recover_bus(adap);
+ return -EAGAIN;
+ }
+
+ npcm_i2c_init_params(bus);
+ bus->dest_addr = slave_addr;
+ bus->msgs = msgs;
+ bus->msgs_num = num;
+ bus->cmd_err = 0;
+ bus->read_block_use = read_block;
+
+ reinit_completion(&bus->cmd_complete);
+ if (!npcm_i2c_master_start_xmit(bus, slave_addr, nwrite, nread,
+ write_data, read_data, read_PEC,
+ read_block))
+ ret = -EBUSY;
+
+ if (ret != -EBUSY) {
+ time_left = wait_for_completion_timeout(&bus->cmd_complete,
+ timeout);
+
+ if (time_left == 0) {
+#ifdef CONFIG_DEBUG_FS
+ if (bus->timeout_count == ULLONG_MAX) {
+ dev_dbg(bus->dev,
+ "%s reaches to max, reset to 0",
+ "timeout_count");
+ bus->timeout_count = 0;
+ }
+ bus->timeout_count++;
+#endif
+ if (bus->master_or_slave == I2C_MASTER) {
+ i2c_recover_bus(adap);
+ bus->cmd_err = -EIO;
+ bus->state = I2C_IDLE;
+ }
+ }
+ }
+ ret = bus->cmd_err;
+
+ /* if there was BER, check if need to recover the bus: */
+ if (bus->cmd_err == -EAGAIN)
+ ret = i2c_recover_bus(adap);
+
+ return bus->cmd_err;
+}
+
+static u32 npcm_i2c_functionality(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_I2C |
+ I2C_FUNC_SMBUS_EMUL |
+ I2C_FUNC_SMBUS_BLOCK_DATA |
+ I2C_FUNC_SMBUS_PEC |
+ I2C_FUNC_SLAVE;
+}
+
+static const struct i2c_adapter_quirks npcm_i2c_quirks = {
+ .max_read_len = 32768,
+ .max_write_len = 32768,
+ .max_num_msgs = 2,
+ .flags = I2C_AQ_COMB_WRITE_THEN_READ,
+};
+
+static const struct i2c_algorithm npcm_i2c_algo = {
+ .master_xfer = npcm_i2c_master_xfer,
+ .functionality = npcm_i2c_functionality,
+};
+
+#ifdef CONFIG_DEBUG_FS
+/* i2c debugfs directory: used to keep health monitor of i2c devices */
+static struct dentry *npcm_i2c_debugfs_dir;
+
+static int i2c_debugfs_get(void *data, u64 *val)
+{
+ *val = *(u64 *)(data);
+ return 0;
+}
+DEFINE_DEBUGFS_ATTRIBUTE(i2c_debugfs_ops, i2c_debugfs_get, NULL, "0x%02llx\n");
+
+static void i2c_init_debugfs(struct platform_device *pdev, struct npcm_i2c *bus)
+{
+ if (!npcm_i2c_debugfs_dir)
+ return;
+
+ if (!pdev || !bus)
+ return;
+
+ bus->debugfs = debugfs_create_dir(dev_name(&pdev->dev),
+ npcm_i2c_debugfs_dir);
+ if (IS_ERR_OR_NULL(bus->debugfs)) {
+ bus->debugfs = NULL;
+ return;
+ }
+
+ debugfs_create_file("ber_count", 0444, bus->debugfs,
+ &bus->ber_count,
+ &i2c_debugfs_ops);
+
+ debugfs_create_file("rec_succ_count", 0444, bus->debugfs,
+ &bus->rec_succ_count,
+ &i2c_debugfs_ops);
+
+ debugfs_create_file("rec_fail_count", 0444, bus->debugfs,
+ &bus->rec_fail_count,
+ &i2c_debugfs_ops);
+
+ debugfs_create_file("nack_count", 0444, bus->debugfs,
+ &bus->nack_count,
+ &i2c_debugfs_ops);
+
+ debugfs_create_file("timeout_count", 0444, bus->debugfs,
+ &bus->timeout_count,
+ &i2c_debugfs_ops);
+}
+#endif
+
+static int npcm_i2c_probe_bus(struct platform_device *pdev)
+{
+ struct npcm_i2c *bus;
+ struct i2c_adapter *adap;
+ struct clk *i2c_clk;
+ static struct regmap *gcr_regmap;
+ static struct regmap *clk_regmap;
+ int ret;
+ int num;
+
+ bus = devm_kzalloc(&pdev->dev, sizeof(*bus), GFP_KERNEL);
+ if (!bus)
+ return -ENOMEM;
+
+ bus->dev = &pdev->dev;
+
+ num = of_alias_get_id(pdev->dev.of_node, "i2c");
+ bus->num = num;
+ /* core clk must be acquired to calculate module timing settings */
+ i2c_clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(i2c_clk))
+ return PTR_ERR(i2c_clk);
+ bus->apb_clk = clk_get_rate(i2c_clk);
+
+ gcr_regmap = syscon_regmap_lookup_by_compatible("nuvoton,npcm750-gcr");
+ if (IS_ERR(gcr_regmap))
+ return IS_ERR(gcr_regmap);
+ regmap_write(gcr_regmap, NPCM_I2CSEGCTL, NPCM_I2CSEGCTL_INIT_VAL);
+
+ clk_regmap = syscon_regmap_lookup_by_compatible("nuvoton,npcm750-clk");
+ if (IS_ERR(clk_regmap))
+ return IS_ERR(clk_regmap);
+
+ bus->reg = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(bus->reg))
+ return PTR_ERR((bus)->reg);
+
+ spin_lock_init(&bus->lock);
+ init_completion(&bus->cmd_complete);
+
+ adap = &bus->adap;
+ adap->owner = THIS_MODULE;
+ adap->class = I2C_CLASS_HWMON | I2C_CLASS_SPD | I2C_CLIENT_SLAVE;
+ adap->retries = 3;
+ adap->timeout = HZ;
+ adap->algo = &npcm_i2c_algo;
+ adap->quirks = &npcm_i2c_quirks;
+ adap->algo_data = bus;
+ adap->dev.parent = &pdev->dev;
+ adap->dev.of_node = pdev->dev.of_node;
+ adap->nr = pdev->id;
+
+ bus->irq = platform_get_irq(pdev, 0);
+ if (bus->irq < 0)
+ return bus->irq;
+
+ ret = devm_request_irq(bus->dev, bus->irq, npcm_i2c_bus_irq, 0,
+ dev_name(bus->dev), bus);
+ if (ret)
+ return ret;
+
+ ret = __npcm_i2c_init(bus, pdev);
+ if (ret)
+ return ret;
+
+ ret = npcm_i2c_recovery_init(adap);
+ if (ret)
+ return ret;
+
+ i2c_set_adapdata(adap, bus);
+
+ snprintf(bus->adap.name, sizeof(bus->adap.name), "Nuvoton i2c");
+ ret = i2c_add_numbered_adapter(&bus->adap);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to add numbered adapter %d\n", ret);
+ return ret;
+ }
+ platform_set_drvdata(pdev, bus);
+
+#ifdef CONFIG_DEBUG_FS
+ i2c_init_debugfs(pdev, bus);
+#endif
+ return 0;
+}
+
+static int npcm_i2c_remove_bus(struct platform_device *pdev)
+{
+ unsigned long lock_flags;
+ struct npcm_i2c *bus = platform_get_drvdata(pdev);
+
+ spin_lock_irqsave(&bus->lock, lock_flags);
+ npcm_i2c_disable(bus);
+ spin_unlock_irqrestore(&bus->lock, lock_flags);
+ i2c_del_adapter(&bus->adap);
+
+#ifdef CONFIG_DEBUG_FS
+ debugfs_remove_recursive(bus->debugfs);
+#endif
+
+ return 0;
+}
+
+static const struct of_device_id npcm_i2c_bus_of_table[] = {
+ { .compatible = "nuvoton,npcm750-i2c", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, npcm_i2c_bus_of_table);
+
+static struct platform_driver npcm_i2c_bus_driver = {
+ .probe = npcm_i2c_probe_bus,
+ .remove = npcm_i2c_remove_bus,
+ .driver = {
+ .name = "nuvoton-i2c",
+ .of_match_table = npcm_i2c_bus_of_table,
+ }
+};
+module_platform_driver(npcm_i2c_bus_driver);
+
+#ifdef CONFIG_DEBUG_FS
+static int __init npcm_i2c_init(void)
+{
+ npcm_i2c_debugfs_dir = debugfs_create_dir("i2c", NULL);
+ if (IS_ERR_OR_NULL(npcm_i2c_debugfs_dir)) {
+ pr_warn("i2c init of debugfs failed\n");
+ npcm_i2c_debugfs_dir = NULL;
+ }
+
+ return 0;
+}
+
+static void __exit npcm_i2c_exit(void)
+{
+ debugfs_remove_recursive(npcm_i2c_debugfs_dir);
+}
+
+module_init(npcm_i2c_init);
+module_exit(npcm_i2c_exit);
+#endif
+
+MODULE_AUTHOR("Avi Fishman <avi.fishman@gmail.com>");
+MODULE_AUTHOR("Tali Perry <tali.perry@nuvoton.com>");
+MODULE_AUTHOR("Tyrone Ting <kfting@nuvoton.com>");
+MODULE_DESCRIPTION("Nuvoton I2C Bus Driver");
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION("0.1.3");
+
--
2.22.0
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* Re: [PATCH] arm64: Fix PTRACE_SYSEMU semantics
From: Sudeep Holla @ 2020-05-20 10:03 UTC (permalink / raw)
To: Catalin Marinas
Cc: will.deacon, linux-kernel, oleg, Keno Fischer, Sudeep Holla,
Will Deacon, linux-arm-kernel
In-Reply-To: <20200519120725.GA20313@gaia>
Hi Catalin,
On Tue, May 19, 2020 at 01:07:27PM +0100, Catalin Marinas wrote:
> On Mon, May 18, 2020 at 12:41:20PM +0100, Will Deacon wrote:
> > On Fri, May 15, 2020 at 06:22:53PM -0400, Keno Fischer wrote:
> > > Quoth the man page:
> > > ```
> > > If the tracee was restarted by PTRACE_SYSCALL or PTRACE_SYSEMU, the
> > > tracee enters syscall-enter-stop just prior to entering any system
> > > call (which will not be executed if the restart was using
> > > PTRACE_SYSEMU, regardless of any change made to registers at this
> > > point or how the tracee is restarted after this stop).
> > > ```
> > >
> > > The parenthetical comment is currently true on x86 and powerpc,
> > > but not currently true on arm64. arm64 re-checks the _TIF_SYSCALL_EMU
> > > flag after the syscall entry ptrace stop. However, at this point,
> > > it reflects which method was used to re-start the syscall
> > > at the entry stop, rather than the method that was used to reach it.
> > > Fix that by recording the original flag before performing the ptrace
> > > stop, bringing the behavior in line with documentation and x86/powerpc.
> > >
> > > Signed-off-by: Keno Fischer <keno@juliacomputing.com>
> > > ---
> > > arch/arm64/kernel/ptrace.c | 8 +++++---
> > > 1 file changed, 5 insertions(+), 3 deletions(-)
> > >
> > > diff --git a/arch/arm64/kernel/ptrace.c b/arch/arm64/kernel/ptrace.c
> > > index b3d3005d9515..b67b4d14aa17 100644
> > > --- a/arch/arm64/kernel/ptrace.c
> > > +++ b/arch/arm64/kernel/ptrace.c
> > > @@ -1829,10 +1829,12 @@ static void tracehook_report_syscall(struct pt_regs *regs,
> > >
> > > int syscall_trace_enter(struct pt_regs *regs)
> > > {
> > > - if (test_thread_flag(TIF_SYSCALL_TRACE) ||
> > > - test_thread_flag(TIF_SYSCALL_EMU)) {
> > > + u32 flags = READ_ONCE(current_thread_info()->flags) &
> > > + (_TIF_SYSCALL_EMU | _TIF_SYSCALL_TRACE);
> > > +
> > > + if (flags) {
> >
> > nit: but I'd rather the '&' operation was in the conditional so that the
> > 'flags' variable holds all of the flags.
> >
> > With that:
> >
> > Acked-by: Will Deacon <will@kernel.org>
> >
> > Also needs:
> >
> > Cc: <stable@vger.kernel.org>
> > Fixes: f086f67485c5 ("arm64: ptrace: add support for syscall emulation")
> >
> > Catalin -- can you pick this up for 5.7 please, with my 'nit' addressed?
>
> I'll queue it with the above addressed. I think flags also needs to be
> unsigned long rather than u32.
>
> However, before sending the pull request, I'd like Sudeep to confirm
> that it doesn't break his original use-case for this feature.
>
I just tested it with my simple programs I had before. I have also asked
teams working on gvisor to test. They have tested it and see no
regression. I will ask them to reply here.
Tested-by: Sudeep Holla <sudeep.holla@arm.com>
--
Regards,
Sudeep
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH] arm64: Fix PTRACE_SYSEMU semantics
From: Catalin Marinas @ 2020-05-20 10:05 UTC (permalink / raw)
To: Sudeep Holla
Cc: will.deacon, linux-kernel, oleg, Keno Fischer, Will Deacon,
linux-arm-kernel
In-Reply-To: <20200520100330.GA25430@bogus>
On Wed, May 20, 2020 at 11:03:30AM +0100, Sudeep Holla wrote:
> On Tue, May 19, 2020 at 01:07:27PM +0100, Catalin Marinas wrote:
> > On Mon, May 18, 2020 at 12:41:20PM +0100, Will Deacon wrote:
> > > On Fri, May 15, 2020 at 06:22:53PM -0400, Keno Fischer wrote:
> > > > Quoth the man page:
> > > > ```
> > > > If the tracee was restarted by PTRACE_SYSCALL or PTRACE_SYSEMU, the
> > > > tracee enters syscall-enter-stop just prior to entering any system
> > > > call (which will not be executed if the restart was using
> > > > PTRACE_SYSEMU, regardless of any change made to registers at this
> > > > point or how the tracee is restarted after this stop).
> > > > ```
> > > >
> > > > The parenthetical comment is currently true on x86 and powerpc,
> > > > but not currently true on arm64. arm64 re-checks the _TIF_SYSCALL_EMU
> > > > flag after the syscall entry ptrace stop. However, at this point,
> > > > it reflects which method was used to re-start the syscall
> > > > at the entry stop, rather than the method that was used to reach it.
> > > > Fix that by recording the original flag before performing the ptrace
> > > > stop, bringing the behavior in line with documentation and x86/powerpc.
> > > >
> > > > Signed-off-by: Keno Fischer <keno@juliacomputing.com>
> > > > ---
> > > > arch/arm64/kernel/ptrace.c | 8 +++++---
> > > > 1 file changed, 5 insertions(+), 3 deletions(-)
> > > >
> > > > diff --git a/arch/arm64/kernel/ptrace.c b/arch/arm64/kernel/ptrace.c
> > > > index b3d3005d9515..b67b4d14aa17 100644
> > > > --- a/arch/arm64/kernel/ptrace.c
> > > > +++ b/arch/arm64/kernel/ptrace.c
> > > > @@ -1829,10 +1829,12 @@ static void tracehook_report_syscall(struct pt_regs *regs,
> > > >
> > > > int syscall_trace_enter(struct pt_regs *regs)
> > > > {
> > > > - if (test_thread_flag(TIF_SYSCALL_TRACE) ||
> > > > - test_thread_flag(TIF_SYSCALL_EMU)) {
> > > > + u32 flags = READ_ONCE(current_thread_info()->flags) &
> > > > + (_TIF_SYSCALL_EMU | _TIF_SYSCALL_TRACE);
> > > > +
> > > > + if (flags) {
> > >
> > > nit: but I'd rather the '&' operation was in the conditional so that the
> > > 'flags' variable holds all of the flags.
> > >
> > > With that:
> > >
> > > Acked-by: Will Deacon <will@kernel.org>
> > >
> > > Also needs:
> > >
> > > Cc: <stable@vger.kernel.org>
> > > Fixes: f086f67485c5 ("arm64: ptrace: add support for syscall emulation")
> > >
> > > Catalin -- can you pick this up for 5.7 please, with my 'nit' addressed?
> >
> > I'll queue it with the above addressed. I think flags also needs to be
> > unsigned long rather than u32.
> >
> > However, before sending the pull request, I'd like Sudeep to confirm
> > that it doesn't break his original use-case for this feature.
>
> I just tested it with my simple programs I had before. I have also asked
> teams working on gvisor to test. They have tested it and see no
> regression. I will ask them to reply here.
>
> Tested-by: Sudeep Holla <sudeep.holla@arm.com>
Thanks Sudeep.
--
Catalin
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* 回复: [PATCH] arm64: Fix PTRACE_SYSEMU semantics
From: Bin Lu @ 2020-05-20 10:07 UTC (permalink / raw)
To: Catalin Marinas, Will Deacon
Cc: Will Deacon, linux-kernel@vger.kernel.org, oleg@redhat.com,
Keno Fischer, Sudeep Holla, linux-arm-kernel@lists.infradead.org
In-Reply-To: <20200519120725.GA20313@gaia>
Tested-by: Bin Lu <bin.lu@arm.com >
I have just tested all gVisor syscall test cases with ptrace(Regs, FPRegs, TLS) on Arm64 platform.
The test results are the same as before there was no patch.
My idea is that this kernel patch has no bad effects on gVisor.
Linux Kernel version: 5.7.0-rc6+
gVisor version: release-20200511.0
> -----邮件原件-----
> 发件人: linux-arm-kernel <linux-arm-kernel-bounces@lists.infradead.org> 代
> 表 Catalin Marinas
> 发送时间: 2020年5月19日 20:07
> 收件人: Will Deacon <will@kernel.org>
> 抄送: Will Deacon <Will.Deacon@arm.com>; linux-kernel@vger.kernel.org;
> oleg@redhat.com; Keno Fischer <keno@juliacomputing.com>; Sudeep Holla
> <Sudeep.Holla@arm.com>; linux-arm-kernel@lists.infradead.org
> 主题: Re: [PATCH] arm64: Fix PTRACE_SYSEMU semantics
>
> On Mon, May 18, 2020 at 12:41:20PM +0100, Will Deacon wrote:
> > On Fri, May 15, 2020 at 06:22:53PM -0400, Keno Fischer wrote:
> > > Quoth the man page:
> > > ```
> > > If the tracee was restarted by PTRACE_SYSCALL or PTRACE_SYSEMU,
> the
> > > tracee enters syscall-enter-stop just prior to entering any system
> > > call (which will not be executed if the restart was using
> > > PTRACE_SYSEMU, regardless of any change made to registers at this
> > > point or how the tracee is restarted after this stop).
> > > ```
> > >
> > > The parenthetical comment is currently true on x86 and powerpc, but
> > > not currently true on arm64. arm64 re-checks the _TIF_SYSCALL_EMU
> > > flag after the syscall entry ptrace stop. However, at this point, it
> > > reflects which method was used to re-start the syscall at the entry
> > > stop, rather than the method that was used to reach it.
> > > Fix that by recording the original flag before performing the ptrace
> > > stop, bringing the behavior in line with documentation and x86/powerpc.
> > >
> > > Signed-off-by: Keno Fischer <keno@juliacomputing.com>
> > > ---
> > > arch/arm64/kernel/ptrace.c | 8 +++++---
> > > 1 file changed, 5 insertions(+), 3 deletions(-)
> > >
> > > diff --git a/arch/arm64/kernel/ptrace.c b/arch/arm64/kernel/ptrace.c
> > > index b3d3005d9515..b67b4d14aa17 100644
> > > --- a/arch/arm64/kernel/ptrace.c
> > > +++ b/arch/arm64/kernel/ptrace.c
> > > @@ -1829,10 +1829,12 @@ static void tracehook_report_syscall(struct
> > > pt_regs *regs,
> > >
> > > int syscall_trace_enter(struct pt_regs *regs) {
> > > - if (test_thread_flag(TIF_SYSCALL_TRACE) ||
> > > - test_thread_flag(TIF_SYSCALL_EMU)) {
> > > + u32 flags = READ_ONCE(current_thread_info()->flags) &
> > > + (_TIF_SYSCALL_EMU | _TIF_SYSCALL_TRACE);
> > > +
> > > + if (flags) {
> >
> > nit: but I'd rather the '&' operation was in the conditional so that
> > the 'flags' variable holds all of the flags.
> >
> > With that:
> >
> > Acked-by: Will Deacon <will@kernel.org>
> >
> > Also needs:
> >
> > Cc: <stable@vger.kernel.org>
> > Fixes: f086f67485c5 ("arm64: ptrace: add support for syscall
> > emulation")
> >
> > Catalin -- can you pick this up for 5.7 please, with my 'nit' addressed?
>
> I'll queue it with the above addressed. I think flags also needs to be unsigned
> long rather than u32.
>
> However, before sending the pull request, I'd like Sudeep to confirm that it
> doesn't break his original use-case for this feature.
>
> --
> Catalin
>
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH v2 2/4] clk / soc: mediatek: Bind clock and gpu driver for mt2712
From: Stephen Boyd @ 2020-05-20 10:13 UTC (permalink / raw)
To: Enric Balletbo i Serra, ck.hu, mark.rutland, ulrich.hecht+renesas
Cc: Chun-Kuang Hu, drinkcat, Greg Kroah-Hartman, Michael Turquette,
matthias.bgg, linux-kernel, Richard Fontana, Matthias Brugger,
linux-mediatek, Allison Randal, hsinyi, matthias.bgg,
Thomas Gleixner, Collabora Kernel ML, linux-clk, linux-arm-kernel
In-Reply-To: <20200401201736.2980433-2-enric.balletbo@collabora.com>
Quoting Enric Balletbo i Serra (2020-04-01 13:17:34)
> Now that the mmsys driver is the top-level entry point for the
> multimedia subsystem, we could bind the clock and the gpu driver on
> those devices that is expected to work, so the drm driver is
> intantiated by the mmsys driver and display, hopefully, working again on
> those devices.
>
> Signed-off-by: Enric Balletbo i Serra <enric.balletbo@collabora.com>
> Reviewed-by: Chun-Kuang Hu <chunkuang.hu@kernel.org>
> ---
Acked-by: Stephen Boyd <sboyd@kernel.org>
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH v2 3/4] clk / soc: mediatek: Bind clock and gpu driver for mt2701
From: Stephen Boyd @ 2020-05-20 10:13 UTC (permalink / raw)
To: Enric Balletbo i Serra, ck.hu, mark.rutland, ulrich.hecht+renesas
Cc: Kate Stewart, Chun-Kuang Hu, drinkcat, Michael Turquette,
matthias.bgg, linux-kernel, Richard Fontana, Matthias Brugger,
linux-mediatek, Allison Randal, hsinyi, matthias.bgg,
Thomas Gleixner, Collabora Kernel ML, linux-clk, linux-arm-kernel
In-Reply-To: <20200401201736.2980433-3-enric.balletbo@collabora.com>
Quoting Enric Balletbo i Serra (2020-04-01 13:17:35)
> Now that the mmsys driver is the top-level entry point for the
> multimedia subsystem, we could bind the clock and the gpu driver on
> those devices that is expected to work, so the drm driver is
> intantiated by the mmsys driver and display, hopefully, working again.
>
> Signed-off-by: Enric Balletbo i Serra <enric.balletbo@collabora.com>
> Reviewed-by: Chun-Kuang Hu <chunkuang.hu@kernel.org>
> ---
Acked-by: Stephen Boyd <sboyd@kernel.org>
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH 1/4] clk/soc: mediatek: mt8183: Bind clock driver from platform device
From: Stephen Boyd @ 2020-05-20 10:14 UTC (permalink / raw)
To: Matthias Brugger, Michael Turquette, Rob Herring, matthias.bgg
Cc: Kate Stewart, devicetree, Greg Kroah-Hartman, linux-kernel,
linux-mediatek, linux-arm-kernel, mtk01761, Thomas Gleixner,
linux-clk, Allison Randal
In-Reply-To: <20200518113156.25009-1-matthias.bgg@kernel.org>
Quoting matthias.bgg@kernel.org (2020-05-18 04:31:53)
> From: Matthias Brugger <matthias.bgg@gmail.com>
>
> The mmsys driver is now the top level entry point for the multimedia
> system (mmsys), we bind the clock driver by creating a platform device.
> We also bind the MediaTek DRM driver which is not yet implement and
> therefor will errror out for now.
>
> Signed-off-by: Matthias Brugger <matthias.bgg@gmail.com>
> ---
Acked-by: Stephen Boyd <sboyd@kernel.org>
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH 2/4] clk/soc: mediatek: mt6797: Bind clock driver from platform device
From: Stephen Boyd @ 2020-05-20 10:15 UTC (permalink / raw)
To: Matthias Brugger, Michael Turquette, Rob Herring, matthias.bgg
Cc: Kate Stewart, devicetree, Greg Kroah-Hartman, linux-kernel,
Richard Fontana, linux-mediatek, linux-arm-kernel, mtk01761,
Thomas Gleixner, linux-clk, Allison Randal
In-Reply-To: <20200518113156.25009-2-matthias.bgg@kernel.org>
Quoting matthias.bgg@kernel.org (2020-05-18 04:31:54)
> From: Matthias Brugger <matthias.bgg@gmail.com>
>
> The mmsys driver is now the top level entry point for the multimedia
> system (mmsys), we bind the clock driver by creating a platform device.
> We also bind the MediaTek DRM driver which is not yet implement and
> therefor will errror out for now.
>
> Signed-off-by: Matthias Brugger <matthias.bgg@gmail.com>
> ---
Acked-by: Stephen Boyd <sboyd@kernel.org>
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH 3/4] clk/soc: mediatek: mt6779: Bind clock driver from platform device
From: Stephen Boyd @ 2020-05-20 10:15 UTC (permalink / raw)
To: Matthias Brugger, Michael Turquette, Rob Herring, matthias.bgg
Cc: Kate Stewart, devicetree, Greg Kroah-Hartman, linux-kernel,
linux-mediatek, linux-arm-kernel, mtk01761, Thomas Gleixner,
linux-clk, Allison Randal
In-Reply-To: <20200518113156.25009-3-matthias.bgg@kernel.org>
Quoting matthias.bgg@kernel.org (2020-05-18 04:31:55)
> From: Matthias Brugger <matthias.bgg@gmail.com>
>
> The mmsys driver is now the top level entry point for the multimedia
> system (mmsys), we bind the clock driver by creating a platform device.
> We also bind the MediaTek DRM driver which is not yet implement and
> therefor will errror out for now.
>
> Signed-off-by: Matthias Brugger <matthias.bgg@gmail.com>
> ---
Acked-by: Stephen Boyd <sboyd@kernel.org>
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox