diff options
-rw-r--r-- | Documentation/boards/stm32mp.rst | 1 | ||||
-rw-r--r-- | arch/arm/boards/Makefile | 1 | ||||
-rw-r--r-- | arch/arm/boards/lxa-mc1/Makefile | 2 | ||||
-rw-r--r-- | arch/arm/boards/lxa-mc1/board.c | 58 | ||||
-rw-r--r-- | arch/arm/boards/lxa-mc1/lowlevel.c | 26 | ||||
-rw-r--r-- | arch/arm/configs/stm32mp_defconfig | 28 | ||||
-rw-r--r-- | arch/arm/dts/Makefile | 1 | ||||
-rw-r--r-- | arch/arm/dts/at91-sama5d27_giantboard.dts | 2 | ||||
-rw-r--r-- | arch/arm/dts/at91-sama5d27_som1_ek.dts | 1 | ||||
-rw-r--r-- | arch/arm/dts/sama5d2.dtsi | 8 | ||||
-rw-r--r-- | arch/arm/dts/stm32mp157c-lxa-mc1.dts | 44 | ||||
-rw-r--r-- | arch/arm/dts/stm32mp157c-lxa-mc1.dtsi | 362 | ||||
-rw-r--r-- | arch/arm/dts/stm32mp15xx-osd32.dtsi | 229 | ||||
-rw-r--r-- | arch/arm/mach-stm32mp/Kconfig | 4 | ||||
-rw-r--r-- | drivers/mci/Kconfig | 8 | ||||
-rw-r--r-- | drivers/mci/Makefile | 1 | ||||
-rw-r--r-- | drivers/mci/atmel-sdhci-common.c | 407 | ||||
-rw-r--r-- | drivers/mci/atmel-sdhci.c | 169 | ||||
-rw-r--r-- | drivers/mci/atmel-sdhci.h | 28 | ||||
-rw-r--r-- | drivers/mci/sdhci.c | 11 | ||||
-rw-r--r-- | drivers/mci/sdhci.h | 29 | ||||
-rw-r--r-- | images/Makefile.stm32mp | 5 | ||||
-rw-r--r-- | include/linux/iopoll.h | 29 |
23 files changed, 1444 insertions, 10 deletions
diff --git a/Documentation/boards/stm32mp.rst b/Documentation/boards/stm32mp.rst index de793ab3c9..607c59fd07 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 9fe458e0a3..e9e9163d58 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 0000000000..092c31d6b2 --- /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 0000000000..7f1f3ccd7e --- /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 0000000000..274f824a16 --- /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 f69f9f966a..4b902e1741 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 ddfe64e83b..1aeaa61e01 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 940379e430..7e48fa18ae 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 936f07eac4..cd038dc7c1 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 e69de29bb2..51e964fc0f 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 0000000000..57baaf4005 --- /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 0000000000..6603cf280e --- /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 = ðernet0; + 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 = <®_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 = <®_3v3>; + + port { + panel_input: endpoint { + remote-endpoint = <<dc_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; + }; +}; + +ðernet0 { + pinctrl-names = "default"; + pinctrl-0 = <ðernet0_rgmii_mc1pins_b>; + phy-mode = "rgmii-id"; + phy-handle = <ðphy>; + 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 = <®_3v3>; + reg = <0x38>; + reset-gpios = <&gpiof 8 GPIO_ACTIVE_LOW>; /* TOUCH_RESET# */ + touchscreen-size-x = <1792>; + touchscreen-size-y = <1024>; + wakeup-source; + }; +}; + +<dc { + pinctrl-names = "default"; + pinctrl-0 = <<dc_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 = <®_5v2>; /* VIN */ + ldo2-supply = <®_5v2>; /* PMIC_LDO25IN */ + ldo5-supply = <®_5v2>; /* PMIC_LDO25IN */ + boost-supply = <®_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 = <®_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 = <®_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 0000000000..8750835033 --- /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 = <®_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 9b55a3d218..6e816ef9d1 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 73da291300..60c60ceaa5 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 54eb65978e..177483dcfb 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 0000000000..680b1980c0 --- /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 0000000000..6351186476 --- /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 0000000000..897ed4e4de --- /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 172c8343a1..dba26b2665 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 a307dc97cd..7b3f64486f 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 910e029a5b..38872c9525 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 66c8f652ca..8bf912e173 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) |