diff --git a/boards/arm64/rpi_5/Kconfig.board b/boards/arm64/rpi_5/Kconfig.board new file mode 100644 index 00000000000000..2a60534b4c8d5c --- /dev/null +++ b/boards/arm64/rpi_5/Kconfig.board @@ -0,0 +1,6 @@ +# Copyright 2024 Junho Lee +# SPDX-License-Identifier: Apache-2.0 + +config BOARD_RPI_5 + bool "Broadcom BCM2712" + depends on SOC_BCM2712 diff --git a/boards/arm64/rpi_5/Kconfig.defconfig b/boards/arm64/rpi_5/Kconfig.defconfig new file mode 100644 index 00000000000000..a555a718911a3c --- /dev/null +++ b/boards/arm64/rpi_5/Kconfig.defconfig @@ -0,0 +1,6 @@ +# Copyright 2023 honglin leng +# SPDX-License-Identifier: Apache-2.0 + +config BOARD + default "Raspberry Pi 5" + depends on BOARD_RPI_5 diff --git a/boards/arm64/rpi_5/board.cmake b/boards/arm64/rpi_5/board.cmake new file mode 100644 index 00000000000000..9881313609aae2 --- /dev/null +++ b/boards/arm64/rpi_5/board.cmake @@ -0,0 +1 @@ +# SPDX-License-Identifier: Apache-2.0 diff --git a/boards/arm64/rpi_5/board.yml b/boards/arm64/rpi_5/board.yml new file mode 100644 index 00000000000000..d604b7f4e5130f --- /dev/null +++ b/boards/arm64/rpi_5/board.yml @@ -0,0 +1,5 @@ +board: + name: rpi_5 + vendor: raspberrypi + socs: + - name: bcm2712 diff --git a/boards/arm64/rpi_5/doc/index.rst b/boards/arm64/rpi_5/doc/index.rst new file mode 100644 index 00000000000000..99bf51879d6fad --- /dev/null +++ b/boards/arm64/rpi_5/doc/index.rst @@ -0,0 +1,193 @@ +.. rpi_5: + +Raspberry Pi 5 (Cortex-A76) +########################### + +Overview +******** + +`Raspberry Pi 5 product-brief`_ + +Hardware +******** + +- Broadcom BCM2712 2.4GHz quad-core 64-bit Arm Cortex-A76 CPU, with cryptography extensions, 512KB per-core L2 caches and a 2MB shared L3 cache +- VideoCore VII GPU, supporting OpenGL ES 3.1, Vulkan 1.2 +- Dual 4Kp60 HDMI® display output with HDR support +- 4Kp60 HEVC decoder +- LPDDR4X-4267 SDRAM (4GB and 8GB SKUs available at launch) +- Dual-band 802.11ac Wi-Fi® +- Bluetooth 5.0 / Bluetooth Low Energy (BLE) +- microSD card slot, with support for high-speed SDR104 mode +- 2 x USB 3.0 ports, supporting simultaneous 5Gbps operation +- 2 x USB 2.0 ports +- Gigabit Ethernet, with PoE+ support (requires separate PoE+ HAT) +- 2 x 4-lane MIPI camera/display transceivers +- PCIe 2.0 x1 interface for fast peripherals (requires separate M.2 HAT or other adapter) +- 5V/5A DC power via USB-C, with Power Delivery support +- Raspberry Pi standard 40-pin header +- Real-time clock (RTC), powered from external battery +- Power button + +Supported Features +================== + +The Raspberry Pi 5 board configuration supports the following hardware features: + +.. list-table:: + :header-rows: 1 + + * - Peripheral + - Kconfig option + - Devicetree compatible + * - GIC-400 + - N/A + - :dtcompatible:`arm,gic-v2` + * - GPIO + - :kconfig:option:`CONFIG_GPIO` + - :dtcompatible:`brcm,brcmstb-gpio` + * - UART + - :kconfig:option:`CONFIG_SERIAL` + - :dtcompatible:`arm,pl011` + +Not all hardware features are supported yet. See `Raspberry Pi hardware`_ for the complete list of hardware features. + +The default configuration can be found in +:zephyr_file:`boards/raspberrypi/rpi_5/rpi_5_defconfig`. + +Programming and Debugging +************************* + +Blinky +====== + +In brief, + 1. Format your Micro SD card with MBR and FAT32. + 2. Save three files below in the root directory. + * config.txt + * zephyr.bin + * `bcm2712-rpi-5.dtb`_ + 3. Insert the Micro SD card and power on the Raspberry Pi 5. + +then, You will see the Raspberry Pi 5 running the `zephyr.bin`. + +config.txt +---------- + +.. code-block:: text + + kernel=zephyr.bin + arm_64bit=1 + + +zephyr.bin +---------- + +Build an app `samples/basic/blinky` + +.. zephyr-app-commands:: + :zephyr-app: samples/basic/blinky + :board: rpi_5 + :goals: build + +Copy `zephyr.bin` from `build/zephyr` directory to the root directory of the Micro SD card. + +Insert the Micro SD card and power on the Raspberry Pi 5. And then, the STAT LED will start to blink. + + +Serial Communication +==================== + +wiring +------ + +You will need the following items: + * `Raspberry Pi Debug Probe`_ + * JST cable: 3-pin JST connector to 3-pin JST connector cable + * USB cable: USB A male - Micro USB B male + +Use the JST cable to connect the Raspberry Pi Debug Probe UART port to the Raspberry Pi 5 UART port between the HDMI ports. + +Then connect the Raspberry Pi Debug Probe to your computer with a USB cable. + + +config.txt +---------- + +.. code-block:: text + + kernel=zephyr.bin + arm_64bit=1 + enable_uart=1 + uart_2ndstage=1 + + +zephyr.bin +---------- + +Build an app `samples/hello_world` + +.. zephyr-app-commands:: + :zephyr-app: samples/hello_world + :board: rpi_5 + :goals: build + +Copy `zephyr.bin` from `build/zephyr` directory to the root directory of the Micro SD card. + +Insert the Micro SD card into your Raspberry Pi 5. + + +serial terminal emulator +------------------------ + +When you power on the Raspberry Pi 5, you will see the following output in the serial console: + +.. code-block:: text + + *** Booting Zephyr OS build XXXXXXXXXXXX *** + Hello World! rpi_5/bcm2712 + + +.. _Raspberry Pi 5 product-brief: + https://datasheets.raspberrypi.com/rpi5/raspberry-pi-5-product-brief.pdf + +.. _Raspberry Pi hardware: + https://www.raspberrypi.com/documentation/computers/raspberry-pi.html + +.. _bcm2712-rpi-5.dtb: + https://github.com/raspberrypi/firmware/raw/master/boot/bcm2712-rpi-5-b.dtb + +.. _Raspberry Pi Debug Probe: + https://www.raspberrypi.com/products/debug-probe/ + +XEN Dom0 +======== + +The Raspberry Pi 5 platform can be used to run as Xen Zephyr Dom0. For such purposes the `xen_dom0` +snippet can be used. + +Run below command as an example of RPI 5 Zephyr build as Dom0: + +.. code-block:: bash + + west build -b rpi_5 -p always -S xen_dom0 samples/hello_world + +It is expected to be used with special application performing Xen Domain-0/Dom0 functions. + +.. note:: + + The "hypervisor@x" and "memory@x" DT nodes may need to be updated depending on the Xen boot, + because normaly Xan will update DT for the target Kernel, but this is not possible in case + of Zephyr. See comments in `rpi_5_xen_dom0.dts`. + +XEN DomD with HW passthrough +============================ + +The Raspberry Pi 5 platform can be used to run as Xen Zephyr DomD with RPI 5 HW support. +For such purposes the `rpi_5_xen_domd` snippet can be used. + +Run the command below as an example of RPI 5 Zephyr build as DomD: + +.. code-block:: bash + + west build -b xenvm -S rpi_5_xen_domd samples/basic/blinky diff --git a/boards/arm64/rpi_5/rpi_5.dts b/boards/arm64/rpi_5/rpi_5.dts new file mode 100644 index 00000000000000..933d5cf5136121 --- /dev/null +++ b/boards/arm64/rpi_5/rpi_5.dts @@ -0,0 +1,89 @@ +/* + * Copyright 2024 Junho Lee + * Copyright (c) 2024 EPAM Systems + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/dts-v1/; + +#include +#include + +/ { + compatible = "raspberrypi,5-model-b", "brcm,bcm2712"; + model = "Raspberry Pi 5"; + #address-cells = <2>; + #size-cells = <1>; + + aliases { + led0 = &led_act; + sdhc0 = &sdio1; + }; + + chosen { + zephyr,sram = &sram0; + zephyr,console = &uart10; + zephyr,shell-uart = &uart10; + }; + + leds { + compatible = "gpio-leds"; + + led_act: led-act { + gpios = <&gio_aon 9 GPIO_ACTIVE_LOW>; + label = "ACT"; + }; + }; + + sd_io_1v8_reg: sd_io_1v8_reg { + compatible = "regulator-gpio"; + regulator-name = "vdd-sd-io"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <3300000>; + regulator-boot-on; + regulator-always-on; + gpios = <&gio_aon 3 GPIO_ACTIVE_HIGH>; + states = <1800000 0x1 + 3300000 0x0>; + status = "okay"; + }; + + sd_vcc_reg: sd_vcc_reg { + compatible = "regulator-fixed"; + regulator-name = "vcc-sd"; + regulator-boot-on; + enable-gpios = <&gio_aon 4 GPIO_ACTIVE_HIGH>; + status = "okay"; + }; +}; + +/* samples required more memory and RPI5 has it, set it to 4M */ +&sram0 { + reg = <0x0 0x200000 0x400000>; +}; + +&gio_aon { + status = "okay"; +}; + +&uart10 { + status = "okay"; + current-speed = <115200>; +}; + +/* SDIO1 is used to drive removable the SD card */ +&sdio1 { + vqmmc-supply = <&sd_io_1v8_reg>; + vmmc-supply = <&sd_vcc_reg>; + bus-width = <4>; + cd-gpios = <&gio_aon 5 GPIO_ACTIVE_LOW>; + /* request freq from hw */ + max-bus-freq = <0>; + status = "okay"; + + disk { + compatible = "zephyr,sdmmc-disk"; + status = "okay"; + }; +}; diff --git a/boards/arm64/rpi_5/rpi_5.yaml b/boards/arm64/rpi_5/rpi_5.yaml new file mode 100644 index 00000000000000..e293d78e9cd1fb --- /dev/null +++ b/boards/arm64/rpi_5/rpi_5.yaml @@ -0,0 +1,7 @@ +identifier: rpi_5 +name: Raspberry Pi 5 +type: mcu +arch: arm64 +toolchain: + - zephyr + - cross-compile diff --git a/boards/arm64/rpi_5/rpi_5_defconfig b/boards/arm64/rpi_5/rpi_5_defconfig new file mode 100644 index 00000000000000..d3b777c339e5d0 --- /dev/null +++ b/boards/arm64/rpi_5/rpi_5_defconfig @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: Apache-2.0 + +CONFIG_SOC_BCM2712=y +CONFIG_BOARD_RPI_5=y +CONFIG_ARM64_VA_BITS_40=y +CONFIG_ARM64_PA_BITS_40=y +CONFIG_TIMER_READS_ITS_FREQUENCY_AT_RUNTIME=y + +# Enable serial console. +CONFIG_SERIAL=y +CONFIG_CONSOLE=y +CONFIG_UART_CONSOLE=y diff --git a/drivers/gpio/CMakeLists.txt b/drivers/gpio/CMakeLists.txt index 3e4ae12dc4ad1b..d4fd0730e64f81 100644 --- a/drivers/gpio/CMakeLists.txt +++ b/drivers/gpio/CMakeLists.txt @@ -91,6 +91,7 @@ zephyr_library_sources_ifdef(CONFIG_GPIO_BCM2711 gpio_bcm2711.c) zephyr_library_sources_ifdef(CONFIG_GPIO_RENESAS_RA gpio_renesas_ra.c) zephyr_library_sources_ifdef(CONFIG_GPIO_RZT2M gpio_rzt2m.c) zephyr_library_sources_ifdef(CONFIG_GPIO_AMBIQ gpio_ambiq.c) +zephyr_library_sources_ifdef(CONFIG_GPIO_BRCMSTB gpio_brcmstb.c) if (CONFIG_GPIO_EMUL_SDL) zephyr_library_sources(gpio_emul_sdl.c) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index ff9d02704d84cc..dfe46a315816f5 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -240,4 +240,6 @@ source "drivers/gpio/Kconfig.rzt2m" source "drivers/gpio/Kconfig.ambiq" +source "drivers/gpio/Kconfig.brcmstb" + endif # GPIO diff --git a/drivers/gpio/Kconfig.brcmstb b/drivers/gpio/Kconfig.brcmstb new file mode 100644 index 00000000000000..0974f05239e6c2 --- /dev/null +++ b/drivers/gpio/Kconfig.brcmstb @@ -0,0 +1,9 @@ +# Copyright (c) 2024 Junho Lee +# SPDX-License-Identifier: Apache-2.0 + +config GPIO_BRCMSTB + bool "Broadcom Set-top box SoC GPIO Driver" + default y + depends on DT_HAS_BRCM_BRCMSTB_GPIO_ENABLED + help + Enable Driver for Broadcom Set-top box SoC GPIO Banks. diff --git a/drivers/gpio/gpio_brcmstb.c b/drivers/gpio/gpio_brcmstb.c new file mode 100644 index 00000000000000..cfec6868f290f7 --- /dev/null +++ b/drivers/gpio/gpio_brcmstb.c @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2024 Junho Lee + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT brcm_brcmstb_gpio + +#include +#include +#include +#include +#include + +#define GIO_DATA 0x04 +#define GIO_IODIR 0x08 + +#define DEV_CFG(dev) ((const struct gpio_brcmstb_config *)(dev)->config) +#define DEV_DATA(dev) ((struct gpio_brcmstb_data *)(dev)->data) + +struct gpio_brcmstb_config { + struct gpio_driver_config common; + + DEVICE_MMIO_NAMED_ROM(reg_base); + mem_addr_t offset; +}; + +struct gpio_brcmstb_data { + struct gpio_driver_data common; + + DEVICE_MMIO_NAMED_RAM(reg_base); + mem_addr_t base; +}; + +static int gpio_brcmstb_pin_configure(const struct device *port, gpio_pin_t pin, gpio_flags_t flags) +{ + struct gpio_brcmstb_data *data = port->data; + + if (flags & (GPIO_SINGLE_ENDED | GPIO_PULL_UP | GPIO_PULL_DOWN)) { + return -ENOTSUP; + } + + if (flags & GPIO_INPUT) { + sys_set_bit(data->base + GIO_IODIR, pin); + } else if (flags & GPIO_OUTPUT) { + sys_clear_bit(data->base + GIO_IODIR, pin); + + if (flags & GPIO_OUTPUT_INIT_HIGH) { + sys_set_bit(data->base + GIO_DATA, pin); + } else if (flags & GPIO_OUTPUT_INIT_LOW) { + sys_clear_bit(data->base + GIO_DATA, pin); + } + } + + return 0; +} + +static int gpio_brcmstb_port_get_raw(const struct device *port, gpio_port_value_t *value) +{ + struct gpio_brcmstb_data *data = port->data; + + *value = sys_read32(data->base + GIO_DATA); + + return 0; +} + +static int gpio_brcmstb_port_set_masked_raw(const struct device *port, gpio_port_pins_t mask, + gpio_port_value_t value) +{ + struct gpio_brcmstb_data *data = port->data; + + sys_clear_bits(data->base + GIO_DATA, mask); + sys_set_bits(data->base + GIO_DATA, (value & mask)); + + return 0; +} + +static int gpio_brcmstb_port_set_bits_raw(const struct device *port, gpio_port_pins_t pins) +{ + struct gpio_brcmstb_data *data = port->data; + + sys_set_bits(data->base + GIO_DATA, pins); + + return 0; +} + +static int gpio_brcmstb_port_clear_bits_raw(const struct device *port, gpio_port_pins_t pins) +{ + struct gpio_brcmstb_data *data = port->data; + + sys_clear_bits(data->base + GIO_DATA, pins); + + return 0; +} + +static int gpio_brcmstb_port_toggle_bits(const struct device *port, gpio_port_pins_t pins) +{ + struct gpio_brcmstb_data *data = port->data; + uint32_t reg_data; + + reg_data = sys_read32(data->base + GIO_DATA); + reg_data ^= pins; + sys_write32(reg_data, data->base + GIO_DATA); + + return 0; +} + +static const struct gpio_driver_api gpio_brcmstb_api = { + .pin_configure = gpio_brcmstb_pin_configure, + .port_get_raw = gpio_brcmstb_port_get_raw, + .port_set_masked_raw = gpio_brcmstb_port_set_masked_raw, + .port_set_bits_raw = gpio_brcmstb_port_set_bits_raw, + .port_clear_bits_raw = gpio_brcmstb_port_clear_bits_raw, + .port_toggle_bits = gpio_brcmstb_port_toggle_bits, +}; + +int gpio_brcmstb_init(const struct device *port) +{ + const struct gpio_brcmstb_config *config = port->config; + struct gpio_brcmstb_data *data = port->data; + + DEVICE_MMIO_NAMED_MAP(port, reg_base, K_MEM_CACHE_NONE); + data->base = DEVICE_MMIO_NAMED_GET(port, reg_base) + config->offset; + + return 0; +} + +#define GPIO_BRCMSTB_INIT(n) \ + static struct gpio_brcmstb_data gpio_brcmstb_data_##n; \ + \ + static const struct gpio_brcmstb_config gpio_brcmstb_cfg_##n = { \ + .common = {.port_pin_mask = GPIO_PORT_PIN_MASK_FROM_DT_INST(0)}, \ + DEVICE_MMIO_NAMED_ROM_INIT(reg_base, DT_INST_PARENT(n)), \ + .offset = DT_INST_REG_ADDR(n), \ + }; \ + \ + DEVICE_DT_INST_DEFINE(n, gpio_brcmstb_init, NULL, &gpio_brcmstb_data_##n, \ + &gpio_brcmstb_cfg_##n, PRE_KERNEL_1, CONFIG_GPIO_INIT_PRIORITY, \ + &gpio_brcmstb_api); + +DT_INST_FOREACH_STATUS_OKAY(GPIO_BRCMSTB_INIT) diff --git a/drivers/sdhc/CMakeLists.txt b/drivers/sdhc/CMakeLists.txt index 8fc18d661cb697..9becf15032f055 100644 --- a/drivers/sdhc/CMakeLists.txt +++ b/drivers/sdhc/CMakeLists.txt @@ -11,4 +11,6 @@ zephyr_library_sources_ifdef(CONFIG_SAM_HSMCI sam_hsmci.c) zephyr_library_sources_ifdef(CONFIG_INTEL_EMMC_HOST intel_emmc_host.c) zephyr_library_sources_ifdef(CONFIG_SDHC_INFINEON_CAT1 ifx_cat1_sdio.c) zephyr_library_sources_ifdef(CONFIG_CDNS_SDHC sdhc_cdns_ll.c sdhc_cdns.c) +zephyr_library_sources_ifdef(CONFIG_SDHCI_GENERIC sdhci_common.c) +zephyr_library_sources_ifdef(CONFIG_BRCM_BCM2712_SDHCI brcm_bcm2712_sdhci.c) endif() diff --git a/drivers/sdhc/Kconfig b/drivers/sdhc/Kconfig index 12d6ee22fc43f6..53d248fc2ef702 100644 --- a/drivers/sdhc/Kconfig +++ b/drivers/sdhc/Kconfig @@ -16,6 +16,8 @@ source "drivers/sdhc/Kconfig.rcar" source "drivers/sdhc/Kconfig.sam_hsmci" source "drivers/sdhc/Kconfig.intel" source "drivers/sdhc/Kconfig.sdhc_cdns" +source "drivers/sdhc/Kconfig.sdhc" +source "drivers/sdhc/Kconfig.brcm" config SDHC_INIT_PRIORITY int "SDHC driver init priority" diff --git a/drivers/sdhc/Kconfig.brcm b/drivers/sdhc/Kconfig.brcm new file mode 100644 index 00000000000000..ac6b6aca2d97bb --- /dev/null +++ b/drivers/sdhc/Kconfig.brcm @@ -0,0 +1,49 @@ +# Copyright (c) 2024 EPAM Systems +# SPDX-License-Identifier: Apache-2.0 + +config BRCM_BCM2712_SDHCI + bool "BCM2712 SDHC (SD/MMC)" + default y + depends on DT_HAS_BRCM_BCM2712_SDHCI_ENABLED + select SDHCI_GENERIC + select REGULATOR + select GPIO + select CACHE_MANAGEMENT + select SDHC_SUPPORTS_UHS if SDMMC_STACK + help + BCM2712 SDHC v3.0 capable with SD interface support. + +if BRCM_BCM2712_SDHCI + +# SDHC DMA needs 64 bit aligned buffers +config SDHC_BUFFER_ALIGNMENT + default 64 + +choice BRCM_BCM2712_XFER_MODE + bool "Select BCM2712 SDHC (SD/MMC) data transfer mode" + default BRCM_BCM2712_XFER_ADMA2 + +config BRCM_BCM2712_XFER_PIO + bool "BCM2712 SDHC PIO data transfer mode" + +config BRCM_BCM2712_XFER_SDMA + bool "BCM2712 SDHC SDMA data transfer mode" + +config BRCM_BCM2712_XFER_ADMA2 + bool "BCM2712 SDHC ADMA2 data transfer mode" + +endchoice + +config BRCM_BCM2712_ADMA2_DESCS_NUM + int "BCM2712 SDHC ADMA number of descriptors" + default 16 if BRCM_BCM2712_XFER_ADMA2 + default 0 + +config BRCM_BCM2712_SDHC_USE_IRQ + bool "BCM2712 SDHC enable IRQ support" + select EVENTS + default y + help + BCM2712 SDHC enable IRQ support for cmd/data transfers. + +endif # BRCM_BCM2712_SDHCI diff --git a/drivers/sdhc/Kconfig.sdhc b/drivers/sdhc/Kconfig.sdhc new file mode 100644 index 00000000000000..c2aa996c09eeda --- /dev/null +++ b/drivers/sdhc/Kconfig.sdhc @@ -0,0 +1,18 @@ +# Copyright (c) 2024 EPAM Systems +# SPDX-License-Identifier: Apache-2.0 + +config SDHCI_GENERIC + bool + select SDHC_SUPPORTS_NATIVE_MODE + help + SD Host Controller (SDHC) generic implementation module according to + SD Host Controller Standard Specification (Version 3.0). + +config SDHCI_GENERIC_ADMA2_DESC_MAX_LEN + int "ADMA2 max xfer size per descriptor" + default 65532 + range 1 65536 + help + Defines ADMA2 max xfer size per descriptor. The default value selected by + taking into account that some SDHC HW implemtations may not handle + properly descriptor "Length Field" == 0 case (65536 bytes xfer). diff --git a/drivers/sdhc/brcm_bcm2712_sdhci.c b/drivers/sdhc/brcm_bcm2712_sdhci.c new file mode 100644 index 00000000000000..b33ea18e58fbc7 --- /dev/null +++ b/drivers/sdhc/brcm_bcm2712_sdhci.c @@ -0,0 +1,529 @@ +/* + * Copyright (c) 2024 EPAM Systems + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT brcm_bcm2712_sdhci + +#include +#include +#include +#include +#include +#include +#include + +#include "sdhci_common.h" + +LOG_MODULE_REGISTER(brcm_bcm2712_sdhci, CONFIG_SDHC_LOG_LEVEL); + +#define LOG_DEV_ERR(dev, format, ...) LOG_ERR("%s:" #format, (dev)->name, ##__VA_ARGS__) +#define LOG_DEV_WRN(dev, format, ...) LOG_WRN("%s:" #format, (dev)->name, ##__VA_ARGS__) +#define LOG_DEV_INF(dev, format, ...) LOG_INF("%s:" #format, (dev)->name, ##__VA_ARGS__) +#define LOG_DEV_DBG(dev, format, ...) LOG_DBG("%s:" #format, (dev)->name, ##__VA_ARGS__) + +#define DEV_CFG(_dev) ((const struct bcm2712_sdhci_config *)(_dev)->config) +#define DEV_DATA(_dev) ((struct bcm2712_sdhci_data *const)(_dev)->data) + +#define SDIO_CFG_CTRL 0x0 +#define SDIO_CFG_CTRL_SDCD_N_TEST_EN BIT(31) +#define SDIO_CFG_CTRL_SDCD_N_TEST_LEV BIT(30) + +#define SDIO_CFG_SD_PIN_SEL 0x44 +#define SDIO_CFG_SD_PIN_SEL_MASK 0x3 +#define SDIO_CFG_SD_PIN_SEL_CARD BIT(1) + +#define SDIO_CFG_CQ_CAPABILITY 0x4c +#define SDIO_CFG_CQ_CAPABILITY_FMUL GENMASK(13, 12) + +#define SDIO_CFG_MAX_50MHZ_MODE 0x1ac +#define SDIO_CFG_MAX_50MHZ_MODE_STRAP_OVERRIDE BIT(31) +#define SDIO_CFG_MAX_50MHZ_MODE_ENABLE BIT(0) + +struct bcm2712_sdhci_config { + DEVICE_MMIO_NAMED_ROM(host); + DEVICE_MMIO_NAMED_ROM(cfg); + + const struct gpio_dt_spec gpio_cd; + const struct device *regulator_vqmmc; + const struct device *regulator_vmmc; + uint32_t clk_freq; + void (*irq_config)(const struct device *dev); + + struct sdhc_adma_desc *adma_descs; + + uint32_t max_bus_freq; + uint32_t min_bus_freq; + uint32_t power_delay_ms; + uint8_t bw_4bit: 1; + uint8_t bw_8bit: 1; + uint8_t non_removable: 1; +}; + +struct bcm2712_sdhci_data { + DEVICE_MMIO_NAMED_RAM(host); + DEVICE_MMIO_NAMED_RAM(cfg); + + struct sdhc_io host_io; + struct sdhc_host_props props; + + struct sdhci_common sdhci_ctx; +}; + +static int bcm2712_sdhci_request(const struct device *dev, struct sdhc_command *cmd, + struct sdhc_data *sd_data) +{ + struct bcm2712_sdhci_data *data = dev->data; + unsigned int retry = cmd->retries; + int ret; + + do { + ret = sdhci_send_req(&data->sdhci_ctx, cmd, sd_data); + if (ret) { + LOG_DEV_ERR(dev, "sd cmd request failed (%d)", ret); + } + } while (ret != 0 && retry--); + return ret; +} + +static int bcm2712_sdhci_get_card_present(const struct device *dev) +{ + const struct bcm2712_sdhci_config *cfg = dev->config; + struct bcm2712_sdhci_data *data = dev->data; + + int cd = 0; + + if (cfg->non_removable) { + cd = 1; + goto out_cd; + } + + if (cfg->gpio_cd.port) { + cd = gpio_pin_get_dt(&cfg->gpio_cd) > 0 ? 1 : cd; + LOG_DEV_DBG(dev, "sd detect gpio %d", cd); + goto out_cd; + } + + cd = sdhci_get_cd(&data->sdhci_ctx); + +out_cd: + LOG_DEV_DBG(dev, "sd detect sdhci %d", cd); + + if (!cd) { + LOG_DEV_INF(dev, "sd card is not detected"); + } + return cd; +} + +static int bcm2712_sdhci_card_busy(const struct device *dev) +{ + struct bcm2712_sdhci_data *data = dev->data; + + return sdhci_is_card_busy(&data->sdhci_ctx); +} + +static int bcm2712_sdhci_set_io(const struct device *dev, struct sdhc_io *ios) +{ + const struct bcm2712_sdhci_config *cfg = dev->config; + struct bcm2712_sdhci_data *data = dev->data; + struct sdhc_io *host_io = &data->host_io; + int ret; + + LOG_DEV_DBG(dev, + "set_io: bus_width:%d clock:%dHz power:%d timing:%d, voltage:%d drv:%d", + ios->bus_width, ios->clock, ios->power_mode, + ios->timing, ios->signal_voltage, ios->driver_type); + + if (host_io->clock != ios->clock) { + + if (ios->clock && + (ios->clock > data->props.f_max || ios->clock < data->props.f_min)) { + LOG_DEV_ERR(dev, "set_io: invalid clock:%d valid range %d - %d Hz", + ios->clock, data->props.f_min, data->props.f_max); + return -EINVAL; + } + + ret = sdhci_set_clock(&data->sdhci_ctx, ios->clock); + if (ret) { + LOG_DEV_ERR(dev, "set_io: set clock failed (%d)", ret); + return ret; + } + host_io->clock = ios->clock; + } + + if (host_io->bus_width != ios->bus_width) { + ret = sdhci_set_bus_width(&data->sdhci_ctx, ios->bus_width); + if (ret) { + LOG_DEV_ERR(dev, "set_io: set bus width failed (%d)", ret); + return ret; + } + host_io->bus_width = ios->bus_width; + } + + if (host_io->power_mode != ios->power_mode) { + if (ios->power_mode == SDHC_POWER_ON) { + regulator_enable(cfg->regulator_vmmc); + + k_msleep(cfg->power_delay_ms); + + sdhci_set_bus_power(&data->sdhci_ctx, SD_VOL_3_3_V); + + regulator_enable(cfg->regulator_vqmmc); + } else if (ios->power_mode == SDHC_POWER_OFF) { + sdhci_set_bus_power(&data->sdhci_ctx, 0); + + if (regulator_is_enabled(cfg->regulator_vqmmc)) { + regulator_disable(cfg->regulator_vqmmc); + } + if (regulator_is_enabled(cfg->regulator_vmmc)) { + regulator_disable(cfg->regulator_vmmc); + } + } else { + return -EINVAL; + } + host_io->power_mode = ios->power_mode; + } + + if (host_io->signal_voltage != ios->signal_voltage) { + if (ios->signal_voltage == SD_VOL_3_3_V) { + ret = regulator_set_voltage(cfg->regulator_vqmmc, 3300000, 3300000); + } else if (ios->signal_voltage == SD_VOL_1_8_V) { + ret = regulator_set_voltage(cfg->regulator_vqmmc, 1800000, 1800000); + } else { + return -EINVAL; + } + + ret = sdhci_set_voltage(&data->sdhci_ctx, ios->signal_voltage); + if (ret) { + LOG_DEV_ERR(dev, "set_io: set signal voltage failed (%d)", ret); + return ret; + } + host_io->signal_voltage = ios->signal_voltage; + } + + if (host_io->timing != ios->timing) { + ret = sdhci_set_uhs_timing(&data->sdhci_ctx, ios->timing); + if (ret) { + LOG_DEV_ERR(dev, "set_io: set uhs timings failed (%d)", ret); + return ret; + } + host_io->timing = ios->timing; + } + + return 0; +} + +static int bcm2712_sdhci_get_host_props(const struct device *dev, struct sdhc_host_props *props) +{ + struct bcm2712_sdhci_data *data = dev->data; + + memcpy(props, &data->props, sizeof(*props)); + return 0; +} + +static int bcm2712_sdhci_reset(const struct device *dev) +{ + struct bcm2712_sdhci_data *data = dev->data; + + return sdhci_reset(&data->sdhci_ctx, SDHCI_RESET_ALL); +} + +static int bcm2712_execute_tuning(const struct device *dev) +{ + struct bcm2712_sdhci_data *data = dev->data; + struct sdhc_io *host_io = &data->host_io; + + LOG_DEV_DBG(dev, "sdhc: tuning starting..."); + + switch (host_io->timing) { + case SDHC_TIMING_SDR50: + if (!data->props.host_caps.sdr50_needs_tuning) { + return 0; + } + __fallthrough; + case SDHC_TIMING_SDR104: + /* + * TODO: add tuning: + * set SDHCI_HOST_CONTROL2.SDHCI_CTRL_EXEC_TUNING + * send CMD19 + * check for !SDHCI_HOST_CONTROL2.SDHCI_CTRL_EXEC_TUNING + */ + return -ENOSYS; + case SDHC_TIMING_DDR50: + /* TODO: it's not clear from SDHC standard if DDR50 need tuning, + * so bypass it. + */ + __fallthrough; + default: + /* no tuning for other timings */ + return 0; + } + + LOG_DEV_DBG(dev, "sdhc: tuning completed"); + return 0; +} + +static const struct sdhc_driver_api bcm2712_sdhci_api = { + .request = bcm2712_sdhci_request, + .set_io = bcm2712_sdhci_set_io, + .get_host_props = bcm2712_sdhci_get_host_props, + .get_card_present = bcm2712_sdhci_get_card_present, + .reset = bcm2712_sdhci_reset, + .card_busy = bcm2712_sdhci_card_busy, + .execute_tuning = bcm2712_execute_tuning, +}; + +static int bcm2712_sdhci_init_host_props(const struct device *dev) +{ + const struct bcm2712_sdhci_config *cfg = dev->config; + struct bcm2712_sdhci_data *data = dev->data; + struct sdhc_host_props *props = &data->props; + struct sdhc_host_caps *host_caps = &props->host_caps; + struct sdhci_common *sdhci_ctx = &data->sdhci_ctx; + bool prop_sup; + int ret = 0; + + props->f_max = cfg->max_bus_freq; + props->f_min = cfg->min_bus_freq; + + props->is_spi = 0; + props->power_delay = cfg->power_delay_ms; + host_caps->bus_4_bit_support = cfg->bw_4bit; + + sdhci_init_caps(sdhci_ctx, props); + + prop_sup = regulator_is_supported_voltage(cfg->regulator_vqmmc, 3300000, 3300000); + if (host_caps->vol_330_support && !prop_sup) { + LOG_DEV_INF(dev, "vdd vol_330 not supported"); + host_caps->vol_330_support = 0; + } + + prop_sup = regulator_is_supported_voltage(cfg->regulator_vqmmc, 3000000, 3000000); + if (host_caps->vol_300_support && !prop_sup) { + LOG_DEV_INF(dev, "vdd vol_300 not supported"); + host_caps->vol_300_support = 0; + } + + prop_sup = regulator_is_supported_voltage(cfg->regulator_vqmmc, 1800000, 1800000); + if (host_caps->vol_180_support && !prop_sup) { + host_caps->vol_180_support = 0; + host_caps->sdr50_support = 0; + host_caps->sdr104_support = 0; + host_caps->ddr50_support = 0; + LOG_DEV_INF(dev, "vdd vol_180 is not supported, drop sdr104, sdr50, ddr50 support"); + } + + if (host_caps->bus_8_bit_support && !cfg->bw_8bit) { + LOG_DEV_INF(dev, "bus_8_bit_support not supported"); + host_caps->bus_8_bit_support = 0; + } + + if (!sdhci_ctx->max_clk) { + goto use_dt_freq; + } + + if (!props->f_max || props->f_max > sdhci_ctx->max_clk) { + LOG_DEV_INF(dev, "reset max bus frequency to %u from %u", sdhci_ctx->max_clk, + props->f_max); + props->f_max = sdhci_ctx->max_clk; + } + +use_dt_freq: + if (!props->f_max) { + LOG_DEV_ERR(dev, "undefined max bus frequency"); + return -EINVAL; + } + + sdhci_enable_auto_cmd12(sdhci_ctx, true); + + if (IS_ENABLED(CONFIG_BRCM_BCM2712_XFER_ADMA2) && host_caps->adma_2_support) { + ret = sdhci_enable_adma2(sdhci_ctx, cfg->adma_descs, + CONFIG_BRCM_BCM2712_ADMA2_DESCS_NUM); + if (ret) { + LOG_DEV_ERR(dev, "enable adma failed (%d)", ret); + } + LOG_DEV_ERR(dev, "enable adma"); + } else if (IS_ENABLED(CONFIG_BRCM_BCM2712_XFER_SDMA) && host_caps->sdma_support) { + ret = sdhci_enable_sdma(sdhci_ctx); + if (ret) { + LOG_DEV_ERR(dev, "enable sdma failed (%d)", ret); + } + LOG_DEV_ERR(dev, "enable sdma"); + } + + if (IS_ENABLED(CONFIG_BRCM_BCM2712_SDHC_USE_IRQ)) { + sdhc_enable_irq(sdhci_ctx, true); + } + + return ret; +} + +static int bcm2712_sdhci_init_hw(const struct device *dev) +{ + const struct bcm2712_sdhci_config *cfg = dev->config; + mm_reg_t cfg_base = DEVICE_MMIO_NAMED_GET(dev, cfg); + uint32_t reg, base_clk_mhz; + + if (cfg->non_removable) { + /* Force presence */ + reg = sys_read32(cfg_base + SDIO_CFG_CTRL); + reg &= ~SDIO_CFG_CTRL_SDCD_N_TEST_LEV; + reg |= SDIO_CFG_CTRL_SDCD_N_TEST_EN; + sys_write32(reg, cfg_base + SDIO_CFG_CTRL); + } else { + /* Enable card detection line */ + reg = sys_read32(cfg_base + SDIO_CFG_SD_PIN_SEL); + reg &= ~SDIO_CFG_SD_PIN_SEL_MASK; + reg |= SDIO_CFG_SD_PIN_SEL_CARD; + sys_write32(reg, cfg_base + SDIO_CFG_SD_PIN_SEL); + } + + reg = sys_read32(cfg_base + SDIO_CFG_MAX_50MHZ_MODE); + reg &= ~SDIO_CFG_MAX_50MHZ_MODE_ENABLE; + reg |= SDIO_CFG_MAX_50MHZ_MODE_STRAP_OVERRIDE; + sys_write32(reg, cfg_base + SDIO_CFG_MAX_50MHZ_MODE); + + /* Guesstimate the timer frequency (controller base clock) */ + base_clk_mhz = cfg->clk_freq / (1000 * 1000); + reg = SDIO_CFG_CQ_CAPABILITY_FMUL | base_clk_mhz; + sys_write32(reg, cfg_base + SDIO_CFG_CQ_CAPABILITY); + return 0; +} + +static int bcm2712_sdhci_init(const struct device *dev) +{ + const struct bcm2712_sdhci_config *cfg = DEV_CFG(dev); + struct bcm2712_sdhci_data *data = DEV_DATA(dev); + int ret; + + DEVICE_MMIO_NAMED_MAP(dev, host, K_MEM_CACHE_NONE); + DEVICE_MMIO_NAMED_MAP(dev, cfg, K_MEM_CACHE_NONE); + + data->sdhci_ctx.reg_base = DEVICE_MMIO_NAMED_GET(dev, host); + + if (cfg->gpio_cd.port) { + if (!device_is_ready(cfg->regulator_vmmc)) { + LOG_DEV_ERR(dev, "gpio is not ready"); + return -ENODEV; + } + + ret = gpio_pin_configure_dt(&cfg->gpio_cd, GPIO_INPUT); + if (ret) { + LOG_DEV_ERR(dev, "gpio init failed (%d)", ret); + return ret; + } + } + + if (!device_is_ready(cfg->regulator_vmmc)) { + LOG_DEV_ERR(dev, "vmmc is not ready"); + return -ENODEV; + } + + if (!device_is_ready(cfg->regulator_vqmmc)) { + LOG_DEV_ERR(dev, "vqmmc is not ready"); + return -ENODEV; + } + + ret = bcm2712_sdhci_init_host_props(dev); + if (ret) { + return ret; + } + + ret = bcm2712_sdhci_init_hw(dev); + if (ret) { + LOG_DEV_ERR(dev, "hw init failed (%d)", ret); + return ret; + } + + ret = sdhci_init(&data->sdhci_ctx); + if (ret) { + LOG_DEV_ERR(dev, "sdhci init failed (%d)", ret); + return ret; + } + + if (regulator_is_enabled(cfg->regulator_vmmc)) { + ret = regulator_disable(cfg->regulator_vmmc); + if (ret) { + LOG_DEV_ERR(dev, "vmmc disable failed (%d)", ret); + return ret; + } + } + + if (regulator_is_enabled(cfg->regulator_vqmmc)) { + ret = regulator_disable(cfg->regulator_vqmmc); + if (ret) { + LOG_DEV_ERR(dev, "vqmmc disable failed (%d)", ret); + return ret; + } + } + + if (IS_ENABLED(CONFIG_BRCM_BCM2712_SDHC_USE_IRQ)) { + cfg->irq_config(dev); + } + + return 0; +} + +#if defined(CONFIG_BRCM_BCM2712_SDHC_USE_IRQ) +static void bcm2712_irq_handler(const void *arg) +{ + const struct device *dev = arg; + struct bcm2712_sdhci_data *data = dev->data; + + sdhc_irq_cb(&data->sdhci_ctx); +} + +#define BCM2712_SDHC_IRQ_CFG(inst) \ + static void irq_config_##inst(const struct device *dev) \ + { \ + IRQ_CONNECT(DT_INST_IRQN(inst), DT_INST_IRQ(inst, priority), bcm2712_irq_handler, \ + DEVICE_DT_INST_GET(inst), DT_INST_IRQ(inst, flags)); \ + irq_enable(DT_INST_IRQN(inst)); \ + } + +#define BCM2712_SDHC_IRQ_INIT(inst) .irq_config = irq_config_##inst, +#else +#define BCM2712_SDHC_IRQ_CFG(inst) +#define BCM2712_SDHC_IRQ_INIT(inst) +#endif /* CONFIG_BRCM_BCM2712_SDHC_USE_IRQ */ + +#if defined(CONFIG_BRCM_BCM2712_XFER_ADMA2) +#define BRCM_BCM2712_ADMA2_DESCS(inst) \ + static struct sdhc_adma_desc \ + adma_descs_##inst[CONFIG_BRCM_BCM2712_ADMA2_DESCS_NUM] __nocache; +#define BRCM_BCM2712_ADMA2_DESCS_INIT(inst) .adma_descs = adma_descs_##inst, +#else +#define BRCM_BCM2712_ADMA2_DESCS(inst) +#define BRCM_BCM2712_ADMA2_DESCS_INIT(inst) +#endif + +#define BCM2712_SDHCI_INIT(inst) \ + BRCM_BCM2712_ADMA2_DESCS(inst) \ + BCM2712_SDHC_IRQ_CFG(inst) \ + static const struct bcm2712_sdhci_config bcm2712_sdhci_config_##inst = { \ + DEVICE_MMIO_NAMED_ROM_INIT_BY_NAME(host, DT_DRV_INST(inst)), \ + DEVICE_MMIO_NAMED_ROM_INIT_BY_NAME(cfg, DT_DRV_INST(inst)), \ + \ + .regulator_vqmmc = DEVICE_DT_GET(DT_INST_PHANDLE(inst, vqmmc_supply)), \ + .regulator_vmmc = DEVICE_DT_GET(DT_INST_PHANDLE(inst, vmmc_supply)), \ + \ + .gpio_cd = GPIO_DT_SPEC_INST_GET_OR(inst, cd_gpios, {0}), \ + \ + .clk_freq = DT_INST_PROP_BY_PHANDLE(inst, clocks, clock_frequency), \ + \ + .bw_4bit = DT_INST_ENUM_HAS_VALUE(inst, bus_width, 4), \ + .bw_8bit = DT_INST_ENUM_HAS_VALUE(inst, bus_width, 8), \ + .max_bus_freq = DT_INST_PROP_OR(inst, max_bus_freq, 0), \ + .min_bus_freq = DT_INST_PROP_OR(inst, min_bus_freq, 400000), \ + .power_delay_ms = DT_INST_PROP_OR(inst, power_delay_ms, 500), \ + .non_removable = DT_INST_PROP_OR(inst, non_removable, 0), \ + BRCM_BCM2712_ADMA2_DESCS_INIT(inst) \ + BCM2712_SDHC_IRQ_INIT(inst) \ + }; \ + static struct bcm2712_sdhci_data bcm2712_sdhci_data_##inst = {}; \ + DEVICE_DT_INST_DEFINE(inst, &bcm2712_sdhci_init, NULL, &bcm2712_sdhci_data_##inst, \ + &bcm2712_sdhci_config_##inst, POST_KERNEL, \ + CONFIG_SDHC_INIT_PRIORITY, &bcm2712_sdhci_api); + +DT_INST_FOREACH_STATUS_OKAY(BCM2712_SDHCI_INIT) diff --git a/drivers/sdhc/sdhci_common.c b/drivers/sdhc/sdhci_common.c new file mode 100644 index 00000000000000..5c0acbebaf8236 --- /dev/null +++ b/drivers/sdhc/sdhci_common.c @@ -0,0 +1,1071 @@ +/* + * Copyright (c) 2024 EPAM Systems + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include + +#include "sdhci_common.h" + +LOG_MODULE_REGISTER(sdhci_common, CONFIG_SDHC_LOG_LEVEL); + +#define SDHCI_DATA_MAX_TIMEOUT 0xE + +#define SDHCI_RESET_TIMEOUT_MS 100 +#define SDHCI_CLK_STABLE_TIMEOUT_MS 20 +#define SDHCI_REQ_SD_BUSY_TIMEOUT_MS 100 + +static int sdhci_send_cmd(struct sdhci_common *sdhci_ctx, struct sdhc_command *cmd, + bool data_present); + +static void sdhci_enable_sd_clock(struct sdhci_common *sdhci_ctx, bool on) +{ + uint16_t clk_reg = 0; + int32_t timeout; + + clk_reg = sys_read16(sdhci_ctx->reg_base + SDHCI_CLOCK_CONTROL); + if (!(clk_reg & SDHCI_CLOCK_INT_EN)) { + /* clock not configured, nothing to do */ + return; + } + + timeout = SDHCI_CLK_STABLE_TIMEOUT_MS * USEC_PER_MSEC; + if (on) { + /* wait for Internal Clock Stable */ + if (!WAIT_FOR(((sys_read16(sdhci_ctx->reg_base + SDHCI_CLOCK_CONTROL) & + SDHCI_CLOCK_INT_STABLE)), + timeout, k_msleep(1))) { + LOG_ERR("sdhc:clk_en: timeout for clk stable"); + } + } + + if (on) { + clk_reg |= SDHCI_CLOCK_CARD_EN; + } else { + clk_reg &= ~SDHCI_CLOCK_CARD_EN; + } + + LOG_DBG("sdhc:clk_en: SDHCI_CLOCK_CONTROL:%04x", clk_reg); + sys_write16(clk_reg, sdhci_ctx->reg_base + SDHCI_CLOCK_CONTROL); +} + +static void sdhci_set_hspd(struct sdhci_common *sdhci_ctx, bool on) +{ + uint8_t ctrl; + + /* Set bus width */ + ctrl = sys_read8(sdhci_ctx->reg_base + SDHCI_HOST_CONTROL); + if (on) { + ctrl |= SDHCI_CTRL_HISPD; + } else { + ctrl &= ~SDHCI_CTRL_HISPD; + } + + LOG_DBG("sdhc:hspd: SDHCI_HOST_CONTROL:%02x", ctrl); + sys_write8(ctrl, sdhci_ctx->reg_base + SDHCI_HOST_CONTROL); +} + +int sdhci_get_cd(struct sdhci_common *sdhci_ctx) +{ + uint32_t reg; + + reg = sys_read32(sdhci_ctx->reg_base + SDHCI_PRESENT_STATE); + LOG_DBG("sdhc:get_cd: SDHCI_PRESENT_STATE:%x", reg); + + return reg & SDHCI_CARD_PRESENT ? 1 : 0; +} + +int sdhci_is_card_busy(struct sdhci_common *sdhci_ctx) +{ + uint32_t reg; + + reg = sys_read32(sdhci_ctx->reg_base + SDHCI_PRESENT_STATE); + LOG_DBG("sdhc:is_busy: SDHCI_PRESENT_STATE card_busy:%x", reg); + + return (reg & SDHCI_DATA_0_LVL_MASK) == SDHCI_DATA_0_LVL_MASK ? 0 : 1; +} + +int sdhci_reset(struct sdhci_common *sdhci_ctx, uint8_t mask) +{ + int32_t timeout; + + timeout = SDHCI_RESET_TIMEOUT_MS * USEC_PER_MSEC; + sys_write8(mask, sdhci_ctx->reg_base + SDHCI_SOFTWARE_RESET); + + if (!WAIT_FOR((!(sys_read8(sdhci_ctx->reg_base + SDHCI_SOFTWARE_RESET) & mask)), timeout, + k_msleep(1))) { + LOG_ERR("sdhc:reset: timeout msk:%02x", mask); + return -ETIMEDOUT; + } + + return 0; +} + +void sdhci_set_bus_power(struct sdhci_common *sdhci_ctx, enum sd_voltage bus_voltage) +{ + uint8_t pwr = 0; + + switch (bus_voltage) { + case SD_VOL_3_3_V: + pwr = SDHCI_POWER_330; + break; + case SD_VOL_3_0_V: + pwr = SDHCI_POWER_300; + break; + case SD_VOL_1_8_V: + pwr = SDHCI_POWER_180; + break; + default: + LOG_DBG("power off"); + } + + if (!pwr) { + sdhci_enable_sd_clock(sdhci_ctx, false); + LOG_DBG("sdhc:bus_power: off"); + sys_write8(0, sdhci_ctx->reg_base + SDHCI_POWER_CONTROL); + return; + } + + sys_write8(pwr, sdhci_ctx->reg_base + SDHCI_POWER_CONTROL); + + pwr |= SDHCI_POWER_ON; + LOG_DBG("sdhc:bus_power: SDHCI_POWER_CONTROL:%02x", pwr); + sys_write8(pwr, sdhci_ctx->reg_base + SDHCI_POWER_CONTROL); + + sdhci_enable_sd_clock(sdhci_ctx, true); +} + +int sdhci_set_clock(struct sdhci_common *sdhci_ctx, enum sdhc_clock_speed clock) +{ + uint16_t clk_reg = 0; + uint16_t divider; + int32_t timeout; + + sys_write16(0, sdhci_ctx->reg_base + SDHCI_CLOCK_CONTROL); + + if (clock == 0) { + LOG_DBG("sdhc:clk_set: off"); + return 0; + } + + /* Check if the Host Controller supports Programmable Clock */ + if (sdhci_ctx->clk_mul) { + uint32_t max_clk = sdhci_ctx->max_clk * sdhci_ctx->clk_mul; + + for (divider = 1; divider <= 1024; divider++) { + if ((max_clk / divider) <= clock) + break; + } + + /* Set Programmable Clock Mode in the Clock Control register */ + clk_reg = SDHCI_PROG_CLOCK_MODE; + divider--; + } else { + /* Version 3.00 divisors must be a multiple of 2. */ + if (sdhci_ctx->max_clk <= clock) { + divider = 1; + } else { + for (divider = 2; + divider < SDHCI_MAX_DIV_SPEC_300; + divider += 2) { + if ((sdhci_ctx->max_clk / divider) <= clock) + break; + } + } + divider >>= 1; + } + + clk_reg |= FIELD_GET(SDHCI_DIV_MASK, divider) << SDHCI_DIVIDER_SHIFT; + clk_reg |= FIELD_GET(SDHCI_DIV_HI_MASK, divider) << SDHCI_DIVIDER_HI_SHIFT; + clk_reg |= SDHCI_CLOCK_INT_EN; + LOG_DBG("sdhc:clk_set: SDHCI_CLOCK_CONTROL:%04x divider:%u", clk_reg, divider); + sys_write16(clk_reg, sdhci_ctx->reg_base + SDHCI_CLOCK_CONTROL); + + + timeout = SDHCI_CLK_STABLE_TIMEOUT_MS * USEC_PER_MSEC; + /* wait for Internal Clock Stable */ + if (!WAIT_FOR(((sys_read16(sdhci_ctx->reg_base + SDHCI_CLOCK_CONTROL) & + SDHCI_CLOCK_INT_STABLE)), + timeout, k_msleep(1))) { + LOG_ERR("sdhc:clk_set: clk stable timeout"); + return -ETIMEDOUT; + } + + clk_reg |= SDHCI_CLOCK_CARD_EN; + LOG_DBG("sdhc:clk_set: stable SDHCI_CLOCK_CONTROL:%04x", clk_reg); + sys_write16(clk_reg, sdhci_ctx->reg_base + SDHCI_CLOCK_CONTROL); + + return 0; +} + +int sdhci_set_bus_width(struct sdhci_common *sdhci_ctx, enum sdhc_bus_width bus_width) +{ + uint8_t ctrl; + + /* Set bus width */ + ctrl = sys_read8(sdhci_ctx->reg_base + SDHCI_HOST_CONTROL); + + ctrl &= ~SDHCI_CTRL_8BITBUS; + + switch (bus_width) { + case SDHC_BUS_WIDTH1BIT: + ctrl &= ~SDHCI_CTRL_4BITBUS; + break; + case SDHC_BUS_WIDTH4BIT: + ctrl |= SDHCI_CTRL_4BITBUS; + break; + case SDHC_BUS_WIDTH8BIT: + ctrl |= SDHCI_CTRL_8BITBUS; + break; + default: + return -ENOTSUP; + } + + LOG_DBG("sdhc:bus_width: SDHCI_HOST_CONTROL:%02x", ctrl); + sys_write8(ctrl, sdhci_ctx->reg_base + SDHCI_HOST_CONTROL); + return 0; +} + +int sdhci_set_voltage(struct sdhci_common *sdhci_ctx, enum sd_voltage signal_voltage) +{ + uint16_t ctrl; + + ctrl = sys_read16(sdhci_ctx->reg_base + SDHCI_HOST_CONTROL2); + + switch (signal_voltage) { + case SD_VOL_3_3_V: + ctrl &= ~SDHCI_CTRL_VDD_180; + sys_write16(ctrl, sdhci_ctx->reg_base + SDHCI_HOST_CONTROL2); + + /* Wait for 5ms */ + k_msleep(5); + + /* 3.3V regulator output should be stable within 5 ms */ + ctrl = sys_read16(sdhci_ctx->reg_base + SDHCI_HOST_CONTROL2); + LOG_DBG("sdhc:set_3_3: SDHCI_HOST_CONTROL2: %04x", ctrl); + if (ctrl & SDHCI_CTRL_VDD_180) { + LOG_ERR("sdhc: 3.3V regulator output did not become stable\n"); + return -EIO; + } + + break; + case SD_VOL_1_8_V: + ctrl |= SDHCI_CTRL_VDD_180; + sys_write16(ctrl, sdhci_ctx->reg_base + SDHCI_HOST_CONTROL2); + + /* Wait for 5 ms */ + k_msleep(5); + + /* 1.8V regulator output has to be stable within 5 ms */ + ctrl = sys_read16(sdhci_ctx->reg_base + SDHCI_HOST_CONTROL2); + LOG_DBG("sdhc:set_1_8: SDHCI_HOST_CONTROL2: %04x", ctrl); + if (!(ctrl & SDHCI_CTRL_VDD_180)) { + LOG_ERR("sdhc: 1.8V regulator output did not become stable\n"); + return -EIO; + } + + break; + default: + LOG_DBG("sdhc: signal voltage not supported %d", signal_voltage); + return -ENOTSUP; + } + + return 0; +} + +int sdhci_set_uhs_timing(struct sdhci_common *sdhci_ctx, enum sdhc_timing_mode timing) +{ + bool hspd = false; + uint16_t reg; + + reg = sys_read16(sdhci_ctx->reg_base + SDHCI_HOST_CONTROL2); + reg &= ~SDHCI_CTRL_UHS_MASK; + + switch (timing) { + case SDHC_TIMING_LEGACY: + case SDHC_TIMING_SDR12: + reg |= SDHCI_CTRL_UHS_SDR12; + break; + case SDHC_TIMING_SDR25: + reg |= SDHCI_CTRL_UHS_SDR25; + hspd = true; + break; + case SDHC_TIMING_SDR50: + reg |= SDHCI_CTRL_UHS_SDR50; + break; + case SDHC_TIMING_DDR50: + reg |= SDHCI_CTRL_UHS_DDR50; + break; + case SDHC_TIMING_SDR104: + reg |= SDHCI_CTRL_UHS_SDR104; + break; + default: + return -ENOTSUP; + } + + sdhci_enable_sd_clock(sdhci_ctx, false); + + LOG_DBG("sdhc:timing: SDHCI_HOST_CONTROL2: %04x", reg); + sys_write16(reg, sdhci_ctx->reg_base + SDHCI_HOST_CONTROL2); + + sdhci_set_hspd(sdhci_ctx, hspd); + + sdhci_enable_sd_clock(sdhci_ctx, true); + return 0; +} + +static int sdhc_stop_transfer(struct sdhci_common *sdhci_ctx) +{ + struct sdhc_command hdc_cmd = {0}; + + hdc_cmd.opcode = SD_STOP_TRANSMISSION; + hdc_cmd.arg = 0; + hdc_cmd.response_type = SD_RSP_TYPE_R1; + hdc_cmd.timeout_ms = CONFIG_SD_CMD_TIMEOUT; + + return sdhci_send_cmd(sdhci_ctx, &hdc_cmd, false); +} + +static bool sdhc_need_cmd12(struct sdhc_command *cmd) +{ + return (cmd->opcode == SD_READ_MULTIPLE_BLOCK || cmd->opcode == SD_WRITE_MULTIPLE_BLOCK); +} + +static int sdhci_poll_data_complete(struct sdhci_common *sdhci_ctx, int32_t timeout) +{ + uint32_t reg_normal_int_stat = 0; + int ret = -ETIMEDOUT; + + timeout = timeout ? timeout : 1; + timeout = timeout * 100; + + while (timeout > 0) { + reg_normal_int_stat = sys_read32(sdhci_ctx->reg_base + SDHCI_INT_STATUS); + if (reg_normal_int_stat & SDHCI_INT_XFER_COMPLETE) { + sys_write32(SDHCI_INT_XFER_COMPLETE, + sdhci_ctx->reg_base + SDHCI_INT_STATUS); + ret = 0; + break; + } + + if (reg_normal_int_stat & SDHCI_INT_ERROR) { + break; + } + + k_busy_wait(10); + timeout--; + } + + if (reg_normal_int_stat & SDHCI_INT_ERROR) { + LOG_ERR("sdhc:req: data(poll) xfer error int_status:%08x", reg_normal_int_stat); + sys_write32(SDHCI_INT_ERROR_MASK, sdhci_ctx->reg_base + SDHCI_INT_STATUS); + ret = -EIO; + } + + if (!timeout) { + LOG_ERR("sdhc:req: data(poll) xfer timeout int_status:%08x", reg_normal_int_stat); + } + + return ret; +} + +static int sdhci_wait_data_complete(struct sdhci_common *sdhci_ctx, uint32_t timeout) +{ + k_timeout_t wait_time; + uint32_t events; + int ret = 0; + + if (timeout == SDHC_TIMEOUT_FOREVER) { + wait_time = K_FOREVER; + } else { + wait_time = K_MSEC(timeout); + } + + events = k_event_wait(&sdhci_ctx->irq_event, + SDHCI_INT_XFER_COMPLETE | SDHCI_INT_ERROR, + false, wait_time); + + if (events & SDHCI_INT_ERROR) { + LOG_ERR("sdhc:req: data(irq) xfer error int_status:%08x", events); + ret = -EIO; + } else if (!events) { + LOG_ERR("sdhc:req: data(irq) xfer timeout"); + ret = -ETIMEDOUT; + } + + return ret; +} + +static void sdhci_data_port_io(struct sdhci_common *sdhci_ctx, uint8_t *data, uint32_t size, + bool read) +{ + uint32_t *offs = (uint32_t *)data; + int i; + + for (i = 0; i < size; i += 4, offs++) { + if (read) { + *offs = sys_read32(sdhci_ctx->reg_base + SDHCI_BUFFER); + } else { + sys_write32(*offs, sdhci_ctx->reg_base + SDHCI_BUFFER); + } + } +} + +static int sdhci_transfer_data(struct sdhci_common *sdhci_ctx, struct sdhc_data *data, + bool is_read) +{ + uint32_t block = data->blocks; + uint32_t reg_present_state; + uint32_t int_status, rdy_mask; + uint8_t *data_ptr = data->data; + int32_t timeout; + + timeout = data->timeout_ms ? data->timeout_ms : 1; + timeout = timeout * 100; + + if (is_read) { + rdy_mask = SDHCI_BUF_RD_ENABLE; + } else { + rdy_mask = SDHCI_BUF_WR_ENABLE; + } + + while (timeout && block) { + int_status = sys_read32(sdhci_ctx->reg_base + SDHCI_INT_STATUS); + if (int_status & SDHCI_INT_ERROR) { + LOG_ERR("sdhc:req: data xfer error int_status:%08x", int_status); + return -EIO; + } + + reg_present_state = sys_read32(sdhci_ctx->reg_base + SDHCI_PRESENT_STATE); + if (reg_present_state & rdy_mask) { + sdhci_data_port_io(sdhci_ctx, data_ptr, data->block_size, is_read); + data_ptr += data->block_size; + block--; + } else { + k_busy_wait(10); + timeout--; + } + }; + + if (!timeout) { + LOG_ERR("sdhc:req: transfer data timeout"); + return -ETIMEDOUT; + } + + return sdhci_poll_data_complete(sdhci_ctx, timeout / 100); +} + +static int sdhci_transfer_data_irq(struct sdhci_common *sdhci_ctx, struct sdhc_data *data, + bool is_read) +{ + uint8_t *data_ptr = data->data; + uint32_t block = data->blocks; + k_timeout_t wait_time; + uint32_t rdy_mask; + uint32_t events; + + if (data->timeout_ms == SDHC_TIMEOUT_FOREVER) { + wait_time = K_FOREVER; + } else { + wait_time = K_MSEC(data->timeout_ms); + } + + rdy_mask = SDHCI_INT_ERROR; + if (is_read) { + rdy_mask |= SDHCI_INT_BUF_RD_READY; + } else { + rdy_mask |= SDHCI_INT_BUF_WR_READY; + } + + while (block) { + events = k_event_wait(&sdhci_ctx->irq_event, rdy_mask, + false, wait_time); + + if (events & SDHCI_INT_ERROR) { + LOG_ERR("sdhc:req: data(irq) io xfer error int_status:%08x", events); + return -EIO; + } else if (!events) { + LOG_ERR("sdhc:req: data(irq) io xfer timeout"); + return -ETIMEDOUT; + } + k_event_clear(&sdhci_ctx->irq_event, rdy_mask); + sdhci_data_port_io(sdhci_ctx, data_ptr, data->block_size, is_read); + data_ptr += data->block_size; + block--; + }; + + return sdhci_wait_data_complete(sdhci_ctx, data->timeout_ms); +} + +static void sdhc_adma_desc_fill(struct sdhc_adma_desc *desc, uintptr_t dma_addr, uint16_t len, + bool end) +{ + uint16_t cmd; + + cmd = SDHC_ADMA2_DESC_VALID | SDHC_ADMA2_DESC_ACT_TRAN; + if (end) { + cmd |= SDHC_ADMA2_DESC_END; + } + + desc->cmd = sys_cpu_to_le16(cmd); + desc->len = sys_cpu_to_le16(len); + desc->addr = sys_cpu_to_le32(dma_addr); + LOG_DBG("sdhc:dma_desc: %016llx", *((uint64_t *)desc)); +} + +static void sdhc_prepare_adma_table(struct sdhci_common *sdhci_ctx, struct sdhc_data *data, + uintptr_t dma_addr) +{ + uint32_t trans_bytes = data->block_size * data->blocks; + struct sdhc_adma_desc *desc = sdhci_ctx->adma_descs; + uintptr_t desc_dma_addr; + uint32_t desc_count, desc_count_idx; + + desc_count = DIV_ROUND_UP(trans_bytes, SDHC_ADMA2_DESC_MAX_LEN); + desc_count_idx = desc_count; + desc_dma_addr = z_mem_phys_addr(sdhci_ctx->adma_descs); + while (--desc_count_idx) { + sdhc_adma_desc_fill(desc, dma_addr, SDHC_ADMA2_DESC_MAX_LEN, false); + dma_addr += SDHC_ADMA2_DESC_MAX_LEN; + trans_bytes -= SDHC_ADMA2_DESC_MAX_LEN; + desc++; + } + + sdhc_adma_desc_fill(desc, dma_addr, trans_bytes, true); + +#if !defined(CONFIG_NOCACHE_MEMORY) + sys_cache_data_flush_range(sdhci_ctx->adma_descs, + (desc_count * sizeof(struct sdhc_adma_desc))); +#endif + + sys_write32(desc_dma_addr, sdhci_ctx->reg_base + SDHCI_ADMA_ADDRESS_LOW); +} + +static void sdhci_set_dma_sel(struct sdhci_common *sdhci_ctx) +{ + uint8_t ctrl; + + if (!sdhci_ctx->f_use_dma) { + return; + } + + /* Set DMA Select */ + ctrl = sys_read8(sdhci_ctx->reg_base + SDHCI_HOST_CONTROL); + + ctrl &= ~SDHCI_CTRL_DMA_MASK; + if (sdhci_ctx->dma_mode == SDHC_DMA_SDMA) { + ctrl |= SDHCI_CTRL_SDMA << SDHCI_CTRL_DMA_SHIFT; + } else if (sdhci_ctx->dma_mode == SDHC_DMA_ADMA2) { + ctrl |= SDHCI_CTRL_ADMA2 << SDHCI_CTRL_DMA_SHIFT; + } + + LOG_DBG("sdhc:dma_sel: SDHCI_HOST_CONTROL:%02x", ctrl); + sys_write8(ctrl, sdhci_ctx->reg_base + SDHCI_HOST_CONTROL); +} + +static void sdhci_prepare_dma(struct sdhci_common *sdhci_ctx, struct sdhc_data *data, + bool is_read) +{ + uintptr_t dma_addr; + + sys_cache_data_flush_range(data->data, (data->blocks * data->block_size)); + + dma_addr = z_mem_phys_addr(data->data); + LOG_DBG("sdhc:req: SDHCI_DMA_ADDRESS %08lx", dma_addr); + if (sdhci_ctx->dma_mode == SDHC_DMA_SDMA) { + sys_write32(dma_addr, sdhci_ctx->reg_base + SDHCI_DMA_ADDRESS); + } else { + sdhc_prepare_adma_table(sdhci_ctx, data, dma_addr); + } +} + +static void sdhci_prepare_data(struct sdhci_common *sdhci_ctx, struct sdhc_command *cmd, + struct sdhc_data *data, bool is_read) +{ + uint16_t transfer_mode = SDHCI_TRNS_BLK_CNT_EN; + uint32_t trans_bytes; + + trans_bytes = data->blocks * data->block_size; + if (data->blocks > 1) { + transfer_mode |= SDHCI_TRNS_MULTI; + } + + if (is_read) { + transfer_mode |= SDHCI_TRNS_READ; + } + + if (sdhci_ctx->f_auto_cmd12 && sdhc_need_cmd12(cmd)) { + transfer_mode |= SDHCI_TRNS_ACMD12; + } + + if (sdhci_ctx->f_use_dma) { + transfer_mode |= SDHCI_TRNS_DMA; + sdhci_prepare_dma(sdhci_ctx, data, is_read); + } + + sys_write16(SDHCI_MAKE_BLKSZ(SDHCI_DEFAULT_BOUNDARY_ARG, data->block_size), + sdhci_ctx->reg_base + SDHCI_BLOCK_SIZE); + sys_write16(data->blocks, sdhci_ctx->reg_base + SDHCI_BLOCK_COUNT); + sys_write16(transfer_mode, sdhci_ctx->reg_base + SDHCI_TRANSFER_MODE); +} + +static int sdhci_dma_check(struct sdhci_common *sdhci_ctx, struct sdhc_data *data) +{ + uint32_t trans_bytes; + + if (!sdhci_ctx->f_use_dma) { + return 0; + } + +#if defined(CONFIG_64BIT) + /* Only 32bit DMA supported */ + if (z_mem_phys_addr(data->data) >> 32) { + k_panic(); + return -EIO; + } +#endif + + trans_bytes = data->block_size * data->blocks; + + if (data->blocks > 65535) { + LOG_ERR("sdhc:sdma: max xfer blocks count (%d) overflow max:65535", data->blocks); + return -EIO; + } + + if (sdhci_ctx->dma_mode == SDHC_DMA_SDMA) { + if (trans_bytes > SDHC_SDMA_XFER_MAX) { + LOG_ERR("sdhc:sdma: max xfer size overflow max:%08d", SDHC_SDMA_XFER_MAX); + return -EIO; + } + } + + if (sdhci_ctx->dma_mode == SDHC_DMA_ADMA2) { + uint32_t desc_count; + + desc_count = DIV_ROUND_UP(trans_bytes, SDHC_ADMA2_DESC_MAX_LEN); + if (desc_count > sdhci_ctx->adma_descs_num) { + LOG_ERR("sdhc:adma: xfer desc number (%u) overflow, max:%u ", desc_count, + sdhci_ctx->adma_descs_num); + return -EIO; + } + } + + return 0; +} + +static void sdhci_cmd_done(struct sdhci_common *sdhci_ctx, struct sdhc_command *cmd, + bool long_resp) +{ + cmd->response[0] = sys_read32(sdhci_ctx->reg_base + SDHCI_RESPONSE); + if (!long_resp) { + return; + } + cmd->response[1] = sys_read32(sdhci_ctx->reg_base + SDHCI_RESPONSE1); + cmd->response[2] = sys_read32(sdhci_ctx->reg_base + SDHCI_RESPONSE2); + cmd->response[3] = sys_read32(sdhci_ctx->reg_base + SDHCI_RESPONSE3); + + /* 136-bit: RTS=01b, Response field R[127:8] - RESP3[23:0], + * RESP2[31:0], RESP1[31:0], RESP0[31:0] + * Subsystem expects 128 bits response but SDHC sends + * 120 bits response from R[127:8]. + */ + cmd->response[3] = ((cmd->response[3] << 8) | ((cmd->response[2] >> 24) & 0xFF)); + cmd->response[2] = ((cmd->response[2] << 8) | ((cmd->response[1] >> 24) & 0xFF)); + cmd->response[1] = ((cmd->response[1] << 8) | ((cmd->response[0] >> 24) & 0xFF)); + cmd->response[0] = (cmd->response[0] << 8); +} + +static int sdhci_poll_cmd_complete(struct sdhci_common *sdhci_ctx, int32_t timeout) +{ + uint32_t reg_normal_int_stat = 0; + int ret = -ETIMEDOUT; + + timeout = timeout ? timeout : 1; + timeout = timeout * 100; + while (timeout) { + reg_normal_int_stat = sys_read32(sdhci_ctx->reg_base + SDHCI_INT_STATUS); + if (reg_normal_int_stat & SDHCI_INT_CMD_COMPLETE) { + sys_write32(SDHCI_INT_CMD_COMPLETE, sdhci_ctx->reg_base + SDHCI_INT_STATUS); + ret = 0; + break; + } + + if (reg_normal_int_stat & SDHCI_INT_ERROR) { + break; + } + + k_busy_wait(10); + timeout--; + } + + if (reg_normal_int_stat & SDHCI_INT_ERROR) { + LOG_ERR("sdhc:req: cmd(poll) error int_status:%08x", reg_normal_int_stat); + sys_write32(SDHCI_INT_ERROR_MASK, sdhci_ctx->reg_base + SDHCI_INT_STATUS); + ret = -EIO; + } + + if (!timeout) { + LOG_ERR("sdhc:req: cmd(poll) timeout int_status:%08x", reg_normal_int_stat); + } + + return ret; +} + +static int sdhc_wait_cmd_complete(struct sdhci_common *sdhci_ctx, int32_t timeout) +{ + k_timeout_t wait_time; + uint32_t events; + int ret = 0; + + if (timeout == SDHC_TIMEOUT_FOREVER) { + wait_time = K_FOREVER; + } else { + wait_time = K_MSEC(timeout); + } + + events = k_event_wait(&sdhci_ctx->irq_event, + SDHCI_INT_CMD_COMPLETE | SDHCI_INT_ERROR, + false, wait_time); + + if (events & SDHCI_INT_ERROR) { + LOG_ERR("sdhc:req: cmd(irq) error int_status:%08x", events); + ret = -EIO; + } else if (!events) { + LOG_ERR("sdhc:req: cmd(irq) timeout"); + ret = -ETIMEDOUT; + } + + return ret; +} + +static int sdhci_send_cmd(struct sdhci_common *sdhci_ctx, struct sdhc_command *cmd, + bool data_present) +{ + bool long_resp = false; + uint16_t resp_type; + uint16_t cmd_flags; + int ret = 0; + + resp_type = (cmd->response_type & SDHC_NATIVE_RESPONSE_MASK); + + switch (resp_type) { + case SD_RSP_TYPE_NONE: + cmd_flags = SDHCI_CMD_RESP_NONE; + break; + case SD_RSP_TYPE_R2: + cmd_flags = SDHCI_CMD_RESP_LONG; + cmd_flags |= SDHCI_CMD_CRC; + long_resp = true; + break; + case SD_RSP_TYPE_R3: + case SD_RSP_TYPE_R4: + cmd_flags = SDHCI_CMD_RESP_SHORT; + break; + case SD_RSP_TYPE_R1: + case SD_RSP_TYPE_R5: + case SD_RSP_TYPE_R6: + case SD_RSP_TYPE_R7: + cmd_flags = SDHCI_CMD_RESP_SHORT; + cmd_flags |= SDHCI_CMD_CRC; + cmd_flags |= SDHCI_CMD_INDEX; + break; + case SD_RSP_TYPE_R1b: + case SD_RSP_TYPE_R5b: + cmd_flags = SDHCI_CMD_RESP_SHORT_BUSY; + cmd_flags |= SDHCI_CMD_CRC; + cmd_flags |= SDHCI_CMD_INDEX; + break; + default: + LOG_ERR("sdhc:req: unknown response type 0x%08x", resp_type); + return -EINVAL; + } + + if (data_present) { + cmd_flags |= SDHCI_CMD_DATA; + } + + if (cmd->opcode == SD_STOP_TRANSMISSION) { + cmd_flags |= (SDHCI_CMD_TYPE_ABORT << SDHCI_CMD_TYPE_SHIFT); + } + + LOG_DBG("sdhc:req: op:%d resp_type:%d", cmd->opcode, resp_type); + + sys_write8(SDHCI_DATA_MAX_TIMEOUT, sdhci_ctx->reg_base + SDHCI_TIMEOUT_CONTROL); + sys_write32(cmd->arg, sdhci_ctx->reg_base + SDHCI_ARGUMENT_1); + sys_write16(SDHCI_MAKE_CMD(cmd->opcode, cmd_flags), sdhci_ctx->reg_base + SDHCI_COMMAND); + + if (cmd->opcode != SD_SEND_TUNING_BLOCK) { + if (sdhci_ctx->f_use_irq) { + ret = sdhc_wait_cmd_complete(sdhci_ctx, cmd->timeout_ms); + } else { + ret = sdhci_poll_cmd_complete(sdhci_ctx, cmd->timeout_ms); + } + } + if (!ret && resp_type != SD_RSP_TYPE_NONE) { + sdhci_cmd_done(sdhci_ctx, cmd, long_resp); + } + + return ret; +} + +int sdhci_send_req(struct sdhci_common *sdhci_ctx, struct sdhc_command *cmd, struct sdhc_data *data) +{ + uint32_t reg_present_state; + bool is_read = false; + int ret = 0; + + reg_present_state = sys_read32(sdhci_ctx->reg_base + SDHCI_PRESENT_STATE); + + if (reg_present_state & SDHCI_CMD_INHIBIT) { + LOG_ERR("sdhc:req: CMD line is not available"); + return -EBUSY; + } + + if (data && (reg_present_state & SDHCI_DATA_INHIBIT)) { + LOG_ERR("sdhc:req: Data line is not available"); + return -EBUSY; + } + + sys_write32(SDHCI_INT_ALL_MASK, sdhci_ctx->reg_base + SDHCI_INT_STATUS); + k_event_clear(&sdhci_ctx->irq_event, SDHCI_INT_ALL_MASK); + + if (cmd->opcode != SD_WRITE_SINGLE_BLOCK && cmd->opcode != SD_WRITE_MULTIPLE_BLOCK) { + is_read = true; + } + + if (data) { + ret = sdhci_dma_check(sdhci_ctx, data); + if (ret) { + return ret; + } + sdhci_prepare_data(sdhci_ctx, cmd, data, is_read); + } + + ret = sdhci_send_cmd(sdhci_ctx, cmd, !!data); + if (ret) { + goto req_error; + } + + if (!data) { + return 0; + } + + LOG_DBG("sdhc:req:data: blocks:%d block_size:%d buf:%p blocknum:%u", data->blocks, + data->block_size, data->data, data->block_addr); + + if (sdhci_ctx->f_use_dma) { + if (sdhci_ctx->f_use_irq) { + ret = sdhci_wait_data_complete(sdhci_ctx, data->timeout_ms); + } else { + ret = sdhci_poll_data_complete(sdhci_ctx, data->timeout_ms); + } + + if (!ret && is_read) { + sys_cache_data_invd_range(data->data, data->blocks * data->block_size); + } + } else { + if (sdhci_ctx->f_use_irq) { + ret = sdhci_transfer_data_irq(sdhci_ctx, data, is_read); + } else { + ret = sdhci_transfer_data(sdhci_ctx, data, is_read); + } + if (ret) { + LOG_ERR("sdhc:req: data transfer failed (%d)", ret); + goto req_error; + } + } + + /* Host need to send CMD12 (stop) at the end of Multiple-block read and write operation, + * if "Auto CMD12 Enable" not configured + */ + if (!sdhci_ctx->f_auto_cmd12 && sdhc_need_cmd12(cmd)) { + ret = sdhc_stop_transfer(sdhci_ctx); + if (ret) { + LOG_ERR("sdhc:req: cmd12 (stop) failed (%d)", ret); + goto req_error; + } + } + + return 0; + +req_error: + sdhci_reset(sdhci_ctx, SDHCI_RESET_CMD); + sdhci_reset(sdhci_ctx, SDHCI_RESET_DATA); + return ret; +} + +static int sdhci_check_version(struct sdhci_common *sdhci_ctx) +{ + bool supported; + + sdhci_ctx->version = sys_read16(sdhci_ctx->reg_base + SDHCI_HOST_VERSION); + + supported = FIELD_GET(SDHCI_SPEC_VER_MASK, sdhci_ctx->version) >= SDHCI_SPEC_300; + LOG_INF("sdhc: detected ver:%lu vendor_ver:%lu %s", + FIELD_GET(SDHCI_SPEC_VER_MASK, sdhci_ctx->version), + FIELD_GET(SDHCI_VENDOR_VER_MASK, sdhci_ctx->version), + supported ? "supported" : "unsupported"); + + if (!supported) { + return -ENOTSUP; + } + + sdhci_ctx->caps = sys_read32(sdhci_ctx->reg_base + SDHCI_CAPABILITIES); + sdhci_ctx->caps1 = sys_read32(sdhci_ctx->reg_base + SDHCI_CAPABILITIES_1); + + LOG_INF("sdhc: capabilities caps0:%x caps1:%x", sdhci_ctx->caps, sdhci_ctx->caps1); + + return 0; +} + +int sdhci_enable_sdma(struct sdhci_common *sdhci_ctx) +{ + sdhci_ctx->f_use_dma = true; + sdhci_ctx->dma_mode = SDHC_DMA_SDMA; + + return 0; +} + +int sdhci_enable_adma2(struct sdhci_common *sdhci_ctx, struct sdhc_adma_desc *descs_table, + uint32_t descs_table_size) +{ + if (!descs_table || !descs_table_size) { + return -EINVAL; + } + +#if defined(CONFIG_64BIT) + /* Only 32bit DMA supported */ + if (((uint64_t)descs_table >> 32)) { + k_panic(); + } +#endif + + sdhci_ctx->f_use_dma = true; + sdhci_ctx->adma_descs = descs_table; + sdhci_ctx->adma_descs_num = descs_table_size; + sdhci_ctx->dma_mode = SDHC_DMA_ADMA2; + + return 0; +} + +int sdhci_init_caps(struct sdhci_common *sdhci_ctx, struct sdhc_host_props *props) +{ + struct sdhc_host_caps *host_caps = &props->host_caps; + int ret; + + ret = sdhci_check_version(sdhci_ctx); + if (ret) { + return ret; + } + + /* + * TODO: The struct sdhc_host_caps->timeout_clk_freq has incorrect size 5bit instead + * of 6bit as per SDHC standard. do not read it form now. + */ + + sdhci_ctx->max_clk = FIELD_GET(SDHCI_CLOCK_BASE_MASK, sdhci_ctx->caps); + sdhci_ctx->max_clk *= 1000000; + LOG_DBG("sdhc:caps: Base Clock Frequency For SD Clock %uHz", sdhci_ctx->max_clk); + + if (!props->f_min) { + props->f_min = props->f_max / SDHCI_MAX_DIV_SPEC_300; + LOG_ERR("sdhc:caps: set min bus frequency to %u", props->f_min); + } + + host_caps->max_blk_len = FIELD_GET(SDHCI_MAX_BLOCK_MASK, sdhci_ctx->caps); + LOG_DBG("sdhc:caps: Max Block Length %u", host_caps->max_blk_len); + + host_caps->bus_8_bit_support = sdhci_ctx->caps & SDHCI_CAN_DO_8BIT ? 1 : 0; + host_caps->adma_2_support = sdhci_ctx->caps & SDHCI_CAN_DO_ADMA2 ? 1 : 0; + host_caps->high_spd_support = sdhci_ctx->caps & SDHCI_CAN_DO_HISPD ? 1 : 0; + host_caps->sdma_support = sdhci_ctx->caps & SDHCI_CAN_DO_SDMA ? 1 : 0; + + host_caps->vol_330_support = sdhci_ctx->caps & SDHCI_CAN_VDD_330 ? 1 : 0; + host_caps->vol_300_support = sdhci_ctx->caps & SDHCI_CAN_VDD_300 ? 1 : 0; + host_caps->vol_180_support = sdhci_ctx->caps & SDHCI_CAN_VDD_180 ? 1 : 0; + host_caps->address_64_bit_support_v3 = sdhci_ctx->caps & SDHCI_CAN_64BIT ? 1 : 0; + + LOG_DBG("sdhc:caps: %s %s %s %s %s %s %s %s", + host_caps->bus_8_bit_support ? "SDHCI_CAN_DO_8BIT" : "", + host_caps->adma_2_support ? "SDHCI_CAN_DO_ADMA2" : "", + host_caps->high_spd_support ? "SDHCI_CAN_DO_HISPD" : "", + host_caps->sdma_support ? "SDHCI_CAN_DO_SDMA" : "", + host_caps->vol_330_support ? "SDHCI_CAN_VDD_330" : "", + host_caps->vol_300_support ? "SDHCI_CAN_VDD_300" : "", + host_caps->vol_180_support ? "SDHCI_CAN_VDD_180" : "", + host_caps->address_64_bit_support_v3 ? "SDHCI_CAN_64BIT" : ""); + + host_caps->slot_type = FIELD_GET(SDHCI_SLOT_TYPE_MASK, sdhci_ctx->caps); + LOG_DBG("sdhc:caps: Slot Type %u", host_caps->slot_type); + + host_caps->sdr50_support = sdhci_ctx->caps1 & SDHCI_SUPPORT_SDR50 ? 1 : 0; + host_caps->sdr104_support = sdhci_ctx->caps1 & SDHCI_SUPPORT_SDR104 ? 1 : 0; + host_caps->ddr50_support = sdhci_ctx->caps1 & SDHCI_SUPPORT_DDR50 ? 1 : 0; + host_caps->drv_type_a_support = sdhci_ctx->caps1 & SDHCI_SUPPORT_DRV_A ? 1 : 0; + host_caps->drv_type_c_support = sdhci_ctx->caps1 & SDHCI_SUPPORT_DRV_C ? 1 : 0; + host_caps->drv_type_d_support = sdhci_ctx->caps1 & SDHCI_SUPPORT_DRV_D ? 1 : 0; + + host_caps->retune_timer_count = + FIELD_GET(SDHCI_RETUNING_TIMER_COUNT_MASK, sdhci_ctx->caps1); + LOG_DBG("sdhc:caps1: Timer Count for Re-Tuning %u", host_caps->retune_timer_count); + + host_caps->sdr50_needs_tuning = sdhci_ctx->caps1 & SDHCI_USE_SDR50_TUNING ? 1 : 0; + + host_caps->retuning_mode = FIELD_GET(SDHCI_RETUNING_MODE_MASK, sdhci_ctx->caps1); + LOG_DBG("sdhc:caps1: Re-Tuning Modes %u", host_caps->retuning_mode); + + host_caps->clk_multiplier = FIELD_GET(SDHCI_CLOCK_MUL_MASK, sdhci_ctx->caps1); + sdhci_ctx->clk_mul = host_caps->clk_multiplier; + LOG_DBG("sdhc:caps1: Clock Multiplier %u", host_caps->clk_multiplier); + + LOG_DBG("sdhc:caps1: %s %s %s %s %s %s %s", + host_caps->sdr50_support ? "SDHCI_SUPPORT_SDR50" : "", + host_caps->sdr104_support ? "SDHCI_SUPPORT_SDR104" : "", + host_caps->ddr50_support ? "SDHCI_SUPPORT_DDR50" : "", + host_caps->drv_type_a_support ? "SDHCI_SUPPORT_DRV_A" : "", + host_caps->drv_type_c_support ? "SDHCI_SUPPORT_DRV_C" : "", + host_caps->drv_type_d_support ? "SDHCI_SUPPORT_DRV_D" : "", + host_caps->sdr50_needs_tuning ? "SDHCI_USE_SDR50_TUNING" : ""); + + return 0; +} + +int sdhci_init(struct sdhci_common *sdhci_ctx) +{ + sdhci_reset(sdhci_ctx, SDHCI_RESET_ALL); + + sdhci_set_dma_sel(sdhci_ctx); + /* Enable only interrupts served by the SD controller */ + sys_write32(SDHCI_INT_DATA_MASK | SDHCI_INT_CMD_MASK, + sdhci_ctx->reg_base + SDHCI_INT_ENABLE); + + /* Mask all sdhci interrupt sources */ + sys_write32(0x0, sdhci_ctx->reg_base + SDHCI_SIGNAL_ENABLE); + if (sdhci_ctx->f_use_irq) { + k_event_init(&sdhci_ctx->irq_event); + /* enable irqs */ + sys_write32(SDHCI_INT_CMD_MASK | SDHCI_INT_DATA_MASK, + sdhci_ctx->reg_base + SDHCI_SIGNAL_ENABLE); + } + + return 0; +} + +void sdhc_irq_cb(struct sdhci_common *sdhci_ctx) +{ + uint32_t irq_status; + + /* save IRQ status */ + irq_status = sys_read32(sdhci_ctx->reg_base + SDHCI_INT_STATUS); + + /* clean irq status */ + sys_write32(irq_status, sdhci_ctx->reg_base + SDHCI_INT_STATUS); + LOG_DBG("sdhc:irq: status %08x", irq_status); + + k_event_post(&sdhci_ctx->irq_event, irq_status); +} diff --git a/drivers/sdhc/sdhci_common.h b/drivers/sdhc/sdhci_common.h new file mode 100644 index 00000000000000..2e9a140626dba8 --- /dev/null +++ b/drivers/sdhc/sdhci_common.h @@ -0,0 +1,509 @@ +/* + * Copyright (c) 2024 EPAM Systems + * + * SPDX-License-Identifier: Apache-2.0 + * + */ + +#ifndef DRIVERS_SDHC_SDHCI_COMMON_H_ +#define DRIVERS_SDHC_SDHCI_COMMON_H_ + +/** + * @brief SD Host Controller (SDHC) generic implementation module according to + * SD Host Controller Standard (Simplified) Specification Version 3.0 + * https://www.sdcard.org/developers/sd-standard-overview/host-controllers/ + * + * It supports: + * - Only SDHC Specification (Simplified) Version 3.0 + * - PIO transfers + * - SDMA transfers + * - ADMA2 transfers (32-bits) + * - SD card specification only + * - SDHC reset + * - SDCLK clock frequency configuration + * - SD Bus Voltage Select + * - v3.3 and v1.8 signal voltages switch + * - Legacy, SDR12, SDR25, DDR50 timings configuration + * + * Limitations + * - no tuning support + * - SDR50 may work, without tuning + * - no presets support + */ + +/* Registers */ +#define SDHCI_DMA_ADDRESS 0x00 /* 32bit */ +#define SDHCI_BLOCK_SIZE 0x04 /* 16bit */ +#define SDHCI_BLOCK_COUNT 0x06 /* 16bit */ +#define SDHCI_ARGUMENT_1 0x08 /* 32bit */ +#define SDHCI_TRANSFER_MODE 0x0C /* 16bit */ +#define SDHCI_COMMAND 0x0E /* 16bit */ +#define SDHCI_RESPONSE 0x10 /* 32bit */ +#define SDHCI_RESPONSE1 0x14 /* 32bit */ +#define SDHCI_RESPONSE2 0x18 /* 32bit */ +#define SDHCI_RESPONSE3 0x1C /* 32bit */ +#define SDHCI_BUFFER 0x20 /* 32bit */ +#define SDHCI_PRESENT_STATE 0x24 /* 32bit */ +#define SDHCI_HOST_CONTROL 0x28 /* 8bit */ +#define SDHCI_POWER_CONTROL 0x29 /* 8bit */ +#define SDHCI_CLOCK_CONTROL 0x2C /* 16bit */ +#define SDHCI_TIMEOUT_CONTROL 0x2E /* 8bit */ +#define SDHCI_SOFTWARE_RESET 0x2F /* 8bit */ +#define SDHCI_INT_STATUS 0x30 /* 32bit Error[31,16] Normal[15,0] */ +#define SDHCI_INT_ENABLE 0x34 /* 32bit Error[31,16] Normal[15,0] */ +#define SDHCI_SIGNAL_ENABLE 0x38 /* 32bit Error[31,16] Normal[15,0]*/ +#define SDHCI_HOST_CONTROL2 0x3E /* 16bit */ +#define SDHCI_CAPABILITIES 0x40 /* 32bit */ +#define SDHCI_CAPABILITIES_1 0x44 /* 32bit */ +#define SDHCI_ADMA_ADDRESS_LOW 0x58 /* 32bit */ +#define SDHCI_HOST_VERSION 0xFE /* 16bit */ + +/* SDHCI_BLOCK_SIZE */ +#define SDHCI_DEFAULT_BOUNDARY_ARG (0x7) +#define SDHC_SDMA_XFER_MAX ((1 << SDHCI_DEFAULT_BOUNDARY_ARG) * 4096) +#define SDHCI_MAKE_BLKSZ(sdma_boundary, blksz) ((((sdma_boundary) & 0x7) << 12) | ((blksz) & 0xFFF)) + +/* SDHCI_TRANSFER_MODE */ +#define SDHCI_TRNS_DMA BIT(0) +#define SDHCI_TRNS_BLK_CNT_EN BIT(1) +#define SDHCI_TRNS_ACMD12 BIT(2) +#define SDHCI_TRNS_ACMD23 BIT(3) +#define SDHCI_TRNS_READ BIT(4) +#define SDHCI_TRNS_MULTI BIT(5) + +/* SDHCI_COMMAND */ +#define SDHCI_CMD_RESP_MASK GENMASK(1, 0) +#define SDHCI_CMD_RESP_NONE 0x0 +#define SDHCI_CMD_RESP_LONG 0x1 +#define SDHCI_CMD_RESP_SHORT 0x2 +#define SDHCI_CMD_RESP_SHORT_BUSY 0x3 +#define SDHCI_CMD_CRC BIT(3) +#define SDHCI_CMD_INDEX BIT(4) +#define SDHCI_CMD_DATA BIT(5) +#define SDHCI_CMD_TYPE_MASK GENMASK(7, 6) +#define SDHCI_CMD_TYPE_SHIFT 6 +#define SDHCI_CMD_TYPE_NORMAL 0x0 +#define SDHCI_CMD_TYPE_RESUME 0x1 +#define SDHCI_CMD_TYPE_SUSPEND 0x2 +#define SDHCI_CMD_TYPE_ABORT 0x3 +#define SDHCI_CMD_OP_MASK GENMASK(13, 8) + +#define SDHCI_MAKE_CMD(op, flags) ((((op) << 8) & SDHCI_CMD_OP_MASK) | ((flags) & 0xff)) + +/* SDHCI_PRESENT_STATE */ +#define SDHCI_CMD_INHIBIT BIT(0) +#define SDHCI_DATA_INHIBIT BIT(1) +#define SDHCI_DAT_ACTIVE BIT(2) +#define SDHCI_DOING_WRITE BIT(8) +#define SDHCI_DOING_READ BIT(9) +#define SDHCI_BUF_WR_ENABLE BIT(10) +#define SDHCI_BUF_RD_ENABLE BIT(11) +#define SDHCI_CARD_PRESENT BIT(16) +#define SDHCI_CARD_STATE_STABLE BIT(17) +#define SDHCI_CARD_DETECT_PIN_LEVEL BIT(18) +#define SDHCI_WRITE_PROTECT BIT(19) +#define SDHCI_DATA_LVL_MASK GENMASK(23, 20) +#define SDHCI_DATA_0_LVL_MASK BIT(20) +#define SDHCI_CMD_LVL_MASK BIT(24) + +/* SDHCI_POWER_CONTROL */ +#define SDHCI_POWER_ON BIT(0) +#define SDHCI_POWER_180 0x0A +#define SDHCI_POWER_300 0x0C +#define SDHCI_POWER_330 0x0E + +/* SDHCI_HOST_CONTROL */ +#define SDHCI_CTRL_LED BIT(0) +#define SDHCI_CTRL_4BITBUS BIT(1) +#define SDHCI_CTRL_HISPD BIT(2) +#define SDHCI_CTRL_DMA_SHIFT 3 +#define SDHCI_CTRL_DMA_MASK GENMASK(4, 3) +#define SDHCI_CTRL_SDMA 0x0 +#define SDHCI_CTRL_ADMA2 0x2 +#define SDHCI_CTRL_8BITBUS BIT(5) +#define SDHCI_CTRL_CD_TEST_INS BIT(6) +#define SDHCI_CTRL_CD_TEST BIT(7) + +/* SDHCI_CLOCK_CONTROL */ +#define SDHCI_CLOCK_INT_EN BIT(0) +#define SDHCI_CLOCK_INT_STABLE BIT(1) +#define SDHCI_CLOCK_CARD_EN BIT(2) +#define SDHCI_PROG_CLOCK_MODE BIT(5) +#define SDHCI_DIVIDER_SHIFT 8 +#define SDHCI_DIVIDER_HI_SHIFT 6 +#define SDHCI_DIV_MASK GENMASK(7, 0) +#define SDHCI_DIV_HI_MASK GENMASK(9, 8) +#define SDHCI_MAX_DIV_SPEC_300 2046 + +/* SDHCI_SOFTWARE_RESET */ +#define SDHCI_RESET_ALL BIT(0) +#define SDHCI_RESET_CMD BIT(1) +#define SDHCI_RESET_DATA BIT(2) + +/* SDHCI_INT_STATUS, SDHCI_INT_ENABLE, SDHCI_SIGNAL_ENABLE */ +#define SDHCI_INT_CMD_COMPLETE BIT(0) +#define SDHCI_INT_XFER_COMPLETE BIT(1) +#define SDHCI_INT_DMA_END BIT(3) +#define SDHCI_INT_BUF_WR_READY BIT(4) +#define SDHCI_INT_BUF_RD_READY BIT(5) +#define SDHCI_INT_CARD_INSERT BIT(6) +#define SDHCI_INT_CARD_REMOVE BIT(7) +#define SDHCI_INT_CARD_INT BIT(8) +#define SDHCI_INT_ERROR BIT(15) +#define SDHCI_INT_TIMEOUT BIT(16) +#define SDHCI_INT_CRC BIT(17) +#define SDHCI_INT_END_BIT BIT(18) +#define SDHCI_INT_INDEX BIT(19) +#define SDHCI_INT_DATA_TIMEOUT BIT(20) +#define SDHCI_INT_DATA_CRC BIT(21) +#define SDHCI_INT_DATA_END_BIT BIT(22) +#define SDHCI_INT_BUS_POWER BIT(23) +#define SDHCI_INT_ACMD12ERR BIT(24) +#define SDHCI_INT_ADMA_ERROR BIT(25) + +#define SDHCI_INT_NORMAL_MASK GENMASK(14, 0) +#define SDHCI_INT_ERROR_MASK GENMASK(31, 15) + +#define SDHCI_INT_CMD_MASK \ + (SDHCI_INT_CMD_COMPLETE | SDHCI_INT_TIMEOUT | SDHCI_INT_CRC | SDHCI_INT_END_BIT | \ + SDHCI_INT_INDEX) +#define SDHCI_INT_DATA_MASK \ + (SDHCI_INT_XFER_COMPLETE | SDHCI_INT_DMA_END | SDHCI_INT_BUF_RD_READY | \ + SDHCI_INT_BUF_WR_READY | SDHCI_INT_DATA_TIMEOUT | SDHCI_INT_DATA_CRC | \ + SDHCI_INT_DATA_END_BIT | SDHCI_INT_ADMA_ERROR) +#define SDHCI_INT_ALL_MASK ((uint32_t)-1) + +/* SDHCI_HOST_CONTROL2 */ +#define SDHCI_CTRL_UHS_MASK GENMASK(2, 0) +#define SDHCI_CTRL_UHS_SDR12 0x0 +#define SDHCI_CTRL_UHS_SDR25 0x1 +#define SDHCI_CTRL_UHS_SDR50 0x2 +#define SDHCI_CTRL_UHS_SDR104 0x3 +#define SDHCI_CTRL_UHS_DDR50 0x4 +#define SDHCI_CTRL_HS400 0x5 /* Non-standard */ +#define SDHCI_CTRL_VDD_180 BIT(3) +#define SDHCI_CTRL_DRV_TYPE_MASK GENMASK(5, 4) +#define SDHCI_CTRL_DRV_TYPE_B 0x0 +#define SDHCI_CTRL_DRV_TYPE_A 0x1 +#define SDHCI_CTRL_DRV_TYPE_C 0x2 +#define SDHCI_CTRL_DRV_TYPE_D 0x3 +#define SDHCI_CTRL_EXEC_TUNING BIT(6) +#define SDHCI_CTRL_TUNED_CLK BIT(7) +#define SDHCI_CTRL_PRESET_VAL_ENABLE BIT(15) + +/* SDHCI_CAPABILITIES */ +#define SDHCI_TIMEOUT_CLK_MASK GENMASK(5, 0) +#define SDHCI_TIMEOUT_CLK_UNIT BIT(7) +#define SDHCI_CLOCK_BASE_MASK GENMASK(15, 8) +#define SDHCI_MAX_BLOCK_MASK GENMASK(17, 16) +#define SDHCI_CAN_DO_8BIT BIT(18) +#define SDHCI_CAN_DO_ADMA2 BIT(19) +#define SDHCI_CAN_DO_HISPD BIT(21) +#define SDHCI_CAN_DO_SDMA BIT(22) +#define SDHCI_CAN_VDD_330 BIT(24) +#define SDHCI_CAN_VDD_300 BIT(25) +#define SDHCI_CAN_VDD_180 BIT(26) +#define SDHCI_CAN_64BIT BIT(28) +#define SDHCI_SLOT_TYPE_MASK GENMASK(31, 30) + +/* SDHCI_CAPABILITIES_1 */ +#define SDHCI_SUPPORT_SDR50 BIT(0) +#define SDHCI_SUPPORT_SDR104 BIT(1) +#define SDHCI_SUPPORT_DDR50 BIT(2) +#define SDHCI_SUPPORT_DRV_A BIT(4) +#define SDHCI_SUPPORT_DRV_C BIT(5) +#define SDHCI_SUPPORT_DRV_D BIT(6) +#define SDHCI_RETUNING_TIMER_COUNT_MASK GENMASK(11, 8) +#define SDHCI_USE_SDR50_TUNING BIT(13) +#define SDHCI_RETUNING_MODE_MASK GENMASK(15, 14) +#define SDHCI_CLOCK_MUL_MASK GENMASK(23, 16) + +/* SDHCI_HOST_VERSION */ +#define SDHCI_VENDOR_VER_MASK GENMASK(15, 8) +#define SDHCI_VENDOR_VER_SHIFT 8 +#define SDHCI_SPEC_VER_MASK GENMASK(7, 0) +#define SDHCI_SPEC_100 0 +#define SDHCI_SPEC_200 1 +#define SDHCI_SPEC_300 2 +#define SDHCI_SPEC_400 3 + +/* ADMA2_DESC2 descriptor */ +#define SDHC_ADMA2_DESC_VALID BIT(0) +#define SDHC_ADMA2_DESC_END BIT(1) +#define SDHC_ADMA2_DESC_INT BIT(2) +#define SDHC_ADMA2_DESC_ACT1 BIT(4) +#define SDHC_ADMA2_DESC_ACT2 BIT(5) +#define SDHC_ADMA2_DESC_ACT_NOP 0 +#define SDHC_ADMA2_DESC_ACT_RSV SDHC_ADMA2_DESC_ACT1 +#define SDHC_ADMA2_DESC_ACT_TRAN SDHC_ADMA2_DESC_ACT2 +#define SDHC_ADMA2_DESC_ACT_LINK SDHC_ADMA2_DESC_ACT2 | SDHC_ADMA2_DESC_ACT1 + +#define SDHC_ADMA2_DESC_MAX_LEN CONFIG_SDHCI_GENERIC_ADMA2_DESC_MAX_LEN + +struct sdhc_adma_desc { + uint16_t cmd; + uint16_t len; + uint32_t addr; +} __packed __aligned(4); + +/** + * @brief SDHC DMA type selection codes. + * + * SDHC v3.0 defines SDMA and ADMA2 modes. + */ +enum sdhc_dma_select { + SDHC_DMA_SDMA = 0x0, + SDHC_DMA_ADMA2 = 0x2, +}; + +/** + * @brief SDHC common data structure. + * + * The SDHC drivers should configure @ref reg_base IO base address first and pass + * pointer on @ref sdhci_common to all SDHC generic APIs. + */ +struct sdhci_common { + mm_reg_t reg_base; /**< SDHC IO base address */ + + /** @cond INTERNAL_HIDDEN */ + uint32_t version; /* SDHC version */ + uint32_t caps; /* SDHC Capabilities register bits[31,0] */ + uint32_t caps1; /* SDHC Capabilities register bits[63,32] */ + uint32_t max_clk; /* Maximum Base Clock frequency */ + uint32_t clk_mul; /* Clock Multiplier value */ + bool f_auto_cmd12; /* flag to Auto CMD12 Enable */ + bool f_use_dma; /* flag to enable DMA */ + bool f_use_irq; /* flag to enable IRQs */ + enum sdhc_dma_select dma_mode; /* selected DMA mode */ + struct sdhc_adma_desc *adma_descs; /* SDHC ADMA2 desc table */ + uint32_t adma_descs_num; /* SDHC ADMA2 desc table size */ + struct k_event irq_event; /* IRQ events */ + /** @endcond */ +}; + +/** + * @brief Initialize SDHC capabilities. + * + * Reads and checks SDHC HW version and capabilities. Fills @ref sdhc_host_props and + * @ref sdhc_host_caps. The SDHC drivers should call this API first and then correct + * @ref sdhc_host_props depending on SDHC HW specific implementation and SW configuration. + * + * @param sdhci_ctx: SDHC data context. + * @param props: generic SD host controller properties to fill. + * + * @retval 0 reset succeeded. + * @retval -ENOTSUP: SDHC HW is not supported. + */ +int sdhci_init_caps(struct sdhci_common *sdhci_ctx, struct sdhc_host_props *props); + +/** + * @brief Enable SDHC SDMA. + * + * Enables SDHC SDMA. + * The SDHC drivers should call this API after @ref sdhci_init_caps. + * + * @param sdhci_ctx: SDHC data context. + * + * @retval 0 reset succeeded. + */ +int sdhci_enable_sdma(struct sdhci_common *sdhci_ctx); + +/** + * @brief Enable SDHC ADMA2. + * + * Enables SDHC ADMA2. The consumer should provide SDHC with preallocated table + * of ADMA2 descriptors. The @ref descs_table should be allocated with @ref __nocache attribute + * if nocache memory is supported by platform. + * The SDHC drivers should call this API after @ref sdhci_init_caps. + * + * @param sdhci_ctx: SDHC data context. + * @param descs_table: ADMA2 descriptors table. + * @param descs_table_size: ADMA2 descriptors table size. + * + * @retval 0 reset succeeded. + * @retval -EINVAL: ADMA2 descriptors table is invalid. + */ +int sdhci_enable_adma2(struct sdhci_common *sdhci_ctx, struct sdhc_adma_desc *descs_table, + uint32_t descs_table_size); + +/** + * @brief Enable SDHC auto CMD12 functionality. + * + * Enables SDHC auto CMD12 functionality in Transfer Mode Register for + * multi-block read/write requests. By default the auto CMD12 is disabled. + * The SDHC drivers should call this API after @ref sdhci_init_caps. + * + * @param sdhci_ctx: SDHC data context. + * @param enable: true to enable auto CMD12. + * + * @retval 0 reset succeeded. + * @retval -ENOTSUP: SDHC HW is not supported. + */ +static inline void sdhci_enable_auto_cmd12(struct sdhci_common *sdhci_ctx, bool enable) +{ + sdhci_ctx->f_auto_cmd12 = enable; +} + +/** + * @brief Enable SDHC IRQ support. + * + * Enables SDHC IRQ support for command/data transfers. + * The SDHC drivers should call this API after @ref sdhci_init_caps. + * The consumer is responsible for SDHC IRQ requesting and enabling and should call + * @ref sdhc_irq_cb from its IRQ handler. + * + * @param sdhci_ctx: SDHC data context. + * @param enable: true to enable IRQ support. + */ +static inline void sdhc_enable_irq(struct sdhci_common *sdhci_ctx, bool enable) +{ + sdhci_ctx->f_use_irq = enable; +} + +/** + * @brief Initialize SDHC hardware. + * + * Performs basic SDHC HW initialization, including "Software Reset For All" operation. + * + * @param sdhci_ctx: SDHC data context. + * + * @retval 0 reset succeeded. + */ +int sdhci_init(struct sdhci_common *sdhci_ctx); + +/** + * @brief Get CD status from SDHC hardware. + * + * Gets CD status from SDHCI_PRESENT_STATE "Card Inserted" field. + * + * @param sdhci_ctx: SDHC data context + * + * @retval 0 card no detected. + * @retval 1 card detected. + */ +int sdhci_get_cd(struct sdhci_common *sdhci_ctx); + +/** + * @brief Software Reset SDHC hardware. + * + * Resets SDHC hardware depending @ref mask: + * bit 0 - Software Reset For All; + * bit 1 - Software Reset For CMD Line; + * bit 2 - Software Reset For DAT Line. + * + * @param sdhci_ctx: SDHC data context. + * @param mask: reset mask. + * + * @retval 0 reset succeeded. + * @retval -ETIMEDOUT reset timed out. + */ +int sdhci_reset(struct sdhci_common *sdhci_ctx, uint8_t mask); + +/** + * @brief Get DAT0 line busy state from SDHC hardware. + * + * @param sdhci_ctx: SDHC data context. + * + * @retval 0 DAT0 free. + * @retval 1 DAT0 busy. + */ +int sdhci_is_card_busy(struct sdhci_common *sdhci_ctx); + +/** + * @brief Configure SDCLK Frequency and enable Internal and SD clocks in SDHC hardware. + * + * Disables Internal and SD clocks If @ref clock is 0. + * Configures SDCLK Frequency and enables Internal and SD clocks If @ref clock is not 0. + * It is responsibility of the SDHC driver to check @ref clock valid range. + * + * @param sdhci_ctx: SDHC data context. + * @param clock: SDCLK Frequency, if 0 - disable clocks. + * + * @retval 0 on success. + * @retval -ETIMEDOUT wait for "Internal Clock Stable" timeout. + */ +int sdhci_set_clock(struct sdhci_common *sdhci_ctx, enum sdhc_clock_speed clock); + +/** + * @brief Set SD Bus Voltage and power on SD bus in SDHC hardware. + * + * Disables SD bus power If @ref bus_voltage is 0. It also disables SD clock. + * Sets SD Bus Voltage and power on SD bus If @ref bus_voltage is not 0. It also enables SD clock + * if it was configured before. + * It is responsibility of the SDHC driver to check @ref bus_voltage valid range. + * + * @param sdhci_ctx: SDHC data context. + * @param bus_voltage: SD Bus Voltage, if 0 - power off SD bus. + */ +void sdhci_set_bus_power(struct sdhci_common *sdhci_ctx, enum sd_voltage bus_voltage); + +/** + * @brief Set SD Bus width in SDHC hardware. + * + * It is responsibility of the SDHC driver to check @ref bus_width valid range. + * + * @param sdhci_ctx: SDHC data context. + * @param bus_width: SD Bus width. + * + * @retval 0 on success. + * @retval -ENOTSUP if @ref bus_width is unsupported. + */ +int sdhci_set_bus_width(struct sdhci_common *sdhci_ctx, enum sdhc_bus_width bus_width); + +/** + * @brief Configure Signaling voltage in SDHC hardware. + * + * Configures Signaling voltage in SDHC hardware between @ref SD_VOL_3_3_V and @ref SD_VOL_1_8_V. + * It is responsibility of the SDHC driver to check @ref signal_voltage valid range. + * + * @param sdhci_ctx: SDHC data context. + * @param signal_voltage: SD Bus Signaling voltage. + * + * @retval 0 on success. + * @retval -ENOTSUP if @ref signal_voltage is unsupported. + * @retval -EIO if voltage switch failed. + */ +int sdhci_set_voltage(struct sdhci_common *sdhci_ctx, enum sd_voltage signal_voltage); + +/** + * @brief Select UHS mode timings in SDHC hardware. + * + * Configures UHS mode timings in SDHCI_HOST_CONTROL2 "UHS Mode Select" field. + * The SD clock is kept disabled while UHS timing mode is changed. + * It is responsibility of the SDHC driver to check @ref signal_voltage valid range. + * + * @param sdhci_ctx: SDHC data context. + * @param timing: UHS timing mode. + * + * @retval 0 on success. + * @retval -ENOTSUP if @ref timing is unsupported. + */ +int sdhci_set_uhs_timing(struct sdhci_common *sdhci_ctx, enum sdhc_timing_mode timing); + +/** + * @brief Send SD command to SDHC hardware. + * + * @param sdhci_ctx: SDHC data context. + * @param cmd: SD command + * @param cmd: SD command data, optional + * + * @retval 0 on success. + * @retval -EBUSY if CMD or DAT lines busy + * @retval -EINVAL if command response type in unknown + * @retval -EIO if error detected during command or data processing + * @retval -ETIMEDOUT reset timed out. + */ +int sdhci_send_req(struct sdhci_common *sdhci_ctx, struct sdhc_command *cmd, + struct sdhc_data *data); + +/** + * @brief SDHC IRQ handler callback. + * + * The consumer is responsible for SDHC IRQ requesting and enabling and should call + * @ref sdhc_irq_cb from its IRQ handler. + * + * @param sdhci_ctx: SDHC data context. + */ +void sdhc_irq_cb(struct sdhci_common *sdhci_ctx); + +#endif /* DRIVERS_SDHC_SDHCI_COMMON_H_ */ diff --git a/dts/arm64/broadcom/bcm2712.dtsi b/dts/arm64/broadcom/bcm2712.dtsi new file mode 100644 index 00000000000000..a41551276cf953 --- /dev/null +++ b/dts/arm64/broadcom/bcm2712.dtsi @@ -0,0 +1,113 @@ +/* + * Copyright 2024 Myeonghyeon Park + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +/ { + cpus { + #address-cells = <1>; + #size-cells = <0>; + + cpu@0 { + device_type = "cpu"; + compatible = "arm,cortex-a76"; + reg = <0>; + }; + }; + + interrupt-parent = <&gic>; + + timer { + compatible = "arm,armv8-timer"; + interrupts = , + , + , + ; + }; + + soc { + #address-cells = <2>; + #size-cells = <1>; + + sram0: memory@200000 { + device_type = "memory"; + compatible = "mmio-sram"; + reg = <0x0 0x200000 0x80000>; + }; + + gic: interrupt-controller@107fff9000 { + compatible = "arm,gic-v2", "arm,gic"; + reg = <0x10 0x7fff9000 0x1000>, + <0x10 0x7fffa000 0x2000>; + interrupt-controller; + #interrupt-cells = <4>; + status = "okay"; + }; + + gpio2@107d517c00 { + compatible = "simple-bus"; + reg = <0x10 0x7d517c00 0x40>; + + #address-cells = <1>; + #size-cells = <0>; + gio_aon: gpio@0 { + compatible = "brcm,brcmstb-gpio"; + reg = <0>; + gpio-controller; + #gpio-cells = <2>; + ngpios = <17>; + status = "disabled"; + }; + }; + + uart10: serial@107d001000 { + compatible = "arm,pl011"; + reg = <0x10 0x7d001000 0x200>; + interrupts = ; + interrupt-names = "irq_121"; + clocks = <&clk_uart>; + status = "disabled"; + }; + }; + + axi: axi { + compatible = "simple-bus"; + #address-cells = <2>; + #size-cells = <2>; + + sdio1: mmc@1000fff000 { + compatible = "brcm,bcm2712-sdhci"; + reg = <0x10 0x00fff000 0x0 0x260>, + <0x10 0x00fff400 0x0 0x200>, + <0x10 0x015040b0 0x0 0x4>, // Bus isolation control + <0x10 0x015200f0 0x0 0x24>; // LCPLL control misc0-8 + reg-names = "host", "cfg", "busisol", "lcpll"; + interrupts = ; + clocks = <&clk_emmc2>; + status = "disabled"; + }; + }; + + clocks { + clk_uart: clk_uart { + compatible = "fixed-clock"; + clock-frequency = <44236800>; + #clock-cells = <0>; + }; + + clk_emmc2: clk_emmc2 { + #clock-cells = <0>; + compatible = "fixed-clock"; + clock-frequency = <200000000>; + }; + }; +}; diff --git a/dts/bindings/gpio/brcm,brcmstb-gpio.yaml b/dts/bindings/gpio/brcm,brcmstb-gpio.yaml new file mode 100644 index 00000000000000..55f58a9f3fdd3e --- /dev/null +++ b/dts/bindings/gpio/brcm,brcmstb-gpio.yaml @@ -0,0 +1,19 @@ +# Copyright (c) 2024 Junho Lee +# SPDX-License-Identifier: Apache-2.0 + +description: BRCMSTB GPIO + +compatible: "brcm,brcmstb-gpio" + +include: [gpio-controller.yaml, base.yaml] + +properties: + reg: + required: true + + "#gpio-cells": + const: 2 + +gpio-cells: + - pin + - flags diff --git a/dts/bindings/sdhc/brcm,bcm2712-sdhci.yaml b/dts/bindings/sdhc/brcm,bcm2712-sdhci.yaml new file mode 100644 index 00000000000000..472ee43b63b7f0 --- /dev/null +++ b/dts/bindings/sdhc/brcm,bcm2712-sdhci.yaml @@ -0,0 +1,47 @@ +# Copyright (c) 2024 EPAM Systems +# SPDX-License-Identifier: Apache-2.0 +description: Broadcom BCM2712 SDHC v3.0 (MMC) + +compatible: "brcm,bcm2712-sdhci" + +include: [sdhc.yaml] + +properties: + clocks: + required: true + + reg: + required: true + + cd-gpios: + type: phandle-array + description: Card Detect pin + + vmmc-supply: + type: phandle + description: | + Supply for the card power + + vqmmc-supply: + type: phandle + description: | + Supply for the bus IO line power, such as a level shifter. + If the level shifter is controlled by a GPIO line, this shall + be modeled as a "regulator-fixed" with a GPIO line for + switching the level shifter on/off. + + bus-width: + type: int + default: 1 + description: | + Bus width for SDMMC access, defaults to the minimum necessary + number of bus lines + enum: + - 1 + - 4 + - 8 + + non-removable: + type: boolean + description: | + Non-removable slots (like eMMC), which are assumed to always be present. diff --git a/modules/fatfs/zephyr_fatfs_config.h b/modules/fatfs/zephyr_fatfs_config.h index 7ced4f74cdf4a1..47d33d6db17a95 100644 --- a/modules/fatfs/zephyr_fatfs_config.h +++ b/modules/fatfs/zephyr_fatfs_config.h @@ -71,6 +71,16 @@ #define FF_FS_TIMEOUT K_FOREVER #endif /* defined(CONFIG_FS_FATFS_REENTRANT) */ +#if defined(CONFIG_FS_FATFS_LBA64) +#undef FF_LBA64 +#define FF_LBA64 CONFIG_FS_FATFS_LBA64 +#endif /* defined(CONFIG_FS_FATFS_LBA64) */ + +#if defined(CONFIG_FS_MULTI_PARTITION) +#undef FF_MULTI_PARTITION +#define FF_MULTI_PARTITION CONFIG_FS_MULTI_PARTITION +#endif /* defined(CONFIG_FS_MULTI_PARTITION) */ + /* * These options are override from default values, but have no Kconfig * options. diff --git a/samples/subsys/fs/fs_sample/boards/rpi_5.conf b/samples/subsys/fs/fs_sample/boards/rpi_5.conf new file mode 100644 index 00000000000000..ba1344315f84bb --- /dev/null +++ b/samples/subsys/fs/fs_sample/boards/rpi_5.conf @@ -0,0 +1,9 @@ +CONFIG_MAIN_STACK_SIZE=4096 +CONFIG_FS_SAMPLE_CREATE_SOME_ENTRIES=n +CONFIG_FS_FATFS_MOUNT_MKFS=n + +CONFIG_SHELL=y +CONFIG_FILE_SYSTEM_SHELL=y + +CONFIG_LOG_MODE_MINIMAL=y +CONFIG_HEAP_MEM_POOL_SIZE=2097152 diff --git a/snippets/rpi_5_xen_domd/README.rst b/snippets/rpi_5_xen_domd/README.rst new file mode 100644 index 00000000000000..37a7c271e90970 --- /dev/null +++ b/snippets/rpi_5_xen_domd/README.rst @@ -0,0 +1,17 @@ +.. _rpi_5_xen_domd: + +RPI 5 Xen DomD: snippet for XEN HW domain +######################################### + +Overview +******** + +This snippet allows user to build Zephyr `xenvm` with RPI 5 hardware support as +a Xen hardware domain (DomD) to demonstrate how RPI 5 hardware can be passed to Xen domain. +Only GPIO LED is supported for now. + +For example: + +.. code-block:: console + + west build -b xenvm -S rpi_5_xen_domd samples/basic/blinky diff --git a/snippets/rpi_5_xen_domd/rpi_5_xen_domd.conf b/snippets/rpi_5_xen_domd/rpi_5_xen_domd.conf new file mode 100644 index 00000000000000..cd989de2f6250d --- /dev/null +++ b/snippets/rpi_5_xen_domd/rpi_5_xen_domd.conf @@ -0,0 +1,3 @@ +CONFIG_ARM64_VA_BITS_40=y +CONFIG_ARM64_PA_BITS_40=y +CONFIG_UART_INTERRUPT_DRIVEN=n diff --git a/snippets/rpi_5_xen_domd/rpi_5_xen_domd.overlay b/snippets/rpi_5_xen_domd/rpi_5_xen_domd.overlay new file mode 100644 index 00000000000000..0074cdae2464bc --- /dev/null +++ b/snippets/rpi_5_xen_domd/rpi_5_xen_domd.overlay @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2024 EPAM Systems. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +/ { + compatible = "raspberrypi,5-model-b-domd", "raspberrypi,5-model-b", "brcm,bcm2712"; + model = "Raspberry Pi 5 Xen DomD"; + + chosen { + zephyr,shell-uart = &xen_hvc; + }; + + aliases { + led0 = &led_act; + }; + + leds { + compatible = "gpio-leds"; + + led_act: led-act { + gpios = <&gio_aon 9 GPIO_ACTIVE_LOW>; + label = "ACT"; + }; + }; + + gpio2@107d517c00 { + compatible = "simple-bus"; + reg = <0x10 0x7d517c00 0x0 0x40>; + + #address-cells = <1>; + #size-cells = <0>; + gio_aon: gpio@0 { + compatible = "brcm,brcmstb-gpio"; + reg = <0>; + gpio-controller; + #gpio-cells = <2>; + ngpios = <17>; + }; + }; +}; diff --git a/snippets/rpi_5_xen_domd/snippet.yml b/snippets/rpi_5_xen_domd/snippet.yml new file mode 100644 index 00000000000000..ef650dc0c2328b --- /dev/null +++ b/snippets/rpi_5_xen_domd/snippet.yml @@ -0,0 +1,4 @@ +name: rpi_5_xen_domd +append: + EXTRA_DTC_OVERLAY_FILE: rpi_5_xen_domd.overlay + EXTRA_CONF_FILE: rpi_5_xen_domd.conf diff --git a/soc/arm64/bcm2712/CMakeLists.txt b/soc/arm64/bcm2712/CMakeLists.txt new file mode 100644 index 00000000000000..733abbebd911a3 --- /dev/null +++ b/soc/arm64/bcm2712/CMakeLists.txt @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: Apache-2.0 +zephyr_sources_ifdef(CONFIG_ARM_MMU mmu_regions.c) + +set(SOC_LINKER_SCRIPT ${ZEPHYR_BASE}/include/zephyr/arch/arm64/scripts/linker.ld CACHE INTERNAL "") diff --git a/soc/arm64/bcm2712/Kconfig.defconfig b/soc/arm64/bcm2712/Kconfig.defconfig new file mode 100644 index 00000000000000..4461c7ad69c081 --- /dev/null +++ b/soc/arm64/bcm2712/Kconfig.defconfig @@ -0,0 +1,17 @@ +# Copyright 2024 Junho Lee +# SPDX-License-Identifier: Apache-2.0 + +if SOC_BCM2712 + +config SOC + default "bcm2712" + +config NUM_IRQS + int + default 512 + +config SYS_CLOCK_HW_CYCLES_PER_SEC + int + default 54000000 + +endif diff --git a/soc/arm64/bcm2712/Kconfig.soc b/soc/arm64/bcm2712/Kconfig.soc new file mode 100644 index 00000000000000..e1d05aaa92bcdd --- /dev/null +++ b/soc/arm64/bcm2712/Kconfig.soc @@ -0,0 +1,8 @@ +# Copyright 2024 Junho Lee +# SPDX-License-Identifier: Apache-2.0 + +config SOC_BCM2712 + bool "bcm2712" + select ARM64 + select CPU_CORTEX_A76 + select ARM_ARCH_TIMER if SYS_CLOCK_EXISTS diff --git a/soc/arm64/bcm2712/mmu_regions.c b/soc/arm64/bcm2712/mmu_regions.c new file mode 100644 index 00000000000000..ea3314ffc2ed14 --- /dev/null +++ b/soc/arm64/bcm2712/mmu_regions.c @@ -0,0 +1,26 @@ +/* + * Copyright 2024 Junho Lee + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +static const struct arm_mmu_region mmu_regions[] = { + MMU_REGION_FLAT_ENTRY("GIC", + DT_REG_ADDR_BY_IDX(DT_INST(0, arm_gic), 0), + DT_REG_SIZE_BY_IDX(DT_INST(0, arm_gic), 0), + MT_DEVICE_nGnRnE | MT_P_RW_U_NA | MT_DEFAULT_SECURE_STATE), + + MMU_REGION_FLAT_ENTRY("GIC", + DT_REG_ADDR_BY_IDX(DT_INST(0, arm_gic), 1), + DT_REG_SIZE_BY_IDX(DT_INST(0, arm_gic), 1), + MT_DEVICE_nGnRnE | MT_P_RW_U_NA | MT_DEFAULT_SECURE_STATE), +}; + +const struct arm_mmu_config mmu_config = { + .num_regions = ARRAY_SIZE(mmu_regions), + .mmu_regions = mmu_regions, +}; diff --git a/soc/arm64/bcm2712/soc.yml b/soc/arm64/bcm2712/soc.yml new file mode 100644 index 00000000000000..ce95e07ac68340 --- /dev/null +++ b/soc/arm64/bcm2712/soc.yml @@ -0,0 +1,4 @@ +series: +- name: bcm2712 + socs: + - name: bcm2712 diff --git a/subsys/fs/Kconfig.fatfs b/subsys/fs/Kconfig.fatfs index e44ca1c781d919..584bc91fa5f933 100644 --- a/subsys/fs/Kconfig.fatfs +++ b/subsys/fs/Kconfig.fatfs @@ -229,6 +229,27 @@ config FS_FATFS_REENTRANT access for each volume. Will create a zephyr mutex object for each FatFs volume and a FatFs system mutex. +config FS_FATFS_LBA64 + bool "Support for 64-bit LBA" + depends on FS_FATFS_EXFAT + help + This option enables support for 64-bit LBA, which also + enables GPT support. + +config FS_MULTI_PARTITION + bool "Support for multiple volumes on the physical drive" + help + This option switches support for multiple volumes on the physical drive. + By default (0), each logical drive number is bound to the same physical + drive number and only an FAT volume found on the physical drive will be + mounted. When this function is enabled (1), each logical drive number can + be bound to arbitrary physical drive and partition listed + in the VolToPart[]. + For example, 2 FAT partition on SD disk (3) in terms of Zephyr: + {3, 1} - mount point "/0:" + {3, 2} - mount point "/1:" + The mount points have to be numbered in this case. + endmenu endif # FAT_FILESYSTEM_ELM diff --git a/tests/drivers/sdhc/src/main.c b/tests/drivers/sdhc/src/main.c index 1ee9b7a1ba9157..205b367a74174f 100644 --- a/tests/drivers/sdhc/src/main.c +++ b/tests/drivers/sdhc/src/main.c @@ -17,6 +17,27 @@ static struct sdhc_io io; K_SEM_DEFINE(card_sem, 0, 1); +/* Prepare IO settings for card */ +static void *sdhc_power_on(void) +{ + int ret; + + ret = sdhc_get_host_props(sdhc_dev, &props); + zassert_equal(ret, 0, "SDHC host props api call failed"); + + io.clock = props.f_min; + io.bus_mode = SDHC_BUSMODE_PUSHPULL; + io.power_mode = SDHC_POWER_ON; + io.bus_width = SDHC_BUS_WIDTH1BIT; + io.timing = SDHC_TIMING_LEGACY; + io.signal_voltage = SD_VOL_3_3_V; + + ret = sdhc_set_io(sdhc_dev, &io); + zassert_equal(ret, 0, "Setting io configuration failed"); + k_msleep(props.power_delay); + return NULL; +} + /* Resets SD host controller, verifies API */ ZTEST(sdhc, test_reset) { @@ -33,6 +54,8 @@ ZTEST(sdhc, test_host_props) { int ret; + zassert_true(device_is_ready(sdhc_dev), "SDHC device is not ready"); + /* Set all host properties to 0xFF */ props.f_max = 0xFF; props.f_min = 0xFF; @@ -56,6 +79,8 @@ ZTEST(sdhc, test_set_io) { int ret; + zassert_true(device_is_ready(sdhc_dev), "SDHC device is not ready"); + io.clock = props.f_min; io.bus_mode = SDHC_BUSMODE_PUSHPULL; io.power_mode = SDHC_POWER_ON; @@ -88,16 +113,12 @@ void sdhc_interrupt_cb(const struct device *dev, int source, const void *data) /* * Verify that the driver can detect a present SD card - * This test must run first, to ensure the card is present. */ -ZTEST(sdhc, test_0_card_presence) +ZTEST(sdhc, test_card_presence) { int ret; - io.clock = props.f_min; - ret = sdhc_set_io(sdhc_dev, &io); - zassert_equal(ret, 0, "Setting io configuration failed"); - k_msleep(props.power_delay); + zassert_true(device_is_ready(sdhc_dev), "SDHC device is not ready"); ret = sdhc_card_present(sdhc_dev); if (ret == 0) { @@ -126,6 +147,8 @@ ZTEST(sdhc, test_card_if_cond) int ret, resp; int check_pattern = SD_IF_COND_CHECK; + zassert_true(device_is_ready(sdhc_dev), "SDHC device is not ready"); + /* Toggle power to card, to clear state */ io.power_mode = SDHC_POWER_OFF; ret = sdhc_set_io(sdhc_dev, &io); @@ -172,4 +195,4 @@ ZTEST(sdhc, test_card_if_cond) } } -ZTEST_SUITE(sdhc, NULL, NULL, NULL, NULL, NULL); +ZTEST_SUITE(sdhc, NULL, sdhc_power_on, NULL, NULL, NULL);