summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSascha Hauer <s.hauer@pengutronix.de>2020-05-14 07:11:20 +0200
committerSascha Hauer <s.hauer@pengutronix.de>2020-05-14 07:11:20 +0200
commit9b01acf90f04aaf3f6f41875e91272cbeac51171 (patch)
treee0a8f1d096d3b8967961386a98cf08f28f494998
parent6e68393fb17254850b9bfc8e268569a05111c29c (diff)
parentb1dd1585deb21db5b595549267219ff9364dd4c8 (diff)
downloadbarebox-9b01acf90f04aaf3f6f41875e91272cbeac51171.tar.gz
Merge branch 'for-next/powerpc'
-rw-r--r--Documentation/boards/stm32mp.rst1
-rw-r--r--arch/arm/boards/Makefile1
-rw-r--r--arch/arm/boards/lxa-mc1/Makefile2
-rw-r--r--arch/arm/boards/lxa-mc1/board.c58
-rw-r--r--arch/arm/boards/lxa-mc1/lowlevel.c26
-rw-r--r--arch/arm/configs/stm32mp_defconfig28
-rw-r--r--arch/arm/dts/Makefile1
-rw-r--r--arch/arm/dts/at91-sama5d27_giantboard.dts2
-rw-r--r--arch/arm/dts/at91-sama5d27_som1_ek.dts1
-rw-r--r--arch/arm/dts/sama5d2.dtsi8
-rw-r--r--arch/arm/dts/stm32mp157c-lxa-mc1.dts44
-rw-r--r--arch/arm/dts/stm32mp157c-lxa-mc1.dtsi362
-rw-r--r--arch/arm/dts/stm32mp15xx-osd32.dtsi229
-rw-r--r--arch/arm/mach-stm32mp/Kconfig4
-rw-r--r--drivers/mci/Kconfig8
-rw-r--r--drivers/mci/Makefile1
-rw-r--r--drivers/mci/atmel-sdhci-common.c407
-rw-r--r--drivers/mci/atmel-sdhci.c169
-rw-r--r--drivers/mci/atmel-sdhci.h28
-rw-r--r--drivers/mci/sdhci.c11
-rw-r--r--drivers/mci/sdhci.h29
-rw-r--r--images/Makefile.stm32mp5
-rw-r--r--include/linux/iopoll.h29
23 files changed, 1444 insertions, 10 deletions
diff --git a/Documentation/boards/stm32mp.rst b/Documentation/boards/stm32mp.rst
index de793ab..607c59f 100644
--- a/Documentation/boards/stm32mp.rst
+++ b/Documentation/boards/stm32mp.rst
@@ -30,6 +30,7 @@ The resulting images will be placed under ``images/``:
::
barebox-stm32mp157c-dk2.img
+ barebox-stm32mp157c-lxa-mc1.img
Flashing barebox
diff --git a/arch/arm/boards/Makefile b/arch/arm/boards/Makefile
index 9fe458e..e9e9163 100644
--- a/arch/arm/boards/Makefile
+++ b/arch/arm/boards/Makefile
@@ -129,6 +129,7 @@ obj-$(CONFIG_MACH_SOCFPGA_TERASIC_SOCKIT) += terasic-sockit/
obj-$(CONFIG_MACH_SOLIDRUN_CUBOX) += solidrun-cubox/
obj-$(CONFIG_MACH_SOLIDRUN_MICROSOM) += solidrun-microsom/
obj-$(CONFIG_MACH_STM32MP157C_DK2) += stm32mp157c-dk2/
+obj-$(CONFIG_MACH_LXA_MC1) += lxa-mc1/
obj-$(CONFIG_MACH_TECHNEXION_PICO_HOBBIT) += technexion-pico-hobbit/
obj-$(CONFIG_MACH_TECHNEXION_WANDBOARD) += technexion-wandboard/
obj-$(CONFIG_MACH_TNY_A9260) += tny-a926x/
diff --git a/arch/arm/boards/lxa-mc1/Makefile b/arch/arm/boards/lxa-mc1/Makefile
new file mode 100644
index 0000000..092c31d
--- /dev/null
+++ b/arch/arm/boards/lxa-mc1/Makefile
@@ -0,0 +1,2 @@
+lwl-y += lowlevel.o
+obj-y += board.o
diff --git a/arch/arm/boards/lxa-mc1/board.c b/arch/arm/boards/lxa-mc1/board.c
new file mode 100644
index 0000000..7f1f3cc
--- /dev/null
+++ b/arch/arm/boards/lxa-mc1/board.c
@@ -0,0 +1,58 @@
+// SPDX-License-Identifier: GPL-2.0+
+#include <common.h>
+#include <linux/sizes.h>
+#include <init.h>
+#include <asm/memory.h>
+#include <mach/bbu.h>
+#include <bootsource.h>
+#include <of.h>
+
+static int of_fixup_regulator_supply_disable(struct device_node *root, void *path)
+{
+ struct device_node *node;
+ struct property *prop;
+
+ node = of_find_node_by_path_from(root, path);
+ if (!node) {
+ pr_warn("fixup for %s failed: not found\n", (const char *)path);
+ return -ENOENT;
+ }
+
+ if (!of_property_read_bool(node, "regulator-always-on"))
+ return 0;
+
+ prop = of_find_property(node, "vin-supply", NULL);
+ if (prop)
+ of_delete_property(prop);
+
+ return 0;
+}
+
+static int mc1_device_init(void)
+{
+ int flags;
+ if (!of_machine_is_compatible("lxa,stm32mp157c-mc1"))
+ return 0;
+
+ flags = bootsource_get_instance() == 0 ? BBU_HANDLER_FLAG_DEFAULT : 0;
+ stm32mp_bbu_mmc_register_handler("sd", "/dev/mmc0.ssbl", flags);
+
+ flags = bootsource_get_instance() == 1 ? BBU_HANDLER_FLAG_DEFAULT : 0;
+ stm32mp_bbu_mmc_register_handler("emmc", "/dev/mmc1.ssbl", flags);
+
+
+ if (bootsource_get_instance() == 0)
+ of_device_enable_path("/chosen/environment-sd");
+ else
+ of_device_enable_path("/chosen/environment-emmc");
+
+ barebox_set_hostname("lxa-mc1");
+
+ /* The regulator is powered by the PMIC, but is always on as far as
+ * software is concerned. Break the reference to the PMIC, so the OS
+ * doesn't need to defer SDMMC/Ethernet peripherals till after the PMIC
+ * is up.
+ */
+ return of_register_fixup(of_fixup_regulator_supply_disable, "/regulator_3v3");
+}
+device_initcall(mc1_device_init);
diff --git a/arch/arm/boards/lxa-mc1/lowlevel.c b/arch/arm/boards/lxa-mc1/lowlevel.c
new file mode 100644
index 0000000..274f824
--- /dev/null
+++ b/arch/arm/boards/lxa-mc1/lowlevel.c
@@ -0,0 +1,26 @@
+// SPDX-License-Identifier: GPL-2.0+
+#include <common.h>
+#include <mach/entry.h>
+#include <debug_ll.h>
+
+extern char __dtb_z_stm32mp157c_lxa_mc1_start[];
+
+static void setup_uart(void)
+{
+ /* first stage has set up the UART, so nothing to do here */
+ putc_ll('>');
+}
+
+ENTRY_FUNCTION(start_stm32mp157c_lxa_mc1, r0, r1, r2)
+{
+ void *fdt;
+
+ stm32mp_cpu_lowlevel_init();
+
+ if (IS_ENABLED(CONFIG_DEBUG_LL))
+ setup_uart();
+
+ fdt = __dtb_z_stm32mp157c_lxa_mc1_start + get_runtime_offset();
+
+ stm32mp1_barebox_entry(fdt);
+}
diff --git a/arch/arm/configs/stm32mp_defconfig b/arch/arm/configs/stm32mp_defconfig
index f69f9f9..4b902e1 100644
--- a/arch/arm/configs/stm32mp_defconfig
+++ b/arch/arm/configs/stm32mp_defconfig
@@ -1,5 +1,6 @@
CONFIG_ARCH_STM32MP=y
CONFIG_MACH_STM32MP157C_DK2=y
+CONFIG_MACH_LXA_MC1=y
CONFIG_THUMB2_BAREBOX=y
CONFIG_ARM_BOARD_APPEND_ATAG=y
CONFIG_ARM_OPTIMZED_STRING_FUNCTIONS=y
@@ -27,13 +28,14 @@ CONFIG_PARTITION_DISK_EFI=y
# CONFIG_PARTITION_DISK_EFI_GPT_COMPARE is not set
CONFIG_DEFAULT_ENVIRONMENT_GENERIC_NEW=y
CONFIG_RESET_SOURCE=y
-CONFIG_MACHINE_ID=y
CONFIG_CMD_DMESG=y
CONFIG_LONGHELP=y
CONFIG_CMD_IOMEM=y
CONFIG_CMD_IMD=y
CONFIG_CMD_MEMINFO=y
CONFIG_CMD_ARM_MMUINFO=y
+CONFIG_CMD_REGULATOR=y
+CONFIG_CMD_MMC=y
# CONFIG_CMD_BOOTU is not set
CONFIG_CMD_GO=y
CONFIG_CMD_RESET=y
@@ -73,8 +75,10 @@ CONFIG_CMD_DETECT=y
CONFIG_CMD_FLASH=y
CONFIG_CMD_GPIO=y
CONFIG_CMD_LED=y
+CONFIG_CMD_POWEROFF=y
CONFIG_CMD_WD=y
CONFIG_CMD_BAREBOX_UPDATE=y
+CONFIG_CMD_OF_DIFF=y
CONFIG_CMD_OF_NODE=y
CONFIG_CMD_OF_PROPERTY=y
CONFIG_CMD_OFTREE=y
@@ -84,8 +88,6 @@ CONFIG_NET_NETCONSOLE=y
CONFIG_OFDEVICE=y
CONFIG_OF_BAREBOX_DRIVERS=y
CONFIG_DRIVER_SERIAL_STM32=y
-CONFIG_DRIVER_NET_DESIGNWARE=y
-CONFIG_DRIVER_NET_DESIGNWARE_GENERIC=y
CONFIG_DRIVER_NET_DESIGNWARE_EQOS=y
CONFIG_DRIVER_NET_DESIGNWARE_STM32=y
CONFIG_AT803X_PHY=y
@@ -94,23 +96,40 @@ CONFIG_REALTEK_PHY=y
# CONFIG_SPI is not set
CONFIG_I2C=y
CONFIG_I2C_STM32=y
+CONFIG_VIDEO=y
+CONFIG_DRIVER_VIDEO_BACKLIGHT=y
+CONFIG_DRIVER_VIDEO_SIMPLE_PANEL=y
CONFIG_MCI=y
CONFIG_MCI_STARTUP=y
+CONFIG_MCI_MMC_BOOT_PARTITIONS=y
CONFIG_MCI_STM32_SDMMC2=y
CONFIG_MFD_STPMIC1=y
+CONFIG_MFD_STM32_TIMERS=y
CONFIG_LED=y
CONFIG_LED_GPIO=y
+CONFIG_LED_PWM=y
CONFIG_LED_GPIO_OF=y
+CONFIG_EEPROM_AT24=y
+CONFIG_KEYBOARD_GPIO=y
+CONFIG_INPUT_SPECIALKEYS=y
CONFIG_WATCHDOG=y
CONFIG_WATCHDOG_POLLER=y
CONFIG_STM32_IWDG_WATCHDOG=y
CONFIG_STPMIC1_WATCHDOG=y
-CONFIG_NVMEM=y
+CONFIG_PWM=y
+CONFIG_PWM_STM32=y
+CONFIG_HWRNG=y
+CONFIG_HWRNG_STM32=y
CONFIG_STM32_BSEC=y
CONFIG_REGULATOR=y
CONFIG_REGULATOR_FIXED=y
+CONFIG_REGULATOR_STM32_PWR=y
CONFIG_REGULATOR_STPMIC1=y
+CONFIG_REMOTEPROC=y
+CONFIG_STM32_REMOTEPROC=y
CONFIG_RESET_STM32=y
+CONFIG_GENERIC_PHY=y
+CONFIG_PHY_STM32_USBPHYC=y
CONFIG_FS_EXT4=y
CONFIG_FS_TFTP=y
CONFIG_FS_NFS=y
@@ -119,4 +138,3 @@ CONFIG_FS_FAT_WRITE=y
CONFIG_FS_FAT_LFN=y
CONFIG_ZLIB=y
CONFIG_CRC8=y
-CONFIG_DIGEST_SHA1_ARM=y
diff --git a/arch/arm/dts/Makefile b/arch/arm/dts/Makefile
index ddfe64e..1aeaa61 100644
--- a/arch/arm/dts/Makefile
+++ b/arch/arm/dts/Makefile
@@ -94,6 +94,7 @@ lwl-dtb-$(CONFIG_MACH_SOLIDRUN_MICROSOM) += imx6dl-hummingboard.dtb.o imx6q-humm
imx6dl-hummingboard2.dtb.o imx6q-hummingboard2.dtb.o \
imx6q-h100.dtb.o
lwl-dtb-$(CONFIG_MACH_STM32MP157C_DK2) += stm32mp157c-dk2.dtb.o
+lwl-dtb-$(CONFIG_MACH_LXA_MC1) += stm32mp157c-lxa-mc1.dtb.o
lwl-dtb-$(CONFIG_MACH_SCB9328) += imx1-scb9328.dtb.o
lwl-dtb-$(CONFIG_MACH_TECHNEXION_WANDBOARD) += imx6q-wandboard.dtb.o imx6dl-wandboard.dtb.o
lwl-dtb-$(CONFIG_MACH_TECHNEXION_PICO_HOBBIT) += imx6ul-pico-hobbit.dtb.o
diff --git a/arch/arm/dts/at91-sama5d27_giantboard.dts b/arch/arm/dts/at91-sama5d27_giantboard.dts
index 940379e..7e48fa1 100644
--- a/arch/arm/dts/at91-sama5d27_giantboard.dts
+++ b/arch/arm/dts/at91-sama5d27_giantboard.dts
@@ -17,6 +17,8 @@
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/regulator/active-semi,8945a-regulator.h>
+#include "sama5d2.dtsi"
+
/ {
model = "Giant Board";
compatible = "groboards,sama5d27-giantboard", "atmel,sama5d27", "atmel,sama5d2", "atmel,sama5";
diff --git a/arch/arm/dts/at91-sama5d27_som1_ek.dts b/arch/arm/dts/at91-sama5d27_som1_ek.dts
index 936f07e..cd038dc 100644
--- a/arch/arm/dts/at91-sama5d27_som1_ek.dts
+++ b/arch/arm/dts/at91-sama5d27_som1_ek.dts
@@ -4,6 +4,7 @@
*/
#include <arm/at91-sama5d27_som1_ek.dts>
+#include "sama5d2.dtsi"
/ {
chosen {
diff --git a/arch/arm/dts/sama5d2.dtsi b/arch/arm/dts/sama5d2.dtsi
index e69de29..51e964f 100644
--- a/arch/arm/dts/sama5d2.dtsi
+++ b/arch/arm/dts/sama5d2.dtsi
@@ -0,0 +1,8 @@
+// SPDX-License-Identifier: (GPL-2.0-or-later OR MIT)
+
+/ {
+ aliases {
+ mmc0 = &sdmmc0;
+ mmc1 = &sdmmc1;
+ };
+};
diff --git a/arch/arm/dts/stm32mp157c-lxa-mc1.dts b/arch/arm/dts/stm32mp157c-lxa-mc1.dts
new file mode 100644
index 0000000..57baaf4
--- /dev/null
+++ b/arch/arm/dts/stm32mp157c-lxa-mc1.dts
@@ -0,0 +1,44 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
+/*
+ * Copyright (C) 2020 Ahmad Fatoum, Pengutronix
+ */
+
+#include "stm32mp157c-lxa-mc1.dtsi"
+#include "stm32mp151.dtsi"
+
+/ {
+ chosen {
+ environment-sd {
+ compatible = "barebox,environment";
+ device-path = &sdmmc1, "partname:barebox-environment";
+ status = "disabled";
+ };
+
+ environment-emmc {
+ compatible = "barebox,environment";
+ device-path = &sdmmc2, "partname:barebox-environment";
+ status = "disabled";
+ };
+ };
+
+};
+
+&panel {
+ display-timings {
+ timing { /* edt,etm0700g0dh6 */
+ clock-frequency = <33260000>;
+ hactive = <800>;
+ vactive = <480>;
+ hfront-porch = <40>;
+ hsync-len = <128>;
+ hback-porch = <88>;
+ vfront-porch = <10>;
+ vsync-len = <2>;
+ vback-porch = <33>;
+ hsync-active = <0>;
+ vsync-active = <0>;
+ de-active = <1>;
+ pixelclk-active = <0>;
+ };
+ };
+};
diff --git a/arch/arm/dts/stm32mp157c-lxa-mc1.dtsi b/arch/arm/dts/stm32mp157c-lxa-mc1.dtsi
new file mode 100644
index 0000000..6603cf2
--- /dev/null
+++ b/arch/arm/dts/stm32mp157c-lxa-mc1.dtsi
@@ -0,0 +1,362 @@
+/* SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-3-Clause) */
+/*
+ * Copyright (C) 2020 STMicroelectronics - All Rights Reserved
+ * Copyright (C) 2020 Ahmad Fatoum, Pengutronix
+ */
+
+/dts-v1/;
+
+#include <arm/stm32mp157.dtsi>
+#include "stm32mp15xx-osd32.dtsi"
+#include <arm/stm32mp15xxac-pinctrl.dtsi>
+
+#include <dt-bindings/gpio/gpio.h>
+#include <dt-bindings/pwm/pwm.h>
+
+/ {
+ model = "Linux Automation MC-1 board";
+ compatible = "lxa,stm32mp157c-mc1", "st,stm32mp157";
+
+ aliases {
+ ethernet0 = &ethernet0;
+ mmc0 = &sdmmc1;
+ mmc1 = &sdmmc2;
+ serial0 = &uart4;
+ };
+
+ backlight: backlight {
+ compatible = "pwm-backlight";
+ pwms = <&backlight_pwm 1 100000 PWM_POLARITY_INVERTED>;
+ brightness-levels = <0 31 63 95 127 159 191 223 255>;
+ default-brightness-level = <7>;
+ power-supply = <&reg_5v2>; /* 3V3_BACKLIGHT */
+ };
+
+ chosen {
+ stdout-path = &uart4;
+ };
+
+ led-act {
+ compatible = "gpio-leds";
+
+ led-green {
+ label = "mc1:green:act";
+ gpios = <&gpioa 13 GPIO_ACTIVE_LOW>;
+ linux,default-trigger = "heartbeat";
+ };
+ };
+
+ led-rgb {
+ compatible = "pwm-leds";
+
+ led-red {
+ label = "mc1:red:rgb";
+ pwms = <&leds_pwm 1 1000000 0>;
+ max-brightness = <255>;
+ active-low;
+ };
+
+ led-green {
+ label = "mc1:green:rgb";
+ pwms = <&leds_pwm 2 1000000 0>;
+ max-brightness = <255>;
+ active-low;
+ };
+
+ led-blue {
+ label = "mc1:blue:rgb";
+ pwms = <&leds_pwm 3 1000000 0>;
+ max-brightness = <255>;
+ active-low;
+ };
+ };
+
+ panel: panel {
+ compatible = "edt,etm0700g0edh6", "simple-panel";
+ backlight = <&backlight>;
+ enable-gpios = <&gpiod 4 GPIO_ACTIVE_HIGH>;
+ power-supply = <&reg_3v3>;
+
+ port {
+ panel_input: endpoint {
+ remote-endpoint = <&ltdc_ep0_out>;
+ };
+ };
+ };
+
+ reg_3v3: regulator_3v3 {
+ compatible = "regulator-fixed";
+ regulator-name = "3V3";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-always-on;
+ vin-supply = <&v3v3>;
+ };
+
+ /* supplied by either debug board or PoE */
+ reg_5v2: regulator_5v2 {
+ compatible = "regulator-fixed";
+ regulator-name = "5V2";
+ regulator-min-microvolt = <5200000>;
+ regulator-max-microvolt = <5200000>;
+ regulator-always-on;
+ };
+};
+
+&ethernet0 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&ethernet0_rgmii_mc1pins_b>;
+ phy-mode = "rgmii-id";
+ phy-handle = <&ethphy>;
+ status = "okay";
+
+ mdio0 {
+ compatible = "snps,dwmac-mdio";
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ ethphy: ethernet-phy@3 { /* KSZ9031RN */
+ reg = <3>;
+ reset-gpios = <&gpiog 0 GPIO_ACTIVE_LOW>; /* ETH_RST# */
+ interrupt-parent = <&gpioa>;
+ interrupts = <6 IRQ_TYPE_EDGE_FALLING>; /* ETH_MDINT# */
+ rxc-skew-ps = <1860>;
+ txc-skew-ps = <1860>;
+ reset-assert-us = <10000>;
+ reset-deassert-us = <300>;
+ micrel,force-master;
+ };
+ };
+};
+
+&gpioz {
+ gpio-line-names = "HWID0", "HWID1", "HWID2", "HWID3", "", "",
+ "HWID4", "HWID5";
+};
+
+&gpu {
+ status = "okay";
+};
+
+&i2c5 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&i2c5_mc1pins_b>;
+ clock-frequency = <400000>;
+ status = "okay";
+
+ touchscreen@38 {
+ compatible = "edt,edt-ft5x06";
+ interrupt-parent = <&gpiod>;
+ interrupts = <11 IRQ_TYPE_EDGE_FALLING>; /* TOUCH_INT# */
+ vcc-supply = <&reg_3v3>;
+ reg = <0x38>;
+ reset-gpios = <&gpiof 8 GPIO_ACTIVE_LOW>; /* TOUCH_RESET# */
+ touchscreen-size-x = <1792>;
+ touchscreen-size-y = <1024>;
+ wakeup-source;
+ };
+};
+
+&ltdc {
+ pinctrl-names = "default";
+ pinctrl-0 = <&ltdc_mc1pins_c>;
+ status = "okay";
+
+ port {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ ltdc_ep0_out: endpoint@0 {
+ reg = <0>;
+ remote-endpoint = <&panel_input>;
+ };
+ };
+};
+
+&pmic {
+ regulators {
+ buck4-supply = <&reg_5v2>; /* VIN */
+ ldo2-supply = <&reg_5v2>; /* PMIC_LDO25IN */
+ ldo5-supply = <&reg_5v2>; /* PMIC_LDO25IN */
+ boost-supply = <&reg_5v2>; /* PMIC_BSTIN */
+ pwr_sw2-supply = <&bst_out>; /* PMIC_SWIN */
+ };
+};
+
+&sdmmc1 {
+ pinctrl-names = "default", "opendrain";
+ pinctrl-0 = <&sdmmc1_b4_pins_a>;
+ pinctrl-1 = <&sdmmc1_b4_od_pins_a>;
+ bus-width = <4>;
+ cd-gpios = <&gpioh 3 GPIO_ACTIVE_LOW>;
+ disable-wp;
+ no-1-8-v;
+ st,neg-edge;
+ vmmc-supply = <&reg_3v3>;
+ status = "okay";
+};
+
+&sdmmc1_b4_pins_a {
+ /*
+ * board lacks external pull-ups on SDMMC lines. Class 10 SD refuses to
+ * work, thus enable internal pull-ups.
+ */
+ pins1 {
+ /delete-property/ bias-disable;
+ bias-pull-up;
+ };
+ pins2 {
+ /delete-property/ bias-disable;
+ bias-pull-up;
+ };
+};
+
+&sdmmc2 {
+ pinctrl-names = "default", "opendrain";
+ pinctrl-0 = <&sdmmc2_b4_pins_a &sdmmc2_d47_mc1pins_b>;
+ pinctrl-1 = <&sdmmc2_b4_od_pins_a &sdmmc2_d47_mc1pins_b>;
+ bus-width = <8>;
+ no-1-8-v;
+ no-sd;
+ no-sdio;
+ non-removable;
+ st,neg-edge;
+ vmmc-supply = <&reg_3v3>;
+ status = "okay";
+};
+
+&timers3 {
+ status = "okay";
+
+ backlight_pwm: pwm {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pwm3_mc1pins_b>;
+ status = "okay";
+ };
+};
+
+&timers5 {
+ status = "okay";
+
+ leds_pwm: pwm {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pwm5_mc1pins_b>;
+ status = "okay";
+ };
+};
+
+&uart4 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&uart4_pins_a>;
+ status = "okay";
+};
+
+&pinctrl {
+ ethernet0_rgmii_mc1pins_b: mc1-rgmii-1 {
+ pins1 {
+ pinmux = <STM32_PINMUX('G', 5, AF11)>, /* ETH_RGMII_CLK125 */
+ <STM32_PINMUX('G', 4, AF11)>, /* ETH_RGMII_GTX_CLK */
+ <STM32_PINMUX('G', 13, AF11)>, /* ETH_RGMII_TXD0 */
+ <STM32_PINMUX('G', 14, AF11)>, /* ETH_RGMII_TXD1 */
+ <STM32_PINMUX('C', 2, AF11)>, /* ETH_RGMII_TXD2 */
+ <STM32_PINMUX('E', 2, AF11)>, /* ETH_RGMII_TXD3 */
+ <STM32_PINMUX('B', 11, AF11)>, /* ETH_RGMII_TX_CTL */
+ <STM32_PINMUX('C', 1, AF11)>; /* ETH_MDC */
+ bias-disable;
+ drive-push-pull;
+ slew-rate = <2>;
+ };
+ pins2 {
+ pinmux = <STM32_PINMUX('A', 2, AF11)>; /* ETH_MDIO */
+ bias-disable;
+ drive-push-pull;
+ slew-rate = <0>;
+ };
+ pins3 {
+ pinmux = <STM32_PINMUX('C', 4, AF11)>, /* ETH_RGMII_RXD0 */
+ <STM32_PINMUX('C', 5, AF11)>, /* ETH_RGMII_RXD1 */
+ <STM32_PINMUX('H', 6, AF11)>, /* ETH_RGMII_RXD2 */
+ <STM32_PINMUX('H', 7, AF11)>, /* ETH_RGMII_RXD3 */
+ <STM32_PINMUX('A', 1, AF11)>, /* ETH_RGMII_RX_CLK */
+ <STM32_PINMUX('A', 7, AF11)>; /* ETH_RGMII_RX_CTL */
+ bias-disable;
+ };
+ };
+
+ i2c5_mc1pins_b: mc1-i2c5-1 {
+ pins {
+ pinmux = <STM32_PINMUX('D', 0, AF4)>, /* I2C5_SCL */
+ <STM32_PINMUX('D', 1, AF4)>; /* I2C5_SDA */
+ bias-disable;
+ drive-open-drain;
+ slew-rate = <0>;
+ };
+ };
+
+ ltdc_mc1pins_c: mc1-ltdc-2 {
+ pins1 {
+ pinmux = <STM32_PINMUX('B', 1, AF9)>, /* LTDC_R6 */
+ <STM32_PINMUX('B', 9, AF14)>, /* LTDC_B7 */
+ <STM32_PINMUX('C', 0, AF14)>, /* LTDC_R5 */
+ <STM32_PINMUX('D', 3, AF14)>, /* LTDC_G7 */
+ <STM32_PINMUX('D', 6, AF14)>, /* LTDC_B2 */
+ <STM32_PINMUX('D', 10, AF14)>, /* LTDC_B3 */
+ <STM32_PINMUX('E', 11, AF14)>, /* LTDC_G3 */
+ <STM32_PINMUX('E', 12, AF14)>, /* LTDC_B4 */
+ <STM32_PINMUX('E', 13, AF14)>, /* LTDC_DE */
+ <STM32_PINMUX('E', 15, AF14)>, /* LTDC_R7 */
+ <STM32_PINMUX('H', 4, AF9)>, /* LTDC_G5 */
+ <STM32_PINMUX('H', 8, AF14)>, /* LTDC_R2 */
+ <STM32_PINMUX('H', 9, AF14)>, /* LTDC_R3 */
+ <STM32_PINMUX('H', 10, AF14)>, /* LTDC_R4 */
+ <STM32_PINMUX('H', 13, AF14)>, /* LTDC_G2 */
+ <STM32_PINMUX('H', 15, AF14)>, /* LTDC_G4 */
+ <STM32_PINMUX('I', 1, AF14)>, /* LTDC_G6 */
+ <STM32_PINMUX('I', 5, AF14)>, /* LTDC_B5 */
+ <STM32_PINMUX('I', 6, AF14)>, /* LTDC_B6 */
+ <STM32_PINMUX('I', 9, AF14)>, /* LTDC_VSYNC */
+ <STM32_PINMUX('I', 10, AF14)>; /* LTDC_HSYNC */
+ bias-disable;
+ drive-push-pull;
+ slew-rate = <0>;
+ };
+ pins2 {
+ pinmux = <STM32_PINMUX('E', 14, AF14)>; /* LTDC_CLK */
+ bias-disable;
+ drive-push-pull;
+ slew-rate = <1>;
+ };
+ };
+
+ pwm3_mc1pins_b: mc1-pwm3-1 {
+ pins {
+ pinmux = <STM32_PINMUX('B', 5, AF2)>; /* TIM3_CH2 */
+ bias-disable;
+ drive-push-pull;
+ slew-rate = <0>;
+ };
+ };
+
+ pwm5_mc1pins_b: mc1-pwm5-1 {
+ pins {
+ pinmux = <STM32_PINMUX('H', 11, AF2)>, /* TIM5_CH2 */
+ <STM32_PINMUX('H', 12, AF2)>, /* TIM5_CH3 */
+ <STM32_PINMUX('I', 0, AF2)>; /* TIM5_CH4 */
+ bias-disable;
+ drive-push-pull;
+ slew-rate = <0>;
+ };
+ };
+
+ sdmmc2_d47_mc1pins_b: mc1-sdmmc2-d47-1 {
+ pins {
+ pinmux = <STM32_PINMUX('A', 8, AF9)>, /* SDMMC2_D4 */
+ <STM32_PINMUX('A', 9, AF10)>, /* SDMMC2_D5 */
+ <STM32_PINMUX('C', 6, AF10)>, /* SDMMC2_D6 */
+ <STM32_PINMUX('C', 7, AF10)>; /* SDMMC2_D7 */
+ slew-rate = <1>;
+ drive-push-pull;
+ bias-disable;
+ };
+ };
+
+};
diff --git a/arch/arm/dts/stm32mp15xx-osd32.dtsi b/arch/arm/dts/stm32mp15xx-osd32.dtsi
new file mode 100644
index 0000000..8750835
--- /dev/null
+++ b/arch/arm/dts/stm32mp15xx-osd32.dtsi
@@ -0,0 +1,229 @@
+/* SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-3-Clause) */
+/*
+ * Copyright (C) 2020 STMicroelectronics - All Rights Reserved
+ * Copyright (C) 2020 Ahmad Fatoum, Pengutronix
+ */
+
+#include <arm/stm32mp15-pinctrl.dtsi>
+
+#include <dt-bindings/mfd/st,stpmic1.h>
+
+/ {
+ reserved-memory {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges;
+
+ mcuram2: mcuram2@10000000 {
+ compatible = "shared-dma-pool";
+ reg = <0x10000000 0x40000>;
+ no-map;
+ };
+
+ vdev0vring0: vdev0vring0@10040000 {
+ compatible = "shared-dma-pool";
+ reg = <0x10040000 0x1000>;
+ no-map;
+ };
+
+ vdev0vring1: vdev0vring1@10041000 {
+ compatible = "shared-dma-pool";
+ reg = <0x10041000 0x1000>;
+ no-map;
+ };
+
+ vdev0buffer: vdev0buffer@10042000 {
+ compatible = "shared-dma-pool";
+ reg = <0x10042000 0x4000>;
+ no-map;
+ };
+
+ mcuram: mcuram@30000000 {
+ compatible = "shared-dma-pool";
+ reg = <0x30000000 0x40000>;
+ no-map;
+ };
+
+ retram: retram@38000000 {
+ compatible = "shared-dma-pool";
+ reg = <0x38000000 0x10000>;
+ no-map;
+ };
+ };
+
+ reg_sip_eeprom: regulator_eeprom {
+ compatible = "regulator-fixed";
+ regulator-name = "sip_eeprom";
+ regulator-always-on;
+ };
+};
+
+&i2c4 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&i2c4_pins_a>;
+ clock-frequency = <400000>;
+ i2c-scl-rising-time-ns = <185>;
+ i2c-scl-falling-time-ns = <20>;
+ status = "okay";
+
+ pmic: stpmic@33 {
+ compatible = "st,stpmic1";
+ reg = <0x33>;
+ interrupts-extended = <&gpioa 0 IRQ_TYPE_EDGE_FALLING>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+
+ regulators {
+ compatible = "st,stpmic1-regulators";
+
+ ldo1-supply = <&v3v3>;
+ ldo6-supply = <&v3v3>;
+ pwr_sw1-supply = <&bst_out>;
+
+ vddcore: buck1 {
+ regulator-name = "vddcore";
+ regulator-min-microvolt = <1200000>;
+ regulator-max-microvolt = <1350000>;
+ regulator-always-on;
+ regulator-initial-mode = <0>;
+ regulator-over-current-protection;
+ };
+
+ vdd_ddr: buck2 {
+ regulator-name = "vdd_ddr";
+ regulator-min-microvolt = <1350000>;
+ regulator-max-microvolt = <1350000>;
+ regulator-always-on;
+ regulator-initial-mode = <0>;
+ regulator-over-current-protection;
+ };
+
+ vdd: buck3 {
+ regulator-name = "vdd";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-always-on;
+ st,mask-reset;
+ regulator-initial-mode = <0>;
+ regulator-over-current-protection;
+ };
+
+ v3v3: buck4 {
+ regulator-name = "v3v3";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-always-on;
+ regulator-over-current-protection;
+ regulator-initial-mode = <0>;
+ };
+
+ v1v8_audio: ldo1 {
+ regulator-name = "v1v8_audio";
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ regulator-always-on;
+ interrupts = <IT_CURLIM_LDO1 0>;
+
+ };
+
+ v3v3_hdmi: ldo2 {
+ regulator-name = "v3v3_hdmi";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-always-on;
+ interrupts = <IT_CURLIM_LDO2 0>;
+
+ };
+
+ vtt_ddr: ldo3 {
+ regulator-name = "vtt_ddr";
+ regulator-min-microvolt = <500000>;
+ regulator-max-microvolt = <750000>;
+ regulator-always-on;
+ regulator-over-current-protection;
+ };
+
+ vdd_usb: ldo4 {
+ regulator-name = "vdd_usb";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ interrupts = <IT_CURLIM_LDO4 0>;
+ };
+
+ vdda: ldo5 {
+ regulator-name = "vdda";
+ regulator-min-microvolt = <2900000>;
+ regulator-max-microvolt = <2900000>;
+ interrupts = <IT_CURLIM_LDO5 0>;
+ regulator-boot-on;
+ };
+
+ v1v2_hdmi: ldo6 {
+ regulator-name = "v1v2_hdmi";
+ regulator-min-microvolt = <1200000>;
+ regulator-max-microvolt = <1200000>;
+ regulator-always-on;
+ interrupts = <IT_CURLIM_LDO6 0>;
+
+ };
+
+ vref_ddr: vref_ddr {
+ regulator-name = "vref_ddr";
+ regulator-always-on;
+ regulator-over-current-protection;
+ };
+
+ bst_out: boost {
+ regulator-name = "bst_out";
+ interrupts = <IT_OCP_BOOST 0>;
+ };
+
+ vbus_otg: pwr_sw1 {
+ regulator-name = "vbus_otg";
+ interrupts = <IT_OCP_OTG 0>;
+ regulator-active-discharge;
+ };
+
+ vbus_sw: pwr_sw2 {
+ regulator-name = "vbus_sw";
+ interrupts = <IT_OCP_SWOUT 0>;
+ regulator-active-discharge;
+ };
+ };
+
+ onkey {
+ compatible = "st,stpmic1-onkey";
+ interrupts = <IT_PONKEY_F 0>, <IT_PONKEY_R 1>;
+ interrupt-names = "onkey-falling", "onkey-rising";
+ };
+
+ pmic_watchdog: watchdog {
+ compatible = "st,stpmic1-wdt";
+ status = "disabled";
+ };
+ };
+
+ sip_eeprom: eeprom@50 {
+ compatible = "atmel,24c32";
+ vcc-supply = <&reg_sip_eeprom>;
+ reg = <0x50>;
+ };
+};
+
+&ipcc {
+ status = "okay";
+};
+
+&m4_rproc {
+ memory-region = <&retram>, <&mcuram>, <&mcuram2>, <&vdev0vring0>,
+ <&vdev0vring1>, <&vdev0buffer>;
+ mboxes = <&ipcc 0>, <&ipcc 1>, <&ipcc 2>;
+ mbox-names = "vq0", "vq1", "shutdown";
+ interrupt-parent = <&exti>;
+ interrupts = <68 1>;
+ status = "okay";
+};
+
+&rng1 {
+ status = "okay";
+};
diff --git a/arch/arm/mach-stm32mp/Kconfig b/arch/arm/mach-stm32mp/Kconfig
index 9b55a3d..6e816ef 100644
--- a/arch/arm/mach-stm32mp/Kconfig
+++ b/arch/arm/mach-stm32mp/Kconfig
@@ -12,4 +12,8 @@ config MACH_STM32MP157C_DK2
select ARCH_STM32MP157
bool "STM32MP157C-DK2 board"
+config MACH_LXA_MC1
+ select ARCH_STM32MP157
+ bool "Linux Automation MC-1 board"
+
endif
diff --git a/drivers/mci/Kconfig b/drivers/mci/Kconfig
index 73da291..60c60ce 100644
--- a/drivers/mci/Kconfig
+++ b/drivers/mci/Kconfig
@@ -125,6 +125,14 @@ config MCI_ATMEL
Enable this entry to add support to read and write SD cards on a
Atmel AT91.
+config MCI_ATMEL_SDHCI
+ bool "ATMEL SDHCI (sama5d2)"
+ select MCI_SDHCI
+ depends on ARCH_AT91
+ help
+ Enable this entry to add support to read and write SD cards on an
+ Atmel sama5d2
+
config MCI_MMCI
bool "ARM PL180 MMCI"
depends on ARM_AMBA
diff --git a/drivers/mci/Makefile b/drivers/mci/Makefile
index 54eb659..177483d 100644
--- a/drivers/mci/Makefile
+++ b/drivers/mci/Makefile
@@ -1,6 +1,7 @@
obj-$(CONFIG_MCI) += mci-core.o
obj-$(CONFIG_MCI_ARASAN) += arasan-sdhci.o
obj-$(CONFIG_MCI_ATMEL) += atmel_mci.o
+obj-$(CONFIG_MCI_ATMEL_SDHCI) += atmel-sdhci.o atmel-sdhci-common.o
obj-$(CONFIG_MCI_BCM283X) += mci-bcm2835.o
obj-$(CONFIG_MCI_BCM283X_SDHOST) += bcm2835-sdhost.o
obj-$(CONFIG_MCI_DOVE) += dove-sdhci.o
diff --git a/drivers/mci/atmel-sdhci-common.c b/drivers/mci/atmel-sdhci-common.c
new file mode 100644
index 0000000..680b198
--- /dev/null
+++ b/drivers/mci/atmel-sdhci-common.c
@@ -0,0 +1,407 @@
+// SPDX-License-Identifier: GPL-2.0-only AND BSD-1-Clause
+/*
+ * Copyright (c) 2015, Atmel Corporation
+ * Copyright (c) 2019, Ahmad Fatoum, Pengutronix
+ *
+ * Atmel's name may not be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ */
+
+#define pr_fmt(fmt) "atmel-sdhci: " fmt
+
+#include <common.h>
+#include <mci.h>
+
+#include "atmel-sdhci.h"
+
+#define AT91_SDHCI_CA1R 0x44 /* Capabilities 1 Register */
+
+#define AT91_SDHCI_MC1R 0x204
+#define AT91_SDHCI_MC1_FCD BIT(7)
+#define AT91_SDHCI_CALCR 0x240
+#define AT91_SDHCI_CALCR_EN BIT(0)
+#define AT91_SDHCI_CALCR_ALWYSON BIT(4)
+
+static inline struct at91_sdhci *to_priv(struct sdhci *sdhci)
+{
+ return container_of(sdhci, struct at91_sdhci, sdhci);
+}
+
+void at91_sdhci_host_capability(struct at91_sdhci *host,
+ unsigned *voltages)
+{
+ u16 caps;
+
+ caps = sdhci_read16(&host->sdhci, SDHCI_CAPABILITIES_1);
+
+ if (caps & SDHCI_HOSTCAP_VOLTAGE_330)
+ *voltages |= MMC_VDD_32_33 | MMC_VDD_33_34;
+ if (caps & SDHCI_HOSTCAP_VOLTAGE_300)
+ *voltages |= MMC_VDD_29_30 | MMC_VDD_30_31;
+ if (caps & SDHCI_HOSTCAP_VOLTAGE_180)
+ *voltages |= MMC_VDD_165_195;
+}
+
+bool at91_sdhci_is_card_inserted(struct at91_sdhci *host)
+{
+ struct sdhci *sdhci = &host->sdhci;
+ bool is_inserted = false;
+ u32 status_mask, reg;
+ int ret;
+
+ /* Enable (unmask) the Interrupt Status 'card inserted' bit */
+ status_mask = sdhci_read32(sdhci, SDHCI_INT_ENABLE);
+ status_mask |= SDHCI_INT_CARD_INSERT;
+ sdhci_write32(sdhci, SDHCI_INT_ENABLE, status_mask);
+
+ reg = sdhci_read32(sdhci, SDHCI_PRESENT_STATE);
+ if (reg & SDHCI_CARD_PRESENT) {
+ is_inserted = true;
+ goto exit;
+ }
+
+ /*
+ * Debouncing of the card detect pin is up to 13ms on sama5d2 rev B
+ * and later. Try to be safe and wait for up to 50ms.
+ */
+ ret = sdhci_read32_poll_timeout(sdhci, SDHCI_INT_STATUS, reg,
+ reg & SDHCI_INT_CARD_INSERT,
+ 50 * USEC_PER_MSEC);
+ if (ret == 0)
+ is_inserted = true;
+exit:
+ status_mask &= ~SDHCI_INT_CARD_INSERT;
+ sdhci_write32(sdhci, SDHCI_INT_ENABLE, status_mask);
+
+ status_mask = sdhci_read32(sdhci, SDHCI_INT_STATUS);
+ status_mask |= SDHCI_INT_CARD_INSERT;
+ sdhci_write32(sdhci, SDHCI_INT_STATUS, status_mask);
+
+ return is_inserted;
+}
+
+static int at91_sdhci_wait_for_done(struct at91_sdhci *host, u32 mask)
+{
+ struct sdhci *sdhci = &host->sdhci;
+ u16 status;
+ int ret;
+
+ ret = sdhci_read16_poll_timeout(sdhci, SDHCI_INT_NORMAL_STATUS, status,
+ (status & mask) == mask || (status & SDHCI_INT_ERROR),
+ USEC_PER_SEC);
+
+ if (ret < 0) {
+ pr_err("SDHCI timeout while waiting for done\n");
+ return ret;
+ }
+
+ if (status & SDHCI_INT_ERROR) {
+ pr_err("SDHCI_INT_ERROR: 0x%08x\n",
+ sdhci_read16(sdhci, SDHCI_INT_ERROR_STATUS));
+ return -EPERM;
+ }
+
+ return status;
+}
+
+int at91_sdhci_send_command(struct at91_sdhci *host, struct mci_cmd *cmd,
+ struct mci_data *data)
+{
+ unsigned command, xfer;
+ struct sdhci *sdhci = &host->sdhci;
+ u32 mask, status, state;
+ int ret;
+
+ /* Wait for idle before next command */
+ mask = SDHCI_CMD_INHIBIT_CMD;
+ if (cmd->cmdidx != MMC_CMD_STOP_TRANSMISSION)
+ mask |= SDHCI_CMD_INHIBIT_DATA;
+
+ ret = sdhci_read32_poll_timeout(sdhci, SDHCI_PRESENT_STATE, state,
+ !(state & mask), 100 * USEC_PER_MSEC);
+ if (ret) {
+ pr_err("timeout while waiting for idle\n");
+ return ret;
+ }
+
+ sdhci_write32(sdhci, SDHCI_INT_STATUS, ~0U);
+
+ mask = SDHCI_INT_CMD_COMPLETE;
+
+ sdhci_set_cmd_xfer_mode(sdhci, cmd, data, false, &command, &xfer);
+
+ if (data) {
+ sdhci_write8(sdhci, SDHCI_TIMEOUT_CONTROL, 0xe);
+ sdhci_write16(sdhci, SDHCI_BLOCK_SIZE,
+ SDHCI_DMA_BOUNDARY_512K
+ | SDHCI_TRANSFER_BLOCK_SIZE(data->blocksize));
+ sdhci_write16(sdhci, SDHCI_BLOCK_COUNT, data->blocks);
+ sdhci_write16(sdhci, SDHCI_TRANSFER_MODE, xfer);
+ if (cmd->resp_type & MMC_RSP_BUSY)
+ mask |= SDHCI_INT_XFER_COMPLETE;
+ } else if (cmd->resp_type & MMC_RSP_BUSY) {
+ sdhci_write16(sdhci, SDHCI_TIMEOUT_CONTROL, 0xe);
+ }
+
+ sdhci_write32(sdhci, SDHCI_ARGUMENT, cmd->cmdarg);
+ sdhci_write16(sdhci, SDHCI_COMMAND, command);
+
+ status = at91_sdhci_wait_for_done(host, mask);
+ if (status >= 0 && (status & (SDHCI_INT_ERROR | mask)) == mask) {
+ sdhci_read_response(sdhci, cmd);
+ sdhci_write32(sdhci, SDHCI_INT_STATUS, mask);
+
+ if (data)
+ sdhci_transfer_data(sdhci, data);
+
+ udelay(1000);
+
+ status = sdhci_read32(sdhci, SDHCI_INT_STATUS);
+ sdhci_write32(sdhci, SDHCI_INT_STATUS, ~0U);
+
+ return 0;
+ }
+
+ status = sdhci_read32(sdhci, SDHCI_INT_STATUS);
+ sdhci_write32(sdhci, SDHCI_INT_STATUS, ~0U);
+
+ sdhci_reset(sdhci, SDHCI_RESET_CMD);
+ sdhci_reset(sdhci, SDHCI_RESET_DATA);
+
+ return status & SDHCI_INT_TIMEOUT ? -ETIMEDOUT : -ECOMM;
+}
+
+static void at91_sdhci_set_power(struct at91_sdhci *host, unsigned vdd)
+{
+ struct sdhci *sdhci = &host->sdhci;
+ u8 pwr = 0;
+
+ switch (vdd) {
+ case MMC_VDD_165_195:
+ pwr = SDHCI_POWER_180;
+ break;
+ case MMC_VDD_29_30:
+ case MMC_VDD_30_31:
+ pwr = SDHCI_POWER_300;
+ break;
+ case MMC_VDD_32_33:
+ case MMC_VDD_33_34:
+ pwr = SDHCI_POWER_330;
+ break;
+ }
+
+ if (pwr == 0) {
+ sdhci_write8(sdhci, SDHCI_POWER_CONTROL, 0);
+ return;
+ }
+
+ pwr |= SDHCI_BUS_POWER_EN;
+
+ sdhci_write8(sdhci, SDHCI_POWER_CONTROL, pwr);
+}
+
+static int at91_sdhci_set_clock(struct at91_sdhci *host, unsigned clock)
+{
+
+ struct sdhci *sdhci = &host->sdhci;
+ unsigned clk = 0, clk_div;
+ unsigned reg;
+ u32 present_mask, caps, caps_clk_mult;
+ int ret;
+
+ present_mask = SDHCI_CMD_INHIBIT_CMD | SDHCI_CMD_INHIBIT_DATA;
+ ret = sdhci_read32_poll_timeout(sdhci, SDHCI_PRESENT_STATE, reg,
+ !(reg & present_mask),
+ 100 * USEC_PER_MSEC);
+ if (ret) {
+ pr_warn("Timeout waiting for CMD and DAT Inhibit bits\n");
+ return ret;
+ }
+
+ sdhci_write16(sdhci, SDHCI_CLOCK_CONTROL, 0);
+
+ if (clock == 0)
+ return 0;
+
+ caps = sdhci_read32(sdhci, AT91_SDHCI_CA1R);
+
+ caps_clk_mult = (caps & SDHCI_CLOCK_MUL_MASK) >> SDHCI_CLOCK_MUL_SHIFT;
+
+ if (caps_clk_mult) {
+ for (clk_div = 1; clk_div <= 1024; clk_div++) {
+ if (host->caps_max_clock / clk_div <= clock)
+ break;
+ }
+ clk = SDHCI_PROG_CLOCK_MODE;
+ clk_div -= 1;
+ } else {
+ /* Version 3.00 divisors must be a multiple of 2. */
+ if (host->caps_max_clock <= clock) {
+ clk_div = 1;
+ } else {
+ for (clk_div = 2; clk_div < 2048; clk_div += 2) {
+ if (host->caps_max_clock / clk_div <= clock)
+ break;
+ }
+ }
+ clk_div >>= 1;
+ }
+
+ clk |= SDHCI_FREQ_SEL(clk_div);
+ clk |= ((clk_div & SDHCI_DIV_HI_MASK) >> SDHCI_DIV_MASK_LEN)
+ << SDHCI_DIVIDER_HI_SHIFT;
+ clk |= SDHCI_INTCLOCK_EN;
+
+ sdhci_write16(sdhci, SDHCI_CLOCK_CONTROL, clk);
+
+ ret = sdhci_read32_poll_timeout(sdhci, SDHCI_CLOCK_CONTROL, clk,
+ clk & SDHCI_INTCLOCK_STABLE,
+ 20 * USEC_PER_MSEC);
+ if (ret) {
+ pr_warn("Timeout waiting for clock stable\n");
+ return ret;
+ }
+
+ clk |= SDHCI_SDCLOCK_EN;
+ sdhci_write16(sdhci, SDHCI_CLOCK_CONTROL, clk);
+
+ reg = sdhci_read8(sdhci, SDHCI_HOST_CONTROL);
+ if (clock > 26000000)
+ reg |= SDHCI_HIGHSPEED_EN;
+ else
+ reg &= ~SDHCI_HIGHSPEED_EN;
+
+ sdhci_write8(sdhci, SDHCI_HOST_CONTROL, reg);
+
+ return 0;
+}
+
+static int at91_sdhci_set_bus_width(struct at91_sdhci *host, unsigned bus_width)
+{
+ struct sdhci *sdhci = &host->sdhci;
+ unsigned reg;
+
+ reg = sdhci_read8(sdhci, SDHCI_HOST_CONTROL);
+
+ switch(bus_width) {
+ case MMC_BUS_WIDTH_8:
+ reg |= SDHCI_DATA_WIDTH_8BIT;
+ break;
+ case MMC_BUS_WIDTH_4:
+ reg &= ~SDHCI_DATA_WIDTH_8BIT;
+ reg |= SDHCI_DATA_WIDTH_4BIT;
+ break;
+ default:
+ reg &= ~SDHCI_DATA_WIDTH_8BIT;
+ reg &= ~SDHCI_DATA_WIDTH_4BIT;
+ }
+
+ sdhci_write8(sdhci, SDHCI_HOST_CONTROL, reg);
+
+ return 0;
+}
+
+int at91_sdhci_set_ios(struct at91_sdhci *host, struct mci_ios *ios)
+{
+ int ret;
+
+ ret = at91_sdhci_set_clock(host, ios->clock);
+ if (ret)
+ return ret;
+
+ return at91_sdhci_set_bus_width(host, ios->bus_width);
+}
+
+int at91_sdhci_init(struct at91_sdhci *host, u32 maxclk,
+ bool force_cd, bool cal_always_on)
+{
+ struct sdhci *sdhci = &host->sdhci;
+ unsigned status_mask;
+
+ host->caps_max_clock = maxclk;
+
+ at91_sdhci_set_power(host, MMC_VDD_32_33);
+
+ status_mask = SDHCI_INT_CMD_COMPLETE
+ | SDHCI_INT_XFER_COMPLETE
+ | SDHCI_INT_SPACE_AVAIL
+ | SDHCI_INT_DATA_AVAIL;
+
+ status_mask |= SDHCI_INT_TIMEOUT
+ | SDHCI_INT_CRC
+ | SDHCI_INT_END_BIT
+ | SDHCI_INT_INDEX
+ | SDHCI_INT_DATA_TIMEOUT
+ | SDHCI_INT_DATA_CRC
+ | SDHCI_INT_DATA_END_BIT;
+
+ sdhci_write32(sdhci, SDHCI_INT_ENABLE, status_mask);
+
+ sdhci_write32(sdhci, SDHCI_SIGNAL_ENABLE, 0);
+
+ /*
+ * If the device attached to the mci bus is not removable, it is safer
+ * to set the Force Card Detect bit. People often don't connect the
+ * card detect signal and use this pin for another purpose. If the card
+ * detect pin is not muxed to SDHCI controller, a default value is
+ * used. This value can be different from a SoC revision to another
+ * one. Problems come when this default value is not card present. To
+ * avoid this case, if the device is non removable then the card
+ * detection procedure using the SDMCC_CD signal is bypassed.
+ * This bit is reset when a software reset for all command is performed
+ * so we need to implement our own reset function to set back this bit.
+ */
+ if (force_cd) {
+ u8 mc1r = sdhci_read8(sdhci, AT91_SDHCI_MC1R);
+ mc1r |= AT91_SDHCI_MC1_FCD;
+ sdhci_write8(sdhci, AT91_SDHCI_MC1R, mc1r);
+ }
+
+ if (cal_always_on) {
+ sdhci_write32(sdhci, AT91_SDHCI_CALCR_ALWYSON | AT91_SDHCI_CALCR_EN,
+ AT91_SDHCI_CALCR);
+ }
+
+ return 0;
+}
+
+static u32 at91_sdhci_read32(struct sdhci *sdhci, int reg)
+{
+ return readl(to_priv(sdhci)->base + reg);
+}
+
+static void at91_sdhci_write32(struct sdhci *sdhci, int reg, u32 value)
+{
+ writel(value, to_priv(sdhci)->base + reg);
+}
+
+static u16 at91_sdhci_read16(struct sdhci *sdhci, int reg)
+{
+ return readw(to_priv(sdhci)->base + reg);
+}
+
+static void at91_sdhci_write16(struct sdhci *sdhci, int reg, u16 value)
+{
+ writew(value, to_priv(sdhci)->base + reg);
+}
+
+static u8 at91_sdhci_read8(struct sdhci *sdhci, int reg)
+{
+ return readb(to_priv(sdhci)->base + reg);
+}
+
+static void at91_sdhci_write8(struct sdhci *sdhci, int reg, u8 value)
+{
+ writeb(value, to_priv(sdhci)->base + reg);
+}
+
+void at91_sdhci_mmio_init(struct at91_sdhci *host, void __iomem *base)
+{
+ host->base = base;
+ host->sdhci.read8 = at91_sdhci_read8;
+ host->sdhci.read16 = at91_sdhci_read16;
+ host->sdhci.read32 = at91_sdhci_read32;
+ host->sdhci.write8 = at91_sdhci_write8;
+ host->sdhci.write16 = at91_sdhci_write16;
+ host->sdhci.write32 = at91_sdhci_write32;
+}
diff --git a/drivers/mci/atmel-sdhci.c b/drivers/mci/atmel-sdhci.c
new file mode 100644
index 0000000..6351186
--- /dev/null
+++ b/drivers/mci/atmel-sdhci.c
@@ -0,0 +1,169 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Atmel SDMMC controller driver.
+ *
+ * Copyright (C) 2015 Atmel,
+ * 2015 Ludovic Desroches <ludovic.desroches@atmel.com>
+ * 2020 Ahmad Fatoum <a.fatoum@pengutronix.de>
+ */
+
+#include <common.h>
+#include <init.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <of.h>
+#include <mci.h>
+
+#include "atmel-sdhci.h"
+
+#define ATMEL_SDHC_MIN_FREQ 400000
+#define ATMEL_SDHC_GCK_RATE 240000000
+
+struct at91_sdhci_priv {
+ struct at91_sdhci host;
+ struct mci_host mci;
+ struct clk *hclock, *gck, *mainck;
+ bool cal_always_on;
+ u32 gck_rate;
+};
+
+#define to_priv(h) container_of(h, struct at91_sdhci_priv, mci)
+
+static int at91_sdhci_mci_send_cmd(struct mci_host *mci, struct mci_cmd *cmd,
+ struct mci_data *data)
+{
+ return at91_sdhci_send_command(&to_priv(mci)->host, cmd, data);
+}
+
+static void at91_sdhci_mci_set_ios(struct mci_host *mci, struct mci_ios *ios)
+{
+ at91_sdhci_set_ios(&to_priv(mci)->host, ios);
+}
+
+static int at91_sdhci_mci_init(struct mci_host *mci, struct device_d *dev)
+{
+ struct at91_sdhci_priv *priv = to_priv(mci);
+ struct sdhci *sdhci = &priv->host.sdhci;
+ int ret;
+
+ ret = sdhci_reset(sdhci, SDHCI_RESET_ALL);
+ if (ret)
+ return ret;
+
+ return at91_sdhci_init(&priv->host, priv->gck_rate,
+ priv->mci.non_removable, priv->cal_always_on);
+}
+
+static int at91_sdhci_conf_clks(struct at91_sdhci_priv *priv)
+{
+ unsigned long real_gck_rate;
+ int ret;
+
+ /*
+ * The mult clock is provided by as a generated clock by the PMC
+ * controller. In order to set the rate of gck, we have to get the
+ * base clock rate and the clock mult from capabilities.
+ */
+ clk_enable(priv->hclock);
+ ret = clk_set_rate(priv->gck, ATMEL_SDHC_GCK_RATE);
+ if (ret < 0) {
+ clk_disable(priv->hclock);
+ return ret;
+ }
+
+ real_gck_rate = clk_get_rate(priv->gck);
+
+ clk_enable(priv->mainck);
+ clk_enable(priv->gck);
+
+ return clamp_t(int, real_gck_rate, ATMEL_SDHC_MIN_FREQ, INT_MAX);
+}
+
+static void at91_sdhci_set_mci_caps(struct at91_sdhci_priv *priv)
+{
+ struct mci_host *mci = &priv->mci;
+ at91_sdhci_host_capability(&priv->host, &mci->voltages);
+
+ if (mci->f_max >= 26000000)
+ mci->host_caps |= MMC_CAP_MMC_HIGHSPEED;
+ if (mci->f_max >= 52000000)
+ mci->host_caps |= MMC_CAP_MMC_HIGHSPEED_52MHZ;
+
+ mci_of_parse(mci);
+}
+
+static int at91_sdhci_card_present(struct mci_host *mci)
+{
+ return at91_sdhci_is_card_inserted(&to_priv(mci)->host);
+}
+
+static int at91_sdhci_probe(struct device_d *dev)
+{
+ struct at91_sdhci_priv *priv;
+ struct resource *iores;
+
+ priv = xzalloc(sizeof(*priv));
+ dev->priv = priv;
+
+ iores = dev_request_mem_resource(dev, 0);
+ if (IS_ERR(iores)) {
+ dev_err(dev, "could not get iomem region\n");
+ return PTR_ERR(iores);
+ }
+
+ priv->mainck = clk_get(dev, "baseclk");
+ if (IS_ERR(priv->mainck)) {
+ dev_err(dev, "failed to get baseclk\n");
+ return PTR_ERR(priv->mainck);
+ }
+
+ priv->hclock = clk_get(dev, "hclock");
+ if (IS_ERR(priv->hclock)) {
+ dev_err(dev, "failed to get hclock\n");
+ return PTR_ERR(priv->hclock);
+ }
+
+ priv->gck = clk_get(dev, "multclk");
+ if (IS_ERR(priv->gck)) {
+ dev_err(dev, "failed to get multclk\n");
+ return PTR_ERR(priv->gck);
+ }
+
+ /*
+ * if SDCAL pin is wrongly connected, we must enable
+ * the analog calibration cell permanently.
+ */
+ priv->cal_always_on = of_property_read_bool(dev->device_node,
+ "microchip,sdcal-inverted");
+
+ at91_sdhci_mmio_init(&priv->host, IOMEM(iores->start));
+
+ priv->gck_rate = at91_sdhci_conf_clks(priv);
+ if (priv->gck_rate < 0)
+ return priv->gck_rate;
+
+ priv->mci.hw_dev = dev;
+ priv->mci.send_cmd = at91_sdhci_mci_send_cmd;
+ priv->mci.set_ios = at91_sdhci_mci_set_ios;
+ priv->mci.init = at91_sdhci_mci_init;
+ priv->mci.f_max = priv->gck_rate;
+ priv->mci.f_min = ATMEL_SDHC_MIN_FREQ;
+ priv->mci.card_present = at91_sdhci_card_present;
+
+ at91_sdhci_set_mci_caps(priv);
+
+ return mci_register(&priv->mci);
+}
+
+static const struct of_device_id at91_sdhci_dt_match[] = {
+ { .compatible = "atmel,sama5d2-sdhci" },
+ { .compatible = "microchip,sam9x60-sdhci" },
+ { /* sentinel */ }
+};
+
+static struct driver_d at91_sdhci_driver = {
+ .name = "sdhci-at91",
+ .of_compatible = DRV_OF_COMPAT(at91_sdhci_dt_match),
+ .probe = at91_sdhci_probe,
+};
+device_platform_driver(at91_sdhci_driver);
diff --git a/drivers/mci/atmel-sdhci.h b/drivers/mci/atmel-sdhci.h
new file mode 100644
index 0000000..897ed4e
--- /dev/null
+++ b/drivers/mci/atmel-sdhci.h
@@ -0,0 +1,28 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (c) 2020 Ahmad Fatoum, Pengutronix
+
+#ifndef ATMEL_SDHCI_H_
+#define ATMEL_SDHCI_H_
+
+#include <linux/types.h>
+#include <mci.h>
+
+#include "sdhci.h"
+
+struct at91_sdhci {
+ struct sdhci sdhci;
+ void __iomem *base;
+ u32 caps_max_clock;
+};
+
+int at91_sdhci_init(struct at91_sdhci *host, u32 maxclk,
+ bool force_cd, bool cal_always_on);
+void at91_sdhci_mmio_init(struct at91_sdhci *host, void __iomem *base);
+int at91_sdhci_send_command(struct at91_sdhci *host, struct mci_cmd *sd_cmd,
+ struct mci_data *data);
+bool at91_sdhci_is_card_inserted(struct at91_sdhci *host);
+void at91_sdhci_host_capability(struct at91_sdhci *host,
+ unsigned int *voltages);
+int at91_sdhci_set_ios(struct at91_sdhci *host, struct mci_ios *ios);
+
+#endif
diff --git a/drivers/mci/sdhci.c b/drivers/mci/sdhci.c
index 172c834..dba26b2 100644
--- a/drivers/mci/sdhci.c
+++ b/drivers/mci/sdhci.c
@@ -138,3 +138,14 @@ int sdhci_transfer_data(struct sdhci *sdhci, struct mci_data *data)
return 0;
}
+
+int sdhci_reset(struct sdhci *sdhci, u8 mask)
+{
+ u8 val;
+
+ sdhci_write8(sdhci, SDHCI_SOFTWARE_RESET, mask);
+
+ return sdhci_read8_poll_timeout(sdhci, SDHCI_SOFTWARE_RESET,
+ val, !(val & mask),
+ 100 * USEC_PER_MSEC);
+}
diff --git a/drivers/mci/sdhci.h b/drivers/mci/sdhci.h
index a307dc9..7b3f644 100644
--- a/drivers/mci/sdhci.h
+++ b/drivers/mci/sdhci.h
@@ -1,6 +1,9 @@
#ifndef __MCI_SDHCI_H
#define __MCI_SDHCI_H
+#include <pbl.h>
+#include <linux/iopoll.h>
+
#define SDHCI_DMA_ADDRESS 0x00
#define SDHCI_BLOCK_SIZE__BLOCK_COUNT 0x04
#define SDHCI_BLOCK_SIZE 0x04
@@ -42,6 +45,7 @@
#define SDHCI_PRESENT_STATE 0x24
#define SDHCI_WRITE_PROTECT BIT(19)
#define SDHCI_CARD_DETECT BIT(18)
+#define SDHCI_CARD_PRESENT BIT(16)
#define SDHCI_BUFFER_READ_ENABLE BIT(11)
#define SDHCI_BUFFER_WRITE_ENABLE BIT(10)
#define SDHCI_DATA_LINE_ACTIVE BIT(2)
@@ -56,18 +60,28 @@
#define SDHCI_HIGHSPEED_EN BIT(2)
#define SDHCI_DATA_WIDTH_4BIT BIT(1)
#define SDHCI_POWER_CONTROL 0x29
+#define SDHCI_POWER_ON 0x01
+#define SDHCI_POWER_180 0x0A
+#define SDHCI_POWER_300 0x0C
+#define SDHCI_POWER_330 0x0E
#define SDHCI_BUS_VOLTAGE_330 SDHCI_BUS_VOLTAGE(7)
#define SDHCI_BUS_VOLTAGE(v) ((v) << 1)
#define SDHCI_BUS_POWER_EN BIT(0)
#define SDHCI_CLOCK_CONTROL__TIMEOUT_CONTROL__SOFTWARE_RESET 0x2c
#define SDHCI_CLOCK_CONTROL 0x2c
+#define SDHCI_DIVIDER_HI_SHIFT 6
+#define SDHCI_DIV_HI_MASK 0x300
+#define SDHCI_DIV_MASK_LEN 8
#define SDHCI_FREQ_SEL(x) (((x) & 0xff) << 8)
+#define SDHCI_PROG_CLOCK_MODE BIT(5)
#define SDHCI_SDCLOCK_EN BIT(2)
#define SDHCI_INTCLOCK_STABLE BIT(1)
#define SDHCI_INTCLOCK_EN BIT(0)
#define SDHCI_TIMEOUT_CONTROL 0x2e
#define SDHCI_SOFTWARE_RESET 0x2f
#define SDHCI_RESET_ALL BIT(0)
+#define SDHCI_RESET_CMD BIT(1)
+#define SDHCI_RESET_DATA BIT(2)
#define SDHCI_INT_STATUS 0x30
#define SDHCI_INT_NORMAL_STATUS 0x30
#define SDHCI_INT_DATA_END_BIT BIT(22)
@@ -79,6 +93,7 @@
#define SDHCI_INT_TIMEOUT BIT(16)
#define SDHCI_INT_ERROR BIT(15)
#define SDHCI_INT_CARD_INT BIT(8)
+#define SDHCI_INT_CARD_INSERT BIT(6)
#define SDHCI_INT_DATA_AVAIL BIT(5)
#define SDHCI_INT_SPACE_AVAIL BIT(4)
#define SDHCI_INT_DMA BIT(3)
@@ -86,6 +101,7 @@
#define SDHCI_INT_CMD_COMPLETE BIT(0)
#define SDHCI_INT_ERROR_STATUS 0x32
#define SDHCI_INT_ENABLE 0x34
+#define SDHCI_INT_ERROR_ENABLE 0x36
#define SDHCI_SIGNAL_ENABLE 0x38
#define SDHCI_ACMD12_ERR__HOST_CONTROL2 0x3C
#define SDHCI_CAPABILITIES 0x40
@@ -96,6 +112,9 @@
#define SDHCI_HOSTCAP_HIGHSPEED BIT(5)
#define SDHCI_HOSTCAP_8BIT BIT(2)
+#define SDHCI_CLOCK_MUL_MASK 0x00FF0000
+#define SDHCI_CLOCK_MUL_SHIFT 16
+
#define SDHCI_SPEC_200_MAX_CLK_DIVIDER 256
#define SDHCI_MMC_BOOT 0xC4
@@ -143,5 +162,15 @@ void sdhci_set_cmd_xfer_mode(struct sdhci *host, struct mci_cmd *cmd,
struct mci_data *data, bool dma, u32 *command,
u32 *xfer);
int sdhci_transfer_data(struct sdhci *sdhci, struct mci_data *data);
+int sdhci_reset(struct sdhci *sdhci, u8 mask);
+
+#define sdhci_read8_poll_timeout(sdhci, reg, val, cond, timeout_us) \
+ read_poll_timeout(sdhci_read8, val, cond, timeout_us, sdhci, reg)
+
+#define sdhci_read16_poll_timeout(sdhci, reg, val, cond, timeout_us) \
+ read_poll_timeout(sdhci_read16, val, cond, timeout_us, sdhci, reg)
+
+#define sdhci_read32_poll_timeout(sdhci, reg, val, cond, timeout_us) \
+ read_poll_timeout(sdhci_read32, val, cond, timeout_us, sdhci, reg)
#endif /* __MCI_SDHCI_H */
diff --git a/images/Makefile.stm32mp b/images/Makefile.stm32mp
index 910e029..38872c9 100644
--- a/images/Makefile.stm32mp
+++ b/images/Makefile.stm32mp
@@ -17,3 +17,8 @@ pblb-$(CONFIG_MACH_STM32MP157C_DK2) += start_stm32mp157c_dk2
FILE_barebox-stm32mp157c-dk2.img = start_stm32mp157c_dk2.pblb.stm32
OPTS_start_stm32mp157c_dk2.pblb.stm32 = $(STM32MP1_OPTS)
image-$(CONFIG_MACH_STM32MP157C_DK2) += barebox-stm32mp157c-dk2.img
+
+pblb-$(CONFIG_MACH_LXA_MC1) += start_stm32mp157c_lxa_mc1
+FILE_barebox-stm32mp157c-lxa-mc1.img = start_stm32mp157c_lxa_mc1.pblb.stm32
+OPTS_start_stm32mp157c_lxa_mc1.pblb.stm32 = $(STM32MP1_OPTS)
+image-$(CONFIG_MACH_LXA_MC1) += barebox-stm32mp157c-lxa-mc1.img
diff --git a/include/linux/iopoll.h b/include/linux/iopoll.h
index 66c8f65..8bf912e 100644
--- a/include/linux/iopoll.h
+++ b/include/linux/iopoll.h
@@ -13,12 +13,12 @@
#include <pbl.h>
/**
- * readx_poll_timeout - Periodically poll an address until a condition is met or a timeout occurs
+ * read_poll_timeout - Periodically poll an address until a condition is met or a timeout occurs
* @op: accessor function (takes @addr as its only argument)
- * @addr: Address to poll
* @val: Variable to read the value into
* @cond: Break condition (usually involving @val)
* @timeout_us: Timeout in us, 0 means never timeout
+ * @args: arguments for @op poll
*
* Returns 0 on success and -ETIMEDOUT upon a timeout. In either
* case, the last read value at @addr is stored in @val.
@@ -29,24 +29,43 @@
* We do not have timing functions in the PBL, so ignore the timeout value and
* loop infinitely here.
*/
-#define readx_poll_timeout(op, addr, val, cond, timeout_us) \
+#define read_poll_timeout(op, val, cond, timeout_us, args...) \
({ \
uint64_t start; \
if (!IN_PBL && timeout_us) \
start = get_time_ns(); \
for (;;) { \
- (val) = op(addr); \
+ (val) = op(args); \
if (cond) \
break; \
if (!IN_PBL && timeout_us && \
is_timeout(start, ((timeout_us) * USECOND))) { \
- (val) = op(addr); \
+ (val) = op(args); \
break; \
} \
} \
(cond) ? 0 : -ETIMEDOUT; \
})
+/**
+ * readx_poll_timeout - Periodically poll an address until a condition is met or a timeout occurs
+ * @op: accessor function (takes @addr as its only argument)
+ * @addr: Address to poll
+ * @val: Variable to read the value into
+ * @cond: Break condition (usually involving @val)
+ * @timeout_us: Timeout in us, 0 means never timeout
+ *
+ * Returns 0 on success and -ETIMEDOUT upon a timeout. In either
+ * case, the last read value at @addr is stored in @val.
+ *
+ * When available, you'll probably want to use one of the specialized
+ * macros defined below rather than this macro directly.
+ *
+ * We do not have timing functions in the PBL, so ignore the timeout value and
+ * loop infinitely here.
+ */
+#define readx_poll_timeout(op, addr, val, cond, timeout_us) \
+ read_poll_timeout(op, val, cond, timeout_us, addr)
#define readb_poll_timeout(addr, val, cond, timeout_us) \
readx_poll_timeout(readb, addr, val, cond, timeout_us)