diff --git a/bsp/hpmicro/hpm6750evk/board/eth_phy_port.c b/bsp/hpmicro/hpm6750evk/board/eth_phy_port.c index 6a096ddef572..b698c3c9bc7d 100644 --- a/bsp/hpmicro/hpm6750evk/board/eth_phy_port.c +++ b/bsp/hpmicro/hpm6750evk/board/eth_phy_port.c @@ -94,9 +94,9 @@ eth_phy_monitor_handle_t phy_monitor_handle = .phy_handle = s_gphys }; -static struct rt_phy_ops phy_ops; +static const struct rt_phy_ops phy_ops; -static rt_phy_status phy_init(void *object, rt_uint32_t phy_addr, rt_uint32_t src_clock_hz) +static rt_phy_status phy_init(rt_phy_t *phy, void *object, rt_uint32_t phy_addr, rt_uint32_t src_clock_hz) { return PHY_STATUS_OK; } diff --git a/bsp/hpmicro/hpm6750evkmini/board/eth_phy_port.c b/bsp/hpmicro/hpm6750evkmini/board/eth_phy_port.c index d025d1b3eb5c..5c11c501cfa3 100644 --- a/bsp/hpmicro/hpm6750evkmini/board/eth_phy_port.c +++ b/bsp/hpmicro/hpm6750evkmini/board/eth_phy_port.c @@ -94,9 +94,9 @@ eth_phy_monitor_handle_t phy_monitor_handle = .phy_handle = s_gphys }; -static struct rt_phy_ops phy_ops; +static const struct rt_phy_ops phy_ops; -static rt_phy_status phy_init(void *object, rt_uint32_t phy_addr, rt_uint32_t src_clock_hz) +static rt_phy_status phy_init(rt_phy_t *phy, void *object, rt_uint32_t phy_addr, rt_uint32_t src_clock_hz) { return PHY_STATUS_OK; } diff --git a/bsp/imxrt/libraries/drivers/drv_ksz8081.c b/bsp/imxrt/libraries/drivers/drv_ksz8081.c index 5ed5fa3b84be..672dc8db1398 100644 --- a/bsp/imxrt/libraries/drivers/drv_ksz8081.c +++ b/bsp/imxrt/libraries/drivers/drv_ksz8081.c @@ -108,7 +108,7 @@ static inline rt_bool_t write_reg(rt_mdio_t *bus, rt_uint32_t addr, rt_uint32_t return RT_TRUE; } -static rt_phy_status rt_phy_init(void *object, rt_uint32_t phy_addr, rt_uint32_t src_clock_hz) +static rt_phy_status rt_phy_init(struct rt_phy_device *phy, void *object, rt_uint32_t phy_addr, rt_uint32_t src_clock_hz) { rt_bool_t ret; rt_phy_status result; @@ -208,7 +208,7 @@ static rt_phy_status rt_phy_init(void *object, rt_uint32_t phy_addr, rt_uint32_t } -static rt_phy_status rt_phy_read(rt_uint32_t reg, rt_uint32_t *data) +static rt_phy_status rt_phy_read(struct rt_phy_device *phy, rt_uint32_t reg, rt_uint32_t *data) { rt_mdio_t *mdio_bus = phy_ksz8081.bus; rt_uint32_t device_id = phy_ksz8081.addr; @@ -220,7 +220,7 @@ static rt_phy_status rt_phy_read(rt_uint32_t reg, rt_uint32_t *data) return PHY_STATUS_FAIL; } -static rt_phy_status rt_phy_write(rt_uint32_t reg, rt_uint32_t data) +static rt_phy_status rt_phy_write(struct rt_phy_device *phy, rt_uint32_t reg, rt_uint32_t data) { rt_mdio_t *mdio_bus = phy_ksz8081.bus; rt_uint32_t device_id = phy_ksz8081.addr; @@ -232,7 +232,7 @@ static rt_phy_status rt_phy_write(rt_uint32_t reg, rt_uint32_t data) return PHY_STATUS_FAIL; } -static rt_phy_status rt_phy_loopback(rt_uint32_t mode, rt_uint32_t speed, rt_bool_t enable) +static rt_phy_status rt_phy_loopback(struct rt_phy_device *phy, rt_uint32_t mode, rt_uint32_t speed, rt_bool_t enable) { rt_uint32_t data = 0; rt_phy_status result; @@ -288,7 +288,7 @@ static rt_phy_status rt_phy_loopback(rt_uint32_t mode, rt_uint32_t speed, rt_boo return result; } -static rt_phy_status get_link_status(rt_bool_t *status) +static rt_phy_status get_link_status(struct rt_phy_device *phy, rt_bool_t *status) { rt_phy_status result; rt_uint32_t data; @@ -310,7 +310,7 @@ static rt_phy_status get_link_status(rt_bool_t *status) } return result; } -static rt_phy_status get_link_speed_duplex(rt_uint32_t *speed, rt_uint32_t *duplex) +static rt_phy_status get_link_speed_duplex(struct rt_phy_device *phy, rt_uint32_t *speed, rt_uint32_t *duplex) { rt_phy_status result = PHY_STATUS_OK; rt_uint32_t data, ctl_reg; @@ -346,7 +346,7 @@ static rt_phy_status get_link_speed_duplex(rt_uint32_t *speed, rt_uint32_t *dupl return result; } -static struct rt_phy_ops phy_ops = +static const struct rt_phy_ops phy_ops = { .init = rt_phy_init, .read = rt_phy_read, diff --git a/bsp/imxrt/libraries/drivers/drv_rtl8211f.c b/bsp/imxrt/libraries/drivers/drv_rtl8211f.c index 43e50c7eb950..7deb3aa79f8f 100644 --- a/bsp/imxrt/libraries/drivers/drv_rtl8211f.c +++ b/bsp/imxrt/libraries/drivers/drv_rtl8211f.c @@ -185,7 +185,7 @@ static status_t PHY_RTL8211F_MMD_Write(uint8_t device, uint16_t addr, uint32_t d return result; } -static rt_phy_status rt_phy_init(void *object, rt_uint32_t phy_addr, rt_uint32_t src_clock_hz) +static rt_phy_status rt_phy_init(struct rt_phy_device *phy, void *object, rt_uint32_t phy_addr, rt_uint32_t src_clock_hz) { rt_bool_t ret; rt_phy_status result; @@ -309,7 +309,7 @@ static rt_phy_status rt_phy_init(void *object, rt_uint32_t phy_addr, rt_uint32_t return result; } -static rt_phy_status rt_phy_read(rt_uint32_t reg, rt_uint32_t *data) +static rt_phy_status rt_phy_read(struct rt_phy_device *phy, rt_uint32_t reg, rt_uint32_t *data) { rt_mdio_t *mdio_bus = phy_rtl8211f.bus; rt_uint32_t device_id = phy_rtl8211f.addr; @@ -321,7 +321,7 @@ static rt_phy_status rt_phy_read(rt_uint32_t reg, rt_uint32_t *data) return PHY_STATUS_FAIL; } -static rt_phy_status rt_phy_write(rt_uint32_t reg, rt_uint32_t data) +static rt_phy_status rt_phy_write(struct rt_phy_device *phy, rt_uint32_t reg, rt_uint32_t data) { rt_mdio_t *mdio_bus = phy_rtl8211f.bus; rt_uint32_t device_id = phy_rtl8211f.addr; @@ -333,7 +333,7 @@ static rt_phy_status rt_phy_write(rt_uint32_t reg, rt_uint32_t data) return PHY_STATUS_FAIL; } -static rt_phy_status rt_phy_loopback(rt_uint32_t mode, rt_uint32_t speed, rt_bool_t enable) +static rt_phy_status rt_phy_loopback(struct rt_phy_device *phy, rt_uint32_t mode, rt_uint32_t speed, rt_bool_t enable) { /* This PHY only supports local loopback. */ // rt_assert(mode == PHY_LOCAL_LOOP); @@ -371,7 +371,7 @@ static rt_phy_status rt_phy_loopback(rt_uint32_t mode, rt_uint32_t speed, rt_boo return result; } -static rt_phy_status get_link_status(rt_bool_t *status) +static rt_phy_status get_link_status(struct rt_phy_device *phy, rt_bool_t *status) { // assert(status); @@ -396,7 +396,7 @@ static rt_phy_status get_link_status(rt_bool_t *status) return result; } -static rt_phy_status get_link_speed_duplex(rt_uint32_t *speed, rt_uint32_t *duplex) +static rt_phy_status get_link_speed_duplex(struct rt_phy_device *phy, rt_uint32_t *speed, rt_uint32_t *duplex) { // assert(!((speed == NULL) && (duplex == NULL))); @@ -441,7 +441,7 @@ static rt_phy_status get_link_speed_duplex(rt_uint32_t *speed, rt_uint32_t *dupl return result; } -static struct rt_phy_ops phy_ops = +static const struct rt_phy_ops phy_ops = { .init = rt_phy_init, .read = rt_phy_read, diff --git a/bsp/qemu-virt64-aarch64/.config b/bsp/qemu-virt64-aarch64/.config index 1c600283e27c..27a69f8cda44 100644 --- a/bsp/qemu-virt64-aarch64/.config +++ b/bsp/qemu-virt64-aarch64/.config @@ -6,7 +6,7 @@ # # RT-Thread Kernel # -CONFIG_RT_NAME_MAX=16 +CONFIG_RT_NAME_MAX=24 # CONFIG_RT_USING_ARCH_DATA_TYPE is not set CONFIG_RT_USING_SMART=y CONFIG_RT_USING_SMP=y @@ -64,7 +64,7 @@ CONFIG_RT_USING_MESSAGEQUEUE=y # Memory Management # CONFIG_RT_PAGE_MAX_ORDER=11 -# CONFIG_RT_USING_MEMPOOL is not set +CONFIG_RT_USING_MEMPOOL=y # CONFIG_RT_USING_SMALL_MEM is not set CONFIG_RT_USING_SLAB=y CONFIG_RT_USING_MEMHEAP=y @@ -174,6 +174,7 @@ CONFIG_RT_DFS_ELM_MAX_SECTOR_SIZE=512 CONFIG_RT_DFS_ELM_REENTRANT=y CONFIG_RT_DFS_ELM_MUTEX_TIMEOUT=3000 CONFIG_RT_USING_DFS_DEVFS=y +CONFIG_RT_USING_DFS_DIRECTFS=y CONFIG_RT_USING_DFS_ROMFS=y # CONFIG_RT_USING_DFS_CROMFS is not set # CONFIG_RT_USING_DFS_RAMFS is not set @@ -192,30 +193,46 @@ CONFIG_RT_LWP_SHM_MAX_NR=64 # # Device Drivers # -CONFIG_RT_USING_DEVICE_IPC=y -CONFIG_RT_UNAMED_PIPE_NUMBER=64 -CONFIG_RT_USING_SYSTEM_WORKQUEUE=y -CONFIG_RT_SYSTEM_WORKQUEUE_STACKSIZE=8192 -CONFIG_RT_SYSTEM_WORKQUEUE_PRIORITY=23 CONFIG_RT_USING_TTY=y # CONFIG_RT_TTY_DEBUG is not set -# CONFIG_RT_USING_CAN is not set # CONFIG_RT_USING_CPUTIME is not set -# CONFIG_RT_USING_I2C is not set -# CONFIG_RT_USING_PHY is not set -# CONFIG_RT_USING_ADC is not set # CONFIG_RT_USING_DAC is not set CONFIG_RT_USING_NULL=y CONFIG_RT_USING_ZERO=y CONFIG_RT_USING_RANDOM=y -# CONFIG_RT_USING_PWM is not set -# CONFIG_RT_USING_SDIO is not set -# CONFIG_RT_USING_SPI is not set -# CONFIG_RT_USING_WDT is not set -# CONFIG_RT_USING_AUDIO is not set -# CONFIG_RT_USING_SENSOR is not set +# CONFIG_RT_USING_PM is not set # CONFIG_RT_USING_TOUCH is not set # CONFIG_RT_USING_LCD is not set +# CONFIG_RT_USING_PULSE_ENCODER is not set +# CONFIG_RT_USING_INPUT_CAPTURE is not set +CONFIG_RT_USING_DEV_BUS=y +# CONFIG_RT_USING_WIFI is not set +CONFIG_RT_USING_DEVICE_IPC=y +CONFIG_RT_UNAMED_PIPE_NUMBER=64 +CONFIG_RT_USING_SYSTEM_WORKQUEUE=y +CONFIG_RT_SYSTEM_WORKQUEUE_STACKSIZE=8192 +CONFIG_RT_SYSTEM_WORKQUEUE_PRIORITY=23 +# CONFIG_RT_USING_ADC is not set +CONFIG_RT_USING_AUDIO=y +CONFIG_RT_AUDIO_REPLAY_MP_BLOCK_SIZE=4096 +CONFIG_RT_AUDIO_REPLAY_MP_BLOCK_COUNT=2 +CONFIG_RT_AUDIO_RECORD_PIPE_SIZE=2048 +CONFIG_RT_AUDIO_INTEL_HDA=y +# CONFIG_RT_AUDIO_ROCKCHIP is not set +CONFIG_RT_USING_BLK=y + +# +# Partition Types +# +CONFIG_RT_BLK_PARTITION_DFS=y +CONFIG_RT_BLK_PARTITION_EFI=y +# CONFIG_RT_USING_CAN is not set +CONFIG_RT_USING_CLK=y +# CONFIG_RT_CLK_ROCKCHIP is not set +CONFIG_RT_USING_FIRMWARE=y +CONFIG_RT_FIRMWARE_PSCI=y +CONFIG_RT_FIRMWARE_QEMU_FW_CFG=y +# CONFIG_RT_FIRMWARE_ARM_SCMI is not set CONFIG_RT_USING_HWCRYPTO=y CONFIG_RT_HWCRYPTO_DEFAULT_NAME="hwcryto" CONFIG_RT_HWCRYPTO_IV_MAX_SIZE=16 @@ -231,16 +248,19 @@ CONFIG_RT_HWCRYPTO_KEYBIT_MAX_SIZE=256 CONFIG_RT_HWCRYPTO_USING_RNG=y # CONFIG_RT_HWCRYPTO_USING_CRC is not set # CONFIG_RT_HWCRYPTO_USING_BIGNUM is not set -# CONFIG_RT_USING_PULSE_ENCODER is not set -# CONFIG_RT_USING_INPUT_CAPTURE is not set -CONFIG_RT_USING_DEV_BUS=y -# CONFIG_RT_USING_WIFI is not set -CONFIG_RT_USING_CLK=y -CONFIG_RT_USING_FIRMWARE=y -CONFIG_RT_FIRMWARE_PSCI=y +# CONFIG_RT_HWCRYPTO_RNG_ROCKCHIP is not set +# CONFIG_RT_USING_HWSPINLOCK is not set CONFIG_RT_USING_HWTIMER=y CONFIG_RT_HWTIMER_ARM_ARCH=y -# CONFIG_RT_HWTIMER_RISCV_CLINT is not set +# CONFIG_RT_HWTIMER_BCM2835 is not set +# CONFIG_RT_HWTIMER_ROCKCHIP is not set +# CONFIG_RT_USING_I2C is not set +CONFIG_RT_USING_INPUT=y +CONFIG_RT_INPUT_KEYBOARD=y +CONFIG_RT_INPUT_KEYBOARD_GPIO_KEYS=y +# CONFIG_RT_INPUT_MISC is not set +# CONFIG_RT_USING_LED is not set +# CONFIG_RT_USING_MBOX is not set # CONFIG_RT_USING_MFD is not set # CONFIG_RT_USING_MTD_NAND is not set CONFIG_RT_USING_MTD_NOR=y @@ -248,48 +268,67 @@ CONFIG_RT_USING_MTD_NOR_CFI=y CONFIG_RT_USING_OFW=y # CONFIG_RT_USING_BUILTIN_FDT is not set CONFIG_RT_FDT_EARLYCON_MSG_SIZE=128 +CONFIG_RT_USING_OFW_DIRECTFS=y CONFIG_RT_USING_PCI=y CONFIG_RT_PCI_MSI=y +CONFIG_RT_PCI_SYS_64BIT=y +CONFIG_RT_PCI_CACHE_LINE_SIZE=8 +# CONFIG_RT_PCI_LOCKLESS is not set CONFIG_RT_PCI_ECAM=y +CONFIG_RT_PCI_HOST_COMMON=y +CONFIG_RT_PCI_HOST_GENERIC=y +# CONFIG_RT_PCI_DW_HOST is not set +# CONFIG_RT_USING_PHY is not set CONFIG_RT_USING_PIC=y CONFIG_MAX_HANDLERS=512 # CONFIG_RT_PIC_BCM2835_INTC is not set # CONFIG_RT_PIC_BCM2836_L1_INTC is not set CONFIG_RT_PIC_ARM_GIC=y -# CONFIG_RT_PIC_ARM_GIC_V2M is not set +CONFIG_RT_PIC_ARM_GIC_V2M=y CONFIG_RT_PIC_ARM_GIC_V3=y -# CONFIG_RT_PIC_ARM_GIC_V3_ITS is not set +CONFIG_RT_PIC_ARM_GIC_V3_ITS=y CONFIG_RT_PIC_ARM_GIC_MAX_NR=1 -# CONFIG_RT_PIC_RISCV_INTC is not set -# CONFIG_RT_PIC_SIFIVE_PLIC is not set +# CONFIG_RT_USING_PINCTRL is not set CONFIG_RT_USING_PIN=y CONFIG_RT_PIN_PL061=y -CONFIG_RT_USING_PM=y -CONFIG_PM_TICKLESS_THRESHOLD_TIME=2 -# CONFIG_PM_USING_CUSTOM_CONFIG is not set -# CONFIG_PM_ENABLE_DEBUG is not set -# CONFIG_PM_ENABLE_SUSPEND_SLEEP_MODE is not set -# CONFIG_PM_ENABLE_THRESHOLD_SLEEP_MODE is not set -# CONFIG_RT_PM_RESET_SYSCON is not set -# CONFIG_RT_PM_RESET_SYSCON_POWEROFF is not set +# CONFIG_RT_PIN_ROCKCHIP is not set +# CONFIG_RT_USING_POWER_RESET is not set +# CONFIG_RT_USING_PWM is not set +# CONFIG_RT_USING_REGULATOR is not set +# CONFIG_RT_USING_RESET is not set CONFIG_RT_USING_RTC=y CONFIG_RT_USING_ALARM=y # CONFIG_RT_USING_SOFT_RTC is not set -CONFIG_RT_RTC_PL031=y # CONFIG_RT_RTC_GOLDFISH is not set +# CONFIG_RT_RTC_HYM8563 is not set +CONFIG_RT_RTC_PL031=y +# CONFIG_RT_RTC_RK_TIMER is not set +# CONFIG_RT_USING_SDIO is not set +# CONFIG_RT_USING_SENSOR is not set CONFIG_RT_USING_SERIAL=y CONFIG_RT_USING_SERIAL_V1=y # CONFIG_RT_USING_SERIAL_V2 is not set CONFIG_RT_SERIAL_USING_DMA=y CONFIG_RT_SERIAL_RB_BUFSZ=256 CONFIG_RT_SERIAL_PL011=y +CONFIG_RT_SERIAL_8250=y +# CONFIG_RT_SERIAL_8250_BCM2835AUX is not set +# CONFIG_RT_SERIAL_8250_DW is not set +# CONFIG_RT_SERIAL_8250_OFW is not set +CONFIG_RT_SERIAL_8250_PCI=y +# CONFIG_RT_USING_SOC is not set +# CONFIG_RT_USING_SPI is not set CONFIG_RT_USING_VIRTIO=y +CONFIG_RT_VIRTIO_TRANSPORT_MMIO=y +CONFIG_RT_VIRTIO_TRANSPORT_PCI=y CONFIG_RT_VIRTIO_NET=y CONFIG_RT_VIRTIO_BLK=y CONFIG_RT_VIRTIO_CONSOLE=y CONFIG_RT_VIRTIO_RNG=y CONFIG_RT_VIRTIO_GPU=y CONFIG_RT_VIRTIO_INPUT=y +CONFIG_RT_VIRTIO_SOUND=y +# CONFIG_RT_USING_WDT is not set # # Using USB @@ -424,6 +463,7 @@ CONFIG_RT_LWIP_USING_PING=y # CONFIG_RT_USING_ULOG is not set # CONFIG_RT_USING_UTEST is not set # CONFIG_RT_USING_VAR_EXPORT is not set +CONFIG_RT_USING_CRASH_CORE=y CONFIG_RT_USING_ADT=y CONFIG_RT_USING_ADT_AVL=y CONFIG_RT_USING_ADT_BITMAP=y diff --git a/bsp/qemu-virt64-aarch64/.gitignore b/bsp/qemu-virt64-aarch64/.gitignore new file mode 100644 index 000000000000..b511ae114b5a --- /dev/null +++ b/bsp/qemu-virt64-aarch64/.gitignore @@ -0,0 +1 @@ +*.qcow2 diff --git a/bsp/qemu-virt64-aarch64/applications/graphic.c b/bsp/qemu-virt64-aarch64/applications/graphic.c index 0ca180de35c8..a9bed041f6b7 100644 --- a/bsp/qemu-virt64-aarch64/applications/graphic.c +++ b/bsp/qemu-virt64-aarch64/applications/graphic.c @@ -1,14 +1,53 @@ -// /* -// * Copyright (c) 2006-2021, RT-Thread Development Team -// * -// * SPDX-License-Identifier: Apache-2.0 -// * -// * Change Logs: -// * Date Author Notes -// * 2021-11-11 GuEe-GUI the first version -// */ - -// #include +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2021-11-11 GuEe-GUI the first version + */ + +#include +#include + +#ifdef RT_VIDEO_FB +static int rom_fb_init(void) +{ + rt_err_t err; + struct fb_var_screeninfo var = {}; + struct fb_fix_screeninfo fix = {}; + rt_device_t rom_fbdev = rt_device_find("fb0"); + + if (!rom_fbdev || rt_device_open(rom_fbdev, 0)) + { + return -RT_EIO; + } + + var.xres = 800; + var.yres = 600; + var.bits_per_pixel = 32; + var.red.offset = 16; + var.red.length = 8; + var.green.offset = 8; + var.green.length = 8; + var.blue.offset = 0; + var.blue.length = 8; + var.transp.offset = 24; + var.transp.length = 8; + + if (!(err = rt_device_control(rom_fbdev, FBIOPUT_VSCREENINFO, &var))) + { + if (!(err = rt_device_control(rom_fbdev, FBIOGET_FSCREENINFO, &fix))) + { + rt_memset((void *)fix.smem_start, 0xff, fix.smem_len); + } + } + + return err; +} +INIT_ENV_EXPORT(rom_fb_init); +#endif /* RT_VIDEO_FB */ // #include // #include diff --git a/bsp/qemu-virt64-aarch64/applications/main.c b/bsp/qemu-virt64-aarch64/applications/main.c index 51c7e70cb5f0..a6ef3bc9f0ba 100644 --- a/bsp/qemu-virt64-aarch64/applications/main.c +++ b/bsp/qemu-virt64-aarch64/applications/main.c @@ -13,9 +13,17 @@ int main(int argc, char** argv) { + const char *oem; + +#ifdef RT_USING_SMART + oem = "Smart"; +#else + oem = "Thread"; +#endif + rt_ubase_t level = rt_hw_interrupt_disable(); - rt_kprintf("Hi, this is RT-Thread!!\n"); + rt_kprintf("Hi, this is RT-%s!!\n", oem); rt_hw_interrupt_enable(level); diff --git a/bsp/qemu-virt64-aarch64/applications/pin.c b/bsp/qemu-virt64-aarch64/applications/pin.c deleted file mode 100644 index 339ab0c4a02b..000000000000 --- a/bsp/qemu-virt64-aarch64/applications/pin.c +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (c) 2006-2023, RT-Thread Development Team - * - * SPDX-License-Identifier: Apache-2.0 - * - * Change Logs: - * Date Author Notes - * 2022-6-30 GuEe-GUI first version - * 2023-2-25 GuEe-GUI using gpio-keys ofw in dm - */ - -#include -#include -#include - -#define DBG_TAG "app.pin" -#define DBG_LVL DBG_INFO -#include - -#ifdef RT_USING_PIN - -#define LINUX_KEY_POWER 116 /* SC System Power Down */ - -void linux_key_poweroff(void *args) -{ - LOG_I("Power off the machine by [%s]", args); - - rt_hw_cpu_shutdown(); -} - -static int pin_init() -{ - struct rt_ofw_node *gpio_keys_np = rt_ofw_find_node_by_compatible(RT_NULL, "gpio-keys"), *key; - - if (!gpio_keys_np) - { - return -1; - } - - rt_ofw_foreach_child_node(gpio_keys_np, key) - { - struct rt_ofw_cell_args args; - - if (!rt_ofw_parse_phandle_cells(key, "gpios", "#gpio-cells", 0, &args)) - { - rt_uint32_t linux_code; - void *key_args = RT_NULL; - void (*fn_ptr)(void *) = RT_NULL; - - rt_ofw_prop_read_u32(key, "linux,code", &linux_code); - - switch (linux_code) - { - case LINUX_KEY_POWER: - fn_ptr = &linux_key_poweroff; - rt_ofw_prop_read_string(key, "label", (const char **)&key_args); - break; - default: - break; - } - - if (fn_ptr) - { - rt_pin_attach_irq(args.args[0], args.args[1], fn_ptr, key_args); - rt_pin_irq_enable(args.args[0], RT_TRUE); - } - - rt_ofw_node_put(args.data); - } - } - - rt_ofw_node_put(gpio_keys_np); - - return 0; -} -INIT_APP_EXPORT(pin_init); - -#endif /* RT_USING_PIN */ diff --git a/bsp/qemu-virt64-aarch64/qemu.py b/bsp/qemu-virt64-aarch64/qemu.py index dbe766820936..97fd7b10ab01 100755 --- a/bsp/qemu-virt64-aarch64/qemu.py +++ b/bsp/qemu-virt64-aarch64/qemu.py @@ -1,84 +1,226 @@ #!/usr/bin/python # -*- coding: utf-8 -*- -import os,sys +import os, sys -opt=sys.argv +opt = sys.argv -graphic_cfg=""" \ - -serial stdio \ - -device virtio-gpu-device,xres=800,yres=600 \ +graphic_cfg = """ \ + -serial stdio -device ramfb \ + -device virtio-gpu-device \ -device virtio-keyboard-device \ -device virtio-mouse-device \ -device virtio-tablet-device \ """ -q_gic=2 -q_dumpdtb="" -q_smp=4 -q_mem=128 -q_graphic="-nographic" -q_debug="" -q_bootargs="console=ttyAMA0 earlycon root=block0 rootfstype=elm" -q_sd="sd.bin" -q_flash="flash.bin" +smmu_cfg = "" +iommu_cfg = "" +plugged_mem_cfg = "" +virtfs_cfg = "" + +q_gic = 2 +q_dumpdtb = "" +q_el = 1 +q_smp = 4 +q_mem = 128 +q_graphic = "-nographic" +q_debug = "" +q_bootargs = "console=ttyAMA0 earlycon cma=8M coherent_pool=2M root=vda0 rootfstype=elm rootwait rw" +q_initrd = "" +q_block = "block" +q_net = "user" +q_ssh = 12055 +q_scsi = "scsi" +q_flash = "flash" +q_emmc = "emmc" +q_nvme = "nvme" +q_plugged_mem = 0 +q_iommu = "smmu" +q_sound = "hda" +q_usbd = "usbd" +q_gl = None +q_9p = "" + +# WSL? +is_windows = sys.platform.startswith('win') or \ + os.popen('which qemu-system-aarch64 | xargs file').read().find('PE') >= 0 or \ + (os.system("readlink `which qemu-system-aarch64` > /dev/null") == 0 and \ + os.popen('readlink `which qemu-system-aarch64` | xargs file').read().find('PE') >= 0) def is_opt(key, inkey): - if str("-"+key) == inkey: + if str("-" + key) == inkey: return True return False -if sys.platform.startswith('win'): - if not os.path.exists(q_sd): - os.system("qemu-img create -f raw {} 64M".format(q_sd)) - if not os.path.exists(q_flash): - os.system("qemu-img create -f raw {} 64M".format(q_flash)) -else: - if not os.path.exists(q_sd): - os.system("dd if=/dev/zero of={} bs=1024 count=65536".format(q_sd)) - if not os.path.exists(q_flash): - os.system("dd if=/dev/zero of={} bs=1024 count=65536".format(q_flash)) - for i in range(len(opt)): if i == 0: continue - inkey=opt[i] + inkey = opt[i] - if is_opt("gic", inkey): q_gic = int(opt[i+1]) - if is_opt("dumpdtb", inkey): q_dumpdtb = str(",dumpdtb=" + opt[i+1]) - if is_opt("smp", inkey): q_smp = int(opt[i+1]) - if is_opt("mem", inkey): q_mem = int(opt[i+1]) + if is_opt("gic", inkey): q_gic = int(opt[i + 1]) + if is_opt("dumpdtb", inkey): q_dumpdtb = str(",dumpdtb=" + opt[i + 1]) + if is_opt("el", inkey): q_el = int(opt[i + 1]) + if is_opt("smp", inkey): q_smp = int(opt[i + 1]) + if is_opt("mem", inkey): q_mem = int(opt[i + 1]) if is_opt("debug", inkey): q_debug = "-S -s" - if is_opt("bootargs", inkey): q_debug = opt[i+1] + if is_opt("bootargs", inkey): q_bootargs = opt[i + 1] + if is_opt("initrd", inkey): q_initrd = str("-initrd " + opt[i + 1]) if is_opt("graphic", inkey): q_graphic = graphic_cfg - if is_opt("sd", inkey): q_sd = opt[i+1] - if is_opt("flash", inkey): q_flash = opt[i+1] + if is_opt("block", inkey): q_block = opt[i + 1] + if is_opt("tap", inkey): q_net = "tap,ifname=tap0" + if is_opt("ssh", inkey): q_ssh = int(opt[i + 1]) + if is_opt("flash", inkey): q_flash = opt[i + 1] + if is_opt("emmc", inkey): q_emmc = opt[i + 1] + if is_opt("nvme", inkey): q_nvme = opt[i + 1] + if is_opt("plugged-mem", inkey): q_plugged_mem = int(opt[i + 1]) + if is_opt("iommu", inkey): q_iommu = opt[i + 1] + if is_opt("sound", inkey): q_sound = opt[i + 1] + if is_opt("usbd", inkey): q_usbd = opt[i + 1] + if is_opt("gl", inkey): q_gl = "-device virtio-gpu-gl-device -display gtk,gl=on " + if is_opt("9p", inkey): q_9p = opt[i + 1] +# SMP if q_smp > 8: q_gic = 3 +# Exception Level +if q_el == 1: + q_el = "" +elif q_el == 2: + q_el = ",virtualization=on" + if q_gic == 3: + q_gic = "max" +elif q_el == 3: + q_el = ",secure=on" +else: + print("Error: Invalid -el {}".format(q_el)) + exit(-1) + +# IOMMU +if q_iommu == "smmu": + smmu_cfg = "iommu=smmuv3," +elif q_iommu == "virtio": + iommu_cfg = "-device virtio-iommu-device,primary-bus=pcie.0 " +else: + print("Error: Invalid -iommu {}".format(q_iommu)) + exit(-1) + +# Display +# --enable-opengl --enable-virglrenderer +if q_gl != None: + if len(q_graphic) > 0: + q_graphic += q_gl + else: + print("Error: GL should in graphic mode") + exit(-1) + +# Sound +if q_sound == "hda": + q_sound = "-device intel-hda -device hda-duplex " +elif q_sound == "virtio": + q_sound = "-device virtio-sound-pci,audiodev=vsnd -audiodev alsa,id=vsnd " +else: + print("Error: Invalid -sound {}".format(q_sound)) + exit(-1) + +# Net +# --enable-slirp +if q_net.find("user") >= 0: + q_net += ",hostfwd=tcp::{}-:22".format(q_ssh) +else: + if not is_windows: + q_net += ",script=no,downscript=no" + print("Warning: SSH not set in TAP") + +# Storage +# pflash have pflash0 and pflash1, pflash0 is used for BootROMs such as UEFI +# if we load file to pflash0, QEMU will boot from it, so we only use pflash1. +# Well, we can R/W in pflash0 by CFI driver, but the data will lost after QEMU exits. +# +# partitions (not support in Windows, Maybe WSL2): +# modprobe nbd max_part=12 +# qemu-nbd --connect=/dev/nbdX ABC.qcow2 +# fdisk /dev/nbdX +# ... +# qemu-nbd --disconnect /dev/nbdX +disk_list = [q_block, q_scsi, q_flash, q_emmc, q_nvme, q_usbd] + +for disk in disk_list: + disk += ".qcow2" + if not os.path.exists(disk): + os.system("qemu-img create -f qcow2 {} 64M".format(disk)) + +# Share File System +# --enable-virtfs +if len(q_9p) > 0: + virtfs_cfg = """ \ + -fsdev local,security_model=passthrough,id=fsdev0,path={} \ + -device virtio-9p-device,fsdev=fsdev0,mount_tag=hostshare \ + """.format(q_9p) + +# Plugged Memory +if is_windows: + if q_plugged_mem > 0: + print("Warning: virtio-mem is not supported in MS Windows") +else: + plugged_mem = "plugged-mem.bin" + + if q_plugged_mem == 0: + q_plugged_mem = 64 + + if not os.path.exists(plugged_mem) or os.path.getsize(plugged_mem) != q_plugged_mem * 1024 * 1024: + os.system("qemu-img create -f raw {} {}M".format(plugged_mem, q_plugged_mem)) + + plugged_mem_cfg = """ \ + -device virtio-mem,memdev=plugged-mem \ + -object memory-backend-file,mem-path={},id=plugged-mem,share=on,size={} \ + """.format(plugged_mem, os.path.getsize(plugged_mem)) + os.system(""" qemu-system-aarch64 \ - -M virt,gic-version={}{} \ + -M virt,acpi=on,{}its=on,gic-version={}{}{} \ -cpu max \ -smp {} \ -m {} \ -kernel rtthread.bin \ -append "{}" \ + -device vmcoreinfo \ + {} \ {} \ {} \ - -drive if=none,file={},format=raw,id=blk0 \ + -drive if=none,file={}.qcow2,format=qcow2,id=blk0 \ -device virtio-blk-device,drive=blk0 \ - -netdev user,id=net0 \ - -device virtio-net-device,netdev=net0 \ + -netdev {},id=net0 \ + -device virtio-net-device,netdev=net0,speed=800000 \ -device virtio-rng-device \ - -device intel-hda \ - -device hda-duplex \ - -drive file={},format=raw,if=pflash,index=1 \ + -device virtio-balloon-device \ + -device virtio-scsi-pci \ + -device scsi-hd,channel=0,scsi-id=0,lun=2,drive=scsi0 \ + -drive file={}.qcow2,format=qcow2,if=none,id=scsi0 \ + {} \ + {} \ + -device virtio-crypto-device,cryptodev=vcrypto \ + -object cryptodev-backend-builtin,id=vcrypto \ -device virtio-serial-device \ -chardev socket,host=127.0.0.1,port=4321,server=on,wait=off,telnet=on,id=console0 \ - -device virtserialport,chardev=console0 -""".format(q_gic, q_dumpdtb, q_smp, q_mem, q_bootargs, q_graphic, q_debug, q_sd, q_flash)) + -device virtserialport,chardev=console0 \ + {} \ + {} \ + -drive if=pflash,file={}.qcow2,format=qcow2,index=1 \ + -device pci-serial,chardev=console1 \ + -chardev socket,host=127.0.0.1,port=4322,server=on,wait=off,telnet=on,id=console1 \ + -device sdhci-pci -device sd-card,drive=emmc \ + -drive if=none,file={}.qcow2,format=qcow2,id=emmc \ + -device nvme,serial=deadbeef,drive=nvme \ + -drive if=none,file={}.qcow2,format=qcow2,id=nvme \ + -device i6300esb -watchdog-action reset \ + -device qemu-xhci \ + -device usb-storage,drive=usbd \ + -drive if=none,file={}.qcow2,format=qcow2,id=usbd \ + -device edu,dma_mask=0xffffffff +""".format(smmu_cfg, q_gic, q_dumpdtb, q_el, q_smp, q_mem, q_bootargs, q_initrd, + q_graphic, q_debug, q_block, q_net, q_scsi, virtfs_cfg, plugged_mem_cfg, + iommu_cfg, q_sound, q_flash, q_emmc, q_nvme, q_usbd)) if len(q_dumpdtb) != 0: - dtb=q_dumpdtb.split('=')[-1] - os.system("dtc -I dtb -O dts {} -o {}".format(dtb, dtb.replace(".dtb", ".dts"))) + dtb = q_dumpdtb.split('=')[-1] + os.system("dtc -I dtb -O dts -@ -A {} -o {}".format(dtb, dtb.replace(".dtb", ".dts"))) diff --git a/bsp/qemu-virt64-aarch64/rtconfig.h b/bsp/qemu-virt64-aarch64/rtconfig.h index 65d5b7599f59..b67f7261b5d0 100644 --- a/bsp/qemu-virt64-aarch64/rtconfig.h +++ b/bsp/qemu-virt64-aarch64/rtconfig.h @@ -6,7 +6,7 @@ /* RT-Thread Kernel */ -#define RT_NAME_MAX 16 +#define RT_NAME_MAX 24 #define RT_USING_SMART #define RT_USING_SMP #define RT_CPUS_NR 4 @@ -43,6 +43,7 @@ /* Memory Management */ #define RT_PAGE_MAX_ORDER 11 +#define RT_USING_MEMPOOL #define RT_USING_SLAB #define RT_USING_MEMHEAP #define RT_MEMHEAP_FAST_MODE @@ -124,6 +125,7 @@ #define RT_DFS_ELM_REENTRANT #define RT_DFS_ELM_MUTEX_TIMEOUT 3000 #define RT_USING_DFS_DEVFS +#define RT_USING_DFS_DIRECTFS #define RT_USING_DFS_ROMFS #define RT_USING_LWP #define RT_LWP_MAX_NR 30 @@ -135,42 +137,62 @@ /* Device Drivers */ +#define RT_USING_TTY +#define RT_USING_NULL +#define RT_USING_ZERO +#define RT_USING_RANDOM +#define RT_USING_DEV_BUS #define RT_USING_DEVICE_IPC #define RT_UNAMED_PIPE_NUMBER 64 #define RT_USING_SYSTEM_WORKQUEUE #define RT_SYSTEM_WORKQUEUE_STACKSIZE 8192 #define RT_SYSTEM_WORKQUEUE_PRIORITY 23 -#define RT_USING_TTY -#define RT_USING_NULL -#define RT_USING_ZERO -#define RT_USING_RANDOM +#define RT_USING_AUDIO +#define RT_AUDIO_REPLAY_MP_BLOCK_SIZE 4096 +#define RT_AUDIO_REPLAY_MP_BLOCK_COUNT 2 +#define RT_AUDIO_RECORD_PIPE_SIZE 2048 +#define RT_AUDIO_INTEL_HDA +#define RT_USING_BLK + +/* Partition Types */ + +#define RT_BLK_PARTITION_DFS +#define RT_BLK_PARTITION_EFI +#define RT_USING_CLK +#define RT_USING_FIRMWARE +#define RT_FIRMWARE_PSCI +#define RT_FIRMWARE_QEMU_FW_CFG #define RT_USING_HWCRYPTO #define RT_HWCRYPTO_DEFAULT_NAME "hwcryto" #define RT_HWCRYPTO_IV_MAX_SIZE 16 #define RT_HWCRYPTO_KEYBIT_MAX_SIZE 256 #define RT_HWCRYPTO_USING_RNG -#define RT_USING_DEV_BUS -#define RT_USING_CLK -#define RT_USING_FIRMWARE -#define RT_FIRMWARE_PSCI #define RT_USING_HWTIMER #define RT_HWTIMER_ARM_ARCH +#define RT_USING_INPUT +#define RT_INPUT_KEYBOARD +#define RT_INPUT_KEYBOARD_GPIO_KEYS #define RT_USING_MTD_NOR #define RT_USING_MTD_NOR_CFI #define RT_USING_OFW #define RT_FDT_EARLYCON_MSG_SIZE 128 +#define RT_USING_OFW_DIRECTFS #define RT_USING_PCI #define RT_PCI_MSI +#define RT_PCI_SYS_64BIT +#define RT_PCI_CACHE_LINE_SIZE 8 #define RT_PCI_ECAM +#define RT_PCI_HOST_COMMON +#define RT_PCI_HOST_GENERIC #define RT_USING_PIC #define MAX_HANDLERS 512 #define RT_PIC_ARM_GIC +#define RT_PIC_ARM_GIC_V2M #define RT_PIC_ARM_GIC_V3 +#define RT_PIC_ARM_GIC_V3_ITS #define RT_PIC_ARM_GIC_MAX_NR 1 #define RT_USING_PIN #define RT_PIN_PL061 -#define RT_USING_PM -#define PM_TICKLESS_THRESHOLD_TIME 2 #define RT_USING_RTC #define RT_USING_ALARM #define RT_RTC_PL031 @@ -179,13 +201,18 @@ #define RT_SERIAL_USING_DMA #define RT_SERIAL_RB_BUFSZ 256 #define RT_SERIAL_PL011 +#define RT_SERIAL_8250 +#define RT_SERIAL_8250_PCI #define RT_USING_VIRTIO +#define RT_VIRTIO_TRANSPORT_MMIO +#define RT_VIRTIO_TRANSPORT_PCI #define RT_VIRTIO_NET #define RT_VIRTIO_BLK #define RT_VIRTIO_CONSOLE #define RT_VIRTIO_RNG #define RT_VIRTIO_GPU #define RT_VIRTIO_INPUT +#define RT_VIRTIO_SOUND /* Using USB */ @@ -275,6 +302,7 @@ /* Utilities */ +#define RT_USING_CRASH_CORE #define RT_USING_ADT #define RT_USING_ADT_AVL #define RT_USING_ADT_BITMAP diff --git a/bsp/qemu-virt64-aarch64/rtconfig.py b/bsp/qemu-virt64-aarch64/rtconfig.py index 98d1a38cec95..7f76fe77d312 100644 --- a/bsp/qemu-virt64-aarch64/rtconfig.py +++ b/bsp/qemu-virt64-aarch64/rtconfig.py @@ -30,7 +30,7 @@ CXXFLAGS= DEVICE + CFPFLAGS + ' -Wall -fdiagnostics-color=always' CFLAGS = DEVICE + CFPFLAGS + ' -Wall -Wno-cpp -std=gnu99 -fdiagnostics-color=always' AFLAGS = ' -c' + AFPFLAGS + ' -x assembler-with-cpp' - LFLAGS = DEVICE + ' -nostartfiles -Wl,--gc-sections,-Map=rtthread.map,-cref,-u,system_vectors -T link.lds' + ' -lsupc++ -lgcc -static' + LFLAGS = DEVICE + ' -nostartfiles -Wl,--no-warn-rwx-segments -Wl,--gc-sections,-Map=rtthread.map,-cref,-u,system_vectors -T link.lds' + ' -lsupc++ -lgcc -static' CPATH = '' LPATH = '' diff --git a/bsp/rockchip/rk3308/.config b/bsp/rockchip/rk3308/.config new file mode 100644 index 000000000000..43e451c6a559 --- /dev/null +++ b/bsp/rockchip/rk3308/.config @@ -0,0 +1,1176 @@ +# +# Automatically generated file; DO NOT EDIT. +# RT-Thread Project Configuration +# + +# +# RT-Thread Kernel +# +CONFIG_RT_NAME_MAX=16 +# CONFIG_RT_USING_ARCH_DATA_TYPE is not set +CONFIG_RT_USING_SMART=y +CONFIG_RT_USING_SMP=y +CONFIG_RT_CPUS_NR=4 +CONFIG_RT_ALIGN_SIZE=8 +# CONFIG_RT_THREAD_PRIORITY_8 is not set +CONFIG_RT_THREAD_PRIORITY_32=y +# CONFIG_RT_THREAD_PRIORITY_256 is not set +CONFIG_RT_THREAD_PRIORITY_MAX=32 +CONFIG_RT_TICK_PER_SECOND=100 +CONFIG_RT_USING_OVERFLOW_CHECK=y +CONFIG_RT_USING_HOOK=y +CONFIG_RT_HOOK_USING_FUNC_PTR=y +CONFIG_RT_USING_IDLE_HOOK=y +CONFIG_RT_IDLE_HOOK_LIST_SIZE=4 +CONFIG_IDLE_THREAD_STACK_SIZE=4096 +CONFIG_SYSTEM_THREAD_STACK_SIZE=4096 +CONFIG_RT_USING_TIMER_SOFT=y +CONFIG_RT_TIMER_THREAD_PRIO=4 +CONFIG_RT_TIMER_THREAD_STACK_SIZE=4096 + +# +# kservice optimization +# +# CONFIG_RT_KSERVICE_USING_STDLIB is not set +# CONFIG_RT_KSERVICE_USING_TINY_SIZE is not set +# CONFIG_RT_USING_TINY_FFS is not set +CONFIG_RT_KPRINTF_USING_LONGLONG=y +CONFIG_RT_DEBUG=y +CONFIG_RT_DEBUG_COLOR=y +# CONFIG_RT_DEBUG_INIT_CONFIG is not set +# CONFIG_RT_DEBUG_THREAD_CONFIG is not set +# CONFIG_RT_DEBUG_SCHEDULER_CONFIG is not set +# CONFIG_RT_DEBUG_IPC_CONFIG is not set +# CONFIG_RT_DEBUG_TIMER_CONFIG is not set +# CONFIG_RT_DEBUG_IRQ_CONFIG is not set +# CONFIG_RT_DEBUG_MEM_CONFIG is not set +# CONFIG_RT_DEBUG_SLAB_CONFIG is not set +# CONFIG_RT_DEBUG_MEMHEAP_CONFIG is not set +# CONFIG_RT_DEBUG_PAGE_LEAK is not set +# CONFIG_RT_DEBUG_MODULE_CONFIG is not set + +# +# Inter-Thread communication +# +CONFIG_RT_USING_SEMAPHORE=y +CONFIG_RT_USING_MUTEX=y +CONFIG_RT_USING_EVENT=y +CONFIG_RT_USING_MAILBOX=y +CONFIG_RT_USING_MESSAGEQUEUE=y +# CONFIG_RT_USING_SIGNALS is not set + +# +# Memory Management +# +CONFIG_RT_PAGE_MAX_ORDER=11 +CONFIG_RT_USING_MEMPOOL=y +CONFIG_RT_USING_SMALL_MEM=y +# CONFIG_RT_USING_SLAB is not set +CONFIG_RT_USING_MEMHEAP=y +CONFIG_RT_MEMHEAP_FAST_MODE=y +# CONFIG_RT_MEMHEAP_BEST_MODE is not set +CONFIG_RT_USING_SMALL_MEM_AS_HEAP=y +# CONFIG_RT_USING_MEMHEAP_AS_HEAP is not set +# CONFIG_RT_USING_SLAB_AS_HEAP is not set +# CONFIG_RT_USING_USERHEAP is not set +# CONFIG_RT_USING_NOHEAP is not set +CONFIG_RT_USING_MEMTRACE=y +# CONFIG_RT_USING_HEAP_ISR is not set +CONFIG_RT_USING_HEAP=y + +# +# Kernel Device Object +# +CONFIG_RT_USING_DEVICE=y +CONFIG_RT_USING_DEVICE_OPS=y +CONFIG_RT_USING_DM=y +CONFIG_RT_USING_DM_FDT=y +CONFIG_RT_USING_INTERRUPT_INFO=y +CONFIG_RT_USING_CONSOLE=y +CONFIG_RT_CONSOLEBUF_SIZE=256 +CONFIG_RT_CONSOLE_DEVICE_NAME="uart2" +CONFIG_RT_VER_NUM=0x50001 +# CONFIG_RT_USING_STDC_ATOMIC is not set + +# +# RT-Thread Architecture +# +CONFIG_ARCH_CPU_64BIT=y +CONFIG_RT_USING_CACHE=y +CONFIG_RT_USING_HW_ATOMIC=y +# CONFIG_ARCH_ARM_BOOTWITH_FLUSH_CACHE is not set +# CONFIG_ARCH_CPU_STACK_GROWS_UPWARD is not set +# CONFIG_RT_USING_CPU_FFS is not set +CONFIG_ARCH_MM_MMU=y +CONFIG_ARCH_ARM=y +CONFIG_ARCH_ARM_MMU=y +CONFIG_KERNEL_VADDR_START=0xffff000000000000 +CONFIG_ARCH_ARMV8=y +CONFIG_ARCH_ARMV8_EXTENSIONS=0 +CONFIG_ARCH_TEXT_OFFSET=0x200000 +CONFIG_ARCH_RAM_OFFSET=0x0 +CONFIG_ARCH_ASPACE_SIZE=0xffffffff +CONFIG_ARCH_SECONDARY_CPU_STACK_SIZE=4096 +CONFIG_ARCH_HAVE_EFFICIENT_UNALIGNED_ACCESS=y + +# +# RT-Thread Components +# +CONFIG_RT_USING_COMPONENTS_INIT=y +CONFIG_RT_USING_USER_MAIN=y +CONFIG_RT_MAIN_THREAD_STACK_SIZE=8192 +CONFIG_RT_MAIN_THREAD_PRIORITY=10 +# CONFIG_RT_USING_LEGACY is not set +CONFIG_RT_USING_MSH=y +CONFIG_RT_USING_FINSH=y +CONFIG_FINSH_USING_MSH=y +CONFIG_FINSH_THREAD_NAME="tshell" +CONFIG_FINSH_THREAD_PRIORITY=20 +CONFIG_FINSH_THREAD_STACK_SIZE=8192 +CONFIG_FINSH_USING_HISTORY=y +CONFIG_FINSH_HISTORY_LINES=5 +CONFIG_FINSH_USING_SYMTAB=y +CONFIG_FINSH_CMD_SIZE=80 +CONFIG_MSH_USING_BUILT_IN_COMMANDS=y +CONFIG_FINSH_USING_DESCRIPTION=y +# CONFIG_FINSH_ECHO_DISABLE_DEFAULT is not set +# CONFIG_FINSH_USING_AUTH is not set +CONFIG_FINSH_ARG_MAX=10 + +# +# DFS: device virtual file system +# +CONFIG_RT_USING_DFS=y +CONFIG_DFS_USING_POSIX=y +CONFIG_DFS_USING_WORKDIR=y +# CONFIG_RT_USING_DFS_MNTTABLE is not set +CONFIG_DFS_FD_MAX=16 +CONFIG_RT_USING_DFS_V1=y +# CONFIG_RT_USING_DFS_V2 is not set +CONFIG_DFS_FILESYSTEMS_MAX=4 +CONFIG_DFS_FILESYSTEM_TYPES_MAX=4 +CONFIG_RT_USING_DFS_ELMFAT=y + +# +# elm-chan's FatFs, Generic FAT Filesystem Module +# +CONFIG_RT_DFS_ELM_CODE_PAGE=437 +CONFIG_RT_DFS_ELM_WORD_ACCESS=y +# CONFIG_RT_DFS_ELM_USE_LFN_0 is not set +# CONFIG_RT_DFS_ELM_USE_LFN_1 is not set +# CONFIG_RT_DFS_ELM_USE_LFN_2 is not set +CONFIG_RT_DFS_ELM_USE_LFN_3=y +CONFIG_RT_DFS_ELM_USE_LFN=3 +CONFIG_RT_DFS_ELM_LFN_UNICODE_0=y +# CONFIG_RT_DFS_ELM_LFN_UNICODE_1 is not set +# CONFIG_RT_DFS_ELM_LFN_UNICODE_2 is not set +# CONFIG_RT_DFS_ELM_LFN_UNICODE_3 is not set +CONFIG_RT_DFS_ELM_LFN_UNICODE=0 +CONFIG_RT_DFS_ELM_MAX_LFN=255 +CONFIG_RT_DFS_ELM_DRIVES=2 +CONFIG_RT_DFS_ELM_MAX_SECTOR_SIZE=512 +# CONFIG_RT_DFS_ELM_USE_ERASE is not set +CONFIG_RT_DFS_ELM_REENTRANT=y +CONFIG_RT_DFS_ELM_MUTEX_TIMEOUT=3000 +CONFIG_RT_USING_DFS_DEVFS=y +# CONFIG_RT_USING_DFS_DIRECTFS is not set +# CONFIG_RT_USING_DFS_ROMFS is not set +# CONFIG_RT_USING_DFS_CROMFS is not set +# CONFIG_RT_USING_DFS_RAMFS is not set +# CONFIG_RT_USING_DFS_TMPFS is not set +# CONFIG_RT_USING_FAL is not set +CONFIG_RT_USING_LWP=y +CONFIG_RT_LWP_MAX_NR=30 +CONFIG_LWP_TASK_STACK_SIZE=16384 +CONFIG_RT_CH_MSG_MAX_NR=1024 +CONFIG_LWP_CONSOLE_INPUT_BUFFER_SIZE=1024 +CONFIG_LWP_TID_MAX_NR=64 +CONFIG_RT_LWP_SHM_MAX_NR=64 +# CONFIG_LWP_UNIX98_PTY is not set + +# +# Device Drivers +# +CONFIG_RT_USING_TTY=y +# CONFIG_RT_TTY_DEBUG is not set +# CONFIG_RT_USING_CPUTIME is not set +# CONFIG_RT_USING_DAC is not set +CONFIG_RT_USING_NULL=y +CONFIG_RT_USING_ZERO=y +CONFIG_RT_USING_RANDOM=y +# CONFIG_RT_USING_PM is not set +# CONFIG_RT_USING_TOUCH is not set +# CONFIG_RT_USING_LCD is not set +# CONFIG_RT_USING_PULSE_ENCODER is not set +# CONFIG_RT_USING_INPUT_CAPTURE is not set +# CONFIG_RT_USING_DEV_BUS is not set +# CONFIG_RT_USING_WIFI is not set +CONFIG_RT_USING_DEVICE_IPC=y +CONFIG_RT_UNAMED_PIPE_NUMBER=64 +CONFIG_RT_USING_SYSTEM_WORKQUEUE=y +CONFIG_RT_SYSTEM_WORKQUEUE_STACKSIZE=8192 +CONFIG_RT_SYSTEM_WORKQUEUE_PRIORITY=23 +CONFIG_RT_USING_ADC=y +CONFIG_RT_ADC_ROCKCHIP_SARADC=y +# CONFIG_RT_USING_AUDIO is not set +# CONFIG_RT_USING_CAN is not set +CONFIG_RT_USING_CLK=y +CONFIG_RT_CLK_ROCKCHIP=y +CONFIG_RT_CLK_ROCKCHIP_RK3308=y +# CONFIG_RT_CLK_ROCKCHIP_RK3568 is not set +CONFIG_RT_USING_FIRMWARE=y +CONFIG_RT_FIRMWARE_PSCI=y +# CONFIG_RT_FIRMWARE_QEMU_FW_CFG is not set +# CONFIG_RT_FIRMWARE_ARM_SCMI is not set +CONFIG_RT_USING_HWCRYPTO=y +CONFIG_RT_HWCRYPTO_DEFAULT_NAME="hwcryto" +CONFIG_RT_HWCRYPTO_IV_MAX_SIZE=16 +CONFIG_RT_HWCRYPTO_KEYBIT_MAX_SIZE=256 +# CONFIG_RT_HWCRYPTO_USING_GCM is not set +# CONFIG_RT_HWCRYPTO_USING_AES is not set +# CONFIG_RT_HWCRYPTO_USING_DES is not set +# CONFIG_RT_HWCRYPTO_USING_3DES is not set +# CONFIG_RT_HWCRYPTO_USING_RC4 is not set +# CONFIG_RT_HWCRYPTO_USING_MD5 is not set +# CONFIG_RT_HWCRYPTO_USING_SHA1 is not set +# CONFIG_RT_HWCRYPTO_USING_SHA2 is not set +CONFIG_RT_HWCRYPTO_USING_RNG=y +# CONFIG_RT_HWCRYPTO_USING_CRC is not set +# CONFIG_RT_HWCRYPTO_USING_BIGNUM is not set +CONFIG_RT_HWCRYPTO_RNG_ROCKCHIP=y +# CONFIG_RT_USING_HWSPINLOCK is not set +CONFIG_RT_USING_HWTIMER=y +CONFIG_RT_HWTIMER_ARM_ARCH=y +# CONFIG_RT_HWTIMER_BCM2835 is not set +CONFIG_RT_HWTIMER_ROCKCHIP=y +CONFIG_RT_USING_I2C=y +# CONFIG_RT_I2C_DEBUG is not set +CONFIG_RT_USING_I2C_BITOPS=y +# CONFIG_RT_I2C_BITOPS_DEBUG is not set +CONFIG_RT_I2C_RK3X=y +# CONFIG_RT_USING_INPUT is not set +CONFIG_RT_USING_LED=y +CONFIG_RT_LED_GPIO=y +# CONFIG_RT_USING_MBOX is not set +CONFIG_RT_USING_MFD=y +# CONFIG_RT_MFD_BCM2835_PM is not set +CONFIG_RT_MFD_SYSCON=y +# CONFIG_RT_MFD_RK8XX is not set +# CONFIG_RT_MFD_RK8XX_I2C is not set +# CONFIG_RT_MFD_RK8XX_SPI is not set +# CONFIG_RT_USING_MTD_NAND is not set +# CONFIG_RT_USING_MTD_NOR is not set +CONFIG_RT_USING_OFW=y +# CONFIG_RT_USING_BUILTIN_FDT is not set +CONFIG_RT_FDT_EARLYCON_MSG_SIZE=128 +# CONFIG_RT_USING_OFW_DIRECTFS is not set +# CONFIG_RT_USING_PCI is not set +CONFIG_RT_USING_PHY=y +# CONFIG_RT_PHY_ROCKCHIP_NANENG_COMBO is not set +# CONFIG_RT_PHY_ROCKCHIP_SNPS_PCIE3 is not set +CONFIG_RT_USING_PIC=y +CONFIG_MAX_HANDLERS=512 +# CONFIG_RT_PIC_BCM2835_INTC is not set +# CONFIG_RT_PIC_BCM2836_L1_INTC is not set +CONFIG_RT_PIC_ARM_GIC=y +# CONFIG_RT_PIC_ARM_GIC_V2M is not set +# CONFIG_RT_PIC_ARM_GIC_V3 is not set +# CONFIG_RT_PIC_ARM_GIC_V3_ITS is not set +CONFIG_RT_PIC_ARM_GIC_MAX_NR=1 +CONFIG_RT_USING_PINCTRL=y +CONFIG_RT_PINCTRL_ROCKCHIP=y +CONFIG_RT_USING_PIN=y +# CONFIG_RT_PIN_PL061 is not set +CONFIG_RT_PIN_ROCKCHIP=y +CONFIG_RT_USING_POWER_RESET=y +# CONFIG_RT_POWER_RESET_SYSCON_POWEROFF is not set +CONFIG_RT_POWER_RESET_SYSCON_REBOOT_MODE=y +# CONFIG_RT_POWER_RESET_SYSCON_REBOOT is not set +CONFIG_RT_POWER_RESET_REBOOT_MODE=y +CONFIG_RT_USING_PWM=y +CONFIG_RT_PWM_ROCKCHIP=y +CONFIG_RT_USING_REGULATOR=y +CONFIG_RT_REGULATOR_GPIO=y +CONFIG_RT_USING_RESET=y +CONFIG_RT_USING_RTC=y +CONFIG_RT_USING_ALARM=y +# CONFIG_RT_USING_SOFT_RTC is not set +# CONFIG_RT_RTC_GOLDFISH is not set +# CONFIG_RT_RTC_HYM8563 is not set +# CONFIG_RT_RTC_PL031 is not set +CONFIG_RT_RTC_RK_TIMER=y +CONFIG_RT_USING_SDIO=y +CONFIG_RT_SDIO_STACK_SIZE=8192 +CONFIG_RT_SDIO_THREAD_PRIORITY=15 +CONFIG_RT_MMCSD_STACK_SIZE=8192 +CONFIG_RT_MMCSD_THREAD_PREORITY=22 +CONFIG_RT_MMCSD_MAX_PARTITION=16 +# CONFIG_RT_SDIO_DEBUG is not set +# CONFIG_RT_SDIO_SDHCI_DWCMSHC is not set +CONFIG_RT_SDIO_DW_MMC=y +# CONFIG_RT_SDIO_DW_MMC_PCI is not set +CONFIG_RT_SDIO_DW_MMC_ROCKCHIP=y +CONFIG_RT_USING_SENSOR=y +CONFIG_RT_USING_SENSOR_CMD=y +CONFIG_RT_SENSOR_ROCKCHIP_TSADC=y +CONFIG_RT_USING_SERIAL=y +CONFIG_RT_USING_SERIAL_V1=y +# CONFIG_RT_USING_SERIAL_V2 is not set +# CONFIG_RT_SERIAL_USING_DMA is not set +CONFIG_RT_SERIAL_RB_BUFSZ=64 +# CONFIG_RT_SERIAL_PL011 is not set +CONFIG_RT_SERIAL_8250=y +# CONFIG_RT_SERIAL_8250_BCM2835AUX is not set +CONFIG_RT_SERIAL_8250_DW=y +# CONFIG_RT_SERIAL_8250_OFW is not set +# CONFIG_RT_SERIAL_8250_PCI is not set +CONFIG_RT_USING_SOC=y +CONFIG_RT_SOC_ROCKCHIP=y +CONFIG_RT_SOC_ROCKCHIP_FIQ_DEBUGGER=y +CONFIG_RT_SOC_ROCKCHIP_GRF=y +# CONFIG_RT_SOC_ROCKCHIP_HW_DECOMPRESS is not set +CONFIG_RT_SOC_ROCKCHIP_IODOMAIN=y +# CONFIG_RT_SOC_ROCKCHIP_PMDOMAIN is not set +CONFIG_RT_USING_SPI=y +# CONFIG_RT_USING_SPI_BITOPS is not set +CONFIG_RT_USING_QSPI=y +# CONFIG_RT_USING_SPI_MSD is not set +# CONFIG_RT_USING_SFUD is not set +# CONFIG_RT_USING_ENC28J60 is not set +# CONFIG_RT_USING_SPI_WIFI is not set +CONFIG_RT_SPI_ROCKCHIP_SFC=y +CONFIG_RT_SPI_ROCKCHIP=y +# CONFIG_RT_USING_VIRTIO is not set +CONFIG_RT_USING_WDT=y +# CONFIG_RT_WDT_BCM2835 is not set +CONFIG_RT_WDT_DW=y + +# +# Using USB +# +# CONFIG_RT_USING_USB is not set +# CONFIG_RT_USING_USB_HOST is not set +# CONFIG_RT_USING_USB_DEVICE is not set + +# +# C/C++ and POSIX layer +# +CONFIG_RT_LIBC_DEFAULT_TIMEZONE=8 + +# +# POSIX (Portable Operating System Interface) layer +# +CONFIG_RT_USING_POSIX_FS=y +CONFIG_RT_USING_POSIX_DEVIO=y +CONFIG_RT_USING_POSIX_STDIO=y +# CONFIG_RT_USING_POSIX_POLL is not set +# CONFIG_RT_USING_POSIX_SELECT is not set +# CONFIG_RT_USING_POSIX_SOCKET is not set +CONFIG_RT_USING_POSIX_TERMIOS=y +# CONFIG_RT_USING_POSIX_AIO is not set +# CONFIG_RT_USING_POSIX_MMAN is not set +CONFIG_RT_USING_POSIX_DELAY=y +CONFIG_RT_USING_POSIX_CLOCK=y +CONFIG_RT_USING_POSIX_TIMER=y +# CONFIG_RT_USING_PTHREADS is not set +# CONFIG_RT_USING_MODULE is not set + +# +# Interprocess Communication (IPC) +# +# CONFIG_RT_USING_POSIX_PIPE is not set +# CONFIG_RT_USING_POSIX_MESSAGE_QUEUE is not set +# CONFIG_RT_USING_POSIX_MESSAGE_SEMAPHORE is not set + +# +# Socket is in the 'Network' category +# +# CONFIG_RT_USING_CPLUSPLUS is not set + +# +# Network +# +# CONFIG_RT_USING_SAL is not set +# CONFIG_RT_USING_NETDEV is not set +# CONFIG_RT_USING_LWIP is not set +# CONFIG_RT_USING_AT is not set + +# +# Utilities +# +# CONFIG_RT_USING_RYM is not set +# CONFIG_RT_USING_ULOG is not set +# CONFIG_RT_USING_UTEST is not set +# CONFIG_RT_USING_VAR_EXPORT is not set +CONFIG_RT_USING_ADT=y +CONFIG_RT_USING_ADT_AVL=y +CONFIG_RT_USING_ADT_BITMAP=y +CONFIG_RT_USING_ADT_HASHMAP=y +CONFIG_RT_USING_ADT_REF=y +# CONFIG_RT_USING_RT_LINK is not set +# CONFIG_RT_USING_VBUS is not set + +# +# RT-Thread Utestcases +# +# CONFIG_RT_USING_UTESTCASES is not set + +# +# RT-Thread online packages +# + +# +# IoT - internet of things +# +# CONFIG_PKG_USING_LWIP is not set +# CONFIG_PKG_USING_LORAWAN_DRIVER is not set +# CONFIG_PKG_USING_PAHOMQTT is not set +# CONFIG_PKG_USING_UMQTT is not set +# CONFIG_PKG_USING_WEBCLIENT is not set +# CONFIG_PKG_USING_WEBNET is not set +# CONFIG_PKG_USING_MONGOOSE is not set +# CONFIG_PKG_USING_MYMQTT is not set +# CONFIG_PKG_USING_KAWAII_MQTT is not set +# CONFIG_PKG_USING_BC28_MQTT is not set +# CONFIG_PKG_USING_WEBTERMINAL is not set +# CONFIG_PKG_USING_LIBMODBUS is not set +# CONFIG_PKG_USING_FREEMODBUS is not set +# CONFIG_PKG_USING_NANOPB is not set + +# +# Wi-Fi +# + +# +# Marvell WiFi +# +# CONFIG_PKG_USING_WLANMARVELL is not set + +# +# Wiced WiFi +# +# CONFIG_PKG_USING_WLAN_WICED is not set +# CONFIG_PKG_USING_RW007 is not set +# CONFIG_PKG_USING_COAP is not set +# CONFIG_PKG_USING_NOPOLL is not set +# CONFIG_PKG_USING_NETUTILS is not set +# CONFIG_PKG_USING_CMUX is not set +# CONFIG_PKG_USING_PPP_DEVICE is not set +# CONFIG_PKG_USING_AT_DEVICE is not set +# CONFIG_PKG_USING_ATSRV_SOCKET is not set +# CONFIG_PKG_USING_WIZNET is not set +# CONFIG_PKG_USING_ZB_COORDINATOR is not set + +# +# IoT Cloud +# +# CONFIG_PKG_USING_ONENET is not set +# CONFIG_PKG_USING_GAGENT_CLOUD is not set +# CONFIG_PKG_USING_ALI_IOTKIT is not set +# CONFIG_PKG_USING_AZURE is not set +# CONFIG_PKG_USING_TENCENT_IOT_EXPLORER is not set +# CONFIG_PKG_USING_JIOT-C-SDK is not set +# CONFIG_PKG_USING_UCLOUD_IOT_SDK is not set +# CONFIG_PKG_USING_JOYLINK is not set +# CONFIG_PKG_USING_EZ_IOT_OS is not set +# CONFIG_PKG_USING_IOTSHARP_SDK is not set +# CONFIG_PKG_USING_NIMBLE is not set +# CONFIG_PKG_USING_LLSYNC_SDK_ADAPTER is not set +# CONFIG_PKG_USING_OTA_DOWNLOADER is not set +# CONFIG_PKG_USING_IPMSG is not set +# CONFIG_PKG_USING_LSSDP is not set +# CONFIG_PKG_USING_AIRKISS_OPEN is not set +# CONFIG_PKG_USING_LIBRWS is not set +# CONFIG_PKG_USING_TCPSERVER is not set +# CONFIG_PKG_USING_PROTOBUF_C is not set +# CONFIG_PKG_USING_DLT645 is not set +# CONFIG_PKG_USING_QXWZ is not set +# CONFIG_PKG_USING_SMTP_CLIENT is not set +# CONFIG_PKG_USING_ABUP_FOTA is not set +# CONFIG_PKG_USING_LIBCURL2RTT is not set +# CONFIG_PKG_USING_CAPNP is not set +# CONFIG_PKG_USING_AGILE_TELNET is not set +# CONFIG_PKG_USING_NMEALIB is not set +# CONFIG_PKG_USING_PDULIB is not set +# CONFIG_PKG_USING_BTSTACK is not set +# CONFIG_PKG_USING_LORAWAN_ED_STACK is not set +# CONFIG_PKG_USING_WAYZ_IOTKIT is not set +# CONFIG_PKG_USING_MAVLINK is not set +# CONFIG_PKG_USING_BSAL is not set +# CONFIG_PKG_USING_AGILE_MODBUS is not set +# CONFIG_PKG_USING_AGILE_FTP is not set +# CONFIG_PKG_USING_EMBEDDEDPROTO is not set +# CONFIG_PKG_USING_RT_LINK_HW is not set +# CONFIG_PKG_USING_RYANMQTT is not set +# CONFIG_PKG_USING_RYANW5500 is not set +# CONFIG_PKG_USING_LORA_PKT_FWD is not set +# CONFIG_PKG_USING_LORA_GW_DRIVER_LIB is not set +# CONFIG_PKG_USING_LORA_PKT_SNIFFER is not set +# CONFIG_PKG_USING_HM is not set +# CONFIG_PKG_USING_SMALL_MODBUS is not set +# CONFIG_PKG_USING_NET_SERVER is not set +# CONFIG_PKG_USING_ZFTP is not set +# CONFIG_PKG_USING_WOL is not set +# CONFIG_PKG_USING_ZEPHYR_POLLING is not set + +# +# security packages +# +# CONFIG_PKG_USING_MBEDTLS is not set +# CONFIG_PKG_USING_LIBSODIUM is not set +# CONFIG_PKG_USING_LIBHYDROGEN is not set +# CONFIG_PKG_USING_TINYCRYPT is not set +# CONFIG_PKG_USING_TFM is not set +# CONFIG_PKG_USING_YD_CRYPTO is not set + +# +# language packages +# + +# +# JSON: JavaScript Object Notation, a lightweight data-interchange format +# +# CONFIG_PKG_USING_CJSON is not set +# CONFIG_PKG_USING_LJSON is not set +# CONFIG_PKG_USING_RT_CJSON_TOOLS is not set +# CONFIG_PKG_USING_RAPIDJSON is not set +# CONFIG_PKG_USING_JSMN is not set +# CONFIG_PKG_USING_AGILE_JSMN is not set +# CONFIG_PKG_USING_PARSON is not set + +# +# XML: Extensible Markup Language +# +# CONFIG_PKG_USING_SIMPLE_XML is not set +# CONFIG_PKG_USING_EZXML is not set +# CONFIG_PKG_USING_LUATOS_SOC is not set +# CONFIG_PKG_USING_LUA is not set +# CONFIG_PKG_USING_JERRYSCRIPT is not set +# CONFIG_PKG_USING_MICROPYTHON is not set +# CONFIG_PKG_USING_PIKASCRIPT is not set +# CONFIG_PKG_USING_RTT_RUST is not set + +# +# multimedia packages +# + +# +# LVGL: powerful and easy-to-use embedded GUI library +# +# CONFIG_PKG_USING_LVGL is not set +# CONFIG_PKG_USING_LITTLEVGL2RTT is not set +# CONFIG_PKG_USING_LV_MUSIC_DEMO is not set +# CONFIG_PKG_USING_GUI_GUIDER_DEMO is not set + +# +# u8g2: a monochrome graphic library +# +# CONFIG_PKG_USING_U8G2_OFFICIAL is not set +# CONFIG_PKG_USING_U8G2 is not set +# CONFIG_PKG_USING_OPENMV is not set +# CONFIG_PKG_USING_MUPDF is not set +# CONFIG_PKG_USING_STEMWIN is not set +# CONFIG_PKG_USING_WAVPLAYER is not set +# CONFIG_PKG_USING_TJPGD is not set +# CONFIG_PKG_USING_PDFGEN is not set +# CONFIG_PKG_USING_HELIX is not set +# CONFIG_PKG_USING_AZUREGUIX is not set +# CONFIG_PKG_USING_TOUCHGFX2RTT is not set +# CONFIG_PKG_USING_NUEMWIN is not set +# CONFIG_PKG_USING_MP3PLAYER is not set +# CONFIG_PKG_USING_TINYJPEG is not set +# CONFIG_PKG_USING_UGUI is not set +# CONFIG_PKG_USING_MCURSES is not set +# CONFIG_PKG_USING_TERMBOX is not set +# CONFIG_PKG_USING_VT100 is not set +# CONFIG_PKG_USING_QRCODE is not set +# CONFIG_PKG_USING_GUIENGINE is not set +# CONFIG_PKG_USING_3GPP_AMRNB is not set + +# +# tools packages +# +# CONFIG_PKG_USING_CMBACKTRACE is not set +# CONFIG_PKG_USING_EASYFLASH is not set +# CONFIG_PKG_USING_EASYLOGGER is not set +# CONFIG_PKG_USING_SYSTEMVIEW is not set +# CONFIG_PKG_USING_SEGGER_RTT is not set +# CONFIG_PKG_USING_RTT_AUTO_EXE_CMD is not set +# CONFIG_PKG_USING_RDB is not set +# CONFIG_PKG_USING_ULOG_EASYFLASH is not set +# CONFIG_PKG_USING_LOGMGR is not set +# CONFIG_PKG_USING_ADBD is not set +# CONFIG_PKG_USING_COREMARK is not set +# CONFIG_PKG_USING_DHRYSTONE is not set +# CONFIG_PKG_USING_MEMORYPERF is not set +# CONFIG_PKG_USING_NR_MICRO_SHELL is not set +# CONFIG_PKG_USING_CHINESE_FONT_LIBRARY is not set +# CONFIG_PKG_USING_LUNAR_CALENDAR is not set +# CONFIG_PKG_USING_BS8116A is not set +# CONFIG_PKG_USING_GPS_RMC is not set +# CONFIG_PKG_USING_URLENCODE is not set +# CONFIG_PKG_USING_UMCN is not set +# CONFIG_PKG_USING_LWRB2RTT is not set +# CONFIG_PKG_USING_CPU_USAGE is not set +# CONFIG_PKG_USING_GBK2UTF8 is not set +# CONFIG_PKG_USING_VCONSOLE is not set +# CONFIG_PKG_USING_KDB is not set +# CONFIG_PKG_USING_WAMR is not set +# CONFIG_PKG_USING_MICRO_XRCE_DDS_CLIENT is not set +# CONFIG_PKG_USING_LWLOG is not set +# CONFIG_PKG_USING_ANV_TRACE is not set +# CONFIG_PKG_USING_ANV_MEMLEAK is not set +# CONFIG_PKG_USING_ANV_TESTSUIT is not set +# CONFIG_PKG_USING_ANV_BENCH is not set +# CONFIG_PKG_USING_DEVMEM is not set +# CONFIG_PKG_USING_REGEX is not set +# CONFIG_PKG_USING_MEM_SANDBOX is not set +# CONFIG_PKG_USING_SOLAR_TERMS is not set +# CONFIG_PKG_USING_GAN_ZHI is not set +# CONFIG_PKG_USING_FDT is not set +# CONFIG_PKG_USING_CBOX is not set +# CONFIG_PKG_USING_SNOWFLAKE is not set +# CONFIG_PKG_USING_HASH_MATCH is not set +# CONFIG_PKG_USING_ARMV7M_DWT_TOOL is not set +# CONFIG_PKG_USING_VOFA_PLUS is not set + +# +# system packages +# + +# +# enhanced kernel services +# +# CONFIG_PKG_USING_RT_MEMCPY_CM is not set +# CONFIG_PKG_USING_RT_KPRINTF_THREADSAFE is not set +CONFIG_PKG_USING_RT_VSNPRINTF_FULL=y +CONFIG_PKG_RT_VSNPRINTF_FULL_PATH="/packages/system/enhanced-kservice/rt_vsnprintf_full" +# CONFIG_RT_VSNPRINTF_FULL_REPLACING_SPRINTF is not set +# CONFIG_RT_VSNPRINTF_FULL_REPLACING_SNPRINTF is not set +# CONFIG_RT_VSNPRINTF_FULL_REPLACING_PRINTF is not set +# CONFIG_RT_VSNPRINTF_FULL_REPLACING_VSPRINTF is not set +# CONFIG_RT_VSNPRINTF_FULL_REPLACING_VSNPRINTF is not set +CONFIG_PKG_USING_RT_VSNPRINTF_FULL_LATEST_VERSION=y +CONFIG_PKG_RT_VSNPRINTF_FULL_VER="latest" + +# +# acceleration: Assembly language or algorithmic acceleration packages +# +# CONFIG_PKG_USING_QFPLIB_M0_FULL is not set +# CONFIG_PKG_USING_QFPLIB_M0_TINY is not set +# CONFIG_PKG_USING_QFPLIB_M3 is not set + +# +# CMSIS: ARM Cortex-M Microcontroller Software Interface Standard +# +# CONFIG_PKG_USING_CMSIS_5 is not set +# CONFIG_PKG_USING_CMSIS_RTOS1 is not set +# CONFIG_PKG_USING_CMSIS_RTOS2 is not set + +# +# Micrium: Micrium software products porting for RT-Thread +# +# CONFIG_PKG_USING_UCOSIII_WRAPPER is not set +# CONFIG_PKG_USING_UCOSII_WRAPPER is not set +# CONFIG_PKG_USING_UC_CRC is not set +# CONFIG_PKG_USING_UC_CLK is not set +# CONFIG_PKG_USING_UC_COMMON is not set +# CONFIG_PKG_USING_UC_MODBUS is not set +# CONFIG_PKG_USING_FREERTOS_WRAPPER is not set +# CONFIG_PKG_USING_CAIRO is not set +# CONFIG_PKG_USING_PIXMAN is not set +# CONFIG_PKG_USING_PARTITION is not set +# CONFIG_PKG_USING_PERF_COUNTER is not set +# CONFIG_PKG_USING_FLASHDB is not set +# CONFIG_PKG_USING_SQLITE is not set +# CONFIG_PKG_USING_RTI is not set +# CONFIG_PKG_USING_DFS_YAFFS is not set +# CONFIG_PKG_USING_LITTLEFS is not set +# CONFIG_PKG_USING_DFS_JFFS2 is not set +# CONFIG_PKG_USING_DFS_UFFS is not set +# CONFIG_PKG_USING_LWEXT4 is not set +# CONFIG_PKG_USING_THREAD_POOL is not set +# CONFIG_PKG_USING_ROBOTS is not set +# CONFIG_PKG_USING_EV is not set +# CONFIG_PKG_USING_SYSWATCH is not set +# CONFIG_PKG_USING_SYS_LOAD_MONITOR is not set +# CONFIG_PKG_USING_PLCCORE is not set +# CONFIG_PKG_USING_RAMDISK is not set +# CONFIG_PKG_USING_MININI is not set +# CONFIG_PKG_USING_QBOOT is not set +# CONFIG_PKG_USING_PPOOL is not set +# CONFIG_PKG_USING_OPENAMP is not set +# CONFIG_PKG_USING_LPM is not set +# CONFIG_PKG_USING_TLSF is not set +# CONFIG_PKG_USING_EVENT_RECORDER is not set +# CONFIG_PKG_USING_ARM_2D is not set +# CONFIG_PKG_USING_MCUBOOT is not set +# CONFIG_PKG_USING_TINYUSB is not set +# CONFIG_PKG_USING_CHERRYUSB is not set +# CONFIG_PKG_USING_KMULTI_RTIMER is not set +# CONFIG_PKG_USING_TFDB is not set +# CONFIG_PKG_USING_QPC is not set +# CONFIG_PKG_USING_AGILE_UPGRADE is not set +# CONFIG_PKG_USING_FLASH_BLOB is not set +# CONFIG_PKG_USING_MLIBC is not set + +# +# peripheral libraries and drivers +# + +# +# sensors drivers +# +# CONFIG_PKG_USING_LSM6DSM is not set +# CONFIG_PKG_USING_LSM6DSL is not set +# CONFIG_PKG_USING_LPS22HB is not set +# CONFIG_PKG_USING_HTS221 is not set +# CONFIG_PKG_USING_LSM303AGR is not set +# CONFIG_PKG_USING_BME280 is not set +# CONFIG_PKG_USING_BME680 is not set +# CONFIG_PKG_USING_BMA400 is not set +# CONFIG_PKG_USING_BMI160_BMX160 is not set +# CONFIG_PKG_USING_SPL0601 is not set +# CONFIG_PKG_USING_MS5805 is not set +# CONFIG_PKG_USING_DA270 is not set +# CONFIG_PKG_USING_DF220 is not set +# CONFIG_PKG_USING_HSHCAL001 is not set +# CONFIG_PKG_USING_BH1750 is not set +# CONFIG_PKG_USING_MPU6XXX is not set +# CONFIG_PKG_USING_AHT10 is not set +# CONFIG_PKG_USING_AP3216C is not set +# CONFIG_PKG_USING_TSL4531 is not set +# CONFIG_PKG_USING_DS18B20 is not set +# CONFIG_PKG_USING_DHT11 is not set +# CONFIG_PKG_USING_DHTXX is not set +# CONFIG_PKG_USING_GY271 is not set +# CONFIG_PKG_USING_GP2Y10 is not set +# CONFIG_PKG_USING_SGP30 is not set +# CONFIG_PKG_USING_HDC1000 is not set +# CONFIG_PKG_USING_BMP180 is not set +# CONFIG_PKG_USING_BMP280 is not set +# CONFIG_PKG_USING_SHTC1 is not set +# CONFIG_PKG_USING_BMI088 is not set +# CONFIG_PKG_USING_HMC5883 is not set +# CONFIG_PKG_USING_MAX6675 is not set +# CONFIG_PKG_USING_TMP1075 is not set +# CONFIG_PKG_USING_SR04 is not set +# CONFIG_PKG_USING_CCS811 is not set +# CONFIG_PKG_USING_PMSXX is not set +# CONFIG_PKG_USING_RT3020 is not set +# CONFIG_PKG_USING_MLX90632 is not set +# CONFIG_PKG_USING_MLX90393 is not set +# CONFIG_PKG_USING_MLX90392 is not set +# CONFIG_PKG_USING_MLX90397 is not set +# CONFIG_PKG_USING_MS5611 is not set +# CONFIG_PKG_USING_MAX31865 is not set +# CONFIG_PKG_USING_VL53L0X is not set +# CONFIG_PKG_USING_INA260 is not set +# CONFIG_PKG_USING_MAX30102 is not set +# CONFIG_PKG_USING_INA226 is not set +# CONFIG_PKG_USING_LIS2DH12 is not set +# CONFIG_PKG_USING_HS300X is not set +# CONFIG_PKG_USING_ZMOD4410 is not set +# CONFIG_PKG_USING_ISL29035 is not set +# CONFIG_PKG_USING_MMC3680KJ is not set +# CONFIG_PKG_USING_QMP6989 is not set +# CONFIG_PKG_USING_BALANCE is not set +# CONFIG_PKG_USING_SHT2X is not set +# CONFIG_PKG_USING_SHT3X is not set +# CONFIG_PKG_USING_AD7746 is not set +# CONFIG_PKG_USING_ADT74XX is not set +# CONFIG_PKG_USING_MAX17048 is not set +# CONFIG_PKG_USING_AS7341 is not set +# CONFIG_PKG_USING_CW2015 is not set +# CONFIG_PKG_USING_ICM20608 is not set +# CONFIG_PKG_USING_PAJ7620 is not set +# CONFIG_PKG_USING_STHS34PF80 is not set + +# +# touch drivers +# +# CONFIG_PKG_USING_GT9147 is not set +# CONFIG_PKG_USING_GT1151 is not set +# CONFIG_PKG_USING_GT917S is not set +# CONFIG_PKG_USING_GT911 is not set +# CONFIG_PKG_USING_FT6206 is not set +# CONFIG_PKG_USING_FT5426 is not set +# CONFIG_PKG_USING_FT6236 is not set +# CONFIG_PKG_USING_XPT2046_TOUCH is not set +# CONFIG_PKG_USING_REALTEK_AMEBA is not set +# CONFIG_PKG_USING_STM32_SDIO is not set +# CONFIG_PKG_USING_ESP_IDF is not set +# CONFIG_PKG_USING_BUTTON is not set +# CONFIG_PKG_USING_PCF8574 is not set +# CONFIG_PKG_USING_SX12XX is not set +# CONFIG_PKG_USING_SIGNAL_LED is not set +# CONFIG_PKG_USING_LEDBLINK is not set +# CONFIG_PKG_USING_LITTLED is not set +# CONFIG_PKG_USING_LKDGUI is not set +# CONFIG_PKG_USING_NRF5X_SDK is not set +# CONFIG_PKG_USING_NRFX is not set + +# +# Kendryte SDK +# +# CONFIG_PKG_USING_K210_SDK is not set +# CONFIG_PKG_USING_KENDRYTE_SDK is not set +# CONFIG_PKG_USING_INFRARED is not set +# CONFIG_PKG_USING_MULTI_INFRARED is not set +# CONFIG_PKG_USING_AGILE_BUTTON is not set +# CONFIG_PKG_USING_AGILE_LED is not set +# CONFIG_PKG_USING_AT24CXX is not set +# CONFIG_PKG_USING_MOTIONDRIVER2RTT is not set +# CONFIG_PKG_USING_PCA9685 is not set +# CONFIG_PKG_USING_ILI9341 is not set +# CONFIG_PKG_USING_I2C_TOOLS is not set +# CONFIG_PKG_USING_NRF24L01 is not set +# CONFIG_PKG_USING_RPLIDAR is not set +# CONFIG_PKG_USING_AS608 is not set +# CONFIG_PKG_USING_RC522 is not set +# CONFIG_PKG_USING_WS2812B is not set +# CONFIG_PKG_USING_EMBARC_BSP is not set +# CONFIG_PKG_USING_EXTERN_RTC_DRIVERS is not set +# CONFIG_PKG_USING_MULTI_RTIMER is not set +# CONFIG_PKG_USING_MAX7219 is not set +# CONFIG_PKG_USING_BEEP is not set +# CONFIG_PKG_USING_EASYBLINK is not set +# CONFIG_PKG_USING_PMS_SERIES is not set +# CONFIG_PKG_USING_CAN_YMODEM is not set +# CONFIG_PKG_USING_LORA_RADIO_DRIVER is not set +# CONFIG_PKG_USING_QLED is not set +# CONFIG_PKG_USING_AGILE_CONSOLE is not set +# CONFIG_PKG_USING_LD3320 is not set +# CONFIG_PKG_USING_WK2124 is not set +# CONFIG_PKG_USING_LY68L6400 is not set +# CONFIG_PKG_USING_DM9051 is not set +# CONFIG_PKG_USING_SSD1306 is not set +# CONFIG_PKG_USING_QKEY is not set +# CONFIG_PKG_USING_RS485 is not set +# CONFIG_PKG_USING_RS232 is not set +# CONFIG_PKG_USING_NES is not set +# CONFIG_PKG_USING_VIRTUAL_SENSOR is not set +# CONFIG_PKG_USING_VDEVICE is not set +# CONFIG_PKG_USING_SGM706 is not set +# CONFIG_PKG_USING_STM32WB55_SDK is not set +# CONFIG_PKG_USING_RDA58XX is not set +# CONFIG_PKG_USING_LIBNFC is not set +# CONFIG_PKG_USING_MFOC is not set +# CONFIG_PKG_USING_TMC51XX is not set +# CONFIG_PKG_USING_TCA9534 is not set +# CONFIG_PKG_USING_KOBUKI is not set +# CONFIG_PKG_USING_ROSSERIAL is not set +# CONFIG_PKG_USING_MICRO_ROS is not set +# CONFIG_PKG_USING_MCP23008 is not set +# CONFIG_PKG_USING_BLUETRUM_SDK is not set +# CONFIG_PKG_USING_MISAKA_AT24CXX is not set +# CONFIG_PKG_USING_MISAKA_RGB_BLING is not set +# CONFIG_PKG_USING_LORA_MODEM_DRIVER is not set +# CONFIG_PKG_USING_SOFT_SERIAL is not set +# CONFIG_PKG_USING_MB85RS16 is not set +# CONFIG_PKG_USING_RFM300 is not set +# CONFIG_PKG_USING_IO_INPUT_FILTER is not set +# CONFIG_PKG_USING_RASPBERRYPI_PICO_SDK is not set +# CONFIG_PKG_USING_LRF_NV7LIDAR is not set +# CONFIG_PKG_USING_AIP650 is not set +# CONFIG_PKG_USING_FINGERPRINT is not set +# CONFIG_PKG_USING_SPI_TOOLS is not set + +# +# AI packages +# +# CONFIG_PKG_USING_LIBANN is not set +# CONFIG_PKG_USING_NNOM is not set +# CONFIG_PKG_USING_ONNX_BACKEND is not set +# CONFIG_PKG_USING_ONNX_PARSER is not set +# CONFIG_PKG_USING_TENSORFLOWLITEMICRO is not set +# CONFIG_PKG_USING_ELAPACK is not set +# CONFIG_PKG_USING_ULAPACK is not set +# CONFIG_PKG_USING_QUEST is not set +# CONFIG_PKG_USING_NAXOS is not set +# CONFIG_PKG_USING_NCNN is not set + +# +# Signal Processing and Control Algorithm Packages +# +# CONFIG_PKG_USING_FIRE_PID_CURVE is not set +# CONFIG_PKG_USING_UKAL is not set + +# +# miscellaneous packages +# + +# +# project laboratory +# + +# +# samples: kernel and components samples +# +# CONFIG_PKG_USING_KERNEL_SAMPLES is not set +# CONFIG_PKG_USING_FILESYSTEM_SAMPLES is not set +# CONFIG_PKG_USING_NETWORK_SAMPLES is not set +# CONFIG_PKG_USING_PERIPHERAL_SAMPLES is not set + +# +# entertainment: terminal games and other interesting software packages +# +# CONFIG_PKG_USING_CMATRIX is not set +# CONFIG_PKG_USING_SL is not set +# CONFIG_PKG_USING_CAL is not set +# CONFIG_PKG_USING_ACLOCK is not set +# CONFIG_PKG_USING_THREES is not set +# CONFIG_PKG_USING_2048 is not set +# CONFIG_PKG_USING_SNAKE is not set +# CONFIG_PKG_USING_TETRIS is not set +# CONFIG_PKG_USING_DONUT is not set +# CONFIG_PKG_USING_COWSAY is not set +# CONFIG_PKG_USING_MORSE is not set +# CONFIG_PKG_USING_LIBCSV is not set +# CONFIG_PKG_USING_OPTPARSE is not set +# CONFIG_PKG_USING_FASTLZ is not set +# CONFIG_PKG_USING_MINILZO is not set +# CONFIG_PKG_USING_QUICKLZ is not set +# CONFIG_PKG_USING_LZMA is not set +# CONFIG_PKG_USING_MULTIBUTTON is not set +# CONFIG_PKG_USING_FLEXIBLE_BUTTON is not set +# CONFIG_PKG_USING_CANFESTIVAL is not set +# CONFIG_PKG_USING_ZLIB is not set +# CONFIG_PKG_USING_MINIZIP is not set +# CONFIG_PKG_USING_HEATSHRINK is not set +# CONFIG_PKG_USING_DSTR is not set +# CONFIG_PKG_USING_TINYFRAME is not set +# CONFIG_PKG_USING_KENDRYTE_DEMO is not set +# CONFIG_PKG_USING_DIGITALCTRL is not set +# CONFIG_PKG_USING_UPACKER is not set +# CONFIG_PKG_USING_UPARAM is not set +# CONFIG_PKG_USING_HELLO is not set +# CONFIG_PKG_USING_VI is not set +# CONFIG_PKG_USING_KI is not set +# CONFIG_PKG_USING_ARMv7M_DWT is not set +# CONFIG_PKG_USING_CRCLIB is not set +# CONFIG_PKG_USING_LWGPS is not set +# CONFIG_PKG_USING_STATE_MACHINE is not set +# CONFIG_PKG_USING_DESIGN_PATTERN is not set +# CONFIG_PKG_USING_CONTROLLER is not set +# CONFIG_PKG_USING_PHASE_LOCKED_LOOP is not set +# CONFIG_PKG_USING_MFBD is not set +# CONFIG_PKG_USING_SLCAN2RTT is not set +# CONFIG_PKG_USING_SOEM is not set +# CONFIG_PKG_USING_QPARAM is not set +# CONFIG_PKG_USING_CorevMCU_CLI is not set + +# +# Arduino libraries +# +# CONFIG_PKG_USING_RTDUINO is not set + +# +# Projects +# +# CONFIG_PKG_USING_ARDUINO_ULTRASOUND_RADAR is not set +# CONFIG_PKG_USING_ARDUINO_SENSOR_KIT is not set +# CONFIG_PKG_USING_ARDUINO_MATLAB_SUPPORT is not set + +# +# Sensors +# +# CONFIG_PKG_USING_ARDUINO_SENSOR_DEVICE_DRIVERS is not set +# CONFIG_PKG_USING_ARDUINO_CAPACITIVESENSOR is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_ADXL375 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_VL53L0X is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_VL53L1X is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SENSOR is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_VL6180X is not set +# CONFIG_PKG_USING_ADAFRUIT_MAX31855 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MAX31865 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MAX31856 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MAX6675 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MLX90614 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LSM9DS1 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_AHTX0 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LSM9DS0 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BMP280 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_ADT7410 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BMP085 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BME680 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MCP9808 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MCP4728 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_INA219 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LTR390 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_ADXL345 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_DHT is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MCP9600 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LSM6DS is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BNO055 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MAX1704X is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MMC56X3 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MLX90393 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MLX90395 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_ICM20X is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_DPS310 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_HTS221 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SHT4X is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SHT31 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_ADXL343 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BME280 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_AS726X is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_AMG88XX is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_AM2320 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_AM2315 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LTR329_LTR303 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BMP085_UNIFIED is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BMP183 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BMP183_UNIFIED is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BMP3XX is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MS8607 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LIS3MDL is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MLX90640 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MMA8451 is not set +# CONFIG_PKG_USING_ADAFRUIT_MSA301 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MPL115A2 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BNO08X is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BNO08X_RVC is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LIS2MDL is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LSM303DLH_MAG is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LC709203F is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_CAP1188 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_CCS811 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_NAU7802 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LIS331 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LPS2X is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LPS35HW is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LSM303_ACCEL is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LIS3DH is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_PCF8591 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MPL3115A2 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MPR121 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MPRLS is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MPU6050 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_PCT2075 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_PM25AQI is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_EMC2101 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_FXAS21002C is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SCD30 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_FXOS8700 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_HMC5883_UNIFIED is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SGP30 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_TMP006 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_TLA202X is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_TCS34725 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SI7021 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SI1145 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SGP40 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SHTC3 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_HDC1000 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_HTU21DF is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_AS7341 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_HTU31D is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SENSORLAB is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_INA260 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_TMP007_LIBRARY is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_L3GD20 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_TMP117 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_TSC2007 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_TSL2561 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_TSL2591_LIBRARY is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_VCNL4040 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_VEML6070 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_VEML6075 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_VEML7700 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_LIS3DHTR is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_DHT is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_ADXL335 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_ADXL345 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_BME280 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_BMP280 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_H3LIS331DL is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_MMA7660 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_TSL2561 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_PAJ7620 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_VL53L0X is not set +# CONFIG_PKG_USING_SEEED_ITG3200 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_SHT31 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_HP20X is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_DRV2605L is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_BBM150 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_HMC5883L is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_LSM303DLH is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_TCS3414CS is not set +# CONFIG_PKG_USING_SEEED_MP503 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_BMP085 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_HIGHTEMP is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_VEML6070 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_SI1145 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_SHT35 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_AT42QT1070 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_LSM6DS3 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_HDC1000 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_HM3301 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_MCP9600 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_LTC2941 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_LDC1612 is not set + +# +# Display +# +# CONFIG_PKG_USING_ARDUINO_U8G2 is not set +# CONFIG_PKG_USING_ARDUINO_U8GLIB_ARDUINO is not set +# CONFIG_PKG_USING_SEEED_TM1637 is not set + +# +# Timing +# +# CONFIG_PKG_USING_ARDUINO_MSTIMER2 is not set + +# +# Data Processing +# +# CONFIG_PKG_USING_ARDUINO_KALMANFILTER is not set +# CONFIG_PKG_USING_ARDUINO_ARDUINOJSON is not set + +# +# Data Storage +# + +# +# Communication +# +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_PN532 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SI4713 is not set + +# +# Device Control +# +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_PCF8574 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_PCA9685 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_PCF85063TP is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_TPA2016 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_DRV2605 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_DS1841 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_DS3502 is not set + +# +# Other +# +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MFRC630 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SI5351 is not set +# CONFIG_PKG_USING_ARDUINO_RTCLIB is not set + +# +# Signal IO +# +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BUSIO is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_TCA8418 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MCP23017 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_ADS1X15 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_AW9523 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MCP3008 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MCP4725 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BD3491FS is not set + +# +# Uncategorized +# +CONFIG_SOC_RK3308=y +CONFIG_RT_USING_AMBA_BUS=y diff --git a/bsp/rockchip/rk3308/Kconfig b/bsp/rockchip/rk3308/Kconfig new file mode 100644 index 000000000000..6ffcf062dc8b --- /dev/null +++ b/bsp/rockchip/rk3308/Kconfig @@ -0,0 +1,33 @@ +mainmenu "RT-Thread Project Configuration" + +config BSP_DIR + string + option env="BSP_ROOT" + default "." + +config RTT_DIR + string + option env="RTT_ROOT" + default "../../.." + +config PKGS_DIR + string + option env="PKGS_ROOT" + default "packages" + +source "$RTT_DIR/Kconfig" +source "$PKGS_DIR/Kconfig" + +config SOC_RK3308 + bool + select ARCH_ARMV8 + select ARCH_CPU_64BIT + select ARCH_ARM_MMU + select RT_USING_CACHE + select RT_USING_COMPONENTS_INIT + select RT_USING_USER_MAIN + default y + +config RT_USING_AMBA_BUS + bool + default y diff --git a/bsp/rockchip/rk3308/SConscript b/bsp/rockchip/rk3308/SConscript new file mode 100644 index 000000000000..744d8d782140 --- /dev/null +++ b/bsp/rockchip/rk3308/SConscript @@ -0,0 +1,14 @@ +# for module compiling +import os +from building import * + +cwd = GetCurrentDir() +objs = [] +list = os.listdir(cwd) + +for d in list: + path = os.path.join(cwd, d) + if os.path.isfile(os.path.join(path, 'SConscript')): + objs = objs + SConscript(os.path.join(d, 'SConscript')) + +Return('objs') diff --git a/bsp/rockchip/rk3308/SConstruct b/bsp/rockchip/rk3308/SConstruct new file mode 100644 index 000000000000..ec6fe0187f21 --- /dev/null +++ b/bsp/rockchip/rk3308/SConstruct @@ -0,0 +1,29 @@ +import os +import sys +import rtconfig + +from rtconfig import RTT_ROOT + +sys.path = sys.path + [os.path.join(RTT_ROOT, 'tools')] +from building import * + +TARGET = 'rtthread.' + rtconfig.TARGET_EXT + +DefaultEnvironment(tools=[]) +env = Environment(tools = ['mingw'], + AS = rtconfig.AS, ASFLAGS = rtconfig.AFLAGS, + CC = rtconfig.CC, CFLAGS = rtconfig.CFLAGS, + CXX = rtconfig.CXX, CXXFLAGS = rtconfig.CXXFLAGS, + AR = rtconfig.AR, ARFLAGS = '-rc', + LINK = rtconfig.LINK, LINKFLAGS = rtconfig.LFLAGS) +env.PrependENVPath('PATH', rtconfig.EXEC_PATH) +env['ASCOM'] = env['ASPPCOM'] + +Export('RTT_ROOT') +Export('rtconfig') + +# prepare building environment +objs = PrepareBuilding(env, RTT_ROOT, has_libcpu = False) + +# make a building +DoBuilding(TARGET, objs) diff --git a/bsp/rockchip/rk3308/applications/SConscript b/bsp/rockchip/rk3308/applications/SConscript new file mode 100644 index 000000000000..c583d3016e0f --- /dev/null +++ b/bsp/rockchip/rk3308/applications/SConscript @@ -0,0 +1,9 @@ +from building import * + +cwd = GetCurrentDir() +src = Glob('*.c') + Glob('*.cpp') +CPPPATH = [cwd] + +group = DefineGroup('Applications', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') diff --git a/bsp/rockchip/rk3308/applications/main.c b/bsp/rockchip/rk3308/applications/main.c new file mode 100644 index 000000000000..27b230e675b5 --- /dev/null +++ b/bsp/rockchip/rk3308/applications/main.c @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2017-5-30 Bernard the first version + */ + +#include +#include + +int main(int argc, char** argv) +{ + const char *oem; + +#ifdef RT_USING_SMART + oem = "Smart"; +#else + oem = "Thread"; +#endif + + rt_ubase_t level = rt_hw_interrupt_disable(); + + rt_kprintf("Hi, this is RT-%s!!\n", oem); + + rt_hw_interrupt_enable(level); + + return 0; +} diff --git a/bsp/rockchip/rk3308/driver/SConscript b/bsp/rockchip/rk3308/driver/SConscript new file mode 100644 index 000000000000..17f80c100b7c --- /dev/null +++ b/bsp/rockchip/rk3308/driver/SConscript @@ -0,0 +1,19 @@ +# RT-Thread building script for component + +from building import * + +cwd = GetCurrentDir() +src = Glob('*.c') +list = os.listdir(cwd) +CPPPATH = [cwd] +objs = [] + +group = DefineGroup('Drivers', src, depend = [''], CPPPATH = CPPPATH) + +for d in list: + path = os.path.join(cwd, d) + if os.path.isfile(os.path.join(path, 'SConscript')): + objs = objs + SConscript(os.path.join(d, 'SConscript')) +objs = objs + group + +Return('objs') diff --git a/bsp/rockchip/rk3308/driver/board.c b/bsp/rockchip/rk3308/driver/board.c new file mode 100644 index 000000000000..e3295faa15d7 --- /dev/null +++ b/bsp/rockchip/rk3308/driver/board.c @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-02-21 GuEe-GUI the first version + */ + +#include +#include + +void rt_hw_board_init(void) +{ + rt_fdt_commit_memregion_early(&(rt_region_t) + { + .name = "memheap", + .start = (rt_size_t)rt_kmem_v2p(HEAP_BEGIN), + .end = (rt_size_t)rt_kmem_v2p(HEAP_END), + }, RT_TRUE); + + rt_hw_common_setup(); +} diff --git a/bsp/rockchip/rk3308/driver/board.h b/bsp/rockchip/rk3308/driver/board.h new file mode 100644 index 000000000000..53318a1dc39f --- /dev/null +++ b/bsp/rockchip/rk3308/driver/board.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2017-5-30 Bernard the first version + */ + +#ifndef __BOARD_H__ +#define __BOARD_H__ + +#include + +extern unsigned char __bss_start; +extern unsigned char __bss_end; + +#define HEAP_BEGIN (void *)&__bss_end +#define HEAP_END ((void *)HEAP_BEGIN + 64 * 1024 * 1024) + +void rt_hw_board_init(void); + +#endif /* __BOARD_H__ */ diff --git a/bsp/rockchip/rk3308/rtconfig.h b/bsp/rockchip/rk3308/rtconfig.h new file mode 100644 index 000000000000..6fa99a248b74 --- /dev/null +++ b/bsp/rockchip/rk3308/rtconfig.h @@ -0,0 +1,374 @@ +#ifndef RT_CONFIG_H__ +#define RT_CONFIG_H__ + +/* Automatically generated file; DO NOT EDIT. */ +/* RT-Thread Project Configuration */ + +/* RT-Thread Kernel */ + +#define RT_NAME_MAX 16 +#define RT_USING_SMART +#define RT_USING_SMP +#define RT_CPUS_NR 4 +#define RT_ALIGN_SIZE 8 +#define RT_THREAD_PRIORITY_32 +#define RT_THREAD_PRIORITY_MAX 32 +#define RT_TICK_PER_SECOND 100 +#define RT_USING_OVERFLOW_CHECK +#define RT_USING_HOOK +#define RT_HOOK_USING_FUNC_PTR +#define RT_USING_IDLE_HOOK +#define RT_IDLE_HOOK_LIST_SIZE 4 +#define IDLE_THREAD_STACK_SIZE 4096 +#define SYSTEM_THREAD_STACK_SIZE 4096 +#define RT_USING_TIMER_SOFT +#define RT_TIMER_THREAD_PRIO 4 +#define RT_TIMER_THREAD_STACK_SIZE 4096 + +/* kservice optimization */ + +#define RT_KPRINTF_USING_LONGLONG +#define RT_DEBUG +#define RT_DEBUG_COLOR + +/* Inter-Thread communication */ + +#define RT_USING_SEMAPHORE +#define RT_USING_MUTEX +#define RT_USING_EVENT +#define RT_USING_MAILBOX +#define RT_USING_MESSAGEQUEUE + +/* Memory Management */ + +#define RT_PAGE_MAX_ORDER 11 +#define RT_USING_MEMPOOL +#define RT_USING_SMALL_MEM +#define RT_USING_MEMHEAP +#define RT_MEMHEAP_FAST_MODE +#define RT_USING_SMALL_MEM_AS_HEAP +#define RT_USING_MEMTRACE +#define RT_USING_HEAP + +/* Kernel Device Object */ + +#define RT_USING_DEVICE +#define RT_USING_DEVICE_OPS +#define RT_USING_DM +#define RT_USING_DM_FDT +#define RT_USING_INTERRUPT_INFO +#define RT_USING_CONSOLE +#define RT_CONSOLEBUF_SIZE 256 +#define RT_CONSOLE_DEVICE_NAME "uart2" +#define RT_VER_NUM 0x50001 + +/* RT-Thread Architecture */ + +#define ARCH_CPU_64BIT +#define RT_USING_CACHE +#define RT_USING_HW_ATOMIC +#define ARCH_MM_MMU +#define ARCH_ARM +#define ARCH_ARM_MMU +#define KERNEL_VADDR_START 0xffff000000000000 +#define ARCH_ARMV8 +#define ARCH_ARMV8_EXTENSIONS 0 +#define ARCH_TEXT_OFFSET 0x200000 +#define ARCH_RAM_OFFSET 0x0 +#define ARCH_ASPACE_SIZE 0xffffffff +#define ARCH_SECONDARY_CPU_STACK_SIZE 4096 +#define ARCH_HAVE_EFFICIENT_UNALIGNED_ACCESS + +/* RT-Thread Components */ + +#define RT_USING_COMPONENTS_INIT +#define RT_USING_USER_MAIN +#define RT_MAIN_THREAD_STACK_SIZE 8192 +#define RT_MAIN_THREAD_PRIORITY 10 +#define RT_USING_MSH +#define RT_USING_FINSH +#define FINSH_USING_MSH +#define FINSH_THREAD_NAME "tshell" +#define FINSH_THREAD_PRIORITY 20 +#define FINSH_THREAD_STACK_SIZE 8192 +#define FINSH_USING_HISTORY +#define FINSH_HISTORY_LINES 5 +#define FINSH_USING_SYMTAB +#define FINSH_CMD_SIZE 80 +#define MSH_USING_BUILT_IN_COMMANDS +#define FINSH_USING_DESCRIPTION +#define FINSH_ARG_MAX 10 + +/* DFS: device virtual file system */ + +#define RT_USING_DFS +#define DFS_USING_POSIX +#define DFS_USING_WORKDIR +#define DFS_FD_MAX 16 +#define RT_USING_DFS_V1 +#define DFS_FILESYSTEMS_MAX 4 +#define DFS_FILESYSTEM_TYPES_MAX 4 +#define RT_USING_DFS_ELMFAT + +/* elm-chan's FatFs, Generic FAT Filesystem Module */ + +#define RT_DFS_ELM_CODE_PAGE 437 +#define RT_DFS_ELM_WORD_ACCESS +#define RT_DFS_ELM_USE_LFN_3 +#define RT_DFS_ELM_USE_LFN 3 +#define RT_DFS_ELM_LFN_UNICODE_0 +#define RT_DFS_ELM_LFN_UNICODE 0 +#define RT_DFS_ELM_MAX_LFN 255 +#define RT_DFS_ELM_DRIVES 2 +#define RT_DFS_ELM_MAX_SECTOR_SIZE 512 +#define RT_DFS_ELM_REENTRANT +#define RT_DFS_ELM_MUTEX_TIMEOUT 3000 +#define RT_USING_DFS_DEVFS +#define RT_USING_LWP +#define RT_LWP_MAX_NR 30 +#define LWP_TASK_STACK_SIZE 16384 +#define RT_CH_MSG_MAX_NR 1024 +#define LWP_CONSOLE_INPUT_BUFFER_SIZE 1024 +#define LWP_TID_MAX_NR 64 +#define RT_LWP_SHM_MAX_NR 64 + +/* Device Drivers */ + +#define RT_USING_TTY +#define RT_USING_NULL +#define RT_USING_ZERO +#define RT_USING_RANDOM +#define RT_USING_DEVICE_IPC +#define RT_UNAMED_PIPE_NUMBER 64 +#define RT_USING_SYSTEM_WORKQUEUE +#define RT_SYSTEM_WORKQUEUE_STACKSIZE 8192 +#define RT_SYSTEM_WORKQUEUE_PRIORITY 23 +#define RT_USING_ADC +#define RT_ADC_ROCKCHIP_SARADC +#define RT_USING_CLK +#define RT_CLK_ROCKCHIP +#define RT_CLK_ROCKCHIP_RK3308 +#define RT_USING_FIRMWARE +#define RT_FIRMWARE_PSCI +#define RT_USING_HWCRYPTO +#define RT_HWCRYPTO_DEFAULT_NAME "hwcryto" +#define RT_HWCRYPTO_IV_MAX_SIZE 16 +#define RT_HWCRYPTO_KEYBIT_MAX_SIZE 256 +#define RT_HWCRYPTO_USING_RNG +#define RT_HWCRYPTO_RNG_ROCKCHIP +#define RT_USING_HWTIMER +#define RT_HWTIMER_ARM_ARCH +#define RT_HWTIMER_ROCKCHIP +#define RT_USING_I2C +#define RT_USING_I2C_BITOPS +#define RT_I2C_RK3X +#define RT_USING_LED +#define RT_LED_GPIO +#define RT_USING_MFD +#define RT_MFD_SYSCON +#define RT_USING_OFW +#define RT_FDT_EARLYCON_MSG_SIZE 128 +#define RT_USING_PHY +#define RT_USING_PIC +#define MAX_HANDLERS 512 +#define RT_PIC_ARM_GIC +#define RT_PIC_ARM_GIC_MAX_NR 1 +#define RT_USING_PINCTRL +#define RT_PINCTRL_ROCKCHIP +#define RT_USING_PIN +#define RT_PIN_ROCKCHIP +#define RT_USING_POWER_RESET +#define RT_POWER_RESET_SYSCON_REBOOT_MODE +#define RT_POWER_RESET_REBOOT_MODE +#define RT_USING_PWM +#define RT_PWM_ROCKCHIP +#define RT_USING_REGULATOR +#define RT_REGULATOR_GPIO +#define RT_USING_RESET +#define RT_USING_RTC +#define RT_USING_ALARM +#define RT_RTC_RK_TIMER +#define RT_USING_SDIO +#define RT_SDIO_STACK_SIZE 8192 +#define RT_SDIO_THREAD_PRIORITY 15 +#define RT_MMCSD_STACK_SIZE 8192 +#define RT_MMCSD_THREAD_PREORITY 22 +#define RT_MMCSD_MAX_PARTITION 16 +#define RT_SDIO_DW_MMC +#define RT_SDIO_DW_MMC_ROCKCHIP +#define RT_USING_SENSOR +#define RT_USING_SENSOR_CMD +#define RT_SENSOR_ROCKCHIP_TSADC +#define RT_USING_SERIAL +#define RT_USING_SERIAL_V1 +#define RT_SERIAL_RB_BUFSZ 64 +#define RT_SERIAL_8250 +#define RT_SERIAL_8250_DW +#define RT_USING_SOC +#define RT_SOC_ROCKCHIP +#define RT_SOC_ROCKCHIP_FIQ_DEBUGGER +#define RT_SOC_ROCKCHIP_GRF +#define RT_SOC_ROCKCHIP_IODOMAIN +#define RT_USING_SPI +#define RT_USING_QSPI +#define RT_SPI_ROCKCHIP_SFC +#define RT_SPI_ROCKCHIP +#define RT_USING_WDT +#define RT_WDT_DW + +/* Using USB */ + + +/* C/C++ and POSIX layer */ + +#define RT_LIBC_DEFAULT_TIMEZONE 8 + +/* POSIX (Portable Operating System Interface) layer */ + +#define RT_USING_POSIX_FS +#define RT_USING_POSIX_DEVIO +#define RT_USING_POSIX_STDIO +#define RT_USING_POSIX_TERMIOS +#define RT_USING_POSIX_DELAY +#define RT_USING_POSIX_CLOCK +#define RT_USING_POSIX_TIMER + +/* Interprocess Communication (IPC) */ + + +/* Socket is in the 'Network' category */ + + +/* Network */ + + +/* Utilities */ + +#define RT_USING_ADT +#define RT_USING_ADT_AVL +#define RT_USING_ADT_BITMAP +#define RT_USING_ADT_HASHMAP +#define RT_USING_ADT_REF + +/* RT-Thread Utestcases */ + + +/* RT-Thread online packages */ + +/* IoT - internet of things */ + + +/* Wi-Fi */ + +/* Marvell WiFi */ + + +/* Wiced WiFi */ + + +/* IoT Cloud */ + + +/* security packages */ + + +/* language packages */ + +/* JSON: JavaScript Object Notation, a lightweight data-interchange format */ + + +/* XML: Extensible Markup Language */ + + +/* multimedia packages */ + +/* LVGL: powerful and easy-to-use embedded GUI library */ + + +/* u8g2: a monochrome graphic library */ + + +/* tools packages */ + + +/* system packages */ + +/* enhanced kernel services */ + +#define PKG_USING_RT_VSNPRINTF_FULL +#define PKG_USING_RT_VSNPRINTF_FULL_LATEST_VERSION + +/* acceleration: Assembly language or algorithmic acceleration packages */ + + +/* CMSIS: ARM Cortex-M Microcontroller Software Interface Standard */ + + +/* Micrium: Micrium software products porting for RT-Thread */ + + +/* peripheral libraries and drivers */ + +/* sensors drivers */ + + +/* touch drivers */ + + +/* Kendryte SDK */ + + +/* AI packages */ + + +/* Signal Processing and Control Algorithm Packages */ + + +/* miscellaneous packages */ + +/* project laboratory */ + +/* samples: kernel and components samples */ + + +/* entertainment: terminal games and other interesting software packages */ + + +/* Arduino libraries */ + + +/* Projects */ + + +/* Sensors */ + + +/* Display */ + + +/* Timing */ + + +/* Data Processing */ + + +/* Data Storage */ + +/* Communication */ + + +/* Device Control */ + + +/* Other */ + + +/* Signal IO */ + + +/* Uncategorized */ + +#define SOC_RK3308 +#define RT_USING_AMBA_BUS + +#endif diff --git a/bsp/rockchip/rk3308/rtconfig.py b/bsp/rockchip/rk3308/rtconfig.py new file mode 100644 index 000000000000..eb7b77489714 --- /dev/null +++ b/bsp/rockchip/rk3308/rtconfig.py @@ -0,0 +1,54 @@ +import os + +# toolchains options +ARCH ='aarch64' +CPU ='cortex-a' +CROSS_TOOL ='gcc' + +if os.getenv('RTT_ROOT'): + RTT_ROOT = os.getenv('RTT_ROOT') +else: + RTT_ROOT = os.path.join(os.getcwd(), '..', '..', '..') + +if os.getenv('RTT_CC'): + CROSS_TOOL = os.getenv('RTT_CC') + +PLATFORM = 'gcc' +EXEC_PATH = r'/opt/gcc-arm-8.3-2019.03-x86_64-aarch64-elf/bin/' + +EXEC_PATH = os.getenv('RTT_EXEC_PATH') or '/usr/bin' + +BUILD = 'debug' + +if PLATFORM == 'gcc': + # toolchains + PREFIX = os.getenv('RTT_CC_PREFIX') or 'aarch64-none-elf-' + CC = PREFIX + 'gcc' + CXX = PREFIX + 'g++' + CPP = PREFIX + 'cpp' + AS = PREFIX + 'gcc' + AR = PREFIX + 'ar' + LINK = PREFIX + 'gcc' + TARGET_EXT = 'elf' + SIZE = PREFIX + 'size' + OBJDUMP = PREFIX + 'objdump' + OBJCPY = PREFIX + 'objcopy' + + DEVICE = ' -g -march=armv8-a -mtune=cortex-a35 -fdiagnostics-color=always' + CPPFLAGS= ' -E -P -x assembler-with-cpp' + CFLAGS = DEVICE + ' -Wall -Wno-cpp' + AFLAGS = ' -c' + ' -x assembler-with-cpp -D__ASSEMBLY__' + LFLAGS = DEVICE + ' -nostartfiles -Wl,--no-warn-rwx-segments -Wl,--gc-sections,-Map=rtthread.map,-cref,-u,system_vectors -T link.lds' + CPATH = '' + LPATH = '' + + if BUILD == 'debug': + CFLAGS += ' -O0 -gdwarf-2' + AFLAGS += ' -gdwarf-2' + else: + CFLAGS += ' -O2' + + CXXFLAGS = CFLAGS + +DUMP_ACTION = OBJDUMP + ' -D -S $TARGET > rtt.asm\n' +POST_ACTION = OBJCPY + ' -O binary $TARGET rtthread.bin\n' + SIZE + ' $TARGET \n' diff --git a/bsp/rockchip/rk3568/.config b/bsp/rockchip/rk3568/.config index 772d3c6469b0..e789337e51ff 100644 --- a/bsp/rockchip/rk3568/.config +++ b/bsp/rockchip/rk3568/.config @@ -6,10 +6,11 @@ # # RT-Thread Kernel # -CONFIG_RT_NAME_MAX=8 +CONFIG_RT_NAME_MAX=24 # CONFIG_RT_USING_ARCH_DATA_TYPE is not set -# CONFIG_RT_USING_SMART is not set -# CONFIG_RT_USING_SMP is not set +CONFIG_RT_USING_SMART=y +CONFIG_RT_USING_SMP=y +CONFIG_RT_CPUS_NR=4 CONFIG_RT_ALIGN_SIZE=8 # CONFIG_RT_THREAD_PRIORITY_8 is not set CONFIG_RT_THREAD_PRIORITY_32=y @@ -22,6 +23,7 @@ CONFIG_RT_HOOK_USING_FUNC_PTR=y CONFIG_RT_USING_IDLE_HOOK=y CONFIG_RT_IDLE_HOOK_LIST_SIZE=4 CONFIG_IDLE_THREAD_STACK_SIZE=4096 +CONFIG_SYSTEM_THREAD_STACK_SIZE=4096 CONFIG_RT_USING_TIMER_SOFT=y CONFIG_RT_TIMER_THREAD_PRIO=4 CONFIG_RT_TIMER_THREAD_STACK_SIZE=4096 @@ -44,6 +46,7 @@ CONFIG_RT_DEBUG_COLOR=y # CONFIG_RT_DEBUG_MEM_CONFIG is not set # CONFIG_RT_DEBUG_SLAB_CONFIG is not set # CONFIG_RT_DEBUG_MEMHEAP_CONFIG is not set +# CONFIG_RT_DEBUG_PAGE_LEAK is not set # CONFIG_RT_DEBUG_MODULE_CONFIG is not set # @@ -80,13 +83,18 @@ CONFIG_RT_USING_HEAP=y # CONFIG_RT_USING_DEVICE=y CONFIG_RT_USING_DEVICE_OPS=y -# CONFIG_RT_USING_DM is not set -# CONFIG_RT_USING_INTERRUPT_INFO is not set +CONFIG_RT_USING_DM=y +CONFIG_RT_USING_DM_FDT=y +CONFIG_RT_USING_INTERRUPT_INFO=y CONFIG_RT_USING_CONSOLE=y -CONFIG_RT_CONSOLEBUF_SIZE=128 +CONFIG_RT_CONSOLEBUF_SIZE=256 CONFIG_RT_CONSOLE_DEVICE_NAME="uart2" -CONFIG_RT_VER_NUM=0x50000 +CONFIG_RT_VER_NUM=0x50001 # CONFIG_RT_USING_STDC_ATOMIC is not set + +# +# RT-Thread Architecture +# CONFIG_ARCH_CPU_64BIT=y CONFIG_RT_USING_CACHE=y CONFIG_RT_USING_HW_ATOMIC=y @@ -96,7 +104,14 @@ CONFIG_RT_USING_HW_ATOMIC=y CONFIG_ARCH_MM_MMU=y CONFIG_ARCH_ARM=y CONFIG_ARCH_ARM_MMU=y +CONFIG_KERNEL_VADDR_START=0xffff000000000000 CONFIG_ARCH_ARMV8=y +CONFIG_ARCH_ARMV8_EXTENSIONS=0 +CONFIG_ARCH_TEXT_OFFSET=0x200000 +CONFIG_ARCH_RAM_OFFSET=0x200000 +CONFIG_ARCH_ASPACE_SIZE=0xffffffff +CONFIG_ARCH_SECONDARY_CPU_STACK_SIZE=4096 +CONFIG_ARCH_HAVE_EFFICIENT_UNALIGNED_ACCESS=y # # RT-Thread Components @@ -111,7 +126,7 @@ CONFIG_RT_USING_FINSH=y CONFIG_FINSH_USING_MSH=y CONFIG_FINSH_THREAD_NAME="tshell" CONFIG_FINSH_THREAD_PRIORITY=20 -CONFIG_FINSH_THREAD_STACK_SIZE=4096 +CONFIG_FINSH_THREAD_STACK_SIZE=8192 CONFIG_FINSH_USING_HISTORY=y CONFIG_FINSH_HISTORY_LINES=5 CONFIG_FINSH_USING_SYMTAB=y @@ -121,50 +136,247 @@ CONFIG_FINSH_USING_DESCRIPTION=y # CONFIG_FINSH_ECHO_DISABLE_DEFAULT is not set # CONFIG_FINSH_USING_AUTH is not set CONFIG_FINSH_ARG_MAX=10 -# CONFIG_RT_USING_DFS is not set + +# +# DFS: device virtual file system +# +CONFIG_RT_USING_DFS=y +CONFIG_DFS_USING_POSIX=y +CONFIG_DFS_USING_WORKDIR=y +# CONFIG_RT_USING_DFS_MNTTABLE is not set +CONFIG_DFS_FD_MAX=16 +CONFIG_RT_USING_DFS_V1=y +# CONFIG_RT_USING_DFS_V2 is not set +CONFIG_DFS_FILESYSTEMS_MAX=4 +CONFIG_DFS_FILESYSTEM_TYPES_MAX=4 +CONFIG_RT_USING_DFS_ELMFAT=y + +# +# elm-chan's FatFs, Generic FAT Filesystem Module +# +CONFIG_RT_DFS_ELM_CODE_PAGE=437 +CONFIG_RT_DFS_ELM_WORD_ACCESS=y +# CONFIG_RT_DFS_ELM_USE_LFN_0 is not set +# CONFIG_RT_DFS_ELM_USE_LFN_1 is not set +# CONFIG_RT_DFS_ELM_USE_LFN_2 is not set +CONFIG_RT_DFS_ELM_USE_LFN_3=y +CONFIG_RT_DFS_ELM_USE_LFN=3 +CONFIG_RT_DFS_ELM_LFN_UNICODE_0=y +# CONFIG_RT_DFS_ELM_LFN_UNICODE_1 is not set +# CONFIG_RT_DFS_ELM_LFN_UNICODE_2 is not set +# CONFIG_RT_DFS_ELM_LFN_UNICODE_3 is not set +CONFIG_RT_DFS_ELM_LFN_UNICODE=0 +CONFIG_RT_DFS_ELM_MAX_LFN=255 +CONFIG_RT_DFS_ELM_DRIVES=2 +CONFIG_RT_DFS_ELM_MAX_SECTOR_SIZE=512 +# CONFIG_RT_DFS_ELM_USE_ERASE is not set +CONFIG_RT_DFS_ELM_REENTRANT=y +CONFIG_RT_DFS_ELM_MUTEX_TIMEOUT=3000 +CONFIG_RT_USING_DFS_DEVFS=y +# CONFIG_RT_USING_DFS_DIRECTFS is not set +# CONFIG_RT_USING_DFS_ROMFS is not set +# CONFIG_RT_USING_DFS_CROMFS is not set +# CONFIG_RT_USING_DFS_RAMFS is not set +# CONFIG_RT_USING_DFS_TMPFS is not set # CONFIG_RT_USING_FAL is not set +CONFIG_RT_USING_LWP=y +CONFIG_RT_LWP_MAX_NR=30 +CONFIG_LWP_TASK_STACK_SIZE=16384 +CONFIG_RT_CH_MSG_MAX_NR=1024 +CONFIG_LWP_CONSOLE_INPUT_BUFFER_SIZE=1024 +CONFIG_LWP_TID_MAX_NR=64 +CONFIG_RT_LWP_SHM_MAX_NR=64 +# CONFIG_LWP_UNIX98_PTY is not set # # Device Drivers # -CONFIG_RT_USING_DEVICE_IPC=y -CONFIG_RT_UNAMED_PIPE_NUMBER=64 -# CONFIG_RT_USING_SYSTEM_WORKQUEUE is not set -CONFIG_RT_USING_SERIAL=y -CONFIG_RT_USING_SERIAL_V1=y -# CONFIG_RT_USING_SERIAL_V2 is not set -# CONFIG_RT_SERIAL_USING_DMA is not set -CONFIG_RT_SERIAL_RB_BUFSZ=64 -# CONFIG_RT_USING_CAN is not set -# CONFIG_RT_USING_HWTIMER is not set +CONFIG_RT_USING_TTY=y +# CONFIG_RT_TTY_DEBUG is not set # CONFIG_RT_USING_CPUTIME is not set -# CONFIG_RT_USING_I2C is not set -# CONFIG_RT_USING_PHY is not set -CONFIG_RT_USING_PIN=y -# CONFIG_RT_USING_ADC is not set # CONFIG_RT_USING_DAC is not set -# CONFIG_RT_USING_NULL is not set -# CONFIG_RT_USING_ZERO is not set -# CONFIG_RT_USING_RANDOM is not set -# CONFIG_RT_USING_PWM is not set -# CONFIG_RT_USING_MTD_NOR is not set -# CONFIG_RT_USING_MTD_NAND is not set +CONFIG_RT_USING_NULL=y +CONFIG_RT_USING_ZERO=y +CONFIG_RT_USING_RANDOM=y # CONFIG_RT_USING_PM is not set -# CONFIG_RT_USING_FDT is not set -# CONFIG_RT_USING_RTC is not set -# CONFIG_RT_USING_SDIO is not set -# CONFIG_RT_USING_SPI is not set -# CONFIG_RT_USING_WDT is not set -# CONFIG_RT_USING_AUDIO is not set -# CONFIG_RT_USING_SENSOR is not set # CONFIG_RT_USING_TOUCH is not set # CONFIG_RT_USING_LCD is not set -# CONFIG_RT_USING_HWCRYPTO is not set # CONFIG_RT_USING_PULSE_ENCODER is not set # CONFIG_RT_USING_INPUT_CAPTURE is not set # CONFIG_RT_USING_DEV_BUS is not set # CONFIG_RT_USING_WIFI is not set +CONFIG_RT_USING_DEVICE_IPC=y +CONFIG_RT_UNAMED_PIPE_NUMBER=64 +CONFIG_RT_USING_SYSTEM_WORKQUEUE=y +CONFIG_RT_SYSTEM_WORKQUEUE_STACKSIZE=8192 +CONFIG_RT_SYSTEM_WORKQUEUE_PRIORITY=23 +CONFIG_RT_USING_ADC=y +CONFIG_RT_ADC_ROCKCHIP_SARADC=y +CONFIG_RT_USING_AUDIO=y +CONFIG_RT_AUDIO_REPLAY_MP_BLOCK_SIZE=4096 +CONFIG_RT_AUDIO_REPLAY_MP_BLOCK_COUNT=2 +CONFIG_RT_AUDIO_RECORD_PIPE_SIZE=2048 +# CONFIG_RT_AUDIO_INTEL_HDA is not set +CONFIG_RT_AUDIO_ROCKCHIP=y +CONFIG_RT_AUDIO_ROCKCHIP_I2S=y +CONFIG_RT_AUDIO_ROCKCHIP_I2S_TDM=y +CONFIG_RT_AUDIO_ROCKCHIP_PDM=y +CONFIG_RT_AUDIO_ROCKCHIP_SPDIF=y +CONFIG_RT_USING_CAN=y +# CONFIG_RT_CAN_USING_HDR is not set +CONFIG_RT_CAN_USING_CANFD=y +CONFIG_RT_CAN_CANFD_ROCKCHIP=y +CONFIG_RT_USING_CLK=y +CONFIG_RT_CLK_SCMI=y +CONFIG_RT_CLK_ROCKCHIP_RK8XX_CLKOUT=y +CONFIG_RT_CLK_ROCKCHIP=y +# CONFIG_RT_CLK_ROCKCHIP_RK3308 is not set +CONFIG_RT_CLK_ROCKCHIP_RK3568=y +CONFIG_RT_USING_FIRMWARE=y +CONFIG_RT_FIRMWARE_PSCI=y +# CONFIG_RT_FIRMWARE_QEMU_FW_CFG is not set +CONFIG_RT_FIRMWARE_ARM_SCMI=y +CONFIG_RT_FIRMWARE_ARM_SCMI_TRANSPORT_SMC=y +CONFIG_RT_USING_HWCRYPTO=y +CONFIG_RT_HWCRYPTO_DEFAULT_NAME="hwcryto" +CONFIG_RT_HWCRYPTO_IV_MAX_SIZE=16 +CONFIG_RT_HWCRYPTO_KEYBIT_MAX_SIZE=256 +# CONFIG_RT_HWCRYPTO_USING_GCM is not set +# CONFIG_RT_HWCRYPTO_USING_AES is not set +# CONFIG_RT_HWCRYPTO_USING_DES is not set +# CONFIG_RT_HWCRYPTO_USING_3DES is not set +# CONFIG_RT_HWCRYPTO_USING_RC4 is not set +# CONFIG_RT_HWCRYPTO_USING_MD5 is not set +# CONFIG_RT_HWCRYPTO_USING_SHA1 is not set +# CONFIG_RT_HWCRYPTO_USING_SHA2 is not set +CONFIG_RT_HWCRYPTO_USING_RNG=y +# CONFIG_RT_HWCRYPTO_USING_CRC is not set +# CONFIG_RT_HWCRYPTO_USING_BIGNUM is not set +CONFIG_RT_HWCRYPTO_RNG_ROCKCHIP=y +CONFIG_RT_USING_HWSPINLOCK=y +CONFIG_RT_HWSPINLOCK_ROCKCHIP=y +CONFIG_RT_USING_HWTIMER=y +CONFIG_RT_HWTIMER_ARM_ARCH=y +# CONFIG_RT_HWTIMER_BCM2835 is not set +CONFIG_RT_HWTIMER_ROCKCHIP=y +CONFIG_RT_USING_I2C=y +# CONFIG_RT_I2C_DEBUG is not set +CONFIG_RT_USING_I2C_BITOPS=y +# CONFIG_RT_I2C_BITOPS_DEBUG is not set +CONFIG_RT_I2C_RK3X=y +CONFIG_RT_USING_INPUT=y +CONFIG_RT_INPUT_KEYBOARD=y +CONFIG_RT_INPUT_KEYBOARD_GPIO_KEYS=y +CONFIG_RT_INPUT_MISC=y +CONFIG_RT_INPUT_MISC_RK8XX_PWRKEY=y +CONFIG_RT_USING_LED=y +CONFIG_RT_LED_GPIO=y +CONFIG_RT_USING_MBOX=y +# CONFIG_RT_MBOX_BCM2835 is not set +CONFIG_RT_MBOX_ROCKCHIP=y +CONFIG_RT_USING_MFD=y +# CONFIG_RT_MFD_BCM2835_PM is not set +CONFIG_RT_MFD_SYSCON=y +CONFIG_RT_MFD_RK8XX=y +CONFIG_RT_MFD_RK8XX_I2C=y +# CONFIG_RT_MFD_RK8XX_SPI is not set +# CONFIG_RT_USING_MTD_NAND is not set +# CONFIG_RT_USING_MTD_NOR is not set +CONFIG_RT_USING_OFW=y +# CONFIG_RT_USING_BUILTIN_FDT is not set +CONFIG_RT_FDT_EARLYCON_MSG_SIZE=128 +# CONFIG_RT_USING_OFW_DIRECTFS is not set +CONFIG_RT_USING_PCI=y +CONFIG_RT_PCI_MSI=y +CONFIG_RT_PCI_SYS_64BIT=y +CONFIG_RT_PCI_CACHE_LINE_SIZE=8 +# CONFIG_RT_PCI_LOCKLESS is not set +# CONFIG_RT_PCI_ECAM is not set +CONFIG_RT_PCI_DW_HOST=y +CONFIG_RT_PCI_DW_HOST_ROCKCHIP=y +CONFIG_RT_USING_PHY=y +CONFIG_RT_PHY_ROCKCHIP_NANENG_COMBO=y +CONFIG_RT_PHY_ROCKCHIP_SNPS_PCIE3=y +CONFIG_RT_USING_PIC=y +CONFIG_MAX_HANDLERS=512 +# CONFIG_RT_PIC_BCM2835_INTC is not set +# CONFIG_RT_PIC_BCM2836_L1_INTC is not set +# CONFIG_RT_PIC_ARM_GIC is not set +# CONFIG_RT_PIC_ARM_GIC_V2M is not set +CONFIG_RT_PIC_ARM_GIC_V3=y +# CONFIG_RT_PIC_ARM_GIC_V3_ITS is not set +CONFIG_RT_USING_PINCTRL=y +CONFIG_RT_PINCTRL_ROCKCHIP=y +# CONFIG_RT_PINCTRL_ROCKCHIP_RK8XX is not set +CONFIG_RT_USING_PIN=y +# CONFIG_RT_PIN_PL061 is not set +CONFIG_RT_PIN_ROCKCHIP=y +CONFIG_RT_USING_POWER_RESET=y +# CONFIG_RT_POWER_RESET_SYSCON_POWEROFF is not set +CONFIG_RT_POWER_RESET_SYSCON_REBOOT_MODE=y +# CONFIG_RT_POWER_RESET_SYSCON_REBOOT is not set +CONFIG_RT_POWER_RESET_REBOOT_MODE=y +CONFIG_RT_USING_PWM=y +CONFIG_RT_PWM_ROCKCHIP=y +CONFIG_RT_USING_REGULATOR=y +CONFIG_RT_REGULATOR_GPIO=y +CONFIG_RT_REGULATOR_RK8XX=y +CONFIG_RT_USING_RESET=y +CONFIG_RT_RESET_SCMI=y +CONFIG_RT_USING_RTC=y +CONFIG_RT_USING_ALARM=y +# CONFIG_RT_USING_SOFT_RTC is not set +# CONFIG_RT_RTC_GOLDFISH is not set +CONFIG_RT_RTC_HYM8563=y +# CONFIG_RT_RTC_PL031 is not set +CONFIG_RT_RTC_RK8XX=y +# CONFIG_RT_RTC_RK_TIMER is not set +CONFIG_RT_USING_SDIO=y +CONFIG_RT_SDIO_STACK_SIZE=8192 +CONFIG_RT_SDIO_THREAD_PRIORITY=15 +CONFIG_RT_MMCSD_STACK_SIZE=8192 +CONFIG_RT_MMCSD_THREAD_PREORITY=22 +CONFIG_RT_MMCSD_MAX_PARTITION=16 +# CONFIG_RT_SDIO_DEBUG is not set +CONFIG_RT_SDIO_SDHCI_DWCMSHC=y +CONFIG_RT_SDIO_DW_MMC=y +# CONFIG_RT_SDIO_DW_MMC_PCI is not set +CONFIG_RT_SDIO_DW_MMC_ROCKCHIP=y +CONFIG_RT_USING_SENSOR=y +CONFIG_RT_USING_SENSOR_CMD=y +CONFIG_RT_SENSOR_ROCKCHIP_TSADC=y +CONFIG_RT_USING_SERIAL=y +CONFIG_RT_USING_SERIAL_V1=y +# CONFIG_RT_USING_SERIAL_V2 is not set +# CONFIG_RT_SERIAL_USING_DMA is not set +CONFIG_RT_SERIAL_RB_BUFSZ=64 +# CONFIG_RT_SERIAL_PL011 is not set +CONFIG_RT_SERIAL_8250=y +# CONFIG_RT_SERIAL_8250_BCM2835AUX is not set +CONFIG_RT_SERIAL_8250_DW=y +# CONFIG_RT_SERIAL_8250_OFW is not set +# CONFIG_RT_SERIAL_8250_PCI is not set +CONFIG_RT_USING_SOC=y +CONFIG_RT_SOC_ROCKCHIP=y +CONFIG_RT_SOC_ROCKCHIP_FIQ_DEBUGGER=y +CONFIG_RT_SOC_ROCKCHIP_GRF=y +# CONFIG_RT_SOC_ROCKCHIP_HW_DECOMPRESS is not set +CONFIG_RT_SOC_ROCKCHIP_IODOMAIN=y +CONFIG_RT_SOC_ROCKCHIP_PMDOMAIN=y +CONFIG_RT_USING_SPI=y +# CONFIG_RT_USING_SPI_BITOPS is not set +CONFIG_RT_USING_QSPI=y +# CONFIG_RT_USING_SPI_MSD is not set +# CONFIG_RT_USING_SFUD is not set +# CONFIG_RT_USING_ENC28J60 is not set +# CONFIG_RT_USING_SPI_WIFI is not set +CONFIG_RT_SPI_ROCKCHIP_SFC=y +CONFIG_RT_SPI_ROCKCHIP=y # CONFIG_RT_USING_VIRTIO is not set +CONFIG_RT_USING_WDT=y +# CONFIG_RT_WDT_BCM2835 is not set +CONFIG_RT_WDT_DW=y +# CONFIG_RT_WDT_RK8XX is not set # # Using USB @@ -181,10 +393,18 @@ CONFIG_RT_LIBC_DEFAULT_TIMEZONE=8 # # POSIX (Portable Operating System Interface) layer # -# CONFIG_RT_USING_POSIX_FS is not set -# CONFIG_RT_USING_POSIX_DELAY is not set -# CONFIG_RT_USING_POSIX_CLOCK is not set -# CONFIG_RT_USING_POSIX_TIMER is not set +CONFIG_RT_USING_POSIX_FS=y +CONFIG_RT_USING_POSIX_DEVIO=y +CONFIG_RT_USING_POSIX_STDIO=y +# CONFIG_RT_USING_POSIX_POLL is not set +# CONFIG_RT_USING_POSIX_SELECT is not set +# CONFIG_RT_USING_POSIX_SOCKET is not set +CONFIG_RT_USING_POSIX_TERMIOS=y +# CONFIG_RT_USING_POSIX_AIO is not set +# CONFIG_RT_USING_POSIX_MMAN is not set +CONFIG_RT_USING_POSIX_DELAY=y +CONFIG_RT_USING_POSIX_CLOCK=y +CONFIG_RT_USING_POSIX_TIMER=y # CONFIG_RT_USING_PTHREADS is not set # CONFIG_RT_USING_MODULE is not set @@ -216,6 +436,10 @@ CONFIG_RT_LIBC_DEFAULT_TIMEZONE=8 # CONFIG_RT_USING_UTEST is not set # CONFIG_RT_USING_VAR_EXPORT is not set CONFIG_RT_USING_ADT=y +CONFIG_RT_USING_ADT_AVL=y +CONFIG_RT_USING_ADT_BITMAP=y +CONFIG_RT_USING_ADT_HASHMAP=y +CONFIG_RT_USING_ADT_REF=y # CONFIG_RT_USING_RT_LINK is not set # CONFIG_RT_USING_VBUS is not set @@ -452,7 +676,15 @@ CONFIG_RT_USING_ADT=y # # CONFIG_PKG_USING_RT_MEMCPY_CM is not set # CONFIG_PKG_USING_RT_KPRINTF_THREADSAFE is not set -# CONFIG_PKG_USING_RT_VSNPRINTF_FULL is not set +CONFIG_PKG_USING_RT_VSNPRINTF_FULL=y +CONFIG_PKG_RT_VSNPRINTF_FULL_PATH="/packages/system/enhanced-kservice/rt_vsnprintf_full" +# CONFIG_RT_VSNPRINTF_FULL_REPLACING_SPRINTF is not set +# CONFIG_RT_VSNPRINTF_FULL_REPLACING_SNPRINTF is not set +# CONFIG_RT_VSNPRINTF_FULL_REPLACING_PRINTF is not set +# CONFIG_RT_VSNPRINTF_FULL_REPLACING_VSPRINTF is not set +# CONFIG_RT_VSNPRINTF_FULL_REPLACING_VSNPRINTF is not set +CONFIG_PKG_USING_RT_VSNPRINTF_FULL_LATEST_VERSION=y +CONFIG_PKG_RT_VSNPRINTF_FULL_VER="latest" # # acceleration: Assembly language or algorithmic acceleration packages @@ -513,6 +745,7 @@ CONFIG_RT_USING_ADT=y # CONFIG_PKG_USING_QPC is not set # CONFIG_PKG_USING_AGILE_UPGRADE is not set # CONFIG_PKG_USING_FLASH_BLOB is not set +# CONFIG_PKG_USING_MLIBC is not set # # peripheral libraries and drivers @@ -609,7 +842,6 @@ CONFIG_RT_USING_ADT=y # CONFIG_PKG_USING_LKDGUI is not set # CONFIG_PKG_USING_NRF5X_SDK is not set # CONFIG_PKG_USING_NRFX is not set -# CONFIG_PKG_USING_WM_LIBRARIES is not set # # Kendryte SDK @@ -667,14 +899,15 @@ CONFIG_RT_USING_ADT=y # CONFIG_PKG_USING_MISAKA_AT24CXX is not set # CONFIG_PKG_USING_MISAKA_RGB_BLING is not set # CONFIG_PKG_USING_LORA_MODEM_DRIVER is not set -# CONFIG_PKG_USING_BL_MCU_SDK is not set # CONFIG_PKG_USING_SOFT_SERIAL is not set # CONFIG_PKG_USING_MB85RS16 is not set # CONFIG_PKG_USING_RFM300 is not set # CONFIG_PKG_USING_IO_INPUT_FILTER is not set # CONFIG_PKG_USING_RASPBERRYPI_PICO_SDK is not set # CONFIG_PKG_USING_LRF_NV7LIDAR is not set +# CONFIG_PKG_USING_AIP650 is not set # CONFIG_PKG_USING_FINGERPRINT is not set +# CONFIG_PKG_USING_SPI_TOOLS is not set # # AI packages @@ -688,6 +921,7 @@ CONFIG_RT_USING_ADT=y # CONFIG_PKG_USING_ULAPACK is not set # CONFIG_PKG_USING_QUEST is not set # CONFIG_PKG_USING_NAXOS is not set +# CONFIG_PKG_USING_NCNN is not set # # Signal Processing and Control Algorithm Packages @@ -972,20 +1206,3 @@ CONFIG_RT_USING_ADT=y # Uncategorized # CONFIG_SOC_RK3568=y - -# -# Hardware Drivers Config -# -CONFIG_BSP_USING_UART=y -# CONFIG_RT_USING_UART0 is not set -# CONFIG_RT_USING_UART1 is not set -CONFIG_RT_USING_UART2=y -# CONFIG_RT_USING_UART3 is not set -# CONFIG_RT_USING_UART4 is not set -# CONFIG_RT_USING_UART5 is not set -# CONFIG_RT_USING_UART6 is not set -# CONFIG_RT_USING_UART7 is not set -# CONFIG_RT_USING_UART8 is not set -# CONFIG_RT_USING_UART9 is not set -CONFIG_BSP_USING_GIC=y -CONFIG_BSP_USING_GICV3=y diff --git a/bsp/rockchip/rk3568/Kconfig b/bsp/rockchip/rk3568/Kconfig index 8dc3754d64d4..29f614e59407 100644 --- a/bsp/rockchip/rk3568/Kconfig +++ b/bsp/rockchip/rk3568/Kconfig @@ -22,9 +22,8 @@ config SOC_RK3568 bool select ARCH_ARMV8 select ARCH_CPU_64BIT + select ARCH_ARM_MMU select RT_USING_CACHE select RT_USING_COMPONENTS_INIT select RT_USING_USER_MAIN default y - -source "$BSP_DIR/driver/Kconfig" diff --git a/bsp/rockchip/rk3568/applications/main.c b/bsp/rockchip/rk3568/applications/main.c index 780e49d0918c..27b230e675b5 100644 --- a/bsp/rockchip/rk3568/applications/main.c +++ b/bsp/rockchip/rk3568/applications/main.c @@ -8,11 +8,24 @@ * 2017-5-30 Bernard the first version */ +#include #include int main(int argc, char** argv) { - rt_kprintf("Hi, this is RT-Thread!!\n"); + const char *oem; + +#ifdef RT_USING_SMART + oem = "Smart"; +#else + oem = "Thread"; +#endif + + rt_ubase_t level = rt_hw_interrupt_disable(); + + rt_kprintf("Hi, this is RT-%s!!\n", oem); + + rt_hw_interrupt_enable(level); return 0; } diff --git a/bsp/rockchip/rk3568/driver/Kconfig b/bsp/rockchip/rk3568/driver/Kconfig deleted file mode 100644 index 55041eba5b39..000000000000 --- a/bsp/rockchip/rk3568/driver/Kconfig +++ /dev/null @@ -1,58 +0,0 @@ -menu "Hardware Drivers Config" - - menuconfig BSP_USING_UART - bool "Using UART" - select RT_USING_SERIAL - default y - - if BSP_USING_UART - config RT_USING_UART0 - bool "Enable UART 0" - default n - - config RT_USING_UART1 - bool "Enable UART 1" - default n - - config RT_USING_UART2 - bool "Enable UART 2" - default y - - config RT_USING_UART3 - bool "Enable UART 3" - default n - - config RT_USING_UART4 - bool "Enable UART 4" - default n - - config RT_USING_UART5 - bool "Enable UART 5" - default n - - config RT_USING_UART6 - bool "Enable UART 6" - default n - - config RT_USING_UART7 - bool "Enable UART 7" - default n - - config RT_USING_UART8 - bool "Enable UART 8" - default n - - config RT_USING_UART9 - bool "Enable UART 9" - default n - endif - - config BSP_USING_GIC - bool - default y - - config BSP_USING_GICV3 - bool - default y - -endmenu diff --git a/bsp/rockchip/rk3568/driver/board.c b/bsp/rockchip/rk3568/driver/board.c index 592262cb4d58..6771c2f4c1c4 100644 --- a/bsp/rockchip/rk3568/driver/board.c +++ b/bsp/rockchip/rk3568/driver/board.c @@ -6,120 +6,20 @@ * Change Logs: * Date Author Notes * 2022-3-08 GuEe-GUI the first version + * 2023-02-21 GuEe-GUI move common init to setup */ -#include -#include - -#include -#include -#include -#include -#include -#include - +#include #include -#include - -struct mem_desc platform_mem_desc[] = -{ - {0x200000, 0x80000000, 0x200000, NORMAL_MEM}, - {UART0_MMIO_BASE, UART0_MMIO_BASE + 0x10000, UART0_MMIO_BASE, DEVICE_MEM}, - {UART1_MMIO_BASE, UART1_MMIO_BASE + 0x90000, UART1_MMIO_BASE, DEVICE_MEM}, - {GIC_PL600_DISTRIBUTOR_PPTR, GIC_PL600_DISTRIBUTOR_PPTR + 0x10000, GIC_PL600_DISTRIBUTOR_PPTR, DEVICE_MEM}, - {GIC_PL600_REDISTRIBUTOR_PPTR, GIC_PL600_REDISTRIBUTOR_PPTR + 0xc0000, GIC_PL600_REDISTRIBUTOR_PPTR, DEVICE_MEM}, -}; - -const rt_uint32_t platform_mem_desc_size = sizeof(platform_mem_desc) / sizeof(platform_mem_desc[0]); - -void idle_wfi(void) -{ - __asm__ volatile ("wfi"); -} void rt_hw_board_init(void) { - extern void *MMUTable; - rt_hw_mmu_map_init(&rt_kernel_space, (void*)0x80000000, 0x10000000, MMUTable, 0); - rt_hw_mmu_setup(&rt_kernel_space, platform_mem_desc, platform_mem_desc_size); - - /* initialize hardware interrupt */ - rt_hw_interrupt_init(); - /* initialize uart */ - rt_hw_uart_init(); - /* initialize timer for os tick */ - rt_hw_gtimer_init(); - - rt_thread_idle_sethook(idle_wfi); - - // TODO porting to FDT-driven PSCI: arm_psci_init(PSCI_METHOD_SMC, RT_NULL, RT_NULL); - -#if defined(RT_USING_CONSOLE) && defined(RT_USING_DEVICE) - /* set console device */ - rt_console_set_device(RT_CONSOLE_DEVICE_NAME); -#endif - -#ifdef RT_USING_HEAP - /* initialize memory system */ - rt_kprintf("heap: [0x%08x - 0x%08x]\n", RT_HW_HEAP_BEGIN, RT_HW_HEAP_END); - rt_system_heap_init(RT_HW_HEAP_BEGIN, RT_HW_HEAP_END); -#endif - -#ifdef RT_USING_COMPONENTS_INIT - rt_components_board_init(); -#endif - -#ifdef RT_USING_SMP - /* install IPI handle */ - rt_hw_ipi_handler_install(RT_SCHEDULE_IPI, rt_scheduler_ipi_handler); - arm_gic_umask(0, IRQ_ARM_IPI_KICK); -#endif -} - -void reboot(void) -{ - // TODO poring to FDT to use new PSCI: arm_psci_system_reboot(); -} -MSH_CMD_EXPORT(reboot, reboot...); - -#ifdef RT_USING_SMP -rt_uint64_t rt_cpu_mpidr_early[] = -{ - [0] = 0x81000000, - [1] = 0x81000100, - [2] = 0x81000200, - [3] = 0x81000300, -}; - -void rt_hw_secondary_cpu_up(void) -{ - int i; - extern void secondary_cpu_start(void); - - for (i = 1; i < RT_CPUS_NR; ++i) + rt_fdt_commit_memregion_early(&(rt_region_t) { - arm_psci_cpu_on(rt_cpu_mpidr_early[i], (rt_uint64_t)secondary_cpu_start); - } -} - -void secondary_cpu_c_start(void) -{ - rt_hw_mmu_init(); - rt_hw_spin_lock(&_cpus_lock); - - arm_gic_cpu_init(0, platform_get_gic_cpu_base()); - arm_gic_redist_init(0, platform_get_gic_redist_base()); - rt_hw_vector_init(); - rt_hw_gtimer_local_enable(); - arm_gic_umask(0, IRQ_ARM_IPI_KICK); + .name = "memheap", + .start = (rt_size_t)rt_kmem_v2p(HEAP_BEGIN), + .end = (rt_size_t)rt_kmem_v2p(HEAP_END), + }, RT_TRUE); - rt_kprintf("\rcall cpu %d on success\n", rt_hw_cpu_id()); - - rt_system_scheduler_start(); -} - -void rt_hw_secondary_cpu_idle_exec(void) -{ - __WFE(); + rt_hw_common_setup(); } -#endif diff --git a/bsp/rockchip/rk3568/driver/board.h b/bsp/rockchip/rk3568/driver/board.h index 9f57b7b0b6db..53318a1dc39f 100644 --- a/bsp/rockchip/rk3568/driver/board.h +++ b/bsp/rockchip/rk3568/driver/board.h @@ -11,18 +11,13 @@ #ifndef __BOARD_H__ #define __BOARD_H__ -#include +#include extern unsigned char __bss_start; extern unsigned char __bss_end; -#define RT_HW_HEAP_BEGIN (void *)&__bss_end -#define RT_HW_HEAP_END (void *)(RT_HW_HEAP_BEGIN + 64 * 1024 * 1024) - -#ifndef RT_USING_SMART -#define PV_OFFSET 0 -#define KERNEL_VADDR_START 0 -#endif +#define HEAP_BEGIN (void *)&__bss_end +#define HEAP_END ((void *)HEAP_BEGIN + 64 * 1024 * 1024) void rt_hw_board_init(void); diff --git a/bsp/rockchip/rk3568/driver/drv_uart.c b/bsp/rockchip/rk3568/driver/drv_uart.c deleted file mode 100644 index 477c2f077d1a..000000000000 --- a/bsp/rockchip/rk3568/driver/drv_uart.c +++ /dev/null @@ -1,358 +0,0 @@ -/* - * Copyright (c) 2006-2022, RT-Thread Development Team - * - * SPDX-License-Identifier: Apache-2.0 - * - * Change Logs: - * Date Author Notes - * 2022-3-08 GuEe-GUI the first version - */ - -#include -#include -#include - -#include - -/* - * The Synopsys DesignWare 8250 has an extra feature whereby it detects if the - * LCR is written whilst busy. If it is, then a busy detect interrupt is - * raised, the LCR needs to be rewritten and the uart status register read. - */ - -#define UART_RX 0 /* In: Receive buffer */ -#define UART_TX 0 /* Out: Transmit buffer */ - -#define UART_DLL 0 /* Out: Divisor Latch Low */ -#define UART_DLM 1 /* Out: Divisor Latch High */ - -#define UART_IER 1 /* Out: Interrupt Enable Register */ -#define UART_IER_RDI 0x01 /* Enable receiver data interrupt */ - -#define UART_SSR 0x22 /* In: Software Reset Register */ -#define UART_USR 0x1f /* UART Status Register */ - -#define UART_LCR 3 /* Out: Line Control Register */ -#define UART_LCR_DLAB 0x80 /* Divisor latch access bit */ -#define UART_LCR_SPAR 0x20 /* Stick parity (?) */ -#define UART_LCR_PARITY 0x8 /* Parity Enable */ -#define UART_LCR_STOP 0x4 /* Stop bits: 0=1 bit, 1=2 bits */ -#define UART_LCR_WLEN8 0x3 /* Wordlength: 8 bits */ - -#define UART_MCR 4 /* Out: Modem Control Register */ -#define UART_MCR_RTS 0x02 /* RTS complement */ - -#define UART_LSR 5 /* In: Line Status Register */ -#define UART_LSR_BI 0x10 /* Break interrupt indicator */ -#define UART_LSR_DR 0x01 /* Receiver data ready */ - -#define UART_IIR 2 /* In: Interrupt ID Register */ -#define UART_IIR_NO_INT 0x01 /* No interrupts pending */ -#define UART_IIR_BUSY 0x07 /* DesignWare APB Busy Detect */ -#define UART_IIR_RX_TIMEOUT 0x0c /* OMAP RX Timeout interrupt */ - -#define UART_FCR 2 /* Out: FIFO Control Register */ -#define UART_FCR_EN_FIFO 0x01 /* Enable the FIFO */ -#define UART_FCR_CLEAR_RCVR 0x02 /* Clear the RCVR FIFO */ -#define UART_FCR_CLEAR_XMIT 0x04 /* Clear the XMIT FIFO */ - -#define UART_REG_SHIFT 0x2 /* Register Shift*/ -#define UART_INPUT_CLK 24000000 - -struct hw_uart_device -{ - rt_ubase_t hw_base; - rt_uint32_t irqno; -#ifdef RT_USING_SMP - struct rt_spinlock spinlock; -#endif -}; - -#define BSP_DEFINE_UART_DEVICE(no) \ -static struct hw_uart_device _uart##no##_device = \ -{ \ - UART##no##_MMIO_BASE, \ - UART##no##_IRQ \ -}; \ -static struct rt_serial_device _serial##no; - -#ifdef RT_USING_UART0 -BSP_DEFINE_UART_DEVICE(0); -#endif - -#ifdef RT_USING_UART1 -BSP_DEFINE_UART_DEVICE(1); -#endif - -#ifdef RT_USING_UART2 -BSP_DEFINE_UART_DEVICE(2); -#endif - -#ifdef RT_USING_UART3 -BSP_DEFINE_UART_DEVICE(3); -#endif - -#ifdef RT_USING_UART4 -BSP_DEFINE_UART_DEVICE(4); -#endif - -#ifdef RT_USING_UART5 -BSP_DEFINE_UART_DEVICE(5); -#endif - -#ifdef RT_USING_UART6 -BSP_DEFINE_UART_DEVICE(6); -#endif - -#ifdef RT_USING_UART7 -BSP_DEFINE_UART_DEVICE(7); -#endif - -#ifdef RT_USING_UART8 -BSP_DEFINE_UART_DEVICE(8); -#endif - -#ifdef RT_USING_UART9 -BSP_DEFINE_UART_DEVICE(9); -#endif - -rt_inline rt_uint32_t dw8250_read32(rt_ubase_t addr, rt_ubase_t offset) -{ - return *((volatile rt_uint32_t *)(addr + (offset << UART_REG_SHIFT))); -} - -rt_inline void dw8250_write32(rt_ubase_t addr, rt_ubase_t offset, rt_uint32_t value) -{ - *((volatile rt_uint32_t *)(addr + (offset << UART_REG_SHIFT))) = value; - - if (offset == UART_LCR) - { - int tries = 1000; - - /* Make sure LCR write wasn't ignored */ - while (tries--) - { - unsigned int lcr = dw8250_read32(addr, UART_LCR); - - if ((value & ~UART_LCR_SPAR) == (lcr & ~UART_LCR_SPAR)) - { - return; - } - - dw8250_write32(addr, UART_FCR, UART_FCR_EN_FIFO | UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); - dw8250_read32(addr, UART_RX); - - *((volatile rt_uint32_t *)(addr + (offset << UART_REG_SHIFT))) = value; - } - } -} - -static rt_err_t dw8250_uart_configure(struct rt_serial_device *serial, struct serial_configure *cfg) -{ - rt_base_t base, rate; - struct hw_uart_device *uart; - - RT_ASSERT(serial != RT_NULL); - uart = (struct hw_uart_device *)serial->parent.user_data; - base = uart->hw_base; - -#ifdef RT_USING_SMP - rt_spin_lock_init(&uart->spinlock); -#endif - - /* Resset UART */ - dw8250_write32(base, UART_SSR, 1); - dw8250_write32(base, UART_SSR, 0); - - dw8250_write32(base, UART_IER, !UART_IER_RDI); - dw8250_write32(base, UART_FCR, UART_FCR_EN_FIFO | UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); - - /* Disable flow ctrl */ - dw8250_write32(base, UART_MCR, 0); - /* Clear RTS */ - dw8250_write32(base, UART_MCR, dw8250_read32(base, UART_MCR) | UART_MCR_RTS); - - rate = UART_INPUT_CLK / 16 / serial->config.baud_rate; - - /* Enable access DLL & DLH */ - dw8250_write32(base, UART_LCR, dw8250_read32(base, UART_LCR) | UART_LCR_DLAB); - dw8250_write32(base, UART_DLL, (rate & 0xff)); - dw8250_write32(base, UART_DLM, (rate & 0xff00) >> 8); - /* Clear DLAB bit */ - dw8250_write32(base, UART_LCR, dw8250_read32(base, UART_LCR) & (~UART_LCR_DLAB)); - - dw8250_write32(base, UART_LCR, (dw8250_read32(base, UART_LCR) & (~UART_LCR_WLEN8)) | UART_LCR_WLEN8); - dw8250_write32(base, UART_LCR, dw8250_read32(base, UART_LCR) & (~UART_LCR_STOP)); - dw8250_write32(base, UART_LCR, dw8250_read32(base, UART_LCR) & (~UART_LCR_PARITY)); - - dw8250_write32(base, UART_IER, UART_IER_RDI); - - return RT_EOK; -} - -static rt_err_t dw8250_uart_control(struct rt_serial_device *serial, int cmd, void *arg) -{ - struct hw_uart_device *uart; - - RT_ASSERT(serial != RT_NULL); - uart = (struct hw_uart_device *)serial->parent.user_data; - - switch (cmd) - { - case RT_DEVICE_CTRL_CLR_INT: - /* Disable rx irq */ - dw8250_write32(uart->hw_base, UART_IER, !UART_IER_RDI); - rt_hw_interrupt_mask(uart->irqno); - break; - - case RT_DEVICE_CTRL_SET_INT: - /* Enable rx irq */ - dw8250_write32(uart->hw_base, UART_IER, UART_IER_RDI); - rt_hw_interrupt_umask(uart->irqno); - break; - } - - return RT_EOK; -} - -static int dw8250_uart_putc(struct rt_serial_device *serial, char c) -{ - rt_base_t base; - struct hw_uart_device *uart; - - RT_ASSERT(serial != RT_NULL); - uart = (struct hw_uart_device *)serial->parent.user_data; - base = uart->hw_base; - - while ((dw8250_read32(base, UART_USR) & 0x2) == 0) - { - } - - dw8250_write32(base, UART_TX, c); - - return 1; -} - -static int dw8250_uart_getc(struct rt_serial_device *serial) -{ - int ch = -1; - rt_base_t base; - struct hw_uart_device *uart; - - RT_ASSERT(serial != RT_NULL); - uart = (struct hw_uart_device *)serial->parent.user_data; - base = uart->hw_base; - - if ((dw8250_read32(base, UART_LSR) & 0x1)) - { - ch = dw8250_read32(base, UART_RX) & 0xff; - } - - return ch; -} - -static const struct rt_uart_ops _uart_ops = -{ - dw8250_uart_configure, - dw8250_uart_control, - dw8250_uart_putc, - dw8250_uart_getc, -}; - -static void rt_hw_uart_isr(int irqno, void *param) -{ - unsigned int iir, status; - struct rt_serial_device *serial = (struct rt_serial_device *)param; - struct hw_uart_device *uart = (struct hw_uart_device *)serial->parent.user_data; - - iir = dw8250_read32(uart->hw_base, UART_IIR); - - /* If don't do this in non-DMA mode then the "RX TIMEOUT" interrupt will fire forever. */ - if ((iir & 0x3f) == UART_IIR_RX_TIMEOUT) - { -#ifdef RT_USING_SMP - rt_base_t level = rt_spin_lock_irqsave(&uart->spinlock); -#endif - - status = dw8250_read32(uart->hw_base, UART_LSR); - - if (!(status & (UART_LSR_DR | UART_LSR_BI))) - { - dw8250_read32(uart->hw_base, UART_RX); - } - -#ifdef RT_USING_SMP - rt_spin_unlock_irqrestore(&uart->spinlock, level); -#endif - } - - if (!(iir & UART_IIR_NO_INT)) - { - rt_hw_serial_isr(serial, RT_SERIAL_EVENT_RX_IND); - } - - if ((iir & UART_IIR_BUSY) == UART_IIR_BUSY) - { - /* Clear the USR */ - dw8250_read32(uart->hw_base, UART_USR); - - return; - } -} - -int rt_hw_uart_init(void) -{ - struct hw_uart_device *uart; - struct serial_configure config = RT_SERIAL_CONFIG_DEFAULT; - - config.baud_rate = 1500000; - -#define BSP_INSTALL_UART_DEVICE(no) \ - uart = &_uart##no##_device; \ - _serial##no.ops = &_uart_ops; \ - _serial##no.config = config; \ - rt_hw_serial_register(&_serial##no, "uart" #no, RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX, uart); \ - rt_hw_interrupt_install(uart->irqno, rt_hw_uart_isr, &_serial##no, "uart" #no); - -#ifdef RT_USING_UART0 - BSP_INSTALL_UART_DEVICE(0); -#endif - -#ifdef RT_USING_UART1 - BSP_INSTALL_UART_DEVICE(1); -#endif - -#ifdef RT_USING_UART2 - BSP_INSTALL_UART_DEVICE(2); -#endif - -#ifdef RT_USING_UART3 - BSP_INSTALL_UART_DEVICE(3); -#endif - -#ifdef RT_USING_UART4 - BSP_INSTALL_UART_DEVICE(4); -#endif - -#ifdef RT_USING_UART5 - BSP_INSTALL_UART_DEVICE(5); -#endif - -#ifdef RT_USING_UART6 - BSP_INSTALL_UART_DEVICE(6); -#endif - -#ifdef RT_USING_UART7 - BSP_INSTALL_UART_DEVICE(7); -#endif - -#ifdef RT_USING_UART8 - BSP_INSTALL_UART_DEVICE(8); -#endif - -#ifdef RT_USING_UART9 - BSP_INSTALL_UART_DEVICE(9); -#endif - - return 0; -} diff --git a/bsp/rockchip/rk3568/driver/rk3568.h b/bsp/rockchip/rk3568/driver/rk3568.h deleted file mode 100644 index c170940ca2eb..000000000000 --- a/bsp/rockchip/rk3568/driver/rk3568.h +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Copyright (c) 2006-2022, RT-Thread Development Team - * - * SPDX-License-Identifier: Apache-2.0 - * - * Change Logs: - * Date Author Notes - * 2022-3-08 GuEe-GUI the first version - */ - -#ifndef __RK3568_H__ -#define __RK3568_H__ - -#include - -/* UART */ -#define UART_MMIO_BASE 0xfe650000 -#define UART0_MMIO_BASE 0xfdd50000 -#define UART1_MMIO_BASE (UART_MMIO_BASE + 0) -#define UART2_MMIO_BASE (UART_MMIO_BASE + 0x10000) -#define UART3_MMIO_BASE (UART_MMIO_BASE + 0x20000) -#define UART4_MMIO_BASE (UART_MMIO_BASE + 0x30000) -#define UART5_MMIO_BASE (UART_MMIO_BASE + 0x40000) -#define UART6_MMIO_BASE (UART_MMIO_BASE + 0x50000) -#define UART7_MMIO_BASE (UART_MMIO_BASE + 0x60000) -#define UART8_MMIO_BASE (UART_MMIO_BASE + 0x70000) -#define UART9_MMIO_BASE (UART_MMIO_BASE + 0x80000) - -#define UART_MMIO_SIZE 0x100 - -#define UART_IRQ_BASE (32 + 116) -#define UART0_IRQ (UART_IRQ_BASE + 0) -#define UART1_IRQ (UART_IRQ_BASE + 1) -#define UART2_IRQ (UART_IRQ_BASE + 2) -#define UART3_IRQ (UART_IRQ_BASE + 3) -#define UART4_IRQ (UART_IRQ_BASE + 4) -#define UART5_IRQ (UART_IRQ_BASE + 5) -#define UART6_IRQ (UART_IRQ_BASE + 6) -#define UART7_IRQ (UART_IRQ_BASE + 7) -#define UART8_IRQ (UART_IRQ_BASE + 8) -#define UART9_IRQ (UART_IRQ_BASE + 9) - -/* GPIO */ -#define GPIO0_MMIO_BASE 0xfdd60000 -#define GPIO1_MMIO_BASE 0xfe740000 -#define GPIO2_MMIO_BASE 0xfe750000 -#define GPIO3_MMIO_BASE 0xfe760000 -#define GPIO4_MMIO_BASE 0xfe770000 - -#define GPIO_MMIO_SIZE 0x100 - -#define GPIO_IRQ_BASE (32 + 33) -#define GPIO0_IRQ (GPIO_IRQ_BASE + 0) -#define GPIO1_IRQ (GPIO_IRQ_BASE + 1) -#define GPIO2_IRQ (GPIO_IRQ_BASE + 2) -#define GPIO3_IRQ (GPIO_IRQ_BASE + 3) -#define GPIO4_IRQ (GPIO_IRQ_BASE + 4) - -/* MMC */ -#define MMC0_MMIO_BASE 0xfe310000 /* sdhci */ -#define MMC1_MMIO_BASE 0xfe2b0000 /* sdmmc0 */ -#define MMC2_MMIO_BASE 0xfe2c0000 /* sdmmc1 */ -#define MMC3_MMIO_BASE 0xfe000000 /* sdmmc2 */ - -#define MMC0_MMIO_SIZE 0x10000 -#define MMC_MMIO_SIZE 0x4000 - -#define MMC0_IRQ (32 + 19) -#define MMC1_IRQ (32 + 98) -#define MMC2_IRQ (32 + 99) -#define MMC3_IRQ (32 + 100) - -/* Ethernet */ -#define GMAC0_MMIO_BASE 0xfe2a0000 -#define GMAC1_MMIO_BASE 0xfe010000 - -#define GMAC_MMIO_SIZE 0x10000 - -#define GMAC0_MAC_IRQ (32 + 27) -#define GMAC0_WAKE_IRQ (32 + 24) -#define GMAC1_MAC_IRQ (32 + 32) -#define GMAC1_WAKE_IRQ (32 + 29) - -/* GIC */ -#define MAX_HANDLERS 256 -#define GIC_IRQ_START 0 -#define ARM_GIC_NR_IRQS 256 -#define ARM_GIC_MAX_NR 1 - -#define IRQ_ARM_IPI_KICK 0 -#define IRQ_ARM_IPI_CALL 1 - -#define GIC_PL600_DISTRIBUTOR_PPTR 0xfd400000 -#define GIC_PL600_REDISTRIBUTOR_PPTR 0xfd460000 -#define GIC_PL600_CONTROLLER_PPTR RT_NULL -#define GIC_PL600_ITS_PPTR 0xfd440000 - -rt_inline rt_uint32_t platform_get_gic_dist_base(void) -{ - return GIC_PL600_DISTRIBUTOR_PPTR; -} - -rt_inline rt_uint32_t platform_get_gic_redist_base(void) -{ - return GIC_PL600_REDISTRIBUTOR_PPTR; -} - -rt_inline rt_uint32_t platform_get_gic_cpu_base(void) -{ - return GIC_PL600_CONTROLLER_PPTR; -} - -rt_inline rt_uint32_t platform_get_gic_its_base(void) -{ - return GIC_PL600_ITS_PPTR; -} - -#endif /* __RK3568_H__ */ diff --git a/bsp/rockchip/rk3568/link.lds b/bsp/rockchip/rk3568/link.lds deleted file mode 100644 index dd62ae35d071..000000000000 --- a/bsp/rockchip/rk3568/link.lds +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Copyright (c) 2006-2023, RT-Thread Development Team - * - * SPDX-License-Identifier: Apache-2.0 - * - * Change Logs: - * Date Author Notes - * 2017-5-30 bernard first version - */ - -OUTPUT_FORMAT("elf64-littleaarch64", "elf64-littleaarch64", "elf64-littleaarch64") -OUTPUT_ARCH(aarch64) - -SECTIONS -{ - . = 0x208000; - . = ALIGN(4096); - .text : - { - KEEP(*(.text.entrypoint)) /* The entry point */ - *(.vectors) - *(.text) /* remaining code */ - *(.text.*) /* remaining code */ - - *(.rodata) /* read-only data (constants) */ - *(.rodata*) - *(.glue_7) - *(.glue_7t) - *(.gnu.linkonce.t*) - - *(COMMON) - - /* section information for finsh shell */ - . = ALIGN(16); - __fsymtab_start = .; - KEEP(*(FSymTab)) - __fsymtab_end = .; - . = ALIGN(16); - __vsymtab_start = .; - KEEP(*(VSymTab)) - __vsymtab_end = .; - . = ALIGN(16); - - /* section information for initial. */ - . = ALIGN(16); - __rt_init_start = .; - KEEP(*(SORT(.rti_fn*))) - __rt_init_end = .; - . = ALIGN(16); - - . = ALIGN(16); - _etext = .; - } - - .eh_frame_hdr : - { - *(.eh_frame_hdr) - *(.eh_frame_entry) - } - .eh_frame : ONLY_IF_RO { KEEP (*(.eh_frame)) } - - . = ALIGN(16); - .data : - { - *(.data) - *(.data.*) - - *(.data1) - *(.data1.*) - - . = ALIGN(16); - _gp = ABSOLUTE(.); /* Base of small data */ - - *(.sdata) - *(.sdata.*) - } - - . = ALIGN(16); - .ctors : - { - PROVIDE(__ctors_start__ = .); - /* new GCC version uses .init_array */ - KEEP(*(SORT(.init_array.*))) - KEEP(*(.init_array)) - PROVIDE(__ctors_end__ = .); - } - - .dtors : - { - PROVIDE(__dtors_start__ = .); - KEEP(*(SORT(.dtors.*))) - KEEP(*(.dtors)) - PROVIDE(__dtors_end__ = .); - } - - . = ALIGN(16); - .bss : - { - PROVIDE(__bss_start = .); - *(.bss) - *(.bss.*) - *(.dynbss) - - . = ALIGN(16); - PROVIDE(__bss_end = .); - } - _end = .; - - /* Stabs debugging sections. */ - .stab 0 : { *(.stab) } - .stabstr 0 : { *(.stabstr) } - .stab.excl 0 : { *(.stab.excl) } - .stab.exclstr 0 : { *(.stab.exclstr) } - .stab.index 0 : { *(.stab.index) } - .stab.indexstr 0 : { *(.stab.indexstr) } - .comment 0 : { *(.comment) } - /* DWARF debug sections. - * Symbols in the DWARF debugging sections are relative to the beginning - * of the section so we begin them at 0. */ - /* DWARF 1 */ - .debug 0 : { *(.debug) } - .line 0 : { *(.line) } - /* GNU DWARF 1 extensions */ - .debug_srcinfo 0 : { *(.debug_srcinfo) } - .debug_sfnames 0 : { *(.debug_sfnames) } - /* DWARF 1.1 and DWARF 2 */ - .debug_aranges 0 : { *(.debug_aranges) } - .debug_pubnames 0 : { *(.debug_pubnames) } - /* DWARF 2 */ - .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) } - .debug_abbrev 0 : { *(.debug_abbrev) } - .debug_line 0 : { *(.debug_line) } - .debug_frame 0 : { *(.debug_frame) } - .debug_str 0 : { *(.debug_str) } - .debug_loc 0 : { *(.debug_loc) } - .debug_macinfo 0 : { *(.debug_macinfo) } - /* SGI/MIPS DWARF 2 extensions */ - .debug_weaknames 0 : { *(.debug_weaknames) } - .debug_funcnames 0 : { *(.debug_funcnames) } - .debug_typenames 0 : { *(.debug_typenames) } - .debug_varnames 0 : { *(.debug_varnames) } -} - -__bss_size = SIZEOF(.bss); diff --git a/bsp/rockchip/rk3568/rtconfig.h b/bsp/rockchip/rk3568/rtconfig.h index 93f112617855..6e1635f00b3c 100644 --- a/bsp/rockchip/rk3568/rtconfig.h +++ b/bsp/rockchip/rk3568/rtconfig.h @@ -6,7 +6,10 @@ /* RT-Thread Kernel */ -#define RT_NAME_MAX 8 +#define RT_NAME_MAX 24 +#define RT_USING_SMART +#define RT_USING_SMP +#define RT_CPUS_NR 4 #define RT_ALIGN_SIZE 8 #define RT_THREAD_PRIORITY_32 #define RT_THREAD_PRIORITY_MAX 32 @@ -17,6 +20,7 @@ #define RT_USING_IDLE_HOOK #define RT_IDLE_HOOK_LIST_SIZE 4 #define IDLE_THREAD_STACK_SIZE 4096 +#define SYSTEM_THREAD_STACK_SIZE 4096 #define RT_USING_TIMER_SOFT #define RT_TIMER_THREAD_PRIO 4 #define RT_TIMER_THREAD_STACK_SIZE 4096 @@ -50,17 +54,30 @@ #define RT_USING_DEVICE #define RT_USING_DEVICE_OPS +#define RT_USING_DM +#define RT_USING_DM_FDT +#define RT_USING_INTERRUPT_INFO #define RT_USING_CONSOLE -#define RT_CONSOLEBUF_SIZE 128 +#define RT_CONSOLEBUF_SIZE 256 #define RT_CONSOLE_DEVICE_NAME "uart2" -#define RT_VER_NUM 0x50000 +#define RT_VER_NUM 0x50001 + +/* RT-Thread Architecture */ + #define ARCH_CPU_64BIT #define RT_USING_CACHE #define RT_USING_HW_ATOMIC #define ARCH_MM_MMU #define ARCH_ARM #define ARCH_ARM_MMU +#define KERNEL_VADDR_START 0xffff000000000000 #define ARCH_ARMV8 +#define ARCH_ARMV8_EXTENSIONS 0 +#define ARCH_TEXT_OFFSET 0x200000 +#define ARCH_RAM_OFFSET 0x200000 +#define ARCH_ASPACE_SIZE 0xffffffff +#define ARCH_SECONDARY_CPU_STACK_SIZE 4096 +#define ARCH_HAVE_EFFICIENT_UNALIGNED_ACCESS /* RT-Thread Components */ @@ -73,7 +90,7 @@ #define FINSH_USING_MSH #define FINSH_THREAD_NAME "tshell" #define FINSH_THREAD_PRIORITY 20 -#define FINSH_THREAD_STACK_SIZE 4096 +#define FINSH_THREAD_STACK_SIZE 8192 #define FINSH_USING_HISTORY #define FINSH_HISTORY_LINES 5 #define FINSH_USING_SYMTAB @@ -82,14 +99,161 @@ #define FINSH_USING_DESCRIPTION #define FINSH_ARG_MAX 10 +/* DFS: device virtual file system */ + +#define RT_USING_DFS +#define DFS_USING_POSIX +#define DFS_USING_WORKDIR +#define DFS_FD_MAX 16 +#define RT_USING_DFS_V1 +#define DFS_FILESYSTEMS_MAX 4 +#define DFS_FILESYSTEM_TYPES_MAX 4 +#define RT_USING_DFS_ELMFAT + +/* elm-chan's FatFs, Generic FAT Filesystem Module */ + +#define RT_DFS_ELM_CODE_PAGE 437 +#define RT_DFS_ELM_WORD_ACCESS +#define RT_DFS_ELM_USE_LFN_3 +#define RT_DFS_ELM_USE_LFN 3 +#define RT_DFS_ELM_LFN_UNICODE_0 +#define RT_DFS_ELM_LFN_UNICODE 0 +#define RT_DFS_ELM_MAX_LFN 255 +#define RT_DFS_ELM_DRIVES 2 +#define RT_DFS_ELM_MAX_SECTOR_SIZE 512 +#define RT_DFS_ELM_REENTRANT +#define RT_DFS_ELM_MUTEX_TIMEOUT 3000 +#define RT_USING_DFS_DEVFS +#define RT_USING_LWP +#define RT_LWP_MAX_NR 30 +#define LWP_TASK_STACK_SIZE 16384 +#define RT_CH_MSG_MAX_NR 1024 +#define LWP_CONSOLE_INPUT_BUFFER_SIZE 1024 +#define LWP_TID_MAX_NR 64 +#define RT_LWP_SHM_MAX_NR 64 + /* Device Drivers */ +#define RT_USING_TTY +#define RT_USING_NULL +#define RT_USING_ZERO +#define RT_USING_RANDOM #define RT_USING_DEVICE_IPC #define RT_UNAMED_PIPE_NUMBER 64 +#define RT_USING_SYSTEM_WORKQUEUE +#define RT_SYSTEM_WORKQUEUE_STACKSIZE 8192 +#define RT_SYSTEM_WORKQUEUE_PRIORITY 23 +#define RT_USING_ADC +#define RT_ADC_ROCKCHIP_SARADC +#define RT_USING_AUDIO +#define RT_AUDIO_REPLAY_MP_BLOCK_SIZE 4096 +#define RT_AUDIO_REPLAY_MP_BLOCK_COUNT 2 +#define RT_AUDIO_RECORD_PIPE_SIZE 2048 +#define RT_AUDIO_ROCKCHIP +#define RT_AUDIO_ROCKCHIP_I2S +#define RT_AUDIO_ROCKCHIP_I2S_TDM +#define RT_AUDIO_ROCKCHIP_PDM +#define RT_AUDIO_ROCKCHIP_SPDIF +#define RT_USING_CAN +#define RT_CAN_USING_CANFD +#define RT_CAN_CANFD_ROCKCHIP +#define RT_USING_CLK +#define RT_CLK_SCMI +#define RT_CLK_ROCKCHIP_RK8XX_CLKOUT +#define RT_CLK_ROCKCHIP +#define RT_CLK_ROCKCHIP_RK3568 +#define RT_USING_FIRMWARE +#define RT_FIRMWARE_PSCI +#define RT_FIRMWARE_ARM_SCMI +#define RT_FIRMWARE_ARM_SCMI_TRANSPORT_SMC +#define RT_USING_HWCRYPTO +#define RT_HWCRYPTO_DEFAULT_NAME "hwcryto" +#define RT_HWCRYPTO_IV_MAX_SIZE 16 +#define RT_HWCRYPTO_KEYBIT_MAX_SIZE 256 +#define RT_HWCRYPTO_USING_RNG +#define RT_HWCRYPTO_RNG_ROCKCHIP +#define RT_USING_HWSPINLOCK +#define RT_HWSPINLOCK_ROCKCHIP +#define RT_USING_HWTIMER +#define RT_HWTIMER_ARM_ARCH +#define RT_HWTIMER_ROCKCHIP +#define RT_USING_I2C +#define RT_USING_I2C_BITOPS +#define RT_I2C_RK3X +#define RT_USING_INPUT +#define RT_INPUT_KEYBOARD +#define RT_INPUT_KEYBOARD_GPIO_KEYS +#define RT_INPUT_MISC +#define RT_INPUT_MISC_RK8XX_PWRKEY +#define RT_USING_LED +#define RT_LED_GPIO +#define RT_USING_MBOX +#define RT_MBOX_ROCKCHIP +#define RT_USING_MFD +#define RT_MFD_SYSCON +#define RT_MFD_RK8XX +#define RT_MFD_RK8XX_I2C +#define RT_USING_OFW +#define RT_FDT_EARLYCON_MSG_SIZE 128 +#define RT_USING_PCI +#define RT_PCI_MSI +#define RT_PCI_SYS_64BIT +#define RT_PCI_CACHE_LINE_SIZE 8 +#define RT_PCI_DW_HOST +#define RT_PCI_DW_HOST_ROCKCHIP +#define RT_USING_PHY +#define RT_PHY_ROCKCHIP_NANENG_COMBO +#define RT_PHY_ROCKCHIP_SNPS_PCIE3 +#define RT_USING_PIC +#define MAX_HANDLERS 512 +#define RT_PIC_ARM_GIC_V3 +#define RT_USING_PINCTRL +#define RT_PINCTRL_ROCKCHIP +#define RT_USING_PIN +#define RT_PIN_ROCKCHIP +#define RT_USING_POWER_RESET +#define RT_POWER_RESET_SYSCON_REBOOT_MODE +#define RT_POWER_RESET_REBOOT_MODE +#define RT_USING_PWM +#define RT_PWM_ROCKCHIP +#define RT_USING_REGULATOR +#define RT_REGULATOR_GPIO +#define RT_REGULATOR_RK8XX +#define RT_USING_RESET +#define RT_RESET_SCMI +#define RT_USING_RTC +#define RT_USING_ALARM +#define RT_RTC_HYM8563 +#define RT_RTC_RK8XX +#define RT_USING_SDIO +#define RT_SDIO_STACK_SIZE 8192 +#define RT_SDIO_THREAD_PRIORITY 15 +#define RT_MMCSD_STACK_SIZE 8192 +#define RT_MMCSD_THREAD_PREORITY 22 +#define RT_MMCSD_MAX_PARTITION 16 +#define RT_SDIO_SDHCI_DWCMSHC +#define RT_SDIO_DW_MMC +#define RT_SDIO_DW_MMC_ROCKCHIP +#define RT_USING_SENSOR +#define RT_USING_SENSOR_CMD +#define RT_SENSOR_ROCKCHIP_TSADC #define RT_USING_SERIAL #define RT_USING_SERIAL_V1 #define RT_SERIAL_RB_BUFSZ 64 -#define RT_USING_PIN +#define RT_SERIAL_8250 +#define RT_SERIAL_8250_DW +#define RT_USING_SOC +#define RT_SOC_ROCKCHIP +#define RT_SOC_ROCKCHIP_FIQ_DEBUGGER +#define RT_SOC_ROCKCHIP_GRF +#define RT_SOC_ROCKCHIP_IODOMAIN +#define RT_SOC_ROCKCHIP_PMDOMAIN +#define RT_USING_SPI +#define RT_USING_QSPI +#define RT_SPI_ROCKCHIP_SFC +#define RT_SPI_ROCKCHIP +#define RT_USING_WDT +#define RT_WDT_DW /* Using USB */ @@ -100,6 +264,13 @@ /* POSIX (Portable Operating System Interface) layer */ +#define RT_USING_POSIX_FS +#define RT_USING_POSIX_DEVIO +#define RT_USING_POSIX_STDIO +#define RT_USING_POSIX_TERMIOS +#define RT_USING_POSIX_DELAY +#define RT_USING_POSIX_CLOCK +#define RT_USING_POSIX_TIMER /* Interprocess Communication (IPC) */ @@ -113,6 +284,10 @@ /* Utilities */ #define RT_USING_ADT +#define RT_USING_ADT_AVL +#define RT_USING_ADT_BITMAP +#define RT_USING_ADT_HASHMAP +#define RT_USING_ADT_REF /* RT-Thread Utestcases */ @@ -159,6 +334,8 @@ /* enhanced kernel services */ +#define PKG_USING_RT_VSNPRINTF_FULL +#define PKG_USING_RT_VSNPRINTF_FULL_LATEST_VERSION /* acceleration: Assembly language or algorithmic acceleration packages */ @@ -232,11 +409,4 @@ #define SOC_RK3568 -/* Hardware Drivers Config */ - -#define BSP_USING_UART -#define RT_USING_UART2 -#define BSP_USING_GIC -#define BSP_USING_GICV3 - #endif diff --git a/bsp/rockchip/rk3568/rtconfig.py b/bsp/rockchip/rk3568/rtconfig.py index 78fb406389a7..b5fc06090597 100644 --- a/bsp/rockchip/rk3568/rtconfig.py +++ b/bsp/rockchip/rk3568/rtconfig.py @@ -16,8 +16,7 @@ PLATFORM = 'gcc' EXEC_PATH = r'/opt/gcc-arm-8.3-2019.03-x86_64-aarch64-elf/bin/' -if os.getenv('RTT_EXEC_PATH'): - EXEC_PATH = os.getenv('RTT_EXEC_PATH') +EXEC_PATH = os.getenv('RTT_EXEC_PATH') or '/usr/bin' BUILD = 'debug' @@ -26,6 +25,7 @@ PREFIX = os.getenv('RTT_CC_PREFIX') or 'aarch64-none-elf-' CC = PREFIX + 'gcc' CXX = PREFIX + 'g++' + CPP = PREFIX + 'cpp' AS = PREFIX + 'gcc' AR = PREFIX + 'ar' LINK = PREFIX + 'gcc' @@ -34,10 +34,11 @@ OBJDUMP = PREFIX + 'objdump' OBJCPY = PREFIX + 'objcopy' - DEVICE = ' -g -march=armv8-a -mtune=cortex-a53' + DEVICE = ' -g -march=armv8-a -mtune=cortex-a55 -fdiagnostics-color=always' + CPPFLAGS= ' -E -P -x assembler-with-cpp' CFLAGS = DEVICE + ' -Wall -Wno-cpp' AFLAGS = ' -c' + ' -x assembler-with-cpp -D__ASSEMBLY__' - LFLAGS = DEVICE + ' -nostartfiles -Wl,--gc-sections,-Map=rtthread.map,-cref,-u,system_vectors -T link.lds' + LFLAGS = DEVICE + ' -nostartfiles -Wl,--no-warn-rwx-segments -Wl,--gc-sections,-Map=rtthread.map,-cref,-u,system_vectors -T link.lds' CPATH = '' LPATH = '' diff --git a/components/dfs/dfs_v1/Kconfig b/components/dfs/dfs_v1/Kconfig index 0d003339f5d4..3ab2e350c05f 100644 --- a/components/dfs/dfs_v1/Kconfig +++ b/components/dfs/dfs_v1/Kconfig @@ -106,6 +106,12 @@ if RT_USING_DFS_V1 bool "Using devfs for device objects" default y + config RT_USING_DFS_DIRECTFS + bool "Enable filesystem for direct access rt_object" + select RT_USING_ADT_REF + depends on RT_USING_MEMHEAP + default n + config RT_USING_DFS_ROMFS bool "Enable ReadOnly file system on flash" default n diff --git a/components/dfs/dfs_v1/filesystems/directfs/SConscript b/components/dfs/dfs_v1/filesystems/directfs/SConscript new file mode 100644 index 000000000000..0a8493f6d125 --- /dev/null +++ b/components/dfs/dfs_v1/filesystems/directfs/SConscript @@ -0,0 +1,11 @@ +# RT-Thread building script for component + +from building import * + +cwd = GetCurrentDir() +src = Glob('*.c') +CPPPATH = [cwd] + +group = DefineGroup('Filesystem', src, depend = ['RT_USING_DFS', 'RT_USING_DFS_DIRECTFS'], CPPPATH = CPPPATH) + +Return('group') diff --git a/components/dfs/dfs_v1/filesystems/directfs/dfs_directfs.c b/components/dfs/dfs_v1/filesystems/directfs/dfs_directfs.c new file mode 100644 index 000000000000..2028fc4f631f --- /dev/null +++ b/components/dfs/dfs_v1/filesystems/directfs/dfs_directfs.c @@ -0,0 +1,352 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-03-01 GuEe-GUI first version + */ + +#include +#include +#include +#include +#include +#include + +#include "dfs_directfs.h" + +static struct rt_spinlock _directfs_lock = { 0 }; +static struct rt_object _directfs_root = +{ + .child_nodes = RT_LIST_OBJECT_INIT(_directfs_root.child_nodes), +}; +static struct directfs_bin_attribute _directfs_default_attr = +{ + .attr = + { + .name = RT_NULL, + }, +}; + +rt_inline void directfs_lock(void) +{ + rt_hw_spin_lock(&_directfs_lock.lock); +} + +rt_inline void directfs_unlock(void) +{ + rt_hw_spin_unlock(&_directfs_lock.lock); +} + +rt_object_t dfs_directfs_find_object(rt_object_t parent, const char *path) +{ + rt_object_t obj = RT_NULL, obj_tmp; + rt_size_t path_part_len; + + if (!path) + { + return obj; + } + + parent = parent ? : &_directfs_root; + path_part_len = rt_strchrnul(path, '/') - path; + + directfs_lock(); + + rt_list_for_each_entry(obj_tmp, &parent->child_nodes, node) + { + if (!rt_strncmp(obj_tmp->name, path, path_part_len)) + { + obj = obj_tmp; + break; + } + } + + directfs_unlock(); + + path += path_part_len; + + return obj ? (*path == '/' ? dfs_directfs_find_object(obj, path + 1) : obj) : RT_NULL; +} + +rt_err_t dfs_directfs_create_link(rt_object_t parent, rt_object_t object, const char *name) +{ + if (!object || !name) + { + return -RT_EINVAL; + } + + parent = parent ? : &_directfs_root; + + rt_strncpy(object->name, name, RT_NAME_MAX); + + object->parent = parent; + object->objectfs = &_directfs_default_attr; + + rt_list_init(&object->node); + rt_list_init(&object->child_nodes); + + directfs_lock(); + + rt_list_insert_before(&parent->child_nodes, &object->node); + + directfs_unlock(); + + return RT_EOK; +} + +rt_err_t dfs_directfs_create_bin_file(rt_object_t object, struct directfs_bin_attribute *attr) +{ + if (!object || !attr) + { + return -RT_EINVAL; + } + + object->objectfs = attr; + + directfs_lock(); + + rt_list_remove(&object->node); + rt_list_insert_after(&object->parent->child_nodes, &object->node); + + directfs_unlock(); + + return RT_EOK; +} + +static int dfs_directfs_open(struct dfs_file *file) +{ + rt_object_t obj = RT_NULL; + + if (rt_strcmp(file->vnode->path, "/")) + { + struct directfs_bin_attribute *battr; + obj = dfs_directfs_find_object(RT_NULL, file->vnode->path + 1); + + if (!obj) + { + return -EIO; + } + + battr = obj->objectfs; + + if ((file->flags & O_DIRECTORY) && battr->attr.name) + { + return -ENOENT; + } + + file->vnode->size = battr->attr.name ? 0 : rt_list_len(&obj->child_nodes); + } + else + { + file->vnode->size = rt_list_len(&_directfs_root.child_nodes); + } + + file->vnode->data = obj; + file->pos = 0; + + return RT_EOK; +} + +static int dfs_directfs_close(struct dfs_file *file) +{ + int ret = -EIO; + rt_object_t obj = file->vnode->data; + + file->vnode->data = RT_NULL; + + if (obj) + { + ret = 0; + } + + return ret; +} + +static int dfs_directfs_read(struct dfs_file *file, void *buf, size_t count) +{ + int ret = 0; + rt_object_t obj = file->vnode->data; + struct directfs_bin_attribute *battr = obj->objectfs; + + if (battr->read && count > 0) + { + ret = battr->read(obj, battr, buf, file->pos, count); + + if (ret > 0) + { + /* update file current position */ + file->pos += ret; + } + } + + return ret; +} + +static int dfs_directfs_write(struct dfs_file *file, const void *buf, size_t count) +{ + int ret = 0; + rt_object_t obj = file->vnode->data; + struct directfs_bin_attribute *battr = obj->objectfs; + + if (battr->write && count > 0) + { + ret = battr->write(obj, battr, (void *)buf, file->pos, count); + + if (ret > 0) + { + /* update file current position */ + file->pos += ret; + } + } + + return ret; +} + +static int dfs_directfs_lseek(struct dfs_file *file, off_t offset) +{ + int ret = -EIO; + + if (offset <= file->vnode->size) + { + file->pos = offset; + ret = file->pos; + } + + return ret; +} + +static int dfs_directfs_getdents(struct dfs_file *file, struct dirent *dirp, uint32_t count) +{ + int ret = -EIO; + rt_list_t *root = &_directfs_root.child_nodes; + rt_object_t obj = file->vnode->data, child; + + /* make integer count */ + count = (count / sizeof(struct dirent)); + + if (obj) + { + root = &obj->child_nodes; + } + + if (count) + { + struct dirent *d; + rt_size_t index = 0, end = file->pos + count, count = 0; + + rt_spin_lock(&_directfs_lock); + + rt_list_for_each_entry(child, root, node) + { + if (index >= (rt_size_t)file->pos) + { + struct directfs_bin_attribute *battr = child->objectfs; + + d = dirp + count; + + /* fill dirent */ + if (!battr->attr.name) + { + d->d_type = DT_DIR; + } + else + { + d->d_type = DT_REG; + } + + d->d_namlen = RT_NAME_MAX; + d->d_reclen = (rt_uint16_t)sizeof(struct dirent); + rt_strncpy(d->d_name, child->name, RT_NAME_MAX); + + ++count; + + /* move to next position */ + ++file->pos; + } + + ++index; + + if (index >= end) + { + break; + } + } + + rt_spin_unlock(&_directfs_lock); + + ret = count * sizeof(struct dirent); + } + else + { + ret = -EINVAL; + } + + return ret; +} + +static const struct dfs_file_ops _sys_fops = +{ + .open = dfs_directfs_open, + .close = dfs_directfs_close, + .read = dfs_directfs_read, + .write = dfs_directfs_write, + .lseek = dfs_directfs_lseek, + .getdents = dfs_directfs_getdents, +}; + +static int dfs_directfs_mount(struct dfs_filesystem *fs, unsigned long rwflag, const void *data) +{ + return RT_EOK; +} + +static int dfs_directfs_unmount(struct dfs_filesystem *fs) +{ + return RT_EOK; +} + +static int dfs_directfs_stat(struct dfs_filesystem *fs, const char *path, struct stat *st) +{ + rt_size_t size; + rt_object_t obj = dfs_directfs_find_object(RT_NULL, path + 1); + struct directfs_bin_attribute *battr = obj->objectfs; + + st->st_dev = 0; + st->st_mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH | + S_IWUSR | S_IWGRP | S_IWOTH; + + if (!battr->attr.name) + { + st->st_mode &= ~S_IFREG; + st->st_mode |= S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH; + } + else + { + size = rt_list_len(&obj->child_nodes); + } + + st->st_size = size; + st->st_mtime = 0; + + return RT_EOK; +} + +static const struct dfs_filesystem_ops _directfs = +{ + .name = "direct", + .flags = DFS_FS_FLAG_DEFAULT, + .fops = &_sys_fops, + + .mount = dfs_directfs_mount, + .unmount = dfs_directfs_unmount, + .stat = dfs_directfs_stat, +}; + +int dfs_directfs_init(void) +{ + /* register direct file system */ + return dfs_register(&_directfs); +} +INIT_COMPONENT_EXPORT(dfs_directfs_init); diff --git a/components/dfs/dfs_v1/filesystems/directfs/dfs_directfs.h b/components/dfs/dfs_v1/filesystems/directfs/dfs_directfs.h new file mode 100644 index 000000000000..d8fd8a93877b --- /dev/null +++ b/components/dfs/dfs_v1/filesystems/directfs/dfs_directfs.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-03-01 GuEe-GUI first version + */ + +#ifndef __DFS_DIRECTFS_H__ +#define __DFS_DIRECTFS_H__ + +#include + +struct rt_varea; + +struct directfs_attribute +{ + const char *name; + rt_uint32_t mode; +}; + +struct directfs_bin_attribute +{ + struct directfs_attribute attr; + rt_size_t size; + + rt_ssize_t (*read)(rt_object_t, struct directfs_bin_attribute *, char *, rt_off_t, rt_size_t); + rt_ssize_t (*write)(rt_object_t, struct directfs_bin_attribute *, char *, rt_off_t, rt_size_t); + rt_err_t (*mmap)(rt_object_t, struct directfs_bin_attribute *attr, struct rt_varea *varea); +}; + +rt_object_t dfs_directfs_find_object(rt_object_t parent, const char *path); +rt_err_t dfs_directfs_create_link(rt_object_t parent, rt_object_t object, const char *name); +rt_err_t dfs_directfs_create_bin_file(rt_object_t object, struct directfs_bin_attribute *attr); + +int dfs_directfs_init(void); + +#endif /* __DFS_SYSFS_H__ */ diff --git a/components/dfs/dfs_v1/src/dfs_file.c b/components/dfs/dfs_v1/src/dfs_file.c index bc9ac3ccdc78..9fa5cf48e188 100644 --- a/components/dfs/dfs_v1/src/dfs_file.c +++ b/components/dfs/dfs_v1/src/dfs_file.c @@ -794,15 +794,16 @@ void ls(const char *pathname) if (dfs_file_stat(fullpath, &stat) == 0) { - rt_kprintf("%-20s", dirent.d_name); if (S_ISDIR(stat.st_mode)) { - rt_kprintf("%-25s\n", ""); + rt_kprintf("%*.s ", 11, ""); } else { - rt_kprintf("%-25lu\n", (unsigned long)stat.st_size); + rt_kprintf("%11d ", (unsigned long)stat.st_size); } + rt_kputs(dirent.d_name); + rt_kputs("\n"); } else rt_kprintf("BAD file: %s\n", dirent.d_name); diff --git a/components/dfs/dfs_v2/src/dfs_file.c b/components/dfs/dfs_v2/src/dfs_file.c index fd97976cf113..761f25833e41 100644 --- a/components/dfs/dfs_v2/src/dfs_file.c +++ b/components/dfs/dfs_v2/src/dfs_file.c @@ -794,15 +794,16 @@ void ls(const char *pathname) if (dfs_file_stat(fullpath, &stat) == 0) { - rt_kprintf("%-20s", dirent.d_name); if (S_ISDIR(stat.st_mode)) { - rt_kprintf("%-25s\n", ""); + rt_kprintf("%*.s ", 11, ""); } else { - rt_kprintf("%-25lu\n", (unsigned long)stat.st_size); + rt_kprintf("%11d ", (unsigned long)stat.st_size); } + rt_kputs(dirent.d_name); + rt_kputs("\n"); } else rt_kprintf("BAD file: %s\n", dirent.d_name); diff --git a/components/drivers/Kconfig b/components/drivers/Kconfig index c6d327a6cfa7..f6349a98b163 100755 --- a/components/drivers/Kconfig +++ b/components/drivers/Kconfig @@ -35,19 +35,6 @@ if RT_USING_TTY default n endif -config RT_USING_CAN - bool "Using CAN device drivers" - default n - -if RT_USING_CAN - config RT_CAN_USING_HDR - bool "Enable CAN hardware filter" - default n - config RT_CAN_USING_CANFD - bool "Enable CANFD support" - default n -endif - config RT_USING_CPUTIME bool "Enable CPU time for high resolution clock counter" default n @@ -83,34 +70,6 @@ if RT_USING_CPUTIME default 0 endif -config RT_USING_I2C - bool "Using I2C device drivers" - default n - -if RT_USING_I2C - config RT_I2C_DEBUG - bool "Use I2C debug message" - default n - - config RT_USING_I2C_BITOPS - bool "Use GPIO to simulate I2C" - default y - - if RT_USING_I2C_BITOPS - config RT_I2C_BITOPS_DEBUG - bool "Use simulate I2C debug message" - default n - endif -endif - -config RT_USING_PHY - bool "Using ethernet phy device drivers" - default n - -config RT_USING_ADC - bool "Using ADC device drivers" - default n - config RT_USING_DAC bool "Using DAC device drivers" default n @@ -127,127 +86,44 @@ config RT_USING_RANDOM bool "Using RANDOM device drivers" default n -config RT_USING_PWM - bool "Using PWM device drivers" - default n - -config RT_USING_SDIO - bool "Using SD/MMC device drivers" +config RT_USING_PM + bool "Using Power Management device drivers" default n - if RT_USING_SDIO - config RT_SDIO_STACK_SIZE - int "The stack size for sdio irq thread" - default 512 - - config RT_SDIO_THREAD_PRIORITY - int "The priority level value of sdio irq thread" - default 15 - - config RT_MMCSD_STACK_SIZE - int "The stack size for mmcsd thread" - default 1024 - - config RT_MMCSD_THREAD_PREORITY - int "The priority level value of mmcsd thread" - default 22 - - config RT_MMCSD_MAX_PARTITION - int "mmcsd max partition" - default 16 - config RT_SDIO_DEBUG - bool "Enable SDIO debug log output" - default n - endif - -config RT_USING_SPI - bool "Using SPI Bus/Device device drivers" - default n - - if RT_USING_SPI - config RT_USING_SPI_BITOPS - select RT_USING_PIN - bool "Use GPIO to simulate SPI" - default n - - if RT_USING_SPI_BITOPS - config RT_SPI_BITOPS_DEBUG - bool "Use simulate SPI debug message" - default n - endif - - config RT_USING_QSPI - bool "Enable QSPI mode" - default n + if RT_USING_PM + config PM_TICKLESS_THRESHOLD_TIME + int "PM tickless threashold time" + default 2 - config RT_USING_SPI_MSD - bool "Using SD/TF card driver with spi" - select RT_USING_DFS + config PM_USING_CUSTOM_CONFIG + bool "PM using custom pm config" default n - config RT_USING_SFUD - bool "Using Serial Flash Universal Driver" + config PM_ENABLE_DEBUG + bool "PM Enable Debug" default n - help - An using JEDEC's SFDP standard serial (SPI) flash universal driver library - if RT_USING_SFUD - config RT_SFUD_USING_SFDP - bool "Using auto probe flash JEDEC SFDP parameter" - default y - - config RT_SFUD_USING_FLASH_INFO_TABLE - bool "Using defined supported flash chip information table" - default y - - config RT_SFUD_USING_QSPI - bool "Using QSPI mode support" - select RT_USING_QSPI - default n - - config RT_SFUD_SPI_MAX_HZ - int "Default spi maximum speed(HZ)" - range 0 50000000 - default 50000000 - help - Read the JEDEC SFDP command must run at 50 MHz or less,and you also can use rt_spi_configure(); to config spi speed. - - config RT_DEBUG_SFUD - bool "Show more SFUD debug information" - default n - endif - - config RT_USING_ENC28J60 - bool "Using ENC28J60 SPI Ethernet network interface" - select RT_USING_LWIP + config PM_ENABLE_SUSPEND_SLEEP_MODE + bool "PM Device suspend change sleep mode" default n - config RT_USING_SPI_WIFI - bool "Using RW009/007 SPI Wi-Fi wireless interface" - select RT_USING_LWIP + config PM_ENABLE_THRESHOLD_SLEEP_MODE + bool "PM using threshold time change sleep mode" default n - endif - -config RT_USING_WDT - bool "Using Watch Dog device drivers" - default n -config RT_USING_AUDIO - bool "Using Audio device drivers" - default n - - if RT_USING_AUDIO - config RT_AUDIO_REPLAY_MP_BLOCK_SIZE - int "Replay memory pool block size" - default 4096 + if PM_ENABLE_THRESHOLD_SLEEP_MODE + config PM_LIGHT_THRESHOLD_TIME + int "PM light mode threashold time" + default 5 - config RT_AUDIO_REPLAY_MP_BLOCK_COUNT - int "Replay memory pool block count" - default 2 + config PM_DEEP_THRESHOLD_TIME + int "PM deep mode threashold time" + default 20 - config RT_AUDIO_RECORD_PIPE_SIZE - int "Record pipe size" - default 2048 + config PM_STANDBY_THRESHOLD_TIME + int "PM standby mode threashold time" + default 100 + endif endif config RT_USING_SENSOR @@ -275,172 +151,6 @@ config RT_USING_LCD bool "Using LCD graphic drivers" default n -menuconfig RT_USING_HWCRYPTO - bool "Using Hardware Crypto drivers" - default n - - if RT_USING_HWCRYPTO - config RT_HWCRYPTO_DEFAULT_NAME - string "Hardware crypto device name" - default "hwcryto" - - config RT_HWCRYPTO_IV_MAX_SIZE - int "IV max size" - default "16" - - config RT_HWCRYPTO_KEYBIT_MAX_SIZE - int "Key max bit length" - default 256 - - config RT_HWCRYPTO_USING_GCM - bool "Using Hardware GCM" - default n - - config RT_HWCRYPTO_USING_AES - bool "Using Hardware AES" - default n - - if RT_HWCRYPTO_USING_AES - config RT_HWCRYPTO_USING_AES_ECB - bool "Using Hardware AES ECB mode" - default y - - config RT_HWCRYPTO_USING_AES_CBC - bool "Using Hardware AES CBC mode" - default n - - config RT_HWCRYPTO_USING_AES_CFB - bool "Using Hardware AES CFB mode" - default n - - config RT_HWCRYPTO_USING_AES_CTR - bool "Using Hardware AES CTR mode" - default n - - config RT_HWCRYPTO_USING_AES_OFB - bool "Using Hardware AES OFB mode" - default n - endif - - config RT_HWCRYPTO_USING_DES - bool "Using Hardware DES" - default n - - if RT_HWCRYPTO_USING_DES - config RT_HWCRYPTO_USING_DES_ECB - bool "Using Hardware DES ECB mode" - default y - - config RT_HWCRYPTO_USING_DES_CBC - bool "Using Hardware DES CBC mode" - default n - endif - - config RT_HWCRYPTO_USING_3DES - bool "Using Hardware 3DES" - default n - - if RT_HWCRYPTO_USING_3DES - config RT_HWCRYPTO_USING_3DES_ECB - bool "Using Hardware 3DES ECB mode" - default y - - config RT_HWCRYPTO_USING_3DES_CBC - bool "Using Hardware 3DES CBC mode" - default n - endif - - config RT_HWCRYPTO_USING_RC4 - bool "Using Hardware RC4" - default n - - config RT_HWCRYPTO_USING_MD5 - bool "Using Hardware MD5" - default n - - config RT_HWCRYPTO_USING_SHA1 - bool "Using Hardware SHA1" - default n - - config RT_HWCRYPTO_USING_SHA2 - bool "Using Hardware SHA2" - default n - - if RT_HWCRYPTO_USING_SHA2 - config RT_HWCRYPTO_USING_SHA2_224 - bool "Using Hardware SHA2_224 mode" - default n - - config RT_HWCRYPTO_USING_SHA2_256 - bool "Using Hardware SHA2_256 mode" - default y - - config RT_HWCRYPTO_USING_SHA2_384 - bool "Using Hardware SHA2_384 mode" - default n - - config RT_HWCRYPTO_USING_SHA2_512 - bool "Using Hardware SHA2_512 mode" - default n - endif - - config RT_HWCRYPTO_USING_RNG - bool "Using Hardware RNG" - default n - - config RT_HWCRYPTO_USING_CRC - bool "Using Hardware CRC" - default n - - if RT_HWCRYPTO_USING_CRC - config RT_HWCRYPTO_USING_CRC_07 - bool "Using Hardware CRC-8 0x07 polynomial" - default n - - config RT_HWCRYPTO_USING_CRC_8005 - bool "Using Hardware CRC-16 0x8005 polynomial" - default n - - config RT_HWCRYPTO_USING_CRC_1021 - bool "Using Hardware CRC-16 0x1021 polynomial" - default n - - config RT_HWCRYPTO_USING_CRC_3D65 - bool "Using Hardware CRC-16 0x3D65 polynomial" - default n - - config RT_HWCRYPTO_USING_CRC_04C11DB7 - bool "Using Hardware CRC-32 0x04C11DB7 polynomial" - default n - endif - - config RT_HWCRYPTO_USING_BIGNUM - bool "Using Hardware bignum" - default n - - if RT_HWCRYPTO_USING_BIGNUM - config RT_HWCRYPTO_USING_BIGNUM_EXPTMOD - bool "Using Hardware bignum expt_mod operation" - default y - - config RT_HWCRYPTO_USING_BIGNUM_MULMOD - bool "Using Hardware bignum mul_mod operation" - default y - - config RT_HWCRYPTO_USING_BIGNUM_MUL - bool "Using Hardware bignum mul operation" - default n - - config RT_HWCRYPTO_USING_BIGNUM_ADD - bool "Using Hardware bignum add operation" - default n - - config RT_HWCRYPTO_USING_BIGNUM_SUB - bool "Using Hardware bignum sub operation" - default n - endif - endif - config RT_USING_PULSE_ENCODER bool "Using PULSE ENCODER device drivers" default n @@ -611,19 +321,41 @@ menuconfig RT_USING_WIFI endif endif +source "$RTT_DIR/components/drivers/adc/Kconfig" +source "$RTT_DIR/components/drivers/audio/Kconfig" +source "$RTT_DIR/components/drivers/block/Kconfig" +source "$RTT_DIR/components/drivers/can/Kconfig" source "$RTT_DIR/components/drivers/clk/Kconfig" source "$RTT_DIR/components/drivers/firmware/Kconfig" +source "$RTT_DIR/components/drivers/hwcrypto/Kconfig" +source "$RTT_DIR/components/drivers/hwspinlock/Kconfig" source "$RTT_DIR/components/drivers/hwtimer/Kconfig" +source "$RTT_DIR/components/drivers/i2c/Kconfig" +source "$RTT_DIR/components/drivers/input/Kconfig" +source "$RTT_DIR/components/drivers/led/Kconfig" +source "$RTT_DIR/components/drivers/mailbox/Kconfig" source "$RTT_DIR/components/drivers/mfd/Kconfig" source "$RTT_DIR/components/drivers/mtd/Kconfig" +source "$RTT_DIR/components/drivers/nvmem/Kconfig" source "$RTT_DIR/components/drivers/ofw/Kconfig" source "$RTT_DIR/components/drivers/pci/Kconfig" +source "$RTT_DIR/components/drivers/phy/Kconfig" source "$RTT_DIR/components/drivers/pic/Kconfig" +source "$RTT_DIR/components/drivers/pinctrl/Kconfig" source "$RTT_DIR/components/drivers/pin/Kconfig" -source "$RTT_DIR/components/drivers/pm/Kconfig" +source "$RTT_DIR/components/drivers/pmdomain/Kconfig" +source "$RTT_DIR/components/drivers/power/Kconfig" +source "$RTT_DIR/components/drivers/pwm/Kconfig" +source "$RTT_DIR/components/drivers/regulator/Kconfig" +source "$RTT_DIR/components/drivers/reset/Kconfig" source "$RTT_DIR/components/drivers/rtc/Kconfig" +source "$RTT_DIR/components/drivers/sdio/Kconfig" source "$RTT_DIR/components/drivers/serial/Kconfig" +source "$RTT_DIR/components/drivers/soc/Kconfig" +source "$RTT_DIR/components/drivers/spi/Kconfig" +source "$RTT_DIR/components/drivers/thermal/Kconfig" source "$RTT_DIR/components/drivers/virtio/Kconfig" +source "$RTT_DIR/components/drivers/watchdog/Kconfig" menu "Using USB" config RT_USING_USB diff --git a/components/drivers/adc/Kconfig b/components/drivers/adc/Kconfig new file mode 100755 index 000000000000..95248eca8e97 --- /dev/null +++ b/components/drivers/adc/Kconfig @@ -0,0 +1,18 @@ +menuconfig RT_USING_ADC + bool "Using ADC device drivers" + default n + +config RT_ADC_ROCKCHIP_SARADC + bool "Rockchip SARADC driver" + depends on RT_USING_DM + depends on RT_USING_ADC + select RT_USING_RESET + select RT_USING_REGULATOR + default n + +config RT_ADC_RP1 + bool "RP1 ADC and temperature sensor driver" + depends on RT_USING_DM + depends on RT_USING_ADC + select RT_USING_REGULATOR + default n diff --git a/components/drivers/adc/SConscript b/components/drivers/adc/SConscript new file mode 100755 index 000000000000..19d7c5795ce7 --- /dev/null +++ b/components/drivers/adc/SConscript @@ -0,0 +1,21 @@ +from building import * + +group = [] + +if not GetDepend(['RT_USING_ADC']): + Return('group') + +cwd = GetCurrentDir() +CPPPATH = [cwd + '/../include'] + +src = ['adc.c'] + +if GetDepend(['RT_ADC_ROCKCHIP_SARADC']): + src += ['adc-rockchip_saradc.c'] + +if GetDepend(['RT_ADC_RP1']): + src += ['adc-rp1.c'] + +group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') diff --git a/components/drivers/adc/adc-rockchip_saradc.c b/components/drivers/adc/adc-rockchip_saradc.c new file mode 100644 index 000000000000..c7b01dd76026 --- /dev/null +++ b/components/drivers/adc/adc-rockchip_saradc.c @@ -0,0 +1,521 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-3-08 GuEe-GUI the first version + */ + +#include +#include +#include + +#define SARADC_DATA 0x00 + +#define SARADC_STAS 0x04 +#define SARADC_STAS_BUSY RT_BIT(0) + +#define SARADC_CTRL 0x08 +#define SARADC_CTRL_IRQ_STATUS RT_BIT(6) +#define SARADC_CTRL_IRQ_ENABLE RT_BIT(5) +#define SARADC_CTRL_POWER_CTRL RT_BIT(3) +#define SARADC_CTRL_CHN_MASK 0x7 + +#define SARADC_DLY_PU_SOC 0x0c +#define SARADC_DLY_PU_SOC_MASK 0x3f + +#define SARADC_TIMEOUT rt_tick_from_millisecond(100) +#define SARADC_MAX_CHANNELS 8 + +/* v2 registers */ +#define SARADC2_CONV_CON 0x000 +#define SARADC_T_PD_SOC 0x004 +#define SARADC_T_DAS_SOC 0x00c +#define SARADC2_END_INT_EN 0x104 +#define SARADC2_ST_CON 0x108 +#define SARADC2_STATUS 0x10c +#define SARADC2_END_INT_ST 0x110 +#define SARADC2_DATA_BASE 0x120 + +#define SARADC2_EN_END_INT RT_BIT(0) +#define SARADC2_START RT_BIT(4) +#define SARADC2_SINGLE_MODE RT_BIT(5) + +#define SARADC2_CONV_CHANNELS RT_GENMASK(15, 0) + +struct saradc_channel +{ + int channel; + int scan_index; + + struct + { + char sign; + rt_uint8_t realbits; + rt_uint8_t storagebits; + rt_uint8_t repeat; + } scan_type; + + rt_base_t info_mask_separate; + rt_base_t info_mask_shared_by_type; + + const char *datasheet_name; +}; + +struct rockchip_saradc; + +struct rockchip_saradc_soc_data +{ + const struct saradc_channel *channels; + int num_channels; + + rt_ubase_t clk_rate; + void (*start)(struct rockchip_saradc *rk_saradc, int chn); + int (*read)(struct rockchip_saradc *rk_saradc); + void (*power_down)(struct rockchip_saradc *rk_saradc); +}; + +struct rockchip_saradc +{ + struct rt_adc_device parent; + + int irq; + void *regs; + struct rt_clk *clk; + struct rt_clk *pclk; + struct rt_regulator *vref; + struct rt_reset_control *rstc; + + const struct rockchip_saradc_soc_data *soc_data; + + rt_uint16_t last_val; + struct saradc_channel *last_chan; + + struct rt_mutex lock; + struct rt_completion completion; +}; + +#define raw_to_rockchip_saradc(raw) rt_container_of(raw, struct rockchip_saradc, parent) + +#define SARADC_CHANNEL(INDEX, ID, RES) \ +{ \ + .channel = INDEX, \ + .info_mask_separate = RT_BIT(0), \ + .info_mask_shared_by_type = RT_BIT(2), \ + .datasheet_name = ID, \ + .scan_index = INDEX, \ + .scan_type = \ + { \ + .sign = 'u', \ + .realbits = RES, \ + .storagebits = 16, \ + }, \ +} + +static void rockchip_saradc_start_v1(struct rockchip_saradc *rk_saradc, int chn) +{ + /* 8 clock periods as delay between power up and start cmd */ + HWREG32(rk_saradc->regs + SARADC_DLY_PU_SOC) = 8; + /* Select the channel to be used and trigger conversion */ + HWREG32(rk_saradc->regs + SARADC_CTRL) = SARADC_CTRL_POWER_CTRL | + (chn & SARADC_CTRL_CHN_MASK) | SARADC_CTRL_IRQ_ENABLE; +} + +static void rockchip_saradc_reset_controller(struct rockchip_saradc *rk_saradc); + +static void rockchip_saradc_start_v2(struct rockchip_saradc *rk_saradc, int chn) +{ + int val; + + if (rk_saradc->rstc) + { + rockchip_saradc_reset_controller(rk_saradc); + } + + HWREG32(rk_saradc->regs + SARADC_T_DAS_SOC) = 0xc; + HWREG32(rk_saradc->regs + SARADC_T_PD_SOC) = 0x20; + + val = RT_FIELD_PREP(SARADC2_EN_END_INT, 1); + val |= val << 16; + HWREG32(rk_saradc->regs + SARADC2_END_INT_EN) = val; + + val = RT_FIELD_PREP(SARADC2_START, 1) | + RT_FIELD_PREP(SARADC2_SINGLE_MODE, 1) | + RT_FIELD_PREP(SARADC2_CONV_CHANNELS, chn); + val |= val << 16; + HWREG32(rk_saradc->regs + SARADC2_CONV_CON) = val; +} + +static int rockchip_saradc_read_v1(struct rockchip_saradc *rk_saradc) +{ + return HWREG32(rk_saradc->regs + SARADC_DATA); +} + +static int rockchip_saradc_read_v2(struct rockchip_saradc *rk_saradc) +{ + int offset; + + /* Clear irq */ + HWREG32(rk_saradc->regs + SARADC2_END_INT_ST) = 0x1; + + offset = SARADC2_DATA_BASE + rk_saradc->last_chan->channel * 0x4; + + return HWREG32(rk_saradc->regs + offset); +} + +static void rockchip_saradc_power_down_v1(struct rockchip_saradc *rk_saradc) +{ + HWREG32(rk_saradc->regs + SARADC_CTRL) = 0; +} + +static const struct saradc_channel rockchip_saradc_channels[] = +{ + SARADC_CHANNEL(0, "adc0", 10), + SARADC_CHANNEL(1, "adc1", 10), + SARADC_CHANNEL(2, "adc2", 10), +}; + +static const struct rockchip_saradc_soc_data saradc_data = +{ + .channels = rockchip_saradc_channels, + .num_channels = RT_ARRAY_SIZE(rockchip_saradc_channels), + .clk_rate = 1000000, + .start = rockchip_saradc_start_v1, + .read = rockchip_saradc_read_v1, + .power_down = rockchip_saradc_power_down_v1, +}; + +static const struct saradc_channel rk3066_tsadc_channels[] = +{ + SARADC_CHANNEL(0, "adc0", 12), + SARADC_CHANNEL(1, "adc1", 12), +}; + +static const struct rockchip_saradc_soc_data rk3066_tsadc_data = +{ + .channels = rk3066_tsadc_channels, + .num_channels = RT_ARRAY_SIZE(rk3066_tsadc_channels), + .clk_rate = 50000, + .start = rockchip_saradc_start_v1, + .read = rockchip_saradc_read_v1, + .power_down = rockchip_saradc_power_down_v1, +}; + +static const struct saradc_channel rk3399_saradc_channels[] = +{ + SARADC_CHANNEL(0, "adc0", 10), + SARADC_CHANNEL(1, "adc1", 10), + SARADC_CHANNEL(2, "adc2", 10), + SARADC_CHANNEL(3, "adc3", 10), + SARADC_CHANNEL(4, "adc4", 10), + SARADC_CHANNEL(5, "adc5", 10), +}; + +static const struct rockchip_saradc_soc_data rk3399_saradc_data = +{ + .channels = rk3399_saradc_channels, + .num_channels = RT_ARRAY_SIZE(rk3399_saradc_channels), + .clk_rate = 1000000, + .start = rockchip_saradc_start_v1, + .read = rockchip_saradc_read_v1, + .power_down = rockchip_saradc_power_down_v1, +}; + +static const struct saradc_channel rk3568_saradc_channels[] = +{ + SARADC_CHANNEL(0, "adc0", 10), + SARADC_CHANNEL(1, "adc1", 10), + SARADC_CHANNEL(2, "adc2", 10), + SARADC_CHANNEL(3, "adc3", 10), + SARADC_CHANNEL(4, "adc4", 10), + SARADC_CHANNEL(5, "adc5", 10), + SARADC_CHANNEL(6, "adc6", 10), + SARADC_CHANNEL(7, "adc7", 10), +}; + +static const struct rockchip_saradc_soc_data rk3568_saradc_data = +{ + .channels = rk3568_saradc_channels, + .num_channels = RT_ARRAY_SIZE(rk3568_saradc_channels), + .clk_rate = 1000000, + .start = rockchip_saradc_start_v1, + .read = rockchip_saradc_read_v1, + .power_down = rockchip_saradc_power_down_v1, +}; + +static const struct saradc_channel rk3588_saradc_channels[] = +{ + SARADC_CHANNEL(0, "adc0", 12), + SARADC_CHANNEL(1, "adc1", 12), + SARADC_CHANNEL(2, "adc2", 12), + SARADC_CHANNEL(3, "adc3", 12), + SARADC_CHANNEL(4, "adc4", 12), + SARADC_CHANNEL(5, "adc5", 12), + SARADC_CHANNEL(6, "adc6", 12), + SARADC_CHANNEL(7, "adc7", 12), +}; + +static const struct rockchip_saradc_soc_data rk3588_saradc_data = +{ + .channels = rk3588_saradc_channels, + .num_channels = RT_ARRAY_SIZE(rk3588_saradc_channels), + .clk_rate = 1000000, + .start = rockchip_saradc_start_v2, + .read = rockchip_saradc_read_v2, +}; + +static void rockchip_saradc_start(struct rockchip_saradc *rk_saradc, int chn) +{ + rk_saradc->soc_data->start(rk_saradc, chn); +} + +static int rockchip_saradc_read(struct rockchip_saradc *rk_saradc) +{ + return rk_saradc->soc_data->read(rk_saradc); +} + +static void rockchip_saradc_power_down(struct rockchip_saradc *rk_saradc) +{ + if (rk_saradc->soc_data->power_down) + { + rk_saradc->soc_data->power_down(rk_saradc); + } +} + +static rt_err_t rockchip_saradc_enabled(struct rt_adc_device *adc, rt_int8_t channel, rt_bool_t enabled) +{ + return RT_EOK; +} + +static rt_err_t rockchip_saradc_convert(struct rt_adc_device *adc, rt_int8_t channel, rt_uint32_t *value) +{ + rt_err_t err = RT_EOK; + struct rockchip_saradc *rk_saradc = raw_to_rockchip_saradc(adc); + + rt_mutex_take(&rk_saradc->lock, RT_WAITING_FOREVER); + + rk_saradc->last_chan = (struct saradc_channel *)&rk_saradc->soc_data->channels[channel]; + rockchip_saradc_start(rk_saradc, channel); + + /* Select the channel to be used and trigger conversion */ + HWREG32(rk_saradc->regs + SARADC_CTRL) =SARADC_CTRL_POWER_CTRL | + (channel & SARADC_CTRL_CHN_MASK) | SARADC_CTRL_IRQ_ENABLE; + + /* Delay 100ms */ + if (!(err = rt_completion_wait(&rk_saradc->completion, SARADC_TIMEOUT))) + { + *value = rk_saradc->last_val; + } + + rt_mutex_release(&rk_saradc->lock); + + return err; +} + +static const struct rt_adc_ops rockchip_saradc_ops = +{ + .enabled = rockchip_saradc_enabled, + .convert = rockchip_saradc_convert, +}; + +static void rockchip_saradc_isr(int irqno, void *param) +{ + struct rockchip_saradc *rk_saradc = (struct rockchip_saradc *)param; + + /* Read value */ + rk_saradc->last_val = rockchip_saradc_read(rk_saradc); + rk_saradc->last_val &= RT_GENMASK(rk_saradc->last_chan->scan_type.realbits - 1, 0); + + rockchip_saradc_power_down(rk_saradc); + + rt_completion_done(&rk_saradc->completion); +} + +static void rockchip_saradc_reset_controller(struct rockchip_saradc *rk_saradc) +{ + rt_reset_control_assert(rk_saradc->rstc); + rt_hw_us_delay(15); + rt_reset_control_deassert(rk_saradc->rstc); +} + +static void rockchip_saradc_free(struct rockchip_saradc *rk_saradc) +{ + if (rk_saradc->regs) + { + rt_iounmap(rk_saradc->regs); + } + + if (!rt_is_err_or_null(rk_saradc->rstc)) + { + rt_reset_control_put(rk_saradc->rstc); + } + + if (!rt_is_err_or_null(rk_saradc->vref)) + { + rt_regulator_disable(rk_saradc->vref); + } + + if (!rt_is_err_or_null(rk_saradc->clk)) + { + rt_clk_disable(rk_saradc->clk); + rt_clk_put(rk_saradc->clk); + } + + if (!rt_is_err_or_null(rk_saradc->pclk)) + { + rt_clk_disable(rk_saradc->pclk); + rt_clk_put(rk_saradc->pclk); + } + + rt_free(rk_saradc); +} + +static rt_err_t rockchip_saradc_probe(struct rt_platform_device *pdev) +{ + rt_err_t err = RT_EOK; + const char *dev_name; + struct rt_device *dev = &pdev->parent; + const struct rockchip_saradc_soc_data *soc_data = pdev->id->data; + struct rockchip_saradc *rk_saradc = rt_calloc(1, sizeof(*rk_saradc)); + + if (!rk_saradc) + { + return -RT_ENOMEM; + } + + rk_saradc->soc_data = soc_data; + rk_saradc->regs = rt_dm_dev_iomap(dev, 0); + + if (!rk_saradc->regs) + { + err = -RT_EIO; + goto _free_res; + } + + rk_saradc->irq = rt_dm_dev_get_irq(dev, 0); + + if (rk_saradc->irq < 0) + { + err = rk_saradc->irq; + goto _free_res; + } + + rk_saradc->vref = rt_regulator_get_optional(dev, "vref"); + + if (rt_is_err(rk_saradc->vref)) + { + err = rt_ptr_err(rk_saradc->vref); + goto _free_res; + } + + rk_saradc->rstc = rt_reset_control_get_by_name(dev, "saradc-apb"); + + if (rt_is_err(rk_saradc->rstc)) + { + err = rt_ptr_err(rk_saradc->rstc); + goto _free_res; + } + + if (rk_saradc->rstc) + { + rockchip_saradc_reset_controller(rk_saradc); + } + + rk_saradc->pclk = rt_clk_get_by_name(dev, "apb_pclk"); + + if (rt_is_err(rk_saradc->pclk)) + { + err = rt_ptr_err(rk_saradc->pclk); + goto _free_res; + } + + if ((err = rt_clk_enable(rk_saradc->pclk))) + { + goto _free_res; + } + + rk_saradc->clk = rt_clk_get_by_name(dev, "saradc"); + + if (rt_is_err(rk_saradc->clk)) + { + err = rt_ptr_err(rk_saradc->clk); + goto _free_res; + } + + if ((err = rt_clk_set_rate(rk_saradc->clk, soc_data->clk_rate))) + { + goto _free_res; + } + + if ((err = rt_clk_enable(rk_saradc->clk))) + { + goto _free_res; + } + + if ((err = rt_regulator_enable(rk_saradc->vref))) + { + goto _free_res; + } + + dev->user_data = rk_saradc; + + rt_dm_dev_set_name_auto(&rk_saradc->parent.parent, "saradc"); + dev_name = rt_dm_dev_get_name(&rk_saradc->parent.parent); + + rt_mutex_init(&rk_saradc->lock, dev_name, RT_IPC_FLAG_PRIO); + rt_completion_init(&rk_saradc->completion); + + rt_hw_adc_register(&rk_saradc->parent, dev_name, &rockchip_saradc_ops, rk_saradc); + rt_hw_interrupt_install(rk_saradc->irq, rockchip_saradc_isr, rk_saradc, dev_name); + rt_hw_interrupt_umask(rk_saradc->irq); + + return RT_EOK; + +_free_res: + rockchip_saradc_free(rk_saradc); + + return err; +} + +static rt_err_t rockchip_saradc_remove(struct rt_platform_device *pdev) +{ + struct rockchip_saradc *rk_saradc = pdev->parent.user_data; + + rt_hw_interrupt_mask(rk_saradc->irq); + rt_pic_detach_irq(rk_saradc->irq, rk_saradc); + + rt_device_unregister(&rk_saradc->parent.parent); + + rockchip_saradc_free(rk_saradc); + + return RT_EOK; +} + +static const struct rt_ofw_node_id rockchip_saradc_ofw_ids[] = +{ + { .compatible = "rockchip,saradc", .data = &saradc_data }, + { .compatible = "rockchip,rk3066-tsadc", .data = &rk3066_tsadc_data }, + { .compatible = "rockchip,rk3399-saradc", .data = &rk3399_saradc_data }, + { .compatible = "rockchip,rk3568-saradc", .data = &rk3568_saradc_data }, + { .compatible = "rockchip,rk3588-saradc", .data = &rk3588_saradc_data, }, + { /* sentinel */ } +}; + +static struct rt_platform_driver rockchip_saradc_driver = +{ + .name = "rockchip-saradc", + .ids = rockchip_saradc_ofw_ids, + + .probe = rockchip_saradc_probe, + .remove = rockchip_saradc_remove, +}; + +static int rockchip_saradc_drv_register(void) +{ + rt_platform_driver_register(&rockchip_saradc_driver); + + return 0; +} +INIT_DRIVER_LATER_EXPORT(rockchip_saradc_drv_register); diff --git a/components/drivers/adc/adc-rp1.c b/components/drivers/adc/adc-rp1.c new file mode 100644 index 000000000000..05130cff9e26 --- /dev/null +++ b/components/drivers/adc/adc-rp1.c @@ -0,0 +1,330 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-08-24 GuEe-GUI first version + */ + +#include +#include +#include + +#define RP1_ADC_CS 0x00 +#define RP1_ADC_RESULT 0x04 +#define RP1_ADC_FCS 0x08 +#define RP1_ADC_FIFO 0x0c +#define RP1_ADC_DIV 0x10 + +#define RP1_ADC_INTR 0x14 +#define RP1_ADC_INTE 0x18 +#define RP1_ADC_INTF 0x1c +#define RP1_ADC_INTS 0x20 + +#define RP1_ADC_RWTYPE_SET 0x2000 +#define RP1_ADC_RWTYPE_CLR 0x3000 + +#define RP1_ADC_CS_RROBIN_MASK 0x1f +#define RP1_ADC_CS_RROBIN_SHIFT 16 +#define RP1_ADC_CS_AINSEL_MASK 0x7 +#define RP1_ADC_CS_AINSEL_SHIFT 12 +#define RP1_ADC_CS_ERR_STICKY 0x400 +#define RP1_ADC_CS_ERR 0x200 +#define RP1_ADC_CS_READY 0x100 +#define RP1_ADC_CS_START_MANY 0x8 +#define RP1_ADC_CS_START_ONCE 0x4 +#define RP1_ADC_CS_TS_EN 0x2 +#define RP1_ADC_CS_EN 0x1 + +#define RP1_ADC_FCS_THRESH_MASK 0xf +#define RP1_ADC_FCS_THRESH_SHIFT 24 +#define RP1_ADC_FCS_LEVEL_MASK 0xf +#define RP1_ADC_FCS_LEVEL_SHIFT 16 +#define RP1_ADC_FCS_OVER 0x800 +#define RP1_ADC_FCS_UNDER 0x400 +#define RP1_ADC_FCS_FULL 0x200 +#define RP1_ADC_FCS_EMPTY 0x100 +#define RP1_ADC_FCS_DREQ_EN 0x8 +#define RP1_ADC_FCS_ERR 0x4 +#define RP1_ADC_FCS_SHIFR 0x2 +#define RP1_ADC_FCS_EN 0x1 + +#define RP1_ADC_FIFO_ERR 0x8000 +#define RP1_ADC_FIFO_VAL_MASK 0xfff + +#define RP1_ADC_DIV_INT_MASK 0xffff +#define RP1_ADC_DIV_INT_SHIFT 8 +#define RP1_ADC_DIV_FRAC_MASK 0xff +#define RP1_ADC_DIV_FRAC_SHIFT 0 + +enum +{ + RP1_ADC_IN, + RP1_ADC_RAW, + RP1_ADC_TEMP, +}; + +struct rp1_adc_channel +{ + rt_uint32_t type; + + const char *name; +}; + +struct rp1_adc +{ + struct rt_adc_device parent; + + void *regs; + int vref_mv; + + struct rt_clk *clk; + struct rt_regulator *vref; + + rt_size_t channel_nr; + struct rp1_adc_channel *channel; + + struct rt_spinlock lock; +}; + +#define raw_to_rp1_adc(raw) rt_container_of(raw, struct rp1_adc, parent) + +static rt_err_t rp1_adc_ready_wait(struct rp1_adc *radc) +{ + int retries = 10; + + while (retries && !(HWREG32(radc->regs + RP1_ADC_CS) & RP1_ADC_CS_READY)) + { + --retries; + } + + return retries ? 0 : -RT_EIO; +} + +static rt_err_t rp1_adc_read(struct rp1_adc *radc, int channel, rt_uint32_t *val) +{ + rt_err_t err; + + rt_hw_spin_lock(&radc->lock.lock); + + HWREG32(radc->regs + RP1_ADC_RWTYPE_CLR + RP1_ADC_CS) = + RP1_ADC_CS_AINSEL_MASK << RP1_ADC_CS_AINSEL_SHIFT; + HWREG32(radc->regs + RP1_ADC_RWTYPE_SET + RP1_ADC_CS) = + channel << RP1_ADC_CS_AINSEL_SHIFT; + HWREG32(radc->regs + RP1_ADC_RWTYPE_SET + RP1_ADC_CS) = + RP1_ADC_CS_START_ONCE; + + if ((err = rp1_adc_ready_wait(radc))) + { + goto _out_lock; + } + + /* Asserted if the completed conversion had a convergence error */ + if (HWREG32(radc->regs + RP1_ADC_CS) & RP1_ADC_CS_ERR) + { + err = -RT_EIO; + goto _out_lock; + } + + *val = HWREG32(radc->regs + RP1_ADC_RESULT); + +_out_lock: + rt_hw_spin_unlock(&radc->lock.lock); + + return err; +} + +static int rp1_adc_to_mv(struct rp1_adc *radc, rt_uint32_t val) +{ + return ((rt_uint64_t)radc->vref_mv * val) / 0xfff; +} + +static struct rp1_adc_channel adc_channel[] = +{ + { .type = RP1_ADC_IN, .name = "in1-input" }, + { .type = RP1_ADC_IN, .name = "in2-input" }, + { .type = RP1_ADC_IN, .name = "in3-input" }, + { .type = RP1_ADC_IN, .name = "in4-input" }, + { .type = RP1_ADC_TEMP, .name = "temp1-input" }, + { .type = RP1_ADC_RAW, .name = "in1-raw" }, + { .type = RP1_ADC_RAW, .name = "in2-raw" }, + { .type = RP1_ADC_RAW, .name = "in3-raw" }, + { .type = RP1_ADC_RAW, .name = "in4-raw" }, + { .type = RP1_ADC_RAW, .name = "temp1-raw" }, +}; + +static rt_err_t rp1_adc_enabled(struct rt_adc_device *adc, rt_int8_t channel, rt_bool_t enabled) +{ + return RT_EOK; +} + +static rt_err_t rp1_adc_convert(struct rt_adc_device *adc, rt_int8_t channel, rt_uint32_t *value) +{ + rt_err_t err; + rt_uint32_t val; + struct rp1_adc_channel *chan; + struct rp1_adc *radc = raw_to_rp1_adc(adc); + + if (channel >= radc->channel_nr) + { + return -RT_EINVAL; + } + + chan = &radc->channel[channel]; + + if (chan->type == RP1_ADC_TEMP) + { + int mv; + + HWREG32(radc->regs + RP1_ADC_RWTYPE_SET + RP1_ADC_CS) = RP1_ADC_CS_TS_EN; + + if ((err = rp1_adc_read(radc, channel, &val))) + { + return err; + } + + mv = rp1_adc_to_mv(radc, val); + + /* T = 27 - (ADC_voltage - 0.706)/0.001721 */ + + *value = 27000 - RT_DIV_ROUND_CLOSEST((mv - 706) * (rt_int64_t)1000000, 1721); + } + else if (chan->type == RP1_ADC_IN || chan->type == RP1_ADC_RAW) + { + if ((err = rp1_adc_read(radc, channel, &val))) + { + return err; + } + + *value = val; + } + + return RT_EOK; +} + +static const struct rt_adc_ops rp1_adc_ops = +{ + .enabled = rp1_adc_enabled, + .convert = rp1_adc_convert, +}; + +static void rp1_adc_free(struct rp1_adc *radc) +{ + if (radc->regs) + { + rt_iounmap(radc->regs); + } + + if (!rt_is_err_or_null(radc->clk)) + { + rt_clk_disable_unprepare(radc->clk); + rt_clk_put(radc->clk); + } + + rt_free(radc); +} + +static rt_err_t rp1_adc_probe(struct rt_platform_device *pdev) +{ + int vref_uv; + rt_err_t err; + const char *dev_name; + struct rt_device *dev = &pdev->parent; + struct rp1_adc *radc = rt_calloc(1, sizeof(*radc)); + + if (!radc) + { + return -RT_ENOMEM; + } + + radc->regs = rt_dm_dev_iomap(dev, 0); + + if (!radc->regs) + { + err = -RT_EIO; + goto _fail; + } + + radc->clk = rt_clk_get_by_index(dev, 0); + + if (rt_is_err(radc->clk)) + { + err = rt_ptr_err(radc->clk); + goto _fail; + } + + rt_clk_set_rate(radc->clk, 50000000); + rt_clk_prepare_enable(radc->clk); + + radc->vref = rt_regulator_get_optional(dev, "vref"); + + if (rt_is_err(radc->vref)) + { + err = rt_ptr_err(radc->vref); + goto _fail; + } + + vref_uv = rt_regulator_get_voltage(radc->vref); + radc->vref_mv = RT_DIV_ROUND_CLOSEST(vref_uv, 1000); + + radc->channel_nr = RT_ARRAY_SIZE(adc_channel); + radc->channel = adc_channel; + + rt_spin_lock_init(&radc->lock); + + dev->user_data = radc; + + rt_dm_dev_set_name_auto(&radc->parent.parent, "adc"); + dev_name = rt_dm_dev_get_name(&radc->parent.parent); + + rt_hw_adc_register(&radc->parent, dev_name, &rp1_adc_ops, radc); + + /* Disable interrupts */ + HWREG32(radc->regs + RP1_ADC_INTE) = 0; + + /* Enable the block, clearing any sticky error */ + HWREG32(radc->regs + RP1_ADC_CS) = RP1_ADC_CS_EN | RP1_ADC_CS_ERR_STICKY; + + return RT_EOK; + +_fail: + rp1_adc_free(radc); + + return err; +} + +static rt_err_t rp1_adc_remove(struct rt_platform_device *pdev) +{ + struct rp1_adc *radc = pdev->parent.user_data; + + rt_device_unregister(&radc->parent.parent); + + rp1_adc_free(radc); + + return RT_EOK; +} + +static const struct rt_ofw_node_id rp1_adc_ofw_ids[] = +{ + { .compatible = "raspberrypi,rp1-adc" }, + { /* sentinel */ } +}; + +static struct rt_platform_driver rp1_adc_driver = +{ + .name = "rp1-adc", + .ids = rp1_adc_ofw_ids, + + .probe = rp1_adc_probe, + .remove = rp1_adc_remove, +}; + +static int rp1_adc_drv_register(void) +{ + rt_platform_driver_register(&rp1_adc_driver); + + return 0; +} +INIT_DRIVER_LATER_EXPORT(rp1_adc_drv_register); diff --git a/components/drivers/misc/adc.c b/components/drivers/adc/adc.c similarity index 98% rename from components/drivers/misc/adc.c rename to components/drivers/adc/adc.c index 0f71e096f248..1216a1de1718 100644 --- a/components/drivers/misc/adc.c +++ b/components/drivers/adc/adc.c @@ -47,11 +47,11 @@ static rt_err_t _adc_control(rt_device_t dev, int cmd, void *args) if (cmd == RT_ADC_CMD_ENABLE && adc->ops->enabled) { - result = adc->ops->enabled(adc, (rt_uint32_t)args, RT_TRUE); + result = adc->ops->enabled(adc, (rt_uint8_t)(rt_ubase_t)args, RT_TRUE); } else if (cmd == RT_ADC_CMD_DISABLE && adc->ops->enabled) { - result = adc->ops->enabled(adc, (rt_uint32_t)args, RT_FALSE); + result = adc->ops->enabled(adc, (rt_uint8_t)(rt_ubase_t)args, RT_FALSE); } else if (cmd == RT_ADC_CMD_GET_RESOLUTION && adc->ops->get_resolution && args) { diff --git a/components/drivers/audio/Kconfig b/components/drivers/audio/Kconfig new file mode 100644 index 000000000000..ec95d4f7e17a --- /dev/null +++ b/components/drivers/audio/Kconfig @@ -0,0 +1,29 @@ +menuconfig RT_USING_AUDIO + select RT_USING_MEMPOOL + bool "Using Audio device drivers" + default n + + if RT_USING_AUDIO + config RT_AUDIO_REPLAY_MP_BLOCK_SIZE + int "Replay memory pool block size" + default 4096 + + config RT_AUDIO_REPLAY_MP_BLOCK_COUNT + int "Replay memory pool block count" + default 2 + + config RT_AUDIO_RECORD_PIPE_SIZE + int "Record pipe size" + default 2048 + endif + +config RT_AUDIO_INTEL_HDA + bool "Enable Intel High Definition Audio" + depends on RT_USING_DM + depends on RT_USING_AUDIO + select RT_USING_PCI + default n + +if RT_USING_DM && RT_USING_AUDIO +source "$RTT_DIR/components/drivers/audio/rockchip/Kconfig" +endif diff --git a/components/drivers/audio/SConscript b/components/drivers/audio/SConscript index 1ea671d5555a..69e0ce516b3b 100644 --- a/components/drivers/audio/SConscript +++ b/components/drivers/audio/SConscript @@ -1,9 +1,23 @@ from building import * -cwd = GetCurrentDir() -src = Glob('*.c') +group = [] +objs = [] + +if not GetDepend('RT_USING_AUDIO'): + Return('group') + +cwd = GetCurrentDir() +list = os.listdir(cwd) CPPPATH = [cwd] -group = DefineGroup('DeviceDrivers', src, depend = ['RT_USING_AUDIO'], CPPPATH = CPPPATH) +src = ['audio.c', 'audio_pipe.c'] + +group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) + +for d in list: + path = os.path.join(cwd, d) + if os.path.isfile(os.path.join(path, 'SConscript')): + objs = objs + SConscript(os.path.join(d, 'SConscript')) +objs = objs + group -Return('group') +Return('objs') diff --git a/components/drivers/audio/intel-hda/SConscript b/components/drivers/audio/intel-hda/SConscript new file mode 100644 index 000000000000..b8dddc11a092 --- /dev/null +++ b/components/drivers/audio/intel-hda/SConscript @@ -0,0 +1,15 @@ +from building import * + +group = [] + +if not GetDepend(['RT_AUDIO_INTEL_HDA']): + Return('group') + +cwd = GetCurrentDir() +CPPPATH = [cwd + '/../../include'] + +src = Glob('*.c') + +group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') diff --git a/components/drivers/audio/intel-hda/hda-defs.h b/components/drivers/audio/intel-hda/hda-defs.h new file mode 100644 index 000000000000..3c349b0ad124 --- /dev/null +++ b/components/drivers/audio/intel-hda/hda-defs.h @@ -0,0 +1,743 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-10-24 GuEe-GUI first version + */ + +#ifndef __INTEL_HDA_DEFS_H__ +#define __INTEL_HDA_DEFS_H__ + +/* + * registers + */ +#define ICH6_REG_GCAP 0x00 /* Global Capabilities */ +#define ICH6_GCAP_64OK (1 << 0) /* 64bit address support */ +#define ICH6_GCAP_NSDO (3 << 1) /* # of serial data out signals */ +#define ICH6_GCAP_BSS (31 << 3) /* # of bidirectional streams */ +#define ICH6_GCAP_ISS (15 << 8) /* # of input streams */ +#define ICH6_GCAP_OSS (15 << 12) /* # of output streams */ +#define ICH6_REG_VMIN 0x02 /* Minor Version */ +#define ICH6_REG_VMAJ 0x03 /* Major Version */ +#define ICH6_REG_OUTPAY 0x04 /* Output Payload Capability */ +#define ICH6_REG_INPAY 0x06 /* Input Payload Capability */ +#define ICH6_REG_GCTL 0x08 /* Global Control */ +#define ICH6_GCTL_RESET (1 << 0) /* controller reset */ +#define ICH6_GCTL_FCNTRL (1 << 1) /* flush control */ +#define ICH6_GCTL_UNSOL (1 << 8) /* accept unsol. response enable */ +#define ICH6_REG_WAKEEN 0x0c /* Wake Enable */ +#define ICH6_REG_STATESTS 0x0e /* State Change Status */ +#define ICH6_REG_GSTS 0x10 /* Global Status */ +#define ICH6_GSTS_FSTS (1 << 1) /* flush status */ +#define ICH6_REG_OUTSTRMPAY 0x18 /* Output Stream Payload Capability */ +#define ICH6_REG_INSTRMPAY 0x1a /* Input Stream Payload Capability */ +#define ICH6_REG_INTCTL 0x20 /* Interrupt Control */ +#define ICH6_REG_INTSTS 0x24 /* Interrupt Status */ +#define ICH6_REG_WALLCLK 0x30 /* Wall Clock Counter: 24Mhz source */ +#define ICH6_REG_SYNC 0x34 +#define ICH6_REG_SSYNC 0x38 /* Stream Synchronization */ +#define ICH6_REG_CORBLBASE 0x40 /* CORB Lower Base Address */ +#define ICH6_REG_CORBUBASE 0x44 /* CORB Upper Base Address */ +#define ICH6_REG_CORBWP 0x48 /* CORB Write Pointer */ +#define ICH6_REG_CORBRP 0x4a /* CORB Read Pointer */ +#define ICH6_CORBRP_RST (1 << 15) /* read pointer reset */ +#define ICH6_REG_CORBCTL 0x4c /* CORB Control */ +#define ICH6_CORBCTL_RUN (1 << 1) /* enable DMA */ +#define ICH6_CORBCTL_CMEIE (1 << 0) /* enable memory error irq */ +#define ICH6_REG_CORBSTS 0x4d /* CORB Status */ +#define ICH6_CORBSTS_CMEI (1 << 0) /* memory error indication */ +#define ICH6_REG_CORBSIZE 0x4e /* CORB Size */ + +#define ICH6_REG_RIRBLBASE 0x50 /* RIRB Lower Base Address */ +#define ICH6_REG_RIRBUBASE 0x54 /* RIRB Upper Base Address */ +#define ICH6_REG_RIRBWP 0x58 /* RIRB Write Pointer */ +#define ICH6_RIRBWP_RST (1 << 15) /* write pointer reset */ +#define ICH6_REG_RINTCNT 0x5a /* Response Interrupt Count */ +#define ICH6_REG_RIRBCTL 0x5c /* RIRB Control */ +#define ICH6_RBCTL_IRQ_EN (1 << 0) /* enable IRQ */ +#define ICH6_RBCTL_DMA_EN (1 << 1) /* enable DMA */ +#define ICH6_RBCTL_OVERRUN_EN (1 << 2) /* enable overrun irq */ +#define ICH6_REG_RIRBSTS 0x5d /* RIRB Status */ +#define ICH6_RBSTS_IRQ (1 << 0) /* response irq */ +#define ICH6_RBSTS_OVERRUN (1 << 2) /* overrun irq */ +#define ICH6_REG_RIRBSIZE 0x5e /* RIRB Size */ + +#define ICH6_REG_IC 0x60 /* Immediate Command Output Interface */ +#define ICH6_REG_IR 0x64 /* Immediate Response Input Interface */ +#define ICH6_REG_IRS 0x68 /* Immediate Command Status */ +#define ICH6_IRS_VALID (1 << 1) +#define ICH6_IRS_BUSY (1 << 0) + +#define ICH6_REG_DPLBASE 0x70 /* DMA Position Lower Base Address */ +#define ICH6_REG_DPUBASE 0x74 /* DMA Position Upper Base Address */ +#define ICH6_DPLBASE_ENABLE 0x1 /* Enable position buffer */ + +#define ICH6_SDI0_OFF 0x080 +#define ICH6_SDI1_OFF 0x0a0 +#define ICH6_SDI2_OFF 0x0c0 +#define ICH6_SDI3_OFF 0x0e0 +#define ICH6_SDO0_OFF 0x100 +#define ICH6_REG_SDO0_CTLL (ICH6_SDO0_OFF + 0x00) +#define ICH6_REG_SDO0_CTLU (ICH6_SDO0_OFF + 0x02) +#define ICH6_REG_SDO0_STS (ICH6_SDO0_OFF + 0x03) +#define SDO0_STS_BCIS (1 << 2) +#define SDO0_STS_FIFOE (1 << 3) +#define SDO0_STS_DESE (1 << 4) +#define SDO0_STS_FIFORDY (1 << 5) +#define ICH6_REG_SDO0_CBL (ICH6_SDO0_OFF + 0x08) +#define ICH6_REG_SDO0_STLVI (ICH6_SDO0_OFF + 0x0c) +#define ICH6_REG_SDO0_FMT (ICH6_SDO0_OFF + 0x12) +#define ICH6_REG_SDO0_BDLPL (ICH6_SDO0_OFF + 0x18) +#define ICH6_REG_SDO0_BDLPU (ICH6_SDO0_OFF + 0x1c) +#define ICH6_SDO1_OFF 0x120 +#define ICH6_SDO2_OFF 0x140 +#define ICH6_SDO3_OFF 0x160 + +/* stream register offsets from stream base */ +#define ICH6_REG_SD_CTL 0x00 +#define ICH6_REG_SD_STS 0x03 +#define ICH6_REG_SD_LPIB 0x04 +#define ICH6_REG_SD_CBL 0x08 +#define ICH6_REG_SD_LVI 0x0c +#define ICH6_REG_SD_FIFOW 0x0e +#define ICH6_REG_SD_FIFOSIZE 0x10 +#define ICH6_REG_SD_FORMAT 0x12 +#define ICH6_REG_SD_BDLPL 0x18 +#define ICH6_REG_SD_BDLPU 0x1c + +/* PCI space */ +#define ICH6_PCIREG_TCSEL 0x44 + +/* + * other constants + */ + +/* max number of SDs */ +/* ICH, ATI and VIA have 4 playback and 4 capture */ +#define ICH6_NUM_CAPTURE 4 +#define ICH6_NUM_PLAYBACK 4 + +/* ULI has 6 playback and 5 capture */ +#define ULI_NUM_CAPTURE 5 +#define ULI_NUM_PLAYBACK 6 + +/* ATI HDMI has 1 playback and 0 capture */ +#define ATIHDMI_NUM_CAPTURE 0 +#define ATIHDMI_NUM_PLAYBACK 1 + +/* TERA has 4 playback and 3 capture */ +#define TERA_NUM_CAPTURE 3 +#define TERA_NUM_PLAYBACK 4 + +/* this number is statically defined for simplicity */ +#define MAX_AZX_DEV 16 + +/* max number of fragments - we may use more if allocating more pages for BDL */ +#define BDL_SIZE 4096 +#define AZX_MAX_BDL_ENTRIES (BDL_SIZE / 16) +#define AZX_MAX_FRAG 32 +/* max buffer size - no h/w limit, you can increase as you like */ +#define AZX_MAX_BUF_SIZE (1024 * 1024 * 1024) + +/* RIRB int mask: overrun[2], response[0] */ +#define RIRB_INT_RESPONSE 0x01 +#define RIRB_INT_OVERRUN 0x04 +#define RIRB_INT_MASK 0x05 + +/* STATESTS int mask: S3,SD2,SD1,SD0 */ +#define AZX_MAX_CODECS 8 +#define AZX_DEFAULT_CODECS 4 +#define STATESTS_INT_MASK ((1 << AZX_MAX_CODECS) - 1) + +/* SD_CTL bits */ +#define SD_CTL_STREAM_RESET 0x01 /* stream reset bit */ +#define SD_CTL_DMA_START 0x02 /* stream DMA start bit */ +#define SD_CTL_STRIPE (3 << 16) /* stripe control */ +#define SD_CTL_TRAFFIC_PRIO (1 << 18) /* traffic priority */ +#define SD_CTL_DIR (1 << 19) /* bi-directional stream */ +#define SD_CTL_STREAM_TAG_MASK (0xf << 20) +#define SD_CTL_STREAM_TAG_SHIFT 20 + +/* SD_CTL and SD_STS */ +#define SD_INT_DESC_ERR 0x10 /* descriptor error interrupt */ +#define SD_INT_FIFO_ERR 0x08 /* FIFO error interrupt */ +#define SD_INT_COMPLETE 0x04 /* completion interrupt */ +#define SD_INT_MASK (SD_INT_DESC_ERR | SD_INT_FIFO_ERR | SD_INT_COMPLETE) + +/* SD_STS */ +#define SD_STS_FIFO_READY 0x20 /* FIFO ready */ + +/* INTCTL and INTSTS */ +#define ICH6_INT_ALL_STREAM 0xff /* all stream interrupts */ +#define ICH6_INT_CTRL_EN 0x40000000 /* controller interrupt enable bit */ +#define ICH6_INT_GLOBAL_EN 0x80000000 /* global interrupt enable bit */ + +/* below are so far hardcoded - should read registers in future */ +#define ICH6_MAX_CORB_ENTRIES 256 +#define ICH6_MAX_RIRB_ENTRIES 256 + +/* position fix mode */ +enum +{ + POS_FIX_AUTO, + POS_FIX_LPIB, + POS_FIX_POSBUF, +}; + +/* Defines for ATI HD Audio support in SB450 south bridge */ +#define ATI_SB450_HDAUDIO_MISC_CNTR2_ADDR 0x42 +#define ATI_SB450_HDAUDIO_ENABLE_SNOOP 0x02 + +/* Defines for Nvidia HDA support */ +#define NVIDIA_HDA_TRANSREG_ADDR 0x4e +#define NVIDIA_HDA_ENABLE_COHBITS 0x0f +#define NVIDIA_HDA_ISTRM_COH 0x4d +#define NVIDIA_HDA_OSTRM_COH 0x4c +#define NVIDIA_HDA_ENABLE_COHBIT 0x01 + +/* Defines for Intel SCH HDA snoop control */ +#define INTEL_SCH_HDA_DEVC 0x78 +#define INTEL_SCH_HDA_DEVC_NOSNOOP (0x1 << 11) + +/* Define IN stream 0 FIFO size offset in VIA controller */ +#define VIA_IN_STREAM0_FIFO_SIZE_OFFSET 0x90 +/* Define VIA HD Audio Device ID*/ +#define VIA_HDAC_DEVICE_ID 0x3288 + +/* HD Audio class code */ +#define PCI_CLASS_MULTIMEDIA_HD_AUDIO 0x0403 + +/* + * nodes + */ +#define AC_NODE_ROOT 0x00 + +/* + * function group types + */ +enum +{ + AC_GRP_AUDIO_FUNCTION = 0x01, + AC_GRP_MODEM_FUNCTION = 0x02, +}; + +/* + * widget types + */ +enum +{ + AC_WID_AUD_OUT, /* Audio Out */ + AC_WID_AUD_IN, /* Audio In */ + AC_WID_AUD_MIX, /* Audio Mixer */ + AC_WID_AUD_SEL, /* Audio Selector */ + AC_WID_PIN, /* Pin Complex */ + AC_WID_POWER, /* Power */ + AC_WID_VOL_KNB, /* Volume Knob */ + AC_WID_BEEP, /* Beep Generator */ + AC_WID_VENDOR = 0x0f /* Vendor specific */ +}; + +/* + * GET verbs + */ +#define AC_VERB_GET_STREAM_FORMAT 0x0a00 +#define AC_VERB_GET_AMP_GAIN_MUTE 0x0b00 +#define AC_VERB_GET_PROC_COEF 0x0c00 +#define AC_VERB_GET_COEF_INDEX 0x0d00 +#define AC_VERB_PARAMETERS 0x0f00 +#define AC_VERB_GET_CONNECT_SEL 0x0f01 +#define AC_VERB_GET_CONNECT_LIST 0x0f02 +#define AC_VERB_GET_PROC_STATE 0x0f03 +#define AC_VERB_GET_SDI_SELECT 0x0f04 +#define AC_VERB_GET_POWER_STATE 0x0f05 +#define AC_VERB_GET_CONV 0x0f06 +#define AC_VERB_GET_PIN_WIDGET_CONTROL 0x0f07 +#define AC_VERB_GET_UNSOLICITED_RESPONSE 0x0f08 +#define AC_VERB_GET_PIN_SENSE 0x0f09 +#define AC_VERB_GET_BEEP_CONTROL 0x0f0a +#define AC_VERB_GET_EAPD_BTLENABLE 0x0f0c +#define AC_VERB_GET_DIGI_CONVERT_1 0x0f0d +#define AC_VERB_GET_DIGI_CONVERT_2 0x0f0e /* unused */ +#define AC_VERB_GET_VOLUME_KNOB_CONTROL 0x0f0f +/* f10-f1a: GPIO */ +#define AC_VERB_GET_GPIO_DATA 0x0f15 +#define AC_VERB_GET_GPIO_MASK 0x0f16 +#define AC_VERB_GET_GPIO_DIRECTION 0x0f17 +#define AC_VERB_GET_GPIO_WAKE_MASK 0x0f18 +#define AC_VERB_GET_GPIO_UNSOLICITED_RSP_MASK 0x0f19 +#define AC_VERB_GET_GPIO_STICKY_MASK 0x0f1a +#define AC_VERB_GET_CONFIG_DEFAULT 0x0f1c +/* f20: AFG/MFG */ +#define AC_VERB_GET_SUBSYSTEM_ID 0x0f20 +#define AC_VERB_GET_CVT_CHAN_COUNT 0x0f2d +#define AC_VERB_GET_HDMI_DIP_SIZE 0x0f2e +#define AC_VERB_GET_HDMI_ELDD 0x0f2f +#define AC_VERB_GET_HDMI_DIP_INDEX 0x0f30 +#define AC_VERB_GET_HDMI_DIP_DATA 0x0f31 +#define AC_VERB_GET_HDMI_DIP_XMIT 0x0f32 +#define AC_VERB_GET_HDMI_CP_CTRL 0x0f33 +#define AC_VERB_GET_HDMI_CHAN_SLOT 0x0f34 + +/* + * SET verbs + */ +#define AC_VERB_SET_STREAM_FORMAT 0x200 +#define AC_VERB_SET_AMP_GAIN_MUTE 0x300 +#define AC_VERB_SET_PROC_COEF 0x400 +#define AC_VERB_SET_COEF_INDEX 0x500 +#define AC_VERB_SET_CONNECT_SEL 0x701 +#define AC_VERB_SET_PROC_STATE 0x703 +#define AC_VERB_SET_SDI_SELECT 0x704 +#define AC_VERB_SET_POWER_STATE 0x705 +#define AC_VERB_SET_CHANNEL_STREAMID 0x706 +#define AC_VERB_SET_PIN_WIDGET_CONTROL 0x707 +#define AC_VERB_SET_UNSOLICITED_ENABLE 0x708 +#define AC_VERB_SET_PIN_SENSE 0x709 +#define AC_VERB_SET_BEEP_CONTROL 0x70a +#define AC_VERB_SET_EAPD_BTLENABLE 0x70c +#define AC_VERB_SET_DIGI_CONVERT_1 0x70d +#define AC_VERB_SET_DIGI_CONVERT_2 0x70e +#define AC_VERB_SET_VOLUME_KNOB_CONTROL 0x70f +#define AC_VERB_SET_GPIO_DATA 0x715 +#define AC_VERB_SET_GPIO_MASK 0x716 +#define AC_VERB_SET_GPIO_DIRECTION 0x717 +#define AC_VERB_SET_GPIO_WAKE_MASK 0x718 +#define AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK 0x719 +#define AC_VERB_SET_GPIO_STICKY_MASK 0x71a +#define AC_VERB_SET_CONFIG_DEFAULT_BYTES_0 0x71c +#define AC_VERB_SET_CONFIG_DEFAULT_BYTES_1 0x71d +#define AC_VERB_SET_CONFIG_DEFAULT_BYTES_2 0x71e +#define AC_VERB_SET_CONFIG_DEFAULT_BYTES_3 0x71f +#define AC_VERB_SET_EAPD 0x788 +#define AC_VERB_SET_CODEC_RESET 0x7ff +#define AC_VERB_SET_CVT_CHAN_COUNT 0x72d +#define AC_VERB_SET_HDMI_DIP_INDEX 0x730 +#define AC_VERB_SET_HDMI_DIP_DATA 0x731 +#define AC_VERB_SET_HDMI_DIP_XMIT 0x732 +#define AC_VERB_SET_HDMI_CP_CTRL 0x733 +#define AC_VERB_SET_HDMI_CHAN_SLOT 0x734 + +/* + * Parameter IDs + */ +#define AC_PAR_VENDOR_ID 0x00 +#define AC_PAR_SUBSYSTEM_ID 0x01 +#define AC_PAR_REV_ID 0x02 +#define AC_PAR_NODE_COUNT 0x04 +#define AC_PAR_FUNCTION_TYPE 0x05 +#define AC_PAR_AUDIO_FG_CAP 0x08 +#define AC_PAR_AUDIO_WIDGET_CAP 0x09 +#define AC_PAR_PCM 0x0a +#define AC_PAR_STREAM 0x0b +#define AC_PAR_PIN_CAP 0x0c +#define AC_PAR_AMP_IN_CAP 0x0d +#define AC_PAR_CONNLIST_LEN 0x0e +#define AC_PAR_POWER_STATE 0x0f +#define AC_PAR_PROC_CAP 0x10 +#define AC_PAR_GPIO_CAP 0x11 +#define AC_PAR_AMP_OUT_CAP 0x12 +#define AC_PAR_VOL_KNB_CAP 0x13 +#define AC_PAR_HDMI_LPCM_CAP 0x20 + +/* + * AC_VERB_PARAMETERS results (32bit) + */ + +/* Function Group Type */ +#define AC_FGT_TYPE (0xff << 0) +#define AC_FGT_TYPE_SHIFT 0 +#define AC_FGT_UNSOL_CAP (1 << 8) + +/* Audio Function Group Capabilities */ +#define AC_AFG_OUT_DELAY (0xf << 0) +#define AC_AFG_IN_DELAY (0xf << 8) +#define AC_AFG_BEEP_GEN (1 << 16) + +/* Audio Widget Capabilities */ +#define AC_WCAP_STEREO (1 << 0) /* stereo I/O */ +#define AC_WCAP_IN_AMP (1 << 1) /* AMP-in present */ +#define AC_WCAP_OUT_AMP (1 << 2) /* AMP-out present */ +#define AC_WCAP_AMP_OVRD (1 << 3) /* AMP-parameter override */ +#define AC_WCAP_FORMAT_OVRD (1 << 4) /* format override */ +#define AC_WCAP_STRIPE (1 << 5) /* stripe */ +#define AC_WCAP_PROC_WID (1 << 6) /* Proc Widget */ +#define AC_WCAP_UNSOL_CAP (1 << 7) /* Unsol capable */ +#define AC_WCAP_CONN_LIST (1 << 8) /* connection list */ +#define AC_WCAP_DIGITAL (1 << 9) /* digital I/O */ +#define AC_WCAP_POWER (1 << 10) /* power control */ +#define AC_WCAP_LR_SWAP (1 << 11) /* L/R swap */ +#define AC_WCAP_CP_CAPS (1 << 12) /* content protection */ +#define AC_WCAP_CHAN_CNT_EXT (7 << 13) /* channel count ext */ +#define AC_WCAP_DELAY (0xf << 16) +#define AC_WCAP_DELAY_SHIFT 16 +#define AC_WCAP_TYPE (0xf << 20) +#define AC_WCAP_TYPE_SHIFT 20 + +/* supported PCM rates and bits */ +#define AC_SUPPCM_RATES (0xfff << 0) +#define AC_SUPPCM_BITS_8 (1 << 16) +#define AC_SUPPCM_BITS_16 (1 << 17) +#define AC_SUPPCM_BITS_20 (1 << 18) +#define AC_SUPPCM_BITS_24 (1 << 19) +#define AC_SUPPCM_BITS_32 (1 << 20) + +/* supported PCM stream format */ +#define AC_SUPFMT_PCM (1 << 0) +#define AC_SUPFMT_FLOAT32 (1 << 1) +#define AC_SUPFMT_AC3 (1 << 2) + +/* GP I/O count */ +#define AC_GPIO_IO_COUNT (0xff << 0) +#define AC_GPIO_O_COUNT (0xff << 8) +#define AC_GPIO_O_COUNT_SHIFT 8 +#define AC_GPIO_I_COUNT (0xff << 16) +#define AC_GPIO_I_COUNT_SHIFT 16 +#define AC_GPIO_UNSOLICITED (1 << 30) +#define AC_GPIO_WAKE (1 << 31) + +/* Converter stream, channel */ +#define AC_CONV_CHANNEL (0xf << 0) +#define AC_CONV_STREAM (0xf << 4) +#define AC_CONV_STREAM_SHIFT 4 + +/* Input converter SDI select */ +#define AC_SDI_SELECT (0xf << 0) + +/* stream format id */ +#define AC_FMT_CHAN_SHIFT 0 +#define AC_FMT_CHAN_MASK (0x0f << 0) +#define AC_FMT_BITS_SHIFT 4 +#define AC_FMT_BITS_MASK (7 << 4) +#define AC_FMT_BITS_8 (0 << 4) +#define AC_FMT_BITS_16 (1 << 4) +#define AC_FMT_BITS_20 (2 << 4) +#define AC_FMT_BITS_24 (3 << 4) +#define AC_FMT_BITS_32 (4 << 4) +#define AC_FMT_DIV_SHIFT 8 +#define AC_FMT_DIV_MASK (7 << 8) +#define AC_FMT_MULT_SHIFT 11 +#define AC_FMT_MULT_MASK (7 << 11) +#define AC_FMT_BASE_SHIFT 14 +#define AC_FMT_BASE_48K (0 << 14) +#define AC_FMT_BASE_44K (1 << 14) +#define AC_FMT_TYPE_SHIFT 15 +#define AC_FMT_TYPE_PCM (0 << 15) +#define AC_FMT_TYPE_NON_PCM (1 << 15) + +/* Unsolicited response control */ +#define AC_UNSOL_TAG (0x3f << 0) +#define AC_UNSOL_ENABLED (1 << 7) +#define AC_USRSP_EN AC_UNSOL_ENABLED + +/* Unsolicited responses */ +#define AC_UNSOL_RES_TAG (0x3f << 26) +#define AC_UNSOL_RES_TAG_SHIFT 26 +#define AC_UNSOL_RES_SUBTAG (0x1f << 21) +#define AC_UNSOL_RES_SUBTAG_SHIFT 21 +#define AC_UNSOL_RES_ELDV (1 << 1) /* ELD Data valid (for HDMI) */ +#define AC_UNSOL_RES_PD (1 << 0) /* pinsense detect */ +#define AC_UNSOL_RES_CP_STATE (1 << 1) /* content protection */ +#define AC_UNSOL_RES_CP_READY (1 << 0) /* content protection */ + +/* Pin widget capabilies */ +#define AC_PINCAP_IMP_SENSE (1 << 0) /* impedance sense capable */ +#define AC_PINCAP_TRIG_REQ (1 << 1) /* trigger required */ +#define AC_PINCAP_PRES_DETECT (1 << 2) /* presence detect capable */ +#define AC_PINCAP_HP_DRV (1 << 3) /* headphone drive capable */ +#define AC_PINCAP_OUT (1 << 4) /* output capable */ +#define AC_PINCAP_IN (1 << 5) /* input capable */ +#define AC_PINCAP_BALANCE (1 << 6) /* balanced I/O capable */ +/* Note: This LR_SWAP pincap is defined in the Realtek ALC883 specification, + * but is marked reserved in the Intel HDA specification. + */ +#define AC_PINCAP_LR_SWAP (1 << 7) /* L/R swap */ +/* Note: The same bit as LR_SWAP is newly defined as HDMI capability + * in HD-audio specification + */ +#define AC_PINCAP_HDMI (1 << 7) /* HDMI pin */ +#define AC_PINCAP_DP (1 << 24) /* DisplayPort pin, can coexist with AC_PINCAP_HDMI */ +#define AC_PINCAP_VREF (0x37 << 8) +#define AC_PINCAP_VREF_SHIFT 8 +#define AC_PINCAP_EAPD (1 << 16) /* EAPD capable */ +#define AC_PINCAP_HBR (1 << 27) /* High Bit Rate */ +/* Vref status (used in pin cap) */ +#define AC_PINCAP_VREF_HIZ (1 << 0) /* Hi-Z */ +#define AC_PINCAP_VREF_50 (1 << 1) /* 50% */ +#define AC_PINCAP_VREF_GRD (1 << 2) /* ground */ +#define AC_PINCAP_VREF_80 (1 << 4) /* 80% */ +#define AC_PINCAP_VREF_100 (1 << 5) /* 100% */ + +/* Amplifier capabilities */ +#define AC_AMPCAP_OFFSET (0x7f << 0) /* 0dB offset */ +#define AC_AMPCAP_OFFSET_SHIFT 0 +#define AC_AMPCAP_NUM_STEPS (0x7f << 8) /* number of steps */ +#define AC_AMPCAP_NUM_STEPS_SHIFT 8 +#define AC_AMPCAP_STEP_SIZE (0x7f << 16) /* step size 0-32dB in 0.25dB */ +#define AC_AMPCAP_STEP_SIZE_SHIFT 16 +#define AC_AMPCAP_MUTE (1 << 31) /* mute capable */ +#define AC_AMPCAP_MUTE_SHIFT 31 + +/* Connection list */ +#define AC_CLIST_LENGTH (0x7f << 0) +#define AC_CLIST_LONG (1 << 7) + +/* Supported power status */ +#define AC_PWRST_D0SUP (1 << 0) +#define AC_PWRST_D1SUP (1 << 1) +#define AC_PWRST_D2SUP (1 << 2) +#define AC_PWRST_D3SUP (1 << 3) +#define AC_PWRST_D3COLDSUP (1 << 4) +#define AC_PWRST_S3D3COLDSUP (1 << 29) +#define AC_PWRST_CLKSTOP (1 << 30) +#define AC_PWRST_EPSS (1U << 31) + +/* Power state values */ +#define AC_PWRST_SETTING (0xf << 0) +#define AC_PWRST_ACTUAL (0xf << 4) +#define AC_PWRST_ACTUAL_SHIFT 4 +#define AC_PWRST_D0 0x00 +#define AC_PWRST_D1 0x01 +#define AC_PWRST_D2 0x02 +#define AC_PWRST_D3 0x03 + +/* Processing capabilies */ +#define AC_PCAP_BENIGN (1 << 0) +#define AC_PCAP_NUM_COEF (0xff << 8) +#define AC_PCAP_NUM_COEF_SHIFT 8 + +/* Volume knobs capabilities */ +#define AC_KNBCAP_NUM_STEPS (0x7f << 0) +#define AC_KNBCAP_DELTA (1 << 7) + +/* HDMI LPCM capabilities */ +#define AC_LPCMCAP_48K_CP_CHNS (0x0f << 0) /* max channels w/ CP-on */ +#define AC_LPCMCAP_48K_NO_CHNS (0x0f << 4) /* max channels w/o CP-on */ +#define AC_LPCMCAP_48K_20BIT (1 << 8) /* 20b bitrate supported */ +#define AC_LPCMCAP_48K_24BIT (1 << 9) /* 24b bitrate supported */ +#define AC_LPCMCAP_96K_CP_CHNS (0x0f << 10) /* max channels w/ CP-on */ +#define AC_LPCMCAP_96K_NO_CHNS (0x0f << 14) /* max channels w/o CP-on */ +#define AC_LPCMCAP_96K_20BIT (1 << 18) /* 20b bitrate supported */ +#define AC_LPCMCAP_96K_24BIT (1 << 19) /* 24b bitrate supported */ +#define AC_LPCMCAP_192K_CP_CHNS (0x0f << 20) /* max channels w/ CP-on */ +#define AC_LPCMCAP_192K_NO_CHNS (0x0f << 24) /* max channels w/o CP-on */ +#define AC_LPCMCAP_192K_20BIT (1 << 28) /* 20b bitrate supported */ +#define AC_LPCMCAP_192K_24BIT (1 << 29) /* 24b bitrate supported */ +#define AC_LPCMCAP_44K (1 << 30) /* 44.1kHz support */ +#define AC_LPCMCAP_44K_MS (1 << 31) /* 44.1kHz-multiplies support */ + +/* + * Control Parameters + */ + +/* Amp gain/mute */ +#define AC_AMP_MUTE (1 << 7) +#define AC_AMP_GAIN (0x7f) +#define AC_AMP_GET_INDEX (0xf << 0) + +#define AC_AMP_GET_LEFT (1 << 13) +#define AC_AMP_GET_RIGHT (0 << 13) +#define AC_AMP_GET_OUTPUT (1 << 15) +#define AC_AMP_GET_INPUT (0 << 15) + +#define AC_AMP_SET_INDEX (0xf << 8) +#define AC_AMP_SET_INDEX_SHIFT 8 +#define AC_AMP_SET_RIGHT (1 << 12) +#define AC_AMP_SET_LEFT (1 << 13) +#define AC_AMP_SET_INPUT (1 << 14) +#define AC_AMP_SET_OUTPUT (1 << 15) + +/* DIGITAL1 bits */ +#define AC_DIG1_ENABLE (1 << 0) +#define AC_DIG1_V (1 << 1) +#define AC_DIG1_VCFG (1 << 2) +#define AC_DIG1_EMPHASIS (1 << 3) +#define AC_DIG1_COPYRIGHT (1 << 4) +#define AC_DIG1_NONAUDIO (1 << 5) +#define AC_DIG1_PROFESSIONAL (1 << 6) +#define AC_DIG1_LEVEL (1 << 7) + +/* DIGITAL2 bits */ +#define AC_DIG2_CC (0x7f << 0) + +/* Pin widget control - 8bit */ +#define AC_PINCTL_EPT (0x3 << 0) +#define AC_PINCTL_EPT_NATIVE 0 +#define AC_PINCTL_EPT_HBR 3 +#define AC_PINCTL_VREFEN (0x7 << 0) +#define AC_PINCTL_VREF_HIZ 0 /* Hi-Z */ +#define AC_PINCTL_VREF_50 1 /* 50% */ +#define AC_PINCTL_VREF_GRD 2 /* ground */ +#define AC_PINCTL_VREF_80 4 /* 80% */ +#define AC_PINCTL_VREF_100 5 /* 100% */ +#define AC_PINCTL_IN_EN (1 << 5) +#define AC_PINCTL_OUT_EN (1 << 6) +#define AC_PINCTL_HP_EN (1 << 7) + +/* Pin sense - 32bit */ +#define AC_PINSENSE_IMPEDANCE_MASK (0x7fffffff) +#define AC_PINSENSE_PRESENCE (1 << 31) +#define AC_PINSENSE_ELDV (1 << 30) /* ELD valid (HDMI) */ + +/* EAPD/BTL enable - 32bit */ +#define AC_EAPDBTL_BALANCED (1 << 0) +#define AC_EAPDBTL_EAPD (1 << 1) +#define AC_EAPDBTL_LR_SWAP (1 << 2) + +/* HDMI ELD data */ +#define AC_ELDD_ELD_VALID (1 << 31) +#define AC_ELDD_ELD_DATA 0xff + +/* HDMI DIP size */ +#define AC_DIPSIZE_ELD_BUF (1 << 3) /* ELD buf size of packet size */ +#define AC_DIPSIZE_PACK_IDX (0x07 << 0) /* packet index */ + +/* HDMI DIP index */ +#define AC_DIPIDX_PACK_IDX (0x07 << 5) /* packet idnex */ +#define AC_DIPIDX_BYTE_IDX (0x1f << 0) /* byte index */ + +/* HDMI DIP xmit (transmit) control */ +#define AC_DIPXMIT_MASK (0x3 << 6) +#define AC_DIPXMIT_DISABLE (0x0 << 6) /* disable xmit */ +#define AC_DIPXMIT_ONCE (0x2 << 6) /* xmit once then disable */ +#define AC_DIPXMIT_BEST (0x3 << 6) /* best effort */ + +/* HDMI content protection (CP) control */ +#define AC_CPCTRL_CES (1 << 9) /* current encryption state */ +#define AC_CPCTRL_READY (1 << 8) /* ready bit */ +#define AC_CPCTRL_SUBTAG (0x1f << 3) /* subtag for unsol-resp */ +#define AC_CPCTRL_STATE (3 << 0) /* current CP request state */ + +/* Converter channel <-> HDMI slot mapping */ +#define AC_CVTMAP_HDMI_SLOT (0xf << 0) /* HDMI slot number */ +#define AC_CVTMAP_CHAN (0xf << 4) /* converter channel number */ + +/* configuration default - 32bit */ +#define AC_DEFCFG_SEQUENCE (0xf << 0) +#define AC_DEFCFG_DEF_ASSOC (0xf << 4) +#define AC_DEFCFG_ASSOC_SHIFT 4 +#define AC_DEFCFG_MISC (0xf << 8) +#define AC_DEFCFG_MISC_SHIFT 8 +#define AC_DEFCFG_MISC_NO_PRESENCE (1 << 0) +#define AC_DEFCFG_COLOR (0xf << 12) +#define AC_DEFCFG_COLOR_SHIFT 12 +#define AC_DEFCFG_CONN_TYPE (0xf << 16) +#define AC_DEFCFG_CONN_TYPE_SHIFT 16 +#define AC_DEFCFG_DEVICE (0xf << 20) +#define AC_DEFCFG_DEVICE_SHIFT 20 +#define AC_DEFCFG_LOCATION (0x3f << 24) +#define AC_DEFCFG_LOCATION_SHIFT 24 +#define AC_DEFCFG_PORT_CONN (0x3 << 30) +#define AC_DEFCFG_PORT_CONN_SHIFT 30 + +/* device device types (0x0-0xf) */ +enum +{ + AC_JACK_LINE_OUT, + AC_JACK_SPEAKER, + AC_JACK_HP_OUT, + AC_JACK_CD, + AC_JACK_SPDIF_OUT, + AC_JACK_DIG_OTHER_OUT, + AC_JACK_MODEM_LINE_SIDE, + AC_JACK_MODEM_HAND_SIDE, + AC_JACK_LINE_IN, + AC_JACK_AUX, + AC_JACK_MIC_IN, + AC_JACK_TELEPHONY, + AC_JACK_SPDIF_IN, + AC_JACK_DIG_OTHER_IN, + AC_JACK_OTHER = 0xf, +}; + +/* jack connection types (0x0-0xf) */ +enum +{ + AC_JACK_CONN_UNKNOWN, + AC_JACK_CONN_1_8, + AC_JACK_CONN_1_4, + AC_JACK_CONN_ATAPI, + AC_JACK_CONN_RCA, + AC_JACK_CONN_OPTICAL, + AC_JACK_CONN_OTHER_DIGITAL, + AC_JACK_CONN_OTHER_ANALOG, + AC_JACK_CONN_DIN, + AC_JACK_CONN_XLR, + AC_JACK_CONN_RJ11, + AC_JACK_CONN_COMB, + AC_JACK_CONN_OTHER = 0xf, +}; + +/* jack colors (0x0-0xf) */ +enum +{ + AC_JACK_COLOR_UNKNOWN, + AC_JACK_COLOR_BLACK, + AC_JACK_COLOR_GREY, + AC_JACK_COLOR_BLUE, + AC_JACK_COLOR_GREEN, + AC_JACK_COLOR_RED, + AC_JACK_COLOR_ORANGE, + AC_JACK_COLOR_YELLOW, + AC_JACK_COLOR_PURPLE, + AC_JACK_COLOR_PINK, + AC_JACK_COLOR_WHITE = 0xe, + AC_JACK_COLOR_OTHER, +}; + +/* Jack location (0x0-0x3f) */ +/* common case */ +enum +{ + AC_JACK_LOC_NONE, + AC_JACK_LOC_REAR, + AC_JACK_LOC_FRONT, + AC_JACK_LOC_LEFT, + AC_JACK_LOC_RIGHT, + AC_JACK_LOC_TOP, + AC_JACK_LOC_BOTTOM, +}; +/* bits 4-5 */ +enum +{ + AC_JACK_LOC_EXTERNAL = 0x00, + AC_JACK_LOC_INTERNAL = 0x10, + AC_JACK_LOC_SEPARATE = 0x20, + AC_JACK_LOC_OTHER = 0x30, +}; + +enum +{ + /* external on primary chasis */ + AC_JACK_LOC_REAR_PANEL = 0x07, + AC_JACK_LOC_DRIVE_BAY, + /* internal */ + AC_JACK_LOC_RISER = 0x17, + AC_JACK_LOC_HDMI, + AC_JACK_LOC_ATAPI, + /* others */ + AC_JACK_LOC_MOBILE_IN = 0x37, + AC_JACK_LOC_MOBILE_OUT, +}; + +/* Port connectivity (0-3) */ +enum +{ + AC_JACK_PORT_COMPLEX, + AC_JACK_PORT_NONE, + AC_JACK_PORT_FIXED, + AC_JACK_PORT_BOTH, +}; + +/* max. connections to a widget */ +#define HDA_MAX_CONNECTIONS 32 + +/* max. codec address */ +#define HDA_MAX_CODEC_ADDRESS 0x0f + +/* max number of PCM devics per card */ +#define HDA_MAX_PCMS 10 + +#endif /* __INTEL_HDA_DEFS_H__ */ diff --git a/components/drivers/audio/intel-hda/hda.c b/components/drivers/audio/intel-hda/hda.c new file mode 100644 index 000000000000..01a4a1b17f72 --- /dev/null +++ b/components/drivers/audio/intel-hda/hda.c @@ -0,0 +1,296 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-10-24 GuEe-GUI first version + */ + +#include +#include + +#include + +#define DBG_TAG "audio.hda" +#define DBG_LVL DBG_INFO +#include + +#include "hda-defs.h" + +#define ICH6_REG_BAR 0 + +#define CORB_ALIGNED 128 + +struct hda_bdl_entry +{ + rt_uint32_t paddr; + rt_uint32_t length; + rt_uint32_t flags; +} rt_packed; + +struct hda_output +{ + rt_uint8_t codec; + rt_uint16_t node_id; + + rt_uint32_t sample_rate; + int amp_gain_steps; + int num_channels; +}; + +struct intel_hda +{ + struct rt_audio_device parent; + + void *ich6_reg_base; + struct rt_pci_device *pdev; + + int stream; + + struct hda_output output; + + void *ring_buffer; + rt_uint32_t *corb; /* command outbound ring buffer */ + rt_uint64_t *rirb; /* response inbound ring buffer */ + struct hda_bdl_entry *bdl; /* buffer descriptor list */ + rt_uint64_t *dma_pos; /* dma position in current buffer */ + + rt_size_t corb_entries; /* number of CORB entries */ + rt_size_t rirb_entries; /* number of RIRB entries */ + rt_uint16_t rirb_read_pointer; /* RIRB read pointer */ + + rt_uint32_t *buffer; + rt_uint32_t buffers_completed; +}; +#define raw_to_intel_hda(raw) rt_container_of(raw, struct intel_hda, parent) + +#define intel_hda_writel(hda, reg, val) HWREG32((hda)->ich6_reg_base + ICH6_REG_##reg) = (val) +#define intel_hda_writew(hda, reg, val) HWREG16((hda)->ich6_reg_base + ICH6_REG_##reg) = (val) +#define intel_hda_writeb(hda, reg, val) HWREG8((hda)->ich6_reg_base + ICH6_REG_##reg) = (val) + +#define intel_hda_readl(hda, reg) HWREG32((hda)->ich6_reg_base + ICH6_REG_##reg) +#define intel_hda_readw(hda, reg) HWREG16((hda)->ich6_reg_base + ICH6_REG_##reg) +#define intel_hda_readb(hda, reg) HWREG8((hda)->ich6_reg_base + ICH6_REG_##reg) + +static rt_err_t intel_hda_audio_getcaps(struct rt_audio_device *audio, + struct rt_audio_caps *caps) +{ + return RT_EOK; +} + +static rt_err_t intel_hda_audio_configure(struct rt_audio_device *audio, + struct rt_audio_caps *caps) +{ + return RT_EOK; +} + +static rt_err_t intel_hda_audio_init(struct rt_audio_device *audio) +{ + return RT_EOK; +} + +static rt_err_t intel_hda_audio_start(struct rt_audio_device *audio, int stream) +{ + return RT_EOK; +} + +static rt_err_t intel_hda_audio_stop(struct rt_audio_device *audio, int stream) +{ + return RT_EOK; +} + +static rt_ssize_t intel_hda_audio_transmit(struct rt_audio_device *audio, + const void *write_buf, void *read_buf, rt_size_t size) +{ + return RT_EOK; +} + +static void intel_hda_audio_buffer_info(struct rt_audio_device *audio, + struct rt_audio_buf_info *info) +{ +} + +const static struct rt_audio_ops intel_hda_audio_ops = +{ + .getcaps = intel_hda_audio_getcaps, + .configure = intel_hda_audio_configure, + .init = intel_hda_audio_init, + .start = intel_hda_audio_start, + .stop = intel_hda_audio_stop, + .transmit = intel_hda_audio_transmit, + .buffer_info = intel_hda_audio_buffer_info, +}; + +static void intel_hda_isr(int irqno, void *param) +{ + rt_uint8_t sts; + rt_uint32_t isr; + struct intel_hda *hda = param; + + isr = intel_hda_readl(hda, INTSTS); + sts = intel_hda_readb(hda, SDO0_CTLL); + + if (sts & SDO0_STS_BCIS) + { + if (hda->stream == AUDIO_STREAM_REPLAY) + { + rt_audio_tx_complete(&hda->parent); + + hda->buffers_completed++; + hda->buffers_completed %= BDL_SIZE; + } + else if (hda->stream == AUDIO_STREAM_RECORD) + { + // rt_audio_rx_done(); + } + else + { + LOG_E("Unknow stream = %d", hda->stream); + } + } + + /* Reset interrupt status registers */ + intel_hda_writel(hda, INTSTS, isr); + intel_hda_writeb(hda, SDO0_CTLL, sts); +} + +static void intel_hda_setup_corb(struct intel_hda *hda) +{ + +} + +static void intel_hda_setup_rirb(struct intel_hda *hda) +{ + +} + +static void intel_hda_reset(struct intel_hda *hda) +{ + /* Clear CORB/RIRB RUN bits before reset */ + intel_hda_writel(hda, CORBCTL, 0); + intel_hda_writel(hda, RIRBCTL, 0); + + while ((intel_hda_readl(hda, CORBCTL) & ICH6_CORBCTL_RUN) || + (intel_hda_readl(hda, RIRBCTL) & ICH6_RBCTL_DMA_EN)) + { + rt_hw_cpu_relax(); + } + + /* Reset the CRST bit and wait until hardware is in reset */ + intel_hda_writel(hda, GCTL, 0); + + while (intel_hda_readl(hda, GCTL) & ICH6_GCTL_RESET) + { + rt_hw_cpu_relax(); + } + + rt_hw_dmb(); + + /* Take the hardware out of reset */ + intel_hda_writel(hda, GCTL, ICH6_GCTL_RESET); + + while ((intel_hda_readl(hda, GCTL) & ICH6_GCTL_RESET) == 0) + { + rt_hw_cpu_relax(); + } + + /* Enable all interrupts */ + intel_hda_writew(hda, WAKEEN, 0xffff); + intel_hda_writel(hda, INTCTL, 0x800000ff); + + intel_hda_setup_corb(hda); + intel_hda_setup_rirb(hda); + + /* Need to wait 25 frames (521 us) before enumerating codecs */ + rt_thread_mdelay(1); +} + +static rt_err_t intel_hda_probe(struct rt_pci_device *pdev) +{ + rt_err_t err; + const char *audio_name; + char dev_name[RT_NAME_MAX]; + struct intel_hda *hda = rt_calloc(1, sizeof(*hda)); + + if (!hda) + { + return -RT_ENOMEM; + } + + hda->ich6_reg_base = rt_pci_iomap(pdev, ICH6_REG_BAR); + + if (!hda->ich6_reg_base) + { + err = -RT_EIO; + + goto _free_res; + } + + // hda->ring_buffer = rt_malloc_align(, CORB_ALIGNED); + + intel_hda_reset(hda); + + rt_dm_dev_set_name_auto(&hda->parent.parent, "audio"); + audio_name = rt_dm_dev_get_name(&hda->parent.parent); + + hda->parent.ops = (struct rt_audio_ops *)&intel_hda_audio_ops; + if ((err = rt_audio_register(&hda->parent, audio_name, RT_DEVICE_FLAG_RDWR, hda))) + { + goto _free_res; + } + + pdev->parent.user_data = hda; + + rt_snprintf(dev_name, sizeof(dev_name), "%s-hda", audio_name); + rt_hw_interrupt_install(pdev->irq, intel_hda_isr, hda, dev_name); + rt_pci_irq_unmask(pdev); + + LOG_I("Intel HD Audio Controller v%d.%d (ich%s)", + intel_hda_readb(hda, VMAJ), intel_hda_readb(hda, VMIN), pdev->id->data); + + return RT_EOK; + +_free_res: + if (hda->ich6_reg_base) + { + rt_iounmap(hda->ich6_reg_base); + } + + rt_free(hda); + + return err; +} + +static rt_err_t intel_hda_remove(struct rt_pci_device *pdev) +{ + struct intel_hda *hda = pdev->parent.user_data; + + rt_pci_irq_mask(pdev); + rt_pic_detach_irq(pdev->irq, hda); + + rt_device_unregister(&hda->parent.parent); + + rt_iounmap(hda->ich6_reg_base); + rt_free(hda); + + return RT_EOK; +} + +static struct rt_pci_device_id intel_hda_pci_ids[] = +{ + { RT_PCI_DEVICE_ID(PCI_VENDOR_ID_INTEL, 0x2668), .data = "6" }, + { RT_PCI_DEVICE_ID(PCI_VENDOR_ID_INTEL, 0x293e), .data = "9" }, + { /* sentinel */ } +}; + +static struct rt_pci_driver intel_hda_driver = +{ + .name = "intel-hda", + + .ids = intel_hda_pci_ids, + .probe = intel_hda_probe, + .remove = intel_hda_remove, +}; +RT_PCI_DRIVER_EXPORT(intel_hda_driver); diff --git a/components/drivers/audio/rockchip/Kconfig b/components/drivers/audio/rockchip/Kconfig new file mode 100644 index 000000000000..d92200cbcab2 --- /dev/null +++ b/components/drivers/audio/rockchip/Kconfig @@ -0,0 +1,24 @@ +menuconfig RT_AUDIO_ROCKCHIP + bool "Enable Rockchip Audio" + select RT_USING_OFW + default n + +config RT_AUDIO_ROCKCHIP_I2S + bool "Rockchip I2S Device Driver" + depends on RT_AUDIO_ROCKCHIP + default n + +config RT_AUDIO_ROCKCHIP_I2S_TDM + bool "Rockchip I2S/TDM Device Driver" + depends on RT_AUDIO_ROCKCHIP + default n + +config RT_AUDIO_ROCKCHIP_PDM + bool "Rockchip PDM Controller Driver" + depends on RT_AUDIO_ROCKCHIP + default n + +config RT_AUDIO_ROCKCHIP_SPDIF + bool "Rockchip SPDIF Controller Driver" + depends on RT_AUDIO_ROCKCHIP + default n diff --git a/components/drivers/block/Kconfig b/components/drivers/block/Kconfig new file mode 100644 index 000000000000..1a8090d89de7 --- /dev/null +++ b/components/drivers/block/Kconfig @@ -0,0 +1,8 @@ +menuconfig RT_USING_BLK + bool "Using Block device drivers" + depends on RT_USING_DM + default n + +if RT_USING_BLK +source "$RTT_DIR/components/drivers/block/partitions/Kconfig" +endif diff --git a/components/drivers/block/SConscript b/components/drivers/block/SConscript new file mode 100644 index 000000000000..df56d322d739 --- /dev/null +++ b/components/drivers/block/SConscript @@ -0,0 +1,23 @@ +from building import * + +group = [] +objs = [] + +if not GetDepend(['RT_USING_BLK']): + Return('group') + +cwd = GetCurrentDir() +list = os.listdir(cwd) +CPPPATH = [cwd + '/../include'] + +src = ['blk.c', 'blk_dm.c', 'blk_partition.c'] + +group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) + +for d in list: + path = os.path.join(cwd, d) + if os.path.isfile(os.path.join(path, 'SConscript')): + objs = objs + SConscript(os.path.join(d, 'SConscript')) +objs = objs + group + +Return('objs') diff --git a/components/drivers/block/blk.c b/components/drivers/block/blk.c new file mode 100644 index 000000000000..dcbb319ebe61 --- /dev/null +++ b/components/drivers/block/blk.c @@ -0,0 +1,341 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-02-25 GuEe-GUI the first version + */ + +#define DBG_TAG "rtdm.blk" +#define DBG_LVL DBG_INFO +#include + +#include "blk_dm.h" + +static void blk_remove_all(struct rt_blk_disk *disk) +{ + struct rt_blk_device *blk, *blk_next; + + /* Remove all partitions */ + rt_list_for_each_entry_safe(blk, blk_next, &disk->part_nodes, list) + { + disk_remove_blk_dev(blk, RT_TRUE); + } +} + +static rt_ssize_t blk_read(rt_device_t dev, rt_off_t sector, + void *buffer, rt_size_t sector_count) +{ + rt_ssize_t res; + struct rt_blk_disk *disk = to_blk_disk(dev); + + rt_sem_take(&disk->usr_lock, RT_WAITING_FOREVER); + + res = disk->ops->read(disk, sector, buffer, sector_count); + + rt_sem_release(&disk->usr_lock); + + return res; +} + +static rt_ssize_t blk_write(rt_device_t dev, rt_off_t sector, + const void *buffer, rt_size_t sector_count) +{ + rt_ssize_t res; + struct rt_blk_disk *disk = to_blk_disk(dev); + + if (rt_unlikely(disk->read_only)) + { + return -RT_ENOSYS; + } + + rt_sem_take(&disk->usr_lock, RT_WAITING_FOREVER); + + res = disk->ops->write(disk, sector, buffer, sector_count); + + rt_sem_release(&disk->usr_lock); + + return res; +} + +static rt_ssize_t blk_parallel_read(rt_device_t dev, rt_off_t sector, + void *buffer, rt_size_t sector_count) +{ + struct rt_blk_disk *disk = to_blk_disk(dev); + + return disk->ops->read(disk, sector, buffer, sector_count); +} + +static rt_ssize_t blk_parallel_write(rt_device_t dev, rt_off_t sector, + const void *buffer, rt_size_t sector_count) +{ + struct rt_blk_disk *disk = to_blk_disk(dev); + + if (rt_unlikely(disk->read_only)) + { + return -RT_ENOSYS; + } + + return disk->ops->write(disk, sector, buffer, sector_count); +} + +static rt_err_t blk_control(rt_device_t dev, int cmd, void *args) +{ + rt_err_t err; + struct rt_blk_disk *disk = to_blk_disk(dev); + + switch (cmd) + { + case RT_DEVICE_CTRL_BLK_GETGEOME: + if (rt_unlikely(!args)) + { + err = -RT_EINVAL; + break; + } + + err = disk->ops->getgeome(disk, args); + break; + + case RT_DEVICE_CTRL_BLK_SYNC: + if (disk->ops->sync) + { + rt_sem_take(&disk->usr_lock, RT_WAITING_FOREVER); + + spin_lock(&disk->lock); + + err = disk->ops->sync(disk); + + spin_unlock(&disk->lock); + + rt_sem_release(&disk->usr_lock); + } + else + { + err = -RT_ENOSYS; + } + break; + + case RT_DEVICE_CTRL_BLK_ERASE: + if (disk->ops->erase) + { + rt_sem_take(&disk->usr_lock, RT_WAITING_FOREVER); + + spin_lock(&disk->lock); + + if (disk->parent.ref_count != 1) + { + err = -RT_EBUSY; + goto _unlock; + } + + blk_remove_all(disk); + + err = disk->ops->erase(disk); + + _unlock: + spin_unlock(&disk->lock); + + rt_sem_release(&disk->usr_lock); + } + else + { + err = -RT_ENOSYS; + } + break; + + case RT_DEVICE_CTRL_BLK_AUTOREFRESH: + if (disk->ops->autorefresh) + { + err = disk->ops->autorefresh(disk, !!args); + } + else + { + err = -RT_ENOSYS; + } + break; + + default: + err = -RT_EINVAL; + break; + } + + return err; +} + +#ifdef RT_USING_DEVICE_OPS +const static struct rt_device_ops blk_ops = +{ + .read = blk_read, + .write = blk_write, + .control = blk_control, +}; + +const static struct rt_device_ops blk_parallel_ops = +{ + .read = blk_parallel_read, + .write = blk_parallel_write, + .control = blk_control, +}; +#endif + +rt_err_t rt_hw_blk_disk_register(struct rt_blk_disk *disk) +{ + rt_err_t err; + const char *disk_name; + rt_uint16_t flags = RT_DEVICE_FLAG_RDONLY; + + if (!disk || !disk->ops) + { + return -RT_EINVAL; + } + +#if RT_NAME_MAX > 0 + if (disk->parent.parent.name[0] == '\0') +#else + if (disk->parent.parent.name) +#endif + { + return -RT_EINVAL; + } + + disk_name = to_disk_name(disk); + + err = rt_sem_init(&disk->usr_lock, disk_name, 1, RT_IPC_FLAG_PRIO); + + if (err) + { + LOG_E("%s: Init user mutex error = %s", rt_strerror(err)); + + return err; + } + + rt_list_init(&disk->part_nodes); + rt_spin_lock_init(&disk->lock); + + disk->parent.type = RT_Device_Class_Block; +#ifdef RT_USING_DEVICE_OPS + if (disk->parallel_io) + { + disk->parent.ops = &blk_parallel_ops; + } + else + { + disk->parent.ops = &blk_ops; + } +#else + if (disk->parallel_io) + { + disk->parent.read = blk_parallel_read; + disk->parent.write = blk_parallel_write; + } + else + { + disk->parent.read = blk_read; + disk->parent.write = blk_write; + } + disk->parent.control = blk_control; +#endif + + if (!disk->ops->write) + { + disk->read_only = RT_TRUE; + } + + if (!disk->read_only) + { + flags |= RT_DEVICE_FLAG_WRONLY; + } + + err = rt_device_register(&disk->parent, disk_name, flags); + + if (err) + { + rt_sem_detach(&disk->usr_lock); + } + + return err; +} + +rt_err_t rt_hw_blk_disk_unregister(struct rt_blk_disk *disk) +{ + rt_err_t err; + + if (!disk) + { + return -RT_EINVAL; + } + + spin_lock(&disk->lock); + + if (disk->parent.ref_count != 1) + { + err = -RT_EBUSY; + goto _unlock; + } + + /* Flush all data */ + if (disk->ops->sync) + { + err = disk->ops->sync(disk); + + if (err) + { + LOG_E("%s: Sync error = %s", to_disk_name(disk), rt_strerror(err)); + + goto _unlock; + } + } + + rt_sem_detach(&disk->usr_lock); + + blk_remove_all(disk); + + err = rt_device_unregister(&disk->parent); + +_unlock: + spin_unlock(&disk->lock); + + return err; +} + +rt_ssize_t rt_blk_disk_get_capacity(struct rt_blk_disk *disk) +{ + rt_ssize_t res; + struct rt_device_blk_geometry geometry; + + if (!disk) + { + return -RT_EINVAL; + } + + res = disk->ops->getgeome(disk, &geometry); + + if (!res) + { + return geometry.sector_count; + } + + return res; +} + +rt_ssize_t rt_blk_disk_get_logical_block_size(struct rt_blk_disk *disk) +{ + rt_ssize_t res; + struct rt_device_blk_geometry geometry; + + if (!disk) + { + return -RT_EINVAL; + } + + res = disk->ops->getgeome(disk, &geometry); + + if (!res) + { + return geometry.bytes_per_sector; + } + + return res; +} diff --git a/components/drivers/block/blk_dm.c b/components/drivers/block/blk_dm.c new file mode 100644 index 000000000000..17489f9a518c --- /dev/null +++ b/components/drivers/block/blk_dm.c @@ -0,0 +1,236 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-02-25 GuEe-GUI first version + */ + +#include "blk_dm.h" + +static rt_err_t blk_dev_open(rt_device_t dev, rt_uint16_t oflag) +{ + struct rt_blk_device *blk = to_blk(dev); + + return rt_device_open(&blk->disk->parent, oflag); +} + +static rt_err_t blk_dev_close(rt_device_t dev) +{ + struct rt_blk_device *blk = to_blk(dev); + + return rt_device_close(&blk->disk->parent); +} + +static rt_ssize_t blk_dev_read(rt_device_t dev, rt_off_t sector, + void *buffer, rt_size_t sector_count) +{ + struct rt_blk_device *blk = to_blk(dev); + + if (rt_unlikely(sector > blk->sector_start + blk->sector_count)) + { + return -RT_EINVAL; + } + + if (rt_unlikely(sector_count > blk->sector_count)) + { + return -RT_EINVAL; + } + + return rt_device_read(&blk->disk->parent, + blk->sector_start + sector, buffer, sector_count); +} + +static rt_ssize_t blk_dev_write(rt_device_t dev, rt_off_t sector, + const void *buffer, rt_size_t sector_count) +{ + struct rt_blk_device *blk = to_blk(dev); + + if (rt_unlikely(sector > blk->sector_start + blk->sector_count)) + { + return -RT_EINVAL; + } + + if (rt_unlikely(sector_count > blk->sector_count)) + { + return -RT_EINVAL; + } + + return rt_device_write(&blk->disk->parent, + blk->sector_start + sector, buffer, sector_count); +} + +static rt_err_t blk_dev_control(rt_device_t dev, int cmd, void *args) +{ + rt_err_t err; + struct rt_blk_device *blk = to_blk(dev); + struct rt_device_blk_geometry disk_geometry, *geometry; + + switch (cmd) + { + case RT_DEVICE_CTRL_BLK_GETGEOME: + if (rt_unlikely(!(geometry = args))) + { + err = -RT_EINVAL; + break; + } + + err = blk->disk->ops->getgeome(blk->disk, &disk_geometry); + + if (rt_unlikely(err)) + { + break; + } + + geometry->bytes_per_sector = disk_geometry.bytes_per_sector; + geometry->block_size = disk_geometry.block_size; + geometry->sector_count = blk->sector_count; + break; + + case RT_DEVICE_CTRL_BLK_SYNC: + rt_device_control(&blk->disk->parent, cmd, args); + break; + + case RT_DEVICE_CTRL_BLK_ERASE: + case RT_DEVICE_CTRL_BLK_AUTOREFRESH: + if (blk->disk->partitions <= 1) + { + rt_device_control(&blk->disk->parent, cmd, args); + } + else + { + err = -RT_EIO; + } + break; + + case RT_DEVICE_CTRL_BLK_PARTITION: + if (rt_unlikely(!args)) + { + err = -RT_EINVAL; + break; + } + + rt_memcpy(args, &blk->partition, sizeof(blk->partition)); + break; + + default: + err = -RT_EINVAL; + break; + } + + return err; +} + +#ifdef RT_USING_DEVICE_OPS +const static struct rt_device_ops blk_dev_ops = +{ + .open = blk_dev_open, + .close = blk_dev_close, + .read = blk_dev_read, + .write = blk_dev_write, + .control = blk_dev_control, +}; +#endif + +rt_err_t blk_dev_initialize(struct rt_blk_device *blk) +{ + struct rt_device *dev; + + if (!blk) + { + return -RT_EINVAL; + } + + dev = &blk->parent; + dev->type = RT_Device_Class_Block; +#ifdef RT_USING_DEVICE_OPS + dev->ops = &blk_dev_ops; +#else + dev->open = blk_dev_open; + dev->close = blk_dev_close; + dev->read = blk_dev_read; + dev->write = blk_dev_write; + dev->control = blk_dev_control; +#endif + + return RT_EOK; +} + +rt_err_t disk_add_blk_dev(struct rt_blk_disk *disk, struct rt_blk_device *blk) +{ + rt_err_t err; + const char *disk_name, *name_fmt; + + if (!disk || !blk) + { + return -RT_EINVAL; + } + + blk->disk = disk; + rt_list_init(&blk->list); + + disk_name = to_disk_name(disk); + + /* End is [a-zA-Z] or [0-9] */ + if (disk_name[rt_strlen(disk_name) - 1] < 'a') + { + name_fmt = "%sp%d"; + } + else + { + name_fmt = "%s%d"; + } + + rt_dm_dev_set_name(&blk->parent, name_fmt, disk_name, blk->partno); + + err = rt_device_register(&blk->parent, to_blk_name(blk), + disk->parent.flag & RT_DEVICE_FLAG_RDWR); + + if (err) + { + return err; + } + + spin_lock(&disk->lock); + + rt_list_insert_before(&disk->part_nodes, &blk->list); + + spin_unlock(&disk->lock); + + return RT_EOK; +} + +rt_err_t disk_remove_blk_dev(struct rt_blk_device *blk, rt_bool_t lockless) +{ + struct rt_blk_disk *disk; + + if (!blk) + { + return -RT_EINVAL; + } + + disk = blk->disk; + + if (!disk) + { + return -RT_EINVAL; + } + + rt_device_unregister(&blk->parent); + + if (!lockless) + { + spin_lock(&disk->lock); + } + + rt_list_remove(&blk->list); + + if (!lockless) + { + spin_unlock(&disk->lock); + } + + return RT_EOK; +} diff --git a/components/drivers/block/blk_dm.h b/components/drivers/block/blk_dm.h new file mode 100644 index 000000000000..f0b6ebec409b --- /dev/null +++ b/components/drivers/block/blk_dm.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-02-25 GuEe-GUI first version + */ + +#ifndef __BLK_DM_H__ +#define __BLK_DM_H__ + +#include +#include +#include + +#define to_blk_disk(dev) rt_container_of(dev, struct rt_blk_disk, parent) +#define to_blk(dev) rt_container_of(dev, struct rt_blk_device, parent) + +#define to_disk_name(disk) rt_dm_dev_get_name(&disk->parent) +#define to_blk_name(blk) rt_dm_dev_get_name(&blk->parent) + +rt_inline void spin_lock(struct rt_spinlock *spinlock) +{ + rt_hw_spin_lock(&spinlock->lock); +} + +rt_inline void spin_unlock(struct rt_spinlock *spinlock) +{ + rt_hw_spin_unlock(&spinlock->lock); +} + +rt_err_t blk_dev_initialize(struct rt_blk_device *blk); +rt_err_t disk_add_blk_dev(struct rt_blk_disk *disk, struct rt_blk_device *blk); +rt_err_t disk_remove_blk_dev(struct rt_blk_device *blk, rt_bool_t lockless); + +#endif /* __BLK_DM_H__ */ diff --git a/components/drivers/block/blk_partition.c b/components/drivers/block/blk_partition.c new file mode 100644 index 000000000000..411ee45fec87 --- /dev/null +++ b/components/drivers/block/blk_partition.c @@ -0,0 +1,177 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-02-25 GuEe-GUI the first version + */ + +#define DBG_TAG "blk.part" +#define DBG_LVL DBG_INFO +#include + +#include "blk_partition.h" + +static rt_err_t (*partition_list[])(struct rt_blk_disk *) = +{ +#ifdef RT_BLK_PARTITION_EFI + efi_partition, +#endif +#ifdef RT_BLK_PARTITION_DFS + dfs_partition, +#endif +}; + +rt_err_t blk_put_partition(struct rt_blk_disk *disk, const char *type, + rt_size_t start, rt_size_t count, int partno) +{ + rt_err_t err; + + struct rt_blk_device *blk = rt_calloc(1, sizeof(*blk)); + + if (type && rt_strcmp(type, "dfs")) + { + rt_uint32_t ssz = rt_blk_disk_get_logical_block_size(disk); + + rt_kprintf("found part[%d], begin: %d, size: ", partno, start * ssz); + + if ((count >> 11) == 0) + { + rt_kprintf("%d%cB\n", count >> 1, 'K'); /* KB */ + } + else + { + rt_uint32_t size_mb = count >> 11; /* MB */ + + if ((size_mb >> 10) == 0) + { + rt_kprintf("%d.%d%cB\n", size_mb, (count >> 1) & 0x3ff, 'M'); + } + else + { + rt_kprintf("%d.%d%cB\n", size_mb >> 10, size_mb & 0x3ff, 'G'); + } + } + } + + if (!blk) + { + err = -RT_ENOMEM; + goto _fail; + } + + err = blk_dev_initialize(blk); + + if (err) + { + goto _fail; + } + + blk->partno = partno; + blk->sector_start = start; + blk->sector_count = count; + + blk->partition.offset = start; + blk->partition.size = count; + blk->partition.lock = &disk->usr_lock; + + err = disk_add_blk_dev(disk, blk); + + if (err) + { + goto _fail; + } + + return RT_EOK; + +_fail: + LOG_E("%s: Put partition[%d] start = %d count = %d error = %s", + to_disk_name(disk), partno, start, count, rt_strerror(err)); + + if (blk) + { + rt_free(blk); + } + + return err; +} + +rt_err_t rt_blk_disk_probe_partition(struct rt_blk_disk *disk) +{ + rt_err_t err = RT_EOK; + + if (!disk) + { + return -RT_EINVAL; + } + + if (disk->partitions) + { + return err; + } + + err = -RT_EEMPTY; + + if (disk->max_partitions == RT_BLK_PARTITION_NONE) + { + LOG_D("%s: Unsupported partitions", to_disk_name(disk)); + + return err; + } + + for (int i = 0; i < RT_ARRAY_SIZE(partition_list); ++i) + { + rt_err_t part_err = partition_list[i](disk); + + if (part_err == -RT_ENOMEM) + { + err = part_err; + break; + } + + if (!part_err) + { + err = RT_EOK; + break; + } + } + + if (err && err != -RT_ENOMEM) + { + /* No partition found */ + rt_size_t total_sectors = rt_blk_disk_get_capacity(disk); + + err = blk_put_partition(disk, RT_NULL, 0, total_sectors, 0); + } + + return err; +} + +static int blk_disk_probe_partition(void) +{ + struct rt_object *obj; + struct rt_device *dev; + struct rt_blk_disk *disk; + struct rt_object_information *info = rt_object_get_information(RT_Object_Class_Device); + + rt_list_for_each_entry(obj, &info->object_list, list) + { + dev = rt_container_of(obj, struct rt_device, parent); + + if (dev->type != RT_Device_Class_Block) + { + continue; + } + + disk = to_blk_disk(dev); + + LOG_D("%s: Probing disk partitions", to_disk_name(disk)); + + rt_blk_disk_probe_partition(disk); + } + + return 0; +} +INIT_DRIVER_LATER_EXPORT(blk_disk_probe_partition); diff --git a/components/drivers/block/blk_partition.h b/components/drivers/block/blk_partition.h new file mode 100644 index 000000000000..be5d00282239 --- /dev/null +++ b/components/drivers/block/blk_partition.h @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-02-25 GuEe-GUI first version + */ + +#ifndef __BLK_PARTITION_H__ +#define __BLK_PARTITION_H__ + +#include "blk_dm.h" + +rt_err_t blk_put_partition(struct rt_blk_disk *disk, const char *type, + rt_size_t start, rt_size_t count, int partno); + +rt_err_t dfs_partition(struct rt_blk_disk *disk); +rt_err_t efi_partition(struct rt_blk_disk *disk); + +#endif /* __BLK_PARTITION_H__ */ diff --git a/components/drivers/block/partitions/Kconfig b/components/drivers/block/partitions/Kconfig new file mode 100644 index 000000000000..e216bd7b58c1 --- /dev/null +++ b/components/drivers/block/partitions/Kconfig @@ -0,0 +1,11 @@ +menu "Partition Types" + +config RT_BLK_PARTITION_DFS + bool "DFS Partition support" + default y + +config RT_BLK_PARTITION_EFI + bool "EFI Globally Unique Identifier (GUID) Partition support" + default y + +endmenu diff --git a/components/drivers/block/partitions/SConscript b/components/drivers/block/partitions/SConscript new file mode 100644 index 000000000000..06b320b18d5e --- /dev/null +++ b/components/drivers/block/partitions/SConscript @@ -0,0 +1,18 @@ +from building import * + +group = [] + +cwd = GetCurrentDir() +CPPPATH = [cwd + '/../../include'] + +src = [] + +if GetDepend(['RT_BLK_PARTITION_DFS']): + src += ['dfs.c'] + +if GetDepend(['RT_BLK_PARTITION_EFI']): + src += ['efi.c'] + +group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') diff --git a/components/drivers/block/partitions/dfs.c b/components/drivers/block/partitions/dfs.c new file mode 100644 index 000000000000..47aa3437d518 --- /dev/null +++ b/components/drivers/block/partitions/dfs.c @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2011-07-25 weety first version + * 2023-02-25 GuEe-GUI make blk interface + */ + +#include "efi.h" + +#define DBG_TAG "blk.part.dfs" +#define DBG_LVL DBG_INFO +#include + +rt_err_t dfs_partition(struct rt_blk_disk *disk) +{ + rt_ssize_t res; + struct dfs_partition part; + rt_uint8_t *sector = rt_malloc(SECTOR_SIZE); + + if (!sector) + { + return -RT_ENOMEM; + } + + res = disk->ops->read(disk, 0, sector, 1); + + if (res < 0) + { + return res; + } + + for (rt_size_t i = 0; i < disk->max_partitions; ++i) + { + res = dfs_filesystem_get_partition(&part, sector, i); + + if (res) + { + break; + } + + if (blk_put_partition(disk, "dfs", part.offset, part.size, i) == -RT_ENOMEM) + { + break; + } + } + + return RT_EOK; +} diff --git a/components/drivers/block/partitions/efi.c b/components/drivers/block/partitions/efi.c new file mode 100644 index 000000000000..af71f1ce4432 --- /dev/null +++ b/components/drivers/block/partitions/efi.c @@ -0,0 +1,738 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-05-05 linzhenxing first version + * 2023-02-25 GuEe-GUI make blk interface + */ + +#include "efi.h" + +#define DBG_TAG "blk.part.efi" +#define DBG_LVL DBG_LOG +#include + +static rt_bool_t force_gpt = 0; + +static int force_gpt_setup(void) +{ +#ifdef RT_USING_OFW + force_gpt = !!rt_ofw_bootargs_select("gpt", 0); +#endif + + return 0; +} +INIT_FRAMEWORK_EXPORT(force_gpt_setup); + +/** + * @brief This function is EFI version of crc32 function. + * + * @param buf the buffer to calculate crc32 of. + * @param len the length of buf. + * @return EFI-style CRC32 value for @buf. + */ +rt_inline rt_uint32_t efi_crc32(const rt_uint8_t *buf, rt_size_t len) +{ + rt_ubase_t crc = 0xffffffffUL; + + for (rt_size_t i = 0; i < len; ++i) + { + crc ^= buf[i]; + + for (int j = 0; j < 8; ++j) + { + crc = (crc >> 1) ^ ((crc & 1) ? 0xedb88320L : 0); + } + } + + return ~crc; +} + +/** + * @brief This function get number of last logical block of device. + * + * @param disk the blk of disk. + * @return last LBA value on success, 0 on error. + * This is stored (by sd and ide-geometry) in + * the part[0] entry for this disk, and is the number of + * physical sectors available on the disk. + */ +static rt_size_t last_lba(struct rt_blk_disk *disk) +{ + return rt_blk_disk_get_capacity(disk) - 1ULL; +} + +rt_inline int pmbr_part_valid(gpt_mbr_record *part) +{ + if (part->os_type != EFI_PMBR_OSTYPE_EFI_GPT) + { + return 0; + } + + /* set to 0x00000001 (i.e., the LBA of the GPT Partition Header) */ + if (rt_le32_to_cpu(part->starting_lba) != GPT_PRIMARY_PARTITION_TABLE_LBA) + { + return 0; + } + + return GPT_MBR_PROTECTIVE; +} + +/** + * @brief This function test Protective MBR for validity. + * + * @param mbr the pointer to a legacy mbr structure. + * @param total_sectors the amount of sectors in the device + * @return + * 0 -> Invalid MBR + * 1 -> GPT_MBR_PROTECTIVE + * 2 -> GPT_MBR_HYBRID + */ +static int is_pmbr_valid(legacy_mbr *mbr, rt_size_t total_sectors) +{ + rt_uint32_t sz = 0; + int part = 0, ret = 0; /* invalid by default */ + + if (!mbr || rt_le16_to_cpu(mbr->signature) != MSDOS_MBR_SIGNATURE) + { + goto _done; + } + + for (int i = 0; i < 4; ++i) + { + ret = pmbr_part_valid(&mbr->partition_record[i]); + + if (ret == GPT_MBR_PROTECTIVE) + { + part = i; + /* + * Ok, we at least know that there's a protective MBR, + * now check if there are other partition types for + * hybrid MBR. + */ + goto _check_hybrid; + } + } + + if (ret != GPT_MBR_PROTECTIVE) + { + goto _done; + } + +_check_hybrid: + for (int i = 0; i < 4; i++) + { + if (mbr->partition_record[i].os_type != EFI_PMBR_OSTYPE_EFI_GPT && + mbr->partition_record[i].os_type != 0x00) + { + ret = GPT_MBR_HYBRID; + } + } + + /* + * Protective MBRs take up the lesser of the whole disk + * or 2 TiB (32bit LBA), ignoring the rest of the disk. + * Some partitioning programs, nonetheless, choose to set + * the size to the maximum 32-bit limitation, disregarding + * the disk size. + * + * Hybrid MBRs do not necessarily comply with this. + * + * Consider a bad value here to be a warning to support dd'ing + * an image from a smaller disk to a larger disk. + */ + if (ret == GPT_MBR_PROTECTIVE) + { + sz = rt_le32_to_cpu(mbr->partition_record[part].size_in_lba); + + if (sz != (rt_uint32_t)total_sectors - 1 && sz != 0xffffffff) + { + LOG_W("GPT: mbr size in lba (%u) different than whole disk (%u)", + sz, rt_min_t(rt_uint32_t, total_sectors - 1, 0xffffffff)); + } + } + +_done: + return ret; +} + +/** + * @brief This function read bytes from disk, starting at given LBA. + * + * @param disk the blk of disk. + * @param lba the Logical Block Address of the partition table. + * @param buffer the destination buffer. + * @param count the bytes to read. + * @return number of bytes read on success, 0 on error. + */ +static rt_size_t read_lba(struct rt_blk_disk *disk, + rt_uint64_t lba, rt_uint8_t *buffer, rt_size_t count) +{ + rt_size_t totalreadcount = 0; + + if (!buffer || lba > last_lba(disk)) + { + return 0; + } + + for (rt_uint64_t n = lba; count; ++n) + { + int copied = 512; + + disk->ops->read(disk, n, buffer, 1); + + if (copied > count) + { + copied = count; + } + + buffer += copied; + totalreadcount += copied; + count -= copied; + } + + return totalreadcount; +} + +/** + * @brief This function reads partition entries from disk. + * + * @param disk the blk of disk. + * @param gpt the GPT header + * @return ptes on success, null on error. + */ +static gpt_entry *alloc_read_gpt_entries(struct rt_blk_disk *disk, + gpt_header *gpt) +{ + rt_size_t count; + gpt_entry *pte; + rt_uint64_t entry_lba; + + if (!gpt) + { + return RT_NULL; + } + + count = (rt_size_t)rt_le32_to_cpu(gpt->num_partition_entries) * + rt_le32_to_cpu(gpt->sizeof_partition_entry); + + if (!count) + { + return RT_NULL; + } + + pte = rt_malloc(count); + + if (!pte) + { + return RT_NULL; + } + + entry_lba = rt_le64_to_cpu(gpt->partition_entry_lba); + + if (read_lba(disk, entry_lba, (rt_uint8_t *)pte, count) < count) + { + rt_free(pte); + pte = RT_NULL; + + return RT_NULL; + } + + /* Remember to free pte when done */ + return pte; +} + +/** + * @brief This function allocates GPT header, reads into it from disk. + * + * @param disk the blk of disk. + * @param lba the Logical Block Address of the partition table + * @return GPT header on success, null on error. + */ +static gpt_header *alloc_read_gpt_header(struct rt_blk_disk *disk, rt_uint64_t lba) +{ + gpt_header *gpt; + rt_uint32_t ssz = rt_blk_disk_get_logical_block_size(disk); + + gpt = rt_malloc(ssz); + + if (!gpt) + { + return RT_NULL; + } + + if (read_lba(disk, lba, (rt_uint8_t *)gpt, ssz) < ssz) + { + rt_free(gpt); + gpt = RT_NULL; + + return RT_NULL; + } + + /* Remember to free gpt when finished with it */ + return gpt; +} + +/** + * @brief This function tests one GPT header and PTEs for validity. + * + * @param disk the blk of disk. + * @param lba the Logical Block Address of the GPT header to test. + * @param gpt the GPT header ptr, filled on return. + * @param ptes the PTEs ptr, filled on return. + * @returns true if valid, false on error. + * If valid, returns pointers to newly allocated GPT header and PTEs. + */ +static rt_bool_t is_gpt_valid(struct rt_blk_disk *disk, + rt_uint64_t lba, gpt_header **gpt, gpt_entry **ptes) +{ + rt_uint32_t crc, origcrc; + rt_uint64_t lastlba, pt_size; + rt_ssize_t logical_block_size; + + if (!ptes) + { + return RT_FALSE; + } + + if (!(*gpt = alloc_read_gpt_header(disk, lba))) + { + return RT_FALSE; + } + + /* Check the GUID Partition Table signature */ + if (rt_le64_to_cpu((*gpt)->signature) != GPT_HEADER_SIGNATURE) + { + LOG_D("%s: GUID Partition Table Header signature is wrong: %lld != %lld", + to_disk_name(disk), + (rt_uint64_t)rt_le64_to_cpu((*gpt)->signature), + (rt_uint64_t)GPT_HEADER_SIGNATURE); + + goto _fail; + } + + /* Check the GUID Partition Table header size is too big */ + logical_block_size = rt_blk_disk_get_logical_block_size(disk); + + if (rt_le32_to_cpu((*gpt)->header_size) > logical_block_size) + { + LOG_D("%s: GUID Partition Table Header size is too large: %u > %u", + to_disk_name(disk), + rt_le32_to_cpu((*gpt)->header_size), + logical_block_size); + + goto _fail; + } + + /* Check the GUID Partition Table header size is too small */ + if (rt_le32_to_cpu((*gpt)->header_size) < sizeof(gpt_header)) + { + LOG_D("%s: GUID Partition Table Header size is too small: %u < %u", + to_disk_name(disk), + rt_le32_to_cpu((*gpt)->header_size), + sizeof(gpt_header)); + + goto _fail; + } + + /* Check the GUID Partition Table CRC */ + origcrc = rt_le32_to_cpu((*gpt)->header_crc32); + (*gpt)->header_crc32 = 0; + crc = efi_crc32((const rt_uint8_t *)(*gpt), rt_le32_to_cpu((*gpt)->header_size)); + + if (crc != origcrc) + { + LOG_D("%s: GUID Partition Table Header CRC is wrong: %x != %x", + to_disk_name(disk), crc, origcrc); + + goto _fail; + } + + (*gpt)->header_crc32 = rt_cpu_to_le32(origcrc); + + /* + * Check that the start_lba entry points to the LBA that contains + * the GUID Partition Table + */ + if (rt_le64_to_cpu((*gpt)->start_lba) != lba) + { + LOG_D("%s: GPT start_lba incorrect: %lld != %lld", + to_disk_name(disk), + (rt_uint64_t)rt_le64_to_cpu((*gpt)->start_lba), + (rt_uint64_t)lba); + + goto _fail; + } + + /* Check the first_usable_lba and last_usable_lba are within the disk */ + lastlba = last_lba(disk); + + if (rt_le64_to_cpu((*gpt)->first_usable_lba) > lastlba) + { + LOG_D("%s: GPT: first_usable_lba incorrect: %lld > %lld", + to_disk_name(disk), + (rt_uint64_t)rt_le64_to_cpu((*gpt)->first_usable_lba), + (rt_uint64_t)lastlba); + + goto _fail; + } + + if (rt_le64_to_cpu((*gpt)->last_usable_lba) > lastlba) + { + LOG_D("%s: GPT: last_usable_lba incorrect: %lld > %lld", + to_disk_name(disk), + (rt_uint64_t)rt_le64_to_cpu((*gpt)->last_usable_lba), + (rt_uint64_t)lastlba); + + goto _fail; + } + if (rt_le64_to_cpu((*gpt)->last_usable_lba) < rt_le64_to_cpu((*gpt)->first_usable_lba)) + { + LOG_D("%s: GPT: last_usable_lba incorrect: %lld > %lld", + to_disk_name(disk), + (rt_uint64_t)rt_le64_to_cpu((*gpt)->last_usable_lba), + (rt_uint64_t)rt_le64_to_cpu((*gpt)->first_usable_lba)); + + goto _fail; + } + + /* Check that sizeof_partition_entry has the correct value */ + if (rt_le32_to_cpu((*gpt)->sizeof_partition_entry) != sizeof(gpt_entry)) + { + LOG_D("%s: GUID Partition Entry Size check failed", to_disk_name(disk)); + + goto _fail; + } + + /* Sanity check partition table size */ + pt_size = (rt_uint64_t)rt_le32_to_cpu((*gpt)->num_partition_entries) * + rt_le32_to_cpu((*gpt)->sizeof_partition_entry); + + if (!(*ptes = alloc_read_gpt_entries(disk, *gpt))) + { + goto _fail; + } + + /* Check the GUID Partition Entry Array CRC */ + crc = efi_crc32((const rt_uint8_t *)(*ptes), pt_size); + + if (crc != rt_le32_to_cpu((*gpt)->partition_entry_array_crc32)) + { + LOG_D("%s: GUID Partition Entry Array CRC check failed", to_disk_name(disk)); + + goto _fail_ptes; + } + + /* We're done, all's well */ + return RT_TRUE; + +_fail_ptes: + rt_free(*ptes); + *ptes = RT_NULL; + +_fail: + rt_free(*gpt); + *gpt = RT_NULL; + + return RT_FALSE; +} + +/** + * @brief This function tests one PTE for validity. + * + * @param pte the pte to check. + * @param lastlba the last lba of the disk. + * @return valid boolean of pte. + */ +rt_inline rt_bool_t is_pte_valid(const gpt_entry *pte, const rt_size_t lastlba) +{ + if ((!efi_guidcmp(pte->partition_type_guid, NULL_GUID)) || + rt_le64_to_cpu(pte->starting_lba) > lastlba || + rt_le64_to_cpu(pte->ending_lba) > lastlba) + { + return RT_FALSE; + } + + return RT_TRUE; +} + +/** + * @brief This function search disk for valid GPT headers and PTEs. + * + * @param disk the blk of disk. + * @param pgpt the primary GPT header. + * @param agpt the alternate GPT header. + * @param lastlba the last LBA number. + */ +static void compare_gpts(struct rt_blk_disk *disk, + gpt_header *pgpt, gpt_header *agpt, rt_uint64_t lastlba) +{ + int error_found = 0; + + if (!pgpt || !agpt) + { + return; + } + + if (rt_le64_to_cpu(pgpt->start_lba) != rt_le64_to_cpu(agpt->alternate_lba)) + { + LOG_W("%s: GPT:Primary header LBA(%lld) != Alt(%lld), header alternate_lba", + to_disk_name(disk), + (rt_uint64_t)rt_le64_to_cpu(pgpt->start_lba), + (rt_uint64_t)rt_le64_to_cpu(agpt->alternate_lba)); + + ++error_found; + } + + if (rt_le64_to_cpu(pgpt->alternate_lba) != rt_le64_to_cpu(agpt->start_lba)) + { + LOG_W("%s: GPT:Primary header alternate_lba(%lld) != Alt(%lld), header start_lba", + to_disk_name(disk), + (rt_uint64_t)rt_le64_to_cpu(pgpt->alternate_lba), + (rt_uint64_t)rt_le64_to_cpu(agpt->start_lba)); + + ++error_found; + } + + if (rt_le64_to_cpu(pgpt->first_usable_lba) != rt_le64_to_cpu(agpt->first_usable_lba)) + { + LOG_W("%s: GPT:first_usable_lbas don't match %lld != %lld", + to_disk_name(disk), + (rt_uint64_t)rt_le64_to_cpu(pgpt->first_usable_lba), + (rt_uint64_t)rt_le64_to_cpu(agpt->first_usable_lba)); + + ++error_found; + } + + if (rt_le64_to_cpu(pgpt->last_usable_lba) != rt_le64_to_cpu(agpt->last_usable_lba)) + { + LOG_W("%s: GPT:last_usable_lbas don't match %lld != %lld", + to_disk_name(disk), + (rt_uint64_t)rt_le64_to_cpu(pgpt->last_usable_lba), + (rt_uint64_t)rt_le64_to_cpu(agpt->last_usable_lba)); + + ++error_found; + } + + if (efi_guidcmp(pgpt->disk_guid, agpt->disk_guid)) + { + LOG_W("%s: GPT:disk_guids don't match", to_disk_name(disk)); + + ++error_found; + } + + if (rt_le32_to_cpu(pgpt->num_partition_entries) != + rt_le32_to_cpu(agpt->num_partition_entries)) + { + LOG_W("%s: GPT:num_partition_entries don't match: 0x%x != 0x%x", + to_disk_name(disk), + rt_le32_to_cpu(pgpt->num_partition_entries), + rt_le32_to_cpu(agpt->num_partition_entries)); + + ++error_found; + } + + if (rt_le32_to_cpu(pgpt->sizeof_partition_entry) != + rt_le32_to_cpu(agpt->sizeof_partition_entry)) + { + LOG_W("%s: GPT:sizeof_partition_entry values don't match: 0x%x != 0x%x", + to_disk_name(disk), + rt_le32_to_cpu(pgpt->sizeof_partition_entry), + rt_le32_to_cpu(agpt->sizeof_partition_entry)); + + ++error_found; + } + + if (rt_le32_to_cpu(pgpt->partition_entry_array_crc32) != + rt_le32_to_cpu(agpt->partition_entry_array_crc32)) + { + LOG_W("%s: GPT:partition_entry_array_crc32 values don't match: 0x%x != 0x%x", + to_disk_name(disk), + rt_le32_to_cpu(pgpt->partition_entry_array_crc32), + rt_le32_to_cpu(agpt->partition_entry_array_crc32)); + + ++error_found; + } + + if (rt_le64_to_cpu(pgpt->alternate_lba) != lastlba) + { + LOG_W("%s: GPT:Primary header thinks Alt. header is not at the end of the disk: %lld != %lld", + to_disk_name(disk), + (rt_uint64_t)rt_le64_to_cpu(pgpt->alternate_lba), + (rt_uint64_t)lastlba); + + ++error_found; + } + + if (rt_le64_to_cpu(agpt->start_lba) != lastlba) + { + LOG_W("%s: GPT:Alternate GPT header not at the end of the disk: %lld != %lld", + to_disk_name(disk), + (rt_uint64_t)rt_le64_to_cpu(agpt->start_lba), + (rt_uint64_t)lastlba); + + ++error_found; + } + + if (error_found) + { + LOG_W("GPT: Use GNU Parted to correct GPT errors"); + } +} + +/** + * @brief This function search disk for valid GPT headers and PTEs. + * + * @param disk the disk parsed partitions. + * @param gpt the GPT header ptr, filled on return. + * @param ptes the PTEs ptr, filled on return. + * @return 1 if valid, 0 on error. + * If valid, returns pointers to newly allocated GPT header and PTEs. + * Validity depends on PMBR being valid (or being overridden by the + * 'gpt' kernel command line option) and finding either the Primary + * GPT header and PTEs valid, or the Alternate GPT header and PTEs + * valid. If the Primary GPT header is not valid, the Alternate GPT header + * is not checked unless the 'gpt' kernel command line option is passed. + * This protects against devices which misreport their size, and forces + * the user to decide to use the Alternate GPT. + */ +static rt_bool_t find_valid_gpt(struct rt_blk_disk *disk, + gpt_header **gpt, gpt_entry **ptes) +{ + int good_pgpt = 0, good_agpt = 0, good_pmbr = 0; + gpt_header *pgpt = RT_NULL, *agpt = RT_NULL; + gpt_entry *pptes = RT_NULL, *aptes = RT_NULL; + legacy_mbr *legacymbr; + rt_size_t total_sectors = rt_blk_disk_get_capacity(disk); + rt_size_t lastlba; + + if (!ptes) + { + return RT_FALSE; + } + + lastlba = last_lba(disk); + + if (!force_gpt) + { + /* This will be added to the EFI Spec. per Intel after v1.02. */ + legacymbr = rt_malloc(sizeof(*legacymbr)); + + if (!legacymbr) + { + return RT_FALSE; + } + + read_lba(disk, 0, (rt_uint8_t *)legacymbr, sizeof(*legacymbr)); + good_pmbr = is_pmbr_valid(legacymbr, total_sectors); + rt_free(legacymbr); + + if (!good_pmbr) + { + return RT_FALSE; + } + + LOG_D("%s: Device has a %s MBR", to_disk_name(disk), + good_pmbr == GPT_MBR_PROTECTIVE ? "protective" : "hybrid"); + } + + good_pgpt = is_gpt_valid(disk, GPT_PRIMARY_PARTITION_TABLE_LBA, &pgpt, &pptes); + + if (good_pgpt) + { + good_agpt = is_gpt_valid(disk, rt_le64_to_cpu(pgpt->alternate_lba), &agpt, &aptes); + } + + if (!good_agpt && force_gpt) + { + good_agpt = is_gpt_valid(disk, lastlba, &agpt, &aptes); + } + + /* The obviously unsuccessful case */ + if (!good_pgpt && !good_agpt) + { + goto _fail; + } + + compare_gpts(disk, pgpt, agpt, lastlba); + + /* The good cases */ + if (good_pgpt) + { + *gpt = pgpt; + *ptes = pptes; + rt_free(agpt); + rt_free(aptes); + + if (!good_agpt) + { + LOG_D("%s: Alternate GPT is invalid, using primary GPT", to_disk_name(disk)); + } + + return RT_TRUE; + } + else if (good_agpt) + { + *gpt = agpt; + *ptes = aptes; + rt_free(pgpt); + rt_free(pptes); + + LOG_D("%s: Primary GPT is invalid, using alternate GPT", to_disk_name(disk)); + + return RT_TRUE; + } + +_fail: + rt_free(pgpt); + rt_free(agpt); + rt_free(pptes); + rt_free(aptes); + + *gpt = RT_NULL; + *ptes = RT_NULL; + + return RT_FALSE; +} + +rt_err_t efi_partition(struct rt_blk_disk *disk) +{ + rt_uint32_t entries_nr; + gpt_header *gpt = RT_NULL; + gpt_entry *ptes = RT_NULL; + + if (!find_valid_gpt(disk, &gpt, &ptes) || !gpt || !ptes) + { + rt_free(gpt); + rt_free(ptes); + + return -RT_EINVAL; + } + + entries_nr = rt_le32_to_cpu(gpt->num_partition_entries); + + for (int i = 0; i < entries_nr && i < disk->max_partitions; ++i) + { + rt_uint64_t start = rt_le64_to_cpu(ptes[i].starting_lba); + rt_uint64_t size = rt_le64_to_cpu(ptes[i].ending_lba) - + rt_le64_to_cpu(ptes[i].starting_lba) + 1ULL; + + if (!is_pte_valid(&ptes[i], last_lba(disk))) + { + continue; + } + + if (blk_put_partition(disk, "gpt", start, size, i) == -RT_ENOMEM) + { + break; + } + } + + rt_free(gpt); + rt_free(ptes); + + return RT_EOK; +} diff --git a/components/drivers/block/partitions/efi.h b/components/drivers/block/partitions/efi.h new file mode 100644 index 000000000000..7bab9603989a --- /dev/null +++ b/components/drivers/block/partitions/efi.h @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-05-05 linzhenxing first version + * 2023-02-25 GuEe-GUI make blk interface + */ + +#ifndef __PARTITIONS_EFI_H__ +#define __PARTITIONS_EFI_H__ + +#include "../blk_partition.h" + +#define MSDOS_MBR_SIGNATURE 0xaa55 +#define EFI_PMBR_OSTYPE_EFI 0xef +#define EFI_PMBR_OSTYPE_EFI_GPT 0xee + +#define GPT_MBR_PROTECTIVE 1 +#define GPT_MBR_HYBRID 2 + +#define GPT_HEADER_SIGNATURE 0x5452415020494645ULL +#define GPT_HEADER_REVISION_V1 0x00010000 +#define GPT_PRIMARY_PARTITION_TABLE_LBA 1 + +#ifndef __UUID_H__ +#define UUID_SIZE 16 + +typedef struct +{ + rt_uint8_t b[UUID_SIZE]; +} guid_t; +#endif /* __UUID_H__ */ + +#ifndef __EFI_H__ +typedef guid_t efi_guid_t rt_align(4); + +#define EFI_GUID(a, b, c, d...) (efi_guid_t) \ +{{ \ + (a) & 0xff, ((a) >> 8) & 0xff, ((a) >> 16) & 0xff, ((a) >> 24) & 0xff, \ + (b) & 0xff, ((b) >> 8) & 0xff, \ + (c) & 0xff, ((c) >> 8) & 0xff, \ + d \ +}} + +#define NULL_GUID \ + EFI_GUID(0x00000000, 0x0000, 0x0000, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00) + +rt_inline int efi_guidcmp(efi_guid_t left, efi_guid_t right) +{ + return rt_memcmp(&left, &right, sizeof (efi_guid_t)); +} +#endif /* __EFI_H__ */ + +#define PARTITION_SYSTEM_GUID \ + EFI_GUID(0xc12a7328, 0xf81f, 0x11d2, 0xba, 0x4b, 0x00, 0xa0, 0xc9, 0x3e, 0xc9, 0x3b) + +#define LEGACY_MBR_PARTITION_GUID \ + EFI_GUID(0x024dee41, 0x33e7, 0x11d3, 0x9d, 0x69, 0x00, 0x08, 0xc7, 0x81, 0xf3, 0x9f) + +#define PARTITION_MSFT_RESERVED_GUID \ + EFI_GUID(0xe3c9e316, 0x0b5c, 0x4db8, 0x81, 0x7d, 0xf9, 0x2d, 0xf0, 0x02, 0x15, 0xae) + +#define PARTITION_BASIC_DATA_GUID \ + EFI_GUID(0xebd0a0a2, 0xb9e5, 0x4433, 0x87, 0xc0, 0x68, 0xb6, 0xb7, 0x26, 0x99, 0xc7) + +typedef struct _gpt_header +{ + rt_le64_t signature; + rt_le32_t revision; + rt_le32_t header_size; + rt_le32_t header_crc32; + rt_le32_t reserved1; + rt_le64_t start_lba; + rt_le64_t alternate_lba; + rt_le64_t first_usable_lba; + rt_le64_t last_usable_lba; + efi_guid_t disk_guid; + rt_le64_t partition_entry_lba; + rt_le32_t num_partition_entries; + rt_le32_t sizeof_partition_entry; + rt_le32_t partition_entry_array_crc32; + + /* + * The rest of the logical block is reserved by UEFI and must be zero. + * EFI standard handles this by: + * + * uint8_t reserved2[BlockSize - 92]; + */ +} rt_packed gpt_header; + +typedef struct _gpt_entry_attributes +{ + rt_uint64_t required_to_function:1; + rt_uint64_t reserved:47; + rt_uint64_t type_guid_specific:16; +} rt_packed gpt_entry_attributes; + +typedef struct _gpt_entry +{ + efi_guid_t partition_type_guid; + efi_guid_t unique_partition_guid; + rt_le64_t starting_lba; + rt_le64_t ending_lba; + gpt_entry_attributes attributes; + rt_le16_t partition_name[72/sizeof(rt_le16_t)]; +} rt_packed gpt_entry; + +typedef struct _gpt_mbr_record +{ + rt_uint8_t boot_indicator; /* unused by EFI, set to 0x80 for bootable */ + rt_uint8_t start_head; /* unused by EFI, pt start in CHS */ + rt_uint8_t start_sector; /* unused by EFI, pt start in CHS */ + rt_uint8_t start_track; + rt_uint8_t os_type; /* EFI and legacy non-EFI OS types */ + rt_uint8_t end_head; /* unused by EFI, pt end in CHS */ + rt_uint8_t end_sector; /* unused by EFI, pt end in CHS */ + rt_uint8_t end_track; /* unused by EFI, pt end in CHS */ + rt_le32_t starting_lba; /* used by EFI - start addr of the on disk pt */ + rt_le32_t size_in_lba; /* used by EFI - size of pt in LBA */ +} rt_packed gpt_mbr_record; + + +typedef struct _legacy_mbr +{ + rt_uint8_t boot_code[440]; + rt_le32_t unique_mbr_signature; + rt_le16_t unknown; + gpt_mbr_record partition_record[4]; + rt_le16_t signature; +} rt_packed legacy_mbr; + +#endif /* __PARTITIONS_EFI_H__ */ diff --git a/components/drivers/can/Kconfig b/components/drivers/can/Kconfig new file mode 100755 index 000000000000..20726985a9e6 --- /dev/null +++ b/components/drivers/can/Kconfig @@ -0,0 +1,20 @@ +menuconfig RT_USING_CAN + bool "Using CAN device drivers" + default n + +if RT_USING_CAN + config RT_CAN_USING_HDR + bool "Enable CAN hardware filter" + default n + config RT_CAN_USING_CANFD + bool "Enable CANFD support" + default n +endif + +config RT_CAN_CANFD_ROCKCHIP + bool "Rockchip CANFD controller" + depends on RT_USING_DM + depends on RT_USING_CAN + depends on RT_CAN_USING_CANFD + select RT_USING_RESET + default n diff --git a/components/drivers/can/SConscript b/components/drivers/can/SConscript index 84ae2a10daaa..e91ab216a42a 100644 --- a/components/drivers/can/SConscript +++ b/components/drivers/can/SConscript @@ -1,8 +1,15 @@ from building import * cwd = GetCurrentDir() -src = Glob('*.c') +src = ['can.c'] CPPPATH = [cwd + '/../include'] + +if GetDepend(['RT_USING_DM']): + src += ['can_dm.c'] + + if GetDepend(['RT_CAN_CANFD_ROCKCHIP']): + src += ['canfd-rockchip.c'] + group = DefineGroup('DeviceDrivers', src, depend = ['RT_USING_CAN'], CPPPATH = CPPPATH) Return('group') diff --git a/components/drivers/can/can.c b/components/drivers/can/can.c index 63b738b2c525..55f57c1e4ff5 100644 --- a/components/drivers/can/can.c +++ b/components/drivers/can/can.c @@ -152,7 +152,7 @@ rt_inline int _can_int_tx(struct rt_can_device *can, const struct rt_can_msg *da rt_list_remove(&tx_tosnd->list); rt_hw_interrupt_enable(level); - no = ((rt_uint32_t)tx_tosnd - (rt_uint32_t)tx_fifo->buffer) / sizeof(struct rt_can_sndbxinx_list); + no = ((rt_ubase_t)tx_tosnd - (rt_ubase_t)tx_fifo->buffer) / sizeof(struct rt_can_sndbxinx_list); tx_tosnd->result = RT_CAN_SND_RESULT_WAIT; if (can->ops->sendmsg(can, data, no) != RT_EOK) { @@ -514,7 +514,7 @@ static rt_err_t rt_can_control(struct rt_device *dev, case RT_CAN_CMD_SET_PRIV: /* configure device */ - if ((rt_uint32_t)args != can->config.privmode) + if ((rt_ubase_t)args != can->config.privmode) { int i; rt_base_t level; diff --git a/components/drivers/can/can_dm.c b/components/drivers/can/can_dm.c new file mode 100644 index 000000000000..a61cf8e6aa74 --- /dev/null +++ b/components/drivers/can/can_dm.c @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-11-26 GuEe-GUI first version + */ + +#include "can_dm.h" + +static const rt_uint8_t dlc2len[] = +{ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 12, 16, 20, 24, 32, 48, 64 +}; + +rt_uint8_t can_dlc2len(rt_uint8_t can_dlc) +{ + return dlc2len[can_dlc & 0x0F]; +} + +static const rt_uint8_t len2dlc[] = +{ + 0, 1, 2, 3, 4, 5, 6, 7, 8, /* 0 - 8 */ + 9, 9, 9, 9, /* 9 - 12 */ + 10, 10, 10, 10, /* 13 - 16 */ + 11, 11, 11, 11, /* 17 - 20 */ + 12, 12, 12, 12, /* 21 - 24 */ + 13, 13, 13, 13, 13, 13, 13, 13, /* 25 - 32 */ + 14, 14, 14, 14, 14, 14, 14, 14, /* 33 - 40 */ + 14, 14, 14, 14, 14, 14, 14, 14, /* 41 - 48 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 49 - 56 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 57 - 64 */ +}; + +rt_uint8_t can_len2dlc(rt_uint8_t len) +{ + if (len <= 64) + { + return len2dlc[len]; + } + + return 0xf; +} diff --git a/components/drivers/can/can_dm.h b/components/drivers/can/can_dm.h new file mode 100644 index 000000000000..e9b5f70de1b0 --- /dev/null +++ b/components/drivers/can/can_dm.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-11-26 GuEe-GUI first version + */ + +#ifndef __CAN_DM_H__ +#define __CAN_DM_H__ + +#include +#include +#include + +/* CAN payload length and DLC definitions according to ISO 11898-1 */ +#define CAN_MAX_DLC 8 +#define CAN_MAX_RAW_DLC 15 +#define CAN_MAX_DLEN 8 + +/* CAN FD payload length and DLC definitions according to ISO 11898-7 */ +#define CANFD_MAX_DLC 15 +#define CANFD_MAX_DLEN 64 + +/* + * To be used in the CAN netdriver receive path to ensure conformance with + * ISO 11898-1 Chapter 8.4.2.3 (DLC field) + */ +#define can_get_dlc(v) (rt_min_t(rt_uint8_t, (v), CAN_MAX_DLC)) +#define canfd_get_dlc(v) (rt_min_t(rt_uint8_t, (v), CANFD_MAX_DLC)) + +rt_uint8_t can_dlc2len(rt_uint8_t can_dlc); +rt_uint8_t can_len2dlc(rt_uint8_t len); + +#endif /* __CAN_DM_H__ */ diff --git a/components/drivers/can/canfd-rockchip.c b/components/drivers/can/canfd-rockchip.c new file mode 100644 index 000000000000..d46a70b60b8c --- /dev/null +++ b/components/drivers/can/canfd-rockchip.c @@ -0,0 +1,746 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-11-26 GuEe-GUI first version + */ + +#include "can_dm.h" + +#define DBG_TAG "canfd.rockchip" +#define DBG_LVL DBG_INFO +#include + +#define CAN_MODE 0x00 +#define CAN_CMD 0x04 +#define CAN_STATE 0x08 +#define CAN_INT 0x0c +#define CAN_INT_MASK 0x10 +#define CAN_LOSTARB_CODE 0x28 +#define CAN_ERR_CODE 0x2c +#define CAN_RX_ERR_CNT 0x34 +#define CAN_TX_ERR_CNT 0x38 +#define CAN_IDCODE 0x3c +#define CAN_IDMASK 0x40 +#define CAN_NBTP 0x100 +#define CAN_DBTP 0x104 +#define CAN_TDCR 0x108 +#define CAN_TSCC 0x10c +#define CAN_TSCV 0x110 +#define CAN_TXEFC 0x114 +#define CAN_RXFC 0x118 +#define CAN_AFC 0x11c +#define CAN_IDCODE0 0x120 +#define CAN_IDMASK0 0x124 +#define CAN_IDCODE1 0x128 +#define CAN_IDMASK1 0x12c +#define CAN_IDCODE2 0x130 +#define CAN_IDMASK2 0x134 +#define CAN_IDCODE3 0x138 +#define CAN_IDMASK3 0x13c +#define CAN_IDCODE4 0x140 +#define CAN_IDMASK4 0x144 +#define CAN_TXFIC 0x200 +#define CAN_TXID 0x204 +#define CAN_TXDAT0 0x208 +#define CAN_TXDAT1 0x20c +#define CAN_TXDAT2 0x210 +#define CAN_TXDAT3 0x214 +#define CAN_TXDAT4 0x218 +#define CAN_TXDAT5 0x21c +#define CAN_TXDAT6 0x220 +#define CAN_TXDAT7 0x224 +#define CAN_TXDAT8 0x228 +#define CAN_TXDAT9 0x22c +#define CAN_TXDAT10 0x230 +#define CAN_TXDAT11 0x234 +#define CAN_TXDAT12 0x238 +#define CAN_TXDAT13 0x23c +#define CAN_TXDAT14 0x240 +#define CAN_TXDAT15 0x244 +#define CAN_RXFIC 0x300 +#define CAN_RXID 0x304 +#define CAN_RXTS 0x308 +#define CAN_RXDAT0 0x30c +#define CAN_RXDAT1 0x310 +#define CAN_RXDAT2 0x314 +#define CAN_RXDAT3 0x318 +#define CAN_RXDAT4 0x31c +#define CAN_RXDAT5 0x320 +#define CAN_RXDAT6 0x324 +#define CAN_RXDAT7 0x328 +#define CAN_RXDAT8 0x32c +#define CAN_RXDAT9 0x330 +#define CAN_RXDAT10 0x334 +#define CAN_RXDAT11 0x338 +#define CAN_RXDAT12 0x33c +#define CAN_RXDAT13 0x340 +#define CAN_RXDAT14 0x344 +#define CAN_RXDAT15 0x348 +#define CAN_RXFRD 0x400 +#define CAN_TXEFRD 0x500 + +enum +{ + ROCKCHIP_CANFD_MODE = 0, + ROCKCHIP_CAN_MODE, +}; + +#define DATE_LENGTH_12_BYTE 0x9 +#define DATE_LENGTH_16_BYTE 0xa +#define DATE_LENGTH_20_BYTE 0xb +#define DATE_LENGTH_24_BYTE 0xc +#define DATE_LENGTH_32_BYTE 0xd +#define DATE_LENGTH_48_BYTE 0xe +#define DATE_LENGTH_64_BYTE 0xf + +#define SLEEP_STATE RT_BIT(6) +#define BUS_OFF_STATE RT_BIT(5) +#define ERROR_WARNING_STATE RT_BIT(4) +#define TX_PERIOD_STATE RT_BIT(3) +#define RX_PERIOD_STATE RT_BIT(2) +#define TX_BUFFER_FULL_STATE RT_BIT(1) +#define RX_BUFFER_FULL_STATE RT_BIT(0) + +#define CAN_TX0_REQ RT_BIT(0) +#define CAN_TX1_REQ RT_BIT(1) +#define CAN_TX_REQ_FULL ((CAN_TX0_REQ) | (CAN_TX1_REQ)) + +#define MODE_FDOE RT_BIT(15) +#define MODE_BRSD RT_BIT(13) +#define MODE_SPACE_RX RT_BIT(12) +#define MODE_AUTO_RETX RT_BIT(10) +#define MODE_RXSORT RT_BIT(7) +#define MODE_TXORDER RT_BIT(6) +#define MODE_RXSTX RT_BIT(5) +#define MODE_LBACK RT_BIT(4) +#define MODE_SILENT RT_BIT(3) +#define MODE_SELF_TEST RT_BIT(2) +#define MODE_SLEEP RT_BIT(1) +#define RESET_MODE 0 +#define WORK_MODE RT_BIT(0) + +#define RX_FINISH_INT RT_BIT(0) +#define TX_FINISH_INT RT_BIT(1) +#define ERR_WARN_INT RT_BIT(2) +#define RX_BUF_OV_INT RT_BIT(3) +#define PASSIVE_ERR_INT RT_BIT(4) +#define TX_LOSTARB_INT RT_BIT(5) +#define BUS_ERR_INT RT_BIT(6) +#define RX_FIFO_FULL_INT RT_BIT(7) +#define RX_FIFO_OV_INT RT_BIT(8) +#define BUS_OFF_INT RT_BIT(9) +#define BUS_OFF_RECOVERY_INT RT_BIT(10) +#define TSC_OV_INT RT_BIT(11) +#define TXE_FIFO_OV_INT RT_BIT(12) +#define TXE_FIFO_FULL_INT RT_BIT(13) +#define WAKEUP_INT RT_BIT(14) + +#define ERR_TYPE_MASK RT_GENMASK(28, 26) +#define ERR_TYPE_SHIFT 26 +#define BIT_ERR 0 +#define STUFF_ERR 1 +#define FORM_ERR 2 +#define ACK_ERR 3 +#define CRC_ERR 4 +#define ERR_DIR_RX RT_BIT(25) +#define ERR_LOC_MASK RT_GENMASK(15, 0) + +/* Nominal Bit Timing & Prescaler Register (NBTP) */ +#define NBTP_MODE_3_SAMPLES RT_BIT(31) +#define NBTP_NSJW_SHIFT 24 +#define NBTP_NSJW_MASK (0x7f << NBTP_NSJW_SHIFT) +#define NBTP_NBRP_SHIFT 16 +#define NBTP_NBRP_MASK (0xff << NBTP_NBRP_SHIFT) +#define NBTP_NTSEG2_SHIFT 8 +#define NBTP_NTSEG2_MASK (0x7f << NBTP_NTSEG2_SHIFT) +#define NBTP_NTSEG1_SHIFT 0 +#define NBTP_NTSEG1_MASK (0x7f << NBTP_NTSEG1_SHIFT) + +/* Data Bit Timing & Prescaler Register (DBTP) */ +#define DBTP_MODE_3_SAMPLES RT_BIT(21) +#define DBTP_DSJW_SHIFT 17 +#define DBTP_DSJW_MASK (0xf << DBTP_DSJW_SHIFT) +#define DBTP_DBRP_SHIFT 9 +#define DBTP_DBRP_MASK (0xff << DBTP_DBRP_SHIFT) +#define DBTP_DTSEG2_SHIFT 5 +#define DBTP_DTSEG2_MASK (0xf << DBTP_DTSEG2_SHIFT) +#define DBTP_DTSEG1_SHIFT 0 +#define DBTP_DTSEG1_MASK (0x1f << DBTP_DTSEG1_SHIFT) + +/* Transmitter Delay Compensation Register (TDCR) */ +#define TDCR_TDCO_SHIFT 1 +#define TDCR_TDCO_MASK (0x3f << TDCR_TDCO_SHIFT) +#define TDCR_TDC_ENABLE RT_BIT(0) + +#define TX_FD_ENABLE RT_BIT(5) +#define TX_FD_BRS_ENABLE RT_BIT(4) + +#define FIFO_ENABLE RT_BIT(0) + +#define FORMAT_SHIFT 7 +#define FORMAT_MASK (0x1 << FORMAT_SHIFT) +#define RTR_SHIFT 6 +#define RTR_MASK (0x1 << RTR_SHIFT) +#define FDF_SHIFT 5 +#define FDF_MASK (0x1 << FDF_SHIFT) +#define BRS_SHIFT 4 +#define BRS_MASK (0x1 << BRS_SHIFT) +#define TDC_SHIFT 1 +#define TDC_MASK (0x3f << TDC_SHIFT) +#define DLC_SHIFT 0 +#define DLC_MASK (0xf << DLC_SHIFT) + +#define CAN_RF_SIZE 0x48 +#define CAN_TEF_SIZE 0x8 +#define CAN_TXEFRD_OFFSET(n) (CAN_TXEFRD + CAN_TEF_SIZE * (n)) +#define CAN_RXFRD_OFFSET(n) (CAN_RXFRD + CAN_RF_SIZE * (n)) + +#define CAN_RX_FILTER_MASK 0x1fffffff + +struct rockchip_canfd +{ + struct rt_can_device parent; + + int irq; + void *regs; + rt_ubase_t mode; + + struct rt_can_msg rx_msg, tx_msg; + + struct rt_clk_array *clk_arr; + struct rt_reset_control *rstc; +}; + +#define raw_to_rockchip_canfd(raw) rt_container_of(raw, struct rockchip_canfd, parent) + +rt_inline rt_uint32_t rockchip_canfd_read(struct rockchip_canfd *rk_canfd, + int offset) +{ + return HWREG32(rk_canfd->regs + offset); +} + +rt_inline void rockchip_canfd_write(struct rockchip_canfd *rk_canfd, + int offset, rt_uint32_t val) +{ + HWREG32(rk_canfd->regs + offset) = val; +} + +static rt_err_t set_reset_mode(struct rockchip_canfd *rk_canfd) +{ + rt_reset_control_assert(rk_canfd->rstc); + rt_hw_us_delay(2); + rt_reset_control_deassert(rk_canfd->rstc); + + rockchip_canfd_write(rk_canfd, CAN_MODE, 0); + + return RT_EOK; +} + +static rt_err_t set_normal_mode(struct rockchip_canfd *rk_canfd) +{ + rt_uint32_t val; + + val = rockchip_canfd_read(rk_canfd, CAN_MODE); + val |= WORK_MODE; + rockchip_canfd_write(rk_canfd, CAN_MODE, val); + + return RT_EOK; +} + +static rt_err_t rockchip_canfd_configure(struct rt_can_device *can, struct can_configure *conf) +{ + rt_uint32_t val, reg_btp; + rt_uint16_t n_brp, n_tseg1, n_tseg2, d_brp, d_tseg1, d_tseg2, tdc = 0, sjw = 0; + struct rockchip_canfd *rk_canfd = raw_to_rockchip_canfd(can); + + set_reset_mode(rk_canfd); + + rockchip_canfd_write(rk_canfd, CAN_INT_MASK, 0xffff); + + /* RECEIVING FILTER, accept all */ + rockchip_canfd_write(rk_canfd, CAN_IDCODE, 0); + rockchip_canfd_write(rk_canfd, CAN_IDMASK, CAN_RX_FILTER_MASK); + rockchip_canfd_write(rk_canfd, CAN_IDCODE0, 0); + rockchip_canfd_write(rk_canfd, CAN_IDMASK0, CAN_RX_FILTER_MASK); + rockchip_canfd_write(rk_canfd, CAN_IDCODE1, 0); + rockchip_canfd_write(rk_canfd, CAN_IDMASK1, CAN_RX_FILTER_MASK); + rockchip_canfd_write(rk_canfd, CAN_IDCODE2, 0); + rockchip_canfd_write(rk_canfd, CAN_IDMASK2, CAN_RX_FILTER_MASK); + rockchip_canfd_write(rk_canfd, CAN_IDCODE3, 0); + rockchip_canfd_write(rk_canfd, CAN_IDMASK3, CAN_RX_FILTER_MASK); + rockchip_canfd_write(rk_canfd, CAN_IDCODE4, 0); + rockchip_canfd_write(rk_canfd, CAN_IDMASK4, CAN_RX_FILTER_MASK); + + /* Set mode */ + val = rockchip_canfd_read(rk_canfd, CAN_MODE); + + /* RX fifo enable */ + rockchip_canfd_write(rk_canfd, CAN_RXFC, + rockchip_canfd_read(rk_canfd, CAN_RXFC) | FIFO_ENABLE); + + /* Mode */ + switch (conf->mode) + { + case RT_CAN_MODE_NORMAL: + val |= MODE_FDOE; + rockchip_canfd_write(rk_canfd, CAN_TXFIC, + rockchip_canfd_read(rk_canfd, CAN_TXFIC) | TX_FD_ENABLE); + break; + + case RT_CAN_MODE_LISTEN: + val |= MODE_SILENT; + break; + + case RT_CAN_MODE_LOOPBACK: + val |= MODE_SELF_TEST | MODE_LBACK; + break; + } + + val |= MODE_AUTO_RETX; + + rockchip_canfd_write(rk_canfd, CAN_MODE, val); + + switch (conf->baud_rate) + { + case CAN1MBaud: + d_brp = 4; + d_tseg1 = 13; + d_tseg2 = 4; + n_brp = 4; + n_tseg1 = 13; + n_tseg2 = 4; + break; + + case CAN800kBaud: + d_brp = 4; + d_tseg1 = 18; + d_tseg2 = 4; + n_brp = 4; + n_tseg1 = 18; + n_tseg2 = 4; + break; + + case CAN500kBaud: + d_brp = 9; + d_tseg1 = 13; + d_tseg2 = 4; + n_brp = 4; + n_tseg1 = 33; + n_tseg2 = 4; + break; + + case CAN250kBaud: + d_brp = 9; + d_tseg1 = 24; + d_tseg2 = 13; + n_brp = 4; + n_tseg1 = 68; + n_tseg2 = 9; + break; + + case CAN125kBaud: + d_brp = 24; + d_tseg1 = 13; + d_tseg2 = 4; + n_brp = 9; + n_tseg1 = 43; + n_tseg2 = 4; + break; + + case CAN100kBaud: + d_brp = 24; + d_tseg1 = 33; + d_tseg2 = 4; + n_brp = 24; + n_tseg1 = 33; + n_tseg2 = 4; + break; + + case CAN50kBaud: + d_brp = 49; + d_tseg1 = 13; + d_tseg2 = 4; + n_brp = 24; + n_tseg1 = 68; + n_tseg2 = 9; + break; + + default: + d_brp = 4; + d_tseg1 = 13; + d_tseg2 = 4; + n_brp = 4; + n_tseg1 = 13; + n_tseg2 = 4; + break; + } + + reg_btp = (n_brp << NBTP_NBRP_SHIFT) | (sjw << NBTP_NSJW_SHIFT) | + (n_tseg1 << NBTP_NTSEG1_SHIFT) | (n_tseg2 << NBTP_NTSEG2_SHIFT); + + rockchip_canfd_write(rk_canfd, CAN_NBTP, reg_btp); + + reg_btp |= (d_brp << DBTP_DBRP_SHIFT) | (sjw << DBTP_DSJW_SHIFT) | + (d_tseg1 << DBTP_DTSEG1_SHIFT) | (d_tseg2 << DBTP_DTSEG2_SHIFT); + + rockchip_canfd_write(rk_canfd, CAN_DBTP, reg_btp); + + if (tdc) + { + rockchip_canfd_write(rk_canfd, CAN_TDCR, + rockchip_canfd_read(rk_canfd, CAN_TDCR) | (tdc << TDC_SHIFT)); + } + + set_normal_mode(rk_canfd); + + rockchip_canfd_write(rk_canfd, CAN_INT_MASK, 0); + + return RT_EOK; +} + +static rt_err_t rockchip_canfd_control(struct rt_can_device *can, int cmd, void *args) +{ + struct rockchip_canfd *rk_canfd = raw_to_rockchip_canfd(can); + + switch (cmd) + { + case RT_CAN_CMD_SET_MODE: + switch ((rt_base_t)args) + { + case RT_CAN_MODE_NORMAL: + case RT_CAN_MODE_LISTEN: + case RT_CAN_MODE_LOOPBACK: + case RT_CAN_MODE_LOOPBACKANLISTEN: + can->config.mode = (rt_uint32_t)(rt_base_t)args; + break; + + default: + return -RT_ENOSYS; + } + break; + + case RT_CAN_CMD_SET_BAUD: + can->config.baud_rate = (rt_uint32_t)(rt_base_t)args; + break; + + case RT_CAN_CMD_GET_STATUS: + can->status.rcverrcnt = rockchip_canfd_read(rk_canfd, CAN_RX_ERR_CNT); + can->status.snderrcnt = rockchip_canfd_read(rk_canfd, CAN_TX_ERR_CNT); + can->status.errcode = rockchip_canfd_read(rk_canfd, CAN_ERR_CODE); + rt_memcpy(args, &can->status, sizeof(can->status)); + return RT_EOK; + + default: + return -RT_ENOSYS; + } + + rockchip_canfd_configure(can, &can->config); + + return RT_EOK; +} + +static int rockchip_canfd_sendmsg(struct rt_can_device *can, const void *buf, rt_uint32_t boxno) +{ + rt_uint32_t dlc, cmd; + struct rt_can_msg *tx_msg; + struct rockchip_canfd *rk_canfd = raw_to_rockchip_canfd(can); + + tx_msg = &rk_canfd->tx_msg; + rt_memcpy(tx_msg, buf, sizeof(*tx_msg)); + + if (rockchip_canfd_read(rk_canfd, CAN_CMD) & CAN_TX0_REQ) + { + cmd = CAN_TX1_REQ; + } + else + { + cmd = CAN_TX0_REQ; + } + + dlc = can_len2dlc(tx_msg->len) & DLC_MASK; + + if (tx_msg->ide) + { + dlc |= FORMAT_MASK; + } + + if (tx_msg->rtr) + { + dlc |= RTR_MASK; + } + + if (can->config.mode == RT_CAN_MODE_NORMAL) + { + dlc |= TX_FD_ENABLE; + } + + rockchip_canfd_write(rk_canfd, CAN_TXID, tx_msg->id); + rockchip_canfd_write(rk_canfd, CAN_TXFIC, dlc); + + for (int i = 0; i < tx_msg->len; i += 4) + { + rockchip_canfd_write(rk_canfd, CAN_TXDAT0 + i, + *(rt_uint32_t *)(tx_msg->data + i)); + } + + rockchip_canfd_write(rk_canfd, CAN_CMD, cmd); + + return RT_EOK; +} + +static int rockchip_canfd_recvmsg(struct rt_can_device *can, void *buf, rt_uint32_t boxno) +{ + struct rockchip_canfd *rk_canfd = raw_to_rockchip_canfd(can); + + rt_memcpy(buf, &rk_canfd->rx_msg, sizeof(rk_canfd->rx_msg)); + + return RT_EOK; +} + +static const struct rt_can_ops rockchip_canfd_ops = +{ + .configure = rockchip_canfd_configure, + .control = rockchip_canfd_control, + .sendmsg = rockchip_canfd_sendmsg, + .recvmsg = rockchip_canfd_recvmsg, +}; + +static rt_err_t rockchip_canfd_rx(struct rockchip_canfd *rk_canfd) +{ + struct rt_can_msg *rx_msg = &rk_canfd->rx_msg; + rt_uint32_t id_rockchip_canfd, dlc, data[16] = { 0 }; + + dlc = rockchip_canfd_read(rk_canfd, CAN_RXFRD); + id_rockchip_canfd = rockchip_canfd_read(rk_canfd, CAN_RXFRD); + rockchip_canfd_read(rk_canfd, CAN_RXFRD); + + for (int i = 0; i < RT_ARRAY_SIZE(data); ++i) + { + data[i] = rockchip_canfd_read(rk_canfd, CAN_RXFRD); + } + + rx_msg->id = id_rockchip_canfd; + rx_msg->ide = (dlc & FORMAT_MASK) >> FORMAT_SHIFT; + rx_msg->rtr = (dlc & RTR_MASK) >> RTR_SHIFT; + + if (dlc & FDF_MASK) + { + rx_msg->len = can_dlc2len(dlc & DLC_MASK); + } + else + { + rx_msg->len = can_get_dlc(dlc & DLC_MASK); + } + + if (!rx_msg->rtr) + { + for (int i = 0; i < rx_msg->len; i += 4) + { + *(rt_uint32_t *)(rx_msg->data + i) = data[i / 4]; + } + } + + return RT_EOK; +} + +static rt_err_t rockchip_canfd_err(struct rockchip_canfd *rk_canfd, rt_uint8_t ints) +{ + rt_uint32_t sta_reg; + struct rt_can_device *can = &rk_canfd->parent; + + can->status.rcverrcnt = rockchip_canfd_read(rk_canfd, CAN_RX_ERR_CNT); + can->status.snderrcnt = rockchip_canfd_read(rk_canfd, CAN_TX_ERR_CNT); + can->status.errcode = rockchip_canfd_read(rk_canfd, CAN_ERR_CODE); + sta_reg = rockchip_canfd_read(rk_canfd, CAN_STATE); + + if (ints & BUS_OFF_INT) + { + can->status.errcode |= BUSOFF; + } + else if (ints & ERR_WARN_INT) + { + can->status.errcode |= ERRWARNING; + } + else if (ints & PASSIVE_ERR_INT) + { + can->status.errcode |= ERRPASSIVE; + } + + if ((can->status.errcode & BUSOFF) || (sta_reg & BUS_OFF_STATE)) + { + LOG_E("%s should bus off"); + } + + return RT_EOK; +} + +static void rockchip_canfd_isr(int irqno, void *param) +{ + rt_uint8_t ints; + struct rockchip_canfd *rk_canfd = param; + const rt_uint8_t err_ints = ERR_WARN_INT | RX_BUF_OV_INT | PASSIVE_ERR_INT | + TX_LOSTARB_INT | BUS_ERR_INT; + + ints = rockchip_canfd_read(rk_canfd, CAN_INT); + + if (ints & RX_FINISH_INT) + { + rockchip_canfd_rx(rk_canfd); + rt_hw_can_isr(&rk_canfd->parent, RT_CAN_EVENT_RX_IND); + } + else if (ints & TX_FINISH_INT) + { + rt_hw_can_isr(&rk_canfd->parent, RT_CAN_EVENT_TX_DONE); + } + else if (ints & (RX_FIFO_FULL_INT | RX_FIFO_OV_INT)) + { + rt_hw_can_isr(&rk_canfd->parent, RT_CAN_EVENT_RXOF_IND); + rockchip_canfd_rx(rk_canfd); + } + else if (ints & err_ints) + { + if (rockchip_canfd_err(rk_canfd, ints)) + { + LOG_E("Bus error in ISR"); + } + } + + rockchip_canfd_write(rk_canfd, CAN_INT, ints); +} + +static void rockchip_canfd_free(struct rockchip_canfd *rk_canfd) +{ + if (rk_canfd->regs) + { + rt_iounmap(rk_canfd->regs); + } + + if (!rt_is_err_or_null(rk_canfd->clk_arr)) + { + rt_clk_array_put(rk_canfd->clk_arr); + } + + if (!rt_is_err_or_null(rk_canfd->rstc)) + { + rt_reset_control_put(rk_canfd->rstc); + } + + rt_free(rk_canfd); +} + +static rt_err_t rockchip_canfd_probe(struct rt_platform_device *pdev) +{ + rt_err_t err; + const char *dev_name; + struct rt_can_device *can; + struct can_configure *conf; + struct rt_device *dev = &pdev->parent; + struct rockchip_canfd *rk_canfd = rt_calloc(1, sizeof(*rk_canfd)); + + if (!rk_canfd) + { + return -RT_ENOMEM; + } + + rk_canfd->mode = (rt_ubase_t)pdev->id->data; + rk_canfd->regs = rt_dm_dev_iomap(dev, 0); + + if (!rk_canfd->regs) + { + err = -RT_EIO; + goto _fail; + } + + rk_canfd->irq = rt_dm_dev_get_irq(dev, 0); + + if (rk_canfd->irq < 0) + { + err = rk_canfd->irq; + goto _fail; + } + + rk_canfd->clk_arr = rt_clk_get_array(dev); + + if (rt_is_err(rk_canfd->clk_arr)) + { + err = rt_ptr_err(rk_canfd->clk_arr); + goto _fail; + } + + rk_canfd->rstc = rt_reset_control_get_array(dev); + + if (rt_is_err(rk_canfd->rstc)) + { + err = rt_ptr_err(rk_canfd->rstc); + goto _fail; + } + + rt_pin_ctrl_confs_apply_by_name(dev, RT_NULL); + + can = &rk_canfd->parent; + conf = &can->config; + + conf->baud_rate = rt_clk_get_rate(rk_canfd->clk_arr->clks[0]); + conf->msgboxsz = 1; + conf->sndboxnumber = 1; + conf->mode = RT_CAN_MODE_NORMAL; + conf->ticks = 50; +#ifdef RT_CAN_USING_HDR + conf->maxhdr = 4; +#endif + + dev->user_data = rk_canfd; + + rt_dm_dev_set_name_auto(&can->parent, "can"); + dev_name = rt_dm_dev_get_name(&can->parent); + + rt_hw_interrupt_install(rk_canfd->irq, rockchip_canfd_isr, rk_canfd, dev_name); + rt_hw_interrupt_umask(rk_canfd->irq); + + if ((err = rt_hw_can_register(can, dev_name, &rockchip_canfd_ops, rk_canfd))) + { + goto _fail; + } + + return RT_EOK; + +_fail: + rockchip_canfd_free(rk_canfd); + + return err; +} + +static rt_err_t rockchip_canfd_remove(struct rt_platform_device *pdev) +{ + struct rockchip_canfd *rk_canfd = pdev->parent.user_data; + + rt_hw_interrupt_mask(rk_canfd->irq); + rt_pic_detach_irq(rk_canfd->irq, rk_canfd); + + rt_device_unregister(&rk_canfd->parent.parent); + + rockchip_canfd_free(rk_canfd); + + return RT_EOK; +} + +static const struct rt_ofw_node_id rockchip_canfd_ofw_ids[] = +{ + { .compatible = "rockchip,canfd-1.0", .data = (void *)ROCKCHIP_CANFD_MODE }, + { .compatible = "rockchip,can-2.0", .data = (void *)ROCKCHIP_CAN_MODE }, + { /* sentinel */ } +}; + +static struct rt_platform_driver rockchip_canfd_driver = +{ + .name = "canfd-rockchip", + .ids = rockchip_canfd_ofw_ids, + + .probe = rockchip_canfd_probe, + .remove = rockchip_canfd_remove, +}; +RT_PLATFORM_DRIVER_EXPORT(rockchip_canfd_driver); diff --git a/components/drivers/clk/Kconfig b/components/drivers/clk/Kconfig index d9ab62684c58..be4398c1b552 100755 --- a/components/drivers/clk/Kconfig +++ b/components/drivers/clk/Kconfig @@ -1,4 +1,15 @@ menuconfig RT_USING_CLK bool "Using Common Clock Framework (CLK)" depends on RT_USING_DM - default n + select RT_USING_ADT_REF + default y + +config RT_CLK_SCMI + bool "Clock driver controlled via SCMI interface" + depends on RT_USING_CLK + depends on RT_FIRMWARE_ARM_SCMI + default y + +if RT_USING_CLK +source "$RTT_DIR/components/drivers/clk/rockchip/Kconfig" +endif diff --git a/components/drivers/clk/SConscript b/components/drivers/clk/SConscript index 9a9b21b8c657..4d1f0682cc00 100644 --- a/components/drivers/clk/SConscript +++ b/components/drivers/clk/SConscript @@ -1,15 +1,29 @@ from building import * group = [] +objs = [] if not GetDepend(['RT_USING_CLK']): Return('group') cwd = GetCurrentDir() +list = os.listdir(cwd) CPPPATH = [cwd + '/../include'] -src = ['clk.c', 'clk-fixed-rate.c'] +src = ['clk.c'] + +if GetDepend(['RT_CLK_SCMI']): + src += ['clk-scmi.c'] + +if GetDepend(['RT_USING_OFW']): + src += ['clk-fixed-rate.c'] group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) -Return('group') +for d in list: + path = os.path.join(cwd, d) + if os.path.isfile(os.path.join(path, 'SConscript')): + objs = objs + SConscript(os.path.join(d, 'SConscript')) +objs = objs + group + +Return('objs') diff --git a/components/drivers/clk/clk-scmi.c b/components/drivers/clk/clk-scmi.c new file mode 100644 index 000000000000..f816c85af5a7 --- /dev/null +++ b/components/drivers/clk/clk-scmi.c @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-11-26 GuEe-GUI first version + */ + +#include +#include + +#define DBG_TAG "clk.scmi" +#define DBG_LVL DBG_INFO +#include + +struct scmi_clk +{ + struct rt_clk_node parent; + + struct rt_scmi_device *sdev; +}; + +#define raw_to_scmi_clk(raw) rt_container_of(raw, struct scmi_clk, parent) + +static rt_err_t scmi_clk_op_gate(struct scmi_clk *sclk, int clk_id, rt_bool_t enable) +{ + struct scmi_clk_state_in in = + { + .clock_id = rt_cpu_to_le32(clk_id), + .attributes = rt_cpu_to_le32(enable), + }; + struct scmi_clk_state_out out; + struct rt_scmi_msg msg = RT_SCMI_MSG_IN(SCMI_CLOCK_CONFIG_SET, in, out); + + return rt_scmi_process_msg(sclk->sdev, &msg); +} + +static rt_base_t scmi_clk_op_get_rate(struct scmi_clk *sclk, int clk_id) +{ + rt_ubase_t res; + struct scmi_clk_rate_get_in in = + { + .clock_id = rt_cpu_to_le32(clk_id), + }; + struct scmi_clk_rate_get_out out; + struct rt_scmi_msg msg = RT_SCMI_MSG_IN(SCMI_CLOCK_RATE_GET, in, out); + + res = rt_scmi_process_msg(sclk->sdev, &msg); + + if ((rt_base_t)res >= 0) + { + res = (rt_ubase_t)(((rt_uint64_t)out.rate_msb << 32) | out.rate_lsb); + } + + return res; +} + +static rt_base_t scmi_clk_op_set_rate(struct scmi_clk *sclk, int clk_id, rt_ubase_t rate) +{ + struct scmi_clk_rate_set_in in = + { + .clock_id = rt_cpu_to_le32(clk_id), + .flags = rt_cpu_to_le32(SCMI_CLK_RATE_ROUND_CLOSEST), + .rate_lsb = rt_cpu_to_le32((rt_uint32_t)rate), + .rate_msb = rt_cpu_to_le32((rt_uint32_t)((rt_uint64_t)rate >> 32)), + }; + struct scmi_clk_rate_set_out out; + struct rt_scmi_msg msg = RT_SCMI_MSG_IN(SCMI_CLOCK_RATE_SET, in, out); + + return rt_scmi_process_msg(sclk->sdev, &msg); +} + +static rt_err_t scmi_clk_init(struct rt_clk *clk, void *fw_data) +{ + struct scmi_clk *sclk = raw_to_scmi_clk(clk->clk_np); + struct rt_ofw_cell_args *args = fw_data; + rt_ubase_t clk_id = args->args[0]; + + clk->rate = scmi_clk_op_get_rate(sclk, clk_id); + clk->priv = (void *)clk_id; + + return RT_EOK; +} + +static rt_err_t scmi_clk_enable(struct rt_clk *clk) +{ + struct scmi_clk *sclk = raw_to_scmi_clk(clk->clk_np); + + return scmi_clk_op_gate(sclk, (rt_ubase_t)clk->priv, RT_TRUE); +} + +static void scmi_clk_disable(struct rt_clk *clk) +{ + struct scmi_clk *sclk = raw_to_scmi_clk(clk->clk_np); + + scmi_clk_op_gate(sclk, (rt_ubase_t)clk->priv, RT_FALSE); +} + +static rt_err_t scmi_clk_set_rate(struct rt_clk *clk, rt_ubase_t rate, rt_ubase_t parent_rate) +{ + struct scmi_clk *sclk = raw_to_scmi_clk(clk->clk_np); + + return scmi_clk_op_set_rate(sclk, (rt_ubase_t)clk->priv, rate); +} + +static const struct rt_clk_ops scmi_clk_ops = +{ + .init = scmi_clk_init, + .enable = scmi_clk_enable, + .disable = scmi_clk_disable, + .set_rate = scmi_clk_set_rate, +}; + +static rt_err_t scmi_clk_probe(struct rt_scmi_device *sdev) +{ + rt_err_t err; + struct rt_clk_node *clk_np; + struct scmi_clk *sclk = rt_calloc(1, sizeof(*sclk)); + + if (!sclk) + { + return -RT_ENOMEM; + } + + sclk->sdev = sdev; + + clk_np = &sclk->parent; + clk_np->ops = &scmi_clk_ops; + + if ((err = rt_clk_register(clk_np, RT_NULL))) + { + rt_free(sclk); + + goto _end; + } + + rt_dm_dev_bind_fwdata(&sdev->parent, RT_NULL, &sclk->parent); + +_end: + return err; +} + +static const struct rt_scmi_device_id scmi_clk_ids[] = +{ + { SCMI_PROTOCOL_ID_CLOCK, "clocks" }, + { /* sentinel */ }, +}; + +static struct rt_scmi_driver scmi_clk_driver = +{ + .name = "clk-scmi", + .ids = scmi_clk_ids, + + .probe = scmi_clk_probe, +}; +RT_SCMI_DRIVER_EXPORT(scmi_clk_driver); diff --git a/components/drivers/clk/clk.c b/components/drivers/clk/clk.c index f11ecd19813e..a32139f2d44c 100644 --- a/components/drivers/clk/clk.c +++ b/components/drivers/clk/clk.c @@ -8,7 +8,6 @@ * 2022-11-26 GuEe-GUI first version */ -#include #include #include @@ -21,102 +20,194 @@ static struct rt_spinlock _clk_lock = { 0 }; static rt_list_t _clk_nodes = RT_LIST_OBJECT_INIT(_clk_nodes); +static rt_list_t _clk_notifier_nodes = RT_LIST_OBJECT_INIT(_clk_notifier_nodes); static void clk_release(struct ref *r) { - struct rt_clk *clk = rt_container_of(r, struct rt_clk, ref); + struct rt_clk_node *clk_np = rt_container_of(r, struct rt_clk_node, ref); - LOG_E("%s is release", clk->name); + LOG_E("%s is release", clk_np->name); + (void)clk_np; RT_ASSERT(0); } -rt_inline struct rt_clk *clk_get(struct rt_clk *clk) +rt_inline struct rt_clk_node *clk_get(struct rt_clk_node *clk_np) { - ref_get(&clk->ref); + ref_get(&clk_np->ref); + + return clk_np; +} + +rt_inline void clk_put(struct rt_clk_node *clk_np) +{ + ref_put(&clk_np->ref, &clk_release); +} + +static struct rt_clk *clk_alloc(struct rt_clk_node *clk_np, const char *dev_id, + const char *con_id, void *fw_node) +{ + struct rt_clk *clk = rt_calloc(1, sizeof(*clk)); + + if (clk) + { + clk->clk_np = clk_np; + clk->dev_id = dev_id; + clk->con_id = con_id; + + clk->fw_node = fw_node; + } + else + { + clk = rt_err_ptr(-RT_ENOMEM); + } + + return clk; +} + +static void clk_free(struct rt_clk *clk) +{ + struct rt_clk_node *clk_np = clk->clk_np; + + if (clk_np && clk_np->ops->finit) + { + clk_np->ops->finit(clk); + } + + rt_free(clk); +} + +static struct rt_clk *clk_create(struct rt_clk_node *clk_np, const char *dev_id, + const char *con_id, void *fw_data, void *fw_node) +{ + struct rt_clk *clk = clk_alloc(clk_np, dev_id, con_id, fw_node); + + if (!rt_is_err(clk)) + { + clk_get(clk_np); + + if (clk_np->ops->init && clk_np->ops->init(clk, fw_data)) + { + LOG_E("Dev[%s] Con[%s] init fail", dev_id, con_id); + + clk_free(clk); + clk = RT_NULL; + } + } return clk; } -rt_inline void clk_put(struct rt_clk *clk) +static rt_err_t clk_notify(struct rt_clk_node *clk_np, rt_ubase_t msg, rt_ubase_t old_rate, rt_ubase_t new_rate) { - ref_put(&clk->ref, &clk_release); + rt_err_t err = RT_EOK; + struct rt_clk_notifier *notifier; + + rt_list_for_each_entry(notifier, &_clk_notifier_nodes, list) + { + if (notifier->clk->clk_np == clk_np) + { + err = notifier->callback(notifier, msg, old_rate, new_rate); + + /* Only check hareware's error */ + if (err == -RT_EIO) + { + break; + } + } + } + + return err; } -static void clk_set_parent(struct rt_clk *clk, struct rt_clk *parent) +static void clk_set_parent(struct rt_clk_node *clk_np, struct rt_clk_node *parent_np) { - rt_spin_lock(&_clk_lock); + rt_hw_spin_lock(&_clk_lock.lock); - clk->parent = parent; + clk_np->parent = parent_np; - rt_list_insert_after(&parent->children_nodes, &clk->list); + rt_list_insert_after(&parent_np->children_nodes, &clk_np->list); - rt_spin_unlock(&_clk_lock); + rt_hw_spin_unlock(&_clk_lock.lock); } static const struct rt_clk_ops unused_clk_ops = { }; -rt_err_t rt_clk_register(struct rt_clk *clk, struct rt_clk *parent) +rt_err_t rt_clk_register(struct rt_clk_node *clk_np, struct rt_clk_node *parent_np) { rt_err_t err = RT_EOK; + struct rt_clk *clk = RT_NULL; - if (clk) + if (clk_np) + { + clk = clk_alloc(clk_np, RT_NULL, RT_NULL, RT_NULL); + } + else { - if (!clk->ops) + err = -RT_EINVAL; + } + + if (!err && clk_np) + { + clk_np->clk = clk; + + if (!clk_np->ops) { - clk->ops = &unused_clk_ops; + clk_np->ops = &unused_clk_ops; } - ref_init(&clk->ref); - rt_list_init(&clk->list); - rt_list_init(&clk->children_nodes); + ref_init(&clk_np->ref); + rt_list_init(&clk_np->list); + rt_list_init(&clk_np->children_nodes); + clk_np->multi_clk = 0; - if (parent) + if (parent_np) { - clk_set_parent(clk, parent); + clk_set_parent(clk_np, parent_np); } else { - clk->parent = RT_NULL; + clk_np->parent = RT_NULL; - rt_spin_lock(&_clk_lock); + rt_hw_spin_lock(&_clk_lock.lock); - rt_list_insert_after(&_clk_nodes, &clk->list); + rt_list_insert_after(&_clk_nodes, &clk_np->list); - rt_spin_unlock(&_clk_lock); + rt_hw_spin_unlock(&_clk_lock.lock); } } else { - err = -RT_EINVAL; + err = -RT_ENOMEM; } return err; } -rt_err_t rt_clk_unregister(struct rt_clk *clk) +rt_err_t rt_clk_unregister(struct rt_clk_node *clk_np) { rt_err_t err = RT_EOK; - if (clk) + if (clk_np) { err = -RT_EBUSY; - rt_spin_lock(&_clk_lock); + rt_hw_spin_lock(&_clk_lock.lock); - if (rt_list_isempty(&clk->children_nodes)) + if (rt_list_isempty(&clk_np->children_nodes)) { - if (ref_read(&clk->ref) <= 1) + if (ref_read(&clk_np->ref) <= 1) { - rt_list_remove(&clk->list); + rt_list_remove(&clk_np->list); + clk_free(clk_np->clk); err = RT_EOK; } } - rt_spin_unlock(&_clk_lock); + rt_hw_spin_unlock(&_clk_lock.lock); } else { @@ -126,42 +217,63 @@ rt_err_t rt_clk_unregister(struct rt_clk *clk) return err; } -void rt_clk_put(struct rt_clk *clk) +rt_err_t rt_clk_notifier_register(struct rt_clk *clk, struct rt_clk_notifier *notifier) { - if (clk) + if (!clk || !clk->clk_np || !notifier) { - clk_put(clk); + return -RT_EINVAL; } + + rt_hw_spin_lock(&_clk_lock.lock); + + ++clk->clk_np->notifier_count; + rt_list_init(¬ifier->list); + rt_list_insert_after(&_clk_notifier_nodes, ¬ifier->list); + + rt_hw_spin_unlock(&_clk_lock.lock); + + return RT_EOK; } -struct rt_clk *rt_clk_get_parent(struct rt_clk *clk) +rt_err_t rt_clk_notifier_unregister(struct rt_clk *clk, struct rt_clk_notifier *notifier) { - struct rt_clk *parent = RT_NULL; + struct rt_clk_notifier *notifier_find; - if (clk) + if (!clk || !notifier) { - rt_spin_lock(&_clk_lock); + return -RT_EINVAL; + } + + rt_hw_spin_lock(&_clk_lock.lock); - parent = clk->parent; + rt_list_for_each_entry(notifier_find, &_clk_notifier_nodes, list) + { + if (notifier_find->clk->clk_np == notifier->clk->clk_np) + { + --clk->clk_np->notifier_count; + rt_list_remove(¬ifier->list); - rt_spin_unlock(&_clk_lock); + break; + } } - return parent; + rt_hw_spin_unlock(&_clk_lock.lock); + + return RT_EOK; } -static rt_err_t clk_prepare(struct rt_clk *clk) +static rt_err_t clk_prepare(struct rt_clk *clk, struct rt_clk_node *clk_np) { rt_err_t err = RT_EOK; - if (clk->parent) + if (clk_np->parent) { - clk_prepare(clk->parent); + clk_prepare(clk_np->clk, clk_np->parent); } - if (clk->ops->prepare) + if (clk_np->ops->prepare) { - err = clk->ops->prepare(clk); + err = clk_np->ops->prepare(clk); } return err; @@ -173,32 +285,28 @@ rt_err_t rt_clk_prepare(struct rt_clk *clk) RT_DEBUG_NOT_IN_INTERRUPT; - if (clk) + if (clk && clk->clk_np) { - rt_spin_lock(&_clk_lock); + rt_hw_spin_lock(&_clk_lock.lock); - err = clk_prepare(clk); + err = clk_prepare(clk, clk->clk_np); - rt_spin_unlock(&_clk_lock); - } - else - { - err = -RT_EINVAL; + rt_hw_spin_unlock(&_clk_lock.lock); } return err; } -static void clk_unprepare(struct rt_clk *clk) +static void clk_unprepare(struct rt_clk *clk, struct rt_clk_node *clk_np) { - if (clk->parent) + if (clk_np->parent) { - clk_unprepare(clk->parent); + clk_unprepare(clk_np->clk, clk_np->parent); } - if (clk->ops->unprepare) + if (clk_np->ops->unprepare) { - clk->ops->unprepare(clk); + clk_np->ops->unprepare(clk); } } @@ -208,34 +316,30 @@ rt_err_t rt_clk_unprepare(struct rt_clk *clk) RT_DEBUG_NOT_IN_INTERRUPT; - if (clk) + if (clk && clk->clk_np) { - rt_spin_lock(&_clk_lock); + rt_hw_spin_lock(&_clk_lock.lock); - clk_unprepare(clk); + clk_unprepare(clk, clk->clk_np); - rt_spin_unlock(&_clk_lock); - } - else - { - err = -RT_EINVAL; + rt_hw_spin_unlock(&_clk_lock.lock); } return err; } -static rt_err_t clk_enable(struct rt_clk *clk) +static rt_err_t clk_enable(struct rt_clk *clk, struct rt_clk_node *clk_np) { rt_err_t err = RT_EOK; - if (clk->parent) + if (clk_np->parent) { - clk_enable(clk->parent); + clk_enable(clk_np->clk, clk_np->parent); } - if (clk->ops->enable) + if (clk_np->ops->enable) { - err = clk->ops->enable(clk); + err = clk_np->ops->enable(clk); } return err; @@ -245,44 +349,40 @@ rt_err_t rt_clk_enable(struct rt_clk *clk) { rt_err_t err = RT_EOK; - if (clk) + if (clk && clk->clk_np) { - rt_spin_lock(&_clk_lock); + rt_hw_spin_lock(&_clk_lock.lock); - err = clk_enable(clk); + err = clk_enable(clk, clk->clk_np); - rt_spin_unlock(&_clk_lock); - } - else - { - err = -RT_EINVAL; + rt_hw_spin_unlock(&_clk_lock.lock); } return err; } -static void clk_disable(struct rt_clk *clk) +static void clk_disable(struct rt_clk *clk, struct rt_clk_node *clk_np) { - if (clk->parent) + if (clk_np->parent) { - clk_disable(clk->parent); + clk_disable(clk_np->clk, clk_np->parent); } - if (clk->ops->disable) + if (clk_np->ops->disable) { - clk->ops->disable(clk); + clk_np->ops->disable(clk); } } void rt_clk_disable(struct rt_clk *clk) { - if (clk) + if (clk && clk->clk_np) { - rt_spin_lock(&_clk_lock); + rt_hw_spin_lock(&_clk_lock.lock); - clk_disable(clk); + clk_disable(clk, clk->clk_np); - rt_spin_unlock(&_clk_lock); + rt_hw_spin_unlock(&_clk_lock.lock); } } @@ -306,10 +406,6 @@ rt_err_t rt_clk_prepare_enable(struct rt_clk *clk) } } } - else - { - err = -RT_EINVAL; - } return err; } @@ -325,30 +421,154 @@ void rt_clk_disable_unprepare(struct rt_clk *clk) } } +rt_err_t rt_clk_array_prepare(struct rt_clk_array *clk_arr) +{ + rt_err_t err = RT_EOK; + + if (clk_arr) + { + for (int i = 0; i < clk_arr->count; ++i) + { + if ((err = rt_clk_prepare(clk_arr->clks[i]))) + { + LOG_E("CLK Array[%d] %s failed error = %s", i, + "prepare", rt_strerror(err)); + + while (i --> 0) + { + rt_clk_unprepare(clk_arr->clks[i]); + } + + break; + } + } + } + + return err; +} + +rt_err_t rt_clk_array_unprepare(struct rt_clk_array *clk_arr) +{ + rt_err_t err = RT_EOK; + + if (clk_arr) + { + for (int i = 0; i < clk_arr->count; ++i) + { + if ((err = rt_clk_unprepare(clk_arr->clks[i]))) + { + LOG_E("CLK Array[%d] %s failed error = %s", i, + "unprepare", rt_strerror(err)); + + break; + } + } + } + + return err; +} + +rt_err_t rt_clk_array_enable(struct rt_clk_array *clk_arr) +{ + rt_err_t err = RT_EOK; + + if (clk_arr) + { + for (int i = 0; i < clk_arr->count; ++i) + { + if ((err = rt_clk_enable(clk_arr->clks[i]))) + { + LOG_E("CLK Array[%d] %s failed error = %s", i, + "enable", rt_strerror(err)); + + while (i --> 0) + { + rt_clk_disable(clk_arr->clks[i]); + } + + break; + } + } + } + + return err; +} + +void rt_clk_array_disable(struct rt_clk_array *clk_arr) +{ + if (clk_arr) + { + for (int i = 0; i < clk_arr->count; ++i) + { + rt_clk_disable(clk_arr->clks[i]); + } + } +} + +rt_err_t rt_clk_array_prepare_enable(struct rt_clk_array *clk_arr) +{ + rt_err_t err = RT_EOK; + + if (clk_arr) + { + for (int i = 0; i < clk_arr->count; ++i) + { + if ((err = rt_clk_prepare_enable(clk_arr->clks[i]))) + { + LOG_E("CLK Array[%d] %s failed error = %s", i, + "prepare_enable", rt_strerror(err)); + + while (i --> 0) + { + rt_clk_disable_unprepare(clk_arr->clks[i]); + } + + break; + } + } + } + + return err; +} + +void rt_clk_array_disable_unprepare(struct rt_clk_array *clk_arr) +{ + if (clk_arr) + { + for (int i = 0; i < clk_arr->count; ++i) + { + rt_clk_disable_unprepare(clk_arr->clks[i]); + } + } +} + rt_err_t rt_clk_set_rate_range(struct rt_clk *clk, rt_ubase_t min, rt_ubase_t max) { rt_err_t err = RT_EOK; - if (clk) + if (clk && clk->clk_np) { - rt_spin_lock(&_clk_lock); + struct rt_clk_node *clk_np = clk->clk_np; + + rt_hw_spin_lock(&_clk_lock.lock); - if (clk->ops->set_rate) + if (clk_np->ops->set_rate) { - rt_ubase_t rate = clk->rate; - rt_ubase_t old_min = clk->min_rate; - rt_ubase_t old_max = clk->max_rate; + rt_ubase_t rate = clk_np->rate; + rt_ubase_t old_min = clk_np->min_rate; + rt_ubase_t old_max = clk_np->max_rate; - clk->min_rate = min; - clk->max_rate = max; + clk_np->min_rate = min; + clk_np->max_rate = max; rate = rt_clamp(rate, min, max); - err = clk->ops->set_rate(clk, rate, rt_clk_get_rate(clk->parent)); + err = clk_np->ops->set_rate(clk, rate, + rt_clk_get_rate(clk_np->parent ? clk_np->parent->clk : RT_NULL)); if (err) { - clk->min_rate = old_min; - clk->max_rate = old_max; + clk_np->min_rate = old_min; + clk_np->max_rate = old_max; } } else @@ -356,11 +576,7 @@ rt_err_t rt_clk_set_rate_range(struct rt_clk *clk, rt_ubase_t min, rt_ubase_t ma err = -RT_ENOSYS; } - rt_spin_unlock(&_clk_lock); - } - else - { - err = -RT_EINVAL; + rt_hw_spin_unlock(&_clk_lock.lock); } return err; @@ -370,13 +586,11 @@ rt_err_t rt_clk_set_min_rate(struct rt_clk *clk, rt_ubase_t rate) { rt_err_t err = RT_EOK; - if (clk) - { - err = rt_clk_set_rate_range(clk, rate, clk->max_rate); - } - else + if (clk && clk->clk_np) { - err = -RT_EINVAL; + struct rt_clk_node *clk_np = clk->clk_np; + + err = rt_clk_set_rate_range(clk, rate, clk_np->max_rate); } return err; @@ -386,13 +600,11 @@ rt_err_t rt_clk_set_max_rate(struct rt_clk *clk, rt_ubase_t rate) { rt_err_t err = RT_EOK; - if (clk) - { - err = rt_clk_set_rate_range(clk, clk->min_rate, rate); - } - else + if (clk && clk->clk_np) { - err = -RT_EINVAL; + struct rt_clk_node *clk_np = clk->clk_np; + + err = rt_clk_set_rate_range(clk, clk_np->min_rate, rate); } return err; @@ -402,62 +614,336 @@ rt_err_t rt_clk_set_rate(struct rt_clk *clk, rt_ubase_t rate) { rt_err_t err = RT_EOK; - if (clk) + if (clk && clk->clk_np) { - rt_spin_lock(&_clk_lock); + struct rt_clk_node *clk_np = clk->clk_np; - if (clk->ops->set_rate) + rt_hw_spin_lock(&_clk_lock.lock); + + if (clk_np->min_rate && rate < clk_np->min_rate) { - err = clk->ops->set_rate(clk, rate, rt_clk_get_rate(clk->parent)); + err = -RT_EINVAL; } - else + + if (clk_np->max_rate && rate > clk_np->max_rate) { - err = -RT_ENOSYS; + err = -RT_EINVAL; + } + + if (!err) + { + if (clk_np->ops->set_rate) + { + rt_ubase_t old_rate = clk_np->rate; + + err = clk_np->ops->set_rate(clk, rate, + rt_clk_get_rate(clk_np->parent ? clk_np->parent->clk : RT_NULL)); + + if (clk_np->rate != old_rate) + { + clk_notify(clk_np, RT_CLK_MSG_PRE_RATE_CHANGE, old_rate, clk_np->rate); + } + } + else + { + err = -RT_ENOSYS; + } } - rt_spin_unlock(&_clk_lock); + rt_hw_spin_unlock(&_clk_lock.lock); + } + + return err; +} + +rt_ubase_t rt_clk_get_rate(struct rt_clk *clk) +{ + rt_ubase_t rate = -1UL; + + if (clk) + { + if (clk->rate) + { + rate = clk->rate; + } + else if (clk->clk_np) + { + rate = clk->clk_np->rate; + } } else { - err = -RT_EINVAL; + rate = 0; + } + + return rate; +} + +rt_err_t rt_clk_set_phase(struct rt_clk *clk, int degrees) +{ + rt_err_t err = RT_EOK; + + if (clk && clk->clk_np && clk->clk_np->ops->set_phase) + { + rt_hw_spin_lock(&_clk_lock.lock); + + err = clk->clk_np->ops->set_phase(clk, degrees); + + rt_hw_spin_unlock(&_clk_lock.lock); } return err; } -rt_ubase_t rt_clk_get_rate(struct rt_clk *clk) +rt_base_t rt_clk_get_phase(struct rt_clk *clk) +{ + rt_base_t res = RT_EOK; + + if (clk && clk->clk_np && clk->clk_np->ops->get_phase) + { + rt_hw_spin_lock(&_clk_lock.lock); + + res = clk->clk_np->ops->get_phase(clk); + + rt_hw_spin_unlock(&_clk_lock.lock); + } + + return res; +} + +rt_base_t rt_clk_round_rate(struct rt_clk *clk, rt_ubase_t rate) +{ + rt_base_t res = RT_EOK; + + if (clk && clk->clk_np && clk->clk_np->ops->round_rate) + { + rt_ubase_t best_parent_rate; + struct rt_clk_node *clk_np = clk->clk_np; + + rt_hw_spin_lock(&_clk_lock.lock); + + if (clk_np->min_rate && clk_np->max_rate) + { + rate = rt_clamp(rate, clk_np->min_rate, clk_np->max_rate); + } + + res = clk->clk_np->ops->round_rate(clk, rate, &best_parent_rate); + (void)best_parent_rate; + + rt_hw_spin_unlock(&_clk_lock.lock); + } + + return res; +} + +rt_err_t rt_clk_set_parent(struct rt_clk *clk, struct rt_clk *clk_parent) +{ + rt_err_t err = RT_EOK; + + if (clk && clk->clk_np && clk->clk_np->ops->set_parent) + { + rt_hw_spin_lock(&_clk_lock.lock); + + err = clk->clk_np->ops->set_parent(clk, clk_parent); + + rt_hw_spin_unlock(&_clk_lock.lock); + } + + return err; +} + +struct rt_clk *rt_clk_get_parent(struct rt_clk *clk) +{ + struct rt_clk *parent = RT_NULL; + + if (clk) + { + struct rt_clk_node *clk_np = clk->clk_np; + + rt_hw_spin_lock(&_clk_lock.lock); + + parent = clk_np->parent ? clk_np->parent->clk : RT_NULL; + + rt_hw_spin_unlock(&_clk_lock.lock); + } + + return parent; +} + +struct rt_clk_array *rt_clk_get_array(struct rt_device *dev) { - return clk ? clk->rate : -1UL; + struct rt_clk_array *clk_arr = RT_NULL; + +#ifdef RT_USING_OFW + clk_arr = rt_ofw_get_clk_array(dev->ofw_node); +#endif + + return clk_arr; } +struct rt_clk *rt_clk_get_by_index(struct rt_device *dev, int index) +{ + struct rt_clk *clk = RT_NULL; + #ifdef RT_USING_OFW -struct rt_clk *rt_ofw_get_clk(struct rt_ofw_node *np, int index) + clk = rt_ofw_get_clk(dev->ofw_node, index); +#endif + + return clk; +} + +struct rt_clk *rt_clk_get_by_name(struct rt_device *dev, const char *name) { struct rt_clk *clk = RT_NULL; - if (np && index >= 0) +#ifdef RT_USING_OFW + clk = rt_ofw_get_clk_by_name(dev->ofw_node, name); +#endif + + return clk; +} + +void rt_clk_array_put(struct rt_clk_array *clk_arr) +{ + if (clk_arr) + { + for (int i = 0; i < clk_arr->count; ++i) + { + if (clk_arr->clks[i]) + { + rt_clk_put(clk_arr->clks[i]); + } + else + { + break; + } + } + + rt_free(clk_arr); + } +} + +void rt_clk_put(struct rt_clk *clk) +{ + if (clk) { - rt_phandle phandle; + clk_put(clk->clk_np); + clk_free(clk); + } +} + +#ifdef RT_USING_OFW +static struct rt_clk *ofw_get_clk_no_lock(struct rt_ofw_node *np, int index, const char *name) +{ + struct rt_clk *clk = RT_NULL; + struct rt_ofw_cell_args clk_args; + + if (!rt_ofw_parse_phandle_cells(np, "clocks", "#clock-cells", index, &clk_args)) + { + int count; + struct rt_ofw_node *clk_ofw_np = clk_args.data; + struct rt_clk_node *clk_np = rt_ofw_data(clk_ofw_np); + + count = rt_ofw_count_of_clk(clk_ofw_np); - rt_spin_lock(&_clk_lock); + rt_ofw_node_put(clk_ofw_np); - if (!rt_ofw_prop_read_u32_index(np, "clocks", index, (rt_uint32_t *)&phandle)) + if (clk_np) { - struct rt_ofw_node *clk_np = rt_ofw_find_node_by_phandle(phandle); + if (count > 1) + { + /* args[0] must be the index of CLK */ + clk_np = &clk_np[clk_args.args[0]]; + } - if (clk_np) + clk = clk_create(clk_np, np->full_name, name, &clk_args, np); + } + else + { + clk = rt_err_ptr(-RT_ERROR); + } + } + + return clk; +} + +static struct rt_clk *ofw_get_clk(struct rt_ofw_node *np, int index, const char *name) +{ + struct rt_clk *clk; + + rt_hw_spin_lock(&_clk_lock.lock); + + clk = ofw_get_clk_no_lock(np, index, name); + + rt_hw_spin_unlock(&_clk_lock.lock); + + return clk; +} + +struct rt_clk_array *rt_ofw_get_clk_array(struct rt_ofw_node *np) +{ + int count; + struct rt_clk_array *clk_arr = RT_NULL; + + if (!np) + { + return rt_err_ptr(-RT_EINVAL); + } + + if ((count = rt_ofw_count_phandle_cells(np, "clocks", "#clock-cells")) > 0) + { + clk_arr = rt_calloc(1, sizeof(*clk_arr) + sizeof(clk_arr->clks[0]) * count); + + if (clk_arr) + { + int i; + rt_err_t err = RT_EOK; + rt_bool_t has_name = rt_ofw_prop_read_bool(np, "clock-names"); + + clk_arr->count = count; + + rt_hw_spin_lock(&_clk_lock.lock); + + for (i = 0; i < count; ++i) { - clk = rt_ofw_data(clk_np); - rt_ofw_node_put(clk_np); + const char *name = RT_NULL; - if (clk) + if (has_name) { - clk = clk_get(clk); + rt_ofw_prop_read_string_index(np, "clock-names", i, &name); + } + + clk_arr->clks[i] = ofw_get_clk_no_lock(np, i, name); + + if (rt_is_err(clk_arr->clks[i])) + { + err = rt_ptr_err(clk_arr->clks[i]); + + --i; + break; } } + + rt_hw_spin_unlock(&_clk_lock.lock); + + if (i > 0 && i < count) + { + rt_clk_array_put(clk_arr); + clk_arr = rt_err_ptr(err); + } } + } - rt_spin_unlock(&_clk_lock); + return clk_arr; +} + +struct rt_clk *rt_ofw_get_clk(struct rt_ofw_node *np, int index) +{ + struct rt_clk *clk = RT_NULL; + + if (np && index >= 0) + { + clk = ofw_get_clk(np, index, RT_NULL); } return clk; @@ -469,26 +955,82 @@ struct rt_clk *rt_ofw_get_clk_by_name(struct rt_ofw_node *np, const char *name) if (np && name) { - int index; - struct rt_ofw_cell_args clk_args; + int index = rt_ofw_prop_index_of_string(np, "clock-names", name); + + if (index >= 0) + { + clk = ofw_get_clk(np, index, name); + } + } - rt_spin_lock(&_clk_lock); + return clk; +} - index = rt_ofw_prop_index_of_string(np, "clock-names", name); +rt_ssize_t rt_ofw_count_of_clk(struct rt_ofw_node *clk_ofw_np) +{ + if (clk_ofw_np) + { + struct rt_clk_node *clk_np = rt_ofw_data(clk_ofw_np); - if (index >= 0 && !rt_ofw_parse_phandle_cells(np, "clocks", "#clock-cells", index, &clk_args)) + if (clk_np && clk_np->multi_clk) { - clk = rt_ofw_data(clk_args.data); + return clk_np->multi_clk; + } + else + { + const fdt32_t *cell; + rt_uint32_t count = 0; + struct rt_ofw_prop *prop; + + prop = rt_ofw_get_prop(clk_ofw_np, "clock-indices", RT_NULL); - if (clk) + if (prop) { - clk = clk_get(clk); + rt_uint32_t max_idx, idx; + + for (cell = rt_ofw_prop_next_u32(prop, RT_NULL, &idx); + cell; + cell = rt_ofw_prop_next_u32(prop, cell, &idx)) + { + if (idx > max_idx) + { + max_idx = idx; + } + } + + count = max_idx + 1; } - } + else + { + rt_ssize_t len; - rt_spin_unlock(&_clk_lock); + if ((prop = rt_ofw_get_prop(clk_ofw_np, "clock-output-names", &len))) + { + char *value = prop->value; + + for (int i = 0; i < len; ++i, ++value) + { + if (*value == '\0') + { + ++count; + } + } + } + else + { + count = 1; + } + } + + if (clk_np) + { + clk_np->multi_clk = count; + } + + return count; + } } - return clk; + return -RT_EINVAL; } -#endif /* RT_USING_OFW */ \ No newline at end of file +#endif /* RT_USING_OFW */ diff --git a/components/drivers/clk/rockchip/Kconfig b/components/drivers/clk/rockchip/Kconfig new file mode 100644 index 000000000000..1163cac60233 --- /dev/null +++ b/components/drivers/clk/rockchip/Kconfig @@ -0,0 +1,21 @@ +menuconfig RT_CLK_ROCKCHIP_RK8XX_CLKOUT + bool "Clock driver for RK805/RK808/RK809/RK817/RK818" + depends on RT_MFD_RK8XX + select RT_USING_OFW + default n + +menuconfig RT_CLK_ROCKCHIP + bool "Rockchip clock controller common" + select RT_USING_OFW + select RT_USING_RESET + default n + +config RT_CLK_ROCKCHIP_RK3308 + bool "Rockchip RK3308 clock controller support" + depends on RT_CLK_ROCKCHIP + default n + +config RT_CLK_ROCKCHIP_RK3568 + bool "Rockchip RK3568 clock controller support" + depends on RT_CLK_ROCKCHIP + default n diff --git a/components/drivers/clk/rockchip/SConscript b/components/drivers/clk/rockchip/SConscript new file mode 100644 index 000000000000..c740ce5795b7 --- /dev/null +++ b/components/drivers/clk/rockchip/SConscript @@ -0,0 +1,24 @@ +from building import * + +group = [] + +if not GetDepend(['RT_CLK_ROCKCHIP']) and not GetDepend(['RT_CLK_ROCKCHIP_RK8XX_CLKOUT']): + Return('group') + +cwd = GetCurrentDir() +CPPPATH = [cwd + '/../../include'] + +src = [] + +if GetDepend(['RT_CLK_ROCKCHIP_RK8XX_CLKOUT']): + src += ['clk-rk8xx-clkout.c'] + +if GetDepend(['RT_CLK_ROCKCHIP_RK3308']): + src += ['clk-rk3308.c'] + +if GetDepend(['RT_CLK_ROCKCHIP_RK3568']): + src += ['clk-rk3568.c'] + +group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') diff --git a/components/drivers/clk/rockchip/clk-mmc-phase.c b/components/drivers/clk/rockchip/clk-mmc-phase.c new file mode 100644 index 000000000000..f7254a34a563 --- /dev/null +++ b/components/drivers/clk/rockchip/clk-mmc-phase.c @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-3-08 GuEe-GUI the first version + */ + +#define ROCKCHIP_MMC_DELAY_SEL RT_BIT(11) +#define ROCKCHIP_MMC_DEGREE_OFFSET 1 +#define ROCKCHIP_MMC_DEGREE_MASK (0x3 << ROCKCHIP_MMC_DEGREE_OFFSET) +#define ROCKCHIP_MMC_DELAYNUM_OFFSET 3 +#define ROCKCHIP_MMC_DELAYNUM_MASK (0xff << ROCKCHIP_MMC_DELAYNUM_OFFSET) +/* + * Each fine delay is between 44ps-77ps. Assume each fine delay is 60ps to + * simplify calculations. So 45degs could be anywhere between 33deg and 57.8deg. + */ +#define ROCKCHIP_MMC_DELAY_ELEMENT_PSEC 60 + +#define PSECS_PER_SEC 1000000000000LL + +#define RK3288_MMC_CLKGEN_DIV 2 + +rt_inline rt_ubase_t rk_clk_mmc_recalc(rt_ubase_t parent_rate) +{ + return parent_rate / RK3288_MMC_CLKGEN_DIV; +} + +static rt_err_t rk_clk_mmc_set_phase(rt_ubase_t rate, void *reg, int shift, + int degrees) +{ + rt_uint32_t raw_value, delay; + rt_uint8_t nineties, remainder, delay_num; + + /* + * The below calculation is based on the output clock from + * MMC host to the card, which expects the phase clock inherits + * the clock rate from its parent, namely the output clock + * provider of MMC host. However, things may go wrong if + * (1) It is orphan. + * (2) It is assigned to the wrong parent. + * + * This check help debug the case (1), which seems to be the + * most likely problem we often face and which makes it difficult + * for people to debug unstable mmc tuning results. + */ + if (!rate) + { + LOG_E("Invalid CLK rate in phase setting"); + + return -RT_EINVAL; + } + + nineties = degrees / 90; + remainder = (degrees % 90); + + /* + * Due to the inexact nature of the "fine" delay, we might + * actually go non-monotonic. We don't go _too_ monotonic + * though, so we should be OK. Here are options of how we may + * work: + * + * Ideally we end up with: + * 1.0, 2.0, ..., 69.0, 70.0, ..., 89.0, 90.0 + * + * On one extreme (if delay is actually 44ps): + * .73, 1.5, ..., 50.6, 51.3, ..., 65.3, 90.0 + * The other (if delay is actually 77ps): + * 1.3, 2.6, ..., 88.6. 89.8, ..., 114.0, 90 + * + * It's possible we might make a delay that is up to 25 + * degrees off from what we think we're making. That's OK + * though because we should be REALLY far from any bad range. + */ + + /* + * Convert to delay; do a little extra work to make sure we + * don't overflow 32-bit / 64-bit numbers. + */ + delay = 10000000; /* PSECS_PER_SEC / 10000 / 10 */ + delay *= remainder; + delay = RT_DIV_ROUND_CLOSEST(delay, (rate / 1000) * 36 * + (ROCKCHIP_MMC_DELAY_ELEMENT_PSEC / 10)); + + delay_num = (rt_uint8_t)rt_min_t(rt_uint32_t, delay, 255); + + raw_value = delay_num ? ROCKCHIP_MMC_DELAY_SEL : 0; + raw_value |= delay_num << ROCKCHIP_MMC_DELAYNUM_OFFSET; + raw_value |= nineties; + HWREG32(reg) = HIWORD_UPDATE(raw_value, 0x07ff, shift); + + return RT_EOK; +} + +static rt_base_t rk_clk_mmc_get_phase(rt_ubase_t rate, void *reg, int shift) +{ + rt_uint16_t degrees; + rt_uint32_t raw_value, delay_num = 0; + + /* Constant signal, no measurable phase shift */ + if (!rate) + { + return 0; + } + + raw_value = HWREG32(reg) >> shift; + degrees = (raw_value & ROCKCHIP_MMC_DEGREE_MASK) * 90; + + if (raw_value & ROCKCHIP_MMC_DELAY_SEL) + { + /* degrees/delaynum * 1000000 */ + rt_ubase_t factor = (ROCKCHIP_MMC_DELAY_ELEMENT_PSEC / 10) * + 36 * (rate / 10000); + + delay_num = (raw_value & ROCKCHIP_MMC_DELAYNUM_MASK); + delay_num >>= ROCKCHIP_MMC_DELAYNUM_OFFSET; + degrees += RT_DIV_ROUND_CLOSEST(delay_num * factor, 1000000); + } + + return degrees % 360; +} diff --git a/components/drivers/clk/rockchip/clk-pll.c b/components/drivers/clk/rockchip/clk-pll.c new file mode 100644 index 000000000000..b1ac354a0aa4 --- /dev/null +++ b/components/drivers/clk/rockchip/clk-pll.c @@ -0,0 +1,403 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-3-08 GuEe-GUI the first version + */ + +/* Define pll mode */ +#define RKCLK_PLL_MODE_SLOW 0 +#define RKCLK_PLL_MODE_NORMAL 1 +#define RKCLK_PLL_MODE_DEEP 2 + +/* Only support RK3036 type CLK */ +#define PLLCON0_FBDIV_MASK 0xfff +#define PLLCON0_FBDIV_SHIFT 0 +#define PLLCON0_POSTDIV1_MASK (0x7 << 12) +#define PLLCON0_POSTDIV1_SHIFT 12 +#define PLLCON1_LOCK_STATUS (1 << 10) +#define PLLCON1_REFDIV_MASK 0x3f +#define PLLCON1_REFDIV_SHIFT 0 +#define PLLCON1_POSTDIV2_MASK (0x7 << 6) +#define PLLCON1_POSTDIV2_SHIFT 6 +#define PLLCON1_DSMPD_MASK (0x1 << 12) +#define PLLCON1_DSMPD_SHIFT 12 +#define PLLCON2_FRAC_MASK 0xffffff +#define PLLCON2_FRAC_SHIFT 0 +#define PLLCON1_PWRDOWN_SHIT 13 +#define PLLCON1_PWRDOWN (1 << PLLCON1_PWRDOWN_SHIT) + +#define MIN_FOUTVCO_FREQ (800 * MHZ) +#define MAX_FOUTVCO_FREQ (2000 * MHZ) + +static struct rk_pll_rate_table auto_table; + +static int gcd(int m, int n) +{ + while (m > 0) + { + if (n > m) + { + int t = m; + m = n; + n = t; + } + m -= n; + } + + return n; +} + +/* + * rational_best_approximation(31415, 10000, + * (1 << 8) - 1, (1 << 5) - 1, &n, &d); + * + * you may look at given_numerator as a fixed point number, + * with the fractional part size described in given_denominator. + * + * for theoretical background, see: + * http://en.wikipedia.org/wiki/Continued_fraction + */ +static void rational_best_approximation(rt_ubase_t given_numerator, + rt_ubase_t given_denominator, + rt_ubase_t max_numerator, + rt_ubase_t max_denominator, + rt_ubase_t *best_numerator, + rt_ubase_t *best_denominator) +{ + rt_ubase_t n, d, n0, d0, n1, d1; + + n = given_numerator; + d = given_denominator; + n0 = 0; + d1 = 0; + n1 = 1; + d0 = 1; + + for (;;) + { + rt_ubase_t t, a; + + if (n1 > max_numerator || d1 > max_denominator) + { + n1 = n0; + d1 = d0; + break; + } + if (d == 0) + { + break; + } + t = d; + a = n / d; + d = n % d; + n = t; + t = n0 + a * n1; + n0 = n1; + n1 = t; + t = d0 + a * d1; + d0 = d1; + d1 = t; + } + *best_numerator = n1; + *best_denominator = d1; +} + +/* + * How to calculate the PLL(from TRM V0.3 Part 1 Page 63): + * Formulas also embedded within the Fractional PLL Verilog model: + * If DSMPD = 1 (DSM is disabled, "integer mode") + * FOUTVCO = FREF / REFDIV * FBDIV + * FOUTPOSTDIV = FOUTVCO / POSTDIV1 / POSTDIV2 + * Where: + * FOUTVCO = Fractional PLL non-divided output frequency + * FOUTPOSTDIV = Fractional PLL divided output frequency + * (output of second post divider) + * FREF = Fractional PLL input reference frequency, (the OSC_HZ 24MHz input) + * REFDIV = Fractional PLL input reference clock divider + * FBDIV = Integer value programmed into feedback divide + */ + +static int rk_pll_clk_set_postdiv(rt_ubase_t fout_hz, rt_uint32_t *postdiv1, + rt_uint32_t *postdiv2, rt_uint32_t *foutvco) +{ + rt_ubase_t freq; + + if (fout_hz < MIN_FOUTVCO_FREQ) + { + for (*postdiv1 = 1; *postdiv1 <= 7; ++(*postdiv1)) + { + for (*postdiv2 = 1; *postdiv2 <= 7; ++(*postdiv2)) + { + freq = fout_hz * (*postdiv1) * (*postdiv2); + if (freq >= MIN_FOUTVCO_FREQ && freq <= MAX_FOUTVCO_FREQ) + { + *foutvco = freq; + return 0; + } + } + } + } + else + { + *postdiv1 = 1; + *postdiv2 = 1; + } + return 0; +} + +static struct rk_pll_rate_table *rk_pll_clk_set_by_auto(rt_ubase_t fin_hz, rt_ubase_t fout_hz) +{ + struct rk_pll_rate_table *rate_table = &auto_table; + rt_uint32_t foutvco = fout_hz; + rt_ubase_t fin_64, frac_64; + rt_uint32_t f_frac, postdiv1, postdiv2; + rt_ubase_t clk_gcd = 0; + + if (fin_hz == 0 || fout_hz == 0 || fout_hz == fin_hz) + { + return RT_NULL; + } + + rk_pll_clk_set_postdiv(fout_hz, &postdiv1, &postdiv2, &foutvco); + rate_table->postdiv1 = postdiv1; + rate_table->postdiv2 = postdiv2; + rate_table->dsmpd = 1; + + if (fin_hz / MHZ * MHZ == fin_hz && fout_hz / MHZ * MHZ == fout_hz) + { + fin_hz /= MHZ; + foutvco /= MHZ; + clk_gcd = gcd(fin_hz, foutvco); + rate_table->refdiv = fin_hz / clk_gcd; + rate_table->fbdiv = foutvco / clk_gcd; + + rate_table->frac = 0; + } + else + { + clk_gcd = gcd(fin_hz / MHZ, foutvco / MHZ); + rate_table->refdiv = fin_hz / MHZ / clk_gcd; + rate_table->fbdiv = foutvco / MHZ / clk_gcd; + + rate_table->frac = 0; + + f_frac = (foutvco % MHZ); + fin_64 = fin_hz; + fin_64 = fin_64 / rate_table->refdiv; + frac_64 = f_frac << 24; + frac_64 = frac_64 / fin_64; + rate_table->frac = frac_64; + + if (rate_table->frac > 0) + { + rate_table->dsmpd = 0; + } + } + return rate_table; +} + +static const struct rk_pll_rate_table *rk_get_pll_settings(struct rk_pll_clock *pll, rt_ubase_t rate) +{ + struct rk_pll_rate_table *rate_table = pll->rate_table; + + while (rate_table->rate) + { + if (rate_table->rate == rate) + { + break; + } + ++rate_table; + } + + if (rate_table->rate != rate) + { + return rk_pll_clk_set_by_auto(24 * MHZ, rate); + } + else + { + return rate_table; + } +} + +static rt_ubase_t rk_pll_get_rate(struct rk_pll_clock *pll, void *base); + +static int rk_pll_set_rate(struct rk_pll_clock *pll, void *base, rt_ubase_t drate) +{ + const struct rk_pll_rate_table *rate; + + if (rk_pll_get_rate(pll, base) == drate) + { + return 0; + } + + pll->mode_mask = PLL_MODE_MASK; + rate = rk_get_pll_settings(pll, drate); + + if (!rate) + { + return -RT_ERROR; + } + + /* + * When power on or changing PLL setting, we must force PLL into slow mode + * to ensure output stable clock. + */ + rk_clrsetreg(base + pll->mode_offset, pll->mode_mask << pll->mode_shift, RKCLK_PLL_MODE_SLOW << pll->mode_shift); + + /* Power down */ + rk_setreg(base + pll->con_offset + 0x4, 1 << PLLCON1_PWRDOWN_SHIT); + + rk_clrsetreg(base + pll->con_offset, (PLLCON0_POSTDIV1_MASK | PLLCON0_FBDIV_MASK), + (rate->postdiv1 << PLLCON0_POSTDIV1_SHIFT) |rate->fbdiv); + rk_clrsetreg(base + pll->con_offset + 0x4, (PLLCON1_POSTDIV2_MASK | PLLCON1_REFDIV_MASK), + (rate->postdiv2 << PLLCON1_POSTDIV2_SHIFT | rate->refdiv << PLLCON1_REFDIV_SHIFT)); + + if (!rate->dsmpd) + { + rt_uint32_t val; + + rk_clrsetreg(base + pll->con_offset + 0x4, PLLCON1_DSMPD_MASK, + rate->dsmpd << PLLCON1_DSMPD_SHIFT); + + val = HWREG32(base + pll->con_offset + 0x8) & (~PLLCON2_FRAC_MASK); + HWREG32(base + pll->con_offset + 0x8) = val | (rate->frac << PLLCON2_FRAC_SHIFT); + } + + /* Power Up */ + rk_clrreg(base + pll->con_offset + 0x4, 1 << PLLCON1_PWRDOWN_SHIT); + + /* Waiting for pll lock */ + while (!(HWREG32(base + pll->con_offset + 0x4) & (1 << pll->lock_shift))) + { + } + + rk_clrsetreg(base + pll->mode_offset, pll->mode_mask << pll->mode_shift, RKCLK_PLL_MODE_NORMAL << pll->mode_shift); + + return 0; +} + +static rt_ubase_t rk_pll_get_rate(struct rk_pll_clock *pll, void *base) +{ + rt_uint32_t refdiv, fbdiv, postdiv1, postdiv2, dsmpd, frac; + rt_uint32_t con = 0, shift, mask; + rt_ubase_t rate; + + pll->mode_mask = PLL_MODE_MASK; + + con = HWREG32(base + pll->mode_offset); + shift = pll->mode_shift; + mask = pll->mode_mask << shift; + + switch ((con & mask) >> shift) + { + case RKCLK_PLL_MODE_SLOW: + return OSC_HZ; + case RKCLK_PLL_MODE_NORMAL: + /* normal mode */ + con = HWREG32(base + pll->con_offset); + postdiv1 = (con & PLLCON0_POSTDIV1_MASK) >> PLLCON0_POSTDIV1_SHIFT; + fbdiv = (con & PLLCON0_FBDIV_MASK) >> PLLCON0_FBDIV_SHIFT; + con = HWREG32(base + pll->con_offset + 0x4); + postdiv2 = (con & PLLCON1_POSTDIV2_MASK) >> PLLCON1_POSTDIV2_SHIFT; + refdiv = (con & PLLCON1_REFDIV_MASK) >> PLLCON1_REFDIV_SHIFT; + dsmpd = (con & PLLCON1_DSMPD_MASK) >> PLLCON1_DSMPD_SHIFT; + con = HWREG32(base + pll->con_offset + 0x8); + frac = (con & PLLCON2_FRAC_MASK) >> PLLCON2_FRAC_SHIFT; + rate = (24 * fbdiv / (refdiv * postdiv1 * postdiv2)) * 1000000; + + if (dsmpd == 0) + { + rt_uint64_t frac_rate = OSC_HZ * (rt_uint64_t)frac; + + rt_do_div(frac_rate, refdiv); + frac_rate >>= 24; + rt_do_div(frac_rate, postdiv1); + rt_do_div(frac_rate, postdiv1); + rate += frac_rate; + } + return rate; + case RKCLK_PLL_MODE_DEEP: + default: + return 32768; + } +} + +static const struct rk_cpu_rate_table *rk_get_cpu_settings(struct rk_cpu_rate_table *cpu_table, rt_ubase_t rate) +{ + struct rk_cpu_rate_table *ps = cpu_table; + + while (ps->rate) + { + if (ps->rate == rate) + { + break; + } + ++ps; + } + if (ps->rate != rate) + { + return RT_NULL; + } + else + { + return ps; + } +} + +static rt_base_t rk_clk_pll_round_rate(const struct rk_pll_rate_table *pll_rates, + rt_size_t rate_count, rt_ubase_t drate, rt_ubase_t *prate) +{ + int i; + + /* Assumming rate_table is in descending order */ + for (i = 0; i < rate_count; i++) + { + if (drate >= pll_rates[i].rate) + { + return pll_rates[i].rate; + } + } + + /* return minimum supported value */ + return pll_rates[i - 1].rate; +} + +static void rk_clk_set_default_rates(struct rt_clk *clk, + rt_err_t (*clk_set_rate)(struct rt_clk *, rt_ubase_t, rt_ubase_t), int id) +{ + rt_uint32_t rate; + struct rt_ofw_cell_args clk_args; + struct rt_ofw_node *np = clk->fw_node; + const char *rate_propname = "assigned-clock-rates"; + + if (!rt_ofw_prop_read_bool(np, rate_propname)) + { + return; + } + + for (int i = 0; ; ++i) + { + if (rt_ofw_parse_phandle_cells(np, "assigned-clocks", "#clock-cells", i, &clk_args)) + { + break; + } + + rt_ofw_node_put(clk_args.data); + + if (clk_args.args[0] != id) + { + continue; + } + + if (!rt_ofw_prop_read_u32_index(np, rate_propname, i, &rate)) + { + clk_set_rate(clk, rate, 0); + } + + break; + } +} diff --git a/components/drivers/clk/rockchip/clk-rk3308.c b/components/drivers/clk/rockchip/clk-rk3308.c new file mode 100644 index 000000000000..5ce898429c93 --- /dev/null +++ b/components/drivers/clk/rockchip/clk-rk3308.c @@ -0,0 +1,2080 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-3-08 GuEe-GUI the first version + */ + +#include "clk.h" + +#define DBG_TAG "clk.rk3308" +#define DBG_LVL DBG_INFO +#include + +#include + +#define APLL_HZ (816 * MHZ) + +#define CORE_ACLK_HZ 408000000 +#define CORE_DBG_HZ 204000000 + +#define BUS_ACLK_HZ 200000000 +#define BUS_HCLK_HZ 100000000 +#define BUS_PCLK_HZ 100000000 + +#define PERI_ACLK_HZ 200000000 +#define PERI_HCLK_HZ 100000000 +#define PERI_PCLK_HZ 100000000 + +#define AUDIO_HCLK_HZ 100000000 +#define AUDIO_PCLK_HZ 100000000 + +struct rk_pll +{ + rt_uint32_t con0; + rt_uint32_t con1; + rt_uint32_t con2; + rt_uint32_t con3; + rt_uint32_t con4; + rt_uint32_t reserved0[3]; +}; + +struct rk_cru +{ + struct rk_pll pll[4]; + rt_uint32_t reserved1[8]; + rt_uint32_t mode; + rt_uint32_t misc; + rt_uint32_t reserved2[2]; + rt_uint32_t glb_cnt_th; + rt_uint32_t glb_rst_st; + rt_uint32_t glb_srst_fst; + rt_uint32_t glb_srst_snd; + rt_uint32_t glb_rst_con; + rt_uint32_t pll_lock; + rt_uint32_t reserved3[6]; + rt_uint32_t hwffc_con0; + rt_uint32_t reserved4; + rt_uint32_t hwffc_th; + rt_uint32_t hwffc_intst; + rt_uint32_t apll_con0_s; + rt_uint32_t apll_con1_s; + rt_uint32_t clksel_con0_s; + rt_uint32_t reserved5; + rt_uint32_t clksel_con[74]; + rt_uint32_t reserved6[54]; + rt_uint32_t clkgate_con[15]; + rt_uint32_t reserved7[(0x380 - 0x338) / 4 - 1]; + rt_uint32_t ssgtbl[32]; + rt_uint32_t softrst_con[10]; + rt_uint32_t reserved8[(0x480 - 0x424) / 4 - 1]; + rt_uint32_t sdmmc_con[2]; + rt_uint32_t sdio_con[2]; + rt_uint32_t emmc_con[2]; +}; + +/* Private data for the clock driver - used by rk_get_cru() */ +struct rk_clk_priv +{ + struct rk_cru *cru; + rt_ubase_t armclk_hz; + rt_ubase_t dpll_hz; + rt_ubase_t vpll0_hz; + rt_ubase_t vpll1_hz; +}; + +struct rk_clk_platform_data +{ + rt_uint32_t id; + void *base; +}; + +struct rk_clk +{ + struct rt_reset_controller_clk_node parent; + + void *base; + struct rk_clk_priv clk_info; + struct rk_clk_platform_data pdata[CLK_NR_CLKS]; +}; + +#define raw_to_rk_clk(raw) rt_container_of(raw, struct rk_clk, parent) + +#define PLL_CON(x) ((x) * 0x4) +#define MODE_CON 0xa0 + +enum plls +{ + apll, dpll, vpll0, vpll1, +}; + +enum +{ + /* PLLCON0*/ + PLL_BP_SHIFT = 15, + PLL_POSTDIV1_SHIFT = 12, + PLL_POSTDIV1_MASK = 7 << PLL_POSTDIV1_SHIFT, + PLL_FBDIV_SHIFT = 0, + PLL_FBDIV_MASK = 0xfff, + + /* PLLCON1 */ + PLL_PDSEL_SHIFT = 15, + PLL_PD1_SHIFT = 14, + PLL_PD_SHIFT = 13, + PLL_PD_MASK = 1 << PLL_PD_SHIFT, + PLL_DSMPD_SHIFT = 12, + PLL_DSMPD_MASK = 1 << PLL_DSMPD_SHIFT, + PLL_LOCK_STATUS_SHIFT = 10, + PLL_LOCK_STATUS_MASK = 1 << PLL_LOCK_STATUS_SHIFT, + PLL_POSTDIV2_SHIFT = 6, + PLL_POSTDIV2_MASK = 7 << PLL_POSTDIV2_SHIFT, + PLL_REFDIV_SHIFT = 0, + PLL_REFDIV_MASK = 0x3f, + + /* PLLCON2 */ + PLL_FOUT4PHASEPD_SHIFT = 27, + PLL_FOUTVCOPD_SHIFT = 26, + PLL_FOUTPOSTDIVPD_SHIFT = 25, + PLL_DACPD_SHIFT = 24, + PLL_FRAC_DIV = 0xffffff, + + /* CRU_MODE */ + PLLMUX_FROM_XIN24M = 0, + PLLMUX_FROM_PLL, + PLLMUX_FROM_RTC32K, + USBPHY480M_MODE_SHIFT = 8, + USBPHY480M_MODE_MASK = 3 << USBPHY480M_MODE_SHIFT, + VPLL1_MODE_SHIFT = 6, + VPLL1_MODE_MASK = 3 << VPLL1_MODE_SHIFT, + VPLL0_MODE_SHIFT = 4, + VPLL0_MODE_MASK = 3 << VPLL0_MODE_SHIFT, + DPLL_MODE_SHIFT = 2, + DPLL_MODE_MASK = 3 << DPLL_MODE_SHIFT, + APLL_MODE_SHIFT = 0, + APLL_MODE_MASK = 3 << APLL_MODE_SHIFT, + + /* CRU_CLK_SEL0_CON */ + CORE_ACLK_DIV_SHIFT = 12, + CORE_ACLK_DIV_MASK = 0x7 << CORE_ACLK_DIV_SHIFT, + CORE_DBG_DIV_SHIFT = 8, + CORE_DBG_DIV_MASK = 0xf << CORE_DBG_DIV_SHIFT, + CORE_CLK_PLL_SEL_SHIFT = 6, + CORE_CLK_PLL_SEL_MASK = 0x3 << CORE_CLK_PLL_SEL_SHIFT, + CORE_CLK_PLL_SEL_APLL = 0, + CORE_CLK_PLL_SEL_VPLL0, + CORE_CLK_PLL_SEL_VPLL1, + CORE_DIV_CON_SHIFT = 0, + CORE_DIV_CON_MASK = 0x0f << CORE_DIV_CON_SHIFT, + + /* CRU_CLK_SEL5_CON */ + BUS_PLL_SEL_SHIFT = 6, + BUS_PLL_SEL_MASK = 0x3 << BUS_PLL_SEL_SHIFT, + BUS_PLL_SEL_DPLL = 0, + BUS_PLL_SEL_VPLL0, + BUS_PLL_SEL_VPLL1, + BUS_ACLK_DIV_SHIFT = 0, + BUS_ACLK_DIV_MASK = 0x1f << BUS_ACLK_DIV_SHIFT, + + /* CRU_CLK_SEL6_CON */ + BUS_PCLK_DIV_SHIFT = 8, + BUS_PCLK_DIV_MASK = 0x1f << BUS_PCLK_DIV_SHIFT, + BUS_HCLK_DIV_SHIFT = 0, + BUS_HCLK_DIV_MASK = 0x1f << BUS_HCLK_DIV_SHIFT, + + /* CRU_CLK_SEL7_CON */ + CRYPTO_APK_SEL_SHIFT = 14, + CRYPTO_APK_PLL_SEL_MASK = 3 << CRYPTO_APK_SEL_SHIFT, + CRYPTO_PLL_SEL_DPLL = 0, + CRYPTO_PLL_SEL_VPLL0, + CRYPTO_PLL_SEL_VPLL1 = 0, + CRYPTO_APK_DIV_SHIFT = 8, + CRYPTO_APK_DIV_MASK = 0x1f << CRYPTO_APK_DIV_SHIFT, + CRYPTO_PLL_SEL_SHIFT = 6, + CRYPTO_PLL_SEL_MASK = 3 << CRYPTO_PLL_SEL_SHIFT, + CRYPTO_DIV_SHIFT = 0, + CRYPTO_DIV_MASK = 0x1f << CRYPTO_DIV_SHIFT, + + /* CRU_CLK_SEL8_CON */ + DCLK_VOP_SEL_SHIFT = 14, + DCLK_VOP_SEL_MASK = 0x3 << DCLK_VOP_SEL_SHIFT, + DCLK_VOP_SEL_DIVOUT = 0, + DCLK_VOP_SEL_FRACOUT, + DCLK_VOP_SEL_24M, + DCLK_VOP_PLL_SEL_SHIFT = 10, + DCLK_VOP_PLL_SEL_MASK = 0x3 << DCLK_VOP_PLL_SEL_SHIFT, + DCLK_VOP_PLL_SEL_DPLL = 0, + DCLK_VOP_PLL_SEL_VPLL0, + DCLK_VOP_PLL_SEL_VPLL1, + DCLK_VOP_DIV_SHIFT = 0, + DCLK_VOP_DIV_MASK = 0xff, + + /* CRU_CLK_SEL10_CON */ + CLK_UART_SRC_SEL_SHIFT = 13, + CLK_UART_SRC_SEL_MASK = 0x7 << CLK_UART_SRC_SEL_SHIFT, + CLK_UART_SRC_SEL_DPLL = 0, + CLK_UART_SRC_SEL_VPLL0, + CLK_UART_SRC_SEL_VPLL1, + CLK_UART_SRC_SEL_480M, + CLK_UART_SRC_SEL_XIN_OSC0, + CLK_UART_SRC_DIV_SHIFT = 0, + CLK_UART_SRC_DIV_MASK = 0x3f << CLK_UART_SRC_DIV_SHIFT, + + /* CRU_CLK_SEL11_CON */ + CLK_UART_SEL_SHIFT = 14, + CLK_UART_SEL_MASK = 0x3 << CLK_UART_SEL_SHIFT, + CLK_UART_SEL_SRC = 0, + CLK_UART_SEL_NP5, + CLK_UART_SEL_FRAC_OUT, + CLK_UART_NP5_DIV_SHIFT = 0, + CLK_UART_NP5_DIV_MASK = 0x3f << CLK_UART_NP5_DIV_SHIFT, + + /* CRU_CLKSEL12_CON */ + CLK_UART_FRAC_NUMERATOR_SHIFT = 16, + CLK_UART_FRAC_NUMERATOR_MASK = 0xffff << 16, + CLK_UART_FRAC_DENOMINATOR_SHIFT = 0, + CLK_UART_FRAC_DENOMINATOR_MASK = 0xffff, + + /* CRU_CLK_SEL25_CON */ + /* CRU_CLK_SEL26_CON */ + /* CRU_CLK_SEL27_CON */ + /* CRU_CLK_SEL28_CON */ + CLK_I2C_PLL_SEL_SHIFT = 14, + CLK_I2C_PLL_SEL_MASK = 0x3 << CLK_I2C_PLL_SEL_SHIFT, + CLK_I2C_PLL_SEL_DPLL = 0, + CLK_I2C_PLL_SEL_VPLL0, + CLK_I2C_PLL_SEL_24M, + CLK_I2C_DIV_CON_SHIFT = 0, + CLK_I2C_DIV_CON_MASK = 0x7f << CLK_I2C_DIV_CON_SHIFT, + + /* CRU_CLK_SEL29_CON */ + CLK_PWM_PLL_SEL_SHIFT = 14, + CLK_PWM_PLL_SEL_MASK = 0x3 << CLK_PWM_PLL_SEL_SHIFT, + CLK_PWM_PLL_SEL_DPLL = 0, + CLK_PWM_PLL_SEL_VPLL0, + CLK_PWM_PLL_SEL_24M, + CLK_PWM_DIV_CON_SHIFT = 0, + CLK_PWM_DIV_CON_MASK = 0x7f << CLK_PWM_DIV_CON_SHIFT, + + /* CRU_CLK_SEL30_CON */ + /* CRU_CLK_SEL31_CON */ + /* CRU_CLK_SEL32_CON */ + CLK_SPI_PLL_SEL_SHIFT = 14, + CLK_SPI_PLL_SEL_MASK = 0x3 << CLK_SPI_PLL_SEL_SHIFT, + CLK_SPI_PLL_SEL_DPLL = 0, + CLK_SPI_PLL_SEL_VPLL0, + CLK_SPI_PLL_SEL_24M, + CLK_SPI_DIV_CON_SHIFT = 0, + CLK_SPI_DIV_CON_MASK = 0x7f << CLK_SPI_DIV_CON_SHIFT, + + /* CRU_CLK_SEL34_CON */ + CLK_SARADC_DIV_CON_SHIFT = 0, + CLK_SARADC_DIV_CON_MASK = 0x7ff << CLK_SARADC_DIV_CON_SHIFT, + + /* CRU_CLK_SEL36_CON */ + PERI_PLL_SEL_SHIFT = 6, + PERI_PLL_SEL_MASK = 0x3 << PERI_PLL_SEL_SHIFT, + PERI_PLL_DPLL = 0, + PERI_PLL_VPLL0, + PERI_PLL_VPLL1, + PERI_ACLK_DIV_SHIFT = 0, + PERI_ACLK_DIV_MASK = 0x1f << PERI_ACLK_DIV_SHIFT, + + /* CRU_CLK_SEL37_CON */ + PERI_PCLK_DIV_SHIFT = 8, + PERI_PCLK_DIV_MASK = 0x1f << PERI_PCLK_DIV_SHIFT, + PERI_HCLK_DIV_SHIFT = 0, + PERI_HCLK_DIV_MASK = 0x1f << PERI_HCLK_DIV_SHIFT, + + /* CRU_CLKSEL41_CON */ + EMMC_CLK_SEL_SHIFT = 15, + EMMC_CLK_SEL_MASK = 1 << EMMC_CLK_SEL_SHIFT, + EMMC_CLK_SEL_EMMC = 0, + EMMC_CLK_SEL_EMMC_DIV50, + EMMC_PLL_SHIFT = 8, + EMMC_PLL_MASK = 0x3 << EMMC_PLL_SHIFT, + EMMC_SEL_DPLL = 0, + EMMC_SEL_VPLL0, + EMMC_SEL_VPLL1, + EMMC_SEL_24M, + EMMC_DIV_SHIFT = 0, + EMMC_DIV_MASK = 0xff << EMMC_DIV_SHIFT, + + /* CRU_CLKSEL43_CON */ + MAC_CLK_SPEED_SEL_SHIFT = 15, + MAC_CLK_SPEED_SEL_MASK = 1 << MAC_CLK_SPEED_SEL_SHIFT, + MAC_CLK_SPEED_SEL_10M = 0, + MAC_CLK_SPEED_SEL_100M, + MAC_CLK_SOURCE_SEL_SHIFT = 14, + MAC_CLK_SOURCE_SEL_MASK = 1 << MAC_CLK_SOURCE_SEL_SHIFT, + MAC_CLK_SOURCE_SEL_INTERNAL = 0, + MAC_CLK_SOURCE_SEL_EXTERNAL, + MAC_PLL_SHIFT = 6, + MAC_PLL_MASK = 0x3 << MAC_PLL_SHIFT, + MAC_SEL_DPLL = 0, + MAC_SEL_VPLL0, + MAC_SEL_VPLL1, + MAC_DIV_SHIFT = 0, + MAC_DIV_MASK = 0x1f << MAC_DIV_SHIFT, + + /* CRU_CLK_SEL45_CON */ + AUDIO_PCLK_DIV_SHIFT = 8, + AUDIO_PCLK_DIV_MASK = 0x1f << AUDIO_PCLK_DIV_SHIFT, + AUDIO_PLL_SEL_SHIFT = 6, + AUDIO_PLL_SEL_MASK = 0x3 << AUDIO_PLL_SEL_SHIFT, + AUDIO_PLL_VPLL0 = 0, + AUDIO_PLL_VPLL1, + AUDIO_PLL_24M, + AUDIO_HCLK_DIV_SHIFT = 0, + AUDIO_HCLK_DIV_MASK = 0x1f << AUDIO_HCLK_DIV_SHIFT, +}; + +enum +{ + VCO_MAX_HZ = 3200U * 1000000, + VCO_MIN_HZ = 800 * 1000000, + OUTPUT_MAX_HZ = 3200U * 1000000, + OUTPUT_MIN_HZ = 24 * 1000000, +}; + +static struct rk_pll_rate_table pll_rates[] = +{ + /* _mhz, _refdiv, _fbdiv, _postdiv1, _postdiv2, _dsmpd, _frac */ + PLL_RATE(1608000000, 1, 67, 1, 1, 1, 0), + PLL_RATE(1584000000, 1, 66, 1, 1, 1, 0), + PLL_RATE(1560000000, 1, 65, 1, 1, 1, 0), + PLL_RATE(1536000000, 1, 64, 1, 1, 1, 0), + PLL_RATE(1512000000, 1, 63, 1, 1, 1, 0), + PLL_RATE(1488000000, 1, 62, 1, 1, 1, 0), + PLL_RATE(1464000000, 1, 61, 1, 1, 1, 0), + PLL_RATE(1440000000, 1, 60, 1, 1, 1, 0), + PLL_RATE(1416000000, 1, 59, 1, 1, 1, 0), + PLL_RATE(1392000000, 1, 58, 1, 1, 1, 0), + PLL_RATE(1368000000, 1, 57, 1, 1, 1, 0), + PLL_RATE(1344000000, 1, 56, 1, 1, 1, 0), + PLL_RATE(1320000000, 1, 55, 1, 1, 1, 0), + PLL_RATE(1296000000, 1, 54, 1, 1, 1, 0), + PLL_RATE(1272000000, 1, 53, 1, 1, 1, 0), + PLL_RATE(1248000000, 1, 52, 1, 1, 1, 0), + PLL_RATE(1200000000, 1, 50, 1, 1, 1, 0), + PLL_RATE(1188000000, 2, 99, 1, 1, 1, 0), + PLL_RATE(1104000000, 1, 46, 1, 1, 1, 0), + PLL_RATE(1100000000, 12, 550, 1, 1, 1, 0), + PLL_RATE(1008000000, 1, 84, 2, 1, 1, 0), + PLL_RATE(1000000000, 6, 500, 2, 1, 1, 0), + PLL_RATE(984000000, 1, 82, 2, 1, 1, 0), + PLL_RATE(960000000, 1, 80, 2, 1, 1, 0), + PLL_RATE(936000000, 1, 78, 2, 1, 1, 0), + PLL_RATE(912000000, 1, 76, 2, 1, 1, 0), + PLL_RATE(900000000, 4, 300, 2, 1, 1, 0), + PLL_RATE(888000000, 1, 74, 2, 1, 1, 0), + PLL_RATE(864000000, 1, 72, 2, 1, 1, 0), + PLL_RATE(840000000, 1, 70, 2, 1, 1, 0), + PLL_RATE(816000000, 1, 68, 2, 1, 1, 0), + PLL_RATE(800000000, 6, 400, 2, 1, 1, 0), + PLL_RATE(700000000, 6, 350, 2, 1, 1, 0), + PLL_RATE(696000000, 1, 58, 2, 1, 1, 0), + PLL_RATE(624000000, 1, 52, 2, 1, 1, 0), + PLL_RATE(600000000, 1, 75, 3, 1, 1, 0), + PLL_RATE(594000000, 2, 99, 2, 1, 1, 0), + PLL_RATE(504000000, 1, 63, 3, 1, 1, 0), + PLL_RATE(500000000, 6, 250, 2, 1, 1, 0), + PLL_RATE(408000000, 1, 68, 2, 2, 1, 0), + PLL_RATE(312000000, 1, 52, 2, 2, 1, 0), + PLL_RATE(216000000, 1, 72, 4, 2, 1, 0), + PLL_RATE(96000000, 1, 64, 4, 4, 1, 0), +}; + +static struct rk_cpu_rate_table cpu_rates[] = +{ + CPUCLK_RATE(1608000000, 1, 7), + CPUCLK_RATE(1512000000, 1, 7), + CPUCLK_RATE(1488000000, 1, 5), + CPUCLK_RATE(1416000000, 1, 5), + CPUCLK_RATE(1392000000, 1, 5), + CPUCLK_RATE(1296000000, 1, 5), + CPUCLK_RATE(1200000000, 1, 5), + CPUCLK_RATE(1104000000, 1, 5), + CPUCLK_RATE(1008000000, 1, 5), + CPUCLK_RATE(912000000, 1, 5), + CPUCLK_RATE(816000000, 1, 3), + CPUCLK_RATE(696000000, 1, 3), + CPUCLK_RATE(600000000, 1, 3), + CPUCLK_RATE(408000000, 1, 1), + CPUCLK_RATE(312000000, 1, 1), + CPUCLK_RATE(216000000, 1, 1), + CPUCLK_RATE(96000000, 1, 1), +}; + +static struct rk_pll_clock pll_clks[] = +{ + [apll] = PLL(PLL_APLL, PLL_CON(0), MODE_CON, 0, 10, 0, pll_rates), + [dpll] = PLL(PLL_DPLL, PLL_CON(8), MODE_CON, 2, 10, 0, RT_NULL), + [vpll0] = PLL(PLL_VPLL0, PLL_CON(16), MODE_CON, 4, 10, 0, RT_NULL), + [vpll1] = PLL(PLL_VPLL1, PLL_CON(24), MODE_CON, 6, 10, 0, RT_NULL), +}; + +static struct rk_clk_gate clk_gates[] = +{ + GATE(SCLK_PVTM_CORE, "clk_pvtm_core", "xin24m", 0, 4), + GATE(ACLK_BUS_SRC, "clk_bus_src", RT_NULL, 1, 0), + GATE(PCLK_BUS, "pclk_bus", "clk_bus_src", 1, 3), + GATE(PCLK_DDR, "pclk_ddr", "pclk_bus", 4, 15), + GATE(HCLK_BUS, "hclk_bus", "clk_bus_src", 1, 2), + GATE(ACLK_BUS, "aclk_bus", "clk_bus_src", 1, 1), + GATE(SCLK_UART0, "clk_uart0", "clk_uart0_mux", 1, 12), + GATE(SCLK_UART1, "clk_uart1", "clk_uart1_mux", 2, 0), + GATE(SCLK_UART2, "clk_uart2", "clk_uart2_mux", 2, 4), + GATE(SCLK_UART3, "clk_uart3", "clk_uart3_mux", 2, 8), + GATE(SCLK_UART4, "clk_uart4", "clk_uart4_mux", 2, 12), + GATE(SCLK_I2C0, "clk_i2c0", RT_NULL, 2, 13), + GATE(SCLK_I2C1, "clk_i2c1", RT_NULL, 2, 14), + GATE(SCLK_I2C2, "clk_i2c2", RT_NULL, 2, 15), + GATE(SCLK_I2C3, "clk_i2c3", RT_NULL, 3, 0), + GATE(SCLK_PWM0, "clk_pwm0", RT_NULL, 3, 1), + GATE(SCLK_PWM1, "clk_pwm1", RT_NULL, 15, 0), + GATE(SCLK_PWM2, "clk_pwm2", RT_NULL, 15, 1), + GATE(SCLK_SPI0, "clk_spi0", RT_NULL, 3, 2), + GATE(SCLK_SPI1, "clk_spi1", RT_NULL, 3, 3), + GATE(SCLK_SPI2, "clk_spi2", RT_NULL, 3, 4), + GATE(SCLK_TIMER0, "sclk_timer0", "xin24m", 3, 10), + GATE(SCLK_TIMER1, "sclk_timer1", "xin24m", 3, 11), + GATE(SCLK_TIMER2, "sclk_timer2", "xin24m", 3, 12), + GATE(SCLK_TIMER3, "sclk_timer3", "xin24m", 3, 13), + GATE(SCLK_TIMER4, "sclk_timer4", "xin24m", 3, 14), + GATE(SCLK_TIMER5, "sclk_timer5", "xin24m", 3, 15), + GATE(SCLK_TSADC, "clk_tsadc", "xin24m", 3, 5), + GATE(SCLK_SARADC, "clk_saradc", "xin24m", 3, 6), + GATE(SCLK_OTP, "clk_otp", "xin24m", 3, 7), + GATE(SCLK_OTP_USR, "clk_otp_usr", "clk_otp", 3, 8), + GATE(SCLK_CPU_BOOST, "clk_cpu_boost", "xin24m", 3, 9), + GATE(SCLK_CRYPTO, "clk_crypto", RT_NULL, 1, 4), + GATE(SCLK_CRYPTO_APK, "clk_crypto_apk", RT_NULL, 1, 5), + GATE(DCLK_VOP, "dclk_vop", "dclk_vop_mux", 1, 8), + GATE(ACLK_PERI_SRC, "clk_peri_src", RT_NULL, 8, 0), + GATE(ACLK_PERI, "aclk_peri", "clk_peri_src", 8, 1), + GATE(HCLK_PERI, "hclk_peri", "clk_peri_src", 8, 2), + GATE(PCLK_PERI, "pclk_peri", "clk_peri_src", 8, 3), + GATE(SCLK_NANDC_DIV, "clk_nandc_div", RT_NULL, 8, 4), + GATE(SCLK_NANDC_DIV50, "clk_nandc_div50", RT_NULL, 8, 4), + GATE(SCLK_NANDC, "clk_nandc", RT_NULL, 8, 5), + GATE(SCLK_SDMMC_DIV, "clk_sdmmc_div", RT_NULL, 8, 6), + GATE(SCLK_SDMMC_DIV50, "clk_sdmmc_div50", RT_NULL, 8, 6), + GATE(SCLK_SDMMC, "clk_sdmmc", RT_NULL, 8, 7), + GATE(SCLK_SDIO_DIV, "clk_sdio_div", RT_NULL, 8, 8), + GATE(SCLK_SDIO_DIV50, "clk_sdio_div50", RT_NULL, 8, 8), + GATE(SCLK_SDIO, "clk_sdio", RT_NULL, 8, 9), + GATE(SCLK_EMMC_DIV, "clk_emmc_div", RT_NULL, 8, 10), + GATE(SCLK_EMMC_DIV50, "clk_emmc_div50", RT_NULL, 8, 10), + GATE(SCLK_EMMC, "clk_emmc", RT_NULL, 8, 11), + GATE(SCLK_SFC, "clk_sfc", RT_NULL, 8, 12), + GATE(SCLK_OTG_ADP, "clk_otg_adp", "clk_rtc32k", 8, 13), + GATE(SCLK_MAC_SRC, "clk_mac_src", RT_NULL, 8, 14), + GATE(SCLK_MAC_REF, "clk_mac_ref", "clk_mac", 9, 1), + GATE(SCLK_MAC_RX_TX, "clk_mac_rx_tx", "clk_mac", 9, 0), + GATE(SCLK_OWIRE, "clk_owire", RT_NULL, 8, 15), + GATE(SCLK_DDRCLK, "clk_ddrphy4x_src", RT_NULL, 0, 10), + GATE(PCLK_PMU, "pclk_pmu", "pclk_bus", 4, 5), + GATE(SCLK_PMU, "clk_pmu", "pclk_bus", 4, 6), + GATE(SCLK_USBPHY_REF, "clk_usbphy_ref", RT_NULL, 4, 8), + GATE(SCLK_WIFI, "clk_wifi", RT_NULL, 4, 1), + GATE(SCLK_PVTM_PMU, "clk_pvtm_pmu", "xin24m", 4, 4), + GATE(HCLK_AUDIO, "hclk_audio", "clk_audio_src", 10, 1), + GATE(PCLK_AUDIO, "pclk_audio", "clk_audio_src", 10, 2), + GATE(SCLK_PDM, "clk_pdm", "clk_pdm_mux", 10, 5), + GATE(SCLK_I2S0_8CH_TX_SRC, "clk_i2s0_8ch_tx_src", RT_NULL, 10, 12), + GATE(SCLK_I2S0_8CH_TX, "clk_i2s0_8ch_tx", RT_NULL, 10, 14), + GATE(SCLK_I2S0_8CH_TX_OUT, "clk_i2s0_8ch_tx_out", RT_NULL, 10, 15), + GATE(SCLK_I2S0_8CH_RX_SRC, "clk_i2s0_8ch_rx_src", RT_NULL, 11, 0), + GATE(SCLK_I2S0_8CH_RX, "clk_i2s0_8ch_rx", RT_NULL, 11, 2), + GATE(SCLK_I2S0_8CH_RX_OUT, "clk_i2s0_8ch_rx_out", "clk_i2s0_8ch_rx", 11, 3), + GATE(SCLK_I2S1_8CH_TX_SRC, "clk_i2s1_8ch_tx_src", RT_NULL, 11, 4), + GATE(SCLK_I2S1_8CH_TX, "clk_i2s1_8ch_tx", RT_NULL, 11, 6), + GATE(SCLK_I2S1_8CH_TX_OUT, "clk_i2s1_8ch_tx_out", RT_NULL, 11, 7), + GATE(SCLK_I2S1_8CH_RX_SRC, "clk_i2s1_8ch_rx_src", RT_NULL, 11, 8), + GATE(SCLK_I2S1_8CH_RX, "clk_i2s1_8ch_rx", RT_NULL, 11, 10), + GATE(SCLK_I2S1_8CH_RX_OUT, "clk_i2s1_8ch_rx_out", "clk_i2s1_8ch_rx", 11, 11), + GATE(SCLK_I2S2_8CH_TX_SRC, "clk_i2s2_8ch_tx_src", RT_NULL, 11, 12), + GATE(SCLK_I2S2_8CH_TX, "clk_i2s2_8ch_tx", RT_NULL, 11, 14), + GATE(SCLK_I2S2_8CH_TX_OUT, "clk_i2s2_8ch_tx_out", RT_NULL, 11, 15), + GATE(SCLK_I2S2_8CH_RX_SRC, "clk_i2s2_8ch_rx_src", RT_NULL, 12, 0), + GATE(SCLK_I2S2_8CH_RX, "clk_i2s2_8ch_rx", RT_NULL, 12, 2), + GATE(SCLK_I2S2_8CH_RX_OUT, "clk_i2s2_8ch_rx_out", "clk_i2s2_8ch_rx", 12, 3), + GATE(SCLK_I2S3_8CH_TX_SRC, "clk_i2s3_8ch_tx_src", RT_NULL, 12, 4), + GATE(SCLK_I2S3_8CH_TX, "clk_i2s3_8ch_tx", RT_NULL, 12, 6), + GATE(SCLK_I2S3_8CH_TX_OUT, "clk_i2s3_8ch_tx_out", RT_NULL, 12, 7), + GATE(SCLK_I2S3_8CH_RX_SRC, "clk_i2s3_8ch_rx_src", RT_NULL, 12, 8), + GATE(SCLK_I2S3_8CH_RX, "clk_i2s3_8ch_rx", RT_NULL, 12, 10), + GATE(SCLK_I2S3_8CH_RX_OUT, "clk_i2s3_8ch_rx_out", "clk_i2s3_8ch_rx", 12, 11), + GATE(SCLK_I2S0_2CH_SRC, "clk_i2s0_2ch_src", RT_NULL, 12, 12), + GATE(SCLK_I2S0_2CH, "clk_i2s0_2ch", "clk_i2s0_2ch_mux", 12, 14), + GATE(SCLK_I2S0_2CH_OUT, "clk_i2s0_2ch_out", RT_NULL, 12, 15), + GATE(SCLK_I2S1_2CH_SRC, "clk_i2s1_2ch_src", RT_NULL, 13, 0), + GATE(SCLK_I2S1_2CH, "clk_i2s1_2ch", "clk_i2s1_2ch_mux", 13, 2), + GATE(SCLK_I2S1_2CH_OUT, "clk_i2s1_2ch_out", RT_NULL, 13, 3), + GATE(SCLK_SPDIF_TX_DIV, "clk_spdif_tx_div", RT_NULL, 10, 6), + GATE(SCLK_SPDIF_TX_DIV50, "clk_spdif_tx_div50", RT_NULL, 10, 6), + GATE(SCLK_SPDIF_TX, "clk_spdif_tx", "clk_spdif_tx_mux", 10, 8), + GATE(SCLK_SPDIF_RX_DIV, "clk_spdif_rx_div", RT_NULL, 10, 9), + GATE(SCLK_SPDIF_RX_DIV50, "clk_spdif_rx_div50", RT_NULL, 10, 9), + GATE(SCLK_SPDIF_RX, "clk_spdif_rx", "clk_spdif_rx_mux", 10, 11), + GATE(ACLK_MAC, "aclk_mac", "aclk_peri", 9, 4), + GATE(HCLK_NANDC, "hclk_nandc", "hclk_peri", 9, 6), + GATE(HCLK_SDMMC, "hclk_sdmmc", "hclk_peri", 9, 7), + GATE(HCLK_SDIO, "hclk_sdio", "hclk_peri", 9, 8), + GATE(HCLK_EMMC, "hclk_emmc", "hclk_peri", 9, 9), + GATE(HCLK_SFC, "hclk_sfc", "hclk_peri", 9, 10), + GATE(HCLK_OTG, "hclk_otg", "hclk_peri", 9, 11), + GATE(HCLK_HOST, "hclk_host", "hclk_peri", 9, 12), + GATE(HCLK_HOST_ARB, "hclk_host_arb", "hclk_peri", 9, 13), + GATE(PCLK_MAC, "pclk_mac", "pclk_peri", 9, 15), + GATE(HCLK_PDM, "hclk_pdm", "hclk_audio", 14, 1), + GATE(HCLK_SPDIFTX, "hclk_spdiftx", "hclk_audio", 14, 2), + GATE(HCLK_SPDIFRX, "hclk_spdifrx", "hclk_audio", 14, 3), + GATE(HCLK_I2S0_8CH, "hclk_i2s0_8ch", "hclk_audio", 14, 4), + GATE(HCLK_I2S1_8CH, "hclk_i2s1_8ch", "hclk_audio", 14, 5), + GATE(HCLK_I2S2_8CH, "hclk_i2s2_8ch", "hclk_audio", 14, 6), + GATE(HCLK_I2S3_8CH, "hclk_i2s3_8ch", "hclk_audio", 14, 7), + GATE(HCLK_I2S0_2CH, "hclk_i2s0_2ch", "hclk_audio", 14, 8), + GATE(HCLK_I2S1_2CH, "hclk_i2s1_2ch", "hclk_audio", 14, 9), + GATE(HCLK_VAD, "hclk_vad", "hclk_audio", 14, 10), + GATE(PCLK_ACODEC, "pclk_acodec", "pclk_audio", 14, 12), + GATE(ACLK_CRYPTO, "aclk_crypto", "aclk_bus", 5, 2), + GATE(ACLK_VOP, "aclk_vop", "aclk_bus", 5, 3), + GATE(HCLK_CRYPTO, "hclk_crypto", "hclk_bus", 5, 7), + GATE(HCLK_VOP, "hclk_vop", "hclk_bus", 5, 8), + GATE(PCLK_UART0, "pclk_uart0", "pclk_bus", 5, 10), + GATE(PCLK_UART1, "pclk_uart1", "pclk_bus", 5, 11), + GATE(PCLK_UART2, "pclk_uart2", "pclk_bus", 5, 12), + GATE(PCLK_UART3, "pclk_uart3", "pclk_bus", 5, 13), + GATE(PCLK_UART4, "pclk_uart4", "pclk_bus", 5, 14), + GATE(PCLK_I2C0, "pclk_i2c0", "pclk_bus", 5, 15), + GATE(PCLK_I2C1, "pclk_i2c1", "pclk_bus", 6, 0), + GATE(PCLK_I2C2, "pclk_i2c2", "pclk_bus", 6, 1), + GATE(PCLK_I2C3, "pclk_i2c3", "pclk_bus", 6, 2), + GATE(PCLK_PWM0, "pclk_pwm0", "pclk_bus", 6, 3), + GATE(PCLK_SPI0, "pclk_spi0", "pclk_bus", 6, 4), + GATE(PCLK_SPI1, "pclk_spi1", "pclk_bus", 6, 5), + GATE(PCLK_SPI2, "pclk_spi2", "pclk_bus", 6, 6), + GATE(PCLK_SARADC, "pclk_saradc", "pclk_bus", 6, 7), + GATE(PCLK_TSADC, "pclk_tsadc", "pclk_bus", 6, 8), + GATE(PCLK_TIMER, "pclk_timer", "pclk_bus", 6, 9), + GATE(PCLK_OTP_NS, "pclk_otp_ns", "pclk_bus", 6, 10), + GATE(PCLK_GPIO0, "pclk_gpio0", "pclk_bus", 6, 12), + GATE(PCLK_GPIO1, "pclk_gpio1", "pclk_bus", 6, 13), + GATE(PCLK_GPIO2, "pclk_gpio2", "pclk_bus", 6, 14), + GATE(PCLK_GPIO3, "pclk_gpio3", "pclk_bus", 6, 15), + GATE(PCLK_GPIO4, "pclk_gpio4", "pclk_bus", 7, 0), + GATE(PCLK_SGRF, "pclk_sgrf", "pclk_bus", 7, 1), + GATE(PCLK_GRF, "pclk_grf", "pclk_bus", 7, 2), + GATE(PCLK_USBSD_DET, "pclk_usbsd_det", "pclk_bus", 7, 3), + GATE(PCLK_DDR_UPCTL, "pclk_ddr_upctl", "pclk_bus", 7, 4), + GATE(PCLK_DDR_MON, "pclk_ddr_mon", "pclk_bus", 7, 5), + GATE(PCLK_DDRPHY, "pclk_ddrphy", "pclk_bus", 7, 6), + GATE(PCLK_DDR_STDBY, "pclk_ddr_stdby", "pclk_bus", 7, 7), + GATE(PCLK_USB_GRF, "pclk_usb_grf", "pclk_bus", 7, 8), + GATE(PCLK_CRU, "pclk_cru", "pclk_bus", 7, 9), + GATE(PCLK_OTP_PHY, "pclk_otp_phy", "pclk_bus", 7, 10), + GATE(PCLK_CPU_BOOST, "pclk_cpu_boost", "pclk_bus", 7, 11), + GATE(PCLK_PWM1, "pclk_pwm1", "pclk_bus", 7, 12), + GATE(PCLK_PWM2, "pclk_pwm2", "pclk_bus", 7, 13), + GATE(PCLK_CAN, "pclk_can", "pclk_bus", 7, 14), + GATE(PCLK_OWIRE, "pclk_owire", "pclk_bus", 7, 15), +}; + +#define PLL_MODE_MASK 0x1 +#include "clk-pll.c" +#include "clk-mmc-phase.c" +#include "softrst.c" + +static rt_base_t armclk_set_clk(struct rk_clk_priv *priv, rt_ubase_t hz) +{ + struct rk_cru *cru = priv->cru; + const struct rk_cpu_rate_table *rate; + rt_ubase_t old_rate; + + rate = rk_get_cpu_settings(cpu_rates, hz); + if (!rate) + { + LOG_E("Unsupport rate %u", hz); + + return -RT_ENOSYS; + } + + /* + * select apll as cpu/core clock pll source and + * set up dependent divisors for PERI and ACLK clocks. + * core hz : apll = 1:1 + */ + old_rate = rk_pll_get_rate(&pll_clks[apll], priv->cru); + if (old_rate > hz) + { + if (rk_pll_set_rate(&pll_clks[apll], priv->cru, hz)) + { + return -RT_EINVAL; + } + + rk_clrsetreg(&cru->clksel_con[0], + CORE_CLK_PLL_SEL_MASK | CORE_DIV_CON_MASK | + CORE_ACLK_DIV_MASK | CORE_DBG_DIV_MASK, + rate->aclk_div << CORE_ACLK_DIV_SHIFT | + rate->pclk_div << CORE_DBG_DIV_SHIFT | + CORE_CLK_PLL_SEL_APLL << CORE_CLK_PLL_SEL_SHIFT | + 0 << CORE_DIV_CON_SHIFT); + } + else if (old_rate < hz) + { + rk_clrsetreg(&cru->clksel_con[0], + CORE_CLK_PLL_SEL_MASK | CORE_DIV_CON_MASK | + CORE_ACLK_DIV_MASK | CORE_DBG_DIV_MASK, + rate->aclk_div << CORE_ACLK_DIV_SHIFT | + rate->pclk_div << CORE_DBG_DIV_SHIFT | + CORE_CLK_PLL_SEL_APLL << CORE_CLK_PLL_SEL_SHIFT | + 0 << CORE_DIV_CON_SHIFT); + + if (rk_pll_set_rate(&pll_clks[apll], priv->cru, hz)) + { + return -RT_EINVAL; + } + } + + return rk_pll_get_rate(&pll_clks[apll], priv->cru); +} + +static void clk_get_pll_rate(struct rk_clk_priv *priv) +{ + if (!priv->dpll_hz) + { + priv->dpll_hz = rk_pll_get_rate(&pll_clks[dpll], priv->cru); + } + + if (!priv->vpll0_hz) + { + priv->vpll0_hz = rk_pll_get_rate(&pll_clks[vpll0], priv->cru); + } + + if (!priv->vpll1_hz) + { + priv->vpll1_hz = rk_pll_get_rate(&pll_clks[vpll1], priv->cru); + } +} + +static rt_base_t i2c_get_clk(struct rk_clk_priv *priv, rt_ubase_t clk_id) +{ + struct rk_cru *cru = priv->cru; + rt_uint32_t div, con, con_id; + + switch (clk_id) + { + case SCLK_I2C0: + con_id = 25; + break; + case SCLK_I2C1: + con_id = 26; + break; + case SCLK_I2C2: + con_id = 27; + break; + case SCLK_I2C3: + con_id = 28; + break; + default: + return -RT_EINVAL; + } + + con = HWREG32(&cru->clksel_con[con_id]); + div = con >> CLK_I2C_DIV_CON_SHIFT & CLK_I2C_DIV_CON_MASK; + + return DIV_TO_RATE(priv->dpll_hz, div); +} + +static rt_base_t i2c_set_clk(struct rk_clk_priv *priv, rt_ubase_t clk_id, rt_ubase_t rate) +{ + struct rk_cru *cru = priv->cru; + rt_uint32_t src_clk_div, con_id; + + src_clk_div = RT_DIV_ROUND_UP(priv->dpll_hz, rate); + RT_ASSERT(src_clk_div - 1 <= 127); + + switch (clk_id) + { + case SCLK_I2C0: + con_id = 25; + break; + case SCLK_I2C1: + con_id = 26; + break; + case SCLK_I2C2: + con_id = 27; + break; + case SCLK_I2C3: + con_id = 28; + break; + default: + return -RT_EINVAL; + } + + rk_clrsetreg(&cru->clksel_con[con_id], + CLK_I2C_PLL_SEL_MASK | CLK_I2C_DIV_CON_MASK, + CLK_I2C_PLL_SEL_DPLL << CLK_I2C_PLL_SEL_SHIFT | + (src_clk_div - 1) << CLK_I2C_DIV_CON_SHIFT); + + return i2c_get_clk(priv, clk_id); +} + +static rt_base_t mac_set_clk(struct rk_clk_priv *priv, rt_ubase_t clk_id, rt_ubase_t rate) +{ + struct rk_cru *cru = priv->cru; + rt_uint32_t con = HWREG32(&cru->clksel_con[43]); + rt_ubase_t pll_rate; + rt_uint8_t div; + + if ((con >> MAC_PLL_SHIFT) & MAC_SEL_VPLL0) + { + pll_rate = rk_pll_get_rate(&pll_clks[vpll0], priv->cru); + } + else if ((con >> MAC_PLL_SHIFT) & MAC_SEL_VPLL1) + { + pll_rate = rk_pll_get_rate(&pll_clks[vpll1], priv->cru); + } + else + { + pll_rate = rk_pll_get_rate(&pll_clks[dpll], priv->cru); + } + + /* default set 50MHZ for gmac */ + if (!rate) + { + rate = 50000000; + } + + div = RT_DIV_ROUND_UP(pll_rate, rate) - 1; + RT_ASSERT(div < 32); + + rk_clrsetreg(&cru->clksel_con[43], MAC_DIV_MASK, div << MAC_DIV_SHIFT); + + return DIV_TO_RATE(pll_rate, div); +} + +static rt_base_t mac_set_speed_clk(struct rk_clk_priv *priv, rt_ubase_t clk_id, rt_ubase_t rate) +{ + struct rk_cru *cru = priv->cru; + + if (rate != 2500000 && rate != 25000000) + { + LOG_E("Unsupported mac speed: %u", rate); + + return -RT_EINVAL; + } + + rk_clrsetreg(&cru->clksel_con[43], MAC_CLK_SPEED_SEL_MASK, + ((rate == 2500000) ? 0 : 1) << MAC_CLK_SPEED_SEL_SHIFT); + + return 0; +} + +static rt_base_t mmc_get_clk(struct rk_clk_priv *priv, rt_ubase_t clk_id) +{ + struct rk_cru *cru = priv->cru; + rt_uint32_t div, con, con_id; + + switch (clk_id) + { + case HCLK_SDMMC: + case SCLK_SDMMC: + con_id = 39; + break; + case HCLK_EMMC: + case SCLK_EMMC: + case SCLK_EMMC_SAMPLE: + con_id = 41; + break; + default: + return -RT_EINVAL; + } + + con = HWREG32(&cru->clksel_con[con_id]); + div = (con & EMMC_DIV_MASK) >> EMMC_DIV_SHIFT; + + if ((con & EMMC_PLL_MASK) >> EMMC_PLL_SHIFT == EMMC_SEL_24M) + { + return DIV_TO_RATE(OSC_HZ, div) / 2; + } + else + { + return DIV_TO_RATE(priv->vpll0_hz, div) / 2; + } +} + +static rt_base_t mmc_set_clk(struct rk_clk_priv *priv, rt_ubase_t clk_id, rt_ubase_t rate) +{ + struct rk_cru *cru = priv->cru; + int src_clk_div; + rt_uint32_t con_id; + + switch (clk_id) + { + case HCLK_SDMMC: + case SCLK_SDMMC: + con_id = 39; + break; + case HCLK_EMMC: + case SCLK_EMMC: + con_id = 41; + break; + default: + return -RT_EINVAL; + } + /* Select clk_sdmmc/emmc source from VPLL0 by default */ + /* mmc clock defaulg div 2 internal, need provide double in cru */ + src_clk_div = RT_DIV_ROUND_UP(priv->vpll0_hz / 2, rate); + + if (src_clk_div > 127) + { + /* use 24MHz source for 400KHz clock */ + src_clk_div = RT_DIV_ROUND_UP(OSC_HZ / 2, rate); + rk_clrsetreg(&cru->clksel_con[con_id], + EMMC_PLL_MASK | EMMC_DIV_MASK | EMMC_CLK_SEL_MASK, + EMMC_CLK_SEL_EMMC << EMMC_CLK_SEL_SHIFT | + EMMC_SEL_24M << EMMC_PLL_SHIFT | + (src_clk_div - 1) << EMMC_DIV_SHIFT); + } + else + { + rk_clrsetreg(&cru->clksel_con[con_id], + EMMC_PLL_MASK | EMMC_DIV_MASK | EMMC_CLK_SEL_MASK, + EMMC_CLK_SEL_EMMC << EMMC_CLK_SEL_SHIFT | + EMMC_SEL_VPLL0 << EMMC_PLL_SHIFT | + (src_clk_div - 1) << EMMC_DIV_SHIFT); + } + + return mmc_get_clk(priv, clk_id); +} + +static rt_base_t saradc_get_clk(struct rk_clk_priv *priv, rt_ubase_t clk_id) +{ + struct rk_cru *cru = priv->cru; + rt_uint32_t div, con; + + con = HWREG32(&cru->clksel_con[34]); + div = con >> CLK_SARADC_DIV_CON_SHIFT & CLK_SARADC_DIV_CON_MASK; + + return DIV_TO_RATE(OSC_HZ, div); +} + +static rt_base_t saradc_set_clk(struct rk_clk_priv *priv, rt_ubase_t clk_id, rt_ubase_t rate) +{ + struct rk_cru *cru = priv->cru; + int src_clk_div; + + src_clk_div = RT_DIV_ROUND_UP(OSC_HZ, rate); + RT_ASSERT(src_clk_div - 1 <= 2047); + + rk_clrsetreg(&cru->clksel_con[34], CLK_SARADC_DIV_CON_MASK, + (src_clk_div - 1) << CLK_SARADC_DIV_CON_SHIFT); + + return saradc_get_clk(priv, clk_id); +} + +static rt_base_t tsadc_get_clk(struct rk_clk_priv *priv, rt_ubase_t clk_id) +{ + struct rk_cru *cru = priv->cru; + rt_uint32_t div, con; + + con = HWREG32(&cru->clksel_con[33]); + div = con >> CLK_SARADC_DIV_CON_SHIFT & CLK_SARADC_DIV_CON_MASK; + + return DIV_TO_RATE(OSC_HZ, div); +} + +static rt_base_t tsadc_set_clk(struct rk_clk_priv *priv, rt_ubase_t clk_id, rt_ubase_t rate) +{ + struct rk_cru *cru = priv->cru; + int src_clk_div; + + src_clk_div = RT_DIV_ROUND_UP(OSC_HZ, rate); + RT_ASSERT(src_clk_div - 1 <= 2047); + + rk_clrsetreg(&cru->clksel_con[33], CLK_SARADC_DIV_CON_MASK, + (src_clk_div - 1) << CLK_SARADC_DIV_CON_SHIFT); + + return tsadc_get_clk(priv, clk_id); +} + +static rt_base_t spi_get_clk(struct rk_clk_priv *priv, rt_ubase_t clk_id) +{ + struct rk_cru *cru = priv->cru; + rt_uint32_t div, con, con_id; + + switch (clk_id) + { + case SCLK_SPI0: + con_id = 30; + break; + case SCLK_SPI1: + con_id = 31; + break; + case SCLK_SPI2: + con_id = 32; + break; + default: + return -RT_EINVAL; + } + + con = HWREG32(&cru->clksel_con[con_id]); + div = con >> CLK_SPI_DIV_CON_SHIFT & CLK_SPI_DIV_CON_MASK; + + return DIV_TO_RATE(priv->dpll_hz, div); +} + +static rt_base_t spi_set_clk(struct rk_clk_priv *priv, rt_ubase_t clk_id, rt_ubase_t rate) +{ + struct rk_cru *cru = priv->cru; + rt_uint32_t src_clk_div, con_id; + + src_clk_div = RT_DIV_ROUND_UP(priv->dpll_hz, rate); + RT_ASSERT(src_clk_div - 1 <= 127); + + switch (clk_id) + { + case SCLK_SPI0: + con_id = 30; + break; + case SCLK_SPI1: + con_id = 31; + break; + case SCLK_SPI2: + con_id = 32; + break; + default: + return -RT_EINVAL; + } + + rk_clrsetreg(&cru->clksel_con[con_id], + CLK_SPI_PLL_SEL_MASK | CLK_SPI_DIV_CON_MASK, + CLK_SPI_PLL_SEL_DPLL << CLK_SPI_PLL_SEL_SHIFT | + (src_clk_div - 1) << CLK_SPI_DIV_CON_SHIFT); + + return spi_get_clk(priv, clk_id); +} + +static rt_base_t pwm_get_clk(struct rk_clk_priv *priv, rt_ubase_t clk_id) +{ + struct rk_cru *cru = priv->cru; + rt_uint32_t div, con; + + con = HWREG32(&cru->clksel_con[29]); + div = con >> CLK_PWM_DIV_CON_SHIFT & CLK_PWM_DIV_CON_MASK; + + return DIV_TO_RATE(priv->dpll_hz, div); +} + +static rt_base_t pwm_set_clk(struct rk_clk_priv *priv, rt_ubase_t clk_id, rt_ubase_t rate) +{ + struct rk_cru *cru = priv->cru; + int src_clk_div; + + src_clk_div = RT_DIV_ROUND_UP(priv->dpll_hz, rate); + RT_ASSERT(src_clk_div - 1 <= 127); + + rk_clrsetreg(&cru->clksel_con[29], + CLK_PWM_PLL_SEL_MASK | CLK_PWM_DIV_CON_MASK, + CLK_PWM_PLL_SEL_DPLL << CLK_PWM_PLL_SEL_SHIFT | + (src_clk_div - 1) << CLK_PWM_DIV_CON_SHIFT); + + return pwm_get_clk(priv, clk_id); +} + +static rt_base_t vop_get_clk(struct rk_clk_priv *priv, rt_ubase_t clk_id) +{ + struct rk_cru *cru = priv->cru; + rt_uint32_t div, pll_sel, vol_sel, con, parent; + + con = HWREG32(&cru->clksel_con[8]); + vol_sel = (con & DCLK_VOP_SEL_MASK) >> DCLK_VOP_SEL_SHIFT; + pll_sel = (con & DCLK_VOP_PLL_SEL_MASK) >> DCLK_VOP_PLL_SEL_SHIFT; + div = con & DCLK_VOP_DIV_MASK; + + if (vol_sel == DCLK_VOP_SEL_24M) + { + parent = OSC_HZ; + } + else if (vol_sel == DCLK_VOP_SEL_DIVOUT) + { + switch (pll_sel) + { + case DCLK_VOP_PLL_SEL_DPLL: + parent = priv->dpll_hz; + break; + case DCLK_VOP_PLL_SEL_VPLL0: + parent = priv->vpll0_hz; + break; + case DCLK_VOP_PLL_SEL_VPLL1: + parent = priv->vpll0_hz; + break; + default: + return -RT_EINVAL; + } + } else { + return -RT_EINVAL; + } + + return DIV_TO_RATE(parent, div); +} + +static rt_base_t vop_set_clk(struct rk_clk_priv *priv, rt_ubase_t clk_id, rt_ubase_t rate) +{ + struct rk_cru *cru = priv->cru; + rt_ubase_t pll_rate, now, best_rate = 0; + rt_uint32_t div, best_div = 0, best_sel = 0; + + for (int i = 0; i <= DCLK_VOP_PLL_SEL_VPLL1; i++) + { + switch (i) + { + case DCLK_VOP_PLL_SEL_DPLL: + pll_rate = priv->dpll_hz; + break; + case DCLK_VOP_PLL_SEL_VPLL0: + pll_rate = priv->vpll0_hz; + break; + case DCLK_VOP_PLL_SEL_VPLL1: + pll_rate = priv->vpll1_hz; + break; + default: + return -RT_EINVAL; + } + + div = RT_DIV_ROUND_UP(pll_rate, rate); + + if (div > 255) + { + continue; + } + now = pll_rate / div; + + if (rt_abs(rate - now) < rt_abs(rate - best_rate)) + { + best_rate = now; + best_div = div; + best_sel = i; + } + } + + if (best_rate != rate && rate == OSC_HZ) + { + rk_clrsetreg(&cru->clksel_con[8], + DCLK_VOP_SEL_MASK, + DCLK_VOP_SEL_24M << DCLK_VOP_SEL_SHIFT); + } + else if (best_rate) + { + rk_clrsetreg(&cru->clksel_con[8], + DCLK_VOP_SEL_MASK | DCLK_VOP_PLL_SEL_MASK | + DCLK_VOP_DIV_MASK, + DCLK_VOP_SEL_DIVOUT << DCLK_VOP_SEL_SHIFT | + best_sel << DCLK_VOP_PLL_SEL_SHIFT | + (best_div - 1) << DCLK_VOP_DIV_SHIFT); + } + else + { + return -RT_EINVAL; + } + + return vop_get_clk(priv, clk_id); +} + +static rt_base_t bus_get_clk(struct rk_clk_priv *priv, rt_ubase_t clk_id) +{ + struct rk_cru *cru = priv->cru; + rt_uint32_t div, con, parent = priv->dpll_hz; + + switch (clk_id) + { + case ACLK_BUS: + con = HWREG32(&cru->clksel_con[5]); + div = (con & BUS_ACLK_DIV_MASK) >> BUS_ACLK_DIV_SHIFT; + break; + case HCLK_BUS: + con = HWREG32(&cru->clksel_con[6]); + div = (con & BUS_HCLK_DIV_MASK) >> BUS_HCLK_DIV_SHIFT; + break; + case PCLK_BUS: + case PCLK_WDT: + con = HWREG32(&cru->clksel_con[6]); + div = (con & BUS_PCLK_DIV_MASK) >> BUS_PCLK_DIV_SHIFT; + break; + default: + return -RT_EINVAL; + } + + return DIV_TO_RATE(parent, div); +} + +static rt_base_t bus_set_clk(struct rk_clk_priv *priv, rt_ubase_t clk_id, rt_ubase_t rate) +{ + struct rk_cru *cru = priv->cru; + int src_clk_div; + + src_clk_div = RT_DIV_ROUND_UP(priv->dpll_hz, rate); + RT_ASSERT(src_clk_div - 1 <= 31); + + /* + * select dpll as pd_bus bus clock source and + * set up dependent divisors for PCLK/HCLK and ACLK clocks. + */ + switch (clk_id) + { + case ACLK_BUS: + rk_clrsetreg(&cru->clksel_con[5], BUS_PLL_SEL_MASK | BUS_ACLK_DIV_MASK, + BUS_PLL_SEL_DPLL << BUS_PLL_SEL_SHIFT | + (src_clk_div - 1) << BUS_ACLK_DIV_SHIFT); + break; + case HCLK_BUS: + rk_clrsetreg(&cru->clksel_con[6], BUS_HCLK_DIV_MASK, + (src_clk_div - 1) << BUS_HCLK_DIV_SHIFT); + break; + case PCLK_BUS: + rk_clrsetreg(&cru->clksel_con[6], BUS_PCLK_DIV_MASK, + (src_clk_div - 1) << BUS_PCLK_DIV_SHIFT); + break; + default: + return -RT_EINVAL; + } + + return bus_get_clk(priv, clk_id); +} + +static rt_base_t peri_get_clk(struct rk_clk_priv *priv, rt_ubase_t clk_id) +{ + struct rk_cru *cru = priv->cru; + rt_uint32_t div, con, parent = priv->dpll_hz; + + switch (clk_id) + { + case ACLK_PERI: + con = HWREG32(&cru->clksel_con[36]); + div = (con & PERI_ACLK_DIV_MASK) >> PERI_ACLK_DIV_SHIFT; + break; + case HCLK_PERI: + con = HWREG32(&cru->clksel_con[37]); + div = (con & PERI_HCLK_DIV_MASK) >> PERI_HCLK_DIV_SHIFT; + break; + case PCLK_PERI: + con = HWREG32(&cru->clksel_con[37]); + div = (con & PERI_PCLK_DIV_MASK) >> PERI_PCLK_DIV_SHIFT; + break; + default: + return -RT_EINVAL; + } + + return DIV_TO_RATE(parent, div); +} + +static rt_base_t peri_set_clk(struct rk_clk_priv *priv, rt_ubase_t clk_id, rt_ubase_t rate) +{ + struct rk_cru *cru = priv->cru; + int src_clk_div; + + src_clk_div = RT_DIV_ROUND_UP(priv->dpll_hz, rate); + RT_ASSERT(src_clk_div - 1 <= 31); + + /* + * select dpll as pd_peri bus clock source and + * set up dependent divisors for PCLK/HCLK and ACLK clocks. + */ + switch (clk_id) + { + case ACLK_PERI: + rk_clrsetreg(&cru->clksel_con[36], PERI_PLL_SEL_MASK | PERI_ACLK_DIV_MASK, + PERI_PLL_DPLL << PERI_PLL_SEL_SHIFT | + (src_clk_div - 1) << PERI_ACLK_DIV_SHIFT); + break; + case HCLK_PERI: + rk_clrsetreg(&cru->clksel_con[37], PERI_HCLK_DIV_MASK, + (src_clk_div - 1) << PERI_HCLK_DIV_SHIFT); + break; + case PCLK_PERI: + rk_clrsetreg(&cru->clksel_con[37], PERI_PCLK_DIV_MASK, + (src_clk_div - 1) << PERI_PCLK_DIV_SHIFT); + break; + default: + return -RT_EINVAL; + } + + return peri_get_clk(priv, clk_id); +} + +static rt_base_t audio_get_clk(struct rk_clk_priv *priv, rt_ubase_t clk_id) +{ + struct rk_cru *cru = priv->cru; + rt_uint32_t div, con, parent = priv->vpll0_hz; + + switch (clk_id) + { + case HCLK_AUDIO: + con = HWREG32(&cru->clksel_con[45]); + div = (con & AUDIO_HCLK_DIV_MASK) >> AUDIO_HCLK_DIV_SHIFT; + break; + case PCLK_AUDIO: + con = HWREG32(&cru->clksel_con[45]); + div = (con & AUDIO_PCLK_DIV_MASK) >> AUDIO_PCLK_DIV_SHIFT; + break; + default: + return -RT_EINVAL; + } + + return DIV_TO_RATE(parent, div); +} + +static rt_base_t audio_set_clk(struct rk_clk_priv *priv, rt_ubase_t clk_id, rt_ubase_t rate) +{ + struct rk_cru *cru = priv->cru; + int src_clk_div; + + src_clk_div = RT_DIV_ROUND_UP(priv->vpll0_hz, rate); + RT_ASSERT(src_clk_div - 1 <= 31); + + /* + * select vpll0 as audio bus clock source and + * set up dependent divisors for HCLK and PCLK clocks. + */ + switch (clk_id) + { + case HCLK_AUDIO: + rk_clrsetreg(&cru->clksel_con[45], AUDIO_PLL_SEL_MASK | AUDIO_HCLK_DIV_MASK, + AUDIO_PLL_VPLL0 << AUDIO_PLL_SEL_SHIFT | + (src_clk_div - 1) << AUDIO_HCLK_DIV_SHIFT); + break; + case PCLK_AUDIO: + rk_clrsetreg(&cru->clksel_con[45], AUDIO_PLL_SEL_MASK | AUDIO_PCLK_DIV_MASK, + AUDIO_PLL_VPLL0 << AUDIO_PLL_SEL_SHIFT | + (src_clk_div - 1) << AUDIO_PCLK_DIV_SHIFT); + break; + default: + return -RT_EINVAL; + } + + return peri_get_clk(priv, clk_id); +} + +static rt_base_t crypto_get_clk(struct rk_clk_priv *priv, rt_ubase_t clk_id) +{ + struct rk_cru *cru = priv->cru; + rt_uint32_t div, con, parent; + + switch (clk_id) + { + case SCLK_CRYPTO: + con = HWREG32(&cru->clksel_con[7]); + div = (con & CRYPTO_DIV_MASK) >> CRYPTO_DIV_SHIFT; + parent = priv->vpll0_hz; + break; + case SCLK_CRYPTO_APK: + con = HWREG32(&cru->clksel_con[7]); + div = (con & CRYPTO_APK_DIV_MASK) >> CRYPTO_APK_DIV_SHIFT; + parent = priv->vpll0_hz; + break; + default: + return -RT_EINVAL; + } + + return DIV_TO_RATE(parent, div); +} + +static rt_base_t crypto_set_clk(struct rk_clk_priv *priv, rt_ubase_t clk_id, rt_ubase_t rate) +{ + struct rk_cru *cru = priv->cru; + int src_clk_div; + + src_clk_div = RT_DIV_ROUND_UP(priv->vpll0_hz, rate); + RT_ASSERT(src_clk_div - 1 <= 31); + + /* + * select gpll as crypto clock source and + * set up dependent divisors for crypto clocks. + */ + switch (clk_id) + { + case SCLK_CRYPTO: + rk_clrsetreg(&cru->clksel_con[7], CRYPTO_PLL_SEL_MASK | CRYPTO_DIV_MASK, + CRYPTO_PLL_SEL_VPLL0 << CRYPTO_PLL_SEL_SHIFT | + (src_clk_div - 1) << CRYPTO_DIV_SHIFT); + break; + case SCLK_CRYPTO_APK: + rk_clrsetreg(&cru->clksel_con[7], CRYPTO_APK_PLL_SEL_MASK | CRYPTO_APK_DIV_MASK, + CRYPTO_PLL_SEL_VPLL0 << CRYPTO_APK_SEL_SHIFT | + (src_clk_div - 1) << CRYPTO_APK_DIV_SHIFT); + break; + default: + return -RT_EINVAL; + } + + return crypto_get_clk(priv, clk_id); +} + +static rt_ubase_t uart_get_rate(struct rk_clk_priv *priv, rt_ubase_t clk_id) +{ + struct rk_cru *cru = priv->cru; + rt_uint32_t reg, con, fracdiv, div, src, p_src, p_rate; + rt_ubase_t m, n; + + switch (clk_id) + { + case SCLK_UART0: + reg = 10; + break; + case SCLK_UART1: + reg = 13; + break; + case SCLK_UART2: + reg = 16; + break; + case SCLK_UART3: + reg = 19; + break; + case SCLK_UART4: + reg = 22; + break; + default: + return -RT_ERROR; + } + + con = HWREG32(&cru->clksel_con[reg]); + p_src = (con & CLK_UART_SRC_SEL_MASK) >> CLK_UART_SRC_SEL_SHIFT; + div = (con & CLK_UART_SRC_DIV_MASK) >> CLK_UART_SRC_DIV_SHIFT; + + if (p_src == CLK_UART_SRC_SEL_DPLL) + { + p_rate = priv->dpll_hz; + } + else if (p_src == CLK_UART_SRC_SEL_VPLL0) + { + p_rate = priv->vpll0_hz; + } + else if (p_src == CLK_UART_SRC_SEL_VPLL1) + { + p_rate = priv->vpll1_hz; + } + else if (p_src == CLK_UART_SRC_SEL_480M) + { + p_rate = 480000000; + } + else + { + p_rate = OSC_HZ; + } + + con = HWREG32(&cru->clksel_con[reg + 1]); + src = (con & CLK_UART_SEL_MASK) >> CLK_UART_SEL_SHIFT; + + if (src == CLK_UART_SEL_SRC) + { + return DIV_TO_RATE(p_rate, div); + } + else if (src == CLK_UART_SEL_NP5) + { + div = (con & CLK_UART_NP5_DIV_MASK) >> CLK_UART_NP5_DIV_SHIFT; + return DIV_TO_RATE(p_rate, div); + } + else if (src == CLK_UART_SEL_FRAC_OUT) + { + fracdiv = HWREG32(&cru->clksel_con[reg + 2]); + n = fracdiv & CLK_UART_FRAC_NUMERATOR_MASK; + n >>= CLK_UART_FRAC_NUMERATOR_SHIFT; + m = fracdiv & CLK_UART_FRAC_DENOMINATOR_MASK; + m >>= CLK_UART_FRAC_DENOMINATOR_SHIFT; + return DIV_TO_RATE(p_rate, div) * n / m; + } + else + { + return OSC_HZ; + } +} + +static rt_ubase_t uart_set_rate(struct rk_clk_priv *priv, rt_ubase_t clk_id, rt_ubase_t rate) +{ + struct rk_cru *cru = priv->cru; + rt_uint32_t reg, clk_src, uart_src, src_div, np5_div; + rt_ubase_t m = 0, n = 0, val; + + if (priv->dpll_hz % rate == 0) + { + clk_src = CLK_UART_SRC_SEL_DPLL; + uart_src = CLK_UART_SEL_SRC; + src_div = RT_DIV_ROUND_UP(priv->dpll_hz, rate); + np5_div = 2; + } + else if (priv->vpll0_hz % rate == 0) + { + clk_src = CLK_UART_SRC_SEL_VPLL0; + uart_src = CLK_UART_SEL_SRC; + src_div = RT_DIV_ROUND_UP(priv->dpll_hz, rate); + np5_div = 2; + } + else if (priv->vpll1_hz % rate == 0) + { + clk_src = CLK_UART_SRC_SEL_VPLL1; + uart_src = CLK_UART_SEL_SRC; + src_div = RT_DIV_ROUND_UP(priv->dpll_hz, rate); + np5_div = 2; + } + else if (rate == OSC_HZ) + { + clk_src = CLK_UART_SRC_SEL_DPLL; + uart_src = CLK_UART_SRC_SEL_XIN_OSC0; + np5_div = 2; + src_div = 2; + } + else + { + clk_src = CLK_UART_SRC_SEL_DPLL; + uart_src = CLK_UART_SEL_FRAC_OUT; + src_div = 2; + np5_div = 2; + rational_best_approximation(rate, priv->dpll_hz / src_div, RT_GENMASK(16 - 1, 0), RT_GENMASK(16 - 1, 0), &m, &n); + } + + switch (clk_id) + { + case SCLK_UART0: + reg = 10; + break; + case SCLK_UART1: + reg = 13; + break; + case SCLK_UART2: + reg = 16; + break; + case SCLK_UART3: + reg = 19; + break; + case SCLK_UART4: + reg = 22; + break; + default: + return -RT_ERROR; + } + + rk_clrsetreg(&cru->clksel_con[reg], CLK_UART_SRC_SEL_MASK | CLK_UART_SRC_DIV_MASK, + (clk_src << CLK_UART_SRC_SEL_SHIFT) | ((src_div - 1) << CLK_UART_SRC_DIV_SHIFT)); + rk_clrsetreg(&cru->clksel_con[reg + 1], CLK_UART_SEL_MASK | CLK_UART_NP5_DIV_MASK, + (uart_src << CLK_UART_SEL_SHIFT) | ((np5_div - 1) << CLK_UART_NP5_DIV_SHIFT)); + + if (m && n) + { + val = m << CLK_UART_FRAC_NUMERATOR_SHIFT | n; + HWREG32(&cru->clksel_con[reg + 2]) = val; + } + + return uart_get_rate(priv, clk_id); +} + +static rt_base_t rk_clk_get_rate(struct rk_clk_platform_data *pdata, + struct rk_clk *rk_clk) +{ + struct rk_clk_priv *priv = &rk_clk->clk_info; + rt_ubase_t rate = 0; + + switch (pdata->id) + { + case PLL_APLL: + case ARMCLK: + rate = rk_pll_get_rate(&pll_clks[apll], priv->cru); + break; + case PLL_DPLL: + rate = rk_pll_get_rate(&pll_clks[dpll], priv->cru); + break; + case PLL_VPLL0: + rate = rk_pll_get_rate(&pll_clks[vpll0], priv->cru); + break; + case PLL_VPLL1: + rate = rk_pll_get_rate(&pll_clks[vpll1], priv->cru); + break; + case HCLK_SDMMC: + case HCLK_EMMC: + case SCLK_SDMMC: + case SCLK_EMMC: + case SCLK_EMMC_SAMPLE: + rate = mmc_get_clk(priv, pdata->id); + break; + case SCLK_I2C0: + case SCLK_I2C1: + case SCLK_I2C2: + case SCLK_I2C3: + rate = i2c_get_clk(priv, pdata->id); + break; + case SCLK_SARADC: + rate = saradc_get_clk(priv, pdata->id); + break; + case SCLK_TSADC: + rate = tsadc_get_clk(priv, pdata->id); + break; + case SCLK_SPI0: + case SCLK_SPI1: + rate = spi_get_clk(priv, pdata->id); + break; + case SCLK_PWM0: + rate = pwm_get_clk(priv, pdata->id); + break; + case DCLK_VOP: + rate = vop_get_clk(priv, pdata->id); + break; + case ACLK_BUS: + case HCLK_BUS: + case PCLK_BUS: + case PCLK_WDT: + rate = bus_get_clk(priv, pdata->id); + break; + case ACLK_PERI: + case HCLK_PERI: + case PCLK_PERI: + rate = peri_get_clk(priv, pdata->id); + break; + case HCLK_AUDIO: + case PCLK_AUDIO: + rate = audio_get_clk(priv, pdata->id); + break; + case SCLK_CRYPTO: + case SCLK_CRYPTO_APK: + rate = crypto_get_clk(priv, pdata->id); + break; + case SCLK_UART0: + case SCLK_UART1: + case SCLK_UART2: + case SCLK_UART3: + case SCLK_UART4: + rate = uart_get_rate(priv, pdata->id); + break; + case SCLK_TIMER0: + case SCLK_TIMER1: + case SCLK_TIMER2: + case SCLK_TIMER3: + case SCLK_TIMER4: + case SCLK_TIMER5: + rate = OSC_HZ; + break; + default: + return -RT_EINVAL; + } + + return rate; +} + +static rt_base_t rk_clk_set_rate(struct rk_clk_platform_data *pdata, + struct rk_clk *rk_clk, rt_ubase_t rate) +{ + struct rk_clk_priv *priv = &rk_clk->clk_info; + rt_ubase_t res = 0; + + switch (pdata->id) + { + case PLL_DPLL: + res = rk_pll_set_rate(&pll_clks[dpll], priv->cru, rate); + priv->dpll_hz = rk_pll_get_rate(&pll_clks[dpll], priv->cru); + break; + case ARMCLK: + if (priv->armclk_hz) + { + armclk_set_clk(priv, rate); + } + priv->armclk_hz = rate; + break; + case HCLK_SDMMC: + case HCLK_EMMC: + case SCLK_SDMMC: + case SCLK_EMMC: + /* make CIU CLK happy */ + res = mmc_set_clk(priv, pdata->id, rt_min_t(rt_ubase_t, 48 * MHZ, rate)); + break; + case SCLK_I2C0: + case SCLK_I2C1: + case SCLK_I2C2: + case SCLK_I2C3: + res = i2c_set_clk(priv, pdata->id, rate); + break; + case SCLK_MAC: + res = mac_set_clk(priv, pdata->id, rate); + break; + case SCLK_MAC_RMII: + res = mac_set_speed_clk(priv, pdata->id, rate); + break; + case SCLK_SARADC: + res = saradc_set_clk(priv, pdata->id, rate); + break; + case SCLK_TSADC: + res = tsadc_set_clk(priv, pdata->id, rate); + break; + case SCLK_SPI0: + case SCLK_SPI1: + res = spi_set_clk(priv, pdata->id, rate); + break; + case SCLK_PWM0: + res = pwm_set_clk(priv, pdata->id, rate); + break; + case DCLK_VOP: + res = vop_set_clk(priv, pdata->id, rate); + break; + case ACLK_BUS: + case HCLK_BUS: + case PCLK_BUS: + res = bus_set_clk(priv, pdata->id, rate); + break; + case ACLK_PERI: + case HCLK_PERI: + case PCLK_PERI: + res = peri_set_clk(priv, pdata->id, rate); + break; + case HCLK_AUDIO: + case PCLK_AUDIO: + res = audio_set_clk(priv, pdata->id, rate); + break; + case SCLK_CRYPTO: + case SCLK_CRYPTO_APK: + res = crypto_set_clk(priv, pdata->id, rate); + break; + case SCLK_UART0: + case SCLK_UART1: + case SCLK_UART2: + case SCLK_UART3: + case SCLK_UART4: + rate = uart_set_rate(priv, pdata->id, rate); + default: + return -RT_EINVAL; + } + + return res; +} + +static rt_err_t rk_clk_wait_lock(struct rk_clk_platform_data *pdata) +{ + rt_err_t err = RT_EOK; + rt_uint32_t count = 0, pllcon; + + /* + * Lock time typical 250, max 500 input clock cycles @24MHz, So define a + * very safe maximum of 1000us, meaning 24000 cycles. + */ + do { + pllcon = HWREG32(pdata->base + PLL_CON(1)); + rt_hw_us_delay(100); + ++count; + } while (pllcon & PLLCON1_LOCK_STATUS && count < 10); + + if (count >= 10) + { + err = -RT_ETIMEOUT; + } + + return err; +} + +static rt_err_t mac_set_parent(struct rk_clk_platform_data *pdata, + struct rk_clk_platform_data *ppdata, struct rk_clk *rk_clk) +{ + struct rk_clk_priv *priv = &rk_clk->clk_info; + struct rk_cru *cru = priv->cru; + + /* + * If the requested parent is in the same clock-controller and + * the id is SCLK_MAC_SRC, switch to the internal clock. + */ + if (ppdata->id == SCLK_MAC_SRC) + { + LOG_D("MAC switching RMII to %s", "SCLK_MAC"); + rk_clrreg(&cru->clksel_con[43], RT_BIT(14)); + } + else + { + LOG_D("MAC switching RMII to %s", "CLKIN"); + rk_setreg(&cru->clksel_con[43], RT_BIT(14)); + } + + return 0; +} + +static rt_err_t mmc_set_phase(struct rk_clk_platform_data *pdata, + struct rk_clk *rk_clk, rt_uint32_t degrees) +{ + void *reg; + rt_ubase_t rate; + struct rk_clk_priv *priv = &rk_clk->clk_info; + struct rk_cru *cru = priv->cru; + + rate = rk_clk_get_rate(pdata, rk_clk); + + switch (pdata->id) + { + case SCLK_SDMMC_DRV: + reg = &cru->sdmmc_con[0]; + break; + + case SCLK_SDMMC_SAMPLE: + reg = &cru->sdmmc_con[1]; + break; + + case SCLK_SDIO_DRV: + reg = &cru->sdio_con[0]; + break; + + case SCLK_SDIO_SAMPLE: + reg = &cru->sdio_con[1]; + break; + + case SCLK_EMMC_DRV: + reg = &cru->emmc_con[0]; + break; + + case SCLK_EMMC_SAMPLE: + reg = &cru->emmc_con[1]; + break; + } + + return rk_clk_mmc_set_phase(rate, reg, 1, degrees); +} + +static rt_base_t mmc_get_phase(struct rk_clk_platform_data *pdata, + struct rk_clk *rk_clk) +{ + void *reg; + rt_ubase_t rate; + struct rk_clk_priv *priv = &rk_clk->clk_info; + struct rk_cru *cru = priv->cru; + + rate = rk_clk_get_rate(pdata, rk_clk); + + switch (pdata->id) + { + case SCLK_SDMMC_DRV: + reg = &cru->sdmmc_con[0]; + break; + + case SCLK_SDMMC_SAMPLE: + reg = &cru->sdmmc_con[1]; + break; + + case SCLK_SDIO_DRV: + reg = &cru->sdio_con[0]; + break; + + case SCLK_SDIO_SAMPLE: + reg = &cru->sdio_con[1]; + break; + + case SCLK_EMMC_DRV: + reg = &cru->emmc_con[0]; + break; + + case SCLK_EMMC_SAMPLE: + reg = &cru->emmc_con[1]; + break; + } + + return rk_clk_mmc_get_phase(rate, reg, 1); +} + +static rt_err_t rk3308_clk_init(struct rt_clk *clk, void *fw_data) +{ + struct rk_clk *rk_clk = raw_to_rk_clk(clk->clk_np); + struct rt_ofw_cell_args *args = fw_data; + struct rk_clk_platform_data *pdata; + rt_uint32_t clk_id = args->args[0]; + rt_ubase_t reg_base; + + pdata = &rk_clk->pdata[clk_id]; + reg_base = (rt_ubase_t)rk_clk->clk_info.cru; + + switch (clk_id) + { + case PLL_APLL: + case ARMCLK: + reg_base += pll_clks[apll].con_offset; + break; + case PLL_DPLL: + reg_base += pll_clks[dpll].con_offset; + break; + case PLL_VPLL0: + reg_base += pll_clks[vpll0].con_offset; + break; + case PLL_VPLL1: + reg_base += pll_clks[vpll1].con_offset; + break; + default: + reg_base = RT_NULL; + break; + } + + pdata->id = clk_id; + pdata->base = (void *)reg_base; + + clk->rate = rk_clk_get_rate(pdata, rk_clk); + clk->priv = pdata; + + rk_clk_set_default_rates(clk, clk->clk_np->ops->set_rate, clk_id); + + return RT_EOK; +} + +static rt_err_t rk3308_clk_enable(struct rt_clk *clk) +{ + struct rk_clk_platform_data *pdata = clk->priv; + struct rk_clk *rk_clk = raw_to_rk_clk(clk->clk_np); + struct rk_cru *cru = rk_clk->clk_info.cru; + + if (pdata->base) + { + HWREG32(pdata->base + PLL_CON(1)) = HIWORD_UPDATE(0, PLLCON1_PWRDOWN, 0); + + rk_clk_wait_lock(pdata); + } + else + { + struct rk_clk_gate *gate = &clk_gates[pdata->id]; + + rk_clrreg(&cru->clkgate_con[gate->con_idx], RT_BIT(gate->con_bit)); + } + + return RT_EOK; +} + +static void rk3308_clk_disable(struct rt_clk *clk) +{ + struct rk_clk_platform_data *pdata = clk->priv; + struct rk_clk *rk_clk = raw_to_rk_clk(clk->clk_np); + struct rk_cru *cru = rk_clk->clk_info.cru; + + if (pdata->base) + { + HWREG32(pdata->base + PLL_CON(1)) = HIWORD_UPDATE(PLLCON1_PWRDOWN, PLLCON1_PWRDOWN, 0); + } + else + { + struct rk_clk_gate *gate = &clk_gates[pdata->id]; + + rk_setreg(&cru->clkgate_con[gate->con_idx], RT_BIT(gate->con_bit)); + } +} + +static rt_bool_t rk3308_clk_is_enabled(struct rt_clk *clk) +{ + struct rk_clk_platform_data *pdata = clk->priv; + + if (pdata->base) + { + rt_uint32_t pllcon = HWREG32(pdata->base + PLL_CON(1)); + + return !(pllcon & PLLCON1_PWRDOWN); + } + + return RT_TRUE; +} + +static rt_err_t rk3308_clk_set_rate(struct rt_clk *clk, rt_ubase_t rate, rt_ubase_t parent_rate) +{ + rt_ubase_t res_rate; + struct rk_clk_platform_data *pdata = clk->priv; + struct rk_clk *rk_clk = raw_to_rk_clk(clk->clk_np); + + res_rate = rk_clk_set_rate(pdata, rk_clk, rate); + + if ((rt_base_t)res_rate > 0) + { + clk->rate = res_rate; + } + + return (rt_ubase_t)res_rate > 0 ? RT_EOK : (rt_err_t)res_rate; +} + +static rt_err_t rk3308_clk_set_parent(struct rt_clk *clk, struct rt_clk *parent) +{ + struct rk_clk_platform_data *pdata = clk->priv, *ppdata = parent->priv; + struct rk_clk *rk_clk = raw_to_rk_clk(clk->clk_np); + + switch (pdata->id) + { + case SCLK_MAC: + return mac_set_parent(pdata, ppdata, rk_clk); + + default: + break; + } + + return -RT_EINVAL; +} + +static rt_err_t rk3308_clk_set_phase(struct rt_clk *clk, int degrees) +{ + rt_err_t res; + struct rk_clk_platform_data *pdata = clk->priv; + struct rk_clk *rk_clk = raw_to_rk_clk(clk->clk_np); + + switch (pdata->id) + { + case SCLK_EMMC_SAMPLE: + case SCLK_SDMMC_SAMPLE: + case SCLK_SDIO_SAMPLE: + case SCLK_SDMMC_DRV: + case SCLK_EMMC_DRV: + case SCLK_SDIO_DRV: + res = mmc_set_phase(pdata, rk_clk, degrees); + break; + + default: + return -RT_EINVAL; + } + + return res; +} + +static rt_base_t rk3308_clk_get_phase(struct rt_clk *clk) +{ + rt_base_t res; + struct rk_clk_platform_data *pdata = clk->priv; + struct rk_clk *rk_clk = raw_to_rk_clk(clk->clk_np); + + switch (pdata->id) + { + case SCLK_EMMC_SAMPLE: + case SCLK_SDMMC_SAMPLE: + case SCLK_SDIO_SAMPLE: + case SCLK_SDMMC_DRV: + case SCLK_EMMC_DRV: + case SCLK_SDIO_DRV: + res = mmc_get_phase(pdata, rk_clk); + break; + + default: + return -RT_EINVAL; + } + + return res; +} + +static rt_base_t rk3308_clk_round_rate(struct rt_clk *clk, rt_ubase_t drate, + rt_ubase_t *prate) +{ + return rk_clk_pll_round_rate(pll_rates, RT_ARRAY_SIZE(pll_rates), drate, prate); +} + +static const struct rt_clk_ops rk3308_clk_ops = +{ + .init = rk3308_clk_init, + .enable = rk3308_clk_enable, + .disable = rk3308_clk_disable, + .is_enabled = rk3308_clk_is_enabled, + .set_rate = rk3308_clk_set_rate, + .set_parent = rk3308_clk_set_parent, + .set_phase = rk3308_clk_set_phase, + .get_phase = rk3308_clk_get_phase, + .round_rate = rk3308_clk_round_rate, +}; + +static void rk3308_clk_type_init(struct rk_clk *rk_clk, struct rt_ofw_node *np) +{ + rt_ubase_t cpu_freq = APLL_HZ; + struct rk_clk_priv *priv = &rk_clk->clk_info; + const char *rockchip_cpu_freq = rt_ofw_bootargs_select("rockchip.cpu_freq=", 0); + + priv->cru = (struct rk_cru *)rk_clk->base; + + if (rockchip_cpu_freq) + { + cpu_freq = atol(rockchip_cpu_freq); + } + + if (rk_pll_get_rate(&pll_clks[apll], priv->cru) != cpu_freq) + { + if (armclk_set_clk(priv, cpu_freq) < 0) + { + LOG_E("Failed to set armclk rate to %u, error = %s", cpu_freq); + } + } + + clk_get_pll_rate(priv); + + bus_set_clk(priv, ACLK_BUS, BUS_ACLK_HZ); + bus_set_clk(priv, HCLK_BUS, BUS_HCLK_HZ); + bus_set_clk(priv, PCLK_BUS, BUS_PCLK_HZ); + + peri_set_clk(priv, ACLK_PERI, PERI_ACLK_HZ); + peri_set_clk(priv, HCLK_PERI, PERI_HCLK_HZ); + peri_set_clk(priv, PCLK_PERI, PERI_PCLK_HZ); + + audio_set_clk(priv, HCLK_AUDIO, AUDIO_HCLK_HZ); + audio_set_clk(priv, PCLK_AUDIO, AUDIO_PCLK_HZ); +} + +static rt_err_t clk_rk3308_probe(struct rt_platform_device *pdev) +{ + rt_err_t err = RT_EOK; + void *softrst_regs; + struct rt_ofw_node *np = pdev->parent.ofw_node; + struct rk_clk *rk_clk = rt_calloc(1, sizeof(*rk_clk)); + + if (!rk_clk) + { + return -RT_ENOMEM; + } + + rk_clk->base = rt_ofw_iomap(np, 0); + + if (!rk_clk->base) + { + err = -RT_EIO; + goto _fail; + } + + rk3308_clk_type_init(rk_clk, np); + softrst_regs = &rk_clk->clk_info.cru->softrst_con; + + rk_clk->parent.parent.ops = &rk3308_clk_ops; + + if ((err = rt_clk_register(&rk_clk->parent.parent, RT_NULL))) + { + goto _fail; + } + + if ((err = rk_register_softrst(&rk_clk->parent.rstcer, np, + softrst_regs, ROCKCHIP_SOFTRST_HIWORD_MASK))) + { + goto _fail; + } + + rt_ofw_data(np) = &rk_clk->parent; + + return RT_EOK; + +_fail: + if (rk_clk->base) + { + rt_iounmap(rk_clk->base); + } + + rt_free(rk_clk); + + return err; +} + +static const struct rt_ofw_node_id clk_rk3308_ofw_ids[] = +{ + { .compatible = "rockchip,rk3308-cru", }, + { /* sentinel */ } +}; + +static struct rt_platform_driver clk_rk3308_driver = +{ + .name = "clk-rk3308", + .ids = clk_rk3308_ofw_ids, + + .probe = clk_rk3308_probe, +}; + +static int clk_rk3308_register(void) +{ + rt_platform_driver_register(&clk_rk3308_driver); + + return 0; +} +INIT_SUBSYS_EXPORT(clk_rk3308_register); diff --git a/components/drivers/clk/rockchip/clk-rk3568.c b/components/drivers/clk/rockchip/clk-rk3568.c new file mode 100644 index 000000000000..2394554f9c71 --- /dev/null +++ b/components/drivers/clk/rockchip/clk-rk3568.c @@ -0,0 +1,4777 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-3-08 GuEe-GUI the first version + */ + +#include "clk.h" + +#define DBG_TAG "clk.rk3568" +#define DBG_LVL DBG_INFO +#include + +#include + +#define APLL_HZ (816 * MHZ) +#define GPLL_HZ (1188 * MHZ) +#define CPLL_HZ (1000 * MHZ) +#define PPLL_HZ (100 * MHZ) + +struct rk_pll +{ + rt_uint32_t con0; + rt_uint32_t con1; + rt_uint32_t con2; + rt_uint32_t con3; + rt_uint32_t con4; + rt_uint32_t reserved0[3]; +}; + +struct rk_pmucru +{ + struct rk_pll pll[2]; + rt_uint32_t reserved0[16]; + rt_uint32_t mode_con00; + rt_uint32_t reserved1[31]; + rt_uint32_t pmu_clksel_con[10]; + rt_uint32_t reserved2[22]; + rt_uint32_t pmu_clkgate_con[3]; + rt_uint32_t reserved3[29]; + rt_uint32_t pmu_softrst_con[1]; +}; + +struct rk_cru +{ + struct rk_pll pll[6]; + rt_uint32_t mode_con00; + rt_uint32_t misc_con[3]; + rt_uint32_t glb_cnt_th; + rt_uint32_t glb_srst_fst; + rt_uint32_t glb_srsr_snd; + rt_uint32_t glb_rst_con; + rt_uint32_t glb_rst_st; + rt_uint32_t reserved0[7]; + rt_uint32_t clksel_con[85]; + rt_uint32_t reserved1[43]; + rt_uint32_t clkgate_con[36]; + rt_uint32_t reserved2[28]; + rt_uint32_t softrst_con[30]; + rt_uint32_t reserved3[2]; + rt_uint32_t ssgtbl[32]; + rt_uint32_t reserved4[32]; + rt_uint32_t sdmmc0_con[2]; + rt_uint32_t sdmmc1_con[2]; + rt_uint32_t sdmmc2_con[2]; + rt_uint32_t emmc_con[2]; +}; + +struct rk_pmuclk_priv +{ + struct rk_pmucru *pmucru; + rt_ubase_t ppll_hz; + rt_ubase_t hpll_hz; +}; + +struct rk_clk_priv +{ + struct rk_cru *cru; + rt_ubase_t ppll_hz; + rt_ubase_t hpll_hz; + rt_ubase_t gpll_hz; + rt_ubase_t cpll_hz; + rt_ubase_t npll_hz; + rt_ubase_t vpll_hz; + rt_ubase_t armclk_hz; + rt_ubase_t armclk_enter_hz; + rt_ubase_t armclk_init_hz; + rt_bool_t sync_kernel; + rt_bool_t set_armclk_rate; +}; + +struct rk_clk_platform_data +{ + rt_uint32_t id; + void *base; +}; + +enum rk_clk_type +{ + rk_clk_type_clk, + rk_clk_type_pmuclk, +}; + +struct rk_clk +{ + struct rt_reset_controller_clk_node parent; + + void *base; + enum rk_clk_type type; + + union + { + struct rk_clk_priv clk_info; + struct rk_pmuclk_priv pmuclk_info; + }; + + struct rk_clk_platform_data pdata[]; +}; + +#define raw_to_rk_clk(raw) rt_container_of(raw, struct rk_clk, parent) + +#define PMU_MODE 0x80 +#define PMU_PLL_CON(x) ((x) * 0x4) +#define PLL_CON(x) ((x) * 0x4) +#define MODE_CON 0xc0 + +enum pmu_plls +{ + ppll, hpll, +}; + +enum plls +{ + apll, dpll, gpll, cpll, npll, vpll, +}; + +enum +{ + /* CRU_PMU_CLK_SEL0_CON */ + RTC32K_SEL_SHIFT = 6, + RTC32K_SEL_MASK = 0x3 << RTC32K_SEL_SHIFT, + RTC32K_SEL_PMUPVTM = 0, + RTC32K_SEL_OSC1_32K, + RTC32K_SEL_OSC0_DIV32K, + + /* CRU_PMU_CLK_SEL1_CON */ + RTC32K_FRAC_NUMERATOR_SHIFT = 16, + RTC32K_FRAC_NUMERATOR_MASK = 0xffff << 16, + RTC32K_FRAC_DENOMINATOR_SHIFT = 0, + RTC32K_FRAC_DENOMINATOR_MASK = 0xffff, + + /* CRU_PMU_CLK_SEL2_CON */ + PCLK_PDPMU_SEL_SHIFT = 15, + PCLK_PDPMU_SEL_MASK = 1 << PCLK_PDPMU_SEL_SHIFT, + PCLK_PDPMU_SEL_PPLL = 0, + PCLK_PDPMU_SEL_GPLL, + PCLK_PDPMU_DIV_SHIFT = 0, + PCLK_PDPMU_DIV_MASK = 0x1f, + + /* CRU_PMU_CLK_SEL3_CON */ + CLK_I2C0_DIV_SHIFT = 0, + CLK_I2C0_DIV_MASK = 0x7f, + + /* PMUCRU_PMUCLKSEL_CON04 */ + CLK_UART0_SEL_SHIFT = 10, + CLK_UART0_SEL_MASK = 0x3 << CLK_UART0_SEL_SHIFT, + CLK_UART0_SEL_DIV = 0, + CLK_UART0_SEL_FRACDIV, + CLK_UART0_SEL_XIN24M, + CLK_UART0_DIV_SEL_SHIFT = 8, + CLK_UART0_DIV_SEL_MASK = 0x3 << CLK_UART0_DIV_SEL_SHIFT, + CLK_UART0_SRC_SEL_PPLL = 0, + CLK_UART0_SRC_SEL_480M, + CLK_UART0_SRC_SEL_CPLL, + CLK_UART0_SRC_SEL_GPLL, + CLK_UART0_DIV_DIV_SHIFT = 0, + CLK_UART0_DIV_DIV_MASK = 0x3f << CLK_UART0_DIV_DIV_SHIFT, + + /* PMUCRU_PMUCLKSEL_CON05 */ + CLK_UART0_FRAC_NUMERATOR_SHIFT = 16, + CLK_UART0_FRAC_NUMERATOR_MASK = 0xffff << 16, + CLK_UART0_FRAC_DENOMINATOR_SHIFT= 0, + CLK_UART0_FRAC_DENOMINATOR_MASK = 0xffff, + + /* PMUCRU_PMUCLKSEL_CON09 */ + CLK_PCIE_PHY2_REF_SEL_SHIFT = 11, + CLK_PCIE_PHY2_REF_SEL_MASK = 1 << CLK_PCIE_PHY2_REF_SEL_SHIFT, + CLK_PCIE_PHY1_REF_SEL_SHIFT = 7, + CLK_PCIE_PHY1_REF_SEL_MASK = 1 << CLK_PCIE_PHY1_REF_SEL_SHIFT, + CLK_PCIE_PHY0_REF_SEL_SHIFT = 3, + CLK_PCIE_PHY0_REF_SEL_MASK = 1 << CLK_PCIE_PHY0_REF_SEL_SHIFT, + CLK_PCIE_PHY_REF_SEL_24M = 0, + CLK_PCIE_PHY_REF_SEL_PPLL, + CLK_PCIE_PHY2_PLL_DIV_SHIFT = 8, + CLK_PCIE_PHY2_PLL_DIV_MASK = 7 << CLK_PCIE_PHY2_PLL_DIV_SHIFT, + CLK_PCIE_PHY1_PLL_DIV_SHIFT = 4, + CLK_PCIE_PHY1_PLL_DIV_MASK = 7 << CLK_PCIE_PHY1_PLL_DIV_SHIFT, + CLK_PCIE_PHY0_PLL_DIV_SHIFT = 0, + CLK_PCIE_PHY0_PLL_DIV_MASK = 7 << CLK_PCIE_PHY0_PLL_DIV_SHIFT, + + /* CRU_PMU_CLK_SEL6_CON */ + CLK_PWM0_SEL_SHIFT = 7, + CLK_PWM0_SEL_MASK = 1 << CLK_PWM0_SEL_SHIFT, + CLK_PWM0_SEL_XIN24M = 0, + CLK_PWM0_SEL_PPLL, + CLK_PWM0_DIV_SHIFT = 0, + CLK_PWM0_DIV_MASK = 0x7f, + + /* CRU_CLK_SEL0_CON */ + CLK_CORE_PRE_SEL_SHIFT = 7, + CLK_CORE_PRE_SEL_MASK = 1 << CLK_CORE_PRE_SEL_SHIFT, + CLK_CORE_PRE_SEL_SRC = 0, + CLK_CORE_PRE_SEL_APLL, + + /* CRU_CLK_SEL2_CON */ + SCLK_CORE_PRE_SEL_SHIFT = 15, + SCLK_CORE_PRE_SEL_MASK = 1 << SCLK_CORE_PRE_SEL_SHIFT, + SCLK_CORE_PRE_SEL_SRC = 0, + SCLK_CORE_PRE_SEL_NPLL, + SCLK_CORE_SRC_SEL_SHIFT = 8, + SCLK_CORE_SRC_SEL_MASK = 3 << SCLK_CORE_SRC_SEL_SHIFT, + SCLK_CORE_SRC_SEL_APLL = 0, + SCLK_CORE_SRC_SEL_GPLL, + SCLK_CORE_SRC_SEL_NPLL, + SCLK_CORE_SRC_DIV_SHIFT = 0, + SCLK_CORE_SRC_DIV_MASK = 0x1f << SCLK_CORE_SRC_DIV_SHIFT, + + /* CRU_CLK_SEL3_CON */ + GICCLK_CORE_DIV_SHIFT = 8, + GICCLK_CORE_DIV_MASK = 0x1f << GICCLK_CORE_DIV_SHIFT, + ATCLK_CORE_DIV_SHIFT = 0, + ATCLK_CORE_DIV_MASK = 0x1f << ATCLK_CORE_DIV_SHIFT, + + /* CRU_CLK_SEL4_CON */ + PERIPHCLK_CORE_PRE_DIV_SHIFT = 8, + PERIPHCLK_CORE_PRE_DIV_MASK = 0x1f << PERIPHCLK_CORE_PRE_DIV_SHIFT, + PCLK_CORE_PRE_DIV_SHIFT = 0, + PCLK_CORE_PRE_DIV_MASK = 0x1f << PCLK_CORE_PRE_DIV_SHIFT, + + /* CRU_CLK_SEL5_CON */ + ACLK_CORE_NIU2BUS_SEL_SHIFT = 14, + ACLK_CORE_NIU2BUS_SEL_MASK = 0x3 << ACLK_CORE_NIU2BUS_SEL_SHIFT, + ACLK_CORE_NDFT_DIV_SHIFT = 8, + ACLK_CORE_NDFT_DIV_MASK = 0x1f << ACLK_CORE_NDFT_DIV_SHIFT, + + /* CRU_CLK_SEL10_CON */ + HCLK_PERIMID_SEL_SHIFT = 6, + HCLK_PERIMID_SEL_MASK = 3 << HCLK_PERIMID_SEL_SHIFT, + HCLK_PERIMID_SEL_150M = 0, + HCLK_PERIMID_SEL_100M, + HCLK_PERIMID_SEL_75M, + HCLK_PERIMID_SEL_24M, + ACLK_PERIMID_SEL_SHIFT = 4, + ACLK_PERIMID_SEL_MASK = 3 << ACLK_PERIMID_SEL_SHIFT, + ACLK_PERIMID_SEL_300M = 0, + ACLK_PERIMID_SEL_200M, + ACLK_PERIMID_SEL_100M, + ACLK_PERIMID_SEL_24M, + + /* CRU_CLK_SEL27_CON */ + CLK_CRYPTO_PKA_SEL_SHIFT = 6, + CLK_CRYPTO_PKA_SEL_MASK = 3 << CLK_CRYPTO_PKA_SEL_SHIFT, + CLK_CRYPTO_PKA_SEL_300M = 0, + CLK_CRYPTO_PKA_SEL_200M, + CLK_CRYPTO_PKA_SEL_100M, + CLK_CRYPTO_CORE_SEL_SHIFT = 4, + CLK_CRYPTO_CORE_SEL_MASK = 3 << CLK_CRYPTO_CORE_SEL_SHIFT, + CLK_CRYPTO_CORE_SEL_200M = 0, + CLK_CRYPTO_CORE_SEL_150M, + CLK_CRYPTO_CORE_SEL_100M, + HCLK_SECURE_FLASH_SEL_SHIFT = 2, + HCLK_SECURE_FLASH_SEL_MASK = 3 << HCLK_SECURE_FLASH_SEL_SHIFT, + HCLK_SECURE_FLASH_SEL_150M = 0, + HCLK_SECURE_FLASH_SEL_100M, + HCLK_SECURE_FLASH_SEL_75M, + HCLK_SECURE_FLASH_SEL_24M, + ACLK_SECURE_FLASH_SEL_SHIFT = 0, + ACLK_SECURE_FLASH_SEL_MASK = 3 << ACLK_SECURE_FLASH_SEL_SHIFT, + ACLK_SECURE_FLASH_SEL_200M = 0, + ACLK_SECURE_FLASH_SEL_150M, + ACLK_SECURE_FLASH_SEL_100M, + ACLK_SECURE_FLASH_SEL_24M, + + /* CRU_CLK_SEL28_CON */ + CCLK_EMMC_SEL_SHIFT = 12, + CCLK_EMMC_SEL_MASK = 7 << CCLK_EMMC_SEL_SHIFT, + CCLK_EMMC_SEL_24M = 0, + CCLK_EMMC_SEL_200M, + CCLK_EMMC_SEL_150M, + CCLK_EMMC_SEL_100M, + CCLK_EMMC_SEL_50M, + CCLK_EMMC_SEL_375K, + BCLK_EMMC_SEL_SHIFT = 8, + BCLK_EMMC_SEL_MASK = 3 << BCLK_EMMC_SEL_SHIFT, + BCLK_EMMC_SEL_200M = 0, + BCLK_EMMC_SEL_150M, + BCLK_EMMC_SEL_125M, + SCLK_SFC_SEL_SHIFT = 4, + SCLK_SFC_SEL_MASK = 7 << SCLK_SFC_SEL_SHIFT, + SCLK_SFC_SEL_24M = 0, + SCLK_SFC_SEL_50M, + SCLK_SFC_SEL_75M, + SCLK_SFC_SEL_100M, + SCLK_SFC_SEL_125M, + SCLK_SFC_SEL_150M, + NCLK_NANDC_SEL_SHIFT = 0, + NCLK_NANDC_SEL_MASK = 3 << NCLK_NANDC_SEL_SHIFT, + NCLK_NANDC_SEL_200M = 0, + NCLK_NANDC_SEL_150M, + NCLK_NANDC_SEL_100M, + NCLK_NANDC_SEL_24M, + + /* CRU_CLK_SEL30_CON */ + CLK_SDMMC1_SEL_SHIFT = 12, + CLK_SDMMC1_SEL_MASK = 7 << CLK_SDMMC1_SEL_SHIFT, + CLK_SDMMC0_SEL_SHIFT = 8, + CLK_SDMMC0_SEL_MASK = 7 << CLK_SDMMC0_SEL_SHIFT, + CLK_SDMMC_SEL_24M = 0, + CLK_SDMMC_SEL_400M, + CLK_SDMMC_SEL_300M, + CLK_SDMMC_SEL_100M, + CLK_SDMMC_SEL_50M, + CLK_SDMMC_SEL_750K, + + /* CRU_CLK_SEL31_CON */ + CLK_MAC0_OUT_SEL_SHIFT = 14, + CLK_MAC0_OUT_SEL_MASK = 3 << CLK_MAC0_OUT_SEL_SHIFT, + CLK_MAC0_OUT_SEL_125M = 0, + CLK_MAC0_OUT_SEL_50M, + CLK_MAC0_OUT_SEL_25M, + CLK_MAC0_OUT_SEL_24M, + CLK_GMAC0_PTP_REF_SEL_SHIFT = 12, + CLK_GMAC0_PTP_REF_SEL_MASK = 3 << CLK_GMAC0_PTP_REF_SEL_SHIFT, + CLK_GMAC0_PTP_REF_SEL_62_5M = 0, + CLK_GMAC0_PTP_REF_SEL_100M, + CLK_GMAC0_PTP_REF_SEL_50M, + CLK_GMAC0_PTP_REF_SEL_24M, + CLK_MAC0_2TOP_SEL_SHIFT = 8, + CLK_MAC0_2TOP_SEL_MASK = 3 << CLK_MAC0_2TOP_SEL_SHIFT, + CLK_MAC0_2TOP_SEL_125M = 0, + CLK_MAC0_2TOP_SEL_50M, + CLK_MAC0_2TOP_SEL_25M, + CLK_MAC0_2TOP_SEL_PPLL, + RGMII0_CLK_SEL_SHIFT = 4, + RGMII0_CLK_SEL_MASK = 3 << RGMII0_CLK_SEL_SHIFT, + RGMII0_CLK_SEL_125M = 0, + RGMII0_CLK_SEL_125M_1, + RGMII0_CLK_SEL_2_5M, + RGMII0_CLK_SEL_25M, + RMII0_CLK_SEL_SHIFT = 3, + RMII0_CLK_SEL_MASK = 1 << RMII0_CLK_SEL_SHIFT, + RMII0_CLK_SEL_2_5M = 0, + RMII0_CLK_SEL_25M, + RMII0_EXTCLK_SEL_SHIFT = 2, + RMII0_EXTCLK_SEL_MASK = 1 << RMII0_EXTCLK_SEL_SHIFT, + RMII0_EXTCLK_SEL_MAC0_TOP = 0, + RMII0_EXTCLK_SEL_IO, + RMII0_MODE_SHIFT = 0, + RMII0_MODE_MASK = 3 << RMII0_MODE_SHIFT, + RMII0_MODE_SEL_RGMII = 0, + RMII0_MODE_SEL_RMII, + RMII0_MODE_SEL_GMII, + + /* CRU_CLK_SEL32_CON */ + CLK_SDMMC2_SEL_SHIFT = 8, + CLK_SDMMC2_SEL_MASK = 7 << CLK_SDMMC2_SEL_SHIFT, + + /* CRU_CLK_SEL38_CON */ + ACLK_VOP_PRE_SEL_SHIFT = 6, + ACLK_VOP_PRE_SEL_MASK = 3 << ACLK_VOP_PRE_SEL_SHIFT, + ACLK_VOP_PRE_SEL_CPLL = 0, + ACLK_VOP_PRE_SEL_GPLL, + ACLK_VOP_PRE_SEL_HPLL, + ACLK_VOP_PRE_SEL_VPLL, + ACLK_VOP_PRE_DIV_SHIFT = 0, + ACLK_VOP_PRE_DIV_MASK = 0x1f << ACLK_VOP_PRE_DIV_SHIFT, + + /* CRU_CLK_SEL39_CON */ + DCLK0_VOP_SEL_SHIFT = 10, + DCLK0_VOP_SEL_MASK = 3 << DCLK0_VOP_SEL_SHIFT, + DCLK_VOP_SEL_HPLL = 0, + DCLK_VOP_SEL_VPLL, + DCLK_VOP_SEL_GPLL, + DCLK_VOP_SEL_CPLL, + DCLK0_VOP_DIV_SHIFT = 0, + DCLK0_VOP_DIV_MASK = 0xff << DCLK0_VOP_DIV_SHIFT, + + /* CRU_CLK_SEL40_CON */ + DCLK1_VOP_SEL_SHIFT = 10, + DCLK1_VOP_SEL_MASK = 3 << DCLK1_VOP_SEL_SHIFT, + DCLK1_VOP_DIV_SHIFT = 0, + DCLK1_VOP_DIV_MASK = 0xff << DCLK1_VOP_DIV_SHIFT, + + /* CRU_CLK_SEL41_CON */ + DCLK2_VOP_SEL_SHIFT = 10, + DCLK2_VOP_SEL_MASK = 3 << DCLK2_VOP_SEL_SHIFT, + DCLK2_VOP_DIV_SHIFT = 0, + DCLK2_VOP_DIV_MASK = 0xff << DCLK2_VOP_DIV_SHIFT, + + /* CRU_CLK_SEL43_CON */ + DCLK_EBC_SEL_SHIFT = 6, + DCLK_EBC_SEL_MASK = 3 << DCLK_EBC_SEL_SHIFT, + DCLK_EBC_SEL_GPLL_400M = 0, + DCLK_EBC_SEL_CPLL_333M, + DCLK_EBC_SEL_GPLL_200M, + + /* CRU_CLK_SEL47_CON */ + ACLK_RKVDEC_SEL_SHIFT = 7, + ACLK_RKVDEC_SEL_MASK = 1 << ACLK_RKVDEC_SEL_SHIFT, + ACLK_RKVDEC_SEL_GPLL = 0, + ACLK_RKVDEC_SEL_CPLL, + ACLK_RKVDEC_DIV_SHIFT = 0, + ACLK_RKVDEC_DIV_MASK = 0x1f << ACLK_RKVDEC_DIV_SHIFT, + + /* CRU_CLK_SEL49_CON */ + CLK_RKVDEC_CORE_SEL_SHIFT = 14, + CLK_RKVDEC_CORE_SEL_MASK = 0x3 << CLK_RKVDEC_CORE_SEL_SHIFT, + CLK_RKVDEC_CORE_SEL_GPLL = 0, + CLK_RKVDEC_CORE_SEL_CPLL, + CLK_RKVDEC_CORE_SEL_NPLL, + CLK_RKVDEC_CORE_SEL_VPLL, + CLK_RKVDEC_CORE_DIV_SHIFT = 8, + CLK_RKVDEC_CORE_DIV_MASK = 0x1f << CLK_RKVDEC_CORE_DIV_SHIFT, + + /* CRU_CLK_SEL50_CON */ + PCLK_BUS_SEL_SHIFT = 4, + PCLK_BUS_SEL_MASK = 3 << PCLK_BUS_SEL_SHIFT, + PCLK_BUS_SEL_100M = 0, + PCLK_BUS_SEL_75M, + PCLK_BUS_SEL_50M, + PCLK_BUS_SEL_24M, + ACLK_BUS_SEL_SHIFT = 0, + ACLK_BUS_SEL_MASK = 3 << ACLK_BUS_SEL_SHIFT, + ACLK_BUS_SEL_200M = 0, + ACLK_BUS_SEL_150M, + ACLK_BUS_SEL_100M, + ACLK_BUS_SEL_24M, + + /* CRU_CLK_SEL51_CON */ + CLK_TSADC_DIV_SHIFT = 8, + CLK_TSADC_DIV_MASK = 0x7f << CLK_TSADC_DIV_SHIFT, + CLK_TSADC_TSEN_SEL_SHIFT = 4, + CLK_TSADC_TSEN_SEL_MASK = 0x3 << CLK_TSADC_TSEN_SEL_SHIFT, + CLK_TSADC_TSEN_SEL_24M = 0, + CLK_TSADC_TSEN_SEL_100M, + CLK_TSADC_TSEN_SEL_CPLL_100M, + CLK_TSADC_TSEN_DIV_SHIFT = 0, + CLK_TSADC_TSEN_DIV_MASK = 0x7 << CLK_TSADC_TSEN_DIV_SHIFT, + + /* CRU_CLK_SEL52_CON */ + CLK_UART_SEL_SHIFT = 12, + CLK_UART_SEL_MASK = 0x3 << CLK_UART_SEL_SHIFT, + CLK_UART_SEL_SRC = 0, + CLK_UART_SEL_FRAC, + CLK_UART_SEL_XIN24M, + CLK_UART_SRC_SEL_SHIFT = 8, + CLK_UART_SRC_SEL_MASK = 0x3 << CLK_UART_SRC_SEL_SHIFT, + CLK_UART_SRC_SEL_GPLL = 0, + CLK_UART_SRC_SEL_CPLL, + CLK_UART_SRC_SEL_480M, + CLK_UART_SRC_DIV_SHIFT = 0, + CLK_UART_SRC_DIV_MASK = 0x3f << CLK_UART_SRC_DIV_SHIFT, + + /* CRU_CLK_SEL53_CON */ + CLK_UART_FRAC_NUMERATOR_SHIFT = 16, + CLK_UART_FRAC_NUMERATOR_MASK = 0xffff << 16, + CLK_UART_FRAC_DENOMINATOR_SHIFT = 0, + CLK_UART_FRAC_DENOMINATOR_MASK = 0xffff, + + /* CRU_CLK_SEL71_CON */ + CLK_I2C_SEL_SHIFT = 8, + CLK_I2C_SEL_MASK = 3 << CLK_I2C_SEL_SHIFT, + CLK_I2C_SEL_200M = 0, + CLK_I2C_SEL_100M, + CLK_I2C_SEL_24M, + CLK_I2C_SEL_CPLL_100M, + + /* CRU_CLK_SEL72_CON */ + CLK_PWM3_SEL_SHIFT = 12, + CLK_PWM3_SEL_MASK = 3 << CLK_PWM3_SEL_SHIFT, + CLK_PWM2_SEL_SHIFT = 10, + CLK_PWM2_SEL_MASK = 3 << CLK_PWM2_SEL_SHIFT, + CLK_PWM1_SEL_SHIFT = 8, + CLK_PWM1_SEL_MASK = 3 << CLK_PWM1_SEL_SHIFT, + CLK_PWM_SEL_100M = 0, + CLK_PWM_SEL_24M, + CLK_PWM_SEL_CPLL_100M, + CLK_SPI3_SEL_SHIFT = 6, + CLK_SPI3_SEL_MASK = 3 << CLK_SPI3_SEL_SHIFT, + CLK_SPI2_SEL_SHIFT = 4, + CLK_SPI2_SEL_MASK = 3 << CLK_SPI2_SEL_SHIFT, + CLK_SPI1_SEL_SHIFT = 2, + CLK_SPI1_SEL_MASK = 3 << CLK_SPI1_SEL_SHIFT, + CLK_SPI0_SEL_SHIFT = 0, + CLK_SPI0_SEL_MASK = 3 << CLK_SPI0_SEL_SHIFT, + CLK_SPI_SEL_200M = 0, + CLK_SPI_SEL_24M, + CLK_SPI_SEL_CPLL_100M, + + /* CRU_CLK_SEL73_CON */ + PCLK_TOP_SEL_SHIFT = 12, + PCLK_TOP_SEL_MASK = 3 << PCLK_TOP_SEL_SHIFT, + PCLK_TOP_SEL_100M = 0, + PCLK_TOP_SEL_75M, + PCLK_TOP_SEL_50M, + PCLK_TOP_SEL_24M, + HCLK_TOP_SEL_SHIFT = 8, + HCLK_TOP_SEL_MASK = 3 << HCLK_TOP_SEL_SHIFT, + HCLK_TOP_SEL_150M = 0, + HCLK_TOP_SEL_100M, + HCLK_TOP_SEL_75M, + HCLK_TOP_SEL_24M, + ACLK_TOP_LOW_SEL_SHIFT = 4, + ACLK_TOP_LOW_SEL_MASK = 3 << ACLK_TOP_LOW_SEL_SHIFT, + ACLK_TOP_LOW_SEL_400M = 0, + ACLK_TOP_LOW_SEL_300M, + ACLK_TOP_LOW_SEL_200M, + ACLK_TOP_LOW_SEL_24M, + ACLK_TOP_HIGH_SEL_SHIFT = 0, + ACLK_TOP_HIGH_SEL_MASK = 3 << ACLK_TOP_HIGH_SEL_SHIFT, + ACLK_TOP_HIGH_SEL_500M = 0, + ACLK_TOP_HIGH_SEL_400M, + ACLK_TOP_HIGH_SEL_300M, + ACLK_TOP_HIGH_SEL_24M, + + /* CRU_CLK_SEL78_CON */ + CPLL_500M_DIV_SHIFT = 8, + CPLL_500M_DIV_MASK = 0x1f << CPLL_500M_DIV_SHIFT, + + /* CRU_CLK_SEL79_CON */ + CPLL_250M_DIV_SHIFT = 8, + CPLL_250M_DIV_MASK = 0x1f << CPLL_250M_DIV_SHIFT, + CPLL_333M_DIV_SHIFT = 0, + CPLL_333M_DIV_MASK = 0x1f << CPLL_333M_DIV_SHIFT, + + /* CRU_CLK_SEL80_CON */ + CPLL_62P5M_DIV_SHIFT = 8, + CPLL_62P5M_DIV_MASK = 0x1f << CPLL_62P5M_DIV_SHIFT, + CPLL_125M_DIV_SHIFT = 0, + CPLL_125M_DIV_MASK = 0x1f << CPLL_125M_DIV_SHIFT, + + /* CRU_CLK_SEL81_CON */ + CPLL_25M_DIV_SHIFT = 8, + CPLL_25M_DIV_MASK = 0x1f << CPLL_25M_DIV_SHIFT, + CPLL_50M_DIV_SHIFT = 0, + CPLL_50M_DIV_MASK = 0x1f << CPLL_50M_DIV_SHIFT, + + /* CRU_CLK_SEL82_CON */ + CPLL_100M_DIV_SHIFT = 0, + CPLL_100M_DIV_MASK = 0x1f << CPLL_100M_DIV_SHIFT, +}; + +static struct rk_cpu_rate_table cpu_rates[] = +{ + CPUCLK_RATE(1800000000, 1, 7), + CPUCLK_RATE(1704000000, 1, 7), + CPUCLK_RATE(1608000000, 1, 5), + CPUCLK_RATE(1584000000, 1, 5), + CPUCLK_RATE(1560000000, 1, 5), + CPUCLK_RATE(1536000000, 1, 5), + CPUCLK_RATE(1512000000, 1, 5), + CPUCLK_RATE(1488000000, 1, 5), + CPUCLK_RATE(1464000000, 1, 5), + CPUCLK_RATE(1440000000, 1, 5), + CPUCLK_RATE(1416000000, 1, 5), + CPUCLK_RATE(1392000000, 1, 5), + CPUCLK_RATE(1368000000, 1, 5), + CPUCLK_RATE(1344000000, 1, 5), + CPUCLK_RATE(1320000000, 1, 5), + CPUCLK_RATE(1296000000, 1, 5), + CPUCLK_RATE(1272000000, 1, 5), + CPUCLK_RATE(1248000000, 1, 5), + CPUCLK_RATE(1224000000, 1, 5), + CPUCLK_RATE(1200000000, 1, 3), + CPUCLK_RATE(1104000000, 1, 3), + CPUCLK_RATE(1008000000, 1, 3), + CPUCLK_RATE(912000000, 1, 3), + CPUCLK_RATE(816000000, 1, 3), + CPUCLK_RATE(696000000, 1, 3), + CPUCLK_RATE(600000000, 1, 3), + CPUCLK_RATE(408000000, 1, 3), + CPUCLK_RATE(312000000, 1, 3), + CPUCLK_RATE(216000000, 1, 3), + CPUCLK_RATE(96000000, 1, 3), + { /* sentinel */ }, +}; + +static struct rk_pll_rate_table pll_rates[] = +{ + /* _mhz, _refdiv, _fbdiv, _postdiv1, _postdiv2, _dsmpd, _frac */ + PLL_RATE(2208000000, 1, 92, 1, 1, 1, 0), + PLL_RATE(2184000000, 1, 91, 1, 1, 1, 0), + PLL_RATE(2160000000, 1, 90, 1, 1, 1, 0), + PLL_RATE(2088000000, 1, 87, 1, 1, 1, 0), + PLL_RATE(2064000000, 1, 86, 1, 1, 1, 0), + PLL_RATE(2040000000, 1, 85, 1, 1, 1, 0), + PLL_RATE(2016000000, 1, 84, 1, 1, 1, 0), + PLL_RATE(1992000000, 1, 83, 1, 1, 1, 0), + PLL_RATE(1920000000, 1, 80, 1, 1, 1, 0), + PLL_RATE(1896000000, 1, 79, 1, 1, 1, 0), + PLL_RATE(1800000000, 1, 75, 1, 1, 1, 0), + PLL_RATE(1704000000, 1, 71, 1, 1, 1, 0), + PLL_RATE(1608000000, 1, 67, 1, 1, 1, 0), + PLL_RATE(1600000000, 3, 200, 1, 1, 1, 0), + PLL_RATE(1584000000, 1, 132, 2, 1, 1, 0), + PLL_RATE(1560000000, 1, 130, 2, 1, 1, 0), + PLL_RATE(1536000000, 1, 128, 2, 1, 1, 0), + PLL_RATE(1512000000, 1, 126, 2, 1, 1, 0), + PLL_RATE(1488000000, 1, 124, 2, 1, 1, 0), + PLL_RATE(1464000000, 1, 122, 2, 1, 1, 0), + PLL_RATE(1440000000, 1, 120, 2, 1, 1, 0), + PLL_RATE(1416000000, 1, 118, 2, 1, 1, 0), + PLL_RATE(1400000000, 3, 350, 2, 1, 1, 0), + PLL_RATE(1392000000, 1, 116, 2, 1, 1, 0), + PLL_RATE(1368000000, 1, 114, 2, 1, 1, 0), + PLL_RATE(1344000000, 1, 112, 2, 1, 1, 0), + PLL_RATE(1320000000, 1, 110, 2, 1, 1, 0), + PLL_RATE(1296000000, 1, 108, 2, 1, 1, 0), + PLL_RATE(1272000000, 1, 106, 2, 1, 1, 0), + PLL_RATE(1248000000, 1, 104, 2, 1, 1, 0), + PLL_RATE(1200000000, 1, 100, 2, 1, 1, 0), + PLL_RATE(1188000000, 1, 99, 2, 1, 1, 0), + PLL_RATE(1104000000, 1, 92, 2, 1, 1, 0), + PLL_RATE(1100000000, 3, 275, 2, 1, 1, 0), + PLL_RATE(1008000000, 1, 84, 2, 1, 1, 0), + PLL_RATE(1000000000, 3, 250, 2, 1, 1, 0), + PLL_RATE(912000000, 1, 76, 2, 1, 1, 0), + PLL_RATE(816000000, 1, 68, 2, 1, 1, 0), + PLL_RATE(800000000, 3, 200, 2, 1, 1, 0), + PLL_RATE(700000000, 3, 350, 4, 1, 1, 0), + PLL_RATE(696000000, 1, 116, 4, 1, 1, 0), + PLL_RATE(600000000, 1, 100, 4, 1, 1, 0), + PLL_RATE(594000000, 1, 99, 4, 1, 1, 0), + PLL_RATE(500000000, 1, 125, 6, 1, 1, 0), + PLL_RATE(408000000, 1, 68, 2, 2, 1, 0), + PLL_RATE(312000000, 1, 78, 6, 1, 1, 0), + PLL_RATE(297000000, 2, 99, 4, 1, 1, 0), + PLL_RATE(241500000, 2, 161, 4, 2, 1, 0), + PLL_RATE(216000000, 1, 72, 4, 2, 1, 0), + PLL_RATE(200000000, 1, 100, 3, 4, 1, 0), + PLL_RATE(148500000, 1, 99, 4, 4, 1, 0), + PLL_RATE(135000000, 2, 45, 4, 1, 1, 0), + PLL_RATE(119000000, 3, 119, 4, 2, 1, 0), + PLL_RATE(108000000, 2, 45, 5, 1, 1, 0), + PLL_RATE(100000000, 1, 150, 6, 6, 1, 0), + PLL_RATE(96000000, 1, 96, 6, 4, 1, 0), + PLL_RATE(78750000, 1, 96, 6, 4, 1, 0), + PLL_RATE(74250000, 2, 99, 4, 4, 1, 0), + { /* sentinel */ }, +}; + +static struct rk_pll_clock pmu_pll_clks[] = +{ + [ppll] = PLL(PLL_PPLL, PMU_PLL_CON(0), PMU_MODE, 0, 10, 0, pll_rates), + [hpll] = PLL(PLL_HPLL, PMU_PLL_CON(16), PMU_MODE, 2, 10, 0, pll_rates), +}; + +static struct rk_pll_clock pll_clks[] = +{ + [apll] = PLL(PLL_APLL, PLL_CON(0), MODE_CON, 0, 10, 0, pll_rates), + [dpll] = PLL(PLL_DPLL, PLL_CON(8), MODE_CON, 2, 10, 0, RT_NULL), + [gpll] = PLL(PLL_HPLL, PLL_CON(16), MODE_CON, 6, 10, 0, pll_rates), + [cpll] = PLL(PLL_CPLL, PLL_CON(24), MODE_CON, 4, 10, 0, pll_rates), + [npll] = PLL(PLL_NPLL, PLL_CON(32), MODE_CON, 10, 10, 0, pll_rates), + [vpll] = PLL(PLL_VPLL, PLL_CON(40), MODE_CON, 12, 10, 0, pll_rates), +}; + +static struct rk_clk_gate clk_gates[] = +{ + /* CRU_GATE_CON00 */ + /* CRU_GATE_CON01 */ + GATE(PCLK_CORE_PVTM, "pclk_core_pvtm", "pclk_core_pre", 1, 9), + GATE(CLK_CORE_PVTM, "clk_core_pvtm", "xin24m", 1, 10), + GATE(CLK_CORE_PVTM_CORE, "clk_core_pvtm_core", "armclk", 1, 11), + GATE(CLK_CORE_PVTPLL, "clk_core_pvtpll", "armclk", 1, 12), + /* CRU_GATE_CON02 */ + GATE(CLK_GPU_SRC, "clk_gpu_src", "clk_gpu_pre_c", 2, 0), + GATE(PCLK_GPU_PRE, "pclk_gpu_pre", "pclk_gpu_pre_div", 2, 2), + GATE(CLK_GPU, "clk_gpu", "clk_gpu_pre_c", 2, 3), + GATE(PCLK_GPU_PVTM, "pclk_gpu_pvtm", "pclk_gpu_pre", 2, 6), + GATE(CLK_GPU_PVTM, "clk_gpu_pvtm", "xin24m", 2, 7), + GATE(CLK_GPU_PVTM_CORE, "clk_gpu_pvtm_core", "clk_gpu_src", 2, 8), + GATE(CLK_GPU_PVTPLL, "clk_gpu_pvtpll", "clk_gpu_src", 2, 9), + GATE(ACLK_GPU_PRE, "aclk_gpu_pre", "aclk_gpu_pre_div", 2, 11), + /* CRU_GATE_CON03 */ + GATE(CLK_NPU_SRC, "clk_npu_src", "clk_npu_src_c", 3, 0), + GATE(CLK_NPU_NP5, "clk_npu_np5", "clk_npu_np5_c", 3, 1), + GATE(HCLK_NPU_PRE, "hclk_npu_pre", "hclk_npu_pre_div", 3, 2), + GATE(PCLK_NPU_PRE, "pclk_npu_pre", "pclk_npu_pre_div", 3, 3), + GATE(ACLK_NPU_PRE, "aclk_npu_pre", "clk_npu", 3, 4), + GATE(ACLK_NPU, "aclk_npu", "aclk_npu_pre", 3, 7), + GATE(HCLK_NPU, "hclk_npu", "hclk_npu_pre", 3, 8), + GATE(PCLK_NPU_PVTM, "pclk_npu_pvtm", "pclk_npu_pre", 3, 9), + GATE(CLK_NPU_PVTM, "clk_npu_pvtm", "xin24m", 3, 10), + GATE(CLK_NPU_PVTM_CORE, "clk_npu_pvtm_core", "clk_npu_pre_ndft", 3, 11), + GATE(CLK_NPU_PVTPLL, "clk_npu_pvtpll", "clk_npu_pre_ndft", 3, 12), + /* CRU_GATE_CON04 */ + GATE(CLK_DDRPHY1X_SRC, "clk_ddrphy1x_src", "clk_ddrphy1x_src_c", 4, 0), + GATE(CLK_MSCH, "clk_msch", "clk_msch_div", 4, 2), + GATE(CLK24_DDRMON, "clk24_ddrmon", "xin24m", 4, 15), + /* CRU_GATE_CON05 */ + GATE(ACLK_GIC_AUDIO, "aclk_gic_audio", "aclk_gic_audio_sel", 5, 0), + GATE(HCLK_GIC_AUDIO, "hclk_gic_audio", "hclk_gic_audio_sel", 5, 1), + GATE(ACLK_GIC600, "aclk_gic600", "aclk_gic_audio", 5, 4), + GATE(ACLK_SPINLOCK, "aclk_spinlock", "aclk_gic_audio", 5, 7), + GATE(HCLK_SDMMC_BUFFER, "hclk_sdmmc_buffer", "hclk_gic_audio", 5, 8), + GATE(DCLK_SDMMC_BUFFER, "dclk_sdmmc_buffer", "dclk_sdmmc_buffer_sel", 5, 9), + GATE(HCLK_I2S0_8CH, "hclk_i2s0_8ch", "hclk_gic_audio", 5, 10), + GATE(HCLK_I2S1_8CH, "hclk_i2s1_8ch", "hclk_gic_audio", 5, 11), + GATE(HCLK_I2S2_2CH, "hclk_i2s2_2ch", "hclk_gic_audio", 5, 12), + GATE(HCLK_I2S3_2CH, "hclk_i2s3_2ch", "hclk_gic_audio", 5, 13), + GATE(HCLK_PDM, "hclk_pdm", "hclk_gic_audio", 5, 14), + GATE(MCLK_PDM, "mclk_pdm", "mclk_pdm_sel", 5, 15), + /* CRU_GATE_CON06 */ + GATE(CLK_I2S0_8CH_TX_SRC, "clk_i2s0_8ch_tx_src", "clk_i2s0_8ch_tx_src_c", 6, 0), + GATE(CLK_I2S0_8CH_TX_FRAC, "clk_i2s0_8ch_tx_frac", "clk_i2s0_8ch_tx_frac_div", 6, 1), + GATE(MCLK_I2S0_8CH_TX, "mclk_i2s0_8ch_tx", "clk_i2s0_8ch_tx", 6, 2), + GATE(I2S0_MCLKOUT_TX, "i2s0_mclkout_tx", "i2s0_mclkout_tx_sel", 6, 3), + GATE(CLK_I2S0_8CH_RX_SRC, "clk_i2s0_8ch_rx_src", "clk_i2s0_8ch_rx_src_c", 6, 4), + GATE(CLK_I2S0_8CH_RX_FRAC, "clk_i2s0_8ch_rx_frac", "clk_i2s0_8ch_rx_frac_div", 6, 5), + GATE(MCLK_I2S0_8CH_RX, "mclk_i2s0_8ch_rx", "clk_i2s0_8ch_rx", 6, 6), + GATE(I2S0_MCLKOUT_RX, "i2s0_mclkout_rx", "i2s0_mclkout_rx_sel", 6, 7), + GATE(CLK_I2S1_8CH_TX_SRC, "clk_i2s1_8ch_tx_src", "clk_i2s1_8ch_tx_src_c", 6, 8), + GATE(CLK_I2S1_8CH_TX_FRAC, "clk_i2s1_8ch_tx_frac", "clk_i2s1_8ch_tx_frac_div", 6, 9), + GATE(MCLK_I2S1_8CH_TX, "mclk_i2s1_8ch_tx", "clk_i2s1_8ch_tx", 6, 10), + GATE(I2S1_MCLKOUT_TX, "i2s1_mclkout_tx", "i2s1_mclkout_tx_sel", 6, 11), + GATE(CLK_I2S1_8CH_RX_SRC, "clk_i2s1_8ch_rx_src", "clk_i2s1_8ch_rx_src_c", 6, 12), + GATE(CLK_I2S1_8CH_RX_FRAC, "clk_i2s1_8ch_rx_frac", "clk_i2s1_8ch_rx_frac_div", 6, 13), + GATE(MCLK_I2S1_8CH_RX, "mclk_i2s1_8ch_rx", "clk_i2s1_8ch_rx", 6, 14), + GATE(I2S1_MCLKOUT_RX, "i2s1_mclkout_rx", "i2s1_mclkout_rx_sel", 6, 15), + /* CRU_GATE_CON07 */ + GATE(CLK_I2S2_2CH_SRC, "clk_i2s2_2ch_src", "clk_i2s2_2ch_src_c", 7, 0), + GATE(CLK_I2S2_2CH_FRAC, "clk_i2s2_2ch_frac", "clk_i2s2_2ch_frac_div", 7, 1), + GATE(MCLK_I2S2_2CH, "mclk_i2s2_2ch", "clk_i2s2_2ch", 7, 2), + GATE(I2S2_MCLKOUT, "i2s2_mclkout", "i2s2_mclkout_sel", 7, 3), + GATE(CLK_I2S3_2CH_TX, "clk_i2s3_2ch_tx_src", "clk_i2s3_2ch_tx_src_c", 7, 4), + GATE(CLK_I2S3_2CH_TX_FRAC, "clk_i2s3_2ch_tx_frac", "clk_i2s3_2ch_tx_frac_div", 7, 5), + GATE(MCLK_I2S3_2CH_TX, "mclk_i2s3_2ch_tx", "clk_i2s3_2ch_tx", 7, 6), + GATE(I2S3_MCLKOUT_TX, "i2s3_mclkout_tx", "i2s3_mclkout_tx_sel", 7, 7), + GATE(CLK_I2S3_2CH_RX, "clk_i2s3_2ch_rx_src", "clk_i2s3_2ch_rx_src_div", 7, 8), + GATE(CLK_I2S3_2CH_RX_FRAC, "clk_i2s3_2ch_rx_frac", "clk_i2s3_2ch_rx_frac_div", 7, 9), + GATE(MCLK_I2S3_2CH_RX, "mclk_i2s3_2ch_rx", "clk_i2s3_2ch_rx", 7, 10), + GATE(I2S3_MCLKOUT_RX, "i2s3_mclkout_rx", "i2s3_mclkout_rx_sel", 7, 11), + GATE(HCLK_VAD, "hclk_vad", "hclk_gic_audio", 7, 12), + GATE(HCLK_SPDIF_8CH, "hclk_spdif_8ch", "hclk_gic_audio", 7, 13), + GATE(MCLK_SPDIF_8CH_SRC, "mclk_spdif_8ch_src", "mclk_spdif_8ch_src_c", 7, 14), + GATE(MCLK_SPDIF_8CH_FRAC, "mclk_spdif_8ch_frac", "mclk_spdif_8ch_frac_div", 7, 15), + /* CRU_GATE_CON08 */ + GATE(HCLK_AUDPWM, "hclk_audpwm", "hclk_gic_audio", 8, 0), + GATE(SCLK_AUDPWM_SRC, "sclk_audpwm_src", "sclk_audpwm_src_c", 8, 1), + GATE(SCLK_AUDPWM_FRAC, "sclk_audpwm_frac", "sclk_audpwm_frac_frac", 8, 2), + GATE(HCLK_ACDCDIG, "hclk_acdcdig", "hclk_gic_audio", 8, 3), + GATE(CLK_ACDCDIG_I2C, "clk_acdcdig_i2c", "clk_acdcdig_i2c_sel", 8, 4), + GATE(CLK_ACDCDIG_DAC, "clk_acdcdig_dac", "mclk_i2s3_2ch_tx", 8, 5), + GATE(CLK_ACDCDIG_ADC, "clk_acdcdig_adc", "mclk_i2s3_2ch_rx", 8, 6), + GATE(ACLK_SECURE_FLASH, "aclk_secure_flash", "aclk_secure_flash_sel", 8, 7), + GATE(HCLK_SECURE_FLASH, "hclk_secure_flash", "hclk_secure_flash_sel", 8, 8), + GATE(ACLK_CRYPTO_NS, "aclk_crypto_ns", "aclk_secure_flash", 8, 11), + GATE(HCLK_CRYPTO_NS, "hclk_crypto_ns", "hclk_secure_flash", 8, 12), + GATE(CLK_CRYPTO_NS_CORE, "clk_crypto_ns_core", "clk_crypto_ns_core_sel", 8, 13), + GATE(CLK_CRYPTO_NS_PKA, "clk_crypto_ns_pka", "clk_crypto_ns_pka_sel", 8, 14), + GATE(CLK_CRYPTO_NS_RNG, "clk_crypto_ns_rng", "hclk_secure_flash", 8, 15), + /* CRU_GATE_CON09 */ + GATE(HCLK_NANDC, "hclk_nandc", "hclk_secure_flash", 9, 0), + GATE(NCLK_NANDC, "nclk_nandc", "nclk_nandc_sel", 9, 1), + GATE(HCLK_SFC, "hclk_sfc", "hclk_secure_flash", 9, 2), + GATE(HCLK_SFC_XIP, "hclk_sfc_xip", "hclk_secure_flash", 9, 3), + GATE(SCLK_SFC, "sclk_sfc", "sclk_sfc_sel", 9, 4), + GATE(ACLK_EMMC, "aclk_emmc", "aclk_secure_flash", 9, 5), + GATE(HCLK_EMMC, "hclk_emmc", "hclk_secure_flash", 9, 6), + GATE(BCLK_EMMC, "bclk_emmc", "bclk_emmc_sel", 9, 7), + GATE(CCLK_EMMC, "cclk_emmc", "cclk_emmc_sel", 9, 8), + GATE(TCLK_EMMC, "tclk_emmc", "xin24m", 9, 9), + GATE(HCLK_TRNG_NS, "hclk_trng_ns", "hclk_secure_flash", 9, 10), + GATE(CLK_TRNG_NS, "clk_trng_ns", "hclk_secure_flash", 9, 11), + /* CRU_GATE_CON10 */ + GATE(ACLK_PIPE, "aclk_pipe", "aclk_pipe_sel", 10, 0), + GATE(PCLK_PIPE, "pclk_pipe", "pclk_pipe_div", 10, 1), + GATE(CLK_XPCS_EEE, "clk_xpcs_eee", "clk_xpcs_eee_sel", 10, 4), + GATE(ACLK_USB3OTG0, "aclk_usb3otg0", "aclk_pipe", 10, 8), + GATE(CLK_USB3OTG0_REF, "clk_usb3otg0_ref", "xin24m", 10, 9), + GATE(CLK_USB3OTG0_SUSPEND, "clk_usb3otg0_suspend", "clk_usb3otg0_suspend_sel", 10, 10), + GATE(ACLK_USB3OTG1, "aclk_usb3otg1", "aclk_pipe", 10, 12), + GATE(CLK_USB3OTG1_REF, "clk_usb3otg1_ref", "xin24m", 10, 13), + GATE(CLK_USB3OTG1_SUSPEND, "clk_usb3otg1_suspend", "clk_usb3otg1_suspend_sel", 10, 14), + /* CRU_GATE_CON11 */ + GATE(ACLK_SATA0, "aclk_sata0", "aclk_pipe", 11, 0), + GATE(CLK_SATA0_PMALIVE, "clk_sata0_pmalive", "clk_gpll_div_20m", 11, 1), + GATE(CLK_SATA0_RXOOB, "clk_sata0_rxoob", "clk_cpll_div_50m", 11, 2), + GATE(ACLK_SATA1, "aclk_sata1", "aclk_pipe", 11, 4), + GATE(CLK_SATA1_PMALIVE, "clk_sata1_pmalive", "clk_gpll_div_20m", 11, 5), + GATE(CLK_SATA1_RXOOB, "clk_sata1_rxoob", "clk_cpll_div_50m", 11, 6), + GATE(ACLK_SATA2, "aclk_sata2", "aclk_pipe", 11, 8), + GATE(CLK_SATA2_PMALIVE, "clk_sata2_pmalive", "clk_gpll_div_20m", 11, 9), + GATE(CLK_SATA2_RXOOB, "clk_sata2_rxoob", "clk_cpll_div_50m", 11, 10), + /* CRU_GATE_CON12 */ + GATE(ACLK_PCIE20_MST, "aclk_pcie20_mst", "aclk_pipe", 12, 0), + GATE(ACLK_PCIE20_SLV, "aclk_pcie20_slv", "aclk_pipe", 12, 1), + GATE(ACLK_PCIE20_DBI, "aclk_pcie20_dbi", "aclk_pipe", 12, 2), + GATE(PCLK_PCIE20, "pclk_pcie20", "pclk_pipe", 12, 3), + GATE(CLK_PCIE20_AUX_NDFT, "clk_pcie20_aux_ndft", "xin24m", 12, 4), + GATE(ACLK_PCIE30X1_MST, "aclk_pcie30x1_mst", "aclk_pipe", 12, 8), + GATE(ACLK_PCIE30X1_SLV, "aclk_pcie30x1_slv", "aclk_pipe", 12, 9), + GATE(ACLK_PCIE30X1_DBI, "aclk_pcie30x1_dbi", "aclk_pipe", 12, 10), + GATE(PCLK_PCIE30X1, "pclk_pcie30x1", "pclk_pipe", 12, 11), + GATE(CLK_PCIE30X1_AUX_NDFT, "clk_pcie30x1_aux_ndft", "xin24m", 12, 12), + /* CRU_GATE_CON13 */ + GATE(ACLK_PCIE30X2_MST, "aclk_pcie30x2_mst", "aclk_pipe", 13, 0), + GATE(ACLK_PCIE30X2_SLV, "aclk_pcie30x2_slv", "aclk_pipe", 13, 1), + GATE(ACLK_PCIE30X2_DBI, "aclk_pcie30x2_dbi", "aclk_pipe", 13, 2), + GATE(PCLK_PCIE30X2, "pclk_pcie30x2", "pclk_pipe", 13, 3), + GATE(CLK_PCIE30X2_AUX_NDFT, "clk_pcie30x2_aux_ndft", "xin24m", 13, 4), + GATE(PCLK_XPCS, "pclk_xpcs", "pclk_pipe", 13, 6), + /* CRU_GATE_CON14 */ + GATE(ACLK_PERIMID, "aclk_perimid", "aclk_perimid_sel", 14, 0), + GATE(HCLK_PERIMID, "hclk_perimid", "hclk_perimid_sel", 14, 1), + GATE(ACLK_PHP, "aclk_php", "aclk_php_sel", 14, 8), + GATE(HCLK_PHP, "hclk_php", "hclk_php_sel", 14, 9), + GATE(PCLK_PHP, "pclk_php", "pclk_php_div", 14, 10), + /* CRU_GATE_CON15 */ + GATE(HCLK_SDMMC0, "hclk_sdmmc0", "hclk_php", 15, 0), + GATE(CLK_SDMMC0, "clk_sdmmc0", "clk_sdmmc0_sel", 15, 1), + GATE(HCLK_SDMMC1, "hclk_sdmmc1", "hclk_php", 15, 2), + GATE(CLK_SDMMC1, "clk_sdmmc1", "clk_sdmmc1_sel", 15, 3), + GATE(CLK_GMAC0_PTP_REF, "clk_gmac0_ptp_ref", "clk_gmac0_ptp_ref_sel", 15, 4), + GATE(ACLK_GMAC0, "aclk_gmac0", "aclk_php", 15, 5), + GATE(PCLK_GMAC0, "pclk_gmac0", "pclk_php", 15, 6), + GATE(CLK_MAC0_2TOP, "clk_mac0_2top", "clk_mac0_2top_sel", 15, 7), + GATE(CLK_MAC0_OUT, "clk_mac0_out", "clk_mac0_out_sel", 15, 8), + GATE(CLK_MAC0_REFOUT, "clk_mac0_refout", "clk_mac0_2top", 15, 12), + /* CRU_GATE_CON16 */ + GATE(ACLK_USB, "aclk_usb", "aclk_usb_sel", 16, 0), + GATE(HCLK_USB, "hclk_usb", "hclk_usb_sel", 16, 1), + GATE(PCLK_USB, "pclk_usb", "pclk_usb_div", 16, 2), + GATE(HCLK_USB2HOST0, "hclk_usb2host0", "hclk_usb", 16, 12), + GATE(HCLK_USB2HOST0_ARB, "hclk_usb2host0_arb", "hclk_usb", 16, 13), + GATE(HCLK_USB2HOST1, "hclk_usb2host1", "hclk_usb", 16, 14), + GATE(HCLK_USB2HOST1_ARB, "hclk_usb2host1_arb", "hclk_usb", 16, 15), + /* CRU_GATE_CON17 */ + GATE(HCLK_SDMMC2, "hclk_sdmmc2", "hclk_usb", 17, 0), + GATE(CLK_SDMMC2, "clk_sdmmc2", "clk_sdmmc2_sel", 17, 1), + GATE(CLK_GMAC1_PTP_REF, "clK_gmac1_ptp_ref", "clk_gmac1_ptp_ref_sel", 17, 2), + GATE(ACLK_GMAC1, "aclk_gmac1", "aclk_usb", 17, 3), + GATE(PCLK_GMAC1, "pclk_gmac1", "pclk_usb", 17, 4), + GATE(CLK_MAC1_2TOP, "clk_mac1_2top", "clk_mac1_2top_sel", 17, 5), + GATE(CLK_MAC1_OUT, "clk_mac1_out", "clk_mac1_out_sel", 17, 6), + GATE(CLK_MAC1_REFOUT, "clk_mac1_refout", "clk_mac1_2top", 17, 10), + /* CRU_GATE_CON18 */ + GATE(ACLK_VI, "aclk_vi", "aclk_vi_sel", 18, 0), + GATE(HCLK_VI, "hclk_vi", "hclk_vi_div", 18, 1), + GATE(PCLK_VI, "pclk_vi", "pclk_vi_div", 18, 2), + GATE(ACLK_VICAP, "aclk_vicap", "aclk_vi", 18, 9), + GATE(HCLK_VICAP, "hclk_vicap", "hclk_vi", 18, 10), + GATE(DCLK_VICAP, "dclk_vicap", "dclk_vicap1_sel", 18, 11), + /* CRU_GATE_CON19 */ + GATE(ACLK_ISP, "aclk_isp", "aclk_vi", 19, 0), + GATE(HCLK_ISP, "hclk_isp", "hclk_vi", 19, 1), + GATE(CLK_ISP, "clk_isp", "clk_isp_c", 19, 2), + GATE(PCLK_CSI2HOST1, "pclk_csi2host1", "pclk_vi", 19, 4), + GATE(CLK_CIF_OUT, "clk_cif_out", "clk_cif_out_c", 19, 8), + GATE(CLK_CAM0_OUT, "clk_cam0_out", "clk_cam0_out_c", 19, 9), + GATE(CLK_CAM1_OUT, "clk_cam1_out", "clk_cam1_out_c", 19, 9), + /* CRU_GATE_CON20 */ + GATE(ACLK_VO, "aclk_vo", "aclk_vo_sel", 20, 0), + GATE(HCLK_VO, "hclk_vo", "hclk_vo_div", 20, 1), + GATE(PCLK_VO, "pclk_vo", "pclk_vo_div", 20, 2), + GATE(ACLK_VOP_PRE, "aclk_vop_pre", "aclk_vop_pre_c", 20, 6), + GATE(ACLK_VOP, "aclk_vop", "aclk_vop_pre", 20, 8), + GATE(HCLK_VOP, "hclk_vop", "hclk_vo", 20, 9), + GATE(DCLK_VOP0, "dclk_vop0", "dclk_vop0_c", 20, 10), + GATE(DCLK_VOP1, "dclk_vop1", "dclk_vop1_c", 20, 11), + GATE(DCLK_VOP2, "dclk_vop2", "dclk_vop2_c", 20, 12), + GATE(CLK_VOP_PWM, "clk_vop_pwm", "xin24m", 20, 13), + /* CRU_GATE_CON21 */ + GATE(ACLK_HDCP, "aclk_hdcp", "aclk_vo", 21, 0), + GATE(HCLK_HDCP, "hclk_hdcp", "hclk_vo", 21, 1), + GATE(PCLK_HDCP, "pclk_hdcp", "pclk_vo", 21, 2), + GATE(PCLK_HDMI_HOST, "pclk_hdmi_host", "pclk_vo", 21, 3), + GATE(CLK_HDMI_SFR, "clk_hdmi_sfr", "xin24m", 21, 4), + GATE(CLK_HDMI_CEC, "clk_hdmi_cec", "clk_rtc_32k", 21, 5), + GATE(PCLK_DSITX_0, "pclk_dsitx_0", "pclk_vo", 21, 6), + GATE(PCLK_DSITX_1, "pclk_dsitx_1", "pclk_vo", 21, 7), + GATE(PCLK_EDP_CTRL, "pclk_edp_ctrl", "pclk_vo", 21, 8), + GATE(CLK_EDP_200M, "clk_edp_200m", "clk_edp_200m_sel", 21, 9), + /* CRU_GATE_CON22 */ + GATE(ACLK_VPU_PRE, "aclk_vpu_pre", "aclk_vpu_pre_c", 22, 0), + GATE(HCLK_VPU_PRE, "hclk_vpu_pre", "aclk_vpu_pre_c", 22, 1), + GATE(ACLK_VPU, "aclk_vpu", "aclk_vpu_pre", 22, 4), + GATE(HCLK_VPU, "hclk_vpu", "hclk_vpu_pre", 22, 5), + GATE(PCLK_RGA_PRE, "pclk_rga_pre", "pclk_rga_pre_div", 22, 12), + GATE(PCLK_EINK, "pclk_eink", "pclk_rga_pre", 22, 14), + GATE(HCLK_EINK, "hclk_eink", "hclk_rga_pre", 22, 15), + /* CRU_GATE_CON23 */ + GATE(ACLK_RGA_PRE, "aclk_rga_pre", "aclk_rga_pre_sel", 23, 0), + GATE(HCLK_RGA_PRE, "hclk_rga_pre", "hclk_rga_pre_div", 23, 1), + GATE(ACLK_RGA, "aclk_rga", "aclk_rga_pre", 23, 4), + GATE(HCLK_RGA, "hclk_rga", "hclk_rga_pre", 23, 5), + GATE(CLK_RGA_CORE, "clk_rga_core", "clk_rga_core_sel", 23, 6), + GATE(ACLK_IEP, "aclk_iep", "aclk_rga_pre", 23, 7), + GATE(HCLK_IEP, "hclk_iep", "hclk_rga_pre", 23, 8), + GATE(CLK_IEP_CORE, "clk_iep_core", "clk_iep_core_sel", 23, 9), + GATE(HCLK_EBC, "hclk_ebc", "hclk_rga_pre", 23, 10), + GATE(DCLK_EBC, "dclk_ebc", "dclk_ebc_sel", 23, 11), + GATE(ACLK_JDEC, "aclk_jdec", "aclk_rga_pre", 23, 12), + GATE(HCLK_JDEC, "hclk_jdec", "hclk_rga_pre", 23, 13), + GATE(ACLK_JENC, "aclk_jenc", "aclk_rga_pre", 23, 14), + GATE(HCLK_JENC, "hclk_jenc", "hclk_rga_pre", 23, 15), + /* CRU_GATE_CON24 */ + GATE(ACLK_RKVENC_PRE, "aclk_rkvenc_pre", "aclk_rkvenc_pre_c", 24, 0), + GATE(HCLK_RKVENC_PRE, "hclk_rkvenc_pre", "hclk_rkvenc_pre_div", 24, 1), + GATE(ACLK_RKVENC, "aclk_rkvenc", "aclk_rkvenc_pre", 24, 6), + GATE(HCLK_RKVENC, "hclk_rkvenc", "hclk_rkvenc_pre", 24, 7), + GATE(CLK_RKVENC_CORE, "clk_rkvenc_core", "clk_rkvenc_core_c", 24, 8), + GATE(ACLK_RKVDEC_PRE, "aclk_rkvdec_pre", "aclk_rkvdec_pre_c", 25, 0), + /* CRU_GATE_CON25 */ + GATE(HCLK_RKVDEC_PRE, "hclk_rkvdec_pre", "hclk_rkvdec_pre_div", 25, 1), + GATE(ACLK_RKVDEC, "aclk_rkvdec", "aclk_rkvdec_pre", 25, 4), + GATE(HCLK_RKVDEC, "hclk_rkvdec", "hclk_rkvdec_pre", 25, 5), + GATE(CLK_RKVDEC_CA, "clk_rkvdec_ca", "clk_rkvdec_ca_c", 25, 6), + GATE(CLK_RKVDEC_CORE, "clk_rkvdec_core", "clk_rkvdec_core_c", 25, 7), + GATE(CLK_RKVDEC_HEVC_CA, "clk_rkvdec_hevc_ca", "clk_rkvdec_hevc_ca_c", 25, 8), + /* CRU_GATE_CON26 */ + GATE(ACLK_BUS, "aclk_bus", "aclk_bus_sel", 26, 0), + GATE(PCLK_BUS, "pclk_bus", "pclk_bus_sel", 26, 1), + GATE(PCLK_TSADC, "pclk_tsadc", "pclk_bus", 26, 4), + GATE(CLK_TSADC_TSEN, "clk_tsadc_tsen", "clk_tsadc_tsen_c", 26, 5), + GATE(CLK_TSADC, "clk_tsadc", "clk_tsadc_div", 26, 6), + GATE(PCLK_SARADC, "pclk_saradc", "pclk_bus", 26, 7), + GATE(CLK_SARADC, "clk_saradc", "xin24m", 26, 8), + GATE(PCLK_OTPC_NS, "pclk_otpc_ns", "hclk_secure_flash", 26, 9), + GATE(CLK_OTPC_NS_SBPI, "clk_otpc_ns_sbpi", "xin24m", 26, 10), + GATE(CLK_OTPC_NS_USR, "clk_otpc_ns_usr", "xin_osc0_half", 26, 11), + GATE(PCLK_SCR, "pclk_scr", "pclk_bus", 26, 12), + GATE(PCLK_WDT_NS, "pclk_wdt_ns", "pclk_bus", 26, 13), + GATE(TCLK_WDT_NS, "tclk_wdt_ns", "xin24m", 26, 14), + /* CRU_GATE_CON27 */ + GATE(PCLK_CAN0, "pclk_can0", "pclk_bus", 27, 5), + GATE(CLK_CAN0, "clk_can0", "clk_can0_c", 27, 6), + GATE(PCLK_CAN1, "pclk_can1", "pclk_bus", 27, 7), + GATE(CLK_CAN1, "clk_can1", "clk_can1_c", 27, 8), + GATE(PCLK_CAN2, "pclk_can2", "pclk_bus", 27, 9), + GATE(CLK_CAN2, "clk_can2", "clk_can2_c", 27, 10), + GATE(PCLK_UART1, "pclk_uart1", "pclk_bus", 27, 12), + GATE(CLK_UART1_SRC, "clk_uart1_src", "clk_uart1_src_c", 27, 13), + GATE(CLK_UART1_FRAC, "clk_uart1_frac", "clk_uart1_frac_frac", 27, 14), + GATE(SCLK_UART1, "sclk_uart1", "sclk_uart1_sel", 27, 15), + /* CRU_GATE_CON28 */ + GATE(PCLK_UART2, "pclk_uart2", "pclk_bus", 28, 0), + GATE(CLK_UART2_SRC, "clk_uart2_src", "clk_uart2_src_c", 28, 1), + GATE(CLK_UART2_FRAC, "clk_uart2_frac", "clk_uart2_frac_frac", 28, 2), + GATE(SCLK_UART2, "sclk_uart2", "sclk_uart2_sel", 28, 3), + GATE(PCLK_UART3, "pclk_uart3", "pclk_bus", 28, 4), + GATE(CLK_UART3_SRC, "clk_uart3_src", "clk_uart3_src_c", 28, 5), + GATE(CLK_UART3_FRAC, "clk_uart3_frac", "clk_uart3_frac_frac", 28, 6), + GATE(SCLK_UART3, "sclk_uart3", "sclk_uart3_sel", 28, 7), + GATE(PCLK_UART4, "pclk_uart4", "pclk_bus", 28, 8), + GATE(CLK_UART4_SRC, "clk_uart4_src", "clk_uart4_src_c", 28, 9), + GATE(CLK_UART4_FRAC, "clk_uart4_frac", "clk_uart4_frac_frac", 28, 10), + GATE(SCLK_UART4, "sclk_uart4", "sclk_uart4_sel", 28, 11), + GATE(PCLK_UART5, "pclk_uart5", "pclk_bus", 28, 12), + GATE(CLK_UART5_SRC, "clk_uart5_src", "clk_uart5_src_c", 28, 13), + GATE(CLK_UART5_FRAC, "clk_uart5_frac", "clk_uart5_frac_frac", 28, 14), + GATE(SCLK_UART5, "sclk_uart5", "sclk_uart5_sel", 28, 15), + /* CRU_GATE_CON29 */ + GATE(PCLK_UART6, "pclk_uart6", "pclk_bus", 29, 0), + GATE(CLK_UART6_SRC, "clk_uart6_src", "clk_uart6_src_c", 29, 1), + GATE(CLK_UART6_FRAC, "clk_uart6_frac", "clk_uart6_frac_frac", 29, 2), + GATE(SCLK_UART6, "sclk_uart6", "sclk_uart6_sel", 29, 3), + GATE(PCLK_UART7, "pclk_uart7", "pclk_bus", 29, 4), + GATE(CLK_UART7_SRC, "clk_uart7_src", "clk_uart7_src_c", 29, 5), + GATE(CLK_UART7_FRAC, "clk_uart7_frac", "clk_uart7_frac_frac", 29, 6), + GATE(SCLK_UART7, "sclk_uart7", "sclk_uart7_sel", 29, 7), + GATE(PCLK_UART8, "pclk_uart8", "pclk_bus", 29, 8), + GATE(CLK_UART8_SRC, "clk_uart8_src", "clk_uart8_src_c", 29, 9), + GATE(CLK_UART8_FRAC, "clk_uart8_frac", "clk_uart8_frac_frac", 29, 10), + GATE(SCLK_UART8, "sclk_uart8", "sclk_uart8_sel", 29, 11), + GATE(PCLK_UART9, "pclk_uart9", "pclk_bus", 29, 12), + GATE(CLK_UART9_SRC, "clk_uart9_src", "clk_uart9_src_c", 29, 13), + GATE(CLK_UART9_FRAC, "clk_uart9_frac", "clk_uart9_frac_frac", 29, 14), + GATE(SCLK_UART9, "sclk_uart9", "sclk_uart9_sel", 29, 15), + /* CRU_GATE_CON30 */ + GATE(PCLK_I2C1, "pclk_i2c1", "pclk_bus", 30, 0), + GATE(CLK_I2C1, "clk_i2c1", "clk_i2c", 30, 1), + GATE(PCLK_I2C2, "pclk_i2c2", "pclk_bus", 30, 2), + GATE(CLK_I2C2, "clk_i2c2", "clk_i2c", 30, 3), + GATE(PCLK_I2C3, "pclk_i2c3", "pclk_bus", 30, 4), + GATE(CLK_I2C3, "clk_i2c3", "clk_i2c", 30, 5), + GATE(PCLK_I2C4, "pclk_i2c4", "pclk_bus", 30, 6), + GATE(CLK_I2C4, "clk_i2c4", "clk_i2c", 30, 7), + GATE(PCLK_I2C5, "pclk_i2c5", "pclk_bus", 30, 8), + GATE(CLK_I2C5, "clk_i2c5", "clk_i2c", 30, 9), + GATE(PCLK_SPI0, "pclk_spi0", "pclk_bus", 30, 10), + GATE(CLK_SPI0, "clk_spi0", "clk_spi0_sel", 30, 11), + GATE(PCLK_SPI1, "pclk_spi1", "pclk_bus", 30, 12), + GATE(CLK_SPI1, "clk_spi1", "clk_spi1_sel", 30, 13), + GATE(PCLK_SPI2, "pclk_spi2", "pclk_bus", 30, 14), + GATE(CLK_SPI2, "clk_spi2", "clk_spi2_sel", 30, 15), + /* CRU_GATE_CON31 */ + GATE(PCLK_SPI3, "pclk_spi3", "pclk_bus", 31, 0), + GATE(CLK_SPI3, "clk_spi3", "clk_spi3_sel", 31, 1), + GATE(PCLK_GPIO1, "pclk_gpio1", "pclk_bus", 31, 2), + GATE(DBCLK_GPIO1, "dbclk_gpio1", "dbclk_gpio", 31, 3), + GATE(PCLK_GPIO2, "pclk_gpio2", "pclk_bus", 31, 4), + GATE(DBCLK_GPIO2, "dbclk_gpio2", "dbclk_gpio", 31, 5), + GATE(PCLK_GPIO3, "pclk_gpio3", "pclk_bus", 31, 6), + GATE(DBCLK_GPIO3, "dbclk_gpio3", "dbclk_gpio", 31, 7), + GATE(PCLK_GPIO4, "pclk_gpio4", "pclk_bus", 31, 8), + GATE(DBCLK_GPIO4, "dbclk_gpio4", "dbclk_gpio", 31, 9), + GATE(PCLK_PWM1, "pclk_pwm1", "pclk_bus", 31, 10), + GATE(CLK_PWM1, "clk_pwm1", "clk_pwm1_sel", 31, 11), + GATE(CLK_PWM1_CAPTURE, "clk_pwm1_capture", "xin24m", 31, 12), + GATE(PCLK_PWM2, "pclk_pwm2", "pclk_bus", 31, 13), + GATE(CLK_PWM2, "clk_pwm2", "clk_pwm2_sel", 31, 14), + GATE(CLK_PWM2_CAPTURE, "clk_pwm2_capture", "xin24m", 31, 15), + /* CRU_GATE_CON32 */ + GATE(PCLK_PWM3, "pclk_pwm3", "pclk_bus", 32, 0), + GATE(CLK_PWM3, "clk_pwm3", "clk_pwm3_sel", 32, 1), + GATE(CLK_PWM3_CAPTURE, "clk_pwm3_capture", "xin24m", 32, 2), + GATE(PCLK_TIMER, "pclk_timer", "pclk_bus", 32, 3), + GATE(CLK_TIMER0, "clk_timer0", "xin24m", 32, 4), + GATE(CLK_TIMER1, "clk_timer1", "xin24m", 32, 5), + GATE(CLK_TIMER2, "clk_timer2", "xin24m", 32, 6), + GATE(CLK_TIMER3, "clk_timer3", "xin24m", 32, 7), + GATE(CLK_TIMER4, "clk_timer4", "xin24m", 32, 8), + GATE(CLK_TIMER5, "clk_timer5", "xin24m", 32, 9), + GATE(CLK_I2C, "clk_i2c", "clk_i2c_sel", 32, 10), + GATE(DBCLK_GPIO, "dbclk_gpio", "dbclk_gpio_sel", 32, 11), + GATE(ACLK_MCU, "aclk_mcu", "aclk_bus", 32, 13), + GATE(PCLK_INTMUX, "pclk_intmux", "pclk_bus", 32, 14), + GATE(PCLK_MAILBOX, "pclk_mailbox", "pclk_bus", 32, 15), + /* CRU_GATE_CON33 */ + GATE(ACLK_TOP_HIGH, "aclk_top_high", "aclk_top_high_sel", 33, 0), + GATE(ACLK_TOP_LOW, "aclk_top_low", "aclk_top_low_sel", 33, 1), + GATE(HCLK_TOP, "hclk_top", "hclk_top_sel", 33, 2), + GATE(PCLK_TOP, "pclk_top", "pclk_top_sel", 33, 3), + GATE(PCLK_PCIE30PHY, "pclk_pcie30phy", "pclk_top", 33, 8), + GATE(CLK_OPTC_ARB, "clk_optc_arb", "clk_optc_arb_sel", 33, 9), + GATE(PCLK_MIPICSIPHY, "pclk_mipicsiphy", "pclk_top", 33, 13), + GATE(PCLK_MIPIDSIPHY0, "pclk_mipidsiphy0", "pclk_top", 33, 14), + GATE(PCLK_MIPIDSIPHY1, "pclk_mipidsiphy1", "pclk_top", 33, 15), + /* CRU_GATE_CON34 */ + GATE(PCLK_PIPEPHY0, "pclk_pipephy0", "pclk_top", 34, 4), + GATE(PCLK_PIPEPHY1, "pclk_pipephy1", "pclk_top", 34, 5), + GATE(PCLK_PIPEPHY2, "pclk_pipephy2", "pclk_top", 34, 6), + GATE(PCLK_CPU_BOOST, "pclk_cpu_boost", "pclk_top", 34, 11), + GATE(CLK_CPU_BOOST, "clk_cpu_boost", "xin24m", 34, 12), + GATE(PCLK_OTPPHY, "pclk_otpphy", "pclk_top", 34, 13), + GATE(PCLK_EDPPHY_GRF, "pclk_edpphy_grf", "pclk_top", 34, 14), + /* CRU_GATE_CON35 */ + GATE(CPLL_500M, "clk_cpll_div_500m", "clk_cpll_div_500m_div", 35, 7), + GATE(CPLL_333M, "clk_cpll_div_333m", "clk_cpll_div_333m_div", 35, 8), + GATE(CPLL_250M, "clk_cpll_div_250m", "clk_cpll_div_250m_div", 35, 9), + GATE(CPLL_125M, "clk_cpll_div_125m", "clk_cpll_div_125m_div", 35, 10), + GATE(CPLL_100M, "clk_cpll_div_100m", "clk_cpll_div_100m_div", 35, 11), + GATE(CPLL_62P5M, "clk_cpll_div_62P5m", "clk_cpll_div_62P5m_div", 35, 12), + GATE(CPLL_50M, "clk_cpll_div_50m", "clk_cpll_div_50m_div", 35, 13), + GATE(CPLL_25M, "clk_cpll_div_25m", "clk_cpll_div_25m_div", 35, 14), +}; + +static struct rk_clk_gate pmu_clk_gates[] = +{ + /* PMUCRU_PMUGATE_CON00 */ + GATE(XIN_OSC0_DIV, "xin_osc0_div", "xin_osc0_div_div", 0, 0), + GATE(CLK_RTC_32K, "clk_rtc_32k", "clk_rtc_32k_mux", 0, 1), + GATE(PCLK_PDPMU, "pclk_pdpmu", "pclk_pdpmu_pre", 0, 2), + GATE(PCLK_PMU, "pclk_pmu", "pclk_pdpmu", 0, 6), + GATE(CLK_PMU, "clk_pmu", "xin24m", 0, 7), + /* PMUCRU_PMUGATE_CON01 */ + GATE(PCLK_I2C0, "pclk_i2c0", "pclk_pdpmu", 1, 0), + GATE(CLK_I2C0, "clk_i2c0", "clk_i2c0_div", 1, 1), + GATE(PCLK_UART0, "pclk_uart0", "pclk_pdpmu", 1, 2), + GATE(CLK_UART0_DIV, "sclk_uart0_div", "sclk_uart0_div_div", 1, 3), + GATE(CLK_UART0_FRAC, "sclk_uart0_frac", "sclk_uart0_frac_div", 1, 4), + GATE(SCLK_UART0, "sclk_uart0", "sclk_uart0_mux", 1, 5), + GATE(PCLK_PWM0, "pclk_pwm0", "pclk_pdpmu", 1, 6), + GATE(CLK_PWM0, "clk_pwm0", "clk_pwm0_div", 1, 7), + GATE(CLK_CAPTURE_PWM0_NDFT, "clk_capture_pwm0_ndft", "xin24m", 1, 8), + GATE(PCLK_GPIO0, "pclk_gpio0", "pclk_pdpmu", 1, 9), + GATE(DBCLK_GPIO0, "dbclk_gpio0", "dbclk_gpio0_sel", 1, 10), + GATE(PCLK_PMUPVTM, "pclk_pmupvtm", "pclk_pdpmu", 1, 11), + GATE(CLK_PMUPVTM, "clk_pmupvtm", "xin24m", 1, 12), + GATE(CLK_CORE_PMUPVTM, "clk_core_pmupvtm", "xin24m", 1, 13), + /* PMUCRU_PMUGATE_CON02 */ + GATE(CLK_REF24M, "clk_ref24m", "clk_ref24m_div", 2, 0), + GATE(XIN_OSC0_USBPHY0_G, "xin_osc0_usbphy0_g", "xin24m", 2, 1), + GATE(XIN_OSC0_USBPHY1_G, "xin_osc0_usbphy1_g", "xin24m", 2, 2), + GATE(XIN_OSC0_MIPIDSIPHY0_G, "xin_osc0_mipidsiphy0_g", "xin24m", 2, 3), + GATE(XIN_OSC0_MIPIDSIPHY1_G, "xin_osc0_mipidsiphy1_g", "xin24m", 2, 4), + GATE(CLK_WIFI_DIV, "clk_wifi_div", "clk_wifi_div_div", 2, 5), + GATE(CLK_WIFI_OSC0, "clk_wifi_osc0", "xin24m", 2, 6), + GATE(CLK_PCIEPHY0_DIV, "clk_pciephy0_div", "clk_pciephy0_div_div", 2, 7), + GATE(CLK_PCIEPHY0_OSC0, "clk_pciephy0_osc0", "xin24m", 2, 8), + GATE(CLK_PCIEPHY1_DIV, "clk_pciephy1_div", "clk_pciephy1_div_div", 2, 9), + GATE(CLK_PCIEPHY1_OSC0, "clk_pciephy1_osc0", "xin24m", 2, 10), + GATE(CLK_PCIEPHY2_DIV, "clk_pciephy2_div", "clk_pciephy2_div_div", 2, 11), + GATE(CLK_PCIEPHY2_OSC0, "clk_pciephy2_osc0", "xin24m", 2, 12), + GATE(CLK_PCIE30PHY_REF_M, "clk_pcie30phy_ref_m", "ppll_ph0", 2, 13), + GATE(CLK_PCIE30PHY_REF_N, "clk_pcie30phy_ref_n", "ppll_ph180", 2, 14), + GATE(XIN_OSC0_EDPPHY_G, "xin_osc0_edpphy_g", "xin24m", 2, 15), +}; + +#define PLL_MODE_MASK 0x1 +#include "clk-pll.c" +#include "clk-mmc-phase.c" +#include "softrst.c" + +static struct rk_pmuclk_priv *find_pmu(void) +{ + struct rk_pmuclk_priv *pmu_priv = RT_NULL; + const char *compatible = "rockchip,rk3568-pmucru"; + struct rt_ofw_node *np = rt_ofw_find_node_by_compatible(RT_NULL, compatible); + + if (np) + { + struct rk_clk *rk_clk = rt_ofw_data(np); + + pmu_priv = &rk_clk->pmuclk_info; + rt_ofw_node_put(np); + } + else + { + LOG_E("Find pmucru %s fail", compatible); + } + + return pmu_priv; +} + +static rt_ubase_t pmu_pll_set_rate(rt_ubase_t pll_id, rt_ubase_t rate) +{ + struct rk_pmuclk_priv *pmu_priv = find_pmu(); + + if (pmu_priv) + { + rk_pll_set_rate(&pmu_pll_clks[pll_id], pmu_priv->pmucru, rate); + } + + return 0; +} + +static rt_ubase_t pmu_pll_get_rate(rt_ubase_t pll_id) +{ + struct rk_pmuclk_priv *pmu_priv = find_pmu(); + + if (pmu_priv) + { + return rk_pll_get_rate(&pmu_pll_clks[pll_id], &pmu_priv->pmucru); + } + + return 0; +} + +static rt_ubase_t rtc32k_get_pmuclk(struct rk_pmuclk_priv *priv) +{ + struct rk_pmucru *pmucru = priv->pmucru; + rt_ubase_t m, n; + rt_uint32_t fracdiv; + + fracdiv = HWREG32(&pmucru->pmu_clksel_con[1]); + m = fracdiv & RTC32K_FRAC_NUMERATOR_MASK; + m >>= RTC32K_FRAC_NUMERATOR_SHIFT; + n = fracdiv & RTC32K_FRAC_DENOMINATOR_MASK; + n >>= RTC32K_FRAC_DENOMINATOR_SHIFT; + + return OSC_HZ * m / n; +} + +static rt_ubase_t rtc32k_set_pmuclk(struct rk_pmuclk_priv *priv, rt_ubase_t rate) +{ + struct rk_pmucru *pmucru = priv->pmucru; + rt_ubase_t m, n, val; + + rk_clrsetreg(&pmucru->pmu_clksel_con[0], RTC32K_SEL_MASK, RTC32K_SEL_OSC0_DIV32K << RTC32K_SEL_SHIFT); + + rational_best_approximation(rate, OSC_HZ, RT_GENMASK(16 - 1, 0), RT_GENMASK(16 - 1, 0), &m, &n); + val = m << RTC32K_FRAC_NUMERATOR_SHIFT | n; + HWREG32(&pmucru->pmu_clksel_con[1]) = val; + + return rtc32k_get_pmuclk(priv); +} + +static rt_ubase_t uart_get_pmuclk(struct rk_pmuclk_priv *priv, rt_ubase_t clk_id) +{ + struct rk_pmucru *pmucru = priv->pmucru; + rt_uint32_t reg, con, fracdiv, div, src, p_src, p_rate; + rt_ubase_t m, n; + + switch (clk_id) + { + case SCLK_UART0: + reg = 4; + break; + default: + return -RT_ERROR; + } + + con = HWREG32(&pmucru->pmu_clksel_con[reg]); + src = (con & CLK_UART0_SEL_MASK) >> CLK_UART0_SEL_SHIFT; + div = (con & CLK_UART0_DIV_DIV_MASK) >> CLK_UART0_DIV_DIV_SHIFT; + p_src = (con & CLK_UART0_DIV_SEL_MASK) >> CLK_UART0_DIV_SEL_SHIFT; + + if (p_src == CLK_UART0_SRC_SEL_PPLL) + { + p_rate = priv->ppll_hz; + } + else if (p_src == CLK_UART0_SRC_SEL_GPLL) + { + p_rate = priv->hpll_hz; + } + else + { + p_rate = 480000000; + } + if (src == CLK_UART0_SEL_DIV) + { + return DIV_TO_RATE(p_rate, div); + } + else if (src == CLK_UART0_SEL_FRACDIV) + { + fracdiv = HWREG32(&pmucru->pmu_clksel_con[reg + 1]); + n = fracdiv & CLK_UART0_FRAC_NUMERATOR_MASK; + n >>= CLK_UART0_FRAC_NUMERATOR_SHIFT; + m = fracdiv & CLK_UART0_FRAC_DENOMINATOR_MASK; + m >>= CLK_UART0_FRAC_DENOMINATOR_SHIFT; + return DIV_TO_RATE(p_rate, div) * n / m; + } + else + { + return OSC_HZ; + } +} + +static rt_ubase_t uart_set_pmuclk(struct rk_pmuclk_priv *priv, rt_ubase_t clk_id, rt_ubase_t rate) +{ + struct rk_pmucru *pmucru = priv->pmucru; + rt_uint32_t reg, clk_src, uart_src, div; + rt_ubase_t m = 0, n = 0, val; + + if (priv->ppll_hz % rate == 0) + { + clk_src = CLK_UART0_SRC_SEL_PPLL; + uart_src = CLK_UART0_SEL_DIV; + div = RT_DIV_ROUND_UP(priv->ppll_hz, rate); + } + else if (priv->hpll_hz % rate == 0) + { + clk_src = CLK_UART0_SRC_SEL_GPLL; + uart_src = CLK_UART0_SEL_DIV; + div = RT_DIV_ROUND_UP(priv->hpll_hz, rate); + } + else if (rate == OSC_HZ) + { + clk_src = CLK_UART0_SRC_SEL_GPLL; + uart_src = CLK_UART0_SEL_XIN24M; + div = 2; + } + + switch (clk_id) + { + case SCLK_UART0: + reg = 4; + break; + default: + return -RT_ERROR; + } + + rk_clrsetreg(&pmucru->pmu_clksel_con[reg], CLK_UART0_SEL_MASK | CLK_UART0_DIV_SEL_MASK | CLK_UART0_DIV_DIV_MASK, + (clk_src << CLK_UART0_DIV_SEL_SHIFT) | (uart_src << CLK_UART0_SEL_SHIFT) | + ((div - 1) << CLK_UART0_DIV_DIV_SHIFT)); + if (m && n) + { + val = m << CLK_UART0_FRAC_NUMERATOR_SHIFT | n; + HWREG32(&pmucru->pmu_clksel_con[reg + 1]) = val; + } + + return uart_get_pmuclk(priv, clk_id); +} + +static rt_ubase_t i2c_get_pmuclk(struct rk_pmuclk_priv *priv, rt_ubase_t clk_id) +{ + struct rk_pmucru *pmucru = priv->pmucru; + rt_uint32_t div, con; + + switch (clk_id) + { + case CLK_I2C0: + con = HWREG32(&pmucru->pmu_clksel_con[3]); + div = (con & CLK_I2C0_DIV_MASK) >> CLK_I2C0_DIV_SHIFT; + break; + default: + return -RT_ERROR; + } + + return DIV_TO_RATE(priv->ppll_hz, div); +} + +static rt_ubase_t i2c_set_pmuclk(struct rk_pmuclk_priv *priv, rt_ubase_t clk_id, rt_ubase_t rate) +{ + struct rk_pmucru *pmucru = priv->pmucru; + int src_clk_div; + + src_clk_div = RT_DIV_ROUND_UP(priv->ppll_hz, rate); + RT_ASSERT(src_clk_div - 1 <= 127); + + switch (clk_id) + { + case CLK_I2C0: + rk_clrsetreg(&pmucru->pmu_clksel_con[3], CLK_I2C0_DIV_MASK, (src_clk_div - 1) << CLK_I2C0_DIV_SHIFT); + break; + default: + return -RT_ERROR; + } + + return i2c_get_pmuclk(priv, clk_id); +} + +static rt_ubase_t pwm_get_pmuclk(struct rk_pmuclk_priv *priv, rt_ubase_t clk_id) +{ + struct rk_pmucru *pmucru = priv->pmucru; + rt_uint32_t div, sel, con, parent; + + switch (clk_id) + { + case CLK_PWM0: + con = HWREG32(&pmucru->pmu_clksel_con[6]); + sel = (con & CLK_PWM0_SEL_MASK) >> CLK_PWM0_SEL_SHIFT; + div = (con & CLK_PWM0_DIV_MASK) >> CLK_PWM0_DIV_SHIFT; + if (sel == CLK_PWM0_SEL_XIN24M) + { + parent = OSC_HZ; + } + else + { + parent = priv->ppll_hz; + } + break; + default: + return -RT_ERROR; + } + + return DIV_TO_RATE(parent, div); +} + +static rt_ubase_t pwm_set_pmuclk(struct rk_pmuclk_priv *priv, rt_ubase_t clk_id, rt_ubase_t rate) +{ + struct rk_pmucru *pmucru = priv->pmucru; + int src_clk_div; + + switch (clk_id) + { + case CLK_PWM0: + if (rate == OSC_HZ) + { + rk_clrsetreg(&pmucru->pmu_clksel_con[6], CLK_PWM0_SEL_MASK | CLK_PWM0_DIV_MASK, + (CLK_PWM0_SEL_XIN24M << CLK_PWM0_SEL_SHIFT) | 0 << CLK_PWM0_SEL_SHIFT); + } + else + { + src_clk_div = RT_DIV_ROUND_UP(priv->ppll_hz, rate); + RT_ASSERT(src_clk_div - 1 <= 127); + rk_clrsetreg(&pmucru->pmu_clksel_con[6], CLK_PWM0_DIV_MASK | CLK_PWM0_DIV_MASK, + (CLK_PWM0_SEL_PPLL << CLK_PWM0_SEL_SHIFT) | (src_clk_div - 1) << CLK_PWM0_DIV_SHIFT); + } + break; + default: + return -RT_ERROR; + } + + return pwm_get_pmuclk(priv, clk_id); +} + +static int armclk_set_clk(struct rk_clk_priv *priv, rt_ubase_t hz) +{ + struct rk_cru *cru = priv->cru; + const struct rk_cpu_rate_table *rate; + rt_ubase_t old_rate; + + rate = rk_get_cpu_settings(cpu_rates, hz); + if (!rate) + { + LOG_E("Unsupport rate %u", hz); + + return -RT_ENOSYS; + } + + rk_clrsetreg(&cru->clksel_con[0], CLK_CORE_PRE_SEL_MASK, (CLK_CORE_PRE_SEL_SRC << CLK_CORE_PRE_SEL_SHIFT)); + rk_clrsetreg(&cru->clksel_con[2], + SCLK_CORE_PRE_SEL_MASK | SCLK_CORE_SRC_SEL_MASK | SCLK_CORE_SRC_DIV_MASK, + (SCLK_CORE_PRE_SEL_SRC << SCLK_CORE_PRE_SEL_SHIFT) | + (SCLK_CORE_SRC_SEL_APLL <cru); + if (old_rate > hz) + { + if (rk_pll_set_rate(&pll_clks[apll], priv->cru, hz)) + { + return -RT_ENOSYS; + } + rk_clrsetreg(&cru->clksel_con[3], GICCLK_CORE_DIV_MASK | ATCLK_CORE_DIV_MASK, + rate->pclk_div << GICCLK_CORE_DIV_SHIFT | rate->pclk_div << ATCLK_CORE_DIV_SHIFT); + rk_clrsetreg(&cru->clksel_con[4], PERIPHCLK_CORE_PRE_DIV_MASK | PCLK_CORE_PRE_DIV_MASK, + rate->pclk_div << PCLK_CORE_PRE_DIV_SHIFT | rate->pclk_div << PERIPHCLK_CORE_PRE_DIV_SHIFT); + rk_clrsetreg(&cru->clksel_con[5], ACLK_CORE_NDFT_DIV_MASK, rate->aclk_div << ACLK_CORE_NDFT_DIV_SHIFT); + } + else if (old_rate < hz) + { + rk_clrsetreg(&cru->clksel_con[3], GICCLK_CORE_DIV_MASK | ATCLK_CORE_DIV_MASK, + rate->pclk_div << GICCLK_CORE_DIV_SHIFT | rate->pclk_div << ATCLK_CORE_DIV_SHIFT); + rk_clrsetreg(&cru->clksel_con[4], PERIPHCLK_CORE_PRE_DIV_MASK | PCLK_CORE_PRE_DIV_MASK, + rate->pclk_div << PCLK_CORE_PRE_DIV_SHIFT | rate->pclk_div << PERIPHCLK_CORE_PRE_DIV_SHIFT); + rk_clrsetreg(&cru->clksel_con[5], ACLK_CORE_NDFT_DIV_MASK, rate->aclk_div << ACLK_CORE_NDFT_DIV_SHIFT); + + if (rk_pll_set_rate(&pll_clks[apll], priv->cru, hz)) + { + return -RT_ENOSYS; + } + } + + return 0; +} + +static rt_ubase_t cpll_div_get_rate(struct rk_clk_priv *priv, rt_ubase_t clk_id) +{ + struct rk_cru *cru = priv->cru; + int div, mask, shift, con; + + switch (clk_id) + { + case CPLL_500M: + con = 78; + mask = CPLL_500M_DIV_MASK; + shift = CPLL_500M_DIV_SHIFT; + break; + case CPLL_333M: + con = 79; + mask = CPLL_333M_DIV_MASK; + shift = CPLL_333M_DIV_SHIFT; + break; + case CPLL_250M: + con = 79; + mask = CPLL_250M_DIV_MASK; + shift = CPLL_250M_DIV_SHIFT; + break; + case CPLL_125M: + con = 80; + mask = CPLL_125M_DIV_MASK; + shift = CPLL_125M_DIV_SHIFT; + break; + case CPLL_100M: + con = 82; + mask = CPLL_100M_DIV_MASK; + shift = CPLL_100M_DIV_SHIFT; + break; + case CPLL_62P5M: + con = 80; + mask = CPLL_62P5M_DIV_MASK; + shift = CPLL_62P5M_DIV_SHIFT; + break; + case CPLL_50M: + con = 81; + mask = CPLL_50M_DIV_MASK; + shift = CPLL_50M_DIV_SHIFT; + break; + case CPLL_25M: + con = 81; + mask = CPLL_25M_DIV_MASK; + shift = CPLL_25M_DIV_SHIFT; + break; + default: + return -RT_ERROR; + } + + div = (HWREG32(&cru->clksel_con[con]) & mask) >> shift; + return DIV_TO_RATE(priv->cpll_hz, div); +} + +static rt_ubase_t cpll_div_set_rate(struct rk_clk_priv *priv, rt_ubase_t clk_id, rt_ubase_t rate) +{ + struct rk_cru *cru = priv->cru; + int div, mask, shift, con; + + switch (clk_id) + { + case CPLL_500M: + con = 78; + mask = CPLL_500M_DIV_MASK; + shift = CPLL_500M_DIV_SHIFT; + break; + case CPLL_333M: + con = 79; + mask = CPLL_333M_DIV_MASK; + shift = CPLL_333M_DIV_SHIFT; + break; + case CPLL_250M: + con = 79; + mask = CPLL_250M_DIV_MASK; + shift = CPLL_250M_DIV_SHIFT; + break; + case CPLL_125M: + con = 80; + mask = CPLL_125M_DIV_MASK; + shift = CPLL_125M_DIV_SHIFT; + break; + case CPLL_100M: + con = 82; + mask = CPLL_100M_DIV_MASK; + shift = CPLL_100M_DIV_SHIFT; + break; + case CPLL_62P5M: + con = 80; + mask = CPLL_62P5M_DIV_MASK; + shift = CPLL_62P5M_DIV_SHIFT; + break; + case CPLL_50M: + con = 81; + mask = CPLL_50M_DIV_MASK; + shift = CPLL_50M_DIV_SHIFT; + break; + case CPLL_25M: + con = 81; + mask = CPLL_25M_DIV_MASK; + shift = CPLL_25M_DIV_SHIFT; + break; + default: + return -RT_ERROR; + } + + div = RT_DIV_ROUND_UP(priv->cpll_hz, rate); + RT_ASSERT(div - 1 <= 31); + rk_clrsetreg(&cru->clksel_con[con], mask, (div - 1) << shift); + + return cpll_div_get_rate(priv, clk_id); +} + +static rt_ubase_t bus_get_clk(struct rk_clk_priv *priv, rt_ubase_t clk_id) +{ + struct rk_cru *cru = priv->cru; + rt_uint32_t con, sel, rate; + + switch (clk_id) + { + case ACLK_BUS: + con = HWREG32(&cru->clksel_con[50]); + sel = (con & ACLK_BUS_SEL_MASK) >> ACLK_BUS_SEL_SHIFT; + + if (sel == ACLK_BUS_SEL_200M) + { + rate = 200 * MHZ; + } + else if (sel == ACLK_BUS_SEL_150M) + { + rate = 150 * MHZ; + } + else if (sel == ACLK_BUS_SEL_100M) + { + rate = 100 * MHZ; + } + else + { + rate = OSC_HZ; + } + break; + case PCLK_BUS: + case PCLK_WDT_NS: + con = HWREG32(&cru->clksel_con[50]); + sel = (con & PCLK_BUS_SEL_MASK) >> PCLK_BUS_SEL_SHIFT; + if (sel == PCLK_BUS_SEL_100M) + { + rate = 100 * MHZ; + } + else if (sel == PCLK_BUS_SEL_75M) + { + rate = 75 * MHZ; + } + else if (sel == PCLK_BUS_SEL_50M) + { + rate = 50 * MHZ; + } + else + { + rate = OSC_HZ; + } + break; + default: + return -RT_ERROR; + } + + return rate; +} + +static rt_ubase_t bus_set_clk(struct rk_clk_priv *priv, rt_ubase_t clk_id, rt_ubase_t rate) +{ + struct rk_cru *cru = priv->cru; + int src_clk; + + switch (clk_id) + { + case ACLK_BUS: + if (rate == 200 * MHZ) + { + src_clk = ACLK_BUS_SEL_200M; + } + else if (rate == 150 * MHZ) + { + src_clk = ACLK_BUS_SEL_150M; + } + else if (rate == 100 * MHZ) + { + src_clk = ACLK_BUS_SEL_100M; + } + else + { + src_clk = ACLK_BUS_SEL_24M; + } + rk_clrsetreg(&cru->clksel_con[50], ACLK_BUS_SEL_MASK, src_clk << ACLK_BUS_SEL_SHIFT); + break; + case PCLK_BUS: + case PCLK_WDT_NS: + if (rate == 100 * MHZ) + { + src_clk = PCLK_BUS_SEL_100M; + } + else if (rate == 75 * MHZ) + { + src_clk = PCLK_BUS_SEL_75M; + } + else if (rate == 50 * MHZ) + { + src_clk = PCLK_BUS_SEL_50M; + } + else + { + src_clk = PCLK_BUS_SEL_24M; + } + rk_clrsetreg(&cru->clksel_con[50], PCLK_BUS_SEL_MASK, src_clk << PCLK_BUS_SEL_SHIFT); + break; + + default: + return -RT_ENOSYS; + } + + return bus_get_clk(priv, clk_id); +} + +static rt_ubase_t perimid_get_clk(struct rk_clk_priv *priv, rt_ubase_t clk_id) +{ + struct rk_cru *cru = priv->cru; + rt_uint32_t con, sel, rate; + + switch (clk_id) + { + case ACLK_PERIMID: + con = HWREG32(&cru->clksel_con[10]); + sel = (con & ACLK_PERIMID_SEL_MASK) >> ACLK_PERIMID_SEL_SHIFT; + if (sel == ACLK_PERIMID_SEL_300M) + { + rate = 300 * MHZ; + } + else if (sel == ACLK_PERIMID_SEL_200M) + { + rate = 200 * MHZ; + } + else if (sel == ACLK_PERIMID_SEL_100M) + { + rate = 100 * MHZ; + } + else + { + rate = OSC_HZ; + } + break; + case HCLK_PERIMID: + con = HWREG32(&cru->clksel_con[10]); + sel = (con & HCLK_PERIMID_SEL_MASK) >> HCLK_PERIMID_SEL_SHIFT; + if (sel == HCLK_PERIMID_SEL_150M) + { + rate = 150 * MHZ; + } + else if (sel == HCLK_PERIMID_SEL_100M) + { + rate = 100 * MHZ; + } + else if (sel == HCLK_PERIMID_SEL_75M) + { + rate = 75 * MHZ; + } + else + { + rate = OSC_HZ; + } + break; + default: + return -RT_ERROR; + } + + return rate; +} + +static rt_ubase_t perimid_set_clk(struct rk_clk_priv *priv, rt_ubase_t clk_id, rt_ubase_t rate) +{ + struct rk_cru *cru = priv->cru; + int src_clk; + + switch (clk_id) + { + case ACLK_PERIMID: + if (rate == 300 * MHZ) + { + src_clk = ACLK_PERIMID_SEL_300M; + } + else if (rate == 200 * MHZ) + { + src_clk = ACLK_PERIMID_SEL_200M; + } + else if (rate == 100 * MHZ) + { + src_clk = ACLK_PERIMID_SEL_100M; + } + else + { + src_clk = ACLK_PERIMID_SEL_24M; + } + rk_clrsetreg(&cru->clksel_con[10], ACLK_PERIMID_SEL_MASK, src_clk << ACLK_PERIMID_SEL_SHIFT); + break; + case HCLK_PERIMID: + if (rate == 150 * MHZ) + { + src_clk = HCLK_PERIMID_SEL_150M; + } + else if (rate == 100 * MHZ) + { + src_clk = HCLK_PERIMID_SEL_100M; + } + else if (rate == 75 * MHZ) + { + src_clk = HCLK_PERIMID_SEL_75M; + } + else + { + src_clk = HCLK_PERIMID_SEL_24M; + } + rk_clrsetreg(&cru->clksel_con[10], HCLK_PERIMID_SEL_MASK, src_clk << HCLK_PERIMID_SEL_SHIFT); + break; + + default: + return -RT_ENOSYS; + } + + return perimid_get_clk(priv, clk_id); +} + +static rt_ubase_t top_get_clk(struct rk_clk_priv *priv, rt_ubase_t clk_id) +{ + struct rk_cru *cru = priv->cru; + rt_uint32_t con, sel, rate; + + switch (clk_id) + { + case ACLK_TOP_HIGH: + con = HWREG32(&cru->clksel_con[73]); + sel = (con & ACLK_TOP_HIGH_SEL_MASK) >> ACLK_TOP_HIGH_SEL_SHIFT; + if (sel == ACLK_TOP_HIGH_SEL_500M) + { + rate = 500 * MHZ; + } + else if (sel == ACLK_TOP_HIGH_SEL_400M) + { + rate = 400 * MHZ; + } + else if (sel == ACLK_TOP_HIGH_SEL_300M) + { + rate = 300 * MHZ; + } + else + { + rate = OSC_HZ; + } + break; + case ACLK_TOP_LOW: + con = HWREG32(&cru->clksel_con[73]); + sel = (con & ACLK_TOP_LOW_SEL_MASK) >> ACLK_TOP_LOW_SEL_SHIFT; + if (sel == ACLK_TOP_LOW_SEL_400M) + { + rate = 400 * MHZ; + } + else if (sel == ACLK_TOP_LOW_SEL_300M) + { + rate = 300 * MHZ; + } + else if (sel == ACLK_TOP_LOW_SEL_200M) + { + rate = 200 * MHZ; + } + else + { + rate = OSC_HZ; + } + break; + case HCLK_TOP: + con = HWREG32(&cru->clksel_con[73]); + sel = (con & HCLK_TOP_SEL_MASK) >> HCLK_TOP_SEL_SHIFT; + if (sel == HCLK_TOP_SEL_150M) + { + rate = 150 * MHZ; + } + else if (sel == HCLK_TOP_SEL_100M) + { + rate = 100 * MHZ; + } + else if (sel == HCLK_TOP_SEL_75M) + { + rate = 75 * MHZ; + } + else + { + rate = OSC_HZ; + } + break; + case PCLK_TOP: + con = HWREG32(&cru->clksel_con[73]); + sel = (con & PCLK_TOP_SEL_MASK) >> PCLK_TOP_SEL_SHIFT; + if (sel == PCLK_TOP_SEL_100M) + { + rate = 100 * MHZ; + } + else if (sel == PCLK_TOP_SEL_75M) + { + rate = 75 * MHZ; + } + else if (sel == PCLK_TOP_SEL_50M) + { + rate = 50 * MHZ; + } + else + { + rate = OSC_HZ; + } + break; + default: + return -RT_ERROR; + } + + return rate; +} + +static rt_ubase_t top_set_clk(struct rk_clk_priv *priv, rt_ubase_t clk_id, rt_ubase_t rate) +{ + struct rk_cru *cru = priv->cru; + int src_clk; + + switch (clk_id) + { + case ACLK_TOP_HIGH: + if (rate == 500 * MHZ) + { + src_clk = ACLK_TOP_HIGH_SEL_500M; + } + else if (rate == 400 * MHZ) + { + src_clk = ACLK_TOP_HIGH_SEL_400M; + } + else if (rate == 300 * MHZ) + { + src_clk = ACLK_TOP_HIGH_SEL_300M; + } + else + { + src_clk = ACLK_TOP_HIGH_SEL_24M; + } + rk_clrsetreg(&cru->clksel_con[73], ACLK_TOP_HIGH_SEL_MASK, src_clk << ACLK_TOP_HIGH_SEL_SHIFT); + break; + case ACLK_TOP_LOW: + if (rate == 400 * MHZ) + { + src_clk = ACLK_TOP_LOW_SEL_400M; + } + else if (rate == 300 * MHZ) + { + src_clk = ACLK_TOP_LOW_SEL_300M; + } + else if (rate == 200 * MHZ) + { + src_clk = ACLK_TOP_LOW_SEL_200M; + } + else + { + src_clk = ACLK_TOP_LOW_SEL_24M; + } + rk_clrsetreg(&cru->clksel_con[73], ACLK_TOP_LOW_SEL_MASK, src_clk << ACLK_TOP_LOW_SEL_SHIFT); + break; + case HCLK_TOP: + if (rate == 150 * MHZ) + { + src_clk = HCLK_TOP_SEL_150M; + } + else if (rate == 100 * MHZ) + { + src_clk = HCLK_TOP_SEL_100M; + } + else if (rate == 75 * MHZ) + { + src_clk = HCLK_TOP_SEL_75M; + } + else + { + src_clk = HCLK_TOP_SEL_24M; + } + rk_clrsetreg(&cru->clksel_con[73], HCLK_TOP_SEL_MASK, src_clk << HCLK_TOP_SEL_SHIFT); + break; + case PCLK_TOP: + if (rate == 100 * MHZ) + { + src_clk = PCLK_TOP_SEL_100M; + } + else if (rate == 75 * MHZ) + { + src_clk = PCLK_TOP_SEL_75M; + } + else if (rate == 50 * MHZ) + { + src_clk = PCLK_TOP_SEL_50M; + } + else + { + src_clk = PCLK_TOP_SEL_24M; + } + rk_clrsetreg(&cru->clksel_con[73], PCLK_TOP_SEL_MASK, src_clk << PCLK_TOP_SEL_SHIFT); + break; + + default: + return -RT_ENOSYS; + } + + return top_get_clk(priv, clk_id); +} + +static rt_ubase_t i2c_get_clk(struct rk_clk_priv *priv, rt_ubase_t clk_id) +{ + struct rk_cru *cru = priv->cru; + rt_uint32_t sel, con; + rt_ubase_t rate; + + switch (clk_id) + { + case CLK_I2C1: + case CLK_I2C2: + case CLK_I2C3: + case CLK_I2C4: + case CLK_I2C5: + con = HWREG32(&cru->clksel_con[71]); + sel = (con & CLK_I2C_SEL_MASK) >> CLK_I2C_SEL_SHIFT; + if (sel == CLK_I2C_SEL_200M) + { + rate = 200 * MHZ; + } + else if (sel == CLK_I2C_SEL_100M) + { + rate = 100 * MHZ; + } + else if (sel == CLK_I2C_SEL_CPLL_100M) + { + rate = 100 * MHZ; + } + else + { + rate = OSC_HZ; + } + break; + default: + return -RT_ERROR; + } + + return rate; +} + +static rt_ubase_t i2c_set_clk(struct rk_clk_priv *priv, rt_ubase_t clk_id, rt_ubase_t rate) +{ + struct rk_cru *cru = priv->cru; + int src_clk; + + if (rate == 200 * MHZ) + { + src_clk = CLK_I2C_SEL_200M; + } + else if (rate == 100 * MHZ) + { + src_clk = CLK_I2C_SEL_100M; + } + else + { + src_clk = CLK_I2C_SEL_24M; + } + + switch (clk_id) + { + case CLK_I2C1: + case CLK_I2C2: + case CLK_I2C3: + case CLK_I2C4: + case CLK_I2C5: + rk_clrsetreg(&cru->clksel_con[71], CLK_I2C_SEL_MASK, src_clk << CLK_I2C_SEL_SHIFT); + break; + default: + return -RT_ERROR; + } + + return i2c_get_clk(priv, clk_id); +} + +static rt_ubase_t spi_get_clk(struct rk_clk_priv *priv, rt_ubase_t clk_id) +{ + struct rk_cru *cru = priv->cru; + rt_uint32_t sel, con; + + con = HWREG32(&cru->clksel_con[72]); + + switch (clk_id) + { + case CLK_SPI0: + sel = (con & CLK_SPI0_SEL_MASK) >> CLK_SPI0_SEL_SHIFT; + break; + case CLK_SPI1: + sel = (con & CLK_SPI1_SEL_MASK) >> CLK_SPI1_SEL_SHIFT; + break; + case CLK_SPI2: + sel = (con & CLK_SPI2_SEL_MASK) >> CLK_SPI2_SEL_SHIFT; + break; + case CLK_SPI3: + sel = (con & CLK_SPI3_SEL_MASK) >> CLK_SPI3_SEL_SHIFT; + break; + default: + return -RT_ERROR; + } + + switch (sel) + { + case CLK_SPI_SEL_200M: + return 200 * MHZ; + case CLK_SPI_SEL_24M: + return OSC_HZ; + case CLK_SPI_SEL_CPLL_100M: + return 100 * MHZ; + default: + return -RT_ERROR; + } +} + +static rt_ubase_t spi_set_clk(struct rk_clk_priv *priv, rt_ubase_t clk_id, rt_ubase_t rate) +{ + struct rk_cru *cru = priv->cru; + int src_clk; + + if (rate == 200 * MHZ) + { + src_clk = CLK_SPI_SEL_200M; + } + else if (rate == 100 * MHZ) + { + src_clk = CLK_SPI_SEL_CPLL_100M; + } + else + { + src_clk = CLK_SPI_SEL_24M; + } + + switch (clk_id) + { + case CLK_SPI0: + rk_clrsetreg(&cru->clksel_con[72], CLK_SPI0_SEL_MASK, src_clk << CLK_SPI0_SEL_SHIFT); + break; + case CLK_SPI1: + rk_clrsetreg(&cru->clksel_con[72], CLK_SPI1_SEL_MASK, src_clk << CLK_SPI1_SEL_SHIFT); + break; + case CLK_SPI2: + rk_clrsetreg(&cru->clksel_con[72], CLK_SPI2_SEL_MASK, src_clk << CLK_SPI2_SEL_SHIFT); + break; + case CLK_SPI3: + rk_clrsetreg(&cru->clksel_con[72], CLK_SPI3_SEL_MASK, src_clk << CLK_SPI3_SEL_SHIFT); + break; + default: + return -RT_ERROR; + } + + return spi_get_clk(priv, clk_id); +} + +static rt_ubase_t pwm_get_clk(struct rk_clk_priv *priv, rt_ubase_t clk_id) +{ + struct rk_cru *cru = priv->cru; + rt_uint32_t sel, con; + + con = HWREG32(&cru->clksel_con[72]); + + switch (clk_id) + { + case CLK_PWM1: + sel = (con & CLK_PWM1_SEL_MASK) >> CLK_PWM3_SEL_SHIFT; + break; + case CLK_PWM2: + sel = (con & CLK_PWM2_SEL_MASK) >> CLK_PWM2_SEL_SHIFT; + break; + case CLK_PWM3: + sel = (con & CLK_PWM3_SEL_MASK) >> CLK_PWM3_SEL_SHIFT; + break; + default: + return -RT_ERROR; + } + + switch (sel) + { + case CLK_PWM_SEL_100M: + return 100 * MHZ; + case CLK_PWM_SEL_24M: + return OSC_HZ; + case CLK_PWM_SEL_CPLL_100M: + return 100 * MHZ; + default: + return -RT_ERROR; + } +} + +static rt_ubase_t pwm_set_clk(struct rk_clk_priv *priv, rt_ubase_t clk_id, rt_ubase_t rate) +{ + struct rk_cru *cru = priv->cru; + int src_clk; + + if (rate == 100 * MHZ) + { + src_clk = CLK_PWM_SEL_100M; + } + else + { + src_clk = CLK_PWM_SEL_24M; + } + + switch (clk_id) + { + case CLK_PWM1: + rk_clrsetreg(&cru->clksel_con[72], CLK_PWM1_SEL_MASK, src_clk << CLK_PWM1_SEL_SHIFT); + break; + case CLK_PWM2: + rk_clrsetreg(&cru->clksel_con[72], CLK_PWM2_SEL_MASK, src_clk << CLK_PWM2_SEL_SHIFT); + break; + case CLK_PWM3: + rk_clrsetreg(&cru->clksel_con[72], CLK_PWM3_SEL_MASK, src_clk << CLK_PWM3_SEL_SHIFT); + break; + default: + return -RT_ERROR; + } + + return pwm_get_clk(priv, clk_id); +} + +static rt_ubase_t adc_get_clk(struct rk_clk_priv *priv, rt_ubase_t clk_id) +{ + struct rk_cru *cru = priv->cru; + rt_uint32_t div, sel, con, prate; + + switch (clk_id) + { + case CLK_SARADC: + return OSC_HZ; + case CLK_TSADC_TSEN: + con = HWREG32(&cru->clksel_con[51]); + div = (con & CLK_TSADC_TSEN_DIV_MASK) >> CLK_TSADC_TSEN_DIV_SHIFT; + sel = (con & CLK_TSADC_TSEN_SEL_MASK) >> CLK_TSADC_TSEN_SEL_SHIFT; + if (sel == CLK_TSADC_TSEN_SEL_24M) + { + prate = OSC_HZ; + } + else + { + prate = 100 * MHZ; + } + return DIV_TO_RATE(prate, div); + case CLK_TSADC: + con = HWREG32(&cru->clksel_con[51]); + div = (con & CLK_TSADC_DIV_MASK) >> CLK_TSADC_DIV_SHIFT; + prate = adc_get_clk(priv, CLK_TSADC_TSEN); + return DIV_TO_RATE(prate, div); + default: + return -RT_ERROR; + } +} + +static rt_ubase_t adc_set_clk(struct rk_clk_priv *priv, rt_ubase_t clk_id, rt_ubase_t rate) +{ + struct rk_cru *cru = priv->cru; + int src_clk_div; + rt_ubase_t prate = 0; + + switch (clk_id) + { + case CLK_SARADC: + return OSC_HZ; + case CLK_TSADC_TSEN: + if (!(OSC_HZ % rate)) + { + src_clk_div = RT_DIV_ROUND_UP(OSC_HZ, rate); + RT_ASSERT(src_clk_div - 1 <= 7); + rk_clrsetreg(&cru->clksel_con[51], CLK_TSADC_TSEN_SEL_MASK | CLK_TSADC_TSEN_DIV_MASK, + (CLK_TSADC_TSEN_SEL_24M << CLK_TSADC_TSEN_SEL_SHIFT) | + (src_clk_div - 1) << CLK_TSADC_TSEN_DIV_SHIFT); + } + else + { + src_clk_div = RT_DIV_ROUND_UP(100 * MHZ, rate); + RT_ASSERT(src_clk_div - 1 <= 7); + rk_clrsetreg(&cru->clksel_con[51], CLK_TSADC_TSEN_SEL_MASK | CLK_TSADC_TSEN_DIV_MASK, + (CLK_TSADC_TSEN_SEL_100M << CLK_TSADC_TSEN_SEL_SHIFT) | + (src_clk_div - 1) << CLK_TSADC_TSEN_DIV_SHIFT); + } + break; + case CLK_TSADC: + prate = adc_get_clk(priv, CLK_TSADC_TSEN); + src_clk_div = RT_DIV_ROUND_UP(prate, rate); + RT_ASSERT(src_clk_div - 1 <= 128); + rk_clrsetreg(&cru->clksel_con[51], CLK_TSADC_DIV_MASK, (src_clk_div - 1) << CLK_TSADC_DIV_SHIFT); + break; + default: + return -RT_ERROR; + } + return adc_get_clk(priv, clk_id); +} + +static rt_ubase_t crypto_get_rate(struct rk_clk_priv *priv, rt_ubase_t clk_id) +{ + struct rk_cru *cru = priv->cru; + rt_uint32_t sel, con; + + switch (clk_id) + { + case ACLK_SECURE_FLASH: + case ACLK_CRYPTO_NS: + con = HWREG32(&cru->clksel_con[27]); + sel = (con & ACLK_SECURE_FLASH_SEL_MASK) >> ACLK_SECURE_FLASH_SEL_SHIFT; + if (sel == ACLK_SECURE_FLASH_SEL_200M) + { + return 200 * MHZ; + } + else if (sel == ACLK_SECURE_FLASH_SEL_150M) + { + return 150 * MHZ; + } + else if (sel == ACLK_SECURE_FLASH_SEL_100M) + { + return 100 * MHZ; + } + else + { + return 24 * MHZ; + } + case HCLK_SECURE_FLASH: + case HCLK_CRYPTO_NS: + case CLK_CRYPTO_NS_RNG: + con = HWREG32(&cru->clksel_con[27]); + sel = (con & HCLK_SECURE_FLASH_SEL_MASK) >> HCLK_SECURE_FLASH_SEL_SHIFT; + if (sel == HCLK_SECURE_FLASH_SEL_150M) + { + return 150 * MHZ; + } + else if (sel == HCLK_SECURE_FLASH_SEL_100M) + { + return 100 * MHZ; + } + else if (sel == HCLK_SECURE_FLASH_SEL_75M) + { + return 75 * MHZ; + } + else + { + return 24 * MHZ; + } + case CLK_CRYPTO_NS_CORE: + con = HWREG32(&cru->clksel_con[27]); + sel = (con & CLK_CRYPTO_CORE_SEL_MASK) >> CLK_CRYPTO_CORE_SEL_SHIFT; + if (sel == CLK_CRYPTO_CORE_SEL_200M) + { + return 200 * MHZ; + } + else if (sel == CLK_CRYPTO_CORE_SEL_150M) + { + return 150 * MHZ; + } + else + { + return 100 * MHZ; + } + case CLK_CRYPTO_NS_PKA: + con = HWREG32(&cru->clksel_con[27]); + sel = (con & CLK_CRYPTO_PKA_SEL_MASK) >> CLK_CRYPTO_PKA_SEL_SHIFT; + if (sel == CLK_CRYPTO_PKA_SEL_300M) + { + return 300 * MHZ; + } + else if (sel == CLK_CRYPTO_PKA_SEL_200M) + { + return 200 * MHZ; + } + else + { + return 100 * MHZ; + } + default: + return -RT_ERROR; + } +} + +static rt_ubase_t crypto_set_rate(struct rk_clk_priv *priv, rt_ubase_t clk_id, rt_ubase_t rate) +{ + struct rk_cru *cru = priv->cru; + rt_uint32_t src_clk, mask, shift; + + switch (clk_id) + { + case ACLK_SECURE_FLASH: + case ACLK_CRYPTO_NS: + mask = ACLK_SECURE_FLASH_SEL_MASK; + shift = ACLK_SECURE_FLASH_SEL_SHIFT; + if (rate == 200 * MHZ) + { + src_clk = ACLK_SECURE_FLASH_SEL_200M; + } + else if (rate == 150 * MHZ) + { + src_clk = ACLK_SECURE_FLASH_SEL_150M; + } + else if (rate == 100 * MHZ) + { + src_clk = ACLK_SECURE_FLASH_SEL_100M; + } + else + { + src_clk = ACLK_SECURE_FLASH_SEL_24M; + } + break; + case HCLK_SECURE_FLASH: + case HCLK_CRYPTO_NS: + case CLK_CRYPTO_NS_RNG: + mask = HCLK_SECURE_FLASH_SEL_MASK; + shift = HCLK_SECURE_FLASH_SEL_SHIFT; + if (rate == 150 * MHZ) + { + src_clk = HCLK_SECURE_FLASH_SEL_150M; + } + else if (rate == 100 * MHZ) + { + src_clk = HCLK_SECURE_FLASH_SEL_100M; + } + else if (rate == 75 * MHZ) + { + src_clk = HCLK_SECURE_FLASH_SEL_75M; + } + else + { + src_clk = HCLK_SECURE_FLASH_SEL_24M; + } + break; + case CLK_CRYPTO_NS_CORE: + mask = CLK_CRYPTO_CORE_SEL_MASK; + shift = CLK_CRYPTO_CORE_SEL_SHIFT; + if (rate == 200 * MHZ) + { + src_clk = CLK_CRYPTO_CORE_SEL_200M; + } + else if (rate == 150 * MHZ) + { + src_clk = CLK_CRYPTO_CORE_SEL_150M; + } + else + { + src_clk = CLK_CRYPTO_CORE_SEL_100M; + } + break; + case CLK_CRYPTO_NS_PKA: + mask = CLK_CRYPTO_PKA_SEL_MASK; + shift = CLK_CRYPTO_PKA_SEL_SHIFT; + if (rate == 300 * MHZ) + { + src_clk = CLK_CRYPTO_PKA_SEL_300M; + } + else if (rate == 200 * MHZ) + { + src_clk = CLK_CRYPTO_PKA_SEL_200M; + } + else + { + src_clk = CLK_CRYPTO_PKA_SEL_100M; + } + break; + default: + return -RT_ERROR; + } + + rk_clrsetreg(&cru->clksel_con[27], mask, src_clk << shift); + + return crypto_get_rate(priv, clk_id); +} + +static rt_ubase_t sdmmc_get_clk(struct rk_clk_priv *priv, rt_ubase_t clk_id) +{ + struct rk_cru *cru = priv->cru; + rt_uint32_t sel, con; + + switch (clk_id) + { + case HCLK_SDMMC0: + case CLK_SDMMC0: + con = HWREG32(&cru->clksel_con[30]); + sel = (con & CLK_SDMMC0_SEL_MASK) >> CLK_SDMMC0_SEL_SHIFT; + break; + case CLK_SDMMC1: + con = HWREG32(&cru->clksel_con[30]); + sel = (con & CLK_SDMMC1_SEL_MASK) >> CLK_SDMMC1_SEL_SHIFT; + break; + case CLK_SDMMC2: + con = HWREG32(&cru->clksel_con[32]); + sel = (con & CLK_SDMMC2_SEL_MASK) >> CLK_SDMMC2_SEL_SHIFT; + break; + default: + return -RT_ERROR; + } + + switch (sel) + { + case CLK_SDMMC_SEL_24M: + return OSC_HZ; + case CLK_SDMMC_SEL_400M: + return 400 * MHZ; + case CLK_SDMMC_SEL_300M: + return 300 * MHZ; + case CLK_SDMMC_SEL_100M: + return 100 * MHZ; + case CLK_SDMMC_SEL_50M: + return 50 * MHZ; + case CLK_SDMMC_SEL_750K: + return 750 * KHZ; + default: + return -RT_ERROR; + } +} + +static rt_ubase_t sdmmc_set_clk(struct rk_clk_priv *priv, rt_ubase_t clk_id, rt_ubase_t rate) +{ + struct rk_cru *cru = priv->cru; + int src_clk; + + switch (rate) + { + case OSC_HZ: + src_clk = CLK_SDMMC_SEL_24M; + break; + case 400 * MHZ: + src_clk = CLK_SDMMC_SEL_400M; + break; + case 300 * MHZ: + src_clk = CLK_SDMMC_SEL_300M; + break; + case 100 * MHZ: + src_clk = CLK_SDMMC_SEL_100M; + break; + case 52 * MHZ: + case 50 * MHZ: + src_clk = CLK_SDMMC_SEL_50M; + break; + case 750 * KHZ: + case 400 * KHZ: + src_clk = CLK_SDMMC_SEL_750K; + break; + default: + return -RT_ERROR; + } + + switch (clk_id) + { + case HCLK_SDMMC0: + case CLK_SDMMC0: + rk_clrsetreg(&cru->clksel_con[30], CLK_SDMMC0_SEL_MASK, src_clk << CLK_SDMMC0_SEL_SHIFT); + break; + case CLK_SDMMC1: + rk_clrsetreg(&cru->clksel_con[30], CLK_SDMMC1_SEL_MASK, src_clk << CLK_SDMMC1_SEL_SHIFT); + break; + case CLK_SDMMC2: + rk_clrsetreg(&cru->clksel_con[32], CLK_SDMMC2_SEL_MASK, src_clk << CLK_SDMMC2_SEL_SHIFT); + break; + default: + return -RT_ERROR; + } + + return sdmmc_get_clk(priv, clk_id); +} + +static rt_ubase_t sfc_get_clk(struct rk_clk_priv *priv) +{ + struct rk_cru *cru = priv->cru; + rt_uint32_t sel, con; + + con = HWREG32(&cru->clksel_con[28]); + sel = (con & SCLK_SFC_SEL_MASK) >> SCLK_SFC_SEL_SHIFT; + + switch (sel) + { + case SCLK_SFC_SEL_24M: + return OSC_HZ; + case SCLK_SFC_SEL_50M: + return 50 * MHZ; + case SCLK_SFC_SEL_75M: + return 75 * MHZ; + case SCLK_SFC_SEL_100M: + return 100 * MHZ; + case SCLK_SFC_SEL_125M: + return 125 * MHZ; + case SCLK_SFC_SEL_150M: + return 150 * KHZ; + default: + return -RT_ERROR; + } +} + +static rt_ubase_t sfc_set_clk(struct rk_clk_priv *priv, rt_ubase_t rate) +{ + struct rk_cru *cru = priv->cru; + int src_clk; + + switch (rate) + { + case OSC_HZ: + src_clk = SCLK_SFC_SEL_24M; + break; + case 50 * MHZ: + src_clk = SCLK_SFC_SEL_50M; + break; + case 75 * MHZ: + src_clk = SCLK_SFC_SEL_75M; + break; + case 100 * MHZ: + src_clk = SCLK_SFC_SEL_100M; + break; + case 125 * MHZ: + src_clk = SCLK_SFC_SEL_125M; + break; + case 150 * KHZ: + src_clk = SCLK_SFC_SEL_150M; + break; + default: + return -RT_ERROR; + } + + rk_clrsetreg(&cru->clksel_con[28], SCLK_SFC_SEL_MASK, src_clk << SCLK_SFC_SEL_SHIFT); + + return sfc_get_clk(priv); +} + +static rt_ubase_t nand_get_clk(struct rk_clk_priv *priv) +{ + struct rk_cru *cru = priv->cru; + rt_uint32_t sel, con; + + con = HWREG32(&cru->clksel_con[28]); + sel = (con & NCLK_NANDC_SEL_MASK) >> NCLK_NANDC_SEL_SHIFT; + + switch (sel) + { + case NCLK_NANDC_SEL_200M: + return 200 * MHZ; + case NCLK_NANDC_SEL_150M: + return 150 * MHZ; + case NCLK_NANDC_SEL_100M: + return 100 * MHZ; + case NCLK_NANDC_SEL_24M: + return OSC_HZ; + default: + return -RT_ERROR; + } +} + +static rt_ubase_t nand_set_clk(struct rk_clk_priv *priv, rt_ubase_t rate) +{ + struct rk_cru *cru = priv->cru; + int src_clk; + + switch (rate) + { + case OSC_HZ: + src_clk = NCLK_NANDC_SEL_24M; + break; + case 100 * MHZ: + src_clk = NCLK_NANDC_SEL_100M; + break; + case 150 * MHZ: + src_clk = NCLK_NANDC_SEL_150M; + break; + case 200 * MHZ: + src_clk = NCLK_NANDC_SEL_200M; + break; + default: + return -RT_ERROR; + } + + rk_clrsetreg(&cru->clksel_con[28], NCLK_NANDC_SEL_MASK, src_clk << NCLK_NANDC_SEL_SHIFT); + + return nand_get_clk(priv); +} + +static rt_ubase_t emmc_get_clk(struct rk_clk_priv *priv) +{ + struct rk_cru *cru = priv->cru; + rt_uint32_t sel, con; + + con = HWREG32(&cru->clksel_con[28]); + sel = (con & CCLK_EMMC_SEL_MASK) >> CCLK_EMMC_SEL_SHIFT; + + switch (sel) + { + case CCLK_EMMC_SEL_200M: + return 200 * MHZ; + case CCLK_EMMC_SEL_150M: + return 150 * MHZ; + case CCLK_EMMC_SEL_100M: + return 100 * MHZ; + case CCLK_EMMC_SEL_50M: + return 50 * MHZ; + case CCLK_EMMC_SEL_375K: + return 375 * KHZ; + case CCLK_EMMC_SEL_24M: + return OSC_HZ; + default: + return -RT_ERROR; + } +} + +static rt_ubase_t emmc_set_clk(struct rk_clk_priv *priv, rt_ubase_t rate) +{ + struct rk_cru *cru = priv->cru; + int src_clk; + + switch (rate) + { + case OSC_HZ: + src_clk = CCLK_EMMC_SEL_24M; + break; + case 52 * MHZ: + case 50 * MHZ: + src_clk = CCLK_EMMC_SEL_50M; + break; + case 100 * MHZ: + src_clk = CCLK_EMMC_SEL_100M; + break; + case 150 * MHZ: + src_clk = CCLK_EMMC_SEL_150M; + break; + case 200 * MHZ: + src_clk = CCLK_EMMC_SEL_200M; + break; + case 400 * KHZ: + case 375 * KHZ: + src_clk = CCLK_EMMC_SEL_375K; + break; + default: + return -RT_ERROR; + } + + rk_clrsetreg(&cru->clksel_con[28], CCLK_EMMC_SEL_MASK, src_clk << CCLK_EMMC_SEL_SHIFT); + + return emmc_get_clk(priv); +} + +static rt_ubase_t emmc_get_bclk(struct rk_clk_priv *priv) +{ + struct rk_cru *cru = priv->cru; + rt_uint32_t sel, con; + + con = HWREG32(&cru->clksel_con[28]); + sel = (con & BCLK_EMMC_SEL_MASK) >> BCLK_EMMC_SEL_SHIFT; + + switch (sel) + { + case BCLK_EMMC_SEL_200M: + return 200 * MHZ; + case BCLK_EMMC_SEL_150M: + return 150 * MHZ; + case BCLK_EMMC_SEL_125M: + return 125 * MHZ; + default: + return -RT_ERROR; + } +} + +static rt_ubase_t emmc_set_bclk(struct rk_clk_priv *priv, rt_ubase_t rate) +{ + struct rk_cru *cru = priv->cru; + int src_clk; + + switch (rate) + { + case 200 * MHZ: + src_clk = BCLK_EMMC_SEL_200M; + break; + case 150 * MHZ: + src_clk = BCLK_EMMC_SEL_150M; + break; + case 125 * MHZ: + src_clk = BCLK_EMMC_SEL_125M; + break; + default: + return -RT_ERROR; + } + + rk_clrsetreg(&cru->clksel_con[28], BCLK_EMMC_SEL_MASK, src_clk << BCLK_EMMC_SEL_SHIFT); + + return emmc_get_bclk(priv); +} + +static rt_ubase_t aclk_vop_get_clk(struct rk_clk_priv *priv) +{ + struct rk_cru *cru = priv->cru; + rt_uint32_t div, sel, con, parent; + + con = HWREG32(&cru->clksel_con[38]); + div = (con & ACLK_VOP_PRE_DIV_MASK) >> ACLK_VOP_PRE_DIV_SHIFT; + sel = (con & ACLK_VOP_PRE_SEL_MASK) >> ACLK_VOP_PRE_SEL_SHIFT; + + if (sel == ACLK_VOP_PRE_SEL_GPLL) + { + parent = priv->gpll_hz; + } + else if (sel == ACLK_VOP_PRE_SEL_CPLL) + { + parent = priv->cpll_hz; + } + else if (sel == ACLK_VOP_PRE_SEL_VPLL) + { + parent = priv->vpll_hz; + } + else + { + parent = priv->hpll_hz; + } + + return DIV_TO_RATE(parent, div); +} + +static rt_ubase_t aclk_vop_set_clk(struct rk_clk_priv *priv, rt_ubase_t rate) +{ + struct rk_cru *cru = priv->cru; + int src_clk_div, src_clk_mux; + + if ((priv->cpll_hz % rate) == 0) + { + src_clk_div = RT_DIV_ROUND_UP(priv->cpll_hz, rate); + src_clk_mux = ACLK_VOP_PRE_SEL_CPLL; + } + else + { + src_clk_div = RT_DIV_ROUND_UP(priv->gpll_hz, rate); + src_clk_mux = ACLK_VOP_PRE_SEL_GPLL; + } + + RT_ASSERT(src_clk_div - 1 <= 31); + rk_clrsetreg(&cru->clksel_con[38], ACLK_VOP_PRE_SEL_MASK | ACLK_VOP_PRE_DIV_MASK, + src_clk_mux << ACLK_VOP_PRE_SEL_SHIFT | (src_clk_div - 1) << ACLK_VOP_PRE_DIV_SHIFT); + + return aclk_vop_get_clk(priv); +} + +static rt_ubase_t dclk_vop_get_clk(struct rk_clk_priv *priv, rt_ubase_t clk_id) +{ + struct rk_cru *cru = priv->cru; + rt_uint32_t conid, div, sel, con, parent; + + switch (clk_id) + { + case DCLK_VOP0: + conid = 39; + break; + case DCLK_VOP1: + conid = 40; + break; + case DCLK_VOP2: + conid = 41; + break; + default: + return -RT_ERROR; + } + + con = HWREG32(&cru->clksel_con[conid]); + div = (con & DCLK0_VOP_DIV_MASK) >> DCLK0_VOP_DIV_SHIFT; + sel = (con & DCLK0_VOP_SEL_MASK) >> DCLK0_VOP_SEL_SHIFT; + + if (sel == DCLK_VOP_SEL_HPLL) + { + parent = pmu_pll_get_rate(hpll); + } + else if (sel == DCLK_VOP_SEL_VPLL) + { + parent = rk_pll_get_rate(&pll_clks[vpll], &priv->cru); + } + else if (sel == DCLK_VOP_SEL_GPLL) + { + parent = priv->gpll_hz; + } + else if (sel == DCLK_VOP_SEL_CPLL) + { + parent = priv->cpll_hz; + } + else + { + return -RT_ERROR; + } + + return DIV_TO_RATE(parent, div); +} + +#define VOP_PLL_LIMIT_FREQ 600000000 + +static rt_ubase_t dclk_vop_set_clk(struct rk_clk_priv *priv, rt_ubase_t clk_id, rt_ubase_t rate) +{ + struct rk_cru *cru = priv->cru; + rt_ubase_t pll_rate, now, best_rate = 0; + rt_uint32_t i, conid, con, sel, div, best_div = 0, best_sel = 0; + + switch (clk_id) + { + case DCLK_VOP0: + conid = 39; + break; + case DCLK_VOP1: + conid = 40; + break; + case DCLK_VOP2: + conid = 41; + break; + default: + return -RT_ERROR; + } + + con = HWREG32(&cru->clksel_con[conid]); + sel = (con & DCLK0_VOP_SEL_MASK) >> DCLK0_VOP_SEL_SHIFT; + + if (sel == DCLK_VOP_SEL_HPLL) + { + div = 1; + rk_clrsetreg(&cru->clksel_con[conid], DCLK0_VOP_DIV_MASK | DCLK0_VOP_SEL_MASK, + (DCLK_VOP_SEL_HPLL << DCLK0_VOP_SEL_SHIFT) | ((div - 1) << DCLK0_VOP_DIV_SHIFT)); + pmu_pll_set_rate(hpll, div * rate); + } + else if (sel == DCLK_VOP_SEL_VPLL) + { + div = RT_DIV_ROUND_UP(VOP_PLL_LIMIT_FREQ, rate); + rk_clrsetreg(&cru->clksel_con[conid], DCLK0_VOP_DIV_MASK | DCLK0_VOP_SEL_MASK, + (DCLK_VOP_SEL_VPLL << DCLK0_VOP_SEL_SHIFT) | ((div - 1) << DCLK0_VOP_DIV_SHIFT)); + rk_pll_set_rate(&pll_clks[vpll], priv->cru, div * rate); + } + else + { + for (i = 0; i <= DCLK_VOP_SEL_CPLL; i++) + { + switch (i) + { + case DCLK_VOP_SEL_GPLL: + pll_rate = priv->gpll_hz; + break; + case DCLK_VOP_SEL_CPLL: + pll_rate = priv->cpll_hz; + break; + default: + return -RT_ENOSYS; + } + + div = RT_DIV_ROUND_UP(pll_rate, rate); + + if (div > 255) + { + continue; + } + + now = pll_rate / div; + + if (rt_abs(rate - now) < rt_abs(rate - best_rate)) + { + best_rate = now; + best_div = div; + best_sel = i; + } + } + + if (best_rate) { + rk_clrsetreg(&cru->clksel_con[conid], DCLK0_VOP_DIV_MASK | DCLK0_VOP_SEL_MASK, + best_sel << DCLK0_VOP_SEL_SHIFT | (best_div - 1) << DCLK0_VOP_DIV_SHIFT); + } + else + { + return -RT_ENOSYS; + } + } + return dclk_vop_get_clk(priv, clk_id); +} + +static rt_ubase_t gmac_src_get_clk(struct rk_clk_priv *priv, rt_ubase_t mac_id) +{ + struct rk_cru *cru = priv->cru; + rt_uint32_t sel, con; + + con = HWREG32(&cru->clksel_con[31 + mac_id * 2]); + sel = (con & CLK_MAC0_2TOP_SEL_MASK) >> CLK_MAC0_2TOP_SEL_SHIFT; + + switch (sel) + { + case CLK_MAC0_2TOP_SEL_125M: + return 125 * MHZ; + case CLK_MAC0_2TOP_SEL_50M: + return 50 * MHZ; + case CLK_MAC0_2TOP_SEL_25M: + return 25 * MHZ; + case CLK_MAC0_2TOP_SEL_PPLL: + return pmu_pll_get_rate(hpll); + default: + return -RT_ERROR; + } +} + +static rt_ubase_t gmac_src_set_clk(struct rk_clk_priv *priv, rt_ubase_t mac_id, rt_ubase_t rate) +{ + struct rk_cru *cru = priv->cru; + int src_clk; + + switch (rate) + { + case 125 * MHZ: + src_clk = CLK_MAC0_2TOP_SEL_125M; + break; + case 50 * MHZ: + src_clk = CLK_MAC0_2TOP_SEL_50M; + break; + case 25 * MHZ: + src_clk = CLK_MAC0_2TOP_SEL_25M; + break; + default: + return -RT_ERROR; + } + + rk_clrsetreg(&cru->clksel_con[31 + mac_id * 2], CLK_MAC0_2TOP_SEL_MASK, src_clk << CLK_MAC0_2TOP_SEL_SHIFT); + + return gmac_src_get_clk(priv, mac_id); +} + +static rt_ubase_t gmac_out_get_clk(struct rk_clk_priv *priv, rt_ubase_t mac_id) +{ + struct rk_cru *cru = priv->cru; + rt_uint32_t sel, con; + + con = HWREG32(&cru->clksel_con[31 + mac_id * 2]); + sel = (con & CLK_MAC0_OUT_SEL_MASK) >> CLK_MAC0_OUT_SEL_SHIFT; + + switch (sel) + { + case CLK_MAC0_OUT_SEL_125M: + return 125 * MHZ; + case CLK_MAC0_OUT_SEL_50M: + return 50 * MHZ; + case CLK_MAC0_OUT_SEL_25M: + return 25 * MHZ; + case CLK_MAC0_OUT_SEL_24M: + return OSC_HZ; + default: + return -RT_ERROR; + } +} + +static rt_ubase_t gmac_out_set_clk(struct rk_clk_priv *priv, rt_ubase_t mac_id, rt_ubase_t rate) +{ + struct rk_cru *cru = priv->cru; + int src_clk; + + switch (rate) + { + case 125 * MHZ: + src_clk = CLK_MAC0_OUT_SEL_125M; + break; + case 50 * MHZ: + src_clk = CLK_MAC0_OUT_SEL_50M; + break; + case 25 * MHZ: + src_clk = CLK_MAC0_OUT_SEL_25M; + break; + case 24 * MHZ: + src_clk = CLK_MAC0_OUT_SEL_24M; + break; + default: + return -RT_ERROR; + } + + rk_clrsetreg(&cru->clksel_con[31 + mac_id * 2], CLK_MAC0_OUT_SEL_MASK, src_clk << CLK_MAC0_OUT_SEL_SHIFT); + + return gmac_out_get_clk(priv, mac_id); +} + +static rt_ubase_t gmac_ptp_ref_get_clk(struct rk_clk_priv *priv, rt_ubase_t mac_id) +{ + struct rk_cru *cru = priv->cru; + rt_uint32_t sel, con; + + con = HWREG32(&cru->clksel_con[31 + mac_id * 2]); + sel = (con & CLK_GMAC0_PTP_REF_SEL_MASK) >> CLK_GMAC0_PTP_REF_SEL_SHIFT; + + switch (sel) + { + case CLK_GMAC0_PTP_REF_SEL_62_5M: + return 62500 * KHZ; + case CLK_GMAC0_PTP_REF_SEL_100M: + return 100 * MHZ; + case CLK_GMAC0_PTP_REF_SEL_50M: + return 50 * MHZ; + case CLK_GMAC0_PTP_REF_SEL_24M: + return OSC_HZ; + default: + return -RT_ERROR; + } +} + +static rt_ubase_t gmac_ptp_ref_set_clk(struct rk_clk_priv *priv, rt_ubase_t mac_id, rt_ubase_t rate) +{ + struct rk_cru *cru = priv->cru; + int src_clk; + + switch (rate) + { + case 62500 * KHZ: + src_clk = CLK_GMAC0_PTP_REF_SEL_62_5M; + break; + case 100 * MHZ: + src_clk = CLK_GMAC0_PTP_REF_SEL_100M; + break; + case 50 * MHZ: + src_clk = CLK_GMAC0_PTP_REF_SEL_50M; + break; + case 24 * MHZ: + src_clk = CLK_GMAC0_PTP_REF_SEL_24M; + break; + default: + return -RT_ERROR; + } + + rk_clrsetreg(&cru->clksel_con[31 + mac_id * 2], CLK_GMAC0_PTP_REF_SEL_MASK, src_clk << CLK_GMAC0_PTP_REF_SEL_SHIFT); + + return gmac_ptp_ref_get_clk(priv, mac_id); +} + +static rt_ubase_t gmac_tx_rx_set_clk(struct rk_clk_priv *priv, rt_ubase_t mac_id, rt_ubase_t rate) +{ + struct rk_cru *cru = priv->cru; + rt_uint32_t con, sel, div_sel; + + con = HWREG32(&cru->clksel_con[31 + mac_id * 2]); + sel = (con & RMII0_MODE_MASK) >> RMII0_MODE_SHIFT; + + if (sel == RMII0_MODE_SEL_RGMII) + { + if (rate == 2500000) + { + div_sel = RGMII0_CLK_SEL_2_5M; + } + else if (rate == 25000000) + { + div_sel = RGMII0_CLK_SEL_25M; + } + else + { + div_sel = RGMII0_CLK_SEL_125M; + } + rk_clrsetreg(&cru->clksel_con[31 + mac_id * 2], RGMII0_CLK_SEL_MASK, div_sel << RGMII0_CLK_SEL_SHIFT); + } + else if (sel == RMII0_MODE_SEL_RMII) + { + if (rate == 2500000) + { + div_sel = RMII0_CLK_SEL_2_5M; + } + else + { + div_sel = RMII0_CLK_SEL_25M; + } + rk_clrsetreg(&cru->clksel_con[31 + mac_id * 2], RMII0_CLK_SEL_MASK, div_sel << RMII0_CLK_SEL_SHIFT); + } + + return 0; +} + +static rt_ubase_t ebc_get_clk(struct rk_clk_priv *priv) +{ + struct rk_cru *cru = priv->cru; + rt_uint32_t con, div, p_rate; + + con = HWREG32(&cru->clksel_con[79]); + div = (con & CPLL_333M_DIV_MASK) >> CPLL_333M_DIV_SHIFT; + p_rate = DIV_TO_RATE(priv->cpll_hz, div); + + con = HWREG32(&cru->clksel_con[43]); + div = (con & DCLK_EBC_SEL_MASK) >> DCLK_EBC_SEL_SHIFT; + + switch (div) + { + case DCLK_EBC_SEL_GPLL_400M: + return 400 * MHZ; + case DCLK_EBC_SEL_CPLL_333M: + return p_rate; + case DCLK_EBC_SEL_GPLL_200M: + return 200 * MHZ; + default: + return -RT_ERROR; + } +} + +static rt_ubase_t ebc_set_clk(struct rk_clk_priv *priv, rt_ubase_t rate) +{ + struct rk_cru *cru = priv->cru; + int src_clk_div; + + src_clk_div = RT_DIV_ROUND_UP(priv->cpll_hz, rate); + RT_ASSERT(src_clk_div - 1 <= 31); + rk_clrsetreg(&cru->clksel_con[79], CPLL_333M_DIV_MASK, (src_clk_div - 1) << CPLL_333M_DIV_SHIFT); + rk_clrsetreg(&cru->clksel_con[43], DCLK_EBC_SEL_MASK, DCLK_EBC_SEL_CPLL_333M << DCLK_EBC_SEL_SHIFT); + + return ebc_get_clk(priv); +} + +static rt_ubase_t rkvdec_get_clk(struct rk_clk_priv *priv, rt_ubase_t clk_id) +{ + struct rk_cru *cru = priv->cru; + rt_uint32_t con, div, src, p_rate; + + switch (clk_id) + { + case ACLK_RKVDEC_PRE: + case ACLK_RKVDEC: + con = HWREG32(&cru->clksel_con[47]); + src = (con & ACLK_RKVDEC_SEL_MASK) >> ACLK_RKVDEC_SEL_SHIFT; + div = (con & ACLK_RKVDEC_DIV_MASK) >> ACLK_RKVDEC_DIV_SHIFT; + + if (src == ACLK_RKVDEC_SEL_CPLL) + { + p_rate = priv->cpll_hz; + } + else + { + p_rate = priv->gpll_hz; + } + return DIV_TO_RATE(p_rate, div); + case CLK_RKVDEC_CORE: + con = HWREG32(&cru->clksel_con[49]); + src = (con & CLK_RKVDEC_CORE_SEL_MASK) >> CLK_RKVDEC_CORE_SEL_SHIFT; + div = (con & CLK_RKVDEC_CORE_DIV_MASK) >> CLK_RKVDEC_CORE_DIV_SHIFT; + + if (src == CLK_RKVDEC_CORE_SEL_CPLL) + { + p_rate = priv->cpll_hz; + } + else if (src == CLK_RKVDEC_CORE_SEL_NPLL) + { + p_rate = priv->npll_hz; + } + else if (src == CLK_RKVDEC_CORE_SEL_VPLL) + { + p_rate = priv->vpll_hz; + } + else + { + p_rate = priv->gpll_hz; + } + return DIV_TO_RATE(p_rate, div); + default: + return -RT_ERROR; + } +} + +static rt_ubase_t rkvdec_set_clk(struct rk_clk_priv *priv, rt_ubase_t clk_id, rt_ubase_t rate) +{ + struct rk_cru *cru = priv->cru; + int src_clk_div, src, p_rate; + + switch (clk_id) + { + case ACLK_RKVDEC_PRE: + case ACLK_RKVDEC: + src = (HWREG32(&cru->clksel_con[47]) & ACLK_RKVDEC_SEL_MASK) >> ACLK_RKVDEC_SEL_SHIFT; + if (src == ACLK_RKVDEC_SEL_CPLL) + { + p_rate = priv->cpll_hz; + } + else + { + p_rate = priv->gpll_hz; + } + src_clk_div = RT_DIV_ROUND_UP(p_rate, rate); + RT_ASSERT(src_clk_div - 1 <= 31); + rk_clrsetreg(&cru->clksel_con[47], ACLK_RKVDEC_SEL_MASK | ACLK_RKVDEC_DIV_MASK, + (src << ACLK_RKVDEC_SEL_SHIFT) | (src_clk_div - 1) << ACLK_RKVDEC_DIV_SHIFT); + break; + case CLK_RKVDEC_CORE: + src = (HWREG32(&cru->clksel_con[49]) & CLK_RKVDEC_CORE_SEL_MASK) >> CLK_RKVDEC_CORE_SEL_SHIFT; + if (src == CLK_RKVDEC_CORE_SEL_CPLL) + { + p_rate = priv->cpll_hz; + } + else if (src == CLK_RKVDEC_CORE_SEL_NPLL) + { + p_rate = priv->npll_hz; + } + else if (src == CLK_RKVDEC_CORE_SEL_VPLL) + { + p_rate = priv->vpll_hz; + } + else + { + p_rate = priv->gpll_hz; + } + src_clk_div = RT_DIV_ROUND_UP(p_rate, rate); + RT_ASSERT(src_clk_div - 1 <= 31); + rk_clrsetreg(&cru->clksel_con[49], CLK_RKVDEC_CORE_SEL_MASK | CLK_RKVDEC_CORE_DIV_MASK, + (src << CLK_RKVDEC_CORE_SEL_SHIFT) | (src_clk_div - 1) << CLK_RKVDEC_CORE_DIV_SHIFT); + break; + default: + return -RT_ERROR; + } + + return rkvdec_get_clk(priv, clk_id); +} + +static rt_ubase_t uart_get_rate(struct rk_clk_priv *priv, rt_ubase_t clk_id) +{ + struct rk_cru *cru = priv->cru; + rt_uint32_t reg, con, fracdiv, div, src, p_src, p_rate; + rt_ubase_t m, n; + + switch (clk_id) + { + case SCLK_UART1: + reg = 52; + break; + case SCLK_UART2: + reg = 54; + break; + case SCLK_UART3: + reg = 56; + break; + case SCLK_UART4: + reg = 58; + break; + case SCLK_UART5: + reg = 60; + break; + case SCLK_UART6: + reg = 62; + break; + case SCLK_UART7: + reg = 64; + break; + case SCLK_UART8: + reg = 66; + break; + case SCLK_UART9: + reg = 68; + break; + default: + return -RT_ERROR; + } + + con = HWREG32(&cru->clksel_con[reg]); + src = (con & CLK_UART_SEL_MASK) >> CLK_UART_SEL_SHIFT; + div = (con & CLK_UART_SRC_DIV_MASK) >> CLK_UART_SRC_DIV_SHIFT; + p_src = (con & CLK_UART_SRC_SEL_MASK) >> CLK_UART_SRC_SEL_SHIFT; + + if (p_src == CLK_UART_SRC_SEL_GPLL) + { + p_rate = priv->gpll_hz; + } + else if (p_src == CLK_UART_SRC_SEL_CPLL) + { + p_rate = priv->cpll_hz; + } + else + { + p_rate = 480000000; + } + if (src == CLK_UART_SEL_SRC) + { + return DIV_TO_RATE(p_rate, div); + } + else if (src == CLK_UART_SEL_FRAC) + { + fracdiv = HWREG32(&cru->clksel_con[reg + 1]); + n = fracdiv & CLK_UART_FRAC_NUMERATOR_MASK; + n >>= CLK_UART_FRAC_NUMERATOR_SHIFT; + m = fracdiv & CLK_UART_FRAC_DENOMINATOR_MASK; + m >>= CLK_UART_FRAC_DENOMINATOR_SHIFT; + return DIV_TO_RATE(p_rate, div) * n / m; + } + else + { + return OSC_HZ; + } +} + +static rt_ubase_t uart_set_rate(struct rk_clk_priv *priv, rt_ubase_t clk_id, rt_ubase_t rate) +{ + struct rk_cru *cru = priv->cru; + rt_uint32_t reg, clk_src, uart_src, div; + rt_ubase_t m = 0, n = 0, val; + + if (priv->gpll_hz % rate == 0) + { + clk_src = CLK_UART_SRC_SEL_GPLL; + uart_src = CLK_UART_SEL_SRC; + div = RT_DIV_ROUND_UP(priv->gpll_hz, rate); + } + else if (priv->cpll_hz % rate == 0) + { + clk_src = CLK_UART_SRC_SEL_CPLL; + uart_src = CLK_UART_SEL_SRC; + div = RT_DIV_ROUND_UP(priv->gpll_hz, rate); + } + else if (rate == OSC_HZ) + { + clk_src = CLK_UART_SRC_SEL_GPLL; + uart_src = CLK_UART_SEL_XIN24M; + div = 2; + } + else + { + clk_src = CLK_UART_SRC_SEL_GPLL; + uart_src = CLK_UART_SEL_FRAC; + div = 2; + rational_best_approximation(rate, priv->gpll_hz / div, RT_GENMASK(16 - 1, 0), RT_GENMASK(16 - 1, 0), &m, &n); + } + + switch (clk_id) + { + case SCLK_UART1: + reg = 52; + break; + case SCLK_UART2: + reg = 54; + break; + case SCLK_UART3: + reg = 56; + break; + case SCLK_UART4: + reg = 58; + break; + case SCLK_UART5: + reg = 60; + break; + case SCLK_UART6: + reg = 62; + break; + case SCLK_UART7: + reg = 64; + break; + case SCLK_UART8: + reg = 66; + break; + case SCLK_UART9: + reg = 68; + break; + default: + return -RT_ERROR; + } + rk_clrsetreg(&cru->clksel_con[reg], CLK_UART_SEL_MASK | CLK_UART_SRC_SEL_MASK | CLK_UART_SRC_DIV_MASK, + (clk_src << CLK_UART_SRC_SEL_SHIFT) | (uart_src << CLK_UART_SEL_SHIFT) | + ((div - 1) << CLK_UART_SRC_DIV_SHIFT)); + if (m && n) + { + val = m << CLK_UART_FRAC_NUMERATOR_SHIFT | n; + HWREG32(&cru->clksel_con[reg + 1]) = val; + } + + return uart_get_rate(priv, clk_id); +} + +static rt_ubase_t pmu_get_pmuclk(struct rk_pmuclk_priv *priv) +{ + struct rk_pmucru *pmucru = priv->pmucru; + rt_uint32_t div, con, sel, parent; + + con = HWREG32(&pmucru->pmu_clksel_con[2]); + sel = (con & PCLK_PDPMU_SEL_MASK) >> PCLK_PDPMU_SEL_SHIFT; + div = (con & PCLK_PDPMU_DIV_MASK) >> PCLK_PDPMU_DIV_SHIFT; + + if (sel) + { + parent = GPLL_HZ; + } + else + { + parent = priv->ppll_hz; + } + + return DIV_TO_RATE(parent, div); +} + +static rt_ubase_t pmu_set_pmuclk(struct rk_pmuclk_priv *priv, rt_ubase_t rate) +{ + struct rk_pmucru *pmucru = priv->pmucru; + int src_clk_div; + + src_clk_div = RT_DIV_ROUND_UP(priv->ppll_hz, rate); + RT_ASSERT(src_clk_div - 1 <= 31); + + rk_clrsetreg(&pmucru->pmu_clksel_con[2], PCLK_PDPMU_DIV_MASK | PCLK_PDPMU_SEL_MASK, + (PCLK_PDPMU_SEL_PPLL << PCLK_PDPMU_SEL_SHIFT) | ((src_clk_div - 1) << PCLK_PDPMU_DIV_SHIFT)); + + return pmu_get_pmuclk(priv); +} + +static rt_ubase_t pciephy_ref_get_pmuclk(struct rk_pmuclk_priv *priv, rt_ubase_t clk_id) +{ + rt_uint32_t con, div, src; + struct rk_pmucru *pmucru = priv->pmucru; + + switch (clk_id) + { + case CLK_PCIEPHY0_REF: + con = HWREG32(&pmucru->pmu_clksel_con[9]); + src = (con & CLK_PCIE_PHY0_REF_SEL_MASK) >> CLK_PCIE_PHY0_REF_SEL_SHIFT; + con = HWREG32(&pmucru->pmu_clksel_con[9]); + div = (con & CLK_PCIE_PHY0_PLL_DIV_MASK) >> CLK_PCIE_PHY0_PLL_DIV_SHIFT; + break; + case CLK_PCIEPHY1_REF: + con = HWREG32(&pmucru->pmu_clksel_con[9]); + src = (con & CLK_PCIE_PHY1_REF_SEL_MASK) >> CLK_PCIE_PHY1_REF_SEL_SHIFT; + con = HWREG32(&pmucru->pmu_clksel_con[9]); + div = (con & CLK_PCIE_PHY1_PLL_DIV_MASK) >> CLK_PCIE_PHY1_PLL_DIV_SHIFT; + break; + case CLK_PCIEPHY2_REF: + con = HWREG32(&pmucru->pmu_clksel_con[9]); + src = (con & CLK_PCIE_PHY2_REF_SEL_MASK) >> CLK_PCIE_PHY2_REF_SEL_SHIFT; + con = HWREG32(&pmucru->pmu_clksel_con[9]); + div = (con & CLK_PCIE_PHY2_PLL_DIV_MASK) >> CLK_PCIE_PHY2_PLL_DIV_SHIFT; + break; + } + + if (src == CLK_PCIE_PHY_REF_SEL_PPLL) + { + return DIV_TO_RATE(priv->ppll_hz, div); + } + else + { + return OSC_HZ; + } +} + +static rt_ubase_t pciephy_ref_set_pmuclk(struct rk_pmuclk_priv *priv, rt_ubase_t clk_id, rt_ubase_t rate) +{ + rt_uint32_t clk_src, div; + struct rk_pmucru *pmucru = priv->pmucru; + + if (rate == OSC_HZ) + { + clk_src = CLK_PCIE_PHY_REF_SEL_24M; + div = 1; + } + else + { + clk_src = CLK_PCIE_PHY_REF_SEL_PPLL; + div = RT_DIV_ROUND_UP(priv->ppll_hz, rate); + } + + switch (clk_id) { + case CLK_PCIEPHY0_REF: + rk_clrsetreg(&pmucru->pmu_clksel_con[9], CLK_PCIE_PHY0_REF_SEL_MASK, + (clk_src << CLK_PCIE_PHY0_REF_SEL_SHIFT)); + rk_clrsetreg(&pmucru->pmu_clksel_con[9], CLK_PCIE_PHY0_PLL_DIV_MASK, + ((div - 1) << CLK_PCIE_PHY0_PLL_DIV_SHIFT)); + break; + case CLK_PCIEPHY1_REF: + rk_clrsetreg(&pmucru->pmu_clksel_con[9], CLK_PCIE_PHY1_REF_SEL_MASK, + (clk_src << CLK_PCIE_PHY1_REF_SEL_SHIFT)); + rk_clrsetreg(&pmucru->pmu_clksel_con[9], CLK_PCIE_PHY1_PLL_DIV_MASK, + ((div - 1) << CLK_PCIE_PHY1_PLL_DIV_SHIFT)); + break; + case CLK_PCIEPHY2_REF: + rk_clrsetreg(&pmucru->pmu_clksel_con[9], CLK_PCIE_PHY2_REF_SEL_MASK, + (clk_src << CLK_PCIE_PHY2_REF_SEL_SHIFT)); + rk_clrsetreg(&pmucru->pmu_clksel_con[9], CLK_PCIE_PHY2_PLL_DIV_MASK, + ((div - 1) << CLK_PCIE_PHY2_PLL_DIV_SHIFT)); + break; + default: + return -RT_EINVAL; + } + + return pciephy_ref_get_pmuclk(priv, clk_id); +} + +static rt_ubase_t rk_pmuclk_type_get_rate(struct rk_clk_platform_data *pdata, + struct rk_clk *rk_clk) +{ + struct rk_pmuclk_priv *priv = &rk_clk->pmuclk_info; + rt_ubase_t rate = 0; + + if (!priv->ppll_hz) + { + return -RT_ERROR; + } + + switch (pdata->id) + { + case PLL_PPLL: + rate = rk_pll_get_rate(&pmu_pll_clks[ppll], &priv->pmucru); + break; + case PLL_HPLL: + rate = rk_pll_get_rate(&pmu_pll_clks[hpll], &priv->pmucru); + break; + case CLK_RTC_32K: + case CLK_RTC32K_FRAC: + rate = rtc32k_get_pmuclk(priv); + break; + case SCLK_UART0: + rate = uart_get_pmuclk(priv, pdata->id); + break; + case CLK_I2C0: + rate = i2c_get_pmuclk(priv, pdata->id); + break; + case CLK_PWM0: + rate = pwm_get_pmuclk(priv, pdata->id); + break; + case PCLK_PMU: + rate = pmu_get_pmuclk(priv); + break; + case CLK_PCIEPHY0_REF: + case CLK_PCIEPHY1_REF: + case CLK_PCIEPHY2_REF: + rate = pciephy_ref_get_pmuclk(priv, pdata->id); + break; + default: + return -RT_ERROR; + } + + return rate; +} + +static rt_ubase_t rk_pmuclk_set_rate(struct rk_clk_platform_data *pdata, + struct rk_clk *rk_clk, rt_ubase_t rate) +{ + struct rk_pmuclk_priv *priv = &rk_clk->pmuclk_info; + rt_ubase_t res = 0; + + if (!priv->ppll_hz) + { + return -RT_ERROR; + } + + switch (pdata->id) + { + case PLL_PPLL: + res = rk_pll_set_rate(&pmu_pll_clks[ppll], priv->pmucru, rate); + priv->ppll_hz = rk_pll_get_rate(&pmu_pll_clks[ppll], &priv->pmucru); + break; + case PLL_HPLL: + res = rk_pll_set_rate(&pmu_pll_clks[hpll], priv->pmucru, rate); + priv->hpll_hz = rk_pll_get_rate(&pmu_pll_clks[hpll], &priv->pmucru); + break; + case CLK_RTC_32K: + case CLK_RTC32K_FRAC: + res = rtc32k_set_pmuclk(priv, rate); + break; + case SCLK_UART0: + res = uart_set_pmuclk(priv, pdata->id, rate); + break; + case CLK_I2C0: + res = i2c_set_pmuclk(priv, pdata->id, rate); + break; + case CLK_PWM0: + res = pwm_set_pmuclk(priv, pdata->id, rate); + break; + case PCLK_PMU: + res = pmu_set_pmuclk(priv, rate); + break; + case CLK_PCIEPHY0_REF: + case CLK_PCIEPHY1_REF: + case CLK_PCIEPHY2_REF: + res = pciephy_ref_set_pmuclk(priv, pdata->id, rate); + break; + default: + return -RT_ERROR; + } + + return res; +} + +static rt_ubase_t rk_clk_type_get_rate(struct rk_clk_platform_data *pdata, + struct rk_clk *rk_clk) +{ + struct rk_clk_priv *priv = &rk_clk->clk_info; + rt_ubase_t rate = 0; + + if (!priv->gpll_hz) + { + return -RT_ERROR; + } + + switch (pdata->id) + { + case PLL_APLL: + case ARMCLK: + rate = rk_pll_get_rate(&pll_clks[apll], &priv->cru); + break; + case PLL_CPLL: + rate = rk_pll_get_rate(&pll_clks[cpll], &priv->cru); + break; + case PLL_GPLL: + rate = rk_pll_get_rate(&pll_clks[gpll], &priv->cru); + break; + case PLL_NPLL: + rate = rk_pll_get_rate(&pll_clks[npll], &priv->cru); + break; + case PLL_VPLL: + rate = rk_pll_get_rate(&pll_clks[vpll], &priv->cru); + break; + case PLL_DPLL: + rate = rk_pll_get_rate(&pll_clks[dpll], &priv->cru); + break; + case ACLK_BUS: + case PCLK_BUS: + case PCLK_WDT_NS: + rate = bus_get_clk(priv, pdata->id); + break; + case ACLK_PERIMID: + case HCLK_PERIMID: + rate = perimid_get_clk(priv, pdata->id); + break; + case ACLK_TOP_HIGH: + case ACLK_TOP_LOW: + case HCLK_TOP: + case PCLK_TOP: + rate = top_get_clk(priv, pdata->id); + break; + case CLK_I2C1: + case CLK_I2C2: + case CLK_I2C3: + case CLK_I2C4: + case CLK_I2C5: + rate = i2c_get_clk(priv, pdata->id); + break; + case CLK_SPI0: + case CLK_SPI1: + case CLK_SPI2: + case CLK_SPI3: + rate = spi_get_clk(priv, pdata->id); + break; + case CLK_PWM1: + case CLK_PWM2: + case CLK_PWM3: + rate = pwm_get_clk(priv, pdata->id); + break; + case CLK_SARADC: + case CLK_TSADC_TSEN: + case CLK_TSADC: + rate = adc_get_clk(priv, pdata->id); + break; + case HCLK_SDMMC0: + case CLK_SDMMC0: + case CLK_SDMMC1: + case CLK_SDMMC2: + rate = sdmmc_get_clk(priv, pdata->id); + break; + case SCLK_SFC: + rate = sfc_get_clk(priv); + break; + case NCLK_NANDC: + rate = nand_get_clk(priv); + break; + case CCLK_EMMC: + rate = emmc_get_clk(priv); + break; + case BCLK_EMMC: + rate = emmc_get_bclk(priv); + break; + case ACLK_VOP: + rate = aclk_vop_get_clk(priv); + break; + case DCLK_VOP0: + case DCLK_VOP1: + case DCLK_VOP2: + rate = dclk_vop_get_clk(priv, pdata->id); + break; + case SCLK_GMAC0: + case CLK_MAC0_2TOP: + case CLK_MAC0_REFOUT: + rate = gmac_src_get_clk(priv, 0); + break; + case CLK_MAC0_OUT: + rate = gmac_out_get_clk(priv, 0); + break; + case CLK_GMAC0_PTP_REF: + rate = gmac_ptp_ref_get_clk(priv, 0); + break; + case SCLK_GMAC1: + case CLK_MAC1_2TOP: + case CLK_MAC1_REFOUT: + rate = gmac_src_get_clk(priv, 1); + break; + case CLK_MAC1_OUT: + rate = gmac_out_get_clk(priv, 1); + break; + case CLK_GMAC1_PTP_REF: + rate = gmac_ptp_ref_get_clk(priv, 1); + break; + case DCLK_EBC: + rate = ebc_get_clk(priv); + break; + case ACLK_RKVDEC_PRE: + case ACLK_RKVDEC: + case CLK_RKVDEC_CORE: + rate = rkvdec_get_clk(priv, pdata->id); + break; + case TCLK_WDT_NS: + rate = OSC_HZ; + break; + case SCLK_UART1: + case SCLK_UART2: + case SCLK_UART3: + case SCLK_UART4: + case SCLK_UART5: + case SCLK_UART6: + case SCLK_UART7: + case SCLK_UART8: + case SCLK_UART9: + rate = uart_get_rate(priv, pdata->id); + break; + case ACLK_SECURE_FLASH: + case ACLK_CRYPTO_NS: + case HCLK_SECURE_FLASH: + case HCLK_CRYPTO_NS: + case CLK_CRYPTO_NS_RNG: + case CLK_CRYPTO_NS_CORE: + case CLK_CRYPTO_NS_PKA: + rate = crypto_get_rate(priv, pdata->id); + break; + case CPLL_500M: + case CPLL_333M: + case CPLL_250M: + case CPLL_125M: + case CPLL_100M: + case CPLL_62P5M: + case CPLL_50M: + case CPLL_25M: + rate = cpll_div_get_rate(priv, pdata->id); + break; + case CLK_TIMER0: + case CLK_TIMER1: + case CLK_TIMER2: + case CLK_TIMER3: + case CLK_TIMER4: + case CLK_TIMER5: + rate = OSC_HZ; + break; + default: + return -RT_ERROR; + } + + return rate; +}; + +static rt_ubase_t rk_clk_set_rate(struct rk_clk_platform_data *pdata, + struct rk_clk *rk_clk, rt_ubase_t rate) +{ + struct rk_clk_priv *priv = &rk_clk->clk_info; + rt_ubase_t res = 0; + + if (!priv->gpll_hz) + { + return -RT_ERROR; + } + + switch (pdata->id) + { + case PLL_APLL: + case ARMCLK: + if (priv->armclk_hz) + { + armclk_set_clk(priv, rate); + } + priv->armclk_hz = rate; + break; + case PLL_CPLL: + res = rk_pll_set_rate(&pll_clks[cpll], priv->cru, rate); + priv->cpll_hz = rk_pll_get_rate(&pll_clks[cpll], &priv->cru); + break; + case PLL_GPLL: + res = rk_pll_set_rate(&pll_clks[gpll], priv->cru, rate); + priv->gpll_hz = rk_pll_get_rate(&pll_clks[gpll], &priv->cru); + break; + case PLL_NPLL: + res = rk_pll_set_rate(&pll_clks[npll], priv->cru, rate); + break; + case PLL_VPLL: + res = rk_pll_set_rate(&pll_clks[vpll], priv->cru, rate); + priv->vpll_hz = rk_pll_get_rate(&pll_clks[vpll], &priv->cru); + break; + case ACLK_BUS: + case PCLK_BUS: + case PCLK_WDT_NS: + res = bus_set_clk(priv, pdata->id, rate); + break; + case ACLK_PERIMID: + case HCLK_PERIMID: + res = perimid_set_clk(priv, pdata->id, rate); + break; + case ACLK_TOP_HIGH: + case ACLK_TOP_LOW: + case HCLK_TOP: + case PCLK_TOP: + res = top_set_clk(priv, pdata->id, rate); + break; + case CLK_I2C1: + case CLK_I2C2: + case CLK_I2C3: + case CLK_I2C4: + case CLK_I2C5: + res = i2c_set_clk(priv, pdata->id, rate); + break; + case CLK_SPI0: + case CLK_SPI1: + case CLK_SPI2: + case CLK_SPI3: + res = spi_set_clk(priv, pdata->id, rate); + break; + case CLK_PWM1: + case CLK_PWM2: + case CLK_PWM3: + res = pwm_set_clk(priv, pdata->id, rate); + break; + case CLK_SARADC: + case CLK_TSADC_TSEN: + case CLK_TSADC: + res = adc_set_clk(priv, pdata->id, rate); + break; + case HCLK_SDMMC0: + case CLK_SDMMC0: + case CLK_SDMMC1: + case CLK_SDMMC2: + res = sdmmc_set_clk(priv, pdata->id, rate); + break; + case SCLK_SFC: + res = sfc_set_clk(priv, rate); + break; + case NCLK_NANDC: + res = nand_set_clk(priv, rate); + break; + case CCLK_EMMC: + res = emmc_set_clk(priv, rate); + break; + case BCLK_EMMC: + res = emmc_set_bclk(priv, rate); + break; + case ACLK_VOP: + res = aclk_vop_set_clk(priv, rate); + break; + case DCLK_VOP0: + case DCLK_VOP1: + case DCLK_VOP2: + res = dclk_vop_set_clk(priv, pdata->id, rate); + break; + case SCLK_GMAC0: + case CLK_MAC0_2TOP: + case CLK_MAC0_REFOUT: + res = gmac_src_set_clk(priv, 0, rate); + break; + case CLK_MAC0_OUT: + res = gmac_out_set_clk(priv, 0, rate); + break; + case SCLK_GMAC0_RX_TX: + res = gmac_tx_rx_set_clk(priv, 0, rate); + break; + case CLK_GMAC0_PTP_REF: + res = gmac_ptp_ref_set_clk(priv, 0, rate); + break; + case SCLK_GMAC1: + case CLK_MAC1_2TOP: + case CLK_MAC1_REFOUT: + res = gmac_src_set_clk(priv, 1, rate); + break; + case CLK_MAC1_OUT: + res = gmac_out_set_clk(priv, 1, rate); + break; + case SCLK_GMAC1_RX_TX: + res = gmac_tx_rx_set_clk(priv, 1, rate); + break; + case CLK_GMAC1_PTP_REF: + res = gmac_ptp_ref_set_clk(priv, 1, rate); + break; + case DCLK_EBC: + res = ebc_set_clk(priv, rate); + break; + case ACLK_RKVDEC_PRE: + case ACLK_RKVDEC: + case CLK_RKVDEC_CORE: + res = rkvdec_set_clk(priv, pdata->id, rate); + break; + case TCLK_WDT_NS: + res = OSC_HZ; + break; + case SCLK_UART1: + case SCLK_UART2: + case SCLK_UART3: + case SCLK_UART4: + case SCLK_UART5: + case SCLK_UART6: + case SCLK_UART7: + case SCLK_UART8: + case SCLK_UART9: + res = uart_set_rate(priv, pdata->id, rate); + break; + case ACLK_SECURE_FLASH: + case ACLK_CRYPTO_NS: + case HCLK_SECURE_FLASH: + case HCLK_CRYPTO_NS: + case CLK_CRYPTO_NS_RNG: + case CLK_CRYPTO_NS_CORE: + case CLK_CRYPTO_NS_PKA: + res = crypto_set_rate(priv, pdata->id, rate); + break; + case CPLL_500M: + case CPLL_333M: + case CPLL_250M: + case CPLL_125M: + case CPLL_100M: + case CPLL_62P5M: + case CPLL_50M: + case CPLL_25M: + res = cpll_div_set_rate(priv, pdata->id, rate); + break; + default: + return -RT_ERROR; + } + + return res; +}; + +static rt_uint32_t rk_clk_get_rate(struct rk_clk_platform_data *pdata, + struct rk_clk *rk_clk) +{ + rt_uint32_t rate = 0; + + if (rk_clk->type == rk_clk_type_clk) + { + rate = rk_clk_type_get_rate(pdata, rk_clk); + } + else if (rk_clk->type == rk_clk_type_pmuclk) + { + rate = rk_pmuclk_type_get_rate(pdata, rk_clk); + } + + return rate; +} + +static rt_err_t rk_clk_wait_lock(struct rk_clk_platform_data *pdata) +{ + rt_err_t err = RT_EOK; + rt_uint32_t count = 0, pllcon; + + /* + * Lock time typical 250, max 500 input clock cycles @24MHz, So define a + * very safe maximum of 1000us, meaning 24000 cycles. + */ + do { + pllcon = HWREG32(pdata->base + PLL_CON(1)); + rt_hw_us_delay(100); + ++count; + } while (pllcon & PLLCON1_LOCK_STATUS && count < 10); + + if (count >= 10) + { + err = -RT_ETIMEOUT; + } + + return err; +} + +static rt_err_t rtc32k_set_parent(struct rk_clk_platform_data *pdata, + struct rk_clk_platform_data *ppdata, struct rk_clk *rk_clk) +{ + struct rk_pmuclk_priv *priv = &rk_clk->pmuclk_info; + struct rk_pmucru *pmucru = priv->pmucru; + + if (ppdata->id == CLK_RTC32K_FRAC) + { + rk_clrsetreg(&pmucru->pmu_clksel_con[0], RTC32K_SEL_MASK, + RTC32K_SEL_OSC0_DIV32K << RTC32K_SEL_SHIFT); + } + else + { + rk_clrsetreg(&pmucru->pmu_clksel_con[0], RTC32K_SEL_MASK, + RTC32K_SEL_OSC1_32K << RTC32K_SEL_SHIFT); + } + + return RT_EOK; +} + +static rt_err_t rk_pmuclk_set_parent(struct rk_clk_platform_data *pdata, + struct rk_clk_platform_data *ppdata, struct rk_clk *rk_clk) +{ + switch (pdata->id) + { + case CLK_RTC_32K: + return rtc32k_set_parent(pdata, ppdata, rk_clk); + + default: + return -RT_EINVAL; + } + + return RT_EOK; +} + +static rt_err_t gmac0_src_set_parent(struct rk_clk_platform_data *pdata, + struct rk_clk_platform_data *ppdata, struct rk_clk *rk_clk) +{ + struct rk_clk_priv *priv = &rk_clk->clk_info; + struct rk_cru *cru = priv->cru; + + if (ppdata->id == CLK_MAC0_2TOP) + { + rk_clrsetreg(&cru->clksel_con[31], RMII0_EXTCLK_SEL_MASK, + RMII0_EXTCLK_SEL_MAC0_TOP << RMII0_EXTCLK_SEL_SHIFT); + } + else + { + rk_clrsetreg(&cru->clksel_con[31], RMII0_EXTCLK_SEL_MASK, + RMII0_EXTCLK_SEL_IO << RMII0_EXTCLK_SEL_SHIFT); + } + + return RT_EOK; +} + +static rt_err_t gmac1_src_set_parent(struct rk_clk_platform_data *pdata, + struct rk_clk_platform_data *ppdata, struct rk_clk *rk_clk) +{ + struct rk_clk_priv *priv = &rk_clk->clk_info; + struct rk_cru *cru = priv->cru; + + if (ppdata->id == CLK_MAC1_2TOP) + { + rk_clrsetreg(&cru->clksel_con[33], RMII0_EXTCLK_SEL_MASK, + RMII0_EXTCLK_SEL_MAC0_TOP << RMII0_EXTCLK_SEL_SHIFT); + } + else + { + rk_clrsetreg(&cru->clksel_con[33], RMII0_EXTCLK_SEL_MASK, + RMII0_EXTCLK_SEL_IO << RMII0_EXTCLK_SEL_SHIFT); + } + + return RT_EOK; +} + +static rt_err_t gmac0_tx_rx_set_parent(struct rk_clk_platform_data *pdata, + struct rk_clk_platform_data *ppdata, struct rk_clk *rk_clk) +{ + struct rk_clk_priv *priv = &rk_clk->clk_info; + struct rk_cru *cru = priv->cru; + + if (ppdata->id == SCLK_GMAC0_RGMII_SPEED) + { + rk_clrsetreg(&cru->clksel_con[31], RMII0_MODE_MASK, + RMII0_MODE_SEL_RGMII << RMII0_MODE_SHIFT); + } + else if (ppdata->id == SCLK_GMAC0_RMII_SPEED) + { + rk_clrsetreg(&cru->clksel_con[31], RMII0_MODE_MASK, + RMII0_MODE_SEL_RMII << RMII0_MODE_SHIFT); + } + else + { + rk_clrsetreg(&cru->clksel_con[31], RMII0_MODE_MASK, + RMII0_MODE_SEL_GMII << RMII0_MODE_SHIFT); + } + + return RT_EOK; +} + +static rt_err_t gmac1_tx_rx_set_parent(struct rk_clk_platform_data *pdata, + struct rk_clk_platform_data *ppdata, struct rk_clk *rk_clk) +{ + struct rk_clk_priv *priv = &rk_clk->clk_info; + struct rk_cru *cru = priv->cru; + + if (ppdata->id == SCLK_GMAC1_RGMII_SPEED) + { + rk_clrsetreg(&cru->clksel_con[33], RMII0_MODE_MASK, + RMII0_MODE_SEL_RGMII << RMII0_MODE_SHIFT); + } + else if (ppdata->id == SCLK_GMAC1_RMII_SPEED) + { + rk_clrsetreg(&cru->clksel_con[33], RMII0_MODE_MASK, + RMII0_MODE_SEL_RMII << RMII0_MODE_SHIFT); + } + else + { + rk_clrsetreg(&cru->clksel_con[33], RMII0_MODE_MASK, + RMII0_MODE_SEL_GMII << RMII0_MODE_SHIFT); + } + + return RT_EOK; +} + +static rt_err_t dclk_vop_set_parent(struct rk_clk_platform_data *pdata, + struct rk_clk_platform_data *ppdata, struct rk_clk *rk_clk) +{ + struct rk_clk_priv *priv = &rk_clk->clk_info; + struct rk_cru *cru = priv->cru; + rt_uint32_t con_id; + + switch (pdata->id) + { + case DCLK_VOP0: + con_id = 39; + break; + + case DCLK_VOP1: + con_id = 40; + break; + + case DCLK_VOP2: + con_id = 41; + break; + + default: + return -RT_EINVAL; + } + + if (ppdata->id == PLL_VPLL) + { + rk_clrsetreg(&cru->clksel_con[con_id], DCLK0_VOP_SEL_MASK, + DCLK_VOP_SEL_VPLL << DCLK0_VOP_SEL_SHIFT); + } + else + { + rk_clrsetreg(&cru->clksel_con[con_id], DCLK0_VOP_SEL_MASK, + DCLK_VOP_SEL_HPLL << DCLK0_VOP_SEL_SHIFT); + } + + return RT_EOK; +} + +static rt_err_t rkvdec_set_parent(struct rk_clk_platform_data *pdata, + struct rk_clk_platform_data *ppdata, struct rk_clk *rk_clk) +{ + struct rk_clk_priv *priv = &rk_clk->clk_info; + struct rk_cru *cru = priv->cru; + rt_uint32_t con_id, mask, shift; + + switch (pdata->id) + { + case ACLK_RKVDEC_PRE: + con_id = 47; + mask = ACLK_RKVDEC_SEL_MASK; + shift = ACLK_RKVDEC_SEL_SHIFT; + break; + + case CLK_RKVDEC_CORE: + con_id = 49; + mask = CLK_RKVDEC_CORE_SEL_MASK; + shift = CLK_RKVDEC_CORE_SEL_SHIFT; + break; + + default: + return -RT_EINVAL; + } + + if (ppdata->id == PLL_CPLL) + { + rk_clrsetreg(&cru->clksel_con[con_id], mask, + ACLK_RKVDEC_SEL_CPLL << shift); + } + else + { + rk_clrsetreg(&cru->clksel_con[con_id], mask, + ACLK_RKVDEC_SEL_GPLL << shift); + } + + return RT_EOK; +} + +static int rk_clk_set_parent(struct rk_clk_platform_data *pdata, + struct rk_clk_platform_data *ppdata, struct rk_clk *rk_clk) +{ + switch (pdata->id) + { + case SCLK_GMAC0: + return gmac0_src_set_parent(pdata, ppdata, rk_clk); + + case SCLK_GMAC1: + return gmac1_src_set_parent(pdata, ppdata, rk_clk); + + case SCLK_GMAC0_RX_TX: + return gmac0_tx_rx_set_parent(pdata, ppdata, rk_clk); + + case SCLK_GMAC1_RX_TX: + return gmac1_tx_rx_set_parent(pdata, ppdata, rk_clk); + + case DCLK_VOP0: + case DCLK_VOP1: + case DCLK_VOP2: + return dclk_vop_set_parent(pdata, ppdata, rk_clk); + + case ACLK_RKVDEC_PRE: + case CLK_RKVDEC_CORE: + return rkvdec_set_parent(pdata, ppdata, rk_clk); + + default: + return -RT_EINVAL; + } + + return RT_EOK; +} + +static rt_err_t mmc_set_phase(struct rk_clk_platform_data *pdata, + struct rk_clk *rk_clk, rt_uint32_t degrees) +{ + void *reg; + rt_ubase_t rate; + struct rk_clk_priv *priv = &rk_clk->clk_info; + struct rk_cru *cru = priv->cru; + + rate = rk_clk_get_rate(pdata, rk_clk); + + switch (pdata->id) + { + case SCLK_SDMMC0_DRV: + reg = &cru->emmc_con[0]; + break; + + case SCLK_SDMMC0_SAMPLE: + reg = &cru->emmc_con[1]; + break; + + case SCLK_SDMMC1_DRV: + reg = &cru->emmc_con[0]; + break; + + case SCLK_SDMMC1_SAMPLE: + reg = &cru->emmc_con[1]; + break; + + case SCLK_SDMMC2_DRV: + reg = &cru->emmc_con[0]; + break; + + case SCLK_SDMMC2_SAMPLE: + reg = &cru->emmc_con[1]; + break; + + case SCLK_EMMC_DRV: + reg = &cru->emmc_con[0]; + break; + + case SCLK_EMMC_SAMPLE: + reg = &cru->emmc_con[1]; + break; + } + + return rk_clk_mmc_set_phase(rate, reg, 1, degrees); +} + +static rt_base_t mmc_get_phase(struct rk_clk_platform_data *pdata, + struct rk_clk *rk_clk) +{ + void *reg; + rt_ubase_t rate; + struct rk_clk_priv *priv = &rk_clk->clk_info; + struct rk_cru *cru = priv->cru; + + rate = rk_clk_get_rate(pdata, rk_clk); + + switch (pdata->id) + { + case SCLK_SDMMC0_DRV: + reg = &cru->emmc_con[0]; + break; + + case SCLK_SDMMC0_SAMPLE: + reg = &cru->emmc_con[1]; + break; + + case SCLK_SDMMC1_DRV: + reg = &cru->emmc_con[0]; + break; + + case SCLK_SDMMC1_SAMPLE: + reg = &cru->emmc_con[1]; + break; + + case SCLK_SDMMC2_DRV: + reg = &cru->emmc_con[0]; + break; + + case SCLK_SDMMC2_SAMPLE: + reg = &cru->emmc_con[1]; + break; + + case SCLK_EMMC_DRV: + reg = &cru->emmc_con[0]; + break; + + case SCLK_EMMC_SAMPLE: + reg = &cru->emmc_con[1]; + break; + } + + return rk_clk_mmc_get_phase(rate, reg, 1); +} + +static rt_err_t rk3568_clk_init(struct rt_clk *clk, void *fw_data) +{ + struct rk_clk *rk_clk = raw_to_rk_clk(clk->clk_np); + struct rt_ofw_cell_args *args = fw_data; + struct rk_clk_platform_data *pdata; + rt_uint32_t clk_id = args->args[0]; + rt_ubase_t reg_base; + + pdata = &rk_clk->pdata[clk_id]; + + if (rk_clk->type == rk_clk_type_pmuclk) + { + reg_base = (rt_ubase_t)rk_clk->pmuclk_info.pmucru; + + switch (clk_id) + { + case PLL_PPLL: + reg_base += pmu_pll_clks[ppll].con_offset; + break; + case PLL_HPLL: + reg_base += pmu_pll_clks[hpll].con_offset; + break; + default: + reg_base = RT_NULL; + break; + } + } + else if (rk_clk->type == rk_clk_type_clk) + { + reg_base = (rt_ubase_t)rk_clk->clk_info.cru; + + switch (clk_id) + { + case PLL_APLL: + case ARMCLK: + reg_base += pll_clks[apll].con_offset; + break; + case PLL_CPLL: + reg_base += pll_clks[cpll].con_offset; + break; + case PLL_GPLL: + reg_base += pll_clks[gpll].con_offset; + break; + case PLL_NPLL: + reg_base += pll_clks[npll].con_offset; + break; + case PLL_VPLL: + reg_base += pll_clks[vpll].con_offset; + break; + case PLL_DPLL: + reg_base += pll_clks[dpll].con_offset; + break; + default: + reg_base = RT_NULL; + break; + } + } + else + { + LOG_E("Unknow type of rk clk = %d", rk_clk->type); + RT_ASSERT(0); + } + + pdata->id = clk_id; + pdata->base = (void *)reg_base; + + clk->rate = rk_clk_get_rate(pdata, rk_clk); + clk->priv = pdata; + + rk_clk_set_default_rates(clk, clk->clk_np->ops->set_rate, clk_id); + + return RT_EOK; +} + +static rt_err_t rk3568_clk_enable(struct rt_clk *clk) +{ + struct rk_clk_platform_data *pdata = clk->priv; + struct rk_clk *rk_clk = raw_to_rk_clk(clk->clk_np); + struct rk_cru *cru = rk_clk->clk_info.cru; + struct rk_pmucru *pmucru = rk_clk->pmuclk_info.pmucru; + + if (pdata->base) + { + HWREG32(pdata->base + PLL_CON(1)) = HIWORD_UPDATE(0, PLLCON1_PWRDOWN, 0); + + rk_clk_wait_lock(pdata); + } + else + { + void *con_regs; + struct rk_clk_gate *gate; + + if (rk_clk->type == rk_clk_type_clk) + { + gate = &clk_gates[pdata->id]; + con_regs = &cru->clkgate_con[gate->con_idx]; + } + else if (rk_clk->type == rk_clk_type_pmuclk) + { + gate = &pmu_clk_gates[pdata->id]; + con_regs = &pmucru->pmu_clkgate_con[gate->con_idx]; + } + else + { + return -RT_EINVAL; + } + + rk_clrreg(con_regs, RT_BIT(gate->con_bit)); + } + + return RT_EOK; +} + +static void rk3568_clk_disable(struct rt_clk *clk) +{ + struct rk_clk_platform_data *pdata = clk->priv; + struct rk_clk *rk_clk = raw_to_rk_clk(clk->clk_np); + struct rk_cru *cru = rk_clk->clk_info.cru; + struct rk_pmucru *pmucru = rk_clk->pmuclk_info.pmucru; + + if (pdata->base) + { + HWREG32(pdata->base + PLL_CON(1)) = HIWORD_UPDATE(PLLCON1_PWRDOWN, PLLCON1_PWRDOWN, 0); + } + else + { + void *con_regs; + struct rk_clk_gate *gate; + + if (rk_clk->type == rk_clk_type_clk) + { + gate = &clk_gates[pdata->id]; + con_regs = &cru->clkgate_con[gate->con_idx]; + } + else if (rk_clk->type == rk_clk_type_pmuclk) + { + gate = &pmu_clk_gates[pdata->id]; + con_regs = &pmucru->pmu_clkgate_con[gate->con_idx]; + } + else + { + return; + } + + rk_setreg(con_regs, RT_BIT(gate->con_bit)); + } +} + +static rt_bool_t rk3568_clk_is_enabled(struct rt_clk *clk) +{ + struct rk_clk_platform_data *pdata = clk->priv; + + if (pdata->base) + { + rt_uint32_t pllcon = HWREG32(pdata->base + PLL_CON(1)); + + return !(pllcon & PLLCON1_PWRDOWN); + } + + return RT_TRUE; +} + +static rt_err_t rk3568_clk_set_rate(struct rt_clk *clk, rt_ubase_t rate, rt_ubase_t parent_rate) +{ + rt_ubase_t res_rate; + struct rk_clk_platform_data *pdata = clk->priv; + struct rk_clk *rk_clk = raw_to_rk_clk(clk->clk_np); + + if (rk_clk->type == rk_clk_type_clk) + { + res_rate = rk_clk_set_rate(pdata, rk_clk, rate); + + if ((rt_base_t)res_rate > 0) + { + clk->rate = res_rate; + } + } + else if (rk_clk->type == rk_clk_type_pmuclk) + { + res_rate = rk_pmuclk_set_rate(pdata, rk_clk, rate); + + if ((rt_base_t)res_rate > 0) + { + clk->rate = res_rate; + } + } + else + { + return -RT_EINVAL; + } + + return (rt_ubase_t)res_rate > 0 ? RT_EOK : (rt_err_t)res_rate; +} + +static rt_err_t rk3568_clk_set_parent(struct rt_clk *clk, struct rt_clk *parent) +{ + rt_err_t err; + struct rk_clk_platform_data *pdata = clk->priv, *ppdata = parent->priv; + struct rk_clk *rk_clk = raw_to_rk_clk(clk->clk_np); + struct rk_clk *rk_clk_parent = raw_to_rk_clk(clk->clk_np); + + if (rk_clk->type != rk_clk_parent->type) + { + return -RT_EINVAL; + } + + if (rk_clk->type == rk_clk_type_clk) + { + err = rk_clk_set_parent(pdata, ppdata, rk_clk); + } + else if (rk_clk->type == rk_clk_type_pmuclk) + { + err = rk_pmuclk_set_parent(pdata, ppdata, rk_clk); + } + else + { + return -RT_EINVAL; + } + + return err; +} + +static rt_err_t rk3568_clk_set_phase(struct rt_clk *clk, int degrees) +{ + rt_err_t res; + struct rk_clk_platform_data *pdata = clk->priv; + struct rk_clk *rk_clk = raw_to_rk_clk(clk->clk_np); + + switch (pdata->id) + { + case SCLK_SDMMC0_DRV: + case SCLK_SDMMC0_SAMPLE: + case SCLK_SDMMC1_DRV: + case SCLK_SDMMC1_SAMPLE: + case SCLK_SDMMC2_DRV: + case SCLK_SDMMC2_SAMPLE: + case SCLK_EMMC_DRV: + case SCLK_EMMC_SAMPLE: + res = mmc_set_phase(pdata, rk_clk, degrees); + break; + + default: + return -RT_EINVAL; + } + + return res; +} + +static rt_base_t rk3568_clk_get_phase(struct rt_clk *clk) +{ + rt_base_t res; + struct rk_clk_platform_data *pdata = clk->priv; + struct rk_clk *rk_clk = raw_to_rk_clk(clk->clk_np); + + switch (pdata->id) + { + case SCLK_SDMMC0_DRV: + case SCLK_SDMMC0_SAMPLE: + case SCLK_SDMMC1_DRV: + case SCLK_SDMMC1_SAMPLE: + case SCLK_SDMMC2_DRV: + case SCLK_SDMMC2_SAMPLE: + case SCLK_EMMC_DRV: + case SCLK_EMMC_SAMPLE: + res = mmc_get_phase(pdata, rk_clk); + break; + + default: + return -RT_EINVAL; + } + + return res; +} + +static rt_base_t rk3568_clk_round_rate(struct rt_clk *clk, rt_ubase_t drate, + rt_ubase_t *prate) +{ + return rk_clk_pll_round_rate(pll_rates, RT_ARRAY_SIZE(pll_rates), drate, prate); +} + +static const struct rt_clk_ops rk3568_clk_ops = +{ + .init = rk3568_clk_init, + .enable = rk3568_clk_enable, + .disable = rk3568_clk_disable, + .is_enabled = rk3568_clk_is_enabled, + .set_rate = rk3568_clk_set_rate, + .set_parent = rk3568_clk_set_parent, + .set_phase = rk3568_clk_set_phase, + .get_phase = rk3568_clk_get_phase, + .round_rate = rk3568_clk_round_rate, +}; + +static void rk3568_clk_type_init(struct rk_clk *rk_clk, struct rt_ofw_node *np) +{ + rt_ubase_t cpu_freq = APLL_HZ; + struct rk_clk_priv *priv = &rk_clk->clk_info; + const char *rockchip_cpu_freq = rt_ofw_bootargs_select("rockchip.cpu_freq=", 0); + + priv->cru = (struct rk_cru *)rk_clk->base; + + if (!priv->armclk_enter_hz) + { + priv->armclk_enter_hz = rk_pll_get_rate(&pll_clks[apll], &priv->cru); + priv->armclk_init_hz = priv->armclk_enter_hz; + } + + if (rockchip_cpu_freq) + { + cpu_freq = atol(rockchip_cpu_freq); + } + + if (priv->armclk_init_hz != cpu_freq) + { + if (!armclk_set_clk(priv, cpu_freq)) + { + priv->armclk_init_hz = cpu_freq; + } + } + + if (priv->cpll_hz != CPLL_HZ) + { + if (!rk_pll_set_rate(&pll_clks[cpll], priv->cru, CPLL_HZ)) + { + priv->cpll_hz = CPLL_HZ; + } + } + + if (priv->gpll_hz != GPLL_HZ) + { + if (!rk_pll_set_rate(&pll_clks[gpll], priv->cru, GPLL_HZ)) + { + priv->gpll_hz = GPLL_HZ; + } + } + + priv->ppll_hz = pmu_pll_get_rate(ppll); + priv->hpll_hz = pmu_pll_get_rate(hpll); +} + +static void rk3568_pmu_clk_type_init(struct rk_clk *rk_clk, struct rt_ofw_node *np) +{ + struct rk_pmuclk_priv *priv = &rk_clk->pmuclk_info; + priv->pmucru = (struct rk_pmucru *)rk_clk->base; + + if (priv->ppll_hz != PPLL_HZ) + { + if (!rk_pll_set_rate(&pmu_pll_clks[ppll], priv->pmucru, PPLL_HZ)) + { + priv->ppll_hz = PPLL_HZ; + } + } + + /* Ungate PCIe30phy refclk_m and refclk_n */ + rk_clrsetreg(&priv->pmucru->pmu_clkgate_con[2], 0x3 << 13, 0 << 13); +} + +static rt_err_t clk_rk3568_probe(struct rt_platform_device *pdev) +{ + rt_err_t err; + rt_size_t data_size = 0; + struct rk_clk *rk_clk; + struct rt_ofw_node *np = pdev->parent.ofw_node; + enum rk_clk_type type = (rt_ubase_t)pdev->id->data; + + if (type == rk_clk_type_clk) + { + data_size = CLK_NR_CLKS; + } + else if (type == rk_clk_type_pmuclk) + { + data_size = CLKPMU_NR_CLKS; + } + + data_size *= sizeof(struct rk_clk_platform_data); + rk_clk = rt_malloc(sizeof(*rk_clk) + data_size); + + if (rk_clk) + { + void *softrst_regs = RT_NULL; + rt_memset(&rk_clk->parent, 0, sizeof(rk_clk->parent)); + + rk_clk->base = rt_ofw_iomap(np, 0); + + if (!rk_clk->base) + { + err = -RT_EIO; + goto _fail; + } + + if (type == rk_clk_type_clk) + { + rk_clk->type = rk_clk_type_clk; + + rk3568_clk_type_init(rk_clk, np); + softrst_regs = &rk_clk->clk_info.cru->softrst_con; + } + else if (type == rk_clk_type_pmuclk) + { + rk_clk->type = rk_clk_type_pmuclk; + + rk3568_pmu_clk_type_init(rk_clk, np); + softrst_regs = &rk_clk->pmuclk_info.pmucru->pmu_softrst_con; + } + + rk_clk->parent.parent.ops = &rk3568_clk_ops; + + if ((err = rt_clk_register(&rk_clk->parent.parent, RT_NULL))) + { + goto _fail; + } + + if ((err = rk_register_softrst(&rk_clk->parent.rstcer, np, + softrst_regs, ROCKCHIP_SOFTRST_HIWORD_MASK))) + { + goto _fail; + } + + rt_ofw_data(np) = &rk_clk->parent; + } + else + { + err = -RT_ENOMEM; + } + + return err; + +_fail: + if (rk_clk->base) + { + rt_iounmap(rk_clk->base); + } + + rt_free(rk_clk); + + return err; +} + +static const struct rt_ofw_node_id clk_rk3568_ofw_ids[] = +{ + { .compatible = "rockchip,rk3568-cru", .data = (void *)rk_clk_type_clk }, + { .compatible = "rockchip,rk3568-pmucru", .data = (void *)rk_clk_type_pmuclk }, + { /* sentinel */ } +}; + +static struct rt_platform_driver clk_rk3568_driver = +{ + .name = "clk-rk3568", + .ids = clk_rk3568_ofw_ids, + + .probe = clk_rk3568_probe, +}; + +static int clk_rk3568_register(void) +{ + rt_platform_driver_register(&clk_rk3568_driver); + + return 0; +} +INIT_SUBSYS_EXPORT(clk_rk3568_register); diff --git a/components/drivers/clk/rockchip/clk-rk8xx-clkout.c b/components/drivers/clk/rockchip/clk-rk8xx-clkout.c new file mode 100644 index 000000000000..833b8f32cdd2 --- /dev/null +++ b/components/drivers/clk/rockchip/clk-rk8xx-clkout.c @@ -0,0 +1,201 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-11-26 GuEe-GUI first version + */ + +#include +#include +#include + +#define DBG_TAG "clk.rk8xx" +#define DBG_LVL DBG_INFO +#include + +#include "../../mfd/rk8xx.h" + +struct rk8xx_clkout +{ + struct rt_clk_node parent[2]; + + struct rk8xx *rk8xx; +}; + +#define raw_to_rk8xx_clkout(raw, idx) &(rt_container_of(raw, struct rk8xx_clkout, parent[0])[-idx]) + +static rt_err_t rk808_clkout_init(struct rt_clk *clk, void *fw_data) +{ + clk->rate = 32768; + + return RT_EOK; +} + +static const struct rt_clk_ops rk808_clkout1_ops = +{ + .init = rk808_clkout_init, +}; + +static rt_err_t rk808_clkout2_enable(struct rt_clk *clk, rt_bool_t enable) +{ + struct rk8xx_clkout *rk8xx_clkout = raw_to_rk8xx_clkout(clk->clk_np, 1); + + return rk8xx_update_bits(rk8xx_clkout->rk8xx, RK808_CLK32OUT_REG, + CLK32KOUT2_EN, enable ? CLK32KOUT2_EN : 0); +} + +static rt_err_t rk808_clkout2_prepare(struct rt_clk *clk) +{ + return rk808_clkout2_enable(clk, RT_TRUE); +} + +static void rk808_clkout2_unprepare(struct rt_clk *clk) +{ + rk808_clkout2_enable(clk, RT_FALSE); +} + +static rt_bool_t rk808_clkout2_is_prepared(struct rt_clk *clk) +{ + rt_uint32_t val; + struct rk8xx_clkout *rk8xx_clkout = raw_to_rk8xx_clkout(clk->clk_np, 1); + + val = rk8xx_read(rk8xx_clkout->rk8xx, RK808_CLK32OUT_REG); + + if ((rt_err_t)val < 0) + { + return RT_FALSE; + } + + return (val & CLK32KOUT2_EN) ? RT_TRUE : RT_FALSE; +} + +static const struct rt_clk_ops rk808_clkout2_ops = +{ + .init = rk808_clkout_init, + .prepare = rk808_clkout2_prepare, + .unprepare = rk808_clkout2_unprepare, + .is_prepared = rk808_clkout2_is_prepared, +}; + +static rt_err_t rk817_clkout2_enable(struct rt_clk *clk, rt_bool_t enable) +{ + struct rk8xx_clkout *rk8xx_clkout = raw_to_rk8xx_clkout(clk->clk_np, 1); + + return rk8xx_update_bits(rk8xx_clkout->rk8xx, RK817_SYS_CFG(1), + RK817_CLK32KOUT2_EN, enable ? RK817_CLK32KOUT2_EN : 0); +} + +static rt_err_t rk817_clkout2_prepare(struct rt_clk *clk) +{ + return rk817_clkout2_enable(clk, RT_TRUE); +} + +static void rk817_clkout2_unprepare(struct rt_clk *clk) +{ + rk817_clkout2_enable(clk, RT_FALSE); +} + +static rt_bool_t rk817_clkout2_is_prepared(struct rt_clk *clk) +{ + rt_uint32_t val; + struct rk8xx_clkout *rk8xx_clkout = raw_to_rk8xx_clkout(clk->clk_np, 1); + + val = rk8xx_read(rk8xx_clkout->rk8xx, RK817_SYS_CFG(1)); + + if ((rt_err_t)val < 0) + { + return RT_FALSE; + } + + return (val & RK817_CLK32KOUT2_EN) ? RT_TRUE : RT_FALSE; +} + +static const struct rt_clk_ops rk817_clkout2_ops = +{ + .init = rk808_clkout_init, + .prepare = rk817_clkout2_prepare, + .unprepare = rk817_clkout2_unprepare, + .is_prepared = rk817_clkout2_is_prepared, +}; + +static rt_err_t rk8xx_clkout_probe(struct rt_platform_device *pdev) +{ + rt_err_t err; + struct rt_clk_node *clk_np; + struct rk8xx *rk8xx = pdev->priv; + struct rt_ofw_node *np = pdev->parent.ofw_node; + struct rk8xx_clkout *rk8xx_clkout = rt_calloc(1, sizeof(*rk8xx_clkout)); + + if (!rk8xx_clkout) + { + return -RT_ENOMEM; + } + + rk8xx_clkout->rk8xx = rk8xx; + + /* clkout1 */ + clk_np = &rk8xx_clkout->parent[0]; + clk_np->ops = &rk808_clkout1_ops; + clk_np->name = "rk8xx-clkout1"; + rt_ofw_prop_read_string(np, "clock-output-names", &clk_np->name); + + /* clkout2 */ + clk_np = &rk8xx_clkout->parent[1]; + switch (rk8xx->variant) + { + case RK809_ID: + case RK817_ID: + clk_np->ops = &rk817_clkout2_ops; + break; + + /* + * For the default case, it match the following PMIC type. + * RK805_ID + * RK808_ID + * RK818_ID + */ + default: + clk_np->ops = &rk808_clkout2_ops; + break; + } + clk_np->name = "rk8xx-clkout2"; + rt_ofw_prop_read_string(np, "clock-output-names", &clk_np->name); + + if ((err = rt_clk_register(&rk8xx_clkout->parent[0], RT_NULL))) + { + goto _fail; + } + + if ((err = rt_clk_register(&rk8xx_clkout->parent[1], RT_NULL))) + { + rt_clk_unregister(&rk8xx_clkout->parent[0]); + + goto _fail; + } + + rt_ofw_data(np) = &rk8xx_clkout->parent[0]; + + return RT_EOK; + +_fail: + rt_free(rk8xx_clkout); + + return err; +} + +static struct rt_platform_driver rk8xx_clkout_driver = +{ + .name = "rk8xx-clkout", + .probe = rk8xx_clkout_probe, +}; + +static int rk8xx_clkout_register(void) +{ + rt_platform_driver_register(&rk8xx_clkout_driver); + + return 0; +} +INIT_SUBSYS_EXPORT(rk8xx_clkout_register); diff --git a/components/drivers/clk/rockchip/clk.h b/components/drivers/clk/rockchip/clk.h new file mode 100644 index 000000000000..e4eb20db556f --- /dev/null +++ b/components/drivers/clk/rockchip/clk.h @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-3-08 GuEe-GUI the first version + */ + +#ifndef __ROCKCHIP_CLK_H__ +#define __ROCKCHIP_CLK_H__ + +#include +#include +#include + +#include +#include "../../soc/rockchip/rockchip.h" + +#define HZ 100 +#define KHZ 1000 +#define MHZ 1000000 +#define OSC_HZ (24 * MHZ) + +struct rk_cpu_rate_table +{ + rt_ubase_t rate; + rt_uint32_t aclk_div; + rt_uint32_t pclk_div; +}; + +struct rk_pll_rate_table +{ + rt_ubase_t rate; + rt_uint32_t nr; + rt_uint32_t nf; + rt_uint32_t no; + rt_uint32_t nb; + + rt_uint32_t fbdiv; + rt_uint32_t postdiv1; + rt_uint32_t refdiv; + rt_uint32_t postdiv2; + rt_uint32_t dsmpd; + rt_uint32_t frac; +}; + +struct rk_pll_clock +{ + rt_uint32_t id; + rt_uint32_t con_offset; + rt_uint32_t mode_offset; + rt_uint32_t mode_shift; + rt_uint32_t lock_shift; + rt_uint32_t pll_flags; + struct rk_pll_rate_table *rate_table; + rt_uint32_t mode_mask; +}; + +struct rk_clk_gate +{ + const char *name; + const char *parent_name; + + int con_idx; + int con_bit; +}; + +#define GATE(_id, _name, \ +_pname, _con_idx, _con_bit) \ +[_id] = \ +{ \ + .name = _name, \ + .parent_name = _pname, \ + .con_idx = _con_idx, \ + .con_bit = _con_bit, \ +} + + +#define CPUCLK_RATE(_rate, \ + _aclk_div, _pclk_div) \ +{ \ + .rate = _rate##U, \ + .aclk_div = _aclk_div, \ + .pclk_div = _pclk_div, \ +} + +#define PLL_RATE(_rate, _refdiv, _fbdiv, \ + _postdiv1, _postdiv2, _dsmpd, _frac) \ +{ \ + .rate = _rate##U, \ + .fbdiv = _fbdiv, \ + .postdiv1 = _postdiv1, \ + .refdiv = _refdiv, \ + .postdiv2 = _postdiv2, \ + .dsmpd = _dsmpd, \ + .frac = _frac, \ +} + +#define PLL(_id, _con, _mode, _mshift, \ + _lshift, _pflags, _rtable) \ +{ \ + .id = _id, \ + .con_offset = _con, \ + .mode_offset = _mode, \ + .mode_shift = _mshift, \ + .lock_shift = _lshift, \ + .pll_flags = _pflags, \ + .rate_table = _rtable, \ +} + +#define DIV_TO_RATE(input_rate, div) ((input_rate) / ((div) + 1)) + +#define ROCKCHIP_SOFTRST_HIWORD_MASK RT_BIT(0) + +#endif /* __ROCKCHIP_CLK_H__ */ diff --git a/components/drivers/clk/rockchip/softrst.c b/components/drivers/clk/rockchip/softrst.c new file mode 100644 index 000000000000..5becdeb6117b --- /dev/null +++ b/components/drivers/clk/rockchip/softrst.c @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-11-26 GuEe-GUI first version + */ + +struct rockchip_softrst +{ + void *regs; + int num_per_reg; + rt_uint8_t flags; + + struct rt_spinlock lock; +}; + +static rt_err_t rockchip_softrst_assert(struct rt_reset_control *rstc) +{ + int bank, offset; + struct rockchip_softrst *softrst = rstc->rstcer->priv; + + bank = rstc->id / softrst->num_per_reg; + offset = rstc->id % softrst->num_per_reg; + + if (softrst->flags & ROCKCHIP_SOFTRST_HIWORD_MASK) + { + HWREG32(softrst->regs + (bank * 4)) = RT_BIT(offset) | (RT_BIT(offset) << 16); + } + else + { + rt_uint32_t reg; + rt_ubase_t level; + + level = rt_spin_lock_irqsave(&softrst->lock); + + reg = HWREG32(softrst->regs + (bank * 4)); + HWREG32(softrst->regs + (bank * 4)) = reg | RT_BIT(offset); + + rt_spin_unlock_irqrestore(&softrst->lock, level); + } + + return RT_EOK; +} + +static rt_err_t rockchip_softrst_deassert(struct rt_reset_control *rstc) +{ + int bank, offset; + struct rockchip_softrst *softrst = rstc->rstcer->priv; + + bank = rstc->id / softrst->num_per_reg; + offset = rstc->id % softrst->num_per_reg; + + if (softrst->flags & ROCKCHIP_SOFTRST_HIWORD_MASK) + { + HWREG32(softrst->regs + (bank * 4)) = (RT_BIT(offset) << 16); + } + else + { + rt_uint32_t reg; + rt_ubase_t level; + + level = rt_spin_lock_irqsave(&softrst->lock); + + reg = HWREG32(softrst->regs + (bank * 4)); + HWREG32(softrst->regs + (bank * 4)) = reg & ~RT_BIT(offset); + + rt_spin_unlock_irqrestore(&softrst->lock, level); + } + + return RT_EOK; +} + +static const struct rt_reset_control_ops rockchip_softrst_ops = +{ + .assert = rockchip_softrst_assert, + .deassert = rockchip_softrst_deassert, +}; + +static rt_err_t rk_register_softrst(struct rt_reset_controller *rstcer, + struct rt_ofw_node *np, void *regs, rt_uint8_t flags) +{ + rt_err_t err; + struct rockchip_softrst *softrst = rt_calloc(1, sizeof(*softrst)); + + if (!softrst) + { + return -RT_ENOMEM; + } + + rstcer->priv = softrst; + + rt_spin_lock_init(&softrst->lock); + + softrst->regs = regs; + softrst->flags = flags; + softrst->num_per_reg = (flags & ROCKCHIP_SOFTRST_HIWORD_MASK) ? 16 : 32; + + rstcer->ofw_node = np; + rstcer->ops = &rockchip_softrst_ops; + + if ((err = rt_reset_controller_register(rstcer))) + { + rt_free(softrst); + } + + return err; +} diff --git a/components/drivers/core/SConscript b/components/drivers/core/SConscript index d201b7bba4ad..da2def1369c4 100644 --- a/components/drivers/core/SConscript +++ b/components/drivers/core/SConscript @@ -6,7 +6,7 @@ CPPPATH = [cwd + '/../include'] group = [] if GetDepend(['RT_USING_DM']): - src += ['dm.c', 'bus.c', 'platform.c'] + src += ['dm.c', 'bus.c', 'platform.c', 'power.c', 'power_domain.c'] if GetDepend(['RT_USING_OFW']): src += ['platform_ofw.c'] diff --git a/components/drivers/core/bus.c b/components/drivers/core/bus.c index e5293d0f44b0..d800fc93335a 100644 --- a/components/drivers/core/bus.c +++ b/components/drivers/core/bus.c @@ -6,6 +6,7 @@ * Change Logs: * Date Author Notes * 2023-04-12 ErikChan the first version + * 2023-12-12 GuEe-GUI make spinlock less */ #include @@ -15,18 +16,22 @@ #define DBG_LVL DBG_INFO #include -static struct rt_bus bus_root = +#if defined(RT_USING_DFS) && defined(RT_USING_DFS_DIRECTFS) +#include +#include +#endif + +static struct rt_spinlock bus_lock = {}; +static rt_list_t bus_nodes = RT_LIST_OBJECT_INIT(bus_nodes); + +rt_inline void spin_lock(struct rt_spinlock *spinlock) { - .name = "root", - .children = RT_LIST_OBJECT_INIT(bus_root.children), -}; + rt_hw_spin_lock(&spinlock->lock); +} -/** - * @brief This function get the root bus - */ -rt_bus_t rt_bus_root(void) +rt_inline void spin_unlock(struct rt_spinlock *spinlock) { - return &bus_root; + rt_hw_spin_unlock(&spinlock->lock); } /** @@ -34,30 +39,43 @@ rt_bus_t rt_bus_root(void) * * @param bus the target bus * - * @param drv the target drv to be matched + * @param data the data push when call fn * * @param fn the function callback in each loop * * @return the error code, RT_EOK on added successfully. */ -rt_err_t rt_bus_for_each_dev(rt_bus_t bus, rt_driver_t drv, int (*fn)(rt_driver_t drv, rt_device_t dev)) +rt_err_t rt_bus_for_each_dev(rt_bus_t bus, void *data, int (*fn)(rt_device_t dev, void *)) { - rt_base_t level; rt_device_t dev; + rt_err_t err = -RT_EEMPTY; + rt_list_t *dev_list; + struct rt_spinlock *dev_lock; RT_ASSERT(bus != RT_NULL); - RT_ASSERT(drv != RT_NULL); - level = rt_spin_lock_irqsave(&bus->spinlock); + dev_list = &bus->dev_list; + dev_lock = &bus->dev_lock; + + spin_lock(dev_lock); + dev = rt_list_entry(dev_list->next, struct rt_device, node); + spin_unlock(dev_lock); - rt_list_for_each_entry(dev, &bus->dev_list, node) + while (&dev->node != dev_list) { - fn(drv, dev); - } + if (!fn(dev, data)) + { + err = RT_EOK; - rt_spin_unlock_irqrestore(&bus->spinlock, level); + break; + } - return RT_EOK; + spin_lock(dev_lock); + dev = rt_list_entry(dev->node.next, struct rt_device, node); + spin_unlock(dev_lock); + } + + return err; } /** @@ -65,104 +83,97 @@ rt_err_t rt_bus_for_each_dev(rt_bus_t bus, rt_driver_t drv, int (*fn)(rt_driver_ * * @param bus the target bus * - * @param dev the target dev to be matched + * @param data the data push when call fn * * @param fn the function callback in each loop * * @return the error code, RT_EOK on added successfully. */ -rt_err_t rt_bus_for_each_drv(rt_bus_t bus, rt_device_t dev, int (*fn)(rt_driver_t drv, rt_device_t dev)) +rt_err_t rt_bus_for_each_drv(rt_bus_t bus, void *data, int (*fn)(rt_driver_t drv, void *)) { - rt_err_t err; - rt_base_t level; rt_driver_t drv; + rt_err_t err = -RT_EEMPTY; + rt_list_t *drv_list; + struct rt_spinlock *drv_lock; RT_ASSERT(bus != RT_NULL); - RT_ASSERT(dev != RT_NULL); - if (rt_list_isempty(&bus->drv_list)) - { - return RT_EOK; - } - - err = -RT_ERROR; + drv_list = &bus->drv_list; + drv_lock = &bus->drv_lock; - level = rt_spin_lock_irqsave(&bus->spinlock); + spin_lock(drv_lock); + drv = rt_list_entry(drv_list->next, struct rt_driver, node); + spin_unlock(drv_lock); - rt_list_for_each_entry(drv, &bus->drv_list, node) + while (&drv->node != drv_list) { - if (fn(drv, dev)) + if (!fn(drv, data)) { - err = -RT_EOK; + err = RT_EOK; break; } - } - rt_spin_unlock_irqrestore(&bus->spinlock, level); + spin_lock(drv_lock); + drv = rt_list_entry(drv->node.next, struct rt_driver, node); + spin_unlock(drv_lock); + } return err; } -/** - * @brief This function add a bus to the root - * - * @param bus_node the bus to be added - * - * @return the error code, RT_EOK on added successfully. - */ -rt_err_t rt_bus_add(rt_bus_t bus_node) +static rt_err_t bus_probe(rt_driver_t drv, rt_device_t dev) { - rt_base_t level; - - RT_ASSERT(bus_node != RT_NULL); - - bus_node->bus = &bus_root; - rt_list_init(&bus_node->list); - - level = rt_spin_lock_irqsave(&bus_node->spinlock); - - rt_list_insert_before(&bus_root.children, &bus_node->list); - - rt_spin_unlock_irqrestore(&bus_node->spinlock, level); - - return RT_EOK; -} - -/** - * @brief This function match the device and driver, probe them if match successed - * - * @param drv the drv to match/probe - * - * @param dev the dev to match/probe - * - * @return the result of probe, 1 on added successfully. - */ -static int rt_bus_probe(rt_driver_t drv, rt_device_t dev) -{ - int ret = 0; rt_bus_t bus = drv->bus; + rt_err_t err = -RT_EEMPTY; if (!bus) { bus = dev->bus; } - RT_ASSERT(bus != RT_NULL); - if (!dev->drv && bus->match(drv, dev)) { dev->drv = drv; - ret = bus->probe(dev); + err = bus->probe(dev); - if (ret) + if (err) { dev->drv = RT_NULL; } } - return ret; + return err; +} + +static int bus_probe_driver(rt_device_t dev, void *drv_ptr) +{ + bus_probe(drv_ptr, dev); + + /* + * The driver is shared by multiple devices, + * so we always return the '1' to enumerate all devices. + */ + return 1; +} + +static int bus_probe_device(rt_driver_t drv, void *dev_ptr) +{ + rt_err_t err; + + err = bus_probe(drv, dev_ptr); + + if (!err) + { + rt_bus_t bus = drv->bus; + + spin_lock(&bus->drv_lock); + ++drv->ref_count; + spin_unlock(&bus->drv_lock); + } + + return err; } /** @@ -176,20 +187,21 @@ static int rt_bus_probe(rt_driver_t drv, rt_device_t dev) */ rt_err_t rt_bus_add_driver(rt_bus_t bus, rt_driver_t drv) { - rt_base_t level; - RT_ASSERT(bus != RT_NULL); RT_ASSERT(drv != RT_NULL); drv->bus = bus; + rt_list_init(&drv->node); - level = rt_spin_lock_irqsave(&bus->spinlock); - + spin_lock(&bus->drv_lock); rt_list_insert_before(&bus->drv_list, &drv->node); + spin_unlock(&bus->drv_lock); - rt_spin_unlock_irqrestore(&bus->spinlock, level); + rt_bus_for_each_dev(bus, drv, bus_probe_driver); - rt_bus_for_each_dev(drv->bus, drv, rt_bus_probe); +#ifdef RT_USING_DFS_DIRECTFS + dfs_directfs_create_link(&bus->drv_dir, &drv->parent, drv->parent.name); +#endif return RT_EOK; } @@ -205,20 +217,21 @@ rt_err_t rt_bus_add_driver(rt_bus_t bus, rt_driver_t drv) */ rt_err_t rt_bus_add_device(rt_bus_t bus, rt_device_t dev) { - rt_base_t level; - RT_ASSERT(bus != RT_NULL); RT_ASSERT(dev != RT_NULL); dev->bus = bus; + rt_list_init(&dev->node); - level = rt_spin_lock_irqsave(&bus->spinlock); - + spin_lock(&bus->dev_lock); rt_list_insert_before(&bus->dev_list, &dev->node); - - rt_spin_unlock_irqrestore(&bus->spinlock, level); + spin_unlock(&bus->dev_lock); - rt_bus_for_each_drv(dev->bus, dev, rt_bus_probe); + rt_bus_for_each_drv(bus, dev, bus_probe_device); + +#ifdef RT_USING_DFS_DIRECTFS + dfs_directfs_create_link(&bus->dev_dir, &dev->parent, dev->parent.name); +#endif return RT_EOK; } @@ -232,13 +245,31 @@ rt_err_t rt_bus_add_device(rt_bus_t bus, rt_device_t dev) */ rt_err_t rt_bus_remove_driver(rt_driver_t drv) { + rt_err_t err; + rt_bus_t bus; + + RT_ASSERT(drv != RT_NULL); RT_ASSERT(drv->bus != RT_NULL); - LOG_D("Bus(%s) remove driver %s", drv->bus->name, drv->name); + bus = drv->bus; - rt_list_remove(&drv->node); + LOG_D("Bus(%s) remove driver %s", bus->name, drv->parent.name); - return RT_EOK; + spin_lock(&bus->drv_lock); + + if (drv->ref_count) + { + err = -RT_EBUSY; + } + else + { + rt_list_remove(&drv->node); + err = RT_EOK; + } + + spin_unlock(&bus->drv_lock); + + return err; } /** @@ -250,13 +281,104 @@ rt_err_t rt_bus_remove_driver(rt_driver_t drv) */ rt_err_t rt_bus_remove_device(rt_device_t dev) { + rt_bus_t bus; + rt_driver_t drv; + rt_err_t err = RT_EOK; + + RT_ASSERT(dev != RT_NULL); RT_ASSERT(dev->bus != RT_NULL); - LOG_D("Bus(%s) remove device %s", dev->bus->name, dev->name); + bus = dev->bus; + drv = dev->drv; + LOG_D("Bus(%s) remove device %s", bus->name, dev->parent.name); + + spin_lock(&bus->dev_lock); rt_list_remove(&dev->node); + spin_unlock(&bus->dev_lock); - return RT_EOK; + if (dev->bus->remove) + { + err = dev->bus->remove(dev); + } + else if (drv) + { + if (drv->shutdown) + { + err = drv->shutdown(dev); + } + + /* device and driver are in the same bus */ + spin_lock(&bus->drv_lock); + --drv->ref_count; + spin_unlock(&bus->drv_lock); + } + + return err; +} + +struct bus_shutdown_info +{ + rt_bus_t bus; + + rt_err_t err; +}; + +static int device_shutdown(rt_device_t dev, void *info_ptr) +{ + rt_bus_t bus; + rt_err_t err = RT_EOK; + struct bus_shutdown_info *info = info_ptr; + + bus = info->bus; + + if (bus->shutdown) + { + LOG_D("Device(%s) shutdown", dev->parent.name); + err = bus->shutdown(dev); + LOG_D(" Result: %s", rt_strerror(err)); + } + else if (dev->drv && dev->drv->shutdown) + { + LOG_D("Device(%s) shutdown", dev->parent.name); + err = dev->drv->shutdown(dev); + LOG_D(" Result: %s", rt_strerror(err)); + } + + if (err) + { + /* Only get the last one while system not crash */ + info->err = err; + } + + /* Go on, we want to ask all devices to shutdown */ + return 1; +} + +/** + * @brief This function call all buses' shutdown + * + * @return the error code, RT_EOK on shutdown successfully. + */ +rt_err_t rt_bus_shutdown(void) +{ + rt_bus_t bus = RT_NULL; + struct bus_shutdown_info info = + { + .err = RT_EOK, + }; + + spin_lock(&bus_lock); + + rt_list_for_each_entry(bus, &bus_nodes, list) + { + info.bus = bus; + rt_bus_for_each_dev(bus, &info, device_shutdown); + } + + spin_unlock(&bus_lock); + + return info.err; } /** @@ -265,24 +387,24 @@ rt_err_t rt_bus_remove_device(rt_device_t dev) * * @return the bus finded by name. */ -rt_bus_t rt_bus_find_by_name(char *name) +rt_bus_t rt_bus_find_by_name(const char *name) { rt_bus_t bus = RT_NULL; - struct rt_list_node *node = RT_NULL; - if (!rt_list_isempty(&bus_root.children)) + RT_ASSERT(name != RT_NULL); + + spin_lock(&bus_lock); + + rt_list_for_each_entry(bus, &bus_nodes, list) { - rt_list_for_each(node, &bus_root.children) + if (!rt_strncmp(bus->name, name, RT_NAME_MAX)) { - bus = rt_list_entry(node, struct rt_bus, list); - - if (!rt_strncmp(bus->name, name, RT_NAME_MAX)) - { - return bus; - } + break; } } + spin_unlock(&bus_lock); + return bus; } @@ -297,22 +419,20 @@ rt_bus_t rt_bus_find_by_name(char *name) */ rt_err_t rt_bus_reload_driver_device(rt_bus_t new_bus, rt_device_t dev) { - rt_base_t level; + rt_bus_t old_bus; RT_ASSERT(new_bus != RT_NULL); RT_ASSERT(dev != RT_NULL); + RT_ASSERT(dev->bus != RT_NULL); + RT_ASSERT(dev->bus != new_bus); - level = rt_spin_lock_irqsave(&new_bus->spinlock); + old_bus = dev->bus; + spin_lock(&old_bus->dev_lock); rt_list_remove(&dev->node); - rt_list_insert_before(&new_bus->dev_list, &dev->node); - - rt_list_remove(&dev->drv->node); - rt_list_insert_before(&new_bus->drv_list, &dev->drv->node); + spin_unlock(&old_bus->dev_lock); - rt_spin_unlock_irqrestore(&new_bus->spinlock, level); - - return RT_EOK; + return rt_bus_add_device(new_bus, dev); } /** @@ -323,11 +443,37 @@ rt_err_t rt_bus_reload_driver_device(rt_bus_t new_bus, rt_device_t dev) */ rt_err_t rt_bus_register(rt_bus_t bus) { - rt_list_init(&bus->children); + RT_ASSERT(bus != RT_NULL); + + rt_list_init(&bus->list); rt_list_init(&bus->dev_list); rt_list_init(&bus->drv_list); - rt_bus_add(bus); + rt_spin_lock_init(&bus->dev_lock); + rt_spin_lock_init(&bus->drv_lock); + + spin_lock(&bus_lock); + + rt_list_insert_before(&bus_nodes, &bus->list); + +#ifdef RT_USING_DFS_DIRECTFS + do { + static rt_object_t bus_obj = RT_NULL; + + if (!bus_obj) + { + bus_obj = dfs_directfs_find_object(RT_NULL, "bus"); + + RT_ASSERT(bus_obj != RT_NULL); + } + + dfs_directfs_create_link(bus_obj, &bus->parent, bus->name); + dfs_directfs_create_link(&bus->parent, &bus->dev_dir, "devices"); + dfs_directfs_create_link(&bus->parent, &bus->drv_dir, "drivers"); + } while (0); +#endif + + spin_unlock(&bus_lock); return RT_EOK; } diff --git a/components/drivers/core/dm.c b/components/drivers/core/dm.c index 12ec3e18f844..b3e112c3eba3 100644 --- a/components/drivers/core/dm.c +++ b/components/drivers/core/dm.c @@ -10,6 +10,10 @@ #include +#ifdef RT_USING_OFW +#include +#include +#endif #include #ifdef RT_USING_SMP @@ -56,10 +60,10 @@ struct prefix_track int uid; const char *prefix; }; -static struct rt_spinlock _prefix_nodes_lock; +static struct rt_spinlock _prefix_nodes_lock = { 0 }; static rt_list_t _prefix_nodes = RT_LIST_OBJECT_INIT(_prefix_nodes); -int rt_dm_set_dev_name_auto(rt_device_t dev, const char *prefix) +int rt_dm_dev_set_name_auto(rt_device_t dev, const char *prefix) { int uid = -1; struct prefix_track *pt = RT_NULL; @@ -104,17 +108,17 @@ int rt_dm_set_dev_name_auto(rt_device_t dev, const char *prefix) rt_spin_unlock(&_prefix_nodes_lock); } - return rt_dm_set_dev_name(dev, "%s%u", prefix, uid); + return rt_dm_dev_set_name(dev, "%s%u", prefix, uid); } -int rt_dm_get_dev_name_id(rt_device_t dev) +int rt_dm_dev_get_name_id(rt_device_t dev) { int id = 0, len; const char *name; RT_ASSERT(dev != RT_NULL); - name = rt_dm_get_dev_name(dev); + name = rt_dm_dev_get_name(dev); len = rt_strlen(name) - 1; name += len; @@ -137,7 +141,7 @@ int rt_dm_get_dev_name_id(rt_device_t dev) return id; } -int rt_dm_set_dev_name(rt_device_t dev, const char *format, ...) +int rt_dm_dev_set_name(rt_device_t dev, const char *format, ...) { int n; va_list arg_ptr; @@ -152,9 +156,275 @@ int rt_dm_set_dev_name(rt_device_t dev, const char *format, ...) return n; } -const char *rt_dm_get_dev_name(rt_device_t dev) +const char *rt_dm_dev_get_name(rt_device_t dev) { RT_ASSERT(dev != RT_NULL); return dev->parent.name; } + +#ifdef RT_USING_OFW +#define ofw_api_call(name, ...) rt_ofw_##name(__VA_ARGS__) +#define ofw_api_call_ptr(name, ...) ofw_api_call(name, __VA_ARGS__) +#else +#define ofw_api_call(name, ...) (-RT_ENOSYS) +#define ofw_api_call_ptr(name, ...) RT_NULL +#endif + +int rt_dm_dev_get_address_count(rt_device_t dev) +{ + RT_ASSERT(dev != RT_NULL); + + if (dev->ofw_node) + { + return ofw_api_call(get_address_count, dev->ofw_node); + } + + return -RT_ENOSYS; +} + +rt_err_t rt_dm_dev_get_address(rt_device_t dev, int index, + rt_uint64_t *out_address, rt_uint64_t *out_size) +{ + RT_ASSERT(dev != RT_NULL); + + if (dev->ofw_node) + { + return ofw_api_call(get_address, dev->ofw_node, index, + out_address, out_size); + } + + return -RT_ENOSYS; +} + +rt_err_t rt_dm_dev_get_address_by_name(rt_device_t dev, const char *name, + rt_uint64_t *out_address, rt_uint64_t *out_size) +{ + RT_ASSERT(dev != RT_NULL); + + if (dev->ofw_node) + { + return ofw_api_call(get_address_by_name, dev->ofw_node, name, + out_address, out_size); + } + + return -RT_ENOSYS; +} + +int rt_dm_dev_get_address_array(rt_device_t dev, int nr, rt_uint64_t *out_regs) +{ + RT_ASSERT(dev != RT_NULL); + + if (dev->ofw_node) + { + return ofw_api_call(get_address_array, dev->ofw_node, nr, out_regs); + } + + return -RT_ENOSYS; +} + +void *rt_dm_dev_iomap(rt_device_t dev, int index) +{ + RT_ASSERT(dev != RT_NULL); + + if (dev->ofw_node) + { + return ofw_api_call_ptr(iomap, dev->ofw_node, index); + } + + return RT_NULL; +} + +void *rt_dm_dev_iomap_by_name(rt_device_t dev, const char *name) +{ + RT_ASSERT(dev != RT_NULL); + + if (dev->ofw_node) + { + return ofw_api_call_ptr(iomap_by_name, dev->ofw_node, name); + } + + return RT_NULL; +} + +int rt_dm_dev_get_irq_count(rt_device_t dev) +{ + RT_ASSERT(dev != RT_NULL); + + if (dev->ofw_node) + { + return ofw_api_call(get_irq_count, dev->ofw_node); + } + + return -RT_ENOSYS; +} + +int rt_dm_dev_get_irq(rt_device_t dev, int index) +{ + RT_ASSERT(dev != RT_NULL); + + if (dev->ofw_node) + { + return ofw_api_call(get_irq, dev->ofw_node, index); + } + + return -RT_ENOSYS; +} + +int rt_dm_dev_get_irq_by_name(rt_device_t dev, const char *name) +{ + RT_ASSERT(dev != RT_NULL); + + if (dev->ofw_node) + { + return ofw_api_call(get_irq_by_name, dev->ofw_node, name); + } + + return -RT_ENOSYS; +} + +void rt_dm_dev_bind_fwdata(rt_device_t dev, void *fw_np, void *data) +{ + RT_ASSERT(dev != RT_NULL); + + if (!dev->ofw_node && fw_np) + { + #ifdef RT_USING_OFW + dev->ofw_node = fw_np; + rt_ofw_data(fw_np) = data; + #endif + } + + RT_ASSERT(dev->ofw_node != RT_NULL); + +#ifdef RT_USING_OFW + rt_ofw_data(dev->ofw_node) = data; +#endif +} + +void rt_dm_dev_unbind_fwdata(rt_device_t dev, void *fw_np) +{ + void *dev_fw_np; + + RT_ASSERT(dev != RT_NULL); + + if (!dev->ofw_node && fw_np) + { + #ifdef RT_USING_OFW + dev_fw_np = fw_np; + rt_ofw_data(fw_np) = RT_NULL; + #endif + } + + RT_ASSERT(dev_fw_np != RT_NULL); + +#ifdef RT_USING_OFW + rt_ofw_data(dev_fw_np) = RT_NULL; +#endif +} + +int rt_dm_dev_prop_read_u8_array_index(rt_device_t dev, const char *propname, + int index, int nr, rt_uint8_t *out_values) +{ + RT_ASSERT(dev != RT_NULL); + + if (dev->ofw_node) + { + return ofw_api_call(prop_read_u8_array_index, dev->ofw_node, propname, + index, nr, out_values); + } + + return -RT_ENOSYS; +} + +int rt_dm_dev_prop_read_u16_array_index(rt_device_t dev, const char *propname, + int index, int nr, rt_uint16_t *out_values) +{ + RT_ASSERT(dev != RT_NULL); + + if (dev->ofw_node) + { + return ofw_api_call(prop_read_u16_array_index, dev->ofw_node, propname, + index, nr, out_values); + } + + return -RT_ENOSYS; +} + +int rt_dm_dev_prop_read_u32_array_index(rt_device_t dev, const char *propname, + int index, int nr, rt_uint32_t *out_values) +{ + RT_ASSERT(dev != RT_NULL); + + if (dev->ofw_node) + { + return ofw_api_call(prop_read_u32_array_index, dev->ofw_node, propname, + index, nr, out_values); + } + + return -RT_ENOSYS; +} + +int rt_dm_dev_prop_read_u64_array_index(rt_device_t dev, const char *propname, + int index, int nr, rt_uint64_t *out_values) +{ + RT_ASSERT(dev != RT_NULL); + + if (dev->ofw_node) + { + return ofw_api_call(prop_read_u64_array_index, dev->ofw_node, propname, + index, nr, out_values); + } + + return -RT_ENOSYS; +} + +int rt_dm_dev_prop_read_string_array_index(rt_device_t dev, const char *propname, + int index, int nr, const char **out_strings) +{ + RT_ASSERT(dev != RT_NULL); + + if (dev->ofw_node) + { + return ofw_api_call(prop_read_string_array_index, dev->ofw_node, propname, + index, nr, out_strings); + } + + return -RT_ENOSYS; +} + +int rt_dm_dev_prop_count_of_size(rt_device_t dev, const char *propname, int size) +{ + RT_ASSERT(dev != RT_NULL); + + if (dev->ofw_node) + { + return ofw_api_call(prop_count_of_size, dev->ofw_node, propname, size); + } + + return -RT_ENOSYS; +} + +int rt_dm_dev_prop_index_of_string(rt_device_t dev, const char *propname, const char *string) +{ + RT_ASSERT(dev != RT_NULL); + + if (dev->ofw_node) + { + return ofw_api_call(prop_index_of_string, dev->ofw_node, propname, string); + } + + return -RT_ENOSYS; +} + +rt_bool_t rt_dm_dev_prop_read_bool(rt_device_t dev, const char *propname) +{ + RT_ASSERT(dev != RT_NULL); + + if (dev->ofw_node) + { + return ofw_api_call(prop_read_bool, dev->ofw_node, propname); + } + + return RT_FALSE; +} diff --git a/components/drivers/core/platform.c b/components/drivers/core/platform.c index 62e092a3fa9c..0b995732d4d4 100644 --- a/components/drivers/core/platform.c +++ b/components/drivers/core/platform.c @@ -10,9 +10,14 @@ #include +#define DBG_TAG "rtdm.platform" +#define DBG_LVL DBG_INFO +#include + #include #include #include +#include static struct rt_bus platform_bus; @@ -27,6 +32,11 @@ struct rt_platform_device *rt_platform_device_alloc(const char *name) { struct rt_platform_device *pdev = rt_calloc(1, sizeof(*pdev)); + if (!pdev) + { + return RT_NULL; + } + pdev->parent.bus = &platform_bus; pdev->name = name; @@ -43,6 +53,11 @@ rt_err_t rt_platform_driver_register(struct rt_platform_driver *pdrv) RT_ASSERT(pdrv != RT_NULL); pdrv->parent.bus = &platform_bus; +#if RT_NAME_MAX > 0 + rt_strcpy(pdrv->parent.parent.name, pdrv->name); +#else + pdrv->parent.parent.name = pdrv->name; +#endif return rt_driver_register(&pdrv->parent); } @@ -65,16 +80,24 @@ static rt_bool_t platform_match(rt_driver_t drv, rt_device_t dev) struct rt_platform_driver *pdrv = rt_container_of(drv, struct rt_platform_driver, parent); struct rt_platform_device *pdev = rt_container_of(dev, struct rt_platform_device, parent); + /* 1、match with ofw node */ if (np) { - /* 1、match with ofw node */ + #ifdef RT_USING_OFW pdev->id = rt_ofw_node_match(np, pdrv->ids); + #else + pdev->id = RT_NULL; + #endif - return !!pdev->id; + if (pdev->id) + { + return RT_TRUE; + } } - else if (pdev->name && pdrv->name) + + /* 2、match with name */ + if (pdev->name && pdrv->name) { - /* 2、match with name */ if (pdev->name == pdrv->name) { return RT_TRUE; @@ -95,31 +118,76 @@ static rt_err_t platform_probe(rt_device_t dev) struct rt_platform_driver *pdrv = rt_container_of(dev->drv, struct rt_platform_driver, parent); struct rt_platform_device *pdev = rt_container_of(dev, struct rt_platform_device, parent); + err = rt_dm_power_domain_attach(dev, RT_TRUE); + + if (err && err != -RT_EEMPTY) + { + LOG_E("Attach power domain error = %s in device %s", pdev->name, rt_strerror(err)); + + return err; + } + err = pdrv->probe(pdev); if (!err) { + #ifdef RT_USING_OFW if (np) { rt_ofw_node_set_flag(np, RT_OFW_F_READLY); } + #endif } else { - if (np) + if (err == -RT_ENOMEM) { - rt_ofw_data(np) = &pdev->parent; + LOG_W("System not memory in driver %s", pdrv->name); } + + rt_dm_power_domain_detach(dev, RT_TRUE); } return err; } +static rt_err_t platform_remove(rt_device_t dev) +{ + struct rt_platform_driver *pdrv = rt_container_of(dev->drv, struct rt_platform_driver, parent); + struct rt_platform_device *pdev = rt_container_of(dev, struct rt_platform_device, parent); + + if (pdrv && pdrv->remove) + { + pdrv->remove(pdev); + } + + rt_dm_power_domain_detach(dev, RT_TRUE); + + return RT_EOK; +} + +static rt_err_t platform_shutdown(rt_device_t dev) +{ + struct rt_platform_driver *pdrv = rt_container_of(dev->drv, struct rt_platform_driver, parent); + struct rt_platform_device *pdev = rt_container_of(dev, struct rt_platform_device, parent); + + if (pdrv && pdrv->shutdown) + { + pdrv->shutdown(pdev); + } + + rt_dm_power_domain_detach(dev, RT_TRUE); + + return RT_EOK; +} + static struct rt_bus platform_bus = { .name = "platform", .match = platform_match, .probe = platform_probe, + .remove = platform_remove, + .shutdown = platform_shutdown, }; static int platform_bus_init(void) diff --git a/components/drivers/core/platform_ofw.c b/components/drivers/core/platform_ofw.c index 5327a9af6b59..428360aa1148 100644 --- a/components/drivers/core/platform_ofw.c +++ b/components/drivers/core/platform_ofw.c @@ -14,9 +14,13 @@ #define DBG_LVL DBG_INFO #include +#include +#include #include #include +#include "../ofw/ofw_internal.h" + static const struct rt_ofw_node_id platform_ofw_ids[] = { { .compatible = "simple-bus", }, @@ -36,10 +40,74 @@ static const struct rt_ofw_node_id platform_ofw_ids[] = { /* sentinel */ } }; +static void ofw_device_rename(struct rt_device *dev) +{ + rt_uint32_t mask; + rt_uint64_t addr; + const char *dev_name = dev->parent.name; + struct rt_ofw_node *np = dev->ofw_node; + +#if RT_NAME_MAX > 0 + if (dev_name[0] == '\0') + { + dev_name = RT_NULL; + } +#endif + + while (np->parent) + { + if (!rt_ofw_get_address(np, 0, &addr, RT_NULL)) + { + const char *node_name = rt_fdt_node_name(np->full_name); + rt_size_t tag_len = rt_strchrnul(node_name, '@') - node_name; + + if (!rt_ofw_prop_read_u32(np, "mask", &mask)) + { + rt_dm_dev_set_name(dev, dev_name ? "%lx.%x.%.*s:%s" : "%lx.%x.%.*s", + addr, __rt_ffs(mask) - 1, tag_len, node_name, dev_name); + } + else + { + rt_dm_dev_set_name(dev, dev_name ? "%lx.%.*s:%s" : "%lx.%.*s", + addr, tag_len, node_name, dev_name); + } + + return; + } + + rt_dm_dev_set_name(dev, dev_name ? "%s:%s" : "%s", + rt_fdt_node_name(np->full_name), dev_name); + + np = np->parent; + } +} + +static struct rt_platform_device *alloc_ofw_platform_device(struct rt_ofw_node *np) +{ + struct rt_platform_device *pdev = rt_platform_device_alloc(""); + + if (pdev) + { + /* inc reference of dt-node */ + rt_ofw_node_get(np); + rt_ofw_node_set_flag(np, RT_OFW_F_PLATFORM); + + pdev->parent.ofw_node = np; + + ofw_device_rename(&pdev->parent); + } + else + { + LOG_E("Alloc device fail for %s", rt_ofw_node_full_name(np)); + } + + return pdev; +} + static rt_err_t platform_ofw_device_probe_once(struct rt_ofw_node *parent_np) { rt_err_t err = RT_EOK; - struct rt_ofw_node *np, *child; + struct rt_ofw_node *np; struct rt_platform_device *pdev; rt_ofw_foreach_available_child_node(parent_np, np) @@ -48,6 +116,8 @@ static rt_err_t platform_ofw_device_probe_once(struct rt_ofw_node *parent_np) struct rt_ofw_node_id *id; struct rt_ofw_prop *compat_prop = RT_NULL; + LOG_D("%s found in %s", np->full_name, parent_np->full_name); + /* Is system node or have driver */ if (rt_ofw_node_test_flag(np, RT_OFW_F_SYSTEM) || rt_ofw_node_test_flag(np, RT_OFW_F_READLY)) @@ -66,35 +136,32 @@ static rt_err_t platform_ofw_device_probe_once(struct rt_ofw_node *parent_np) id = rt_ofw_prop_match(compat_prop, platform_ofw_ids); - if (id && (child = rt_ofw_get_next_child(np, RT_NULL))) + if (id && np->child) { /* scan next level */ - err = platform_ofw_device_probe_once(child); - - rt_ofw_node_put(child); + err = platform_ofw_device_probe_once(np); if (err) { + rt_ofw_node_put(np); LOG_E("%s bus probe fail", np->full_name); break; } } - pdev = rt_platform_device_alloc(np->name); + pdev = alloc_ofw_platform_device(np); if (!pdev) { + rt_ofw_node_put(np); err = -RT_ENOMEM; break; } - /* inc reference of dt-node */ - rt_ofw_node_get(np); - rt_ofw_node_set_flag(np, RT_OFW_F_PLATFORM); - - pdev->parent.ofw_node = np; + pdev->dev_id = ofw_alias_node_id(np); + LOG_D("%s register to bus", np->full_name); rt_platform_device_register(pdev); } @@ -102,18 +169,58 @@ static rt_err_t platform_ofw_device_probe_once(struct rt_ofw_node *parent_np) return err; } +rt_err_t rt_platform_ofw_device_probe_child(struct rt_ofw_node *np) +{ + rt_err_t err; + struct rt_ofw_node *parent = rt_ofw_get_parent(np); + + if (parent && rt_strcmp(parent->name, "/") && + rt_ofw_get_prop(np, "compatible", RT_NULL) && + !rt_ofw_node_test_flag(np, RT_OFW_F_PLATFORM)) + { + struct rt_platform_device *pdev = alloc_ofw_platform_device(np); + + if (pdev) + { + err = rt_platform_device_register(pdev); + } + else + { + err = -RT_ENOMEM; + } + } + else + { + err = -RT_EINVAL; + } + + rt_ofw_node_put(parent); + + return err; +} + static int platform_ofw_device_probe(void) { rt_err_t err = RT_EOK; - struct rt_ofw_node *root_np; - - root_np = rt_ofw_find_node_by_path("/"); + struct rt_ofw_node *node; - if (root_np) + if (ofw_node_root) { - err = platform_ofw_device_probe_once(root_np); + err = platform_ofw_device_probe_once(ofw_node_root); + + rt_ofw_node_put(ofw_node_root); + + if ((node = rt_ofw_find_node_by_path("/firmware"))) + { + platform_ofw_device_probe_once(node); + rt_ofw_node_put(node); + } - rt_ofw_node_put(root_np); + if ((node = rt_ofw_get_child_by_compatible(ofw_node_chosen, "simple-framebuffer"))) + { + platform_ofw_device_probe_once(node); + rt_ofw_node_put(node); + } } else { diff --git a/components/drivers/core/power.c b/components/drivers/core/power.c new file mode 100644 index 000000000000..2f407111e1e2 --- /dev/null +++ b/components/drivers/core/power.c @@ -0,0 +1,235 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-09-24 GuEe-GUI the first version + */ + +#include + +#define DBG_TAG "rtdm.power" +#define DBG_LVL DBG_INFO +#include + +struct power_off_track +{ + rt_slist_t list; + + struct rt_device *dev; + rt_err_t (*callback)(struct rt_device *); +}; + +void (*rt_dm_machine_shutdown)(void) = RT_NULL; +void (*rt_dm_machine_reset)(void) = RT_NULL; + +static struct rt_spinlock _power_off_lock = {}; +static rt_slist_t _power_off_handler_nodes[RT_DM_POWER_OFF_MODE_NR][RT_DM_POWER_OFF_PRIO_NR] = +{ + [0 ... RT_DM_POWER_OFF_MODE_NR - 1] = + { + [0 ... RT_DM_POWER_OFF_PRIO_NR - 1] = + { + RT_NULL, + } + } +}; + +static rt_used char * const _mode_name[] = +{ + [RT_DM_POWER_OFF_MODE_SHUTDOWN] = "SHUTDOWN", + [RT_DM_POWER_OFF_MODE_RESET] = "RESET", +}; + +rt_err_t rt_dm_power_off_handler(struct rt_device *dev, int mode, int priority, + rt_err_t (*callback)(struct rt_device *dev)) +{ + struct power_off_track *track; + + RT_ASSERT(mode < RT_DM_POWER_OFF_MODE_NR); + RT_ASSERT(priority < RT_DM_POWER_OFF_PRIO_NR); + + track = rt_malloc(sizeof(*track)); + + if (!track) + { + return -RT_ENOMEM; + } + + rt_slist_init(&track->list); + track->dev = dev; + track->callback = callback; + + rt_hw_spin_lock(&_power_off_lock.lock); + + rt_slist_insert(&_power_off_handler_nodes[mode][priority], &track->list); + + rt_hw_spin_unlock(&_power_off_lock.lock); + + return RT_EOK; +} + +static void dm_power_off_handler(int mode) +{ + struct power_off_track *track; + + rt_hw_spin_lock(&_power_off_lock.lock); + + for (int i = 0; i < RT_DM_POWER_OFF_PRIO_NR; ++i) + { + rt_slist_t *nodes = &_power_off_handler_nodes[mode][i]; + + rt_slist_for_each_entry(track, nodes, list) + { + rt_err_t err; + struct rt_device *dev = track->dev; + + if ((err = track->callback(dev))) + { + LOG_E("%s: %s fail error = %s", dev ? rt_dm_dev_get_name(dev) : RT_NULL, + _mode_name[mode], rt_strerror(err)); + } + } + } + + rt_hw_spin_unlock(&_power_off_lock.lock); +} + +struct reboot_mode_track +{ + rt_slist_t list; + + struct rt_device *dev; + rt_err_t (*callback)(struct rt_device *, char *cmd); +}; + +static char *_reboot_mode_cmd = "normal"; +static struct rt_spinlock _reboot_mode_lock = {}; +static rt_slist_t _reboot_mode_handler_nodes = { RT_NULL }; + +rt_err_t rt_dm_reboot_mode_register(struct rt_device *dev, + rt_err_t (*callback)(struct rt_device *, char *cmd)) +{ + struct reboot_mode_track *track; + + track = rt_malloc(sizeof(*track)); + + if (!track) + { + return -RT_ENOMEM; + } + + rt_slist_init(&track->list); + track->dev = dev; + track->callback = callback; + + rt_hw_spin_lock(&_reboot_mode_lock.lock); + + rt_slist_insert(&_reboot_mode_handler_nodes, &track->list); + + rt_hw_spin_unlock(&_reboot_mode_lock.lock); + + return RT_EOK; +} + +static rt_err_t dm_reboot_notifiy(struct rt_device *) +{ + struct reboot_mode_track *track; + + rt_hw_spin_lock(&_reboot_mode_lock.lock); + + rt_slist_for_each_entry(track, &_reboot_mode_handler_nodes, list) + { + rt_err_t err; + struct rt_device *dev = track->dev; + + if ((err = track->callback(dev, _reboot_mode_cmd))) + { + LOG_E("%s: %s fail error = %s", dev ? rt_dm_dev_get_name(dev) : RT_NULL, + "reboot mode apply", rt_strerror(err)); + } + } + + rt_hw_spin_unlock(&_reboot_mode_lock.lock); + + return RT_EOK; +} + +static int reboot_mode_init(void) +{ + return rt_dm_power_off_handler(RT_NULL, RT_DM_POWER_OFF_MODE_RESET, + RT_DM_POWER_OFF_PRIO_HIGH, &dm_reboot_notifiy); +} +INIT_CORE_EXPORT(reboot_mode_init); + +void rt_hw_cpu_reset_mode(char *cmd) +{ + static struct rt_spinlock pe_lock = {}; + + rt_hw_spin_lock(&pe_lock.lock); + + _reboot_mode_cmd = cmd ? : _reboot_mode_cmd; + + rt_hw_cpu_reset(); + + /* Unreachable */ + rt_hw_spin_unlock(&pe_lock.lock); +} + +void rt_hw_cpu_shutdown(void) +{ + register rt_ubase_t level; + + dm_power_off_handler(RT_DM_POWER_OFF_MODE_SHUTDOWN); + + rt_bus_shutdown(); + +#ifdef RT_USING_PIC + rt_pic_irq_finit(); +#endif + + LOG_I("Shutdown"); + + /* Machine shutdown */ + if (rt_dm_machine_shutdown) + { + rt_dm_machine_shutdown(); + } + + level = rt_hw_interrupt_disable(); + while (level) + { + RT_ASSERT(0); + } +} +MSH_CMD_EXPORT_ALIAS(rt_hw_cpu_shutdown, shutdown, shutdown machine); + +void rt_hw_cpu_reset(void) +{ + register rt_ubase_t level; + + dm_power_off_handler(RT_DM_POWER_OFF_MODE_RESET); + + rt_bus_shutdown(); + +#ifdef RT_USING_PIC + rt_pic_irq_finit(); +#endif + + LOG_I("Reset"); + + /* Machine reset */ + if (rt_dm_machine_reset) + { + rt_dm_machine_reset(); + } + + level = rt_hw_interrupt_disable(); + while (level) + { + RT_ASSERT(0); + } +} +MSH_CMD_EXPORT_ALIAS(rt_hw_cpu_reset, reset, reset machine); diff --git a/components/drivers/core/power_domain.c b/components/drivers/core/power_domain.c new file mode 100644 index 000000000000..6570b893f066 --- /dev/null +++ b/components/drivers/core/power_domain.c @@ -0,0 +1,462 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-09-24 GuEe-GUI the first version + */ + +#include + +#define DBG_TAG "rtdm.power_domain" +#define DBG_LVL DBG_INFO +#include + +#include + +void rt_dm_power_domain_proxy_default_name(struct rt_dm_power_domain_proxy *proxy) +{ +#if RT_NAME_MAX > 0 + rt_strncpy(proxy->parent.name, "PMP", RT_NAME_MAX); +#else + proxy->parent.name = "PMP"; +#endif +} + +void rt_dm_power_domain_proxy_ofw_bind(struct rt_dm_power_domain_proxy *proxy, + struct rt_ofw_node *np) +{ + if (!proxy || !proxy->ofw_parse || !np) + { + return; + } + + rt_dm_power_domain_proxy_default_name(proxy); + rt_ofw_data(np) = proxy; +} + +static void dm_power_domain_init(struct rt_dm_power_domain *domain) +{ +#if RT_NAME_MAX > 0 + rt_strncpy(domain->parent.name, "PMD", RT_NAME_MAX); +#else + domain->parent.name = "PMD"; +#endif + + domain->parent_domain = RT_NULL; + + rt_list_init(&domain->list); + rt_list_init(&domain->child_nodes); + rt_list_init(&domain->unit_nodes); + + ref_init(&domain->ref); + rt_spin_lock_init(&domain->lock); +} + +static rt_bool_t dm_power_domain_is_free(struct rt_dm_power_domain *domain) +{ + return ref_read(&domain->ref) == 1 && !rt_list_isempty(&domain->child_nodes); +} + +rt_err_t rt_dm_power_domain_register(struct rt_dm_power_domain *domain) +{ + if (!domain) + { + return -RT_EINVAL; + } + + dm_power_domain_init(domain); + + return RT_EOK; +} + +rt_err_t rt_dm_power_domain_unregister(struct rt_dm_power_domain *domain) +{ + rt_err_t err = RT_EOK; + + if (!domain) + { + return -RT_EINVAL; + } + + if (!dm_power_domain_is_free(domain)) + { + return -RT_EBUSY; + } + + if (domain->parent_domain) + { + err = rt_dm_power_domain_unregister_child(domain->parent_domain, domain); + } + + return err; +} + +rt_err_t rt_dm_power_domain_register_child(struct rt_dm_power_domain *domain, + struct rt_dm_power_domain *child_domain) +{ + if (!domain || !child_domain) + { + return -RT_EINVAL; + } + + dm_power_domain_init(child_domain); + child_domain->parent_domain = domain; + + return RT_EOK; +} + +rt_err_t rt_dm_power_domain_unregister_child(struct rt_dm_power_domain *domain, + struct rt_dm_power_domain *child_domain) +{ + rt_err_t err = RT_EOK; + + if (!domain || !child_domain) + { + return -RT_EINVAL; + } + + rt_hw_spin_lock(&domain->lock.lock); + + if (dm_power_domain_is_free(domain)) + { + rt_list_remove(&child_domain->list); + } + else + { + err = -RT_EBUSY; + } + + rt_hw_spin_unlock(&domain->lock.lock); + + return err; +} + +rt_err_t rt_dm_power_domain_power_on(struct rt_dm_power_domain *domain) +{ + rt_err_t err = RT_EOK; + struct rt_dm_power_domain *child_domain; + + if (!domain) + { + return -RT_EINVAL; + } + + rt_hw_spin_lock(&domain->lock.lock); + + if (ref_read(&domain->ref) == 1) + { + err = domain->power_on(domain); + } + + if (!err) + { + struct rt_dm_power_domain *fail_domain = RT_NULL; + + rt_list_for_each_entry(child_domain, &domain->child_nodes, list) + { + err = rt_dm_power_domain_power_on(child_domain); + + if (err) + { + fail_domain = child_domain; + break; + } + } + + if (fail_domain) + { + rt_list_for_each_entry(child_domain, &domain->child_nodes, list) + { + if (child_domain == fail_domain) + { + break; + } + + rt_dm_power_domain_power_off(child_domain); + } + } + } + + rt_hw_spin_unlock(&domain->lock.lock); + + if (!err) + { + ref_get(&domain->ref); + } + + return err; +} + +static void dm_power_domain_release(struct ref *r) +{ + struct rt_dm_power_domain *domain = rt_container_of(r, struct rt_dm_power_domain, ref); + + if (domain->dev) + { + LOG_E("%s power domain is release", rt_dm_dev_get_name(domain->dev)); + } + + RT_ASSERT(0); +} + +rt_err_t rt_dm_power_domain_power_off(struct rt_dm_power_domain *domain) +{ + rt_err_t err; + struct rt_dm_power_domain *child_domain; + + if (!domain) + { + return -RT_EINVAL; + } + + ref_put(&domain->ref, dm_power_domain_release); + + rt_hw_spin_lock(&domain->lock.lock); + + if (ref_read(&domain->ref) == 1) + { + err = domain->power_off(domain); + } + + if (!err) + { + struct rt_dm_power_domain *fail_domain = RT_NULL; + + rt_list_for_each_entry(child_domain, &domain->child_nodes, list) + { + err = rt_dm_power_domain_power_off(child_domain); + + if (err) + { + fail_domain = child_domain; + break; + } + } + + if (fail_domain) + { + rt_list_for_each_entry(child_domain, &domain->child_nodes, list) + { + if (child_domain == fail_domain) + { + break; + } + + rt_dm_power_domain_power_on(child_domain); + } + } + } + + rt_hw_spin_unlock(&domain->lock.lock); + + if (err) + { + ref_get(&domain->ref); + } + + return err; +} + +#ifdef RT_USING_OFW +static struct rt_dm_power_domain *ofw_find_power_domain(struct rt_device *dev, + int index, struct rt_ofw_cell_args *args) +{ + struct rt_object *obj; + struct rt_dm_power_domain_proxy *proxy; + struct rt_dm_power_domain *domain = RT_NULL; + struct rt_ofw_node *np = dev->ofw_node, *power_domain_np; + + if (!rt_ofw_parse_phandle_cells(np, "power-domains", "#power-domain-cells", + index, args)) + { + power_domain_np = args->data; + + if (power_domain_np && (obj = rt_ofw_data(power_domain_np))) + { + if (!rt_strcmp(obj->name, "PMP")) + { + proxy = rt_container_of(obj, struct rt_dm_power_domain_proxy, parent); + domain = proxy->ofw_parse(proxy, args); + } + else if (!rt_strcmp(obj->name, "PMD")) + { + domain = rt_container_of(obj, struct rt_dm_power_domain, parent); + } + + rt_ofw_node_put(power_domain_np); + } + } + + return domain; +} +#else +rt_inline struct rt_dm_power_domain *ofw_find_power_domain(struct rt_device *dev, + int index, struct rt_ofw_cell_args *args) +{ + return RT_NULL; +} +#endif /* RT_USING_OFW */ + +struct rt_dm_power_domain *rt_dm_power_domain_get_by_index(struct rt_device *dev, + int index) +{ + struct rt_ofw_cell_args args; + struct rt_dm_power_domain *domain; + + if (!dev || index < 0) + { + return RT_NULL; + } + + if ((domain = ofw_find_power_domain(dev, index, &args))) + { + goto _end; + } + +_end: + return domain; +} + +struct rt_dm_power_domain *rt_dm_power_domain_get_by_name(struct rt_device *dev, + const char *name) +{ + int index; + + if (!dev || !name) + { + return RT_NULL; + } + + if ((index = rt_dm_dev_prop_index_of_string(dev, "power-domain-names", name)) < 0) + { + LOG_E("%s find power domain %s not found", rt_dm_dev_get_name(dev)); + + return RT_NULL; + } + + return rt_dm_power_domain_get_by_index(dev, index); +} + +rt_err_t rt_dm_power_domain_put(struct rt_dm_power_domain *domain) +{ + if (!domain) + { + return -RT_EINVAL; + } + + return RT_EOK; +} + +rt_err_t rt_dm_power_domain_attach(struct rt_device *dev, rt_bool_t on) +{ + int id = -1; + rt_err_t err = RT_EOK; + struct rt_ofw_cell_args args; + struct rt_dm_power_domain *domain; + struct rt_dm_power_domain_unit *unit; + + if (!dev) + { + return -RT_EINVAL; + } + + /* We only attach the first one, get domains self if there are multiple domains */ + if ((domain = ofw_find_power_domain(dev, 0, &args))) + { + id = args.args[0]; + } + + if (!domain) + { + return -RT_EEMPTY; + } + + unit = rt_malloc(sizeof(*unit)); + + if (!unit) + { + return -RT_ENOMEM; + } + + rt_list_init(&unit->list); + unit->id = id; + unit->domain = domain; + + dev->power_domain_unit = unit; + + rt_hw_spin_lock(&domain->lock.lock); + + if (domain->attach_dev) + { + err = domain->attach_dev(domain, dev); + } + + if (!err) + { + rt_list_insert_before(&domain->unit_nodes, &unit->list); + } + + rt_hw_spin_unlock(&domain->lock.lock); + + if (err) + { + dev->power_domain_unit = RT_NULL; + rt_free(unit); + + return err; + } + + if (on) + { + err = rt_dm_power_domain_power_on(domain); + } + + return err; +} + +rt_err_t rt_dm_power_domain_detach(struct rt_device *dev, rt_bool_t off) +{ + rt_err_t err = RT_EOK; + struct rt_dm_power_domain *domain; + struct rt_dm_power_domain_unit *unit; + + if (!dev || !dev->power_domain_unit) + { + return -RT_EINVAL; + } + + unit = dev->power_domain_unit; + domain = unit->domain; + + rt_hw_spin_lock(&domain->lock.lock); + + if (domain->detach_dev) + { + err = domain->detach_dev(domain, dev); + } + + if (!err) + { + rt_list_remove(&unit->list); + } + + rt_hw_spin_unlock(&domain->lock.lock); + + if (err) + { + return err; + } + + rt_free(unit); + dev->power_domain_unit = RT_NULL; + + if (off) + { + err = rt_dm_power_domain_power_off(domain); + } + + return err; +} diff --git a/components/drivers/firmware/Kconfig b/components/drivers/firmware/Kconfig index 270926f538ba..1889b077c3b6 100644 --- a/components/drivers/firmware/Kconfig +++ b/components/drivers/firmware/Kconfig @@ -6,5 +6,22 @@ menuconfig RT_USING_FIRMWARE config RT_FIRMWARE_PSCI bool "Power State Coordination Interface (PSCI)" depends on RT_USING_FIRMWARE - select RT_USING_PM + select RT_USING_OFW default n + +config RT_FIRMWARE_QEMU_FW_CFG + bool "QEMU Firmware Configuration" + depends on RT_USING_FIRMWARE + select RT_USING_DFS + select RT_USING_DFS_DIRECTFS + default n + +config RT_FIRMWARE_RASPBERRYPI + bool "Raspberrypi Firmware" + depends on RT_USING_FIRMWARE + select RT_USING_OFW + select RT_USING_MBOX + select RT_USING_DMA + default n + +source "$RTT_DIR/components/drivers/firmware/arm_scmi/Kconfig" diff --git a/components/drivers/firmware/arm_scmi/Kconfig b/components/drivers/firmware/arm_scmi/Kconfig new file mode 100644 index 000000000000..0988d91f9c91 --- /dev/null +++ b/components/drivers/firmware/arm_scmi/Kconfig @@ -0,0 +1,11 @@ +menuconfig RT_FIRMWARE_ARM_SCMI + bool "ARM System Control and Management Interface Protocol (SCMI)" + depends on RT_USING_FIRMWARE + depends on ARCH_ARM_CORTEX_A || ARCH_ARMV8 + select RT_USING_OFW + default n + +config RT_FIRMWARE_ARM_SCMI_TRANSPORT_SMC + bool "SCMI transport based on SMC" + depends on RT_FIRMWARE_ARM_SCMI + default y diff --git a/components/drivers/firmware/arm_scmi/SConscript b/components/drivers/firmware/arm_scmi/SConscript new file mode 100644 index 000000000000..247504b24b77 --- /dev/null +++ b/components/drivers/firmware/arm_scmi/SConscript @@ -0,0 +1,18 @@ +from building import * + +group = [] + +if not GetDepend(['RT_FIRMWARE_ARM_SCMI']): + Return('group') + +cwd = GetCurrentDir() +CPPPATH = [cwd + '/../../include'] + +src = ['agent.c', 'bus.c', 'shmem.c'] + +if GetDepend(['RT_FIRMWARE_ARM_SCMI_TRANSPORT_SMC']): + src += ['agent-smc.c'] + +group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') diff --git a/components/drivers/firmware/arm_scmi/agent-smc.c b/components/drivers/firmware/arm_scmi/agent-smc.c new file mode 100644 index 000000000000..cb3e2c3010e9 --- /dev/null +++ b/components/drivers/firmware/arm_scmi/agent-smc.c @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-11-26 GuEe-GUI first version + */ + +#include + +#include +#include +#include +#include + +#define DBG_TAG "scmi.agent.smc" +#define DBG_LVL DBG_INFO +#include + +#include "agent.h" + +struct scmi_agent_smc +{ + int irq; + rt_uint32_t func_id; + +#define SHMEM_SIZE (4 * SIZE_KB) +#define SHMEM_SHIFT 12 +#define SHMEM_PAGE(x) (((rt_ubase_t)(x) >> SHMEM_SHIFT)) +#define SHMEM_OFFSET(x) ((x) & (SHMEM_SIZE - 1)) + rt_uint32_t param_page; + rt_uint32_t param_offset; + + rt_bool_t done; + + struct rt_spinlock lock; + struct scmi_shared_mem *shmem; +}; + +static void scmi_agent_smc_isr(int irqno, void *param) +{ + struct scmi_agent_smc *asmc = param; + + HWREG32(&asmc->done) = RT_TRUE; + rt_hw_dmb(); +} + +static rt_err_t scmi_agent_smc_setup(struct scmi_agent *agent, + struct rt_platform_device *pdev) +{ + rt_err_t err; + struct rt_ofw_node *np = pdev->parent.ofw_node, *shmem_np; + struct scmi_agent_smc *asmc = rt_calloc(1, sizeof(*asmc)); + + if (!asmc) + { + return -RT_ENOMEM; + } + + if ((err = rt_ofw_prop_read_u32(np, "arm,smc-id", &asmc->func_id))) + { + goto _fail; + } + + shmem_np = rt_ofw_parse_phandle(np, "shmem", 0); + + if (rt_ofw_node_is_compatible(shmem_np, "arm,scmi-shmem")) + { + err = -RT_EINVAL; + rt_ofw_node_put(shmem_np); + + goto _fail; + } + + asmc->shmem = rt_ofw_iomap(shmem_np, 0); + rt_ofw_node_put(shmem_np); + + if (!asmc->shmem) + { + err = -RT_EIO; + goto _fail; + } + + if (rt_ofw_node_is_compatible(np, "arm,scmi-smc-param")) + { + rt_ubase_t base = (rt_ubase_t)rt_kmem_v2p(asmc->shmem); + + asmc->param_page = SHMEM_PAGE(base); + asmc->param_offset = SHMEM_OFFSET(base); + } + + asmc->irq = rt_ofw_get_irq_by_name(np, "a2p"); + + if (asmc->irq >= 0) + { + rt_hw_interrupt_install(asmc->irq, scmi_agent_smc_isr, asmc, "scmi"); + rt_hw_interrupt_umask(asmc->irq); + } + + rt_spin_lock_init(&asmc->lock); + + agent->priv = asmc; + + return RT_EOK; + +_fail: + if (asmc->shmem) + { + rt_iounmap(asmc->shmem); + } + rt_free(asmc); + + return err; +} + +static rt_err_t scmi_agent_smc_process_msg(struct scmi_agent *agent, + struct rt_scmi_msg *msg) +{ + rt_err_t err; + rt_ubase_t page, offset; + struct arm_smccc_res_t res; + struct scmi_shared_mem *shmem; + struct scmi_agent_smc *asmc = agent->priv; + + rt_spin_lock(&asmc->lock); + + if (asmc->irq >= 0) + { + while (HWREG32(&asmc->done)) + { + rt_hw_cpu_relax(); + } + } + + shmem = asmc->shmem; + + if (rt_unlikely(err = scmi_shmem_msg_write(shmem, msg))) + { + goto _out_lock; + } + + if (asmc->irq >= 0) + { + HWREG32(&asmc->done) = RT_FALSE; + } + + page = asmc->param_page; + offset = asmc->param_offset; + + arm_smccc_smc(asmc->func_id, page, offset, 0, 0, 0, 0, 0, &res, RT_NULL); + + if (rt_unlikely(res.a0)) + { + err = -RT_EIO; + } + else + { + err = scmi_shmem_msg_read(shmem, msg); + } + + scmi_shmem_clear_channel(shmem); + +_out_lock: + rt_spin_unlock(&asmc->lock); + + return err; +} + +struct scmi_agent_ops scmi_agent_smc_ops = +{ + .name = "smc", + .setup = scmi_agent_smc_setup, + .process_msg = scmi_agent_smc_process_msg, +}; diff --git a/components/drivers/firmware/arm_scmi/agent.c b/components/drivers/firmware/arm_scmi/agent.c new file mode 100644 index 000000000000..928025d4a0b0 --- /dev/null +++ b/components/drivers/firmware/arm_scmi/agent.c @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-11-26 GuEe-GUI first version + */ + +#include "agent.h" + +#define DBG_TAG "scmi.agent" +#define DBG_LVL DBG_INFO +#include + +rt_err_t rt_scmi_process_msg(struct rt_scmi_device *sdev, struct rt_scmi_msg *msg) +{ + struct scmi_agent *agent; + + if (rt_unlikely(!sdev || !msg)) + { + return -RT_EINVAL; + } + + agent = sdev->agent; + msg->sdev = sdev; + + return agent->ops->process_msg(agent, msg); +} + +static const char * const _scmi_error_table[] = +{ + [-SCMI_SUCCESS] = "SUCCESS", + [-SCMI_ERR_SUPPORT] = "SUPPORT", + [-SCMI_ERR_PARAMS] = "PARAMS", + [-SCMI_ERR_ACCESS] = "ACCESS", + [-SCMI_ERR_ENTRY] = "ENTRY", + [-SCMI_ERR_RANGE] = "RANGE", + [-SCMI_ERR_BUSY] = "BUSY", + [-SCMI_ERR_COMMS] = "COMMS", + [-SCMI_ERR_GENERIC] = "GENERIC", + [-SCMI_ERR_HARDWARE] = "HARDWARE", + [-SCMI_ERR_PROTOCOL] = "PROTOCOL", +}; + +const char *rt_scmi_strerror(rt_base_t err) +{ + const char *str; + + err = err < 0 ? -err : err; + + if (err < RT_ARRAY_SIZE(_scmi_error_table)) + { + str = _scmi_error_table[err]; + } + else + { + str = "UNKNOWN"; + } + + return str; +} + +static rt_err_t scmi_channels_setup(struct scmi_agent *agent, + struct rt_platform_device *pdev) +{ + struct rt_ofw_node *np = pdev->parent.ofw_node, *chn; + + rt_ofw_foreach_available_child_node(np, chn) + { + rt_uint32_t prot_id; + struct rt_scmi_device *sdev; + + if (rt_ofw_prop_read_u32(chn, "reg", &prot_id)) + { + continue; + } + + sdev = rt_calloc(1, sizeof(*sdev)); + + if (!sdev) + { + rt_ofw_node_put(chn); + return -RT_ENOMEM; + } + + sdev->parent.ofw_node = chn; + sdev->protocol_id = prot_id; + sdev->agent = agent; + + rt_scmi_device_register(sdev); + } + + return RT_EOK; +} + +static rt_err_t scmi_probe(struct rt_platform_device *pdev) +{ + rt_err_t err; + struct rt_scmi_device *base_sdev; + struct scmi_agent *agent = rt_malloc(sizeof(*agent) + sizeof(*base_sdev)); + const struct scmi_agent_ops *agent_ops; + + if (!agent) + { + return -RT_ENOMEM; + } + + agent_ops = pdev->id->data; + agent->ops = agent_ops; + + if ((err = agent_ops->setup(agent, pdev))) + { + LOG_E("Setup interface %s error = %s", agent_ops->name, rt_strerror(err)); + goto _fail; + } + + if ((err = scmi_channels_setup(agent, pdev))) + { + goto _fail; + } + + base_sdev = (void *)&agent[1]; + rt_memset(base_sdev, 0, sizeof(*base_sdev)); + + base_sdev->protocol_id = SCMI_PROTOCOL_ID_BASE; + base_sdev->agent = agent; + + if ((err = rt_scmi_device_register(base_sdev))) + { + LOG_W("Base protocol register error = %s", rt_strerror(err)); + } + + return RT_EOK; + +_fail: + rt_free(agent); + + return err; +} + +extern struct rt_scmi_agent_ops scmi_agent_smc_ops; + +static const struct rt_ofw_node_id scmi_ofw_ids[] = +{ +#ifdef RT_FIRMWARE_ARM_SCMI_TRANSPORT_SMC + { .compatible = "arm,scmi-smc", .data = &scmi_agent_smc_ops }, + { .compatible = "arm,scmi-smc-param", .data = &scmi_agent_smc_ops }, +#endif + { /* sentinel */ } +}; + +static struct rt_platform_driver scmi_driver = +{ + .name = "arm-scmi", + .ids = scmi_ofw_ids, + + .probe = scmi_probe, +}; + +static int scmi_drv_register(void) +{ + rt_platform_driver_register(&scmi_driver); + + return 0; +} +INIT_FRAMEWORK_EXPORT(scmi_drv_register); diff --git a/components/drivers/firmware/arm_scmi/agent.h b/components/drivers/firmware/arm_scmi/agent.h new file mode 100644 index 000000000000..d82db335e365 --- /dev/null +++ b/components/drivers/firmware/arm_scmi/agent.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-11-26 GuEe-GUI first version + */ + +#ifndef __SCMI_AGENT_H__ +#define __SCMI_AGENT_H__ + +#include +#include + +#include "shmem.h" + +struct scmi_agent_ops +{ + const char *name; + + rt_err_t (*setup)(struct scmi_agent *agent, struct rt_platform_device *pdev); + rt_err_t (*process_msg)(struct scmi_agent *agent, struct rt_scmi_msg *msg); +}; + +struct scmi_agent +{ + const struct scmi_agent_ops *ops; + + void *priv; +}; + +#endif /* __SCMI_AGENT_H__ */ diff --git a/components/drivers/firmware/arm_scmi/bus.c b/components/drivers/firmware/arm_scmi/bus.c new file mode 100644 index 000000000000..e4e9c7e12a73 --- /dev/null +++ b/components/drivers/firmware/arm_scmi/bus.c @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-11-26 GuEe-GUI first version + */ + +#include +#include + +#define DBG_TAG "scmi.bus" +#define DBG_LVL DBG_INFO +#include + +static struct rt_bus scmi_bus; + +rt_err_t rt_scmi_driver_register(struct rt_scmi_driver *driver) +{ + RT_ASSERT(driver != RT_NULL); + + driver->parent.bus = &scmi_bus; + + return rt_driver_register(&driver->parent); +} + +rt_err_t rt_scmi_device_register(struct rt_scmi_device *device) +{ + RT_ASSERT(device != RT_NULL); + + return rt_bus_add_device(&scmi_bus, &device->parent); +} +static rt_bool_t scmi_match(rt_driver_t drv, rt_device_t dev) +{ + const struct rt_scmi_device_id *id; + struct rt_scmi_driver *driver = rt_container_of(drv, struct rt_scmi_driver, parent); + struct rt_scmi_device *device = rt_container_of(dev, struct rt_scmi_device, parent); + + for (id = driver->ids; id->protocol_id; ++id) + { + if (id->protocol_id == device->protocol_id) + { + if (!id->name || !device->name || !rt_strcmp(id->name, device->name)) + { + return RT_TRUE; + } + } + } + + return RT_FALSE; +} + +static rt_err_t scmi_probe(rt_device_t dev) +{ + rt_err_t err; + struct rt_scmi_driver *driver = rt_container_of(dev->drv, struct rt_scmi_driver, parent); + struct rt_scmi_device *device = rt_container_of(dev, struct rt_scmi_device, parent); + + if (!device->agent) + { + return -RT_EINVAL; + } + + err = driver->probe(device); + + return err; +} + +static struct rt_bus scmi_bus = +{ + .name = "scmi", + .match = scmi_match, + .probe = scmi_probe, +}; + +static int scmi_bus_init(void) +{ + rt_bus_register(&scmi_bus); + + return 0; +} +INIT_CORE_EXPORT(scmi_bus_init); diff --git a/components/drivers/firmware/arm_scmi/shmem.c b/components/drivers/firmware/arm_scmi/shmem.c new file mode 100644 index 000000000000..bfc83767e596 --- /dev/null +++ b/components/drivers/firmware/arm_scmi/shmem.c @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-11-26 GuEe-GUI first version + */ + +#define DBG_TAG "scmi.shmem" +#define DBG_LVL DBG_INFO +#include + +#include "shmem.h" + +struct scmi_shared_mem +{ + rt_le32_t reserved; + rt_le32_t channel_status; +#define SCMI_SHMEM_CHAN_STAT_CHANNEL_ERROR RT_BIT(1) +#define SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE RT_BIT(0) + rt_le32_t reserved1[2]; + rt_le32_t flags; +#define SCMI_SHMEM_FLAG_INTR_ENABLED RT_BIT(0) + rt_le32_t length; + rt_le32_t msg_header; + rt_uint8_t msg_payload[]; +}; + +rt_err_t scmi_shmem_msg_write(struct scmi_shared_mem *shmem, + struct rt_scmi_msg *msg) +{ + if (rt_unlikely(!shmem || !msg || + !msg->in_msg || !msg->in_msg_size || + !msg->out_msg || !msg->out_msg_size)) + { + return -RT_EINVAL; + } + + if (rt_unlikely(!(shmem->channel_status & SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE))) + { + LOG_E("Channel busy"); + + return -RT_EBUSY; + } + + /* Load message in shared memory */ + shmem->channel_status &= ~SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE; + shmem->length = msg->in_msg_size + sizeof(shmem->msg_header); + shmem->msg_header = SHMEM_HEADER_TOKEN(0) | + SHMEM_HEADER_MESSAGE_TYPE(0) | + SHMEM_HEADER_PROTOCOL_ID(msg->sdev->protocol_id) | + SHMEM_HEADER_MESSAGE_ID(msg->message_id); + + rt_memcpy(shmem->msg_payload, msg->in_msg, msg->in_msg_size); + + return RT_EOK; +} + +rt_err_t scmi_shmem_msg_read(struct scmi_shared_mem *shmem, struct rt_scmi_msg *msg) +{ + if (rt_unlikely(!shmem || !msg)) + { + return -RT_EINVAL; + } + + if (rt_unlikely(!(shmem->channel_status & SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE))) + { + LOG_E("Channel unexpectedly busy"); + + return -RT_EBUSY; + } + + if (rt_unlikely(shmem->channel_status & SCMI_SHMEM_CHAN_STAT_CHANNEL_ERROR)) + { + LOG_E("Channel error reported, reset channel"); + + return -RT_EIO; + } + + if (rt_unlikely(shmem->length > msg->out_msg_size + sizeof(shmem->msg_header))) + { + LOG_E("Buffer < %u too small", shmem->length); + + return -RT_EINVAL; + } + + msg->out_msg_size = shmem->length - sizeof(shmem->msg_header); + + rt_memcpy(msg->out_msg, shmem->msg_payload, msg->out_msg_size); + + return RT_EOK; +} + +void scmi_shmem_clear_channel(struct scmi_shared_mem *shmem) +{ + if (shmem) + { + shmem->channel_status &= ~SCMI_SHMEM_CHAN_STAT_CHANNEL_ERROR; + } +} diff --git a/components/drivers/firmware/arm_scmi/shmem.h b/components/drivers/firmware/arm_scmi/shmem.h new file mode 100644 index 000000000000..b96b120a0f5d --- /dev/null +++ b/components/drivers/firmware/arm_scmi/shmem.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-11-26 GuEe-GUI first version + */ + +#ifndef __SCMI_SHMEM_H__ +#define __SCMI_SHMEM_H__ + +#include +#include + +struct scmi_shared_mem; + +rt_err_t scmi_shmem_msg_write(struct scmi_shared_mem *shmem, struct rt_scmi_msg *msg); +rt_err_t scmi_shmem_msg_read(struct scmi_shared_mem *shmem, struct rt_scmi_msg *msg); +void scmi_shmem_clear_channel(struct scmi_shared_mem *shmem); + +/* Share memory */ +#define SHMEM_HEADER_TOKEN(token) (((token) << 18) & RT_GENMASK(31, 18)) +#define SHMEM_HEADER_PROTOCOL_ID(proto) (((proto) << 10) & RT_GENMASK(17, 10)) +#define SHMEM_HEADER_MESSAGE_TYPE(type) (((type) << 18) & RT_GENMASK(9, 8)) +#define SHMEM_HEADER_MESSAGE_ID(id) ((id) & RT_GENMASK(7, 0)) + +#endif /* __SCMI_SHMEM_H__ */ diff --git a/components/drivers/firmware/psci/psci.c b/components/drivers/firmware/psci/psci.c index c1b4ff41827a..e5dfde5c4bbe 100644 --- a/components/drivers/firmware/psci/psci.c +++ b/components/drivers/firmware/psci/psci.c @@ -19,11 +19,11 @@ #include #include -#include #include #include #include #include +#include struct psci_ops { @@ -319,14 +319,14 @@ static rt_err_t psci_0_2_init(struct rt_ofw_node *np) _psci_ops.get_affinity_info = psci_affinity_info; _psci_ops.migrate_info_type = psci_migrate_info_type; - if (!rt_pm_shutdown) + if (!rt_dm_machine_shutdown) { - rt_pm_shutdown = psci_system_off; + rt_dm_machine_shutdown = psci_system_off; } - if (!rt_pm_reset) + if (!rt_dm_machine_reset) { - rt_pm_reset = psci_system_reboot; + rt_dm_machine_reset = psci_system_reboot; } } else @@ -427,4 +427,4 @@ static int psci_drv_register(void) return 0; } -INIT_SUBSYS_EXPORT(psci_drv_register); +INIT_FRAMEWORK_EXPORT(psci_drv_register); diff --git a/components/drivers/firmware/qemu/SConscript b/components/drivers/firmware/qemu/SConscript new file mode 100644 index 000000000000..1f5108aa31e8 --- /dev/null +++ b/components/drivers/firmware/qemu/SConscript @@ -0,0 +1,15 @@ +from building import * + +group = [] + +if not GetDepend(['RT_FIRMWARE_QEMU_FW_CFG']): + Return('group') + +cwd = GetCurrentDir() +CPPPATH = [cwd + '/../../include'] + +src = Glob('*.c') + +group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') diff --git a/components/drivers/firmware/qemu/fw_cfg.c b/components/drivers/firmware/qemu/fw_cfg.c new file mode 100644 index 000000000000..dd48f4db7c45 --- /dev/null +++ b/components/drivers/firmware/qemu/fw_cfg.c @@ -0,0 +1,724 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-02-25 GuEe-GUI the first version + */ + +#include +#include +#include +#include + +#define DBG_TAG "fw.qemu" +#define DBG_LVL DBG_INFO +#include + +#include +#include + +#include +#include + +#include "fw_cfg.h" + +/* arch-specific ctrl & data register offsets are not available in ACPI, DT */ +#if !(defined(FW_CFG_CTRL_OFF) && defined(FW_CFG_DATA_OFF)) +# if (defined(ARCH_ARM) || defined(ARCH_ARMV8) || defined(ARCH_LOONGARCH)) +# define FW_CFG_CTRL_OFF 0x08 +# define FW_CFG_DATA_OFF 0x00 +# define FW_CFG_DMA_OFF 0x10 +# elif defined(ARCH_PARISC) /* parisc */ +# define FW_CFG_CTRL_OFF 0x00 +# define FW_CFG_DATA_OFF 0x04 +# elif (defined(ARCH_PPC) || defined(ARCH_SPARC32)) /* ppc/mac,sun4m */ +# define FW_CFG_CTRL_OFF 0x00 +# define FW_CFG_DATA_OFF 0x02 +# elif (defined(ARCH_IA32) || defined(ARCH_SPARC64)) /* x86, sun4u */ +# define FW_CFG_CTRL_OFF 0x00 +# define FW_CFG_DATA_OFF 0x01 +# define FW_CFG_DMA_OFF 0x04 +# else +# error "QEMU FW_CFG not available on this architecture!" +# endif +#endif + +struct fw_cfg_info +{ + struct rt_object obj; + + rt_list_t list; + + rt_uint32_t size; + rt_uint16_t select; + char name[FW_CFG_MAX_FILE_PATH]; +}; +#define raw_to_fw_cfg_info(raw) rt_container_of(raw, struct fw_cfg_info, obj) + +static void *_fw_cfg_dev_base; +static void *_fw_cfg_reg_ctrl; +static void *_fw_cfg_reg_data; +static void *_fw_cfg_reg_dma; +static rt_bool_t _fw_cfg_is_mmio; +static rt_uint32_t _fw_cfg_rev = 0; + +static rt_object_t _directfs_firmware_root = RT_NULL; +static struct rt_spinlock _fw_cfg_dev_lock = { 0 }; +static rt_list_t _fw_cfg_nodes = RT_LIST_OBJECT_INIT(_fw_cfg_nodes); + +static void fw_cfg_sel_endianness(rt_uint16_t key) +{ + rt_hw_barrier(dsb, st); + + if (_fw_cfg_is_mmio) + { + HWREG16(_fw_cfg_reg_ctrl) = rt_cpu_to_be16(key); + } + else + { + HWREG16(_fw_cfg_reg_ctrl) = key; + } +} + +static rt_base_t fw_cfg_read_blob(rt_uint16_t key, void *buf, rt_off_t pos, rt_size_t count) +{ + rt_uint8_t tmp; + + rt_spin_lock(&_fw_cfg_dev_lock); + fw_cfg_sel_endianness(key); + + while (pos-- > 0) + { + tmp = HWREG8(_fw_cfg_reg_data); + } + + if (count) + { + int loop = count; + rt_uint8_t *buffer = buf; + + do { + tmp = HWREG8(_fw_cfg_reg_data); + *buffer++ = tmp; + } while (--loop); + } + + rt_spin_unlock(&_fw_cfg_dev_lock); + + return count; +} + +rt_inline rt_bool_t fw_cfg_dma_enabled(void) +{ + return (_fw_cfg_rev & FW_CFG_VERSION_DMA) && _fw_cfg_reg_dma; +} + +/* qemu fw_cfg device is sync today, but spec says it may become async */ +static void fw_cfg_wait_for_control(struct fw_cfg_dma_access *dma) +{ + for (;;) + { + rt_uint32_t ctrl = rt_be32_to_cpu(HWREG32(&dma->control)); + + /* do not reorder the read to d->control */ + rt_hw_barrier(dsb, ld); + + if ((ctrl & ~FW_CFG_DMA_CTL_ERROR) == 0) + { + break; + } + + rt_hw_cpu_relax(); + } +} + +static rt_base_t fw_cfg_dma_transfer(void *address, rt_uint32_t length, rt_uint32_t control) +{ + rt_ubase_t dma_pa; + rt_base_t res = length; + struct fw_cfg_dma_access dma = + { + .address = rt_cpu_to_be64((rt_uint64_t)(address ? rt_kmem_v2p(address) : 0)), + .length = rt_cpu_to_be32(length), + .control = rt_cpu_to_be32(control), + }; + + dma_pa = (rt_ubase_t)rt_kmem_v2p(&dma); + + HWREG32(_fw_cfg_reg_dma) = rt_cpu_to_be32((rt_uint64_t)dma_pa >> 32); + /* force memory to sync before notifying device via MMIO */ + rt_hw_barrier(dsb, st); + HWREG32(_fw_cfg_reg_dma + 4) = rt_cpu_to_be32(dma_pa); + + fw_cfg_wait_for_control(&dma); + + if ((rt_be32_to_cpu(HWREG32(&dma.control)) & FW_CFG_DMA_CTL_ERROR)) + { + res = -RT_EIO; + } + + return res; +} + +static rt_base_t fw_cfg_write_blob(rt_uint16_t key, void *buf, rt_off_t pos, rt_size_t count) +{ + rt_base_t res = count; + + rt_spin_lock(&_fw_cfg_dev_lock); + + if (pos == 0) + { + res = fw_cfg_dma_transfer(buf, count, key << 16 | FW_CFG_DMA_CTL_SELECT | FW_CFG_DMA_CTL_WRITE); + } + else + { + fw_cfg_sel_endianness(key); + res = fw_cfg_dma_transfer(RT_NULL, pos, FW_CFG_DMA_CTL_SKIP); + + if (res >= 0) + { + res = fw_cfg_dma_transfer(buf, count, FW_CFG_DMA_CTL_WRITE); + } + } + + rt_spin_unlock(&_fw_cfg_dev_lock); + + return res; +} + +#ifdef RT_USING_CRASH_CORE +static rt_base_t fw_cfg_write_vmcoreinfo(const struct fw_cfg_file *file) +{ + rt_ubase_t res; + struct fw_cfg_vmcoreinfo info = + { + .guest_format = rt_cpu_to_le16(FW_CFG_VMCOREINFO_FORMAT_ELF), + .size = rt_cpu_to_le32(VMCOREINFO_NOTE_SIZE), + .paddr = rt_cpu_to_le64(vmcoreinfo_note_paddr()), + }; + + res = fw_cfg_write_blob(rt_be16_to_cpu(file->select), &info, 0, sizeof(struct fw_cfg_vmcoreinfo)); + + return res; +} +#endif /* RT_USING_CRASH_CORE */ + +#ifdef RT_VIDEO_FB +#define pixman(a, b, c, d) ((rt_uint32_t)(a) | ((rt_uint32_t)(b) << 8) | ((rt_uint32_t)(c) << 16) | ((rt_uint32_t)(d) << 24)) + +struct ramfb_device +{ + struct rt_device parent; + + struct fb_var_screeninfo var; + struct fb_fix_screeninfo fix; + struct rt_mutex lock; + + const struct fw_cfg_file *file; +}; + +static rt_err_t ramfb_control(rt_device_t dev, int cmd, void *args) +{ + rt_err_t err = RT_EOK; + struct ramfb_device *fbdev = rt_container_of(dev, struct ramfb_device, parent); + + rt_mutex_take(&fbdev->lock, RT_WAITING_FOREVER); + + switch (cmd) + { + case FBIOGET_VSCREENINFO: + if (!args) + { + err = -RT_EINVAL; + break; + } + rt_memcpy(args, &fbdev->var, sizeof(fbdev->var)); + break; + + case FBIOPUT_VSCREENINFO: + { + void *fb; + rt_base_t res; + rt_uint32_t bpp, fb_len, format; + struct fw_cfg_ram_fb ram_fb; + struct fb_var_screeninfo *usr_var = args; + + bpp = usr_var->bits_per_pixel; + + if (bpp == 32) + { + char alpha, codeb; + rt_uint32_t codeb_off; + + if (usr_var->red.length != 8 || + usr_var->green.length != 8 || + usr_var->blue.length != 8 || + usr_var->transp.length != 8) + { + err = -RT_EINVAL; + break; + } + + alpha = usr_var->transp.length ? 'A' : 'X'; + + if (usr_var->transp.offset == 24) + { + codeb_off = 16; + } + else + { + codeb_off = 24; + } + + if (usr_var->red.offset == codeb_off) + { + codeb = 'R'; + } + else if (usr_var->green.offset == codeb_off) + { + codeb = 'G'; + } + else if (usr_var->blue.offset == codeb_off) + { + codeb = 'B'; + } + else + { + err = -RT_EINVAL; + break; + } + + if (codeb_off == 24) + { + format = pixman(codeb, alpha, '2', '4'); + } + else + { + format = pixman(alpha, codeb, '2', '4'); + } + } + else if (bpp == 24) + { + char codea; + + if (usr_var->red.length != 8 || + usr_var->green.length != 8 || + usr_var->blue.length != 8) + { + err = -RT_EINVAL; + break; + } + + if (usr_var->red.offset == 16) + { + codea = 'R'; + } + else if (usr_var->blue.offset == 16) + { + codea = 'B'; + } + else + { + err = -RT_EINVAL; + break; + } + + format = pixman(codea, 'G', '2', '4'); + } + else if (bpp == 16) + { + if (usr_var->red.offset != 11 || usr_var->red.length != 5 || + usr_var->green.offset != 5 || usr_var->green.length != 6 || + usr_var->blue.offset != 6 || usr_var->blue.length != 5) + { + err = -RT_EINVAL; + break; + } + + format = pixman('R', 'G', '1', '6'); + } + else if (bpp == 15) + { + if (usr_var->red.offset != 10 || usr_var->red.length != 5) + { + err = -RT_EINVAL; + break; + } + + if (usr_var->transp.length) + { + if (usr_var->transp.length != 1 || usr_var->transp.offset != 15) + { + err = -RT_EINVAL; + break; + } + } + + format = pixman(usr_var->transp.length ? 'A' : 'X', 'R', '1', '5'); + } + else + { + err = -RT_EINVAL; + break; + } + + fb_len = usr_var->xres * usr_var->yres * (bpp / 8); + fb = rt_malloc_align(fb_len, ARCH_PAGE_SIZE); + + if (!fb) + { + err = -RT_ENOMEM; + break; + } + + ram_fb.addr = rt_cpu_to_be64((rt_ubase_t)rt_kmem_v2p(fb)), + ram_fb.fourcc = rt_cpu_to_be32(format), + ram_fb.flags = rt_cpu_to_be32(0), + ram_fb.width = rt_cpu_to_be32(usr_var->xres), + ram_fb.height = rt_cpu_to_be32(usr_var->yres), + ram_fb.stride = rt_cpu_to_be32((bpp / 8) * usr_var->xres), + + res = fw_cfg_write_blob(rt_be16_to_cpu(fbdev->file->select), &ram_fb, 0, sizeof(struct fw_cfg_ram_fb)); + + if (res >= 0) + { + if (fbdev->fix.smem_start) + { + rt_free_align((void *)fbdev->fix.smem_start); + } + + fbdev->fix.smem_start = (rt_ubase_t)fb; + fbdev->fix.smem_len = fb_len; + } + else + { + rt_free_align(fb); + err = -RT_EIO; + } + } + break; + + case FBIOGET_FSCREENINFO: + if (!args) + { + err = -RT_EINVAL; + break; + } + rt_memcpy(args, &fbdev->fix, sizeof(fbdev->fix)); + break; + + case FBIOBLANK: + if (fbdev->fix.smem_start) + { + rt_memset((void *)fbdev->fix.smem_start, 0, fbdev->fix.smem_len); + } + break; + + case FBIOGETCMAP: + case FBIOPUTCMAP: + case FBIOGET_VBLANK: + case FBIOPAN_DISPLAY: + case FBIO_CURSOR: + case FBIOGET_CON2FBMAP: + case FBIOPUT_CON2FBMAP: + case FBIO_ALLOC: + case FBIO_FREE: + case FBIOGET_GLYPH: + case FBIOGET_HWCINFO: + case FBIOPUT_MODEINFO: + case FBIOGET_DISPINFO: + err = -RT_ENOSYS; + break; + + case FBIO_WAITFORVSYNC: + break; + + default: + err = -RT_EINVAL; + break; + } + + rt_mutex_release(&fbdev->lock); + + return err; +} + +#ifdef RT_USING_DEVICE_OPS +const static struct rt_device_ops ramfb_ops = +{ + .control = ramfb_control, +}; +#endif + +static rt_base_t fw_cfg_setup_ramfb(const struct fw_cfg_file *file) +{ + const char *dev_name; + struct ramfb_device *fbdev = rt_calloc(1, sizeof(*fbdev)); + + if (!fbdev) + { + return -RT_ENOMEM; + } + + fbdev->file = file; + + fbdev->parent.type = RT_Device_Class_Graphic; +#ifdef RT_USING_DEVICE_OPS + fbdev->parent.ops = &ramfb_ops; +#else + fbdev->parent.control = ramfb_control; +#endif + + rt_dm_dev_set_name_auto(&fbdev->parent, "fb"); + dev_name = rt_dm_dev_get_name(&fbdev->parent); + + rt_mutex_init(&fbdev->lock, dev_name, RT_IPC_FLAG_PRIO); + + return rt_device_register(&fbdev->parent, dev_name, RT_DEVICE_FLAG_RDWR); +} +#endif /* RT_VIDEO_FB */ + +static rt_ssize_t fw_cfg_directfs_read_raw(rt_object_t obj, struct directfs_bin_attribute *attr, + char *buffer, rt_off_t pos, rt_size_t count) +{ + rt_ssize_t res; + struct fw_cfg_info *info = raw_to_fw_cfg_info(obj); + + if (pos <= info->size) + { + if (count > info->size - pos) + { + count = info->size - pos; + } + + res = fw_cfg_read_blob(info->select, buffer, pos, count); + } + else + { + res = -RT_EINVAL; + } + + return res; +} + +static struct directfs_bin_attribute fw_cfg_directfs_attr_raw = +{ + .attr = + { + .name = "raw", + }, + .read = fw_cfg_directfs_read_raw, +}; + +static rt_err_t fw_cfg_register_file(const struct fw_cfg_file *file) +{ + char *path; + rt_object_t parent = _directfs_firmware_root; + struct fw_cfg_info *info = rt_malloc(sizeof(*info)); + + if (!info) + { + return -RT_ENOMEM; + } + +#ifdef RT_USING_CRASH_CORE + if (fw_cfg_dma_enabled() && !rt_strcmp(file->name, FW_CFG_VMCOREINFO_FILENAME)) + { + if (fw_cfg_write_vmcoreinfo(file) < 0) + { + LOG_W("failed to write vmcoreinfo"); + } + } +#endif /* RT_USING_CRASH_CORE */ + +#ifdef RT_VIDEO_FB + if (fw_cfg_dma_enabled() && !rt_strcmp(file->name, FW_CFG_RAMFB_FILENAME)) + { + if (fw_cfg_setup_ramfb(file) < 0) + { + LOG_W("failed to setup ramfb"); + } + } +#endif /* RT_VIDEO_FB */ + + rt_list_init(&info->list); + + info->size = rt_be32_to_cpu(file->size); + info->select = rt_be16_to_cpu(file->select); + rt_strncpy(info->name, file->name, FW_CFG_MAX_FILE_PATH); + + rt_list_insert_before(&_fw_cfg_nodes, &info->list); + + path = info->name; + + while (*path) + { + const char *basename = path; + + while (*path && *path != '/') + { + ++path; + } + + if (*path) + { + rt_object_t dir; + + *path = '\0'; + dir = dfs_directfs_find_object(parent, basename); + + if (!dir) + { + dir = rt_malloc(sizeof(*dir)); + + if (!dir) + { + break; + } + + dfs_directfs_create_link(parent, dir, basename); + } + + parent = dir; + *path = '/'; + } + else + { + dfs_directfs_create_link(parent, &info->obj, basename); + dfs_directfs_create_bin_file(&info->obj, &fw_cfg_directfs_attr_raw); + + break; + } + + ++path; + } + + return RT_EOK; +} + +static rt_err_t fw_cfg_register_dir_entries(void) +{ + rt_err_t err = 0; + rt_uint32_t count; + rt_size_t dir_size; + rt_be32_t files_count; + struct fw_cfg_file *dir; + + err = fw_cfg_read_blob(FW_CFG_FILE_DIR, &files_count, 0, sizeof(files_count)); + + if (err < 0) + { + return err; + } + + count = rt_be32_to_cpu(files_count); + dir_size = count * sizeof(struct fw_cfg_file); + + dir = rt_malloc(dir_size); + + if (!dir) + { + return -RT_ENOMEM; + } + + err = fw_cfg_read_blob(FW_CFG_FILE_DIR, dir, sizeof(files_count), dir_size); + + if (err < 0) + { + return err; + } + + _directfs_firmware_root = dfs_directfs_find_object(RT_NULL, "firmware"); + + for (int i = 0; i < count; ++i) + { + if ((err = fw_cfg_register_file(&dir[i]))) + { + break; + } + } + + rt_free(dir); + + return err; +} + +static rt_err_t qemu_fw_cfg_ofw_init(struct rt_platform_device *pdev, rt_uint32_t *ctrl, rt_uint32_t *data, rt_uint32_t *dma) +{ + struct rt_ofw_node *np = pdev->parent.ofw_node; + + rt_ofw_prop_read_u32(np, "ctrl", ctrl); + rt_ofw_prop_read_u32(np, "data", data); + rt_ofw_prop_read_u32(np, "dma", dma); + + _fw_cfg_dev_base = rt_ofw_iomap(np, 0); + + return _fw_cfg_dev_base ? RT_EOK : -RT_ERROR; +} + +static rt_err_t qemu_fw_cfg_probe(struct rt_platform_device *pdev) +{ + rt_le32_t rev; + rt_err_t err = RT_EOK; + char sig[FW_CFG_SIG_SIZE]; + rt_uint32_t ctrl = FW_CFG_CTRL_OFF, data = FW_CFG_DATA_OFF, dma = FW_CFG_DMA_OFF; + + if ((err = qemu_fw_cfg_ofw_init(pdev, &ctrl, &data, &dma))) + { + goto _fail; + } + +#ifdef ARCH_SUPPORT_PIO + _fw_cfg_is_mmio = RT_FALSE; +#else + _fw_cfg_is_mmio = RT_TRUE; +#endif + + _fw_cfg_reg_ctrl = _fw_cfg_dev_base + ctrl; + _fw_cfg_reg_data = _fw_cfg_dev_base + data; + _fw_cfg_reg_dma = _fw_cfg_dev_base + dma; + + if (fw_cfg_read_blob(FW_CFG_SIGNATURE, sig, 0, FW_CFG_SIG_SIZE) < 0 || rt_memcmp(sig, "QEMU", FW_CFG_SIG_SIZE)) + { + err = -RT_ENOSYS; + goto _fail; + } + + if (fw_cfg_read_blob(FW_CFG_ID, &rev, 0, sizeof(rev)) < 0) + { + err = -RT_ENOSYS; + goto _fail; + } + + _fw_cfg_rev = rt_le32_to_cpu(rev); + + fw_cfg_register_dir_entries(); + +_fail: + return err; +} + +static const struct rt_ofw_node_id qemu_fw_cfg_ofw_ids[] = +{ + { .compatible = "qemu,fw-cfg-mmio", }, + { /* sentinel */ } +}; + +static struct rt_platform_driver qemu_fw_cfg_driver = +{ + .name = "qemu-fw-cfg", + .ids = qemu_fw_cfg_ofw_ids, + + .probe = qemu_fw_cfg_probe, +}; + +static int qemu_fw_cfg_drv_register(void) +{ + rt_platform_driver_register(&qemu_fw_cfg_driver); + + return 0; +} +INIT_SUBSYS_EXPORT(qemu_fw_cfg_drv_register); diff --git a/components/drivers/firmware/qemu/fw_cfg.h b/components/drivers/firmware/qemu/fw_cfg.h new file mode 100644 index 000000000000..9cf41275a8c1 --- /dev/null +++ b/components/drivers/firmware/qemu/fw_cfg.h @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-02-25 GuEe-GUI the first version + */ + +#ifndef __QEMU_FW_CFG_H__ +#define __QEMU_FW_CFG_H__ + +#include +#include + +#define FW_CFG_ACPI_DEVICE_ID "QEMU0002" + +/* selector key values for "well-known" fw_cfg entries */ +#define FW_CFG_SIGNATURE 0x00 +#define FW_CFG_ID 0x01 +#define FW_CFG_UUID 0x02 +#define FW_CFG_RAM_SIZE 0x03 +#define FW_CFG_NOGRAPHIC 0x04 +#define FW_CFG_NB_CPUS 0x05 +#define FW_CFG_MACHINE_ID 0x06 +#define FW_CFG_KERNEL_ADDR 0x07 +#define FW_CFG_KERNEL_SIZE 0x08 +#define FW_CFG_KERNEL_CMDLINE 0x09 +#define FW_CFG_INITRD_ADDR 0x0a +#define FW_CFG_INITRD_SIZE 0x0b +#define FW_CFG_BOOT_DEVICE 0x0c +#define FW_CFG_NUMA 0x0d +#define FW_CFG_BOOT_MENU 0x0e +#define FW_CFG_MAX_CPUS 0x0f +#define FW_CFG_KERNEL_ENTRY 0x10 +#define FW_CFG_KERNEL_DATA 0x11 +#define FW_CFG_INITRD_DATA 0x12 +#define FW_CFG_CMDLINE_ADDR 0x13 +#define FW_CFG_CMDLINE_SIZE 0x14 +#define FW_CFG_CMDLINE_DATA 0x15 +#define FW_CFG_SETUP_ADDR 0x16 +#define FW_CFG_SETUP_SIZE 0x17 +#define FW_CFG_SETUP_DATA 0x18 +#define FW_CFG_FILE_DIR 0x19 + +#define FW_CFG_FILE_FIRST 0x20 +#define FW_CFG_FILE_SLOTS_MIN 0x10 + +#define FW_CFG_WRITE_CHANNEL 0x4000 +#define FW_CFG_ARCH_LOCAL 0x8000 +#define FW_CFG_ENTRY_MASK (~(FW_CFG_WRITE_CHANNEL | FW_CFG_ARCH_LOCAL)) + +#define FW_CFG_INVALID 0xffff + +/* width in bytes of fw_cfg control register */ +#define FW_CFG_CTL_SIZE 0x02 + +/* fw_cfg "file name" is up to 56 characters (including terminating nul) */ +#define FW_CFG_MAX_FILE_PATH 56 + +/* size in bytes of fw_cfg signature */ +#define FW_CFG_SIG_SIZE 4 + +/* FW_CFG_ID bits */ +#define FW_CFG_VERSION 0x01 +#define FW_CFG_VERSION_DMA 0x02 + +/* fw_cfg file directory entry type */ +struct fw_cfg_file +{ + rt_be32_t size; + rt_be16_t select; + rt_le16_t reserved; + char name[FW_CFG_MAX_FILE_PATH]; +}; + +/* FW_CFG_DMA_CONTROL bits */ +#define FW_CFG_DMA_CTL_ERROR 0x01 +#define FW_CFG_DMA_CTL_READ 0x02 +#define FW_CFG_DMA_CTL_SKIP 0x04 +#define FW_CFG_DMA_CTL_SELECT 0x08 +#define FW_CFG_DMA_CTL_WRITE 0x10 + +#define FW_CFG_DMA_SIGNATURE 0x51454d5520434647ULL /* "QEMU CFG" */ + +/* Control as first field allows for different structures selected by this + * field, which might be useful in the future + */ +struct fw_cfg_dma_access +{ + rt_be32_t control; + rt_be32_t length; + rt_be64_t address; +} rt_packed; + +#define FW_CFG_RAMFB_FILENAME "etc/ramfb" + +struct fw_cfg_ram_fb +{ + rt_be64_t addr; + rt_be32_t fourcc; + rt_be32_t flags; + rt_be32_t width; + rt_be32_t height; + rt_be32_t stride; +} rt_packed; + +#define FW_CFG_VMCOREINFO_FILENAME "etc/vmcoreinfo" + +#define FW_CFG_VMCOREINFO_FORMAT_NONE 0x0 +#define FW_CFG_VMCOREINFO_FORMAT_ELF 0x1 + +struct fw_cfg_vmcoreinfo +{ + rt_le16_t host_format; + rt_le16_t guest_format; + rt_le32_t size; + rt_le64_t paddr; +} rt_packed; + +#endif /* __QEMU_FW_CFG_H__ */ diff --git a/components/drivers/firmware/raspberrypi/SConscript b/components/drivers/firmware/raspberrypi/SConscript new file mode 100644 index 000000000000..83b544c6a64c --- /dev/null +++ b/components/drivers/firmware/raspberrypi/SConscript @@ -0,0 +1,15 @@ +from building import * + +group = [] + +if not GetDepend(['RT_FIRMWARE_RASPBERRYPI']): + Return('group') + +cwd = GetCurrentDir() +CPPPATH = [cwd + '/../../include'] + +src = Glob('*.c') + +group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') diff --git a/components/drivers/firmware/raspberrypi/firmware.c b/components/drivers/firmware/raspberrypi/firmware.c new file mode 100644 index 000000000000..7736318d5f09 --- /dev/null +++ b/components/drivers/firmware/raspberrypi/firmware.c @@ -0,0 +1,372 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-08-24 GuEe-GUI first version + */ + +#include +#include + +#define DBG_TAG "fw.raspberrypi" +#define DBG_LVL DBG_INFO +#include + +#include +#include "firmware.h" + +#define MBOX_MSG(chan, data28) (((data28) & ~0xf) | ((chan) & 0xf)) +#define MBOX_CHAN(msg) ((msg) & 0xf) +#define MBOX_DATA28(msg) ((msg) & ~0xf) +#define MBOX_CHAN_PROPERTY 8 + +struct rpi_firmware +{ + struct rt_mbox_client parent; + + /* The property channel. */ + struct rt_mbox_chan *chan; + struct rt_completion done; + rt_uint32_t enabled; + + struct ref consumers; + rt_uint32_t get_throttled; + + struct rt_spinlock transaction_lock; +}; + +static rt_err_t rpi_firmware_transaction(struct rpi_firmware *rpi_fw, + rt_uint32_t chan, rt_uint32_t data) +{ + rt_ssize_t res; + rt_uint32_t message = MBOX_MSG(chan, data); + + rt_hw_spin_lock(&rpi_fw->transaction_lock.lock); + + res = rt_mbox_send(rpi_fw->chan, &message, RT_WAITING_FOREVER); + + if (res >= 0) + { + if (rt_completion_wait(&rpi_fw->done, 100)) + { + res = RT_EOK; + } + else + { + res = -RT_ETIMEOUT; + LOG_W("Firmware transaction timeout"); + } + } + else + { + LOG_E("Mailbox send message error = %s", rt_strerror(res)); + } + + rt_hw_spin_unlock(&rpi_fw->transaction_lock.lock); + + return res; +} + +rt_err_t rpi_firmware_property(struct rpi_firmware *rpi_fw, + rt_uint32_t tag, void *tag_data, rt_size_t buf_size) +{ + rt_err_t err; + struct rpi_firmware_property_tag_header *header; + /* + * Some mailboxes can use over 1k bytes. Rather than checking + * size and using stack or kmalloc depending on requirements, + * just use kmalloc. Mailboxes don't get called enough to worry + * too much about the time taken in the allocation. + */ + void *data = rt_malloc(sizeof(*header) + buf_size); + + if (!data) + { + return -RT_ENOMEM; + } + + header = data; + header->tag = tag; + header->buf_size = buf_size; + header->req_resp_size = 0; + rt_memcpy(data + sizeof(*header), tag_data, buf_size); + + err = rpi_firmware_property_list(rpi_fw, data, buf_size + sizeof(*header)); + + rt_memcpy(tag_data, data + sizeof(*header), buf_size); + + rt_free(data); + + return err; +} + +rt_err_t rpi_firmware_property_list(struct rpi_firmware *rpi_fw, + void *data, rt_size_t tag_size) +{ + rt_uint32_t *buf; + rt_ubase_t bus_addr; + rt_size_t size = tag_size + 12; + rt_err_t err; + + /* Packets are processed a dword at a time. */ + if (size & 3) + { + return -RT_EINVAL; + } + + buf = rt_dma_alloc_coherent(rpi_fw->parent.dev, + RT_ALIGN(size, ARCH_PAGE_SIZE), &bus_addr); + + if (!buf) + { + return -RT_ENOMEM; + } + + buf[0] = size; + buf[1] = RPI_FIRMWARE_STATUS_REQUEST; + rt_memcpy(&buf[2], data, tag_size); + buf[size / 4 - 1] = RPI_FIRMWARE_PROPERTY_END; + rt_hw_wmb(); + + err = rpi_firmware_transaction(rpi_fw, MBOX_CHAN_PROPERTY, bus_addr); + + rt_hw_rmb(); + rt_memcpy(data, &buf[2], tag_size); + + if (err == 0 && buf[1] != RPI_FIRMWARE_STATUS_SUCCESS) + { + /* + * The tag name here might not be the one causing the + * error, if there were multiple tags in the request. + * But single-tag is the most common, so go with it. + */ + LOG_E("Request 0x%08x returned status 0x%08x", buf[2], buf[1]); + err = -RT_EINVAL; + } + + rt_dma_free_coherent(rpi_fw->parent.dev, RT_ALIGN(size, ARCH_PAGE_SIZE), buf, bus_addr); + + return err; +} + +rt_uint32_t rpi_firmware_clk_get_max_rate(struct rpi_firmware *rpi_fw, rt_uint32_t id) +{ + struct rpi_firmware_clk_rate_request msg = RPI_FIRMWARE_CLK_RATE_REQUEST(id); + + if (rpi_firmware_property(rpi_fw, RPI_FIRMWARE_GET_MAX_CLOCK_RATE, + &msg, sizeof(msg))) + { + return RT_UINT32_MAX; + } + + return rt_le32_to_cpu(msg.rate); +} + +static const struct rt_ofw_node_id rpi_firmware_ofw_ids[]; + +struct rt_ofw_node *rpi_firmware_find_node(void) +{ + return rt_ofw_find_node_by_ids(RT_NULL, rpi_firmware_ofw_ids); +} + +struct rpi_firmware *rpi_firmware_get(struct rt_ofw_node *fw_np) +{ + struct rpi_firmware *rpi_fw = rt_ofw_data(fw_np); + + if (!rpi_fw) + { + return RT_NULL; + } + + if (!ref_get_unless_zero(&rpi_fw->consumers)) + { + return RT_NULL; + } + + return rpi_fw; +} + +static void rpi_firmware_release(struct ref *ref) +{ + struct rpi_firmware *rpi_fw = rt_container_of(ref, struct rpi_firmware, consumers); + + rt_mbox_free(rpi_fw->chan); + rt_free(rpi_fw); +} + +void rpi_firmware_put(struct rpi_firmware *rpi_fw) +{ + ref_put(&rpi_fw->consumers, rpi_firmware_release); +} + +static rt_err_t rpi_firmware_notify_reboot(struct rt_device *dev, char *cmd) +{ + rt_uint32_t reboot_flags = 0; + struct rpi_firmware *rpi_fw = dev->user_data; + + if (cmd && rt_strstr(cmd, "tryboot")) + { + reboot_flags |= 0x1; + } + + if (reboot_flags) + { + rpi_firmware_property(rpi_fw, RPI_FIRMWARE_SET_REBOOT_FLAGS, + &reboot_flags, sizeof(reboot_flags)); + } + + rpi_firmware_property(rpi_fw, RPI_FIRMWARE_NOTIFY_REBOOT, RT_NULL, 0); + + return RT_EOK; +} + +static void rpi_firmware_rx_callback(struct rt_mbox_client *client, void *data) +{ + struct rpi_firmware *rpi_fw = rt_container_of(client, struct rpi_firmware, parent); + + rt_completion_done(&rpi_fw->done); +} + +static void rpi_firmware_check_revision_info(struct rpi_firmware *rpi_fw) +{ + struct tm *tm; + rt_uint32_t revision, variant, hash[5]; + static const char * const variant_strs[] = + { + "unknown", + "start", + "start_x", + "start_db", + "start_cd", + }; + const char *variant_str = "cmd unsupported"; + + if (rpi_firmware_property(rpi_fw, RPI_FIRMWARE_GET_FIRMWARE_REVISION, + &revision, sizeof(revision))) + { + goto _check_hash; + } + + tm = localtime((time_t *)&revision); + + if (!rpi_firmware_property(rpi_fw, RPI_FIRMWARE_GET_FIRMWARE_VARIANT, + &variant, sizeof(variant))) + { + if (variant >= RT_ARRAY_SIZE(variant_strs)) + { + variant = 0; + } + + variant_str = variant_strs[variant]; + } + + LOG_I("Attached to firmware from %04d-%02d-%02d-%02d-%02d-%02d, variant %s", + tm->tm_year, tm->tm_mon, tm->tm_mday, + tm->tm_hour, tm->tm_min, tm->tm_sec, variant_str); + (void)tm; + (void)variant_str; + +_check_hash: + if (rpi_firmware_property(rpi_fw, RPI_FIRMWARE_GET_FIRMWARE_HASH, + hash, sizeof(hash))) + { + return; + } + + LOG_I("Firmware hash is %08x%08x%08x%08x%08x", + hash[0], hash[1], hash[2], hash[3], hash[4]); +} + +static rt_err_t rpi_firmware_probe(struct rt_platform_device *pdev) +{ + rt_err_t err; + rt_uint32_t packet; + struct rt_device *dev = &pdev->parent; + struct rpi_firmware *rpi_fw = rt_calloc(1, sizeof(*rpi_fw)); + + if (!rpi_fw) + { + return -RT_ENOMEM; + } + + dev->user_data = rpi_fw; + rpi_fw->parent.dev = dev; + rpi_fw->parent.rx_callback = rpi_firmware_rx_callback; + + rpi_fw->chan = rt_mbox_request_by_index(&rpi_fw->parent, 0); + + if ((err = rt_dm_reboot_mode_register(dev, &rpi_firmware_notify_reboot))) + { + goto _fail; + } + + rt_completion_init(&rpi_fw->done); + ref_init(&rpi_fw->consumers); + rt_spin_lock_init(&rpi_fw->transaction_lock); + + rt_dm_dev_bind_fwdata(dev, RT_NULL, rpi_fw); + + rpi_firmware_check_revision_info(rpi_fw); + + if (!rpi_firmware_property(rpi_fw, RPI_FIRMWARE_GET_THROTTLED, + &packet, sizeof(packet))) + { + struct rt_platform_device *voltage_monitor; + + if (!(voltage_monitor = rt_calloc(1, sizeof(*voltage_monitor)))) + { + LOG_E("No memory to create voltage monitor"); + + err = -RT_ENOMEM; + goto _fail; + } + + voltage_monitor->name = "raspberrypi-voltage-monitor"; + voltage_monitor->priv = dev; + + rt_bus_add_device(pdev->parent.bus, voltage_monitor); + } + + return RT_EOK; + +_fail: + + rt_free(rpi_fw); + + return err; +} + +static rt_err_t rpi_firmware_shutdown(struct rt_platform_device *pdev) +{ + struct rpi_firmware *rpi_fw = pdev->parent.user_data; + + rpi_firmware_property(rpi_fw, RPI_FIRMWARE_NOTIFY_REBOOT, RT_NULL, 0); + + return RT_EOK; +} + +static const struct rt_ofw_node_id rpi_firmware_ofw_ids[] = +{ + { .compatible = "raspberrypi,bcm2835-firmware", }, + { /* sentinel */ } +}; + +static struct rt_platform_driver rpi_firmware_driver = +{ + .name = "raspberrypi-firmware", + .ids = rpi_firmware_ofw_ids, + + .probe = rpi_firmware_probe, + .shutdown = rpi_firmware_shutdown, +}; + +static int rpi_firmware_drv_register(void) +{ + rt_platform_driver_register(&rpi_firmware_driver); + + return 0; +} +INIT_PLATFORM_EXPORT(rpi_firmware_drv_register); diff --git a/components/drivers/firmware/raspberrypi/firmware.h b/components/drivers/firmware/raspberrypi/firmware.h new file mode 100644 index 000000000000..de4c577a9f22 --- /dev/null +++ b/components/drivers/firmware/raspberrypi/firmware.h @@ -0,0 +1,206 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-08-24 GuEe-GUI first version + */ + +#ifndef __RASPBERRYPI_FIRMWARE_H__ +#define __RASPBERRYPI_FIRMWARE_H__ + +#include + +struct rpi_firmware; + +enum rpi_firmware_property_status +{ + RPI_FIRMWARE_STATUS_REQUEST = 0, + RPI_FIRMWARE_STATUS_SUCCESS = 0x80000000, + RPI_FIRMWARE_STATUS_ERROR = 0x80000001, +}; + +struct rpi_firmware_property_tag_header +{ + rt_uint32_t tag; + rt_uint32_t buf_size; + rt_uint32_t req_resp_size; /* always 4 byte aligned */ +}; + +enum rpi_firmware_property_tag +{ + RPI_FIRMWARE_PROPERTY_END = 0, + RPI_FIRMWARE_GET_FIRMWARE_REVISION = 0x00000001, + RPI_FIRMWARE_GET_FIRMWARE_VARIANT = 0x00000002, + RPI_FIRMWARE_GET_FIRMWARE_HASH = 0x00000003, + + RPI_FIRMWARE_SET_CURSOR_INFO = 0x00008010, + RPI_FIRMWARE_SET_CURSOR_STATE = 0x00008011, + + RPI_FIRMWARE_GET_BOARD_MODEL = 0x00010001, + RPI_FIRMWARE_GET_BOARD_REVISION = 0x00010002, + RPI_FIRMWARE_GET_BOARD_MAC_ADDRESS = 0x00010003, + RPI_FIRMWARE_GET_BOARD_SERIAL = 0x00010004, + RPI_FIRMWARE_GET_ARM_MEMORY = 0x00010005, + RPI_FIRMWARE_GET_VC_MEMORY = 0x00010006, + RPI_FIRMWARE_GET_CLOCKS = 0x00010007, + RPI_FIRMWARE_GET_POWER_STATE = 0x00020001, + RPI_FIRMWARE_GET_TIMING = 0x00020002, + RPI_FIRMWARE_SET_POWER_STATE = 0x00028001, + RPI_FIRMWARE_GET_CLOCK_STATE = 0x00030001, + RPI_FIRMWARE_GET_CLOCK_RATE = 0x00030002, + RPI_FIRMWARE_GET_VOLTAGE = 0x00030003, + RPI_FIRMWARE_GET_MAX_CLOCK_RATE = 0x00030004, + RPI_FIRMWARE_GET_MAX_VOLTAGE = 0x00030005, + RPI_FIRMWARE_GET_TEMPERATURE = 0x00030006, + RPI_FIRMWARE_GET_MIN_CLOCK_RATE = 0x00030007, + RPI_FIRMWARE_GET_MIN_VOLTAGE = 0x00030008, + RPI_FIRMWARE_GET_TURBO = 0x00030009, + RPI_FIRMWARE_GET_MAX_TEMPERATURE = 0x0003000a, + RPI_FIRMWARE_GET_STC = 0x0003000b, + RPI_FIRMWARE_ALLOCATE_MEMORY = 0x0003000c, + RPI_FIRMWARE_LOCK_MEMORY = 0x0003000d, + RPI_FIRMWARE_UNLOCK_MEMORY = 0x0003000e, + RPI_FIRMWARE_RELEASE_MEMORY = 0x0003000f, + RPI_FIRMWARE_EXECUTE_CODE = 0x00030010, + RPI_FIRMWARE_EXECUTE_QPU = 0x00030011, + RPI_FIRMWARE_SET_ENABLE_QPU = 0x00030012, + RPI_FIRMWARE_GET_DISPMANX_RESOURCE_MEM_HANDLE = 0x00030014, + RPI_FIRMWARE_GET_EDID_BLOCK = 0x00030020, + RPI_FIRMWARE_GET_CUSTOMER_OTP = 0x00030021, + RPI_FIRMWARE_GET_EDID_BLOCK_DISPLAY = 0x00030023, + RPI_FIRMWARE_GET_DOMAIN_STATE = 0x00030030, + RPI_FIRMWARE_GET_THROTTLED = 0x00030046, + RPI_FIRMWARE_GET_CLOCK_MEASURED = 0x00030047, + RPI_FIRMWARE_NOTIFY_REBOOT = 0x00030048, + RPI_FIRMWARE_SET_CLOCK_STATE = 0x00038001, + RPI_FIRMWARE_SET_CLOCK_RATE = 0x00038002, + RPI_FIRMWARE_SET_VOLTAGE = 0x00038003, + RPI_FIRMWARE_SET_TURBO = 0x00038009, + RPI_FIRMWARE_SET_CUSTOMER_OTP = 0x00038021, + RPI_FIRMWARE_SET_DOMAIN_STATE = 0x00038030, + RPI_FIRMWARE_GET_GPIO_STATE = 0x00030041, + RPI_FIRMWARE_SET_GPIO_STATE = 0x00038041, + RPI_FIRMWARE_SET_SDHOST_CLOCK = 0x00038042, + RPI_FIRMWARE_GET_GPIO_CONFIG = 0x00030043, + RPI_FIRMWARE_SET_GPIO_CONFIG = 0x00038043, + RPI_FIRMWARE_GET_PERIPH_REG = 0x00030045, + RPI_FIRMWARE_SET_PERIPH_REG = 0x00038045, + RPI_FIRMWARE_GET_POE_HAT_VAL = 0x00030049, + RPI_FIRMWARE_SET_POE_HAT_VAL = 0x00038049, + RPI_FIRMWARE_SET_POE_HAT_VAL_OLD = 0x00030050, + RPI_FIRMWARE_NOTIFY_XHCI_RESET = 0x00030058, + RPI_FIRMWARE_GET_REBOOT_FLAGS = 0x00030064, + RPI_FIRMWARE_SET_REBOOT_FLAGS = 0x00038064, + RPI_FIRMWARE_NOTIFY_DISPLAY_DONE = 0x00030066, + RPI_FIRMWARE_GET_BUTTONS_PRESSED = 0x00030088, + RPI_FIRMWARE_GET_RTC_REG = 0x00030087, + RPI_FIRMWARE_SET_RTC_REG = 0x00038087, + + /* Dispmanx TAGS */ + RPI_FIRMWARE_FRAMEBUFFER_ALLOCATE = 0x00040001, + RPI_FIRMWARE_FRAMEBUFFER_BLANK = 0x00040002, + RPI_FIRMWARE_FRAMEBUFFER_GET_PHYSICAL_WIDTH_HEIGHT = 0x00040003, + RPI_FIRMWARE_FRAMEBUFFER_GET_VIRTUAL_WIDTH_HEIGHT = 0x00040004, + RPI_FIRMWARE_FRAMEBUFFER_GET_DEPTH = 0x00040005, + RPI_FIRMWARE_FRAMEBUFFER_GET_PIXEL_ORDER = 0x00040006, + RPI_FIRMWARE_FRAMEBUFFER_GET_ALPHA_MODE = 0x00040007, + RPI_FIRMWARE_FRAMEBUFFER_GET_PITCH = 0x00040008, + RPI_FIRMWARE_FRAMEBUFFER_GET_VIRTUAL_OFFSET = 0x00040009, + RPI_FIRMWARE_FRAMEBUFFER_GET_OVERSCAN = 0x0004000a, + RPI_FIRMWARE_FRAMEBUFFER_GET_PALETTE = 0x0004000b, + RPI_FIRMWARE_FRAMEBUFFER_GET_LAYER = 0x0004000c, + RPI_FIRMWARE_FRAMEBUFFER_GET_TRANSFORM = 0x0004000d, + RPI_FIRMWARE_FRAMEBUFFER_GET_VSYNC = 0x0004000e, + RPI_FIRMWARE_FRAMEBUFFER_GET_TOUCHBUF = 0x0004000f, + RPI_FIRMWARE_FRAMEBUFFER_GET_GPIOVIRTBUF = 0x00040010, + RPI_FIRMWARE_FRAMEBUFFER_RELEASE = 0x00048001, + RPI_FIRMWARE_FRAMEBUFFER_GET_DISPLAY_ID = 0x00040016, + RPI_FIRMWARE_FRAMEBUFFER_SET_DISPLAY_NUM = 0x00048013, + RPI_FIRMWARE_FRAMEBUFFER_GET_NUM_DISPLAYS = 0x00040013, + RPI_FIRMWARE_FRAMEBUFFER_GET_DISPLAY_SETTINGS = 0x00040014, + RPI_FIRMWARE_FRAMEBUFFER_TEST_PHYSICAL_WIDTH_HEIGHT = 0x00044003, + RPI_FIRMWARE_FRAMEBUFFER_TEST_VIRTUAL_WIDTH_HEIGHT = 0x00044004, + RPI_FIRMWARE_FRAMEBUFFER_TEST_DEPTH = 0x00044005, + RPI_FIRMWARE_FRAMEBUFFER_TEST_PIXEL_ORDER = 0x00044006, + RPI_FIRMWARE_FRAMEBUFFER_TEST_ALPHA_MODE = 0x00044007, + RPI_FIRMWARE_FRAMEBUFFER_TEST_VIRTUAL_OFFSET = 0x00044009, + RPI_FIRMWARE_FRAMEBUFFER_TEST_OVERSCAN = 0x0004400a, + RPI_FIRMWARE_FRAMEBUFFER_TEST_PALETTE = 0x0004400b, + RPI_FIRMWARE_FRAMEBUFFER_TEST_LAYER = 0x0004400c, + RPI_FIRMWARE_FRAMEBUFFER_TEST_TRANSFORM = 0x0004400d, + RPI_FIRMWARE_FRAMEBUFFER_TEST_VSYNC = 0x0004400e, + RPI_FIRMWARE_FRAMEBUFFER_SET_PHYSICAL_WIDTH_HEIGHT = 0x00048003, + RPI_FIRMWARE_FRAMEBUFFER_SET_VIRTUAL_WIDTH_HEIGHT = 0x00048004, + RPI_FIRMWARE_FRAMEBUFFER_SET_DEPTH = 0x00048005, + RPI_FIRMWARE_FRAMEBUFFER_SET_PIXEL_ORDER = 0x00048006, + RPI_FIRMWARE_FRAMEBUFFER_SET_ALPHA_MODE = 0x00048007, + RPI_FIRMWARE_FRAMEBUFFER_SET_PITCH = 0x00048008, + RPI_FIRMWARE_FRAMEBUFFER_SET_VIRTUAL_OFFSET = 0x00048009, + RPI_FIRMWARE_FRAMEBUFFER_SET_OVERSCAN = 0x0004800a, + RPI_FIRMWARE_FRAMEBUFFER_SET_PALETTE = 0x0004800b, + + RPI_FIRMWARE_FRAMEBUFFER_SET_TOUCHBUF = 0x0004801f, + RPI_FIRMWARE_FRAMEBUFFER_SET_GPIOVIRTBUF = 0x00048020, + RPI_FIRMWARE_FRAMEBUFFER_SET_VSYNC = 0x0004800e, + RPI_FIRMWARE_FRAMEBUFFER_SET_LAYER = 0x0004800c, + RPI_FIRMWARE_FRAMEBUFFER_SET_TRANSFORM = 0x0004800d, + RPI_FIRMWARE_FRAMEBUFFER_SET_BACKLIGHT = 0x0004800f, + + RPI_FIRMWARE_VCHIQ_INIT = 0x00048010, + + RPI_FIRMWARE_SET_PLANE = 0x00048015, + RPI_FIRMWARE_GET_DISPLAY_TIMING = 0x00040017, + RPI_FIRMWARE_SET_TIMING = 0x00048017, + RPI_FIRMWARE_GET_DISPLAY_CFG = 0x00040018, + RPI_FIRMWARE_SET_DISPLAY_POWER = 0x00048019, + RPI_FIRMWARE_GET_COMMAND_LINE = 0x00050001, + RPI_FIRMWARE_GET_DMA_CHANNELS = 0x00060001, +}; + +enum rpi_firmware_clk_id +{ + RPI_FIRMWARE_EMMC_CLK_ID = 1, + RPI_FIRMWARE_UART_CLK_ID, + RPI_FIRMWARE_ARM_CLK_ID, + RPI_FIRMWARE_CORE_CLK_ID, + RPI_FIRMWARE_V3D_CLK_ID, + RPI_FIRMWARE_H264_CLK_ID, + RPI_FIRMWARE_ISP_CLK_ID, + RPI_FIRMWARE_SDRAM_CLK_ID, + RPI_FIRMWARE_PIXEL_CLK_ID, + RPI_FIRMWARE_PWM_CLK_ID, + RPI_FIRMWARE_HEVC_CLK_ID, + RPI_FIRMWARE_EMMC2_CLK_ID, + RPI_FIRMWARE_M2MC_CLK_ID, + RPI_FIRMWARE_PIXEL_BVB_CLK_ID, + RPI_FIRMWARE_VEC_CLK_ID, + RPI_FIRMWARE_DISP_CLK_ID, + RPI_FIRMWARE_NUM_CLK_ID, +}; + +#define GET_DISPLAY_SETTINGS_PAYLOAD_SIZE 64 + +struct rpi_firmware_clk_rate_request +{ + rt_le32_t id; /* ID of the clock being queried */ + rt_le32_t rate; /* Rate in Hertz. Set by the firmware */ +} rt_packed; + +#define RPI_FIRMWARE_CLK_RATE_REQUEST(ID) \ +{ \ + .id = ID, \ +} + +rt_err_t rpi_firmware_property(struct rpi_firmware *rpi_fw, rt_uint32_t tag, void *tag_data, rt_size_t buf_size); +rt_err_t rpi_firmware_property_list(struct rpi_firmware *rpi_fw, void *data, rt_size_t tag_size); + +rt_uint32_t rpi_firmware_clk_get_max_rate(struct rpi_firmware *rpi_fw, rt_uint32_t id); + +struct rt_ofw_node *rpi_firmware_find_node(void); +struct rpi_firmware *rpi_firmware_get(struct rt_ofw_node *fw_np); +void rpi_firmware_put(struct rpi_firmware *rpi_fw); + +#endif /* __RASPBERRYPI_FIRMWARE_H__ */ diff --git a/components/drivers/hwcrypto/Kconfig b/components/drivers/hwcrypto/Kconfig new file mode 100644 index 000000000000..758a6308b492 --- /dev/null +++ b/components/drivers/hwcrypto/Kconfig @@ -0,0 +1,188 @@ +menuconfig RT_USING_HWCRYPTO + bool "Using Hardware Crypto device drivers" + default n + + if RT_USING_HWCRYPTO + config RT_HWCRYPTO_DEFAULT_NAME + string "Hardware crypto device name" + default "hwcryto" + + config RT_HWCRYPTO_IV_MAX_SIZE + int "IV max size" + default "16" + + config RT_HWCRYPTO_KEYBIT_MAX_SIZE + int "Key max bit length" + default 256 + + config RT_HWCRYPTO_USING_GCM + bool "Using Hardware GCM" + default n + + config RT_HWCRYPTO_USING_AES + bool "Using Hardware AES" + default n + + if RT_HWCRYPTO_USING_AES + config RT_HWCRYPTO_USING_AES_ECB + bool "Using Hardware AES ECB mode" + default y + + config RT_HWCRYPTO_USING_AES_CBC + bool "Using Hardware AES CBC mode" + default n + + config RT_HWCRYPTO_USING_AES_CFB + bool "Using Hardware AES CFB mode" + default n + + config RT_HWCRYPTO_USING_AES_CTR + bool "Using Hardware AES CTR mode" + default n + + config RT_HWCRYPTO_USING_AES_OFB + bool "Using Hardware AES OFB mode" + default n + endif + + config RT_HWCRYPTO_USING_DES + bool "Using Hardware DES" + default n + + if RT_HWCRYPTO_USING_DES + config RT_HWCRYPTO_USING_DES_ECB + bool "Using Hardware DES ECB mode" + default y + + config RT_HWCRYPTO_USING_DES_CBC + bool "Using Hardware DES CBC mode" + default n + endif + + config RT_HWCRYPTO_USING_3DES + bool "Using Hardware 3DES" + default n + + if RT_HWCRYPTO_USING_3DES + config RT_HWCRYPTO_USING_3DES_ECB + bool "Using Hardware 3DES ECB mode" + default y + + config RT_HWCRYPTO_USING_3DES_CBC + bool "Using Hardware 3DES CBC mode" + default n + endif + + config RT_HWCRYPTO_USING_RC4 + bool "Using Hardware RC4" + default n + + config RT_HWCRYPTO_USING_MD5 + bool "Using Hardware MD5" + default n + + config RT_HWCRYPTO_USING_SHA1 + bool "Using Hardware SHA1" + default n + + config RT_HWCRYPTO_USING_SHA2 + bool "Using Hardware SHA2" + default n + + if RT_HWCRYPTO_USING_SHA2 + config RT_HWCRYPTO_USING_SHA2_224 + bool "Using Hardware SHA2_224 mode" + default n + + config RT_HWCRYPTO_USING_SHA2_256 + bool "Using Hardware SHA2_256 mode" + default y + + config RT_HWCRYPTO_USING_SHA2_384 + bool "Using Hardware SHA2_384 mode" + default n + + config RT_HWCRYPTO_USING_SHA2_512 + bool "Using Hardware SHA2_512 mode" + default n + endif + + config RT_HWCRYPTO_USING_RNG + bool "Using Hardware RNG" + default n + + config RT_HWCRYPTO_USING_CRC + bool "Using Hardware CRC" + default n + + if RT_HWCRYPTO_USING_CRC + config RT_HWCRYPTO_USING_CRC_07 + bool "Using Hardware CRC-8 0x07 polynomial" + default n + + config RT_HWCRYPTO_USING_CRC_8005 + bool "Using Hardware CRC-16 0x8005 polynomial" + default n + + config RT_HWCRYPTO_USING_CRC_1021 + bool "Using Hardware CRC-16 0x1021 polynomial" + default n + + config RT_HWCRYPTO_USING_CRC_3D65 + bool "Using Hardware CRC-16 0x3D65 polynomial" + default n + + config RT_HWCRYPTO_USING_CRC_04C11DB7 + bool "Using Hardware CRC-32 0x04C11DB7 polynomial" + default n + endif + + config RT_HWCRYPTO_USING_BIGNUM + bool "Using Hardware bignum" + default n + + if RT_HWCRYPTO_USING_BIGNUM + config RT_HWCRYPTO_USING_BIGNUM_EXPTMOD + bool "Using Hardware bignum expt_mod operation" + default y + + config RT_HWCRYPTO_USING_BIGNUM_MULMOD + bool "Using Hardware bignum mul_mod operation" + default y + + config RT_HWCRYPTO_USING_BIGNUM_MUL + bool "Using Hardware bignum mul operation" + default n + + config RT_HWCRYPTO_USING_BIGNUM_ADD + bool "Using Hardware bignum add operation" + default n + + config RT_HWCRYPTO_USING_BIGNUM_SUB + bool "Using Hardware bignum sub operation" + default n + endif + endif + +config RT_HWCRYPTO_RNG_BCM2835 + bool "Broadcom BCM2835/BCM63xx Random Number Generator support" + depends on RT_USING_DM + depends on RT_USING_HWCRYPTO + select RT_HWCRYPTO_USING_RNG + select RT_USING_RESET + default n + +config RT_HWCRYPTO_RNG_IPROC_RNG200 + bool "Broadcom iProc/STB RNG200 support" + depends on RT_USING_DM + depends on RT_USING_HWCRYPTO + select RT_HWCRYPTO_USING_RNG + default n + +config RT_HWCRYPTO_RNG_ROCKCHIP + bool "Rockchip Random Number Generator support" + depends on RT_USING_DM + depends on RT_USING_HWCRYPTO + select RT_HWCRYPTO_USING_RNG + select RT_USING_RESET + default n diff --git a/components/drivers/hwcrypto/SConscript b/components/drivers/hwcrypto/SConscript index 90feff50b36d..396db28e00d5 100644 --- a/components/drivers/hwcrypto/SConscript +++ b/components/drivers/hwcrypto/SConscript @@ -29,6 +29,15 @@ if GetDepend(['RT_HWCRYPTO_USING_CRC']): if GetDepend(['RT_HWCRYPTO_USING_BIGNUM']): src += ['hw_bignum.c'] +if GetDepend(['RT_HWCRYPTO_RNG_BCM2835']): + src += ['hw-rng-bcm2835.c'] + +if GetDepend(['RT_HWCRYPTO_RNG_IPROC_RNG200']): + src += ['hw-rng-iproc-rng200.c'] + +if GetDepend(['RT_HWCRYPTO_RNG_ROCKCHIP']): + src += ['hw-rng-rockchip.c'] + group = DefineGroup('DeviceDrivers', src, depend = ['RT_USING_HWCRYPTO'], CPPPATH = CPPPATH) Return('group') diff --git a/components/drivers/hwcrypto/hw-rng-bcm2835.c b/components/drivers/hwcrypto/hw-rng-bcm2835.c new file mode 100644 index 000000000000..ad3870b8cc76 --- /dev/null +++ b/components/drivers/hwcrypto/hw-rng-bcm2835.c @@ -0,0 +1,330 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-3-08 GuEe-GUI the first version + */ + +#include +#include +#include + +#define DBG_TAG "rng.bcm2835" +#define DBG_LVL DBG_INFO +#include + +#define RNG_CTRL 0x0 +#define RNG_STATUS 0x4 +#define RNG_DATA 0x8 +#define RNG_INT_MASK 0x10 + +/* enable rng */ +#define RNG_RBGEN 0x1 + +/* the initial numbers generated are "less random" so will be discarded */ +#define RNG_WARMUP_COUNT 0x40000 + +#define RNG_INT_OFF 0x1 + +struct bcm2835_rng_soc_data +{ + rt_bool_t mask_interrupts; +}; + +struct bcm2835_rng +{ + struct rt_hwcrypto_device parent; + + void *regs; + + rt_bool_t mask_interrupts; + struct rt_clk *clk; + struct rt_reset_control *rstc; +}; + +#define raw_to_bcm2835_rng(raw) rt_container_of(raw, struct bcm2835_rng, parent) + +rt_inline rt_uint32_t rng_readl(struct bcm2835_rng *brng, int offset) +{ + return HWREG32(brng->regs + offset); +} + +rt_inline void rng_writel(struct bcm2835_rng *brng, int offset, rt_uint32_t val) +{ + HWREG32(brng->regs + offset) = val; +} + +static rt_uint32_t bcm2835_rng_read(struct bcm2835_rng *brng, void *buf, + rt_size_t max, rt_bool_t wait) +{ + rt_uint32_t max_words = max / sizeof(rt_uint32_t), num_words; + + while ((rng_readl(brng, RNG_STATUS) >> 24) == 0) + { + if (!wait) + { + return 0; + } + + rt_thread_mdelay(1000); + } + + num_words = rng_readl(brng, RNG_STATUS) >> 24; + + if (num_words > max_words) + { + num_words = max_words; + } + + for (int count = 0; count < num_words; ++count) + { + ((rt_uint32_t *)buf)[count] = rng_readl(brng, RNG_DATA); + } + + return num_words * sizeof(rt_uint32_t); +} + +static rt_err_t bcm2835_rng_init(struct bcm2835_rng *brng) +{ + rt_err_t err; + rt_uint32_t val; + + if ((err = rt_clk_prepare_enable(brng->clk))) + { + LOG_E("CLK enable error = %s", rt_strerror(err)); + return err; + } + + if ((err = rt_reset_control_reset(brng->rstc))) + { + LOG_E("Reset error = %s", rt_strerror(err)); + return err; + } + + if (brng->mask_interrupts) + { + /* mask the interrupt */ + val = rng_readl(brng, RNG_INT_MASK); + val |= RNG_INT_OFF; + rng_writel(brng, RNG_INT_MASK, val); + } + + /* set warm-up count & enable */ + rng_writel(brng, RNG_STATUS, RNG_WARMUP_COUNT); + rng_writel(brng, RNG_CTRL, RNG_RBGEN); + + return RT_EOK; +} + +static void bcm2835_rng_cleanup(struct bcm2835_rng *brng) +{ + /* Disable rng hardware */ + rng_writel(brng, RNG_CTRL, 0); + + rt_clk_disable_unprepare(brng->clk); +} + +static rt_uint32_t bcm2835_rng_rand(struct hwcrypto_rng *ctx) +{ + rt_size_t size; + rt_uint32_t rand; + struct bcm2835_rng *brng = raw_to_bcm2835_rng(ctx->parent.device); + + size = bcm2835_rng_read(brng, &rand, sizeof(rand), RT_TRUE); + + if (rt_unlikely(size != sizeof(rand))) + { + return 0; + } + + return rand; +} + +static const struct hwcrypto_rng_ops rng_ops = +{ + .update = bcm2835_rng_rand, +}; + +static rt_err_t bcm2835_rng_create(struct rt_hwcrypto_ctx *ctx) +{ + rt_err_t res = RT_EOK; + struct hwcrypto_rng *rng; + + switch (ctx->type & HWCRYPTO_MAIN_TYPE_MASK) + { + case HWCRYPTO_TYPE_RNG: + ctx->contex = RT_NULL; + + rng = rt_container_of(ctx, struct hwcrypto_rng, parent); + rng->ops = &rng_ops; + break; + + default: + res = -RT_ENOSYS; + break; + } + + return res; +} + +static void bcm2835_rng_destroy(struct rt_hwcrypto_ctx *ctx) +{ + struct bcm2835_rng *brng = raw_to_bcm2835_rng(ctx->device); + + bcm2835_rng_cleanup(brng); + + rt_free(ctx->contex); +} + +static rt_err_t bcm2835_rng_copy(struct rt_hwcrypto_ctx *des, const struct rt_hwcrypto_ctx *src) +{ + rt_err_t err = RT_EOK; + + switch (src->type & HWCRYPTO_MAIN_TYPE_MASK) + { + case HWCRYPTO_TYPE_RNG: + break; + default: + err = -RT_ENOSYS; + break; + } + + return err; +} + +static void bcm2835_rng_reset(struct rt_hwcrypto_ctx *ctx) +{ + struct bcm2835_rng *brng = raw_to_bcm2835_rng(ctx->device); + + bcm2835_rng_init(brng); +} + +static const struct rt_hwcrypto_ops bcm2835_rng_ops = +{ + .create = bcm2835_rng_create, + .destroy = bcm2835_rng_destroy, + .copy = bcm2835_rng_copy, + .reset = bcm2835_rng_reset, +}; + +static void bcm2835_rng_free(struct bcm2835_rng *brng) +{ + if (!brng->regs) + { + rt_iounmap(brng->regs); + } + + if (!rt_is_err_or_null(brng->rstc)) + { + rt_reset_control_assert(brng->rstc); + rt_reset_control_put(brng->rstc); + } + + if (!rt_is_err_or_null(brng->clk)) + { + rt_clk_disable_unprepare(brng->clk); + rt_clk_put(brng->clk); + } + + rt_free(brng); +} + +static rt_err_t bcm2835_rng_probe(struct rt_platform_device *pdev) +{ + rt_err_t err = RT_EOK; + struct rt_device *dev = &pdev->parent; + const struct bcm2835_rng_soc_data *soc_data; + struct bcm2835_rng *brng = rt_calloc(1, sizeof(*brng)); + + if (!brng) + { + return -RT_ENOMEM; + } + + brng->regs = rt_dm_dev_iomap(dev, 0); + + if (!brng->regs) + { + err = -RT_EIO; + goto _fail; + } + + brng->clk = rt_clk_get_by_index(dev, 0); + + if (rt_is_err(brng->clk)) + { + err = rt_ptr_err(brng->clk); + goto _fail; + } + + brng->rstc = rt_reset_control_get_by_index(dev, 0); + + if (rt_is_err(brng->rstc)) + { + err = rt_ptr_err(brng->rstc); + goto _fail; + } + + if ((soc_data = pdev->id->data)) + { + brng->mask_interrupts = soc_data->mask_interrupts; + } + + dev->user_data = brng; + + brng->parent.ops = &bcm2835_rng_ops; + + if ((err = rt_hwcrypto_register(&brng->parent, "hwrng"))) + { + goto _fail; + } + + bcm2835_rng_init(brng); + + return RT_EOK; + +_fail: + bcm2835_rng_free(brng); + + return err; +} + +static rt_err_t bcm2835_rng_remove(struct rt_platform_device *pdev) +{ + struct bcm2835_rng *brng = pdev->parent.user_data; + + bcm2835_rng_cleanup(brng); + + rt_device_unregister(&brng->parent.parent); + + bcm2835_rng_free(brng); + + return RT_EOK; +} + +static const struct bcm2835_rng_soc_data nsp_rng_data = +{ + .mask_interrupts = RT_TRUE, +}; + +static const struct rt_ofw_node_id bcm2835_rng_ofw_ids[] = +{ + { .compatible = "brcm,bcm2835-rng" }, + { .compatible = "brcm,bcm-nsp-rng", .data = &nsp_rng_data }, + { .compatible = "brcm,bcm5301x-rng", .data = &nsp_rng_data }, + { .compatible = "brcm,bcm6368-rng" }, + { /* sentinel */ } +}; + +static struct rt_platform_driver bcm2835_rng_driver = +{ + .name = "bcm2835-rng", + .ids = bcm2835_rng_ofw_ids, + + .probe = bcm2835_rng_probe, + .remove = bcm2835_rng_remove, +}; +RT_PLATFORM_DRIVER_EXPORT(bcm2835_rng_driver); diff --git a/components/drivers/hwcrypto/hw-rng-iproc-rng200.c b/components/drivers/hwcrypto/hw-rng-iproc-rng200.c new file mode 100644 index 000000000000..82dfbb95bb25 --- /dev/null +++ b/components/drivers/hwcrypto/hw-rng-iproc-rng200.c @@ -0,0 +1,326 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-3-08 GuEe-GUI the first version + */ + +#include +#include +#include + +#define DBG_TAG "rng.bcm2835" +#define DBG_LVL DBG_INFO +#include + +#define RNG_CTRL_OFFSET 0x00 +#define RNG_CTRL_RNG_RBGEN_MASK 0x00001fff +#define RNG_CTRL_RNG_RBGEN_ENABLE 0x00000001 + +#define RNG_SOFT_RESET_OFFSET 0x04 +#define RNG_SOFT_RESET 0x00000001 + +#define RBG_SOFT_RESET_OFFSET 0x08 +#define RBG_SOFT_RESET 0x00000001 + +#define RNG_INT_STATUS_OFFSET 0x18 +#define RNG_INT_STATUS_MASTER_FAIL_LOCKOUT_IRQ_MASK 0x80000000 +#define RNG_INT_STATUS_STARTUP_TRANSITIONS_MET_IRQ_MASK 0x00020000 +#define RNG_INT_STATUS_NIST_FAIL_IRQ_MASK 0x00000020 +#define RNG_INT_STATUS_TOTAL_BITS_COUNT_IRQ_MASK 0x00000001 + +#define RNG_FIFO_DATA_OFFSET 0x20 + +#define RNG_FIFO_COUNT_OFFSET 0x24 +#define RNG_FIFO_COUNT_RNG_FIFO_COUNT_MASK 0x000000ff + +#define MAX_RESETS_PER_READ 1 +#define MAX_IDLE_TIME (1 * 100) + +struct iproc_rng200 +{ + struct rt_hwcrypto_device parent; + + void *regs; +}; + +#define raw_to_iproc_rng200(raw) rt_container_of(raw, struct iproc_rng200, parent) + +static void iproc_rng200_enable_set(struct iproc_rng200 *rng200, rt_bool_t enable) +{ + rt_uint32_t val; + + val = HWREG32(rng200->regs + RNG_CTRL_OFFSET); + val &= ~RNG_CTRL_RNG_RBGEN_MASK; + + if (enable) + { + val |= RNG_CTRL_RNG_RBGEN_ENABLE; + } + + HWREG32(rng200->regs + RNG_CTRL_OFFSET) = val; +} + +static void iproc_rng200_restart(struct iproc_rng200 *rng200) +{ + rt_uint32_t val; + void *regs = rng200->regs; + + iproc_rng200_enable_set(rng200, RT_FALSE); + + /* Clear all interrupt status */ + HWREG32(regs + RNG_INT_STATUS_OFFSET) = 0xffffffffUL; + + /* Reset RNG and RBG */ + val = HWREG32(regs + RBG_SOFT_RESET_OFFSET); + val |= RBG_SOFT_RESET; + HWREG32(regs + RBG_SOFT_RESET_OFFSET) = val; + + val = HWREG32(regs + RNG_SOFT_RESET_OFFSET); + val |= RNG_SOFT_RESET; + HWREG32(regs + RNG_SOFT_RESET_OFFSET) = val; + + val = HWREG32(regs + RNG_SOFT_RESET_OFFSET); + val &= ~RNG_SOFT_RESET; + HWREG32(regs + RNG_SOFT_RESET_OFFSET) = val; + + val = HWREG32(regs + RBG_SOFT_RESET_OFFSET); + val &= ~RBG_SOFT_RESET; + HWREG32(regs + RBG_SOFT_RESET_OFFSET) = val; + + iproc_rng200_enable_set(rng200, RT_TRUE); +} + +static rt_uint32_t iproc_rng200_read(struct iproc_rng200 *rng200, void *buf, + rt_size_t max, rt_bool_t wait) +{ + rt_uint32_t num_remaining = max, status, num_resets = 0; + + while (num_remaining > 0) + { + rt_thread_mdelay(MAX_IDLE_TIME >> 1); + + /* Is RNG sane? If not, reset it. */ + status = HWREG32(rng200->regs + RNG_INT_STATUS_OFFSET); + if ((status & (RNG_INT_STATUS_MASTER_FAIL_LOCKOUT_IRQ_MASK | + RNG_INT_STATUS_NIST_FAIL_IRQ_MASK)) != 0) + { + if (num_resets >= MAX_RESETS_PER_READ) + { + return max - num_remaining; + } + + iproc_rng200_restart(rng200); + ++num_resets; + } + + /* Are there any random numbers available? */ + if ((HWREG32(rng200->regs + RNG_FIFO_COUNT_OFFSET) & + RNG_FIFO_COUNT_RNG_FIFO_COUNT_MASK) > 0) + { + if (num_remaining >= sizeof(rt_uint32_t)) + { + /* Buffer has room to store entire word */ + *(rt_uint32_t *)buf = HWREG32(rng200->regs + RNG_FIFO_DATA_OFFSET); + buf += sizeof(rt_uint32_t); + num_remaining -= sizeof(rt_uint32_t); + } + else + { + /* Buffer can only store partial word */ + rt_uint32_t rnd_number = HWREG32(rng200->regs + RNG_FIFO_DATA_OFFSET); + + rt_memcpy(buf, &rnd_number, num_remaining); + buf += num_remaining; + num_remaining = 0; + } + } + else + { + if (!wait) + { + /* Cannot wait, return immediately */ + return max - num_remaining; + } + + /* Can wait, give others chance to run */ + rt_hw_us_delay((rt_min(num_remaining * 10, 500U)) >> 1); + } + } + + return max - num_remaining; +} + +static rt_err_t iproc_rng200_init(struct iproc_rng200 *rng200) +{ + iproc_rng200_enable_set(rng200, RT_TRUE); + + return RT_EOK; +} + +static void iproc_rng200_cleanup(struct iproc_rng200 *rng200) +{ + iproc_rng200_enable_set(rng200, RT_FALSE); +} + +static rt_uint32_t iproc_rng200_rand(struct hwcrypto_rng *ctx) +{ + rt_size_t size; + rt_uint32_t rand; + struct iproc_rng200 *rng200 = raw_to_iproc_rng200(ctx->parent.device); + + size = iproc_rng200_read(rng200, &rand, sizeof(rand), RT_TRUE); + + if (rt_unlikely(size != sizeof(rand))) + { + return 0; + } + + return rand; +} + +static const struct hwcrypto_rng_ops rng_ops = +{ + .update = iproc_rng200_rand, +}; + +static rt_err_t iproc_rng200_create(struct rt_hwcrypto_ctx *ctx) +{ + rt_err_t res = RT_EOK; + struct hwcrypto_rng *rng; + + switch (ctx->type & HWCRYPTO_MAIN_TYPE_MASK) + { + case HWCRYPTO_TYPE_RNG: + ctx->contex = RT_NULL; + + rng = rt_container_of(ctx, struct hwcrypto_rng, parent); + rng->ops = &rng_ops; + break; + + default: + res = -RT_ENOSYS; + break; + } + + return res; +} + +static void iproc_rng200_destroy(struct rt_hwcrypto_ctx *ctx) +{ + struct iproc_rng200 *rng200 = raw_to_iproc_rng200(ctx->device); + + iproc_rng200_cleanup(rng200); + + rt_free(ctx->contex); +} + +static rt_err_t iproc_rng200_copy(struct rt_hwcrypto_ctx *des, const struct rt_hwcrypto_ctx *src) +{ + rt_err_t err = RT_EOK; + + switch (src->type & HWCRYPTO_MAIN_TYPE_MASK) + { + case HWCRYPTO_TYPE_RNG: + break; + default: + err = -RT_ENOSYS; + break; + } + + return err; +} + +static void iproc_rng200_reset(struct rt_hwcrypto_ctx *ctx) +{ + struct iproc_rng200 *rng200 = raw_to_iproc_rng200(ctx->device); + + iproc_rng200_init(rng200); +} + +static const struct rt_hwcrypto_ops iproc_rng200_ops = +{ + .create = iproc_rng200_create, + .destroy = iproc_rng200_destroy, + .copy = iproc_rng200_copy, + .reset = iproc_rng200_reset, +}; + +static rt_err_t iproc_rng200_probe(struct rt_platform_device *pdev) +{ + rt_err_t err; + struct rt_device *dev = &pdev->parent; + struct iproc_rng200 *rng200 = rt_calloc(1, sizeof(*rng200)); + + if (!rng200) + { + return -RT_ENOMEM; + } + + rng200->regs = rt_dm_dev_iomap(dev, 0); + + if (!rng200->regs) + { + err = -RT_EIO; + goto _fail; + } + + dev->user_data = rng200; + + rng200->parent.ops = &iproc_rng200_ops; + + if ((err = rt_hwcrypto_register(&rng200->parent, "hwrng"))) + { + goto _fail; + } + + iproc_rng200_init(rng200); + + return RT_EOK; + +_fail: + if (rng200->regs) + { + rt_iounmap(rng200->regs); + } + + rt_free(rng200); + + return err; +} + +static rt_err_t iproc_rng200_remove(struct rt_platform_device *pdev) +{ + struct iproc_rng200 *rng200 = pdev->parent.user_data; + + iproc_rng200_cleanup(rng200); + + rt_device_unregister(&rng200->parent.parent); + + rt_iounmap(rng200->regs); + rt_free(rng200); + + return RT_EOK; +} + +static const struct rt_ofw_node_id iproc_rng200_ofw_ids[] = +{ + { .compatible = "brcm,bcm2711-rng200", }, + { .compatible = "brcm,bcm7211-rng200", }, + { .compatible = "brcm,bcm7278-rng200", }, + { .compatible = "brcm,iproc-rng200", }, + { /* sentinel */ } +}; + +static struct rt_platform_driver iproc_rng200_driver = +{ + .name = "iproc-rng200", + .ids = iproc_rng200_ofw_ids, + + .probe = iproc_rng200_probe, + .remove = iproc_rng200_remove, +}; +RT_PLATFORM_DRIVER_EXPORT(iproc_rng200_driver); diff --git a/components/drivers/hwcrypto/hw-rng-rockchip.c b/components/drivers/hwcrypto/hw-rng-rockchip.c new file mode 100644 index 000000000000..a29efefb7b49 --- /dev/null +++ b/components/drivers/hwcrypto/hw-rng-rockchip.c @@ -0,0 +1,565 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-3-08 GuEe-GUI the first version + */ + +#include +#include +#include + +#define DBG_TAG "rng.rockchip" +#define DBG_LVL DBG_INFO +#include + +#include "../../soc/rockchip/rockchip.h" + +#define ROCKCHIP_AUTOSUSPEND_DELAY 100 +#define ROCKCHIP_POLL_PERIOD_US 100 +#define ROCKCHIP_POLL_TIMEOUT_US 50000 +#define RK_MAX_RNG_BYTE (32) + +/* start of CRYPTO V1 register define */ +#define CRYPTO_V1_CTRL 0x0008 +#define CRYPTO_V1_RNG_START RT_BIT(8) +#define CRYPTO_V1_RNG_FLUSH RT_BIT(9) + +#define CRYPTO_V1_TRNG_CTRL 0x0200 +#define CRYPTO_V1_OSC_ENABLE RT_BIT(16) +#define CRYPTO_V1_TRNG_SAMPLE_PERIOD(x) (x) + +#define CRYPTO_V1_TRNG_DOUT_0 0x0204 +/* end of CRYPTO V1 register define */ + +/* start of CRYPTO V2 register define */ +#define CRYPTO_V2_RNG_DEFAULT_OFFSET 0x0400 +#define CRYPTO_V2_RNG_CTL 0x0 +#define CRYPTO_V2_RNG_64_BIT_LEN ((0x00) << (4)) +#define CRYPTO_V2_RNG_128_BIT_LEN ((0x01) << (4)) +#define CRYPTO_V2_RNG_192_BIT_LEN ((0x02) << (4)) +#define CRYPTO_V2_RNG_256_BIT_LEN ((0x03) << (4)) +#define CRYPTO_V2_RNG_FATESY_SOC_RING ((0x00) << (2)) +#define CRYPTO_V2_RNG_SLOWER_SOC_RING_0 ((0x01) << (2)) +#define CRYPTO_V2_RNG_SLOWER_SOC_RING_1 ((0x02) << (2)) +#define CRYPTO_V2_RNG_SLOWEST_SOC_RING ((0x03) << (2)) +#define CRYPTO_V2_RNG_ENABLE RT_BIT(1) +#define CRYPTO_V2_RNG_START RT_BIT(0) +#define CRYPTO_V2_RNG_SAMPLE_CNT 0x0004 +#define CRYPTO_V2_RNG_DOUT_0 0x0010 +/* end of CRYPTO V2 register define */ + +/* start of TRNG_V1 register define */ +/* TRNG is no longer subordinate to the Crypto module */ +#define TRNG_V1_CTRL 0x0000 +#define TRNG_V1_CTRL_NOP ((0x00) << (0)) +#define TRNG_V1_CTRL_RAND ((0x01) << (0)) +#define TRNG_V1_CTRL_SEED ((0x02) << (0)) + +#define TRNG_V1_STAT 0x0004 +#define TRNG_V1_STAT_SEEDED RT_BIT(9) +#define TRNG_V1_STAT_GENERATING RT_BIT(30) +#define TRNG_V1_STAT_RESEEDING RT_BIT(31) + +#define TRNG_V1_MODE 0x0008 +#define TRNG_V1_MODE_128_BIT ((0x00) << (3)) +#define TRNG_V1_MODE_256_BIT ((0x01) << (3)) + +#define TRNG_V1_IE 0x0010 +#define TRNG_V1_IE_GLBL_EN RT_BIT(31) +#define TRNG_V1_IE_SEED_DONE_EN RT_BIT(1) +#define TRNG_V1_IE_RAND_RDY_EN RT_BIT(0) + +#define TRNG_V1_ISTAT 0x0014 +#define TRNG_V1_ISTAT_RAND_RDY RT_BIT(0) + +/* RAND0 ~ RAND7 */ +#define TRNG_V1_RAND0 0x0020 +#define TRNG_V1_RAND7 0x003C + +#define TRNG_V1_AUTO_RQSTS 0x0060 + +#define TRNG_V1_VERSION 0x00F0 +#define TRNG_v1_VERSION_CODE 0x46bc +/* end of TRNG_V1 register define */ + +struct rockchip_rng; + +struct rockchip_rng_soc_data +{ + rt_uint32_t default_offset; + + rt_err_t (*init)(struct rockchip_rng *rk_rng); + rt_uint32_t (*read)(struct rockchip_rng *rk_rng, void *buf, rt_size_t max, rt_bool_t wait); +}; + +struct rockchip_rng +{ + struct rt_hwcrypto_device parent; + + void *regs; + + struct rt_clk_array *clk_arr; + struct rt_reset_control *rstc; + + const struct rockchip_rng_soc_data *soc_data; +}; + +#define raw_to_rockchip_rng(raw) rt_container_of(raw, struct rockchip_rng, parent) + +static void rockchip_rng_writel(struct rockchip_rng *rk_rng, rt_uint32_t val, int offset) +{ + HWREG32(rk_rng->regs + offset) = val; +} + +static rt_uint32_t rockchip_rng_readl(struct rockchip_rng *rk_rng, int offset) +{ + return HWREG32(rk_rng->regs + offset); +} + +static void rockchip_rng_read_regs(struct rockchip_rng *rk_rng, rt_uint32_t offset, + void *buf, rt_size_t size) +{ + rt_uint32_t *data = buf; + + for (int i = 0; i < size; i += 4, ++offset) + { + data[i] = rt_be32_to_cpu(rockchip_rng_readl(rk_rng, offset)); + } +} + +static rt_uint32_t rockchip_crypto_v1_read(struct rockchip_rng *rk_rng, void *buf, + rt_size_t max, rt_bool_t wait) +{ + rt_tick_t start; + rt_uint32_t res = 0, reg_ctrl = 0; + int timeout = rt_tick_from_millisecond(ROCKCHIP_POLL_TIMEOUT_US / 1000); + + /* enable osc_ring to get entropy, sample period is set as 100 */ + reg_ctrl = CRYPTO_V1_OSC_ENABLE | CRYPTO_V1_TRNG_SAMPLE_PERIOD(100); + rockchip_rng_writel(rk_rng, reg_ctrl, CRYPTO_V1_TRNG_CTRL); + + reg_ctrl = HIWORD_UPDATE(CRYPTO_V1_RNG_START, CRYPTO_V1_RNG_START, 0); + + rockchip_rng_writel(rk_rng, reg_ctrl, CRYPTO_V1_CTRL); + + start = rt_tick_get(); + + while (rockchip_rng_readl(rk_rng, CRYPTO_V1_CTRL) & CRYPTO_V1_RNG_START) + { + rt_hw_us_delay(ROCKCHIP_POLL_PERIOD_US); + + if ((rt_tick_get() - start) > timeout) + { + goto _time_out; + } + + rt_hw_cpu_relax(); + } + + res = rt_min_t(rt_size_t, max, RK_MAX_RNG_BYTE); + + rockchip_rng_read_regs(rk_rng, CRYPTO_V1_TRNG_DOUT_0, buf, res); + +_time_out: + /* close TRNG */ + rockchip_rng_writel(rk_rng, HIWORD_UPDATE(0, CRYPTO_V1_RNG_START, 0), CRYPTO_V1_CTRL); + + return res; +} + +static rt_uint32_t rockchip_crypto_v2_read(struct rockchip_rng *rk_rng, void *buf, + rt_size_t max, rt_bool_t wait) +{ + rt_tick_t start; + rt_uint32_t res = 0, reg_ctrl = 0; + int timeout = rt_tick_from_millisecond(ROCKCHIP_POLL_TIMEOUT_US / 1000); + + /* enable osc_ring to get entropy, sample period is set as 100 */ + rockchip_rng_writel(rk_rng, 100, CRYPTO_V2_RNG_SAMPLE_CNT); + + reg_ctrl |= CRYPTO_V2_RNG_256_BIT_LEN; + reg_ctrl |= CRYPTO_V2_RNG_SLOWER_SOC_RING_0; + reg_ctrl |= CRYPTO_V2_RNG_ENABLE; + reg_ctrl |= CRYPTO_V2_RNG_START; + + rockchip_rng_writel(rk_rng, HIWORD_UPDATE(reg_ctrl, 0xffff, 0), CRYPTO_V2_RNG_CTL); + + start = rt_tick_get(); + + while (rockchip_rng_readl(rk_rng, CRYPTO_V2_RNG_CTL) & CRYPTO_V2_RNG_START) + { + rt_hw_us_delay(ROCKCHIP_POLL_PERIOD_US); + + if ((rt_tick_get() - start) > timeout) + { + goto _time_out; + } + + rt_hw_cpu_relax(); + } + + res = rt_min_t(rt_size_t, max, RK_MAX_RNG_BYTE); + + rockchip_rng_read_regs(rk_rng, CRYPTO_V2_RNG_DOUT_0, buf, res); + +_time_out: + /* close TRNG */ + rockchip_rng_writel(rk_rng, HIWORD_UPDATE(0, 0xffff, 0), CRYPTO_V2_RNG_CTL); + + return res; +} + +static rt_err_t rockchip_trng_v1_init(struct rockchip_rng *rk_rng) +{ + rt_uint32_t auto_reseed_cnt = 1000; + rt_uint32_t reg_ctrl, status, version; + + version = rockchip_rng_readl(rk_rng, TRNG_V1_VERSION); + + if (version != TRNG_v1_VERSION_CODE) + { + LOG_E("Wrong trng version, expected = %08x, actual = %08x", + TRNG_V1_VERSION, version); + + return -RT_EIO; + } + + status = rockchip_rng_readl(rk_rng, TRNG_V1_STAT); + + /* TRNG should wait RAND_RDY triggered if it is busy or not seeded */ + if (!(status & TRNG_V1_STAT_SEEDED) || (status & TRNG_V1_STAT_GENERATING) || + (status & TRNG_V1_STAT_RESEEDING)) + { + rt_tick_t start; + rt_uint32_t mask = TRNG_V1_STAT_SEEDED | TRNG_V1_STAT_GENERATING | + TRNG_V1_STAT_RESEEDING; + int timeout = rt_tick_from_millisecond(ROCKCHIP_POLL_TIMEOUT_US / 1000); + + rt_hw_us_delay(10); + + /* wait for GENERATING and RESEEDING flag to clear */ + start = rt_tick_get(); + + while ((rockchip_rng_readl(rk_rng, TRNG_V1_STAT) & mask) != TRNG_V1_STAT_SEEDED) + { + rt_hw_us_delay(ROCKCHIP_POLL_PERIOD_US); + + if ((rt_tick_get() - start) > timeout) + { + break; + } + + rt_hw_cpu_relax(); + } + } + + /* clear ISTAT flag because trng may auto reseeding when power on */ + reg_ctrl = rockchip_rng_readl(rk_rng, TRNG_V1_ISTAT); + rockchip_rng_writel(rk_rng, reg_ctrl, TRNG_V1_ISTAT); + + /* auto reseed after (auto_reseed_cnt * 16) byte rand generate */ + rockchip_rng_writel(rk_rng, auto_reseed_cnt, TRNG_V1_AUTO_RQSTS); + + return RT_EOK; +} + +static rt_uint32_t rockchip_trng_v1_read(struct rockchip_rng *rk_rng, void *buf, + rt_size_t max, rt_bool_t wait) +{ + rt_uint32_t res = 0, reg_ctrl = 0; + + /* clear ISTAT anyway */ + reg_ctrl = rockchip_rng_readl(rk_rng, TRNG_V1_ISTAT); + rockchip_rng_writel(rk_rng, reg_ctrl, TRNG_V1_ISTAT); + + /* generate 256bit random */ + rockchip_rng_writel(rk_rng, TRNG_V1_MODE_256_BIT, TRNG_V1_MODE); + rockchip_rng_writel(rk_rng, TRNG_V1_CTRL_RAND, TRNG_V1_CTRL); + + /* + * Generate2 56 bit random data will cost 1024 clock cycles. + * Estimated at 150M RNG module frequency, it takes 6.7 microseconds. + */ + rt_hw_us_delay(10); + reg_ctrl = rockchip_rng_readl(rk_rng, TRNG_V1_ISTAT); + + if (!(reg_ctrl & TRNG_V1_ISTAT_RAND_RDY)) + { + rt_tick_t start = rt_tick_get(); + int timeout = rt_tick_from_millisecond(ROCKCHIP_POLL_TIMEOUT_US / 1000); + + /* wait RAND_RDY triggered */ + while (!(rockchip_rng_readl(rk_rng, TRNG_V1_ISTAT) & TRNG_V1_ISTAT_RAND_RDY)) + { + rt_hw_us_delay(ROCKCHIP_POLL_PERIOD_US); + + if ((rt_tick_get() - start) > timeout) + { + goto _time_out; + } + + rt_hw_cpu_relax(); + } + } + + res = rt_min_t(rt_size_t, max, RK_MAX_RNG_BYTE); + + rockchip_rng_read_regs(rk_rng, TRNG_V1_RAND0, buf, res); + + /* clear all status flag */ + rockchip_rng_writel(rk_rng, reg_ctrl, TRNG_V1_ISTAT); +_time_out: + /* close TRNG */ + rockchip_rng_writel(rk_rng, TRNG_V1_CTRL_NOP, TRNG_V1_CTRL); + + return res; +} + +static rt_uint32_t rockchip_rng_read(struct rockchip_rng *rk_rng, void *buf, + rt_size_t max, rt_bool_t wait) +{ + rt_uint32_t res; + int read_len = 0; + + if (!rk_rng->soc_data->read) + { + return 0; + } + + res = 0; + + while (max > res) + { + read_len = rk_rng->soc_data->read(rk_rng, buf + res, max - res, wait); + + if (read_len < 0) + { + res = read_len; + + break; + } + res += read_len; + } + + return res; +} + +static rt_uint32_t rockchip_rng_rand(struct hwcrypto_rng *ctx) +{ + rt_uint32_t rand; + struct rockchip_rng *rk_rng = raw_to_rockchip_rng(ctx->parent.device); + + if (rt_unlikely(rockchip_rng_read(rk_rng, &rand, sizeof(rand), RT_TRUE) != sizeof(rand))) + { + return 0; + } + + return rand; +} + +static const struct hwcrypto_rng_ops rng_ops = +{ + .update = rockchip_rng_rand, +}; + +static rt_err_t rockchip_rng_create(struct rt_hwcrypto_ctx *ctx) +{ + rt_err_t res = RT_EOK; + struct hwcrypto_rng *rng; + + switch (ctx->type & HWCRYPTO_MAIN_TYPE_MASK) + { + case HWCRYPTO_TYPE_RNG: + ctx->contex = RT_NULL; + + rng = rt_container_of(ctx, struct hwcrypto_rng, parent); + rng->ops = &rng_ops; + break; + + default: + res = -RT_ENOSYS; + break; + } + + return res; +} + +static void rockchip_rng_destroy(struct rt_hwcrypto_ctx *ctx) +{ + rt_free(ctx->contex); +} + +static rt_err_t rockchip_rng_copy(struct rt_hwcrypto_ctx *des, const struct rt_hwcrypto_ctx *src) +{ + rt_err_t err = RT_EOK; + + switch (src->type & HWCRYPTO_MAIN_TYPE_MASK) + { + case HWCRYPTO_TYPE_RNG: + break; + default: + err = -RT_ENOSYS; + break; + } + + return err; +} + +static void rockchip_rng_reset(struct rt_hwcrypto_ctx *ctx) +{ +} + +static const struct rt_hwcrypto_ops rockchip_rng_ops = +{ + .create = rockchip_rng_create, + .destroy = rockchip_rng_destroy, + .copy = rockchip_rng_copy, + .reset = rockchip_rng_reset, +}; + +static void rockchip_rng_free(struct rockchip_rng *rk_rng) +{ + if (!rk_rng->regs) + { + rt_iounmap(rk_rng->regs); + } + + if (!rt_is_err_or_null(rk_rng->rstc)) + { + rt_reset_control_assert(rk_rng->rstc); + rt_reset_control_put(rk_rng->rstc); + } + + if (!rt_is_err_or_null(rk_rng->clk_arr)) + { + rt_clk_array_disable_unprepare(rk_rng->clk_arr); + rt_clk_array_put(rk_rng->clk_arr); + } + + rt_free(rk_rng); +} + +static rt_err_t rockchip_rng_probe(struct rt_platform_device *pdev) +{ + rt_err_t err = RT_EOK; + struct rt_device *dev = &pdev->parent; + struct rockchip_rng *rk_rng = rt_calloc(1, sizeof(*rk_rng)); + + if (!rk_rng) + { + return -RT_ENOMEM; + } + + rk_rng->soc_data = pdev->id->data; + + rk_rng->regs = rt_dm_dev_iomap(dev, 0); + + if (!rk_rng->regs) + { + err = -RT_EIO; + goto _fail; + } + + rk_rng->rstc = rt_reset_control_get_by_name(dev, "reset"); + + if (rt_is_err(rk_rng->rstc)) + { + err = rt_ptr_err(rk_rng->rstc); + goto _fail; + } + + if (rk_rng->rstc) + { + rt_reset_control_assert(rk_rng->rstc); + rt_hw_us_delay(10); + rt_reset_control_deassert(rk_rng->rstc); + } + + rk_rng->clk_arr = rt_clk_get_array(dev); + + if (rt_is_err(rk_rng->clk_arr)) + { + err = rt_ptr_err(rk_rng->clk_arr); + goto _fail; + } + + rt_clk_array_prepare_enable(rk_rng->clk_arr); + + if (rk_rng->soc_data->init) + { + err = rk_rng->soc_data->init(rk_rng); + } + + if (err) + { + goto _fail; + } + + dev->user_data = rk_rng; + + rk_rng->parent.ops = &rockchip_rng_ops; + + if ((err = rt_hwcrypto_register(&rk_rng->parent, "hwrng"))) + { + goto _fail; + } + + return RT_EOK; + +_fail: + rockchip_rng_free(rk_rng); + + return err; +} + +static rt_err_t rockchip_rng_remove(struct rt_platform_device *pdev) +{ + struct rockchip_rng *rk_rng = pdev->parent.user_data; + + rt_device_unregister(&rk_rng->parent.parent); + + rockchip_rng_free(rk_rng); + + return RT_EOK; +} + +static const struct rockchip_rng_soc_data rk_crypto_v1_soc_data = +{ + .default_offset = 0, + .read = rockchip_crypto_v1_read, +}; + +static const struct rockchip_rng_soc_data rk_crypto_v2_soc_data = +{ + .default_offset = CRYPTO_V2_RNG_DEFAULT_OFFSET, + .read = rockchip_crypto_v2_read, +}; + +static const struct rockchip_rng_soc_data rk_trng_v1_soc_data = +{ + .default_offset = 0, + .init = rockchip_trng_v1_init, + .read = rockchip_trng_v1_read, +}; + +static const struct rt_ofw_node_id rockchip_rng_ofw_ids[] = +{ + { .compatible = "rockchip,cryptov1-rng", .data = &rk_crypto_v1_soc_data, }, + { .compatible = "rockchip,cryptov2-rng", .data = &rk_crypto_v2_soc_data, }, + { .compatible = "rockchip,trngv1", .data = &rk_trng_v1_soc_data, }, + { /* sentinel */ } +}; + +static struct rt_platform_driver rockchip_rng_driver = +{ + .name = "rockchip-rng", + .ids = rockchip_rng_ofw_ids, + + .probe = rockchip_rng_probe, + .remove = rockchip_rng_remove, +}; +RT_PLATFORM_DRIVER_EXPORT(rockchip_rng_driver); diff --git a/components/drivers/hwspinlock/Kconfig b/components/drivers/hwspinlock/Kconfig new file mode 100644 index 000000000000..48119c4b86b9 --- /dev/null +++ b/components/drivers/hwspinlock/Kconfig @@ -0,0 +1,15 @@ +menuconfig RT_USING_HWSPINLOCK + bool "Using Hardware Spinlock device drivers" + depends on RT_USING_DM + select RT_USING_OFW + select RT_USING_ADT_REF + default n + help + Hardware spinlock modules provide hardware assistance for + synchronization and mutual exclusion between heterogeneous processors + and those not operating under a single, shared operating system. + +config RT_HWSPINLOCK_ROCKCHIP + bool "Rockchip Hardware Spinlock device" + depends on RT_USING_HWSPINLOCK + default n diff --git a/components/drivers/hwspinlock/SConscript b/components/drivers/hwspinlock/SConscript new file mode 100755 index 000000000000..dfd6370e5f74 --- /dev/null +++ b/components/drivers/hwspinlock/SConscript @@ -0,0 +1,18 @@ +from building import * + +group = [] + +if not GetDepend(['RT_USING_HWSPINLOCK']): + Return('group') + +cwd = GetCurrentDir() +CPPPATH = [cwd + '/../include'] + +src = ['hwspinlock.c'] + +if GetDepend(['RT_HWSPINLOCK_ROCKCHIP']): + src += ['hwspinlock-rockchip.c'] + +group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') diff --git a/components/drivers/hwspinlock/hwspinlock-rockchip.c b/components/drivers/hwspinlock/hwspinlock-rockchip.c new file mode 100644 index 000000000000..afaef14e1423 --- /dev/null +++ b/components/drivers/hwspinlock/hwspinlock-rockchip.c @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-09-23 GuEe-GUI first version + */ + +#include "hwspinlock_dm.h" + +#define HWSPINLOCK_NUMBER 64 +#define HWSPINLOCK_STATUS_OFFSET(x) (0x4 * (x)) +#define HWSPINLOCK_OWNER_ID 0x1 + +struct rockchip_hwspinlock +{ + struct rt_hwspinlock_bank parent; + + void *regs; +}; + +static rt_err_t rockchip_hwspinlock_trylock(struct rt_hwspinlock *hwlock) +{ + void *lock_regs = hwlock->priv; + + HWREG32(lock_regs) = HWSPINLOCK_OWNER_ID; + + /* + * Get only first 4bits and compare to HWSPINLOCK_OWNER_ID: + * when 4bits is 0, 4bits can be written with new value. + * when 4bits is not 0, 4bits cannot be written. + * when write data is 0x0000, 4bits clean to 0. + */ + + return ((HWREG32(lock_regs) & 0xf) == HWSPINLOCK_OWNER_ID); +} + +static void rockchip_hwspinlock_unlock(struct rt_hwspinlock *hwlock) +{ + void *lock_regs = hwlock->priv; + + HWREG32(lock_regs) = 0; +} + +static const struct rt_hwspinlock_ops rk_hwspinlock_ops = +{ + .trylock = rockchip_hwspinlock_trylock, + .unlock = rockchip_hwspinlock_unlock, +}; + +static rt_err_t rockchip_hwspinlock_probe(struct rt_platform_device *pdev) +{ + rt_err_t err; + struct rt_hwspinlock *hwlock; + struct rt_hwspinlock_bank *bank; + struct rt_device *dev = &pdev->parent; + struct rockchip_hwspinlock *rk_hwspinlock; + + rk_hwspinlock = hwspinlock_bank_alloc(rk_hwspinlock, HWSPINLOCK_NUMBER); + + if (!rk_hwspinlock) + { + return -RT_ENOMEM; + } + + rk_hwspinlock->regs = rt_dm_dev_iomap(dev, 0); + + if (!rk_hwspinlock->regs) + { + err = -RT_EIO; + + goto _fail; + } + + bank = &rk_hwspinlock->parent; + hwlock = &bank->locks[0]; + + for (int i = 0; i < HWSPINLOCK_NUMBER; ++i, ++hwlock) + { + hwlock->priv = rk_hwspinlock->regs + HWSPINLOCK_STATUS_OFFSET(i); + } + + bank->dev = dev; + bank->ops = &rk_hwspinlock_ops; + bank->locks_nr = HWSPINLOCK_NUMBER; + + if ((err = rt_hwspinlock_bank_register(bank))) + { + goto _fail; + } + + dev->user_data = rk_hwspinlock; + + return RT_EOK; + +_fail: + if (rk_hwspinlock->regs) + { + rt_iounmap(rk_hwspinlock->regs); + } + rt_free(rk_hwspinlock); + + return err; +} + +static rt_err_t rockchip_hwspinlock_remove(struct rt_platform_device *pdev) +{ + struct rockchip_hwspinlock *rk_hwspinlock = pdev->parent.user_data; + + rt_hwspinlock_bank_unregister(&rk_hwspinlock->parent); + + rt_iounmap(rk_hwspinlock->regs); + + rt_free(rk_hwspinlock); + + return RT_EOK; +} + +static const struct rt_ofw_node_id rockchip_hwspinlock_ofw_ids[] = +{ + { .compatible = "rockchip,hwspinlock" }, + { /* sentinel */ } +}; + +static struct rt_platform_driver rockchip_hwspinlock_driver = +{ + .name = "hwspinlock-rockchip", + .ids = rockchip_hwspinlock_ofw_ids, + + .probe = rockchip_hwspinlock_probe, + .remove = rockchip_hwspinlock_remove, +}; + +static int rockchip_hwspinlock_drv_register(void) +{ + rt_platform_driver_register(&rockchip_hwspinlock_driver); + + return 0; +} +INIT_FRAMEWORK_EXPORT(rockchip_hwspinlock_drv_register); diff --git a/components/drivers/hwspinlock/hwspinlock.c b/components/drivers/hwspinlock/hwspinlock.c new file mode 100644 index 000000000000..1cbc57a83dd3 --- /dev/null +++ b/components/drivers/hwspinlock/hwspinlock.c @@ -0,0 +1,345 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-09-23 GuEe-GUI first version + */ + +#include + +#define DBG_TAG "rtdm.hwspinlock" +#define DBG_LVL DBG_INFO +#include + +#include "hwspinlock_dm.h" + +static struct rt_spinlock hwspinlock_ops_lock = {}; +static rt_list_t hwspinlock_bank_nodes = RT_LIST_OBJECT_INIT(hwspinlock_bank_nodes); + +rt_err_t rt_hwspinlock_bank_register(struct rt_hwspinlock_bank *bank) +{ + struct rt_hwspinlock *hwlock; + + if (!bank || !bank->ops || bank->locks_nr <= 0 || !bank->dev) + { + return -RT_EINVAL; + } + + rt_list_init(&bank->list); + ref_init(&bank->ref); + + hwlock = &bank->locks[0]; + + for (int i = bank->locks_nr - 1; i >= 0; --i, ++hwlock) + { + rt_spin_lock_init(&hwlock->lock); + hwlock->used = RT_FALSE; + } + + rt_spin_lock(&hwspinlock_ops_lock); + + rt_list_insert_after(&hwspinlock_bank_nodes, &bank->list); + + rt_spin_unlock(&hwspinlock_ops_lock); + + rt_dm_dev_bind_fwdata(bank->dev, RT_NULL, bank); + + return RT_EOK; +} + +rt_err_t rt_hwspinlock_bank_unregister(struct rt_hwspinlock_bank *bank) +{ + rt_err_t err; + + if (!bank) + { + return -RT_EINVAL; + } + + rt_spin_lock(&hwspinlock_ops_lock); + + if (ref_read(&bank->ref) == 1) + { + rt_dm_dev_unbind_fwdata(bank->dev, RT_NULL); + + err = RT_EOK; + } + else + { + err = -RT_EBUSY; + } + + rt_spin_unlock(&hwspinlock_ops_lock); + + return err; +} + +rt_err_t rt_hwspin_trylock_mode(struct rt_hwspinlock *hwlock, int mode, + rt_ubase_t *out_level) +{ + rt_err_t err; + + if (rt_unlikely(!hwlock || (!out_level && mode == RT_HWSPINLOCK_IRQSTATE))) + { + return -RT_EINVAL; + } + + switch (mode) + { + case RT_HWSPINLOCK_IRQSTATE: + err = rt_spin_trylock_irqsave(&hwlock->lock, out_level); + break; + + case RT_HWSPINLOCK_RAW: + case RT_HWSPINLOCK_IN_ATOMIC: + err = RT_EOK; + break; + + default: + err = rt_spin_trylock(&hwlock->lock); + break; + } + + if (err < 0) + { + return -RT_EBUSY; + } + + err = hwlock->bank->ops->trylock(hwlock); + + if (!err) + { + switch (mode) + { + case RT_HWSPINLOCK_IRQSTATE: + rt_spin_unlock_irqrestore(&hwlock->lock, *out_level); + break; + + case RT_HWSPINLOCK_RAW: + case RT_HWSPINLOCK_IN_ATOMIC: + break; + + default: + rt_spin_unlock(&hwlock->lock); + break; + } + + return -RT_EBUSY; + } + + rt_hw_dmb(); + + return err; +} + +rt_err_t rt_hwspin_lock_timeout_mode(struct rt_hwspinlock *hwlock, int mode, + rt_uint32_t timeout_ms, rt_ubase_t *out_level) +{ + rt_err_t err; + rt_base_t us_timeout; + rt_tick_t timeout = rt_tick_get() + rt_tick_from_millisecond(timeout_ms); + + if (mode == RT_HWSPINLOCK_IN_ATOMIC) + { + us_timeout = timeout_ms * 1000; + } + + for (;;) + { + err = rt_hwspin_trylock_mode(hwlock, mode, out_level); + + if (err != -RT_EBUSY) + { + break; + } + + if (mode == RT_HWSPINLOCK_IN_ATOMIC) + { + /* timeout_ms is ms unit, so delay 1/10 ms */ + const int retry_us = 100; + + rt_hw_us_delay(retry_us); + us_timeout -= retry_us; + + if (us_timeout < 0) + { + return -RT_ETIMEOUT; + } + } + else if (timeout > rt_tick_get()) + { + return -RT_ETIMEOUT; + } + + if (hwlock->bank->ops->relax) + { + hwlock->bank->ops->relax(hwlock); + } + } + + return err; +} + +void rt_hwspin_unlock_mode(struct rt_hwspinlock *hwlock, int mode, + rt_ubase_t *out_level) +{ + if (rt_unlikely(!hwlock || (!out_level && mode == RT_HWSPINLOCK_IRQSTATE))) + { + return; + } + + rt_hw_dmb(); + + hwlock->bank->ops->unlock(hwlock); + + switch (mode) + { + case RT_HWSPINLOCK_IRQSTATE: + rt_spin_unlock_irqrestore(&hwlock->lock, *out_level); + break; + + case RT_HWSPINLOCK_RAW: + case RT_HWSPINLOCK_IN_ATOMIC: + break; + + default: + rt_spin_unlock(&hwlock->lock); + break; + } +} + +static struct rt_hwspinlock *hwspinlock_request(int id) +{ + struct rt_hwspinlock_bank *bank; + struct rt_hwspinlock *hwlock = RT_NULL; + + rt_spin_lock(&hwspinlock_ops_lock); + + rt_list_for_each_entry(bank, &hwspinlock_bank_nodes, list) + { + if (id <= 0) + { + for (int i = 0; i < bank->locks_nr; ++i) + { + if (!bank->locks[i].used) + { + hwlock = &bank->locks[i]; + break; + } + } + } + else if (id >= bank->base_id && id <= bank->base_id + bank->locks_nr) + { + int offset = id - bank->base_id; + + if (!bank->locks[offset].used) + { + hwlock = &bank->locks[offset]; + break; + } + } + } + + if (hwlock) + { + hwlock->used = RT_TRUE; + } + + rt_spin_unlock(&hwspinlock_ops_lock); + + return hwlock; +} + +struct rt_hwspinlock *rt_hwspinlock_request(void) +{ + return hwspinlock_request(-1); +} + +struct rt_hwspinlock *rt_hwspinlock_request_id(int id) +{ + return hwspinlock_request(id); +} + +static void hwspinlock_release(struct ref *r) +{ + struct rt_hwspinlock_bank *bank = rt_container_of(r, struct rt_hwspinlock_bank, ref); + + LOG_E("%s is release", rt_dm_dev_get_name(bank->dev)); + (void)bank; + + RT_ASSERT(0); +} + +rt_err_t rt_hwspinlock_free(struct rt_hwspinlock *hwlock) +{ + if (!hwlock) + { + return -RT_EINVAL; + } + + ref_put(&hwlock->bank->ref, &hwspinlock_release); + hwlock->used = RT_TRUE; + + return RT_EOK; +} + +int rt_ofw_get_hwspinlock_id(struct rt_ofw_node *np, int index) +{ + int id; + rt_err_t err; + struct rt_ofw_cell_args args; + struct rt_hwspinlock_bank *bank; + + if (rt_unlikely(!np && index < 0)) + { + return -RT_EINVAL; + } + + rt_spin_lock(&hwspinlock_ops_lock); + + err = rt_ofw_parse_phandle_cells(np, "hwlocks", "#hwlock-cells", index, &args); + + if (rt_unlikely(err)) + { + goto _out_lock; + } + + bank = rt_ofw_data(args.data); + rt_ofw_node_put(args.data); + + if (rt_unlikely(!bank || args.args_count != 1)) + { + err = -RT_ENOSYS; + } + else + { + id = bank->base_id + args.args[0]; + } + +_out_lock: + rt_spin_unlock(&hwspinlock_ops_lock); + + return err < 0 ? err : id; +} + +int rt_ofw_get_hwspinlock_id_byname(struct rt_ofw_node *np, const char *name) +{ + int index; + + if (rt_unlikely(!np && !name)) + { + return -RT_EINVAL; + } + + index = rt_ofw_prop_index_of_string(np, "hwlock-names", name); + + if (rt_unlikely(index < 0)) + { + return index; + } + + return rt_ofw_get_hwspinlock_id(np, index); +} diff --git a/components/drivers/hwspinlock/hwspinlock_dm.h b/components/drivers/hwspinlock/hwspinlock_dm.h new file mode 100644 index 000000000000..7602a92c226d --- /dev/null +++ b/components/drivers/hwspinlock/hwspinlock_dm.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-09-23 GuEe-GUI first version + */ + +#ifndef __HWSPINLOCK_DM_H__ +#define __HWSPINLOCK_DM_H__ + +#include +#include +#include + +struct rt_hwspinlock +{ + struct rt_hwspinlock_bank *bank; + struct rt_spinlock lock; + + rt_bool_t used; + void *priv; +}; + +struct rt_hwspinlock_ops +{ + rt_err_t (*trylock)(struct rt_hwspinlock *hwlock); + void (*unlock)(struct rt_hwspinlock *hwlock); + void (*relax)(struct rt_hwspinlock *hwlock); +}; + +struct rt_hwspinlock_bank +{ + rt_list_t list; + struct ref ref; + + struct rt_device *dev; + const struct rt_hwspinlock_ops *ops; + + int base_id; + rt_size_t locks_nr; + struct rt_hwspinlock locks[]; +}; + +#define hwspinlock_bank_alloc(obj, locks_nr) \ + rt_calloc(1, sizeof(typeof(*obj)) + sizeof(struct rt_hwspinlock) * (locks_nr)) + +rt_inline int hwspinlock_find_id(struct rt_hwspinlock *hwlock) +{ + int idx_offset = hwlock - &hwlock->bank->locks[0]; + + return hwlock->bank->base_id + idx_offset; +} + +#endif /* __HWSPINLOCK_DM_H__ */ diff --git a/components/drivers/hwtimer/Kconfig b/components/drivers/hwtimer/Kconfig index 624b05f6f76a..811cf27afb51 100755 --- a/components/drivers/hwtimer/Kconfig +++ b/components/drivers/hwtimer/Kconfig @@ -1,15 +1,22 @@ menuconfig RT_USING_HWTIMER - bool "Using hardware timer device drivers" + bool "Using Hardware Timer device drivers" default n config RT_HWTIMER_ARM_ARCH bool "ARM ARCH Timer" depends on RT_USING_DM depends on RT_USING_HWTIMER + depends on ARCH_ARM_CORTEX_A || ARCH_ARMV8 default n -config RT_HWTIMER_RISCV_CLINT - bool "RISC-V CLINT Timer" +config RT_HWTIMER_BCM2835 + bool "BCM2835 Timer" + depends on RT_USING_DM + depends on RT_USING_HWTIMER + default n + +config RT_HWTIMER_ROCKCHIP + bool "RockChip Timer" depends on RT_USING_DM depends on RT_USING_HWTIMER default n diff --git a/components/drivers/hwtimer/SConscript b/components/drivers/hwtimer/SConscript index 44d9c8a1a26c..1806e5cd507a 100644 --- a/components/drivers/hwtimer/SConscript +++ b/components/drivers/hwtimer/SConscript @@ -11,10 +11,13 @@ CPPPATH = [cwd + '/../include'] src = ['hwtimer.c'] if GetDepend(['RT_HWTIMER_ARM_ARCH']): - src += ['hwtimer-arm-arch.c'] + src += ['hwtimer-arm_arch.c'] -if GetDepend(['RT_HWTIMER_RISCV_CLINT']): - src += ['hwtimer-riscv-clint.c'] +if GetDepend(['RT_HWTIMER_BCM2835']): + src += ['hwtimer-bcm2835_timer.c'] + +if GetDepend(['RT_HWTIMER_ROCKCHIP']): + src += ['hwtimer-rockchip_timer.c'] group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) diff --git a/components/drivers/hwtimer/hwtimer-arm-arch.c b/components/drivers/hwtimer/hwtimer-arm_arch.c similarity index 88% rename from components/drivers/hwtimer/hwtimer-arm-arch.c rename to components/drivers/hwtimer/hwtimer-arm_arch.c index 5c53f918d8b6..651d62243020 100644 --- a/components/drivers/hwtimer/hwtimer-arm-arch.c +++ b/components/drivers/hwtimer/hwtimer-arm_arch.c @@ -305,36 +305,6 @@ static void arm_arch_timer_isr(int vector, void *param) rt_tick_increase(); } -static rt_err_t arm_arch_timer_ofw_init(struct rt_platform_device *pdev, int irq_idx) -{ - rt_err_t err = RT_EOK; - const char *irq_name[] = - { - "phys", /* Secure Phys IRQ */ - "virt", /* Non-secure Phys IRQ */ - "hyp-phys", /* Virt IRQ */ - "hyp-virt", /* Hyp IRQ */ - }; - struct rt_ofw_node *np = pdev->parent.ofw_node; - - if (np) - { - arm_arch_timer_irq = rt_ofw_get_irq_by_name(np, irq_name[irq_idx]); - - if (arm_arch_timer_irq < 0) - { - arm_arch_timer_irq = rt_ofw_get_irq(np, irq_idx); - } - } - - if (arm_arch_timer_irq < 0) - { - err = -RT_EEMPTY; - } - - return err; -} - static int arm_arch_timer_post_init(void) { arm_arch_timer_local_enable(); @@ -345,8 +315,14 @@ INIT_SECONDARY_CPU_EXPORT(arm_arch_timer_post_init); static rt_err_t arm_arch_timer_probe(struct rt_platform_device *pdev) { - rt_err_t err; int mode_idx, irq_idx; + const char *irq_name[] = + { + "phys", /* Secure Phys IRQ */ + "virt", /* Non-secure Phys IRQ */ + "hyp-phys", /* Virt IRQ */ + "hyp-virt", /* Hyp IRQ */ + }; #if defined(ARCH_SUPPORT_TEE) mode_idx = 0; @@ -359,21 +335,28 @@ static rt_err_t arm_arch_timer_probe(struct rt_platform_device *pdev) irq_idx = 1; #endif - err = arm_arch_timer_ofw_init(pdev, irq_idx); + arm_arch_timer_irq = rt_dm_dev_get_irq_by_name(&pdev->parent, irq_name[irq_idx]); - if (!err) + if (arm_arch_timer_irq < 0) { - arm_arch_timer_ctrl_handle = ctrl_handle[mode_idx]; - arm_arch_timer_value_handle = value_handle[mode_idx]; + arm_arch_timer_irq = rt_dm_dev_get_irq(&pdev->parent, irq_idx); + } + + if (arm_arch_timer_irq < 0) + { + return -RT_EEMPTY; + } - rt_hw_interrupt_install(arm_arch_timer_irq, arm_arch_timer_isr, RT_NULL, "tick-arm-timer"); + arm_arch_timer_ctrl_handle = ctrl_handle[mode_idx]; + arm_arch_timer_value_handle = value_handle[mode_idx]; - timer_step = arm_arch_timer_get_frequency() / RT_TICK_PER_SECOND; + rt_hw_interrupt_install(arm_arch_timer_irq, arm_arch_timer_isr, RT_NULL, "tick-arm-timer"); - arm_arch_timer_local_enable(); - } + timer_step = arm_arch_timer_get_frequency() / RT_TICK_PER_SECOND; + + arm_arch_timer_local_enable(); - return err; + return RT_EOK; } static const struct rt_ofw_node_id arm_arch_timer_ofw_ids[] = diff --git a/components/drivers/hwtimer/hwtimer-bcm2835_timer.c b/components/drivers/hwtimer/hwtimer-bcm2835_timer.c new file mode 100644 index 000000000000..eecebb30091f --- /dev/null +++ b/components/drivers/hwtimer/hwtimer-bcm2835_timer.c @@ -0,0 +1,242 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-12-06 GuEe-GUI first version + */ + +#include +#include +#include + +#define REG_CONTROL 0x00 +#define REG_COUNTER_LO 0x04 +#define REG_COUNTER_HI 0x08 +#define REG_COMPARE(n) (0x0c + (n) * 4) +#define MAX_TIMER 3 +#define DEFAULT_TIMER 3 + +struct bcm2835_timer +{ + struct rt_hwtimer_device parent; + + int irq; + void *base; + void *control; + void *compare; + int match_mask; + + struct rt_hwtimer_info info; +}; + +#define raw_to_bcm2835_timer(raw) rt_container_of(raw, struct bcm2835_timer, parent) + +static void bcm2835_timer_init(struct rt_hwtimer_device *timer, rt_uint32_t state) +{ +} + +static rt_err_t bcm2835_timer_start(struct rt_hwtimer_device *timer, rt_uint32_t cnt, rt_hwtimer_mode_t mode) +{ + rt_err_t err = RT_EOK; + struct bcm2835_timer *bcm2835_timer = raw_to_bcm2835_timer(timer); + + switch (mode) + { + case HWTIMER_MODE_ONESHOT: + HWREG32(bcm2835_timer->compare) = HWREG32(bcm2835_timer->base + REG_COUNTER_LO) + cnt; + break; + + case HWTIMER_MODE_PERIOD: + err = -RT_ENOSYS; + break; + + default: + err = -RT_EINVAL; + break; + } + + return err; +} + +static void bcm2835_timer_stop(struct rt_hwtimer_device *timer) +{ +} + +static rt_uint32_t bcm2835_timer_count_get(struct rt_hwtimer_device *timer) +{ + struct bcm2835_timer *bcm2835_timer = raw_to_bcm2835_timer(timer); + + return HWREG32(bcm2835_timer->base + REG_COUNTER_LO); +} + +static rt_err_t bcm2835_timer_ctrl(struct rt_hwtimer_device *timer, rt_uint32_t cmd, void *args) +{ + rt_err_t err = RT_EOK; + struct bcm2835_timer *bcm2835_timer = raw_to_bcm2835_timer(timer); + + switch (cmd) + { + case HWTIMER_CTRL_FREQ_SET: + err = -RT_ENOSYS; + break; + + case HWTIMER_CTRL_STOP: + break; + + case HWTIMER_CTRL_INFO_GET: + if (args) + { + rt_memcpy(args, &bcm2835_timer->info, sizeof(bcm2835_timer->info)); + } + else + { + err = -RT_ERROR; + } + break; + + case HWTIMER_CTRL_MODE_SET: + err = -RT_ENOSYS; + break; + + default: + err = -RT_EINVAL; + break; + } + + return err; +} + +const static struct rt_hwtimer_ops bcm2835_timer_ops = +{ + .init = bcm2835_timer_init, + .start = bcm2835_timer_start, + .stop = bcm2835_timer_stop, + .count_get = bcm2835_timer_count_get, + .control = bcm2835_timer_ctrl, +}; + +static void bcm2835_timer_isr(int irqno, void *param) +{ + struct bcm2835_timer *bcm2835_timer = (struct bcm2835_timer *)param; + + if (HWREG32(bcm2835_timer->control) & bcm2835_timer->match_mask) + { + HWREG32(bcm2835_timer->control) = bcm2835_timer->match_mask; + + rt_device_hwtimer_isr(&bcm2835_timer->parent); + } +} + +static rt_err_t bcm2835_timer_probe(struct rt_platform_device *pdev) +{ + rt_err_t err = RT_EOK; + rt_uint32_t freq; + const char *dev_name; + struct rt_device *dev = &pdev->parent; + struct bcm2835_timer *timer = rt_calloc(1, sizeof(*timer)); + + if (!timer) + { + return -RT_ENOMEM; + } + + timer->base = rt_dm_dev_iomap(dev, 0); + + if (!timer->base) + { + err = -RT_EIO; + + goto _fail; + } + + timer->control = timer->base + REG_CONTROL; + timer->compare = timer->base + REG_COMPARE(DEFAULT_TIMER); + timer->match_mask = RT_BIT(DEFAULT_TIMER); + + timer->irq = rt_dm_dev_get_irq(dev, DEFAULT_TIMER); + + if (timer->irq < 0) + { + err = -RT_EIO; + + goto _fail; + } + + if (rt_dm_dev_prop_read_u32(dev, "clock-frequency", &freq)) + { + err = -RT_EIO; + + goto _fail; + } + + dev->user_data = timer; + + timer->parent.ops = &bcm2835_timer_ops; + timer->parent.info = &timer->info; + + timer->info.maxfreq = freq; + timer->info.minfreq = freq; + timer->info.maxcnt = 0xffffffff; + timer->info.cntmode = HWTIMER_CNTMODE_UP; + + rt_dm_dev_set_name_auto(&timer->parent.parent, "timer"); + dev_name = rt_dm_dev_get_name(&timer->parent.parent); + + rt_device_hwtimer_register(&timer->parent, dev_name, RT_NULL); + rt_hw_interrupt_install(timer->irq, bcm2835_timer_isr, timer, dev_name); + rt_hw_interrupt_umask(timer->irq); + + return err; + +_fail: + if (timer->base) + { + rt_iounmap(timer->base); + } + rt_free(timer); + + return err; +} + +static rt_err_t bcm2835_timer_remove(struct rt_platform_device *pdev) +{ + struct bcm2835_timer *timer = pdev->parent.user_data; + + bcm2835_timer_stop(&timer->parent); + + rt_hw_interrupt_mask(timer->irq); + rt_pic_detach_irq(timer->irq, timer); + + rt_device_unregister(&timer->parent.parent); + + rt_iounmap(timer->base); + rt_free(timer); + + return RT_EOK; +} + +static const struct rt_ofw_node_id bcm2835_timer_ofw_ids[] = +{ + { .compatible = "brcm,bcm2835-system-timer", }, + { /* sentinel */ } +}; + +static struct rt_platform_driver bcm2835_timer_driver = +{ + .name = "hwtimer-bcm2835", + .ids = bcm2835_timer_ofw_ids, + + .probe = bcm2835_timer_probe, + .remove = bcm2835_timer_remove, +}; + +static int bcm2835_timer_drv_register(void) +{ + rt_platform_driver_register(&bcm2835_timer_driver); + + return 0; +} +INIT_DRIVER_EARLY_EXPORT(bcm2835_timer_drv_register); diff --git a/components/drivers/hwtimer/hwtimer-riscv-clint.c b/components/drivers/hwtimer/hwtimer-riscv-clint.c deleted file mode 100755 index c03e54956e62..000000000000 --- a/components/drivers/hwtimer/hwtimer-riscv-clint.c +++ /dev/null @@ -1,251 +0,0 @@ -clint@2000000 { - interrupts-extended = <0x08 0x03 0x08 0x07 0x06 0x03 0x06 0x07 0x04 0x03 0x04 0x07 0x02 0x03 0x02 0x07>; - reg = <0x00 0x2000000 0x00 0x10000>; - compatible = "sifive,clint0", "riscv,clint0"; -}; - - -// SPDX-License-Identifier: GPL-2.0 -/* - * Copyright (C) 2020 Western Digital Corporation or its affiliates. - * - * Most of the M-mode (i.e. NoMMU) RISC-V systems usually have a - * CLINT MMIO timer device. - */ - -#define pr_fmt(fmt) "clint: " fmt -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifndef CONFIG_RISCV_M_MODE -#include -#endif - -#define CLINT_IPI_OFF 0 -#define CLINT_TIMER_CMP_OFF 0x4000 -#define CLINT_TIMER_VAL_OFF 0xbff8 - -/* CLINT manages IPI and Timer for RISC-V M-mode */ -static u32 __iomem *clint_ipi_base; -static u64 __iomem *clint_timer_cmp; -static u64 __iomem *clint_timer_val; -static unsigned long clint_timer_freq; -static unsigned int clint_timer_irq; - -#ifdef CONFIG_RISCV_M_MODE -u64 __iomem *clint_time_val; -EXPORT_SYMBOL(clint_time_val); -#endif - -static void clint_send_ipi(const struct cpumask *target) -{ - unsigned int cpu; - - for_each_cpu(cpu, target) - writel(1, clint_ipi_base + cpuid_to_hartid_map(cpu)); -} - -static void clint_clear_ipi(void) -{ - writel(0, clint_ipi_base + cpuid_to_hartid_map(smp_processor_id())); -} - -static struct riscv_ipi_ops clint_ipi_ops = { - .ipi_inject = clint_send_ipi, - .ipi_clear = clint_clear_ipi, -}; - -#ifdef CONFIG_64BIT -#define clint_get_cycles() readq_relaxed(clint_timer_val) -#else -#define clint_get_cycles() readl_relaxed(clint_timer_val) -#define clint_get_cycles_hi() readl_relaxed(((u32 *)clint_timer_val) + 1) -#endif - -#ifdef CONFIG_64BIT -static u64 notrace clint_get_cycles64(void) -{ - return clint_get_cycles(); -} -#else /* CONFIG_64BIT */ -static u64 notrace clint_get_cycles64(void) -{ - u32 hi, lo; - - do { - hi = clint_get_cycles_hi(); - lo = clint_get_cycles(); - } while (hi != clint_get_cycles_hi()); - - return ((u64)hi << 32) | lo; -} -#endif /* CONFIG_64BIT */ - -static u64 clint_rdtime(struct clocksource *cs) -{ - return clint_get_cycles64(); -} - -static struct clocksource clint_clocksource = { - .name = "clint_clocksource", - .rating = 300, - .mask = CLOCKSOURCE_MASK(64), - .flags = CLOCK_SOURCE_IS_CONTINUOUS, - .read = clint_rdtime, -}; - -static int clint_clock_next_event(unsigned long delta, - struct clock_event_device *ce) -{ - void __iomem *r = clint_timer_cmp + - cpuid_to_hartid_map(smp_processor_id()); - - csr_set(CSR_IE, IE_TIE); - writeq_relaxed(clint_get_cycles64() + delta, r); - return 0; -} - -static DEFINE_PER_CPU(struct clock_event_device, clint_clock_event) = { - .name = "clint_clockevent", - .features = CLOCK_EVT_FEAT_ONESHOT, - .rating = 100, - .set_next_event = clint_clock_next_event, -}; - -static int clint_timer_starting_cpu(unsigned int cpu) -{ - struct clock_event_device *ce = per_cpu_ptr(&clint_clock_event, cpu); - - ce->cpumask = cpumask_of(cpu); - clockevents_config_and_register(ce, clint_timer_freq, 100, 0x7fffffff); - - enable_percpu_irq(clint_timer_irq, - irq_get_trigger_type(clint_timer_irq)); - return 0; -} - -static int clint_timer_dying_cpu(unsigned int cpu) -{ - disable_percpu_irq(clint_timer_irq); - return 0; -} - -static irqreturn_t clint_timer_interrupt(int irq, void *dev_id) -{ - struct clock_event_device *evdev = this_cpu_ptr(&clint_clock_event); - - csr_clear(CSR_IE, IE_TIE); - evdev->event_handler(evdev); - - return IRQ_HANDLED; -} - -static int __init clint_timer_init_dt(struct device_node *np) -{ - int rc; - u32 i, nr_irqs; - void __iomem *base; - struct of_phandle_args oirq; - - /* - * Ensure that CLINT device interrupts are either RV_IRQ_TIMER or - * RV_IRQ_SOFT. If it's anything else then we ignore the device. - */ - nr_irqs = of_irq_count(np); - for (i = 0; i < nr_irqs; i++) { - if (of_irq_parse_one(np, i, &oirq)) { - pr_err("%pOFP: failed to parse irq %d.\n", np, i); - continue; - } - - if ((oirq.args_count != 1) || - (oirq.args[0] != RV_IRQ_TIMER && - oirq.args[0] != RV_IRQ_SOFT)) { - pr_err("%pOFP: invalid irq %d (hwirq %d)\n", - np, i, oirq.args[0]); - return -ENODEV; - } - - /* Find parent irq domain and map timer irq */ - if (!clint_timer_irq && - oirq.args[0] == RV_IRQ_TIMER && - irq_find_host(oirq.np)) - clint_timer_irq = irq_of_parse_and_map(np, i); - } - - /* If CLINT timer irq not found then fail */ - if (!clint_timer_irq) { - pr_err("%pOFP: timer irq not found\n", np); - return -ENODEV; - } - - base = of_iomap(np, 0); - if (!base) { - pr_err("%pOFP: could not map registers\n", np); - return -ENODEV; - } - - clint_ipi_base = base + CLINT_IPI_OFF; - clint_timer_cmp = base + CLINT_TIMER_CMP_OFF; - clint_timer_val = base + CLINT_TIMER_VAL_OFF; - clint_timer_freq = riscv_timebase; - -#ifdef CONFIG_RISCV_M_MODE - /* - * Yes, that's an odd naming scheme. time_val is public, but hopefully - * will die in favor of something cleaner. - */ - clint_time_val = clint_timer_val; -#endif - - pr_info("%pOFP: timer running at %ld Hz\n", np, clint_timer_freq); - - rc = clocksource_register_hz(&clint_clocksource, clint_timer_freq); - if (rc) { - pr_err("%pOFP: clocksource register failed [%d]\n", np, rc); - goto fail_iounmap; - } - - sched_clock_register(clint_get_cycles64, 64, clint_timer_freq); - - rc = request_percpu_irq(clint_timer_irq, clint_timer_interrupt, - "clint-timer", &clint_clock_event); - if (rc) { - pr_err("registering percpu irq failed [%d]\n", rc); - goto fail_iounmap; - } - - rc = cpuhp_setup_state(CPUHP_AP_CLINT_TIMER_STARTING, - "clockevents/clint/timer:starting", - clint_timer_starting_cpu, - clint_timer_dying_cpu); - if (rc) { - pr_err("%pOFP: cpuhp setup state failed [%d]\n", np, rc); - goto fail_free_irq; - } - - riscv_set_ipi_ops(&clint_ipi_ops); - clint_clear_ipi(); - - return 0; - -fail_free_irq: - free_irq(clint_timer_irq, &clint_clock_event); -fail_iounmap: - iounmap(base); - return rc; -} - -TIMER_OF_DECLARE(clint_timer, "riscv,clint0", clint_timer_init_dt); -TIMER_OF_DECLARE(clint_timer1, "sifive,clint0", clint_timer_init_dt); \ No newline at end of file diff --git a/components/drivers/hwtimer/hwtimer-rockchip_timer.c b/components/drivers/hwtimer/hwtimer-rockchip_timer.c new file mode 100644 index 000000000000..f0f184b79b74 --- /dev/null +++ b/components/drivers/hwtimer/hwtimer-rockchip_timer.c @@ -0,0 +1,328 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-12-06 GuEe-GUI first version + */ + +#include +#include +#include + +#include "../clk/rockchip/clk.h" + +#define TIMER_LOAD_COUNT0 0x00 +#define TIMER_LOAD_COUNT1 0x04 +#define TIMER_CURRENT_VALUE0 0x08 +#define TIMER_CURRENT_VALUE1 0x0c +#define TIMER_CONTROL_REG3288 0x10 +#define TIMER_CONTROL_REG3399 0x1c +#define TIMER_INT_STATUS 0x18 + +#define TIMER_DISABLE 0x0 +#define TIMER_ENABLE 0x1 +#define TIMER_MODE_FREE_RUNNING (0 << 1) +#define TIMER_MODE_USER_DEFINED_COUNT (1 << 1) +#define TIMER_INT_UNMASK (1 << 2) + +struct rk_timer +{ + struct rt_hwtimer_device parent; + + void *base; + void *ctrl; + struct rt_clk *clk; + struct rt_clk *pclk; + + int irq; + rt_uint32_t freq; + rt_uint32_t cycle; + rt_bool_t status; + + struct rt_hwtimer_info info; +}; + +#define raw_to_rk_timer(raw) rt_container_of(raw, struct rk_timer, parent) + +struct rk_timer_data +{ + rt_uint32_t ctrl_reg; +}; + +rt_inline void rk_timer_disable(struct rk_timer *timer) +{ + HWREG32(timer->ctrl) = TIMER_DISABLE; +} + +rt_inline void rk_timer_enable(struct rk_timer *timer, rt_uint32_t flags) +{ + HWREG32(timer->ctrl) = TIMER_ENABLE | flags; +} + +rt_inline rt_uint32_t rk_timer_current_value(struct rk_timer *timer) +{ + return HWREG32(timer->base + TIMER_CURRENT_VALUE0); +} + +static void rk_timer_update_counter(unsigned long cycles, struct rk_timer *timer) +{ + HWREG32(timer->base + TIMER_LOAD_COUNT0) = cycles; + HWREG32(timer->base + TIMER_LOAD_COUNT1) = 0; +} + +static void rk_timer_interrupt_clear(struct rk_timer *timer) +{ + HWREG32(timer->base + TIMER_INT_STATUS) = 1; +} + +static void rk_timer_init(struct rt_hwtimer_device *timer, rt_uint32_t state) +{ +} + +static rt_err_t rk_timer_start(struct rt_hwtimer_device *timer, rt_uint32_t cnt, rt_hwtimer_mode_t mode) +{ + rt_err_t err = RT_EOK; + struct rk_timer *rk_timer = raw_to_rk_timer(timer); + + switch (mode) + { + case HWTIMER_MODE_ONESHOT: + rk_timer_disable(rk_timer); + rk_timer_update_counter(cnt, rk_timer); + rk_timer_enable(rk_timer, TIMER_MODE_USER_DEFINED_COUNT | TIMER_INT_UNMASK); + break; + + case HWTIMER_MODE_PERIOD: + rk_timer_disable(rk_timer); + rk_timer_update_counter(rk_timer->freq / HZ - 1, rk_timer); + rk_timer_enable(rk_timer, TIMER_MODE_FREE_RUNNING | TIMER_INT_UNMASK); + break; + + default: + err = -RT_EINVAL; + break; + } + + if (!err) + { + rk_timer->cycle = cnt; + rk_timer->status = RT_TRUE; + } + + return err; +} + +static void rk_timer_stop(struct rt_hwtimer_device *timer) +{ + struct rk_timer *rk_timer = raw_to_rk_timer(timer); + + rk_timer->status = RT_FALSE; + rk_timer_disable(rk_timer); +} + +static rt_uint32_t rk_timer_count_get(struct rt_hwtimer_device *timer) +{ + struct rk_timer *rk_timer = raw_to_rk_timer(timer); + + return rk_timer_current_value(rk_timer); +} + +static rt_err_t rk_timer_ctrl(struct rt_hwtimer_device *timer, rt_uint32_t cmd, void *args) +{ + rt_err_t err = RT_EOK; + struct rk_timer *rk_timer = raw_to_rk_timer(timer); + + switch (cmd) + { + case HWTIMER_CTRL_FREQ_SET: + err = -RT_ENOSYS; + break; + + case HWTIMER_CTRL_STOP: + rk_timer_stop(timer); + break; + + case HWTIMER_CTRL_INFO_GET: + if (args) + { + rt_memcpy(args, &rk_timer->info, sizeof(rk_timer->info)); + } + else + { + err = -RT_ERROR; + } + break; + + case HWTIMER_CTRL_MODE_SET: + err = rk_timer_start(timer, rk_timer->cycle, (rt_hwtimer_mode_t)args); + break; + + default: + err = -RT_EINVAL; + break; + } + + return err; +} + +const static struct rt_hwtimer_ops rk_timer_ops = +{ + .init = rk_timer_init, + .start = rk_timer_start, + .stop = rk_timer_stop, + .count_get = rk_timer_count_get, + .control = rk_timer_ctrl, +}; + +static void rk_timer_isr(int irqno, void *param) +{ + struct rk_timer *rk_timer = (struct rk_timer *)param; + + rk_timer_interrupt_clear(rk_timer); + + if (rk_timer->status) + { + rt_device_hwtimer_isr(&rk_timer->parent); + } +} + +static void rk_timer_free(struct rk_timer *timer) +{ + if (timer->base) + { + rt_iounmap(timer->base); + } + + if (!rt_is_err_or_null(timer->pclk)) + { + rt_clk_put(timer->pclk); + } + + if (!rt_is_err_or_null(timer->clk)) + { + rt_clk_put(timer->clk); + } + + rt_free(timer); +} + +static rt_err_t rk_timer_probe(struct rt_platform_device *pdev) +{ + rt_err_t err = RT_EOK; + const char *dev_name; + struct rt_device *dev = &pdev->parent; + struct rk_timer *timer = rt_calloc(1, sizeof(*timer)); + const struct rk_timer_data *timer_data = pdev->id->data; + + if (!timer) + { + return -RT_ENOMEM; + } + + if (rt_is_err(timer->pclk = rt_clk_get_by_name(dev, "pclk"))) + { + err = rt_ptr_err(timer->pclk); + + goto _fail; + } + + if (rt_is_err(timer->clk = rt_clk_get_by_name(dev, "timer"))) + { + err = rt_ptr_err(timer->clk); + + goto _fail; + } + + timer->base = rt_dm_dev_iomap(dev, 0); + + if (!timer->base) + { + err = -RT_EIO; + + goto _fail; + } + + dev->user_data = timer; + + timer->ctrl = timer->base + timer_data->ctrl_reg; + + rt_clk_enable(timer->pclk); + rt_clk_enable(timer->clk); + timer->freq = rt_clk_get_rate(timer->clk); + timer->irq = rt_dm_dev_get_irq(dev, 0); + + rk_timer_interrupt_clear(timer); + rk_timer_disable(timer); + + timer->parent.ops = &rk_timer_ops; + timer->parent.info = &timer->info; + + timer->info.maxfreq = timer->freq; + timer->info.minfreq = timer->freq; + timer->info.maxcnt = 0xffffffff; + timer->info.cntmode = HWTIMER_CNTMODE_UP; + + rt_dm_dev_set_name_auto(&timer->parent.parent, "timer"); + dev_name = rt_dm_dev_get_name(&timer->parent.parent); + + rt_device_hwtimer_register(&timer->parent, dev_name, RT_NULL); + rt_hw_interrupt_install(timer->irq, rk_timer_isr, timer, dev_name); + rt_hw_interrupt_umask(timer->irq); + + return err; + +_fail: + rk_timer_free(timer); + + return err; +} + +static rt_err_t rk_timer_remove(struct rt_platform_device *pdev) +{ + struct rk_timer *timer = pdev->parent.user_data; + + rk_timer_stop(&timer->parent); + + rt_device_unregister(&timer->parent.parent); + + rk_timer_free(timer); + + return RT_EOK; +} + +static const struct rk_timer_data rk3288_timer_data = +{ + .ctrl_reg = TIMER_CONTROL_REG3288, +}; + +static const struct rk_timer_data rk3399_timer_data = +{ + .ctrl_reg = TIMER_CONTROL_REG3399, +}; + +static const struct rt_ofw_node_id rk_timer_ofw_ids[] = +{ + { .compatible = "rockchip,rk3288-timer", .data = &rk3288_timer_data }, + { .compatible = "rockchip,rk3399-timer", .data = &rk3399_timer_data }, + { /* sentinel */ } +}; + +static struct rt_platform_driver rk_timer_driver = +{ + .name = "hwtimer-rockchip", + .ids = rk_timer_ofw_ids, + + .probe = rk_timer_probe, + .remove = rk_timer_remove, +}; + +static int rk_timer_drv_register(void) +{ + rt_platform_driver_register(&rk_timer_driver); + + return 0; +} +INIT_DRIVER_EARLY_EXPORT(rk_timer_drv_register); diff --git a/components/drivers/hwtimer/hwtimer.c b/components/drivers/hwtimer/hwtimer.c index b10f5dc696ed..cc43be7ee409 100644 --- a/components/drivers/hwtimer/hwtimer.c +++ b/components/drivers/hwtimer/hwtimer.c @@ -15,8 +15,24 @@ #define DBG_LVL DBG_INFO #include +#ifdef RT_USING_DM void (*rt_device_hwtimer_us_delay)(rt_uint32_t us) = RT_NULL; +void rt_hw_us_delay(rt_uint32_t us) +{ + if (rt_device_hwtimer_us_delay) + { + rt_device_hwtimer_us_delay(us); + } + else + { + LOG_E("Implemented at least in the libcpu"); + + RT_ASSERT(0); + } +} +#endif /* RT_USING_DM */ + rt_inline rt_uint32_t timeout_calc(rt_hwtimer_t *timer, rt_hwtimerval_t *tv) { float overflow; diff --git a/components/drivers/i2c/Kconfig b/components/drivers/i2c/Kconfig new file mode 100755 index 000000000000..a0e7bd642c30 --- /dev/null +++ b/components/drivers/i2c/Kconfig @@ -0,0 +1,27 @@ +menuconfig RT_USING_I2C + bool "Using I2C device drivers" + default n + +if RT_USING_I2C + config RT_I2C_DEBUG + bool "Use I2C debug message" + default n + + config RT_USING_I2C_BITOPS + bool "Use GPIO to simulate I2C" + default y + + if RT_USING_I2C_BITOPS + config RT_I2C_BITOPS_DEBUG + bool "Use simulate I2C debug message" + default n + endif +endif + +config RT_I2C_RK3X + bool "Rockchip RK3xxx I2C adapter" + depends on RT_USING_DM + depends on RT_USING_I2C + select RT_MFD_SYSCON + select RT_USING_PINCTRL + default n diff --git a/components/drivers/i2c/SConscript b/components/drivers/i2c/SConscript index 5e85a645a7c3..4a2ee9260eb5 100644 --- a/components/drivers/i2c/SConscript +++ b/components/drivers/i2c/SConscript @@ -1,6 +1,8 @@ Import('RTT_ROOT') from building import * +objs = [] + cwd = GetCurrentDir() src = Split(""" i2c_core.c @@ -13,6 +15,15 @@ if GetDepend('RT_USING_I2C_BITOPS'): # The set of source files associated with this SConscript file. path = [cwd + '/../include'] +if GetDepend(['RT_USING_DM']): + src += ['i2c_bus.c', 'i2c_dm.c'] + group = DefineGroup('DeviceDrivers', src, depend = ['RT_USING_I2C'], CPPPATH = path) -Return('group') +for d in os.listdir(cwd): + path = os.path.join(cwd, d) + if os.path.isfile(os.path.join(path, 'SConscript')): + objs = objs + SConscript(os.path.join(d, 'SConscript')) +objs = objs + group + +Return('objs') diff --git a/components/drivers/i2c/busses/SConscript b/components/drivers/i2c/busses/SConscript new file mode 100644 index 000000000000..203446b96836 --- /dev/null +++ b/components/drivers/i2c/busses/SConscript @@ -0,0 +1,18 @@ +from building import * + +group = [] + +if not GetDepend(['RT_USING_I2C']) and not GetDepend(['RT_USING_DM']): + Return('group') + +cwd = GetCurrentDir() +CPPPATH = [cwd + '/../../include'] + +src = [] + +if GetDepend('RT_I2C_RK3X'): + src += ['i2c-rk3x.c'] + +group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') diff --git a/components/drivers/i2c/busses/i2c-rk3x.c b/components/drivers/i2c/busses/i2c-rk3x.c new file mode 100644 index 000000000000..14881765147e --- /dev/null +++ b/components/drivers/i2c/busses/i2c-rk3x.c @@ -0,0 +1,1352 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-3-08 GuEe-GUI the first version + */ + +#include +#include +#include + +#define DBG_TAG "i2c.rk3x" +#define DBG_LVL DBG_INFO +#include + +#include "../i2c_dm.h" + +/* Register Map */ +#define REG_CON 0x00 /* control register */ +#define REG_CLKDIV 0x04 /* clock divisor register */ +#define REG_MRXADDR 0x08 /* slave address for REGISTER_TX */ +#define REG_MRXRADDR 0x0c /* slave register address for REGISTER_TX */ +#define REG_MTXCNT 0x10 /* number of bytes to be transmitted */ +#define REG_MRXCNT 0x14 /* number of bytes to be received */ +#define REG_IEN 0x18 /* interrupt enable */ +#define REG_IPD 0x1c /* interrupt pending */ +#define REG_FCNT 0x20 /* finished count */ + +/* Data buffer offsets */ +#define TXBUFFER_BASE 0x100 +#define RXBUFFER_BASE 0x200 + +/* REG_CON bits */ +#define REG_CON_EN RT_BIT(0) + +enum +{ + REG_CON_MOD_TX = 0, /* transmit data */ + REG_CON_MOD_REGISTER_TX, /* select register and restart */ + REG_CON_MOD_RX, /* receive data */ + REG_CON_MOD_REGISTER_RX, /* broken: transmits read addr AND writes register addr */ +}; + +#define REG_CON_MOD(mod) ((mod) << 1) +#define REG_CON_MOD_MASK (RT_BIT(1) | RT_BIT(2)) +#define REG_CON_START RT_BIT(3) +#define REG_CON_STOP RT_BIT(4) +#define REG_CON_LASTACK RT_BIT(5) /* 1: send NACK after last received byte */ +#define REG_CON_ACTACK RT_BIT(6) /* 1: stop if NACK is received */ + +#define REG_CON_TUNING_MASK RT_GENMASK_ULL(15, 8) + +#define REG_CON_SDA_CFG(cfg) ((cfg) << 8) +#define REG_CON_STA_CFG(cfg) ((cfg) << 12) +#define REG_CON_STO_CFG(cfg) ((cfg) << 14) + +/* REG_MRXADDR bits */ +#define REG_MRXADDR_VALID(x) RT_BIT(24 + (x)) /* [x*8+7:x*8] of MRX[R]ADDR valid */ + +/* REG_IEN/REG_IPD bits */ +#define REG_INT_BTF RT_BIT(0) /* a byte was transmitted */ +#define REG_INT_BRF RT_BIT(1) /* a byte was received */ +#define REG_INT_MBTF RT_BIT(2) /* master data transmit finished */ +#define REG_INT_MBRF RT_BIT(3) /* master data receive finished */ +#define REG_INT_START RT_BIT(4) /* START condition generated */ +#define REG_INT_STOP RT_BIT(5) /* STOP condition generated */ +#define REG_INT_NAKRCV RT_BIT(6) /* NACK received */ +#define REG_INT_ALL 0x7f + +/* Constants */ +#define WAIT_TIMEOUT 1000 /* ms */ +#define DEFAULT_SCL_RATE (100 * 1000) /* Hz */ + +/* I2C specification values for various modes */ +struct i2c_spec_values +{ + rt_ubase_t min_hold_start_ns; /* min hold time (repeated) START condition */ + rt_ubase_t min_low_ns; /* min LOW period of the SCL clock */ + rt_ubase_t min_high_ns; /* min HIGH period of the SCL cloc */ + rt_ubase_t min_setup_start_ns; /* min set-up time for a repeated START conditio */ + rt_ubase_t max_data_hold_ns; /* max data hold time */ + rt_ubase_t min_data_setup_ns; /* min data set-up time */ + rt_ubase_t min_setup_stop_ns; /* min set-up time for STOP condition */ + rt_ubase_t min_hold_buffer_ns; /* min bus free time between a STOP and */ +}; + +static const struct i2c_spec_values standard_mode_spec = +{ + .min_hold_start_ns = 4000, + .min_low_ns = 4700, + .min_high_ns = 4000, + .min_setup_start_ns = 4700, + .max_data_hold_ns = 3450, + .min_data_setup_ns = 250, + .min_setup_stop_ns = 4000, + .min_hold_buffer_ns = 4700, +}; + +static const struct i2c_spec_values fast_mode_spec = +{ + .min_hold_start_ns = 600, + .min_low_ns = 1300, + .min_high_ns = 600, + .min_setup_start_ns = 600, + .max_data_hold_ns = 900, + .min_data_setup_ns = 100, + .min_setup_stop_ns = 600, + .min_hold_buffer_ns = 1300, +}; + +static const struct i2c_spec_values fast_mode_plus_spec = +{ + .min_hold_start_ns = 260, + .min_low_ns = 500, + .min_high_ns = 260, + .min_setup_start_ns = 260, + .max_data_hold_ns = 400, + .min_data_setup_ns = 50, + .min_setup_stop_ns = 260, + .min_hold_buffer_ns = 500, +}; + +/* + * Calculated V1 timings, setup/hold start time and setup stop time for v1's + * calc_timings, the tuning should all be 0 for old hardware anyone using v0's + * calc_timings. + */ +struct rk3x_i2c_calced_timings +{ + rt_ubase_t div_low; /* Divider output for low */ + rt_ubase_t div_high; /* Divider output for high */ + rt_uint32_t tuning; /* Used to adjust setup/hold data time */ +}; + +enum rk3x_i2c_state +{ + STATE_IDLE, + STATE_START, + STATE_READ, + STATE_WRITE, + STATE_STOP +}; + +struct rk3x_i2c_soc_data +{ + int grf_offset; + rt_err_t (*calc_timings)(rt_ubase_t, struct i2c_timings *, + struct rk3x_i2c_calced_timings *); +}; + +struct rk3x_i2c +{ + struct rt_i2c_bus_device parent; + + const struct rk3x_i2c_soc_data *soc_data; + + int irq; + void *regs; + + struct rt_clk *clk; + struct rt_clk *pclk; + struct rt_clk_notifier clk_notifier; + + struct i2c_timings timings; + + struct rt_spinlock lock; + struct rt_completion done; + + struct rt_i2c_msg *msg; + rt_uint8_t addr; + rt_uint32_t mode; + rt_bool_t is_last_msg; + + enum rk3x_i2c_state state; + rt_uint32_t processed; + rt_err_t error; +}; + +#define raw_to_rk3x_i2c(raw) rt_container_of(raw, struct rk3x_i2c, parent) + +rt_inline void i2c_writel(struct rk3x_i2c *i2c, rt_uint32_t value, int offset) +{ + HWREG32(i2c->regs + offset) = value; +} + +rt_inline rt_uint32_t i2c_readl(struct rk3x_i2c *i2c, int offset) +{ + return HWREG32(i2c->regs + offset); +} + +/* Reset all interrupt pending bits */ +rt_inline void rk3x_i2c_clean_ipd(struct rk3x_i2c *i2c) +{ + i2c_writel(i2c, REG_INT_ALL, REG_IPD); +} + +/* Generate a START condition, which triggers a REG_INT_START interrupt */ +static void rk3x_i2c_start(struct rk3x_i2c *i2c) +{ + rt_uint32_t val = i2c_readl(i2c, REG_CON) & REG_CON_TUNING_MASK; + + i2c_writel(i2c, REG_INT_START, REG_IEN); + + /* enable adapter with correct mode, send START condition */ + val |= REG_CON_EN | REG_CON_MOD(i2c->mode) | REG_CON_START; + + /* if we want to react to NACK, set ACTACK bit */ + if (!(i2c->msg->flags & RT_I2C_IGNORE_NACK)) + { + val |= REG_CON_ACTACK; + } + + i2c_writel(i2c, val, REG_CON); +} + +/* Generate a STOP condition, which triggers a REG_INT_STOP interrupt */ +static void rk3x_i2c_stop(struct rk3x_i2c *i2c, rt_err_t error) +{ + rt_uint32_t ctrl; + + i2c->processed = 0; + i2c->msg = RT_NULL; + i2c->error = error; + + if (i2c->is_last_msg) + { + /* Enable stop interrupt */ + i2c_writel(i2c, REG_INT_STOP, REG_IEN); + + i2c->state = STATE_STOP; + + ctrl = i2c_readl(i2c, REG_CON); + ctrl |= REG_CON_STOP; + i2c_writel(i2c, ctrl, REG_CON); + } + else + { + /* Signal rk3x_i2c_xfer to start the next message. */ + i2c->state = STATE_IDLE; + + /* + * The HW is actually not capable of REPEATED START. But we can + * get the intended effect by resetting its internal state + * and issuing an ordinary START. + */ + ctrl = i2c_readl(i2c, REG_CON) & REG_CON_TUNING_MASK; + i2c_writel(i2c, ctrl, REG_CON); + + /* signal that we are finished with the current msg */ + rt_completion_done(&i2c->done); + } +} + +/* Setup a read according to i2c->msg */ +static void rk3x_i2c_prepare_read(struct rk3x_i2c *i2c) +{ + rt_uint32_t con, len = i2c->msg->len - i2c->processed; + + con = i2c_readl(i2c, REG_CON); + + /* + * The hw can read up to 32 bytes at a time. If we need more than one + * chunk, send an ACK after the last byte of the current chunk. + */ + if (len > 32) + { + len = 32; + con &= ~REG_CON_LASTACK; + } + else + { + con |= REG_CON_LASTACK; + } + + /* make sure we are in plain RX mode if we read a second chunk */ + if (i2c->processed != 0) + { + con &= ~REG_CON_MOD_MASK; + con |= REG_CON_MOD(REG_CON_MOD_RX); + } + + i2c_writel(i2c, con, REG_CON); + i2c_writel(i2c, len, REG_MRXCNT); +} + +/* Fill the transmit buffer with data from i2c->msg */ +static void rk3x_i2c_fill_transmit_buf(struct rk3x_i2c *i2c) +{ + rt_uint32_t cnt = 0; + + for (int i = 0; i < 8; ++i) + { + rt_uint32_t val = 0; + + for (int j = 0; j < 4; ++j) + { + rt_uint8_t byte; + + if ((i2c->processed == i2c->msg->len) && (cnt != 0)) + { + break; + } + + if (i2c->processed == 0 && cnt == 0) + { + byte = (i2c->addr & 0x7f) << 1; + } + else + { + byte = i2c->msg->buf[i2c->processed++]; + } + + val |= byte << (j * 8); + cnt++; + } + + i2c_writel(i2c, val, TXBUFFER_BASE + 4 * i); + + if (i2c->processed == i2c->msg->len) + { + break; + } + } + + i2c_writel(i2c, cnt, REG_MTXCNT); +} + +/* IRQ handlers for individual states */ +static void rk3x_i2c_handle_start(struct rk3x_i2c *i2c, rt_uint32_t ipd) +{ + if (!(ipd & REG_INT_START)) + { + rk3x_i2c_stop(i2c, -RT_EIO); + LOG_W("Unexpected irq in START: 0x%x", ipd); + rk3x_i2c_clean_ipd(i2c); + + return; + } + + /* ack interrupt */ + i2c_writel(i2c, REG_INT_START, REG_IPD); + + /* disable start bit */ + i2c_writel(i2c, i2c_readl(i2c, REG_CON) & ~REG_CON_START, REG_CON); + + /* enable appropriate interrupts and transition */ + if (i2c->mode == REG_CON_MOD_TX) + { + i2c_writel(i2c, REG_INT_MBTF | REG_INT_NAKRCV, REG_IEN); + i2c->state = STATE_WRITE; + rk3x_i2c_fill_transmit_buf(i2c); + } + else + { + /* in any other case, we are going to be reading. */ + i2c_writel(i2c, REG_INT_MBRF | REG_INT_NAKRCV, REG_IEN); + i2c->state = STATE_READ; + rk3x_i2c_prepare_read(i2c); + } +} + +static void rk3x_i2c_handle_write(struct rk3x_i2c *i2c, rt_uint32_t ipd) +{ + if (!(ipd & REG_INT_MBTF)) + { + rk3x_i2c_stop(i2c, -RT_EIO); + LOG_E("Unexpected irq in WRITE: 0x%x", ipd); + rk3x_i2c_clean_ipd(i2c); + + return; + } + + /* ack interrupt */ + i2c_writel(i2c, REG_INT_MBTF, REG_IPD); + + /* are we finished? */ + if (i2c->processed == i2c->msg->len) + { + rk3x_i2c_stop(i2c, i2c->error); + } + else + { + rk3x_i2c_fill_transmit_buf(i2c); + } +} + +static void rk3x_i2c_handle_read(struct rk3x_i2c *i2c, unsigned int ipd) +{ + rt_uint32_t val, len = i2c->msg->len - i2c->processed; + + /* we only care for MBRF here. */ + if (!(ipd & REG_INT_MBRF)) + { + return; + } + + /* ack interrupt (read also produces a spurious START flag, clear it too) */ + i2c_writel(i2c, REG_INT_MBRF | REG_INT_START, REG_IPD); + + /* Can only handle a maximum of 32 bytes at a time */ + if (len > 32) + { + len = 32; + } + + /* read the data from receive buffer */ + for (int i = 0; i < len; ++i) + { + rt_uint8_t byte; + + if (i % 4 == 0) + { + val = i2c_readl(i2c, RXBUFFER_BASE + (i / 4) * 4); + } + + byte = (val >> ((i % 4) * 8)) & 0xff; + i2c->msg->buf[i2c->processed++] = byte; + } + + /* are we finished? */ + if (i2c->processed == i2c->msg->len) + { + rk3x_i2c_stop(i2c, i2c->error); + } + else + { + rk3x_i2c_prepare_read(i2c); + } +} + +static void rk3x_i2c_handle_stop(struct rk3x_i2c *i2c, rt_uint32_t ipd) +{ + rt_uint32_t con; + + if (!(ipd & REG_INT_STOP)) + { + rk3x_i2c_stop(i2c, -RT_EIO); + LOG_E("Unexpected irq in STOP: 0x%x", ipd); + rk3x_i2c_clean_ipd(i2c); + + return; + } + + /* ack interrupt */ + i2c_writel(i2c, REG_INT_STOP, REG_IPD); + + /* disable STOP bit */ + con = i2c_readl(i2c, REG_CON); + con &= ~REG_CON_STOP; + i2c_writel(i2c, con, REG_CON); + + i2c->state = STATE_IDLE; + + /* signal rk3x_i2c_xfer that we are finished */ + rt_completion_done(&i2c->done); +} + +static void rk3x_i2c_adapt_div(struct rk3x_i2c *i2c, rt_ubase_t clk_rate) +{ + rt_err_t err; + rt_ubase_t level; + rt_uint32_t val; + struct rk3x_i2c_calced_timings calc; + struct i2c_timings *timings = &i2c->timings; + + if ((err = i2c->soc_data->calc_timings(clk_rate, timings, &calc))) + { + LOG_W("Could not reach SCL freq %u", timings->bus_freq_hz); + } + + rt_clk_enable(i2c->pclk); + + level = rt_spin_lock_irqsave(&i2c->lock); + + val = i2c_readl(i2c, REG_CON); + val &= ~REG_CON_TUNING_MASK; + val |= calc.tuning; + i2c_writel(i2c, val, REG_CON); + i2c_writel(i2c, (calc.div_high << 16) | (calc.div_low & 0xffff), REG_CLKDIV); + + rt_spin_unlock_irqrestore(&i2c->lock, level); + + rt_clk_disable(i2c->pclk); +} + +static rt_err_t rk3x_i2c_clk_notifier(struct rt_clk_notifier *notifier, + rt_ubase_t msg, rt_ubase_t old_rate, rt_ubase_t new_rate) +{ + struct rk3x_i2c_calced_timings calc; + struct rk3x_i2c *i2c = rt_container_of(notifier, struct rk3x_i2c, clk_notifier); + + switch (msg) + { + case RT_CLK_MSG_PRE_RATE_CHANGE: + /* + * Try the calculation (but don't store the result) ahead of + * time to see if we need to block the clock change. Timings + * shouldn't actually take effect until rk3x_i2c_adapt_div(). + */ + if (i2c->soc_data->calc_timings(new_rate, &i2c->timings, &calc) != 0) + { + return -RT_EIO; + } + + /* scale up */ + if (new_rate > old_rate) + { + rk3x_i2c_adapt_div(i2c, new_rate); + } + break; + + case RT_CLK_MSG_POST_RATE_CHANGE: + /* scale down */ + if (new_rate < old_rate) + { + rk3x_i2c_adapt_div(i2c, new_rate); + } + break; + + case RT_CLK_MSG_ABORT_RATE_CHANGE: + /* scale up */ + if (new_rate > old_rate) + { + rk3x_i2c_adapt_div(i2c, old_rate); + } + break; + + default: + break; + } + + return RT_EOK; +} + +static const struct i2c_spec_values *rk3x_i2c_get_spec(rt_uint32_t speed) +{ + if (speed <= I2C_MAX_STANDARD_MODE_FREQ) + { + return &standard_mode_spec; + } + else if (speed <= I2C_MAX_FAST_MODE_FREQ) + { + return &fast_mode_spec; + } + else + { + return &fast_mode_plus_spec; + } +} + +/** + * rk3x_i2c_v0_calc_timings - Calculate divider values for desired SCL frequency + * @clk_rate: I2C input clock rate + * @t: Known I2C timing information + * @t_calc: Caculated rk3x private timings that would be written into regs + * + * Return: %0 on success, -%EINVAL if the goal SCL rate is too slow. In that case + * a best-effort divider value is returned in divs. If the target rate is + * too high, we silently use the highest possible rate. + */ +static rt_err_t rk3x_i2c_v0_calc_timings(rt_ubase_t clk_rate, + struct i2c_timings *t, struct rk3x_i2c_calced_timings *t_calc) +{ + rt_err_t err = RT_EOK; + rt_ubase_t min_low_ns, min_high_ns; + rt_ubase_t max_low_ns, min_total_ns; + rt_ubase_t clk_rate_khz, scl_rate_khz; + rt_ubase_t min_low_div, min_high_div; + rt_ubase_t max_low_div; + rt_ubase_t min_div_for_hold, min_total_div; + rt_ubase_t extra_div, extra_low_div, ideal_low_div; + rt_ubase_t data_hold_buffer_ns = 50; + const struct i2c_spec_values *spec; + + /* Only support standard-mode and fast-mode */ + if (t->bus_freq_hz > I2C_MAX_FAST_MODE_FREQ) + { + t->bus_freq_hz = I2C_MAX_FAST_MODE_FREQ; + } + + /* prevent scl_rate_khz from becoming 0 */ + if (t->bus_freq_hz < 1000) + { + t->bus_freq_hz = 1000; + } + + /* + * min_low_ns: The minimum number of ns we need to hold low to + * meet I2C specification, should include fall time. + * min_high_ns: The minimum number of ns we need to hold high to + * meet I2C specification, should include rise time. + * max_low_ns: The maximum number of ns we can hold low to meet + * I2C specification. + * + * Note: max_low_ns should be (maximum data hold time * 2 - buffer) + * This is because the i2c host on Rockchip holds the data line + * for half the low time. + */ + spec = rk3x_i2c_get_spec(t->bus_freq_hz); + min_high_ns = t->scl_rise_ns + spec->min_high_ns; + + /* + * Timings for repeated start: + * - controller appears to drop SDA at .875x (7/8) programmed clk high. + * - controller appears to keep SCL high for 2x programmed clk high. + * + * We need to account for those rules in picking our "high" time so + * we meet tSU;STA and tHD;STA times. + */ + min_high_ns = rt_max(min_high_ns, RT_DIV_ROUND_UP( + (t->scl_rise_ns + spec->min_setup_start_ns) * 1000, 875)); + min_high_ns = rt_max(min_high_ns, RT_DIV_ROUND_UP( + (t->scl_rise_ns + spec->min_setup_start_ns + t->sda_fall_ns + + spec->min_high_ns), 2)); + + min_low_ns = t->scl_fall_ns + spec->min_low_ns; + max_low_ns = spec->max_data_hold_ns * 2 - data_hold_buffer_ns; + min_total_ns = min_low_ns + min_high_ns; + + /* Adjust to avoid overflow */ + clk_rate_khz = RT_DIV_ROUND_UP(clk_rate, 1000); + scl_rate_khz = t->bus_freq_hz / 1000; + + /* + * We need the total div to be >= this number + * so we don't clock too fast. + */ + min_total_div = RT_DIV_ROUND_UP(clk_rate_khz, scl_rate_khz * 8); + + /* These are the min dividers needed for min hold times. */ + min_low_div = RT_DIV_ROUND_UP(clk_rate_khz * min_low_ns, 8 * 1000000); + min_high_div = RT_DIV_ROUND_UP(clk_rate_khz * min_high_ns, 8 * 1000000); + min_div_for_hold = (min_low_div + min_high_div); + + /* + * This is the maximum divider so we don't go over the maximum. + * We don't round up here (we round down) since this is a maximum. + */ + max_low_div = clk_rate_khz * max_low_ns / (8 * 1000000); + + if (min_low_div > max_low_div) + { + max_low_div = min_low_div; + } + + if (min_div_for_hold > min_total_div) + { + /* Time needed to meet hold requirements is important. Just use that. */ + t_calc->div_low = min_low_div; + t_calc->div_high = min_high_div; + } + else + { + /* + * We've got to distribute some time among the low and high + * so we don't run too fast. + */ + extra_div = min_total_div - min_div_for_hold; + + /* + * We'll try to split things up perfectly evenly, + * biasing slightly towards having a higher div + * for low (spend more time low). + */ + ideal_low_div = RT_DIV_ROUND_UP(clk_rate_khz * min_low_ns, + scl_rate_khz * 8 * min_total_ns); + + /* Don't allow it to go over the maximum */ + if (ideal_low_div > max_low_div) + { + ideal_low_div = max_low_div; + } + + /* Handle when the ideal low div is going to take up more than we have. */ + if (ideal_low_div > min_low_div + extra_div) + { + ideal_low_div = min_low_div + extra_div; + } + + /* Give low the "ideal" and give high whatever extra is left */ + extra_low_div = ideal_low_div - min_low_div; + t_calc->div_low = ideal_low_div; + t_calc->div_high = min_high_div + (extra_div - extra_low_div); + } + + /* + * Adjust to the fact that the hardware has an implicit "+1". + * NOTE: Above calculations always produce div_low > 0 and div_high > 0. + */ + --t_calc->div_low; + --t_calc->div_high; + + /* Give the tuning value 0, that would not update con register */ + t_calc->tuning = 0; + + /* Maximum divider supported by hw is 0xffff */ + if (t_calc->div_low > 0xffff) + { + t_calc->div_low = 0xffff; + err = -RT_EINVAL; + } + + if (t_calc->div_high > 0xffff) + { + t_calc->div_high = 0xffff; + err = -RT_EINVAL; + } + + return err; +} + +/** + * rk3x_i2c_v1_calc_timings - Calculate timing values for desired SCL frequency + * @clk_rate: I2C input clock rate + * @t: Known I2C timing information + * @t_calc: Caculated rk3x private timings that would be written into regs + * + * Return: %0 on success, -%EINVAL if the goal SCL rate is too slow. In that case + * a best-effort divider value is returned in divs. If the target rate is + * too high, we silently use the highest possible rate. + * The following formulas are v1's method to calculate timings. + * + * l = divl + 1; + * h = divh + 1; + * s = sda_update_config + 1; + * u = start_setup_config + 1; + * p = stop_setup_config + 1; + * T = Tclk_i2c; + * + * tHigh = 8 * h * T; + * tLow = 8 * l * T; + * + * tHD;sda = (l * s + 1) * T; + * tSU;sda = [(8 - s) * l + 1] * T; + * tI2C = 8 * (l + h) * T; + * + * tSU;sta = (8h * u + 1) * T; + * tHD;sta = [8h * (u + 1) - 1] * T; + * tSU;sto = (8h * p + 1) * T; + */ +static rt_err_t rk3x_i2c_v1_calc_timings(rt_ubase_t clk_rate, + struct i2c_timings *t, struct rk3x_i2c_calced_timings *t_calc) +{ + rt_err_t err = 0; + rt_ubase_t min_low_ns, min_high_ns; + rt_ubase_t min_setup_start_ns, min_setup_data_ns; + rt_ubase_t min_setup_stop_ns, max_hold_data_ns; + rt_ubase_t clk_rate_khz, scl_rate_khz; + rt_ubase_t min_low_div, min_high_div; + rt_ubase_t min_div_for_hold, min_total_div; + rt_ubase_t extra_div, extra_low_div; + rt_ubase_t sda_update_cfg, stp_sta_cfg, stp_sto_cfg; + const struct i2c_spec_values *spec; + + /* Support standard-mode, fast-mode and fast-mode plus */ + if (t->bus_freq_hz > I2C_MAX_FAST_MODE_PLUS_FREQ) + { + t->bus_freq_hz = I2C_MAX_FAST_MODE_PLUS_FREQ; + } + + /* prevent scl_rate_khz from becoming 0 */ + if (t->bus_freq_hz < 1000) + { + t->bus_freq_hz = 1000; + } + + /* + * min_low_ns: The minimum number of ns we need to hold low to + * meet I2C specification, should include fall time. + * min_high_ns: The minimum number of ns we need to hold high to + * meet I2C specification, should include rise time. + */ + spec = rk3x_i2c_get_spec(t->bus_freq_hz); + + /* calculate min-divh and min-divl */ + clk_rate_khz = RT_DIV_ROUND_UP(clk_rate, 1000); + scl_rate_khz = t->bus_freq_hz / 1000; + min_total_div = RT_DIV_ROUND_UP(clk_rate_khz, scl_rate_khz * 8); + + min_high_ns = t->scl_rise_ns + spec->min_high_ns; + min_high_div = RT_DIV_ROUND_UP(clk_rate_khz * min_high_ns, 8 * 1000000); + + min_low_ns = t->scl_fall_ns + spec->min_low_ns; + min_low_div = RT_DIV_ROUND_UP(clk_rate_khz * min_low_ns, 8 * 1000000); + + /* + * Final divh and divl must be greater than 0, otherwise the + * hardware would not output the i2c clk. + */ + min_high_div = (min_high_div < 1) ? 2 : min_high_div; + min_low_div = (min_low_div < 1) ? 2 : min_low_div; + + /* These are the min dividers needed for min hold times. */ + min_div_for_hold = (min_low_div + min_high_div); + + /* + * This is the maximum divider so we don't go over the maximum. + * We don't round up here (we round down) since this is a maximum. + */ + if (min_div_for_hold >= min_total_div) + { + /* + * Time needed to meet hold requirements is important. + * Just use that. + */ + t_calc->div_low = min_low_div; + t_calc->div_high = min_high_div; + } + else + { + /* + * We've got to distribute some time among the low and high + * so we don't run too fast. + * We'll try to split things up by the scale of min_low_div and + * min_high_div, biasing slightly towards having a higher div + * for low (spend more time low). + */ + extra_div = min_total_div - min_div_for_hold; + extra_low_div = RT_DIV_ROUND_UP(min_low_div * extra_div, min_div_for_hold); + + t_calc->div_low = min_low_div + extra_low_div; + t_calc->div_high = min_high_div + (extra_div - extra_low_div); + } + + /* + * calculate sda data hold count by the rules, data_upd_st:3 + * is a appropriate value to reduce calculated times. + */ + for (sda_update_cfg = 3; sda_update_cfg > 0; --sda_update_cfg) + { + max_hold_data_ns = RT_DIV_ROUND_UP((sda_update_cfg + * (t_calc->div_low) + 1) * 1000000, clk_rate_khz); + min_setup_data_ns = RT_DIV_ROUND_UP(((8 - sda_update_cfg) + * (t_calc->div_low) + 1) * 1000000, clk_rate_khz); + + if (max_hold_data_ns < spec->max_data_hold_ns && + min_setup_data_ns > spec->min_data_setup_ns) + { + break; + } + } + + /* calculate setup start config */ + min_setup_start_ns = t->scl_rise_ns + spec->min_setup_start_ns; + stp_sta_cfg = RT_DIV_ROUND_UP(clk_rate_khz * min_setup_start_ns - 1000000, + 8 * 1000000 * (t_calc->div_high)); + + /* calculate setup stop config */ + min_setup_stop_ns = t->scl_rise_ns + spec->min_setup_stop_ns; + stp_sto_cfg = RT_DIV_ROUND_UP(clk_rate_khz * min_setup_stop_ns - 1000000, + 8 * 1000000 * (t_calc->div_high)); + + t_calc->tuning = REG_CON_SDA_CFG(--sda_update_cfg) | + REG_CON_STA_CFG(--stp_sta_cfg) | REG_CON_STO_CFG(--stp_sto_cfg); + + --t_calc->div_low; + --t_calc->div_high; + + /* Maximum divider supported by hw is 0xffff */ + if (t_calc->div_low > 0xffff) + { + t_calc->div_low = 0xffff; + err = -RT_EINVAL; + } + + if (t_calc->div_high > 0xffff) + { + t_calc->div_high = 0xffff; + err = -RT_EINVAL; + } + + return err; +} + +/* Setup I2C registers for an I2C operation specified by msgs, num */ +static rt_ssize_t rk3x_i2c_setup(struct rk3x_i2c *i2c, struct rt_i2c_msg *msgs, + int num) +{ + rt_ssize_t res = 0; + rt_uint32_t addr = (msgs[0].addr & 0x7f) << 1; + + /* + * The I2C adapter can issue a small (len < 4) write packet before + * reading. This speeds up SMBus-style register reads. + * The MRXADDR/MRXRADDR hold the slave address and the slave register + * address in this case. + */ + + if (num >= 2 && msgs[0].len < 4 && + !(msgs[0].flags & RT_I2C_RD) && (msgs[1].flags & RT_I2C_RD)) + { + rt_uint32_t reg_addr = 0; + + LOG_D("Combined write/read from addr 0x%x", addr >> 1); + + /* Fill MRXRADDR with the register address(es) */ + for (int i = 0; i < msgs[0].len; ++i) + { + reg_addr |= msgs[0].buf[i] << (i * 8); + reg_addr |= REG_MRXADDR_VALID(i); + } + + /* msgs[0] is handled by hw. */ + i2c->msg = &msgs[1]; + i2c->mode = REG_CON_MOD_REGISTER_TX; + + i2c_writel(i2c, addr | REG_MRXADDR_VALID(0), REG_MRXADDR); + i2c_writel(i2c, reg_addr, REG_MRXRADDR); + + res = 2; + } + else + { + /* We'll have to do it the boring way and process the msgs one-by-one. */ + if (msgs[0].flags & RT_I2C_RD) + { + /* set read bit */ + addr |= 1; + + /* + * We have to transmit the slave addr first. Use + * MOD_REGISTER_TX for that purpose. + */ + i2c->mode = REG_CON_MOD_REGISTER_TX; + i2c_writel(i2c, addr | REG_MRXADDR_VALID(0), REG_MRXADDR); + i2c_writel(i2c, 0, REG_MRXRADDR); + } + else + { + i2c->mode = REG_CON_MOD_TX; + } + + i2c->msg = &msgs[0]; + + res = 1; + } + + i2c->addr = msgs[0].addr; + i2c->state = STATE_START; + i2c->processed = 0; + i2c->error = RT_EOK; + + rk3x_i2c_clean_ipd(i2c); + + return res; +} + +static rt_ssize_t rk3x_i2c_master_xfer(struct rt_i2c_bus_device *bus, + struct rt_i2c_msg msgs[], rt_uint32_t num) +{ + rt_ssize_t res = 0; + rt_uint32_t val; + rt_ubase_t level; + rt_err_t timeout_err; + struct rk3x_i2c *i2c = raw_to_rk3x_i2c(bus); + + level = rt_spin_lock_irqsave(&i2c->lock); + + rt_clk_enable(i2c->clk); + rt_clk_enable(i2c->pclk); + + i2c->is_last_msg = RT_FALSE; + + /* Process msgs */ + for (int i = 0; i < num; i += res) + { + res = rk3x_i2c_setup(i2c, msgs + i, num - i); + + if (res < 0) + { + LOG_E("%s setup failed", rt_dm_dev_get_name(&i2c->parent.parent)); + + break; + } + + if (i + res >= num) + { + i2c->is_last_msg = RT_TRUE; + } + + rt_spin_unlock_irqrestore(&i2c->lock, level); + + rk3x_i2c_start(i2c); + + timeout_err = rt_completion_wait(&i2c->done, rt_tick_from_millisecond(WAIT_TIMEOUT)); + + level = rt_spin_lock_irqsave(&i2c->lock); + + if (timeout_err) + { + LOG_E("timeout, ipd: 0x%02x, state: %d", i2c_readl(i2c, REG_IPD), i2c->state); + + /* Force a STOP condition without interrupt */ + i2c_writel(i2c, 0, REG_IEN); + val = i2c_readl(i2c, REG_CON) & REG_CON_TUNING_MASK; + val |= REG_CON_EN | REG_CON_STOP; + i2c_writel(i2c, val, REG_CON); + + i2c->state = STATE_IDLE; + + res = timeout_err; + break; + } + + if (i2c->error) + { + res = i2c->error; + + break; + } + } + + rt_clk_disable(i2c->pclk); + rt_clk_disable(i2c->clk); + + rt_spin_unlock_irqrestore(&i2c->lock, level); + + return res < 0 ? res : num; +} + +const static struct rt_i2c_bus_device_ops rk3x_i2c_ops = +{ + .master_xfer = rk3x_i2c_master_xfer, +}; + +static void rk3x_i2c_irq(int irqno, void *param) +{ + rt_uint32_t ipd; + struct rk3x_i2c *i2c = param; + + rt_spin_lock(&i2c->lock); + + ipd = i2c_readl(i2c, REG_IPD); + + if (i2c->state == STATE_IDLE) + { + LOG_W("IRQ in STATE_IDLE, ipd = 0x%x", ipd); + rk3x_i2c_clean_ipd(i2c); + + goto _out; + } + + LOG_D("IRQ: state %d, ipd: %x", i2c->state, ipd); + + /* Clean interrupt bits we don't care about */ + ipd &= ~(REG_INT_BRF | REG_INT_BTF); + + if (ipd & REG_INT_NAKRCV) + { + /* + * We got a NACK in the last operation. Depending on whether + * IGNORE_NAK is set, we have to stop the operation and report + * an error. + */ + i2c_writel(i2c, REG_INT_NAKRCV, REG_IPD); + + ipd &= ~REG_INT_NAKRCV; + + if (!(i2c->msg->flags & RT_I2C_IGNORE_NACK)) + { + LOG_E("Flags error"); + + rk3x_i2c_stop(i2c, -RT_EIO); + } + } + + /* is there anything left to handle? */ + if ((ipd & REG_INT_ALL) == 0) + { + goto _out; + } + + switch (i2c->state) + { + case STATE_START: + rk3x_i2c_handle_start(i2c, ipd); + break; + + case STATE_WRITE: + rk3x_i2c_handle_write(i2c, ipd); + break; + + case STATE_READ: + rk3x_i2c_handle_read(i2c, ipd); + break; + + case STATE_STOP: + rk3x_i2c_handle_stop(i2c, ipd); + break; + + case STATE_IDLE: + break; + } + +_out: + rt_spin_unlock(&i2c->lock); +} + +static void rk3x_i2c_free(struct rk3x_i2c *i2c) +{ + if (i2c->regs) + { + rt_iounmap(i2c->regs); + } + + if (!rt_is_err_or_null(i2c->clk)) + { + rt_clk_unprepare(i2c->clk); + rt_clk_put(i2c->clk); + } + + if (!rt_is_err_or_null(i2c->pclk)) + { + if (!rt_is_err_or_null(i2c->clk) && i2c->pclk != i2c->clk) + { + rt_clk_unprepare(i2c->pclk); + rt_clk_put(i2c->pclk); + } + } + + if (i2c->clk_notifier.callback) + { + rt_clk_notifier_unregister(i2c->clk, &i2c->clk_notifier); + } + + rt_free(i2c); +} + +static rt_err_t rk3x_i2c_probe(struct rt_platform_device *pdev) +{ + rt_err_t err; + const char *dev_name; + struct rt_device *dev = &pdev->parent; + struct rk3x_i2c *i2c = rt_calloc(1, sizeof(*i2c)); + + if (!i2c) + { + return -RT_ENOMEM; + } + + i2c->soc_data = pdev->id->data; + i2c_timings_ofw_parse(dev->ofw_node, &i2c->timings, RT_TRUE); + + i2c->regs = rt_dm_dev_iomap(dev, 0); + + if (!i2c->regs) + { + err = -RT_EIO; + + goto _fail; + } + + i2c->irq = rt_dm_dev_get_irq(dev, 0); + + if (i2c->irq < 0) + { + err = i2c->irq; + + goto _fail; + } + + if (i2c->soc_data->grf_offset >= 0) + { + rt_uint32_t value; + struct rt_syscon *grf; + struct rt_ofw_node *np = dev->ofw_node; + int id = rt_ofw_get_alias_id(np, "i2c"); + + if (id < 0) + { + LOG_E("alias id not found"); + + goto _fail; + } + + grf = rt_syscon_find_by_ofw_phandle(np, "rockchip,grf"); + + if (!grf) + { + err = -RT_EIO; + LOG_E("I2C%d %s not found", id, "rockchip,grf"); + + goto _fail; + } + + /* 27+i: write mask, 11+i: value */ + value = RT_BIT(27 + id) | RT_BIT(11 + id); + + if ((err = rt_syscon_write(grf, i2c->soc_data->grf_offset, value))) + { + LOG_E("Could not write to GRF: %s", rt_strerror(err)); + + goto _fail; + } + } + + if (i2c->soc_data->calc_timings == rk3x_i2c_v0_calc_timings) + { + i2c->clk = rt_clk_get_by_index(dev, 0); + i2c->pclk = i2c->clk; + } + else + { + i2c->clk = rt_clk_get_by_name(dev, "i2c"); + i2c->pclk = rt_clk_get_by_name(dev, "pclk"); + } + + if (rt_is_err(i2c->clk)) + { + err = rt_ptr_err(i2c->clk); + + goto _fail; + } + + if ((err = rt_clk_prepare(i2c->clk))) + { + goto _fail; + } + + if (rt_is_err(i2c->pclk)) + { + err = rt_ptr_err(i2c->pclk); + + goto _fail; + } + + if ((err = rt_clk_prepare(i2c->pclk))) + { + goto _fail; + } + + i2c->clk_notifier.callback = rk3x_i2c_clk_notifier; + if ((err = rt_clk_notifier_register(i2c->clk, &i2c->clk_notifier))) + { + goto _fail; + } + + if ((err = rt_clk_enable(i2c->clk))) + { + LOG_E("Can't enable bus clk: %s", rt_strerror(err)); + + goto _fail; + } + + rk3x_i2c_adapt_div(i2c, rt_clk_get_rate(i2c->clk)); + rt_clk_disable(i2c->clk); + + rt_pin_ctrl_confs_apply_by_name(dev, RT_NULL); + + rt_spin_lock_init(&i2c->lock); + rt_completion_init(&i2c->done); + + rt_dm_dev_set_name_auto(&i2c->parent.parent, "i2c"); + dev_name = rt_dm_dev_get_name(&i2c->parent.parent); + + rt_hw_interrupt_install(i2c->irq, rk3x_i2c_irq, i2c, dev_name); + rt_hw_interrupt_umask(i2c->irq); + + dev->user_data = i2c; + + i2c->parent.ops = &rk3x_i2c_ops; + i2c->parent.parent.ofw_node = dev->ofw_node; + + rt_i2c_bus_device_register(&i2c->parent, dev_name); + + rt_dm_dev_bind_fwdata(dev, RT_NULL, i2c); + + return RT_EOK; + +_fail: + rk3x_i2c_free(i2c); + + return err; +} + +static rt_err_t rk3x_i2c_remove(struct rt_platform_device *pdev) +{ + struct rk3x_i2c *i2c =pdev->parent.user_data; + + rt_device_unregister(&i2c->parent.parent); + + rk3x_i2c_free(i2c); + + return RT_EOK; +} + +static const struct rk3x_i2c_soc_data rv1108_soc_data = +{ + .grf_offset = -1, + .calc_timings = rk3x_i2c_v1_calc_timings, +}; + +static const struct rk3x_i2c_soc_data rv1126_soc_data = +{ + .grf_offset = 0x118, + .calc_timings = rk3x_i2c_v1_calc_timings, +}; + +static const struct rk3x_i2c_soc_data rk3066_soc_data = +{ + .grf_offset = 0x154, + .calc_timings = rk3x_i2c_v0_calc_timings, +}; + +static const struct rk3x_i2c_soc_data rk3188_soc_data = +{ + .grf_offset = 0x0a4, + .calc_timings = rk3x_i2c_v0_calc_timings, +}; + +static const struct rk3x_i2c_soc_data rk3228_soc_data = +{ + .grf_offset = -1, + .calc_timings = rk3x_i2c_v0_calc_timings, +}; + +static const struct rk3x_i2c_soc_data rk3288_soc_data = +{ + .grf_offset = -1, + .calc_timings = rk3x_i2c_v0_calc_timings, +}; + +static const struct rk3x_i2c_soc_data rk3399_soc_data = +{ + .grf_offset = -1, + .calc_timings = rk3x_i2c_v1_calc_timings, +}; + +static const struct rt_ofw_node_id rk3x_i2c_ofw_ids[] = +{ + { .compatible = "rockchip,rv1108-i2c", .data = &rv1108_soc_data }, + { .compatible = "rockchip,rv1126-i2c", .data = &rv1126_soc_data }, + { .compatible = "rockchip,rk3066-i2c", .data = &rk3066_soc_data }, + { .compatible = "rockchip,rk3188-i2c", .data = &rk3188_soc_data }, + { .compatible = "rockchip,rk3228-i2c", .data = &rk3228_soc_data }, + { .compatible = "rockchip,rk3288-i2c", .data = &rk3288_soc_data }, + { .compatible = "rockchip,rk3399-i2c", .data = &rk3399_soc_data }, + { /* sentinel */ } +}; + +static struct rt_platform_driver rk3x_i2c_driver = +{ + .name = "rk3x-i2c", + .ids = rk3x_i2c_ofw_ids, + + .probe = rk3x_i2c_probe, + .remove = rk3x_i2c_remove, +}; +RT_PLATFORM_DRIVER_EXPORT(rk3x_i2c_driver); diff --git a/components/drivers/i2c/i2c_bus.c b/components/drivers/i2c/i2c_bus.c new file mode 100644 index 000000000000..335def602e8b --- /dev/null +++ b/components/drivers/i2c/i2c_bus.c @@ -0,0 +1,182 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-12-06 GuEe-GUI first version + */ + +#include "i2c_dm.h" + +#define DBG_TAG "i2c.bus" +#define DBG_LVL DBG_INFO +#include + +static struct rt_bus i2c_bus; + +void i2c_bus_scan_clients(struct rt_i2c_bus_device *bus) +{ +#ifdef RT_USING_OFW + if (bus->parent.ofw_node) + { + struct rt_ofw_node *np = bus->parent.ofw_node, *child_np, *i2c_client_np; + + rt_ofw_foreach_available_child_node(np, child_np) + { + rt_uint32_t client_addr; + struct rt_i2c_client *client; + + if (rt_ofw_prop_read_bool(child_np, "compatible")) + { + i2c_client_np = child_np; + } + else + { + /* Maybe in i2c-mux */ + i2c_client_np = rt_ofw_get_next_child(child_np, RT_NULL); + + if (!rt_ofw_prop_read_bool(i2c_client_np, "compatible")) + { + continue; + } + } + + client = rt_calloc(1, sizeof(*client)); + + if (!client) + { + rt_ofw_node_put(i2c_client_np); + LOG_E("Not memory to create i2c client: %s", + rt_ofw_node_full_name(i2c_client_np)); + + return; + } + + rt_ofw_prop_read_u32(i2c_client_np, "reg", &client_addr); + + client->parent.ofw_node = i2c_client_np; + client->name = rt_ofw_node_name(i2c_client_np); + client->bus = bus; + client->client_addr = client_addr; + + rt_i2c_device_register(client); + + if (i2c_client_np != child_np) + { + rt_ofw_node_put(i2c_client_np); + } + } + } +#endif /* RT_USING_OFW */ +} + +rt_err_t rt_i2c_driver_register(struct rt_i2c_driver *driver) +{ + RT_ASSERT(driver != RT_NULL); + + driver->parent.bus = &i2c_bus; + + return rt_driver_register(&driver->parent); +} + +rt_err_t rt_i2c_device_register(struct rt_i2c_client *client) +{ + RT_ASSERT(client != RT_NULL); + + return rt_bus_add_device(&i2c_bus, &client->parent); +} + +static rt_bool_t i2c_match(rt_driver_t drv, rt_device_t dev) +{ + const struct rt_i2c_device_id *id; + struct rt_i2c_driver *driver = rt_container_of(drv, struct rt_i2c_driver, parent); + struct rt_i2c_client *client = rt_container_of(dev, struct rt_i2c_client, parent); + + if ((id = driver->ids)) + { + for (; id->name[0]; ++id) + { + if (!rt_strcmp(id->name, client->name)) + { + client->id = id; + client->ofw_id = RT_NULL; + + return RT_TRUE; + } + } + } + +#ifdef RT_USING_OFW + client->ofw_id = rt_ofw_node_match(client->parent.ofw_node, driver->ofw_ids); + + if (client->ofw_id) + { + client->id = RT_NULL; + + return RT_TRUE; + } +#endif + + return RT_FALSE; +} + +static rt_err_t i2c_probe(rt_device_t dev) +{ + rt_err_t err; + struct rt_i2c_driver *driver = rt_container_of(dev->drv, struct rt_i2c_driver, parent); + struct rt_i2c_client *client = rt_container_of(dev, struct rt_i2c_client, parent); + + if (!client->bus) + { + return -RT_EINVAL; + } + + err = driver->probe(client); + + return err; +} + +static rt_err_t i2c_remove(rt_device_t dev) +{ + struct rt_i2c_driver *driver = rt_container_of(dev->drv, struct rt_i2c_driver, parent); + struct rt_i2c_client *client = rt_container_of(dev, struct rt_i2c_client, parent); + + if (driver && driver->remove) + { + driver->remove(client); + } + + return RT_EOK; +} + +static rt_err_t i2c_shutdown(rt_device_t dev) +{ + struct rt_i2c_driver *driver = rt_container_of(dev->drv, struct rt_i2c_driver, parent); + struct rt_i2c_client *client = rt_container_of(dev, struct rt_i2c_client, parent); + + if (driver && driver->shutdown) + { + driver->shutdown(client); + } + + return RT_EOK; +} + +static struct rt_bus i2c_bus = +{ + .name = "i2c", + .match = i2c_match, + .probe = i2c_probe, + .remove = i2c_remove, + .shutdown = i2c_shutdown, +}; + +static int i2c_bus_init(void) +{ + rt_bus_register(&i2c_bus); + + return 0; +} +INIT_CORE_EXPORT(i2c_bus_init); diff --git a/components/drivers/i2c/i2c_core.c b/components/drivers/i2c/i2c_core.c index 17671ea24182..324731f679f1 100644 --- a/components/drivers/i2c/i2c_core.c +++ b/components/drivers/i2c/i2c_core.c @@ -19,6 +19,10 @@ #endif #include +#ifdef RT_USING_DM +#include "i2c_dm.h" +#endif + rt_err_t rt_i2c_bus_device_register(struct rt_i2c_bus_device *bus, const char *bus_name) { @@ -32,6 +36,13 @@ rt_err_t rt_i2c_bus_device_register(struct rt_i2c_bus_device *bus, LOG_I("I2C bus [%s] registered", bus_name); +#ifdef RT_USING_DM + if (!res) + { + i2c_bus_scan_clients(bus); + } +#endif + return res; } diff --git a/components/drivers/i2c/i2c_dm.c b/components/drivers/i2c/i2c_dm.c new file mode 100644 index 000000000000..31e8e0e74161 --- /dev/null +++ b/components/drivers/i2c/i2c_dm.c @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-12-06 GuEe-GUI first version + */ + +#include "i2c_dm.h" + +#define DBG_TAG "i2c.dm" +#define DBG_LVL DBG_INFO +#include + +#ifdef RT_USING_OFW +static void i2c_parse_timing(struct rt_ofw_node *dev_np, const char *propname, + rt_uint32_t *out_value, rt_uint32_t def_value, rt_bool_t use_defaults) +{ + if (rt_ofw_prop_read_u32(dev_np, propname, out_value) && use_defaults) + { + *out_value = def_value; + } +} + +rt_err_t i2c_timings_ofw_parse(struct rt_ofw_node *dev_np, struct i2c_timings *timings, + rt_bool_t use_defaults) +{ + rt_ubase_t def; + rt_bool_t udef = use_defaults; + struct i2c_timings *t = timings; + + i2c_parse_timing(dev_np, "clock-frequency", &t->bus_freq_hz, I2C_MAX_STANDARD_MODE_FREQ, udef); + + def = t->bus_freq_hz <= I2C_MAX_STANDARD_MODE_FREQ ? 1000 : t->bus_freq_hz <= I2C_MAX_FAST_MODE_FREQ ? 300 : 120; + i2c_parse_timing(dev_np, "i2c-scl-rising-time-ns", &t->scl_rise_ns, def, udef); + + def = t->bus_freq_hz <= I2C_MAX_FAST_MODE_FREQ ? 300 : 120; + i2c_parse_timing(dev_np, "i2c-scl-falling-time-ns", &t->scl_fall_ns, def, udef); + + i2c_parse_timing(dev_np, "i2c-scl-internal-delay-ns", &t->scl_int_delay_ns, 0, udef); + i2c_parse_timing(dev_np, "i2c-sda-falling-time-ns", &t->sda_fall_ns, t->scl_fall_ns, udef); + i2c_parse_timing(dev_np, "i2c-sda-hold-time-ns", &t->sda_hold_ns, 0, udef); + i2c_parse_timing(dev_np, "i2c-digital-filter-width-ns", &t->digital_filter_width_ns, 0, udef); + i2c_parse_timing(dev_np, "i2c-analog-filter-cutoff-frequency", &t->analog_filter_cutoff_freq_hz, 0, udef); + + return RT_EOK; +} +#endif /* RT_USING_OFW */ diff --git a/components/drivers/i2c/i2c_dm.h b/components/drivers/i2c/i2c_dm.h new file mode 100644 index 000000000000..8b3ca59d3dc3 --- /dev/null +++ b/components/drivers/i2c/i2c_dm.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-11-26 GuEe-GUI first version + */ + +#ifndef __I2C_DM_H__ +#define __I2C_DM_H__ + +#include +#include +#include + +/* I2C Frequency Modes */ +#define I2C_MAX_STANDARD_MODE_FREQ 100000 +#define I2C_MAX_FAST_MODE_FREQ 400000 +#define I2C_MAX_FAST_MODE_PLUS_FREQ 1000000 +#define I2C_MAX_TURBO_MODE_FREQ 1400000 +#define I2C_MAX_HIGH_SPEED_MODE_FREQ 3400000 +#define I2C_MAX_ULTRA_FAST_MODE_FREQ 5000000 + +struct i2c_timings +{ + rt_uint32_t bus_freq_hz; /* the bus frequency in Hz */ + rt_uint32_t scl_rise_ns; /* time SCL signal takes to rise in ns; t(r) in the I2C specification */ + rt_uint32_t scl_fall_ns; /* time SCL signal takes to fall in ns; t(f) in the I2C specification */ + rt_uint32_t scl_int_delay_ns; /* time IP core additionally needs to setup SCL in ns */ + rt_uint32_t sda_fall_ns; /* time SDA signal takes to fall in ns; t(f) in the I2C specification */ + rt_uint32_t sda_hold_ns; /* time IP core additionally needs to hold SDA in ns */ + rt_uint32_t digital_filter_width_ns; /* width in ns of spikes on i2c lines that the IP core digital filter can filter out */ + rt_uint32_t analog_filter_cutoff_freq_hz; /* threshold frequency for the low pass IP core analog filter */ +}; + +#ifdef RT_USING_OFW +rt_err_t i2c_timings_ofw_parse(struct rt_ofw_node *dev_np, struct i2c_timings *timings, + rt_bool_t use_defaults); +#else +rt_inline rt_err_t i2c_timings_ofw_parse(struct rt_ofw_node *dev_np, struct i2c_timings *timings, + rt_bool_t use_defaults) +{ + return RT_EOK; +} +#endif /* RT_USING_OFW */ + +void i2c_bus_scan_clients(struct rt_i2c_bus_device *bus); + +#endif /* __I2C_DM_H__ */ diff --git a/components/drivers/include/drivers/blk.h b/components/drivers/include/drivers/blk.h new file mode 100644 index 000000000000..b8bff02ac7e9 --- /dev/null +++ b/components/drivers/include/drivers/blk.h @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-02-25 GuEe-GUI first version + */ + +#ifndef __BLK_H__ +#define __BLK_H__ + +#include + +struct rt_blk_disk_ops; + +struct rt_blk_disk +{ + struct rt_device parent; + + const struct rt_blk_disk_ops *ops; + + rt_uint32_t read_only:1; + rt_uint32_t parallel_io:1; + + rt_uint32_t partitions; +#define RT_BLK_PARTITION_NONE (-1) +#define RT_BLK_PARTITION_MAX (RT_UINT32_MAX >> 1) + rt_int32_t max_partitions; + rt_list_t part_nodes; + + struct rt_spinlock lock; + struct rt_semaphore usr_lock; +}; + +struct rt_blk_disk_ops +{ + rt_ssize_t (*read)(struct rt_blk_disk *, rt_off_t sector, void *buffer, + rt_size_t sector_count); + rt_ssize_t (*write)(struct rt_blk_disk *, rt_off_t sector, const void *buffer, + rt_size_t sector_count); + rt_err_t (*getgeome)(struct rt_blk_disk *, struct rt_device_blk_geometry *); + rt_err_t (*sync)(struct rt_blk_disk *); + rt_err_t (*erase)(struct rt_blk_disk *); + rt_err_t (*autorefresh)(struct rt_blk_disk *, rt_bool_t); +}; + +#ifndef __DFS_H__ +#include + +struct rt_blk_device +{ + struct rt_device parent; + + int partno; + struct dfs_partition partition; + + rt_list_t list; + struct rt_blk_disk *disk; + + rt_size_t sector_start; + rt_size_t sector_count; +}; +#else +struct rt_blk_device; +#endif /* __DFS_H__ */ + +rt_err_t rt_hw_blk_disk_register(struct rt_blk_disk *disk); +rt_err_t rt_hw_blk_disk_unregister(struct rt_blk_disk *disk); + +rt_err_t rt_blk_disk_probe_partition(struct rt_blk_disk *disk); +rt_ssize_t rt_blk_disk_get_capacity(struct rt_blk_disk *disk); +rt_ssize_t rt_blk_disk_get_logical_block_size(struct rt_blk_disk *disk); + +#endif /* __BLK_H__ */ diff --git a/components/drivers/include/drivers/clk.h b/components/drivers/include/drivers/clk.h index 524fd6ebf694..fc211ce3f836 100644 --- a/components/drivers/include/drivers/clk.h +++ b/components/drivers/include/drivers/clk.h @@ -17,35 +17,79 @@ #include struct rt_clk_ops; +struct rt_reset_control_node; -struct rt_clk +struct rt_clk_node { + /* + * Defined as the array like this if if the CLK have multi out clocks: + * + * struct XYZ_single_clk + * { + * struct rt_clk_node parent; + * ... + * }; + * + * struct XYZ_multi_clk + * { + * struct rt_clk_node parent[N]; + * ... + * }; + * We assume the 'N' is the max value of element in 'clock-indices' if OFW. + */ rt_list_t list; rt_list_t children_nodes; const char *name; const struct rt_clk_ops *ops; - struct rt_clk *parent; + struct rt_clk_node *parent; struct ref ref; rt_ubase_t rate; rt_ubase_t min_rate; rt_ubase_t max_rate; - void *sysdata; + rt_size_t notifier_count; + + void *priv; + + struct rt_clk *clk; + rt_size_t multi_clk; }; struct rt_clk_fixed_rate { - struct rt_clk clk; + struct rt_clk_node clk; rt_ubase_t fixed_rate; rt_ubase_t fixed_accuracy; }; +struct rt_clk +{ + struct rt_clk_node *clk_np; + + const char *dev_id; + const char *con_id; + + rt_ubase_t rate; + + void *fw_node; + void *priv; +}; + +struct rt_clk_array +{ + rt_size_t count; + struct rt_clk *clks[]; +}; + struct rt_clk_ops { + rt_err_t (*init)(struct rt_clk *, void *fw_data); + rt_err_t (*finit)(struct rt_clk *); + /* API */ rt_err_t (*prepare)(struct rt_clk *); void (*unprepare)(struct rt_clk *); rt_bool_t (*is_prepared)(struct rt_clk *); @@ -53,13 +97,37 @@ struct rt_clk_ops void (*disable)(struct rt_clk *); rt_bool_t (*is_enabled)(struct rt_clk *); rt_err_t (*set_rate)(struct rt_clk *, rt_ubase_t rate, rt_ubase_t parent_rate); + rt_err_t (*set_parent)(struct rt_clk *, struct rt_clk *parent); + rt_err_t (*set_phase)(struct rt_clk *, int degrees); + rt_base_t (*get_phase)(struct rt_clk *); + rt_base_t (*round_rate)(struct rt_clk *, rt_ubase_t drate, rt_ubase_t *prate); }; -rt_err_t rt_clk_register(struct rt_clk *clk, struct rt_clk *parent); -rt_err_t rt_clk_unregister(struct rt_clk *clk); +struct rt_clk_notifier; -void rt_clk_put(struct rt_clk *clk); -struct rt_clk *rt_clk_get_parent(struct rt_clk *clk); +#define RT_CLK_MSG_PRE_RATE_CHANGE RT_BIT(0) +#define RT_CLK_MSG_POST_RATE_CHANGE RT_BIT(1) +#define RT_CLK_MSG_ABORT_RATE_CHANGE RT_BIT(2) + +typedef rt_err_t (*rt_clk_notifier_callback)(struct rt_clk_notifier *notifier, + rt_ubase_t msg, rt_ubase_t old_rate, rt_ubase_t new_rate); + +struct rt_clk_notifier +{ + rt_list_t list; + + struct rt_clk *clk; + rt_clk_notifier_callback callback; + void *priv; +}; + +rt_err_t rt_clk_register(struct rt_clk_node *clk_np, struct rt_clk_node *parent_np); +rt_err_t rt_clk_unregister(struct rt_clk_node *clk_np); + +rt_err_t rt_clk_notifier_register(struct rt_clk *clk, struct rt_clk_notifier *notifier); +rt_err_t rt_clk_notifier_unregister(struct rt_clk *clk, struct rt_clk_notifier *notifier); + +rt_err_t rt_clk_set_parent(struct rt_clk *clk, struct rt_clk *clk_parent); rt_err_t rt_clk_prepare(struct rt_clk *clk); rt_err_t rt_clk_unprepare(struct rt_clk *clk); @@ -70,24 +138,52 @@ void rt_clk_disable(struct rt_clk *clk); rt_err_t rt_clk_prepare_enable(struct rt_clk *clk); void rt_clk_disable_unprepare(struct rt_clk *clk); +rt_err_t rt_clk_array_prepare(struct rt_clk_array *clk_arr); +rt_err_t rt_clk_array_unprepare(struct rt_clk_array *clk_arr); + +rt_err_t rt_clk_array_enable(struct rt_clk_array *clk_arr); +void rt_clk_array_disable(struct rt_clk_array *clk_arr); + +rt_err_t rt_clk_array_prepare_enable(struct rt_clk_array *clk_arr); +void rt_clk_array_disable_unprepare(struct rt_clk_array *clk_arr); + rt_err_t rt_clk_set_rate_range(struct rt_clk *clk, rt_ubase_t min, rt_ubase_t max); rt_err_t rt_clk_set_min_rate(struct rt_clk *clk, rt_ubase_t rate); rt_err_t rt_clk_set_max_rate(struct rt_clk *clk, rt_ubase_t rate); rt_err_t rt_clk_set_rate(struct rt_clk *clk, rt_ubase_t rate); rt_ubase_t rt_clk_get_rate(struct rt_clk *clk); +rt_err_t rt_clk_set_phase(struct rt_clk *clk, int degrees); +rt_base_t rt_clk_get_phase(struct rt_clk *clk); + +rt_base_t rt_clk_round_rate(struct rt_clk *clk, rt_ubase_t rate); + +struct rt_clk *rt_clk_get_parent(struct rt_clk *clk); + +struct rt_clk_array *rt_clk_get_array(struct rt_device *dev); +struct rt_clk *rt_clk_get_by_index(struct rt_device *dev, int index); +struct rt_clk *rt_clk_get_by_name(struct rt_device *dev, const char *name); +void rt_clk_array_put(struct rt_clk_array *clk_arr); +void rt_clk_put(struct rt_clk *clk); + #ifdef RT_USING_OFW +struct rt_clk_array *rt_ofw_get_clk_array(struct rt_ofw_node *np); struct rt_clk *rt_ofw_get_clk(struct rt_ofw_node *np, int index); struct rt_clk *rt_ofw_get_clk_by_name(struct rt_ofw_node *np, const char *name); +rt_ssize_t rt_ofw_count_of_clk(struct rt_ofw_node *clk_ofw_np); #else -struct rt_clk *rt_ofw_get_clk(struct rt_ofw_node *np, int index) +rt_inline struct rt_clk *rt_ofw_get_clk(struct rt_ofw_node *np, int index) { return RT_NULL; } -struct rt_clk *rt_ofw_get_clk_by_name(struct rt_ofw_node *np, const char *name) +rt_inline struct rt_clk *rt_ofw_get_clk_by_name(struct rt_ofw_node *np, const char *name) { return RT_NULL; } +rt_inline rt_ssize_t rt_ofw_count_of_clk(struct rt_ofw_node *clk_ofw_np) +{ + return 0; +} #endif /* RT_USING_OFW */ #endif /* __CLK_H__ */ diff --git a/components/drivers/include/drivers/core/bus.h b/components/drivers/include/drivers/core/bus.h index dd12cf6dbad5..b78bb2c285dd 100644 --- a/components/drivers/include/drivers/core/bus.h +++ b/components/drivers/include/drivers/core/bus.h @@ -12,6 +12,7 @@ #define __BUS_H__ #include +#include #include #include @@ -20,25 +21,28 @@ typedef struct rt_bus *rt_bus_t; struct rt_bus { struct rt_object parent; /**< inherit from rt_object */ +#ifdef RT_USING_DFS_DIRECTFS + struct rt_object dev_dir; + struct rt_object drv_dir; +#endif - char *name; - struct rt_bus *bus; + const char *name; rt_list_t list; - rt_list_t children; rt_list_t dev_list; rt_list_t drv_list; - struct rt_spinlock spinlock; + struct rt_spinlock dev_lock; + struct rt_spinlock drv_lock; rt_bool_t (*match)(rt_driver_t drv, rt_device_t dev); rt_err_t (*probe)(rt_device_t dev); + rt_err_t (*remove)(rt_device_t dev); + rt_err_t (*shutdown)(rt_device_t dev); }; -rt_bus_t rt_bus_root(void); - -rt_err_t rt_bus_for_each_dev(rt_bus_t bus, rt_driver_t drv, int (*fn)(rt_driver_t drv, rt_device_t dev)); -rt_err_t rt_bus_for_each_drv(rt_bus_t bus, rt_device_t dev, int (*fn)(rt_driver_t drv, rt_device_t dev)); +rt_err_t rt_bus_for_each_dev(rt_bus_t bus, void *data, int (*fn)(rt_device_t dev, void *)); +rt_err_t rt_bus_for_each_drv(rt_bus_t bus, void *data, int (*fn)(rt_driver_t drv, void *)); rt_err_t rt_bus_add(rt_bus_t bus); rt_err_t rt_bus_add_driver(rt_bus_t bus, rt_driver_t drv); @@ -46,7 +50,9 @@ rt_err_t rt_bus_add_device(rt_bus_t bus, rt_device_t dev); rt_err_t rt_bus_remove_driver(rt_driver_t drv); rt_err_t rt_bus_remove_device(rt_device_t dev); -rt_bus_t rt_bus_find_by_name(char *name); +rt_err_t rt_bus_shutdown(void); + +rt_bus_t rt_bus_find_by_name(const char *name); rt_err_t rt_bus_reload_driver_device(rt_bus_t new_bus, rt_device_t dev); rt_err_t rt_bus_register(rt_bus_t bus); diff --git a/components/drivers/include/drivers/core/device.h b/components/drivers/include/drivers/core/device.h index 87019a37972e..ec98dbc51430 100644 --- a/components/drivers/include/drivers/core/device.h +++ b/components/drivers/include/drivers/core/device.h @@ -31,13 +31,14 @@ struct rt_device_notify struct rt_device { struct rt_object parent; /**< inherit from rt_object */ + rt_list_t node; struct rt_bus *bus; - void *priv; #ifdef RT_USING_DM rt_driver_t drv; void *ofw_node; + void *power_domain_unit; #endif enum rt_device_class_type type; /**< device type */ rt_uint16_t flag; /**< device flag */ diff --git a/components/drivers/include/drivers/core/driver.h b/components/drivers/include/drivers/core/driver.h index 60661a2cfb40..f708e11f26c9 100644 --- a/components/drivers/include/drivers/core/driver.h +++ b/components/drivers/include/drivers/core/driver.h @@ -15,8 +15,12 @@ struct rt_driver { - struct rt_bus *bus; + struct rt_object parent; + rt_list_t node; + struct rt_bus *bus; + + rt_uint32_t ref_count; #ifdef RT_USING_DEVICE_OPS const struct rt_device_ops *dev_ops; @@ -29,15 +33,11 @@ struct rt_driver rt_ssize_t (*write) (rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size); rt_err_t (*control)(rt_device_t dev, int cmd, void *args); #endif - const struct filesystem_ops *fops; - const char *name; - int (*probe)(struct rt_device *dev); int (*remove)(struct rt_device *dev); - - void *priv; + int (*shutdown)(struct rt_device *dev); }; int rt_driver_probe_device(struct rt_driver *drv, struct rt_device *dev); diff --git a/components/drivers/include/drivers/core/power.h b/components/drivers/include/drivers/core/power.h new file mode 100644 index 000000000000..c056d97e2802 --- /dev/null +++ b/components/drivers/include/drivers/core/power.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-09-24 GuEe-GUI the first version + */ + +#ifndef __RT_DM_POWER_H__ +#define __RT_DM_POWER_H__ + +#include + +enum rt_dm_power_off_mode +{ + RT_DM_POWER_OFF_MODE_SHUTDOWN, + RT_DM_POWER_OFF_MODE_RESET, + + RT_DM_POWER_OFF_MODE_NR, +}; + +enum rt_dm_power_off_priority +{ + RT_DM_POWER_OFF_PRIO_PLATFORM, + RT_DM_POWER_OFF_PRIO_LOW, + RT_DM_POWER_OFF_PRIO_DEFAULT, + RT_DM_POWER_OFF_PRIO_HIGH, + RT_DM_POWER_OFF_PRIO_FIRMWARE, + + RT_DM_POWER_OFF_PRIO_NR, +}; + +rt_err_t rt_dm_power_off_handler(struct rt_device *dev, int mode, int priority, + rt_err_t (*callback)(struct rt_device *)); + +rt_err_t rt_dm_reboot_mode_register(struct rt_device *dev, + rt_err_t (*callback)(struct rt_device *, char *cmd)); + +void rt_hw_cpu_reset_mode(char *cmd); + +extern void (*rt_dm_machine_shutdown)(void); +extern void (*rt_dm_machine_reset)(void); + +#endif /* __RT_DM_POWER_H__ */ diff --git a/components/drivers/include/drivers/core/power_domain.h b/components/drivers/include/drivers/core/power_domain.h new file mode 100644 index 000000000000..ab4923bcc014 --- /dev/null +++ b/components/drivers/include/drivers/core/power_domain.h @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-11-21 GuEe-GUI first version + */ + +#ifndef __RT_DM_POWER_DOMAIN_H__ +#define __RT_DM_POWER_DOMAIN_H__ + +#include +#include + +#include + +struct rt_dm_power_domain_unit +{ + rt_list_t list; + + int id; + struct rt_dm_power_domain *domain; +}; + +struct rt_dm_power_domain_proxy +{ + struct rt_object parent; + + struct rt_dm_power_domain *(*ofw_parse)(struct rt_dm_power_domain_proxy *, + struct rt_ofw_cell_args *); +}; + +struct rt_dm_power_domain +{ + struct rt_object parent; + + struct rt_device *dev; + struct rt_dm_power_domain *parent_domain; + + rt_list_t list; + rt_list_t child_nodes; + rt_list_t unit_nodes; + + struct ref ref; + struct rt_spinlock lock; + + rt_err_t (*power_on)(struct rt_dm_power_domain *); + rt_err_t (*power_off)(struct rt_dm_power_domain *); + rt_err_t (*attach_dev)(struct rt_dm_power_domain *, struct rt_device *); + rt_err_t (*detach_dev)(struct rt_dm_power_domain *, struct rt_device *); + + void *pirv; +}; + +void rt_dm_power_domain_proxy_default_name(struct rt_dm_power_domain_proxy *proxy); +void rt_dm_power_domain_proxy_ofw_bind(struct rt_dm_power_domain_proxy *proxy, + struct rt_ofw_node *np); + +rt_err_t rt_dm_power_domain_register(struct rt_dm_power_domain *domain); +rt_err_t rt_dm_power_domain_unregister(struct rt_dm_power_domain *domain); + +rt_err_t rt_dm_power_domain_register_child(struct rt_dm_power_domain *domain, + struct rt_dm_power_domain *child_domain); +rt_err_t rt_dm_power_domain_unregister_child(struct rt_dm_power_domain *domain, + struct rt_dm_power_domain *child_domain); + +rt_err_t rt_dm_power_domain_power_on(struct rt_dm_power_domain *domain); +rt_err_t rt_dm_power_domain_power_off(struct rt_dm_power_domain *domain); + +struct rt_dm_power_domain *rt_dm_power_domain_get_by_index(struct rt_device *dev, int index); +struct rt_dm_power_domain *rt_dm_power_domain_get_by_name(struct rt_device *dev, const char *name); +rt_err_t rt_dm_power_domain_put(struct rt_dm_power_domain *domain); + +rt_err_t rt_dm_power_domain_attach(struct rt_device *dev, rt_bool_t on); +rt_err_t rt_dm_power_domain_detach(struct rt_device *dev, rt_bool_t off); + +#endif /* __RT_DM_POWER_DOMAIN_H__ */ diff --git a/components/drivers/include/drivers/core/rtdm.h b/components/drivers/include/drivers/core/rtdm.h index 95d3c259b8f1..d2cb4f081813 100755 --- a/components/drivers/include/drivers/core/rtdm.h +++ b/components/drivers/include/drivers/core/rtdm.h @@ -13,6 +13,7 @@ #include #include +#include #include #include @@ -26,11 +27,140 @@ extern int rt_hw_cpu_id(void); void rt_dm_secondary_cpu_init(void); -int rt_dm_set_dev_name_auto(rt_device_t dev, const char *prefix); -int rt_dm_get_dev_name_id(rt_device_t dev); +int rt_dm_dev_set_name_auto(rt_device_t dev, const char *prefix); +int rt_dm_dev_get_name_id(rt_device_t dev); -int rt_dm_set_dev_name(rt_device_t dev, const char *format, ...); -const char *rt_dm_get_dev_name(rt_device_t dev); +int rt_dm_dev_set_name(rt_device_t dev, const char *format, ...); +const char *rt_dm_dev_get_name(rt_device_t dev); + +int rt_dm_dev_get_address_count(rt_device_t dev); +rt_err_t rt_dm_dev_get_address(rt_device_t dev, int index, + rt_uint64_t *out_address, rt_uint64_t *out_size); +rt_err_t rt_dm_dev_get_address_by_name(rt_device_t dev, const char *name, + rt_uint64_t *out_address, rt_uint64_t *out_size); +int rt_dm_dev_get_address_array(rt_device_t dev, int nr, rt_uint64_t *out_regs); + +void *rt_dm_dev_iomap(rt_device_t dev, int index); +void *rt_dm_dev_iomap_by_name(rt_device_t dev, const char *name); + +int rt_dm_dev_get_irq_count(rt_device_t dev); +int rt_dm_dev_get_irq(rt_device_t dev, int index); +int rt_dm_dev_get_irq_by_name(rt_device_t dev, const char *name); + +void rt_dm_dev_bind_fwdata(rt_device_t dev, void *fw_np, void *data); +void rt_dm_dev_unbind_fwdata(rt_device_t dev, void *fw_np); + +int rt_dm_dev_prop_read_u8_array_index(rt_device_t dev, const char *propname, + int index, int nr, rt_uint8_t *out_values); +int rt_dm_dev_prop_read_u16_array_index(rt_device_t dev, const char *propname, + int index, int nr, rt_uint16_t *out_values); +int rt_dm_dev_prop_read_u32_array_index(rt_device_t dev, const char *propname, + int index, int nr, rt_uint32_t *out_values); +int rt_dm_dev_prop_read_u64_array_index(rt_device_t dev, const char *propname, + int index, int nr, rt_uint64_t *out_values); +int rt_dm_dev_prop_read_string_array_index(rt_device_t dev, const char *propname, + int index, int nr, const char **out_strings); + +int rt_dm_dev_prop_count_of_size(rt_device_t dev, const char *propname, int size); +int rt_dm_dev_prop_index_of_string(rt_device_t dev, const char *propname, const char *string); + +rt_bool_t rt_dm_dev_prop_read_bool(rt_device_t dev, const char *propname); + +rt_inline rt_err_t rt_dm_dev_prop_read_u8_index(rt_device_t dev, const char *propname, + int index, rt_uint8_t *out_value) +{ + int nr = rt_dm_dev_prop_read_u8_array_index(dev, propname, index, 1, out_value); + + return nr > 0 ? RT_EOK : (rt_err_t)nr; +} + +rt_inline rt_err_t rt_dm_dev_prop_read_u16_index(rt_device_t dev, const char *propname, + int index, rt_uint16_t *out_value) +{ + int nr = rt_dm_dev_prop_read_u16_array_index(dev, propname, index, 1, out_value); + + return nr > 0 ? RT_EOK : (rt_err_t)nr; +} + +rt_inline rt_err_t rt_dm_dev_prop_read_u32_index(rt_device_t dev, const char *propname, + int index, rt_uint32_t *out_value) +{ + int nr = rt_dm_dev_prop_read_u32_array_index(dev, propname, index, 1, out_value); + + return nr > 0 ? RT_EOK : (rt_err_t)nr; +} + +rt_inline rt_err_t rt_dm_dev_prop_read_u64_index(rt_device_t dev, const char *propname, + int index, rt_uint64_t *out_value) +{ + int nr = rt_dm_dev_prop_read_u64_array_index(dev, propname, index, 1, out_value); + + return nr > 0 ? RT_EOK : (rt_err_t)nr; +} + +rt_inline rt_err_t rt_dm_dev_prop_read_string_index(rt_device_t dev, const char *propname, + int index, const char **out_string) +{ + int nr = rt_dm_dev_prop_read_string_array_index(dev, propname, index, 1, out_string); + + return nr > 0 ? RT_EOK : (rt_err_t)nr; +} + +rt_inline rt_err_t rt_dm_dev_prop_read_u8(rt_device_t dev, const char *propname, + rt_uint8_t *out_value) +{ + return rt_dm_dev_prop_read_u8_index(dev, propname, 0, out_value); +} + +rt_inline rt_err_t rt_dm_dev_prop_read_u16(rt_device_t dev, const char *propname, + rt_uint16_t *out_value) +{ + return rt_dm_dev_prop_read_u16_index(dev, propname, 0, out_value); +} + +rt_inline rt_err_t rt_dm_dev_prop_read_u32(rt_device_t dev, const char *propname, + rt_uint32_t *out_value) +{ + return rt_dm_dev_prop_read_u32_index(dev, propname, 0, out_value); +} + +rt_inline rt_err_t rt_dm_dev_prop_read_s32(rt_device_t dev, const char *propname, + rt_int32_t *out_value) +{ + return rt_dm_dev_prop_read_u32_index(dev, propname, 0, (rt_uint32_t *)out_value); +} + +rt_inline rt_err_t rt_dm_dev_prop_read_u64(rt_device_t dev, const char *propname, + rt_uint64_t *out_value) +{ + return rt_dm_dev_prop_read_u64_index(dev, propname, 0, out_value); +} + +rt_inline rt_err_t rt_dm_dev_prop_read_string(rt_device_t dev, const char *propname, + const char **out_string) +{ + return rt_dm_dev_prop_read_string_index(dev, propname, 0, out_string); +} + +rt_inline int rt_dm_dev_prop_count_of_u8(rt_device_t dev, const char *propname) +{ + return rt_dm_dev_prop_count_of_size(dev, propname, sizeof(rt_uint8_t)); +} + +rt_inline int rt_dm_dev_prop_count_of_u16(rt_device_t dev, const char *propname) +{ + return rt_dm_dev_prop_count_of_size(dev, propname, sizeof(rt_uint16_t)); +} + +rt_inline int rt_dm_dev_prop_count_of_u32(rt_device_t dev, const char *propname) +{ + return rt_dm_dev_prop_count_of_size(dev, propname, sizeof(rt_uint32_t)); +} + +rt_inline int rt_dm_dev_prop_count_of_u64(rt_device_t dev, const char *propname) +{ + return rt_dm_dev_prop_count_of_size(dev, propname, sizeof(rt_uint64_t)); +} /* init cpu, memory, interrupt-controller, bus... */ #define INIT_CORE_EXPORT(fn) INIT_EXPORT(fn, "1.0") @@ -40,8 +170,12 @@ const char *rt_dm_get_dev_name(rt_device_t dev); #define INIT_PLATFORM_EXPORT(fn) INIT_EXPORT(fn, "1.2") /* init sys-timer, clk, pinctrl... */ #define INIT_SUBSYS_EXPORT(fn) INIT_EXPORT(fn, "1.3") +/* init subsystem if depends more... */ +#define INIT_SUBSYS_LATER_EXPORT(fn) INIT_EXPORT(fn, "1.3.1") /* init early drivers */ #define INIT_DRIVER_EARLY_EXPORT(fn) INIT_EXPORT(fn, "1.4") +/* init later drivers */ +#define INIT_DRIVER_LATER_EXPORT(fn) INIT_EXPORT(fn, "3.0") /* init in secondary_cpu_c_start */ #define INIT_SECONDARY_CPU_EXPORT(fn) INIT_EXPORT(fn, "7") /* init after mount fs */ diff --git a/components/drivers/include/drivers/dma.h b/components/drivers/include/drivers/dma.h new file mode 100644 index 000000000000..ab4ea50deeb5 --- /dev/null +++ b/components/drivers/include/drivers/dma.h @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-02-25 GuEe-GUI the first version + */ + +#ifndef __DMA_H__ +#define __DMA_H__ + +#include + +#include +#include + +struct rt_dma_chan; +struct rt_dma_controller_ops; + +struct rt_dma_controller +{ + rt_list_t list; + + struct rt_device *dev; + + const struct rt_dma_controller_ops *ops; +}; + +enum rt_dma_transfer_direction +{ + RT_DMA_MEM_TO_MEM, + RT_DMA_MEM_TO_DEV, + RT_DMA_DEV_TO_MEM, + RT_DMA_DEV_TO_DEV, + RT_DMA_TRANS_NONE, +}; + +enum rt_dma_slave_buswidth +{ + RT_DMA_SLAVE_BUSWIDTH_UNDEFINED = 0, + RT_DMA_SLAVE_BUSWIDTH_1_BYTE = 1, + RT_DMA_SLAVE_BUSWIDTH_2_BYTES = 2, + RT_DMA_SLAVE_BUSWIDTH_3_BYTES = 3, + RT_DMA_SLAVE_BUSWIDTH_4_BYTES = 4, + RT_DMA_SLAVE_BUSWIDTH_8_BYTES = 8, + RT_DMA_SLAVE_BUSWIDTH_16_BYTES = 16, + RT_DMA_SLAVE_BUSWIDTH_32_BYTES = 32, + RT_DMA_SLAVE_BUSWIDTH_64_BYTES = 64, + RT_DMA_SLAVE_BUSWIDTH_128_BYTES = 128, +}; + +struct rt_dma_slave_config +{ + enum rt_dma_transfer_direction direction; + enum rt_dma_slave_buswidth src_addr_width; + enum rt_dma_slave_buswidth dst_addr_width; + + rt_ubase_t src_addr; + rt_ubase_t dst_addr; + + rt_uint32_t src_maxburst; + rt_uint32_t dst_maxburst; + rt_uint32_t src_port_window_size; + rt_uint32_t dst_port_window_size; +}; + +#define RT_DMA_CHAN_F_NONE RT_BIT(0) + +struct rt_dma_controller_ops +{ + struct rt_dma_chan *(*request_channel)(struct rt_dma_controller *, struct rt_device *slave); + rt_err_t (*release_channel)(struct rt_dma_chan *); + + rt_err_t (*start)(struct rt_dma_chan *); + rt_err_t (*stop)(struct rt_dma_chan *); + rt_err_t (*config)(struct rt_dma_chan *, struct rt_dma_slave_config *); + + rt_err_t (*prep_dma_memcpy)(struct rt_dma_chan *, + rt_ubase_t dst, rt_ubase_t src, rt_size_t len, rt_ubase_t flags); + + rt_err_t (*prep_dma_cyclic)(struct rt_dma_chan *, + rt_ubase_t buf_addr, rt_size_t buf_len, rt_size_t period_len, + enum rt_dma_transfer_direction, rt_ubase_t flags); + + rt_err_t (*prep_dma_single)(struct rt_dma_chan *, + rt_ubase_t buf_addr, rt_size_t buf_len, + enum rt_dma_transfer_direction, rt_ubase_t flags); +}; + +struct rt_dma_chan +{ + struct rt_dma_controller *ctrl; + struct rt_device *slave; + + rt_list_t list; + void (*callback)(struct rt_dma_chan *); + + void *priv; +}; + +rt_err_t rt_dma_controller_register(struct rt_dma_controller *ctrl); +rt_err_t rt_dma_controller_unregister(struct rt_dma_controller *ctrl); + +#define RT_DMA_F_LINEAR RT_BIT(0) +#define RT_DMA_F_32BITS RT_BIT(1) +#define RT_DMA_F_NOCACHE RT_BIT(2) + +void *rt_dma_alloc(struct rt_device *dev, rt_size_t size, + rt_ubase_t *dma_handle, rt_ubase_t flags); + +void rt_dma_free(struct rt_device *dev, rt_size_t size, + void *cpu_addr, rt_ubase_t dma_handle, rt_ubase_t flags); + +rt_inline void *rt_dma_alloc_coherent(struct rt_device *dev, rt_size_t size, + rt_ubase_t *dma_handle) +{ + return rt_dma_alloc(dev, size, dma_handle, + RT_DMA_F_NOCACHE | RT_DMA_F_LINEAR); +} + +rt_inline void rt_dma_free_coherent(struct rt_device *dev, rt_size_t size, + void *cpu_addr, rt_ubase_t dma_handle) +{ + rt_dma_free(dev, size, cpu_addr, dma_handle, + RT_DMA_F_NOCACHE | RT_DMA_F_LINEAR); +} + +void rt_dma_flush(struct rt_device *dev, void *dma_va, rt_size_t size); + +rt_err_t rt_dma_pool_install(rt_region_t *region); +rt_err_t rt_dma_pool_extract(rt_region_t *region_list, rt_size_t list_len, + rt_size_t cma_size, rt_size_t coherent_pool_size); + +#endif /* __DMA_H__ */ diff --git a/components/drivers/include/drivers/hwspinlock.h b/components/drivers/include/drivers/hwspinlock.h new file mode 100644 index 000000000000..4335f485a5cc --- /dev/null +++ b/components/drivers/include/drivers/hwspinlock.h @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-09-23 GuEe-GUI first version + */ + +#ifndef __HWSPINLOCK_H__ +#define __HWSPINLOCK_H__ + +#include +#include +#include + +enum rt_hwspinlock_mode +{ + RT_HWSPINLOCK_NONE = 0, + RT_HWSPINLOCK_IRQSTATE, + RT_HWSPINLOCK_RAW, + RT_HWSPINLOCK_IN_ATOMIC, /* Called while in atomic context */ +}; + +struct rt_hwspinlock; +struct rt_hwspinlock_ops; +struct rt_hwspinlock_bank; + +rt_err_t rt_hwspinlock_bank_register(struct rt_hwspinlock_bank *bank); +rt_err_t rt_hwspinlock_bank_unregister(struct rt_hwspinlock_bank *bank); + +rt_err_t rt_hwspin_trylock_mode(struct rt_hwspinlock *hwlock, int mode, + rt_ubase_t *out_level); +rt_err_t rt_hwspin_lock_timeout_mode(struct rt_hwspinlock *hwlock, int mode, + rt_uint32_t timeout_ms, rt_ubase_t *out_level); +void rt_hwspin_unlock_mode(struct rt_hwspinlock *hwlock, int mode, + rt_ubase_t *out_level); + +rt_inline rt_err_t rt_hwspin_trylock(struct rt_hwspinlock *hwlock) +{ + return rt_hwspin_trylock_mode(hwlock, RT_HWSPINLOCK_NONE, RT_NULL); +} + +rt_inline rt_err_t rt_hwspin_trylock_irqsave(struct rt_hwspinlock *hwlock, + rt_ubase_t *out_level) +{ + return rt_hwspin_trylock_mode(hwlock, RT_HWSPINLOCK_IRQSTATE, out_level); +} + +rt_inline rt_err_t rt_hwspin_trylock_raw(struct rt_hwspinlock *hwlock) +{ + return rt_hwspin_trylock_mode(hwlock, RT_HWSPINLOCK_RAW, RT_NULL); +} + +rt_inline rt_err_t rt_hwspin_trylock_in_atomic(struct rt_hwspinlock *hwlock) +{ + return rt_hwspin_trylock_mode(hwlock, RT_HWSPINLOCK_IN_ATOMIC, RT_NULL); +} + +rt_inline rt_err_t rt_hwspin_lock_timeout(struct rt_hwspinlock *hwlock, + rt_uint32_t timeout_ms) +{ + return rt_hwspin_lock_timeout_mode(hwlock, RT_HWSPINLOCK_NONE, timeout_ms, + RT_NULL); +} + +rt_inline rt_err_t rt_hwspin_lock_timeout_irqsave(struct rt_hwspinlock *hwlock, + rt_uint32_t timeout_ms, rt_ubase_t *out_level) +{ + return rt_hwspin_lock_timeout_mode(hwlock, RT_HWSPINLOCK_IRQSTATE, + timeout_ms, out_level); +} + +rt_inline rt_err_t rt_hwspin_lock_timeout_raw(struct rt_hwspinlock *hwlock, + rt_uint32_t timeout_ms) +{ + return rt_hwspin_lock_timeout_mode(hwlock, RT_HWSPINLOCK_RAW, timeout_ms, + RT_NULL); +} + +rt_inline rt_err_t rt_hwspin_lock_timeout_in_atomic(struct rt_hwspinlock *hwlock, + rt_uint32_t timeout_ms) +{ + return rt_hwspin_lock_timeout_mode(hwlock, RT_HWSPINLOCK_IN_ATOMIC, + timeout_ms, RT_NULL); +} + +rt_inline void rt_hwspin_unlock(struct rt_hwspinlock *hwlock) +{ + rt_hwspin_unlock_mode(hwlock, RT_HWSPINLOCK_NONE, RT_NULL); +} + +rt_inline void rt_hwspin_unlock_irqsave(struct rt_hwspinlock *hwlock, + rt_ubase_t *out_level) +{ + rt_hwspin_unlock_mode(hwlock, RT_HWSPINLOCK_IRQSTATE, out_level); +} + +rt_inline void rt_hwspin_unlock_raw(struct rt_hwspinlock *hwlock) +{ + rt_hwspin_unlock_mode(hwlock, RT_HWSPINLOCK_RAW, RT_NULL); +} + +rt_inline void rt_hwspin_unlock_in_atomic(struct rt_hwspinlock *hwlock) +{ + rt_hwspin_unlock_mode(hwlock, RT_HWSPINLOCK_IN_ATOMIC, RT_NULL); +} + +struct rt_hwspinlock *rt_hwspinlock_request(void); +struct rt_hwspinlock *rt_hwspinlock_request_id(int id); +rt_err_t rt_hwspinlock_free(struct rt_hwspinlock *hwlock); + +int rt_ofw_get_hwspinlock_id(struct rt_ofw_node *np, int index); +int rt_ofw_get_hwspinlock_id_byname(struct rt_ofw_node *np, const char *name); + +#endif /* __HWSPINLOCK_H__ */ diff --git a/components/drivers/include/drivers/hwtimer.h b/components/drivers/include/drivers/hwtimer.h index 83f3d3ed9b56..6f11ff2c5456 100644 --- a/components/drivers/include/drivers/hwtimer.h +++ b/components/drivers/include/drivers/hwtimer.h @@ -78,7 +78,9 @@ typedef struct rt_hwtimer_device rt_err_t rt_device_hwtimer_register(rt_hwtimer_t *timer, const char *name, void *user_data); void rt_device_hwtimer_isr(rt_hwtimer_t *timer); +#ifdef RT_USING_DM extern void (*rt_device_hwtimer_us_delay)(rt_uint32_t us); +#endif #ifdef __cplusplus } diff --git a/components/drivers/include/drivers/i2c.h b/components/drivers/include/drivers/i2c.h index 2a900536dfc5..42452ce5f270 100644 --- a/components/drivers/include/drivers/i2c.h +++ b/components/drivers/include/drivers/i2c.h @@ -63,10 +63,43 @@ struct rt_i2c_bus_device struct rt_i2c_client { +#ifdef RT_USING_DM + struct rt_device parent; + + const char *name; + const struct rt_i2c_device_id *id; + const struct rt_ofw_node_id *ofw_id; +#endif + struct rt_i2c_bus_device *bus; rt_uint16_t client_addr; }; +#ifdef RT_USING_DM +struct rt_i2c_device_id +{ + char name[20]; + void *data; +}; + +struct rt_i2c_driver +{ + struct rt_driver parent; + + const struct rt_i2c_device_id *ids; + const struct rt_ofw_node_id *ofw_ids; + + rt_err_t (*probe)(struct rt_i2c_client *client); + rt_err_t (*remove)(struct rt_i2c_client *client); + rt_err_t (*shutdown)(struct rt_i2c_client *client); +}; + +rt_err_t rt_i2c_driver_register(struct rt_i2c_driver *driver); +rt_err_t rt_i2c_device_register(struct rt_i2c_client *client); + +#define RT_I2C_DRIVER_EXPORT(driver) RT_DRIVER_EXPORT(driver, i2c, BUILIN) +#endif /* RT_USING_DM */ + rt_err_t rt_i2c_bus_device_register(struct rt_i2c_bus_device *bus, const char *bus_name); struct rt_i2c_bus_device *rt_i2c_bus_device_find(const char *bus_name); diff --git a/components/drivers/include/drivers/led.h b/components/drivers/include/drivers/led.h new file mode 100644 index 000000000000..aeb80a6d64f0 --- /dev/null +++ b/components/drivers/include/drivers/led.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-3-08 GuEe-GUI the first version + */ + +#ifndef __LED_H__ +#define __LED_H__ + +#include +#include + +struct rt_led_ops; + +enum rt_led_state +{ + RT_LED_S_OFF, + RT_LED_S_ON, + RT_LED_S_TOGGLE, + RT_LED_S_BLINK, + + RT_LED_STATE_NR, +}; + +struct rt_led_device +{ + struct rt_device parent; + + const struct rt_led_ops *ops; + + struct rt_spinlock spinlock; + + void *sysdata; + void *priv; +}; + +struct rt_led_ops +{ + rt_err_t (*set_state)(struct rt_led_device *led, enum rt_led_state state); + rt_err_t (*get_state)(struct rt_led_device *led, enum rt_led_state *out_state); + rt_err_t (*set_period)(struct rt_led_device *led, rt_uint32_t period_ms); + rt_err_t (*set_brightness)(struct rt_led_device *led, rt_uint32_t brightness); +}; + +rt_err_t rt_hw_led_register(struct rt_led_device *led); +rt_err_t rt_hw_led_unregister(struct rt_led_device *led); + +rt_err_t rt_led_set_state(struct rt_led_device *led, enum rt_led_state state); +rt_err_t rt_led_get_state(struct rt_led_device *led, enum rt_led_state *out_state); +rt_err_t rt_led_set_period(struct rt_led_device *led, rt_uint32_t period_ms); +rt_err_t rt_led_set_brightness(struct rt_led_device *led, rt_uint32_t brightness); + +#endif /* __LED_H__ */ diff --git a/components/drivers/include/drivers/mailbox.h b/components/drivers/include/drivers/mailbox.h new file mode 100644 index 000000000000..8bc713b7e3b7 --- /dev/null +++ b/components/drivers/include/drivers/mailbox.h @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-09-23 GuEe-GUI first version + */ + +#ifndef __MAILBOX_H__ +#define __MAILBOX_H__ + +#include +#include + +struct rt_mbox_chan; +struct rt_mbox_client; +struct rt_mbox_controller_ops; + +struct rt_mbox_controller +{ + rt_list_t list; + + struct rt_device *dev; + + const struct rt_mbox_controller_ops *ops; + + rt_size_t num_chans; + struct rt_mbox_chan *chans; +}; + +struct rt_mbox_controller_ops +{ + rt_err_t (*request)(struct rt_mbox_chan *); + void (*free)(struct rt_mbox_chan *); + rt_err_t (*send)(struct rt_mbox_chan *, const void *data); + rt_err_t (*peek)(struct rt_mbox_chan *); + int (*ofw_parse)(struct rt_mbox_controller *, struct rt_ofw_cell_args *); +}; + +struct rt_mbox_chan +{ + struct rt_mbox_controller *ctrl; + struct rt_mbox_client *client; + + void *data; + rt_bool_t complete; + struct rt_timer timer; + struct rt_spinlock lock; + + void *priv; +}; + +struct rt_mbox_client +{ + struct rt_device *dev; + + void (*rx_callback)(struct rt_mbox_client *, void *data); + void (*tx_prepare)(struct rt_mbox_client *, const void *data); + void (*tx_done)(struct rt_mbox_client *, const void *data, rt_err_t err); +}; + +rt_err_t rt_mbox_controller_register(struct rt_mbox_controller *ctrl); +rt_err_t rt_mbox_controller_unregister(struct rt_mbox_controller *ctrl); + +rt_err_t rt_mbox_send(struct rt_mbox_chan *chan, const void *data, + rt_uint32_t timeout_ms); +void rt_mbox_send_done(struct rt_mbox_chan *chan, rt_err_t err); +rt_err_t rt_mbox_peek(struct rt_mbox_chan *chan); +rt_err_t rt_mbox_recv(struct rt_mbox_chan *chan, void *data); + +struct rt_mbox_chan *rt_mbox_request_by_index(struct rt_mbox_client *client, int index); +struct rt_mbox_chan *rt_mbox_request_by_name(struct rt_mbox_client *client, char *name); +rt_err_t rt_mbox_free(struct rt_mbox_chan *chan); + +#endif /* __MAILBOX_H__ */ diff --git a/components/drivers/include/drivers/misc.h b/components/drivers/include/drivers/misc.h index aa2b715512c7..d64274870b7b 100644 --- a/components/drivers/include/drivers/misc.h +++ b/components/drivers/include/drivers/misc.h @@ -12,6 +12,7 @@ #define __MISC_H__ #include +#include #ifdef ARCH_CPU_64BIT #define RT_BITS_PER_LONG 64 @@ -22,6 +23,10 @@ #define RT_DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d)) +#define RT_DIV_ROUND_DOWN_ULL(ll, d) ({ rt_uint64_t _tmp = (ll); rt_do_div(_tmp, d); _tmp; }) + +#define RT_DIV_ROUND_UP_ULL(ll, d) RT_DIV_ROUND_DOWN_ULL((rt_uint64_t)(ll) + (d) - 1, (d)) + #define RT_DIV_ROUND_CLOSEST(x, divisor) \ ({ \ typeof(x) __x = x; \ @@ -33,6 +38,18 @@ (((__x) - ((__d) / 2)) / (__d)); \ }) +#define RT_DIV_ROUND_CLOSEST_ULL(x, divisor) \ +({ \ + typeof(divisor) __d = divisor; \ + rt_uint64_t _tmp = (x) + (__d) / 2; \ + rt_do_div(_tmp, __d); \ + _tmp; \ +}) + +#define RT_FIELD_PREP(mask, val) (((rt_uint64_t)(val) << (__rt_ffsl((mask)) - 1)) & (mask)) +#define RT_FIELD_GET(mask, reg) (((reg) & (mask)) >> (__rt_ffsl((mask)) - 1)) +#define RT_FIELD_CLEAR(mask, reg) ((reg) & (~(mask))) + #define RT_BIT(n) (1UL << (n)) #define RT_BIT_ULL(n) (1ULL << (n)) #define RT_BIT_MASK(nr) (1UL << ((nr) % RT_BITS_PER_LONG)) @@ -48,6 +65,20 @@ #define RT_ARRAY_SIZE(arr) (sizeof(arr) / sizeof(arr[0])) +#define rt_likely(x) __builtin_expect(!!(x), 1) +#define rt_unlikely(x) __builtin_expect(!!(x), 0) + +#define rt_err_ptr(err) ((void *)(rt_base_t)(err)) +#define rt_ptr_err(ptr) ((rt_err_t)(rt_base_t)(ptr)) +#define rt_is_err_value(ptr) rt_unlikely((rt_ubase_t)(void *)(ptr) >= (rt_ubase_t)-4095) +#define rt_is_err(ptr) rt_is_err_value(ptr) +#define rt_is_err_or_null(ptr) (rt_unlikely(!ptr) || rt_is_err_value((rt_ubase_t)ptr)) + +#define rt_upper_32_bits(n) ((rt_uint32_t)(((n) >> 16) >> 16)) +#define rt_lower_32_bits(n) ((rt_uint32_t)((n) & 0xffffffff)) +#define rt_upper_16_bits(n) ((rt_uint16_t)((n) >> 16)) +#define rt_lower_16_bits(n) ((rt_uint16_t)((n) & 0xffff)) + #define rt_min(x, y) \ ({ \ typeof(x) _x = (x); \ @@ -71,6 +102,13 @@ _x < _y ? _x: _y; \ }) +#define rt_max_t(type, x, y) \ +({ \ + type _x = (x); \ + type _y = (y); \ + _x > _y ? _x: _y; \ +}) + #define rt_clamp(val, lo, hi) rt_min((typeof(val))rt_max(val, lo), hi) #define rt_do_div(n, base) \ @@ -83,4 +121,79 @@ _rem; \ }) +#define rt_abs(x) \ +({ \ + long ret; \ + if (sizeof(x) == sizeof(long)) \ + { \ + long __x = (x); \ + ret = (__x < 0) ? -__x : __x; \ + } \ + else \ + { \ + int __x = (x); \ + ret = (__x < 0) ? -__x : __x; \ + } \ + ret; \ +}) + +#define rt_roundup(x, y) \ +({ \ + typeof(y) __y = y; \ + (((x) + (__y - 1)) / __y) * __y; \ +}) + +#define rt_rounddown(x, y) \ +({ \ + typeof(x) __x = (x); \ + __x - (__x % (y)); \ +}) + +#ifndef rt_ilog2 +rt_inline int rt_ilog2(rt_ubase_t v) +{ + int l = 0; + + while ((1UL << l) < v) + { + l++; + } + + return l; +} +#endif /* !rt_ilog2 */ + +#ifndef rt_bcd2bin +rt_inline rt_ubase_t rt_bcd2bin(rt_uint8_t val) +{ + return (val & 0x0f) + (val >> 4) * 10; +} +#endif /* !rt_bcd2bin */ + +#ifndef rt_bin2bcd +rt_inline rt_uint8_t rt_bin2bcd(rt_ubase_t val) +{ + return ((val / 10) << 4) + val % 10; +} +#endif /* !rt_bin2bcd */ + +#ifndef rt_div_u64_rem +rt_inline rt_uint64_t rt_div_u64_rem(rt_uint64_t dividend, rt_uint32_t divisor, + rt_uint32_t *remainder) +{ + *remainder = dividend % divisor; + + return dividend / divisor; +} +#endif /* !rt_div_u64_rem */ + +#ifndef rt_div_u64 +rt_inline rt_uint64_t rt_div_u64(rt_uint64_t dividend, rt_uint32_t divisor) +{ + rt_uint32_t remainder; + + return rt_div_u64_rem(dividend, divisor, &remainder); +} +#endif /* !rt_div_u64 */ + #endif /* __MISC_H__ */ diff --git a/components/drivers/include/drivers/mmcsd_cmd.h b/components/drivers/include/drivers/mmcsd_cmd.h index d47094281518..6fb9f37ae941 100644 --- a/components/drivers/include/drivers/mmcsd_cmd.h +++ b/components/drivers/include/drivers/mmcsd_cmd.h @@ -76,6 +76,7 @@ extern "C" { /* This is basically the same command as for MMC with some quirks. */ #define SD_SEND_RELATIVE_ADDR 3 /* bcr R6 */ #define SD_SEND_IF_COND 8 /* bcr [11:0] See below R7 */ +#define SD_SWITCH_VOLTAGE 11 /* ac R1 */ /* class 10 */ #define SD_SWITCH 6 /* adtc [31:0] See below R1 */ diff --git a/components/drivers/include/drivers/mmcsd_core.h b/components/drivers/include/drivers/mmcsd_core.h index 3a6ac13aa9fa..b4b0e2c2d098 100644 --- a/components/drivers/include/drivers/mmcsd_core.h +++ b/components/drivers/include/drivers/mmcsd_core.h @@ -242,6 +242,12 @@ void mmcsd_set_bus_width(struct rt_mmcsd_host *host, rt_uint32_t width); void mmcsd_set_timing(struct rt_mmcsd_host *host, rt_uint32_t timing); void mmcsd_set_data_timeout(struct rt_mmcsd_data *data, const struct rt_mmcsd_card *card); rt_uint32_t mmcsd_select_voltage(struct rt_mmcsd_host *host, rt_uint32_t ocr); +rt_err_t mmcsd_set_signal_voltage(struct rt_mmcsd_host *host, unsigned char signal_voltage); +void mmcsd_set_initial_signal_voltage(struct rt_mmcsd_host *host); +rt_err_t mmcsd_host_set_uhs_voltage(struct rt_mmcsd_host *host); +rt_err_t mmcsd_set_uhs_voltage(struct rt_mmcsd_host *host, rt_uint32_t ocr); +rt_err_t mmcsd_send_tuning(struct rt_mmcsd_host *host, rt_uint32_t opcode, rt_err_t *cmd_error); +rt_err_t mmcsd_send_abort_tuning(struct rt_mmcsd_host *host, rt_uint32_t opcode); void mmcsd_change(struct rt_mmcsd_host *host); void mmcsd_detect(void *param); void mmcsd_host_init(struct rt_mmcsd_host *host); diff --git a/components/drivers/include/drivers/mmcsd_host.h b/components/drivers/include/drivers/mmcsd_host.h index c6935c105630..b902dad09300 100644 --- a/components/drivers/include/drivers/mmcsd_host.h +++ b/components/drivers/include/drivers/mmcsd_host.h @@ -85,8 +85,23 @@ struct rt_mmcsd_host_ops rt_int32_t (*get_card_status)(struct rt_mmcsd_host *host); void (*enable_sdio_irq)(struct rt_mmcsd_host *host, rt_int32_t en); rt_int32_t (*execute_tuning)(struct rt_mmcsd_host *host, rt_int32_t opcode); + rt_bool_t (*card_busy)(struct rt_mmcsd_host *host); + rt_err_t (*signal_voltage_switch)(struct rt_mmcsd_host *host, struct rt_mmcsd_io_cfg *io_cfg); }; +#ifdef RT_USING_REGULATOR +struct rt_regulator; + +struct rt_mmcsd_supply +{ + rt_bool_t vqmmc_enabled; + rt_bool_t regulator_enabled; + + struct rt_regulator *vmmc; /* Card power supply */ + struct rt_regulator *vqmmc; /* Optional Vccq supply */ +}; +#endif /* RT_USING_REGULATOR */ + struct rt_mmcsd_host { char name[RT_NAME_MAX]; @@ -129,6 +144,7 @@ struct rt_mmcsd_host #define MMCSD_SUP_HS200_1V2 (1 << 10) #define MMCSD_SUP_HS200 (MMCSD_SUP_HS200_1V2 | MMCSD_SUP_HS200_1V8) /* hs200 sdr */ #define MMCSD_SUP_NONREMOVABLE (1 << 11) +#define controller_is_removable(host) (!(host->flags & MMCSD_SUP_NONREMOVABLE)) rt_uint32_t max_seg_size; /* maximum size of one dma segment */ rt_uint32_t max_dma_segs; /* maximum number of dma segments in one request */ @@ -145,6 +161,10 @@ struct rt_mmcsd_host struct rt_semaphore *sdio_irq_sem; struct rt_thread *sdio_irq_thread; +#ifdef RT_USING_REGULATOR + struct rt_mmcsd_supply supply; +#endif + void *private_data; }; #ifdef __cplusplus diff --git a/components/drivers/include/drivers/nvmem.h b/components/drivers/include/drivers/nvmem.h new file mode 100644 index 000000000000..1c2449fd148e --- /dev/null +++ b/components/drivers/include/drivers/nvmem.h @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-02-25 GuEe-GUI the first version + */ + +#ifndef __NVMEM_H__ +#define __NVMEM_H__ + +#include + +#include +#include +#include +#include + +struct rt_nvmem_device +{ + struct rt_device parent; + + int cells_nr; + rt_list_t cell_nodes; + + rt_ssize_t (*reg_read)(struct rt_nvmem_device *, int offset, void *val, rt_size_t bytes); + rt_ssize_t (*reg_write)(struct rt_nvmem_device *, int offset, void *val, rt_size_t bytes); + + rt_ssize_t size; + int word_size; + int stride; + + rt_bool_t read_only; + rt_bool_t ignore_wp; + rt_base_t wp_pin; + rt_uint8_t wp_pin_active; + + struct ref ref; + struct rt_spinlock spinlock; + + void *priv; +}; + +struct rt_nvmem_cell +{ + rt_list_t list; + + int index; + const char *id; + const rt_bool_t free_able; + + rt_uint32_t offset; + rt_uint32_t bytes; + rt_uint32_t bit_offset; + rt_uint32_t nbits; + + struct ref ref; + + struct rt_ofw_node *np; + struct rt_nvmem_device *nvmem; +}; + +rt_err_t rt_nvmem_device_register(struct rt_nvmem_device *ndev); +rt_err_t rt_nvmem_device_unregister(struct rt_nvmem_device *ndev); + +rt_err_t rt_nvmem_device_append_cell(struct rt_nvmem_device *ndev, struct rt_nvmem_cell *cell); + +rt_ssize_t rt_nvmem_cell_read(struct rt_nvmem_cell *cell, void *buffer, rt_size_t len); +rt_ssize_t rt_nvmem_cell_write(struct rt_nvmem_cell *cell, void *buffer, rt_size_t len); + +rt_ssize_t rt_nvmem_cell_read_u8(struct rt_nvmem_cell *cell, rt_uint8_t *out_val); +rt_ssize_t rt_nvmem_cell_read_u16(struct rt_nvmem_cell *cell, rt_uint16_t *out_val); +rt_ssize_t rt_nvmem_cell_read_u32(struct rt_nvmem_cell *cell, rt_uint32_t *out_val); +rt_ssize_t rt_nvmem_cell_read_u64(struct rt_nvmem_cell *cell, rt_uint64_t *out_val); + +struct rt_nvmem_cell *rt_nvmem_get_cell_by_index(struct rt_device *dev, int index); +struct rt_nvmem_cell *rt_nvmem_get_cell_by_name(struct rt_device *dev, const char *id); +void rt_nvmem_put_cell(struct rt_nvmem_cell *cell); + +#endif /* __NVMEM_H__ */ diff --git a/components/drivers/include/drivers/ofw.h b/components/drivers/include/drivers/ofw.h index 3471726877a6..6b4cf97e4415 100644 --- a/components/drivers/include/drivers/ofw.h +++ b/components/drivers/include/drivers/ofw.h @@ -22,6 +22,10 @@ typedef rt_uint32_t rt_phandle; struct rt_ofw_prop { +#ifdef RT_USING_OFW_DIRECTFS + struct rt_object obj; +#endif + const char *name; int length; void *value; @@ -31,6 +35,10 @@ struct rt_ofw_prop struct rt_ofw_node { +#ifdef RT_USING_OFW_DIRECTFS + struct rt_object obj; +#endif + const char *name; /* full_name is 'path/tag' or 'path/tag@reg' */ const char *full_name; @@ -205,11 +213,15 @@ struct rt_ofw_node *rt_ofw_get_alias_node(const char *tag, int id); int rt_ofw_get_alias_id(struct rt_ofw_node *np, const char *tag); int rt_ofw_get_alias_last_id(const char *tag); +struct rt_ofw_node *rt_ofw_append_child(struct rt_ofw_node *parent, const char *full_name); +rt_err_t rt_ofw_append_prop(struct rt_ofw_node *np, const char *name, int length, void *value); + struct rt_ofw_node *rt_ofw_parse_phandle(const struct rt_ofw_node *np, const char *phandle_name, int index); rt_err_t rt_ofw_parse_phandle_cells(const struct rt_ofw_node *np, const char *list_name, const char *cells_name, int index, struct rt_ofw_cell_args *out_args); int rt_ofw_count_phandle_cells(const struct rt_ofw_node *np, const char *list_name, const char *cells_name); +const char *rt_ofw_get_prop_fuzzy_name(const struct rt_ofw_node *np, const char *name); struct rt_ofw_prop *rt_ofw_get_prop(const struct rt_ofw_node *np, const char *name, rt_ssize_t *out_length); rt_inline const void *rt_ofw_prop_read_raw(const struct rt_ofw_node *np, const char *name, rt_ssize_t *out_length) diff --git a/components/drivers/include/drivers/ofw_fdt.h b/components/drivers/include/drivers/ofw_fdt.h index a9c951ca40fb..7aeec71df5fa 100755 --- a/components/drivers/include/drivers/ofw_fdt.h +++ b/components/drivers/include/drivers/ofw_fdt.h @@ -20,6 +20,7 @@ struct rt_fdt_earlycon union { rt_ubase_t size, width; }; void *fdt; + char options[32]; long nodeoffset; void *data; @@ -41,8 +42,6 @@ struct rt_fdt_earlycon_id rt_err_t (*setup)(struct rt_fdt_earlycon *earlycon, const char *options); }; -#define RT_FDT_EARLYCON_OPTION_SIGNATURE '\n' - #define RT_FDT_EARLYCON_EXPORT(_name, _type, _compatible, _setup) \ static const struct rt_fdt_earlycon_id __rt_fdt_##_name##_earlycon \ rt_used RT_OFW_SYMBOL(earlycon, _) = \ @@ -71,6 +70,7 @@ rt_err_t rt_fdt_boot_dump(void); void rt_fdt_earlycon_output(const char *str); void rt_fdt_earlycon_kick(int why); rt_err_t rt_fdt_scan_chosen_stdout(void); +rt_err_t rt_fdt_bootargs_select(const char *key, int index, const char **out_result); rt_err_t rt_fdt_unflatten(void); struct rt_ofw_node *rt_fdt_unflatten_single(void *fdt); diff --git a/components/drivers/include/drivers/ofw_io.h b/components/drivers/include/drivers/ofw_io.h index 5fdfe8f5e6f8..4ebfbe2c82ae 100755 --- a/components/drivers/include/drivers/ofw_io.h +++ b/components/drivers/include/drivers/ofw_io.h @@ -11,6 +11,7 @@ #ifndef __OFW_IO_H__ #define __OFW_IO_H__ +#include #include int rt_ofw_bus_addr_cells(struct rt_ofw_node *np); diff --git a/components/drivers/include/drivers/pci.h b/components/drivers/include/drivers/pci.h index 063d53ff53c8..9aa6a6c06882 100644 --- a/components/drivers/include/drivers/pci.h +++ b/components/drivers/include/drivers/pci.h @@ -6,4 +6,409 @@ * Change Logs: * Date Author Notes * 2022-08-25 GuEe-GUI first version - */ \ No newline at end of file + */ + +#ifndef __PCI_H__ +#define __PCI_H__ + +#include +#include +#include +#include +#include +#include + +#include "../../pci/pci_ids.h" +#include "../../pci/pci_regs.h" + +#define RT_PCI_INTX_PIN_MAX 4 +#define RT_PCI_BAR_NR_MAX 6 +#define RT_PCI_DEVICE_MAX 32 +#define RT_PCI_FUNCTION_MAX 8 + +#define RT_PCI_FIND_CAP_TTL 48 + +/* + * The PCI interface treats multi-function devices as independent + * devices. The slot/function address of each device is encoded + * in a single byte as follows: + * + * 7:3 = slot + * 2:0 = function + */ +#define RT_PCI_DEVFN(slot, func) ((((slot) & 0x1f) << 3) | ((func) & 0x07)) +#define RT_PCI_SLOT(devfn) (((devfn) >> 3) & 0x1f) +#define RT_PCI_FUNC(devfn) ((devfn) & 0x07) + +struct rt_pci_bus_region +{ + rt_uint64_t phy_addr; + rt_uint64_t cpu_addr; + rt_uint64_t size; + + rt_uint64_t bus_start; + +#define PCI_BUS_REGION_F_NONE 0xffffffff /* PCI no memory */ +#define PCI_BUS_REGION_F_MEM 0x00000000 /* PCI memory space */ +#define PCI_BUS_REGION_F_IO 0x00000001 /* PCI IO space */ +#define PCI_BUS_REGION_F_PREFETCH 0x00000008 /* Prefetchable PCI memory */ + rt_ubase_t flags; +}; + +struct rt_pci_bus_resource +{ + rt_ubase_t base; + rt_size_t size; + + rt_ubase_t flags; +}; + +enum +{ + PCI_F_MULTI_FUNCTION, /* Multi-function device */ + PCI_F_NO_64BIT_MSI, /* May only use 32-bit MSIs */ + PCI_F_NO_MSI, /* May not use MSI */ + PCI_F_ARI, /* ARI forwarding */ + PCI_F_MSI, /* MSI enable */ + PCI_F_MSIX, /* MSIx enable */ + PCI_F_BROKEN_INTX_MASKING, /* INTx masking can't be used */ + + PCI_FLAGS_NR +}; + +/* + * PCI topology: + * + * +-----+-----+ +-------------+ PCI Bus 0 +------------+ PCI Bus 1 + * | RAM | CPU |---------| Host Bridge |--------+-----| PCI Bridge |-----+ + * +-----+-----+ +-------------+ | +------------+ | +-------------+ + * | +----| End Point 2 | + * +-------------+ +-------------+ | +-------------+ | +-------------+ + * | End Point 5 |----+ | End Point 0 |-------+ | End Point 3 |----+ + * +-------------+ | +-------------+ | +-------------+ | + * | | | + * +-------------+ | +-------------+ | +-------------+ | +-------------+ + * | End Point 6 |----+----| ISA Bridge |-------+-----| End Point 1 | +----| End Point 4 | + * +-------------+ +-------------+ | +-------------+ +-------------+ + * | + * +------+ +----------------+ | + * | Port |---------| CardBus Bridge |----+ + * +------+ +----------------+ + */ + +struct rt_pci_bus; + +struct rt_pci_device_id +{ +#define PCI_ANY_ID (~0) +#define RT_PCI_DEVICE_ID(vend, dev) \ + .vendor = (vend), \ + .device = (dev), \ + .subsystem_vendor = PCI_ANY_ID, \ + .subsystem_device = PCI_ANY_ID + + rt_uint32_t vendor, device; /* Vendor and device ID or PCI_ANY_ID */ + rt_uint32_t subsystem_vendor; /* Subsystem ID's or PCI_ANY_ID */ + rt_uint32_t subsystem_device; /* Subsystem ID's or PCI_ANY_ID */ + rt_uint32_t class, class_mask; /* (class, subclass, prog-if) triplet */ + + void *data; +}; + +struct rt_pci_device +{ + struct rt_device parent; + const char *name; + + rt_list_t list; + rt_list_t list_in_bus; + struct rt_pci_bus *bus; + struct rt_pci_bus *subbus; /* In PCI-to-PCI bridge, 'End Point' or 'Port' is NULL */ + + const struct rt_pci_device_id *id; + + rt_uint32_t devfn; /* Encoded device & function index */ + rt_uint16_t vendor; + rt_uint16_t device; + rt_uint16_t subsystem_vendor; + rt_uint16_t subsystem_device; + rt_uint32_t class; /* 3 bytes: (base, sub, prog-if) */ + rt_uint8_t revision; + rt_uint8_t hdr_type; + rt_uint8_t max_latency; + rt_uint8_t min_grantl; + rt_uint8_t int_pin; + rt_uint8_t int_line; + + void *sysdata; + + int irq; + rt_uint8_t pin; + + struct rt_pci_bus_resource resource[RT_PCI_BAR_NR_MAX]; + + rt_uint8_t pcie_cap; + rt_uint8_t msi_cap; + rt_uint8_t msix_cap; + DECLARE_BITMAP(flags, PCI_FLAGS_NR); + +#ifdef RT_PCI_MSI + void *msix_base; + rt_list_t msi_desc_nodes; + struct rt_spinlock msi_lock; +#endif +}; + +struct rt_pci_host_bridge +{ + struct rt_device parent; + + rt_uint32_t busnr; + rt_uint32_t domain; + + struct rt_pci_bus *root_bus; + struct rt_pci_ops *ops; + + rt_uint32_t bus_range[2]; + rt_size_t bus_regions_nr; + struct rt_pci_bus_region *bus_regions; + + rt_uint8_t (*irq_slot)(struct rt_pci_device *pdev, rt_uint8_t *pinp); + int (*irq_map)(struct rt_pci_device *pdev, rt_uint8_t slot, rt_uint8_t pin); + + void *sysdata; + rt_uint8_t priv[0]; +}; +#define rt_device_to_pci_host_bridge(dev) rt_container_of(dev, struct rt_pci_host_bridge, parent) + +struct rt_pci_ops +{ + rt_err_t (*add)(struct rt_pci_bus *bus); + rt_err_t (*remove)(struct rt_pci_bus *bus); + + void *(*map)(struct rt_pci_bus *bus, rt_uint32_t devfn, int reg); + + rt_err_t (*read)(struct rt_pci_bus *bus, rt_uint32_t devfn, int reg, int width, rt_uint32_t *value); + rt_err_t (*write)(struct rt_pci_bus *bus, rt_uint32_t devfn, int reg, int width, rt_uint32_t value); +}; + +struct rt_pci_bus +{ + rt_list_t list; + rt_list_t children_nodes; + rt_list_t devices_nodes; + struct rt_pci_bus *parent; + + union + { + /* In PCI-to-PCI bridge, parent is not NULL */ + struct rt_pci_device *self; + /* In Host bridge, this is Root bus ('PCI Bus 0') */ + struct rt_pci_host_bridge *host_bridge; + }; + + struct rt_pci_ops *ops; + + char name[48]; + char number; + + void *sysdata; +}; + +struct rt_pci_driver +{ + struct rt_driver parent; + + const char *name; + const struct rt_pci_device_id *ids; + + rt_err_t (*probe)(struct rt_pci_device *pdev); + rt_err_t (*remove)(struct rt_pci_device *pdev); + rt_err_t (*shutdown)(struct rt_pci_device *pdev); +}; + +struct rt_pci_msix_entry +{ + int irq; + int index; +}; + +void rt_pci_msi_init(struct rt_pci_device *pdev); +void rt_pci_msix_init(struct rt_pci_device *pdev); + +rt_inline rt_bool_t rt_pci_device_test_flag(const struct rt_pci_device *pdev, int flag) +{ + return bitmap_test_bit((bitmap_t *)pdev->flags, flag); +} + +rt_inline void rt_pci_device_set_flag(struct rt_pci_device *pdev, int flag) +{ + bitmap_set_bit(pdev->flags, flag); +} + +rt_inline rt_bool_t rt_pci_device_test_and_set_flag(struct rt_pci_device *pdev, int flag) +{ + rt_bool_t res = rt_pci_device_test_flag(pdev, flag); + + rt_pci_device_set_flag(pdev, flag); + + return res; +} + +rt_inline void rt_pci_device_clear_flag(struct rt_pci_device *pdev, int flag) +{ + bitmap_clear_bit(pdev->flags, flag); +} + +struct rt_pci_host_bridge *rt_pci_host_bridge_alloc(rt_size_t priv_size); +rt_err_t rt_pci_host_bridge_init(struct rt_pci_host_bridge *host_bridge); +rt_err_t rt_pci_host_bridge_probe(struct rt_pci_host_bridge *host_bridge); + +struct rt_pci_device *rt_pci_alloc_device(struct rt_pci_bus *bus); +struct rt_pci_device *rt_pci_scan_single_device(struct rt_pci_bus *bus, rt_uint32_t devfn); +rt_err_t rt_pci_setup_device(struct rt_pci_device *pdev); +rt_size_t rt_pci_scan_slot(struct rt_pci_bus *bus, rt_uint32_t devfn); +rt_uint32_t rt_pci_scan_child_buses(struct rt_pci_bus *bus, rt_size_t buses); + +rt_err_t rt_pci_host_bridge_register(struct rt_pci_host_bridge *host_bridge); +rt_err_t rt_pci_scan_root_bus_bridge(struct rt_pci_host_bridge *host_bridge); + +rt_uint32_t rt_pci_domain(struct rt_pci_device *pdev); + +rt_uint8_t rt_pci_bus_find_capability(struct rt_pci_bus *bus, rt_uint32_t devfn, int cap); +rt_uint8_t rt_pci_find_capability(struct rt_pci_device *pdev, int cap); +rt_uint8_t rt_pci_find_next_capability(struct rt_pci_device *pdev, rt_uint8_t pos, int cap); + +struct rt_pci_bus *rt_pci_find_root_bus(struct rt_pci_bus *bus); +struct rt_pci_host_bridge *rt_pci_find_host_bridge(struct rt_pci_bus *bus); + +rt_inline rt_bool_t rt_pci_is_root_bus(struct rt_pci_bus *bus) +{ + return bus->parent ? RT_FALSE : RT_TRUE; +} + +rt_inline rt_bool_t rt_pci_is_bridge(struct rt_pci_device *pdev) +{ + return pdev->hdr_type == PCIM_HDRTYPE_BRIDGE || + pdev->hdr_type == PCIM_HDRTYPE_CARDBUS; +} + +#define rt_pci_foreach_bridge(pdev, bus) \ + rt_list_for_each_entry(pdev, &bus->devices_nodes, list_in_bus) \ + if (rt_pci_is_bridge(pdev)) + +rt_err_t rt_pci_bus_read_config_u8(struct rt_pci_bus *bus, rt_uint32_t devfn, int pos, rt_uint8_t *value); +rt_err_t rt_pci_bus_read_config_u16(struct rt_pci_bus *bus, rt_uint32_t devfn, int pos, rt_uint16_t *value); +rt_err_t rt_pci_bus_read_config_u32(struct rt_pci_bus *bus, rt_uint32_t devfn, int pos, rt_uint32_t *value); + +rt_err_t rt_pci_bus_write_config_u8(struct rt_pci_bus *bus, rt_uint32_t devfn, int reg, rt_uint8_t value); +rt_err_t rt_pci_bus_write_config_u16(struct rt_pci_bus *bus, rt_uint32_t devfn, int reg, rt_uint16_t value); +rt_err_t rt_pci_bus_write_config_u32(struct rt_pci_bus *bus, rt_uint32_t devfn, int reg, rt_uint32_t value); + +rt_err_t rt_pci_bus_read_config_uxx(struct rt_pci_bus *bus, rt_uint32_t devfn, int reg, int width, rt_uint32_t *value); +rt_err_t rt_pci_bus_write_config_uxx(struct rt_pci_bus *bus, rt_uint32_t devfn, int reg, int width, rt_uint32_t value); + +rt_inline rt_err_t rt_pci_read_config_u8(const struct rt_pci_device *pdev, int reg, rt_uint8_t *value) +{ + return rt_pci_bus_read_config_u8(pdev->bus, pdev->devfn, reg, value); +} + +rt_inline rt_err_t rt_pci_read_config_u16(const struct rt_pci_device *pdev, int reg, rt_uint16_t *value) +{ + return rt_pci_bus_read_config_u16(pdev->bus, pdev->devfn, reg, value); +} + +rt_inline rt_err_t rt_pci_read_config_u32(const struct rt_pci_device *pdev, int reg, rt_uint32_t *value) +{ + return rt_pci_bus_read_config_u32(pdev->bus, pdev->devfn, reg, value); +} + +rt_inline rt_err_t rt_pci_write_config_u8(const struct rt_pci_device *pdev, int reg, rt_uint8_t value) +{ + return rt_pci_bus_write_config_u8(pdev->bus, pdev->devfn, reg, value); +} + +rt_inline rt_err_t rt_pci_write_config_u16(const struct rt_pci_device *pdev, int reg, rt_uint16_t value) +{ + return rt_pci_bus_write_config_u16(pdev->bus, pdev->devfn, reg, value); +} + +rt_inline rt_err_t rt_pci_write_config_u32(const struct rt_pci_device *pdev, int reg, rt_uint32_t value) +{ + return rt_pci_bus_write_config_u32(pdev->bus, pdev->devfn, reg, value); +} + +rt_inline rt_bool_t rt_pci_enabled(struct rt_pci_device *pdev, int bit) +{ + return bitmap_test_bit(pdev->flags, bit); +} + +#ifdef RT_USING_OFW +int rt_pci_ofw_irq_parse_and_map(struct rt_pci_device *pdev, rt_uint8_t slot, rt_uint8_t pin); +rt_err_t rt_pci_ofw_parse_ranges(struct rt_ofw_node *dev_np, struct rt_pci_host_bridge *host_bridge); +rt_err_t rt_pci_ofw_host_bridge_init(struct rt_ofw_node *dev_np, struct rt_pci_host_bridge *host_bridge); +rt_err_t rt_pci_ofw_bus_init(struct rt_pci_bus *bus); +rt_err_t rt_pci_ofw_device_init(struct rt_pci_device *pdev); +#else +rt_inline rt_err_t rt_pci_ofw_host_bridge_init(struct rt_ofw_node *dev_np, struct rt_pci_host_bridge *host_bridge) +{ + return RT_EOK; +} +rt_inline rt_err_t rt_pci_ofw_bus_init(struct rt_pci_bus *bus) +{ + return RT_EOK; +} +rt_inline rt_err_t rt_pci_ofw_device_init(struct rt_pci_device *pdev) +{ + return RT_EOK; +} +rt_inline int rt_pci_ofw_irq_parse_and_map(struct rt_pci_device *pdev, rt_uint8_t slot, rt_uint8_t pin) +{ + return -1; +} +rt_inline rt_err_t rt_pci_ofw_parse_ranges(struct rt_ofw_node *dev_np, struct rt_pci_host_bridge *host_bridge) +{ + return -RT_ENOSYS; +} +#endif /* RT_USING_OFW */ + +rt_inline void *rt_pci_iomap(struct rt_pci_device *pdev, int bar_idx) +{ + struct rt_pci_bus_resource *res = &pdev->resource[bar_idx]; + + RT_ASSERT(bar_idx < RT_ARRAY_SIZE(pdev->resource)); + + return rt_ioremap((void *)res->base, res->size); +} + +rt_uint8_t rt_pci_irq_intx(struct rt_pci_device *pdev, rt_uint8_t pin); +rt_uint8_t rt_pci_irq_slot(struct rt_pci_device *pdev, rt_uint8_t *pinp); + +void rt_pci_assign_irq(struct rt_pci_device *pdev); + +void rt_pci_intx(struct rt_pci_device *pdev, rt_bool_t enable); +rt_bool_t rt_pci_check_and_mask_intx(struct rt_pci_device *pdev); +rt_bool_t rt_pci_check_and_unmask_intx(struct rt_pci_device *pdev); + +void rt_pci_irq_mask(struct rt_pci_device *pdev); +void rt_pci_irq_unmask(struct rt_pci_device *pdev); + +rt_err_t rt_pci_region_setup(struct rt_pci_host_bridge *host_bridge); +struct rt_pci_bus_region *rt_pci_region_alloc(struct rt_pci_host_bridge *host_bridge, + void **out_addr, rt_size_t size, rt_ubase_t flags, rt_bool_t mem64); + +rt_err_t rt_pci_device_alloc_resource(struct rt_pci_host_bridge *host_bridge, struct rt_pci_device *pdev); + +void rt_pci_enum_device(struct rt_pci_bus *bus, rt_bool_t (callback(struct rt_pci_device *))); + +const struct rt_pci_device_id *rt_pci_match_id(struct rt_pci_device *pdev, const struct rt_pci_device_id *id); +const struct rt_pci_device_id *rt_pci_match_ids(struct rt_pci_device *pdev, const struct rt_pci_device_id *ids); + +rt_err_t rt_pci_driver_register(struct rt_pci_driver *pdrv); +rt_err_t rt_pci_device_register(struct rt_pci_device *pdev); + +#define RT_PCI_DRIVER_EXPORT(driver) RT_DRIVER_EXPORT(driver, pci, BUILIN) + +extern struct rt_spinlock rt_pci_lock; + +#endif /* __PCI_H__ */ diff --git a/components/drivers/include/drivers/pci_msi.h b/components/drivers/include/drivers/pci_msi.h index 063d53ff53c8..a1ac800bf5f4 100644 --- a/components/drivers/include/drivers/pci_msi.h +++ b/components/drivers/include/drivers/pci_msi.h @@ -6,4 +6,75 @@ * Change Logs: * Date Author Notes * 2022-08-25 GuEe-GUI first version - */ \ No newline at end of file + */ + +#ifndef __PCI_MSI_H__ +#define __PCI_MSI_H__ + +#include +#include + +struct rt_pci_msi_desc +{ + union + { + rt_uint32_t msi_mask; /* [PCI MSI] MSI cached mask bits */ + rt_uint32_t msix_ctrl; /* [PCI MSI-X] MSI-X cached per vector control bits */ + }; + struct + { + rt_uint8_t is_msix : 1; /* [PCI MSI/X] True if MSI-X */ + rt_uint8_t multiple : 3; /* [PCI MSI/X] log2 num of messages allocated */ + rt_uint8_t multi_cap : 3; /* [PCI MSI/X] log2 num of messages supported */ + rt_uint8_t can_mask : 1; /* [PCI MSI/X] Masking supported? */ + rt_uint8_t is_64 : 1; /* [PCI MSI/X] Address size is 64bit? */ + rt_uint8_t is_virtual : 1; + int default_irq; /* [PCI MSI/X] The default pre-assigned non-MSI irq */ + } msi_attrib; + union + { + rt_uint8_t mask_pos; /* [PCI MSI] Mask register position */ + void *mask_base; /* [PCI MSI-X] Mask register base address */ + }; +}; + +struct rt_pci_msi_msg +{ + rt_uint32_t address_lo; + rt_uint32_t address_hi; + rt_uint32_t data; +}; + +struct rt_msi_desc +{ + rt_list_t list; + + /* Shared device/bus type independent data */ + int irq; + rt_size_t nvec_used; + struct rt_pci_device *pci_dev; + struct rt_pci_msi_msg msg; + DECLARE_BITMAP(affinity, RT_CPUS_NR); + + void (*write_msi_msg)(struct rt_pci_msi_desc *entry, void *data); + void *write_msi_msg_data; + + rt_uint16_t msi_index; + struct rt_pci_msi_desc pci; + + void *priv; +}; + +#define rt_msi_first_desc(dev) \ + rt_list_isempty(&dev->msi_desc_nodes) ? RT_NULL : rt_list_entry(dev->msi_desc_nodes.next, struct msi_desc, list) + +#define rt_msi_for_each_desc(desc, dev) \ + if (!rt_list_isempty(&dev->msi_desc_nodes) || (desc = RT_NULL, RT_FALSE)) \ + rt_list_for_each_entry(desc, &dev->msi_desc_nodes, list) + +#define rt_msix_table_size(flags) ((flags & PCIM_MSIXCTRL_TABLE_SIZE) + 1) + +void rt_pci_msi_mask_irq(struct rt_pic_irq *pirq); +void rt_pci_msi_unmask_irq(struct rt_pic_irq *pirq); + +#endif /* __PCI_MSI_H__ */ diff --git a/components/drivers/include/drivers/phy.h b/components/drivers/include/drivers/phy.h index 862b711ff069..e32ffa138586 100644 --- a/components/drivers/include/drivers/phy.h +++ b/components/drivers/include/drivers/phy.h @@ -47,23 +47,39 @@ typedef struct rt_phy_device struct rt_device parent; struct rt_mdio_bus *bus; rt_uint32_t addr; - struct rt_phy_ops *ops; + const struct rt_phy_ops *ops; }rt_phy_t; typedef rt_int32_t rt_phy_status; struct rt_phy_ops { - rt_phy_status (*init)(void *object, rt_uint32_t phy_addr, rt_uint32_t src_clock_hz); - rt_phy_status (*read)(rt_uint32_t reg, rt_uint32_t *data); - rt_phy_status (*write)(rt_uint32_t reg, rt_uint32_t data); - rt_phy_status (*loopback)(rt_uint32_t mode, rt_uint32_t speed, rt_bool_t enable); - rt_phy_status (*get_link_status)(rt_bool_t *status); - rt_phy_status (*get_link_speed_duplex)(rt_uint32_t *speed, rt_uint32_t *duplex); + rt_phy_status (*init)(struct rt_phy_device *, void *object, rt_uint32_t phy_addr, rt_uint32_t src_clock_hz); + rt_phy_status (*exit)(struct rt_phy_device *, void *object, rt_uint32_t phy_addr); + rt_phy_status (*power_on)(struct rt_phy_device *); + rt_phy_status (*power_off)(struct rt_phy_device *); + rt_phy_status (*read)(struct rt_phy_device *, rt_uint32_t reg, rt_uint32_t *data); + rt_phy_status (*write)(struct rt_phy_device *, rt_uint32_t reg, rt_uint32_t data); + rt_phy_status (*loopback)(struct rt_phy_device *, rt_uint32_t mode, rt_uint32_t speed, rt_bool_t enable); + rt_phy_status (*get_link_status)(struct rt_phy_device *, rt_bool_t *status); + rt_phy_status (*get_link_speed_duplex)(struct rt_phy_device *, rt_uint32_t *speed, rt_uint32_t *duplex); +#ifdef RT_USING_DM + rt_err_t (*ofw_parse)(struct rt_phy_device *, struct rt_ofw_cell_args *phy_args); +#endif }; rt_err_t rt_hw_phy_register(struct rt_phy_device *phy, const char *name); +rt_phy_status rt_phy_init(struct rt_phy_device *phy, void *object, rt_uint32_t phy_addr, rt_uint32_t src_clock_hz); +rt_phy_status rt_phy_exit(struct rt_phy_device *phy, void *object, rt_uint32_t phy_addr); +rt_phy_status rt_phy_power_on(struct rt_phy_device *phy); +rt_phy_status rt_phy_power_off(struct rt_phy_device *phy); + +#ifdef RT_USING_DM +struct rt_phy_device *rt_phy_get_by_index(struct rt_device *dev, int index); +struct rt_phy_device *rt_phy_get_by_name(struct rt_device *dev, const char *id); +#endif /* RT_USING_DM */ + #ifdef __cplusplus } #endif diff --git a/components/drivers/include/drivers/pic.h b/components/drivers/include/drivers/pic.h index 8b02de69d4b9..d7293e133853 100755 --- a/components/drivers/include/drivers/pic.h +++ b/components/drivers/include/drivers/pic.h @@ -25,15 +25,26 @@ struct rt_pic_irq; struct rt_pic { + /* + * Other IC is not implemented with PIC but rt_device/object, we need to + * identify with this object: + * + * struct rt_ic_XYZ_device + * { + * struct rt_device parent; + * struct rt_pic pic; + * ... + * }; + */ + struct rt_object parent; + rt_list_t list; - struct rt_pic_ops *ops; + const struct rt_pic_ops *ops; void *priv_data; void *user_data; - struct rt_pic *parent; - int irq_start; rt_size_t irq_nr; struct rt_pic_irq *pirqs; @@ -66,6 +77,9 @@ struct rt_pic_ops int (*irq_map)(struct rt_pic *pic, int hwirq, rt_uint32_t mode); rt_err_t (*irq_parse)(struct rt_pic *pic, struct rt_ofw_cell_args *args, struct rt_pic_irq *out_pirq); + +#define RT_PIC_F_IRQ_ROUTING RT_BIT(0) /* Routing ISR when cascade */ + rt_ubase_t flags; }; struct rt_pic_isr @@ -95,6 +109,9 @@ struct rt_pic_irq rt_uint32_t priority; DECLARE_BITMAP(affinity, RT_CPUS_NR); + rt_list_t list; + rt_list_t children_nodes; + struct rt_pci_msi_desc *msi_desc; struct rt_pic_isr isr; @@ -102,8 +119,12 @@ struct rt_pic_irq struct rt_spinlock rw_lock; struct rt_pic *pic; + struct rt_pic_irq *parent; }; +void rt_pic_default_name(struct rt_pic *pic); +struct rt_pic *rt_pic_dynamic_cast(void *ptr); + rt_err_t rt_pic_linear_irq(struct rt_pic *pic, rt_size_t irq_nr); int rt_pic_config_ipi(struct rt_pic *pic, int ipi_index, int hwirq); @@ -120,9 +141,10 @@ rt_inline struct rt_pic_irq *rt_pic_find_irq(struct rt_pic *pic, int irq_index) } struct rt_pic_irq *rt_pic_find_ipi(struct rt_pic *pic, int ipi_index); +struct rt_pic_irq *rt_pic_find_pirq(struct rt_pic *pic, int irq); -int rt_pic_cascade(struct rt_pic *pic, struct rt_pic *parent_pic, int hwirq, rt_uint32_t mode); -void rt_pic_uncascade(struct rt_pic *pic, int irq); +rt_err_t rt_pic_cascade(struct rt_pic_irq *pirq, int parent_irq); +rt_err_t rt_pic_uncascade(struct rt_pic_irq *pirq); rt_err_t rt_pic_attach_irq(int irq, rt_isr_handler_t handler, void *uid, const char *name, int flags); rt_err_t rt_pic_detach_irq(int irq, void *uid); @@ -150,15 +172,15 @@ rt_err_t rt_pic_irq_set_triger_mode(int irq, rt_uint32_t mode); rt_uint32_t rt_pic_irq_get_triger_mode(int irq); void rt_pic_irq_send_ipi(int irq, bitmap_t *cpumask); -void rt_pic_irq_parent_enable(struct rt_pic *ppic, struct rt_pic_irq *pirq); -void rt_pic_irq_parent_disable(struct rt_pic *ppic, struct rt_pic_irq *pirq); -void rt_pic_irq_parent_ack(struct rt_pic *ppic, struct rt_pic_irq *pirq); -void rt_pic_irq_parent_mask(struct rt_pic *ppic, struct rt_pic_irq *pirq); -void rt_pic_irq_parent_unmask(struct rt_pic *ppic, struct rt_pic_irq *pirq); -void rt_pic_irq_parent_eoi(struct rt_pic *ppic, struct rt_pic_irq *pirq); -rt_err_t rt_pic_irq_parent_set_priority(struct rt_pic *ppic, struct rt_pic_irq *pirq, rt_uint32_t priority); -rt_err_t rt_pic_irq_parent_set_affinity(struct rt_pic *ppic, struct rt_pic_irq *pirq, bitmap_t *affinity); -rt_err_t rt_pic_irq_parent_set_triger_mode(struct rt_pic *ppic, struct rt_pic_irq *pirq, rt_uint32_t mode); +void rt_pic_irq_parent_enable(struct rt_pic_irq *pirq); +void rt_pic_irq_parent_disable(struct rt_pic_irq *pirq); +void rt_pic_irq_parent_ack(struct rt_pic_irq *pirq); +void rt_pic_irq_parent_mask(struct rt_pic_irq *pirq); +void rt_pic_irq_parent_unmask(struct rt_pic_irq *pirq); +void rt_pic_irq_parent_eoi(struct rt_pic_irq *pirq); +rt_err_t rt_pic_irq_parent_set_priority(struct rt_pic_irq *pirq, rt_uint32_t priority); +rt_err_t rt_pic_irq_parent_set_affinity(struct rt_pic_irq *pirq, bitmap_t *affinity); +rt_err_t rt_pic_irq_parent_set_triger_mode(struct rt_pic_irq *pirq, rt_uint32_t mode); #define RT_PIC_OFW_DECLARE(name, ids, handler) RT_OFW_STUB_EXPORT(name, ids, pic, handler) diff --git a/components/drivers/include/drivers/pin.h b/components/drivers/include/drivers/pin.h index 4b369d5b8d3f..111b7bb9561b 100644 --- a/components/drivers/include/drivers/pin.h +++ b/components/drivers/include/drivers/pin.h @@ -7,6 +7,7 @@ * Date Author Notes * 2015-01-20 Bernard the first version * 2017-10-20 ZYH add mode open drain and input pull down + * 2022-11-26 GuEe-GUI add pic for pin in dm, support pinctrl */ #ifndef PIN_H__ @@ -18,10 +19,26 @@ extern "C" { #endif +#ifdef RT_USING_DM +#include + +struct rt_pin_irqchip +{ + struct rt_pic parent; + + int irq; + rt_base_t pin_range[2]; +}; +#endif /* RT_USING_DM */ + /* pin device and operations for RT-Thread */ struct rt_device_pin { struct rt_device parent; +#ifdef RT_USING_DM + /* MUST keep the order member after parent */ + struct rt_pin_irqchip irqchip; +#endif /* RT_USING_DM */ const struct rt_pin_ops *ops; }; @@ -36,6 +53,39 @@ struct rt_device_pin #define PIN_MODE_INPUT_PULLDOWN 0x03 #define PIN_MODE_OUTPUT_OD 0x04 +#ifdef RT_USING_PINCTRL +enum +{ + PIN_CONFIG_BIAS_BUS_HOLD, + PIN_CONFIG_BIAS_DISABLE, + PIN_CONFIG_BIAS_HIGH_IMPEDANCE, + PIN_CONFIG_BIAS_PULL_DOWN, + PIN_CONFIG_BIAS_PULL_PIN_DEFAULT, + PIN_CONFIG_BIAS_PULL_UP, + PIN_CONFIG_DRIVE_OPEN_DRAIN, + PIN_CONFIG_DRIVE_OPEN_SOURCE, + PIN_CONFIG_DRIVE_PUSH_PULL, + PIN_CONFIG_DRIVE_STRENGTH, + PIN_CONFIG_DRIVE_STRENGTH_UA, + PIN_CONFIG_INPUT_DEBOUNCE, + PIN_CONFIG_INPUT_ENABLE, + PIN_CONFIG_INPUT_SCHMITT, + PIN_CONFIG_INPUT_SCHMITT_ENABLE, + PIN_CONFIG_MODE_LOW_POWER, + PIN_CONFIG_MODE_PWM, + PIN_CONFIG_OUTPUT, + PIN_CONFIG_OUTPUT_ENABLE, + PIN_CONFIG_OUTPUT_IMPEDANCE_OHMS, + PIN_CONFIG_PERSIST_STATE, + PIN_CONFIG_POWER_SOURCE, + PIN_CONFIG_SKEW_DELAY, + PIN_CONFIG_SLEEP_HARDWARE_STATE, + PIN_CONFIG_SLEW_RATE, + PIN_CONFIG_END = 0x7f, + PIN_CONFIG_MAX = 0xff, +}; +#endif /* RT_USING_PINCTRL */ + #define PIN_IRQ_MODE_RISING 0x00 #define PIN_IRQ_MODE_FALLING 0x01 #define PIN_IRQ_MODE_RISING_FALLING 0x02 @@ -66,6 +116,16 @@ struct rt_pin_irq_hdr void (*hdr)(void *args); void *args; }; + +#ifdef RT_USING_PINCTRL +struct rt_pin_ctrl_conf_params +{ + const char *propname; + rt_uint32_t param; + rt_uint32_t default_value; +}; +#endif /* RT_USING_PINCTRL */ + struct rt_pin_ops { void (*pin_mode)(struct rt_device *device, rt_base_t pin, rt_uint8_t mode); @@ -76,6 +136,13 @@ struct rt_pin_ops rt_err_t (*pin_detach_irq)(struct rt_device *device, rt_base_t pin); rt_err_t (*pin_irq_enable)(struct rt_device *device, rt_base_t pin, rt_uint8_t enabled); rt_base_t (*pin_get)(const char *name); +#ifdef RT_USING_DM + rt_err_t (*pin_irq_mode)(struct rt_device *device, rt_base_t pin, rt_uint8_t mode); + rt_ssize_t (*pin_parse)(struct rt_device *device, struct rt_ofw_cell_args *args, rt_uint32_t *flags); +#endif +#ifdef RT_USING_PINCTRL + rt_err_t (*pin_ctrl_confs_apply)(struct rt_device *device, void *fw_conf_np); +#endif /* RT_USING_PINCTRL */ }; int rt_device_pin_register(const char *name, const struct rt_pin_ops *ops, void *user_data); @@ -88,6 +155,24 @@ rt_err_t rt_pin_attach_irq(rt_base_t pin, rt_uint8_t mode, rt_err_t rt_pin_detach_irq(rt_base_t pin); rt_err_t rt_pin_irq_enable(rt_base_t pin, rt_uint8_t enabled); +#ifdef RT_USING_DM +rt_ssize_t rt_pin_get_named_pin(struct rt_device *dev, const char *propname, int index, + rt_uint8_t *out_mode, rt_uint8_t *out_value); +rt_ssize_t rt_pin_get_named_pin_count(struct rt_device *dev, const char *propname); + +#ifdef RT_USING_OFW +rt_ssize_t rt_ofw_get_named_pin(struct rt_ofw_node *np, const char *propname, int index, + rt_uint8_t *out_mode, rt_uint8_t *out_value); +rt_ssize_t rt_ofw_get_named_pin_count(struct rt_ofw_node *np, const char *propname); +#endif +#endif /* RT_USING_DM */ + +#ifdef RT_USING_PINCTRL +rt_ssize_t rt_pin_ctrl_confs_lookup(struct rt_device *device, const char *name); +rt_err_t rt_pin_ctrl_confs_apply(struct rt_device *device, int index); +rt_err_t rt_pin_ctrl_confs_apply_by_name(struct rt_device *device, const char *name); +#endif /* RT_USING_PINCTRL */ + #ifdef __cplusplus } #endif diff --git a/components/drivers/include/drivers/platform.h b/components/drivers/include/drivers/platform.h index 3caccabd2d40..2f9915ceacfd 100644 --- a/components/drivers/include/drivers/platform.h +++ b/components/drivers/include/drivers/platform.h @@ -18,6 +18,8 @@ struct rt_platform_device { struct rt_device parent; + int dev_id; + const char *name; const struct rt_ofw_node_id *id; @@ -32,6 +34,8 @@ struct rt_platform_driver const struct rt_ofw_node_id *ids; rt_err_t (*probe)(struct rt_platform_device *pdev); + rt_err_t (*remove)(struct rt_platform_device *pdev); + rt_err_t (*shutdown)(struct rt_platform_device *pdev); }; struct rt_platform_device *rt_platform_device_alloc(const char *name); @@ -39,6 +43,8 @@ struct rt_platform_device *rt_platform_device_alloc(const char *name); rt_err_t rt_platform_driver_register(struct rt_platform_driver *pdrv); rt_err_t rt_platform_device_register(struct rt_platform_device *pdev); +rt_err_t rt_platform_ofw_device_probe_child(struct rt_ofw_node *np); + #define RT_PLATFORM_DRIVER_EXPORT(driver) RT_DRIVER_EXPORT(driver, platform, BUILIN) #endif /* __PLATFORM_H__ */ diff --git a/components/drivers/include/drivers/pm.h b/components/drivers/include/drivers/pm.h index 6d5961b2fa27..45b3ae09e6a5 100644 --- a/components/drivers/include/drivers/pm.h +++ b/components/drivers/include/drivers/pm.h @@ -199,9 +199,6 @@ void rt_pm_release(rt_uint8_t sleep_mode); void rt_pm_release_all(rt_uint8_t sleep_mode); int rt_pm_run_enter(rt_uint8_t run_mode); -extern void (*rt_pm_shutdown)(void); -extern void (*rt_pm_reset)(void); - void rt_pm_device_register(struct rt_device *device, const struct rt_device_pm_ops *ops); void rt_pm_device_unregister(struct rt_device *device); diff --git a/components/drivers/include/drivers/regulator.h b/components/drivers/include/drivers/regulator.h new file mode 100644 index 000000000000..be787e38c7d0 --- /dev/null +++ b/components/drivers/include/drivers/regulator.h @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-09-23 GuEe-GUI first version + */ + +#ifndef __REGULATOR_H__ +#define __REGULATOR_H__ + +#include +#include +#include + +#include + +#define RT_REGULATOR_UVOLT_INVALID (((int)(RT_UINT32_MAX >> 1))) + +struct rt_regulator_param +{ + const char *name; + + int min_uvolt; /* In uV */ + int max_uvolt; /* In uV */ + int min_uamp; /* In uA */ + int max_uamp; /* In uA */ + int ramp_delay; /* In uV/usec */ + int enable_delay; /* In usec */ + int off_on_delay; /* In usec */ + + rt_uint32_t enable_active_high:1; + rt_uint32_t boot_on:1; /* Is enabled on boot */ + rt_uint32_t always_on:1; /* Must be enabled */ + rt_uint32_t soft_start:1; /* Ramp voltage slowly */ + rt_uint32_t pull_down:1; /* Pull down resistor when regulator off */ + rt_uint32_t over_current_protection:1; /* Auto disable on over current */ +}; + +struct rt_regulator_ops; + +struct rt_regulator_node +{ + rt_list_t list; + rt_list_t children_nodes; + + struct rt_device *dev; + struct rt_regulator_node *parent; + + const char *supply_name; + const struct rt_regulator_ops *ops; + + struct ref ref; + rt_atomic_t enabled_count; + + const struct rt_regulator_param *param; + + rt_list_t notifier_nodes; + + void *priv; +}; + +/* + * NOTE: Power regulator control is dangerous work. We don't want non-internal + * consumer could access the power regulator tree without regulator's API. So + * we defined the `rt_regulator` member in core instead of here. + */ +struct rt_regulator; + +#define RT_REGULATOR_MODE_INVALID 0 +#define RT_REGULATOR_MODE_FAST RT_BIT(0) +#define RT_REGULATOR_MODE_NORMAL RT_BIT(1) +#define RT_REGULATOR_MODE_IDLE RT_BIT(2) +#define RT_REGULATOR_MODE_STANDBY RT_BIT(3) + +struct rt_regulator_ops +{ + rt_err_t (*enable)(struct rt_regulator_node *); + rt_err_t (*disable)(struct rt_regulator_node *); + rt_bool_t (*is_enabled)(struct rt_regulator_node *); + rt_err_t (*set_voltage)(struct rt_regulator_node *, int min_uvolt, int max_uvolt); + int (*get_voltage)(struct rt_regulator_node *); + rt_err_t (*set_mode)(struct rt_regulator_node *, rt_uint32_t mode); + rt_int32_t (*get_mode)(struct rt_regulator_node *); + rt_err_t (*set_ramp_delay)(struct rt_regulator_node *, int ramp); + rt_uint32_t (*enable_time)(struct rt_regulator_node *); +}; + +struct rt_regulator_notifier; + +#define RT_REGULATOR_MSG_ENABLE RT_BIT(0) +#define RT_REGULATOR_MSG_DISABLE RT_BIT(1) +#define RT_REGULATOR_MSG_VOLTAGE_CHANGE RT_BIT(2) +#define RT_REGULATOR_MSG_VOLTAGE_CHANGE_ERR RT_BIT(3) + +union rt_regulator_notifier_args +{ + struct + { + int old_uvolt; + int min_uvolt; + int max_uvolt; + }; +}; + +typedef rt_err_t (*rt_regulator_notifier_callback)(struct rt_regulator_notifier *notifier, + rt_ubase_t msg, void *data); + +struct rt_regulator_notifier +{ + rt_list_t list; + + struct rt_regulator *regulator; + rt_regulator_notifier_callback callback; + void *priv; +}; + +rt_err_t rt_regulator_register(struct rt_regulator_node *reg_np); +rt_err_t rt_regulator_unregister(struct rt_regulator_node *reg_np); + +rt_err_t rt_regulator_notifier_register(struct rt_regulator *reg, + struct rt_regulator_notifier *notifier); +rt_err_t rt_regulator_notifier_unregister(struct rt_regulator *reg, + struct rt_regulator_notifier *notifier); + +struct rt_regulator *rt_regulator_get_optional(struct rt_device *dev, const char *id); +void rt_regulator_put(struct rt_regulator *reg); + +rt_err_t rt_regulator_enable(struct rt_regulator *reg); +rt_err_t rt_regulator_disable(struct rt_regulator *reg); +rt_bool_t rt_regulator_is_enabled(struct rt_regulator *reg); + +rt_bool_t rt_regulator_is_supported_voltage(struct rt_regulator *reg, int min_uvolt, int max_uvolt); +rt_err_t rt_regulator_set_voltage(struct rt_regulator *reg, int min_uvolt, int max_uvolt); +int rt_regulator_get_voltage(struct rt_regulator *reg); + +rt_err_t rt_regulator_set_mode(struct rt_regulator *reg, rt_uint32_t mode); +rt_int32_t rt_regulator_get_mode(struct rt_regulator *reg); + +rt_inline rt_err_t rt_regulator_set_voltage_triplet(struct rt_regulator *reg, + int min_uvolt, int target_uvolt, int max_uvolt) +{ + if (!rt_regulator_set_voltage(reg, target_uvolt, max_uvolt)) + { + return RT_EOK; + } + + return rt_regulator_set_voltage(reg, min_uvolt, max_uvolt); +} + +#endif /* __REGULATOR_H__ */ diff --git a/components/drivers/include/drivers/reset.h b/components/drivers/include/drivers/reset.h new file mode 100644 index 000000000000..46b97cecc36f --- /dev/null +++ b/components/drivers/include/drivers/reset.h @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-11-26 GuEe-GUI first version + */ + +#ifndef __RESET_H__ +#define __RESET_H__ + +#include +#include +#include +#include + +struct rt_reset_control_ops; + +struct rt_reset_controller +{ + rt_list_t list; + rt_list_t rstc_nodes; + + const char *name; + const struct rt_reset_control_ops *ops; + + struct rt_ofw_node *ofw_node; + void *priv; + + struct rt_spinlock spinlock; +}; + +/* + * It seems that most reset controllers are coupled to CLK. + * So we need a generic extends object. + * Put it at the end if the CLK have multi out clocks. + */ +struct rt_reset_controller_clk_node +{ + struct rt_clk_node parent; + + struct rt_reset_controller rstcer; +}; + +struct rt_reset_control +{ + rt_list_t list; + + struct rt_reset_controller *rstcer; + + int id; + const char *con_id; + rt_bool_t is_array; + + void *priv; +}; + +struct rt_reset_control_ops +{ + /* + * rt_ofw_cell_args return: + * args[0] = rstc.id + */ + rt_err_t (*ofw_parse)(struct rt_reset_control *rstc, struct rt_ofw_cell_args *args); + /* API */ + rt_err_t (*reset)(struct rt_reset_control *rstc); + rt_err_t (*assert)(struct rt_reset_control *rstc); + rt_err_t (*deassert)(struct rt_reset_control *rstc); + int (*status)(struct rt_reset_control *rstc); +}; + +rt_err_t rt_reset_controller_register(struct rt_reset_controller *rstcer); +rt_err_t rt_reset_controller_unregister(struct rt_reset_controller *rstcer); + +rt_err_t rt_reset_control_reset(struct rt_reset_control *rstc); +rt_err_t rt_reset_control_assert(struct rt_reset_control *rstc); +rt_err_t rt_reset_control_deassert(struct rt_reset_control *rstc); +int rt_reset_control_status(struct rt_reset_control *rstc); + +rt_ssize_t rt_reset_control_get_count(struct rt_device *dev); +struct rt_reset_control *rt_reset_control_get_array(struct rt_device *dev); +struct rt_reset_control *rt_reset_control_get_by_index(struct rt_device *dev, int index); +struct rt_reset_control *rt_reset_control_get_by_name(struct rt_device *dev, const char *name); +void rt_reset_control_put(struct rt_reset_control *rstc); + +struct rt_reset_control *rt_ofw_get_reset_control_array(struct rt_ofw_node *np); +struct rt_reset_control *rt_ofw_get_reset_control_by_index(struct rt_ofw_node *np, int index); +struct rt_reset_control *rt_ofw_get_reset_control_by_name(struct rt_ofw_node *np, const char *name); + +#endif /* __RESET_H__ */ diff --git a/components/drivers/include/drivers/rt_drv_pwm.h b/components/drivers/include/drivers/rt_drv_pwm.h index e9e170714465..23bf1c35bd7c 100644 --- a/components/drivers/include/drivers/rt_drv_pwm.h +++ b/components/drivers/include/drivers/rt_drv_pwm.h @@ -41,6 +41,9 @@ struct rt_pwm_configuration rt_bool_t complementary; }; +#define rt_pwm_conf_duty_cycle(conf) ((conf)->pulse * 100 / (conf)->period) +#define rt_pwm_conf_pulse(conf, duty_cycle) ((duty_cycle) * (conf)->period / 100) + struct rt_device_pwm; struct rt_pwm_ops { @@ -62,5 +65,6 @@ rt_err_t rt_pwm_set_period(struct rt_device_pwm *device, int channel, rt_uint32_ rt_err_t rt_pwm_set_pulse(struct rt_device_pwm *device, int channel, rt_uint32_t pulse); rt_err_t rt_pwm_set_dead_time(struct rt_device_pwm *device, int channel, rt_uint32_t dead_time); rt_err_t rt_pwm_set_phase(struct rt_device_pwm *device, int channel, rt_uint32_t phase); +rt_err_t rt_pwm_get(struct rt_device_pwm *device, struct rt_pwm_configuration *cfg); #endif /* __DRV_PWM_H_INCLUDE__ */ diff --git a/components/drivers/include/drivers/scmi.h b/components/drivers/include/drivers/scmi.h new file mode 100644 index 000000000000..9a5180afbc31 --- /dev/null +++ b/components/drivers/include/drivers/scmi.h @@ -0,0 +1,307 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-11-26 GuEe-GUI first version + */ + +#ifndef __SCMI_H__ +#define __SCMI_H__ + +#include +#include +#include + +#define SCMI_PROTOCOL_ID_BASE 0x10 +#define SCMI_PROTOCOL_ID_POWER 0x11 +#define SCMI_PROTOCOL_ID_SYSTEM 0x12 +#define SCMI_PROTOCOL_ID_PERF 0x13 +#define SCMI_PROTOCOL_ID_CLOCK 0x14 +#define SCMI_PROTOCOL_ID_SENSOR 0x15 +#define SCMI_PROTOCOL_ID_RESET 0x16 +#define SCMI_PROTOCOL_ID_VOLTAGE 0x17 +#define SCMI_PROTOCOL_ID_POWERCAP 0x18 + +#define SCMI_SUCCESS 0 /* Success */ +#define SCMI_ERR_SUPPORT (-1) /* Not supported */ +#define SCMI_ERR_PARAMS (-2) /* Invalid Parameters */ +#define SCMI_ERR_ACCESS (-3) /* Invalid access/permission denied */ +#define SCMI_ERR_ENTRY (-4) /* Not found */ +#define SCMI_ERR_RANGE (-5) /* Value out of range */ +#define SCMI_ERR_BUSY (-6) /* Device busy */ +#define SCMI_ERR_COMMS (-7) /* Communication Error */ +#define SCMI_ERR_GENERIC (-8) /* Generic Error */ +#define SCMI_ERR_HARDWARE (-9) /* Hardware Error */ +#define SCMI_ERR_PROTOCOL (-10) /* Protocol Error */ + +#define SCMI_MAX_STR_SIZE 64 +#define SCMI_SHORT_NAME_MAX_SIZE 16 +#define SCMI_MAX_NUM_RATES 16 + +struct rt_scmi_device; + +/* + * struct rt_scmi_msg - Context of a SCMI message sent and the response received + * + * @protocol_id: SCMI protocol ID + * @message_id: SCMI message ID for a defined protocol ID + * @in_msg: Pointer to the message payload sent by the driver + * @in_msg_size: Byte size of the message payload sent + * @out_msg: Pointer to buffer to store response message payload + * @out_msg_size: Byte size of the response buffer and response payload + */ +struct rt_scmi_msg +{ + struct rt_scmi_device *sdev; + + rt_uint32_t message_id; + + rt_uint8_t *in_msg; + rt_size_t in_msg_size; + + rt_uint8_t *out_msg; + rt_size_t out_msg_size; +}; + +/* Helper macro to match a message on input/output array references */ +#define RT_SCMI_MSG_IN(MSG_ID, IN, OUT) \ +(struct rt_scmi_msg) { \ + .message_id = MSG_ID, \ + .in_msg = (rt_uint8_t *)&(IN), \ + .in_msg_size = sizeof(IN), \ + .out_msg = (rt_uint8_t *)&(OUT), \ + .out_msg_size = sizeof(OUT), \ +} + +enum scmi_common_message_id +{ + SCMI_COM_MSG_VERSION = 0x0, + SCMI_COM_MSG_ATTRIBUTES = 0x1, + SCMI_COM_MSG_MESSAGE_ATTRIBUTES = 0x2, +}; + +/* + * SCMI Power Protocol + */ +enum scmi_power_protocol_cmd +{ + SCMI_POWER_DOMAIN_ATTRIBUTES = 0x3, + SCMI_POWER_STATE_SET = 0x4, + SCMI_POWER_STATE_GET = 0x5, + SCMI_POWER_STATE_NOTIFY = 0x6, + SCMI_POWER_DOMAIN_NAME_GET = 0x8, +}; + +struct scmi_power_attributes +{ + rt_le16_t num_domains; + rt_le16_t reserved; + rt_le32_t stats_addr_low; + rt_le32_t stats_addr_high; + rt_le32_t stats_size; +}; + +struct scmi_power_state_set_in +{ + rt_le32_t flags; + rt_le32_t domain; +#define SCMI_POWER_STATE_TYPE_SHIFT 30 +#define SCMI_POWER_STATE_ID_MASK (RT_BIT(28) - 1) +#define SCMI_POWER_STATE_PARAM(type, id) ((((type) & RT_BIT(0)) << SCMI_POWER_STATE_TYPE_SHIFT) | ((id) & SCMI_POWER_STATE_ID_MASK)) +#define SCMI_POWER_STATE_GENERIC_ON SCMI_POWER_STATE_PARAM(0, 0) +#define SCMI_POWER_STATE_GENERIC_OFF SCMI_POWER_STATE_PARAM(1, 0) + rt_le32_t state; +}; + +struct scmi_power_state_set_out +{ + rt_le32_t state; +}; + +/* + * SCMI Clock Protocol + */ +enum scmi_clock_message_id +{ + SCMI_CLOCK_ATTRIBUTES = 0x3, + SCMI_CLOCK_DESCRIBE_RATES = 0x4, + SCMI_CLOCK_RATE_SET = 0x5, + SCMI_CLOCK_RATE_GET = 0x6, + SCMI_CLOCK_CONFIG_SET = 0x7, +}; + +/** + * struct scmi_clk_state_in - Message payload for CLOCK_CONFIG_SET command + * @clock_id: SCMI clock ID + * @attributes: Attributes of the targets clock state + */ +struct scmi_clk_state_in +{ + rt_le32_t clock_id; + rt_le32_t attributes; +}; + +/** + * struct scmi_clk_state_out - Response payload for CLOCK_CONFIG_SET command + * @status: SCMI command status + */ +struct scmi_clk_state_out +{ + rt_le32_t status; +}; + +/** + * struct scmi_clk_state_in - Message payload for CLOCK_RATE_GET command + * @clock_id: SCMI clock ID + * @attributes: Attributes of the targets clock state + */ +struct scmi_clk_rate_get_in +{ + rt_le32_t clock_id; +}; + +/** + * struct scmi_clk_rate_get_out - Response payload for CLOCK_RATE_GET command + * @status: SCMI command status + * @rate_lsb: 32bit LSB of the clock rate in Hertz + * @rate_msb: 32bit MSB of the clock rate in Hertz + */ +struct scmi_clk_rate_get_out +{ + rt_le32_t status; + rt_le32_t rate_lsb; + rt_le32_t rate_msb; +}; + +/** + * struct scmi_clk_state_in - Message payload for CLOCK_RATE_SET command + * @clock_id: SCMI clock ID + * @flags: Flags for the clock rate set request + * @rate_lsb: 32bit LSB of the clock rate in Hertz + * @rate_msb: 32bit MSB of the clock rate in Hertz + */ +struct scmi_clk_rate_set_in +{ +#define SCMI_CLK_RATE_ASYNC_NOTIFY RT_BIT(0) +#define SCMI_CLK_RATE_ASYNC_NORESP (RT_BIT(0) | RT_BIT(1)) +#define SCMI_CLK_RATE_ROUND_DOWN 0 +#define SCMI_CLK_RATE_ROUND_UP RT_BIT(2) +#define SCMI_CLK_RATE_ROUND_CLOSEST RT_BIT(3) + rt_le32_t flags; + rt_le32_t clock_id; + rt_le32_t rate_lsb; + rt_le32_t rate_msb; +}; + +/** + * struct scmi_clk_rate_set_out - Response payload for CLOCK_RATE_SET command + * @status: SCMI command status + */ +struct scmi_clk_rate_set_out +{ + rt_le32_t status; +}; + +/* + * SCMI Reset Domain Protocol + */ + +enum scmi_reset_message_id +{ + SCMI_RESET_ATTRIBUTES = 0x3, + SCMI_RESET_RESET = 0x4, +}; + +#define SCMI_ATTRIBUTES_FLAG_ASYNC RT_BIT(31) +#define SCMI_ATTRIBUTES_FLAG_NOTIF RT_BIT(30) + +/** + * struct scmi_attr_in - Payload for RESET_DOMAIN_ATTRIBUTES message + * @domain_id: SCMI reset domain ID + */ +struct scmi_attr_in +{ + rt_le32_t domain_id; +}; + +/** + * struct scmi_attr_out - Payload for RESET_DOMAIN_ATTRIBUTES response + * @attributes: Retrieved attributes of the reset domain + * @latency: Reset cycle max lantency + * @name: Reset domain name + */ +struct scmi_attr_out +{ + rt_le32_t attributes; + rt_le32_t latency; + rt_uint8_t name[SCMI_SHORT_NAME_MAX_SIZE]; +}; + +/** + * struct scmi_reset_in - Message payload for RESET command + * @domain_id: SCMI reset domain ID + * @flags: Flags for the reset request + * @reset_state: Reset target state + */ +struct scmi_reset_in +{ + rt_le32_t domain_id; +#define SCMI_RESET_FLAG_RESET RT_BIT(0) +#define SCMI_RESET_FLAG_ASSERT RT_BIT(1) +#define SCMI_RESET_FLAG_ASYNC RT_BIT(2) + rt_le32_t flags; +#define SCMI_ARCH_COLD_RESET 0 + rt_le32_t reset_state; +}; + +/** + * struct scmi_reset_out - Response payload for RESET command + * @status: SCMI command status + */ +struct scmi_reset_out +{ + rt_le32_t status; +}; + +struct scmi_agent; + +struct rt_scmi_device_id +{ + rt_uint8_t protocol_id; + const char *name; +}; + +struct rt_scmi_device +{ + struct rt_device parent; + + const char *name; + rt_uint8_t protocol_id; + + struct scmi_agent *agent; +}; + +struct rt_scmi_driver +{ + struct rt_driver parent; + + const char *name; + const struct rt_scmi_device_id *ids; + + rt_err_t (*probe)(struct rt_scmi_device *sdev); + rt_err_t (*remove)(struct rt_scmi_device *sdev); + rt_err_t (*shutdown)(struct rt_scmi_device *sdev); +}; + +rt_err_t rt_scmi_driver_register(struct rt_scmi_driver *driver); +rt_err_t rt_scmi_device_register(struct rt_scmi_device *device); + +#define RT_SCMI_DRIVER_EXPORT(driver) RT_DRIVER_EXPORT(driver, scmi, BUILIN) + +rt_err_t rt_scmi_process_msg(struct rt_scmi_device *sdev, struct rt_scmi_msg *msg); +const char *rt_scmi_strerror(rt_base_t err); + +#endif /* __SCMI_H__ */ diff --git a/components/drivers/include/drivers/spi.h b/components/drivers/include/drivers/spi.h index 7f7c2cedccdc..b8fc04da03eb 100644 --- a/components/drivers/include/drivers/spi.h +++ b/components/drivers/include/drivers/spi.h @@ -78,7 +78,12 @@ struct rt_spi_configuration { rt_uint8_t mode; rt_uint8_t data_width; +#ifdef RT_USING_DM + rt_uint8_t data_width_tx; + rt_uint8_t data_width_rx; +#else rt_uint16_t reserved; +#endif rt_uint32_t max_hz; }; @@ -90,6 +95,12 @@ struct rt_spi_bus rt_uint8_t mode; const struct rt_spi_ops *ops; +#ifdef RT_USING_DM + rt_base_t *pins; + rt_bool_t slave; + int num_chipselect; +#endif /* RT_USING_DM */ + struct rt_mutex lock; struct rt_spi_device *owner; }; @@ -103,6 +114,17 @@ struct rt_spi_ops rt_ssize_t (*xfer)(struct rt_spi_device *device, struct rt_spi_message *message); }; +#ifdef RT_USING_DM +struct rt_spi_delay +{ +#define RT_SPI_DELAY_UNIT_USECS 0 +#define RT_SPI_DELAY_UNIT_NSECS 1 +#define RT_SPI_DELAY_UNIT_SCK 2 + rt_uint16_t value; + rt_uint8_t unit; +}; +#endif /* RT_USING_DM */ + /** * SPI Virtual BUS, one device must connected to a virtual BUS */ @@ -111,6 +133,17 @@ struct rt_spi_device struct rt_device parent; struct rt_spi_bus *bus; +#ifdef RT_USING_DM + const char *name; + const struct rt_spi_device_id *id; + const struct rt_ofw_node_id *ofw_id; + + rt_uint8_t chip_select; + struct rt_spi_delay cs_setup; + struct rt_spi_delay cs_hold; + struct rt_spi_delay cs_inactive; +#endif + struct rt_spi_configuration config; rt_base_t cs_pin; void *user_data; @@ -166,6 +199,31 @@ struct rt_qspi_device #define SPI_DEVICE(dev) ((struct rt_spi_device *)(dev)) +#ifdef RT_USING_DM +struct rt_spi_device_id +{ + char name[20]; + void *data; +}; + +struct rt_spi_driver +{ + struct rt_driver parent; + + const struct rt_spi_device_id *ids; + const struct rt_ofw_node_id *ofw_ids; + + rt_err_t (*probe)(struct rt_spi_device *device); + rt_err_t (*remove)(struct rt_spi_device *device); + rt_err_t (*shutdown)(struct rt_spi_device *device); +}; + +rt_err_t rt_spi_driver_register(struct rt_spi_driver *driver); +rt_err_t rt_spi_device_register(struct rt_spi_device *device); + +#define RT_SPI_DRIVER_EXPORT(driver) RT_DRIVER_EXPORT(driver, spi, BUILIN) +#endif /* RT_USING_DM */ + /* register a SPI bus */ rt_err_t rt_spi_bus_register(struct rt_spi_bus *bus, const char *name, diff --git a/components/drivers/include/drivers/syscon.h b/components/drivers/include/drivers/syscon.h index 672449570791..2ab597c7703b 100755 --- a/components/drivers/include/drivers/syscon.h +++ b/components/drivers/include/drivers/syscon.h @@ -30,7 +30,7 @@ rt_err_t rt_syscon_write(struct rt_syscon *syscon, rt_off_t offset, rt_uint32_t rt_err_t rt_syscon_update_bits(struct rt_syscon *syscon, rt_off_t offset, rt_uint32_t mask, rt_uint32_t val); struct rt_syscon *rt_syscon_find_by_ofw_node(struct rt_ofw_node *np); -struct rt_syscon *rt_syscon_find_by_compatible(const char *compatible); -struct rt_syscon *rt_syscon_find_by_phandle(struct rt_ofw_node *np, const char *propname); +struct rt_syscon *rt_syscon_find_by_ofw_compatible(const char *compatible); +struct rt_syscon *rt_syscon_find_by_ofw_phandle(struct rt_ofw_node *np, const char *propname); #endif /* __SYSCON_H__ */ diff --git a/components/drivers/include/drivers/thermal.h b/components/drivers/include/drivers/thermal.h new file mode 100644 index 000000000000..03b8ad4c195f --- /dev/null +++ b/components/drivers/include/drivers/thermal.h @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-3-08 GuEe-GUI the first version + */ + +#ifndef __THERMAL_H__ +#define __THERMAL_H__ + +#include + +/* No upper/lower limit requirement */ +#define RT_THERMAL_NO_LIMIT ((rt_uint32_t)~0) + +struct rt_thermal_zone_ops; +struct rt_thermal_cooling_device; +struct rt_thermal_cooling_device_ops; + +struct rt_thermal_trip +{ + int temperature; /* temperature value in millidegree celsius */ + int hysteresis; /* relative hysteresis in millidegree celsius */ + void *priv; +}; + +struct rt_thermal_zone_params +{ + int sustainable_power; /* Sustainable power (heat) that this thermal zone + * can dissipate in mW + */ + + int slope; /* slope of a linear temperature adjustment curve */ + int offset; /* offset of a linear temperature adjustment curve */ +}; + +struct rt_thermal_zone_device +{ + struct rt_device parent; + + int zone_id; + const struct rt_thermal_zone_ops *ops; + + rt_size_t trips_nr; + struct rt_thermal_trip *trips; + struct rt_thermal_zone_params params; + + rt_list_t notifier_nodes; + + rt_list_t zone_list; + rt_list_t cooling_list; + struct rt_thermal_cooling_device *cooling_dev; + + void *priv; +}; + +struct rt_thermal_zone_ops +{ + rt_err_t (*bind)(struct rt_thermal_zone_device *, struct rt_thermal_cooling_device *); + rt_err_t (*unbind)(struct rt_thermal_zone_device *, struct rt_thermal_cooling_device *); + rt_err_t (*get_temp)(struct rt_thermal_zone_device *, int *out_temp); + rt_err_t (*set_trips)(struct rt_thermal_zone_device *, int low_temp, int high_temp); + rt_err_t (*set_trip_temp)(struct rt_thermal_zone_device *, int trip_id, int temp); + rt_err_t (*set_trip_hyst)(struct rt_thermal_zone_device *, int trip_id, int hyst); +}; + +struct rt_thermal_cooling_device +{ + struct rt_device parent; + + const struct rt_thermal_cooling_device_ops *ops; + + rt_ubase_t max_level; /* The cooling capacity indicator */ + + rt_list_t zone_nodes; + + void *priv; +}; + +struct rt_thermal_cooling_device_ops +{ + rt_err_t (*get_max_level)(struct rt_thermal_cooling_device *, rt_ubase_t *out_level); + rt_err_t (*get_cur_level)(struct rt_thermal_cooling_device *, rt_ubase_t *out_level); + rt_err_t (*set_cur_level)(struct rt_thermal_cooling_device *, rt_ubase_t level); +}; + +struct rt_thermal_notifier; + +#define RT_THERMAL_MSG_EVENT_UNSPECIFIED RT_BIT(0) /* Unspecified event */ +#define RT_THERMAL_MSG_EVENT_TEMP_SAMPLE RT_BIT(0) /* New Temperature sample */ +#define RT_THERMAL_MSG_TRIP_VIOLATED RT_BIT(0) /* TRIP Point violation */ +#define RT_THERMAL_MSG_TRIP_CHANGED RT_BIT(0) /* TRIP Point temperature changed */ +#define RT_THERMAL_MSG_DEVICE_DOWN RT_BIT(0) /* Thermal device is down */ +#define RT_THERMAL_MSG_DEVICE_UP RT_BIT(0) /* Thermal device is up after a down event */ +#define RT_THERMAL_MSG_DEVICE_POWER_CAPABILITY_CHANGED RT_BIT(0) /* power capability changed */ +#define RT_THERMAL_MSG_TABLE_CHANGED RT_BIT(0) /* Thermal table(s) changed */ +#define RT_THERMAL_MSG_EVENT_KEEP_ALIVE RT_BIT(0) /* Request for user space handler to respond */ + +typedef rt_err_t (*rt_thermal_notifier_callback)(struct rt_thermal_notifier *notifier, + rt_ubase_t msg); + +struct rt_thermal_notifier +{ + rt_list_t list; + + struct rt_thermal_zone_device *zdev; + rt_thermal_notifier_callback callback; + void *priv; +}; + +rt_err_t rt_thermal_zone_device_register(struct rt_thermal_zone_device *); +rt_err_t rt_thermal_zone_device_unregister(struct rt_thermal_zone_device *); + +rt_err_t rt_thermal_cooling_device_register(struct rt_thermal_cooling_device *); +rt_err_t rt_thermal_cooling_device_unregister(struct rt_thermal_cooling_device *); + +rt_err_t rt_thermal_zone_notifier_register(struct rt_thermal_zone_device *, + struct rt_thermal_notifier *notifier); +rt_err_t rt_thermal_zone_notifier_unregister(struct rt_thermal_zone_device *, + struct rt_thermal_notifier *notifier); + +void rt_thermal_zone_device_update(struct rt_thermal_zone_device *, rt_ubase_t msg); + +rt_err_t rt_thermal_zone_get_trip(struct rt_thermal_zone_device *, int trip_id, + struct rt_thermal_trip *out_trip); + +#endif /* __THERMAL_H__ */ diff --git a/components/drivers/include/drivers/virtio.h b/components/drivers/include/drivers/virtio.h index 95f490121e90..38217e83a890 100644 --- a/components/drivers/include/drivers/virtio.h +++ b/components/drivers/include/drivers/virtio.h @@ -19,6 +19,8 @@ #include #include +#include "../../virtio/virtio_ids.h" + #define VIRTIO_STATUS_ACKNOWLEDGE RT_BIT(0) #define VIRTIO_STATUS_DRIVER RT_BIT(1) #define VIRTIO_STATUS_DRIVER_OK RT_BIT(2) @@ -43,69 +45,6 @@ #define VIRTIO_TRANSPORT_F_START VIRTIO_F_RING_INDIRECT_DESC #define VIRTIO_TRANSPORT_F_END VIRTIO_F_RING_RESET -enum -{ - /* virtio 1.0 */ - VIRTIO_DEVICE_ID_INVALID = 0, /* Invalid device */ - VIRTIO_DEVICE_ID_NET = 1, /* Net */ - VIRTIO_DEVICE_ID_BLOCK = 2, /* Block */ - VIRTIO_DEVICE_ID_CONSOLE = 3, /* Console */ - VIRTIO_DEVICE_ID_RNG = 4, /* Rng */ - VIRTIO_DEVICE_ID_BALLOON = 5, /* Balloon */ - VIRTIO_DEVICE_ID_IOMEM = 6, /* IO memory */ - VIRTIO_DEVICE_ID_RPMSG = 7, /* Remote processor messaging */ - VIRTIO_DEVICE_ID_SCSI = 8, /* SCSI */ - VIRTIO_DEVICE_ID_9P = 9, /* 9p console */ - VIRTIO_DEVICE_ID_MAC80211_WLAN = 10, /* Mac80211 wlan */ - VIRTIO_DEVICE_ID_RPROC_SERIAL = 11, /* Remoteproc serial link */ - VIRTIO_DEVICE_ID_CAIF = 12, /* CAIF */ - VIRTIO_DEVICE_ID_MEM_BALLOON = 13, /* Memory balloon */ - VIRTIO_DEVICE_ID_GPU = 16, /* GPU */ - VIRTIO_DEVICE_ID_TIME = 17, /* Timer/clock device */ - VIRTIO_DEVICE_ID_INPUT = 18, /* Input */ - /* virtio 1.1 */ - VIRTIO_DEVICE_ID_SOCKET = 19, /* Socket device */ - VIRTIO_DEVICE_ID_CRYPTO = 20, /* Crypto device */ - VIRTIO_DEVICE_ID_SIG_DIS_MOD = 21, /* Signal Distribution Module */ - VIRTIO_DEVICE_ID_PSTORE = 22, /* Pstore device */ - VIRTIO_DEVICE_ID_IOMMU = 23, /* IOMMU device */ - VIRTIO_DEVICE_ID_MEM = 24, /* Memory device */ - /* virtio 1.2 */ - VIRTIO_DEVICE_ID_AUDIO = 25, /* Audio device */ - VIRTIO_DEVICE_ID_FS = 26, /* File system device */ - VIRTIO_DEVICE_ID_PMEM = 27, /* PMEM device */ - VIRTIO_DEVICE_ID_RPMB = 28, /* RPMB device */ - VIRTIO_DEVICE_ID_MAC80211_HWSIM = 29, /* Mac80211 hwsim wireless simulation device */ - VIRTIO_DEVICE_ID_VIDEO_ENCODER = 30, /* Video encoder device */ - VIRTIO_DEVICE_ID_VIDEO_DECODER = 31, /* Video decoder device */ - VIRTIO_DEVICE_ID_SCMI = 32, /* SCMI device */ - VIRTIO_DEVICE_ID_NITRO_SEC_MOD = 33, /* NitroSecureModule */ - VIRTIO_DEVICE_ID_I2C_ADAPTER = 34, /* I2C adapter */ - VIRTIO_DEVICE_ID_WATCHDOG = 35, /* Watchdog */ - VIRTIO_DEVICE_ID_CAN = 36, /* CAN device */ - VIRTIO_DEVICE_ID_DMABUF = 37, /* Virtio dmabuf */ - VIRTIO_DEVICE_ID_PARAM_SERV = 38, /* Parameter Server */ - VIRTIO_DEVICE_ID_AUDIO_POLICY = 39, /* Audio policy device */ - VIRTIO_DEVICE_ID_BT = 40, /* Bluetooth device */ - VIRTIO_DEVICE_ID_GPIO = 41, /* GPIO device */ - VIRTIO_DEVICE_ID_RDMA = 42, /* RDMA device */ - - VIRTIO_DEVICE_ID_MAX -}; - -enum -{ - VIRTIO_PCI_SUBSYSTEM_DEVICE_ID_NET = 0x1000, /* Network card */ - VIRTIO_PCI_SUBSYSTEM_DEVICE_ID_BLOCK = 0x1001, /* Block device */ - VIRTIO_PCI_SUBSYSTEM_DEVICE_ID_BALLOON = 0x1002, /* Memory ballooning (traditional) */ - VIRTIO_PCI_SUBSYSTEM_DEVICE_ID_CONSOLE = 0x1003, /* Console */ - VIRTIO_PCI_SUBSYSTEM_DEVICE_ID_SCSI = 0x1004, /* SCSI host */ - VIRTIO_PCI_SUBSYSTEM_DEVICE_ID_RNG = 0x1005, /* Entropy source */ - VIRTIO_PCI_SUBSYSTEM_DEVICE_ID_9P = 0x1009, /* 9P transport */ - - VIRTIO_PCI_SUBSYSTEM_DEVICE_ID_MAX -}; - struct rt_virtio_device_id { #define VIRTIO_DEVICE_ANY_ID 0xffffffff @@ -204,6 +143,8 @@ struct rt_virtio_driver rt_uint64_t features; rt_err_t (*probe)(struct rt_virtio_device *vdev); + rt_err_t (*remove)(struct rt_virtio_device *vdev); + rt_err_t (*shutdown)(struct rt_virtio_device *vdev); void (*config_changed)(struct rt_virtio_device *vdev); }; diff --git a/components/drivers/include/dt-bindings/clock/bcm2835-aux.h b/components/drivers/include/dt-bindings/clock/bcm2835-aux.h new file mode 100644 index 000000000000..189b2b48ad16 --- /dev/null +++ b/components/drivers/include/dt-bindings/clock/bcm2835-aux.h @@ -0,0 +1,15 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __DT_BINDINGS_CLOCK_BCM2835_AUX_H__ +#define __DT_BINDINGS_CLOCK_BCM2835_AUX_H__ + +#define BCM2835_AUX_CLOCK_UART 0 +#define BCM2835_AUX_CLOCK_SPI1 1 +#define BCM2835_AUX_CLOCK_SPI2 2 +#define BCM2835_AUX_CLOCK_COUNT 3 + +#endif /* __DT_BINDINGS_CLOCK_BCM2835_AUX_H__ */ diff --git a/components/drivers/include/dt-bindings/clock/rk3308-cru.h b/components/drivers/include/dt-bindings/clock/rk3308-cru.h new file mode 100644 index 000000000000..3967932d2d1a --- /dev/null +++ b/components/drivers/include/dt-bindings/clock/rk3308-cru.h @@ -0,0 +1,387 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __DT_BINDINGS_CLK_ROCKCHIP_RK3308_H__ +#define __DT_BINDINGS_CLK_ROCKCHIP_RK3308_H__ + +/* core clocks */ +#define PLL_APLL 1 +#define PLL_DPLL 2 +#define PLL_VPLL0 3 +#define PLL_VPLL1 4 +#define ARMCLK 5 + +/* sclk (special clocks) */ +#define USB480M 14 +#define SCLK_RTC32K 15 +#define SCLK_PVTM_CORE 16 +#define SCLK_UART0 17 +#define SCLK_UART1 18 +#define SCLK_UART2 19 +#define SCLK_UART3 20 +#define SCLK_UART4 21 +#define SCLK_I2C0 22 +#define SCLK_I2C1 23 +#define SCLK_I2C2 24 +#define SCLK_I2C3 25 +#define SCLK_PWM0 26 +#define SCLK_SPI0 27 +#define SCLK_SPI1 28 +#define SCLK_SPI2 29 +#define SCLK_TIMER0 30 +#define SCLK_TIMER1 31 +#define SCLK_TIMER2 32 +#define SCLK_TIMER3 33 +#define SCLK_TIMER4 34 +#define SCLK_TIMER5 35 +#define SCLK_TSADC 36 +#define SCLK_SARADC 37 +#define SCLK_OTP 38 +#define SCLK_OTP_USR 39 +#define SCLK_CPU_BOOST 40 +#define SCLK_CRYPTO 41 +#define SCLK_CRYPTO_APK 42 +#define SCLK_NANDC_DIV 43 +#define SCLK_NANDC_DIV50 44 +#define SCLK_NANDC 45 +#define SCLK_SDMMC_DIV 46 +#define SCLK_SDMMC_DIV50 47 +#define SCLK_SDMMC 48 +#define SCLK_SDMMC_DRV 49 +#define SCLK_SDMMC_SAMPLE 50 +#define SCLK_SDIO_DIV 51 +#define SCLK_SDIO_DIV50 52 +#define SCLK_SDIO 53 +#define SCLK_SDIO_DRV 54 +#define SCLK_SDIO_SAMPLE 55 +#define SCLK_EMMC_DIV 56 +#define SCLK_EMMC_DIV50 57 +#define SCLK_EMMC 58 +#define SCLK_EMMC_DRV 59 +#define SCLK_EMMC_SAMPLE 60 +#define SCLK_SFC 61 +#define SCLK_OTG_ADP 62 +#define SCLK_MAC_SRC 63 +#define SCLK_MAC 64 +#define SCLK_MAC_REF 65 +#define SCLK_MAC_RX_TX 66 +#define SCLK_MAC_RMII 67 +#define SCLK_DDR_MON_TIMER 68 +#define SCLK_DDR_MON 69 +#define SCLK_DDRCLK 70 +#define SCLK_PMU 71 +#define SCLK_USBPHY_REF 72 +#define SCLK_WIFI 73 +#define SCLK_PVTM_PMU 74 +#define SCLK_PDM 75 +#define SCLK_I2S0_8CH_TX 76 +#define SCLK_I2S0_8CH_TX_OUT 77 +#define SCLK_I2S0_8CH_RX 78 +#define SCLK_I2S0_8CH_RX_OUT 79 +#define SCLK_I2S1_8CH_TX 80 +#define SCLK_I2S1_8CH_TX_OUT 81 +#define SCLK_I2S1_8CH_RX 82 +#define SCLK_I2S1_8CH_RX_OUT 83 +#define SCLK_I2S2_8CH_TX 84 +#define SCLK_I2S2_8CH_TX_OUT 85 +#define SCLK_I2S2_8CH_RX 86 +#define SCLK_I2S2_8CH_RX_OUT 87 +#define SCLK_I2S3_8CH_TX 88 +#define SCLK_I2S3_8CH_TX_OUT 89 +#define SCLK_I2S3_8CH_RX 90 +#define SCLK_I2S3_8CH_RX_OUT 91 +#define SCLK_I2S0_2CH 92 +#define SCLK_I2S0_2CH_OUT 93 +#define SCLK_I2S1_2CH 94 +#define SCLK_I2S1_2CH_OUT 95 +#define SCLK_SPDIF_TX_DIV 96 +#define SCLK_SPDIF_TX_DIV50 97 +#define SCLK_SPDIF_TX 98 +#define SCLK_SPDIF_RX_DIV 99 +#define SCLK_SPDIF_RX_DIV50 100 +#define SCLK_SPDIF_RX 101 +#define SCLK_I2S0_8CH_TX_MUX 102 +#define SCLK_I2S0_8CH_RX_MUX 103 +#define SCLK_I2S1_8CH_TX_MUX 104 +#define SCLK_I2S1_8CH_RX_MUX 105 +#define SCLK_I2S2_8CH_TX_MUX 106 +#define SCLK_I2S2_8CH_RX_MUX 107 +#define SCLK_I2S3_8CH_TX_MUX 108 +#define SCLK_I2S3_8CH_RX_MUX 109 +#define SCLK_I2S0_8CH_TX_SRC 110 +#define SCLK_I2S0_8CH_RX_SRC 111 +#define SCLK_I2S1_8CH_TX_SRC 112 +#define SCLK_I2S1_8CH_RX_SRC 113 +#define SCLK_I2S2_8CH_TX_SRC 114 +#define SCLK_I2S2_8CH_RX_SRC 115 +#define SCLK_I2S3_8CH_TX_SRC 116 +#define SCLK_I2S3_8CH_RX_SRC 117 +#define SCLK_I2S0_2CH_SRC 118 +#define SCLK_I2S1_2CH_SRC 119 +#define SCLK_PWM1 120 +#define SCLK_PWM2 121 +#define SCLK_OWIRE 122 + +/* dclk */ +#define DCLK_VOP 125 + +/* aclk */ +#define ACLK_BUS_SRC 130 +#define ACLK_BUS 131 +#define ACLK_PERI_SRC 132 +#define ACLK_PERI 133 +#define ACLK_MAC 134 +#define ACLK_CRYPTO 135 +#define ACLK_VOP 136 +#define ACLK_GIC 137 +#define ACLK_DMAC0 138 +#define ACLK_DMAC1 139 + +/* hclk */ +#define HCLK_BUS 150 +#define HCLK_PERI 151 +#define HCLK_AUDIO 152 +#define HCLK_NANDC 153 +#define HCLK_SDMMC 154 +#define HCLK_SDIO 155 +#define HCLK_EMMC 156 +#define HCLK_SFC 157 +#define HCLK_OTG 158 +#define HCLK_HOST 159 +#define HCLK_HOST_ARB 160 +#define HCLK_PDM 161 +#define HCLK_SPDIFTX 162 +#define HCLK_SPDIFRX 163 +#define HCLK_I2S0_8CH 164 +#define HCLK_I2S1_8CH 165 +#define HCLK_I2S2_8CH 166 +#define HCLK_I2S3_8CH 167 +#define HCLK_I2S0_2CH 168 +#define HCLK_I2S1_2CH 169 +#define HCLK_VAD 170 +#define HCLK_CRYPTO 171 +#define HCLK_VOP 172 + +/* pclk */ +#define PCLK_BUS 190 +#define PCLK_DDR 191 +#define PCLK_PERI 192 +#define PCLK_PMU 193 +#define PCLK_AUDIO 194 +#define PCLK_MAC 195 +#define PCLK_ACODEC 196 +#define PCLK_UART0 197 +#define PCLK_UART1 198 +#define PCLK_UART2 199 +#define PCLK_UART3 200 +#define PCLK_UART4 201 +#define PCLK_I2C0 202 +#define PCLK_I2C1 203 +#define PCLK_I2C2 204 +#define PCLK_I2C3 205 +#define PCLK_PWM0 206 +#define PCLK_SPI0 207 +#define PCLK_SPI1 208 +#define PCLK_SPI2 209 +#define PCLK_SARADC 210 +#define PCLK_TSADC 211 +#define PCLK_TIMER 212 +#define PCLK_OTP_NS 213 +#define PCLK_WDT 214 +#define PCLK_GPIO0 215 +#define PCLK_GPIO1 216 +#define PCLK_GPIO2 217 +#define PCLK_GPIO3 218 +#define PCLK_GPIO4 219 +#define PCLK_SGRF 220 +#define PCLK_GRF 221 +#define PCLK_USBSD_DET 222 +#define PCLK_DDR_UPCTL 223 +#define PCLK_DDR_MON 224 +#define PCLK_DDRPHY 225 +#define PCLK_DDR_STDBY 226 +#define PCLK_USB_GRF 227 +#define PCLK_CRU 228 +#define PCLK_OTP_PHY 229 +#define PCLK_CPU_BOOST 230 +#define PCLK_PWM1 231 +#define PCLK_PWM2 232 +#define PCLK_CAN 233 +#define PCLK_OWIRE 234 + +#define CLK_NR_CLKS (PCLK_OWIRE + 1) + +/* soft-reset indices */ + +/* cru_softrst_con0 */ +#define SRST_CORE0_PO 0 +#define SRST_CORE1_PO 1 +#define SRST_CORE2_PO 2 +#define SRST_CORE3_PO 3 +#define SRST_CORE0 4 +#define SRST_CORE1 5 +#define SRST_CORE2 6 +#define SRST_CORE3 7 +#define SRST_CORE0_DBG 8 +#define SRST_CORE1_DBG 9 +#define SRST_CORE2_DBG 10 +#define SRST_CORE3_DBG 11 +#define SRST_TOPDBG 12 +#define SRST_CORE_NOC 13 +#define SRST_STRC_A 14 +#define SRST_L2C 15 + +/* cru_softrst_con1 */ +#define SRST_DAP 16 +#define SRST_CORE_PVTM 17 +#define SRST_CORE_PRF 18 +#define SRST_CORE_GRF 19 +#define SRST_DDRUPCTL 20 +#define SRST_DDRUPCTL_P 22 +#define SRST_MSCH 23 +#define SRST_DDRMON_P 25 +#define SRST_DDRSTDBY_P 26 +#define SRST_DDRSTDBY 27 +#define SRST_DDRPHY 28 +#define SRST_DDRPHY_DIV 29 +#define SRST_DDRPHY_P 30 + +/* cru_softrst_con2 */ +#define SRST_BUS_NIU_H 32 +#define SRST_USB_NIU_P 33 +#define SRST_CRYPTO_A 34 +#define SRST_CRYPTO_H 35 +#define SRST_CRYPTO 36 +#define SRST_CRYPTO_APK 37 +#define SRST_VOP_A 38 +#define SRST_VOP_H 39 +#define SRST_VOP_D 40 +#define SRST_INTMEM_A 41 +#define SRST_ROM_H 42 +#define SRST_GIC_A 43 +#define SRST_UART0_P 44 +#define SRST_UART0 45 +#define SRST_UART1_P 46 +#define SRST_UART1 47 + +/* cru_softrst_con3 */ +#define SRST_UART2_P 48 +#define SRST_UART2 49 +#define SRST_UART3_P 50 +#define SRST_UART3 51 +#define SRST_UART4_P 52 +#define SRST_UART4 53 +#define SRST_I2C0_P 54 +#define SRST_I2C0 55 +#define SRST_I2C1_P 56 +#define SRST_I2C1 57 +#define SRST_I2C2_P 58 +#define SRST_I2C2 59 +#define SRST_I2C3_P 60 +#define SRST_I2C3 61 +#define SRST_PWM0_P 62 +#define SRST_PWM0 63 + +/* cru_softrst_con4 */ +#define SRST_SPI0_P 64 +#define SRST_SPI0 65 +#define SRST_SPI1_P 66 +#define SRST_SPI1 67 +#define SRST_SPI2_P 68 +#define SRST_SPI2 69 +#define SRST_SARADC_P 70 +#define SRST_TSADC_P 71 +#define SRST_TSADC 72 +#define SRST_TIMER0_P 73 +#define SRST_TIMER0 74 +#define SRST_TIMER1 75 +#define SRST_TIMER2 76 +#define SRST_TIMER3 77 +#define SRST_TIMER4 78 +#define SRST_TIMER5 79 + +/* cru_softrst_con5 */ +#define SRST_OTP_NS_P 80 +#define SRST_OTP_NS_SBPI 81 +#define SRST_OTP_NS_USR 82 +#define SRST_OTP_PHY_P 83 +#define SRST_OTP_PHY 84 +#define SRST_GPIO0_P 86 +#define SRST_GPIO1_P 87 +#define SRST_GPIO2_P 88 +#define SRST_GPIO3_P 89 +#define SRST_GPIO4_P 90 +#define SRST_GRF_P 91 +#define SRST_USBSD_DET_P 92 +#define SRST_PMU 93 +#define SRST_PMU_PVTM 94 +#define SRST_USB_GRF_P 95 + +/* cru_softrst_con6 */ +#define SRST_CPU_BOOST 96 +#define SRST_CPU_BOOST_P 97 +#define SRST_PWM1_P 98 +#define SRST_PWM1 99 +#define SRST_PWM2_P 100 +#define SRST_PWM2 101 +#define SRST_PERI_NIU_A 104 +#define SRST_PERI_NIU_H 105 +#define SRST_PERI_NIU_p 106 +#define SRST_USB2OTG_H 107 +#define SRST_USB2OTG 108 +#define SRST_USB2OTG_ADP 109 +#define SRST_USB2HOST_H 110 +#define SRST_USB2HOST_ARB_H 111 + +/* cru_softrst_con7 */ +#define SRST_USB2HOST_AUX_H 112 +#define SRST_USB2HOST_EHCI 113 +#define SRST_USB2HOST 114 +#define SRST_USBPHYPOR 115 +#define SRST_UTMI0 116 +#define SRST_UTMI1 117 +#define SRST_SDIO_H 118 +#define SRST_EMMC_H 119 +#define SRST_SFC_H 120 +#define SRST_SFC 121 +#define SRST_SD_H 122 +#define SRST_NANDC_H 123 +#define SRST_NANDC_N 124 +#define SRST_MAC_A 125 +#define SRST_CAN_P 126 +#define SRST_OWIRE_P 127 + +/* cru_softrst_con8 */ +#define SRST_AUDIO_NIU_H 128 +#define SRST_AUDIO_NIU_P 129 +#define SRST_PDM_H 130 +#define SRST_PDM_M 131 +#define SRST_SPDIFTX_H 132 +#define SRST_SPDIFTX_M 133 +#define SRST_SPDIFRX_H 134 +#define SRST_SPDIFRX_M 135 +#define SRST_I2S0_8CH_H 136 +#define SRST_I2S0_8CH_TX_M 137 +#define SRST_I2S0_8CH_RX_M 138 +#define SRST_I2S1_8CH_H 139 +#define SRST_I2S1_8CH_TX_M 140 +#define SRST_I2S1_8CH_RX_M 141 +#define SRST_I2S2_8CH_H 142 +#define SRST_I2S2_8CH_TX_M 143 + +/* cru_softrst_con9 */ +#define SRST_I2S2_8CH_RX_M 144 +#define SRST_I2S3_8CH_H 145 +#define SRST_I2S3_8CH_TX_M 146 +#define SRST_I2S3_8CH_RX_M 147 +#define SRST_I2S0_2CH_H 148 +#define SRST_I2S0_2CH_M 149 +#define SRST_I2S1_2CH_H 150 +#define SRST_I2S1_2CH_M 151 +#define SRST_VAD_H 152 +#define SRST_ACODEC_P 153 + +#endif /* __DT_BINDINGS_CLK_ROCKCHIP_RK3308_H__ */ diff --git a/components/drivers/include/dt-bindings/clock/rk3568-cru.h b/components/drivers/include/dt-bindings/clock/rk3568-cru.h new file mode 100755 index 000000000000..cc57edf1f064 --- /dev/null +++ b/components/drivers/include/dt-bindings/clock/rk3568-cru.h @@ -0,0 +1,926 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __DT_BINDINGS_CLK_ROCKCHIP_RK3568_H__ +#define __DT_BINDINGS_CLK_ROCKCHIP_RK3568_H__ + +/* pmucru-clocks indices */ + +/* pmucru plls */ +#define PLL_PPLL 1 +#define PLL_HPLL 2 + +/* pmucru clocks */ +#define XIN_OSC0_DIV 4 +#define CLK_RTC_32K 5 +#define CLK_PMU 6 +#define CLK_I2C0 7 +#define CLK_RTC32K_FRAC 8 +#define CLK_UART0_DIV 9 +#define CLK_UART0_FRAC 10 +#define SCLK_UART0 11 +#define DBCLK_GPIO0 12 +#define CLK_PWM0 13 +#define CLK_CAPTURE_PWM0_NDFT 14 +#define CLK_PMUPVTM 15 +#define CLK_CORE_PMUPVTM 16 +#define CLK_REF24M 17 +#define XIN_OSC0_USBPHY0_G 18 +#define CLK_USBPHY0_REF 19 +#define XIN_OSC0_USBPHY1_G 20 +#define CLK_USBPHY1_REF 21 +#define XIN_OSC0_MIPIDSIPHY0_G 22 +#define CLK_MIPIDSIPHY0_REF 23 +#define XIN_OSC0_MIPIDSIPHY1_G 24 +#define CLK_MIPIDSIPHY1_REF 25 +#define CLK_WIFI_DIV 26 +#define CLK_WIFI_OSC0 27 +#define CLK_WIFI 28 +#define CLK_PCIEPHY0_DIV 29 +#define CLK_PCIEPHY0_OSC0 30 +#define CLK_PCIEPHY0_REF 31 +#define CLK_PCIEPHY1_DIV 32 +#define CLK_PCIEPHY1_OSC0 33 +#define CLK_PCIEPHY1_REF 34 +#define CLK_PCIEPHY2_DIV 35 +#define CLK_PCIEPHY2_OSC0 36 +#define CLK_PCIEPHY2_REF 37 +#define CLK_PCIE30PHY_REF_M 38 +#define CLK_PCIE30PHY_REF_N 39 +#define CLK_HDMI_REF 40 +#define XIN_OSC0_EDPPHY_G 41 +#define PCLK_PDPMU 42 +#define PCLK_PMU 43 +#define PCLK_UART0 44 +#define PCLK_I2C0 45 +#define PCLK_GPIO0 46 +#define PCLK_PMUPVTM 47 +#define PCLK_PWM0 48 +#define CLK_PDPMU 49 +#define SCLK_32K_IOE 50 + +#define CLKPMU_NR_CLKS (SCLK_32K_IOE + 1) + +/* cru-clocks indices */ + +/* cru plls */ +#define PLL_APLL 1 +#define PLL_DPLL 2 +#define PLL_CPLL 3 +#define PLL_GPLL 4 +#define PLL_VPLL 5 +#define PLL_NPLL 6 + +/* cru clocks */ +#define CPLL_333M 9 +#define ARMCLK 10 +#define USB480M 11 +#define ACLK_CORE_NIU2BUS 18 +#define CLK_CORE_PVTM 19 +#define CLK_CORE_PVTM_CORE 20 +#define CLK_CORE_PVTPLL 21 +#define CLK_GPU_SRC 22 +#define CLK_GPU_PRE_NDFT 23 +#define CLK_GPU_PRE_MUX 24 +#define ACLK_GPU_PRE 25 +#define PCLK_GPU_PRE 26 +#define CLK_GPU 27 +#define CLK_GPU_NP5 28 +#define PCLK_GPU_PVTM 29 +#define CLK_GPU_PVTM 30 +#define CLK_GPU_PVTM_CORE 31 +#define CLK_GPU_PVTPLL 32 +#define CLK_NPU_SRC 33 +#define CLK_NPU_PRE_NDFT 34 +#define CLK_NPU 35 +#define CLK_NPU_NP5 36 +#define HCLK_NPU_PRE 37 +#define PCLK_NPU_PRE 38 +#define ACLK_NPU_PRE 39 +#define ACLK_NPU 40 +#define HCLK_NPU 41 +#define PCLK_NPU_PVTM 42 +#define CLK_NPU_PVTM 43 +#define CLK_NPU_PVTM_CORE 44 +#define CLK_NPU_PVTPLL 45 +#define CLK_DDRPHY1X_SRC 46 +#define CLK_DDRPHY1X_HWFFC_SRC 47 +#define CLK_DDR1X 48 +#define CLK_MSCH 49 +#define CLK24_DDRMON 50 +#define ACLK_GIC_AUDIO 51 +#define HCLK_GIC_AUDIO 52 +#define HCLK_SDMMC_BUFFER 53 +#define DCLK_SDMMC_BUFFER 54 +#define ACLK_GIC600 55 +#define ACLK_SPINLOCK 56 +#define HCLK_I2S0_8CH 57 +#define HCLK_I2S1_8CH 58 +#define HCLK_I2S2_2CH 59 +#define HCLK_I2S3_2CH 60 +#define CLK_I2S0_8CH_TX_SRC 61 +#define CLK_I2S0_8CH_TX_FRAC 62 +#define MCLK_I2S0_8CH_TX 63 +#define I2S0_MCLKOUT_TX 64 +#define CLK_I2S0_8CH_RX_SRC 65 +#define CLK_I2S0_8CH_RX_FRAC 66 +#define MCLK_I2S0_8CH_RX 67 +#define I2S0_MCLKOUT_RX 68 +#define CLK_I2S1_8CH_TX_SRC 69 +#define CLK_I2S1_8CH_TX_FRAC 70 +#define MCLK_I2S1_8CH_TX 71 +#define I2S1_MCLKOUT_TX 72 +#define CLK_I2S1_8CH_RX_SRC 73 +#define CLK_I2S1_8CH_RX_FRAC 74 +#define MCLK_I2S1_8CH_RX 75 +#define I2S1_MCLKOUT_RX 76 +#define CLK_I2S2_2CH_SRC 77 +#define CLK_I2S2_2CH_FRAC 78 +#define MCLK_I2S2_2CH 79 +#define I2S2_MCLKOUT 80 +#define CLK_I2S3_2CH_TX_SRC 81 +#define CLK_I2S3_2CH_TX_FRAC 82 +#define MCLK_I2S3_2CH_TX 83 +#define I2S3_MCLKOUT_TX 84 +#define CLK_I2S3_2CH_RX_SRC 85 +#define CLK_I2S3_2CH_RX_FRAC 86 +#define MCLK_I2S3_2CH_RX 87 +#define I2S3_MCLKOUT_RX 88 +#define HCLK_PDM 89 +#define MCLK_PDM 90 +#define HCLK_VAD 91 +#define HCLK_SPDIF_8CH 92 +#define MCLK_SPDIF_8CH_SRC 93 +#define MCLK_SPDIF_8CH_FRAC 94 +#define MCLK_SPDIF_8CH 95 +#define HCLK_AUDPWM 96 +#define SCLK_AUDPWM_SRC 97 +#define SCLK_AUDPWM_FRAC 98 +#define SCLK_AUDPWM 99 +#define HCLK_ACDCDIG 100 +#define CLK_ACDCDIG_I2C 101 +#define CLK_ACDCDIG_DAC 102 +#define CLK_ACDCDIG_ADC 103 +#define ACLK_SECURE_FLASH 104 +#define HCLK_SECURE_FLASH 105 +#define ACLK_CRYPTO_NS 106 +#define HCLK_CRYPTO_NS 107 +#define CLK_CRYPTO_NS_CORE 108 +#define CLK_CRYPTO_NS_PKA 109 +#define CLK_CRYPTO_NS_RNG 110 +#define HCLK_TRNG_NS 111 +#define CLK_TRNG_NS 112 +#define PCLK_OTPC_NS 113 +#define CLK_OTPC_NS_SBPI 114 +#define CLK_OTPC_NS_USR 115 +#define HCLK_NANDC 116 +#define NCLK_NANDC 117 +#define HCLK_SFC 118 +#define HCLK_SFC_XIP 119 +#define SCLK_SFC 120 +#define ACLK_EMMC 121 +#define HCLK_EMMC 122 +#define BCLK_EMMC 123 +#define CCLK_EMMC 124 +#define TCLK_EMMC 125 +#define ACLK_PIPE 126 +#define PCLK_PIPE 127 +#define PCLK_PIPE_GRF 128 +#define ACLK_PCIE20_MST 129 +#define ACLK_PCIE20_SLV 130 +#define ACLK_PCIE20_DBI 131 +#define PCLK_PCIE20 132 +#define CLK_PCIE20_AUX_NDFT 133 +#define CLK_PCIE20_AUX_DFT 134 +#define CLK_PCIE20_PIPE_DFT 135 +#define ACLK_PCIE30X1_MST 136 +#define ACLK_PCIE30X1_SLV 137 +#define ACLK_PCIE30X1_DBI 138 +#define PCLK_PCIE30X1 139 +#define CLK_PCIE30X1_AUX_NDFT 140 +#define CLK_PCIE30X1_AUX_DFT 141 +#define CLK_PCIE30X1_PIPE_DFT 142 +#define ACLK_PCIE30X2_MST 143 +#define ACLK_PCIE30X2_SLV 144 +#define ACLK_PCIE30X2_DBI 145 +#define PCLK_PCIE30X2 146 +#define CLK_PCIE30X2_AUX_NDFT 147 +#define CLK_PCIE30X2_AUX_DFT 148 +#define CLK_PCIE30X2_PIPE_DFT 149 +#define ACLK_SATA0 150 +#define CLK_SATA0_PMALIVE 151 +#define CLK_SATA0_RXOOB 152 +#define CLK_SATA0_PIPE_NDFT 153 +#define CLK_SATA0_PIPE_DFT 154 +#define ACLK_SATA1 155 +#define CLK_SATA1_PMALIVE 156 +#define CLK_SATA1_RXOOB 157 +#define CLK_SATA1_PIPE_NDFT 158 +#define CLK_SATA1_PIPE_DFT 159 +#define ACLK_SATA2 160 +#define CLK_SATA2_PMALIVE 161 +#define CLK_SATA2_RXOOB 162 +#define CLK_SATA2_PIPE_NDFT 163 +#define CLK_SATA2_PIPE_DFT 164 +#define ACLK_USB3OTG0 165 +#define CLK_USB3OTG0_REF 166 +#define CLK_USB3OTG0_SUSPEND 167 +#define ACLK_USB3OTG1 168 +#define CLK_USB3OTG1_REF 169 +#define CLK_USB3OTG1_SUSPEND 170 +#define CLK_XPCS_EEE 171 +#define PCLK_XPCS 172 +#define ACLK_PHP 173 +#define HCLK_PHP 174 +#define PCLK_PHP 175 +#define HCLK_SDMMC0 176 +#define CLK_SDMMC0 177 +#define HCLK_SDMMC1 178 +#define CLK_SDMMC1 179 +#define ACLK_GMAC0 180 +#define PCLK_GMAC0 181 +#define CLK_MAC0_2TOP 182 +#define CLK_MAC0_OUT 183 +#define CLK_MAC0_REFOUT 184 +#define CLK_GMAC0_PTP_REF 185 +#define ACLK_USB 186 +#define HCLK_USB 187 +#define PCLK_USB 188 +#define HCLK_USB2HOST0 189 +#define HCLK_USB2HOST0_ARB 190 +#define HCLK_USB2HOST1 191 +#define HCLK_USB2HOST1_ARB 192 +#define HCLK_SDMMC2 193 +#define CLK_SDMMC2 194 +#define ACLK_GMAC1 195 +#define PCLK_GMAC1 196 +#define CLK_MAC1_2TOP 197 +#define CLK_MAC1_OUT 198 +#define CLK_MAC1_REFOUT 199 +#define CLK_GMAC1_PTP_REF 200 +#define ACLK_PERIMID 201 +#define HCLK_PERIMID 202 +#define ACLK_VI 203 +#define HCLK_VI 204 +#define PCLK_VI 205 +#define ACLK_VICAP 206 +#define HCLK_VICAP 207 +#define DCLK_VICAP 208 +#define ICLK_VICAP_G 209 +#define ACLK_ISP 210 +#define HCLK_ISP 211 +#define CLK_ISP 212 +#define PCLK_CSI2HOST1 213 +#define CLK_CIF_OUT 214 +#define CLK_CAM0_OUT 215 +#define CLK_CAM1_OUT 216 +#define ACLK_VO 217 +#define HCLK_VO 218 +#define PCLK_VO 219 +#define ACLK_VOP_PRE 220 +#define ACLK_VOP 221 +#define HCLK_VOP 222 +#define DCLK_VOP0 223 +#define DCLK_VOP1 224 +#define DCLK_VOP2 225 +#define CLK_VOP_PWM 226 +#define ACLK_HDCP 227 +#define HCLK_HDCP 228 +#define PCLK_HDCP 229 +#define PCLK_HDMI_HOST 230 +#define CLK_HDMI_SFR 231 +#define PCLK_DSITX_0 232 +#define PCLK_DSITX_1 233 +#define PCLK_EDP_CTRL 234 +#define CLK_EDP_200M 235 +#define ACLK_VPU_PRE 236 +#define HCLK_VPU_PRE 237 +#define ACLK_VPU 238 +#define HCLK_VPU 239 +#define ACLK_RGA_PRE 240 +#define HCLK_RGA_PRE 241 +#define PCLK_RGA_PRE 242 +#define ACLK_RGA 243 +#define HCLK_RGA 244 +#define CLK_RGA_CORE 245 +#define ACLK_IEP 246 +#define HCLK_IEP 247 +#define CLK_IEP_CORE 248 +#define HCLK_EBC 249 +#define DCLK_EBC 250 +#define ACLK_JDEC 251 +#define HCLK_JDEC 252 +#define ACLK_JENC 253 +#define HCLK_JENC 254 +#define PCLK_EINK 255 +#define HCLK_EINK 256 +#define ACLK_RKVENC_PRE 257 +#define HCLK_RKVENC_PRE 258 +#define ACLK_RKVENC 259 +#define HCLK_RKVENC 260 +#define CLK_RKVENC_CORE 261 +#define ACLK_RKVDEC_PRE 262 +#define HCLK_RKVDEC_PRE 263 +#define ACLK_RKVDEC 264 +#define HCLK_RKVDEC 265 +#define CLK_RKVDEC_CA 266 +#define CLK_RKVDEC_CORE 267 +#define CLK_RKVDEC_HEVC_CA 268 +#define ACLK_BUS 269 +#define PCLK_BUS 270 +#define PCLK_TSADC 271 +#define CLK_TSADC_TSEN 272 +#define CLK_TSADC 273 +#define PCLK_SARADC 274 +#define CLK_SARADC 275 +#define PCLK_SCR 276 +#define PCLK_WDT_NS 277 +#define TCLK_WDT_NS 278 +#define ACLK_DMAC0 279 +#define ACLK_DMAC1 280 +#define ACLK_MCU 281 +#define PCLK_INTMUX 282 +#define PCLK_MAILBOX 283 +#define PCLK_UART1 284 +#define CLK_UART1_SRC 285 +#define CLK_UART1_FRAC 286 +#define SCLK_UART1 287 +#define PCLK_UART2 288 +#define CLK_UART2_SRC 289 +#define CLK_UART2_FRAC 290 +#define SCLK_UART2 291 +#define PCLK_UART3 292 +#define CLK_UART3_SRC 293 +#define CLK_UART3_FRAC 294 +#define SCLK_UART3 295 +#define PCLK_UART4 296 +#define CLK_UART4_SRC 297 +#define CLK_UART4_FRAC 298 +#define SCLK_UART4 299 +#define PCLK_UART5 300 +#define CLK_UART5_SRC 301 +#define CLK_UART5_FRAC 302 +#define SCLK_UART5 303 +#define PCLK_UART6 304 +#define CLK_UART6_SRC 305 +#define CLK_UART6_FRAC 306 +#define SCLK_UART6 307 +#define PCLK_UART7 308 +#define CLK_UART7_SRC 309 +#define CLK_UART7_FRAC 310 +#define SCLK_UART7 311 +#define PCLK_UART8 312 +#define CLK_UART8_SRC 313 +#define CLK_UART8_FRAC 314 +#define SCLK_UART8 315 +#define PCLK_UART9 316 +#define CLK_UART9_SRC 317 +#define CLK_UART9_FRAC 318 +#define SCLK_UART9 319 +#define PCLK_CAN0 320 +#define CLK_CAN0 321 +#define PCLK_CAN1 322 +#define CLK_CAN1 323 +#define PCLK_CAN2 324 +#define CLK_CAN2 325 +#define CLK_I2C 326 +#define PCLK_I2C1 327 +#define CLK_I2C1 328 +#define PCLK_I2C2 329 +#define CLK_I2C2 330 +#define PCLK_I2C3 331 +#define CLK_I2C3 332 +#define PCLK_I2C4 333 +#define CLK_I2C4 334 +#define PCLK_I2C5 335 +#define CLK_I2C5 336 +#define PCLK_SPI0 337 +#define CLK_SPI0 338 +#define PCLK_SPI1 339 +#define CLK_SPI1 340 +#define PCLK_SPI2 341 +#define CLK_SPI2 342 +#define PCLK_SPI3 343 +#define CLK_SPI3 344 +#define PCLK_PWM1 345 +#define CLK_PWM1 346 +#define CLK_PWM1_CAPTURE 347 +#define PCLK_PWM2 348 +#define CLK_PWM2 349 +#define CLK_PWM2_CAPTURE 350 +#define PCLK_PWM3 351 +#define CLK_PWM3 352 +#define CLK_PWM3_CAPTURE 353 +#define DBCLK_GPIO 354 +#define PCLK_GPIO1 355 +#define DBCLK_GPIO1 356 +#define PCLK_GPIO2 357 +#define DBCLK_GPIO2 358 +#define PCLK_GPIO3 359 +#define DBCLK_GPIO3 360 +#define PCLK_GPIO4 361 +#define DBCLK_GPIO4 362 +#define OCC_SCAN_CLK_GPIO 363 +#define PCLK_TIMER 364 +#define CLK_TIMER0 365 +#define CLK_TIMER1 366 +#define CLK_TIMER2 367 +#define CLK_TIMER3 368 +#define CLK_TIMER4 369 +#define CLK_TIMER5 370 +#define ACLK_TOP_HIGH 371 +#define ACLK_TOP_LOW 372 +#define HCLK_TOP 373 +#define PCLK_TOP 374 +#define PCLK_PCIE30PHY 375 +#define CLK_OPTC_ARB 376 +#define PCLK_MIPICSIPHY 377 +#define PCLK_MIPIDSIPHY0 378 +#define PCLK_MIPIDSIPHY1 379 +#define PCLK_PIPEPHY0 380 +#define PCLK_PIPEPHY1 381 +#define PCLK_PIPEPHY2 382 +#define PCLK_CPU_BOOST 383 +#define CLK_CPU_BOOST 384 +#define PCLK_OTPPHY 385 +#define SCLK_GMAC0 386 +#define SCLK_GMAC0_RGMII_SPEED 387 +#define SCLK_GMAC0_RMII_SPEED 388 +#define SCLK_GMAC0_RX_TX 389 +#define SCLK_GMAC1 390 +#define SCLK_GMAC1_RGMII_SPEED 391 +#define SCLK_GMAC1_RMII_SPEED 392 +#define SCLK_GMAC1_RX_TX 393 +#define SCLK_SDMMC0_DRV 394 +#define SCLK_SDMMC0_SAMPLE 395 +#define SCLK_SDMMC1_DRV 396 +#define SCLK_SDMMC1_SAMPLE 397 +#define SCLK_SDMMC2_DRV 398 +#define SCLK_SDMMC2_SAMPLE 399 +#define SCLK_EMMC_DRV 400 +#define SCLK_EMMC_SAMPLE 401 +#define PCLK_EDPPHY_GRF 402 +#define CLK_HDMI_CEC 403 +#define CLK_I2S0_8CH_TX 404 +#define CLK_I2S0_8CH_RX 405 +#define CLK_I2S1_8CH_TX 406 +#define CLK_I2S1_8CH_RX 407 +#define CLK_I2S2_2CH 408 +#define CLK_I2S3_2CH_TX 409 +#define CLK_I2S3_2CH_RX 410 +#define CPLL_500M 411 +#define CPLL_250M 412 +#define CPLL_125M 413 +#define CPLL_62P5M 414 +#define CPLL_50M 415 +#define CPLL_25M 416 +#define CPLL_100M 417 +#define SCLK_DDRCLK 418 + +#define PCLK_CORE_PVTM 450 + +#define CLK_NR_CLKS (PCLK_CORE_PVTM + 1) + +/* pmu soft-reset indices */ +/* pmucru_softrst_con0 */ +#define SRST_P_PDPMU_NIU 0 +#define SRST_P_PMUCRU 1 +#define SRST_P_PMUGRF 2 +#define SRST_P_I2C0 3 +#define SRST_I2C0 4 +#define SRST_P_UART0 5 +#define SRST_S_UART0 6 +#define SRST_P_PWM0 7 +#define SRST_PWM0 8 +#define SRST_P_GPIO0 9 +#define SRST_GPIO0 10 +#define SRST_P_PMUPVTM 11 +#define SRST_PMUPVTM 12 + +/* soft-reset indices */ + +/* cru_softrst_con0 */ +#define SRST_NCORERESET0 0 +#define SRST_NCORERESET1 1 +#define SRST_NCORERESET2 2 +#define SRST_NCORERESET3 3 +#define SRST_NCPUPORESET0 4 +#define SRST_NCPUPORESET1 5 +#define SRST_NCPUPORESET2 6 +#define SRST_NCPUPORESET3 7 +#define SRST_NSRESET 8 +#define SRST_NSPORESET 9 +#define SRST_NATRESET 10 +#define SRST_NGICRESET 11 +#define SRST_NPRESET 12 +#define SRST_NPERIPHRESET 13 + +/* cru_softrst_con1 */ +#define SRST_A_CORE_NIU2DDR 16 +#define SRST_A_CORE_NIU2BUS 17 +#define SRST_P_DBG_NIU 18 +#define SRST_P_DBG 19 +#define SRST_P_DBG_DAPLITE 20 +#define SRST_DAP 21 +#define SRST_A_ADB400_CORE2GIC 22 +#define SRST_A_ADB400_GIC2CORE 23 +#define SRST_P_CORE_GRF 24 +#define SRST_P_CORE_PVTM 25 +#define SRST_CORE_PVTM 26 +#define SRST_CORE_PVTPLL 27 + +/* cru_softrst_con2 */ +#define SRST_GPU 32 +#define SRST_A_GPU_NIU 33 +#define SRST_P_GPU_NIU 34 +#define SRST_P_GPU_PVTM 35 +#define SRST_GPU_PVTM 36 +#define SRST_GPU_PVTPLL 37 +#define SRST_A_NPU_NIU 40 +#define SRST_H_NPU_NIU 41 +#define SRST_P_NPU_NIU 42 +#define SRST_A_NPU 43 +#define SRST_H_NPU 44 +#define SRST_P_NPU_PVTM 45 +#define SRST_NPU_PVTM 46 +#define SRST_NPU_PVTPLL 47 + +/* cru_softrst_con3 */ +#define SRST_A_MSCH 51 +#define SRST_HWFFC_CTRL 52 +#define SRST_DDR_ALWAYSON 53 +#define SRST_A_DDRSPLIT 54 +#define SRST_DDRDFI_CTL 55 +#define SRST_A_DMA2DDR 57 + +/* cru_softrst_con4 */ +#define SRST_A_PERIMID_NIU 64 +#define SRST_H_PERIMID_NIU 65 +#define SRST_A_GIC_AUDIO_NIU 66 +#define SRST_H_GIC_AUDIO_NIU 67 +#define SRST_A_GIC600 68 +#define SRST_A_GIC600_DEBUG 69 +#define SRST_A_GICADB_CORE2GIC 70 +#define SRST_A_GICADB_GIC2CORE 71 +#define SRST_A_SPINLOCK 72 +#define SRST_H_SDMMC_BUFFER 73 +#define SRST_D_SDMMC_BUFFER 74 +#define SRST_H_I2S0_8CH 75 +#define SRST_H_I2S1_8CH 76 +#define SRST_H_I2S2_2CH 77 +#define SRST_H_I2S3_2CH 78 + +/* cru_softrst_con5 */ +#define SRST_M_I2S0_8CH_TX 80 +#define SRST_M_I2S0_8CH_RX 81 +#define SRST_M_I2S1_8CH_TX 82 +#define SRST_M_I2S1_8CH_RX 83 +#define SRST_M_I2S2_2CH 84 +#define SRST_M_I2S3_2CH_TX 85 +#define SRST_M_I2S3_2CH_RX 86 +#define SRST_H_PDM 87 +#define SRST_M_PDM 88 +#define SRST_H_VAD 89 +#define SRST_H_SPDIF_8CH 90 +#define SRST_M_SPDIF_8CH 91 +#define SRST_H_AUDPWM 92 +#define SRST_S_AUDPWM 93 +#define SRST_H_ACDCDIG 94 +#define SRST_ACDCDIG 95 + +/* cru_softrst_con6 */ +#define SRST_A_SECURE_FLASH_NIU 96 +#define SRST_H_SECURE_FLASH_NIU 97 +#define SRST_A_CRYPTO_NS 103 +#define SRST_H_CRYPTO_NS 104 +#define SRST_CRYPTO_NS_CORE 105 +#define SRST_CRYPTO_NS_PKA 106 +#define SRST_CRYPTO_NS_RNG 107 +#define SRST_H_TRNG_NS 108 +#define SRST_TRNG_NS 109 + +/* cru_softrst_con7 */ +#define SRST_H_NANDC 112 +#define SRST_N_NANDC 113 +#define SRST_H_SFC 114 +#define SRST_H_SFC_XIP 115 +#define SRST_S_SFC 116 +#define SRST_A_EMMC 117 +#define SRST_H_EMMC 118 +#define SRST_B_EMMC 119 +#define SRST_C_EMMC 120 +#define SRST_T_EMMC 121 + +/* cru_softrst_con8 */ +#define SRST_A_PIPE_NIU 128 +#define SRST_P_PIPE_NIU 130 +#define SRST_P_PIPE_GRF 133 +#define SRST_A_SATA0 134 +#define SRST_SATA0_PIPE 135 +#define SRST_SATA0_PMALIVE 136 +#define SRST_SATA0_RXOOB 137 +#define SRST_A_SATA1 138 +#define SRST_SATA1_PIPE 139 +#define SRST_SATA1_PMALIVE 140 +#define SRST_SATA1_RXOOB 141 + +/* cru_softrst_con9 */ +#define SRST_A_SATA2 144 +#define SRST_SATA2_PIPE 145 +#define SRST_SATA2_PMALIVE 146 +#define SRST_SATA2_RXOOB 147 +#define SRST_USB3OTG0 148 +#define SRST_USB3OTG1 149 +#define SRST_XPCS 150 +#define SRST_XPCS_TX_DIV10 151 +#define SRST_XPCS_RX_DIV10 152 +#define SRST_XPCS_XGXS_RX 153 + +/* cru_softrst_con10 */ +#define SRST_P_PCIE20 160 +#define SRST_PCIE20_POWERUP 161 +#define SRST_MSTR_ARESET_PCIE20 162 +#define SRST_SLV_ARESET_PCIE20 163 +#define SRST_DBI_ARESET_PCIE20 164 +#define SRST_BRESET_PCIE20 165 +#define SRST_PERST_PCIE20 166 +#define SRST_CORE_RST_PCIE20 167 +#define SRST_NSTICKY_RST_PCIE20 168 +#define SRST_STICKY_RST_PCIE20 169 +#define SRST_PWR_RST_PCIE20 170 + +/* cru_softrst_con11 */ +#define SRST_P_PCIE30X1 176 +#define SRST_PCIE30X1_POWERUP 177 +#define SRST_M_ARESET_PCIE30X1 178 +#define SRST_S_ARESET_PCIE30X1 179 +#define SRST_D_ARESET_PCIE30X1 180 +#define SRST_BRESET_PCIE30X1 181 +#define SRST_PERST_PCIE30X1 182 +#define SRST_CORE_RST_PCIE30X1 183 +#define SRST_NSTC_RST_PCIE30X1 184 +#define SRST_STC_RST_PCIE30X1 185 +#define SRST_PWR_RST_PCIE30X1 186 + +/* cru_softrst_con12 */ +#define SRST_P_PCIE30X2 192 +#define SRST_PCIE30X2_POWERUP 193 +#define SRST_M_ARESET_PCIE30X2 194 +#define SRST_S_ARESET_PCIE30X2 195 +#define SRST_D_ARESET_PCIE30X2 196 +#define SRST_BRESET_PCIE30X2 197 +#define SRST_PERST_PCIE30X2 198 +#define SRST_CORE_RST_PCIE30X2 199 +#define SRST_NSTC_RST_PCIE30X2 200 +#define SRST_STC_RST_PCIE30X2 201 +#define SRST_PWR_RST_PCIE30X2 202 + +/* cru_softrst_con13 */ +#define SRST_A_PHP_NIU 208 +#define SRST_H_PHP_NIU 209 +#define SRST_P_PHP_NIU 210 +#define SRST_H_SDMMC0 211 +#define SRST_SDMMC0 212 +#define SRST_H_SDMMC1 213 +#define SRST_SDMMC1 214 +#define SRST_A_GMAC0 215 +#define SRST_GMAC0_TIMESTAMP 216 + +/* cru_softrst_con14 */ +#define SRST_A_USB_NIU 224 +#define SRST_H_USB_NIU 225 +#define SRST_P_USB_NIU 226 +#define SRST_P_USB_GRF 227 +#define SRST_H_USB2HOST0 228 +#define SRST_H_USB2HOST0_ARB 229 +#define SRST_USB2HOST0_UTMI 230 +#define SRST_H_USB2HOST1 231 +#define SRST_H_USB2HOST1_ARB 232 +#define SRST_USB2HOST1_UTMI 233 +#define SRST_H_SDMMC2 234 +#define SRST_SDMMC2 235 +#define SRST_A_GMAC1 236 +#define SRST_GMAC1_TIMESTAMP 237 + +/* cru_softrst_con15 */ +#define SRST_A_VI_NIU 240 +#define SRST_H_VI_NIU 241 +#define SRST_P_VI_NIU 242 +#define SRST_A_VICAP 247 +#define SRST_H_VICAP 248 +#define SRST_D_VICAP 249 +#define SRST_I_VICAP 250 +#define SRST_P_VICAP 251 +#define SRST_H_ISP 252 +#define SRST_ISP 253 +#define SRST_P_CSI2HOST1 255 + +/* cru_softrst_con16 */ +#define SRST_A_VO_NIU 256 +#define SRST_H_VO_NIU 257 +#define SRST_P_VO_NIU 258 +#define SRST_A_VOP_NIU 259 +#define SRST_A_VOP 260 +#define SRST_H_VOP 261 +#define SRST_VOP0 262 +#define SRST_VOP1 263 +#define SRST_VOP2 264 +#define SRST_VOP_PWM 265 +#define SRST_A_HDCP 266 +#define SRST_H_HDCP 267 +#define SRST_P_HDCP 268 +#define SRST_P_HDMI_HOST 270 +#define SRST_HDMI_HOST 271 + +/* cru_softrst_con17 */ +#define SRST_P_DSITX_0 272 +#define SRST_P_DSITX_1 273 +#define SRST_P_EDP_CTRL 274 +#define SRST_EDP_24M 275 +#define SRST_A_VPU_NIU 280 +#define SRST_H_VPU_NIU 281 +#define SRST_A_VPU 282 +#define SRST_H_VPU 283 +#define SRST_H_EINK 286 +#define SRST_P_EINK 287 + +/* cru_softrst_con18 */ +#define SRST_A_RGA_NIU 288 +#define SRST_H_RGA_NIU 289 +#define SRST_P_RGA_NIU 290 +#define SRST_A_RGA 292 +#define SRST_H_RGA 293 +#define SRST_RGA_CORE 294 +#define SRST_A_IEP 295 +#define SRST_H_IEP 296 +#define SRST_IEP_CORE 297 +#define SRST_H_EBC 298 +#define SRST_D_EBC 299 +#define SRST_A_JDEC 300 +#define SRST_H_JDEC 301 +#define SRST_A_JENC 302 +#define SRST_H_JENC 303 + +/* cru_softrst_con19 */ +#define SRST_A_VENC_NIU 304 +#define SRST_H_VENC_NIU 305 +#define SRST_A_RKVENC 307 +#define SRST_H_RKVENC 308 +#define SRST_RKVENC_CORE 309 + +/* cru_softrst_con20 */ +#define SRST_A_RKVDEC_NIU 320 +#define SRST_H_RKVDEC_NIU 321 +#define SRST_A_RKVDEC 322 +#define SRST_H_RKVDEC 323 +#define SRST_RKVDEC_CA 324 +#define SRST_RKVDEC_CORE 325 +#define SRST_RKVDEC_HEVC_CA 326 + +/* cru_softrst_con21 */ +#define SRST_A_BUS_NIU 336 +#define SRST_P_BUS_NIU 338 +#define SRST_P_CAN0 340 +#define SRST_CAN0 341 +#define SRST_P_CAN1 342 +#define SRST_CAN1 343 +#define SRST_P_CAN2 344 +#define SRST_CAN2 345 +#define SRST_P_GPIO1 346 +#define SRST_GPIO1 347 +#define SRST_P_GPIO2 348 +#define SRST_GPIO2 349 +#define SRST_P_GPIO3 350 +#define SRST_GPIO3 351 + +/* cru_softrst_con22 */ +#define SRST_P_GPIO4 352 +#define SRST_GPIO4 353 +#define SRST_P_I2C1 354 +#define SRST_I2C1 355 +#define SRST_P_I2C2 356 +#define SRST_I2C2 357 +#define SRST_P_I2C3 358 +#define SRST_I2C3 359 +#define SRST_P_I2C4 360 +#define SRST_I2C4 361 +#define SRST_P_I2C5 362 +#define SRST_I2C5 363 +#define SRST_P_OTPC_NS 364 +#define SRST_OTPC_NS_SBPI 365 +#define SRST_OTPC_NS_USR 366 + +/* cru_softrst_con23 */ +#define SRST_P_PWM1 368 +#define SRST_PWM1 369 +#define SRST_P_PWM2 370 +#define SRST_PWM2 371 +#define SRST_P_PWM3 372 +#define SRST_PWM3 373 +#define SRST_P_SPI0 374 +#define SRST_SPI0 375 +#define SRST_P_SPI1 376 +#define SRST_SPI1 377 +#define SRST_P_SPI2 378 +#define SRST_SPI2 379 +#define SRST_P_SPI3 380 +#define SRST_SPI3 381 + +/* cru_softrst_con24 */ +#define SRST_P_SARADC 384 +#define SRST_P_TSADC 385 +#define SRST_TSADC 386 +#define SRST_P_TIMER 387 +#define SRST_TIMER0 388 +#define SRST_TIMER1 389 +#define SRST_TIMER2 390 +#define SRST_TIMER3 391 +#define SRST_TIMER4 392 +#define SRST_TIMER5 393 +#define SRST_P_UART1 394 +#define SRST_S_UART1 395 + +/* cru_softrst_con25 */ +#define SRST_P_UART2 400 +#define SRST_S_UART2 401 +#define SRST_P_UART3 402 +#define SRST_S_UART3 403 +#define SRST_P_UART4 404 +#define SRST_S_UART4 405 +#define SRST_P_UART5 406 +#define SRST_S_UART5 407 +#define SRST_P_UART6 408 +#define SRST_S_UART6 409 +#define SRST_P_UART7 410 +#define SRST_S_UART7 411 +#define SRST_P_UART8 412 +#define SRST_S_UART8 413 +#define SRST_P_UART9 414 +#define SRST_S_UART9 415 + +/* cru_softrst_con26 */ +#define SRST_P_GRF 416 +#define SRST_P_GRF_VCCIO12 417 +#define SRST_P_GRF_VCCIO34 418 +#define SRST_P_GRF_VCCIO567 419 +#define SRST_P_SCR 420 +#define SRST_P_WDT_NS 421 +#define SRST_T_WDT_NS 422 +#define SRST_P_DFT2APB 423 +#define SRST_A_MCU 426 +#define SRST_P_INTMUX 427 +#define SRST_P_MAILBOX 428 + +/* cru_softrst_con27 */ +#define SRST_A_TOP_HIGH_NIU 432 +#define SRST_A_TOP_LOW_NIU 433 +#define SRST_H_TOP_NIU 434 +#define SRST_P_TOP_NIU 435 +#define SRST_P_TOP_CRU 438 +#define SRST_P_DDRPHY 439 +#define SRST_DDRPHY 440 +#define SRST_P_MIPICSIPHY 442 +#define SRST_P_MIPIDSIPHY0 443 +#define SRST_P_MIPIDSIPHY1 444 +#define SRST_P_PCIE30PHY 445 +#define SRST_PCIE30PHY 446 +#define SRST_P_PCIE30PHY_GRF 447 + +/* cru_softrst_con28 */ +#define SRST_P_APB2ASB_LEFT 448 +#define SRST_P_APB2ASB_BOTTOM 449 +#define SRST_P_ASB2APB_LEFT 450 +#define SRST_P_ASB2APB_BOTTOM 451 +#define SRST_P_PIPEPHY0 452 +#define SRST_PIPEPHY0 453 +#define SRST_P_PIPEPHY1 454 +#define SRST_PIPEPHY1 455 +#define SRST_P_PIPEPHY2 456 +#define SRST_PIPEPHY2 457 +#define SRST_P_USB2PHY0_GRF 458 +#define SRST_P_USB2PHY1_GRF 459 +#define SRST_P_CPU_BOOST 460 +#define SRST_CPU_BOOST 461 +#define SRST_P_OTPPHY 462 +#define SRST_OTPPHY 463 + +/* cru_softrst_con29 */ +#define SRST_USB2PHY0_POR 464 +#define SRST_USB2PHY0_USB3OTG0 465 +#define SRST_USB2PHY0_USB3OTG1 466 +#define SRST_USB2PHY1_POR 467 +#define SRST_USB2PHY1_USB2HOST0 468 +#define SRST_USB2PHY1_USB2HOST1 469 +#define SRST_P_EDPPHY_GRF 470 +#define SRST_TSADCPHY 471 +#define SRST_GMAC0_DELAYLINE 472 +#define SRST_GMAC1_DELAYLINE 473 +#define SRST_OTPC_ARB 474 +#define SRST_P_PIPEPHY0_GRF 475 +#define SRST_P_PIPEPHY1_GRF 476 +#define SRST_P_PIPEPHY2_GRF 477 + +#endif /* __DT_BINDINGS_CLK_ROCKCHIP_RK3568_H__ */ diff --git a/components/drivers/include/dt-bindings/clock/rp1.h b/components/drivers/include/dt-bindings/clock/rp1.h new file mode 100644 index 000000000000..ac78d82db9fc --- /dev/null +++ b/components/drivers/include/dt-bindings/clock/rp1.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __DT_BINDINGS_CLOCK_RP1_H__ +#define __DT_BINDINGS_CLOCK_RP1_H__ + +#define RP1_PLL_SYS_CORE 0 +#define RP1_PLL_AUDIO_CORE 1 +#define RP1_PLL_VIDEO_CORE 2 + +#define RP1_PLL_SYS 3 +#define RP1_PLL_AUDIO 4 +#define RP1_PLL_VIDEO 5 + +#define RP1_PLL_SYS_PRI_PH 6 +#define RP1_PLL_SYS_SEC_PH 7 +#define RP1_PLL_AUDIO_PRI_PH 8 + +#define RP1_PLL_SYS_SEC 9 +#define RP1_PLL_AUDIO_SEC 10 +#define RP1_PLL_VIDEO_SEC 11 + +#define RP1_CLK_SYS 12 +#define RP1_CLK_SLOW_SYS 13 +#define RP1_CLK_DMA 14 +#define RP1_CLK_UART 15 +#define RP1_CLK_ETH 16 +#define RP1_CLK_PWM0 17 +#define RP1_CLK_PWM1 18 +#define RP1_CLK_AUDIO_IN 19 +#define RP1_CLK_AUDIO_OUT 20 +#define RP1_CLK_I2S 21 +#define RP1_CLK_MIPI0_CFG 22 +#define RP1_CLK_MIPI1_CFG 23 +#define RP1_CLK_PCIE_AUX 24 +#define RP1_CLK_USBH0_MICROFRAME 25 +#define RP1_CLK_USBH1_MICROFRAME 26 +#define RP1_CLK_USBH0_SUSPEND 27 +#define RP1_CLK_USBH1_SUSPEND 28 +#define RP1_CLK_ETH_TSU 29 +#define RP1_CLK_ADC 30 +#define RP1_CLK_SDIO_TIMER 31 +#define RP1_CLK_SDIO_ALT_SRC 32 +#define RP1_CLK_GP0 33 +#define RP1_CLK_GP1 34 +#define RP1_CLK_GP2 35 +#define RP1_CLK_GP3 36 +#define RP1_CLK_GP4 37 +#define RP1_CLK_GP5 38 +#define RP1_CLK_VEC 39 +#define RP1_CLK_DPI 40 +#define RP1_CLK_MIPI0_DPI 41 +#define RP1_CLK_MIPI1_DPI 42 + +#endif /* __DT_BINDINGS_CLOCK_RP1_H__ */ diff --git a/components/drivers/include/dt-bindings/input/event-codes.h b/components/drivers/include/dt-bindings/input/event-codes.h new file mode 100644 index 000000000000..506de135d0d7 --- /dev/null +++ b/components/drivers/include/dt-bindings/input/event-codes.h @@ -0,0 +1,13 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __DT_BINDINGS_INPUT_EVENT_CODES_H__ +#define __DT_BINDINGS_INPUT_EVENT_CODES_H__ + +#define KEY_POWER 116 +#define KEY_RESTART 408 + +#endif /* __DT_BINDINGS_INPUT_EVENT_CODES_H__ */ \ No newline at end of file diff --git a/components/drivers/include/dt-bindings/interrupt-controller/irq.h b/components/drivers/include/dt-bindings/interrupt-controller/irq.h index 6252787f4d61..4ca8b237ddc8 100644 --- a/components/drivers/include/dt-bindings/interrupt-controller/irq.h +++ b/components/drivers/include/dt-bindings/interrupt-controller/irq.h @@ -7,11 +7,11 @@ #ifndef __DT_BINDINGS_INTERRUPT_CONTROLLER_IRQ_H__ #define __DT_BINDINGS_INTERRUPT_CONTROLLER_IRQ_H__ -#define IRQ_TYPE_NONE 0 -#define IRQ_TYPE_EDGE_RISING 1 -#define IRQ_TYPE_EDGE_FALLING 2 -#define IRQ_TYPE_EDGE_BOTH (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING) -#define IRQ_TYPE_LEVEL_HIGH 4 -#define IRQ_TYPE_LEVEL_LOW 8 +#define IRQ_TYPE_NONE 0 +#define IRQ_TYPE_EDGE_RISING 1 +#define IRQ_TYPE_EDGE_FALLING 2 +#define IRQ_TYPE_EDGE_BOTH (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING) +#define IRQ_TYPE_LEVEL_HIGH 4 +#define IRQ_TYPE_LEVEL_LOW 8 #endif /* __DT_BINDINGS_INTERRUPT_CONTROLLER_IRQ_H__ */ \ No newline at end of file diff --git a/components/drivers/include/dt-bindings/phy/phy-snps-pcie3.h b/components/drivers/include/dt-bindings/phy/phy-snps-pcie3.h new file mode 100644 index 000000000000..81d4ba92cb53 --- /dev/null +++ b/components/drivers/include/dt-bindings/phy/phy-snps-pcie3.h @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __DT_BINDINGS_PHY_SNPS_PCIE3_H__ +#define __DT_BINDINGS_PHY_SNPS_PCIE3_H__ + +/* + * pcie30_phy_mode[2:0] + * bit2: aggregation + * bit1: bifurcation for port 1 + * bit0: bifurcation for port 0 + */ +#define PHY_MODE_PCIE_AGGREGATION 4 /* PCIe3x4 */ +#define PHY_MODE_PCIE_NANBNB 0 /* P1:PCIe3x2 + P0:PCIe3x2 */ +#define PHY_MODE_PCIE_NANBBI 1 /* P1:PCIe3x2 + P0:PCIe3x1*2 */ +#define PHY_MODE_PCIE_NABINB 2 /* P1:PCIe3x1*2 + P0:PCIe3x2 */ +#define PHY_MODE_PCIE_NABIBI 3 /* P1:PCIe3x1*2 + P0:PCIe3x1*2 */ + +#endif /* __DT_BINDINGS_PHY_SNPS_PCIE3_H__ */ diff --git a/components/drivers/include/dt-bindings/phy/phy.h b/components/drivers/include/dt-bindings/phy/phy.h new file mode 100644 index 000000000000..3d92bf919807 --- /dev/null +++ b/components/drivers/include/dt-bindings/phy/phy.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __DT_BINDINGS_PHY_H__ +#define __DT_BINDINGS_PHY_H__ + +#define PHY_NONE 0 +#define PHY_TYPE_SATA 1 +#define PHY_TYPE_PCIE 2 +#define PHY_TYPE_USB2 3 +#define PHY_TYPE_USB3 4 +#define PHY_TYPE_UFS 5 +#define PHY_TYPE_DP 6 +#define PHY_TYPE_XPCS 7 +#define PHY_TYPE_SGMII 8 +#define PHY_TYPE_QSGMII 9 +#define PHY_TYPE_DPHY 10 +#define PHY_TYPE_CPHY 11 +#define PHY_TYPE_USXGMII 12 + +#endif /* __DT_BINDINGS_PHY_H__ */ diff --git a/components/drivers/include/dt-bindings/pin/pin.h b/components/drivers/include/dt-bindings/pin/pin.h new file mode 100644 index 000000000000..3e72289d225d --- /dev/null +++ b/components/drivers/include/dt-bindings/pin/pin.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __DT_BINDINGS_PIN_PIN_H__ +#define __DT_BINDINGS_PIN_PIN_H__ + +/* Bit 0 express polarity */ +#define PIN_ACTIVE_HIGH 0 +#define PIN_ACTIVE_LOW 1 + +/* Bit 1 express single-endedness */ +#define PIN_PUSH_PULL 0 +#define PIN_SINGLE_ENDED 2 + +/* Bit 2 express Open drain or open source */ +#define PIN_LINE_OPEN_SOURCE 0 +#define PIN_LINE_OPEN_DRAIN 4 + +/* + * Open Drain/Collector is the combination of single-ended open drain interface. + * Open Source/Emitter is the combination of single-ended open source interface. + */ +#define PIN_OPEN_DRAIN (PIN_SINGLE_ENDED | PIN_LINE_OPEN_DRAIN) +#define PIN_OPEN_SOURCE (PIN_SINGLE_ENDED | PIN_LINE_OPEN_SOURCE) + +/* Bit 3 express PIN suspend/resume and reset persistence */ +#define PIN_PERSISTENT 0 +#define PIN_TRANSITORY 8 + +/* Bit 4 express pull up */ +#define PIN_PULL_UP 16 + +/* Bit 5 express pull down */ +#define PIN_PULL_DOWN 32 + +/* Bit 6 express pull disable */ +#define PIN_PULL_DISABLE 64 + +#endif /* __DT_BINDINGS_PIN_PIN_H__ */ \ No newline at end of file diff --git a/components/drivers/include/dt-bindings/pin/state.h b/components/drivers/include/dt-bindings/pin/state.h new file mode 100644 index 000000000000..40af55b783ff --- /dev/null +++ b/components/drivers/include/dt-bindings/pin/state.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __DT_BINDINGS_PIN_STATE_H__ +#define __DT_BINDINGS_PIN_STATE_H__ + +#define PIND_FLAGS_BIT_DIR_SET (1 << 0) +#define PIND_FLAGS_BIT_DIR_OUT (1 << 1) +#define PIND_FLAGS_BIT_DIR_VAL (1 << 2) +#define PIND_FLAGS_BIT_OPEN_DRAIN (1 << 3) +#define PIND_FLAGS_BIT_NONEXCLUSIVE (1 << 4) + +/* Don't change anything */ +#define PIND_ASIS 0 +/* Set lines to input mode */ +#define PIND_IN PIND_FLAGS_BIT_DIR_SET +/* Set lines to output and drive them low */ +#define PIND_OUT_LOW (PIND_FLAGS_BIT_DIR_SET | PIND_FLAGS_BIT_DIR_OUT) +/* Set lines to output and drive them high */ +#define PIND_OUT_HIGH (PIND_FLAGS_BIT_DIR_SET | PIND_FLAGS_BIT_DIR_OUT | PIND_FLAGS_BIT_DIR_VAL) +/* Set lines to open-drain output and drive them low */ +#define PIND_OUT_LOW_OPEN_DRAIN (PIND_OUT_LOW | PIND_FLAGS_BIT_OPEN_DRAIN) +/* Set lines to open-drain output and drive them high */ +#define PIND_OUT_HIGH_OPEN_DRAIN (PIND_OUT_HIGH | PIND_FLAGS_BIT_OPEN_DRAIN) + +#endif /* __DT_BINDINGS_PIN_STATE_H__ */ \ No newline at end of file diff --git a/components/drivers/include/dt-bindings/pinctrl/rockchip.h b/components/drivers/include/dt-bindings/pinctrl/rockchip.h new file mode 100644 index 000000000000..7cee4123eba1 --- /dev/null +++ b/components/drivers/include/dt-bindings/pinctrl/rockchip.h @@ -0,0 +1,232 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __DT_BINDINGS_PINCTRL_ROCKCHIP_H__ +#define __DT_BINDINGS_PINCTRL_ROCKCHIP_H__ + +#define RK_GPIO0 0 +#define RK_GPIO1 1 +#define RK_GPIO2 2 +#define RK_GPIO3 3 +#define RK_GPIO4 4 + +#define RK_PA0 0 +#define RK_PA1 1 +#define RK_PA2 2 +#define RK_PA3 3 +#define RK_PA4 4 +#define RK_PA5 5 +#define RK_PA6 6 +#define RK_PA7 7 +#define RK_PB0 8 +#define RK_PB1 9 +#define RK_PB2 10 +#define RK_PB3 11 +#define RK_PB4 12 +#define RK_PB5 13 +#define RK_PB6 14 +#define RK_PB7 15 +#define RK_PC0 16 +#define RK_PC1 17 +#define RK_PC2 18 +#define RK_PC3 19 +#define RK_PC4 20 +#define RK_PC5 21 +#define RK_PC6 22 +#define RK_PC7 23 +#define RK_PD0 24 +#define RK_PD1 25 +#define RK_PD2 26 +#define RK_PD3 27 +#define RK_PD4 28 +#define RK_PD5 29 +#define RK_PD6 30 +#define RK_PD7 31 + +#define RK_FUNC_GPIO 0 +#define RK_FUNC_0 0 +#define RK_FUNC_1 1 +#define RK_FUNC_2 2 +#define RK_FUNC_3 3 +#define RK_FUNC_4 4 +#define RK_FUNC_5 5 +#define RK_FUNC_6 6 +#define RK_FUNC_7 7 +#define RK_FUNC_8 8 +#define RK_FUNC_9 9 +#define RK_FUNC_10 10 +#define RK_FUNC_11 11 +#define RK_FUNC_12 12 +#define RK_FUNC_13 13 +#define RK_FUNC_14 14 +#define RK_FUNC_15 15 + +#define RK_GPIO0_A0 0 +#define RK_GPIO0_A1 1 +#define RK_GPIO0_A2 2 +#define RK_GPIO0_A3 3 +#define RK_GPIO0_A4 4 +#define RK_GPIO0_A5 5 +#define RK_GPIO0_A6 6 +#define RK_GPIO0_A7 7 +#define RK_GPIO0_B0 8 +#define RK_GPIO0_B1 9 +#define RK_GPIO0_B2 10 +#define RK_GPIO0_B3 11 +#define RK_GPIO0_B4 12 +#define RK_GPIO0_B5 13 +#define RK_GPIO0_B6 14 +#define RK_GPIO0_B7 15 +#define RK_GPIO0_C0 16 +#define RK_GPIO0_C1 17 +#define RK_GPIO0_C2 18 +#define RK_GPIO0_C3 19 +#define RK_GPIO0_C4 20 +#define RK_GPIO0_C5 21 +#define RK_GPIO0_C6 22 +#define RK_GPIO0_C7 23 +#define RK_GPIO0_D0 24 +#define RK_GPIO0_D1 25 +#define RK_GPIO0_D2 26 +#define RK_GPIO0_D3 27 +#define RK_GPIO0_D4 28 +#define RK_GPIO0_D5 29 +#define RK_GPIO0_D6 30 +#define RK_GPIO0_D7 31 + +#define RK_GPIO1_A0 32 +#define RK_GPIO1_A1 33 +#define RK_GPIO1_A2 34 +#define RK_GPIO1_A3 35 +#define RK_GPIO1_A4 36 +#define RK_GPIO1_A5 37 +#define RK_GPIO1_A6 38 +#define RK_GPIO1_A7 39 +#define RK_GPIO1_B0 40 +#define RK_GPIO1_B1 41 +#define RK_GPIO1_B2 42 +#define RK_GPIO1_B3 43 +#define RK_GPIO1_B4 44 +#define RK_GPIO1_B5 45 +#define RK_GPIO1_B6 46 +#define RK_GPIO1_B7 47 +#define RK_GPIO1_C0 48 +#define RK_GPIO1_C1 49 +#define RK_GPIO1_C2 50 +#define RK_GPIO1_C3 51 +#define RK_GPIO1_C4 52 +#define RK_GPIO1_C5 53 +#define RK_GPIO1_C6 54 +#define RK_GPIO1_C7 55 +#define RK_GPIO1_D0 56 +#define RK_GPIO1_D1 57 +#define RK_GPIO1_D2 58 +#define RK_GPIO1_D3 59 +#define RK_GPIO1_D4 60 +#define RK_GPIO1_D5 61 +#define RK_GPIO1_D6 62 +#define RK_GPIO1_D7 63 + +#define RK_GPIO2_A0 64 +#define RK_GPIO2_A1 65 +#define RK_GPIO2_A2 66 +#define RK_GPIO2_A3 67 +#define RK_GPIO2_A4 68 +#define RK_GPIO2_A5 69 +#define RK_GPIO2_A6 70 +#define RK_GPIO2_A7 71 +#define RK_GPIO2_B0 72 +#define RK_GPIO2_B1 73 +#define RK_GPIO2_B2 74 +#define RK_GPIO2_B3 75 +#define RK_GPIO2_B4 76 +#define RK_GPIO2_B5 77 +#define RK_GPIO2_B6 78 +#define RK_GPIO2_B7 79 +#define RK_GPIO2_C0 80 +#define RK_GPIO2_C1 81 +#define RK_GPIO2_C2 82 +#define RK_GPIO2_C3 83 +#define RK_GPIO2_C4 84 +#define RK_GPIO2_C5 85 +#define RK_GPIO2_C6 86 +#define RK_GPIO2_C7 87 +#define RK_GPIO2_D0 88 +#define RK_GPIO2_D1 89 +#define RK_GPIO2_D2 90 +#define RK_GPIO2_D3 91 +#define RK_GPIO2_D4 92 +#define RK_GPIO2_D5 93 +#define RK_GPIO2_D6 94 +#define RK_GPIO2_D7 95 + +#define RK_GPIO3_A0 96 +#define RK_GPIO3_A1 97 +#define RK_GPIO3_A2 98 +#define RK_GPIO3_A3 99 +#define RK_GPIO3_A4 100 +#define RK_GPIO3_A5 101 +#define RK_GPIO3_A6 102 +#define RK_GPIO3_A7 103 +#define RK_GPIO3_B0 104 +#define RK_GPIO3_B1 105 +#define RK_GPIO3_B2 106 +#define RK_GPIO3_B3 107 +#define RK_GPIO3_B4 108 +#define RK_GPIO3_B5 109 +#define RK_GPIO3_B6 110 +#define RK_GPIO3_B7 111 +#define RK_GPIO3_C0 112 +#define RK_GPIO3_C1 113 +#define RK_GPIO3_C2 114 +#define RK_GPIO3_C3 115 +#define RK_GPIO3_C4 116 +#define RK_GPIO3_C5 117 +#define RK_GPIO3_C6 118 +#define RK_GPIO3_C7 119 +#define RK_GPIO3_D0 120 +#define RK_GPIO3_D1 121 +#define RK_GPIO3_D2 122 +#define RK_GPIO3_D3 123 +#define RK_GPIO3_D4 124 +#define RK_GPIO3_D5 125 +#define RK_GPIO3_D6 126 +#define RK_GPIO3_D7 127 + +#define RK_GPIO4_A0 128 +#define RK_GPIO4_A1 129 +#define RK_GPIO4_A2 130 +#define RK_GPIO4_A3 131 +#define RK_GPIO4_A4 132 +#define RK_GPIO4_A5 133 +#define RK_GPIO4_A6 134 +#define RK_GPIO4_A7 135 +#define RK_GPIO4_B0 136 +#define RK_GPIO4_B1 137 +#define RK_GPIO4_B2 138 +#define RK_GPIO4_B3 139 +#define RK_GPIO4_B4 140 +#define RK_GPIO4_B5 141 +#define RK_GPIO4_B6 142 +#define RK_GPIO4_B7 143 +#define RK_GPIO4_C0 144 +#define RK_GPIO4_C1 145 +#define RK_GPIO4_C2 146 +#define RK_GPIO4_C3 147 +#define RK_GPIO4_C4 148 +#define RK_GPIO4_C5 149 +#define RK_GPIO4_C6 150 +#define RK_GPIO4_C7 151 +#define RK_GPIO4_D0 152 +#define RK_GPIO4_D1 153 +#define RK_GPIO4_D2 154 +#define RK_GPIO4_D3 155 +#define RK_GPIO4_D4 156 +#define RK_GPIO4_D5 157 +#define RK_GPIO4_D6 158 +#define RK_GPIO4_D7 159 + +#endif /* __DT_BINDINGS_PINCTRL_ROCKCHIP_H__ */ diff --git a/components/drivers/include/dt-bindings/power/bcm2835-pm.h b/components/drivers/include/dt-bindings/power/bcm2835-pm.h new file mode 100644 index 000000000000..5864167be089 --- /dev/null +++ b/components/drivers/include/dt-bindings/power/bcm2835-pm.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __DT_BINDINGS_POWER_BCM2835_PM_H__ +#define __DT_BINDINGS_POWER_BCM2835_PM_H__ + +#define BCM2835_POWER_DOMAIN_GRAFX 0 +#define BCM2835_POWER_DOMAIN_GRAFX_V3D 1 +#define BCM2835_POWER_DOMAIN_IMAGE 2 +#define BCM2835_POWER_DOMAIN_IMAGE_PERI 3 +#define BCM2835_POWER_DOMAIN_IMAGE_ISP 4 +#define BCM2835_POWER_DOMAIN_IMAGE_H264 5 +#define BCM2835_POWER_DOMAIN_USB 6 +#define BCM2835_POWER_DOMAIN_DSI0 7 +#define BCM2835_POWER_DOMAIN_DSI1 8 +#define BCM2835_POWER_DOMAIN_CAM0 9 +#define BCM2835_POWER_DOMAIN_CAM1 10 +#define BCM2835_POWER_DOMAIN_CCP2TX 11 +#define BCM2835_POWER_DOMAIN_HDMI 12 + +#define BCM2835_POWER_DOMAIN_COUNT 13 + +#define BCM2835_RESET_V3D 0 +#define BCM2835_RESET_ISP 1 +#define BCM2835_RESET_H264 2 + +#define BCM2835_RESET_COUNT 3 + +#endif /* __DT_BINDINGS_POWER_BCM2835_PM_H__ */ diff --git a/components/drivers/include/dt-bindings/power/px30-power.h b/components/drivers/include/dt-bindings/power/px30-power.h new file mode 100644 index 000000000000..6ff9080ce9ea --- /dev/null +++ b/components/drivers/include/dt-bindings/power/px30-power.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __DT_BINDINGS_POWER_PX30_POWER_H__ +#define __DT_BINDINGS_POWER_PX30_POWER_H__ + +/* VD_CORE */ +#define PX30_PD_A35_0 0 +#define PX30_PD_A35_1 1 +#define PX30_PD_A35_2 2 +#define PX30_PD_A35_3 3 +#define PX30_PD_SCU 4 + +/* VD_LOGIC */ +#define PX30_PD_USB 5 +#define PX30_PD_DDR 6 +#define PX30_PD_SDCARD 7 +#define PX30_PD_CRYPTO 8 +#define PX30_PD_GMAC 9 +#define PX30_PD_MMC_NAND 10 +#define PX30_PD_VPU 11 +#define PX30_PD_VO 12 +#define PX30_PD_VI 13 +#define PX30_PD_GPU 14 + +/* VD_PMU */ +#define PX30_PD_PMU 15 + +#endif /* __DT_BINDINGS_POWER_PX30_POWER_H__ */ diff --git a/components/drivers/include/dt-bindings/power/raspberrypi-power.h b/components/drivers/include/dt-bindings/power/raspberrypi-power.h new file mode 100644 index 000000000000..573c39206bd1 --- /dev/null +++ b/components/drivers/include/dt-bindings/power/raspberrypi-power.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __DT_BINDINGS_POWER_RASPBERRYPI_POWER_H__ +#define __DT_BINDINGS_POWER_RASPBERRYPI_POWER_H__ + +#define RPI_POWER_DOMAIN_I2C0 0 +#define RPI_POWER_DOMAIN_I2C1 1 +#define RPI_POWER_DOMAIN_I2C2 2 +#define RPI_POWER_DOMAIN_VIDEO_SCALER 3 +#define RPI_POWER_DOMAIN_VPU1 4 +#define RPI_POWER_DOMAIN_HDMI 5 +#define RPI_POWER_DOMAIN_USB 6 +#define RPI_POWER_DOMAIN_VEC 7 +#define RPI_POWER_DOMAIN_JPEG 8 +#define RPI_POWER_DOMAIN_H264 9 +#define RPI_POWER_DOMAIN_V3D 10 +#define RPI_POWER_DOMAIN_ISP 11 +#define RPI_POWER_DOMAIN_UNICAM0 12 +#define RPI_POWER_DOMAIN_UNICAM1 13 +#define RPI_POWER_DOMAIN_CCP2RX 14 +#define RPI_POWER_DOMAIN_CSI2 15 +#define RPI_POWER_DOMAIN_CPI 16 +#define RPI_POWER_DOMAIN_DSI0 17 +#define RPI_POWER_DOMAIN_DSI1 18 +#define RPI_POWER_DOMAIN_TRANSPOSER 19 +#define RPI_POWER_DOMAIN_CCP2TX 20 +#define RPI_POWER_DOMAIN_CDP 21 +#define RPI_POWER_DOMAIN_ARM 22 + +#define RPI_POWER_DOMAIN_COUNT 23 + +#endif /* __DT_BINDINGS_POWER_BCMRASPBERRYPI_POWERH__ */ diff --git a/components/drivers/include/dt-bindings/power/rk3036-power.h b/components/drivers/include/dt-bindings/power/rk3036-power.h new file mode 100644 index 000000000000..1163f3760dfe --- /dev/null +++ b/components/drivers/include/dt-bindings/power/rk3036-power.h @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __DT_BINDINGS_POWER_RK3036_POWER_H__ +#define __DT_BINDINGS_POWER_RK3036_POWER_H__ + +#define RK3036_PD_MSCH 0 +#define RK3036_PD_CORE 1 +#define RK3036_PD_PERI 2 +#define RK3036_PD_VIO 3 +#define RK3036_PD_VPU 4 +#define RK3036_PD_GPU 5 +#define RK3036_PD_SYS 6 + +#endif /* __DT_BINDINGS_POWER_RK3036_POWER_H__ */ diff --git a/components/drivers/include/dt-bindings/power/rk3066-power.h b/components/drivers/include/dt-bindings/power/rk3066-power.h new file mode 100644 index 000000000000..406681a814f7 --- /dev/null +++ b/components/drivers/include/dt-bindings/power/rk3066-power.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __DT_BINDINGS_POWER_RK3066_POWER_H__ +#define __DT_BINDINGS_POWER_RK3066_POWER_H__ + +/* VD_CORE */ +#define RK3066_PD_A9_0 0 +#define RK3066_PD_A9_1 1 +#define RK3066_PD_DBG 4 +#define RK3066_PD_SCU 5 + +/* VD_LOGIC */ +#define RK3066_PD_VIDEO 6 +#define RK3066_PD_VIO 7 +#define RK3066_PD_GPU 8 +#define RK3066_PD_PERI 9 +#define RK3066_PD_CPU 10 +#define RK3066_PD_ALIVE 11 + +/* VD_PMU */ +#define RK3066_PD_RTC 12 + +#endif /* __DT_BINDINGS_POWER_RK3066_POWER_H__ */ diff --git a/components/drivers/include/dt-bindings/power/rk3128-power.h b/components/drivers/include/dt-bindings/power/rk3128-power.h new file mode 100644 index 000000000000..88d88da4fb27 --- /dev/null +++ b/components/drivers/include/dt-bindings/power/rk3128-power.h @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __DT_BINDINGS_POWER_RK3128_POWER_H__ +#define __DT_BINDINGS_POWER_RK3128_POWER_H__ + +/* VD_CORE */ +#define RK3128_PD_CORE 0 + +/* VD_LOGIC */ +#define RK3128_PD_VIO 1 +#define RK3128_PD_VIDEO 2 +#define RK3128_PD_GPU 3 +#define RK3128_PD_MSCH 4 + +#endif /* __DT_BINDINGS_POWER_RK3128_POWER_H__ */ diff --git a/components/drivers/include/dt-bindings/power/rk3188-power.h b/components/drivers/include/dt-bindings/power/rk3188-power.h new file mode 100644 index 000000000000..ab7969c510f7 --- /dev/null +++ b/components/drivers/include/dt-bindings/power/rk3188-power.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __DT_BINDINGS_POWER_RK3188_POWER_H__ +#define __DT_BINDINGS_POWER_RK3188_POWER_H__ + +/* VD_CORE */ +#define RK3188_PD_A9_0 0 +#define RK3188_PD_A9_1 1 +#define RK3188_PD_A9_2 2 +#define RK3188_PD_A9_3 3 +#define RK3188_PD_DBG 4 +#define RK3188_PD_SCU 5 + +/* VD_LOGIC */ +#define RK3188_PD_VIDEO 6 +#define RK3188_PD_VIO 7 +#define RK3188_PD_GPU 8 +#define RK3188_PD_PERI 9 +#define RK3188_PD_CPU 10 +#define RK3188_PD_ALIVE 11 + +/* VD_PMU */ +#define RK3188_PD_RTC 12 + +#endif /* __DT_BINDINGS_POWER_RK3188_POWER_H__ */ diff --git a/components/drivers/include/dt-bindings/power/rk3228-power.h b/components/drivers/include/dt-bindings/power/rk3228-power.h new file mode 100644 index 000000000000..71b0124a087d --- /dev/null +++ b/components/drivers/include/dt-bindings/power/rk3228-power.h @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __DT_BINDINGS_POWER_RK3228_POWER_H__ +#define __DT_BINDINGS_POWER_RK3228_POWER_H__ + +#define RK3228_PD_CORE 0 +#define RK3228_PD_MSCH 1 +#define RK3228_PD_BUS 2 +#define RK3228_PD_SYS 3 +#define RK3228_PD_VIO 4 +#define RK3228_PD_VOP 5 +#define RK3228_PD_VPU 6 +#define RK3228_PD_RKVDEC 7 +#define RK3228_PD_GPU 8 +#define RK3228_PD_PERI 9 +#define RK3228_PD_GMAC 10 + +#endif /* __DT_BINDINGS_POWER_RK3228_POWER_H__ */ diff --git a/components/drivers/include/dt-bindings/power/rk3288-power.h b/components/drivers/include/dt-bindings/power/rk3288-power.h new file mode 100644 index 000000000000..19d6b19ca850 --- /dev/null +++ b/components/drivers/include/dt-bindings/power/rk3288-power.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __DT_BINDINGS_POWER_RK3288_POWER_H__ +#define __DT_BINDINGS_POWER_RK3288_POWER_H__ + +/* VD_CORE */ +#define RK3288_PD_A17_0 0 +#define RK3288_PD_A17_1 1 +#define RK3288_PD_A17_2 2 +#define RK3288_PD_A17_3 3 +#define RK3288_PD_SCU 4 +#define RK3288_PD_DEBUG 5 +#define RK3288_PD_MEM 6 + +/* VD_LOGIC */ +#define RK3288_PD_BUS 7 +#define RK3288_PD_PERI 8 +#define RK3288_PD_VIO 9 +#define RK3288_PD_ALIVE 10 +#define RK3288_PD_HEVC 11 +#define RK3288_PD_VIDEO 12 + +/* VD_GPU */ +#define RK3288_PD_GPU 13 + +/* VD_PMU */ +#define RK3288_PD_PMU 14 + +#endif /* __DT_BINDINGS_POWER_RK3288_POWER_H__ */ diff --git a/components/drivers/include/dt-bindings/power/rk3328-power.h b/components/drivers/include/dt-bindings/power/rk3328-power.h new file mode 100644 index 000000000000..df2dab049aa8 --- /dev/null +++ b/components/drivers/include/dt-bindings/power/rk3328-power.h @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __DT_BINDINGS_POWER_RK3328_POWER_H__ +#define __DT_BINDINGS_POWER_RK3328_POWER_H__ + +#define RK3328_PD_CORE 0 +#define RK3328_PD_GPU 1 +#define RK3328_PD_BUS 2 +#define RK3328_PD_MSCH 3 +#define RK3328_PD_PERI 4 +#define RK3328_PD_VIDEO 5 +#define RK3328_PD_HEVC 6 +#define RK3328_PD_SYS 7 +#define RK3328_PD_VPU 8 +#define RK3328_PD_VIO 9 + +#endif /* __DT_BINDINGS_POWER_RK3328_POWER_H__ */ diff --git a/components/drivers/include/dt-bindings/power/rk3366-power.h b/components/drivers/include/dt-bindings/power/rk3366-power.h new file mode 100644 index 000000000000..f34f7a974acf --- /dev/null +++ b/components/drivers/include/dt-bindings/power/rk3366-power.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __DT_BINDINGS_POWER_RK3366_POWER_H__ +#define __DT_BINDINGS_POWER_RK3366_POWER_H__ + +/* VD_CORE */ +#define RK3366_PD_A53_0 0 +#define RK3366_PD_A53_1 1 +#define RK3366_PD_A53_2 2 +#define RK3366_PD_A53_3 3 + +/* VD_LOGIC */ +#define RK3366_PD_BUS 4 +#define RK3366_PD_PERI 5 +#define RK3366_PD_VIO 6 +#define RK3366_PD_VIDEO 7 +#define RK3366_PD_RKVDEC 8 +#define RK3366_PD_WIFIBT 9 +#define RK3366_PD_VPU 10 +#define RK3366_PD_GPU 11 +#define RK3366_PD_ALIVE 12 + +/* VD_PMU */ +#define RK3366_PD_PMU 13 + +#endif /* __DT_BINDINGS_POWER_RK3366_POWER_H__ */ diff --git a/components/drivers/include/dt-bindings/power/rk3368-power.h b/components/drivers/include/dt-bindings/power/rk3368-power.h new file mode 100644 index 000000000000..4355fc5629c8 --- /dev/null +++ b/components/drivers/include/dt-bindings/power/rk3368-power.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __DT_BINDINGS_POWER_RK3368_POWER_H__ +#define __DT_BINDINGS_POWER_RK3368_POWER_H__ + +/* VD_CORE */ +#define RK3368_PD_A53_L0 0 +#define RK3368_PD_A53_L1 1 +#define RK3368_PD_A53_L2 2 +#define RK3368_PD_A53_L3 3 +#define RK3368_PD_SCU_L 4 +#define RK3368_PD_A53_B0 5 +#define RK3368_PD_A53_B1 6 +#define RK3368_PD_A53_B2 7 +#define RK3368_PD_A53_B3 8 +#define RK3368_PD_SCU_B 9 + +/* VD_LOGIC */ +#define RK3368_PD_BUS 10 +#define RK3368_PD_PERI 11 +#define RK3368_PD_VIO 12 +#define RK3368_PD_ALIVE 13 +#define RK3368_PD_VIDEO 14 +#define RK3368_PD_GPU_0 15 +#define RK3368_PD_GPU_1 16 + +/* VD_PMU */ +#define RK3368_PD_PMU 17 + +#endif /* __DT_BINDINGS_POWER_RK3368_POWER_H__ */ diff --git a/components/drivers/include/dt-bindings/power/rk3399-power.h b/components/drivers/include/dt-bindings/power/rk3399-power.h new file mode 100644 index 000000000000..12db7b1530ad --- /dev/null +++ b/components/drivers/include/dt-bindings/power/rk3399-power.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __DT_BINDINGS_POWER_RK3399_POWER_H__ +#define __DT_BINDINGS_POWER_RK3399_POWER_H__ + +/* VD_CORE_L */ +#define RK3399_PD_A53_L0 0 +#define RK3399_PD_A53_L1 1 +#define RK3399_PD_A53_L2 2 +#define RK3399_PD_A53_L3 3 +#define RK3399_PD_SCU_L 4 + +/* VD_CORE_B */ +#define RK3399_PD_A72_B0 5 +#define RK3399_PD_A72_B1 6 +#define RK3399_PD_SCU_B 7 + +/* VD_LOGIC */ +#define RK3399_PD_TCPD0 8 +#define RK3399_PD_TCPD1 9 +#define RK3399_PD_CCI 10 +#define RK3399_PD_CCI0 11 +#define RK3399_PD_CCI1 12 +#define RK3399_PD_PERILP 13 +#define RK3399_PD_PERIHP 14 +#define RK3399_PD_VIO 15 +#define RK3399_PD_VO 16 +#define RK3399_PD_VOPB 17 +#define RK3399_PD_VOPL 18 +#define RK3399_PD_ISP0 19 +#define RK3399_PD_ISP1 20 +#define RK3399_PD_HDCP 21 +#define RK3399_PD_GMAC 22 +#define RK3399_PD_EMMC 23 +#define RK3399_PD_USB3 24 +#define RK3399_PD_EDP 25 +#define RK3399_PD_GIC 26 +#define RK3399_PD_SD 27 +#define RK3399_PD_SDIOAUDIO 28 +#define RK3399_PD_ALIVE 29 + +/* VD_CENTER */ +#define RK3399_PD_CENTER 30 +#define RK3399_PD_VCODEC 31 +#define RK3399_PD_VDU 32 +#define RK3399_PD_RGA 33 +#define RK3399_PD_IEP 34 + +/* VD_GPU */ +#define RK3399_PD_GPU 35 + +/* VD_PMU */ +#define RK3399_PD_PMU 36 + +#endif /* __DT_BINDINGS_POWER_RK3399_POWER_H__ */ diff --git a/components/drivers/include/dt-bindings/power/rk3568-power.h b/components/drivers/include/dt-bindings/power/rk3568-power.h new file mode 100644 index 000000000000..7d32f6e0a997 --- /dev/null +++ b/components/drivers/include/dt-bindings/power/rk3568-power.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __DT_BINDINGS_POWER_RK3568_POWER_H__ +#define __DT_BINDINGS_POWER_RK3568_POWER_H__ + +/* VD_CORE */ +#define RK3568_PD_CPU_0 0 +#define RK3568_PD_CPU_1 1 +#define RK3568_PD_CPU_2 2 +#define RK3568_PD_CPU_3 3 +#define RK3568_PD_CORE_ALIVE 4 + +/* VD_PMU */ +#define RK3568_PD_PMU 5 + +/* VD_NPU */ +#define RK3568_PD_NPU 6 + +/* VD_GPU */ +#define RK3568_PD_GPU 7 + +/* VD_LOGIC */ +#define RK3568_PD_VI 8 +#define RK3568_PD_VO 9 +#define RK3568_PD_RGA 10 +#define RK3568_PD_VPU 11 +#define RK3568_PD_CENTER 12 +#define RK3568_PD_RKVDEC 13 +#define RK3568_PD_RKVENC 14 +#define RK3568_PD_PIPE 15 +#define RK3568_PD_LOGIC_ALIVE 16 + +#endif /* __DT_BINDINGS_POWER_RK3568_POWER_H__ */ diff --git a/components/drivers/include/dt-bindings/power/rk3588-power.h b/components/drivers/include/dt-bindings/power/rk3588-power.h new file mode 100644 index 000000000000..8438b6e66b37 --- /dev/null +++ b/components/drivers/include/dt-bindings/power/rk3588-power.h @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __DT_BINDINGS_POWER_RK3588_POWER_H__ +#define __DT_BINDINGS_POWER_RK3588_POWER_H__ + +/* VD_LITDSU */ +#define RK3588_PD_CPU_0 0 +#define RK3588_PD_CPU_1 1 +#define RK3588_PD_CPU_2 2 +#define RK3588_PD_CPU_3 3 + +/* VD_BIGCORE0 */ +#define RK3588_PD_CPU_4 4 +#define RK3588_PD_CPU_5 5 + +/* VD_BIGCORE1 */ +#define RK3588_PD_CPU_6 6 +#define RK3588_PD_CPU_7 7 + +/* VD_NPU */ +#define RK3588_PD_NPU 8 +#define RK3588_PD_NPUTOP 9 +#define RK3588_PD_NPU1 10 +#define RK3588_PD_NPU2 11 + +/* VD_GPU */ +#define RK3588_PD_GPU 12 + +/* VD_VCODEC */ +#define RK3588_PD_VCODEC 13 +#define RK3588_PD_RKVDEC0 14 +#define RK3588_PD_RKVDEC1 15 +#define RK3588_PD_VENC0 16 +#define RK3588_PD_VENC1 17 + +/* VD_DD01 */ +#define RK3588_PD_DDR01 18 + +/* VD_DD23 */ +#define RK3588_PD_DDR23 19 + +/* VD_LOGIC */ +#define RK3588_PD_CENTER 20 +#define RK3588_PD_VDPU 21 +#define RK3588_PD_RGA30 22 +#define RK3588_PD_AV1 23 +#define RK3588_PD_VOP 24 +#define RK3588_PD_VO0 25 +#define RK3588_PD_VO1 26 +#define RK3588_PD_VI 27 +#define RK3588_PD_ISP1 28 +#define RK3588_PD_FEC 29 +#define RK3588_PD_RGA31 30 +#define RK3588_PD_USB 31 +#define RK3588_PD_PHP 32 +#define RK3588_PD_GMAC 33 +#define RK3588_PD_PCIE 34 +#define RK3588_PD_NVM 35 +#define RK3588_PD_NVM0 36 +#define RK3588_PD_SDIO 37 +#define RK3588_PD_AUDIO 38 +#define RK3588_PD_SECURE 39 +#define RK3588_PD_SDMMC 40 +#define RK3588_PD_CRYPTO 41 +#define RK3588_PD_BUS 42 + +/* VD_PMU */ +#define RK3588_PD_PMU1 43 + +#endif /* __DT_BINDINGS_POWER_RK3588_POWER_H__ */ diff --git a/components/drivers/include/dt-bindings/power/rv1126-power.h b/components/drivers/include/dt-bindings/power/rv1126-power.h new file mode 100644 index 000000000000..eda1d6d71f13 --- /dev/null +++ b/components/drivers/include/dt-bindings/power/rv1126-power.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __DT_BINDINGS_POWER_RV1126_POWER_H__ +#define __DT_BINDINGS_POWER_RV1126_POWER_H__ + +/* VD_CORE */ +#define RV1126_PD_CPU_0 0 +#define RV1126_PD_CPU_1 1 +#define RV1126_PD_CPU_2 2 +#define RV1126_PD_CPU_3 3 +#define RV1126_PD_CORE_ALIVE 4 + +/* VD_PMU */ +#define RV1126_PD_PMU 5 +#define RV1126_PD_PMU_ALIVE 6 + +/* VD_NPU */ +#define RV1126_PD_NPU 7 + +/* VD_VEPU */ +#define RV1126_PD_VEPU 8 + +/* VD_LOGIC */ +#define RV1126_PD_VI 9 +#define RV1126_PD_VO 10 +#define RV1126_PD_ISPP 11 +#define RV1126_PD_VDPU 12 +#define RV1126_PD_CRYPTO 13 +#define RV1126_PD_DDR 14 +#define RV1126_PD_NVM 15 +#define RV1126_PD_SDIO 16 +#define RV1126_PD_USB 17 +#define RV1126_PD_LOGIC_ALIVE 18 + +#endif /* __DT_BINDINGS_POWER_RK3036_POWER_H__ */ diff --git a/components/drivers/include/dt-bindings/reset/raspberrypi,firmware-reset.h b/components/drivers/include/dt-bindings/reset/raspberrypi,firmware-reset.h new file mode 100644 index 000000000000..37c61353258e --- /dev/null +++ b/components/drivers/include/dt-bindings/reset/raspberrypi,firmware-reset.h @@ -0,0 +1,13 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __DT_BINDINGS_POWER_RASPBERRYPI_FIRMWARE_RESET_H__ +#define __DT_BINDINGS_POWER_RASPBERRYPI_FIRMWARE_RESET_H__ + +#define RASPBERRYPI_FIRMWARE_RESET_ID_USB 0 +#define RASPBERRYPI_FIRMWARE_RESET_NUM_IDS 1 + +#endif /* __DT_BINDINGS_POWER_RASPBERRYPI_FIRMWARE_RESET_H__ */ diff --git a/components/drivers/include/rtdevice.h b/components/drivers/include/rtdevice.h index c5f8f8c1448a..08dd5a704214 100644 --- a/components/drivers/include/rtdevice.h +++ b/components/drivers/include/rtdevice.h @@ -168,18 +168,47 @@ extern "C" { #ifdef RT_USING_DM #include "drivers/core/bus.h" #include "drivers/core/rtdm.h" +#include "drivers/core/power.h" +#include "drivers/core/power_domain.h" #include "drivers/platform.h" +#ifdef RT_USING_BLK +#include "drivers/blk.h" +#endif + #ifdef RT_USING_CLK #include "drivers/clk.h" #endif +#ifdef RT_USING_DMA +#include "drivers/dma.h" +#endif + +#ifdef RT_USING_HWSPINLOCK +#include "drivers/hwspinlock.h" +#endif + #ifdef RT_USING_FIRMWARE +#ifdef RT_FIRMWARE_ARM_SCMI +#include "drivers/scmi.h" +#endif #ifdef RT_FIRMWARE_PSCI #include "drivers/psci.h" #endif #endif /* RT_USING_FIRMWARE */ +#ifdef RT_USING_LED +#include "drivers/led.h" +#endif + +#ifdef RT_USING_MBOX +#include "drivers/mailbox.h" +#endif + +#ifdef RT_USING_NVMEM +#include "drivers/nvmem.h" +#endif + #ifdef RT_USING_OFW #include "drivers/ofw.h" #include "drivers/ofw_fdt.h" @@ -192,6 +221,29 @@ extern "C" { #include "drivers/pic.h" #endif +#ifdef RT_USING_PCI +#include "drivers/pci.h" +#ifdef RT_PCI_MSI +#include "drivers/pci_msi.h" +#endif +#endif + +#ifdef RT_USING_REGULATOR +#include "drivers/regulator.h" +#endif + +#ifdef RT_USING_RESET +#include "drivers/reset.h" +#endif + +#ifdef RT_MFD_SYSCON +#include "drivers/syscon.h" +#endif + +#ifdef RT_USING_THERMAL +#include "drivers/thermal.h" +#endif + #ifdef RT_USING_VIRTIO #include "drivers/virtio.h" #include "drivers/virtq.h" diff --git a/components/drivers/input/Kconfig b/components/drivers/input/Kconfig new file mode 100644 index 000000000000..8a285bd54c10 --- /dev/null +++ b/components/drivers/input/Kconfig @@ -0,0 +1,10 @@ +menuconfig RT_USING_INPUT + bool "Using Input device drivers" + depends on RT_USING_DM + default n + +if RT_USING_INPUT +source "$RTT_DIR/components/drivers/input/keyboard/Kconfig" +source "$RTT_DIR/components/drivers/input/misc/Kconfig" +source "$RTT_DIR/components/drivers/input/touchscreen/Kconfig" +endif diff --git a/components/drivers/input/SConscript b/components/drivers/input/SConscript new file mode 100644 index 000000000000..46a58d3eba73 --- /dev/null +++ b/components/drivers/input/SConscript @@ -0,0 +1,23 @@ +from building import * + +group = [] +objs = [] + +if not GetDepend(['RT_USING_INPUT']): + Return('group') + +cwd = GetCurrentDir() +list = os.listdir(cwd) +CPPPATH = [cwd + '/../include'] + +src = [] + +group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) + +for d in list: + path = os.path.join(cwd, d) + if os.path.isfile(os.path.join(path, 'SConscript')): + objs = objs + SConscript(os.path.join(d, 'SConscript')) +objs = objs + group + +Return('objs') diff --git a/components/drivers/input/keyboard/Kconfig b/components/drivers/input/keyboard/Kconfig new file mode 100644 index 000000000000..30090f68443d --- /dev/null +++ b/components/drivers/input/keyboard/Kconfig @@ -0,0 +1,9 @@ +menuconfig RT_INPUT_KEYBOARD + bool "Keyboards" + default n + +config RT_INPUT_KEYBOARD_GPIO_KEYS + bool "GPIO" + depends on RT_INPUT_KEYBOARD + select RT_USING_OFW + default n diff --git a/components/drivers/input/keyboard/SConscript b/components/drivers/input/keyboard/SConscript new file mode 100644 index 000000000000..8b9935acf3d8 --- /dev/null +++ b/components/drivers/input/keyboard/SConscript @@ -0,0 +1,18 @@ +from building import * + +group = [] + +if not GetDepend(['RT_INPUT_KEYBOARD']): + Return('group') + +cwd = GetCurrentDir() +CPPPATH = [cwd + '/../../include'] + +src = [] + +if GetDepend(['RT_INPUT_KEYBOARD_GPIO_KEYS']): + src += ['gpio-keys.c'] + +group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') diff --git a/components/drivers/input/keyboard/gpio-keys.c b/components/drivers/input/keyboard/gpio-keys.c new file mode 100644 index 000000000000..d1dead52a351 --- /dev/null +++ b/components/drivers/input/keyboard/gpio-keys.c @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-3-08 GuEe-GUI the first version + */ + +#include +#include +#include + +#define DBG_TAG "input.keyboard.gpio" +#define DBG_LVL DBG_INFO +#include + +struct gpio_key +{ + rt_base_t pin; + rt_uint8_t mode; + rt_uint32_t code; +}; + +typedef void (*gpio_key_handler)(void *); + +static rt_err_t gpio_attach_event(struct gpio_key *gkey, void (*handler)(void *)) +{ + rt_err_t err; + + err = rt_pin_attach_irq(gkey->pin, gkey->mode, handler, gkey); + + if (!err) + { + rt_pin_irq_enable(gkey->pin, RT_TRUE); + } + + return err; +} + +static rt_err_t ofw_append_gpio_key(struct rt_ofw_node *np) +{ + rt_err_t err; + const char *propname; + struct gpio_key *gkey = rt_calloc(1, sizeof(*gkey)); + + gkey->pin = rt_ofw_get_named_pin(np, RT_NULL, 0, &gkey->mode, RT_NULL); + + if (gkey->pin < 0) + { + err = gkey->pin; + + goto _fail; + } + + if ((propname = rt_ofw_get_prop_fuzzy_name(np, ",code$")) && + !rt_ofw_prop_read_u32(np, propname, &gkey->code)) + { + switch (gkey->code) + { + case KEY_POWER: + err = gpio_attach_event(gkey, (gpio_key_handler)rt_hw_cpu_shutdown); + break; + + case KEY_RESTART: + err = gpio_attach_event(gkey, (gpio_key_handler)rt_hw_cpu_reset); + break; + + default: + err = -RT_EINVAL; + LOG_W("Unsupported event code = %d", gkey->code); + break; + } + } + + if (err) + { + goto _fail; + } + + rt_ofw_data(np) = gkey; + + return RT_EOK; + +_fail: + rt_free(gkey); + + return err; +} + +static rt_err_t gpio_key_probe(struct rt_platform_device *pdev) +{ + rt_err_t err = RT_EOK; + struct rt_ofw_node *key_np, *np = pdev->parent.ofw_node; + + rt_ofw_foreach_available_child_node(np, key_np) + { + rt_err_t err = ofw_append_gpio_key(key_np); + + if (err == -RT_ENOMEM) + { + rt_ofw_node_put(key_np); + + return err; + } + else if (err) + { + LOG_E("%s: create KEY fail", rt_ofw_node_full_name(key_np)); + continue; + } + } + + return err; +} + +static rt_err_t gpio_key_remove(struct rt_platform_device *pdev) +{ + struct rt_ofw_node *key_np, *np = pdev->parent.ofw_node; + + rt_ofw_foreach_available_child_node(np, key_np) + { + struct gpio_key *gkey = rt_ofw_data(key_np); + + if (!gkey) + { + continue; + } + + rt_ofw_data(key_np) = RT_NULL; + + rt_pin_irq_enable(gkey->pin, RT_FALSE); + rt_pin_detach_irq(gkey->pin); + + rt_free(gkey); + } + + return RT_EOK; +} + +static const struct rt_ofw_node_id gpio_key_ofw_ids[] = +{ + { .compatible = "gpio-keys" }, + { /* sentinel */ } +}; + +static struct rt_platform_driver gpio_key_driver = +{ + .name = "gpio-keys", + .ids = gpio_key_ofw_ids, + + .probe = gpio_key_probe, + .remove = gpio_key_remove, +}; + +static int gpio_key_drv_register(void) +{ + rt_platform_driver_register(&gpio_key_driver); + + return 0; +} +INIT_DRIVER_LATER_EXPORT(gpio_key_drv_register); diff --git a/components/drivers/input/misc/Kconfig b/components/drivers/input/misc/Kconfig new file mode 100644 index 000000000000..b4a15c68daa9 --- /dev/null +++ b/components/drivers/input/misc/Kconfig @@ -0,0 +1,8 @@ +menuconfig RT_INPUT_MISC + bool "Misc" + default n + +config RT_INPUT_MISC_RK8XX_PWRKEY + bool "Rockchip RK805/RK806/RK817 PMIC power key support" + depends on RT_INPUT_MISC + default n diff --git a/components/drivers/input/misc/SConscript b/components/drivers/input/misc/SConscript new file mode 100644 index 000000000000..0da57bcd09f9 --- /dev/null +++ b/components/drivers/input/misc/SConscript @@ -0,0 +1,18 @@ +from building import * + +group = [] + +if not GetDepend(['RT_INPUT_MISC']): + Return('group') + +cwd = GetCurrentDir() +CPPPATH = [cwd + '/../../include'] + +src = [] + +if GetDepend(['RT_INPUT_MISC_RK8XX_PWRKEY']): + src += ['rk8xx-pwrkey.c'] + +group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') diff --git a/components/drivers/input/misc/rk8xx-pwrkey.c b/components/drivers/input/misc/rk8xx-pwrkey.c new file mode 100644 index 000000000000..02dce916b510 --- /dev/null +++ b/components/drivers/input/misc/rk8xx-pwrkey.c @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-3-08 GuEe-GUI the first version + */ + +#include +#include +#include + +struct rk8xx_pwrkey +{ + int fall_irq, rise_irq; + + struct rt_thread *poweroff_task; +}; + +static void rk8xx_pwrkey_fall_isr(int irqno, void *param) +{ + /* Key down */ +} + +static void rk8xx_pwrkey_rise_isr(int irqno, void *param) +{ + struct rk8xx_pwrkey *pwr = param; + + /* Key up */ + rt_thread_resume(pwr->poweroff_task); +} + +static void rk8xx_pwrkey_power_off_task(void *param) +{ + struct rk8xx_pwrkey *pwr = param; + + rt_thread_suspend(pwr->poweroff_task); + rt_schedule(); + + rt_hw_interrupt_mask(pwr->fall_irq); + rt_hw_interrupt_mask(pwr->rise_irq); + + rt_hw_cpu_shutdown(); +} + +static rt_err_t rk8xx_pwrkey_probe(struct rt_platform_device *pdev) +{ + rt_err_t err; + struct rt_device *dev = &pdev->parent; + struct rk8xx_pwrkey *pwr = rt_calloc(1, sizeof(*pwr)); + + if (!pwr) + { + return -RT_ENOMEM; + } + + if ((pwr->fall_irq = rt_dm_dev_get_irq(dev, 0)) < 0) + { + err = pwr->fall_irq; + goto _fail; + } + + if ((pwr->rise_irq = rt_dm_dev_get_irq(dev, 1)) < 0) + { + err = pwr->rise_irq; + goto _fail; + } + + pwr->poweroff_task = rt_thread_create("pwrkey-rk8xx", &rk8xx_pwrkey_power_off_task, + pwr, SYSTEM_THREAD_STACK_SIZE, RT_THREAD_PRIORITY_MAX / 2, 10); + + if (!pwr->poweroff_task) + { + goto _fail; + } + + rt_thread_startup(pwr->poweroff_task); + + dev->user_data = pwr; + + rt_hw_interrupt_install(pwr->fall_irq, rk8xx_pwrkey_fall_isr, pwr, "pwrkey-rk8xx-fall"); + rt_hw_interrupt_umask(pwr->fall_irq); + + rt_hw_interrupt_install(pwr->rise_irq, rk8xx_pwrkey_rise_isr, pwr, "pwrkey-rk8xx-rise"); + rt_hw_interrupt_umask(pwr->rise_irq); + + return RT_EOK; + +_fail: + rt_free(pwr); + + return err; +} + +static rt_err_t rk8xx_pwrkey_remove(struct rt_platform_device *pdev) +{ + struct rk8xx_pwrkey *pwr = pdev->parent.user_data; + + rt_hw_interrupt_mask(pwr->fall_irq); + rt_pic_detach_irq(pwr->fall_irq, pwr); + + rt_hw_interrupt_mask(pwr->rise_irq); + rt_pic_detach_irq(pwr->rise_irq, pwr); + + rt_thread_delete(pwr->poweroff_task); + + rt_free(pwr); + + return RT_EOK; +} + +static struct rt_platform_driver rk8xx_pwrkey_driver = +{ + .name = "rk8xx-pwrkey", + .probe = rk8xx_pwrkey_probe, + .remove = rk8xx_pwrkey_remove, +}; +RT_PLATFORM_DRIVER_EXPORT(rk8xx_pwrkey_driver); diff --git a/components/drivers/input/touchscreen/Kconfig b/components/drivers/input/touchscreen/Kconfig new file mode 100644 index 000000000000..3eb2d5b688f2 --- /dev/null +++ b/components/drivers/input/touchscreen/Kconfig @@ -0,0 +1,14 @@ +menuconfig RT_INPUT_TOUCHSCREEN + bool "Touchscreens" + select RT_USING_TOUCH + default n + +config RT_INPUT_TOUCHSCREEN_RASPBERRYPI_FW + bool "Raspberry Pi's firmware base touch screen support" + depends on RT_INPUT_TOUCHSCREEN + select RT_USING_OFW + select RT_USING_DMA + select RT_FIRMWARE_RASPBERRYPI + select RT_USING_ADT + select RT_USING_ADT_BITMAP + default n diff --git a/components/drivers/input/touchscreen/SConscript b/components/drivers/input/touchscreen/SConscript new file mode 100644 index 000000000000..932aaf2f2c20 --- /dev/null +++ b/components/drivers/input/touchscreen/SConscript @@ -0,0 +1,18 @@ +from building import * + +group = [] + +if not GetDepend(['RT_INPUT_TOUCHSCREEN']): + Return('group') + +cwd = GetCurrentDir() +CPPPATH = [cwd + '/../../include'] + +src = [] + +if GetDepend(['RT_INPUT_TOUCHSCREEN_RASPBERRYPI_FW']): + src += ['raspberrypi-ts.c'] + +group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') diff --git a/components/drivers/input/touchscreen/raspberrypi-ts.c b/components/drivers/input/touchscreen/raspberrypi-ts.c new file mode 100644 index 000000000000..9bb6864d228a --- /dev/null +++ b/components/drivers/input/touchscreen/raspberrypi-ts.c @@ -0,0 +1,311 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-11-26 GuEe-GUI first version + */ + +#include +#include +#include + +#define DBG_TAG "input.touchscreen.rpi-ts" +#define DBG_LVL DBG_INFO +#include + +#include "../../firmware/raspberrypi/firmware.h" + +#define RPI_TS_DEFAULT_WIDTH 800 +#define RPI_TS_DEFAULT_HEIGHT 480 + +#define RPI_TS_MAX_SUPPORTED_POINTS 10 + +#define RPI_TS_FTS_TOUCH_DOWN 0 +#define RPI_TS_FTS_TOUCH_CONTACT 2 + +#define RPI_TS_POLL_INTERVAL 17 /* 60fps */ + +#define RPI_TS_NPOINTS_REG_INVALIDATE 99 + +struct rpi_ts_regs +{ + rt_uint8_t device_mode; + rt_uint8_t gesture_id; + rt_uint8_t num_points; + + struct rpi_ts_touch + { + rt_uint8_t xh; + rt_uint8_t xl; + rt_uint8_t yh; + rt_uint8_t yl; + rt_uint8_t pressure; /* Not supported */ + rt_uint8_t area; /* Not supported */ + } point[RPI_TS_MAX_SUPPORTED_POINTS]; +}; + +struct rpi_ts +{ + struct rt_touch_device parent; + + void *fw_regs; + rt_ubase_t fw_regs_phys; + + int known_ids; + struct rpi_ts_regs ts_regs; +}; + +#define raw_to_rpi_ts(raw) rt_container_of(raw, struct rpi_ts, parent) + +static rt_size_t rpi_ts_touch_readpoint(struct rt_touch_device *touch, void *buf, rt_size_t touch_num) +{ + int touchid; + int event_type; + int modified_ids = 0; + bitmap_t released_ids; + int x, y, bit, points_report = 0; + struct rt_touch_data *point = buf; + struct rpi_ts *rts = raw_to_rpi_ts(touch); + struct rpi_ts_regs *regs = &rts->ts_regs; + + rt_memcpy(regs, rts->fw_regs, sizeof(*regs)); + /* + * We poll the memory based register copy of the touchscreen chip using + * the number of points register to know whether the copy has been + * updated (we write 99 to the memory copy, the GPU will write between + * 0 - 10 points) + */ + HWREG8(rts->fw_regs + (rt_size_t)&((struct rpi_ts_regs *)0)->num_points) = + RPI_TS_NPOINTS_REG_INVALIDATE; + + if (regs->num_points == RPI_TS_NPOINTS_REG_INVALIDATE || + (regs->num_points == 0 && rts->known_ids == 0)) + { + return points_report; + } + + for (int i = 0; i < regs->num_points; ++i) + { + x = (((int)regs->point[i].xh & 0xf) << 8) + regs->point[i].xl; + y = (((int)regs->point[i].yh & 0xf) << 8) + regs->point[i].yl; + touchid = (regs->point[i].yh >> 4) & 0xf; + event_type = (regs->point[i].xh >> 6) & 0x03; + + modified_ids |= RT_BIT(touchid); + + if (event_type == RPI_TS_FTS_TOUCH_DOWN || + event_type == RPI_TS_FTS_TOUCH_CONTACT) + { + if (event_type == RPI_TS_FTS_TOUCH_DOWN) + { + point->event = RT_TOUCH_EVENT_DOWN; + } + else + { + point->event = RT_TOUCH_EVENT_MOVE; + } + + point->track_id = touchid; + point->width = 1; + point->x_coordinate = x; + point->y_coordinate = y; + point->timestamp = rt_tick_get(); + + ++point; + ++points_report; + } + } + + released_ids = rts->known_ids & ~modified_ids; + bitmap_for_each_set_bit(&released_ids, bit, RPI_TS_MAX_SUPPORTED_POINTS) + { + if (rt_unlikely(points_report >= touch_num)) + { + break; + } + + modified_ids &= ~RT_BIT(bit); + + point->event = RT_TOUCH_EVENT_UP; + point->track_id = bit; + point->width = 1; + point->x_coordinate = 0; + point->y_coordinate = 0; + point->timestamp = rt_tick_get(); + + ++point; + ++points_report; + } + + rts->known_ids = modified_ids; + + return points_report; +} + +static rt_err_t rpi_ts_touch_control(struct rt_touch_device *touch, int cmd, void *arg) +{ + rt_err_t err = RT_EOK; + struct rpi_ts *rts = raw_to_rpi_ts(touch); + + if (!arg) + { + return -RT_EINVAL; + } + + switch (cmd) + { + case RT_TOUCH_CTRL_GET_ID: + *(int *)arg = 0; + break; + + case RT_TOUCH_CTRL_GET_INFO: + rt_memcpy(arg, &rts->parent.info, sizeof(rts->parent.info)); + break; + + default: + err = -RT_ENOSYS; + break; + } + + return err; +} + +const static struct rt_touch_ops rpi_ts_ops = +{ + .touch_readpoint = rpi_ts_touch_readpoint, + .touch_control = rpi_ts_touch_control, +}; + +static rt_err_t rpi_ts_probe(struct rt_platform_device *pdev) +{ + rt_err_t err = RT_EOK; + const char *dev_name; + rt_uint32_t touchbuf, tsx, tsy; + struct rpi_firmware *rpi_fw = RT_NULL; + struct rt_device *dev = &pdev->parent; + struct rt_ofw_node *np = dev->ofw_node, *fw_np; + struct rpi_ts *rts = rt_calloc(1, sizeof(*rts)); + + if (!rts) + { + return -RT_ENOMEM; + } + + fw_np = rt_ofw_parse_phandle(np, "firmware", 0); + + if (!fw_np) + { + err = -RT_EINVAL; + goto _fail; + } + + rpi_fw = rpi_firmware_get(fw_np); + rt_ofw_node_put(fw_np); + + if (!rpi_fw) + { + err = -RT_EINVAL; + goto _fail; + } + + rts->fw_regs = rt_dma_alloc_coherent(dev, ARCH_PAGE_SIZE, &rts->fw_regs_phys); + + if (!rts->fw_regs) + { + LOG_E("Failed to dma buffer"); + + goto _fail; + } + + if (rts->fw_regs_phys > RT_UINT32_MAX) + { + LOG_E("64 bits dma buffer is not support"); + + goto _fail; + } + + touchbuf = (rt_uint32_t)rts->fw_regs_phys; + + if ((err = rpi_firmware_property(rpi_fw, RPI_FIRMWARE_FRAMEBUFFER_SET_TOUCHBUF, + &touchbuf, sizeof(touchbuf)))) + { + LOG_E("Set touchbuf from firmware error = %s", rt_strerror(err)); + + goto _fail; + } + + if (rt_ofw_prop_read_u32(np, "touchscreen-size-x", &tsx)) + { + tsx = RPI_TS_DEFAULT_WIDTH; + } + + if (rt_ofw_prop_read_u32(np, "touchscreen-size-y", &tsy)) + { + tsy = RPI_TS_DEFAULT_HEIGHT; + } + + rt_dm_dev_bind_fwdata(dev, RT_NULL, &rts->parent); + dev->user_data = rts; + + rts->parent.info.type = RT_TOUCH_TYPE_CAPACITANCE; + rts->parent.info.vendor = RT_TOUCH_VENDOR_UNKNOWN; + rts->parent.info.point_num = RPI_TS_MAX_SUPPORTED_POINTS; + rts->parent.info.range_x = tsx; + rts->parent.info.range_y = tsy; + rts->parent.ops = &rpi_ts_ops; + + rt_dm_dev_set_name_auto(&rts->parent.parent, "touch"); + dev_name = rt_dm_dev_get_name(&rts->parent.parent); + + rt_hw_touch_register(&rts->parent, dev_name, 0, rts); + + return RT_EOK; + +_fail: + if (rts->fw_regs) + { + rt_dma_free_coherent(dev, ARCH_PAGE_SIZE, rts->fw_regs, rts->fw_regs_phys); + } + + if (rpi_fw) + { + rpi_firmware_put(rpi_fw); + } + + rt_free(rts); + + return err; +} + +static rt_err_t rpi_ts_remove(struct rt_platform_device *pdev) +{ + struct rt_device *dev = &pdev->parent; + struct rpi_ts *rts = dev->user_data; + + rt_dma_free_coherent(dev, ARCH_PAGE_SIZE, rts->fw_regs, rts->fw_regs_phys); + + rt_device_unregister(&rts->parent.parent); + + rt_free(rts); + + return RT_EOK; +} + +static const struct rt_ofw_node_id rpi_ts_ofw_ids[] = +{ + { .compatible = "raspberrypi,firmware-ts" }, + { /* sentinel */ } +}; + +static struct rt_platform_driver rpi_ts_driver = +{ + .name = "touchscreen-rpi", + .ids = rpi_ts_ofw_ids, + + .probe = rpi_ts_probe, + .remove = rpi_ts_remove, +}; +RT_PLATFORM_DRIVER_EXPORT(rpi_ts_driver); diff --git a/components/drivers/led/Kconfig b/components/drivers/led/Kconfig new file mode 100644 index 000000000000..6eb155f32dd8 --- /dev/null +++ b/components/drivers/led/Kconfig @@ -0,0 +1,24 @@ +menuconfig RT_USING_LED + bool "Using Light Emitting Diode (LED) device drivers" + depends on RT_USING_DM + default n + +config RT_LED_GPIO + bool "GPIO connected LEDs Support" + depends on RT_USING_LED + depends on RT_USING_PINCTRL + depends on RT_USING_OFW + default n + +config RT_LED_PWM + bool "PWM driven LEDs Support" + depends on RT_USING_LED + depends on RT_USING_PWM + depends on RT_USING_OFW + default n + +config RT_LED_SYSCON + bool "System controllers connected LEDs Support" + depends on RT_USING_LED + depends on RT_MFD_SYSCON + default n diff --git a/components/drivers/led/SConscript b/components/drivers/led/SConscript new file mode 100644 index 000000000000..ab55d23c64f0 --- /dev/null +++ b/components/drivers/led/SConscript @@ -0,0 +1,24 @@ +from building import * + +group = [] + +if not GetDepend(['RT_USING_LED']): + Return('group') + +cwd = GetCurrentDir() +CPPPATH = [cwd + '/../include'] + +src = ['led.c'] + +if GetDepend(['RT_LED_GPIO']): + src += ['led-gpio.c'] + +if GetDepend(['RT_LED_PWM']): + src += ['led-pwm.c'] + +if GetDepend(['RT_LED_SYSCON']): + src += ['led-syscon.c'] + +group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') diff --git a/components/drivers/led/led-gpio.c b/components/drivers/led/led-gpio.c new file mode 100644 index 000000000000..eb6784beb24d --- /dev/null +++ b/components/drivers/led/led-gpio.c @@ -0,0 +1,237 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-3-08 GuEe-GUI the first version + */ + +#include +#include + +#define DBG_TAG "led.gpio" +#define DBG_LVL DBG_INFO +#include + +struct gpio_led +{ + struct rt_led_device parent; + + rt_base_t pin; + rt_uint8_t active_val; +}; + +#define raw_to_gpio_led(raw) rt_container_of(raw, struct gpio_led, parent); + +static rt_err_t gpio_led_set_state(struct rt_led_device *led, enum rt_led_state state) +{ + rt_err_t err = RT_EOK; + struct gpio_led *gled = raw_to_gpio_led(led); + + rt_pin_mode(gled->pin, PIN_MODE_OUTPUT); + + switch (state) + { + case RT_LED_S_OFF: + rt_pin_write(gled->pin, !gled->active_val); + break; + + case RT_LED_S_ON: + rt_pin_write(gled->pin, gled->active_val); + break; + + case RT_LED_S_TOGGLE: + err = led->ops->get_state(led, &state); + + if (!err) + { + err = led->ops->set_state(led, state == RT_LED_S_OFF ? RT_LED_S_ON : RT_LED_S_OFF); + } + break; + + default: + return -RT_ENOSYS; + } + + return RT_EOK; +} + +static rt_err_t gpio_led_get_state(struct rt_led_device *led, enum rt_led_state *out_state) +{ + struct gpio_led *gled = raw_to_gpio_led(led); + + switch (rt_pin_read(gled->pin)) + { + case PIN_LOW: + *out_state = RT_LED_S_OFF; + break; + + case PIN_HIGH: + *out_state = RT_LED_S_ON; + break; + + default: + return -RT_ERROR; + } + + return RT_EOK; +} + +const static struct rt_led_ops gpio_led_ops = +{ + .set_state = gpio_led_set_state, + .get_state = gpio_led_get_state, +}; + +static rt_err_t ofw_append_gpio_led(struct rt_ofw_node *np) +{ + rt_err_t err; + enum rt_led_state led_state = RT_LED_S_OFF; + const char *propname, *state, *trigger; + struct gpio_led *gled = rt_malloc(sizeof(*gled)); + + if (!gled) + { + return -RT_ENOMEM; + } + + gled->pin = rt_ofw_get_named_pin(np, RT_NULL, 0, RT_NULL, &gled->active_val); + + if (gled->pin < 0) + { + err = gled->pin; + + goto _fail; + } + + gled->parent.ops = &gpio_led_ops; + + rt_dm_dev_set_name_auto(&gled->parent.parent, "led"); + + if ((err = rt_hw_led_register(&gled->parent))) + { + goto _fail; + } + + if (!rt_ofw_prop_read_string(np, "default-state", &state)) + { + if (!rt_strcmp(state, "on")) + { + led_state = RT_LED_S_ON; + } + } + + if ((propname = rt_ofw_get_prop_fuzzy_name(np, "default-trigger$"))) + { + if (!rt_ofw_prop_read_string(np, propname, &trigger)) + { + if (!rt_strcmp(trigger, "heartbeat") || + !rt_strcmp(trigger, "timer")) + { + led_state = RT_LED_S_BLINK; + } + } + } + + rt_led_set_state(&gled->parent, led_state); + + rt_ofw_data(np) = &gled->parent; + + return RT_EOK; + +_fail: + rt_free(gled); + + return err; +} + +static rt_err_t gpio_led_probe(struct rt_platform_device *pdev) +{ + rt_bool_t pinctrl_apply = RT_FALSE; + struct rt_ofw_node *led_np, *np = pdev->parent.ofw_node; + + if (rt_ofw_prop_read_bool(np, "pinctrl-0")) + { + pinctrl_apply = RT_TRUE; + rt_pin_ctrl_confs_apply_by_name(&pdev->parent, RT_NULL); + } + + rt_ofw_foreach_available_child_node(np, led_np) + { + rt_err_t err = ofw_append_gpio_led(led_np); + + if (err == -RT_ENOMEM) + { + rt_ofw_node_put(led_np); + + return err; + } + else if (err) + { + LOG_E("%s: create LED fail", rt_ofw_node_full_name(led_np)); + continue; + } + + if (!pinctrl_apply) + { + struct rt_device dev_tmp; + + dev_tmp.ofw_node = led_np; + rt_pin_ctrl_confs_apply_by_name(&dev_tmp, RT_NULL); + } + } + + return RT_EOK; +} + +static rt_err_t gpio_led_remove(struct rt_platform_device *pdev) +{ + struct gpio_led *gled; + struct rt_led_device *led_dev; + struct rt_ofw_node *led_np, *np = pdev->parent.ofw_node; + + rt_ofw_foreach_available_child_node(np, led_np) + { + led_dev = rt_ofw_data(led_np); + + if (!led_dev) + { + continue; + } + + gled = rt_container_of(led_dev, struct gpio_led, parent); + + rt_ofw_data(led_np) = RT_NULL; + + rt_hw_led_unregister(&gled->parent); + + rt_free(gled); + } + + return RT_EOK; +} + +static const struct rt_ofw_node_id gpio_led_ofw_ids[] = +{ + { .compatible = "gpio-leds" }, + { /* sentinel */ } +}; + +static struct rt_platform_driver gpio_led_driver = +{ + .name = "led-gpio", + .ids = gpio_led_ofw_ids, + + .probe = gpio_led_probe, + .remove = gpio_led_remove, +}; + +static int gpio_led_drv_register(void) +{ + rt_platform_driver_register(&gpio_led_driver); + + return 0; +} +INIT_DRIVER_LATER_EXPORT(gpio_led_drv_register); diff --git a/components/drivers/led/led-pwm.c b/components/drivers/led/led-pwm.c new file mode 100644 index 000000000000..edc68edc263a --- /dev/null +++ b/components/drivers/led/led-pwm.c @@ -0,0 +1,301 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-3-08 GuEe-GUI the first version + */ + +#include +#include + +#define DBG_TAG "led.pwm" +#define DBG_LVL DBG_INFO +#include + +struct pwm_led +{ + struct rt_led_device parent; + + struct rt_device_pwm *pwm_dev; + struct rt_pwm_configuration pwm_conf; + + rt_uint32_t max_brightness; + rt_uint32_t brightness; + rt_bool_t active_low; + rt_bool_t enabled; +}; + +#define raw_to_pwm_led(raw) rt_container_of(raw, struct pwm_led, parent); + +static rt_err_t pwm_led_set_state(struct rt_led_device *led, enum rt_led_state state) +{ + rt_err_t err = RT_EOK; + struct rt_pwm_configuration pwm_conf = {}; + struct pwm_led *pled = raw_to_pwm_led(led); + + switch (state) + { + case RT_LED_S_OFF: + if ((err = rt_pwm_set(pled->pwm_dev, + pled->pwm_conf.period, pled->pwm_conf.channel, 0))) + { + break; + } + + if (!(err = rt_pwm_disable(pled->pwm_dev, pled->pwm_conf.channel))) + { + pled->enabled = RT_FALSE; + } + break; + + case RT_LED_S_ON: + pwm_conf.channel = pled->pwm_conf.channel; + pwm_conf.period = pled->pwm_conf.period; + pwm_conf.pulse = pled->pwm_conf.pulse; + pwm_conf.complementary = pled->active_low; + + if ((err = rt_device_control(&pled->pwm_dev->parent, PWM_CMD_SET, &pwm_conf))) + { + break; + } + + if (!(err = rt_pwm_enable(pled->pwm_dev, pled->pwm_conf.channel))) + { + pled->enabled = RT_TRUE; + } + break; + + case RT_LED_S_TOGGLE: + err = led->ops->get_state(led, &state); + + if (!err) + { + err = led->ops->set_state(led, state == RT_LED_S_OFF ? RT_LED_S_ON : RT_LED_S_OFF); + } + break; + + default: + return -RT_ENOSYS; + } + + return RT_EOK; +} + +static rt_err_t pwm_led_get_state(struct rt_led_device *led, enum rt_led_state *out_state) +{ + struct pwm_led *pled = raw_to_pwm_led(led); + + *out_state = pled->enabled ? RT_LED_S_ON : RT_LED_S_OFF; + + return RT_EOK; +} + +static rt_err_t pwm_led_set_brightness(struct rt_led_device *led, rt_uint32_t brightness) +{ + struct pwm_led *pled = raw_to_pwm_led(led); + rt_uint64_t duty = pled->pwm_conf.period; + + duty *= brightness; + rt_do_div(duty, pled->max_brightness); + + if (pled->active_low) + { + duty = pled->pwm_conf.period - duty; + } + + pled->pwm_conf.pulse = rt_pwm_conf_pulse(&pled->pwm_conf, duty); + + return rt_pwm_set(pled->pwm_dev, + pled->pwm_conf.channel, pled->pwm_conf.period, pled->pwm_conf.pulse); +} + +const static struct rt_led_ops pwm_led_ops = +{ + .set_state = pwm_led_set_state, + .get_state = pwm_led_get_state, + .set_brightness = pwm_led_set_brightness, +}; + +static rt_err_t ofw_append_pwm_led(struct rt_ofw_node *np) +{ + rt_err_t err; + rt_bool_t need_set_brightness = RT_TRUE; + enum rt_led_state led_state = RT_LED_S_OFF; + const char *propname, *state, *trigger; + struct rt_ofw_cell_args pwm_args; + struct pwm_led *pled = rt_malloc(sizeof(*pled)); + + if (!pled) + { + return -RT_ENOMEM; + } + + pled->active_low = rt_ofw_prop_read_bool(np, "active-low"); + + if (rt_ofw_prop_read_u32(np, "max-brightness", &pled->max_brightness)) + { + goto _fail; + } + + if (rt_ofw_parse_phandle_cells(np, "pwms", "#pwm-cells", 0, &pwm_args)) + { + goto _fail; + } + + pled->pwm_dev = rt_ofw_data(pwm_args.data); + rt_ofw_node_put(pwm_args.data); + + if (!pled->pwm_dev) + { + goto _fail; + } + + pled->pwm_conf.channel = pwm_args.args[0]; + pled->pwm_conf.period = pwm_args.args[1]; + + pled->parent.ops = &pwm_led_ops; + + rt_dm_dev_set_name_auto(&pled->parent.parent, "led"); + + if ((err = rt_hw_led_register(&pled->parent))) + { + goto _fail; + } + + if (!rt_ofw_prop_read_string(np, "default-state", &state)) + { + rt_pwm_get(pled->pwm_dev, &pled->pwm_conf); + + if (!rt_strcmp(state, "on")) + { + led_state = RT_LED_S_ON; + pled->brightness = pled->max_brightness; + } + else if (!rt_strcmp(state, "keep")) + { + if (pled->pwm_conf.period) + { + rt_uint64_t brightness; + + brightness = pled->max_brightness; + brightness *= rt_pwm_conf_duty_cycle(&pled->pwm_conf); + rt_do_div(brightness, pled->pwm_conf.period); + + pled->brightness = brightness; + + need_set_brightness = RT_FALSE; + } + else + { + goto _out_state_check; + } + } + + pled->pwm_conf.period = pwm_args.args[1]; + } + +_out_state_check: + if ((propname = rt_ofw_get_prop_fuzzy_name(np, "default-trigger$"))) + { + if (!rt_ofw_prop_read_string(np, propname, &trigger)) + { + if (!rt_strcmp(trigger, "heartbeat") || + !rt_strcmp(trigger, "timer")) + { + led_state = RT_LED_S_BLINK; + } + } + } + + rt_led_set_state(&pled->parent, led_state); + + if (need_set_brightness) + { + pwm_led_set_brightness(&pled->parent, pled->brightness); + } + + rt_ofw_data(np) = &pled->parent; + + return RT_EOK; + +_fail: + rt_free(pled); + + return err; +} + +static rt_err_t pwm_led_probe(struct rt_platform_device *pdev) +{ + struct rt_ofw_node *led_np, *np = pdev->parent.ofw_node; + + rt_ofw_foreach_available_child_node(np, led_np) + { + rt_err_t err = ofw_append_pwm_led(led_np); + + if (err == -RT_ENOMEM) + { + rt_ofw_node_put(led_np); + + return err; + } + else if (err) + { + LOG_E("%s: create LED fail", rt_ofw_node_full_name(led_np)); + } + } + + return RT_EOK; +} + +static rt_err_t pwm_led_remove(struct rt_platform_device *pdev) +{ + struct pwm_led *pled; + struct rt_led_device *led_dev; + struct rt_ofw_node *led_np, *np = pdev->parent.ofw_node; + + rt_ofw_foreach_available_child_node(np, led_np) + { + led_dev = rt_ofw_data(led_np); + + if (!led_dev) + { + continue; + } + + pled = rt_container_of(led_dev, struct pwm_led, parent); + + rt_ofw_data(led_np) = RT_NULL; + + rt_hw_led_unregister(&pled->parent); + + rt_free(pled); + } + + return RT_EOK; +} + +static const struct rt_ofw_node_id pwm_led_ofw_ids[] = +{ + { .compatible = "pwm-leds" }, + { /* sentinel */ } +}; + +static struct rt_platform_driver pwm_led_driver = +{ + .name = "led-pwm", + .ids = pwm_led_ofw_ids, + + .probe = pwm_led_probe, + .remove = pwm_led_remove, +}; + +static int pwm_led_drv_register(void) +{ + rt_platform_driver_register(&pwm_led_driver); + + return 0; +} +INIT_DRIVER_LATER_EXPORT(pwm_led_drv_register); diff --git a/components/drivers/led/led-syscon.c b/components/drivers/led/led-syscon.c new file mode 100644 index 000000000000..d7af414c6198 --- /dev/null +++ b/components/drivers/led/led-syscon.c @@ -0,0 +1,198 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-3-08 GuEe-GUI the first version + */ + +#include +#include + +#define DBG_TAG "led.syscon" +#define DBG_LVL DBG_INFO +#include + +struct syscon_led +{ + struct rt_led_device parent; + + struct rt_syscon *map; + rt_uint32_t offset; + rt_uint32_t mask; +}; + +#define raw_to_syscon_led(raw) rt_container_of(raw, struct syscon_led, parent); + +static rt_err_t syscon_led_set_state(struct rt_led_device *led, enum rt_led_state state) +{ + rt_err_t err = RT_EOK; + struct syscon_led *sled = raw_to_syscon_led(led); + + switch (state) + { + case RT_LED_S_OFF: + err = rt_syscon_update_bits(sled->map, sled->offset, sled->mask, 0); + break; + + case RT_LED_S_ON: + err = rt_syscon_update_bits(sled->map, sled->offset, sled->mask, sled->mask); + break; + + case RT_LED_S_TOGGLE: + err = led->ops->get_state(led, &state); + + if (!err) + { + err = led->ops->set_state(led, state == RT_LED_S_OFF ? RT_LED_S_ON : RT_LED_S_OFF); + } + break; + + default: + return -RT_ENOSYS; + } + + return RT_EOK; +} + +static rt_err_t syscon_led_get_state(struct rt_led_device *led, enum rt_led_state *out_state) +{ + rt_err_t err; + rt_uint32_t val; + struct syscon_led *sled = raw_to_syscon_led(led); + + if (!(err = rt_syscon_read(sled->map, sled->offset, &val))) + { + if ((val & ~sled->mask)) + { + *out_state = RT_LED_S_ON; + } + else + { + *out_state = RT_LED_S_OFF; + } + } + + return err; +} + +const static struct rt_led_ops syscon_led_ops = +{ + .set_state = syscon_led_set_state, + .get_state = syscon_led_get_state, +}; + +static rt_err_t syscon_led_probe(struct rt_platform_device *pdev) +{ + rt_err_t err; + const char *propname, *state, *trigger; + enum rt_led_state led_state = RT_LED_S_OFF; + struct rt_ofw_node *np = pdev->parent.ofw_node, *pnp; + struct syscon_led *sled = rt_calloc(1, sizeof(*sled)); + + if (!sled) + { + return -RT_ENOMEM; + } + + if (!(pnp = rt_ofw_get_parent(np))) + { + goto _fail; + } + + if (rt_ofw_prop_read_u32(np, "offset", &sled->offset)) + { + goto _fail; + } + + if (rt_ofw_prop_read_u32(np, "mask", &sled->mask)) + { + goto _fail; + } + + sled->map = rt_syscon_find_by_ofw_node(pnp); + rt_ofw_node_put(pnp); + + if (!sled->map) + { + goto _fail; + } + + pdev->parent.user_data = sled; + + sled->parent.ops = &syscon_led_ops; + + rt_dm_dev_set_name_auto(&sled->parent.parent, "led"); + + if ((err = rt_hw_led_register(&sled->parent))) + { + goto _fail; + } + + if (!rt_ofw_prop_read_string(np, "default-state", &state)) + { + if (!rt_strcmp(state, "on")) + { + led_state = RT_LED_S_ON; + } + } + + if ((propname = rt_ofw_get_prop_fuzzy_name(np, "default-trigger$"))) + { + if (!rt_ofw_prop_read_string(np, propname, &trigger)) + { + if (!rt_strcmp(trigger, "heartbeat") || + !rt_strcmp(trigger, "timer")) + { + led_state = RT_LED_S_BLINK; + } + } + } + + rt_led_set_state(&sled->parent, led_state); + + return RT_EOK; + +_fail: + rt_free(sled); + + rt_ofw_node_put(pnp); + + return err; +} + +static rt_err_t syscon_led_remove(struct rt_platform_device *pdev) +{ + struct syscon_led *sled = pdev->parent.user_data; + + rt_hw_led_unregister(&sled->parent); + + rt_free(sled); + + return RT_EOK; +} + +static const struct rt_ofw_node_id syscon_led_ofw_ids[] = +{ + { .compatible = "register-bit-led" }, + { /* sentinel */ } +}; + +static struct rt_platform_driver syscon_led_driver = +{ + .name = "leds-syscon", + .ids = syscon_led_ofw_ids, + + .probe = syscon_led_probe, + .remove = syscon_led_remove, +}; + +static int syscon_led_drv_register(void) +{ + rt_platform_driver_register(&syscon_led_driver); + + return 0; +} +INIT_DRIVER_LATER_EXPORT(syscon_led_drv_register); diff --git a/components/drivers/led/led.c b/components/drivers/led/led.c new file mode 100644 index 000000000000..bc2567cf410a --- /dev/null +++ b/components/drivers/led/led.c @@ -0,0 +1,302 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-3-08 GuEe-GUI the first version + */ + +#include + +#define DBG_TAG "rtdm.led" +#define DBG_LVL DBG_INFO +#include + +#include +#include +#include + +struct blink_timer +{ + rt_bool_t toggle; + rt_bool_t enabled; + struct rt_timer timer; +}; + +static const char * const _led_states[] = +{ + [RT_LED_S_OFF] = "off", + [RT_LED_S_ON] = "on", + [RT_LED_S_TOGGLE] = "toggle", + [RT_LED_S_BLINK] = "blink", +}; + +static rt_ssize_t _led_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size) +{ + rt_err_t err; + enum rt_led_state state; + struct rt_led_device *led = rt_container_of(dev, struct rt_led_device, parent); + + err = rt_led_get_state(led, &state); + + if (!err) + { + LOG_D("%s state = %d (%s)", rt_dm_dev_get_name(&led->parent), + state, _led_states[state]); + + ((char *)buffer)[0] = '0' + state; + } + + return err ? : (pos == 0); +} + +static rt_ssize_t _led_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size) +{ + rt_uint8_t state = ((char *)buffer)[0]; + struct rt_led_device *led = rt_container_of(dev, struct rt_led_device, parent); + + if (state - '0' < RT_ARRAY_SIZE(_led_states)) + { + rt_led_set_state(led, (enum rt_led_state)(state - '0')); + + LOG_D("Switch %s to state = %c (%s)", rt_dm_dev_get_name(&led->parent), + state, _led_states[state - '0']); + } + else if (state == '#') + { + rt_uint32_t brightness = atoi(&(((char *)buffer)[1])); + + rt_led_set_brightness(led, brightness); + } + else + { + LOG_D("Invalid state = %c, (0:%s 1:%s 2:%s 3:%s)", state, + _led_states[0], _led_states[1], _led_states[2], _led_states[3]); + } + + return size; +} + +#ifdef RT_USING_DEVICE_OPS +const static struct rt_device_ops _led_ops = +{ + .read = _led_read, + .write = _led_write, +}; +#endif + +static void _led_blink_timerout(void *param) +{ + struct rt_led_device *led = param; + struct blink_timer *btimer = led->sysdata; + + if (btimer->toggle) + { + led->ops->set_state(led, RT_LED_S_OFF); + } + else + { + led->ops->set_state(led, RT_LED_S_ON); + } + + btimer->toggle = !btimer->toggle; +} + +rt_err_t rt_hw_led_register(struct rt_led_device *led) +{ + const char *dev_name; + + if (!led || !led->ops) + { + return -RT_EINVAL; + } + + led->parent.type = RT_Device_Class_Char; +#ifdef RT_USING_DEVICE_OPS + led->parent.ops = &_led_ops; +#else + led->parent.read = _led_read; + led->parent.write = _led_write; +#endif + + dev_name = rt_dm_dev_get_name(&led->parent); + + led->sysdata = RT_NULL; + rt_spin_lock_init(&led->spinlock); + + if (!led->ops->set_period && led->ops->set_state) + { + struct blink_timer *btimer = rt_malloc(sizeof(*btimer)); + + if (!btimer) + { + LOG_E("%s create blink timer failed", dev_name); + + return -RT_ENOMEM; + } + + led->sysdata = btimer; + + btimer->toggle = RT_FALSE; + btimer->enabled = RT_FALSE; + rt_timer_init(&btimer->timer, dev_name, _led_blink_timerout, led, + rt_tick_from_millisecond(500), RT_TIMER_FLAG_PERIODIC); + } + + return rt_device_register(&led->parent, dev_name, RT_DEVICE_FLAG_RDWR); +} + +rt_err_t rt_hw_led_unregister(struct rt_led_device *led) +{ + if (!led) + { + return -RT_EINVAL; + } + + rt_led_set_state(led, RT_LED_S_OFF); + + if (led->sysdata) + { + struct blink_timer *btimer = led->sysdata; + + rt_timer_detach(&btimer->timer); + + rt_free(btimer); + } + + rt_device_unregister(&led->parent); + + return RT_EOK; +} + +rt_err_t rt_led_set_state(struct rt_led_device *led, enum rt_led_state state) +{ + rt_err_t err; + struct blink_timer *btimer; + + if (!led) + { + return -RT_EINVAL; + } + + if (!led->ops->set_state) + { + return -RT_ENOSYS; + } + + rt_spin_lock(&led->spinlock); + + btimer = led->sysdata; + + if (btimer && btimer->enabled) + { + rt_timer_stop(&btimer->timer); + } + + err = led->ops->set_state(led, state); + + if (state == RT_LED_S_BLINK) + { + if (err == -RT_ENOSYS && btimer && !btimer->enabled) + { + btimer->enabled = RT_TRUE; + rt_timer_start(&btimer->timer); + } + } + else if (btimer && btimer->enabled) + { + if (err) + { + rt_timer_start(&btimer->timer); + } + else + { + btimer->enabled = RT_FALSE; + } + } + + rt_spin_unlock(&led->spinlock); + + return err; +} + +rt_err_t rt_led_get_state(struct rt_led_device *led, enum rt_led_state *out_state) +{ + rt_err_t err; + + if (!led || !out_state) + { + return -RT_EINVAL; + } + + if (!led->ops->get_state) + { + return -RT_ENOSYS; + } + + rt_spin_lock(&led->spinlock); + + err = led->ops->get_state(led, out_state); + + rt_spin_unlock(&led->spinlock); + + return err; +} + +rt_err_t rt_led_set_period(struct rt_led_device *led, rt_uint32_t period_ms) +{ + rt_err_t err; + + if (!led) + { + return -RT_EINVAL; + } + + if (!led->ops->set_period && !led->sysdata) + { + return -RT_ENOSYS; + } + + rt_spin_lock(&led->spinlock); + + if (led->ops->set_period) + { + err = led->ops->set_period(led, period_ms); + } + else + { + struct blink_timer *btimer = led->sysdata; + rt_tick_t tick = rt_tick_from_millisecond(period_ms); + + err = rt_timer_control(&btimer->timer, RT_TIMER_CTRL_SET_TIME, &tick); + } + + rt_spin_unlock(&led->spinlock); + + return err; +} + +rt_err_t rt_led_set_brightness(struct rt_led_device *led, rt_uint32_t brightness) +{ + rt_err_t err; + + if (!led) + { + return -RT_EINVAL; + } + + if (!led->ops->set_brightness) + { + return -RT_ENOSYS; + } + + rt_spin_lock(&led->spinlock); + + err = led->ops->set_brightness(led, brightness); + + rt_spin_unlock(&led->spinlock); + + return err; +} diff --git a/components/drivers/mailbox/Kconfig b/components/drivers/mailbox/Kconfig new file mode 100644 index 000000000000..9f458cd22323 --- /dev/null +++ b/components/drivers/mailbox/Kconfig @@ -0,0 +1,15 @@ +menuconfig RT_USING_MBOX + bool "Using Hardware Mailbox device drivers" + depends on RT_USING_DM + select RT_USING_OFW + default n + +config RT_MBOX_BCM2835 + bool "BCM2835 Mailbox" + depends on RT_USING_MBOX + default n + +config RT_MBOX_ROCKCHIP + bool "Rockchip Soc Integrated Mailbox Support" + depends on RT_USING_MBOX + default n diff --git a/components/drivers/mailbox/SConscript b/components/drivers/mailbox/SConscript new file mode 100755 index 000000000000..89ddb7d511ff --- /dev/null +++ b/components/drivers/mailbox/SConscript @@ -0,0 +1,21 @@ +from building import * + +group = [] + +if not GetDepend(['RT_USING_MBOX']): + Return('group') + +cwd = GetCurrentDir() +CPPPATH = [cwd + '/../include'] + +src = ['mailbox.c'] + +if GetDepend(['RT_MBOX_BCM2835']): + src += ['mailbox-bcm2835.c'] + +if GetDepend(['RT_MBOX_ROCKCHIP']): + src += ['mailbox-rockchip.c'] + +group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') diff --git a/components/drivers/mailbox/mailbox-bcm2835.c b/components/drivers/mailbox/mailbox-bcm2835.c new file mode 100644 index 000000000000..952e50ce98c8 --- /dev/null +++ b/components/drivers/mailbox/mailbox-bcm2835.c @@ -0,0 +1,215 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-09-23 GuEe-GUI first version + */ + +#include +#include + +/* Mailboxes */ +#define ARM_0_MAIL0 0x00 +#define ARM_0_MAIL1 0x20 + +/* + * Mailbox registers. We basically only support mailbox 0 & 1. We + * deliver to the VC in mailbox 1, it delivers to us in mailbox 0. See + * BCM2835-ARM-Peripherals.pdf section 1.3 for an explanation about + * the placement of memory barriers. + */ +#define MAIL0_RD (ARM_0_MAIL0 + 0x00) +#define MAIL0_POL (ARM_0_MAIL0 + 0x10) +#define MAIL0_STA (ARM_0_MAIL0 + 0x18) +#define MAIL0_CNF (ARM_0_MAIL0 + 0x1C) +#define MAIL1_WRT (ARM_0_MAIL1 + 0x00) +#define MAIL1_STA (ARM_0_MAIL1 + 0x18) + +/* Status register: FIFO state. */ +#define ARM_MS_FULL RT_BIT(31) +#define ARM_MS_EMPTY RT_BIT(30) + +/* Configuration register: Enable interrupts. */ +#define ARM_MC_IHAVEDATAIRQEN RT_BIT(0) + +struct bcm2835_mbox +{ + struct rt_mbox_controller parent; + + int irq; + void *regs; + struct rt_spinlock lock; +}; + +#define raw_to_bcm2835_mbox(raw) rt_container_of(raw, struct bcm2835_mbox, parent) + +rt_inline rt_uint32_t bcm2835_mbox_readl(struct bcm2835_mbox *bcm_mbox, int offset) +{ + return HWREG32(bcm_mbox->regs + offset); +} + +rt_inline void bcm2835_mbox_writel(struct bcm2835_mbox *bcm_mbox, int offset, + rt_uint32_t value) +{ + HWREG32(bcm_mbox->regs + offset) = value; +} + +static rt_err_t bcm2835_mbox_request(struct rt_mbox_chan *chan) +{ + struct bcm2835_mbox *bcm_mbox = raw_to_bcm2835_mbox(chan->ctrl); + + /* Enable the interrupt on data reception */ + bcm2835_mbox_writel(bcm_mbox, MAIL0_CNF, ARM_MC_IHAVEDATAIRQEN); + + return RT_EOK; +} + +static void bcm2835_mbox_free(struct rt_mbox_chan *chan) +{ + struct bcm2835_mbox *bcm_mbox = raw_to_bcm2835_mbox(chan->ctrl); + + bcm2835_mbox_writel(bcm_mbox, MAIL0_CNF, 0); +} + +static rt_err_t bcm2835_mbox_send(struct rt_mbox_chan *chan, const void *data) +{ + rt_uint32_t msg = *(rt_uint32_t *)data; + struct bcm2835_mbox *bcm_mbox = raw_to_bcm2835_mbox(chan->ctrl); + + rt_spin_lock(&bcm_mbox->lock); + + bcm2835_mbox_writel(bcm_mbox, MAIL1_WRT, msg); + + rt_spin_unlock(&bcm_mbox->lock); + + return RT_EOK; +} + +static int bcm2835_mbox_ofw_parse(struct rt_mbox_controller *ctrl, + struct rt_ofw_cell_args *args) +{ + if (args->args_count != 0) + { + return -RT_EINVAL; + } + + return 0; +} + +static const struct rt_mbox_controller_ops bcm2835_mbox_ops = +{ + .request = bcm2835_mbox_request, + .free = bcm2835_mbox_free, + .send = bcm2835_mbox_send, + .ofw_parse = bcm2835_mbox_ofw_parse, +}; + +static void bcm2835_mbox_isr(int irqno, void *param) +{ + struct bcm2835_mbox *bcm_mbox = param; + + while (!(bcm2835_mbox_readl(bcm_mbox, MAIL0_STA) & ARM_MS_EMPTY)) + { + rt_uint32_t msg = bcm2835_mbox_readl(bcm_mbox, MAIL0_RD); + + rt_mbox_recv(&bcm_mbox->parent.chans[0], &msg); + } +} + +static rt_err_t bcm2835_mbox_probe(struct rt_platform_device *pdev) +{ + rt_err_t err; + struct rt_device *dev = &pdev->parent; + struct bcm2835_mbox *bcm_mbox = rt_calloc(1, sizeof(*bcm_mbox)); + + if (!bcm_mbox) + { + return -RT_ENOMEM; + } + + bcm_mbox->regs = rt_dm_dev_iomap(dev, 0); + + if (!bcm_mbox->regs) + { + err = -RT_EIO; + + goto _fail; + } + + bcm_mbox->irq = rt_dm_dev_get_irq(dev, 0); + + if (bcm_mbox->irq < 0) + { + err = bcm_mbox->irq; + + goto _fail; + } + + dev->user_data = bcm_mbox; + + rt_hw_interrupt_install(bcm_mbox->irq, bcm2835_mbox_isr, bcm_mbox, "bcm2835-mbox"); + rt_hw_interrupt_umask(bcm_mbox->irq); + + bcm_mbox->parent.dev = dev; + bcm_mbox->parent.num_chans = 1; + bcm_mbox->parent.ops = &bcm2835_mbox_ops; + + if ((err = rt_mbox_controller_register(&bcm_mbox->parent))) + { + goto _fail; + } + + return RT_EOK; + +_fail: + if (bcm_mbox->regs) + { + rt_iounmap(bcm_mbox->regs); + } + + rt_free(bcm_mbox); + + return err; +} + +static rt_err_t bcm2835_mbox_remove(struct rt_platform_device *pdev) +{ + struct bcm2835_mbox *bcm_mbox = pdev->parent.user_data; + + rt_hw_interrupt_mask(bcm_mbox->irq); + rt_pic_detach_irq(bcm_mbox->irq, bcm_mbox); + + rt_mbox_controller_unregister(&bcm_mbox->parent); + + rt_iounmap(bcm_mbox->regs); + + rt_free(bcm_mbox); + + return RT_EOK; +} + +static const struct rt_ofw_node_id bcm2835_mbox_ofw_ids[] = +{ + { .compatible = "brcm,bcm2835-mbox" }, + { /* sentinel */ } +}; + +static struct rt_platform_driver bcm2835_mbox_driver = +{ + .name = "mailbox-bcm2835", + .ids = bcm2835_mbox_ofw_ids, + + .probe = bcm2835_mbox_probe, + .remove = bcm2835_mbox_remove, +}; + +static int bcm2835_mbox_drv_register(void) +{ + rt_platform_driver_register(&bcm2835_mbox_driver); + + return 0; +} +INIT_FRAMEWORK_EXPORT(bcm2835_mbox_drv_register); diff --git a/components/drivers/mailbox/mailbox-rockchip.c b/components/drivers/mailbox/mailbox-rockchip.c new file mode 100644 index 000000000000..9d312ab4e48a --- /dev/null +++ b/components/drivers/mailbox/mailbox-rockchip.c @@ -0,0 +1,341 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-09-23 GuEe-GUI first version + */ + +#include +#include + +#define DBG_TAG "mailbox.rockchip" +#define DBG_LVL DBG_INFO +#include + +#define MAILBOX_A2B_INTEN 0x00 +#define MAILBOX_A2B_STATUS 0x04 +#define MAILBOX_A2B_CMD(x) (0x08 + (x) * 8) +#define MAILBOX_A2B_DAT(x) (0x0c + (x) * 8) + +#define MAILBOX_B2A_INTEN 0x28 +#define MAILBOX_B2A_STATUS 0x2c +#define MAILBOX_B2A_CMD(x) (0x30 + (x) * 8) +#define MAILBOX_B2A_DAT(x) (0x34 + (x) * 8) + +struct rockchip_mbox_msg +{ + rt_uint32_t cmd; + int rx_size; +}; + +struct rockchip_mbox_soc_data +{ + int num_chans; +}; + +struct rockchip_mbox_chan +{ + int idx; + int irq; + struct rockchip_mbox_msg *msg; + struct rockchip_mbox *rk_mbox; +}; + +struct rockchip_mbox +{ + struct rt_mbox_controller parent; + + void *regs; + struct rt_clk *pclk; + struct rt_thread *irq_thread; + + rt_uint32_t buf_size; + struct rockchip_mbox_chan chans[]; +}; + +#define raw_to_rockchip_mbox(raw) rt_container_of(raw, struct rockchip_mbox, parent) + +rt_inline rt_uint32_t rockchip_mbox_readl(struct rockchip_mbox *rk_mbox, int offset) +{ + return HWREG32(rk_mbox->regs + offset); +} + +rt_inline void rockchip_mbox_writel(struct rockchip_mbox *rk_mbox, int offset, + rt_uint32_t value) +{ + HWREG32(rk_mbox->regs + offset) = value; +} + +static rt_err_t rockchip_mbox_request(struct rt_mbox_chan *chan) +{ + struct rockchip_mbox *rk_mbox = raw_to_rockchip_mbox(chan->ctrl); + + /* Enable all B2A interrupts */ + rockchip_mbox_writel(rk_mbox, MAILBOX_B2A_INTEN, + (1 << rk_mbox->parent.num_chans) - 1); + + return RT_EOK; +} + +static void rockchip_mbox_free(struct rt_mbox_chan *chan) +{ + int index; + struct rockchip_mbox *rk_mbox = raw_to_rockchip_mbox(chan->ctrl); + + /* Disable all B2A interrupts */ + rockchip_mbox_writel(rk_mbox, MAILBOX_B2A_INTEN, 0); + + index = chan - rk_mbox->parent.chans; + rk_mbox->chans[index].msg = RT_NULL; +} + +static rt_err_t rockchip_mbox_send(struct rt_mbox_chan *chan, const void *data) +{ + int index; + struct rockchip_mbox_msg *msg = (void *)data; + struct rockchip_mbox *rk_mbox = raw_to_rockchip_mbox(chan->ctrl); + + if (msg->rx_size > rk_mbox->buf_size) + { + LOG_E("Transmit size over buf size(%d)", rk_mbox->buf_size); + + return -RT_EINVAL; + } + + index = chan - rk_mbox->parent.chans; + rk_mbox->chans[index].msg = msg; + + rockchip_mbox_writel(rk_mbox, MAILBOX_A2B_CMD(index), msg->cmd); + rockchip_mbox_writel(rk_mbox, MAILBOX_A2B_DAT(index), msg->rx_size); + + return RT_EOK; +} + +static const struct rt_mbox_controller_ops rk_mbox_ops = +{ + .request = rockchip_mbox_request, + .free = rockchip_mbox_free, + .send = rockchip_mbox_send, +}; + +static void rockchip_mbox_thread_isr(void *param) +{ + struct rockchip_mbox_msg *msg; + struct rockchip_mbox *rk_mbox = param; + + while (RT_TRUE) + { + rt_thread_suspend(rk_mbox->irq_thread); + rt_schedule(); + + for (int idx = 0; idx < rk_mbox->parent.num_chans; ++idx) + { + msg = rk_mbox->chans[idx].msg; + + if (!msg) + { + LOG_E("Chan[%d]: B2A message is NULL", idx); + + break; + } + + rt_mbox_recv(&rk_mbox->parent.chans[idx], msg); + rk_mbox->chans[idx].msg = RT_NULL; + } + } +} + +static void rockchip_mbox_isr(int irqno, void *param) +{ + rt_uint32_t status; + struct rockchip_mbox *rk_mbox = param; + + status = rockchip_mbox_readl(rk_mbox, MAILBOX_B2A_STATUS); + + for (int idx = 0; idx < rk_mbox->parent.num_chans; ++idx) + { + if ((status & RT_BIT(idx)) && (irqno == rk_mbox->chans[idx].irq)) + { + /* Clear mbox interrupt */ + rockchip_mbox_writel(rk_mbox, MAILBOX_B2A_STATUS, RT_BIT(idx)); + + rt_thread_resume(rk_mbox->irq_thread); + + return; + } + } + + return; +} + +static void rockchip_mbox_free_resource(struct rockchip_mbox *rk_mbox) +{ + if (rk_mbox->regs) + { + rt_iounmap(rk_mbox->regs); + } + + if (!rt_is_err_or_null(rk_mbox->pclk)) + { + rt_clk_disable_unprepare(rk_mbox->pclk); + rt_clk_put(rk_mbox->pclk); + } + + if (rk_mbox->irq_thread) + { + rt_thread_delete(rk_mbox->irq_thread); + } + + rt_free(rk_mbox); +} + +static rt_err_t rockchip_mbox_probe(struct rt_platform_device *pdev) +{ + rt_err_t err; + char chan_name[RT_NAME_MAX]; + rt_uint64_t io_addr, io_size; + struct rockchip_mbox *rk_mbox; + struct rockchip_mbox_chan *chan; + struct rt_device *dev = &pdev->parent; + const struct rockchip_mbox_soc_data *soc_data = pdev->id->data; + + rk_mbox = rt_calloc(1, sizeof(*rk_mbox) + + soc_data->num_chans * sizeof(struct rockchip_mbox_chan)); + + if (!rk_mbox) + { + return -RT_ENOMEM; + } + + if ((err = rt_dm_dev_get_address(dev, 0, &io_addr, &io_size))) + { + goto _fail; + } + + rk_mbox->regs = rt_ioremap((void *)io_addr, (size_t)io_size); + + if (!rk_mbox->regs) + { + err = -RT_EIO; + goto _fail; + } + + rk_mbox->pclk = rt_clk_get_by_name(dev, "pclk_mailbox"); + + if (rt_is_err(rk_mbox->pclk)) + { + err = rt_ptr_err(rk_mbox->pclk); + + goto _fail; + } + + if ((err = rt_clk_prepare_enable(rk_mbox->pclk))) + { + goto _fail; + } + + rk_mbox->irq_thread = rt_thread_create("rk_mbox", &rockchip_mbox_thread_isr, + rk_mbox, SYSTEM_THREAD_STACK_SIZE, RT_THREAD_PRIORITY_MAX / 2, 10); + + if (!rk_mbox->irq_thread) + { + LOG_E("Create Mailbox IRQ thread fail"); + goto _fail; + } + + rt_thread_startup(rk_mbox->irq_thread); + + chan = &rk_mbox->chans[0]; + + for (int i = 0; i < soc_data->num_chans; ++i, ++chan) + { + int irq = rt_dm_dev_get_irq(dev, i); + + if (irq < 0) + { + err = irq; + + goto _fail; + } + + rt_snprintf(chan_name, sizeof(chan_name), "rk_mbox-%d", i); + + rt_hw_interrupt_install(irq, rockchip_mbox_isr, rk_mbox, chan_name); + rt_hw_interrupt_umask(irq); + + chan->idx = i; + chan->irq = irq; + chan->rk_mbox = rk_mbox; + } + + rk_mbox->buf_size = io_size / (soc_data->num_chans * 2); + + dev->user_data = rk_mbox; + + rk_mbox->parent.dev = dev; + rk_mbox->parent.num_chans = soc_data->num_chans; + rk_mbox->parent.ops = &rk_mbox_ops; + + if ((err = rt_mbox_controller_register(&rk_mbox->parent))) + { + goto _fail; + } + + return RT_EOK; + +_fail: + rockchip_mbox_free_resource(rk_mbox); + + return err; +} + +static rt_err_t rockchip_mbox_remove(struct rt_platform_device *pdev) +{ + struct rockchip_mbox_chan *chan; + struct rockchip_mbox *rk_mbox = pdev->parent.user_data; + + chan = &rk_mbox->chans[0]; + + for (int i = 0; i < rk_mbox->parent.num_chans; ++i, ++chan) + { + rt_hw_interrupt_mask(chan->irq); + rt_pic_detach_irq(chan->irq, rk_mbox); + } + + rt_mbox_controller_unregister(&rk_mbox->parent); + + rockchip_mbox_free_resource(rk_mbox); + + return RT_EOK; +} + +static const struct rockchip_mbox_soc_data rk3368_data = +{ + .num_chans = 4, +}; + +static const struct rt_ofw_node_id rockchip_mbox_ofw_ids[] = +{ + { .compatible = "rockchip,rk3368-mailbox", .data = &rk3368_data }, + { /* sentinel */ } +}; + +static struct rt_platform_driver rockchip_mbox_driver = +{ + .name = "mailbox-rockchip", + .ids = rockchip_mbox_ofw_ids, + + .probe = rockchip_mbox_probe, + .remove = rockchip_mbox_remove, +}; + +static int rockchip_mbox_drv_register(void) +{ + rt_platform_driver_register(&rockchip_mbox_driver); + + return 0; +} +INIT_FRAMEWORK_EXPORT(rockchip_mbox_drv_register); diff --git a/components/drivers/mailbox/mailbox.c b/components/drivers/mailbox/mailbox.c new file mode 100644 index 000000000000..231f846a1002 --- /dev/null +++ b/components/drivers/mailbox/mailbox.c @@ -0,0 +1,336 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-09-23 GuEe-GUI first version + */ + +#include +#include + +#define DBG_TAG "rtdm.mailbox" +#define DBG_LVL DBG_INFO +#include + +#include +#include +#include + +static struct rt_spinlock mbox_ops_lock = {}; +static rt_list_t mbox_nodes = RT_LIST_OBJECT_INIT(mbox_nodes); + +static void mbox_chan_timeout(void *param); + +rt_err_t rt_mbox_controller_register(struct rt_mbox_controller *ctrl) +{ + int len; + struct rt_mbox_chan *chan; + char timer_name[RT_NAME_MAX]; + + if (!ctrl || !ctrl->dev || !ctrl->ops || !ctrl->num_chans) + { + return -RT_EINVAL; + } + + ctrl->chans = rt_calloc(ctrl->num_chans, sizeof(struct rt_mbox_chan)); + + if (!ctrl->chans) + { + return -RT_ENOMEM; + } + + len = rt_snprintf(timer_name, sizeof(timer_name), "%s-", + rt_dm_dev_get_name(ctrl->dev)); + + RT_ASSERT(len < sizeof(timer_name)); + + chan = &ctrl->chans[0]; + + for (int i = 0; i < ctrl->num_chans; ++i, ++chan) + { + chan->ctrl = ctrl; + rt_spin_lock_init(&chan->lock); + + rt_snprintf(&timer_name[len], sizeof(timer_name) - len, "%d", i); + rt_timer_init(&chan->timer, timer_name, mbox_chan_timeout, chan, + 0, RT_TIMER_FLAG_PERIODIC); + } + + rt_list_init(&ctrl->list); + rt_dm_dev_bind_fwdata(ctrl->dev, RT_NULL, ctrl); + + rt_spin_lock(&mbox_ops_lock); + + rt_list_insert_after(&mbox_nodes, &ctrl->list); + + rt_spin_unlock(&mbox_ops_lock); + + return RT_EOK; +} + +rt_err_t rt_mbox_controller_unregister(struct rt_mbox_controller *ctrl) +{ + struct rt_mbox_chan *chan; + + if (!ctrl) + { + return -RT_EINVAL; + } + + rt_spin_lock(&mbox_ops_lock); + + rt_dm_dev_unbind_fwdata(ctrl->dev, RT_NULL); + rt_list_remove(&ctrl->list); + + rt_spin_unlock(&mbox_ops_lock); + + chan = &ctrl->chans[0]; + + for (int i = ctrl->num_chans - 1; i >= 0; --i, ++chan) + { + rt_mbox_free(&ctrl->chans[i]); + } + + rt_free(ctrl->chans); + + return RT_EOK; +} + +rt_err_t rt_mbox_send(struct rt_mbox_chan *chan, const void *data, + rt_uint32_t timeout_ms) +{ + rt_err_t err; + rt_ubase_t level; + rt_bool_t timer_go = RT_FALSE; + struct rt_mbox_client *client; + struct rt_mbox_controller *ctrl; + + if (!chan || !data) + { + return -RT_EINVAL; + } + + ctrl = chan->ctrl; + client = chan->client; + + level = rt_spin_lock_irqsave(&chan->lock); + + if (client->tx_prepare) + { + client->tx_prepare(client, data); + } + + chan->complete = RT_FALSE; + err = ctrl->ops->send(chan, data); + + if (!err) + { + chan->data = (void *)data; + + if (timeout_ms != RT_WAITING_FOREVER) + { + rt_tick_t tick = rt_tick_from_millisecond(timeout_ms); + + rt_timer_control(&chan->timer, RT_TIMER_CTRL_SET_TIME, &tick); + + timer_go = RT_TRUE; + } + } + else + { + chan->complete = RT_TRUE; + } + + rt_spin_unlock_irqrestore(&chan->lock, level); + + if (timer_go) + { + rt_timer_start(&chan->timer); + } + + return err; +} + +void rt_mbox_send_done(struct rt_mbox_chan *chan, rt_err_t err) +{ + void *data; + rt_ubase_t level; + + level = rt_spin_lock_irqsave(&chan->lock); + + data = chan->data; + chan->data = RT_NULL; + + rt_spin_unlock_irqrestore(&chan->lock, level); + + if (chan->client->tx_done) + { + chan->client->tx_done(chan->client, data, err); + } + + chan->complete = RT_TRUE; +} + +static void mbox_chan_timeout(void *param) +{ + rt_err_t err = RT_EOK; + struct rt_mbox_chan *chan = param; + + rt_timer_stop(&chan->timer); + + if (!chan->complete) + { + err = -RT_ETIMEOUT; + } + + rt_mbox_send_done(chan, err); +} + +rt_err_t rt_mbox_peek(struct rt_mbox_chan *chan) +{ + if (!chan) + { + return -RT_EINVAL; + } + + if (chan->ctrl->ops->peek) + { + return chan->ctrl->ops->peek(chan); + } + + return -RT_ENOSYS; +} + +rt_err_t rt_mbox_recv(struct rt_mbox_chan *chan, void *data) +{ + if (!chan || !data) + { + return -RT_EINVAL; + } + + if (chan->client->rx_callback) + { + chan->client->rx_callback(chan->client, data); + } + + return RT_EOK; +} + +static int mbox_controller_ofw_parse_default(struct rt_mbox_controller *ctrl, + struct rt_ofw_cell_args *args) +{ + if (args->args_count != 1) + { + return -RT_EINVAL; + } + + return args->args[0]; +} + +struct rt_mbox_chan *rt_mbox_request_by_index(struct rt_mbox_client *client, int index) +{ + rt_err_t err; + struct rt_ofw_node *np; + struct rt_ofw_cell_args args; + struct rt_mbox_controller *ctrl; + struct rt_mbox_chan *chan = RT_NULL; + + if (!client && index < 0) + { + return RT_NULL; + } + + np = client->dev->ofw_node; + + rt_spin_lock(&mbox_ops_lock); + + err = rt_ofw_parse_phandle_cells(np, "mboxes", "#mbox-cells", index, &args); + + if (err) + { + goto _out_lock; + } + + ctrl = rt_ofw_data(args.data); + rt_ofw_node_put(args.data); + + if (ctrl) + { + int index; + + if (ctrl->ops->ofw_parse) + { + index = ctrl->ops->ofw_parse(ctrl, &args); + } + else + { + index = mbox_controller_ofw_parse_default(ctrl, &args); + } + + if (index >= 0) + { + chan = &ctrl->chans[index]; + } + else + { + LOG_E("Parse chan from %s error = %s", + rt_dm_dev_get_name(ctrl->dev), rt_strerror(index)); + + goto _out_lock; + } + + if (ctrl->ops->request) + { + rt_err_t err = ctrl->ops->request(chan); + + if (err) + { + LOG_E("Request chan[%d] from %s error = %s", + index, rt_dm_dev_get_name(ctrl->dev), rt_strerror(err)); + } + } + } + +_out_lock: + rt_spin_unlock(&mbox_ops_lock); + + return chan; +} + +struct rt_mbox_chan *rt_mbox_request_by_name(struct rt_mbox_client *client, char *name) +{ + int index; + struct rt_ofw_node *np; + + if (!client || !name) + { + return RT_NULL; + } + + np = client->dev->ofw_node; + index = rt_ofw_prop_index_of_string(np, "mbox-names", name); + + if (index < 0) + { + return RT_NULL; + } + + return rt_mbox_request_by_index(client, index); +} + +rt_err_t rt_mbox_free(struct rt_mbox_chan *chan) +{ + if (chan) + { + chan->ctrl->ops->free(chan); + } + else + { + return -RT_EINVAL; + } + + return RT_EOK; +} diff --git a/components/drivers/mfd/Kconfig b/components/drivers/mfd/Kconfig index dbd72a98cc0e..b26d937e8668 100644 --- a/components/drivers/mfd/Kconfig +++ b/components/drivers/mfd/Kconfig @@ -1,9 +1,46 @@ menuconfig RT_USING_MFD - bool "Using Multifunction Device Drivers" + bool "Using Multifunction device drivers" depends on RT_USING_DM default n +config RT_MFD_BCM2835_PM + bool "BCM2835 PM driver" + depends on RT_USING_MFD + default n + +config RT_MFD_EDU + bool "Educational device driver" + depends on RT_USING_MFD + select RT_USING_PCI + select RT_USING_DMA + default n + config RT_MFD_SYSCON bool "System Controller Register R/W" depends on RT_USING_MFD + select RT_USING_OFW default y + +config RT_MFD_RK8XX + bool + depends on RT_USING_MFD + select RT_USING_PIC + default n + +config RT_MFD_RK8XX_I2C + bool "Rockchip RK805/RK808/RK809/RK817/RK818 Power Management Chip" + depends on RT_USING_MFD + select RT_MFD_RK8XX + select RT_USING_OFW + select RT_USING_I2C + select RT_MFD_SYSCON + default n + +config RT_MFD_RK8XX_SPI + bool "Rockchip RK806 Power Management Chip" + depends on RT_USING_MFD + select RT_MFD_RK8XX + select RT_USING_OFW + select RT_USING_SPI + select RT_MFD_SYSCON + default n diff --git a/components/drivers/mfd/SConscript b/components/drivers/mfd/SConscript index e9206158a646..f28515c5d35e 100755 --- a/components/drivers/mfd/SConscript +++ b/components/drivers/mfd/SConscript @@ -7,10 +7,26 @@ if not GetDepend(['RT_USING_MFD']): cwd = GetCurrentDir() CPPPATH = [cwd + '/../include'] +src = [] + +if GetDepend(['RT_MFD_BCM2835_PM']): + src += ['bcm2835-pm.c'] + +if GetDepend(['RT_MFD_EDU']): + src += ['edu.c'] if GetDepend(['RT_MFD_SYSCON']): src += ['mfd-syscon.c'] +if GetDepend(['RT_MFD_RK8XX']): + src += ['rk8xx.c'] + + if GetDepend(['RT_MFD_RK8XX_I2C']): + src += ['rk8xx-i2c.c'] + + if GetDepend(['RT_MFD_RK8XX_SPI']): + src += ['rk8xx-spi.c'] + group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) Return('group') diff --git a/components/drivers/mfd/bcm2835-pm.c b/components/drivers/mfd/bcm2835-pm.c new file mode 100644 index 000000000000..9f0177764d25 --- /dev/null +++ b/components/drivers/mfd/bcm2835-pm.c @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-02-25 GuEe-GUI the first version + */ + +#include +#include + +#define DBG_TAG "mfd.bcm2835-pm" +#define DBG_LVL DBG_INFO +#include + +#include "bcm2835-pm.h" + +static rt_err_t bcm2835_pm_probe(struct rt_platform_device *pdev) +{ + rt_err_t err; + const char *pm_name; + rt_bool_t is_2712 = RT_FALSE: + struct rt_device *dev = &pdev->parent; + struct bcm2835_pm *pm = rt_malloc(sizeof(*pm)); + + if (!pm) + { + return -RT_ENOMEM; + } + +#ifdef RT_USING_OFW + is_2712 = rt_ofw_node_is_compatible(dev->ofw_node, "brcm,bcm2712-pm"); + + if (rt_ofw_prop_read_bool(dev->ofw_node, "reg-names")) + { + if (!(pm->base = rt_dm_dev_iomap_by_name(dev, "pm"))) + { + err = -RT_EIO; + + goto _fail; + } + + pm->asb = rt_dm_dev_iomap_by_name(dev, "asb"); + pm->rpivid_asb = rt_dm_dev_iomap_by_name(dev, "rpivid_asb"); + } + else +#endif /* RT_USING_OFW */ + { + if (!(pm->base = rt_dm_dev_iomap(dev, 0))) + { + err = -RT_EIO; + + goto _fail; + } + + pm->asb = rt_dm_dev_iomap(dev, 1); + pm->rpivid_asb = rt_dm_dev_iomap(dev, 2); + } + + pm->ofw_node = dev->ofw_node; + + if (pm->asb || is_2712) + { + pm_name = "bcm2835-power"; + } + else + { + pm_name = "bcm2835-wdt"; + } + + pdev->name = pm_name; + pdev->priv = pm; + /* Ask bus to check drivers' name */ + pdev->parent.ofw_node = RT_NULL; + + return rt_bus_add_device(pdev->parent.bus, &pdev->parent); + +_fail: + rt_free(pm); + + return err; +} + +static const struct rt_ofw_node_id bcm2835_pm_ofw_ids[] = +{ + { .compatible = "brcm,bcm2835-pm-wdt", }, + { .compatible = "brcm,bcm2835-pm", }, + { .compatible = "brcm,bcm2711-pm", }, + { .compatible = "brcm,bcm2712-pm", }, + { /* sentinel */ } +}; + +static struct rt_platform_driver bcm2835_pm_driver = +{ + .name = "pm-bcm2835", + .ids = bcm2835_pm_ofw_ids, + + .probe = bcm2835_pm_probe, +}; +RT_PLATFORM_DRIVER_EXPORT(bcm2835_pm_driver); diff --git a/components/drivers/mfd/bcm2835-pm.h b/components/drivers/mfd/bcm2835-pm.h new file mode 100644 index 000000000000..1cf48f9be16b --- /dev/null +++ b/components/drivers/mfd/bcm2835-pm.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-02-25 GuEe-GUI the first version + */ + +#ifndef __BCM2835_PM_H__ +#define __BCM2835_PM_H__ + +struct bcm2835_pm +{ + void *base; + void *asb; + void *rpivid_asb; + + void *ofw_node; +}; + +#endif /* __BCM2835_PM_H__ */ diff --git a/components/drivers/mfd/edu.c b/components/drivers/mfd/edu.c new file mode 100644 index 000000000000..0c397668680e --- /dev/null +++ b/components/drivers/mfd/edu.c @@ -0,0 +1,288 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-02-25 GuEe-GUI the first version + */ + +#include +#include +#include + +#define DBG_TAG "mfd.edu" +#define DBG_LVL DBG_INFO +#include + +#include + +#define PCI_EDU_REGS_BAR 0 +#define EDU_REG_VERSION 0x00 +#define EDU_REG_CARD_LIVENESS 0x04 +#define EDU_REG_VALUE 0x08 +#define EDU_REG_STATUS 0x20 +#define EDU_REG_STATUS_IRQ 0x80 +#define EDU_REG_IRQ_STATUS 0x24 +#define EDU_REG_IRQ_RAISE 0x60 +#define EDU_REG_IRQ_ACK 0x64 +#define EDU_REG_DMA_SRC 0x80 +#define EDU_REG_DMA_DST 0x88 +#define EDU_REG_DMA_SIZE 0x90 +#define EDU_REG_DMA_CMD 0x98 +#define EDU_DMA_CMD_RUN 0x1 +#define EDU_DMA_CMD_TO_PCI 0x0 +#define EDU_DMA_CMD_FROM_PCI 0x2 +#define EDU_DMA_CMD_IRQ 0x4 + +#define EDU_FACTORIAL_ACK 0x00000001 + +#define EDU_DMA_ACK 0x00000100 +#define EDU_DMA_FREE (~0UL) +#define EDU_DMA_BASE 0x40000 +#define EDU_DMA_SIZE ((rt_size_t)(4096 - 1)) +#define EDU_DMA_POLL_SIZE 128 + +struct edu_device +{ + struct rt_device parent; + struct rt_dma_controller dma_ctrl; + + void *regs; + rt_uint32_t ack; + + rt_ubase_t dma_src; + rt_ubase_t dma_dst; + + struct rt_mutex lock; + struct rt_completion done; +}; + +#define raw_to_edu_device(raw) rt_container_of(raw, struct edu_device, parent) + +rt_inline rt_uint32_t edu_readl(struct edu_device *edu, int offset) +{ + return HWREG32(edu->regs + offset); +} + +rt_inline void edu_writel(struct edu_device *edu, int offset, rt_uint32_t value) +{ + HWREG32(edu->regs + offset) = value; +} + +static rt_err_t edu_dma_memcpy(struct rt_dma_chan *dma_chan, + rt_ubase_t dst, rt_ubase_t src, rt_size_t len, rt_ubase_t flags) +{ + struct edu_device *edu = rt_container_of(dma_chan->ctrl, struct edu_device, dma_ctrl); + + rt_mutex_take(&edu->lock, RT_WAITING_FOREVER); + + edu->ack = EDU_DMA_ACK; + + while ((rt_ssize_t)len > 0) + { + rt_uint32_t cmd = EDU_DMA_CMD_RUN; + rt_uint32_t blen = rt_min_t(rt_ssize_t, EDU_DMA_SIZE, len); + + if (blen > EDU_DMA_POLL_SIZE) + { + cmd |= EDU_DMA_CMD_IRQ; + } + + edu_writel(edu, EDU_REG_DMA_SRC, (rt_ubase_t)src); + edu_writel(edu, EDU_REG_DMA_DST, EDU_DMA_BASE); + edu_writel(edu, EDU_REG_DMA_SIZE, blen); + edu_writel(edu, EDU_REG_DMA_CMD, cmd | EDU_DMA_CMD_TO_PCI); + + if (cmd & EDU_DMA_CMD_IRQ) + { + rt_completion_wait(&edu->done, RT_WAITING_FOREVER); + } + else + { + while (edu_readl(edu, EDU_REG_DMA_CMD) & EDU_DMA_CMD_RUN) + { + rt_hw_cpu_relax(); + } + } + + edu_writel(edu, EDU_REG_DMA_SRC, EDU_DMA_BASE); + edu_writel(edu, EDU_REG_DMA_DST, (rt_ubase_t)dst); + edu_writel(edu, EDU_REG_DMA_SIZE, blen); + edu_writel(edu, EDU_REG_DMA_CMD, cmd | EDU_DMA_CMD_FROM_PCI); + + if (cmd & EDU_DMA_CMD_IRQ) + { + rt_completion_wait(&edu->done, RT_WAITING_FOREVER); + } + else + { + while (edu_readl(edu, EDU_REG_DMA_CMD) & EDU_DMA_CMD_RUN) + { + rt_hw_cpu_relax(); + } + } + + len -= blen; + src += blen; + dst += blen; + } + + rt_mutex_release(&edu->lock); + + return RT_EOK; +} + +const static struct rt_dma_controller_ops edu_dma_ops = +{ + .prep_dma_memcpy = edu_dma_memcpy, +}; + +static rt_ssize_t edu_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size) +{ + rt_uint32_t number; + struct edu_device *edu = raw_to_edu_device(dev); + + rt_mutex_take(&edu->lock, RT_WAITING_FOREVER); + + number = edu_readl(edu, EDU_REG_VALUE); + + rt_mutex_release(&edu->lock); + + rt_memcpy(buffer, &number, rt_min(sizeof(number), size)); + + return rt_min(sizeof(number), size); +} + +static rt_ssize_t edu_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size) +{ + rt_uint32_t number = 0; + struct edu_device *edu = raw_to_edu_device(dev); + + rt_memcpy(&number, buffer, rt_min(sizeof(number), size)); + + rt_mutex_take(&edu->lock, RT_WAITING_FOREVER); + + edu->ack = EDU_FACTORIAL_ACK; + edu_writel(edu, EDU_REG_STATUS, EDU_REG_STATUS_IRQ); + edu_writel(edu, EDU_REG_VALUE, number); + + rt_completion_wait(&edu->done, RT_WAITING_FOREVER); + + rt_mutex_release(&edu->lock); + + return rt_min(sizeof(number), size); +} + +#ifdef RT_USING_DEVICE_OPS +const static struct rt_device_ops edu_ops = +{ + .read = edu_read, + .write = edu_write, +}; +#endif + +static void edu_isr(int irqno, void *param) +{ + struct edu_device *edu = param; + + edu_writel(edu, EDU_REG_IRQ_ACK, edu->ack); + rt_completion_done(&edu->done); +} + +static rt_err_t edu_probe(struct rt_pci_device *pdev) +{ + rt_err_t err; + struct edu_device *edu = rt_calloc(1, sizeof(*pdev)); + + if (!edu) + { + return -RT_ENOMEM; + } + + edu->regs = rt_pci_iomap(pdev, PCI_EDU_REGS_BAR); + + if (!edu->regs) + { + err = -RT_EIO; + goto _fail; + } + + edu->dma_ctrl.ops = &edu_dma_ops; + + if ((err = rt_dma_controller_register(&edu->dma_ctrl))) + { + goto _fail; + } + + edu->parent.type = RT_Device_Class_Char; +#ifdef RT_USING_DEVICE_OPS + edu->parent.ops = &edu_ops; +#else + edu->parent.read = edu_read; + edu->parent.write = edu_write; +#endif + + if ((err = rt_device_register(&edu->parent, "edu", RT_DEVICE_FLAG_RDWR))) + { + goto _fail; + } + + rt_hw_interrupt_install(pdev->irq, edu_isr, edu, "edu"); + rt_pci_irq_unmask(pdev); + + pdev->parent.user_data = edu; + + rt_mutex_init(&edu->lock, "edu", RT_IPC_FLAG_PRIO); + rt_completion_init(&edu->done); + + LOG_D("EDU PCI device v%d.%d", edu_readl(edu, EDU_REG_VERSION) >> 16, + (edu_readl(edu, EDU_REG_VERSION) >> 8) & 0xff); + + return RT_EOK; + +_fail: + if (edu->dma_ctrl.ops) + { + rt_dma_controller_unregister(&edu->dma_ctrl); + } + + if (edu->regs) + { + rt_iounmap(edu->regs); + } + + rt_free(edu); + + return err; +} + +static rt_err_t edu_remove(struct rt_pci_device *pdev) +{ + struct edu_device *edu = pdev->parent.user_data; + + rt_dma_controller_unregister(&edu->dma_ctrl); + rt_device_unregister(&edu->parent); + + rt_iounmap(edu->regs); + rt_free(edu); + + return RT_EOK; +} + +static struct rt_pci_device_id edu_ids[] = +{ + { RT_PCI_DEVICE_ID(0x1234, 0x11e8), }, + { /* sentinel */ } +}; + +static struct rt_pci_driver edu_driver = +{ + .name = "edu", + + .ids = edu_ids, + .probe = edu_probe, + .remove = edu_remove, +}; +RT_PCI_DRIVER_EXPORT(edu_driver); diff --git a/components/drivers/mfd/mfd-syscon.c b/components/drivers/mfd/mfd-syscon.c index f4894b321e82..260d591389f0 100644 --- a/components/drivers/mfd/mfd-syscon.c +++ b/components/drivers/mfd/mfd-syscon.c @@ -15,6 +15,7 @@ #include #include #include +#include static struct rt_spinlock _syscon_nodes_lock = { 0 }; static rt_list_t _syscon_nodes = RT_LIST_OBJECT_INIT(_syscon_nodes); @@ -28,6 +29,8 @@ rt_err_t rt_syscon_read(struct rt_syscon *syscon, rt_off_t offset, rt_uint32_t * *out_val = HWREG32(syscon->iomem_base + offset); rt_spin_unlock_irqrestore(&syscon->rw_lock, level); + + return RT_EOK; } else { @@ -44,6 +47,8 @@ rt_err_t rt_syscon_write(struct rt_syscon *syscon, rt_off_t offset, rt_uint32_t HWREG32(syscon->iomem_base + offset) = val; rt_spin_unlock_irqrestore(&syscon->rw_lock, level); + + return RT_EOK; } else { @@ -76,10 +81,20 @@ rt_err_t rt_syscon_update_bits(struct rt_syscon *syscon, rt_off_t offset, rt_uin return err; } +static rt_err_t syscon_probe(struct rt_platform_device *pdev); + struct rt_syscon *rt_syscon_find_by_ofw_node(struct rt_ofw_node *np) { - struct rt_syscon *syscon; - rt_ubase_t level = rt_spin_lock_irqsave(&_syscon_nodes_lock); + rt_ubase_t level; + struct rt_syscon *syscon = RT_NULL; + struct rt_platform_device syscon_pdev; + + if (!np) + { + goto _exit; + } + + level = rt_spin_lock_irqsave(&_syscon_nodes_lock); /* ofw_data is not safety */ rt_list_for_each_entry(syscon, &_syscon_nodes, list) @@ -92,10 +107,30 @@ struct rt_syscon *rt_syscon_find_by_ofw_node(struct rt_ofw_node *np) rt_spin_unlock_irqrestore(&_syscon_nodes_lock, level); + if (syscon) + { + goto _exit; + } + + /* Not found, try probe this node */ + if (!rt_ofw_node_is_compatible(np, "syscon") && + !rt_ofw_node_is_compatible(np, "simple-mfd")) + { + goto _exit; + } + + syscon_pdev.parent.ofw_node = np; + + if (!syscon_probe(&syscon_pdev)) + { + syscon = rt_ofw_data(np); + } + +_exit: return syscon; } -struct rt_syscon *rt_syscon_find_by_compatible(const char *compatible) +struct rt_syscon *rt_syscon_find_by_ofw_compatible(const char *compatible) { struct rt_syscon *syscon; struct rt_ofw_node *syscon_np = rt_ofw_find_node_by_compatible(RT_NULL, compatible); @@ -110,7 +145,7 @@ struct rt_syscon *rt_syscon_find_by_compatible(const char *compatible) return syscon; } -struct rt_syscon *rt_syscon_find_by_phandle(struct rt_ofw_node *np, const char *propname) +struct rt_syscon *rt_syscon_find_by_ofw_phandle(struct rt_ofw_node *np, const char *propname) { struct rt_syscon *syscon; struct rt_ofw_node *syscon_np = rt_ofw_parse_phandle(np, propname, 0); @@ -130,7 +165,7 @@ static rt_err_t syscon_probe(struct rt_platform_device *pdev) rt_err_t err; struct rt_ofw_node *np; rt_uint64_t iomem_range[2]; - struct syscon *syscon = rt_malloc(sizeof(*syscon)); + struct rt_syscon *syscon = rt_calloc(1, sizeof(*syscon)); if (!syscon) { @@ -152,8 +187,13 @@ static rt_err_t syscon_probe(struct rt_platform_device *pdev) goto _fail; } + rt_list_init(&syscon->list); + rt_list_insert_after(&_syscon_nodes, &syscon->list); + rt_spin_lock_init(&syscon->rw_lock); + pdev->parent.user_data = syscon; + syscon->np = pdev->parent.ofw_node; rt_ofw_data(np) = syscon; @@ -165,6 +205,17 @@ static rt_err_t syscon_probe(struct rt_platform_device *pdev) return err; } +static rt_err_t syscon_remove(struct rt_platform_device *pdev) +{ + struct rt_syscon *syscon = pdev->parent.user_data; + + rt_iounmap(syscon->iomem_base); + + rt_free(syscon); + + return RT_EOK; +} + static const struct rt_ofw_node_id syscon_ofw_ids[] = { { .compatible = "syscon" }, @@ -177,6 +228,7 @@ static struct rt_platform_driver syscon_driver = .ids = syscon_ofw_ids, .probe = syscon_probe, + .remove = syscon_remove, }; static int syscon_drv_register(void) @@ -185,4 +237,4 @@ static int syscon_drv_register(void) return 0; } -INIT_SUBSYS_EXPORT(syscon_drv_register); +INIT_FRAMEWORK_EXPORT(syscon_drv_register); diff --git a/components/drivers/mfd/rk8xx-i2c.c b/components/drivers/mfd/rk8xx-i2c.c new file mode 100644 index 000000000000..17a43d8ab9cf --- /dev/null +++ b/components/drivers/mfd/rk8xx-i2c.c @@ -0,0 +1,187 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-11-26 GuEe-GUI first version + */ + +#include +#include + +#define DBG_TAG "mfd.rk8xx-i2c" +#define DBG_LVL DBG_INFO +#include + +#include "rk8xx.h" + +struct rk8xx_i2c_soc_data +{ + int variant; +}; + +static rt_uint32_t rk8xx_i2c_read(struct rk8xx *rk8xx, rt_uint16_t reg) +{ + rt_uint8_t data = 0; + rt_uint8_t send_buf[2]; + struct rt_i2c_msg msg[2]; + struct rt_i2c_client *client = rk8xx_to_i2c_client(rk8xx); + + send_buf[0] = (reg & 0xff); + + msg[0].addr = client->client_addr; + msg[0].flags = RT_I2C_WR; + msg[0].len = 1; + msg[0].buf = send_buf; + + msg[1].addr = client->client_addr; + msg[1].flags = RT_I2C_RD; + msg[1].len = 1; + msg[1].buf = &data; + + if (rt_i2c_transfer(client->bus, msg, 2) == 2) + { + return data; + } + else + { + return (rt_uint32_t)-RT_ERROR; + } +} + +static rt_err_t rk8xx_i2c_write(struct rk8xx *rk8xx, rt_uint16_t reg, + rt_uint8_t data) +{ + rt_uint8_t send_buf[2]; + struct rt_i2c_msg msg; + struct rt_i2c_client *client = rk8xx_to_i2c_client(rk8xx); + + send_buf[0] = reg & 0xff; + send_buf[1] = data; + + msg.addr = client->client_addr; + msg.flags = RT_I2C_WR; + msg.len = 2; + msg.buf = send_buf; + + if (rt_i2c_transfer(client->bus, &msg, 1) == 1) + { + return RT_EOK; + } + else + { + return -RT_ERROR; + } +} + +static rt_err_t rk8xx_i2c_update_bits(struct rk8xx *rk8xx, rt_uint16_t reg, + rt_uint8_t mask, rt_uint8_t data) +{ + rt_uint32_t old, tmp; + + old = rk8xx_i2c_read(rk8xx, reg); + + if (old < 0) + { + return old; + } + + tmp = old & ~mask; + tmp |= (data & mask); + + return rk8xx_i2c_write(rk8xx, reg, tmp); +} + +static rt_err_t rk8xx_i2c_probe(struct rt_i2c_client *client) +{ + rt_err_t err; + const struct rk8xx_i2c_soc_data *soc_data; + struct rk8xx *rk8xx = rt_calloc(1, sizeof(*rk8xx)); + + if (!rk8xx) + { + return -RT_ENOMEM; + } + + rk8xx->irq = rt_dm_dev_get_irq(&client->parent, 0); + + if (rk8xx->irq < 0) + { + err = rk8xx->irq; + goto _fail; + } + + soc_data = client->ofw_id->data; + client->parent.user_data = rk8xx; + + rk8xx->variant = soc_data->variant; + rk8xx->dev = &client->parent; + rk8xx->read = rk8xx_i2c_read; + rk8xx->write = rk8xx_i2c_write; + rk8xx->update_bits = rk8xx_i2c_update_bits; + + if ((err = rk8xx_probe(rk8xx))) + { + goto _fail; + } + + return RT_EOK; + +_fail: + rt_free(rk8xx); + + return err; +} + +static rt_err_t rk8xx_i2c_shutdown(struct rt_i2c_client *client) +{ + struct rk8xx *rk8xx = client->parent.user_data; + + return rk8xx_shutdown(rk8xx); +} + +static const struct rk8xx_i2c_soc_data rk805_data = +{ + .variant = RK805_ID, +}; + +static const struct rk8xx_i2c_soc_data rk808_data = +{ + .variant = RK808_ID, +}; + +static const struct rk8xx_i2c_soc_data rk809_data = +{ + .variant = RK809_ID, +}; + +static const struct rk8xx_i2c_soc_data rk817_data = +{ + .variant = RK817_ID, +}; + +static const struct rk8xx_i2c_soc_data rk818_data = +{ + .variant = RK818_ID, +}; + +static const struct rt_ofw_node_id rk8xx_i2c_ofw_ids[] = +{ + { .compatible = "rockchip,rk805", .data = &rk805_data }, + { .compatible = "rockchip,rk808", .data = &rk808_data }, + { .compatible = "rockchip,rk809", .data = &rk809_data }, + { .compatible = "rockchip,rk817", .data = &rk817_data }, + { .compatible = "rockchip,rk818", .data = &rk818_data }, + { /* sentinel */ }, +}; + +static struct rt_i2c_driver rk8xx_i2c_driver = +{ + .ofw_ids = rk8xx_i2c_ofw_ids, + + .probe = rk8xx_i2c_probe, + .shutdown = rk8xx_i2c_shutdown, +}; +RT_I2C_DRIVER_EXPORT(rk8xx_i2c_driver); diff --git a/components/drivers/mfd/rk8xx-spi.c b/components/drivers/mfd/rk8xx-spi.c new file mode 100644 index 000000000000..85097a2fe8df --- /dev/null +++ b/components/drivers/mfd/rk8xx-spi.c @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-11-26 GuEe-GUI first version + */ + +#include +#include + +#define DBG_TAG "mfd.rk8xx-spi" +#define DBG_LVL DBG_INFO +#include + +#include "rk8xx.h" + +#define RK806_CMD_PACKAGE(CMD, LEN) \ + (RK806_CMD_##CMD | RK806_CMD_CRC_DIS | (LEN & RK806_CMD_LEN_MSK)) + +/* + * Format of SPI commands (3 data packages: CMD, REG_L, REG_H) + * CMD R/W[7]: R=0, W=1 + * CMD CRC_EN[6]: Enable=1, Disable=0 + * CMD Len[3:0]: + * case 1: CRC_EN=1 + * The length of data written or read is noted in Len[3:0]. + * The host or slave computer transmits CRC data at the position of len+1. + * case 2: CRC_EN=0 + * The data transmission takes no advantage of the length. + * The addresses of registers of slave computer self-increase within the interval of 0~255. + * + * REG_L[7:0]: The address of the target register is low-8 bit. + * REG_H[15:8]: The address of the target register is high-8 bit. + */ + +static rt_uint32_t rk8xx_spi_read(struct rk8xx *rk8xx, rt_uint16_t reg) +{ + rt_err_t err; + rt_uint8_t data = 0, package[3]; + struct rt_spi_device *spi_dev = rk8xx_to_spi_device(rk8xx); + + package[0] = RK806_CMD_PACKAGE(READ, sizeof(data)); + package[1] = reg & 0xff; + package[2] = (reg >> 8) & 0xff; + + err = rt_spi_send_then_recv(spi_dev, package, sizeof(package), + &data, sizeof(data)); + + return !err ? data : (rt_uint32_t)err; +} + +static rt_err_t rk8xx_spi_write(struct rk8xx *rk8xx, rt_uint16_t reg, + rt_uint8_t data) +{ + rt_ssize_t res; + rt_uint8_t package[4]; + struct rt_spi_device *spi_dev = rk8xx_to_spi_device(rk8xx); + + package[0] = RK806_CMD_PACKAGE(WRITE, sizeof(data)); + package[1] = reg & 0xff; + package[2] = (reg >> 8) & 0xff; + package[3] = data; + + res = rt_spi_transfer(spi_dev, package, RT_NULL, sizeof(package)); + + return res > 0 ? RT_EOK : (res == 0 ? -RT_EIO : res); +} + +static rt_err_t rk8xx_spi_update_bits(struct rk8xx *rk8xx, rt_uint16_t reg, + rt_uint8_t mask, rt_uint8_t data) +{ + rt_uint32_t old, tmp; + + old = rk8xx_spi_read(rk8xx, reg); + + if ((rt_err_t)old < 0) + { + return old; + } + + tmp = old & ~mask; + tmp |= (data & mask); + + return rk8xx_spi_write(rk8xx, reg, tmp); +} + +static rt_err_t rk8xx_spi_probe(struct rt_spi_device *spi_dev) +{ + rt_err_t err; + struct rk8xx *rk8xx = rt_calloc(1, sizeof(*rk8xx)); + + if (!rk8xx) + { + return -RT_ENOMEM; + } + + rk8xx->irq = rt_dm_dev_get_irq(&spi_dev->parent, 0); + + if (rk8xx->irq < 0) + { + err = rk8xx->irq; + goto _fail; + } + + rk8xx->variant = RK806_ID; + rk8xx->dev = &spi_dev->parent; + rk8xx->read = rk8xx_spi_read; + rk8xx->write = rk8xx_spi_write; + rk8xx->update_bits = rk8xx_spi_update_bits; + + if ((err = rk8xx_probe(rk8xx))) + { + goto _fail; + } + + rt_dm_dev_set_name(&spi_dev->parent, "pmic"); + + return RT_EOK; + +_fail: + rt_free(rk8xx); + + return err; +} + +static const struct rt_spi_device_id rk8xx_spi_ids[] = +{ + { .name = "rk806" }, + { /* sentinel */ }, +}; + +static const struct rt_ofw_node_id rk8xx_spi_ofw_ids[] = +{ + { .compatible = "rockchip,rk806" }, + { /* sentinel */ }, +}; + +static struct rt_spi_driver rk8xx_spi_driver = +{ + .ids = rk8xx_spi_ids, + .ofw_ids = rk8xx_spi_ofw_ids, + + .probe = rk8xx_spi_probe, +}; +RT_SPI_DRIVER_EXPORT(rk8xx_spi_driver); diff --git a/components/drivers/mfd/rk8xx.c b/components/drivers/mfd/rk8xx.c new file mode 100644 index 000000000000..9b7014f5c389 --- /dev/null +++ b/components/drivers/mfd/rk8xx.c @@ -0,0 +1,998 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-11-26 GuEe-GUI first version + */ + +#include +#include + +#define DBG_TAG "mfd.rk8xx" +#define DBG_LVL DBG_INFO +#include + +#include "rk8xx.h" + +struct rk8xx_reg_data +{ + int addr; + int mask; + int value; +}; + +struct rk8xx_endpoint +{ + const char *name; + const char *ofw_name; + + int irqs_nr; + const int *irqs_list; +}; + +struct rk8xx_irq_map +{ + rt_uint32_t reg_offset; + rt_uint32_t mask; +}; + +#define RK8XX_IRQ_REG(IRQ, OFF, MASK) \ + [IRQ] = { .reg_offset = (OFF), .mask = (MASK) } + +#define RK8XX_IRQ_REG_LINE(ID, REG_BITS) \ + [ID] = { .reg_offset = (ID) / (REG_BITS), .mask = RT_BIT((ID) % (REG_BITS)), } + +struct rk8xx_irqchip +{ + struct rt_pic parent; + + struct rk8xx *rk8xx; + +#define RK8XX_IRQCHIP_NUM_REGS_MAX 3 + int num_regs; + int reg_stride; + int mask_base; + int status_base; + int ack_base; + + int irqs_nr; + const struct rk8xx_irq_map *irqs_map; + + struct rt_thread *irq_thread; +}; + +#define raw_to_rk8xx_irqchip(raw) rt_container_of(raw, struct rk8xx_irqchip, parent) + +struct rk8xx_power +{ + struct rt_device parent; + + struct rk8xx *rk8xx; +}; + +#define raw_to_rk8xx_power(raw) rt_container_of(raw, struct rk8xx_power, parent) + +static void rk8xx_irq_mask(struct rt_pic_irq *pirq) +{ + rt_uint16_t base; + struct rk8xx_irqchip *rk8xx_ic = raw_to_rk8xx_irqchip(pirq->pic); + const struct rk8xx_irq_map *irq_map = &rk8xx_ic->irqs_map[pirq->hwirq]; + + base = rk8xx_ic->mask_base + (irq_map->reg_offset / rk8xx_ic->reg_stride); + + rk8xx_update_bits(rk8xx_ic->rk8xx, base, irq_map->mask, irq_map->mask); +} + +static void rk8xx_irq_unmask(struct rt_pic_irq *pirq) +{ + rt_uint16_t base; + struct rk8xx_irqchip *rk8xx_ic = raw_to_rk8xx_irqchip(pirq->pic); + const struct rk8xx_irq_map *irq_map = &rk8xx_ic->irqs_map[pirq->hwirq]; + + base = rk8xx_ic->mask_base + (irq_map->reg_offset / rk8xx_ic->reg_stride); + + rk8xx_update_bits(rk8xx_ic->rk8xx, base, irq_map->mask, 0); +} + +static int rk8xx_irq_map(struct rt_pic *pic, int hwirq, rt_uint32_t mode) +{ + int irq = -1; + struct rt_pic_irq *pirq = rt_pic_find_irq(pic, hwirq); + + if (pirq) + { + struct rk8xx_irqchip *rk8xx_ic = raw_to_rk8xx_irqchip(pic); + + irq = rt_pic_config_irq(pic, hwirq, hwirq); + pirq->mode = mode; + rt_pic_cascade(pirq, rk8xx_ic->rk8xx->irq); + } + + return irq; +} + +static rt_err_t rk8xx_irq_parse(struct rt_pic *pic, struct rt_ofw_cell_args *args, struct rt_pic_irq *out_pirq) +{ + struct rk8xx_irqchip *rk8xx_ic = raw_to_rk8xx_irqchip(pic); + + if (args->args_count != 1) + { + return -RT_EINVAL; + } + + out_pirq->mode = rt_pic_irq_get_triger_mode(rk8xx_ic->rk8xx->irq); + out_pirq->hwirq = args->args[0]; + + return RT_EOK; +} + +const static struct rt_pic_ops rk8xx_irq_ops = +{ + .name = "RK8XX", + .irq_mask = rk8xx_irq_mask, + .irq_unmask = rk8xx_irq_unmask, + .irq_map = rk8xx_irq_map, + .irq_parse = rk8xx_irq_parse, +}; + +static void rk8xx_pmic_thread_isr(void *param) +{ + rt_uint32_t status[RK8XX_IRQCHIP_NUM_REGS_MAX]; + rt_uint16_t base, reg; + struct rk8xx *rk8xx; + struct rt_pic_irq *pirq; + struct rk8xx_irqchip *rk8xx_ic = param; + + rk8xx = rk8xx_ic->rk8xx; + + while (RT_TRUE) + { + rt_thread_suspend(rk8xx_ic->irq_thread); + rt_schedule(); + + base = rk8xx_ic->status_base; + rt_memset(status, 0, sizeof(status)); + + for (int i = 0; i < rk8xx_ic->num_regs; ++i) + { + reg = base + i * rk8xx_ic->reg_stride; + status[i] = rk8xx_read(rk8xx, reg); + + if ((rt_err_t)status[i] < 0) + { + LOG_E("Read IRQ status failed error = %s", rt_strerror(status[i])); + + goto _end; + } + } + + base = rk8xx_ic->ack_base; + + for (int i = 0; i < rk8xx_ic->num_regs; ++i) + { + rt_err_t err; + + reg = base + i * rk8xx_ic->reg_stride; + err = rk8xx_write(rk8xx, reg, status[i]); + + if (err) + { + LOG_E("ACK IRQ failed error = %s", rt_strerror(err)); + + goto _end; + } + } + + for (int i = 0; i < rk8xx_ic->irqs_nr; ++i) + { + base = rk8xx_ic->irqs_map[i].reg_offset / rk8xx_ic->reg_stride; + + if (status[base] & rk8xx_ic->irqs_map[i].mask) + { + pirq = rt_pic_find_irq(&rk8xx_ic->parent, i); + + if (pirq && pirq->pic) + { + rt_pic_handle_isr(pirq); + } + } + } + _end: + rt_hw_interrupt_umask(rk8xx->irq); + } +} + +static void rk8xx_pmic_isr(int irqno, void *param) +{ + struct rk8xx_irqchip *rk8xx_ic = param; + + rt_hw_interrupt_mask(rk8xx_ic->rk8xx->irq); + + rt_thread_resume(rk8xx_ic->irq_thread); +} + +static const struct rk8xx_irq_map rk805_irqs[] = +{ + RK8XX_IRQ_REG(RK805_IRQ_PWRON_RISE, 0, RK805_IRQ_PWRON_RISE_MSK), + RK8XX_IRQ_REG(RK805_IRQ_VB_LOW, 0, RK805_IRQ_VB_LOW_MSK), + RK8XX_IRQ_REG(RK805_IRQ_PWRON, 0, RK805_IRQ_PWRON_MSK), + RK8XX_IRQ_REG(RK805_IRQ_PWRON_LP, 0, RK805_IRQ_PWRON_LP_MSK), + RK8XX_IRQ_REG(RK805_IRQ_HOTDIE, 0, RK805_IRQ_HOTDIE_MSK), + RK8XX_IRQ_REG(RK805_IRQ_RTC_ALARM, 0, RK805_IRQ_RTC_ALARM_MSK), + RK8XX_IRQ_REG(RK805_IRQ_RTC_PERIOD, 0, RK805_IRQ_RTC_PERIOD_MSK), + RK8XX_IRQ_REG(RK805_IRQ_PWRON_FALL, 0, RK805_IRQ_PWRON_FALL_MSK), +}; + +static const struct rk8xx_irq_map rk806_irqs[] = +{ + /* INT_STS0 IRQs */ + RK8XX_IRQ_REG(RK806_IRQ_PWRON_FALL, 0, RK806_INT_STS_PWRON_FALL), + RK8XX_IRQ_REG(RK806_IRQ_PWRON_RISE, 0, RK806_INT_STS_PWRON_RISE), + RK8XX_IRQ_REG(RK806_IRQ_PWRON, 0, RK806_INT_STS_PWRON), + RK8XX_IRQ_REG(RK806_IRQ_PWRON_LP, 0, RK806_INT_STS_PWRON_LP), + RK8XX_IRQ_REG(RK806_IRQ_HOTDIE, 0, RK806_INT_STS_HOTDIE), + RK8XX_IRQ_REG(RK806_IRQ_VDC_RISE, 0, RK806_INT_STS_VDC_RISE), + RK8XX_IRQ_REG(RK806_IRQ_VDC_FALL, 0, RK806_INT_STS_VDC_FALL), + RK8XX_IRQ_REG(RK806_IRQ_VB_LO, 0, RK806_INT_STS_VB_LO), + /* INT_STS1 IRQs */ + RK8XX_IRQ_REG(RK806_IRQ_REV0, 1, RK806_INT_STS_REV0), + RK8XX_IRQ_REG(RK806_IRQ_REV1, 1, RK806_INT_STS_REV1), + RK8XX_IRQ_REG(RK806_IRQ_REV2, 1, RK806_INT_STS_REV2), + RK8XX_IRQ_REG(RK806_IRQ_CRC_ERROR, 1, RK806_INT_STS_CRC_ERROR), + RK8XX_IRQ_REG(RK806_IRQ_SLP3_GPIO, 1, RK806_INT_STS_SLP3_GPIO), + RK8XX_IRQ_REG(RK806_IRQ_SLP2_GPIO, 1, RK806_INT_STS_SLP2_GPIO), + RK8XX_IRQ_REG(RK806_IRQ_SLP1_GPIO, 1, RK806_INT_STS_SLP1_GPIO), + RK8XX_IRQ_REG(RK806_IRQ_WDT, 1, RK806_INT_STS_WDT), +}; + +static const struct rk8xx_irq_map rk808_irqs[] = +{ + /* INT_STS */ + RK8XX_IRQ_REG(RK808_IRQ_VOUT_LO, 0, RK808_IRQ_VOUT_LO_MSK), + RK8XX_IRQ_REG(RK808_IRQ_VB_LO, 0, RK808_IRQ_VB_LO_MSK), + RK8XX_IRQ_REG(RK808_IRQ_PWRON, 0, RK808_IRQ_PWRON_MSK), + RK8XX_IRQ_REG(RK808_IRQ_PWRON_LP, 0, RK808_IRQ_PWRON_LP_MSK), + RK8XX_IRQ_REG(RK808_IRQ_HOTDIE, 0, RK808_IRQ_HOTDIE_MSK), + RK8XX_IRQ_REG(RK808_IRQ_RTC_ALARM, 0, RK808_IRQ_RTC_ALARM_MSK), + RK8XX_IRQ_REG(RK808_IRQ_RTC_PERIOD, 0, RK808_IRQ_RTC_PERIOD_MSK), + /* INT_STS2 */ + RK8XX_IRQ_REG(RK808_IRQ_PLUG_IN_INT, 1, RK808_IRQ_PLUG_IN_INT_MSK), + RK8XX_IRQ_REG(RK808_IRQ_PLUG_OUT_INT, 1, RK808_IRQ_PLUG_OUT_INT_MSK), +}; + +static const struct rk8xx_irq_map rk818_irqs[] = +{ + /* INT_STS */ + RK8XX_IRQ_REG(RK818_IRQ_VOUT_LO, 0, RK818_IRQ_VOUT_LO_MSK), + RK8XX_IRQ_REG(RK818_IRQ_VB_LO, 0, RK818_IRQ_VB_LO_MSK), + RK8XX_IRQ_REG(RK818_IRQ_PWRON, 0, RK818_IRQ_PWRON_MSK), + RK8XX_IRQ_REG(RK818_IRQ_PWRON_LP, 0, RK818_IRQ_PWRON_LP_MSK), + RK8XX_IRQ_REG(RK818_IRQ_HOTDIE, 0, RK818_IRQ_HOTDIE_MSK), + RK8XX_IRQ_REG(RK818_IRQ_RTC_ALARM, 0, RK818_IRQ_RTC_ALARM_MSK), + RK8XX_IRQ_REG(RK818_IRQ_RTC_PERIOD, 0, RK818_IRQ_RTC_PERIOD_MSK), + RK8XX_IRQ_REG(RK818_IRQ_USB_OV, 0, RK818_IRQ_USB_OV_MSK), + /* INT_STS2 */ + RK8XX_IRQ_REG(RK818_IRQ_PLUG_IN, 1, RK818_IRQ_PLUG_IN_MSK), + RK8XX_IRQ_REG(RK818_IRQ_PLUG_OUT, 1, RK818_IRQ_PLUG_OUT_MSK), + RK8XX_IRQ_REG(RK818_IRQ_CHG_OK, 1, RK818_IRQ_CHG_OK_MSK), + RK8XX_IRQ_REG(RK818_IRQ_CHG_TE, 1, RK818_IRQ_CHG_TE_MSK), + RK8XX_IRQ_REG(RK818_IRQ_CHG_TS1, 1, RK818_IRQ_CHG_TS1_MSK), + RK8XX_IRQ_REG(RK818_IRQ_TS2, 1, RK818_IRQ_TS2_MSK), + RK8XX_IRQ_REG(RK818_IRQ_CHG_CVTLIM, 1, RK818_IRQ_CHG_CVTLIM_MSK), + RK8XX_IRQ_REG(RK818_IRQ_DISCHG_ILIM, 1, RK818_IRQ_DISCHG_ILIM_MSK), +}; + +static const struct rk8xx_irq_map rk817_irqs[] = +{ + RK8XX_IRQ_REG_LINE(RK817_IRQ_PWRON_FALL, 8), + RK8XX_IRQ_REG_LINE(RK817_IRQ_PWRON_RISE, 8), + RK8XX_IRQ_REG_LINE(RK817_IRQ_PWRON, 8), + RK8XX_IRQ_REG_LINE(RK817_IRQ_PWMON_LP, 8), + RK8XX_IRQ_REG_LINE(RK817_IRQ_HOTDIE, 8), + RK8XX_IRQ_REG_LINE(RK817_IRQ_RTC_ALARM, 8), + RK8XX_IRQ_REG_LINE(RK817_IRQ_RTC_PERIOD, 8), + RK8XX_IRQ_REG_LINE(RK817_IRQ_VB_LO, 8), + RK8XX_IRQ_REG_LINE(RK817_IRQ_PLUG_IN, 8), + RK8XX_IRQ_REG_LINE(RK817_IRQ_PLUG_OUT, 8), + RK8XX_IRQ_REG_LINE(RK817_IRQ_CHRG_TERM, 8), + RK8XX_IRQ_REG_LINE(RK817_IRQ_CHRG_TIME, 8), + RK8XX_IRQ_REG_LINE(RK817_IRQ_CHRG_TS, 8), + RK8XX_IRQ_REG_LINE(RK817_IRQ_USB_OV, 8), + RK8XX_IRQ_REG_LINE(RK817_IRQ_CHRG_IN_CLMP, 8), + RK8XX_IRQ_REG_LINE(RK817_IRQ_BAT_DIS_ILIM, 8), + RK8XX_IRQ_REG_LINE(RK817_IRQ_GATE_GPIO, 8), + RK8XX_IRQ_REG_LINE(RK817_IRQ_TS_GPIO, 8), + RK8XX_IRQ_REG_LINE(RK817_IRQ_CODEC_PD, 8), + RK8XX_IRQ_REG_LINE(RK817_IRQ_CODEC_PO, 8), + RK8XX_IRQ_REG_LINE(RK817_IRQ_CLASSD_MUTE_DONE, 8), + RK8XX_IRQ_REG_LINE(RK817_IRQ_CLASSD_OCP, 8), + RK8XX_IRQ_REG_LINE(RK817_IRQ_BAT_OVP, 8), + RK8XX_IRQ_REG_LINE(RK817_IRQ_CHRG_BAT_HI, 8) +}; + +static const int rk808_rtc_irqs[] = +{ + RK808_IRQ_RTC_ALARM, +}; + +static const int rk817_rtc_irqs[] = +{ + RK817_IRQ_RTC_ALARM, +}; + +static const int rk805_pwrkey_irqs[] = +{ + RK805_IRQ_PWRON_RISE, + RK805_IRQ_PWRON_FALL, +}; + +static const int rk806_pwrkey_irqs[] = +{ + RK806_IRQ_PWRON_FALL, + RK806_IRQ_PWRON_RISE, +}; + +static const int rk817_pwrkey_irqs[] = +{ + RK817_IRQ_PWRON_RISE, + RK817_IRQ_PWRON_FALL, +}; + +static const int rk806_watchdog_irqs[] = +{ + RK806_IRQ_WDT, +}; + +static const struct rk8xx_reg_data rk805_pre_init_regs[] = +{ + { RK805_BUCK1_CONFIG_REG, RK805_BUCK1_2_ILMAX_MASK, RK805_BUCK1_2_ILMAX_4000MA }, + { RK805_BUCK2_CONFIG_REG, RK805_BUCK1_2_ILMAX_MASK, RK805_BUCK1_2_ILMAX_4000MA }, + { RK805_BUCK3_CONFIG_REG, RK805_BUCK3_4_ILMAX_MASK, RK805_BUCK3_ILMAX_3000MA }, + { RK805_BUCK4_CONFIG_REG, RK805_BUCK3_4_ILMAX_MASK, RK805_BUCK4_ILMAX_3500MA }, + { RK805_BUCK4_CONFIG_REG, BUCK_ILMIN_MASK, BUCK_ILMIN_400MA }, + { RK805_THERMAL_REG, TEMP_HOTDIE_MSK, TEMP115C }, +}; + +static const struct rk8xx_reg_data rk806_pre_init_regs[] = +{ + { RK806_GPIO_INT_CONFIG, RK806_INT_POL_MSK, RK806_INT_POL_L }, + { RK806_SYS_CFG3, RK806_SLAVE_RESTART_FUN_MSK, RK806_SLAVE_RESTART_FUN_EN }, + { RK806_SYS_OPTION, RK806_SYS_ENB2_2M_MSK, RK806_SYS_ENB2_2M_EN }, +}; + +static const struct rk8xx_reg_data rk808_pre_init_regs[] = +{ + { RK808_BUCK3_CONFIG_REG, BUCK_ILMIN_MASK, BUCK_ILMIN_150MA }, + { RK808_BUCK4_CONFIG_REG, BUCK_ILMIN_MASK, BUCK_ILMIN_200MA }, + { RK808_BOOST_CONFIG_REG, BOOST_ILMIN_MASK, BOOST_ILMIN_100MA }, + { RK808_BUCK1_CONFIG_REG, BUCK1_RATE_MASK, BUCK_ILMIN_200MA }, + { RK808_BUCK2_CONFIG_REG, BUCK2_RATE_MASK, BUCK_ILMIN_200MA }, + { RK808_DCDC_UV_ACT_REG, BUCK_UV_ACT_MASK, BUCK_UV_ACT_DISABLE}, + { RK808_VB_MON_REG, MASK_ALL, VB_LO_ACT | VB_LO_SEL_3500MV }, +}; + +static const struct rk8xx_reg_data rk817_pre_init_regs[] = +{ + {RK817_RTC_CTRL_REG, RTC_STOP, RTC_STOP}, + /* Codec specific registers */ + { RK817_CODEC_DTOP_VUCTL, MASK_ALL, 0x03 }, + { RK817_CODEC_DTOP_VUCTIME, MASK_ALL, 0x00 }, + { RK817_CODEC_DTOP_LPT_SRST, MASK_ALL, 0x00 }, + { RK817_CODEC_DTOP_DIGEN_CLKE, MASK_ALL, 0x00 }, + /* from vendor driver, CODEC_AREF_RTCFG0 not defined in data sheet */ + { RK817_CODEC_AREF_RTCFG0, MASK_ALL, 0x00 }, + { RK817_CODEC_AREF_RTCFG1, MASK_ALL, 0x06 }, + { RK817_CODEC_AADC_CFG0, MASK_ALL, 0xc8 }, + /* from vendor driver, CODEC_AADC_CFG1 not defined in data sheet */ + { RK817_CODEC_AADC_CFG1, MASK_ALL, 0x00 }, + { RK817_CODEC_DADC_VOLL, MASK_ALL, 0x00 }, + { RK817_CODEC_DADC_VOLR, MASK_ALL, 0x00 }, + { RK817_CODEC_DADC_SR_ACL0, MASK_ALL, 0x00 }, + { RK817_CODEC_DADC_ALC1, MASK_ALL, 0x00 }, + { RK817_CODEC_DADC_ALC2, MASK_ALL, 0x00 }, + { RK817_CODEC_DADC_NG, MASK_ALL, 0x00 }, + { RK817_CODEC_DADC_HPF, MASK_ALL, 0x00 }, + { RK817_CODEC_DADC_RVOLL, MASK_ALL, 0xff }, + { RK817_CODEC_DADC_RVOLR, MASK_ALL, 0xff }, + { RK817_CODEC_AMIC_CFG0, MASK_ALL, 0x70 }, + { RK817_CODEC_AMIC_CFG1, MASK_ALL, 0x00 }, + { RK817_CODEC_DMIC_PGA_GAIN, MASK_ALL, 0x66 }, + { RK817_CODEC_DMIC_LMT1, MASK_ALL, 0x00 }, + { RK817_CODEC_DMIC_LMT2, MASK_ALL, 0x00 }, + { RK817_CODEC_DMIC_NG1, MASK_ALL, 0x00 }, + { RK817_CODEC_DMIC_NG2, MASK_ALL, 0x00 }, + /* from vendor driver, CODEC_ADAC_CFG0 not defined in data sheet */ + { RK817_CODEC_ADAC_CFG0, MASK_ALL, 0x00 }, + { RK817_CODEC_ADAC_CFG1, MASK_ALL, 0x07 }, + { RK817_CODEC_DDAC_POPD_DACST, MASK_ALL, 0x82 }, + { RK817_CODEC_DDAC_VOLL, MASK_ALL, 0x00 }, + { RK817_CODEC_DDAC_VOLR, MASK_ALL, 0x00 }, + { RK817_CODEC_DDAC_SR_LMT0, MASK_ALL, 0x00 }, + { RK817_CODEC_DDAC_LMT1, MASK_ALL, 0x00 }, + { RK817_CODEC_DDAC_LMT2, MASK_ALL, 0x00 }, + { RK817_CODEC_DDAC_MUTE_MIXCTL, MASK_ALL, 0xa0 }, + { RK817_CODEC_DDAC_RVOLL, MASK_ALL, 0xff }, + { RK817_CODEC_DADC_RVOLR, MASK_ALL, 0xff }, + { RK817_CODEC_AMIC_CFG0, MASK_ALL, 0x70 }, + { RK817_CODEC_AMIC_CFG1, MASK_ALL, 0x00 }, + { RK817_CODEC_DMIC_PGA_GAIN, MASK_ALL, 0x66 }, + { RK817_CODEC_DMIC_LMT1, MASK_ALL, 0x00 }, + { RK817_CODEC_DMIC_LMT2, MASK_ALL, 0x00 }, + { RK817_CODEC_DMIC_NG1, MASK_ALL, 0x00 }, + { RK817_CODEC_DMIC_NG2, MASK_ALL, 0x00 }, + /* from vendor driver, CODEC_ADAC_CFG0 not defined in data sheet */ + { RK817_CODEC_ADAC_CFG0, MASK_ALL, 0x00 }, + { RK817_CODEC_ADAC_CFG1, MASK_ALL, 0x07 }, + { RK817_CODEC_DDAC_POPD_DACST, MASK_ALL, 0x82 }, + { RK817_CODEC_DDAC_VOLL, MASK_ALL, 0x00 }, + { RK817_CODEC_DDAC_VOLR, MASK_ALL, 0x00 }, + { RK817_CODEC_DDAC_SR_LMT0, MASK_ALL, 0x00 }, + { RK817_CODEC_DDAC_LMT1, MASK_ALL, 0x00 }, + { RK817_CODEC_DDAC_LMT2, MASK_ALL, 0x00 }, + { RK817_CODEC_DDAC_MUTE_MIXCTL, MASK_ALL, 0xa0 }, + { RK817_CODEC_DDAC_RVOLL, MASK_ALL, 0xff }, + { RK817_CODEC_DDAC_RVOLR, MASK_ALL, 0xff }, + { RK817_CODEC_AHP_ANTI0, MASK_ALL, 0x00 }, + { RK817_CODEC_AHP_ANTI1, MASK_ALL, 0x00 }, + { RK817_CODEC_AHP_CFG0, MASK_ALL, 0xe0 }, + { RK817_CODEC_AHP_CFG1, MASK_ALL, 0x1f }, + { RK817_CODEC_AHP_CP, MASK_ALL, 0x09 }, + { RK817_CODEC_ACLASSD_CFG1, MASK_ALL, 0x69 }, + { RK817_CODEC_ACLASSD_CFG2, MASK_ALL, 0x44 }, + { RK817_CODEC_APLL_CFG0, MASK_ALL, 0x04 }, + { RK817_CODEC_APLL_CFG1, MASK_ALL, 0x00 }, + { RK817_CODEC_APLL_CFG2, MASK_ALL, 0x30 }, + { RK817_CODEC_APLL_CFG3, MASK_ALL, 0x19 }, + { RK817_CODEC_APLL_CFG4, MASK_ALL, 0x65 }, + { RK817_CODEC_APLL_CFG5, MASK_ALL, 0x01 }, + { RK817_CODEC_DI2S_CKM, MASK_ALL, 0x01 }, + { RK817_CODEC_DI2S_RSD, MASK_ALL, 0x00 }, + { RK817_CODEC_DI2S_RXCR1, MASK_ALL, 0x00 }, + { RK817_CODEC_DI2S_RXCR2, MASK_ALL, 0x17 }, + { RK817_CODEC_DI2S_RXCMD_TSD, MASK_ALL, 0x00 }, + { RK817_CODEC_DI2S_TXCR1, MASK_ALL, 0x00 }, + { RK817_CODEC_DI2S_TXCR2, MASK_ALL, 0x17 }, + { RK817_CODEC_DI2S_TXCR3_TXCMD, MASK_ALL, 0x00 }, + { RK817_GPIO_INT_CFG, RK817_INT_POL_MSK, RK817_INT_POL_L}, + { RK817_SYS_CFG(1), RK817_HOTDIE_TEMP_MSK | RK817_TSD_TEMP_MSK, RK817_HOTDIE_105 | RK817_TSD_140}, +}; + +static const struct rk8xx_reg_data rk818_pre_init_regs[] = { + /* improve efficiency */ + { RK818_BUCK2_CONFIG_REG, BUCK2_RATE_MASK, BUCK_ILMIN_250MA }, + { RK818_BUCK4_CONFIG_REG, BUCK_ILMIN_MASK, BUCK_ILMIN_250MA }, + { RK818_BOOST_CONFIG_REG, BOOST_ILMIN_MASK, BOOST_ILMIN_100MA }, + { RK818_USB_CTRL_REG, RK818_USB_ILIM_SEL_MASK, RK818_USB_ILMIN_2000MA }, + /* close charger when usb lower then 3.4V */ + { RK818_USB_CTRL_REG, RK818_USB_CHG_SD_VSEL_MASK, (0x7 << 4) }, + /* no action when vref */ + { RK818_H5V_EN_REG, RT_BIT(1), RK818_REF_RDY_CTRL }, + /* enable HDMI 5V */ + { RK818_H5V_EN_REG, RT_BIT(0), RK818_H5V_EN }, + { RK808_VB_MON_REG, MASK_ALL, VB_LO_ACT | VB_LO_SEL_3500MV }, +}; + +static const struct rk8xx_endpoint rk805_eps[] = +{ + { .name = "rk8xx-clkout", }, + { .name = "rk8xx-pinctrl", .ofw_name = "pinctrl_rk8xx" }, + { .name = "rk8xx-regulator", .ofw_name = "regulators" }, + { + .name = "rk8xx-rtc", + .irqs_nr = RT_ARRAY_SIZE(rk808_rtc_irqs), + .irqs_list = rk808_rtc_irqs, + }, + { + .name = "rk8xx-pwrkey", + .ofw_name = "pwrkey", + .irqs_nr = RT_ARRAY_SIZE(rk805_pwrkey_irqs), + .irqs_list = rk805_pwrkey_irqs, + }, +}; + +static const struct rk8xx_endpoint rk806_eps[] = +{ + { .name = "rk8xx-pinctrl", .ofw_name = "pinctrl_rk806" }, + { .name = "rk8xx-regulator", .ofw_name = "regulators" }, + { + .name = "rk8xx-pwrkey", + .ofw_name = "pwrkey", + .irqs_nr = RT_ARRAY_SIZE(rk806_pwrkey_irqs), + .irqs_list = rk806_pwrkey_irqs, + }, + { + .name = "rk8xx-watchdog", + .irqs_nr = RT_ARRAY_SIZE(rk806_watchdog_irqs), + .irqs_list = rk806_watchdog_irqs, + }, +}; + +static const struct rk8xx_endpoint rk808_eps[] = +{ + { .name = "rk8xx-clkout", }, + { .name = "rk8xx-regulator", .ofw_name = "regulators" }, + { + .name = "rk8xx-rtc", + .irqs_nr = RT_ARRAY_SIZE(rk808_rtc_irqs), + .irqs_list = rk808_rtc_irqs, + }, +}; + +static const struct rk8xx_endpoint rk817_eps[] = +{ + { .name = "rk8xx-clkout", }, + { .name = "rk8xx-regulator", .ofw_name = "regulators" }, + { + .name = "rk8xx-rtc", + .irqs_nr = RT_ARRAY_SIZE(rk817_rtc_irqs), + .irqs_list = rk817_rtc_irqs, + }, + { + .name = "rk8xx-pwrkey", + .ofw_name = "pwrkey", + .irqs_nr = RT_ARRAY_SIZE(rk817_pwrkey_irqs), + .irqs_list = rk817_pwrkey_irqs, + }, +}; + +static const struct rk8xx_endpoint rk818_eps[] = +{ + { .name = "rk8xx-clkout", }, + { .name = "rk8xx-regulator", .ofw_name = "regulators" }, + { + .name = "rk8xx-rtc", + .irqs_nr = RT_ARRAY_SIZE(rk808_rtc_irqs), + .irqs_list = rk808_rtc_irqs, + }, +}; + +static rt_err_t rk8xx_power_off(struct rt_device *dev) +{ + rt_uint32_t reg, bit; + struct rk8xx_power *rk8xx_power = raw_to_rk8xx_power(dev); + struct rk8xx *rk8xx = rk8xx_power->rk8xx; + + switch (rk8xx->variant) + { + case RK805_ID: + reg = RK805_DEV_CTRL_REG; + bit = DEV_OFF; + break; + + case RK806_ID: + reg = RK806_SYS_CFG3; + bit = DEV_OFF; + break; + + case RK808_ID: + reg = RK808_DEVCTRL_REG, + bit = DEV_OFF_RST; + break; + + case RK809_ID: + case RK817_ID: + reg = RK817_SYS_CFG(3); + bit = DEV_OFF; + break; + + case RK818_ID: + reg = RK818_DEVCTRL_REG; + bit = DEV_OFF; + break; + + default: + return -RT_EEMPTY; + } + + return rk8xx_update_bits(rk8xx, reg, bit, bit); +} + +static rt_err_t rk8xx_restart(struct rt_device *dev) +{ + rt_uint32_t reg, bit; + struct rk8xx_power *rk8xx_power = raw_to_rk8xx_power(dev); + struct rk8xx *rk8xx = rk8xx_power->rk8xx; + + switch (rk8xx->variant) + { + case RK806_ID: + reg = RK806_SYS_CFG3; + bit = DEV_RST; + break; + + case RK809_ID: + case RK817_ID: + reg = RK817_SYS_CFG(3); + bit = DEV_RST; + break; + + default: + return -RT_EEMPTY; + } + + return rk8xx_update_bits(rk8xx, reg, bit, bit); +} + +static rt_err_t create_rk8xx_platform_device(rt_bus_t platform_bus, + struct rk8xx *rk8xx, + const char *name, + void *ofw_node) +{ + rt_err_t err; + struct rt_platform_device *pdev = rt_platform_device_alloc(name); + + if (!pdev) + { + return -RT_ENOMEM; + } + + pdev->parent.ofw_node = ofw_node; + pdev->priv = rk8xx; + + err = rt_bus_add_device(platform_bus, &pdev->parent); + + if (err && err != -RT_ENOSYS) + { + LOG_E("Add RK8XX - %s error = %s", name, rt_strerror(err)); + } + + return err; +} + +static rt_err_t rk8xx_ofw_bind_irq(struct rk8xx_irqchip *rk8xx_ic, + struct rt_ofw_node *dev_np, + struct rt_ofw_node *np, + const struct rk8xx_endpoint *ep) +{ + /* + * ic: rk8xx-interrupt-controller { + * #interrupt-cells = <1>; + * + * rk8xx-endpoint0 { + * interrupts-extended = <&ic 0>; + * } + * + * rk8xx-endpoint1 { + * interrupts-extended = <&ic 1>, <&ic 2>; + * } + * } + */ + rt_err_t err; + fdt32_t *values; + rt_size_t irq_list_size; + static fdt32_t irq_cell; + static struct rt_ofw_node *ic_np = RT_NULL; + + if (!ic_np) + { + ic_np = rt_ofw_append_child(dev_np, "rk8xx-interrupt-controller"); + + if (!ic_np) + { + return -RT_ENOSYS; + } + + irq_cell = cpu_to_fdt32(1); + err = rt_ofw_append_prop(ic_np, "#interrupt-cells", sizeof(fdt32_t), &irq_cell); + + if (err) + { + return err; + } + + rt_ofw_data(ic_np) = &rk8xx_ic->parent; + } + + irq_list_size = sizeof(fdt32_t) * 2 * ep->irqs_nr; + values = rt_malloc(irq_list_size); + + if (!values) + { + return -RT_ENOMEM; + } + + for (int i = 0; i < ep->irqs_nr; ++i) + { + values[i * 2] = cpu_to_fdt32(ic_np->phandle); + values[i * 2 + 1] = cpu_to_fdt32(ep->irqs_list[i]); + } + + if ((err = rt_ofw_append_prop(np, "interrupts-extended", irq_list_size, values))) + { + rt_free(values); + } + + return err; +} + +rt_err_t rk8xx_shutdown(struct rk8xx *rk8xx) +{ + rt_err_t err; + + switch (rk8xx->variant) + { + case RK805_ID: + err = rk8xx_update_bits(rk8xx, RK805_GPIO_IO_POL_REG, SLP_SD_MSK, SHUTDOWN_FUN); + break; + + case RK809_ID: + case RK817_ID: + err = rk8xx_update_bits(rk8xx, RK817_SYS_CFG(3), RK817_SLPPIN_FUNC_MSK, SLPPIN_DN_FUN); + break; + + default: + return RT_EOK; + } + + if (err) + { + LOG_W("Cannot switch to power down function"); + } + + return err; +} + +rt_err_t rk8xx_probe(struct rk8xx *rk8xx) +{ + rt_err_t err; + rt_bool_t iomux_retry; + rt_bus_t platform_bus; + int rk8xx_ep_nr, pre_init_regs_nr; + struct rt_ofw_node *np, *dev_np; + struct rk8xx_irqchip *rk8xx_ic; + const struct rk8xx_endpoint *rk8xx_ep; + const struct rk8xx_reg_data *pre_init_regs; + + if (!rk8xx) + { + return -RT_EINVAL; + } + + platform_bus = rt_bus_find_by_name("platform"); + + if (!platform_bus) + { + return -RT_EIO; + } + + dev_np = rk8xx->dev->ofw_node; + + rk8xx_ic = rt_calloc(1, sizeof(*rk8xx_ic)); + + if (!rk8xx_ic) + { + return -RT_ENOMEM; + } + + switch (rk8xx->variant) + { + case RK805_ID: + rk8xx_ic->num_regs = 1; + rk8xx_ic->status_base = RK805_INT_STS_REG; + rk8xx_ic->mask_base = RK805_INT_STS_MSK_REG; + rk8xx_ic->ack_base = RK805_INT_STS_REG; + rk8xx_ic->irqs_nr = RT_ARRAY_SIZE(rk805_irqs); + rk8xx_ic->irqs_map = rk805_irqs; + + rk8xx_ep = rk805_eps; + rk8xx_ep_nr = RT_ARRAY_SIZE(rk805_eps); + + pre_init_regs = rk805_pre_init_regs; + pre_init_regs_nr = RT_ARRAY_SIZE(rk805_pre_init_regs); + break; + + case RK806_ID: + rk8xx_ic->num_regs = 2; + rk8xx_ic->reg_stride = 2; + rk8xx_ic->mask_base = RK806_INT_MSK0; + rk8xx_ic->status_base = RK806_INT_STS0; + rk8xx_ic->ack_base = RK806_INT_STS0; + rk8xx_ic->irqs_nr = RT_ARRAY_SIZE(rk806_irqs); + rk8xx_ic->irqs_map = rk806_irqs; + + rk8xx_ep = rk806_eps; + rk8xx_ep_nr = RT_ARRAY_SIZE(rk806_eps); + + pre_init_regs = rk806_pre_init_regs; + pre_init_regs_nr = RT_ARRAY_SIZE(rk806_pre_init_regs); + break; + + case RK808_ID: + rk8xx_ic->num_regs = 2; + rk8xx_ic->reg_stride = 2; + rk8xx_ic->status_base = RK808_INT_STS_REG1; + rk8xx_ic->mask_base = RK808_INT_STS_MSK_REG1; + rk8xx_ic->ack_base = RK808_INT_STS_REG1; + rk8xx_ic->irqs_nr = RT_ARRAY_SIZE(rk808_irqs); + rk8xx_ic->irqs_map = rk808_irqs; + + rk8xx_ep = rk808_eps; + rk8xx_ep_nr = RT_ARRAY_SIZE(rk808_eps); + + pre_init_regs = rk808_pre_init_regs; + pre_init_regs_nr = RT_ARRAY_SIZE(rk808_pre_init_regs); + break; + + case RK809_ID: + case RK817_ID: + rk8xx_ic->num_regs = 3; + rk8xx_ic->reg_stride = 2; + rk8xx_ic->status_base = RK817_INT_STS_REG0; + rk8xx_ic->mask_base = RK817_INT_STS_MSK_REG0; + rk8xx_ic->ack_base = RK817_INT_STS_REG0; + rk8xx_ic->irqs_nr = RT_ARRAY_SIZE(rk817_irqs); + rk8xx_ic->irqs_map = rk817_irqs; + + rk8xx_ep = rk817_eps; + rk8xx_ep_nr = RT_ARRAY_SIZE(rk817_eps); + + pre_init_regs = rk817_pre_init_regs; + pre_init_regs_nr = RT_ARRAY_SIZE(rk817_pre_init_regs); + break; + + case RK818_ID: + rk8xx_ic->num_regs = 2; + rk8xx_ic->reg_stride = 2; + rk8xx_ic->status_base = RK818_INT_STS_REG1; + rk8xx_ic->mask_base = RK818_INT_STS_MSK_REG1; + rk8xx_ic->ack_base = RK818_INT_STS_REG1; + rk8xx_ic->irqs_nr = RT_ARRAY_SIZE(rk818_irqs); + rk8xx_ic->irqs_map = rk818_irqs; + + rk8xx_ep = rk818_eps; + rk8xx_ep_nr = RT_ARRAY_SIZE(rk818_eps); + + pre_init_regs = rk818_pre_init_regs; + pre_init_regs_nr = RT_ARRAY_SIZE(rk818_pre_init_regs); + break; + + default: + LOG_E("Unsupported RK8XX ID %u", rk8xx->variant); + return -RT_EINVAL; + } + + RT_ASSERT(rk8xx_ic->num_regs <= RK8XX_IRQCHIP_NUM_REGS_MAX); + + rk8xx_ic->rk8xx = rk8xx; + rk8xx_ic->parent.priv_data = rk8xx_ic; + rk8xx_ic->parent.ops = &rk8xx_irq_ops; + + err = rt_pic_linear_irq(&rk8xx_ic->parent, rk8xx_ic->irqs_nr); + + if (err) + { + LOG_E("Init RK8XX IRQ chip failed error = %s", rt_strerror(err)); + + return err; + } + + /* Clear all interrupts */ + for (int i = 0; i < rk8xx_ic->num_regs; ++i) + { + rk8xx_write(rk8xx, rk8xx_ic->ack_base + i * rk8xx_ic->reg_stride, 0xff); + } + + for (int i = 0; i < pre_init_regs_nr; ++i) + { + err = rk8xx_update_bits(rk8xx, pre_init_regs[i].addr, + pre_init_regs[i].mask, pre_init_regs[i].value); + + if (err) + { + LOG_E("Write to %x fail", pre_init_regs[i].addr); + return err; + } + } + + rt_pic_user_extends(&rk8xx_ic->parent); + + iomux_retry = rt_pin_ctrl_confs_apply_by_name(rk8xx->dev, RT_NULL) < 0; + + if (rt_ofw_prop_read_bool(dev_np, "rockchip,system-power-controller")) + { + struct rt_device *dev; + struct rk8xx_power *rk8xx_power = rt_calloc(1, sizeof(*rk8xx_power)); + + if (!rk8xx_power) + { + return -RT_ENOMEM; + } + + dev = &rk8xx_power->parent; + rt_dm_dev_set_name(dev, "rk-sys-pm"); + rk8xx_power->rk8xx = rk8xx; + + err = rt_dm_power_off_handler(dev, RT_DM_POWER_OFF_MODE_SHUTDOWN, + RT_DM_POWER_OFF_PRIO_HIGH, &rk8xx_power_off); + + if (err) + { + LOG_E("Add %s failed", "shutdown"); + } + + err = rt_dm_power_off_handler(dev, RT_DM_POWER_OFF_MODE_RESET, + RT_DM_POWER_OFF_PRIO_HIGH, &rk8xx_restart); + + if (err) + { + LOG_E("Add %s failed", "reset"); + } + } + + for (int i = 0; i < rk8xx_ep_nr; ++i, ++rk8xx_ep) + { + np = RT_NULL; + + if (rk8xx_ep->ofw_name) + { + if (!(np = rt_ofw_get_child_by_tag(dev_np, rk8xx_ep->ofw_name))) + { + LOG_W("%s not found", rk8xx_ep->ofw_name); + + continue; + } + + if (!rt_ofw_node_is_available(np)) + { + continue; + } + } + + if (rk8xx_ep->irqs_nr) + { + if (!np && !(np = rt_ofw_append_child(dev_np, rk8xx_ep->name))) + { + continue; + } + + err = rk8xx_ofw_bind_irq(rk8xx_ic, dev_np, np, rk8xx_ep); + + if (err == -RT_ENOMEM) + { + goto _out_put; + } + } + + if (!rt_strcmp(rk8xx_ep->name, "rk8xx-clkout")) + { + np = rt_ofw_node_get(dev_np); + } + + err = create_rk8xx_platform_device(platform_bus, rk8xx, rk8xx_ep->name, np); + + _out_put: + rt_ofw_node_put(np); + + if (err == -RT_ENOMEM) + { + return err; + } + } + + if (iomux_retry) + { + if ((err = rt_pin_ctrl_confs_apply_by_name(rk8xx->dev, RT_NULL))) + { + LOG_W("Pinctrl apply error = %s", rt_strerror(err)); + } + } + + rk8xx_ic->irq_thread = rt_thread_create("rk8xx-pmic", &rk8xx_pmic_thread_isr, + rk8xx_ic, SYSTEM_THREAD_STACK_SIZE, RT_THREAD_PRIORITY_MAX / 2, 10); + + if (!rk8xx_ic->irq_thread) + { + goto _not_irq_conf; + } + + rt_thread_startup(rk8xx_ic->irq_thread); + + rt_hw_interrupt_install(rk8xx->irq, &rk8xx_pmic_isr, rk8xx_ic, "rk8xx-pmic"); + rt_hw_interrupt_umask(rk8xx->irq); + +_not_irq_conf: + return RT_EOK; +} diff --git a/components/drivers/mfd/rk8xx.h b/components/drivers/mfd/rk8xx.h new file mode 100644 index 000000000000..609693761d90 --- /dev/null +++ b/components/drivers/mfd/rk8xx.h @@ -0,0 +1,912 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-11-26 GuEe-GUI first version + */ + +#ifndef __RK8XX_H__ +#define __RK8XX_H__ + +#include +#include + +/* CONFIG REGISTER */ +#define RK805_VB_MON_REG 0x21 +#define RK805_THERMAL_REG 0x22 + +/* POWER CHANNELS ENABLE REGISTER */ +#define RK805_DCDC_EN_REG 0x23 +#define RK805_SLP_DCDC_EN_REG 0x25 +#define RK805_SLP_LDO_EN_REG 0x26 +#define RK805_LDO_EN_REG 0x27 + +/* BUCK AND LDO CONFIG REGISTER */ +#define RK805_BUCK_LDO_SLP_LP_EN_REG 0x2a +#define RK805_BUCK1_CONFIG_REG 0x2e +#define RK805_BUCK1_ON_VSEL_REG 0x2f +#define RK805_BUCK1_SLP_VSEL_REG 0x30 +#define RK805_BUCK2_CONFIG_REG 0x32 +#define RK805_BUCK2_ON_VSEL_REG 0x33 +#define RK805_BUCK2_SLP_VSEL_REG 0x34 +#define RK805_BUCK3_CONFIG_REG 0x36 +#define RK805_BUCK4_CONFIG_REG 0x37 +#define RK805_BUCK4_ON_VSEL_REG 0x38 +#define RK805_BUCK4_SLP_VSEL_REG 0x39 +#define RK805_LDO1_ON_VSEL_REG 0x3b +#define RK805_LDO1_SLP_VSEL_REG 0x3c +#define RK805_LDO2_ON_VSEL_REG 0x3d +#define RK805_LDO2_SLP_VSEL_REG 0x3e +#define RK805_LDO3_ON_VSEL_REG 0x3f +#define RK805_LDO3_SLP_VSEL_REG 0x40 + +/* INTERRUPT REGISTER */ +#define RK805_PWRON_LP_INT_TIME_REG 0x47 +#define RK805_PWRON_DB_REG 0x48 +#define RK805_DEV_CTRL_REG 0x4b +#define RK805_INT_STS_REG 0x4c +#define RK805_INT_STS_MSK_REG 0x4d +#define RK805_GPIO_IO_POL_REG 0x50 +#define RK805_OUT_REG 0x52 +#define RK805_ON_SOURCE_REG 0xae +#define RK805_OFF_SOURCE_REG 0xaf + +#define RK805_NUM_REGULATORS 7 + +#define RK805_PWRON_FALL_RISE_INT_EN 0x0 +#define RK805_PWRON_FALL_RISE_INT_MSK 0x81 + +#define RK805_BUCK1_2_ILMAX_2500MA 0x0 +#define RK805_BUCK1_2_ILMAX_3000MA 0x1 +#define RK805_BUCK1_2_ILMAX_3500MA 0x2 +#define RK805_BUCK1_2_ILMAX_4000MA 0x3 + +#define RK805_BUCK3_ILMAX_1500MA 0x0 +#define RK805_BUCK3_ILMAX_2000MA 0x1 +#define RK805_BUCK3_ILMAX_2500MA 0x2 +#define RK805_BUCK3_ILMAX_3000MA 0x3 + +#define RK805_BUCK4_ILMAX_2000MA 0x0 +#define RK805_BUCK4_ILMAX_2500MA 0x1 +#define RK805_BUCK4_ILMAX_3000MA 0x2 +#define RK805_BUCK4_ILMAX_3500MA 0x3 + +/* RK805 IRQ Definitions */ +#define RK805_IRQ_PWRON_RISE 0 +#define RK805_IRQ_VB_LOW 1 +#define RK805_IRQ_PWRON 2 +#define RK805_IRQ_PWRON_LP 3 +#define RK805_IRQ_HOTDIE 4 +#define RK805_IRQ_RTC_ALARM 5 +#define RK805_IRQ_RTC_PERIOD 6 +#define RK805_IRQ_PWRON_FALL 7 + +#define RK805_IRQ_PWRON_RISE_MSK RT_BIT(0) +#define RK805_IRQ_VB_LOW_MSK RT_BIT(1) +#define RK805_IRQ_PWRON_MSK RT_BIT(2) +#define RK805_IRQ_PWRON_LP_MSK RT_BIT(3) +#define RK805_IRQ_HOTDIE_MSK RT_BIT(4) +#define RK805_IRQ_RTC_ALARM_MSK RT_BIT(5) +#define RK805_IRQ_RTC_PERIOD_MSK RT_BIT(6) +#define RK805_IRQ_PWRON_FALL_MSK RT_BIT(7) + +#define RK805_PWR_RISE_INT_STATUS RT_BIT(0) +#define RK805_VB_LOW_INT_STATUS RT_BIT(1) +#define RK805_PWRON_INT_STATUS RT_BIT(2) +#define RK805_PWRON_LP_INT_STATUS RT_BIT(3) +#define RK805_HOTDIE_INT_STATUS RT_BIT(4) +#define RK805_ALARM_INT_STATUS RT_BIT(5) +#define RK805_PERIOD_INT_STATUS RT_BIT(6) +#define RK805_PWR_FALL_INT_STATUS RT_BIT(7) + +#define RK805_BUCK1_2_ILMAX_MASK (3 << 6) +#define RK805_BUCK3_4_ILMAX_MASK (3 << 3) +#define RK805_RTC_PERIOD_INT_MASK (1 << 6) +#define RK805_RTC_ALARM_INT_MASK (1 << 5) +#define RK805_INT_ALARM_EN (1 << 3) +#define RK805_INT_TIMER_EN (1 << 2) + +#define RK806_POWER_EN0 0x0 +#define RK806_POWER_EN1 0x1 +#define RK806_POWER_EN2 0x2 +#define RK806_POWER_EN3 0x3 +#define RK806_POWER_EN4 0x4 +#define RK806_POWER_EN5 0x5 +#define RK806_POWER_SLP_EN0 0x6 +#define RK806_POWER_SLP_EN1 0x7 +#define RK806_POWER_SLP_EN2 0x8 +#define RK806_POWER_DISCHRG_EN0 0x9 +#define RK806_POWER_DISCHRG_EN1 0xa +#define RK806_POWER_DISCHRG_EN2 0xb +#define RK806_BUCK_FB_CONFIG 0xc +#define RK806_SLP_LP_CONFIG 0xd +#define RK806_POWER_FPWM_EN0 0xe +#define RK806_POWER_FPWM_EN1 0xf +#define RK806_BUCK1_CONFIG 0x10 +#define RK806_BUCK2_CONFIG 0x11 +#define RK806_BUCK3_CONFIG 0x12 +#define RK806_BUCK4_CONFIG 0x13 +#define RK806_BUCK5_CONFIG 0x14 +#define RK806_BUCK6_CONFIG 0x15 +#define RK806_BUCK7_CONFIG 0x16 +#define RK806_BUCK8_CONFIG 0x17 +#define RK806_BUCK9_CONFIG 0x18 +#define RK806_BUCK10_CONFIG 0x19 +#define RK806_BUCK1_ON_VSEL 0x1a +#define RK806_BUCK2_ON_VSEL 0x1b +#define RK806_BUCK3_ON_VSEL 0x1c +#define RK806_BUCK4_ON_VSEL 0x1d +#define RK806_BUCK5_ON_VSEL 0x1e +#define RK806_BUCK6_ON_VSEL 0x1f +#define RK806_BUCK7_ON_VSEL 0x20 +#define RK806_BUCK8_ON_VSEL 0x21 +#define RK806_BUCK9_ON_VSEL 0x22 +#define RK806_BUCK10_ON_VSEL 0x23 +#define RK806_BUCK1_SLP_VSEL 0x24 +#define RK806_BUCK2_SLP_VSEL 0x25 +#define RK806_BUCK3_SLP_VSEL 0x26 +#define RK806_BUCK4_SLP_VSEL 0x27 +#define RK806_BUCK5_SLP_VSEL 0x28 +#define RK806_BUCK6_SLP_VSEL 0x29 +#define RK806_BUCK7_SLP_VSEL 0x2a +#define RK806_BUCK8_SLP_VSEL 0x2b +#define RK806_BUCK9_SLP_VSEL 0x2d +#define RK806_BUCK10_SLP_VSEL 0x2e +#define RK806_BUCK_DEBUG1 0x30 +#define RK806_BUCK_DEBUG2 0x31 +#define RK806_BUCK_DEBUG3 0x32 +#define RK806_BUCK_DEBUG4 0x33 +#define RK806_BUCK_DEBUG5 0x34 +#define RK806_BUCK_DEBUG6 0x35 +#define RK806_BUCK_DEBUG7 0x36 +#define RK806_BUCK_DEBUG8 0x37 +#define RK806_BUCK_DEBUG9 0x38 +#define RK806_BUCK_DEBUG10 0x39 +#define RK806_BUCK_DEBUG11 0x3a +#define RK806_BUCK_DEBUG12 0x3b +#define RK806_BUCK_DEBUG13 0x3c +#define RK806_BUCK_DEBUG14 0x3d +#define RK806_BUCK_DEBUG15 0x3e +#define RK806_BUCK_DEBUG16 0x3f +#define RK806_BUCK_DEBUG17 0x40 +#define RK806_BUCK_DEBUG18 0x41 +#define RK806_NLDO_IMAX 0x42 +#define RK806_NLDO1_ON_VSEL 0x43 +#define RK806_NLDO2_ON_VSEL 0x44 +#define RK806_NLDO3_ON_VSEL 0x45 +#define RK806_NLDO4_ON_VSEL 0x46 +#define RK806_NLDO5_ON_VSEL 0x47 +#define RK806_NLDO1_SLP_VSEL 0x48 +#define RK806_NLDO2_SLP_VSEL 0x49 +#define RK806_NLDO3_SLP_VSEL 0x4a +#define RK806_NLDO4_SLP_VSEL 0x4b +#define RK806_NLDO5_SLP_VSEL 0x4c +#define RK806_PLDO_IMAX 0x4d +#define RK806_PLDO1_ON_VSEL 0x4e +#define RK806_PLDO2_ON_VSEL 0x4f +#define RK806_PLDO3_ON_VSEL 0x50 +#define RK806_PLDO4_ON_VSEL 0x51 +#define RK806_PLDO5_ON_VSEL 0x52 +#define RK806_PLDO6_ON_VSEL 0x53 +#define RK806_PLDO1_SLP_VSEL 0x54 +#define RK806_PLDO2_SLP_VSEL 0x55 +#define RK806_PLDO3_SLP_VSEL 0x56 +#define RK806_PLDO4_SLP_VSEL 0x57 +#define RK806_PLDO5_SLP_VSEL 0x58 +#define RK806_PLDO6_SLP_VSEL 0x59 +#define RK806_CHIP_NAME 0x5a +#define RK806_CHIP_VER 0x5b +#define RK806_OTP_VER 0x5c +#define RK806_SYS_STS 0x5d +#define RK806_SYS_CFG0 0x5e +#define RK806_SYS_CFG1 0x5f +#define RK806_SYS_OPTION 0x61 +#define RK806_SLEEP_CONFIG0 0x62 +#define RK806_SLEEP_CONFIG1 0x63 +#define RK806_SLEEP_CTR_SEL0 0x64 +#define RK806_SLEEP_CTR_SEL1 0x65 +#define RK806_SLEEP_CTR_SEL2 0x66 +#define RK806_SLEEP_CTR_SEL3 0x67 +#define RK806_SLEEP_CTR_SEL4 0x68 +#define RK806_SLEEP_CTR_SEL5 0x69 +#define RK806_DVS_CTRL_SEL0 0x6a +#define RK806_DVS_CTRL_SEL1 0x6b +#define RK806_DVS_CTRL_SEL2 0x6c +#define RK806_DVS_CTRL_SEL3 0x6d +#define RK806_DVS_CTRL_SEL4 0x6e +#define RK806_DVS_CTRL_SEL5 0x6f +#define RK806_DVS_START_CTRL 0x70 +#define RK806_SLEEP_GPIO 0x71 +#define RK806_SYS_CFG3 0x72 +#define RK806_WDT 0x73 +#define RK806_ON_SOURCE 0x74 +#define RK806_OFF_SOURCE 0x75 +#define RK806_PWRON_KEY 0x76 +#define RK806_INT_STS0 0x77 +#define RK806_INT_MSK0 0x78 +#define RK806_INT_STS1 0x79 +#define RK806_INT_MSK1 0x7a +#define RK806_GPIO_INT_CONFIG 0x7b +#define RK806_DATA_REG0 0x7c +#define RK806_DATA_REG1 0x7d +#define RK806_DATA_REG2 0x7e +#define RK806_DATA_REG3 0x7f +#define RK806_DATA_REG4 0x80 +#define RK806_DATA_REG5 0x81 +#define RK806_DATA_REG6 0x82 +#define RK806_DATA_REG7 0x83 +#define RK806_DATA_REG8 0x84 +#define RK806_DATA_REG9 0x85 +#define RK806_DATA_REG10 0x86 +#define RK806_DATA_REG11 0x87 +#define RK806_DATA_REG12 0x88 +#define RK806_DATA_REG13 0x89 +#define RK806_DATA_REG14 0x8a +#define RK806_DATA_REG15 0x8b +#define RK806_TM_REG 0x8c +#define RK806_OTP_EN_REG 0x8d +#define RK806_FUNC_OTP_EN_REG 0x8e +#define RK806_TEST_REG1 0x8f +#define RK806_TEST_REG2 0x90 +#define RK806_TEST_REG3 0x91 +#define RK806_TEST_REG4 0x92 +#define RK806_TEST_REG5 0x93 +#define RK806_BUCK_VSEL_OTP_REG0 0x94 +#define RK806_BUCK_VSEL_OTP_REG1 0x95 +#define RK806_BUCK_VSEL_OTP_REG2 0x96 +#define RK806_BUCK_VSEL_OTP_REG3 0x97 +#define RK806_BUCK_VSEL_OTP_REG4 0x98 +#define RK806_BUCK_VSEL_OTP_REG5 0x99 +#define RK806_BUCK_VSEL_OTP_REG6 0x9a +#define RK806_BUCK_VSEL_OTP_REG7 0x9b +#define RK806_BUCK_VSEL_OTP_REG8 0x9c +#define RK806_BUCK_VSEL_OTP_REG9 0x9d +#define RK806_NLDO1_VSEL_OTP_REG0 0x9e +#define RK806_NLDO1_VSEL_OTP_REG1 0x9f +#define RK806_NLDO1_VSEL_OTP_REG2 0xa0 +#define RK806_NLDO1_VSEL_OTP_REG3 0xa1 +#define RK806_NLDO1_VSEL_OTP_REG4 0xa2 +#define RK806_PLDO_VSEL_OTP_REG0 0xa3 +#define RK806_PLDO_VSEL_OTP_REG1 0xa4 +#define RK806_PLDO_VSEL_OTP_REG2 0xa5 +#define RK806_PLDO_VSEL_OTP_REG3 0xa6 +#define RK806_PLDO_VSEL_OTP_REG4 0xa7 +#define RK806_PLDO_VSEL_OTP_REG5 0xa8 +#define RK806_BUCK_EN_OTP_REG1 0xa9 +#define RK806_NLDO_EN_OTP_REG1 0xaa +#define RK806_PLDO_EN_OTP_REG1 0xab +#define RK806_BUCK_FB_RES_OTP_REG1 0xac +#define RK806_OTP_RESEV_REG0 0xad +#define RK806_OTP_RESEV_REG1 0xae +#define RK806_OTP_RESEV_REG2 0xaf +#define RK806_OTP_RESEV_REG3 0xb0 +#define RK806_OTP_RESEV_REG4 0xb1 +#define RK806_BUCK_SEQ_REG0 0xb2 +#define RK806_BUCK_SEQ_REG1 0xb3 +#define RK806_BUCK_SEQ_REG2 0xb4 +#define RK806_BUCK_SEQ_REG3 0xb5 +#define RK806_BUCK_SEQ_REG4 0xb6 +#define RK806_BUCK_SEQ_REG5 0xb7 +#define RK806_BUCK_SEQ_REG6 0xb8 +#define RK806_BUCK_SEQ_REG7 0xb9 +#define RK806_BUCK_SEQ_REG8 0xba +#define RK806_BUCK_SEQ_REG9 0xbb +#define RK806_BUCK_SEQ_REG10 0xbc +#define RK806_BUCK_SEQ_REG11 0xbd +#define RK806_BUCK_SEQ_REG12 0xbe +#define RK806_BUCK_SEQ_REG13 0xbf +#define RK806_BUCK_SEQ_REG14 0xc0 +#define RK806_BUCK_SEQ_REG15 0xc1 +#define RK806_BUCK_SEQ_REG16 0xc2 +#define RK806_BUCK_SEQ_REG17 0xc3 +#define RK806_HK_TRIM_REG1 0xc4 +#define RK806_HK_TRIM_REG2 0xc5 +#define RK806_BUCK_REF_TRIM_REG1 0xc6 +#define RK806_BUCK_REF_TRIM_REG2 0xc7 +#define RK806_BUCK_REF_TRIM_REG3 0xc8 +#define RK806_BUCK_REF_TRIM_REG4 0xc9 +#define RK806_BUCK_REF_TRIM_REG5 0xca +#define RK806_BUCK_OSC_TRIM_REG1 0xcb +#define RK806_BUCK_OSC_TRIM_REG2 0xcc +#define RK806_BUCK_OSC_TRIM_REG3 0xcd +#define RK806_BUCK_OSC_TRIM_REG4 0xce +#define RK806_BUCK_OSC_TRIM_REG5 0xcf +#define RK806_BUCK_TRIM_ZCDIOS_REG1 0xd0 +#define RK806_BUCK_TRIM_ZCDIOS_REG2 0xd1 +#define RK806_NLDO_TRIM_REG1 0xd2 +#define RK806_NLDO_TRIM_REG2 0xd3 +#define RK806_NLDO_TRIM_REG3 0xd4 +#define RK806_PLDO_TRIM_REG1 0xd5 +#define RK806_PLDO_TRIM_REG2 0xd6 +#define RK806_PLDO_TRIM_REG3 0xd7 +#define RK806_TRIM_ICOMP_REG1 0xd8 +#define RK806_TRIM_ICOMP_REG2 0xd9 +#define RK806_EFUSE_CONTROL_REGH 0xda +#define RK806_FUSE_PROG_REG 0xdb +#define RK806_MAIN_FSM_STS_REG 0xdd +#define RK806_FSM_REG 0xde +#define RK806_TOP_RESEV_OFFR 0xec +#define RK806_TOP_RESEV_POR 0xed +#define RK806_BUCK_VRSN_REG1 0xee +#define RK806_BUCK_VRSN_REG2 0xef +#define RK806_NLDO_RLOAD_SEL_REG1 0xf0 +#define RK806_PLDO_RLOAD_SEL_REG1 0xf1 +#define RK806_PLDO_RLOAD_SEL_REG2 0xf2 +#define RK806_BUCK_CMIN_MX_REG1 0xf3 +#define RK806_BUCK_CMIN_MX_REG2 0xf4 +#define RK806_BUCK_FREQ_SET_REG1 0xf5 +#define RK806_BUCK_FREQ_SET_REG2 0xf6 +#define RK806_BUCK_RS_MEABS_REG1 0xf7 +#define RK806_BUCK_RS_MEABS_REG2 0xf8 +#define RK806_BUCK_RS_ZDLEB_REG1 0xf9 +#define RK806_BUCK_RS_ZDLEB_REG2 0xfa +#define RK806_BUCK_RSERVE_REG1 0xfb +#define RK806_BUCK_RSERVE_REG2 0xfc +#define RK806_BUCK_RSERVE_REG3 0xfd +#define RK806_BUCK_RSERVE_REG4 0xfe +#define RK806_BUCK_RSERVE_REG5 0xff + +#define RK806_WDT_ACT RT_BIT(4) +#define RK806_WDT_ACT_SEND_IRQ 0 +#define RK806_WDT_ACT_RESTART 1 +#define RK806_WDT_EN RT_BIT(3) +#define RK806_WDT_SET RT_GENMASK(2, 0) + +/* INT_STS Register field definitions */ +#define RK806_INT_STS_PWRON_FALL RT_BIT(0) +#define RK806_INT_STS_PWRON_RISE RT_BIT(1) +#define RK806_INT_STS_PWRON RT_BIT(2) +#define RK806_INT_STS_PWRON_LP RT_BIT(3) +#define RK806_INT_STS_HOTDIE RT_BIT(4) +#define RK806_INT_STS_VDC_RISE RT_BIT(5) +#define RK806_INT_STS_VDC_FALL RT_BIT(6) +#define RK806_INT_STS_VB_LO RT_BIT(7) +#define RK806_INT_STS_REV0 RT_BIT(0) +#define RK806_INT_STS_REV1 RT_BIT(1) +#define RK806_INT_STS_REV2 RT_BIT(2) +#define RK806_INT_STS_CRC_ERROR RT_BIT(3) +#define RK806_INT_STS_SLP3_GPIO RT_BIT(4) +#define RK806_INT_STS_SLP2_GPIO RT_BIT(5) +#define RK806_INT_STS_SLP1_GPIO RT_BIT(6) +#define RK806_INT_STS_WDT RT_BIT(7) + +#define RK806_INT_POL_MSK RT_BIT(1) +#define RK806_INT_POL_H RT_BIT(1) +#define RK806_INT_POL_L 0 + +#define RK806_SLAVE_RESTART_FUN_MSK RT_BIT(1) +#define RK806_SLAVE_RESTART_FUN_EN RT_BIT(1) +#define RK806_SLAVE_RESTART_FUN_OFF 0 + +#define RK806_SYS_ENB2_2M_MSK RT_BIT(1) +#define RK806_SYS_ENB2_2M_EN RT_BIT(1) +#define RK806_SYS_ENB2_2M_OFF 0 + +#define RK806_CMD_READ 0 +#define RK806_CMD_WRITE RT_BIT(7) +#define RK806_CMD_CRC_EN RT_BIT(6) +#define RK806_CMD_CRC_DIS 0 +#define RK806_CMD_LEN_MSK 0x0f +#define RK806_REG_H 0x00 + +/* RK806 INT_STS0 registers */ +#define RK806_IRQ_PWRON_FALL 0 +#define RK806_IRQ_PWRON_RISE 1 +#define RK806_IRQ_PWRON 2 +#define RK806_IRQ_PWRON_LP 3 +#define RK806_IRQ_HOTDIE 4 +#define RK806_IRQ_VDC_RISE 5 +#define RK806_IRQ_VDC_FALL 6 +#define RK806_IRQ_VB_LO 7 + +/* RK806 INT_STS0 registers */ +#define RK806_IRQ_REV0 8 +#define RK806_IRQ_REV1 9 +#define RK806_IRQ_REV2 10 +#define RK806_IRQ_CRC_ERROR 11 +#define RK806_IRQ_SLP3_GPIO 12 +#define RK806_IRQ_SLP2_GPIO 13 +#define RK806_IRQ_SLP1_GPIO 14 +#define RK806_IRQ_WDT 15 + +#define RK808_SECONDS_REG 0x00 +#define RK808_MINUTES_REG 0x01 +#define RK808_HOURS_REG 0x02 +#define RK808_DAYS_REG 0x03 +#define RK808_MONTHS_REG 0x04 +#define RK808_YEARS_REG 0x05 +#define RK808_WEEKS_REG 0x06 +#define RK808_ALARM_SECONDS_REG 0x08 +#define RK808_ALARM_MINUTES_REG 0x09 +#define RK808_ALARM_HOURS_REG 0x0a +#define RK808_ALARM_DAYS_REG 0x0b +#define RK808_ALARM_MONTHS_REG 0x0c +#define RK808_ALARM_YEARS_REG 0x0d +#define RK808_RTC_CTRL_REG 0x10 +#define RK808_RTC_STATUS_REG 0x11 +#define RK808_RTC_INT_REG 0x12 +#define RK808_RTC_COMP_LSB_REG 0x13 +#define RK808_RTC_COMP_MSB_REG 0x14 +#define RK808_ID_MSB 0x17 +#define RK808_ID_LSB 0x18 +#define RK808_CLK32OUT_REG 0x20 +#define RK808_VB_MON_REG 0x21 +#define RK808_THERMAL_REG 0x22 +#define RK808_DCDC_EN_REG 0x23 +#define RK808_LDO_EN_REG 0x24 +#define RK808_SLEEP_SET_OFF_REG1 0x25 +#define RK808_SLEEP_SET_OFF_REG2 0x26 +#define RK808_DCDC_UV_STS_REG 0x27 +#define RK808_DCDC_UV_ACT_REG 0x28 +#define RK808_LDO_UV_STS_REG 0x29 +#define RK808_LDO_UV_ACT_REG 0x2a +#define RK808_DCDC_PG_REG 0x2b +#define RK808_LDO_PG_REG 0x2c +#define RK808_VOUT_MON_TDB_REG 0x2d +#define RK808_BUCK1_CONFIG_REG 0x2e +#define RK808_BUCK1_ON_VSEL_REG 0x2f +#define RK808_BUCK1_SLP_VSEL_REG 0x30 +#define RK808_BUCK1_DVS_VSEL_REG 0x31 +#define RK808_BUCK2_CONFIG_REG 0x32 +#define RK808_BUCK2_ON_VSEL_REG 0x33 +#define RK808_BUCK2_SLP_VSEL_REG 0x34 +#define RK808_BUCK2_DVS_VSEL_REG 0x35 +#define RK808_BUCK3_CONFIG_REG 0x36 +#define RK808_BUCK4_CONFIG_REG 0x37 +#define RK808_BUCK4_ON_VSEL_REG 0x38 +#define RK808_BUCK4_SLP_VSEL_REG 0x39 +#define RK808_BOOST_CONFIG_REG 0x3a +#define RK808_LDO1_ON_VSEL_REG 0x3b +#define RK808_LDO1_SLP_VSEL_REG 0x3c +#define RK808_LDO2_ON_VSEL_REG 0x3d +#define RK808_LDO2_SLP_VSEL_REG 0x3e +#define RK808_LDO3_ON_VSEL_REG 0x3f +#define RK808_LDO3_SLP_VSEL_REG 0x40 +#define RK808_LDO4_ON_VSEL_REG 0x41 +#define RK808_LDO4_SLP_VSEL_REG 0x42 +#define RK808_LDO5_ON_VSEL_REG 0x43 +#define RK808_LDO5_SLP_VSEL_REG 0x44 +#define RK808_LDO6_ON_VSEL_REG 0x45 +#define RK808_LDO6_SLP_VSEL_REG 0x46 +#define RK808_LDO7_ON_VSEL_REG 0x47 +#define RK808_LDO7_SLP_VSEL_REG 0x48 +#define RK808_LDO8_ON_VSEL_REG 0x49 +#define RK808_LDO8_SLP_VSEL_REG 0x4a +#define RK808_DEVCTRL_REG 0x4b +#define RK808_INT_STS_REG1 0x4c +#define RK808_INT_STS_MSK_REG1 0x4d +#define RK808_INT_STS_REG2 0x4e +#define RK808_INT_STS_MSK_REG2 0x4f +#define RK808_IO_POL_REG 0x50 + +/* RK808 IRQ Definitions */ +#define RK808_IRQ_VOUT_LO 0 +#define RK808_IRQ_VB_LO 1 +#define RK808_IRQ_PWRON 2 +#define RK808_IRQ_PWRON_LP 3 +#define RK808_IRQ_HOTDIE 4 +#define RK808_IRQ_RTC_ALARM 5 +#define RK808_IRQ_RTC_PERIOD 6 +#define RK808_IRQ_PLUG_IN_INT 7 +#define RK808_IRQ_PLUG_OUT_INT 8 +#define RK808_NUM_IRQ 9 + +#define RK808_IRQ_VOUT_LO_MSK RT_BIT(0) +#define RK808_IRQ_VB_LO_MSK RT_BIT(1) +#define RK808_IRQ_PWRON_MSK RT_BIT(2) +#define RK808_IRQ_PWRON_LP_MSK RT_BIT(3) +#define RK808_IRQ_HOTDIE_MSK RT_BIT(4) +#define RK808_IRQ_RTC_ALARM_MSK RT_BIT(5) +#define RK808_IRQ_RTC_PERIOD_MSK RT_BIT(6) +#define RK808_IRQ_PLUG_IN_INT_MSK RT_BIT(0) +#define RK808_IRQ_PLUG_OUT_INT_MSK RT_BIT(1) + +#define RK809_BUCK5_CONFIG(i) (RK817_BOOST_OTG_CFG + (i) * 1) + +#define RK817_SECONDS_REG 0x00 +#define RK817_MINUTES_REG 0x01 +#define RK817_HOURS_REG 0x02 +#define RK817_DAYS_REG 0x03 +#define RK817_MONTHS_REG 0x04 +#define RK817_YEARS_REG 0x05 +#define RK817_WEEKS_REG 0x06 +#define RK817_ALARM_SECONDS_REG 0x07 +#define RK817_ALARM_MINUTES_REG 0x08 +#define RK817_ALARM_HOURS_REG 0x09 +#define RK817_ALARM_DAYS_REG 0x0a +#define RK817_ALARM_MONTHS_REG 0x0b +#define RK817_ALARM_YEARS_REG 0x0c +#define RK817_RTC_CTRL_REG 0xd +#define RK817_RTC_STATUS_REG 0xe +#define RK817_RTC_INT_REG 0xf +#define RK817_RTC_COMP_LSB_REG 0x10 +#define RK817_RTC_COMP_MSB_REG 0x11 + +/* RK817 Codec Registers */ +#define RK817_CODEC_DTOP_VUCTL 0x12 +#define RK817_CODEC_DTOP_VUCTIME 0x13 +#define RK817_CODEC_DTOP_LPT_SRST 0x14 +#define RK817_CODEC_DTOP_DIGEN_CLKE 0x15 +#define RK817_CODEC_AREF_RTCFG0 0x16 +#define RK817_CODEC_AREF_RTCFG1 0x17 +#define RK817_CODEC_AADC_CFG0 0x18 +#define RK817_CODEC_AADC_CFG1 0x19 +#define RK817_CODEC_DADC_VOLL 0x1a +#define RK817_CODEC_DADC_VOLR 0x1b +#define RK817_CODEC_DADC_SR_ACL0 0x1e +#define RK817_CODEC_DADC_ALC1 0x1f +#define RK817_CODEC_DADC_ALC2 0x20 +#define RK817_CODEC_DADC_NG 0x21 +#define RK817_CODEC_DADC_HPF 0x22 +#define RK817_CODEC_DADC_RVOLL 0x23 +#define RK817_CODEC_DADC_RVOLR 0x24 +#define RK817_CODEC_AMIC_CFG0 0x27 +#define RK817_CODEC_AMIC_CFG1 0x28 +#define RK817_CODEC_DMIC_PGA_GAIN 0x29 +#define RK817_CODEC_DMIC_LMT1 0x2a +#define RK817_CODEC_DMIC_LMT2 0x2b +#define RK817_CODEC_DMIC_NG1 0x2c +#define RK817_CODEC_DMIC_NG2 0x2d +#define RK817_CODEC_ADAC_CFG0 0x2e +#define RK817_CODEC_ADAC_CFG1 0x2f +#define RK817_CODEC_DDAC_POPD_DACST 0x30 +#define RK817_CODEC_DDAC_VOLL 0x31 +#define RK817_CODEC_DDAC_VOLR 0x32 +#define RK817_CODEC_DDAC_SR_LMT0 0x35 +#define RK817_CODEC_DDAC_LMT1 0x36 +#define RK817_CODEC_DDAC_LMT2 0x37 +#define RK817_CODEC_DDAC_MUTE_MIXCTL 0x38 +#define RK817_CODEC_DDAC_RVOLL 0x39 +#define RK817_CODEC_DDAC_RVOLR 0x3a +#define RK817_CODEC_AHP_ANTI0 0x3b +#define RK817_CODEC_AHP_ANTI1 0x3c +#define RK817_CODEC_AHP_CFG0 0x3d +#define RK817_CODEC_AHP_CFG1 0x3e +#define RK817_CODEC_AHP_CP 0x3f +#define RK817_CODEC_ACLASSD_CFG1 0x40 +#define RK817_CODEC_ACLASSD_CFG2 0x41 +#define RK817_CODEC_APLL_CFG0 0x42 +#define RK817_CODEC_APLL_CFG1 0x43 +#define RK817_CODEC_APLL_CFG2 0x44 +#define RK817_CODEC_APLL_CFG3 0x45 +#define RK817_CODEC_APLL_CFG4 0x46 +#define RK817_CODEC_APLL_CFG5 0x47 +#define RK817_CODEC_DI2S_CKM 0x48 +#define RK817_CODEC_DI2S_RSD 0x49 +#define RK817_CODEC_DI2S_RXCR1 0x4a +#define RK817_CODEC_DI2S_RXCR2 0x4b +#define RK817_CODEC_DI2S_RXCMD_TSD 0x4c +#define RK817_CODEC_DI2S_TXCR1 0x4d +#define RK817_CODEC_DI2S_TXCR2 0x4e +#define RK817_CODEC_DI2S_TXCR3_TXCMD 0x4f + +#define RK817_POWER_EN_REG(i) (0xb1 + (i)) +#define RK817_POWER_SLP_EN_REG(i) (0xb5 + (i)) + +#define RK817_POWER_CONFIG (0xb9) + +#define RK817_BUCK_CONFIG_REG(i) (0xba + (i) * 3) + +#define RK817_BUCK1_ON_VSEL_REG 0xbb +#define RK817_BUCK1_SLP_VSEL_REG 0xbc + +#define RK817_BUCK2_CONFIG_REG 0xbd +#define RK817_BUCK2_ON_VSEL_REG 0xbe +#define RK817_BUCK2_SLP_VSEL_REG 0xbf + +#define RK817_BUCK3_CONFIG_REG 0xc0 +#define RK817_BUCK3_ON_VSEL_REG 0xc1 +#define RK817_BUCK3_SLP_VSEL_REG 0xc2 + +#define RK817_BUCK4_CONFIG_REG 0xc3 +#define RK817_BUCK4_ON_VSEL_REG 0xc4 +#define RK817_BUCK4_SLP_VSEL_REG 0xc5 + +#define RK817_LDO_ON_VSEL_REG(idx) (0xcc + (idx) * 2) +#define RK817_BOOST_OTG_CFG (0xde) + +#define RK817_HOTDIE_TEMP_MSK (0x3 << 4) +#define RK817_HOTDIE_85 (0x0 << 4) +#define RK817_HOTDIE_95 (0x1 << 4) +#define RK817_HOTDIE_105 (0x2 << 4) +#define RK817_HOTDIE_115 (0x3 << 4) + +#define RK817_TSD_TEMP_MSK RT_BIT(6) +#define RK817_TSD_140 0 +#define RK817_TSD_160 RT_BIT(6) + +#define RK817_INT_POL_MSK RT_BIT(1) +#define RK817_INT_POL_H RT_BIT(1) +#define RK817_INT_POL_L 0 + +enum rk817_reg_id +{ + RK817_ID_DCDC1 = 0, + RK817_ID_DCDC2, + RK817_ID_DCDC3, + RK817_ID_DCDC4, + RK817_ID_LDO1, + RK817_ID_LDO2, + RK817_ID_LDO3, + RK817_ID_LDO4, + RK817_ID_LDO5, + RK817_ID_LDO6, + RK817_ID_LDO7, + RK817_ID_LDO8, + RK817_ID_LDO9, + RK817_ID_BOOST, + RK817_ID_BOOST_OTG_SW, + RK817_NUM_REGULATORS +}; + +#define RK817_SYS_CFG(i) ((i) + 0xf1) + +#define RK817_CLK32KOUT2_EN RT_BIT(7) + +#define RK817_SLPPIN_FUNC_MSK (0x3 << 3) +#define SLPPIN_NULL_FUN (0x0 << 3) +#define SLPPIN_SLP_FUN (0x1 << 3) +#define SLPPIN_DN_FUN (0x2 << 3) +#define SLPPIN_RST_FUN (0x3 << 3) + +#define RK817_RST_FUNC_MSK (0x3 << 6) +#define RK817_RST_FUNC_SFT (6) +#define RK817_RST_FUNC_CNT (3) +#define RK817_RST_FUNC_DEV (0) /* reset the dev */ +#define RK817_RST_FUNC_REG (0x1 << 6) /* reset the reg only */ + +/* INTERRUPT REGISTER */ +#define RK817_INT_STS_REG0 0xf8 +#define RK817_INT_STS_MSK_REG0 0xf9 +#define RK817_INT_STS_REG1 0xfa +#define RK817_INT_STS_MSK_REG1 0xfb +#define RK817_INT_STS_REG2 0xfc +#define RK817_INT_STS_MSK_REG2 0xfd +#define RK817_GPIO_INT_CFG 0xfe + +/* IRQ Definitions */ +#define RK817_IRQ_PWRON_FALL 0 +#define RK817_IRQ_PWRON_RISE 1 +#define RK817_IRQ_PWRON 2 +#define RK817_IRQ_PWMON_LP 3 +#define RK817_IRQ_HOTDIE 4 +#define RK817_IRQ_RTC_ALARM 5 +#define RK817_IRQ_RTC_PERIOD 6 +#define RK817_IRQ_VB_LO 7 +#define RK817_IRQ_PLUG_IN 8 +#define RK817_IRQ_PLUG_OUT 9 +#define RK817_IRQ_CHRG_TERM 10 +#define RK817_IRQ_CHRG_TIME 11 +#define RK817_IRQ_CHRG_TS 12 +#define RK817_IRQ_USB_OV 13 +#define RK817_IRQ_CHRG_IN_CLMP 14 +#define RK817_IRQ_BAT_DIS_ILIM 15 +#define RK817_IRQ_GATE_GPIO 16 +#define RK817_IRQ_TS_GPIO 17 +#define RK817_IRQ_CODEC_PD 18 +#define RK817_IRQ_CODEC_PO 19 +#define RK817_IRQ_CLASSD_MUTE_DONE 20 +#define RK817_IRQ_CLASSD_OCP 21 +#define RK817_IRQ_BAT_OVP 22 +#define RK817_IRQ_CHRG_BAT_HI 23 + +#define RK818_DCDC1 0 +#define RK818_LDO1 4 +#define RK818_NUM_REGULATORS 17 + +enum rk818_reg +{ + RK818_ID_DCDC1, + RK818_ID_DCDC2, + RK818_ID_DCDC3, + RK818_ID_DCDC4, + RK818_ID_BOOST, + RK818_ID_LDO1, + RK818_ID_LDO2, + RK818_ID_LDO3, + RK818_ID_LDO4, + RK818_ID_LDO5, + RK818_ID_LDO6, + RK818_ID_LDO7, + RK818_ID_LDO8, + RK818_ID_LDO9, + RK818_ID_SWITCH, + RK818_ID_HDMI_SWITCH, + RK818_ID_OTG_SWITCH, +}; + +#define RK818_DCDC_EN_REG 0x23 +#define RK818_LDO_EN_REG 0x24 +#define RK818_SLEEP_SET_OFF_REG1 0x25 +#define RK818_SLEEP_SET_OFF_REG2 0x26 +#define RK818_DCDC_UV_STS_REG 0x27 +#define RK818_DCDC_UV_ACT_REG 0x28 +#define RK818_LDO_UV_STS_REG 0x29 +#define RK818_LDO_UV_ACT_REG 0x2a +#define RK818_DCDC_PG_REG 0x2b +#define RK818_LDO_PG_REG 0x2c +#define RK818_VOUT_MON_TDB_REG 0x2d +#define RK818_BUCK1_CONFIG_REG 0x2e +#define RK818_BUCK1_ON_VSEL_REG 0x2f +#define RK818_BUCK1_SLP_VSEL_REG 0x30 +#define RK818_BUCK2_CONFIG_REG 0x32 +#define RK818_BUCK2_ON_VSEL_REG 0x33 +#define RK818_BUCK2_SLP_VSEL_REG 0x34 +#define RK818_BUCK3_CONFIG_REG 0x36 +#define RK818_BUCK4_CONFIG_REG 0x37 +#define RK818_BUCK4_ON_VSEL_REG 0x38 +#define RK818_BUCK4_SLP_VSEL_REG 0x39 +#define RK818_BOOST_CONFIG_REG 0x3a +#define RK818_LDO1_ON_VSEL_REG 0x3b +#define RK818_LDO1_SLP_VSEL_REG 0x3c +#define RK818_LDO2_ON_VSEL_REG 0x3d +#define RK818_LDO2_SLP_VSEL_REG 0x3e +#define RK818_LDO3_ON_VSEL_REG 0x3f +#define RK818_LDO3_SLP_VSEL_REG 0x40 +#define RK818_LDO4_ON_VSEL_REG 0x41 +#define RK818_LDO4_SLP_VSEL_REG 0x42 +#define RK818_LDO5_ON_VSEL_REG 0x43 +#define RK818_LDO5_SLP_VSEL_REG 0x44 +#define RK818_LDO6_ON_VSEL_REG 0x45 +#define RK818_LDO6_SLP_VSEL_REG 0x46 +#define RK818_LDO7_ON_VSEL_REG 0x47 +#define RK818_LDO7_SLP_VSEL_REG 0x48 +#define RK818_LDO8_ON_VSEL_REG 0x49 +#define RK818_LDO8_SLP_VSEL_REG 0x4a +#define RK818_BOOST_LDO9_ON_VSEL_REG 0x54 +#define RK818_BOOST_LDO9_SLP_VSEL_REG 0x55 +#define RK818_DEVCTRL_REG 0x4b +#define RK818_INT_STS_REG1 0X4c +#define RK818_INT_STS_MSK_REG1 0x4d +#define RK818_INT_STS_REG2 0x4e +#define RK818_INT_STS_MSK_REG2 0x4f +#define RK818_IO_POL_REG 0x50 +#define RK818_H5V_EN_REG 0x52 +#define RK818_SLEEP_SET_OFF_REG3 0x53 +#define RK818_BOOST_LDO9_ON_VSEL_REG 0x54 +#define RK818_BOOST_LDO9_SLP_VSEL_REG 0x55 +#define RK818_BOOST_CTRL_REG 0x56 +#define RK818_DCDC_ILMAX 0x90 +#define RK818_USB_CTRL_REG 0xa1 + +#define RK818_H5V_EN RT_BIT(0) +#define RK818_REF_RDY_CTRL RT_BIT(1) +#define RK818_USB_ILIM_SEL_MASK 0xf +#define RK818_USB_ILMIN_2000MA 0x7 +#define RK818_USB_CHG_SD_VSEL_MASK 0x70 + +/* RK818 IRQ Definitions */ +#define RK818_IRQ_VOUT_LO 0 +#define RK818_IRQ_VB_LO 1 +#define RK818_IRQ_PWRON 2 +#define RK818_IRQ_PWRON_LP 3 +#define RK818_IRQ_HOTDIE 4 +#define RK818_IRQ_RTC_ALARM 5 +#define RK818_IRQ_RTC_PERIOD 6 +#define RK818_IRQ_USB_OV 7 +#define RK818_IRQ_PLUG_IN 8 +#define RK818_IRQ_PLUG_OUT 9 +#define RK818_IRQ_CHG_OK 10 +#define RK818_IRQ_CHG_TE 11 +#define RK818_IRQ_CHG_TS1 12 +#define RK818_IRQ_TS2 13 +#define RK818_IRQ_CHG_CVTLIM 14 +#define RK818_IRQ_DISCHG_ILIM 15 + +#define RK818_IRQ_VOUT_LO_MSK RT_BIT(0) +#define RK818_IRQ_VB_LO_MSK RT_BIT(1) +#define RK818_IRQ_PWRON_MSK RT_BIT(2) +#define RK818_IRQ_PWRON_LP_MSK RT_BIT(3) +#define RK818_IRQ_HOTDIE_MSK RT_BIT(4) +#define RK818_IRQ_RTC_ALARM_MSK RT_BIT(5) +#define RK818_IRQ_RTC_PERIOD_MSK RT_BIT(6) +#define RK818_IRQ_USB_OV_MSK RT_BIT(7) +#define RK818_IRQ_PLUG_IN_MSK RT_BIT(0) +#define RK818_IRQ_PLUG_OUT_MSK RT_BIT(1) +#define RK818_IRQ_CHG_OK_MSK RT_BIT(2) +#define RK818_IRQ_CHG_TE_MSK RT_BIT(3) +#define RK818_IRQ_CHG_TS1_MSK RT_BIT(4) +#define RK818_IRQ_TS2_MSK RT_BIT(5) +#define RK818_IRQ_CHG_CVTLIM_MSK RT_BIT(6) +#define RK818_IRQ_DISCHG_ILIM_MSK RT_BIT(7) +#define RK818_NUM_IRQ 16 + +#define BUCK_ILMIN_MASK (7 << 0) +#define BOOST_ILMIN_MASK (7 << 0) +#define BUCK1_RATE_MASK (3 << 3) +#define BUCK2_RATE_MASK (3 << 3) +#define MASK_ALL 0xff + +#define BUCK_UV_ACT_MASK 0x0f +#define BUCK_UV_ACT_DISABLE 0 + +#define SWITCH2_EN RT_BIT(6) +#define SWITCH1_EN RT_BIT(5) +#define DEV_OFF_RST RT_BIT(3) +#define DEV_RST RT_BIT(2) +#define DEV_OFF RT_BIT(0) +#define RTC_STOP RT_BIT(0) + +#define VB_LO_ACT RT_BIT(4) +#define VB_LO_SEL_3500MV (7 << 0) + +#define VOUT_LO_INT RT_BIT(0) +#define CLK32KOUT2_EN RT_BIT(0) + +#define TEMP115C 0x0c +#define TEMP_HOTDIE_MSK 0x0c +#define SLP_SD_MSK (0x3 << 2) +#define SHUTDOWN_FUN (0x2 << 2) +#define SLEEP_FUN (0x1 << 2) +#define RK8XX_ID_MSK 0xfff0 +#define PWM_MODE_MSK RT_BIT(7) +#define FPWM_MODE RT_BIT(7) +#define AUTO_PWM_MODE 0 + +#define BUCK_ILMIN_50MA 0 +#define BUCK_ILMIN_100MA 1 +#define BUCK_ILMIN_150MA 2 +#define BUCK_ILMIN_200MA 3 +#define BUCK_ILMIN_250MA 4 +#define BUCK_ILMIN_300MA 5 +#define BUCK_ILMIN_350MA 6 +#define BUCK_ILMIN_400MA 7 + +#define BOOST_ILMIN_75MA 0 +#define BOOST_ILMIN_100MA 1 +#define BOOST_ILMIN_125MA 2 +#define BOOST_ILMIN_150MA 3 +#define BOOST_ILMIN_175MA 4 +#define BOOST_ILMIN_200MA 5 +#define BOOST_ILMIN_225MA 6 +#define BOOST_ILMIN_250MA 7 + +enum +{ + RK805_ID = 0x8050, + RK806_ID = 0x8060, + RK808_ID = 0x0000, + RK809_ID = 0x8090, + RK817_ID = 0x8170, + RK818_ID = 0x8180, +}; + +struct rk8xx +{ + int variant; + + int irq; + struct rt_device *dev; + + rt_uint32_t (*read)(struct rk8xx *, rt_uint16_t reg); + rt_err_t (*write)(struct rk8xx *, rt_uint16_t reg, rt_uint8_t data); + rt_err_t (*update_bits)(struct rk8xx *, rt_uint16_t reg, rt_uint8_t mask, + rt_uint8_t data); +}; + +#define rk8xx_to_i2c_client(rk8xx) rt_container_of((rk8xx)->dev, struct rt_i2c_client, parent) +#define rk8xx_to_spi_device(rk8xx) rt_container_of((rk8xx)->dev, struct rt_spi_device, parent) + +rt_inline rt_uint32_t rk8xx_read(struct rk8xx *rk8xx, rt_uint16_t reg) +{ + return rk8xx->read(rk8xx, reg); +} + +rt_inline rt_err_t rk8xx_write(struct rk8xx *rk8xx, rt_uint16_t reg, rt_uint8_t data) +{ + return rk8xx->write(rk8xx, reg, data); +} + +rt_inline rt_err_t rk8xx_update_bits(struct rk8xx *rk8xx, rt_uint16_t reg, + rt_uint8_t mask, rt_uint8_t data) +{ + return rk8xx->update_bits(rk8xx, reg, mask, data); +} + +rt_err_t rk8xx_probe(struct rk8xx *rk8xx); +rt_err_t rk8xx_shutdown(struct rk8xx *rk8xx); + +#endif /* __RK8XX_H__ */ diff --git a/components/drivers/misc/SConscript b/components/drivers/misc/SConscript index 18db8bad4831..049b6aaedc8a 100644 --- a/components/drivers/misc/SConscript +++ b/components/drivers/misc/SConscript @@ -5,15 +5,9 @@ src = [] CPPPATH = [cwd + '/../include'] group = [] -if GetDepend(['RT_USING_ADC']): - src = src + ['adc.c'] - if GetDepend(['RT_USING_DAC']): src = src + ['dac.c'] -if GetDepend(['RT_USING_PWM']): - src = src + ['rt_drv_pwm.c'] - if GetDepend(['RT_USING_PULSE_ENCODER']): src = src + ['pulse_encoder.c'] diff --git a/components/drivers/mtd/mtd-cfi.c b/components/drivers/mtd/mtd-cfi.c index 3940cbaf39f2..d91e58e14dd6 100755 --- a/components/drivers/mtd/mtd-cfi.c +++ b/components/drivers/mtd/mtd-cfi.c @@ -90,8 +90,7 @@ static const rt_uint32_t offset_multiply[9][5] = {0, 16, 8, 0, 8}, /* port width = 8 (64-bit) */ }; -#if DBG_LVL >= DBG_INFO -const char *const vendor_name[] = +rt_used const char *const vendor_name[] = { [CFI_CMDSET_INTEL_EXTENDED] = "Intel/Sharp extended", [CFI_CMDSET_AMD_STANDARD] = "AMD/Fujitsu standard", @@ -100,7 +99,6 @@ const char *const vendor_name[] = [CFI_CMDSET_MITSU_STANDARD] = "Mitsubishi standard", [CFI_CMDSET_MITSU_EXTENDED] = "Mitsubishi extendend", }; -#endif static rt_uint8_t *cfi_flash_make_addr(struct cfi_flash_map *maps, rt_int32_t sect, rt_uint32_t offset) { @@ -111,53 +109,28 @@ static rt_uint8_t *cfi_flash_make_addr(struct cfi_flash_map *maps, rt_int32_t se static rt_uint8_t cfi_flash_read_u8(struct cfi_flash_map *maps, rt_uint32_t offset) { - rt_uint8_t value; - rt_uint8_t *cp = cfi_flash_make_addr(maps, 0, offset); - -#ifndef ARCH_CPU_BIG_ENDIAN - value = cp[0]; -#else - value = (cp[maps->portwidth - 1]); -#endif + void *addr = cfi_flash_make_addr(maps, 0, offset); - return value; + return HWREG8(addr); } static rt_uint16_t cfi_flash_read_u16(struct cfi_flash_map *maps, rt_int32_t sect, rt_uint32_t offset) { - rt_uint16_t value; - rt_uint8_t *addr = cfi_flash_make_addr(maps, sect, offset); - -#ifndef ARCH_CPU_BIG_ENDIAN - value = (addr[(maps->portwidth)] << 8) | addr[0]; -#else - value = (rt_uint16_t)((addr[(2 * maps->portwidth) - 1] << 8) | addr[maps->portwidth - 1]); -#endif + void *addr = cfi_flash_make_addr(maps, sect, offset); - return value; + return rt_cpu_to_le16(HWREG16(addr)); } static rt_uint32_t cfi_flash_read_u32(struct cfi_flash_map *maps, rt_int32_t sect, rt_uint32_t offset) { - rt_uint32_t value; - rt_uint8_t *addr = cfi_flash_make_addr(maps, sect, offset); + void *addr = cfi_flash_make_addr(maps, sect, offset); -#ifndef ARCH_CPU_BIG_ENDIAN - value = (addr[0] << 16) | - (addr[(maps->portwidth)] << 24) | - (addr[(2 * maps->portwidth)]) | (addr[(3 * maps->portwidth)] << 8); -#else - value = (rt_uint32_t)(addr[(2 * maps->portwidth) - 1] << 24) | - (addr[(maps->portwidth) - 1] << 16) | - (addr[(4 * maps->portwidth) - 1] << 8) | addr[(3 * maps->portwidth) - 1]; -#endif - - return value; + return rt_cpu_to_le32(HWREG32(addr)); } static void cfi_flash_make_cmd(struct cfi_flash_map *maps, rt_uint8_t cmd, void *cmdbuf) { - rt_uint8_t *cp = (rt_uint8_t *) cmdbuf; + rt_uint8_t *cp = (rt_uint8_t *)cmdbuf; if (maps->chipwidth < CFI_FLASH_BY32) { @@ -261,7 +234,7 @@ static void cfi_flash_add_byte(struct cfi_flash_map *maps, union cfiword *cword, case CFI_FLASH_16BIT: #ifndef ARCH_CPU_BIG_ENDIAN { - unsigned short w = c; + rt_uint16_t w = c; w <<= 8; cword->w = (cword->w >> 8) | w; } @@ -313,7 +286,7 @@ static rt_err_t cfi_flash_status_check(struct cfi_flash_map *maps, union cfiptr data = *cptr.wp; /* test to see if bit6 is NOT toggling */ - if ((data & 0xFFFF) == (lastdata & 0xFFFF)) + if ((data & 0xffff) == (lastdata & 0xffff)) { ready = 1; } @@ -325,7 +298,7 @@ static rt_err_t cfi_flash_status_check(struct cfi_flash_map *maps, union cfiptr data = *cptr.wp; /* test to see if bit6 is toggling */ - if ((data & 0xFFFF) != (lastdata & 0xFFFF)) + if ((data & 0xffff) != (lastdata & 0xffff)) { err = -RT_ERROR; } @@ -335,7 +308,6 @@ static rt_err_t cfi_flash_status_check(struct cfi_flash_map *maps, union cfiptr lastdata = data; } - if (err || ready == 0) { LOG_E("error wait for flash ready, status = 0x%04x", data); @@ -346,8 +318,8 @@ static rt_err_t cfi_flash_status_check(struct cfi_flash_map *maps, union cfiptr return err; } -static rt_int32_t cfi_flash_full_status_check(struct cfi_flash_map *maps, - union cfiptr cptr, union cfiword * cword, rt_uint32_t tout, char *prompt) +static rt_err_t cfi_flash_full_status_check(struct cfi_flash_map *maps, + union cfiptr cptr, union cfiword *cword, rt_uint32_t tout, char *prompt) { rt_err_t err; @@ -505,10 +477,16 @@ static rt_err_t cfi_flash_write_cfibuffer(struct cfi_flash_map *maps, rt_ubase_t default: return -RT_EINVAL; - break; } - cfi_flash_write_cmd(maps, sector, 0, (rt_uint8_t) cnt - 1); + if (maps->portwidth == CFI_FLASH_8BIT) + { + cfi_flash_write_cmd(maps, sector, 0, (rt_uint8_t)cnt - 1); + } + else + { + HWREG8(cfi_flash_make_addr(maps, sector, 0)) = (rt_uint8_t)cnt - 1; + } while (cnt-- > 0) { @@ -540,7 +518,7 @@ static rt_err_t cfi_flash_write_cfibuffer(struct cfi_flash_map *maps, rt_ubase_t case CFI_CMDSET_AMD_STANDARD: case CFI_CMDSET_AMD_EXTENDED: src.cp = cp; - dst.cp = (rt_uint8_t *) dest; + dst.cp = (rt_uint8_t *)dest; sector = cfi_find_sector(maps, dest); cfi_flash_unlock_seq(maps, 0); @@ -550,7 +528,7 @@ static rt_err_t cfi_flash_write_cfibuffer(struct cfi_flash_map *maps, rt_ubase_t { case CFI_FLASH_8BIT: cnt = len; - cfi_flash_write_cmd(maps, sector, 0, (rt_uint8_t) cnt - 1); + cfi_flash_write_cmd(maps, sector, 0, (rt_uint8_t)cnt - 1); while (cnt-- > 0) { *dst.cp++ = *src.cp++; @@ -559,7 +537,7 @@ static rt_err_t cfi_flash_write_cfibuffer(struct cfi_flash_map *maps, rt_ubase_t case CFI_FLASH_16BIT: cnt = len >> 1; - cfi_flash_write_cmd(maps, sector, 0, (rt_uint8_t) cnt - 1); + cfi_flash_write_cmd(maps, sector, 0, (rt_uint8_t)cnt - 1); while (cnt-- > 0) { *dst.wp++ = *src.wp++; @@ -583,11 +561,25 @@ static rt_err_t cfi_flash_write_cfibuffer(struct cfi_flash_map *maps, rt_ubase_t } } +static void cfi_flash_reset(struct cfi_flash_map *maps) +{ + /* + * We do not yet know what kind of commandset to use, so we issue + * the reset command in both Intel and AMD variants, in the hope + * that AMD flash roms ignore the Intel command. + */ + cfi_flash_write_cmd(maps, 0, 0, CFI_AMD_CMD_RESET); + rt_hw_us_delay(1); + cfi_flash_write_cmd(maps, 0, 0, CFI_FLASH_CMD_RESET); +} + static rt_err_t cfi_flash_detect(struct cfi_flash *cfi, int map) { union cfiptr cptr1, cptr2, cptr3; struct cfi_flash_map *maps = &cfi->maps[map]; + cfi_flash_reset(maps); + for (maps->portwidth = CFI_FLASH_8BIT; maps->portwidth <= CFI_FLASH_16BIT; maps->portwidth <<= 1) @@ -631,7 +623,7 @@ static rt_ssize_t cfi_flash_read(struct rt_mtd_nor_device *dev, rt_off_t offset, cfi_flash_write_cmd(maps, offset / maps->block_size, offset % maps->block_size, maps->cmd_reset); - memcpy(data, ((const void *)(maps->base + offset)), length); + rt_memcpy(data, ((const void *)(maps->base + offset)), length); rt_mutex_release(&maps->rw_lock); @@ -819,13 +811,14 @@ static rt_ssize_t cfi_flash_write(struct rt_mtd_nor_device *dev, rt_off_t offset static rt_err_t cfi_flash_erase_block(struct rt_mtd_nor_device *dev, rt_off_t offset, rt_size_t length) { union cfiptr cptr; - union cfiword cword; + union cfiword cword = { .c = 0xff }; rt_int32_t sect; rt_int32_t prot = 0; + rt_err_t err = RT_EOK; struct cfi_flash_map *maps = raw_to_cfi_flash_map(dev); rt_off_t sect_start = offset / maps->block_size, sect_end = (offset + length) / maps->block_size; - cword.c = 0xff; + rt_mutex_take(&maps->rw_lock, RT_WAITING_FOREVER); for (sect = sect_start; sect <= sect_end; ++sect) { @@ -839,7 +832,8 @@ static rt_err_t cfi_flash_erase_block(struct rt_mtd_nor_device *dev, rt_off_t of { LOG_W("%d protected sectors will not be erased", prot); - return -RT_EIO; + err = -RT_EIO; + goto _out_lock; } if (sect_start == 0 && sect_end == (maps->sect_count - 1) @@ -857,7 +851,8 @@ static rt_err_t cfi_flash_erase_block(struct rt_mtd_nor_device *dev, rt_off_t of if (cfi_flash_full_status_check(maps, cptr, &cword, maps->erase_chip_tout, "chip erase")) { - return -RT_ERROR; + err = -RT_ERROR; + goto _out_lock; } } else @@ -893,13 +888,17 @@ static rt_err_t cfi_flash_erase_block(struct rt_mtd_nor_device *dev, rt_off_t of if (cfi_flash_full_status_check(maps, cptr, &cword, maps->erase_blk_tout, "sector erase")) { - return -RT_ERROR; + err = -RT_ERROR; + goto _out_lock; } } } } - return RT_EOK; +_out_lock: + rt_mutex_release(&maps->rw_lock); + + return err; } const static struct rt_mtd_nor_driver_ops cfi_flash_ops = @@ -910,52 +909,36 @@ const static struct rt_mtd_nor_driver_ops cfi_flash_ops = .erase_block = cfi_flash_erase_block, }; -static rt_err_t cfi_flash_ofw_init(struct rt_platform_device *pdev, struct cfi_flash *cfi) +static rt_err_t cfi_flash_probe(struct rt_platform_device *pdev) { - rt_err_t err; - struct rt_ofw_node *np = pdev->parent.ofw_node; - - cfi->maps_nr = rt_ofw_get_address_count(np); + const char *name; + rt_err_t err = RT_EOK; + struct rt_device *dev = &pdev->parent; + struct cfi_flash *cfi = rt_calloc(1, sizeof(*cfi)); - if (cfi->maps_nr <= 0) + if (!cfi) { - return -RT_EEMPTY; + return -RT_ENOMEM; } - if ((err = rt_ofw_prop_read_u32(np, "bank-width", &cfi->bank_width))) - { - return err; - } + cfi->maps_nr = rt_dm_dev_get_address_count(dev); - if (!(cfi->maps = rt_calloc(1, sizeof(*cfi->maps) * cfi->maps_nr))) + if (cfi->maps_nr <= 0) { - return -RT_ENOMEM; - } + err = -RT_EEMPTY; - for (int i = 0; i < cfi->maps_nr; ++i) - { - if ((err = rt_ofw_get_address(np, i, &cfi->maps[i].address, &cfi->maps[i].size))) - { - break; - } + goto _fail; } - return err; -} - -static rt_err_t cfi_flash_probe(struct rt_platform_device *pdev) -{ - const char *name; - rt_err_t err = RT_EOK; - struct cfi_flash *cfi = rt_calloc(1, sizeof(*cfi)); - - if (!cfi) + if ((err = rt_dm_dev_prop_read_u32(dev, "bank-width", &cfi->bank_width))) { - return -RT_ENOMEM; + goto _fail; } - if ((err = cfi_flash_ofw_init(pdev, cfi))) + if (!(cfi->maps = rt_calloc(1, sizeof(*cfi->maps) * cfi->maps_nr))) { + err = -RT_ENOMEM; + goto _fail; } @@ -963,11 +946,23 @@ static rt_err_t cfi_flash_probe(struct rt_platform_device *pdev) { struct cfi_flash_map *maps = &cfi->maps[i]; rt_int32_t size_ratio, offset_etout, offset_wbtout, wtout; - rt_uint64_t address = maps->address, size = maps->size; + rt_uint64_t address, size; rt_size_t sect_count = 0; rt_ubase_t sector; /* map a page early first */ - void *early_base = rt_ioremap((void *)address, RT_MM_PAGE_SIZE); + void *early_base; + + if (rt_dm_dev_get_address(dev, i, &address, &size) < 0) + { + err = -RT_EIO; + + goto _fail; + } + + maps->address = address; + maps->size = size; + + early_base = rt_ioremap((void *)address, RT_MM_PAGE_SIZE); if (!early_base) { @@ -1109,12 +1104,12 @@ static rt_err_t cfi_flash_probe(struct rt_platform_device *pdev) maps->parent.block_end = maps->sect_count; maps->parent.block_size = maps->block_size; - if ((err = rt_dm_set_dev_name_auto(&maps->parent.parent, "nor")) < 0) + if ((err = rt_dm_dev_set_name_auto(&maps->parent.parent, "nor")) < 0) { goto _fail; } - name = rt_dm_get_dev_name(&maps->parent.parent); + name = rt_dm_dev_get_name(&maps->parent.parent); if ((err = rt_mutex_init(&maps->rw_lock, name, RT_IPC_FLAG_PRIO))) { @@ -1127,6 +1122,8 @@ static rt_err_t cfi_flash_probe(struct rt_platform_device *pdev) } } + dev->user_data = cfi; + return RT_EOK; _fail: @@ -1154,6 +1151,37 @@ static rt_err_t cfi_flash_probe(struct rt_platform_device *pdev) return err; } +static rt_err_t cfi_flash_remove(struct rt_platform_device *pdev) +{ + struct cfi_flash *cfi = pdev->parent.user_data; + + if (cfi->maps) + { + for (int i = 0; i < cfi->maps_nr; ++i) + { + struct cfi_flash_map *maps = &cfi->maps[i]; + + if (maps->base) + { + rt_iounmap(maps->base); + } + + if (maps->sect) + { + rt_free(maps->sect); + } + } + + rt_device_unregister(&cfi->maps->parent.parent); + + rt_free(cfi->maps); + } + + rt_free(cfi); + + return RT_EOK; +} + static const struct rt_ofw_node_id cfi_flash_ofw_ids[] = { { .compatible = "cfi-flash" }, @@ -1166,5 +1194,6 @@ static struct rt_platform_driver cfi_flash_driver = .ids = cfi_flash_ofw_ids, .probe = cfi_flash_probe, + .remove = cfi_flash_remove, }; RT_PLATFORM_DRIVER_EXPORT(cfi_flash_driver); diff --git a/components/drivers/nvmem/Kconfig b/components/drivers/nvmem/Kconfig new file mode 100644 index 000000000000..689022c64608 --- /dev/null +++ b/components/drivers/nvmem/Kconfig @@ -0,0 +1,10 @@ +menuconfig RT_USING_NVMEM + bool "Using Non Volatile Memory (NVMEM) device drivers" + depends on RT_USING_DM + select RT_USING_OFW + default n + +config RT_NVMEM_ROCKCHIP_OTP + bool "Rockchip OTP controller support" + depends on RT_USING_NVMEM + default n diff --git a/components/drivers/nvmem/SConscript b/components/drivers/nvmem/SConscript new file mode 100755 index 000000000000..29d71fca90f4 --- /dev/null +++ b/components/drivers/nvmem/SConscript @@ -0,0 +1,18 @@ +from building import * + +group = [] + +if not GetDepend(['RT_USING_NVMEM']): + Return('group') + +cwd = GetCurrentDir() +CPPPATH = [cwd + '/../include'] + +src = ['nvmem.c'] + +if GetDepend(['RT_NVMEM_ROCKCHIP_OTP']): + src += ['nvmem-rockchip-otp.c'] + +group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') diff --git a/components/drivers/nvmem/nvmem-rockchip-otp.c b/components/drivers/nvmem/nvmem-rockchip-otp.c new file mode 100644 index 000000000000..431e3fd9d20e --- /dev/null +++ b/components/drivers/nvmem/nvmem-rockchip-otp.c @@ -0,0 +1,1023 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-02-25 GuEe-GUI the first version + */ + +#include +#include + +#include + +#define DBG_TAG "nvmem.rockchip.otp" +#define DBG_LVL DBG_INFO +#include + +/* OTP Register Offsets */ +#define OTPC_SBPI_CTRL 0x0020 +#define OTPC_SBPI_CMD_VALID_PRE 0x0024 +#define OTPC_SBPI_CS_VALID_PRE 0x0028 +#define OTPC_SBPI_STATUS 0x002C +#define OTPC_USER_CTRL 0x0100 +#define OTPC_USER_ADDR 0x0104 +#define OTPC_USER_ENABLE 0x0108 +#define OTPC_USER_QP 0x0120 +#define OTPC_USER_Q 0x0124 +#define OTPC_INT_STATUS 0x0304 +#define OTPC_SBPI_CMD0_OFFSET 0x1000 +#define OTPC_SBPI_CMD1_OFFSET 0x1004 + +#define OTPC_MODE_CTRL 0x2000 +#define OTPC_IRQ_ST 0x2008 +#define OTPC_ACCESS_ADDR 0x200c +#define OTPC_RD_DATA 0x2010 +#define OTPC_REPR_RD_TRANS_NUM 0x2020 + +#define OTPC_DEEP_STANDBY 0x0 +#define OTPC_STANDBY 0x1 +#define OTPC_ACTIVE 0x2 +#define OTPC_READ_ACCESS 0x3 +#define OTPC_TRANS_NUM 0x1 +#define OTPC_RDM_IRQ_ST RT_BIT(0) +#define OTPC_STB2ACT_IRQ_ST RT_BIT(7) +#define OTPC_DP2STB_IRQ_ST RT_BIT(8) +#define OTPC_ACT2STB_IRQ_ST RT_BIT(9) +#define OTPC_STB2DP_IRQ_ST RT_BIT(10) +#define PX30S_NBYTES 4 +#define PX30S_NO_SECURE_OFFSET 224 + +/* OTP Register bits and masks */ +#define OTPC_USER_ADDR_MASK RT_GENMASK(31, 16) +#define OTPC_USE_USER RT_BIT(0) +#define OTPC_USE_USER_MASK RT_GENMASK(16, 16) +#define OTPC_USER_FSM_ENABLE RT_BIT(0) +#define OTPC_USER_FSM_ENABLE_MASK RT_GENMASK(16, 16) +#define OTPC_SBPI_DONE RT_BIT(1) +#define OTPC_USER_DONE RT_BIT(2) + +#define SBPI_DAP_ADDR 0x02 +#define SBPI_DAP_ADDR_SHIFT 8 +#define SBPI_DAP_ADDR_MASK RT_GENMASK(31, 24) +#define SBPI_CMD_VALID_MASK RT_GENMASK(31, 16) +#define SBPI_DAP_CMD_WRF 0xC0 +#define SBPI_DAP_REG_ECC 0x3A +#define SBPI_ECC_ENABLE 0x00 +#define SBPI_ECC_DISABLE 0x09 +#define SBPI_ENABLE RT_BIT(0) +#define SBPI_ENABLE_MASK RT_GENMASK(16, 16) + +#define OTPC_TIMEOUT 10000 +#define OTPC_TIMEOUT_PROG 100000 +#define RK3568_NBYTES 2 + +#define RK3588_OTPC_AUTO_CTRL 0x04 +#define RK3588_OTPC_AUTO_EN 0x08 +#define RK3588_OTPC_INT_ST 0x84 +#define RK3588_OTPC_DOUT0 0x20 +#define RK3588_NO_SECURE_OFFSET 0x300 +#define RK3588_NBYTES 4 +#define RK3588_BURST_NUM 1 +#define RK3588_BURST_SHIFT 8 +#define RK3588_ADDR_SHIFT 16 +#define RK3588_AUTO_EN RT_BIT(0) +#define RK3588_RD_DONE RT_BIT(1) + +#define RV1126_OTP_NVM_CEB 0x00 +#define RV1126_OTP_NVM_RSTB 0x04 +#define RV1126_OTP_NVM_ST 0x18 +#define RV1126_OTP_NVM_RADDR 0x1C +#define RV1126_OTP_NVM_RSTART 0x20 +#define RV1126_OTP_NVM_RDATA 0x24 +#define RV1126_OTP_NVM_TRWH 0x28 +#define RV1126_OTP_READ_ST 0x30 +#define RV1126_OTP_NVM_PRADDR 0x34 +#define RV1126_OTP_NVM_PRLEN 0x38 +#define RV1126_OTP_NVM_PRDATA 0x3c +#define RV1126_OTP_NVM_FAILTIME 0x40 +#define RV1126_OTP_NVM_PRSTART 0x44 +#define RV1126_OTP_NVM_PRSTATE 0x48 + +/* + * +----------+------------------+--------------------------+ + * | TYPE | RANGE(byte) | NOTE | + * +----------+------------------+--------------------------+ + * | system | 0x000 ~ 0x0ff | system info, read only | + * +----------+------------------+--------------------------+ + * | oem | 0x100 ~ 0x1ef | for customized | + * +----------+------------------+--------------------------+ + * | reserved | 0x1f0 ~ 0x1f7 | future extension | + * +----------+------------------+--------------------------+ + * | wp | 0x1f8 ~ 0x1ff | write protection for oem | + * +----------+------------------+--------------------------+ + * + * +-----+ +------------------+ + * | wp | -- | wp for oem range | + * +-----+ +------------------+ + * | 1f8 | | 0x100 ~ 0x11f | + * +-----+ +------------------+ + * | 1f9 | | 0x120 ~ 0x13f | + * +-----+ +------------------+ + * | 1fa | | 0x140 ~ 0x15f | + * +-----+ +------------------+ + * | 1fb | | 0x160 ~ 0x17f | + * +-----+ +------------------+ + * | 1fc | | 0x180 ~ 0x19f | + * +-----+ +------------------+ + * | 1fd | | 0x1a0 ~ 0x1bf | + * +-----+ +------------------+ + * | 1fe | | 0x1c0 ~ 0x1df | + * +-----+ +------------------+ + * | 1ff | | 0x1e0 ~ 0x1ef | + * +-----+ +------------------+ + */ +#define RV1126_OTP_OEM_OFFSET 0x100 +#define RV1126_OTP_OEM_SIZE 0xf0 +#define RV1126_OTP_WP_OFFSET 0x1f8 +#define RV1126_OTP_WP_SIZE 0x8 + +/* each bit mask 32 bits in OTP NVM */ +#define ROCKCHIP_OTP_WP_MASK_NBITS 64 + +#define OFFSET_IS_ALIGNED(x, a) (((x) & ((typeof(x))(a) - 1)) == 0) + +struct rockchip_otp; + +struct rockchip_otp_data +{ + int size; + + rt_ssize_t (*read)(struct rockchip_otp *, int offset, void *buf, rt_size_t bytes); + rt_ssize_t (*write)(struct rockchip_otp *, int offset, void *buf, rt_size_t bytes); + rt_err_t (*init)(struct rockchip_otp *); +}; + +struct rockchip_otp +{ + struct rt_nvmem_device parent; + void *regs; + + rt_bool_t was_writed; + struct rt_clk_array *clk_arr; + struct rt_reset_control *rstc; + const struct rockchip_otp_data *soc_data; + + struct rt_mutex mutex; + DECLARE_BITMAP(wp_mask, ROCKCHIP_OTP_WP_MASK_NBITS); +}; + +#define raw_to_rockchip_otp(raw) rt_container_of(raw, struct rockchip_otp, parent) + +#define readl_poll_timeout(ADDR, VAL, COND, DELAY_US, TIMEOUT_US) \ +({ \ + rt_uint64_t timeout_us = TIMEOUT_US; \ + rt_int64_t left_ns = timeout_us * 1000L; \ + rt_ubase_t delay_us = DELAY_US; \ + rt_uint64_t delay_ns = delay_us * 1000L; \ + for (;;) \ + { \ + (VAL) = HWREG32(ADDR); \ + if (COND) \ + { \ + break; \ + } \ + if (timeout_us && left_ns < 0) \ + { \ + (VAL) = HWREG32(ADDR); \ + break; \ + } \ + if (delay_us) \ + { \ + rt_hw_us_delay(delay_us); \ + if (timeout_us) \ + { \ + left_ns -= delay_ns; \ + } \ + } \ + rt_hw_cpu_relax(); \ + if (timeout_us) \ + { \ + --left_ns; \ + } \ + } \ + (COND) ? RT_EOK : -RT_ETIMEOUT; \ +}) + +static rt_err_t rockchip_otp_reset(struct rockchip_otp *rk_otp) +{ + rt_err_t err; + + if ((err = rt_reset_control_assert(rk_otp->rstc))) + { + LOG_E("Failed to assert otp phy error = %s", rt_strerror(err)); + return err; + } + + rt_hw_us_delay(2); + + if ((err = rt_reset_control_deassert(rk_otp->rstc))) + { + LOG_E("Failed to deassert otp phy error = %s", rt_strerror(err)); + return err; + } + + return RT_EOK; +} + +static rt_err_t px30_otp_wait_status(struct rockchip_otp *rk_otp, rt_uint32_t flag) +{ + rt_err_t err; + rt_uint32_t status = 0; + + err = readl_poll_timeout(rk_otp->regs + OTPC_INT_STATUS, status, + (status & flag), 1, OTPC_TIMEOUT); + + if (err) + { + return err; + } + + /* Clean int status */ + HWREG32(rk_otp->regs + OTPC_INT_STATUS) = flag; + + return RT_EOK; +} + +static rt_err_t px30_otp_ecc_enable(struct rockchip_otp *rk_otp, rt_bool_t enable) +{ + rt_err_t err; + + HWREG32(rk_otp->regs + OTPC_SBPI_CTRL) = SBPI_DAP_ADDR_MASK | + (SBPI_DAP_ADDR << SBPI_DAP_ADDR_SHIFT); + HWREG32(rk_otp->regs + OTPC_SBPI_CMD_VALID_PRE) = SBPI_CMD_VALID_MASK | 0x1; + HWREG32(rk_otp->regs + OTPC_SBPI_CMD0_OFFSET) = + SBPI_DAP_CMD_WRF | SBPI_DAP_REG_ECC; + + HWREG32(rk_otp->regs + OTPC_SBPI_CMD1_OFFSET) = + enable ? SBPI_ECC_ENABLE : SBPI_ECC_DISABLE; + HWREG32(rk_otp->regs + OTPC_SBPI_CTRL) = SBPI_ENABLE_MASK | SBPI_ENABLE; + + if ((err = px30_otp_wait_status(rk_otp, OTPC_SBPI_DONE)) < 0) + { + LOG_E("Timeout during ecc_enable"); + } + + return err; +} + +static rt_ssize_t px30_otp_read(struct rockchip_otp *rk_otp, int offset, + void *val, rt_size_t bytes) +{ + rt_ssize_t res; + rt_uint8_t *buf = val; + + if ((res = rt_clk_array_prepare_enable(rk_otp->clk_arr)) < 0) + { + LOG_E("Failed to prepare/enable clks"); + + return res; + } + + if ((res = rockchip_otp_reset(rk_otp))) + { + LOG_E("Failed to reset otp phy"); + + goto _disable_clks; + } + + if ((res = px30_otp_ecc_enable(rk_otp, RT_FALSE)) < 0) + { + LOG_E("Enable ECC error = %s", rt_strerror(res)); + + goto _disable_clks; + } + + HWREG32(rk_otp->regs + OTPC_USER_CTRL) = OTPC_USE_USER | OTPC_USE_USER_MASK; + rt_hw_us_delay(5); + + for (int i = 0; i < bytes; ++i) + { + HWREG32(rk_otp->regs + OTPC_USER_ADDR) = offset++ | OTPC_USER_ADDR_MASK; + HWREG32(rk_otp->regs + OTPC_USER_ENABLE) = + OTPC_USER_FSM_ENABLE | OTPC_USER_FSM_ENABLE_MASK; + + if ((res = px30_otp_wait_status(rk_otp, OTPC_USER_DONE)) < 0) + { + LOG_E("Timeout during read setup"); + + goto _read_end; + } + + *buf++ = HWREG8(rk_otp->regs + OTPC_USER_Q); + } + +_read_end: + HWREG32(rk_otp->regs + OTPC_USER_CTRL) = 0x0 | OTPC_USE_USER_MASK; + +_disable_clks: + rt_clk_array_disable_unprepare(rk_otp->clk_arr); + + return bytes; +} + +static rt_err_t px30s_otp_wait_status(struct rockchip_otp *rk_otp, rt_uint32_t flag) +{ + rt_err_t err; + rt_uint32_t status = 0; + + err = readl_poll_timeout(rk_otp->regs + OTPC_IRQ_ST, status, + (status & flag), 1, OTPC_TIMEOUT); + + if (err) + { + return err; + } + + /* clean int status */ + HWREG32(rk_otp->regs + OTPC_IRQ_ST) = flag; + + return 0; +} + +static rt_err_t px30s_otp_active(struct rockchip_otp *rk_otp) +{ + rt_uint32_t mode; + rt_err_t err = RT_EOK; + + mode = HWREG32(rk_otp->regs + OTPC_MODE_CTRL); + + switch (mode) + { + case OTPC_DEEP_STANDBY: + HWREG32(rk_otp->regs + OTPC_MODE_CTRL) = OTPC_STANDBY; + + if ((err = px30s_otp_wait_status(rk_otp, OTPC_DP2STB_IRQ_ST)) < 0) + { + LOG_E("Timeout during wait dp2stb"); + + return err; + } + + /* Fallthrough */ + case OTPC_STANDBY: + HWREG32(rk_otp->regs + OTPC_MODE_CTRL) = OTPC_ACTIVE; + + if ((err = px30s_otp_wait_status(rk_otp, OTPC_STB2ACT_IRQ_ST)) < 0) + { + LOG_E("Timeout during wait stb2act"); + + return err; + } + + break; + + default: + break; + } + + return err; +} + +static rt_err_t px30s_otp_standby(struct rockchip_otp *rk_otp) +{ + rt_uint32_t mode; + rt_err_t err = RT_EOK; + + mode = HWREG32(rk_otp->regs + OTPC_MODE_CTRL); + + switch (mode) + { + case OTPC_ACTIVE: + HWREG32(rk_otp->regs + OTPC_MODE_CTRL) = OTPC_STANDBY; + + if ((err = px30s_otp_wait_status(rk_otp, OTPC_ACT2STB_IRQ_ST)) < 0) + { + LOG_E("Timeout during wait act2stb"); + + return err; + } + + /* Fallthrough */ + case OTPC_STANDBY: + HWREG32(rk_otp->regs + OTPC_MODE_CTRL) = OTPC_DEEP_STANDBY; + + if ((err = px30s_otp_wait_status(rk_otp, OTPC_STB2DP_IRQ_ST)) < 0) + { + LOG_E("Timeout during wait stb2dp"); + + return err; + } + + break; + + default: + break; + } + + return err; +} + +static rt_ssize_t px30s_otp_read(struct rockchip_otp *rk_otp, int offset, + void *val, rt_size_t bytes) +{ + rt_ssize_t res; + rt_uint8_t *buf; + rt_uint32_t out_value; + int addr_start, addr_end, addr_offset, addr_len; + + if (offset >= rk_otp->soc_data->size) + { + return -RT_ENOMEM; + } + + if (offset + bytes > rk_otp->soc_data->size) + { + bytes = rk_otp->soc_data->size - offset; + } + + if ((res = rt_clk_array_prepare_enable(rk_otp->clk_arr)) < 0) + { + LOG_E("Failed to prepare/enable clks"); + + return res; + } + + if ((res = rockchip_otp_reset(rk_otp))) + { + LOG_E("Failed to reset otp phy"); + + goto _disable_clks; + } + + if ((res = px30s_otp_active(rk_otp))) + { + goto _disable_clks; + } + + addr_start = rt_rounddown(offset, PX30S_NBYTES) / PX30S_NBYTES; + addr_end = rt_roundup(offset + bytes, PX30S_NBYTES) / PX30S_NBYTES; + addr_offset = offset % PX30S_NBYTES; + addr_len = addr_end - addr_start; + addr_start += PX30S_NO_SECURE_OFFSET; + + buf = rt_calloc(PX30S_NBYTES, addr_len * sizeof(*buf)); + if (!buf) + { + res = -RT_ENOMEM; + goto _read_end; + } + + for (int i = 0; addr_len--; i += PX30S_NBYTES) + { + HWREG32(rk_otp->regs + OTPC_REPR_RD_TRANS_NUM) = OTPC_TRANS_NUM; + HWREG32(rk_otp->regs + OTPC_ACCESS_ADDR) = addr_start++; + HWREG32(rk_otp->regs + OTPC_MODE_CTRL) = OTPC_READ_ACCESS; + + if ((res = px30s_otp_wait_status(rk_otp, OTPC_RDM_IRQ_ST)) < 0) + { + LOG_E("timeout during wait rd"); + + goto _read_end; + } + + out_value = HWREG32(rk_otp->regs + OTPC_RD_DATA); + rt_memcpy(&buf[i], &out_value, PX30S_NBYTES); + } + + rt_memcpy(val, buf + addr_offset, (rt_uint32_t)bytes); + res = bytes; + +_read_end: + rt_free(buf); + px30s_otp_standby(rk_otp); + +_disable_clks: + rt_clk_array_disable_unprepare(rk_otp->clk_arr); + + return res; +} + +static rt_ssize_t rk3568_otp_read(struct rockchip_otp *rk_otp, int offset, + void *val, rt_size_t bytes) +{ + rt_ssize_t res; + rt_uint8_t *buf; + rt_uint32_t otp_qp, out_value; + int addr_start, addr_end, addr_offset, addr_len; + + addr_start = rt_rounddown(offset, RK3568_NBYTES) / RK3568_NBYTES; + addr_end = rt_roundup(offset + bytes, RK3568_NBYTES) / RK3568_NBYTES; + addr_offset = offset % RK3568_NBYTES; + addr_len = addr_end - addr_start; + + buf = rt_calloc(RK3568_NBYTES, addr_len * sizeof(*buf)); + if (!buf) + { + return -RT_ENOMEM; + } + + if ((res = rt_clk_array_prepare_enable(rk_otp->clk_arr)) < 0) + { + LOG_E("Failed to prepare/enable clks"); + goto _out_free; + } + + if ((res = rockchip_otp_reset(rk_otp))) + { + LOG_E("failed to reset otp phy"); + + goto _disable_clks; + } + + if ((res = px30_otp_ecc_enable(rk_otp, RT_TRUE)) < 0) + { + LOG_E("Enable ECC error = %s", rt_strerror(res)); + + goto _disable_clks; + } + + HWREG32(rk_otp->regs + OTPC_USER_CTRL) = OTPC_USE_USER | OTPC_USE_USER_MASK; + + rt_hw_us_delay(5); + + for (int i = 0; addr_len--; i += RK3568_NBYTES) + { + HWREG32(rk_otp->regs + OTPC_USER_ADDR) = + addr_start++ | OTPC_USER_ADDR_MASK; + HWREG32(rk_otp->regs + OTPC_USER_ENABLE) = + OTPC_USER_FSM_ENABLE | OTPC_USER_FSM_ENABLE_MASK; + + if ((res = px30_otp_wait_status(rk_otp, OTPC_USER_DONE)) < 0) + { + LOG_E("Timeout during read setup"); + + goto _read_end; + } + + otp_qp = HWREG32(rk_otp->regs + OTPC_USER_QP); + + if (((otp_qp & 0xc0) == 0xc0) || (otp_qp & 0x20)) + { + res = -RT_EIO; + LOG_E("ECC check error during read setup"); + + goto _read_end; + } + + out_value = HWREG32(rk_otp->regs + OTPC_USER_Q); + rt_memcpy(&buf[i], &out_value, RK3568_NBYTES); + } + + rt_memcpy(val, buf + addr_offset, bytes); + res = bytes; + +_read_end: + HWREG32(rk_otp->regs + OTPC_USER_CTRL) = 0x0 | OTPC_USE_USER_MASK; + +_disable_clks: + + rt_clk_array_disable_unprepare(rk_otp->clk_arr); +_out_free: + rt_free(buf); + + return res; +} + +static rt_err_t rk3588_otp_wait_status(struct rockchip_otp *rk_otp, + rt_uint32_t flag) +{ + rt_err_t err; + rt_uint32_t status = 0; + + err = readl_poll_timeout(rk_otp->regs + RK3588_OTPC_INT_ST, status, + (status & flag), 1, OTPC_TIMEOUT); + + if (err) + { + return err; + } + + /* clean int status */ + HWREG32(rk_otp->regs + RK3588_OTPC_INT_ST) = flag; + + return 0; +} + +static rt_ssize_t rk3588_otp_read(struct rockchip_otp *rk_otp, int offset, + void *val, rt_size_t bytes) +{ + rt_ssize_t res = 0; + rt_uint8_t *buf; + rt_uint32_t out_value; + int addr_start, addr_end, addr_offset, addr_len; + + if (offset >= rk_otp->soc_data->size) + { + return -RT_ENOMEM; + } + + if (offset + bytes > rk_otp->soc_data->size) + { + bytes = rk_otp->soc_data->size - offset; + } + + addr_start = rt_rounddown(offset, RK3588_NBYTES) / RK3588_NBYTES; + addr_end = rt_roundup(offset + bytes, RK3588_NBYTES) / RK3588_NBYTES; + addr_offset = offset % RK3588_NBYTES; + addr_len = addr_end - addr_start; + addr_start += RK3588_NO_SECURE_OFFSET; + + buf = rt_calloc(RK3588_NBYTES, addr_len * sizeof(*buf)); + if (!buf) + { + return -RT_ENOMEM; + } + + if ((res = rt_clk_array_prepare_enable(rk_otp->clk_arr)) < 0) + { + LOG_E("Failed to prepare/enable clks"); + + goto _out_free; + } + + for (int i = 0; addr_len--; i += RK3588_NBYTES, ++addr_start) + { + HWREG32(rk_otp->regs + RK3588_OTPC_AUTO_CTRL) = + (addr_start << RK3588_ADDR_SHIFT) | + (RK3588_BURST_NUM << RK3588_BURST_SHIFT); + HWREG32(rk_otp->regs + RK3588_OTPC_AUTO_EN) = RK3588_AUTO_EN; + + if ((res = rk3588_otp_wait_status(rk_otp, RK3588_RD_DONE)) < 0) + { + LOG_E("Timeout during read setup"); + + goto _read_end; + } + + out_value = HWREG32(rk_otp->regs + RK3588_OTPC_DOUT0); + rt_memcpy(&buf[i], &out_value, RK3588_NBYTES); + } + + rt_memcpy(val, buf + addr_offset, bytes); + res = bytes; + +_read_end: + rt_clk_array_disable_unprepare(rk_otp->clk_arr); + +_out_free: + rt_free(buf); + + return res; +} + +static rt_err_t rv1126_otp_init(struct rockchip_otp *rk_otp) +{ + rt_err_t err; + rt_uint32_t status = 0; + + HWREG32(rk_otp->regs + RV1126_OTP_NVM_CEB) = 0x0; + err = readl_poll_timeout(rk_otp->regs + RV1126_OTP_NVM_ST, status, + status & 0x1, 1, OTPC_TIMEOUT); + + if (err < 0) + { + LOG_E("Timeout during set ceb"); + + return err; + } + + HWREG32(rk_otp->regs + RV1126_OTP_NVM_RSTB) = 0x1; + err = readl_poll_timeout(rk_otp->regs + RV1126_OTP_NVM_ST, status, + status & 0x4, 1, OTPC_TIMEOUT); + + if (err < 0) + { + LOG_E("Timeout during set rstb"); + + return err; + } + + return RT_EOK; +} + +static rt_ssize_t rv1126_otp_read(struct rockchip_otp *rk_otp, int offset, + void *val, rt_size_t bytes) +{ + rt_ssize_t res; + rt_uint8_t *buf = val; + rt_uint32_t status = 0; + + for (int i = 0; i < bytes; ++i) + { + HWREG32(rk_otp->regs + RV1126_OTP_NVM_RADDR) = offset++; + HWREG32(rk_otp->regs + RV1126_OTP_NVM_RSTART) = 0x1; + + res = readl_poll_timeout(rk_otp->regs + RV1126_OTP_READ_ST, + status, status == 0, 1, OTPC_TIMEOUT); + + if (res < 0) + { + LOG_E("Timeout during read setup"); + + return res; + } + + *buf++ = HWREG8(rk_otp->regs + RV1126_OTP_NVM_RDATA); + } + + return bytes; +} + +static rt_err_t rv1126_otp_prog(struct rockchip_otp *rk_otp, + rt_uint32_t bit_offset, rt_uint32_t data, rt_uint32_t bit_len) +{ + rt_err_t err; + rt_uint32_t status = 0; + + if (!data) + { + return 0; + } + + HWREG32(rk_otp->regs + RV1126_OTP_NVM_PRADDR) = bit_offset; + HWREG32(rk_otp->regs + RV1126_OTP_NVM_PRLEN) = bit_len - 1; + HWREG32(rk_otp->regs + RV1126_OTP_NVM_PRDATA) = data; + HWREG32(rk_otp->regs + RV1126_OTP_NVM_PRSTART) = 1; + + /* Wait max 100 ms */ + err = readl_poll_timeout(rk_otp->regs + RV1126_OTP_NVM_PRSTATE, + status, status == 0, 1, OTPC_TIMEOUT_PROG); + + if (err) + { + LOG_E("Timeout during prog"); + } + + return err; +} + +static rt_ssize_t rv1126_otp_write(struct rockchip_otp *rk_otp, int offset, + void *val, rt_size_t bytes) +{ + rt_ssize_t res; + rt_uint8_t *buf = val, val_r, val_w; + + for (int i = 0; i < bytes; ++i) + { + if ((res = rv1126_otp_read(rk_otp, offset, &val_r, 1))) + { + return res; + } + + val_w = *buf & (~val_r); + + if ((res = rv1126_otp_prog(rk_otp, offset * 8, val_w, 8))) + { + return res; + } + + ++buf; + ++offset; + } + + return bytes; +} + +static rt_ssize_t rv1126_otp_wp(struct rockchip_otp *rk_otp, int offset, + rt_size_t bytes) +{ + int nbits = bytes / 4; + rt_uint32_t bit_idx = (offset - RV1126_OTP_OEM_OFFSET) / 4; + + for (int i = 0; i < nbits; ++i) + { + bitmap_set_bit(rk_otp->wp_mask, bit_idx + i); + } + + return rv1126_otp_write(rk_otp, RV1126_OTP_WP_OFFSET, rk_otp->wp_mask, + RV1126_OTP_WP_SIZE); +} + +static rt_ssize_t rv1126_otp_oem_write(struct rockchip_otp *rk_otp, int offset, + void *val, rt_size_t bytes) +{ + rt_ssize_t res; + + if (offset < RV1126_OTP_OEM_OFFSET || + offset > (RV1126_OTP_OEM_OFFSET + RV1126_OTP_OEM_SIZE - 1) || + bytes > RV1126_OTP_OEM_SIZE || + (offset + bytes) > (RV1126_OTP_OEM_OFFSET + RV1126_OTP_OEM_SIZE)) + { + return -RT_EINVAL; + } + + if (!OFFSET_IS_ALIGNED(offset, 4) || !OFFSET_IS_ALIGNED(bytes, 4)) + { + return -RT_EINVAL; + } + + if (!(res = rv1126_otp_write(rk_otp, offset, val, bytes))) + { + res = rv1126_otp_wp(rk_otp, offset, bytes); + } + + return res; +} + +static rt_ssize_t rockchip_otp_read(struct rt_nvmem_device *ndev, int offset, + void *val, rt_size_t bytes) +{ + rt_ssize_t res = -RT_EINVAL; + struct rockchip_otp *rk_otp = raw_to_rockchip_otp(ndev); + + rt_mutex_take(&rk_otp->mutex, RT_WAITING_FOREVER); + + if (rk_otp->soc_data && rk_otp->soc_data->read) + { + res = rk_otp->soc_data->read(rk_otp, offset, val, bytes); + } + + rt_mutex_release(&rk_otp->mutex); + + return res; +} + +static rt_ssize_t rockchip_otp_write(struct rt_nvmem_device *ndev, int offset, + void *val, rt_size_t bytes) +{ + rt_ssize_t res = -RT_EINVAL; + struct rockchip_otp *rk_otp = raw_to_rockchip_otp(ndev); + + rt_mutex_take(&rk_otp->mutex, RT_WAITING_FOREVER); + + if (!rk_otp->was_writed && rk_otp->soc_data && rk_otp->soc_data->write) + { + res = rk_otp->soc_data->write(rk_otp, offset, val, bytes); + } + + rt_mutex_release(&rk_otp->mutex); + + return res; +} + +static const struct rockchip_otp_data px30_data = +{ + .size = 0x40, + .read = px30_otp_read, +}; + +static const struct rockchip_otp_data px30s_data = +{ + .size = 0x80, + .read = px30s_otp_read, +}; + +static const struct rockchip_otp_data rk3568_data = +{ + .size = 0x80, + .read = rk3568_otp_read, +}; + +static const struct rockchip_otp_data rk3588_data = +{ + .size = 0x400, + .read = rk3588_otp_read, +}; + +static const struct rockchip_otp_data rv1106_data = +{ + .size = 0x80, + .read = rk3568_otp_read, +}; + +static const struct rockchip_otp_data rv1126_data = +{ + .size = 0x200, + .init = rv1126_otp_init, + .read = rv1126_otp_read, + .write = rv1126_otp_oem_write, +}; + +static rt_err_t rockchip_otp_probe(struct rt_platform_device *pdev) +{ + rt_err_t err; + struct rt_nvmem_device *nvmem; + struct rt_device *dev = &pdev->parent; + const struct rockchip_otp_data *soc_data = pdev->id->data; + struct rockchip_otp *rk_otp = rt_calloc(1, sizeof(*rk_otp)); + + if (!rk_otp) + { + return -RT_ENOMEM; + } + + rk_otp->regs = rt_dm_dev_iomap(dev, 0); + + if (!rk_otp->regs) + { + err = -RT_EIO; + goto _fail; + } + + rk_otp->clk_arr = rt_clk_get_array(dev); + + if (rt_is_err(rk_otp->clk_arr)) + { + err = rt_ptr_err(rk_otp->clk_arr); + goto _fail; + } + + rk_otp->rstc = rt_reset_control_get_array(dev); + + if (rt_is_err(rk_otp->rstc)) + { + err = rt_ptr_err(rk_otp->rstc); + goto _fail; + } + + if (soc_data->init) + { + if ((err = soc_data->init(rk_otp))) + { + goto _fail; + } + } + + dev->user_data = rk_otp; + + rk_otp->soc_data = soc_data; + nvmem = &rk_otp->parent; + + nvmem->parent.ofw_node = dev->ofw_node; + nvmem->reg_read = rockchip_otp_read, + nvmem->reg_write = rockchip_otp_write, + nvmem->size = soc_data->size; + nvmem->read_only = soc_data->write == RT_NULL; + nvmem->stride = 1; + nvmem->word_size = 1; + + if ((err = rt_nvmem_device_register(&rk_otp->parent))) + { + goto _fail; + } + + rt_mutex_init(&rk_otp->mutex, "rockchip-otp", RT_IPC_FLAG_PRIO); + + return RT_EOK; + +_fail: + if (!rt_is_err_or_null(rk_otp->regs)) + { + rt_iounmap(rk_otp->regs); + } + + if (!rt_is_err_or_null(rk_otp->clk_arr)) + { + rt_clk_array_put(rk_otp->clk_arr); + } + + rt_free(rk_otp); + + return err; +} + +static rt_err_t rockchip_otp_remove(struct rt_platform_device *pdev) +{ + rt_err_t err; + struct rockchip_otp *rk_otp = pdev->parent.user_data; + + err = rt_nvmem_device_unregister(&rk_otp->parent); + + rt_iounmap(rk_otp->regs); + rt_clk_array_put(rk_otp->clk_arr); + + rt_free(rk_otp); + + return err; +} + +static const struct rt_ofw_node_id rockchip_otp_ofw_ids[] = +{ + { .compatible = "rockchip,px30-otp", .data = &px30_data, }, + { .compatible = "rockchip,px30s-otp", .data = &px30s_data, }, + { .compatible = "rockchip,rk3308-otp", .data = &px30_data, }, + { .compatible = "rockchip,rk3308bs-otp", .data = &px30s_data, }, + { .compatible = "rockchip,rk3568-otp", .data = &rk3568_data, }, + { .compatible = "rockchip,rk3588-otp", .data = &rk3588_data, }, + { .compatible = "rockchip,rv1106-otp", .data = &rv1106_data, }, + { .compatible = "rockchip,rv1126-otp", .data = &rv1126_data, }, + { /* sentinel */ } +}; + +static struct rt_platform_driver rockchip_otp_driver = +{ + .name = "nvmem-rockchip-otp", + .ids = rockchip_otp_ofw_ids, + + .probe = rockchip_otp_probe, + .remove = rockchip_otp_remove, +}; +RT_PLATFORM_DRIVER_EXPORT(rockchip_otp_driver); diff --git a/components/drivers/nvmem/nvmem.c b/components/drivers/nvmem/nvmem.c new file mode 100644 index 000000000000..64240be5b3aa --- /dev/null +++ b/components/drivers/nvmem/nvmem.c @@ -0,0 +1,531 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-02-25 GuEe-GUI the first version + */ + +#include +#include + +#define DBG_TAG "rtdm.nvmem" +#define DBG_LVL DBG_INFO +#include + +#ifndef BITS_PER_BYTE +#define BITS_PER_BYTE 8 +#endif + +rt_err_t rt_nvmem_device_register(struct rt_nvmem_device *ndev) +{ + struct rt_ofw_node *np; + + if (!ndev) + { + return -RT_EINVAL; + } + + np = ndev->parent.ofw_node; + + if (!ndev->ignore_wp) + { + ndev->wp_pin = rt_pin_get_named_pin(&ndev->parent, "wp", 0, + RT_NULL, &ndev->wp_pin_active); + + if (ndev->wp_pin < 0 && ndev->wp_pin != -RT_EEMPTY) + { + return -RT_EINVAL; + } + } + + ref_init(&ndev->ref); + + ndev->read_only = rt_dm_dev_prop_read_bool(&ndev->parent, "read-only") || + ndev->read_only || !ndev->reg_write; + + if (np) + { + rt_ofw_data(np) = ndev; + } + + return RT_EOK; +} + +rt_err_t rt_nvmem_device_unregister(struct rt_nvmem_device *ndev) +{ + if (!ndev) + { + return -RT_EINVAL; + } + + if (ref_read(&ndev->ref) != 1) + { + return -RT_EBUSY; + } + + return RT_EOK; +} + +rt_err_t rt_nvmem_device_append_cell(struct rt_nvmem_device *ndev, + struct rt_nvmem_cell *cell) +{ + rt_ubase_t level; + + if (!ndev) + { + return -RT_EINVAL; + } + + if (!ndev->cells_nr) + { + rt_list_init(&ndev->cell_nodes); + } + + rt_list_init(&cell->list); + + level = rt_spin_lock_irqsave(&ndev->spinlock); + rt_list_insert_before(&ndev->cell_nodes, &cell->list); + ++ndev->cells_nr; + rt_spin_unlock_irqrestore(&ndev->spinlock, level); + + ref_get(&ndev->ref); + + return RT_EOK; +} + +rt_ssize_t rt_nvmem_cell_read(struct rt_nvmem_cell *cell, void *buffer, + rt_size_t len) +{ + rt_ssize_t res; + struct rt_nvmem_device *nvmem; + + if (rt_unlikely(!cell || !buffer || !len)) + { + return -RT_EINVAL; + } + + nvmem = cell->nvmem; + + if (rt_unlikely(len > nvmem->size || len > cell->bytes)) + { + return -RT_EINVAL; + } + + if (rt_unlikely(!nvmem->reg_read)) + { + return -RT_ENOSYS; + } + + res = nvmem->reg_read(nvmem, cell->offset, buffer, len); + + if (cell->bit_offset || cell->nbits) + { + /* Shift buffer */ + rt_uint8_t *p, *b; + int extra, bit_offset = cell->bit_offset; + + p = b = buffer; + + if (bit_offset) + { + /* First shift */ + *b++ >>= bit_offset; + + /* Setup rest of the bytes if any */ + for (int i = 1; i < cell->bytes; ++i) + { + /* Get bits from next byte and shift them towards msb */ + *p |= *b << (BITS_PER_BYTE - bit_offset); + + p = b; + *b++ >>= bit_offset; + } + } + else + { + /* Point to the msb */ + p += cell->bytes - 1; + } + + /* Result fits in less bytes */ + extra = cell->bytes - RT_DIV_ROUND_UP(cell->nbits, BITS_PER_BYTE); + while (--extra >= 0) + { + *p-- = 0; + } + + /* Clear msb bits if any leftover in the last byte */ + if (cell->nbits % BITS_PER_BYTE) + { + *p &= RT_GENMASK((cell->nbits % BITS_PER_BYTE) - 1, 0); + } + } + + return res; +} + +rt_ssize_t rt_nvmem_cell_write(struct rt_nvmem_cell *cell, void *buffer, + rt_size_t len) +{ + rt_ssize_t res = 0; + struct rt_nvmem_device *nvmem; + + if (rt_unlikely(!cell || !buffer || !len)) + { + return -RT_EINVAL; + } + + nvmem = cell->nvmem; + + if (rt_unlikely(len > nvmem->size || len > cell->bytes)) + { + return -RT_EINVAL; + } + + if (rt_unlikely(!nvmem->reg_write)) + { + return -RT_ENOSYS; + } + + if (cell->bit_offset || cell->nbits) + { + /* Shift buffer */ + int nbits, bit_offset = cell->bit_offset; + rt_uint8_t v, *p, *buf, *b, pbyte, pbits; + + nbits = cell->nbits; + buf = rt_calloc(1, cell->bytes); + + if (!buf) + { + return -RT_ENOMEM; + } + + rt_memcpy(buf, buffer, len); + p = b = buf; + + if (bit_offset) + { + pbyte = *b; + *b <<= bit_offset; + + /* Setup the first byte with lsb bits from nvmem */ + if ((res = nvmem->reg_read(nvmem, cell->offset, &v, 1)) < 0) + { + goto _end; + } + *b++ |= RT_GENMASK(bit_offset - 1, 0) & v; + + /* Setup rest of the byte if any */ + for (int i = 1; i < cell->bytes; ++i) + { + /* Get last byte bits and shift them towards lsb */ + pbits = pbyte >> (BITS_PER_BYTE - 1 - bit_offset); + pbyte = *b; + p = b; + *b <<= bit_offset; + *b++ |= pbits; + } + } + + /* If it's not end on byte boundary */ + if ((nbits + bit_offset) % BITS_PER_BYTE) + { + /* Setup the last byte with msb bits from nvmem */ + if ((res = nvmem->reg_read(nvmem, cell->offset + cell->bytes - 1, &v, 1)) < 0) + { + goto _end; + } + + *p |= RT_GENMASK(7, (nbits + bit_offset) % BITS_PER_BYTE) & v; + } + + buffer = buf; + + _end: + if (res < 0) + { + rt_free(buf); + return res; + } + } + + if (nvmem->wp_pin >= 0) + { + rt_pin_write(nvmem->wp_pin, !nvmem->wp_pin_active); + } + + res = nvmem->reg_write(nvmem, cell->offset, buffer, len); + + if (nvmem->wp_pin >= 0) + { + rt_pin_write(nvmem->wp_pin, nvmem->wp_pin_active); + } + + if (cell->bit_offset || cell->nbits) + { + rt_free(buffer); + } + + return res; +} + +rt_ssize_t rt_nvmem_cell_read_u8(struct rt_nvmem_cell *cell, rt_uint8_t *out_val) +{ + return rt_nvmem_cell_read(cell, out_val, sizeof(*out_val)); +} + +rt_ssize_t rt_nvmem_cell_read_u16(struct rt_nvmem_cell *cell, rt_uint16_t *out_val) +{ + return rt_nvmem_cell_read(cell, out_val, sizeof(*out_val)); +} + +rt_ssize_t rt_nvmem_cell_read_u32(struct rt_nvmem_cell *cell, rt_uint32_t *out_val) +{ + return rt_nvmem_cell_read(cell, out_val, sizeof(*out_val)); +} + +rt_ssize_t rt_nvmem_cell_read_u64(struct rt_nvmem_cell *cell, rt_uint64_t *out_val) +{ + return rt_nvmem_cell_read(cell, out_val, sizeof(*out_val)); +} + +static struct rt_nvmem_cell *ofw_nvmem_get_cell(struct rt_ofw_node *np, + int index, const char *const_id) +{ + rt_ubase_t level; + rt_ssize_t length; + const fdt32_t *addr; + struct rt_nvmem_device *nvmem; + struct rt_nvmem_cell *cell = RT_NULL; + struct rt_ofw_cell_args cell_args; + struct rt_ofw_node *cell_np = RT_NULL, *nvmem_np = RT_NULL; + + if (rt_ofw_parse_phandle_cells(np, + "nvmem-cells", "#nvmem-cell-cells", index, &cell_args)) + { + return RT_NULL; + } + + cell_np = cell_args.data; + index = cell_args.args_count ? cell_args.args[0] : 0; + + if (!cell_np) + { + cell = rt_err_ptr(-RT_ERROR); + goto _put_node; + } + + /* 1.find in ofw node */ + cell = rt_ofw_data(cell_np); + + if (cell && ref_read(&cell->ref) > 0) + { + ref_get(&cell->ref); + goto _put_node; + } + + cell = rt_err_ptr(-RT_ERROR); + nvmem_np = rt_ofw_get_parent(cell_np); + + if (!nvmem_np) + { + goto _put_node; + } + + nvmem = rt_ofw_data(nvmem_np); + + if (!nvmem) + { + goto _put_node; + } + + level = rt_spin_lock_irqsave(&nvmem->spinlock); + + /* 2.find in const node */ + rt_list_for_each_entry(cell, &nvmem->cell_nodes, list) + { + if (cell->index == index) + { + if (const_id && cell->id && rt_strcmp(const_id, cell->id)) + { + continue; + } + + if (ref_read(&cell->ref)) + { + ref_get(&cell->ref); + } + else + { + cell = RT_NULL; + } + + break; + } + } + + rt_spin_unlock_irqrestore(&nvmem->spinlock, level); + + if (cell) + { + goto _put_node; + } + + /* 3.create a new one */ + cell = rt_calloc(1, sizeof(*cell)); + + if (!cell) + { + cell = rt_err_ptr(-RT_ENOMEM); + LOG_E("No memory to create cell: %s (%d)", const_id, index); + goto _put_node; + } + + cell->index = index; + cell->id = const_id; + + *(rt_bool_t *)&cell->free_able = RT_TRUE; + + addr = rt_ofw_prop_read_raw(cell_np, "reg", &length); + if (!addr || length < 2 * sizeof(rt_uint32_t)) + { + LOG_E("%s Invalid reg", rt_ofw_node_full_name(cell_np)); + goto _fail; + } + + cell->offset = fdt32_to_cpu(*addr++); + cell->bytes = fdt32_to_cpu(*addr); + + addr = rt_ofw_prop_read_raw(cell_np, "reg", &length); + if (addr && length == 2 * sizeof(rt_uint32_t)) + { + cell->bit_offset = fdt32_to_cpu(*addr++); + cell->nbits = fdt32_to_cpu(*addr); + } + + /* user ref is '1' */ + ref_init(&cell->ref); + + cell->np = cell_np; + cell->nvmem = nvmem; + + rt_nvmem_device_append_cell(nvmem, cell); + + rt_ofw_node_get(cell_np); + rt_ofw_data(cell_np) = cell; + + goto _put_node; + +_fail: + rt_free(cell); + cell = RT_NULL; + +_put_node: + rt_ofw_node_put(cell_np); + rt_ofw_node_put(nvmem_np); + + return cell; +} + +struct rt_nvmem_cell *rt_nvmem_get_cell_by_index(struct rt_device *dev, + int index) +{ + if (!dev || index < 0) + { + return RT_NULL; + } + + if (dev->ofw_node) + { + return ofw_nvmem_get_cell(dev->ofw_node, index, RT_NULL); + } + + return RT_NULL; +} + +struct rt_nvmem_cell *rt_nvmem_get_cell_by_name(struct rt_device *dev, + const char *id) +{ + struct rt_ofw_node *np; + + if (!dev || !id) + { + return RT_NULL; + } + + np = dev->ofw_node; + + if (np) + { + int index = 0; + const char *const_id; + struct rt_ofw_prop *prop; + + rt_ofw_foreach_prop_string(np, "nvmem-cell-names", prop, const_id) + { + if (!rt_strcmp(id, const_id)) + { + return ofw_nvmem_get_cell(np, index, const_id); + } + + ++index; + } + } + + return RT_NULL; +} + +static void nvmem_release(struct ref *r) +{ + struct rt_nvmem_device *ndev = rt_container_of(r, struct rt_nvmem_device, ref); + + if (ndev->parent.ofw_node) + { + LOG_E("%s device is release", rt_ofw_node_full_name(ndev->parent.ofw_node)); + } + + RT_ASSERT(0); +} + +static void cell_release(struct ref *r) +{ + rt_ubase_t level; + struct rt_nvmem_cell *cell = rt_container_of(r, struct rt_nvmem_cell, ref); + struct rt_nvmem_device *nvmem = cell->nvmem; + + if (!cell->free_able) + { + /* only free able can entry */ + LOG_E("%s cell is release", cell->id); + RT_ASSERT(0); + } + + if (cell->np) + { + rt_ofw_data(cell->np) = RT_NULL; + rt_ofw_node_put(cell->np); + } + + level = rt_spin_lock_irqsave(&nvmem->spinlock); + rt_list_remove(&cell->list); + --nvmem->cells_nr; + rt_spin_unlock_irqrestore(&nvmem->spinlock, level); + + ref_put(&nvmem->ref, &nvmem_release); + + rt_free(cell); +} + +void rt_nvmem_put_cell(struct rt_nvmem_cell *cell) +{ + if (!cell) + { + return; + } + + ref_put(&cell->ref, &cell_release); +} diff --git a/components/drivers/ofw/Kconfig b/components/drivers/ofw/Kconfig index bebb5b43843e..80a58d0028e6 100755 --- a/components/drivers/ofw/Kconfig +++ b/components/drivers/ofw/Kconfig @@ -1,9 +1,9 @@ menuconfig RT_USING_OFW bool "Using Open Firmware (OFW)" + depends on RT_USING_DM select RT_USING_ADT select RT_USING_ADT_REF select RT_USING_ADT_BITMAP - depends on RT_USING_DM default n config RT_USING_BUILTIN_FDT @@ -20,3 +20,10 @@ config RT_FDT_EARLYCON_MSG_SIZE int "Earlycon message buffer size (KB)" depends on RT_USING_OFW default 128 + +config RT_USING_OFW_DIRECTFS + bool "Export fdt in direct access" + depends on RT_USING_OFW + select RT_USING_DFS + select RT_USING_DFS_DIRECTFS + default n diff --git a/components/drivers/ofw/base.c b/components/drivers/ofw/base.c index 562ce6093e04..ad7bf7b0d71b 100644 --- a/components/drivers/ofw/base.c +++ b/components/drivers/ofw/base.c @@ -8,6 +8,7 @@ * 2022-08-25 GuEe-GUI first version */ +#include #include #include @@ -27,7 +28,7 @@ struct rt_ofw_node *ofw_node_chosen = RT_NULL; struct rt_ofw_node *ofw_node_aliases = RT_NULL; struct rt_ofw_node *ofw_node_reserved_memory = RT_NULL; -static rt_phandle _phandle_range[2] = { 1, 1 }; +static rt_phandle _phandle_range[2] = { 1, 1 }, _phandle_next = 1; static struct rt_ofw_node **_phandle_hash = RT_NULL; static rt_list_t _aliases_nodes = RT_LIST_OBJECT_INIT(_aliases_nodes); @@ -35,6 +36,7 @@ static rt_list_t _aliases_nodes = RT_LIST_OBJECT_INIT(_aliases_nodes); rt_err_t ofw_phandle_hash_reset(rt_phandle min, rt_phandle max) { rt_err_t err = RT_EOK; + rt_phandle next = max; struct rt_ofw_node **hash_ptr = RT_NULL; max = RT_ALIGN(max, OFW_NODE_MIN_HASH); @@ -68,6 +70,7 @@ rt_err_t ofw_phandle_hash_reset(rt_phandle min, rt_phandle max) _phandle_range[0] = min; } _phandle_range[1] = max; + _phandle_next = next + 1; _phandle_hash = hash_ptr; } @@ -79,6 +82,40 @@ rt_err_t ofw_phandle_hash_reset(rt_phandle min, rt_phandle max) return err; } +static rt_phandle ofw_phandle_next(void) +{ + rt_phandle next; + static struct rt_spinlock op_lock = {}; + + rt_hw_spin_lock(&op_lock.lock); + + RT_ASSERT(_phandle_next != OFW_PHANDLE_MAX); + + if (_phandle_next <= _phandle_range[1]) + { + next = _phandle_next++; + } + else + { + rt_err_t err = ofw_phandle_hash_reset(_phandle_range[0], _phandle_next); + + if (!err) + { + next = _phandle_next++; + } + else + { + next = 0; + LOG_E("Expanded phandle hash[%u, %u] fail error = %s", + _phandle_range[0], _phandle_next + 1, rt_strerror(err)); + } + } + + rt_hw_spin_unlock(&op_lock.lock); + + return next; +} + static void ofw_prop_destroy(struct rt_ofw_prop *prop) { struct rt_ofw_prop *next; @@ -229,6 +266,7 @@ static void ofw_node_release(struct ref *r) struct rt_ofw_node *np = rt_container_of(r, struct rt_ofw_node, ref); LOG_E("%s is release", np->full_name); + (void)np; RT_ASSERT(0); } @@ -609,7 +647,7 @@ struct rt_ofw_node *rt_ofw_find_node_by_path(const char *path) break; } - path += len; + path += len + !!*next; } np = tmp; @@ -1030,9 +1068,9 @@ rt_err_t ofw_alias_scan(void) continue; } - end = name + rt_strlen(name); + end = name + rt_strlen(name) - 1; - while (*end && !(*end >= '0' && *end <= '9')) + while (*end && !(*end >= '0' && *end <= '9') && end > name) { --end; } @@ -1042,7 +1080,7 @@ rt_err_t ofw_alias_scan(void) id += (*end - '0') * rate; rate *= 10; - --end; + ++end; } info = rt_malloc(sizeof(*info)); @@ -1057,7 +1095,7 @@ rt_err_t ofw_alias_scan(void) info->id = id; info->tag = name; - info->tag_len = end - name; + info->tag_len = end - name - 1; info->np = tmp; rt_list_insert_after(&_aliases_nodes, &info->list); @@ -1091,6 +1129,32 @@ struct rt_ofw_node *rt_ofw_get_alias_node(const char *tag, int id) return np; } +int ofw_alias_node_id(struct rt_ofw_node *np) +{ + int id; + struct alias_info *info; + + if (np) + { + id = -1; + + rt_list_for_each_entry(info, &_aliases_nodes, list) + { + if (info->np == np) + { + id = info->id; + break; + } + } + } + else + { + id = -RT_EINVAL; + } + + return id; +} + int rt_ofw_get_alias_id(struct rt_ofw_node *np, const char *tag) { int id; @@ -1152,6 +1216,109 @@ int rt_ofw_get_alias_last_id(const char *tag) return id; } +struct rt_ofw_node *rt_ofw_append_child(struct rt_ofw_node *parent, const char *full_name) +{ + rt_phandle phandle; + rt_err_t err = RT_EOK; + fdt32_t *phandle_value; + struct rt_ofw_node *np = RT_NULL, *child; + + if (full_name) + { + if ((phandle = ofw_phandle_next())) + { + np = rt_calloc(1, sizeof(*np) + sizeof(*phandle_value)); + } + } + + if (np) + { + parent = parent ? : ofw_node_root; + + np->full_name = full_name; + np->phandle = phandle; + np->parent = parent; + + ref_init(&np->ref); + + phandle_value = (void *)np + sizeof(*np); + *phandle_value = cpu_to_fdt32(phandle); + + err = rt_ofw_append_prop(np, "phandle", sizeof(*phandle_value), phandle_value); + + if (!err) + { + if (parent->child) + { + rt_ofw_foreach_child_node(parent, child) + { + if (!child->sibling) + { + child->sibling = np; + rt_ofw_node_put(child); + break; + } + } + } + else + { + parent->child = np; + } + } + else + { + rt_free(np); + np = RT_NULL; + } + } + + return np; +} + +rt_err_t rt_ofw_append_prop(struct rt_ofw_node *np, const char *name, int length, void *value) +{ + rt_err_t err = RT_EOK; + + if (np && name && ((length && value) || (!length && !value))) + { + struct rt_ofw_prop *prop = rt_malloc(sizeof(*prop)), *last_prop; + + if (prop) + { + prop->name = name; + prop->length = length; + prop->value = value; + prop->next = RT_NULL; + + if (np->props) + { + rt_ofw_foreach_prop(np, last_prop) + { + if (!last_prop->next) + { + last_prop->next = prop; + break; + } + } + } + else + { + np->props = prop; + } + } + else + { + err = -RT_ENOMEM; + } + } + else + { + err = -RT_EINVAL; + } + + return err; +} + struct rt_ofw_node *rt_ofw_parse_phandle(const struct rt_ofw_node *np, const char *phandle_name, int index) { struct rt_ofw_cell_args args; @@ -1295,6 +1462,169 @@ int rt_ofw_count_phandle_cells(const struct rt_ofw_node *np, const char *list_na return count; } +static const char *ofw_get_prop_fuzzy_name(const struct rt_ofw_node *np, const char *name) +{ + char *sf, split_field[64]; + rt_size_t len = 0, max_ak = 0; + const char *str, *result = RT_NULL; + DECLARE_BITMAP(ak, sizeof(split_field)); + struct rt_ofw_prop *prop; + + /* + * List: + * + * node { + * property; + * front-prop-rear; + * front-prop; + * prop-rear; + * }; + * + * if call: + * ofw_get_prop_fuzzy_name(node, name): + * ["prop"] => property + * ["-prop"] => front-prop-rear + * ["prop-"] => front-prop-rear + * ["-prop$"] => front-prop + * ["^prop-"] => prop-rear + * ["-prop-"] => front-prop-rear + * ["front-*-rear"] => front-prop-rear + */ + + str = name; + sf = split_field; + + if (str[0] != '^') + { + /* As '*' */ + *sf++ = '\0'; + bitmap_set_bit(ak, len++); + } + else + { + ++str; + } + + for (; *str && len < sizeof(split_field); ++str, ++sf, ++len) + { + if (*str != '*') + { + *sf = *str; + bitmap_clear_bit(ak, len); + } + else + { + max_ak = len; + *sf = '\0'; + bitmap_set_bit(ak, len); + } + } + *sf = '\0'; + + if (str[-1] != '$') + { + /* As '*' */ + max_ak = len; + bitmap_set_bit(ak, len++); + } + else + { + sf[-1] = '\0'; + --len; + } + + sf = split_field; + + if (len >= sizeof(split_field)) + { + LOG_W("%s fuzzy name = %s len is %d out of %d", np->full_name, name, rt_strlen(name), sizeof(split_field)); + } + + rt_ofw_foreach_prop(np, prop) + { + int prep_ak = 0, next_ak, field; + rt_bool_t match = RT_TRUE; + const char *propname = prop->name, *fuzzy_name = sf; + + if (!bitmap_test_bit(ak, prep_ak)) + { + next_ak = bitmap_next_set_bit(ak, prep_ak + 1, max_ak) ? : len; + field = next_ak - prep_ak; + + if (rt_strncmp(propname, fuzzy_name, field)) + { + continue; + } + + propname += field; + fuzzy_name += field; + prep_ak = next_ak; + } + + bitmap_for_each_set_bit_from(ak, prep_ak, next_ak, max_ak) + { + /* Skip the '*' */ + if (prep_ak == next_ak) + { + ++fuzzy_name; + + next_ak = bitmap_next_set_bit(ak, prep_ak + 1, max_ak); + } + + if (!(str = rt_strstr(propname, fuzzy_name))) + { + match = RT_FALSE; + break; + } + + field = next_ak - prep_ak; + propname = str + field - 1; + fuzzy_name += field; + prep_ak = next_ak; + } + + if (match) + { + if ((max_ak || !split_field[0]) && next_ak >= max_ak && len - max_ak > 1) + { + if (next_ak == max_ak) + { + /* Skip the last '*' */ + ++fuzzy_name; + } + + if (!(propname = rt_strstr(propname, fuzzy_name))) + { + continue; + } + + /* Check end flag */ + if (propname[len - max_ak - 1] != '\0') + { + continue; + } + } + + result = prop->name; + break; + } + } + + return result; +} + +const char *rt_ofw_get_prop_fuzzy_name(const struct rt_ofw_node *np, const char *name) +{ + const char *propname = RT_NULL; + + if (np && name) + { + propname = ofw_get_prop_fuzzy_name(np, name); + } + + return propname; +} + struct rt_ofw_prop *rt_ofw_get_prop(const struct rt_ofw_node *np, const char *name, rt_ssize_t *out_length) { struct rt_ofw_prop *prop = RT_NULL; diff --git a/components/drivers/ofw/fdt.c b/components/drivers/ofw/fdt.c index 477511b96b4c..57b9dc84d73a 100755 --- a/components/drivers/ofw/fdt.c +++ b/components/drivers/ofw/fdt.c @@ -14,12 +14,17 @@ #include #include #include +#ifdef RT_USING_OFW_DIRECTFS +#include +#include +#endif #define DBG_TAG "rtdm.ofw" #define DBG_LVL DBG_INFO #include #include "ofw_internal.h" +#include "../serial/serial_dm.h" struct rt_fdt_earlycon fdt_earlycon rt_section(".bss.noclean.earlycon"); @@ -79,14 +84,13 @@ rt_uint64_t rt_fdt_translate_address(void *fdt, int nodeoffset, rt_uint64_t addr int addr_cells; int size_cells; } local, cpu; - int parent, length, group_len; + int parent, length = 0, group_len; const fdt32_t *ranges = RT_NULL; parent = fdt_parent_offset(fdt, nodeoffset); if (parent >= 0) { - length = 0; ranges = fdt_getprop(fdt, nodeoffset, "ranges", &length); } @@ -322,7 +326,7 @@ static rt_err_t fdt_reserved_memory_reg(int nodeoffset, const char *uname) } else { - while (len >= t_len) + for (; len >= t_len; len -= t_len) { base = rt_fdt_next_cell(&prop, _root_addr_cells); size = rt_fdt_next_cell(&prop, _root_size_cells); @@ -334,8 +338,6 @@ static rt_err_t fdt_reserved_memory_reg(int nodeoffset, const char *uname) base = rt_fdt_translate_address(_fdt, nodeoffset, base); reserve_memregion(fdt_get_name(_fdt, nodeoffset, RT_NULL), base, size); - - len -= t_len; } } } @@ -665,12 +667,17 @@ void rt_fdt_earlycon_kick(int why) fdt_earlycon.console_kick(&fdt_earlycon, why); } - if (why == FDT_EARLYCON_KICK_COMPLETED && fdt_earlycon.msg_idx) + if (why == FDT_EARLYCON_KICK_COMPLETED) { - fdt_earlycon.msg_idx = 0; + fdt_earlycon.console_putc = RT_NULL; - /* Dump old messages */ - rt_kputs(fdt_earlycon.msg); + if (fdt_earlycon.msg_idx) + { + fdt_earlycon.msg_idx = 0; + + /* Dump old messages */ + rt_kputs(fdt_earlycon.msg); + } } } @@ -788,6 +795,8 @@ rt_err_t rt_fdt_scan_chosen_stdout(void) if (options && *options && *options != ' ') { options_len = rt_strchrnul(options, ' ') - options; + + rt_strncpy(fdt_earlycon.options, options, options_len); } /* console > stdout-path */ @@ -825,7 +834,7 @@ rt_err_t rt_fdt_scan_chosen_stdout(void) if (best_earlycon_id && best_earlycon_id->setup) { - rt_bool_t used_options = RT_FALSE; + const char earlycon_magic[] = { 'O', 'F', 'W', '\0' }; if (!con_type) { @@ -834,24 +843,25 @@ rt_err_t rt_fdt_scan_chosen_stdout(void) fdt_earlycon.fdt = _fdt; fdt_earlycon.nodeoffset = offset; - err = best_earlycon_id->setup(&fdt_earlycon, options); + options = &fdt_earlycon.options[options_len + 1]; + rt_strncpy((void *)options, earlycon_magic, RT_ARRAY_SIZE(earlycon_magic)); + + err = best_earlycon_id->setup(&fdt_earlycon, fdt_earlycon.options); - for (int i = 0; i < options_len; ++i) + if (rt_strncmp(options, earlycon_magic, RT_ARRAY_SIZE(earlycon_magic))) { - if (options[i] == RT_FDT_EARLYCON_OPTION_SIGNATURE) + const char *option_start = options - 1; + + while (option_start[-1] != '\0') { - /* Restore ',' */ - ((char *)options)[i++] = ','; - options = &options[i]; - options_len -= i; - used_options = RT_TRUE; - break; + --option_start; } + + rt_memmove(fdt_earlycon.options, option_start, options - option_start); } - if (!used_options) + else { - options = RT_NULL; - options_len = 0; + fdt_earlycon.options[0] = '\0'; } } } @@ -878,8 +888,57 @@ rt_err_t rt_fdt_scan_chosen_stdout(void) if (fdt_earlycon.mmio) { - LOG_I("Earlycon: %s at MMIO/PIO %p (options '%.*s')", - con_type, fdt_earlycon.mmio, options_len, options ? options : ""); + LOG_I("Earlycon: %s at MMIO/PIO %p (options '%s')", + con_type, fdt_earlycon.mmio, fdt_earlycon.options); + } + + return err; +} + +rt_err_t rt_fdt_bootargs_select(const char *key, int index, const char **out_result) +{ + rt_err_t err; + + if (key && index >= 0 && out_result) + { + int offset = fdt_path_offset(_fdt, "/chosen"); + + if (offset >= 0) + { + int len, key_len = rt_strlen(key); + const char *bootargs = fdt_getprop(_fdt, offset, "bootargs", &len), *end; + + end = bootargs + len; + err = -RT_EEMPTY; + + for (int i = 0; bootargs < end; ++i) + { + bootargs = rt_strstr(bootargs, key); + + if (!bootargs) + { + break; + } + + bootargs += key_len; + + if (i == index) + { + *out_result = bootargs; + + err = -RT_EOK; + break; + } + } + } + else + { + err = -RT_ERROR; + } + } + else + { + err = -RT_EINVAL; } return err; @@ -936,6 +995,45 @@ rt_err_t rt_fdt_unflatten(void) return err; } +#ifdef RT_USING_OFW_DIRECTFS +static rt_ssize_t dts_directfs_read_raw(rt_object_t obj, struct directfs_bin_attribute *attr, + char *buffer, rt_off_t pos, rt_size_t count) +{ + rt_ssize_t res; + rt_size_t size; + struct rt_ofw_prop *prop = rt_container_of(obj, struct rt_ofw_prop, obj); + + size = prop->length; + + if (pos <= size) + { + if (count > size - pos) + { + count = size - pos; + } + + rt_memcpy(buffer, prop->value + pos, count); + + res = count; + } + else + { + res = -RT_EINVAL; + } + + return res; +} + +static struct directfs_bin_attribute dts_directfs_attr_raw = +{ + .attr = + { + .name = "raw", + }, + .read = dts_directfs_read_raw, +}; +#endif /* RT_USING_OFW_DIRECTFS */ + static rt_err_t fdt_unflatten_props(struct rt_ofw_node *np, int node_off) { rt_err_t err = RT_EOK; @@ -964,6 +1062,11 @@ static rt_err_t fdt_unflatten_props(struct rt_ofw_node *np, int node_off) np->name = prop->value; } + #ifdef RT_USING_OFW_DIRECTFS + dfs_directfs_create_link(&np->obj, &prop->obj, prop->name); + dfs_directfs_create_bin_file(&prop->obj, &dts_directfs_attr_raw); + #endif + prop_off = fdt_next_property_offset(_fdt, prop_off); if (prop_off < 0) @@ -1009,6 +1112,13 @@ static rt_err_t fdt_unflatten_single(struct rt_ofw_node *np, int node_off) } } + #ifdef RT_USING_OFW_DIRECTFS + if (parent) + { + dfs_directfs_create_link(&parent->obj, &np->obj, np->full_name); + } + #endif + if ((err = fdt_unflatten_props(np, node_off))) { break; @@ -1070,11 +1180,52 @@ static rt_err_t fdt_unflatten_single(struct rt_ofw_node *np, int node_off) return err; } +#ifdef RT_USING_OFW_DIRECTFS +static rt_ssize_t fdt_directfs_read_raw(rt_object_t obj, struct directfs_bin_attribute *attr, + char *buffer, rt_off_t pos, rt_size_t count) +{ + rt_ssize_t res; + void *fdt = ((struct fdt_info *)ofw_node_root->name)->fdt; + rt_size_t fdt_size = fdt_totalsize(fdt); + + if (pos <= fdt_size) + { + if (count > fdt_size - pos) + { + count = fdt_size - pos; + } + + rt_memcpy(buffer, fdt + pos, count); + + res = count; + } + else + { + res = -RT_EINVAL; + } + + return res; +} + +static struct directfs_bin_attribute fdt_directfs_attr_raw = +{ + .attr = + { + .name = "raw", + }, + .read = fdt_directfs_read_raw, +}; +#endif /* RT_USING_OFW_DIRECTFS */ + struct rt_ofw_node *rt_fdt_unflatten_single(void *fdt) { int root_off; struct fdt_info *header; struct rt_ofw_node *root = RT_NULL; +#ifdef RT_USING_OFW_DIRECTFS + rt_object_t fdt_fsobj; + rt_object_t firmware = dfs_directfs_find_object(RT_NULL, "firmware"); +#endif if (fdt && (root_off = fdt_path_offset(fdt, "/")) >= 0) { @@ -1092,9 +1243,23 @@ struct rt_ofw_node *rt_fdt_unflatten_single(void *fdt) header->rsvmap = (struct fdt_reserve_entry *)((void *)fdt + fdt_off_mem_rsvmap(fdt)); header->rsvmap_nr = fdt_num_mem_rsv(fdt); + #ifdef RT_USING_OFW_DIRECTFS + dfs_directfs_create_link(firmware, &root->obj, "devicetree"); + #endif + if (!fdt_unflatten_single(root, root_off)) { root->name = (const char *)header; + + #ifdef RT_USING_OFW_DIRECTFS + fdt_fsobj = rt_malloc(sizeof(*fdt_fsobj)); + + if (fdt_fsobj) + { + dfs_directfs_create_link(firmware, fdt_fsobj, "fdt"); + dfs_directfs_create_bin_file(fdt_fsobj, &fdt_directfs_attr_raw); + } + #endif } else { diff --git a/components/drivers/ofw/io.c b/components/drivers/ofw/io.c index b5d6b584251f..5f4892c1485c 100755 --- a/components/drivers/ofw/io.c +++ b/components/drivers/ofw/io.c @@ -10,7 +10,6 @@ #include -#include #include #include #include @@ -155,7 +154,7 @@ static rt_err_t ofw_get_address_by_name(struct rt_ofw_node *np, const char *name { int index = 0; - rt_err_t err = RT_EOK; + rt_err_t err = -RT_EEMPTY; const char *reg_name; struct rt_ofw_prop *prop; @@ -386,6 +385,22 @@ rt_uint64_t rt_ofw_translate_address(struct rt_ofw_node *np, const char *range_t return cpu_addr; } +#ifdef ARCH_CPU_64BIT +#define ofw_address_cpu_cast(np, address) (void *)(address) +#else +#define ofw_address_cpu_cast(np, address) \ +({ \ + if (((address) >> 32)) \ + { \ + LOG_W("%s find 64 bits address = %x%x", \ + rt_ofw_node_full_name(np), \ + ofw_static_cast(rt_ubase_t, (address) >> 32), \ + ofw_static_cast(rt_ubase_t, (address))); \ + } \ + (void *)ofw_static_cast(rt_ubase_t, (address)); \ +}) +#endif + void *rt_ofw_iomap(struct rt_ofw_node *np, int index) { void *iomem = RT_NULL; @@ -396,7 +411,7 @@ void *rt_ofw_iomap(struct rt_ofw_node *np, int index) if (!ofw_get_address(np, index, ®s[0], ®s[1])) { - iomem = rt_ioremap((void *)regs[0], (size_t)regs[1]); + iomem = rt_ioremap(ofw_address_cpu_cast(np, regs[0]), (size_t)regs[1]); } } @@ -413,7 +428,7 @@ void *rt_ofw_iomap_by_name(struct rt_ofw_node *np, const char *name) if (!ofw_get_address_by_name(np, name, ®s[0], ®s[1])) { - iomem = rt_ioremap((void *)regs[0], (size_t)regs[1]); + iomem = rt_ioremap(ofw_address_cpu_cast(np, regs[0]), (size_t)regs[1]); } } diff --git a/components/drivers/ofw/irq.c b/components/drivers/ofw/irq.c index f5457453b4ab..74a57b235335 100755 --- a/components/drivers/ofw/irq.c +++ b/components/drivers/ofw/irq.c @@ -67,7 +67,7 @@ static rt_err_t ofw_parse_irq_map(struct rt_ofw_node *np, struct rt_ofw_cell_arg * child interrupt specifier * The interrupt specifier of the child node being mapped. The number * of 32-bit cells required to specify this component is described by - * the #interrupt-cells property of this node—the nexus node containing + * the #interrupt-cells property of this node-the nexus node containing * the interrupt-map property. * * interrupt-parent @@ -130,7 +130,7 @@ static rt_err_t ofw_parse_irq_map(struct rt_ofw_node *np, struct rt_ofw_cell_arg * <0x1800 0 0 4 &gic 0 0 GIC_SPI 3 IRQ_TYPE_LEVEL_HIGH>; // INTD * }; * - * In fact, basically no SoC will be use multi ic to implemented INTx. + * In fact, almost no SoC will be use multi IC to implement INTx. * before call ofw_parse_irq_map(np, &args): * * args.data = addr; @@ -196,6 +196,9 @@ static rt_err_t ofw_parse_irq_map(struct rt_ofw_node *np, struct rt_ofw_cell_arg break; } + map_len /= sizeof(fdt32_t); + map_mask_len /= sizeof(fdt32_t); + err = -RT_EINVAL; addr = irq_args->data; @@ -491,7 +494,7 @@ rt_err_t rt_ofw_parse_irq_cells(struct rt_ofw_node *np, int index, struct rt_ofw struct rt_ofw_node *rt_ofw_find_irq_parent(struct rt_ofw_node *np, int *out_interrupt_cells) { - rt_ofw_foreach_parent_node(np) + for (np = rt_ofw_node_get(np); np; np = rt_ofw_get_next_parent(np)) { rt_phandle ic_phandle; @@ -523,7 +526,7 @@ static int ofw_map_irq(struct rt_ofw_cell_args *irq_args) { int irq; struct rt_ofw_node *ic_np = irq_args->data; - struct rt_pic *pic = rt_ofw_data(ic_np); + struct rt_pic *pic = rt_pic_dynamic_cast(rt_ofw_data(ic_np)); /* args.data is "interrupt-controller" */ if (pic) @@ -611,7 +614,26 @@ int rt_ofw_get_irq(struct rt_ofw_node *np, int index) if (irq >= 0) { + rt_phandle cpu_phandle; + irq = ofw_map_irq(&irq_args); + + if (irq >= 0 && !rt_ofw_prop_read_u32_index(np, "interrupt-affinity", index, &cpu_phandle)) + { + rt_uint64_t cpuid = rt_ofw_get_cpu_id(rt_ofw_find_node_by_phandle(cpu_phandle)); + + if ((rt_int64_t)cpuid >= 0) + { + DECLARE_BITMAP(affinity, RT_CPUS_NR) = { 0 }; + + bitmap_set_bit(affinity, cpuid); + + if (rt_pic_irq_set_affinity(irq, affinity) == -RT_ENOSYS) + { + LOG_W("%s irq affinity init fail", np->full_name); + } + } + } } } else diff --git a/components/drivers/ofw/ofw.c b/components/drivers/ofw/ofw.c index 88a5fe963d04..8d11008c2250 100755 --- a/components/drivers/ofw/ofw.c +++ b/components/drivers/ofw/ofw.c @@ -16,6 +16,7 @@ #include #include "ofw_internal.h" +#include "../serial/serial_dm.h" struct rt_ofw_stub *rt_ofw_stub_probe_range(struct rt_ofw_node *np, const struct rt_ofw_stub *stub_start, const struct rt_ofw_stub *stub_end) @@ -59,7 +60,7 @@ struct rt_ofw_stub *rt_ofw_stub_probe_range(struct rt_ofw_node *np, static const char *ofw_console_serial_find(char *dst_con, struct rt_ofw_node *np) { - rt_object_t rt_obj; + rt_object_t rt_obj = RT_NULL; const char *ofw_name = RT_NULL; struct rt_serial_device *rt_serial = rt_ofw_data(np); @@ -83,6 +84,41 @@ static const char *ofw_console_serial_find(char *dst_con, struct rt_ofw_node *np return ofw_name; } +static int tty_device_compare(rt_device_t dev, void *data) +{ + rt_ubase_t *args_list; + char *dst_con; + const char *console, **ofw_name; + const struct rt_ofw_node_id *id; + struct rt_platform_device *pdev; + int *tty_idx, tty_id, tty_name_len; + + pdev = rt_container_of(dev, struct rt_platform_device, parent); + id = pdev->id; + + args_list = data; + tty_idx = (int *)args_list[0]; + tty_id = args_list[1]; + console = (const char *)args_list[2]; + tty_name_len = args_list[3]; + dst_con = (char *)args_list[4]; + ofw_name = (const char **)args_list[5]; + + if (id && id->type[0] && !rt_strncmp(id->type, console, tty_name_len)) + { + if (*tty_idx == tty_id) + { + *ofw_name = ofw_console_serial_find(dst_con, pdev->parent.ofw_node); + + return RT_EOK; + } + + ++*tty_idx; + } + + return -RT_EEMPTY; +} + static const char *ofw_console_tty_find(char *dst_con, const char *con) { const char *ofw_name = RT_NULL; @@ -95,8 +131,7 @@ static const char *ofw_console_tty_find(char *dst_con, const char *con) if (platform_bus) { - rt_device_t dev; - rt_ubase_t level; + rt_ubase_t args_list[6]; const char *console = con; int tty_idx = 0, tty_id = 0, tty_name_len; @@ -117,27 +152,13 @@ static const char *ofw_console_tty_find(char *dst_con, const char *con) ++con; } - level = rt_spin_lock_irqsave(&platform_bus->spinlock); - - rt_list_for_each_entry(dev, &platform_bus->dev_list, node) - { - struct rt_platform_device *pdev = rt_container_of(dev, struct rt_platform_device, parent); - const struct rt_ofw_node_id *id = pdev->id; - - if (id && id->type[0] && !rt_strncmp(id->type, console, tty_name_len)) - { - if (tty_idx == tty_id) - { - ofw_name = ofw_console_serial_find(dst_con, pdev->parent.ofw_node); - - break; - } - - ++tty_idx; - } - } - - rt_spin_unlock_irqrestore(&platform_bus->spinlock, level); + args_list[0] = (rt_ubase_t)&tty_idx; + args_list[1] = tty_id; + args_list[2] = (rt_ubase_t)console; + args_list[3] = tty_name_len; + args_list[4] = (rt_ubase_t)dst_con; + args_list[5] = (rt_ubase_t)&ofw_name; + rt_bus_for_each_dev(platform_bus, &args_list, tty_device_compare); } return ofw_name; @@ -146,7 +167,7 @@ static const char *ofw_console_tty_find(char *dst_con, const char *con) rt_err_t rt_ofw_console_setup(void) { rt_err_t err = -RT_ENOSYS; - char con_name[RT_NAME_MAX]; + char con_name[RT_NAME_MAX], *options = RT_NULL; const char *ofw_name = RT_NULL, *stdout_path, *con; /* chosen.console > chosen.stdout-path > RT_CONSOLE_DEVICE_NAME */ @@ -168,6 +189,19 @@ rt_err_t rt_ofw_console_setup(void) if (ofw_name) { + const char *ch = con; + + /* Find 'ttyX,BBBBPNF' */ + while (*ch && *ch != ' ') + { + if (*ch++ == ',') + { + options = (char *)ch; + + break; + } + } + err = RT_EOK; break; } @@ -208,6 +242,19 @@ rt_err_t rt_ofw_console_setup(void) rt_console_set_device(con); + /* Reconfigure serial by options */ + if (options) + { + rt_device_t con_dev = rt_console_get_device(); + + if (con_dev) + { + struct serial_configure con_conf = serial_cfg_from_args(options); + + rt_device_control(con_dev, RT_DEVICE_CTRL_CONFIG, &con_conf); + } + } + rt_fdt_earlycon_kick(FDT_EARLYCON_KICK_COMPLETED); LOG_I("Console: %s (%s)", con, ofw_name ? ofw_name : ""); @@ -252,6 +299,11 @@ const char *rt_ofw_bootargs_select(const char *key, int index) { *(char *)ch++ = '\0'; } + if (*ch == '\0') + { + /* space in the end */ + --bootargs_nr; + } --ch; } @@ -461,6 +513,11 @@ static void ofw_node_dump_dts(struct rt_ofw_node *np, rt_bool_t sibling_too) dts_put_depth(depth); rt_kputs("};\n"); + if (!sibling_too && org_np == np) + { + break; + } + while (np->parent && !np->sibling) { np = np->parent; @@ -497,7 +554,14 @@ void rt_ofw_node_dump_dts(struct rt_ofw_node *np, rt_bool_t sibling_too) struct fdt_info *header = (struct fdt_info *)np->name; struct fdt_reserve_entry *rsvmap = header->rsvmap; - rt_kprintf("/dts-v%x/;\n\n", fdt_version(header->fdt)); + /* + * shall be present to identify the file as a version 1 DTS + * (dts files without this tag will be treated by dtc + * as being in the obsolete version 0, which uses + * a different format for integers in addition to + * other small but incompatible changes). + */ + rt_kprintf("/dts-v1/;\n\n"); for (int i = header->rsvmap_nr - 1; i >= 0; --i) { diff --git a/components/drivers/ofw/ofw_internal.h b/components/drivers/ofw/ofw_internal.h index 619cf4a92260..34ff11c39695 100755 --- a/components/drivers/ofw/ofw_internal.h +++ b/components/drivers/ofw/ofw_internal.h @@ -69,6 +69,8 @@ extern struct rt_fdt_earlycon fdt_earlycon; (to_type)(((value) >> ((sizeof(value) - sizeof(to_type)) * 8))) rt_err_t ofw_alias_scan(void); +int ofw_alias_node_id(struct rt_ofw_node *np); + rt_err_t ofw_phandle_hash_reset(rt_phandle min, rt_phandle max); #endif /* __OFW_INTERNAL_H__ */ diff --git a/components/drivers/pci/Kconfig b/components/drivers/pci/Kconfig index 20e725e77aac..1ceb757728d0 100644 --- a/components/drivers/pci/Kconfig +++ b/components/drivers/pci/Kconfig @@ -1,6 +1,7 @@ menuconfig RT_USING_PCI bool "Using Peripheral Component Interconnect Express (PCIe/PCI)" depends on RT_USING_DM + depends on RT_USING_PIC default n config RT_PCI_MSI @@ -8,9 +9,28 @@ config RT_PCI_MSI depends on RT_USING_PCI default y +config RT_PCI_SYS_64BIT + bool "System 64bit" + depends on RT_USING_PCI + depends on ARCH_CPU_64BIT + default y + +config RT_PCI_CACHE_LINE_SIZE + int "Cache line size" + depends on RT_USING_PCI + default 8 if ARCH_CPU_64BIT + default 4 + +config RT_PCI_LOCKLESS + bool "Lock less in PCI options" + depends on RT_USING_PCI + default n + config RT_PCI_ECAM bool "PCIe ECAM" depends on RT_USING_PCI default y help PCIe Express Enhanced Configuration Access Mechanism + +source "$RTT_DIR/components/drivers/pci/host/Kconfig" diff --git a/components/drivers/pci/SConscript b/components/drivers/pci/SConscript new file mode 100644 index 000000000000..40a6cabb8fde --- /dev/null +++ b/components/drivers/pci/SConscript @@ -0,0 +1,28 @@ +from building import * + +objs = [] + +if not GetDepend(['RT_USING_PCI']): + Return('objs') + +cwd = GetCurrentDir() +list = os.listdir(cwd) +CPPPATH = [cwd + '/../include'] + +src = ['access.c', 'host-bridge.c', 'irq.c', 'pci.c', 'probe.c'] + +if GetDepend(['RT_USING_OFW']): + src += ['ofw.c'] + +if GetDepend(['RT_PCI_ECAM']): + src += ['ecam.c'] + +group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) + +for d in list: + path = os.path.join(cwd, d) + if os.path.isfile(os.path.join(path, 'SConscript')): + objs = objs + SConscript(os.path.join(d, 'SConscript')) +objs = objs + group + +Return('objs') diff --git a/components/drivers/pci/access.c b/components/drivers/pci/access.c new file mode 100755 index 000000000000..9a389485a45d --- /dev/null +++ b/components/drivers/pci/access.c @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-10-24 GuEe-GUI first version + */ + +#include +#include + +#include + +struct rt_spinlock rt_pci_lock = { 0 }; + +#ifdef RT_PCI_LOCKLESS +#define pci_lock_config(l) do { (void)(l); } while (0) +#define pci_unlock_config(l) do { (void)(l); } while (0) +#else +#define pci_lock_config(l) l = rt_spin_lock_irqsave(&rt_pci_lock) +#define pci_unlock_config(l) rt_spin_unlock_irqrestore(&rt_pci_lock, l) +#endif + +#define PCI_OPS_READ(name, type) \ +rt_err_t rt_pci_bus_read_config_##name(struct rt_pci_bus *bus, rt_uint32_t devfn, int reg, type *value) \ +{ \ + rt_err_t err; \ + rt_ubase_t level; \ + rt_uint32_t data = 0; \ + pci_lock_config(level); \ + err = bus->ops->read(bus, devfn, reg, sizeof(type), &data); \ + *value = err ? (type)(~0) : (type)data; \ + pci_unlock_config(level); \ + return err; \ +} + +#define PCI_OPS_WRITE(name, type) \ +rt_err_t rt_pci_bus_write_config_##name(struct rt_pci_bus *bus, rt_uint32_t devfn, int reg, type value) \ +{ \ + rt_err_t err; \ + rt_ubase_t level; \ + pci_lock_config(level); \ + err = bus->ops->write(bus, devfn, reg, sizeof(type), value); \ + pci_unlock_config(level); \ + return err; \ +} + +#define PCI_OPS(name, type) \ + PCI_OPS_READ(name, type) \ + PCI_OPS_WRITE(name, type) + +PCI_OPS(u8, rt_uint8_t) +PCI_OPS(u16, rt_uint16_t) +PCI_OPS(u32, rt_uint32_t) + +#undef PCI_OP_WRITE +#undef PCI_OP_READ +#undef PCI_OPS + +rt_err_t rt_pci_bus_read_config_uxx(struct rt_pci_bus *bus, rt_uint32_t devfn, int reg, int width, rt_uint32_t *val) +{ + void *base; + + if ((base = bus->ops->map(bus, devfn, reg))) + { + if (width == 1) + { + *val = HWREG8(base); + } + else if (width == 2) + { + *val = HWREG16(base); + } + else + { + *val = HWREG32(base); + } + + return RT_EOK; + } + + return -RT_ERROR; +} + +rt_err_t rt_pci_bus_write_config_uxx(struct rt_pci_bus *bus, rt_uint32_t devfn, int reg, int width, rt_uint32_t val) +{ + void *base; + + if ((base = bus->ops->map(bus, devfn, reg))) + { + if (width == 1) + { + HWREG8(base) = val; + } + else if (width == 2) + { + HWREG16(base) = val; + } + else + { + HWREG32(base) = val; + } + + return RT_EOK; + } + + return -RT_ERROR; +} diff --git a/components/drivers/pci/ecam.c b/components/drivers/pci/ecam.c new file mode 100644 index 000000000000..559e0d8d5041 --- /dev/null +++ b/components/drivers/pci/ecam.c @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-10-24 GuEe-GUI first version + */ + +#include +#include + +#define DBG_TAG "pci.ecam" +#define DBG_LVL DBG_INFO +#include + +#include "ecam.h" + +struct pci_ecam_config_window *pci_ecam_create(struct rt_pci_host_bridge *host_bridge, + const struct pci_ecam_ops *ops) +{ + struct pci_ecam_config_window *conf_win = rt_calloc(1, sizeof(*conf_win)); + + if (!conf_win) + { + return RT_NULL; + } + + conf_win->bus_range = host_bridge->bus_range; + conf_win->bus_shift = ops->bus_shift; + conf_win->ops = ops; + + host_bridge->ops = (struct rt_pci_ops *)&ops->pci_ops; + + return conf_win; +} + +void *pci_ecam_map(struct rt_pci_bus *bus, rt_uint32_t devfn, int where) +{ + struct pci_ecam_config_window *conf_win = bus->sysdata; + const struct pci_ecam_ops *eops = conf_win->ops; + void *win = conf_win->win, *map; + rt_uint32_t busn = bus->number, bus_shift = eops->bus_shift, devfn_shift = eops->bus_shift - 8; + + if (eops->bus_shift) + { + rt_uint32_t bus_offset = (busn & PCIE_ECAM_BUS_MASK) << bus_shift; + rt_uint32_t devfn_offset = (devfn & PCIE_ECAM_DEVFN_MASK) << devfn_shift; + + where &= PCIE_ECAM_REG_MASK; + map = win + (bus_offset | devfn_offset | where); + } + else + { + map = win + PCIE_ECAM_OFFSET(busn, devfn, where); + } + + return map; +} + +const struct pci_ecam_ops pci_generic_ecam_ops = +{ + .pci_ops = + { + .map = pci_ecam_map, + .read = rt_pci_bus_read_config_uxx, + .write = rt_pci_bus_write_config_uxx, + } +}; diff --git a/components/drivers/pci/ecam.h b/components/drivers/pci/ecam.h new file mode 100644 index 000000000000..97491311d730 --- /dev/null +++ b/components/drivers/pci/ecam.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-10-24 GuEe-GUI first version + */ + +#ifndef __RT_PCI_ECAM_H__ +#define __RT_PCI_ECAM_H__ + +#include +#include +#include +#include + +/* + * Memory address shift values for the byte-level address that + * can be used when accessing the PCI Express Configuration Space. + */ + +/* + * Enhanced Configuration Access Mechanism (ECAM) + * + * See PCI Express Base Specification, Revision 5.0, Version 1.0, + * Section 7.2.2, Table 7-1, p. 677. + */ +#define PCIE_ECAM_BUS_SHIFT 20 /* Bus number */ +#define PCIE_ECAM_DEVFN_SHIFT 12 /* Device and Function number */ + +#define PCIE_ECAM_BUS_MASK 0xff +#define PCIE_ECAM_DEVFN_MASK 0xff +#define PCIE_ECAM_REG_MASK 0xfff /* Limit offset to a maximum of 4K */ + +#define PCIE_ECAM_BUS(x) (((x) & PCIE_ECAM_BUS_MASK) << PCIE_ECAM_BUS_SHIFT) +#define PCIE_ECAM_DEVFN(x) (((x) & PCIE_ECAM_DEVFN_MASK) << PCIE_ECAM_DEVFN_SHIFT) +#define PCIE_ECAM_REG(x) ((x) & PCIE_ECAM_REG_MASK) + +#define PCIE_ECAM_OFFSET(bus, devfn, where) \ + (PCIE_ECAM_BUS(bus) | PCIE_ECAM_DEVFN(devfn) | PCIE_ECAM_REG(where)) + +struct pci_ecam_ops +{ + rt_uint32_t bus_shift; + struct rt_pci_ops pci_ops; +}; + +struct pci_ecam_config_window +{ + rt_uint32_t *bus_range; + rt_uint32_t bus_shift; + + void *win; + void *priv; + const struct pci_ecam_ops *ops; +}; + +/* Default ECAM ops */ +extern const struct pci_ecam_ops pci_generic_ecam_ops; + +void *pci_ecam_map(struct rt_pci_bus *bus, rt_uint32_t devfn, int where); +struct pci_ecam_config_window *pci_ecam_create(struct rt_pci_host_bridge *host_bridge, + const struct pci_ecam_ops *ops); +rt_err_t pci_host_common_probe(struct rt_platform_device *pdev); +rt_err_t pci_host_common_remove(struct rt_platform_device *pdev); + +#endif /* __RT_PCI_ECAM_H__ */ diff --git a/components/drivers/pci/host-bridge.c b/components/drivers/pci/host-bridge.c new file mode 100644 index 000000000000..11067323f0cc --- /dev/null +++ b/components/drivers/pci/host-bridge.c @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-10-24 GuEe-GUI first version + */ + +#include + +#include + +static rt_err_t host_bridge_probe(struct rt_pci_device *pdev) +{ + rt_err_t err = -RT_EINVAL; + + if (pdev->class >> 8 == PCIS_BRIDGE_HOST) + { + err = RT_EOK; + } + + return err; +} + +static struct rt_pci_device_id host_bridge_pci_ids[] = +{ + { RT_PCI_DEVICE_ID(PCI_VENDOR_ID_REDHAT, 0x0008) }, + { /* sentinel */ } +}; + +static struct rt_pci_driver host_bridge_driver = +{ + .name = "host-bridge", + + .ids = host_bridge_pci_ids, + .probe = host_bridge_probe, +}; +RT_PCI_DRIVER_EXPORT(host_bridge_driver); diff --git a/components/drivers/pci/host/Kconfig b/components/drivers/pci/host/Kconfig new file mode 100644 index 000000000000..446d6f2ef8df --- /dev/null +++ b/components/drivers/pci/host/Kconfig @@ -0,0 +1,12 @@ +config RT_PCI_HOST_COMMON + bool "Common PCI host controller" + depends on RT_PCI_ECAM + default y + +config RT_PCI_HOST_GENERIC + bool "Generic PCI host controller" + depends on RT_PCI_ECAM + select RT_PCI_HOST_COMMON + default y + +source "$RTT_DIR/components/drivers/pci/host/dw/Kconfig" diff --git a/components/drivers/pci/host/SConscript b/components/drivers/pci/host/SConscript new file mode 100644 index 000000000000..91555f88d4e1 --- /dev/null +++ b/components/drivers/pci/host/SConscript @@ -0,0 +1,25 @@ +from building import * + +objs = [] + +cwd = GetCurrentDir() +list = os.listdir(cwd) +CPPPATH = [cwd + '/../../include'] + +src = [] + +if GetDepend(['RT_PCI_HOST_COMMON']): + src += ['pci-host-common.c'] + +if GetDepend(['RT_PCI_HOST_GENERIC']): + src += ['pci-host-generic.c'] + +group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) + +for d in list: + path = os.path.join(cwd, d) + if os.path.isfile(os.path.join(path, 'SConscript')): + objs = objs + SConscript(os.path.join(d, 'SConscript')) +objs = objs + group + +Return('objs') diff --git a/components/drivers/pci/host/dw/Kconfig b/components/drivers/pci/host/dw/Kconfig new file mode 100644 index 000000000000..8928820faf7f --- /dev/null +++ b/components/drivers/pci/host/dw/Kconfig @@ -0,0 +1,13 @@ +config RT_PCI_DW_HOST + bool "DesignWare-based PCIe host" + depends on RT_USING_PCI + default n + +config RT_PCI_DW_HOST_ROCKCHIP + bool "Rockchip DesignWare PCIe host" + depends on RT_PCI_DW_HOST + select RT_USING_PHY + select RT_USING_RESET + select RT_MFD_SYSCON + select RT_USING_REGULATOR + default n diff --git a/components/drivers/pci/host/dw/SConscript b/components/drivers/pci/host/dw/SConscript new file mode 100644 index 000000000000..19100e7df7ba --- /dev/null +++ b/components/drivers/pci/host/dw/SConscript @@ -0,0 +1,18 @@ +from building import * + +group = [] + +if not GetDepend(['RT_PCI_DW_HOST']): + Return('group') + +cwd = GetCurrentDir() +CPPPATH = [cwd + '/../../../include'] + +src = ['pcie-dw.c', 'pcie-dw_ep.c', 'pcie-dw_host.c', 'pcie-dw_platfrom.c'] + +if GetDepend(['RT_PCI_DW_HOST_ROCKCHIP']): + src += ['pcie-dw-rockchip.c'] + +group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') diff --git a/components/drivers/pci/host/dw/pcie-dw-rockchip.c b/components/drivers/pci/host/dw/pcie-dw-rockchip.c new file mode 100644 index 000000000000..a6057161bc47 --- /dev/null +++ b/components/drivers/pci/host/dw/pcie-dw-rockchip.c @@ -0,0 +1,504 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-09-23 GuEe-GUI first version + */ + +#define DBG_TAG "pci.dw.rockchip" +#define DBG_LVL DBG_INFO +#include + +#include "pcie-dw.h" + +/* + * The upper 16 bits of PCIE_CLIENT_CONFIG are a write + * mask for the lower 16 bits. + */ +#define HIWORD_UPDATE(mask, val) (((mask) << 16) | (val)) +#define HIWORD_UPDATE_BIT(val) HIWORD_UPDATE(val, val) +#define HIWORD_DISABLE_BIT(val) HIWORD_UPDATE(val, ~val) + +#define PCIE_CLIENT_RC_MODE HIWORD_UPDATE_BIT(0x40) +#define PCIE_CLIENT_ENABLE_LTSSM HIWORD_UPDATE_BIT(0xc) +#define PCIE_SMLH_LINKUP RT_BIT(16) +#define PCIE_RDLH_LINKUP RT_BIT(17) +#define PCIE_LINKUP (PCIE_SMLH_LINKUP | PCIE_RDLH_LINKUP) +#define PCIE_L0S_ENTRY 0x11 +#define PCIE_CLIENT_GENERAL_CONTROL 0x0 +#define PCIE_CLIENT_INTR_STATUS_LEGACY 0x8 +#define PCIE_CLIENT_INTR_MASK_LEGACY 0x1c +#define PCIE_CLIENT_GENERAL_DEBUG 0x104 +#define PCIE_CLIENT_HOT_RESET_CTRL 0x180 +#define PCIE_CLIENT_LTSSM_STATUS 0x300 +#define PCIE_LTSSM_ENABLE_ENHANCE RT_BIT(4) +#define PCIE_LTSSM_STATUS_MASK RT_GENMASK(5, 0) + +struct rockchip_pcie +{ + struct dw_pcie pci; + void *apb_base; + + rt_base_t rst_pin; + struct rt_phy_device *phy; + struct rt_clk_array *clk_arr; + struct rt_reset_control *rstc; + struct rt_regulator *vpcie3v3; + + int intx_irq; + struct rt_pic intx_pic; +}; + +#define to_rockchip_pcie(dw_pcie) rt_container_of(dw_pcie, struct rockchip_pcie, pci) + +rt_inline rt_uint32_t rockchip_pcie_readl_apb(struct rockchip_pcie *rk_pcie, + int offset) +{ + return HWREG32(rk_pcie->apb_base + offset); +} + +rt_inline void rockchip_pcie_writel_apb(struct rockchip_pcie *rk_pcie, + rt_uint32_t val, int offset) +{ + HWREG32(rk_pcie->apb_base + offset) = val; +} + +static void rockchip_pcie_enable_ltssm(struct rockchip_pcie *rk_pcie) +{ + rockchip_pcie_writel_apb(rk_pcie, PCIE_CLIENT_ENABLE_LTSSM, + PCIE_CLIENT_GENERAL_CONTROL); +} + +static rt_bool_t rockchip_pcie_link_up(struct dw_pcie *pci) +{ + rt_uint32_t val; + struct rockchip_pcie *rk_pcie = to_rockchip_pcie(pci); + + val = rockchip_pcie_readl_apb(rk_pcie, PCIE_CLIENT_LTSSM_STATUS); + + if ((val & PCIE_LINKUP) == PCIE_LINKUP && + (val & PCIE_LTSSM_STATUS_MASK) == PCIE_L0S_ENTRY) + { + return RT_TRUE; + } + + return RT_FALSE; +} + +static rt_err_t rockchip_pcie_start_link(struct dw_pcie *pci) +{ + struct rockchip_pcie *rk_pcie = to_rockchip_pcie(pci); + + /* Reset device */ + if (rk_pcie->rst_pin >= 0) + { + rt_pin_write(rk_pcie->rst_pin, PIN_LOW); + } + + rockchip_pcie_enable_ltssm(rk_pcie); + + /* + * PCIe requires the refclk to be stable for 100µs prior to releasing + * PERST. See table 2-4 in section 2.6.2 AC Specifications of the PCI + * Express Card Electromechanical Specification, 1.1. However, we don't + * know if the refclk is coming from RC's PHY or external OSC. If it's + * from RC, so enabling LTSSM is the just right place to release #PERST. + * We need more extra time as before, rather than setting just + * 100us as we don't know how long should the device need to reset. + */ + rt_thread_mdelay(100); + + if (rk_pcie->rst_pin >= 0) + { + rt_pin_write(rk_pcie->rst_pin, PIN_HIGH); + } + + return RT_EOK; +} + +static const struct dw_pcie_ops dw_pcie_ops = +{ + .link_up = rockchip_pcie_link_up, + .start_link = rockchip_pcie_start_link, +}; + +static void rockchip_pcie_intx_mask(struct rt_pic_irq *pirq) +{ + struct rockchip_pcie *rk_pcie = pirq->pic->priv_data; + + rockchip_pcie_writel_apb(rk_pcie, HIWORD_UPDATE_BIT(RT_BIT(pirq->hwirq)), + PCIE_CLIENT_INTR_MASK_LEGACY); +} + +static void rockchip_pcie_intx_unmask(struct rt_pic_irq *pirq) +{ + struct rockchip_pcie *rk_pcie = pirq->pic->priv_data; + + rockchip_pcie_writel_apb(rk_pcie, HIWORD_DISABLE_BIT(RT_BIT(pirq->hwirq)), + PCIE_CLIENT_INTR_MASK_LEGACY); +} + +static int rockchip_intx_irq_map(struct rt_pic *pic, int hwirq, rt_uint32_t mode) +{ + int irq; + struct rt_pic_irq *pirq = rt_pic_find_irq(pic, hwirq); + + if (pirq) + { + if (pirq->irq >= 0) + { + irq = pirq->irq; + } + else + { + struct rockchip_pcie *rk_pcie = pic->priv_data; + + irq = rt_pic_config_irq(pic, hwirq, hwirq); + rt_pic_cascade(pirq, rk_pcie->intx_irq); + rt_pic_irq_set_triger_mode(irq, mode); + } + } + else + { + irq = -1; + } + + return irq; +} + +static rt_err_t rockchip_intx_irq_parse(struct rt_pic *pic, + struct rt_ofw_cell_args *args, struct rt_pic_irq *out_pirq) +{ + rt_err_t err = RT_EOK; + + if (args->args_count == 1) + { + out_pirq->hwirq = args->args[1]; + } + else + { + err = -RT_EINVAL; + } + + return err; +} + +const static struct rt_pic_ops rockchip_intx_ops = +{ + .name = "RK-INTx", + .irq_mask = rockchip_pcie_intx_mask, + .irq_unmask = rockchip_pcie_intx_unmask, + .irq_map = rockchip_intx_irq_map, + .irq_parse = rockchip_intx_irq_parse, +}; + +static void rk_pcie_intx_isr(int irqno, void *param) +{ + rt_uint32_t ints; + struct rt_pic_irq *pirq; + struct rockchip_pcie *rk_pcie = param; + + ints = rockchip_pcie_readl_apb(rk_pcie, PCIE_CLIENT_INTR_STATUS_LEGACY); + + for (int pin = 0; ints && pin < RT_PCI_INTX_PIN_MAX; ++pin, ints >>= 1) + { + if ((ints & 1)) + { + pirq = rt_pic_find_irq(&rk_pcie->intx_pic, pin); + + rt_pic_handle_isr(pirq); + } + } +} + +static rt_err_t rockchip_pcie_init_irq(struct rockchip_pcie *rk_pcie) +{ + struct rt_ofw_node *np = rk_pcie->pci.dev->ofw_node, *intx_np; + struct rt_pic *intx_pic = &rk_pcie->intx_pic; + + intx_np = rt_ofw_get_child_by_tag(np, "legacy-interrupt-controller"); + + if (!intx_np) + { + LOG_E("INTx ofw node not found"); + + return -RT_EIO; + } + + rk_pcie->intx_irq = rt_ofw_get_irq(intx_np, 0); + + rt_ofw_node_put(intx_np); + + if (rk_pcie->intx_irq < 0) + { + rk_pcie->intx_irq = rt_ofw_get_irq_by_name(np, "legacy"); + } + + if (rk_pcie->intx_irq < 0) + { + LOG_E("INTx irq get fail"); + + return rk_pcie->intx_irq; + } + + intx_pic->priv_data = rk_pcie; + intx_pic->ops = &rockchip_intx_ops; + + rt_pic_linear_irq(intx_pic, RT_PCI_INTX_PIN_MAX); + + rt_pic_user_extends(intx_pic); + + rt_ofw_data(np) = intx_pic; + + rt_hw_interrupt_install(rk_pcie->intx_irq, rk_pcie_intx_isr, rk_pcie, "rk-pcie-INTx"); + rt_hw_interrupt_umask(rk_pcie->intx_irq); + + return RT_EOK; +} + +static rt_err_t rockchip_pcie_host_init(struct dw_pcie_rp *pp) +{ + rt_err_t err; + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct rockchip_pcie *rk_pcie = to_rockchip_pcie(pci); + rt_uint32_t val = HIWORD_UPDATE_BIT(PCIE_LTSSM_ENABLE_ENHANCE); + + if ((err = rockchip_pcie_init_irq(rk_pcie))) + { + return err; + } + + /* LTSSM enable control mode */ + rockchip_pcie_writel_apb(rk_pcie, val, PCIE_CLIENT_HOT_RESET_CTRL); + rockchip_pcie_writel_apb(rk_pcie, PCIE_CLIENT_RC_MODE, PCIE_CLIENT_GENERAL_CONTROL); + + return RT_EOK; +} + +static const struct dw_pcie_host_ops rockchip_pcie_host_ops = +{ + .host_init = rockchip_pcie_host_init, +}; + +static rt_err_t rockchip_pcie_phy_init(struct rockchip_pcie *rk_pcie) +{ + rt_phy_status phy_status; + struct rt_device *dev = rk_pcie->pci.dev; + + rk_pcie->phy = rt_phy_get_by_name(dev, "pcie-phy"); + if (!rk_pcie->phy) + { + LOG_E("Missing PHY"); + return -RT_ERROR; + } + + phy_status = rt_phy_init(rk_pcie->phy, RT_NULL, 0, 0); + if (phy_status) + { + rk_pcie->phy = RT_NULL; + LOG_E("%s PHY fail", "Init"); + + return -RT_EIO; + } + + phy_status = rt_phy_power_on(rk_pcie->phy); + if (phy_status) + { + rt_phy_exit(rk_pcie->phy, RT_NULL, 0); + rk_pcie->phy = RT_NULL; + + LOG_E("%s PHY fail", "Power on"); + + return -RT_EIO; + } + + return RT_EOK; +} + +static rt_err_t rockchip_pcie_resource_init(struct rockchip_pcie *rk_pcie) +{ + struct rt_device *dev = rk_pcie->pci.dev; + + rk_pcie->apb_base = rt_dm_dev_iomap_by_name(dev, "apb"); + if (!rk_pcie->apb_base) + { + rk_pcie->apb_base = rt_dm_dev_iomap_by_name(dev, "pcie-apb"); + } + + if (!rk_pcie->apb_base) + { + return -RT_EIO; + } + + rk_pcie->rst_pin = rt_pin_get_named_pin(dev, "reset", 0, RT_NULL, RT_NULL); + if (rk_pcie->rst_pin < 0 && rk_pcie->rst_pin != -RT_EEMPTY) + { + return (rt_err_t)rk_pcie->rst_pin; + } + + if (rk_pcie->rst_pin >= 0) + { + rt_pin_mode(rk_pcie->rst_pin, PIN_MODE_OUTPUT); + rt_pin_write(rk_pcie->rst_pin, PIN_HIGH); + } + + rk_pcie->rstc = rt_reset_control_get_array(dev); + + if (rt_is_err(rk_pcie->rstc)) + { + return rt_ptr_err(rk_pcie->rstc); + } + + return RT_EOK; +} + +static void rockchip_pcie_phy_deinit(struct rockchip_pcie *rk_pcie) +{ + rt_phy_exit(rk_pcie->phy, RT_NULL, 0); + rt_phy_power_off(rk_pcie->phy); +} + +static rt_err_t rockchip_pcie_clk_init(struct rockchip_pcie *rk_pcie) +{ + struct rt_device *dev = rk_pcie->pci.dev; + + rk_pcie->clk_arr = rt_clk_get_array(dev); + + if (rt_is_err(rk_pcie->clk_arr)) + { + return rt_ptr_err(rk_pcie->clk_arr); + } + + return rt_clk_array_prepare_enable(rk_pcie->clk_arr); +} + +static void rockchip_pcie_free(struct rockchip_pcie *rk_pcie) +{ + if (rk_pcie->apb_base) + { + rt_iounmap(rk_pcie->apb_base); + } + + if (rk_pcie->rstc) + { + rt_reset_control_put(rk_pcie->rstc); + } + + if (rk_pcie->vpcie3v3) + { + rt_regulator_disable(rk_pcie->vpcie3v3); + } + + if (rk_pcie->phy) + { + rockchip_pcie_phy_deinit(rk_pcie); + } + + if (rk_pcie->clk_arr) + { + rt_clk_array_disable_unprepare(rk_pcie->clk_arr); + rt_clk_array_put(rk_pcie->clk_arr); + } + + rt_free(rk_pcie); +} + +static rt_err_t rockchip_pcie_probe(struct rt_platform_device *pdev) +{ + rt_err_t err; + struct rt_device *dev = &pdev->parent; + struct rockchip_pcie *rk_pcie = rt_calloc(1, sizeof(*rk_pcie)); + + if (!rk_pcie) + { + return -RT_EINVAL; + } + + rk_pcie->pci.dev = dev; + rk_pcie->pci.ops = &dw_pcie_ops; + rk_pcie->pci.pp.ops = &rockchip_pcie_host_ops; + rk_pcie->intx_irq = -RT_EEMPTY; + + if ((err = rockchip_pcie_resource_init(rk_pcie))) + { + goto _free_res; + } + + if ((err = rt_reset_control_assert(rk_pcie->rstc))) + { + goto _free_res; + } + + rk_pcie->vpcie3v3 = rt_regulator_get_optional(dev, "vpcie3v3"); + + if (!rt_is_err(rk_pcie->vpcie3v3)) + { + if ((err = rt_regulator_enable(rk_pcie->vpcie3v3))) + { + goto _free_res; + } + } + + if ((err = rockchip_pcie_phy_init(rk_pcie))) + { + goto _free_res; + } + + if ((err = rt_reset_control_deassert(rk_pcie->rstc))) + { + goto _free_res; + } + + if ((err = rockchip_pcie_clk_init(rk_pcie))) + { + goto _free_res; + } + + if ((err = dw_pcie_host_init(&rk_pcie->pci.pp))) + { + goto _free_res; + } + + dev->user_data = rk_pcie; + + return RT_EOK; + +_free_res: + rockchip_pcie_free(rk_pcie); + + return err; +} + +static rt_err_t rockchip_pcie_remove(struct rt_platform_device *pdev) +{ + struct rockchip_pcie *rk_pcie = pdev->parent.user_data; + + if (rk_pcie->intx_irq >= 0) + { + rt_hw_interrupt_mask(rk_pcie->intx_irq); + rt_pic_detach_irq(rk_pcie->intx_irq, rk_pcie); + } + + rockchip_pcie_free(rk_pcie); + + return RT_EOK; +} + +static const struct rt_ofw_node_id rockchip_pcie_ofw_ids[] = +{ + { .compatible = "rockchip,rk3568-pcie" }, + { /* sentinel */ } +}; + +static struct rt_platform_driver rockchip_pcie_driver = +{ + .name = "rockchip-dw-pcie", + .ids = rockchip_pcie_ofw_ids, + + .probe = rockchip_pcie_probe, + .remove = rockchip_pcie_remove, +}; +RT_PLATFORM_DRIVER_EXPORT(rockchip_pcie_driver); diff --git a/components/drivers/pci/host/dw/pcie-dw.c b/components/drivers/pci/host/dw/pcie-dw.c new file mode 100644 index 000000000000..80a3c7dd9d80 --- /dev/null +++ b/components/drivers/pci/host/dw/pcie-dw.c @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-09-23 GuEe-GUI first version + */ + +#include "pcie-dw.h" + +rt_err_t dw_pcie_host_init(struct dw_pcie_rp *pp) +{ + return RT_EOK; +} diff --git a/components/drivers/pci/host/dw/pcie-dw.h b/components/drivers/pci/host/dw/pcie-dw.h new file mode 100644 index 000000000000..26f572f52451 --- /dev/null +++ b/components/drivers/pci/host/dw/pcie-dw.h @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-09-23 GuEe-GUI first version + */ + +#ifndef __PCIE_DESIGNWARE_H__ +#define __PCIE_DESIGNWARE_H__ + +#include +#include + +struct dw_pcie_ops; +struct dw_pcie_host_ops; + +struct dw_pcie_rp +{ + rt_bool_t has_msi_ctrl:1; + rt_bool_t cfg0_io_shared:1; + + rt_uint64_t cfg0_base; + void *va_cfg0_base; + rt_uint32_t cfg0_size; + + rt_size_t io_base; + rt_ubase_t io_bus_addr; + rt_uint32_t io_size; + + int irq; + const struct dw_pcie_host_ops *ops; +}; + +struct dw_pcie_host_ops +{ + rt_err_t (*host_init)(struct dw_pcie_rp *pp); + void (*host_deinit)(struct dw_pcie_rp *pp); + rt_err_t (*msi_host_init)(struct dw_pcie_rp *pp); +}; + +struct dw_pcie_ep +{ + const struct dw_pcie_ep_ops *ops; + + rt_ubase_t phys_base; + rt_size_t addr_size; + rt_size_t page_size; + rt_uint8_t bar_to_atu[PCI_STD_NUM_BARS]; + rt_ubase_t *outbound_addr; + rt_ubase_t *ib_window_map; + rt_ubase_t *ob_window_map; + void *msi_mem; + rt_ubase_t msi_mem_phys; +}; + +struct dw_pcie +{ + struct rt_device *dev; + + void *dbi_base; + void *dbi_base2; + void *atu_base; + + rt_size_t atu_size; + rt_uint32_t num_ib_windows; + rt_uint32_t num_ob_windows; + rt_uint32_t region_align; + rt_uint64_t region_limit; + + struct dw_pcie_rp pp; + struct dw_pcie_ep ep; + const struct dw_pcie_ops *ops; + + void *priv; +}; + +struct dw_pcie_ops +{ + rt_uint64_t (*cpu_addr_fixup)(struct dw_pcie *pcie, rt_uint64_t cpu_addr); + rt_uint32_t (*read_dbi)(struct dw_pcie *pcie, void *base, rt_uint32_t reg, rt_size_t size); + void (*write_dbi)(struct dw_pcie *pcie, void *base, rt_uint32_t reg, rt_size_t size, rt_uint32_t val); + void (*write_dbi2)(struct dw_pcie *pcie, void *base, rt_uint32_t reg, rt_size_t size, rt_uint32_t val); + rt_bool_t (*link_up)(struct dw_pcie *pcie); + rt_err_t (*start_link)(struct dw_pcie *pcie); + void (*stop_link)(struct dw_pcie *pcie); +}; + +#define to_dw_pcie_from_pp(port) rt_container_of((port), struct dw_pcie, pp) +#define to_dw_pcie_from_ep(endpoint) rt_container_of((endpoint), struct dw_pcie, ep) + +rt_err_t dw_pcie_host_init(struct dw_pcie_rp *pp); + +#endif /* __PCIE_DESIGNWARE_H__ */ diff --git a/components/drivers/pci/host/dw/pcie-dw_ep.c b/components/drivers/pci/host/dw/pcie-dw_ep.c new file mode 100644 index 000000000000..ba89ddaf28e6 --- /dev/null +++ b/components/drivers/pci/host/dw/pcie-dw_ep.c @@ -0,0 +1,9 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-09-23 GuEe-GUI first version + */ \ No newline at end of file diff --git a/components/drivers/pci/host/dw/pcie-dw_host.c b/components/drivers/pci/host/dw/pcie-dw_host.c new file mode 100644 index 000000000000..ba89ddaf28e6 --- /dev/null +++ b/components/drivers/pci/host/dw/pcie-dw_host.c @@ -0,0 +1,9 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-09-23 GuEe-GUI first version + */ \ No newline at end of file diff --git a/components/drivers/pci/host/dw/pcie-dw_platfrom.c b/components/drivers/pci/host/dw/pcie-dw_platfrom.c new file mode 100644 index 000000000000..ba89ddaf28e6 --- /dev/null +++ b/components/drivers/pci/host/dw/pcie-dw_platfrom.c @@ -0,0 +1,9 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-09-23 GuEe-GUI first version + */ \ No newline at end of file diff --git a/components/drivers/pci/host/pci-host-common.c b/components/drivers/pci/host/pci-host-common.c new file mode 100644 index 000000000000..8655aeaa7a3c --- /dev/null +++ b/components/drivers/pci/host/pci-host-common.c @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-10-24 GuEe-GUI first version + */ + +#include + +#include "../ecam.h" + +rt_err_t pci_host_common_probe(struct rt_platform_device *pdev) +{ + void *base; + rt_err_t err; + struct rt_device *dev = &pdev->parent; + struct pci_ecam_config_window *conf_win; + struct rt_pci_host_bridge *host_bridge = rt_pci_host_bridge_alloc(0); + + if (!host_bridge) + { + return -RT_ENOMEM; + } + + if (!(base = rt_dm_dev_iomap(dev, 0))) + { + err = -RT_EIO; + goto _fail; + } + + host_bridge->parent.ofw_node = dev->ofw_node; + host_bridge->sysdata = conf_win = pci_ecam_create(host_bridge, + (const struct pci_ecam_ops *)pdev->id->data); + + if (!conf_win) + { + err = -RT_ENOMEM; + goto _fail; + } + + conf_win->win = base; + conf_win->priv = host_bridge; + + if ((err = rt_pci_host_bridge_init(host_bridge))) + { + goto _fail; + } + + if ((err = rt_pci_host_bridge_probe(host_bridge))) + { + goto _fail; + } + + dev->user_data = host_bridge; + + return RT_EOK; + +_fail: + if (base) + { + rt_iounmap(base); + } + rt_free(host_bridge); + + return err; +} + +rt_err_t pci_host_common_remove(struct rt_platform_device *pdev) +{ + struct pci_ecam_config_window *conf_win; + struct rt_pci_host_bridge *host_bridge = pdev->parent.user_data; + + conf_win = host_bridge->sysdata; + + rt_iounmap(conf_win->win); + rt_free(host_bridge); + + return RT_EOK; +} diff --git a/components/drivers/pci/host/pci-host-generic.c b/components/drivers/pci/host/pci-host-generic.c new file mode 100644 index 000000000000..7496d5249b79 --- /dev/null +++ b/components/drivers/pci/host/pci-host-generic.c @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-10-24 GuEe-GUI first version + */ + +#include + +#include "../ecam.h" + +static const struct pci_ecam_ops gen_pci_cfg_cam_bus_ops = +{ + .bus_shift = 16, + .pci_ops = + { + .map = pci_ecam_map, + .read = rt_pci_bus_read_config_uxx, + .write = rt_pci_bus_write_config_uxx, + } +}; + +static void *pci_dw_ecam_map_bus(struct rt_pci_bus *bus, rt_uint32_t devfn, int where) +{ + struct pci_ecam_config_window *conf_win = bus->sysdata; + + if (bus->number == conf_win->bus_range[0] && RT_PCI_SLOT(devfn) > 0) + { + return RT_NULL; + } + + return pci_ecam_map(bus, devfn, where); +} + +static const struct pci_ecam_ops pci_dw_ecam_bus_ops = +{ + .pci_ops = + { + .map = pci_dw_ecam_map_bus, + .read = rt_pci_bus_read_config_uxx, + .write = rt_pci_bus_write_config_uxx, + } +}; + +static const struct rt_ofw_node_id gen_pci_ofw_ids[] = +{ + { .compatible = "pci-host-cam-generic", .data = &gen_pci_cfg_cam_bus_ops }, + { .compatible = "pci-host-ecam-generic", .data = &pci_generic_ecam_ops }, + { .compatible = "marvell,armada8k-pcie-ecam", .data = &pci_dw_ecam_bus_ops }, + { .compatible = "socionext,synquacer-pcie-ecam", .data = &pci_dw_ecam_bus_ops }, + { .compatible = "snps,dw-pcie-ecam", .data = &pci_dw_ecam_bus_ops }, + { /* sentinel */ } +}; + +static struct rt_platform_driver gen_pci_driver = +{ + .name = "pci-host-generic", + .ids = gen_pci_ofw_ids, + + .probe = pci_host_common_probe, + .remove = pci_host_common_remove, +}; +RT_PLATFORM_DRIVER_EXPORT(gen_pci_driver); diff --git a/components/drivers/pci/irq.c b/components/drivers/pci/irq.c new file mode 100644 index 000000000000..c7bd1a849afb --- /dev/null +++ b/components/drivers/pci/irq.c @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-11-07 GuEe-GUI first version + */ + +#include + +#define DBG_TAG "pci.irq" +#define DBG_LVL DBG_INFO +#include + +#include + +void rt_pci_assign_irq(struct rt_pci_device *pdev) +{ + int irq = 0; + rt_uint8_t pin, slot = -1; + struct rt_pci_host_bridge *host_bridge = rt_pci_find_host_bridge(pdev->bus); + + if (!host_bridge->irq_map) + { + LOG_D("PCI-Device<%s> runtime IRQ mapping not provided by platform", + rt_dm_dev_get_name(&pdev->parent)); + + return; + } + + /* Must try the swizzle when interrupt line passes through a P2P bridge */ + rt_pci_read_config_u8(pdev, PCIR_INTPIN, &pin); + + if (pin > RT_PCI_INTX_PIN_MAX) + { + pin = 1; + } + + if (pin) + { + if (host_bridge->irq_slot) + { + slot = host_bridge->irq_slot(pdev, &pin); + } + + /* Map IRQ */ + if ((irq = host_bridge->irq_map(pdev, slot, pin)) == -1) + { + irq = 0; + } + } + pdev->irq = irq; + + LOG_D("PCI-Device<%s> assign IRQ: got %d", rt_dm_dev_get_name(&pdev->parent), pdev->irq); + + /* Save IRQ */ + rt_pci_write_config_u8(pdev, PCIR_INTLINE, irq); +} diff --git a/components/drivers/pci/msi/SConscript b/components/drivers/pci/msi/SConscript new file mode 100644 index 000000000000..3fc141d68dff --- /dev/null +++ b/components/drivers/pci/msi/SConscript @@ -0,0 +1,15 @@ +from building import * + +group = [] + +if not GetDepend(['RT_PCI_MSI']): + Return('group') + +cwd = GetCurrentDir() +CPPPATH = [cwd + '/../../include'] + +src = ['msi.c', 'msi_dev.c'] + +group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') diff --git a/components/drivers/pci/msi/msi.c b/components/drivers/pci/msi/msi.c new file mode 100644 index 000000000000..274202cb3a42 --- /dev/null +++ b/components/drivers/pci/msi/msi.c @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-11-07 GuEe-GUI first version + */ + +#include + +#define DBG_TAG "pci.msi" +#define DBG_LVL DBG_INFO +#include + +void rt_pci_msi_mask_irq(struct rt_pic_irq *pirq) +{ +} + +void rt_pci_msi_unmask_irq(struct rt_pic_irq *pirq) +{ +} diff --git a/components/drivers/pci/msi/msi_dev.c b/components/drivers/pci/msi/msi_dev.c new file mode 100644 index 000000000000..c03237a9a20d --- /dev/null +++ b/components/drivers/pci/msi/msi_dev.c @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-10-24 GuEe-GUI first version + */ + +#include + +void rt_pci_msi_init(struct rt_pci_device *pdev) +{ + if (pdev && (pdev->msi_cap = rt_pci_find_capability(pdev, PCIY_MSI))) + { + rt_uint16_t ctrl; + + rt_pci_read_config_u16(pdev, pdev->msi_cap + PCIR_MSI_CTRL, &ctrl); + + if (ctrl & PCIM_MSICTRL_MSI_ENABLE) + { + rt_pci_write_config_u16(pdev, pdev->msi_cap + PCIR_MSI_CTRL, ctrl & ~PCIM_MSICTRL_MSI_ENABLE); + } + + if (!(ctrl & PCIM_MSICTRL_64BIT)) + { + rt_pci_device_set_flag(pdev, PCI_F_NO_64BIT_MSI); + } + } +} + +void rt_pci_msix_init(struct rt_pci_device *pdev) +{ + if (pdev && (pdev->msix_cap = rt_pci_find_capability(pdev, PCIY_MSIX))) + { + rt_uint16_t ctrl; + + rt_pci_read_config_u16(pdev, pdev->msix_cap + PCIR_MSIX_CTRL, &ctrl); + + if (ctrl & PCIM_MSIXCTRL_MSIX_ENABLE) + { + rt_pci_write_config_u16(pdev, pdev->msix_cap + PCIR_MSIX_CTRL, ctrl & ~PCIM_MSIXCTRL_MSIX_ENABLE); + } + } +} diff --git a/components/drivers/pci/ofw.c b/components/drivers/pci/ofw.c new file mode 100644 index 000000000000..b6ff8e676bb9 --- /dev/null +++ b/components/drivers/pci/ofw.c @@ -0,0 +1,310 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-10-24 GuEe-GUI first version + */ + +#include +#include + +#define DBG_TAG "pci.ofw" +#define DBG_LVL DBG_INFO +#include + +#include +#include +#include +#include +#include + +static rt_err_t pci_ofw_irq_parse(struct rt_pci_device *pdev, struct rt_ofw_cell_args *out_irq) +{ + rt_err_t err = RT_EOK; + rt_uint8_t pin; + fdt32_t map_addr[4]; + struct rt_pci_device *p2pdev; + struct rt_ofw_node *dev_np, *p2pnode = RT_NULL; + + /* Parse device tree if dev have a device node */ + dev_np = pdev->parent.ofw_node; + + if (dev_np) + { + err = rt_ofw_parse_irq_cells(dev_np, 0, out_irq); + + if (err) + { + return err; + } + } + + /* Assume #interrupt-cells is 1 */ + if ((err = rt_pci_read_config_u8(pdev, PCIR_INTPIN, &pin))) + { + goto _err; + } + + /* No pin, exit with no error message. */ + if (pin == 0) + { + return -RT_ENOSYS; + } + + /* Try local interrupt-map in the device node */ + if (rt_ofw_prop_read_raw(dev_np, "interrupt-map", RT_NULL)) + { + pin = rt_pci_irq_intx(pdev, pin); + p2pnode = dev_np; + } + + /* Walk up the PCI tree */ + while (!p2pnode) + { + p2pdev = pdev->bus->self; + + /* Is the root bus -> host bridge */ + if (rt_pci_is_root_bus(pdev->bus)) + { + struct rt_pci_host_bridge *host_bridge = pdev->bus->host_bridge; + + p2pnode = host_bridge->parent.ofw_node; + + if (!p2pnode) + { + err = -RT_EINVAL; + + goto _err; + } + } + else + { + /* Is P2P bridge */ + p2pnode = p2pdev->parent.ofw_node; + } + + if (p2pnode) + { + break; + } + + /* Try get INTx in P2P */ + pin = rt_pci_irq_intx(pdev, pin); + pdev = p2pdev; + } + + /* For more format detail, please read `components/drivers/ofw/irq.c:ofw_parse_irq_map` */ + out_irq->data = map_addr; + out_irq->args_count = 2; + out_irq->args[0] = 3; + out_irq->args[1] = 1; + + /* In addr cells */ + map_addr[0] = cpu_to_fdt32((pdev->bus->number << 16) | (pdev->devfn << 8)); + map_addr[1] = cpu_to_fdt32(0); + map_addr[2] = cpu_to_fdt32(0); + /* In pin cells */ + map_addr[3] = cpu_to_fdt32(pin); + + err = rt_ofw_parse_irq_map(p2pnode, out_irq); + +_err: + if (err == -RT_EEMPTY) + { + LOG_W("PCI-Device<%s> no interrupt-map found, INTx interrupts not available", + rt_dm_dev_get_name(&pdev->parent)); + LOG_W("PCI-Device<%s> possibly some PCI slots don't have level triggered interrupts capability", + rt_dm_dev_get_name(&pdev->parent)); + } + else if (err && err != -RT_ENOSYS) + { + LOG_E("PCI-Device<%s> irq parse failed with err = %s", + rt_dm_dev_get_name(&pdev->parent), rt_strerror(err)); + } + + return err; +} + +int rt_pci_ofw_irq_parse_and_map(struct rt_pci_device *pdev, rt_uint8_t slot, rt_uint8_t pin) +{ + int irq = -1; + rt_err_t status; + struct rt_ofw_cell_args irq_args; + + if (!pdev) + { + goto _end; + } + + status = pci_ofw_irq_parse(pdev, &irq_args); + + if (status) + { + goto _end; + } + + irq = rt_ofw_map_irq(&irq_args); + +_end: + return irq; +} + +rt_err_t rt_pci_ofw_parse_ranges(struct rt_ofw_node *dev_np, struct rt_pci_host_bridge *host_bridge) +{ + const fdt32_t *cell; + rt_ssize_t total_cells; + int groups, space_code; + rt_uint32_t phy_addr[3]; + rt_uint64_t cpu_addr, phy_addr_size; + int phy_addr_cells = -1, phy_size_cells = -1, cpu_addr_cells; + + if (!dev_np || !host_bridge) + { + return -RT_EINVAL; + } + + cpu_addr_cells = rt_ofw_io_addr_cells(dev_np); + rt_ofw_prop_read_s32(dev_np, "#address-cells", &phy_addr_cells); + rt_ofw_prop_read_s32(dev_np, "#size-cells", &phy_size_cells); + + if (phy_addr_cells != 3 || phy_size_cells < 1 || cpu_addr_cells < 1) + { + return -RT_EINVAL; + } + + cell = rt_ofw_prop_read_raw(dev_np, "ranges", &total_cells); + + if (!cell) + { + return -RT_EINVAL; + } + + groups = total_cells / sizeof(*cell) / (phy_addr_cells + phy_size_cells + cpu_addr_cells); + host_bridge->bus_regions = rt_malloc(groups * sizeof(struct rt_pci_bus_region)); + + if (!host_bridge->bus_regions) + { + return -RT_ENOMEM; + } + + host_bridge->bus_regions_nr = 0; + + for (int i = 0; i < groups; ++i) + { + /* + * ranges: + * phys.hi cell: npt000ss bbbbbbbb dddddfff rrrrrrrr + * phys.low cell: llllllll llllllll llllllll llllllll + * phys.mid cell: hhhhhhhh hhhhhhhh hhhhhhhh hhhhhhhh + * + * n: relocatable region flag (doesn't play a role here) + * p: prefetchable (cacheable) region flag + * t: aliased address flag (doesn't play a role here) + * ss: space code + * 00: configuration space + * 01: I/O space + * 10: 32 bit memory space + * 11: 64 bit memory space + * bbbbbbbb: The PCI bus number + * ddddd: The device number + * fff: The function number. Used for multifunction PCI devices. + * rrrrrrrr: Register number; used for configuration cycles. + */ + + for (int j = 0; j < phy_addr_cells; ++j) + { + phy_addr[j] = rt_fdt_read_number(cell++, 1); + } + + space_code = (phy_addr[0] >> 24) & 0x3; + + cpu_addr = rt_fdt_read_number(cell, cpu_addr_cells); + cell += cpu_addr_cells; + phy_addr_size = rt_fdt_read_number(cell, phy_size_cells); + cell += phy_size_cells; + + host_bridge->bus_regions[i].phy_addr = ((rt_uint64_t)phy_addr[1] << 32) | phy_addr[2]; + host_bridge->bus_regions[i].cpu_addr = cpu_addr; + host_bridge->bus_regions[i].size = phy_addr_size; + + host_bridge->bus_regions[i].bus_start = host_bridge->bus_regions[i].phy_addr; + + if (space_code & 2) + { + host_bridge->bus_regions[i].flags = phy_addr[0] & (1U << 30) ? + PCI_BUS_REGION_F_PREFETCH : PCI_BUS_REGION_F_MEM; + } + else if (space_code & 1) + { + host_bridge->bus_regions[i].flags = PCI_BUS_REGION_F_IO; + } + else + { + continue; + } + + ++host_bridge->bus_regions_nr; + } + + return rt_pci_region_setup(host_bridge); +} + +rt_err_t rt_pci_ofw_host_bridge_init(struct rt_ofw_node *dev_np, struct rt_pci_host_bridge *host_bridge) +{ + rt_err_t err; + const char *propname; + + if (!dev_np || !host_bridge) + { + return -RT_EINVAL; + } + + host_bridge->irq_slot = rt_pci_irq_slot; + host_bridge->irq_map = rt_pci_ofw_irq_parse_and_map; + + if (rt_ofw_prop_read_u32_array_index(dev_np, "bus-range", 0, 2, host_bridge->bus_range) < 0) + { + return -RT_EIO; + } + + propname = rt_ofw_get_prop_fuzzy_name(dev_np, ",pci-domain$"); + rt_ofw_prop_read_u32(dev_np, propname, &host_bridge->domain); + + err = rt_pci_ofw_parse_ranges(dev_np, host_bridge); + + return err; +} + +rt_err_t rt_pci_ofw_bus_init(struct rt_pci_bus *bus) +{ + rt_err_t err = RT_EOK; + + return err; +} + +rt_err_t rt_pci_ofw_device_init(struct rt_pci_device *pdev) +{ + struct rt_ofw_node *np = RT_NULL; + + if (!pdev) + { + return -RT_EINVAL; + } + + if (pdev->bus->parent) + { + np = pdev->bus->self->parent.ofw_node; + } + + if (!np) + { + return RT_EOK; + } + + pdev->parent.ofw_node = np; + + return RT_EOK; +} diff --git a/components/drivers/pci/pci.c b/components/drivers/pci/pci.c new file mode 100644 index 000000000000..8040f0ed82a1 --- /dev/null +++ b/components/drivers/pci/pci.c @@ -0,0 +1,761 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-10-24 GuEe-GUI first version + */ + +#include +#include + +#define DBG_TAG "rtdm.pci" +#define DBG_LVL DBG_INFO +#include + +#include +#include +#include + +rt_uint32_t rt_pci_domain(struct rt_pci_device *pdev) +{ + struct rt_pci_host_bridge *host_bridge; + + if (!pdev) + { + return RT_UINT32_MAX; + } + + if ((host_bridge = rt_pci_find_host_bridge(pdev->bus))) + { + return host_bridge->domain; + } + + return RT_UINT32_MAX; +} + +static rt_uint8_t pci_find_next_cap_ttl(struct rt_pci_bus *bus, rt_uint32_t devfn, rt_uint8_t pos, int cap, int *ttl) +{ + rt_uint8_t ret = 0, id; + rt_uint16_t ent; + + rt_pci_bus_read_config_u8(bus, devfn, pos, &pos); + + while ((*ttl)--) + { + if (pos < 0x40) + { + break; + } + + pos &= ~3; + rt_pci_bus_read_config_u16(bus, devfn, pos, &ent); + + id = ent & 0xff; + if (id == 0xff) + { + break; + } + if (id == cap) + { + ret = pos; + break; + } + pos = (ent >> 8); + } + + return ret; +} + +static rt_uint8_t pci_find_next_cap(struct rt_pci_bus *bus, rt_uint32_t devfn, rt_uint8_t pos, int cap) +{ + int ttl = RT_PCI_FIND_CAP_TTL; + + return pci_find_next_cap_ttl(bus, devfn, pos, cap, &ttl); +} + +static rt_uint8_t pci_bus_find_cap_start(struct rt_pci_bus *bus, rt_uint32_t devfn, rt_uint8_t hdr_type) +{ + rt_uint8_t res = 0; + rt_uint16_t status; + + rt_pci_bus_read_config_u16(bus, devfn, PCIR_STATUS, &status); + + if (status & PCIM_STATUS_CAPPRESENT) + { + switch (hdr_type) + { + case PCIM_HDRTYPE_NORMAL: + case PCIM_HDRTYPE_BRIDGE: + res = PCIR_CAP_PTR; + break; + + case PCIM_HDRTYPE_CARDBUS: + res = PCIR_CAP_PTR_2; + break; + } + } + + return res; +} + +rt_uint8_t rt_pci_bus_find_capability(struct rt_pci_bus *bus, rt_uint32_t devfn, int cap) +{ + rt_uint8_t hdr_type, ret = RT_UINT8_MAX; + + if (bus) + { + rt_pci_bus_read_config_u8(bus, devfn, PCIR_HDRTYPE, &hdr_type); + + ret = pci_bus_find_cap_start(bus, devfn, hdr_type & PCIM_HDRTYPE); + + if (ret) + { + ret = pci_find_next_cap(bus, devfn, ret, cap); + } + } + + return ret; +} + +rt_uint8_t rt_pci_find_capability(struct rt_pci_device *pdev, int cap) +{ + rt_uint8_t res = RT_UINT8_MAX; + + if (pdev) + { + res = pci_bus_find_cap_start(pdev->bus, pdev->devfn, pdev->hdr_type); + + if (res) + { + res = pci_find_next_cap(pdev->bus, pdev->devfn, res, cap); + } + } + + return res; +} + +rt_uint8_t rt_pci_find_next_capability(struct rt_pci_device *pdev, rt_uint8_t pos, int cap) +{ + rt_uint8_t res = RT_UINT8_MAX; + + if (pdev) + { + res = pci_find_next_cap(pdev->bus, pdev->devfn, pos + PCICAP_NEXTPTR, cap); + } + + return res; +} + +void rt_pci_intx(struct rt_pci_device *pdev, rt_bool_t enable) +{ + rt_uint16_t pci_command, new; + + if (!pdev) + { + return; + } + + rt_pci_read_config_u16(pdev, PCIR_COMMAND, &pci_command); + + if (enable) + { + new = pci_command & ~PCIM_CMD_INTxDIS; + } + else + { + new = pci_command | PCIM_CMD_INTxDIS; + } + + if (new != pci_command) + { + rt_pci_write_config_u16(pdev, PCIR_COMMAND, new); + } +} + +static rt_bool_t pci_check_and_set_intx_mask(struct rt_pci_device *pdev, rt_bool_t mask) +{ + rt_ubase_t level; + rt_bool_t irq_pending; + rt_bool_t res = RT_TRUE; + rt_uint16_t origcmd, newcmd; + rt_uint32_t cmd_status_dword; + struct rt_pci_bus *bus = pdev->bus; + + level = rt_spin_lock_irqsave(&rt_pci_lock); + + bus->ops->read(bus, pdev->devfn, PCIR_COMMAND, 4, &cmd_status_dword); + + irq_pending = (cmd_status_dword >> 16) & PCIM_STATUS_INTxSTATE; + + /* + * Check interrupt status register to see whether our device + * triggered the interrupt (when masking) or the next IRQ is + * already pending (when unmasking). + */ + if (mask != irq_pending) + { + res = RT_FALSE; + } + else + { + origcmd = cmd_status_dword; + newcmd = origcmd & ~PCIM_CMD_INTxDIS; + + if (mask) + { + newcmd |= PCIM_CMD_INTxDIS; + } + if (newcmd != origcmd) + { + bus->ops->write(bus, pdev->devfn, PCIR_COMMAND, 2, newcmd); + } + } + + rt_spin_unlock_irqrestore(&rt_pci_lock, level); + + return res; +} + +rt_bool_t rt_pci_check_and_mask_intx(struct rt_pci_device *pdev) +{ + rt_bool_t res = RT_FALSE; + + if (pdev) + { + res = pci_check_and_set_intx_mask(pdev, RT_TRUE); + } + + return res; +} + +rt_bool_t rt_pci_check_and_unmask_intx(struct rt_pci_device *pdev) +{ + rt_bool_t res = RT_FALSE; + + if (pdev) + { + res = pci_check_and_set_intx_mask(pdev, RT_FALSE); + } + + return res; +} + +void rt_pci_irq_mask(struct rt_pci_device *pdev) +{ + if (pdev) + { + rt_pci_intx(pdev, RT_FALSE); + } +} + +void rt_pci_irq_unmask(struct rt_pci_device *pdev) +{ + if (pdev) + { + rt_hw_interrupt_umask(pdev->irq); + rt_pci_intx(pdev, RT_TRUE); + } +} + +struct rt_pci_bus *rt_pci_find_root_bus(struct rt_pci_bus *bus) +{ + if (!bus) + { + return RT_NULL; + } + + while (bus->parent) + { + bus = bus->parent; + } + + return bus; +} + +struct rt_pci_host_bridge *rt_pci_find_host_bridge(struct rt_pci_bus *bus) +{ + if (!bus) + { + return RT_NULL; + } + + if ((bus = rt_pci_find_root_bus(bus))) + { + return rt_container_of(bus->host_bridge, struct rt_pci_host_bridge, parent); + } + + return RT_NULL; +} + +rt_uint8_t rt_pci_irq_intx(struct rt_pci_device *pdev, rt_uint8_t pin) +{ + int slot = 0; + + if (!rt_pci_enabled(pdev, PCI_F_ARI)) + { + slot = RT_PCI_SLOT(pdev->devfn); + } + + return (((pin - 1) + slot) % 4) + 1; +} + +rt_uint8_t rt_pci_irq_slot(struct rt_pci_device *pdev, rt_uint8_t *pinp) +{ + rt_uint8_t pin = *pinp; + + while (!rt_pci_is_root_bus(pdev->bus)) + { + pin = rt_pci_irq_intx(pdev, pin); + pdev = pdev->bus->self; + } + + *pinp = pin; + + return RT_PCI_SLOT(pdev->devfn); +} + +rt_err_t rt_pci_region_setup(struct rt_pci_host_bridge *host_bridge) +{ + rt_err_t err = host_bridge->bus_regions_nr == 0 ? -RT_EEMPTY : RT_EOK; + + for (int i = 0; i < host_bridge->bus_regions_nr; ++i) + { + struct rt_pci_bus_region *region = &host_bridge->bus_regions[i]; + /* + * Avoid allocating PCI resources from address 0 -- this is illegal + * according to PCI 2.1 and moreover. Use a reasonable starting value of + * 0x1000 instead if the bus start address is below 0x1000. + */ + region->bus_start = rt_max_t(rt_size_t, 0x1000, region->phy_addr); + + LOG_I("Bus %s region(%d):", + region->flags == PCI_BUS_REGION_F_MEM ? "Memory" : + (region->flags & PCI_BUS_REGION_F_PREFETCH ? "Prefetchable Mem" : + (region->flags & PCI_BUS_REGION_F_IO ? "I/O" : "Unknown")), i); + LOG_I(" cpu: [%p, %p]", region->cpu_addr, (region->cpu_addr + region->size - 1)); + LOG_I(" physical: [%p, %p]", region->phy_addr, (region->phy_addr + region->size - 1)); + } + + return err; +} + +struct rt_pci_bus_region *rt_pci_region_alloc(struct rt_pci_host_bridge *host_bridge, + void **out_addr, rt_size_t size, rt_ubase_t flags, rt_bool_t mem64) +{ + struct rt_pci_bus_region *bus_region, *region = RT_NULL; + + bus_region = &host_bridge->bus_regions[0]; + + for (int i = 0; i < host_bridge->bus_regions_nr; ++i, ++bus_region) + { + if (bus_region->flags == flags && bus_region->size > 0) + { + void *addr; + + region = bus_region; + addr = (void *)(((region->bus_start - 1) | (size - 1)) + 1); + + if ((rt_uint64_t)addr - region->phy_addr + size <= region->size) + { + rt_bool_t addr64 = !!rt_upper_32_bits((rt_ubase_t)addr); + + if (mem64) + { + if (!addr64) + { + region = RT_NULL; + + /* Try again */ + continue; + } + } + else if (addr64) + { + region = RT_NULL; + + /* Try again */ + continue; + } + + region->bus_start = ((rt_uint64_t)addr + size); + *out_addr = addr; + } + + break; + } + } + + return region; +} + +rt_err_t rt_pci_device_alloc_resource(struct rt_pci_host_bridge *host_bridge, struct rt_pci_device *pdev) +{ + rt_err_t err = RT_EOK; + rt_size_t size; + rt_ubase_t addr; + rt_uint32_t cfg; + rt_size_t bars_nr; + rt_uint8_t hdr_type; + rt_bool_t prefetch = RT_FALSE; + rt_uint16_t class, command = 0; + + for (int i = 0; i < host_bridge->bus_regions_nr; ++i) + { + if (host_bridge->bus_regions[i].flags == PCI_BUS_REGION_F_PREFETCH) + { + prefetch = RT_TRUE; + break; + } + } + + rt_pci_read_config_u16(pdev, PCIR_COMMAND, &command); + command = (command & ~(PCIM_CMD_PORTEN | PCIM_CMD_MEMEN)) | PCIM_CMD_BUSMASTEREN; + rt_pci_read_config_u8(pdev, PCIR_HDRTYPE, &hdr_type); + + if (pdev->hdr_type != hdr_type) + { + LOG_W("%s may not initialized", rt_dm_dev_get_name(&pdev->parent)); + } + + switch (hdr_type) + { + case PCIM_HDRTYPE_NORMAL: + bars_nr = PCI_STD_NUM_BARS; + break; + + case PCIM_HDRTYPE_BRIDGE: + bars_nr = 2; + break; + + case PCIM_HDRTYPE_CARDBUS: + bars_nr = 0; + break; + + default: + bars_nr = 0; + break; + } + + for (int i = 0; i < bars_nr; ++i) + { + rt_ubase_t flags; + rt_ubase_t bar_base; + rt_bool_t mem64 = RT_FALSE; + struct rt_pci_bus_region *region; + + cfg = 0; + bar_base = PCIR_BAR(i); + + rt_pci_write_config_u32(pdev, bar_base, RT_UINT32_MAX); + rt_pci_read_config_u32(pdev, bar_base, &cfg); + + if (!cfg) + { + continue; + } + else if (cfg == RT_UINT32_MAX) + { + rt_pci_write_config_u32(pdev, bar_base, 0UL); + continue; + } + + if (cfg & PCIM_BAR_SPACE) + { + mem64 = RT_FALSE; + flags = PCI_BUS_REGION_F_IO; + + size = cfg & PCIM_BAR_IO_MASK; + size &= ~(size - 1); + } + else + { + /* memory */ + if ((cfg & PCIM_BAR_MEM_TYPE_MASK) == PCIM_BAR_MEM_TYPE_64) + { + /* 64bits */ + rt_uint32_t cfg64; + rt_uint64_t bar64; + + mem64 = RT_TRUE; + + rt_pci_write_config_u32(pdev, bar_base + sizeof(rt_uint32_t), RT_UINT32_MAX); + rt_pci_read_config_u32(pdev, bar_base + sizeof(rt_uint32_t), &cfg64); + + bar64 = ((rt_uint64_t)cfg64 << 32) | cfg; + + size = ~(bar64 & PCIM_BAR_MEM_MASK) + 1; + } + else + { + /* 32bits */ + mem64 = RT_FALSE; + size = (rt_uint32_t)(~(cfg & PCIM_BAR_MEM_MASK) + 1); + } + + if (prefetch && (cfg & PCIM_BAR_MEM_PREFETCH)) + { + flags = PCI_BUS_REGION_F_PREFETCH; + } + else + { + flags = PCI_BUS_REGION_F_MEM; + } + } + + region = rt_pci_region_alloc(host_bridge, (void **)&addr, size, flags, mem64); + + if (region) + { + rt_pci_write_config_u32(pdev, bar_base, addr); + + if (mem64) + { + bar_base += sizeof(rt_uint32_t); + #ifdef RT_PCI_SYS_64BIT + rt_pci_write_config_u32(pdev, bar_base, (rt_uint32_t)(addr >> 32)); + #else + /* + * If we are a 64-bit decoder then increment to the upper 32 bits + * of the bar and force it to locate in the lower 4GB of memory. + */ + rt_pci_write_config_u32(pdev, bar_base, 0UL); + #endif + } + + pdev->resource[i].size = size; + #ifdef ARCH_SUPPORT_PIO + pdev->resource[i].base = addr; + #else + if (flags != PCI_BUS_REGION_F_IO) + { + pdev->resource[i].base = addr; + } + else + { + rt_ubase_t offset = addr - region->phy_addr; + pdev->resource[i].base = region->cpu_addr + offset; + } + #endif + pdev->resource[i].flags = flags; + + if (mem64) + { + ++i; + pdev->resource[i].flags = PCI_BUS_REGION_F_NONE; + } + } + else + { + err = -RT_ERROR; + LOG_W("%s alloc bar(%d) address fail", rt_dm_dev_get_name(&pdev->parent), i); + } + + command |= (cfg & PCIM_BAR_SPACE) ? PCIM_CMD_PORTEN : PCIM_CMD_MEMEN; + } + + if (hdr_type == PCIM_HDRTYPE_NORMAL || hdr_type == PCIM_HDRTYPE_BRIDGE) + { + int rom_addr = (hdr_type == PCIM_HDRTYPE_NORMAL) ? PCIR_BIOS : PCIR_BIOS_1; + + rt_pci_write_config_u32(pdev, rom_addr, 0xfffffffe); + rt_pci_read_config_u32(pdev, rom_addr, &cfg); + + if (cfg) + { + size = -(cfg & ~1); + + if (rt_pci_region_alloc(host_bridge, (void **)&addr, size, PCI_BUS_REGION_F_MEM, RT_FALSE)) + { + rt_pci_write_config_u32(pdev, rom_addr, addr); + } + command |= PCIM_CMD_MEMEN; + } + } + + rt_pci_read_config_u16(pdev, PCIR_SUBCLASS, &class); + + if (class == PCIS_DISPLAY_VGA) + { + command |= PCIM_CMD_PORTEN; + } + + rt_pci_write_config_u16(pdev, PCIR_COMMAND, command); + rt_pci_write_config_u8(pdev, PCIR_CACHELNSZ, RT_PCI_CACHE_LINE_SIZE); + rt_pci_write_config_u8(pdev, PCIR_LATTIMER, 0x80); + + return err; +} + +void rt_pci_enum_device(struct rt_pci_bus *bus, rt_bool_t (callback(struct rt_pci_device *))) +{ + rt_bool_t is_end = RT_FALSE; + struct rt_pci_device *pdev; + struct rt_pci_bus *parent; + + /* Walk tree */ + while (bus && !is_end) + { + /* Goto bottom */ + for (;;) + { + if (rt_list_isempty(&bus->children_nodes)) + { + parent = bus->parent; + break; + } + bus = rt_list_entry(&bus->children_nodes, struct rt_pci_bus, list); + } + + rt_list_for_each_entry(pdev, &bus->devices_nodes, list) + { + if (callback(pdev)) + { + is_end = RT_TRUE; + break; + } + } + + /* Up a level or goto next */ + while (!is_end) + { + if (!parent) + { + /* Root bus, is end */ + bus = RT_NULL; + break; + } + + if (bus->list.next != &parent->children_nodes) + { + /* has next sibling */ + bus = rt_list_entry(bus->list.next, struct rt_pci_bus, list); + break; + } + + /* all device on this buss' parent */ + rt_list_for_each_entry(pdev, &parent->devices_nodes, list) + { + if (callback(pdev)) + { + is_end = RT_TRUE; + break; + } + } + + bus = parent; + parent = parent->parent; + } + } + + /* host bridge */ + if (!is_end) + { + // callback(bus->bridge); + } +} + +const struct rt_pci_device_id *rt_pci_match_id(struct rt_pci_device *pdev, const struct rt_pci_device_id *id) +{ + if ((id->vendor == PCI_ANY_ID || id->vendor == pdev->vendor) && + (id->device == PCI_ANY_ID || id->device == pdev->device) && + (id->subsystem_vendor == PCI_ANY_ID || id->subsystem_vendor == pdev->subsystem_vendor) && + (id->subsystem_device == PCI_ANY_ID || id->subsystem_device == pdev->subsystem_device) && + !((id->class ^ pdev->class) & id->class_mask)) + { + return id; + } + + return RT_NULL; +} + +const struct rt_pci_device_id *rt_pci_match_ids(struct rt_pci_device *pdev, const struct rt_pci_device_id *ids) +{ + while (ids->vendor || ids->subsystem_vendor || ids->class_mask) + { + if (rt_pci_match_id(pdev, ids)) + { + return ids; + } + + ++ids; + } + + return RT_NULL; +} + +static struct rt_bus pci_bus; + +rt_err_t rt_pci_driver_register(struct rt_pci_driver *pdrv) +{ + RT_ASSERT(pdrv != RT_NULL); + + pdrv->parent.bus = &pci_bus; +#if RT_NAME_MAX > 0 + rt_strcpy(pdrv->parent.parent.name, pdrv->name); +#else + pdrv->parent.parent.name = pdrv->name; +#endif + + return rt_driver_register(&pdrv->parent); +} + +rt_err_t rt_pci_device_register(struct rt_pci_device *pdev) +{ + RT_ASSERT(pdev != RT_NULL); + + rt_list_init(&pdev->list); + + return rt_bus_add_device(&pci_bus, &pdev->parent); +} + +static rt_bool_t pci_match(rt_driver_t drv, rt_device_t dev) +{ + rt_bool_t match = RT_FALSE; + struct rt_pci_driver *pdrv = rt_container_of(drv, struct rt_pci_driver, parent); + struct rt_pci_device *pdev = rt_container_of(dev, struct rt_pci_device, parent); + + if (pdrv->name && pdev->name) + { + match = rt_strcmp(pdrv->name, pdev->name) ? RT_FALSE : RT_TRUE; + } + + if (!match) + { + pdev->id = rt_pci_match_ids(pdev, pdrv->ids); + + match = pdev->id ? RT_TRUE : RT_FALSE; + } + + return match; +} + +static rt_err_t pci_probe(rt_device_t dev) +{ + rt_err_t err = RT_EOK; + struct rt_pci_driver *pdrv = rt_container_of(dev->drv, struct rt_pci_driver, parent); + struct rt_pci_device *pdev = rt_container_of(dev, struct rt_pci_device, parent); + + rt_pci_assign_irq(pdev); + + err = pdrv->probe(pdev); + + return err; +} + +static struct rt_bus pci_bus = +{ + .name = "pci", + .match = pci_match, + .probe = pci_probe, +}; + +static int pci_bus_init(void) +{ + rt_bus_register(&pci_bus); + + return 0; +} +INIT_CORE_EXPORT(pci_bus_init); diff --git a/components/drivers/pci/pci_ids.h b/components/drivers/pci/pci_ids.h new file mode 100644 index 000000000000..97e9fdd570b6 --- /dev/null +++ b/components/drivers/pci/pci_ids.h @@ -0,0 +1,271 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __PCI_IDS_H__ +#define __PCI_IDS_H__ + +#define PCI_VENDOR_ID_LOONGSON 0x0014 +#define PCI_VENDOR_ID_TTTECH 0x0357 +#define PCI_VENDOR_ID_DYNALINK 0x0675 +#define PCI_VENDOR_ID_UBIQUITI 0x0777 +#define PCI_VENDOR_ID_BERKOM 0x0871 +#define PCI_VENDOR_ID_COMPAQ 0x0e11 +#define PCI_VENDOR_ID_NCR 0x1000 +#define PCI_VENDOR_ID_ATI 0x1002 +#define PCI_VENDOR_ID_VLSI 0x1004 +#define PCI_VENDOR_ID_ADL 0x1005 +#define PCI_VENDOR_ID_NS 0x100b +#define PCI_VENDOR_ID_TSENG 0x100c +#define PCI_VENDOR_ID_WEITEK 0x100e +#define PCI_VENDOR_ID_DEC 0x1011 +#define PCI_VENDOR_ID_CIRRUS 0x1013 +#define PCI_VENDOR_ID_IBM 0x1014 +#define PCI_VENDOR_ID_UNISYS 0x1018 +#define PCI_VENDOR_ID_COMPEX2 0x101a +#define PCI_VENDOR_ID_WD 0x101c +#define PCI_VENDOR_ID_AMI 0x101e +#define PCI_VENDOR_ID_AMD 0x1022 +#define PCI_VENDOR_ID_TRIDENT 0x1023 +#define PCI_VENDOR_ID_AI 0x1025 +#define PCI_VENDOR_ID_DELL 0x1028 +#define PCI_VENDOR_ID_MATROX 0x102b +#define PCI_VENDOR_ID_MOBILITY_ELECTRONICS 0x14f2 +#define PCI_VENDOR_ID_CT 0x102c +#define PCI_VENDOR_ID_MIRO 0x1031 +#define PCI_VENDOR_ID_NEC 0x1033 +#define PCI_VENDOR_ID_FD 0x1036 +#define PCI_VENDOR_ID_SI 0x1039 +#define PCI_VENDOR_ID_HP 0x103c +#define PCI_VENDOR_ID_PCTECH 0x1042 +#define PCI_VENDOR_ID_ASUSTEK 0x1043 +#define PCI_VENDOR_ID_DPT 0x1044 +#define PCI_VENDOR_ID_OPTI 0x1045 +#define PCI_VENDOR_ID_ELSA 0x1048 +#define PCI_VENDOR_ID_STMICRO 0x104a +#define PCI_VENDOR_ID_BUSLOGIC 0x104b +#define PCI_VENDOR_ID_TI 0x104c +#define PCI_VENDOR_ID_SONY 0x104d +#define PCI_VENDOR_ID_ANIGMA 0x1051 +#define PCI_VENDOR_ID_EFAR 0x1055 +#define PCI_VENDOR_ID_MOTOROLA 0x1057 +#define PCI_VENDOR_ID_PROMISE 0x105a +#define PCI_VENDOR_ID_FOXCONN 0x105b +#define PCI_VENDOR_ID_UMC 0x1060 +#define PCI_VENDOR_ID_PICOPOWER 0x1066 +#define PCI_VENDOR_ID_MYLEX 0x1069 +#define PCI_VENDOR_ID_APPLE 0x106b +#define PCI_VENDOR_ID_YAMAHA 0x1073 +#define PCI_VENDOR_ID_QLOGIC 0x1077 +#define PCI_VENDOR_ID_CYRIX 0x1078 +#define PCI_VENDOR_ID_CONTAQ 0x1080 +#define PCI_VENDOR_ID_OLICOM 0x108d +#define PCI_VENDOR_ID_SUN 0x108e +#define PCI_VENDOR_ID_NI 0x1093 +#define PCI_VENDOR_ID_CMD 0x1095 +#define PCI_VENDOR_ID_BROOKTREE 0x109e +#define PCI_VENDOR_ID_SGI 0x10a9 +#define PCI_VENDOR_ID_WINBOND 0x10ad +#define PCI_VENDOR_ID_PLX 0x10b5 +#define PCI_VENDOR_ID_MADGE 0x10b6 +#define PCI_VENDOR_ID_3COM 0x10b7 +#define PCI_VENDOR_ID_AL 0x10b9 +#define PCI_VENDOR_ID_NEOMAGIC 0x10c8 +#define PCI_VENDOR_ID_TCONRAD 0x10da +#define PCI_VENDOR_ID_ROHM 0x10db +#define PCI_VENDOR_ID_NVIDIA 0x10de +#define PCI_VENDOR_ID_IMS 0x10e0 +#define PCI_VENDOR_ID_AMCC 0x10e8 +#define PCI_VENDOR_ID_INTERG 0x10ea +#define PCI_VENDOR_ID_REALTEK 0x10ec +#define PCI_VENDOR_ID_XILINX 0x10ee +#define PCI_VENDOR_ID_INIT 0x1101 +#define PCI_VENDOR_ID_CREATIVE 0x1102 +#define PCI_VENDOR_ID_ECTIVA PCI_VENDOR_ID_CREATIVE +#define PCI_VENDOR_ID_TTI 0x1103 +#define PCI_VENDOR_ID_SIGMA 0x1105 +#define PCI_VENDOR_ID_VIA 0x1106 +#define PCI_VENDOR_ID_SIEMENS 0x110a +#define PCI_VENDOR_ID_VORTEX 0x1119 +#define PCI_VENDOR_ID_EF 0x111a +#define PCI_VENDOR_ID_IDT 0x111d +#define PCI_VENDOR_ID_FORE 0x1127 +#define PCI_VENDOR_ID_PHILIPS 0x1131 +#define PCI_VENDOR_ID_EICON 0x1133 +#define PCI_VENDOR_ID_CISCO 0x1137 +#define PCI_VENDOR_ID_ZIATECH 0x1138 +#define PCI_VENDOR_ID_SYSKONNECT 0x1148 +#define PCI_VENDOR_ID_DIGI 0x114f +#define PCI_VENDOR_ID_XIRCOM 0x115d +#define PCI_VENDOR_ID_SERVERWORKS 0x1166 +#define PCI_VENDOR_ID_ALTERA 0x1172 +#define PCI_VENDOR_ID_SBE 0x1176 +#define PCI_VENDOR_ID_TOSHIBA 0x1179 +#define PCI_VENDOR_ID_TOSHIBA_2 0x102f +#define PCI_VENDOR_ID_ATTO 0x117c +#define PCI_VENDOR_ID_RICOH 0x1180 +#define PCI_VENDOR_ID_DLINK 0x1186 +#define PCI_VENDOR_ID_ARTOP 0x1191 +#define PCI_VENDOR_ID_ZEITNET 0x1193 +#define PCI_VENDOR_ID_FUJITSU_ME 0x119e +#define PCI_VENDOR_ID_MARVELL 0x11ab +#define PCI_VENDOR_ID_V3 0x11b0 +#define PCI_VENDOR_ID_ATT 0x11c1 +#define PCI_VENDOR_ID_SPECIALIX 0x11cb +#define PCI_VENDOR_ID_ANALOG_DEVICES 0x11d4 +#define PCI_VENDOR_ID_ZORAN 0x11de +#define PCI_VENDOR_ID_COMPEX 0x11f6 +#define PCI_VENDOR_ID_PMC_Sierra 0x11f8 +#define PCI_VENDOR_ID_RP 0x11fe +#define PCI_VENDOR_ID_CYCLADES 0x120e +#define PCI_VENDOR_ID_ESSENTIAL 0x120f +#define PCI_VENDOR_ID_O2 0x1217 +#define PCI_VENDOR_ID_3DFX 0x121a +#define PCI_VENDOR_ID_AVM 0x1244 +#define PCI_VENDOR_ID_STALLION 0x124d +#define PCI_VENDOR_ID_ESS 0x125d +#define PCI_VENDOR_ID_SATSAGEM 0x1267 +#define PCI_VENDOR_ID_ENSONIQ 0x1274 +#define PCI_VENDOR_ID_TRANSMETA 0x1279 +#define PCI_VENDOR_ID_ROCKWELL 0x127a +#define PCI_VENDOR_ID_ITE 0x1283 +#define PCI_VENDOR_ID_ALTEON 0x12ae +#define PCI_VENDOR_ID_NVIDIA_SGS 0x12d2 +#define PCI_VENDOR_ID_PERICOM 0x12d8 +#define PCI_VENDOR_ID_AUREAL 0x12eb +#define PCI_VENDOR_ID_ELECTRONICDESIGNGMBH 0x12f8 +#define PCI_VENDOR_ID_ESDGMBH 0x12fe +#define PCI_VENDOR_ID_CB 0x1307 +#define PCI_VENDOR_ID_SIIG 0x131f +#define PCI_VENDOR_ID_RADISYS 0x1331 +#define PCI_VENDOR_ID_MICRO_MEMORY 0x1332 +#define PCI_VENDOR_ID_DOMEX 0x134a +#define PCI_VENDOR_ID_INTASHIELD 0x135a +#define PCI_VENDOR_ID_QUATECH 0x135c +#define PCI_VENDOR_ID_SEALEVEL 0x135e +#define PCI_VENDOR_ID_HYPERCOPE 0x1365 +#define PCI_VENDOR_ID_DIGIGRAM 0x1369 +#define PCI_VENDOR_ID_KAWASAKI 0x136b +#define PCI_VENDOR_ID_CNET 0x1371 +#define PCI_VENDOR_ID_LMC 0x1376 +#define PCI_VENDOR_ID_NETGEAR 0x1385 +#define PCI_VENDOR_ID_APPLICOM 0x1389 +#define PCI_VENDOR_ID_MOXA 0x1393 +#define PCI_VENDOR_ID_CCD 0x1397 +#define PCI_VENDOR_ID_EXAR 0x13a8 +#define PCI_VENDOR_ID_MICROGATE 0x13c0 +#define PCI_VENDOR_ID_3WARE 0x13c1 +#define PCI_VENDOR_ID_IOMEGA 0x13ca +#define PCI_VENDOR_ID_ABOCOM 0x13d1 +#define PCI_VENDOR_ID_SUNDANCE 0x13f0 +#define PCI_VENDOR_ID_CMEDIA 0x13f6 +#define PCI_VENDOR_ID_ADVANTECH 0x13fe +#define PCI_VENDOR_ID_MEILHAUS 0x1402 +#define PCI_VENDOR_ID_LAVA 0x1407 +#define PCI_VENDOR_ID_TIMEDIA 0x1409 +#define PCI_VENDOR_ID_ICE 0x1412 +#define PCI_VENDOR_ID_MICROSOFT 0x1414 +#define PCI_VENDOR_ID_OXSEMI 0x1415 +#define PCI_VENDOR_ID_CHELSIO 0x1425 +#define PCI_VENDOR_ID_ADLINK 0x144a +#define PCI_VENDOR_ID_SAMSUNG 0x144d +#define PCI_VENDOR_ID_GIGABYTE 0x1458 +#define PCI_VENDOR_ID_AMBIT 0x1468 +#define PCI_VENDOR_ID_MYRICOM 0x14c1 +#define PCI_VENDOR_ID_MEDIATEK 0x14c3 +#define PCI_VENDOR_ID_TITAN 0x14d2 +#define PCI_VENDOR_ID_PANACOM 0x14d4 +#define PCI_VENDOR_ID_SIPACKETS 0x14d9 +#define PCI_VENDOR_ID_AFAVLAB 0x14db +#define PCI_VENDOR_ID_AMPLICON 0x14dc +#define PCI_VENDOR_ID_BCM_GVC 0x14a4 +#define PCI_VENDOR_ID_TOPIC 0x151f +#define PCI_VENDOR_ID_MAINPINE 0x1522 +#define PCI_VENDOR_ID_SYBA 0x1592 +#define PCI_VENDOR_ID_MORETON 0x15aa +#define PCI_VENDOR_ID_VMWARE 0x15ad +#define PCI_VENDOR_ID_ZOLTRIX 0x15b0 +#define PCI_VENDOR_ID_MELLANOX 0x15b3 +#define PCI_VENDOR_ID_DFI 0x15bd +#define PCI_VENDOR_ID_QUICKNET 0x15e2 +#define PCI_VENDOR_ID_PDC 0x15e9 +#define PCI_VENDOR_ID_FARSITE 0x1619 +#define PCI_VENDOR_ID_ARIMA 0x161f +#define PCI_VENDOR_ID_BROCADE 0x1657 +#define PCI_VENDOR_ID_SIBYTE 0x166d +#define PCI_VENDOR_ID_ATHEROS 0x168c +#define PCI_VENDOR_ID_NETCELL 0x169c +#define PCI_VENDOR_ID_CENATEK 0x16ca +#define PCI_VENDOR_ID_SYNOPSYS 0x16c3 +#define PCI_VENDOR_ID_USR 0x16ec +#define PCI_VENDOR_ID_VITESSE 0x1725 +#define PCI_VENDOR_ID_LINKSYS 0x1737 +#define PCI_VENDOR_ID_ALTIMA 0x173b +#define PCI_VENDOR_ID_CAVIUM 0x177d +#define PCI_VENDOR_ID_TECHWELL 0x1797 +#define PCI_VENDOR_ID_BELKIN 0x1799 +#define PCI_VENDOR_ID_RDC 0x17f3 +#define PCI_VENDOR_ID_GLI 0x17a0 +#define PCI_VENDOR_ID_LENOVO 0x17aa +#define PCI_VENDOR_ID_QCOM 0x17cb +#define PCI_VENDOR_ID_CDNS 0x17cd +#define PCI_VENDOR_ID_ARECA 0x17d3 +#define PCI_VENDOR_ID_S2IO 0x17d5 +#define PCI_VENDOR_ID_SITECOM 0x182d +#define PCI_VENDOR_ID_TOPSPIN 0x1867 +#define PCI_VENDOR_ID_COMMTECH 0x18f7 +#define PCI_VENDOR_ID_SILAN 0x1904 +#define PCI_VENDOR_ID_RENESAS 0x1912 +#define PCI_VENDOR_ID_SOLARFLARE 0x1924 +#define PCI_VENDOR_ID_TDI 0x192e +#define PCI_VENDOR_ID_FREESCALE 0x1957 +#define PCI_VENDOR_ID_NXP PCI_VENDOR_ID_FREESCALE +#define PCI_VENDOR_ID_PASEMI 0x1959 +#define PCI_VENDOR_ID_ATTANSIC 0x1969 +#define PCI_VENDOR_ID_JMICRON 0x197b +#define PCI_VENDOR_ID_KORENIX 0x1982 +#define PCI_VENDOR_ID_HUAWEI 0x19e5 +#define PCI_VENDOR_ID_NETRONOME 0x19ee +#define PCI_VENDOR_ID_QMI 0x1a32 +#define PCI_VENDOR_ID_AZWAVE 0x1a3b +#define PCI_VENDOR_ID_REDHAT_QUMRANET 0x1af4 +#define PCI_VENDOR_ID_ASMEDIA 0x1b21 +#define PCI_VENDOR_ID_REDHAT 0x1b36 +#define PCI_VENDOR_ID_SILICOM_DENMARK 0x1c2c +#define PCI_VENDOR_ID_AMAZON_ANNAPURNA_LABS 0x1c36 +#define PCI_VENDOR_ID_CIRCUITCO 0x1cc8 +#define PCI_VENDOR_ID_AMAZON 0x1d0f +#define PCI_VENDOR_ID_ZHAOXIN 0x1d17 +#define PCI_VENDOR_ID_HYGON 0x1d94 +#define PCI_VENDOR_ID_FUNGIBLE 0x1dad +#define PCI_VENDOR_ID_HXT 0x1dbf +#define PCI_VENDOR_ID_TEKRAM 0x1de1 +#define PCI_VENDOR_ID_TEHUTI 0x1fc9 +#define PCI_VENDOR_ID_SUNIX 0x1fd4 +#define PCI_VENDOR_ID_HINT 0x3388 +#define PCI_VENDOR_ID_3DLABS 0x3d3d +#define PCI_VENDOR_ID_NETXEN 0x4040 +#define PCI_VENDOR_ID_AKS 0x416c +#define PCI_VENDOR_ID_ACCESSIO 0x494f +#define PCI_VENDOR_ID_S3 0x5333 +#define PCI_VENDOR_ID_DUNORD 0x5544 +#define PCI_VENDOR_ID_DCI 0x6666 +#define PCI_VENDOR_ID_INTEL 0x8086 +#define PCI_VENDOR_ID_SCALEMP 0x8686 +#define PCI_VENDOR_ID_COMPUTONE 0x8e0e +#define PCI_VENDOR_ID_KTI 0x8e2e +#define PCI_VENDOR_ID_ADAPTEC 0x9004 +#define PCI_VENDOR_ID_ADAPTEC2 0x9005 +#define PCI_VENDOR_ID_HOLTEK 0x9412 +#define PCI_VENDOR_ID_NETMOS 0x9710 +#define PCI_VENDOR_ID_3COM_2 0xa727 +#define PCI_VENDOR_ID_DIGIUM 0xd161 +#define PCI_VENDOR_ID_TIGERJET 0xe159 +#define PCI_VENDOR_ID_XILINX_RME 0xea60 +#define PCI_VENDOR_ID_XEN 0x5853 +#define PCI_VENDOR_ID_OCZ 0x1b85 +#define PCI_VENDOR_ID_NCUBE 0x10ff + +#endif /* __PCI_IDS_H__ */ diff --git a/components/drivers/pci/pci_regs.h b/components/drivers/pci/pci_regs.h new file mode 100644 index 000000000000..95c526ec9c66 --- /dev/null +++ b/components/drivers/pci/pci_regs.h @@ -0,0 +1,1024 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __PCI_REGS_H__ +#define __PCI_REGS_H__ + +#include + +/* + * PCI standard defines + * Copyright 1994, Drew Eckhardt + * Copyright 1997--1999 Martin Mares + * + * For more information, please consult the following manuals (look at + * http://www.pcisig.com/ for how to get them): + * + * PCI BIOS Specification + * PCI Local Bus Specification + * PCI to PCI Bridge Specification + * PCI System Design Guide + * + * For HyperTransport information, please consult the following manuals + * from http://www.hypertransport.org : + * + * The HyperTransport I/O Link Specification + * + * Mean of prefix: + * + * PCIM_xxx: mask to locate subfield in register + * PCIR_xxx: config register offset + * PCIC_xxx: device class + * PCIS_xxx: device subclass + * PCIP_xxx: device programming interface + * PCIV_xxx: PCI vendor ID (only required to fixup ancient devices) + * PCID_xxx: device ID + * PCIY_xxx: capability identification number + * PCIZ_xxx: extended capability identification number + */ + +/* some PCI bus constants */ +#define PCI_DOMAINMAX 65535 /* highest supported domain number */ +#define PCI_BUSMAX 255 /* highest supported bus number */ +#define PCI_SLOTMAX 31 /* highest supported slot number */ +#define PCI_FUNCMAX 7 /* highest supported function number */ +#define PCI_REGMAX 255 /* highest supported config register addr */ +#define PCIE_REGMAX 4095 /* highest supported config register addr */ +#define PCI_MAXHDRTYPE 2 +#define PCI_STD_HEADER_SIZEOF 64 +#define PCI_STD_NUM_BARS 6 /* number of standard BARs */ + +/* PCI config header registers for all devices */ + +#define PCIR_DEVVENDOR 0x00 +#define PCIR_VENDOR 0x00 +#define PCIR_DEVICE 0x02 +#define PCIR_COMMAND 0x04 +#define PCIM_CMD_PORTEN 0x0001 +#define PCIM_CMD_MEMEN 0x0002 +#define PCIM_CMD_BUSMASTEREN 0x0004 +#define PCIM_CMD_SPECIALEN 0x0008 +#define PCIM_CMD_MWRICEN 0x0010 +#define PCIM_CMD_PERRESPEN 0x0040 +#define PCIM_CMD_SERRESPEN 0x0100 +#define PCIM_CMD_BACKTOBACK 0x0200 +#define PCIM_CMD_INTxDIS 0x0400 +#define PCIR_STATUS 0x06 +#define PCIM_STATUS_INTxSTATE 0x0008 +#define PCIM_STATUS_CAPPRESENT 0x0010 +#define PCIM_STATUS_66CAPABLE 0x0020 +#define PCIM_STATUS_BACKTOBACK 0x0080 +#define PCIM_STATUS_MDPERR 0x0100 +#define PCIM_STATUS_SEL_FAST 0x0000 +#define PCIM_STATUS_SEL_MEDIMUM 0x0200 +#define PCIM_STATUS_SEL_SLOW 0x0400 +#define PCIM_STATUS_SEL_MASK 0x0600 +#define PCIM_STATUS_STABORT 0x0800 +#define PCIM_STATUS_RTABORT 0x1000 +#define PCIM_STATUS_RMABORT 0x2000 +#define PCIM_STATUS_SERR 0x4000 +#define PCIM_STATUS_PERR 0x8000 +#define PCIR_REVID 0x08 +#define PCIR_PROGIF 0x09 +#define PCIR_SUBCLASS 0x0a +#define PCIR_CLASS 0x0b +#define PCIR_CACHELNSZ 0x0c +#define PCIR_LATTIMER 0x0d +#define PCIR_HDRTYPE 0x0e +#define PCIM_HDRTYPE 0x7f +#define PCIM_HDRTYPE_NORMAL 0x00 +#define PCIM_HDRTYPE_BRIDGE 0x01 +#define PCIM_HDRTYPE_CARDBUS 0x02 +#define PCIM_MFDEV 0x80 +#define PCIR_BIST 0x0f + +/* PCI Spec rev 2.2: 0FFFFh is an invalid value for Vendor ID. */ +#define PCIV_INVALID 0xffff + +/* Capability Register Offsets */ + +#define PCICAP_ID 0x0 +#define PCICAP_NEXTPTR 0x1 + +/* Capability Identification Numbers */ + +#define PCIY_PMG 0x01 /* PCI Power Management */ +#define PCIY_AGP 0x02 /* AGP */ +#define PCIY_VPD 0x03 /* Vital Product Data */ +#define PCIY_SLOTID 0x04 /* Slot Identification */ +#define PCIY_MSI 0x05 /* Message Signaled Interrupts */ +#define PCIY_CHSWP 0x06 /* CompactPCI Hot Swap */ +#define PCIY_PCIX 0x07 /* PCI-X */ +#define PCIY_HT 0x08 /* HyperTransport */ +#define PCIY_VENDOR 0x09 /* Vendor Unique */ +#define PCIY_DEBUG 0x0a /* Debug port */ +#define PCIY_CRES 0x0b /* CompactPCI central resource control */ +#define PCIY_HOTPLUG 0x0c /* PCI Hot-Plug */ +#define PCIY_SUBVENDOR 0x0d /* PCI-PCI bridge subvendor ID */ +#define PCIY_AGP8X 0x0e /* AGP 8x */ +#define PCIY_SECDEV 0x0f /* Secure Device */ +#define PCIY_EXPRESS 0x10 /* PCI Express */ +#define PCIY_MSIX 0x11 /* MSI-X */ +#define PCIY_SATA 0x12 /* SATA */ +#define PCIY_PCIAF 0x13 /* PCI Advanced Features */ +#define PCIY_EA 0x14 /* PCI Extended Allocation */ +#define PCIY_FPB 0x15 /* Flattening Portal Bridge */ + +/* Extended Capability Register Fields */ + +#define PCIR_EXTCAP 0x100 +#define PCIM_EXTCAP_ID 0x0000ffff +#define PCIM_EXTCAP_VER 0x000f0000 +#define PCIM_EXTCAP_NEXTPTR 0xfff00000 +#define PCI_EXTCAP_ID(ecap) ((ecap) & PCIM_EXTCAP_ID) +#define PCI_EXTCAP_VER(ecap) (((ecap) & PCIM_EXTCAP_VER) >> 16) +#define PCI_EXTCAP_NEXTPTR(ecap) (((ecap) & PCIM_EXTCAP_NEXTPTR) >> 20) + +/* Extended Capability Identification Numbers */ + +#define PCIZ_AER 0x0001 /* Advanced Error Reporting */ +#define PCIZ_VC 0x0002 /* Virtual Channel if MFVC Ext Cap not set */ +#define PCIZ_SERNUM 0x0003 /* Device Serial Number */ +#define PCIZ_PWRBDGT 0x0004 /* Power Budgeting */ +#define PCIZ_RCLINK_DCL 0x0005 /* Root Complex Link Declaration */ +#define PCIZ_RCLINK_CTL 0x0006 /* Root Complex Internal Link Control */ +#define PCIZ_RCEC_ASSOC 0x0007 /* Root Complex Event Collector Association */ +#define PCIZ_MFVC 0x0008 /* Multi-Function Virtual Channel */ +#define PCIZ_VC2 0x0009 /* Virtual Channel if MFVC Ext Cap set */ +#define PCIZ_RCRB 0x000a /* RCRB Header */ +#define PCIZ_VENDOR 0x000b /* Vendor Unique */ +#define PCIZ_CAC 0x000c /* Configuration Access Correction -- obsolete */ +#define PCIZ_ACS 0x000d /* Access Control Services */ +#define PCIZ_ARI 0x000e /* Alternative Routing-ID Interpretation */ +#define PCIZ_ATS 0x000f /* Address Translation Services */ +#define PCIZ_SRIOV 0x0010 /* Single Root IO Virtualization */ +#define PCIZ_MRIOV 0x0011 /* Multiple Root IO Virtualization */ +#define PCIZ_MULTICAST 0x0012 /* Multicast */ +#define PCIZ_PAGE_REQ 0x0013 /* Page Request */ +#define PCIZ_AMD 0x0014 /* Reserved for AMD */ +#define PCIZ_RESIZE_BAR 0x0015 /* Resizable BAR */ +#define PCIZ_DPA 0x0016 /* Dynamic Power Allocation */ +#define PCIZ_TPH_REQ 0x0017 /* TPH Requester */ +#define PCIZ_LTR 0x0018 /* Latency Tolerance Reporting */ +#define PCIZ_SEC_PCIE 0x0019 /* Secondary PCI Express */ +#define PCIZ_PMUX 0x001a /* Protocol Multiplexing */ +#define PCIZ_PASID 0x001b /* Process Address Space ID */ +#define PCIZ_LN_REQ 0x001c /* LN Requester */ +#define PCIZ_DPC 0x001d /* Downstream Port Containment */ +#define PCIZ_L1PM 0x001e /* L1 PM Substates */ +#define PCIZ_PTM 0x001f /* Precision Time Measurement */ +#define PCIZ_M_PCIE 0x0020 /* PCIe over M-PHY */ +#define PCIZ_FRS 0x0021 /* FRS Queuing */ +#define PCIZ_RTR 0x0022 /* Readiness Time Reporting */ +#define PCIZ_DVSEC 0x0023 /* Designated Vendor-Specific */ +#define PCIZ_VF_REBAR 0x0024 /* VF Resizable BAR */ +#define PCIZ_DLNK 0x0025 /* Data Link Feature */ +#define PCIZ_16GT 0x0026 /* Physical Layer 16.0 GT/s */ +#define PCIZ_LMR 0x0027 /* Lane Margining at Receiver */ +#define PCIZ_HIER_ID 0x0028 /* Hierarchy ID */ +#define PCIZ_NPEM 0x0029 /* Native PCIe Enclosure Management */ +#define PCIZ_PL32 0x002a /* Physical Layer 32.0 GT/s */ +#define PCIZ_AP 0x002b /* Alternate Protocol */ +#define PCIZ_SFI 0x002c /* System Firmware Intermediary */ + +/* config registers for header type 0 devices */ + +#define PCIR_BARS 0x10 +#define PCIR_BAR(x) (PCIR_BARS + (x) * 4) +#define PCI_RID2BAR(rid) (((rid) - PCIR_BARS) / 4) +#define PCI_BAR_IO(x) (((x) & PCIM_BAR_SPACE) == PCIM_BAR_IO_SPACE) +#define PCI_BAR_MEM(x) (((x) & PCIM_BAR_SPACE) == PCIM_BAR_MEM_SPACE) +#define PCIM_BAR_SPACE 0x01 /* 0 = memory, 1 = I/O */ +#define PCIM_BAR_SPACE_IO 0x01 +#define PCIM_BAR_SPACE_MEMORY 0x00 +#define PCIM_BAR_MEM_TYPE_MASK 0x06 +#define PCIM_BAR_MEM_TYPE_32 0x00 /* 32 bit address */ +#define PCIM_BAR_MEM_TYPE_1M 0x02 /* Below 1M [obsolete] */ +#define PCIM_BAR_MEM_TYPE_64 0x04 /* 64 bit address */ +#define PCIM_BAR_MEM_PREFETCH 0x08 /* prefetchable? */ +#define PCIM_BAR_MEM_MASK (~0x0fUL) +#define PCIM_BAR_IO_MASK (~0x03UL) +#define PCIR_CIS 0x28 +#define PCIM_CIS_ASI_MASK 0x00000007 +#define PCIM_CIS_ASI_CONFIG 0 +#define PCIM_CIS_ASI_BAR0 1 +#define PCIM_CIS_ASI_BAR1 2 +#define PCIM_CIS_ASI_BAR2 3 +#define PCIM_CIS_ASI_BAR3 4 +#define PCIM_CIS_ASI_BAR4 5 +#define PCIM_CIS_ASI_BAR5 6 +#define PCIM_CIS_ASI_ROM 7 +#define PCIM_CIS_ADDR_MASK 0x0ffffff8 +#define PCIM_CIS_ROM_MASK 0xf0000000 +#define PCIM_CIS_CONFIG_MASK 0xff +#define PCIR_SUBVEND_0 0x2c +#define PCIR_SUBDEV_0 0x2e +#define PCIR_BIOS 0x30 +#define PCIM_BIOS_ENABLE 0x01 +#define PCIM_BIOS_ADDR_MASK 0xfffff800 +#define PCIR_CAP_PTR 0x34 +#define PCIR_INTLINE 0x3c +#define PCIR_INTPIN 0x3d +#define PCIR_MINGNT 0x3e +#define PCIR_MAXLAT 0x3f + +/* config registers for header type 1 (PCI-to-PCI bridge) devices */ + +#define PCIR_MAX_BAR_1 1 +#define PCIR_SECSTAT_1 0x1e + +#define PCIR_PRIBUS_1 0x18 +#define PCIR_SECBUS_1 0x19 +#define PCIR_SUBBUS_1 0x1a +#define PCIR_SECLAT_1 0x1b + +#define PCIR_IOBASEL_1 0x1c +#define PCIR_IOLIMITL_1 0x1d +#define PCIR_IOBASEH_1 0x30 +#define PCIR_IOLIMITH_1 0x32 +#define PCIM_BRIO_16 0x0 +#define PCIM_BRIO_32 0x1 +#define PCIM_BRIO_MASK 0xf + +#define PCIR_MEMBASE_1 0x20 +#define PCIR_MEMLIMIT_1 0x22 + +#define PCIR_PMBASEL_1 0x24 +#define PCIR_PMLIMITL_1 0x26 +#define PCIR_PMBASEH_1 0x28 +#define PCIR_PMLIMITH_1 0x2c +#define PCIM_BRPM_32 0x0 +#define PCIM_BRPM_64 0x1 +#define PCIM_BRPM_MASK 0xf + +#define PCIR_BIOS_1 0x38 +#define PCIR_BRIDGECTL_1 0x3e + +#define PCI_PPBMEMBASE(h, l) ((((rt_uint64_t)(h) << 32) + ((l) << 16)) & ~0xfffff) +#define PCI_PPBMEMLIMIT(h, l) ((((rt_uint64_t)(h) << 32) + ((l) << 16)) | 0xfffff) +#define PCI_PPBIOBASE(h, l) ((((h) << 16) + ((l) << 8)) & ~0xfff) +#define PCI_PPBIOLIMIT(h, l) ((((h) << 16) + ((l) << 8)) | 0xfff) + +/* config registers for header t ype 2 (CardBus) devices */ + +#define PCIR_MAX_BAR_2 0 +#define PCIR_CAP_PTR_2 0x14 +#define PCIR_SECSTAT_2 0x16 + +#define PCIR_PRIBUS_2 0x18 +#define PCIR_SECBUS_2 0x19 +#define PCIR_SUBBUS_2 0x1a +#define PCIR_SECLAT_2 0x1b + +#define PCIR_MEMBASE0_2 0x1c +#define PCIR_MEMLIMIT0_2 0x20 +#define PCIR_MEMBASE1_2 0x24 +#define PCIR_MEMLIMIT1_2 0x28 +#define PCIR_IOBASE0_2 0x2c +#define PCIR_IOLIMIT0_2 0x30 +#define PCIR_IOBASE1_2 0x34 +#define PCIR_IOLIMIT1_2 0x38 +#define PCIM_CBBIO_16 0x0 +#define PCIM_CBBIO_32 0x1 +#define PCIM_CBBIO_MASK 0x3 + +#define PCIR_BRIDGECTL_2 0x3e + +#define PCIR_SUBVEND_2 0x40 +#define PCIR_SUBDEV_2 0x42 + +#define PCIR_PCCARDIF_2 0x44 + +#define PCI_CBBMEMBASE(l) ((l) & ~0xfffff) +#define PCI_CBBMEMLIMIT(l) ((l) | 0xfffff) +#define PCI_CBBIOBASE(l) ((l) & ~0x3) +#define PCI_CBBIOLIMIT(l) ((l) | 0x3) + +/* PCI device class, subclass and programming interface definitions */ +#define PCIC_NOT_DEFINED 0x0000 +#define PCIS_NOT_DEFINED_VGA 0x0001 + +#define PCIC_STORAGE 0x01 +#define PCIS_STORAGE_SCSI 0x0100 +#define PCIS_STORAGE_IDE 0x0101 +#define PCIS_STORAGE_FLOPPY 0x0102 +#define PCIS_STORAGE_IPI 0x0103 +#define PCIS_STORAGE_RAID 0x0104 +#define PCIS_STORAGE_SATA 0x0106 +#define PCIS_STORAGE_SATA_AHCI 0x010601 +#define PCIS_STORAGE_SAS 0x0107 +#define PCIS_STORAGE_EXPRESS 0x010802 +#define PCIS_STORAGE_OTHER 0x0180 + +#define PCIC_NETWORK 0x02 +#define PCIS_NETWORK_ETHERNET 0x0200 +#define PCIS_NETWORK_TOKEN_RING 0x0201 +#define PCIS_NETWORK_FDDI 0x0202 +#define PCIS_NETWORK_ATM 0x0203 +#define PCIS_NETWORK_OTHER 0x0280 + +#define PCIC_DISPLAY 0x03 +#define PCIS_DISPLAY_VGA 0x0300 +#define PCIS_DISPLAY_XGA 0x0301 +#define PCIS_DISPLAY_3D 0x0302 +#define PCIS_DISPLAY_OTHER 0x0380 + +#define PCIC_MULTIMEDIA 0x04 +#define PCIS_MULTIMEDIA_VIDEO 0x0400 +#define PCIS_MULTIMEDIA_AUDIO 0x0401 +#define PCIS_MULTIMEDIA_PHONE 0x0402 +#define PCIS_MULTIMEDIA_HD_AUDIO 0x0403 +#define PCIS_MULTIMEDIA_OTHER 0x0480 + +#define PCIC_MEMORY 0x05 +#define PCIS_MEMORY_RAM 0x0500 +#define PCIS_MEMORY_FLASH 0x0501 +#define PCIS_MEMORY_CXL 0x0502 +#define PCIS_MEMORY_OTHER 0x0580 + +#define PCIC_BRIDGE 0x06 +#define PCIS_BRIDGE_HOST 0x0600 +#define PCIS_BRIDGE_ISA 0x0601 +#define PCIS_BRIDGE_EISA 0x0602 +#define PCIS_BRIDGE_MC 0x0603 +#define PCIS_BRIDGE_PCI 0x0604 +#define PCIS_BRIDGE_PCI_NORMAL 0x060400 +#define PCIS_BRIDGE_PCI_SUBTRACTIVE 0x060401 +#define PCIS_BRIDGE_PCMCIA 0x0605 +#define PCIS_BRIDGE_NUBUS 0x0606 +#define PCIS_BRIDGE_CARDBUS 0x0607 +#define PCIS_BRIDGE_RACEWAY 0x0608 +#define PCIS_BRIDGE_OTHER 0x0680 + +#define PCIC_COMMUNICATION 0x07 +#define PCIS_COMMUNICATION_SERIAL 0x0700 +#define PCIS_COMMUNICATION_PARALLEL 0x0701 +#define PCIS_COMMUNICATION_MULTISERIAL 0x0702 +#define PCIS_COMMUNICATION_MODEM 0x0703 +#define PCIS_COMMUNICATION_OTHER 0x0780 + +#define PCIC_SYSTEM 0x08 +#define PCIS_SYSTEM_PIC 0x0800 +#define PCIS_SYSTEM_PIC_IOAPIC 0x080010 +#define PCIS_SYSTEM_PIC_IOXAPIC 0x080020 +#define PCIS_SYSTEM_DMA 0x0801 +#define PCIS_SYSTEM_TIMER 0x0802 +#define PCIS_SYSTEM_RTC 0x0803 +#define PCIS_SYSTEM_PCI_HOTPLUG 0x0804 +#define PCIS_SYSTEM_SDHCI 0x0805 +#define PCIS_SYSTEM_RCEC 0x0807 +#define PCIS_SYSTEM_OTHER 0x0880 + +#define PCIC_INPUT 0x09 +#define PCIS_INPUT_KEYBOARD 0x0900 +#define PCIS_INPUT_PEN 0x0901 +#define PCIS_INPUT_MOUSE 0x0902 +#define PCIS_INPUT_SCANNER 0x0903 +#define PCIS_INPUT_GAMEPORT 0x0904 +#define PCIS_INPUT_OTHER 0x0980 + +#define PCIC_DOCKING 0x0a +#define PCIS_DOCKING_GENERIC 0x0a00 +#define PCIS_DOCKING_OTHER 0x0a80 + +#define PCIC_PROCESSOR 0x0b +#define PCIS_PROCESSOR_386 0x0b00 +#define PCIS_PROCESSOR_486 0x0b01 +#define PCIS_PROCESSOR_PENTIUM 0x0b02 +#define PCIS_PROCESSOR_ALPHA 0x0b10 +#define PCIS_PROCESSOR_POWERPC 0x0b20 +#define PCIS_PROCESSOR_MIPS 0x0b30 +#define PCIS_PROCESSOR_CO 0x0b40 + +#define PCIC_SERIAL 0x0c +#define PCIS_SERIAL_FIREWIRE 0x0c00 +#define PCIS_SERIAL_FIREWIRE_OHCI 0x0c0010 +#define PCIS_SERIAL_ACCESS 0x0c01 +#define PCIS_SERIAL_SSA 0x0c02 +#define PCIS_SERIAL_USB 0x0c03 +#define PCIS_SERIAL_USB_UHCI 0x0c0300 +#define PCIS_SERIAL_USB_OHCI 0x0c0310 +#define PCIS_SERIAL_USB_EHCI 0x0c0320 +#define PCIS_SERIAL_USB_XHCI 0x0c0330 +#define PCIS_SERIAL_USB_DEVICE 0x0c03fe +#define PCIS_SERIAL_FIBER 0x0c04 +#define PCIS_SERIAL_SMBUS 0x0c05 +#define PCIS_SERIAL_IPMI 0x0c07 +#define PCIS_SERIAL_IPMI_SMIC 0x0c0700 +#define PCIS_SERIAL_IPMI_KCS 0x0c0701 +#define PCIS_SERIAL_IPMI_BT 0x0c0702 + +#define PCIC_WIRELESS 0x0d +#define PCIS_WIRELESS_RF_CONTROLLER 0x0d10 +#define PCIS_WIRELESS_WHCI 0x0d1010 + +#define PCIC_INTELLIGENT 0x0e +#define PCIS_INTELLIGENT_I2O 0x0e00 + +#define PCIC_SATELLITE 0x0f +#define PCIS_SATELLITE_TV 0x0f00 +#define PCIS_SATELLITE_AUDIO 0x0f01 +#define PCIS_SATELLITE_VOICE 0x0f03 +#define PCIS_SATELLITE_DATA 0x0f04 + +#define PCIC_CRYPT 0x10 +#define PCIS_CRYPT_NETWORK 0x1000 +#define PCIS_CRYPT_ENTERTAINMENT 0x1001 +#define PCIS_CRYPT_OTHER 0x1080 + +#define PCIC_SIGNAL_PROCESSING 0x11 +#define PCIS_SP_DPIO 0x1100 +#define PCIS_SP_OTHER 0x1180 + +#define PCIS_OTHERS 0xff + +/* Bridge Control Values. */ +#define PCIB_BCR_PERR_ENABLE 0x0001 +#define PCIB_BCR_SERR_ENABLE 0x0002 +#define PCIB_BCR_ISA_ENABLE 0x0004 +#define PCIB_BCR_VGA_ENABLE 0x0008 +#define PCIB_BCR_MASTER_ABORT_MODE 0x0020 +#define PCIB_BCR_SECBUS_RESET 0x0040 +#define PCIB_BCR_SECBUS_BACKTOBACK 0x0080 +#define PCIB_BCR_PRI_DISCARD_TIMEOUT 0x0100 +#define PCIB_BCR_SEC_DISCARD_TIMEOUT 0x0200 +#define PCIB_BCR_DISCARD_TIMER_STATUS 0x0400 +#define PCIB_BCR_DISCARD_TIMER_SERREN 0x0800 + +#define CBB_BCR_PERR_ENABLE 0x0001 +#define CBB_BCR_SERR_ENABLE 0x0002 +#define CBB_BCR_ISA_ENABLE 0x0004 +#define CBB_BCR_VGA_ENABLE 0x0008 +#define CBB_BCR_MASTER_ABORT_MODE 0x0020 +#define CBB_BCR_CARDBUS_RESET 0x0040 +#define CBB_BCR_IREQ_INT_ENABLE 0x0080 +#define CBB_BCR_PREFETCH_0_ENABLE 0x0100 +#define CBB_BCR_PREFETCH_1_ENABLE 0x0200 +#define CBB_BCR_WRITE_POSTING_ENABLE 0x0400 + +/* PCI power manangement */ +#define PCIR_POWER_CAP 0x2 +#define PCIM_PCAP_SPEC 0x0007 +#define PCIM_PCAP_PMEREQCLK 0x0008 +#define PCIM_PCAP_DEVSPECINIT 0x0020 +#define PCIM_PCAP_AUXPWR_0 0x0000 +#define PCIM_PCAP_AUXPWR_55 0x0040 +#define PCIM_PCAP_AUXPWR_100 0x0080 +#define PCIM_PCAP_AUXPWR_160 0x00c0 +#define PCIM_PCAP_AUXPWR_220 0x0100 +#define PCIM_PCAP_AUXPWR_270 0x0140 +#define PCIM_PCAP_AUXPWR_320 0x0180 +#define PCIM_PCAP_AUXPWR_375 0x01c0 +#define PCIM_PCAP_AUXPWRMASK 0x01c0 +#define PCIM_PCAP_D1SUPP 0x0200 +#define PCIM_PCAP_D2SUPP 0x0400 +#define PCIM_PCAP_D0PME 0x0800 +#define PCIM_PCAP_D1PME 0x1000 +#define PCIM_PCAP_D2PME 0x2000 +#define PCIM_PCAP_D3PME_HOT 0x4000 +#define PCIM_PCAP_D3PME_COLD 0x8000 + +#define PCIR_POWER_STATUS 0x4 +#define PCIM_PSTAT_D0 0x0000 +#define PCIM_PSTAT_D1 0x0001 +#define PCIM_PSTAT_D2 0x0002 +#define PCIM_PSTAT_D3 0x0003 +#define PCIM_PSTAT_DMASK 0x0003 +#define PCIM_PSTAT_NOSOFTRESET 0x0008 +#define PCIM_PSTAT_PMEENABLE 0x0100 +#define PCIM_PSTAT_D0POWER 0x0000 +#define PCIM_PSTAT_D1POWER 0x0200 +#define PCIM_PSTAT_D2POWER 0x0400 +#define PCIM_PSTAT_D3POWER 0x0600 +#define PCIM_PSTAT_D0HEAT 0x0800 +#define PCIM_PSTAT_D1HEAT 0x0a00 +#define PCIM_PSTAT_D2HEAT 0x0c00 +#define PCIM_PSTAT_D3HEAT 0x0e00 +#define PCIM_PSTAT_DATASELMASK 0x1e00 +#define PCIM_PSTAT_DATAUNKN 0x0000 +#define PCIM_PSTAT_DATADIV10 0x2000 +#define PCIM_PSTAT_DATADIV100 0x4000 +#define PCIM_PSTAT_DATADIV1000 0x6000 +#define PCIM_PSTAT_DATADIVMASK 0x6000 +#define PCIM_PSTAT_PME 0x8000 + +#define PCIR_POWER_BSE 0x6 +#define PCIM_PMCSR_BSE_D3B3 0x00 +#define PCIM_PMCSR_BSE_D3B2 0x40 +#define PCIM_PMCSR_BSE_BPCCE 0x80 + +#define PCIR_POWER_DATA 0x7 + +/* VPD capability registers */ +#define PCIR_VPD_ADDR 0x2 +#define PCIR_VPD_DATA 0x4 + +/* PCI Message Signalled Interrupts (MSI) */ +#define PCIR_MSI_CTRL 0x2 +#define PCIM_MSICTRL_VECTOR 0x0100 +#define PCIM_MSICTRL_64BIT 0x0080 +#define PCIM_MSICTRL_MME_MASK 0x0070 +#define PCIM_MSICTRL_MME_1 0x0000 +#define PCIM_MSICTRL_MME_2 0x0010 +#define PCIM_MSICTRL_MME_4 0x0020 +#define PCIM_MSICTRL_MME_8 0x0030 +#define PCIM_MSICTRL_MME_16 0x0040 +#define PCIM_MSICTRL_MME_32 0x0050 +#define PCIM_MSICTRL_MMC_MASK 0x000e +#define PCIM_MSICTRL_MMC_1 0x0000 +#define PCIM_MSICTRL_MMC_2 0x0002 +#define PCIM_MSICTRL_MMC_4 0x0004 +#define PCIM_MSICTRL_MMC_8 0x0006 +#define PCIM_MSICTRL_MMC_16 0x0008 +#define PCIM_MSICTRL_MMC_32 0x000a +#define PCIM_MSICTRL_MSI_ENABLE 0x0001 +#define PCIR_MSI_ADDR 0x4 +#define PCIR_MSI_ADDR_HIGH 0x8 +#define PCIR_MSI_DATA 0x8 +#define PCIR_MSI_DATA_64BIT 0xc +#define PCIR_MSI_MASK 0x10 +#define PCIR_MSI_PENDING 0x14 + +/* PCI Enhanced Allocation registers */ +#define PCIR_EA_NUM_ENT 2 /* Number of Capability Entries */ +#define PCIM_EA_NUM_ENT_MASK 0x3f /* Num Entries Mask */ +#define PCIR_EA_FIRST_ENT 4 /* First EA Entry in List */ +#define PCIR_EA_FIRST_ENT_BRIDGE 8 /* First EA Entry for Bridges */ +#define PCIM_EA_ES 0x00000007 /* Entry Size */ +#define PCIM_EA_BEI 0x000000f0 /* BAR Equivalent Indicator */ +#define PCIM_EA_BEI_OFFSET 4 +/* 0-5 map to BARs 0-5 respectively */ +#define PCIM_EA_BEI_BAR_0 0 +#define PCIM_EA_BEI_BAR_5 5 +#define PCIM_EA_BEI_BAR(x) (((x) >> PCIM_EA_BEI_OFFSET) & 0xf) +#define PCIM_EA_BEI_BRIDGE 0x6 /* Resource behind bridge */ +#define PCIM_EA_BEI_ENI 0x7 /* Equivalent Not Indicated */ +#define PCIM_EA_BEI_ROM 0x8 /* Expansion ROM */ +/* 9-14 map to VF BARs 0-5 respectively */ +#define PCIM_EA_BEI_VF_BAR_0 9 +#define PCIM_EA_BEI_VF_BAR_5 14 +#define PCIM_EA_BEI_RESERVED 0xf /* Reserved - Treat like ENI */ +#define PCIM_EA_PP 0x0000ff00 /* Primary Properties */ +#define PCIM_EA_PP_OFFSET 8 +#define PCIM_EA_SP_OFFSET 16 +#define PCIM_EA_SP 0x00ff0000 /* Secondary Properties */ +#define PCIM_EA_P_MEM 0x00 /* Non-Prefetch Memory */ +#define PCIM_EA_P_MEM_PREFETCH 0x01 /* Prefetchable Memory */ +#define PCIM_EA_P_IO 0x02 /* I/O Space */ +#define PCIM_EA_P_VF_MEM_PREFETCH 0x03 /* VF Prefetchable Memory */ +#define PCIM_EA_P_VF_MEM 0x04 /* VF Non-Prefetch Memory */ +#define PCIM_EA_P_BRIDGE_MEM 0x05 /* Bridge Non-Prefetch Memory */ +#define PCIM_EA_P_BRIDGE_MEM_PREFETCH 0x06 /* Bridge Prefetchable Memory */ +#define PCIM_EA_P_BRIDGE_IO 0x07 /* Bridge I/O Space */ +/* 0x08-0xfc reserved */ +#define PCIM_EA_P_MEM_RESERVED 0xfd /* Reserved Memory */ +#define PCIM_EA_P_IO_RESERVED 0xfe /* Reserved I/O Space */ +#define PCIM_EA_P_UNAVAILABLE 0xff /* Entry Unavailable */ +#define PCIM_EA_WRITABLE 0x40000000 /* Writable: 1 = RW, 0 = HwInit */ +#define PCIM_EA_ENABLE 0x80000000 /* Enable for this entry */ +#define PCIM_EA_BASE 4 /* Base Address Offset */ +#define PCIM_EA_MAX_OFFSET 8 /* MaxOffset (resource length) */ +/* bit 0 is reserved */ +#define PCIM_EA_IS_64 0x00000002 /* 64-bit field flag */ +#define PCIM_EA_FIELD_MASK 0xfffffffc /* For Base & Max Offset */ +/* Bridge config register */ +#define PCIM_EA_SEC_NR(reg) ((reg) & 0xff) +#define PCIM_EA_SUB_NR(reg) (((reg) >> 8) & 0xff) + +/* PCI-X definitions */ + +/* For header type 0 devices */ +#define PCIXR_COMMAND 0x2 +#define PCIXM_COMMAND_DPERR_E 0x0001 /* Data Parity Error Recovery */ +#define PCIXM_COMMAND_ERO 0x0002 /* Enable Relaxed Ordering */ +#define PCIXM_COMMAND_MAX_READ 0x000c /* Maximum Burst Read Count */ +#define PCIXM_COMMAND_MAX_READ_512 0x0000 +#define PCIXM_COMMAND_MAX_READ_1024 0x0004 +#define PCIXM_COMMAND_MAX_READ_2048 0x0008 +#define PCIXM_COMMAND_MAX_READ_4096 0x000c +#define PCIXM_COMMAND_MAX_SPLITS 0x0070 /* Maximum Split Transactions */ +#define PCIXM_COMMAND_MAX_SPLITS_1 0x0000 +#define PCIXM_COMMAND_MAX_SPLITS_2 0x0010 +#define PCIXM_COMMAND_MAX_SPLITS_3 0x0020 +#define PCIXM_COMMAND_MAX_SPLITS_4 0x0030 +#define PCIXM_COMMAND_MAX_SPLITS_8 0x0040 +#define PCIXM_COMMAND_MAX_SPLITS_12 0x0050 +#define PCIXM_COMMAND_MAX_SPLITS_16 0x0060 +#define PCIXM_COMMAND_MAX_SPLITS_32 0x0070 +#define PCIXM_COMMAND_VERSION 0x3000 +#define PCIXR_STATUS 0x4 +#define PCIXM_STATUS_DEVFN 0x000000ff +#define PCIXM_STATUS_BUS 0x0000ff00 +#define PCIXM_STATUS_64BIT 0x00010000 +#define PCIXM_STATUS_133CAP 0x00020000 +#define PCIXM_STATUS_SC_DISCARDED 0x00040000 +#define PCIXM_STATUS_UNEXP_SC 0x00080000 +#define PCIXM_STATUS_COMPLEX_DEV 0x00100000 +#define PCIXM_STATUS_MAX_READ 0x00600000 +#define PCIXM_STATUS_MAX_READ_512 0x00000000 +#define PCIXM_STATUS_MAX_READ_1024 0x00200000 +#define PCIXM_STATUS_MAX_READ_2048 0x00400000 +#define PCIXM_STATUS_MAX_READ_4096 0x00600000 +#define PCIXM_STATUS_MAX_SPLITS 0x03800000 +#define PCIXM_STATUS_MAX_SPLITS_1 0x00000000 +#define PCIXM_STATUS_MAX_SPLITS_2 0x00800000 +#define PCIXM_STATUS_MAX_SPLITS_3 0x01000000 +#define PCIXM_STATUS_MAX_SPLITS_4 0x01800000 +#define PCIXM_STATUS_MAX_SPLITS_8 0x02000000 +#define PCIXM_STATUS_MAX_SPLITS_12 0x02800000 +#define PCIXM_STATUS_MAX_SPLITS_16 0x03000000 +#define PCIXM_STATUS_MAX_SPLITS_32 0x03800000 +#define PCIXM_STATUS_MAX_CUM_READ 0x1c000000 +#define PCIXM_STATUS_RCVD_SC_ERR 0x20000000 +#define PCIXM_STATUS_266CAP 0x40000000 +#define PCIXM_STATUS_533CAP 0x80000000 + +/* For header type 1 devices (PCI-X bridges) */ +#define PCIXR_SEC_STATUS 0x2 +#define PCIXM_SEC_STATUS_64BIT 0x0001 +#define PCIXM_SEC_STATUS_133CAP 0x0002 +#define PCIXM_SEC_STATUS_SC_DISC 0x0004 +#define PCIXM_SEC_STATUS_UNEXP_SC 0x0008 +#define PCIXM_SEC_STATUS_SC_OVERRUN 0x0010 +#define PCIXM_SEC_STATUS_SR_DELAYED 0x0020 +#define PCIXM_SEC_STATUS_BUS_MODE 0x03c0 +#define PCIXM_SEC_STATUS_VERSION 0x3000 +#define PCIXM_SEC_STATUS_266CAP 0x4000 +#define PCIXM_SEC_STATUS_533CAP 0x8000 +#define PCIXR_BRIDGE_STATUS 0x4 +#define PCIXM_BRIDGE_STATUS_DEVFN 0x000000ff +#define PCIXM_BRIDGE_STATUS_BUS 0x0000ff00 +#define PCIXM_BRIDGE_STATUS_64BIT 0x00010000 +#define PCIXM_BRIDGE_STATUS_133CAP 0x00020000 +#define PCIXM_BRIDGE_STATUS_SC_DISCARDED 0x00040000 +#define PCIXM_BRIDGE_STATUS_UNEXP_SC 0x00080000 +#define PCIXM_BRIDGE_STATUS_SC_OVERRUN 0x00100000 +#define PCIXM_BRIDGE_STATUS_SR_DELAYED 0x00200000 +#define PCIXM_BRIDGE_STATUS_DEVID_MSGCAP 0x20000000 +#define PCIXM_BRIDGE_STATUS_266CAP 0x40000000 +#define PCIXM_BRIDGE_STATUS_533CAP 0x80000000 + +/* HT (HyperTransport) Capability definitions */ +#define PCIR_HT_COMMAND 0x2 +#define PCIM_HTCMD_CAP_MASK 0xf800 /* Capability type. */ +#define PCIM_HTCAP_SLAVE 0x0000 /* 000xx */ +#define PCIM_HTCAP_HOST 0x2000 /* 001xx */ +#define PCIM_HTCAP_SWITCH 0x4000 /* 01000 */ +#define PCIM_HTCAP_INTERRUPT 0x8000 /* 10000 */ +#define PCIM_HTCAP_REVISION_ID 0x8800 /* 10001 */ +#define PCIM_HTCAP_UNITID_CLUMPING 0x9000 /* 10010 */ +#define PCIM_HTCAP_EXT_CONFIG_SPACE 0x9800 /* 10011 */ +#define PCIM_HTCAP_ADDRESS_MAPPING 0xa000 /* 10100 */ +#define PCIM_HTCAP_MSI_MAPPING 0xa800 /* 10101 */ +#define PCIM_HTCAP_DIRECT_ROUTE 0xb000 /* 10110 */ +#define PCIM_HTCAP_VCSET 0xb800 /* 10111 */ +#define PCIM_HTCAP_RETRY_MODE 0xc000 /* 11000 */ +#define PCIM_HTCAP_X86_ENCODING 0xc800 /* 11001 */ +#define PCIM_HTCAP_GEN3 0xd000 /* 11010 */ +#define PCIM_HTCAP_FLE 0xd800 /* 11011 */ +#define PCIM_HTCAP_PM 0xe000 /* 11100 */ +#define PCIM_HTCAP_HIGH_NODE_COUNT 0xe800 /* 11101 */ + +/* HT MSI Mapping Capability definitions. */ +#define PCIM_HTCMD_MSI_ENABLE 0x0001 +#define PCIM_HTCMD_MSI_FIXED 0x0002 +#define PCIR_HTMSI_ADDRESS_LO 0x4 +#define PCIR_HTMSI_ADDRESS_HI 0x8 + +/* PCI Vendor capability definitions */ +#define PCIR_VENDOR_LENGTH 0x2 +#define PCIR_VENDOR_DATA 0x3 + +/* PCI Device capability definitions */ +#define PCIR_DEVICE_LENGTH 0x2 + +/* PCI EHCI Debug Port definitions */ +#define PCIR_DEBUG_PORT 0x2 +#define PCIM_DEBUG_PORT_OFFSET 0x1fff +#define PCIM_DEBUG_PORT_BAR 0xe000 + +/* PCI-PCI Bridge Subvendor definitions */ +#define PCIR_SUBVENDCAP_ID 0x4 +#define PCIR_SUBVENDCAP 0x4 +#define PCIR_SUBDEVCAP 0x6 + +/* PCI Express definitions */ +#define PCIER_FLAGS 0x2 +#define PCIEM_FLAGS_VERSION 0x000f +#define PCIEM_FLAGS_TYPE 0x00f0 +#define PCIEM_TYPE_ENDPOINT 0x0000 +#define PCIEM_TYPE_LEGACY_ENDPOINT 0x0010 +#define PCIEM_TYPE_ROOT_PORT 0x0040 +#define PCIEM_TYPE_UPSTREAM_PORT 0x0050 +#define PCIEM_TYPE_DOWNSTREAM_PORT 0x0060 +#define PCIEM_TYPE_PCI_BRIDGE 0x0070 +#define PCIEM_TYPE_PCIE_BRIDGE 0x0080 +#define PCIEM_TYPE_ROOT_INT_EP 0x0090 +#define PCIEM_TYPE_ROOT_EC 0x00a0 +#define PCIEM_FLAGS_SLOT 0x0100 +#define PCIEM_FLAGS_IRQ 0x3e00 +#define PCIER_DEVICE_CAP 0x4 +#define PCIEM_CAP_MAX_PAYLOAD 0x00000007 +#define PCIEM_CAP_PHANTHOM_FUNCS 0x00000018 +#define PCIEM_CAP_EXT_TAG_FIELD 0x00000020 +#define PCIEM_CAP_L0S_LATENCY 0x000001c0 +#define PCIEM_CAP_L1_LATENCY 0x00000e00 +#define PCIEM_CAP_ROLE_ERR_RPT 0x00008000 +#define PCIEM_CAP_SLOT_PWR_LIM_VAL 0x03fc0000 +#define PCIEM_CAP_SLOT_PWR_LIM_SCALE 0x0c000000 +#define PCIEM_CAP_FLR 0x10000000 +#define PCIER_DEVICE_CTL 0x8 +#define PCIEM_CTL_COR_ENABLE 0x0001 +#define PCIEM_CTL_NFER_ENABLE 0x0002 +#define PCIEM_CTL_FER_ENABLE 0x0004 +#define PCIEM_CTL_URR_ENABLE 0x0008 +#define PCIEM_CTL_RELAXED_ORD_ENABLE 0x0010 +#define PCIEM_CTL_MAX_PAYLOAD 0x00e0 +#define PCIEM_CTL_EXT_TAG_FIELD 0x0100 +#define PCIEM_CTL_PHANTHOM_FUNCS 0x0200 +#define PCIEM_CTL_AUX_POWER_PM 0x0400 +#define PCIEM_CTL_NOSNOOP_ENABLE 0x0800 +#define PCIEM_CTL_MAX_READ_REQUEST 0x7000 +#define PCIEM_CTL_BRDG_CFG_RETRY 0x8000 /* PCI-E - PCI/PCI-X bridges */ +#define PCIEM_CTL_INITIATE_FLR 0x8000 /* FLR capable endpoints */ +#define PCIER_DEVICE_STA 0xa +#define PCIEM_STA_CORRECTABLE_ERROR 0x0001 +#define PCIEM_STA_NON_FATAL_ERROR 0x0002 +#define PCIEM_STA_FATAL_ERROR 0x0004 +#define PCIEM_STA_UNSUPPORTED_REQ 0x0008 +#define PCIEM_STA_AUX_POWER 0x0010 +#define PCIEM_STA_TRANSACTION_PND 0x0020 +#define PCIER_LINK_CAP 0xc +#define PCIEM_LINK_CAP_MAX_SPEED 0x0000000f +#define PCIEM_LINK_CAP_MAX_WIDTH 0x000003f0 +#define PCIEM_LINK_CAP_ASPM 0x00000c00 +#define PCIEM_LINK_CAP_L0S_EXIT 0x00007000 +#define PCIEM_LINK_CAP_L1_EXIT 0x00038000 +#define PCIEM_LINK_CAP_CLOCK_PM 0x00040000 +#define PCIEM_LINK_CAP_SURPRISE_DOWN 0x00080000 +#define PCIEM_LINK_CAP_DL_ACTIVE 0x00100000 +#define PCIEM_LINK_CAP_LINK_BW_NOTIFY 0x00200000 +#define PCIEM_LINK_CAP_ASPM_COMPLIANCE 0x00400000 +#define PCIEM_LINK_CAP_PORT 0xff000000 +#define PCIER_LINK_CTL 0x10 +#define PCIEM_LINK_CTL_ASPMC_DIS 0x0000 +#define PCIEM_LINK_CTL_ASPMC_L0S 0x0001 +#define PCIEM_LINK_CTL_ASPMC_L1 0x0002 +#define PCIEM_LINK_CTL_ASPMC 0x0003 +#define PCIEM_LINK_CTL_RCB 0x0008 +#define PCIEM_LINK_CTL_LINK_DIS 0x0010 +#define PCIEM_LINK_CTL_RETRAIN_LINK 0x0020 +#define PCIEM_LINK_CTL_COMMON_CLOCK 0x0040 +#define PCIEM_LINK_CTL_EXTENDED_SYNC 0x0080 +#define PCIEM_LINK_CTL_ECPM 0x0100 +#define PCIEM_LINK_CTL_HAWD 0x0200 +#define PCIEM_LINK_CTL_LBMIE 0x0400 +#define PCIEM_LINK_CTL_LABIE 0x0800 +#define PCIER_LINK_STA 0x12 +#define PCIEM_LINK_STA_SPEED 0x000f +#define PCIEM_LINK_STA_WIDTH 0x03f0 +#define PCIEM_LINK_STA_TRAINING_ERROR 0x0400 +#define PCIEM_LINK_STA_TRAINING 0x0800 +#define PCIEM_LINK_STA_SLOT_CLOCK 0x1000 +#define PCIEM_LINK_STA_DL_ACTIVE 0x2000 +#define PCIEM_LINK_STA_LINK_BW_MGMT 0x4000 +#define PCIEM_LINK_STA_LINK_AUTO_BW 0x8000 +#define PCIER_SLOT_CAP 0x14 +#define PCIEM_SLOT_CAP_APB 0x00000001 +#define PCIEM_SLOT_CAP_PCP 0x00000002 +#define PCIEM_SLOT_CAP_MRLSP 0x00000004 +#define PCIEM_SLOT_CAP_AIP 0x00000008 +#define PCIEM_SLOT_CAP_PIP 0x00000010 +#define PCIEM_SLOT_CAP_HPS 0x00000020 +#define PCIEM_SLOT_CAP_HPC 0x00000040 +#define PCIEM_SLOT_CAP_SPLV 0x00007f80 +#define PCIEM_SLOT_CAP_SPLS 0x00018000 +#define PCIEM_SLOT_CAP_EIP 0x00020000 +#define PCIEM_SLOT_CAP_NCCS 0x00040000 +#define PCIEM_SLOT_CAP_PSN 0xfff80000 +#define PCIER_SLOT_CTL 0x18 +#define PCIEM_SLOT_CTL_ABPE 0x0001 +#define PCIEM_SLOT_CTL_PFDE 0x0002 +#define PCIEM_SLOT_CTL_MRLSCE 0x0004 +#define PCIEM_SLOT_CTL_PDCE 0x0008 +#define PCIEM_SLOT_CTL_CCIE 0x0010 +#define PCIEM_SLOT_CTL_HPIE 0x0020 +#define PCIEM_SLOT_CTL_AIC 0x00c0 +#define PCIEM_SLOT_CTL_AI_ON 0x0040 +#define PCIEM_SLOT_CTL_AI_BLINK 0x0080 +#define PCIEM_SLOT_CTL_AI_OFF 0x00c0 +#define PCIEM_SLOT_CTL_PIC 0x0300 +#define PCIEM_SLOT_CTL_PI_ON 0x0100 +#define PCIEM_SLOT_CTL_PI_BLINK 0x0200 +#define PCIEM_SLOT_CTL_PI_OFF 0x0300 +#define PCIEM_SLOT_CTL_PCC 0x0400 +#define PCIEM_SLOT_CTL_PC_ON 0x0000 +#define PCIEM_SLOT_CTL_PC_OFF 0x0400 +#define PCIEM_SLOT_CTL_EIC 0x0800 +#define PCIEM_SLOT_CTL_DLLSCE 0x1000 +#define PCIER_SLOT_STA 0x1a +#define PCIEM_SLOT_STA_ABP 0x0001 +#define PCIEM_SLOT_STA_PFD 0x0002 +#define PCIEM_SLOT_STA_MRLSC 0x0004 +#define PCIEM_SLOT_STA_PDC 0x0008 +#define PCIEM_SLOT_STA_CC 0x0010 +#define PCIEM_SLOT_STA_MRLSS 0x0020 +#define PCIEM_SLOT_STA_PDS 0x0040 +#define PCIEM_SLOT_STA_EIS 0x0080 +#define PCIEM_SLOT_STA_DLLSC 0x0100 +#define PCIER_ROOT_CTL 0x1c +#define PCIEM_ROOT_CTL_SERR_CORR 0x0001 +#define PCIEM_ROOT_CTL_SERR_NONFATAL 0x0002 +#define PCIEM_ROOT_CTL_SERR_FATAL 0x0004 +#define PCIEM_ROOT_CTL_PME 0x0008 +#define PCIEM_ROOT_CTL_CRS_VIS 0x0010 +#define PCIER_ROOT_CAP 0x1e +#define PCIEM_ROOT_CAP_CRS_VIS 0x0001 +#define PCIER_ROOT_STA 0x20 +#define PCIEM_ROOT_STA_PME_REQID_MASK 0x0000ffff +#define PCIEM_ROOT_STA_PME_STATUS 0x00010000 +#define PCIEM_ROOT_STA_PME_PEND 0x00020000 +#define PCIER_DEVICE_CAP2 0x24 +#define PCIEM_CAP2_COMP_TIMO_RANGES 0x0000000f +#define PCIEM_CAP2_COMP_TIMO_RANGE_A 0x00000001 +#define PCIEM_CAP2_COMP_TIMO_RANGE_B 0x00000002 +#define PCIEM_CAP2_COMP_TIMO_RANGE_C 0x00000004 +#define PCIEM_CAP2_COMP_TIMO_RANGE_D 0x00000008 +#define PCIEM_CAP2_COMP_TIMO_DISABLE 0x00000010 +#define PCIEM_CAP2_ARI 0x00000020 +#define PCIER_DEVICE_CTL2 0x28 +#define PCIEM_CTL2_COMP_TIMO_VAL 0x000f +#define PCIEM_CTL2_COMP_TIMO_50MS 0x0000 +#define PCIEM_CTL2_COMP_TIMO_100US 0x0001 +#define PCIEM_CTL2_COMP_TIMO_10MS 0x0002 +#define PCIEM_CTL2_COMP_TIMO_55MS 0x0005 +#define PCIEM_CTL2_COMP_TIMO_210MS 0x0006 +#define PCIEM_CTL2_COMP_TIMO_900MS 0x0009 +#define PCIEM_CTL2_COMP_TIMO_3500MS 0x000a +#define PCIEM_CTL2_COMP_TIMO_13S 0x000d +#define PCIEM_CTL2_COMP_TIMO_64S 0x000e +#define PCIEM_CTL2_COMP_TIMO_DISABLE 0x0010 +#define PCIEM_CTL2_ARI 0x0020 +#define PCIEM_CTL2_ATOMIC_REQ_ENABLE 0x0040 +#define PCIEM_CTL2_ATOMIC_EGR_BLOCK 0x0080 +#define PCIEM_CTL2_ID_ORDERED_REQ_EN 0x0100 +#define PCIEM_CTL2_ID_ORDERED_CMP_EN 0x0200 +#define PCIEM_CTL2_LTR_ENABLE 0x0400 +#define PCIEM_CTL2_OBFF 0x6000 +#define PCIEM_OBFF_DISABLE 0x0000 +#define PCIEM_OBFF_MSGA_ENABLE 0x2000 +#define PCIEM_OBFF_MSGB_ENABLE 0x4000 +#define PCIEM_OBFF_WAKE_ENABLE 0x6000 +#define PCIEM_CTL2_END2END_TLP 0x8000 +#define PCIER_DEVICE_STA2 0x2a +#define PCIER_LINK_CAP2 0x2c +#define PCIER_LINK_CTL2 0x30 +#define PCIER_LINK_STA2 0x32 +#define PCIER_SLOT_CAP2 0x34 +#define PCIER_SLOT_CTL2 0x38 +#define PCIER_SLOT_STA2 0x3a + +/* MSI-X definitions */ +#define PCIR_MSIX_CTRL 0x2 +#define PCIM_MSIXCTRL_MSIX_ENABLE 0x8000 +#define PCIM_MSIXCTRL_FUNCTION_MASK 0x4000 +#define PCIM_MSIXCTRL_TABLE_SIZE 0x07ff +#define PCIR_MSIX_TABLE 0x4 +#define PCIR_MSIX_PBA 0x8 +#define PCIM_MSIX_BIR_MASK 0x7 +#define PCIM_MSIX_BIR_BAR_10 0 +#define PCIM_MSIX_BIR_BAR_14 1 +#define PCIM_MSIX_BIR_BAR_18 2 +#define PCIM_MSIX_BIR_BAR_1C 3 +#define PCIM_MSIX_BIR_BAR_20 4 +#define PCIM_MSIX_BIR_BAR_24 5 +#define PCIM_MSIX_VCTRL_MASK 0x1 + +/* PCI Advanced Features definitions */ +#define PCIR_PCIAF_CAP 0x3 +#define PCIM_PCIAFCAP_TP 0x01 +#define PCIM_PCIAFCAP_FLR 0x02 +#define PCIR_PCIAF_CTRL 0x4 +#define PCIR_PCIAFCTRL_FLR 0x01 +#define PCIR_PCIAF_STATUS 0x5 +#define PCIR_PCIAFSTATUS_TP 0x01 + +/* Advanced Error Reporting */ +#define PCIR_AER_UC_STATUS 0x04 +#define PCIM_AER_UC_TRAINING_ERROR 0x00000001 +#define PCIM_AER_UC_DL_PROTOCOL_ERROR 0x00000010 +#define PCIM_AER_UC_SURPRISE_LINK_DOWN 0x00000020 +#define PCIM_AER_UC_POISONED_TLP 0x00001000 +#define PCIM_AER_UC_FC_PROTOCOL_ERROR 0x00002000 +#define PCIM_AER_UC_COMPLETION_TIMEOUT 0x00004000 +#define PCIM_AER_UC_COMPLETER_ABORT 0x00008000 +#define PCIM_AER_UC_UNEXPECTED_COMPLETION 0x00010000 +#define PCIM_AER_UC_RECEIVER_OVERFLOW 0x00020000 +#define PCIM_AER_UC_MALFORMED_TLP 0x00040000 +#define PCIM_AER_UC_ECRC_ERROR 0x00080000 +#define PCIM_AER_UC_UNSUPPORTED_REQUEST 0x00100000 +#define PCIM_AER_UC_ACS_VIOLATION 0x00200000 +#define PCIM_AER_UC_INTERNAL_ERROR 0x00400000 +#define PCIM_AER_UC_MC_BLOCKED_TLP 0x00800000 +#define PCIM_AER_UC_ATOMIC_EGRESS_BLK 0x01000000 +#define PCIM_AER_UC_TLP_PREFIX_BLOCKED 0x02000000 +#define PCIR_AER_UC_MASK 0x08 /* Shares bits with UC_STATUS */ +#define PCIR_AER_UC_SEVERITY 0x0c /* Shares bits with UC_STATUS */ +#define PCIR_AER_COR_STATUS 0x10 +#define PCIM_AER_COR_RECEIVER_ERROR 0x00000001 +#define PCIM_AER_COR_BAD_TLP 0x00000040 +#define PCIM_AER_COR_BAD_DLLP 0x00000080 +#define PCIM_AER_COR_REPLAY_ROLLOVER 0x00000100 +#define PCIM_AER_COR_REPLAY_TIMEOUT 0x00001000 +#define PCIM_AER_COR_ADVISORY_NF_ERROR 0x00002000 +#define PCIM_AER_COR_INTERNAL_ERROR 0x00004000 +#define PCIM_AER_COR_HEADER_LOG_OVFLOW 0x00008000 +#define PCIR_AER_COR_MASK 0x14 /* Shares bits with COR_STATUS */ +#define PCIR_AER_CAP_CONTROL 0x18 +#define PCIM_AER_FIRST_ERROR_PTR 0x0000001f +#define PCIM_AER_ECRC_GEN_CAPABLE 0x00000020 +#define PCIM_AER_ECRC_GEN_ENABLE 0x00000040 +#define PCIM_AER_ECRC_CHECK_CAPABLE 0x00000080 +#define PCIM_AER_ECRC_CHECK_ENABLE 0x00000100 +#define PCIM_AER_MULT_HDR_CAPABLE 0x00000200 +#define PCIM_AER_MULT_HDR_ENABLE 0x00000400 +#define PCIM_AER_TLP_PREFIX_LOG_PRESENT 0x00000800 +#define PCIR_AER_HEADER_LOG 0x1c +#define PCIR_AER_ROOTERR_CMD 0x2c /* Only for root complex ports */ +#define PCIM_AER_ROOTERR_COR_ENABLE 0x00000001 +#define PCIM_AER_ROOTERR_NF_ENABLE 0x00000002 +#define PCIM_AER_ROOTERR_F_ENABLE 0x00000004 +#define PCIR_AER_ROOTERR_STATUS 0x30 /* Only for root complex ports */ +#define PCIM_AER_ROOTERR_COR_ERR 0x00000001 +#define PCIM_AER_ROOTERR_MULTI_COR_ERR 0x00000002 +#define PCIM_AER_ROOTERR_UC_ERR 0x00000004 +#define PCIM_AER_ROOTERR_MULTI_UC_ERR 0x00000008 +#define PCIM_AER_ROOTERR_FIRST_UC_FATAL 0x00000010 +#define PCIM_AER_ROOTERR_NF_ERR 0x00000020 +#define PCIM_AER_ROOTERR_F_ERR 0x00000040 +#define PCIM_AER_ROOTERR_INT_MESSAGE 0xf8000000 +#define PCIR_AER_COR_SOURCE_ID 0x34 /* Only for root complex ports */ +#define PCIR_AER_ERR_SOURCE_ID 0x36 /* Only for root complex ports */ +#define PCIR_AER_TLP_PREFIX_LOG 0x38 /* Only for TLP prefix functions */ + +/* Virtual Channel definitions */ +#define PCIR_VC_CAP1 0x04 +#define PCIM_VC_CAP1_EXT_COUNT 0x00000007 +#define PCIM_VC_CAP1_LOWPRI_EXT_COUNT 0x00000070 +#define PCIR_VC_CAP2 0x08 +#define PCIR_VC_CONTROL 0x0c +#define PCIR_VC_STATUS 0x0e +#define PCIR_VC_RESOURCE_CAP(n) (0x10 + (n) * 0x0c) +#define PCIR_VC_RESOURCE_CTL(n) (0x14 + (n) * 0x0c) +#define PCIR_VC_RESOURCE_STA(n) (0x18 + (n) * 0x0c) + +/* Serial Number definitions */ +#define PCIR_SERIAL_LOW 0x04 +#define PCIR_SERIAL_HIGH 0x08 + +/* SR-IOV definitions */ +#define PCIR_SRIOV_CTL 0x08 +#define PCIM_SRIOV_VF_EN 0x01 +#define PCIM_SRIOV_VF_MSE 0x08 /* Memory space enable. */ +#define PCIM_SRIOV_ARI_EN 0x10 +#define PCIR_SRIOV_TOTAL_VFS 0x0e +#define PCIR_SRIOV_NUM_VFS 0x10 +#define PCIR_SRIOV_VF_OFF 0x14 +#define PCIR_SRIOV_VF_STRIDE 0x16 +#define PCIR_SRIOV_VF_DID 0x1a +#define PCIR_SRIOV_PAGE_CAP 0x1c +#define PCIR_SRIOV_PAGE_SIZE 0x20 + +#define PCI_SRIOV_BASE_PAGE_SHIFT 12 + +#define PCIR_SRIOV_BARS 0x24 +#define PCIR_SRIOV_BAR(x) (PCIR_SRIOV_BARS + (x) * 4) + +/* Extended Capability Vendor-Specific definitions */ +#define PCIR_VSEC_HEADER 0x04 +#define PCIR_VSEC_ID(hdr) ((hdr) & 0xffff) +#define PCIR_VSEC_REV(hdr) (((hdr) & 0xf0000) >> 16) +#define PCIR_VSEC_LENGTH(hdr) (((hdr) & 0xfff00000) >> 20) +#define PCIR_VSEC_DATA 0x08 + +/* + * PCI Express Firmware Interface definitions + */ +#define PCI_OSC_STATUS 0 +#define PCI_OSC_SUPPORT 1 +#define PCIM_OSC_SUPPORT_EXT_PCI_CONF 0x01 /* Extended PCI Config Space */ +#define PCIM_OSC_SUPPORT_ASPM 0x02 /* Active State Power Management */ +#define PCIM_OSC_SUPPORT_CPMC 0x04 /* Clock Power Management Cap */ +#define PCIM_OSC_SUPPORT_SEG_GROUP 0x08 /* PCI Segment Groups supported */ +#define PCIM_OSC_SUPPORT_MSI 0x10 /* MSI signalling supported */ +#define PCI_OSC_CTL 2 +#define PCIM_OSC_CTL_PCIE_HP 0x01 /* PCIe Native Hot Plug */ +#define PCIM_OSC_CTL_SHPC_HP 0x02 /* SHPC Native Hot Plug */ +#define PCIM_OSC_CTL_PCIE_PME 0x04 /* PCIe Native Power Mgt Events */ +#define PCIM_OSC_CTL_PCIE_AER 0x08 /* PCIe Advanced Error Reporting */ +#define PCIM_OSC_CTL_PCIE_CAP_STRUCT 0x10 /* Various Capability Structures */ + +#endif /* __PCI_REGS_H__ */ diff --git a/components/drivers/pci/probe.c b/components/drivers/pci/probe.c new file mode 100644 index 000000000000..eedb4e519210 --- /dev/null +++ b/components/drivers/pci/probe.c @@ -0,0 +1,488 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-10-24 GuEe-GUI first version + */ + +#include + +#define DBG_TAG "pci.probe" +#define DBG_LVL DBG_INFO +#include + +#include + +struct rt_pci_host_bridge *rt_pci_host_bridge_alloc(rt_size_t priv_size) +{ + struct rt_pci_host_bridge *bridge = rt_calloc(1, sizeof(*bridge) + priv_size); + + return bridge; +} + +rt_err_t rt_pci_host_bridge_init(struct rt_pci_host_bridge *host_bridge) +{ + rt_err_t err = RT_EOK; + + if (host_bridge->parent.ofw_node) + { + err = rt_pci_ofw_host_bridge_init(host_bridge->parent.ofw_node, host_bridge); + } + + return err; +} + +struct rt_pci_device *rt_pci_alloc_device(struct rt_pci_bus *bus) +{ + struct rt_pci_device *pdev = rt_calloc(1, sizeof(*pdev)); + + if (!pdev) + { + return RT_NULL; + } + + rt_list_init(&pdev->list_in_bus); + pdev->bus = bus; + + if (bus) + { + rt_list_insert_before(&bus->devices_nodes, &pdev->list_in_bus); + } + + pdev->subsystem_vendor = PCI_ANY_ID; + pdev->subsystem_device = PCI_ANY_ID; + + pdev->irq = -1; + + for (int i = 0; i < RT_ARRAY_SIZE(pdev->resource); ++i) + { + pdev->resource[i].flags = PCI_BUS_REGION_F_NONE; + } + +#ifdef RT_PCI_MSI + rt_list_init(&pdev->msi_desc_nodes); + rt_spin_lock_init(&pdev->msi_lock); +#endif + + return pdev; +} + +struct rt_pci_device *rt_pci_scan_single_device(struct rt_pci_bus *bus, rt_uint32_t devfn) +{ + struct rt_pci_device *pdev = RT_NULL; + rt_uint16_t vendor = PCI_ANY_ID, device = PCI_ANY_ID; + + if (!bus) + { + goto _end; + } + + rt_pci_bus_read_config_u16(bus, devfn, PCIR_VENDOR, &vendor); + rt_pci_bus_read_config_u16(bus, devfn, PCIR_DEVICE, &device); + + if (vendor == (typeof(vendor))PCI_ANY_ID) + { + goto _end; + } + + if (!(pdev = rt_pci_alloc_device(bus))) + { + goto _end; + } + + pdev->devfn = devfn; + pdev->vendor = vendor; + pdev->device = device; + + rt_dm_dev_set_name(&pdev->parent, "%04x:%02x:%02x.%d", + rt_pci_domain(pdev), pdev->bus->number, RT_PCI_SLOT(pdev->devfn), RT_PCI_FUNC(pdev->devfn)); + + if (rt_pci_setup_device(pdev)) + { + rt_free(pdev); + pdev = RT_NULL; + + goto _end; + } + + rt_pci_device_register(pdev); + +_end: + return pdev; +} + +static rt_bool_t pci_intx_mask_broken(struct rt_pci_device *pdev) +{ + rt_bool_t res = RT_FALSE; + rt_uint16_t orig, toggle, new; + + rt_pci_read_config_u16(pdev, PCIR_COMMAND, &orig); + toggle = orig ^ PCIM_CMD_INTxDIS; + rt_pci_write_config_u16(pdev, PCIR_COMMAND, toggle); + rt_pci_read_config_u16(pdev, PCIR_COMMAND, &new); + + rt_pci_write_config_u16(pdev, PCIR_COMMAND, orig); + + if (new != toggle) + { + res = RT_TRUE; + } + + return res; +} + +static void pci_read_irq(struct rt_pci_device *pdev) +{ + rt_uint8_t irq = 0; + + rt_pci_read_config_u8(pdev, PCIR_INTPIN, &irq); + pdev->pin = irq; + + if (irq) + { + rt_pci_read_config_u8(pdev, PCIR_INTLINE, &irq); + } + pdev->irq = irq; +} + +static void pcie_set_port_type(struct rt_pci_device *pdev) +{ + int pos; + + if (!(pos = rt_pci_find_capability(pdev, PCIY_EXPRESS))) + { + return; + } + + pdev->pcie_cap = pos; +} + +static void pci_init_capabilities(struct rt_pci_device *pdev) +{ +#ifdef RT_PCI_MSI + rt_pci_msi_init(pdev); /* Disable MSI */ + rt_pci_msix_init(pdev); /* Disable MSI-X */ +#endif + + pcie_set_port_type(pdev); + + rt_pci_device_clear_flag(pdev, PCI_F_NO_MSI); + rt_pci_device_clear_flag(pdev, PCI_F_MSI); + rt_pci_device_clear_flag(pdev, PCI_F_MSIX); +} + +rt_err_t rt_pci_setup_device(struct rt_pci_device *pdev) +{ + rt_uint8_t pos; + rt_uint32_t class = 0; + struct rt_pci_host_bridge *host_bridge; + + if (!pdev) + { + return -RT_EINVAL; + } + + if (!(host_bridge = rt_pci_find_host_bridge(pdev->bus))) + { + return -RT_EINVAL; + } + + rt_pci_ofw_device_init(pdev); + + rt_pci_read_config_u32(pdev, PCIR_REVID, &class); + + pdev->revision = class & 0xff; + pdev->class = class >> 8; /* Upper 3 bytes */ + rt_pci_read_config_u8(pdev, PCIR_HDRTYPE, &pdev->hdr_type); + + /* Clear errors left from system firmware */ + rt_pci_write_config_u16(pdev, PCIR_STATUS, 0xffff); + + if (pdev->hdr_type & 0x80) + { + rt_pci_device_set_flag(pdev, PCI_F_MULTI_FUNCTION); + } + pdev->hdr_type &= PCIM_HDRTYPE; + + if (pci_intx_mask_broken(pdev)) + { + rt_pci_device_set_flag(pdev, PCI_F_BROKEN_INTX_MASKING); + } + + rt_dm_dev_set_name(&pdev->parent, "%04x:%02x:%02x.%d", rt_pci_domain(pdev), + pdev->bus->number, RT_PCI_SLOT(pdev->devfn), RT_PCI_FUNC(pdev->devfn)); + + switch (pdev->hdr_type) + { + case PCIM_HDRTYPE_NORMAL: + if (class == PCIS_BRIDGE_PCI) + { + goto error; + } + pci_read_irq(pdev); + rt_pci_device_alloc_resource(host_bridge, pdev); + rt_pci_read_config_u16(pdev, PCIR_SUBVEND_0, &pdev->subsystem_vendor); + rt_pci_read_config_u16(pdev, PCIR_SUBDEV_0, &pdev->subsystem_device); + break; + + case PCIM_HDRTYPE_BRIDGE: + pci_read_irq(pdev); + rt_pci_device_alloc_resource(host_bridge, pdev); + pos = rt_pci_find_capability(pdev, PCIY_SUBVENDOR); + if (pos) + { + rt_pci_read_config_u16(pdev, PCIR_SUBVENDCAP, &pdev->subsystem_vendor); + rt_pci_read_config_u16(pdev, PCIR_SUBDEVCAP, &pdev->subsystem_device); + } + break; + + case PCIM_HDRTYPE_CARDBUS: + if (class != PCIS_BRIDGE_CARDBUS) + { + goto error; + } + pci_read_irq(pdev); + rt_pci_device_alloc_resource(host_bridge, pdev); + rt_pci_read_config_u16(pdev, PCIR_SUBVEND_2, &pdev->subsystem_vendor); + rt_pci_read_config_u16(pdev, PCIR_SUBDEV_2, &pdev->subsystem_device); + break; + + default: + LOG_E("Ignoring device unknown header type %02x", pdev->hdr_type); + return -RT_EIO; + + error: + LOG_E("Ignoring class %08x (doesn't match header type %02x)", pdev->class, pdev->hdr_type); + pdev->class = PCIC_NOT_DEFINED << 8; + } + + pci_init_capabilities(pdev); + + return RT_EOK; +} + +static rt_uint32_t pci_scan_bridge_extend(struct rt_pci_bus *bus, struct rt_pci_device *pdev, + rt_uint32_t bus_no_start, rt_uint32_t buses, rt_bool_t reconfigured) +{ + rt_bool_t broken = RT_FALSE, is_cardbus; + rt_uint8_t primary, secondary, subordinate; + rt_uint32_t value, bus_no = bus_no_start, next_busnr; + + is_cardbus = (pdev->hdr_type == PCIM_HDRTYPE_CARDBUS); + + rt_pci_read_config_u32(pdev, PCIR_PRIBUS_1, &value); + primary = value & 0xff; + secondary = (value >> 8) & 0xff; + subordinate = (value >> 16) & 0xff; + + LOG_D("Scanning [bus %02x-%02x] behind bridge, reconfigured %d", + secondary, subordinate, reconfigured); + + if (!primary && (primary != bus->number) && secondary && subordinate) + { + LOG_W("Primary bus is hard wired to 0"); + + primary = bus->number; + } + + /* Check if setup is sensible at all */ + if (!reconfigured && (primary != bus->number || secondary <= bus->number || + secondary > subordinate)) + { + LOG_I("Bridge configuration invalid ([bus %02x-%02x]), reconfiguring", + secondary, subordinate); + + broken = RT_TRUE; + } + + if ((secondary || subordinate) && !is_cardbus && !broken) + { + next_busnr = secondary; + } + else + { + next_busnr = bus_no_start + 1; + } + + LOG_I("Found: PCI %sBus %04x:%02x", is_cardbus ? "Card" : "", + rt_pci_domain(pdev), next_busnr); + + /* + * We should init bridge here, but the PCI bridges are always used in the PC + * servers. We just output the bridge information to develop. + */ + + return bus_no; +} + +rt_uint32_t rt_pci_scan_bridge(struct rt_pci_bus *bus, struct rt_pci_device *pdev, + rt_uint32_t bus_no_start, rt_bool_t reconfigured) +{ + if (!bus || !pdev) + { + return RT_UINT32_MAX; + } + + return pci_scan_bridge_extend(bus, pdev, bus_no_start, 0, reconfigured); +} + +rt_size_t rt_pci_scan_slot(struct rt_pci_bus *bus, rt_uint32_t devfn) +{ + rt_size_t nr = 0; + + if (!bus) + { + goto _end; + } + + for (int func = 0; func < RT_PCI_FUNCTION_MAX; ++func, ++devfn) + { + struct rt_pci_device *pdev = rt_pci_scan_single_device(bus, devfn); + + if (pdev) + { + ++nr; + + /* If this is a single function device, don't scan past the first function. */ + if (!rt_pci_device_test_flag(pdev, PCI_F_MULTI_FUNCTION)) + { + if (func > 0) + { + rt_pci_device_set_flag(pdev, PCI_F_MULTI_FUNCTION); + } + else + { + break; + } + } + } + else if (func == 0) + { + break; + } + } + +_end: + return nr; +} + +rt_uint32_t rt_pci_scan_child_buses(struct rt_pci_bus *bus, rt_size_t buses) +{ + rt_uint32_t bus_no; + struct rt_pci_device *pdev = RT_NULL; + + if (!bus) + { + bus_no = RT_UINT32_MAX; + + goto _end; + } + + bus_no = bus->number; + + for (rt_uint32_t devfn = 0; + devfn < RT_PCI_DEVFN(RT_PCI_DEVICE_MAX - 1, RT_PCI_FUNCTION_MAX - 1); + devfn += RT_PCI_FUNCTION_MAX) + { + rt_pci_scan_slot(bus, devfn); + } + + rt_pci_foreach_bridge(pdev, bus) + { + int offset; + + bus_no = pci_scan_bridge_extend(bus, pdev, bus_no, buses, RT_FALSE); + offset = bus_no - bus->number; + + if (buses > offset) + { + buses -= offset; + } + else + { + break; + } + } + +_end: + return bus_no; +} + +rt_uint32_t rt_pci_scan_child_bus(struct rt_pci_bus *bus) +{ + return rt_pci_scan_child_buses(bus, 0); +} + +static struct rt_pci_bus *pci_alloc_bus(struct rt_pci_bus *parent) +{ + struct rt_pci_bus *bus = rt_calloc(1, sizeof(*bus)); + + if (!bus) + { + return RT_NULL; + } + + bus->parent = parent; + + rt_list_init(&bus->list); + rt_list_init(&bus->children_nodes); + rt_list_init(&bus->devices_nodes); + + return bus; +} + +rt_err_t rt_pci_host_bridge_register(struct rt_pci_host_bridge *host_bridge) +{ + struct rt_pci_bus *bus = pci_alloc_bus(RT_NULL); + + if (!bus) + { + return -RT_ENOMEM; + } + + host_bridge->root_bus = bus; + + bus->sysdata = host_bridge->sysdata; + bus->host_bridge = host_bridge; + bus->ops = host_bridge->ops; + + rt_sprintf(bus->name, "%04x:%02x", host_bridge->domain, host_bridge->busnr); + + if (bus->ops->add) + { + rt_err_t err = bus->ops->add(bus); + + if (err) + { + LOG_E("PCI-Bus<%s> add bus failed with err = %s", bus->name, rt_strerror(err)); + } + } + + return RT_EOK; +} + +rt_err_t rt_pci_scan_root_bus_bridge(struct rt_pci_host_bridge *host_bridge) +{ + rt_err_t err; + + err = rt_pci_host_bridge_register(host_bridge); + + if (!err) + { + rt_pci_scan_child_bus(host_bridge->root_bus); + } + + return err; +} + +rt_err_t rt_pci_host_bridge_probe(struct rt_pci_host_bridge *host_bridge) +{ + rt_err_t err; + + err = rt_pci_scan_root_bus_bridge(host_bridge); + + return err; +} diff --git a/components/drivers/phy/Kconfig b/components/drivers/phy/Kconfig new file mode 100644 index 000000000000..96d33a75f4ca --- /dev/null +++ b/components/drivers/phy/Kconfig @@ -0,0 +1,7 @@ +menuconfig RT_USING_PHY + bool "Using Port Physical Layer (PHY) device drivers" + default n + +if RT_USING_DM && RT_USING_PHY +source "$RTT_DIR/components/drivers/phy/rockchip/Kconfig" +endif diff --git a/components/drivers/phy/SConscript b/components/drivers/phy/SConscript index 5b6effc98ab5..c5314bd199f2 100644 --- a/components/drivers/phy/SConscript +++ b/components/drivers/phy/SConscript @@ -1,8 +1,26 @@ from building import * -cwd = GetCurrentDir() -src = Glob('*.c') +group = [] +objs = [] + +if not GetDepend(['RT_USING_PHY']): + Return('group') + +cwd = GetCurrentDir() +list = os.listdir(cwd) CPPPATH = [cwd + '/../include'] -group = DefineGroup('DeviceDrivers', src, depend = ['RT_USING_PHY'], CPPPATH = CPPPATH) -Return('group') +src = ['phy.c'] + +if GetDepend(['RT_USING_DM']): + src += ['phy_dm.c'] + +group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) + +for d in list: + path = os.path.join(cwd, d) + if os.path.isfile(os.path.join(path, 'SConscript')): + objs = objs + SConscript(os.path.join(d, 'SConscript')) +objs = objs + group + +Return('objs') diff --git a/components/drivers/phy/phy.c b/components/drivers/phy/phy.c index 363b483c589e..1a24cd509093 100644 --- a/components/drivers/phy/phy.c +++ b/components/drivers/phy/phy.c @@ -74,3 +74,43 @@ rt_err_t rt_hw_phy_register(struct rt_phy_device *phy, const char *name) return ret; } + +rt_phy_status rt_phy_init(struct rt_phy_device *phy, void *object, rt_uint32_t phy_addr, rt_uint32_t src_clock_hz) +{ + if (phy->ops->init) + { + return phy->ops->init(phy, object, phy_addr, src_clock_hz); + } + + return PHY_STATUS_OK; +} + +rt_phy_status rt_phy_exit(struct rt_phy_device *phy, void *object, rt_uint32_t phy_addr) +{ + if (phy->ops->exit) + { + return phy->ops->exit(phy, object, phy_addr); + } + + return PHY_STATUS_OK; +} + +rt_phy_status rt_phy_power_on(struct rt_phy_device *phy) +{ + if (phy->ops->power_on) + { + return phy->ops->power_on(phy); + } + + return PHY_STATUS_OK; +} + +rt_phy_status rt_phy_power_off(struct rt_phy_device *phy) +{ + if (phy->ops->power_off) + { + return phy->ops->power_off(phy); + } + + return PHY_STATUS_OK; +} diff --git a/components/drivers/phy/phy_dm.c b/components/drivers/phy/phy_dm.c new file mode 100644 index 000000000000..83cb82a503c9 --- /dev/null +++ b/components/drivers/phy/phy_dm.c @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-10-24 GuEe-GUI first version + */ + +#include "phy_dm.h" + +#ifdef RT_USING_OFW +static struct rt_phy_device *ofw_phy_get_by_index(struct rt_ofw_node *np, int index) +{ + struct rt_ofw_cell_args phy_args; + struct rt_phy_device *phy_dev = RT_NULL; + + if (!rt_ofw_parse_phandle_cells(np, "phys", "#phy-cells", index, &phy_args)) + { + phy_dev = rt_ofw_data(phy_args.data); + + rt_ofw_node_put(phy_args.data); + + if (phy_dev) + { + if (phy_dev->ops->ofw_parse) + { + phy_dev->ops->ofw_parse(phy_dev, &phy_args); + } + } + } + + return phy_dev; +} +#else +static struct rt_phy_device *ofw_phy_get_by_index(struct rt_ofw_node *np, int index) +{ + return RT_NULL; +} +#endif /* RT_USING_OFW */ + +struct rt_phy_device *rt_phy_get_by_index(struct rt_device *dev, int index) +{ + struct rt_phy_device *phy_dev; + + if (!dev || index < 0) + { + return RT_NULL; + } + + if (dev->ofw_node) + { + phy_dev = ofw_phy_get_by_index(dev->ofw_node, index); + } + + return phy_dev; +} + +struct rt_phy_device *rt_phy_get_by_name(struct rt_device *dev, const char *id) +{ + int index; + + if (!dev || !id) + { + return RT_NULL; + } + + index = rt_dm_dev_prop_index_of_string(dev, "phy-names", id); + + return rt_phy_get_by_index(dev, index); +} diff --git a/components/drivers/phy/phy_dm.h b/components/drivers/phy/phy_dm.h new file mode 100644 index 000000000000..b67ab6a2a40a --- /dev/null +++ b/components/drivers/phy/phy_dm.h @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-10-24 GuEe-GUI first version + */ + +#ifndef __PHY_DM_H__ +#define __PHY_DM_H__ + +#include +#include +#include + +#endif /* __PHY_DM_H__ */ diff --git a/components/drivers/phy/rockchip/Kconfig b/components/drivers/phy/rockchip/Kconfig new file mode 100644 index 000000000000..d9de90bcb2de --- /dev/null +++ b/components/drivers/phy/rockchip/Kconfig @@ -0,0 +1,9 @@ +config RT_PHY_ROCKCHIP_NANENG_COMBO + bool "Rockchip NANENG COMBO PHY Driver" + select RT_MFD_SYSCON + default n + +config RT_PHY_ROCKCHIP_SNPS_PCIE3 + bool "Rockchip Snps PCIe3 PHY Driver" + select RT_MFD_SYSCON + default n diff --git a/components/drivers/phy/rockchip/SConscript b/components/drivers/phy/rockchip/SConscript new file mode 100644 index 000000000000..7daf98d914df --- /dev/null +++ b/components/drivers/phy/rockchip/SConscript @@ -0,0 +1,17 @@ +from building import * + +group = [] +cwd = GetCurrentDir() +CPPPATH = [cwd + '/../../include'] + +src = [] + +if GetDepend(['RT_PHY_ROCKCHIP_NANENG_COMBO']): + src += ['phy-rockchip-naneng-combphy.c'] + +if GetDepend(['RT_PHY_ROCKCHIP_SNPS_PCIE3']): + src += ['phy-rockchip-snps-pcie3.c'] + +group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') diff --git a/components/drivers/phy/rockchip/phy-rockchip-naneng-combphy.c b/components/drivers/phy/rockchip/phy-rockchip-naneng-combphy.c new file mode 100644 index 000000000000..6dd9b0b11c0f --- /dev/null +++ b/components/drivers/phy/rockchip/phy-rockchip-naneng-combphy.c @@ -0,0 +1,854 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-10-24 GuEe-GUI first version + */ + +#include +#include + +#include "../phy_dm.h" + +#define DBG_TAG "phy.rockchip.naneng-combphy" +#define DBG_LVL DBG_INFO +#include + +#define HZ_PER_MHZ 1000000UL +#define BIT_WRITEABLE_SHIFT 16 +#define REF_CLOCK_24MHz (24 * HZ_PER_MHZ) +#define REF_CLOCK_25MHz (25 * HZ_PER_MHZ) +#define REF_CLOCK_100MHz (100 * HZ_PER_MHZ) + +/* COMBO PHY REG */ +#define PHYREG6 0x14 +#define PHYREG6_PLL_DIV_MASK RT_GENMASK(7, 6) +#define PHYREG6_PLL_DIV_SHIFT 6 +#define PHYREG6_PLL_DIV_2 1 + +#define PHYREG7 0x18 +#define PHYREG7_TX_RTERM_MASK RT_GENMASK(7, 4) +#define PHYREG7_TX_RTERM_SHIFT 4 +#define PHYREG7_TX_RTERM_50OHM 8 +#define PHYREG7_RX_RTERM_MASK RT_GENMASK(3, 0) +#define PHYREG7_RX_RTERM_SHIFT 0 +#define PHYREG7_RX_RTERM_44OHM 15 + +#define PHYREG8 0x1c +#define PHYREG8_SSC_EN RT_BIT(4) + +#define PHYREG11 0x28 +#define PHYREG11_SU_TRIM_0_7 0xf0 + +#define PHYREG12 0x2c +#define PHYREG12_PLL_LPF_ADJ_VALUE 4 + +#define PHYREG13 0x30 +#define PHYREG13_RESISTER_MASK RT_GENMASK(5, 4) +#define PHYREG13_RESISTER_SHIFT 0x4 +#define PHYREG13_RESISTER_HIGH_Z 3 +#define PHYREG13_CKRCV_AMP0 RT_BIT(7) + +#define PHYREG14 0x34 +#define PHYREG14_CKRCV_AMP1 RT_BIT(0) + +#define PHYREG15 0x38 +#define PHYREG15_CTLE_EN RT_BIT(0) +#define PHYREG15_SSC_CNT_MASK RT_GENMASK(7, 6) +#define PHYREG15_SSC_CNT_SHIFT 6 +#define PHYREG15_SSC_CNT_VALUE 1 + +#define PHYREG16 0x3c +#define PHYREG16_SSC_CNT_VALUE 0x5f + +#define PHYREG18 0x44 +#define PHYREG18_PLL_LOOP 0x32 + +#define PHYREG27 0x6c +#define PHYREG27_RX_TRIM_RK3588 0x4c + +#define PHYREG32 0x7c +#define PHYREG32_SSC_MASK RT_GENMASK(7, 4) +#define PHYREG32_SSC_DIR_SHIFT 4 +#define PHYREG32_SSC_UPWARD 0 +#define PHYREG32_SSC_DOWNWARD 1 +#define PHYREG32_SSC_OFFSET_SHIFT 6 +#define PHYREG32_SSC_OFFSET_500PPM 1 + +#define PHYREG33 0x80 +#define PHYREG33_PLL_KVCO_MASK RT_GENMASK(4, 2) +#define PHYREG33_PLL_KVCO_SHIFT 2 +#define PHYREG33_PLL_KVCO_VALUE 2 + +struct rockchip_combphy; + +struct combphy_reg +{ + rt_uint16_t offset; + rt_uint16_t bitend; + rt_uint16_t bitstart; + rt_uint16_t disable; + rt_uint16_t enable; +}; + +struct rockchip_combphy_grfcfg +{ + struct combphy_reg pcie_mode_set; + struct combphy_reg usb_mode_set; + struct combphy_reg sgmii_mode_set; + struct combphy_reg qsgmii_mode_set; + struct combphy_reg pipe_rxterm_set; + struct combphy_reg pipe_txelec_set; + struct combphy_reg pipe_txcomp_set; + struct combphy_reg pipe_clk_25m; + struct combphy_reg pipe_clk_100m; + struct combphy_reg pipe_phymode_sel; + struct combphy_reg pipe_rate_sel; + struct combphy_reg pipe_rxterm_sel; + struct combphy_reg pipe_txelec_sel; + struct combphy_reg pipe_txcomp_sel; + struct combphy_reg pipe_clk_ext; + struct combphy_reg pipe_sel_usb; + struct combphy_reg pipe_sel_qsgmii; + struct combphy_reg pipe_phy_status; + struct combphy_reg con0_for_pcie; + struct combphy_reg con1_for_pcie; + struct combphy_reg con2_for_pcie; + struct combphy_reg con3_for_pcie; + struct combphy_reg con0_for_sata; + struct combphy_reg con1_for_sata; + struct combphy_reg con2_for_sata; + struct combphy_reg con3_for_sata; + struct combphy_reg pipe_con0_for_sata; + struct combphy_reg pipe_con1_for_sata; + struct combphy_reg pipe_xpcs_phy_ready; + struct combphy_reg pipe_pcie1l0_sel; + struct combphy_reg pipe_pcie1l1_sel; +}; + +struct rockchip_combphy_cfg +{ + const struct rockchip_combphy_grfcfg *grfcfg; + rt_err_t (*combphy_cfg)(struct rockchip_combphy *rk_cphy); +}; + +struct rockchip_combphy +{ + struct rt_phy_device parent; + + void *regs; + rt_uint8_t type; + rt_bool_t enable_ssc; + rt_bool_t ext_refclk; + + struct rt_syscon *pipe_grf; + struct rt_syscon *phy_grf; + struct rt_reset_control *rstc; + struct rt_clk *refclk; + struct rt_clk_array *clk_arr; + const struct rockchip_combphy_cfg *cfg; +}; + +#define raw_to_rockchip_combphy(raw) rt_container_of(raw, struct rockchip_combphy, parent) + +static void rockchip_combphy_updatel(struct rockchip_combphy *rk_cphy, + int mask, int val, int offset) +{ + rt_uint32_t data; + + data = HWREG32(rk_cphy->regs + offset); + data = (data & ~(mask)) | val; + HWREG32(rk_cphy->regs + offset) = data; +} + +static rt_err_t rockchip_combphy_param_write(struct rt_syscon *regmap, + const struct combphy_reg *reg, bool en) +{ + rt_uint32_t val, mask, tmp; + + tmp = en ? reg->enable : reg->disable; + mask = RT_GENMASK(reg->bitend, reg->bitstart); + val = (tmp << reg->bitstart) | (mask << BIT_WRITEABLE_SHIFT); + + return rt_syscon_write(regmap, reg->offset, val); +} + +static rt_uint32_t rockchip_combphy_is_ready(struct rockchip_combphy *rk_cphy) +{ + rt_uint32_t mask, val; + const struct rockchip_combphy_grfcfg *cfg = rk_cphy->cfg->grfcfg; + + mask = RT_GENMASK(cfg->pipe_phy_status.bitend, cfg->pipe_phy_status.bitstart); + + rt_syscon_read(rk_cphy->phy_grf, cfg->pipe_phy_status.offset, &val); + val = (val & mask) >> cfg->pipe_phy_status.bitstart; + + return val; +} + +static rt_err_t rk3568_combphy_cfg(struct rockchip_combphy *rk_cphy) +{ + rt_uint32_t val; + rt_ubase_t rate; + const struct rockchip_combphy_grfcfg *cfg = rk_cphy->cfg->grfcfg; + + switch (rk_cphy->type) + { + case PHY_TYPE_PCIE: + /* Set SSC downward spread spectrum. */ + rockchip_combphy_updatel(rk_cphy, PHYREG32_SSC_MASK, + PHYREG32_SSC_DOWNWARD << PHYREG32_SSC_DIR_SHIFT, PHYREG32); + + rockchip_combphy_param_write(rk_cphy->phy_grf, &cfg->con0_for_pcie, RT_TRUE); + rockchip_combphy_param_write(rk_cphy->phy_grf, &cfg->con1_for_pcie, RT_TRUE); + rockchip_combphy_param_write(rk_cphy->phy_grf, &cfg->con2_for_pcie, RT_TRUE); + rockchip_combphy_param_write(rk_cphy->phy_grf, &cfg->con3_for_pcie, RT_TRUE); + break; + + case PHY_TYPE_USB3: + /* Set SSC downward spread spectrum. */ + rockchip_combphy_updatel(rk_cphy, PHYREG32_SSC_MASK, + PHYREG32_SSC_DOWNWARD << PHYREG32_SSC_DIR_SHIFT, PHYREG32); + + /* Enable adaptive CTLE for USB3.0 Rx. */ + val = HWREG32(rk_cphy->regs + PHYREG15); + val |= PHYREG15_CTLE_EN; + HWREG32(rk_cphy->regs + PHYREG15) = val; + + /* Set PLL KVCO fine tuning signals. */ + rockchip_combphy_updatel(rk_cphy, PHYREG33_PLL_KVCO_MASK, + PHYREG33_PLL_KVCO_VALUE << PHYREG33_PLL_KVCO_SHIFT, PHYREG33); + + /* Enable controlling random jitter. */ + HWREG32(rk_cphy->regs + PHYREG12) = PHYREG12_PLL_LPF_ADJ_VALUE; + + /* Set PLL input clock divider 1/2. */ + rockchip_combphy_updatel(rk_cphy, PHYREG6_PLL_DIV_MASK, + PHYREG6_PLL_DIV_2 << PHYREG6_PLL_DIV_SHIFT, PHYREG6); + + HWREG32(rk_cphy->regs + PHYREG18) = PHYREG18_PLL_LOOP; + HWREG32(rk_cphy->regs + PHYREG11) = PHYREG11_SU_TRIM_0_7; + + rockchip_combphy_param_write(rk_cphy->phy_grf, &cfg->pipe_sel_usb, RT_TRUE); + rockchip_combphy_param_write(rk_cphy->phy_grf, &cfg->pipe_txcomp_sel, RT_FALSE); + rockchip_combphy_param_write(rk_cphy->phy_grf, &cfg->pipe_txelec_sel, RT_FALSE); + rockchip_combphy_param_write(rk_cphy->phy_grf, &cfg->usb_mode_set, RT_TRUE); + break; + + case PHY_TYPE_SATA: + /* Enable adaptive CTLE for SATA Rx. */ + val = HWREG32(rk_cphy->regs + PHYREG15); + val |= PHYREG15_CTLE_EN; + HWREG32(rk_cphy->regs + PHYREG15) = val; + /* + * Set tx_rterm=50ohm and rx_rterm=44ohm for SATA. + * 0: 60ohm, 8: 50ohm 15: 44ohm (by step abort 1ohm) + */ + val = PHYREG7_TX_RTERM_50OHM << PHYREG7_TX_RTERM_SHIFT; + val |= PHYREG7_RX_RTERM_44OHM << PHYREG7_RX_RTERM_SHIFT; + HWREG32(rk_cphy->regs + PHYREG7) = val; + + rockchip_combphy_param_write(rk_cphy->phy_grf, &cfg->con0_for_sata, RT_TRUE); + rockchip_combphy_param_write(rk_cphy->phy_grf, &cfg->con1_for_sata, RT_TRUE); + rockchip_combphy_param_write(rk_cphy->phy_grf, &cfg->con2_for_sata, RT_TRUE); + rockchip_combphy_param_write(rk_cphy->phy_grf, &cfg->con3_for_sata, RT_TRUE); + rockchip_combphy_param_write(rk_cphy->pipe_grf, &cfg->pipe_con0_for_sata, RT_TRUE); + break; + + case PHY_TYPE_SGMII: + rockchip_combphy_param_write(rk_cphy->pipe_grf, &cfg->pipe_xpcs_phy_ready, RT_TRUE); + rockchip_combphy_param_write(rk_cphy->phy_grf, &cfg->pipe_phymode_sel, RT_TRUE); + rockchip_combphy_param_write(rk_cphy->phy_grf, &cfg->pipe_sel_qsgmii, RT_TRUE); + rockchip_combphy_param_write(rk_cphy->phy_grf, &cfg->sgmii_mode_set, RT_TRUE); + break; + + case PHY_TYPE_QSGMII: + rockchip_combphy_param_write(rk_cphy->pipe_grf, &cfg->pipe_xpcs_phy_ready, RT_TRUE); + rockchip_combphy_param_write(rk_cphy->phy_grf, &cfg->pipe_phymode_sel, RT_TRUE); + rockchip_combphy_param_write(rk_cphy->phy_grf, &cfg->pipe_rate_sel, RT_TRUE); + rockchip_combphy_param_write(rk_cphy->phy_grf, &cfg->pipe_sel_qsgmii, RT_TRUE); + rockchip_combphy_param_write(rk_cphy->phy_grf, &cfg->qsgmii_mode_set, RT_TRUE); + break; + + default: + LOG_E("Incompatible PHY type"); + return -RT_EINVAL; + } + + rate = rt_clk_get_rate(rk_cphy->refclk); + + switch (rate) + { + case REF_CLOCK_24MHz: + if (rk_cphy->type == PHY_TYPE_USB3 || rk_cphy->type == PHY_TYPE_SATA) + { + /* Set ssc_cnt[9:0]=0101111101 & 31.5KHz. */ + val = PHYREG15_SSC_CNT_VALUE << PHYREG15_SSC_CNT_SHIFT; + rockchip_combphy_updatel(rk_cphy, PHYREG15_SSC_CNT_MASK, val, PHYREG15); + + HWREG32(rk_cphy->regs + PHYREG16) = PHYREG16_SSC_CNT_VALUE; + } + break; + + case REF_CLOCK_25MHz: + rockchip_combphy_param_write(rk_cphy->phy_grf, &cfg->pipe_clk_25m, RT_TRUE); + break; + + case REF_CLOCK_100MHz: + rockchip_combphy_param_write(rk_cphy->phy_grf, &cfg->pipe_clk_100m, RT_TRUE); + if (rk_cphy->type == PHY_TYPE_PCIE) + { + /* PLL KVCO fine tuning. */ + val = PHYREG33_PLL_KVCO_VALUE << PHYREG33_PLL_KVCO_SHIFT; + rockchip_combphy_updatel(rk_cphy, PHYREG33_PLL_KVCO_MASK, val, PHYREG33); + + /* Enable controlling random jitter. */ + HWREG32(rk_cphy->regs + PHYREG12) = PHYREG12_PLL_LPF_ADJ_VALUE; + + val = PHYREG6_PLL_DIV_2 << PHYREG6_PLL_DIV_SHIFT; + rockchip_combphy_updatel(rk_cphy, PHYREG6_PLL_DIV_MASK, + val, PHYREG6); + + HWREG32(rk_cphy->regs + PHYREG18) = PHYREG18_PLL_LOOP; + HWREG32(rk_cphy->regs + PHYREG11) = PHYREG11_SU_TRIM_0_7; + } + else if (rk_cphy->type == PHY_TYPE_SATA) + { + /* downward spread spectrum +500ppm */ + val = PHYREG32_SSC_DOWNWARD << PHYREG32_SSC_DIR_SHIFT; + val |= PHYREG32_SSC_OFFSET_500PPM << PHYREG32_SSC_OFFSET_SHIFT; + rockchip_combphy_updatel(rk_cphy, PHYREG32_SSC_MASK, val, PHYREG32); + } + break; + + default: + LOG_E("Unsupported rate: %u", rate); + return -RT_EINVAL; + } + + if (rk_cphy->ext_refclk) + { + rockchip_combphy_param_write(rk_cphy->phy_grf, &cfg->pipe_clk_ext, RT_TRUE); + + if (rk_cphy->type == PHY_TYPE_PCIE && rate == REF_CLOCK_100MHz) + { + val = PHYREG13_RESISTER_HIGH_Z << PHYREG13_RESISTER_SHIFT; + val |= PHYREG13_CKRCV_AMP0; + rockchip_combphy_updatel(rk_cphy, PHYREG13_RESISTER_MASK, val, PHYREG13); + + val = HWREG32(rk_cphy->regs + PHYREG14); + val |= PHYREG14_CKRCV_AMP1; + HWREG32(rk_cphy->regs + PHYREG14) = val; + } + } + + if (rk_cphy->enable_ssc) + { + val = HWREG32(rk_cphy->regs + PHYREG8); + val |= PHYREG8_SSC_EN; + HWREG32(rk_cphy->regs + PHYREG8) = val; + } + + return 0; +} + +static const struct rockchip_combphy_grfcfg rk3568_combphy_grfcfgs = +{ + /* pipe-phy-grf */ + .pcie_mode_set = { 0x0000, 5, 0, 0x00, 0x0011 }, + .usb_mode_set = { 0x0000, 5, 0, 0x00, 0x0004 }, + .sgmii_mode_set = { 0x0000, 5, 0, 0x00, 0x0001 }, + .qsgmii_mode_set = { 0x0000, 5, 0, 0x00, 0x0021 }, + .pipe_rxterm_set = { 0x0000, 12, 12, 0x00, 0x0001 }, + .pipe_txelec_set = { 0x0004, 1, 1, 0x00, 0x0001 }, + .pipe_txcomp_set = { 0x0004, 4, 4, 0x00, 0x0001 }, + .pipe_clk_25m = { 0x0004, 14, 13, 0x00, 0x0001 }, + .pipe_clk_100m = { 0x0004, 14, 13, 0x00, 0x0002 }, + .pipe_phymode_sel = { 0x0008, 1, 1, 0x00, 0x0001 }, + .pipe_rate_sel = { 0x0008, 2, 2, 0x00, 0x0001 }, + .pipe_rxterm_sel = { 0x0008, 8, 8, 0x00, 0x0001 }, + .pipe_txelec_sel = { 0x0008, 12, 12, 0x00, 0x0001 }, + .pipe_txcomp_sel = { 0x0008, 15, 15, 0x00, 0x0001 }, + .pipe_clk_ext = { 0x000c, 9, 8, 0x02, 0x0001 }, + .pipe_sel_usb = { 0x000c, 14, 13, 0x00, 0x0001 }, + .pipe_sel_qsgmii = { 0x000c, 15, 13, 0x00, 0x0007 }, + .pipe_phy_status = { 0x0034, 6, 6, 0x01, 0x0000 }, + .con0_for_pcie = { 0x0000, 15, 0, 0x00, 0x1000 }, + .con1_for_pcie = { 0x0004, 15, 0, 0x00, 0x0000 }, + .con2_for_pcie = { 0x0008, 15, 0, 0x00, 0x0101 }, + .con3_for_pcie = { 0x000c, 15, 0, 0x00, 0x0200 }, + .con0_for_sata = { 0x0000, 15, 0, 0x00, 0x0119 }, + .con1_for_sata = { 0x0004, 15, 0, 0x00, 0x0040 }, + .con2_for_sata = { 0x0008, 15, 0, 0x00, 0x80c3 }, + .con3_for_sata = { 0x000c, 15, 0, 0x00, 0x4407 }, + /* pipe-grf */ + .pipe_con0_for_sata = { 0x0000, 15, 0, 0x00, 0x2220 }, + .pipe_xpcs_phy_ready = { 0x0040, 2, 2, 0x00, 0x0001 }, +}; + +static const struct rockchip_combphy_cfg rk3568_combphy_cfgs = +{ + .grfcfg = &rk3568_combphy_grfcfgs, + .combphy_cfg = rk3568_combphy_cfg, +}; + +static rt_err_t rk3588_combphy_cfg(struct rockchip_combphy *rk_cphy) +{ + rt_uint32_t val; + rt_ubase_t rate; + const struct rockchip_combphy_grfcfg *cfg = rk_cphy->cfg->grfcfg; + + switch (rk_cphy->type) + { + case PHY_TYPE_PCIE: + rockchip_combphy_param_write(rk_cphy->phy_grf, &cfg->con0_for_pcie, RT_TRUE); + rockchip_combphy_param_write(rk_cphy->phy_grf, &cfg->con1_for_pcie, RT_TRUE); + rockchip_combphy_param_write(rk_cphy->phy_grf, &cfg->con2_for_pcie, RT_TRUE); + rockchip_combphy_param_write(rk_cphy->phy_grf, &cfg->con3_for_pcie, RT_TRUE); + rockchip_combphy_param_write(rk_cphy->pipe_grf, &cfg->pipe_pcie1l0_sel, RT_TRUE); + rockchip_combphy_param_write(rk_cphy->pipe_grf, &cfg->pipe_pcie1l1_sel, RT_TRUE); + break; + + case PHY_TYPE_USB3: + /* Set SSC downward spread spectrum */ + rockchip_combphy_updatel(rk_cphy, PHYREG32_SSC_MASK, + PHYREG32_SSC_DOWNWARD << PHYREG32_SSC_DIR_SHIFT, PHYREG32); + + /* Enable adaptive CTLE for USB3.0 Rx. */ + val = HWREG32(rk_cphy->regs + PHYREG15); + val |= PHYREG15_CTLE_EN; + HWREG32(rk_cphy->regs + PHYREG15) = val; + + /* Set PLL KVCO fine tuning signals. */ + rockchip_combphy_updatel(rk_cphy, PHYREG33_PLL_KVCO_MASK, + PHYREG33_PLL_KVCO_VALUE << PHYREG33_PLL_KVCO_SHIFT, PHYREG33); + + /* Enable controlling random jitter. */ + HWREG32(rk_cphy->regs + PHYREG12) = PHYREG12_PLL_LPF_ADJ_VALUE; + + /* Set PLL input clock divider 1/2. */ + rockchip_combphy_updatel(rk_cphy, PHYREG6_PLL_DIV_MASK, + PHYREG6_PLL_DIV_2 << PHYREG6_PLL_DIV_SHIFT, PHYREG6); + + HWREG32(rk_cphy->regs + PHYREG18) = PHYREG18_PLL_LOOP; + HWREG32(rk_cphy->regs + PHYREG11) = PHYREG11_SU_TRIM_0_7; + + rockchip_combphy_param_write(rk_cphy->phy_grf, &cfg->pipe_txcomp_sel, RT_FALSE); + rockchip_combphy_param_write(rk_cphy->phy_grf, &cfg->pipe_txelec_sel, RT_FALSE); + rockchip_combphy_param_write(rk_cphy->phy_grf, &cfg->usb_mode_set, RT_TRUE); + break; + + case PHY_TYPE_SATA: + /* Enable adaptive CTLE for SATA Rx. */ + val = HWREG32(rk_cphy->regs + PHYREG15); + val |= PHYREG15_CTLE_EN; + HWREG32(rk_cphy->regs + PHYREG15) = val; + /* + * Set tx_rterm=50ohm and rx_rterm=44ohm for SATA. + * 0: 60ohm, 8: 50ohm 15: 44ohm (by step abort 1ohm) + */ + val = PHYREG7_TX_RTERM_50OHM << PHYREG7_TX_RTERM_SHIFT; + val |= PHYREG7_RX_RTERM_44OHM << PHYREG7_RX_RTERM_SHIFT; + HWREG32(rk_cphy->regs + PHYREG7) = val; + + rockchip_combphy_param_write(rk_cphy->phy_grf, &cfg->con0_for_sata, RT_TRUE); + rockchip_combphy_param_write(rk_cphy->phy_grf, &cfg->con1_for_sata, RT_TRUE); + rockchip_combphy_param_write(rk_cphy->phy_grf, &cfg->con2_for_sata, RT_TRUE); + rockchip_combphy_param_write(rk_cphy->phy_grf, &cfg->con3_for_sata, RT_TRUE); + rockchip_combphy_param_write(rk_cphy->pipe_grf, &cfg->pipe_con0_for_sata, RT_TRUE); + rockchip_combphy_param_write(rk_cphy->pipe_grf, &cfg->pipe_con1_for_sata, RT_TRUE); + break; + + case PHY_TYPE_SGMII: + case PHY_TYPE_QSGMII: + default: + LOG_E("Incompatible PHY type"); + return -RT_EINVAL; + } + + rate = rt_clk_get_rate(rk_cphy->refclk); + + switch (rate) + { + case REF_CLOCK_24MHz: + if (rk_cphy->type == PHY_TYPE_USB3 || rk_cphy->type == PHY_TYPE_SATA) + { + /* Set ssc_cnt[9:0]=0101111101 & 31.5KHz. */ + val = PHYREG15_SSC_CNT_VALUE << PHYREG15_SSC_CNT_SHIFT; + rockchip_combphy_updatel(rk_cphy, PHYREG15_SSC_CNT_MASK, + val, PHYREG15); + + HWREG32(rk_cphy->regs + PHYREG16) = PHYREG16_SSC_CNT_VALUE; + } + break; + + case REF_CLOCK_25MHz: + rockchip_combphy_param_write(rk_cphy->phy_grf, &cfg->pipe_clk_25m, RT_TRUE); + break; + + case REF_CLOCK_100MHz: + rockchip_combphy_param_write(rk_cphy->phy_grf, &cfg->pipe_clk_100m, RT_TRUE); + if (rk_cphy->type == PHY_TYPE_PCIE) + { + /* PLL KVCO fine tuning. */ + val = 4 << PHYREG33_PLL_KVCO_SHIFT; + rockchip_combphy_updatel(rk_cphy, PHYREG33_PLL_KVCO_MASK, val, PHYREG33); + + /* Enable controlling random jitter. */ + HWREG32(rk_cphy->regs + PHYREG12) = PHYREG12_PLL_LPF_ADJ_VALUE; + + /* Set up rx_trim: PLL LPF C1 85pf R1 1.25kohm */ + HWREG32(rk_cphy->regs + PHYREG27) = PHYREG27_RX_TRIM_RK3588; + + /* Set up su_trim: */ + HWREG32(rk_cphy->regs + PHYREG11) = PHYREG11_SU_TRIM_0_7; + } + else if (rk_cphy->type == PHY_TYPE_SATA) + { + /* downward spread spectrum +500ppm */ + val = PHYREG32_SSC_DOWNWARD << PHYREG32_SSC_DIR_SHIFT; + val |= PHYREG32_SSC_OFFSET_500PPM << PHYREG32_SSC_OFFSET_SHIFT; + rockchip_combphy_updatel(rk_cphy, PHYREG32_SSC_MASK, val, PHYREG32); + } + break; + default: + LOG_E("Unsupported rate: %u", rate); + return -RT_EINVAL; + } + + if (rk_cphy->ext_refclk) + { + rockchip_combphy_param_write(rk_cphy->phy_grf, &cfg->pipe_clk_ext, RT_TRUE); + + if (rk_cphy->type == PHY_TYPE_PCIE && rate == REF_CLOCK_100MHz) + { + val = PHYREG13_RESISTER_HIGH_Z << PHYREG13_RESISTER_SHIFT; + val |= PHYREG13_CKRCV_AMP0; + rockchip_combphy_updatel(rk_cphy, PHYREG13_RESISTER_MASK, val, PHYREG13); + + val = HWREG32(rk_cphy->regs + PHYREG14); + val |= PHYREG14_CKRCV_AMP1; + HWREG32(rk_cphy->regs + PHYREG14) = val; + } + } + + if (rk_cphy->enable_ssc) + { + val = HWREG32(rk_cphy->regs + PHYREG8); + val |= PHYREG8_SSC_EN; + HWREG32(rk_cphy->regs + PHYREG8) = val; + } + + return RT_EOK; +} + +static const struct rockchip_combphy_grfcfg rk3588_combphy_grfcfgs = +{ + /* pipe-phy-grf */ + .pcie_mode_set = { 0x0000, 5, 0, 0x00, 0x0011 }, + .usb_mode_set = { 0x0000, 5, 0, 0x00, 0x0004 }, + .pipe_rxterm_set = { 0x0000, 12, 12, 0x00, 0x0001 }, + .pipe_txelec_set = { 0x0004, 1, 1, 0x00, 0x0001 }, + .pipe_txcomp_set = { 0x0004, 4, 4, 0x00, 0x0001 }, + .pipe_clk_25m = { 0x0004, 14, 13, 0x00, 0x0001 }, + .pipe_clk_100m = { 0x0004, 14, 13, 0x00, 0x0002 }, + .pipe_rxterm_sel = { 0x0008, 8, 8, 0x00, 0x0001 }, + .pipe_txelec_sel = { 0x0008, 12, 12, 0x00, 0x0001 }, + .pipe_txcomp_sel = { 0x0008, 15, 15, 0x00, 0x0001 }, + .pipe_clk_ext = { 0x000c, 9, 8, 0x02, 0x0001 }, + .pipe_phy_status = { 0x0034, 6, 6, 0x01, 0x0000 }, + .con0_for_pcie = { 0x0000, 15, 0, 0x00, 0x1000 }, + .con1_for_pcie = { 0x0004, 15, 0, 0x00, 0x0000 }, + .con2_for_pcie = { 0x0008, 15, 0, 0x00, 0x0101 }, + .con3_for_pcie = { 0x000c, 15, 0, 0x00, 0x0200 }, + .con0_for_sata = { 0x0000, 15, 0, 0x00, 0x0129 }, + .con1_for_sata = { 0x0004, 15, 0, 0x00, 0x0000 }, + .con2_for_sata = { 0x0008, 15, 0, 0x00, 0x80c1 }, + .con3_for_sata = { 0x000c, 15, 0, 0x00, 0x0407 }, + /* pipe-grf */ + .pipe_con0_for_sata = { 0x0000, 11, 5, 0x00, 0x0022 }, + .pipe_con1_for_sata = { 0x0000, 2, 0, 0x00, 0x0002 }, + .pipe_pcie1l0_sel = { 0x0100, 0, 0, 0x01, 0x0000 }, + .pipe_pcie1l1_sel = { 0x0100, 1, 1, 0x01, 0x0000 }, +}; + +static const struct rockchip_combphy_cfg rk3588_combphy_cfgs = +{ + .grfcfg = &rk3588_combphy_grfcfgs, + .combphy_cfg = rk3588_combphy_cfg, +}; + +static rt_phy_status rockchip_combphy_init(struct rt_phy_device *phy_device, + void *object, rt_uint32_t phy_addr, rt_uint32_t src_clock_hz) +{ + rt_err_t err; + struct rockchip_combphy *rk_cphy = raw_to_rockchip_combphy(phy_device); + const struct rockchip_combphy_grfcfg *cfg = rk_cphy->cfg->grfcfg; + + if ((err = rt_clk_array_prepare_enable(rk_cphy->clk_arr))) + { + LOG_E("Failed to enable clks error = %s", rt_strerror(err)); + + return PHY_STATUS_FAIL; + } + + switch (rk_cphy->type) + { + case PHY_TYPE_PCIE: + case PHY_TYPE_USB3: + case PHY_TYPE_SATA: + case PHY_TYPE_SGMII: + case PHY_TYPE_QSGMII: + if (rk_cphy->cfg->combphy_cfg) + { + err = rk_cphy->cfg->combphy_cfg(rk_cphy); + } + break; + + default: + LOG_E("Incompatible PHY type"); + err = -RT_EINVAL; + break; + } + + if (err) + { + LOG_E("Failed to init PHY for type %d", rk_cphy->type); + + goto _out_clk; + } + + if ((err = rt_reset_control_deassert(rk_cphy->rstc))) + { + goto _out_clk; + } + + if (rk_cphy->type == PHY_TYPE_USB3) + { + rt_uint32_t val; + rt_int32_t timeout_us = 1000; + + while (timeout_us --> 0) + { + val = rockchip_combphy_is_ready(rk_cphy); + + if (val == cfg->pipe_phy_status.enable) + { + break; + } + + rt_hw_us_delay(10); + rt_hw_cpu_relax(); + } + + if (timeout_us <= 0) + { + LOG_W("Wait PHY status ready timeout"); + } + } + + return PHY_STATUS_OK; + +_out_clk: + rt_clk_array_disable_unprepare(rk_cphy->clk_arr); + + return PHY_STATUS_FAIL; +} + +static rt_phy_status rockchip_combphy_exit(struct rt_phy_device *phy_device, + void *object, rt_uint32_t phy_addr) +{ + struct rockchip_combphy *rk_cphy = raw_to_rockchip_combphy(phy_device); + + rt_clk_array_disable_unprepare(rk_cphy->clk_arr); + rt_reset_control_assert(rk_cphy->rstc); + + return PHY_STATUS_OK; +} + +static rt_err_t rockchip_combphy_ofw_parse(struct rt_phy_device *phy_device, + struct rt_ofw_cell_args *phy_args) +{ + struct rockchip_combphy *rk_cphy = raw_to_rockchip_combphy(phy_device); + + if (phy_args->args_count != 1) + { + LOG_E("Invalid number of arguments"); + + return -RT_EINVAL; + } + + if (rk_cphy->type != PHY_NONE && rk_cphy->type != phy_args->args[0]) + { + LOG_W("PHY select type %d from type %d", + phy_args->args[0], rk_cphy->type); + } + + rk_cphy->type = phy_args->args[0]; + + return RT_EOK; +} + +const static struct rt_phy_ops rochchip_combphy_ops = +{ + .init = rockchip_combphy_init, + .exit = rockchip_combphy_exit, + .ofw_parse = rockchip_combphy_ofw_parse, +}; + +static void rockchip_combphy_free(struct rockchip_combphy *rk_cphy) +{ + if (rk_cphy->regs) + { + rt_iounmap(rk_cphy->regs); + } + + if (!rt_is_err_or_null(rk_cphy->clk_arr)) + { + rt_clk_array_put(rk_cphy->clk_arr); + } + + if (!rt_is_err_or_null(rk_cphy->rstc)) + { + rt_reset_control_put(rk_cphy->rstc); + } + + rt_free(rk_cphy); +} + +static rt_err_t rockchip_combphy_probe(struct rt_platform_device *pdev) +{ + rt_err_t err; + const char *dev_name; + struct rt_phy_device *phy; + struct rt_device *dev = &pdev->parent; + struct rockchip_combphy *rk_cphy = rt_calloc(1, sizeof(*rk_cphy)); + const struct rockchip_combphy_cfg *phy_cfg; + + if (!rk_cphy) + { + return -RT_ENOMEM; + } + + phy_cfg = pdev->id->data; + rk_cphy->type = PHY_NONE; + rk_cphy->cfg = phy_cfg; + + rk_cphy->regs = rt_dm_dev_iomap(dev, 0); + if (!rk_cphy->regs) + { + err = -RT_EIO; + goto _fail; + } + + rk_cphy->clk_arr = rt_clk_get_array(dev); + if (rt_is_err(rk_cphy->clk_arr)) + { + err = rt_ptr_err(rk_cphy->clk_arr); + goto _fail; + } + + for (int i = rk_cphy->clk_arr->count - 1; i >= 0; --i) + { + if (!rt_strncmp(rk_cphy->clk_arr->clks[i]->con_id, "ref", 3)) + { + rk_cphy->refclk = rk_cphy->clk_arr->clks[i]; + } + } + + if (!rk_cphy->refclk) + { + err = -RT_EIO; + goto _fail; + } + + rk_cphy->pipe_grf = rt_syscon_find_by_ofw_phandle(dev->ofw_node, "rockchip,pipe-grf"); + if (!rk_cphy->pipe_grf) + { + err = -RT_EIO; + goto _fail; + } + + rk_cphy->phy_grf = rt_syscon_find_by_ofw_phandle(dev->ofw_node, "rockchip,pipe-phy-grf"); + if (!rk_cphy->phy_grf) + { + err = -RT_EIO; + goto _fail; + } + + rk_cphy->enable_ssc = rt_dm_dev_prop_read_bool(dev, "rockchip,enable-ssc"); + rk_cphy->ext_refclk = rt_dm_dev_prop_read_bool(dev, "rockchip,ext-refclk"); + + rk_cphy->rstc = rt_reset_control_get_array(dev); + if (rt_is_err(rk_cphy->rstc)) + { + err = rt_ptr_err(rk_cphy->rstc); + goto _fail; + } + + err = rt_reset_control_assert(rk_cphy->rstc); + if (err) + { + goto _fail; + } + + dev->user_data = rk_cphy; + + phy = &rk_cphy->parent; + phy->ops = &rochchip_combphy_ops; + + rt_dm_dev_set_name_auto(&phy->parent, "phy"); + dev_name = rt_dm_dev_get_name(&phy->parent); + + if ((err = rt_hw_phy_register(phy, dev_name))) + { + goto _fail; + } + + rt_dm_dev_bind_fwdata(dev, RT_NULL, phy); + + return RT_EOK; + +_fail: + rockchip_combphy_free(rk_cphy); + + return err; +} + +static rt_err_t rockchip_combphy_remove(struct rt_platform_device *pdev) +{ + struct rockchip_combphy *rk_cphy = pdev->parent.user_data; + + rt_device_unregister(&rk_cphy->parent.parent); + + rockchip_combphy_free(rk_cphy); + + return RT_EOK; +} + +static const struct rt_ofw_node_id rockchip_combphy_ofw_ids[] = +{ + { .compatible = "rockchip,rk3568-naneng-combphy", .data = &rk3568_combphy_cfgs }, + { .compatible = "rockchip,rk3588-naneng-combphy", .data = &rk3588_combphy_cfgs }, + { /* sentinel */ } +}; + +static struct rt_platform_driver rockchip_combphy_driver = +{ + .name = "phy-rockchip-naneng-combphy", + .ids = rockchip_combphy_ofw_ids, + + .probe = rockchip_combphy_probe, + .remove = rockchip_combphy_remove, +}; + +static int rockchip_combphy_drv_register(void) +{ + rt_platform_driver_register(&rockchip_combphy_driver); + + return 0; +} +INIT_DRIVER_EARLY_EXPORT(rockchip_combphy_drv_register); diff --git a/components/drivers/phy/rockchip/phy-rockchip-snps-pcie3.c b/components/drivers/phy/rockchip/phy-rockchip-snps-pcie3.c new file mode 100644 index 000000000000..d360b849f095 --- /dev/null +++ b/components/drivers/phy/rockchip/phy-rockchip-snps-pcie3.c @@ -0,0 +1,462 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-10-24 GuEe-GUI first version + */ + +#include +#include + +#include "../phy_dm.h" + +#define DBG_TAG "phy.rockchip.snps-pcie3" +#define DBG_LVL DBG_INFO +#include + +#define GRF_ENABLE_SHIFT 16 +#define GRF_ENABLE_MASK RT_GENMASK(31, 16) +#define GRF_ENABLE (GRF_ENABLE_MASK << GRF_ENABLE_SHIFT) + +struct rockchip_pcie3_phy_soc_data; + +struct rockchip_pcie3_phy +{ + struct rt_phy_device parent; + + void *regs; + /* mode: RC, EP */ + int mode; + /* pcie30_phymode: Aggregation, Bifurcation */ + int pcie30_phymode; + rt_bool_t is_bifurcation; + + int num_lanes; + rt_uint32_t lanes[4]; + + struct rt_syscon *phy_grf; + struct rt_syscon *pipe_grf; + struct rt_reset_control *rstc; + struct rt_clk_array *clk_arr; + + const struct rockchip_pcie3_phy_soc_data *soc_data; +}; + +#define raw_to_rockchip_pcie3_phy(raw) rt_container_of(raw, struct rockchip_pcie3_phy, parent) + +struct rockchip_pcie3_phy_soc_data +{ + rt_err_t (*phy_init)(struct rockchip_pcie3_phy *); +}; + +#define RK3568_GRF_PCIE30PHY_CON1 0x4 +#define RK3568_GRF_PCIE30PHY_CON6 0x18 +#define RK3568_GRF_PCIE30PHY_CON9 0x24 +#define RK3568_GRF_PCIE30PHY_DA_OCM (RT_BIT(15) | RT_BIT(GRF_ENABLE_SHIFT + 15)) +#define RK3568_GRF_PCIE30PHY_STATUS0 0x80 +#define RK3568_GRF_PCIE30PHY_WR_EN (0xf << 16) +#define RK3568_SRAM_INIT_DONE(reg) (reg & RT_BIT(14)) +#define RK3568_BIFURCATION_LANE_0_1 RT_BIT(0) + +static rt_err_t rockchip_p3phy_rk3568_init(struct rockchip_pcie3_phy *rk_p3phy) +{ + rt_err_t err; + rt_uint32_t reg = 0, timeout_us; + rt_bool_t bifurcation = RT_FALSE; + + /* Deassert PCIe PMA output clamp mode */ + rt_syscon_write(rk_p3phy->phy_grf, RK3568_GRF_PCIE30PHY_CON9, + RK3568_GRF_PCIE30PHY_DA_OCM); + + for (int i = 0; i < rk_p3phy->num_lanes; ++i) + { + if (rk_p3phy->lanes[i] > 1) + { + bifurcation = RT_TRUE; + break; + } + } + + /* Set bifurcation if needed, and it doesn't care RC/EP */ + if (bifurcation) + { + LOG_D("%s: bifurcation %s", + rt_dm_dev_get_name(&rk_p3phy->parent.parent), "enabled"); + + rt_syscon_write(rk_p3phy->phy_grf, RK3568_GRF_PCIE30PHY_CON6, + RK3568_GRF_PCIE30PHY_WR_EN | RK3568_BIFURCATION_LANE_0_1); + rt_syscon_write(rk_p3phy->phy_grf, RK3568_GRF_PCIE30PHY_CON1, + RK3568_GRF_PCIE30PHY_DA_OCM); + } + else + { + LOG_D("%s: bifurcation %s", + rt_dm_dev_get_name(&rk_p3phy->parent.parent), "disabled"); + + rt_syscon_write(rk_p3phy->phy_grf, RK3568_GRF_PCIE30PHY_CON6, + RK3568_GRF_PCIE30PHY_WR_EN & ~RK3568_BIFURCATION_LANE_0_1); + } + + rt_reset_control_deassert(rk_p3phy->rstc); + + timeout_us = 500; + while (timeout_us --> 0) + { + err = rt_syscon_read(rk_p3phy->phy_grf, RK3568_GRF_PCIE30PHY_STATUS0, ®); + + if (err || RK3568_SRAM_INIT_DONE(reg)) + { + break; + } + + rt_hw_us_delay(1); + rt_hw_cpu_relax(); + } + + if (err) + { + LOG_E("%s: lock failed 0x%x, check input refclk and power supply", + rt_dm_dev_get_name(&rk_p3phy->parent.parent), reg); + } + + return err; +} + +static const struct rockchip_pcie3_phy_soc_data rk3568_data = +{ + .phy_init = rockchip_p3phy_rk3568_init, +}; + +#define PHP_GRF_PCIESEL_CON 0x100 +#define RK3588_PCIE3PHY_GRF_CMN_CON0 0x0 +#define RK3588_PCIE3PHY_GRF_DA_OCM (RT_BIT(8) | RT_BIT(GRF_ENABLE_SHIFT + 8)) +#define RK3588_PCIE3PHY_GRF_MODE_SHIFT 0x7 +#define RK3588_PCIE3PHY_GRF_PHY0_STATUS1 0x904 +#define RK3588_PCIE3PHY_GRF_PHY1_STATUS1 0xa04 +#define RK3588_SRAM_INIT_DONE(reg) (reg & RT_BIT(0)) + +#define RK3588_BIFURCATION_LANE_0_1 RT_BIT(0) +#define RK3588_BIFURCATION_LANE_2_3 RT_BIT(1) +#define RK3588_LANE_AGGREGATION RT_BIT(2) + +static rt_err_t rockchip_p3phy_rk3588_init(struct rockchip_pcie3_phy *rk_p3phy) +{ + rt_uint8_t mode = 0; + rt_uint32_t reg = 0, timeout_us; + rt_err_t err, err_res = RT_EOK; + + /* Deassert PCIe PMA output clamp mode */ + rt_syscon_write(rk_p3phy->phy_grf, RK3588_PCIE3PHY_GRF_CMN_CON0, + RK3588_PCIE3PHY_GRF_DA_OCM); + + /* Set bifurcation if needed */ + for (int i = 0; i < rk_p3phy->num_lanes; ++i) + { + if (!rk_p3phy->lanes[i]) + { + mode |= (RT_BIT(i) << 3); + } + if (rk_p3phy->lanes[i] > 1) + { + mode |= (RT_BIT(i) >> 1); + } + } + + if (!mode) + { + reg = RK3588_LANE_AGGREGATION; + } + else + { + if (mode & (RT_BIT(0) | RT_BIT(1))) + { + reg |= RK3588_BIFURCATION_LANE_0_1; + } + if (mode & (RT_BIT(2) | RT_BIT(3))) + { + reg |= RK3588_BIFURCATION_LANE_2_3; + } + } + + rt_syscon_write(rk_p3phy->phy_grf, RK3588_PCIE3PHY_GRF_CMN_CON0, + (RK3588_PCIE3PHY_GRF_MODE_SHIFT << GRF_ENABLE_SHIFT) | reg); + + /* Set pcie1ln_sel in PHP_GRF_PCIESEL_CON */ + if (rk_p3phy->pipe_grf) + { + reg = (mode & (RT_BIT(6) | RT_BIT(7))) >> 6; + + if (reg) + { + rt_syscon_write(rk_p3phy->pipe_grf, PHP_GRF_PCIESEL_CON, + (reg << GRF_ENABLE_SHIFT) | reg); + } + } + + rt_reset_control_deassert(rk_p3phy->rstc); + + timeout_us = 500; + while (timeout_us --> 0) + { + err = rt_syscon_read(rk_p3phy->phy_grf, RK3588_PCIE3PHY_GRF_PHY0_STATUS1, ®); + + if (err || RK3588_SRAM_INIT_DONE(reg)) + { + break; + } + + rt_hw_us_delay(1); + rt_hw_cpu_relax(); + } + err_res |= err; + + timeout_us = 500; + while (timeout_us --> 0) + { + err = rt_syscon_read(rk_p3phy->phy_grf, RK3588_PCIE3PHY_GRF_PHY1_STATUS1, ®); + + if (err || RK3588_SRAM_INIT_DONE(reg)) + { + break; + } + + rt_hw_us_delay(1); + rt_hw_cpu_relax(); + } + err_res |= err; + + if (err_res) + { + LOG_E("%s: lock failed 0x%x, check input refclk and power supply", + rt_dm_dev_get_name(&rk_p3phy->parent.parent), reg); + } + + return err_res; +} + +static const struct rockchip_pcie3_phy_soc_data rk3588_data = +{ + .phy_init = rockchip_p3phy_rk3588_init, +}; + +static rt_phy_status rockchip_pcie3_phy_init(struct rt_phy_device *phy_device, + void *object, rt_uint32_t phy_addr, rt_uint32_t src_clock_hz) +{ + rt_err_t err; + struct rockchip_pcie3_phy *rk_p3phy = raw_to_rockchip_pcie3_phy(phy_device); + + if ((err = rt_clk_array_prepare_enable(rk_p3phy->clk_arr))) + { + LOG_E("Enable PCIe bulk clks erros = %s", rt_strerror(err)); + + return PHY_STATUS_FAIL; + } + + rt_reset_control_assert(rk_p3phy->rstc); + rt_hw_us_delay(1); + + if (rk_p3phy->soc_data->phy_init) + { + if ((err = rk_p3phy->soc_data->phy_init(rk_p3phy))) + { + rt_clk_array_disable_unprepare(rk_p3phy->clk_arr); + + LOG_E("Init PCIe PHY erros = %s", rt_strerror(err)); + + return PHY_STATUS_FAIL; + } + } + + return PHY_STATUS_OK; +} + +static rt_phy_status rockchip_pcie3_phy_exit(struct rt_phy_device *phy_device, + void *object, rt_uint32_t phy_addr) +{ + struct rockchip_pcie3_phy *rk_p3phy = raw_to_rockchip_pcie3_phy(phy_device); + + rt_clk_array_disable_unprepare(rk_p3phy->clk_arr); + rt_reset_control_assert(rk_p3phy->rstc); + + return PHY_STATUS_OK; +} + +const static struct rt_phy_ops rockchip_pcie3_phy_ops = +{ + .init = rockchip_pcie3_phy_init, + .exit = rockchip_pcie3_phy_exit, +}; + +static void rockchip_pcie3_phy_free(struct rockchip_pcie3_phy *rk_p3phy) +{ + if (rk_p3phy->regs) + { + rt_iounmap(rk_p3phy->regs); + } + + if (!rt_is_err_or_null(rk_p3phy->clk_arr)) + { + rt_clk_array_put(rk_p3phy->clk_arr); + } + + if (!rt_is_err_or_null(rk_p3phy->rstc)) + { + rt_reset_control_put(rk_p3phy->rstc); + } + + rt_free(rk_p3phy); +} + +static rt_err_t rockchip_pcie3_phy_probe(struct rt_platform_device *pdev) +{ + rt_err_t err; + rt_uint32_t val; + const char *dev_name; + struct rt_phy_device *phy; + struct rt_device *dev = &pdev->parent; + struct rockchip_pcie3_phy *rk_p3phy = rt_calloc(1, sizeof(*rk_p3phy)); + + if (!rk_p3phy) + { + return -RT_ENOMEM; + } + + rk_p3phy->regs = rt_dm_dev_iomap(dev, 0); + + if (!rk_p3phy->regs) + { + err = -RT_EIO; + goto _fail; + } + + rk_p3phy->phy_grf = rt_syscon_find_by_ofw_phandle(dev->ofw_node, "rockchip,phy-grf"); + + if (!rk_p3phy->phy_grf) + { + err = -RT_EIO; + goto _fail; + } + + rk_p3phy->pipe_grf = rt_syscon_find_by_ofw_phandle(dev->ofw_node, "rockchip,pipe-grf"); + + if (!rk_p3phy->pipe_grf && rt_dm_dev_prop_read_bool(dev, "rockchip,pipe-grf")) + { + err = -RT_EIO; + goto _fail; + } + + rk_p3phy->num_lanes = rt_dm_dev_prop_read_u32_array_index(dev, + "data-lanes", 0, RT_ARRAY_SIZE(rk_p3phy->lanes), rk_p3phy->lanes); + + if (rk_p3phy->num_lanes == -RT_EEMPTY) + { + rk_p3phy->num_lanes = 1; + rk_p3phy->lanes[0] = 1; + } + else if (rk_p3phy->num_lanes < 0) + { + err = rk_p3phy->num_lanes; + LOG_E("Failed to read \"data-lanes\""); + goto _fail; + } + + rk_p3phy->rstc = rt_reset_control_get_by_name(dev, "phy"); + + if (rt_is_err(rk_p3phy->rstc)) + { + err = rt_ptr_err(rk_p3phy->rstc); + goto _fail; + } + + rk_p3phy->clk_arr = rt_clk_get_array(dev); + + if (rt_is_err(rk_p3phy->clk_arr)) + { + err = rt_ptr_err(rk_p3phy->clk_arr); + goto _fail; + } + + if (!rt_dm_dev_prop_read_u32(dev, "rockchip,pcie30-phymode", &val) && val <= 4) + { + rk_p3phy->pcie30_phymode = val; + } + else + { + rk_p3phy->pcie30_phymode = PHY_MODE_PCIE_AGGREGATION; + } + + rt_syscon_write(rk_p3phy->phy_grf, RK3588_PCIE3PHY_GRF_CMN_CON0, + GRF_ENABLE | rk_p3phy->pcie30_phymode); + + if (rk_p3phy->pipe_grf) + { + rt_uint32_t reg = rk_p3phy->pcie30_phymode & 3; + + if (reg) + { + rt_syscon_write(rk_p3phy->pipe_grf, PHP_GRF_PCIESEL_CON, + (reg << GRF_ENABLE_SHIFT) | reg); + } + } + + rk_p3phy->soc_data = pdev->id->data; + dev->user_data = rk_p3phy; + + phy = &rk_p3phy->parent; + phy->ops = &rockchip_pcie3_phy_ops; + + rt_dm_dev_set_name_auto(&phy->parent, "phy"); + dev_name = rt_dm_dev_get_name(&phy->parent); + + if ((err = rt_hw_phy_register(phy, dev_name))) + { + goto _fail; + } + + rt_dm_dev_bind_fwdata(dev, RT_NULL, phy); + + return RT_EOK; + +_fail: + rockchip_pcie3_phy_free(rk_p3phy); + + return err; +} + +static rt_err_t rockchip_pcie3_phy_remove(struct rt_platform_device *pdev) +{ + struct rockchip_pcie3_phy *rk_p3phy = pdev->parent.user_data; + + rt_device_unregister(&rk_p3phy->parent.parent); + + rockchip_pcie3_phy_free(rk_p3phy); + + return RT_EOK; +} + +static const struct rt_ofw_node_id rockchip_pcie3_phy_ofw_ids[] = +{ + { .compatible = "rockchip,rk3568-pcie3-phy", .data = &rk3568_data }, + { .compatible = "rockchip,rk3588-pcie3-phy", .data = &rk3588_data }, + { /* sentinel */ } +}; + +static struct rt_platform_driver rockchip_pcie3_phy_driver = +{ + .name = "phy-rockchip-snps-pcie3", + .ids = rockchip_pcie3_phy_ofw_ids, + + .probe = rockchip_pcie3_phy_probe, + .remove = rockchip_pcie3_phy_remove, +}; + +static int rockchip_pcie3_phy_drv_register(void) +{ + rt_platform_driver_register(&rockchip_pcie3_phy_driver); + + return 0; +} +INIT_DRIVER_EARLY_EXPORT(rockchip_pcie3_phy_drv_register); diff --git a/components/drivers/pic/Kconfig b/components/drivers/pic/Kconfig index 0c9686ec8667..60299b211c7c 100755 --- a/components/drivers/pic/Kconfig +++ b/components/drivers/pic/Kconfig @@ -7,35 +7,57 @@ menuconfig RT_USING_PIC config MAX_HANDLERS int "IRQ max handlers" depends on RT_USING_PIC - range 1 4294967294 + range 1 2147483647 default 256 +config RT_PIC_BCM2712_MIP + bool "Broadcom 2712 MSI-X Interrupt Peripheral support" if RT_PCI_MSI + depends on RT_USING_PIC + select RT_USING_OFW + default n + config RT_PIC_BCM2835_INTC bool "BCM2835 intc" depends on RT_USING_PIC + select RT_USING_OFW default n config RT_PIC_BCM2836_L1_INTC bool "BCM2836 L1 intc" depends on RT_USING_PIC + select RT_USING_OFW + default n + +config RT_PIC_BRCMSTB_L2_IRQ + bool "BRCMSTB L2 IRQ" + depends on RT_USING_PIC + select RT_USING_OFW default n config RT_PIC_ARM_GIC bool "ARM GICv2/v1" depends on RT_USING_PIC + select RT_USING_OFW default n config RT_PIC_ARM_GIC_V2M bool "ARM GIC V2M" if RT_PIC_ARM_GIC && RT_PCI_MSI + select RT_USING_OFW + select RT_USING_ADT_REF + select RT_USING_ADT_BITMAP default n config RT_PIC_ARM_GIC_V3 bool "ARM GICv3" depends on RT_USING_PIC + select RT_USING_OFW default n config RT_PIC_ARM_GIC_V3_ITS bool "ARM GICv3 ITS (Interrupt Translation Service)" if RT_PIC_ARM_GIC_V3 && RT_PCI_MSI + select RT_USING_OFW + select RT_USING_ADT_REF + select RT_USING_ADT_BITMAP default n config RT_PIC_ARM_GIC_MAX_NR @@ -44,13 +66,3 @@ config RT_PIC_ARM_GIC_MAX_NR depends on RT_PIC_ARM_GIC default 2 if SOC_REALVIEW default 1 - -config RT_PIC_RISCV_INTC - bool "RISC-V Local Interrupt Controller" - depends on RT_USING_PIC - default n - -config RT_PIC_SIFIVE_PLIC - bool "SiFive Platform-Level Interrupt Controller" - depends on RT_USING_PIC - default n diff --git a/components/drivers/pic/SConscript b/components/drivers/pic/SConscript index 08cdc3847944..196a60bb7cb4 100644 --- a/components/drivers/pic/SConscript +++ b/components/drivers/pic/SConscript @@ -10,14 +10,23 @@ CPPPATH = [cwd + '/../include'] src = ['pic.c'] +if GetDepend(['RT_PIC_BCM2712_MIP']): + src += ['pic-bcm2712-mip.c'] + if GetDepend(['RT_PIC_BCM2835_INTC']): src += ['pic-bcm2835.c'] if GetDepend(['RT_PIC_BCM2836_L1_INTC']): src += ['pic-bcm2836.c'] +if GetDepend(['RT_PIC_BRCMSTB_L2_IRQ']): + src += ['pic-brcmstb-l2.c'] + +if GetDepend(['RT_PIC_ARM_GIC']) or GetDepend(['RT_PIC_ARM_GIC_V3']): + src += ['pic-gic-common.c'] + if GetDepend(['RT_PIC_ARM_GIC']): - src += ['pic-gicv2.c', 'pic-gic-common.c'] + src += ['pic-gicv2.c'] if GetDepend(['RT_PIC_ARM_GIC_V2M']): src += ['pic-gicv2m.c'] @@ -28,12 +37,6 @@ if GetDepend(['RT_PIC_ARM_GIC_V3']): if GetDepend(['RT_PIC_ARM_GIC_V3_ITS']): src += ['pic-gicv3-its.c'] -if GetDepend(['RT_PIC_RISCV_INTC']): - src += ['pic-riscv-intc.c'] - -if GetDepend(['RT_PIC_SIFIVE_PLIC']): - src += ['pic-sifive-plic.c'] - group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) Return('group') diff --git a/components/drivers/pic/pic-bcm2712-mip.c b/components/drivers/pic/pic-bcm2712-mip.c new file mode 100644 index 000000000000..6ce8ccffcde3 --- /dev/null +++ b/components/drivers/pic/pic-bcm2712-mip.c @@ -0,0 +1,265 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-11-25 GuEe-GUI first version + */ + +#include +#include +#include + +#define DBG_TAG "pic.mip-msi" +#define DBG_LVL DBG_INFO +#include + +#define MIP_INT_RAISED 0x00 +#define MIP_INT_CLEARED 0x10 +#define MIP_INT_CFGL_HOST 0x20 +#define MIP_INT_CFGH_HOST 0x30 +#define MIP_INT_MASKL_HOST 0x40 +#define MIP_INT_MASKH_HOST 0x50 +#define MIP_INT_MASKL_VPU 0x60 +#define MIP_INT_MASKH_VPU 0x70 +#define MIP_INT_STATUSL_HOST 0x80 +#define MIP_INT_STATUSH_HOST 0x90 +#define MIP_INT_STATUSL_VPU 0xa0 +#define MIP_INT_STATUSH_VPU 0xb0 + +struct mip_msi +{ + struct rt_pic parent; + struct rt_pic *ppic; + struct rt_ofw_node *pic_np; + + void *base; + rt_ubase_t msg_addr; + rt_uint32_t msi_base; /* The SGI number that MSIs start */ + rt_uint32_t num_msis; /* The number of SGIs for MSIs */ + rt_uint32_t msi_offset; /* Shift the allocated msi up by N */ + rt_ubase_t *msi_map; + + struct rt_spinlock msi_map_lock; +}; + +#define raw_to_mip_msi(raw) rt_container_of(raw, struct mip_msi, parent) + +static rt_err_t mip_msi_irq_init(struct rt_pic *pic) +{ + struct mip_msi *mip = raw_to_mip_msi(pic); + + mip->ppic = rt_ofw_data(mip->pic_np); + + return mip->ppic ? RT_EOK : -RT_ENOSYS; +} + +static void mip_msi_irq_mask(struct rt_pic_irq *pirq) +{ + rt_pci_msi_mask_irq(pirq); + rt_pic_irq_parent_mask(pirq); +} + +static void mip_msi_irq_unmask(struct rt_pic_irq *pirq) +{ + rt_pci_msi_unmask_irq(pirq); + rt_pic_irq_parent_unmask(pirq); +} + +static void mip_msi_compose_msi_msg(struct rt_pic_irq *pirq, struct rt_pci_msi_msg *msg) +{ + struct mip_msi *mip = raw_to_mip_msi(pirq->pic); + + msg->address_hi = rt_upper_32_bits(mip->msg_addr); + msg->address_lo = rt_lower_32_bits(mip->msg_addr); + msg->data = pirq->hwirq; +} + +static int mip_msi_irq_alloc_msi(struct rt_pic *pic, struct rt_pci_msi_desc *msi_desc) +{ + int irq, parent_irq, hwirq, hwirq_index; + struct rt_pic_irq *pirq; + struct mip_msi *mip = raw_to_mip_msi(pic); + + rt_spin_lock(&mip->msi_map_lock); + + hwirq_index = bitmap_next_clear_bit(mip->msi_map, 0, mip->num_msis); + + if (hwirq_index >= mip->num_msis) + { + irq = -RT_EEMPTY; + goto _out_lock; + } + + hwirq = hwirq_index + mip->msi_offset; + + parent_irq = mip->ppic->ops->irq_map(mip->ppic, hwirq, RT_IRQ_MODE_EDGE_RISING); + if (parent_irq < 0) + { + irq = parent_irq; + goto _out_lock; + } + + irq = rt_pic_config_irq(pic, hwirq_index, hwirq); + if (irq < 0) + { + goto _out_lock; + } + pirq = rt_pic_find_irq(pic, hwirq_index); + + rt_pic_cascade(pirq, parent_irq); + + bitmap_set_bit(mip->msi_map, hwirq_index); + +_out_lock: + rt_spin_unlock(&mip->msi_map_lock); + + return irq; +} + +static void mip_msi_irq_free_msi(struct rt_pic *pic, int irq) +{ + struct rt_pic_irq *pirq; + struct mip_msi *mip = raw_to_mip_msi(pic); + + pirq = rt_pic_find_pirq(pic, irq); + + if (!pirq) + { + return; + } + + rt_spin_lock(&mip->msi_map_lock); + + rt_pic_uncascade(pirq); + bitmap_clear_bit(mip->msi_map, pirq->hwirq - mip->msi_offset); + + rt_spin_unlock(&mip->msi_map_lock); +} + +const static struct rt_pic_ops mip_msi_ops = +{ + .name = "MIP-MSI", + .irq_init = mip_msi_irq_init, + .irq_mask = mip_msi_irq_mask, + .irq_unmask = mip_msi_irq_unmask, + .irq_eoi = rt_pic_irq_parent_eoi, + .irq_set_affinity = rt_pic_irq_parent_set_affinity, + .irq_set_triger_mode = rt_pic_irq_parent_set_triger_mode, + .irq_compose_msi_msg = mip_msi_compose_msi_msg, + .irq_alloc_msi = mip_msi_irq_alloc_msi, + .irq_free_msi = mip_msi_irq_free_msi, + .flags = RT_PIC_F_IRQ_ROUTING, +}; + +static rt_err_t mip_msi_ofw_init(struct rt_ofw_node *np, const struct rt_ofw_node_id *id) +{ + rt_err_t err; + struct mip_msi *mip = rt_calloc(1, sizeof(*mip)); + + if (!mip) + { + return -RT_ENOMEM; + } + + mip->pic_np = rt_ofw_find_irq_parent(np, RT_NULL); + + if (!mip->pic_np) + { + LOG_E("Unable to find PIC parent"); + err = -RT_EINVAL; + goto _fail; + } + + mip->base = rt_ofw_iomap(np, 0); + + if (!mip->base) + { + err = -RT_EIO; + goto _fail; + } + + if (rt_ofw_prop_read_u32(np, "brcm,msi-base-spi", &mip->msi_base)) + { + LOG_E("Unable to parse MSI base"); + err = -RT_EINVAL; + goto _fail; + } + + if (rt_ofw_prop_read_u32(np, "brcm,msi-num-spis", &mip->num_msis)) + { + LOG_E("Unable to parse MSI numbers"); + err = -RT_EINVAL; + goto _fail; + } + + if (rt_ofw_prop_read_u32(np, "brcm,msi-offset", &mip->msi_offset)) + { + mip->msi_offset = 0; + } + + if (rt_ofw_prop_read_u64(np, "brcm,msi-pci-addr", &mip->msg_addr)) + { + LOG_E("Unable to parse MSI address"); + err = -RT_EINVAL; + goto _fail; + } + + mip->msi_map = rt_calloc(RT_BITS_TO_LONGS(mip->num_msis), sizeof(*mip->msi_map)); + + if (!mip->msi_map) + { + err = -RT_ENOMEM; + goto _fail; + } + + /* + * Begin with all MSI-Xs masked in for the host, masked out for the VPU, + * and edge-triggered. + */ + HWREG32(mip->base + MIP_INT_MASKL_HOST) = 0; + HWREG32(mip->base + MIP_INT_MASKH_HOST) = 0; + HWREG32(mip->base + MIP_INT_MASKL_VPU) = ~0; + HWREG32(mip->base + MIP_INT_MASKH_VPU) = ~0; + HWREG32(mip->base + MIP_INT_CFGL_HOST) = ~0; + HWREG32(mip->base + MIP_INT_CFGH_HOST) = ~0; + + LOG_D("Found %d MSIx, starting at %d", mip->num_msis, mip->msi_base); + + rt_spin_lock_init(&mip->msi_map_lock); + + mip->parent.priv_data = mip; + mip->parent.ops = &mip_msi_ops; + + rt_pic_linear_irq(&mip->parent, RT_BITS_TO_LONGS(mip->num_msis)); + rt_pic_user_extends(&mip->parent); + + rt_ofw_data(np) = &mip->parent; + rt_ofw_node_set_flag(np, RT_OFW_F_READLY); + + return RT_EOK; + +_fail: + if (mip->base) + { + rt_iounmap(mip->base); + } + + if (mip->msi_map) + { + rt_free(mip->msi_map); + } + + rt_free(mip); + + return err; +} + +static const struct rt_ofw_node_id mip_msi_ofw_ids[] = +{ + { .compatible = "brcm,bcm2712-mip-intc" }, + { /* sentinel */ } +}; +RT_PIC_OFW_DECLARE(mip_msi, mip_msi_ofw_ids, mip_msi_ofw_init); diff --git a/components/drivers/pic/pic-bcm2835.c b/components/drivers/pic/pic-bcm2835.c index 25e6b6ae248b..e97471b97854 100755 --- a/components/drivers/pic/pic-bcm2835.c +++ b/components/drivers/pic/pic-bcm2835.c @@ -151,7 +151,7 @@ static rt_err_t bcm2835_arm_intc_irq_parse(struct rt_pic *pic, return err; } -static struct rt_pic_ops bcm2835_arm_intc_ops = +const static struct rt_pic_ops bcm2835_arm_intc_ops = { .name = "BCM2835-ARMCTRL-level", .irq_mask = bcm2835_arm_intc_mask_irq, @@ -239,4 +239,4 @@ static const struct rt_ofw_node_id bcm2835_arm_intc_ofw_ids[] = { .compatible = "brcm,bcm2836-armctrl-ic", }, { /* sentinel */ } }; -RT_OF_DECLARE_CORE(bcm2835_arm_intc, bcm2835_arm_intc_ofw_ids, bcm2835_arm_ofw_intc); +RT_PIC_OFW_DECLARE(bcm2835_arm_intc, bcm2835_arm_intc_ofw_ids, bcm2835_arm_ofw_intc); diff --git a/components/drivers/pic/pic-bcm2836.c b/components/drivers/pic/pic-bcm2836.c index 293465447869..5919bab1c889 100755 --- a/components/drivers/pic/pic-bcm2836.c +++ b/components/drivers/pic/pic-bcm2836.c @@ -10,6 +10,7 @@ #include #include +#include #define DBG_TAG "irqchip.bcm2836" #define DBG_LVL DBG_INFO @@ -115,7 +116,7 @@ static void bcm2836_arm_l1_intc_irq_ack(struct rt_pic_irq *pirq) case LOCAL_IRQ_MAILBOX1: case LOCAL_IRQ_MAILBOX2: case LOCAL_IRQ_MAILBOX3: - HWREG32(intc.base + LOCAL_MAILBOX0_CLR0 + 16 * rt_hw_cpu_id()) = RT_BIT(data->hwirq); + HWREG32(intc.base + LOCAL_MAILBOX0_CLR0 + 16 * rt_hw_cpu_id()) = RT_BIT(pirq->hwirq); break; default: break; @@ -124,7 +125,7 @@ static void bcm2836_arm_l1_intc_irq_ack(struct rt_pic_irq *pirq) static void bcm2836_arm_l1_intc_irq_mask(struct rt_pic_irq *pirq) { - bcm2836_arm_irqchip_irq_ops(data->hwirq, RT_TRUE); + bcm2836_arm_irqchip_irq_ops(pirq->hwirq, RT_TRUE); } static void bcm2836_arm_l1_intc_irq_unmask(struct rt_pic_irq *pirq) @@ -149,7 +150,7 @@ static void bcm2836_arm_l1_intc_irq_send_ipi(struct rt_pic_irq *pirq, bitmap_t * } } -static int bcm2836_arm_l1_intc_irq_map(struct rt_irq_chip_desc *chip_desc, int hwirq, struct rt_irq_info *init_info) +static int bcm2836_arm_l1_intc_irq_map(struct rt_pic *pic, int hwirq, rt_uint32_t mode) { return rt_pic_config_irq(pic, hwirq, hwirq); } @@ -157,7 +158,7 @@ static int bcm2836_arm_l1_intc_irq_map(struct rt_irq_chip_desc *chip_desc, int h static rt_err_t bcm2836_arm_l1_intc_irq_parse(struct rt_pic *pic, struct rt_ofw_cell_args *args, struct rt_pic_irq *out_pirq) { - rt_err_t ret = RT_EOK; + rt_err_t err = RT_EOK; if (args->args_count == 2) { @@ -166,13 +167,13 @@ static rt_err_t bcm2836_arm_l1_intc_irq_parse(struct rt_pic *pic, } else { - ret = -RT_EINVAL; + err = -RT_EINVAL; } - return ret; + return err; } -static struct rt_pic_ops bcm2836_arm_l1_intc_ops = +const static struct rt_pic_ops bcm2836_arm_l1_intc_ops = { .name = "BCM2836-ARM-L1-INTC", .irq_ack = bcm2836_arm_l1_intc_irq_ack, @@ -232,7 +233,7 @@ static rt_err_t bcm2836_arm_l1_intc_init(struct rt_ofw_node *np, const struct rt { rt_err_t err = RT_EOK; - intc.base = rt_ofw_iomap(node, 0); + intc.base = rt_ofw_iomap(np, 0); if (intc.base) { @@ -244,6 +245,11 @@ static rt_err_t bcm2836_arm_l1_intc_init(struct rt_ofw_node *np, const struct rt rt_pic_linear_irq(&intc.parent, LAST_IRQ + 1); + for (int ipi = 0; ipi < 2; ++ipi) + { + rt_pic_config_ipi(&intc.parent, ipi, LOCAL_IRQ_MAILBOX0 + ipi); + } + rt_pic_add_traps(bcm2836_arm_l1_intc_handler, &intc); } else @@ -254,9 +260,9 @@ static rt_err_t bcm2836_arm_l1_intc_init(struct rt_ofw_node *np, const struct rt return err; } -static const struct rt_ofw_ofw_id bcm2836_arm_l1_intc_ofw_ids[] = +static const struct rt_ofw_node_id bcm2836_arm_l1_intc_ofw_ids[] = { { .compatible = "brcm,bcm2836-l1-intc" }, { /* sentinel */ } }; -RT_OF_DECLARE_CORE(bcm2836_arm_l1_intc, bcm2836_arm_l1_intc_ofw_ids, bcm2836_arm_l1_intc_init); +RT_PIC_OFW_DECLARE(bcm2836_arm_l1_intc, bcm2836_arm_l1_intc_ofw_ids, bcm2836_arm_l1_intc_init); diff --git a/components/drivers/pic/pic-brcmstb-l2.c b/components/drivers/pic/pic-brcmstb-l2.c new file mode 100644 index 000000000000..a3ab83243401 --- /dev/null +++ b/components/drivers/pic/pic-brcmstb-l2.c @@ -0,0 +1,236 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-08-24 GuEe-GUI first version + */ + +#include +#include +#include + +#define DBG_TAG "irqchip.brcmstb_l2" +#define DBG_LVL DBG_INFO +#include + +struct brcmstb_intc_init_params +{ + int cpu_status; + int cpu_clear; + int cpu_mask_status; + int cpu_mask_set; + int cpu_mask_clear; +}; + +struct brcmstb_l2_intc +{ + struct rt_pic parent; + + int irq; + void *regs; + rt_bool_t can_wake; + + const struct brcmstb_intc_init_params *params; +}; + +#define raw_to_brcmstb_l2_intc(raw) rt_container_of(raw, struct brcmstb_l2_intc, parent) + +/* Register offsets in the L2 latched interrupt controller */ +static const struct brcmstb_intc_init_params l2_edge_intc = +{ + .cpu_status = 0x00, + .cpu_clear = 0x08, + .cpu_mask_status = 0x0c, + .cpu_mask_set = 0x10, + .cpu_mask_clear = 0x14 +}; + +/* Register offsets in the L2 level interrupt controller */ +static const struct brcmstb_intc_init_params l2_lvl_intc = +{ + .cpu_status = 0x00, + .cpu_clear = -1, /* Register not present */ + .cpu_mask_status = 0x04, + .cpu_mask_set = 0x08, + .cpu_mask_clear = 0x0c +}; + +/* Register offsets in the 2711 L2 level interrupt controller */ +static const struct brcmstb_intc_init_params l2_2711_lvl_intc = +{ + .cpu_status = 0x00, + .cpu_clear = 0x08, + .cpu_mask_status = 0x0c, + .cpu_mask_set = 0x10, + .cpu_mask_clear = 0x14 +}; + +static void brcmstb_l2_intc_irq_ack(struct rt_pic_irq *pirq) +{ + struct brcmstb_l2_intc *bl2 = raw_to_brcmstb_l2_intc(pirq->pic); + + if (bl2->params->cpu_clear >= 0) + { + HWREG32(bl2->regs + bl2->params->cpu_clear) = RT_BIT(pirq->hwirq); + } +} + +static void brcmstb_l2_intc_irq_mask(struct rt_pic_irq *pirq) +{ + struct brcmstb_l2_intc *bl2 = raw_to_brcmstb_l2_intc(pirq->pic); + + HWREG32(bl2->regs + bl2->params->cpu_mask_set) = RT_BIT(pirq->hwirq); +} + +static void brcmstb_l2_intc_irq_unmask(struct rt_pic_irq *pirq) +{ + struct brcmstb_l2_intc *bl2 = raw_to_brcmstb_l2_intc(pirq->pic); + + HWREG32(bl2->regs + bl2->params->cpu_mask_clear) = RT_BIT(pirq->hwirq); +} + +static int brcmstb_l2_intc_irq_map(struct rt_pic *pic, int hwirq, rt_uint32_t mode) +{ + return rt_pic_config_irq(pic, hwirq, hwirq); +} + +static rt_err_t brcmstb_l2_intc_irq_parse(struct rt_pic *pic, + struct rt_ofw_cell_args *args, struct rt_pic_irq *out_pirq) +{ + rt_err_t err = RT_EOK; + struct brcmstb_l2_intc *bl2 = rt_container_of(pic, struct brcmstb_l2_intc, parent); + + if (args->args_count == 1) + { + out_pirq->hwirq = args->args[0]; + + if (bl2->params == &l2_edge_intc) + { + out_pirq->mode = RT_IRQ_MODE_LEVEL_HIGH; + } + else if (bl2->params == &l2_lvl_intc) + { + out_pirq->mode = RT_IRQ_MODE_EDGE_RISING; + } + } + else + { + err = -RT_EINVAL; + } + + return err; +} + +const static struct rt_pic_ops brcmstb_l2_intc_ops = +{ + .name = "BRCMSTB-L2-INTC", + .irq_ack = brcmstb_l2_intc_irq_ack, + .irq_mask = brcmstb_l2_intc_irq_mask, + .irq_unmask = brcmstb_l2_intc_irq_unmask, + .irq_map = brcmstb_l2_intc_irq_map, + .irq_parse = brcmstb_l2_intc_irq_parse, +}; + +static void brcmstb_l2_intc_isr(int irqno, void *params) +{ + struct rt_pic_irq *pirq; + rt_uint32_t hwirq, status; + struct brcmstb_l2_intc *bl2 = params; + + status = HWREG32(bl2->regs + bl2->params->cpu_status) & + HWREG32(bl2->regs + bl2->params->cpu_mask_status); + + if (!status) + { + LOG_E("Bad irq trigger"); + + return; + } + + do { + hwirq = __rt_ffs(status) - 1; + status &= ~RT_BIT(hwirq); + + pirq = rt_pic_find_irq(&bl2->parent, hwirq); + brcmstb_l2_intc_irq_ack(pirq); + + rt_pic_handle_isr(pirq); + } while (status); +} + +static rt_err_t brcmstb_l2_intc_ofw_intc(struct rt_ofw_node *np, const struct rt_ofw_node_id *id) +{ + rt_err_t err; + const struct brcmstb_intc_init_params *params; + struct brcmstb_l2_intc *bl2 = rt_calloc(1, sizeof(*bl2)); + + if (!bl2) + { + return -RT_ENOMEM; + } + + bl2->regs = rt_ofw_iomap(np, 0); + + if (!bl2->regs) + { + err = -RT_EIO; + goto _fail; + } + + bl2->params = params = id->data; + + /* Disable all interrupts by default */ + HWREG32(bl2->regs + params->cpu_mask_set) = 0xffffffff; + + /* Wakeup interrupts may be retained from S5 (cold boot) */ + bl2->can_wake = rt_ofw_prop_read_bool(np, "brcm,irq-can-wake"); + if (!bl2->can_wake && params->cpu_clear >= 0) + { + HWREG32(bl2->regs + params->cpu_clear) = 0xffffffff; + } + + bl2->irq = rt_ofw_get_irq(np, 0); + + if (bl2->irq < 0) + { + LOG_E("Failed to find parent interrupt"); + + err = -RT_EINVAL; + goto _fail; + } + + bl2->parent.priv_data = &bl2; + bl2->parent.ops = &brcmstb_l2_intc_ops; + + rt_ofw_data(np) = &bl2->parent; + + rt_pic_linear_irq(&bl2->parent, 32); + + rt_hw_interrupt_install(bl2->irq, brcmstb_l2_intc_isr, bl2, "BRCMSTB-L2-INTC"); + + return RT_EOK; + +_fail: + if (bl2->regs) + { + rt_iounmap(bl2->regs); + } + + rt_free(bl2); + + return err; +} + +static const struct rt_ofw_node_id brcmstb_l2_intc_ofw_ids[] = +{ + { .compatible = "brcm,l2-intc", .data = &l2_edge_intc }, + { .compatible = "brcm,hif-spi-l2-intc", .data = &l2_edge_intc }, + { .compatible = "brcm,upg-aux-aon-l2-intc", .data = &l2_edge_intc }, + { .compatible = "brcm,bcm7271-l2-intc", .data = &l2_lvl_intc }, + { .compatible = "brcm,bcm2711-l2-intc", .data = &l2_2711_lvl_intc }, + { /* sentinel */ } +}; +RT_PIC_OFW_DECLARE(brcmstb_l2_intc, brcmstb_l2_intc_ofw_ids, brcmstb_l2_intc_ofw_intc); diff --git a/components/drivers/pic/pic-gic-common.c b/components/drivers/pic/pic-gic-common.c index f8eaa4c7d218..b6b4e18e49b1 100644 --- a/components/drivers/pic/pic-gic-common.c +++ b/components/drivers/pic/pic-gic-common.c @@ -11,7 +11,7 @@ #include #include -#define DBG_TAG "pic.gic-common" +#define DBG_TAG "pic.gic*" #define DBG_LVL DBG_INFO #include @@ -24,7 +24,7 @@ void gic_common_init_quirk_ofw(const struct rt_ofw_node *ic_np, const struct gic { for (; quirks->desc; ++quirks) { - if (!rt_ofw_node_is_compatible(ic_np, quirks->compatible)) + if (!quirks->compatible || !rt_ofw_node_is_compatible(ic_np, quirks->compatible)) { continue; } @@ -47,7 +47,7 @@ void gic_common_init_quirk_hw(rt_uint32_t iidr, const struct gic_quirk *quirks, continue; } - if (quirks->iidr & (iidr & quirks->iidr_mask)) + if (quirks->iidr == (iidr & quirks->iidr_mask)) { RT_ASSERT(quirks->init != RT_NULL); diff --git a/components/drivers/pic/pic-gicv2.c b/components/drivers/pic/pic-gicv2.c index e8709ad5b836..d09457ae4f9e 100644 --- a/components/drivers/pic/pic-gicv2.c +++ b/components/drivers/pic/pic-gicv2.c @@ -32,6 +32,7 @@ #define raw_to_gicv2(raw) rt_container_of(raw, struct gicv2, parent) +static rt_bool_t needs_rmw_access = RT_FALSE; static int _gicv2_nr = 0, _init_cpu_id = 0; static struct gicv2 _gicv2_list[RT_PIC_ARM_GIC_MAX_NR] = {}; static rt_bool_t _gicv2_eoi_mode_ns = RT_FALSE; @@ -200,8 +201,31 @@ static rt_err_t gicv2_irq_set_affinity(struct rt_pic_irq *pirq, bitmap_t *affini int hwirq = pirq->hwirq; struct gicv2 *gic = raw_to_gicv2(pirq->pic); rt_uint32_t target_list = ((rt_uint8_t *)affinity)[gic - &_gicv2_list[0]]; + rt_uint8_t valb = _gicv2_cpumask_map[__rt_ffs(target_list) - 1]; + void *io_addr = gic->dist_base + GIC_DIST_TARGET + hwirq; - HWREG32(gic->dist_base + GIC_DIST_TARGET + hwirq) = _gicv2_cpumask_map[__rt_ffs(target_list) - 1]; + if (rt_unlikely(needs_rmw_access)) + { + /* RMW write byte */ + rt_uint32_t val; + rt_ubase_t level; + rt_ubase_t offset = (rt_ubase_t)io_addr & 3UL, shift = offset * 8; + static struct rt_spinlock rmw_lock = {}; + + level = rt_spin_lock_irqsave(&rmw_lock); + + io_addr -= offset; + val = HWREG32(io_addr); + val &= ~RT_GENMASK(shift + 7, shift); + val |= valb << shift; + HWREG32(io_addr) = val; + + rt_spin_unlock_irqrestore(&rmw_lock, level); + } + else + { + HWREG8(io_addr) = valb; + } return RT_EOK; } @@ -252,12 +276,16 @@ static int gicv2_irq_map(struct rt_pic *pic, int hwirq, rt_uint32_t mode) if (pirq && hwirq >= GIC_SGI_NR) { - pirq->hwirq = hwirq; pirq->mode = mode; pirq->priority = GICD_INT_DEF_PRI; bitmap_set_bit(pirq->affinity, _init_cpu_id); irq = rt_pic_config_irq(pic, irq_index, hwirq); + + if (irq >= 0 && mode != RT_IRQ_MODE_LEVEL_HIGH) + { + gicv2_irq_set_triger_mode(pirq, mode); + } } else { @@ -298,7 +326,7 @@ static rt_err_t gicv2_irq_parse(struct rt_pic *pic, struct rt_ofw_cell_args *arg return err; } -static struct rt_pic_ops gicv2_ops = +const static struct rt_pic_ops gicv2_ops = { .name = "GICv2", .irq_init = gicv2_irq_init, @@ -349,8 +377,24 @@ static rt_bool_t gicv2_handler(void *data) return res; } +static rt_err_t gicv2_enable_rmw_access(void *data) +{ + if (rt_ofw_machine_is_compatible("renesas,emev2")) + { + needs_rmw_access = RT_TRUE; + return RT_EOK; + } + + return -RT_EINVAL; +} + static const struct gic_quirk _gicv2_quirks[] = { + { + .desc = "GICv2: Broken byte access", + .compatible = "arm,pl390", + .init = gicv2_enable_rmw_access, + }, { /* sentinel */ } }; diff --git a/components/drivers/pic/pic-gicv2m.c b/components/drivers/pic/pic-gicv2m.c index a5d0e026f864..69a8e3aa38c0 100755 --- a/components/drivers/pic/pic-gicv2m.c +++ b/components/drivers/pic/pic-gicv2m.c @@ -10,21 +10,15 @@ #include #include +#include -#define DBG_TAG "irqchip.gic-v2m" +#define DBG_TAG "pic.gic-v2m" #define DBG_LVL DBG_INFO #include +#include #include -#include -#include - -#include -#include -#include -#include -#include -#include +#include "pic-gic-common.h" /* * MSI_TYPER: @@ -56,82 +50,79 @@ struct gicv2m { - struct rt_irq_chip_desc parent; + struct rt_pic parent; - rt_list_t list; void *base; void *base_phy; rt_uint32_t spi_start; /* The SPI number that MSIs start */ rt_uint32_t spis_nr; /* The number of SPIs for MSIs */ rt_uint32_t spi_offset; /* Offset to be subtracted from SPI number */ - rt_bitmap_t *vectors; /* MSI vector bitmap */ + bitmap_t *vectors; /* MSI vector bitmap */ rt_uint32_t flags; /* Flags for v2m's specific implementation */ void *gic; + struct rt_spinlock lock; }; -static rt_list_t _v2m_nodes = RT_LIST_OBJECT_INIT(_v2m_nodes); -static struct rt_spinlock _v2m_lock = { 0 }; +#define raw_to_gicv2m(raw) rt_container_of(raw, struct gicv2m, parent) static rt_ubase_t gicv2m_get_msi_addr(struct gicv2m *v2m, int hwirq) { - rt_ubase_t ret; + rt_ubase_t addr; if (v2m->flags & GICV2M_GRAVITON_ADDRESS_ONLY) { - ret = (rt_ubase_t)v2m->base_phy | ((hwirq - 32) << 3); + addr = (rt_ubase_t)v2m->base_phy | ((hwirq - 32) << 3); } else { - ret = (rt_ubase_t)v2m->base_phy + V2M_MSI_SETSPI_NS; + addr = (rt_ubase_t)v2m->base_phy + V2M_MSI_SETSPI_NS; } - return ret; + return addr; } static rt_bool_t is_msi_spi_valid(rt_uint32_t base, rt_uint32_t num) { - rt_bool_t ret = RT_TRUE; - if (base < V2M_MIN_SPI) { - LOG_E("Invalid MSI base SPI (base:%u)", base); + LOG_E("Invalid MSI base SPI (base: %u)", base); - ret = RT_FALSE; + return RT_FALSE; } else if ((num == 0) || (base + num > V2M_MAX_SPI)) { LOG_E("Number of SPIs (%u) exceed maximum (%u)", num, V2M_MAX_SPI - V2M_MIN_SPI + 1); - ret = RT_FALSE; + return RT_FALSE; } - return ret; + return RT_TRUE; } -void gicv2m_irq_mask(struct rt_irq_data *data) +static void gicv2m_irq_mask(struct rt_pic_irq *pirq) { - pci_msi_mask_irq(data); - rt_irqchip_irq_parent_mask(data); + rt_pci_msi_mask_irq(pirq); + rt_pic_irq_parent_mask(pirq); } -void gicv2m_irq_unmask(struct rt_irq_data *data) +static void gicv2m_irq_unmask(struct rt_pic_irq *pirq) { - pci_msi_unmask_irq(data); - rt_irqchip_irq_parent_unmask(data); + rt_pci_msi_unmask_irq(pirq); + rt_pic_irq_parent_unmask(pirq); } -void gicv2m_compose_msi_msg(struct rt_irq_data *data, struct msi_msg *msg) +static void gicv2m_compose_msi_msg(struct rt_pic_irq *pirq, struct rt_pci_msi_msg *msg) { - struct gicv2m *v2m = data->chip_data; + struct gicv2m *v2m = raw_to_gicv2m(pirq->pic); if (v2m) { - rt_ubase_t addr = gicv2m_get_msi_addr(v2m, data->hwirq); + rt_ubase_t addr = gicv2m_get_msi_addr(v2m, pirq->hwirq); - msg->address_hi = ((rt_uint32_t)(((addr) >> 16) >> 16)); - msg->address_lo = ((rt_uint32_t)((addr) & RT_UINT32_MAX)); + msg->address_hi = rt_upper_32_bits(addr); + msg->address_lo = rt_lower_32_bits(addr); if (v2m->flags & GICV2M_GRAVITON_ADDRESS_ONLY) { @@ -139,7 +130,7 @@ void gicv2m_compose_msi_msg(struct rt_irq_data *data, struct msi_msg *msg) } else { - msg->data = data->hwirq; + msg->data = pirq->hwirq; } if (v2m->flags & GICV2M_NEEDS_SPI_OFFSET) @@ -149,229 +140,224 @@ void gicv2m_compose_msi_msg(struct rt_irq_data *data, struct msi_msg *msg) } } -int gicv2m_irq_alloc_msi(struct rt_irq_chip_desc *chip_desc, struct msi_desc *msi_desc) +static int gicv2m_irq_alloc_msi(struct rt_pic *pic, struct rt_pci_msi_desc *msi_desc) { - int irq = -1; - struct gicv2m *v2m; - rt_ubase_t level = rt_spin_lock_irqsave(&_v2m_lock); + rt_ubase_t level; + int irq, parent_irq, hwirq, hwirq_index; + struct rt_pic_irq *pirq; + struct gicv2m *v2m = raw_to_gicv2m(pic); + struct rt_pic *ppic = v2m->gic; - rt_list_for_each_entry(v2m, &_v2m_nodes, list) - { - int vector = rt_bitmap_next_clear_bit(v2m->vectors, 0, v2m->spis_nr); + level = rt_spin_lock_irqsave(&v2m->lock); - if (vector >= 0) - { - struct rt_irq_info spi = - { - .mode = RT_IRQ_MODE_EDGE_RISING, - .type = RT_IRQ_TYPE_EXTERNAL, - .msi_desc = msi_desc, - }; + hwirq_index = bitmap_next_clear_bit(v2m->vectors, 0, v2m->spis_nr); - irq = rt_irqchip_chip_parent_map(chip_desc, v2m->gic, v2m->spi_start + v2m->spi_offset + vector, &spi); + if (hwirq_index >= v2m->spis_nr) + { + irq = -RT_EEMPTY; + goto _out_lock; + } - if (irq < 0) - { - continue; - } + hwirq = v2m->spi_start + v2m->spi_offset + hwirq_index; - rt_bitmap_set_bit(v2m->vectors, vector); + parent_irq = ppic->ops->irq_map(ppic, hwirq, RT_IRQ_MODE_EDGE_RISING); + if (parent_irq < 0) + { + irq = parent_irq; + goto _out_lock; + } - break; - } + irq = rt_pic_config_irq(pic, hwirq_index, hwirq); + if (irq < 0) + { + goto _out_lock; } + pirq = rt_pic_find_irq(pic, hwirq_index); + + rt_pic_cascade(pirq, parent_irq); - rt_spin_unlock_irqrestore(&_v2m_lock, level); + bitmap_set_bit(v2m->vectors, hwirq_index); + +_out_lock: + rt_spin_unlock_irqrestore(&v2m->lock, level); return irq; } -void gicv2m_irq_free_msi(struct rt_irq_chip_desc *chip_desc, int irq) +static void gicv2m_irq_free_msi(struct rt_pic *pic, int irq) { - rt_ubase_t level = rt_spin_lock_irqsave(&_v2m_lock); - - if (!rt_list_isempty(&_v2m_nodes)) - { - struct rt_irq_data irqdata = { .irq = -1 }; + rt_ubase_t level; + struct rt_pic_irq *pirq; + struct gicv2m *v2m = raw_to_gicv2m(pic); - rt_irqchip_search_irq(irq, &irqdata); + pirq = rt_pic_find_pirq(pic, irq); - if (irqdata.irq >= 0) - { - struct gicv2m *v2m = irqdata.chip_data; + if (!pirq) + { + return; + } - rt_bitmap_clear_bit(v2m->vectors, irqdata.hwirq - (v2m->spi_start + v2m->spi_offset)); + level = rt_spin_lock_irqsave(&v2m->lock); - rt_irqchip_chip_parent_unmap(chip_desc, irq); - } - } + rt_pic_uncascade(pirq); + bitmap_clear_bit(v2m->vectors, pirq->hwirq - (v2m->spi_start + v2m->spi_offset)); - rt_spin_unlock_irqrestore(&_v2m_lock, level); + rt_spin_unlock_irqrestore(&v2m->lock, level); } -static struct rt_irq_chip gicv2m_irq_chip = +const static struct rt_pic_ops gicv2m_ops = { .name = "GICv2m", - .irq_ack = rt_irqchip_irq_parent_ack, + .irq_ack = rt_pic_irq_parent_ack, .irq_mask = gicv2m_irq_mask, .irq_unmask = gicv2m_irq_unmask, - .irq_eoi = rt_irqchip_irq_parent_eoi, - .irq_set_priority = rt_irqchip_irq_parent_set_priority, - .irq_set_affinity = rt_irqchip_irq_parent_set_affinity, + .irq_eoi = rt_pic_irq_parent_eoi, + .irq_set_priority = rt_pic_irq_parent_set_priority, + .irq_set_affinity = rt_pic_irq_parent_set_affinity, .irq_compose_msi_msg = gicv2m_compose_msi_msg, .irq_alloc_msi = gicv2m_irq_alloc_msi, .irq_free_msi = gicv2m_irq_free_msi, + .flags = RT_PIC_F_IRQ_ROUTING, }; -static const struct rt_of_device_id gicv2m_of_match[] = +static const struct rt_ofw_node_id gicv2m_ofw_match[] = { { .compatible = "arm,gic-v2m-frame" }, { /* sentinel */ } }; -rt_err_t gicv2m_of_probe(struct rt_device_node *node, const struct rt_of_device_id *id) +rt_err_t gicv2m_ofw_probe(struct rt_ofw_node *np, const struct rt_ofw_node_id *id) { - rt_err_t ret = RT_EOK; - rt_uint64_t regs[2]; - struct rt_device_node *v2m_np; + rt_err_t err = RT_EOK; + struct rt_ofw_node *v2m_np; - rt_of_for_each_child_of_node(node, v2m_np) + rt_ofw_foreach_available_child_node(np, v2m_np) { - rt_bool_t init_ok = RT_TRUE; struct gicv2m *v2m; rt_size_t bitmap_size; rt_uint32_t spi_start = 0, spis_nr = 0; - if (!rt_of_device_is_available(v2m_np)) + if (!rt_ofw_node_match(v2m_np, gicv2m_ofw_match)) { continue; } - if (!rt_of_match_node(gicv2m_of_match, v2m_np)) + if (!rt_ofw_prop_read_bool(v2m_np, "msi-controller")) { continue; } - if (!rt_of_find_property(v2m_np, "msi-controller", RT_NULL)) + if (!(v2m = rt_malloc(sizeof(*v2m)))) { - continue; + rt_ofw_node_put(v2m_np); + + err = -RT_ENOMEM; + break; } - rt_memset(regs, 0, sizeof(regs)); + v2m->base = rt_ofw_iomap(v2m_np, 0); - if (rt_of_address_to_array(v2m_np, regs) < 0) + if (!v2m->base) { + LOG_E("%s: IO map failed", rt_ofw_node_full_name(v2m_np)); continue; } - if (!(v2m = rt_malloc(sizeof(*v2m)))) - { - ret = -RT_ENOMEM; - break; - } - - rt_list_init(&v2m->list); - v2m->base_phy = (void *)regs[0]; - v2m->base = rt_ioremap(v2m->base_phy, regs[1]); + v2m->base_phy = rt_kmem_v2p(v2m->base); v2m->flags = 0; - if (!rt_of_property_read_u32(v2m_np, "arm,msi-base-spi", &spi_start) && - !rt_of_property_read_u32(v2m_np, "arm,msi-num-spis", &spis_nr)) + if (!rt_ofw_prop_read_u32(v2m_np, "arm,msi-base-spi", &spi_start) && + !rt_ofw_prop_read_u32(v2m_np, "arm,msi-num-spis", &spis_nr)) { LOG_I("DT overriding V2M MSI_TYPER (base:%u, num:%u)", spi_start, spis_nr); } - do { - if (spi_start && spis_nr) - { - v2m->spi_start = spi_start; - v2m->spis_nr = spis_nr; - } - else - { - rt_uint32_t typer; - - /* Graviton should always have explicit spi_start/spis_nr */ - if (v2m->flags & GICV2M_GRAVITON_ADDRESS_ONLY) - { - init_ok = RT_FALSE; - break; - } - typer = HWREG32(v2m->base + V2M_MSI_TYPER); - - v2m->spi_start = V2M_MSI_TYPER_BASE_SPI(typer); - v2m->spis_nr = V2M_MSI_TYPER_NUM_SPI(typer); - } + if (spi_start && spis_nr) + { + v2m->spi_start = spi_start; + v2m->spis_nr = spis_nr; + } + else + { + rt_uint32_t typer; - if (!is_msi_spi_valid(v2m->spi_start, v2m->spis_nr)) + /* Graviton should always have explicit spi_start/spis_nr */ + if (v2m->flags & GICV2M_GRAVITON_ADDRESS_ONLY) { - init_ok = RT_FALSE; - break; + goto _fail; } + typer = HWREG32(v2m->base + V2M_MSI_TYPER); - /* - * APM X-Gene GICv2m implementation has an erratum where - * the MSI data needs to be the offset from the spi_start - * in order to trigger the correct MSI interrupt. This is - * different from the standard GICv2m implementation where - * the MSI data is the absolute value within the range from - * spi_start to (spi_start + num_spis). - * - * Broadcom NS2 GICv2m implementation has an erratum where the MSI data - * is 'spi_number - 32' - * - * Reading that register fails on the Graviton implementation - */ - if (!(v2m->flags & GICV2M_GRAVITON_ADDRESS_ONLY)) - { - switch (HWREG32(v2m->base + V2M_MSI_IIDR)) - { - case XGENE_GICV2M_MSI_IIDR: - v2m->flags |= GICV2M_NEEDS_SPI_OFFSET; - v2m->spi_offset = v2m->spi_start; - break; - case BCM_NS2_GICV2M_MSI_IIDR: - v2m->flags |= GICV2M_NEEDS_SPI_OFFSET; - v2m->spi_offset = 32; - break; - } - } + v2m->spi_start = V2M_MSI_TYPER_BASE_SPI(typer); + v2m->spis_nr = V2M_MSI_TYPER_NUM_SPI(typer); + } - bitmap_size = RT_BITMAP_LEN(v2m->spis_nr) * sizeof(rt_bitmap_t); + if (!is_msi_spi_valid(v2m->spi_start, v2m->spis_nr)) + { + goto _fail; + } - if (!(v2m->vectors = rt_malloc(bitmap_size))) + /* + * APM X-Gene GICv2m implementation has an erratum where + * the MSI data needs to be the offset from the spi_start + * in order to trigger the correct MSI interrupt. This is + * different from the standard GICv2m implementation where + * the MSI data is the absolute value within the range from + * spi_start to (spi_start + num_spis). + * + * Broadcom NS2 GICv2m implementation has an erratum where the MSI data + * is 'spi_number - 32' + * + * Reading that register fails on the Graviton implementation + */ + if (!(v2m->flags & GICV2M_GRAVITON_ADDRESS_ONLY)) + { + switch (HWREG32(v2m->base + V2M_MSI_IIDR)) { - ret = -RT_ENOMEM; - init_ok = RT_FALSE; + case XGENE_GICV2M_MSI_IIDR: + v2m->flags |= GICV2M_NEEDS_SPI_OFFSET; + v2m->spi_offset = v2m->spi_start; + break; + + case BCM_NS2_GICV2M_MSI_IIDR: + v2m->flags |= GICV2M_NEEDS_SPI_OFFSET; + v2m->spi_offset = 32; break; } + } - rt_memset(v2m->vectors, 0, bitmap_size); - } while (0); + bitmap_size = BITMAP_LEN(v2m->spis_nr) * sizeof(bitmap_t); - if (!init_ok) + if (!(v2m->vectors = rt_calloc(1, bitmap_size))) { - rt_iounmap(v2m->base); - rt_free(v2m); - - if (ret) - { - break; - } + err = -RT_ENOMEM; + goto _fail; } - v2m->parent.chip = &gicv2m_irq_chip; - v2m->parent.chip_data = v2m; - v2m->gic = rt_of_data(node); + rt_spin_lock_init(&v2m->lock); + + v2m->parent.priv_data = v2m; + v2m->parent.ops = &gicv2m_ops; + v2m->gic = rt_ofw_data(np); + + rt_pic_linear_irq(&v2m->parent, v2m->spis_nr); + rt_pic_user_extends(&v2m->parent); + + rt_ofw_data(v2m_np) = &v2m->parent; + rt_ofw_node_set_flag(v2m_np, RT_OFW_F_READLY); - rt_spin_lock(&_v2m_lock); - rt_list_insert_after(&_v2m_nodes, &v2m->list); - rt_spin_unlock(&_v2m_lock); + continue; - rt_of_data(v2m_np) = &v2m->parent; + _fail: + rt_iounmap(v2m->base); + rt_free(v2m); - rt_of_node_set_flag(v2m_np, OF_F_READY); + if (err) + { + rt_ofw_node_put(v2m_np); + break; + } } - return ret; + return err; } diff --git a/components/drivers/pic/pic-gicv3-its.c b/components/drivers/pic/pic-gicv3-its.c index 5062799093ae..18f56874f499 100755 --- a/components/drivers/pic/pic-gicv3-its.c +++ b/components/drivers/pic/pic-gicv3-its.c @@ -10,41 +10,43 @@ #include #include +#include -#define DBG_TAG "irqchip.gic-v3-its" +#define DBG_TAG "pic.gic-v3-its" #define DBG_LVL DBG_INFO #include #include #include -#include - -#include -#include -#include -#include -#include -#include #include +#include "pic-gicv3.h" +#include "pic-gic-common.h" -#define ITS_CMD_QUEUE_SIZE (64 * KB) -#define ITS_CMD_QUEUE_ALIGN (64 * KB) +#define ITS_CMD_QUEUE_SIZE (64 * SIZE_KB) +#define ITS_CMD_QUEUE_ALIGN (64 * SIZE_KB) -#define ITS_MAX_LPI_NIRQS (64 * KB) +#define ITS_MAX_LPI_NIRQS (64 * SIZE_KB) #define ITS_MAX_LPI_NRBITS 16 /* 64K => 1 << 16 */ -#define ITS_LPI_CONFIG_TABLE_ALIGN (64 * KB) +#define ITS_LPI_CONFIG_TABLE_ALIGN (64 * SIZE_KB) #define ITS_LPI_CONFIG_PROP_DEFAULT_PRIO GICD_INT_DEF_PRI +#define ITS_LPI_CONFIG_PROP_SHIFT 2 +#define ITS_LPI_CONFIG_PROP_MASK RT_GENMASK(7, ITS_LPI_CONFIG_PROP_SHIFT) #define RDIST_FLAGS_PROPBASE_NEEDS_FLUSHING (1 << 0) #define RDIST_FLAGS_RD_TABLES_PREALLOCATED (1 << 1) +#define RDIST_FLAGS_FORCE_NON_SHAREABLE (1 << 2) #define ITS_FLAGS_CMDQ_NEEDS_FLUSHING (1ULL << 0) #define ITS_FLAGS_WORKAROUND_CAVIUM_22375 (1ULL << 1) -#define ITS_FLAGS_WORKAROUND_CAVIUM_23144 (1ULL << 2) +#define ITS_FLAGS_FORCE_NON_SHAREABLE (1ULL << 2) + +#define RD_LOCAL_LPI_ENABLED RT_BIT(0) +#define RD_LOCAL_PENDTABLE_PREALLOCATED RT_BIT(1) +#define RD_LOCAL_MEMRESERVE_DONE RT_BIT(2) struct gicv3_its { - struct rt_irq_chip_desc parent; + struct rt_pic parent; rt_list_t list; void *base; @@ -55,24 +57,32 @@ struct gicv3_its rt_uint32_t flags; rt_size_t lpis; - void *conf_table; - rt_size_t conf_table_size; + rt_size_t lpis_irq_base; + + void *lpi_table; + void *lpi_pending_table; + rt_size_t lpi_table_size; struct rt_spinlock lock; struct gicv3 *gic; + struct rt_ofw_node *np; }; -static rt_list_t _its_nodes = RT_LIST_OBJECT_INIT(_its_nodes); +#define raw_to_gicv3_its(raw) rt_container_of(raw, struct gicv3_its, parent) + +static rt_list_t its_nodes = RT_LIST_OBJECT_INIT(its_nodes); rt_inline void its_readq(struct gicv3_its *its, int off, rt_uint64_t *out_value) { - *out_value = HWREG64(its->base + off); + *out_value = HWREG32(its->base + off); + *out_value |= (rt_uint64_t)HWREG32(its->base + off + 4) << 32; } rt_inline void its_writeq(struct gicv3_its *its, int off, rt_uint64_t value) { - HWREG64(its->base + off) = value; + HWREG32(its->base + off) = (rt_uint32_t)value; + HWREG32(its->base + off + 4) = (rt_uint32_t)(value >> 32); } rt_inline void its_readl(struct gicv3_its *its, int off, rt_uint32_t *out_value) @@ -85,150 +95,387 @@ rt_inline void its_writel(struct gicv3_its *its, int off, rt_uint32_t value) HWREG32(its->base + off) = value; } -static rt_err_t gicv3_its_irq_init(void) +rt_inline void *lpi_base_config(struct gicv3_its *its, int index) +{ + return &((rt_uint8_t *)its->lpi_table)[index + its->lpis_irq_base - 8192]; +} + +rt_inline void lpi_flush_config(struct gicv3_its *its, rt_uint8_t *conf) { + if ((its->gic->redist_flags & RDIST_FLAGS_PROPBASE_NEEDS_FLUSHING)) + { + /* Clean D-cache under command. */ + rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, conf, sizeof(*conf)); + } + else + { + /* DSB inner shareable, store */ + rt_hw_wmb(); + } +} + +rt_inline void *gicr_rd_base(struct gicv3_its *its) +{ + return its->gic->redist_percpu_base[rt_hw_cpu_id()]; +} + +rt_inline rt_uint64_t *gicr_rd_flags(struct gicv3_its *its) +{ + return &its->gic->redist_percpu_flags[rt_hw_cpu_id()]; +} + +static rt_bool_t gicr_supports_plpis(struct gicv3_its *its) +{ + return !!(HWREG64(gicr_rd_base(its) + GICR_TYPER) & GICR_TYPER_PLPIS); +} + +static rt_err_t redist_disable_lpis(struct gicv3_its *its) +{ + void *gicr = gicr_rd_base(its); + rt_uint64_t timeout = 1000000L, val; + + if (!gicr_supports_plpis(its)) + { + LOG_I("CPU#%d: LPIs not supported", rt_hw_cpu_id()); + return -RT_ENOSYS; + } + + val = HWREG32(gicr + GICR_CTLR); + if (!(val & GICR_CTLR_ENABLE_LPIS)) + { + return RT_EOK; + } + + /* + * If coming via a CPU hotplug event, we don't need to disable + * LPIs before trying to re-enable them. They are already + * configured and all is well in the world. + * + * If running with preallocated tables, there is nothing to do. + */ + if ((*gicr_rd_flags(its) & RD_LOCAL_LPI_ENABLED) || + (its->gic->flags & RDIST_FLAGS_RD_TABLES_PREALLOCATED)) + { + return RT_EOK; + } + + /* + * From that point on, we only try to do some damage control. + */ + LOG_W("CPU%d: Booted with LPIs enabled, memory probably corrupted", rt_hw_cpu_id()); + + /* Disable LPIs */ + val &= ~GICR_CTLR_ENABLE_LPIS; + HWREG32(gicr + GICR_CTLR) = val; + + /* Make sure any change to GICR_CTLR is observable by the GIC */ + rt_hw_barrier(dsb, sy); + + /* + * Software must observe RWP==0 after clearing GICR_CTLR.EnableLPIs + * from 1 to 0 before programming GICR_PEND{PROP}BASER registers. + * Error out if we time out waiting for RWP to clear. + */ + while (HWREG32(gicr + GICR_CTLR) & GICR_CTLR_RWP) + { + if (!timeout) + { + LOG_E("CPU#%d: Timeout while disabling LPIs", rt_hw_cpu_id()); + + return -RT_ETIMEOUT; + } + + rt_hw_us_delay(1); + --timeout; + } + + /* + * After it has been written to 1, it is IMPLEMENTATION + * DEFINED whether GICR_CTLR.EnableLPI becomes RES1 or can be + * cleared to 0. Error out if clearing the bit failed. + */ + if (HWREG32(gicr + GICR_CTLR) & GICR_CTLR_ENABLE_LPIS) + { + LOG_E("CPU%d: Failed to disable LPIs", rt_hw_cpu_id()); + + return -RT_EBUSY; + } + + return RT_EOK; +} + +static void gicv3_its_cpu_init_lpis(struct gicv3_its *its) +{ + void *gicr; + rt_ubase_t paddr; + rt_uint64_t val, tmp; + + if (*gicr_rd_flags(its) & RD_LOCAL_LPI_ENABLED) + { + return; + } + + gicr = gicr_rd_base(its); + + val = HWREG32(gicr + GICR_CTLR); + + if ((its->gic->redist_flags & RDIST_FLAGS_RD_TABLES_PREALLOCATED) && + (val & GICR_CTLR_ENABLE_LPIS)) + { + *gicr_rd_flags(its) |= RD_LOCAL_PENDTABLE_PREALLOCATED; + + goto _out; + } + + paddr = (rt_ubase_t)rt_kmem_v2p(its->lpi_pending_table); + + /* set PROPBASE */ + val = ((rt_ubase_t)rt_kmem_v2p(its->lpi_table) | + GITS_CBASER_InnerShareable | + GITS_CBASER_RaWaWb | + ((ITS_MAX_LPI_NRBITS - 1) & GICR_PROPBASER_IDBITS_MASK)); + + HWREG64(gicr + GICR_PROPBASER) = val; + tmp = HWREG64(gicr + GICR_PROPBASER); + + if (its->gic->redist_flags & RDIST_FLAGS_FORCE_NON_SHAREABLE) + { + tmp &= ~GICR_PBASER_SHARE_MASK_ALL; + } + + if ((tmp ^ val) & GICR_PBASER_SHARE_MASK_ALL) + { + if (!(tmp & GICR_PBASER_SHARE_MASK_ALL)) + { + /* The HW reports non-shareable, we must remove the cacheability attributes as well */ + val &= ~(GICR_PBASER_SHARE_MASK_ALL | GICR_PBASER_INNER_MASK_ALL); + val |= GICR_PBASER_nC; + HWREG64(gicr + GICR_PROPBASER) = val; + } + + LOG_I("Using cache flushing for LPI property table"); + its->gic->redist_flags |= RDIST_FLAGS_PROPBASE_NEEDS_FLUSHING; + } + + val = (paddr | GICR_PBASER_InnerShareable | GICR_PBASER_RaWaWb); + + HWREG64(gicr + GICR_PENDBASER) = val; + tmp = HWREG64(gicr + GICR_PENDBASER); + + if (its->gic->redist_flags & RDIST_FLAGS_FORCE_NON_SHAREABLE) + { + tmp &= ~GICR_PBASER_SHARE_MASK_ALL; + } + + if (!(tmp & GICR_PBASER_SHARE_MASK_ALL)) + { + /* + * The HW reports non-shareable, we must remove the + * cacheability attributes as well. + */ + val &= ~(GICR_PBASER_SHARE_MASK_ALL | GICR_PBASER_INNER_MASK_ALL); + val |= GICR_PBASER_nC; + HWREG64(gicr + GICR_PENDBASER) = val; + } + + /* Enable LPIs */ + val = HWREG32(gicr + GICR_CTLR); + val |= GICR_CTLR_ENABLE_LPIS; + HWREG32(gicr + GICR_CTLR) = val; + + rt_hw_barrier(dsb, sy); + +_out: + *gicr_rd_flags(its) |= RD_LOCAL_LPI_ENABLED; +} + +static rt_err_t gicv3_its_irq_init(struct rt_pic *pic) +{ + rt_err_t err; + struct gicv3_its *its = raw_to_gicv3_its(pic); + + if ((err = redist_disable_lpis(its))) + { + return err; + } + + gicv3_its_cpu_init_lpis(its); + return RT_EOK; } -static void gicv3_its_irq_mask(struct rt_irq_data *data) +static void gicv3_its_irq_mask(struct rt_pic_irq *pirq) { - rt_uint8_t *conf; - struct gicv3_its *its = data->chip_data; + struct gicv3_its *its = raw_to_gicv3_its(pirq->pic); + rt_uint8_t *conf = lpi_base_config(its, pirq->hwirq); + + *conf &= ~GITS_LPI_CFG_ENABLED; + lpi_flush_config(its, conf); + + // its_cmd_inv(dev, girq->gi_its_dev, girq); } -static void gicv3_its_irq_unmask(struct rt_irq_data *data) +static void gicv3_its_irq_unmask(struct rt_pic_irq *pirq) { + struct gicv3_its *its = raw_to_gicv3_its(pirq->pic); + rt_uint8_t *conf = lpi_base_config(its, pirq->hwirq); + + *conf |= GITS_LPI_CFG_ENABLED; + lpi_flush_config(its, conf); + // its_cmd_inv(dev, girq->gi_its_dev, girq); } -static rt_err_t gicv3_its_set_priority(struct rt_irq_data *data, rt_uint32_t priority) +static rt_err_t gicv3_its_set_priority(struct rt_pic_irq *pirq, rt_uint32_t priority) { - rt_err_t ret = RT_EOK; + struct gicv3_its *its = raw_to_gicv3_its(pirq->pic); + rt_uint8_t *conf = lpi_base_config(its, pirq->hwirq); - return ret; + *conf = (priority << ITS_LPI_CONFIG_PROP_SHIFT) | (*conf & (~ITS_LPI_CONFIG_PROP_MASK)); + lpi_flush_config(its, conf); + + // its_cmd_inv(dev, girq->gi_its_dev, girq); + + return RT_EOK; } -static rt_err_t gicv3_its_set_affinity(struct rt_irq_data *data, rt_bitmap_t *affinity) +static rt_err_t gicv3_its_set_affinity(struct rt_pic_irq *pirq, bitmap_t *affinity) { - rt_err_t ret = RT_EOK; + rt_err_t err = RT_EOK; - return ret; + return err; } -static void gicv3_its_irq_compose_msi_msg(struct rt_irq_data *data, struct msi_msg *msg) +static void gicv3_its_irq_compose_msi_msg(struct rt_pic_irq *pirq, struct rt_pci_msi_msg *msg) { } -static int gicv3_its_irq_alloc_msi(struct rt_irq_chip_desc *chip_desc, struct msi_desc *msi_desc) +static int gicv3_its_irq_alloc_msi(struct rt_pic *pic, struct rt_pci_msi_desc *msi_desc) { - int ret = -1; + int irq = -1; - return ret; + return irq; } -static void gicv3_its_irq_free_msi(struct rt_irq_chip_desc *chip_desc, int irq) +static void gicv3_its_irq_free_msi(struct rt_pic *pic, int irq) { } -static struct rt_irq_chip gicv3_its_irq_chip = +const static struct rt_pic_ops gicv3_its_ops = { .name = "GICv3-ITS", .irq_init = gicv3_its_irq_init, - .irq_ack = rt_irqchip_irq_parent_ack, + .irq_ack = rt_pic_irq_parent_ack, .irq_mask = gicv3_its_irq_mask, .irq_unmask = gicv3_its_irq_unmask, - .irq_eoi = rt_irqchip_irq_parent_eoi, + .irq_eoi = rt_pic_irq_parent_eoi, .irq_set_priority = gicv3_its_set_priority, .irq_set_affinity = gicv3_its_set_affinity, .irq_compose_msi_msg = gicv3_its_irq_compose_msi_msg, .irq_alloc_msi = gicv3_its_irq_alloc_msi, .irq_free_msi = gicv3_its_irq_free_msi, + .flags = RT_PIC_F_IRQ_ROUTING, }; static rt_err_t its_cmd_queue_init(struct gicv3_its *its) { - rt_err_t ret = RT_EOK; + void *cmd_phy_base; + rt_uint32_t ctlr; + rt_uint64_t baser, tmp; its->cmd_base = rt_malloc_align(ITS_CMD_QUEUE_SIZE, ITS_CMD_QUEUE_ALIGN); - if (its->cmd_base) + if (!its->cmd_base) { - void *cmd_phy_base; - rt_uint64_t baser, tmp; + return -RT_ENOMEM; + } - its->cmd_idx = 0; - its->flags = 0; + its->cmd_idx = 0; + its->flags = 0; - cmd_phy_base = rt_kmem_v2p(its->cmd_base); + cmd_phy_base = rt_kmem_v2p(its->cmd_base); - baser = GITS_CBASER_VALID | GITS_CBASER_RaWaWb | GITS_CBASER_InnerShareable | \ - ((rt_uint64_t)cmd_phy_base) | (ITS_CMD_QUEUE_SIZE / (4 * KB) - 1); + baser = GITS_CBASER_VALID | GITS_CBASER_RaWaWb | GITS_CBASER_InnerShareable | \ + ((rt_uint64_t)cmd_phy_base) | (ITS_CMD_QUEUE_SIZE / (4 * SIZE_KB) - 1); - its_writeq(its, GITS_CBASER, baser); + its_writeq(its, GITS_CBASER, baser); - its_readq(its, GITS_CBASER, &tmp); + its_readq(its, GITS_CBASER, &tmp); - if ((tmp & GITS_BASER_SHAREABILITY(GITS_CBASER, SHARE_MASK)) != GITS_CBASER_InnerShareable) + if (its->flags & ITS_FLAGS_FORCE_NON_SHAREABLE) + { + tmp &= ~GITS_CBASER_SHARE_MASK_ALL; + } + + if ((tmp ^ baser) & GITS_CBASER_SHARE_MASK_ALL) + { + if (!(tmp & GITS_CBASER_SHARE_MASK_ALL)) { + /* The HW reports non-shareable, we must remove the cacheability attributes as well */ + baser &= ~(GITS_CBASER_SHARE_MASK_ALL | GITS_CBASER_INNER_MASK_ALL); + baser |= GITS_CBASER_nC; + + its_writeq(its, GITS_CBASER, baser); } - /* Get the next command from the start of the buffer */ - its_writeq(its, GITS_CWRITER, 0); + LOG_I("Using cache flushing for cmd queue"); + its->flags |= ITS_FLAGS_CMDQ_NEEDS_FLUSHING; + } + + /* Get the next command from the start of the buffer */ + its_writeq(its, GITS_CWRITER, 0); + + its_readl(its, GITS_CTLR, &ctlr); + ctlr |= GITS_CTLR_ENABLE; + its_writel(its, GITS_CTLR, ctlr); + + return RT_EOK; +} + +static rt_err_t its_lpi_table_init(struct gicv3_its *its) +{ + rt_uint32_t lpis, id_bits, numlpis = 1UL << GICD_TYPER_NUM_LPIS(its->gic->gicd_typer); + + if ((its->gic->redist_flags & RDIST_FLAGS_RD_TABLES_PREALLOCATED)) + { + rt_uint64_t val = HWREG64(gicr_rd_base(its) + GICR_PROPBASER); + id_bits = (val & GICR_PROPBASER_IDBITS_MASK) + 1; } else { - ret = -RT_ENOMEM; + id_bits = rt_min_t(rt_uint32_t, GICD_TYPER_ID_BITS(its->gic->gicd_typer), ITS_MAX_LPI_NRBITS); } - return ret; -} + lpis = (1UL << id_bits) - 8192; -static rt_err_t its_conftable_init(struct gicv3_its *its) -{ - rt_err_t ret = RT_EOK; - // void *conf_table; - // rt_uint32_t lpis, id_bits, numlpis = 1UL << GICD_TYPER_NUM_LPIS(its->gic->gicd_typer); - - // if (its->gic->redist_flags & RDIST_FLAGS_RD_TABLES_PREALLOCATED) - // { - // rt_uint64_t val = gicr_read_propbaser(gic_data_rdist_rd_base() + GICR_PROPBASER); - // id_bits = (val & GICR_PROPBASER_IDBITS_MASK) + 1; - // } - // else - // { - // id_bits = min_t(rt_uint32_t, GICD_TYPER_ID_BITS(its->gic->gicd_typer), ITS_MAX_LPI_NRBITS); - // } - - // lpis = (1UL << id_bits) - 8192; - - // if (numlpis > 2 && numlpis > lpis) - // { - // lpis = numlpis; - // LOG_W("Using hypervisor restricted LPI range [%u]", lpis); - // } - - // its->lpis = lpis; - // its->conf_table_size = lpis; /* LPI Configuration table entry is 1 byte */ - // its->conf_table = rt_malloc_align(its->conf_table_size, ITS_LPI_CONFIG_TABLE_ALIGN); - - // if (its->conf_table) - // { - // /* Set the default configuration */ - // rt_memset(its->conf_table, ITS_LPI_CONFIG_PROP_DEFAULT_PRIO | GITS_LPI_CFG_GROUP1, its->conf_table_size); - - // /* Initializing the allocator is just the same as freeing the full range of LPIs. */ - - // /* Flush the table to memory */ - // rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, its->conf_table, its->conf_table_size); - - // LOG_D("ITS: Allocator initialized for %u LPIs", lpis); - // } - // else - // { - // ret = -RT_ENOMEM; - // } - - return ret; + if (numlpis > 2 && numlpis > lpis) + { + lpis = numlpis; + LOG_W("Using hypervisor restricted LPI range [%u]", lpis); + } + + its->lpis = lpis; + its->lpi_table_size = lpis; /* LPI Configuration table entry is 1 byte */ + its->lpi_table = rt_malloc_align(its->lpi_table_size * 2, ITS_LPI_CONFIG_TABLE_ALIGN); + + if (!its->lpi_table) + { + return -RT_ENOMEM; + } + + its->lpi_pending_table = its->lpi_table + its->lpi_table_size; + + /* Set the default configuration */ + rt_memset(its->lpi_table, ITS_LPI_CONFIG_PROP_DEFAULT_PRIO | GITS_LPI_CFG_GROUP1, its->lpi_table_size); + + /* Initializing the allocator is just the same as freeing the full range of LPIs. */ + + /* Flush the table to memory */ + rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, its->lpi_table, its->lpi_table_size); + + LOG_D("ITS: Allocator initialized for %u LPIs", lpis); + + return RT_EOK; } static void its_init_fail(struct gicv3_its *its) @@ -246,91 +493,168 @@ static void its_init_fail(struct gicv3_its *its) rt_free(its); } +static rt_err_t its_quirk_cavium_22375(void *data) +{ + struct gicv3_its *its = data; + + its->flags |= ITS_FLAGS_WORKAROUND_CAVIUM_22375; + + return RT_EOK; +} + +static rt_err_t its_enable_rk3588001(void *data) +{ + struct gicv3_its *its = data; + struct gicv3 *gic = its->gic; + + if (!rt_ofw_machine_is_compatible("rockchip,rk3588") && + !rt_ofw_machine_is_compatible("rockchip,rk3588s")) + { + return -RT_EINVAL; + } + + its->flags |= ITS_FLAGS_FORCE_NON_SHAREABLE; + gic->redist_flags |= RDIST_FLAGS_FORCE_NON_SHAREABLE; + + return RT_EOK; +} + +static rt_err_t its_set_non_coherent(void *data) +{ + struct gicv3_its *its = data; + + if (!rt_ofw_prop_read_bool(its->np, "dma-noncoherent")) + { + return -RT_EINVAL; + } + + its->flags |= ITS_FLAGS_FORCE_NON_SHAREABLE; + + return RT_EOK; +} + static const struct gic_quirk _its_quirks[] = { + { + .desc = "ITS: Cavium ThunderX errata: 22375, 24313", + .iidr = 0xa100034c, + .iidr_mask = 0xffff0fff, + .init = its_quirk_cavium_22375, + }, + { + .desc = "ITS: Rockchip erratum RK3588001", + .iidr = 0x0201743b, + .iidr_mask = 0xffffffff, + .init = its_enable_rk3588001, + }, + { + .desc = "ITS: non-coherent attribute", + .compatible = "arm,gic-v3-its", + .init = its_set_non_coherent, + }, { /* sentinel */ } }; -static const struct rt_of_device_id gicv3_its_of_match[] = +static const struct rt_ofw_node_id gicv3_its_ofw_match[] = { { .compatible = "arm,gic-v3-its" }, { /* sentinel */ } }; -rt_err_t gicv3_its_of_probe(struct rt_device_node *node, const struct rt_of_device_id *id) +rt_err_t gicv3_its_ofw_probe(struct rt_ofw_node *np, const struct rt_ofw_node_id *id) { - rt_err_t ret = -RT_EEMPTY; - rt_uint64_t regs[2]; - struct rt_device_node *its_np; + rt_err_t err = -RT_EEMPTY; + struct gicv3_its *its; + struct rt_ofw_node *its_np; - rt_of_for_each_child_of_node(node, its_np) + rt_ofw_foreach_available_child_node(np, its_np) { - struct gicv3_its *its; - - if (!rt_of_device_is_available(its_np)) + if (!rt_ofw_node_match(its_np, gicv3_its_ofw_match)) { continue; } - if (!rt_of_match_node(gicv3_its_of_match, its_np)) + if (!rt_ofw_prop_read_bool(its_np, "msi-controller")) { continue; } - if (!rt_of_find_property(its_np, "msi-controller", RT_NULL)) + if (!(its = rt_malloc(sizeof(struct gicv3_its)))) { - continue; + rt_ofw_node_put(its_np); + + err = -RT_ENOMEM; + goto _free_all; } - rt_memset(regs, 0, sizeof(regs)); + its->base = rt_ofw_iomap(its_np, 0); - if (rt_of_address_to_array(its_np, regs) < 0) + if (!its->base) { + LOG_E("%s: IO map failed", rt_ofw_node_full_name(its_np)); + its_init_fail(its); continue; } - if (!(its = rt_malloc(sizeof(struct gicv3_its)))) + /* Make sure ALL the ITS are reset before we probe any, as they may be sharing memory */ + for (int i = 0; i < GITS_TRANSLATION_TABLE_DESCRIPTORS_NR; ++i) { - ret = -RT_ENOMEM; - break; + its_writeq(its, GITS_BASER + (i << 3), 0); } + its->np = its_np; rt_list_init(&its->list); - its->base_phy = (void *)regs[0]; - its->base = rt_ioremap(its->base_phy, regs[1]); + rt_list_insert_before(&its_nodes, &its->list); + } - its->parent.chip = &gicv3_its_irq_chip; - its->parent.chip_data = its; - its->gic = rt_of_data(node); + rt_list_for_each_entry(its, &its_nodes, list) + { + its->base_phy = rt_kmem_v2p(its->base); + its->gic = rt_ofw_data(np); - if (its_conftable_init(its)) + if ((err = its_lpi_table_init(its))) { - its_init_fail(its); - continue; + goto _fail; } - gic_common_init_quirk_hw(HWREG32(its->base + GITS_IIDR), _its_quirks, its->gic); + gic_common_init_quirk_hw(HWREG32(its->base + GITS_IIDR), _its_quirks, its); + gic_common_init_quirk_ofw(its->np, _its_quirks, its); - if (its_cmd_queue_init(its)) + if ((err = its_cmd_queue_init(its))) { - its_init_fail(its); - continue; + goto _fail; } rt_spin_lock_init(&its->lock); - rt_list_insert_after(&_its_nodes, &its->list); - rt_of_data(its_np) = &its->parent; - rt_of_node_set_flag(its_np, OF_F_READY); + its->parent.priv_data = its; + its->parent.ops = &gicv3_its_ops; + + rt_pic_linear_irq(&its->parent, its->gic->lpi_nr); + rt_pic_user_extends(&its->parent); + + its_np = its->np; + rt_ofw_data(its_np) = &its->parent; + rt_ofw_node_set_flag(its_np, RT_OFW_F_READLY); + + continue; - ret = RT_EOK; + _fail: + its_init_fail(its); + + if (err == -RT_ENOMEM) + { + break; + } } - if (!ret) + return err; + +_free_all: + rt_list_for_each_entry(its, &its_nodes, list) { - /* Install forced that call `gicv3_its_irq_init` auto */ - rt_irqchip_install_forced(&gicv3_its_irq_chip); + rt_free(its); } - return ret; + return err; } diff --git a/components/drivers/pic/pic-gicv3.c b/components/drivers/pic/pic-gicv3.c index d9249423ce26..4a4470dc06b6 100644 --- a/components/drivers/pic/pic-gicv3.c +++ b/components/drivers/pic/pic-gicv3.c @@ -35,6 +35,7 @@ static int _init_cpu_id; static struct gicv3 _gic; static rt_bool_t _gicv3_eoi_mode_ns = RT_FALSE; +static rt_bool_t _gicv3_arm64_2941627_erratum = RT_FALSE; enum { @@ -257,7 +258,20 @@ static void gicv3_dist_init(void) HWREG64(base + GICD_IROUTERnE + i * 8) = affinity; } - _gic.max_irq = rt_min_t(int, MAX_HANDLERS - 1, RT_GENMASK(23, 0)); + if (GICD_TYPER_NUM_LPIS(_gic.gicd_typer)) + { + /* Max LPI = 8192 + Math.pow(2, num_LPIs + 1) - 1 */ + rt_size_t num_lpis = (1 << (GICD_TYPER_NUM_LPIS(_gic.gicd_typer) + 1)) + 1; + + _gic.lpi_nr = rt_min_t(int, num_lpis, 1 << GICD_TYPER_ID_BITS(_gic.gicd_typer)); + } + else + { + _gic.lpi_nr = 1 << GICD_TYPER_ID_BITS(_gic.gicd_typer); + } + + /* SPI + eSPI + LPIs */ + _gic.irq_nr = _gic.line_nr - 32 + _gic.espi_nr + _gic.lpi_nr; } static void gicv3_redist_enable(rt_bool_t enable) @@ -455,8 +469,11 @@ static void gicv3_irq_eoi(struct rt_pic_irq *pirq) write_gicreg(ICC_EOIR1_SYS, hwirq); rt_hw_isb(); - write_gicreg(ICC_DIR_SYS, hwirq); - rt_hw_isb(); + if (!_gicv3_arm64_2941627_erratum) + { + write_gicreg(ICC_DIR_SYS, hwirq); + rt_hw_isb(); + } } } } @@ -477,7 +494,7 @@ static rt_err_t gicv3_irq_set_priority(struct rt_pic_irq *pirq, rt_uint32_t prio } offset = gicv3_hwirq_convert_offset_index(hwirq, GICD_IPRIORITYR, &index); - HWREG32(base + offset + index) = priority; + HWREG8(base + offset + index) = priority; return RT_EOK; } @@ -485,7 +502,7 @@ static rt_err_t gicv3_irq_set_priority(struct rt_pic_irq *pirq, rt_uint32_t prio static rt_err_t gicv3_irq_set_affinity(struct rt_pic_irq *pirq, bitmap_t *affinity) { rt_err_t ret = RT_EOK; - rt_uint64_t val; + rt_uint64_t val; rt_ubase_t mpidr; rt_uint32_t offset, index; int hwirq = pirq->hwirq, cpu_id = bitmap_next_set_bit(affinity, 0, RT_CPUS_NR); @@ -537,7 +554,7 @@ static void gicv3_irq_send_ipi(struct rt_pic_irq *pirq, bitmap_t *cpumask) { #define __mpidr_to_sgi_affinity(cluster_id, level) \ (MPIDR_AFFINITY_LEVEL(cluster_id, level) << ICC_SGI1R_AFFINITY_##level##_SHIFT) - int cpu_id, limit; + int cpu_id, last_cpu_id, limit; rt_uint64_t initid, range_sel, target_list, cluster_id; range_sel = 0; @@ -551,6 +568,7 @@ static void gicv3_irq_send_ipi(struct rt_pic_irq *pirq, bitmap_t *cpumask) target_list = 1 << ((mpidr & MPIDR_LEVEL_MASK) % ICC_SGI1R_TARGET_LIST_MAX); limit = rt_min(cpu_id + ICC_SGI1R_TARGET_LIST_MAX, RT_CPUS_NR); + last_cpu_id = cpu_id; bitmap_for_each_set_bit_from(cpumask, cpu_id, cpu_id, limit) { rt_uint64_t mpidr = rt_cpu_mpidr_table[cpu_id]; @@ -558,9 +576,12 @@ static void gicv3_irq_send_ipi(struct rt_pic_irq *pirq, bitmap_t *cpumask) if (cluster_id != (mpidr & (~MPIDR_LEVEL_MASK))) { range_sel = 0; + /* Don't break next cpuid */ + cpu_id = last_cpu_id; break; } + last_cpu_id = cpu_id; target_list |= 1 << ((mpidr & MPIDR_LEVEL_MASK) % ICC_SGI1R_TARGET_LIST_MAX); } @@ -581,8 +602,19 @@ static void gicv3_irq_send_ipi(struct rt_pic_irq *pirq, bitmap_t *cpumask) static int gicv3_irq_map(struct rt_pic *pic, int hwirq, rt_uint32_t mode) { - int irq, irq_index = hwirq - GIC_SGI_NR; - struct rt_pic_irq *pirq = rt_pic_find_irq(pic, irq_index); + struct rt_pic_irq *pirq; + int irq, hwirq_type, irq_index; + + hwirq_type = gicv3_hwirq_type(hwirq); + if (hwirq_type != LPI_TYPE) + { + irq_index = hwirq - GIC_SGI_NR; + } + else + { + irq_index = _gic.irq_nr - _gic.lpi_nr + hwirq - 8192; + } + pirq = rt_pic_find_irq(pic, irq_index); if (pirq && hwirq >= GIC_SGI_NR) { @@ -599,6 +631,11 @@ static int gicv3_irq_map(struct rt_pic *pic, int hwirq, rt_uint32_t mode) } irq = rt_pic_config_irq(pic, irq_index, hwirq); + + if (irq >= 0 && mode != RT_IRQ_MODE_LEVEL_HIGH) + { + gicv3_irq_set_triger_mode(pirq, mode); + } } else { @@ -664,7 +701,7 @@ static rt_err_t gicv3_irq_parse(struct rt_pic *pic, struct rt_ofw_cell_args *arg return err; } -static struct rt_pic_ops gicv3_ops = +const static struct rt_pic_ops gicv3_ops = { .name = "GICv3", .irq_init = gicv3_irq_init, @@ -724,6 +761,12 @@ static rt_err_t gicv3_enable_quirk_msm8996(void *data) return RT_EOK; } +static rt_err_t gicv3_enable_quirk_arm64_2941627(void *data) +{ + _gicv3_arm64_2941627_erratum = RT_TRUE; + return RT_EOK; +} + static const struct gic_quirk _gicv3_quirks[] = { { @@ -731,6 +774,20 @@ static const struct gic_quirk _gicv3_quirks[] = .compatible = "qcom,msm8996-gic-v3", .init = gicv3_enable_quirk_msm8996, }, + { + /* GIC-700: 2941627 workaround - IP variant [0,1] */ + .desc = "GICv3: ARM64 erratum 2941627", + .iidr = 0x0400043b, + .iidr_mask = 0xff0e0fff, + .init = gicv3_enable_quirk_arm64_2941627, + }, + { + /* GIC-700: 2941627 workaround - IP variant [2] */ + .desc = "GICv3: ARM64 erratum 2941627", + .iidr = 0x0402043b, + .iidr_mask = 0xff0f0fff, + .init = gicv3_enable_quirk_arm64_2941627, + }, { /* sentinel */ } }; @@ -786,7 +843,6 @@ static rt_err_t gicv3_iomap_init(rt_uint64_t *regs) /* ArchRev[4:7] */ _gic.version = HWREG32(_gic.dist_base + GICD_PIDR2) >> 4; - gic_common_init_quirk_hw(HWREG32(_gic.dist_base + GICD_IIDR), _gicv3_quirks, &_gic.parent); } while (0); if (ret && idx >= 0) @@ -825,7 +881,7 @@ static void gicv3_init(void) _gic.parent.priv_data = &_gic; _gic.parent.ops = &gicv3_ops; - rt_pic_linear_irq(&_gic.parent, _gic.max_irq + 1 - GIC_SGI_NR); + rt_pic_linear_irq(&_gic.parent, _gic.irq_nr - GIC_SGI_NR); gic_common_sgi_config(_gic.dist_base, &_gic.parent, 0); rt_pic_add_traps(gicv3_handler, &_gic); @@ -857,11 +913,11 @@ static void gicv3_init_fail(void) static rt_err_t gicv3_ofw_init(struct rt_ofw_node *np, const struct rt_ofw_node_id *id) { - rt_err_t ret = RT_EOK; + rt_err_t err = RT_EOK; do { rt_size_t reg_nr_max; - rt_err_t msi_init = -RT_ENOSYS; + rt_err_t msi_init = -RT_ENOSYS; rt_uint32_t redist_regions_nr; rt_uint64_t *regs, redist_stride; @@ -876,14 +932,17 @@ static rt_err_t gicv3_ofw_init(struct rt_ofw_node *np, const struct rt_ofw_node_ if (!regs) { - ret = -RT_ENOMEM; + err = -RT_ENOMEM; break; } rt_ofw_get_address_array(np, reg_nr_max, regs); _gic.redist_regions_nr = redist_regions_nr; - if ((ret = gicv3_iomap_init(regs))) + err = gicv3_iomap_init(regs); + rt_free(regs); + + if (err) { break; } @@ -891,7 +950,7 @@ static rt_err_t gicv3_ofw_init(struct rt_ofw_node *np, const struct rt_ofw_node_ if (_gic.version != 3 && _gic.version != 4) { LOG_E("Version = %d is not support", _gic.version); - ret = -RT_EINVAL; + err = -RT_EINVAL; break; } @@ -919,12 +978,12 @@ static rt_err_t gicv3_ofw_init(struct rt_ofw_node *np, const struct rt_ofw_node_ } } while (0); - if (ret) + if (err) { gicv3_init_fail(); } - return ret; + return err; } static const struct rt_ofw_node_id gicv3_ofw_ids[] = diff --git a/components/drivers/pic/pic-gicv3.h b/components/drivers/pic/pic-gicv3.h index 53c03916090a..4a7250eb82ce 100644 --- a/components/drivers/pic/pic-gicv3.h +++ b/components/drivers/pic/pic-gicv3.h @@ -124,12 +124,12 @@ #define GICR_TYPER_DirectLPIS (1U << 3) #define GICR_TYPER_LAST (1U << 4) #define GICR_TYPER_RVPEID (1U << 7) -#define GICR_TYPER_COM_LPI_AFF GENMASK_ULL(25, 24) -#define GICR_TYPER_AFFINITY GENMASK_ULL(63, 32) +#define GICR_TYPER_COM_LPI_AFF RT_GENMASK_ULL(25, 24) +#define GICR_TYPER_AFFINITY RT_GENMASK_ULL(63, 32) -#define GICR_INVLPIR_INTID GENMASK_ULL(31, 0) -#define GICR_INVLPIR_VPEID GENMASK_ULL(47, 32) -#define GICR_INVLPIR_V GENMASK_ULL(63, 63) +#define GICR_INVLPIR_INTID RT_GENMASK_ULL(31, 0) +#define GICR_INVLPIR_VPEID RT_GENMASK_ULL(47, 32) +#define GICR_INVLPIR_V RT_GENMASK_ULL(63, 63) #define GICR_INVALLR_VPEID GICR_INVLPIR_VPEID #define GICR_INVALLR_V GICR_INVLPIR_V @@ -145,70 +145,109 @@ #define GICR_WAKER_ChildrenAsleep (1U << 2) #define GICR_PROPBASER_IDBITS_MASK (0x1f) -#define GICR_PROPBASER_ADDRESS(x) ((x) & GENMASK_ULL(51, 12)) -#define GICR_PENDBASER_ADDRESS(x) ((x) & GENMASK_ULL(51, 16)) +#define GICR_PROPBASER_ADDRESS(x) ((x) & RT_GENMASK_ULL(51, 12)) +#define GICR_PENDBASER_ADDRESS(x) ((x) & RT_GENMASK_ULL(51, 16)) /* ITS registers */ #define GITS_CTLR 0x0000 #define GITS_IIDR 0x0004 #define GITS_TYPER 0x0008 -#define GITS_MPAMIDR 0x0010 -#define GITS_PARTIDR 0x0014 #define GITS_MPIDR 0x0018 -#define GITS_STATUSR 0x0040 -#define GITS_UMSIR 0x0048 -#define GITS_CBASER 0x0048 +#define GITS_CBASER 0x0080 #define GITS_CWRITER 0x0088 #define GITS_CREADR 0x0090 -#define GITS_BASER 0x0100 /* 0x0100~0x0138 */ +#define GITS_BASER 0x0100 +#define GITS_IDREGS_BASE 0xffd0 +#define GITS_PIDR0 0xffe0 +#define GITS_PIDR1 0xffe4 +#define GITS_PIDR2 GICR_PIDR2 +#define GITS_PIDR4 0xffd0 +#define GITS_CIDR0 0xfff0 +#define GITS_CIDR1 0xfff4 +#define GITS_CIDR2 0xfff8 +#define GITS_CIDR3 0xfffc + +#define GITS_TRANSLATER 0x10040 + +#define GITS_SGIR 0x20020 + +#define GITS_SGIR_VPEID RT_GENMASK_ULL(47, 32) +#define GITS_SGIR_VINTID RT_GENMASK_ULL(3, 0) + +#define GITS_CTLR_ENABLE (1U << 0) +#define GITS_CTLR_ImDe (1U << 1) +#define GITS_CTLR_ITS_NUMBER_SHIFT 4 +#define GITS_CTLR_ITS_NUMBER (0xFU << GITS_CTLR_ITS_NUMBER_SHIFT) +#define GITS_CTLR_QUIESCENT (1U << 31) + +#define GITS_TYPER_PLPIS (1UL << 0) +#define GITS_TYPER_VLPIS (1UL << 1) +#define GITS_TYPER_ITT_ENTRY_SIZE_SHIFT 4 +#define GITS_TYPER_ITT_ENTRY_SIZE RT_GENMASK_ULL(7, 4) +#define GITS_TYPER_IDBITS_SHIFT 8 +#define GITS_TYPER_DEVBITS_SHIFT 13 +#define GITS_TYPER_DEVBITS RT_GENMASK_ULL(17, 13) +#define GITS_TYPER_PTA (1UL << 19) +#define GITS_TYPER_HCC_SHIFT 24 +#define GITS_TYPER_HCC(r) (((r) >> GITS_TYPER_HCC_SHIFT) & 0xff) +#define GITS_TYPER_VMOVP (1ULL << 37) +#define GITS_TYPER_VMAPP (1ULL << 40) +#define GITS_TYPER_SVPET RT_GENMASK_ULL(42, 41) /* * ITS commands */ -#define GITS_CMD_MAPD 0x08 -#define GITS_CMD_MAPC 0x09 -#define GITS_CMD_MAPTI 0x0a -#define GITS_CMD_MAPI 0x0b -#define GITS_CMD_MOVI 0x01 -#define GITS_CMD_DISCARD 0x0f -#define GITS_CMD_INV 0x0c -#define GITS_CMD_MOVALL 0x0e -#define GITS_CMD_INVALL 0x0d -#define GITS_CMD_INT 0x03 -#define GITS_CMD_CLEAR 0x04 -#define GITS_CMD_SYNC 0x05 +#define GITS_CMD_MAPD 0x08 +#define GITS_CMD_MAPC 0x09 +#define GITS_CMD_MAPTI 0x0a +#define GITS_CMD_MAPI 0x0b +#define GITS_CMD_MOVI 0x01 +#define GITS_CMD_DISCARD 0x0f +#define GITS_CMD_INV 0x0c +#define GITS_CMD_MOVALL 0x0e +#define GITS_CMD_INVALL 0x0d +#define GITS_CMD_INT 0x03 +#define GITS_CMD_CLEAR 0x04 +#define GITS_CMD_SYNC 0x05 /* ITS Config Area */ -#define GITS_LPI_CFG_GROUP1 (1 << 1) -#define GITS_LPI_CFG_ENABLED (1 << 0) +#define GITS_LPI_CFG_GROUP1 (1 << 1) +#define GITS_LPI_CFG_ENABLED (1 << 0) /* ITS Command Queue Descriptor */ #define GITS_CBASER_VALID (1UL << 63) #define GITS_CBASER_SHAREABILITY_SHIFT (10) #define GITS_CBASER_INNER_CACHEABILITY_SHIFT (59) #define GITS_CBASER_OUTER_CACHEABILITY_SHIFT (53) +#define GICR_PBASER_SHAREABILITY_SHIFT (10) +#define GICR_PBASER_INNER_CACHEABILITY_SHIFT (7) +#define GICR_PBASER_OUTER_CACHEABILITY_SHIFT (56) + +#define GITS_TRANSLATION_TABLE_DESCRIPTORS_NR 8 #define GITS_BASER_CACHEABILITY(reg, inner_outer, type) \ - (GITS_CBASER_CACHE_##type << reg##_##inner_outer##_CACHEABILITY_SHIFT) + (GIC_BASER_CACHE_##type << reg##_##inner_outer##_CACHEABILITY_SHIFT) #define GITS_BASER_SHAREABILITY(reg, type) \ - (GITS_CBASER_##type << reg##_SHAREABILITY_SHIFT) - -#define GITS_CBASER_CACHE_DnGnRnE 0x0UL /* Device-nGnRnE. */ -#define GITS_CBASER_CACHE_NIN 0x1UL /* Normal Inner Non-cacheable. */ -#define GITS_CBASER_CACHE_NIRAWT 0x2UL /* Normal Inner Cacheable Read-allocate, Write-through. */ -#define GITS_CBASER_CACHE_NIRAWB 0x3UL /* Normal Inner Cacheable Read-allocate, Write-back. */ -#define GITS_CBASER_CACHE_NIWAWT 0x4UL /* Normal Inner Cacheable Write-allocate, Write-through. */ -#define GITS_CBASER_CACHE_NIWAWB 0x5UL /* Normal Inner Cacheable Write-allocate, Write-back. */ -#define GITS_CBASER_CACHE_NIRAWAWT 0x6UL /* Normal Inner Cacheable Read-allocate, Write-allocate, Write-through. */ -#define GITS_CBASER_CACHE_NIRAWAWB 0x7UL /* Normal Inner Cacheable Read-allocate, Write-allocate, Write-back. */ -#define GITS_CBASER_CACHE_MASK 0x7UL -#define GITS_CBASER_SHARE_NS 0x0UL /* Non-shareable. */ -#define GITS_CBASER_SHARE_IS 0x1UL /* Inner Shareable. */ -#define GITS_CBASER_SHARE_OS 0x2UL /* Outer Shareable. */ -#define GITS_CBASER_SHARE_RES 0x3UL /* Reserved. Treated as 0b00 */ -#define GITS_CBASER_SHARE_MASK 0x3UL + (GIC_BASER_##type << reg##_SHAREABILITY_SHIFT) + +#define GIC_BASER_CACHE_DnGnRnE 0x0UL /* Device-nGnRnE. */ +#define GIC_BASER_CACHE_NIN 0x1UL /* Normal Inner Non-cacheable. */ +#define GIC_BASER_CACHE_NIRAWT 0x2UL /* Normal Inner Cacheable Read-allocate, Write-through. */ +#define GIC_BASER_CACHE_NIRAWB 0x3UL /* Normal Inner Cacheable Read-allocate, Write-back. */ +#define GIC_BASER_CACHE_NIWAWT 0x4UL /* Normal Inner Cacheable Write-allocate, Write-through. */ +#define GIC_BASER_CACHE_NIWAWB 0x5UL /* Normal Inner Cacheable Write-allocate, Write-back. */ +#define GIC_BASER_CACHE_NIRAWAWT 0x6UL /* Normal Inner Cacheable Read-allocate, Write-allocate, Write-through. */ +#define GIC_BASER_CACHE_NIRAWAWB 0x7UL /* Normal Inner Cacheable Read-allocate, Write-allocate, Write-back. */ +#define GIC_BASER_CACHE_MASK 0x7UL +#define GIC_BASER_SHARE_NS 0x0UL /* Non-shareable. */ +#define GIC_BASER_SHARE_IS 0x1UL /* Inner Shareable. */ +#define GIC_BASER_SHARE_OS 0x2UL /* Outer Shareable. */ +#define GIC_BASER_SHARE_RES 0x3UL /* Reserved. Treated as 0b00 */ +#define GIC_BASER_SHARE_MASK 0x3UL #define GITS_CBASER_InnerShareable GITS_BASER_SHAREABILITY(GITS_CBASER, SHARE_IS) +#define GITS_CBASER_SHARE_MASK_ALL GITS_BASER_SHAREABILITY(GITS_CBASER, SHARE_MASK) +#define GITS_CBASER_INNER_MASK_ALL GITS_BASER_CACHEABILITY(GITS_CBASER, INNER, MASK) #define GITS_CBASER_nCnB GITS_BASER_CACHEABILITY(GITS_CBASER, INNER, DnGnRnE) #define GITS_CBASER_nC GITS_BASER_CACHEABILITY(GITS_CBASER, INNER, NIN) #define GITS_CBASER_RaWt GITS_BASER_CACHEABILITY(GITS_CBASER, INNER, NIRAWT) @@ -218,6 +257,18 @@ #define GITS_CBASER_RaWaWt GITS_BASER_CACHEABILITY(GITS_CBASER, INNER, NIRAWAWT) #define GITS_CBASER_RaWaWb GITS_BASER_CACHEABILITY(GITS_CBASER, INNER, NIRAWAWB) +#define GICR_PBASER_InnerShareable GITS_BASER_SHAREABILITY(GICR_PBASER, SHARE_IS) +#define GICR_PBASER_SHARE_MASK_ALL GITS_BASER_SHAREABILITY(GICR_PBASER, SHARE_MASK) +#define GICR_PBASER_INNER_MASK_ALL GITS_BASER_CACHEABILITY(GICR_PBASER, INNER, MASK) +#define GICR_PBASER_nCnB GITS_BASER_CACHEABILITY(GICR_PBASER, INNER, DnGnRnE) +#define GICR_PBASER_nC GITS_BASER_CACHEABILITY(GICR_PBASER, INNER, NIN) +#define GICR_PBASER_RaWt GITS_BASER_CACHEABILITY(GICR_PBASER, INNER, NIRAWT) +#define GICR_PBASER_RaWb GITS_BASER_CACHEABILITY(GICR_PBASER, INNER, NIRAWB) +#define GICR_PBASER_WaWt GITS_BASER_CACHEABILITY(GICR_PBASER, INNER, NIWAWT) +#define GICR_PBASER_WaWb GITS_BASER_CACHEABILITY(GICR_PBASER, INNER, NIWAWB) +#define GICR_PBASER_RaWaWt GITS_BASER_CACHEABILITY(GICR_PBASER, INNER, NIRAWAWT) +#define GICR_PBASER_RaWaWb GITS_BASER_CACHEABILITY(GICR_PBASER, INNER, NIRAWAWB) + #define GIC_EPPI_BASE_INTID 1056 #define GIC_ESPI_BASE_INTID 4096 @@ -269,16 +320,18 @@ struct gicv3 struct rt_pic parent; int version; - int max_irq; + int irq_nr; rt_uint32_t gicd_typer; rt_size_t line_nr; rt_size_t espi_nr; + rt_size_t lpi_nr; rt_ubase_t flags; void *dist_base; rt_size_t dist_size; void *redist_percpu_base[RT_CPUS_NR]; + rt_uint64_t redist_percpu_flags[RT_CPUS_NR]; rt_size_t percpu_ppi_nr[RT_CPUS_NR]; struct diff --git a/components/drivers/pic/pic-riscv-intc.c b/components/drivers/pic/pic-riscv-intc.c deleted file mode 100755 index de6951930e85..000000000000 --- a/components/drivers/pic/pic-riscv-intc.c +++ /dev/null @@ -1,146 +0,0 @@ - interrupt-controller { - #interrupt-cells = <0x01>; - interrupt-controller; - compatible = "riscv,cpu-intc"; - phandle = <0x02>; - }; - -// SPDX-License-Identifier: GPL-2.0 -/* - * Copyright (C) 2012 Regents of the University of California - * Copyright (C) 2017-2018 SiFive - * Copyright (C) 2020 Western Digital Corporation or its affiliates. - */ - -#define pr_fmt(fmt) "riscv-intc: " fmt -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static struct irq_domain *intc_domain; - -static asmlinkage void riscv_intc_irq(struct pt_regs *regs) -{ - unsigned long cause = regs->cause & ~CAUSE_IRQ_FLAG; - - if (unlikely(cause >= BITS_PER_LONG)) - panic("unexpected interrupt cause"); - - switch (cause) { -#ifdef CONFIG_SMP - case RV_IRQ_SOFT: - /* - * We only use software interrupts to pass IPIs, so if a - * non-SMP system gets one, then we don't know what to do. - */ - handle_IPI(regs); - break; -#endif - default: - generic_handle_domain_irq(intc_domain, cause); - break; - } -} - -/* - * On RISC-V systems local interrupts are masked or unmasked by writing - * the SIE (Supervisor Interrupt Enable) CSR. As CSRs can only be written - * on the local hart, these functions can only be called on the hart that - * corresponds to the IRQ chip. - */ - -static void riscv_intc_irq_mask(struct irq_data *d) -{ - csr_clear(CSR_IE, RT_BIT(d->hwirq)); -} - -static void riscv_intc_irq_unmask(struct irq_data *d) -{ - csr_set(CSR_IE, RT_BIT(d->hwirq)); -} - -static int riscv_intc_cpu_starting(unsigned int cpu) -{ - csr_set(CSR_IE, RT_BIT(RV_IRQ_SOFT)); - return 0; -} - -static int riscv_intc_cpu_dying(unsigned int cpu) -{ - csr_clear(CSR_IE, RT_BIT(RV_IRQ_SOFT)); - return 0; -} - -static struct irq_chip riscv_intc_chip = { - .name = "RISC-V INTC", - .irq_mask = riscv_intc_irq_mask, - .irq_unmask = riscv_intc_irq_unmask, -}; - -static int riscv_intc_domain_map(struct irq_domain *d, unsigned int irq, - irq_hw_number_t hwirq) -{ - irq_set_percpu_devid(irq); - irq_domain_set_info(d, irq, hwirq, &riscv_intc_chip, d->host_data, - handle_percpu_devid_irq, NULL, NULL); - - return 0; -} - -static const struct irq_domain_ops riscv_intc_domain_ops = { - .map = riscv_intc_domain_map, - .xlate = irq_domain_xlate_onecell, -}; - -static int __init riscv_intc_init(struct device_node *node, - struct device_node *parent) -{ - int rc; - unsigned long hartid; - - rc = riscv_of_parent_hartid(node, &hartid); - if (rc < 0) { - pr_warn("unable to find hart id for %pOF\n", node); - return 0; - } - - /* - * The DT will have one INTC DT node under each CPU (or HART) - * DT node so riscv_intc_init() function will be called once - * for each INTC DT node. We only need to do INTC initialization - * for the INTC DT node belonging to boot CPU (or boot HART). - */ - if (riscv_hartid_to_cpuid(hartid) != smp_processor_id()) - return 0; - - intc_domain = irq_domain_add_linear(node, BITS_PER_LONG, - &riscv_intc_domain_ops, NULL); - if (!intc_domain) { - pr_err("unable to add IRQ domain\n"); - return -ENXIO; - } - - rc = set_handle_irq(&riscv_intc_irq); - if (rc) { - pr_err("failed to set irq handler\n"); - return rc; - } - - cpuhp_setup_state(CPUHP_AP_IRQ_RISCV_STARTING, - "irqchip/riscv/intc:starting", - riscv_intc_cpu_starting, - riscv_intc_cpu_dying); - - pr_info("%d local interrupts mapped\n", BITS_PER_LONG); - - return 0; -} - -IRQCHIP_DECLARE(riscv, "riscv,cpu-intc", riscv_intc_init); diff --git a/components/drivers/pic/pic-sifive-plic.c b/components/drivers/pic/pic-sifive-plic.c deleted file mode 100755 index a5157a79539b..000000000000 --- a/components/drivers/pic/pic-sifive-plic.c +++ /dev/null @@ -1,500 +0,0 @@ -plic@c000000 { - phandle = <0x09>; - riscv,ndev = <0x35>; - reg = <0x00 0xc000000 0x00 0x600000>; - interrupts-extended = <0x08 0x0b 0x08 0x09 0x06 0x0b 0x06 0x09 0x04 0x0b 0x04 0x09 0x02 0x0b 0x02 0x09>; - interrupt-controller; - compatible = "sifive,plic-1.0.0", "riscv,plic0"; - #interrupt-cells = <0x01>; -}; - -// SPDX-License-Identifier: GPL-2.0 -/* - * Copyright (C) 2017 SiFive - * Copyright (C) 2018 Christoph Hellwig - */ -#define pr_fmt(fmt) "plic: " fmt -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* - * This driver implements a version of the RISC-V PLIC with the actual layout - * specified in chapter 8 of the SiFive U5 Coreplex Series Manual: - * - * https://static.dev.sifive.com/U54-MC-RVCoreIP.pdf - * - * The largest number supported by devices marked as 'sifive,plic-1.0.0', is - * 1024, of which device 0 is defined as non-existent by the RISC-V Privileged - * Spec. - */ - -#define MAX_DEVICES 1024 -#define MAX_CONTEXTS 15872 - -/* - * Each interrupt source has a priority register associated with it. - * We always hardwire it to one in Linux. - */ -#define PRIORITY_BASE 0 -#define PRIORITY_PER_ID 4 - -/* - * Each hart context has a vector of interrupt enable bits associated with it. - * There's one bit for each interrupt source. - */ -#define CONTEXT_ENABLE_BASE 0x2000 -#define CONTEXT_ENABLE_SIZE 0x80 - -/* - * Each hart context has a set of control registers associated with it. Right - * now there's only two: a source priority threshold over which the hart will - * take an interrupt, and a register to claim interrupts. - */ -#define CONTEXT_BASE 0x200000 -#define CONTEXT_SIZE 0x1000 -#define CONTEXT_THRESHOLD 0x00 -#define CONTEXT_CLAIM 0x04 - -#define PLIC_DISABLE_THRESHOLD 0x7 -#define PLIC_ENABLE_THRESHOLD 0 - -#define PLIC_QUIRK_EDGE_INTERRUPT 0 - -struct plic_priv { - struct cpumask lmask; - struct irq_domain *irqdomain; - void __iomem *regs; - unsigned long plic_quirks; -}; - -struct plic_handler { - bool present; - void __iomem *hart_base; - /* - * Protect mask operations on the registers given that we can't - * assume atomic memory operations work on them. - */ - raw_spinlock_t enable_lock; - void __iomem *enable_base; - struct plic_priv *priv; -}; -static int plic_parent_irq __ro_after_init; -static bool plic_cpuhp_setup_done __ro_after_init; -static DEFINE_PER_CPU(struct plic_handler, plic_handlers); - -static int plic_irq_set_type(struct irq_data *d, unsigned int type); - -static void __plic_toggle(void __iomem *enable_base, int hwirq, int enable) -{ - u32 __iomem *reg = enable_base + (hwirq / 32) * sizeof(u32); - u32 hwirq_mask = 1 << (hwirq % 32); - - if (enable) - writel(readl(reg) | hwirq_mask, reg); - else - writel(readl(reg) & ~hwirq_mask, reg); -} - -static void plic_toggle(struct plic_handler *handler, int hwirq, int enable) -{ - raw_spin_lock(&handler->enable_lock); - __plic_toggle(handler->enable_base, hwirq, enable); - raw_spin_unlock(&handler->enable_lock); -} - -static inline void plic_irq_toggle(const struct cpumask *mask, - struct irq_data *d, int enable) -{ - int cpu; - - for_each_cpu(cpu, mask) { - struct plic_handler *handler = per_cpu_ptr(&plic_handlers, cpu); - - plic_toggle(handler, d->hwirq, enable); - } -} - -static void plic_irq_enable(struct irq_data *d) -{ - plic_irq_toggle(irq_data_get_effective_affinity_mask(d), d, 1); -} - -static void plic_irq_disable(struct irq_data *d) -{ - plic_irq_toggle(irq_data_get_effective_affinity_mask(d), d, 0); -} - -static void plic_irq_unmask(struct irq_data *d) -{ - struct plic_priv *priv = irq_data_get_irq_chip_data(d); - - writel(1, priv->regs + PRIORITY_BASE + d->hwirq * PRIORITY_PER_ID); -} - -static void plic_irq_mask(struct irq_data *d) -{ - struct plic_priv *priv = irq_data_get_irq_chip_data(d); - - writel(0, priv->regs + PRIORITY_BASE + d->hwirq * PRIORITY_PER_ID); -} - -static void plic_irq_eoi(struct irq_data *d) -{ - struct plic_handler *handler = this_cpu_ptr(&plic_handlers); - - writel(d->hwirq, handler->hart_base + CONTEXT_CLAIM); -} - -#ifdef CONFIG_SMP -static int plic_set_affinity(struct irq_data *d, - const struct cpumask *mask_val, bool force) -{ - unsigned int cpu; - struct cpumask amask; - struct plic_priv *priv = irq_data_get_irq_chip_data(d); - - cpumask_and(&amask, &priv->lmask, mask_val); - - if (force) - cpu = cpumask_first(&amask); - else - cpu = cpumask_any_and(&amask, cpu_online_mask); - - if (cpu >= nr_cpu_ids) - return -EINVAL; - - plic_irq_disable(d); - - irq_data_update_effective_affinity(d, cpumask_of(cpu)); - - if (!irqd_irq_disabled(d)) - plic_irq_enable(d); - - return IRQ_SET_MASK_OK_DONE; -} -#endif - -static struct irq_chip plic_edge_chip = { - .name = "SiFive PLIC", - .irq_enable = plic_irq_enable, - .irq_disable = plic_irq_disable, - .irq_ack = plic_irq_eoi, - .irq_mask = plic_irq_mask, - .irq_unmask = plic_irq_unmask, -#ifdef CONFIG_SMP - .irq_set_affinity = plic_set_affinity, -#endif - .irq_set_type = plic_irq_set_type, - .flags = IRQCHIP_AFFINITY_PRE_STARTUP, -}; - -static struct irq_chip plic_chip = { - .name = "SiFive PLIC", - .irq_enable = plic_irq_enable, - .irq_disable = plic_irq_disable, - .irq_mask = plic_irq_mask, - .irq_unmask = plic_irq_unmask, - .irq_eoi = plic_irq_eoi, -#ifdef CONFIG_SMP - .irq_set_affinity = plic_set_affinity, -#endif - .irq_set_type = plic_irq_set_type, - .flags = IRQCHIP_AFFINITY_PRE_STARTUP, -}; - -static int plic_irq_set_type(struct irq_data *d, unsigned int type) -{ - struct plic_priv *priv = irq_data_get_irq_chip_data(d); - - if (!test_bit(PLIC_QUIRK_EDGE_INTERRUPT, &priv->plic_quirks)) - return IRQ_SET_MASK_OK_NOCOPY; - - switch (type) { - case IRQ_TYPE_EDGE_RISING: - irq_set_chip_handler_name_locked(d, &plic_edge_chip, - handle_edge_irq, NULL); - break; - case IRQ_TYPE_LEVEL_HIGH: - irq_set_chip_handler_name_locked(d, &plic_chip, - handle_fasteoi_irq, NULL); - break; - default: - return -EINVAL; - } - - return IRQ_SET_MASK_OK; -} - -static int plic_irqdomain_map(struct irq_domain *d, unsigned int irq, - irq_hw_number_t hwirq) -{ - struct plic_priv *priv = d->host_data; - - irq_domain_set_info(d, irq, hwirq, &plic_chip, d->host_data, - handle_fasteoi_irq, NULL, NULL); - irq_set_noprobe(irq); - irq_set_affinity(irq, &priv->lmask); - return 0; -} - -static int plic_irq_domain_translate(struct irq_domain *d, - struct irq_fwspec *fwspec, - unsigned long *hwirq, - unsigned int *type) -{ - struct plic_priv *priv = d->host_data; - - if (test_bit(PLIC_QUIRK_EDGE_INTERRUPT, &priv->plic_quirks)) - return irq_domain_translate_twocell(d, fwspec, hwirq, type); - - return irq_domain_translate_onecell(d, fwspec, hwirq, type); -} - -static int plic_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, - unsigned int nr_irqs, void *arg) -{ - int i, ret; - irq_hw_number_t hwirq; - unsigned int type; - struct irq_fwspec *fwspec = arg; - - ret = plic_irq_domain_translate(domain, fwspec, &hwirq, &type); - if (ret) - return ret; - - for (i = 0; i < nr_irqs; i++) { - ret = plic_irqdomain_map(domain, virq + i, hwirq + i); - if (ret) - return ret; - } - - return 0; -} - -static const struct irq_domain_ops plic_irqdomain_ops = { - .translate = plic_irq_domain_translate, - .alloc = plic_irq_domain_alloc, - .free = irq_domain_free_irqs_top, -}; - -/* - * Handling an interrupt is a two-step process: first you claim the interrupt - * by reading the claim register, then you complete the interrupt by writing - * that source ID back to the same claim register. This automatically enables - * and disables the interrupt, so there's nothing else to do. - */ -static void plic_handle_irq(struct irq_desc *desc) -{ - struct plic_handler *handler = this_cpu_ptr(&plic_handlers); - struct irq_chip *chip = irq_desc_get_chip(desc); - void __iomem *claim = handler->hart_base + CONTEXT_CLAIM; - irq_hw_number_t hwirq; - - WARN_ON_ONCE(!handler->present); - - chained_irq_enter(chip, desc); - - while ((hwirq = readl(claim))) { - int err = generic_handle_domain_irq(handler->priv->irqdomain, - hwirq); - if (unlikely(err)) - pr_warn_ratelimited("can't find mapping for hwirq %lu\n", - hwirq); - } - - chained_irq_exit(chip, desc); -} - -static void plic_set_threshold(struct plic_handler *handler, u32 threshold) -{ - /* priority must be > threshold to trigger an interrupt */ - writel(threshold, handler->hart_base + CONTEXT_THRESHOLD); -} - -static int plic_dying_cpu(unsigned int cpu) -{ - if (plic_parent_irq) - disable_percpu_irq(plic_parent_irq); - - return 0; -} - -static int plic_starting_cpu(unsigned int cpu) -{ - struct plic_handler *handler = this_cpu_ptr(&plic_handlers); - - if (plic_parent_irq) - enable_percpu_irq(plic_parent_irq, - irq_get_trigger_type(plic_parent_irq)); - else - pr_warn("cpu%d: parent irq not available\n", cpu); - plic_set_threshold(handler, PLIC_ENABLE_THRESHOLD); - - return 0; -} - -static int __init __plic_init(struct device_node *node, - struct device_node *parent, - unsigned long plic_quirks) -{ - int error = 0, nr_contexts, nr_handlers = 0, i; - u32 nr_irqs; - struct plic_priv *priv; - struct plic_handler *handler; - - priv = kzalloc(sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; - - priv->plic_quirks = plic_quirks; - - priv->regs = of_iomap(node, 0); - if (WARN_ON(!priv->regs)) { - error = -EIO; - goto out_free_priv; - } - - error = -EINVAL; - of_property_read_u32(node, "riscv,ndev", &nr_irqs); - if (WARN_ON(!nr_irqs)) - goto out_iounmap; - - nr_contexts = of_irq_count(node); - if (WARN_ON(!nr_contexts)) - goto out_iounmap; - - error = -ENOMEM; - priv->irqdomain = irq_domain_add_linear(node, nr_irqs + 1, - &plic_irqdomain_ops, priv); - if (WARN_ON(!priv->irqdomain)) - goto out_iounmap; - - for (i = 0; i < nr_contexts; i++) { - struct of_phandle_args parent; - irq_hw_number_t hwirq; - int cpu; - unsigned long hartid; - - if (of_irq_parse_one(node, i, &parent)) { - pr_err("failed to parse parent for context %d.\n", i); - continue; - } - - /* - * Skip contexts other than external interrupts for our - * privilege level. - */ - if (parent.args[0] != RV_IRQ_EXT) { - /* Disable S-mode enable bits if running in M-mode. */ - if (IS_ENABLED(CONFIG_RISCV_M_MODE)) { - void __iomem *enable_base = priv->regs + - CONTEXT_ENABLE_BASE + - i * CONTEXT_ENABLE_SIZE; - - for (hwirq = 1; hwirq <= nr_irqs; hwirq++) - __plic_toggle(enable_base, hwirq, 0); - } - continue; - } - - error = riscv_of_parent_hartid(parent.np, &hartid); - if (error < 0) { - pr_warn("failed to parse hart ID for context %d.\n", i); - continue; - } - - cpu = riscv_hartid_to_cpuid(hartid); - if (cpu < 0) { - pr_warn("Invalid cpuid for context %d\n", i); - continue; - } - - /* Find parent domain and register chained handler */ - if (!plic_parent_irq && irq_find_host(parent.np)) { - plic_parent_irq = irq_of_parse_and_map(node, i); - if (plic_parent_irq) - irq_set_chained_handler(plic_parent_irq, - plic_handle_irq); - } - - /* - * When running in M-mode we need to ignore the S-mode handler. - * Here we assume it always comes later, but that might be a - * little fragile. - */ - handler = per_cpu_ptr(&plic_handlers, cpu); - if (handler->present) { - pr_warn("handler already present for context %d.\n", i); - plic_set_threshold(handler, PLIC_DISABLE_THRESHOLD); - goto done; - } - - cpumask_set_cpu(cpu, &priv->lmask); - handler->present = true; - handler->hart_base = priv->regs + CONTEXT_BASE + - i * CONTEXT_SIZE; - raw_spin_lock_init(&handler->enable_lock); - handler->enable_base = priv->regs + CONTEXT_ENABLE_BASE + - i * CONTEXT_ENABLE_SIZE; - handler->priv = priv; -done: - for (hwirq = 1; hwirq <= nr_irqs; hwirq++) { - plic_toggle(handler, hwirq, 0); - writel(1, priv->regs + PRIORITY_BASE + - hwirq * PRIORITY_PER_ID); - } - nr_handlers++; - } - - /* - * We can have multiple PLIC instances so setup cpuhp state only - * when context handler for current/boot CPU is present. - */ - handler = this_cpu_ptr(&plic_handlers); - if (handler->present && !plic_cpuhp_setup_done) { - cpuhp_setup_state(CPUHP_AP_IRQ_SIFIVE_PLIC_STARTING, - "irqchip/sifive/plic:starting", - plic_starting_cpu, plic_dying_cpu); - plic_cpuhp_setup_done = true; - } - - pr_info("%pOFP: mapped %d interrupts with %d handlers for" - " %d contexts.\n", node, nr_irqs, nr_handlers, nr_contexts); - return 0; - -out_iounmap: - iounmap(priv->regs); -out_free_priv: - kfree(priv); - return error; -} - -static int __init plic_init(struct device_node *node, - struct device_node *parent) -{ - return __plic_init(node, parent, 0); -} - -IRQCHIP_DECLARE(sifive_plic, "sifive,plic-1.0.0", plic_init); -IRQCHIP_DECLARE(riscv_plic0, "riscv,plic0", plic_init); /* for legacy systems */ - -static int __init plic_edge_init(struct device_node *node, - struct device_node *parent) -{ - return __plic_init(node, parent, RT_BIT(PLIC_QUIRK_EDGE_INTERRUPT)); -} - -IRQCHIP_DECLARE(andestech_nceplic100, "andestech,nceplic100", plic_edge_init); -IRQCHIP_DECLARE(thead_c900_plic, "thead,c900-plic", plic_edge_init); diff --git a/components/drivers/pic/pic.c b/components/drivers/pic/pic.c index 240fcaa47c17..2d0c6b023358 100644 --- a/components/drivers/pic/pic.c +++ b/components/drivers/pic/pic.c @@ -54,9 +54,9 @@ static rt_list_t _traps_nodes = RT_LIST_OBJECT_INIT(_traps_nodes); static struct rt_pic_irq *irq2pirq(int irq) { - struct rt_pic_irq *pirq; + struct rt_pic_irq *pirq = RT_NULL; - if (irq >= 0 && irq < MAX_HANDLERS) + if ((irq >= 0) && (irq < MAX_HANDLERS)) { pirq = &_pirq_hash[irq]; @@ -86,6 +86,48 @@ static void append_pic(struct rt_pic *pic) } } +void rt_pic_default_name(struct rt_pic *pic) +{ + if (pic) + { + #if RT_NAME_MAX > 0 + rt_strncpy(pic->parent.name, "PIC", RT_NAME_MAX); + #else + pic->parent.name = "PIC"; + #endif + } +} + +struct rt_pic *rt_pic_dynamic_cast(void *ptr) +{ + struct rt_pic *pic = RT_NULL, *tmp = RT_NULL; + + if (ptr) + { + struct rt_object *obj = ptr; + + if (obj->type == RT_Object_Class_Unknown) + { + tmp = (void *)obj; + } + else if (obj->type == RT_Object_Class_Device) + { + tmp = (void *)obj + sizeof(struct rt_device); + } + else + { + tmp = (void *)obj + sizeof(struct rt_object); + } + + if (tmp && !rt_strcmp(tmp->parent.name, "PIC")) + { + pic = tmp; + } + } + + return pic; +} + rt_err_t rt_pic_linear_irq(struct rt_pic *pic, rt_size_t irq_nr) { rt_err_t err = RT_EOK; @@ -98,6 +140,9 @@ rt_err_t rt_pic_linear_irq(struct rt_pic *pic, rt_size_t irq_nr) { rt_list_init(&pic->list); + rt_pic_default_name(pic); + pic->parent.type = RT_Object_Class_Unknown; + pic->irq_start = _pirq_hash_idx; pic->irq_nr = irq_nr; pic->pirqs = &_pirq_hash[_pirq_hash_idx]; @@ -135,6 +180,8 @@ static void config_pirq(struct rt_pic *pic, struct rt_pic_irq *pirq, int irq, in pirq->pic = pic; + rt_list_init(&pirq->list); + rt_list_init(&pirq->children_nodes); rt_list_init(&pirq->isr.list); rt_spin_unlock_irqrestore(&pirq->rw_lock, level); @@ -195,91 +242,84 @@ struct rt_pic_irq *rt_pic_find_ipi(struct rt_pic *pic, int ipi_index) return pirq; } -int rt_pic_cascade(struct rt_pic *pic, struct rt_pic *parent_pic, int hwirq, rt_uint32_t mode) +struct rt_pic_irq *rt_pic_find_pirq(struct rt_pic *pic, int irq) { - int irq = -RT_EINVAL; - - if (pic && parent_pic && hwirq >= 0) + if (pic && irq >= pic->irq_start && irq <= pic->irq_start + pic->irq_nr) { - struct rt_pic *ppic = parent_pic; - rt_ubase_t level = rt_spin_lock_irqsave(&_pic_lock); + return &pic->pirqs[irq - pic->irq_start]; + } - while (!ppic->ops->irq_map && ppic->parent) - { - ppic = ppic->parent; - } + return RT_NULL; +} - rt_spin_unlock_irqrestore(&_pic_lock, level); +rt_err_t rt_pic_cascade(struct rt_pic_irq *pirq, int parent_irq) +{ + rt_err_t err = RT_EOK; - if (ppic->ops->irq_map) - { - struct rt_pic_irq *pirq; + if (pirq && !pirq->parent && parent_irq >= 0) + { + struct rt_pic_irq *parent; - irq = ppic->ops->irq_map(ppic, hwirq, mode); + rt_spin_lock(&pirq->rw_lock); - if (irq >= 0 && (pirq = irq2pirq(irq))) - { - rt_spin_lock(&pirq->rw_lock); + parent = irq2pirq(parent_irq); - pirq->pic = pic; - pic->parent = parent_pic; + if (parent) + { + pirq->parent = parent; + pirq->priority = parent->priority; + rt_memcpy(&pirq->affinity, &parent->affinity, sizeof(pirq->affinity)); + } - rt_spin_unlock(&pirq->rw_lock); + rt_spin_unlock(&pirq->rw_lock); - if (rt_list_isempty(&pic->list)) - { - rt_ubase_t level = rt_spin_lock_irqsave(&_pic_lock); + if (parent && pirq->pic->ops->flags & RT_PIC_F_IRQ_ROUTING) + { + rt_spin_lock(&parent->rw_lock); - append_pic(pic); + rt_list_insert_before(&parent->children_nodes, &pirq->list); - rt_spin_unlock_irqrestore(&_pic_lock, level); - } - } - } - else - { - irq = -RT_ENOSYS; + rt_spin_unlock(&parent->rw_lock); } } + else + { + err = -RT_EINVAL; + } - return irq; + return err; } -void rt_pic_uncascade(struct rt_pic *pic, int irq) +rt_err_t rt_pic_uncascade(struct rt_pic_irq *pirq) { - struct rt_pic_irq *pirq; + rt_err_t err = RT_EOK; - if (pic && pic->parent && irq >= 0 && (pirq = irq2pirq(irq))) + if (pirq && pirq->parent) { - struct rt_pic *ppic, *prev = RT_NULL; + struct rt_pic_irq *parent; rt_spin_lock(&pirq->rw_lock); - ppic = pirq->pic; + parent = pirq->parent; + pirq->parent = RT_NULL; - while (ppic && pic->parent != ppic->parent) - { - prev = ppic; - ppic = ppic->parent; - } + rt_spin_unlock(&pirq->rw_lock); - if (ppic) + if (parent && pirq->pic->ops->flags & RT_PIC_F_IRQ_ROUTING) { - if (prev) - { - pirq->pic = prev; - prev->parent = pic->parent; - } - else - { - pirq->pic = pic->parent; - } + rt_spin_lock(&parent->rw_lock); - pic->parent = RT_NULL; - } + rt_list_remove(&pirq->list); - rt_spin_unlock(&pirq->rw_lock); + rt_spin_unlock(&parent->rw_lock); + } } + else + { + err = -RT_EINVAL; + } + + return err; } rt_err_t rt_pic_attach_irq(int irq, rt_isr_handler_t handler, void *uid, const char *name, int flags) @@ -453,7 +493,7 @@ rt_err_t rt_pic_do_traps(void) rt_err_t rt_pic_handle_isr(struct rt_pic_irq *pirq) { - rt_err_t err; + rt_err_t err = -RT_EEMPTY; rt_list_t *handler_nodes; struct rt_irq_desc *action; @@ -466,6 +506,20 @@ rt_err_t rt_pic_handle_isr(struct rt_pic_irq *pirq) handler_nodes = &pirq->isr.list; action = &pirq->isr.action; + if (!rt_list_isempty(&pirq->children_nodes)) + { + struct rt_pic_irq *child; + + rt_list_for_each_entry(child, &pirq->children_nodes, list) + { + rt_pic_irq_ack(child->irq); + + err = rt_pic_handle_isr(child); + + rt_pic_irq_eoi(child->irq); + } + } + if (action->handler) { action->handler(pirq->irq, action->param); @@ -489,10 +543,8 @@ rt_err_t rt_pic_handle_isr(struct rt_pic_irq *pirq) #endif } } - } - else - { - err = -RT_EEMPTY; + + err = RT_EOK; } return err; @@ -547,49 +599,101 @@ rt_err_t rt_pic_irq_finit(void) return err; } -#define _irq_call_helper(irq, fn) \ -({ \ - struct rt_pic_irq *pirq; \ - if ((pirq = irq2pirq(irq))) \ - { \ - rt_spin_lock(&pirq->rw_lock); \ - if (pirq->pic->ops->fn) \ - pirq->pic->ops->fn(pirq); \ - rt_spin_unlock(&pirq->rw_lock); \ - } \ -}) - void rt_pic_irq_enable(int irq) { - _irq_call_helper(irq, irq_enable); + struct rt_pic_irq *pirq = irq2pirq(irq); + + RT_ASSERT(pirq != RT_NULL); + + rt_hw_spin_lock(&pirq->rw_lock.lock); + + if (pirq->pic->ops->irq_enable) + { + pirq->pic->ops->irq_enable(pirq); + } + + rt_hw_spin_unlock(&pirq->rw_lock.lock); } void rt_pic_irq_disable(int irq) { - _irq_call_helper(irq, irq_disable); + struct rt_pic_irq *pirq = irq2pirq(irq); + + RT_ASSERT(pirq != RT_NULL); + + rt_hw_spin_lock(&pirq->rw_lock.lock); + + if (pirq->pic->ops->irq_disable) + { + pirq->pic->ops->irq_disable(pirq); + } + + rt_hw_spin_unlock(&pirq->rw_lock.lock); } void rt_pic_irq_ack(int irq) { - _irq_call_helper(irq, irq_ack); + struct rt_pic_irq *pirq = irq2pirq(irq); + + RT_ASSERT(pirq != RT_NULL); + + rt_hw_spin_lock(&pirq->rw_lock.lock); + + if (pirq->pic->ops->irq_ack) + { + pirq->pic->ops->irq_ack(pirq); + } + + rt_hw_spin_unlock(&pirq->rw_lock.lock); } void rt_pic_irq_mask(int irq) { - _irq_call_helper(irq, irq_mask); + struct rt_pic_irq *pirq = irq2pirq(irq); + + RT_ASSERT(pirq != RT_NULL); + + rt_hw_spin_lock(&pirq->rw_lock.lock); + + if (pirq->pic->ops->irq_mask) + { + pirq->pic->ops->irq_mask(pirq); + } + + rt_hw_spin_unlock(&pirq->rw_lock.lock); } void rt_pic_irq_unmask(int irq) { - _irq_call_helper(irq, irq_unmask); + struct rt_pic_irq *pirq = irq2pirq(irq); + + RT_ASSERT(pirq != RT_NULL); + + rt_hw_spin_lock(&pirq->rw_lock.lock); + + if (pirq->pic->ops->irq_unmask) + { + pirq->pic->ops->irq_unmask(pirq); + } + + rt_hw_spin_unlock(&pirq->rw_lock.lock); } void rt_pic_irq_eoi(int irq) { - _irq_call_helper(irq, irq_eoi); -} + struct rt_pic_irq *pirq = irq2pirq(irq); + + RT_ASSERT(pirq != RT_NULL); -#undef _irq_call_helper + rt_hw_spin_lock(&pirq->rw_lock.lock); + + if (pirq->pic->ops->irq_eoi) + { + pirq->pic->ops->irq_eoi(pirq); + } + + rt_hw_spin_unlock(&pirq->rw_lock.lock); +} rt_err_t rt_pic_irq_set_priority(int irq, rt_uint32_t priority) { @@ -598,7 +702,7 @@ rt_err_t rt_pic_irq_set_priority(int irq, rt_uint32_t priority) if (pirq) { - rt_spin_lock(&pirq->rw_lock); + rt_hw_spin_lock(&pirq->rw_lock.lock); if (pirq->pic->ops->irq_set_priority) { @@ -614,7 +718,7 @@ rt_err_t rt_pic_irq_set_priority(int irq, rt_uint32_t priority) err = -RT_ENOSYS; } - rt_spin_unlock(&pirq->rw_lock); + rt_hw_spin_unlock(&pirq->rw_lock.lock); } return err; @@ -627,11 +731,11 @@ rt_uint32_t rt_pic_irq_get_priority(int irq) if (pirq) { - rt_spin_lock(&pirq->rw_lock); + rt_hw_spin_lock(&pirq->rw_lock.lock); priority = pirq->priority; - rt_spin_unlock(&pirq->rw_lock); + rt_hw_spin_unlock(&pirq->rw_lock.lock); } return priority; @@ -644,7 +748,7 @@ rt_err_t rt_pic_irq_set_affinity(int irq, bitmap_t *affinity) if (affinity && (pirq = irq2pirq(irq))) { - rt_spin_lock(&pirq->rw_lock); + rt_hw_spin_lock(&pirq->rw_lock.lock); if (pirq->pic->ops->irq_set_affinity) { @@ -660,7 +764,7 @@ rt_err_t rt_pic_irq_set_affinity(int irq, bitmap_t *affinity) err = -RT_ENOSYS; } - rt_spin_unlock(&pirq->rw_lock); + rt_hw_spin_unlock(&pirq->rw_lock.lock); } return err; @@ -673,12 +777,12 @@ rt_err_t rt_pic_irq_get_affinity(int irq, bitmap_t *out_affinity) if (out_affinity && (pirq = irq2pirq(irq))) { - rt_spin_lock(&pirq->rw_lock); + rt_hw_spin_lock(&pirq->rw_lock.lock); rt_memcpy(out_affinity, pirq->affinity, sizeof(pirq->affinity)); err = RT_EOK; - rt_spin_unlock(&pirq->rw_lock); + rt_hw_spin_unlock(&pirq->rw_lock.lock); } return err; @@ -691,7 +795,7 @@ rt_err_t rt_pic_irq_set_triger_mode(int irq, rt_uint32_t mode) if ((~mode & RT_IRQ_MODE_MASK) && (pirq = irq2pirq(irq))) { - rt_spin_lock(&pirq->rw_lock); + rt_hw_spin_lock(&pirq->rw_lock.lock); if (pirq->pic->ops->irq_set_triger_mode) { @@ -707,7 +811,7 @@ rt_err_t rt_pic_irq_set_triger_mode(int irq, rt_uint32_t mode) err = -RT_ENOSYS; } - rt_spin_unlock(&pirq->rw_lock); + rt_hw_spin_unlock(&pirq->rw_lock.lock); } return err; @@ -720,11 +824,11 @@ rt_uint32_t rt_pic_irq_get_triger_mode(int irq) if (pirq) { - rt_spin_lock(&pirq->rw_lock); + rt_hw_spin_lock(&pirq->rw_lock.lock); mode = pirq->mode; - rt_spin_unlock(&pirq->rw_lock); + rt_hw_spin_unlock(&pirq->rw_lock.lock); } return mode; @@ -747,82 +851,126 @@ void rt_pic_irq_send_ipi(int irq, bitmap_t *cpumask) } } -#define _pirq_parent_call_helper(ppic, pirq, fn, ret,...) \ -({ \ - if (ppic && pirq) \ - { \ - rt_spin_lock(&pirq->rw_lock); \ - if (ppic->ops->fn) \ - { \ - struct rt_pic *cpic; \ - cpic = pirq->pic; /* push old pic */ \ - pirq->pic = ppic; \ - ret ppic->ops->fn(pirq __VA_ARGS__); \ - pirq->pic = cpic; /* pop old pic */ \ - } \ - rt_spin_unlock(&pirq->rw_lock); \ - } \ -}) - -void rt_pic_irq_parent_enable(struct rt_pic *ppic, struct rt_pic_irq *pirq) +void rt_pic_irq_parent_enable(struct rt_pic_irq *pirq) { - _pirq_parent_call_helper(ppic, pirq, irq_enable,,); + RT_ASSERT(pirq != RT_NULL); + pirq = pirq->parent; + + if (pirq->pic->ops->irq_enable) + { + pirq->pic->ops->irq_enable(pirq); + } } -void rt_pic_irq_parent_disable(struct rt_pic *ppic, struct rt_pic_irq *pirq) +void rt_pic_irq_parent_disable(struct rt_pic_irq *pirq) { - _pirq_parent_call_helper(ppic, pirq, irq_disable,,); + RT_ASSERT(pirq != RT_NULL); + pirq = pirq->parent; + + if (pirq->pic->ops->irq_disable) + { + pirq->pic->ops->irq_disable(pirq); + } } -void rt_pic_irq_parent_ack(struct rt_pic *ppic, struct rt_pic_irq *pirq) +void rt_pic_irq_parent_ack(struct rt_pic_irq *pirq) { - _pirq_parent_call_helper(ppic, pirq, irq_ack,,); + RT_ASSERT(pirq != RT_NULL); + pirq = pirq->parent; + + if (pirq->pic->ops->irq_ack) + { + pirq->pic->ops->irq_ack(pirq); + } } -void rt_pic_irq_parent_mask(struct rt_pic *ppic, struct rt_pic_irq *pirq) +void rt_pic_irq_parent_mask(struct rt_pic_irq *pirq) { - _pirq_parent_call_helper(ppic, pirq, irq_mask,,); + RT_ASSERT(pirq != RT_NULL); + pirq = pirq->parent; + + if (pirq->pic->ops->irq_mask) + { + pirq->pic->ops->irq_mask(pirq); + } } -void rt_pic_irq_parent_unmask(struct rt_pic *ppic, struct rt_pic_irq *pirq) +void rt_pic_irq_parent_unmask(struct rt_pic_irq *pirq) { - _pirq_parent_call_helper(ppic, pirq, irq_unmask,,); + RT_ASSERT(pirq != RT_NULL); + pirq = pirq->parent; + + if (pirq->pic->ops->irq_unmask) + { + pirq->pic->ops->irq_unmask(pirq); + } } -void rt_pic_irq_parent_eoi(struct rt_pic *ppic, struct rt_pic_irq *pirq) +void rt_pic_irq_parent_eoi(struct rt_pic_irq *pirq) { - _pirq_parent_call_helper(ppic, pirq, irq_eoi,,); + RT_ASSERT(pirq != RT_NULL); + pirq = pirq->parent; + + if (pirq->pic->ops->irq_eoi) + { + pirq->pic->ops->irq_eoi(pirq); + } } -rt_err_t rt_pic_irq_parent_set_priority(struct rt_pic *ppic, struct rt_pic_irq *pirq, rt_uint32_t priority) +rt_err_t rt_pic_irq_parent_set_priority(struct rt_pic_irq *pirq, rt_uint32_t priority) { rt_err_t err = -RT_ENOSYS; - _pirq_parent_call_helper(ppic, pirq, irq_set_priority, err = , ,priority); + RT_ASSERT(pirq != RT_NULL); + pirq = pirq->parent; + + if (pirq->pic->ops->irq_set_priority) + { + if (!(err = pirq->pic->ops->irq_set_priority(pirq, priority))) + { + pirq->priority = priority; + } + } return err; } -rt_err_t rt_pic_irq_parent_set_affinity(struct rt_pic *ppic, struct rt_pic_irq *pirq, bitmap_t *affinity) +rt_err_t rt_pic_irq_parent_set_affinity(struct rt_pic_irq *pirq, bitmap_t *affinity) { rt_err_t err = -RT_ENOSYS; - _pirq_parent_call_helper(ppic, pirq, irq_set_affinity, err = , ,affinity); + RT_ASSERT(pirq != RT_NULL); + pirq = pirq->parent; + + if (pirq->pic->ops->irq_set_affinity) + { + if (!(err = pirq->pic->ops->irq_set_affinity(pirq, affinity))) + { + rt_memcpy(pirq->affinity, affinity, sizeof(pirq->affinity)); + } + } return err; } -rt_err_t rt_pic_irq_parent_set_triger_mode(struct rt_pic *ppic, struct rt_pic_irq *pirq, rt_uint32_t mode) +rt_err_t rt_pic_irq_parent_set_triger_mode(struct rt_pic_irq *pirq, rt_uint32_t mode) { rt_err_t err = -RT_ENOSYS; - _pirq_parent_call_helper(ppic, pirq, irq_set_triger_mode, err = , ,mode); + RT_ASSERT(pirq != RT_NULL); + pirq = pirq->parent; + + if (pirq->pic->ops->irq_set_triger_mode) + { + if (!(err = pirq->pic->ops->irq_set_triger_mode(pirq, mode))) + { + pirq->mode = mode; + } + } return err; } -#undef _pirq_parent_call_helper - #ifdef RT_USING_OFW RT_OFW_STUB_RANGE_EXPORT(pic, _pic_ofw_start, _pic_ofw_end); diff --git a/components/drivers/pin/Kconfig b/components/drivers/pin/Kconfig index 2d359718085c..92f51bc6bdcc 100755 --- a/components/drivers/pin/Kconfig +++ b/components/drivers/pin/Kconfig @@ -1,5 +1,5 @@ menuconfig RT_USING_PIN - bool "Using generic GPIO device drivers" + bool "Using Generic GPIO device drivers" default y config RT_PIN_PL061 @@ -7,3 +7,17 @@ config RT_PIN_PL061 depends on RT_USING_DM depends on RT_USING_PIN default n + +config RT_PIN_RASPBERRYPI_EXP + bool "Raspberry Pi 3 GPIO Expander" + depends on RT_USING_DM + depends on RT_USING_PIN + select RT_FIRMWARE_RASPBERRYPI + default n + +config RT_PIN_ROCKCHIP + bool "Rockchip GPIO support" + depends on RT_USING_DM + depends on RT_USING_PIN + select RT_USING_OFW + default n diff --git a/components/drivers/pin/SConscript b/components/drivers/pin/SConscript index 0cfe2790d513..780d51700702 100755 --- a/components/drivers/pin/SConscript +++ b/components/drivers/pin/SConscript @@ -10,9 +10,21 @@ CPPPATH = [cwd + '/../include'] src = ['pin.c'] +if GetDepend(['RT_USING_DM']): + src += ['pin_dm.c'] + +if GetDepend(['RT_USING_OFW']): + src += ['pin_ofw.c'] + if GetDepend(['RT_PIN_PL061']): src += ['pin-pl061.c'] +if GetDepend(['RT_PIN_RASPBERRYPI_EXP']): + src += ['pin-raspberrypi-exp.c'] + +if GetDepend(['RT_PIN_ROCKCHIP']): + src += ['pin-rockchip.c'] + group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) Return('group') diff --git a/components/drivers/pin/pin-pl061.c b/components/drivers/pin/pin-pl061.c index dc5bc899f6df..79c4cfd78917 100755 --- a/components/drivers/pin/pin-pl061.c +++ b/components/drivers/pin/pin-pl061.c @@ -12,6 +12,8 @@ #include #include +#include "pin_dm.h" + #define PL061_DIR 0x400 #define PL061_IS 0x404 #define PL061_IBE 0x408 @@ -25,13 +27,15 @@ struct pl061 { - void *base; + struct rt_device_pin parent; + int irq; + void *base; + struct rt_clk *pclk; struct rt_spinlock spinlock; - void (*hdr[PL061_GPIO_NR])(void *args); - void *args[PL061_GPIO_NR]; + struct rt_pin_irq_hdr hdr[PL061_GPIO_NR]; }; #define raw_to_pl061(raw) ((struct pl061 *)((raw)->user_data)) @@ -64,11 +68,15 @@ static void pl061_isr(int irqno, void *param) { if (pending & RT_BIT(pin)) { + struct rt_pin_irq_hdr *hdr_info = &pl061->hdr[pin]; + mask |= RT_BIT(pin); - if (pl061->hdr[pin]) + pin_pic_handle_isr(&pl061->parent, pin); + + if (hdr_info->hdr) { - pl061->hdr[pin](pl061->args[pin]); + hdr_info->hdr(hdr_info->args); } } } @@ -141,8 +149,93 @@ static rt_int8_t pl061_pin_read(struct rt_device *device, rt_base_t pin) return value; } +static rt_err_t pl061_pin_irq_mode(struct rt_device *device, rt_base_t pin, rt_uint8_t mode); + static rt_err_t pl061_pin_attach_irq(struct rt_device *device, rt_base_t pin, rt_uint8_t mode, void (*hdr)(void *args), void *args) +{ + rt_err_t err; + rt_ubase_t level; + struct rt_pin_irq_hdr *hdr_info; + struct pl061 *pl061 = raw_to_pl061(device); + + if (pin < 0 || pin >= PL061_GPIO_NR) + { + return -RT_EINVAL; + } + + if ((err = pl061_pin_irq_mode(device, pin, mode))) + { + return err; + } + + level = rt_spin_lock_irqsave(&pl061->spinlock); + + hdr_info = &pl061->hdr[pin]; + hdr_info->hdr = hdr; + hdr_info->args = args; + + rt_spin_unlock_irqrestore(&pl061->spinlock, level); + + return err; +} + +static rt_err_t pl061_pin_detach_irq(struct rt_device *device, rt_base_t pin) +{ + rt_err_t err = RT_EOK; + struct rt_pin_irq_hdr *hdr_info; + struct pl061 *pl061 = raw_to_pl061(device); + + if (pin >= 0 && pin < PL061_GPIO_NR) + { + rt_ubase_t level = rt_spin_lock_irqsave(&pl061->spinlock); + + hdr_info = &pl061->hdr[pin]; + hdr_info->hdr = RT_NULL; + hdr_info->args = RT_NULL; + + rt_spin_unlock_irqrestore(&pl061->spinlock, level); + } + else + { + err = -RT_EINVAL; + } + + return err; +} + +static rt_err_t pl061_pin_irq_enable(struct rt_device *device, rt_base_t pin, rt_uint8_t enabled) +{ + rt_err_t err = RT_EOK; + struct pl061 *pl061 = raw_to_pl061(device); + + if (pin >= 0 && pin < PL061_GPIO_NR) + { + rt_uint8_t gpioie, mask = RT_BIT(pin); + rt_ubase_t level = rt_spin_lock_irqsave(&pl061->spinlock); + + if (enabled) + { + gpioie = pl061_read(pl061, PL061_IE) | mask; + } + else + { + gpioie = pl061_read(pl061, PL061_IE) & ~mask; + } + + pl061_write(pl061, PL061_IE, gpioie); + + rt_spin_unlock_irqrestore(&pl061->spinlock, level); + } + else + { + err = -RT_EINVAL; + } + + return err; +} + +static rt_err_t pl061_pin_irq_mode(struct rt_device *device, rt_base_t pin, rt_uint8_t mode) { rt_err_t err = RT_EOK; struct pl061 *pl061 = raw_to_pl061(device); @@ -213,62 +306,6 @@ static rt_err_t pl061_pin_attach_irq(struct rt_device *device, rt_base_t pin, pl061_write(pl061, PL061_IBE, gpioibe); pl061_write(pl061, PL061_IEV, gpioiev); - pl061->hdr[pin] = hdr; - pl061->args[pin] = args; - - rt_spin_unlock_irqrestore(&pl061->spinlock, level); - } - else - { - err = -RT_EINVAL; - } - - return err; -} - -static rt_err_t pl061_pin_detach_irq(struct rt_device *device, rt_base_t pin) -{ - rt_err_t err = RT_EOK; - struct pl061 *pl061 = raw_to_pl061(device); - - if (pin >= 0 && pin < PL061_GPIO_NR) - { - rt_ubase_t level = rt_spin_lock_irqsave(&pl061->spinlock); - - pl061->hdr[pin] = RT_NULL; - pl061->args[pin] = RT_NULL; - - rt_spin_unlock_irqrestore(&pl061->spinlock, level); - } - else - { - err = -RT_EINVAL; - } - - return err; -} - -static rt_err_t pl061_pin_irq_enable(struct rt_device *device, rt_base_t pin, rt_uint8_t enabled) -{ - rt_err_t err = RT_EOK; - struct pl061 *pl061 = raw_to_pl061(device); - - if (pin >= 0 && pin < PL061_GPIO_NR) - { - rt_uint8_t gpioie, mask = RT_BIT(pin); - rt_ubase_t level = rt_spin_lock_irqsave(&pl061->spinlock); - - if (enabled) - { - gpioie = pl061_read(pl061, PL061_IE) | mask; - } - else - { - gpioie = pl061_read(pl061, PL061_IE) & ~mask; - } - - pl061_write(pl061, PL061_IE, gpioie); - rt_spin_unlock_irqrestore(&pl061->spinlock, level); } else @@ -287,60 +324,83 @@ static const struct rt_pin_ops pl061_pin_ops = .pin_attach_irq = pl061_pin_attach_irq, .pin_detach_irq = pl061_pin_detach_irq, .pin_irq_enable = pl061_pin_irq_enable, + .pin_irq_mode = pl061_pin_irq_mode, }; -static rt_err_t pl061_ofw_init(struct rt_platform_device *pdev, struct pl061 *pl061) +static rt_err_t pl061_probe(struct rt_platform_device *pdev) { - rt_err_t err = RT_EOK; - struct rt_ofw_node *np = pdev->parent.ofw_node; - - pl061->base = rt_ofw_iomap(np, 0); + rt_err_t err; + struct rt_device *dev = &pdev->parent; + struct pl061 *pl061 = rt_calloc(1, sizeof(*pl061)); - if (pl061->base) + if (!pl061) { - pl061->irq = rt_ofw_get_irq(np, 0); - - if (pl061->irq < 0) - { - err = -RT_ERROR; - } + return -RT_ENOMEM; } - else + + pl061->base = rt_dm_dev_iomap(dev, 0); + + if (!pl061->base) { err = -RT_EIO; - } - return err; -} + goto _fail; + } -static rt_err_t pl061_probe(struct rt_platform_device *pdev) -{ - rt_err_t err = RT_EOK; - struct pl061 *pl061 = rt_calloc(1, sizeof(*pl061)); + pl061->irq = rt_dm_dev_get_irq(dev, 0); - if (pl061) + if (pl061->irq < 0) { - err = pl061_ofw_init(pdev, pl061); + err = pl061->irq; + + goto _fail; } - else + + pl061->pclk = rt_clk_get_by_name(dev, "apb_pclk"); + + if (rt_is_err(pl061->pclk)) { - err = -RT_ENOMEM; + err = rt_ptr_err(pl061->pclk); + + goto _fail; } - if (!err) + if ((err = rt_clk_prepare_enable(pl061->pclk))) { - rt_spin_lock_init(&pl061->spinlock); + goto _fail; + } - rt_device_pin_register("gpio", &pl061_pin_ops, pl061); + rt_dm_dev_bind_fwdata(dev, RT_NULL, &pl061->parent); - rt_hw_interrupt_install(pl061->irq, pl061_isr, pl061, "gpio-pl061"); - rt_hw_interrupt_umask(pl061->irq); + rt_spin_lock_init(&pl061->spinlock); + + pl061->parent.irqchip.irq = pl061->irq; + pl061->parent.irqchip.pin_range[0] = 0; + pl061->parent.irqchip.pin_range[1] = PL061_GPIO_NR - 1; + pl061->parent.ops = &pl061_pin_ops; + pin_pic_init(&pl061->parent); + + rt_device_pin_register("gpio", &pl061_pin_ops, pl061); + + rt_hw_interrupt_install(pl061->irq, pl061_isr, pl061, "gpio-pl061"); + rt_hw_interrupt_umask(pl061->irq); + + return RT_EOK; + +_fail: + if (pl061->base) + { + rt_iounmap(pl061->base); } - else + + if (!rt_is_err_or_null(pl061->pclk)) { - rt_free(pl061); + rt_clk_disable_unprepare(pl061->pclk); + rt_clk_put(pl061->pclk); } + rt_free(pl061); + return err; } diff --git a/components/drivers/pin/pin-raspberrypi-exp.c b/components/drivers/pin/pin-raspberrypi-exp.c new file mode 100644 index 000000000000..24a053e1720e --- /dev/null +++ b/components/drivers/pin/pin-raspberrypi-exp.c @@ -0,0 +1,263 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-3-08 GuEe-GUI the first version + */ + +#include +#include +#include + +#define DBG_TAG "pin.raspberrypi-exp" +#define DBG_LVL DBG_INFO +#include + +#include "pin_dm.h" +#include "../firmware/raspberrypi/firmware.h" + +#define RPI_EXP_GPIO_BASE 128 + +#define RPI_EXP_GPIO_DIR_IN 0 +#define RPI_EXP_GPIO_DIR_OUT 1 + +#define RPI_EXP_GPIO_NR 8 + +struct gpio_set_config +{ + rt_uint32_t gpio; + rt_uint32_t direction; + rt_uint32_t polarity; + rt_uint32_t term_en; + rt_uint32_t term_pull_up; + rt_uint32_t state; +}; + +struct gpio_get_config +{ + rt_uint32_t gpio; + rt_uint32_t direction; + rt_uint32_t polarity; + rt_uint32_t term_en; + rt_uint32_t term_pull_up; +}; + +struct gpio_get_set_state +{ + rt_uint32_t gpio; + rt_uint32_t state; +}; + +struct rpi_exp_gpio +{ + struct rt_device_pin parent; + + struct rpi_firmware *rpi_fw; +}; + +#define raw_to_rpi_exp_gpio(raw) rt_container_of(raw, struct rpi_exp_gpio, parent) + +static int rpi_exp_gpio_get_polarity(struct rpi_exp_gpio *regpio, rt_base_t pin) +{ + rt_err_t err; + struct gpio_get_config get; + + get.gpio = pin + RPI_EXP_GPIO_BASE; + + if ((err = rpi_firmware_property(regpio->rpi_fw, RPI_FIRMWARE_GET_GPIO_CONFIG, + &get, sizeof(get))) || get.gpio != 0) + { + LOG_E("Failed to get PIN %u config (%s, %x)", pin, rt_strerror(err), get.gpio); + + return err ? err : -RT_EIO; + } + + return get.polarity; +} + +static void rpi_exp_gpio_pin_mode(struct rt_device *device, rt_base_t pin, rt_uint8_t mode) +{ + rt_err_t err; + rt_int32_t polarity; + union + { + struct gpio_set_config in; + struct gpio_set_config out; + } set; + struct rpi_exp_gpio *regpio = raw_to_rpi_exp_gpio(device); + + if (pin >= RPI_EXP_GPIO_NR) + { + return; + } + + switch (mode) + { + case PIN_MODE_OUTPUT: + set.out.gpio = pin + RPI_EXP_GPIO_BASE; + set.out.direction = RPI_EXP_GPIO_DIR_OUT; + set.out.term_en = 0; /* n/a as an output */ + set.out.term_pull_up = 0; /* n/a as termination disabled */ + set.out.state = 1; /* Output state */ + + polarity = rpi_exp_gpio_get_polarity(regpio, pin); + if (polarity < 0) + { + LOG_E("Failed to get PIN %u polarity error = %s", rt_strerror(polarity)); + + break; + } + set.out.polarity = polarity; /* Retain existing setting */ + + if ((err = rpi_firmware_property(regpio->rpi_fw, RPI_FIRMWARE_SET_GPIO_CONFIG, + &set.out, sizeof(set.out))) || set.out.gpio != 0) + { + LOG_E("Failed to set PIN %u to output (%s, %x)", pin, rt_strerror(err), set.out.gpio); + } + + break; + + case PIN_MODE_INPUT: + set.in.gpio = pin + RPI_EXP_GPIO_BASE; + set.in.direction = RPI_EXP_GPIO_DIR_IN; + set.in.term_en = 0; /* Termination disabled */ + set.in.term_pull_up = 0; /* n/a as termination disabled */ + set.in.state = 0; /* n/a as configured as an input */ + + polarity = rpi_exp_gpio_get_polarity(regpio, pin); + if (polarity < 0) + { + LOG_E("Failed to get PIN %u polarity error = %s", rt_strerror(polarity)); + + break; + } + set.in.polarity = polarity; /* Retain existing setting */ + + if ((err = rpi_firmware_property(regpio->rpi_fw, RPI_FIRMWARE_SET_GPIO_CONFIG, + &set.in, sizeof(set.in))) || set.in.gpio != 0) + { + LOG_E("Failed to set PIN %u to input (%s, %x)", pin, rt_strerror(err), set.in.gpio); + } + break; + + default: + break; + } +} + +static void rpi_exp_gpio_pin_write(struct rt_device *device, rt_base_t pin, rt_uint8_t value) +{ + rt_err_t err; + struct gpio_get_set_state set; + struct rpi_exp_gpio *regpio = raw_to_rpi_exp_gpio(device); + + if (pin >= RPI_EXP_GPIO_NR) + { + return; + } + + set.gpio = pin + RPI_EXP_GPIO_BASE; + set.state = value; + + if ((err = rpi_firmware_property(regpio->rpi_fw, RPI_FIRMWARE_SET_GPIO_STATE, + &set, sizeof(set))) || set.gpio != 0) + { + LOG_E("Failed to set PIN %u state (%s, %x)", pin, rt_strerror(err), set.gpio); + } +} + +static rt_int8_t rpi_exp_gpio_pin_read(struct rt_device *device, rt_base_t pin) +{ + rt_err_t err; + struct gpio_get_set_state get; + struct rpi_exp_gpio *regpio = raw_to_rpi_exp_gpio(device); + + if (pin >= RPI_EXP_GPIO_NR) + { + return -RT_EINVAL; + } + + get.gpio = pin + RPI_EXP_GPIO_BASE; + get.state = 0; + + if ((err = rpi_firmware_property(regpio->rpi_fw, RPI_FIRMWARE_GET_GPIO_STATE, + &get, sizeof(get))) || get.gpio != 0) + { + LOG_E("Failed to get PIN %u state (%s, %x)", pin, rt_strerror(err), get.gpio); + + return err ? err : -RT_EIO; + } + + return !!get.state; +} + +static const struct rt_pin_ops rpi_exp_gpio_pin_ops = +{ + .pin_mode = rpi_exp_gpio_pin_mode, + .pin_write = rpi_exp_gpio_pin_write, + .pin_read = rpi_exp_gpio_pin_read, +}; + +static rt_err_t rpi_exp_gpio_probe(struct rt_platform_device *pdev) +{ + rt_err_t err; + struct rt_device *dev = &pdev->parent; + struct rt_ofw_node *np = dev->ofw_node, *fw_np; + struct rpi_exp_gpio *rpi_gpio = rt_calloc(1, sizeof(*rpi_gpio)); + + if (!rpi_gpio) + { + return -RT_ENOMEM; + } + + fw_np = rt_ofw_get_parent(np); + + if (!fw_np) + { + err = -RT_EINVAL; + goto _fail; + } + + rpi_gpio->rpi_fw = rpi_firmware_get(fw_np); + rt_ofw_node_put(fw_np); + + if (!rpi_gpio->rpi_fw) + { + err = -RT_EINVAL; + goto _fail; + } + + rpi_gpio->parent.ops = &rpi_exp_gpio_pin_ops; + + rt_dm_dev_bind_fwdata(dev, RT_NULL, &rpi_gpio->parent); + + return RT_EOK; + +_fail: + if (rpi_gpio->rpi_fw) + { + rpi_firmware_put(rpi_gpio->rpi_fw); + } + + rt_free(rpi_gpio); + + return err; +} + +static const struct rt_ofw_node_id rpi_exp_gpio_ofw_ids[] = +{ + { .compatible = "raspberrypi,firmware-gpio" }, + { /* sentinel */ } +}; + +static struct rt_platform_driver rpi_exp_gpio_driver = +{ + .name = "raspberrypi-exp-gpio", + .ids = rpi_exp_gpio_ofw_ids, + + .probe = rpi_exp_gpio_probe, +}; +RT_PLATFORM_DRIVER_EXPORT(rpi_exp_gpio_driver); diff --git a/components/drivers/pin/pin-rockchip.c b/components/drivers/pin/pin-rockchip.c new file mode 100644 index 000000000000..6cbd205b7c5d --- /dev/null +++ b/components/drivers/pin/pin-rockchip.c @@ -0,0 +1,687 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-11-26 GuEe-GUI first version + */ + +#include +#include +#include + +#include "pin_dm.h" +#include "../pinctrl/pinctrl-rockchip.h" + +#define RK_PIN_MAX RK_GPIO4_D7 + +struct rk_gpio +{ + struct rt_device parent; + rt_bool_t init_ok; + + struct + { + struct rt_pin_irq_hdr hdr; + rt_uint32_t mask_cache; + } irq_info[RK_PIN_MAX]; + + struct rockchip_pinctrl_device *pinctrl_dev; +}; + +static struct rk_gpio rk_gpio = +{ + .init_ok = RT_FALSE, + .irq_info[0 ... RK_PIN_MAX - 1] = + { + .mask_cache = 0xffffffffU, + } +}; + +#define raw_pin_to_bank(raw, pin) \ +({ \ + struct rockchip_pin_bank *___pin_bank = RT_NULL; \ + struct rockchip_pinctrl_device *___pinctrl_dev = (raw)->user_data; \ + struct rockchip_pin_ctrl *___pinctrl = ___pinctrl_dev->pinctrl; \ + if (pin <= RK_PIN_MAX && ___pinctrl->pin_banks[(pin) / 32].gpio_regs) \ + { \ + ___pin_bank = &___pinctrl->pin_banks[(pin) / 32]; \ + } \ + ___pin_bank; \ +}) +#define raw_pin_to_id(pin) (pin % 32) + +#define GPIO_TYPE_V1 0 +#define GPIO_TYPE_V2 0x01000c2b +#define GPIO_TYPE_V2_1 0x0101157c + +static const struct rockchip_gpio_regs _gpio_regs_v1 = +{ + .port_dr = 0x00, + .port_ddr = 0x04, + .int_en = 0x30, + .int_mask = 0x34, + .int_type = 0x38, + .int_polarity = 0x3c, + .int_status = 0x40, + .int_rawstatus = 0x44, + .debounce = 0x48, + .port_eoi = 0x4c, + .ext_port = 0x50, +}; + +static const struct rockchip_gpio_regs _gpio_regs_v2 = +{ + .port_dr = 0x00, + .port_ddr = 0x08, + .int_en = 0x10, + .int_mask = 0x18, + .int_type = 0x20, + .int_polarity = 0x28, + .int_bothedge = 0x30, + .int_status = 0x50, + .int_rawstatus = 0x58, + .debounce = 0x38, + .dbclk_div_en = 0x40, + .dbclk_div_con = 0x48, + .port_eoi = 0x60, + .ext_port = 0x70, + .version_id = 0x78, +}; + +rt_inline void gpio_writel_v2(void *base, rt_uint32_t val) +{ + HWREG32(base) = (val & 0xffff) | 0xffff0000U; + HWREG32(base + 0x4) = (val >> 16) | 0xffff0000U; +} + +rt_inline rt_uint32_t gpio_readl_v2(void *base) +{ + return HWREG32(base + 0x4) << 16 | HWREG32(base); +} + +rt_inline void rockchip_gpio_writel(struct rockchip_pin_bank *pin_bank, rt_uint32_t value, int offset) +{ + void *base = pin_bank->reg_base + offset; + + if (pin_bank->gpio_type == GPIO_TYPE_V2) + { + gpio_writel_v2(base, value); + } + else + { + HWREG32(base) = value; + } +} + +rt_inline rt_uint32_t rockchip_gpio_readl(struct rockchip_pin_bank *pin_bank, int offset) +{ + rt_uint32_t value; + void *base = pin_bank->reg_base + offset; + + if (pin_bank->gpio_type == GPIO_TYPE_V2) + { + value = gpio_readl_v2(base); + } + else + { + value = HWREG32(base); + } + + return value; +} + +rt_inline void rockchip_gpio_writel_bit(struct rockchip_pin_bank *pin_bank, + rt_uint32_t bit, rt_uint32_t value, int offset) +{ + rt_uint32_t data; + void *base = pin_bank->reg_base + offset; + + if (pin_bank->gpio_type == GPIO_TYPE_V2) + { + if (value) + { + data = RT_BIT(bit % 16) | RT_BIT(bit % 16 + 16); + } + else + { + data = RT_BIT(bit % 16 + 16); + } + HWREG32(bit >= 16 ? base + 0x4 : base) = data; + } + else + { + data = HWREG32(base); + data &= ~RT_BIT(bit); + + if (value) + { + data |= RT_BIT(bit); + } + HWREG32(base) = data; + } +} + +rt_inline rt_uint32_t rockchip_gpio_readl_bit(struct rockchip_pin_bank *pin_bank, + rt_uint32_t bit, int offset) +{ + rt_uint32_t data; + void *base = pin_bank->reg_base + offset; + + if (pin_bank->gpio_type == GPIO_TYPE_V2) + { + data = HWREG32(bit >= 16 ? base + 0x4 : base); + data >>= bit % 16; + } + else + { + data = HWREG32(base); + data >>= bit; + } + + return data & 1; +} + +static void rk_pin_mode(struct rt_device *device, rt_base_t pin, rt_uint8_t mode) +{ + rt_base_t level; + struct rockchip_pinctrl_device *pinctrl_dev = device->user_data; + struct rockchip_pin_bank *pin_bank = raw_pin_to_bank(device, pin); + + if (!pin_bank) + { + return; + } + + switch (mode) + { + case PIN_MODE_OUTPUT: + case PIN_MODE_OUTPUT_OD: + pinctrl_dev->pinctrl->set_mux(pin_bank, pin, RK_FUNC_GPIO); + + level = rt_spin_lock_irqsave(&pin_bank->spinlock); + rockchip_gpio_writel_bit(pin_bank, raw_pin_to_id(pin), 1, pin_bank->gpio_regs->port_ddr); + rt_spin_unlock_irqrestore(&pin_bank->spinlock, level); + break; + + case PIN_MODE_INPUT: + case PIN_MODE_INPUT_PULLUP: + case PIN_MODE_INPUT_PULLDOWN: + pinctrl_dev->pinctrl->set_mux(pin_bank, pin, RK_FUNC_GPIO); + + level = rt_spin_lock_irqsave(&pin_bank->spinlock); + rockchip_gpio_writel_bit(pin_bank, raw_pin_to_id(pin), 0, pin_bank->gpio_regs->port_ddr); + rt_spin_unlock_irqrestore(&pin_bank->spinlock, level); + break; + + default: + break; + } +} + +static void rk_pin_write(struct rt_device *device, rt_base_t pin, rt_uint8_t value) +{ + struct rockchip_pin_bank *pin_bank = raw_pin_to_bank(device, pin); + + if (!pin_bank) + { + return; + } + + rockchip_gpio_writel_bit(pin_bank, raw_pin_to_id(pin), value, pin_bank->gpio_regs->port_dr); +} + +static rt_int8_t rk_pin_read(struct rt_device *device, rt_base_t pin) +{ + rt_uint32_t data; + struct rockchip_pin_bank *pin_bank = raw_pin_to_bank(device, pin); + + if (!pin_bank) + { + return -1; + } + + data = HWREG32(pin_bank->reg_base + pin_bank->gpio_regs->ext_port); + data >>= raw_pin_to_id(pin); + + return data & 1; +} + +static rt_err_t rk_pin_irq_mode(struct rt_device *device, rt_base_t pin, rt_uint8_t mode); + +static rt_err_t rk_pin_attach_irq(struct rt_device *device, rt_base_t pin, rt_uint8_t mode, + void (*hdr)(void *args), void *args) +{ + rt_err_t err; + typeof(rk_gpio.irq_info[0]) *irq_info; + struct rockchip_pin_bank *pin_bank = raw_pin_to_bank(device, pin); + + if (!pin_bank || !hdr) + { + return -RT_EINVAL; + } + + if ((err = rk_pin_irq_mode(device, pin, mode))) + { + return err; + } + + irq_info = &rk_gpio.irq_info[pin]; + + irq_info->hdr.mode = mode; + irq_info->hdr.args = args; + irq_info->hdr.hdr = hdr; + + return RT_EOK; +} + +static rt_err_t rk_pin_detach_irq(struct rt_device *device, rt_base_t pin) +{ + typeof(rk_gpio.irq_info[0]) *irq_info; + struct rockchip_pin_bank *pin_bank = raw_pin_to_bank(device, pin); + + if (!pin_bank) + { + return -RT_EINVAL; + } + + irq_info = &rk_gpio.irq_info[pin]; + + irq_info->hdr.hdr = RT_NULL; + irq_info->hdr.args = RT_NULL; + + return RT_EOK; +} + +static rt_err_t rk_pin_irq_enable(struct rt_device *device, rt_base_t pin, rt_uint8_t enabled) +{ + rt_base_t level; + typeof(rk_gpio.irq_info[0]) *irq_info; + struct rockchip_pin_bank *pin_bank = raw_pin_to_bank(device, pin); + + if (!pin_bank) + { + return -RT_EINVAL; + } + + level = rt_spin_lock_irqsave(&pin_bank->spinlock); + irq_info = &rk_gpio.irq_info[pin]; + + if (enabled) + { + irq_info->mask_cache &= ~RT_BIT(raw_pin_to_id(pin)); + } + else + { + irq_info->mask_cache |= RT_BIT(raw_pin_to_id(pin)); + } + rockchip_gpio_writel(pin_bank, irq_info->mask_cache, pin_bank->gpio_regs->int_mask); + + rt_spin_unlock_irqrestore(&pin_bank->spinlock, level); + + return RT_EOK; +} + +static rt_err_t rk_pin_irq_mode(struct rt_device *device, rt_base_t pin, rt_uint8_t mode) +{ + rt_base_t level; + rt_err_t err = RT_EOK; + rt_uint32_t mask, int_level, polarity, data; + struct rockchip_pin_bank *pin_bank = raw_pin_to_bank(device, pin); + + if (!pin_bank) + { + return -RT_EINVAL; + } + + level = rt_spin_lock_irqsave(&pin_bank->spinlock); + + rockchip_gpio_writel_bit(pin_bank, raw_pin_to_id(pin), 0, pin_bank->gpio_regs->port_ddr); + + mask = RT_BIT(raw_pin_to_id(pin)); + int_level = rockchip_gpio_readl(pin_bank, pin_bank->gpio_regs->int_type); + polarity = rockchip_gpio_readl(pin_bank, pin_bank->gpio_regs->int_polarity); + + if (mode == PIN_IRQ_MODE_RISING_FALLING) + { + if (pin_bank->gpio_type == GPIO_TYPE_V2) + { + rockchip_gpio_writel_bit(pin_bank, raw_pin_to_id(pin), 1, pin_bank->gpio_regs->int_bothedge); + goto _end; + } + else + { + pin_bank->toggle_edge_mode |= mask; + int_level &= ~mask; + + /* + * Determine gpio state. If 1 next interrupt should be + * low otherwise high. + */ + data = HWREG32(pin_bank->reg_base + pin_bank->gpio_regs->ext_port); + + if (data & mask) + { + polarity &= ~mask; + } + else + { + polarity |= mask; + } + } + } + else + { + if (pin_bank->gpio_type == GPIO_TYPE_V2) + { + rockchip_gpio_writel_bit(pin_bank, raw_pin_to_id(pin), 0, pin_bank->gpio_regs->int_bothedge); + } + else + { + pin_bank->toggle_edge_mode &= ~mask; + } + + switch (mode) + { + case PIN_IRQ_MODE_RISING: + int_level |= mask; + polarity |= mask; + break; + + case PIN_IRQ_MODE_FALLING: + int_level |= mask; + polarity &= ~mask; + break; + + case PIN_IRQ_MODE_HIGH_LEVEL: + int_level &= ~mask; + polarity |= mask; + break; + + case PIN_IRQ_MODE_LOW_LEVEL: + int_level &= ~mask; + polarity &= ~mask; + break; + + default: + err = -RT_EINVAL; + goto _end; + } + } + + rockchip_gpio_writel(pin_bank, int_level, pin_bank->gpio_regs->int_type); + rockchip_gpio_writel(pin_bank, polarity, pin_bank->gpio_regs->int_polarity); + +_end: + rt_spin_unlock_irqrestore(&pin_bank->spinlock, level); + + return err; +} + +static rt_ssize_t rk_pin_parse(struct rt_device *device, struct rt_ofw_cell_args *args, rt_uint32_t *flags) +{ + rt_ssize_t pin; + struct rt_device_pin *pin_dev = rt_ofw_data(args->data); + + pin = pin_dev->irqchip.pin_range[0] + args->args[0]; + + if (flags) + { + *flags = args->args[1]; + } + + return pin; +} + +static const struct rt_pin_ops rk_pin_ops = +{ + .pin_mode = rk_pin_mode, + .pin_write = rk_pin_write, + .pin_read = rk_pin_read, + .pin_attach_irq = rk_pin_attach_irq, + .pin_detach_irq = rk_pin_detach_irq, + .pin_irq_enable = rk_pin_irq_enable, + .pin_irq_mode = rk_pin_irq_mode, + .pin_parse = rk_pin_parse, +}; + +static void rk_pin_isr(int irqno, void *param) +{ + rt_uint32_t pending; + struct rockchip_pin_bank *pin_bank = param; + + pending = HWREG32(pin_bank->reg_base + pin_bank->gpio_regs->int_status); + + for (rt_ubase_t pin = 0; pin < 32 && pending; ++pin) + { + rt_uint32_t clr = RT_BIT(pin); + typeof(rk_gpio.irq_info[0]) *irq_info; + + if (!(clr & pending)) + { + continue; + } + + if (clr & pin_bank->toggle_edge_mode) + { + rt_uint32_t data, data_old, polarity; + + data = HWREG32(pin_bank->reg_base + pin_bank->gpio_regs->ext_port); + + do { + rt_ubase_t level = rt_spin_lock_irqsave(&pin_bank->spinlock); + + polarity = HWREG32(pin_bank->reg_base + pin_bank->gpio_regs->int_polarity); + + if (data & clr) + { + polarity &= ~clr; + } + else + { + polarity |= clr; + } + HWREG32(pin_bank->reg_base + pin_bank->gpio_regs->int_polarity) = polarity; + + rt_spin_unlock_irqrestore(&pin_bank->spinlock, level); + + data_old = data; + data = HWREG32(pin_bank->reg_base + pin_bank->gpio_regs->ext_port); + } while ((data & clr) != (data_old & clr)); + } + + pin_pic_handle_isr(&pin_bank->parent, pin); + + irq_info = &rk_gpio.irq_info[pin_bank->parent.irqchip.pin_range[0] + pin]; + + if (irq_info->hdr.hdr) + { + irq_info->hdr.hdr(irq_info->hdr.args); + } + + rockchip_gpio_writel_bit(pin_bank, pin, 1, pin_bank->gpio_regs->port_eoi); + + /* clear this pin irq */ + pending &= ~clr; + } +} + +static rt_err_t rockchip_gpio_probe(struct rt_platform_device *pdev) +{ + rt_err_t err; + int id, version; + const char *name; + struct rockchip_pin_bank *pin_bank; + struct rt_ofw_node *np = pdev->parent.ofw_node; + struct rt_ofw_node *npp = rt_ofw_get_parent(np); + struct rockchip_pinctrl_device *pinctrl_dev = rt_ofw_data(npp); + struct rockchip_pin_ctrl *pinctrl = pinctrl_dev->pinctrl; + static int gpio_uid = 0; + + rt_ofw_node_put(npp); + + id = rt_ofw_get_alias_id(np, "gpio"); + + if (id < 0) + { + id = gpio_uid++; + } + + pin_bank = &pinctrl->pin_banks[id]; + + pin_bank->reg_base = rt_ofw_iomap(np, 0); + + if (!pin_bank->reg_base) + { + err = -RT_EIO; + goto _out_res; + } + + pin_bank->irq = rt_ofw_get_irq(np, 0); + + if (pin_bank->irq < 0) + { + err = pin_bank->irq; + goto _out_res; + } + + pin_bank->clk = rt_ofw_get_clk(np, 0); + + if (rt_is_err(pin_bank->clk)) + { + err = rt_ptr_err(pin_bank->clk); + goto _out_res; + } + + rt_clk_prepare_enable(pin_bank->clk); + + version = HWREG32(pin_bank->reg_base + _gpio_regs_v2.version_id); + + if (version == GPIO_TYPE_V2 || version == GPIO_TYPE_V2_1) + { + pin_bank->gpio_regs = &_gpio_regs_v2; + pin_bank->gpio_type = GPIO_TYPE_V2; + + pin_bank->db_clk = rt_ofw_get_clk(np, 1); + + if (rt_is_err(pin_bank->db_clk)) + { + err = rt_ptr_err(pin_bank->db_clk); + goto _out_res; + } + } + else + { + pin_bank->gpio_regs = &_gpio_regs_v1; + pin_bank->gpio_type = GPIO_TYPE_V1; + } + + rt_dm_dev_set_name_auto(&rk_gpio.parent, "gpio"); + name = rt_dm_dev_get_name(&rk_gpio.parent); + + rt_hw_interrupt_install(pin_bank->irq, rk_pin_isr, pin_bank, name); + rt_hw_interrupt_umask(pin_bank->irq); + + rockchip_gpio_writel(pin_bank, 0xffffffffU, pin_bank->gpio_regs->int_mask); + rockchip_gpio_writel(pin_bank, 0xffffffffU, pin_bank->gpio_regs->port_eoi); + rockchip_gpio_writel(pin_bank, 0xffffffffU, pin_bank->gpio_regs->int_en); + + rt_spin_lock_init(&pin_bank->spinlock); + + pin_bank->parent.irqchip.irq = pin_bank->irq; + pin_bank->parent.irqchip.pin_range[0] = id * 32; + pin_bank->parent.irqchip.pin_range[1] = pin_bank->parent.irqchip.pin_range[0] + RK_PD7; + pin_bank->parent.ops = &rk_pin_ops; + pin_bank->parent.parent.user_data = pinctrl_dev; + pin_pic_init(&pin_bank->parent); + + rt_ofw_data(np) = &pin_bank->parent; + + if (!rk_gpio.init_ok) + { + rk_gpio.init_ok = RT_TRUE; + rk_gpio.pinctrl_dev = pinctrl_dev; + + rt_device_pin_register("gpio", &rk_pin_ops, pinctrl_dev); + } + + return RT_EOK; + +_out_res: + if (pin_bank->reg_base) + { + rt_iounmap(pin_bank->reg_base); + } + + if (rt_is_err_or_null(pin_bank->clk)) + { + rt_clk_disable_unprepare(pin_bank->clk); + rt_clk_put(pin_bank->clk); + } + + if (rt_is_err_or_null(pin_bank->db_clk)) + { + rt_clk_disable_unprepare(pin_bank->db_clk); + rt_clk_put(pin_bank->db_clk); + } + + return err; +} + +static const struct rt_ofw_node_id rockchip_gpio_ofw_ids[] = +{ + { .compatible = "rockchip,gpio-bank" }, + { /* sentinel */ } +}; + +static struct rt_platform_driver rockchip_gpio_driver = +{ + .name = "rockchip-gpio", + .ids = rockchip_gpio_ofw_ids, + + .probe = rockchip_gpio_probe, +}; + +static int rockchip_gpio_register(void) +{ + int idx = 0; + struct rt_ofw_node *np = rt_ofw_find_node_by_path("/pinctrl"), *gpio_np; + + if (!np || !rt_ofw_data(np)) + { + goto _end; + } + + rt_platform_driver_register(&rockchip_gpio_driver); + + rt_ofw_foreach_available_child_node(np, gpio_np) + { + if (idx > RK_GPIO4) + { + rt_ofw_node_put(gpio_np); + + break; + } + + if (!rt_ofw_prop_read_bool(gpio_np, "compatible")) + { + continue; + } + + if (rt_ofw_data(gpio_np)) + { + continue; + } + + ++idx; + rt_platform_ofw_device_probe_child(gpio_np); + } + +_end: + rt_ofw_node_put(np); + + return 0; +} +INIT_SUBSYS_EXPORT(rockchip_gpio_register); diff --git a/components/drivers/pin/pin_dm.c b/components/drivers/pin/pin_dm.c new file mode 100644 index 000000000000..8d9974a2311e --- /dev/null +++ b/components/drivers/pin/pin_dm.c @@ -0,0 +1,220 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-11-26 GuEe-GUI first version + */ + +#include "pin_dm.h" + +static void pin_dm_irq_mask(struct rt_pic_irq *pirq) +{ + struct rt_device_pin *gpio = pirq->pic->priv_data; + + gpio->ops->pin_irq_enable(&gpio->parent, pirq->hwirq, 0); +} + +static void pin_dm_irq_unmask(struct rt_pic_irq *pirq) +{ + struct rt_device_pin *gpio = pirq->pic->priv_data; + + gpio->ops->pin_irq_enable(&gpio->parent, pirq->hwirq, 1); +} + +static rt_err_t pin_dm_irq_set_triger_mode(struct rt_pic_irq *pirq, rt_uint32_t mode) +{ + rt_uint8_t pin_mode; + struct rt_device_pin *gpio = pirq->pic->priv_data; + + switch (mode) + { + case RT_IRQ_MODE_EDGE_RISING: + pin_mode = PIN_IRQ_MODE_RISING; + break; + + case RT_IRQ_MODE_EDGE_FALLING: + pin_mode = PIN_IRQ_MODE_FALLING; + break; + + case RT_IRQ_MODE_EDGE_BOTH: + pin_mode = PIN_IRQ_MODE_RISING_FALLING; + break; + + case RT_IRQ_MODE_LEVEL_HIGH: + pin_mode = PIN_IRQ_MODE_HIGH_LEVEL; + break; + + case RT_IRQ_MODE_LEVEL_LOW: + pin_mode = PIN_IRQ_MODE_LOW_LEVEL; + break; + + default: + return -RT_ENOSYS; + } + + return gpio->ops->pin_irq_mode(&gpio->parent, pirq->hwirq, pin_mode); +} + +static int pin_dm_irq_map(struct rt_pic *pic, int hwirq, rt_uint32_t mode) +{ + int irq = -1; + struct rt_device_pin *gpio = pic->priv_data; + struct rt_pic_irq *pirq = rt_pic_find_irq(pic, hwirq); + + if (pirq) + { + irq = rt_pic_config_irq(pic, hwirq, hwirq); + + if (irq >= 0) + { + rt_pic_cascade(pirq, gpio->irqchip.irq); + rt_pic_irq_set_triger_mode(irq, mode); + } + } + + return irq; +} + +static rt_err_t pin_dm_irq_parse(struct rt_pic *pic, struct rt_ofw_cell_args *args, struct rt_pic_irq *out_pirq) +{ + rt_err_t err = RT_EOK; + + if (args->args_count == 2) + { + out_pirq->hwirq = args->args[0]; + out_pirq->mode = args->args[1] & RT_IRQ_MODE_MASK; + } + else + { + err = -RT_EINVAL; + } + + return err; +} + +const static struct rt_pic_ops pin_dm_ops = +{ + .name = "GPIO", + .irq_enable = pin_dm_irq_mask, + .irq_disable = pin_dm_irq_unmask, + .irq_mask = pin_dm_irq_mask, + .irq_unmask = pin_dm_irq_unmask, + .irq_set_triger_mode = pin_dm_irq_set_triger_mode, + .irq_map = pin_dm_irq_map, + .irq_parse = pin_dm_irq_parse, +}; + +rt_err_t pin_pic_handle_isr(struct rt_device_pin *gpio, rt_base_t pin) +{ + rt_err_t err; + + if (gpio) + { + struct rt_pin_irqchip *irqchip = &gpio->irqchip; + + if (pin >= irqchip->pin_range[0] && pin <= irqchip->pin_range[1]) + { + struct rt_pic_irq *pirq; + + pirq = rt_pic_find_irq(&irqchip->parent, pin - irqchip->pin_range[0]); + + if (pirq->irq >= 0) + { + err = rt_pic_handle_isr(pirq); + } + else + { + err = -RT_EINVAL; + } + } + else + { + err = -RT_EINVAL; + } + } + else + { + err = -RT_EINVAL; + } + + return err; +} + +rt_err_t pin_pic_init(struct rt_device_pin *gpio) +{ + rt_err_t err; + + if (gpio) + { + struct rt_pin_irqchip *irqchip = &gpio->irqchip; + + if (irqchip->pin_range[0] >= 0 && irqchip->pin_range[1] >= irqchip->pin_range[0]) + { + struct rt_pic *pic = &irqchip->parent; + rt_size_t pin_nr = irqchip->pin_range[1] - irqchip->pin_range[0] + 1; + + pic->priv_data = gpio; + pic->ops = &pin_dm_ops; + /* Make sure the type of gpio for pic */ + gpio->parent.parent.type = RT_Object_Class_Device; + rt_pic_default_name(&irqchip->parent); + + err = rt_pic_linear_irq(pic, pin_nr); + rt_pic_user_extends(pic); + } + else + { + err = -RT_EINVAL; + } + } + else + { + err = -RT_EINVAL; + } + + return err; +} + +rt_ssize_t rt_pin_get_named_pin(struct rt_device *dev, const char *propname, int index, + rt_uint8_t *out_mode, rt_uint8_t *out_value) +{ + rt_ssize_t res = -RT_ENOSYS; + + RT_ASSERT(dev != RT_NULL); + +#ifdef RT_USING_OFW + if (dev->ofw_node) + { + res = rt_ofw_get_named_pin(dev->ofw_node, propname, index, out_mode, out_value); + } + else + { + res = -RT_EINVAL; + } +#endif /* RT_USING_OFW */ + + return res; +} + +rt_ssize_t rt_pin_get_named_pin_count(struct rt_device *dev, const char *propname) +{ + rt_ssize_t count = -RT_ENOSYS; + + RT_ASSERT(dev != RT_NULL); + +#ifdef RT_USING_OFW + if (dev->ofw_node) + { + count = rt_ofw_get_named_pin_count(dev->ofw_node, propname); + } + else + { + count = -RT_EINVAL; + } +#endif /* RT_USING_OFW */ + + return count; +} diff --git a/components/drivers/pin/pin_dm.h b/components/drivers/pin/pin_dm.h new file mode 100644 index 000000000000..b5146a2795c5 --- /dev/null +++ b/components/drivers/pin/pin_dm.h @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-11-26 GuEe-GUI first version + */ + +#ifndef __PIN_DM_H__ +#define __PIN_DM_H__ + +#include +#include +#include + +rt_err_t pin_pic_handle_isr(struct rt_device_pin *gpio, rt_base_t pin); +rt_err_t pin_pic_init(struct rt_device_pin *gpio); + +#endif /* __PIN_DM_H__ */ diff --git a/components/drivers/pin/pin_ofw.c b/components/drivers/pin/pin_ofw.c new file mode 100644 index 000000000000..e4c18b175975 --- /dev/null +++ b/components/drivers/pin/pin_ofw.c @@ -0,0 +1,170 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-11-26 GuEe-GUI first version + */ + +#include + +#include "pin_dm.h" + +static const char * const gpio_suffixes[] = +{ + "gpios", "gpio" +}; + +rt_ssize_t rt_ofw_get_named_pin(struct rt_ofw_node *np, const char *propname, int index, + rt_uint8_t *out_mode, rt_uint8_t *out_value) +{ + rt_ssize_t pin; + rt_uint8_t mode; + rt_uint8_t value; + rt_uint32_t flags; + char gpios_name[64]; + struct rt_device_pin *pin_dev; + struct rt_ofw_node *pin_dev_np; + struct rt_ofw_cell_args pin_args; + + if (!np && index < 0) + { + return -RT_EINVAL; + } + + for (int i = 0; i < RT_ARRAY_SIZE(gpio_suffixes); ++i) + { + if (propname) + { + rt_snprintf(gpios_name, sizeof(gpios_name), "%s-%s", propname, gpio_suffixes[i]); + } + else + { + rt_snprintf(gpios_name, sizeof(gpios_name), "%s", gpio_suffixes[i]); + } + + pin = rt_ofw_parse_phandle_cells(np, gpios_name, "#gpio-cells", index, &pin_args); + + if (pin >= 0) + { + break; + } + } + + if (pin < 0) + { + return pin; + } + + pin_dev_np = pin_args.data; + pin_dev = rt_ofw_data(pin_dev_np); + + if (!pin_dev) + { + pin = -RT_ERROR; + + goto _out_converts; + } + + value = PIN_LOW; + mode = PIN_MODE_OUTPUT; + + if (pin_dev->ops->pin_parse) + { + pin = pin_dev->ops->pin_parse(&pin_dev->parent, &pin_args, &flags); + } + else + { + /* + * We always assume that the args[0] is the pin number if driver not + * implemented `pin_parse`. + */ + pin = pin_args.args[0]; + + goto _out_converts; + } + + if (out_mode) + { + if (flags & PIN_OPEN_DRAIN) + { + mode = PIN_MODE_OUTPUT_OD; + } + + switch (flags & RT_GENMASK(6, 4)) + { + case PIN_PULL_UP: + mode = PIN_MODE_INPUT_PULLUP; + break; + + case PIN_PULL_DOWN: + mode = PIN_MODE_INPUT_PULLDOWN; + break; + + case PIN_PULL_DISABLE: + mode = PIN_MODE_INPUT; + break; + } + } + + if (out_value) + { + if (flags == (PIN_ACTIVE_HIGH | PIN_PUSH_PULL)) + { + value = PIN_HIGH; + } + else if (flags == (PIN_ACTIVE_LOW | PIN_PUSH_PULL)) + { + value = PIN_LOW; + } + } + +_out_converts: + rt_ofw_node_put(pin_dev_np); + + if (out_mode) + { + *out_mode = mode; + } + + if (out_value) + { + *out_value = value; + } + + return pin; +} + +rt_ssize_t rt_ofw_get_named_pin_count(struct rt_ofw_node *np, const char *propname) +{ + char gpios_name[64]; + rt_ssize_t count = 0; + + if (!np || !propname) + { + return -RT_EINVAL; + } + + for (int i = 0; i < RT_ARRAY_SIZE(gpio_suffixes); ++i) + { + if (propname) + { + rt_snprintf(gpios_name, sizeof(gpios_name), "%s-%s", propname, gpio_suffixes[i]); + } + else + { + rt_snprintf(gpios_name, sizeof(gpios_name), "%s", gpio_suffixes[i]); + } + + count = rt_ofw_count_phandle_cells(np, propname, "#gpio-cells"); + + if (count > 0) + { + break; + } + } + + return count; +} diff --git a/components/drivers/pinctrl/Kconfig b/components/drivers/pinctrl/Kconfig new file mode 100644 index 000000000000..823daefe6ac9 --- /dev/null +++ b/components/drivers/pinctrl/Kconfig @@ -0,0 +1,17 @@ +menuconfig RT_USING_PINCTRL + bool "Using Pin controllers device drivers" + depends on RT_USING_DM + depends on RT_USING_PIN + default n + +config RT_PINCTRL_ROCKCHIP + bool "Rockchip gpio and pinctrl driver" + depends on RT_USING_PINCTRL + select RT_MFD_SYSCON + default n + +config RT_PINCTRL_ROCKCHIP_RK8XX + bool "Pinctrl and GPIO driver for RK805/RK806 PMIC" + depends on RT_USING_PINCTRL + depends on RT_MFD_RK8XX + default n diff --git a/components/drivers/pinctrl/SConscript b/components/drivers/pinctrl/SConscript new file mode 100644 index 000000000000..df88779e4630 --- /dev/null +++ b/components/drivers/pinctrl/SConscript @@ -0,0 +1,29 @@ +from building import * + +group = [] +objs = [] + +if not GetDepend(['RT_USING_PINCTRL']): + Return('group') + +cwd = GetCurrentDir() +list = os.listdir(cwd) +CPPPATH = [cwd + '/../include'] + +src = ['pinctrl.c'] + +if GetDepend(['RT_PINCTRL_ROCKCHIP_RK8XX']): + src += ['pinctrl-rk8xx.c'] + +if GetDepend(['RT_PINCTRL_ROCKCHIP']): + src += ['pinctrl-rockchip.c'] + +group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) + +for d in list: + path = os.path.join(cwd, d) + if os.path.isfile(os.path.join(path, 'SConscript')): + objs = objs + SConscript(os.path.join(d, 'SConscript')) +objs = objs + group + +Return('objs') diff --git a/components/drivers/pinctrl/pinctrl-rk8xx.c b/components/drivers/pinctrl/pinctrl-rk8xx.c new file mode 100644 index 000000000000..e42e6dd2bf13 --- /dev/null +++ b/components/drivers/pinctrl/pinctrl-rk8xx.c @@ -0,0 +1,399 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-11-26 GuEe-GUI first version + */ + +#include +#include + +#define DBG_TAG "pinctrl.rk8xx" +#define DBG_LVL DBG_INFO +#include + +#include "../mfd/rk8xx.h" + +struct rk8xx_pin +{ + const char *name; +}; + +enum rk8xx_pin_mux +{ + RK805_PINMUX_GPIO = 0, + RK806_PINMUX_FUN0 = 0, + RK806_PINMUX_FUN1, + RK806_PINMUX_FUN2, + RK806_PINMUX_FUN3, + RK806_PINMUX_FUN4, + RK806_PINMUX_FUN5, +}; + +struct rk8xx_pin_function +{ + const char *name; + enum rk8xx_pin_mux mux; +}; + +struct rk8xx_pin_conf +{ + rt_uint8_t reg; + rt_uint8_t fun_reg; + rt_uint8_t fun_msk; + rt_uint8_t dir_msk; + rt_uint8_t val_msk; +}; + +struct rk8xx_pinctrl +{ + struct rt_device_pin parent; + + struct rk8xx *rk8xx; + + int pins_nr; + const struct rk8xx_pin *pins; + + int pin_func_nr; + const struct rk8xx_pin_function *pin_funcs; + + int pin_confs_nr; + const struct rk8xx_pin_conf *pin_confs; +}; + +#define raw_to_rk8xx_pinctrl(raw) rt_container_of(raw, struct rk8xx_pinctrl, parent) + +static void rk8xx_pinctrl_set_direction(struct rk8xx_pinctrl *rk8xx_pl, + rt_base_t pin, rt_bool_t input) +{ + rt_uint32_t val; + const struct rk8xx_pin_conf *pin_conf = &rk8xx_pl->pin_confs[pin]; + + if (!pin_conf->dir_msk) + { + /* Default output */ + return; + } + + val = rk8xx_read(rk8xx_pl->rk8xx, pin_conf->reg); + + if ((rt_err_t)val < 0) + { + LOG_E("Get PIN%d direction failed", pin); + } + + return; +} + +static void rk8xx_pinctrl_mode(struct rt_device *device, rt_base_t pin, rt_uint8_t mode) +{ + struct rk8xx_pinctrl *rk8xx_pl = raw_to_rk8xx_pinctrl(device); + + switch (mode) + { + case PIN_MODE_OUTPUT: + rk8xx_pinctrl_set_direction(rk8xx_pl, pin, RT_FALSE); + break; + + case PIN_MODE_INPUT: + if (rk8xx_pl->rk8xx->variant != RK805_ID) + { + rk8xx_pinctrl_set_direction(rk8xx_pl, pin, RT_TRUE); + } + break; + + default: + break; + } +} + +static void rk8xx_pinctrl_write(struct rt_device *device, rt_base_t pin, + rt_uint8_t value) +{ + rt_err_t err; + struct rk8xx_pinctrl *rk8xx_pl = raw_to_rk8xx_pinctrl(device); + const struct rk8xx_pin_conf *pin_conf = &rk8xx_pl->pin_confs[pin]; + + err = rk8xx_update_bits(rk8xx_pl->rk8xx, + pin_conf->reg, pin_conf->val_msk, value ? pin_conf->val_msk : 0); + + if (err) + { + LOG_E("Set PIN%d value %d failed", pin, value); + } +} + +static rt_int8_t rk8xx_pinctrl_read(struct rt_device *device, rt_base_t pin) +{ + int val; + struct rk8xx_pinctrl *rk8xx_pl = raw_to_rk8xx_pinctrl(device); + + val = rk8xx_read(rk8xx_pl->rk8xx, rk8xx_pl->pin_confs[pin].reg); + + if ((rt_err_t)val < 0) + { + LOG_E("Get PIN%d value failed", pin); + + return val; + } + + return !!(val & rk8xx_pl->pin_confs[pin].val_msk); +} + +static rt_ssize_t rk8xx_pin_parse(struct rt_device *device, + struct rt_ofw_cell_args *args, rt_uint32_t *flags) +{ + if (flags) + { + *flags = args->args[1]; + } + + return args->args[0]; +} + +static rt_err_t rk8xx_pinctrl_confs_apply(struct rt_device *device, void *fw_conf_np) +{ + int i; + rt_err_t err; + rt_base_t pin = 0; + enum rk8xx_pin_mux mux; + const char *pin_name, *function; + const struct rk8xx_pin_conf *pin_conf; + struct rt_ofw_node *conf_np = fw_conf_np; + struct rk8xx_pinctrl *rk8xx_pl = raw_to_rk8xx_pinctrl(device); + + if ((err = rt_ofw_prop_read_string(conf_np, "pins", &pin_name))) + { + LOG_E("%s: %s not found", rt_ofw_node_full_name(conf_np), "pins"); + return err; + } + + if ((err = rt_ofw_prop_read_string(conf_np, "function", &function))) + { + LOG_E("%s: %s not found", rt_ofw_node_full_name(conf_np), "function"); + return err; + } + + for (i = 0; i < rk8xx_pl->pins_nr; ++i) + { + if (!rt_strcmp(rk8xx_pl->pins[i].name, pin_name)) + { + break; + } + ++pin; + } + + if (i == rk8xx_pl->pins_nr) + { + return -RT_EINVAL; + } + + for (i = 0; i < rk8xx_pl->pin_func_nr; ++i) + { + if (!rt_strcmp(rk8xx_pl->pin_funcs[i].name, function)) + { + mux = rk8xx_pl->pin_funcs[i].mux; + break; + } + } + + if (i == rk8xx_pl->pin_func_nr) + { + return -RT_EINVAL; + } + + pin_conf = &rk8xx_pl->pin_confs[pin]; + + if (!pin_conf->fun_msk) + { + return RT_EOK; + } + + mux <<= __rt_ffs(pin_conf->fun_msk) - 1; + err = rk8xx_update_bits(rk8xx_pl->rk8xx, + pin_conf->fun_reg, pin_conf->fun_msk, mux); + + if (err) + { + LOG_E("Set PIN%d func%d failed", pin, mux); + } + + return err; +} + +static const struct rt_pin_ops rk8xx_pinctrl_ops = +{ + .pin_mode = rk8xx_pinctrl_mode, + .pin_write = rk8xx_pinctrl_write, + .pin_read = rk8xx_pinctrl_read, + .pin_parse = rk8xx_pin_parse, + .pin_ctrl_confs_apply = rk8xx_pinctrl_confs_apply, +}; + +static const struct rk8xx_pin rk805_pins[] = +{ + { .name = "gpio0", }, + { .name = "gpio1", }, +}; + +static const struct rk8xx_pin_function rk805_gpio_funcs[] = +{ + { .name = "gpio", .mux = RK805_PINMUX_GPIO }, +}; + +#define RK805_GPIO0_VAL_MSK RT_BIT(0) +#define RK805_GPIO1_VAL_MSK RT_BIT(1) + +static const struct rk8xx_pin_conf rk805_gpio_confs[] = +{ + { + .reg = RK805_OUT_REG, + .val_msk = RK805_GPIO0_VAL_MSK, + }, + { + .reg = RK805_OUT_REG, + .val_msk = RK805_GPIO1_VAL_MSK, + }, +}; + +static struct rk8xx_pin rk806_pins[] = +{ + { .name = "gpio_pwrctrl1", }, + { .name = "gpio_pwrctrl2", }, + { .name = "gpio_pwrctrl3", }, +}; + +static const struct rk8xx_pin_function rk806_gpio_funcs[] = +{ + { .name = "pin_fun0", .mux = RK806_PINMUX_FUN0 }, + { .name = "pin_fun1", .mux = RK806_PINMUX_FUN1 }, + { .name = "pin_fun2", .mux = RK806_PINMUX_FUN2 }, + { .name = "pin_fun3", .mux = RK806_PINMUX_FUN3 }, + { .name = "pin_fun4", .mux = RK806_PINMUX_FUN4 }, + { .name = "pin_fun5", .mux = RK806_PINMUX_FUN5 }, +}; + +#define RK806_PWRCTRL1_DR RT_BIT(0) +#define RK806_PWRCTRL2_DR RT_BIT(1) +#define RK806_PWRCTRL3_DR RT_BIT(2) +#define RK806_PWRCTRL1_DATA RT_BIT(4) +#define RK806_PWRCTRL2_DATA RT_BIT(5) +#define RK806_PWRCTRL3_DATA RT_BIT(6) +#define RK806_PWRCTRL1_FUN RT_GENMASK(2, 0) +#define RK806_PWRCTRL2_FUN RT_GENMASK(6, 4) +#define RK806_PWRCTRL3_FUN RT_GENMASK(2, 0) + +static const struct rk8xx_pin_conf rk806_gpio_confs[] = +{ + { + .fun_reg = RK806_SLEEP_CONFIG0, + .fun_msk = RK806_PWRCTRL1_FUN, + .reg = RK806_SLEEP_GPIO, + .val_msk = RK806_PWRCTRL1_DATA, + .dir_msk = RK806_PWRCTRL1_DR, + }, + { + .fun_reg = RK806_SLEEP_CONFIG0, + .fun_msk = RK806_PWRCTRL2_FUN, + .reg = RK806_SLEEP_GPIO, + .val_msk = RK806_PWRCTRL2_DATA, + .dir_msk = RK806_PWRCTRL2_DR, + }, + { + .fun_reg = RK806_SLEEP_CONFIG1, + .fun_msk = RK806_PWRCTRL3_FUN, + .reg = RK806_SLEEP_GPIO, + .val_msk = RK806_PWRCTRL3_DATA, + .dir_msk = RK806_PWRCTRL3_DR, + } +}; + +static rt_err_t rk8xx_pinctrl_probe(struct rt_platform_device *pdev) +{ + rt_err_t err; + char *compat; + int pins_nr, pin_func_nr, pin_confs_nr; + const struct rk8xx_pin *pins; + const struct rk8xx_pin_function *pin_funcs; + const struct rk8xx_pin_conf *pin_confs; + struct rk8xx *rk8xx = pdev->priv; + struct rt_ofw_node *np = pdev->parent.ofw_node; + struct rk8xx_pinctrl *rk8xx_pl = rt_calloc(1, sizeof(*rk8xx_pl)); + + if (!rk8xx_pl) + { + return -RT_ENOMEM; + } + + rk8xx_pl->rk8xx = rk8xx; + + switch (rk8xx->variant) + { + case RK805_ID: + compat = "rk805-pinctrl"; + pins_nr = RT_ARRAY_SIZE(rk805_pins); + pins = rk805_pins; + pin_func_nr = RT_ARRAY_SIZE(rk805_gpio_funcs); + pin_funcs = rk805_gpio_funcs; + pin_confs_nr = RT_ARRAY_SIZE(rk805_gpio_confs); + pin_confs = rk805_gpio_confs; + break; + + case RK806_ID: + compat = "rk806-pinctrl"; + pins_nr = RT_ARRAY_SIZE(rk806_pins); + pins = rk806_pins; + pin_func_nr = RT_ARRAY_SIZE(rk806_gpio_funcs); + pin_funcs = rk806_gpio_funcs; + pin_confs_nr = RT_ARRAY_SIZE(rk806_gpio_confs); + pin_confs = rk806_gpio_confs; + break; + + default: + err = -RT_EINVAL; + goto _fail; + } + + rk8xx_pl->parent.ops = &rk8xx_pinctrl_ops; + rk8xx_pl->pins_nr = pins_nr; + rk8xx_pl->pins = pins; + rk8xx_pl->pin_func_nr = pin_func_nr; + rk8xx_pl->pin_funcs = pin_funcs; + rk8xx_pl->pin_confs_nr = pin_confs_nr; + rk8xx_pl->pin_confs = pin_confs; + + if (!rt_ofw_prop_read_bool(np, "compatible")) + { + /* make pinctrl can find that */ + if ((err = rt_ofw_append_prop(np, "compatible", rt_strlen(compat) + 1, compat))) + { + goto _fail; + } + } + + rt_ofw_data(np) = &rk8xx_pl->parent; + + return RT_EOK; + +_fail: + rt_free(rk8xx_pl); + + return err; +} + +static struct rt_platform_driver rk8xx_pinctrl_driver = +{ + .name = "rk8xx-pinctrl", + .probe = rk8xx_pinctrl_probe, +}; + +static int rk8xx_pinctrl_register(void) +{ + rt_platform_driver_register(&rk8xx_pinctrl_driver); + + return 0; +} +INIT_PLATFORM_EXPORT(rk8xx_pinctrl_register); diff --git a/components/drivers/pinctrl/pinctrl-rockchip.c b/components/drivers/pinctrl/pinctrl-rockchip.c new file mode 100644 index 000000000000..a3355cf0a586 --- /dev/null +++ b/components/drivers/pinctrl/pinctrl-rockchip.c @@ -0,0 +1,1272 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-3-08 GuEe-GUI the first version + */ + +#include +#include + +#define DBG_TAG "pinctrl.rockchip" +#define DBG_LVL DBG_INFO +#include + +#include "pinctrl-rockchip.h" + +/* + * Encode variants of iomux registers into a type variable + */ +#define IOMUX_GPIO_ONLY RT_BIT(0) +#define IOMUX_WIDTH_4BIT RT_BIT(1) +#define IOMUX_SOURCE_PMU RT_BIT(2) +#define IOMUX_UNROUTED RT_BIT(3) +#define IOMUX_WIDTH_3BIT RT_BIT(4) +#define IOMUX_WIDTH_2BIT RT_BIT(5) +#define IOMUX_L_SOURCE_PMU RT_BIT(6) + +#define PIN_BANK_IOMUX_FLAGS(ID, PINS, \ + LABEL, IOM0, IOM1, IOM2, IOM3) \ +{ \ + .bank_num = ID, \ + .nr_pins = PINS, \ + .name = LABEL, \ + .iomux = \ + { \ + { .type = IOM0, .offset = -1 }, \ + { .type = IOM1, .offset = -1 }, \ + { .type = IOM2, .offset = -1 }, \ + { .type = IOM3, .offset = -1 }, \ + }, \ +} + +#define PIN_BANK_MUX_ROUTE_FLAGS( \ + ID, PIN, FUNC, REG, VAL, FLAG) \ +{ \ + .bank_num = ID, \ + .pin = PIN, \ + .func = FUNC, \ + .route_offset = REG, \ + .route_val = VAL, \ + .route_location = FLAG, \ +} + +#define PIN_BANK_IOMUX_FLAGS_PULL_FLAGS(ID, \ + PINS, LABEL, IOM0, IOM1, IOM2, IOM3, \ + PULL0, PULL1, PULL2, PULL3) \ +{ \ + .bank_num = ID, \ + .nr_pins = PINS, \ + .name = LABEL, \ + .iomux = \ + { \ + { .type = IOM0, .offset = -1 }, \ + { .type = IOM1, .offset = -1 }, \ + { .type = IOM2, .offset = -1 }, \ + { .type = IOM3, .offset = -1 }, \ + }, \ + .pull_type[0] = PULL0, \ + .pull_type[1] = PULL1, \ + .pull_type[2] = PULL2, \ + .pull_type[3] = PULL3, \ +} + +#define RK_MUXROUTE_SAME(ID, PIN, FUNC, REG, VAL) \ + PIN_BANK_MUX_ROUTE_FLAGS(ID, PIN, FUNC, REG, VAL, ROCKCHIP_ROUTE_SAME) + +#define RK_MUXROUTE_GRF(ID, PIN, FUNC, REG, VAL) \ + PIN_BANK_MUX_ROUTE_FLAGS(ID, PIN, FUNC, REG, VAL, ROCKCHIP_ROUTE_GRF) + +#define RK_MUXROUTE_PMU(ID, PIN, FUNC, REG, VAL) \ + PIN_BANK_MUX_ROUTE_FLAGS(ID, PIN, FUNC, REG, VAL, ROCKCHIP_ROUTE_PMU) + +#define PIN_BANK_FLAGS_IOMUX_PULL(ID, PIN, LABEL, M, P) \ + PIN_BANK_IOMUX_FLAGS_PULL_FLAGS(ID, PIN, LABEL, M, M, M, M, P, P, P, P) + +#define RK_RECALCED_DATA( \ + NUM, PIN, REG, BIT, MASK) \ +{ \ + .num = NUM, \ + .pin = PIN, \ + .reg = REG, \ + .bit = BIT, \ + .mask = MASK, \ +} + +static int rockchip_pull_list[PULL_TYPE_MAX][4] = +{ + { + PIN_CONFIG_BIAS_DISABLE, + PIN_CONFIG_BIAS_PULL_UP, + PIN_CONFIG_BIAS_PULL_DOWN, + PIN_CONFIG_BIAS_BUS_HOLD + }, + { + PIN_CONFIG_BIAS_DISABLE, + PIN_CONFIG_BIAS_PULL_DOWN, + PIN_CONFIG_BIAS_DISABLE, + PIN_CONFIG_BIAS_PULL_UP + }, +}; + +static int rockchip_translate_pull_value(int type, int pull) +{ + int res = -RT_EINVAL; + + for (int i = 0; i < RT_ARRAY_SIZE(rockchip_pull_list[type]); ++i) + { + if (rockchip_pull_list[type][i] == pull) + { + res = i; + break; + } + } + + return res; +} + +static void rockchip_translate_recalced_mux(struct rockchip_pin_bank *pin_bank, + int pin, int *reg, rt_uint8_t *bit, int *mask) +{ + int i; + struct rockchip_pin_data *drvdata = pin_bank->drvdata; + struct rockchip_pin_ctrl *pinctrl = drvdata->pinctrl; + struct rockchip_mux_recalced_data *data; + + for (i = 0; i < pinctrl->niomux_recalced; ++i) + { + data = &pinctrl->iomux_recalced[i]; + + if (data->num == pin_bank->bank_num && data->pin == pin) + { + break; + } + } + + if (i >= pinctrl->niomux_recalced) + { + return; + } + + *reg = data->reg; + *mask = data->mask; + *bit = data->bit; +} + +static struct rockchip_mux_route_data rk3308_mux_route_data[] = +{ + RK_MUXROUTE_SAME(0, RK_PC3, 1, 0x314, RT_BIT(16 + 0) | RT_BIT(0)), /* rtc_clk */ + RK_MUXROUTE_SAME(1, RK_PC6, 2, 0x314, RT_BIT(16 + 2) | RT_BIT(16 + 3)), /* uart2_rxm0 */ + RK_MUXROUTE_SAME(4, RK_PD2, 2, 0x314, RT_BIT(16 + 2) | RT_BIT(16 + 3) | RT_BIT(2)), /* uart2_rxm1 */ + RK_MUXROUTE_SAME(0, RK_PB7, 2, 0x608, RT_BIT(16 + 8) | RT_BIT(16 + 9)), /* i2c3_sdam0 */ + RK_MUXROUTE_SAME(3, RK_PB4, 2, 0x608, RT_BIT(16 + 8) | RT_BIT(16 + 9) | RT_BIT(8)), /* i2c3_sdam1 */ + RK_MUXROUTE_SAME(2, RK_PA0, 3, 0x608, RT_BIT(16 + 8) | RT_BIT(16 + 9) | RT_BIT(9)), /* i2c3_sdam2 */ + RK_MUXROUTE_SAME(1, RK_PA3, 2, 0x308, RT_BIT(16 + 3)), /* i2s-8ch-1-sclktxm0 */ + RK_MUXROUTE_SAME(1, RK_PA4, 2, 0x308, RT_BIT(16 + 3)), /* i2s-8ch-1-sclkrxm0 */ + RK_MUXROUTE_SAME(1, RK_PB5, 2, 0x308, RT_BIT(16 + 3) | RT_BIT(3)), /* i2s-8ch-1-sclktxm1 */ + RK_MUXROUTE_SAME(1, RK_PB6, 2, 0x308, RT_BIT(16 + 3) | RT_BIT(3)), /* i2s-8ch-1-sclkrxm1 */ + RK_MUXROUTE_SAME(1, RK_PA4, 3, 0x308, RT_BIT(16 + 12) | RT_BIT(16 + 13)), /* pdm-clkm0 */ + RK_MUXROUTE_SAME(1, RK_PB6, 4, 0x308, RT_BIT(16 + 12) | RT_BIT(16 + 13) | RT_BIT(12)), /* pdm-clkm1 */ + RK_MUXROUTE_SAME(2, RK_PA6, 2, 0x308, RT_BIT(16 + 12) | RT_BIT(16 + 13) | RT_BIT(13)), /* pdm-clkm2 */ + RK_MUXROUTE_SAME(2, RK_PA4, 3, 0x600, RT_BIT(16 + 2) | RT_BIT(2)), /* pdm-clkm-m2 */ + RK_MUXROUTE_SAME(3, RK_PB2, 3, 0x314, RT_BIT(16 + 9)), /* spi1_miso */ + RK_MUXROUTE_SAME(2, RK_PA4, 2, 0x314, RT_BIT(16 + 9) | RT_BIT(9)), /* spi1_miso_m1 */ + RK_MUXROUTE_SAME(0, RK_PB3, 3, 0x314, RT_BIT(16 + 10) | RT_BIT(16 + 11)), /* owire_m0 */ + RK_MUXROUTE_SAME(1, RK_PC6, 7, 0x314, RT_BIT(16 + 10) | RT_BIT(16 + 11) | RT_BIT(10)), /* owire_m1 */ + RK_MUXROUTE_SAME(2, RK_PA2, 5, 0x314, RT_BIT(16 + 10) | RT_BIT(16 + 11) | RT_BIT(11)), /* owire_m2 */ + RK_MUXROUTE_SAME(0, RK_PB3, 2, 0x314, RT_BIT(16 + 12) | RT_BIT(16 + 13)), /* can_rxd_m0 */ + RK_MUXROUTE_SAME(1, RK_PC6, 5, 0x314, RT_BIT(16 + 12) | RT_BIT(16 + 13) | RT_BIT(12)), /* can_rxd_m1 */ + RK_MUXROUTE_SAME(2, RK_PA2, 4, 0x314, RT_BIT(16 + 12) | RT_BIT(16 + 13) | RT_BIT(13)), /* can_rxd_m2 */ + RK_MUXROUTE_SAME(1, RK_PC4, 3, 0x314, RT_BIT(16 + 14)), /* mac_rxd0_m0 */ + RK_MUXROUTE_SAME(4, RK_PA2, 2, 0x314, RT_BIT(16 + 14) | RT_BIT(14)), /* mac_rxd0_m1 */ + RK_MUXROUTE_SAME(3, RK_PB4, 4, 0x314, RT_BIT(16 + 15)), /* uart3_rx */ + RK_MUXROUTE_SAME(0, RK_PC1, 3, 0x314, RT_BIT(16 + 15) | RT_BIT(15)), /* uart3_rx_m1 */ +}; + +static struct rockchip_mux_recalced_data rk3308_mux_recalced_data[] = +{ + RK_RECALCED_DATA(1, 14, 0x28, 12, 0xf), /* gpio1b6_sel */ + RK_RECALCED_DATA(1, 15, 0x2c, 0, 0x3), /* gpio1b7_sel */ + RK_RECALCED_DATA(1, 18, 0x30, 4, 0xf), /* gpio1c2_sel */ + RK_RECALCED_DATA(1, 19, 0x30, 8, 0xf), /* gpio1c3_sel */ + RK_RECALCED_DATA(1, 20, 0x30, 12, 0xf), /* gpio1c4_sel */ + RK_RECALCED_DATA(1, 21, 0x34, 0, 0xf), /* gpio1c5_sel */ + RK_RECALCED_DATA(1, 22, 0x34, 4, 0xf), /* gpio1c6_sel */ + RK_RECALCED_DATA(1, 23, 0x34, 8, 0xf), /* gpio1c7_sel */ + RK_RECALCED_DATA(2, 2, 0x40, 4, 0x3), /* gpio2a2_sel */ + RK_RECALCED_DATA(2, 3, 0x40, 6, 0x3), /* gpio2a3_sel */ + RK_RECALCED_DATA(2, 16, 0x50, 0, 0x3), /* gpio2c0_sel */ + RK_RECALCED_DATA(3, 10, 0x68, 4, 0x3), /* gpio3b2_sel */ + RK_RECALCED_DATA(3, 11, 0x68, 6, 0x3), /* gpio3b3_sel */ + RK_RECALCED_DATA(3, 12, 0x68, 8, 0xf), /* gpio3b4_sel */ + RK_RECALCED_DATA(3, 13, 0x68, 12, 0xf), /* gpio3b5_sel */ +}; + +static rt_err_t rk3308_set_mux(struct rockchip_pin_bank *pin_bank, int pin, int mux) +{ + rt_uint8_t bit; + rt_uint32_t data; + int iomux_num = (pin / 8), reg, mask; + struct rt_syscon *regmap; + struct rockchip_pin_data *drvdata = pin_bank->drvdata; + + if ((pin_bank->iomux[iomux_num].type & IOMUX_SOURCE_PMU)) + { + regmap = drvdata->regmap_pmu; + } + else + { + regmap = drvdata->regmap_base; + } + + reg = pin_bank->iomux[iomux_num].offset; + if (pin_bank->iomux[iomux_num].type & IOMUX_WIDTH_4BIT) + { + if ((pin % 8) >= 4) + { + reg += 0x4; + } + bit = (pin % 4) * 4; + mask = 0xf; + } + else + { + bit = (pin % 8) * 2; + mask = 0x3; + } + + if (pin_bank->recalced_mask & RT_BIT(pin)) + { + rockchip_translate_recalced_mux(pin_bank, pin, ®, &bit, &mask); + } + + data = (mask << (bit + 16)); + data |= (mux & mask) << bit; + + return rt_syscon_write(regmap, reg, data); +} + +#define RK3308_PULL_OFFSET 0xa0 +#define RK3308_PULL_BITS_PER_PIN 2 +#define RK3308_PULL_PINS_PER_REG 8 +#define RK3308_PULL_BANK_STRIDE 16 + +static rt_err_t rk3308_set_pull(struct rockchip_pin_bank *pin_bank, int pin, int pull) +{ + int reg, pull_value; + rt_uint32_t data; + rt_uint8_t bit, type; + struct rt_syscon *regmap; + struct rockchip_pin_data *drvdata = pin_bank->drvdata; + + if (pull == PIN_CONFIG_BIAS_PULL_PIN_DEFAULT) + { + return -RT_ENOSYS; + } + + regmap = drvdata->regmap_base; + reg = RK3308_PULL_OFFSET; + reg += pin_bank->bank_num * RK3308_PULL_BANK_STRIDE; + reg += ((pin / RK3308_PULL_PINS_PER_REG) * 4); + + bit = (pin % RK3308_PULL_PINS_PER_REG); + bit *= RK3308_PULL_BITS_PER_PIN; + + type = pin_bank->pull_type[pin / 8]; + pull_value = rockchip_translate_pull_value(type, pull); + + if (pull_value < 0) + { + LOG_E("Not supported pull = %d, fixup the code or firmware", pull); + + return pull_value; + } + + /* enable the write to the equivalent lower bits */ + data = ((1 << ROCKCHIP_PULL_BITS_PER_PIN) - 1) << (bit + 16); + data |= (pull_value << bit); + + return rt_syscon_write(regmap, reg, data); +} + +#define RK3308_DRV_GRF_OFFSET 0x100 +#define RK3308_DRV_BITS_PER_PIN 2 +#define RK3308_DRV_PINS_PER_REG 8 +#define RK3308_DRV_BANK_STRIDE 16 + +static rt_err_t rk3308_set_drive(struct rockchip_pin_bank *pin_bank, int pin, int strength) +{ + int reg; + rt_uint8_t bit; + rt_uint32_t data; + struct rt_syscon *regmap; + struct rockchip_pin_data *drvdata = pin_bank->drvdata; + + regmap = drvdata->regmap_base; + reg = RK3308_DRV_GRF_OFFSET; + reg += pin_bank->bank_num * RK3308_DRV_BANK_STRIDE; + reg += ((pin / RK3308_DRV_PINS_PER_REG) * 4); + + bit = (pin % RK3308_DRV_PINS_PER_REG); + bit *= RK3308_DRV_BITS_PER_PIN; + + /* enable the write to the equivalent lower bits */ + data = ((1 << ROCKCHIP_DRV_BITS_PER_PIN) - 1) << (bit + 16); + data |= (strength << bit); + + return rt_syscon_write(regmap, reg, data); +} + +#define RK3308_SCHMITT_PINS_PER_REG 8 +#define RK3308_SCHMITT_BANK_STRIDE 16 +#define RK3308_SCHMITT_GRF_OFFSET 0x1a0 + +static rt_err_t rk3308_set_schmitt(struct rockchip_pin_bank *pin_bank, int pin, int enable) +{ + int reg; + rt_uint8_t bit; + rt_uint32_t data; + struct rt_syscon *regmap; + struct rockchip_pin_data *drvdata = pin_bank->drvdata; + + regmap = drvdata->regmap_base; + reg = RK3308_SCHMITT_GRF_OFFSET; + + reg += pin_bank->bank_num * RK3308_SCHMITT_BANK_STRIDE; + reg += ((pin / RK3308_SCHMITT_PINS_PER_REG) * 4); + bit = pin % RK3308_SCHMITT_PINS_PER_REG; + + /* enable the write to the equivalent lower bits */ + data = RT_BIT(bit + 16) | (enable << bit); + + return rt_syscon_write(regmap, reg, data); +} + +static struct rockchip_pin_bank rk3308_pin_banks[] = +{ + PIN_BANK_IOMUX_FLAGS(0, 32, "gpio0", IOMUX_WIDTH_2BIT, IOMUX_WIDTH_2BIT, IOMUX_WIDTH_2BIT, IOMUX_WIDTH_2BIT), + PIN_BANK_IOMUX_FLAGS(1, 32, "gpio1", IOMUX_WIDTH_2BIT, IOMUX_WIDTH_2BIT, IOMUX_WIDTH_2BIT, IOMUX_WIDTH_2BIT), + PIN_BANK_IOMUX_FLAGS(2, 32, "gpio2", IOMUX_WIDTH_2BIT, IOMUX_WIDTH_2BIT, IOMUX_WIDTH_2BIT, IOMUX_WIDTH_2BIT), + PIN_BANK_IOMUX_FLAGS(3, 32, "gpio3", IOMUX_WIDTH_2BIT, IOMUX_WIDTH_2BIT, IOMUX_WIDTH_2BIT, IOMUX_WIDTH_2BIT), + PIN_BANK_IOMUX_FLAGS(4, 32, "gpio4", IOMUX_WIDTH_2BIT, IOMUX_WIDTH_2BIT, IOMUX_WIDTH_2BIT, IOMUX_WIDTH_2BIT), +}; + +static struct rockchip_pin_ctrl rk3308_pin_ctrl = +{ + .pin_banks = rk3308_pin_banks, + .banks_nr = RT_ARRAY_SIZE(rk3308_pin_banks), + .label = "RK3308-GPIO", + .type = RK3308, + .grf_mux_offset = 0x0, + .grf_drv_offset = RK3308_DRV_GRF_OFFSET, + .iomux_recalced = rk3308_mux_recalced_data, + .niomux_recalced = RT_ARRAY_SIZE(rk3308_mux_recalced_data), + .iomux_routes = rk3308_mux_route_data, + .niomux_routes = RT_ARRAY_SIZE(rk3308_mux_route_data), + .set_mux = rk3308_set_mux, + .set_pull = rk3308_set_pull, + .set_drive = rk3308_set_drive, + .set_schmitt = rk3308_set_schmitt, +}; + +static rt_err_t rk3568_set_mux(struct rockchip_pin_bank *pin_bank, int pin, int mux) +{ + rt_uint8_t bit; + rt_uint32_t data; + int iomux_num = (pin / 8), reg, mask; + struct rt_syscon *regmap; + struct rockchip_pin_data *drvdata = pin_bank->drvdata; + + if ((pin_bank->iomux[iomux_num].type & IOMUX_SOURCE_PMU)) + { + regmap = drvdata->regmap_pmu; + } + else + { + regmap = drvdata->regmap_base; + } + + reg = pin_bank->iomux[iomux_num].offset; + if ((pin % 8) >= 4) + { + reg += 0x4; + } + bit = (pin % 4) * 4; + mask = 0xf; + + data = (mask << (bit + 16)); + data |= (mux & mask) << bit; + + return rt_syscon_write(regmap, reg, data); +} + +#define RK3568_PULL_PMU_OFFSET 0x20 +#define RK3568_PULL_GRF_OFFSET 0x80 +#define RK3568_PULL_BITS_PER_PIN 2 +#define RK3568_PULL_PINS_PER_REG 8 +#define RK3568_PULL_BANK_STRIDE 0x10 + +static rt_err_t rk3568_set_pull(struct rockchip_pin_bank *pin_bank, int pin, int pull) +{ + int reg, pull_value; + rt_uint32_t data; + rt_uint8_t bit, type; + struct rt_syscon *regmap; + struct rockchip_pin_data *drvdata = pin_bank->drvdata; + + if (pull == PIN_CONFIG_BIAS_PULL_PIN_DEFAULT) + { + return -RT_ENOSYS; + } + + if (pin_bank->bank_num == 0) + { + regmap = drvdata->regmap_pmu; + reg = RK3568_PULL_PMU_OFFSET; + reg += pin_bank->bank_num * RK3568_PULL_BANK_STRIDE; + } + else + { + regmap = drvdata->regmap_base; + reg = RK3568_PULL_GRF_OFFSET; + reg += (pin_bank->bank_num - 1) * RK3568_PULL_BANK_STRIDE; + } + + reg += ((pin / RK3568_PULL_PINS_PER_REG) * 4); + bit = (pin % RK3568_PULL_PINS_PER_REG); + bit *= RK3568_PULL_BITS_PER_PIN; + + type = pin_bank->pull_type[pin / 8]; + pull_value = rockchip_translate_pull_value(type, pull); + + /* + * pull-up being 1 for everything except the GPIO0_D3-D6, + * where that pull up value becomes 3 + */ + if (pin_bank->bank_num == 0 && pin >= RK_GPIO0_D3 && pin <= RK_GPIO0_D6) + { + if (pull_value == 1) + { + pull_value = 3; + } + } + + if (pull_value < 0) + { + LOG_E("Not supported pull = %d, fixup the code or firmware", pull); + + return pull_value; + } + + /* enable the write to the equivalent lower bits */ + data = ((1 << ROCKCHIP_PULL_BITS_PER_PIN) - 1) << (bit + 16); + data |= (pull_value << bit); + + return rt_syscon_write(regmap, reg, data); +} + +#define RK3568_DRV_PMU_OFFSET 0x70 +#define RK3568_DRV_GRF_OFFSET 0x200 +#define RK3568_DRV_BITS_PER_PIN 8 +#define RK3568_DRV_PINS_PER_REG 2 +#define RK3568_DRV_BANK_STRIDE 0x40 + +#define RK3568_GRF_GPIO1C5_DS 0x840 +#define RK3568_GRF_GPIO2A2_DS 0x844 +#define RK3568_GRF_GPIO2B0_DS 0x848 +#define RK3568_GRF_GPIO3A0_DS 0x84c +#define RK3568_GRF_GPIO3A6_DS 0x850 +#define RK3568_GRF_GPIO4A0_DS 0x854 + +static rt_err_t rk3568_set_drive(struct rockchip_pin_bank *pin_bank, int pin, int strength) +{ + rt_err_t err; + rt_uint8_t bit; + rt_uint32_t data; + int reg, drv = (1 << (strength + 1)) - 1; + struct rt_syscon *regmap; + struct rockchip_pin_data *drvdata = pin_bank->drvdata; + + if (pin_bank->bank_num == 0) + { + regmap = drvdata->regmap_pmu; + reg = RK3568_DRV_PMU_OFFSET; + } + else + { + regmap = drvdata->regmap_base; + reg = RK3568_DRV_GRF_OFFSET; + reg += (pin_bank->bank_num - 1) * RK3568_DRV_BANK_STRIDE; + } + + reg += ((pin / RK3568_DRV_PINS_PER_REG) * 4); + bit = (pin % RK3568_DRV_PINS_PER_REG); + bit *= RK3568_DRV_BITS_PER_PIN; + + /* enable the write to the equivalent lower bits */ + data = ((1 << RK3568_DRV_BITS_PER_PIN) - 1) << (bit + 16); + data |= (drv << bit); + + if ((err = rt_syscon_write(regmap, reg, data))) + { + return err; + } + + if (pin_bank->bank_num == RK_GPIO1 && pin == RK_PC5) + { + reg = RK3568_GRF_GPIO1C5_DS; + } + else if (pin_bank->bank_num == RK_GPIO2 && pin == RK_PA2) + { + reg = RK3568_GRF_GPIO2A2_DS; + } + else if (pin_bank->bank_num == RK_GPIO2 && pin == RK_PB0) + { + reg = RK3568_GRF_GPIO2B0_DS; + } + else if (pin_bank->bank_num == RK_GPIO3 && pin == RK_PA0) + { + reg = RK3568_GRF_GPIO3A0_DS; + } + else if (pin_bank->bank_num == RK_GPIO3 && pin == RK_PA6) + { + reg = RK3568_GRF_GPIO3A6_DS; + } + else if (pin_bank->bank_num == RK_GPIO4 && pin == RK_PA0) + { + reg = RK3568_GRF_GPIO4A0_DS; + } + else + { + return RT_EOK; + } + + data = ((1 << RK3568_DRV_BITS_PER_PIN) - 1) << 16; + data |= drv; + + return rt_syscon_write(regmap, reg, data); +} + +#define RK3568_SCHMITT_BITS_PER_PIN 2 +#define RK3568_SCHMITT_PINS_PER_REG 8 +#define RK3568_SCHMITT_BANK_STRIDE 0x10 +#define RK3568_SCHMITT_GRF_OFFSET 0xc0 +#define RK3568_SCHMITT_PMUGRF_OFFSET 0x30 + +static rt_err_t rk3568_set_schmitt(struct rockchip_pin_bank *pin_bank, int pin, int enable) +{ + int reg; + rt_uint8_t bit; + rt_uint32_t data; + struct rt_syscon *regmap; + struct rockchip_pin_data *drvdata = pin_bank->drvdata; + + if (pin_bank->bank_num == 0) + { + regmap = drvdata->regmap_pmu; + reg = RK3568_SCHMITT_PMUGRF_OFFSET; + } + else + { + regmap = drvdata->regmap_base; + reg = RK3568_SCHMITT_GRF_OFFSET; + reg += (pin_bank->bank_num - 1) * RK3568_SCHMITT_BANK_STRIDE; + } + + reg += ((pin / RK3568_SCHMITT_PINS_PER_REG) * 4); + bit = pin % RK3568_SCHMITT_PINS_PER_REG; + bit *= RK3568_SCHMITT_BITS_PER_PIN; + + /* enable the write to the equivalent lower bits */ + data = ((1 << RK3568_SCHMITT_BITS_PER_PIN) - 1) << (bit + 16); + data |= (enable << bit); + + return rt_syscon_write(regmap, reg, data); +} + +static struct rockchip_pin_bank rk3568_pin_banks[] = +{ + PIN_BANK_IOMUX_FLAGS(0, 32, "gpio0", IOMUX_SOURCE_PMU | IOMUX_WIDTH_4BIT, IOMUX_SOURCE_PMU | IOMUX_WIDTH_4BIT, + IOMUX_SOURCE_PMU | IOMUX_WIDTH_4BIT, IOMUX_SOURCE_PMU | IOMUX_WIDTH_4BIT), + PIN_BANK_IOMUX_FLAGS(1, 32, "gpio1", IOMUX_WIDTH_4BIT, IOMUX_WIDTH_4BIT, IOMUX_WIDTH_4BIT, IOMUX_WIDTH_4BIT), + PIN_BANK_IOMUX_FLAGS(2, 32, "gpio2", IOMUX_WIDTH_4BIT, IOMUX_WIDTH_4BIT, IOMUX_WIDTH_4BIT, IOMUX_WIDTH_4BIT), + PIN_BANK_IOMUX_FLAGS(3, 32, "gpio3", IOMUX_WIDTH_4BIT, IOMUX_WIDTH_4BIT, IOMUX_WIDTH_4BIT, IOMUX_WIDTH_4BIT), + PIN_BANK_IOMUX_FLAGS(4, 32, "gpio4", IOMUX_WIDTH_4BIT, IOMUX_WIDTH_4BIT, IOMUX_WIDTH_4BIT, IOMUX_WIDTH_4BIT), +}; + +static struct rockchip_pin_ctrl rk3568_pin_ctrl = +{ + .pin_banks = rk3568_pin_banks, + .banks_nr = RT_ARRAY_SIZE(rk3568_pin_banks), + .label = "RK3568-GPIO", + .type = RK3568, + .grf_mux_offset = 0x0, + .pmu_mux_offset = 0x0, + .grf_drv_offset = RK3568_DRV_GRF_OFFSET, + .pmu_drv_offset = RK3568_DRV_PMU_OFFSET, + .set_mux = rk3568_set_mux, + .set_pull = rk3568_set_pull, + .set_drive = rk3568_set_drive, + .set_schmitt = rk3568_set_schmitt, +}; + +static rt_err_t rk3588_set_mux(struct rockchip_pin_bank *pin_bank, int pin, int mux) +{ + rt_err_t err; + rt_uint8_t bit; + rt_uint32_t data; + int iomux_num = (pin / 8), reg, mask; + struct rt_syscon *regmap; + struct rockchip_pin_data *drvdata = pin_bank->drvdata; + + regmap = drvdata->regmap_base; + reg = pin_bank->iomux[iomux_num].offset; + if (pin % 8 >= 4) + { + reg += 0x4; + } + bit = (pin % 4) * 4; + mask = 0xf; + + if (pin_bank->bank_num == 0) + { + if (pin >= RK_PB4 && pin <= RK_PD7) + { + rt_uint32_t reg0 = 0; + + if (mux < 8) + { + /* PMU2_IOC_BASE */ + reg0 = reg + 0x4000 - 0xc; + data = mask << (bit + 16); + data |= (mux & mask) << bit; + err = rt_syscon_write(regmap, reg0, data); + + /* BUS_IOC_BASE */ + reg0 = reg + 0x8000; + data = (mask << (bit + 16)); + rt_syscon_write(regmap, reg0, data); + } + else + { + /* PMU2_IOC_BASE */ + reg0 = reg + 0x4000 - 0xc; + data = mask << (bit + 16); + data |= 8 << bit; + err = rt_syscon_write(regmap, reg0, data); + + /* BUS_IOC_BASE */ + reg0 = reg + 0x8000; + data = mask << (bit + 16); + data |= mux << bit; + rt_syscon_write(regmap, reg0, data); + } + } + else + { + data = mask << (bit + 16); + data |= (mux & mask) << bit; + err = rt_syscon_write(regmap, reg, data); + } + + return err; + } + else if (pin_bank->bank_num > 0) + { + /* BUS_IOC_BASE */ + reg += 0x8000; + } + + data = mask << (bit + 16); + data |= (mux & mask) << bit; + + return rt_syscon_write(regmap, reg, data); +} + +#define RK3588_PMU1_IOC_REG 0x0000 +#define RK3588_PMU2_IOC_REG 0x4000 +#define RK3588_BUS_IOC_REG 0x8000 +#define RK3588_VCCIO1_4_IOC_REG 0x9000 +#define RK3588_VCCIO3_5_IOC_REG 0xa000 +#define RK3588_VCCIO2_IOC_REG 0xb000 +#define RK3588_VCCIO6_IOC_REG 0xc000 +#define RK3588_EMMC_IOC_REG 0xd000 + +#define RK3588_PULL_BITS_PER_PIN 2 +#define RK3588_PULL_PINS_PER_REG 8 + +static const rt_uint32_t rk3588_pull_regs[][2] = +{ + { RK_GPIO0_A0, RK3588_PMU1_IOC_REG + 0x0020 }, + { RK_GPIO0_B0, RK3588_PMU1_IOC_REG + 0x0024 }, + { RK_GPIO0_B5, RK3588_PMU2_IOC_REG + 0x0028 }, + { RK_GPIO0_C0, RK3588_PMU2_IOC_REG + 0x002c }, + { RK_GPIO0_D0, RK3588_PMU2_IOC_REG + 0x0030 }, + { RK_GPIO1_A0, RK3588_VCCIO1_4_IOC_REG + 0x0110 }, + { RK_GPIO1_B0, RK3588_VCCIO1_4_IOC_REG + 0x0114 }, + { RK_GPIO1_C0, RK3588_VCCIO1_4_IOC_REG + 0x0118 }, + { RK_GPIO1_D0, RK3588_VCCIO1_4_IOC_REG + 0x011c }, + { RK_GPIO2_A0, RK3588_EMMC_IOC_REG + 0x0120 }, + { RK_GPIO2_A6, RK3588_VCCIO3_5_IOC_REG + 0x0120 }, + { RK_GPIO2_B0, RK3588_VCCIO3_5_IOC_REG + 0x0124 }, + { RK_GPIO2_C0, RK3588_VCCIO3_5_IOC_REG + 0x0128 }, + { RK_GPIO2_D0, RK3588_EMMC_IOC_REG + 0x012c }, + { RK_GPIO3_A0, RK3588_VCCIO3_5_IOC_REG + 0x0130 }, + { RK_GPIO3_B0, RK3588_VCCIO3_5_IOC_REG + 0x0134 }, + { RK_GPIO3_C0, RK3588_VCCIO3_5_IOC_REG + 0x0138 }, + { RK_GPIO3_D0, RK3588_VCCIO3_5_IOC_REG + 0x013c }, + { RK_GPIO4_A0, RK3588_VCCIO6_IOC_REG + 0x0140 }, + { RK_GPIO4_B0, RK3588_VCCIO6_IOC_REG + 0x0144 }, + { RK_GPIO4_C0, RK3588_VCCIO6_IOC_REG + 0x0148 }, + { RK_GPIO4_C2, RK3588_VCCIO3_5_IOC_REG + 0x0148 }, + { RK_GPIO4_D0, RK3588_VCCIO2_IOC_REG + 0x014c }, +}; + +static rt_err_t rk3588_set_pull(struct rockchip_pin_bank *pin_bank, int pin, int pull) +{ + int reg, pull_value; + rt_uint32_t data; + rt_uint8_t bit, type; + struct rt_syscon *regmap; + struct rockchip_pin_data *drvdata = pin_bank->drvdata; + + if (pull == PIN_CONFIG_BIAS_PULL_PIN_DEFAULT) + { + return -RT_ENOSYS; + } + + for (int i = RT_ARRAY_SIZE(rk3588_pull_regs) - 1; i >= 0; --i) + { + if (pin >= rk3588_pull_regs[i][0]) + { + reg = rk3588_pull_regs[i][1]; + reg += ((pin - rk3588_pull_regs[i][0]) / RK3588_PULL_PINS_PER_REG) * 4; + bit = ((pin % 32) % RK3588_PULL_PINS_PER_REG) * RK3588_PULL_BITS_PER_PIN; + + goto _find; + } + } + + return -RT_EINVAL; + +_find: + regmap = drvdata->regmap_base; + + type = pin_bank->pull_type[pin / 8]; + pull_value = rockchip_translate_pull_value(type, pull); + + if (pull_value < 0) + { + LOG_E("Not supported pull = %d, fixup the code or firmware", pull); + + return pull_value; + } + + /* enable the write to the equivalent lower bits */ + data = ((1 << ROCKCHIP_PULL_BITS_PER_PIN) - 1) << (bit + 16); + data |= pull_value << bit; + + return rt_syscon_write(regmap, reg, data); +} + +#define RK3588_DRV_BITS_PER_PIN 4 +#define RK3588_DRV_PINS_PER_REG 4 + +static const rt_uint32_t rk3588_drive_regs[][2] = +{ + { RK_GPIO0_A0, RK3588_PMU1_IOC_REG + 0x0010 }, + { RK_GPIO0_A4, RK3588_PMU1_IOC_REG + 0x0014 }, + { RK_GPIO0_B0, RK3588_PMU1_IOC_REG + 0x0018 }, + { RK_GPIO0_B4, RK3588_PMU2_IOC_REG + 0x0014 }, + { RK_GPIO0_C0, RK3588_PMU2_IOC_REG + 0x0018 }, + { RK_GPIO0_C4, RK3588_PMU2_IOC_REG + 0x001c }, + { RK_GPIO0_D0, RK3588_PMU2_IOC_REG + 0x0020 }, + { RK_GPIO0_D4, RK3588_PMU2_IOC_REG + 0x0024 }, + { RK_GPIO1_A0, RK3588_VCCIO1_4_IOC_REG + 0x0020 }, + { RK_GPIO1_A4, RK3588_VCCIO1_4_IOC_REG + 0x0024 }, + { RK_GPIO1_B0, RK3588_VCCIO1_4_IOC_REG + 0x0028 }, + { RK_GPIO1_B4, RK3588_VCCIO1_4_IOC_REG + 0x002c }, + { RK_GPIO1_C0, RK3588_VCCIO1_4_IOC_REG + 0x0030 }, + { RK_GPIO1_C4, RK3588_VCCIO1_4_IOC_REG + 0x0034 }, + { RK_GPIO1_D0, RK3588_VCCIO1_4_IOC_REG + 0x0038 }, + { RK_GPIO1_D4, RK3588_VCCIO1_4_IOC_REG + 0x003c }, + { RK_GPIO2_A0, RK3588_EMMC_IOC_REG + 0x0040 }, + { RK_GPIO2_A4, RK3588_VCCIO3_5_IOC_REG + 0x0044 }, + { RK_GPIO2_B0, RK3588_VCCIO3_5_IOC_REG + 0x0048 }, + { RK_GPIO2_B4, RK3588_VCCIO3_5_IOC_REG + 0x004c }, + { RK_GPIO2_C0, RK3588_VCCIO3_5_IOC_REG + 0x0050 }, + { RK_GPIO2_C4, RK3588_VCCIO3_5_IOC_REG + 0x0054 }, + { RK_GPIO2_D0, RK3588_EMMC_IOC_REG + 0x0058 }, + { RK_GPIO2_D4, RK3588_EMMC_IOC_REG + 0x005c }, + { RK_GPIO3_A0, RK3588_VCCIO3_5_IOC_REG + 0x0060 }, + { RK_GPIO3_A4, RK3588_VCCIO3_5_IOC_REG + 0x0064 }, + { RK_GPIO3_B0, RK3588_VCCIO3_5_IOC_REG + 0x0068 }, + { RK_GPIO3_B4, RK3588_VCCIO3_5_IOC_REG + 0x006c }, + { RK_GPIO3_C0, RK3588_VCCIO3_5_IOC_REG + 0x0070 }, + { RK_GPIO3_C4, RK3588_VCCIO3_5_IOC_REG + 0x0074 }, + { RK_GPIO3_D0, RK3588_VCCIO3_5_IOC_REG + 0x0078 }, + { RK_GPIO3_D4, RK3588_VCCIO3_5_IOC_REG + 0x007c }, + { RK_GPIO4_A0, RK3588_VCCIO6_IOC_REG + 0x0080 }, + { RK_GPIO4_A4, RK3588_VCCIO6_IOC_REG + 0x0084 }, + { RK_GPIO4_B0, RK3588_VCCIO6_IOC_REG + 0x0088 }, + { RK_GPIO4_B4, RK3588_VCCIO6_IOC_REG + 0x008c }, + { RK_GPIO4_C0, RK3588_VCCIO6_IOC_REG + 0x0090 }, + { RK_GPIO4_C2, RK3588_VCCIO3_5_IOC_REG + 0x0090 }, + { RK_GPIO4_C4, RK3588_VCCIO3_5_IOC_REG + 0x0094 }, + { RK_GPIO4_D0, RK3588_VCCIO2_IOC_REG + 0x0098 }, + { RK_GPIO4_D4, RK3588_VCCIO2_IOC_REG + 0x009c }, +}; + +static rt_err_t rk3588_set_drive(struct rockchip_pin_bank *pin_bank, int pin, int strength) +{ + int reg; + rt_uint8_t bit; + rt_uint32_t data; + struct rt_syscon *regmap; + struct rockchip_pin_data *drvdata = pin_bank->drvdata; + + for (int i = RT_ARRAY_SIZE(rk3588_drive_regs) - 1; i >= 0; --i) + { + if (pin >= rk3588_drive_regs[i][0]) + { + reg = rk3588_drive_regs[i][1]; + reg += ((pin - rk3588_drive_regs[i][0]) / RK3588_DRV_PINS_PER_REG) * 4; + bit = ((pin % 32) % RK3588_DRV_PINS_PER_REG) * RK3588_DRV_BITS_PER_PIN; + + goto _find; + } + } + + return -RT_EINVAL; + +_find: + regmap = drvdata->regmap_base; + + /* enable the write to the equivalent lower bits */ + data = ((1 << ROCKCHIP_DRV_BITS_PER_PIN) - 1) << (bit + 16); + data |= (strength << bit); + + return rt_syscon_write(regmap, reg, data); +} + +#define RK3588_SMT_BITS_PER_PIN 1 +#define RK3588_SMT_PINS_PER_REG 8 + +static const rt_uint32_t rk3588_schmitt_regs[][2] = +{ + { RK_GPIO0_A0, RK3588_PMU1_IOC_REG + 0x0030 }, + { RK_GPIO0_B0, RK3588_PMU1_IOC_REG + 0x0034 }, + { RK_GPIO0_B5, RK3588_PMU2_IOC_REG + 0x0040 }, + { RK_GPIO0_C0, RK3588_PMU2_IOC_REG + 0x0044 }, + { RK_GPIO0_D0, RK3588_PMU2_IOC_REG + 0x0048 }, + { RK_GPIO1_A0, RK3588_VCCIO1_4_IOC_REG + 0x0210 }, + { RK_GPIO1_B0, RK3588_VCCIO1_4_IOC_REG + 0x0214 }, + { RK_GPIO1_C0, RK3588_VCCIO1_4_IOC_REG + 0x0218 }, + { RK_GPIO1_D0, RK3588_VCCIO1_4_IOC_REG + 0x021c }, + { RK_GPIO2_A0, RK3588_EMMC_IOC_REG + 0x0220 }, + { RK_GPIO2_A6, RK3588_VCCIO3_5_IOC_REG + 0x0220 }, + { RK_GPIO2_B0, RK3588_VCCIO3_5_IOC_REG + 0x0224 }, + { RK_GPIO2_C0, RK3588_VCCIO3_5_IOC_REG + 0x0228 }, + { RK_GPIO2_D0, RK3588_EMMC_IOC_REG + 0x022c }, + { RK_GPIO3_A0, RK3588_VCCIO3_5_IOC_REG + 0x0230 }, + { RK_GPIO3_B0, RK3588_VCCIO3_5_IOC_REG + 0x0234 }, + { RK_GPIO3_C0, RK3588_VCCIO3_5_IOC_REG + 0x0238 }, + { RK_GPIO3_D0, RK3588_VCCIO3_5_IOC_REG + 0x023c }, + { RK_GPIO4_A0, RK3588_VCCIO6_IOC_REG + 0x0240 }, + { RK_GPIO4_B0, RK3588_VCCIO6_IOC_REG + 0x0244 }, + { RK_GPIO4_C0, RK3588_VCCIO6_IOC_REG + 0x0248 }, + { RK_GPIO4_C2, RK3588_VCCIO3_5_IOC_REG + 0x0248 }, + { RK_GPIO4_D0, RK3588_VCCIO2_IOC_REG + 0x024c }, +}; + +static rt_err_t rk3588_set_schmitt(struct rockchip_pin_bank *pin_bank, int pin, int enable) +{ + int reg; + rt_uint8_t bit; + rt_uint32_t data; + struct rt_syscon *regmap; + struct rockchip_pin_data *drvdata = pin_bank->drvdata; + + for (int i = RT_ARRAY_SIZE(rk3588_schmitt_regs) - 1; i >= 0; --i) + { + if (pin >= rk3588_schmitt_regs[i][0]) + { + reg = rk3588_schmitt_regs[i][1]; + reg += ((pin - rk3588_schmitt_regs[i][0]) / RK3588_SMT_PINS_PER_REG) * 4; + bit = ((pin % 32) % RK3588_SMT_PINS_PER_REG) * RK3588_SMT_BITS_PER_PIN; + + goto _find; + } + } + + return -RT_EINVAL; + +_find: + regmap = drvdata->regmap_base; + + /* enable the write to the equivalent lower bits */ + data = ((1 << RK3588_SMT_BITS_PER_PIN) - 1) << (bit + 16); + data |= (enable << bit); + + return rt_syscon_write(regmap, reg, data); +} + +static struct rockchip_pin_bank rk3588_pin_banks[] = +{ + PIN_BANK_FLAGS_IOMUX_PULL(0, 32, "gpio0", IOMUX_WIDTH_4BIT, PULL_TYPE_IO_1V8_ONLY), + PIN_BANK_FLAGS_IOMUX_PULL(1, 32, "gpio1", IOMUX_WIDTH_4BIT, PULL_TYPE_IO_1V8_ONLY), + PIN_BANK_FLAGS_IOMUX_PULL(2, 32, "gpio2", IOMUX_WIDTH_4BIT, PULL_TYPE_IO_1V8_ONLY), + PIN_BANK_FLAGS_IOMUX_PULL(3, 32, "gpio3", IOMUX_WIDTH_4BIT, PULL_TYPE_IO_1V8_ONLY), + PIN_BANK_FLAGS_IOMUX_PULL(4, 32, "gpio4", IOMUX_WIDTH_4BIT, PULL_TYPE_IO_1V8_ONLY), +}; + +static struct rockchip_pin_ctrl rk3588_pin_ctrl = +{ + .pin_banks = rk3588_pin_banks, + .banks_nr = RT_ARRAY_SIZE(rk3588_pin_banks), + .label = "RK3588-GPIO", + .type = RK3588, + .set_mux = rk3588_set_mux, + .set_pull = rk3588_set_pull, + .set_drive = rk3588_set_drive, + .set_schmitt = rk3588_set_schmitt, +}; + +static const struct rt_pin_ctrl_conf_params rockchip_conf_params[] = +{ + { "bias-disable", PIN_CONFIG_BIAS_DISABLE, 0 }, + { "bias-bus-hold", PIN_CONFIG_BIAS_BUS_HOLD, 0 }, + { "bias-pull-up", PIN_CONFIG_BIAS_PULL_UP, 1 }, + { "bias-pull-down", PIN_CONFIG_BIAS_PULL_DOWN, 1 }, + { "bias-pull-pin-default", PIN_CONFIG_BIAS_PULL_PIN_DEFAULT, 1 }, + { "drive-strength", PIN_CONFIG_DRIVE_STRENGTH, 0 }, + { "input-schmitt-disable", PIN_CONFIG_INPUT_SCHMITT_ENABLE, 0 }, + { "input-schmitt-enable", PIN_CONFIG_INPUT_SCHMITT_ENABLE, 1 }, +}; + +static int rockchip_pinconf_prop_name_to_param(const char *propname, rt_uint32_t *default_value) +{ + const struct rt_pin_ctrl_conf_params *params = rockchip_conf_params; + + for (int i = 0; i < RT_ARRAY_SIZE(rockchip_conf_params); ++i, ++params) + { + if (!rt_strcmp(params->propname, propname)) + { + *default_value = params->default_value; + + return params->param; + } + } + + return -RT_ENOSYS; +} + +static rt_err_t rockchip_pinconf_pull_apply(struct rockchip_pin_ctrl *pinctrl, + struct rockchip_pin_bank *pin_bank, rt_uint32_t pin, rt_uint32_t param, rt_uint32_t arg) +{ + rt_err_t err = RT_EOK; + + switch (param) + { + case PIN_CONFIG_BIAS_DISABLE: + case PIN_CONFIG_BIAS_PULL_UP: + case PIN_CONFIG_BIAS_PULL_DOWN: + case PIN_CONFIG_BIAS_PULL_PIN_DEFAULT: + case PIN_CONFIG_BIAS_BUS_HOLD: + if (pinctrl->set_pull) + { + err = pinctrl->set_pull(pin_bank, pin, param); + } + else + { + err = -RT_ENOSYS; + } + break; + + case PIN_CONFIG_DRIVE_STRENGTH: + if (pinctrl->set_drive) + { + err = pinctrl->set_drive(pin_bank, pin, arg); + } + else + { + err = -RT_ENOSYS; + } + break; + + case PIN_CONFIG_INPUT_SCHMITT_ENABLE: + if (pinctrl->set_schmitt) + { + err = pinctrl->set_schmitt(pin_bank, pin, arg); + } + else + { + err = -RT_ENOSYS; + } + break; + + default: + break; + } + + return err; +} + +static rt_err_t rockchip_pinctrl_confs_apply(struct rt_device *device, void *fw_conf_np) +{ + rt_err_t err = RT_EOK; + const fdt32_t *cell; + int pin, function, param; + rt_uint32_t value, default_value, arg; + struct rt_ofw_prop *prop, *pin_prop; + struct rt_ofw_node *pull_np, *conf_np = fw_conf_np; + struct rockchip_pin_ctrl *pinctrl; + struct rockchip_pin_bank *pin_bank; + struct rockchip_pinctrl_device *pinctrl_dev; + + LOG_D("Pinctrl apply '%s'", rt_ofw_node_full_name(conf_np)); + + pinctrl_dev = rt_container_of(device, struct rockchip_pinctrl_device, parent); + pinctrl = pinctrl_dev->pinctrl; + + rt_ofw_foreach_prop_u32(conf_np, "rockchip,pins", prop, cell, value) + { + /* bank -> pin -> function -> pull */ + pin_bank = &pinctrl->pin_banks[value]; + + cell = rt_ofw_prop_next_u32(prop, cell, &value); + pin = value; + + cell = rt_ofw_prop_next_u32(prop, cell, &value); + function = value; + + cell = rt_ofw_prop_next_u32(prop, cell, &value); + pull_np = rt_ofw_find_node_by_phandle(value); + + if (!pull_np) + { + err = -RT_ERROR; + LOG_E("Firmware ref error in '%s'", rt_ofw_node_full_name(conf_np)); + + break; + } + + if (pinctrl->set_mux) + { + LOG_D("IOMUX from GPIO%d-%c%d to function(%d)", + pin_bank->bank_num, 'A' + (pin % 32) / 8, pin % 8, function); + + err = pinctrl->set_mux(pin_bank, pin, function); + + if (err) + { + break; + } + } + + rt_ofw_foreach_prop(pull_np, pin_prop) + { + if (!rt_strcmp(pin_prop->name, "phandle")) + { + continue; + } + + param = rockchip_pinconf_prop_name_to_param(pin_prop->name, &default_value); + + if (param < 0) + { + err = param; + + break; + } + + if (pin_prop->length < sizeof(*cell)) + { + arg = default_value; + } + else + { + rt_ofw_prop_next_u32(pin_prop, RT_NULL, &arg); + } + + err = rockchip_pinconf_pull_apply(pinctrl, pin_bank, pin, param, arg); + + if (err && err != -RT_ENOSYS) + { + break; + } + } + + rt_ofw_node_put(pull_np); + } + + return err; +} + +static const struct rt_pin_ops rockchip_pinctrl_ops = +{ + .pin_ctrl_confs_apply = rockchip_pinctrl_confs_apply, +}; + +static rt_err_t rockchip_pinctrl_probe(struct rt_platform_device *pdev) +{ + rt_err_t err = RT_EOK; + int grf_offs, pmu_offs, drv_grf_offs, drv_pmu_offs; + struct rockchip_pin_data *drvdata = RT_NULL; + struct rt_ofw_node *np = pdev->parent.ofw_node; + struct rockchip_pin_ctrl *pinctrl = (typeof(pinctrl))pdev->id->data; + struct rockchip_pin_bank *pin_bank = pinctrl->pin_banks; + struct rockchip_pinctrl_device *pinctrl_dev = rt_malloc(sizeof(*pinctrl_dev)); + + if (!pinctrl_dev) + { + return -RT_ENOMEM; + } + + drvdata = &pinctrl_dev->drvdata; + + if (!(drvdata->regmap_base = rt_syscon_find_by_ofw_phandle(np, "rockchip,grf"))) + { + err = -RT_EIO; + goto _fail; + } + + drvdata->regmap_pmu = rt_syscon_find_by_ofw_phandle(np, "rockchip,pmu"); + drvdata->pinctrl = pinctrl; + + pinctrl_dev->parent.ops = &rockchip_pinctrl_ops; + pinctrl_dev->pinctrl = pinctrl; + pinctrl->pins_nr = 0; + + grf_offs = pinctrl->grf_mux_offset; + pmu_offs = pinctrl->pmu_mux_offset; + drv_pmu_offs = pinctrl->pmu_drv_offset; + drv_grf_offs = pinctrl->grf_drv_offset; + pin_bank = pinctrl->pin_banks; + + for (int i = 0; i < pinctrl->banks_nr; ++i, ++pin_bank) + { + for (int bank_pins = 0, j = 0; j < 4; ++j) + { + int inc; + struct rockchip_drv *drv = &pin_bank->drv[j]; + struct rockchip_iomux *iomux = &pin_bank->iomux[j]; + + if (bank_pins >= pin_bank->nr_pins) + { + break; + } + + /* Preset iomux offset value, set new start value */ + if (iomux->offset >= 0) + { + if ((iomux->type & IOMUX_SOURCE_PMU) || (iomux->type & IOMUX_L_SOURCE_PMU)) + { + pmu_offs = iomux->offset; + } + else + { + grf_offs = iomux->offset; + } + } + else + { + /* Set current iomux offset */ + iomux->offset = ((iomux->type & IOMUX_SOURCE_PMU) || (iomux->type & IOMUX_L_SOURCE_PMU)) ? + pmu_offs : grf_offs; + } + + /* Preset drv offset value, set new start value */ + if (drv->offset >= 0) + { + if (iomux->type & IOMUX_SOURCE_PMU) + { + drv_pmu_offs = drv->offset; + } + else + { + drv_grf_offs = drv->offset; + } + } + else + { + /* Set current drv offset */ + drv->offset = (iomux->type & IOMUX_SOURCE_PMU) ? drv_pmu_offs : drv_grf_offs; + } + + /* + * Increase offset according to iomux width. + * 4bit iomux'es are spread over two registers. + */ + inc = (iomux->type & (IOMUX_WIDTH_4BIT | IOMUX_WIDTH_3BIT | IOMUX_WIDTH_2BIT)) ? 8 : 4; + + /* Preset drv offset value, set new start value */ + if ((iomux->type & IOMUX_SOURCE_PMU) || (iomux->type & IOMUX_L_SOURCE_PMU)) + { + pmu_offs += inc; + } + else + { + grf_offs += inc; + } + + /* + * Increase offset according to drv width. + * 3bit drive-strenth'es are spread over two registers. + */ + inc = ((drv->drv_type == DRV_TYPE_IO_1V8_3V0_AUTO) || (drv->drv_type == DRV_TYPE_IO_3V3_ONLY)) ? 8 : 4; + + if (iomux->type & IOMUX_SOURCE_PMU) + { + drv_pmu_offs += inc; + } + else + { + drv_grf_offs += inc; + } + + bank_pins += 8; + } + + pin_bank->drvdata = drvdata; + rt_spin_lock_init(&pin_bank->spinlock); + + pinctrl->pins_nr += pin_bank->nr_pins; + } + + rt_ofw_data(np) = &pinctrl_dev->parent; + + return RT_EOK; + +_fail: + rt_free(pinctrl_dev); + + return err; +} + +static const struct rt_ofw_node_id rockchip_pinctrl_ofw_ids[] = +{ + { .compatible = "rockchip,rk3308-pinctrl", .data = &rk3308_pin_ctrl }, + { .compatible = "rockchip,rk3568-pinctrl", .data = &rk3568_pin_ctrl }, + { .compatible = "rockchip,rk3588-pinctrl", .data = &rk3588_pin_ctrl }, + { /* sentinel */ } +}; + +static struct rt_platform_driver rockchip_pinctrl_driver = +{ + .name = "pinctrl-rockchip", + .ids = rockchip_pinctrl_ofw_ids, + + .probe = rockchip_pinctrl_probe, +}; + +static int rockchip_pinctrl_register(void) +{ + rt_platform_driver_register(&rockchip_pinctrl_driver); + + return 0; +} +INIT_PLATFORM_EXPORT(rockchip_pinctrl_register); diff --git a/components/drivers/pinctrl/pinctrl-rockchip.h b/components/drivers/pinctrl/pinctrl-rockchip.h new file mode 100644 index 000000000000..610fa9bad2b7 --- /dev/null +++ b/components/drivers/pinctrl/pinctrl-rockchip.h @@ -0,0 +1,178 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-3-08 GuEe-GUI the first version + */ + +#ifndef __ROCKCHIP_PINCTRL_H__ +#define __ROCKCHIP_PINCTRL_H__ + +#include +#include + +#include +#include "../soc/rockchip/rockchip.h" + +#define ROCKCHIP_PULL_BITS_PER_PIN 2 +#define ROCKCHIP_PULL_PINS_PER_REG 8 +#define ROCKCHIP_PULL_BANK_STRIDE 16 +#define ROCKCHIP_DRV_BITS_PER_PIN 2 +#define ROCKCHIP_DRV_PINS_PER_REG 8 +#define ROCKCHIP_DRV_BANK_STRIDE 16 +#define ROCKCHIP_DRV_3BITS_PER_PIN 3 + +enum rockchip_pin_drv_type +{ + DRV_TYPE_IO_DEFAULT = 0, + DRV_TYPE_IO_1V8_OR_3V0, + DRV_TYPE_IO_1V8_ONLY, + DRV_TYPE_IO_1V8_3V0_AUTO, + DRV_TYPE_IO_3V3_ONLY, + DRV_TYPE_MAX +}; + +enum rockchip_pin_pull_type +{ + PULL_TYPE_IO_DEFAULT = 0, + PULL_TYPE_IO_1V8_ONLY, + PULL_TYPE_MAX +}; + +enum rockchip_pinctrl_type +{ + RK3308, + RK3568, + RK3588, +}; + +struct rockchip_gpio_regs +{ + rt_uint32_t port_dr; /* Data register */ + rt_uint32_t port_ddr; /* Data direction register */ + rt_uint32_t int_en; /* Interrupt enable */ + rt_uint32_t int_mask; /* Interrupt mask */ + rt_uint32_t int_type; /* Interrupt trigger type, such as high, low, edge trriger type. */ + rt_uint32_t int_polarity; /* Interrupt polarity enable register */ + rt_uint32_t int_bothedge; /* Interrupt bothedge enable register */ + rt_uint32_t int_status; /* Interrupt status register */ + rt_uint32_t int_rawstatus; /* Int_status = int_rawstatus & int_mask */ + rt_uint32_t debounce; /* Enable debounce for interrupt signal */ + rt_uint32_t dbclk_div_en; /* Enable divider for debounce clock */ + rt_uint32_t dbclk_div_con; /* Setting for divider of debounce clock */ + rt_uint32_t port_eoi; /* End of interrupt of the port */ + rt_uint32_t ext_port; /* Port data from external */ + rt_uint32_t version_id; /* Controller version register */ +}; + +struct rockchip_iomux +{ + int type; + int offset; +}; + +struct rockchip_drv +{ + enum rockchip_pin_drv_type drv_type; + int offset; +}; + +struct rockchip_pin_data +{ + struct rt_syscon *regmap_base; + struct rt_syscon *regmap_pmu; + rt_size_t reg_size; + + struct rockchip_pin_ctrl *pinctrl; +}; + +struct rockchip_pin_bank +{ + struct rt_device_pin parent; + + const char *name; + + int irq; + void *reg_base; + struct rt_clk *clk; + struct rt_clk *db_clk; + + rt_uint8_t nr_pins; + rt_uint8_t bank_num; + rt_uint32_t gpio_type; + rt_uint32_t toggle_edge_mode; + struct rockchip_pin_data *drvdata; + const struct rockchip_gpio_regs *gpio_regs; + + struct rockchip_iomux iomux[4]; + struct rockchip_drv drv[4]; + enum rockchip_pin_pull_type pull_type[4]; + struct rt_spinlock spinlock; + + rt_uint32_t recalced_mask; +}; + +struct rockchip_mux_recalced_data +{ + rt_uint8_t num; + rt_uint8_t pin; + rt_uint32_t reg; + rt_uint8_t bit; + rt_uint8_t mask; +}; + +enum rockchip_mux_route_location +{ + ROCKCHIP_ROUTE_SAME = 0, + ROCKCHIP_ROUTE_PMU, + ROCKCHIP_ROUTE_GRF, +}; + +struct rockchip_mux_route_data +{ + rt_uint8_t bank_num; + rt_uint8_t pin; + rt_uint8_t func; + enum rockchip_mux_route_location route_location; + rt_uint32_t route_offset; + rt_uint32_t route_val; +}; + +struct rockchip_pin_ctrl +{ + char *label; + enum rockchip_pinctrl_type type; + + struct rockchip_pin_bank *pin_banks; + rt_uint32_t banks_nr; + rt_uint32_t pins_nr; + + int grf_mux_offset; + int pmu_mux_offset; + int grf_drv_offset; + int pmu_drv_offset; + + struct rockchip_mux_recalced_data *iomux_recalced; + rt_uint32_t niomux_recalced; + + struct rockchip_mux_route_data *iomux_routes; + rt_uint32_t niomux_routes; + + rt_err_t (*set_mux)(struct rockchip_pin_bank *pin_bank, int pin, int mux); + rt_err_t (*set_pull)(struct rockchip_pin_bank *pin_bank, int pin, int pull); + rt_err_t (*set_drive)(struct rockchip_pin_bank *pin_bank, int pin, int strength); + rt_err_t (*set_schmitt)(struct rockchip_pin_bank *pin_bank, int pin, int enable); +}; + +struct rockchip_pinctrl_device +{ + struct rt_device_pin parent; + + struct rockchip_pin_data drvdata; + struct rockchip_pin_ctrl *pinctrl; +}; + +#endif /* __ROCKCHIP_PINCTRL_H__ */ diff --git a/components/drivers/pinctrl/pinctrl.c b/components/drivers/pinctrl/pinctrl.c new file mode 100644 index 000000000000..06aa87b2c0e7 --- /dev/null +++ b/components/drivers/pinctrl/pinctrl.c @@ -0,0 +1,229 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-11-26 GuEe-GUI first version + */ + +#include +#include + +#define DBG_TAG "rtdm.pinctrl" +#define DBG_LVL DBG_INFO +#include + +#include +#include + +#ifdef RT_USING_OFW +static rt_err_t ofw_pin_ctrl_confs_apply(struct rt_ofw_node *np, int index) +{ + rt_err_t err = -RT_EEMPTY; + rt_phandle phandle; + const fdt32_t *cell; + struct rt_ofw_prop *prop; + char pinctrl_n_name[sizeof("pinctrl-0")]; + + rt_sprintf(pinctrl_n_name, "pinctrl-%d", index); + index = 0; + + rt_ofw_foreach_prop_u32(np, pinctrl_n_name, prop, cell, phandle) + { + struct rt_device_pin *pinctrl = RT_NULL; + struct rt_ofw_node *conf_np, *pinctrl_np; + + conf_np = pinctrl_np = rt_ofw_find_node_by_phandle(phandle); + + if (!conf_np) + { + err = -RT_EIO; + + break; + } + /* + * We always assume the phandle in pinctrl-N is the pinctrl-device + * node's child node. If not, we need a better way to find it: + * + * / { + * serial@4600 { + * device_type = "serial"; + * reg = <0x4600 0x100>; + * clock-frequency = <0>; + * pinctrl-names = "default"; + * pinctrl-0 = <&uart_pin>; + * }; + * + * i2c@4700 { + * reg = <0x4700 0x100>; + * pinctrl-names = "default"; + * pinctrl-0 = <&i2c_pin_scl, &i2c_pin_sda>; + * }; + * + * pinctrl: pinctrl { + * + * uart_pin { + * multi,pins = + * <0 PD0 1 &uart_rx_pull_up>, + * <0 PD1 1 &uart_tx_pull_up>; + * }; + * + * i2c_pin_scl { + * single,pins = <0 PB1>; + * pull = <&i2c_pull_none_smt>; + * function = <1>; + * }; + * + * i2c_pin_sda { + * single,pins = <0 PB2>; + * pull = <&i2c_pull_none_smt>; + * function = <1>; + * }; + * }; + * } + */ + rt_ofw_foreach_parent_node(pinctrl_np) + { + if (rt_ofw_prop_read_bool(pinctrl_np, "compatible")) + { + break; + } + } + + if (pinctrl_np) + { + pinctrl = rt_ofw_data(pinctrl_np); + + rt_ofw_node_put(pinctrl_np); + } + + if (!pinctrl || !pinctrl->ops || !pinctrl->ops->pin_ctrl_confs_apply) + { + if (index) + { + err = -RT_EEMPTY; + } + else + { + err = -RT_ERROR; + } + + rt_ofw_node_put(conf_np); + + break; + } + + err = pinctrl->ops->pin_ctrl_confs_apply(&pinctrl->parent, conf_np); + rt_ofw_node_put(conf_np); + + if (err) + { + break; + } + + ++index; + } + + return err; +} + +static int ofw_pin_ctrl_confs_lookup(struct rt_ofw_node *np, const char *name) +{ + return rt_ofw_prop_index_of_string(np, "pinctrl-names", name); +} + +static rt_err_t ofw_pin_ctrl_confs_apply_by_name(struct rt_ofw_node *np, const char *name) +{ + int index; + rt_err_t err; + + index = ofw_pin_ctrl_confs_lookup(np, name); + + if (index >= 0) + { + err = ofw_pin_ctrl_confs_apply(np, index); + } + else + { + err = -RT_EEMPTY; + } + + return err; +} +#endif /* RT_USING_OFW */ + +rt_ssize_t rt_pin_ctrl_confs_lookup(struct rt_device *device, const char *name) +{ + rt_ssize_t res; + + if (device && name) + { + res = -RT_ENOSYS; + + #ifdef RT_USING_OFW + if (device->ofw_node) + { + res = ofw_pin_ctrl_confs_lookup(device->ofw_node, name); + } + #endif /* RT_USING_OFW */ + } + else + { + res = -RT_EINVAL; + } + + return res; +} + +rt_err_t rt_pin_ctrl_confs_apply(struct rt_device *device, int index) +{ + rt_err_t err; + + if (device && index >= 0) + { + err = -RT_ENOSYS; + + #ifdef RT_USING_OFW + if (device->ofw_node) + { + err = ofw_pin_ctrl_confs_apply(device->ofw_node, index); + } + #endif /* RT_USING_OFW */ + } + else + { + err = -RT_EINVAL; + } + + return err; +} + +rt_err_t rt_pin_ctrl_confs_apply_by_name(struct rt_device *device, const char *name) +{ + rt_err_t err; + + if (device) + { + if (!name) + { + name = "default"; + } + + err = -RT_ENOSYS; + + #ifdef RT_USING_OFW + if (device->ofw_node) + { + err = ofw_pin_ctrl_confs_apply_by_name(device->ofw_node, name); + } + #endif /* RT_USING_OFW */ + } + else + { + err = -RT_EINVAL; + } + + return err; +} diff --git a/components/drivers/pm/Kconfig b/components/drivers/pm/Kconfig deleted file mode 100644 index fffea8de6056..000000000000 --- a/components/drivers/pm/Kconfig +++ /dev/null @@ -1,51 +0,0 @@ -menuconfig RT_USING_PM - bool "Using Power Management device drivers" - default n - - if RT_USING_PM - config PM_TICKLESS_THRESHOLD_TIME - int "PM tickless threashold time" - default 2 - - config PM_USING_CUSTOM_CONFIG - bool "PM using custom pm config" - default n - - config PM_ENABLE_DEBUG - bool "PM Enable Debug" - default n - - config PM_ENABLE_SUSPEND_SLEEP_MODE - bool "PM Device suspend change sleep mode" - default n - - config PM_ENABLE_THRESHOLD_SLEEP_MODE - bool "PM using threshold time change sleep mode" - default n - - if PM_ENABLE_THRESHOLD_SLEEP_MODE - config PM_LIGHT_THRESHOLD_TIME - int "PM light mode threashold time" - default 5 - - config PM_DEEP_THRESHOLD_TIME - int "PM deep mode threashold time" - default 20 - - config PM_STANDBY_THRESHOLD_TIME - int "PM standby mode threashold time" - default 100 - endif - endif - -config RT_PM_RESET_SYSCON - bool "Generic SYSCON regmap reset driver" - depends on RT_USING_PM - depends on RT_USING_OFW - select RT_MFD_SYSCON - -config RT_PM_RESET_SYSCON_POWEROFF - bool "Generic SYSCON regmap poweroff driver" - depends on RT_USING_PM - depends on RT_USING_OFW - select RT_MFD_SYSCON diff --git a/components/drivers/pm/SConscript b/components/drivers/pm/SConscript index 09d8d879ca94..fd23e8293c36 100644 --- a/components/drivers/pm/SConscript +++ b/components/drivers/pm/SConscript @@ -1,21 +1,15 @@ from building import * -group = [] - -if not GetDepend(['RT_USING_PM']): - Return('group') - cwd = GetCurrentDir() +src = [] CPPPATH = [cwd + '/../include'] +group = [] -src = ['pm.c', 'lptimer.c'] - -if GetDepend(['RT_PM_RESET_SYSCON_POWEROFF']): - src += ['pm-syscon-poweroff.c'] - -if GetDepend(['RT_PM_RESET_SYSCON']): - src += ['pm-syscon-reboot.c'] +if GetDepend(['RT_USING_PM']): + src = src + ['pm.c'] + src = src + ['lptimer.c'] -group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) +if len(src): + group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) Return('group') diff --git a/components/drivers/pm/pm.c b/components/drivers/pm/pm.c index ad6110dd8b8a..024e301792e7 100644 --- a/components/drivers/pm/pm.c +++ b/components/drivers/pm/pm.c @@ -53,9 +53,6 @@ #endif #endif -void (*rt_pm_shutdown)(void) = RT_NULL; -void (*rt_pm_reset)(void) = RT_NULL; - static struct rt_pm _pm; /* default mode : system power on */ diff --git a/components/drivers/pmdomain/Kconfig b/components/drivers/pmdomain/Kconfig new file mode 100644 index 000000000000..73e423027f1e --- /dev/null +++ b/components/drivers/pmdomain/Kconfig @@ -0,0 +1,15 @@ +if RT_USING_DM +menu "Power Management (PM) Domains device drivers" + +config RT_PMDOMAIN_ROCKCHIP + bool "Rockchip" + select RT_MFD_SYSCON + default n + +config RT_PMDOMAIN_SCMI + bool "ARM SCMI" + select RT_FIRMWARE_ARM_SCMI + default n + +endmenu +endif diff --git a/components/drivers/pmdomain/SConscript b/components/drivers/pmdomain/SConscript new file mode 100644 index 000000000000..d47a37d1d107 --- /dev/null +++ b/components/drivers/pmdomain/SConscript @@ -0,0 +1,21 @@ +from building import * + +group = [] + +if not GetDepend(['RT_USING_DM']): + Return('group') + +cwd = GetCurrentDir() +CPPPATH = [cwd + '/../include'] + +src = [] + +if GetDepend(['RT_PMDOMAIN_ROCKCHIP']): + src += ['pm-domain-rockchip.c'] + +if GetDepend(['RT_PMDOMAIN_SCMI']): + src += ['pm-domain-scmi.c'] + +group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') diff --git a/components/drivers/pmdomain/pm-domain-rockchip.c b/components/drivers/pmdomain/pm-domain-rockchip.c new file mode 100644 index 000000000000..c5f2603a2316 --- /dev/null +++ b/components/drivers/pmdomain/pm-domain-rockchip.c @@ -0,0 +1,1241 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-11-21 GuEe-GUI first version + */ + +#include +#include +#include + +#include + +#define DBG_TAG "pm-domain.rockchip" +#define DBG_LVL DBG_INFO +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct rockchip_domain_info +{ + const char *name; + int pwr_mask; + int status_mask; + int req_mask; + int idle_mask; + int ack_mask; + rt_bool_t active_wakeup; + int pwr_w_mask; + int req_w_mask; + int mem_status_mask; + int repair_status_mask; + rt_uint32_t pwr_offset; + rt_uint32_t mem_offset; + rt_uint32_t req_offset; +}; + +struct rockchip_pmu_info +{ + rt_uint32_t pwr_offset; + rt_uint32_t status_offset; + rt_uint32_t req_offset; + rt_uint32_t idle_offset; + rt_uint32_t ack_offset; + rt_uint32_t mem_pwr_offset; + rt_uint32_t chain_status_offset; + rt_uint32_t mem_status_offset; + rt_uint32_t repair_status_offset; + + rt_uint32_t core_pwrcnt_offset; + rt_uint32_t gpu_pwrcnt_offset; + + rt_uint32_t core_power_transition_time; + rt_uint32_t gpu_power_transition_time; + + int num_domains; + const struct rockchip_domain_info *domain_info; +}; + +#define DOMAIN(NAME, PWR, \ +STATUS, REQ, IDLE, ACK, WAKEUP) \ +{ \ + .name = NAME, \ + .pwr_mask = (PWR), \ + .status_mask = (STATUS), \ + .req_mask = (REQ), \ + .idle_mask = (IDLE), \ + .ack_mask = (ACK), \ + .active_wakeup = WAKEUP, \ +} + +#define DOMAIN_M(NAME, PWR, \ +STATUS, REQ, IDLE, ACK, WAKEUP) \ +{ \ + .name = NAME, \ + .pwr_w_mask = (PWR) << 16, \ + .pwr_mask = (PWR), \ + .status_mask = (STATUS), \ + .req_w_mask = (REQ) << 16, \ + .req_mask = (REQ), \ + .idle_mask = (IDLE), \ + .ack_mask = (ACK), \ + .active_wakeup = WAKEUP, \ +} + +#define DOMAIN_M_O_R(NAME, P_OFF, PWR, \ +STATUS, M_OFF, M_STATUS, R_STATUS, \ +R_OFF, REQ, IDLE, ACK, WAKEUP) \ +{ \ + .name = NAME, \ + .pwr_offset = P_OFF, \ + .pwr_w_mask = (PWR) << 16, \ + .pwr_mask = (PWR), \ + .status_mask = (STATUS), \ + .mem_offset = M_OFF, \ + .mem_status_mask = (M_STATUS), \ + .repair_status_mask = (R_STATUS), \ + .req_offset = R_OFF, \ + .req_w_mask = (REQ) << 16, \ + .req_mask = (REQ), \ + .idle_mask = (IDLE), \ + .ack_mask = (ACK), \ + .active_wakeup = WAKEUP, \ +} + +#define MAX_QOS_REGS_NUM 5 +#define QOS_PRIORITY 0x08 +#define QOS_MODE 0x0c +#define QOS_BANDWIDTH 0x10 +#define QOS_SATURATION 0x14 +#define QOS_EXTCONTROL 0x18 + +struct rockchip_pmu; + +struct rockchip_pm_domain +{ + struct rt_dm_power_domain parent; + + const struct rockchip_domain_info *info; + struct rockchip_pmu *pmu; + struct rt_ofw_node *np; + + int num_qos; + struct rt_syscon **qos_regmaps; + + rt_uint32_t *qos_save_regs[MAX_QOS_REGS_NUM]; + + struct rt_clk_array *clk_arr; +}; + +#define raw_to_rockchip_pm_domain(raw) rt_container_of(raw, struct rockchip_pm_domain, parent) + +struct rockchip_pmu +{ + struct rt_dm_power_domain_proxy parent; + + struct rt_syscon *regmap; + const struct rockchip_pmu_info *info; + + struct rockchip_pm_domain rk_pmdomains[]; +}; + +#define raw_to_rockchip_pmu(raw) rt_container_of(raw, struct rockchip_pmu, parent) + +#define read_uxx_poll_timeout(OP, PD, VAL, COND, DELAY_US, TIMEOUT_US) \ +({ \ + rt_uint64_t timeout_us = TIMEOUT_US; \ + rt_int64_t left_ns = timeout_us * 1000L; \ + rt_ubase_t delay_us = DELAY_US; \ + rt_uint64_t delay_ns = delay_us * 1000L; \ + for (;;) \ + { \ + (VAL) = OP(PD); \ + if (COND) \ + { \ + break; \ + } \ + if (timeout_us && left_ns < 0) \ + { \ + (VAL) = OP(PD); \ + break; \ + } \ + if (delay_us) \ + { \ + rt_hw_us_delay(delay_us); \ + if (timeout_us) \ + { \ + left_ns -= delay_ns; \ + } \ + } \ + rt_hw_cpu_relax(); \ + if (timeout_us) \ + { \ + --left_ns; \ + } \ + } \ + (COND) ? RT_EOK : -RT_ETIMEOUT; \ +}) + +static rt_bool_t rockchip_pmu_domain_is_idle(struct rockchip_pm_domain *rk_domain) +{ + rt_uint32_t val; + struct rockchip_pmu *pmu = rk_domain->pmu; + const struct rockchip_domain_info *domain_info = rk_domain->info; + + rt_syscon_read(pmu->regmap, pmu->info->idle_offset, &val); + + return (val & domain_info->idle_mask) == domain_info->idle_mask; +} + +static rt_uint32_t rockchip_pmu_read_ack(struct rockchip_pmu *pmu) +{ + rt_uint32_t val; + + rt_syscon_read(pmu->regmap, pmu->info->ack_offset, &val); + + return val; +} + +static rt_err_t rockchip_pmu_set_idle_request(struct rockchip_pm_domain *rk_domain, + rt_bool_t idle) +{ + rt_err_t err; + rt_bool_t is_idle; + rt_uint32_t val, target_ack, domain_req_offset; + struct rockchip_pmu *pmu = rk_domain->pmu; + const struct rockchip_domain_info *domain_info = rk_domain->info; + + domain_req_offset = domain_info->req_offset; + + if (domain_info->req_mask == 0) + { + return RT_EOK; + } + else if (domain_info->req_w_mask) + { + rt_syscon_write(pmu->regmap, pmu->info->req_offset + domain_req_offset, + idle ? (domain_info->req_mask | domain_info->req_w_mask) : + domain_info->req_w_mask); + } + else + { + rt_syscon_update_bits(pmu->regmap, pmu->info->req_offset + domain_req_offset, + domain_info->req_mask, idle ? -1U : 0); + } + + rt_hw_wmb(); + + /* Wait util idle_ack = 1 */ + target_ack = idle ? domain_info->ack_mask : 0; + err = read_uxx_poll_timeout(rockchip_pmu_read_ack, pmu, val, + (val & domain_info->ack_mask) == target_ack, 0, 10000); + + if (err) + { + LOG_E("Failed to get ack on domain '%s' = %d", + rt_ofw_node_full_name(rk_domain->np), val); + return err; + } + + err = read_uxx_poll_timeout(rockchip_pmu_domain_is_idle, rk_domain, + is_idle, is_idle == idle, 0, 10000); + if (err) + { + LOG_E("Failed to set idle on domain '%s' = %d", + rt_ofw_node_full_name(rk_domain->np), is_idle); + return err; + } + + return RT_EOK; +} + +static void rockchip_pmu_save_qos(struct rockchip_pm_domain *rk_domain) +{ + for (int i = 0; i < rk_domain->num_qos; ++i) + { + rt_syscon_read(rk_domain->qos_regmaps[i], QOS_PRIORITY, + &rk_domain->qos_save_regs[0][i]); + rt_syscon_read(rk_domain->qos_regmaps[i], QOS_MODE, + &rk_domain->qos_save_regs[1][i]); + rt_syscon_read(rk_domain->qos_regmaps[i], QOS_BANDWIDTH, + &rk_domain->qos_save_regs[2][i]); + rt_syscon_read(rk_domain->qos_regmaps[i], QOS_SATURATION, + &rk_domain->qos_save_regs[3][i]); + rt_syscon_read(rk_domain->qos_regmaps[i], QOS_EXTCONTROL, + &rk_domain->qos_save_regs[4][i]); + } +} + +static void rockchip_pmu_restore_qos(struct rockchip_pm_domain *rk_domain) +{ + for (int i = 0; i < rk_domain->num_qos; ++i) + { + rt_syscon_write(rk_domain->qos_regmaps[i], QOS_PRIORITY, + rk_domain->qos_save_regs[0][i]); + rt_syscon_write(rk_domain->qos_regmaps[i], QOS_MODE, + rk_domain->qos_save_regs[1][i]); + rt_syscon_write(rk_domain->qos_regmaps[i], QOS_BANDWIDTH, + rk_domain->qos_save_regs[2][i]); + rt_syscon_write(rk_domain->qos_regmaps[i], QOS_SATURATION, + rk_domain->qos_save_regs[3][i]); + rt_syscon_write(rk_domain->qos_regmaps[i], QOS_EXTCONTROL, + rk_domain->qos_save_regs[4][i]); + } +} + +static rt_bool_t rockchip_pmu_domain_is_on(struct rockchip_pm_domain *rk_domain) +{ + rt_uint32_t val; + struct rockchip_pmu *pmu = rk_domain->pmu; + + if (rk_domain->info->repair_status_mask) + { + rt_syscon_read(pmu->regmap, pmu->info->repair_status_offset, &val); + + /* 1'b1: power on, 1'b0: power off */ + return val & rk_domain->info->repair_status_mask; + } + + /* check idle status for idle-only domains */ + if (rk_domain->info->status_mask == 0) + { + return !rockchip_pmu_domain_is_idle(rk_domain); + } + + rt_syscon_read(pmu->regmap, pmu->info->status_offset, &val); + + /* 1'b0: power on, 1'b1: power off */ + return !(val & rk_domain->info->status_mask); +} + +static bool rockchip_pmu_domain_is_mem_on(struct rockchip_pm_domain *rk_domain) +{ + rt_uint32_t val; + struct rockchip_pmu *pmu = rk_domain->pmu; + + rt_syscon_read(pmu->regmap, pmu->info->mem_status_offset + rk_domain->info->mem_offset, &val); + + /* 1'b0: power on, 1'b1: power off */ + return !(val & rk_domain->info->mem_status_mask); +} + +static rt_bool_t rockchip_pmu_domain_is_chain_on(struct rockchip_pm_domain *rk_domain) +{ + rt_uint32_t val; + struct rockchip_pmu *pmu = rk_domain->pmu; + + rt_syscon_read(pmu->regmap, pmu->info->chain_status_offset + rk_domain->info->mem_offset, &val); + + /* 1'b1: power on, 1'b0: power off */ + return val & rk_domain->info->mem_status_mask; +} + +static rt_err_t rockchip_pmu_domain_mem_reset(struct rockchip_pm_domain *rk_domain) +{ + rt_bool_t is_on; + rt_err_t err = 0; + struct rockchip_pmu *pmu = rk_domain->pmu; + + err = read_uxx_poll_timeout(rockchip_pmu_domain_is_chain_on, rk_domain, + is_on, is_on == RT_TRUE, 0, 10000); + + if (err) + { + LOG_E("Failed to get chain status '%s', target_on = %s, is %s", + rt_ofw_node_full_name(rk_domain->np), "1", is_on ? "on" : "off"); + goto _err; + } + + rt_hw_us_delay(20); + + rt_syscon_write(pmu->regmap, pmu->info->mem_pwr_offset + rk_domain->info->pwr_offset, + (rk_domain->info->pwr_mask | rk_domain->info->pwr_w_mask)); + rt_hw_wmb(); + + err = read_uxx_poll_timeout(rockchip_pmu_domain_is_mem_on, rk_domain, + is_on, is_on == RT_FALSE, 0, 10000); + + if (err) + { + LOG_E("Failed to get mem status '%s', target_on = %s, is %s", + rt_ofw_node_full_name(rk_domain->np), "0", is_on ? "on" : "off"); + goto _err; + } + + rt_syscon_write(pmu->regmap, pmu->info->mem_pwr_offset + rk_domain->info->pwr_offset, + rk_domain->info->pwr_w_mask); + rt_hw_wmb(); + + err = read_uxx_poll_timeout(rockchip_pmu_domain_is_mem_on, rk_domain, + is_on, is_on == RT_TRUE, 0, 10000); + + if (err) + { + LOG_E("Failed to get mem status '%s', target_on = %s, is %s", + rt_ofw_node_full_name(rk_domain->np), "1", is_on ? "on" : "off"); + } + +_err: + return err; +} + +static void rockchip_do_pmu_set_power_domain(struct rockchip_pm_domain *rk_domain, + rt_bool_t on) +{ + rt_uint32_t pd_pwr_offset; + rt_bool_t is_on, is_mem_on = RT_FALSE; + struct rockchip_pmu *pmu = rk_domain->pmu; + + pd_pwr_offset = rk_domain->info->pwr_offset; + + if (rk_domain->info->pwr_mask == 0) + { + return; + } + + if (on && rk_domain->info->mem_status_mask) + { + is_mem_on = rockchip_pmu_domain_is_mem_on(rk_domain); + } + + if (rk_domain->info->pwr_w_mask) + { + rt_syscon_write(pmu->regmap, pmu->info->pwr_offset + pd_pwr_offset, + on ? rk_domain->info->pwr_w_mask : + (rk_domain->info->pwr_mask | rk_domain->info->pwr_w_mask)); + } + else + { + rt_syscon_update_bits(pmu->regmap, pmu->info->pwr_offset + pd_pwr_offset, + rk_domain->info->pwr_mask, on ? 0 : -1U); + } + + rt_hw_wmb(); + + if (is_mem_on && rockchip_pmu_domain_mem_reset(rk_domain)) + { + return; + } + + if (read_uxx_poll_timeout(rockchip_pmu_domain_is_on, rk_domain, + is_on, is_on == on, 0, 10000)) + { + LOG_E("Failed to set domain '%s', is %s", + rt_ofw_node_full_name(rk_domain->np), is_on ? "on" : "off"); + return; + } +} + +static rt_err_t rockchip_pm_domain_power(struct rockchip_pm_domain *rk_pmdomain, + rt_bool_t power_on) +{ + rt_err_t err = RT_EOK; + + if (rockchip_pmu_domain_is_on(rk_pmdomain) != power_on) + { + if (rk_pmdomain->clk_arr) + { + if ((err = rt_clk_array_enable(rk_pmdomain->clk_arr))) + { + LOG_E("Failed to enable clocks"); + + return err; + } + } + + if (!power_on) + { + rockchip_pmu_save_qos(rk_pmdomain); + + /* if powering down, idle request to NIU first */ + rockchip_pmu_set_idle_request(rk_pmdomain, RT_TRUE); + } + + rockchip_do_pmu_set_power_domain(rk_pmdomain, power_on); + + if (power_on) + { + /* if powering up, leave idle mode */ + rockchip_pmu_set_idle_request(rk_pmdomain, RT_FALSE); + + rockchip_pmu_restore_qos(rk_pmdomain); + } + + if (rk_pmdomain->clk_arr) + { + rt_clk_array_disable(rk_pmdomain->clk_arr); + } + } + + return err; +} + +static rt_err_t rockchip_pd_power_on(struct rt_dm_power_domain *domain) +{ + struct rockchip_pm_domain *rk_pmdomain = raw_to_rockchip_pm_domain(domain); + + return rockchip_pm_domain_power(rk_pmdomain, RT_TRUE); +} + +static rt_err_t rockchip_pd_power_off(struct rt_dm_power_domain *domain) +{ + struct rockchip_pm_domain *rk_pmdomain = raw_to_rockchip_pm_domain(domain); + + return rockchip_pm_domain_power(rk_pmdomain, RT_FALSE); +} + +static void rockchip_pm_remove_one_domain(struct rockchip_pm_domain *rk_domain) +{ + if (rk_domain->pmu) + { + rt_dm_power_domain_unregister(&rk_domain->parent); + rk_domain->pmu = RT_NULL; + } + + for (int i = 0; i < MAX_QOS_REGS_NUM; ++i) + { + if (rk_domain->qos_save_regs[i]) + { + rt_free(rk_domain->qos_save_regs[i]); + rk_domain->qos_save_regs[i] = RT_NULL; + } + } + + if (rk_domain->qos_regmaps) + { + rt_free(rk_domain->qos_regmaps); + rk_domain->qos_regmaps = RT_NULL; + } + + if (rk_domain->clk_arr) + { + rt_clk_array_unprepare(rk_domain->clk_arr); + rt_clk_array_put(rk_domain->clk_arr); + + rk_domain->clk_arr = RT_NULL; + } +} + +static rt_err_t rockchip_pm_add_one_domain(struct rockchip_pmu *pmu, + struct rt_ofw_node *np) +{ + rt_err_t err; + rt_uint32_t idx; + struct rt_dm_power_domain *domain; + struct rockchip_pm_domain *rk_domain; + + if ((err = rt_ofw_prop_read_u32(np, "reg", &idx))) + { + LOG_E("Failed to read %s domain id (reg) error = %s", + rt_ofw_node_full_name(np), rt_strerror(err)); + return err; + } + + rk_domain = &pmu->rk_pmdomains[idx]; + + /* RK3588 has domains with two parents (RKVDEC0/RKVDEC1) */ + if (rk_domain->pmu) + { + return RT_EOK; + } + + rk_domain->num_qos = rt_ofw_count_phandle_cells(np, "pm_qos", RT_NULL); + + if (rk_domain->num_qos > 0) + { + rk_domain->qos_regmaps = rt_calloc(rk_domain->num_qos, + sizeof(*rk_domain->qos_regmaps)); + + if (!rk_domain->qos_regmaps) + { + return -RT_ENOMEM; + } + + for (int i = 0; i < MAX_QOS_REGS_NUM; ++i) + { + rk_domain->qos_save_regs[i] = rt_calloc(rk_domain->num_qos, + sizeof(rt_uint32_t)); + + if (!rk_domain->qos_save_regs[i]) + { + err = -RT_ENOMEM; + goto _free; + } + } + + for (int i = 0; i < rk_domain->num_qos; ++i) + { + struct rt_ofw_node *qos_np = rt_ofw_parse_phandle(np, "pm_qos", i); + + if (!qos_np) + { + err = -RT_EIO; + goto _free; + } + + if (!(rk_domain->qos_regmaps[i] = rt_syscon_find_by_ofw_node(qos_np))) + { + rt_ofw_node_put(qos_np); + err = -RT_EIO; + + goto _free; + } + + rt_ofw_node_put(qos_np); + } + } + + rk_domain->clk_arr = rt_ofw_get_clk_array(np); + + if (rt_is_err(rk_domain->clk_arr)) + { + LOG_E("Failed to %s %s CLKs", "read", rt_ofw_node_full_name(np)); + err = rt_ptr_err(rk_domain->clk_arr); + + goto _free; + } + + if (!rk_domain->clk_arr) + { + goto _out_clk; + } + + if ((err = rt_clk_array_prepare(rk_domain->clk_arr))) + { + LOG_E("Failed to %s %s CLKs", "prepare", rt_ofw_node_full_name(np)); + goto _free; + } + +_out_clk: + domain = &rk_domain->parent; + domain->power_off = rockchip_pd_power_off; + domain->power_on = rockchip_pd_power_on; + + if ((err = rt_dm_power_domain_register(domain))) + { + goto _free; + } + + /* OK flag */ + rk_domain->info = pmu->info->domain_info; + rk_domain->pmu = pmu; + rk_domain->np = np; + + rt_ofw_data(np) = &rk_domain->parent; + + return RT_EOK; + +_free: + rockchip_pm_remove_one_domain(rk_domain); + + return err; +} + +static rt_err_t rockchip_pm_add_subdomain(struct rockchip_pmu *pmu, + struct rt_ofw_node *parent) +{ + rt_err_t err; + rt_uint32_t idx; + struct rt_ofw_node *np; + struct rockchip_pm_domain *child_rk_domain, *parent_rk_domain; + + if ((err = rt_ofw_prop_read_u32(parent, "reg", &idx))) + { + LOG_E("Failed to read %s domain id (reg) error = %s", + rt_ofw_node_full_name(parent), rt_strerror(err)); + return err; + } + + parent_rk_domain = &pmu->rk_pmdomains[idx]; + + rt_ofw_foreach_child_node(parent, np) + { + if ((err = rockchip_pm_add_one_domain(pmu, np))) + { + LOG_E("Failed to handle node %s error = %s", + rt_ofw_node_full_name(np), rt_strerror(err)); + goto _err; + } + + if ((err = rt_ofw_prop_read_u32(np, "reg", &idx))) + { + LOG_E("Failed to read %s domain id (reg) error = %s", + rt_ofw_node_full_name(np), rt_strerror(err)); + goto _err; + } + + child_rk_domain = &pmu->rk_pmdomains[idx]; + + if ((err = rt_dm_power_domain_register_child(&parent_rk_domain->parent, + &child_rk_domain->parent))) + { + LOG_E("Failed to handle subdomain node %s error = %s", + rt_ofw_node_full_name(np), rt_strerror(err)); + } + + rockchip_pm_add_subdomain(pmu, np); + } + + return RT_EOK; + +_err: + rt_ofw_node_put(np); + + return err; +} + +static void rockchip_pmdomain_conf_cnt(struct rockchip_pmu *pmu, + rt_uint32_t domain_reg_offset, int count) +{ + /* 1. configure domain power down transition count */ + rt_syscon_write(pmu->regmap, domain_reg_offset, count); + + /* 2. power up count. */ + rt_syscon_write(pmu->regmap, domain_reg_offset + 4, count); +} + +static struct rt_dm_power_domain *rockchip_pm_domain_proxy_ofw_parse( + struct rt_dm_power_domain_proxy *proxy, struct rt_ofw_cell_args *args) +{ + struct rockchip_pmu *pmu = raw_to_rockchip_pmu(proxy); + + return &pmu->rk_pmdomains[args->args[0]].parent; +} + +static rt_err_t rockchip_pmdomain_probe(struct rt_platform_device *pdev) +{ + rt_err_t err; + struct rockchip_pmu *pmu; + struct rt_ofw_node *np, *grf_np, *child; + const struct rockchip_pmu_info *pmu_info = pdev->id->data; + + pmu = rt_calloc(1, sizeof(*pmu) + sizeof(pmu->rk_pmdomains[0]) * pmu_info->num_domains); + + if (!pmu) + { + return -RT_ENOMEM; + } + + np = pdev->parent.ofw_node; + grf_np = rt_ofw_get_parent(np); + + pmu->regmap = rt_syscon_find_by_ofw_node(grf_np); + + rt_ofw_node_put(grf_np); + + if (!pmu->regmap) + { + err = -RT_EIO; + + goto _fail; + } + + pmu->info = pmu_info; + + /* Configure power up and down transition delays for CORE and GPU domains. */ + if (pmu_info->core_power_transition_time) + { + rockchip_pmdomain_conf_cnt(pmu, pmu_info->core_pwrcnt_offset, + pmu_info->core_power_transition_time); + } + + if (pmu_info->gpu_pwrcnt_offset) + { + rockchip_pmdomain_conf_cnt(pmu, pmu_info->gpu_pwrcnt_offset, + pmu_info->gpu_power_transition_time); + } + + rt_ofw_foreach_available_child_node(np, child) + { + if ((err = rockchip_pm_add_one_domain(pmu, child))) + { + LOG_E("Failed to handle node %s error = %s", + rt_ofw_node_full_name(child), rt_strerror(err)); + + rt_ofw_node_put(child); + goto _free_domain; + } + + if ((err = rockchip_pm_add_subdomain(pmu, child))) + { + LOG_E("Failed to handle subdomain node %s error = %s", + rt_ofw_node_full_name(child), rt_strerror(err)); + + rt_ofw_node_put(child); + goto _free_domain; + } + } + + pmu->parent.ofw_parse = rockchip_pm_domain_proxy_ofw_parse; + rt_dm_power_domain_proxy_ofw_bind(&pmu->parent, np); + + return RT_EOK; + +_free_domain: + for (int i = 0; i < pmu_info->num_domains; ++i) + { + struct rockchip_pm_domain *rk_domain; + + rk_domain = raw_to_rockchip_pm_domain(&pmu->rk_pmdomains[i]); + + if (rk_domain->pmu) + { + rockchip_pm_remove_one_domain(rk_domain); + } + } + +_fail: + rt_free(pmu); + + return err; +} + +#define DOMAIN_PX30(name, pwr, status, req, wakeup) DOMAIN_M(name, pwr, status, req, (req) << 16, req, wakeup) + +static const struct rockchip_domain_info px30_pm_domains[] = +{ + [PX30_PD_USB] = DOMAIN_PX30("usb", RT_BIT(5), RT_BIT(5), RT_BIT(10), RT_FALSE), + [PX30_PD_SDCARD] = DOMAIN_PX30("sdcard", RT_BIT(8), RT_BIT(8), RT_BIT(9), RT_FALSE), + [PX30_PD_GMAC] = DOMAIN_PX30("gmac", RT_BIT(10), RT_BIT(10), RT_BIT(6), RT_FALSE), + [PX30_PD_MMC_NAND] = DOMAIN_PX30("mmc_nand", RT_BIT(11), RT_BIT(11), RT_BIT(5), RT_FALSE), + [PX30_PD_VPU] = DOMAIN_PX30("vpu", RT_BIT(12), RT_BIT(12), RT_BIT(14), RT_FALSE), + [PX30_PD_VO] = DOMAIN_PX30("vo", RT_BIT(13), RT_BIT(13), RT_BIT(7), RT_FALSE), + [PX30_PD_VI] = DOMAIN_PX30("vi", RT_BIT(14), RT_BIT(14), RT_BIT(8), RT_FALSE), + [PX30_PD_GPU] = DOMAIN_PX30("gpu", RT_BIT(15), RT_BIT(15), RT_BIT(2), RT_FALSE), +}; + +static const struct rockchip_pmu_info px30_pmu = +{ + .pwr_offset = 0x18, + .status_offset = 0x20, + .req_offset = 0x64, + .idle_offset = 0x6c, + .ack_offset = 0x6c, + + .num_domains = RT_ARRAY_SIZE(px30_pm_domains), + .domain_info = px30_pm_domains, +}; + +#define DOMAIN_RK3036(name, req, ack, idle, wakeup) DOMAIN_M(name, 0, 0, req, idle, ack, wakeup) + +static const struct rockchip_domain_info rk3036_pm_domains[] = +{ + [RK3036_PD_MSCH] = DOMAIN_RK3036("msch", RT_BIT(14), RT_BIT(23), RT_BIT(30), RT_TRUE), + [RK3036_PD_CORE] = DOMAIN_RK3036("core", RT_BIT(13), RT_BIT(17), RT_BIT(24), RT_FALSE), + [RK3036_PD_PERI] = DOMAIN_RK3036("peri", RT_BIT(12), RT_BIT(18), RT_BIT(25), RT_FALSE), + [RK3036_PD_VIO] = DOMAIN_RK3036("vio", RT_BIT(11), RT_BIT(19), RT_BIT(26), RT_FALSE), + [RK3036_PD_VPU] = DOMAIN_RK3036("vpu", RT_BIT(10), RT_BIT(20), RT_BIT(27), RT_FALSE), + [RK3036_PD_GPU] = DOMAIN_RK3036("gpu", RT_BIT(9), RT_BIT(21), RT_BIT(28), RT_FALSE), + [RK3036_PD_SYS] = DOMAIN_RK3036("sys", RT_BIT(8), RT_BIT(22), RT_BIT(29), RT_FALSE), +}; + +static const struct rockchip_pmu_info rk3036_pmu = +{ + .req_offset = 0x148, + .idle_offset = 0x14c, + .ack_offset = 0x14c, + + .num_domains = RT_ARRAY_SIZE(rk3036_pm_domains), + .domain_info = rk3036_pm_domains, +}; + +static const struct rockchip_domain_info rk3066_pm_domains[] = +{ + [RK3066_PD_GPU] = DOMAIN("gpu", RT_BIT(9), RT_BIT(9), RT_BIT(3), RT_BIT(24), RT_BIT(29), RT_FALSE), + [RK3066_PD_VIDEO] = DOMAIN("video", RT_BIT(8), RT_BIT(8), RT_BIT(4), RT_BIT(23), RT_BIT(28), RT_FALSE), + [RK3066_PD_VIO] = DOMAIN("vio", RT_BIT(7), RT_BIT(7), RT_BIT(5), RT_BIT(22), RT_BIT(27), RT_FALSE), + [RK3066_PD_PERI] = DOMAIN("peri", RT_BIT(6), RT_BIT(6), RT_BIT(2), RT_BIT(25), RT_BIT(30), RT_FALSE), + [RK3066_PD_CPU] = DOMAIN("cpu", 0, RT_BIT(5), RT_BIT(1), RT_BIT(26), RT_BIT(31), RT_FALSE), +}; + +static const struct rockchip_pmu_info rk3066_pmu = +{ + .pwr_offset = 0x08, + .status_offset = 0x0c, + .req_offset = 0x38, /* PMU_MISC_CON1 */ + .idle_offset = 0x0c, + .ack_offset = 0x0c, + + .num_domains = RT_ARRAY_SIZE(rk3066_pm_domains), + .domain_info = rk3066_pm_domains, +}; + +#define DOMAIN_RK3288(name, pwr, status, req, wakeup) DOMAIN(name, pwr, status, req, req, (req) << 16, wakeup) + +static const struct rockchip_domain_info rk3128_pm_domains[] = +{ + [RK3128_PD_CORE] = DOMAIN_RK3288("core", RT_BIT(0), RT_BIT(0), RT_BIT(4), RT_FALSE), + [RK3128_PD_MSCH] = DOMAIN_RK3288("msch", 0, 0, RT_BIT(6), RT_TRUE), + [RK3128_PD_VIO] = DOMAIN_RK3288("vio", RT_BIT(3), RT_BIT(3), RT_BIT(2), RT_FALSE), + [RK3128_PD_VIDEO] = DOMAIN_RK3288("video", RT_BIT(2), RT_BIT(2), RT_BIT(1), RT_FALSE), + [RK3128_PD_GPU] = DOMAIN_RK3288("gpu", RT_BIT(1), RT_BIT(1), RT_BIT(3), RT_FALSE), +}; + +static const struct rockchip_pmu_info rk3128_pmu = +{ + .pwr_offset = 0x04, + .status_offset = 0x08, + .req_offset = 0x0c, + .idle_offset = 0x10, + .ack_offset = 0x10, + + .num_domains = RT_ARRAY_SIZE(rk3128_pm_domains), + .domain_info = rk3128_pm_domains, +}; + +static const struct rockchip_domain_info rk3188_pm_domains[] = +{ + [RK3188_PD_GPU] = DOMAIN("gpu", RT_BIT(9), RT_BIT(9), RT_BIT(3), RT_BIT(24), RT_BIT(29), RT_FALSE), + [RK3188_PD_VIDEO] = DOMAIN("video", RT_BIT(8), RT_BIT(8), RT_BIT(4), RT_BIT(23), RT_BIT(28), RT_FALSE), + [RK3188_PD_VIO] = DOMAIN("vio", RT_BIT(7), RT_BIT(7), RT_BIT(5), RT_BIT(22), RT_BIT(27), RT_FALSE), + [RK3188_PD_PERI] = DOMAIN("peri", RT_BIT(6), RT_BIT(6), RT_BIT(2), RT_BIT(25), RT_BIT(30), RT_FALSE), + [RK3188_PD_CPU] = DOMAIN("cpu", RT_BIT(5), RT_BIT(5), RT_BIT(1), RT_BIT(26), RT_BIT(31), RT_FALSE), +}; + +static const struct rockchip_pmu_info rk3188_pmu = +{ + .pwr_offset = 0x08, + .status_offset = 0x0c, + .req_offset = 0x38, /* PMU_MISC_CON1 */ + .idle_offset = 0x0c, + .ack_offset = 0x0c, + + .num_domains = RT_ARRAY_SIZE(rk3188_pm_domains), + .domain_info = rk3188_pm_domains, +}; + +static const struct rockchip_domain_info rk3228_pm_domains[] = +{ + [RK3228_PD_CORE] = DOMAIN_RK3036("core", RT_BIT(0), RT_BIT(0), RT_BIT(16), RT_TRUE), + [RK3228_PD_MSCH] = DOMAIN_RK3036("msch", RT_BIT(1), RT_BIT(1), RT_BIT(17), RT_TRUE), + [RK3228_PD_BUS] = DOMAIN_RK3036("bus", RT_BIT(2), RT_BIT(2), RT_BIT(18), RT_TRUE), + [RK3228_PD_SYS] = DOMAIN_RK3036("sys", RT_BIT(3), RT_BIT(3), RT_BIT(19), RT_TRUE), + [RK3228_PD_VIO] = DOMAIN_RK3036("vio", RT_BIT(4), RT_BIT(4), RT_BIT(20), RT_FALSE), + [RK3228_PD_VOP] = DOMAIN_RK3036("vop", RT_BIT(5), RT_BIT(5), RT_BIT(21), RT_FALSE), + [RK3228_PD_VPU] = DOMAIN_RK3036("vpu", RT_BIT(6), RT_BIT(6), RT_BIT(22), RT_FALSE), + [RK3228_PD_RKVDEC] = DOMAIN_RK3036("vdec", RT_BIT(7), RT_BIT(7), RT_BIT(23), RT_FALSE), + [RK3228_PD_GPU] = DOMAIN_RK3036("gpu", RT_BIT(8), RT_BIT(8), RT_BIT(24), RT_FALSE), + [RK3228_PD_PERI] = DOMAIN_RK3036("peri", RT_BIT(9), RT_BIT(9), RT_BIT(25), RT_TRUE), + [RK3228_PD_GMAC] = DOMAIN_RK3036("gmac", RT_BIT(10), RT_BIT(10), RT_BIT(26), RT_FALSE), +}; + +static const struct rockchip_pmu_info rk3228_pmu = +{ + .req_offset = 0x40c, + .idle_offset = 0x488, + .ack_offset = 0x488, + + .num_domains = RT_ARRAY_SIZE(rk3228_pm_domains), + .domain_info = rk3228_pm_domains, +}; + +static const struct rockchip_domain_info rk3288_pm_domains[] = +{ + [RK3288_PD_VIO] = DOMAIN_RK3288("vio", RT_BIT(7), RT_BIT(7), RT_BIT(4), RT_FALSE), + [RK3288_PD_HEVC] = DOMAIN_RK3288("hevc", RT_BIT(14), RT_BIT(10), RT_BIT(9), RT_FALSE), + [RK3288_PD_VIDEO] = DOMAIN_RK3288("video", RT_BIT(8), RT_BIT(8), RT_BIT(3), RT_FALSE), + [RK3288_PD_GPU] = DOMAIN_RK3288("gpu", RT_BIT(9), RT_BIT(9), RT_BIT(2), RT_FALSE), +}; + +static const struct rockchip_pmu_info rk3288_pmu = +{ + .pwr_offset = 0x08, + .status_offset = 0x0c, + .req_offset = 0x10, + .idle_offset = 0x14, + .ack_offset = 0x14, + + .core_pwrcnt_offset = 0x34, + .gpu_pwrcnt_offset = 0x3c, + + .core_power_transition_time = 24, /* 1us */ + .gpu_power_transition_time = 24, /* 1us */ + + .num_domains = RT_ARRAY_SIZE(rk3288_pm_domains), + .domain_info = rk3288_pm_domains, +}; + +#define DOMAIN_RK3328(name, pwr, status, req, wakeup) DOMAIN_M(name, pwr, pwr, req, (req) << 10, req, wakeup) + +static const struct rockchip_domain_info rk3328_pm_domains[] = +{ + [RK3328_PD_CORE] = DOMAIN_RK3328("core", 0, RT_BIT(0), RT_BIT(0), RT_FALSE), + [RK3328_PD_GPU] = DOMAIN_RK3328("gpu", 0, RT_BIT(1), RT_BIT(1), RT_FALSE), + [RK3328_PD_BUS] = DOMAIN_RK3328("bus", 0, RT_BIT(2), RT_BIT(2), RT_TRUE), + [RK3328_PD_MSCH] = DOMAIN_RK3328("msch", 0, RT_BIT(3), RT_BIT(3), RT_TRUE), + [RK3328_PD_PERI] = DOMAIN_RK3328("peri", 0, RT_BIT(4), RT_BIT(4), RT_TRUE), + [RK3328_PD_VIDEO] = DOMAIN_RK3328("video", 0, RT_BIT(5), RT_BIT(5), RT_FALSE), + [RK3328_PD_HEVC] = DOMAIN_RK3328("hevc", 0, RT_BIT(6), RT_BIT(6), RT_FALSE), + [RK3328_PD_VIO] = DOMAIN_RK3328("vio", 0, RT_BIT(8), RT_BIT(8), RT_FALSE), + [RK3328_PD_VPU] = DOMAIN_RK3328("vpu", 0, RT_BIT(9), RT_BIT(9), RT_FALSE), +}; + +static const struct rockchip_pmu_info rk3328_pmu = +{ + .req_offset = 0x414, + .idle_offset = 0x484, + .ack_offset = 0x484, + + .num_domains = RT_ARRAY_SIZE(rk3328_pm_domains), + .domain_info = rk3328_pm_domains, +}; + +#define DOMAIN_RK3368(name, pwr, status, req, wakeup) DOMAIN(name, pwr, status, req, (req) << 16, req, wakeup) + +static const struct rockchip_domain_info rk3366_pm_domains[] = +{ + [RK3366_PD_PERI] = DOMAIN_RK3368("peri", RT_BIT(10), RT_BIT(10), RT_BIT(6), RT_TRUE), + [RK3366_PD_VIO] = DOMAIN_RK3368("vio", RT_BIT(14), RT_BIT(14), RT_BIT(8), RT_FALSE), + [RK3366_PD_VIDEO] = DOMAIN_RK3368("video", RT_BIT(13), RT_BIT(13), RT_BIT(7), RT_FALSE), + [RK3366_PD_RKVDEC] = DOMAIN_RK3368("vdec", RT_BIT(11), RT_BIT(11), RT_BIT(7), RT_FALSE), + [RK3366_PD_WIFIBT] = DOMAIN_RK3368("wifibt", RT_BIT(8), RT_BIT(8), RT_BIT(9), RT_FALSE), + [RK3366_PD_VPU] = DOMAIN_RK3368("vpu", RT_BIT(12), RT_BIT(12), RT_BIT(7), RT_FALSE), + [RK3366_PD_GPU] = DOMAIN_RK3368("gpu", RT_BIT(15), RT_BIT(15), RT_BIT(2), RT_FALSE), +}; + +static const struct rockchip_pmu_info rk3366_pmu = +{ + .pwr_offset = 0x0c, + .status_offset = 0x10, + .req_offset = 0x3c, + .idle_offset = 0x40, + .ack_offset = 0x40, + + .core_pwrcnt_offset = 0x48, + .gpu_pwrcnt_offset = 0x50, + + .core_power_transition_time = 24, + .gpu_power_transition_time = 24, + + .num_domains = RT_ARRAY_SIZE(rk3366_pm_domains), + .domain_info = rk3366_pm_domains, +}; + +static const struct rockchip_domain_info rk3368_pm_domains[] = +{ + [RK3368_PD_PERI] = DOMAIN_RK3368("peri", RT_BIT(13), RT_BIT(12), RT_BIT(6), RT_TRUE), + [RK3368_PD_VIO] = DOMAIN_RK3368("vio", RT_BIT(15), RT_BIT(14), RT_BIT(8), RT_FALSE), + [RK3368_PD_VIDEO] = DOMAIN_RK3368("video", RT_BIT(14), RT_BIT(13), RT_BIT(7), RT_FALSE), + [RK3368_PD_GPU_0] = DOMAIN_RK3368("gpu_0", RT_BIT(16), RT_BIT(15), RT_BIT(2), RT_FALSE), + [RK3368_PD_GPU_1] = DOMAIN_RK3368("gpu_1", RT_BIT(17), RT_BIT(16), RT_BIT(2), RT_FALSE), +}; + +static const struct rockchip_pmu_info rk3368_pmu = +{ + .pwr_offset = 0x0c, + .status_offset = 0x10, + .req_offset = 0x3c, + .idle_offset = 0x40, + .ack_offset = 0x40, + + .core_pwrcnt_offset = 0x48, + .gpu_pwrcnt_offset = 0x50, + + .core_power_transition_time = 24, + .gpu_power_transition_time = 24, + + .num_domains = RT_ARRAY_SIZE(rk3368_pm_domains), + .domain_info = rk3368_pm_domains, +}; + +#define DOMAIN_RK3399(name, pwr, status, req, wakeup) DOMAIN(name, pwr, status, req, req, req, wakeup) + +static const struct rockchip_domain_info rk3399_pm_domains[] = +{ + [RK3399_PD_TCPD0] = DOMAIN_RK3399("tcpd0", RT_BIT(8), RT_BIT(8), 0, RT_FALSE), + [RK3399_PD_TCPD1] = DOMAIN_RK3399("tcpd1", RT_BIT(9), RT_BIT(9), 0, RT_FALSE), + [RK3399_PD_CCI] = DOMAIN_RK3399("cci", RT_BIT(10), RT_BIT(10), 0, RT_TRUE), + [RK3399_PD_CCI0] = DOMAIN_RK3399("cci0", 0, 0, RT_BIT(15), RT_TRUE), + [RK3399_PD_CCI1] = DOMAIN_RK3399("cci1", 0, 0, RT_BIT(16), RT_TRUE), + [RK3399_PD_PERILP] = DOMAIN_RK3399("perilp", RT_BIT(11), RT_BIT(11), RT_BIT(1), RT_TRUE), + [RK3399_PD_PERIHP] = DOMAIN_RK3399("perihp", RT_BIT(12), RT_BIT(12), RT_BIT(2), RT_TRUE), + [RK3399_PD_CENTER] = DOMAIN_RK3399("center", RT_BIT(13), RT_BIT(13), RT_BIT(14), RT_TRUE), + [RK3399_PD_VIO] = DOMAIN_RK3399("vio", RT_BIT(14), RT_BIT(14), RT_BIT(17), RT_FALSE), + [RK3399_PD_GPU] = DOMAIN_RK3399("gpu", RT_BIT(15), RT_BIT(15), RT_BIT(0), RT_FALSE), + [RK3399_PD_VCODEC] = DOMAIN_RK3399("vcodec", RT_BIT(16), RT_BIT(16), RT_BIT(3), RT_FALSE), + [RK3399_PD_VDU] = DOMAIN_RK3399("vdu", RT_BIT(17), RT_BIT(17), RT_BIT(4), RT_FALSE), + [RK3399_PD_RGA] = DOMAIN_RK3399("rga", RT_BIT(18), RT_BIT(18), RT_BIT(5), RT_FALSE), + [RK3399_PD_IEP] = DOMAIN_RK3399("iep", RT_BIT(19), RT_BIT(19), RT_BIT(6), RT_FALSE), + [RK3399_PD_VO] = DOMAIN_RK3399("vo", RT_BIT(20), RT_BIT(20), 0, RT_FALSE), + [RK3399_PD_VOPB] = DOMAIN_RK3399("vopb", 0, 0, RT_BIT(7), RT_FALSE), + [RK3399_PD_VOPL] = DOMAIN_RK3399("vopl", 0, 0, RT_BIT(8), RT_FALSE), + [RK3399_PD_ISP0] = DOMAIN_RK3399("isp0", RT_BIT(22), RT_BIT(22), RT_BIT(9), RT_FALSE), + [RK3399_PD_ISP1] = DOMAIN_RK3399("isp1", RT_BIT(23), RT_BIT(23), RT_BIT(10), RT_FALSE), + [RK3399_PD_HDCP] = DOMAIN_RK3399("hdcp", RT_BIT(24), RT_BIT(24), RT_BIT(11), RT_FALSE), + [RK3399_PD_GMAC] = DOMAIN_RK3399("gmac", RT_BIT(25), RT_BIT(25), RT_BIT(23), RT_TRUE), + [RK3399_PD_EMMC] = DOMAIN_RK3399("emmc", RT_BIT(26), RT_BIT(26), RT_BIT(24), RT_TRUE), + [RK3399_PD_USB3] = DOMAIN_RK3399("usb3", RT_BIT(27), RT_BIT(27), RT_BIT(12), RT_TRUE), + [RK3399_PD_EDP] = DOMAIN_RK3399("edp", RT_BIT(28), RT_BIT(28), RT_BIT(22), RT_FALSE), + [RK3399_PD_GIC] = DOMAIN_RK3399("gic", RT_BIT(29), RT_BIT(29), RT_BIT(27), RT_TRUE), + [RK3399_PD_SD] = DOMAIN_RK3399("sd", RT_BIT(30), RT_BIT(30), RT_BIT(28), RT_TRUE), + [RK3399_PD_SDIOAUDIO] = DOMAIN_RK3399("sdioaudio", RT_BIT(31), RT_BIT(31), RT_BIT(29), RT_TRUE), +}; + +static const struct rockchip_pmu_info rk3399_pmu = +{ + .pwr_offset = 0x14, + .status_offset = 0x18, + .req_offset = 0x60, + .idle_offset = 0x64, + .ack_offset = 0x68, + + /* ARM Trusted Firmware manages power transition times */ + .num_domains = RT_ARRAY_SIZE(rk3399_pm_domains), + .domain_info = rk3399_pm_domains, +}; + +#define DOMAIN_RK3568(name, pwr, req, wakeup) DOMAIN_M(name, pwr, pwr, req, req, req, wakeup) + +static const struct rockchip_domain_info rk3568_pm_domains[] = +{ + [RK3568_PD_NPU] = DOMAIN_RK3568("npu", RT_BIT(1), RT_BIT(2), RT_FALSE), + [RK3568_PD_GPU] = DOMAIN_RK3568("gpu", RT_BIT(0), RT_BIT(1), RT_FALSE), + [RK3568_PD_VI] = DOMAIN_RK3568("vi", RT_BIT(6), RT_BIT(3), RT_FALSE), + [RK3568_PD_VO] = DOMAIN_RK3568("vo", RT_BIT(7), RT_BIT(4), RT_FALSE), + [RK3568_PD_RGA] = DOMAIN_RK3568("rga", RT_BIT(5), RT_BIT(5), RT_FALSE), + [RK3568_PD_VPU] = DOMAIN_RK3568("vpu", RT_BIT(2), RT_BIT(6), RT_FALSE), + [RK3568_PD_RKVDEC] = DOMAIN_RK3568("vdec", RT_BIT(4), RT_BIT(8), RT_FALSE), + [RK3568_PD_RKVENC] = DOMAIN_RK3568("venc", RT_BIT(3), RT_BIT(7), RT_FALSE), + [RK3568_PD_PIPE] = DOMAIN_RK3568("pipe", RT_BIT(8), RT_BIT(11), RT_FALSE), +}; + +static const struct rockchip_pmu_info rk3568_pmu = +{ + .pwr_offset = 0xa0, + .status_offset = 0x98, + .req_offset = 0x50, + .idle_offset = 0x68, + .ack_offset = 0x60, + + .num_domains = RT_ARRAY_SIZE(rk3568_pm_domains), + .domain_info = rk3568_pm_domains, +}; + +#define DOMAIN_RK3588(name, p_offset, pwr, status, m_offset, m_status, r_status, r_offset, req, idle, wakeup) \ + DOMAIN_M_O_R(name, p_offset, pwr, status, m_offset, m_status, r_status, r_offset, req, idle, idle, wakeup) + +static const struct rockchip_domain_info rk3588_pm_domains[] = +{ + [RK3588_PD_GPU] = DOMAIN_RK3588("gpu", 0x0, RT_BIT(0), 0, 0x0, 0, RT_BIT(1), 0x0, RT_BIT(0), RT_BIT(0), RT_FALSE), + [RK3588_PD_NPU] = DOMAIN_RK3588("npu", 0x0, RT_BIT(1), RT_BIT(1), 0x0, 0, 0, 0x0, 0, 0, RT_FALSE), + [RK3588_PD_VCODEC] = DOMAIN_RK3588("vcodec", 0x0, RT_BIT(2), RT_BIT(2), 0x0, 0, 0, 0x0, 0, 0, RT_FALSE), + [RK3588_PD_NPUTOP] = DOMAIN_RK3588("nputop", 0x0, RT_BIT(3), 0, 0x0, RT_BIT(11), RT_BIT(2), 0x0, RT_BIT(1), RT_BIT(1), RT_FALSE), + [RK3588_PD_NPU1] = DOMAIN_RK3588("npu1", 0x0, RT_BIT(4), 0, 0x0, RT_BIT(12), RT_BIT(3), 0x0, RT_BIT(2), RT_BIT(2), RT_FALSE), + [RK3588_PD_NPU2] = DOMAIN_RK3588("npu2", 0x0, RT_BIT(5), 0, 0x0, RT_BIT(13), RT_BIT(4), 0x0, RT_BIT(3), RT_BIT(3), RT_FALSE), + [RK3588_PD_VENC0] = DOMAIN_RK3588("venc0", 0x0, RT_BIT(6), 0, 0x0, RT_BIT(14), RT_BIT(5), 0x0, RT_BIT(4), RT_BIT(4), RT_FALSE), + [RK3588_PD_VENC1] = DOMAIN_RK3588("venc1", 0x0, RT_BIT(7), 0, 0x0, RT_BIT(15), RT_BIT(6), 0x0, RT_BIT(5), RT_BIT(5), RT_FALSE), + [RK3588_PD_RKVDEC0] = DOMAIN_RK3588("rkvdec0", 0x0, RT_BIT(8), 0, 0x0, RT_BIT(16), RT_BIT(7), 0x0, RT_BIT(6), RT_BIT(6), RT_FALSE), + [RK3588_PD_RKVDEC1] = DOMAIN_RK3588("rkvdec1", 0x0, RT_BIT(9), 0, 0x0, RT_BIT(17), RT_BIT(8), 0x0, RT_BIT(7), RT_BIT(7), RT_FALSE), + [RK3588_PD_VDPU] = DOMAIN_RK3588("vdpu", 0x0, RT_BIT(10), 0, 0x0, RT_BIT(18), RT_BIT(9), 0x0, RT_BIT(8), RT_BIT(8), RT_FALSE), + [RK3588_PD_RGA30] = DOMAIN_RK3588("rga30", 0x0, RT_BIT(11), 0, 0x0, RT_BIT(19), RT_BIT(10), 0x0, 0, 0, RT_FALSE), + [RK3588_PD_AV1] = DOMAIN_RK3588("av1", 0x0, RT_BIT(12), 0, 0x0, RT_BIT(20), RT_BIT(11), 0x0, RT_BIT(9), RT_BIT(9), RT_FALSE), + [RK3588_PD_VI] = DOMAIN_RK3588("vi", 0x0, RT_BIT(13), 0, 0x0, RT_BIT(21), RT_BIT(12), 0x0, RT_BIT(10), RT_BIT(10), RT_FALSE), + [RK3588_PD_FEC] = DOMAIN_RK3588("fec", 0x0, RT_BIT(14), 0, 0x0, RT_BIT(22), RT_BIT(13), 0x0, 0, 0, RT_FALSE), + [RK3588_PD_ISP1] = DOMAIN_RK3588("isp1", 0x0, RT_BIT(15), 0, 0x0, RT_BIT(23), RT_BIT(14), 0x0, RT_BIT(11), RT_BIT(11), RT_FALSE), + [RK3588_PD_RGA31] = DOMAIN_RK3588("rga31", 0x4, RT_BIT(0), 0, 0x0, RT_BIT(24), RT_BIT(15), 0x0, RT_BIT(12), RT_BIT(12), RT_FALSE), + [RK3588_PD_VOP] = DOMAIN_RK3588("vop", 0x4, RT_BIT(1), 0, 0x0, RT_BIT(25), RT_BIT(16), 0x0, RT_BIT(13) | RT_BIT(14), RT_BIT(13) | RT_BIT(14), RT_FALSE), + [RK3588_PD_VO0] = DOMAIN_RK3588("vo0", 0x4, RT_BIT(2), 0, 0x0, RT_BIT(26), RT_BIT(17), 0x0, RT_BIT(15), RT_BIT(15), RT_FALSE), + [RK3588_PD_VO1] = DOMAIN_RK3588("vo1", 0x4, RT_BIT(3), 0, 0x0, RT_BIT(27), RT_BIT(18), 0x4, RT_BIT(0), RT_BIT(16), RT_FALSE), + [RK3588_PD_AUDIO] = DOMAIN_RK3588("audio", 0x4, RT_BIT(4), 0, 0x0, RT_BIT(28), RT_BIT(19), 0x4, RT_BIT(1), RT_BIT(17), RT_FALSE), + [RK3588_PD_PHP] = DOMAIN_RK3588("php", 0x4, RT_BIT(5), 0, 0x0, RT_BIT(29), RT_BIT(20), 0x4, RT_BIT(5), RT_BIT(21), RT_FALSE), + [RK3588_PD_GMAC] = DOMAIN_RK3588("gmac", 0x4, RT_BIT(6), 0, 0x0, RT_BIT(30), RT_BIT(21), 0x0, 0, 0, RT_FALSE), + [RK3588_PD_PCIE] = DOMAIN_RK3588("pcie", 0x4, RT_BIT(7), 0, 0x0, RT_BIT(31), RT_BIT(22), 0x0, 0, 0, RT_TRUE), + [RK3588_PD_NVM] = DOMAIN_RK3588("nvm", 0x4, RT_BIT(8), RT_BIT(24), 0x4, 0, 0, 0x4, RT_BIT(2), RT_BIT(18), RT_FALSE), + [RK3588_PD_NVM0] = DOMAIN_RK3588("nvm0", 0x4, RT_BIT(9), 0, 0x4, RT_BIT(1), RT_BIT(23), 0x0, 0, 0, RT_FALSE), + [RK3588_PD_SDIO] = DOMAIN_RK3588("sdio", 0x4, RT_BIT(10), 0, 0x4, RT_BIT(2), RT_BIT(24), 0x4, RT_BIT(3), RT_BIT(19), RT_FALSE), + [RK3588_PD_USB] = DOMAIN_RK3588("usb", 0x4, RT_BIT(11), 0, 0x4, RT_BIT(3), RT_BIT(25), 0x4, RT_BIT(4), RT_BIT(20), RT_TRUE), + [RK3588_PD_SDMMC] = DOMAIN_RK3588("sdmmc", 0x4, RT_BIT(13), 0, 0x4, RT_BIT(5), RT_BIT(26), 0x0, 0, 0, RT_FALSE), +}; + +static const struct rockchip_pmu_info rk3588_pmu = +{ + .pwr_offset = 0x14c, + .status_offset = 0x180, + .req_offset = 0x10c, + .idle_offset = 0x120, + .ack_offset = 0x118, + .mem_pwr_offset = 0x1a0, + .chain_status_offset = 0x1f0, + .mem_status_offset = 0x1f8, + .repair_status_offset = 0x290, + + .num_domains = RT_ARRAY_SIZE(rk3588_pm_domains), + .domain_info = rk3588_pm_domains, +}; + +#define DOMAIN_RV1126(name, pwr, req, idle, wakeup) DOMAIN_M(name, pwr, pwr, req, idle, idle, wakeup) + +static const struct rockchip_domain_info rv1126_pm_domains[] = +{ + [RV1126_PD_VEPU] = DOMAIN_RV1126("vepu", RT_BIT(2), RT_BIT(9), RT_BIT(9), RT_FALSE), + [RV1126_PD_VI] = DOMAIN_RV1126("vi", RT_BIT(4), RT_BIT(6), RT_BIT(6), RT_FALSE), + [RV1126_PD_ISPP] = DOMAIN_RV1126("ispp", RT_BIT(1), RT_BIT(8), RT_BIT(8), RT_FALSE), + [RV1126_PD_VDPU] = DOMAIN_RV1126("vdpu", RT_BIT(3), RT_BIT(10), RT_BIT(10), RT_FALSE), + [RV1126_PD_NVM] = DOMAIN_RV1126("nvm", RT_BIT(7), RT_BIT(11), RT_BIT(11), RT_FALSE), + [RV1126_PD_SDIO] = DOMAIN_RV1126("sdio", RT_BIT(8), RT_BIT(13), RT_BIT(13), RT_FALSE), + [RV1126_PD_USB] = DOMAIN_RV1126("usb", RT_BIT(9), RT_BIT(15), RT_BIT(15), RT_FALSE), +}; + +static const struct rockchip_pmu_info rv1126_pmu = +{ + .pwr_offset = 0x110, + .status_offset = 0x108, + .req_offset = 0xc0, + .idle_offset = 0xd8, + .ack_offset = 0xd0, + + .num_domains = RT_ARRAY_SIZE(rv1126_pm_domains), + .domain_info = rv1126_pm_domains, +}; + +static const struct rt_ofw_node_id rockchip_pmdomain_ofw_ids[] = +{ + { .compatible = "rockchip,px30-power-controller", .data = &px30_pmu, }, + { .compatible = "rockchip,rk3036-power-controller", .data = &rk3036_pmu, }, + { .compatible = "rockchip,rk3066-power-controller", .data = &rk3066_pmu, }, + { .compatible = "rockchip,rk3128-power-controller", .data = &rk3128_pmu, }, + { .compatible = "rockchip,rk3188-power-controller", .data = &rk3188_pmu, }, + { .compatible = "rockchip,rk3228-power-controller", .data = &rk3228_pmu, }, + { .compatible = "rockchip,rk3288-power-controller", .data = &rk3288_pmu, }, + { .compatible = "rockchip,rk3328-power-controller", .data = &rk3328_pmu, }, + { .compatible = "rockchip,rk3366-power-controller", .data = &rk3366_pmu, }, + { .compatible = "rockchip,rk3368-power-controller", .data = &rk3368_pmu, }, + { .compatible = "rockchip,rk3399-power-controller", .data = &rk3399_pmu, }, + { .compatible = "rockchip,rk3568-power-controller", .data = &rk3568_pmu, }, + { .compatible = "rockchip,rk3588-power-controller", .data = &rk3588_pmu, }, + { .compatible = "rockchip,rv1126-power-controller", .data = &rv1126_pmu, }, + { /* sentinel */ } +}; + +static struct rt_platform_driver rockchip_pmdomain_driver = +{ + .name = "rockchip-pmdomain", + .ids = rockchip_pmdomain_ofw_ids, + + .probe = rockchip_pmdomain_probe, +}; + +static int rockchip_pmdomain_drv_register(void) +{ + rt_platform_driver_register(&rockchip_pmdomain_driver); + + return 0; +} +INIT_SUBSYS_LATER_EXPORT(rockchip_pmdomain_drv_register); diff --git a/components/drivers/pmdomain/pm-domain-scmi.c b/components/drivers/pmdomain/pm-domain-scmi.c new file mode 100644 index 000000000000..9fba8e1b283e --- /dev/null +++ b/components/drivers/pmdomain/pm-domain-scmi.c @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-11-21 GuEe-GUI first version + */ + +#include +#include + +#define DBG_TAG "pm-domain.scmi" +#define DBG_LVL DBG_INFO +#include + +struct scmi_pm_domain_proxy; + +struct scmi_pm_domain +{ + struct rt_dm_power_domain parent; + + rt_uint32_t domain; + + struct scmi_pm_domain_proxy *proxy; +}; + +#define raw_to_scmi_pm_domain(raw) rt_container_of(raw, struct scmi_pm_domain, parent) + +struct scmi_pm_domain_proxy +{ + struct rt_dm_power_domain_proxy parent; + + struct rt_scmi_device *sdev; + + rt_uint32_t num_domains; + struct scmi_pm_domain domains[]; +}; + +#define raw_to_scmi_pm_domain_proxy(raw) rt_container_of(raw, struct scmi_pm_domain_proxy, parent) + +static rt_err_t scmi_pm_domain_power(struct scmi_pm_domain *scmi_pd, rt_bool_t power_on) +{ + struct scmi_power_state_set_in in = + { + .flags = rt_cpu_to_le32(0), + .domain = rt_cpu_to_le32(scmi_pd->domain), + .state = rt_cpu_to_le32(power_on ? SCMI_POWER_STATE_GENERIC_ON : SCMI_POWER_STATE_GENERIC_OFF), + }; + struct scmi_power_state_set_out out; + struct rt_scmi_msg msg = RT_SCMI_MSG_IN(SCMI_POWER_STATE_SET, in, out); + + return rt_scmi_process_msg(scmi_pd->proxy->sdev, &msg); +} + +static rt_err_t scmi_pd_power_on(struct rt_dm_power_domain *domain) +{ + return scmi_pm_domain_power(raw_to_scmi_pm_domain(domain), RT_TRUE); +} + +static rt_err_t scmi_pd_power_off(struct rt_dm_power_domain *domain) +{ + return scmi_pm_domain_power(raw_to_scmi_pm_domain(domain), RT_FALSE); +} + +static struct rt_dm_power_domain *scmi_pm_domain_proxy_ofw_parse( + struct rt_dm_power_domain_proxy *proxy, struct rt_ofw_cell_args *args) +{ + struct scmi_pm_domain_proxy *scmi_proxy = raw_to_scmi_pm_domain_proxy(proxy); + + return &scmi_proxy->domains[args->args[0]].parent; +} + +static rt_err_t scmi_pm_domain_probe(struct rt_scmi_device *sdev) +{ + rt_err_t err; + rt_uint32_t num_domains; + struct scmi_pm_domain *scmi_pds; + struct scmi_pm_domain_proxy *scmi_proxy; + struct scmi_power_attributes attr = {}; + struct rt_scmi_msg msg = RT_SCMI_MSG_IN(SCMI_COM_MSG_ATTRIBUTES, attr, attr); + + if ((err = rt_scmi_process_msg(sdev, &msg))) + { + return err; + } + + num_domains = rt_le16_to_cpu(attr.num_domains); + scmi_proxy = rt_calloc(1, sizeof(*scmi_proxy) + sizeof(*scmi_pds) * num_domains); + + if (!scmi_proxy) + { + return -RT_ENOMEM; + } + + scmi_proxy->sdev = sdev; + scmi_proxy->num_domains = num_domains; + + scmi_pds = scmi_proxy->domains; + + for (int i = 0; i < num_domains; ++i, ++scmi_pds) + { + struct rt_dm_power_domain *domain = &scmi_pds->parent; + + domain->power_off = scmi_pd_power_off; + domain->power_on = scmi_pd_power_on; + + scmi_pds->domain = i; + scmi_pds->proxy = scmi_proxy; + + rt_dm_power_domain_register(domain); + } + + scmi_proxy->parent.ofw_parse = scmi_pm_domain_proxy_ofw_parse; + rt_dm_power_domain_proxy_ofw_bind(&scmi_proxy->parent, sdev->parent.ofw_node); + + return RT_EOK; +} + +static const struct rt_scmi_device_id scmi_pm_domain_ids[] = +{ + { SCMI_PROTOCOL_ID_POWER, "genpd" }, + { /* sentinel */ }, +}; + +static struct rt_scmi_driver scmi_pm_domain_driver = +{ + .name = "pm-domain-scmi", + .ids = scmi_pm_domain_ids, + + .probe = scmi_pm_domain_probe, +}; +RT_SCMI_DRIVER_EXPORT(scmi_pm_domain_driver); diff --git a/components/drivers/power/Kconfig b/components/drivers/power/Kconfig new file mode 100644 index 000000000000..e1b39b992cd4 --- /dev/null +++ b/components/drivers/power/Kconfig @@ -0,0 +1 @@ +source "$RTT_DIR/components/drivers/power/reset/Kconfig" diff --git a/components/drivers/power/SConscript b/components/drivers/power/SConscript new file mode 100644 index 000000000000..0b84689e91c6 --- /dev/null +++ b/components/drivers/power/SConscript @@ -0,0 +1,11 @@ +from building import * + +cwd = GetCurrentDir() +objs = [] +list = os.listdir(cwd) + +for d in list: + path = os.path.join(cwd, d) + if os.path.isfile(os.path.join(path, 'SConscript')): + objs = objs + SConscript(os.path.join(d, 'SConscript')) +Return('objs') diff --git a/components/drivers/power/reset/Kconfig b/components/drivers/power/reset/Kconfig new file mode 100644 index 000000000000..376fa1b03436 --- /dev/null +++ b/components/drivers/power/reset/Kconfig @@ -0,0 +1,23 @@ +menuconfig RT_USING_POWER_RESET + bool "Using Board level reset or power off" + depends on RT_USING_DM + +config RT_POWER_RESET_SYSCON_POWEROFF + bool "Generic SYSCON regmap poweroff driver" + depends on RT_USING_POWER_RESET + select RT_MFD_SYSCON + +config RT_POWER_RESET_SYSCON_REBOOT_MODE + bool "Generic SYSCON regmap reboot mode driver" + depends on RT_USING_POWER_RESET + select RT_MFD_SYSCON + select RT_POWER_RESET_REBOOT_MODE + +config RT_POWER_RESET_SYSCON_REBOOT + bool "Generic SYSCON regmap reboot driver" + depends on RT_USING_POWER_RESET + select RT_MFD_SYSCON + +config RT_POWER_RESET_REBOOT_MODE + bool + select RT_USING_OFW diff --git a/components/drivers/power/reset/SConscript b/components/drivers/power/reset/SConscript new file mode 100644 index 000000000000..cc330a5bd3eb --- /dev/null +++ b/components/drivers/power/reset/SConscript @@ -0,0 +1,27 @@ +from building import * + +group = [] + +if not GetDepend(['RT_USING_POWER_RESET']): + Return('group') + +cwd = GetCurrentDir() +CPPPATH = [cwd + '/../../include'] + +src = [] + +if GetDepend(['RT_POWER_RESET_REBOOT_MODE']): + src += ['reboot-mode.c'] + +if GetDepend(['RT_POWER_RESET_SYSCON_POWEROFF']): + src += ['syscon-poweroff.c'] + +if GetDepend(['RT_POWER_RESET_SYSCON_REBOOT_MODE']): + src += ['syscon-reboot-mode.c'] + +if GetDepend(['RT_POWER_RESET_SYSCON_REBOOT']): + src += ['syscon-reboot.c'] + +group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') diff --git a/components/drivers/power/reset/reboot-mode.c b/components/drivers/power/reset/reboot-mode.c new file mode 100644 index 000000000000..bb89ac03062c --- /dev/null +++ b/components/drivers/power/reset/reboot-mode.c @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-02-25 GuEe-GUI the first version + */ + +#include "reboot-mode.h" + +#define MODE_SUFFIXE "mode-" + +struct mode_info +{ + rt_slist_t list; + + const char *mode; + rt_uint32_t magic; +}; + +static rt_err_t reboot_mode_work(struct rt_device *dev, char *cmd) +{ + struct mode_info *info; + struct reboot_mode *reboot = (void *)dev; + + cmd = cmd ? : "normal"; + + rt_slist_for_each_entry(info, &reboot->mode_nodes, list) + { + if (!rt_strcmp(info->mode, cmd)) + { + reboot->write(reboot, info->magic); + break; + } + } + + return RT_EOK; +} + +rt_err_t reboot_mode_register(struct reboot_mode *reboot) +{ + rt_err_t err; + struct mode_info *info; + struct rt_ofw_prop *prop; + struct rt_ofw_node *np = reboot->dev->ofw_node; + const int mode_suffixe_len = sizeof(MODE_SUFFIXE) - 1; + + if (!reboot || !reboot->dev) + { + return -RT_EINVAL; + } + + rt_slist_init(&reboot->mode_nodes); + + rt_ofw_foreach_prop(np, prop) + { + if (!rt_strncmp(prop->name, MODE_SUFFIXE, mode_suffixe_len)) + { + continue; + } + + info = rt_malloc(sizeof(*info)); + + if (!info) + { + err = -RT_ENOMEM; + + goto _end; + } + + info->mode = prop->value + mode_suffixe_len; + info->magic = fdt32_to_cpu(*(const fdt32_t *)prop->value); + + rt_slist_init(&info->list); + + rt_slist_insert(&reboot->mode_nodes, &info->list); + } + + err = rt_dm_reboot_mode_register((void *)reboot, &reboot_mode_work); + +_end: + if (err) + { + struct mode_info *prev_info = RT_NULL; + + rt_slist_for_each_entry(info, &reboot->mode_nodes, list) + { + if (prev_info) + { + rt_free(prev_info); + } + + prev_info = info; + } + + if (prev_info) + { + rt_free(prev_info); + } + } + + return RT_EOK; +} diff --git a/components/drivers/power/reset/reboot-mode.h b/components/drivers/power/reset/reboot-mode.h new file mode 100644 index 000000000000..303588203674 --- /dev/null +++ b/components/drivers/power/reset/reboot-mode.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-02-25 GuEe-GUI the first version + */ + +#ifndef __RESET_REBOOT_MODE_H__ +#define __RESET_REBOOT_MODE_H__ + +#include +#include + +struct reboot_mode +{ + rt_slist_t mode_nodes; + + struct rt_device *dev; + rt_err_t (*write)(struct reboot_mode *reboot, rt_uint32_t magic); +}; + +rt_err_t reboot_mode_register(struct reboot_mode *reboot); + +#endif /* __RESET_REBOOT_MODE_H__ */ diff --git a/components/drivers/pm/pm-syscon-poweroff.c b/components/drivers/power/reset/syscon-poweroff.c similarity index 84% rename from components/drivers/pm/pm-syscon-poweroff.c rename to components/drivers/power/reset/syscon-poweroff.c index a8510aba39b2..00273be629f7 100644 --- a/components/drivers/pm/pm-syscon-poweroff.c +++ b/components/drivers/power/reset/syscon-poweroff.c @@ -9,13 +9,12 @@ */ #include +#include -#define DBG_TAG "pm.syscon.poweroff" +#define DBG_TAG "reset.syscon.poweroff" #define DBG_LVL DBG_INFO #include -#include - static struct rt_syscon *syscon; static rt_uint32_t offset, value, mask; @@ -34,7 +33,7 @@ static rt_err_t syscon_poweroff_probe(struct rt_platform_device *pdev) rt_err_t mask_err, value_err; struct rt_ofw_node *np = pdev->parent.ofw_node; - syscon = rt_syscon_find_by_phandle(np, "regmap"); + syscon = rt_syscon_find_by_ofw_phandle(np, "regmap"); if (!syscon) { @@ -70,14 +69,14 @@ static rt_err_t syscon_poweroff_probe(struct rt_platform_device *pdev) mask = 0xffffffff; } - if (rt_pm_shutdown) + if (rt_dm_machine_shutdown) { - LOG_E("rt_pm_shutdown have hook %p", rt_pm_shutdown); + LOG_E("rt_dm_machine_shutdown have hook %p", rt_dm_machine_shutdown); return -RT_EBUSY; } - rt_pm_shutdown = syscon_poweroff; + rt_dm_machine_shutdown = syscon_poweroff; return RT_EOK; } @@ -90,7 +89,7 @@ static const struct rt_ofw_node_id syscon_poweroff_ofw_ids[] = static struct rt_platform_driver syscon_poweroff_driver = { - .name = "pm-syscon-poweroff", + .name = "reset-syscon-poweroff", .ids = syscon_poweroff_ofw_ids, .probe = syscon_poweroff_probe, diff --git a/components/drivers/power/reset/syscon-reboot-mode.c b/components/drivers/power/reset/syscon-reboot-mode.c new file mode 100644 index 000000000000..7d261e7733af --- /dev/null +++ b/components/drivers/power/reset/syscon-reboot-mode.c @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-02-25 GuEe-GUI the first version + */ + +#include +#include + +#define DBG_TAG "reset.syscon.reboot-mode" +#define DBG_LVL DBG_INFO +#include + +#include "reboot-mode.h" + +struct syscon_reboot_mode +{ + struct rt_syscon *map; + + struct reboot_mode reboot; + + rt_uint32_t offset; + rt_uint32_t mask; +}; + +static rt_err_t syscon_reboot_mode_write(struct reboot_mode *reboot, + rt_uint32_t magic) +{ + rt_err_t err; + struct syscon_reboot_mode *srbm; + + srbm = rt_container_of(reboot, struct syscon_reboot_mode, reboot); + + err = rt_syscon_update_bits(srbm->map, srbm->offset, srbm->mask, magic); + + if (err) + { + LOG_E("Update reboot mode bits failed"); + } + + return err; +} + +static rt_err_t syscon_reboot_mode_probe(struct rt_platform_device *pdev) +{ + rt_err_t err; + struct rt_ofw_node *np; + struct rt_device *dev = &pdev->parent; + struct syscon_reboot_mode *srbm = rt_calloc(1, sizeof(*srbm)); + + if (!srbm) + { + return -RT_ENOMEM; + } + + np = rt_ofw_get_parent(dev->ofw_node); + srbm->map = rt_syscon_find_by_ofw_node(np); + rt_ofw_node_put(np); + + if (!srbm->map) + { + err = -RT_EIO; + goto _fail; + } + + srbm->reboot.dev = dev; + srbm->reboot.write = syscon_reboot_mode_write; + srbm->mask = 0xffffffff; + + if (rt_dm_dev_prop_read_u32(dev, "offset", &srbm->offset)) + { + err = -RT_EINVAL; + goto _fail; + } + + rt_dm_dev_prop_read_u32(dev, "mask", &srbm->mask); + + if ((err = reboot_mode_register(&srbm->reboot))) + { + goto _fail; + } + + return RT_EOK; + +_fail: + rt_free(srbm); + + return err; +} + +static const struct rt_ofw_node_id syscon_reboot_mode_ofw_ids[] = +{ + { .compatible = "syscon-reboot-mode" }, + { /* sentinel */ } +}; + +static struct rt_platform_driver syscon_reboot_mode_driver = +{ + .name = "reset-syscon-reboot-mode", + .ids = syscon_reboot_mode_ofw_ids, + + .probe = syscon_reboot_mode_probe, +}; +RT_PLATFORM_DRIVER_EXPORT(syscon_reboot_mode_driver); diff --git a/components/drivers/pm/pm-syscon-reboot.c b/components/drivers/power/reset/syscon-reboot.c similarity index 79% rename from components/drivers/pm/pm-syscon-reboot.c rename to components/drivers/power/reset/syscon-reboot.c index 85b3dc09db23..9f0f96b9c7b2 100644 --- a/components/drivers/pm/pm-syscon-reboot.c +++ b/components/drivers/power/reset/syscon-reboot.c @@ -9,13 +9,12 @@ */ #include +#include -#define DBG_TAG "pm.syscon.reboot" +#define DBG_TAG "reset.syscon.reboot" #define DBG_LVL DBG_INFO #include -#include - static struct rt_syscon *syscon; static rt_uint32_t offset, value, mask; @@ -26,15 +25,15 @@ static void syscon_reboot(void) rt_thread_mdelay(1000); - LOG_E("Unable to restart system\n"); + LOG_E("Unable to restart system"); } -static int syscon_reboot_probe(struct platform_device *pdev) +static rt_err_t syscon_reboot_probe(struct rt_platform_device *pdev) { rt_err_t mask_err, value_err; struct rt_ofw_node *np = pdev->parent.ofw_node; - syscon = rt_syscon_find_by_phandle(np, "regmap"); + syscon = rt_syscon_find_by_ofw_phandle(np, "regmap"); if (!syscon) { @@ -70,14 +69,14 @@ static int syscon_reboot_probe(struct platform_device *pdev) mask = 0xffffffff; } - if (rt_pm_reset) + if (rt_dm_machine_reset) { - LOG_E("rt_pm_reset have hook %p", rt_pm_reset); + LOG_E("rt_dm_machine_reset have hook %p", rt_dm_machine_reset); return -RT_EBUSY; } - rt_pm_reset = syscon_reboot; + rt_dm_machine_reset = syscon_reboot; return RT_EOK; } @@ -90,7 +89,7 @@ static const struct rt_ofw_node_id syscon_reboot_ofw_ids[] = static struct rt_platform_driver syscon_reboot_driver = { - .name = "pm-syscon-reboot", + .name = "reset-syscon-reboot", .ids = syscon_reboot_ofw_ids, .probe = syscon_reboot_probe, diff --git a/components/drivers/pwm/Kconfig b/components/drivers/pwm/Kconfig new file mode 100755 index 000000000000..9f145afe5759 --- /dev/null +++ b/components/drivers/pwm/Kconfig @@ -0,0 +1,31 @@ +menuconfig RT_USING_PWM + bool "Using PWM device drivers" + default n + +config RT_PWM_BCM2835 + bool "BCM2835 PWM support" + select RT_USING_PINCTRL + depends on RT_USING_DM + depends on RT_USING_PWM + default n + +config RT_PWM_BRCMSTB + bool "Broadcom STB PWM support" + select RT_USING_PINCTRL + depends on RT_USING_DM + depends on RT_USING_PWM + default n + +config RT_PWM_ROCKCHIP + bool "Rockchip PWM support" + select RT_USING_PINCTRL + depends on RT_USING_DM + depends on RT_USING_PWM + default n + +config RT_PWM_RP1 + bool "RP1 PWM support" + select RT_USING_PINCTRL + depends on RT_USING_DM + depends on RT_USING_PWM + default n diff --git a/components/drivers/pwm/SConscript b/components/drivers/pwm/SConscript new file mode 100755 index 000000000000..d4120e31ef06 --- /dev/null +++ b/components/drivers/pwm/SConscript @@ -0,0 +1,27 @@ +from building import * + +group = [] + +if not GetDepend(['RT_USING_PWM']): + Return('group') + +cwd = GetCurrentDir() +CPPPATH = [cwd + '/../include'] + +src = ['pwm.c'] + +if GetDepend(['RT_PWM_BCM2835']): + src += ['pwm-bcm2835.c'] + +if GetDepend(['RT_PWM_BRCMSTB']): + src += ['pwm-brcmstb.c'] + +if GetDepend(['RT_PWM_ROCKCHIP']): + src += ['pwm-rockchip.c'] + +if GetDepend(['RT_PWM_RP1']): + src += ['pwm-rp1.c'] + +group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') diff --git a/components/drivers/pwm/pwm-bcm2835.c b/components/drivers/pwm/pwm-bcm2835.c new file mode 100644 index 000000000000..786f3acafb1b --- /dev/null +++ b/components/drivers/pwm/pwm-bcm2835.c @@ -0,0 +1,279 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-11-26 GuEe-GUI first version + */ + +#include +#include +#include + +#define DBG_TAG "pwm.bcm2835" +#define DBG_LVL DBG_INFO +#include + +#define PWM_CONTROL 0x000 +#define PWM_CONTROL_SHIFT(x) ((x) * 8) +#define PWM_CONTROL_MASK 0xff +#define PWM_MODE 0x80 /* Set timer in PWM mode */ +#define PWM_ENABLE (1 << 0) +#define PWM_POLARITY (1 << 4) + +#define PERIOD(x) (((x) * 0x10) + 0x10) +#define DUTY(x) (((x) * 0x10) + 0x14) + +#define PERIOD_MIN 0x2 +#define PWM_NUMBER 2 + +#define NSEC_PER_SEC 1000000000L + +struct bcm2835_pwm +{ + struct rt_device_pwm parent; + + void *base; + struct rt_clk *clk; +}; + +#define raw_to_bcm2835_pwm(raw) rt_container_of(raw, struct bcm2835_pwm, parent) + +static rt_err_t bcm2835_pwm_enable(struct bcm2835_pwm *bpwm, int channel, rt_bool_t enable) +{ + rt_uint32_t value; + + if (channel >= PWM_NUMBER) + { + return -RT_EINVAL; + } + + if (enable) + { + value = HWREG32(bpwm->base + PWM_CONTROL); + value &= ~(PWM_CONTROL_MASK << PWM_CONTROL_SHIFT(channel)); + value |= ((PWM_MODE | PWM_ENABLE) << PWM_CONTROL_SHIFT(channel)); + HWREG32(bpwm->base + PWM_CONTROL) = value; + } + else + { + value = HWREG32(bpwm->base + PWM_CONTROL); + value &= ~((PWM_CONTROL_MASK | PWM_ENABLE) << PWM_CONTROL_SHIFT(channel)); + HWREG32(bpwm->base + PWM_CONTROL) = value; + } + + return RT_EOK; +} + +static rt_err_t bcm2835_pwm_config(struct bcm2835_pwm *bpwm, + struct rt_pwm_configuration *pwm_cfg) +{ + int channel; + rt_uint32_t val; + rt_uint64_t period_cycles, max_period; + rt_ubase_t rate = rt_clk_get_rate(bpwm->clk); + + if (!rate) + { + LOG_E("Failed to get clock rate"); + + return -RT_EINVAL; + } + + channel = pwm_cfg->channel; + + /* + * period_cycles must be a 32 bit value, so period * rate / NSEC_PER_SEC + * must be <= RT_UINT32_MAX. As RT_UINT32_MAX * NSEC_PER_SEC < U64_MAX the + * multiplication period * rate doesn't overflow. + * To calculate the maximal possible period that guarantees the + * above inequality: + * + * round(period * rate / NSEC_PER_SEC) <= RT_UINT32_MAX + * <=> period * rate / NSEC_PER_SEC < RT_UINT32_MAX + 0.5 + * <=> period * rate < (RT_UINT32_MAX + 0.5) * NSEC_PER_SEC + * <=> period < ((RT_UINT32_MAX + 0.5) * NSEC_PER_SEC) / rate + * <=> period < ((RT_UINT32_MAX * NSEC_PER_SEC + NSEC_PER_SEC/2) / rate + * <=> period <= ceil((RT_UINT32_MAX * NSEC_PER_SEC + NSEC_PER_SEC/2) / rate) - 1 + */ + max_period = RT_DIV_ROUND_UP_ULL( + (rt_uint64_t)RT_UINT32_MAX * NSEC_PER_SEC + NSEC_PER_SEC / 2, rate) - 1; + + if (pwm_cfg->period > max_period) + { + return -RT_EINVAL; + } + + /* Set period */ + period_cycles = RT_DIV_ROUND_CLOSEST_ULL(pwm_cfg->period * rate, NSEC_PER_SEC); + + /* Don't accept a period that is too small */ + if (period_cycles < PERIOD_MIN) + { + return -RT_EINVAL; + } + + HWREG32(bpwm->base + PERIOD(channel)) = period_cycles; + + /* Set duty cycle */ + val = RT_DIV_ROUND_CLOSEST_ULL(rt_pwm_conf_duty_cycle(pwm_cfg) * rate, NSEC_PER_SEC); + HWREG32(bpwm->base + DUTY(channel)) = val; + + /* Set polarity */ + val = HWREG32(bpwm->base + PWM_CONTROL); + + if (!pwm_cfg->complementary) + { + val &= ~(PWM_POLARITY << PWM_CONTROL_SHIFT(channel)); + } + else + { + val |= PWM_POLARITY << PWM_CONTROL_SHIFT(channel); + } + + HWREG32(bpwm->base + PWM_CONTROL) = val; + + return RT_EOK; +} + +static rt_err_t bcm2835_pwm_control(struct rt_device_pwm *pwm, int cmd, void *args) +{ + rt_err_t err = RT_EOK; + struct bcm2835_pwm *bpwm = raw_to_bcm2835_pwm(pwm); + struct rt_pwm_configuration *pwm_cfg = args; + + switch (cmd) + { + case PWM_CMD_ENABLE: + err = bcm2835_pwm_enable(bpwm, pwm_cfg->channel, RT_TRUE); + break; + + case PWM_CMD_DISABLE: + err = bcm2835_pwm_enable(bpwm, pwm_cfg->channel, RT_FALSE); + break; + + case PWM_CMD_SET: + case PWM_CMD_SET_PERIOD: + case PWM_CMD_SET_PULSE: + err = bcm2835_pwm_config(bpwm, pwm_cfg); + break; + + default: + err = -RT_EINVAL; + break; + } + + return err; +} + +const static struct rt_pwm_ops bcm2835_pwm_ops = +{ + .control = bcm2835_pwm_control, +}; + +static rt_err_t bcm2835_pwm_probe(struct rt_platform_device *pdev) +{ + rt_err_t err; + struct rt_device *dev = &pdev->parent; + struct bcm2835_pwm *bpwm = rt_calloc(1, sizeof(*bpwm)); + + if (!bpwm) + { + return -RT_ENOMEM; + } + + bpwm->base = rt_dm_dev_iomap(dev, 0); + + if (!bpwm->base) + { + err = -RT_EIO; + goto _fail; + } + + bpwm->clk = rt_clk_get_by_index(dev, 0); + + if (rt_is_err(bpwm->clk)) + { + err = rt_ptr_err(bpwm->clk); + goto _fail; + } + + if ((err = rt_clk_prepare_enable(bpwm->clk))) + { + goto _fail; + } + + dev->user_data = bpwm; + + bpwm->parent.parent.ofw_node = dev->ofw_node; + + rt_dm_dev_set_name_auto(&bpwm->parent.parent, "pwm"); + rt_device_pwm_register(&bpwm->parent, rt_dm_dev_get_name(&bpwm->parent.parent), &bcm2835_pwm_ops, bpwm); + + rt_dm_dev_bind_fwdata(dev, RT_NULL, bpwm); + + return RT_EOK; + +_fail: + if (bpwm->base) + { + rt_iounmap(bpwm->base); + } + + if (!rt_is_err_or_null(bpwm->clk)) + { + rt_clk_disable_unprepare(bpwm->clk); + rt_clk_put(bpwm->clk); + } + + rt_free(bpwm); + + return err; +} + +static rt_err_t bcm2835_pwm_remove(struct rt_platform_device *pdev) +{ + struct rt_device *dev = &pdev->parent; + struct bcm2835_pwm *bpwm = dev->user_data; + + rt_dm_dev_unbind_fwdata(dev, RT_NULL); + + for (int channel = 0; channel < PWM_NUMBER; ++channel) + { + bcm2835_pwm_enable(bpwm, channel, RT_FALSE); + } + + rt_device_unregister(&bpwm->parent.parent); + + rt_clk_disable_unprepare(bpwm->clk); + rt_clk_put(bpwm->clk); + + rt_free(bpwm); + + return RT_EOK; +} + +static const struct rt_ofw_node_id bcm2835_pwm_ofw_ids[] = +{ + { .compatible = "brcm,bcm2835-pwm", }, + { /* sentinel */ } +}; + +static struct rt_platform_driver bcm2835_pwm_driver = +{ + .name = "bcm2835-pwm", + .ids = bcm2835_pwm_ofw_ids, + + .probe = bcm2835_pwm_probe, + .remove = bcm2835_pwm_remove, +}; + +static int bcm2835_pwm_register(void) +{ + rt_platform_driver_register(&bcm2835_pwm_driver); + + return 0; +} +INIT_SUBSYS_LATER_EXPORT(bcm2835_pwm_register); diff --git a/components/drivers/pwm/pwm-brcmstb.c b/components/drivers/pwm/pwm-brcmstb.c new file mode 100644 index 000000000000..988a95c0f891 --- /dev/null +++ b/components/drivers/pwm/pwm-brcmstb.c @@ -0,0 +1,337 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-11-26 GuEe-GUI first version + */ + +#include +#include +#include + +#define DBG_TAG "pwm.brcmstb" +#define DBG_LVL DBG_INFO +#include + +#define PWM_CTRL 0x00 +#define CTRL_START RT_BIT(0) +#define CTRL_OEB RT_BIT(1) +#define CTRL_FORCE_HIGH RT_BIT(2) +#define CTRL_OPENDRAIN RT_BIT(3) +#define CTRL_CHAN_OFFS 4 + +#define PWM_CTRL2 0x04 +#define CTRL2_OUT_SELECT RT_BIT(0) + +#define PWM_CH_SIZE 0x8 + +#define PWM_CWORD_MSB(ch) (0x08 + ((ch) * PWM_CH_SIZE)) +#define PWM_CWORD_LSB(ch) (0x0c + ((ch) * PWM_CH_SIZE)) + +/* Number of bits for the CWORD value */ +#define CWORD_BIT_SIZE 16 + +/* + * Maximum control word value allowed when variable-frequency PWM is used as a + * clock for the constant-frequency PMW. + */ +#define CONST_VAR_F_MAX 32768 +#define CONST_VAR_F_MIN 1 + +#define PWM_ON(ch) (0x18 + ((ch) * PWM_CH_SIZE)) +#define PWM_ON_MIN 1 +#define PWM_PERIOD(ch) (0x1c + ((ch) * PWM_CH_SIZE)) +#define PWM_PERIOD_MIN 0 + +#define PWM_ON_PERIOD_MAX 0xff + +#define PWM_NUMBER 2 + +#define NSEC_PER_SEC 1000000000L + +struct brcmstb_pwm +{ + struct rt_device_pwm parent; + + void *base; + struct rt_clk *clk; +}; + +#define raw_to_brcmstb_pwm(raw) rt_container_of(raw, struct brcmstb_pwm, parent) + +#ifndef mul_u64_u64_div_u64 +static rt_uint64_t mul_u64_u64_div_u64(rt_uint64_t a, rt_uint64_t b, rt_uint64_t c) +{ + /* overflow in rt-thread is unlikely */ + return a * b / c; +} +#endif /* mul_u64_u64_div_u64 */ + +static rt_err_t brcmstb_pwm_enable(struct brcmstb_pwm *bpwm, int channel, rt_bool_t enable) +{ + rt_uint32_t shift, value; + + if (channel >= PWM_NUMBER) + { + return -RT_EINVAL; + } + + shift = channel * CTRL_CHAN_OFFS; + value = HWREG32(bpwm->base + PWM_CTRL); + + if (enable) + { + value &= ~(CTRL_OEB << shift); + value |= (CTRL_START | CTRL_OPENDRAIN) << shift; + } + else + { + value &= ~((CTRL_START | CTRL_OPENDRAIN) << shift); + value |= CTRL_OEB << shift; + } + + HWREG32(bpwm->base + PWM_CTRL) = value; + + return RT_EOK; +} + +/* + * Fv is derived from the variable frequency output. The variable frequency + * output is configured using this formula: + * + * W = cword, if cword < 2 ^ 15 else 16-bit 2's complement of cword + * + * Fv = W x 2 ^ -16 x 27Mhz (reference clock) + * + * The period is: (period + 1) / Fv and "on" time is on / (period + 1) + * + * The PWM core framework specifies that the "duty_ns" parameter is in fact the + * "on" time, so this translates directly into our HW programming here. + */ +static rt_err_t brcmstb_pwm_config(struct brcmstb_pwm *bpwm, + struct rt_pwm_configuration *pwm_cfg) +{ + rt_ubase_t pc, dc, cword = CONST_VAR_F_MAX; + rt_uint32_t value, channel, period_ns, duty_ns; + + channel = pwm_cfg->channel; + period_ns = pwm_cfg->period; + duty_ns = rt_pwm_conf_duty_cycle(pwm_cfg); + + /* + * If asking for a duty_ns equal to period_ns, we need to substract + * the period value by 1 to make it shorter than the "on" time and + * produce a flat 100% duty cycle signal, and max out the "on" time + */ + if (duty_ns == period_ns) + { + dc = PWM_ON_PERIOD_MAX; + pc = PWM_ON_PERIOD_MAX - 1; + goto _done; + } + + while (RT_TRUE) + { + rt_uint64_t rate; + + /* Calculate the base rate from base frequency and current cword */ + rate = (rt_uint64_t)rt_clk_get_rate(bpwm->clk) * (rt_uint64_t)cword; + rate >>= CWORD_BIT_SIZE; + + pc = mul_u64_u64_div_u64(period_ns, rate, NSEC_PER_SEC); + dc = mul_u64_u64_div_u64(duty_ns + 1, rate, NSEC_PER_SEC); + + /* + * We can be called with separate duty and period updates, + * so do not reject dc == 0 right away + */ + if (pc == PWM_PERIOD_MIN || (dc < PWM_ON_MIN && duty_ns)) + { + return -RT_EINVAL; + } + + /* We converged on a calculation */ + if (pc <= PWM_ON_PERIOD_MAX && dc <= PWM_ON_PERIOD_MAX) + { + break; + } + + /* + * The cword needs to be a power of 2 for the variable + * frequency generator to output a 50% duty cycle variable + * frequency which is used as input clock to the fixed + * frequency generator. + */ + cword >>= 1; + + /* Desired periods are too large, we do not have a divider for them */ + if (cword < CONST_VAR_F_MIN) + { + return -RT_EINVAL; + } + } + +_done: + /* + * Configure the defined "cword" value to have the variable frequency + * generator output a base frequency for the constant frequency + * generator to derive from. + */ + HWREG32(bpwm->base + PWM_CWORD_MSB(channel)) = cword >> 8; + HWREG32(bpwm->base + PWM_CWORD_LSB(channel)) = cword & 0xff; + + /* Select constant frequency signal output */ + value = HWREG32(bpwm->base + PWM_CTRL2); + value |= CTRL2_OUT_SELECT << (channel * CTRL_CHAN_OFFS); + HWREG32(bpwm->base + PWM_CTRL2) = value; + + /* Configure on and period value */ + HWREG32(bpwm->base + PWM_PERIOD(channel)) = pc; + HWREG32(bpwm->base + PWM_ON(channel)) = dc; + + return 0; +} + +static rt_err_t brcmstb_pwm_control(struct rt_device_pwm *pwm, int cmd, void *args) +{ + rt_err_t err = RT_EOK; + struct brcmstb_pwm *bpwm = raw_to_brcmstb_pwm(pwm); + struct rt_pwm_configuration *pwm_cfg = args; + + switch (cmd) + { + case PWM_CMD_ENABLE: + err = brcmstb_pwm_enable(bpwm, pwm_cfg->channel, RT_TRUE); + break; + + case PWM_CMD_DISABLE: + err = brcmstb_pwm_enable(bpwm, pwm_cfg->channel, RT_FALSE); + break; + + case PWM_CMD_SET: + case PWM_CMD_SET_PERIOD: + case PWM_CMD_SET_PULSE: + err = brcmstb_pwm_config(bpwm, pwm_cfg); + break; + + default: + err = -RT_EINVAL; + break; + } + + return err; +} + +const static struct rt_pwm_ops brcmstb_pwm_ops = +{ + .control = brcmstb_pwm_control, +}; + +static rt_err_t brcmstb_pwm_probe(struct rt_platform_device *pdev) +{ + rt_err_t err; + struct rt_device *dev = &pdev->parent; + struct brcmstb_pwm *bpwm = rt_calloc(1, sizeof(*bpwm)); + + if (!bpwm) + { + return -RT_ENOMEM; + } + + bpwm->base = rt_dm_dev_iomap(dev, 0); + + if (!bpwm->base) + { + err = -RT_EIO; + goto _fail; + } + + bpwm->clk = rt_clk_get_by_index(dev, 0); + + if (rt_is_err(bpwm->clk)) + { + err = rt_ptr_err(bpwm->clk); + goto _fail; + } + + if ((err = rt_clk_prepare_enable(bpwm->clk))) + { + goto _fail; + } + + dev->user_data = bpwm; + + bpwm->parent.parent.ofw_node = dev->ofw_node; + + rt_dm_dev_set_name_auto(&bpwm->parent.parent, "pwm"); + rt_device_pwm_register(&bpwm->parent, rt_dm_dev_get_name(&bpwm->parent.parent), &brcmstb_pwm_ops, bpwm); + + rt_dm_dev_bind_fwdata(dev, RT_NULL, bpwm); + + return RT_EOK; + +_fail: + if (bpwm->base) + { + rt_iounmap(bpwm->base); + } + + if (!rt_is_err_or_null(bpwm->clk)) + { + rt_clk_disable_unprepare(bpwm->clk); + rt_clk_put(bpwm->clk); + } + + rt_free(bpwm); + + return err; +} + +static rt_err_t brcmstb_pwm_remove(struct rt_platform_device *pdev) +{ + struct rt_device *dev = &pdev->parent; + struct brcmstb_pwm *bpwm = dev->user_data; + + rt_dm_dev_unbind_fwdata(dev, RT_NULL); + + for (int channel = 0; channel < PWM_NUMBER; ++channel) + { + brcmstb_pwm_enable(bpwm, channel, RT_FALSE); + } + + rt_device_unregister(&bpwm->parent.parent); + + rt_clk_disable_unprepare(bpwm->clk); + rt_clk_put(bpwm->clk); + + rt_free(bpwm); + + return RT_EOK; +} + +static const struct rt_ofw_node_id brcmstb_pwm_ofw_ids[] = +{ + { .compatible = "brcm,bcm7038-pwm", }, + { /* sentinel */ } +}; + +static struct rt_platform_driver brcmstb_pwm_driver = +{ + .name = "brcmstb-pwm", + .ids = brcmstb_pwm_ofw_ids, + + .probe = brcmstb_pwm_probe, + .remove = brcmstb_pwm_remove, +}; + +static int brcmstb_pwm_register(void) +{ + rt_platform_driver_register(&brcmstb_pwm_driver); + + return 0; +} +INIT_SUBSYS_LATER_EXPORT(brcmstb_pwm_register); + diff --git a/components/drivers/pwm/pwm-rockchip.c b/components/drivers/pwm/pwm-rockchip.c new file mode 100644 index 000000000000..f3f551bbd8e1 --- /dev/null +++ b/components/drivers/pwm/pwm-rockchip.c @@ -0,0 +1,458 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-3-08 GuEe-GUI the first version + */ + +#include +#include +#include + +#define PWM_CTRL_TIMER_EN (1 << 0) +#define PWM_CTRL_OUTPUT_EN (1 << 3) + +#define PWM_ENABLE (1 << 0) +#define PWM_CONTINUOUS (1 << 1) +#define PWM_DUTY_POSITIVE (1 << 3) +#define PWM_DUTY_NEGATIVE (0 << 3) +#define PWM_INACTIVE_NEGATIVE (0 << 4) +#define PWM_INACTIVE_POSITIVE (1 << 4) +#define PWM_POLARITY_MASK (PWM_DUTY_POSITIVE | PWM_INACTIVE_POSITIVE) +#define PWM_OUTPUT_LEFT (0 << 5) +#define PWM_LOCK_EN (1 << 6) +#define PWM_LP_DISABLE (0 << 8) + +#define NSEC_PER_SEC 1000000000L + +struct rockchip_pwm_regs +{ + rt_ubase_t duty; + rt_ubase_t period; + rt_ubase_t cntr; + rt_ubase_t ctrl; +}; + +struct rockchip_pwm_data +{ + struct rockchip_pwm_regs regs; + + rt_uint32_t prescaler; + rt_bool_t supports_polarity; + rt_bool_t supports_lock; + rt_uint32_t enable_conf; +}; + +struct rockchip_pwm +{ + struct rt_device_pwm parent; + void *base; + + struct rt_clk *clk; + struct rt_clk *pclk; + + const struct rockchip_pwm_data *data; +}; + +#define raw_to_rockchip_pwm(raw) rt_container_of(raw, struct rockchip_pwm, parent) + +static rt_err_t rockchip_pwm_enable(struct rockchip_pwm *rk_pwm, rt_bool_t enable) +{ + rt_uint32_t enable_conf = rk_pwm->data->enable_conf, val; + + if (enable) + { + rt_err_t err = rt_clk_enable(rk_pwm->clk); + + if (err) + { + return err; + } + } + + val = HWREG32(rk_pwm->base + rk_pwm->data->regs.ctrl); + + if (enable) + { + val |= enable_conf; + } + else + { + val &= ~enable_conf; + } + + HWREG32(rk_pwm->base + rk_pwm->data->regs.ctrl) = val; + + if (!enable) + { + rt_clk_disable(rk_pwm->clk); + } + else + { + rt_pin_ctrl_confs_apply_by_name(&rk_pwm->parent.parent, "active"); + } + + return RT_EOK; +} + +static void rockchip_pwm_config(struct rockchip_pwm *rk_pwm, struct rt_pwm_configuration *pwm_cfg) +{ + rt_ubase_t period, duty; + rt_uint64_t clk_rate, div; + rt_uint32_t ctrl; + + clk_rate = rt_clk_get_rate(rk_pwm->clk); + + /* + * Since period and duty cycle registers have a width of 32 bits, every + * possible input period can be obtained using the default prescaler value + * for all practical clock rate values. + */ + div = clk_rate * pwm_cfg->period; + period = RT_DIV_ROUND_CLOSEST_ULL(div, rk_pwm->data->prescaler * NSEC_PER_SEC); + + div = clk_rate * rt_pwm_conf_duty_cycle(pwm_cfg); + duty = RT_DIV_ROUND_CLOSEST_ULL(div, rk_pwm->data->prescaler * NSEC_PER_SEC); + + /* + * Lock the period and duty of previous configuration, then change the duty + * and period, that would not be effective. + */ + ctrl = HWREG32(rk_pwm->base + rk_pwm->data->regs.ctrl); + + if (rk_pwm->data->supports_lock) + { + ctrl |= PWM_LOCK_EN; + HWREG32(rk_pwm->base + rk_pwm->data->regs.ctrl) = ctrl; + } + + HWREG32(rk_pwm->base + rk_pwm->data->regs.period) = period; + HWREG32(rk_pwm->base + rk_pwm->data->regs.duty) = duty; + + if (rk_pwm->data->supports_polarity) + { + ctrl &= ~PWM_POLARITY_MASK; + + if (pwm_cfg->complementary) + { + ctrl |= PWM_DUTY_NEGATIVE | PWM_INACTIVE_POSITIVE; + } + else + { + ctrl |= PWM_DUTY_POSITIVE | PWM_INACTIVE_NEGATIVE; + } + } + + /* + * Unlock and set polarity at the same time, the configuration of duty, + * period and polarity would be effective together at next period. + */ + if (rk_pwm->data->supports_lock) + { + ctrl &= ~PWM_LOCK_EN; + } + + HWREG32(rk_pwm->base + rk_pwm->data->regs.ctrl) = ctrl; +} + +static rt_err_t rockchip_pwm_get_state(struct rockchip_pwm *rk_pwm, struct rt_pwm_configuration *pwm_cfg) +{ + rt_err_t err; + rt_uint32_t val; + rt_uint64_t tmp; + rt_ubase_t clk_rate; + + if ((err = rt_clk_enable(rk_pwm->pclk))) + { + return err; + } + + if ((err = rt_clk_enable(rk_pwm->clk))) + { + return err; + } + + clk_rate = rt_clk_get_rate(rk_pwm->clk); + + tmp = HWREG32(rk_pwm->base + rk_pwm->data->regs.period); + tmp *= rk_pwm->data->prescaler * NSEC_PER_SEC; + pwm_cfg->period = RT_DIV_ROUND_CLOSEST_ULL(tmp, clk_rate); + + tmp = HWREG32(rk_pwm->base + rk_pwm->data->regs.duty); + tmp *= rk_pwm->data->prescaler * NSEC_PER_SEC; + pwm_cfg->pulse = rt_pwm_conf_pulse(pwm_cfg, RT_DIV_ROUND_CLOSEST_ULL(tmp, clk_rate)); + + val = HWREG32(rk_pwm->base + rk_pwm->data->regs.ctrl); + + if (rk_pwm->data->supports_polarity && !(val & PWM_DUTY_POSITIVE)) + { + pwm_cfg->complementary = RT_TRUE; + } + else + { + pwm_cfg->complementary = RT_FALSE; + } + + rt_clk_disable(rk_pwm->clk); + rt_clk_disable(rk_pwm->pclk); + + return RT_EOK; +} + +static rt_err_t rockchip_pwm_control(struct rt_device_pwm *pwm, int cmd, void *args) +{ + rt_err_t err = RT_EOK; + struct rockchip_pwm *rk_pwm = raw_to_rockchip_pwm(pwm); + struct rt_pwm_configuration *pwm_cfg = args; + + rt_clk_enable(rk_pwm->pclk); + + /* RT_PWM framework have check args */ + switch (cmd) + { + case PWM_CMD_ENABLE: + err = rockchip_pwm_enable(rk_pwm, RT_TRUE); + break; + + case PWM_CMD_DISABLE: + err = rockchip_pwm_enable(rk_pwm, RT_FALSE); + break; + + case PWM_CMD_SET: + case PWM_CMD_SET_PERIOD: + case PWM_CMD_SET_PULSE: + rockchip_pwm_config(rk_pwm, pwm_cfg); + break; + + case PWM_CMD_GET: + err = rockchip_pwm_get_state(rk_pwm, pwm_cfg); + break; + + default: + err = -RT_EINVAL; + break; + } + + rt_clk_disable(rk_pwm->pclk); + + return err; +} + +const static struct rt_pwm_ops rockchip_pwm_ops = +{ + .control = rockchip_pwm_control, +}; + +static void rockchip_pwm_free(struct rockchip_pwm *rk_pwm) +{ + if (rk_pwm->base) + { + rt_iounmap(rk_pwm->base); + } + + if (!rt_is_err_or_null(rk_pwm->clk)) + { + rt_clk_disable_unprepare(rk_pwm->clk); + rt_clk_put(rk_pwm->clk); + } + + if (!rt_is_err_or_null(rk_pwm->pclk)) + { + rt_clk_disable_unprepare(rk_pwm->pclk); + rt_clk_put(rk_pwm->pclk); + } + + rt_free(rk_pwm); +} + +static rt_err_t rockchip_pwm_probe(struct rt_platform_device *pdev) +{ + rt_err_t err = RT_EOK; + rt_bool_t enabled; + rt_uint32_t enable_conf, ctrl; + struct rt_device *dev = &pdev->parent; + struct rockchip_pwm *rk_pwm = rt_calloc(1, sizeof(*rk_pwm)); + const struct rockchip_pwm_data *pdata = pdev->id->data; + + if (!rk_pwm) + { + return -RT_ENOMEM; + } + + rk_pwm->data = pdata; + rk_pwm->base = rt_dm_dev_iomap(dev, 0); + + if (!rk_pwm->base) + { + err = -RT_EIO; + goto _free_res; + } + + rk_pwm->clk = rt_clk_get_by_name(dev, "pwm"); + + if (rt_is_err(rk_pwm->clk)) + { + err = rt_ptr_err(rk_pwm->clk); + goto _free_res; + } + +#ifdef RT_USING_OFW + if (rt_ofw_count_phandle_cells(dev->ofw_node, "clocks", "#clock-cells") == 2) + { + rk_pwm->pclk = rt_clk_get_by_name(dev, "pclk"); + + if (rt_is_err(rk_pwm->pclk)) + { + err = rt_ptr_err(rk_pwm->pclk); + goto _free_res; + } + } + else +#endif /* RT_USING_OFW */ + { + rk_pwm->pclk = rk_pwm->clk; + } + + if ((err = rt_clk_prepare_enable(rk_pwm->clk))) + { + goto _free_res; + } + + if ((err = rt_clk_prepare_enable(rk_pwm->pclk))) + { + goto _free_res; + } + + enable_conf = rk_pwm->data->enable_conf; + ctrl = HWREG32(rk_pwm->base + rk_pwm->data->regs.ctrl); + enabled = (ctrl & enable_conf) == enable_conf; + + /* Keep the PWM clk enabled if the PWM appears to be up and running. */ + if (!enabled) + { + rt_clk_disable(rk_pwm->clk); + } + + rt_clk_disable(rk_pwm->pclk); + + dev->user_data = rk_pwm; + + rk_pwm->parent.parent.ofw_node = dev->ofw_node; + + rt_dm_dev_set_name_auto(&rk_pwm->parent.parent, "pwm"); + rt_device_pwm_register(&rk_pwm->parent, rt_dm_dev_get_name(&rk_pwm->parent.parent), &rockchip_pwm_ops, rk_pwm); + + rt_dm_dev_bind_fwdata(dev, RT_NULL, rk_pwm); + + return RT_EOK; + +_free_res: + rockchip_pwm_free(rk_pwm); + + return err; +} + +static rt_err_t rockchip_pwm_remove(struct rt_platform_device *pdev) +{ + struct rt_device *dev = &pdev->parent; + struct rockchip_pwm *rk_pwm = dev->user_data; + + rt_dm_dev_unbind_fwdata(dev, RT_NULL); + + rockchip_pwm_enable(rk_pwm, RT_FALSE); + + rt_device_unregister(&rk_pwm->parent.parent); + + rockchip_pwm_free(rk_pwm); + + return RT_EOK; +} + +static const struct rockchip_pwm_data pwm_data_v1 = +{ + .regs = + { + .duty = 0x04, + .period = 0x08, + .cntr = 0x00, + .ctrl = 0x0c, + }, + .prescaler = 2, + .supports_polarity = RT_FALSE, + .supports_lock = RT_FALSE, + .enable_conf = PWM_CTRL_OUTPUT_EN | PWM_CTRL_TIMER_EN, +}; + +static const struct rockchip_pwm_data pwm_data_v2 = +{ + .regs = + { + .duty = 0x08, + .period = 0x04, + .cntr = 0x00, + .ctrl = 0x0c, + }, + .prescaler = 1, + .supports_polarity = RT_TRUE, + .supports_lock = RT_FALSE, + .enable_conf = PWM_OUTPUT_LEFT | PWM_LP_DISABLE | PWM_ENABLE | PWM_CONTINUOUS, +}; + +static const struct rockchip_pwm_data pwm_data_vop = +{ + .regs = + { + .duty = 0x08, + .period = 0x04, + .cntr = 0x0c, + .ctrl = 0x00, + }, + .prescaler = 1, + .supports_polarity = RT_TRUE, + .supports_lock = RT_FALSE, + .enable_conf = PWM_OUTPUT_LEFT | PWM_LP_DISABLE | PWM_ENABLE | PWM_CONTINUOUS, +}; + +static const struct rockchip_pwm_data pwm_data_v3 = +{ + .regs = + { + .duty = 0x08, + .period = 0x04, + .cntr = 0x00, + .ctrl = 0x0c, + }, + .prescaler = 1, + .supports_polarity = RT_TRUE, + .supports_lock = RT_TRUE, + .enable_conf = PWM_OUTPUT_LEFT | PWM_LP_DISABLE | PWM_ENABLE | PWM_CONTINUOUS, +}; + +static const struct rt_ofw_node_id rockchip_pwm_ofw_ids[] = +{ + { .compatible = "rockchip,rk2928-pwm", .data = &pwm_data_v1 }, + { .compatible = "rockchip,rk3288-pwm", .data = &pwm_data_v2 }, + { .compatible = "rockchip,vop-pwm", .data = &pwm_data_vop }, + { .compatible = "rockchip,rk3328-pwm", .data = &pwm_data_v3 }, + { /* sentinel */ } +}; + +static struct rt_platform_driver rockchip_pwm_driver = +{ + .name = "rockchip-pwm", + .ids = rockchip_pwm_ofw_ids, + + .probe = rockchip_pwm_probe, + .remove = rockchip_pwm_remove, +}; + +static int rockchip_pwm_register(void) +{ + rt_platform_driver_register(&rockchip_pwm_driver); + + return 0; +} +INIT_SUBSYS_LATER_EXPORT(rockchip_pwm_register); diff --git a/components/drivers/pwm/pwm-rp1.c b/components/drivers/pwm/pwm-rp1.c new file mode 100644 index 000000000000..729427c28714 --- /dev/null +++ b/components/drivers/pwm/pwm-rp1.c @@ -0,0 +1,262 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-11-26 GuEe-GUI first version + */ + +#include +#include +#include + +#define DBG_TAG "pwm.rp1" +#define DBG_LVL DBG_INFO +#include + +#define PWM_GLOBAL_CTRL 0x000 +#define PWM_CHANNEL_CTRL(x) (0x014 + ((x) * 16)) +#define PWM_RANGE(x) (0x018 + ((x) * 16)) +#define PWM_DUTY(x) (0x020 + ((x) * 16)) + +/* 8:FIFO_POP_MASK + 0:Trailing edge M/S modulation */ +#define PWM_CHANNEL_DEFAULT (RT_BIT(8) + RT_BIT(0)) +#define PWM_CHANNEL_ENABLE(x) RT_BIT(x) +#define PWM_POLARITY RT_BIT(3) +#define SET_UPDATE RT_BIT(31) +#define PWM_MODE_MASK RT_GENMASK(1, 0) + +#define PWM_NUMBER 4 + +#define NSEC_PER_SEC 1000000000L + +struct rp1_pwm +{ + struct rt_device_pwm parent; + + void *base; + struct rt_clk *clk; +}; + +#define raw_to_rp1_pwm(raw) rt_container_of(raw, struct rp1_pwm, parent) + +static void rp1_pwm_apply_config(struct rp1_pwm *rpwm) +{ + rt_uint32_t value; + + value = HWREG32(rpwm->base + PWM_GLOBAL_CTRL); + value |= SET_UPDATE; + HWREG32(rpwm->base + PWM_GLOBAL_CTRL) = value; +} + +static rt_err_t rp1_pwm_enable(struct rp1_pwm *rpwm, int channel, rt_bool_t enable) +{ + rt_uint32_t value; + + if (channel >= PWM_NUMBER) + { + return -RT_EINVAL; + } + + if (enable) + { + value = PWM_CHANNEL_DEFAULT | PWM_CHANNEL_ENABLE(channel); + HWREG32(rpwm->base + PWM_CHANNEL_CTRL(channel)) = value; + } + else + { + value = HWREG32(rpwm->base + PWM_CHANNEL_CTRL(channel)); + value &= ~(PWM_MODE_MASK | PWM_CHANNEL_ENABLE(channel)); + HWREG32(rpwm->base + PWM_CHANNEL_CTRL(channel)) = value; + + rp1_pwm_apply_config(rpwm); + } + + return RT_EOK; +} + +static rt_err_t rp1_pwm_config(struct rp1_pwm *rpwm, + struct rt_pwm_configuration *pwm_cfg) +{ + int channel; + rt_uint32_t value; + rt_ubase_t clk_period, clk_rate = rt_clk_get_rate(rpwm->clk); + + if (!clk_rate) + { + LOG_E("Failed to get clock rate"); + + return -RT_EINVAL; + } + + channel = pwm_cfg->channel; + + /* set period */ + clk_period = RT_DIV_ROUND_CLOSEST(NSEC_PER_SEC, clk_rate); + + HWREG32(rpwm->base + PWM_DUTY(channel)) = + RT_DIV_ROUND_CLOSEST(rt_pwm_conf_duty_cycle(pwm_cfg), clk_period); + + /* set duty cycle */ + HWREG32(rpwm->base + PWM_RANGE(channel)) = + RT_DIV_ROUND_CLOSEST(pwm_cfg->period, clk_period); + + /* set polarity */ + value = HWREG32(rpwm->base + PWM_CHANNEL_CTRL(channel)); + if (!pwm_cfg->complementary) + { + value &= ~PWM_POLARITY; + } + else + { + value |= PWM_POLARITY; + } + HWREG32(rpwm->base + PWM_CHANNEL_CTRL(channel)) = value; + + rp1_pwm_apply_config(rpwm); + + return RT_EOK; +} + +static rt_err_t rp1_pwm_control(struct rt_device_pwm *pwm, int cmd, void *args) +{ + rt_err_t err = RT_EOK; + struct rp1_pwm *rpwm = raw_to_rp1_pwm(pwm); + struct rt_pwm_configuration *pwm_cfg = args; + + switch (cmd) + { + case PWM_CMD_ENABLE: + err = rp1_pwm_enable(rpwm, pwm_cfg->channel, RT_TRUE); + break; + + case PWM_CMD_DISABLE: + err = rp1_pwm_enable(rpwm, pwm_cfg->channel, RT_FALSE); + break; + + case PWM_CMD_SET: + case PWM_CMD_SET_PERIOD: + case PWM_CMD_SET_PULSE: + err = rp1_pwm_config(rpwm, pwm_cfg); + break; + + default: + err = -RT_EINVAL; + break; + } + + return err; +} + +const static struct rt_pwm_ops rp1_pwm_ops = +{ + .control = rp1_pwm_control, +}; + +static rt_err_t rp1_pwm_probe(struct rt_platform_device *pdev) +{ + rt_err_t err; + struct rt_device *dev = &pdev->parent; + struct rp1_pwm *rpwm = rt_calloc(1, sizeof(*rpwm)); + + if (!rpwm) + { + return -RT_ENOMEM; + } + + rpwm->base = rt_dm_dev_iomap(dev, 0); + + if (!rpwm->base) + { + err = -RT_EIO; + goto _fail; + } + + rpwm->clk = rt_clk_get_by_index(dev, 0); + + if (rt_is_err(rpwm->clk)) + { + err = rt_ptr_err(rpwm->clk); + goto _fail; + } + + if ((err = rt_clk_prepare_enable(rpwm->clk))) + { + goto _fail; + } + + dev->user_data = rpwm; + + rpwm->parent.parent.ofw_node = dev->ofw_node; + + rt_dm_dev_set_name_auto(&rpwm->parent.parent, "pwm"); + rt_device_pwm_register(&rpwm->parent, rt_dm_dev_get_name(&rpwm->parent.parent), &rp1_pwm_ops, rpwm); + + rt_dm_dev_bind_fwdata(dev, RT_NULL, rpwm); + + return RT_EOK; + +_fail: + if (rpwm->base) + { + rt_iounmap(rpwm->base); + } + + if (!rt_is_err_or_null(rpwm->clk)) + { + rt_clk_disable_unprepare(rpwm->clk); + rt_clk_put(rpwm->clk); + } + + rt_free(rpwm); + + return err; +} + +static rt_err_t rp1_pwm_remove(struct rt_platform_device *pdev) +{ + struct rt_device *dev = &pdev->parent; + struct rp1_pwm *rpwm = dev->user_data; + + rt_dm_dev_unbind_fwdata(dev, RT_NULL); + + for (int channel = 0; channel < PWM_NUMBER; ++channel) + { + rp1_pwm_enable(rpwm, channel, RT_FALSE); + } + + rt_device_unregister(&rpwm->parent.parent); + + rt_clk_disable_unprepare(rpwm->clk); + rt_clk_put(rpwm->clk); + + rt_free(rpwm); + + return RT_EOK; +} + +static const struct rt_ofw_node_id rp1_pwm_ofw_ids[] = +{ + { .compatible = "raspberrypi,rp1-pwm" }, + { /* sentinel */ } +}; + +static struct rt_platform_driver rp1_pwm_driver = +{ + .name = "rp1-pwm", + .ids = rp1_pwm_ofw_ids, + + .probe = rp1_pwm_probe, + .remove = rp1_pwm_remove, +}; + +static int rp1_pwm_register(void) +{ + rt_platform_driver_register(&rp1_pwm_driver); + + return 0; +} +INIT_SUBSYS_LATER_EXPORT(rp1_pwm_register); + diff --git a/components/drivers/misc/rt_drv_pwm.c b/components/drivers/pwm/pwm.c similarity index 99% rename from components/drivers/misc/rt_drv_pwm.c rename to components/drivers/pwm/pwm.c index af87a5b60e07..438aafbbd616 100644 --- a/components/drivers/misc/rt_drv_pwm.c +++ b/components/drivers/pwm/pwm.c @@ -116,8 +116,6 @@ rt_err_t rt_device_pwm_register(struct rt_device_pwm *device, const char *name, { rt_err_t result = RT_EOK; - rt_memset(device, 0, sizeof(struct rt_device_pwm)); - #ifdef RT_USING_DEVICE_OPS device->parent.ops = &pwm_device_ops; #else diff --git a/components/drivers/regulator/Kconfig b/components/drivers/regulator/Kconfig new file mode 100644 index 000000000000..a08e86a630f6 --- /dev/null +++ b/components/drivers/regulator/Kconfig @@ -0,0 +1,37 @@ +menuconfig RT_USING_REGULATOR + bool "Using Voltage and Current Regulator" + depends on RT_USING_DM + default n + +config RT_REGULATOR_FAN53555 + bool "Fairchild FAN53555 Regulator" + depends on RT_USING_REGULATOR + depends on RT_USING_I2C + default n + +config RT_REGULATOR_FIXED + bool "Fixed regulator support" + depends on RT_USING_REGULATOR + select RT_USING_PIN + default y + +config RT_REGULATOR_GPIO + bool "GPIO regulator support" + depends on RT_USING_REGULATOR + select RT_USING_PIN + default y + +config RT_REGULATOR_PWM + bool "PWM regulator support" + depends on RT_USING_REGULATOR + select RT_USING_PWM + select RT_USING_PIN + select RT_USING_OFW + default n + +config RT_REGULATOR_RK8XX + bool "Rockchip RK805/RK808/RK809/RK817/RK818 Power regulators" + depends on RT_USING_REGULATOR + depends on RT_MFD_RK8XX + select RT_USING_PIN + default y diff --git a/components/drivers/regulator/SConscript b/components/drivers/regulator/SConscript new file mode 100755 index 000000000000..43437e5add03 --- /dev/null +++ b/components/drivers/regulator/SConscript @@ -0,0 +1,30 @@ +from building import * + +group = [] + +if not GetDepend(['RT_USING_REGULATOR']): + Return('group') + +cwd = GetCurrentDir() +CPPPATH = [cwd + '/../include'] + +src = ['regulator.c', 'regulator_dm.c'] + +if GetDepend(['RT_REGULATOR_FAN53555']): + src += ['regulator-fan53555.c'] + +if GetDepend(['RT_REGULATOR_FIXED']): + src += ['regulator-fixed.c'] + +if GetDepend(['RT_REGULATOR_GPIO']): + src += ['regulator-gpio.c'] + +if GetDepend(['RT_REGULATOR_PWM']): + src += ['regulator-pwm.c'] + +if GetDepend(['RT_REGULATOR_RK8XX']): + src += ['regulator-rk8xx.c'] + +group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') diff --git a/components/drivers/regulator/regulator-fan53555.c b/components/drivers/regulator/regulator-fan53555.c new file mode 100644 index 000000000000..06b5a6f097aa --- /dev/null +++ b/components/drivers/regulator/regulator-fan53555.c @@ -0,0 +1,1024 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-11-26 GuEe-GUI first version + */ + +#define DBG_TAG "regulator.rk860x" +#define DBG_LVL DBG_INFO +#include + +#include "regulator_dm.h" + +/* Voltage setting */ +#define FAN53555_VSEL0 0x00 +#define FAN53555_VSEL1 0x01 + +#define TCS4525_VSEL0 0x11 +#define TCS4525_VSEL1 0x10 +#define TCS4525_TIME 0x13 +#define TCS4525_COMMAND 0x14 +#define TCS4525_LIMCONF 0x16 + +#define RK8600_VSEL0 FAN53555_VSEL0 +#define RK8600_VSEL1 FAN53555_VSEL1 +#define RK8602_VSEL0 0x06 +#define RK8602_VSEL1 0x07 +#define RK860X_MAX_SET 0x08 + +/* Control register */ +#define FAN53555_CONTROL 0x02 +/* IC Type */ +#define FAN53555_ID1 0x03 +/* IC mask version */ +#define FAN53555_ID2 0x04 +/* Monitor register */ +#define FAN53555_MONITOR 0x05 + +/* VSEL bit definitions */ +#define VSEL_BUCK_EN RT_BIT(7) +#define VSEL_MODE RT_BIT(6) +#define RK8600_VSEL_NSEL_MASK 0x3f +#define RK8602_VSEL_NSEL_MASK 0xff +/* Chip ID */ +#define DIE_ID 0x0f +#define DIE_REV 0x0f +/* Control bit definitions */ +#define CTL_OUTPUT_DISCHG RT_BIT(7) +#define CTL_SLEW_MASK (0x7 << 4) +#define CTL_SLEW_SHIFT 4 +#define CTL_RESET RT_BIT(2) +#define CTL_MODE_VSEL0_MODE RT_BIT(0) +#define CTL_MODE_VSEL1_MODE RT_BIT(1) + +#define FAN53555_NVOLTAGES 64 /* Numbers of voltages */ +#define FAN53526_NVOLTAGES 128 +#define RK8600_NVOLTAGES FAN53555_NVOLTAGES +#define RK8602_NVOLTAGES 160 + +#define TCS_VSEL0_MODE RT_BIT(7) +#define TCS_VSEL1_MODE RT_BIT(6) + +#define TCS_SLEW_MASK (0x3 << 3) +#define TCS_SLEW_SHIFT 3 + +/* VSEL ID */ +enum +{ + FAN53555_VSEL_ID_0 = 0, + FAN53555_VSEL_ID_1, +}; + +enum fan53555_vendor +{ + FAN53526_VENDOR_FAIRCHILD = 0, + FAN53555_VENDOR_FAIRCHILD, + RK8600_VENDOR_ROCKCHIP, /* RK8600, RK8601 */ + RK8602_VENDOR_ROCKCHIP, /* RK8602, RK8603 */ + FAN53555_VENDOR_SILERGY, + FAN53526_VENDOR_TCS, +}; + +enum +{ + FAN53526_CHIP_ID_01 = 1, +}; + +enum +{ + FAN53526_CHIP_REV_08 = 8, +}; + +/* IC Type */ +enum +{ + FAN53555_CHIP_ID_00 = 0, + FAN53555_CHIP_ID_01, + FAN53555_CHIP_ID_02, + FAN53555_CHIP_ID_03, + FAN53555_CHIP_ID_04, + FAN53555_CHIP_ID_05, + FAN53555_CHIP_ID_08 = 8, +}; + +enum +{ + RK8600_CHIP_ID_08 = 8, /* RK8600, RK8601 */ + RK8602_CHIP_ID_10 = 10, /* RK8602, RK8603 */ +}; + +enum +{ + TCS4525_CHIP_ID_12 = 12, +}; + +enum +{ + TCS4526_CHIP_ID_00 = 0, +}; + +/* IC mask revision */ +enum +{ + FAN53555_CHIP_REV_00 = 0x3, + FAN53555_CHIP_REV_13 = 0xf, +}; + +enum +{ + SILERGY_SYR82X = 8, + SILERGY_SYR83X = 9, +}; + +struct fan53555_regulator +{ + struct rt_regulator_node parent; + struct rt_regulator_param param; + + struct rt_i2c_client *client; + + rt_ubase_t vsel_pin; + + /* IC Type and Rev */ + int chip_id; + int chip_rev; + enum fan53555_vendor vendor; + + rt_uint32_t limit_volt; + + /* Voltage setting register */ + rt_uint32_t vol_reg; + rt_uint32_t vol_mask; + rt_uint32_t sleep_reg; + rt_uint32_t en_reg; + rt_uint32_t sleep_en_reg; + /* Mode */ + rt_uint32_t mode_reg; + rt_uint32_t mode_mask; + /* Slew rate */ + rt_uint32_t slew_reg; + rt_uint32_t slew_mask; + rt_uint32_t slew_shift; + /* Voltage range and step(linear) */ + rt_uint32_t vsel_min; + rt_uint32_t vsel_step; + rt_uint32_t vsel_count; + + /* Voltage slew rate limiting */ + rt_uint32_t slew_rate; + rt_uint32_t sleep_vsel_id; + + const int *ramp_delay_table; + int ramp_delay_nr; +}; + +#define raw_to_fan53555_regulator(raw) rt_container_of(raw, struct fan53555_regulator, parent) + +static const int slew_rates[] = +{ + 64000, 32000, 16000, 8000, 4000, 2000, 1000, 500, +}; + +static const int tcs_slew_rates[] = +{ + 18700, 9300, 4600, 2300, +}; + +static rt_err_t fan53555_regulator_write(struct fan53555_regulator *freg, + rt_uint8_t reg, rt_uint8_t value) +{ + rt_int32_t res; + struct rt_i2c_msg msg[1]; + rt_uint8_t data[sizeof(reg) + sizeof(value)] = { reg }; + struct rt_i2c_client *client = freg->client; + + rt_memcpy(&data[sizeof(reg)], &value, sizeof(value)); + + msg[0].buf = data; + msg[0].addr = client->client_addr; + msg[0].len = sizeof(data); + msg[0].flags = RT_I2C_WR; + + res = rt_i2c_transfer(client->bus, msg, 1); + + return res > 0 ? RT_EOK : res; +} + +static rt_err_t fan53555_regulator_read(struct fan53555_regulator *freg, + rt_uint8_t reg, rt_uint8_t *values) +{ + rt_int32_t res; + struct rt_i2c_msg msg[2]; + struct rt_i2c_client *client = freg->client; + + msg[0].buf = ® + msg[0].addr = client->client_addr; + msg[0].len = sizeof(reg); + msg[0].flags = RT_I2C_WR; + + msg[1].buf = (rt_uint8_t *)values; + msg[1].addr = client->client_addr; + msg[1].len = sizeof(*values); + msg[1].flags = RT_I2C_RD; + + res = rt_i2c_transfer(client->bus, msg, 2); + + return res > 0 ? RT_EOK : res; +} + +static rt_err_t fan53555_regulator_update_bits(struct fan53555_regulator *freg, + rt_uint8_t reg, rt_uint8_t mask, rt_uint8_t data) +{ + rt_err_t err; + rt_uint8_t old, tmp; + + if ((err = fan53555_regulator_read(freg, reg, &old))) + { + return err; + } + + tmp = old & ~mask; + tmp |= (data & mask); + + return fan53555_regulator_write(freg, reg, tmp); +} + +static rt_err_t fan53555_regulator_enable(struct rt_regulator_node *reg_np) +{ + struct fan53555_regulator *freg = raw_to_fan53555_regulator(reg_np); + + if (freg->vsel_pin >= 0) + { + rt_pin_write(freg->vsel_pin, !freg->sleep_vsel_id); + + return RT_EOK; + } + + return fan53555_regulator_update_bits(freg, freg->en_reg, VSEL_BUCK_EN, VSEL_BUCK_EN); +} + +static rt_err_t fan53555_regulator_disable(struct rt_regulator_node *reg_np) +{ + struct fan53555_regulator *freg = raw_to_fan53555_regulator(reg_np); + + if (freg->vsel_pin >= 0) + { + rt_pin_write(freg->vsel_pin, freg->sleep_vsel_id); + + return RT_EOK; + } + + return fan53555_regulator_update_bits(freg, freg->en_reg, VSEL_BUCK_EN, 0); +} + +static rt_bool_t fan53555_regulator_is_enabled(struct rt_regulator_node *reg_np) +{ + rt_err_t err; + rt_uint8_t val; + struct fan53555_regulator *freg = raw_to_fan53555_regulator(reg_np); + + if (freg->vsel_pin) + { + rt_uint8_t pin_val = rt_pin_read(freg->vsel_pin); + + return freg->sleep_vsel_id ? !pin_val : pin_val; + } + + if ((err = fan53555_regulator_read(freg, freg->en_reg, &val))) + { + LOG_E("Read %s register error = %s", "enable", rt_strerror(err)); + + return RT_FALSE; + } + + if (val & VSEL_BUCK_EN) + { + return RT_TRUE; + } + + return RT_FALSE; +} + +static rt_err_t fan53555_regulator_set_voltage(struct rt_regulator_node *reg_np, + int min_uvolt, int max_uvolt) +{ + int uvolt; + rt_uint8_t selector; + struct fan53555_regulator *freg = raw_to_fan53555_regulator(reg_np); + + if (freg->vsel_count == 1 && freg->vsel_step == 0) + { + if (min_uvolt <= freg->vsel_min && freg->vsel_min <= max_uvolt) + { + return 0; + } + else + { + return -RT_EINVAL; + } + } + + if (!freg->vsel_step) + { + return -RT_EINVAL; + } + + if (min_uvolt < freg->vsel_min) + { + min_uvolt = freg->vsel_min; + } + + selector = RT_DIV_ROUND_UP(min_uvolt - freg->vsel_min, freg->vsel_step); + + if ((rt_int8_t)selector < 0) + { + return selector; + } + + if (selector >= freg->vsel_count) + { + return -RT_EINVAL; + } + + uvolt = freg->vsel_min + (freg->vsel_step * selector); + + if (uvolt < min_uvolt || uvolt > max_uvolt) + { + return -RT_EINVAL; + } + + selector <<= __rt_ffs(freg->vol_mask) - 1; + + return fan53555_regulator_update_bits(freg, freg->vol_reg, freg->vol_mask, selector); +} + +static int fan53555_regulator_get_voltage(struct rt_regulator_node *reg_np) +{ + rt_err_t err; + rt_uint8_t selector; + struct fan53555_regulator *freg = raw_to_fan53555_regulator(reg_np); + + if ((err = fan53555_regulator_read(freg, RK860X_MAX_SET, &selector))) + { + return err; + } + + if (selector >= freg->vsel_count) + { + return -RT_EINVAL; + } + + return freg->vsel_min + (freg->vsel_step * selector); +} + +static rt_err_t fan53555_regulator_set_mode(struct rt_regulator_node *reg_np, + rt_uint32_t mode) +{ + struct fan53555_regulator *freg = raw_to_fan53555_regulator(reg_np); + + switch (mode) + { + case RT_REGULATOR_MODE_FAST: + fan53555_regulator_update_bits(freg, + freg->mode_reg, freg->mode_mask, freg->mode_mask); + break; + + case RT_REGULATOR_MODE_NORMAL: + fan53555_regulator_update_bits(freg, + freg->mode_reg, freg->mode_mask, 0); + break; + + default: + return -RT_EINVAL; + } + + return RT_EOK; +} + +static rt_int32_t fan53555_regulator_get_mode(struct rt_regulator_node *reg_np) +{ + rt_err_t err; + rt_uint8_t val; + struct fan53555_regulator *freg = raw_to_fan53555_regulator(reg_np); + + if ((err = fan53555_regulator_read(freg, freg->mode_reg, &val))) + { + LOG_E("Read %s register error = %s", "mode", rt_strerror(err)); + + return RT_FALSE; + } + + if (val & freg->mode_mask) + { + return RT_REGULATOR_MODE_FAST; + } + else + { + return RT_REGULATOR_MODE_NORMAL; + } +} + +static rt_err_t fan53555_regulator_set_ramp_delay(struct rt_regulator_node *reg_np, + int ramp) +{ + int regval = -1, ramp_delay_nr; + const int *ramp_delay_table; + struct fan53555_regulator *freg = raw_to_fan53555_regulator(reg_np); + + ramp_delay_nr = freg->ramp_delay_nr; + ramp_delay_table = freg->ramp_delay_table; + + for (int i = 0; i < ramp_delay_nr; ++i) + { + if (ramp <= ramp_delay_table[i]) + { + regval = i; + continue; + } + + break; + } + + if (regval < 0) + { + LOG_E("unsupported ramp value %d", ramp); + + return -RT_EINVAL; + } + + return fan53555_regulator_update_bits(freg, freg->slew_reg, + freg->slew_mask, regval << freg->slew_shift); +} + +static const struct rt_regulator_ops fan53555_regulator_switch_ops = +{ + .enable = fan53555_regulator_enable, + .disable = fan53555_regulator_disable, + .is_enabled = fan53555_regulator_is_enabled, + .set_voltage = fan53555_regulator_set_voltage, + .get_voltage = fan53555_regulator_get_voltage, + .set_mode = fan53555_regulator_set_mode, + .get_mode = fan53555_regulator_get_mode, + .set_ramp_delay = fan53555_regulator_set_ramp_delay, +}; + +#ifdef RT_USING_OFW +static rt_err_t fan53555_regulator_parse_ofw(struct fan53555_regulator *freg, + struct rt_device *dev) +{ + rt_err_t err; + struct rt_ofw_node *np = dev->ofw_node; + + if ((err = regulator_ofw_parse(np, &freg->param))) + { + return err; + } + + rt_ofw_prop_read_u32(np, "limit-microvolt", &freg->limit_volt); + + rt_ofw_prop_read_u32(np, "fcs,suspend-voltage-selector", &freg->sleep_vsel_id); + rt_ofw_prop_read_u32(np, "rockchip,suspend-voltage-selector", &freg->sleep_vsel_id); + + freg->vsel_pin = rt_ofw_get_named_pin(np, "vsel", 0, RT_NULL, RT_NULL); + + if (freg->vsel_pin < 0 && freg->vsel_pin != -RT_EEMPTY) + { + return freg->vsel_pin; + } + + return RT_EOK; +} +#else +static rt_err_t fan53555_regulator_parse_ofw(struct fan53555_regulator *freg, + struct rt_device *dev) +{ + return RT_EOK; +} +#endif /* RT_USING_OFW */ + +static rt_err_t fan53555_regulator_probe(struct rt_i2c_client *client) +{ + rt_err_t err; + rt_uint8_t val; + struct rt_regulator_node *rgp; + struct rt_device *dev = &client->parent; + struct fan53555_regulator *freg = rt_calloc(1, sizeof(*freg)); + + if (!freg) + { + return -RT_ENOMEM; + } + + if ((err = fan53555_regulator_parse_ofw(freg, dev))) + { + goto _fail; + } + + if (dev->ofw_node) + { + freg->vendor = (rt_ubase_t)client->ofw_id->data; + } + else + { + if (!freg->param.ramp_delay) + { + int slew_idx = freg->slew_rate; + + if (slew_idx > RT_ARRAY_SIZE(slew_rates)) + { + LOG_E("Invalid slew rate"); + + err = -RT_EINVAL; + goto _fail; + } + + freg->param.ramp_delay = slew_rates[slew_idx]; + } + + freg->vendor = (rt_ubase_t)client->id->data; + } + + /* Get chip ID */ + if ((err = fan53555_regulator_read(freg, FAN53555_ID1, &val))) + { + goto _fail; + } + freg->chip_id = val & DIE_ID; + + /* Get chip revision */ + if ((err = fan53555_regulator_read(freg, FAN53555_ID2, &val))) + { + goto _fail; + } + freg->chip_rev = val & DIE_REV; + + if (freg->sleep_vsel_id > FAN53555_VSEL_ID_1) + { + LOG_E("Invalid vsel-id = %d", freg->sleep_vsel_id); + goto _fail; + } + + /* + * For 00,01 options: + * VOUT = 0.7125V + NSELx * 12.5mV, from 0.7125 to 1.5V. + * For 02,03 options: + * VOUT = 0.5V + NSELx * 6.25mV, from 0.5 to 1.5V. + */ + + /* Setup voltage control register */ + freg->vol_reg = 0xff; + switch (freg->vendor) + { + case FAN53526_VENDOR_FAIRCHILD: + case FAN53555_VENDOR_FAIRCHILD: + case FAN53555_VENDOR_SILERGY: + switch (freg->sleep_vsel_id) + { + case FAN53555_VSEL_ID_0: + freg->sleep_reg = FAN53555_VSEL0; + freg->vol_reg = FAN53555_VSEL1; + break; + + case FAN53555_VSEL_ID_1: + freg->sleep_reg = FAN53555_VSEL1; + freg->vol_reg = FAN53555_VSEL0; + break; + + default: + break; + } + freg->sleep_en_reg = freg->sleep_reg; + freg->en_reg = freg->vol_reg; + break; + + case RK8600_VENDOR_ROCKCHIP: + switch (freg->sleep_vsel_id) + { + case FAN53555_VSEL_ID_0: + freg->sleep_reg = RK8600_VSEL0; + freg->vol_reg = RK8600_VSEL1; + break; + + case FAN53555_VSEL_ID_1: + freg->sleep_reg = RK8600_VSEL1; + freg->vol_reg = RK8600_VSEL0; + break; + + default: + break; + } + freg->sleep_en_reg = freg->sleep_reg; + freg->en_reg = freg->vol_reg; + freg->vol_reg = RK8600_VSEL_NSEL_MASK; + break; + + case RK8602_VENDOR_ROCKCHIP: + switch (freg->sleep_vsel_id) + { + case FAN53555_VSEL_ID_0: + freg->sleep_reg = RK8602_VSEL0; + freg->vol_reg = RK8602_VSEL1; + freg->sleep_en_reg = RK8600_VSEL0; + freg->en_reg = RK8600_VSEL1; + break; + + case FAN53555_VSEL_ID_1: + freg->sleep_reg = RK8602_VSEL1; + freg->vol_reg = RK8602_VSEL0; + freg->sleep_en_reg = RK8600_VSEL1; + freg->en_reg = RK8600_VSEL0; + break; + + default: + break; + } + freg->vol_reg = RK8602_VSEL_NSEL_MASK; + break; + + case FAN53526_VENDOR_TCS: + switch (freg->sleep_vsel_id) + { + case FAN53555_VSEL_ID_0: + freg->sleep_reg = TCS4525_VSEL0; + freg->vol_reg = TCS4525_VSEL1; + break; + + case FAN53555_VSEL_ID_1: + freg->sleep_reg = TCS4525_VSEL1; + freg->vol_reg = TCS4525_VSEL0; + break; + + default: + break; + } + freg->sleep_en_reg = freg->sleep_reg; + freg->en_reg = freg->vol_reg; + break; + + default: + break; + } + + /* Setup mode control register */ + switch (freg->vendor) + { + case FAN53526_VENDOR_FAIRCHILD: + freg->mode_reg = FAN53555_CONTROL; + + switch (freg->sleep_vsel_id) + { + case FAN53555_VSEL_ID_0: + freg->mode_mask = CTL_MODE_VSEL1_MODE; + break; + + case FAN53555_VSEL_ID_1: + freg->mode_mask = CTL_MODE_VSEL0_MODE; + break; + + default: + break; + } + break; + case FAN53555_VENDOR_FAIRCHILD: + case FAN53555_VENDOR_SILERGY: + case RK8600_VENDOR_ROCKCHIP: + freg->mode_reg = freg->vol_reg; + freg->mode_mask = VSEL_MODE; + break; + + case RK8602_VENDOR_ROCKCHIP: + freg->mode_mask = VSEL_MODE; + + switch (freg->sleep_vsel_id) + { + case FAN53555_VSEL_ID_0: + freg->mode_reg = RK8600_VSEL1; + break; + + case FAN53555_VSEL_ID_1: + freg->mode_reg = RK8600_VSEL0; + break; + + default: + break; + } + break; + + case FAN53526_VENDOR_TCS: + freg->mode_reg = TCS4525_COMMAND; + + switch (freg->sleep_vsel_id) + { + case FAN53555_VSEL_ID_0: + freg->mode_mask = TCS_VSEL1_MODE; + break; + + case FAN53555_VSEL_ID_1: + freg->mode_mask = TCS_VSEL0_MODE; + break; + + default: + break; + } + break; + + default: + break; + } + + /* Setup voltage range */ + switch (freg->vendor) + { + case FAN53526_VENDOR_FAIRCHILD: + /* Init voltage range and step */ + switch (freg->chip_id) + { + case FAN53526_CHIP_ID_01: + switch (freg->chip_rev) + { + case FAN53526_CHIP_REV_08: + freg->vsel_min = 600000; + freg->vsel_step = 6250; + break; + + default: + LOG_E("Chip ID %d with rev %d not supported", + freg->chip_id, freg->chip_rev); + err = -RT_EINVAL; + goto _fail; + } + break; + + default: + LOG_E("Chip ID %d not supported", freg->chip_id); + err = -RT_EINVAL; + goto _fail; + } + + freg->slew_reg = FAN53555_CONTROL; + freg->slew_mask = CTL_SLEW_MASK; + freg->slew_shift = CTL_SLEW_SHIFT; + freg->ramp_delay_table = slew_rates; + freg->ramp_delay_nr = RT_ARRAY_SIZE(slew_rates); + freg->vsel_count = FAN53526_NVOLTAGES; + break; + + case FAN53555_VENDOR_FAIRCHILD: + /* Init voltage range and step */ + switch (freg->chip_id) { + case FAN53555_CHIP_ID_00: + switch (freg->chip_rev) + { + case FAN53555_CHIP_REV_00: + freg->vsel_min = 600000; + freg->vsel_step = 10000; + break; + + case FAN53555_CHIP_REV_13: + freg->vsel_min = 800000; + freg->vsel_step = 10000; + break; + + default: + LOG_E("Chip ID %d with rev %d not supported", + freg->chip_id, freg->chip_rev); + err = -RT_EINVAL; + goto _fail; + } + break; + case FAN53555_CHIP_ID_01: + case FAN53555_CHIP_ID_03: + case FAN53555_CHIP_ID_05: + case FAN53555_CHIP_ID_08: + freg->vsel_min = 600000; + freg->vsel_step = 10000; + break; + + case FAN53555_CHIP_ID_04: + freg->vsel_min = 603000; + freg->vsel_step = 12826; + break; + + default: + LOG_E("Chip ID %d not supported", freg->chip_id); + err = -RT_EINVAL; + goto _fail; + } + + freg->slew_reg = FAN53555_CONTROL; + freg->slew_mask = CTL_SLEW_MASK; + freg->slew_shift = CTL_SLEW_SHIFT; + freg->ramp_delay_table = slew_rates; + freg->ramp_delay_nr = RT_ARRAY_SIZE(slew_rates); + freg->vsel_count = FAN53555_NVOLTAGES; + break; + + case FAN53555_VENDOR_SILERGY: + /* Init voltage range and step */ + switch (freg->chip_id) + { + case SILERGY_SYR82X: + case SILERGY_SYR83X: + freg->vsel_min = 712500; + freg->vsel_step = 12500; + break; + + default: + LOG_E("Chip ID %d not supported", freg->chip_id); + err = -RT_EINVAL; + goto _fail; + } + + freg->slew_reg = FAN53555_CONTROL; + freg->slew_mask = CTL_SLEW_MASK; + freg->slew_shift = CTL_SLEW_SHIFT; + freg->ramp_delay_table = slew_rates; + freg->ramp_delay_nr = RT_ARRAY_SIZE(slew_rates); + freg->vsel_count = FAN53555_NVOLTAGES; + break; + + case RK8600_VENDOR_ROCKCHIP: + /* Init voltage range and step */ + switch (freg->chip_id) + { + case RK8600_CHIP_ID_08: + freg->vsel_min = 712500; + freg->vsel_step = 12500; + break; + + default: + LOG_E("Chip ID %d not supported", freg->chip_id); + err = -RT_EINVAL; + goto _fail; + } + + freg->slew_reg = FAN53555_CONTROL; + freg->slew_mask = CTL_SLEW_MASK; + freg->slew_shift = CTL_SLEW_SHIFT; + freg->ramp_delay_table = slew_rates; + freg->ramp_delay_nr = RT_ARRAY_SIZE(slew_rates); + freg->vsel_count = RK8600_NVOLTAGES; + break; + + case RK8602_VENDOR_ROCKCHIP: + /* Init voltage range and step */ + switch (freg->chip_id) + { + case RK8602_CHIP_ID_10: + freg->vsel_min = 500000; + freg->vsel_step = 6250; + break; + + default: + LOG_E("Chip ID %d not supported", freg->chip_id); + err = -RT_EINVAL; + goto _fail; + } + + freg->slew_reg = FAN53555_CONTROL; + freg->slew_mask = CTL_SLEW_MASK; + freg->slew_shift = CTL_SLEW_SHIFT; + freg->ramp_delay_table = slew_rates; + freg->ramp_delay_nr = RT_ARRAY_SIZE(slew_rates); + freg->vsel_count = RK8602_NVOLTAGES; + break; + + case FAN53526_VENDOR_TCS: + switch (freg->chip_id) + { + case TCS4525_CHIP_ID_12: + case TCS4526_CHIP_ID_00: + freg->slew_reg = TCS4525_TIME; + freg->slew_mask = TCS_SLEW_MASK; + freg->ramp_delay_table = tcs_slew_rates; + freg->ramp_delay_nr = RT_ARRAY_SIZE(tcs_slew_rates); + + /* Init voltage range and step */ + freg->vsel_min = 600000; + freg->vsel_step = 6250; + freg->vsel_count = FAN53526_NVOLTAGES; + break; + + default: + LOG_E("Chip ID %d not supported", freg->chip_id); + err = -RT_EINVAL; + goto _fail; + } + break; + + default: + break; + } + + if (freg->limit_volt) + { + if (freg->limit_volt < freg->vsel_min || freg->limit_volt > 1500000) + { + freg->limit_volt = 1500000; + } + + val = (freg->limit_volt - freg->vsel_min) / freg->vsel_step; + err = fan53555_regulator_write(freg, RK860X_MAX_SET, val); + + if (err) + { + LOG_E("%s: Failed to set limit voltage", client->name); + goto _fail; + } + } + + rgp = &freg->parent; + rgp->ops = &fan53555_regulator_switch_ops; + rgp->param = &freg->param; + rgp->dev = &client->parent; + + freg->param.enable_delay = 400; + + client->parent.user_data = freg; + + if ((err = rt_regulator_register(rgp))) + { + goto _fail; + } + + return RT_EOK; + +_fail: + rt_free(freg); + + return err; +} + +static rt_err_t fan53555_regulator_shutdown(struct rt_i2c_client *client) +{ + rt_err_t err = RT_EOK; + struct fan53555_regulator *freg = client->parent.user_data; + + switch (freg->vendor) + { + case FAN53555_VENDOR_FAIRCHILD: + case FAN53555_VENDOR_SILERGY: + err = fan53555_regulator_update_bits(freg, freg->slew_reg, CTL_RESET, CTL_RESET); + break; + + case FAN53526_VENDOR_TCS: + err = fan53555_regulator_update_bits(freg, TCS4525_LIMCONF, CTL_RESET, CTL_RESET); + break; + + default: + break; + } + + if (err < 0) + { + LOG_E("Shutdown error = %s", rt_strerror(err)); + } + + return RT_EOK; +} + +static const struct rt_i2c_device_id fan53555_regulator_ids[] = +{ + { .name = "fan53526", .data = (void *)FAN53526_VENDOR_FAIRCHILD }, + { .name = "fan53555", .data = (void *)FAN53555_VENDOR_FAIRCHILD }, + { .name = "rk8600", .data = (void *)RK8600_VENDOR_ROCKCHIP }, + { .name = "rk8601", .data = (void *)RK8600_VENDOR_ROCKCHIP }, + { .name = "rk8602", .data = (void *)RK8602_VENDOR_ROCKCHIP }, + { .name = "rk8603", .data = (void *)RK8602_VENDOR_ROCKCHIP }, + { .name = "syr827", .data = (void *)FAN53555_VENDOR_SILERGY }, + { .name = "syr828", .data = (void *)FAN53555_VENDOR_SILERGY }, + { .name = "tcs4525", .data = (void *)FAN53526_VENDOR_TCS }, + { .name = "tcs4526", .data = (void *)FAN53526_VENDOR_TCS }, + { .name = "tcs452x", .data = (void *)FAN53526_VENDOR_TCS }, + { /* sentinel */ }, +}; + +static const struct rt_ofw_node_id fan53555_regulator_ofw_ids[] = +{ + { .compatible = "fcs,fan53526", .data = (void *)FAN53526_VENDOR_FAIRCHILD }, + { .compatible = "fcs,fan53555", .data = (void *)FAN53555_VENDOR_FAIRCHILD }, + { .compatible = "rockchip,rk8600", .data = (void *)RK8600_VENDOR_ROCKCHIP }, + { .compatible = "rockchip,rk8601", .data = (void *)RK8600_VENDOR_ROCKCHIP }, + { .compatible = "rockchip,rk8602", .data = (void *)RK8602_VENDOR_ROCKCHIP }, + { .compatible = "rockchip,rk8603", .data = (void *)RK8602_VENDOR_ROCKCHIP }, + { .compatible = "silergy,syr827", .data = (void *)FAN53555_VENDOR_SILERGY }, + { .compatible = "silergy,syr828", .data = (void *)FAN53555_VENDOR_SILERGY }, + { .compatible = "tcs,tcs4525", .data = (void *)FAN53526_VENDOR_TCS }, + { .compatible = "tcs,tcs4526", .data = (void *)FAN53526_VENDOR_TCS }, + { .compatible = "tcs,tcs452x", .data = (void *)FAN53526_VENDOR_TCS }, + { /* sentinel */ }, +}; + +static struct rt_i2c_driver fan53555_regulator_driver = +{ + .ids = fan53555_regulator_ids, + .ofw_ids = fan53555_regulator_ofw_ids, + + .probe = fan53555_regulator_probe, + .shutdown = fan53555_regulator_shutdown, +}; +RT_I2C_DRIVER_EXPORT(fan53555_regulator_driver); diff --git a/components/drivers/regulator/regulator-fixed.c b/components/drivers/regulator/regulator-fixed.c new file mode 100644 index 000000000000..47aab7607a80 --- /dev/null +++ b/components/drivers/regulator/regulator-fixed.c @@ -0,0 +1,170 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-09-23 GuEe-GUI first version + */ + +#include "regulator_dm.h" + +struct regulator_fixed +{ + struct rt_regulator_node parent; + struct rt_regulator_param param; + + rt_size_t enable_cnt; + rt_base_t enable_pin; + const char *input_supply; +}; + +#define raw_to_regulator_fixed(raw) rt_container_of(raw, struct regulator_fixed, parent) + +static rt_err_t regulator_fixed_enable(struct rt_regulator_node *reg_np) +{ + struct regulator_fixed *rf = raw_to_regulator_fixed(reg_np); + struct rt_regulator_param *param = &rf->param; + + if (rf->enable_pin < 0 || param->always_on) + { + return RT_EOK; + } + + if (rf->enable_cnt == 0) + { + rt_pin_write(rf->enable_pin, param->enable_active_high ? PIN_HIGH : PIN_LOW); + } + ++rf->enable_cnt; + + return RT_EOK; +} + +static rt_err_t regulator_fixed_disable(struct rt_regulator_node *reg_np) +{ + struct regulator_fixed *rf = raw_to_regulator_fixed(reg_np); + struct rt_regulator_param *param = &rf->param; + + if (rf->enable_pin < 0 || param->always_on) + { + return RT_EOK; + } + + if (rf->enable_cnt == 1) + { + rt_pin_write(rf->enable_pin, param->enable_active_high ? PIN_LOW: PIN_HIGH); + } + --rf->enable_cnt; + + return RT_EOK; +} + +static rt_bool_t regulator_fixed_is_enabled(struct rt_regulator_node *reg_np) +{ + rt_uint8_t active; + struct regulator_fixed *rf = raw_to_regulator_fixed(reg_np); + struct rt_regulator_param *param = &rf->param; + + if (rf->enable_pin < 0 || param->always_on) + { + return RT_TRUE; + } + + active = rt_pin_read(rf->enable_pin); + + if (param->enable_active_high) + { + return active == PIN_HIGH; + } + + return active == PIN_LOW; +} + +static int regulator_fixed_get_voltage(struct rt_regulator_node *reg_np) +{ + struct regulator_fixed *rf = raw_to_regulator_fixed(reg_np); + + return rf->param.min_uvolt + (rf->param.max_uvolt - rf->param.min_uvolt) / 2; +} + +static const struct rt_regulator_ops regulator_fixed_ops = +{ + .enable = regulator_fixed_enable, + .disable = regulator_fixed_disable, + .is_enabled = regulator_fixed_is_enabled, + .get_voltage = regulator_fixed_get_voltage, +}; + +static rt_err_t regulator_fixed_probe(struct rt_platform_device *pdev) +{ + rt_err_t err; + rt_uint32_t val; + struct rt_device *dev = &pdev->parent; + struct regulator_fixed *rf = rt_malloc(sizeof(*rf)); + struct rt_regulator_node *rnp; + + if (!rf) + { + return -RT_ENOMEM; + } + + regulator_ofw_parse(dev->ofw_node, &rf->param); + + rnp = &rf->parent; + rnp->supply_name = rf->param.name; + rnp->ops = ®ulator_fixed_ops; + rnp->param = &rf->param; + rnp->dev = &pdev->parent; + + rf->enable_pin = rt_pin_get_named_pin(dev, "gpio", 0, RT_NULL, RT_NULL); + + if (rf->enable_pin < 0) + { + rf->enable_pin = -1; + } + + if (!rt_dm_dev_prop_read_u32(dev, "startup-delay-us", &val)) + { + rf->param.enable_delay = val; + } + + if (!rt_dm_dev_prop_read_u32(dev, "off-on-delay-us", &val)) + { + rf->param.off_on_delay = val; + } + + if (rt_dm_dev_prop_read_bool(dev, "vin-supply")) + { + rf->input_supply = "vin"; + } + + if ((err = rt_regulator_register(rnp))) + { + rt_free(rf); + } + + return RT_EOK; +} + +static const struct rt_ofw_node_id regulator_fixed_ofw_ids[] = +{ + { .compatible = "regulator-fixed" }, + { /* sentinel */ } +}; + +static struct rt_platform_driver regulator_fixed_driver = +{ + .name = "reg-fixed-voltage", + .ids = regulator_fixed_ofw_ids, + + .probe = regulator_fixed_probe, +}; + +static int regulator_fixed_register(void) +{ + rt_platform_driver_register(®ulator_fixed_driver); + + return 0; +} +INIT_SUBSYS_LATER_EXPORT(regulator_fixed_register); diff --git a/components/drivers/regulator/regulator-gpio.c b/components/drivers/regulator/regulator-gpio.c new file mode 100644 index 000000000000..15e7e6dac7d5 --- /dev/null +++ b/components/drivers/regulator/regulator-gpio.c @@ -0,0 +1,329 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-09-23 GuEe-GUI first version + */ + +#include + +#include "regulator_dm.h" + +struct regulator_gpio_state +{ + rt_uint32_t value; + rt_uint32_t gpios; +}; + +struct regulator_gpio_desc +{ + rt_base_t pin; + rt_uint32_t flags; +}; + +struct regulator_gpio +{ + struct rt_regulator_node parent; + + rt_size_t enable_cnt; + rt_base_t enable_pin; + + rt_size_t pins_nr; + struct regulator_gpio_desc *pins_desc; + + int state; + rt_size_t states_nr; + struct regulator_gpio_state *states; + + const char *input_supply; + rt_uint32_t startup_delay; + rt_uint32_t off_on_delay; + rt_bool_t enabled_at_boot; + struct rt_regulator_param param; +}; + +#define raw_to_regulator_gpio(raw) rt_container_of(raw, struct regulator_gpio, parent) + +static rt_err_t regulator_gpio_enable(struct rt_regulator_node *reg_np) +{ + struct regulator_gpio *rg = raw_to_regulator_gpio(reg_np); + struct rt_regulator_param *param = &rg->param; + + if (rg->enable_pin < 0 || param->always_on) + { + return RT_EOK; + } + + if (rg->enable_cnt == 0) + { + rt_pin_write(rg->enable_pin, param->enable_active_high ? PIN_HIGH : PIN_LOW); + } + ++rg->enable_cnt; + + return RT_EOK; +} + +static rt_err_t regulator_gpio_disable(struct rt_regulator_node *reg_np) +{ + struct regulator_gpio *rg = raw_to_regulator_gpio(reg_np); + struct rt_regulator_param *param = &rg->param; + + if (rg->enable_pin < 0 || param->always_on) + { + return RT_EOK; + } + + if (rg->enable_cnt == 1) + { + rt_pin_write(rg->enable_pin, param->enable_active_high ? PIN_LOW: PIN_HIGH); + } + --rg->enable_cnt; + + return RT_EOK; +} + +static rt_bool_t regulator_gpio_is_enabled(struct rt_regulator_node *reg_np) +{ + rt_uint8_t active; + struct regulator_gpio *rg = raw_to_regulator_gpio(reg_np); + struct rt_regulator_param *param = &rg->param; + + if (rg->enable_pin < 0 || param->always_on) + { + return RT_TRUE; + } + + active = rt_pin_read(rg->enable_pin); + + if (param->enable_active_high) + { + return active == PIN_HIGH; + } + + return active == PIN_LOW; +} + +static rt_err_t regulator_gpio_set_voltage(struct rt_regulator_node *reg_np, + int min_uvolt, int max_uvolt) +{ + int target = 0, best_val = RT_REGULATOR_UVOLT_INVALID; + struct regulator_gpio *rg = raw_to_regulator_gpio(reg_np); + + for (int i = 0; i < rg->states_nr; ++i) + { + struct regulator_gpio_state *state = &rg->states[i]; + + if (state->value < best_val && + state->value >= min_uvolt && + state->value <= max_uvolt) + { + target = state->gpios; + best_val = state->value; + } + } + + if (best_val == RT_REGULATOR_UVOLT_INVALID) + { + return -RT_EINVAL; + } + + for (int i = 0; i < rg->pins_nr; ++i) + { + int state = (target >> i) & 1; + + if (!rg->param.enable_active_high) + { + state = !state; + } + + rt_pin_write(rg->pins_desc[i].pin, state); + } + + rg->state = target; + + + return RT_EOK; +} + +static int regulator_gpio_get_voltage(struct rt_regulator_node *reg_np) +{ + struct regulator_gpio *rg = raw_to_regulator_gpio(reg_np); + + for (int i = 0; i < rg->states_nr; ++i) + { + if (rg->states[i].gpios == rg->state) + { + return rg->states[i].value; + } + } + + return -RT_EINVAL; +} + +static const struct rt_regulator_ops regulator_gpio_ops = +{ + .enable = regulator_gpio_enable, + .disable = regulator_gpio_disable, + .is_enabled = regulator_gpio_is_enabled, + .set_voltage = regulator_gpio_set_voltage, + .get_voltage = regulator_gpio_get_voltage, +}; + +static rt_err_t regulator_gpio_probe(struct rt_platform_device *pdev) +{ + rt_err_t err; + struct rt_device *dev = &pdev->parent; + struct regulator_gpio *rg = rt_calloc(1, sizeof(*rg)); + struct rt_regulator_node *rgp; + + if (!rg) + { + return -RT_ENOMEM; + } + + regulator_ofw_parse(dev->ofw_node, &rg->param); + + rgp = &rg->parent; + rgp->supply_name = rg->param.name; + rgp->ops = ®ulator_gpio_ops; + rgp->param = &rg->param; + rgp->dev = &pdev->parent; + + rt_dm_dev_prop_read_u32(dev, "startup-delay-us", &rg->startup_delay); + rt_dm_dev_prop_read_u32(dev, "off-on-delay-us", &rg->off_on_delay); + + if (rt_dm_dev_prop_read_bool(dev, "vin-supply")) + { + rg->input_supply = "vin"; + } + + rg->enable_pin = rt_pin_get_named_pin(dev, "enable-gpios", 0, RT_NULL, RT_NULL); + + if (rg->enable_pin < 0) + { + rg->enable_pin = -1; + } + + rg->pins_nr = rt_pin_get_named_pin_count(dev, "gpios"); + + if (rg->pins_nr < 0) + { + err = rg->pins_nr; + + goto _fail; + } + + if (rg->pins_nr > 0) + { + rg->pins_desc = rt_malloc(sizeof(*rg->pins_desc) * rg->pins_nr); + + if (!rg->pins_desc) + { + err = -RT_ENOMEM; + + goto _fail; + } + + for (int i = 0; i < rg->pins_nr; ++i) + { + rt_uint32_t val; + struct regulator_gpio_desc *gpiod = &rg->pins_desc[i]; + + gpiod->pin = rt_pin_get_named_pin(dev, "gpios", i, RT_NULL, RT_NULL); + + if (rt_dm_dev_prop_read_u32_index(dev, "gpios-states", i, &val) < 0) + { + gpiod->flags = PIND_OUT_HIGH; + } + else + { + gpiod->flags = val ? PIND_OUT_HIGH : PIND_OUT_LOW; + } + + if (gpiod->flags == PIND_OUT_HIGH) + { + rg->state |= (1 << i); + } + } + } + + rg->states_nr = rt_dm_dev_prop_count_of_u32(dev, "states") / 2; + + if (rg->states_nr < 0) + { + err = -RT_EIO; + + goto _fail; + } + + rg->states = rt_malloc(sizeof(*rg->states) * rg->states_nr); + + if (!rg->states) + { + err = -RT_ENOMEM; + + goto _fail; + } + + for (int i = 0; i < rg->states_nr; ++i) + { + if (rt_dm_dev_prop_read_u32_index(dev, "states", i * 2, &rg->states[i].value) < 0) + { + err = -RT_EIO; + + goto _fail; + } + + if (rt_dm_dev_prop_read_u32_index(dev, "states", i * 2 + 1, &rg->states[i].gpios) < 0) + { + err = -RT_EIO; + + goto _fail; + } + } + + if ((err = rt_regulator_register(rgp))) + { + goto _fail; + } + + return RT_EOK; + +_fail: + if (rg->pins_desc) + { + rt_free(rg->pins_desc); + } + if (rg->states) + { + rt_free(rg->states); + } + rt_free(rg); + + return err; +} + +static const struct rt_ofw_node_id regulator_gpio_ofw_ids[] = +{ + { .compatible = "regulator-gpio" }, + { /* sentinel */ } +}; + +static struct rt_platform_driver regulator_gpio_driver = +{ + .name = "regulator-gpio", + .ids = regulator_gpio_ofw_ids, + + .probe = regulator_gpio_probe, +}; + +static int regulator_gpio_register(void) +{ + rt_platform_driver_register(®ulator_gpio_driver); + + return 0; +} +INIT_SUBSYS_LATER_EXPORT(regulator_gpio_register); diff --git a/components/drivers/regulator/regulator-pwm.c b/components/drivers/regulator/regulator-pwm.c new file mode 100644 index 000000000000..c47c6903b7cd --- /dev/null +++ b/components/drivers/regulator/regulator-pwm.c @@ -0,0 +1,423 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-11-26 GuEe-GUI first version + */ + +#define DBG_TAG "regulator.pwm" +#define DBG_LVL DBG_INFO +#include + +#include "regulator_dm.h" + +struct pwm_continuous_reg_data +{ + rt_uint32_t min_uvolt_dutycycle; + rt_uint32_t max_uvolt_dutycycle; + rt_uint32_t dutycycle_unit; +}; + +struct pwm_voltages +{ + rt_uint32_t uvolt; + rt_uint32_t dutycycle; +}; + +struct pwm_regulator +{ + struct rt_regulator_node parent; + struct rt_regulator_param param; + + rt_bool_t enabled; + struct rt_device_pwm *pwm_dev; + struct rt_pwm_configuration pwm_conf; + struct pwm_voltages *duty_cycle_table; + struct pwm_continuous_reg_data continuous; + + int selector; + rt_uint32_t n_voltages; + + rt_base_t enable_pin; +}; + +#define raw_to_pwm_regulator(raw) rt_container_of(raw, struct pwm_regulator, parent) + +static rt_err_t pwm_regulator_enable(struct rt_regulator_node *reg_np) +{ + rt_err_t err; + struct pwm_regulator *pr = raw_to_pwm_regulator(reg_np); + + if (pr->enable_pin >= 0) + { + rt_pin_write(pr->enable_pin, PIN_HIGH); + } + + if (!(err = rt_pwm_enable(pr->pwm_dev, pr->pwm_conf.channel))) + { + pr->enabled = RT_TRUE; + } + + return err; +} + +static rt_err_t pwm_regulator_disable(struct rt_regulator_node *reg_np) +{ + rt_err_t err; + struct pwm_regulator *pr = raw_to_pwm_regulator(reg_np); + + if ((err = rt_pwm_disable(pr->pwm_dev, pr->pwm_conf.channel))) + { + return err; + } + + pr->enabled = RT_FALSE; + + if (pr->enable_pin >= 0) + { + rt_pin_write(pr->enable_pin, PIN_LOW); + } + + return RT_EOK; +} + +static rt_bool_t pwm_regulator_is_enabled(struct rt_regulator_node *reg_np) +{ + struct pwm_regulator *pr = raw_to_pwm_regulator(reg_np); + + if (pr->enable_pin >= 0 && rt_pin_read(pr->enable_pin) == PIN_LOW) + { + return RT_FALSE; + } + + return pr->enabled; +} + +static rt_err_t pwm_regulator_table_set_voltage(struct rt_regulator_node *reg_np, + int min_uvolt, int max_uvolt) +{ + rt_err_t err; + int selector = -1; + rt_uint32_t duty_cycle; + struct rt_pwm_configuration pwm_conf = {}; + struct pwm_regulator *pr = raw_to_pwm_regulator(reg_np); + + for (int i = 0; i < pr->n_voltages; ++i) + { + int uvolt = pr->duty_cycle_table[i].uvolt; + + if (uvolt >= min_uvolt && uvolt <= max_uvolt) + { + selector = i; + break; + } + } + + if (selector < 0) + { + return -RT_EINVAL; + } + + rt_pwm_get(pr->pwm_dev, &pwm_conf); + pwm_conf.period = pr->pwm_conf.period; + pwm_conf.complementary = pr->pwm_conf.complementary; + + duty_cycle = pr->duty_cycle_table[selector].dutycycle; + duty_cycle = RT_DIV_ROUND_CLOSEST_ULL((rt_uint64_t)duty_cycle * pwm_conf.period, 100); + + pwm_conf.pulse = rt_pwm_conf_pulse(&pwm_conf, duty_cycle); + + if ((err = rt_pwm_set(pr->pwm_dev, pwm_conf.channel, pwm_conf.period, pwm_conf.pulse))) + { + return err; + } + + pr->selector = selector; + + return RT_EOK; +} + +static int pwm_regulator_table_get_voltage(struct rt_regulator_node *reg_np) +{ + struct pwm_regulator *pr = raw_to_pwm_regulator(reg_np); + + return pr->duty_cycle_table[pr->selector].uvolt; +} + +static rt_err_t pwm_regulator_continuous_set_voltage(struct rt_regulator_node *reg_np, + int min_uvolt, int max_uvolt) +{ + struct rt_pwm_configuration pwm_conf = {}; + struct pwm_regulator *pr = raw_to_pwm_regulator(reg_np); + rt_uint32_t min_uvolt_duty = pr->continuous.min_uvolt_dutycycle; + rt_uint32_t max_uvolt_duty = pr->continuous.max_uvolt_dutycycle; + rt_uint32_t duty_unit = pr->continuous.dutycycle_unit, diff_duty, dutycycle; + int fix_min_uvolt = reg_np->param->min_uvolt; + int fix_max_uvolt = reg_np->param->max_uvolt; + int diff_uvolt = fix_max_uvolt - fix_min_uvolt; + + rt_pwm_get(pr->pwm_dev, &pr->pwm_conf); + pwm_conf.period = pr->pwm_conf.period; + pwm_conf.complementary = pr->pwm_conf.complementary; + + if (max_uvolt_duty < min_uvolt_duty) + { + diff_duty = min_uvolt_duty - max_uvolt_duty; + } + else + { + diff_duty = max_uvolt_duty - min_uvolt_duty; + } + + dutycycle = RT_DIV_ROUND_CLOSEST_ULL( + (rt_uint64_t)(min_uvolt - fix_min_uvolt) * diff_duty, diff_uvolt); + + if (max_uvolt_duty < min_uvolt_duty) + { + dutycycle = min_uvolt_duty - dutycycle; + } + else + { + dutycycle = min_uvolt_duty + dutycycle; + } + + pr->pwm_conf.pulse = rt_pwm_conf_pulse(&pwm_conf, + RT_DIV_ROUND_CLOSEST_ULL((rt_uint64_t)dutycycle * pwm_conf.period, duty_unit)); + + return rt_pwm_set(pr->pwm_dev, pwm_conf.channel, pwm_conf.period, pwm_conf.pulse); +} + +static int pwm_regulator_continuous_get_voltage(struct rt_regulator_node *reg_np) +{ + struct pwm_regulator *pr = raw_to_pwm_regulator(reg_np); + rt_uint32_t min_uvolt_duty = pr->continuous.min_uvolt_dutycycle; + rt_uint32_t max_uvolt_duty = pr->continuous.max_uvolt_dutycycle; + rt_uint32_t duty_unit = pr->continuous.dutycycle_unit, diff_duty; + int min_uvolt = reg_np->param->min_uvolt; + int max_uvolt = reg_np->param->max_uvolt; + int uvolt, diff_uvolt = max_uvolt - min_uvolt; + + rt_pwm_get(pr->pwm_dev, &pr->pwm_conf); + + uvolt = pr->pwm_conf.period ? RT_DIV_ROUND_CLOSEST_ULL( + (rt_uint64_t)rt_pwm_conf_duty_cycle(&pr->pwm_conf) * duty_unit, + pr->pwm_conf.period) : 0; + + if (max_uvolt_duty < min_uvolt_duty) + { + uvolt = min_uvolt_duty - uvolt; + diff_duty = min_uvolt_duty - max_uvolt_duty; + } + else + { + uvolt = uvolt - min_uvolt_duty; + diff_duty = max_uvolt_duty - min_uvolt_duty; + } + + uvolt = RT_DIV_ROUND_CLOSEST_ULL((rt_uint64_t)uvolt * diff_uvolt, diff_duty); + + return uvolt + min_uvolt; +} + +static const struct rt_regulator_ops pwm_regulator_voltage_table_ops = +{ + .enable = pwm_regulator_enable, + .disable = pwm_regulator_disable, + .is_enabled = pwm_regulator_is_enabled, + .set_voltage = pwm_regulator_table_set_voltage, + .get_voltage = pwm_regulator_table_get_voltage, +}; + +static const struct rt_regulator_ops pwm_regulator_voltage_continuous_ops = +{ + .enable = pwm_regulator_enable, + .disable = pwm_regulator_disable, + .is_enabled = pwm_regulator_is_enabled, + .set_voltage = pwm_regulator_continuous_set_voltage, + .get_voltage = pwm_regulator_continuous_get_voltage, +}; + +static rt_err_t pwm_regulator_init_table(struct rt_ofw_node *np, + struct pwm_regulator *pr) +{ + rt_err_t err; + rt_ssize_t length = 0; + rt_uint32_t dutycycle; + struct pwm_voltages *duty_cycle_table; + + rt_ofw_prop_read_raw(np, "voltage-table", &length); + + if ((length < sizeof(*duty_cycle_table)) || + (length % sizeof(*duty_cycle_table))) + { + LOG_E("`voltage-table` length = %d is invalid", length); + + return -RT_EINVAL; + } + + duty_cycle_table = rt_calloc(1, length); + + if (!duty_cycle_table) + { + return -RT_ENOMEM; + } + + err = rt_ofw_prop_read_u32_array_index(np, "voltage-table", + 0, length / sizeof(rt_uint32_t), (rt_uint32_t *)duty_cycle_table); + + if (err) + { + rt_free(duty_cycle_table); + + return err; + } + + rt_pwm_get(pr->pwm_dev, &pr->pwm_conf); + + pr->selector = -1; + pr->n_voltages = length / sizeof(*duty_cycle_table); + + dutycycle = pr->pwm_conf.period ? RT_DIV_ROUND_CLOSEST_ULL( + (rt_uint64_t)rt_pwm_conf_duty_cycle(&pr->pwm_conf) * 100, + pr->pwm_conf.period) : 0; + + for (int i = 0; i < pr->n_voltages; ++i) + { + if (dutycycle == pr->duty_cycle_table[i].dutycycle) + { + pr->selector = i; + break; + } + } + + if (pr->selector < 0) + { + rt_free(duty_cycle_table); + + return -RT_EINVAL; + } + + + pr->parent.ops = &pwm_regulator_voltage_table_ops; + + return RT_EOK; +} + +static rt_err_t pwm_regulator_init_continuous(struct rt_ofw_node *np, + struct pwm_regulator *pr) +{ + rt_uint32_t dutycycle_unit = 100, dutycycle_range[2] = { 0, 100 }; + + rt_ofw_prop_read_u32_array_index(np, "pwm-dutycycle-range", 0, 2, + dutycycle_range); + rt_ofw_prop_read_u32(np, "pwm-dutycycle-unit", &dutycycle_unit); + + if (dutycycle_range[0] > dutycycle_unit || + dutycycle_range[1] > dutycycle_unit) + { + return -RT_EINVAL; + } + + pr->continuous.dutycycle_unit = dutycycle_unit; + pr->continuous.min_uvolt_dutycycle = dutycycle_range[0]; + pr->continuous.max_uvolt_dutycycle = dutycycle_range[1]; + + pr->parent.ops = &pwm_regulator_voltage_continuous_ops; + + return RT_EOK; +} + +static rt_err_t pwm_regulator_probe(struct rt_platform_device *pdev) +{ + rt_err_t err; + struct rt_ofw_cell_args pwm_args; + struct rt_ofw_node *np = pdev->parent.ofw_node; + struct pwm_regulator *pr = rt_calloc(1, sizeof(*pr)); + struct rt_regulator_node *rgp; + + if (!pr) + { + return -RT_ENOMEM; + } + + if ((err = regulator_ofw_parse(np, &pr->param))) + { + goto _fail; + } + + if (rt_ofw_parse_phandle_cells(np, "pwms", "#pwm-cells", 0, &pwm_args)) + { + goto _fail; + } + + pr->pwm_dev = rt_ofw_data(pwm_args.data); + rt_ofw_node_put(pwm_args.data); + + if (!pr->pwm_dev) + { + goto _fail; + } + + pr->pwm_conf.channel = pwm_args.args[0]; + pr->pwm_conf.period = pwm_args.args[1]; + pr->pwm_conf.complementary = pwm_args.args[2]; + + pr->enable_pin = rt_ofw_get_named_pin(np, "enable", 0, RT_NULL, RT_NULL); + + if (pr->enable_pin < 0 && pr->enable_pin != -RT_EEMPTY) + { + err = pr->enable_pin; + goto _fail; + } + + rgp = &pr->parent; + rgp->supply_name = pr->param.name; + rgp->param = &pr->param; + rgp->dev = &pdev->parent; + + if (rt_ofw_prop_read_bool(np, "voltage-table")) + { + err = pwm_regulator_init_table(np, pr); + } + else + { + err = pwm_regulator_init_continuous(np, pr); + } + + if (err || (err = rt_regulator_register(rgp))) + { + goto _fail; + } + + return RT_EOK; + +_fail: + rt_free(pr); + + return err; +} + +static const struct rt_ofw_node_id pwm_regulator_ofw_ids[] = +{ + { .compatible = "pwm-regulator" }, + { /* sentinel */ } +}; + +static struct rt_platform_driver pwm_regulator_driver = +{ + .name = "pwm-regulator", + .ids = pwm_regulator_ofw_ids, + + .probe = pwm_regulator_probe, +}; + +static int pwm_regulator_register(void) +{ + rt_platform_driver_register(&pwm_regulator_driver); + + return 0; +} +INIT_DRIVER_EARLY_EXPORT(pwm_regulator_register); diff --git a/components/drivers/regulator/regulator-rk8xx.c b/components/drivers/regulator/regulator-rk8xx.c new file mode 100644 index 000000000000..a3e3a00cfa31 --- /dev/null +++ b/components/drivers/regulator/regulator-rk8xx.c @@ -0,0 +1,1236 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-11-26 GuEe-GUI first version + */ + +#define DBG_TAG "regulator.rk8xx" +#define DBG_LVL DBG_INFO +#include + +#include "regulator_dm.h" +#include "../mfd/rk8xx.h" + +/* Field Definitions */ +#define RK808_BUCK_VSEL_MASK 0x3f +#define RK808_BUCK4_VSEL_MASK 0xf +#define RK808_LDO_VSEL_MASK 0x1f + +#define RK809_BUCK5_VSEL_MASK 0x7 + +#define RK817_LDO_VSEL_MASK 0x7f +#define RK817_BOOST_VSEL_MASK 0x7 +#define RK817_BUCK_VSEL_MASK 0x7f + +#define RK818_BUCK_VSEL_MASK 0x3f +#define RK818_BUCK4_VSEL_MASK 0x1f +#define RK818_LDO_VSEL_MASK 0x1f +#define RK818_LDO3_ON_VSEL_MASK 0xf +#define RK818_BOOST_ON_VSEL_MASK 0xe0 + +#define RK806_DCDC_SLP_REG_OFFSET 0x0a +#define RK806_NLDO_SLP_REG_OFFSET 0x05 +#define RK806_PLDO_SLP_REG_OFFSET 0x06 + +#define RK806_BUCK_SEL_CNT 0xff +#define RK806_LDO_SEL_CNT 0xff + +/* Ramp rate definitions for buck1 / buck2 only */ +#define RK808_RAMP_RATE_OFFSET 3 +#define RK808_RAMP_RATE_MASK (3 << RK808_RAMP_RATE_OFFSET) +#define RK808_RAMP_RATE_2MV_PER_US (0 << RK808_RAMP_RATE_OFFSET) +#define RK808_RAMP_RATE_4MV_PER_US (1 << RK808_RAMP_RATE_OFFSET) +#define RK808_RAMP_RATE_6MV_PER_US (2 << RK808_RAMP_RATE_OFFSET) +#define RK808_RAMP_RATE_10MV_PER_US (3 << RK808_RAMP_RATE_OFFSET) + +#define RK808_DVS2_POL RT_BIT(2) +#define RK808_DVS1_POL RT_BIT(1) + +/* Offset from XXX_ON_VSEL to XXX_SLP_VSEL */ +#define RK808_SLP_REG_OFFSET 1 +/* Offset from XXX_ON_VSEL to XXX_DVS_VSEL */ +#define RK808_DVS_REG_OFFSET 2 +/* max steps for increase voltage of Buck1/2, equal 100mv*/ +#define MAX_STEPS_ONE_TIME 8 +#define MAX_PIN_NR 2 + +#define ENABLE_MASK(id) (RT_BIT(id) | RT_BIT(4 + (id))) +#define DISABLE_VAL(id) (RT_BIT(4 + (id))) + +struct rk8xx_regulator_range +{ + int min; + int min_sel; + int max_sel; + int step; +}; + +#define RK8XX_REGULATOR_RANGE(UVOLT_MIN, MIN_SEL, MAX_SEL, UVOLT_STEP) \ +{ \ + .min = UVOLT_MIN, \ + .min_sel = MIN_SEL, \ + .max_sel = MAX_SEL, \ + .step = UVOLT_STEP, \ +} + +struct rk8xx_regulator_desc +{ + const char *name; + const char *supply_name; + + const struct rt_regulator_ops *ops; + + int voltages_nr; + int uvolt_min; + int uvolt_step; + + int ranges_nr; + const struct rk8xx_regulator_range *ranges; + + rt_uint32_t vsel_reg; + rt_uint32_t vsel_mask; + rt_uint32_t enable_reg; + rt_uint32_t enable_mask; + rt_uint32_t enable_val; + rt_uint32_t disable_val; +}; + +#define RK8XX_REGULATOR_DESC(NAME, SUPPLY_NAME, \ + OPS, \ + VOLTAGES_NR, UVOLT_MIN, UVOLT_STEP, \ + RANGES_NR, RANGES, \ + VSEL_REG, VSEL_MASK, ENABLE_REG, \ + ENABLE_MASK, ENABLE_VAL, DISABLE_VAL) \ +{ \ + .name = NAME, \ + .supply_name = SUPPLY_NAME, \ + .ops = &OPS, \ + .voltages_nr = VOLTAGES_NR, \ + .uvolt_min = UVOLT_MIN, \ + .uvolt_step = UVOLT_STEP, \ + .ranges_nr = RANGES_NR, \ + .ranges = RANGES, \ + .vsel_reg = VSEL_REG, \ + .vsel_mask = VSEL_MASK, \ + .enable_reg = ENABLE_REG, \ + .enable_mask = ENABLE_MASK, \ + .enable_val = ENABLE_VAL, \ + .disable_val = DISABLE_VAL, \ +} + +#define RK8XX_REGULATOR_VOLT(NAME, SUPPLY_NAME, OPS, \ + VOLTAGES_NR, UVOLT_MIN, UVOLT_STEP, \ + VSEL_REG, VSEL_MASK, \ + ENABLE_REG, ENABLE_MASK, ENABLE_VAL, DISABLE_VAL) \ + RK8XX_REGULATOR_DESC(NAME, SUPPLY_NAME, OPS, \ + VOLTAGES_NR, UVOLT_MIN, UVOLT_STEP, \ + 0, RT_NULL, \ + VSEL_REG, VSEL_MASK, \ + ENABLE_REG, ENABLE_MASK, ENABLE_VAL, DISABLE_VAL) + +#define RK8XX_REGULATOR_VOLT_RANGE(NAME, SUPPLY_NAME, OPS, \ + MIN, MAX, STEP, \ + VSEL_REG, VSEL_MASK, \ + ENABLE_REG, ENABLE_MASK, ENABLE_VAL, DISABLE_VAL) \ + RK8XX_REGULATOR_DESC(NAME, SUPPLY_NAME, OPS, \ + (((MAX) - (MIN)) / (STEP) + 1), MIN, STEP, \ + 0, RT_NULL, \ + VSEL_REG, VSEL_MASK, \ + ENABLE_REG, ENABLE_MASK, ENABLE_VAL, DISABLE_VAL) + +#define RK8XX_REGULATOR_VOLT_RANGES(NAME, SUPPLY_NAME, OPS, \ + VOLTAGES_NR, RANGES, \ + VSEL_REG, VSEL_MASK, \ + ENABLE_REG, ENABLE_MASK, ENABLE_VAL, DISABLE_VAL) \ + RK8XX_REGULATOR_DESC(NAME, SUPPLY_NAME, OPS, \ + VOLTAGES_NR, 0, 0, \ + RT_ARRAY_SIZE(RANGES), RANGES, \ + VSEL_REG, VSEL_MASK, \ + ENABLE_REG, ENABLE_MASK, ENABLE_VAL, DISABLE_VAL) + +#define RK8XX_REGULATOR_SWITCH_DESC(NAME, SUPPLY_NAME, OPS, \ + ENABLE_REG, ENABLE_MASK, ENABLE_VAL, DISABLE_VAL) \ + RK8XX_REGULATOR_DESC(NAME, SUPPLY_NAME, OPS, \ + 1, 0, 0, \ + 0, RT_NULL, \ + 0, 0, \ + ENABLE_REG, ENABLE_MASK, ENABLE_VAL, DISABLE_VAL) + +struct rk8xx_regulator +{ + struct rt_regulator_node parent; + struct rt_regulator_param param; + + rt_ubase_t pin; + + struct rt_device device; + struct rk8xx *rk8xx; + struct rt_ofw_node *ofw_node; + + const struct rk8xx_regulator_desc *desc; +}; + +#define raw_to_rk8xx_regulator(raw) rt_container_of(raw, struct rk8xx_regulator, parent) + +static int regulator_voltage_from_selector(struct rk8xx_regulator *rk8xx_reg, + int selector) +{ + int uvolt = -1; + const struct rk8xx_regulator_desc *desc = rk8xx_reg->desc; + + if (desc->ranges_nr) + { + const struct rk8xx_regulator_range *range = &desc->ranges[0]; + + for (int i = desc->ranges_nr - 1; i >= 0; --i, ++range) + { + if (range->min_sel <= selector && range->max_sel >= selector) + { + if (range->min_sel > selector || range->max_sel < selector) + { + return -RT_EINVAL; + } + + uvolt = range->min + (selector - range->min_sel) * range->step; + + break; + } + } + } + else if (desc->voltages_nr) + { + if (selector >= desc->voltages_nr) + { + return -RT_EINVAL; + } + + return desc->uvolt_min + (desc->uvolt_step * selector); + } + else + { + LOG_E("Regulator %s-%s voltages info not found", desc->name, desc->supply_name); + } + + return uvolt; +} + +static int regulator_voltage_to_selector_range(struct rk8xx_regulator *rk8xx_reg, + int min_uvolt, int max_uvolt) +{ + int selector, max, uvolt, i; + const struct rk8xx_regulator_range *range; + const struct rk8xx_regulator_desc *desc = rk8xx_reg->desc; + + if (!desc->ranges) + { + return -RT_EINVAL; + } + + for (i = 0; i < desc->ranges_nr; ++i) + { + range = &desc->ranges[i]; + max = range->min + (range->max_sel - range->min_sel) * range->step; + + if (max < min_uvolt) + { + continue; + } + + if (range->min > min_uvolt) + { + selector = range->min_sel; + } + else if (range->step == 0) + { + selector = range->max_sel; + } + else + { + selector = RT_DIV_ROUND_UP(min_uvolt - range->min, range->step) + + range->min_sel; + } + + uvolt = regulator_voltage_from_selector(rk8xx_reg, selector); + + if (uvolt >= min_uvolt && uvolt <= max_uvolt) + { + break; + } + } + + if (i == desc->ranges_nr) + { + return -RT_EINVAL; + } + + return selector; +} + +static int regulator_voltage_to_selector(struct rk8xx_regulator *rk8xx_reg, + int min_uvolt, int max_uvolt) +{ + int selector, uvolt; + + /* Allow uvolt_step to be 0 for fixed voltage */ + if (rk8xx_reg->desc->voltages_nr == 1 && rk8xx_reg->desc->uvolt_step == 0) + { + if (min_uvolt <= rk8xx_reg->desc->uvolt_min && rk8xx_reg->desc->uvolt_min <= max_uvolt) + { + return 0; + } + else + { + return -RT_EINVAL; + } + } + + if (!rk8xx_reg->desc->uvolt_step) + { + return -RT_EINVAL; + } + + if (min_uvolt < rk8xx_reg->desc->uvolt_min) + { + min_uvolt = rk8xx_reg->desc->uvolt_min; + } + + selector = RT_DIV_ROUND_UP(min_uvolt - rk8xx_reg->desc->uvolt_min, rk8xx_reg->desc->uvolt_step); + + if (selector < 0) + { + return selector; + } + + /* Map back into a voltage to verify we're still in bounds */ + uvolt = regulator_voltage_from_selector(rk8xx_reg, selector); + + if (uvolt < min_uvolt || uvolt > max_uvolt) + { + return -RT_EINVAL; + } + + return selector; +} + +static rt_err_t rk8xx_regulator_enable(struct rt_regulator_node *reg_np) +{ + rt_uint32_t val; + struct rk8xx_regulator *rk8xx_reg = raw_to_rk8xx_regulator(reg_np); + + val = rk8xx_reg->desc->enable_val; + + if (!val) + { + val = rk8xx_reg->desc->enable_mask; + } + + return rk8xx_update_bits(rk8xx_reg->rk8xx, rk8xx_reg->desc->enable_reg, + rk8xx_reg->desc->enable_mask, val); +} + +static rt_err_t rk8xx_regulator_disable(struct rt_regulator_node *reg_np) +{ + rt_uint32_t val; + struct rk8xx_regulator *rk8xx_reg = raw_to_rk8xx_regulator(reg_np); + + val = rk8xx_reg->desc->disable_val; + + return rk8xx_update_bits(rk8xx_reg->rk8xx, rk8xx_reg->desc->enable_reg, + rk8xx_reg->desc->enable_mask, val); +} + +static rt_bool_t rk8xx_regulator_is_enabled(struct rt_regulator_node *reg_np) +{ + rt_uint32_t val; + struct rk8xx_regulator *rk8xx_reg = raw_to_rk8xx_regulator(reg_np); + + val = rk8xx_read(rk8xx_reg->rk8xx, rk8xx_reg->desc->enable_reg); + + if ((rt_err_t)val < 0) + { + return RT_FALSE; + } + + val &= rk8xx_reg->desc->enable_mask; + + if (rk8xx_reg->desc->enable_val) + { + return val == rk8xx_reg->desc->enable_val; + } + + return val != 0; +} + +static rt_err_t rk8xx_regulator_set_voltage(struct rt_regulator_node *reg_np, + int min_uvolt, int max_uvolt) +{ + int selector; + struct rk8xx_regulator *rk8xx_reg = raw_to_rk8xx_regulator(reg_np); + + selector = regulator_voltage_to_selector(rk8xx_reg, min_uvolt, max_uvolt); + + if (selector < 0) + { + if (selector == -RT_EINVAL) + { + selector = regulator_voltage_to_selector_range(rk8xx_reg, + min_uvolt, max_uvolt); + + if (selector < 0) + { + return -RT_EINVAL; + } + } + } + + selector <<= __rt_ffs(rk8xx_reg->desc->vsel_mask) - 1; + + return rk8xx_update_bits(rk8xx_reg->rk8xx, rk8xx_reg->desc->vsel_reg, + rk8xx_reg->desc->vsel_mask, selector); +} + +static int rk8xx_regulator_get_voltage(struct rt_regulator_node *reg_np) +{ + int uvolt; + rt_uint32_t val; + struct rk8xx_regulator *rk8xx_reg = raw_to_rk8xx_regulator(reg_np); + + val = rk8xx_read(rk8xx_reg->rk8xx, rk8xx_reg->desc->vsel_reg); + + if ((rt_err_t)val < 0) + { + return val; + } + + val &= rk8xx_reg->desc->vsel_mask; + val >>= __rt_ffs(rk8xx_reg->desc->vsel_mask) - 1; + + uvolt = regulator_voltage_from_selector(rk8xx_reg, val); + + return uvolt < 0 ? -RT_EINVAL : uvolt; +} + +/* wmsk */ +static rt_bool_t rk8xx_regulator_wmsk_is_enabled(struct rt_regulator_node *reg_np) +{ + rt_uint32_t val; + struct rk8xx_regulator *rk8xx_reg = raw_to_rk8xx_regulator(reg_np); + + val = rk8xx_read(rk8xx_reg->rk8xx, rk8xx_reg->desc->enable_reg); + + if ((rt_err_t)val < 0) + { + return RT_FALSE; + } + + val |= rk8xx_reg->desc->enable_mask & 0xf0; + val &= rk8xx_reg->desc->enable_mask; + + if (rk8xx_reg->desc->enable_val) + { + return val == rk8xx_reg->desc->enable_val; + } + + return val != 0; +} + +/* buck */ +static rt_err_t rk8xx_regulator_buck_set_voltage_no_pin(struct rt_regulator_node *reg_np, + int min_uvolt, int max_uvolt) +{ + rt_err_t err; + int delta_sel, selector; + rt_uint32_t old_sel, tmp, val, mask; + struct rk8xx_regulator *rk8xx_reg = raw_to_rk8xx_regulator(reg_np); + + selector = regulator_voltage_to_selector(rk8xx_reg, min_uvolt, max_uvolt); + + if (selector < 0) + { + if (selector == -RT_EINVAL) + { + selector = regulator_voltage_to_selector_range(rk8xx_reg, + min_uvolt, max_uvolt); + + if (selector < 0) + { + return -RT_EINVAL; + } + } + } + + val = rk8xx_read(rk8xx_reg->rk8xx, rk8xx_reg->desc->vsel_reg); + + if ((rt_err_t)val < 0) + { + return (rt_err_t)val; + } + + mask = rk8xx_reg->desc->vsel_mask; + tmp = val & ~mask; + old_sel = val & mask; + old_sel >>= __rt_ffs(mask) - 1; + delta_sel = selector - old_sel; + + while (delta_sel > MAX_STEPS_ONE_TIME) + { + old_sel += MAX_STEPS_ONE_TIME; + val = old_sel << (__rt_ffs(mask) - 1); + val |= tmp; + + rk8xx_write(rk8xx_reg->rk8xx, rk8xx_reg->desc->vsel_reg, val); + delta_sel = selector - old_sel; + } + + selector <<= __rt_ffs(mask) - 1; + val = tmp | selector; + err = rk8xx_write(rk8xx_reg->rk8xx, rk8xx_reg->desc->vsel_reg, val); + + /* wait 1us to make sure the target voltage to be stable */ + rt_hw_us_delay(1); + + return err; +} + +static rt_err_t rk8xx_regulator_buck_set_voltage(struct rt_regulator_node *reg_np, + int min_uvolt, int max_uvolt) +{ + rt_err_t err; + int selector; + rt_ssize_t gpio_level; + rt_uint32_t reg, old_sel; + struct rk8xx_regulator *rk8xx_reg = raw_to_rk8xx_regulator(reg_np); + + if ((rt_base_t)rk8xx_reg->pin < 0) + { + return rk8xx_regulator_buck_set_voltage_no_pin(reg_np, min_uvolt, max_uvolt); + } + + selector = regulator_voltage_to_selector(rk8xx_reg, min_uvolt, max_uvolt); + + if (selector < 0) + { + if (selector == -RT_EINVAL) + { + selector = regulator_voltage_to_selector_range(rk8xx_reg, + min_uvolt, max_uvolt); + + if (selector < 0) + { + return -RT_EINVAL; + } + } + } + + reg = rk8xx_reg->desc->vsel_reg; + + gpio_level = rt_pin_read(rk8xx_reg->pin); + + if (gpio_level == 0) + { + reg += RK808_DVS_REG_OFFSET; + old_sel = rk8xx_read(rk8xx_reg->rk8xx, rk8xx_reg->desc->vsel_reg); + } + else + { + old_sel = rk8xx_read(rk8xx_reg->rk8xx, reg + RK808_DVS_REG_OFFSET); + } + + if ((rt_err_t)old_sel < 0) + { + return -RT_EIO; + } + + selector <<= __rt_ffs(rk8xx_reg->desc->vsel_mask) - 1; + selector |= old_sel & ~rk8xx_reg->desc->vsel_mask; + + err = rk8xx_write(rk8xx_reg->rk8xx, reg, selector); + + if (err) + { + return err; + } + + rt_pin_write(rk8xx_reg->pin, !gpio_level); + + return err; +} + +static int rk8xx_regulator_buck_get_voltage(struct rt_regulator_node *reg_np) +{ + int uvolt; + rt_uint32_t val; + struct rk8xx_regulator *rk8xx_reg = raw_to_rk8xx_regulator(reg_np); + + if ((rt_base_t)rk8xx_reg->pin < 0 || rt_pin_read(rk8xx_reg->pin) == 0) + { + return rk8xx_regulator_get_voltage(reg_np); + } + + val = rk8xx_read(rk8xx_reg->rk8xx, rk8xx_reg->desc->vsel_reg + RK808_DVS_REG_OFFSET); + + if ((rt_err_t)val < 0) + { + return (int)val; + } + + val &= rk8xx_reg->desc->vsel_mask; + val >>= __rt_ffs(rk8xx_reg->desc->vsel_mask) - 1; + + uvolt = regulator_voltage_from_selector(rk8xx_reg, val); + + return uvolt < 0 ? -RT_EINVAL : uvolt; +} + +static const struct rt_regulator_ops rk8xx_regulator_ops = +{ + .enable = rk8xx_regulator_enable, + .disable = rk8xx_regulator_disable, + .is_enabled = rk8xx_regulator_is_enabled, + .set_voltage = rk8xx_regulator_set_voltage, + .get_voltage = rk8xx_regulator_get_voltage, +}; + +static const struct rt_regulator_ops rk8xx_regulator_wmsk_ops = +{ + .enable = rk8xx_regulator_enable, + .disable = rk8xx_regulator_disable, + .is_enabled = rk8xx_regulator_wmsk_is_enabled, + .set_voltage = rk8xx_regulator_set_voltage, + .get_voltage = rk8xx_regulator_get_voltage, +}; + +static const struct rt_regulator_ops rk8xx_regulator_buck_ops = +{ + .enable = rk8xx_regulator_enable, + .disable = rk8xx_regulator_disable, + .is_enabled = rk8xx_regulator_is_enabled, + .set_voltage = rk8xx_regulator_buck_set_voltage, + .get_voltage = rk8xx_regulator_buck_get_voltage, +}; + +static const struct rt_regulator_ops rk8xx_regulator_switch_ops = +{ + .enable = rk8xx_regulator_enable, + .disable = rk8xx_regulator_disable, + .is_enabled = rk8xx_regulator_is_enabled, +}; + +static const struct rt_regulator_ops rk8xx_regulator_switch_wmsk_ops = +{ + .enable = rk8xx_regulator_enable, + .disable = rk8xx_regulator_disable, + .is_enabled = rk8xx_regulator_wmsk_is_enabled, +}; + +static const struct rk8xx_regulator_range rk805_buck_1_2_voltage_ranges[] = +{ + RK8XX_REGULATOR_RANGE(712500, 0, 59, 12500), + RK8XX_REGULATOR_RANGE(1800000, 60, 62, 200000), + RK8XX_REGULATOR_RANGE(2300000, 63, 63, 0), +}; + +static const struct rk8xx_regulator_desc rk805_desc[] = +{ + RK8XX_REGULATOR_DESC("DCDC_REG1", "vcc1", rk8xx_regulator_ops, + 64, 0, 0, + RT_ARRAY_SIZE(rk805_buck_1_2_voltage_ranges), rk805_buck_1_2_voltage_ranges, + RK805_BUCK1_ON_VSEL_REG, RK818_BUCK_VSEL_MASK, + RK805_DCDC_EN_REG, RT_BIT(0), 0, 0), + RK8XX_REGULATOR_DESC("DCDC_REG2", "vcc2", rk8xx_regulator_ops, + 64, 0, 0, + RT_ARRAY_SIZE(rk805_buck_1_2_voltage_ranges), rk805_buck_1_2_voltage_ranges, + RK805_BUCK2_ON_VSEL_REG, RK818_BUCK_VSEL_MASK, + RK805_DCDC_EN_REG, RT_BIT(1), 0, 0), + RK8XX_REGULATOR_SWITCH_DESC("DCDC_REG3", "vcc3", rk8xx_regulator_switch_ops, + RK805_DCDC_EN_REG, RT_BIT(2), 0, 0), + RK8XX_REGULATOR_VOLT_RANGE("DCDC_REG4", "vcc4", rk8xx_regulator_ops, + 800000, 3400000, 100000, + RK805_BUCK4_ON_VSEL_REG, RK818_BUCK4_VSEL_MASK, + RK805_DCDC_EN_REG, RT_BIT(3), 0, 0), + + RK8XX_REGULATOR_VOLT_RANGE("LDO_REG1", "vcc5", rk8xx_regulator_ops, + 800000, 3400000, 100000, + RK805_LDO1_ON_VSEL_REG, RK818_LDO_VSEL_MASK, + RK805_LDO_EN_REG, RT_BIT(0), 0, 0), + RK8XX_REGULATOR_VOLT_RANGE("LDO_REG2", "vcc5", rk8xx_regulator_ops, + 800000, 3400000, 100000, + RK805_LDO2_ON_VSEL_REG, RK818_LDO_VSEL_MASK, + RK805_LDO_EN_REG, RT_BIT(1), 0, 0), + RK8XX_REGULATOR_VOLT_RANGE("LDO_REG3", "vcc6", rk8xx_regulator_ops, + 800000, 3400000, 100000, + RK805_LDO3_ON_VSEL_REG, RK818_LDO_VSEL_MASK, + RK805_LDO_EN_REG, RT_BIT(2), 0, 0), +}; + +static const struct rk8xx_regulator_range rk806_buck_voltage_ranges[] = +{ + RK8XX_REGULATOR_RANGE(500000, 0, 160, 6250), + RK8XX_REGULATOR_RANGE(1500000, 161, 237, 25000), + RK8XX_REGULATOR_RANGE(3400000, 238, 255, 0), +}; + +static const struct rk8xx_regulator_range rk806_ldo_voltage_ranges[] = +{ + RK8XX_REGULATOR_RANGE(500000, 0, 232, 12500), + RK8XX_REGULATOR_RANGE(3400000, 233, 255, 0), +}; + +static const struct rk8xx_regulator_desc rk806_desc[] = +{ + RK8XX_REGULATOR_VOLT_RANGES("DCDC_REG1", "vcc1", rk8xx_regulator_wmsk_ops, + RK806_BUCK_SEL_CNT, rk806_buck_voltage_ranges, + RK806_BUCK1_ON_VSEL, 0xff, + RK806_POWER_EN0, ENABLE_MASK(0), ENABLE_MASK(0), DISABLE_VAL(0)), + RK8XX_REGULATOR_VOLT_RANGES("DCDC_REG2", "vcc2", rk8xx_regulator_wmsk_ops, + RK806_BUCK_SEL_CNT, rk806_buck_voltage_ranges, + RK806_BUCK2_ON_VSEL, 0xff, + RK806_POWER_EN0, ENABLE_MASK(1), ENABLE_MASK(1), DISABLE_VAL(1)), + RK8XX_REGULATOR_VOLT_RANGES("DCDC_REG3", "vcc3", rk8xx_regulator_wmsk_ops, + RK806_BUCK_SEL_CNT, rk806_buck_voltage_ranges, + RK806_BUCK3_ON_VSEL, 0xff, + RK806_POWER_EN0, ENABLE_MASK(2), ENABLE_MASK(2), DISABLE_VAL(2)), + RK8XX_REGULATOR_VOLT_RANGES("DCDC_REG4", "vcc4", rk8xx_regulator_wmsk_ops, + RK806_BUCK_SEL_CNT, rk806_buck_voltage_ranges, + RK806_BUCK4_ON_VSEL, 0xff, + RK806_POWER_EN0, ENABLE_MASK(3), ENABLE_MASK(3), DISABLE_VAL(3)), + + RK8XX_REGULATOR_VOLT_RANGES("DCDC_REG5", "vcc5", rk8xx_regulator_wmsk_ops, + RK806_BUCK_SEL_CNT, rk806_buck_voltage_ranges, + RK806_BUCK5_ON_VSEL, 0xff, + RK806_POWER_EN1, ENABLE_MASK(0), ENABLE_MASK(0), DISABLE_VAL(0)), + RK8XX_REGULATOR_VOLT_RANGES("DCDC_REG6", "vcc6", rk8xx_regulator_wmsk_ops, + RK806_BUCK_SEL_CNT, rk806_buck_voltage_ranges, + RK806_BUCK6_ON_VSEL, 0xff, + RK806_POWER_EN1, ENABLE_MASK(1), ENABLE_MASK(1), DISABLE_VAL(1)), + RK8XX_REGULATOR_VOLT_RANGES("DCDC_REG7", "vcc7", rk8xx_regulator_wmsk_ops, + RK806_BUCK_SEL_CNT, rk806_buck_voltage_ranges, + RK806_BUCK7_ON_VSEL, 0xff, + RK806_POWER_EN1, ENABLE_MASK(2), ENABLE_MASK(2), DISABLE_VAL(2)), + RK8XX_REGULATOR_VOLT_RANGES("DCDC_REG8", "vcc8", rk8xx_regulator_wmsk_ops, + RK806_BUCK_SEL_CNT, rk806_buck_voltage_ranges, + RK806_BUCK8_ON_VSEL, 0xff, + RK806_POWER_EN1, ENABLE_MASK(3), ENABLE_MASK(3), DISABLE_VAL(3)), + + RK8XX_REGULATOR_VOLT_RANGES("DCDC_REG9", "vcc9", rk8xx_regulator_wmsk_ops, + RK806_BUCK_SEL_CNT, rk806_buck_voltage_ranges, + RK806_BUCK9_ON_VSEL, 0xff, + RK806_POWER_EN2, ENABLE_MASK(0), ENABLE_MASK(0), DISABLE_VAL(0)), + RK8XX_REGULATOR_VOLT_RANGES("DCDC_REG10", "vcc10", rk8xx_regulator_wmsk_ops, + RK806_BUCK_SEL_CNT, rk806_buck_voltage_ranges, + RK806_BUCK10_ON_VSEL, 0xff, + RK806_POWER_EN2, ENABLE_MASK(1), ENABLE_MASK(1), DISABLE_VAL(1)), + + RK8XX_REGULATOR_VOLT_RANGES("PLDO_REG1", "vcc13", rk8xx_regulator_ops, + RK806_LDO_SEL_CNT, rk806_ldo_voltage_ranges, + RK806_NLDO1_ON_VSEL, 0xff, + RK806_POWER_EN3, ENABLE_MASK(0), ENABLE_MASK(0), DISABLE_VAL(0)), + RK8XX_REGULATOR_VOLT_RANGES("PLDO_REG2", "vcc13", rk8xx_regulator_ops, + RK806_LDO_SEL_CNT, rk806_ldo_voltage_ranges, + RK806_NLDO2_ON_VSEL, 0xff, + RK806_POWER_EN3, ENABLE_MASK(1), ENABLE_MASK(1), DISABLE_VAL(1)), + RK8XX_REGULATOR_VOLT_RANGES("PLDO_REG3", "vcc13", rk8xx_regulator_ops, + RK806_LDO_SEL_CNT, rk806_ldo_voltage_ranges, + RK806_NLDO3_ON_VSEL, 0xff, + RK806_POWER_EN3, ENABLE_MASK(2), ENABLE_MASK(2), DISABLE_VAL(2)), + RK8XX_REGULATOR_VOLT_RANGES("PLDO_REG4", "vcc14", rk8xx_regulator_ops, + RK806_LDO_SEL_CNT, rk806_ldo_voltage_ranges, + RK806_NLDO4_ON_VSEL, 0xff, + RK806_POWER_EN3, ENABLE_MASK(3), ENABLE_MASK(3), DISABLE_VAL(3)), + + RK8XX_REGULATOR_VOLT_RANGES("PLDO_REG5", "vcc14", rk8xx_regulator_ops, + RK806_LDO_SEL_CNT, rk806_ldo_voltage_ranges, + RK806_NLDO5_ON_VSEL, 0xff, + RK806_POWER_EN5, ENABLE_MASK(2), ENABLE_MASK(2), DISABLE_VAL(2)), + + RK8XX_REGULATOR_VOLT_RANGES("PLDO_REG6", "vcc11", rk8xx_regulator_ops, + RK806_LDO_SEL_CNT, rk806_ldo_voltage_ranges, + RK806_PLDO1_ON_VSEL, 0xff, + RK806_POWER_EN4, ENABLE_MASK(1), ENABLE_MASK(1), DISABLE_VAL(1)), + RK8XX_REGULATOR_VOLT_RANGES("NLDO_REG1", "vcc11", rk8xx_regulator_ops, + RK806_LDO_SEL_CNT, rk806_ldo_voltage_ranges, + RK806_PLDO2_ON_VSEL, 0xff, + RK806_POWER_EN4, ENABLE_MASK(2), ENABLE_MASK(2), DISABLE_VAL(2)), + RK8XX_REGULATOR_VOLT_RANGES("NLDO_REG2", "vcc11", rk8xx_regulator_ops, + RK806_LDO_SEL_CNT, rk806_ldo_voltage_ranges, + RK806_PLDO3_ON_VSEL, 0xff, + RK806_POWER_EN4, ENABLE_MASK(3), ENABLE_MASK(3), DISABLE_VAL(3)), + + RK8XX_REGULATOR_VOLT_RANGES("NLDO_REG3", "vcc12", rk8xx_regulator_ops, + RK806_LDO_SEL_CNT, rk806_ldo_voltage_ranges, + RK806_PLDO4_ON_VSEL, 0xff, + RK806_POWER_EN5, ENABLE_MASK(0), ENABLE_MASK(0), DISABLE_VAL(0)), + RK8XX_REGULATOR_VOLT_RANGES("NLDO_REG4", "vcc12", rk8xx_regulator_ops, + RK806_LDO_SEL_CNT, rk806_ldo_voltage_ranges, + RK806_PLDO5_ON_VSEL, 0xff, + RK806_POWER_EN5, ENABLE_MASK(1), ENABLE_MASK(1), DISABLE_VAL(1)), + + RK8XX_REGULATOR_VOLT_RANGES("NLDO_REG5", "vcca", rk8xx_regulator_ops, + RK806_LDO_SEL_CNT, rk806_ldo_voltage_ranges, + RK806_PLDO6_ON_VSEL, 0xff, + RK806_POWER_EN4, ENABLE_MASK(0), ENABLE_MASK(0), DISABLE_VAL(0)), +}; + +static const struct rk8xx_regulator_range rk808_ldo3_voltage_ranges[] = +{ + RK8XX_REGULATOR_RANGE(800000, 0, 13, 100000), + RK8XX_REGULATOR_RANGE(2500000, 15, 15, 0), +}; + +static const struct rk8xx_regulator_desc rk808_desc[] = +{ + RK8XX_REGULATOR_VOLT("DCDC_REG1", "vcc1", rk8xx_regulator_buck_ops, + 64, 712500, 12500, + RK808_BUCK1_ON_VSEL_REG, RK808_BUCK_VSEL_MASK, + RK808_DCDC_EN_REG, RT_BIT(0), 0, 0), + RK8XX_REGULATOR_VOLT("DCDC_REG2", "vcc2", rk8xx_regulator_buck_ops, + 64, 712500, 12500, + RK808_BUCK2_ON_VSEL_REG, RK808_BUCK_VSEL_MASK, + RK808_DCDC_EN_REG, RT_BIT(1), 0, 0), + RK8XX_REGULATOR_SWITCH_DESC("DCDC_REG3", "vcc3", rk8xx_regulator_switch_ops, + RK808_DCDC_EN_REG, RT_BIT(2), 0, 0), + RK8XX_REGULATOR_VOLT_RANGE("DCDC_REG4", "vcc4", rk8xx_regulator_ops, + 1800000, 3400000, 100000, + RK808_BUCK4_ON_VSEL_REG, RK808_BUCK4_VSEL_MASK, + RK808_DCDC_EN_REG, RT_BIT(3), 0, 0), + + RK8XX_REGULATOR_VOLT_RANGE("LDO_REG1", "vcc6", rk8xx_regulator_ops, + 1800000, 3400000, 100000, + RK808_LDO1_ON_VSEL_REG, RK808_LDO_VSEL_MASK, + RK808_LDO_EN_REG, RT_BIT(0), 0, 0), + RK8XX_REGULATOR_VOLT_RANGE("LDO_REG2", "vcc6", rk8xx_regulator_ops, + 1800000, 3400000, 100000, + RK808_LDO2_ON_VSEL_REG, RK808_LDO_VSEL_MASK, + RK808_LDO_EN_REG, RT_BIT(1), 0, 0), + RK8XX_REGULATOR_DESC("LDO_REG3", "vcc7", rk8xx_regulator_ops, + 16, 0, 0, + RT_ARRAY_SIZE(rk808_ldo3_voltage_ranges), rk808_ldo3_voltage_ranges, + RK808_LDO3_ON_VSEL_REG, RK808_BUCK4_VSEL_MASK, + RK808_LDO_EN_REG, RT_BIT(2), 0, 0), + RK8XX_REGULATOR_VOLT_RANGE("LDO_REG4", "vcc9", rk8xx_regulator_ops, + 1800000, 3400000, 100000, + RK808_LDO4_ON_VSEL_REG, RK808_LDO_VSEL_MASK, + RK808_LDO_EN_REG, RT_BIT(3), 0, 0), + + RK8XX_REGULATOR_VOLT_RANGE("LDO_REG5", "vcc9", rk8xx_regulator_ops, + 1800000, 3400000, 100000, + RK808_LDO5_ON_VSEL_REG, RK808_LDO_VSEL_MASK, + RK808_LDO_EN_REG, RT_BIT(4), 0, 0), + RK8XX_REGULATOR_VOLT_RANGE("LDO_REG6", "vcc10", rk8xx_regulator_ops, + 800000, 2500000, 100000, + RK808_LDO6_ON_VSEL_REG, RK808_LDO_VSEL_MASK, + RK808_LDO_EN_REG, RT_BIT(5), 0, 0), + RK8XX_REGULATOR_VOLT_RANGE("LDO_REG7", "vcc7", rk8xx_regulator_ops, + 800000, 2500000, 100000, + RK808_LDO7_ON_VSEL_REG, RK808_LDO_VSEL_MASK, + RK808_LDO_EN_REG, RT_BIT(6), 0, 0), + RK8XX_REGULATOR_VOLT_RANGE("LDO_REG8", "vcc11", rk8xx_regulator_ops, + 1800000, 3400000, 100000, + RK808_LDO8_ON_VSEL_REG, RK808_LDO_VSEL_MASK, + RK808_LDO_EN_REG, RT_BIT(7), 0, 0), + + RK8XX_REGULATOR_SWITCH_DESC("SWITCH_REG1", "vcc8", rk8xx_regulator_switch_ops, + RK808_DCDC_EN_REG, RT_BIT(5), 0, 0), + RK8XX_REGULATOR_SWITCH_DESC("SWITCH_REG2", "vcc12", rk8xx_regulator_switch_ops, + RK808_DCDC_EN_REG, RT_BIT(6), 0, 0), +}; + +#define RK817_BUCK1_MIN0 500000 +#define RK817_BUCK1_MAX0 1500000 + +#define RK817_BUCK1_MIN1 1600000 +#define RK817_BUCK1_MAX1 2400000 + +#define RK817_BUCK3_MAX1 3400000 + +#define RK817_BUCK1_STP0 12500 +#define RK817_BUCK1_STP1 100000 + +#define RK817_BUCK1_SEL0 ((RK817_BUCK1_MAX0 - RK817_BUCK1_MIN0) / RK817_BUCK1_STP0) +#define RK817_BUCK1_SEL1 ((RK817_BUCK1_MAX1 - RK817_BUCK1_MIN1) / RK817_BUCK1_STP1) +#define RK817_BUCK3_SEL1 ((RK817_BUCK3_MAX1 - RK817_BUCK1_MIN1) / RK817_BUCK1_STP1) + +#define RK817_BUCK1_SEL_CNT (RK817_BUCK1_SEL0 + RK817_BUCK1_SEL1 + 1) +#define RK817_BUCK3_SEL_CNT (RK817_BUCK1_SEL0 + RK817_BUCK3_SEL1 + 1) + +static const struct rk8xx_regulator_range rk817_buck1_voltage_ranges[] = +{ + RK8XX_REGULATOR_RANGE(RK817_BUCK1_MIN0, 0, RK817_BUCK1_SEL0, RK817_BUCK1_STP0), + RK8XX_REGULATOR_RANGE(RK817_BUCK1_MIN1, RK817_BUCK1_SEL0 + 1, RK817_BUCK1_SEL_CNT, RK817_BUCK1_STP1), +}; + +static const struct rk8xx_regulator_range rk817_buck3_voltage_ranges[] = +{ + RK8XX_REGULATOR_RANGE(RK817_BUCK1_MIN0, 0, RK817_BUCK1_SEL0, RK817_BUCK1_STP0), + RK8XX_REGULATOR_RANGE(RK817_BUCK1_MIN1, RK817_BUCK1_SEL0 + 1, RK817_BUCK3_SEL_CNT, RK817_BUCK1_STP1), +}; + +#define RK809_BUCK5_SEL_CNT 8 + +static const struct rk8xx_regulator_range rk809_buck5_voltage_ranges[] = +{ + RK8XX_REGULATOR_RANGE(1500000, 0, 0, 0), + RK8XX_REGULATOR_RANGE(1800000, 1, 3, 200000), + RK8XX_REGULATOR_RANGE(2800000, 4, 5, 200000), + RK8XX_REGULATOR_RANGE(3300000, 6, 7, 300000), +}; + +static const struct rk8xx_regulator_desc rk809_desc[] = +{ + RK8XX_REGULATOR_DESC("DCDC_REG1", "vcc1", rk8xx_regulator_wmsk_ops, + RK817_BUCK1_SEL_CNT + 1, 0, 0, + RT_ARRAY_SIZE(rk817_buck1_voltage_ranges), rk817_buck1_voltage_ranges, + RK817_BUCK1_ON_VSEL_REG, RK817_BUCK_VSEL_MASK, + RK817_POWER_EN_REG(0), ENABLE_MASK(RK817_ID_DCDC1), ENABLE_MASK(RK817_ID_DCDC1), DISABLE_VAL(RK817_ID_DCDC1)), + RK8XX_REGULATOR_DESC("DCDC_REG2", "vcc2", rk8xx_regulator_wmsk_ops, + RK817_BUCK1_SEL_CNT + 1, 0, 0, + RT_ARRAY_SIZE(rk817_buck1_voltage_ranges), rk817_buck1_voltage_ranges, + RK817_BUCK2_ON_VSEL_REG, RK817_BUCK_VSEL_MASK, + RK817_POWER_EN_REG(0), ENABLE_MASK(RK817_ID_DCDC2), ENABLE_MASK(RK817_ID_DCDC2), DISABLE_VAL(RK817_ID_DCDC2)), + RK8XX_REGULATOR_DESC("DCDC_REG3", "vcc3", rk8xx_regulator_wmsk_ops, + RK817_BUCK1_SEL_CNT + 1, 0, 0, + RT_ARRAY_SIZE(rk817_buck1_voltage_ranges), rk817_buck1_voltage_ranges, + RK817_BUCK3_ON_VSEL_REG, RK817_BUCK_VSEL_MASK, + RK817_POWER_EN_REG(0), ENABLE_MASK(RK817_ID_DCDC3), ENABLE_MASK(RK817_ID_DCDC3), DISABLE_VAL(RK817_ID_DCDC3)), + RK8XX_REGULATOR_DESC("DCDC_REG4", "vcc4", rk8xx_regulator_wmsk_ops, + RK817_BUCK3_SEL_CNT + 1, 0, 0, + RT_ARRAY_SIZE(rk817_buck3_voltage_ranges), rk817_buck3_voltage_ranges, + RK817_BUCK4_ON_VSEL_REG, RK817_BUCK_VSEL_MASK, + RK817_POWER_EN_REG(0), ENABLE_MASK(RK817_ID_DCDC4), ENABLE_MASK(RK817_ID_DCDC4), DISABLE_VAL(RK817_ID_DCDC4)), + + RK8XX_REGULATOR_DESC("DCDC_REG5", "vcc9", rk8xx_regulator_wmsk_ops, + RK809_BUCK5_SEL_CNT, 0, 0, + RT_ARRAY_SIZE(rk809_buck5_voltage_ranges), rk809_buck5_voltage_ranges, + RK809_BUCK5_CONFIG(0), RK809_BUCK5_VSEL_MASK, + RK817_POWER_EN_REG(3), ENABLE_MASK(1), ENABLE_MASK(1), DISABLE_VAL(1)), + + RK8XX_REGULATOR_VOLT_RANGE("LDO_REG1", "vcc5", rk8xx_regulator_wmsk_ops, + 600000, 3400000, 25000, + RK817_LDO_ON_VSEL_REG(0), RK817_LDO_VSEL_MASK, + RK817_POWER_EN_REG(1), ENABLE_MASK(0), 0, DISABLE_VAL(0)), + RK8XX_REGULATOR_VOLT_RANGE("LDO_REG2", "vcc5", rk8xx_regulator_wmsk_ops, + 600000, 3400000, 25000, + RK817_LDO_ON_VSEL_REG(1), RK817_LDO_VSEL_MASK, + RK817_POWER_EN_REG(1), ENABLE_MASK(1), 0, DISABLE_VAL(1)), + RK8XX_REGULATOR_VOLT_RANGE("LDO_REG3", "vcc5", rk8xx_regulator_wmsk_ops, + 600000, 3400000, 25000, + RK817_LDO_ON_VSEL_REG(2), RK817_LDO_VSEL_MASK, + RK817_POWER_EN_REG(1), ENABLE_MASK(2), 0, DISABLE_VAL(2)), + RK8XX_REGULATOR_VOLT_RANGE("LDO_REG4", "vcc6", rk8xx_regulator_wmsk_ops, + 600000, 3400000, 25000, + RK817_LDO_ON_VSEL_REG(3), RK817_LDO_VSEL_MASK, + RK817_POWER_EN_REG(1), ENABLE_MASK(3), 0, DISABLE_VAL(3)), + + RK8XX_REGULATOR_VOLT_RANGE("LDO_REG5", "vcc6", rk8xx_regulator_wmsk_ops, + 600000, 3400000, 25000, + RK817_LDO_ON_VSEL_REG(4), RK817_LDO_VSEL_MASK, + RK817_POWER_EN_REG(2), ENABLE_MASK(0), 0, DISABLE_VAL(0)), + RK8XX_REGULATOR_VOLT_RANGE("LDO_REG6", "vcc6", rk8xx_regulator_wmsk_ops, + 600000, 3400000, 25000, + RK817_LDO_ON_VSEL_REG(5), RK817_LDO_VSEL_MASK, + RK817_POWER_EN_REG(2), ENABLE_MASK(1), 0, DISABLE_VAL(1)), + RK8XX_REGULATOR_VOLT_RANGE("LDO_REG7", "vcc7", rk8xx_regulator_wmsk_ops, + 600000, 3400000, 25000, + RK817_LDO_ON_VSEL_REG(6), RK817_LDO_VSEL_MASK, + RK817_POWER_EN_REG(2), ENABLE_MASK(2), 0, DISABLE_VAL(2)), + RK8XX_REGULATOR_VOLT_RANGE("LDO_REG8", "vcc7", rk8xx_regulator_wmsk_ops, + 600000, 3400000, 25000, + RK817_LDO_ON_VSEL_REG(7), RK817_LDO_VSEL_MASK, + RK817_POWER_EN_REG(2), ENABLE_MASK(3), 0, DISABLE_VAL(3)), + + RK8XX_REGULATOR_VOLT_RANGE("LDO_REG9", "vcc7", rk8xx_regulator_wmsk_ops, + 600000, 3400000, 25000, + RK817_LDO_ON_VSEL_REG(8), RK817_LDO_VSEL_MASK, + RK817_POWER_EN_REG(3), ENABLE_MASK(0), 0, DISABLE_VAL(0)), + + RK8XX_REGULATOR_SWITCH_DESC("SWITCH_REG1", "vcc9", rk8xx_regulator_switch_wmsk_ops, + RK817_POWER_EN_REG(3), ENABLE_MASK(2), 0, DISABLE_VAL(2)), + RK8XX_REGULATOR_SWITCH_DESC("SWITCH_REG2", "vcc8", rk8xx_regulator_switch_wmsk_ops, + RK817_POWER_EN_REG(3), ENABLE_MASK(3), 0, DISABLE_VAL(3)), +}; + +static const struct rk8xx_regulator_desc rk817_desc[] = +{ + RK8XX_REGULATOR_DESC("DCDC_REG1", "vcc1", rk8xx_regulator_wmsk_ops, + RK817_BUCK1_SEL_CNT + 1, 0, 0, + RT_ARRAY_SIZE(rk817_buck1_voltage_ranges), rk817_buck1_voltage_ranges, + RK817_BUCK1_ON_VSEL_REG, RK817_BUCK_VSEL_MASK, + RK817_POWER_EN_REG(0), ENABLE_MASK(RK817_ID_DCDC1), ENABLE_MASK(RK817_ID_DCDC1), DISABLE_VAL(RK817_ID_DCDC1)), + RK8XX_REGULATOR_DESC("DCDC_REG2", "vcc2", rk8xx_regulator_wmsk_ops, + RK817_BUCK1_SEL_CNT + 1, 0, 0, + RT_ARRAY_SIZE(rk817_buck1_voltage_ranges), rk817_buck1_voltage_ranges, + RK817_BUCK2_ON_VSEL_REG, RK817_BUCK_VSEL_MASK, + RK817_POWER_EN_REG(0), ENABLE_MASK(RK817_ID_DCDC2), ENABLE_MASK(RK817_ID_DCDC2), DISABLE_VAL(RK817_ID_DCDC2)), + RK8XX_REGULATOR_DESC("DCDC_REG3", "vcc3", rk8xx_regulator_wmsk_ops, + RK817_BUCK1_SEL_CNT + 1, 0, 0, + RT_ARRAY_SIZE(rk817_buck1_voltage_ranges), rk817_buck1_voltage_ranges, + RK817_BUCK3_ON_VSEL_REG, RK817_BUCK_VSEL_MASK, + RK817_POWER_EN_REG(0), ENABLE_MASK(RK817_ID_DCDC3), ENABLE_MASK(RK817_ID_DCDC3), DISABLE_VAL(RK817_ID_DCDC3)), + RK8XX_REGULATOR_DESC("DCDC_REG4", "vcc4", rk8xx_regulator_wmsk_ops, + RK817_BUCK3_SEL_CNT + 1, 0, 0, + RT_ARRAY_SIZE(rk817_buck3_voltage_ranges), rk817_buck3_voltage_ranges, + RK817_BUCK4_ON_VSEL_REG, RK817_BUCK_VSEL_MASK, + RK817_POWER_EN_REG(0), ENABLE_MASK(RK817_ID_DCDC4), ENABLE_MASK(RK817_ID_DCDC4), DISABLE_VAL(RK817_ID_DCDC4)), + + RK8XX_REGULATOR_VOLT_RANGE("LDO_REG1", "vcc5", rk8xx_regulator_ops, + 600000, 3400000, 25000, + RK817_LDO_ON_VSEL_REG(0), RK817_LDO_VSEL_MASK, + RK817_POWER_EN_REG(1), ENABLE_MASK(0), 0, DISABLE_VAL(0)), + RK8XX_REGULATOR_VOLT_RANGE("LDO_REG2", "vcc5", rk8xx_regulator_ops, + 600000, 3400000, 25000, + RK817_LDO_ON_VSEL_REG(1), RK817_LDO_VSEL_MASK, + RK817_POWER_EN_REG(1), ENABLE_MASK(1), 0, DISABLE_VAL(1)), + RK8XX_REGULATOR_VOLT_RANGE("LDO_REG3", "vcc5", rk8xx_regulator_ops, + 600000, 3400000, 25000, + RK817_LDO_ON_VSEL_REG(2), RK817_LDO_VSEL_MASK, + RK817_POWER_EN_REG(1), ENABLE_MASK(2), 0, DISABLE_VAL(2)), + RK8XX_REGULATOR_VOLT_RANGE("LDO_REG4", "vcc6", rk8xx_regulator_ops, + 600000, 3400000, 25000, + RK817_LDO_ON_VSEL_REG(3), RK817_LDO_VSEL_MASK, + RK817_POWER_EN_REG(1), ENABLE_MASK(3), 0, DISABLE_VAL(3)), + + RK8XX_REGULATOR_VOLT_RANGE("LDO_REG5", "vcc6", rk8xx_regulator_ops, + 600000, 3400000, 25000, + RK817_LDO_ON_VSEL_REG(4), RK817_LDO_VSEL_MASK, + RK817_POWER_EN_REG(2), ENABLE_MASK(0), 0, DISABLE_VAL(0)), + RK8XX_REGULATOR_VOLT_RANGE("LDO_REG6", "vcc6", rk8xx_regulator_ops, + 600000, 3400000, 25000, + RK817_LDO_ON_VSEL_REG(5), RK817_LDO_VSEL_MASK, + RK817_POWER_EN_REG(2), ENABLE_MASK(1), 0, DISABLE_VAL(1)), + RK8XX_REGULATOR_VOLT_RANGE("LDO_REG7", "vcc7", rk8xx_regulator_ops, + 600000, 3400000, 25000, + RK817_LDO_ON_VSEL_REG(6), RK817_LDO_VSEL_MASK, + RK817_POWER_EN_REG(2), ENABLE_MASK(2), 0, DISABLE_VAL(2)), + RK8XX_REGULATOR_VOLT_RANGE("LDO_REG8", "vcc7", rk8xx_regulator_ops, + 600000, 3400000, 25000, + RK817_LDO_ON_VSEL_REG(7), RK817_LDO_VSEL_MASK, + RK817_POWER_EN_REG(2), ENABLE_MASK(3), 0, DISABLE_VAL(3)), + + RK8XX_REGULATOR_VOLT_RANGE("LDO_REG9", "vcc7", rk8xx_regulator_ops, + 600000, 3400000, 25000, + RK817_LDO_ON_VSEL_REG(8), RK817_LDO_VSEL_MASK, + RK817_POWER_EN_REG(3), ENABLE_MASK(0), 0, DISABLE_VAL(0)), + + RK8XX_REGULATOR_VOLT_RANGE("BOOST", "vcc8", rk8xx_regulator_wmsk_ops, + 4700000, 5400000, 100000, + RK817_BOOST_OTG_CFG, RK817_BOOST_VSEL_MASK, + RK817_POWER_EN_REG(3), ENABLE_MASK(1), ENABLE_MASK(1), DISABLE_VAL(1)), + RK8XX_REGULATOR_SWITCH_DESC("OTG_SWITCH", "vcc9", rk8xx_regulator_switch_wmsk_ops, + RK817_POWER_EN_REG(3), ENABLE_MASK(2), 0, DISABLE_VAL(2)), +}; + +static const struct rk8xx_regulator_desc rk818_desc[] = +{ + RK8XX_REGULATOR_VOLT("DCDC_REG1", "vcc1", rk8xx_regulator_ops, + 64, 712500, 12500, + RK818_BUCK1_ON_VSEL_REG, RK818_BUCK_VSEL_MASK, + RK818_DCDC_EN_REG, RT_BIT(0), 0, 0), + RK8XX_REGULATOR_VOLT("DCDC_REG2", "vcc2", rk8xx_regulator_ops, + 64, 712500, 12500, + RK818_BUCK2_ON_VSEL_REG, RK818_BUCK_VSEL_MASK, + RK818_DCDC_EN_REG, RT_BIT(1), 0, 0), + RK8XX_REGULATOR_SWITCH_DESC("DCDC_REG3", "vcc3", rk8xx_regulator_switch_ops, + RK818_DCDC_EN_REG, RT_BIT(2), 0, 0), + RK8XX_REGULATOR_VOLT_RANGE("DCDC_REG4", "vcc4", rk8xx_regulator_ops, + 1800000, 3600000, 100000, + RK818_BUCK4_ON_VSEL_REG, RK818_BUCK4_VSEL_MASK, + RK818_DCDC_EN_REG, RT_BIT(3), 0, 0), + + RK8XX_REGULATOR_VOLT_RANGE("DCDC_BOOST", "boost", rk8xx_regulator_ops, + 4700000, 5400000, 100000, + RK818_BOOST_LDO9_ON_VSEL_REG, RK818_BOOST_ON_VSEL_MASK, + RK818_DCDC_EN_REG, RT_BIT(4), 0, 0), + + RK8XX_REGULATOR_VOLT_RANGE("LDO_REG1", "vcc6", rk8xx_regulator_ops, + 1800000, 3400000, 100000, + RK818_LDO1_ON_VSEL_REG, RK818_LDO_VSEL_MASK, + RK818_LDO_EN_REG, RT_BIT(0), 0, 0), + RK8XX_REGULATOR_VOLT_RANGE("LDO_REG2", "vcc6", rk8xx_regulator_ops, + 1800000, 3400000, 100000, + RK818_LDO2_ON_VSEL_REG, RK818_LDO_VSEL_MASK, + RK818_LDO_EN_REG, RT_BIT(1), 0, 0), + RK8XX_REGULATOR_DESC("LDO_REG3", "vcc7", rk8xx_regulator_ops, + 16, 0, 0, + RT_ARRAY_SIZE(rk808_ldo3_voltage_ranges), rk808_ldo3_voltage_ranges, + RK818_LDO3_ON_VSEL_REG, RK818_LDO3_ON_VSEL_MASK, + RK818_LDO_EN_REG, RT_BIT(2), 0, 0), + RK8XX_REGULATOR_VOLT_RANGE("LDO_REG4", "vcc8", rk8xx_regulator_ops, + 1800000, 3400000, 100000, + RK818_LDO4_ON_VSEL_REG, RK818_LDO_VSEL_MASK, + RK818_LDO_EN_REG, RT_BIT(3), 0, 0), + + RK8XX_REGULATOR_VOLT_RANGE("LDO_REG5", "vcc7", rk8xx_regulator_ops, + 1800000, 3400000, 100000, + RK818_LDO5_ON_VSEL_REG, RK818_LDO_VSEL_MASK, + RK818_LDO_EN_REG, RT_BIT(4), 0, 0), + RK8XX_REGULATOR_VOLT_RANGE("LDO_REG6", "vcc8", rk8xx_regulator_ops, + 800000, 2500000, 100000, + RK818_LDO6_ON_VSEL_REG, RK818_LDO_VSEL_MASK, + RK818_LDO_EN_REG, RT_BIT(5), 0, 0), + RK8XX_REGULATOR_VOLT_RANGE("LDO_REG7", "vcc7", rk8xx_regulator_ops, + 800000, 2500000, 100000, + RK818_LDO7_ON_VSEL_REG, RK818_LDO_VSEL_MASK, + RK818_LDO_EN_REG, RT_BIT(6), 0, 0), + RK8XX_REGULATOR_VOLT_RANGE("LDO_REG8", "vcc8", rk8xx_regulator_ops, + 1800000, 3400000, 100000, + RK818_LDO8_ON_VSEL_REG, RK818_LDO_VSEL_MASK, + RK818_LDO_EN_REG, RT_BIT(7), 0, 0), + + RK8XX_REGULATOR_VOLT_RANGE("LDO_REG9", "vcc9", rk8xx_regulator_ops, + 1800000, 3400000, 100000, + RK818_BOOST_LDO9_ON_VSEL_REG, RK818_LDO_VSEL_MASK, + RK818_DCDC_EN_REG, RT_BIT(5), 0, 0), + + RK8XX_REGULATOR_SWITCH_DESC("SWITCH_REG", "vcc9", rk8xx_regulator_switch_ops, + RK818_DCDC_EN_REG, RT_BIT(6), 0, 0), + RK8XX_REGULATOR_SWITCH_DESC("HDMI_SWITCH", "h_5v", rk8xx_regulator_switch_ops, + RK818_H5V_EN_REG, RT_BIT(0), 0, 0), + RK8XX_REGULATOR_SWITCH_DESC("OTG_SWITCH", "usb", rk8xx_regulator_switch_ops, + RK818_DCDC_EN_REG, RT_BIT(7), 0, 0), +}; + +static rt_err_t append_rk8xx_regulator(struct rk8xx *rk8xx, struct rt_ofw_node *np, + const struct rk8xx_regulator_desc *desc_table, int id) +{ + rt_err_t err = RT_EOK; + struct rt_regulator_node *rgp; + struct rk8xx_regulator *rk8xx_reg; + + rk8xx_reg = rt_calloc(1, sizeof(*rk8xx_reg)); + + if (!rk8xx_reg) + { + return -RT_ENOMEM; + } + + rk8xx_reg->rk8xx = rk8xx; + rk8xx_reg->desc = &desc_table[id]; + + regulator_ofw_parse(np, &rk8xx_reg->param); + + rgp = &rk8xx_reg->parent; + rgp->ops = rk8xx_reg->desc->ops; + rgp->param = &rk8xx_reg->param; + rgp->dev = &rk8xx_reg->device; + + rgp->dev->ofw_node = np; + + if (id < MAX_PIN_NR) + { + rt_uint32_t tmp; + rt_uint8_t mode, value; + + rk8xx_reg->pin = rt_ofw_get_named_pin(rk8xx->dev->ofw_node, + "dvs", id, &mode, &value); + + if ((rt_base_t)rk8xx_reg->pin >= 0) + { + rt_pin_mode(rk8xx_reg->pin, mode); + rt_pin_write(rk8xx_reg->pin, value); + + tmp = id ? RK808_DVS2_POL : RK808_DVS1_POL; + rk8xx_update_bits(rk8xx, RK808_IO_POL_REG, tmp, + rt_pin_read(rk8xx_reg->pin) == PIN_LOW ? 0 : tmp); + } + } + + if ((err = rt_regulator_register(rgp))) + { + goto _fail; + } + + return RT_EOK; + +_fail: + rt_free(rk8xx_reg); + + return err; +} + +static rt_err_t rk8xx_regulator_probe(struct rt_platform_device *pdev) +{ + int desc_nr; + rt_err_t err; + struct rk8xx *rk8xx = pdev->priv; + struct rt_ofw_node *np, *regulators_np = pdev->parent.ofw_node; + const struct rk8xx_regulator_desc *desc_table; + + switch (rk8xx->variant) + { + case RK805_ID: + desc_table = rk805_desc; + desc_nr = RT_ARRAY_SIZE(rk805_desc); + break; + + case RK806_ID: + desc_table = rk806_desc; + desc_nr = RT_ARRAY_SIZE(rk806_desc); + break; + + case RK808_ID: + desc_table = rk808_desc; + desc_nr = RT_ARRAY_SIZE(rk808_desc); + break; + + case RK809_ID: + desc_table = rk809_desc; + desc_nr = RT_ARRAY_SIZE(rk809_desc); + break; + + case RK817_ID: + desc_table = rk817_desc; + desc_nr = RT_ARRAY_SIZE(rk817_desc); + break; + + case RK818_ID: + desc_table = rk818_desc; + desc_nr = RT_ARRAY_SIZE(rk818_desc); + break; + + default: + return -RT_ENOSYS; + } + + for (int i = 0; i < desc_nr; ++i) + { + const char *name = desc_table[i].name; + + if (!(np = rt_ofw_get_child_by_tag(regulators_np, name))) + { + continue; + } + + rt_ofw_node_put(np); + + if ((err = append_rk8xx_regulator(rk8xx, np, desc_table, i))) + { + LOG_E("Append RK8XX regulator %s fail error = %s", + rt_ofw_node_full_name(np), rt_strerror(err)); + + if (err == -RT_ENOMEM) + { + return err; + } + } + } + + return RT_EOK; +} + +static struct rt_platform_driver rk8xx_regulator_driver = +{ + .name = "rk8xx-regulator", + .probe = rk8xx_regulator_probe, +}; + +static int rk8xx_regulator_register(void) +{ + rt_platform_driver_register(&rk8xx_regulator_driver); + + return 0; +} +INIT_SUBSYS_EXPORT(rk8xx_regulator_register); diff --git a/components/drivers/regulator/regulator.c b/components/drivers/regulator/regulator.c new file mode 100644 index 000000000000..ddae972ececd --- /dev/null +++ b/components/drivers/regulator/regulator.c @@ -0,0 +1,632 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-09-23 GuEe-GUI first version + */ + +#include +#include + +#define DBG_TAG "rtdm.regulator" +#define DBG_LVL DBG_INFO +#include + +#include +#include + +struct rt_regulator +{ + struct rt_regulator_node *reg_np; +}; + +static struct rt_spinlock _regulator_lock = { 0 }; + +static rt_err_t regulator_enable(struct rt_regulator_node *reg_np); +static rt_err_t regulator_disable(struct rt_regulator_node *reg_np); + +static void regulator_release(struct ref *r) +{ + struct rt_regulator_node *reg_np = rt_container_of(r, struct rt_regulator_node, ref); + + LOG_E("%s is release", reg_np->supply_name); + (void)reg_np; + + RT_ASSERT(0); +} + +rt_inline struct rt_regulator_node *regulator_get(struct rt_regulator_node *reg_np) +{ + ref_get(®_np->ref); + + return reg_np; +} + +rt_inline void regulator_put(struct rt_regulator_node *reg_np) +{ + ref_put(®_np->ref, ®ulator_release); +} + +rt_err_t rt_regulator_register(struct rt_regulator_node *reg_np) +{ + const struct rt_regulator_param *param; + + if (!reg_np || !reg_np->dev || !reg_np->param || !reg_np->ops) + { + return -RT_EINVAL; + } + + rt_list_init(®_np->list); + rt_list_init(®_np->children_nodes); + rt_list_init(®_np->notifier_nodes); + ref_init(®_np->ref); + rt_atomic_store(®_np->enabled_count, 0); + + param = reg_np->param; + + reg_np->parent = RT_NULL; + +#ifdef RT_USING_OFW + if (reg_np->dev->ofw_node) + { + rt_ofw_data(reg_np->dev->ofw_node) = reg_np; + } +#endif /* RT_USING_OFW */ + + if (param->boot_on || param->always_on) + { + regulator_enable(reg_np); + } + + return RT_EOK; +} + +rt_err_t rt_regulator_unregister(struct rt_regulator_node *reg_np) +{ + rt_err_t err = RT_EOK; + + if (!reg_np) + { + return -RT_EINVAL; + } + + rt_hw_spin_lock(&_regulator_lock.lock); + + if (rt_atomic_load(®_np->enabled_count) != 0) + { + err = -RT_EBUSY; + + LOG_E("%s was enabled by consumer", reg_np->supply_name); + + goto _unlock; + } + + if (!(reg_np->param->boot_on || reg_np->param->always_on)) + { + regulator_disable(reg_np); + } + + if (!rt_list_isempty(®_np->children_nodes) || + ref_read(®_np->ref) > 1) + { + err = -RT_EBUSY; + + goto _unlock; + } + + reg_np->parent = RT_NULL; + rt_list_remove(®_np->list); + +_unlock: + rt_hw_spin_unlock(&_regulator_lock.lock); + + return err; +} + +rt_err_t rt_regulator_notifier_register(struct rt_regulator *reg, + struct rt_regulator_notifier *notifier) +{ + struct rt_regulator_node *reg_np; + + if (!reg || !notifier) + { + return -RT_EINVAL; + } + + rt_hw_spin_lock(&_regulator_lock.lock); + + reg_np = reg->reg_np; + notifier->regulator = reg; + + rt_list_init(¬ifier->list); + rt_list_insert_after(®_np->notifier_nodes, ¬ifier->list); + + rt_hw_spin_unlock(&_regulator_lock.lock); + + return RT_EOK; +} + +rt_err_t rt_regulator_notifier_unregister(struct rt_regulator *reg, + struct rt_regulator_notifier *notifier) +{ + if (!reg || !notifier) + { + return -RT_EINVAL; + } + + rt_hw_spin_lock(&_regulator_lock.lock); + + rt_list_remove(¬ifier->list); + + rt_hw_spin_unlock(&_regulator_lock.lock); + + return RT_EOK; +} + +static rt_err_t regulator_notifier_call_chain(struct rt_regulator_node *reg_np, + rt_ubase_t msg, void *data) +{ + rt_err_t err = RT_EOK; + struct rt_regulator_notifier *notifier; + rt_list_t *head = ®_np->notifier_nodes; + + if (rt_list_isempty(head)) + { + return err; + } + + rt_list_for_each_entry(notifier, head, list) + { + err = notifier->callback(notifier, msg, data); + + if (err == -RT_EIO) + { + break; + } + } + + return err; +} + +static rt_uint32_t regulator_get_enable_time(struct rt_regulator_node *reg_np) +{ + if (reg_np->param->enable_delay) + { + return reg_np->param->enable_delay; + } + + if (reg_np->ops->enable_time) + { + return reg_np->ops->enable_time(reg_np); + } + + return 0; +} + +static void regulator_delay(rt_uint32_t delay) +{ + rt_uint32_t ms = delay / 1000; + rt_uint32_t us = delay % 1000; + + if (ms > 0) + { + /* + * For small enough values, handle super-millisecond + * delays in the usleep_range() call below. + */ + if (ms < 20) + { + us += ms * 1000; + } + else + { + rt_thread_mdelay(ms); + } + } + + /* + * Give the scheduler some room to coalesce with any other + * wakeup sources. For delays shorter than 10 us, don't even + * bother setting up high-resolution timers and just busy-loop. + */ + if (us >= 10) + { + rt_hw_us_delay((us + 100) >> 1); + } + else + { + rt_hw_us_delay(us); + } +} + +static rt_err_t regulator_enable(struct rt_regulator_node *reg_np) +{ + rt_err_t err = RT_EOK; + rt_uint32_t enable_delay = regulator_get_enable_time(reg_np); + + if (reg_np->ops->enable) + { + err = reg_np->ops->enable(reg_np); + + if (!err) + { + if (enable_delay) + { + regulator_delay(enable_delay); + } + + rt_atomic_add(®_np->enabled_count, 1); + err = regulator_notifier_call_chain(reg_np, RT_REGULATOR_MSG_ENABLE, RT_NULL); + } + } + + if (!err && reg_np->parent) + { + err = regulator_enable(reg_np->parent); + } + + return err; +} + +rt_err_t rt_regulator_enable(struct rt_regulator *reg) +{ + rt_err_t err; + + if (!reg) + { + return -RT_EINVAL; + } + + if (rt_regulator_is_enabled(reg)) + { + return RT_EOK; + } + + rt_hw_spin_lock(&_regulator_lock.lock); + + err = regulator_enable(reg->reg_np); + + rt_hw_spin_unlock(&_regulator_lock.lock); + + return err; +} + +static rt_err_t regulator_disable(struct rt_regulator_node *reg_np) +{ + rt_err_t err = RT_EOK; + + if (reg_np->ops->disable) + { + err = reg_np->ops->disable(reg_np); + + if (!err) + { + if (reg_np->param->off_on_delay) + { + regulator_delay(reg_np->param->off_on_delay); + } + + err = regulator_notifier_call_chain(reg_np, RT_REGULATOR_MSG_DISABLE, RT_NULL); + } + } + + if (!err && reg_np->parent) + { + err = regulator_disable(reg_np->parent); + } + + return err; +} + +rt_err_t rt_regulator_disable(struct rt_regulator *reg) +{ + rt_err_t err; + + if (!reg) + { + return -RT_EINVAL; + } + + if (!rt_regulator_is_enabled(reg)) + { + return RT_EOK; + } + + if (rt_atomic_load(®->reg_np->enabled_count) != 0) + { + rt_atomic_sub(®->reg_np->enabled_count, 1); + + return RT_EOK; + } + + rt_hw_spin_lock(&_regulator_lock.lock); + + err = regulator_disable(reg->reg_np); + + rt_hw_spin_unlock(&_regulator_lock.lock); + + return err; +} + +rt_bool_t rt_regulator_is_enabled(struct rt_regulator *reg) +{ + if (!reg) + { + return -RT_EINVAL; + } + + if (reg->reg_np->ops->is_enabled) + { + return reg->reg_np->ops->is_enabled(reg->reg_np); + } + + return rt_atomic_load(®->reg_np->enabled_count) > 0; +} + +static rt_err_t regulator_set_voltage(struct rt_regulator_node *reg_np, int min_uvolt, int max_uvolt) +{ + rt_err_t err = RT_EOK; + + if (reg_np->ops->set_voltage) + { + union rt_regulator_notifier_args args; + + RT_ASSERT(reg_np->ops->get_voltage != RT_NULL); + + args.old_uvolt = reg_np->ops->get_voltage(reg_np); + args.min_uvolt = min_uvolt; + args.max_uvolt = max_uvolt; + + err = regulator_notifier_call_chain(reg_np, RT_REGULATOR_MSG_VOLTAGE_CHANGE, &args); + + if (!err) + { + err = reg_np->ops->set_voltage(reg_np, min_uvolt, max_uvolt); + } + + if (err) + { + regulator_notifier_call_chain(reg_np, RT_REGULATOR_MSG_VOLTAGE_CHANGE_ERR, + (void *)(rt_base_t)args.old_uvolt); + } + } + + if (!err && reg_np->parent) + { + err = regulator_set_voltage(reg_np->parent, min_uvolt, max_uvolt); + } + + return err; +} + +rt_bool_t rt_regulator_is_supported_voltage(struct rt_regulator *reg, int min_uvolt, int max_uvolt) +{ + const struct rt_regulator_param *param; + + RT_ASSERT(reg != RT_NULL); + + param = reg->reg_np->param; + + if (!param) + { + return RT_FALSE; + } + + return param->min_uvolt <= min_uvolt && param->max_uvolt >= max_uvolt; +} + +rt_err_t rt_regulator_set_voltage(struct rt_regulator *reg, int min_uvolt, int max_uvolt) +{ + rt_err_t err; + + if (!reg) + { + return -RT_EINVAL; + } + + rt_hw_spin_lock(&_regulator_lock.lock); + + err = regulator_set_voltage(reg->reg_np, min_uvolt, max_uvolt); + + rt_hw_spin_unlock(&_regulator_lock.lock); + + return err; +} + +int rt_regulator_get_voltage(struct rt_regulator *reg) +{ + int uvolt = RT_REGULATOR_UVOLT_INVALID; + struct rt_regulator_node *reg_np; + + if (!reg) + { + return -RT_EINVAL; + } + + rt_hw_spin_lock(&_regulator_lock.lock); + + reg_np = reg->reg_np; + + if (reg_np->ops->get_voltage) + { + uvolt = reg_np->ops->get_voltage(reg->reg_np); + } + else + { + uvolt = -RT_ENOSYS; + } + + rt_hw_spin_unlock(&_regulator_lock.lock); + + return uvolt; +} + +rt_err_t rt_regulator_set_mode(struct rt_regulator *reg, rt_uint32_t mode) +{ + rt_err_t err; + struct rt_regulator_node *reg_np; + + if (!reg) + { + return -RT_EINVAL; + } + + rt_hw_spin_lock(&_regulator_lock.lock); + + reg_np = reg->reg_np; + + if (reg_np->ops->set_mode) + { + err = reg_np->ops->set_mode(reg_np, mode); + } + else + { + err = -RT_ENOSYS; + } + + rt_hw_spin_unlock(&_regulator_lock.lock); + + return err; +} + +rt_int32_t rt_regulator_get_mode(struct rt_regulator *reg) +{ + rt_int32_t mode; + struct rt_regulator_node *reg_np; + + if (!reg) + { + return -RT_EINVAL; + } + + rt_hw_spin_lock(&_regulator_lock.lock); + + reg_np = reg->reg_np; + + if (reg_np->ops->get_mode) + { + mode = reg_np->ops->get_mode(reg_np); + } + else + { + mode = -RT_ENOSYS; + } + + rt_hw_spin_unlock(&_regulator_lock.lock); + + return mode; +} + +static void regulator_check_parent(struct rt_regulator_node *reg_np) +{ + if (reg_np->parent) + { + return; + } + else + { + #ifdef RT_USING_OFW + rt_phandle parent_phandle = 0; + struct rt_ofw_node *np = reg_np->dev->ofw_node; + + while (np) + { + if (rt_ofw_prop_read_u32(np, "vin-supply", &parent_phandle)) + { + break; + } + + if (!(np = rt_ofw_find_node_by_phandle(parent_phandle))) + { + break; + } + + if (!(reg_np->parent = rt_ofw_data(np))) + { + LOG_W("%s parent ofw node = %s not init", + reg_np->supply_name, rt_ofw_node_full_name(np)); + + break; + } + + rt_list_insert_after(®_np->parent->children_nodes, ®_np->list); + rt_ofw_node_put(np); + } + #endif + } +} + +struct rt_regulator *rt_regulator_get_optional(struct rt_device *dev, const char *id) +{ + struct rt_regulator *reg = RT_NULL; + struct rt_regulator_node *reg_np = RT_NULL; + + if (!dev || !id) + { + goto _end; + } + +#ifdef RT_USING_OFW + if (dev->ofw_node) + { + rt_phandle supply_phandle; + struct rt_ofw_node *np = dev->ofw_node; + char supply_name[64]; + + rt_snprintf(supply_name, sizeof(supply_name), "%s-supply", id); + + if (rt_ofw_prop_read_u32(np, supply_name, &supply_phandle)) + { + goto _end; + } + + if (!(np = rt_ofw_find_node_by_phandle(supply_phandle))) + { + reg = rt_err_ptr(-RT_EIO); + goto _end; + } + + reg_np = rt_ofw_data(np); + } +#endif + + if (!reg_np) + { + reg = rt_err_ptr(-RT_ENOSYS); + goto _end; + } + + rt_hw_spin_lock(&_regulator_lock.lock); + + regulator_check_parent(reg_np); + + rt_hw_spin_unlock(&_regulator_lock.lock); + + reg = rt_calloc(1, sizeof(*reg)); + + if (!reg) + { + reg = rt_err_ptr(-RT_ENOMEM); + goto _end; + } + + reg->reg_np = reg_np; + regulator_get(reg_np); + +_end: + return reg; +} + +void rt_regulator_put(struct rt_regulator *reg) +{ + if (reg) + { + return; + } + + regulator_put(reg->reg_np); + rt_free(reg); +} diff --git a/components/drivers/regulator/regulator_dm.c b/components/drivers/regulator/regulator_dm.c new file mode 100644 index 000000000000..d8096c43d301 --- /dev/null +++ b/components/drivers/regulator/regulator_dm.c @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-09-23 GuEe-GUI first version + */ + +#include "regulator_dm.h" + +#ifdef RT_USING_OFW +rt_err_t regulator_ofw_parse(struct rt_ofw_node *np, struct rt_regulator_param *param) +{ + rt_uint32_t pval; + + param->name = rt_ofw_prop_read_raw(np, "regulator-name", RT_NULL); + + if (!rt_ofw_prop_read_u32(np, "regulator-min-microvolt", &pval)) + { + param->min_uvolt = pval; + } + + if (!rt_ofw_prop_read_u32(np, "regulator-max-microvolt", &pval)) + { + param->max_uvolt = pval; + } + + if (!rt_ofw_prop_read_u32(np, "regulator-min-microamp", &pval)) + { + param->min_uamp = pval; + } + + if (!rt_ofw_prop_read_u32(np, "regulator-max-microamp", &pval)) + { + param->max_uamp = pval; + } + + if (!rt_ofw_prop_read_u32(np, "regulator-ramp-delay", &pval)) + { + param->ramp_delay = pval; + } + + if (!rt_ofw_prop_read_u32(np, "regulator-enable-ramp-delay", &pval)) + { + param->enable_delay = pval; + } + + param->enable_active_high = rt_ofw_prop_read_bool(np, "enable-active-high"); + param->boot_on = rt_ofw_prop_read_bool(np, "regulator-boot-on"); + param->always_on = rt_ofw_prop_read_bool(np, "regulator-always-on"); + param->soft_start = rt_ofw_prop_read_bool(np, "regulator-soft-start"); + param->pull_down = rt_ofw_prop_read_bool(np, "regulator-pull-down"); + param->over_current_protection = rt_ofw_prop_read_bool(np, "regulator-over-current-protection"); + + return RT_EOK; +} +#endif /* RT_USING_OFW */ diff --git a/components/drivers/regulator/regulator_dm.h b/components/drivers/regulator/regulator_dm.h new file mode 100644 index 000000000000..207bb1279465 --- /dev/null +++ b/components/drivers/regulator/regulator_dm.h @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-09-23 GuEe-GUI first version + */ + +#ifndef __REGULATOR_DM_H__ +#define __REGULATOR_DM_H__ + +#include +#include + +#ifdef RT_USING_OFW +rt_err_t regulator_ofw_parse(struct rt_ofw_node *np, struct rt_regulator_param *param); +#else +rt_inline rt_err_t regulator_ofw_parse(struct rt_ofw_node *np, struct rt_regulator_param *param); +{ + return RT_EOK; +} +#endif /* RT_USING_OFW */ + +#endif /* __REGULATOR_DM_H__ */ diff --git a/components/drivers/reset/Kconfig b/components/drivers/reset/Kconfig new file mode 100644 index 000000000000..d8788a82099e --- /dev/null +++ b/components/drivers/reset/Kconfig @@ -0,0 +1,27 @@ +menuconfig RT_USING_RESET + bool "Using Reset Controller support" + depends on RT_USING_DM + select RT_USING_OFW + default n + +config RT_RESET_BRCMSTB_RESCAL + bool "Broadcom STB RESCAL reset controller" + depends on RT_USING_RESET + default n + +config RT_RESET_BRCMSTB + bool "Broadcom STB reset controller" + depends on RT_USING_RESET + default n + +config RT_RESET_RASPBERRYPI + bool "Raspberry Pi 4 Firmware Reset Driver" + depends on RT_USING_RESET + select RT_FIRMWARE_RASPBERRYPI + default n + +config RT_RESET_SCMI + bool "Reset driver controlled via ARM SCMI interface" + depends on RT_USING_RESET + depends on RT_FIRMWARE_ARM_SCMI + default n diff --git a/components/drivers/reset/SConscript b/components/drivers/reset/SConscript new file mode 100755 index 000000000000..4c89d2cfb43e --- /dev/null +++ b/components/drivers/reset/SConscript @@ -0,0 +1,27 @@ +from building import * + +group = [] + +if not GetDepend(['RT_USING_RESET']): + Return('group') + +cwd = GetCurrentDir() +CPPPATH = [cwd + '/../include'] + +src = ['reset.c'] + +if GetDepend(['RT_RESET_BRCMSTB_RESCAL']): + src += ['reset-brcmstb-rescal.c'] + +if GetDepend(['RT_RESET_BRCMSTB']): + src += ['reset-brcmstb.c'] + +if GetDepend(['RT_RESET_RASPBERRYPI']): + src += ['reset-raspberrypi.c'] + +if GetDepend(['RT_RESET_SCMI']): + src += ['reset-scmi.c'] + +group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') diff --git a/components/drivers/reset/reset-brcmstb-rescal.c b/components/drivers/reset/reset-brcmstb-rescal.c new file mode 100644 index 000000000000..788357d5f67e --- /dev/null +++ b/components/drivers/reset/reset-brcmstb-rescal.c @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-11-26 GuEe-GUI first version + */ + +#include +#include + +#define DBG_TAG "reset.brcmstb-rescal" +#define DBG_LVL DBG_INFO +#include + +#include + +#define BRCM_RESCAL_START 0x0 +#define BRCM_RESCAL_START_BIT RT_BIT(0) +#define BRCM_RESCAL_CTRL 0x4 +#define BRCM_RESCAL_STATUS 0x8 +#define BRCM_RESCAL_STATUS_BIT RT_BIT(0) + +struct brcm_rescal_reset +{ + struct rt_reset_controller parent; + + void *regs; +}; + +static rt_err_t brcm_rescal_reset_ofw_parse(struct rt_reset_control *rstc, struct rt_ofw_cell_args *args) +{ + args->args[0] = 0; + + return RT_EOK; +} + +static rt_err_t brcm_rescal_reset_reset(struct rt_reset_control *rstc) +{ + rt_uint32_t reg; + struct brcm_rescal_reset *brrst = rstc->rstcer->priv; + + reg = HWREG32(brrst->regs + BRCM_RESCAL_START); + HWREG32(brrst->regs + BRCM_RESCAL_START) = reg | BRCM_RESCAL_START_BIT; + reg = HWREG32(brrst->regs + BRCM_RESCAL_START); + + if (!(reg & BRCM_RESCAL_START_BIT)) + { + LOG_E("Failed to start SATA/PCIe rescal"); + + return -RT_EIO; + } + + for (rt_uint32_t time = 0; time < 1000; time += 100) + { + reg = HWREG32(brrst->regs + BRCM_RESCAL_STATUS); + + if (reg & BRCM_RESCAL_STATUS_BIT) + { + goto _status_ok; + } + + rt_hw_us_delay(100); + + rt_hw_cpu_relax(); + } + + LOG_E("Time out on SATA/PCIe rescal"); + + return -RT_ETIMEOUT; + +_status_ok: + reg = HWREG32(brrst->regs + BRCM_RESCAL_START); + HWREG32(brrst->regs + BRCM_RESCAL_START) = reg & ~BRCM_RESCAL_START_BIT; + + LOG_D("SATA/PCIe rescal success"); + + return RT_EOK; +} + +static rt_err_t brcm_rescal_reset_assert(struct rt_reset_control *rstc) +{ + return RT_EOK; +} + +static rt_err_t brcm_rescal_reset_deassert(struct rt_reset_control *rstc) +{ + return brcm_rescal_reset_reset(rstc); +} + +const static struct rt_reset_control_ops brcm_rescal_reset_ops = +{ + .ofw_parse = brcm_rescal_reset_ofw_parse, + .reset = brcm_rescal_reset_reset, + .assert = brcm_rescal_reset_assert, + .deassert = brcm_rescal_reset_deassert, +}; + +static rt_err_t brcm_rescal_reset_probe(struct rt_platform_device *pdev) +{ + rt_err_t err; + struct rt_reset_controller *rstcer; + struct rt_device *dev = &pdev->parent; + struct brcm_rescal_reset *brrst = rt_calloc(1, sizeof(*brrst)); + + if (!brrst) + { + return -RT_ENOMEM; + } + + brrst->regs = rt_dm_dev_iomap(dev, 0); + + if (!brrst->regs) + { + err = -RT_EIO; + goto _fail; + } + + rstcer = &brrst->parent; + + rstcer->priv = brrst; + rstcer->ofw_node = dev->ofw_node; + rstcer->ops = &brcm_rescal_reset_ops; + + return RT_EOK; + +_fail: + if (brrst->regs) + { + rt_iounmap(brrst->regs); + } + + rt_free(brrst); + + return err; +} + +static const struct rt_ofw_node_id brcm_rescal_reset_ofw_ids[] = +{ + { .compatible = "brcm,bcm7216-pcie-sata-rescal" }, + { /* sentinel */ } +}; + +static struct rt_platform_driver brcm_rescal_reset_driver = +{ + .name = "reset-brcm-rescal", + .ids = brcm_rescal_reset_ofw_ids, + + .probe = brcm_rescal_reset_probe, +}; + +static int brcm_rescal_reset_register(void) +{ + rt_platform_driver_register(&brcm_rescal_reset_driver); + + return 0; +} +INIT_SUBSYS_EXPORT(brcm_rescal_reset_register); diff --git a/components/drivers/reset/reset-brcmstb.c b/components/drivers/reset/reset-brcmstb.c new file mode 100644 index 000000000000..1708e106c4c8 --- /dev/null +++ b/components/drivers/reset/reset-brcmstb.c @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-11-26 GuEe-GUI first version + */ + +#include +#include + +#define DBG_TAG "reset.brcmstb" +#define DBG_LVL DBG_INFO +#include + +#define SW_INIT_SET 0x00 +#define SW_INIT_CLEAR 0x04 +#define SW_INIT_STATUS 0x08 + +#define SW_INIT_BIT(id) RT_BIT((id) & 0x1f) +#define SW_INIT_BANK(id) ((id) >> 5) + +/* + * A full bank contains extra registers that we are not utilizing but still + * qualify as a single bank. + */ +#define SW_INIT_BANK_SIZE 0x18 + +struct brcmstb_reset +{ + struct rt_reset_controller parent; + + void *regs; +}; + +static rt_err_t brcmstb_reset_assert(struct rt_reset_control *rstc) +{ + struct brcmstb_reset *brst = rstc->rstcer->priv; + rt_uint32_t off = SW_INIT_BANK(rstc->id) * SW_INIT_BANK_SIZE; + + HWREG32(brst->regs + off + SW_INIT_SET) = SW_INIT_BIT(rstc->id); + + return RT_EOK; +} + +static rt_err_t brcmstb_reset_deassert(struct rt_reset_control *rstc) +{ + struct brcmstb_reset *brst = rstc->rstcer->priv; + unsigned int off = SW_INIT_BANK(rstc->id) * SW_INIT_BANK_SIZE; + + HWREG32(brst->regs + off + SW_INIT_CLEAR) = SW_INIT_BIT(rstc->id); + + /* + * Maximum reset delay after de-asserting a line and seeing block + * operation is typically 14us for the worst case, build some slack here. + */ + rt_hw_us_delay(150); + + return RT_EOK; +} + +static int brcmstb_reset_status(struct rt_reset_control *rstc) +{ + struct brcmstb_reset *brst = rstc->rstcer->priv; + rt_uint32_t off = SW_INIT_BANK(rstc->id) * SW_INIT_BANK_SIZE; + + return HWREG32(brst->regs + off + SW_INIT_STATUS) & SW_INIT_BIT(rstc->id); +} + +const static struct rt_reset_control_ops brcmstb_reset_ops = +{ + .assert = brcmstb_reset_assert, + .deassert = brcmstb_reset_deassert, + .status = brcmstb_reset_status, +}; + +static rt_err_t brcmstb_reset_probe(struct rt_platform_device *pdev) +{ + rt_err_t err; + struct rt_reset_controller *rstcer; + struct rt_device *dev = &pdev->parent; + struct brcmstb_reset *brst = rt_calloc(1, sizeof(*brst)); + + if (!brst) + { + return -RT_ENOMEM; + } + + brst->regs = rt_dm_dev_iomap(dev, 0); + + if (!brst->regs) + { + err = -RT_EIO; + goto _fail; + } + + rstcer = &brst->parent; + + rstcer->priv = brst; + rstcer->ofw_node = dev->ofw_node; + rstcer->ops = &brcmstb_reset_ops; + + return RT_EOK; + +_fail: + if (brst->regs) + { + rt_iounmap(brst->regs); + } + + rt_free(brst); + + return err; +} + +static const struct rt_ofw_node_id brcmstb_reset_ofw_ids[] = +{ + { .compatible = "brcm,brcmstb-reset" }, + { /* sentinel */ } +}; + +static struct rt_platform_driver brcmstb_reset_driver = +{ + .name = "reset-brcmstb", + .ids = brcmstb_reset_ofw_ids, + + .probe = brcmstb_reset_probe, +}; + +static int brcmstb_reset_register(void) +{ + rt_platform_driver_register(&brcmstb_reset_driver); + + return 0; +} +INIT_SUBSYS_EXPORT(brcmstb_reset_register); diff --git a/components/drivers/reset/reset-raspberrypi.c b/components/drivers/reset/reset-raspberrypi.c new file mode 100644 index 000000000000..04abe98cd51f --- /dev/null +++ b/components/drivers/reset/reset-raspberrypi.c @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-11-26 GuEe-GUI first version + */ + +#include +#include + +#define DBG_TAG "reset.brcmstb" +#define DBG_LVL DBG_INFO +#include + +#include +#include "../firmware/raspberrypi/firmware.h" + +struct rpi_reset +{ + struct rt_reset_controller parent; + + struct rpi_firmware *rpi_fw; +}; + +#define raw_to_rpi_reset(raw) rt_container_of(raw, struct rpi_reset, parent) + +static rt_err_t rpi_reset_reset(struct rt_reset_control *rstc) +{ + rt_err_t err = RT_EOK; + rt_uint32_t dev_addr; + struct rpi_reset *rrst = rstc->rstcer->priv; + + switch (rstc->id) { + case RASPBERRYPI_FIRMWARE_RESET_ID_USB: + /* + * The Raspberry Pi 4 gets its USB functionality from VL805, a + * PCIe chip that implements xHCI. After a PCI reset, VL805's + * firmware may either be loaded directly from an EEPROM or, if + * not present, by the SoC's co-processor, VideoCore. rpi's + * VideoCore OS contains both the non public firmware load + * logic and the VL805 firmware blob. This triggers the + * aforementioned process. + * + * The pci device address is expected is expected by the + * firmware encoded like this: + * + * PCI_BUS << 20 | PCI_SLOT << 15 | PCI_FUNC << 12 + * + * But since rpi's PCIe is hardwired, we know the address in + * advance. + */ + dev_addr = 0x100000; + + if ((err = rpi_firmware_property(rrst->rpi_fw, RPI_FIRMWARE_NOTIFY_XHCI_RESET, + &dev_addr, sizeof(dev_addr)))) + { + break; + } + + /* Wait for vl805 to startup */ + rt_hw_us_delay(500); + break; + + default: + err = -RT_EINVAL; + break; + } + + return err; +} + +const static struct rt_reset_control_ops rpi_reset_ops = +{ + .reset = rpi_reset_reset, +}; + +static rt_err_t rpi_reset_probe(struct rt_platform_device *pdev) +{ + rt_err_t err; + struct rt_reset_controller *rstcer; + struct rt_ofw_node *np = pdev->parent.ofw_node, *fw_np; + struct rpi_reset *rrst = rt_calloc(1, sizeof(*rrst)); + + if (!rrst) + { + return -RT_ENOMEM; + } + + fw_np = rt_ofw_parse_phandle(np, "firmware", 0); + + if (!fw_np) + { + err = -RT_EINVAL; + goto _fail; + } + + rrst->rpi_fw = rpi_firmware_get(fw_np); + rt_ofw_node_put(fw_np); + + if (!rrst->rpi_fw) + { + err = -RT_EINVAL; + goto _fail; + } + + rstcer = &rrst->parent; + + rstcer->priv = rrst; + rstcer->ofw_node = np; + rstcer->ops = &rpi_reset_ops; + + return RT_EOK; + +_fail: + rt_free(rrst); + + return err; +} + +static const struct rt_ofw_node_id rpi_reset_ofw_ids[] = +{ + { .compatible = "raspberrypi,firmware-reset" }, + { /* sentinel */ } +}; + +static struct rt_platform_driver rpi_reset_driver = +{ + .name = "reset-raspberrypi", + .ids = rpi_reset_ofw_ids, + + .probe = rpi_reset_probe, +}; + +static int rpi_reset_register(void) +{ + rt_platform_driver_register(&rpi_reset_driver); + + return 0; +} +INIT_SUBSYS_EXPORT(rpi_reset_register); diff --git a/components/drivers/reset/reset-scmi.c b/components/drivers/reset/reset-scmi.c new file mode 100644 index 000000000000..5135aace96f8 --- /dev/null +++ b/components/drivers/reset/reset-scmi.c @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-11-26 GuEe-GUI first version + */ + +#include +#include + +#define DBG_TAG "reset.scmi" +#define DBG_LVL DBG_INFO +#include + +struct scmi_reset +{ + struct rt_reset_controller parent; + + struct rt_scmi_device *sdev; +}; + +#define raw_to_scmi_reset(raw) rt_container_of(raw, struct scmi_reset, parent) + +static rt_err_t scmi_reset_do(struct scmi_reset *srst, int domain, + rt_uint32_t flags, rt_uint32_t state) +{ + struct scmi_reset_in in = + { + .domain_id = rt_cpu_to_le32(domain), + .flags = rt_cpu_to_le32(flags), + .reset_state = rt_cpu_to_le32(state), + }; + struct scmi_reset_out out; + struct rt_scmi_msg msg = RT_SCMI_MSG_IN(SCMI_RESET_RESET, in, out); + + return rt_scmi_process_msg(srst->sdev, &msg); +} + +static rt_err_t scmi_reset_reset(struct rt_reset_control *rstc) +{ + struct scmi_reset *srst = raw_to_scmi_reset(rstc); + + return scmi_reset_do(srst, rstc->id, SCMI_RESET_FLAG_RESET, SCMI_ARCH_COLD_RESET); +} + +static rt_err_t scmi_reset_assert(struct rt_reset_control *rstc) +{ + struct scmi_reset *srst = raw_to_scmi_reset(rstc); + + return scmi_reset_do(srst, rstc->id, SCMI_RESET_FLAG_ASSERT, SCMI_ARCH_COLD_RESET); +} + +static rt_err_t scmi_reset_deassert(struct rt_reset_control *rstc) +{ + struct scmi_reset *srst = raw_to_scmi_reset(rstc); + + return scmi_reset_do(srst, rstc->id, 0, SCMI_ARCH_COLD_RESET); +} + +static const struct rt_reset_control_ops scmi_reset_ops = +{ + .reset = scmi_reset_reset, + .assert = scmi_reset_assert, + .deassert = scmi_reset_deassert, +}; + +static rt_err_t scmi_reset_probe(struct rt_scmi_device *sdev) +{ + rt_err_t err; + struct rt_reset_controller *rstcer; + struct scmi_reset *srst = rt_calloc(1, sizeof(*srst)); + + if (!srst) + { + return -RT_ENOMEM; + } + + rstcer = &srst->parent; + + rstcer->priv = srst; + rstcer->ofw_node = sdev->parent.ofw_node; + rstcer->ops = &scmi_reset_ops; + + if ((err = rt_reset_controller_register(&srst->parent))) + { + rt_free(srst); + } + + return err; +} + +static const struct rt_scmi_device_id scmi_reset_ids[] = +{ + { SCMI_PROTOCOL_ID_RESET, "reset" }, + { /* sentinel */ }, +}; + +static struct rt_scmi_driver scmi_reset_driver = +{ + .name = "reset-scmi", + .ids = scmi_reset_ids, + + .probe = scmi_reset_probe, +}; +RT_SCMI_DRIVER_EXPORT(scmi_reset_driver); diff --git a/components/drivers/reset/reset.c b/components/drivers/reset/reset.c new file mode 100644 index 000000000000..7448725833e1 --- /dev/null +++ b/components/drivers/reset/reset.c @@ -0,0 +1,430 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-11-26 GuEe-GUI first version + */ + +#include +#include + +#define DBG_TAG "rtdm.reset" +#define DBG_LVL DBG_INFO +#include + +#include +#include +#include + +struct reset_control_array +{ + struct rt_reset_control captain; + + rt_size_t count; + struct rt_reset_control *rstcs[]; +}; + +#define reset_control_to_array(rstc) rt_container_of(rstc, struct reset_control_array, captain) + +static struct rt_spinlock _rstcer_lock = { 0 }; +static rt_list_t _rstcer_nodes = RT_LIST_OBJECT_INIT(_rstcer_nodes); + +rt_err_t rt_reset_controller_register(struct rt_reset_controller *rstcer) +{ + rt_ubase_t level; + + if (!rstcer) + { + return -RT_EINVAL; + } + + rt_list_init(&rstcer->list); + rt_list_init(&rstcer->rstc_nodes); + rt_spin_lock_init(&rstcer->spinlock); + + level = rt_spin_lock_irqsave(&_rstcer_lock); + + rt_list_insert_after(&_rstcer_nodes, &rstcer->list); + + rt_spin_unlock_irqrestore(&_rstcer_lock, level); + + if (rstcer->ofw_node) + { + if (!rt_ofw_data(rstcer->ofw_node)) + { + rt_ofw_data(rstcer->ofw_node) = rstcer; + } + } + + return RT_EOK; +} + +rt_err_t rt_reset_controller_unregister(struct rt_reset_controller *rstcer) +{ + if (rstcer) + { + rt_spin_lock(&_rstcer_lock); + + rt_list_remove(&rstcer->list); + + rt_spin_unlock(&_rstcer_lock); + + return RT_EOK; + } + + return -RT_EINVAL; +} + +rt_err_t rt_reset_control_reset(struct rt_reset_control *rstc) +{ + rt_err_t err; + + if (!rstc) + { + return -RT_EINVAL; + } + + if (rstc->rstcer->ops->reset) + { + if ((err = rstc->rstcer->ops->reset(rstc))) + { + return err; + } + } + + if (rstc->is_array) + { + struct reset_control_array *rstc_arr = reset_control_to_array(rstc); + + for (int i = 0; i < rstc_arr->count; ++i) + { + if ((err = rt_reset_control_reset(rstc_arr->rstcs[i]))) + { + return err; + } + } + } + + return RT_EOK; +} + +rt_err_t rt_reset_control_assert(struct rt_reset_control *rstc) +{ + rt_err_t err; + + if (!rstc) + { + return -RT_EINVAL; + } + + if (rstc->rstcer->ops->assert) + { + if ((err = rstc->rstcer->ops->assert(rstc))) + { + return err; + } + } + + if (rstc->is_array) + { + struct reset_control_array *rstc_arr = reset_control_to_array(rstc); + + for (int i = 0; i < rstc_arr->count; ++i) + { + if ((err = rt_reset_control_assert(rstc_arr->rstcs[i]))) + { + if (rstc->rstcer->ops->deassert) + { + rstc->rstcer->ops->deassert(rstc); + } + + while (i --> 0) + { + rt_reset_control_deassert(rstc_arr->rstcs[i]); + } + + return err; + } + } + } + + return RT_EOK; +} + +rt_err_t rt_reset_control_deassert(struct rt_reset_control *rstc) +{ + rt_err_t err; + + if (!rstc) + { + return -RT_EINVAL; + } + + if (rstc->rstcer->ops->deassert) + { + if ((err = rstc->rstcer->ops->deassert(rstc))) + { + return err; + } + } + + if (rstc->is_array) + { + struct reset_control_array *rstc_arr = reset_control_to_array(rstc); + + for (int i = 0; i < rstc_arr->count; ++i) + { + if ((err = rt_reset_control_deassert(rstc_arr->rstcs[i]))) + { + if (rstc->rstcer->ops->assert) + { + rstc->rstcer->ops->assert(rstc); + } + + while (i --> 0) + { + rt_reset_control_assert(rstc_arr->rstcs[i]); + } + + return err; + } + } + } + + return RT_EOK; +} + +int rt_reset_control_status(struct rt_reset_control *rstc) +{ + if (!rstc) + { + return -RT_EINVAL; + } + + if (rstc->rstcer->ops->status) + { + return rstc->rstcer->ops->status(rstc); + } + + return -RT_ENOSYS; +} + +static void reset_free(struct rt_reset_control *rstc) +{ + if (rstc->is_array) + { + struct reset_control_array *rstc_arr = reset_control_to_array(rstc); + + for (int i = 0; i < rstc_arr->count; ++i) + { + rt_reset_control_put(rstc_arr->rstcs[i]); + } + } + + rt_free(rstc); +} + +struct rt_reset_control *rt_reset_control_get_array(struct rt_device *dev) +{ + return rt_ofw_get_reset_control_array(dev->ofw_node); +} + +struct rt_reset_control *rt_reset_control_get_by_index(struct rt_device *dev, int index) +{ + return rt_ofw_get_reset_control_by_index(dev->ofw_node, index); +} + +struct rt_reset_control *rt_reset_control_get_by_name(struct rt_device *dev, const char *name) +{ + return rt_ofw_get_reset_control_by_name(dev->ofw_node, name); +} + +void rt_reset_control_put(struct rt_reset_control *rstc) +{ + struct rt_reset_controller *rstcer; + + if (!rstc) + { + return; + } + + rstcer = rstc->rstcer; + + rt_spin_lock(&rstcer->spinlock); + + rt_list_remove(&rstc->list); + + rt_spin_unlock(&rstcer->spinlock); + + reset_free(rstc); +} + +static struct rt_reset_control *ofw_get_reset_control(struct rt_ofw_node *np, int index, + const char *name, rt_bool_t is_array) +{ + rt_err_t err = RT_EOK; + struct rt_reset_control *rstc; + struct rt_ofw_cell_args reset_args = {}; + struct rt_reset_controller *rstcer = RT_NULL; + + if (is_array) + { + rt_size_t rstc_nr; + struct reset_control_array *rstc_arr; + + rstc_nr = rt_ofw_count_phandle_cells(np, "resets", "#reset-cells"); + + if (!rstc_nr) + { + return RT_NULL; + } + + rstc_arr = rt_calloc(1, sizeof(*rstc_arr) + sizeof(struct rt_reset_control *) * rstc_nr); + + if (!rstc_arr) + { + LOG_E("No memory to create %s[%d] reset control", + rt_ofw_node_full_name(np), index); + + return rt_err_ptr(-RT_ENOMEM); + } + + rstc_arr->count = rstc_nr - 1; + + for (int i = 0; i < rstc_arr->count; ++i) + { + rstc_arr->rstcs[i] = ofw_get_reset_control(np, i + 1, RT_NULL, RT_FALSE); + + if (rt_is_err(rstc_arr->rstcs[i])) + { + err = rt_ptr_err(rstc_arr->rstcs[i]); + + while (i --> 0) + { + rt_reset_control_put(rstc_arr->rstcs[i]); + } + + rt_free(rstc_arr); + rstc_arr = RT_NULL; + + return rt_err_ptr(err); + } + } + + rstc = &rstc_arr->captain; + rstc->is_array = RT_TRUE; + } + else + { + rstc = rt_calloc(1, sizeof(*rstc)); + + if (!rstc) + { + LOG_E("No memory to create %s[%d] reset control", + rt_ofw_node_full_name(np), index); + + return rt_err_ptr(-RT_ENOMEM); + } + } + + if (!rt_ofw_parse_phandle_cells(np, "resets", "#reset-cells", index, &reset_args)) + { + void *rt_data = rt_ofw_data(reset_args.data); + + if (rt_data) + { + /* check is clk */ + if (rt_ofw_prop_read_bool(reset_args.data, "#clock-cells")) + { + struct rt_clk_node *clk_np = rt_data; + struct rt_reset_controller_clk_node *rstcer_clk; + + clk_np = &clk_np[rt_ofw_count_of_clk(reset_args.data) - 1]; + rstcer_clk = rt_container_of(clk_np, struct rt_reset_controller_clk_node, parent); + + rstcer = &rstcer_clk->rstcer; + } + else + { + rstcer = rt_data; + } + } + + rt_ofw_node_put(reset_args.data); + } + + if (!rstcer) + { + err = -RT_EINVAL; + goto _fail; + } + + if (!name && rt_ofw_prop_read_bool(np, "reset-names")) + { + rt_ofw_prop_read_string_index(np, "reset-names", index, &name); + } + + rstc->con_id = name; + rstc->rstcer = rstcer; + + if (rstcer->ops->ofw_parse) + { + err = rstcer->ops->ofw_parse(rstc, &reset_args); + + if (err) + { + LOG_E("Parse %s reset control error = %s", + rt_ofw_node_full_name(np), rt_strerror(err)); + + goto _fail; + } + } + + rstc->id = reset_args.args[0]; + + rt_list_init(&rstc->list); + + rt_spin_lock(&rstcer->spinlock); + + rt_list_insert_after(&rstcer->rstc_nodes, &rstc->list); + + rt_spin_unlock(&rstcer->spinlock); + + return rstc; + +_fail: + if (rstc && !rstc->is_array) + { + rt_free(rstc); + } + + return rt_err_ptr(err); +} + +struct rt_reset_control *rt_ofw_get_reset_control_array(struct rt_ofw_node *np) +{ + return ofw_get_reset_control(np, 0, RT_NULL, RT_TRUE); +} + +struct rt_reset_control *rt_ofw_get_reset_control_by_index(struct rt_ofw_node *np, int index) +{ + return ofw_get_reset_control(np, index, RT_NULL, RT_FALSE); +} + +struct rt_reset_control *rt_ofw_get_reset_control_by_name(struct rt_ofw_node *np, const char *name) +{ + if (np) + { + int index = rt_ofw_prop_index_of_string(np, "reset-names", name); + + if (index >= 0) + { + return ofw_get_reset_control(np, index, name, RT_FALSE); + } + } + + return RT_NULL; +} diff --git a/components/drivers/rtc/Kconfig b/components/drivers/rtc/Kconfig index 8ebece448656..2cf9bcf5bc0f 100755 --- a/components/drivers/rtc/Kconfig +++ b/components/drivers/rtc/Kconfig @@ -1,5 +1,5 @@ menuconfig RT_USING_RTC - bool "Using RTC device drivers" + bool "Using Real Time Clock (RTC) device drivers" default n if RT_USING_RTC @@ -12,10 +12,11 @@ menuconfig RT_USING_RTC default n endif -config RT_RTC_PL031 - bool "ARM PL031" +config RT_RTC_DS1302 + bool "Dallas/Maxim DS1302" depends on RT_USING_DM depends on RT_USING_RTC + select RT_USING_SPI default n config RT_RTC_GOLDFISH @@ -23,3 +24,49 @@ config RT_RTC_GOLDFISH depends on RT_USING_DM depends on RT_USING_RTC default n + +config RT_RTC_HYM8563 + bool "Haoyu Microelectronics HYM8563" + depends on RT_USING_DM + depends on RT_USING_RTC + select RT_USING_I2C + default n + +config RT_RTC_PCF8563 + bool "Philips PCF8563/Epson RTC8564" + depends on RT_USING_DM + depends on RT_USING_RTC + select RT_USING_I2C + default n + +config RT_RTC_PL031 + bool "ARM PL031" + depends on RT_USING_DM + depends on RT_USING_RTC + default n + +config RT_RTC_RK8XX + bool "Rockchip RK805/RK808/RK809/RK817/RK818 RTC" + depends on RT_USING_DM + depends on RT_USING_RTC + depends on RT_MFD_RK8XX + +config RT_RTC_RK_TIMER + bool "Rockchip RTC" + depends on RT_USING_DM + depends on RT_USING_RTC + default n + +config RT_RTC_RPI + bool "Raspberry Pi RTC" + depends on RT_USING_DM + depends on RT_USING_RTC + select RT_FIRMWARE_RASPBERRYPI + default n + +config RT_RTC_RX8010 + bool "Epson RX8010SJ" + depends on RT_USING_DM + depends on RT_USING_RTC + select RT_USING_I2C + default n diff --git a/components/drivers/rtc/SConscript b/components/drivers/rtc/SConscript index 572ec85aedf9..cb87714c2d83 100644 --- a/components/drivers/rtc/SConscript +++ b/components/drivers/rtc/SConscript @@ -16,12 +16,36 @@ if GetDepend(['RT_USING_ALARM']): if GetDepend(['RT_USING_SOFT_RTC']): src += ['soft_rtc.c'] -if GetDepend(['RT_RTC_PL031']): - src += ['rtc-pl031.c'] +if GetDepend(['RT_USING_DM']): + src += ['rtc_dm.c'] + +if GetDepend(['RT_RTC_DS1302']): + src += ['rtc-ds1302.c'] if GetDepend(['RT_RTC_GOLDFISH']): src += ['rtc-goldfish.c'] +if GetDepend(['RT_RTC_HYM8563']): + src += ['rtc-hym8563.c'] + +if GetDepend(['RT_RTC_PCF8563']): + src += ['rtc-pcf8563.c'] + +if GetDepend(['RT_RTC_PL031']): + src += ['rtc-pl031.c'] + +if GetDepend(['RT_RTC_RK8XX']): + src += ['rtc-rk8xx.c'] + +if GetDepend(['RT_RTC_RK_TIMER']): + src += ['rtc-rk_timer.c'] + +if GetDepend(['RT_RTC_RPI']): + src += ['rtc-rpi.c'] + +if GetDepend(['RT_RTC_RX8010']): + src += ['rtc-rx8010.c'] + group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) Return('group') diff --git a/components/drivers/rtc/rtc-ds1302.c b/components/drivers/rtc/rtc-ds1302.c new file mode 100644 index 000000000000..a11660bfe01c --- /dev/null +++ b/components/drivers/rtc/rtc-ds1302.c @@ -0,0 +1,256 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-09-23 GuEe-GUI first version + */ + +#include "rtc_dm.h" + +#define DBG_TAG "rtc.ds1302" +#define DBG_LVL DBG_INFO +#include + +#define RTC_CMD_READ 0x81 /* Read command */ +#define RTC_CMD_WRITE 0x80 /* Write command */ + +#define RTC_CMD_WRITE_ENABLE 0x00 /* Write enable */ +#define RTC_CMD_WRITE_DISABLE 0x80 /* Write disable */ + +#define RTC_ADDR_RAM0 0x20 /* Address of RAM0 */ +#define RTC_ADDR_TCR 0x08 /* Address of trickle charge register */ +#define RTC_CLCK_BURST 0x1F /* Address of clock burst */ +#define RTC_CLCK_LEN 0x08 /* Size of clock burst */ +#define RTC_ADDR_CTRL 0x07 /* Address of control register */ +#define RTC_ADDR_YEAR 0x06 /* Address of year register */ +#define RTC_ADDR_DAY 0x05 /* Address of day of week register */ +#define RTC_ADDR_MON 0x04 /* Address of month register */ +#define RTC_ADDR_DATE 0x03 /* Address of day of month register */ +#define RTC_ADDR_HOUR 0x02 /* Address of hour register */ +#define RTC_ADDR_MIN 0x01 /* Address of minute register */ +#define RTC_ADDR_SEC 0x00 /* Address of second register */ + +static rt_err_t ds1302_rtc_get_time(struct rt_spi_device *spi_dev, time_t *sec) +{ + struct tm tm; + rt_err_t err; + rt_uint8_t addr = RTC_CLCK_BURST << 1 | RTC_CMD_READ, buf[RTC_CLCK_LEN - 1]; + + err = rt_spi_send_then_recv(spi_dev, &addr, sizeof(addr), buf, sizeof(buf)); + + if (rt_unlikely(err)) + { + return err; + } + + /* Decode the registers */ + tm.tm_sec = rt_bcd2bin(buf[RTC_ADDR_SEC]); + tm.tm_min = rt_bcd2bin(buf[RTC_ADDR_MIN]); + tm.tm_hour = rt_bcd2bin(buf[RTC_ADDR_HOUR]); + tm.tm_wday = buf[RTC_ADDR_DAY] - 1; + tm.tm_mday = rt_bcd2bin(buf[RTC_ADDR_DATE]); + tm.tm_mon = rt_bcd2bin(buf[RTC_ADDR_MON]) - 1; + tm.tm_year = rt_bcd2bin(buf[RTC_ADDR_YEAR]) + 100; + + *sec = timegm(&tm); + + return RT_EOK; +} + +static rt_err_t ds1302_rtc_set_time(struct rt_spi_device *spi_dev, time_t *sec) +{ + rt_err_t err; + struct tm *tm; + rt_uint8_t buf[1 + RTC_CLCK_LEN], *bp; + + tm = localtime(sec); + + /* Enable writing */ + bp = buf; + *bp++ = RTC_ADDR_CTRL << 1 | RTC_CMD_WRITE; + *bp++ = RTC_CMD_WRITE_ENABLE; + + err = rt_spi_send_then_recv(spi_dev, buf, 2, RT_NULL, 0); + + if (rt_unlikely(err)) + { + return err; + } + + /* Write registers starting at the first time/date address. */ + bp = buf; + *bp++ = RTC_CLCK_BURST << 1 | RTC_CMD_WRITE; + + *bp++ = rt_bin2bcd(tm->tm_sec); + *bp++ = rt_bin2bcd(tm->tm_min); + *bp++ = rt_bin2bcd(tm->tm_hour); + *bp++ = rt_bin2bcd(tm->tm_mday); + *bp++ = rt_bin2bcd(tm->tm_mon + 1); + *bp++ = tm->tm_wday + 1; + *bp++ = rt_bin2bcd(tm->tm_year % 100); + *bp++ = RTC_CMD_WRITE_DISABLE; + + return rt_spi_send_then_recv(spi_dev, buf, sizeof(buf), RT_NULL, 0); +} + +static rt_err_t ds1302_rtc_control(rt_device_t dev, int cmd, void *args) +{ + rt_err_t err = RT_EOK; + struct rt_spi_device *spi_dev = dev->user_data; + + if (!args) + { + return -RT_EINVAL; + } + + switch (cmd) + { + case RT_DEVICE_CTRL_RTC_GET_TIME: + err = ds1302_rtc_get_time(spi_dev, args); + break; + + case RT_DEVICE_CTRL_RTC_SET_TIME: + err = ds1302_rtc_set_time(spi_dev, args); + break; + + case RT_DEVICE_CTRL_RTC_GET_TIMEVAL: + err = ds1302_rtc_get_time(spi_dev, (time_t *)&((struct timeval *)args)->tv_sec); + break; + + case RT_DEVICE_CTRL_RTC_SET_TIMEVAL: + err = ds1302_rtc_set_time(spi_dev, (time_t *)&((struct timeval *)args)->tv_sec); + break; + + case RT_DEVICE_CTRL_RTC_GET_ALARM: + case RT_DEVICE_CTRL_RTC_SET_ALARM: + err = -RT_ENOSYS; + break; + + default: + err = -RT_EINVAL; + break; + } + + return err; +} + +#ifdef RT_USING_DEVICE_OPS +const static struct rt_device_ops ds1302_rtc_ops = +{ + .control = ds1302_rtc_control, +}; +#endif + +static rt_err_t ds1302_rtc_probe(struct rt_spi_device *spi_dev) +{ + rt_err_t err = RT_EOK; + const char *dev_name; + rt_uint8_t addr, buf[4]; + + if (spi_dev->config.max_hz > 2000000) + { + LOG_E("Speed is too high"); + return -RT_EINVAL; + } + else if (spi_dev->config.mode & RT_SPI_CPHA) + { + LOG_E("Bad mode"); + return -RT_EINVAL; + } + + addr = RTC_ADDR_CTRL << 1 | RTC_CMD_READ; + + if ((err = rt_spi_send_then_recv(spi_dev, &addr, sizeof(addr), buf, 1))) + { + LOG_E("Control register read error = %s", rt_strerror(err)); + return err; + } + + if ((buf[0] & ~RTC_CMD_WRITE_DISABLE) != 0) + { + if ((err = rt_spi_send_then_recv(spi_dev, &addr, sizeof(addr), buf, 1))) + { + LOG_E("Control register read error = %s", rt_strerror(err)); + return err; + } + + if ((buf[0] & ~RTC_CMD_WRITE_DISABLE) != 0) + { + LOG_E("Junk in control register"); + return -RT_EIO; + } + } + + if (buf[0] == 0) + { + buf[0] = RTC_ADDR_CTRL << 1 | RTC_CMD_WRITE; + buf[1] = RTC_CMD_WRITE_DISABLE; + + if ((err = rt_spi_send_then_recv(spi_dev, buf, 2, RT_NULL, 0))) + { + LOG_E("Control register write error = %s", rt_strerror(err)); + return err; + } + + addr = RTC_ADDR_CTRL << 1 | RTC_CMD_READ; + + if ((err = rt_spi_send_then_recv(spi_dev, &addr, sizeof(addr), buf, 1))) + { + LOG_E("Reading control register error = %s", rt_strerror(err)); + return err; + } + + if (buf[0] != RTC_CMD_WRITE_DISABLE) + { + LOG_E("Failed to detect chip"); + return -RT_EIO; + } + } + + spi_dev->parent.user_data = spi_dev; + + spi_dev->parent.type = RT_Device_Class_RTC; +#ifdef RT_USING_DEVICE_OPS + spi_dev->parent.ops = &ds1302_rtc_ops; +#else + spi_dev->parent.control = ds1302_rtc_control; +#endif + + rtc_dev_set_name(&spi_dev->parent); + dev_name = rt_dm_dev_get_name(&spi_dev->parent); + err = rt_device_register(&spi_dev->parent, dev_name, RT_DEVICE_FLAG_RDWR); + + return err; +} + +static rt_err_t ds1302_rtc_remove(struct rt_spi_device *spi_dev) +{ + rt_device_unregister(&spi_dev->parent); + + return RT_EOK; +} + +static const struct rt_spi_device_id ds1302_rtc_ids[] = +{ + { .name = "ds1302" }, + { /* sentinel */ }, +}; + +static const struct rt_ofw_node_id ds1302_rtc_ofw_ids[] = +{ + { .compatible = "maxim,ds1302" }, + { /* sentinel */ }, +}; + +static struct rt_spi_driver ds1302_rtc_driver = +{ + .ids = ds1302_rtc_ids, + .ofw_ids = ds1302_rtc_ofw_ids, + + .probe = ds1302_rtc_probe, + .remove = ds1302_rtc_remove, +}; +RT_SPI_DRIVER_EXPORT(ds1302_rtc_driver); diff --git a/components/drivers/rtc/rtc-goldfish.c b/components/drivers/rtc/rtc-goldfish.c index d23594c1b625..d74d5655081e 100644 --- a/components/drivers/rtc/rtc-goldfish.c +++ b/components/drivers/rtc/rtc-goldfish.c @@ -8,11 +8,7 @@ * 2022-11-26 GuEe-GUI first version */ -#include -#include -#include - -#include +#include "rtc_dm.h" #define GOLDFISH_RTC_TIME_LOW 0x00 /* get low bits of current time and update GOLDFISH_RTC_TIME_HIGH */ #define GOLDFISH_RTC_TIME_HIGH 0x04 /* get high bits of time at last GOLDFISH_RTC_TIME_LOW read */ @@ -67,7 +63,7 @@ static void goldfish_rtc_get_secs(struct goldfish_rtc *grtc, time_t *sec) time |= time_high << 32; } - do_div(time, NSEC_PER_SEC); + rt_do_div(time, NSEC_PER_SEC); rt_memcpy(sec, &time, sizeof(*sec)); } @@ -184,70 +180,81 @@ const static struct rt_device_ops goldfish_rtc_ops = }; #endif -static rt_err_t goldfish_rtc_ofw_init(struct rt_platform_device *pdev, struct goldfish_rtc *grtc) +static rt_err_t goldfish_rtc_probe(struct rt_platform_device *pdev) { rt_err_t err = RT_EOK; - struct rt_ofw_node *np = pdev->parent.ofw_node; - - grtc->base = rt_ofw_iomap(np, 0); + const char *dev_name; + struct rt_device *dev = &pdev->parent; + struct goldfish_rtc *grtc = rt_calloc(1, sizeof(*grtc)); - if (grtc->base) + if (!grtc) { - grtc->irq = rt_ofw_get_irq(np, 0); - - if (grtc->irq >= 0) - { - rt_ofw_data(np) = &grtc->parent; - } - else - { - err = -RT_ERROR; - } + return -RT_ENOMEM; } - else + + grtc->base = rt_dm_dev_iomap(dev, 0); + + if (!grtc->base) { err = -RT_EIO; + goto _fail; } - return err; -} - -static rt_err_t goldfish_rtc_probe(struct rt_platform_device *pdev) -{ - rt_err_t err = RT_EOK; - struct goldfish_rtc *grtc = rt_calloc(1, sizeof(*grtc)); + grtc->irq = rt_dm_dev_get_irq(dev, 0); - if (grtc) - { - err = goldfish_rtc_ofw_init(pdev, grtc); - } - else + if (grtc->irq < 0) { - err = -RT_ENOMEM; + err = grtc->irq; + goto _fail; } - if (!err) - { - grtc->parent.type = RT_Device_Class_RTC; - #ifdef RT_USING_DEVICE_OPS - grtc->parent.ops = &goldfish_rtc_ops; - #else - grtc->parent.control = goldfish_rtc_control; - #endif + rt_dm_dev_bind_fwdata(dev, RT_NULL, &grtc->parent); - rt_device_register(&grtc->parent, "rtc", RT_DEVICE_FLAG_RDWR); + dev->user_data = grtc; - rt_hw_interrupt_install(grtc->irq, goldfish_rtc_isr, grtc, "rtc-goldfish"); - rt_hw_interrupt_umask(grtc->irq); - } - else + grtc->parent.type = RT_Device_Class_RTC; +#ifdef RT_USING_DEVICE_OPS + grtc->parent.ops = &goldfish_rtc_ops; +#else + grtc->parent.control = goldfish_rtc_control; +#endif + + rtc_dev_set_name(&grtc->parent); + dev_name = rt_dm_dev_get_name(&grtc->parent); + rt_device_register(&grtc->parent, dev_name, RT_DEVICE_FLAG_RDWR); + + rt_hw_interrupt_install(grtc->irq, goldfish_rtc_isr, grtc, "rtc-goldfish"); + rt_hw_interrupt_umask(grtc->irq); + + return RT_EOK; + +_fail: + if (grtc->base) { - rt_free(grtc); + rt_iounmap(grtc->base); } + rt_free(grtc); + return err; } +static rt_err_t goldfish_rtc_remove(struct rt_platform_device *pdev) +{ + struct goldfish_rtc *grtc = pdev->parent.user_data; + + rt_hw_interrupt_mask(grtc->irq); + rt_pic_detach_irq(grtc->irq, grtc); + + rt_device_unregister(&grtc->parent); + + rt_iounmap(grtc->base); + + rt_free(grtc); + + return RT_EOK; +} + static const struct rt_ofw_node_id goldfish_rtc_ofw_ids[] = { { .compatible = "google,goldfish-rtc" }, @@ -260,5 +267,6 @@ static struct rt_platform_driver goldfish_rtc_driver = .ids = goldfish_rtc_ofw_ids, .probe = goldfish_rtc_probe, + .remove = goldfish_rtc_remove, }; RT_PLATFORM_DRIVER_EXPORT(goldfish_rtc_driver); diff --git a/components/drivers/rtc/rtc-hym8563.c b/components/drivers/rtc/rtc-hym8563.c new file mode 100644 index 000000000000..2eed9aff1e37 --- /dev/null +++ b/components/drivers/rtc/rtc-hym8563.c @@ -0,0 +1,772 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-12-06 GuEe-GUI first version + */ + +#include "rtc_dm.h" + +#define DBG_TAG "rtc.hym8563" +#define DBG_LVL DBG_INFO +#include + +#define HYM8563_CTL1 0x00 +#define HYM8563_CTL1_TEST RT_BIT(7) +#define HYM8563_CTL1_STOP RT_BIT(5) +#define HYM8563_CTL1_TESTC RT_BIT(3) + +#define HYM8563_CTL2 0x01 +#define HYM8563_CTL2_TI_TP RT_BIT(4) +#define HYM8563_CTL2_AF RT_BIT(3) +#define HYM8563_CTL2_TF RT_BIT(2) +#define HYM8563_CTL2_AIE RT_BIT(1) +#define HYM8563_CTL2_TIE RT_BIT(0) + +#define HYM8563_SEC 0x02 +#define HYM8563_SEC_VL RT_BIT(7) +#define HYM8563_SEC_MASK 0x7f + +#define HYM8563_MIN 0x03 +#define HYM8563_MIN_MASK 0x7f + +#define HYM8563_HOUR 0x04 +#define HYM8563_HOUR_MASK 0x3f + +#define HYM8563_DAY 0x05 +#define HYM8563_DAY_MASK 0x3f + +#define HYM8563_WEEKDAY 0x06 +#define HYM8563_WEEKDAY_MASK 0x07 + +#define HYM8563_MONTH 0x07 +#define HYM8563_MONTH_CENTURY RT_BIT(7) +#define HYM8563_MONTH_MASK 0x1f + +#define HYM8563_YEAR 0x08 + +#define HYM8563_ALM_MIN 0x09 +#define HYM8563_ALM_HOUR 0x0a +#define HYM8563_ALM_DAY 0x0b +#define HYM8563_ALM_WEEK 0x0c + +/* Each alarm check can be disabled by setting this bit in the register */ +#define HYM8563_ALM_BIT_DISABLE RT_BIT(7) + +#define HYM8563_CLKOUT 0x0d +#define HYM8563_CLKOUT_ENABLE RT_BIT(7) +#define HYM8563_CLKOUT_32768 0 +#define HYM8563_CLKOUT_1024 1 +#define HYM8563_CLKOUT_32 2 +#define HYM8563_CLKOUT_1 3 +#define HYM8563_CLKOUT_MASK 3 + +#define HYM8563_TMR_CTL 0x0e +#define HYM8563_TMR_CTL_ENABLE RT_BIT(7) +#define HYM8563_TMR_CTL_4096 0 +#define HYM8563_TMR_CTL_64 1 +#define HYM8563_TMR_CTL_1 2 +#define HYM8563_TMR_CTL_1_60 3 +#define HYM8563_TMR_CTL_MASK 3 + +#define HYM8563_TMR_CNT 0x0f + +struct hym8563_rtc +{ + struct rt_device parent; + struct rt_clk_node clkout_hw; + + int irq; + struct rt_i2c_client *client; + struct rt_thread *irq_thread; + + struct rt_rtc_wkalarm wkalarm; +}; + +#define raw_to_hym8563_rtc(raw) rt_container_of(raw, struct hym8563_rtc, parent) +#define raw_to_hym8563_clkout(raw) rt_container_of(raw, struct hym8563_rtc, clkout_hw) + +static rt_int32_t i2c_smbus_read_byte_data(struct rt_i2c_client *client, + rt_uint8_t command) +{ + rt_int32_t res; + rt_uint8_t ret = 0; + struct rt_i2c_msg msg[2]; + + msg[0].buf = &command; + msg[0].addr = client->client_addr; + msg[0].len = 1; + msg[0].flags = RT_I2C_WR; + + msg[1].buf = &ret; + msg[1].addr = client->client_addr; + msg[1].len = 1; + msg[1].flags = RT_I2C_RD; + + res = rt_i2c_transfer(client->bus, msg, 2); + + return res == 2 ? ret : res; +} + +static rt_int32_t i2c_smbus_write_byte_data(struct rt_i2c_client *client, + rt_uint8_t command, rt_uint8_t value) +{ + rt_int32_t res; + struct rt_i2c_msg msg[1]; + rt_uint8_t data[2] = { command, value }; + + msg[0].buf = data; + msg[0].addr = client->client_addr; + msg[0].len = 2; + msg[0].flags = RT_I2C_WR; + + res = rt_i2c_transfer(client->bus, msg, 1); + + return res == 1 ? 0 : res; +} + +/* Returns the number of read bytes */ +static rt_int32_t i2c_smbus_read_i2c_block_data(struct rt_i2c_client *client, + rt_uint8_t command, rt_uint8_t length, rt_uint8_t *values) +{ + struct rt_i2c_msg msg[2]; + + msg[0].buf = &command; + msg[0].addr = client->client_addr; + msg[0].len = 1; + msg[0].flags = RT_I2C_WR; + + msg[1].buf = values; + msg[1].addr = client->client_addr; + msg[1].len = length; + msg[1].flags = RT_I2C_RD; + + return rt_i2c_transfer(client->bus, msg, 2); +} + +static rt_int32_t i2c_smbus_write_i2c_block_data(struct rt_i2c_client *client, + rt_uint8_t command, rt_uint8_t length, const rt_uint8_t *values) +{ + rt_uint8_t data[32]; + struct rt_i2c_msg msg[1]; + + length = rt_min_t(rt_uint8_t, length, RT_ARRAY_SIZE(data) - 1); + + data[0] = command; + rt_memcpy(&data[1], values, length); + + msg[0].buf = data; + msg[0].addr = client->client_addr; + msg[0].len = length + 1; + msg[0].flags = RT_I2C_WR; + + return rt_i2c_transfer(client->bus, msg, 1); +} + +static void hym8563_rtc_read_time(struct hym8563_rtc *hym8563, time_t *sec) +{ + struct tm tm; + rt_uint8_t buf[7]; + + if (i2c_smbus_read_i2c_block_data(hym8563->client, HYM8563_SEC, 7, buf) < 0) + { + return; + } + + if (buf[0] & HYM8563_SEC_VL) + { + LOG_D("no valid clock/calendar values available"); + } + + tm.tm_sec = rt_bcd2bin(buf[0] & HYM8563_SEC_MASK); + tm.tm_min = rt_bcd2bin(buf[1] & HYM8563_MIN_MASK); + tm.tm_hour = rt_bcd2bin(buf[2] & HYM8563_HOUR_MASK); + tm.tm_mday = rt_bcd2bin(buf[3] & HYM8563_DAY_MASK); + tm.tm_wday = rt_bcd2bin(buf[4] & HYM8563_WEEKDAY_MASK); /* 0 = Sun */ + tm.tm_mon = rt_bcd2bin(buf[5] & HYM8563_MONTH_MASK) - 1; /* 0 = Jan */ + tm.tm_year = rt_bcd2bin(buf[6]) + 100; + + *sec = timegm(&tm); +} + +static void hym8563_rtc_set_time(struct hym8563_rtc *hym8563, time_t *sec) +{ + struct tm *tm; + rt_uint8_t buf[7]; + struct rt_i2c_client *client = hym8563->client; + + tm = localtime(sec); + + /* Years >= 2100 are to far in the future, 19XX is to early */ + if (tm->tm_year < 100 || tm->tm_year >= 200) + { + return; + } + + buf[0] = rt_bin2bcd(tm->tm_sec); + buf[1] = rt_bin2bcd(tm->tm_min); + buf[2] = rt_bin2bcd(tm->tm_hour); + buf[3] = rt_bin2bcd(tm->tm_mday); + buf[4] = rt_bin2bcd(tm->tm_wday); + buf[5] = rt_bin2bcd(tm->tm_mon + 1); + + /* + * While the HYM8563 has a century flag in the month register, + * it does not seem to carry it over a subsequent write/read. + * So we'll limit ourself to 100 years, starting at 2000 for now. + */ + buf[6] = rt_bin2bcd(tm->tm_year - 100); + + /* CTL1 only contains TEST-mode bits apart from stop, so no need to read the value first */ + if (i2c_smbus_write_byte_data(client, HYM8563_CTL1, HYM8563_CTL1_STOP) < 0) + { + return; + } + + if (i2c_smbus_write_i2c_block_data(client, HYM8563_SEC, 7, buf) < 0) + { + return; + } + + if (i2c_smbus_write_byte_data(client, HYM8563_CTL1, 0) < 0) + { + return; + } +} + +static int hym8563_rtc_alarm_irq_enable(struct hym8563_rtc *hym8563, rt_bool_t enabled) +{ + int data; + struct rt_i2c_client *client = hym8563->client; + + data = i2c_smbus_read_byte_data(client, HYM8563_CTL2); + + if (data < 0) + { + return data; + } + + if (enabled) + { + data |= HYM8563_CTL2_AIE; + } + else + { + data &= ~HYM8563_CTL2_AIE; + } + + return i2c_smbus_write_byte_data(client, HYM8563_CTL2, data); +}; + +static int hym8563_rtc_read_alarm(struct hym8563_rtc *hym8563, + struct rt_rtc_wkalarm *alarm) +{ + int res; + rt_uint8_t buf[4]; + struct rt_i2c_client *client = hym8563->client; + + res = i2c_smbus_read_i2c_block_data(client, HYM8563_ALM_MIN, 4, buf); + + if (res < 0) + { + return res; + } + + /* The alarm only has a minute accuracy */ + alarm->tm_sec = 0; + alarm->tm_min = (buf[0] & HYM8563_ALM_BIT_DISABLE) ? + -1 : rt_bcd2bin(buf[0] & HYM8563_MIN_MASK); + alarm->tm_hour = (buf[1] & HYM8563_ALM_BIT_DISABLE) ? + -1 : rt_bcd2bin(buf[1] & HYM8563_HOUR_MASK); + /* + * alarm->tm_mday = (buf[2] & HYM8563_ALM_BIT_DISABLE) ? + * -1 : rt_bcd2bin(buf[2] & HYM8563_DAY_MASK); + * + * alarm->tm_wday = (buf[3] & HYM8563_ALM_BIT_DISABLE) ? + * -1 : rt_bcd2bin(buf[3] & HYM8563_WEEKDAY_MASK); + */ + + res = i2c_smbus_read_byte_data(client, HYM8563_CTL2); + + if (res < 0) + { + return res; + } + + alarm->enable = res & HYM8563_CTL2_AIE ? RT_TRUE : RT_FALSE; + + return 0; +} + +static int hym8563_rtc_set_alarm(struct hym8563_rtc *hym8563, + struct rt_rtc_wkalarm *alarm) +{ + int res; + rt_uint8_t buf[4]; + struct rt_i2c_client *client = hym8563->client; + struct rt_rtc_wkalarm *wkalarm = &hym8563->wkalarm; + + res = i2c_smbus_read_byte_data(client, HYM8563_CTL2); + + if (res < 0) + { + return res; + } + + res &= ~HYM8563_CTL2_AIE; + + res = i2c_smbus_write_byte_data(client, HYM8563_CTL2, res); + + if (res < 0) + { + return res; + } + + buf[0] = (alarm->tm_min < 60 && alarm->tm_min >= 0) ? + rt_bin2bcd(alarm->tm_min) : HYM8563_ALM_BIT_DISABLE; + buf[1] = (alarm->tm_hour < 24 && alarm->tm_hour >= 0) ? + rt_bin2bcd(alarm->tm_hour) : HYM8563_ALM_BIT_DISABLE; + buf[2] = HYM8563_ALM_BIT_DISABLE; + buf[3] = HYM8563_ALM_BIT_DISABLE; + + /* + * buf[2] = (alarm->tm_mday <= 31 && alarm->tm_mday >= 1) ? + * rt_bin2bcd(alarm->tm_mday) : HYM8563_ALM_BIT_DISABLE; + * + * buf[3] = (alarm->tm_wday < 7 && alarm->tm_wday >= 0) ? + * rt_bin2bcd(alarm->tm_wday) : HYM8563_ALM_BIT_DISABLE; + */ + + res = i2c_smbus_write_i2c_block_data(client, HYM8563_ALM_MIN, 4, buf); + + if (res < 0) + { + return res; + } + + res = hym8563_rtc_alarm_irq_enable(hym8563, alarm->enable); + + if (!(res < 0)) + { + wkalarm->enable = alarm->enable; + wkalarm->tm_hour = alarm->tm_hour; + wkalarm->tm_min = alarm->tm_min; + wkalarm->tm_sec = alarm->tm_sec; + } + + return res; +} + +static rt_err_t hym8563_rtc_control(rt_device_t dev, int cmd, void *args) +{ + rt_err_t err = RT_EOK; + struct hym8563_rtc *hym8563 = raw_to_hym8563_rtc(dev); + + if (!args) + { + return -RT_EINVAL; + } + + switch (cmd) + { + case RT_DEVICE_CTRL_RTC_GET_TIME: + hym8563_rtc_read_time(hym8563, args); + break; + + case RT_DEVICE_CTRL_RTC_SET_TIME: + hym8563_rtc_set_time(hym8563, args); + break; + + case RT_DEVICE_CTRL_RTC_GET_TIMEVAL: + hym8563_rtc_read_time(hym8563, (time_t *)&((struct timeval *)args)->tv_sec); + break; + + case RT_DEVICE_CTRL_RTC_SET_TIMEVAL: + hym8563_rtc_set_time(hym8563, (time_t *)&((struct timeval *)args)->tv_sec); + break; + + case RT_DEVICE_CTRL_RTC_GET_ALARM: + err = hym8563_rtc_read_alarm(hym8563, args); + break; + + case RT_DEVICE_CTRL_RTC_SET_ALARM: + err = hym8563_rtc_set_alarm(hym8563, args); + break; + + default: + err = -RT_EINVAL; + break; + } + + return err; +} + +#ifdef RT_USING_DEVICE_OPS +const static struct rt_device_ops hym8563_rtc_ops = +{ + .control = hym8563_rtc_control, +}; +#endif + +static void hym8563_rtc_thread_isr(void *param) +{ + int data, res; + struct hym8563_rtc *hym8563 = param; + struct rt_i2c_client *client = hym8563->client; + + while (RT_TRUE) + { + rt_thread_suspend(hym8563->irq_thread); + rt_schedule(); + + data = i2c_smbus_read_byte_data(client, HYM8563_CTL2); + + if (data < 0) + { + LOG_E("IRQ: error %sing i2c data %d", "read", data); + + return; + } + + data &= ~HYM8563_CTL2_AF; + + res = i2c_smbus_write_byte_data(client, HYM8563_CTL2, data); + + if (res < 0) + { + LOG_E("IRQ: error %sing i2c data %d", "writ", res); + + return; + } + + rt_alarm_update(&hym8563->parent, 1); + } +} + +static void hym8563_rtc_isr(int irqno, void *param) +{ + struct hym8563_rtc *hym8563 = param; + + rt_thread_resume(hym8563->irq_thread); +} + +static int clkout_rates[] = +{ + 32768, 1024, 32, 1, +}; + +static rt_err_t hym8563_clkout_init(struct rt_clk *clk, void *fw_data) +{ + int res; + struct hym8563_rtc *hym8563 = raw_to_hym8563_clkout(clk->clk_np); + + res = i2c_smbus_read_byte_data(hym8563->client, HYM8563_CLKOUT); + + if (res < 0) + { + return res; + } + + res &= HYM8563_CLKOUT_MASK; + clk->rate = clkout_rates[res]; + + return RT_EOK; +} + +static int hym8563_clkout_control(struct hym8563_rtc *hym8563, rt_bool_t enable) +{ + int res = i2c_smbus_read_byte_data(hym8563->client, HYM8563_CLKOUT); + + if (res < 0) + { + return res; + } + + if (enable) + { + res |= HYM8563_CLKOUT_ENABLE; + } + else + { + res &= ~HYM8563_CLKOUT_ENABLE; + } + + return i2c_smbus_write_byte_data(hym8563->client, HYM8563_CLKOUT, res); +} + +static rt_err_t hym8563_clkout_prepare(struct rt_clk *clk) +{ + struct hym8563_rtc *hym8563 = raw_to_hym8563_clkout(clk->clk_np); + + return hym8563_clkout_control(hym8563, RT_TRUE); +} + +static void hym8563_clkout_unprepare(struct rt_clk *clk) +{ + struct hym8563_rtc *hym8563 = raw_to_hym8563_clkout(clk->clk_np); + + hym8563_clkout_control(hym8563, RT_FALSE); +} + +static rt_bool_t hym8563_clkout_is_prepared(struct rt_clk *clk) +{ + int res; + struct hym8563_rtc *hym8563 = raw_to_hym8563_clkout(clk->clk_np); + + res = i2c_smbus_read_byte_data(hym8563->client, HYM8563_CLKOUT); + + if (res < 0) + { + return RT_FALSE; + } + + return !!(res & HYM8563_CLKOUT_ENABLE); +} + +static rt_err_t hym8563_clkout_set_rate(struct rt_clk *clk, rt_ubase_t rate, + rt_ubase_t parent_rate) +{ + int res; + struct hym8563_rtc *hym8563 = raw_to_hym8563_clkout(clk->clk_np); + + res = i2c_smbus_read_byte_data(hym8563->client, HYM8563_CLKOUT); + + if (res < 0) + { + return res; + } + + for (int i = 0; i < RT_ARRAY_SIZE(clkout_rates); ++i) + { + if (clkout_rates[i] == rate) + { + res &= ~HYM8563_CLKOUT_MASK; + res |= i; + + res = i2c_smbus_write_byte_data(hym8563->client, HYM8563_CLKOUT, res); + + if (res >= 0) + { + clk->rate = rate; + } + + return res; + } + } + + return -RT_EINVAL; +} + +static rt_base_t hym8563_clkout_round_rate(struct rt_clk *clk, rt_ubase_t drate, + rt_ubase_t *prate) +{ + for (int i = 0; i < RT_ARRAY_SIZE(clkout_rates); ++i) + { + if (clkout_rates[i] <= drate) + { + return clkout_rates[i]; + } + } + + return 0; +} + +static const struct rt_clk_ops hym8563_clkout_ops = +{ + .init = hym8563_clkout_init, + .prepare = hym8563_clkout_prepare, + .unprepare = hym8563_clkout_unprepare, + .is_prepared = hym8563_clkout_is_prepared, + .set_rate = hym8563_clkout_set_rate, + .round_rate = hym8563_clkout_round_rate, +}; + +static void hym8563_clkout_register_clk(struct hym8563_rtc *hym8563) +{ + if (i2c_smbus_write_byte_data(hym8563->client, HYM8563_CLKOUT, 0) < 0) + { + return; + } + + hym8563->clkout_hw.ops = &hym8563_clkout_ops; + + if (rt_clk_register(&hym8563->clkout_hw, RT_NULL)) + { + return; + } + + rt_dm_dev_bind_fwdata(&hym8563->client->parent, RT_NULL, &hym8563->clkout_hw); +} + +static rt_err_t hym8563_init_device(struct rt_i2c_client *client) +{ + int res; + + /* Clear stop flag if present */ + res = i2c_smbus_write_byte_data(client, HYM8563_CTL1, 0); + if (res < 0) + { + return res; + } + + res = i2c_smbus_read_byte_data(client, HYM8563_CTL2); + if (res < 0) + { + return res; + } + + /* Disable alarm and timer interrupts */ + res &= ~HYM8563_CTL2_AIE; + res &= ~HYM8563_CTL2_TIE; + + /* Clear any pending alarm and timer flags */ + if (res & HYM8563_CTL2_AF) + { + res &= ~HYM8563_CTL2_AF; + } + + if (res & HYM8563_CTL2_TF) + { + res &= ~HYM8563_CTL2_TF; + } + + res &= ~HYM8563_CTL2_TI_TP; + + return i2c_smbus_write_byte_data(client, HYM8563_CTL2, res); +} + +static rt_err_t hym8563_rtc_probe(struct rt_i2c_client *client) +{ + rt_err_t err; + rt_int32_t res; + const char *dev_name; + struct rt_device *dev = &client->parent; + struct hym8563_rtc *hym8563 = rt_calloc(1, sizeof(*hym8563)); + + if (!hym8563) + { + return -RT_ENOMEM; + } + + if ((res = hym8563_init_device(client)) < 0) + { + err = res; + + goto _fail; + } + + hym8563->irq = rt_dm_dev_get_irq(dev, 0); + hym8563->client = client; + + /* check state of calendar information */ + if ((res = i2c_smbus_read_byte_data(client, HYM8563_SEC)) < 0) + { + err = res; + + goto _fail; + } + + LOG_D("rtc information is %s", (res & HYM8563_SEC_VL) ? "invalid" : "valid"); + + rt_pin_ctrl_confs_apply_by_name(dev, RT_NULL); + + if (hym8563->irq >= 0) + { + hym8563->irq_thread = rt_thread_create("rtc-hym8563", &hym8563_rtc_thread_isr, + hym8563, SYSTEM_THREAD_STACK_SIZE, RT_THREAD_PRIORITY_MAX / 2, 10); + + if (!hym8563->irq_thread) + { + err = -RT_ERROR; + LOG_E("Create RTC IRQ thread fail"); + goto _fail; + } + + rt_thread_startup(hym8563->irq_thread); + + rt_hw_interrupt_install(hym8563->irq, hym8563_rtc_isr, hym8563, "rtc-hym8563"); + rt_hw_interrupt_umask(hym8563->irq); + } + + dev->user_data = hym8563; + + hym8563->parent.type = RT_Device_Class_RTC; +#ifdef RT_USING_DEVICE_OPS + hym8563->parent.ops = &hym8563_rtc_ops; +#else + hym8563->parent.control = hym8563_rtc_control; +#endif + + rtc_dev_set_name(&hym8563->parent); + dev_name = rt_dm_dev_get_name(&hym8563->parent); + rt_device_register(&hym8563->parent, dev_name, RT_DEVICE_FLAG_RDWR); + + hym8563_clkout_register_clk(hym8563); + + return RT_EOK; + +_fail: + if (hym8563->irq_thread) + { + rt_thread_delete(hym8563->irq_thread); + } + rt_free(hym8563); + + return err; +} + +static rt_err_t hym8563_rtc_remove(struct rt_i2c_client *client) +{ + struct hym8563_rtc *hym8563 = client->parent.user_data; + + rt_dm_dev_unbind_fwdata(&client->parent, RT_NULL); + + if (hym8563->irq >= 0) + { + if (hym8563->wkalarm.enable) + { + hym8563_rtc_alarm_irq_enable(hym8563, RT_FALSE); + } + + rt_hw_interrupt_mask(hym8563->irq); + rt_pic_detach_irq(hym8563->irq, hym8563); + + rt_thread_delete(hym8563->irq_thread); + } + + if (hym8563->clkout_hw.ops) + { + rt_clk_unregister(&hym8563->clkout_hw); + } + + rt_device_unregister(&hym8563->parent); + + rt_free(hym8563); + + return RT_EOK; +} + +static const struct rt_i2c_device_id hym8563_rtc_ids[] = +{ + { .name = "hym8563" }, + { /* sentinel */ }, +}; + +static const struct rt_ofw_node_id hym8563_rtc_ofw_ids[] = +{ + { .compatible = "haoyu,hym8563" }, + { /* sentinel */ }, +}; + +static struct rt_i2c_driver hym8563_rtc_driver = +{ + .ids = hym8563_rtc_ids, + .ofw_ids = hym8563_rtc_ofw_ids, + + .probe = hym8563_rtc_probe, + .remove = hym8563_rtc_remove, +}; +RT_I2C_DRIVER_EXPORT(hym8563_rtc_driver); diff --git a/components/drivers/rtc/rtc-pcf8563.c b/components/drivers/rtc/rtc-pcf8563.c new file mode 100644 index 000000000000..83aaa88516b6 --- /dev/null +++ b/components/drivers/rtc/rtc-pcf8563.c @@ -0,0 +1,675 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-11-26 GuEe-GUI first version + */ + +#include +#include + +#define DBG_TAG "rtc.pcf8563" +#define DBG_LVL DBG_INFO +#include + +#include "rtc_dm.h" + +#define PCF8563_REG_ST1 0x00 /* status */ +#define PCF8563_REG_ST2 0x01 +#define PCF8563_BIT_AIE RT_BIT(1) +#define PCF8563_BIT_AF RT_BIT(3) +#define PCF8563_BITS_ST2_N (7 << 5) + +#define PCF8563_REG_SC 0x02 /* datetime */ +#define PCF8563_REG_MN 0x03 +#define PCF8563_REG_HR 0x04 +#define PCF8563_REG_DM 0x05 +#define PCF8563_REG_DW 0x06 +#define PCF8563_REG_MO 0x07 +#define PCF8563_REG_YR 0x08 + +#define PCF8563_REG_AMN 0x09 /* alarm */ + +#define PCF8563_REG_CLKO 0x0d /* clock out */ +#define PCF8563_REG_CLKO_FE 0x80 /* clock out enabled */ +#define PCF8563_REG_CLKO_F_MASK 0x03 /* frequenc mask */ +#define PCF8563_REG_CLKO_F_32768H 0x00 +#define PCF8563_REG_CLKO_F_1024HZ 0x01 +#define PCF8563_REG_CLKO_F_32HZ 0x02 +#define PCF8563_REG_CLKO_F_1HZ 0x03 + +#define PCF8563_REG_TMRC 0x0e /* timer control */ +#define PCF8563_TMRC_ENABLE RT_BIT(7) +#define PCF8563_TMRC_4096 0 +#define PCF8563_TMRC_64 1 +#define PCF8563_TMRC_1 2 +#define PCF8563_TMRC_1_60 3 +#define PCF8563_TMRC_MASK 3 + +#define PCF8563_REG_TMR 0x0f /* timer */ + +#define PCF8563_SC_LV 0x80 /* low voltage */ +#define PCF8563_MO_C 0x80 /* century */ + +struct pcf8563_rtc +{ + struct rt_device parent; + struct rt_clk_node clkout_hw; + + int irq; + int c_polarity; + + struct rt_i2c_client *client; + struct rt_thread *irq_thread; + + struct rt_rtc_wkalarm wkalarm; +}; + +#define raw_to_pcf8563_rtc(raw) rt_container_of(raw, struct pcf8563_rtc, parent) +#define raw_to_pcf8563_clkout(raw) rt_container_of(raw, struct pcf8563_rtc, clkout_hw) + +static rt_err_t pcf8563_read_block_data(struct pcf8563_rtc *pcf8563, + rt_uint8_t reg, rt_uint8_t length, rt_uint8_t *buf) +{ + rt_int32_t res; + struct rt_i2c_msg msg[2]; + struct rt_i2c_client *client = pcf8563->client; + + msg[0].buf = ® + msg[0].addr = client->client_addr; + msg[0].len = 1; + msg[0].flags = RT_I2C_WR; + + msg[1].buf = buf; + msg[1].addr = client->client_addr; + msg[1].len = length; + msg[1].flags = RT_I2C_RD; + + res = rt_i2c_transfer(client->bus, msg, 2); + + return res > 0 ? RT_EOK : res; +} + +static rt_err_t pcf8563_write_block_data(struct pcf8563_rtc *pcf8563, + rt_uint8_t reg, rt_uint8_t length, rt_uint8_t *buf) +{ + rt_int32_t res; + struct rt_i2c_client *client = pcf8563->client; + + for (int i = 0; i < length; i++) + { + rt_uint8_t data[2] = { reg + i, buf[i] }; + + res = rt_i2c_master_send(client->bus, client->client_addr, + RT_I2C_WR, data, sizeof(data)); + + if (res != sizeof(data)) + { + return -RT_EIO; + } + } + + return RT_EOK; +} + +static rt_err_t pcf8563_set_alarm_mode(struct pcf8563_rtc *pcf8563, rt_bool_t on) +{ + rt_err_t err; + rt_uint8_t buf; + + err = pcf8563_read_block_data(pcf8563, PCF8563_REG_ST2, 1, &buf); + + if (err) + { + return err; + } + + if (on) + { + buf |= PCF8563_BIT_AIE; + } + else + { + buf &= ~PCF8563_BIT_AIE; + } + + buf &= ~(PCF8563_BIT_AF | PCF8563_BITS_ST2_N); + + err = pcf8563_write_block_data(pcf8563, PCF8563_REG_ST2, 1, &buf); + + if (err) + { + LOG_E("Write %s error", "PCF8563_REG_ST2"); + + return -RT_EIO; + } + + return RT_EOK; +} + +static rt_err_t pcf8563_get_alarm_mode(struct pcf8563_rtc *pcf8563, + rt_uint8_t *en, rt_uint8_t *pen) +{ + rt_err_t err; + rt_uint8_t buf; + + err = pcf8563_read_block_data(pcf8563, PCF8563_REG_ST2, 1, &buf); + + if (err) + { + return err; + } + + if (en) + { + *en = !!(buf & PCF8563_BIT_AIE); + } + + if (pen) + { + *pen = !!(buf & PCF8563_BIT_AF); + } + + return RT_EOK; +} + +static int pcf8563_rtc_read_time(struct pcf8563_rtc *pcf8563, time_t *sec) +{ + rt_err_t err; + struct tm tm; + rt_uint8_t buf[9]; + + err = pcf8563_read_block_data(pcf8563, PCF8563_REG_ST1, 9, buf); + + if (err) + { + return err; + } + + if (buf[PCF8563_REG_SC] & PCF8563_SC_LV) + { + LOG_E("Low voltage detected, date/time is not reliable"); + + return -RT_EINVAL; + } + + tm.tm_sec = rt_bcd2bin(buf[PCF8563_REG_SC] & 0x7f); + tm.tm_min = rt_bcd2bin(buf[PCF8563_REG_MN] & 0x7f); + tm.tm_hour = rt_bcd2bin(buf[PCF8563_REG_HR] & 0x3f); /* rtc hr 0-23 */ + tm.tm_mday = rt_bcd2bin(buf[PCF8563_REG_DM] & 0x3f); + tm.tm_wday = buf[PCF8563_REG_DW] & 0x07; + tm.tm_mon = rt_bcd2bin(buf[PCF8563_REG_MO] & 0x1f) - 1; /* rtc mn 1-12 */ + tm.tm_year = rt_bcd2bin(buf[PCF8563_REG_YR]) + 100; + + /* detect the polarity heuristically. see note above. */ + pcf8563->c_polarity = (buf[PCF8563_REG_MO] & PCF8563_MO_C) ? + (tm.tm_year >= 100) : (tm.tm_year < 100); + + *sec = timegm(&tm); + + return RT_EOK; +} + +static int pcf8563_rtc_set_time(struct pcf8563_rtc *pcf8563, time_t *sec) +{ + struct tm *tm; + rt_uint8_t buf[9]; + + tm = localtime(sec); + + /* hours, minutes and seconds */ + buf[PCF8563_REG_SC] = rt_bin2bcd(tm->tm_sec); + buf[PCF8563_REG_MN] = rt_bin2bcd(tm->tm_min); + buf[PCF8563_REG_HR] = rt_bin2bcd(tm->tm_hour); + + buf[PCF8563_REG_DM] = rt_bin2bcd(tm->tm_mday); + + /* month, 1 - 12 */ + buf[PCF8563_REG_MO] = rt_bin2bcd(tm->tm_mon + 1); + + /* year and century */ + buf[PCF8563_REG_YR] = rt_bin2bcd(tm->tm_year - 100); + if (pcf8563->c_polarity ? (tm->tm_year >= 100) : (tm->tm_year < 100)) + { + buf[PCF8563_REG_MO] |= PCF8563_MO_C; + } + + buf[PCF8563_REG_DW] = tm->tm_wday & 0x07; + + return pcf8563_write_block_data(pcf8563, PCF8563_REG_SC, + 9 - PCF8563_REG_SC, buf + PCF8563_REG_SC); +} + +static int pcf8563_rtc_read_alarm(struct pcf8563_rtc *pcf8563, + struct rt_rtc_wkalarm *alarm) +{ + rt_err_t err; + rt_uint8_t pending, buf[4]; + + err = pcf8563_read_block_data(pcf8563, PCF8563_REG_AMN, 4, buf); + + if (err) + { + return err; + } + + alarm->tm_sec = 0; + alarm->tm_min = rt_bcd2bin(buf[0] & 0x7f); + alarm->tm_hour = rt_bcd2bin(buf[1] & 0x3f); + /* + * alarm->tm_mday = rt_bcd2bin(buf[2] & 0x3f); + * alarm->tm_wday = rt_bcd2bin(buf[3] & 0x7); + */ + + return pcf8563_get_alarm_mode(pcf8563, (rt_uint8_t *)&alarm->enable, &pending); +} + +static int pcf8563_rtc_set_alarm(struct pcf8563_rtc *pcf8563, + struct rt_rtc_wkalarm *alarm) +{ + rt_err_t err; + rt_uint8_t buf[4]; + struct rt_rtc_wkalarm *wkalarm = &pcf8563->wkalarm; + + buf[0] = rt_bin2bcd(alarm->tm_min); + buf[1] = rt_bin2bcd(alarm->tm_hour); + buf[2] = rt_bin2bcd(0); /* alarm->tm_mday */ + buf[3] = 0 & 0x07; /* alarm->tm_wday */ + + err = pcf8563_write_block_data(pcf8563, PCF8563_REG_AMN, 4, buf); + + if (err) + { + return err; + } + + err = pcf8563_set_alarm_mode(pcf8563, alarm->enable); + + if (!err) + { + wkalarm->enable = alarm->enable; + wkalarm->tm_hour = alarm->tm_hour; + wkalarm->tm_min = alarm->tm_min; + wkalarm->tm_sec = alarm->tm_sec; + } + + return err; +} + +static rt_err_t pcf8563_rtc_control(rt_device_t dev, int cmd, void *args) +{ + rt_err_t err = RT_EOK; + struct pcf8563_rtc *pcf8563 = raw_to_pcf8563_rtc(dev); + + if (!args) + { + return -RT_EINVAL; + } + + switch (cmd) + { + case RT_DEVICE_CTRL_RTC_GET_TIME: + err = pcf8563_rtc_read_time(pcf8563, args); + break; + + case RT_DEVICE_CTRL_RTC_SET_TIME: + err = pcf8563_rtc_set_time(pcf8563, args); + break; + + case RT_DEVICE_CTRL_RTC_GET_TIMEVAL: + err = pcf8563_rtc_read_time(pcf8563, (time_t *)&((struct timeval *)args)->tv_sec); + break; + + case RT_DEVICE_CTRL_RTC_SET_TIMEVAL: + err = pcf8563_rtc_set_time(pcf8563, (time_t *)&((struct timeval *)args)->tv_sec); + break; + + case RT_DEVICE_CTRL_RTC_GET_ALARM: + err = pcf8563_rtc_read_alarm(pcf8563, args); + break; + + case RT_DEVICE_CTRL_RTC_SET_ALARM: + err = pcf8563_rtc_set_alarm(pcf8563, args); + break; + + default: + err = -RT_EINVAL; + break; + } + + return err; +} + +#ifdef RT_USING_DEVICE_OPS +const static struct rt_device_ops pcf8563_rtc_ops = +{ + .control = pcf8563_rtc_control, +}; +#endif + +static void pcf8563_rtc_thread_isr(void *param) +{ + rt_err_t err; + rt_uint8_t pending; + struct pcf8563_rtc *pcf8563 = param; + + while (RT_TRUE) + { + rt_thread_suspend(pcf8563->irq_thread); + rt_schedule(); + + err = pcf8563_get_alarm_mode(pcf8563, NULL, &pending); + + if (err) + { + continue; + } + + if (pending) + { + rt_alarm_update(&pcf8563->parent, 1); + + pcf8563_set_alarm_mode(pcf8563, 1); + } + } +} + +static void pcf8563_rtc_isr(int irqno, void *param) +{ + struct pcf8563_rtc *pcf8563 = param; + + rt_thread_resume(pcf8563->irq_thread); +} + +static const int clkout_rates[] = +{ + 32768, 1024, 32, 1, +}; + +static rt_err_t pcf8563_clkout_init(struct rt_clk *clk, void *fw_data) +{ + rt_err_t err; + rt_uint8_t buf; + struct pcf8563_rtc *pcf8563 = raw_to_pcf8563_clkout(clk->clk_np); + + err = pcf8563_read_block_data(pcf8563, PCF8563_REG_CLKO, 1, &buf); + + if (err) + { + return err; + } + + buf &= PCF8563_REG_CLKO_F_MASK; + clk->rate = clkout_rates[buf]; + + return RT_EOK; +} + +static rt_err_t pcf8563_clkout_control(struct pcf8563_rtc *pcf8563, rt_bool_t enable) +{ + rt_err_t err; + rt_uint8_t buf; + + if ((err = pcf8563_read_block_data(pcf8563, PCF8563_REG_CLKO, 1, &buf))) + { + return err; + } + + if (enable) + { + buf |= PCF8563_REG_CLKO_FE; + } + else + { + buf &= ~PCF8563_REG_CLKO_FE; + } + + return pcf8563_write_block_data(pcf8563, PCF8563_REG_CLKO, 1, &buf); +} + +static rt_err_t pcf8563_clkout_prepare(struct rt_clk *clk) +{ + struct pcf8563_rtc *pcf8563 = raw_to_pcf8563_clkout(clk->clk_np); + + return pcf8563_clkout_control(pcf8563, RT_TRUE); +} + +static void pcf8563_clkout_unprepare(struct rt_clk *clk) +{ + struct pcf8563_rtc *pcf8563 = raw_to_pcf8563_clkout(clk->clk_np); + + pcf8563_clkout_control(pcf8563, RT_FALSE); +} + +static rt_bool_t pcf8563_clkout_is_prepared(struct rt_clk *clk) +{ + rt_uint8_t buf; + struct pcf8563_rtc *pcf8563 = raw_to_pcf8563_clkout(clk->clk_np); + + if (pcf8563_read_block_data(pcf8563, PCF8563_REG_CLKO, 1, &buf)) + { + return RT_FALSE; + } + + return !!(buf & PCF8563_REG_CLKO_FE); +} + +static rt_err_t pcf8563_clkout_set_rate(struct rt_clk *clk, rt_ubase_t rate, + rt_ubase_t parent_rate) +{ + rt_err_t err; + rt_uint8_t buf; + struct pcf8563_rtc *pcf8563 = raw_to_pcf8563_clkout(clk->clk_np); + + err = pcf8563_read_block_data(pcf8563, PCF8563_REG_CLKO, 1, &buf); + + if (err) + { + return err; + } + + for (int i = 0; i < RT_ARRAY_SIZE(clkout_rates); ++i) + { + if (clkout_rates[i] == rate) + { + buf &= ~PCF8563_REG_CLKO_F_MASK; + buf |= i; + + err = pcf8563_write_block_data(pcf8563, PCF8563_REG_CLKO, 1, &buf); + + if (!err) + { + clk->rate = rate; + } + + return err; + } + } + + + return -RT_EINVAL; +} + +static rt_base_t pcf8563_clkout_round_rate(struct rt_clk *clk, rt_ubase_t drate, + rt_ubase_t *prate) +{ + for (int i = 0; i < RT_ARRAY_SIZE(clkout_rates); ++i) + { + if (clkout_rates[i] <= drate) + { + return clkout_rates[i]; + } + } + + return 0; +} + +static const struct rt_clk_ops pcf8563_clkout_ops = +{ + .init = pcf8563_clkout_init, + .prepare = pcf8563_clkout_prepare, + .unprepare = pcf8563_clkout_unprepare, + .is_prepared = pcf8563_clkout_is_prepared, + .set_rate = pcf8563_clkout_set_rate, + .round_rate = pcf8563_clkout_round_rate, +}; + +static void pcf8563_clkout_register_clk(struct pcf8563_rtc *pcf8563) +{ + rt_uint8_t buf; + + if (pcf8563_write_block_data(pcf8563, PCF8563_REG_CLKO, 1, &buf)) + { + return; + } + + pcf8563->clkout_hw.ops = &pcf8563_clkout_ops; + + if (rt_clk_register(&pcf8563->clkout_hw, RT_NULL)) + { + return; + } + + rt_dm_dev_bind_fwdata(&pcf8563->client->parent, RT_NULL, &pcf8563->clkout_hw); +} + +static rt_err_t pcf8563_rtc_probe(struct rt_i2c_client *client) +{ + rt_err_t err; + rt_uint8_t buf; + const char *dev_name; + struct rt_device *dev = &client->parent; + struct pcf8563_rtc *pcf8563 = rt_calloc(1, sizeof(*pcf8563)); + + if (!pcf8563) + { + return -RT_ENOMEM; + } + + pcf8563->irq = rt_dm_dev_get_irq(dev, 0); + pcf8563->client = client; + + /* Set timer to lowest frequency to save power (ref Haoyu datasheet) */ + buf = PCF8563_TMRC_1_60; + + if ((err = pcf8563_write_block_data(pcf8563, PCF8563_REG_TMRC, 1, &buf))) + { + LOG_E("Write %s error", "PCF8563_REG_TMRC"); + goto _fail; + } + + /* Clear flags and disable interrupts */ + buf = 0; + + if ((err = pcf8563_write_block_data(pcf8563, PCF8563_REG_ST2, 1, &buf))) + { + LOG_E("Write %s error", "PCF8563_REG_ST2"); + goto _fail; + } + + if (pcf8563->irq >= 0) + { + pcf8563->irq_thread = rt_thread_create("rtc-pcf8563", &pcf8563_rtc_thread_isr, + pcf8563, SYSTEM_THREAD_STACK_SIZE, RT_THREAD_PRIORITY_MAX / 2, 10); + + if (!pcf8563->irq_thread) + { + err = -RT_ERROR; + LOG_E("Create RTC IRQ thread fail"); + goto _fail; + } + + rt_thread_startup(pcf8563->irq_thread); + + rt_hw_interrupt_install(pcf8563->irq, pcf8563_rtc_isr, pcf8563, "rtc-pcf8563"); + rt_hw_interrupt_umask(pcf8563->irq); + } + + dev->user_data = pcf8563; + + pcf8563->parent.type = RT_Device_Class_RTC; +#ifdef RT_USING_DEVICE_OPS + pcf8563->parent.ops = &pcf8563_rtc_ops; +#else + pcf8563->parent.control = pcf8563_rtc_control; +#endif + + rtc_dev_set_name(&pcf8563->parent); + dev_name = rt_dm_dev_get_name(&pcf8563->parent); + rt_device_register(&pcf8563->parent, dev_name, RT_DEVICE_FLAG_RDWR); + + pcf8563_clkout_register_clk(pcf8563); + + return RT_EOK; + +_fail: + if (pcf8563->irq_thread) + { + rt_thread_delete(pcf8563->irq_thread); + } + rt_free(pcf8563); + + return err; +} + +static rt_err_t pcf8563_rtc_remove(struct rt_i2c_client *client) +{ + struct pcf8563_rtc *pcf8563 = client->parent.user_data; + + rt_dm_dev_unbind_fwdata(&client->parent, RT_NULL); + + if (pcf8563->irq >= 0) + { + if (pcf8563->wkalarm.enable) + { + pcf8563_set_alarm_mode(pcf8563, RT_FALSE); + } + + rt_hw_interrupt_mask(pcf8563->irq); + rt_pic_detach_irq(pcf8563->irq, pcf8563); + + rt_thread_delete(pcf8563->irq_thread); + } + + if (pcf8563->clkout_hw.ops) + { + rt_clk_unregister(&pcf8563->clkout_hw); + } + + rt_device_unregister(&pcf8563->parent); + + rt_free(pcf8563); + + return RT_EOK; +} + +static const struct rt_i2c_device_id pcf8563_rtc_ids[] = +{ + { .name = "pcf8563" }, + { .name = "rtc8564" }, + { .name = "pca8565" }, + { /* sentinel */ }, +}; + +static const struct rt_ofw_node_id pcf8563_rtc_ofw_ids[] = +{ + { .compatible = "nxp,pcf8563" }, + { .compatible = "epson,rtc8564" }, + { .compatible = "microcrystal,rv8564" }, + { .compatible = "nxp,pca8565" }, + { /* sentinel */ }, +}; + +static struct rt_i2c_driver pcf8563_rtc_driver = +{ + .ids = pcf8563_rtc_ids, + .ofw_ids = pcf8563_rtc_ofw_ids, + + .probe = pcf8563_rtc_probe, + .remove = pcf8563_rtc_remove, +}; +RT_I2C_DRIVER_EXPORT(pcf8563_rtc_driver); + diff --git a/components/drivers/rtc/rtc-pl031.c b/components/drivers/rtc/rtc-pl031.c index 25cc9a4bf87b..c6b482c1b6c9 100755 --- a/components/drivers/rtc/rtc-pl031.c +++ b/components/drivers/rtc/rtc-pl031.c @@ -8,11 +8,7 @@ * 2022-11-26 GuEe-GUI first version */ -#include -#include -#include - -#include +#include "rtc_dm.h" #define PL031_DR 0x00 /* data read register */ #define PL031_MR 0x04 /* match register */ @@ -34,6 +30,7 @@ struct pl031 int irq; void *base; + struct rt_clk *pclk; struct rt_rtc_wkalarm wkalarm; }; @@ -186,71 +183,105 @@ const static struct rt_device_ops pl031_rtc_ops = }; #endif -static rt_err_t pl031_ofw_init(struct rt_platform_device *pdev, struct pl031 *pl031) +static rt_err_t pl031_probe(struct rt_platform_device *pdev) { rt_err_t err = RT_EOK; - struct rt_ofw_node *np = pdev->parent.ofw_node; - - pl031->base = rt_ofw_iomap(np, 0); + const char *dev_name; + struct rt_device *dev = &pdev->parent; + struct pl031 *pl031 = rt_calloc(1, sizeof(*pl031)); - if (pl031->base) + if (!pl031) { - pl031->irq = rt_ofw_get_irq(np, 0); - - if (pl031->irq >= 0) - { - rt_ofw_data(np) = &pl031->parent; - } - else - { - err = -RT_ERROR; - } + return -RT_ENOMEM; } - else + + pl031->base = rt_dm_dev_iomap(dev, 0); + + if (!pl031->base) { err = -RT_EIO; + + goto _fail; } - return err; -} + pl031->irq = rt_dm_dev_get_irq(dev, 0); -static rt_err_t pl031_probe(struct rt_platform_device *pdev) -{ - rt_err_t err = RT_EOK; - struct pl031 *pl031 = rt_calloc(1, sizeof(*pl031)); + if (pl031->irq < 0) + { + err = pl031->irq; - if (pl031) + goto _fail; + } + + pl031->pclk = rt_clk_get_by_name(dev, "apb_pclk"); + + if (rt_is_err(pl031->pclk)) { - err = pl031_ofw_init(pdev, pl031); + err = rt_ptr_err(pl031->pclk); + + goto _fail; } - else + + if ((err = rt_clk_prepare_enable(pl031->pclk))) { - err = -RT_ENOMEM; + goto _fail; } - if (!err) + rt_dm_dev_bind_fwdata(dev, RT_NULL, &pl031->parent); + + dev->user_data = pl031; + + pl031->parent.type = RT_Device_Class_RTC; +#ifdef RT_USING_DEVICE_OPS + pl031->parent.ops = &pl031_rtc_ops; +#else + pl031->parent.init = pl031_init; + pl031->parent.control = pl031_control; +#endif + + rtc_dev_set_name(&pl031->parent); + dev_name = rt_dm_dev_get_name(&pl031->parent); + rt_device_register(&pl031->parent, dev_name, RT_DEVICE_FLAG_RDWR); + + rt_hw_interrupt_install(pl031->irq, pl031_isr, pl031, "rtc-pl031"); + rt_hw_interrupt_umask(pl031->irq); + + return RT_EOK; + +_fail: + if (pl031->base) { - pl031->parent.type = RT_Device_Class_RTC; - #ifdef RT_USING_DEVICE_OPS - pl031->parent.ops = &pl031_rtc_ops; - #else - pl031->parent.init = pl031_init; - pl031->parent.control = pl031_control; - #endif - - rt_device_register(&pl031->parent, "rtc", RT_DEVICE_FLAG_RDWR); - - rt_hw_interrupt_install(pl031->irq, pl031_isr, pl031, "rtc-pl031"); - rt_hw_interrupt_umask(pl031->irq); + rt_iounmap(pl031->base); } - else + + if (pl031->pclk) { - rt_free(pl031); + rt_clk_disable_unprepare(pl031->pclk); + rt_clk_put(pl031->pclk); } + rt_free(pl031); + return err; } +static rt_err_t pl031_remove(struct rt_platform_device *pdev) +{ + struct pl031 *pl031 = pdev->parent.user_data; + + rt_hw_interrupt_mask(pl031->irq); + rt_pic_detach_irq(pl031->irq, pl031); + + rt_device_unregister(&pl031->parent); + + rt_clk_disable_unprepare(pl031->pclk); + rt_clk_put(pl031->pclk); + + rt_free(pl031); + + return RT_EOK; +} + static const struct rt_ofw_node_id pl031_ofw_ids[] = { { .compatible = "arm,pl031" }, @@ -263,5 +294,6 @@ static struct rt_platform_driver pl031_driver = .ids = pl031_ofw_ids, .probe = pl031_probe, + .remove = pl031_remove, }; RT_PLATFORM_DRIVER_EXPORT(pl031_driver); diff --git a/components/drivers/rtc/rtc-rk8xx.c b/components/drivers/rtc/rtc-rk8xx.c new file mode 100644 index 000000000000..0a18960981e4 --- /dev/null +++ b/components/drivers/rtc/rtc-rk8xx.c @@ -0,0 +1,464 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-11-26 GuEe-GUI first version + */ + +#include +#include + +#define DBG_TAG "rtc.rk8xx" +#define DBG_LVL DBG_INFO +#include + +#include "rtc_dm.h" +#include "../mfd/rk8xx.h" + +#define BIT_RTC_CTRL_REG_STOP_RTC_M RT_BIT(0) +#define BIT_RTC_CTRL_REG_RTC_GET_TIME RT_BIT(6) +#define BIT_RTC_CTRL_REG_RTC_READSEL_M RT_BIT(7) +#define BIT_RTC_INTERRUPTS_REG_IT_ALARM_M RT_BIT(3) + +#define RTC_STATUS_MASK 0xfe +#define SECONDS_REG_MSK 0x7f +#define MINUTES_REG_MAK 0x7f +#define HOURS_REG_MSK 0x3f +#define DAYS_REG_MSK 0x3f +#define MONTHS_REG_MSK 0x1f +#define YEARS_REG_MSK 0xff +#define WEEKS_REG_MSK 0x7 + +#define NUM_TIME_REGS (RK808_WEEKS_REG - RK808_SECONDS_REG + 1) +#define NUM_ALARM_REGS (RK808_ALARM_YEARS_REG - RK808_ALARM_SECONDS_REG + 1) + +struct rk_rtc_compat_reg +{ + rt_uint32_t ctrl_reg; + rt_uint32_t status_reg; + rt_uint32_t alarm_seconds_reg; + rt_uint32_t int_reg; + rt_uint32_t seconds_reg; +}; + +struct rk8xx_rtc +{ + struct rt_device parent; + + int irq; + struct rk8xx *rk8xx; + struct rk_rtc_compat_reg *creg; + struct rt_thread *irq_thread; +}; + +#define raw_to_rk8xx_rtc(raw) rt_container_of(raw, struct rk8xx_rtc, parent) + +static rt_err_t rk8xx_rtc_read_time(struct rk8xx_rtc *rk8xx_rtc, time_t *sec) +{ + rt_err_t err; + struct tm tm; + struct rk8xx *rk8xx; + rt_uint32_t ctrl_reg, seconds_reg; + rt_uint8_t rtc_data[NUM_TIME_REGS]; + + rk8xx = rk8xx_rtc->rk8xx; + ctrl_reg = rk8xx_rtc->creg->ctrl_reg; + seconds_reg = rk8xx_rtc->creg->seconds_reg; + + err = rk8xx_update_bits(rk8xx, ctrl_reg, BIT_RTC_CTRL_REG_RTC_GET_TIME, + BIT_RTC_CTRL_REG_RTC_GET_TIME); + + if (err) + { + LOG_E("Update RTC control error = %s", rt_strerror(err)); + + return err; + } + + err = rk8xx_update_bits(rk8xx, ctrl_reg, BIT_RTC_CTRL_REG_RTC_GET_TIME, 0); + + if (err) + { + LOG_E("Update RTC control error = %s", rt_strerror(err)); + + return err; + } + + for (int i = 0; i < RT_ARRAY_SIZE(rtc_data); ++i) + { + rtc_data[i] = rk8xx_read(rk8xx, seconds_reg + i * sizeof(rt_uint8_t)); + } + + tm.tm_sec = rt_bcd2bin(rtc_data[0] & SECONDS_REG_MSK); + tm.tm_min = rt_bcd2bin(rtc_data[1] & MINUTES_REG_MAK); + tm.tm_hour = rt_bcd2bin(rtc_data[2] & HOURS_REG_MSK); + tm.tm_mday = rt_bcd2bin(rtc_data[3] & DAYS_REG_MSK); + tm.tm_mon = (rt_bcd2bin(rtc_data[4] & MONTHS_REG_MSK)) - 1; + tm.tm_year = (rt_bcd2bin(rtc_data[5] & YEARS_REG_MSK)) + 100; + tm.tm_wday = rt_bcd2bin(rtc_data[6] & WEEKS_REG_MSK); + + *sec = mktime(&tm); + + return RT_EOK; +} + +static rt_err_t rk8xx_rtc_set_time(struct rk8xx_rtc *rk8xx_rtc, time_t *sec) +{ + rt_err_t err; + struct tm *tm; + struct rk8xx *rk8xx; + rt_uint32_t ctrl_reg, seconds_reg; + rt_uint8_t rtc_data[NUM_TIME_REGS]; + + rk8xx = rk8xx_rtc->rk8xx; + ctrl_reg = rk8xx_rtc->creg->ctrl_reg; + seconds_reg = rk8xx_rtc->creg->seconds_reg; + + tm = localtime(sec); + rtc_data[0] = rt_bin2bcd(tm->tm_sec); + rtc_data[1] = rt_bin2bcd(tm->tm_min); + rtc_data[2] = rt_bin2bcd(tm->tm_hour); + rtc_data[3] = rt_bin2bcd(tm->tm_mday); + rtc_data[4] = rt_bin2bcd(tm->tm_mon + 1); + rtc_data[5] = rt_bin2bcd(tm->tm_year - 100); + rtc_data[6] = rt_bin2bcd(tm->tm_wday); + + err = rk8xx_update_bits(rk8xx, ctrl_reg, BIT_RTC_CTRL_REG_STOP_RTC_M, + BIT_RTC_CTRL_REG_STOP_RTC_M); + + if (err) + { + LOG_E("Update RTC control error = %s", rt_strerror(err)); + + return err; + } + + for (int i = 0; i < RT_ARRAY_SIZE(rtc_data); ++i) + { + err = rk8xx_write(rk8xx, seconds_reg + i * sizeof(rt_uint8_t), rtc_data[i]); + + if (err) + { + LOG_E("Update RTC time[%d] error = %s", i, rt_strerror(err)); + + return err; + } + } + + err = rk8xx_update_bits(rk8xx, ctrl_reg, BIT_RTC_CTRL_REG_STOP_RTC_M, 0); + + if (err) + { + LOG_E("Update RTC control error = %s", rt_strerror(err)); + + return err; + } + + return RT_EOK; +} + +static void rk8xx_rtc_read_alarm(struct rk8xx_rtc *rk8xx_rtc, + struct rt_rtc_wkalarm *alarm) +{ + struct rk8xx *rk8xx; + rt_uint8_t alrm_data[NUM_ALARM_REGS]; + rt_uint32_t int_reg, alarm_seconds_reg; + + rk8xx = rk8xx_rtc->rk8xx; + int_reg = rk8xx_rtc->creg->int_reg; + alarm_seconds_reg = rk8xx_rtc->creg->alarm_seconds_reg; + + for (int i = 0; i < 3; ++i) + { + alrm_data[i] = rk8xx_read(rk8xx, alarm_seconds_reg + i * sizeof(rt_uint8_t)); + } + + alarm->tm_sec = rt_bcd2bin(alrm_data[0] & SECONDS_REG_MSK); + alarm->tm_min = rt_bcd2bin(alrm_data[1] & MINUTES_REG_MAK); + alarm->tm_hour = rt_bcd2bin(alrm_data[2] & HOURS_REG_MSK); + + alarm->enable = !!(rk8xx_read(rk8xx, int_reg) & BIT_RTC_INTERRUPTS_REG_IT_ALARM_M); +} + +static rt_err_t rk8xx_rtc_set_alarm(struct rk8xx_rtc *rk8xx_rtc, + struct rt_rtc_wkalarm *alarm) +{ + rt_err_t err; + struct rk8xx *rk8xx; + rt_uint32_t int_reg, alarm_seconds_reg; + rt_uint8_t alrm_data[NUM_ALARM_REGS]; + + rk8xx = rk8xx_rtc->rk8xx; + int_reg = rk8xx_rtc->creg->int_reg; + alarm_seconds_reg = rk8xx_rtc->creg->alarm_seconds_reg; + + err = rk8xx_update_bits(rk8xx, int_reg, BIT_RTC_INTERRUPTS_REG_IT_ALARM_M, 0); + + if (err) + { + LOG_E("%s RTC alarm error = %s", "Stop", rt_strerror(err)); + + return err; + } + + alrm_data[0] = rt_bin2bcd(alarm->tm_sec); + alrm_data[1] = rt_bin2bcd(alarm->tm_min); + alrm_data[2] = rt_bin2bcd(alarm->tm_hour); + + for (int i = 0; i < RT_ARRAY_SIZE(alrm_data); ++i) + { + err = rk8xx_write(rk8xx, alarm_seconds_reg + i * sizeof(rt_uint8_t), + alrm_data[i]); + + if (err) + { + LOG_E("Update RTC alarm time[%d] error = %s", i, rt_strerror(err)); + + return err; + } + } + + if (alarm->enable) + { + err = rk8xx_update_bits(rk8xx, int_reg, + BIT_RTC_INTERRUPTS_REG_IT_ALARM_M, + BIT_RTC_INTERRUPTS_REG_IT_ALARM_M); + + if (err) + { + LOG_E("%s RTC alarm error = %s", "Start", rt_strerror(err)); + + return err; + } + } + + return RT_EOK; +} + +static rt_err_t rk8xx_rtc_control(rt_device_t dev, int cmd, void *args) +{ + rt_err_t err = RT_EOK; + struct rk8xx_rtc *rk8xx_rtc = raw_to_rk8xx_rtc(dev); + + if (!args) + { + return -RT_EINVAL; + } + + switch (cmd) + { + case RT_DEVICE_CTRL_RTC_GET_TIME: + err = rk8xx_rtc_read_time(rk8xx_rtc, args); + break; + + case RT_DEVICE_CTRL_RTC_SET_TIME: + err = rk8xx_rtc_set_time(rk8xx_rtc, args); + break; + + case RT_DEVICE_CTRL_RTC_GET_TIMEVAL: + err = rk8xx_rtc_read_time(rk8xx_rtc, (time_t *)&((struct timeval *)args)->tv_sec); + break; + + case RT_DEVICE_CTRL_RTC_SET_TIMEVAL: + err = rk8xx_rtc_set_time(rk8xx_rtc, (time_t *)&((struct timeval *)args)->tv_sec); + break; + + case RT_DEVICE_CTRL_RTC_GET_ALARM: + rk8xx_rtc_read_alarm(rk8xx_rtc, args); + break; + + case RT_DEVICE_CTRL_RTC_SET_ALARM: + rk8xx_rtc_set_alarm(rk8xx_rtc, args); + break; + + default: + err = -RT_EINVAL; + break; + } + + return err; +} + +#ifdef RT_USING_DEVICE_OPS +const static struct rt_device_ops rk8xx_rtc_ops = +{ + .control = rk8xx_rtc_control, +}; +#endif + +static void rk8xx_rtc_thread_isr(void *param) +{ + rt_err_t err; + struct rk8xx_rtc *rk8xx_rtc = param; + + while (RT_TRUE) + { + rt_thread_suspend(rk8xx_rtc->irq_thread); + rt_schedule(); + + err = rk8xx_write(rk8xx_rtc->rk8xx, rk8xx_rtc->creg->status_reg, + RTC_STATUS_MASK); + + if (err) + { + LOG_E("Update RTC status error = %s", rt_strerror(err)); + + continue; + } + + rt_alarm_update(&rk8xx_rtc->parent, 1); + } +} + +static void rk8xx_rtc_isr(int irqno, void *param) +{ + struct rk8xx_rtc *rk8xx_rtc = param; + + rt_thread_resume(rk8xx_rtc->irq_thread); +} + +static struct rk_rtc_compat_reg rk808_creg = +{ + .ctrl_reg = RK808_RTC_CTRL_REG, + .status_reg = RK808_RTC_STATUS_REG, + .alarm_seconds_reg = RK808_ALARM_SECONDS_REG, + .int_reg = RK808_RTC_INT_REG, + .seconds_reg = RK808_SECONDS_REG, +}; + +static struct rk_rtc_compat_reg rk817_creg = +{ + .ctrl_reg = RK817_RTC_CTRL_REG, + .status_reg = RK817_RTC_STATUS_REG, + .alarm_seconds_reg = RK817_ALARM_SECONDS_REG, + .int_reg = RK817_RTC_INT_REG, + .seconds_reg = RK817_SECONDS_REG, +}; + +static rt_err_t rk8xx_rtc_probe(struct rt_platform_device *pdev) +{ + rt_err_t err; + const char *dev_name; + struct rk8xx *rk8xx = pdev->priv; + struct rt_device *dev = &pdev->parent; + struct rk8xx_rtc *rk8xx_rtc = rt_calloc(1, sizeof(*rk8xx_rtc)); + + if (!rk8xx_rtc) + { + return -RT_ENOMEM; + } + + rk8xx_rtc->rk8xx = rk8xx; + + switch (rk8xx->variant) + { + case RK809_ID: + case RK817_ID: + rk8xx_rtc->creg = &rk817_creg; + break; + + default: + rk8xx_rtc->creg = &rk808_creg; + break; + } + + err = rk8xx_update_bits(rk8xx, rk8xx_rtc->creg->ctrl_reg, + BIT_RTC_CTRL_REG_STOP_RTC_M | + BIT_RTC_CTRL_REG_RTC_READSEL_M, + BIT_RTC_CTRL_REG_RTC_READSEL_M); + + if (err) + { + LOG_E("Update RTC control error = %s", rt_strerror(err)); + goto _fail; + } + + err = rk8xx_write(rk8xx, rk8xx_rtc->creg->status_reg, RTC_STATUS_MASK); + + if (err) + { + LOG_E("Write RTC status error = %s", rt_strerror(err)); + goto _fail; + } + + rk8xx_rtc->irq = rt_dm_dev_get_irq(dev, 0); + + if (rk8xx_rtc->irq < 0) + { + err = rk8xx_rtc->irq; + goto _fail; + } + + rk8xx_rtc->irq_thread = rt_thread_create("rtc-rk8xx", &rk8xx_rtc_thread_isr, + rk8xx_rtc, SYSTEM_THREAD_STACK_SIZE, RT_THREAD_PRIORITY_MAX / 2, 10); + + if (!rk8xx_rtc->irq_thread) + { + err = -RT_ERROR; + LOG_E("Create RTC IRQ thread fail"); + goto _fail; + } + + rt_thread_startup(rk8xx_rtc->irq_thread); + + dev->user_data = rk8xx_rtc; + + rk8xx_rtc->parent.type = RT_Device_Class_RTC; +#ifdef RT_USING_DEVICE_OPS + rk8xx_rtc->parent.ops = &rk8xx_rtc_ops; +#else + rk8xx_rtc->parent.control = rk8xx_rtc_control; +#endif + + rtc_dev_set_name(&rk8xx_rtc->parent); + dev_name = rt_dm_dev_get_name(&rk8xx_rtc->parent); + rt_device_register(&rk8xx_rtc->parent, dev_name, RT_DEVICE_FLAG_RDWR); + + rt_hw_interrupt_install(rk8xx_rtc->irq, rk8xx_rtc_isr, rk8xx_rtc, "rtc-rk8xx"); + rt_hw_interrupt_umask(rk8xx_rtc->irq); + + return RT_EOK; + +_fail: + if (rk8xx_rtc->irq_thread) + { + rt_thread_delete(rk8xx_rtc->irq_thread); + } + rt_free(rk8xx_rtc); + + return err; +} + +static rt_err_t rk8xx_rtc_remove(struct rt_platform_device *pdev) +{ + struct rk8xx_rtc *rk8xx_rtc = pdev->parent.user_data; + + rt_hw_interrupt_mask(rk8xx_rtc->irq); + rt_pic_detach_irq(rk8xx_rtc->irq, rk8xx_rtc); + + rt_device_unregister(&rk8xx_rtc->parent); + + rt_thread_delete(rk8xx_rtc->irq_thread); + + rt_free(rk8xx_rtc); + + return RT_EOK; +} + +static struct rt_platform_driver rk8xx_rtc_driver = +{ + .name = "rk8xx-rtc", + .probe = rk8xx_rtc_probe, + .remove = rk8xx_rtc_remove, +}; + +static int rk8xx_rtc_drv_register(void) +{ + rt_platform_driver_register(&rk8xx_rtc_driver); + + return 0; +} +INIT_DRIVER_LATER_EXPORT(rk8xx_rtc_drv_register); diff --git a/components/drivers/rtc/rtc-rk_timer.c b/components/drivers/rtc/rtc-rk_timer.c new file mode 100644 index 000000000000..87fa49c6a22c --- /dev/null +++ b/components/drivers/rtc/rtc-rk_timer.c @@ -0,0 +1,397 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-12-06 GuEe-GUI first version + */ + +#include "rtc_dm.h" + +#define TIMER_LOAD_COUNT0 0x00 +#define TIMER_LOAD_COUNT1 0x04 +#define TIMER_CURRENT_VALUE0 0x08 +#define TIMER_CURRENT_VALUE1 0x0c +#define TIMER_CONTROL_REG3288 0x10 +#define TIMER_INT_STATUS 0x18 + +#define TIMER_ENABLE RT_BIT(0) +#define TIMER_MODE_USER_DEFINED_COUNT RT_BIT(1) +#define TIMER_INT_UNMASK RT_BIT(2) + +/* Forbid any alarms which would trigger inside the threshold */ +#define TIMER_ALARM_THRESHOLD_MS 10 +#define TIMER_COUNT_MAX ((rt_uint64_t)-1) + +struct rk_timer_rtc_data +{ + int ctrl_reg_offset; +}; + +struct rk_timer_rtc +{ + struct rt_device parent; + + int irq; + void *base; + + struct rt_clk *clk; + struct rt_clk *pclk; + + rt_uint32_t freq; + rt_uint64_t timebase; + rt_bool_t alarm_irq_enabled; + struct rt_rtc_wkalarm wkalarm; + + const struct rk_timer_rtc_data *soc_data; +}; + +#define raw_to_rk_timer_rtc(raw) rt_container_of(raw, struct rk_timer_rtc, parent) + +rt_inline rt_uint64_t tick_to_sec(struct rk_timer_rtc *rk_rtc, rt_uint64_t tick) +{ + rt_do_div(tick, rk_rtc->freq); + + return tick; +} + +rt_inline rt_uint64_t ms_to_tick(struct rk_timer_rtc *rk_rtc, int ms) +{ + return ms * rk_rtc->freq / 1000; +} + +rt_inline rt_uint64_t tick_to_time64(struct rk_timer_rtc *rk_rtc, rt_uint64_t tick) +{ + return tick_to_sec(rk_rtc, tick) + rk_rtc->timebase; +} + +rt_inline rt_uint64_t time64_to_tick(struct rk_timer_rtc *rk_rtc, rt_uint64_t time) +{ + return (time - rk_rtc->timebase) * rk_rtc->freq; +} + +rt_inline void rk_timer_rtc_writel(struct rk_timer_rtc *rk_rtc, int offset, rt_uint32_t value) +{ + HWREG32(rk_rtc->base + offset) = value; +} + +rt_inline rt_uint32_t rk_timer_rtc_readl(struct rk_timer_rtc *rk_rtc, int offset) +{ + return HWREG32(rk_rtc->base + offset); +} + +rt_inline void rk_timer_rtc_writeq(struct rk_timer_rtc *rk_rtc, int offset, rt_uint64_t value) +{ + HWREG32(rk_rtc->base + offset) = value; + HWREG32(rk_rtc->base + offset + 4) = value >> 32; +} + +rt_inline rt_uint64_t rk_timer_rtc_readq(struct rk_timer_rtc *rk_rtc, int offset) +{ + rt_uint32_t val_lo, val_hi, tmp_hi; + + do { + val_hi = HWREG32(rk_rtc->base + offset + 4); + val_lo = HWREG32(rk_rtc->base + offset); + tmp_hi = HWREG32(rk_rtc->base + offset + 4); + } while (val_hi != tmp_hi); + + return ((rt_uint64_t) val_hi << 32) | val_lo; +} + +rt_inline void rk_timer_rtc_irq_clear(struct rk_timer_rtc *rk_rtc) +{ + return rk_timer_rtc_writel(rk_rtc->base, TIMER_INT_STATUS, 1); +} + +rt_inline void rk_timer_rtc_irq_enable(struct rk_timer_rtc *rk_rtc, rt_bool_t enabled) +{ + rt_uint32_t old_val; + + /* Clear any pending irq before enable it */ + if (enabled) + { + rk_timer_rtc_irq_clear(rk_rtc); + } + + old_val = rk_timer_rtc_readl(rk_rtc, rk_rtc->soc_data->ctrl_reg_offset); + old_val &= ~TIMER_INT_UNMASK; + rk_timer_rtc_writel(rk_rtc, rk_rtc->soc_data->ctrl_reg_offset, + old_val | (enabled ? TIMER_INT_UNMASK : 0)); +} + +static void rk_timer_rtc_reset(struct rk_timer_rtc *rk_rtc) +{ + rk_timer_rtc_writel(rk_rtc, rk_rtc->soc_data->ctrl_reg_offset, 0); + + /* Init load count to UINT64_MAX to keep timer running */ + rk_timer_rtc_writeq(rk_rtc, TIMER_LOAD_COUNT0, TIMER_COUNT_MAX); + + /* Clear any pending irq before enable it */ + rk_timer_rtc_irq_clear(rk_rtc); + + /* Enable timer in user-defined count mode with irq unmasked */ + rk_timer_rtc_writel(rk_rtc, rk_rtc->soc_data->ctrl_reg_offset, + TIMER_ENABLE | TIMER_MODE_USER_DEFINED_COUNT | TIMER_INT_UNMASK); +} + +static void rk_timer_rtc_read_time(struct rk_timer_rtc *rk_rtc, time_t *sec) +{ + rt_uint64_t tick; + + tick = rk_timer_rtc_readq(rk_rtc, TIMER_CURRENT_VALUE0); + + *sec = (time_t)tick_to_time64(rk_rtc, tick); +} + +static void rk_timer_rtc_set_time(struct rk_timer_rtc *rk_rtc, time_t *sec) +{ + rk_rtc->timebase = (rt_uint64_t)*sec; + + rk_timer_rtc_reset(rk_rtc); +} + +static void rk_timer_rtc_read_alarm(struct rk_timer_rtc *rk_rtc, + struct rt_rtc_wkalarm *alarm) +{ + *alarm = rk_rtc->wkalarm; + + alarm->enable = rk_rtc->alarm_irq_enabled; +} + +static rt_err_t rk_timer_rtc_set_alarm(struct rk_timer_rtc *rk_rtc, + struct rt_rtc_wkalarm *alarm) +{ + rt_uint64_t alarm_tick, alarm_threshold_tick, cur_tick, time; + + rk_rtc->alarm_irq_enabled = alarm->enable; + + time = alarm->tm_hour * 3600 + alarm->tm_min * 60 + alarm->tm_sec; + alarm_tick = time64_to_tick(rk_rtc, time); + cur_tick = rk_timer_rtc_readq(rk_rtc, TIMER_CURRENT_VALUE0); + + /* Don't set an alarm in the past or about to pass */ + alarm_threshold_tick = ms_to_tick(rk_rtc, TIMER_ALARM_THRESHOLD_MS); + + if (alarm_tick <= (cur_tick + alarm_threshold_tick)) + { + return -RT_ERROR; + } + + /* + * When the current value counts up to the load count, the timer will + * stop and generate an irq. + */ + rk_timer_rtc_writeq(rk_rtc, TIMER_LOAD_COUNT0, alarm_tick); + + return RT_EOK; +} + +static rt_err_t rk_timer_rtc_control(rt_device_t dev, int cmd, void *args) +{ + rt_err_t err = RT_EOK; + struct rk_timer_rtc *rk_rtc = raw_to_rk_timer_rtc(dev); + + if (!args) + { + return -RT_EINVAL; + } + + switch (cmd) + { + case RT_DEVICE_CTRL_RTC_GET_TIME: + rk_timer_rtc_read_time(rk_rtc, args); + break; + + case RT_DEVICE_CTRL_RTC_SET_TIME: + rk_timer_rtc_set_time(rk_rtc, args); + break; + + case RT_DEVICE_CTRL_RTC_GET_TIMEVAL: + rk_timer_rtc_read_time(rk_rtc, (time_t *)&((struct timeval *)args)->tv_sec); + break; + + case RT_DEVICE_CTRL_RTC_SET_TIMEVAL: + rk_timer_rtc_set_time(rk_rtc, (time_t *)&((struct timeval *)args)->tv_sec); + break; + + case RT_DEVICE_CTRL_RTC_GET_ALARM: + rk_timer_rtc_read_alarm(rk_rtc, args); + break; + + case RT_DEVICE_CTRL_RTC_SET_ALARM: + err = rk_timer_rtc_set_alarm(rk_rtc, args); + break; + + default: + err = -RT_EINVAL; + break; + } + + return err; +} + +#ifdef RT_USING_DEVICE_OPS +const static struct rt_device_ops rk_timer_rtc_ops = +{ + .control = rk_timer_rtc_control, +}; +#endif + +static void rk_timer_rtc_isr(int irqno, void *param) +{ + struct rk_timer_rtc *rk_rtc = param; + + rk_timer_rtc_writeq(rk_rtc, TIMER_LOAD_COUNT0, TIMER_COUNT_MAX); + rk_timer_rtc_writel(rk_rtc, TIMER_INT_STATUS, 1); + + if (rk_rtc->alarm_irq_enabled) + { + rt_alarm_update(&rk_rtc->parent, 1); + } +} + +static void rk_timer_rtc_free(struct rk_timer_rtc *rk_rtc) +{ + if (rk_rtc->base) + { + rt_iounmap(rk_rtc->base); + } + + if (!rt_is_err_or_null(rk_rtc->pclk)) + { + rt_clk_disable_unprepare(rk_rtc->pclk); + rt_clk_put(rk_rtc->pclk); + } + + if (!rt_is_err_or_null(rk_rtc->clk)) + { + rt_clk_disable_unprepare(rk_rtc->clk); + rt_clk_put(rk_rtc->clk); + } + + rt_free(rk_rtc); +} + +static rt_err_t rk_timer_rtc_probe(struct rt_platform_device *pdev) +{ + rt_err_t err; + const char *dev_name; + struct rt_device *dev = &pdev->parent; + struct rk_timer_rtc *rk_rtc = rt_calloc(1, sizeof(*rk_rtc)); + + if (!rk_rtc) + { + return -RT_ENOMEM; + } + + rk_rtc->soc_data = pdev->id->data; + + rk_rtc->base = rt_dm_dev_iomap(dev, 0); + + if (!rk_rtc->base) + { + err = -RT_EIO; + + goto _fail; + } + + rk_rtc->irq = rt_dm_dev_get_irq(dev, 0); + + if (rk_rtc->irq < 0) + { + err = rk_rtc->irq; + + goto _fail; + } + + rk_rtc->pclk = rt_clk_get_by_name(dev, "pclk"); + + if (rt_is_err(rk_rtc->pclk)) + { + err = rt_ptr_err(rk_rtc->pclk); + + goto _fail; + } + + if ((err = rt_clk_prepare_enable(rk_rtc->pclk))) + { + goto _fail; + } + + rk_rtc->clk = rt_clk_get_by_name(dev, "timer"); + + if (rt_is_err(rk_rtc->clk)) + { + err = rt_ptr_err(rk_rtc->clk); + + goto _fail; + } + + if ((err = rt_clk_prepare_enable(rk_rtc->clk))) + { + goto _fail; + } + + rk_rtc->freq = rt_clk_get_rate(rk_rtc->clk); + + dev->user_data = rk_rtc; + + rk_rtc->parent.type = RT_Device_Class_RTC; +#ifdef RT_USING_DEVICE_OPS + rk_rtc->parent.ops = &rk_timer_rtc_ops; +#else + rk_rtc->parent.control = rk_timer_rtc_control; +#endif + + rtc_dev_set_name(&rk_rtc->parent); + dev_name = rt_dm_dev_get_name(&rk_rtc->parent); + rt_device_register(&rk_rtc->parent, dev_name, RT_DEVICE_FLAG_RDWR); + + rt_hw_interrupt_install(rk_rtc->irq, rk_timer_rtc_isr, rk_rtc, "rtc-rockchip"); + rt_hw_interrupt_umask(rk_rtc->irq); + + return RT_EOK; + +_fail: + rk_timer_rtc_free(rk_rtc); + + return err; +} + +static rt_err_t rk_timer_rtc_remove(struct rt_platform_device *pdev) +{ + struct rk_timer_rtc *rk_rtc = pdev->parent.user_data; + + rt_hw_interrupt_mask(rk_rtc->irq); + rt_pic_detach_irq(rk_rtc->irq, rk_rtc); + + rt_device_unregister(&rk_rtc->parent); + + rk_timer_rtc_free(rk_rtc); + + return RT_EOK; +} + +static const struct rk_timer_rtc_data rk3288_timer_rtc_data = +{ + .ctrl_reg_offset = TIMER_CONTROL_REG3288, +}; + +static const struct rt_ofw_node_id rk_timer_rtc_ofw_ids[] = +{ + { .compatible = "rockchip,rk3308-timer-rtc", .data = &rk3288_timer_rtc_data }, + { /* sentinel */ } +}; + +static struct rt_platform_driver rk_timer_rtc_driver = +{ + .name = "rk-timer-rtc", + .ids = rk_timer_rtc_ofw_ids, + + .probe = rk_timer_rtc_probe, + .remove = rk_timer_rtc_remove, +}; +RT_PLATFORM_DRIVER_EXPORT(rk_timer_rtc_driver); diff --git a/components/drivers/rtc/rtc-rpi.c b/components/drivers/rtc/rtc-rpi.c new file mode 100644 index 000000000000..74984f8bab06 --- /dev/null +++ b/components/drivers/rtc/rtc-rpi.c @@ -0,0 +1,337 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-11-26 GuEe-GUI first version + */ + +#define DBG_TAG "rtc.rpi" +#define DBG_LVL DBG_INFO +#include + +#include "rtc_dm.h" +#include "../firmware/raspberrypi/firmware.h" + +enum +{ + RTC_TIME, + RTC_ALARM, + RTC_ALARM_PENDING, + RTC_ALARM_ENABLE, + RTC_BBAT_CHG_VOLTS, + RTC_BBAT_CHG_VOLTS_MIN, + RTC_BBAT_CHG_VOLTS_MAX, + RTC_BBAT_VOLTS +}; + +struct rpi_rtc +{ + struct rt_device parent; + + struct rpi_firmware *rpi_fw; + rt_uint32_t bbat_vchg_microvolts; + + struct rt_mutex lock; + struct rt_timer alarm_mon; + struct rt_rtc_wkalarm wkalarm; +}; + +#define raw_to_rpi_rtc(raw) rt_container_of(raw, struct rpi_rtc, parent) + +static rt_err_t rpi_rtc_alarm_clear_pending(struct rpi_rtc *rrtc) +{ + rt_uint32_t data[2] = { RTC_ALARM_PENDING, 1 }; + + return rpi_firmware_property(rrtc->rpi_fw, RPI_FIRMWARE_SET_RTC_REG, + &data, sizeof(data)); +} + +static rt_err_t rpi_rtc_set_charge_voltage(struct rpi_rtc *rrtc) +{ + rt_err_t err; + rt_uint32_t data[2] = { RTC_BBAT_CHG_VOLTS, rrtc->bbat_vchg_microvolts }; + + err = rpi_firmware_property(rrtc->rpi_fw, RPI_FIRMWARE_SET_RTC_REG, + &data, sizeof(data)); + + if (err) + { + LOG_E("Failed to set trickle charge voltage to %uuV error = %s", + rrtc->bbat_vchg_microvolts, rt_strerror(err)); + } + else if (rrtc->bbat_vchg_microvolts) + { + LOG_I("Trickle charging enabled at %uuV", rrtc->bbat_vchg_microvolts); + } + + return err; +} + +static rt_err_t rpi_rtc_alarm_irq_is_enabled(struct rpi_rtc *rrtc, + rt_bool_t *enabled) +{ + rt_err_t err; + rt_uint32_t data[2] = { RTC_ALARM_ENABLE }; + + err = rpi_firmware_property(rrtc->rpi_fw, RPI_FIRMWARE_GET_RTC_REG, + &data, sizeof(data)); + + *enabled = !!(data[1] & 0x1); + + return err; +} + +static rt_err_t rpi_rtc_alarm_irq_enable(struct rpi_rtc *rrtc, rt_bool_t enabled) +{ + rt_uint32_t data[2] = { RTC_ALARM_ENABLE, enabled }; + + return rpi_firmware_property(rrtc->rpi_fw, RPI_FIRMWARE_SET_RTC_REG, + &data, sizeof(data)); +} + +static rt_err_t rpi_rtc_read_time(struct rpi_rtc *rrtc, time_t *sec) +{ + rt_err_t err; + rt_uint32_t data[2] = { RTC_TIME }; + + err = rpi_firmware_property(rrtc->rpi_fw, RPI_FIRMWARE_GET_RTC_REG, + &data, sizeof(data)); + + *sec = data[1]; + + return err; +} + +static rt_err_t rpi_rtc_set_time(struct rpi_rtc *rrtc, time_t *sec) +{ + rt_uint32_t data[2] = { RTC_TIME, (rt_uint32_t)*sec }; + + return rpi_firmware_property(rrtc->rpi_fw, RPI_FIRMWARE_SET_RTC_REG, + &data, sizeof(data)); +} + +static rt_err_t rpi_rtc_read_alarm(struct rpi_rtc *rrtc, + struct rt_rtc_wkalarm *alarm) +{ + rt_err_t err; + rt_uint32_t data[2] = { RTC_ALARM }; + + if (!(err = rpi_rtc_alarm_irq_is_enabled(rrtc, &alarm->enable))) + { + err = rpi_firmware_property(rrtc->rpi_fw, RPI_FIRMWARE_GET_RTC_REG, + &data, sizeof(data)); + } + + rtc_timestamp_to_wkalarm(data[1], alarm); + + return err; +} + +static rt_err_t rpi_rtc_set_alarm(struct rpi_rtc *rrtc, + struct rt_rtc_wkalarm *alarm) +{ + rt_err_t err; + struct rt_rtc_wkalarm *wkalarm = &rrtc->wkalarm; + rt_uint32_t data[2] = { RTC_ALARM, (rt_uint32_t)rtc_wkalarm_to_timestamp(alarm) }; + + err = rpi_firmware_property(rrtc->rpi_fw, RPI_FIRMWARE_SET_RTC_REG, + &data, sizeof(data)); + + if (!err && !(err = rpi_rtc_alarm_irq_enable(rrtc, alarm->enable))) + { + rt_mutex_take(&rrtc->lock, RT_WAITING_FOREVER); + + wkalarm->enable = alarm->enable; + wkalarm->tm_hour = alarm->tm_hour; + wkalarm->tm_min = alarm->tm_min; + wkalarm->tm_sec = alarm->tm_sec; + + if (wkalarm->enable) + { + /* + * raspberrypi firmware rtc not has IRQ for alarm, + * so we should poll once per second. + */ + rt_tick_t tick = rt_tick_from_millisecond(1000); + + rt_timer_control(&rrtc->alarm_mon, RT_TIMER_CTRL_SET_TIME, &tick); + + rt_timer_start(&rrtc->alarm_mon); + } + + rt_mutex_release(&rrtc->lock); + } + + return err; +} + +static rt_err_t rpi_rtc_control(rt_device_t dev, int cmd, void *args) +{ + rt_err_t err = RT_EOK; + struct rpi_rtc *rrtc = raw_to_rpi_rtc(dev); + + if (!args) + { + return -RT_EINVAL; + } + + switch (cmd) + { + case RT_DEVICE_CTRL_RTC_GET_TIME: + err = rpi_rtc_read_time(rrtc, args); + break; + + case RT_DEVICE_CTRL_RTC_SET_TIME: + err = rpi_rtc_set_time(rrtc, args); + break; + + case RT_DEVICE_CTRL_RTC_GET_TIMEVAL: + err = rpi_rtc_read_time(rrtc, (time_t *)&((struct timeval *)args)->tv_sec); + break; + + case RT_DEVICE_CTRL_RTC_SET_TIMEVAL: + err = rpi_rtc_set_time(rrtc, (time_t *)&((struct timeval *)args)->tv_sec); + break; + + case RT_DEVICE_CTRL_RTC_GET_ALARM: + err = rpi_rtc_read_alarm(rrtc, args); + break; + + case RT_DEVICE_CTRL_RTC_SET_ALARM: + err = rpi_rtc_set_alarm(rrtc, args); + break; + + default: + err = -RT_EINVAL; + break; + } + + return err; +} + +#ifdef RT_USING_DEVICE_OPS +const static struct rt_device_ops rpi_rtc_rtc_ops = +{ + .control = rpi_rtc_control, +}; +#endif + +static void rpi_rtc_alarm_mon(void *param) +{ + struct rpi_rtc *rrtc = param; + struct rt_rtc_wkalarm alarm; + time_t alarm_time, next_alarm_time; + + rt_mutex_take(&rrtc->lock, RT_WAITING_FOREVER); + + rpi_rtc_read_alarm(rrtc, &alarm); + alarm_time = rtc_wkalarm_to_timestamp(&alarm); + next_alarm_time = rtc_wkalarm_to_timestamp(&rrtc->wkalarm); + + if (alarm_time < next_alarm_time) + { + rrtc->wkalarm.enable = RT_FALSE; + rt_timer_stop(&rrtc->alarm_mon); + rt_alarm_update(&rrtc->parent, 1); + } + + rt_mutex_release(&rrtc->lock); +} + +static rt_err_t rpi_rtc_probe(struct rt_platform_device *pdev) +{ + rt_err_t err = RT_EOK; + const char *dev_name; + struct rpi_firmware *rpi_fw; + struct rt_device *dev = &pdev->parent; + struct rt_ofw_node *np = dev->ofw_node, *fw_np; + struct rpi_rtc *rrtc = rt_calloc(1, sizeof(*rrtc)); + + if (!rrtc) + { + return -RT_ENOMEM; + } + + fw_np = rt_ofw_parse_phandle(np, "firmware", 0); + + if (!fw_np) + { + err = -RT_EINVAL; + goto _fail; + } + + rpi_fw = rpi_firmware_get(fw_np); + rt_ofw_node_put(fw_np); + + if (!rpi_fw) + { + err = -RT_EINVAL; + goto _fail; + } + + rrtc->rpi_fw = rpi_fw; + + rpi_rtc_alarm_clear_pending(rrtc); + + rt_ofw_prop_read_u32(np, "trickle-charge-microvolt", &rrtc->bbat_vchg_microvolts); + rpi_rtc_set_charge_voltage(rrtc); + + rt_dm_dev_bind_fwdata(dev, RT_NULL, &rrtc->parent); + + dev->user_data = rrtc; + + rrtc->parent.type = RT_Device_Class_RTC; +#ifdef RT_USING_DEVICE_OPS + rrtc->parent.ops = &rpi_rtc_rtc_ops; +#else + rrtc->parent.control = rpi_rtc_control; +#endif + + rtc_dev_set_name(&rrtc->parent); + dev_name = rt_dm_dev_get_name(&rrtc->parent); + + rt_mutex_init(&rrtc->lock, dev_name, RT_IPC_FLAG_FIFO); + rt_timer_init(&rrtc->alarm_mon, dev_name, rpi_rtc_alarm_mon, rrtc, + 0, RT_TIMER_FLAG_PERIODIC); + + rt_device_register(&rrtc->parent, dev_name, RT_DEVICE_FLAG_RDWR); + + return RT_EOK; + +_fail: + rt_free(rrtc); + + return err; +} + +static rt_err_t rpi_rtc_remove(struct rt_platform_device *pdev) +{ + struct rpi_rtc *rrtc = pdev->parent.user_data; + + rt_timer_detach(&rrtc->alarm_mon); + rt_mutex_detach(&rrtc->lock); + rt_device_unregister(&rrtc->parent); + + rt_free(rrtc); + + return RT_EOK; +} + +static const struct rt_ofw_node_id rpi_rtc_ofw_ids[] = +{ + { .compatible = "raspberrypi,rpi-rtc" }, + { /* sentinel */ } +}; + +static struct rt_platform_driver rpi_rtc_driver = +{ + .name = "rtc-rpi", + .ids = rpi_rtc_ofw_ids, + + .probe = rpi_rtc_probe, + .remove = rpi_rtc_remove, +}; +RT_PLATFORM_DRIVER_EXPORT(rpi_rtc_driver); diff --git a/components/drivers/rtc/rtc-rx8010.c b/components/drivers/rtc/rtc-rx8010.c new file mode 100644 index 000000000000..9ffb020c9b1b --- /dev/null +++ b/components/drivers/rtc/rtc-rx8010.c @@ -0,0 +1,637 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-11-26 GuEe-GUI first version + */ + +#include +#include + +#define DBG_TAG "rtc.rx8010" +#define DBG_LVL DBG_INFO +#include + +#include "rtc_dm.h" + +#define RX8010_SEC 0x10 +#define RX8010_MIN 0x11 +#define RX8010_HOUR 0x12 +#define RX8010_WDAY 0x13 +#define RX8010_MDAY 0x14 +#define RX8010_MONTH 0x15 +#define RX8010_YEAR 0x16 +#define RX8010_RESV17 0x17 +#define RX8010_ALMIN 0x18 +#define RX8010_ALHOUR 0x19 +#define RX8010_ALWDAY 0x1a +#define RX8010_TCOUNT0 0x1b +#define RX8010_TCOUNT1 0x1c +#define RX8010_EXT 0x1d +#define RX8010_FLAG 0x1e +#define RX8010_CTRL 0x1f +/* 0x20 to 0x2F are user registers */ +#define RX8010_RESV30 0x30 +#define RX8010_RESV31 0x31 +#define RX8010_IRQ 0x32 + +#define RX8010_EXT_WADA RT_BIT(3) + +#define RX8010_FLAG_VLF RT_BIT(1) +#define RX8010_FLAG_AF RT_BIT(3) +#define RX8010_FLAG_TF RT_BIT(4) +#define RX8010_FLAG_UF RT_BIT(5) + +#define RX8010_CTRL_AIE RT_BIT(3) +#define RX8010_CTRL_UIE RT_BIT(5) +#define RX8010_CTRL_STOP RT_BIT(6) +#define RX8010_CTRL_TEST RT_BIT(7) + +#define RX8010_ALARM_AE RT_BIT(7) + +struct rx8010_rtc +{ + struct rt_device parent; + + int irq; + rt_uint8_t ctrlreg; + + struct rt_i2c_client *client; + struct rt_thread *irq_thread; + + struct rt_rtc_wkalarm wkalarm; +}; + +#define raw_to_rx8010_rtc(raw) rt_container_of(raw, struct rx8010_rtc, parent) + +static rt_err_t rx8010_rtc_write(struct rx8010_rtc *rx8010, + rt_uint8_t reg, rt_uint8_t value) +{ + rt_int32_t res; + struct rt_i2c_msg msg[1]; + rt_uint8_t data[sizeof(reg) + sizeof(value)] = { reg }; + struct rt_i2c_client *client = rx8010->client; + + rt_memcpy(&data[sizeof(reg)], &value, sizeof(value)); + + msg[0].buf = data; + msg[0].addr = client->client_addr; + msg[0].len = sizeof(data); + msg[0].flags = RT_I2C_WR; + + res = rt_i2c_transfer(client->bus, msg, 1); + + return res > 0 ? RT_EOK : res; +} + +static rt_err_t rx8010_rtc_read(struct rx8010_rtc *rx8010, + rt_uint8_t reg, rt_uint8_t *values) +{ + rt_int32_t res; + struct rt_i2c_msg msg[2]; + struct rt_i2c_client *client = rx8010->client; + + msg[0].buf = ® + msg[0].addr = client->client_addr; + msg[0].len = sizeof(reg); + msg[0].flags = RT_I2C_WR; + + msg[1].buf = (rt_uint8_t *)values; + msg[1].addr = client->client_addr; + msg[1].len = sizeof(*values); + msg[1].flags = RT_I2C_RD; + + res = rt_i2c_transfer(client->bus, msg, 2); + + return res > 0 ? RT_EOK : res; +} + +static rt_err_t rx8010_rtc_set_bit(struct rx8010_rtc *rx8010, + rt_uint8_t reg, rt_uint8_t bit) +{ + rt_err_t err; + rt_uint8_t value; + + if ((err = rx8010_rtc_read(rx8010, reg, &value))) + { + return err; + } + + return rx8010_rtc_write(rx8010, reg, value | bit); +} + +static rt_err_t rx8010_rtc_clear_bit(struct rx8010_rtc *rx8010, + rt_uint8_t reg, rt_uint8_t bit) +{ + rt_err_t err; + rt_uint8_t value; + + if ((err = rx8010_rtc_read(rx8010, reg, &value))) + { + return err; + } + + return rx8010_rtc_write(rx8010, reg, value & ~bit); +} + +static rt_err_t rx8010_rtc_read_time(struct rx8010_rtc *rx8010, time_t *sec) +{ + struct tm tm; + rt_err_t err; + rt_uint8_t flagreg, date[RX8010_YEAR - RX8010_SEC + 1]; + + if ((err = rx8010_rtc_read(rx8010, RX8010_FLAG, &flagreg))) + { + return err; + } + + if (flagreg & RX8010_FLAG_VLF) + { + LOG_W("Frequency stop detected"); + + return -RT_EINVAL; + } + + for (int i = 0; i < sizeof(date); ++i) + { + if ((err = rx8010_rtc_read(rx8010, RX8010_SEC + i, &date[i]))) + { + return err; + } + } + + tm.tm_sec = rt_bcd2bin(date[RX8010_SEC - RX8010_SEC] & 0x7f); + tm.tm_min = rt_bcd2bin(date[RX8010_MIN - RX8010_SEC] & 0x7f); + tm.tm_hour = rt_bcd2bin(date[RX8010_HOUR - RX8010_SEC] & 0x3f); + tm.tm_mday = rt_bcd2bin(date[RX8010_MDAY - RX8010_SEC] & 0x3f); + tm.tm_mon = rt_bcd2bin(date[RX8010_MONTH - RX8010_SEC] & 0x1f) - 1; + tm.tm_year = rt_bcd2bin(date[RX8010_YEAR - RX8010_SEC]) + 100; + tm.tm_wday = __rt_ffs(date[RX8010_WDAY - RX8010_SEC] & 0x7f); + + *sec = timegm(&tm); + + return RT_EOK; +} + +static rt_err_t rx8010_rtc_set_time(struct rx8010_rtc *rx8010, time_t *sec) +{ + rt_err_t err; + struct tm *tm; + rt_uint8_t date[RX8010_YEAR - RX8010_SEC + 1]; + + /* Set STOP bit before changing clock/calendar */ + if ((err = rx8010_rtc_set_bit(rx8010, RX8010_CTRL, RX8010_CTRL_STOP))) + { + return err; + } + + tm = localtime(sec); + + date[RX8010_SEC - RX8010_SEC] = rt_bin2bcd(tm->tm_sec); + date[RX8010_MIN - RX8010_SEC] = rt_bin2bcd(tm->tm_min); + date[RX8010_HOUR - RX8010_SEC] = rt_bin2bcd(tm->tm_hour); + date[RX8010_MDAY - RX8010_SEC] = rt_bin2bcd(tm->tm_mday); + date[RX8010_MONTH - RX8010_SEC] = rt_bin2bcd(tm->tm_mon + 1); + date[RX8010_YEAR - RX8010_SEC] = rt_bin2bcd(tm->tm_year - 100); + date[RX8010_WDAY - RX8010_SEC] = rt_bin2bcd(1 << tm->tm_wday); + + for (int i = 0; i < sizeof(date); ++i) + { + if ((err = rx8010_rtc_write(rx8010, RX8010_SEC + i, date[i]))) + { + return err; + } + } + + /* Clear STOP bit after changing clock/calendar */ + if ((err = rx8010_rtc_clear_bit(rx8010, RX8010_CTRL, RX8010_CTRL_STOP))) + { + return err; + } + + if ((err = rx8010_rtc_clear_bit(rx8010, RX8010_FLAG, RX8010_FLAG_VLF))) + { + return err; + } + + return RT_EOK; +} + +static rt_err_t rx8010_rtc_read_alarm(struct rx8010_rtc *rx8010, + struct rt_rtc_wkalarm *alarm) +{ + rt_err_t err; + rt_uint8_t alarmvals[3], flagreg; + + for (int i = 0; i < RT_ARRAY_SIZE(alarmvals); ++i) + { + if ((err = rx8010_rtc_read(rx8010, RX8010_ALMIN + i, &alarmvals[i]))) + { + return err; + } + } + + if ((err = rx8010_rtc_read(rx8010, RX8010_FLAG, &flagreg))) + { + return err; + } + + alarm->tm_sec = 0; + alarm->tm_min = rt_bcd2bin(alarmvals[0] & 0x7f); + alarm->tm_hour = rt_bcd2bin(alarmvals[1] & 0x3f); + + if (!(alarmvals[2] & RX8010_ALARM_AE)) + { + /* alarm->tm_mday = rt_bcd2bin(alarmvals[2] & 0x7f) */ + } + + alarm->enable = !!(rx8010->ctrlreg & RX8010_CTRL_AIE); + + return RT_EOK; +} + +static rt_err_t rx8010_alarm_irq_enable(struct rx8010_rtc *rx8010, rt_bool_t enabled) +{ + rt_err_t err; + rt_uint8_t ctrl; + + ctrl = rx8010->ctrlreg; + + if (enabled) + { + ctrl |= RX8010_CTRL_AIE | RX8010_CTRL_UIE; + } + else + { + ctrl &= ~(RX8010_CTRL_UIE | RX8010_CTRL_AIE); + } + + if ((err = rx8010_rtc_clear_bit(rx8010, RX8010_FLAG, RX8010_FLAG_AF))) + { + return err; + } + + if (ctrl != rx8010->ctrlreg) + { + rx8010->ctrlreg = ctrl; + + if ((err = rx8010_rtc_write(rx8010, RX8010_CTRL, rx8010->ctrlreg))) + { + return err; + } + } + + return RT_EOK; +} + +static rt_err_t rx8010_rtc_set_alarm(struct rx8010_rtc *rx8010, + struct rt_rtc_wkalarm *alarm) +{ + rt_err_t err; + rt_uint8_t alarmvals[3]; + struct rt_rtc_wkalarm *wkalarm = &rx8010->wkalarm; + + if (rx8010->ctrlreg & (RX8010_CTRL_AIE | RX8010_CTRL_UIE)) + { + rx8010->ctrlreg &= ~(RX8010_CTRL_AIE | RX8010_CTRL_UIE); + + if ((err = rx8010_rtc_write(rx8010, RX8010_CTRL, rx8010->ctrlreg))) + { + return err; + } + } + + if ((err = rx8010_rtc_clear_bit(rx8010, RX8010_FLAG, RX8010_FLAG_AF))) + { + return err; + } + + alarmvals[0] = rt_bin2bcd(alarm->tm_min); + alarmvals[1] = rt_bin2bcd(alarm->tm_hour); + alarmvals[2] = rt_bin2bcd(0); /* alarm->tm_mday */ + + for (int i = 0; i < RT_ARRAY_SIZE(alarmvals) - 1; ++i) + { + if ((err = rx8010_rtc_write(rx8010, RX8010_ALMIN + i, alarmvals[i]))) + { + return err; + } + } + + if ((err = rx8010_rtc_clear_bit(rx8010, RX8010_EXT, RX8010_EXT_WADA))) + { + return err; + } + + if (alarmvals[2] == 0) + { + alarmvals[2] |= RX8010_ALARM_AE; + } + + if ((err = rx8010_rtc_write(rx8010, RX8010_ALWDAY, alarmvals[2]))) + { + return err; + } + + if (alarm->enable) + { + rx8010->ctrlreg |= RX8010_CTRL_UIE | RX8010_CTRL_AIE | RX8010_CTRL_UIE; + + if ((err = rx8010_rtc_write(rx8010, RX8010_CTRL, rx8010->ctrlreg))) + { + return err; + } + } + + if (!(err = rx8010_alarm_irq_enable(rx8010, alarm->enable))) + { + wkalarm->enable = alarm->enable; + wkalarm->tm_hour = alarm->tm_hour; + wkalarm->tm_min = alarm->tm_min; + wkalarm->tm_sec = alarm->tm_sec; + } + + return err; +} + +static rt_err_t rx8010_rtc_control(rt_device_t dev, int cmd, void *args) +{ + rt_err_t err = RT_EOK; + struct rx8010_rtc *rx8010 = raw_to_rx8010_rtc(dev); + + if (!args) + { + return -RT_EINVAL; + } + + switch (cmd) + { + case RT_DEVICE_CTRL_RTC_GET_TIME: + err = rx8010_rtc_read_time(rx8010, args); + break; + + case RT_DEVICE_CTRL_RTC_SET_TIME: + err = rx8010_rtc_set_time(rx8010, args); + break; + + case RT_DEVICE_CTRL_RTC_GET_TIMEVAL: + err = rx8010_rtc_read_time(rx8010, (time_t *)&((struct timeval *)args)->tv_sec); + break; + + case RT_DEVICE_CTRL_RTC_SET_TIMEVAL: + err = rx8010_rtc_set_time(rx8010, (time_t *)&((struct timeval *)args)->tv_sec); + break; + + case RT_DEVICE_CTRL_RTC_GET_ALARM: + err = rx8010_rtc_read_alarm(rx8010, args); + break; + + case RT_DEVICE_CTRL_RTC_SET_ALARM: + err = rx8010_rtc_set_alarm(rx8010, args); + break; + + default: + err = -RT_EINVAL; + break; + } + + return err; +} + +#ifdef RT_USING_DEVICE_OPS +const static struct rt_device_ops rx8010_rtc_ops = +{ + .control = rx8010_rtc_control, +}; +#endif + +static void rx8010_rtc_thread_isr(void *param) +{ + rt_err_t err; + rt_uint8_t flagreg; + struct rx8010_rtc *rx8010 = param; + + while (RT_TRUE) + { + rt_thread_suspend(rx8010->irq_thread); + rt_schedule(); + + if ((err = rx8010_rtc_read(rx8010, RX8010_FLAG, &flagreg))) + { + LOG_E("Read flag error = %s", rt_strerror(err)); + continue; + } + + if (flagreg & RX8010_FLAG_VLF) + { + LOG_W("Frequency stop detected"); + } + + if (flagreg & RX8010_FLAG_TF) + { + flagreg &= ~RX8010_FLAG_TF; + rt_alarm_update(&rx8010->parent, 1); + } + + if (flagreg & RX8010_FLAG_AF) + { + flagreg &= ~RX8010_FLAG_AF; + rt_alarm_update(&rx8010->parent, 1); + } + + if (flagreg & RX8010_FLAG_UF) + { + flagreg &= ~RX8010_FLAG_UF; + rt_alarm_update(&rx8010->parent, 1); + } + + if ((err = rx8010_rtc_write(rx8010, RX8010_FLAG, flagreg))) + { + LOG_E("Write flag error = %s", rt_strerror(err)); + } + } +} + +static void rx8010_rtc_isr(int irqno, void *param) +{ + struct rx8010_rtc *rx8010 = param; + + rt_thread_resume(rx8010->irq_thread); +} + +static rt_err_t rx8010_init(struct rx8010_rtc *rx8010) +{ + rt_err_t err; + rt_uint8_t ctrl[2]; + int need_clear = 0; + + /* Initialize reserved registers as specified in datasheet */ + if ((err = rx8010_rtc_write(rx8010, RX8010_RESV17, 0xd8))) + { + return err; + } + + if ((err = rx8010_rtc_write(rx8010, RX8010_RESV30, 0x00))) + { + return err; + } + + if ((err = rx8010_rtc_write(rx8010, RX8010_RESV31, 0x08))) + { + return err; + } + + if ((err = rx8010_rtc_write(rx8010, RX8010_IRQ, 0x00))) + { + return err; + } + + err |= rx8010_rtc_read(rx8010, RX8010_FLAG, &ctrl[0]); + err |= rx8010_rtc_read(rx8010, RX8010_FLAG + 1, &ctrl[1]); + + if (err) + { + return err; + } + + if (ctrl[0] & RX8010_FLAG_VLF) + { + LOG_W("Frequency stop was detected"); + } + + if (ctrl[0] & RX8010_FLAG_AF) + { + LOG_W("Alarm was detected"); + need_clear = 1; + } + + if (ctrl[0] & (RX8010_FLAG_TF | RX8010_FLAG_UF)) + { + need_clear = 1; + } + + if (need_clear) + { + ctrl[0] &= ~(RX8010_FLAG_AF | RX8010_FLAG_TF | RX8010_FLAG_UF); + + if ((err = rx8010_rtc_write(rx8010, RX8010_FLAG, ctrl[0]))) + { + return err; + } + } + + rx8010->ctrlreg = ctrl[1] & ~RX8010_CTRL_TEST; + + return RT_EOK; +} + +static rt_err_t rx8010_rtc_probe(struct rt_i2c_client *client) +{ + rt_err_t err; + const char *dev_name; + struct rt_device *dev = &client->parent; + struct rx8010_rtc *rx8010 = rt_calloc(1, sizeof(*rx8010)); + + if (!rx8010) + { + return -RT_ENOMEM; + } + + rx8010->client = client; + + if ((err = rx8010_init(rx8010))) + { + goto _fail; + } + + rx8010->irq = rt_dm_dev_get_irq(dev, 0); + + if (rx8010->irq >= 0) + { + rx8010->irq_thread = rt_thread_create("rtc-rx8010", &rx8010_rtc_thread_isr, + rx8010, SYSTEM_THREAD_STACK_SIZE, RT_THREAD_PRIORITY_MAX / 2, 10); + + if (!rx8010->irq_thread) + { + err = -RT_ERROR; + LOG_E("Create RTC IRQ thread fail"); + goto _fail; + } + + rt_thread_startup(rx8010->irq_thread); + + rt_hw_interrupt_install(rx8010->irq, rx8010_rtc_isr, rx8010, "rtc-rx8010"); + rt_hw_interrupt_umask(rx8010->irq); + } + + dev->user_data = rx8010; + + rx8010->parent.type = RT_Device_Class_RTC; +#ifdef RT_USING_DEVICE_OPS + rx8010->parent.ops = &rx8010_rtc_ops; +#else + rx8010->parent.control = rx8010_rtc_control; +#endif + + rtc_dev_set_name(&rx8010->parent); + dev_name = rt_dm_dev_get_name(&rx8010->parent); + rt_device_register(&rx8010->parent, dev_name, RT_DEVICE_FLAG_RDWR); + + return RT_EOK; + +_fail: + if (rx8010->irq_thread) + { + rt_thread_delete(rx8010->irq_thread); + } + + rt_free(rx8010); + + return err; +} + +static rt_err_t rx8010_rtc_remove(struct rt_i2c_client *client) +{ + struct rx8010_rtc *rx8010 = client->parent.user_data; + + rx8010_rtc_set_bit(rx8010, RX8010_CTRL, RX8010_CTRL_STOP); + + if (rx8010->irq >= 0) + { + rt_hw_interrupt_mask(rx8010->irq); + rt_pic_detach_irq(rx8010->irq, rx8010); + + rt_thread_delete(rx8010->irq_thread); + } + + rt_device_unregister(&rx8010->parent); + + rt_free(rx8010); + + return RT_EOK; +} + +static const struct rt_i2c_device_id rx8010_rtc_ids[] = +{ + { .name = "rx8010" }, + { /* sentinel */ }, +}; + +static const struct rt_ofw_node_id rx8010_rtc_ofw_ids[] = +{ + { .compatible = "epson,rx8010" }, + { /* sentinel */ }, +}; + +static struct rt_i2c_driver rx8010_rtc_driver = +{ + .ids = rx8010_rtc_ids, + .ofw_ids = rx8010_rtc_ofw_ids, + + .probe = rx8010_rtc_probe, + .remove = rx8010_rtc_remove, +}; +RT_I2C_DRIVER_EXPORT(rx8010_rtc_driver); \ No newline at end of file diff --git a/components/drivers/rtc/rtc_dm.c b/components/drivers/rtc/rtc_dm.c new file mode 100644 index 000000000000..ccc983b73fda --- /dev/null +++ b/components/drivers/rtc/rtc_dm.c @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-12-06 GuEe-GUI first version + */ + +#include +#include "rtc_dm.h" + +#define DBG_TAG "rtc.dm" +#define DBG_LVL DBG_INFO +#include + +int rtc_dev_set_name(struct rt_device *rtc_dev) +{ + int id; + static volatile rt_atomic_t uid = 1; + + RT_ASSERT(rtc_dev != RT_NULL) + + if (rt_device_find("rtc")) + { + id = (int)rt_hw_atomic_add(&uid, 1); + + return rt_dm_dev_set_name(rtc_dev, "rtc%u", id); + } + else + { + return rt_dm_dev_set_name(rtc_dev, "rtc"); + } +} + +time_t rtc_wkalarm_to_timestamp(struct rt_rtc_wkalarm *alarm) +{ + struct tm tm_time; + time_t current_time; + + current_time = time(RT_NULL); + localtime_r(¤t_time, &tm_time); + + tm_time.tm_sec = alarm->tm_sec; + tm_time.tm_min = alarm->tm_min; + tm_time.tm_hour = alarm->tm_hour; + + return timegm(&tm_time); +} + +void rtc_timestamp_to_wkalarm(time_t timestamp, struct rt_rtc_wkalarm *alarm) +{ + struct tm tm_time; + + localtime_r(×tamp, &tm_time); + + alarm->tm_sec = tm_time.tm_sec; + alarm->tm_min = tm_time.tm_min; + alarm->tm_hour = tm_time.tm_hour; +} diff --git a/components/drivers/rtc/rtc_dm.h b/components/drivers/rtc/rtc_dm.h new file mode 100644 index 000000000000..a04d1f3116a0 --- /dev/null +++ b/components/drivers/rtc/rtc_dm.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-12-06 GuEe-GUI first version + */ + +#ifndef __RTC_DM_H__ +#define __RTC_DM_H__ + +#include +#include +#include + +#include + +int rtc_dev_set_name(struct rt_device *rtc_dev); +time_t rtc_wkalarm_to_timestamp(struct rt_rtc_wkalarm *alarm); +void rtc_timestamp_to_wkalarm(time_t timestamp, struct rt_rtc_wkalarm *alarm); + +#endif /* __RTC_DM_H__ */ diff --git a/components/drivers/sdio/Kconfig b/components/drivers/sdio/Kconfig new file mode 100644 index 000000000000..086b65de5279 --- /dev/null +++ b/components/drivers/sdio/Kconfig @@ -0,0 +1,32 @@ +menuconfig RT_USING_SDIO + bool "Using SD/MMC device drivers" + default n + + if RT_USING_SDIO + config RT_SDIO_STACK_SIZE + int "The stack size for sdio irq thread" + default 512 + + config RT_SDIO_THREAD_PRIORITY + int "The priority level value of sdio irq thread" + default 15 + + config RT_MMCSD_STACK_SIZE + int "The stack size for mmcsd thread" + default 1024 + + config RT_MMCSD_THREAD_PREORITY + int "The priority level value of mmcsd thread" + default 22 + + config RT_MMCSD_MAX_PARTITION + int "mmcsd max partition" + default 16 + config RT_SDIO_DEBUG + bool "Enable SDIO debug log output" + default n + endif + +if RT_USING_DM && RT_USING_SDIO +source "$RTT_DIR/components/drivers/sdio/host/Kconfig" +endif diff --git a/components/drivers/sdio/SConscript b/components/drivers/sdio/SConscript index d5b6eb1cb9b9..662a9db40565 100644 --- a/components/drivers/sdio/SConscript +++ b/components/drivers/sdio/SConscript @@ -14,6 +14,19 @@ mmc.c # The set of source files associated with this SConscript file. path = [cwd + '/../include'] +if GetDepend(['RT_USING_DM']): + src += ['sdio_dm.c'] + + if GetDepend(['RT_USING_REGULATOR']): + src += ['regulator.c'] + group = DefineGroup('DeviceDrivers', src, depend = ['RT_USING_SDIO'], CPPPATH = path) +objs = [] + +for d in os.listdir(cwd): + path = os.path.join(cwd, d) + if os.path.isfile(os.path.join(path, 'SConscript')): + objs = objs + SConscript(os.path.join(d, 'SConscript')) +objs = objs + group -Return('group') +Return('objs') diff --git a/components/drivers/sdio/block_dev.c b/components/drivers/sdio/block_dev.c index 1cdcec22b084..fe14146dc914 100644 --- a/components/drivers/sdio/block_dev.c +++ b/components/drivers/sdio/block_dev.c @@ -26,6 +26,12 @@ static rt_list_t blk_devices = RT_LIST_OBJECT_INIT(blk_devices); #define BLK_MIN(a, b) ((a) < (b) ? (a) : (b)) +#ifdef RT_USING_DM +#define BLK_PART_FMT "%sp%d" +#else +#define BLK_PART_FMT "%s%d" +#endif + struct mmcsd_blk_device { struct rt_mmcsd_card *card; @@ -479,7 +485,7 @@ rt_int32_t gpt_device_probe(struct rt_mmcsd_card *card) status = gpt_get_partition_param(card, &blk_dev->part, i); if (status == RT_EOK) { - rt_snprintf(dname, sizeof(dname) - 1, "%s%d", card->host->name, i); + rt_snprintf(dname, sizeof(dname) - 1, BLK_PART_FMT, card->host->name, i); rt_snprintf(sname, sizeof(sname) - 1, "sem_%s%d", card->host->name, i + 1); blk_dev->part.lock = rt_sem_create(sname, 1, RT_IPC_FLAG_FIFO); @@ -610,7 +616,7 @@ rt_int32_t mbr_device_probe(struct rt_mmcsd_card *card) status = dfs_filesystem_get_partition(&blk_dev->part, sector, i); if (status == RT_EOK) { - rt_snprintf(dname, sizeof(dname) - 1, "%s%d", card->host->name, i); + rt_snprintf(dname, sizeof(dname) - 1, BLK_PART_FMT, card->host->name, i); rt_snprintf(sname, sizeof(sname) - 1, "sem_%s%d", card->host->name, i + 1); blk_dev->part.lock = rt_sem_create(sname, 1, RT_IPC_FLAG_FIFO); diff --git a/components/drivers/sdio/host/Kconfig b/components/drivers/sdio/host/Kconfig new file mode 100644 index 000000000000..70da7143255d --- /dev/null +++ b/components/drivers/sdio/host/Kconfig @@ -0,0 +1,25 @@ +config RT_SDIO_SDHCI_DWCMSHC + bool "Synopsys DWC MSHC SDHCI" + select RT_USING_OFW + default n + +config RT_SDIO_DW_MMC + bool "Synopsys DesignWare MMC Family" + select RT_USING_PINCTRL + select RT_USING_RESET + select RT_USING_REGULATOR + select RT_USING_DEVICE_IPC + select RT_USING_SYSTEM_WORKQUEUE + default n + +config RT_SDIO_DW_MMC_PCI + bool "Synopsys Designware MCI support on PCI bus" + depends on RT_SDIO_DW_MMC + select RT_USING_PCI + default n + +config RT_SDIO_DW_MMC_ROCKCHIP + bool "Rockchip" + depends on RT_SDIO_DW_MMC + select RT_USING_OFW + default n diff --git a/components/drivers/sdio/host/SConscript b/components/drivers/sdio/host/SConscript new file mode 100644 index 000000000000..019f8f7306b1 --- /dev/null +++ b/components/drivers/sdio/host/SConscript @@ -0,0 +1,27 @@ +from building import * + +group = [] + +if not GetDepend(['RT_USING_SDIO']) and not GetDepend(['RT_USING_DM']): + Return('group') + +cwd = GetCurrentDir() +CPPPATH = [cwd + '/../../include'] + +src = [] + +if GetDepend(['RT_SDIO_SDHCI_DWCMSHC']): + src += ['sdhci-dwcmshc.c'] + +if GetDepend(['RT_SDIO_DW_MMC']): + src += ['sdio-dw.c', 'sdio-dw-platfrom.c'] + + if GetDepend(['RT_SDIO_DW_MMC_PCI']): + src += ['sdio-dw-pci.c'] + + if GetDepend(['RT_SDIO_DW_MMC_ROCKCHIP']): + src += ['sdio-dw_rockchip.c'] + +group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') diff --git a/components/drivers/sdio/host/sdhci-dwcmshc.c b/components/drivers/sdio/host/sdhci-dwcmshc.c new file mode 100644 index 000000000000..7695a2e455d2 --- /dev/null +++ b/components/drivers/sdio/host/sdhci-dwcmshc.c @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + */ + +#include + +#include "../sdio_dm.h" + +#define SDHCI_DWCMSHC_ARG2_STUFF RT_GENMASK(31, 16) + +/* DWCMSHC specific Mode Select value */ +#define DWCMSHC_CTRL_HS400 0x7 + +/* DWC IP vendor area 1 pointer */ +#define DWCMSHC_P_VENDOR_AREA1 0xe8 +#define DWCMSHC_AREA1_MASK RT_GENMASK(11, 0) +/* Offset inside the vendor area 1 */ +#define DWCMSHC_HOST_CTRL3 0x8 +#define DWCMSHC_EMMC_CONTROL 0x2c +#define DWCMSHC_CARD_IS_EMMC RT_BIT(0) +#define DWCMSHC_ENHANCED_STROBE RT_BIT(8) +#define DWCMSHC_EMMC_ATCTRL 0x40 + +/* Rockchip specific Registers */ +#define DWCMSHC_EMMC_DLL_CTRL 0x800 +#define DWCMSHC_EMMC_DLL_RXCLK 0x804 +#define DWCMSHC_EMMC_DLL_TXCLK 0x808 +#define DWCMSHC_EMMC_DLL_STRBIN 0x80c +#define DECMSHC_EMMC_DLL_CMDOUT 0x810 +#define DWCMSHC_EMMC_DLL_STATUS0 0x840 +#define DWCMSHC_EMMC_DLL_START RT_BIT(0) +#define DWCMSHC_EMMC_DLL_LOCKED RT_BIT(8) +#define DWCMSHC_EMMC_DLL_TIMEOUT RT_BIT(9) +#define DWCMSHC_EMMC_DLL_RXCLK_SRCSEL 29 +#define DWCMSHC_EMMC_DLL_START_POINT 16 +#define DWCMSHC_EMMC_DLL_INC 8 +#define DWCMSHC_EMMC_DLL_BYPASS RT_BIT(24) +#define DWCMSHC_EMMC_DLL_DLYENA RT_BIT(27) +#define DLL_TXCLK_TAPNUM_DEFAULT 0x10 +#define DLL_TXCLK_TAPNUM_90_DEGREES 0xA +#define DLL_TXCLK_TAPNUM_FROM_SW RT_BIT(24) +#define DLL_STRBIN_TAPNUM_DEFAULT 0x8 +#define DLL_STRBIN_TAPNUM_FROM_SW RT_BIT(24) +#define DLL_STRBIN_DELAY_NUM_SEL RT_BIT(26) +#define DLL_STRBIN_DELAY_NUM_OFFSET 16 +#define DLL_STRBIN_DELAY_NUM_DEFAULT 0x16 +#define DLL_RXCLK_NO_INVERTER 1 +#define DLL_RXCLK_INVERTER 0 +#define DLL_CMDOUT_TAPNUM_90_DEGREES 0x8 +#define DLL_RXCLK_ORI_GATE RT_BIT(31) +#define DLL_CMDOUT_TAPNUM_FROM_SW RT_BIT(24) +#define DLL_CMDOUT_SRC_CLK_NEG RT_BIT(28) +#define DLL_CMDOUT_EN_SRC_CLK_NEG RT_BIT(29) + +#define DLL_LOCK_WO_TMOUT(x) \ + ((((x) & DWCMSHC_EMMC_DLL_LOCKED) == DWCMSHC_EMMC_DLL_LOCKED) && \ + (((x) & DWCMSHC_EMMC_DLL_TIMEOUT) == 0)) + +#define BOUNDARY_OK(addr, len) \ + ((addr | ((128 * SIZE_MB) - 1)) == ((addr + len - 1) | ((128 * SIZE_MB) - 1))) + +struct sdwcmshc_soc_data +{ + const struct rt_mmcsd_host_ops *ops; +}; + +struct dwcmshc_sdhci +{ + struct rt_clk *bus_clk; + /* P_VENDOR_SPECIFIC_AREA reg */ + int vendor_specific_area1; + + void *priv; +}; + +enum dwcmshc_rk_type +{ + DWCMSHC_RK3568, + DWCMSHC_RK3588, +}; + +struct rk35xx_priv +{ + /* Rockchip specified optional clocks */ + struct rt_clk_array *clk_arr; + struct reset_control *rstc; + + enum dwcmshc_rk_type devtype; + rt_uint8_t txclk_tapnum; +}; + +static rt_err_t sdhci_dwcmshc_probe(struct rt_platform_device *pdev) +{ + return RT_EOK; +} + +static const struct sdwcmshc_soc_data sdhci_dwcmshc_data = +{ +}; + +static const struct sdwcmshc_soc_data sdhci_dwcmshc_rk35xx_data = +{ +}; + +static const struct rt_ofw_node_id sdhci_dwcmshc_ofw_ids[] = +{ + { .compatible = "rockchip,rk3588-dwcmshc", .data = &sdhci_dwcmshc_rk35xx_data, }, + { .compatible = "rockchip,rk3568-dwcmshc", .data = &sdhci_dwcmshc_rk35xx_data, }, + { .compatible = "rockchip,dwcmshc-sdhci", .data = &sdhci_dwcmshc_rk35xx_data, }, + { .compatible = "snps,dwcmshc-sdhci", .data = &sdhci_dwcmshc_data, }, + { /* sentinel */ } +}; + +static struct rt_platform_driver sdhci_dwcmshc_driver = +{ + .name = "sdhci-dwcmshc", + .ids = sdhci_dwcmshc_ofw_ids, + + .probe = sdhci_dwcmshc_probe, +}; +RT_PLATFORM_DRIVER_EXPORT(sdhci_dwcmshc_driver); diff --git a/components/drivers/sdio/host/sdio-dw-pci.c b/components/drivers/sdio/host/sdio-dw-pci.c new file mode 100644 index 000000000000..57ab4eb3aa5e --- /dev/null +++ b/components/drivers/sdio/host/sdio-dw-pci.c @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-12-06 GuEe-GUI first version + */ + +#include "sdio-dw.h" + +#include + +#define SYNOPSYS_DW_MCI_VENDOR_ID 0x700 +#define SYNOPSYS_DW_MCI_DEVICE_ID 0x1107 + +#define MCI_REG_NO 2 + +static const struct sdio_dw_drv_data sdio_dw_pci_drv_data = +{ +}; + +static rt_err_t sdio_dw_pci_probe(struct rt_pci_device *pdev) +{ + rt_err_t err; + struct sdio_dw *sd = rt_calloc(1, sizeof(*sd)); + + if (!sd) + { + return -RT_ENOMEM; + } + + sd->base = rt_pci_iomap(pdev, MCI_REG_NO); + + if (!sd->base) + { + goto _fail; + } + + sd->irq = pdev->irq; + rt_pci_irq_unmask(pdev); + + sd->base_phy = (rt_ubase_t)rt_kmem_v2p(sd->base); + sd->drv_data = &sdio_dw_pci_drv_data; + + /* board data */ + sd->bus_hz = 33 * 1000 * 1000; + sd->detect_delay_ms = 200; + sd->fifo_depth = 32; + + pdev->parent.user_data = sd; + + if ((err = sdio_dw_probe(sd))) + { + goto _fail; + } + + return RT_EOK; + +_fail: + if (sd->base) + { + rt_iounmap(sd->base); + } + + rt_free(sd); + + return err; +} + +static rt_err_t sdio_dw_pci_remove(struct rt_pci_device *pdev) +{ + struct sdio_dw *sd = pdev->parent.user_data; + + sdio_dw_remove(sd); + + rt_iounmap(sd->base); + + rt_free(sd); + + return RT_EOK; +} + +static struct rt_pci_device_id sdio_dw_pci_pci_ids[] = +{ + { RT_PCI_DEVICE_ID(SYNOPSYS_DW_MCI_VENDOR_ID, SYNOPSYS_DW_MCI_DEVICE_ID) }, + { /* sentinel */ } +}; + +static struct rt_pci_driver sdio_dw_pci_driver = +{ + .name = "dw-mmc-pci", + + .ids = sdio_dw_pci_pci_ids, + .probe = sdio_dw_pci_probe, + .remove = sdio_dw_pci_remove, +}; +RT_PCI_DRIVER_EXPORT(sdio_dw_pci_driver); diff --git a/components/drivers/sdio/host/sdio-dw-platfrom.c b/components/drivers/sdio/host/sdio-dw-platfrom.c new file mode 100644 index 000000000000..ece41f246ee1 --- /dev/null +++ b/components/drivers/sdio/host/sdio-dw-platfrom.c @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-12-06 GuEe-GUI first version + */ + +#include "sdio-dw-platfrom.h" + +#include + +rt_err_t sdio_dw_platfrom_register(struct rt_platform_device *pdev, + const struct sdio_dw_drv_data *drv_data) +{ + rt_err_t err = RT_EOK; + struct rt_device *dev = &pdev->parent; + struct sdio_dw *sd = rt_calloc(1, sizeof(*sd)); + + if (!sd) + { + return -RT_ENOMEM; + } + + sd->base = rt_dm_dev_iomap(dev, 0); + + if (!sd->base) + { + err = -RT_EIO; + goto _fail; + } + + sd->irq = rt_dm_dev_get_irq(dev, 0); + + if (sd->irq < 0) + { + err = sd->irq; + + goto _fail; + } + + sd->parent.ofw_node = dev->ofw_node; + + sd->base_phy = (rt_ubase_t)rt_kmem_v2p(sd->base); + sd->drv_data = drv_data; + + pdev->parent.user_data = sd; + + if ((err = sdio_dw_probe(sd))) + { + goto _fail; + } + + return RT_EOK; + +_fail: + if (sd->base) + { + rt_iounmap(sd->base); + } + + rt_free(sd); + + return err; +} + +static rt_err_t sdio_dw_platfrom_probe(struct rt_platform_device *pdev) +{ + const struct sdio_dw_drv_data *drv_data = RT_NULL; + + if (pdev->parent.ofw_node) + { + drv_data = pdev->id->data; + } + + return sdio_dw_platfrom_register(pdev, drv_data); +} + +static rt_err_t sdio_dw_platfrom_remove(struct rt_platform_device *pdev) +{ + struct sdio_dw *sd = pdev->parent.user_data; + + sdio_dw_remove(sd); + + rt_iounmap(sd->base); + + rt_free(sd); + + return RT_EOK; +} + +static const struct rt_ofw_node_id sdio_dw_platfrom_ofw_ids[] = +{ + { .compatible = "snps,dw-mshc", }, + { .compatible = "img,pistachio-dw-mshc", }, + { /* sentinel */ } +}; + +static struct rt_platform_driver sdio_dw_platfrom_driver = +{ + .name = "dw-mmc", + .ids = sdio_dw_platfrom_ofw_ids, + + .probe = sdio_dw_platfrom_probe, + .remove = sdio_dw_platfrom_remove, +}; +RT_PLATFORM_DRIVER_EXPORT(sdio_dw_platfrom_driver); diff --git a/components/drivers/sdio/host/sdio-dw-platfrom.h b/components/drivers/sdio/host/sdio-dw-platfrom.h new file mode 100644 index 000000000000..314d4ea4e00e --- /dev/null +++ b/components/drivers/sdio/host/sdio-dw-platfrom.h @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-12-06 GuEe-GUI first version + */ + +#ifndef __SDIO_DW_PLATFROM_H__ +#define __SDIO_DW_PLATFROM_H__ + +#include "sdio-dw.h" + +rt_err_t sdio_dw_platfrom_register(struct rt_platform_device *pdev, + const struct sdio_dw_drv_data *drv_data); + +#endif /* __SDIO_DW_PLATFROM_H__ */ diff --git a/components/drivers/sdio/host/sdio-dw.c b/components/drivers/sdio/host/sdio-dw.c new file mode 100644 index 000000000000..6326425127d7 --- /dev/null +++ b/components/drivers/sdio/host/sdio-dw.c @@ -0,0 +1,3156 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-12-06 GuEe-GUI first version + */ + +#include + +#define DBG_TAG "sdio.dw" +#define DBG_LVL DBG_INFO +#include + +#include +#include + +#include "sdio-dw.h" + +/* Common flag combinations */ +#define PINT(x) SDIO_DW_INT_##x +#define SDIO_DW_DATA_ERROR_FLAGS (PINT(DRTO) | PINT(DCRC) | PINT(HTO) | PINT(SBE) | PINT(EBE) | PINT(HLE)) +#define SDIO_DW_CMD_ERROR_FLAGS (PINT(RTO) | PINT(RCRC) | PINT(RESP_ERR) | PINT(HLE)) +#define SDIO_DW_ERROR_FLAGS (SDIO_DW_DATA_ERROR_FLAGS | SDIO_DW_CMD_ERROR_FLAGS) +#define SDIO_DW_SEND_STATUS 1 +#define SDIO_DW_RECV_STATUS 2 +#define SDIO_DW_DMA_THRESHOLD 16 + +#define SDIO_DW_FREQ_HZ_MAX 200000000 +#define SDIO_DW_FREQ_HZ_MIN 100000 + +#define PINTC(x) SDIO_DW_IDMAC_INT_##x +#define SDIO_DW_IDMAC_INT_CLR (PINTC(AI) | PINTC(NI) | PINTC(CES) | PINTC(DU) | PINTC(FBE) | PINTC(RI) | PINTC(TI)) + +#define DESC_RING_BUF_SZ ARCH_PAGE_SIZE +#define NSEC_PER_SEC 1000000000L +#define USEC_PER_MSEC 1000L +#define MSEC_PER_SEC 1000L + +struct idmac_desc64 +{ + rt_uint32_t des0; /* Control descriptor */ +#define IDMAC_OWN_CLR64(x) !((x) & rt_cpu_to_le32(IDMAC_DES0_OWN)) + + rt_uint32_t des1; /* Reserved */ + + rt_uint32_t des2; /* Buffer sizes */ +#define IDMAC_64ADDR_SET_BUFFER1_SIZE(d, s) \ + ((d)->des2 = ((d)->des2 & rt_cpu_to_le32(0x03ffe000)) | ((rt_cpu_to_le32(s)) & rt_cpu_to_le32(0x1fff))) + + rt_uint32_t des3; /* Reserved */ + + rt_uint32_t des4; /* Lower 32-bits of buffer address pointer 1 */ + rt_uint32_t des5; /* Upper 32-bits of buffer address pointer 1 */ + + rt_uint32_t des6; /* Lower 32-bits of next descriptor address */ + rt_uint32_t des7; /* Upper 32-bits of next descriptor address */ +}; + +struct idmac_desc32 +{ + rt_le32_t des0; /* Control Descriptor */ +#define IDMAC_DES0_DIC RT_BIT(1) +#define IDMAC_DES0_LD RT_BIT(2) +#define IDMAC_DES0_FD RT_BIT(3) +#define IDMAC_DES0_CH RT_BIT(4) +#define IDMAC_DES0_ER RT_BIT(5) +#define IDMAC_DES0_CES RT_BIT(30) +#define IDMAC_DES0_OWN RT_BIT(31) + rt_le32_t des1; /* Buffer sizes */ + +#define IDMAC_32ADDR_SET_BUFFER1_SIZE(d, s) \ + ((d)->des1 = ((d)->des1 & rt_cpu_to_le32(0x03ffe000)) | (rt_cpu_to_le32((s) & 0x1fff))) + + rt_le32_t des2; /* Buffer 1 physical address */ + rt_le32_t des3; /* Buffer 2 physical address */ +}; + +/* Each descriptor can transfer up to 4KB of data in chained mode */ +#define DW_MCI_DESC_DATA_LENGTH 0x1000 + +static rt_bool_t sdio_dw_ctrl_reset(struct sdio_dw *sd, rt_uint32_t reset) +{ + rt_uint32_t ctrl; + rt_tick_t start; + int timeout = rt_tick_from_millisecond(500); + + ctrl = sdio_dw_readl(sd, CTRL); + ctrl |= reset; + sdio_dw_writel(sd, CTRL, ctrl); + + start = rt_tick_get(); + + while ((sdio_dw_readl(sd, CTRL) & reset)) + { + if ((rt_tick_get() - start) > timeout) + { + LOG_E("Timeout resetting block (ctrl reset 0x%x)", ctrl & reset); + + return RT_FALSE; + } + + rt_hw_cpu_relax(); + } + + return RT_TRUE; +} + +static void sdio_dw_wait_while_busy(struct sdio_dw *sd, rt_uint32_t cmd_flags) +{ + if ((cmd_flags & SDIO_DW_CMD_PRV_DAT_WAIT) && !(cmd_flags & SDIO_DW_CMD_VOLT_SWITCH)) + { + rt_tick_t start = rt_tick_get(); + int timeout = rt_tick_from_millisecond(500); + + while ((sdio_dw_readl(sd, STATUS) & SDIO_DW_STATUS_BUSY)) + { + if ((rt_tick_get() - start) > timeout) + { + LOG_E("Wait busy fail"); + + break; + } + + rt_hw_cpu_relax(); + } + } +} + +static void sdio_dw_send_cmd(struct sdio_dw_slot *slot, rt_uint32_t cmd, rt_uint32_t arg) +{ + rt_tick_t start; + struct sdio_dw *sd = slot->sd; + int timeout = rt_tick_from_millisecond(500); + + sdio_dw_writel(sd, CMDARG, arg); + rt_hw_wmb(); + sdio_dw_wait_while_busy(sd, cmd); + sdio_dw_writel(sd, CMD, SDIO_DW_CMD_START | cmd); + + start = rt_tick_get(); + + while ((sdio_dw_readl(sd, CMD) & SDIO_DW_CMD_START)) + { + if ((rt_tick_get() - start) > timeout) + { + LOG_E("Wait command start fail"); + + break; + } + + rt_hw_cpu_relax(); + } +} + +rt_inline void sdio_dw_set_cto(struct sdio_dw *sd) +{ + rt_ubase_t level; + rt_uint32_t cto_clks, cto_div, cto_ms; + + cto_clks = sdio_dw_readl(sd, TMOUT) & 0xff; + cto_div = (sdio_dw_readl(sd, CLKDIV) & 0xff) * 2; + + if (cto_div == 0) + { + cto_div = 1; + } + + cto_ms = RT_DIV_ROUND_UP_ULL((rt_uint64_t)MSEC_PER_SEC * cto_clks * cto_div, + sd->bus_hz); + + /* Add a bit spare time */ + cto_ms += 10; + + /* + * The durations we're working with are fairly short so we have to be extra + * careful about synchronization here. Specifically in hardware a command + * timeout is _at most_ 5.1 ms, so that means we expect an interrupt + * (either command done or timeout) to come rather quickly after the + * sdio_dw_writel. ...but just in case we have a long interrupt latency + * let's add a bit of paranoia. + * + * In general we'll assume that at least an interrupt will be asserted in + * hardware by the time the cto_timer runs. ...and if it hasn't been + * asserted in hardware by that time then we'll assume it'll never come. + */ + level = rt_spin_lock_irqsave(&sd->irq_lock); + + if (!bitmap_test_bit(&sd->pending_events, EVENT_CMD_COMPLETE)) + { + rt_tick_t tick = rt_tick_from_millisecond(cto_ms) + 1; + + rt_timer_control(&sd->cto_timer, RT_TIMER_CTRL_SET_TIME, &tick); + + rt_timer_start(&sd->cto_timer); + } + + rt_spin_unlock_irqrestore(&sd->irq_lock, level); +} + +static void sdio_dw_start_cmd(struct sdio_dw_slot *slot, rt_uint32_t cmd, + rt_uint32_t arg) +{ + struct sdio_dw *sd = slot->sd; + + sdio_dw_writel(sd, CMDARG, arg); + rt_hw_wmb(); + sdio_dw_wait_while_busy(sd, cmd); + sdio_dw_writel(sd, CMD, SDIO_DW_CMD_START | cmd); + + /* Response expected command only */ + if ((cmd & SDIO_DW_CMD_RESP_EXP)) + { + sdio_dw_set_cto(sd); + } +} + +rt_inline void send_stop_abort(struct sdio_dw *sd, struct rt_mmcsd_data *data) +{ + struct rt_mmcsd_cmd *stop = &sd->stop_abort; + + sdio_dw_start_cmd(sd->slot, sd->stop_cmdr, stop->arg); +} + +/* DMA interface functions */ +static void sdio_dw_stop_dma(struct sdio_dw *sd) +{ + if (sd->using_dma) + { + sd->dma_ops->stop(sd); + sd->dma_ops->cleanup(sd); + } + + /* Data transfer was stopped by the interrupt handler */ + bitmap_set_bit(&sd->pending_events, EVENT_XFER_COMPLETE); +} + +static rt_uint32_t sdio_dw_prep_stop_abort(struct sdio_dw *sd, struct rt_mmcsd_cmd *cmd) +{ + rt_uint32_t cmdr; + struct rt_mmcsd_cmd *stop; + + if (!cmd->data) + { + return 0; + } + + stop = &sd->stop_abort; + cmdr = cmd->cmd_code; + rt_memset(stop, 0, sizeof(*stop)); + + if (cmdr == READ_SINGLE_BLOCK || + cmdr == READ_MULTIPLE_BLOCK || + cmdr == WRITE_BLOCK || + cmdr == WRITE_MULTIPLE_BLOCK || + cmdr == SEND_TUNING_BLOCK || + cmdr == SEND_TUNING_BLOCK_HS200 || + cmdr == GEN_CMD) + { + stop->cmd_code = STOP_TRANSMISSION; + stop->arg = 0; + stop->flags = CMD_AC; + } + else if (cmdr == SD_IO_RW_EXTENDED) + { + stop->cmd_code = SD_IO_RW_DIRECT; + stop->arg |= (1 << 31) | (0 << 28) | ((cmd->arg >> 28) & 0x7); + stop->flags = RESP_SPI_R5 | CMD_AC; + } + else + { + return 0; + } + + cmdr = stop->cmd_code | SDIO_DW_CMD_STOP | SDIO_DW_CMD_RESP_CRC | SDIO_DW_CMD_RESP_EXP; + + if (!bitmap_test_bit(&sd->slot->flags, DW_MMC_CARD_NO_USE_HOLD)) + { + cmdr |= SDIO_DW_CMD_USE_HOLD_REG; + } + + return cmdr; +} + +static void sdio_dw_idmac_reset(struct sdio_dw *sd) +{ + /* Software reset of DMA */ + sdio_dw_writel(sd, BMOD, sdio_dw_readl(sd, BMOD) | SDIO_DW_IDMAC_SWRESET); +} + +static rt_err_t sdio_dw_idmac_init(struct sdio_dw *sd) +{ + int i; + rt_err_t err = RT_EOK; + + if (sd->dma_64bit_address) + { + struct idmac_desc64 *p; + /* Number of descriptors in the ring buffer */ + sd->ring_size = DESC_RING_BUF_SZ / sizeof(struct idmac_desc64); + + /* Forward link the descriptor list */ + for (i = 0, p = sd->dma_buf; i < sd->ring_size - 1; ++i, ++p) + { + p->des6 = (sd->dma_buf_phy + (sizeof(struct idmac_desc64) * (i + 1))) & 0xffffffff; + p->des7 = (rt_uint64_t)(sd->dma_buf_phy + (sizeof(struct idmac_desc64) * (i + 1))) >> 32; + /* Initialize reserved and buffer size fields to "0" */ + p->des0 = 0; + p->des1 = 0; + p->des2 = 0; + p->des3 = 0; + } + + /* Set the last descriptor as the end-of-ring descriptor */ + p->des6 = sd->dma_buf_phy & 0xffffffff; + p->des7 = (rt_uint64_t)sd->dma_buf_phy >> 32; + p->des0 = IDMAC_DES0_ER; + } + else + { + struct idmac_desc32 *p; + /* Number of descriptors in the ring buffer */ + sd->ring_size = DESC_RING_BUF_SZ / sizeof(struct idmac_desc32); + + /* Forward link the descriptor list */ + for (i = 0, p = sd->dma_buf; i < sd->ring_size - 1; ++i, ++p) + { + p->des3 = rt_cpu_to_le32(sd->dma_buf_phy + (sizeof(struct idmac_desc32) * (i + 1))); + p->des0 = 0; + p->des1 = 0; + } + + /* Set the last descriptor as the end-of-ring descriptor */ + p->des3 = rt_cpu_to_le32(sd->dma_buf_phy); + p->des0 = rt_cpu_to_le32(IDMAC_DES0_ER); + } + + sdio_dw_idmac_reset(sd); + + if (sd->dma_64bit_address) + { + /* Mask out interrupts - get Tx & Rx complete only */ + sdio_dw_writel(sd, IDSTS64, SDIO_DW_IDMAC_INT_CLR); + sdio_dw_writel(sd, IDINTEN64, PINTC(NI) | PINTC(RI) | PINTC(TI)); + + /* Set the descriptor base address */ + sdio_dw_writel(sd, DBADDRL, sd->dma_buf_phy & 0xffffffff); + sdio_dw_writel(sd, DBADDRU, (rt_uint64_t)sd->dma_buf_phy >> 32); + } + else + { + /* Mask out interrupts - get Tx & Rx complete only */ + sdio_dw_writel(sd, IDSTS, SDIO_DW_IDMAC_INT_CLR); + sdio_dw_writel(sd, IDINTEN, PINTC(NI) | PINTC(RI) | PINTC(TI)); + + /* Set the descriptor base address */ + sdio_dw_writel(sd, DBADDR, sd->dma_buf_phy); + } + + return err; +} + +rt_inline rt_err_t sdio_dw_prepare_desc64(struct sdio_dw *sd, struct rt_mmcsd_data *data) +{ + rt_uint32_t desc_len; + rt_uint64_t mem_addr; + int timeout = rt_tick_from_millisecond(100); + struct idmac_desc64 *desc_first, *desc_last, *desc; + + desc_first = desc_last = desc = sd->dma_buf; + mem_addr = (rt_uint64_t)rt_kmem_v2p(sd->last_buf); + + for (rt_uint32_t length = sd->last_remain; length; ++desc) + { + rt_tick_t start = rt_tick_get(); + + desc_len = rt_min_t(rt_uint32_t, length, DW_MCI_DESC_DATA_LENGTH); + length -= desc_len; + + /* + * Wait for the former clear OWN bit operation of IDMAC to make sure + * that this descriptor isn't still owned by IDMAC as IDMAC's write ops + * and CPU's read ops are asynchronous. + */ + while ((HWREG32(&desc->des0) & IDMAC_DES0_OWN)) + { + if ((rt_tick_get() - start) > timeout) + { + goto _err_own_bit; + } + + rt_hw_cpu_relax(); + } + + /* Set the OWN bit and disable interrupts for this descriptor */ + desc->des0 = IDMAC_DES0_OWN | IDMAC_DES0_DIC | IDMAC_DES0_CH; + + /* Buffer length */ + IDMAC_64ADDR_SET_BUFFER1_SIZE(desc, desc_len); + + /* Physical address to DMA to/from */ + desc->des4 = mem_addr & 0xffffffff; + desc->des5 = mem_addr >> 32; + + /* Update physical address for the next desc */ + mem_addr += desc_len; + + /* Save pointer to the last descriptor */ + desc_last = desc; + } + + /* Set first descriptor */ + desc_first->des0 |= IDMAC_DES0_FD; + + /* Set last descriptor */ + desc_last->des0 &= ~(IDMAC_DES0_CH | IDMAC_DES0_DIC); + desc_last->des0 |= IDMAC_DES0_LD; + + return RT_EOK; + +_err_own_bit: + /* restore the descriptor chain as it's polluted */ + LOG_D("Descriptor is still owned by IDMAC"); + + rt_memset(sd->dma_buf, 0, DESC_RING_BUF_SZ); + sdio_dw_idmac_init(sd); + + return -RT_EINVAL; +} + +rt_inline rt_err_t sdio_dw_prepare_desc32(struct sdio_dw *sd, struct rt_mmcsd_data *data) +{ + rt_uint32_t desc_len, mem_addr; + int timeout = rt_tick_from_millisecond(100); + struct idmac_desc32 *desc_first, *desc_last, *desc; + + desc_first = desc_last = desc = sd->dma_buf; + mem_addr = (rt_ubase_t)rt_kmem_v2p(sd->last_buf); + + for (rt_uint32_t length = sd->last_remain; length; ++desc) + { + rt_tick_t start = rt_tick_get(); + + desc_len = rt_min_t(rt_uint32_t, length, DW_MCI_DESC_DATA_LENGTH); + length -= desc_len; + + /* + * Wait for the former clear OWN bit operation of IDMAC to make sure + * that this descriptor isn't still owned by IDMAC as IDMAC's write ops + * and CPU's read ops are asynchronous. + */ + while (!IDMAC_OWN_CLR64(HWREG32(&desc->des0))) + { + if ((rt_tick_get() - start) > timeout) + { + goto _err_own_bit; + } + + rt_hw_cpu_relax(); + } + + /* Set the OWN bit and disable interrupts for this descriptor */ + desc->des0 = rt_cpu_to_le32(IDMAC_DES0_OWN | IDMAC_DES0_DIC | IDMAC_DES0_CH); + + /* Buffer length */ + IDMAC_32ADDR_SET_BUFFER1_SIZE(desc, desc_len); + + /* Physical address to DMA to/from */ + desc->des2 = rt_cpu_to_le32(mem_addr); + + /* Update physical address for the next desc */ + mem_addr += desc_len; + + /* Save pointer to the last descriptor */ + desc_last = desc; + } + + /* Set first descriptor */ + desc_first->des0 |= rt_cpu_to_le32(IDMAC_DES0_FD); + + /* Set last descriptor */ + desc_last->des0 &= rt_cpu_to_le32(~(IDMAC_DES0_CH | IDMAC_DES0_DIC)); + desc_last->des0 |= rt_cpu_to_le32(IDMAC_DES0_LD); + + return RT_EOK; + +_err_own_bit: + /* restore the descriptor chain as it's polluted */ + LOG_D("Descriptor is still owned by IDMAC"); + + rt_memset(sd->dma_buf, 0, DESC_RING_BUF_SZ); + sdio_dw_idmac_init(sd); + + return -RT_EINVAL; +} + +static rt_err_t sdio_dw_idmac_start(struct sdio_dw *sd) +{ + rt_err_t err = RT_EOK; + + if (sd->dma_64bit_address) + { + err = sdio_dw_prepare_desc64(sd, sd->data); + } + else + { + err = sdio_dw_prepare_desc32(sd, sd->data); + } + + if (err) + { + goto _out; + } + + /* Drain writebuffer */ + rt_hw_wmb(); + + /* Make sure to reset DMA in case we did PIO before this */ + sdio_dw_ctrl_reset(sd, SDIO_DW_CTRL_DMA_RESET); + sdio_dw_idmac_reset(sd); + + /* Select IDMAC interface */ + sdio_dw_writel(sd, CTRL, sdio_dw_readl(sd, CTRL) | SDIO_DW_CTRL_USE_IDMAC); + + /* Drain writebuffer */ + rt_hw_wmb(); + + /* Enable the IDMAC */ + sdio_dw_writel(sd, BMOD, sdio_dw_readl(sd, BMOD) | SDIO_DW_IDMAC_ENABLE | SDIO_DW_IDMAC_FB); + + /* Start it running */ + sdio_dw_writel(sd, PLDMND, 1); + +_out: + return err; +} + +static rt_err_t sdio_dw_idmac_complete(struct sdio_dw *sd) +{ + rt_err_t err = RT_EOK; + struct rt_mmcsd_data *data = sd->data; + + sd->dma_ops->cleanup(sd); + + if (data) + { + rt_hw_cpu_dcache_ops(RT_HW_CACHE_INVALIDATE, data->buf, data->blks * data->blksize); + bitmap_set_bit(&sd->pending_events, EVENT_XFER_COMPLETE); + rt_workqueue_urgent_work(sd->state_wq, &sd->state_work); + } + + return err; +} + +static rt_err_t sdio_dw_idmac_stop(struct sdio_dw *sd) +{ + rt_uint32_t reg; + + /* Disable and reset the IDMAC interface */ + reg = sdio_dw_readl(sd, CTRL); + reg &= ~SDIO_DW_CTRL_USE_IDMAC; + reg |= SDIO_DW_CTRL_DMA_RESET; + sdio_dw_writel(sd, CTRL, reg); + + /* Stop the IDMAC running */ + reg = sdio_dw_readl(sd, BMOD); + reg &= ~(SDIO_DW_IDMAC_ENABLE | SDIO_DW_IDMAC_FB); + reg |= SDIO_DW_IDMAC_SWRESET; + sdio_dw_writel(sd, BMOD, reg); + + return RT_EOK; +} + +static rt_err_t sdio_dw_idmac_cleanup(struct sdio_dw *sd) +{ + return RT_EOK; +} + +static const struct sdio_dw_dma_ops sdio_dw_idmac_ops = +{ + .init = sdio_dw_idmac_init, + .start = sdio_dw_idmac_start, + .complete = sdio_dw_idmac_complete, + .stop = sdio_dw_idmac_stop, + .cleanup = sdio_dw_idmac_cleanup, +}; + +static const struct sdio_dw_dma_ops sdio_dw_edmac_ops = +{ +}; + +static rt_bool_t sdio_dw_get_cd(struct sdio_dw_slot *slot) +{ + rt_bool_t present; + struct sdio_dw *sd = slot->sd; + + if (!controller_is_removable(slot->host)) + { + present = RT_TRUE; + } + else + { + present = (sdio_dw_readl(sd, CDETECT) & (1 << slot->id)) == 0; + } + + return present; +} + +static void sdio_dw_adjust_fifoth(struct sdio_dw *sd, struct rt_mmcsd_data *data) +{ + static const rt_uint32_t mszs[] = { 1, 4, 8, 16, 32, 64, 128, 256 }; + rt_uint32_t blksz = data->blksize; + rt_uint32_t fifo_width = 1 << sd->data_shift; + rt_uint32_t blksz_depth = blksz / fifo_width, fifoth_val; + rt_uint32_t msize = 0, rx_wmark = 1, tx_wmark, tx_wmark_invers; + int idx = RT_ARRAY_SIZE(mszs) - 1; + + /* PIO should ship this scenario */ + if (!sd->use_dma) + { + return; + } + + tx_wmark = (sd->fifo_depth) / 2; + tx_wmark_invers = sd->fifo_depth - tx_wmark; + + /* MSIZE is '1', if blksz is not a multiple of the FIFO width */ + if (blksz % fifo_width) + { + goto _done; + } + + do { + if (!((blksz_depth % mszs[idx]) || (tx_wmark_invers % mszs[idx]))) + { + msize = idx; + rx_wmark = mszs[idx] - 1; + + break; + } + } while (--idx > 0); + /* If idx is '0', it won't be tried Thus, initial values are uesed */ + +_done: + fifoth_val = SDIO_DW_SET_FIFOTH(msize, rx_wmark, tx_wmark); + sdio_dw_writel(sd, FIFOTH, fifoth_val); +} + +static void sdio_dw_ctrl_thld(struct sdio_dw *sd, struct rt_mmcsd_data *data) +{ + rt_uint8_t enable; + rt_uint16_t thld_size; + rt_uint32_t blksz_depth, fifo_depth; + rt_uint32_t blksz = data->blksize; + + /* + * CDTHRCTL doesn't exist prior to 240A (in fact that register offset is + * in the FIFO region, so we really shouldn't access it). + */ + if (sd->verid < SDIO_DW_240A || + (sd->verid < SDIO_DW_280A && (data->flags & DATA_DIR_WRITE))) + { + return; + } + + /* + * Card write Threshold is introduced since 2.80a + * It's used when HS400 mode is enabled. + */ + if ((data->flags & DATA_DIR_WRITE) && sd->timing != MMCSD_TIMING_MMC_HS400) + { + goto _disable; + } + + if ((data->flags & DATA_DIR_WRITE)) + { + enable = SDIO_DW_CARD_WR_THR_EN; + } + else + { + enable = SDIO_DW_CARD_RD_THR_EN; + } + + if (sd->timing != MMCSD_TIMING_MMC_HS200 && + sd->timing != MMCSD_TIMING_UHS_SDR104 && + sd->timing != MMCSD_TIMING_MMC_HS400) + { + goto _disable; + } + + blksz_depth = blksz / (1 << sd->data_shift); + fifo_depth = sd->fifo_depth; + + if (blksz_depth > fifo_depth) + { + goto _disable; + } + + /* + * If (blksz_depth) >= (fifo_depth >> 1), should be 'thld_size <= blksz' + * If (blksz_depth) < (fifo_depth >> 1), should be thld_size = blksz + * Currently just choose blksz. + */ + thld_size = blksz; + sdio_dw_writel(sd, CDTHRCTL, SDIO_DW_SET_THLD(thld_size, enable)); + + return; + +_disable: + sdio_dw_writel(sd, CDTHRCTL, 0); +} + +static rt_err_t sdio_dw_submit_data_dma(struct sdio_dw *sd, struct rt_mmcsd_data *data) +{ + rt_uint32_t temp; + rt_ubase_t level; + + sd->using_dma = RT_FALSE; + + /* If we don't have a channel, we can't do DMA */ + if (!sd->use_dma) + { + return -RT_ENOSYS; + } + + sd->using_dma = RT_TRUE; + + /* + * Decide the MSIZE and RX/TX Watermark. If current block size is same with + * previous size, no need to update fifoth. + */ + if (sd->prev_blksz != data->blksize) + { + sdio_dw_adjust_fifoth(sd, data); + } + + /* Enable the DMA interface */ + temp = sdio_dw_readl(sd, CTRL); + temp |= SDIO_DW_CTRL_DMA_ENABLE; + sdio_dw_writel(sd, CTRL, temp); + + /* Disable RX/TX IRQs, let DMA handle it */ + level = rt_spin_lock_irqsave(&sd->irq_lock); + temp = sdio_dw_readl(sd, INTMASK); + temp &= ~(PINT(RXDR) | PINT(TXDR)); + sdio_dw_writel(sd, INTMASK, temp); + rt_spin_unlock_irqrestore(&sd->irq_lock, level); + + /* Flush data to memory */ + rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, sd->last_buf, sd->last_remain); + + if (sd->dma_ops->start(sd)) + { + /* We can't do DMA, try PIO for this one */ + sd->dma_ops->stop(sd); + + return -RT_ENOSYS; + } + + return RT_EOK; +} + +static void sdio_dw_submit_data(struct sdio_dw *sd, struct rt_mmcsd_data *data) +{ + rt_ubase_t level; + rt_uint32_t temp; + + data->err = -RT_ERROR; + sd->data = data; + sd->last_buf = data->buf; + sd->last_remain = data->blks * data->blksize; + + if ((data->flags & DATA_DIR_READ)) + { + sd->dir_status = SDIO_DW_RECV_STATUS; + } + else + { + sd->dir_status = SDIO_DW_SEND_STATUS; + } + + sdio_dw_ctrl_thld(sd, data); + + if (sdio_dw_submit_data_dma(sd, data)) + { + sd->part_buf_start = 0; + sd->part_buf_count = 0; + + sdio_dw_writel(sd, RINTSTS, PINT(TXDR) | PINT(RXDR)); + + level = rt_spin_lock_irqsave(&sd->irq_lock); + temp = sdio_dw_readl(sd, INTMASK); + temp |= PINT(TXDR) | PINT(RXDR); + sdio_dw_writel(sd, INTMASK, temp); + rt_spin_unlock_irqrestore(&sd->irq_lock, level); + + temp = sdio_dw_readl(sd, CTRL); + temp &= ~SDIO_DW_CTRL_DMA_ENABLE; + sdio_dw_writel(sd, CTRL, temp); + + /* + * Use the initial fifoth_val for PIO mode. If wm_algined is set, we set + * watermark same as data size. If next issued data may be transfered by + * DMA mode, prev_blksz should be invalidated. + */ + if (sd->wm_aligned) + { + sdio_dw_adjust_fifoth(sd, data); + } + else + { + sdio_dw_writel(sd, FIFOTH, sd->fifoth_val); + } + sd->prev_blksz = 0; + } + else + { + /* + * Keep the current block size. + * It will be used to decide whether to update fifoth register next time. + */ + sd->prev_blksz = data->blksize; + } +} + +static void sdio_dw_setup_bus(struct sdio_dw_slot *slot, rt_bool_t force_clkinit) +{ + struct sdio_dw *sd = slot->sd; + rt_uint32_t clock = slot->clock; + rt_uint32_t cmd_bits = SDIO_DW_CMD_UPD_CLK | SDIO_DW_CMD_PRV_DAT_WAIT; + + /* We must continue to set bit 28 in CMD until the change is complete */ + if (sd->state == STATE_WAITING_CMD11_DONE) + { + cmd_bits |= SDIO_DW_CMD_VOLT_SWITCH; + } + + if (!clock) + { + sdio_dw_writel(sd, CLKENA, 0); + sdio_dw_send_cmd(slot, cmd_bits, 0); + } + else if (clock != sd->current_speed || force_clkinit) + { + rt_uint32_t clk_en_a, div = sd->bus_hz / clock; + + if (sd->bus_hz % clock && sd->bus_hz > clock) + { + /* Move the + 1 after the divide to prevent over-clocking the card */ + div += 1; + } + + div = (sd->bus_hz != clock) ? RT_DIV_ROUND_UP(div, 2) : 0; + + if (clock != slot->clk_old && + !bitmap_test_bit(&slot->flags, DW_MMC_CARD_NEEDS_POLL) && + !force_clkinit) + { + LOG_D("Bus speed (slot %d) = %uHz (slot req %uHz, actual %uHZ div = %d)", + slot->id, sd->bus_hz, clock, + div ? ((sd->bus_hz / div) >> 1) : sd->bus_hz, div); + } + + /* Disable clock */ + sdio_dw_writel(sd, CLKENA, 0); + sdio_dw_writel(sd, CLKSRC, 0); + + /* Inform CIU */ + sdio_dw_send_cmd(slot, cmd_bits, 0); + + /* Set clock to desired speed */ + sdio_dw_writel(sd, CLKDIV, div); + + /* Inform CIU */ + sdio_dw_send_cmd(slot, cmd_bits, 0); + + /* Enable clock; only low power if no SDIO */ + clk_en_a = SDIO_DW_CLKEN_ENABLE << slot->id; + if (!bitmap_test_bit(&slot->flags, DW_MMC_CARD_NO_LOW_PWR)) + { + clk_en_a |= SDIO_DW_CLKEN_LOW_PWR << slot->id; + } + sdio_dw_writel(sd, CLKENA, clk_en_a); + + /* Inform CIU */ + sdio_dw_send_cmd(slot, cmd_bits, 0); + + /* Keep the last clock value that was requested from core */ + slot->clk_old = clock; + } + + sd->current_speed = clock; + + /* Set the current slot bus width */ + sdio_dw_writel(sd, CTYPE, (slot->ctype << slot->id)); +} + +static void sdio_dw_set_data_timeout(struct sdio_dw *sd, rt_uint32_t timeout_ns) +{ + rt_uint64_t tmp; + rt_uint32_t clk_div, tmout; + const struct sdio_dw_drv_data *drv_data = sd->drv_data; + + if (drv_data && drv_data->set_data_timeout) + { + drv_data->set_data_timeout(sd, timeout_ns); + + return; + } + + clk_div = (sdio_dw_readl(sd, CLKDIV) & 0xff) * 2; + + if (clk_div == 0) + { + clk_div = 1; + } + + tmp = RT_DIV_ROUND_UP_ULL((rt_uint64_t)timeout_ns * sd->bus_hz, NSEC_PER_SEC); + tmp = RT_DIV_ROUND_UP_ULL(tmp, clk_div); + + /* TMOUT[7:0] (RESPONSE_TIMEOUT): Set maximum */ + tmout = 0xff; + + /* TMOUT[31:8] (DATA_TIMEOUT) */ + if (!tmp || tmp > 0xffffff) + { + tmout |= (0xffffff << 8); + } + else + { + tmout |= (tmp & 0xffffff) << 8; + } + + sdio_dw_writel(sd, TMOUT, tmout); +} + +/* Push final bytes to part_buf, only use during push */ +static void sdio_dw_set_part_bytes(struct sdio_dw *sd, void *buf, int cnt) +{ + rt_memcpy((void *)&sd->part_buf, buf, cnt); + sd->part_buf_count = cnt; +} + +/* Append bytes to part_buf, only use during push */ +static int sdio_dw_push_part_bytes(struct sdio_dw *sd, void *buf, int cnt) +{ + cnt = rt_min(cnt, (1 << sd->data_shift) - sd->part_buf_count); + rt_memcpy((void *)&sd->part_buf + sd->part_buf_count, buf, cnt); + sd->part_buf_count += cnt; + + return cnt; +} + +/* Pull first bytes from part_buf, only use during pull */ +static int sdio_dw_pull_part_bytes(struct sdio_dw *sd, void *buf, int cnt) +{ + cnt = rt_min_t(int, cnt, sd->part_buf_count); + + if (cnt) + { + rt_memcpy(buf, (void *)&sd->part_buf + sd->part_buf_start, cnt); + sd->part_buf_count -= cnt; + sd->part_buf_start += cnt; + } + + return cnt; +} + +/* Pull final bytes from the part_buf, assuming it's just been filled */ +static void sdio_dw_pull_final_bytes(struct sdio_dw *sd, void *buf, int cnt) +{ + rt_memcpy(buf, &sd->part_buf, cnt); + sd->part_buf_start = cnt; + sd->part_buf_count = (1 << sd->data_shift) - cnt; +} + +static void sdio_dw_push_data16(struct sdio_dw *sd, void *buf, int cnt) +{ + struct rt_mmcsd_data *data = sd->data; + int init_cnt = cnt; + + /* Try and push anything in the part_buf */ + if ((sd->part_buf_count)) + { + int len = sdio_dw_push_part_bytes(sd, buf, cnt); + + buf += len; + cnt -= len; + + if (sd->part_buf_count == 2) + { + sdio_dw_fifo_writew(sd, sd->part_buf16); + sd->part_buf_count = 0; + } + } +#ifndef ARCH_HAVE_EFFICIENT_UNALIGNED_ACCESS + if (((rt_ubase_t)buf & 0x1)) + { + while (cnt >= 2) + { + rt_uint16_t aligned_buf[64]; + int len = rt_min(cnt & -2, (int)sizeof(aligned_buf)); + int items = len >> 1; + + /* rt_memcpy from input buffer into aligned buffer */ + rt_memcpy(aligned_buf, buf, len); + buf += len; + cnt -= len; + + /* Push data from aligned buffer into fifo */ + for (int i = 0; i < items; ++i) + { + sdio_dw_fifo_writew(sd, aligned_buf[i]); + } + } + } + else +#endif /* !ARCH_HAVE_EFFICIENT_UNALIGNED_ACCESS */ + { + rt_uint16_t *pdata = buf; + + for (; cnt >= 2; cnt -= 2) + { + sdio_dw_fifo_writew(sd, *pdata++); + } + buf = pdata; + } + /* Put anything remaining in the part_buf */ + if (cnt) + { + sdio_dw_set_part_bytes(sd, buf, cnt); + + /* Push data if we have reached the expected data length */ + if ((data->bytes_xfered + init_cnt) == (data->blksize * data->blks)) + { + sdio_dw_fifo_writew(sd, sd->part_buf16); + } + } +} + +static void sdio_dw_pull_data16(struct sdio_dw *sd, void *buf, int cnt) +{ +#ifndef ARCH_HAVE_EFFICIENT_UNALIGNED_ACCESS + if (((rt_ubase_t)buf & 0x1)) + { + while (cnt >= 2) + { + /* Pull data from fifo into aligned buffer */ + rt_uint16_t aligned_buf[64]; + int len = rt_min(cnt & -2, (int)sizeof(aligned_buf)); + int items = len >> 1; + + for (int i = 0; i < items; ++i) + { + aligned_buf[i] = sdio_dw_fifo_readw(sd); + } + + /* rt_memcpy from aligned buffer into output buffer */ + rt_memcpy(buf, aligned_buf, len); + buf += len; + cnt -= len; + } + } + else +#endif /* !ARCH_HAVE_EFFICIENT_UNALIGNED_ACCESS */ + { + rt_uint16_t *pdata = buf; + + for (; cnt >= 2; cnt -= 2) + { + *pdata++ = sdio_dw_fifo_readw(sd); + } + buf = pdata; + } + if (cnt) + { + sd->part_buf16 = sdio_dw_fifo_readw(sd); + sdio_dw_pull_final_bytes(sd, buf, cnt); + } +} + +static void sdio_dw_push_data32(struct sdio_dw *sd, void *buf, int cnt) +{ + struct rt_mmcsd_data *data = sd->data; + int init_cnt = cnt; + + /* Try and push anything in the part_buf */ + if ((sd->part_buf_count)) + { + int len = sdio_dw_push_part_bytes(sd, buf, cnt); + + buf += len; + cnt -= len; + + if (sd->part_buf_count == 4) + { + sdio_dw_fifo_writel(sd, sd->part_buf32); + sd->part_buf_count = 0; + } + } +#ifndef ARCH_HAVE_EFFICIENT_UNALIGNED_ACCESS + if (((rt_ubase_t)buf & 0x3)) + { + while (cnt >= 4) + { + rt_uint32_t aligned_buf[32]; + int len = rt_min(cnt & -4, (int)sizeof(aligned_buf)); + int items = len >> 2; + + /* rt_memcpy from input buffer into aligned buffer */ + rt_memcpy(aligned_buf, buf, len); + buf += len; + cnt -= len; + + /* Push data from aligned buffer into fifo */ + for (int i = 0; i < items; ++i) + { + sdio_dw_fifo_writel(sd, aligned_buf[i]); + } + } + } + else +#endif /* !ARCH_HAVE_EFFICIENT_UNALIGNED_ACCESS */ + { + rt_uint32_t *pdata = buf; + + for (; cnt >= 4; cnt -= 4) + { + sdio_dw_fifo_writel(sd, *pdata++); + } + buf = pdata; + } + /* Put anything remaining in the part_buf */ + if (cnt) + { + sdio_dw_set_part_bytes(sd, buf, cnt); + + /* Push data if we have reached the expected data length */ + if ((data->bytes_xfered + init_cnt) == (data->blksize * data->blks)) + { + sdio_dw_fifo_writel(sd, sd->part_buf32); + } + } +} + +static void sdio_dw_pull_data32(struct sdio_dw *sd, void *buf, int cnt) +{ +#ifndef ARCH_HAVE_EFFICIENT_UNALIGNED_ACCESS + if (((rt_ubase_t)buf & 0x3)) + { + while (cnt >= 4) + { + /* Pull data from fifo into aligned buffer */ + rt_uint32_t aligned_buf[32]; + int len = rt_min(cnt & -4, (int)sizeof(aligned_buf)); + int items = len >> 2; + + for (int i = 0; i < items; ++i) + { + aligned_buf[i] = sdio_dw_fifo_readl(sd); + } + + /* rt_memcpy from aligned buffer into output buffer */ + rt_memcpy(buf, aligned_buf, len); + buf += len; + cnt -= len; + } + } + else +#endif /* !ARCH_HAVE_EFFICIENT_UNALIGNED_ACCESS */ + { + rt_uint32_t *pdata = buf; + + for (; cnt >= 4; cnt -= 4) + { + *pdata++ = sdio_dw_fifo_readl(sd); + } + buf = pdata; + } + if (cnt) + { + sd->part_buf32 = sdio_dw_fifo_readl(sd); + sdio_dw_pull_final_bytes(sd, buf, cnt); + } +} + +static void sdio_dw_push_data64(struct sdio_dw *sd, void *buf, int cnt) +{ + struct rt_mmcsd_data *data = sd->data; + int init_cnt = cnt; + + /* Try and push anything in the part_buf */ + if ((sd->part_buf_count)) + { + int len = sdio_dw_push_part_bytes(sd, buf, cnt); + + buf += len; + cnt -= len; + + if (sd->part_buf_count == 8) + { + sdio_dw_fifo_writeq(sd, sd->part_buf64); + sd->part_buf_count = 0; + } + } +#ifndef ARCH_HAVE_EFFICIENT_UNALIGNED_ACCESS + if (((rt_ubase_t)buf & 0x7)) + { + while (cnt >= 8) + { + rt_uint64_t aligned_buf[16]; + int len = rt_min(cnt & -8, (int)sizeof(aligned_buf)); + int items = len >> 3; + + /* rt_memcpy from input buffer into aligned buffer */ + rt_memcpy(aligned_buf, buf, len); + buf += len; + cnt -= len; + + /* Push data from aligned buffer into fifo */ + for (int i = 0; i < items; ++i) + { + sdio_dw_fifo_writeq(sd, aligned_buf[i]); + } + } + } + else +#endif /* !ARCH_HAVE_EFFICIENT_UNALIGNED_ACCESS */ + { + rt_uint64_t *pdata = buf; + + for (; cnt >= 8; cnt -= 8) + { + sdio_dw_fifo_writeq(sd, *pdata++); + } + buf = pdata; + } + /* Put anything remaining in the part_buf */ + if (cnt) + { + sdio_dw_set_part_bytes(sd, buf, cnt); + + /* Push data if we have reached the expected data length */ + if ((data->bytes_xfered + init_cnt) == (data->blksize * data->blks)) + { + sdio_dw_fifo_writeq(sd, sd->part_buf64); + } + } +} + +static void sdio_dw_pull_data64(struct sdio_dw *sd, void *buf, int cnt) +{ +#ifndef ARCH_HAVE_EFFICIENT_UNALIGNED_ACCESS + if (((rt_ubase_t)buf & 0x7)) + { + while (cnt >= 8) + { + /* Pull data from fifo into aligned buffer */ + rt_uint64_t aligned_buf[16]; + int len = rt_min(cnt & -8, (int)sizeof(aligned_buf)); + int items = len >> 3; + + for (int i = 0; i < items; ++i) + { + aligned_buf[i] = sdio_dw_fifo_readq(sd); + } + + /* rt_memcpy from aligned buffer into output buffer */ + rt_memcpy(buf, aligned_buf, len); + buf += len; + cnt -= len; + } + } + else +#endif /* !ARCH_HAVE_EFFICIENT_UNALIGNED_ACCESS */ + { + rt_uint64_t *pdata = buf; + + for (; cnt >= 8; cnt -= 8) + { + *pdata++ = sdio_dw_fifo_readq(sd); + } + buf = pdata; + } + if (cnt) + { + sd->part_buf64 = sdio_dw_fifo_readq(sd); + sdio_dw_pull_final_bytes(sd, buf, cnt); + } +} + +static void sdio_dw_pull_data(struct sdio_dw *sd, void *buf, int cnt) +{ + /* Get remaining partial bytes */ + int len = sdio_dw_pull_part_bytes(sd, buf, cnt); + + if (len != cnt) + { + buf += len; + cnt -= len; + + /* Get the rest of the data */ + sd->pull_data(sd, buf, cnt); + } +} + +static void sdio_dw_read_data_pio(struct sdio_dw *sd, rt_bool_t dto) +{ + void *buf; + int shift = sd->data_shift; + struct rt_mmcsd_data *data = sd->data; + rt_uint32_t status, remain, fcnt, len; + + buf = sd->last_buf; + remain = sd->last_remain; + + do { + if (!remain) + { + break; + } + + do { + fcnt = (SDIO_DW_GET_FCNT(sdio_dw_readl(sd, STATUS)) << shift) + sd->part_buf_count; + len = rt_min(remain, fcnt); + + if (!len) + { + break; + } + + sdio_dw_pull_data(sd, buf, len); + data->bytes_xfered += len; + buf += len; + remain -= len; + } while (remain); + + status = sdio_dw_readl(sd, MINTSTS); + sdio_dw_writel(sd, RINTSTS, PINT(RXDR)); + /* If the RXDR is ready read again */ + } while ((status & PINT(RXDR)) || (dto && SDIO_DW_GET_FCNT(sdio_dw_readl(sd, STATUS)))); + + sd->last_buf = remain ? buf : RT_NULL; + sd->last_remain = remain; + + rt_hw_wmb(); + bitmap_set_bit(&sd->pending_events, EVENT_XFER_COMPLETE); +} + +static void sdio_dw_write_data_pio(struct sdio_dw *sd) +{ + void *buf; + int shift = sd->data_shift; + struct rt_mmcsd_data *data = sd->data; + rt_uint32_t status, remain, fcnt, len, fifo_depth; + + buf = sd->last_buf; + remain = sd->last_remain; + fifo_depth = sd->fifo_depth; + + do { + if (!remain) + { + break; + } + + do { + fcnt = ((fifo_depth - SDIO_DW_GET_FCNT(sdio_dw_readl(sd, STATUS))) << shift) - sd->part_buf_count; + len = rt_min(remain, fcnt); + + if (!len) + { + break; + } + + sd->push_data(sd, buf, len); + data->bytes_xfered += len; + buf += len; + remain -= len; + } while (remain); + + status = sdio_dw_readl(sd, MINTSTS); + sdio_dw_writel(sd, RINTSTS, PINT(TXDR)); + /* If TXDR write again */ + } while ((status & PINT(TXDR))); + + sd->last_buf = remain ? buf : RT_NULL; + sd->last_remain = remain; + + rt_hw_wmb(); + bitmap_set_bit(&sd->pending_events, EVENT_XFER_COMPLETE); +} + +static void sdio_dw_init_dma(struct sdio_dw *sd) +{ + int addr_config; + + /* + * Check tansfer mode from HCON[17:16] + * Clear the ambiguous description of dw_mmc databook: + * 2b'00: No DMA Interface -> Actually means using Internal DMA block + * 2b'01: DesignWare DMA Interface -> Synopsys DW-DMA block + * 2b'10: Generic DMA Interface -> non-Synopsys generic DMA block + * 2b'11: Non DW DMA Interface -> pio only + * Compared to DesignWare DMA Interface, Generic DMA Interface has a simpler + * request/acknowledge handshake mechanism and both of them are regarded as + * external dma master for dw_mmc. + */ + sd->use_dma = SDIO_DW_GET_TRANS_MODE(sdio_dw_readl(sd, HCON)); + + if (sd->use_dma == DMA_INTERFACE_IDMA) + { + sd->use_dma = TRANS_MODE_IDMAC; + } + else if (sd->use_dma == DMA_INTERFACE_DWDMA || sd->use_dma == DMA_INTERFACE_GDMA) + { + sd->use_dma = TRANS_MODE_EDMAC; + } + else + { + goto _no_dma; + } + + /* Determine which DMA interface to use */ + if (sd->use_dma == TRANS_MODE_IDMAC) + { + /* Check ADDR_CONFIG bit in HCON to find IDMAC address bus width */ + addr_config = SDIO_DW_GET_ADDR_CONFIG(sdio_dw_readl(sd, HCON)); + + /* Supports IDMAC in 64/32-bit address mode */ + sd->dma_64bit_address = (addr_config == 1); + LOG_D("IDMAC supports %s-bit address mode", sd->dma_64bit_address ? "64" : "32"); + + /* Alloc memory for translation */ + sd->dma_buf_cache = rt_pages_alloc(rt_page_bits(DESC_RING_BUF_SZ)); + + if (!sd->dma_buf_cache) + { + LOG_E("Could not alloc DMA memory witch cache"); + + goto _no_dma; + } + + sd->dma_buf_phy = (rt_ubase_t)rt_kmem_v2p(sd->dma_buf_cache); + sd->dma_buf = rt_ioremap_nocache((void *)sd->dma_buf_phy, DESC_RING_BUF_SZ); + + if (!sd->dma_buf) + { + rt_pages_free(sd->dma_buf_cache, rt_page_bits(DESC_RING_BUF_SZ)); + sd->dma_buf_cache = RT_NULL; + + LOG_E("Could not alloc DMA memory"); + + goto _no_dma; + } + + sd->dma_ops = &sdio_dw_idmac_ops; + LOG_D("Using internal DMA controller"); + } + else + { + if (!rt_dm_dev_prop_read_bool(&sd->parent, "dma-names") || + !rt_dm_dev_prop_read_bool(&sd->parent, "dmas")) + { + goto _no_dma; + } + + sd->dma_ops = &sdio_dw_edmac_ops; + LOG_D("Using external DMA controller"); + } + + if (sd->dma_ops->init && sd->dma_ops->start && sd->dma_ops->stop && sd->dma_ops->cleanup) + { + if (sd->dma_ops->init(sd)) + { + LOG_E("Unable to initialize DMA Controller"); + goto _no_dma; + } + } + else + { + LOG_E("DMA initialization not found"); + goto _no_dma; + } + + return; + +_no_dma: + LOG_D("Using PIO mode"); + sd->use_dma = TRANS_MODE_PIO; +} + +static rt_bool_t sdio_dw_reset(struct sdio_dw *sd) +{ + rt_err_t res = RT_FALSE; + rt_uint32_t flags = SDIO_DW_CTRL_RESET | SDIO_DW_CTRL_FIFO_RESET; + + if (sd->use_dma) + { + flags |= SDIO_DW_CTRL_DMA_RESET; + } + + if (sdio_dw_ctrl_reset(sd, flags)) + { + int timeout = 500 * USEC_PER_MSEC; + /* In all cases we clear the RAWINTS register to clear any interrupts */ + sdio_dw_writel(sd, RINTSTS, 0xffffffff); + + if (!sd->use_dma) + { + res = RT_TRUE; + goto _ciu_out; + } + + /* Wait for dma_req to be cleared */ + while ((sdio_dw_readl(sd, STATUS) & SDIO_DW_STATUS_DMA_REQ) && timeout--) + { + rt_hw_cpu_relax(); + } + + if (time <= 0) + { + LOG_E("Timeout waiting for dma_req to be cleared in reset"); + goto _ciu_out; + } + + /* when using DMA next we reset the fifo again */ + if (!sdio_dw_ctrl_reset(sd, SDIO_DW_CTRL_FIFO_RESET)) + { + goto _ciu_out; + } + } + else + { + /* If the controller reset bit did clear, then set clock regs */ + if (!(sdio_dw_readl(sd, CTRL) & SDIO_DW_CTRL_RESET)) + { + LOG_E("FIFO/DMA reset bits didn't clear but ciu was reset, doing clock update"); + + goto _ciu_out; + } + } + + if (sd->use_dma == TRANS_MODE_IDMAC) + { + /* It is also required that we reinit idmac */ + sdio_dw_idmac_init(sd); + } + + res = RT_TRUE; + +_ciu_out: + /* After a CTRL reset we need to have CIU set clock registers */ + sdio_dw_send_cmd(sd->slot, SDIO_DW_CMD_UPD_CLK, 0); + + return res; +} + +static void sdio_dw_start_request(struct sdio_dw *sd, struct rt_mmcsd_cmd *cmd) +{ + rt_uint32_t cmd_flags; + struct sdio_dw_slot *slot = sd->slot; + struct rt_mmcsd_data *data = cmd->data; + + if (sd->state == STATE_WAITING_CMD11_DONE) + { + sd->state = STATE_IDLE; + } + + if (sd->state == STATE_IDLE) + { + sd->state = STATE_SENDING_CMD; + } + + sd->req = sd->slot->req; + sd->cmd = cmd; + + sd->pending_events = 0; + sd->cmd_status = 0; + sd->data_status = 0; + sd->dir_status = 0; + + if (data) + { + sdio_dw_set_data_timeout(sd, data->timeout_ns); + sdio_dw_writel(sd, BYTCNT, data->blksize * data->blks); + sdio_dw_writel(sd, BLKSIZ, data->blksize); + } + + cmd_flags = cmd->cmd_code; + + if (cmd->cmd_code == STOP_TRANSMISSION || + cmd->cmd_code == GO_IDLE_STATE || + cmd->cmd_code == GO_INACTIVE_STATE || + (cmd->cmd_code == SD_IO_RW_DIRECT && ((cmd->arg >> 9) & 0x1ffff) == SDIO_REG_CCCR_IO_ABORT)) + { + cmd_flags |= SDIO_DW_CMD_STOP; + } + else if (cmd->cmd_code != SEND_STATUS && data) + { + cmd_flags |= SDIO_DW_CMD_PRV_DAT_WAIT; + } + + if (cmd->cmd_code == SD_SWITCH_VOLTAGE) + { + rt_uint32_t clk_en_a; + + /* Special bit makes CMD11 not die */ + cmd_flags |= SDIO_DW_CMD_VOLT_SWITCH; + + /* Change state to continue to handle CMD11 weirdness */ + sd->state = STATE_SENDING_CMD11; + + /* + * We need to disable low power mode (automatic clock stop) while + * doing voltage switch so we don't confuse the card, since stopping + * the clock is a specific part of the UHS voltage change dance. + * + * Note that low power mode (SDIO_DW_CLKEN_LOW_PWR) will be + * unconditionally turned back on in dw_mci_setup_bus() if it's ever + * called with a non-zero clock. That shouldn't happen until the + * voltage change is all done. + */ + clk_en_a = sdio_dw_readl(sd, CLKENA); + clk_en_a &= ~(SDIO_DW_CLKEN_LOW_PWR << slot->id); + sdio_dw_writel(sd, CLKENA, clk_en_a); + sdio_dw_send_cmd(sd->slot, SDIO_DW_CMD_UPD_CLK | SDIO_DW_CMD_PRV_DAT_WAIT, 0); + } + + switch (resp_type(cmd)) + { + case RESP_NONE: + break; + + case RESP_R1: + case RESP_R5: + case RESP_R6: + case RESP_R7: + case RESP_R1B: + cmd_flags |= SDIO_DW_CMD_RESP_EXP; + cmd_flags |= SDIO_DW_CMD_RESP_CRC; + break; + + case RESP_R2: + cmd_flags |= SDIO_DW_CMD_RESP_EXP; + cmd_flags |= SDIO_DW_CMD_RESP_CRC; + cmd_flags |= SDIO_DW_CMD_RESP_LONG; + break; + + case RESP_R3: + case RESP_R4: + cmd_flags |= SDIO_DW_CMD_RESP_EXP; + break; + + default: + LOG_D("Unsupported cmd type = %x", resp_type(cmd)); + break; + } + + if (data) + { + cmd_flags |= SDIO_DW_CMD_DAT_EXP; + + if ((data->flags & DATA_DIR_WRITE)) + { + cmd_flags |= SDIO_DW_CMD_DAT_WR; + } + } + + if (!bitmap_test_bit(&slot->flags, DW_MMC_CARD_NO_USE_HOLD)) + { + cmd_flags |= SDIO_DW_CMD_USE_HOLD_REG; + } + + if (bitmap_test_bit(&slot->flags, DW_MMC_CARD_NEED_INIT)) + { + cmd_flags |= SDIO_DW_CMD_INIT; + bitmap_clear_bit(&slot->flags, DW_MMC_CARD_NEED_INIT); + } + + if (data) + { + sdio_dw_submit_data(sd, data); + /* Drain writebuffer */ + rt_hw_wmb(); + } + + sdio_dw_start_cmd(slot, cmd_flags, cmd->arg); + + if (cmd->cmd_code == SD_SWITCH_VOLTAGE) + { + rt_ubase_t level = rt_spin_lock_irqsave(&sd->irq_lock); + + if (!bitmap_test_bit(&sd->pending_events, EVENT_CMD_COMPLETE)) + { + rt_tick_t tick = rt_tick_from_millisecond(500) + 1; + + rt_timer_control(&sd->cmd11_timer, RT_TIMER_CTRL_SET_TIME, &tick); + + rt_timer_start(&sd->cmd11_timer); + } + + rt_spin_unlock_irqrestore(&sd->irq_lock, level); + } + + sd->stop_cmdr = sdio_dw_prep_stop_abort(sd, cmd); +} + +static void sdio_dw_end_request(struct sdio_dw *sd) +{ + sd->slot->req = RT_NULL; + sd->req = RT_NULL; + + if (sd->state == STATE_SENDING_CMD11) + { + sd->state = STATE_WAITING_CMD11_DONE; + } + else + { + sd->state = STATE_IDLE; + } + + rt_hw_spin_unlock(&sd->lock.lock); + + mmcsd_req_complete(sd->slot->host); + + rt_hw_spin_lock(&sd->lock.lock); +} + +static rt_err_t sdio_dw_cmd_complete(struct sdio_dw *sd, struct rt_mmcsd_cmd *cmd) +{ + rt_uint32_t status = sd->cmd_status; + + sd->cmd_status = 0; + + /* Read the response from the card (up to 16 bytes) */ + if (resp_type(cmd) == RESP_R2) + { + cmd->resp[0] = sdio_dw_readl(sd, RESP3); + cmd->resp[1] = sdio_dw_readl(sd, RESP2); + cmd->resp[2] = sdio_dw_readl(sd, RESP1); + cmd->resp[3] = sdio_dw_readl(sd, RESP0); + } + else + { + cmd->resp[0] = sdio_dw_readl(sd, RESP0); + } + + if ((status & PINT(RTO))) + { + cmd->err = -RT_ETIMEOUT; + } + else if ((resp_type(cmd) & (RESP_R1 | RESP_R5 | RESP_R6 | RESP_R7 | RESP_R1B)) && + (status & PINT(RCRC))) + { + cmd->err = -RT_EIO; + } + else if ((status & PINT(RESP_ERR))) + { + cmd->err = -RT_EIO; + } + else + { + cmd->err = RT_EOK; + } + + return cmd->err; +} + +static int sdio_dw_data_complete(struct sdio_dw *sd, struct rt_mmcsd_data *data) +{ + rt_uint32_t status = sd->data_status; + + if (status & SDIO_DW_DATA_ERROR_FLAGS) + { + if (status & PINT(DRTO)) + { + data->err = -RT_ETIMEOUT; + } + else if (status & PINT(DCRC)) + { + data->err = -RT_EIO; + } + else if (status & PINT(EBE)) + { + if (sd->dir_status == SDIO_DW_SEND_STATUS) + { + /* + * No data CRC status was returned. The number of bytes + * transferred will be exaggerated in PIO mode. + */ + data->bytes_xfered = 0; + data->err = -RT_ETIMEOUT; + } + else if (sd->dir_status == SDIO_DW_RECV_STATUS) + { + data->err = -RT_EIO; + } + } + else + { + /* PINT(SBE) is included */ + data->err = -RT_EIO; + } + + LOG_D("Data error, status 0x%x", status); + + /* After an error, there may be data lingering in the FIFO */ + sdio_dw_reset(sd); + } + else + { + data->bytes_xfered = data->blks * data->blksize; + data->err = RT_EOK; + } + + return data->err; +} + +static void sdio_dw_mmc_request(struct rt_mmcsd_host *host, struct rt_mmcsd_req *req) +{ + struct sdio_dw_slot *slot = host->private_data; + struct sdio_dw *sd = slot->sd; + + /* + * The check for card presence and queueing of the request must be atomic, + * otherwise the card could be removed in between and the request wouldn't + * fail until another card was inserted. + */ + if (!sdio_dw_get_cd(slot)) + { + req->cmd->err = -RT_EIO; + mmcsd_req_complete(host); + + return; + } + + rt_hw_spin_lock(&sd->lock.lock); + + sd->slot->req = req; + sdio_dw_start_request(sd, req->sbc ? : req->cmd); + + rt_hw_spin_unlock(&sd->lock.lock); +} + +static void sdio_dw_mmc_set_iocfg(struct rt_mmcsd_host *host, struct rt_mmcsd_io_cfg *ios) +{ + rt_err_t err; + rt_uint32_t regs; + struct sdio_dw_slot *slot = host->private_data; + struct sdio_dw *sd = slot->sd; + const struct sdio_dw_drv_data *drv_data = sd->drv_data; + + /* Bus */ + switch (ios->bus_width) + { + case MMCSD_BUS_WIDTH_4: + slot->ctype = SDIO_DW_CTYPE_4BIT; + break; + + case MMCSD_BUS_WIDTH_8: + slot->ctype = SDIO_DW_CTYPE_8BIT; + break; + + default: + slot->ctype = SDIO_DW_CTYPE_1BIT; + break; + } + + regs = sdio_dw_readl(sd, UHS_REG); + + /* DDR mode set */ + if (ios->timing == MMCSD_TIMING_MMC_DDR52 || + ios->timing == MMCSD_TIMING_UHS_DDR50 || + ios->timing == MMCSD_TIMING_MMC_HS400) + { + regs |= ((0x1 << slot->id) << 16); + } + else + { + regs &= ~((0x1 << slot->id) << 16); + } + + sdio_dw_writel(sd, UHS_REG, regs); + sd->timing = ios->timing; + + /* + * Use mirror of ios->clock to prevent race with mmc core ios update when + * finding the minimum. + */ + slot->clock = ios->clock; + + if (drv_data && drv_data->set_iocfg) + { + drv_data->set_iocfg(sd, ios); + } + + /* Power */ + switch (ios->power_mode) + { + case MMCSD_POWER_UP: + if (host->supply.vmmc) + { + err = sdio_regulator_set_ocr(host, host->supply.vmmc, ios->vdd); + + if (err) + { + LOG_E("Failed to enable vmmc regulator error = %s", rt_strerror(err)); + + return; + } + } + bitmap_set_bit(&slot->flags, DW_MMC_CARD_NEED_INIT); + regs = sdio_dw_readl(sd, PWREN); + regs |= (1 << slot->id); + sdio_dw_writel(sd, PWREN, regs); + break; + + case MMCSD_POWER_ON: + if (!sd->vqmmc_enabled) + { + if (host->supply.vqmmc) + { + err = rt_regulator_enable(host->supply.vqmmc); + + if (err) + { + LOG_E("Failed to enable vqmmc error = %s", rt_strerror(err)); + } + else + { + sd->vqmmc_enabled = RT_TRUE; + } + } + else + { + sd->vqmmc_enabled = RT_TRUE; + } + + sdio_dw_ctrl_reset(sd, SDIO_DW_CTRL_ALL_RESET_FLAGS); + } + + /* Adjust clock / bus width after power is up */ + sdio_dw_setup_bus(slot, RT_FALSE); + break; + + case MMCSD_POWER_OFF: + /* Turn clock off before power goes down */ + sdio_dw_setup_bus(slot, RT_FALSE); + + if (host->supply.vmmc) + { + sdio_regulator_set_ocr(host, host->supply.vmmc, 0); + } + + if (host->supply.vqmmc && sd->vqmmc_enabled) + { + rt_regulator_disable(host->supply.vqmmc); + } + + sd->vqmmc_enabled = RT_FALSE; + + regs = sdio_dw_readl(sd, PWREN); + regs &= ~(1 << slot->id); + sdio_dw_writel(sd, PWREN, regs); + break; + + default: + LOG_E("Invalid power_mode value %x", ios->power_mode); + break; + } + + if (sd->state == STATE_WAITING_CMD11_DONE && ios->clock != 0) + { + sd->state = STATE_IDLE; + } +} + +static rt_int32_t sdio_dw_mmc_get_card_status(struct rt_mmcsd_host *host) +{ + return 0; +} + +static void sdio_dw_mmc_enable_sdio_irq(struct rt_mmcsd_host *host, rt_int32_t enable) +{ + rt_ubase_t level; + rt_uint32_t int_mask, clk_en_a_old, clk_en_a; + struct sdio_dw_slot *slot = host->private_data; + struct sdio_dw *sd = slot->sd; + const rt_uint32_t clken_low_pwr = SDIO_DW_CLKEN_LOW_PWR << slot->id; + + /* + * Low power mode will stop the card clock when idle. According to the + * description of the CLKENA register we should disable low power mode for + * SDIO cards if we need SDIO interrupts to work. + */ + clk_en_a_old = sdio_dw_readl(sd, CLKENA); + + if (enable) + { + bitmap_set_bit(&slot->flags, DW_MMC_CARD_NO_LOW_PWR); + clk_en_a = clk_en_a_old & ~clken_low_pwr; + } + else + { + bitmap_clear_bit(&slot->flags, DW_MMC_CARD_NO_LOW_PWR); + clk_en_a = clk_en_a_old | clken_low_pwr; + } + + if (clk_en_a != clk_en_a_old) + { + sdio_dw_writel(sd, CLKENA, clk_en_a); + sdio_dw_send_cmd(slot, SDIO_DW_CMD_UPD_CLK | SDIO_DW_CMD_PRV_DAT_WAIT, 0); + } + + level = rt_spin_lock_irqsave(&sd->irq_lock); + + /* Enable/disable Slot Specific SDIO interrupt */ + int_mask = sdio_dw_readl(sd, INTMASK); + if (enable) + { + int_mask |= SDIO_DW_INT_SDIO(slot->sdio_id); + } + else + { + int_mask &= ~SDIO_DW_INT_SDIO(slot->sdio_id); + } + sdio_dw_writel(sd, INTMASK, int_mask); + + rt_spin_unlock_irqrestore(&sd->irq_lock, level); +} + +static rt_int32_t sdio_dw_mmc_execute_tuning(struct rt_mmcsd_host *host, rt_int32_t opcode) +{ + struct sdio_dw_slot *slot = host->private_data; + struct sdio_dw *sd = slot->sd; + const struct sdio_dw_drv_data *drv_data = sd->drv_data; + + if (drv_data && drv_data->execute_tuning) + { + return drv_data->execute_tuning(slot, opcode); + } + + return -RT_EINVAL; +} + +static rt_bool_t sdio_dw_mmc_card_busy(struct rt_mmcsd_host *host) +{ + rt_uint32_t status; + struct sdio_dw_slot *slot = host->private_data; + + /* Check the busy bit which is low when DAT[3:0] (the data lines) are 0000 */ + status = sdio_dw_readl(slot->sd, STATUS); + + return !!(status & SDIO_DW_STATUS_BUSY); +} + +static rt_err_t sdio_dw_mmc_signal_voltage_switch(struct rt_mmcsd_host *host, + struct rt_mmcsd_io_cfg *ios) +{ + rt_uint32_t uhs, v18; + struct sdio_dw_slot *slot = host->private_data; + struct sdio_dw *sd = slot->sd; + const struct sdio_dw_drv_data *drv_data = sd->drv_data; + + v18 = SDIO_DW_UHS_18V << slot->id; + + if (drv_data && drv_data->switch_voltage) + { + return drv_data->switch_voltage(host, ios); + } + + /* + * Program the voltage. Note that some instances of dw_mmc may use + * the UHS_REG for this. For other instances (like exynos) the UHS_REG + * does no harm but you need to set the regulator directly. Try both. + */ + uhs = sdio_dw_readl(sd, UHS_REG); + if (ios->signal_voltage == MMCSD_SIGNAL_VOLTAGE_330) + { + uhs &= ~v18; + } + else + { + uhs |= v18; + } + + if (host->supply.vqmmc) + { + rt_err_t err = sdio_regulator_set_vqmmc(host, ios); + + if (err < 0) + { + LOG_D("Regulator set error %s to %s V", rt_strerror(err), + uhs & v18 ? "1.8" : "3.3"); + + return err; + } + } + sdio_dw_writel(sd, UHS_REG, uhs); + + return RT_EOK; +} + +static const struct rt_mmcsd_host_ops sdio_dw_mmc_ops = +{ + .request = sdio_dw_mmc_request, + .set_iocfg = sdio_dw_mmc_set_iocfg, + .get_card_status = sdio_dw_mmc_get_card_status, + .enable_sdio_irq = sdio_dw_mmc_enable_sdio_irq, + .execute_tuning = sdio_dw_mmc_execute_tuning, + .card_busy = sdio_dw_mmc_card_busy, + .signal_voltage_switch = sdio_dw_mmc_signal_voltage_switch, +}; + +static void sdio_dw_set_drto(struct sdio_dw *sd) +{ + rt_ubase_t level; + rt_uint32_t drto_clks, drto_div, drto_ms; + const struct sdio_dw_drv_data *drv_data = sd->drv_data; + + if (drv_data && drv_data->get_drto_clks) + { + drto_clks = drv_data->get_drto_clks(sd); + } + else + { + drto_clks = sdio_dw_readl(sd, TMOUT) >> 8; + } + + drto_div = (sdio_dw_readl(sd, CLKDIV) & 0xff) * 2; + + if (drto_div == 0) + { + drto_div = 1; + } + + drto_ms = RT_DIV_ROUND_UP_ULL((rt_uint64_t)MSEC_PER_SEC * drto_clks * drto_div, + sd->bus_hz); + + /* Add a bit spare time */ + drto_ms += 10; + + level = rt_spin_lock_irqsave(&sd->irq_lock); + + if (!bitmap_test_bit(&sd->pending_events, EVENT_DATA_COMPLETE)) + { + rt_tick_t tick = rt_tick_from_millisecond(drto_ms); + + rt_timer_control(&sd->dto_timer, RT_TIMER_CTRL_SET_TIME, &tick); + + rt_timer_start(&sd->dto_timer); + } + + rt_spin_unlock_irqrestore(&sd->irq_lock, level); +} + +static rt_bool_t sdio_dw_clear_pending_cmd_complete(struct sdio_dw *sd) +{ + if (!bitmap_test_bit(&sd->pending_events, EVENT_CMD_COMPLETE)) + { + return RT_FALSE; + } + + rt_timer_stop(&sd->cto_timer); + bitmap_clear_bit(&sd->pending_events, EVENT_CMD_COMPLETE); + + return RT_TRUE; +} + +static rt_bool_t sdio_dw_clear_pending_data_complete(struct sdio_dw *sd) +{ + if (!bitmap_test_bit(&sd->pending_events, EVENT_DATA_COMPLETE)) + { + return RT_FALSE; + } + + rt_timer_stop(&sd->dto_timer); + bitmap_clear_bit(&sd->pending_events, EVENT_DATA_COMPLETE); + + return RT_TRUE; +} + +static void sdio_dw_state_change(struct rt_work *work, void *work_data) +{ + rt_err_t err; + rt_uint32_t state, prev_state; + struct rt_mmcsd_cmd *cmd; + struct rt_mmcsd_req *req; + struct rt_mmcsd_data *data; + struct sdio_dw *sd = rt_container_of(work, struct sdio_dw, state_work); + + rt_hw_spin_lock(&sd->lock.lock); + + state = sd->state; + req = sd->req; + data = sd->data; + +_next_status: + prev_state = state; + + switch (state) + { + case STATE_IDLE: + case STATE_WAITING_CMD11_DONE: + break; + + case STATE_SENDING_CMD11: + case STATE_SENDING_CMD: + if (!sdio_dw_clear_pending_cmd_complete(sd)) + { + break; + } + + cmd = sd->cmd; + sd->cmd = RT_NULL; + err = sdio_dw_cmd_complete(sd, cmd); + if (cmd == req->sbc && !err) + { + sdio_dw_start_request(sd, req->cmd); + + goto _unlock; + } + + if (cmd->data && err) + { + if (err != -RT_ETIMEOUT && sd->dir_status == SDIO_DW_RECV_STATUS) + { + state = STATE_SENDING_DATA; + + goto _check_status; + } + + send_stop_abort(sd, data); + sdio_dw_stop_dma(sd); + state = STATE_SENDING_STOP; + break; + } + + if (!cmd->data || err) + { + sdio_dw_end_request(sd); + + goto _unlock; + } + + prev_state = state = STATE_SENDING_DATA; + + /* Fall through */ + case STATE_SENDING_DATA: + if (bitmap_test_bit(&sd->pending_events, EVENT_DATA_ERROR)) + { + bitmap_clear_bit(&sd->pending_events, EVENT_DATA_ERROR); + + if (!(sd->data_status & (PINT(DRTO) | PINT(EBE)))) + { + send_stop_abort(sd, data); + } + + sdio_dw_stop_dma(sd); + state = STATE_DATA_ERROR; + break; + } + + if (!bitmap_test_bit(&sd->pending_events, EVENT_XFER_COMPLETE)) + { + /* + * If all data-related interrupts don't come within the given time + * in reading data state. + */ + if (sd->dir_status == SDIO_DW_RECV_STATUS) + { + sdio_dw_set_drto(sd); + } + + break; + } + bitmap_clear_bit(&sd->pending_events, EVENT_XFER_COMPLETE); + + /* + * Handle an EVENT_DATA_ERROR that might have shown up before the + * transfer completed. This might not have been caught by the check + * above because the interrupt could have gone off between the previous + * check and the check for transfer complete. + * + * Technically this ought not be needed assuming we get a DATA_COMPLETE + * eventually (we'll notice the error and end the request), but it + * shouldn't hurt. + * + * This has the advantage of sending the stop command. + */ + if (bitmap_test_bit(&sd->pending_events, EVENT_DATA_ERROR)) + { + bitmap_clear_bit(&sd->pending_events, EVENT_DATA_ERROR); + + if (!(sd->data_status & (PINT(DRTO) | PINT(EBE)))) + { + send_stop_abort(sd, data); + } + + sdio_dw_stop_dma(sd); + state = STATE_DATA_ERROR; + break; + } + prev_state = state = STATE_DATA_BUSY; + + /* Fall through */ + case STATE_DATA_BUSY: + if (!sdio_dw_clear_pending_data_complete(sd)) + { + /* + * If data error interrupt comes but data over interrupt doesn't + * come within the given time. In reading data state. + */ + if (sd->dir_status == SDIO_DW_RECV_STATUS) + { + sdio_dw_set_drto(sd); + } + + break; + } + + sd->data = RT_NULL; + err = sdio_dw_data_complete(sd, data); + + if (!err) + { + if (!data->stop || req->sbc) + { + if (req->sbc && data->stop) + { + data->stop->err = RT_EOK; + } + + sdio_dw_end_request(sd); + + goto _unlock; + } + + /* Stop command for open-ended transfer */ + if (data->stop) + { + send_stop_abort(sd, data); + } + } + else + { + /* + * If we don't have a command complete now we'll never get one since + * we just reset everything; better end the request. + * + * If we do have a command complete we'll fall through to the + * STATE_SENDING_STOP command and everything will be peachy keen. + */ + if (!bitmap_test_bit(&sd->pending_events, EVENT_CMD_COMPLETE)) + { + sd->cmd = RT_NULL; + + sdio_dw_end_request(sd); + + goto _unlock; + } + } + + /* If err has non-zero, stop-abort command has been already issued. */ + prev_state = state = STATE_SENDING_STOP; + + /* Fall through */ + case STATE_SENDING_STOP: + if (!sdio_dw_clear_pending_cmd_complete(sd)) + { + break; + } + + /* CMD error in data command */ + if (req->cmd->err && req->data) + { + sdio_dw_reset(sd); + } + + sd->cmd = RT_NULL; + sd->data = RT_NULL; + + if (!req->sbc && req->stop) + { + sdio_dw_cmd_complete(sd, req->stop); + } + else + { + sd->cmd_status = 0; + } + + sdio_dw_end_request(sd); + goto _unlock; + + case STATE_DATA_ERROR: + if (!bitmap_test_bit(&sd->pending_events, EVENT_XFER_COMPLETE)) + { + break; + } + bitmap_clear_bit(&sd->pending_events, EVENT_XFER_COMPLETE); + + state = STATE_DATA_BUSY; + break; + + default: + break; + } + +_check_status: + if (state != prev_state) + { + goto _next_status; + } + + sd->state = state; +_unlock: + rt_hw_spin_unlock(&sd->lock.lock); +} + +static void sdio_dw_cmd11_timer(void *param) +{ + struct sdio_dw *sd = param; + + if (sd->state != STATE_SENDING_CMD11) + { + LOG_W("Unexpected CMD11 timeout"); + + return; + } + + sd->cmd_status = PINT(RTO); + bitmap_set_bit(&sd->pending_events, EVENT_CMD_COMPLETE); + rt_workqueue_urgent_work(sd->state_wq, &sd->state_work); +} + +static void sdio_dw_cto_timer(void *param) +{ + rt_ubase_t level; + rt_uint32_t pending; + struct sdio_dw *sd = param; + + level = rt_spin_lock_irqsave(&sd->irq_lock); + + /* + * If somehow we have very bad interrupt latency it's remotely possible that + * the timer could fire while the interrupt is still pending or while the + * interrupt is midway through running. Let's be paranoid and detect those + * two cases. Note that this is paranoia is somewhat justified because in + * this function we don't actually cancel the pending command in the + * controller, we just assume it will never come. + */ + /* Read-only mask reg */ + pending = sdio_dw_readl(sd, MINTSTS); + + if ((pending & (SDIO_DW_CMD_ERROR_FLAGS | PINT(CMD_DONE)))) + { + /* The interrupt should fire; no need to act but we can warn */ + LOG_W("Unexpected interrupt latency"); + + goto _unlock; + } + + if (bitmap_test_bit(&sd->pending_events, EVENT_CMD_COMPLETE)) + { + /* Presumably interrupt handler couldn't delete the timer */ + LOG_W("CTO timeout when already completed"); + + goto _unlock; + } + + /* + * Continued paranoia to make sure we're in the state we expect. + * This paranoia isn't really justified but it seems good to be safe. + */ + switch (sd->state) + { + case STATE_SENDING_CMD11: + case STATE_SENDING_CMD: + case STATE_SENDING_STOP: + /* + * If CMD_DONE interrupt does NOT come in sending command state, we + * should notify the driver to terminate current transfer and report a + * command timeout to the core. + */ + sd->cmd_status = PINT(RTO); + bitmap_set_bit(&sd->pending_events, EVENT_CMD_COMPLETE); + rt_workqueue_urgent_work(sd->state_wq, &sd->state_work); + break; + + default: + LOG_W("Unexpected command timeout, state %d", sd->state); + break; + } + +_unlock: + rt_spin_unlock_irqrestore(&sd->irq_lock, level); +} + +static void sdio_dw_dto_timer(void *param) +{ + rt_ubase_t level; + rt_uint32_t pending; + struct sdio_dw *sd = param; + + level = rt_spin_lock_irqsave(&sd->irq_lock); + + /* + * The DTO timer is much longer than the CTO timer, so it's even less likely + * that we'll these cases, but it pays to be paranoid. + */ + /* Read-only mask reg */ + pending = sdio_dw_readl(sd, MINTSTS); + + if ((pending & PINT(DATA_OVER))) + { + /* The interrupt should fire; no need to act but we can warn */ + LOG_W("Unexpected data interrupt latency"); + + goto _unlock; + } + + if (bitmap_test_bit(&sd->pending_events, EVENT_DATA_COMPLETE)) + { + /* Presumably interrupt handler couldn't delete the timer */ + LOG_W("DTO timeout when already completed"); + + goto _unlock; + } + + /* + * Continued paranoia to make sure we're in the state we expect. + * This paranoia isn't really justified but it seems good to be safe. + */ + switch (sd->state) + { + case STATE_SENDING_DATA: + case STATE_DATA_BUSY: + /* + * If DTO interrupt does NOT come in sending data state, we should + * notify the driver to terminate current transfer and report a data + * timeout to the core. + */ + sd->data_status = PINT(DRTO); + bitmap_set_bit(&sd->pending_events, EVENT_DATA_ERROR); + bitmap_set_bit(&sd->pending_events, EVENT_DATA_COMPLETE); + rt_workqueue_urgent_work(sd->state_wq, &sd->state_work); + break; + + default: + LOG_W("Unexpected data timeout, state %d", sd->state); + break; + } + +_unlock: + rt_spin_unlock_irqrestore(&sd->irq_lock, level); +} + +static void sdio_dw_cmd_interrupt(struct sdio_dw *sd, rt_uint32_t status) +{ + rt_timer_stop(&sd->cto_timer); + + if (!sd->cmd_status) + { + sd->cmd_status = status; + } + + /* Drain writebuffer */ + rt_hw_wmb(); + + bitmap_set_bit(&sd->pending_events, EVENT_CMD_COMPLETE); + rt_workqueue_urgent_work(sd->state_wq, &sd->state_work); +} + +static void sdio_dw_isr(int irqno, void *param) +{ + rt_uint32_t pending; + struct sdio_dw *sd = (struct sdio_dw *)param; + struct sdio_dw_slot *slot = sd->slot; + + /* Read-only mask reg */ + pending = sdio_dw_readl(sd, MINTSTS); + + if (pending) + { + if (sd->state == STATE_SENDING_CMD11 && (pending & PINT(VOLT_SWITCH))) + { + sdio_dw_writel(sd, RINTSTS, PINT(VOLT_SWITCH)); + pending &= ~PINT(VOLT_SWITCH); + + rt_hw_spin_lock(&sd->irq_lock.lock); + sdio_dw_cmd_interrupt(sd, pending); + rt_hw_spin_unlock(&sd->irq_lock.lock); + + rt_timer_stop(&sd->cmd11_timer); + } + + if ((pending & SDIO_DW_CMD_ERROR_FLAGS)) + { + rt_hw_spin_lock(&sd->irq_lock.lock); + + rt_timer_stop(&sd->cto_timer); + sdio_dw_writel(sd, RINTSTS, SDIO_DW_CMD_ERROR_FLAGS); + sd->cmd_status = pending; + rt_hw_wmb(); + bitmap_set_bit(&sd->pending_events, EVENT_CMD_COMPLETE); + + rt_hw_spin_unlock(&sd->irq_lock.lock); + } + + if ((pending & SDIO_DW_DATA_ERROR_FLAGS)) + { + rt_hw_spin_lock(&sd->irq_lock.lock); + + if ((sd->quirks & SDIO_DW_QUIRK_EXTENDED_TMOUT)) + { + rt_timer_stop(&sd->dto_timer); + } + + sdio_dw_writel(sd, RINTSTS, SDIO_DW_DATA_ERROR_FLAGS); + sd->data_status = pending; + rt_hw_wmb(); + bitmap_set_bit(&sd->pending_events, EVENT_DATA_ERROR); + + if ((sd->quirks & SDIO_DW_QUIRK_EXTENDED_TMOUT)) + { + /* In case of error, we cannot expect a DTO */ + bitmap_set_bit(&sd->pending_events, EVENT_DATA_COMPLETE); + } + rt_workqueue_urgent_work(sd->state_wq, &sd->state_work); + + rt_hw_spin_unlock(&sd->irq_lock.lock); + } + + if ((pending & PINT(DATA_OVER))) + { + rt_hw_spin_lock(&sd->irq_lock.lock); + + rt_timer_stop(&sd->dto_timer); + + sdio_dw_writel(sd, RINTSTS, PINT(DATA_OVER)); + if (!sd->data_status) + { + sd->data_status = pending; + } + rt_hw_wmb(); + + if (sd->dir_status == SDIO_DW_RECV_STATUS && sd->data && sd->data->buf) + { + sdio_dw_read_data_pio(sd, RT_TRUE); + } + bitmap_set_bit(&sd->pending_events, EVENT_DATA_COMPLETE); + + rt_workqueue_urgent_work(sd->state_wq, &sd->state_work); + + rt_hw_spin_unlock(&sd->irq_lock.lock); + } + + if ((pending & PINT(RXDR))) + { + sdio_dw_writel(sd, RINTSTS, PINT(RXDR)); + + if (sd->dir_status == SDIO_DW_RECV_STATUS && sd->data && sd->data->buf) + { + sdio_dw_read_data_pio(sd, RT_FALSE); + } + } + + if ((pending & PINT(TXDR))) + { + sdio_dw_writel(sd, RINTSTS, PINT(TXDR)); + + if (sd->dir_status == SDIO_DW_SEND_STATUS && sd->data && sd->data->buf) + { + sdio_dw_write_data_pio(sd); + } + } + + if ((pending & PINT(CMD_DONE))) + { + rt_hw_spin_lock(&sd->irq_lock.lock); + + sdio_dw_writel(sd, RINTSTS, PINT(CMD_DONE)); + sdio_dw_cmd_interrupt(sd, pending); + + rt_hw_spin_unlock(&sd->irq_lock.lock); + } + + if ((pending & PINT(CD))) + { + sdio_dw_writel(sd, RINTSTS, PINT(CD)); + mmcsd_change(slot->host); + } + + if ((pending & SDIO_DW_INT_SDIO(slot->sdio_id))) + { + sdio_dw_writel(sd, RINTSTS, SDIO_DW_INT_SDIO(slot->sdio_id)); + sdio_dw_mmc_enable_sdio_irq(slot->host, RT_FALSE); + sdio_irq_wakeup(slot->host); + } + } + + if (sd->use_dma != TRANS_MODE_IDMAC) + { + return; + } + + /* Handle IDMA interrupts */ + pending = sd->dma_64bit_address ? sdio_dw_readl(sd, IDSTS64) : sdio_dw_readl(sd, IDSTS); + + if ((pending & (PINTC(TI) | PINTC(RI)))) + { + if (sd->dma_64bit_address) + { + sdio_dw_writel(sd, IDSTS64, PINTC(TI) | PINTC(RI)); + sdio_dw_writel(sd, IDSTS64, PINTC(NI)); + } + else + { + sdio_dw_writel(sd, IDSTS, PINTC(TI) | PINTC(RI)); + sdio_dw_writel(sd, IDSTS, PINTC(NI)); + } + + if (!bitmap_test_bit(&sd->pending_events, EVENT_DATA_ERROR)) + { + sd->dma_ops->complete(sd); + } + } +} + +#ifdef RT_USING_OFW +static rt_err_t sdio_dw_parse_ofw(struct sdio_dw *sd) +{ + struct rt_ofw_node *np = sd->parent.ofw_node; + const struct sdio_dw_drv_data *drv_data = sd->drv_data; + + rt_ofw_prop_read_u32(np, "fifo-depth", &sd->fifo_depth); + rt_ofw_prop_read_u32(np, "card-detect-delay", &sd->detect_delay_ms); + rt_ofw_prop_read_u32(np, "data-addr", &sd->data_addr_override); + + if (rt_ofw_prop_read_bool(np, "fifo-watermark-aligned")) + { + sd->wm_aligned = RT_TRUE; + } + + rt_ofw_prop_read_u32(np, "clock-frequency", &sd->bus_hz); + + if (drv_data && drv_data->parse_ofw) + { + return drv_data->parse_ofw(sd); + } + + return RT_EOK; +} +#else +rt_inline rt_err_t sdio_dw_parse_ofw(struct sdio_dw *sd) +{ + return -RT_ENOSYS; +} +#endif /* RT_USING_OFW */ + +static rt_err_t sdio_dw_init_slot(struct sdio_dw *sd) +{ + rt_err_t err; + struct sdio_dw_slot *slot; + struct rt_mmcsd_host *host = mmcsd_alloc_host(); + + if (!host) + { + return -RT_ENOMEM; + } + + slot = rt_calloc(1, sizeof(*slot)); + + if (!slot) + { + err = -RT_ENOMEM; + goto _free; + } + + err = sdio_regulator_get_supply(&sd->parent, host); + + if (err) + { + goto _free; + } + + host->ops = &sdio_dw_mmc_ops; + host->private_data = slot; + slot->host = host; + slot->sd = sd; + sd->slot = slot; + + slot->id = 0; + slot->sdio_id = sd->sdio_id0 + slot->id; + + err = sdio_ofw_parse(sd->parent.ofw_node, host); + + if (err) + { + goto _free; + } + + if (!host->valid_ocr) + { + host->valid_ocr = VDD_32_33 | VDD_33_34; + } + + if (sd->minimum_speed) + { + host->freq_min = sd->minimum_speed; + } + else + { + host->freq_min = SDIO_DW_FREQ_HZ_MIN; + } + + if (!host->freq_max) + { + host->freq_max = SDIO_DW_FREQ_HZ_MAX; + } + + /* Useful defaults if platform data is unset. */ + if (sd->use_dma == TRANS_MODE_IDMAC) + { + host->max_dma_segs = sd->ring_size; + host->max_blk_size = 65535; + host->max_seg_size = DESC_RING_BUF_SZ; + host->max_blk_count = (host->max_seg_size * sd->ring_size) / 512; + } + else if (sd->use_dma == TRANS_MODE_EDMAC) + { + host->max_dma_segs = 64; + host->max_blk_size = 65535; + host->max_blk_count = 65535; + host->max_seg_size = host->max_blk_size * host->max_blk_count; + } + else + { + /* TRANS_MODE_PIO */ + host->max_dma_segs = 64; + host->max_blk_size = 65535; + host->max_blk_count = 512; + host->max_seg_size = host->max_blk_size * host->max_blk_count; + } + + return RT_EOK; + +_free: + if (host) + { + mmcsd_free_host(host); + } + if (slot) + { + rt_free(slot); + } + return err; +} + +static void sdio_dw_free(struct sdio_dw *sd) +{ + if (sd->rstc) + { + rt_reset_control_assert(sd->rstc); + rt_reset_control_put(sd->rstc); + } + + if (sd->ciu_clk) + { + rt_clk_disable_unprepare(sd->ciu_clk); + rt_clk_put(sd->ciu_clk); + } + + if (sd->biu_clk) + { + rt_clk_disable_unprepare(sd->biu_clk); + rt_clk_put(sd->biu_clk); + } + + if (sd->dma_buf_cache) + { + rt_pages_free(sd->dma_buf_cache, rt_page_bits(DESC_RING_BUF_SZ)); + } + + if (sd->dma_buf) + { + rt_iounmap(sd->dma_buf); + } +} + +rt_err_t sdio_dw_probe(struct sdio_dw *sd) +{ + int i, len; + rt_err_t err = RT_EOK; + char dev_name[RT_NAME_MAX]; + const struct sdio_dw_drv_data *drv_data = sd->drv_data; + + err = sdio_dw_parse_ofw(sd); + + if (err && err != -RT_ENOSYS) + { + goto _free_res; + } + + if (rt_dm_dev_prop_read_bool(&sd->parent, "resets")) + { + if (!(sd->rstc = rt_reset_control_get_by_name(&sd->parent, "reset"))) + { + LOG_E("Reset controller not found"); + + err = -RT_EIO; + goto _free_res; + } + } + + if (sd->rstc) + { + rt_reset_control_assert(sd->rstc); + rt_hw_us_delay(20); + rt_reset_control_deassert(sd->rstc); + } + + sd->biu_clk = rt_clk_get_by_name(&sd->parent, "biu"); + sd->ciu_clk = rt_clk_get_by_name(&sd->parent, "ciu"); + + if (rt_is_err(sd->biu_clk) || rt_is_err(sd->ciu_clk)) + { + /* board has init clock */ + if (sd->bus_hz) + { + goto _out_clk; + } + + err = rt_is_err(sd->biu_clk) ? rt_ptr_err(sd->biu_clk) : rt_ptr_err(sd->ciu_clk); + goto _free_res; + } + + err = rt_clk_prepare_enable(sd->ciu_clk); + + if (err) + { + goto _free_res; + } + + if (sd->bus_hz) + { + rt_clk_set_rate(sd->ciu_clk, sd->bus_hz); + } + + sd->bus_hz = rt_clk_get_rate(sd->ciu_clk); + + if (!sd->bus_hz) + { + err = -RT_EIO; + LOG_E("Bus speed not found"); + goto _free_res; + } + +_out_clk: + if (drv_data && drv_data->init) + { + err = drv_data->init(sd); + + if (err) + { + goto _free_res; + } + } + + rt_spin_lock_init(&sd->lock); + rt_spin_lock_init(&sd->irq_lock); + + rt_pin_ctrl_confs_apply_by_name(&sd->parent, RT_NULL); + + /* + * Get the host data width - this assumes that HCON has been set with the + * correct values. + */ + i = SDIO_DW_GET_HDATA_WIDTH(sdio_dw_readl(sd, HCON)); + if (!i) + { + sd->push_data = sdio_dw_push_data16; + sd->pull_data = sdio_dw_pull_data16; + sd->data_shift = 1; + } + else if (i == 2) + { + sd->push_data = sdio_dw_push_data64; + sd->pull_data = sdio_dw_pull_data64; + sd->data_shift = 3; + } + else + { + /* Check for a reserved value, and warn if it is */ + if (i != 1) + { + LOG_W("HCON reports a reserved host data width, defaulting to 32-bit access"); + } + + sd->push_data = sdio_dw_push_data32; + sd->pull_data = sdio_dw_pull_data32; + sd->data_shift = 2; + } + + /* Reset all blocks */ + if (!sdio_dw_ctrl_reset(sd, SDIO_DW_CTRL_ALL_RESET_FLAGS)) + { + err = -RT_EIO; + + goto _free_res; + } + + sdio_dw_init_dma(sd); + + /* Clear the interrupts for the host controller */ + sdio_dw_writel(sd, RINTSTS, 0xffffffff); + /* Disable all mmc interrupt first */ + sdio_dw_writel(sd, INTMASK, 0); + + /* Put in max timeout */ + sdio_dw_writel(sd, TMOUT, 0xffffffff); + + /* + * FIFO threshold settings: + * Rx Mark = fifo_size / 2 - 1, + * Tx Mark = fifo_size / 2 + * DMA Size = 8 + */ + if (sd->fifo_depth) + { + /* + * Power-on value of RX_WMark is FIFO_DEPTH-1, but this may have been + * overwritten by the bootloader, just like we're about to do, so if you + * know the value for your hardware, you should put it in the platform + * data. + */ + sd->fifo_depth = sdio_dw_readl(sd, FIFOTH); + sd->fifo_depth = 1 + ((sd->fifo_depth >> 16) & 0xfff); + } + sd->fifoth_val = SDIO_DW_SET_FIFOTH(0x2, sd->fifo_depth / 2 - 1, sd->fifo_depth / 2); + sdio_dw_writel(sd, FIFOTH, sd->fifoth_val); + + /* Disable clock to CIU */ + sdio_dw_writel(sd, CLKENA, 0); + sdio_dw_writel(sd, CLKSRC, 0); + + /* + * In 2.40a spec, Data offset is changed. + * Need to check the version-id and set data-offset for DATA register. + */ + sd->verid = SDIO_DW_GET_VERID(sdio_dw_readl(sd, VERID)); + LOG_D("Version ID is %04x", sd->verid); + + if (sd->data_addr_override) + { + sd->fifo_base = sd->base + sd->data_addr_override; + } + else if (sd->verid < SDIO_DW_240A) + { + sd->fifo_base = sd->base + DATA_OFFSET; + } + else + { + sd->fifo_base = sd->base + DATA_240A_OFFSET; + } + + /* + * Enable interrupts for command done, data over, data empty, receive ready + * and error such as transmit, receive timeout, crc error + */ + sdio_dw_writel(sd, INTMASK, PINT(CMD_DONE) | PINT(DATA_OVER) | PINT(TXDR) | PINT(RXDR) | SDIO_DW_ERROR_FLAGS); + /* Enable mci interrupt */ + sdio_dw_writel(sd, CTRL, SDIO_DW_CTRL_INT_ENABLE); + + /* Enable GPIO interrupt */ + sdio_dw_writel(sd, INTMASK, sdio_dw_readl(sd, INTMASK) | PINT(CD)); + + /* We need at least one slot to succeed */ + err = sdio_dw_init_slot(sd); + + if (err) + { + goto _free_res; + } + + /* Now that slots are all setup, we can enable card detect */ + sdio_dw_writel(sd, INTMASK, sdio_dw_readl(sd, INTMASK) | PINT(CD)); + + len = sdio_host_set_name(sd->slot->host, dev_name); + + sd->state_wq = rt_workqueue_create(dev_name, RT_SYSTEM_WORKQUEUE_STACKSIZE, + RT_MMCSD_THREAD_PREORITY); + + if (!sd->state_wq) + { + err = -RT_ENOMEM; + + goto _free_res; + } + + rt_work_init(&sd->state_work, sdio_dw_state_change, sd); + + rt_hw_interrupt_install(sd->irq, sdio_dw_isr, sd, dev_name); + rt_hw_interrupt_umask(sd->irq); + + rt_strncpy(&dev_name[len], "-cmd11", sizeof(dev_name) - len); + rt_timer_init(&sd->cmd11_timer, dev_name, sdio_dw_cmd11_timer, sd, + 0, RT_TIMER_FLAG_PERIODIC); + + rt_strncpy(&dev_name[len], "-cto", sizeof(dev_name) - len); + rt_timer_init(&sd->cto_timer, dev_name, sdio_dw_cto_timer, sd, + 0, RT_TIMER_FLAG_PERIODIC); + + rt_strncpy(&dev_name[len], "-dto", sizeof(dev_name) - len); + rt_timer_init(&sd->dto_timer, dev_name, sdio_dw_dto_timer, sd, + 0, RT_TIMER_FLAG_PERIODIC); + + mmcsd_change(sd->slot->host); + + return err; + +_free_res: + sdio_dw_free(sd); + + return err; +} + +rt_err_t sdio_dw_remove(struct sdio_dw *sd) +{ + if (sd->slot) + { + mmcsd_free_host(sd->slot->host); + } + + sdio_dw_writel(sd, RINTSTS, 0xffffffff); + /* Disable all mmc interrupt first */ + sdio_dw_writel(sd, INTMASK, 0); + + /* Disable clock to CIU */ + sdio_dw_writel(sd, CLKENA, 0); + sdio_dw_writel(sd, CLKSRC, 0); + + rt_hw_interrupt_mask(sd->irq); + rt_pic_detach_irq(sd->irq, sd); + + rt_timer_detach(&sd->cmd11_timer); + rt_timer_detach(&sd->cto_timer); + rt_timer_detach(&sd->dto_timer); + + sdio_dw_free(sd); + + return RT_EOK; +} diff --git a/components/drivers/sdio/host/sdio-dw.h b/components/drivers/sdio/host/sdio-dw.h new file mode 100644 index 000000000000..aa0db9182e9b --- /dev/null +++ b/components/drivers/sdio/host/sdio-dw.h @@ -0,0 +1,359 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-12-06 GuEe-GUI first version + */ + +#ifndef __SDIO_DW_H__ +#define __SDIO_DW_H__ + +#include "../sdio_dm.h" + +#define SDIO_DW_CTRL 0x000 +#define SDIO_DW_PWREN 0x004 +#define SDIO_DW_CLKDIV 0x008 +#define SDIO_DW_CLKSRC 0x00c +#define SDIO_DW_CLKENA 0x010 +#define SDIO_DW_TMOUT 0x014 +#define SDIO_DW_CTYPE 0x018 +#define SDIO_DW_BLKSIZ 0x01c +#define SDIO_DW_BYTCNT 0x020 +#define SDIO_DW_INTMASK 0x024 +#define SDIO_DW_CMDARG 0x028 +#define SDIO_DW_CMD 0x02c +#define SDIO_DW_RESP0 0x030 +#define SDIO_DW_RESP1 0x034 +#define SDIO_DW_RESP2 0x038 +#define SDIO_DW_RESP3 0x03c +#define SDIO_DW_MINTSTS 0x040 +#define SDIO_DW_RINTSTS 0x044 +#define SDIO_DW_STATUS 0x048 +#define SDIO_DW_FIFOTH 0x04c +#define SDIO_DW_CDETECT 0x050 +#define SDIO_DW_WRTPRT 0x054 +#define SDIO_DW_GPIO 0x058 +#define SDIO_DW_TCBCNT 0x05c +#define SDIO_DW_TBBCNT 0x060 +#define SDIO_DW_DEBNCE 0x064 +#define SDIO_DW_USRID 0x068 +#define SDIO_DW_VERID 0x06c +#define SDIO_DW_HCON 0x070 +#define SDIO_DW_UHS_REG 0x074 +#define SDIO_DW_RST_N 0x078 +#define SDIO_DW_BMOD 0x080 +#define SDIO_DW_PLDMND 0x084 +#define SDIO_DW_DBADDR 0x088 +#define SDIO_DW_IDSTS 0x08c +#define SDIO_DW_IDINTEN 0x090 +#define SDIO_DW_DSCADDR 0x094 +#define SDIO_DW_BUFADDR 0x098 +#define SDIO_DW_CDTHRCTL 0x100 +#define SDIO_DW_UHS_REG_EXT 0x108 +#define SDIO_DW_DDR_REG 0x10c +#define SDIO_DW_ENABLE_SHIFT 0x110 +#define SDIO_DW_DATA(x) (x) +/* + * Registers to support idmac 64-bit address mode + */ +#define SDIO_DW_DBADDRL 0x088 +#define SDIO_DW_DBADDRU 0x08c +#define SDIO_DW_IDSTS64 0x090 +#define SDIO_DW_IDINTEN64 0x094 +#define SDIO_DW_DSCADDRL 0x098 +#define SDIO_DW_DSCADDRU 0x09c +#define SDIO_DW_BUFADDRL 0x0a0 +#define SDIO_DW_BUFADDRU 0x0a4 + +/* Support for longer data read timeout */ +#define SDIO_DW_QUIRK_EXTENDED_TMOUT RT_BIT(0) + +#define SDIO_DW_240A 0x240a +#define SDIO_DW_280A 0x280a + +/* + * Data offset is difference according to Version + * Lower than 2.40a : data register offest is 0x100 + */ +#define DATA_OFFSET 0x100 +#define DATA_240A_OFFSET 0x200 + +/* Control register defines */ +#define SDIO_DW_CTRL_USE_IDMAC RT_BIT(25) +#define SDIO_DW_CTRL_CEATA_INT_EN RT_BIT(11) +#define SDIO_DW_CTRL_SEND_AS_CCSD RT_BIT(10) +#define SDIO_DW_CTRL_SEND_CCSD RT_BIT(9) +#define SDIO_DW_CTRL_ABRT_READ_DATA RT_BIT(8) +#define SDIO_DW_CTRL_SEND_IRQ_RESP RT_BIT(7) +#define SDIO_DW_CTRL_READ_WAIT RT_BIT(6) +#define SDIO_DW_CTRL_DMA_ENABLE RT_BIT(5) +#define SDIO_DW_CTRL_INT_ENABLE RT_BIT(4) +#define SDIO_DW_CTRL_DMA_RESET RT_BIT(2) +#define SDIO_DW_CTRL_FIFO_RESET RT_BIT(1) +#define SDIO_DW_CTRL_RESET RT_BIT(0) +/* Clock Enable register defines */ +#define SDIO_DW_CLKEN_LOW_PWR RT_BIT(16) +#define SDIO_DW_CLKEN_ENABLE RT_BIT(0) +/* Time-out register defines */ +#define SDIO_DW_TMOUT_DATA(n) ((n) << 8) +#define SDIO_DW_TMOUT_DATA_MSK 0xffffff00 +#define SDIO_DW_TMOUT_RESP(n) ((n) & 0xff) +#define SDIO_DW_TMOUT_RESP_MSK 0xff +/* Card-type register defines */ +#define SDIO_DW_CTYPE_8BIT RT_BIT(16) +#define SDIO_DW_CTYPE_4BIT RT_BIT(0) +#define SDIO_DW_CTYPE_1BIT 0 +/* Interrupt status & mask register defines */ +#define SDIO_DW_INT_SDIO(n) RT_BIT(16 + (n)) +#define SDIO_DW_INT_RAW_SDIO RT_BIT(24) +#define SDIO_DW_INT_EBE RT_BIT(15) +#define SDIO_DW_INT_ACD RT_BIT(14) +#define SDIO_DW_INT_SBE RT_BIT(13) +#define SDIO_DW_INT_HLE RT_BIT(12) +#define SDIO_DW_INT_FRUN RT_BIT(11) +#define SDIO_DW_INT_HTO RT_BIT(10) +#define SDIO_DW_INT_VOLT_SWITCH RT_BIT(10) +#define SDIO_DW_INT_DRTO RT_BIT(9) +#define SDIO_DW_INT_RTO RT_BIT(8) +#define SDIO_DW_INT_DCRC RT_BIT(7) +#define SDIO_DW_INT_RCRC RT_BIT(6) +#define SDIO_DW_INT_RXDR RT_BIT(5) +#define SDIO_DW_INT_TXDR RT_BIT(4) +#define SDIO_DW_INT_DATA_OVER RT_BIT(3) +#define SDIO_DW_INT_CMD_DONE RT_BIT(2) +#define SDIO_DW_INT_RESP_ERR RT_BIT(1) +#define SDIO_DW_INT_CD RT_BIT(0) +#define SDIO_DW_INT_ERROR 0xbfc2 +/* Command register defines */ +#define SDIO_DW_CMD_START RT_BIT(31) +#define SDIO_DW_CMD_USE_HOLD_REG RT_BIT(29) +#define SDIO_DW_CMD_VOLT_SWITCH RT_BIT(28) +#define SDIO_DW_CMD_CCS_EXP RT_BIT(23) +#define SDIO_DW_CMD_CEATA_RD RT_BIT(22) +#define SDIO_DW_CMD_UPD_CLK RT_BIT(21) +#define SDIO_DW_CMD_INIT RT_BIT(15) +#define SDIO_DW_CMD_STOP RT_BIT(14) +#define SDIO_DW_CMD_PRV_DAT_WAIT RT_BIT(13) +#define SDIO_DW_CMD_SEND_STOP RT_BIT(12) +#define SDIO_DW_CMD_STRM_MODE RT_BIT(11) +#define SDIO_DW_CMD_DAT_WR RT_BIT(10) +#define SDIO_DW_CMD_DAT_EXP RT_BIT(9) +#define SDIO_DW_CMD_RESP_CRC RT_BIT(8) +#define SDIO_DW_CMD_RESP_LONG RT_BIT(7) +#define SDIO_DW_CMD_RESP_EXP RT_BIT(6) +#define SDIO_DW_CMD_INDX(n) ((n) & 0x1f) +/* Status register defines */ +#define SDIO_DW_GET_FCNT(x) (((x) >> 17) & 0x1fff) +#define SDIO_DW_STATUS_DMA_REQ RT_BIT(31) +#define SDIO_DW_STATUS_BUSY RT_BIT(9) +/* FIFOTH register defines */ +#define SDIO_DW_SET_FIFOTH(m, r, t) (((m) & 0x7) << 28 | ((r) & 0xfff) << 16 | ((t) & 0xfff)) +/* HCON register defines */ +#define DMA_INTERFACE_IDMA (0x0) +#define DMA_INTERFACE_DWDMA (0x1) +#define DMA_INTERFACE_GDMA (0x2) +#define DMA_INTERFACE_NODMA (0x3) +#define SDIO_DW_GET_TRANS_MODE(x) (((x) >> 16) & 0x3) +#define SDIO_DW_GET_SLOT_NUM(x) ((((x) >> 1) & 0x1f) + 1) +#define SDIO_DW_GET_HDATA_WIDTH(x) (((x) >> 7) & 0x7) +#define SDIO_DW_GET_ADDR_CONFIG(x) (((x) >> 27) & 0x1) +/* Internal DMAC interrupt defines */ +#define SDIO_DW_IDMAC_INT_AI RT_BIT(9) +#define SDIO_DW_IDMAC_INT_NI RT_BIT(8) +#define SDIO_DW_IDMAC_INT_CES RT_BIT(5) +#define SDIO_DW_IDMAC_INT_DU RT_BIT(4) +#define SDIO_DW_IDMAC_INT_FBE RT_BIT(2) +#define SDIO_DW_IDMAC_INT_RI RT_BIT(1) +#define SDIO_DW_IDMAC_INT_TI RT_BIT(0) +/* Internal DMAC bus mode bits */ +#define SDIO_DW_IDMAC_ENABLE RT_BIT(7) +#define SDIO_DW_IDMAC_FB RT_BIT(1) +#define SDIO_DW_IDMAC_SWRESET RT_BIT(0) +/* H/W reset */ +#define SDIO_DW_RST_HWACTIVE 0x1 +/* Version ID register define */ +#define SDIO_DW_GET_VERID(x) ((x) & 0xffff) +/* Card read threshold */ +#define SDIO_DW_SET_THLD(v, x) (((v) & 0xfff) << 16 | (x)) +#define SDIO_DW_CARD_WR_THR_EN RT_BIT(2) +#define SDIO_DW_CARD_RD_THR_EN RT_BIT(0) +/* UHS-1 register defines */ +#define SDIO_DW_UHS_DDR RT_BIT(16) +#define SDIO_DW_UHS_18V RT_BIT(0) +/* DDR register defines */ +#define SDIO_DW_DDR_HS400 RT_BIT(31) +/* Enable shift register defines */ +#define SDIO_DW_ENABLE_PHASE RT_BIT(0) +/* All ctrl reset bits */ +#define SDIO_DW_CTRL_ALL_RESET_FLAGS (SDIO_DW_CTRL_RESET | SDIO_DW_CTRL_FIFO_RESET | SDIO_DW_CTRL_DMA_RESET) + +struct sdio_dw +{ + struct rt_device parent; + + struct rt_workqueue *state_wq; + struct rt_work state_work; + + void *base; + void *fifo_base; + rt_ubase_t base_phy; + rt_uint32_t data_addr_override; + rt_bool_t wm_aligned; + + int irq; + int sdio_id0; + rt_uint32_t verid; + rt_uint32_t quirks; + rt_uint32_t bus_hz; + rt_bool_t fifo_mode; + rt_uint32_t fifo_depth; + rt_uint32_t fifoth_val; + rt_uint32_t detect_delay_ms; + rt_uint32_t current_speed; + rt_uint32_t minimum_speed; + void *priv; + + rt_uint32_t vqmmc_enabled; + rt_uint32_t cmd_status; + rt_uint32_t data_status; + rt_uint32_t stop_cmdr; + rt_uint32_t dir_status; +#define STATE_IDLE 0 +#define STATE_SENDING_CMD 1 +#define STATE_SENDING_DATA 2 +#define STATE_DATA_BUSY 3 +#define STATE_SENDING_STOP 4 +#define STATE_DATA_ERROR 5 +#define STATE_SENDING_CMD11 6 +#define STATE_WAITING_CMD11_DONE 7 + rt_uint32_t state; +#define EVENT_CMD_COMPLETE 0 +#define EVENT_XFER_COMPLETE 1 +#define EVENT_DATA_COMPLETE 2 +#define EVENT_DATA_ERROR 3 + bitmap_t pending_events; + + struct rt_mmcsd_req *req; + struct rt_mmcsd_data *data; + struct rt_mmcsd_cmd *cmd; + struct rt_mmcsd_cmd stop_abort; + rt_uint32_t prev_blksz; + rt_uint8_t timing; + + struct rt_clk *biu_clk; + struct rt_clk *ciu_clk; + + void *last_buf; + rt_uint32_t last_remain; + + int data_shift; + rt_uint8_t part_buf_start; + rt_uint8_t part_buf_count; + union + { + rt_uint64_t part_buf; + rt_uint16_t part_buf16; + rt_uint32_t part_buf32; + rt_uint64_t part_buf64; + }; + void (*push_data)(struct sdio_dw *sd, void *buf, int cnt); + void (*pull_data)(struct sdio_dw *sd, void *buf, int cnt); + + /* DMA interface members */ +#define TRANS_MODE_PIO 0 +#define TRANS_MODE_IDMAC 1 +#define TRANS_MODE_EDMAC 2 + rt_bool_t use_dma; + rt_bool_t using_dma; + rt_bool_t dma_64bit_address; + rt_size_t ring_size; + void *dma_buf; + void *dma_buf_cache; + rt_ubase_t dma_buf_phy; + const struct sdio_dw_dma_ops *dma_ops; + + struct rt_timer cmd11_timer; + struct rt_timer cto_timer; + struct rt_timer dto_timer; + + struct rt_reset_control *rstc; + + struct sdio_dw_slot *slot; + const struct sdio_dw_drv_data *drv_data; + + struct rt_spinlock lock, irq_lock; +}; + +/* DMA ops for Internal/External DMAC interface */ +struct sdio_dw_dma_ops +{ + rt_err_t (*init)(struct sdio_dw *sd); + rt_err_t (*start)(struct sdio_dw *sd); + rt_err_t (*complete)(struct sdio_dw *sd); + rt_err_t (*stop)(struct sdio_dw *sd); + rt_err_t (*cleanup)(struct sdio_dw *sd); + rt_err_t (*exit)(struct sdio_dw *sd); +}; + +struct sdio_dw_slot +{ + struct rt_mmcsd_host *host; + struct sdio_dw *sd; + + rt_uint32_t ctype; + + struct rt_mmcsd_req *req; + + rt_uint32_t clock; + rt_uint32_t clk_old; + +#define DW_MMC_CARD_PRESENT 0 +#define DW_MMC_CARD_NEED_INIT 1 +#define DW_MMC_CARD_NO_LOW_PWR 2 +#define DW_MMC_CARD_NO_USE_HOLD 3 +#define DW_MMC_CARD_NEEDS_POLL 4 + bitmap_t flags; + + int id; + int sdio_id; +}; + +struct sdio_dw_drv_data +{ + rt_ubase_t *caps; + rt_uint32_t num_caps; + rt_uint32_t common_caps; + + rt_err_t (*init)(struct sdio_dw *sd); + rt_err_t (*set_iocfg)(struct sdio_dw *sd, struct rt_mmcsd_io_cfg *ios); + rt_err_t (*parse_ofw)(struct sdio_dw *sd); + rt_err_t (*execute_tuning)(struct sdio_dw_slot *slot, rt_uint32_t opcode); + rt_err_t (*prepare_hs400_tuning)(struct sdio_dw *sd, struct rt_mmcsd_io_cfg *ios); + rt_err_t (*switch_voltage)(struct rt_mmcsd_host *host, struct rt_mmcsd_io_cfg *ios); + rt_err_t (*set_data_timeout)(struct sdio_dw *sd, rt_uint32_t timeout_ns); + rt_uint32_t (*get_drto_clks)(struct sdio_dw *sd); +}; + +#define sdio_dw_writel(sd, reg, val) HWREG32((sd)->base + SDIO_DW_##reg) = (val) +#define sdio_dw_writew(sd, reg, val) HWREG16((sd)->base + SDIO_DW_##reg) = (val) +#define sdio_dw_writeb(sd, reg, val) HWREG8((sd)->base + SDIO_DW_##reg) = (val) + +#define sdio_dw_readl(sd, reg) HWREG32((sd)->base + SDIO_DW_##reg) +#define sdio_dw_readw(sd, reg) HWREG16((sd)->base + SDIO_DW_##reg) +#define sdio_dw_readb(sd, reg) HWREG8((sd)->base + SDIO_DW_##reg) + +#define sdio_dw_fifo_writew(sd, val) HWREG16((sd)->fifo_base) = (val) +#define sdio_dw_fifo_writel(sd, val) HWREG32((sd)->fifo_base) = (val) +#define sdio_dw_fifo_writeq(sd, val) HWREG64((sd)->fifo_base) = (val) + +#define sdio_dw_fifo_readw(sd) HWREG16((sd)->fifo_base) +#define sdio_dw_fifo_readl(sd) HWREG32((sd)->fifo_base) +#define sdio_dw_fifo_readq(sd) HWREG64((sd)->fifo_base) + +rt_err_t sdio_dw_probe(struct sdio_dw *sd); +rt_err_t sdio_dw_remove(struct sdio_dw *sd); + +#endif /* __SDIO_DW_H__ */ diff --git a/components/drivers/sdio/host/sdio-dw_rockchip.c b/components/drivers/sdio/host/sdio-dw_rockchip.c new file mode 100644 index 000000000000..7a058c72efe1 --- /dev/null +++ b/components/drivers/sdio/host/sdio-dw_rockchip.c @@ -0,0 +1,468 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-12-06 GuEe-GUI first version + */ + +#include "sdio-dw.h" +#include "sdio-dw-platfrom.h" + +#define DBG_TAG "sdio.dw.rockchip" +#define DBG_LVL DBG_INFO +#include + +#define RK3288_CLKGEN_DIV 2 + +struct phases_range +{ + int start; + int end; +}; + +struct sdio_dw_rockchip_platfrom_data +{ + struct rt_clk *drv_clk; + struct rt_clk *sample_clk; + + rt_bool_t use_v2_tuning; + int last_degree; + rt_uint32_t freq_min; + + rt_uint32_t default_sample_phase; + rt_uint32_t num_phases; + struct phases_range phases[]; +}; + +static rt_err_t sdio_dw_rockchip_init(struct sdio_dw *sd) +{ + /* It is slot 8 on Rockchip SoCs */ + sd->sdio_id0 = 8; + + if (rt_ofw_node_is_compatible(sd->parent.ofw_node, "rockchip,rk3288-dw-mshc")) + { + sd->bus_hz /= RK3288_CLKGEN_DIV; + } + + return RT_EOK; +} + +static rt_err_t sdio_dw_rk3288_set_iocfg(struct sdio_dw *sd, struct rt_mmcsd_io_cfg *ios) +{ + rt_uint32_t freq, bus_hz; + struct sdio_dw_rockchip_platfrom_data *pdata = sd->priv; + + if (!ios->clock) + { + return RT_EOK; + } + + /* + * freq: source clock of mmc controller + * bus_hz: card interface clock generated by CLKGEN + * bus_hz = freq / RK3288_CLKGEN_DIV + * ios->clock = (div == 0) ? bus_hz : (bus_hz / (2 * div)) + * + * Note: div can only be 0 or 1, but div must be set to 1 for eMMC + * DDR52 8-bit mode. + */ + if (ios->clock < pdata->freq_min) + { + ios->clock = pdata->freq_min; + sd->slot->clock = ios->clock; + } + + freq = ios->clock * RK3288_CLKGEN_DIV; + + /* + * The clock frequency chosen here affects CLKDIV in the dw_mmc core. + * That can be either 0 or 1, but it must be set to 1 for eMMC DDR52 + * 8-bit mode. It will be set to 0 for all other modes. + */ + if (ios->bus_width == MMCSD_BUS_WIDTH_8 && ios->timing == MMCSD_TIMING_MMC_DDR52) + { + freq *= 2; + } + + rt_clk_set_rate(sd->ciu_clk, freq); + bus_hz = rt_clk_get_rate(sd->ciu_clk) / RK3288_CLKGEN_DIV; + + if (bus_hz != sd->bus_hz) + { + sd->bus_hz = bus_hz; + sd->current_speed = 0; + } + + if (!rt_is_err_or_null(pdata->sample_clk) && ios->timing <= MMCSD_TIMING_SD_HS) + { + rt_clk_set_phase(pdata->sample_clk, pdata->default_sample_phase); + } + + if (!rt_is_err_or_null(pdata->drv_clk)) + { + int phase = 90; + + switch (ios->timing) + { + case MMCSD_TIMING_MMC_DDR52: + /* + * Since clock in rate with MMC_DDR52 is doubled when + * bus width is 8 we need to double the phase offset + * to get the same timings. + */ + if (ios->bus_width == MMCSD_BUS_WIDTH_8) + { + phase = 180; + } + break; + case MMCSD_TIMING_UHS_SDR104: + case MMCSD_TIMING_MMC_HS200: + /* + * In the case of 150 MHz clock (typical max for + * Rockchip SoCs), 90 degree offset will add a delay + * of 1.67 ns. That will meet min hold time of .8 ns + * as long as clock output delay is < .87 ns. On + * SoCs measured this seems to be OK, but it doesn't + * hurt to give margin here, so we use 180. + */ + phase = 180; + break; + } + + rt_clk_set_phase(pdata->drv_clk, phase); + } + + return RT_EOK; +} + +static rt_err_t sdio_dw_rk3288_parse_ofw(struct sdio_dw *sd) +{ + rt_err_t err = RT_EOK; + rt_uint32_t num_phases; + struct rt_ofw_node *np = sd->parent.ofw_node; + struct sdio_dw_rockchip_platfrom_data *pdata; + + if (rt_ofw_prop_read_u32(np, "rockchip,desired-num-phases", &num_phases)) + { + num_phases = 360; + } + + pdata = rt_malloc(sizeof(*pdata) + sizeof(struct phases_range) * num_phases); + + if (!pdata) + { + return -RT_ENOMEM; + } + + pdata->num_phases = num_phases; + + if (rt_ofw_prop_read_u32(np, "rockchip,default-sample-phase", &pdata->default_sample_phase)) + { + pdata->default_sample_phase = 0; + } + + if (rt_ofw_prop_read_bool(np, "rockchip,use-v2-tuning")) + { + pdata->use_v2_tuning = RT_TRUE; + } + + if (rt_ofw_node_is_compatible(np, "rockchip,rk3568-dw-mshc")) + { + pdata->freq_min = 375000; + } + else + { + pdata->freq_min = 100000; + } + + pdata->drv_clk = rt_ofw_get_clk_by_name(np, "ciu-drive"); + + if (rt_is_err_or_null(pdata->drv_clk)) + { + pdata->drv_clk = rt_ofw_get_clk_by_name(np, "ciu-drv"); + } + + pdata->sample_clk = rt_ofw_get_clk_by_name(np, "ciu-sample"); + + sd->priv = pdata; + + return err; +} + +#define TUNING_ITERATION_TO_PHASE(i, num_phases) \ + (RT_DIV_ROUND_UP((i) * 360, num_phases)) + +static rt_err_t dw_mci_v2_execute_tuning(struct sdio_dw_slot *slot, rt_uint32_t opcode) +{ + int i; + struct rt_mmcsd_host *host = slot->host; + struct sdio_dw_rockchip_platfrom_data *pdata = slot->sd->priv; + rt_uint32_t degrees[4] = { 0, 90, 180, 270 }, degree; + static rt_bool_t inherit = RT_TRUE; + + if (inherit) + { + inherit = RT_FALSE; + degree = degrees[rt_clk_get_phase(pdata->sample_clk) / 90]; + goto _end; + } + + /* + * v2 only support 4 degrees in theory. + * First we inherit sample phases from firmware, which should + * be able work fine, at least in the first place. + * If retune is needed, we search forward to pick the last + * one phase from degree list and loop around until we get one. + * It's impossible all 4 fixed phase won't be able to work. + */ + for (i = 0; i < RT_ARRAY_SIZE(degrees); ++i) + { + degree = degrees[i] + pdata->last_degree; + degree = degree % 360; + rt_clk_set_phase(pdata->sample_clk, degree); + + if (!mmcsd_send_tuning(host, opcode, RT_NULL)) + { + break; + } + } + + if (i == RT_ARRAY_SIZE(degrees)) + { + LOG_W("All phases bad!"); + + return -RT_EIO; + } + +_end: + LOG_D("Successfully tuned phase to %d", degrees[i]); + pdata->last_degree = degree; + + return 0; +} + +static rt_err_t sdio_dw_rk3288_execute_tuning(struct sdio_dw_slot *slot, + rt_uint32_t opcode) +{ + rt_err_t err = RT_EOK; + rt_bool_t tuning, prev_tuning = 0, first_tuning; + int range_count = 0, longest_range_len = -1, longest_range = -1; + int real_middle_phase, middle_phase; + struct rt_mmcsd_host *host = slot->host; + struct sdio_dw_rockchip_platfrom_data *pdata = slot->sd->priv; + struct phases_range *ranges = pdata->phases; + + if (rt_is_err_or_null(pdata->sample_clk)) + { + return -RT_EIO; + } + + if (pdata->use_v2_tuning) + { + if (!(err = dw_mci_v2_execute_tuning(slot, opcode))) + { + return RT_EOK; + } + } + + for (int i = 0; i < pdata->num_phases;) + { + rt_clk_set_phase(pdata->sample_clk, + TUNING_ITERATION_TO_PHASE(i, pdata->num_phases)); + + tuning = !mmcsd_send_tuning(host, opcode, RT_NULL); + + if (i == 0) + { + first_tuning = tuning; + } + + if ((!prev_tuning) && tuning) + { + ++range_count; + ranges[range_count - 1].start = i; + } + if (tuning) + { + ranges[range_count - 1].end = i; + ++i; + } + else if (i == pdata->num_phases - 1) + { + /* No extra skipping rules if we're at the end */ + ++i; + } + else + { + /* + * No need to check too close to an invalid one since + * testing bad phases is slow. Skip 20 degrees. + */ + i += RT_DIV_ROUND_UP(20 * pdata->num_phases, 360); + + /* Always test the last one */ + if (i >= pdata->num_phases) + { + i = pdata->num_phases - 1; + } + } + + prev_tuning = tuning; + } + + if (range_count == 0) + { + LOG_W("All phases bad!"); + + return -RT_EIO; + } + + /* wrap around case, merge the end points */ + if (range_count > 1 && first_tuning && tuning) + { + ranges[0].start = ranges[range_count - 1].start; + --range_count; + } + + if (ranges[0].start == 0 && ranges[0].end == pdata->num_phases - 1) + { + rt_clk_set_phase(pdata->sample_clk, pdata->default_sample_phase); + LOG_I("All phases work, using default phase %u", pdata->default_sample_phase); + + return RT_EOK; + } + + /* Find the longest range */ + for (int i = 0; i < range_count; ++i) + { + int len = (ranges[i].end - ranges[i].start + 1); + + if (len < 0) + { + len += pdata->num_phases; + } + + if (longest_range_len < len) + { + longest_range_len = len; + longest_range = i; + } + + LOG_D("%s phase range %d-%d (%d len)", + "Good", + TUNING_ITERATION_TO_PHASE(ranges[i].start, pdata->num_phases), + TUNING_ITERATION_TO_PHASE(ranges[i].end, pdata->num_phases), + len); + } + + LOG_D("%s phase range %d-%d (%d len)", + "Best", + TUNING_ITERATION_TO_PHASE(ranges[longest_range].start, pdata->num_phases), + TUNING_ITERATION_TO_PHASE(ranges[longest_range].end, pdata->num_phases), + longest_range_len); + + middle_phase = ranges[longest_range].start + longest_range_len / 2; + middle_phase %= pdata->num_phases; + real_middle_phase = TUNING_ITERATION_TO_PHASE(middle_phase, pdata->num_phases); + + /* + * Since we cut out 270 ~ 360, the original algorithm + * still rolling ranges before and after 270 together + * in some corner cases, we should adjust it to avoid + * using any middle phase located between 270 and 360. + * By calculatiion, it happends due to the bad phases + * lay between 90 ~ 180. So others are all fine to chose. + * Pick 270 is a better choice in those cases. In case of + * bad phases exceed 180, the middle phase of rollback + * would be bigger than 315, so we chose 360. + */ + if (real_middle_phase > 270) + { + if (real_middle_phase < 315) + { + real_middle_phase = 270; + } + else + { + real_middle_phase = 360; + } + } + + LOG_I("Successfully tuned phase to %d", + TUNING_ITERATION_TO_PHASE(middle_phase, pdata->num_phases)); + + rt_clk_set_phase(pdata->sample_clk, real_middle_phase); + + return err; +} + +static const struct sdio_dw_drv_data rk2928_drv_data = +{ + .init = sdio_dw_rockchip_init, +}; + +static const struct sdio_dw_drv_data rk3288_drv_data = +{ + .init = sdio_dw_rockchip_init, + .set_iocfg = sdio_dw_rk3288_set_iocfg, + .parse_ofw = sdio_dw_rk3288_parse_ofw, + .execute_tuning = sdio_dw_rk3288_execute_tuning, +}; + +static rt_err_t sdio_dw_rockchip_probe(struct rt_platform_device *pdev) +{ + const struct sdio_dw_drv_data *drv_data = pdev->id->data; + + return sdio_dw_platfrom_register(pdev, drv_data); +} + +static rt_err_t sdio_dw_rockchip_remove(struct rt_platform_device *pdev) +{ + struct sdio_dw *sd = pdev->parent.user_data; + struct sdio_dw_rockchip_platfrom_data *pdata = sd->priv; + + sdio_dw_remove(sd); + + if (!rt_is_err_or_null(pdata->drv_clk)) + { + rt_clk_put(pdata->drv_clk); + } + + if (!rt_is_err_or_null(pdata->sample_clk)) + { + rt_clk_put(pdata->sample_clk); + } + + rt_iounmap(sd->base); + + rt_free(sd); + + return RT_EOK; +} + +static const struct rt_ofw_node_id sdio_dw_rockchip_ofw_ids[] = +{ + { .compatible = "rockchip,rk2928-dw-mshc", .data = &rk2928_drv_data }, + { .compatible = "rockchip,rk3288-dw-mshc", .data = &rk3288_drv_data }, + { /* sentinel */ } +}; + +static struct rt_platform_driver sdio_dw_rockchip_driver = +{ + .name = "dw-mmc-rockchip", + .ids = sdio_dw_rockchip_ofw_ids, + + .probe = sdio_dw_rockchip_probe, + .remove = sdio_dw_rockchip_remove, +}; + +static int sdio_dw_rockchip_drv_register(void) +{ + rt_platform_driver_register(&sdio_dw_rockchip_driver); + + return 0; +} +INIT_DRIVER_LATER_EXPORT(sdio_dw_rockchip_drv_register); diff --git a/components/drivers/sdio/mmcsd_core.c b/components/drivers/sdio/mmcsd_core.c index 77570268c199..00776a90a862 100644 --- a/components/drivers/sdio/mmcsd_core.c +++ b/components/drivers/sdio/mmcsd_core.c @@ -549,6 +549,281 @@ rt_uint32_t mmcsd_select_voltage(struct rt_mmcsd_host *host, rt_uint32_t ocr) return ocr; } +rt_err_t mmcsd_set_signal_voltage(struct rt_mmcsd_host *host, unsigned char signal_voltage) +{ + rt_err_t err = RT_EOK; + unsigned char old_signal_voltage = host->io_cfg.signal_voltage; + + host->io_cfg.signal_voltage = signal_voltage; + if (host->ops->signal_voltage_switch) + { + err = host->ops->signal_voltage_switch(host, &host->io_cfg); + } + + if (err) + { + host->io_cfg.signal_voltage = old_signal_voltage; + } + + return err; +} + +void mmcsd_set_initial_signal_voltage(struct rt_mmcsd_host *host) +{ + /* 3.3V -> 1.8v -> 1.2v */ + if (!mmcsd_set_signal_voltage(host, MMCSD_SIGNAL_VOLTAGE_330)) + { + LOG_D("Initial signal voltage of %sv", "3.3"); + } + else if (!mmcsd_set_signal_voltage(host, MMCSD_SIGNAL_VOLTAGE_180)) + { + LOG_D("Initial signal voltage of %sv", "1.8"); + } + else if (!mmcsd_set_signal_voltage(host, MMCSD_SIGNAL_VOLTAGE_120)) + { + LOG_D("Initial signal voltage of %sv", "1.2"); + } +} + +rt_err_t mmcsd_host_set_uhs_voltage(struct rt_mmcsd_host *host) +{ + rt_uint32_t old_clock = host->io_cfg.clock; + + host->io_cfg.clock = 0; + mmcsd_set_iocfg(host); + + if (mmcsd_set_signal_voltage(host, MMCSD_SIGNAL_VOLTAGE_180)) + { + return -RT_ERROR; + } + + /* Keep clock gated for at least 10 ms, though spec only says 5 ms */ + rt_thread_mdelay(10); + + host->io_cfg.clock = old_clock; + mmcsd_set_iocfg(host); + + return RT_EOK; +} + +static void mmcsd_power_cycle(struct rt_mmcsd_host *host, rt_uint32_t ocr); + +rt_err_t mmcsd_set_uhs_voltage(struct rt_mmcsd_host *host, rt_uint32_t ocr) +{ + rt_err_t err = RT_EOK; + struct rt_mmcsd_cmd cmd; + + if (!host->ops->signal_voltage_switch) + { + return -RT_EINVAL; + } + + if (!host->ops->card_busy) + { + LOG_W("%s: Cannot verify signal voltage switch", host->name); + } + + rt_memset(&cmd, 0, sizeof(struct rt_mmcsd_cmd)); + + cmd.cmd_code = SD_SWITCH_VOLTAGE; + cmd.arg = 0; + cmd.flags = RESP_R1 | CMD_AC; + + err = mmcsd_send_cmd(host, &cmd, 0); + if (err) + { + goto power_cycle; + } + + if (!controller_is_spi(host) && (cmd.resp[0] & R1_ERROR)) + { + return -RT_EIO; + } + + /* + * The card should drive cmd and dat[0:3] low immediately + * after the response of cmd11, but wait 1 ms to be sure + */ + rt_thread_mdelay(1); + if (host->ops->card_busy && !host->ops->card_busy(host)) + { + err = -RT_ERROR; + goto power_cycle; + } + + if (mmcsd_host_set_uhs_voltage(host)) + { + /* + * Voltages may not have been switched, but we've already + * sent CMD11, so a power cycle is required anyway + */ + err = -RT_ERROR; + goto power_cycle; + } + + /* Wait for at least 1 ms according to spec */ + rt_thread_mdelay(1); + + /* + * Failure to switch is indicated by the card holding + * dat[0:3] low + */ + if (host->ops->card_busy && host->ops->card_busy(host)) + { + err = -RT_ERROR; + } + +power_cycle: + if (err) + { + LOG_D("%s: Signal voltage switch failed, power cycling card", host->name); + mmcsd_power_cycle(host, ocr); + } + + return err; +} + +static const rt_uint8_t tuning_blk_pattern_4bit[] = +{ + 0xff, 0x0f, 0xff, 0x00, 0xff, 0xcc, 0xc3, 0xcc, + 0xc3, 0x3c, 0xcc, 0xff, 0xfe, 0xff, 0xfe, 0xef, + 0xff, 0xdf, 0xff, 0xdd, 0xff, 0xfb, 0xff, 0xfb, + 0xbf, 0xff, 0x7f, 0xff, 0x77, 0xf7, 0xbd, 0xef, + 0xff, 0xf0, 0xff, 0xf0, 0x0f, 0xfc, 0xcc, 0x3c, + 0xcc, 0x33, 0xcc, 0xcf, 0xff, 0xef, 0xff, 0xee, + 0xff, 0xfd, 0xff, 0xfd, 0xdf, 0xff, 0xbf, 0xff, + 0xbb, 0xff, 0xf7, 0xff, 0xf7, 0x7f, 0x7b, 0xde, +}; + +static const rt_uint8_t tuning_blk_pattern_8bit[] = +{ + 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, + 0xff, 0xff, 0xcc, 0xcc, 0xcc, 0x33, 0xcc, 0xcc, + 0xcc, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xff, 0xff, + 0xff, 0xee, 0xff, 0xff, 0xff, 0xee, 0xee, 0xff, + 0xff, 0xff, 0xdd, 0xff, 0xff, 0xff, 0xdd, 0xdd, + 0xff, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff, 0xbb, + 0xbb, 0xff, 0xff, 0xff, 0x77, 0xff, 0xff, 0xff, + 0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee, 0xff, + 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, + 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xcc, 0x33, 0xcc, + 0xcc, 0xcc, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xff, + 0xff, 0xff, 0xee, 0xff, 0xff, 0xff, 0xee, 0xee, + 0xff, 0xff, 0xff, 0xdd, 0xff, 0xff, 0xff, 0xdd, + 0xdd, 0xff, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff, + 0xbb, 0xbb, 0xff, 0xff, 0xff, 0x77, 0xff, 0xff, + 0xff, 0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee, +}; + +rt_err_t mmcsd_send_tuning(struct rt_mmcsd_host *host, rt_uint32_t opcode, rt_err_t *cmd_error) +{ + rt_err_t err = RT_EOK; + int size; + rt_uint8_t *data_buf; + const rt_uint8_t *tuning_block_pattern; + struct rt_mmcsd_req req = {}; + struct rt_mmcsd_cmd cmd = {}; + struct rt_mmcsd_data data = {}; + struct rt_mmcsd_io_cfg *io_cfg = &host->io_cfg; + + if (io_cfg->bus_width == MMCSD_BUS_WIDTH_8) + { + tuning_block_pattern = tuning_blk_pattern_8bit; + size = sizeof(tuning_blk_pattern_8bit); + } + else if (io_cfg->bus_width == MMCSD_BUS_WIDTH_4) + { + tuning_block_pattern = tuning_blk_pattern_4bit; + size = sizeof(tuning_blk_pattern_4bit); + } + else + { + return -RT_EINVAL; + } + + data_buf = rt_malloc(size); + if (!data_buf) + { + return -RT_ENOMEM; + } + + rt_memset(data_buf, 0, size); + rt_memset(&req, 0, sizeof(struct rt_mmcsd_req)); + rt_memset(&cmd, 0, sizeof(struct rt_mmcsd_cmd)); + rt_memset(&data, 0, sizeof(struct rt_mmcsd_data)); + + req.cmd = &cmd; + req.data = &data; + + cmd.cmd_code = opcode; + cmd.flags = RESP_R1 | CMD_ADTC; + + data.blksize = size; + data.blks = 1; + data.flags = DATA_DIR_READ; + + /* + * According to the tuning specs, Tuning process + * is normally shorter 40 executions of CMD19, + * and timeout value should be shorter than 150 ms + */ + data.timeout_ns = 150 * 1000000; + + mmcsd_send_request(host, &req); + + if (cmd_error) + { + *cmd_error = cmd.err; + } + + if (cmd.err) + { + err = cmd.err; + goto out_free; + } + + if (data.err) + { + err = data.err; + goto out_free; + } + + if (rt_memcmp(data_buf, tuning_block_pattern, size)) + { + err = -RT_EIO; + } + +out_free: + rt_free(data_buf); + + return err; +} + +rt_err_t mmcsd_send_abort_tuning(struct rt_mmcsd_host *host, rt_uint32_t opcode) +{ + struct rt_mmcsd_cmd cmd = {}; + + /* + * eMMC specification specifies that CMD12 can be used to stop a tuning + * command, but SD specification does not, so do nothing unless it is eMMC. + */ + if (opcode != SEND_TUNING_BLOCK_HS200) + { + return 0; + } + + cmd.cmd_code = STOP_TRANSMISSION; + cmd.flags = RESP_SPI_R1 | RESP_R1 | CMD_AC; + + /* + * For drivers that override R1 to R1b, set an arbitrary timeout based + * on the tuning timeout i.e. 150ms. + */ + cmd.busy_timeout = 150; + + return mmcsd_send_cmd(host, &cmd, 0); +} + static void mmcsd_power_up(struct rt_mmcsd_host *host) { int bit = __rt_fls(host->valid_ocr) - 1; @@ -568,6 +843,8 @@ static void mmcsd_power_up(struct rt_mmcsd_host *host) host->io_cfg.bus_width = MMCSD_BUS_WIDTH_1; mmcsd_set_iocfg(host); + mmcsd_set_initial_signal_voltage(host); + /* * This delay should be sufficient to allow the power supply * to reach the minimum voltage. @@ -599,6 +876,17 @@ static void mmcsd_power_off(struct rt_mmcsd_host *host) mmcsd_set_iocfg(host); } +static void mmcsd_power_cycle(struct rt_mmcsd_host *host, rt_uint32_t ocr) +{ + mmcsd_power_off(host); + + /* Wait at least 1 ms according to SD spec */ + rt_thread_mdelay(1); + + mmcsd_power_up(host); + mmcsd_select_voltage(host, ocr); +} + int mmcsd_wait_cd_changed(rt_int32_t timeout) { struct rt_mmcsd_host *host; diff --git a/components/drivers/sdio/regulator.c b/components/drivers/sdio/regulator.c new file mode 100644 index 000000000000..052823c3f5d4 --- /dev/null +++ b/components/drivers/sdio/regulator.c @@ -0,0 +1,258 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-12-06 GuEe-GUI first version + */ + +#include "sdio_dm.h" + +#define DBG_TAG "sdio.regulator" +#define DBG_LVL DBG_INFO +#include + +static rt_err_t ocrbitnum_to_vdd(int vdd_bit, int *min_uvolt, int *max_uvolt) +{ + int tmp; + + if (!vdd_bit) + { + return -RT_EINVAL; + } + + tmp = vdd_bit - rt_ilog2(VDD_165_195); + + if (tmp == 0) + { + *min_uvolt = 1650 * 1000; + *max_uvolt = 1950 * 1000; + } + else + { + *min_uvolt = 1900 * 1000 + tmp * 100 * 1000; + *max_uvolt = *min_uvolt + 100 * 1000; + } + + return 0; +} + +rt_err_t sdio_regulator_set_ocr(struct rt_mmcsd_host *host, + struct rt_regulator *supply, rt_uint16_t vdd_bit) +{ + rt_err_t err = RT_EOK; + + if (!host) + { + return -RT_EINVAL; + } + + if (rt_is_err_or_null(supply)) + { + return RT_EOK; + } + + if (vdd_bit) + { + int min_uvolt, max_uvolt; + + ocrbitnum_to_vdd(vdd_bit, &min_uvolt, &max_uvolt); + + err = rt_regulator_set_voltage(supply, min_uvolt, max_uvolt); + + if (!err && host->supply.regulator_enabled) + { + err = rt_regulator_enable(supply); + + if (!err) + { + host->supply.regulator_enabled = RT_TRUE; + } + } + } + else if (host->supply.regulator_enabled) + { + err = rt_regulator_disable(supply); + + if (!err) + { + host->supply.regulator_enabled = RT_FALSE; + } + } + + if (err) + { + LOG_E("Set regulator OCR %d error = %s", vdd_bit, rt_strerror(err)); + } + + return err; +} + +static int regulator_set_voltage_if_supported(struct rt_regulator *regulator, + int min_uvolt, int target_uvolt, int max_uvolt) +{ + if (!rt_regulator_is_supported_voltage(regulator, min_uvolt, max_uvolt)) + { + return -RT_EINVAL; + } + + if (rt_regulator_get_voltage(regulator) == target_uvolt) + { + return RT_EOK; + } + + return rt_regulator_set_voltage_triplet(regulator, min_uvolt, target_uvolt, + max_uvolt); +} + +rt_err_t sdio_regulator_set_vqmmc(struct rt_mmcsd_host *host, + struct rt_mmcsd_io_cfg *ios) +{ + rt_err_t err; + int uvolt, min_uvolt, max_uvolt; + + if (rt_is_err_or_null(host->supply.vqmmc)) + { + return -RT_EINVAL; + } + + switch (ios->signal_voltage) + { + case MMCSD_SIGNAL_VOLTAGE_120: + return regulator_set_voltage_if_supported(host->supply.vqmmc, + 1100000, 1200000, 1300000); + + case MMCSD_SIGNAL_VOLTAGE_180: + return regulator_set_voltage_if_supported(host->supply.vqmmc, + 1700000, 1800000, 1950000); + + case MMCSD_SIGNAL_VOLTAGE_330: + err = ocrbitnum_to_vdd(host->io_cfg.vdd, &uvolt, &max_uvolt); + + if (err) + { + return err; + } + + min_uvolt = rt_max(uvolt - 300000, 2700000); + max_uvolt = rt_min(max_uvolt + 200000, 3600000); + + err = regulator_set_voltage_if_supported(host->supply.vqmmc, + min_uvolt, uvolt, max_uvolt); + if (err >= 0) + { + return err; + } + + return regulator_set_voltage_if_supported(host->supply.vqmmc, + 2700000, uvolt, 3600000); + + default: + return -RT_EINVAL; + } +} + +rt_err_t sdio_regulator_get_supply(struct rt_device *dev, struct rt_mmcsd_host *host) +{ + rt_err_t err; + + if (!dev || !host) + { + return -RT_EINVAL; + } + + host->supply.vmmc = rt_regulator_get_optional(dev, "vmmc"); + host->supply.vqmmc = rt_regulator_get_optional(dev, "vqmmc"); + + if (!rt_is_err(host->supply.vmmc)) + { + if (!host->supply.vmmc) + { + LOG_D("No vmmc regulator found"); + } + } + else + { + err = rt_ptr_err(host->supply.vmmc); + goto _fail; + } + + if (!rt_is_err(host->supply.vqmmc)) + { + if (host->supply.vqmmc) + { + LOG_D("No vqmmc regulator found"); + } + } + else + { + err = rt_ptr_err(host->supply.vqmmc); + goto _fail; + } + + return RT_EOK; + +_fail: + if (host->supply.vmmc) + { + rt_regulator_put(host->supply.vmmc); + host->supply.vmmc = RT_NULL; + } + + if (host->supply.vqmmc) + { + rt_regulator_put(host->supply.vqmmc); + host->supply.vqmmc = RT_NULL; + } + + return err; +} + +rt_err_t sdio_regulator_enable_vqmmc(struct rt_mmcsd_host *host) +{ + struct rt_mmcsd_supply *supply; + + if (!host) + { + return -RT_EINVAL; + } + + supply = &host->supply; + + if (!rt_is_err_or_null(supply->vqmmc) && !supply->vqmmc_enabled) + { + rt_err_t err = rt_regulator_enable(supply->vqmmc); + + if (err) + { + LOG_E("Enabling vqmmc regulator failed error = %s", rt_strerror(err)); + } + else + { + supply->vqmmc_enabled = RT_TRUE; + } + } + + return RT_EOK; +} + +void sdio_regulator_disable_vqmmc(struct rt_mmcsd_host *host) +{ + struct rt_mmcsd_supply *supply; + + if (!host) + { + return; + } + + supply = &host->supply; + + if (!rt_is_err_or_null(supply->vqmmc) && supply->vqmmc_enabled) + { + rt_regulator_disable(supply->vqmmc); + + supply->vqmmc_enabled = RT_FALSE; + } +} diff --git a/components/drivers/sdio/sdio_dm.c b/components/drivers/sdio/sdio_dm.c new file mode 100644 index 000000000000..9dd37d800eb5 --- /dev/null +++ b/components/drivers/sdio/sdio_dm.c @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-12-06 GuEe-GUI first version + */ + +#include +#include "sdio_dm.h" + +#define DBG_TAG "sdio.dm" +#define DBG_LVL DBG_INFO +#include + +int sdio_host_set_name(struct rt_mmcsd_host *host, char *out_devname) +{ + static volatile rt_atomic_t uid = 0; + + if (host) + { + int id = (int)rt_hw_atomic_add(&uid, 1), res; + + res = rt_snprintf(host->name, RT_NAME_MAX, "sd%u", id); + + if (out_devname) + { + rt_strncpy(out_devname, host->name, RT_NAME_MAX); + } + + return res; + } + + return -RT_EINVAL; +} + +#ifdef RT_USING_OFW +rt_err_t sdio_ofw_parse(struct rt_ofw_node *dev_np, struct rt_mmcsd_host *host) +{ + rt_uint32_t bus_width = 1; + + if (!dev_np) + { + return -RT_EINVAL; + } + + host->flags = MMCSD_MUTBLKWRITE; + rt_ofw_prop_read_u32(dev_np, "bus-width", &bus_width); + + switch (bus_width) + { + case 0x8: + host->flags |= MMCSD_BUSWIDTH_8; + break; + + case 0x4: + host->flags |= MMCSD_BUSWIDTH_4; + break; + + case 0x1: + break; + + default: + LOG_E("Invalid \"bus-width\" value %d", bus_width); + return -RT_EIO; + } + + rt_ofw_prop_read_u32(dev_np, "max-frequency", &host->freq_max); + + if (rt_ofw_prop_read_bool(dev_np, "non-removable")) + { + host->flags |= MMCSD_SUP_NONREMOVABLE; + } + + if (rt_ofw_prop_read_bool(dev_np, "cap-sdio-irq")) + { + host->flags |= MMCSD_SUP_SDIO_IRQ; + } + + if (rt_ofw_prop_read_bool(dev_np, "cap-sd-highspeed") || + rt_ofw_prop_read_bool(dev_np, "cap-mmc-highspeed")) + { + host->flags |= MMCSD_SUP_HIGHSPEED; + } + + if (rt_ofw_prop_read_bool(dev_np, "mmc-ddr-3_3v")) + { + host->flags |= MMCSD_SUP_DDR_3V3; + } + if (rt_ofw_prop_read_bool(dev_np, "mmc-ddr-1_8v")) + { + host->flags |= MMCSD_SUP_DDR_1V8; + } + if (rt_ofw_prop_read_bool(dev_np, "mmc-ddr-1_2v")) + { + host->flags |= MMCSD_SUP_DDR_1V2; + } + + if (rt_ofw_prop_read_bool(dev_np, "mmc-hs200-1_2v")) + { + host->flags |= MMCSD_SUP_HS200_1V2; + } + if (rt_ofw_prop_read_bool(dev_np, "mmc-hs200-1_8v")) + { + host->flags |= MMCSD_SUP_HS200_1V8; + } + + return RT_EOK; +} +#endif /* RT_USING_OFW */ diff --git a/components/drivers/sdio/sdio_dm.h b/components/drivers/sdio/sdio_dm.h new file mode 100644 index 000000000000..f6da0df9556a --- /dev/null +++ b/components/drivers/sdio/sdio_dm.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-12-06 GuEe-GUI first version + */ + +#ifndef __SDIO_DM_H__ +#define __SDIO_DM_H__ + +#include +#include +#include + +int sdio_host_set_name(struct rt_mmcsd_host *host, char *out_devname); + +#ifdef RT_USING_REGULATOR +rt_err_t sdio_regulator_set_ocr(struct rt_mmcsd_host *host, + struct rt_regulator *supply, rt_uint16_t vdd_bit); +rt_err_t sdio_regulator_set_vqmmc(struct rt_mmcsd_host *host, + struct rt_mmcsd_io_cfg *ios); +rt_err_t sdio_regulator_get_supply(struct rt_device *dev, struct rt_mmcsd_host *host); +rt_err_t sdio_regulator_enable_vqmmc(struct rt_mmcsd_host *host); +void sdio_regulator_disable_vqmmc(struct rt_mmcsd_host *host); +#endif /* RT_USING_REGULATOR */ + +#ifdef RT_USING_OFW +rt_err_t sdio_ofw_parse(struct rt_ofw_node *dev_np, struct rt_mmcsd_host *host); +#else +rt_inline rt_err_t sdio_ofw_parse(struct rt_ofw_node *dev_np, struct rt_mmcsd_host *host) +{ + return RT_EOK; +} +#endif /* RT_USING_OFW */ + +#endif /* __SDIO_DM_H__ */ diff --git a/components/drivers/sensor/sensor.c b/components/drivers/sensor/sensor.c index e100cc27c341..bbe92b2a990b 100644 --- a/components/drivers/sensor/sensor.c +++ b/components/drivers/sensor/sensor.c @@ -358,7 +358,7 @@ static rt_err_t _sensor_control(rt_device_t dev, int cmd, void *args) break; case RT_SENSOR_CTRL_SET_ACCURACY_MODE: /* Configuration sensor power mode */ - mode = (rt_uint32_t)args & 0x000F; + mode = (rt_uint32_t)(rt_ubase_t)args & 0x000F; if (!(mode == RT_SENSOR_MODE_ACCURACY_HIGHEST || mode == RT_SENSOR_MODE_ACCURACY_HIGH ||\ mode == RT_SENSOR_MODE_ACCURACY_MEDIUM || mode == RT_SENSOR_MODE_ACCURACY_LOW ||\ mode == RT_SENSOR_MODE_ACCURACY_LOWEST || mode == RT_SENSOR_MODE_ACCURACY_NOTRUST)) @@ -375,7 +375,7 @@ static rt_err_t _sensor_control(rt_device_t dev, int cmd, void *args) break; case RT_SENSOR_CTRL_SET_POWER_MODE: /* Configuration sensor power mode */ - mode = (rt_uint32_t)args & 0x000F; + mode = (rt_uint32_t)(rt_ubase_t)args & 0x000F; if (!(mode == RT_SENSOR_MODE_POWER_HIGHEST || mode == RT_SENSOR_MODE_POWER_HIGH ||\ mode == RT_SENSOR_MODE_POWER_MEDIUM || mode == RT_SENSOR_MODE_POWER_LOW ||\ mode == RT_SENSOR_MODE_POWER_LOWEST || mode == RT_SENSOR_MODE_POWER_DOWN)) @@ -392,7 +392,7 @@ static rt_err_t _sensor_control(rt_device_t dev, int cmd, void *args) break; case RT_SENSOR_CTRL_SET_FETCH_MODE: /* Configuration sensor power mode */ - mode = (rt_uint32_t)args & 0x000F; + mode = (rt_uint32_t)(rt_ubase_t)args & 0x000F; if (!(mode == RT_SENSOR_MODE_FETCH_POLLING || mode == RT_SENSOR_MODE_FETCH_INT ||\ mode == RT_SENSOR_MODE_FETCH_FIFO)) { diff --git a/components/drivers/sensor/sensor_cmd.c b/components/drivers/sensor/sensor_cmd.c index 4b094fb18d93..88b4faf14d02 100644 --- a/components/drivers/sensor/sensor_cmd.c +++ b/components/drivers/sensor/sensor_cmd.c @@ -713,7 +713,7 @@ static void sensor(int argc, char **argv) else if (argc == 3) { mode = atoi(argv[2]); - if (rt_device_control(dev, RT_SENSOR_CTRL_SET_POWER_MODE, (void *)mode) == RT_EOK) + if (rt_device_control(dev, RT_SENSOR_CTRL_SET_POWER_MODE, (void *)(rt_ubase_t)mode) == RT_EOK) { rt_kprintf("set new power mode as: %s\n", sensor_get_power_mode_name(&sensor->info)); } @@ -745,7 +745,7 @@ static void sensor(int argc, char **argv) else if (argc == 3) { mode = atoi(argv[2]); - if (rt_device_control(dev, RT_SENSOR_CTRL_SET_ACCURACY_MODE, (void *)mode) == RT_EOK) + if (rt_device_control(dev, RT_SENSOR_CTRL_SET_ACCURACY_MODE, (void *)(rt_ubase_t)mode) == RT_EOK) { rt_kprintf("set new accuracy mode as: %s\n", sensor_get_accuracy_mode_name(&sensor->info)); } @@ -777,7 +777,7 @@ static void sensor(int argc, char **argv) else if (argc == 3) { mode = atoi(argv[2]); - if (rt_device_control(dev, RT_SENSOR_CTRL_SET_FETCH_MODE, (void *)mode) == RT_EOK) + if (rt_device_control(dev, RT_SENSOR_CTRL_SET_FETCH_MODE, (void *)(rt_ubase_t)mode) == RT_EOK) { rt_kprintf("set new fetch data mode as: %s\n", sensor_get_fetch_mode_name(&sensor->info)); } diff --git a/components/drivers/serial/8250/8250-bcm2835aux.c b/components/drivers/serial/8250/8250-bcm2835aux.c new file mode 100755 index 000000000000..a7398190426c --- /dev/null +++ b/components/drivers/serial/8250/8250-bcm2835aux.c @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-11-16 GuEe-GUI first version + */ + +#include + +#include "8250.h" + +#define BCM2835_AUX_UART_CNTL 8 +#define BCM2835_AUX_UART_CNTL_RXEN 0x01 /* Receiver enable */ +#define BCM2835_AUX_UART_CNTL_TXEN 0x02 /* Transmitter enable */ +#define BCM2835_AUX_UART_CNTL_AUTORTS 0x04 /* RTS set by RX fill level */ +#define BCM2835_AUX_UART_CNTL_AUTOCTS 0x08 /* CTS stops transmitter */ +#define BCM2835_AUX_UART_CNTL_RTS3 0x00 /* RTS set until 3 chars left */ +#define BCM2835_AUX_UART_CNTL_RTS2 0x10 /* RTS set until 2 chars left */ +#define BCM2835_AUX_UART_CNTL_RTS1 0x20 /* RTS set until 1 chars left */ +#define BCM2835_AUX_UART_CNTL_RTS4 0x30 /* RTS set until 4 chars left */ +#define BCM2835_AUX_UART_CNTL_RTSINV 0x40 /* Invert auto RTS polarity */ +#define BCM2835_AUX_UART_CNTL_CTSINV 0x80 /* Invert auto CTS polarity */ + +struct bcm2835aux +{ + struct serial8250 parent; + rt_uint32_t cntl; +}; + +#define to_bcm2835aux(serial8250) rt_container_of(serial8250, struct bcm2835aux, parent) + +static void bcm2835aux_free_resource(struct bcm2835aux *bcm2835aux) +{ + struct serial8250 *serial = &bcm2835aux->parent; + + if (serial->base) + { + rt_iounmap(serial->base); + } + + if (!rt_is_err_or_null(serial->clk)) + { + rt_clk_disable_unprepare(serial->clk); + rt_clk_put(serial->clk); + } + + rt_free(bcm2835aux); +} + +static void bcm2835aux_serial_remove(struct serial8250 *serial) +{ + struct bcm2835aux *bcm2835aux = to_bcm2835aux(serial); + + bcm2835aux_free_resource(bcm2835aux); +} + +static rt_err_t bcm2835aux_probe(struct rt_platform_device *pdev) +{ + rt_err_t err = RT_EOK; + struct serial8250 *serial; + struct rt_device *dev = &pdev->parent; + struct bcm2835aux *bcm2835aux = serial8250_alloc(bcm2835aux); + struct serial_configure config = RT_SERIAL_CONFIG_DEFAULT; + + if (!bcm2835aux) + { + return -RT_ENOMEM; + } + + serial = &bcm2835aux->parent; + + serial->base = rt_dm_dev_iomap(dev, 0); + + if (serial->base) + { + err = -RT_ERROR; + goto _free_res; + } + + serial->irq = rt_dm_dev_get_irq(dev, 0); + + if (serial->irq < 0) + { + err = serial->irq; + goto _free_res; + } + + serial->clk = rt_clk_get_by_index(dev, 0); + + if (rt_is_err(serial->clk)) + { + err = rt_ptr_err(serial->clk); + goto _free_res; + } + + if ((err = rt_clk_enable(serial->clk))) + { + goto _free_res; + } + + serial->freq = rt_clk_get_rate(serial->clk); + + dev->user_data = serial; + + rt_dm_dev_bind_fwdata(&serial->parent.parent, pdev->parent.ofw_node, &serial->parent); + + serial = &bcm2835aux->parent; + serial->parent.ops = &serial8250_uart_ops; + serial->parent.config = config; + serial->regshift = 2; + serial->iotype = PORT_MMIO; + serial->remove = &bcm2835aux_serial_remove; + serial->data = bcm2835aux; + + bcm2835aux->cntl = BCM2835_AUX_UART_CNTL_RXEN | BCM2835_AUX_UART_CNTL_TXEN; + + if ((err = serial8250_setup(serial))) + { + goto _free_res; + } + + return RT_EOK; + +_free_res: + bcm2835aux_free_resource(bcm2835aux); + + return err; +} + +static rt_err_t bcm2835aux_remove(struct rt_platform_device *pdev) +{ + struct serial8250 *serial = pdev->parent.user_data; + + return serial8250_remove(serial); +} + +static const struct rt_ofw_node_id bcm2835aux_ofw_ids[] = +{ + { .type = "ttyS", .compatible = "brcm,bcm2835-aux-uart" }, + { /* sentinel */ } +}; + +static struct rt_platform_driver bcm2835aux_driver = +{ + .name = "bcm2835-aux-uart", + .ids = bcm2835aux_ofw_ids, + + .probe = bcm2835aux_probe, + .remove = bcm2835aux_remove, +}; + +static int bcm2835aux_drv_register(void) +{ + rt_platform_driver_register(&bcm2835aux_driver); + + return 0; +} +INIT_DRIVER_EARLY_EXPORT(bcm2835aux_drv_register); diff --git a/components/drivers/serial/8250/8250_dw.c b/components/drivers/serial/8250/8250-dw.c similarity index 60% rename from components/drivers/serial/8250/8250_dw.c rename to components/drivers/serial/8250/8250-dw.c index 995769cdf95d..50602521d811 100755 --- a/components/drivers/serial/8250/8250_dw.c +++ b/components/drivers/serial/8250/8250-dw.c @@ -16,12 +16,7 @@ #include -#include - -#include -#include -#include -#include +#include "8250.h" /* Offsets for the DesignWare specific registers */ #define DW_UART_USR 0x1f /* UART Status Register */ @@ -60,6 +55,8 @@ struct dw8250 struct serial8250 parent; struct rt_spinlock spinlock; + struct rt_clk *pclk; + rt_bool_t uart_16550_compatible; struct dw8250_platform_data *platform_data; }; @@ -123,7 +120,10 @@ static rt_err_t dw8250_isr(struct serial8250 *serial, int irq) iir = serial8250_in(serial, UART_IIR); - /* If don't do this in non-DMA mode then the "RX TIMEOUT" interrupt will fire forever. */ + /* + * If don't do this in non-DMA mode then the "RX TIMEOUT" interrupt will + * fire forever. + */ if ((iir & 0x3f) == UART_IIR_RX_TIMEOUT) { rt_base_t level = rt_spin_lock_irqsave(&dw8250->spinlock); @@ -152,66 +152,145 @@ static rt_err_t dw8250_isr(struct serial8250 *serial, int irq) return RT_EOK; } -static void dw8250_remove(struct serial8250 *serial) +static void dw8250_free_resource(struct dw8250 *dw8250) { - rt_free(rt_container_of(serial, struct dw8250, parent)); + struct serial8250 *serial = &dw8250->parent; + + if (serial->base) + { + rt_iounmap(serial->base); + } + + if (!rt_is_err_or_null(serial->clk)) + { + rt_clk_disable_unprepare(serial->clk); + rt_clk_put(serial->clk); + } + + if (!rt_is_err_or_null(dw8250->pclk)) + { + rt_clk_disable_unprepare(dw8250->pclk); + rt_clk_put(dw8250->pclk); + } + + rt_free(dw8250); +} + +static void dw8250_serial_remove(struct serial8250 *serial) +{ + struct dw8250 *dw8250 = to_dw8250(serial); + + dw8250_free_resource(dw8250); } -static rt_err_t dw8250_probe(struct rt_device_node *node, const struct rt_of_device_id *id) +static rt_err_t dw8250_probe(struct rt_platform_device *pdev) { - rt_err_t ret = RT_EOK; - struct serial8250 *serial = RT_NULL; + rt_err_t err; + rt_uint32_t val; + struct serial8250 *serial; + struct rt_device *dev = &pdev->parent; struct dw8250 *dw8250 = serial8250_alloc(dw8250); + struct serial_configure config = RT_SERIAL_CONFIG_DEFAULT; - if (dw8250) + if (!dw8250) { - struct serial_configure config = RT_SERIAL_CONFIG_DEFAULT; + return -RT_ENOMEM; + } + + serial = &dw8250->parent; + serial->base = rt_dm_dev_iomap(dev, 0); - // TODO: remove - config.baud_rate = 1500000; - serial = &dw8250->parent; - serial->base = rt_of_iomap(node, 0); + if (!serial->base) + { + err = -RT_EIO; + + goto _free_res; + } - if (serial->base) + serial->irq = rt_dm_dev_get_irq(dev, 0); + + if (serial->irq < 0) + { + err = serial->irq; + + goto _free_res; + } + + serial->clk = rt_clk_get_by_name(dev, "baudclk"); + dw8250->pclk = rt_clk_get_by_name(dev, "apb_pclk"); + + if (rt_is_err_or_null(serial->clk)) + { + if ((err = rt_dm_dev_prop_read_u32(dev, "clock-frequency", &serial->freq))) { - rt_uint32_t val; - - serial->irq = rt_of_irq_get(node, 0); - - serial->parent.ops = &serial8250_uart_ops; - serial->parent.config = config; - // TODO "clock-frequency" - serial->freq = 24000000; - rt_of_property_read_u32(node, "reg-shift", &serial->regshift); - if (!rt_of_property_read_u32(node, "reg-io-width", &val) && val == 4) - { - serial->iotype = PORT_MMIO32; - serial->serial_in = &dw8250_serial_in32; - serial->serial_out = &dw8250_serial_out32; - } - serial->handle_irq = &dw8250_isr; - serial->remove = &dw8250_remove; - serial->data = dw8250; - - rt_spin_lock_init(&dw8250->spinlock); - dw8250->uart_16550_compatible = rt_of_property_read_bool(node, "snps,uart-16550-compatible"); - dw8250->platform_data = (struct dw8250_platform_data *)id->data; - rt_of_data(node) = &serial->parent; - - ret = serial8250_setup(serial); + goto _free_res; } - else + } + else + { + if ((err = rt_clk_prepare_enable(serial->clk))) { - rt_free(dw8250); - ret = -RT_ERROR; + goto _free_res; } + + serial->freq = rt_clk_get_rate(serial->clk); } - else + + if (rt_is_err(dw8250->pclk)) { - ret = -RT_ENOMEM; + err = rt_ptr_err(dw8250->pclk); + + goto _free_res; } - return ret; + if ((err = rt_clk_prepare_enable(dw8250->pclk))) + { + goto _free_res; + } + + rt_dm_dev_prop_read_u32(dev, "reg-shift", &serial->regshift); + + if (!rt_dm_dev_prop_read_u32(dev, "reg-io-width", &val) && val == 4) + { + serial->iotype = PORT_MMIO32; + serial->serial_in = &dw8250_serial_in32; + serial->serial_out = &dw8250_serial_out32; + } + + dw8250->uart_16550_compatible = rt_dm_dev_prop_read_bool(dev, "snps,uart-16550-compatible"); + dw8250->platform_data = (struct dw8250_platform_data *)pdev->id->data; + + rt_dm_dev_bind_fwdata(&serial->parent.parent, pdev->parent.ofw_node, &serial->parent); + + dev->user_data = serial; + + serial = &dw8250->parent; + serial->parent.ops = &serial8250_uart_ops; + serial->parent.config = config; + serial->handle_irq = &dw8250_isr; + serial->remove = &dw8250_serial_remove; + serial->data = dw8250; + + rt_spin_lock_init(&dw8250->spinlock); + + if ((err = serial8250_setup(serial))) + { + goto _free_res; + } + + return RT_EOK; + +_free_res: + dw8250_free_resource(dw8250); + + return err; +} + +static rt_err_t dw8250_remove(struct rt_platform_device *pdev) +{ + struct serial8250 *serial = pdev->parent.user_data; + + return serial8250_remove(serial); } static const struct dw8250_platform_data dw8250_dw_apb = @@ -244,7 +323,7 @@ static const struct dw8250_platform_data dw8250_starfive_jh7100_data = .quirks = DW_UART_QUIRK_SKIP_SET_RATE, }; -static const struct rt_of_device_id dw8250_of_match[] = +static const struct rt_ofw_node_id dw8250_ofw_ids[] = { { .type = "ttyS", .compatible = "snps,dw-apb-uart", .data = &dw8250_dw_apb }, { .type = "ttyS", .compatible = "cavium,octeon-3860-uart", .data = &dw8250_octeon_3860_data }, @@ -253,4 +332,20 @@ static const struct rt_of_device_id dw8250_of_match[] = { .type = "ttyS", .compatible = "starfive,jh7100-uart", .data = &dw8250_starfive_jh7100_data }, { /* sentinel */ } }; -RT_OF_DECLARE_DEVICE(dw8250_of_match, dw8250_probe); + +static struct rt_platform_driver dw8250_driver = +{ + .name = "dw-apb-uart", + .ids = dw8250_ofw_ids, + + .probe = dw8250_probe, + .remove = dw8250_remove, +}; + +static int dw8250_drv_register(void) +{ + rt_platform_driver_register(&dw8250_driver); + + return 0; +} +INIT_DRIVER_EARLY_EXPORT(dw8250_drv_register); diff --git a/components/drivers/serial/8250/8250-ofw.c b/components/drivers/serial/8250/8250-ofw.c new file mode 100644 index 000000000000..0722f37be29c --- /dev/null +++ b/components/drivers/serial/8250/8250-ofw.c @@ -0,0 +1,205 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-11-09 GuEe-GUI first version + */ + +#include + +#include "8250.h" + +struct ofw_platform_8250 +{ + struct serial8250 parent; + + struct rt_reset_control *rstc; +}; + +#define to_ofw_platform_8250(serial8250) rt_container_of(serial8250, struct ofw_platform_8250, parent) + +static void ofw_platform_8250_free_resource(struct ofw_platform_8250 *op8250) +{ + struct serial8250 *serial = &op8250->parent; + + if (serial->base) + { + rt_iounmap(serial->base); + } + + if (!rt_is_err_or_null(serial->clk)) + { + rt_clk_disable_unprepare(serial->clk); + rt_clk_put(serial->clk); + } + + if (!rt_is_err_or_null(op8250->rstc)) + { + rt_reset_control_put(op8250->rstc); + } + + rt_free(op8250); +} + +static void op8250_remove(struct serial8250 *serial) +{ + struct ofw_platform_8250 *op8250 = to_ofw_platform_8250(serial); + + ofw_platform_8250_free_resource(op8250); +} + +static rt_err_t ofw_platform_8250_probe(struct rt_platform_device *pdev) +{ + rt_err_t err; + rt_uint32_t val; + struct serial8250 *serial; + struct ofw_platform_8250 *op8250; + struct rt_ofw_node *np = pdev->parent.ofw_node; + struct serial_configure config = RT_SERIAL_CONFIG_DEFAULT; + + if (rt_ofw_prop_read_bool(np, "used-by-rtas")) + { + return -RT_EBUSY; + } + + op8250 = serial8250_alloc(op8250); + + if (!op8250) + { + return -RT_ENOMEM; + } + + serial = &op8250->parent; + serial->base = rt_ofw_iomap(np, 0); + + if (!serial->base) + { + err = -RT_EIO; + + goto _fail; + } + + serial->irq = rt_ofw_get_irq(np, 0); + + if (serial->irq < 0) + { + err = serial->irq; + + goto _fail; + } + + if (!rt_ofw_prop_read_u32(np, "clock-frequency", &val)) + { + serial->freq = val; + } + else + { + serial->clk = rt_ofw_get_clk(np, 0); + + if (rt_is_err(serial->clk)) + { + err = rt_ptr_err(serial->clk); + goto _fail; + } + + if ((err = rt_clk_prepare_enable(serial->clk))) + { + goto _fail; + } + + serial->freq = rt_clk_get_rate(serial->clk); + } + + if (!rt_ofw_prop_read_u32(np, "reg-shift", &val)) + { + serial->regshift = val; + } + + serial->iotype = PORT_MMIO; + if (!rt_ofw_prop_read_u32(np, "reg-io-width", &val)) + { + switch (val) + { + case 1: + serial->iotype = PORT_MMIO; + break; + + case 2: + serial->iotype = PORT_MMIO16; + break; + + case 4: + serial->iotype = rt_ofw_prop_read_bool(np, "big-endian") ? + PORT_MMIO32BE : PORT_MMIO32; + break; + } + } + + if (rt_ofw_prop_read_bool(np, "resets")) + { + op8250->rstc = rt_ofw_get_reset_control_by_index(np, 0); + + if (!op8250->rstc) + { + err = -RT_EIO; + + goto _fail; + } + + if ((err = rt_reset_control_deassert(op8250->rstc))) + { + goto _fail; + } + } + + rt_dm_dev_bind_fwdata(&serial->parent.parent, pdev->parent.ofw_node, &serial->parent); + + pdev->parent.user_data = serial; + + serial = &op8250->parent; + serial->parent.ops = &serial8250_uart_ops; + serial->parent.config = config; + serial->remove = &op8250_remove; + serial->data = op8250; + + return RT_EOK; + +_fail: + ofw_platform_8250_free_resource(op8250); + + return err; +} + +static rt_err_t ofw_platform_8250_remove(struct rt_platform_device *pdev) +{ + struct serial8250 *serial = pdev->parent.user_data; + + return serial8250_remove(serial); +} + +static const struct rt_ofw_node_id ofw_platform_8250_ofw_ids[] = +{ + { .type = "ttyS", .compatible = "ns16550a" }, + { .type = "ttyS", .compatible = "ns16550" }, + { /* sentinel */ } +}; + +static struct rt_platform_driver ofw_platform_8250_driver = +{ + .name = "8250-ofw", + .ids = ofw_platform_8250_ofw_ids, + + .probe = ofw_platform_8250_probe, + .remove = ofw_platform_8250_remove, +}; + +static int ofw_platform_8250_drv_register(void) +{ + rt_platform_driver_register(&ofw_platform_8250_driver); + + return 0; +} +INIT_DRIVER_EARLY_EXPORT(ofw_platform_8250_drv_register); diff --git a/components/drivers/serial/8250/8250-pci.c b/components/drivers/serial/8250/8250-pci.c new file mode 100644 index 000000000000..b1bc2b95eabb --- /dev/null +++ b/components/drivers/serial/8250/8250-pci.c @@ -0,0 +1,177 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-11-09 GuEe-GUI first version + */ + +#include + +#include "8250.h" + +#define IO_PORT_BAR 0 + +#define DW_UART_USR 0x1f /* UART Status Register */ + +enum +{ + PCI_SERIAL = 0, + PCI_SERIAL2 = 2, + PCI_SERIAL4 = 4, +}; + +enum +{ + SERIAL_8250 = 0, + SERIAL_16450, + SERIAL_16550, + SERIAL_16650, + SERIAL_16750, + SERIAL_16850, + SERIAL_16950, +}; + +struct pci_serial +{ + struct serial8250 parent; + struct rt_spinlock spinlock; + + struct rt_pci_device *pci_dev; + + rt_uint8_t type; + rt_uint8_t compat; +}; + +#define to_pci_serial(raw) rt_container_of(raw, struct pci_serial, parent) + +static rt_err_t pci_serial_isr(struct serial8250 *serial, int irq) +{ + rt_uint32_t iir; + void *base = serial->base; + + iir = HWREG8(base) = UART_IIR; + + if (!(iir & UART_IIR_NO_INT)) + { + rt_hw_serial_isr(&serial->parent, RT_SERIAL_EVENT_RX_IND); + } + + if ((iir & UART_IIR_BUSY) == UART_IIR_BUSY) + { + /* Clear the USR */ + HWREG8(base) = DW_UART_USR; + } + + return RT_EOK; +} + +static rt_ubase_t pci_serial_clock(struct pci_serial *pci_serial) +{ + rt_ubase_t clock = 1843200; + + return clock; +} + +static void pci_serial_free_resource(struct pci_serial *pci_serial) +{ + struct serial8250 *serial = &pci_serial->parent; + + if (serial->base) + { + rt_iounmap(serial->base); + } + + rt_free(pci_serial); +} + +static void pci_8250serial_remove(struct serial8250 *serial) +{ + struct pci_serial *pci_serial = to_pci_serial(serial); + + rt_pci_irq_mask(pci_serial->pci_dev); + + pci_serial_free_resource(pci_serial); +} + +static rt_err_t pci_serial_probe(struct rt_pci_device *pdev) +{ + rt_err_t err; + struct serial8250 *serial; + struct pci_serial *pci_serial = serial8250_alloc(pci_serial); + struct serial_configure config = RT_SERIAL_CONFIG_DEFAULT; + + if (!pci_serial) + { + return -RT_ENOMEM; + } + + serial = &pci_serial->parent; + serial->size = pdev->resource[IO_PORT_BAR].size; + serial->base = rt_pci_iomap(pdev, IO_PORT_BAR); + + if (!serial->base) + { + err = -RT_EIO; + + goto _free_res; + } + + pdev->parent.user_data = serial; + + serial->irq = pdev->irq; + + serial->parent.ops = &serial8250_uart_ops; + serial->parent.config = config; + serial->freq = pci_serial_clock(pci_serial); + serial->handle_irq = &pci_serial_isr; + serial->iotype = PORT_MMIO; + serial->remove = &pci_8250serial_remove; + serial->data = pci_serial; + + pci_serial->pci_dev = pdev; + pci_serial->type = (rt_ubase_t)pdev->id->data; + rt_spin_lock_init(&pci_serial->spinlock); + rt_pci_read_config_u8(pdev, PCIR_PROGIF, &pci_serial->compat); + + if ((err = serial8250_setup(serial))) + { + goto _free_res; + } + + rt_pci_irq_unmask(pci_serial->pci_dev); + + return RT_EOK; + +_free_res: + pci_serial_free_resource(pci_serial); + + return err; +} + +static rt_err_t pci_serial_remove(struct rt_pci_device *pdev) +{ + struct serial8250 *serial = pdev->parent.user_data; + + return serial8250_remove(serial); +} + +static struct rt_pci_device_id pci_serial_pci_ids[] = +{ + { RT_PCI_DEVICE_ID(PCI_VENDOR_ID_REDHAT, 0x0002), .data = (void *)PCI_SERIAL, }, + { RT_PCI_DEVICE_ID(PCI_VENDOR_ID_REDHAT, 0x0003), .data = (void *)PCI_SERIAL2, }, + { RT_PCI_DEVICE_ID(PCI_VENDOR_ID_REDHAT, 0x0004), .data = (void *)PCI_SERIAL4, }, + { /* sentinel */ } +}; + +static struct rt_pci_driver pci_serial_driver = +{ + .name = "pci-serial", + + .ids = pci_serial_pci_ids, + .probe = pci_serial_probe, + .remove = pci_serial_remove, +}; +RT_PCI_DRIVER_EXPORT(pci_serial_driver); diff --git a/components/drivers/serial/8250/serial8250.h b/components/drivers/serial/8250/8250.h similarity index 83% rename from components/drivers/serial/8250/serial8250.h rename to components/drivers/serial/8250/8250.h index dfb4358369bd..8582d8429486 100755 --- a/components/drivers/serial/8250/serial8250.h +++ b/components/drivers/serial/8250/8250.h @@ -8,16 +8,17 @@ * 2022-11-16 GuEe-GUI first version */ -#ifndef __8250_SERIAL_H__ -#define __8250_SERIAL_H__ +#ifndef __SERIAL_8250_H__ +#define __SERIAL_8250_H__ #include #include #include -#include -#include -#include +#include + +#include "regs.h" +#include "../serial_dm.h" enum { @@ -31,7 +32,7 @@ enum struct serial8250 { struct rt_serial_device parent; - struct clk *clk; + struct rt_clk *clk; int irq; void *base; @@ -40,7 +41,7 @@ struct serial8250 rt_uint32_t regshift; /* reg offset shift */ rt_uint8_t iotype; /* io access style */ - rt_hw_spinlock_t spinlock; + struct rt_spinlock spinlock; rt_uint32_t (*serial_in)(struct serial8250 *, int offset); void (*serial_out)(struct serial8250 *, int offset, int value); @@ -56,6 +57,7 @@ struct serial8250 rt_err_t serial8250_config(struct serial8250 *serial, const char *options); rt_err_t serial8250_setup(struct serial8250 *serial); +rt_err_t serial8250_remove(struct serial8250 *serial); rt_uint32_t serial8250_in(struct serial8250 *serial, int offset); void serial8250_out(struct serial8250 *serial, int offset, int value); @@ -66,9 +68,9 @@ int serial8250_uart_putc(struct rt_serial_device *raw_serial, char c); int serial8250_uart_getc(struct rt_serial_device *raw_serial); int serial8250_early_putc(struct rt_serial_device *raw_serial, char c); -rt_err_t serial8250_early_of_setup(struct serial8250 *serial, struct rt_of_earlycon_desc *desc, const char *options); +rt_err_t serial8250_early_fdt_setup(struct serial8250 *serial, struct rt_fdt_earlycon *con, const char *options); extern struct serial8250 early_serial8250; extern const struct rt_uart_ops serial8250_uart_ops; -#endif /* __8250_SERIAL_H__ */ +#endif /* __SERIAL_8250_H__ */ diff --git a/components/drivers/serial/8250/8250_bcm2835aux.c b/components/drivers/serial/8250/8250_bcm2835aux.c deleted file mode 100755 index 5ac34a825f35..000000000000 --- a/components/drivers/serial/8250/8250_bcm2835aux.c +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright (c) 2006-2022, RT-Thread Development Team - * - * SPDX-License-Identifier: Apache-2.0 - * - * Change Logs: - * Date Author Notes - * 2022-11-16 GuEe-GUI first version - */ - -#include - -#include - -#include -#include -#include -#include - -#define BCM2835_AUX_UART_CNTL 8 -#define BCM2835_AUX_UART_CNTL_RXEN 0x01 /* Receiver enable */ -#define BCM2835_AUX_UART_CNTL_TXEN 0x02 /* Transmitter enable */ -#define BCM2835_AUX_UART_CNTL_AUTORTS 0x04 /* RTS set by RX fill level */ -#define BCM2835_AUX_UART_CNTL_AUTOCTS 0x08 /* CTS stops transmitter */ -#define BCM2835_AUX_UART_CNTL_RTS3 0x00 /* RTS set until 3 chars left */ -#define BCM2835_AUX_UART_CNTL_RTS2 0x10 /* RTS set until 2 chars left */ -#define BCM2835_AUX_UART_CNTL_RTS1 0x20 /* RTS set until 1 chars left */ -#define BCM2835_AUX_UART_CNTL_RTS4 0x30 /* RTS set until 4 chars left */ -#define BCM2835_AUX_UART_CNTL_RTSINV 0x40 /* Invert auto RTS polarity */ -#define BCM2835_AUX_UART_CNTL_CTSINV 0x80 /* Invert auto CTS polarity */ - -struct bcm2835aux -{ - struct serial8250 parent; - rt_uint32_t cntl; -}; - -static void bcm2835aux_remove(struct serial8250 *serial) -{ - rt_free(rt_container_of(serial, struct bcm2835aux, parent)); -} - -static rt_err_t bcm2835aux_probe(struct rt_device_node *node, const struct rt_of_device_id *id) -{ - rt_err_t ret = RT_EOK; - struct serial8250 *serial = RT_NULL; - struct bcm2835aux *bcm2835aux = serial8250_alloc(bcm2835aux); - - if (bcm2835aux) - { - struct serial_configure config = RT_SERIAL_CONFIG_DEFAULT; - - serial = &bcm2835aux->parent; - serial->base = rt_of_iomap(node, 0); - - if (serial->base) - { - serial->clk = clk_of_get(node, 0); - clk_enable(serial->clk); - serial->freq = clk_get_rate(serial->clk); - - if (!serial->freq) - { - rt_iounmap(serial->base); - serial->base = RT_NULL; - } - } - - if (serial->base) - { - serial->irq = rt_of_irq_get(node, 0); - - serial->parent.ops = &serial8250_uart_ops; - serial->parent.config = config; - serial->regshift = 2; - serial->iotype = PORT_MMIO; - serial->remove = &bcm2835aux_remove; - serial->data = bcm2835aux; - - bcm2835aux->cntl = BCM2835_AUX_UART_CNTL_RXEN | BCM2835_AUX_UART_CNTL_TXEN; - rt_of_data(node) = &serial->parent; - - ret = serial8250_setup(serial); - } - else - { - rt_free(bcm2835aux); - ret = -RT_ERROR; - } - } - else - { - ret = -RT_ENOMEM; - } - - return ret; -} - -static const struct rt_of_device_id bcm2835aux_of_match[] = -{ - { .type = "ttyS", .compatible = "brcm,bcm2835-aux-uart" }, - { /* sentinel */ } -}; -RT_OF_DECLARE_DEVICE(bcm2835aux_of_match, bcm2835aux_probe); diff --git a/components/drivers/serial/8250/8250_pci.c b/components/drivers/serial/8250/8250_pci.c deleted file mode 100755 index 909dec9e6f5e..000000000000 --- a/components/drivers/serial/8250/8250_pci.c +++ /dev/null @@ -1,166 +0,0 @@ -/* - * Copyright (c) 2006-2022, RT-Thread Development Team - * - * SPDX-License-Identifier: Apache-2.0 - * - * Change Logs: - * Date Author Notes - * 2022-11-09 GuEe-GUI first version - */ - -#include -#include -#include - -#include -#include - -#include -#include -#include - -#define IO_PORT_BAR 0 - -#define DW_UART_USR 0x1f /* UART Status Register */ - -enum -{ - PCI_SERIAL = 0, - PCI_SERIAL2 = 2, - PCI_SERIAL4 = 4, -}; - -enum -{ - SERIAL_8250 = 0, - SERIAL_16450, - SERIAL_16550, - SERIAL_16650, - SERIAL_16750, - SERIAL_16850, - SERIAL_16950, -}; - -struct pci_serial -{ - struct serial8250 parent; - struct rt_spinlock spinlock; - - struct pci_device *dev; - - rt_uint8_t type; - rt_uint8_t compat; -}; - -#define raw_to_pci_serial(raw) rt_container_of(raw, struct pci_serial, parent) - -static rt_err_t pci_serial_isr(struct serial8250 *serial, int irq) -{ - unsigned int iir; - void *base = serial->base; - - iir = HWREG8(base) = UART_IIR; - - if (!(iir & UART_IIR_NO_INT)) - { - rt_hw_serial_isr(&serial->parent, RT_SERIAL_EVENT_RX_IND); - } - - if ((iir & UART_IIR_BUSY) == UART_IIR_BUSY) - { - /* Clear the USR */ - HWREG8(base) = DW_UART_USR; - } - - return RT_EOK; -} - -static void pci_serial_8250_remove(struct serial8250 *serial) -{ - struct pci_serial *pci_serial = raw_to_pci_serial(serial); - - pci_serial->dev->dev = RT_NULL; - rt_free(pci_serial); -} - -static rt_err_t pci_serial_probe(struct pci_device *dev, const struct pci_device_id *id) -{ - rt_err_t ret = RT_EOK; - struct serial8250 *serial = RT_NULL; - struct pci_serial *pci_serial = serial8250_alloc(pci_serial); - - if (pci_serial) - { - struct serial_configure config = RT_SERIAL_CONFIG_DEFAULT; - - serial = &pci_serial->parent; - serial->size = dev->resource[IO_PORT_BAR].size; - serial->base = rt_ioremap((void *)dev->resource[IO_PORT_BAR].base, serial->size); - - if (serial->base) - { - serial->irq = dev->irq; - - serial->parent.ops = &serial8250_uart_ops; - serial->parent.config = config; - if (dev->bus->cur_bus_speed == PCI_SPEED_UNKNOWN) - { - serial->freq = 1843200; - } - else - { - /* TODO */ - } - serial->handle_irq = &pci_serial_isr; - serial->iotype = PORT_MMIO; - serial->remove = &pci_serial_8250_remove; - serial->data = pci_serial; - - pci_serial->dev = dev; - pci_serial->type = (rt_ubase_t)id->data; - rt_spin_lock_init(&pci_serial->spinlock); - dev->dev = (typeof(dev->dev))&pci_serial->parent; - pci_read_config_byte(dev, PCI_CLASS_PROG, &pci_serial->compat); - - ret = serial8250_setup(serial); - - if (!ret) - { - pci_intx(pci_serial->dev, RT_TRUE); - } - } - else - { - rt_free(pci_serial); - ret = -RT_EIO; - } - } - else - { - ret = -RT_ENOMEM; - } - - return ret; -} - -static void pci_serial_remove(struct pci_device *dev) -{ - pci_serial_8250_remove((struct serial8250 *)dev->dev); -} - -static struct pci_device_id pci_serial_pci_ids[] = -{ - { PCI_DEVICE(PCI_VENDOR_ID_REDHAT, 0x0002), .data = (void *)PCI_SERIAL, }, - { PCI_DEVICE(PCI_VENDOR_ID_REDHAT, 0x0003), .data = (void *)PCI_SERIAL2, }, - { PCI_DEVICE(PCI_VENDOR_ID_REDHAT, 0x0004), .data = (void *)PCI_SERIAL4, }, - { /* sentinel */ } -}; - -static struct pci_driver pci_serial_drv = -{ - .name = "pci-serial", - .id_table = pci_serial_pci_ids, - .probe = pci_serial_probe, - .remove = pci_serial_remove, -}; -PCI_DECLARE(pci_serial, pci_serial_drv); diff --git a/components/drivers/serial/8250/Kconfig b/components/drivers/serial/8250/Kconfig new file mode 100644 index 000000000000..ed0205e519c8 --- /dev/null +++ b/components/drivers/serial/8250/Kconfig @@ -0,0 +1,26 @@ +menuconfig RT_SERIAL_8250 + bool "8250 Serila Family" + default n + +config RT_SERIAL_8250_BCM2835AUX + bool "BCM2835 auxiliar mini UART" + depends on RT_SERIAL_8250 + default n + +config RT_SERIAL_8250_DW + bool "Synopsys DesignWare 8250" + depends on RT_SERIAL_8250 + default n + +config RT_SERIAL_8250_OFW + bool "8250 Platform" + depends on RT_SERIAL_8250 + select RT_USING_OFW + select RT_USING_RESET + default n + +config RT_SERIAL_8250_PCI + bool "8250 PCI/2x/4x" + depends on RT_SERIAL_8250 + select RT_USING_PCI + default n diff --git a/components/drivers/serial/8250/SConscript b/components/drivers/serial/8250/SConscript index 6fdad029dc48..16ec680a3583 100755 --- a/components/drivers/serial/8250/SConscript +++ b/components/drivers/serial/8250/SConscript @@ -1,12 +1,26 @@ from building import * -cwd = GetCurrentDir() -src = [] -CPPPATH = [cwd + '/../../include'] group = [] -if not GetDepend(['RT_USING_SERIAL_8250']): +if not GetDepend(['RT_SERIAL_8250']): Return('group') +cwd = GetCurrentDir() +CPPPATH = [cwd + '/../../include'] + +src = ['core.c', 'early.c'] + +if GetDepend(['RT_SERIAL_8250_BCM2835AUX']): + src += ['8250-bcm2835aux.c'] + +if GetDepend(['RT_SERIAL_8250_DW']): + src += ['8250-dw.c'] + +if GetDepend(['RT_SERIAL_8250_OFW']): + src += ['8250-ofw.c'] + +if GetDepend(['RT_SERIAL_8250_PCI']): + src += ['8250-pci.c'] + group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) Return('group') diff --git a/components/drivers/serial/8250/core.c b/components/drivers/serial/8250/core.c index 30d7fa336d00..8765a267cf66 100755 --- a/components/drivers/serial/8250/core.c +++ b/components/drivers/serial/8250/core.c @@ -8,11 +8,7 @@ * 2022-11-16 GuEe-GUI first version */ -#include - -#include -#include -#include +#include "8250.h" rt_err_t serial8250_config(struct serial8250 *serial, const char *options) { @@ -38,7 +34,7 @@ rt_err_t serial8250_config(struct serial8250 *serial, const char *options) ret = RT_EOK; continue; } - /* user call error */ + /* User call error */ if (ret) { break; @@ -65,7 +61,7 @@ rt_err_t serial8250_config(struct serial8250 *serial, const char *options) serial->iotype = PORT_MMIO32; - for (int i = 0; i < rt_array_size(iotype_table); ++i) + for (int i = 0; i < RT_ARRAY_SIZE(iotype_table); ++i) { if (!rt_strcmp(arg, iotype_table[i].param)) { @@ -108,18 +104,22 @@ static void serial8250_isr(int irqno, void *param) rt_err_t serial8250_setup(struct serial8250 *serial) { rt_err_t ret = RT_EOK; + const char *uart_name; char dev_name[RT_NAME_MAX]; if (serial) { - rt_hw_spin_lock_init(&serial->spinlock); + rt_spin_lock_init(&serial->spinlock); + + serial->serial_in = serial->serial_in ? : &serial8250_in; + serial->serial_out = serial->serial_out ? : &serial8250_out; - serial->serial_in = serial->serial_in ? serial->serial_in : &serial8250_in; - serial->serial_out = serial->serial_out ? serial->serial_out : &serial8250_out; + serial_dev_set_name(&serial->parent); + uart_name = rt_dm_dev_get_name(&serial->parent.parent); - rt_sprintf(dev_name, "uart%d", serial_next_id()); + rt_hw_serial_register(&serial->parent, uart_name, RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX, serial->data); - rt_hw_serial_register(&serial->parent, dev_name, RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX, serial->data); + rt_snprintf(dev_name, sizeof(dev_name), "%s-8250", uart_name); rt_hw_interrupt_install(serial->irq, serial8250_isr, serial, dev_name); } else @@ -130,6 +130,26 @@ rt_err_t serial8250_setup(struct serial8250 *serial) return ret; } +rt_err_t serial8250_remove(struct serial8250 *serial) +{ + rt_err_t err; + + rt_iounmap((void *)serial->base); + serial->base = RT_NULL; + + rt_hw_interrupt_mask(serial->irq); + rt_pic_detach_irq(serial->irq, serial); + + err = rt_device_unregister(&serial->parent.parent); + + if (!err && serial->remove) + { + serial->remove(serial); + } + + return err; +} + rt_uint32_t serial8250_in(struct serial8250 *serial, int offset) { rt_uint32_t ret = 0; @@ -191,78 +211,61 @@ void serial8250_out(struct serial8250 *serial, int offset, int value) rt_err_t serial8250_uart_configure(struct rt_serial_device *raw_serial, struct serial_configure *cfg) { - rt_err_t ret = RT_EOK; + rt_err_t err = RT_EOK; struct serial8250 *serial = raw_to_serial8250(raw_serial); + /* Disable interrupt */ serial->serial_out(serial, UART_IER, !UART_IER_RDI); - /* Enable FIFO, Clear FIFO*/ + /* Enable FIFO, Clear FIFO */ serial->serial_out(serial, UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); - /* Disable interrupt */ - serial->serial_out(serial, UART_IER, 0); - /* DTR + RTS */ - serial->serial_out(serial, UART_MCR, 0x3); + serial->serial_out(serial, UART_MCR, UART_MCR_DTR | UART_MCR_RTS); + if (serial->freq) { - unsigned char lcr = serial8250_in(serial, UART_LCR); - rt_uint32_t wlen = cfg->data_bits + (UART_LCR_WLEN5 - DATA_BITS_5); - #if 0 + rt_uint32_t wlen = cfg->data_bits - DATA_BITS_5 + UART_LCR_WLEN5; rt_uint32_t divisor = serial->freq / 16 / cfg->baud_rate; /* Enable access DLL & DLH */ - serial->serial_out(serial, UART_LCR, lcr | UART_LCR_DLAB); + serial->serial_out(serial, UART_LCR, serial->serial_in(serial, UART_LCR) | UART_LCR_DLAB); serial->serial_out(serial, UART_DLL, (divisor & 0xff)); serial->serial_out(serial, UART_DLM, (divisor >> 8) & 0xff); /* Clear DLAB bit */ - serial->serial_out(serial, UART_LCR, lcr & (~UART_LCR_DLAB)); - #endif + serial->serial_out(serial, UART_LCR, serial->serial_in(serial, UART_LCR) & (~UART_LCR_DLAB)); - serial->serial_out(serial, UART_LCR, (lcr & (~wlen)) | wlen); - serial->serial_out(serial, UART_LCR, lcr & (~UART_LCR_STOP)); - serial->serial_out(serial, UART_LCR, lcr & (~UART_LCR_PARITY)); + serial->serial_out(serial, UART_LCR, (serial->serial_in(serial, UART_LCR) & (~wlen)) | wlen); + serial->serial_out(serial, UART_LCR, serial->serial_in(serial, UART_LCR) & (~UART_LCR_STOP)); + serial->serial_out(serial, UART_LCR, serial->serial_in(serial, UART_LCR) & (~UART_LCR_PARITY)); } serial->serial_out(serial, UART_IER, UART_IER_RDI); - return ret; + return err; } rt_err_t serial8250_uart_control(struct rt_serial_device *raw_serial, int cmd, void *arg) { - rt_err_t ret = RT_EOK; + rt_err_t err = RT_EOK; struct serial8250 *serial = raw_to_serial8250(raw_serial); switch (cmd) { - case RT_DEVICE_CTRL_CLR_INT: - /* disable rx irq */ - serial->serial_out(serial, UART_IER, !UART_IER_RDI); - rt_hw_interrupt_mask(serial->irq); - break; - - case RT_DEVICE_CTRL_SET_INT: - /* enable rx irq */ - serial->serial_out(serial, UART_IER, UART_IER_RDI); - rt_hw_interrupt_umask(serial->irq); - break; - - case RT_DEVICE_CTRL_SHUTDOWN: - rt_iounmap((void *)serial->base); - rt_hw_interrupt_mask(serial->irq); - rt_hw_interrupt_uninstall(serial->irq, serial8250_isr, serial); - - rt_device_unregister(&serial->parent.parent); + case RT_DEVICE_CTRL_CLR_INT: + /* Disable rx irq */ + serial->serial_out(serial, UART_IER, !UART_IER_RDI); + rt_hw_interrupt_mask(serial->irq); + break; - if (serial->remove) - { - serial->remove(serial); - } - break; + case RT_DEVICE_CTRL_SET_INT: + /* Enable rx irq */ + serial->serial_out(serial, UART_IER, UART_IER_RDI); + rt_hw_interrupt_umask(serial->irq); + break; } - return ret; + return err; } int serial8250_uart_putc(struct rt_serial_device *raw_serial, char c) @@ -271,6 +274,7 @@ int serial8250_uart_putc(struct rt_serial_device *raw_serial, char c) while (!(serial->serial_in(serial, UART_LSR) & 0x20)) { + rt_hw_cpu_relax(); } serial->serial_out(serial, UART_TX, c); @@ -293,8 +297,8 @@ int serial8250_uart_getc(struct rt_serial_device *raw_serial) const struct rt_uart_ops serial8250_uart_ops = { - serial8250_uart_configure, - serial8250_uart_control, - serial8250_uart_putc, - serial8250_uart_getc, + .configure = serial8250_uart_configure, + .control = serial8250_uart_control, + .putc = serial8250_uart_putc, + .getc = serial8250_uart_getc, }; diff --git a/components/drivers/serial/8250/early.c b/components/drivers/serial/8250/early.c index 047bf7fd447a..75fdb178736d 100755 --- a/components/drivers/serial/8250/early.c +++ b/components/drivers/serial/8250/early.c @@ -8,14 +8,7 @@ * 2022-11-16 GuEe-GUI first version */ -#include -#include - -#include -#include - -#include -#include +#include "8250.h" struct serial8250 early_serial8250 = { 0 }; @@ -55,8 +48,8 @@ static void init_serial(struct serial8250 *serial) serial8250_early_out(serial, UART_LCR, 0x3); /* 8n1 */ ier = serial8250_early_in(serial, UART_IER); - serial8250_early_out(serial, UART_IER, ier & UART_IER_UUE); /* no interrupt */ - serial8250_early_out(serial, UART_FCR, 0); /* no fifo */ + serial8250_early_out(serial, UART_IER, ier & UART_IER_UUE); /* No interrupt */ + serial8250_early_out(serial, UART_FCR, 0); /* No fifo */ serial8250_early_out(serial, UART_MCR, 0x3); /* DTR + RTS */ if (serial->freq) @@ -70,30 +63,50 @@ static void init_serial(struct serial8250 *serial) } } -rt_err_t serial8250_early_of_setup(struct serial8250 *serial, struct rt_of_earlycon_desc *desc, const char *options) +static void serial8250_early_kick(struct rt_fdt_earlycon *con, int why) +{ + struct serial8250 *serial = raw_to_serial8250(con->data); + + switch (why) + { + case FDT_EARLYCON_KICK_UPDATE: + serial->base = rt_ioremap((void *)con->mmio, con->size); + break; + + case FDT_EARLYCON_KICK_COMPLETED: + rt_iounmap(serial->base); + break; + + default: + break; + } +} + +rt_err_t serial8250_early_fdt_setup(struct serial8250 *serial, struct rt_fdt_earlycon *con, const char *options) { rt_err_t ret = RT_EOK; - if (!serial->base && desc) + if (!serial->base && con) { serial8250_config(serial, options); - desc->mmio = (rt_ubase_t)serial->base; - desc->size = serial->size; + con->mmio = (rt_ubase_t)serial->base; + con->size = serial->size; } - if (serial->base && desc) + if (serial->base && con) { - serial->base = rt_ioremap((void *)serial->base, serial->size); + serial->base = rt_ioremap_early((void *)serial->base, serial->size); } - if (serial->base && desc) + if (serial->base && con) { - desc->console_putc = (typeof(desc->console_putc))&serial8250_early_putc; - desc->data = &serial->parent; + con->console_putc = (typeof(con->console_putc))&serial8250_early_putc; + con->console_kick = serial8250_early_kick; + con->data = &serial->parent; if (!serial->parent.config.baud_rate) { - /* assume the device was initialized, only mask interrupts */ + /* Assume the device was initialized, only mask interrupts */ rt_uint32_t ier = serial8250_early_in(serial, UART_IER); serial8250_early_out(serial, UART_IER, ier & UART_IER_UUE); } @@ -110,40 +123,40 @@ rt_err_t serial8250_early_of_setup(struct serial8250 *serial, struct rt_of_early return ret; } -static void common_init(struct serial8250 *serial, struct rt_of_earlycon_desc *desc) +static void common_init(struct serial8250 *serial, struct rt_fdt_earlycon *con) { - serial->base = (void *)desc->mmio; - serial->size = desc->size; + serial->base = (void *)con->mmio; + serial->size = con->size; serial->iotype = PORT_MMIO32; } -static rt_err_t common_early_setup(struct rt_of_earlycon_desc *desc, const char *options) +static rt_err_t common_early_setup(struct rt_fdt_earlycon *con, const char *options) { struct serial8250 *serial = &early_serial8250; - common_init(serial, desc); + common_init(serial, con); serial->regshift = 2; - fdt_getprop_u32(desc->fdt, desc->node_offset, "reg-shift", &serial->regshift, RT_NULL); + fdt_getprop_u32(con->fdt, con->nodeoffset, "reg-shift", &serial->regshift, RT_NULL); - return serial8250_early_of_setup(serial, desc, options); + return serial8250_early_fdt_setup(serial, con, options); } -RT_OF_EARLYCON_EXPORT(bcm2835aux, "uart8250", "brcm,bcm2835-aux-uart", common_early_setup); -RT_OF_EARLYCON_EXPORT(tegra20, "uart8250", "nvidia,tegra20-uart", common_early_setup); -RT_OF_EARLYCON_EXPORT(dw8250, "uart8250", "snps,dw-apb-uart", common_early_setup); -RT_OF_EARLYCON_EXPORT(ns16550a, "uart8250", "ns16550a", common_early_setup); -RT_OF_EARLYCON_EXPORT(ns16550, "uart8250", "ns16550", common_early_setup); +RT_FDT_EARLYCON_EXPORT(bcm2835aux, "uart8250", "brcm,bcm2835-aux-uart", common_early_setup); +RT_FDT_EARLYCON_EXPORT(tegra20, "uart8250", "nvidia,tegra20-uart", common_early_setup); +RT_FDT_EARLYCON_EXPORT(dw8250, "uart8250", "snps,dw-apb-uart", common_early_setup); +RT_FDT_EARLYCON_EXPORT(ns16550a, "uart8250", "ns16550a", common_early_setup); +RT_FDT_EARLYCON_EXPORT(ns16550, "uart8250", "ns16550", common_early_setup); #ifdef RT_USING_8250_OMAP -static rt_err_t omap8250_early_setup(struct rt_of_earlycon_desc *desc, const char *options) +static rt_err_t omap8250_early_setup(struct rt_fdt_earlycon *con, const char *options) { struct serial8250 *serial = &early_serial8250; - common_init(serial, desc); + common_init(serial, con); serial->regshift = 2; - return serial8250_early_of_setup(serial, desc, options); + return serial8250_early_fdt_setup(serial, con, options); } -OF_EARLYCON_DECLARE(omap8250, "uart8250", "ti,omap2-uart", omap8250_early_setup); -OF_EARLYCON_DECLARE(omap8250, "uart8250", "ti,omap3-uart", omap8250_early_setup); -OF_EARLYCON_DECLARE(omap8250, "uart8250", "ti,omap4-uart", omap8250_early_setup); +RT_FDT_EARLYCON_EXPORT(omap8250, "uart8250", "ti,omap2-uart", omap8250_early_setup); +RT_FDT_EARLYCON_EXPORT(omap8250, "uart8250", "ti,omap3-uart", omap8250_early_setup); +RT_FDT_EARLYCON_EXPORT(omap8250, "uart8250", "ti,omap4-uart", omap8250_early_setup); #endif /* RT_USING_8250_OMAP */ diff --git a/components/drivers/serial/serial_reg.h b/components/drivers/serial/8250/regs.h similarity index 99% rename from components/drivers/serial/serial_reg.h rename to components/drivers/serial/8250/regs.h index e47fbdf8fa92..6f05efe87ad5 100755 --- a/components/drivers/serial/serial_reg.h +++ b/components/drivers/serial/8250/regs.h @@ -4,8 +4,8 @@ * SPDX-License-Identifier: Apache-2.0 */ -#ifndef __SERIAL_REG_H__ -#define __SERIAL_REG_H__ +#ifndef __SERIAL_8250_REGS_H__ +#define __SERIAL_8250_REGS_H__ /* * DLAB=0 @@ -103,8 +103,7 @@ #define UART_LCR_WLEN8 0x03 /* Wordlength: 8 bits */ /* - * Access to some registers depends on register access / configuration - * mode. + * Access to some registers depends on register access / configuration mode. */ #define UART_LCR_CONF_MODE_A UART_LCR_DLAB /* Configutation mode A */ #define UART_LCR_CONF_MODE_B 0xBF /* Configutation mode B */ @@ -363,4 +362,4 @@ #define UART_ALTR_EN_TXFIFO_LW 0x01 /* Enable the TX FIFO Low Watermark */ #define UART_ALTR_TX_LOW 0x41 /* Tx FIFO Low Watermark */ -#endif /* __SERIAL_REG_H__ */ +#endif /* __SERIAL_8250_REGS_H__ */ diff --git a/components/drivers/serial/Kconfig b/components/drivers/serial/Kconfig index 70c9be1b00ca..37829f601d84 100755 --- a/components/drivers/serial/Kconfig +++ b/components/drivers/serial/Kconfig @@ -1,5 +1,5 @@ menuconfig RT_USING_SERIAL - bool "USING Serial device drivers" + bool "Using Serial device drivers" select RT_USING_DEVICE_IPC select RT_USING_DEVICE default y @@ -28,3 +28,7 @@ config RT_SERIAL_PL011 depends on RT_USING_DM depends on RT_USING_SERIAL default n + +if RT_USING_DM && RT_USING_SERIAL +source "$RTT_DIR/components/drivers/serial/8250/Kconfig" +endif diff --git a/components/drivers/serial/SConscript b/components/drivers/serial/SConscript index 8f082642ffbd..3bce2afaa0fa 100644 --- a/components/drivers/serial/SConscript +++ b/components/drivers/serial/SConscript @@ -18,7 +18,7 @@ else: src += ['serial.c'] if GetDepend(['RT_USING_DM']): - src += ['serial_internal.c'] + src += ['serial_dm.c'] if GetDepend(['RT_SERIAL_PL011']): src += ['serial-pl011.c'] diff --git a/components/drivers/serial/serial-pl011.c b/components/drivers/serial/serial-pl011.c index 5220cd92d7df..da6817ecfb86 100755 --- a/components/drivers/serial/serial-pl011.c +++ b/components/drivers/serial/serial-pl011.c @@ -17,19 +17,19 @@ #include -#include "serial_internal.h" - -#define PL011_OEIM (1 << 10) /* overrun error interrupt mask */ -#define PL011_BEIM (1 << 9) /* break error interrupt mask */ -#define PL011_PEIM (1 << 8) /* parity error interrupt mask */ -#define PL011_FEIM (1 << 7) /* framing error interrupt mask */ -#define PL011_RTIM (1 << 6) /* receive timeout interrupt mask */ -#define PL011_TXIM (1 << 5) /* transmit interrupt mask */ -#define PL011_RXIM (1 << 4) /* receive interrupt mask */ -#define PL011_DSRMIM (1 << 3) /* DSR interrupt mask */ -#define PL011_DCDMIM (1 << 2) /* DCD interrupt mask */ -#define PL011_CTSMIM (1 << 1) /* CTS interrupt mask */ -#define PL011_RIMIM (1 << 0) /* RI interrupt mask */ +#include "serial_dm.h" + +#define PL011_OEIM RT_BIT(10) /* overrun error interrupt mask */ +#define PL011_BEIM RT_BIT(9) /* break error interrupt mask */ +#define PL011_PEIM RT_BIT(8) /* parity error interrupt mask */ +#define PL011_FEIM RT_BIT(7) /* framing error interrupt mask */ +#define PL011_RTIM RT_BIT(6) /* receive timeout interrupt mask */ +#define PL011_TXIM RT_BIT(5) /* transmit interrupt mask */ +#define PL011_RXIM RT_BIT(4) /* receive interrupt mask */ +#define PL011_DSRMIM RT_BIT(3) /* DSR interrupt mask */ +#define PL011_DCDMIM RT_BIT(2) /* DCD interrupt mask */ +#define PL011_CTSMIM RT_BIT(1) /* CTS interrupt mask */ +#define PL011_RIMIM RT_BIT(0) /* RI interrupt mask */ #define PL011_DR 0x000 #define PL011_FR 0x018 @@ -54,16 +54,16 @@ #define PL011_LCRH_WLEN(n) ((n - 5) << 5) -#define PL011_CR_CTSEN (1 << 15) -#define PL011_CR_RTSEN (1 << 14) -#define PL011_CR_RTS (1 << 11) -#define PL011_CR_DTR (1 << 10) -#define PL011_CR_RXE (1 << 9) -#define PL011_CR_TXE (1 << 8) -#define PL011_CR_LBE (1 << 7) -#define PL011_CR_SIRLP (1 << 2) -#define PL011_CR_SIREN (1 << 1) -#define PL011_CR_UARTEN (1 << 0) +#define PL011_CR_CTSEN RT_BIT(15) +#define PL011_CR_RTSEN RT_BIT(14) +#define PL011_CR_RTS RT_BIT(11) +#define PL011_CR_DTR RT_BIT(10) +#define PL011_CR_RXE RT_BIT(9) +#define PL011_CR_TXE RT_BIT(8) +#define PL011_CR_LBE RT_BIT(7) +#define PL011_CR_SIRLP RT_BIT(2) +#define PL011_CR_SIREN RT_BIT(1) +#define PL011_CR_UARTEN RT_BIT(0) struct pl011 { @@ -73,6 +73,7 @@ struct pl011 void *base; rt_ubase_t freq; struct rt_clk *clk; + struct rt_clk *pclk; struct rt_spinlock spinlock; }; @@ -202,7 +203,6 @@ static void pl011_early_kick(struct rt_fdt_earlycon *con, int why) switch (why) { case FDT_EARLYCON_KICK_UPDATE: - rt_iounmap_early(pl011->base, con->size); pl011->base = rt_ioremap((void *)con->mmio, con->size); break; @@ -220,11 +220,7 @@ static rt_err_t pl011_early_setup(struct rt_fdt_earlycon *con, const char *optio rt_err_t err = RT_EOK; static struct pl011 pl011 = { }; - if (!options && con->mmio) - { - con->size = 0x1000; - } - else + if (options && !con->mmio) { char *arg; @@ -245,10 +241,16 @@ static rt_err_t pl011_early_setup(struct rt_fdt_earlycon *con, const char *optio if (!con->mmio) { con->mmio = (rt_ubase_t)serial_base_from_args(arg); + break; } } } + if (!con->size) + { + con->size = 0x1000; + } + if (con->mmio) { pl011.base = rt_ioremap_early((void *)con->mmio, con->size); @@ -270,82 +272,128 @@ static rt_err_t pl011_early_setup(struct rt_fdt_earlycon *con, const char *optio } RT_FDT_EARLYCON_EXPORT(pl011, "pl011", "arm,pl011", pl011_early_setup); -static rt_err_t pl011_ofw_init(struct rt_platform_device *pdev, struct pl011 *pl011) +static void pl011_free(struct pl011 *pl011) { - rt_err_t err = RT_EOK; - struct rt_ofw_node *np = pdev->parent.ofw_node; - - pl011->base = rt_ofw_iomap(np, 0); - if (pl011->base) { - pl011->clk = rt_ofw_get_clk(np, 0); - pl011->irq = rt_ofw_get_irq(np, 0); + rt_iounmap(pl011->base); + } - if (pl011->clk && pl011->irq >= 0) - { - rt_ofw_data(np) = &pl011->parent; - } - else - { - err = -RT_ERROR; - } + if (!rt_is_err_or_null(pl011->clk)) + { + rt_clk_disable(pl011->clk); + rt_clk_put(pl011->clk); } - else + + if (!rt_is_err_or_null(pl011->pclk)) { - err = -RT_EIO; + rt_clk_disable_unprepare(pl011->pclk); + rt_clk_put(pl011->pclk); } - return err; + rt_free(pl011); } static rt_err_t pl011_probe(struct rt_platform_device *pdev) { - rt_err_t err = RT_EOK; + rt_err_t err; + const char *name; + char isr_name[RT_NAME_MAX]; + struct rt_device *dev = &pdev->parent; struct pl011 *pl011 = rt_calloc(1, sizeof(*pl011)); + struct serial_configure config = RT_SERIAL_CONFIG_DEFAULT; - if (pl011) + if (!pl011) { - err = pl011_ofw_init(pdev, pl011); + return -RT_ENOMEM; } - else + + pl011->base = rt_dm_dev_iomap(dev, 0); + + if (!pl011->base) { - err = -RT_ENOMEM; + err = -RT_EIO; + + goto _fail; } - if (!err) + pl011->irq = rt_dm_dev_get_irq(dev, 0); + + if (pl011->irq < 0) { - const char *name; - char isr_name[RT_NAME_MAX]; + err = pl011->irq; - struct serial_configure config = RT_SERIAL_CONFIG_DEFAULT; + goto _fail; + } - rt_clk_enable(pl011->clk); - pl011->freq = rt_clk_get_rate(pl011->clk); + pl011->clk = rt_clk_get_by_index(dev, 0); - pl011->parent.ops = &pl011_uart_ops; - pl011->parent.config = config; + if (rt_is_err(pl011->clk)) + { + err = rt_ptr_err(pl011->clk); - rt_spin_lock_init(&pl011->spinlock); + goto _fail; + } + + pl011->pclk = rt_clk_get_by_name(dev, "apb_pclk"); - serial_dev_set_name(&pl011->parent); - name = rt_dm_get_dev_name(&pl011->parent.parent); + if (rt_is_err(pl011->pclk)) + { + err = rt_ptr_err(pl011->pclk); - rt_hw_serial_register(&pl011->parent, name, RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX, pl011); - rt_snprintf(isr_name, RT_NAME_MAX, "%s-pl011", name); - rt_hw_interrupt_install(pl011->irq, pl011_isr, pl011, isr_name); + goto _fail; } - else + + if ((err = rt_clk_prepare_enable(pl011->pclk))) { - rt_free(pl011); + goto _fail; } + rt_dm_dev_bind_fwdata(&pl011->parent.parent, dev->ofw_node, &pl011->parent); + + rt_clk_enable(pl011->clk); + pl011->freq = rt_clk_get_rate(pl011->clk); + + dev->user_data = pl011; + + pl011->parent.ops = &pl011_uart_ops; + pl011->parent.config = config; + + rt_spin_lock_init(&pl011->spinlock); + + serial_dev_set_name(&pl011->parent); + name = rt_dm_dev_get_name(&pl011->parent.parent); + + rt_hw_serial_register(&pl011->parent, name, RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX, pl011); + rt_snprintf(isr_name, RT_NAME_MAX, "%s-pl011", name); + rt_hw_interrupt_install(pl011->irq, pl011_isr, pl011, isr_name); + + return RT_EOK; + +_fail: + pl011_free(pl011); + return err; } +static rt_err_t pl011_remove(struct rt_platform_device *pdev) +{ + struct pl011 *pl011 = pdev->parent.user_data; + + rt_hw_interrupt_mask(pl011->irq); + rt_pic_detach_irq(pl011->irq, pl011); + + rt_device_unregister(&pl011->parent.parent); + + pl011_free(pl011); + + return RT_EOK; +} + static const struct rt_ofw_node_id pl011_ofw_ids[] = { { .type = "ttyAMA", .compatible = "arm,pl011" }, + { .type = "ttyAMA", .compatible = "arm,pl011-axi" }, { /* sentinel */ } }; @@ -355,9 +403,10 @@ static struct rt_platform_driver pl011_driver = .ids = pl011_ofw_ids, .probe = pl011_probe, + .remove = pl011_remove, }; -int pl011_drv_register(void) +static int pl011_drv_register(void) { rt_platform_driver_register(&pl011_driver); diff --git a/components/drivers/serial/serial.c b/components/drivers/serial/serial.c index 34d91f9a2156..8f70eb91fa92 100644 --- a/components/drivers/serial/serial.c +++ b/components/drivers/serial/serial.c @@ -635,6 +635,11 @@ static rt_err_t rt_serial_open(struct rt_device *dev, rt_uint16_t oflag) /* get open flags */ dev->open_flag = oflag & 0xff; +#ifdef RT_USING_PINCTRL + /* initialize iomux in DM */ + rt_pin_ctrl_confs_apply_by_name(dev, RT_NULL); +#endif + /* initialize the Rx/Tx structure according to open flag */ if (serial->serial_rx == RT_NULL) { diff --git a/components/drivers/serial/serial_internal.c b/components/drivers/serial/serial_dm.c old mode 100755 new mode 100644 similarity index 84% rename from components/drivers/serial/serial_internal.c rename to components/drivers/serial/serial_dm.c index 1c7486ebc4c8..d6fca17882c3 --- a/components/drivers/serial/serial_internal.c +++ b/components/drivers/serial/serial_dm.c @@ -9,8 +9,7 @@ */ #include - -#include "serial_internal.h" +#include "serial_dm.h" int serial_dev_set_name(struct rt_serial_device *sdev) { @@ -21,7 +20,7 @@ int serial_dev_set_name(struct rt_serial_device *sdev) id = (int)rt_hw_atomic_add(&uid, 1); - return rt_dm_set_dev_name(&sdev->parent, "uart%u", id); + return rt_dm_dev_set_name(&sdev->parent, "uart%u", id); } void *serial_base_from_args(char *str) @@ -63,10 +62,6 @@ struct serial_configure serial_cfg_from_args(char *str) { rt_uint32_t baudrate = 0; - #ifdef RT_USING_OFW - *(str - 1) = RT_FDT_EARLYCON_OPTION_SIGNATURE; - #endif - /* BBBB is the speed */ while (*str && (*str >= '0' && *str <= '9')) { @@ -109,7 +104,21 @@ struct serial_configure serial_cfg_from_args(char *str) if (*str) { cfg.flowcontrol = (*str == 'r' ? RT_SERIAL_FLOWCONTROL_CTSRTS : RT_SERIAL_FLOWCONTROL_NONE); + ++str; } + + #ifdef RT_USING_OFW + if (*str == '\0') + { + const char earlycon_magic[] = { 'O', 'F', 'W', '\0' }; + + if (!rt_strcmp(++str, earlycon_magic)) + { + /* Is OFW earlycon, we should ACK it */ + rt_memset(str, 0, RT_ARRAY_SIZE(earlycon_magic)); + } + } + #endif } return cfg; diff --git a/components/drivers/serial/serial_internal.h b/components/drivers/serial/serial_dm.h old mode 100755 new mode 100644 similarity index 66% rename from components/drivers/serial/serial_internal.h rename to components/drivers/serial/serial_dm.h index 9207604e83da..20792fc9eb3e --- a/components/drivers/serial/serial_internal.h +++ b/components/drivers/serial/serial_dm.h @@ -8,13 +8,12 @@ * 2022-11-16 GuEe-GUI first version */ -#ifndef __SERIAL_INTERNAL_H__ -#define __SERIAL_INTERNAL_H__ +#ifndef __SERIAL_DM_H__ +#define __SERIAL_DM_H__ #include #include - -#include "serial_reg.h" +#include int serial_dev_set_name(struct rt_serial_device *sdev); @@ -22,8 +21,9 @@ void *serial_base_from_args(char *str); struct serial_configure serial_cfg_from_args(char *str); #define serial_for_each_args(arg, args) \ - for (char *context = (arg = (typeof(arg))args, (void *)RT_NULL); \ - !!(arg = rt_strtok_r(arg, ",", &context)); \ - arg = (!*(arg - 1) ? (*(arg - 1) = ',') : RT_NULL, (typeof(arg))RT_NULL)) + for (char *context = (arg = (typeof(arg))args, (void *)RT_NULL), \ + *context_end = rt_strchrnul((char *)args, ' '); \ + (arg = strtok_r(arg, ",", &context)) && arg < context_end; \ + arg = RT_NULL) -#endif /* __SERIAL_INTERNAL_H__ */ +#endif /* __SERIAL_DM_H__ */ diff --git a/components/drivers/soc/Kconfig b/components/drivers/soc/Kconfig new file mode 100644 index 000000000000..62ea1689b73d --- /dev/null +++ b/components/drivers/soc/Kconfig @@ -0,0 +1,9 @@ +menuconfig RT_USING_SOC + bool "Using SOC (System On Chip) specific Drivers" + depends on RT_USING_DM + default n + +if RT_USING_SOC + source "$RTT_DIR/components/drivers/soc/broadcom/Kconfig" + source "$RTT_DIR/components/drivers/soc/rockchip/Kconfig" +endif diff --git a/components/drivers/soc/SConscript b/components/drivers/soc/SConscript new file mode 100644 index 000000000000..a9ea5eedfd90 --- /dev/null +++ b/components/drivers/soc/SConscript @@ -0,0 +1,23 @@ +from building import * + +group = [] +objs = [] + +if not GetDepend(['RT_USING_SOC']): + Return('group') + +cwd = GetCurrentDir() +list = os.listdir(cwd) +CPPPATH = [cwd + '/../include'] + +src = [] + +group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) + +for d in list: + path = os.path.join(cwd, d) + if os.path.isfile(os.path.join(path, 'SConscript')): + objs = objs + SConscript(os.path.join(d, 'SConscript')) +objs = objs + group + +Return('objs') diff --git a/components/drivers/soc/broadcom/Kconfig b/components/drivers/soc/broadcom/Kconfig new file mode 100644 index 000000000000..a9b2a56c3e90 --- /dev/null +++ b/components/drivers/soc/broadcom/Kconfig @@ -0,0 +1,10 @@ +menuconfig RT_SOC_BROADCOM + bool "Broadcom" + default n + +if RT_SOC_BROADCOM +config RT_SOC_BROADCOM_RASPBERRYPI_VOLTAGE_MONITOR + bool "Raspberry Pi voltage monitor" + select RT_FIRMWARE_RASPBERRYPI + default n +endif diff --git a/components/drivers/soc/broadcom/SConscript b/components/drivers/soc/broadcom/SConscript new file mode 100644 index 000000000000..aa4236c4aab3 --- /dev/null +++ b/components/drivers/soc/broadcom/SConscript @@ -0,0 +1,17 @@ +from building import * + +group = [] + +if not GetDepend(['RT_SOC_BROADCOM']): + Return('group') + +cwd = GetCurrentDir() +CPPPATH = [cwd + '/../../include'] + +src = [] + +if GetDepend(['RT_SOC_BROADCOM_RASPBERRYPI_VOLTAGE_MONITOR']): + src += ['raspberrypi-voltage-monitor.c'] + +group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) +Return('group') diff --git a/components/drivers/soc/broadcom/raspberrypi-voltage-monitor.c b/components/drivers/soc/broadcom/raspberrypi-voltage-monitor.c new file mode 100644 index 000000000000..1186b3970835 --- /dev/null +++ b/components/drivers/soc/broadcom/raspberrypi-voltage-monitor.c @@ -0,0 +1,203 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-08-24 GuEe-GUI first version + */ + +#include +#include +#include + +#define DBG_TAG "soc.broadcom.raspberrypi-voltage-monitor" +#define DBG_LVL DBG_INFO +#include + +#include "../../firmware/raspberrypi/firmware.h" + +#define UNDERVOLTAGE_STICKY_BIT RT_BIT(16) + +struct rpi_voltage_monitor +{ + struct rt_device_notify alarm; + + rt_uint32_t last_throttled; + struct rpi_firmware *rpi_fw; + struct rt_timer delayed_work; +}; + +static rt_err_t rpi_voltage_monitor_open(rt_device_t dev, rt_uint16_t oflag) +{ + if (dev->ref_count) + { + return -RT_EBUSY; + } + + return RT_EOK; +} + +static rt_err_t rpi_voltage_monitor_close(rt_device_t dev) +{ + struct rpi_voltage_monitor *mon = dev->user_data; + + mon->alarm.notify = RT_NULL; + + return RT_EOK; +} + +static rt_ssize_t rpi_voltage_monitor_read(rt_device_t dev, rt_off_t pos, + void *buffer, rt_size_t size) +{ + struct rpi_voltage_monitor *mon = dev->user_data; + + if (size <= sizeof(mon->last_throttled)) + { + return -RT_ENOSPC; + } + + *(rt_uint32_t *)buffer = !!(mon->last_throttled & UNDERVOLTAGE_STICKY_BIT); + + return sizeof(mon->last_throttled); +} + +static rt_err_t rpi_voltage_monitor_control(rt_device_t dev, int cmd, void *args) +{ + rt_err_t err = RT_EOK; + struct rpi_voltage_monitor *mon = dev->user_data; + + switch (cmd) + { + case RT_DEVICE_CTRL_NOTIFY_SET: + if (!args) + { + err = -RT_EINVAL; + break; + } + rt_memcpy(&mon->alarm, args, sizeof(mon->alarm)); + break; + + default: + err = -RT_EINVAL; + break; + } + + return err; +} + +#ifdef RT_USING_DEVICE_OPS +const static struct rt_device_ops rpi_voltage_monitor_ops = +{ + .open = rpi_voltage_monitor_open, + .close = rpi_voltage_monitor_close, + .read = rpi_voltage_monitor_read, + .control = rpi_voltage_monitor_control, +}; +#endif + +static void rpi_voltage_monitor_delayed_work(void *param) +{ + rt_err_t err; + struct rpi_voltage_monitor *mon = param; + rt_uint32_t new_uv, old_uv, value; + + /* Request firmware to clear sticky bits */ + value = 0xffff; + + if ((err = rpi_firmware_property(mon->rpi_fw, RPI_FIRMWARE_GET_THROTTLED, + &value, sizeof(value)))) + { + LOG_E("Failed to get throttled error = %s", rt_strerror(err)); + return; + } + + new_uv = value & UNDERVOLTAGE_STICKY_BIT; + old_uv = mon->last_throttled & UNDERVOLTAGE_STICKY_BIT; + mon->last_throttled = value; + + if (new_uv == old_uv) + { + return; + } + + LOG_D(new_uv ? "Undervoltage detected" : "Voltage normalised"); + + if (mon->alarm.notify) + { + mon->alarm.notify(mon->alarm.dev); + } +} + +static rt_err_t rpi_voltage_monitor_probe(struct rt_platform_device *pdev) +{ + rt_err_t err; + const char *dev_name; + struct rt_device *dev = &pdev->parent, *fw_dev = pdev->priv; + struct rpi_voltage_monitor *mon = rt_calloc(1, sizeof(*mon)); + + if (!mon) + { + return -RT_ENOMEM; + } + + mon->rpi_fw = rpi_firmware_get(fw_dev->ofw_node); + + if (!mon->rpi_fw) + { + err = -RT_EINVAL; + goto _fail; + } + + dev->user_data = mon; + + dev->type = RT_Device_Class_Char; +#ifdef RT_USING_DEVICE_OPS + dev->ops = &rpi_voltage_monitor_ops; +#else + dev->open = rpi_voltage_monitor_open, + dev->close = rpi_voltage_monitor_close, + dev->read = rpi_voltage_monitor_read, + dev->control = rpi_voltage_monitor_control, +#endif + + rt_dm_dev_set_name(dev, "rpi_volt"); + dev_name = rt_dm_dev_get_name(dev); + rt_device_register(dev, dev_name, RT_DEVICE_FLAG_RDONLY); + + rt_timer_init(&mon->delayed_work, dev_name, rpi_voltage_monitor_delayed_work, mon, + rt_tick_from_millisecond(200), RT_TIMER_FLAG_PERIODIC); + rt_timer_start(&mon->delayed_work); + + return RT_EOK; + +_fail: + rt_free(mon); + + return err; +} + +static rt_err_t rpi_voltage_monitor_remove(struct rt_platform_device *pdev) +{ + struct rt_device *dev = &pdev->parent; + struct rpi_voltage_monitor *mon = dev->user_data; + + rt_timer_stop(&mon->delayed_work); + + rt_device_unregister(dev); + + rpi_firmware_put(mon->rpi_fw); + rt_free(mon); + + return RT_EOK; +} + +static struct rt_platform_driver rpi_voltage_monitor_driver = +{ + .name = "raspberrypi-voltage-monitor", + + .probe = rpi_voltage_monitor_probe, + .remove = rpi_voltage_monitor_remove, +}; +RT_PLATFORM_DRIVER_EXPORT(rpi_voltage_monitor_driver); diff --git a/components/drivers/soc/rockchip/Kconfig b/components/drivers/soc/rockchip/Kconfig new file mode 100644 index 000000000000..56e3d2a1ced8 --- /dev/null +++ b/components/drivers/soc/rockchip/Kconfig @@ -0,0 +1,25 @@ +menuconfig RT_SOC_ROCKCHIP + bool "RockChip" + default n + +if RT_SOC_ROCKCHIP +config RT_SOC_ROCKCHIP_FIQ_DEBUGGER + bool "FIQ Debugger" + select RT_USING_OFW + default y + +config RT_SOC_ROCKCHIP_GRF + bool "General Register Files support" + select RT_MFD_SYSCON + default y + +config RT_SOC_ROCKCHIP_HW_DECOMPRESS + bool "HardWare Decompress (LZ4, GZIP, ZLIB)" + default y + +config RT_SOC_ROCKCHIP_IODOMAIN + bool "IO domain support" + select RT_MFD_SYSCON + select RT_USING_REGULATOR + default y +endif diff --git a/components/drivers/soc/rockchip/SConscript b/components/drivers/soc/rockchip/SConscript new file mode 100644 index 000000000000..31015780edd5 --- /dev/null +++ b/components/drivers/soc/rockchip/SConscript @@ -0,0 +1,26 @@ +from building import * + +group = [] + +if not GetDepend(['RT_SOC_ROCKCHIP']): + Return('group') + +cwd = GetCurrentDir() +CPPPATH = [cwd + '/../../include'] + +src = [] + +if GetDepend(['RT_SOC_ROCKCHIP_FIQ_DEBUGGER']): + src += ['fiq-debugger.c'] + +if GetDepend(['RT_SOC_ROCKCHIP_GRF']): + src += ['grf.c'] + +if GetDepend(['RT_SOC_ROCKCHIP_HW_DECOMPRESS']): + src += ['hw-decompress.c'] + +if GetDepend(['RT_SOC_ROCKCHIP_IODOMAIN']): + src += ['io-domain.c'] + +group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) +Return('group') diff --git a/components/drivers/soc/rockchip/fiq-debugger.c b/components/drivers/soc/rockchip/fiq-debugger.c new file mode 100644 index 000000000000..afbb113574d7 --- /dev/null +++ b/components/drivers/soc/rockchip/fiq-debugger.c @@ -0,0 +1,372 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-11-21 GuEe-GUI first version + */ + +#include +#include +#include + +#include +#include + +#include "../../serial/8250/regs.h" +#include "../../serial/serial_dm.h" + +#define UART_USR 0x1f /* In: UART Status Register */ +#define UART_USR_RX_FIFO_FULL 0x10 /* Receive FIFO full */ +#define UART_USR_RX_FIFO_NOT_EMPTY 0x08 /* Receive FIFO not empty */ +#define UART_USR_TX_FIFO_EMPTY 0x04 /* Transmit FIFO empty */ +#define UART_USR_TX_FIFO_NOT_FULL 0x02 /* Transmit FIFO not full */ +#define UART_USR_BUSY 0x01 /* UART busy indicator */ +#define UART_SRR 0x22 /* software reset register */ + +#define FIQ_DEBUGGER_NO_CHAR -1 +#define FIQ_DEBUGGER_BREAK -2 + +struct rockchip_fiq_debugger +{ + struct rt_serial_device parent; + + int irq; + int baudrate; + void *debug_port_base; + rt_bool_t break_seen; +}; + +#define raw_to_fiq_debugger(raw) rt_container_of(raw, struct rockchip_fiq_debugger, parent) + +rt_inline void rockchip_fiq_write(struct rockchip_fiq_debugger *t, rt_uint32_t val, int off) +{ + HWREG32(t->debug_port_base + off * 4) = val; +} + +rt_inline rt_uint32_t rockchip_fiq_read(struct rockchip_fiq_debugger *t, int off) +{ + return HWREG32(t->debug_port_base + off * 4); +} + +rt_inline rt_uint32_t rockchip_fiq_read_lsr(struct rockchip_fiq_debugger *t) +{ + rt_uint32_t ret = rockchip_fiq_read(t, UART_LSR); + + if (ret & UART_LSR_BI) + { + t->break_seen = true; + } + + return ret; +} + +static void fiq_debugger_isr(int irqno, void *param) +{ + rt_uint32_t usr; + struct rt_serial_device *serial = (struct rt_serial_device*)param; + struct rockchip_fiq_debugger *t = raw_to_fiq_debugger(serial); + + usr = rockchip_fiq_read(t, UART_USR); + + if ((usr & UART_USR_RX_FIFO_NOT_EMPTY) == UART_USR_RX_FIFO_NOT_EMPTY) + { + rt_hw_serial_isr(serial, RT_SERIAL_EVENT_RX_IND); + } + + if ((usr & UART_USR_BUSY) == UART_USR_BUSY) + { + /* Clear the USR */ + (void)rockchip_fiq_read(t, UART_USR); + } +} + +static rt_err_t fiq_debugger_uart_configure(struct rt_serial_device *serial, struct serial_configure *cfg) +{ + int dll = 0, dlm = 0; + struct rockchip_fiq_debugger *t = raw_to_fiq_debugger(serial); + + if (rockchip_fiq_read(t, UART_LSR) & UART_LSR_DR) + { + (void)rockchip_fiq_read(t, UART_RX); + } + + switch (t->baudrate) + { + case 1500000: + dll = 0x1; + break; + case 115200: + default: + dll = 0xd; + break; + } + /* reset uart */ + rockchip_fiq_write(t, (1 << 1) | (1 << 2), UART_SRR); + rt_hw_us_delay(10); + /* set uart to loop back mode */ + rockchip_fiq_write(t, 0x10, UART_MCR); + + rockchip_fiq_write(t, 0x83, UART_LCR); + /* set baud rate */ + rockchip_fiq_write(t, dll, UART_DLL); + rockchip_fiq_write(t, dlm, UART_DLM); + rockchip_fiq_write(t, 0x03, UART_LCR); + + /* enable rx interrupt */ + rockchip_fiq_write(t, UART_IER_RDI, UART_IER); + + /* + * interrupt on every character when received, but we can enable fifo for TX + * I found that if we enable the RX fifo, some problem may vanish such as + * when you continuously input characters in the command line the uart irq + * may be disable because of the uart irq is served when CPU is at IRQ + * exception, but it is found unregistered, so it is disable. + */ + rockchip_fiq_write(t, 0x01, UART_FCR); + + /* disbale loop back mode */ + rockchip_fiq_write(t, 0x0, UART_MCR); + + return RT_EOK; +} + +static rt_err_t fiq_debugger_uart_control(struct rt_serial_device *serial, int cmd, void *arg) +{ + struct rockchip_fiq_debugger *t = raw_to_fiq_debugger(serial); + + switch (cmd) + { + case RT_DEVICE_CTRL_CLR_INT: + rt_hw_interrupt_mask(t->irq); + break; + + case RT_DEVICE_CTRL_SET_INT: + rt_hw_interrupt_umask(t->irq); + break; + } + + return RT_EOK; +} + +static int fiq_debugger_uart_putc(struct rt_serial_device *serial, char c) +{ + struct rockchip_fiq_debugger *t = raw_to_fiq_debugger(serial); + rt_uint32_t count = 10000; + + while (!(rockchip_fiq_read(t, UART_USR) & UART_USR_TX_FIFO_NOT_FULL) && count--) + { + rt_hw_cpu_relax(); + } + + rockchip_fiq_write(t, c, UART_TX); + + return 1; +} + +static int fiq_debugger_uart_getc(struct rt_serial_device *serial) +{ + int ch = FIQ_DEBUGGER_NO_CHAR; + rt_uint32_t lsr, temp; + static rt_uint32_t n = 0; + static char buf[32] = {}; + struct rockchip_fiq_debugger *t = raw_to_fiq_debugger(serial); + + /* Clear uart interrupt status */ + rockchip_fiq_read(t, UART_USR); + lsr = rockchip_fiq_read_lsr(t); + + if (lsr & UART_LSR_DR) + { + temp = rockchip_fiq_read(t, UART_RX); + buf[n & 0x1f] = temp; + n++; + + if (temp == 'q' && n > 2) + { + if ((buf[(n - 2) & 0x1f] == 'i') && (buf[(n - 3) & 0x1f] == 'f')) + { + ch = FIQ_DEBUGGER_BREAK; + } + else + { + ch = temp; + } + } + else + { + ch = temp; + } + } + + return ch; +} + +static const struct rt_uart_ops fiq_debugger_uart_ops = +{ + .configure = fiq_debugger_uart_configure, + .control = fiq_debugger_uart_control, + .putc = fiq_debugger_uart_putc, + .getc = fiq_debugger_uart_getc, +}; + +struct rockchip_fiq_debugger *rk_serial_debug_init(void *base, rt_ubase_t paddr, + int irq, int signal_irq, int wakeup_irq, rt_uint32_t baudrate) +{ + struct rockchip_fiq_debugger *t = rt_calloc(1, sizeof(*t)); + + if (t) + { + const char *name; + + t->parent.ops = &fiq_debugger_uart_ops; + t->parent.config = (struct serial_configure)RT_SERIAL_CONFIG_DEFAULT; + t->parent.config.baud_rate = baudrate; + t->irq = irq; + t->baudrate = baudrate; + t->debug_port_base = base; + t->break_seen = RT_FALSE; + + serial_dev_set_name(&t->parent); + name = rt_dm_dev_get_name(&t->parent.parent); + + rt_hw_serial_register(&t->parent, name, RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX, t); + rt_hw_interrupt_install(t->irq, fiq_debugger_isr, &t->parent, name); + } + + return t; +} + +static rt_err_t rockchip_fiq_debugger_probe(struct rt_platform_device *pdev) +{ + rt_err_t err = RT_EOK; + void *base; + rt_uint64_t regs[2]; + struct rt_clk *clk, *pclk; + rt_bool_t found = RT_FALSE; + char dev_name[RT_NAME_MAX]; + int irq, signal_irq = -1; + rt_uint32_t serial_id, baudrate = 0, irq_mode = 0, wake_irq = -1; + struct rt_ofw_node *np = pdev->parent.ofw_node; + + if (rt_ofw_prop_read_u32(np, "rockchip,serial-id", &serial_id) || serial_id == -1) + { + return -RT_EINVAL; + } + + if (rt_ofw_prop_read_u32(np, "rockchip,irq-mode-enable", &irq_mode)) + { + irq_mode = -1; + } + + if (irq_mode == 1) + { + signal_irq = -1; + } + else if (!(signal_irq = rt_ofw_get_irq(np, 0))) + { + return -RT_EINVAL; + } + + if (rt_ofw_prop_read_u32(np, "rockchip,wake-irq", &wake_irq)) + { + wake_irq = -1; + } + + if (rt_ofw_prop_read_u32(np, "rockchip,baudrate", &baudrate)) + { + baudrate = 1500000; + } + + rt_snprintf(dev_name, RT_NAME_MAX, "serial%d", serial_id); + + np = RT_NULL; + + do { + np = rt_ofw_find_node_by_tag(np, "serial"); + + if (np && rt_ofw_get_alias_id(np, "serial") == serial_id) + { + found = RT_TRUE; + break; + } + } while(np); + + if (!found) + { + return -RT_EINVAL; + } + + rt_memset(regs, 0, sizeof(regs)); + rt_ofw_get_address_array(np, 1, regs); + + pclk = rt_ofw_get_clk_by_name(np, "apb_pclk"); + clk = rt_ofw_get_clk_by_name(np, "baudclk"); + + if (!pclk || !clk) + { + err = -RT_ERROR; + goto _fail; + } + + rt_clk_enable(clk); + rt_clk_enable(pclk); + + if ((irq = rt_ofw_get_irq(np, 0)) < 0) + { + err = -RT_ERROR; + goto _fail; + } + + if ((base = rt_ioremap((void *)regs[0], regs[1]))) + { + struct rockchip_fiq_debugger *t = rk_serial_debug_init(base, + (rt_ubase_t)regs[0], irq, signal_irq, wake_irq, baudrate); + + if (t) + { + rt_dm_dev_bind_fwdata(&t->parent.parent, pdev->parent.ofw_node, &t->parent); + } + } + + return err; + +_fail: + if (clk) + { + rt_clk_disable(clk); + rt_clk_put(clk); + } + + if (pclk) + { + rt_clk_disable(pclk); + rt_clk_put(pclk); + } + + return err; +} + +static const struct rt_ofw_node_id rockchip_fiq_debugger_ofw_ids[] = +{ + { .type = "ttyFIQ", .compatible = "rockchip,fiq-debugger" }, + { /* sentinel */ } +}; + +static struct rt_platform_driver rockchip_fiq_debugger_driver = +{ + .name = "rockchip-fiq-debugger", + .ids = rockchip_fiq_debugger_ofw_ids, + + .probe = rockchip_fiq_debugger_probe, +}; + +static int rockchip_fiq_debugger_drv_register(void) +{ + rt_platform_driver_register(&rockchip_fiq_debugger_driver); + + return 0; +} +INIT_DRIVER_EARLY_EXPORT(rockchip_fiq_debugger_drv_register); diff --git a/components/drivers/soc/rockchip/grf.c b/components/drivers/soc/rockchip/grf.c new file mode 100644 index 000000000000..f8c0c62b06e6 --- /dev/null +++ b/components/drivers/soc/rockchip/grf.c @@ -0,0 +1,217 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-11-21 GuEe-GUI first version + */ + +#include +#include +#include + +#define DBG_TAG "soc.rockchip.grf" +#define DBG_LVL DBG_INFO +#include + +#include "rockchip.h" + +struct rockchip_grf_value +{ + const char *desc; + rt_uint32_t reg; + rt_uint32_t val; +}; + +struct rockchip_grf_info +{ + const struct rockchip_grf_value *values; + int values_nr; +}; + +#define RK3036_GRF_SOC_CON0 0x140 + +static const struct rockchip_grf_value rk3036_defaults[] = +{ + /* + * Disable auto jtag/sdmmc switching that causes issues with the + * clock-framework and the mmc controllers making them unreliable. + */ + { "jtag switching", RK3036_GRF_SOC_CON0, HIWORD_UPDATE(0, 1, 11) }, +}; + +static const struct rockchip_grf_info rk3036_grf = +{ + .values = rk3036_defaults, + .values_nr = RT_ARRAY_SIZE(rk3036_defaults), +}; + +#define RK3128_GRF_SOC_CON0 0x140 + +static const struct rockchip_grf_value rk3128_defaults[] = +{ + { "jtag switching", RK3128_GRF_SOC_CON0, HIWORD_UPDATE(0, 1, 8) }, +}; + +static const struct rockchip_grf_info rk3128_grf = +{ + .values = rk3128_defaults, + .values_nr = RT_ARRAY_SIZE(rk3128_defaults), +}; + +#define RK3228_GRF_SOC_CON6 0x418 + +static const struct rockchip_grf_value rk3228_defaults[] = +{ + { "jtag switching", RK3228_GRF_SOC_CON6, HIWORD_UPDATE(0, 1, 8) }, +}; + +static const struct rockchip_grf_info rk3228_grf = +{ + .values = rk3228_defaults, + .values_nr = RT_ARRAY_SIZE(rk3228_defaults), +}; + +#define RK3288_GRF_SOC_CON0 0x244 +#define RK3288_GRF_SOC_CON2 0x24c + +static const struct rockchip_grf_value rk3288_defaults[] = +{ + { "jtag switching", RK3288_GRF_SOC_CON0, HIWORD_UPDATE(0, 1, 12) }, + { "pwm select", RK3288_GRF_SOC_CON2, HIWORD_UPDATE(1, 1, 0) }, +}; + +static const struct rockchip_grf_info rk3288_grf = +{ + .values = rk3288_defaults, + .values_nr = RT_ARRAY_SIZE(rk3288_defaults), +}; + +#define RK3328_GRF_SOC_CON4 0x410 + +static const struct rockchip_grf_value rk3328_defaults[] = +{ + { "jtag switching", RK3328_GRF_SOC_CON4, HIWORD_UPDATE(0, 1, 12) }, +}; + +static const struct rockchip_grf_info rk3328_grf = +{ + .values = rk3328_defaults, + .values_nr = RT_ARRAY_SIZE(rk3328_defaults), +}; + +#define RK3368_GRF_SOC_CON15 0x43c + +static const struct rockchip_grf_value rk3368_defaults[] = +{ + { "jtag switching", RK3368_GRF_SOC_CON15, HIWORD_UPDATE(0, 1, 13) }, +}; + +static const struct rockchip_grf_info rk3368_grf = +{ + .values = rk3368_defaults, + .values_nr = RT_ARRAY_SIZE(rk3368_defaults), +}; + +#define RK3399_GRF_SOC_CON7 0xe21c + +static const struct rockchip_grf_value rk3399_defaults[] = +{ + { "jtag switching", RK3399_GRF_SOC_CON7, HIWORD_UPDATE(0, 1, 12) }, +}; + +static const struct rockchip_grf_info rk3399_grf = +{ + .values = rk3399_defaults, + .values_nr = RT_ARRAY_SIZE(rk3399_defaults), +}; + +#define RK3566_GRF_USB3OTG0_CON1 0x0104 + +static const struct rockchip_grf_value rk3566_defaults[] = +{ + { "usb3otg port switch", RK3566_GRF_USB3OTG0_CON1, HIWORD_UPDATE(0, 1, 12) }, + { "usb3otg clock switch", RK3566_GRF_USB3OTG0_CON1, HIWORD_UPDATE(1, 1, 7) }, + { "usb3otg disable usb3", RK3566_GRF_USB3OTG0_CON1, HIWORD_UPDATE(1, 1, 0) }, +}; + +static const struct rockchip_grf_info rk3566_pipegrf = +{ + .values = rk3566_defaults, + .values_nr = RT_ARRAY_SIZE(rk3566_defaults), +}; + +#define RK3588_GRF_SOC_CON6 0x0318 + +static const struct rockchip_grf_value rk3588_defaults[] = +{ + { "jtag switching", RK3588_GRF_SOC_CON6, HIWORD_UPDATE(0, 1, 14) }, +}; + +static const struct rockchip_grf_info rk3588_sysgrf = +{ + .values = rk3588_defaults, + .values_nr = RT_ARRAY_SIZE(rk3588_defaults), +}; + +static rt_err_t rockchip_grf_probe(struct rt_platform_device *pdev) +{ + struct rt_syscon *grf; + struct rt_ofw_node *np = pdev->parent.ofw_node; + const struct rockchip_grf_info *grf_info = pdev->id->data; + + grf = rt_syscon_find_by_ofw_node(np); + + if (!grf) + { + return -RT_EINVAL; + } + + for (int i = 0; i < grf_info->values_nr; ++i) + { + rt_err_t err; + const struct rockchip_grf_value *val = &grf_info->values[i]; + + err = rt_syscon_write(grf, val->reg, val->val); + LOG_D("%s: adjusting %6x to %10x", val->desc, val->reg, val->val); + + if (err) + { + LOG_E("%s: write %6x to %10x fail", val->desc, val->reg, val->val); + } + } + + return RT_EOK; +} + +static const struct rt_ofw_node_id rockchip_grf_ofw_ids[] = +{ + { .compatible = "rockchip,rk3036-grf", .data = &rk3036_grf, }, + { .compatible = "rockchip,rk3128-grf", .data = &rk3128_grf, }, + { .compatible = "rockchip,rk3228-grf", .data = &rk3228_grf, }, + { .compatible = "rockchip,rk3288-grf", .data = &rk3288_grf, }, + { .compatible = "rockchip,rk3328-grf", .data = &rk3328_grf, }, + { .compatible = "rockchip,rk3368-grf", .data = &rk3368_grf, }, + { .compatible = "rockchip,rk3399-grf", .data = &rk3399_grf, }, + { .compatible = "rockchip,rk3566-pipe-grf", .data = &rk3566_pipegrf, }, + { .compatible = "rockchip,rk3588-sys-grf", .data = &rk3588_sysgrf, }, + { /* sentinel */ } +}; + +static struct rt_platform_driver rockchip_grf_driver = +{ + .name = "rockchip-grf", + .ids = rockchip_grf_ofw_ids, + + .probe = rockchip_grf_probe, +}; + +static int rockchip_grf_drv_register(void) +{ + rt_platform_driver_register(&rockchip_grf_driver); + + return 0; +} +INIT_FRAMEWORK_EXPORT(rockchip_grf_drv_register); diff --git a/components/drivers/soc/rockchip/hw-decompress.c b/components/drivers/soc/rockchip/hw-decompress.c new file mode 100644 index 000000000000..a7f2be51451f --- /dev/null +++ b/components/drivers/soc/rockchip/hw-decompress.c @@ -0,0 +1,572 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-11-21 GuEe-GUI first version + */ + +#include +#include + +#define DBG_TAG "soc.rockchip.hw-decompress" +#define DBG_LVL DBG_INFO +#include + +#include + +#define DECOM_CTRL 0x0 +#define DECOM_ENR 0x4 +#define DECOM_RADDR 0x8 +#define DECOM_WADDR 0xc +#define DECOM_UDDSL 0x10 +#define DECOM_UDDSH 0x14 +#define DECOM_TXTHR 0x18 +#define DECOM_RXTHR 0x1c +#define DECOM_SLEN 0x20 +#define DECOM_STAT 0x24 +#define DECOM_ISR 0x28 +#define DECOM_IEN 0x2c +#define DECOM_AXI_STAT 0x30 +#define DECOM_TSIZEL 0x34 +#define DECOM_TSIZEH 0x38 +#define DECOM_MGNUM 0x3c +#define DECOM_FRAME 0x40 +#define DECOM_DICTID 0x44 +#define DECOM_CSL 0x48 +#define DECOM_CSH 0x4c +#define DECOM_LMTSL 0x50 +#define DECOM_LMTSH 0x54 + +#define LZ4_HEAD_CSUM_CHECK_EN RT_BIT(1) +#define LZ4_BLOCK_CSUM_CHECK_EN RT_BIT(2) +#define LZ4_CONT_CSUM_CHECK_EN RT_BIT(3) + +#define DSOLIEN RT_BIT(19) +#define ZDICTEIEN RT_BIT(18) +#define GCMEIEN RT_BIT(17) +#define GIDEIEN RT_BIT(16) +#define CCCEIEN RT_BIT(15) +#define BCCEIEN RT_BIT(14) +#define HCCEIEN RT_BIT(13) +#define CSEIEN RT_BIT(12) +#define DICTEIEN RT_BIT(11) +#define VNEIEN RT_BIT(10) +#define WNEIEN RT_BIT(9) +#define RDCEIEN RT_BIT(8) +#define WRCEIEN RT_BIT(7) +#define DISEIEN RT_BIT(6) +#define LENEIEN RT_BIT(5) +#define LITEIEN RT_BIT(4) +#define SQMEIEN RT_BIT(3) +#define SLCIEN RT_BIT(2) +#define HDEIEN RT_BIT(1) +#define DSIEN RT_BIT(0) + +#define DECOM_STOP RT_BIT(0) +#define DECOM_COMPLETE RT_BIT(0) +#define DECOM_GZIP_MODE RT_BIT(4) +#define DECOM_ZLIB_MODE RT_BIT(5) +#define DECOM_DEFLATE_MODE RT_BIT(0) + +#define DECOM_ENABLE 0x1 +#define DECOM_DISABLE 0x0 + +#define DECOM_INT_MASK \ +( \ + DSOLIEN | ZDICTEIEN | GCMEIEN | GIDEIEN | CCCEIEN | BCCEIEN | HCCEIEN | \ + CSEIEN | DICTEIEN | VNEIEN | WNEIEN | RDCEIEN | WRCEIEN | DISEIEN | \ + LENEIEN | LITEIEN | SQMEIEN | SLCIEN | HDEIEN | DSIEN \ +) + +enum rockchip_decompress_mod +{ + LZ4_MOD, + GZIP_MOD, + ZLIB_MOD, + + UNKNOW_MOD, +}; + +struct wait_event_queue +{ + rt_list_t list; + + const char *args; + + enum rockchip_decompress_mod mod; + + rt_ubase_t src; + rt_ubase_t dst; + rt_size_t dst_max_size; + + struct rt_thread *task; + struct rt_completion done; +}; + +struct rockchip_decompress +{ + struct rt_device parent; + + int irq; + void *regs; + + rt_ubase_t mem_start; + rt_size_t mem_size; + + struct rt_clk_array *clk_arr; + struct rt_reset_control *rstc; + + rt_list_t work_nodes; + rt_list_t request_nodes; + struct wait_event_queue *current; + + struct rt_mutex lock; + struct rt_spinlock spinlock; +}; + +#define raw_to_rockchip_decompress(raw) rt_container_of(raw, struct rockchip_decompress, parent) + +rt_inline rt_uint32_t rockchip_decompress_readl(struct rockchip_decompress *rk_decom, int offset) +{ + return HWREG32(rk_decom->regs + offset); +} + +rt_inline void rockchip_decompress_writel(struct rockchip_decompress *rk_decom, int offset, rt_uint32_t value) +{ + HWREG32(rk_decom->regs + offset) = value; +} + +static rt_err_t rockchip_decompress_submit(struct rockchip_decompress *rk_decom, + struct wait_event_queue *weq) +{ + rt_uint32_t irq_status, decom_enr, mod_flags; + + rt_uint32_t mod = weq->mod; + rt_ubase_t src = weq->src, dst = weq->dst; + rt_size_t dst_max_size = weq->dst_max_size; + + if (rt_unlikely(mod >= UNKNOW_MOD)) + { + return -RT_EINVAL; + } + + LOG_D("Task: %s, Mode: %s, Src: %p, Dst: %p, MaxSize: %u", + weq->task->parent.name, mod[((const char *const []) { + [LZ4_MOD] = "LZ4", + [GZIP_MOD] = "GZIP", + [ZLIB_MOD] = "ZLIB", + })], src, dst, dst_max_size); + + decom_enr = rockchip_decompress_readl(rk_decom, DECOM_ENR); + if (decom_enr & 0x1) + { + LOG_E("Decompress busy"); + return -RT_EBUSY; + } + + if (rk_decom->rstc) + { + rt_reset_control_assert(rk_decom->rstc); + rt_hw_us_delay(10); + rt_reset_control_deassert(rk_decom->rstc); + } + + irq_status = rockchip_decompress_readl(rk_decom, DECOM_ISR); + + /* clear interrupts */ + if (irq_status) + { + rockchip_decompress_writel(rk_decom, DECOM_ISR, irq_status); + } + + switch (mod) + { + case LZ4_MOD: + mod_flags = LZ4_CONT_CSUM_CHECK_EN | LZ4_HEAD_CSUM_CHECK_EN | + LZ4_BLOCK_CSUM_CHECK_EN | LZ4_MOD; + break; + + case GZIP_MOD: + mod_flags = DECOM_DEFLATE_MODE | DECOM_GZIP_MODE; + break; + + case ZLIB_MOD: + mod_flags = DECOM_DEFLATE_MODE | DECOM_ZLIB_MODE; + break; + } + rockchip_decompress_writel(rk_decom, DECOM_CTRL, mod_flags); + + rockchip_decompress_writel(rk_decom, DECOM_RADDR, src); + rockchip_decompress_writel(rk_decom, DECOM_WADDR, dst); + + rockchip_decompress_writel(rk_decom, DECOM_LMTSL, dst_max_size); + rockchip_decompress_writel(rk_decom, DECOM_LMTSH, 0x0); + + rockchip_decompress_writel(rk_decom, DECOM_IEN, DECOM_INT_MASK); + rockchip_decompress_writel(rk_decom, DECOM_ENR, DECOM_ENABLE); + + rk_decom->current = weq; + + return RT_EOK; +} + +static rt_err_t rockchip_decompress_control(rt_device_t dev, int, void *args) +{ + rt_err_t err = RT_EOK; + rt_bool_t can_submit; + struct rt_thread *task; + struct wait_event_queue *weq = RT_NULL, *weq_tmp, *weq_next; + struct rockchip_decompress *rk_decom = raw_to_rockchip_decompress(dev); + + if (rt_unlikely(!args)) + { + return -RT_EINVAL; + } + + task = rt_thread_self(); + + rt_mutex_take(&rk_decom->lock, RT_WAITING_FOREVER); + + rt_list_for_each_entry_safe(weq_tmp, weq_next, &rk_decom->request_nodes, list) + { + if (weq_tmp->task->stat == RT_THREAD_CLOSE) + { + /* ignore this task */ + LOG_D("Closed Task: %s", weq_tmp->task->parent.name); + + rt_list_remove(&weq_tmp->list); + rt_free(weq_tmp); + continue; + } + + if (weq_tmp->task == task) + { + weq = weq_tmp; + break; + } + } + + if (!weq) + { + weq = rt_malloc(sizeof(*weq)); + + if (rt_unlikely(weq)) + { + err = -RT_ENOMEM; + goto _out_mutex; + } + + rt_list_init(&weq->list); + rt_list_insert_before(&rk_decom->request_nodes, &weq->list); + + weq->args = RT_NULL; + weq->mod = UNKNOW_MOD; + + weq->src = ~0UL; + weq->dst = ~0UL; + weq->dst_max_size = ~0UL; + + weq->task = task; + rt_completion_init(&weq->done); + } + + if (!weq->args) + { + weq->args = args; + } + else + { + const char *cmd = weq->args; + + if (!rt_strcmp(cmd, "mode")) + { + if (rt_unlikely((rt_ubase_t)args >= UNKNOW_MOD)) + { + err = -RT_ENOSYS; + } + } + else if (!rt_strcmp(cmd, "src")) + { + void *src = rt_kmem_v2p(args); + + if (rt_unlikely(src == ARCH_MAP_FAILED)) + { + err = -RT_EINVAL; + } + else + { + weq->src = (rt_ubase_t)src; + } + } + else if (!rt_strcmp(cmd, "dst")) + { + void *dst = rt_kmem_v2p(args); + + if (rt_unlikely(dst == ARCH_MAP_FAILED)) + { + err = -RT_EINVAL; + } + else + { + weq->dst = (rt_ubase_t)dst; + } + } + else if (!rt_strcmp(cmd, "size")) + { + rt_size_t size = (rt_ubase_t)args; + + if (rt_unlikely(!size)) + { + err = -RT_EINVAL; + } + else + { + weq->dst_max_size = size; + } + } + + /* ready to next args */ + weq->args = RT_NULL; + } + + if (!err) + { + can_submit = weq->mod != UNKNOW_MOD && + weq->src != ~0UL && + weq->dst != ~0UL && + weq->dst_max_size != ~0UL; + + if (can_submit) + { + /* remove from request */ + rt_list_remove(&weq->list); + } + } + +_out_mutex: + rt_mutex_release(&rk_decom->lock); + + if (rt_unlikely(err)) + { + return err; + } + + if (can_submit) + { + rt_ubase_t level = rt_spin_lock_irqsave(&rk_decom->spinlock); + + /* submit work */ + rt_list_insert_before(&rk_decom->work_nodes, &weq->list); + + /* no working */ + if (!rk_decom->current) + { + rockchip_decompress_submit(rk_decom, weq); + } + + rt_spin_unlock_irqrestore(&rk_decom->spinlock, level); + + err = rt_completion_wait(&weq->done, RT_WAITING_FOREVER); + rt_free(weq); + } + + return err; +} + +#ifdef RT_USING_DEVICE_OPS +const static struct rt_device_ops rockchip_decompress_ops = +{ + .control = rockchip_decompress_control, +}; +#endif + +static void rockchip_decompress_isr(int irqno, void *param) +{ + rt_uint32_t irq_status, decom_status; + struct rockchip_decompress *rk_decom = param; + + irq_status = rockchip_decompress_readl(rk_decom, DECOM_ISR); + /* clear interrupts */ + rockchip_decompress_writel(rk_decom, DECOM_ISR, irq_status); + + if (irq_status & DECOM_STOP) + { + rt_ubase_t level; + struct wait_event_queue *weq = rk_decom->current, *next; + + decom_status = rockchip_decompress_readl(rk_decom, DECOM_STAT); + + if (!(decom_status & DECOM_COMPLETE)) + { + LOG_E("Decompress failed: irq_status = 0x%x, decom_status = 0x%x", + irq_status, decom_status); + + weq->task->error = -RT_EIO; + } + + level = rt_spin_lock_irqsave(&rk_decom->spinlock); + + /* finish prev work */ + rt_list_remove(&weq->list); + + /* next work */ + if (rt_list_isempty(&rk_decom->work_nodes)) + { + rk_decom->current = RT_NULL; + } + else + { + next = rt_list_first_entry(&rk_decom->work_nodes, typeof(*next), list); + + rockchip_decompress_submit(rk_decom, next); + } + + rt_spin_unlock_irqrestore(&rk_decom->spinlock, level); + + /* ask prev task done */ + rt_completion_done(&weq->done); + } +} + +static void rockchip_decompress_free(struct rockchip_decompress *rk_decom) +{ + if (rk_decom->regs) + { + rt_iounmap(rk_decom->regs); + } + + if (!rt_is_err_or_null(rk_decom->clk_arr)) + { + rt_clk_array_disable_unprepare(rk_decom->clk_arr); + rt_clk_array_put(rk_decom->clk_arr); + } + + if (!rt_is_err_or_null(rk_decom->rstc)) + { + rt_reset_control_put(rk_decom->rstc); + } + + rt_free(rk_decom); +} + +static rt_err_t rockchip_decompress_probe(struct rt_platform_device *pdev) +{ + rt_err_t err; + rt_uint64_t addr, size; + const char *dev_name; + struct rt_device *dev = &pdev->parent; + struct rockchip_decompress *rk_decom = rt_calloc(1, sizeof(*rk_decom)); + + if (!rk_decom) + { + return -RT_ENOMEM; + } + + if ((err = rt_dm_dev_get_address(dev, 0, &addr, &size))) + { + goto _fail; + } + + rk_decom->regs = rt_dm_dev_iomap(dev, 0); + + if (!rk_decom->regs) + { + err = -RT_EIO; + goto _fail; + } + + rk_decom->clk_arr = rt_clk_get_array(dev); + + if (rt_is_err(rk_decom->clk_arr)) + { + err = rt_ptr_err(rk_decom->clk_arr); + goto _fail; + } + + if ((err = rt_clk_array_prepare_enable(rk_decom->clk_arr))) + { + goto _fail; + } + + rk_decom->rstc = rt_reset_control_get_by_name(dev, "dresetn"); + + if (rt_is_err(rk_decom->rstc)) + { + err = rt_ptr_err(rk_decom->rstc); + goto _fail; + } + + rk_decom->irq = rt_dm_dev_get_irq(dev, 0); + + if (rk_decom->irq < 0) + { + err = rk_decom->irq; + goto _fail; + } + + dev->user_data = rk_decom; + + rt_dm_dev_set_name_auto(&rk_decom->parent, "decom"); + dev_name = rt_dm_dev_get_name(&rk_decom->parent); + + rk_decom->parent.type = RT_Device_Class_Char; +#ifdef RT_USING_DEVICE_OPS + rk_decom->parent.ops = &rockchip_decompress_ops; +#else + rk_decom->parent.control = rockchip_decompress_control; +#endif + + if ((err = rt_device_register(&rk_decom->parent, dev_name, RT_DEVICE_FLAG_DEACTIVATE))) + { + goto _fail; + } + + rt_list_init(&rk_decom->work_nodes); + rt_list_init(&rk_decom->request_nodes); + + rt_mutex_init(&rk_decom->lock, dev_name, RT_IPC_FLAG_PRIO); + rt_spin_lock_init(&rk_decom->spinlock); + + rt_hw_interrupt_install(rk_decom->irq, rockchip_decompress_isr, rk_decom, "rk-decompress"); + rt_hw_interrupt_umask(rk_decom->irq); + + return RT_EOK; + +_fail: + rockchip_decompress_free(rk_decom); + + return err; +} + +static rt_err_t rockchip_decompress_remove(struct rt_platform_device *pdev) +{ + struct rockchip_decompress *rk_decom = pdev->parent.user_data; + + rt_hw_interrupt_mask(rk_decom->irq); + rt_pic_detach_irq(rk_decom->irq, rk_decom); + + rt_device_unregister(&rk_decom->parent); + + rockchip_decompress_free(rk_decom); + + return RT_EOK; +} + +static const struct rt_ofw_node_id rockchip_decompress_ofw_ids[] = +{ + { .compatible = "rockchip,hw-decompress" }, + { /* sentinel */ } +}; + +static struct rt_platform_driver rockchip_decompress_driver = +{ + .name = "rockchip-hwdecompress", + .ids = rockchip_decompress_ofw_ids, + + .probe = rockchip_decompress_probe, + .remove = rockchip_decompress_remove, +}; +RT_PLATFORM_DRIVER_EXPORT(rockchip_decompress_driver); diff --git a/components/drivers/soc/rockchip/io-domain.c b/components/drivers/soc/rockchip/io-domain.c new file mode 100644 index 000000000000..5cdd5f74fc2a --- /dev/null +++ b/components/drivers/soc/rockchip/io-domain.c @@ -0,0 +1,683 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-11-21 GuEe-GUI first version + */ + +#include +#include +#include + +#define DBG_TAG "soc.rockchip.io-domain" +#define DBG_LVL DBG_INFO +#include + +#define MAX_SUPPLIES 16 + +/* + * The max voltage for 1.8V and 3.3V come from the Rockchip datasheet under + * "Recommended Operating Conditions" for "Digital GPIO". When the typical + * is 3.3V the max is 3.6V. When the typical is 1.8V the max is 1.98V. + * + * They are used like this: + * - If the voltage on a rail is above the "1.8" voltage (1.98V) we'll tell the + * SoC we're at 3.3. + * - If the voltage on a rail is above the "3.3" voltage (3.6V) we'll consider + * that to be an error. + */ +#define MAX_VOLTAGE_1_8 1980000 +#define MAX_VOLTAGE_3_3 3600000 + +struct rockchip_iodomain; + +struct rockchip_iodomain_supply +{ + struct rockchip_iodomain *domain; + struct rt_regulator *reg; + struct rt_regulator_notifier notifier; + int idx; +}; + +struct rockchip_iodomain_soc_data +{ + int grf_offset; + const char *supply_names[MAX_SUPPLIES]; + void (*init)(struct rockchip_iodomain *domain); + rt_err_t (*write)(struct rockchip_iodomain_supply *supply, int uvolt); +}; + +struct rockchip_iodomain +{ + struct rt_device *dev; + struct rt_syscon *grf; + const struct rockchip_iodomain_soc_data *soc_data; + struct rockchip_iodomain_supply supplies[MAX_SUPPLIES]; + + rt_err_t (*write)(struct rockchip_iodomain_supply *supply, int uvolt); +}; + +#define PX30_IO_VSEL 0x180 +#define PX30_IO_VSEL_VCCIO6_SRC RT_BIT(0) +#define PX30_IO_VSEL_VCCIO6_SUPPLY_NUM 1 + +static void px30_iodomain_init(struct rockchip_iodomain *domain) +{ + rt_uint32_t val; + + /* if no VCCIO6 supply we should leave things alone */ + if (!domain->supplies[PX30_IO_VSEL_VCCIO6_SUPPLY_NUM].reg) + { + return; + } + + /* set vccio6 iodomain to also use this framework instead of a special gpio. */ + val = PX30_IO_VSEL_VCCIO6_SRC | (PX30_IO_VSEL_VCCIO6_SRC << 16); + + if (rt_syscon_write(domain->grf, PX30_IO_VSEL, val) < 0) + { + LOG_W("Couldn't update %s ctrl", "vccio6"); + } +} + +static const struct rockchip_iodomain_soc_data soc_data_px30 = +{ + .grf_offset = 0x180, + .supply_names = + { + [1] = + "vccio6", + "vccio1", + "vccio2", + "vccio3", + "vccio4", + "vccio5", + "vccio-oscgpi", + }, + .init = px30_iodomain_init, +}; + +static const struct rockchip_iodomain_soc_data soc_data_px30_pmu = +{ + .grf_offset = 0x100, + .supply_names = + { + [14] = + "pmuio1", + "pmuio2", + }, +}; + +/* + * On the rk3188 the io-domains are handled by a shared register with the lower + * 8 bits being still being continuing drive-strength settings. + */ +static const struct rockchip_iodomain_soc_data soc_data_rk3188 = +{ + .grf_offset = 0x104, + .supply_names = + { + [8] = + "ap0", + "ap1", + "cif", + "flash", + "vccio0", + "vccio1", + "lcdc0", + "lcdc1", + }, +}; + +static const struct rockchip_iodomain_soc_data soc_data_rk3228 = +{ + .grf_offset = 0x418, + .supply_names = + { + "vccio1", + "vccio2", + "vccio3", + "vccio4", + }, +}; + +#define RK3288_SOC_CON2 0x24c +#define RK3288_SOC_CON2_FLASH0 RT_BIT(7) +#define RK3288_SOC_FLASH_SUPPLY_NUM 2 + +static void rk3288_iodomain_init(struct rockchip_iodomain *domain) +{ + rt_uint32_t val; + + /* if no flash supply we should leave things alone */ + if (!domain->supplies[RK3288_SOC_FLASH_SUPPLY_NUM].reg) + { + return; + } + + /* set flash0 iodomain to also use this framework instead of a special gpio. */ + val = RK3288_SOC_CON2_FLASH0 | (RK3288_SOC_CON2_FLASH0 << 16); + + if (rt_syscon_write(domain->grf, RK3288_SOC_CON2, val) < 0) + { + LOG_W("Couldn't update %s ctrl", "flash0"); + } +} + +static const struct rockchip_iodomain_soc_data soc_data_rk3288 = +{ + .grf_offset = 0x380, + .supply_names = + { + "lcdc", /* LCDC_VDD */ + "dvp", /* DVPIO_VDD */ + "flash0", /* FLASH0_VDD (emmc) */ + "flash1", /* FLASH1_VDD (sdio1) */ + "wifi", /* APIO3_VDD (sdio0) */ + "bb", /* APIO5_VDD */ + "audio", /* APIO4_VDD */ + "sdcard", /* SDMMC0_VDD (sdmmc) */ + "gpio30", /* APIO1_VDD */ + "gpio1830", /* APIO2_VDD */ + }, + .init = rk3288_iodomain_init, +}; + +#define RK3308_SOC_CON0 0x300 +#define RK3308_SOC_CON0_VCCIO3 RT_BIT(8) +#define RK3308_SOC_VCCIO3_SUPPLY_NUM 3 + +static void rk3308_iodomain_init(struct rockchip_iodomain *domain) +{ + rt_uint32_t val; + + /* if no vccio3 supply we should leave things alone */ + if (!domain->supplies[RK3308_SOC_VCCIO3_SUPPLY_NUM].reg) + { + return; + } + + /* set vccio3 iodomain to also use this framework instead of a special gpio. */ + val = RK3308_SOC_CON0_VCCIO3 | (RK3308_SOC_CON0_VCCIO3 << 16); + + if (rt_syscon_write(domain->grf, RK3308_SOC_CON0, val) < 0) + { + LOG_W("Couldn't update %s ctrl", "vccio3"); + } +} + +static const struct rockchip_iodomain_soc_data soc_data_rk3308 = +{ + .grf_offset = 0x300, + .supply_names = + { + "vccio0", + "vccio1", + "vccio2", + "vccio3", + "vccio4", + "vccio5", + }, + .init = rk3308_iodomain_init, +}; + +#define RK3328_SOC_CON4 0x410 +#define RK3328_SOC_CON4_VCCIO2 RT_BIT(7) +#define RK3328_SOC_VCCIO2_SUPPLY_NUM 1 + +static void rk3328_iodomain_init(struct rockchip_iodomain *domain) +{ + rt_uint32_t val; + + /* if no vccio2 supply we should leave things alone */ + if (!domain->supplies[RK3328_SOC_VCCIO2_SUPPLY_NUM].reg) + { + return; + } + + /* set vccio2 iodomain to also use this framework instead of a special gpio. */ + val = RK3328_SOC_CON4_VCCIO2 | (RK3328_SOC_CON4_VCCIO2 << 16); + + if (rt_syscon_write(domain->grf, RK3328_SOC_CON4, val) < 0) + { + LOG_W("Couldn't update %s ctrl", "vccio2 vsel"); + } +} + +static const struct rockchip_iodomain_soc_data soc_data_rk3328 = +{ + .grf_offset = 0x410, + .supply_names = + { + "vccio1", + "vccio2", + "vccio3", + "vccio4", + "vccio5", + "vccio6", + "pmuio", + }, + .init = rk3328_iodomain_init, +}; + +#define RK3368_SOC_CON15 0x43c +#define RK3368_SOC_CON15_FLASH0 RT_BIT(14) +#define RK3368_SOC_FLASH_SUPPLY_NUM 2 + +static void rk3368_iodomain_init(struct rockchip_iodomain *domain) +{ + rt_uint32_t val; + + /* if no flash supply we should leave things alone */ + if (!domain->supplies[RK3368_SOC_FLASH_SUPPLY_NUM].reg) + { + return; + } + + /* set flash0 iodomain to also use this framework instead of a special gpio. */ + val = RK3368_SOC_CON15_FLASH0 | (RK3368_SOC_CON15_FLASH0 << 16); + + if (rt_syscon_write(domain->grf, RK3368_SOC_CON15, val) < 0) + { + LOG_W("Couldn't update %s ctrl", "flash0"); + } +} + +static const struct rockchip_iodomain_soc_data soc_data_rk3368 = +{ + .grf_offset = 0x900, + .supply_names = + { + RT_NULL, /* Reserved */ + "dvp", /* DVPIO_VDD */ + "flash0", /* FLASH0_VDD (emmc) */ + "wifi", /* APIO2_VDD (sdio0) */ + RT_NULL, + "audio", /* APIO3_VDD */ + "sdcard", /* SDMMC0_VDD (sdmmc) */ + "gpio30", /* APIO1_VDD */ + "gpio1830", /* APIO4_VDD (gpujtag) */ + }, + .init = rk3368_iodomain_init, +}; + +static const struct rockchip_iodomain_soc_data soc_data_rk3368_pmu = +{ + .grf_offset = 0x100, + .supply_names = + { + [4] = + "pmu", /* PMU IO domain*/ + "vop", /* LCDC IO domain*/ + }, +}; + +static const struct rockchip_iodomain_soc_data soc_data_rk3399 = +{ + .grf_offset = 0xe640, + .supply_names = + { + "bt656", /* APIO2_VDD */ + "audio", /* APIO5_VDD */ + "sdmmc", /* SDMMC0_VDD */ + "gpio1830", /* APIO4_VDD */ + }, +}; + +#define RK3399_PMUGRF_CON0 0x180 +#define RK3399_PMUGRF_CON0_VSEL RT_BIT(8) +#define RK3399_PMUGRF_VSEL_SUPPLY_NUM 9 + +static void rk3399_pmu_iodomain_init(struct rockchip_iodomain *domain) +{ + rt_uint32_t val; + + /* if no pmu io supply we should leave things alone */ + if (!domain->supplies[RK3399_PMUGRF_VSEL_SUPPLY_NUM].reg) + { + return; + } + + /* set pmu io iodomain to also use this framework instead of a special gpio. */ + val = RK3399_PMUGRF_CON0_VSEL | (RK3399_PMUGRF_CON0_VSEL << 16); + + if (rt_syscon_write(domain->grf, RK3399_PMUGRF_CON0, val) < 0) + { + LOG_W("couldn't update %s ctrl", "pmu io iodomain"); + } +} + +static const struct rockchip_iodomain_soc_data soc_data_rk3399_pmu = +{ + .grf_offset = 0x180, + .supply_names = + { + [9] = + "pmu1830", /* PMUIO2_VDD */ + }, + .init = rk3399_pmu_iodomain_init, +}; + +#define RK3568_PMU_GRF_IO_VSEL0 0x0140 +#define RK3568_PMU_GRF_IO_VSEL1 0x0144 +#define RK3568_PMU_GRF_IO_VSEL2 0x0148 + +static rt_err_t rk3568_iodomain_write(struct rockchip_iodomain_supply *supply, int uvolt) +{ + rt_uint32_t bit, is_3v3 = uvolt > MAX_VOLTAGE_1_8, val0, val1; + struct rockchip_iodomain *domain = supply->domain; + + switch (supply->idx) + { + case 0: /* pmuio1 */ + break; + + case 1: /* pmuio2 */ + bit = supply->idx; + val0 = RT_BIT(16 + bit) | (is_3v3 ? 0 : RT_BIT(bit)); + bit = supply->idx + 4; + val1 = RT_BIT(16 + bit) | (is_3v3 ? RT_BIT(bit) : 0); + + rt_syscon_write(domain->grf, RK3568_PMU_GRF_IO_VSEL2, val0); + rt_syscon_write(domain->grf, RK3568_PMU_GRF_IO_VSEL2, val1); + break; + + case 3: /* vccio2 */ + break; + + case 2: /* vccio1 */ + case 4: /* vccio3 */ + case 5: /* vccio4 */ + case 6: /* vccio5 */ + case 7: /* vccio6 */ + case 8: /* vccio7 */ + bit = supply->idx - 1; + val0 = RT_BIT(16 + bit) | (is_3v3 ? 0 : RT_BIT(bit)); + val1 = RT_BIT(16 + bit) | (is_3v3 ? RT_BIT(bit) : 0); + + rt_syscon_write(domain->grf, RK3568_PMU_GRF_IO_VSEL0, val0); + rt_syscon_write(domain->grf, RK3568_PMU_GRF_IO_VSEL1, val1); + break; + + default: + return -RT_EINVAL; + } + + return 0; +} + +static const struct rockchip_iodomain_soc_data soc_data_rk3568_pmu = +{ + .grf_offset = 0x140, + .supply_names = + { + "pmuio1", + "pmuio2", + "vccio1", + "vccio2", + "vccio3", + "vccio4", + "vccio5", + "vccio6", + "vccio7", + }, + .write = rk3568_iodomain_write, +}; + +static const struct rockchip_iodomain_soc_data soc_data_rv1108 = +{ + .grf_offset = 0x404, + .supply_names = + { + [11] = + "vccio1", + "vccio2", + "vccio3", + "vccio5", + "vccio6", + }, +}; + +static const struct rockchip_iodomain_soc_data soc_data_rv1108_pmu = +{ + .grf_offset = 0x104, + .supply_names = + { + "pmu", + }, +}; + +static const struct rockchip_iodomain_soc_data soc_data_rv1126_pmu = +{ + .grf_offset = 0x140, + .supply_names = + { + [1] = + "vccio1", + "vccio2", + "vccio3", + "vccio4", + "vccio5", + "vccio6", + "vccio7", + "pmuio0", + "pmuio1", + }, +}; + +static rt_err_t rockchip_iodomain_write(struct rockchip_iodomain_supply *supply, int uvolt) +{ + rt_err_t err; + rt_uint32_t val; + struct rockchip_iodomain *domain = supply->domain; + + /* set value bit */ + val = (uvolt > MAX_VOLTAGE_1_8) ? 0 : 1; + val <<= supply->idx; + /* apply hiword-mask */ + val |= (RT_BIT(supply->idx) << 16); + + if ((err = rt_syscon_write(domain->grf, domain->soc_data->grf_offset, val))) + { + LOG_E("Couldn't write to GRF"); + } + + return err; +} + +static rt_err_t rockchip_iodomain_notify(struct rt_regulator_notifier *notifier, + rt_ubase_t msg, void *data) +{ + int uvolt; + rt_err_t err; + struct rockchip_iodomain_supply *supply; + + supply = rt_container_of(notifier, struct rockchip_iodomain_supply, notifier); + + if (msg == RT_REGULATOR_MSG_VOLTAGE_CHANGE) + { + union rt_regulator_notifier_args *args = data; + + uvolt = rt_max(args->old_uvolt, args->max_uvolt); + } + else if (msg == RT_REGULATOR_MSG_VOLTAGE_CHANGE_ERR) + { + uvolt = (int)(rt_base_t)data; + } + + if (uvolt > MAX_VOLTAGE_3_3) + { + LOG_E("Voltage too high: %d", uvolt); + + if (msg == RT_REGULATOR_MSG_VOLTAGE_CHANGE) + { + return -RT_EIO; + } + } + + err = supply->domain->write(supply, uvolt); + + if (err && msg == RT_REGULATOR_MSG_VOLTAGE_CHANGE) + { + return -RT_EIO; + } + + LOG_D("Setting to %d done", uvolt); + + return err; +} + +static rt_err_t rockchip_iodomain_probe(struct rt_platform_device *pdev) +{ + rt_err_t err = RT_EOK; + struct rt_ofw_node *np, *grf_np; + struct rockchip_iodomain *domain = rt_calloc(1, sizeof(*domain)); + + if (!domain) + { + return -RT_ENOMEM; + } + + domain->dev = &pdev->parent; + domain->soc_data = pdev->id->data; + domain->write = domain->soc_data->write ? : rockchip_iodomain_write; + + np = pdev->parent.ofw_node; + + if ((grf_np = rt_ofw_get_parent(np))) + { + domain->grf = rt_syscon_find_by_ofw_node(grf_np); + rt_ofw_node_put(grf_np); + } + else + { + domain->grf = rt_syscon_find_by_ofw_phandle(np, "rockchip,grf"); + } + + if (!domain->grf) + { + err = -RT_EIO; + + goto _fail; + } + + for (int i = 0; i < MAX_SUPPLIES; ++i) + { + int uvolt; + struct rt_regulator *reg; + struct rockchip_iodomain_supply *supply; + const char *supply_name = domain->soc_data->supply_names[i]; + + if (!supply_name) + { + continue; + } + + supply = &domain->supplies[i]; + reg = rt_regulator_get_optional(domain->dev, supply_name); + + if (rt_is_err(reg)) + { + continue; + } + + uvolt = rt_regulator_get_voltage(reg); + + if (uvolt < 0) + { + /* Maybe is a switch */ + if (uvolt == -RT_ENOSYS) + { + rt_regulator_put(reg); + continue; + } + + LOG_E("Can't determine voltage: %s", supply_name); + + goto _fail; + } + + if (uvolt > MAX_VOLTAGE_3_3) + { + LOG_E("Voltage too high: %d", uvolt); + + goto _fail; + } + + supply->idx = i; + supply->domain = domain; + supply->reg = reg; + supply->notifier.callback = rockchip_iodomain_notify; + + if (domain->write(supply, uvolt)) + { + rt_regulator_put(supply->reg); + supply->reg = RT_NULL; + + goto _fail; + } + + if ((err = rt_regulator_notifier_register(supply->reg, &supply->notifier))) + { + rt_regulator_put(supply->reg); + supply->reg = RT_NULL; + + goto _fail; + } + } + + if (domain->soc_data->init) + { + domain->soc_data->init(domain); + } + + return RT_EOK; + +_fail: + for (int i = MAX_SUPPLIES - 1; i >= 0; --i) + { + struct rockchip_iodomain_supply *supply = &domain->supplies[i]; + + if (!rt_is_err_or_null(supply->reg)) + { + rt_regulator_notifier_unregister(supply->reg, &supply->notifier); + rt_regulator_put(supply->reg); + } + } + + rt_free(domain); + + return err; +} + +static const struct rt_ofw_node_id rockchip_iodomain_ofw_ids[] = +{ + { .compatible = "rockchip,px30-io-voltage-domain", .data = &soc_data_px30 }, + { .compatible = "rockchip,px30-pmu-io-voltage-domain", .data = &soc_data_px30_pmu }, + { .compatible = "rockchip,rk3188-io-voltage-domain", .data = &soc_data_rk3188 }, + { .compatible = "rockchip,rk3228-io-voltage-domain", .data = &soc_data_rk3228 }, + { .compatible = "rockchip,rk3288-io-voltage-domain", .data = &soc_data_rk3288 }, + { .compatible = "rockchip,rk3308-io-voltage-domain", .data = &soc_data_rk3308 }, + { .compatible = "rockchip,rk3328-io-voltage-domain", .data = &soc_data_rk3328 }, + { .compatible = "rockchip,rk3368-io-voltage-domain", .data = &soc_data_rk3368 }, + { .compatible = "rockchip,rk3368-pmu-io-voltage-domain", .data = &soc_data_rk3368_pmu }, + { .compatible = "rockchip,rk3399-io-voltage-domain", .data = &soc_data_rk3399 }, + { .compatible = "rockchip,rk3399-pmu-io-voltage-domain", .data = &soc_data_rk3399_pmu }, + { .compatible = "rockchip,rk3568-pmu-io-voltage-domain", .data = &soc_data_rk3568_pmu }, + { .compatible = "rockchip,rv1108-io-voltage-domain", .data = &soc_data_rv1108 }, + { .compatible = "rockchip,rv1108-pmu-io-voltage-domain", .data = &soc_data_rv1108_pmu }, + { .compatible = "rockchip,rv1126-pmu-io-voltage-domain", .data = &soc_data_rv1126_pmu }, + { /* sentinel */ } +}; + +static struct rt_platform_driver rockchip_iodomain_driver = +{ + .name = "rockchip-iodomain", + .ids = rockchip_iodomain_ofw_ids, + + .probe = rockchip_iodomain_probe, +}; +RT_PLATFORM_DRIVER_EXPORT(rockchip_iodomain_driver); diff --git a/components/drivers/soc/rockchip/rockchip.h b/components/drivers/soc/rockchip/rockchip.h new file mode 100644 index 000000000000..c9a61dbf60dd --- /dev/null +++ b/components/drivers/soc/rockchip/rockchip.h @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-3-08 GuEe-GUI the first version + */ + +#ifndef __ROCKCHIP_H__ +#define __ROCKCHIP_H__ + +#include + +#define rk_clrsetreg(addr, clr, set) HWREG32(addr) = (((clr) | (set)) << 16 | (set)) +#define rk_clrreg(addr, clr) HWREG32(addr) = ((clr) << 16) +#define rk_setreg(addr, set) HWREG32(addr) = ((set) << 16 | (set)) + +#define HIWORD_UPDATE(val, mask, shift) ((val) << (shift) | (mask) << ((shift) + 16)) + +#endif /* __ROCKCHIP_H__ */ \ No newline at end of file diff --git a/components/drivers/spi/Kconfig b/components/drivers/spi/Kconfig new file mode 100755 index 000000000000..2bbdc9633926 --- /dev/null +++ b/components/drivers/spi/Kconfig @@ -0,0 +1,84 @@ +menuconfig RT_USING_SPI + bool "Using SPI Bus/Device device drivers" + default n + + if RT_USING_SPI + config RT_USING_SPI_BITOPS + select RT_USING_PIN + bool "Use GPIO to simulate SPI" + default n + + if RT_USING_SPI_BITOPS + config RT_SPI_BITOPS_DEBUG + bool "Use simulate SPI debug message" + default n + endif + + config RT_USING_QSPI + bool "Enable QSPI mode" + default n + + config RT_USING_SPI_MSD + bool "Using SD/TF card driver with spi" + select RT_USING_DFS + default n + + config RT_USING_SFUD + bool "Using Serial Flash Universal Driver" + default n + help + An using JEDEC's SFDP standard serial (SPI) flash universal driver library + + if RT_USING_SFUD + config RT_SFUD_USING_SFDP + bool "Using auto probe flash JEDEC SFDP parameter" + default y + + config RT_SFUD_USING_FLASH_INFO_TABLE + bool "Using defined supported flash chip information table" + default y + + config RT_SFUD_USING_QSPI + bool "Using QSPI mode support" + select RT_USING_QSPI + default n + + config RT_SFUD_SPI_MAX_HZ + int "Default spi maximum speed(HZ)" + range 0 50000000 + default 50000000 + help + Read the JEDEC SFDP command must run at 50 MHz or less,and you also can use rt_spi_configure(); to config spi speed. + + config RT_DEBUG_SFUD + bool "Show more SFUD debug information" + default n + endif + + config RT_USING_ENC28J60 + bool "Using ENC28J60 SPI Ethernet network interface" + select RT_USING_LWIP + default n + + config RT_USING_SPI_WIFI + bool "Using RW009/007 SPI Wi-Fi wireless interface" + select RT_USING_LWIP + default n + endif + +config RT_SPI_ROCKCHIP_SFC + bool "Rockchip Serial Flash Controller (SFC)" + depends on RT_USING_DM + depends on RT_USING_SPI + select RT_USING_QSPI + select RT_USING_PIN + select RT_USING_PINCTRL + default n + +config RT_SPI_ROCKCHIP + bool "Rockchip SPI controller driver" + depends on RT_USING_DM + depends on RT_USING_SPI + select RT_USING_PIN + select RT_USING_PINCTRL + default n diff --git a/components/drivers/spi/SConscript b/components/drivers/spi/SConscript index 27b7a8dd71fe..beda55f8efb6 100644 --- a/components/drivers/spi/SConscript +++ b/components/drivers/spi/SConscript @@ -29,10 +29,20 @@ if GetDepend('RT_USING_SFUD'): if GetDepend('RT_SFUD_USING_SFDP'): src_device += ['sfud/src/sfud_sfdp.c'] - if rtconfig.PLATFORM in ['gcc', 'armclang']: - LOCAL_CFLAGS += ' -std=c99' - elif rtconfig.PLATFORM in ['armcc']: - LOCAL_CFLAGS += ' --c99' + if not GetDepend("RT_USING_DM"): + if rtconfig.PLATFORM in ['gcc', 'armclang']: + LOCAL_CFLAGS += ' -std=c99' + elif rtconfig.PLATFORM in ['armcc']: + LOCAL_CFLAGS += ' --c99' + +if GetDepend('RT_USING_DM'): + src += ['spi_dm.c', 'spi_bus.c'] + + if GetDepend('RT_SPI_ROCKCHIP_SFC'): + src += ['spi-rockchip-sfc.c'] + + if GetDepend('RT_SPI_ROCKCHIP'): + src += ['spi-rockchip.c'] src += src_device diff --git a/components/drivers/spi/qspi_core.c b/components/drivers/spi/qspi_core.c index 010b427f5fe7..94af589943a2 100644 --- a/components/drivers/spi/qspi_core.c +++ b/components/drivers/spi/qspi_core.c @@ -19,10 +19,7 @@ rt_err_t rt_qspi_configure(struct rt_qspi_device *device, struct rt_qspi_configu rt_err_t result = RT_EOK; /* copy configuration items */ - qspi_device->config.parent.mode = cfg->parent.mode; - qspi_device->config.parent.max_hz = cfg->parent.max_hz; - qspi_device->config.parent.data_width = cfg->parent.data_width; - qspi_device->config.parent.reserved = cfg->parent.reserved; + rt_memcpy(&qspi_device->config.parent, &cfg->parent, sizeof(cfg->parent)); qspi_device->config.medium_size = cfg->medium_size; qspi_device->config.ddr_mode = cfg->ddr_mode; qspi_device->config.qspi_dl_width = cfg->qspi_dl_width; diff --git a/components/drivers/spi/spi-rockchip-sfc.c b/components/drivers/spi/spi-rockchip-sfc.c new file mode 100644 index 000000000000..459b6656bd80 --- /dev/null +++ b/components/drivers/spi/spi-rockchip-sfc.c @@ -0,0 +1,1025 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-3-08 GuEe-GUI the first version + */ + +#include "spi_dm.h" + +#define DBG_TAG "spi.rockchip.sfc" +#define DBG_LVL DBG_INFO +#include + +#include +#include + +/* System control */ +#define SFC_CTRL 0x0 +#define SFC_CTRL_PHASE_SEL_NEGETIVE RT_BIT(1) +#define SFC_CTRL_CMD_BITS_SHIFT 8 +#define SFC_CTRL_ADDR_BITS_SHIFT 10 +#define SFC_CTRL_DATA_BITS_SHIFT 12 + +/* Interrupt mask */ +#define SFC_IMR 0x4 +#define SFC_IMR_RX_FULL RT_BIT(0) +#define SFC_IMR_RX_UFLOW RT_BIT(1) +#define SFC_IMR_TX_OFLOW RT_BIT(2) +#define SFC_IMR_TX_EMPTY RT_BIT(3) +#define SFC_IMR_TRAN_FINISH RT_BIT(4) +#define SFC_IMR_BUS_ERR RT_BIT(5) +#define SFC_IMR_NSPI_ERR RT_BIT(6) +#define SFC_IMR_DMA RT_BIT(7) + +/* Interrupt clear */ +#define SFC_ICLR 0x8 +#define SFC_ICLR_RX_FULL RT_BIT(0) +#define SFC_ICLR_RX_UFLOW RT_BIT(1) +#define SFC_ICLR_TX_OFLOW RT_BIT(2) +#define SFC_ICLR_TX_EMPTY RT_BIT(3) +#define SFC_ICLR_TRAN_FINISH RT_BIT(4) +#define SFC_ICLR_BUS_ERR RT_BIT(5) +#define SFC_ICLR_NSPI_ERR RT_BIT(6) +#define SFC_ICLR_DMA RT_BIT(7) + +/* FIFO threshold level */ +#define SFC_FTLR 0xc +#define SFC_FTLR_TX_SHIFT 0 +#define SFC_FTLR_TX_MASK 0x1f +#define SFC_FTLR_RX_SHIFT 8 +#define SFC_FTLR_RX_MASK 0x1f + +/* Reset FSM and FIFO */ +#define SFC_RCVR 0x10 +#define SFC_RCVR_RESET RT_BIT(0) + +/* Enhanced mode */ +#define SFC_AX 0x14 + +/* Address Bit number */ +#define SFC_ABIT 0x18 + +/* Interrupt status */ +#define SFC_ISR 0x1c +#define SFC_ISR_RX_FULL_SHIFT RT_BIT(0) +#define SFC_ISR_RX_UFLOW_SHIFT RT_BIT(1) +#define SFC_ISR_TX_OFLOW_SHIFT RT_BIT(2) +#define SFC_ISR_TX_EMPTY_SHIFT RT_BIT(3) +#define SFC_ISR_TX_FINISH_SHIFT RT_BIT(4) +#define SFC_ISR_BUS_ERR_SHIFT RT_BIT(5) +#define SFC_ISR_NSPI_ERR_SHIFT RT_BIT(6) +#define SFC_ISR_DMA_SHIFT RT_BIT(7) + +/* FIFO status */ +#define SFC_FSR 0x20 +#define SFC_FSR_TX_IS_FULL RT_BIT(0) +#define SFC_FSR_TX_IS_EMPTY RT_BIT(1) +#define SFC_FSR_RX_IS_EMPTY RT_BIT(2) +#define SFC_FSR_RX_IS_FULL RT_BIT(3) +#define SFC_FSR_TXLV_MASK RT_GENMASK(12, 8) +#define SFC_FSR_TXLV_SHIFT 8 +#define SFC_FSR_RXLV_MASK RT_GENMASK(20, 16) +#define SFC_FSR_RXLV_SHIFT 16 + +/* FSM status */ +#define SFC_SR 0x24 +#define SFC_SR_IS_IDLE 0x0 +#define SFC_SR_IS_BUSY 0x1 + +/* Raw interrupt status */ +#define SFC_RISR 0x28 +#define SFC_RISR_RX_FULL RT_BIT(0) +#define SFC_RISR_RX_UNDERFLOW RT_BIT(1) +#define SFC_RISR_TX_OVERFLOW RT_BIT(2) +#define SFC_RISR_TX_EMPTY RT_BIT(3) +#define SFC_RISR_TRAN_FINISH RT_BIT(4) +#define SFC_RISR_BUS_ERR RT_BIT(5) +#define SFC_RISR_NSPI_ERR RT_BIT(6) +#define SFC_RISR_DMA RT_BIT(7) + +/* Version */ +#define SFC_VER 0x2c +#define SFC_VER_3 0x3 +#define SFC_VER_4 0x4 +#define SFC_VER_5 0x5 +#define SFC_VER_6 0x6 + +/* Delay line controller resiter */ +#define SFC_DLL_CTRL0 0x3c +#define SFC_DLL_CTRL0_SCLK_SMP_DLL RT_BIT(15) +#define SFC_DLL_CTRL0_DLL_MAX_VER4 0xffU +#define SFC_DLL_CTRL0_DLL_MAX_VER5 0x1ffU + +/* Master trigger */ +#define SFC_DMA_TRIGGER 0x80 +#define SFC_DMA_TRIGGER_START 1 + +/* Src or Dst addr for master */ +#define SFC_DMA_ADDR 0x84 + +/* Length control register extension 32GB */ +#define SFC_LEN_CTRL 0x88 +#define SFC_LEN_CTRL_TRB_SEL 1 +#define SFC_LEN_EXT 0x8c + +/* Command */ +#define SFC_CMD 0x100 +#define SFC_CMD_IDX_SHIFT 0 +#define SFC_CMD_DUMMY_SHIFT 8 +#define SFC_CMD_DIR_SHIFT 12 +#define SFC_CMD_DIR_RD 0 +#define SFC_CMD_DIR_WR 1 +#define SFC_CMD_ADDR_SHIFT 14 +#define SFC_CMD_ADDR_0BITS 0 +#define SFC_CMD_ADDR_24BITS 1 +#define SFC_CMD_ADDR_32BITS 2 +#define SFC_CMD_ADDR_XBITS 3 +#define SFC_CMD_TRAN_BYTES_SHIFT 16 +#define SFC_CMD_CS_SHIFT 30 + +/* Address */ +#define SFC_ADDR 0x104 + +/* Data */ +#define SFC_DATA 0x108 + +/* The controller and documentation reports that it supports up to 4 CS + * devices (0-3), however I have only been able to test a single CS (CS 0) + * due to the configuration of my device. + */ +#define SFC_MAX_CHIPSELECT_NUM 4 + +/* The SFC can transfer max 16KB - 1 at one time + * we set it to 15.5KB here for alignment. + */ +#define SFC_MAX_IOSIZE_VER3 (512 * 31) +#define SFC_MAX_IOSIZE_VER4 (0xffffffffU) + +/* DMA is only enabled for large data transmission */ +#define SFC_DMA_TRANS_THRETHOLD 0x40 + +/* Maximum clock values from datasheet suggest keeping clock value under + * 150MHz. No minimum or average value is suggested. + */ +#define SFC_MAX_SPEED (150 * 1000 * 1000) +#define SFC_DLL_THRESHOLD_RATE (50 * 1000 * 1000) + +#define SFC_DLL_TRANING_STEP 10 /* Training step */ +#define SFC_DLL_TRANING_VALID_WINDOW 80 /* Valid DLL winbow */ + +#define ROCKCHIP_AUTOSUSPEND_DELAY 2000 + +struct rockchip_sfc +{ + struct rt_spi_bus parent; + + struct rt_clk *clk; + struct rt_clk *hclk; + + int irq; + void *regs; + + void *dma_buffer; + void *dma_buffer_cache; + rt_ubase_t dma_buffer_phy; + rt_bool_t use_dma; + + rt_uint32_t frequency; + rt_uint32_t max_speed_hz; + rt_uint32_t max_iosize; + rt_uint32_t dll_cells; + rt_uint16_t version; + + struct rt_completion done; +}; + +#define raw_to_rockchip_sfc(raw) rt_container_of(raw, struct rockchip_sfc, parent) + +rt_inline rt_uint32_t rockchip_sfc_readl(struct rockchip_sfc *rk_sfc, int offset) +{ + return HWREG32(rk_sfc->regs + offset); +} + +rt_inline void rockchip_sfc_writel(struct rockchip_sfc *rk_sfc, int offset, rt_uint32_t value) +{ + HWREG32(rk_sfc->regs + offset) = value; +} + +static rt_err_t rockchip_sfc_poll(struct rockchip_sfc *rk_sfc, int offset, + rt_uint32_t flag, rt_bool_t is_set, + rt_uint32_t delay_us, rt_int32_t timeout_us) +{ + if (!delay_us) + { + delay_us = 1; + } + + for (; timeout_us > 0; timeout_us -= delay_us) + { + if (is_set) + { + if (rockchip_sfc_readl(rk_sfc, offset) & flag) + { + return RT_EOK; + } + } + else + { + if (!(rockchip_sfc_readl(rk_sfc, offset) & flag)) + { + return RT_EOK; + } + } + + rt_hw_us_delay(delay_us); + rt_hw_cpu_relax(); + } + + return -RT_ETIMEOUT; +} + +static rt_err_t rockchip_sfc_reset(struct rockchip_sfc *rk_sfc) +{ + rt_err_t err; + + rockchip_sfc_writel(rk_sfc, SFC_RCVR, SFC_RCVR_RESET); + + err = rockchip_sfc_poll(rk_sfc, SFC_RCVR, SFC_RCVR_RESET, RT_FALSE, + 20, 1000000L); + + if (err) + { + LOG_E("SFC reset never finished"); + } + + /* Still need to clear the masked interrupt from RISR */ + rockchip_sfc_writel(rk_sfc, SFC_ICLR, 0xffffffff); + + return err; +} + +static rt_uint16_t rockchip_sfc_get_version(struct rockchip_sfc *rk_sfc) +{ + return (rt_uint16_t)(rockchip_sfc_readl(rk_sfc, SFC_VER) & 0xffff); +} + +static rt_uint32_t rockchip_sfc_get_max_iosize(struct rockchip_sfc *rk_sfc) +{ + return SFC_MAX_IOSIZE_VER3; +} + +static rt_uint32_t rockchip_sfc_get_max_dll_cells(struct rockchip_sfc *rk_sfc) +{ + switch (rockchip_sfc_get_version(rk_sfc)) + { + case SFC_VER_6: + case SFC_VER_5: + return SFC_DLL_CTRL0_DLL_MAX_VER5; + + case SFC_VER_4: + return SFC_DLL_CTRL0_DLL_MAX_VER4; + + default: + return 0; + } +} + +static void rockchip_sfc_set_delay_lines(struct rockchip_sfc *rk_sfc, + rt_uint16_t cells) +{ + rt_uint32_t val = 0; + rt_uint16_t cell_max = (rt_uint16_t)rockchip_sfc_get_max_dll_cells(rk_sfc); + + if (cells > cell_max) + { + cells = cell_max; + } + + if (cells) + { + val = SFC_DLL_CTRL0_SCLK_SMP_DLL | cells; + } + + rockchip_sfc_writel(rk_sfc, SFC_DLL_CTRL0, val); +} + +static void rockchip_sfc_irq_unmask(struct rockchip_sfc *rk_sfc, rt_uint32_t mask) +{ + /* Enable transfer complete interrupt */ + rockchip_sfc_writel(rk_sfc, SFC_IMR, + rockchip_sfc_readl(rk_sfc, SFC_IMR) & (~mask)); +} + +static void rockchip_sfc_irq_mask(struct rockchip_sfc *rk_sfc, rt_uint32_t mask) +{ + /* Disable transfer finish interrupt */ + rockchip_sfc_writel(rk_sfc, SFC_IMR, + rockchip_sfc_readl(rk_sfc, SFC_IMR) | mask); +} + +static rt_err_t rockchip_sfc_init(struct rockchip_sfc *rk_sfc) +{ + rockchip_sfc_writel(rk_sfc, SFC_CTRL, 0); + rockchip_sfc_writel(rk_sfc, SFC_ICLR, 0xffffffff); + + rockchip_sfc_irq_mask(rk_sfc, 0xFFFFFFFF); + + if (rockchip_sfc_get_version(rk_sfc) >= SFC_VER_4) + { + rockchip_sfc_writel(rk_sfc, SFC_LEN_CTRL, SFC_LEN_CTRL_TRB_SEL); + } + + return RT_EOK; +} + +static rt_ssize_t rockchip_sfc_wait_txfifo_ready(struct rockchip_sfc *rk_sfc, + rt_uint32_t timeout_us) +{ + if (rockchip_sfc_poll(rk_sfc, SFC_FSR, SFC_FSR_TXLV_MASK, RT_TRUE, 0, timeout_us)) + { + LOG_D("Wait tx fifo timeout"); + + return -RT_ETIMEOUT; + } + + return (rockchip_sfc_readl(rk_sfc, SFC_FSR) & SFC_FSR_TXLV_MASK) >> SFC_FSR_TXLV_SHIFT; +} + +static rt_ssize_t rockchip_sfc_wait_rxfifo_ready(struct rockchip_sfc *rk_sfc, + rt_uint32_t timeout_us) +{ + if (rockchip_sfc_poll(rk_sfc, SFC_FSR, SFC_FSR_RXLV_MASK, RT_TRUE, 0, timeout_us)) + { + LOG_D("Wait rx fifo timeout"); + + return -RT_ETIMEOUT; + } + + return (rockchip_sfc_readl(rk_sfc, SFC_FSR) & SFC_FSR_RXLV_MASK) >> SFC_FSR_RXLV_SHIFT; +} + +static rt_ssize_t rockchip_sfc_write_fifo(struct rockchip_sfc *rk_sfc, + const rt_uint8_t *buf, int len) +{ + int tx_level; + rt_uint8_t bytes = len & 0x3; + rt_uint32_t dwords, write_words, tmp = 0; + + dwords = len >> 2; + while (dwords) + { + if ((tx_level = rockchip_sfc_wait_txfifo_ready(rk_sfc, 1000)) < 0) + { + return tx_level; + } + + write_words = rt_min_t(rt_uint32_t, tx_level, dwords); + + for (int i = 0; i < write_words; ++i) + { + rockchip_sfc_writel(rk_sfc, SFC_DATA, ((rt_uint32_t *)buf)[i]); + } + + buf += write_words << 2; + dwords -= write_words; + } + + /* write the rest non word aligned bytes */ + if (bytes) + { + if ((tx_level = rockchip_sfc_wait_txfifo_ready(rk_sfc, 1000)) < 0) + { + return tx_level; + } + + rt_memcpy(&tmp, buf, bytes); + rockchip_sfc_writel(rk_sfc, SFC_DATA, tmp); + } + + return len; +} + +static rt_ssize_t rockchip_sfc_read_fifo(struct rockchip_sfc *rk_sfc, + rt_uint8_t *buf, int len) +{ + int rx_level; + rt_uint8_t bytes = len & 0x3; + rt_uint32_t dwords, read_words, tmp = 0; + + /* word aligned access only */ + dwords = len >> 2; + while (dwords) + { + if ((rx_level = rockchip_sfc_wait_rxfifo_ready(rk_sfc, 1000)) < 0) + { + return rx_level; + } + + read_words = rt_min_t(rt_uint32_t, rx_level, dwords); + + for (int i = 0; i < read_words; ++i) + { + ((rt_uint32_t *)buf)[i] = rockchip_sfc_readl(rk_sfc, SFC_DATA); + } + + buf += read_words << 2; + dwords -= read_words; + } + + /* read the rest non word aligned bytes */ + if (bytes) + { + if ((rx_level = rockchip_sfc_wait_rxfifo_ready(rk_sfc, 1000)) < 0) + { + return rx_level; + } + + tmp = rockchip_sfc_readl(rk_sfc, SFC_DATA); + rt_memcpy(buf, &tmp, bytes); + } + + return len; +} + +static rt_size_t rockchip_sfc_fifo_transfer_dma(struct rockchip_sfc *rk_sfc, + rt_ubase_t dma_buf, rt_size_t len) +{ + rockchip_sfc_writel(rk_sfc, SFC_ICLR, 0xffffffff); + rockchip_sfc_writel(rk_sfc, SFC_DMA_ADDR, (rt_uint32_t)dma_buf); + rockchip_sfc_writel(rk_sfc, SFC_DMA_TRIGGER_START, SFC_DMA_TRIGGER); + + return len; +} + +static rt_ssize_t rockchip_sfc_xfer_data_poll(struct rockchip_sfc *rk_sfc, + struct rt_spi_message *xfer) +{ + rt_ssize_t res = -RT_EINVAL, len = xfer->length; + + if (xfer->send_buf) + { + if ((res = rockchip_sfc_write_fifo(rk_sfc, xfer->send_buf, len)) < 0) + { + return res; + } + } + + if (xfer->recv_buf) + { + res = rockchip_sfc_read_fifo(rk_sfc, xfer->recv_buf, len); + } + + return res; +} + +static rt_ssize_t rockchip_sfc_xfer_data_dma(struct rockchip_sfc *rk_sfc, + struct rt_spi_message *xfer) +{ + rt_ssize_t res = -RT_EINVAL, len = xfer->length; + + if (xfer->send_buf) + { + rt_memcpy(rk_sfc->dma_buffer, xfer->send_buf, len); + + res = rockchip_sfc_fifo_transfer_dma(rk_sfc, rk_sfc->dma_buffer_phy, len); + + if (rt_completion_wait(&rk_sfc->done, rt_tick_from_millisecond(2000))) + { + LOG_E("DMA wait for %s transfer finish timeout", "tx"); + res = -RT_ETIMEOUT; + + goto _out_irq; + } + } + + if (xfer->recv_buf) + { + res = rockchip_sfc_fifo_transfer_dma(rk_sfc, rk_sfc->dma_buffer_phy, len); + + if (rt_completion_wait(&rk_sfc->done, rt_tick_from_millisecond(2000))) + { + LOG_E("DMA wait for %s transfer finish timeout", "rx"); + res = -RT_ETIMEOUT; + } + } + +_out_irq: + rockchip_sfc_irq_mask(rk_sfc, SFC_IMR_DMA); + + if (res > 0 && xfer->recv_buf) + { + rt_memcpy(xfer->recv_buf, rk_sfc->dma_buffer, len); + } + + return res; +} + +static rt_err_t rockchip_sfc_xfer_done(struct rockchip_sfc *rk_sfc, + rt_uint32_t timeout_us) +{ + if (rockchip_sfc_poll(rk_sfc, SFC_SR, SFC_SR_IS_BUSY, RT_FALSE, + 20, timeout_us)) + { + LOG_E("Wait sfc idle timeout"); + rockchip_sfc_reset(rk_sfc); + + return -RT_EIO; + } + + return RT_EOK; +} + +static rt_err_t rockchip_sfc_xfer_setup(struct rockchip_sfc *rk_sfc, + struct rt_spi_device *spi_dev, struct rt_qspi_message *qspi_xfer) +{ + rt_uint32_t ctrl = 0, cmd = 0; + rt_ssize_t len = qspi_xfer->parent.length; + + /* instruction */ + cmd = qspi_xfer->instruction.content; + ctrl |= (qspi_xfer->instruction.qspi_lines >> 1) << SFC_CTRL_CMD_BITS_SHIFT; + + if (qspi_xfer->address.size) + { + if (qspi_xfer->address.size == 4) + { + cmd |= SFC_CMD_ADDR_32BITS << SFC_CMD_ADDR_SHIFT; + } + else if (qspi_xfer->address.size == 3) + { + cmd |= SFC_CMD_ADDR_24BITS << SFC_CMD_ADDR_SHIFT; + } + else + { + cmd |= SFC_CMD_ADDR_XBITS << SFC_CMD_ADDR_SHIFT; + rockchip_sfc_writel(rk_sfc, SFC_ABIT, qspi_xfer->address.size * 8 - 1); + } + + ctrl |= ((qspi_xfer->address.qspi_lines >> 1) << SFC_CTRL_ADDR_BITS_SHIFT); + } + + /* alternate_bytes */ + if (qspi_xfer->alternate_bytes.size) + { + if (qspi_xfer->alternate_bytes.qspi_lines == 4) + { + cmd |= qspi_xfer->alternate_bytes.size * 2 << SFC_CMD_DUMMY_SHIFT; + } + else if (qspi_xfer->alternate_bytes.qspi_lines == 2) + { + cmd |= qspi_xfer->alternate_bytes.size * 4 << SFC_CMD_DUMMY_SHIFT; + } + else + { + cmd |= qspi_xfer->alternate_bytes.size * 8 << SFC_CMD_DUMMY_SHIFT; + } + } + + /* address */ + if (rk_sfc->version >= SFC_VER_4) + { + /* Clear it if no data to transfer */ + rockchip_sfc_writel(rk_sfc, SFC_LEN_EXT, len); + } + else + { + cmd |= len << SFC_CMD_TRAN_BYTES_SHIFT; + } + if (len) + { + if (qspi_xfer->parent.send_buf) + { + cmd |= SFC_CMD_DIR_WR << SFC_CMD_DIR_SHIFT; + } + + ctrl |= ((qspi_xfer->qspi_data_lines >> 1) << SFC_CTRL_DATA_BITS_SHIFT); + } + if (!len && qspi_xfer->address.size) + { + cmd |= SFC_CMD_DIR_WR << SFC_CMD_DIR_SHIFT; + } + + /* controller */ + ctrl |= SFC_CTRL_PHASE_SEL_NEGETIVE; + cmd |= spi_dev->chip_select << SFC_CMD_CS_SHIFT; + + rockchip_sfc_writel(rk_sfc, SFC_CTRL, ctrl); + rockchip_sfc_writel(rk_sfc, SFC_CMD, cmd); + + if (qspi_xfer->address.size) + { + rockchip_sfc_writel(rk_sfc, SFC_ADDR, qspi_xfer->address.content); + } + + return RT_EOK; +} + +static void rockchip_sfc_adjust_op_work(struct rt_qspi_message *qspi_xfer) +{ + if (qspi_xfer->alternate_bytes.size && !qspi_xfer->address.size) + { + /* + * SFC not support output DUMMY cycles right after CMD cycles, so + * treat it as ADDR cycles. + */ + qspi_xfer->address.size = qspi_xfer->alternate_bytes.size; + qspi_xfer->address.qspi_lines = qspi_xfer->alternate_bytes.qspi_lines; + qspi_xfer->address.content = 0xffffffff; + + qspi_xfer->alternate_bytes.size = 0; + } +} + +static rt_err_t rockchip_sfc_exec_op_bypass(struct rockchip_sfc *rk_sfc, + struct rt_spi_device *spi_dev, struct rt_qspi_message *qspi_xfer) +{ + rt_uint32_t res, len; + + len = rt_min_t(rt_uint32_t, qspi_xfer->parent.length, rk_sfc->max_iosize); + + rockchip_sfc_adjust_op_work(qspi_xfer); + rockchip_sfc_xfer_setup(rk_sfc, spi_dev, qspi_xfer); + res = rockchip_sfc_xfer_data_poll(rk_sfc, &qspi_xfer->parent); + + if (res != len) + { + LOG_E("xfer data failed ret %d", res); + + return -RT_EIO; + } + + return rockchip_sfc_xfer_done(rk_sfc, 100000); +} + +static void rockchip_sfc_delay_lines_tuning(struct rockchip_sfc *rk_sfc, + struct rt_spi_device *spi_dev) +{ + rt_uint8_t id[3], id_temp[3]; + rt_bool_t dll_valid = RT_FALSE; + rt_uint16_t right, left = 0, step = SFC_DLL_TRANING_STEP; + rt_uint16_t cell_max = (rt_uint16_t)rockchip_sfc_get_max_dll_cells(rk_sfc); + struct rt_qspi_message qspi_xfer = {}; + struct rt_spi_message *xfer = &qspi_xfer.parent; + + rt_clk_set_rate(rk_sfc->clk, SFC_DLL_THRESHOLD_RATE); + + qspi_xfer.instruction.content = 0x9f; + qspi_xfer.qspi_data_lines = 1; + + xfer->recv_buf = &id; + xfer->length = sizeof(id); + xfer->cs_take = 1; + xfer->cs_release = 1; + rockchip_sfc_exec_op_bypass(rk_sfc, spi_dev, &qspi_xfer); + + if ((0xff == id[0] && 0xff == id[1]) || (0x00 == id[0] && 0x00 == id[1])) + { + rt_clk_set_rate(rk_sfc->clk, rk_sfc->max_speed_hz); + + return; + } + + rt_clk_set_rate(rk_sfc->clk, rk_sfc->max_speed_hz); + xfer->recv_buf = &id_temp; + xfer->length = sizeof(id_temp); + + for (right = 0; right <= cell_max; right += step) + { + int res; + + rockchip_sfc_set_delay_lines(rk_sfc, right); + rockchip_sfc_exec_op_bypass(rk_sfc, spi_dev, &qspi_xfer); + + res = rt_memcmp(&id, &id_temp, sizeof(id)); + + if (dll_valid && res) + { + right -= step; + break; + } + + if (!dll_valid && !res) + { + left = right; + } + + if (!res) + { + dll_valid = RT_TRUE; + } + + /* Add cell_max to loop */ + if (right == cell_max) + { + break; + } + if (right + step > cell_max) + { + right = cell_max - step; + } + } + + if (dll_valid && (right - left) >= SFC_DLL_TRANING_VALID_WINDOW) + { + if (left == 0 && right < cell_max) + { + rk_sfc->dll_cells = left + (right - left) * 2 / 5; + } + else + { + rk_sfc->dll_cells = left + (right - left) / 2; + } + } + else + { + rk_sfc->dll_cells = 0; + } + + if (rk_sfc->dll_cells) + { + rockchip_sfc_set_delay_lines(rk_sfc, (rt_uint16_t)rk_sfc->dll_cells); + } + else + { + LOG_E("%d %d dll training failed in %dMHz, reduce the frequency", + left, right, rk_sfc->max_speed_hz); + + rockchip_sfc_set_delay_lines(rk_sfc, 0); + rt_clk_set_rate(rk_sfc->clk, SFC_DLL_THRESHOLD_RATE); + rk_sfc->max_speed_hz = rt_clk_get_rate(rk_sfc->clk); + } +} + +static rt_err_t rockchip_sfc_configure(struct rt_spi_device *device, + struct rt_spi_configuration *conf) +{ + rt_err_t err; + struct rockchip_sfc *rk_sfc = raw_to_rockchip_sfc(device->bus); + + if (conf->max_hz != rk_sfc->frequency) + { + if ((err = rt_clk_set_rate(rk_sfc->clk, rk_sfc->max_speed_hz))) + { + return err; + } + + rk_sfc->frequency = rk_sfc->max_speed_hz; + + if (rockchip_sfc_get_version(rk_sfc) >= SFC_VER_4) + { + if (rt_clk_get_rate(rk_sfc->clk) > SFC_DLL_THRESHOLD_RATE) + { + rockchip_sfc_delay_lines_tuning(rk_sfc, device); + } + else + { + rockchip_sfc_set_delay_lines(rk_sfc, 0); + } + } + } + + return RT_EOK; +} + +static rt_ssize_t rockchip_sfc_xfer(struct rt_spi_device *device, + struct rt_spi_message *xfer) +{ + rt_err_t err; + rt_ssize_t res, len = xfer->length; + struct rockchip_sfc *rk_sfc = raw_to_rockchip_sfc(device->bus); + struct rt_qspi_message *qspi_xfer = rt_container_of(xfer, struct rt_qspi_message, parent); + + rockchip_sfc_adjust_op_work(qspi_xfer); + rockchip_sfc_xfer_setup(rk_sfc, device, qspi_xfer); + + if (rk_sfc->use_dma && len >= SFC_DMA_TRANS_THRETHOLD && !(len & 0x3)) + { + rockchip_sfc_irq_unmask(rk_sfc, SFC_IMR_DMA); + res = rockchip_sfc_xfer_data_dma(rk_sfc, xfer); + } + else + { + res = rockchip_sfc_xfer_data_poll(rk_sfc, xfer); + } + + if (res != len) + { + LOG_E("xfer data failed"); + return -RT_EIO; + } + + if ((err = rockchip_sfc_xfer_done(rk_sfc, 100000))) + { + res = err; + } + + return err; +} + +static const struct rt_spi_ops rockchip_sfc_ops = +{ + .configure = rockchip_sfc_configure, + .xfer = rockchip_sfc_xfer, +}; + +static void rockchip_sfc_isr(int irqno, void *param) +{ + rt_uint32_t isr; + struct rockchip_sfc *rk_sfc = param; + + isr = rockchip_sfc_readl(rk_sfc, SFC_RISR); + /* Clear interrupt */ + rockchip_sfc_writel(rk_sfc, SFC_ICLR, isr); + + if (isr & SFC_RISR_DMA) + { + rt_completion_done(&rk_sfc->done); + } +} + +static void rockchip_sfc_free(struct rockchip_sfc *rk_sfc) +{ + if (rk_sfc->regs) + { + rt_iounmap(rk_sfc->regs); + } + + if (!rt_is_err_or_null(rk_sfc->clk)) + { + rt_clk_put(rk_sfc->clk); + } + + if (!rt_is_err_or_null(rk_sfc->hclk)) + { + rt_clk_put(rk_sfc->hclk); + } + + if (rk_sfc->dma_buffer_cache) + { + rt_pages_free(rk_sfc->dma_buffer_cache, rt_page_bits(SFC_MAX_IOSIZE_VER3)); + } + + if (rk_sfc->dma_buffer) + { + rt_iounmap(rk_sfc->dma_buffer); + } + + rt_free(rk_sfc); +} + +static rt_err_t rockchip_sfc_probe(struct rt_platform_device *pdev) +{ + rt_err_t err; + const char *bus_name; + struct rt_device *dev = &pdev->parent; + struct rockchip_sfc *rk_sfc = rt_calloc(1, sizeof(*rk_sfc)); + + if (!rk_sfc) + { + return -RT_ENOMEM; + } + + rk_sfc->regs = rt_dm_dev_iomap(dev, 0); + + if (!rk_sfc->regs) + { + err = -RT_EIO; + goto _fail; + } + + rk_sfc->clk = rt_clk_get_by_name(dev, "clk_sfc"); + + if (rt_is_err(rk_sfc->clk)) + { + err = rt_ptr_err(rk_sfc->clk); + goto _fail; + } + + rk_sfc->hclk = rt_clk_get_by_name(dev, "hclk_sfc"); + + if (rt_is_err(rk_sfc->hclk)) + { + err = rt_ptr_err(rk_sfc->hclk); + goto _fail; + } + + rk_sfc->irq = rt_dm_dev_get_irq(dev, 0); + + if (rk_sfc->irq < 0) + { + err = rk_sfc->irq; + goto _fail; + } + + rk_sfc->use_dma = !rt_dm_dev_prop_read_bool(dev, "rockchip,sfc-no-dma"); + + if (rk_sfc->use_dma) + { + rk_sfc->dma_buffer_cache = rt_pages_alloc(rt_page_bits(SFC_MAX_IOSIZE_VER3)); + + if (!rk_sfc->dma_buffer_cache) + { + LOG_E("Could not alloc DMA memory witch cache"); + + goto _fail; + } + + rk_sfc->dma_buffer_phy = (rt_ubase_t)rt_kmem_v2p(rk_sfc->dma_buffer_cache); + rk_sfc->dma_buffer = rt_ioremap_nocache((void *)rk_sfc->dma_buffer_phy, SFC_MAX_IOSIZE_VER3); + + if (!rk_sfc->dma_buffer) + { + LOG_E("Could not alloc DMA memory"); + + goto _fail; + } + } + + if ((err = rt_clk_prepare_enable(rk_sfc->hclk))) + { + goto _fail; + } + + if ((err = rt_clk_prepare_enable(rk_sfc->clk))) + { + goto _fail; + } + +#ifdef RT_USING_OFW + if (rt_ofw_bootargs_select("rockchip.thunder_boot", 0)) + { + if (rockchip_sfc_poll(rk_sfc, SFC_SR, SFC_SR_IS_BUSY, RT_FALSE, 10, 500000)) + { + LOG_E("Wait for SFC idle timeout"); + } + } +#endif + + if ((err = rockchip_sfc_init(rk_sfc))) + { + goto _fail; + } + + rk_sfc->max_iosize = rockchip_sfc_get_max_iosize(rk_sfc); + rk_sfc->version = rockchip_sfc_get_version(rk_sfc); + + rk_sfc->max_speed_hz = SFC_MAX_SPEED; + rk_sfc->parent.num_chipselect = SFC_MAX_CHIPSELECT_NUM; + rt_completion_init(&rk_sfc->done); + + dev->user_data = rk_sfc; + + rk_sfc->parent.parent.ofw_node = dev->ofw_node; + + rt_dm_dev_set_name_auto(&rk_sfc->parent.parent, "qspi"); + bus_name = rt_dm_dev_get_name(&rk_sfc->parent.parent); + + rt_hw_interrupt_install(rk_sfc->irq, rockchip_sfc_isr, rk_sfc, bus_name); + rt_hw_interrupt_umask(rk_sfc->irq); + + if ((err = rt_qspi_bus_register(&rk_sfc->parent, bus_name, &rockchip_sfc_ops))) + { + goto _fail; + } + + return RT_EOK; + +_fail: + rockchip_sfc_free(rk_sfc); + + return err; +} + +static rt_err_t rockchip_sfc_remove(struct rt_platform_device *pdev) +{ + struct rockchip_sfc *rk_sfc = pdev->parent.user_data; + + rt_hw_interrupt_mask(rk_sfc->irq); + rt_pic_detach_irq(rk_sfc->irq, rk_sfc); + + rt_device_unregister(&rk_sfc->parent.parent); + + rockchip_sfc_free(rk_sfc); + + return RT_EOK; +} + +static const struct rt_ofw_node_id rockchip_sfc_ofw_ids[] = +{ + { .compatible = "rockchip,sfc", }, + { /* sentinel */ } +}; + +static struct rt_platform_driver rockchip_sfc_driver = +{ + .name = "rockchip-sfc", + .ids = rockchip_sfc_ofw_ids, + + .probe = rockchip_sfc_probe, + .remove = rockchip_sfc_remove, +}; +RT_PLATFORM_DRIVER_EXPORT(rockchip_sfc_driver); diff --git a/components/drivers/spi/spi-rockchip.c b/components/drivers/spi/spi-rockchip.c new file mode 100644 index 000000000000..03d13885d908 --- /dev/null +++ b/components/drivers/spi/spi-rockchip.c @@ -0,0 +1,1064 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-3-08 GuEe-GUI the first version + */ + +#include "spi_dm.h" + +#define DBG_TAG "spi.rockchip" +#define DBG_LVL DBG_INFO +#include + +#include + +#define ROCKCHIP_SPI_CLR_BITS(reg, bits) HWREG32(reg) = HWREG32(reg) & ~(bits) +#define ROCKCHIP_SPI_SET_BITS(reg, bits) HWREG32(reg) = HWREG32(reg) | (bits) + +/* SPI register offsets */ +#define ROCKCHIP_SPI_CTRLR0 0x0000 +#define ROCKCHIP_SPI_CTRLR1 0x0004 +#define ROCKCHIP_SPI_SSIENR 0x0008 +#define ROCKCHIP_SPI_SER 0x000c +#define ROCKCHIP_SPI_BAUDR 0x0010 +#define ROCKCHIP_SPI_TXFTLR 0x0014 +#define ROCKCHIP_SPI_RXFTLR 0x0018 +#define ROCKCHIP_SPI_TXFLR 0x001c +#define ROCKCHIP_SPI_RXFLR 0x0020 +#define ROCKCHIP_SPI_SR 0x0024 +#define ROCKCHIP_SPI_IPR 0x0028 +#define ROCKCHIP_SPI_IMR 0x002c +#define ROCKCHIP_SPI_ISR 0x0030 +#define ROCKCHIP_SPI_RISR 0x0034 +#define ROCKCHIP_SPI_ICR 0x0038 +#define ROCKCHIP_SPI_DMACR 0x003c +#define ROCKCHIP_SPI_DMATDLR 0x0040 +#define ROCKCHIP_SPI_DMARDLR 0x0044 +#define ROCKCHIP_SPI_VERSION 0x0048 +#define ROCKCHIP_SPI_TXDR 0x0400 +#define ROCKCHIP_SPI_RXDR 0x0800 + +/* Bit fields in CTRLR0 */ +#define CR0_DFS_OFFSET 0 +#define CR0_DFS_4BIT 0x0 +#define CR0_DFS_8BIT 0x1 +#define CR0_DFS_16BIT 0x2 + +#define CR0_CFS_OFFSET 2 + +#define CR0_SCPH_OFFSET 6 +#define CR0_SCPH_START_EDGE 0x1 +#define CR0_SCPH_MIDDLE_EDGE 0x0 + +#define CR0_SCPOL_OFFSET 7 +#define CR0_SCPOL_HIGH 0x1 +#define CR0_SCPOL_LOW 0x0 + +#define CR0_CSM_OFFSET 8 +#define CR0_CSM_KEEP 0x0 +/* ss_n be high for half sclk_out cycles */ +#define CR0_CSM_HALF 0X1 +/* ss_n be high for one sclk_out cycle */ +#define CR0_CSM_ONE 0x2 + +/* ss_n to sclk_out delay */ +#define CR0_SSD_OFFSET 10 +/* + * The period between ss_n active and + * sclk_out active is half sclk_out cycles + */ +#define CR0_SSD_HALF 0x0 +/* + * The period between ss_n active and + * sclk_out active is one sclk_out cycle + */ +#define CR0_SSD_ONE 0x1 + +#define CR0_EM_OFFSET 11 +#define CR0_EM_LITTLE 0x0 +#define CR0_EM_BIG 0x1 + +#define CR0_FBM_OFFSET 12 +#define CR0_FBM_MSB 0x0 +#define CR0_FBM_LSB 0x1 + +#define CR0_BHT_OFFSET 13 +#define CR0_BHT_16BIT 0x0 +#define CR0_BHT_8BIT 0x1 + +#define CR0_RSD_OFFSET 14 +#define CR0_RSD_MAX 0x3 + +#define CR0_FRF_OFFSET 16 +#define CR0_FRF_SPI 0x0 +#define CR0_FRF_SSP 0x1 +#define CR0_FRF_MICROWIRE 0x2 + +#define CR0_XFM_OFFSET 18 +#define CR0_XFM_MASK (0x03 << SPI_XFM_OFFSET) +#define CR0_XFM_TR 0x0 +#define CR0_XFM_TO 0x1 +#define CR0_XFM_RO 0x2 + +#define CR0_OPM_OFFSET 20 +#define CR0_OPM_MASTER 0x0 +#define CR0_OPM_SLAVE 0x1 + +#define CR0_SOI_OFFSET 23 + +#define CR0_MTM_OFFSET 0x21 + +/* Bit fields in SER, 2bit */ +#define SER_MASK 0x3 + +/* Bit fields in BAUDR */ +#define BAUDR_SCKDV_MIN 2 +#define BAUDR_SCKDV_MAX 65534 + +/* Bit fields in SR, 6bit */ +#define SR_MASK 0x3f +#define SR_BUSY (1 << 0) +#define SR_TF_FULL (1 << 1) +#define SR_TF_EMPTY (1 << 2) +#define SR_RF_EMPTY (1 << 3) +#define SR_RF_FULL (1 << 4) +#define SR_SLAVE_TX_BUSY (1 << 5) + +/* Bit fields in ISR, IMR, ISR, RISR, 5bit */ +#define INT_MASK 0x1f +#define INT_TF_EMPTY (1 << 0) +#define INT_TF_OVERFLOW (1 << 1) +#define INT_RF_UNDERFLOW (1 << 2) +#define INT_RF_OVERFLOW (1 << 3) +#define INT_RF_FULL (1 << 4) +#define INT_CS_INACTIVE (1 << 6) + +/* Bit fields in ICR, 4bit */ +#define ICR_MASK 0x0f +#define ICR_ALL (1 << 0) +#define ICR_RF_UNDERFLOW (1 << 1) +#define ICR_RF_OVERFLOW (1 << 2) +#define ICR_TF_OVERFLOW (1 << 3) + +/* Bit fields in DMACR */ +#define RF_DMA_EN (1 << 0) +#define TF_DMA_EN (1 << 1) + +/* Driver state flags */ +#define RXDMA (1 << 0) +#define TXDMA (1 << 1) + +/* sclk_out: spi master internal logic in rk3x can support 50Mhz */ +#define MAX_SCLK_OUT 50000000U +/* max sclk of driver strength 4mA */ +#define IO_DRIVER_4MA_MAX_SCLK_OUT 24000000U + +/* + * SPI_CTRLR1 is 16-bits, so we should support lengths of 0xffff + 1. However, + * the controller seems to hang when given 0x10000, so stick with this for now. + */ +#define ROCKCHIP_SPI_MAX_TRANLEN 0xffff + +/* 2 for native cs, 2 for cs-gpio */ +#define ROCKCHIP_SPI_MAX_CS_NUM 4 +#define ROCKCHIP_SPI_VER2_TYPE1 0x05ec0002 +#define ROCKCHIP_SPI_VER2_TYPE2 0x00110002 + +struct rockchip_spi_soc_data +{ + rt_uint32_t max_baud_div_in_cpha; +}; + +struct rockchip_spi +{ + struct rt_spi_bus parent; + + struct rt_clk *spiclk; + struct rt_clk *apb_pclk; + struct rt_clk *sclk_in; + + int irq; + void *regs; + void *dma_addr_rx; + void *dma_addr_tx; + + const void *tx; + void *rx; + rt_uint32_t tx_left; + rt_uint32_t rx_left; + rt_uint32_t max_baud_div_in_cpha; + + /* DMA state */ + rt_atomic_t state; + + /* depth of the FIFO buffer */ + rt_uint32_t fifo_len; + /* frequency of spiclk */ + rt_uint32_t freq; + rt_uint32_t speed_hz; + + rt_uint8_t n_bytes; + rt_uint8_t bits_per_word; + rt_uint8_t rsd; + + rt_bool_t cs_asserted[ROCKCHIP_SPI_MAX_CS_NUM]; + + rt_int8_t high_speed_state; + rt_bool_t slave_abort; + rt_bool_t cs_inactive; /* spi slave tansmition stop when cs inactive */ + rt_bool_t cs_high_supported; /* native CS supports active-high polarity */ + + struct rt_spi_message *xfer; + + struct rt_completion done; +}; + +#define raw_to_rockchip_spi(raw) rt_container_of(raw, struct rockchip_spi, parent) + +rt_inline rt_uint32_t rockchip_spi_readl(struct rockchip_spi *rk_spi, int offset) +{ + return HWREG32(rk_spi->regs + offset); +} + +rt_inline void rockchip_spi_writel(struct rockchip_spi *rk_spi, int offset, rt_uint32_t value) +{ + HWREG32(rk_spi->regs + offset) = value; +} + +rt_inline void spi_enable_chip(struct rockchip_spi *rk_spi, rt_bool_t enable) +{ + rockchip_spi_writel(rk_spi, ROCKCHIP_SPI_SSIENR, !!enable); +} + +rt_inline void wait_for_tx_idle(struct rockchip_spi *rk_spi, rt_bool_t slave_mode) +{ + rt_tick_t timeout = rt_tick_get() + 5; + + do { + if (slave_mode) + { + if (!(rockchip_spi_readl(rk_spi, ROCKCHIP_SPI_SR) & SR_SLAVE_TX_BUSY) && + !((rockchip_spi_readl(rk_spi, ROCKCHIP_SPI_SR) & SR_BUSY))) + { + return; + } + } + else + { + if (!(rockchip_spi_readl(rk_spi, ROCKCHIP_SPI_SR) & SR_BUSY)) + { + return; + } + } + } while (timeout > rt_tick_get()); + + LOG_W("Controller is in busy state"); +} + +static rt_uint32_t get_fifo_len(struct rockchip_spi *rk_spi) +{ + rt_uint32_t ver = rockchip_spi_readl(rk_spi, ROCKCHIP_SPI_VERSION); + + switch (ver) + { + case ROCKCHIP_SPI_VER2_TYPE1: + case ROCKCHIP_SPI_VER2_TYPE2: + return 64; + + default: + return 32; + } +} + +static void rockchip_spi_set_cs(struct rt_spi_device *spi_dev, rt_bool_t enable) +{ + rt_uint32_t en_value; + struct rockchip_spi *rk_spi = raw_to_rockchip_spi(spi_dev->bus); + rt_bool_t cs_asserted = spi_dev->config.mode & RT_SPI_CS_HIGH ? !enable : enable; + + /* Return immediately for no-op */ + if (cs_asserted == rk_spi->cs_asserted[spi_dev->chip_select]) + { + return; + } + + if (spi_dev->cs_pin != PIN_NONE) + { + en_value = 1; + } + else + { + en_value = RT_BIT(spi_dev->chip_select); + } + + if (cs_asserted) + { + ROCKCHIP_SPI_SET_BITS(rk_spi->regs + ROCKCHIP_SPI_SER, en_value); + } + else + { + ROCKCHIP_SPI_CLR_BITS(rk_spi->regs + ROCKCHIP_SPI_SER, en_value); + } + + rk_spi->cs_asserted[spi_dev->chip_select] = cs_asserted; +} + +static void rockchip_spi_pio_writer(struct rockchip_spi *rk_spi) +{ + rt_uint32_t tx_free, words; + + tx_free = rk_spi->fifo_len - rockchip_spi_readl(rk_spi, ROCKCHIP_SPI_TXFLR); + words = rt_min(rk_spi->tx_left, tx_free); + + for (rk_spi->tx_left -= words; words; --words) + { + rt_uint32_t txw; + + if (rk_spi->n_bytes == 1) + { + txw = *(rt_uint8_t *)rk_spi->tx; + } + else + { + txw = *(rt_uint16_t *)rk_spi->tx; + } + + rockchip_spi_writel(rk_spi, ROCKCHIP_SPI_TXDR, txw); + rk_spi->tx += rk_spi->n_bytes; + } +} + +static void rockchip_spi_pio_reader(struct rockchip_spi *rk_spi) +{ + rt_uint32_t words, rx_left; + + words = rockchip_spi_readl(rk_spi, ROCKCHIP_SPI_RXFLR); + rx_left = (rk_spi->rx_left > words) ? rk_spi->rx_left - words : 0; + + /* + * the hardware doesn't allow us to change fifo threshold + * level while spi is enabled, so instead make sure to leave + * enough words in the rx fifo to get the last interrupt + * exactly when all words have been received + */ + if (rx_left) + { + rt_uint32_t ftl = rockchip_spi_readl(rk_spi, ROCKCHIP_SPI_RXFTLR) + 1; + + if (rx_left < ftl) + { + rx_left = ftl; + words = rk_spi->rx_left - rx_left; + } + } + + for (rk_spi->rx_left = rx_left; words; --words) + { + rt_uint32_t rxw = rockchip_spi_readl(rk_spi, ROCKCHIP_SPI_RXDR); + + if (!rk_spi->rx) + { + continue; + } + + if (rk_spi->n_bytes == 1) + { + *(rt_uint8_t *)rk_spi->rx = (rt_uint8_t)rxw; + } + else + { + *(rt_uint16_t *)rk_spi->rx = (rt_uint16_t)rxw; + } + + rk_spi->rx += rk_spi->n_bytes; + } +} + +static rt_ssize_t rockchip_spi_prepare_irq(struct rockchip_spi *rk_spi, + struct rt_spi_message *xfer) +{ + int cs_flags = 0; + + rk_spi->tx = xfer->send_buf; + rk_spi->rx = xfer->recv_buf; + rk_spi->tx_left = rk_spi->tx ? xfer->length / rk_spi->n_bytes : 0; + rk_spi->rx_left = xfer->length / rk_spi->n_bytes; + + rockchip_spi_writel(rk_spi, ROCKCHIP_SPI_ICR, 0xffffffff); + + spi_enable_chip(rk_spi, RT_TRUE); + + if (rk_spi->tx_left) + { + rockchip_spi_pio_writer(rk_spi); + } + + if (rk_spi->cs_inactive) + { + cs_flags |= INT_CS_INACTIVE; + } + + rockchip_spi_writel(rk_spi, ROCKCHIP_SPI_IMR, INT_RF_FULL | cs_flags); + + return xfer->length; +} + +static void rockchip_spi_slave_abort(struct rockchip_spi *rk_spi) +{ + rt_uint32_t rx_fifo_left; + + if (rk_spi->rx) + { + rx_fifo_left = rockchip_spi_readl(rk_spi, ROCKCHIP_SPI_RXFLR); + + for (; rx_fifo_left; --rx_fifo_left) + { + rt_uint32_t rxw = rockchip_spi_readl(rk_spi, ROCKCHIP_SPI_RXDR); + + if (rk_spi->n_bytes == 1) + { + *(rt_uint8_t *)rk_spi->rx = (rt_uint8_t)rxw; + } + else + { + *(rt_uint16_t *)rk_spi->rx = (rt_uint16_t)rxw; + } + + rk_spi->rx += rk_spi->n_bytes; + } + + rk_spi->xfer->length = (rt_uint32_t)(rk_spi->rx - rk_spi->xfer->recv_buf); + } + + spi_enable_chip(rk_spi, RT_FALSE); + rk_spi->slave_abort = RT_TRUE; + + rt_completion_done(&rk_spi->done); +} + +static rt_uint32_t rockchip_spi_calc_burst_size(rt_uint32_t data_len) +{ + rt_uint32_t i; + + /* burst size: 1, 2, 4, 8 */ + for (i = 1; i < 8; i <<= 1) + { + if (data_len & i) + { + break; + } + } + + return i; +} + +static rt_err_t rockchip_spi_config(struct rockchip_spi *rk_spi, + struct rt_spi_device *spi_dev, struct rt_spi_message *xfer, + rt_bool_t use_dma, rt_bool_t slave_mode) +{ + rt_uint32_t cr0, cr1, dmacr = 0, rxftlr; + struct rt_spi_configuration *conf = &spi_dev->config; + + rk_spi->slave_abort = RT_FALSE; + + cr0 = (CR0_BHT_8BIT << CR0_BHT_OFFSET) | /* APB, SPI 8bit write/read */ + (CR0_SSD_ONE << CR0_SSD_OFFSET) | /* One sclk_out cycle */ + (CR0_EM_BIG << CR0_EM_OFFSET) | /* Big endian */ + (CR0_FRF_SPI << CR0_FRF_OFFSET) | /* Motorola SPI */ + (CR0_CSM_KEEP << CR0_CSM_OFFSET) | /* Keep low after every frame */ + (rk_spi->rsd << CR0_RSD_OFFSET); /* Rxd sample delay */ + + if (conf->mode & RT_SPI_CPOL) + { + cr0 |= CR0_SCPOL_HIGH << CR0_SCPOL_OFFSET; + } + + if (conf->mode & RT_SPI_CPHA) + { + cr0 |= CR0_SCPH_START_EDGE << CR0_SCPH_OFFSET; + } + + if (conf->mode & RT_SPI_LSB) + { + cr0 |= CR0_FBM_LSB << CR0_FBM_OFFSET; + } + + if (slave_mode) + { + cr0 |= CR0_OPM_SLAVE << CR0_OPM_OFFSET; + } + + if (conf->mode & RT_SPI_CS_HIGH) + { + cr0 |= RT_BIT(spi_dev->chip_select) << CR0_SOI_OFFSET; + } + + if (xfer->recv_buf && xfer->send_buf) + { + cr0 |= CR0_XFM_TR << CR0_XFM_OFFSET; + } + else if (xfer->recv_buf) + { + cr0 |= CR0_XFM_RO << CR0_XFM_OFFSET; + } + else if (use_dma) + { + cr0 |= CR0_XFM_TO << CR0_XFM_OFFSET; + } + + switch (rk_spi->bits_per_word) + { + case 4: + cr0 |= CR0_DFS_4BIT << CR0_DFS_OFFSET; + cr1 = xfer->length - 1; + break; + + case 8: + cr0 |= CR0_DFS_8BIT << CR0_DFS_OFFSET; + cr1 = xfer->length - 1; + break; + + case 16: + cr0 |= CR0_DFS_16BIT << CR0_DFS_OFFSET; + cr1 = xfer->length / 2 - 1; + break; + + default: + return -RT_EINVAL; + } + + if (use_dma) + { + if (xfer->send_buf) + { + dmacr |= TF_DMA_EN; + } + if (xfer->recv_buf) + { + dmacr |= RF_DMA_EN; + } + } + + /* If speed is larger than IO_DRIVER_4MA_MAX_SCLK_OUT, set higher driver strength. */ + if (rk_spi->high_speed_state >= 0) + { + struct rt_device *dev = &rk_spi->parent.parent; + + if (rk_spi->freq > IO_DRIVER_4MA_MAX_SCLK_OUT) + { + rt_pin_ctrl_confs_apply(dev, rk_spi->high_speed_state); + } + else + { + rt_pin_ctrl_confs_apply_by_name(dev, RT_NULL); + } + } + + rockchip_spi_writel(rk_spi, ROCKCHIP_SPI_CTRLR0, cr0); + rockchip_spi_writel(rk_spi, ROCKCHIP_SPI_CTRLR1, cr1); + + /* + * unfortunately setting the fifo threshold level to generate an + * interrupt exactly when the fifo is full doesn't seem to work, + * so we need the strict inequality here + */ + if ((xfer->length / rk_spi->n_bytes) < rk_spi->fifo_len) + { + rxftlr = xfer->length / rk_spi->n_bytes - 1; + } + else + { + rxftlr = rk_spi->fifo_len / 2 - 1; + } + rockchip_spi_writel(rk_spi, ROCKCHIP_SPI_RXFTLR, rxftlr); + + rockchip_spi_writel(rk_spi, ROCKCHIP_SPI_DMATDLR, rk_spi->fifo_len / 2 - 1); + rockchip_spi_writel(rk_spi, ROCKCHIP_SPI_DMARDLR, + rockchip_spi_calc_burst_size(xfer->length / rk_spi->n_bytes) - 1); + rockchip_spi_writel(rk_spi, ROCKCHIP_SPI_DMACR, dmacr); + + if (rk_spi->max_baud_div_in_cpha && conf->max_hz != rk_spi->speed_hz) + { + /* the minimum divisor is 2 */ + if (rk_spi->freq < 2 * conf->max_hz) + { + rt_clk_set_rate(rk_spi->spiclk, 2 * conf->max_hz); + rk_spi->freq = rt_clk_get_rate(rk_spi->spiclk); + } + + if (conf->mode & RT_SPI_CPHA && + RT_DIV_ROUND_UP(rk_spi->freq, conf->max_hz) > rk_spi->max_baud_div_in_cpha) + { + rt_clk_set_rate(rk_spi->spiclk, rk_spi->max_baud_div_in_cpha * conf->max_hz); + rk_spi->freq = rt_clk_get_rate(rk_spi->spiclk); + } + } + + /* + * the hardware only supports an even clock divisor, so + * round divisor = spiclk / speed up to nearest even number + * so that the resulting speed is <= the requested speed + */ + rockchip_spi_writel(rk_spi, ROCKCHIP_SPI_BAUDR, + 2 * RT_DIV_ROUND_UP(rk_spi->freq, 2 * conf->max_hz)); + rk_spi->speed_hz = conf->max_hz; + + return RT_EOK; +} + +static rt_err_t rockchip_spi_configure(struct rt_spi_device *device, + struct rt_spi_configuration *conf) +{ + rt_uint32_t cr0; + rt_uint8_t chip_select = device->chip_select; + struct rockchip_spi *rk_spi = raw_to_rockchip_spi(device->bus); + + if (!conf->max_hz) + { + LOG_E("Not max hz in %s", rt_dm_dev_get_name(&device->parent)); + + return -RT_EINVAL; + } + + if (!rk_spi->parent.pins && conf->mode & RT_SPI_CS_HIGH && !rk_spi->cs_high_supported) + { + LOG_W("None GPIO CS can't be active-high in %s", + rt_dm_dev_get_name(&device->parent)); + return -RT_EINVAL; + } + + cr0 = rockchip_spi_readl(rk_spi, ROCKCHIP_SPI_CTRLR0); + cr0 &= ~(0x3 << CR0_SCPH_OFFSET); + + if (conf->mode & RT_SPI_CPHA) + { + cr0 |= CR0_SCPH_START_EDGE << CR0_SCPH_OFFSET; + } + + if (conf->mode & RT_SPI_CS_HIGH && chip_select <= 1) + { + cr0 |= RT_BIT(chip_select) << CR0_SOI_OFFSET; + } + else if (chip_select <= 1) + { + cr0 &= ~(RT_BIT(chip_select) << CR0_SOI_OFFSET); + } + + rockchip_spi_writel(rk_spi, ROCKCHIP_SPI_CTRLR0, cr0); + + return RT_EOK; +} + +static rt_ssize_t rockchip_spi_xfer(struct rt_spi_device *device, + struct rt_spi_message *xfer) +{ + rt_err_t err; + rt_ssize_t res; + rt_bool_t use_dma = RT_FALSE; + struct rt_spi_bus *spi_bus = device->bus; + struct rockchip_spi *rk_spi = raw_to_rockchip_spi(spi_bus); + + if (!xfer->length) + { + rt_completion_done(&rk_spi->done); + + return rk_spi->xfer ? rk_spi->xfer->length : 1; + } + + if (!xfer->send_buf && !xfer->recv_buf) + { + if (xfer->cs_take) + { + rockchip_spi_set_cs(device, RT_TRUE); + } + + if (xfer->cs_release) + { + rockchip_spi_set_cs(device, RT_FALSE); + } + + LOG_E("No buffer for transfer"); + + return -RT_EINVAL; + } + + if (xfer->length > ROCKCHIP_SPI_MAX_TRANLEN) + { + LOG_E("Transfer is too long = %u", xfer->length); + + return -RT_EINVAL; + } + + rk_spi->n_bytes = rk_spi->bits_per_word <= 8 ? 1 : 2; + rk_spi->xfer = xfer; + + if ((err = rockchip_spi_config(rk_spi, device, xfer, use_dma, spi_bus->slave))) + { + return err; + } + + if (!spi_bus->slave) + { + rt_uint32_t timeout, div; + + if (xfer->cs_take) + { + rockchip_spi_set_cs(device, RT_TRUE); + } + + res = rockchip_spi_prepare_irq(rk_spi, xfer); + + div = RT_DIV_ROUND_UP(rk_spi->freq, device->config.max_hz); + div = (div + 1) & 0xfffe; + timeout = xfer->length * 8 * 1000 / (rk_spi->freq / div) + 100; + + err = rt_completion_wait(&rk_spi->done, rt_tick_from_millisecond(timeout)); + + if (err) + { + res = err; + } + + if (xfer->cs_release) + { + rockchip_spi_set_cs(device, RT_FALSE); + } + + if (xfer->send_buf && !err) + { + wait_for_tx_idle(rk_spi, spi_bus->slave); + } + } + else + { + res = rockchip_spi_prepare_irq(rk_spi, xfer); + + rt_completion_wait(&rk_spi->done, RT_WAITING_FOREVER); + + if (xfer->send_buf) + { + wait_for_tx_idle(rk_spi, spi_bus->slave); + } + } + + spi_enable_chip(rk_spi, RT_FALSE); + + if (res < 0) + { + rockchip_spi_writel(rk_spi, ROCKCHIP_SPI_IMR, 0); + rockchip_spi_writel(rk_spi, ROCKCHIP_SPI_ICR, 0xffffffff); + } + + return res; +} + +const static struct rt_spi_ops rockchip_spi_ops = +{ + .configure = rockchip_spi_configure, + .xfer = rockchip_spi_xfer, +}; + +static void rockchip_spi_isr(int irqno, void *param) +{ + struct rockchip_spi *rk_spi = param; + + /* When int_cs_inactive comes, spi slave abort */ + if (rk_spi->cs_inactive && + rockchip_spi_readl(rk_spi, ROCKCHIP_SPI_IMR) & INT_CS_INACTIVE) + { + rockchip_spi_slave_abort(rk_spi); + + rockchip_spi_writel(rk_spi, ROCKCHIP_SPI_IMR, 0); + rockchip_spi_writel(rk_spi, ROCKCHIP_SPI_ICR, 0xffffffff); + + return; + } + + if (rk_spi->tx_left) + { + rockchip_spi_pio_writer(rk_spi); + } + + rockchip_spi_pio_reader(rk_spi); + + if (!rk_spi->rx_left) + { + spi_enable_chip(rk_spi, RT_FALSE); + + rockchip_spi_writel(rk_spi, ROCKCHIP_SPI_IMR, 0); + rockchip_spi_writel(rk_spi, ROCKCHIP_SPI_ICR, 0xffffffff); + + rt_completion_done(&rk_spi->done); + } +} + +static void rockchip_spi_free(struct rockchip_spi *rk_spi) +{ + if (rk_spi->regs) + { + rt_iounmap(rk_spi->regs); + } + + if (!rt_is_err_or_null(rk_spi->apb_pclk)) + { + rt_clk_disable_unprepare(rk_spi->apb_pclk); + rt_clk_put(rk_spi->apb_pclk); + } + + if (!rt_is_err_or_null(rk_spi->spiclk)) + { + rt_clk_disable_unprepare(rk_spi->spiclk); + rt_clk_put(rk_spi->spiclk); + } + + if (!rt_is_err_or_null(rk_spi->sclk_in)) + { + rt_clk_disable_unprepare(rk_spi->sclk_in); + rt_clk_put(rk_spi->sclk_in); + } + + rt_free(rk_spi); +} + +static rt_err_t rockchip_spi_probe(struct rt_platform_device *pdev) +{ + rt_err_t err; + rt_uint32_t nsecs; + rt_bool_t slave_mode; + const char *bus_name; + struct rt_device *dev = &pdev->parent; + struct rockchip_spi *rk_spi = rt_calloc(1, sizeof(*rk_spi)); + const struct rockchip_spi_soc_data *soc_data; + + if (!rk_spi) + { + return -RT_ENOMEM; + } + + rk_spi->regs = rt_dm_dev_iomap(dev, 0); + + if (!rk_spi->regs) + { + err = -RT_EIO; + + goto _fail; + } + + rk_spi->irq = rt_dm_dev_get_irq(dev, 0); + + if (rk_spi->irq < 0) + { + err = rk_spi->irq; + + goto _fail; + } + + rk_spi->apb_pclk = rt_clk_get_by_name(dev, "apb_pclk"); + + if (rt_is_err(rk_spi->apb_pclk)) + { + err = rt_ptr_err(rk_spi->apb_pclk); + + goto _fail; + } + + if ((err = rt_clk_prepare_enable(rk_spi->apb_pclk))) + { + goto _fail; + } + + rk_spi->spiclk = rt_clk_get_by_name(dev, "spiclk"); + + if (rt_is_err(rk_spi->spiclk)) + { + err = rt_ptr_err(rk_spi->spiclk); + + goto _fail; + } + + if ((err = rt_clk_prepare_enable(rk_spi->spiclk))) + { + goto _fail; + } + + if (rt_dm_dev_prop_index_of_string(dev, "clock-names", "sclk_in") >= 0) + { + rk_spi->sclk_in = rt_clk_get_by_name(dev, "sclk_in"); + + if (rt_is_err(rk_spi->sclk_in)) + { + err = rt_ptr_err(rk_spi->sclk_in); + + goto _fail; + } + + if ((err = rt_clk_prepare_enable(rk_spi->sclk_in))) + { + goto _fail; + } + } + + spi_enable_chip(rk_spi, RT_FALSE); + + rk_spi->freq = rt_clk_get_rate(rk_spi->spiclk); + + if (rk_spi->freq < 0) + { + if ((err = rt_dm_dev_prop_read_u32(dev, "clock-frequency", &rk_spi->freq))) + { + LOG_E("Failed to get clock or clock-frequency"); + + goto _fail; + } + } + + if (!rt_dm_dev_prop_read_u32(dev, "rx-sample-delay-ns", &nsecs)) + { + /* rx sample delay is expressed in parent clock cycles (max 3) */ + rt_uint32_t rsd = RT_DIV_ROUND_CLOSEST(nsecs * (rk_spi->freq >> 8), + 1000000000 >> 8); + + if (!rsd) + { + LOG_W("%u Hz are too slow to express %u ns delay", rk_spi->freq, nsecs); + } + else if (rsd > CR0_RSD_MAX) + { + rsd = CR0_RSD_MAX; + LOG_W("%u Hz are too fast to express %u ns delay, clamping at %u ns", + rk_spi->freq, nsecs, CR0_RSD_MAX * 1000000000U / rk_spi->freq); + } + + rk_spi->rsd = rsd; + } + + rk_spi->bits_per_word = 8; + rk_spi->fifo_len = get_fifo_len(rk_spi); + + if (!rk_spi->fifo_len) + { + LOG_E("Failed to get fifo length"); + err = -RT_EINVAL; + + goto _fail; + } + + if ((soc_data = pdev->id->data)) + { + rk_spi->max_baud_div_in_cpha = soc_data->max_baud_div_in_cpha; + } + + slave_mode = rt_dm_dev_prop_read_bool(dev, "spi-slave"); + rk_spi->parent.slave = slave_mode; + + if (!slave_mode) + { + rt_uint32_t num_cs = 1; + + rt_dm_dev_prop_read_u32(dev, "num-cs", &num_cs); + + rk_spi->parent.num_chipselect = num_cs; + } + + /* DMA configure */ + { + void *spi_regs_phy = rt_kmem_v2p(rk_spi->regs); + + rk_spi->dma_addr_rx = spi_regs_phy + ROCKCHIP_SPI_TXDR; + rk_spi->dma_addr_tx = spi_regs_phy + ROCKCHIP_SPI_RXDR; + } + + switch (rockchip_spi_readl(rk_spi, ROCKCHIP_SPI_VERSION)) + { + case ROCKCHIP_SPI_VER2_TYPE2: + rk_spi->cs_high_supported = RT_TRUE; + rk_spi->parent.mode |= RT_SPI_CS_HIGH; + /* If support DMA and slave_mode, it will TRUE */ + rk_spi->cs_inactive = RT_FALSE; + break; + + default: + rk_spi->cs_inactive = RT_FALSE; + break; + } + + rk_spi->high_speed_state = rt_pin_ctrl_confs_lookup(dev, "high_speed"); + + rt_pin_ctrl_confs_apply_by_name(dev, RT_NULL); + + rt_completion_init(&rk_spi->done); + + dev->user_data = rk_spi; + + rk_spi->parent.parent.ofw_node = dev->ofw_node; + + rt_dm_dev_set_name_auto(&rk_spi->parent.parent, "spi"); + bus_name = rt_dm_dev_get_name(&rk_spi->parent.parent); + + rt_hw_interrupt_install(rk_spi->irq, rockchip_spi_isr, rk_spi, bus_name); + rt_hw_interrupt_umask(rk_spi->irq); + + if ((err = rt_spi_bus_register(&rk_spi->parent, bus_name, &rockchip_spi_ops))) + { + goto _fail; + } + + return RT_EOK; + +_fail: + rockchip_spi_free(rk_spi); + + return err; +} + +static rt_err_t rockchip_spi_remove(struct rt_platform_device *pdev) +{ + struct rockchip_spi *rk_spi = pdev->parent.user_data; + + spi_enable_chip(rk_spi, RT_FALSE); + + rt_hw_interrupt_mask(rk_spi->irq); + rt_pic_detach_irq(rk_spi->irq, rk_spi); + + rt_device_unregister(&rk_spi->parent.parent); + + rockchip_spi_free(rk_spi); + + return RT_EOK; +} + +static const struct rockchip_spi_soc_data px30_data = +{ + .max_baud_div_in_cpha = 4, +}; + +static const struct rt_ofw_node_id rockchip_spi_ofw_ids[] = +{ + { .compatible = "rockchip,px30-spi", .data = &px30_data }, + { .compatible = "rockchip,rk3036-spi", }, + { .compatible = "rockchip,rk3066-spi", }, + { .compatible = "rockchip,rk3188-spi", }, + { .compatible = "rockchip,rk3228-spi", }, + { .compatible = "rockchip,rk3288-spi", }, + { .compatible = "rockchip,rk3308-spi", }, + { .compatible = "rockchip,rk3328-spi", }, + { .compatible = "rockchip,rk3368-spi", }, + { .compatible = "rockchip,rk3399-spi", }, + { .compatible = "rockchip,rv1108-spi", }, + { .compatible = "rockchip,rv1126-spi", }, + { /* sentinel */ } +}; + +static struct rt_platform_driver rockchip_spi_driver = +{ + .name = "rockchip-spi", + .ids = rockchip_spi_ofw_ids, + + .probe = rockchip_spi_probe, + .remove = rockchip_spi_remove, +}; +RT_PLATFORM_DRIVER_EXPORT(rockchip_spi_driver); diff --git a/components/drivers/spi/spi_bus.c b/components/drivers/spi/spi_bus.c new file mode 100644 index 000000000000..af8b199c409d --- /dev/null +++ b/components/drivers/spi/spi_bus.c @@ -0,0 +1,201 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-12-06 GuEe-GUI first version + */ + +#include "spi_dm.h" + +#define DBG_TAG "spi.bus" +#define DBG_LVL DBG_INFO +#include + +extern rt_err_t rt_spidev_device_init(struct rt_spi_device *dev, const char *name); + +static struct rt_bus spi_bus; + +void spi_bus_scan_devices(struct rt_spi_bus *bus) +{ +#ifdef RT_USING_OFW + if (bus->parent.ofw_node) + { + struct rt_ofw_node *np = bus->parent.ofw_node, *spi_dev_np; + + rt_ofw_foreach_available_child_node(np, spi_dev_np) + { + rt_uint64_t reg_offset; + struct rt_spi_device *spi_dev; + + if (!rt_ofw_prop_read_bool(spi_dev_np, "compatible")) + { + continue; + } + + spi_dev = rt_calloc(1, sizeof(*spi_dev)); + + if (!spi_dev) + { + rt_ofw_node_put(spi_dev_np); + LOG_E("Not memory to create spi device: %s", + rt_ofw_node_full_name(spi_dev_np)); + + return; + } + + rt_ofw_get_address(spi_dev_np, 0, ®_offset, RT_NULL); + + spi_dev->parent.ofw_node = spi_dev_np; + spi_dev->parent.type = RT_Device_Class_Unknown; + spi_dev->name = rt_ofw_node_name(spi_dev_np); + spi_dev->bus = bus; + + rt_dm_dev_set_name(&spi_dev->parent, rt_ofw_node_full_name(spi_dev_np)); + + if (spi_device_ofw_parse(spi_dev)) + { + continue; + } + + rt_spi_device_register(spi_dev); + } + } +#endif /* RT_USING_OFW */ +} + +rt_err_t rt_spi_driver_register(struct rt_spi_driver *driver) +{ + RT_ASSERT(driver != RT_NULL); + + driver->parent.bus = &spi_bus; + + return rt_driver_register(&driver->parent); +} + +rt_err_t rt_spi_device_register(struct rt_spi_device *device) +{ + RT_ASSERT(device != RT_NULL); + + return rt_bus_add_device(&spi_bus, &device->parent); +} + +static rt_bool_t spi_match(rt_driver_t drv, rt_device_t dev) +{ + const struct rt_spi_device_id *id; + struct rt_spi_driver *driver = rt_container_of(drv, struct rt_spi_driver, parent); + struct rt_spi_device *device = rt_container_of(dev, struct rt_spi_device, parent); + + if ((id = driver->ids)) + { + for (; id->name[0]; ++id) + { + if (!rt_strcmp(id->name, device->name)) + { + device->id = id; + device->ofw_id = RT_NULL; + + return RT_TRUE; + } + } + } + +#ifdef RT_USING_OFW + device->ofw_id = rt_ofw_node_match(device->parent.ofw_node, driver->ofw_ids); + + if (device->ofw_id) + { + device->id = RT_NULL; + + return RT_TRUE; + } +#endif + + return RT_FALSE; +} + +static rt_err_t spi_probe(rt_device_t dev) +{ + rt_err_t err; + struct rt_spi_bus *bus; + struct rt_spi_driver *driver = rt_container_of(dev->drv, struct rt_spi_driver, parent); + struct rt_spi_device *device = rt_container_of(dev, struct rt_spi_device, parent); + + if (!device->bus) + { + return -RT_EINVAL; + } + + err = driver->probe(device); + + if (err) + { + return err; + } + + bus = device->bus; + + if (bus->pins) + { + device->cs_pin = bus->pins[device->chip_select]; + + rt_pin_mode(device->cs_pin, PIN_MODE_OUTPUT); + } + else + { + device->cs_pin = PIN_NONE; + } + + /* Driver not register SPI device to system */ + if (device->parent.type == RT_Device_Class_Unknown) + { + rt_spidev_device_init(device, rt_dm_dev_get_name(&device->parent)); + } + + return err; +} + +static rt_err_t spi_remove(rt_device_t dev) +{ + struct rt_spi_driver *driver = rt_container_of(dev->drv, struct rt_spi_driver, parent); + struct rt_spi_device *device = rt_container_of(dev, struct rt_spi_device, parent); + + if (driver && driver->remove) + { + driver->remove(device); + } + + return RT_EOK; +} + +static rt_err_t spi_shutdown(rt_device_t dev) +{ + struct rt_spi_driver *driver = rt_container_of(dev->drv, struct rt_spi_driver, parent); + struct rt_spi_device *device = rt_container_of(dev, struct rt_spi_device, parent); + + if (driver && driver->shutdown) + { + driver->shutdown(device); + } + + return RT_EOK; +} + +static struct rt_bus spi_bus = +{ + .name = "spi", + .match = spi_match, + .probe = spi_probe, + .remove = spi_remove, + .shutdown = spi_shutdown, +}; + +static int spi_bus_init(void) +{ + rt_bus_register(&spi_bus); + + return 0; +} +INIT_CORE_EXPORT(spi_bus_init); diff --git a/components/drivers/spi/spi_core.c b/components/drivers/spi/spi_core.c index 9cff697b321f..86ef45c41ed7 100644 --- a/components/drivers/spi/spi_core.c +++ b/components/drivers/spi/spi_core.c @@ -19,6 +19,10 @@ #define DBG_LVL DBG_INFO #include +#ifdef RT_USING_DM +#include "spi_dm.h" +#endif + extern rt_err_t rt_spi_bus_device_init(struct rt_spi_bus *bus, const char *name); extern rt_err_t rt_spidev_device_init(struct rt_spi_device *dev, const char *name); @@ -41,6 +45,46 @@ rt_err_t rt_spi_bus_register(struct rt_spi_bus *bus, /* set bus mode */ bus->mode = RT_SPI_BUS_MODE_SPI; +#ifdef RT_USING_DM + if (!bus->slave) + { + int pin_count = rt_pin_get_named_pin_count(&bus->parent, "cs"); + + if (pin_count > 0) + { + pin_count = rt_max_t(int, pin_count, bus->num_chipselect); + bus->pins = rt_malloc(sizeof(bus->pins[0]) * pin_count); + + if (!bus->pins) + { + rt_device_unregister(&bus->parent); + return -RT_ENOMEM; + } + + for (int i = 0; i < pin_count; ++i) + { + bus->pins[i] = rt_pin_get_named_pin(&bus->parent, "cs", i, + RT_NULL, RT_NULL); + } + } + else if (pin_count == 0) + { + bus->pins = RT_NULL; + } + else + { + result = pin_count; + + LOG_E("CS PIN find error = %s", rt_strerror(result)); + + rt_device_unregister(&bus->parent); + return result; + } + } + + spi_bus_scan_devices(bus); +#endif + return RT_EOK; } diff --git a/components/drivers/spi/spi_dev.c b/components/drivers/spi/spi_dev.c index 963b70c23bfd..221adc948f49 100644 --- a/components/drivers/spi/spi_dev.c +++ b/components/drivers/spi/spi_dev.c @@ -10,6 +10,10 @@ #include #include +#define DBG_TAG "spi.dev" +#define DBG_LVL DBG_INFO +#include + /* SPI bus device interface, compatible with RT-Thread 0.3.x/1.0.x */ static rt_ssize_t _spi_bus_device_read(rt_device_t dev, rt_off_t pos, @@ -155,3 +159,66 @@ rt_err_t rt_spidev_device_init(struct rt_spi_device *dev, const char *name) /* register to device manager */ return rt_device_register(device, name, RT_DEVICE_FLAG_RDWR); } + +#ifdef RT_USING_DM +static rt_err_t spidev_probe(struct rt_spi_device *spi_dev) +{ + const char *bus_name; + struct rt_device *dev = &spi_dev->parent; + + if (spi_dev->parent.ofw_node) + { + if (rt_dm_dev_prop_index_of_string(dev, "compatible", "spidev") >= 0) + { + LOG_E("spidev is not supported in OFW"); + + return -RT_EINVAL; + } + } + + bus_name = rt_dm_dev_get_name(&spi_dev->bus->parent); + rt_dm_dev_set_name(dev, "%s_%d", bus_name, spi_dev->chip_select); + + return RT_EOK; +} + +static const struct rt_spi_device_id spidev_ids[] = +{ + { .name = "dh2228fv" }, + { .name = "ltc2488" }, + { .name = "sx1301" }, + { .name = "bk4" }, + { .name = "dhcom-board" }, + { .name = "m53cpld" }, + { .name = "spi-petra" }, + { .name = "spi-authenta" }, + { .name = "em3581" }, + { .name = "si3210" }, + { /* sentinel */ }, +}; + +static const struct rt_ofw_node_id spidev_ofw_ids[] = +{ + { .compatible = "cisco,spi-petra" }, + { .compatible = "dh,dhcom-board" }, + { .compatible = "lineartechnology,ltc2488" }, + { .compatible = "lwn,bk4" }, + { .compatible = "menlo,m53cpld" }, + { .compatible = "micron,spi-authenta" }, + { .compatible = "rohm,dh2228fv" }, + { .compatible = "semtech,sx1301" }, + { .compatible = "silabs,em3581" }, + { .compatible = "silabs,si3210" }, + { .compatible = "rockchip,spidev" }, + { /* sentinel */ }, +}; + +static struct rt_spi_driver spidev_driver = +{ + .ids = spidev_ids, + .ofw_ids = spidev_ofw_ids, + + .probe = spidev_probe, +}; +RT_SPI_DRIVER_EXPORT(spidev_driver); +#endif /* RT_USING_DM */ diff --git a/components/drivers/spi/spi_dm.c b/components/drivers/spi/spi_dm.c new file mode 100644 index 000000000000..0a54082d0315 --- /dev/null +++ b/components/drivers/spi/spi_dm.c @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-12-06 GuEe-GUI first version + */ + +#include "spi_dm.h" + +#define DBG_TAG "spi.dm" +#define DBG_LVL DBG_INFO +#include + +#ifdef RT_USING_OFW +static void ofw_parse_delay(struct rt_ofw_node *np, struct rt_spi_delay *delay, + const char *prop) +{ + rt_uint32_t value; + + if (!rt_ofw_prop_read_u32(np, prop, &value)) + { + if (value > RT_UINT16_MAX) + { + delay->value = RT_DIV_ROUND_UP(value, 1000); + delay->unit = RT_SPI_DELAY_UNIT_USECS; + } + else + { + delay->value = value; + delay->unit = RT_SPI_DELAY_UNIT_NSECS; + } + } +} + +rt_err_t spi_device_ofw_parse(struct rt_spi_device *spi_dev) +{ + rt_err_t err; + rt_uint32_t value; + struct rt_spi_bus *spi_bus = spi_dev->bus; + struct rt_ofw_node *np = spi_dev->parent.ofw_node; + struct rt_spi_configuration *conf = &spi_dev->config; + + if (rt_ofw_prop_read_bool(np, "spi-cpha")) + { + conf->mode |= RT_SPI_CPHA; + } + if (rt_ofw_prop_read_bool(np, "spi-cpol")) + { + conf->mode |= RT_SPI_CPOL; + } + if (rt_ofw_prop_read_bool(np, "spi-3wire")) + { + conf->mode |= RT_SPI_3WIRE; + } + if (rt_ofw_prop_read_bool(np, "spi-lsb-first")) + { + conf->mode |= RT_SPI_LSB; + } + if (rt_ofw_prop_read_bool(np, "spi-cs-high")) + { + conf->mode |= RT_SPI_CS_HIGH; + } + + value = 1; + rt_ofw_prop_read_u32(np, "spi-tx-bus-width", &value); + conf->data_width_tx = value; + + value = 1; + rt_ofw_prop_read_u32(np, "spi-rx-bus-width", &value); + conf->data_width_rx = value; + + if (spi_bus->slave) + { + if (!rt_ofw_node_tag_equ(np, "slave")) + { + LOG_E("Invalid SPI device = %s", rt_ofw_node_full_name(np)); + + return -RT_EINVAL; + } + + return RT_EOK; + } + + if ((err = rt_ofw_prop_read_u32(np, "reg", &value))) + { + LOG_E("Find 'reg' failed"); + + return err; + } + spi_dev->chip_select = value; + + if (!rt_ofw_prop_read_u32(np, "spi-max-frequency", &value)) + { + conf->max_hz = value; + } + + ofw_parse_delay(np, &spi_dev->cs_setup, "spi-cs-setup-delay-ns"); + ofw_parse_delay(np, &spi_dev->cs_hold, "spi-cs-hold-delay-ns"); + ofw_parse_delay(np, &spi_dev->cs_inactive, "spi-cs-inactive-delay-ns"); + + return RT_EOK; +} +#endif /* RT_USING_OFW */ diff --git a/components/drivers/spi/spi_dm.h b/components/drivers/spi/spi_dm.h new file mode 100644 index 000000000000..61a007ec9e36 --- /dev/null +++ b/components/drivers/spi/spi_dm.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-11-26 GuEe-GUI first version + */ + +#ifndef __SPI_DM_H__ +#define __SPI_DM_H__ + +#include +#include +#include + +#ifdef RT_USING_OFW +rt_err_t spi_device_ofw_parse(struct rt_spi_device *spi_dev); +#else +rt_inline rt_err_t spi_device_ofw_parse(struct rt_spi_device *spi_dev) +{ + return RT_EOK; +} +#endif /* RT_USING_OFW */ + +void spi_bus_scan_devices(struct rt_spi_bus *bus); + +#endif /* __SPI_DM_H__ */ diff --git a/components/drivers/thermal/Kconfig b/components/drivers/thermal/Kconfig new file mode 100644 index 000000000000..01d522bf1b47 --- /dev/null +++ b/components/drivers/thermal/Kconfig @@ -0,0 +1,27 @@ +menuconfig RT_USING_THERMAL + bool "Using Thermal Management device drivers" + depends on RT_USING_DM + default n + +config RT_THERMAL_BCM2835 + bool "Thermal sensors on bcm2835 SoC" + depends on RT_USING_DM + depends on RT_USING_THERMAL + default n + +config RT_THERMAL_BCM2711 + bool "Thermal sensors on bcm2711 SoC" + depends on RT_USING_DM + depends on RT_USING_THERMAL + select RT_MFD_SYSCON + select RT_USING_RESET + default n + +config RT_THERMAL_ROCKCHIP_TSADC + bool "Rockchip TS-ADC driver" + depends on RT_USING_DM + depends on RT_USING_THERMAL + select RT_MFD_SYSCON + select RT_USING_RESET + default n + diff --git a/components/drivers/thermal/SConscript b/components/drivers/thermal/SConscript new file mode 100644 index 000000000000..077b1f10b433 --- /dev/null +++ b/components/drivers/thermal/SConscript @@ -0,0 +1,35 @@ +from building import * + +group = [] +objs = [] + +if not GetDepend(['RT_USING_THERMAL']): + Return('group') + +cwd = GetCurrentDir() +list = os.listdir(cwd) +CPPPATH = [cwd + '/../include'] + +src = ['thermal.c'] + +if GetDepend(['RT_THERMAL_BCM2711']): + src += ['thermal-bcm2711.c'] + +if GetDepend(['RT_THERMAL_BCM2835']): + src += ['thermal-bcm2835.c'] + +if GetDepend(['RT_THERMAL_ROCKCHIP_TSADC']): + src += ['thermal-rockchip_tsadc.c'] + +if GetDepend(['RT_THERMAL_COOL_PWM_FAN']): + src += ['thermal-cool-pwm-fan.c'] + +group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) + +for d in list: + path = os.path.join(cwd, d) + if os.path.isfile(os.path.join(path, 'SConscript')): + objs = objs + SConscript(os.path.join(d, 'SConscript')) +objs = objs + group + +Return('objs') diff --git a/components/drivers/thermal/thermal-bcm2711.c b/components/drivers/thermal/thermal-bcm2711.c new file mode 100644 index 000000000000..c345ffe947a1 --- /dev/null +++ b/components/drivers/thermal/thermal-bcm2711.c @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-3-08 GuEe-GUI the first version + */ + +#include +#include +#include + +#define DBG_TAG "thermal.bcm2711" +#define DBG_LVL DBG_INFO +#include + +#define AVS_RO_TEMP_STATUS 0x200 +#define AVS_RO_TEMP_STATUS_VALID_MSK (RT_BIT(16) | RT_BIT(10)) +#define AVS_RO_TEMP_STATUS_DATA_MSK RT_GENMASK(9, 0) + +struct bcm2711_thermal +{ + struct rt_thermal_zone_device parent; + + struct rt_syscon *regmap; +}; + +#define raw_to_bcm2711_thermal(raw) rt_container_of(raw, struct bcm2711_thermal, parent) + +static rt_err_t bcm2711_thermal_zone_get_temp(struct rt_thermal_zone_device *zdev, + int *out_temp) +{ + rt_err_t err; + rt_uint32_t val; + struct rt_thermal_zone_params *tz_params; + struct bcm2711_thermal *btz = raw_to_bcm2711_thermal(zdev); + + if ((err = rt_syscon_read(btz->regmap, AVS_RO_TEMP_STATUS, &val))) + { + return err; + } + + if (!(val & AVS_RO_TEMP_STATUS_VALID_MSK)) + { + return -RT_EIO; + } + + val &= AVS_RO_TEMP_STATUS_DATA_MSK; + tz_params = &btz->parent.params; + + /* Convert a HW code to a temperature reading (millidegree celsius) */ + *out_temp = tz_params->slope * val + tz_params->offset; + + return RT_EOK; +} + +const static struct rt_thermal_zone_ops bcm2711_thermal_zone_ops = +{ + .get_temp = bcm2711_thermal_zone_get_temp, +}; + +static rt_err_t bcm2711_thermal_probe(struct rt_platform_device *pdev) +{ + rt_err_t err; + struct rt_ofw_node *pnp; + struct rt_device *dev = &pdev->parent; + struct bcm2711_thermal *btz = rt_calloc(1, sizeof(*btz)); + + if (!btz) + { + return -RT_ENOMEM; + } + + pnp = rt_ofw_get_parent(dev->ofw_node); + + if (!pnp) + { + err = -RT_EINVAL; + goto _fail; + } + + btz->regmap = rt_syscon_find_by_ofw_node(pnp); + rt_ofw_node_put(pnp); + + if (!btz->regmap) + { + err = -RT_EINVAL; + goto _fail; + } + + btz->parent.parent.ofw_node = dev->ofw_node; + btz->parent.ops = &bcm2711_thermal_zone_ops; + + if ((err = rt_thermal_zone_device_register(&btz->parent))) + { + goto _fail; + } + + dev->user_data = btz; + + return RT_EOK; + +_fail: + rt_free(btz); + + return err; +} + +static rt_err_t bcm2711_thermal_remove(struct rt_platform_device *pdev) +{ + struct bcm2711_thermal *btz = pdev->parent.user_data; + + rt_thermal_zone_device_unregister(&btz->parent); + + rt_free(btz); + + return RT_EOK; +} + +static const struct rt_ofw_node_id bcm2711_thermal_ofw_ids[] = +{ + { .compatible = "brcm,bcm2711-thermal" }, + { /* sentinel */ } +}; + +static struct rt_platform_driver bcm2711_thermal_driver = +{ + .name = "bcm2711-thermal", + .ids = bcm2711_thermal_ofw_ids, + + .probe = bcm2711_thermal_probe, + .remove = bcm2711_thermal_remove, +}; + +static int bcm2711_thermal_drv_register(void) +{ + rt_platform_driver_register(&bcm2711_thermal_driver); + + return 0; +} +INIT_SUBSYS_LATER_EXPORT(bcm2711_thermal_drv_register); diff --git a/components/drivers/thermal/thermal-bcm2835.c b/components/drivers/thermal/thermal-bcm2835.c new file mode 100644 index 000000000000..8f8c35974b5d --- /dev/null +++ b/components/drivers/thermal/thermal-bcm2835.c @@ -0,0 +1,276 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-3-08 GuEe-GUI the first version + */ + +/* + * Note: as per Raspberry Foundation FAQ + * (https://www.raspberrypi.org/help/faqs/#performanceOperatingTemperature) + * the recommended temperature range for the SoC -40C to +85C + * so the trip limit is set to 80C. + * this applies to all the BCM283X SoC + */ + +#include +#include +#include + +#define DBG_TAG "thermal.bcm2835" +#define DBG_LVL DBG_INFO +#include + +#define BCM2835_TS_TSENSCTL 0x00 +#define BCM2835_TS_TSENSSTAT 0x04 + +#define BCM2835_TS_TSENSCTL_PRWDW RT_BIT(0) +#define BCM2835_TS_TSENSCTL_RSTB RT_BIT(1) + +/* + * bandgap reference voltage in 6 mV increments + * 000b = 1178 mV, 001b = 1184 mV, ... 111b = 1220 mV + */ +#define BCM2835_TS_TSENSCTL_CTRL_BITS 3 +#define BCM2835_TS_TSENSCTL_CTRL_SHIFT 2 +#define BCM2835_TS_TSENSCTL_CTRL_MASK \ + RT_GENMASK(BCM2835_TS_TSENSCTL_CTRL_BITS + \ + BCM2835_TS_TSENSCTL_CTRL_SHIFT - 1, \ + BCM2835_TS_TSENSCTL_CTRL_SHIFT) +#define BCM2835_TS_TSENSCTL_CTRL_DEFAULT 1 +#define BCM2835_TS_TSENSCTL_EN_INT RT_BIT(5) +#define BCM2835_TS_TSENSCTL_DIRECT RT_BIT(6) +#define BCM2835_TS_TSENSCTL_CLR_INT RT_BIT(7) +#define BCM2835_TS_TSENSCTL_THOLD_SHIFT 8 +#define BCM2835_TS_TSENSCTL_THOLD_BITS 10 +#define BCM2835_TS_TSENSCTL_THOLD_MASK \ + RT_GENMASK(BCM2835_TS_TSENSCTL_THOLD_BITS + \ + BCM2835_TS_TSENSCTL_THOLD_SHIFT - 1, \ + BCM2835_TS_TSENSCTL_THOLD_SHIFT) +/* + * time how long the block to be asserted in reset + * which based on a clock counter (TSENS clock assumed) + */ +#define BCM2835_TS_TSENSCTL_RSTDELAY_SHIFT 18 +#define BCM2835_TS_TSENSCTL_RSTDELAY_BITS 8 +#define BCM2835_TS_TSENSCTL_REGULEN RT_BIT(26) + +#define BCM2835_TS_TSENSSTAT_DATA_BITS 10 +#define BCM2835_TS_TSENSSTAT_DATA_SHIFT 0 +#define BCM2835_TS_TSENSSTAT_DATA_MASK \ + RT_GENMASK(BCM2835_TS_TSENSSTAT_DATA_BITS + \ + BCM2835_TS_TSENSSTAT_DATA_SHIFT - 1, \ + BCM2835_TS_TSENSSTAT_DATA_SHIFT) +#define BCM2835_TS_TSENSSTAT_VALID RT_BIT(10) +#define BCM2835_TS_TSENSSTAT_INTERRUPT RT_BIT(11) + +struct bcm2835_thermal +{ + struct rt_thermal_zone_device parent; + + void *regs; + struct rt_clk *clk; +}; + +#define raw_to_bcm2835_thermal(raw) rt_container_of(raw, struct bcm2835_thermal, parent) + +static int bcm2835_thermal_adc2temp(rt_uint32_t adc, int offset, int slope) +{ + return offset + slope * adc; +} + +static int bcm2835_thermal_temp2adc(int temp, int offset, int slope) +{ + temp -= offset; + temp /= slope; + + if (temp < 0) + { + temp = 0; + } + + if (temp >= RT_BIT(BCM2835_TS_TSENSSTAT_DATA_BITS)) + { + temp = RT_BIT(BCM2835_TS_TSENSSTAT_DATA_BITS) - 1; + } + + return temp; +} + +static rt_err_t bcm2835_thermal_zone_get_temp(struct rt_thermal_zone_device *zdev, + int *out_temp) +{ + rt_uint32_t val; + struct rt_thermal_zone_params *tz_params; + struct bcm2835_thermal *btz = raw_to_bcm2835_thermal(zdev); + + val = HWREG32(btz->regs + BCM2835_TS_TSENSSTAT); + + if (!(val & BCM2835_TS_TSENSSTAT_VALID)) + { + return -RT_EIO; + } + + val &= BCM2835_TS_TSENSSTAT_DATA_MASK; + tz_params = &btz->parent.params; + + *out_temp = bcm2835_thermal_adc2temp(val, tz_params->offset, tz_params->slope); + + return RT_EOK; +} + +const static struct rt_thermal_zone_ops bcm2835_thermal_zone_ops = +{ + .get_temp = bcm2835_thermal_zone_get_temp, +}; + +static rt_err_t bcm2835_thermal_probe(struct rt_platform_device *pdev) +{ + rt_err_t err; + rt_uint32_t val; + rt_ubase_t rate; + struct rt_device *dev = &pdev->parent; + struct bcm2835_thermal *btz = rt_calloc(1, sizeof(*btz)); + + if (!btz) + { + return -RT_ENOMEM; + } + + btz->regs = rt_dm_dev_iomap(dev, 0); + + if (!btz->regs) + { + err = -RT_EIO; + goto _fail; + } + + btz->clk = rt_clk_get_by_index(dev, 0); + + if (rt_is_err(btz->clk)) + { + err = rt_ptr_err(btz->clk); + goto _fail; + } + + if ((err = rt_clk_prepare_enable(btz->clk))) + { + goto _fail; + } + + rate = rt_clk_get_rate(btz->clk); + + if ((rate < 1920000) || (rate > 5000000)) + { + LOG_W("Clock running at %u Hz is outside of the recommended range: 1.92 to 5MHz", + btz->clk, rate); + } + + btz->parent.parent.ofw_node = dev->ofw_node; + btz->parent.ops = &bcm2835_thermal_zone_ops; + + if ((err = rt_thermal_zone_device_register(&btz->parent))) + { + goto _fail; + } + + /* + * right now the FW does set up the HW-block, so we are not + * touching the configuration registers. + * But if the HW is not enabled, then set it up + * using "sane" values used by the firmware right now. + */ + val = HWREG32(btz->regs + BCM2835_TS_TSENSCTL); + + if (!(val & BCM2835_TS_TSENSCTL_RSTB)) + { + int offset, slope; + struct rt_thermal_trip trip; + struct rt_thermal_zone_device *tz = &btz->parent; + struct rt_thermal_zone_params *tz_params = &tz->params; + + slope = tz_params->slope; + offset = tz_params->offset; + + if ((err = rt_thermal_zone_get_trip(tz, 0, &trip))) + { + rt_thermal_zone_device_unregister(tz); + + LOG_E("Read trip error = %s", rt_strerror(err)); + goto _fail; + } + + /* set bandgap reference voltage and enable voltage regulator */ + val = (BCM2835_TS_TSENSCTL_CTRL_DEFAULT << + BCM2835_TS_TSENSCTL_CTRL_SHIFT) | BCM2835_TS_TSENSCTL_REGULEN; + + /* use the recommended reset duration */ + val |= (0xFE << BCM2835_TS_TSENSCTL_RSTDELAY_SHIFT); + + /* trip_adc value from info */ + val |= bcm2835_thermal_temp2adc(trip.temperature, offset, slope) + << BCM2835_TS_TSENSCTL_THOLD_SHIFT; + + /* write the value back to the register as 2 steps */ + HWREG32(btz->regs + BCM2835_TS_TSENSCTL) = val; + val |= BCM2835_TS_TSENSCTL_RSTB; + HWREG32(btz->regs + BCM2835_TS_TSENSCTL) = val; + } + + dev->user_data = btz; + + return RT_EOK; + +_fail: + if (!rt_is_err_or_null(btz->clk)) + { + rt_clk_disable_unprepare(btz->clk); + rt_clk_put(btz->clk); + } + + rt_free(btz); + + return err; +} + +static rt_err_t bcm2835_thermal_remove(struct rt_platform_device *pdev) +{ + struct bcm2835_thermal *btz = pdev->parent.user_data; + + rt_thermal_zone_device_unregister(&btz->parent); + + rt_clk_disable_unprepare(btz->clk); + rt_clk_put(btz->clk); + + rt_free(btz); + + return RT_EOK; +} + +static const struct rt_ofw_node_id bcm2835_thermal_ofw_ids[] = +{ + { .compatible = "brcm,bcm2835-thermal" }, + { .compatible = "brcm,bcm2836-thermal" }, + { .compatible = "brcm,bcm2837-thermal" }, + { /* sentinel */ } +}; + +static struct rt_platform_driver bcm2835_thermal_driver = +{ + .name = "bcm2835-thermal", + .ids = bcm2835_thermal_ofw_ids, + + .probe = bcm2835_thermal_probe, + .remove = bcm2835_thermal_remove, +}; + +static int bcm2835_thermal_drv_register(void) +{ + rt_platform_driver_register(&bcm2835_thermal_driver); + + return 0; +} +INIT_SUBSYS_LATER_EXPORT(bcm2835_thermal_drv_register); diff --git a/components/drivers/thermal/thermal-rockchip_tsadc.c b/components/drivers/thermal/thermal-rockchip_tsadc.c new file mode 100644 index 000000000000..af9c7a52dced --- /dev/null +++ b/components/drivers/thermal/thermal-rockchip_tsadc.c @@ -0,0 +1,1647 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-3-08 GuEe-GUI the first version + */ + +#include +#include +#include + +#define DBG_TAG "thermal.rk-tsadc" +#define DBG_LVL DBG_INFO +#include + +/* + * TSADC Sensor Register description: + * + * TSADCV2_* are used for RK3288 SoCs, the other chips can reuse it. + * TSADCV3_* are used for newer SoCs than RK3288. (e.g: RK3228, RK3399) + */ +#define TSADCV2_USER_CON 0x00 +#define TSADCV2_AUTO_CON 0x04 +#define TSADCV2_INT_EN 0x08 +#define TSADCV2_INT_PD 0x0c +#define TSADCV3_AUTO_SRC_CON 0x0c +#define TSADCV3_HT_INT_EN 0x14 +#define TSADCV3_HSHUT_GPIO_INT_EN 0x18 +#define TSADCV3_HSHUT_CRU_INT_EN 0x1c +#define TSADCV3_INT_PD 0x24 +#define TSADCV3_HSHUT_PD 0x28 +#define TSADCV2_DATA(chn) (0x20 + (chn) * 0x04) +#define TSADCV2_COMP_INT(chn) (0x30 + (chn) * 0x04) +#define TSADCV2_COMP_SHUT(chn) (0x40 + (chn) * 0x04) +#define TSADCV3_DATA(chn) (0x2c + (chn) * 0x04) +#define TSADCV3_COMP_INT(chn) (0x6c + (chn) * 0x04) +#define TSADCV3_COMP_SHUT(chn) (0x10c + (chn) * 0x04) +#define TSADCV2_HIGHT_INT_DEBOUNCE 0x60 +#define TSADCV2_HIGHT_TSHUT_DEBOUNCE 0x64 +#define TSADCV3_HIGHT_INT_DEBOUNCE 0x14c +#define TSADCV3_HIGHT_TSHUT_DEBOUNCE 0x150 +#define TSADCV2_AUTO_PERIOD 0x68 +#define TSADCV2_AUTO_PERIOD_HT 0x6c +#define TSADCV3_AUTO_PERIOD 0x154 +#define TSADCV3_AUTO_PERIOD_HT 0x158 + +#define TSADCV2_AUTO_EN RT_BIT(0) +#define TSADCV2_AUTO_EN_MASK RT_BIT(16) +#define TSADCV2_AUTO_SRC_EN(chn) RT_BIT(4 + (chn)) +#define TSADCV3_AUTO_SRC_EN(chn) RT_BIT(chn) +#define TSADCV3_AUTO_SRC_EN_MASK(chn) RT_BIT(16 + chn) +#define TSADCV2_AUTO_TSHUT_POLARITY_HIGH RT_BIT(8) +#define TSADCV2_AUTO_TSHUT_POLARITY_MASK RT_BIT(24) + +#define TSADCV3_AUTO_Q_SEL_EN RT_BIT(1) + +#define TSADCV2_INT_SRC_EN(chn) RT_BIT(chn) +#define TSADCV2_INT_SRC_EN_MASK(chn) RT_BIT(16 + (chn)) +#define TSADCV2_SHUT_2GPIO_SRC_EN(chn) RT_BIT(4 + (chn)) +#define TSADCV2_SHUT_2CRU_SRC_EN(chn) RT_BIT(8 + (chn)) + +#define TSADCV2_INT_PD_CLEAR_MASK (~RT_BIT(8)) +#define TSADCV3_INT_PD_CLEAR_MASK (~RT_BIT(16)) +#define TSADCV4_INT_PD_CLEAR_MASK 0xffffffff + +#define TSADCV2_DATA_MASK 0xfff +#define TSADCV3_DATA_MASK 0x3ff +#define TSADCV4_DATA_MASK 0x1ff + +#define TSADCV2_HIGHT_INT_DEBOUNCE_COUNT 4 +#define TSADCV2_HIGHT_TSHUT_DEBOUNCE_COUNT 4 +#define TSADCV2_AUTO_PERIOD_TIME 250 /* 250ms */ +#define TSADCV2_AUTO_PERIOD_HT_TIME 50 /* 50ms */ +#define TSADCV3_AUTO_PERIOD_TIME 1875 /* 2.5ms */ +#define TSADCV3_AUTO_PERIOD_HT_TIME 1875 /* 2.5ms */ + +#define TSADCV5_AUTO_PERIOD_TIME 1622 /* 2.5ms */ +#define TSADCV5_AUTO_PERIOD_HT_TIME 1622 /* 2.5ms */ +#define TSADCV6_AUTO_PERIOD_TIME 5000 /* 2.5ms */ +#define TSADCV6_AUTO_PERIOD_HT_TIME 5000 /* 2.5ms */ + +#define TSADCV2_USER_INTER_PD_SOC 0x340 /* 13 clocks */ +#define TSADCV5_USER_INTER_PD_SOC 0xfc0 /* 97us, at least 90us */ + +#define GRF_SARADC_TESTBIT 0x0e644 +#define GRF_TSADC_TESTBIT_L 0x0e648 +#define GRF_TSADC_TESTBIT_H 0x0e64c + +#define PX30_GRF_SOC_CON2 0x0408 + +#define RK3568_GRF_TSADC_CON 0x0600 +#define RK3568_GRF_TSADC_ANA_REG0 (0x10001 << 0) +#define RK3568_GRF_TSADC_ANA_REG1 (0x10001 << 1) +#define RK3568_GRF_TSADC_ANA_REG2 (0x10001 << 2) +#define RK3568_GRF_TSADC_TSEN (0x10001 << 8) + +#define RK3588_GRF0_TSADC_CON 0x0100 + +#define RK3588_GRF0_TSADC_TRM (0xff0077 << 0) +#define RK3588_GRF0_TSADC_SHUT_2CRU (0x30003 << 10) +#define RK3588_GRF0_TSADC_SHUT_2GPIO (0x70007 << 12) + +#define GRF_SARADC_TESTBIT_ON (0x10001 << 2) +#define GRF_TSADC_TESTBIT_H_ON (0x10001 << 2) +#define GRF_TSADC_VCM_EN_L (0x10001 << 7) +#define GRF_TSADC_VCM_EN_H (0x10001 << 7) + +#define GRF_CON_TSADC_CH_INV (0x10001 << 1) + +#define TEMP_INVALID ((int)(RT_UINT32_MAX >> 1)) +#define TEMP_SCALE 1000 + +/* + * If the temperature over a period of time High, + * the resulting TSHUT gave CRU module,let it reset the entire chip, + * or via GPIO give PMIC. + */ +enum tshut_mode +{ + TSHUT_MODE_CRU = 0, + TSHUT_MODE_GPIO, +}; + +/* The system Temperature Sensors tshut(tshut) polarity */ +enum tshut_polarity +{ + TSHUT_LOW_ACTIVE = 0, + TSHUT_HIGH_ACTIVE, +}; + +/* The conversion table has the adc value and temperature. */ +enum adc_sort_mode +{ + ADC_DECREMENT = 0, /* the adc value is of diminishing.(e.g. rk3288_code_table) */ + ADC_INCREMENT, /* the adc value is incremental.(e.g. rk3368_code_table) */ +}; + +struct chip_tsadc_table +{ + const struct tsadc_table *id; + rt_size_t length; + + rt_uint32_t data_mask; + enum adc_sort_mode mode; +}; + +struct rockchip_tsadc_chip +{ + /* The sensor id of chip correspond to the ADC channel */ + int chn_offset; + int chn_num; + const char * const *chn_name; + + /* The hardware-controlled tshut property */ + int tshut_temp; + enum tshut_mode tshut_mode; + enum tshut_polarity tshut_polarity; + + /* Chip-wide methods */ + void (*initialize)(struct rt_syscon *grf, void *reg, enum tshut_polarity p); + void (*irq_ack)(void *reg); + void (*control)(void *reg, rt_bool_t on); + + /* Per-sensor methods */ + rt_err_t (*get_temp)(const struct chip_tsadc_table *table, int chn, void *reg, int *temp); + rt_err_t (*set_alarm_temp)(const struct chip_tsadc_table *table, int chn, void *reg, int temp); + rt_err_t (*set_tshut_temp)(const struct chip_tsadc_table *table, int chn, void *reg, int temp); + void (*set_tshut_mode)(int chn, void *reg, enum tshut_mode m); + + /* Per-table methods */ + struct chip_tsadc_table table; +}; + +struct tsadc_table +{ + rt_uint32_t code; /* the value of adc channel */ + int temp; /* the temperature */ +}; + +struct rockchip_tsadc; + +struct rockchip_tsadc_channel +{ + struct rt_thermal_zone_device parent; + + int id; + struct rockchip_tsadc *rk_tsadc; +}; +#define raw_to_rockchip_tsadc_channel(raw) rt_container_of(raw, struct rockchip_tsadc_channel, parent) + +struct rockchip_tsadc +{ + int irq; + void *regs; + struct rt_syscon *grf; + + struct rt_clk *clk; + struct rt_clk *pclk; + + struct rt_reset_control *rstc; + + int tshut_temp; + enum tshut_mode tshut_mode; + enum tshut_polarity tshut_polarity; + + const struct rockchip_tsadc_chip *chip; + struct rockchip_tsadc_channel channels[0]; +}; + +static rt_uint32_t rk_tsadcv2_temp_to_code(const struct chip_tsadc_table *table, + int temp) +{ + rt_ubase_t num; + rt_uint32_t denom, error = table->data_mask; + int high = (table->length - 1) - 1, low = 0, mid = (high + low) / 2; + + /* Return mask code data when the temp is over table range */ + if (temp < table->id[low].temp || temp > table->id[high].temp) + { + goto _exit; + } + + while (low <= high) + { + if (temp == table->id[mid].temp) + { + return table->id[mid].code; + } + else if (temp < table->id[mid].temp) + { + high = mid - 1; + } + else + { + low = mid + 1; + } + + mid = (low + high) / 2; + } + + /* + * The conversion code granularity provided by the table. Let's + * assume that the relationship between temperature and + * analog value between 2 table entries is linear and interpolate + * to produce less granular result. + */ + num = rt_abs(table->id[mid + 1].code - table->id[mid].code); + num *= temp - table->id[mid].temp; + denom = table->id[mid + 1].temp - table->id[mid].temp; + + switch (table->mode) + { + case ADC_DECREMENT: + return table->id[mid].code - (num / denom); + + case ADC_INCREMENT: + return table->id[mid].code + (num / denom); + + default: + LOG_E("Temp to Code: unknown table mode: %d", table->mode); + + return error; + } + +_exit: + LOG_E("Temp to Code: invalid temperature, temp = %d error = %d", temp, error); + + return error; +} + +static rt_err_t rk_tsadcv2_code_to_temp(const struct chip_tsadc_table *table, + rt_uint32_t code, int *temp) +{ + rt_ubase_t denom; + rt_uint32_t low = 1, high = table->length - 1, mid = (low + high) / 2, num; + + switch (table->mode) + { + case ADC_DECREMENT: + code &= table->data_mask; + + if (code <= table->id[high].code) + { + return -RT_ERROR; + } + + while (low <= high) + { + if (code >= table->id[mid].code && code < table->id[mid - 1].code) + { + break; + } + else if (code < table->id[mid].code) + { + low = mid + 1; + } + else + { + high = mid - 1; + } + + mid = (low + high) / 2; + } + break; + + case ADC_INCREMENT: + code &= table->data_mask; + + if (code < table->id[low].code) + { + return -RT_ERROR; + } + + while (low <= high) + { + if (code <= table->id[mid].code && code > table->id[mid - 1].code) + { + break; + } + else if (code > table->id[mid].code) + { + low = mid + 1; + } + else + { + high = mid - 1; + } + + mid = (low + high) / 2; + } + break; + + default: + LOG_E("Code to Temp: unknown table mode: %d", table->mode); + + return -RT_EINVAL; + } + + /* + * The 5C granularity provided by the table is too much. Let's + * assume that the relationship between sensor readings and + * temperature between 2 table entries is linear and interpolate + * to produce less granular result. + */ + num = table->id[mid].temp - table->id[mid - 1].temp; + num *= rt_abs(table->id[mid - 1].code - code); + denom = rt_abs(table->id[mid - 1].code - table->id[mid].code); + *temp = table->id[mid - 1].temp + (num / denom); + + return 0; +} + +/* + * Initialize TASDC Controller. + * + * (1) Set TSADC_V2_AUTO_PERIOD: + * Configure the interleave between every two accessing of + * TSADC in normal operation. + * + * (2) Set TSADCV2_AUTO_PERIOD_HT: + * Configure the interleave between every two accessing of + * TSADC after the temperature is higher than COM_SHUT or COM_INT. + * + * (3) Set TSADCV2_HIGH_INT_DEBOUNCE and TSADC_HIGHT_TSHUT_DEBOUNCE: + * If the temperature is higher than COMP_INT or COMP_SHUT for + * "debounce" times, TSADC controller will generate interrupt or TSHUT. + */ +static void rk_tsadcv2_initialize(struct rt_syscon *grf, void *regs, + enum tshut_polarity tshut_polarity) +{ + if (tshut_polarity == TSHUT_HIGH_ACTIVE) + { + HWREG32(regs + TSADCV2_AUTO_CON) = 0U | TSADCV2_AUTO_TSHUT_POLARITY_HIGH; + } + else + { + HWREG32(regs + TSADCV2_AUTO_CON) = 0U & ~TSADCV2_AUTO_TSHUT_POLARITY_HIGH; + } + + HWREG32(regs + TSADCV2_AUTO_PERIOD) = TSADCV2_AUTO_PERIOD_TIME; + HWREG32(regs + TSADCV2_HIGHT_INT_DEBOUNCE) = TSADCV2_HIGHT_INT_DEBOUNCE_COUNT; + HWREG32(regs + TSADCV2_AUTO_PERIOD_HT) = TSADCV2_AUTO_PERIOD_HT_TIME; + HWREG32(regs + TSADCV2_HIGHT_TSHUT_DEBOUNCE) = TSADCV2_HIGHT_TSHUT_DEBOUNCE_COUNT; +} + +/* + * Initialize TASDC Controller. + * (1) The tsadc control power sequence. + * + * (2) Set TSADC_V2_AUTO_PERIOD: + * Configure the interleave between every two accessing of + * TSADC in normal operation. + * + * (2) Set TSADCV2_AUTO_PERIOD_HT: + * Configure the interleave between every two accessing of + * TSADC after the temperature is higher than COM_SHUT or COM_INT. + * + * (3) Set TSADCV2_HIGH_INT_DEBOUNCE and TSADC_HIGHT_TSHUT_DEBOUNCE: + * If the temperature is higher than COMP_INT or COMP_SHUT for + * "debounce" times, TSADC controller will generate interrupt or TSHUT. + */ +static void rk_tsadcv3_initialize(struct rt_syscon *grf, void *regs, + enum tshut_polarity tshut_polarity) +{ + /* The tsadc control power sequence */ + if (!grf) + { + /* Set interleave value to workround ic time sync issue */ + HWREG32(regs + TSADCV2_USER_CON) = TSADCV2_USER_INTER_PD_SOC; + HWREG32(regs + TSADCV2_AUTO_PERIOD) = TSADCV2_AUTO_PERIOD_TIME; + HWREG32(regs + TSADCV2_HIGHT_INT_DEBOUNCE) = TSADCV2_HIGHT_INT_DEBOUNCE_COUNT; + HWREG32(regs + TSADCV2_AUTO_PERIOD_HT) = TSADCV2_AUTO_PERIOD_HT_TIME; + HWREG32(regs + TSADCV2_HIGHT_TSHUT_DEBOUNCE) = TSADCV2_HIGHT_TSHUT_DEBOUNCE_COUNT; + + } + else + { + /* Enable the voltage common mode feature */ + rt_syscon_write(grf, GRF_TSADC_TESTBIT_L, GRF_TSADC_VCM_EN_L); + rt_syscon_write(grf, GRF_TSADC_TESTBIT_H, GRF_TSADC_VCM_EN_H); + rt_hw_us_delay(15); + + rt_syscon_write(grf, GRF_SARADC_TESTBIT, GRF_SARADC_TESTBIT_ON); + rt_syscon_write(grf, GRF_TSADC_TESTBIT_H, GRF_TSADC_TESTBIT_H_ON); + rt_hw_us_delay(90); + + HWREG32(regs + TSADCV2_AUTO_PERIOD) = TSADCV3_AUTO_PERIOD_TIME; + HWREG32(regs + TSADCV2_HIGHT_INT_DEBOUNCE) = TSADCV2_HIGHT_INT_DEBOUNCE_COUNT; + HWREG32(regs + TSADCV2_AUTO_PERIOD_HT) = TSADCV3_AUTO_PERIOD_HT_TIME; + HWREG32(regs + TSADCV2_HIGHT_TSHUT_DEBOUNCE) = TSADCV2_HIGHT_TSHUT_DEBOUNCE_COUNT; + } + + if (tshut_polarity == TSHUT_HIGH_ACTIVE) + { + HWREG32(regs + TSADCV2_AUTO_CON) = 0U | TSADCV2_AUTO_TSHUT_POLARITY_HIGH; + } + else + { + HWREG32(regs + TSADCV2_AUTO_CON) = 0U & ~TSADCV2_AUTO_TSHUT_POLARITY_HIGH; + } +} + +static void rk_tsadcv4_initialize(struct rt_syscon *grf, void *regs, + enum tshut_polarity tshut_polarity) +{ + rk_tsadcv2_initialize(grf, regs, tshut_polarity); + + rt_syscon_write(grf, PX30_GRF_SOC_CON2, GRF_CON_TSADC_CH_INV); +} + +static void rk_tsadcv7_initialize(struct rt_syscon *grf, void *regs, + enum tshut_polarity tshut_polarity) +{ + HWREG32(regs + TSADCV2_USER_CON) = TSADCV5_USER_INTER_PD_SOC; + HWREG32(regs + TSADCV2_AUTO_PERIOD) = TSADCV5_AUTO_PERIOD_TIME; + HWREG32(regs + TSADCV2_HIGHT_INT_DEBOUNCE) = TSADCV2_HIGHT_INT_DEBOUNCE_COUNT; + HWREG32(regs + TSADCV2_AUTO_PERIOD_HT) = TSADCV5_AUTO_PERIOD_HT_TIME; + HWREG32(regs + TSADCV2_HIGHT_TSHUT_DEBOUNCE) = TSADCV2_HIGHT_TSHUT_DEBOUNCE_COUNT; + + if (tshut_polarity == TSHUT_HIGH_ACTIVE) + { + HWREG32(regs + TSADCV2_AUTO_CON) = 0U | TSADCV2_AUTO_TSHUT_POLARITY_HIGH; + } + else + { + HWREG32(regs + TSADCV2_AUTO_CON) = 0U & ~TSADCV2_AUTO_TSHUT_POLARITY_HIGH; + } + + /* The general register file will is optional and might not be available. */ + if (grf) + { + rt_syscon_write(grf, RK3568_GRF_TSADC_CON, RK3568_GRF_TSADC_TSEN); + rt_hw_us_delay(15); + + rt_syscon_write(grf, RK3568_GRF_TSADC_CON, RK3568_GRF_TSADC_ANA_REG0); + rt_syscon_write(grf, RK3568_GRF_TSADC_CON, RK3568_GRF_TSADC_ANA_REG1); + rt_syscon_write(grf, RK3568_GRF_TSADC_CON, RK3568_GRF_TSADC_ANA_REG2); + rt_hw_us_delay(100); + } +} + +static void rk_tsadcv8_initialize(struct rt_syscon *grf, void *regs, + enum tshut_polarity tshut_polarity) +{ + HWREG32(regs + TSADCV3_AUTO_PERIOD) = TSADCV6_AUTO_PERIOD_TIME; + HWREG32(regs + TSADCV3_AUTO_PERIOD_HT) = TSADCV6_AUTO_PERIOD_HT_TIME; + HWREG32(regs + TSADCV3_HIGHT_INT_DEBOUNCE) = TSADCV2_HIGHT_INT_DEBOUNCE_COUNT; + HWREG32(regs + TSADCV3_HIGHT_TSHUT_DEBOUNCE) = TSADCV2_HIGHT_TSHUT_DEBOUNCE_COUNT; + + if (tshut_polarity == TSHUT_HIGH_ACTIVE) + { + HWREG32(regs + TSADCV2_AUTO_CON) = TSADCV2_AUTO_TSHUT_POLARITY_HIGH | + TSADCV2_AUTO_TSHUT_POLARITY_MASK; + } + else + { + HWREG32(regs + TSADCV2_AUTO_CON) = TSADCV2_AUTO_TSHUT_POLARITY_MASK; + } +} + +static void rk_tsadcv2_irq_ack(void *regs) +{ + rt_uint32_t val; + + val = HWREG32(regs + TSADCV2_INT_PD); + HWREG32(regs + TSADCV2_INT_PD) = val & TSADCV2_INT_PD_CLEAR_MASK; +} + +static void rk_tsadcv3_irq_ack(void *regs) +{ + rt_uint32_t val; + + val = HWREG32(regs + TSADCV2_INT_PD); + HWREG32(regs + TSADCV2_INT_PD) = val & TSADCV3_INT_PD_CLEAR_MASK; +} + +static void rk_tsadcv4_irq_ack(void *regs) +{ + rt_uint32_t val; + + val = HWREG32(regs + TSADCV3_INT_PD); + HWREG32(regs + TSADCV3_INT_PD) = val & TSADCV4_INT_PD_CLEAR_MASK; + + val = HWREG32(regs + TSADCV3_HSHUT_PD); + HWREG32(regs + TSADCV3_HSHUT_PD) = val & TSADCV3_INT_PD_CLEAR_MASK; +} + +static void rk_tsadcv2_control(void *regs, rt_bool_t enable) +{ + rt_uint32_t val = HWREG32(regs + TSADCV2_AUTO_CON); + + if (enable) + { + val |= TSADCV2_AUTO_EN; + } + else + { + val &= ~TSADCV2_AUTO_EN; + } + + HWREG32(regs + TSADCV2_AUTO_CON) = val; +} + +/* + * The tsadc controller is enabled or disabled. + * NOTE: TSADC controller works at auto mode, and some SoCs need set the + * tsadc_q_sel bit on TSADCV2_AUTO_CON[1]. The (1024 - tsadc_q) as output + * adc value if setting this bit to enable. + */ +static void rk_tsadcv3_control(void *regs, rt_bool_t enable) +{ + rt_uint32_t val = HWREG32(regs + TSADCV2_AUTO_CON); + + if (enable) + { + val |= TSADCV2_AUTO_EN | TSADCV3_AUTO_Q_SEL_EN; + } + else + { + val &= ~TSADCV2_AUTO_EN; + } + + HWREG32(regs + TSADCV2_AUTO_CON) = val; +} + +static void rk_tsadcv4_control(void *regs, rt_bool_t enable) +{ + rt_uint32_t val; + + if (enable) + { + val = TSADCV2_AUTO_EN | TSADCV2_AUTO_EN_MASK; + } + else + { + val = TSADCV2_AUTO_EN_MASK; + } + + HWREG32(regs + TSADCV2_AUTO_CON) = val; +} + +static rt_err_t rk_tsadcv2_get_temp(const struct chip_tsadc_table *table, + int chn, void *regs, int *temp) +{ + rt_uint32_t val = HWREG32(regs + TSADCV2_DATA(chn)); + + return rk_tsadcv2_code_to_temp(table, val, temp); +} + +static rt_err_t rk_tsadcv4_get_temp(const struct chip_tsadc_table *table, + int chn, void *regs, int *temp) +{ + rt_uint32_t val = HWREG32(regs + TSADCV3_DATA(chn)); + + return rk_tsadcv2_code_to_temp(table, val, temp); +} + +static rt_err_t rk_tsadcv2_alarm_temp(const struct chip_tsadc_table *table, + int chn, void *regs, int temp) +{ + rt_uint32_t alarm_value; + rt_uint32_t int_en, int_clr; + + /* + * In some cases, some sensors didn't need the trip points, the + * set_trips will pass {-INT_MAX, INT_MAX} to trigger tsadc alarm + * in the end, ignore this case and disable the high temperature + * interrupt. + */ + if (temp == TEMP_INVALID) + { + int_clr = HWREG32(regs + TSADCV2_INT_EN); + int_clr &= ~TSADCV2_INT_SRC_EN(chn); + HWREG32(regs + TSADCV2_INT_EN) = int_clr; + + return RT_EOK; + } + + alarm_value = rk_tsadcv2_temp_to_code(table, temp); + + if (alarm_value == table->data_mask) + { + return -RT_EINVAL; + } + + HWREG32(regs + TSADCV2_COMP_INT(chn)) = alarm_value & table->data_mask; + int_en = HWREG32(regs + TSADCV2_INT_EN); + int_en |= TSADCV2_INT_SRC_EN(chn); + HWREG32(regs + TSADCV2_INT_EN) = int_en; + + return RT_EOK; +} + +static rt_err_t rk_tsadcv3_alarm_temp(const struct chip_tsadc_table *table, + int chn, void *regs, int temp) +{ + rt_uint32_t alarm_value; + + /* + * In some cases, some sensors didn't need the trip points, the + * set_trips will pass {-INT_MAX, INT_MAX} to trigger tsadc alarm + * in the end, ignore this case and disable the high temperature + * interrupt. + */ + if (temp == TEMP_INVALID) + { + HWREG32(regs + TSADCV3_HT_INT_EN) = TSADCV2_INT_SRC_EN_MASK(chn); + + return RT_EOK; + } + + alarm_value = rk_tsadcv2_temp_to_code(table, temp); + + if (alarm_value == table->data_mask) + { + return -RT_EINVAL; + } + + HWREG32(regs + TSADCV3_COMP_INT(chn)) = alarm_value & table->data_mask; + HWREG32(regs + TSADCV3_HT_INT_EN) = + TSADCV2_INT_SRC_EN(chn) | TSADCV2_INT_SRC_EN_MASK(chn); + + return RT_EOK; +} + +static rt_err_t rk_tsadcv2_tshut_temp(const struct chip_tsadc_table *table, + int chn, void *regs, int temp) +{ + rt_uint32_t val, tshut_value = rk_tsadcv2_temp_to_code(table, temp); + + if (tshut_value == table->data_mask) + { + return -RT_EINVAL; + } + + HWREG32(regs + TSADCV2_COMP_SHUT(chn)) = tshut_value; + val = HWREG32(regs + TSADCV2_AUTO_CON); + HWREG32(regs + TSADCV2_AUTO_CON) = val | TSADCV2_AUTO_SRC_EN(chn); + + return RT_EOK; +} + +static rt_err_t rk_tsadcv3_tshut_temp(const struct chip_tsadc_table *table, + int chn, void *regs, int temp) +{ + rt_uint32_t tshut_value = rk_tsadcv2_temp_to_code(table, temp); + + if (tshut_value == table->data_mask) + { + return -RT_EINVAL; + } + + HWREG32(regs + TSADCV3_COMP_SHUT(chn)) = tshut_value; + HWREG32(regs + TSADCV3_AUTO_SRC_CON) = + TSADCV3_AUTO_SRC_EN(chn) | TSADCV3_AUTO_SRC_EN_MASK(chn); + + return RT_EOK; +} + +static void rk_tsadcv2_tshut_mode(int chn, void *regs, enum tshut_mode mode) +{ + rt_uint32_t val = HWREG32(regs + TSADCV2_INT_EN); + + if (mode == TSHUT_MODE_GPIO) + { + val &= ~TSADCV2_SHUT_2CRU_SRC_EN(chn); + val |= TSADCV2_SHUT_2GPIO_SRC_EN(chn); + } + else + { + val &= ~TSADCV2_SHUT_2GPIO_SRC_EN(chn); + val |= TSADCV2_SHUT_2CRU_SRC_EN(chn); + } + + HWREG32(regs + TSADCV2_INT_EN) = val; +} + +static void rk_tsadcv3_tshut_mode(int chn, void *regs, enum tshut_mode mode) +{ + rt_uint32_t val_gpio, val_cru; + + if (mode == TSHUT_MODE_GPIO) + { + val_gpio = TSADCV2_INT_SRC_EN(chn) | TSADCV2_INT_SRC_EN_MASK(chn); + val_cru = TSADCV2_INT_SRC_EN_MASK(chn); + } + else + { + val_cru = TSADCV2_INT_SRC_EN(chn) | TSADCV2_INT_SRC_EN_MASK(chn); + val_gpio = TSADCV2_INT_SRC_EN_MASK(chn); + } + + HWREG32(regs + TSADCV3_HSHUT_GPIO_INT_EN) = val_gpio; + HWREG32(regs + TSADCV3_HSHUT_CRU_INT_EN) = val_cru; +} + +static const struct tsadc_table rk3328_code_table[] = +{ + { 0, -40000 }, + { 296, -40000 }, + { 304, -35000 }, + { 313, -30000 }, + { 331, -20000 }, + { 340, -15000 }, + { 349, -10000 }, + { 359, -5000 }, + { 368, 0 }, + { 378, 5000 }, + { 388, 10000 }, + { 398, 15000 }, + { 408, 20000 }, + { 418, 25000 }, + { 429, 30000 }, + { 440, 35000 }, + { 451, 40000 }, + { 462, 45000 }, + { 473, 50000 }, + { 485, 55000 }, + { 496, 60000 }, + { 508, 65000 }, + { 521, 70000 }, + { 533, 75000 }, + { 546, 80000 }, + { 559, 85000 }, + { 572, 90000 }, + { 586, 95000 }, + { 600, 100000 }, + { 614, 105000 }, + { 629, 110000 }, + { 644, 115000 }, + { 659, 120000 }, + { 675, 125000 }, + { TSADCV2_DATA_MASK, 125000 }, +}; + +static const char * const chn_name_common[] = +{ + "CPU", "GPU", +}; + +static const struct rockchip_tsadc_chip px30_tsadc_data = +{ + /* cpu, gpu */ + .chn_offset = 0, + .chn_num = 2, /* 2 channels for tsadc */ + .chn_name = chn_name_common, + + .tshut_mode = TSHUT_MODE_CRU, /* default TSHUT via CRU */ + .tshut_temp = 95000, + + .initialize = rk_tsadcv4_initialize, + .irq_ack = rk_tsadcv3_irq_ack, + .control = rk_tsadcv3_control, + .get_temp = rk_tsadcv2_get_temp, + .set_alarm_temp = rk_tsadcv2_alarm_temp, + .set_tshut_temp = rk_tsadcv2_tshut_temp, + .set_tshut_mode = rk_tsadcv2_tshut_mode, + + .table = + { + .id = rk3328_code_table, + .length = RT_ARRAY_SIZE(rk3328_code_table), + .data_mask = TSADCV2_DATA_MASK, + .mode = ADC_INCREMENT, + }, +}; + +static const struct tsadc_table rv1108_table[] = +{ + { 0, -40000 }, + { 374, -40000 }, + { 382, -35000 }, + { 389, -30000 }, + { 397, -25000 }, + { 405, -20000 }, + { 413, -15000 }, + { 421, -10000 }, + { 429, -5000 }, + { 436, 0 }, + { 444, 5000 }, + { 452, 10000 }, + { 460, 15000 }, + { 468, 20000 }, + { 476, 25000 }, + { 483, 30000 }, + { 491, 35000 }, + { 499, 40000 }, + { 507, 45000 }, + { 515, 50000 }, + { 523, 55000 }, + { 531, 60000 }, + { 539, 65000 }, + { 547, 70000 }, + { 555, 75000 }, + { 562, 80000 }, + { 570, 85000 }, + { 578, 90000 }, + { 586, 95000 }, + { 594, 100000 }, + { 602, 105000 }, + { 610, 110000 }, + { 618, 115000 }, + { 626, 120000 }, + { 634, 125000 }, + { TSADCV2_DATA_MASK, 125000 }, +}; + +static const struct rockchip_tsadc_chip rv1108_tsadc_data = +{ + /* cpu */ + .chn_offset = 0, + .chn_num = 1, /* one channel for tsadc */ + .chn_name = chn_name_common, + + .tshut_mode = TSHUT_MODE_GPIO, /* default TSHUT via GPIO give PMIC */ + .tshut_polarity = TSHUT_LOW_ACTIVE, /* default TSHUT LOW ACTIVE */ + .tshut_temp = 95000, + + .initialize = rk_tsadcv2_initialize, + .irq_ack = rk_tsadcv3_irq_ack, + .control = rk_tsadcv3_control, + .get_temp = rk_tsadcv2_get_temp, + .set_alarm_temp = rk_tsadcv2_alarm_temp, + .set_tshut_temp = rk_tsadcv2_tshut_temp, + .set_tshut_mode = rk_tsadcv2_tshut_mode, + + .table = + { + .id = rv1108_table, + .length = RT_ARRAY_SIZE(rv1108_table), + .data_mask = TSADCV2_DATA_MASK, + .mode = ADC_INCREMENT, + }, +}; + +static const struct tsadc_table rk3228_code_table[] = +{ + { 0, -40000 }, + { 588, -40000 }, + { 593, -35000 }, + { 598, -30000 }, + { 603, -25000 }, + { 608, -20000 }, + { 613, -15000 }, + { 618, -10000 }, + { 623, -5000 }, + { 629, 0 }, + { 634, 5000 }, + { 639, 10000 }, + { 644, 15000 }, + { 649, 20000 }, + { 654, 25000 }, + { 660, 30000 }, + { 665, 35000 }, + { 670, 40000 }, + { 675, 45000 }, + { 681, 50000 }, + { 686, 55000 }, + { 691, 60000 }, + { 696, 65000 }, + { 702, 70000 }, + { 707, 75000 }, + { 712, 80000 }, + { 717, 85000 }, + { 723, 90000 }, + { 728, 95000 }, + { 733, 100000 }, + { 738, 105000 }, + { 744, 110000 }, + { 749, 115000 }, + { 754, 120000 }, + { 760, 125000 }, + { TSADCV2_DATA_MASK, 125000 }, +}; + +static const struct rockchip_tsadc_chip rk3228_tsadc_data = +{ + /* cpu */ + .chn_offset = 0, + .chn_num = 1, /* one channel for tsadc */ + .chn_name = chn_name_common, + + .tshut_mode = TSHUT_MODE_GPIO, /* default TSHUT via GPIO give PMIC */ + .tshut_polarity = TSHUT_LOW_ACTIVE, /* default TSHUT LOW ACTIVE */ + .tshut_temp = 95000, + + .initialize = rk_tsadcv2_initialize, + .irq_ack = rk_tsadcv3_irq_ack, + .control = rk_tsadcv3_control, + .get_temp = rk_tsadcv2_get_temp, + .set_alarm_temp = rk_tsadcv2_alarm_temp, + .set_tshut_temp = rk_tsadcv2_tshut_temp, + .set_tshut_mode = rk_tsadcv2_tshut_mode, + + .table = + { + .id = rk3228_code_table, + .length = RT_ARRAY_SIZE(rk3228_code_table), + .data_mask = TSADCV3_DATA_MASK, + .mode = ADC_INCREMENT, + }, +}; + +static const struct tsadc_table rk3288_code_table[] = +{ + { TSADCV2_DATA_MASK, -40000 }, + { 3800, -40000 }, + { 3792, -35000 }, + { 3783, -30000 }, + { 3774, -25000 }, + { 3765, -20000 }, + { 3756, -15000 }, + { 3747, -10000 }, + { 3737, -5000 }, + { 3728, 0 }, + { 3718, 5000 }, + { 3708, 10000 }, + { 3698, 15000 }, + { 3688, 20000 }, + { 3678, 25000 }, + { 3667, 30000 }, + { 3656, 35000 }, + { 3645, 40000 }, + { 3634, 45000 }, + { 3623, 50000 }, + { 3611, 55000 }, + { 3600, 60000 }, + { 3588, 65000 }, + { 3575, 70000 }, + { 3563, 75000 }, + { 3550, 80000 }, + { 3537, 85000 }, + { 3524, 90000 }, + { 3510, 95000 }, + { 3496, 100000 }, + { 3482, 105000 }, + { 3467, 110000 }, + { 3452, 115000 }, + { 3437, 120000 }, + { 3421, 125000 }, + { 0, 125000 }, +}; + +static const struct rockchip_tsadc_chip rk3288_tsadc_data = +{ + /* cpu, gpu */ + .chn_offset = 1, + .chn_num = 2, /* two channels for tsadc */ + .chn_name = chn_name_common, + + .tshut_mode = TSHUT_MODE_GPIO, /* default TSHUT via GPIO give PMIC */ + .tshut_polarity = TSHUT_LOW_ACTIVE, /* default TSHUT LOW ACTIVE */ + .tshut_temp = 95000, + + .initialize = rk_tsadcv2_initialize, + .irq_ack = rk_tsadcv2_irq_ack, + .control = rk_tsadcv2_control, + .get_temp = rk_tsadcv2_get_temp, + .set_alarm_temp = rk_tsadcv2_alarm_temp, + .set_tshut_temp = rk_tsadcv2_tshut_temp, + .set_tshut_mode = rk_tsadcv2_tshut_mode, + + .table = + { + .id = rk3288_code_table, + .length = RT_ARRAY_SIZE(rk3288_code_table), + .data_mask = TSADCV2_DATA_MASK, + .mode = ADC_DECREMENT, + }, +}; + +static const struct rockchip_tsadc_chip rk3308_tsadc_data = +{ + /* cpu, gpu */ + .chn_offset = 0, + .chn_num = 2, /* two channels for tsadc */ + .chn_name = chn_name_common, + + .tshut_mode = TSHUT_MODE_CRU, /* default TSHUT via CRU */ + .tshut_temp = 95000, + + .initialize = rk_tsadcv2_initialize, + .irq_ack = rk_tsadcv3_irq_ack, + .control = rk_tsadcv3_control, + .get_temp = rk_tsadcv2_get_temp, + .set_alarm_temp = rk_tsadcv2_alarm_temp, + .set_tshut_temp = rk_tsadcv2_tshut_temp, + .set_tshut_mode = rk_tsadcv2_tshut_mode, + + .table = + { + .id = rk3328_code_table, + .length = RT_ARRAY_SIZE(rk3328_code_table), + .data_mask = TSADCV2_DATA_MASK, + .mode = ADC_INCREMENT, + }, +}; + +static const struct rockchip_tsadc_chip rk3328_tsadc_data = +{ + /* cpu */ + .chn_offset = 0, + .chn_num = 1, /* one channels for tsadc */ + .chn_name = chn_name_common, + + .tshut_mode = TSHUT_MODE_CRU, /* default TSHUT via CRU */ + .tshut_temp = 95000, + + .initialize = rk_tsadcv2_initialize, + .irq_ack = rk_tsadcv3_irq_ack, + .control = rk_tsadcv3_control, + .get_temp = rk_tsadcv2_get_temp, + .set_alarm_temp = rk_tsadcv2_alarm_temp, + .set_tshut_temp = rk_tsadcv2_tshut_temp, + .set_tshut_mode = rk_tsadcv2_tshut_mode, + + .table = + { + .id = rk3328_code_table, + .length = RT_ARRAY_SIZE(rk3328_code_table), + .data_mask = TSADCV2_DATA_MASK, + .mode = ADC_INCREMENT, + }, +}; + +static const struct rockchip_tsadc_chip rk3366_tsadc_data = +{ + /* cpu, gpu */ + .chn_offset = 0, + .chn_num = 2, /* two channels for tsadc */ + .chn_name = chn_name_common, + + .tshut_mode = TSHUT_MODE_GPIO, /* default TSHUT via GPIO give PMIC */ + .tshut_polarity = TSHUT_LOW_ACTIVE, /* default TSHUT LOW ACTIVE */ + .tshut_temp = 95000, + + .initialize = rk_tsadcv3_initialize, + .irq_ack = rk_tsadcv3_irq_ack, + .control = rk_tsadcv3_control, + .get_temp = rk_tsadcv2_get_temp, + .set_alarm_temp = rk_tsadcv2_alarm_temp, + .set_tshut_temp = rk_tsadcv2_tshut_temp, + .set_tshut_mode = rk_tsadcv2_tshut_mode, + + .table = + { + .id = rk3228_code_table, + .length = RT_ARRAY_SIZE(rk3228_code_table), + .data_mask = TSADCV3_DATA_MASK, + .mode = ADC_INCREMENT, + }, +}; + +static const struct tsadc_table rk3368_code_table[] = +{ + { 0, -40000 }, + { 106, -40000 }, + { 108, -35000 }, + { 110, -30000 }, + { 112, -25000 }, + { 114, -20000 }, + { 116, -15000 }, + { 118, -10000 }, + { 120, -5000 }, + { 122, 0 }, + { 124, 5000 }, + { 126, 10000 }, + { 128, 15000 }, + { 130, 20000 }, + { 132, 25000 }, + { 134, 30000 }, + { 136, 35000 }, + { 138, 40000 }, + { 140, 45000 }, + { 142, 50000 }, + { 144, 55000 }, + { 146, 60000 }, + { 148, 65000 }, + { 150, 70000 }, + { 152, 75000 }, + { 154, 80000 }, + { 156, 85000 }, + { 158, 90000 }, + { 160, 95000 }, + { 162, 100000 }, + { 163, 105000 }, + { 165, 110000 }, + { 167, 115000 }, + { 169, 120000 }, + { 171, 125000 }, + { TSADCV3_DATA_MASK, 125000 }, +}; + +static const struct rockchip_tsadc_chip rk3368_tsadc_data = +{ + /* cpu, gpu */ + .chn_offset = 0, + .chn_num = 2, /* two channels for tsadc */ + .chn_name = chn_name_common, + + .tshut_mode = TSHUT_MODE_GPIO, /* default TSHUT via GPIO give PMIC */ + .tshut_polarity = TSHUT_LOW_ACTIVE, /* default TSHUT LOW ACTIVE */ + .tshut_temp = 95000, + + .initialize = rk_tsadcv2_initialize, + .irq_ack = rk_tsadcv2_irq_ack, + .control = rk_tsadcv2_control, + .get_temp = rk_tsadcv2_get_temp, + .set_alarm_temp = rk_tsadcv2_alarm_temp, + .set_tshut_temp = rk_tsadcv2_tshut_temp, + .set_tshut_mode = rk_tsadcv2_tshut_mode, + + .table = + { + .id = rk3368_code_table, + .length = RT_ARRAY_SIZE(rk3368_code_table), + .data_mask = TSADCV3_DATA_MASK, + .mode = ADC_INCREMENT, + }, +}; + +static const struct tsadc_table rk3399_code_table[] = +{ + { 0, -40000 }, + { 402, -40000 }, + { 410, -35000 }, + { 419, -30000 }, + { 427, -25000 }, + { 436, -20000 }, + { 444, -15000 }, + { 453, -10000 }, + { 461, -5000 }, + { 470, 0 }, + { 478, 5000 }, + { 487, 10000 }, + { 496, 15000 }, + { 504, 20000 }, + { 513, 25000 }, + { 521, 30000 }, + { 530, 35000 }, + { 538, 40000 }, + { 547, 45000 }, + { 555, 50000 }, + { 564, 55000 }, + { 573, 60000 }, + { 581, 65000 }, + { 590, 70000 }, + { 599, 75000 }, + { 607, 80000 }, + { 616, 85000 }, + { 624, 90000 }, + { 633, 95000 }, + { 642, 100000 }, + { 650, 105000 }, + { 659, 110000 }, + { 668, 115000 }, + { 677, 120000 }, + { 685, 125000 }, + { TSADCV3_DATA_MASK, 125000 }, +}; + +static const struct rockchip_tsadc_chip rk3399_tsadc_data = +{ + /* cpu, gpu */ + .chn_offset = 0, + .chn_num = 2, /* two channels for tsadc */ + .chn_name = chn_name_common, + + .tshut_mode = TSHUT_MODE_GPIO, /* default TSHUT via GPIO give PMIC */ + .tshut_polarity = TSHUT_LOW_ACTIVE, /* default TSHUT LOW ACTIVE */ + .tshut_temp = 95000, + + .initialize = rk_tsadcv3_initialize, + .irq_ack = rk_tsadcv3_irq_ack, + .control = rk_tsadcv3_control, + .get_temp = rk_tsadcv2_get_temp, + .set_alarm_temp = rk_tsadcv2_alarm_temp, + .set_tshut_temp = rk_tsadcv2_tshut_temp, + .set_tshut_mode = rk_tsadcv2_tshut_mode, + + .table = + { + .id = rk3399_code_table, + .length = RT_ARRAY_SIZE(rk3399_code_table), + .data_mask = TSADCV3_DATA_MASK, + .mode = ADC_INCREMENT, + }, +}; + +static const struct tsadc_table rk3568_code_table[] = +{ + { 0, -40000 }, + { 1584, -40000 }, + { 1620, -35000 }, + { 1652, -30000 }, + { 1688, -25000 }, + { 1720, -20000 }, + { 1756, -15000 }, + { 1788, -10000 }, + { 1824, -5000 }, + { 1856, 0 }, + { 1892, 5000 }, + { 1924, 10000 }, + { 1956, 15000 }, + { 1992, 20000 }, + { 2024, 25000 }, + { 2060, 30000 }, + { 2092, 35000 }, + { 2128, 40000 }, + { 2160, 45000 }, + { 2196, 50000 }, + { 2228, 55000 }, + { 2264, 60000 }, + { 2300, 65000 }, + { 2332, 70000 }, + { 2368, 75000 }, + { 2400, 80000 }, + { 2436, 85000 }, + { 2468, 90000 }, + { 2500, 95000 }, + { 2536, 100000 }, + { 2572, 105000 }, + { 2604, 110000 }, + { 2636, 115000 }, + { 2672, 120000 }, + { 2704, 125000 }, + { TSADCV2_DATA_MASK, 125000 }, +}; + +static const struct rockchip_tsadc_chip rk3568_tsadc_data = +{ + /* cpu, gpu */ + .chn_offset = 0, + .chn_num = 2, /* two channels for tsadc */ + .chn_name = chn_name_common, + + .tshut_mode = TSHUT_MODE_GPIO, /* default TSHUT via GPIO give PMIC */ + .tshut_polarity = TSHUT_LOW_ACTIVE, /* default TSHUT LOW ACTIVE */ + .tshut_temp = 95000, + + .initialize = rk_tsadcv7_initialize, + .irq_ack = rk_tsadcv3_irq_ack, + .control = rk_tsadcv3_control, + .get_temp = rk_tsadcv2_get_temp, + .set_alarm_temp = rk_tsadcv2_alarm_temp, + .set_tshut_temp = rk_tsadcv2_tshut_temp, + .set_tshut_mode = rk_tsadcv2_tshut_mode, + + .table = + { + .id = rk3568_code_table, + .length = RT_ARRAY_SIZE(rk3568_code_table), + .data_mask = TSADCV2_DATA_MASK, + .mode = ADC_INCREMENT, + }, +}; + +static const struct tsadc_table rk3588_code_table[] = +{ + { 0, -40000 }, + { 215, -40000 }, + { 285, 25000 }, + { 350, 85000 }, + { 395, 125000 }, + { TSADCV4_DATA_MASK, 125000 }, +}; + +static const char * const chn_name_rk3588[] = +{ + "Top", "Big Core0", "Big Core1", "Little Core", "Center", "GPU", "NPU", +}; + +static const struct rockchip_tsadc_chip rk3588_tsadc_data = +{ + /* top, big_core0, big_core1, little_core, center, gpu, npu */ + .chn_offset = 0, + .chn_num = 7, /* seven channels for tsadc */ + .chn_name = chn_name_rk3588, + + .tshut_mode = TSHUT_MODE_GPIO, /* default TSHUT via GPIO give PMIC */ + .tshut_polarity = TSHUT_LOW_ACTIVE, /* default TSHUT LOW ACTIVE */ + .tshut_temp = 95000, + .initialize = rk_tsadcv8_initialize, + .irq_ack = rk_tsadcv4_irq_ack, + .control = rk_tsadcv4_control, + .get_temp = rk_tsadcv4_get_temp, + .set_alarm_temp = rk_tsadcv3_alarm_temp, + .set_tshut_temp = rk_tsadcv3_tshut_temp, + .set_tshut_mode = rk_tsadcv3_tshut_mode, + .table = + { + .id = rk3588_code_table, + .length = RT_ARRAY_SIZE(rk3588_code_table), + .data_mask = TSADCV4_DATA_MASK, + .mode = ADC_INCREMENT, + }, +}; + +static rt_err_t rk_tsadc_thermal_zone_get_temp(struct rt_thermal_zone_device *zdev, + int *out_temp) +{ + struct rockchip_tsadc *rk_tsadc; + const struct rockchip_tsadc_chip *chip; + struct rockchip_tsadc_channel *channel = raw_to_rockchip_tsadc_channel(zdev); + + rk_tsadc = channel->rk_tsadc; + chip = rk_tsadc->chip; + + return chip->get_temp(&chip->table, channel->id, rk_tsadc->regs, out_temp); +} + +static rt_err_t rk_tsadc_thermal_zone_set_trips(struct rt_thermal_zone_device *zdev, + int low_temp, int high_temp) +{ + struct rockchip_tsadc *rk_tsadc; + const struct rockchip_tsadc_chip *chip; + struct rockchip_tsadc_channel *channel = raw_to_rockchip_tsadc_channel(zdev); + + rk_tsadc = channel->rk_tsadc; + chip = rk_tsadc->chip; + + LOG_D("Channel[%d]<%s> set trip alarm %d ~ %d", + zdev->zone_id, + chip->chn_name[zdev->zone_id - chip->chn_offset], + low_temp, high_temp); + + return chip->set_alarm_temp(&rk_tsadc->chip->table, channel->id, rk_tsadc->regs, high_temp); +} + +const static struct rt_thermal_zone_ops rk_tsadc_thermal_zone_ops = +{ + .get_temp = rk_tsadc_thermal_zone_get_temp, + .set_trips = rk_tsadc_thermal_zone_set_trips, +}; + +static void rk_tsadc_isr(int irqno, void *param) +{ + struct rockchip_tsadc *rk_tsadc = param; + const struct rockchip_tsadc_chip *chip = rk_tsadc->chip; + + chip->irq_ack(rk_tsadc->regs); + + for (int i = 0; i < chip->chn_num; ++i) + { + struct rockchip_tsadc_channel *channel = &rk_tsadc->channels[i]; + + rt_thermal_zone_device_update(&channel->parent, RT_THERMAL_MSG_EVENT_UNSPECIFIED); + } +} + +static rt_err_t rockchip_tsadc_parse_ofw(struct rt_device *dev, + struct rockchip_tsadc *rk_tsadc) +{ + struct rt_ofw_node *np = dev->ofw_node; + rt_uint32_t shut_temp, tshut_mode, tshut_polarity; + + if (rt_ofw_prop_read_u32(np, "rockchip,hw-tshut-temp", &shut_temp)) + { + rk_tsadc->tshut_temp = rk_tsadc->chip->tshut_temp; + } + else + { + if (shut_temp > TEMP_INVALID) + { + LOG_E("Invalid tshut temperature specified: %d", shut_temp); + + return -RT_EINVAL; + } + + rk_tsadc->tshut_temp = shut_temp; + } + + if (rt_ofw_prop_read_u32(np, "rockchip,hw-tshut-mode", &tshut_mode)) + { + LOG_W("Missing tshut mode property, using default (%s)", + rk_tsadc->chip->tshut_mode == TSHUT_MODE_GPIO ? "gpio" : "cru"); + + rk_tsadc->tshut_mode = rk_tsadc->chip->tshut_mode; + } + else + { + rk_tsadc->tshut_mode = tshut_mode; + } + + if (rk_tsadc->tshut_mode > 1) + { + LOG_E("Invalid tshut mode specified: %d", rk_tsadc->tshut_mode); + + return -RT_EINVAL; + } + + if (rt_ofw_prop_read_u32(np, "rockchip,hw-tshut-polarity", &tshut_polarity)) + { + LOG_W("Missing tshut-polarity property, using default (%s)", + rk_tsadc->chip->tshut_polarity == TSHUT_LOW_ACTIVE ? "low" : "high"); + + rk_tsadc->tshut_polarity = rk_tsadc->chip->tshut_polarity; + } + else + { + rk_tsadc->tshut_polarity = tshut_polarity; + } + + if (rk_tsadc->tshut_polarity > 1) + { + LOG_E("Invalid tshut-polarity specified: %d", rk_tsadc->tshut_polarity); + + return -RT_EINVAL; + } + + rk_tsadc->grf = rt_syscon_find_by_ofw_phandle(np, "rockchip,grf"); + + if (!rk_tsadc->grf) + { + LOG_W("Missing %s property", "rockchip,grf"); + } + + return RT_EOK; +} + +static void rockchip_tsadc_reset_controller(struct rockchip_tsadc *rk_tsadc) +{ + rt_reset_control_assert(rk_tsadc->rstc); + rt_hw_us_delay(15); + rt_reset_control_deassert(rk_tsadc->rstc); +} + +static void rockchip_tsadc_free(struct rockchip_tsadc *rk_tsadc) +{ + if (!rt_is_err_or_null(rk_tsadc->rstc)) + { + rt_reset_control_assert(rk_tsadc->rstc); + rt_reset_control_put(rk_tsadc->rstc); + } + + if (!rt_is_err_or_null(rk_tsadc->clk)) + { + rt_clk_disable(rk_tsadc->clk); + rt_clk_put(rk_tsadc->clk); + } + + if (!rt_is_err_or_null(rk_tsadc->pclk)) + { + rt_clk_disable(rk_tsadc->pclk); + rt_clk_put(rk_tsadc->pclk); + } + + rt_free(rk_tsadc); +} + +static rt_err_t rockchip_tsadc_probe(struct rt_platform_device *pdev) +{ + rt_err_t err; + const struct chip_tsadc_table *table; + struct rt_device *dev = &pdev->parent; + const struct rockchip_tsadc_chip *chip = pdev->id->data; + struct rockchip_tsadc *rk_tsadc = rt_calloc(1, sizeof(*rk_tsadc) + + sizeof(rk_tsadc->channels[0]) * chip->chn_num); + + if (!rk_tsadc) + { + return -RT_ENOMEM; + } + + rk_tsadc->chip = chip; + table = &rk_tsadc->chip->table; + rk_tsadc->irq = rt_dm_dev_get_irq(dev, 0); + + if (rk_tsadc->irq < 0) + { + err = rk_tsadc->irq; + goto _fail; + } + + rk_tsadc->regs = rt_dm_dev_iomap(dev, 0); + + if (!rk_tsadc->regs) + { + err = -RT_EIO; + goto _fail; + } + + rk_tsadc->rstc = rt_reset_control_get_array(dev); + + if (rt_is_err(rk_tsadc->rstc)) + { + err = rt_ptr_err(rk_tsadc->rstc); + goto _fail; + } + + rockchip_tsadc_reset_controller(rk_tsadc); + + rk_tsadc->clk = rt_clk_get_by_name(dev, "tsadc"); + + if (rt_is_err(rk_tsadc->clk)) + { + err = rt_ptr_err(rk_tsadc->clk); + goto _fail; + } + + rk_tsadc->pclk = rt_clk_get_by_name(dev, "apb_pclk"); + + if (rt_is_err(rk_tsadc->pclk)) + { + err = rt_ptr_err(rk_tsadc->pclk); + goto _fail; + } + + if ((err = rt_clk_enable(rk_tsadc->clk))) + { + goto _fail; + } + + if ((err = rt_clk_enable(rk_tsadc->pclk))) + { + goto _fail; + } + + if ((err = rockchip_tsadc_parse_ofw(dev, rk_tsadc))) + { + goto _fail; + } + + rk_tsadc->chip->initialize(rk_tsadc->grf, rk_tsadc->regs, rk_tsadc->tshut_polarity); + + for (int i = 0; i < rk_tsadc->chip->chn_num; ++i) + { + struct rockchip_tsadc_channel *channel = &rk_tsadc->channels[i]; + struct rt_thermal_zone_device *tz = &channel->parent; + + tz->zone_id = chip->chn_offset + i; + channel->rk_tsadc = rk_tsadc; + + chip->set_tshut_mode(channel->id, rk_tsadc->regs, rk_tsadc->tshut_mode); + chip->set_tshut_temp(table, channel->id, rk_tsadc->regs, rk_tsadc->tshut_temp); + + tz->ops = &rk_tsadc_thermal_zone_ops; + + rt_thermal_zone_device_register(tz); + } + + rk_tsadc->chip->control(rk_tsadc->regs, RT_TRUE); + + rt_hw_interrupt_install(rk_tsadc->irq, rk_tsadc_isr, rk_tsadc, "tsadc"); + rt_hw_interrupt_umask(rk_tsadc->irq); + + dev->user_data = rk_tsadc; + + return RT_EOK; + +_fail: + rockchip_tsadc_free(rk_tsadc); + + return err; +} + +static rt_err_t rockchip_tsadc_remove(struct rt_platform_device *pdev) +{ + struct rockchip_tsadc *rk_tsadc = pdev->parent.user_data; + + for (int i = 0; i < rk_tsadc->chip->chn_num; ++i) + { + struct rockchip_tsadc_channel *channel = &rk_tsadc->channels[i]; + + rt_thermal_zone_device_unregister(&channel->parent); + } + + rockchip_tsadc_free(rk_tsadc); + + return RT_EOK; +} + +static const struct rt_ofw_node_id rockchip_tsadc_ofw_ids[] = +{ + { .compatible = "rockchip,px30-tsadc", .data = &px30_tsadc_data, }, + { .compatible = "rockchip,rv1108-tsadc", .data = &rv1108_tsadc_data, }, + { .compatible = "rockchip,rk3228-tsadc", .data = &rk3228_tsadc_data, }, + { .compatible = "rockchip,rk3288-tsadc", .data = &rk3288_tsadc_data, }, + { .compatible = "rockchip,rk3308-tsadc", .data = &rk3308_tsadc_data, }, + { .compatible = "rockchip,rk3328-tsadc", .data = &rk3328_tsadc_data, }, + { .compatible = "rockchip,rk3366-tsadc", .data = &rk3366_tsadc_data, }, + { .compatible = "rockchip,rk3368-tsadc", .data = &rk3368_tsadc_data, }, + { .compatible = "rockchip,rk3399-tsadc", .data = &rk3399_tsadc_data, }, + { .compatible = "rockchip,rk3568-tsadc", .data = &rk3568_tsadc_data, }, + { .compatible = "rockchip,rk3588-tsadc", .data = &rk3588_tsadc_data, }, + { /* sentinel */ } +}; + +static struct rt_platform_driver rockchip_tsadc_driver = +{ + .name = "rockchip-tsadc", + .ids = rockchip_tsadc_ofw_ids, + + .probe = rockchip_tsadc_probe, + .remove = rockchip_tsadc_remove, +}; + +static int rockchip_tsadc_drv_register(void) +{ + rt_platform_driver_register(&rockchip_tsadc_driver); + + return 0; +} +INIT_SUBSYS_LATER_EXPORT(rockchip_tsadc_drv_register); diff --git a/components/drivers/virtio/Kconfig b/components/drivers/virtio/Kconfig index 0f7d0ad5ea0d..c8fc724b3c24 100755 --- a/components/drivers/virtio/Kconfig +++ b/components/drivers/virtio/Kconfig @@ -3,15 +3,28 @@ menuconfig RT_USING_VIRTIO depends on RT_USING_DM default n +config RT_VIRTIO_TRANSPORT_MMIO + bool "Using VirtIO MMIO transport" + depends on RT_USING_VIRTIO + select RT_USING_OFW + default y + +config RT_VIRTIO_TRANSPORT_PCI + bool "Using VirtIO PCI transport" + depends on RT_USING_VIRTIO + select RT_USING_PCI + default y + config RT_VIRTIO_NET bool "VirtIO Net" - select RT_USING_LWIP depends on RT_USING_VIRTIO + select RT_USING_LWIP default y config RT_VIRTIO_BLK bool "VirtIO BLK" depends on RT_USING_VIRTIO + select RT_USING_BLK default y config RT_VIRTIO_CONSOLE @@ -21,9 +34,9 @@ config RT_VIRTIO_CONSOLE config RT_VIRTIO_RNG bool "VirtIO RNG" + depends on RT_USING_VIRTIO select RT_USING_HWCRYPTO select RT_HWCRYPTO_USING_RNG - depends on RT_USING_VIRTIO default y config RT_VIRTIO_GPU @@ -35,3 +48,9 @@ config RT_VIRTIO_INPUT bool "VirtIO Input" depends on RT_USING_VIRTIO default y + +config RT_VIRTIO_SOUND + bool "VirtIO Sound" + depends on RT_USING_VIRTIO + select RT_USING_AUDIO + default y diff --git a/components/drivers/virtio/SConscript b/components/drivers/virtio/SConscript index 18ea6d0b5ecc..da57781ebfed 100644 --- a/components/drivers/virtio/SConscript +++ b/components/drivers/virtio/SConscript @@ -10,9 +10,12 @@ CPPPATH = [cwd + '/../include'] src = ['virtio.c', 'virtio_queue.c'] -if GetDepend(['RT_USING_OFW']): +if GetDepend(['RT_VIRTIO_TRANSPORT_MMIO']): src += ['virtio_mmio.c'] +if GetDepend(['RT_VIRTIO_TRANSPORT_PCI']): + src += ['virtio_pci.c', 'virtio_pci_legacy.c', 'virtio_pci_modern.c'] + if GetDepend(['RT_VIRTIO_NET']): src += ['virtio-net.c'] @@ -31,6 +34,9 @@ if GetDepend(['RT_VIRTIO_RNG']): if GetDepend(['RT_VIRTIO_INPUT']): src += ['virtio-input.c'] +if GetDepend(['RT_VIRTIO_SOUND']): + src += ['virtio-sound.c'] + group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) Return('group') diff --git a/components/drivers/virtio/virtio-blk.c b/components/drivers/virtio/virtio-blk.c index f484f60144bb..7389d8cdb767 100644 --- a/components/drivers/virtio/virtio-blk.c +++ b/components/drivers/virtio/virtio-blk.c @@ -21,6 +21,7 @@ #include #include "virtio-blk.h" +#include "virtio_internal.h" #define VIRTIO_BLK_VQS_NR 1 @@ -39,7 +40,6 @@ struct virtio_blk_request */ struct virtio_blk_req req; - rt_uint8_t status; /* user data */ rt_bool_t done; @@ -47,7 +47,7 @@ struct virtio_blk_request struct virtio_blk { - struct rt_device parent; + struct rt_blk_disk parent; struct rt_virtio_device *vdev; rt_le32_t blk_size; @@ -61,10 +61,12 @@ struct virtio_blk #define raw_to_virtio_blk(raw) rt_container_of(raw, struct virtio_blk, parent) -static void virtio_blk_rw(struct virtio_blk *vblk, rt_off_t pos, void *buffer, rt_size_t count, int flags) +static rt_err_t virtio_blk_rw(struct virtio_blk *vblk, + rt_off_t sector, void *buffer, rt_size_t sector_count, int type) { rt_size_t size; rt_base_t level; + rt_uint8_t status = 0xff; struct rt_virtqueue *vq = RT_NULL; struct virtio_blk_request *request; @@ -92,27 +94,26 @@ static void virtio_blk_rw(struct virtio_blk *vblk, rt_off_t pos, void *buffer, r rt_thread_yield(); } - size = count * vblk->blk_size; + size = sector_count * vblk->blk_size; request = &vblk->request[vq->index * vblk->virtq_nr + rt_virtqueue_next_buf_index(vq)]; request->done = RT_FALSE; - request->req.type = flags; - request->req.ioprio = 0; - request->req.sector = pos * (vblk->blk_size / 512); - request->status = 0xff; + request->req.type = cpu_to_virtio32(vblk->vdev, type); + request->req.ioprio = cpu_to_virtio32(vblk->vdev, 0); + request->req.sector = cpu_to_virtio64(vblk->vdev, sector * (vblk->blk_size / 512)); rt_virtqueue_add_outbuf(vq, &request->req, sizeof(request->req)); - if (flags == VIRTIO_BLK_T_OUT) + if (type == VIRTIO_BLK_T_OUT) { rt_virtqueue_add_outbuf(vq, buffer, size); } - else + else if (type == VIRTIO_BLK_T_IN) { rt_virtqueue_add_inbuf(vq, buffer, size); } - rt_virtqueue_add_inbuf(vq, &request->status, sizeof(request->status)); + rt_virtqueue_add_inbuf(vq, &status, sizeof(status)); rt_virtqueue_kick(vq); @@ -122,67 +123,99 @@ static void virtio_blk_rw(struct virtio_blk *vblk, rt_off_t pos, void *buffer, r { rt_hw_cpu_relax(); } + + switch (status) + { + case VIRTIO_BLK_S_OK: + return RT_EOK; + + case VIRTIO_BLK_S_UNSUPP: + return -RT_ENOSYS; + + case VIRTIO_BLK_S_ZONE_OPEN_RESOURCE: + return 1; + + case VIRTIO_BLK_S_ZONE_ACTIVE_RESOURCE: + return 2; + + case VIRTIO_BLK_S_IOERR: + case VIRTIO_BLK_S_ZONE_UNALIGNED_WP: + default: + return -RT_EIO; + } } -static rt_ssize_t virtio_blk_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t count) +static rt_ssize_t virtio_blk_read(struct rt_blk_disk *disk, rt_off_t sector, void *buffer, rt_size_t sector_count) { - struct virtio_blk *vblk = raw_to_virtio_blk(dev); + rt_ssize_t res; + struct virtio_blk *vblk = raw_to_virtio_blk(disk); - virtio_blk_rw(vblk, pos, buffer, count, VIRTIO_BLK_T_IN); + res = virtio_blk_rw(vblk, sector, buffer, sector_count, VIRTIO_BLK_T_IN); - return count; + return res >= 0 ? sector_count : res; } -static rt_ssize_t virtio_blk_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t count) +static rt_ssize_t virtio_blk_write(struct rt_blk_disk *disk, rt_off_t sector, const void *buffer, rt_size_t sector_count) { - struct virtio_blk *vblk = raw_to_virtio_blk(dev); + rt_ssize_t res; + struct virtio_blk *vblk = raw_to_virtio_blk(disk); - virtio_blk_rw(vblk, pos, (void *)buffer, count, VIRTIO_BLK_T_OUT); + res = virtio_blk_rw(vblk, sector, (void *)buffer, sector_count, VIRTIO_BLK_T_OUT); - return count; + return res >= 0 ? sector_count : res; } -static rt_err_t virtio_blk_control(rt_device_t dev, int cmd, void *args) +static rt_err_t virtio_blk_getgeome(struct rt_blk_disk *disk, struct rt_device_blk_geometry *geometry) { - rt_err_t err = RT_EOK; rt_le64_t capacity; - struct rt_device_blk_geometry *geometry; - struct virtio_blk *vblk = raw_to_virtio_blk(dev); + struct virtio_blk *vblk = raw_to_virtio_blk(disk); - switch (cmd) - { - case RT_DEVICE_CTRL_BLK_GETGEOME: - geometry = args; + rt_virtio_read_config(vblk->vdev, struct virtio_blk_config, capacity, &capacity); - if (!geometry) - { - err = -RT_EINVAL; - break; - } + geometry->bytes_per_sector = 512; + geometry->block_size = vblk->blk_size; + geometry->sector_count = rt_le64_to_cpu(capacity); - rt_virtio_read_config(vblk->vdev, struct virtio_blk_config, capacity, &capacity); + return RT_EOK; +} - geometry->bytes_per_sector = 512; - geometry->block_size = vblk->blk_size; - geometry->sector_count = capacity; - break; +static rt_err_t virtio_blk_sync(struct rt_blk_disk *disk) +{ + struct virtio_blk *vblk = raw_to_virtio_blk(disk); - default: - err = -RT_EINVAL; - break; - } + return virtio_blk_rw(vblk, 0, RT_NULL, 0, VIRTIO_BLK_T_FLUSH); +} - return err; +static rt_err_t virtio_blk_erase(struct rt_blk_disk *disk) +{ + struct virtio_blk *vblk = raw_to_virtio_blk(disk); + + return virtio_blk_rw(vblk, 0, RT_NULL, 0, VIRTIO_BLK_T_SECURE_ERASE); +} + +static rt_err_t virtio_blk_autorefresh(struct rt_blk_disk *disk, rt_bool_t is_auto) +{ + rt_uint8_t writeback = !is_auto; + struct virtio_blk *vblk = raw_to_virtio_blk(disk); + + /* + * 0: write through + * 1: write back + */ + rt_virtio_write_config(vblk->vdev, struct virtio_blk_config, writeback, &writeback); + + return RT_EOK; } -#ifdef RT_USING_DEVICE_OPS -const static struct rt_device_ops virtio_blk_ops = +static const struct rt_blk_disk_ops virtio_blk_ops = { .read = virtio_blk_read, .write = virtio_blk_write, - .control = virtio_blk_control, + .getgeome = virtio_blk_getgeome, + .sync = virtio_blk_sync, + .erase = virtio_blk_erase, + .autorefresh = virtio_blk_autorefresh, }; -#endif static void virtio_blk_done(struct rt_virtqueue *vq) { @@ -192,12 +225,6 @@ static void virtio_blk_done(struct rt_virtqueue *vq) while ((request = rt_virtqueue_read_buf(vq, RT_NULL))) { request->done = RT_TRUE; - - if (request->status != VIRTIO_BLK_S_OK) - { - LOG_E("request status = %d is fail", request->status); - RT_ASSERT(0); - } } rt_spin_unlock_irqrestore(&vq->vdev->vq_lock, level); @@ -213,7 +240,7 @@ static rt_err_t virtio_blk_vq_init(struct virtio_blk *vblk) { vqs_nr = 1; - LOG_W("%s-%s not support %s", rt_dm_get_dev_name(&vblk->vdev->parent), "blk", "VIRTIO_BLK_F_MQ"); + LOG_W("%s-%s not support %s", rt_dm_dev_get_name(&vblk->vdev->parent), "blk", "VIRTIO_BLK_F_MQ"); } vblk->virtq_nr = rt_min(RT_CPUS_NR, 1024) * VIRTIO_BLK_REQUEST_SPLIT_NR; @@ -252,6 +279,7 @@ static void virtio_blk_vq_finit(struct virtio_blk *vblk) static rt_err_t virtio_blk_probe(struct rt_virtio_device *vdev) { rt_err_t err; + static int index = 0; struct virtio_blk *vblk = rt_calloc(1, sizeof(*vblk)); if (!vblk) @@ -260,32 +288,25 @@ static rt_err_t virtio_blk_probe(struct rt_virtio_device *vdev) } vblk->vdev = vdev; - - vblk->parent.type = RT_Device_Class_Block; -#ifdef RT_USING_DEVICE_OPS + vblk->parent.parallel_io = RT_TRUE; vblk->parent.ops = &virtio_blk_ops; -#else - vblk->parent.read = virtio_blk_read; - vblk->parent.write = virtio_blk_write; - vblk->parent.control = virtio_blk_control; -#endif + vblk->parent.max_partitions = RT_BLK_PARTITION_MAX; if ((err = virtio_blk_vq_init(vblk))) { goto _fail; } - if ((err = rt_dm_set_dev_name_auto(&vblk->parent, "block")) < 0) - { - goto _fail; - } + rt_virtio_read_config(vblk->vdev, struct virtio_blk_config, blk_size, &vblk->blk_size); - if ((err = rt_device_register(&vblk->parent, rt_dm_get_dev_name(&vblk->parent), RT_DEVICE_FLAG_RDWR))) + rt_dm_dev_set_name(&vblk->parent.parent, "vd%c", 'a' + index); + + if ((err = rt_hw_blk_disk_register(&vblk->parent))) { goto _fail; } - rt_virtio_read_config(vblk->vdev, struct virtio_blk_config, blk_size, &vblk->blk_size); + ++index; return RT_EOK; @@ -310,9 +331,11 @@ static struct rt_virtio_driver virtio_blk_driver = | RT_BIT(VIRTIO_BLK_F_GEOMETRY) | RT_BIT(VIRTIO_BLK_F_BLK_SIZE) | RT_BIT(VIRTIO_BLK_F_TOPOLOGY) + | RT_BIT(VIRTIO_BLK_F_CONFIG_WCE) | RT_BIT(VIRTIO_BLK_F_MQ) | RT_BIT(VIRTIO_BLK_F_DISCARD) - | RT_BIT(VIRTIO_BLK_F_WRITE_ZEROES), + | RT_BIT(VIRTIO_BLK_F_WRITE_ZEROES) + | RT_BIT(VIRTIO_BLK_F_SECURE_ERASE), .probe = virtio_blk_probe, }; diff --git a/components/drivers/virtio/virtio-blk.h b/components/drivers/virtio/virtio-blk.h index 4ac0da2f8351..83720c3e2fe4 100644 --- a/components/drivers/virtio/virtio-blk.h +++ b/components/drivers/virtio/virtio-blk.h @@ -52,9 +52,13 @@ struct virtio_blk_req rt_le32_t ioprio; rt_le64_t sector; -#define VIRTIO_BLK_S_OK 0 -#define VIRTIO_BLK_S_IOERR 1 -#define VIRTIO_BLK_S_UNSUPP 2 +#define VIRTIO_BLK_S_OK 0 +#define VIRTIO_BLK_S_IOERR 1 +#define VIRTIO_BLK_S_UNSUPP 2 +#define VIRTIO_BLK_S_ZONE_INVALID_CMD 3 +#define VIRTIO_BLK_S_ZONE_UNALIGNED_WP 4 +#define VIRTIO_BLK_S_ZONE_OPEN_RESOURCE 5 +#define VIRTIO_BLK_S_ZONE_ACTIVE_RESOURCE 6 /* * next: * rt_uint8_t data[]; @@ -119,6 +123,6 @@ struct virtio_blk_config rt_le32_t max_secure_erase_sectors; rt_le32_t max_secure_erase_seg; rt_le32_t secure_erase_sector_alignment; -} __attribute__((packed)); +} rt_packed; #endif /* __VIRTIO_BLK_H__ */ diff --git a/components/drivers/virtio/virtio-console.c b/components/drivers/virtio/virtio-console.c index f65c4dd98f1f..756cea9efd25 100644 --- a/components/drivers/virtio/virtio-console.c +++ b/components/drivers/virtio/virtio-console.c @@ -180,16 +180,16 @@ static rt_err_t virtio_console_probe(struct rt_virtio_device *vdev) rt_list_init(&vconsole->port_nodes); - if ((err = rt_dm_set_dev_name_auto(&vport->parent, "vport")) < 0) + if ((err = rt_dm_dev_set_name_auto(&vport->parent, "vport")) < 0) { goto _fail; } - vconsole->uid = rt_dm_get_dev_name_id(&vport->parent); + vconsole->uid = rt_dm_dev_get_name_id(&vport->parent); - rt_dm_set_dev_name(&vport->parent, "%s%dp%d", "vport", vconsole->uid, 0); + rt_dm_dev_set_name(&vport->parent, "%s%dp%d", "vport", vconsole->uid, 0); - if ((err = rt_device_register(&vport->parent, rt_dm_get_dev_name(&vport->parent), + if ((err = rt_device_register(&vport->parent, rt_dm_dev_get_name(&vport->parent), RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_TX | RT_DEVICE_FLAG_INT_RX | RT_DEVICE_FLAG_REMOVABLE))) { goto _fail; diff --git a/components/drivers/virtio/virtio-console.h b/components/drivers/virtio/virtio-console.h index e5e45aed153e..0af72fd97e98 100644 --- a/components/drivers/virtio/virtio-console.h +++ b/components/drivers/virtio/virtio-console.h @@ -22,7 +22,7 @@ struct virtio_console_config rt_uint16_t rows; rt_uint32_t max_nr_ports; rt_uint32_t emerg_wr; -} __attribute__((packed)); +} rt_packed; struct virtio_console_control { diff --git a/components/drivers/virtio/virtio-input.c b/components/drivers/virtio/virtio-input.c index 64f8c692d18e..7860d4cd1f29 100644 --- a/components/drivers/virtio/virtio-input.c +++ b/components/drivers/virtio/virtio-input.c @@ -326,12 +326,12 @@ static rt_err_t virtio_input_probe(struct rt_virtio_device *vdev) goto _fail; } - if ((err = rt_dm_set_dev_name_auto(&vinput->parent, "input-event")) < 0) + if ((err = rt_dm_dev_set_name_auto(&vinput->parent, "input-event")) < 0) { goto _fail; } - dev_name = rt_dm_get_dev_name(&vinput->parent); + dev_name = rt_dm_dev_get_name(&vinput->parent); if ((err = rt_device_register(&vinput->parent, dev_name, RT_DEVICE_FLAG_STANDALONE | RT_DEVICE_FLAG_INT_RX))) { diff --git a/components/drivers/virtio/virtio-input.h b/components/drivers/virtio/virtio-input.h index 2877bfc50d2f..82f60f7ddd44 100644 --- a/components/drivers/virtio/virtio-input.h +++ b/components/drivers/virtio/virtio-input.h @@ -56,7 +56,7 @@ struct virtio_input_config struct virtio_input_absinfo abs; struct virtio_input_devids ids; }; -} __attribute__((packed)); +} rt_packed; struct virtio_input_event { diff --git a/components/drivers/virtio/virtio-net.c b/components/drivers/virtio/virtio-net.c index d25f5d17585c..d891718418b0 100644 --- a/components/drivers/virtio/virtio-net.c +++ b/components/drivers/virtio/virtio-net.c @@ -164,7 +164,7 @@ static struct pbuf *virtio_net_rx(rt_device_t dev) if (size > sizeof(request->rx.mss)) { LOG_W("%s receive buffer's size = %u > %u is overflow", - rt_dm_get_dev_name(&vnet->parent.parent), size, sizeof(request->rx.mss)); + rt_dm_dev_get_name(&vnet->parent.parent), size, sizeof(request->rx.mss)); size = sizeof(request->rx.mss); } @@ -241,9 +241,12 @@ const static struct rt_device_ops virtio_net_ops = static void virtio_net_rx_done(struct rt_virtqueue *vq) { struct virtio_net *vnet = vq->vdev->priv; + rt_ubase_t level = rt_spin_lock_irqsave(&vnet->vdev->vq_lock); virtio_net_enqueue(vnet, vq); + rt_spin_unlock_irqrestore(&vnet->vdev->vq_lock, level); + eth_device_ready(&vnet->parent); } @@ -276,7 +279,7 @@ static void virtio_net_config_changed(struct rt_virtio_device *vdev) link_up = !!((vnet->status = status) & VIRTIO_NET_S_LINK_UP); - LOG_D("%s linkchange to %s", rt_dm_get_dev_name(&vdev->parent), link_up ? "up" : "down"); + LOG_D("%s linkchange to %s", rt_dm_dev_get_name(&vdev->parent), link_up ? "up" : "down"); eth_device_linkchange(&vnet->parent, link_up); } @@ -379,12 +382,12 @@ static rt_err_t virtio_net_probe(struct rt_virtio_device *vdev) goto _fail; } - if ((err = rt_dm_set_dev_name_auto(&vnet->parent.parent, "e")) < 0) + if ((err = rt_dm_dev_set_name_auto(&vnet->parent.parent, "e")) < 0) { goto _fail; } - if ((err = eth_device_init(&vnet->parent, rt_dm_get_dev_name(&vnet->parent.parent)))) + if ((err = eth_device_init(&vnet->parent, rt_dm_dev_get_name(&vnet->parent.parent)))) { goto _fail; } @@ -420,6 +423,7 @@ static struct rt_virtio_driver virtio_net_driver = | RT_BIT(VIRTIO_NET_F_GUEST_ANNOUNCE) | RT_BIT(VIRTIO_NET_F_MQ) | RT_BIT(VIRTIO_NET_F_CTRL_MAC_ADDR) + | RT_BIT(VIRTIO_NET_F_SPEED_DUPLEX) | RT_BIT(VIRTIO_F_ANY_LAYOUT) | RT_BIT(VIRTIO_F_RING_INDIRECT_DESC), diff --git a/components/drivers/virtio/virtio-net.h b/components/drivers/virtio/virtio-net.h index b1fde51eaf1a..e8ad5f34cfe8 100644 --- a/components/drivers/virtio/virtio-net.h +++ b/components/drivers/virtio/virtio-net.h @@ -60,7 +60,7 @@ struct virtio_net_hdr rt_uint16_t csum_start; rt_uint16_t csum_offset; rt_uint16_t num_buffers; -} __attribute__ ((packed)); +} rt_packed; #define VIRTIO_NET_MSS 1514 @@ -77,6 +77,6 @@ struct virtio_net_config rt_uint8_t rss_max_key_size; rt_uint16_t rss_max_indirection_table_length; rt_uint32_t supported_hash_types; -} __attribute__((packed)); +} rt_packed; #endif /* __VIRTIO_NET_H__ */ diff --git a/components/drivers/virtio/virtio-sound.c b/components/drivers/virtio/virtio-sound.c new file mode 100644 index 000000000000..1874bd6d5802 --- /dev/null +++ b/components/drivers/virtio/virtio-sound.c @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-02-25 GuEe-GUI the first version + */ + +#include +#include +#include + +#define DBG_TAG "virtio.dev.sound" +#define DBG_LVL DBG_INFO +#include + +static rt_err_t virtio_sound_probe(struct rt_virtio_device *vdev) +{ + return RT_EOK; +} + +static const struct rt_virtio_device_id virtio_sound_ids[] = +{ + { VIRTIO_DEVICE_ID_AUDIO, VIRTIO_DEVICE_ANY_ID }, + { /* sentinel */ } +}; + +static struct rt_virtio_driver virtio_sound_driver = +{ + .ids = virtio_sound_ids, + .features = + RT_BIT(VIRTIO_F_ANY_LAYOUT) + | RT_BIT(VIRTIO_F_RING_INDIRECT_DESC), + + .probe = virtio_sound_probe, +}; +RT_VIRTIO_DRIVER_EXPORT(virtio_sound_driver); diff --git a/components/drivers/virtio/virtio-sound.h b/components/drivers/virtio/virtio-sound.h new file mode 100644 index 000000000000..efa662773d77 --- /dev/null +++ b/components/drivers/virtio/virtio-sound.h @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-02-25 GuEe-GUI the first version + */ + +#ifndef __VIRTIO_SOUND_H__ +#define __VIRTIO_SOUND_H__ + +#include + +#endif /* __VIRTIO_SOUND_H__ */ diff --git a/components/drivers/virtio/virtio.c b/components/drivers/virtio/virtio.c index 152e2ce9d9aa..91dcf65607e5 100644 --- a/components/drivers/virtio/virtio.c +++ b/components/drivers/virtio/virtio.c @@ -151,6 +151,7 @@ static const char *const virtio_device_id_name[] = [VIRTIO_DEVICE_ID_RNG] = "rng", [VIRTIO_DEVICE_ID_GPU] = "gpu", [VIRTIO_DEVICE_ID_INPUT] = "input", + [VIRTIO_DEVICE_ID_AUDIO] = "sound", [VIRTIO_DEVICE_ID_MAX] = RT_NULL, }; @@ -183,9 +184,22 @@ static struct rt_bus virtio_bus; rt_err_t rt_virtio_driver_register(struct rt_virtio_driver *vdrv) { + const struct rt_virtio_device_id *id; + RT_ASSERT(vdrv != RT_NULL); + id = vdrv->ids; + while ((id + 1)->device) + { + ++id; + } + vdrv->parent.bus = &virtio_bus; +#if RT_NAME_MAX > 0 + rt_strcpy(vdrv->parent.parent.name, virtio_device_id_name[id->device]); +#else + vdrv->parent.parent.name = virtio_device_id_name[id->device]; +#endif return rt_driver_register(&vdrv->parent); } @@ -199,7 +213,7 @@ rt_err_t rt_virtio_device_register(struct rt_virtio_device *vdev) vdev->idx = (int)rt_hw_atomic_add(&uid, 1); - rt_dm_set_dev_name(&vdev->parent, "virtio%u", vdev->idx); + rt_dm_dev_set_name(&vdev->parent, "virtio%u", vdev->idx); rt_list_init(&vdev->vq_node); @@ -273,7 +287,7 @@ static rt_err_t virtio_probe(rt_device_t dev) if (!(status & VIRTIO_STATUS_FEATURES_OK)) { - LOG_E("%s device refuses features: %x", rt_dm_get_dev_name(dev), status); + LOG_E("%s device refuses features: %x", rt_dm_dev_get_name(dev), status); err = -RT_EIO; goto _err; diff --git a/components/drivers/virtio/virtio_ids.h b/components/drivers/virtio/virtio_ids.h new file mode 100644 index 000000000000..f995f95dbfb6 --- /dev/null +++ b/components/drivers/virtio/virtio_ids.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __VIRTIO_IDS_H__ +#define __VIRTIO_IDS_H__ + +enum +{ + /* virtio 1.0 */ + VIRTIO_DEVICE_ID_INVALID = 0, /* Invalid device */ + VIRTIO_DEVICE_ID_NET = 1, /* Net */ + VIRTIO_DEVICE_ID_BLOCK = 2, /* Block */ + VIRTIO_DEVICE_ID_CONSOLE = 3, /* Console */ + VIRTIO_DEVICE_ID_RNG = 4, /* Rng */ + VIRTIO_DEVICE_ID_BALLOON = 5, /* Balloon */ + VIRTIO_DEVICE_ID_IOMEM = 6, /* IO memory */ + VIRTIO_DEVICE_ID_RPMSG = 7, /* Remote processor messaging */ + VIRTIO_DEVICE_ID_SCSI = 8, /* SCSI */ + VIRTIO_DEVICE_ID_9P = 9, /* 9p console */ + VIRTIO_DEVICE_ID_MAC80211_WLAN = 10, /* Mac80211 wlan */ + VIRTIO_DEVICE_ID_RPROC_SERIAL = 11, /* Remoteproc serial link */ + VIRTIO_DEVICE_ID_CAIF = 12, /* CAIF */ + VIRTIO_DEVICE_ID_MEM_BALLOON = 13, /* Memory balloon */ + VIRTIO_DEVICE_ID_GPU = 16, /* GPU */ + VIRTIO_DEVICE_ID_TIME = 17, /* Timer/clock device */ + VIRTIO_DEVICE_ID_INPUT = 18, /* Input */ + /* virtio 1.1 */ + VIRTIO_DEVICE_ID_SOCKET = 19, /* Socket device */ + VIRTIO_DEVICE_ID_CRYPTO = 20, /* Crypto device */ + VIRTIO_DEVICE_ID_SIG_DIS_MOD = 21, /* Signal Distribution Module */ + VIRTIO_DEVICE_ID_PSTORE = 22, /* Pstore device */ + VIRTIO_DEVICE_ID_IOMMU = 23, /* IOMMU device */ + VIRTIO_DEVICE_ID_MEM = 24, /* Memory device */ + /* virtio 1.2 */ + VIRTIO_DEVICE_ID_AUDIO = 25, /* Audio device */ + VIRTIO_DEVICE_ID_FS = 26, /* File system device */ + VIRTIO_DEVICE_ID_PMEM = 27, /* PMEM device */ + VIRTIO_DEVICE_ID_RPMB = 28, /* RPMB device */ + VIRTIO_DEVICE_ID_MAC80211_HWSIM = 29, /* Mac80211 hwsim wireless simulation device */ + VIRTIO_DEVICE_ID_VIDEO_ENCODER = 30, /* Video encoder device */ + VIRTIO_DEVICE_ID_VIDEO_DECODER = 31, /* Video decoder device */ + VIRTIO_DEVICE_ID_SCMI = 32, /* SCMI device */ + VIRTIO_DEVICE_ID_NITRO_SEC_MOD = 33, /* NitroSecureModule */ + VIRTIO_DEVICE_ID_I2C_ADAPTER = 34, /* I2C adapter */ + VIRTIO_DEVICE_ID_WATCHDOG = 35, /* Watchdog */ + VIRTIO_DEVICE_ID_CAN = 36, /* CAN device */ + VIRTIO_DEVICE_ID_DMABUF = 37, /* Virtio dmabuf */ + VIRTIO_DEVICE_ID_PARAM_SERV = 38, /* Parameter Server */ + VIRTIO_DEVICE_ID_AUDIO_POLICY = 39, /* Audio policy device */ + VIRTIO_DEVICE_ID_BT = 40, /* Bluetooth device */ + VIRTIO_DEVICE_ID_GPIO = 41, /* GPIO device */ + VIRTIO_DEVICE_ID_RDMA = 42, /* RDMA device */ + + VIRTIO_DEVICE_ID_MAX +}; + +enum +{ + VIRTIO_PCI_SUBSYSTEM_DEVICE_ID_NET = 0x1000, /* Network card */ + VIRTIO_PCI_SUBSYSTEM_DEVICE_ID_BLOCK = 0x1001, /* Block device */ + VIRTIO_PCI_SUBSYSTEM_DEVICE_ID_BALLOON = 0x1002, /* Memory ballooning (traditional) */ + VIRTIO_PCI_SUBSYSTEM_DEVICE_ID_CONSOLE = 0x1003, /* Console */ + VIRTIO_PCI_SUBSYSTEM_DEVICE_ID_SCSI = 0x1004, /* SCSI host */ + VIRTIO_PCI_SUBSYSTEM_DEVICE_ID_RNG = 0x1005, /* Entropy source */ + VIRTIO_PCI_SUBSYSTEM_DEVICE_ID_9P = 0x1009, /* 9P transport */ + + VIRTIO_PCI_SUBSYSTEM_DEVICE_ID_MAX +}; + +#endif /* __VIRTIO_IDS_H__ */ diff --git a/components/drivers/virtio/virtio_internal.h b/components/drivers/virtio/virtio_internal.h index 4fc1eb21176d..cf8dff2f9056 100644 --- a/components/drivers/virtio/virtio_internal.h +++ b/components/drivers/virtio/virtio_internal.h @@ -30,20 +30,20 @@ rt_inline rt_bool_t virtio_is_little_endian(struct rt_virtio_device *vdev) } #ifdef __CHECKER__ -#define __force __attribute__((force)) +#define FORCE __attribute__((force)) #else -#define __force +#define FORCE #endif rt_inline rt_uint16_t virtio16_to_cpu(struct rt_virtio_device *vdev, rt_uint16_t val) { if (virtio_is_little_endian(vdev)) { - return rt_le16_to_cpu((__force rt_le16_t)val); + return rt_le16_to_cpu((FORCE rt_le16_t)val); } else { - return rt_be16_to_cpu((__force rt_be16_t)val); + return rt_be16_to_cpu((FORCE rt_be16_t)val); } } @@ -51,11 +51,11 @@ rt_inline rt_uint16_t cpu_to_virtio16(struct rt_virtio_device *vdev, rt_uint16_t { if (virtio_is_little_endian(vdev)) { - return (__force rt_le16_t)rt_cpu_to_le16(val); + return (FORCE rt_le16_t)rt_cpu_to_le16(val); } else { - return (__force rt_be16_t)rt_cpu_to_be16(val); + return (FORCE rt_be16_t)rt_cpu_to_be16(val); } } @@ -63,11 +63,11 @@ rt_inline rt_uint32_t virtio32_to_cpu(struct rt_virtio_device *vdev, rt_uint32_t { if (virtio_is_little_endian(vdev)) { - return rt_le32_to_cpu((__force rt_le32_t)val); + return rt_le32_to_cpu((FORCE rt_le32_t)val); } else { - return rt_be32_to_cpu((__force rt_be32_t)val); + return rt_be32_to_cpu((FORCE rt_be32_t)val); } } @@ -75,11 +75,11 @@ rt_inline rt_uint32_t cpu_to_virtio32(struct rt_virtio_device *vdev, rt_uint32_t { if (virtio_is_little_endian(vdev)) { - return (__force rt_le32_t)rt_cpu_to_le32(val); + return (FORCE rt_le32_t)rt_cpu_to_le32(val); } else { - return (__force rt_be32_t)rt_cpu_to_be32(val); + return (FORCE rt_be32_t)rt_cpu_to_be32(val); } } @@ -87,11 +87,11 @@ rt_inline rt_uint64_t virtio64_to_cpu(struct rt_virtio_device *vdev, rt_uint64_t { if (virtio_is_little_endian(vdev)) { - return rt_le64_to_cpu((__force rt_le64_t)val); + return rt_le64_to_cpu((FORCE rt_le64_t)val); } else { - return rt_be64_to_cpu((__force rt_be64_t)val); + return rt_be64_to_cpu((FORCE rt_be64_t)val); } } @@ -99,14 +99,14 @@ rt_inline rt_uint64_t cpu_to_virtio64(struct rt_virtio_device *vdev, rt_uint64_t { if (virtio_is_little_endian(vdev)) { - return (__force rt_le64_t)rt_cpu_to_le64(val); + return (FORCE rt_le64_t)rt_cpu_to_le64(val); } else { - return (__force rt_be64_t)rt_cpu_to_be64(val); + return (FORCE rt_be64_t)rt_cpu_to_be64(val); } } -#undef __force +#undef FORCE #endif /* __VIRTIO_INTERNAL_H__ */ diff --git a/components/drivers/virtio/virtio_mmio.c b/components/drivers/virtio/virtio_mmio.c index 9c1404a85fd8..e51f6f01fda6 100644 --- a/components/drivers/virtio/virtio_mmio.c +++ b/components/drivers/virtio/virtio_mmio.c @@ -16,10 +16,9 @@ #include +#include #include #include -#include -#include #include #define VIRTIO_MMIO_MAGIC 0x000 /* Magic value */ @@ -66,7 +65,6 @@ struct virtio_mmio { struct rt_virtio_device parent; - struct rt_platform_device *pdev; void *base; int irq; @@ -217,7 +215,7 @@ static rt_err_t virtio_mmio_set_features(struct rt_virtio_device *vdev) if (vio->version == 2 && !(vdev->features & RT_BIT(VIRTIO_F_VERSION_1))) { LOG_E("%s (virtio-mmio) devices (version 2) must provide VIRTIO_F_VERSION_1 feature", - rt_dm_get_dev_name(&vdev->parent)); + rt_dm_dev_get_name(&vdev->parent)); return -RT_EINVAL; } @@ -323,7 +321,7 @@ static struct rt_virtqueue *virtio_mmio_install_vq(struct rt_virtio_device *vdev if (num_max == 0) { - LOG_E("%s.virtqueue[%s] num_max is zero", rt_dm_get_dev_name(&vdev->parent), name); + LOG_E("%s.virtqueue[%s] num_max is zero", rt_dm_dev_get_name(&vdev->parent), name); return RT_NULL; } @@ -331,7 +329,7 @@ static struct rt_virtqueue *virtio_mmio_install_vq(struct rt_virtio_device *vdev if (virtq_nr && virtq_nr > num_max) { LOG_E("%s.virtqueue[%s] queue_num(%d) > num_max(%d)", - rt_dm_get_dev_name(&vdev->parent), name, virtq_nr, num_max); + rt_dm_dev_get_name(&vdev->parent), name, virtq_nr, num_max); return RT_NULL; } @@ -438,7 +436,7 @@ static rt_err_t virtio_mmio_release_vqs(struct rt_virtio_device *vdev) if (!status) { LOG_W("%s.virtqueue[%s] VIRTIO_MMIO_QUEUE_READY = %d", - rt_dm_get_dev_name(&vq->vdev->parent), vq->name, status); + rt_dm_dev_get_name(&vq->vdev->parent), vq->name, status); } } @@ -523,117 +521,100 @@ static const struct rt_virtio_transport virtio_mmio_trans = .reset = virtio_mmio_reset, }; -static rt_err_t virtio_mmio_ofw_init(struct rt_platform_device *pdev, struct virtio_mmio *vio) +static rt_err_t virtio_mmio_probe(struct rt_platform_device *pdev) { rt_err_t err = RT_EOK; - struct rt_ofw_node *np = pdev->parent.ofw_node; - - vio->base = rt_ofw_iomap(np, 0); + rt_uint32_t magic; + struct rt_virtio_device *vdev; + struct rt_device *dev = &pdev->parent; + struct virtio_mmio *vio = rt_calloc(1, sizeof(*vio)); - if (vio->base) + if (!vio) { - vio->irq = rt_ofw_get_irq(np, 0); - - if (vio->irq >= 0) - { - vio->pdev = pdev; - rt_ofw_data(np) = &vio->parent.parent; - } - else - { - err = -RT_ERROR; - } + return -RT_ENOMEM; } - else + + vio->base = rt_dm_dev_iomap(dev, 0); + + if (!vio->base) { err = -RT_EIO; - } - return err; -} + goto _fail; + } -static rt_err_t virtio_mmio_probe(struct rt_platform_device *pdev) -{ - rt_err_t err = RT_EOK; - struct rt_virtio_device *vdev; - struct virtio_mmio *vio = rt_calloc(1, sizeof(*vio)); + vio->irq = rt_dm_dev_get_irq(dev, 0); - do { - rt_uint32_t magic; + if (vio->irq < 0) + { + err = vio->irq; - if (!vio) - { - err = -RT_ENOMEM; - break; - } + goto _fail; + } - err = virtio_mmio_ofw_init(pdev, vio); + rt_dm_dev_bind_fwdata(dev, RT_NULL, &vio->parent.parent); - if (err) - { - break; - } + magic = virtio_mmio_read32(vio, VIRTIO_MMIO_MAGIC); - magic = virtio_mmio_read32(vio, VIRTIO_MMIO_MAGIC); - - /* 0x74726976 (a Little Endian equivalent of the "virt" string). */ - if (magic != ('v' | 'i' << 8 | 'r' << 16 | 't' << 24)) - { - err = -RT_EINVAL; + /* 0x74726976 (a Little Endian equivalent of the "virt" string). */ + if (magic != ('v' | 'i' << 8 | 'r' << 16 | 't' << 24)) + { + err = -RT_EINVAL; - LOG_E("invalid magic: %x", magic); + LOG_E("invalid magic: %x", magic); - break; - } + goto _fail; + } - vio->version = virtio_mmio_read32(vio, VIRTIO_MMIO_VERSION); + vio->version = virtio_mmio_read32(vio, VIRTIO_MMIO_VERSION); - /* Only support version 1~2 */ - if (vio->version < 1 || vio->version > 2) - { - err = -RT_ENOSYS; + /* Only support version 1~2 */ + if (vio->version < 1 || vio->version > 2) + { + err = -RT_ENOSYS; - LOG_E("not support version: %d", vio->version); + LOG_E("not support version: %d", vio->version); - break; - } + goto _fail; + } - vdev = &vio->parent; + vdev = &vio->parent; - vdev->id.device = virtio_mmio_read32(vio, VIRTIO_MMIO_DEVICE_ID); + vdev->id.device = virtio_mmio_read32(vio, VIRTIO_MMIO_DEVICE_ID); - if (vdev->id.device == VIRTIO_DEVICE_ID_INVALID) - { - err = -RT_EEMPTY; - break; - } + if (vdev->id.device == VIRTIO_DEVICE_ID_INVALID) + { + err = -RT_EEMPTY; + goto _fail; + } - vdev->id.vendor = virtio_mmio_read32(vio, VIRTIO_MMIO_VENDOR_ID); + vdev->id.vendor = virtio_mmio_read32(vio, VIRTIO_MMIO_VENDOR_ID); - if (vio->version == 1) - { - virtio_mmio_write32(vio, VIRTIO_MMIO_GUEST_PAGE_SIZE, VIRTIO_MMIO_VIRTQ_PAGE_SZIE); - } + if (vio->version == 1) + { + virtio_mmio_write32(vio, VIRTIO_MMIO_GUEST_PAGE_SIZE, VIRTIO_MMIO_VIRTQ_PAGE_SZIE); + } - rt_spin_lock_init(&vio->spinlock); + rt_spin_lock_init(&vio->spinlock); - vdev->trans = &virtio_mmio_trans; + vdev->trans = &virtio_mmio_trans; - err = rt_virtio_device_register(vdev); + if (!(err = rt_virtio_device_register(vdev))) + { + char name[RT_NAME_MAX]; - if (!err) - { - char name[RT_NAME_MAX]; + rt_sprintf(name, "%s-%s", rt_dm_dev_get_name(&vdev->parent), rt_virtio_device_id_name(vdev)); + rt_hw_interrupt_install(vio->irq, virtio_mmio_isr, vio, name); + } - rt_sprintf(name, "%s-%s", rt_dm_get_dev_name(&vdev->parent), rt_virtio_device_id_name(vdev)); - rt_hw_interrupt_install(vio->irq, virtio_mmio_isr, vio, name); - } - } while (0); + return RT_EOK; - if (err) +_fail: + if (vio->base) { - rt_free(vio); + rt_iounmap(vio->base); } + rt_free(vio); return err; } diff --git a/bsp/rockchip/rk3568/driver/drv_uart.h b/components/drivers/virtio/virtio_pci.c old mode 100644 new mode 100755 similarity index 51% rename from bsp/rockchip/rk3568/driver/drv_uart.h rename to components/drivers/virtio/virtio_pci.c index acc6ac0ea4b9..8f0f3228d30f --- a/bsp/rockchip/rk3568/driver/drv_uart.h +++ b/components/drivers/virtio/virtio_pci.c @@ -5,12 +5,5 @@ * * Change Logs: * Date Author Notes - * 2022-3-08 GuEe-GUI the first version + * 2022-11-07 GuEe-GUI first version */ - -#ifndef __DRV_UART_H__ -#define __DRV_UART_H__ - -int rt_hw_uart_init(void); - -#endif /* __DRV_UART_H__ */ diff --git a/components/drivers/virtio/virtio_pci.h b/components/drivers/virtio/virtio_pci.h new file mode 100755 index 000000000000..58ff771ab582 --- /dev/null +++ b/components/drivers/virtio/virtio_pci.h @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-11-07 GuEe-GUI first version + */ + +#ifndef __VIRTIO_PCI_H__ +#define __VIRTIO_PCI_H__ + +#include + +#define VIRTIO_PCI_CAP_COMMON_CFG 1 /* Common configuration */ +#define VIRTIO_PCI_CAP_NOTIFY_CFG 2 /* Notifications */ +#define VIRTIO_PCI_CAP_ISR_CFG 3 /* ISR Status */ +#define VIRTIO_PCI_CAP_DEVICE_CFG 4 /* Device specific configuration */ +#define VIRTIO_PCI_CAP_PCI_CFG 5 /* PCI configuration access */ +#define VIRTIO_PCI_CAP_SHARED_MEMORY_CFG 8 /* Shared memory region */ +#define VIRTIO_PCI_CAP_VENDOR_CFG 9 /* Vendor-specific data */ + +struct virtio_pci_cap +{ + rt_uint8_t cap_vndr; /* Generic PCI field: PCI_CAP_ID_VNDR */ + rt_uint8_t cap_next; /* Generic PCI field: next ptr. */ + rt_uint8_t cap_len; /* Generic PCI field: capability length */ + rt_uint8_t cfg_type; /* Identifies the structure. */ + rt_uint8_t bar; /* Where to find it. */ + rt_uint8_t id; /* Multiple capabilities of the same type */ + rt_uint8_t padding[2]; /* Pad to full dword. */ + rt_uint32_t offset; /* Offset within bar. */ + rt_uint32_t length; /* Length of the structure, in bytes. */ +}; + +struct virtio_pci_cap64 +{ + struct virtio_pci_cap cap; + rt_uint32_t offset_hi; + rt_uint32_t length_hi; +}; + +struct virtio_pci_common_cfg +{ + /* About the whole device. */ + rt_uint32_t device_feature_select; /* Read-write */ + rt_uint32_t device_feature; /* Read-only for driver */ + rt_uint32_t driver_feature_select; /* Read-write */ + rt_uint32_t driver_feature; /* Read-write */ + rt_uint16_t config_msix_vector; /* Read-write */ + rt_uint16_t num_queues; /* Read-only for driver */ + rt_uint8_t device_status; /* Read-write */ + rt_uint8_t config_generation; /* Read-only for driver */ + + /* About a specific virtqueue. */ + rt_uint16_t queue_select; /* Read-write */ + rt_uint16_t queue_size; /* Read-write */ + rt_uint16_t queue_msix_vector; /* Read-write */ + rt_uint16_t queue_enable; /* Read-write */ + rt_uint16_t queue_notify_off; /* Read-only for driver */ + rt_uint64_t queue_desc; /* Read-write */ + rt_uint64_t queue_driver; /* Read-write */ + rt_uint64_t queue_device; /* Read-write */ + rt_uint16_t queue_notify_data; /* Read-only for driver */ + rt_uint16_t queue_reset; /* Read-write */ +}; + +struct virtio_pci_notify_cap +{ + struct virtio_pci_cap cap; + rt_uint32_t notify_off_multiplier; /* Multiplier for queue_notify_off. */ +}; + +struct virtio_pci_vndr_data +{ + rt_uint8_t cap_vndr; /* Generic PCI field: PCI_CAP_ID_VNDR */ + rt_uint8_t cap_next; /* Generic PCI field: next ptr. */ + rt_uint8_t cap_len; /* Generic PCI field: capability length */ + rt_uint8_t cfg_type; /* Identifies the structure. */ + rt_uint16_t vendor_id; /* Identifies the vendor-specific format. */ + /* For Vendor Definition */ + /* Pads structure to a multiple of 4 bytes */ + /* Reads must not have side effects */ +}; + +struct virtio_pci_cfg_cap +{ + struct virtio_pci_cap cap; + rt_uint8_t pci_cfg_data[4]; /* Data for BAR access. */ +}; + + +#define VIRTIO_PCI_DEVICE_FEATURES 0x00 /* Flags representing features the device supports */ +#define VIRTIO_PCI_DRIVER_FEATURES 0x04 /* Device features understood and activated by the driver */ +#define VIRTIO_PCI_QUEUE_PFN 0x08 /* Guest physical page number of the virtual queue */ +#define VIRTIO_PCI_QUEUE_NUM 0x0c /* Virtual queue size */ +#define VIRTIO_PCI_QUEUE_SEL 0x0e /* Virtual queue index */ +#define VIRTIO_PCI_QUEUE_NOTIFY 0x10 /* Queue notifier */ +#define VIRTIO_PCI_STATUS 0x12 /* Device status */ +#define VIRTIO_PCI_INTERRUPT_STATUS 0x13 /* Interrupt status */ + +#endif /* __VIRTIO_PCI_H__ */ diff --git a/components/drivers/virtio/virtio_pci_legacy.c b/components/drivers/virtio/virtio_pci_legacy.c new file mode 100755 index 000000000000..7d39eff3d8d5 --- /dev/null +++ b/components/drivers/virtio/virtio_pci_legacy.c @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-11-07 GuEe-GUI first version + */ + +#include "virtio_pci_legacy.h" + +static rt_err_t virtio_pci_legacy_probe(struct rt_pci_device *pdev) +{ + rt_err_t err = RT_EOK; + + return err; +} + +static struct rt_pci_device_id virtio_pci_legacy_ids[VIRTIO_PCI_DEVICE_END - VIRTIO_PCI_DEVICE_START] = +{ +}; + +static struct rt_pci_driver virtio_pci_legacy_driver = +{ + .name = "virtio-pci-legacy", + + .ids = virtio_pci_legacy_ids, + .probe = virtio_pci_legacy_probe, +}; + +static int virtio_pci_legacy_driver_register(void) +{ + rt_uint16_t id = VIRTIO_PCI_DEVICE_START; + + for (int i = 0; i < RT_ARRAY_SIZE(virtio_pci_legacy_ids); ++i, ++id) + { + virtio_pci_legacy_ids[i] = (struct rt_pci_device_id) + { + RT_PCI_DEVICE_ID(PCI_VENDOR_ID_REDHAT_QUMRANET, id) + }; + } + + rt_pci_driver_register(&virtio_pci_legacy_driver); + + return 0; +} +INIT_DEVICE_EXPORT(virtio_pci_legacy_driver_register); diff --git a/components/drivers/virtio/virtio_pci_legacy.h b/components/drivers/virtio/virtio_pci_legacy.h new file mode 100755 index 000000000000..c5ad82f7ddbc --- /dev/null +++ b/components/drivers/virtio/virtio_pci_legacy.h @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-11-07 GuEe-GUI first version + */ + +#ifndef __VIRTIO_PCI_LEGACY_H__ +#define __VIRTIO_PCI_LEGACY_H__ + +#include "virtio_pci.h" + +/* PCI device ID in the range 0x1000 to 0x103f */ +#define VIRTIO_PCI_DEVICE_START 0x1000 +#define VIRTIO_PCI_DEVICE_END 0x103f + +#endif /* __VIRTIO_PCI_LEGACY_H__ */ diff --git a/components/drivers/virtio/virtio_pci_modern.c b/components/drivers/virtio/virtio_pci_modern.c new file mode 100755 index 000000000000..726b535d4033 --- /dev/null +++ b/components/drivers/virtio/virtio_pci_modern.c @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-11-07 GuEe-GUI first version + */ + +#include "virtio_pci_modern.h" + +static rt_err_t virtio_pci_modern_probe(struct rt_pci_device *pdev) +{ + rt_err_t err = RT_EOK; + + return err; +} + +static struct rt_pci_device_id virtio_pci_modern_ids[VIRTIO_PCI_DEVICE_END - VIRTIO_PCI_DEVICE_START] = +{ +}; + +static struct rt_pci_driver virtio_pci_modern_driver = +{ + .name = "virtio-pci-modern", + + .ids = virtio_pci_modern_ids, + .probe = virtio_pci_modern_probe, +}; + +static int virtio_pci_modern_driver_register(void) +{ + rt_uint16_t id = VIRTIO_PCI_DEVICE_START; + + for (int i = 0; i < RT_ARRAY_SIZE(virtio_pci_modern_ids); ++i, ++id) + { + virtio_pci_modern_ids[i] = (struct rt_pci_device_id) + { + RT_PCI_DEVICE_ID(PCI_VENDOR_ID_REDHAT_QUMRANET, id), + }; + } + + rt_pci_driver_register(&virtio_pci_modern_driver); + + return 0; +} +INIT_DEVICE_EXPORT(virtio_pci_modern_driver_register); diff --git a/components/drivers/virtio/virtio_pci_modern.h b/components/drivers/virtio/virtio_pci_modern.h new file mode 100755 index 000000000000..4db65df38554 --- /dev/null +++ b/components/drivers/virtio/virtio_pci_modern.h @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-11-07 GuEe-GUI first version + */ + +#ifndef __VIRTIO_PCI_MODERN_H__ +#define __VIRTIO_PCI_MODERN_H__ + +#include "virtio_pci.h" + +/* PCI device ID in the range 0x1040 to 0x107f */ +#define VIRTIO_PCI_DEVICE_START 0x1040 +#define VIRTIO_PCI_DEVICE_END 0x107f + +#endif /* __VIRTIO_PCI_MODERN_H__ */ diff --git a/components/drivers/virtio/virtio_queue.c b/components/drivers/virtio/virtio_queue.c index c9035457ce2e..2a749bd107e9 100755 --- a/components/drivers/virtio/virtio_queue.c +++ b/components/drivers/virtio/virtio_queue.c @@ -151,7 +151,7 @@ static rt_err_t vq_split_add_buf(struct rt_virtqueue *vq, void *dma_buf, rt_size if (!vq->num_free) { LOG_D("%s.virtqueue[%s(%d)] add buffer.len = %d fail", - rt_dm_get_dev_name(&vq->vdev->parent), vq->name, vq->index); + rt_dm_dev_get_name(&vq->vdev->parent), vq->name, vq->index); /* Recvice buffer NOW! */ if (is_out) @@ -190,7 +190,7 @@ static rt_err_t vq_split_add_buf(struct rt_virtqueue *vq, void *dma_buf, rt_size vq->num_free--; LOG_D("%s.virtqueue[%s(%d)] add buffer(%p, size = %d) head = %d", - rt_dm_get_dev_name(&vq->vdev->parent), vq->name, vq->index, dma_buf, size, head); + rt_dm_dev_get_name(&vq->vdev->parent), vq->name, vq->index, dma_buf, size, head); if (vq->num_added == RT_UINT16_MAX) { @@ -211,7 +211,7 @@ static rt_bool_t vq_split_submit(struct rt_virtqueue *vq) head = (head - vq->num_added) % virtq_num; LOG_D("%s.virtqueue[%s(%d)] submit head = %d, num_added = %d, idx = %d", - rt_dm_get_dev_name(&vq->vdev->parent), vq->name, vq->index, head, vq->num_added, virtq->avail->idx + 1); + rt_dm_dev_get_name(&vq->vdev->parent), vq->name, vq->index, head, vq->num_added, virtq->avail->idx + 1); /* Reset list info */ vq->num_added = 0; @@ -292,7 +292,7 @@ static void *vq_split_read_buf(struct rt_virtqueue *vq, rt_size_t *out_len) if (!vq_split_pending(vq)) { LOG_D("%s.virtqueue[%s(%d)] read buffer empty", - rt_dm_get_dev_name(&vq->vdev->parent), vq->name, vq->index); + rt_dm_dev_get_name(&vq->vdev->parent), vq->name, vq->index); return RT_NULL; } @@ -307,7 +307,7 @@ static void *vq_split_read_buf(struct rt_virtqueue *vq, rt_size_t *out_len) buf = vq->buffer_hash[idx]; LOG_D("%s.virtqueue[%s(%d)] read head = %d, buffer(%p, size = %d)", - rt_dm_get_dev_name(&vq->vdev->parent), vq->name, vq->index, idx, buf, *out_len); + rt_dm_dev_get_name(&vq->vdev->parent), vq->name, vq->index, idx, buf, *out_len); do { idx = next; @@ -507,7 +507,7 @@ void rt_virtqueue_isr(int irq, struct rt_virtqueue *vq) if (!virtqueue_pending(vq)) { LOG_D("%s.virtqueue[%s(%d)] no buffer pending in %s", - rt_dm_get_dev_name(&vq->vdev->parent), vq->name, vq->index, "isr"); + rt_dm_dev_get_name(&vq->vdev->parent), vq->name, vq->index, "isr"); return; } diff --git a/components/drivers/watchdog/Kconfig b/components/drivers/watchdog/Kconfig new file mode 100644 index 000000000000..58d971082979 --- /dev/null +++ b/components/drivers/watchdog/Kconfig @@ -0,0 +1,28 @@ +menuconfig RT_USING_WDT + bool "Using Watch Dog device drivers" + default n + +config RT_WDT_BCM2835 + bool "Broadcom BCM2835 hardware watchdog" + depends on RT_USING_DM + depends on RT_USING_WDT + default n + +config RT_WDT_DW + bool "Synopsys DesignWare watchdog" + depends on RT_USING_DM + depends on RT_USING_WDT + select RT_USING_RESET + default n + +config RT_WDT_I6300ESB + bool "Intel 6300ESB Timer/Watchdog" + depends on RT_USING_DM + depends on RT_USING_WDT + select RT_USING_PCI + +config RT_WDT_RK8XX + bool "Rockchip RK806 watchdog" + depends on RT_USING_DM + depends on RT_USING_WDT + depends on RT_MFD_RK8XX diff --git a/components/drivers/watchdog/SConscript b/components/drivers/watchdog/SConscript index 38934d3c4175..0f4f8b7c4880 100644 --- a/components/drivers/watchdog/SConscript +++ b/components/drivers/watchdog/SConscript @@ -1,8 +1,27 @@ from building import * -cwd = GetCurrentDir() -src = Glob('*.c') +group = [] + +if not GetDepend(['RT_USING_WDT']): + Return('group') + +cwd = GetCurrentDir() CPPPATH = [cwd + '/../include'] -group = DefineGroup('DeviceDrivers', src, depend = ['RT_USING_WDT'], CPPPATH = CPPPATH) + +src = ['watchdog.c'] + +if GetDepend(['RT_WDT_BCM2835']): + src += ['watchdog-bcm2835.c'] + +if GetDepend(['RT_WDT_DW']): + src += ['watchdog-dw.c'] + +if GetDepend(['RT_WDT_I6300ESB']): + src += ['watchdog-i6300esb.c'] + +if GetDepend(['RT_WDT_RK8XX']): + src += ['watchdog-rk8xx.c'] + +group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) Return('group') diff --git a/components/drivers/watchdog/watchdog-bcm2835.c b/components/drivers/watchdog/watchdog-bcm2835.c new file mode 100644 index 000000000000..ce5e2000cc43 --- /dev/null +++ b/components/drivers/watchdog/watchdog-bcm2835.c @@ -0,0 +1,241 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-3-08 GuEe-GUI the first version + */ + +#include +#include +#include + +#include "../mfd/bcm2835-pm.h" + +#define PM_RSTC 0x1c +#define PM_RSTS 0x20 +#define PM_WDOG 0x24 + +#define PM_PASSWORD 0x5a000000 + +#define PM_WDOG_TIME_SET 0x000fffff +#define PM_RSTC_WRCFG_CLR 0xffffffcf +#define PM_RSTS_HADWRH_SET 0x00000040 +#define PM_RSTC_WRCFG_SET 0x00000030 +#define PM_RSTC_WRCFG_FULL_RESET 0x00000020 +#define PM_RSTC_RESET 0x00000102 + +/* + * The Raspberry Pi firmware uses the RSTS register to know which partition + * to boot from. The partition value is spread into bits 0, 2, 4, 6, 8, 10. + * Partition 63 is a special partition used by the firmware to indicate halt. + */ +#define PM_RSTS_RASPBERRYPI_HALT 0x555 + +#define SECS_TO_WDOG_TICKS(x) ((x) << 16) +#define WDOG_TICKS_TO_SECS(x) ((x) >> 16) + +#define USEC_PER_SEC 1000000L + +struct bcm2835_wdt +{ + rt_watchdog_t parent; + + void *base; + rt_uint32_t timeout; + struct rt_spinlock lock; +}; + +#define raw_to_bcm2835_wdt(raw) rt_container_of(raw, struct bcm2835_wdt, parent) + +static struct bcm2835_wdt *bcm2835_power_off_wdt; + +static void bcm2835_wdt_start(struct bcm2835_wdt *bcm2835_wdt) +{ + rt_uint32_t cur; + rt_ubase_t level = rt_spin_lock_irqsave(&bcm2835_wdt->lock); + + HWREG32(bcm2835_wdt->base + PM_WDOG) = PM_PASSWORD | (SECS_TO_WDOG_TICKS(bcm2835_wdt->timeout) & PM_WDOG_TIME_SET); + cur = HWREG32(bcm2835_wdt->base + PM_RSTC); + HWREG32(bcm2835_wdt->base + PM_RSTC) = PM_PASSWORD | (cur & PM_RSTC_WRCFG_CLR) | PM_RSTC_WRCFG_FULL_RESET; + + rt_spin_unlock_irqrestore(&bcm2835_wdt->lock, level); +} + +static void bcm2835_wdt_stop(struct bcm2835_wdt *bcm2835_wdt) +{ + HWREG32(bcm2835_wdt->base + PM_RSTC) = PM_PASSWORD | PM_RSTC_RESET; +} + +static rt_uint32_t bcm2835_wdt_get_timeleft(struct bcm2835_wdt *bcm2835_wdt) +{ + rt_uint32_t value = HWREG32(bcm2835_wdt->base + PM_WDOG); + + return WDOG_TICKS_TO_SECS(value & PM_WDOG_TIME_SET); +} + +static void bcm2835_restart(struct bcm2835_wdt *bcm2835_wdt) +{ + rt_uint32_t val; + + /* Use a timeout of 10 ticks (~150us) */ + HWREG32(bcm2835_wdt->base + PM_WDOG) = 10 | PM_PASSWORD; + val = HWREG32(bcm2835_wdt->base + PM_RSTC); + val &= PM_RSTC_WRCFG_CLR; + val |= PM_PASSWORD | PM_RSTC_WRCFG_FULL_RESET; + HWREG32(bcm2835_wdt->base + PM_RSTC) = val; + + /* No sleeping, possibly atomic. */ + rt_thread_mdelay(1); +} + +static rt_err_t bcm2835_wdt_init(rt_watchdog_t *wdt) +{ + return RT_EOK; +} + +static rt_err_t bcm2835_wdt_control(rt_watchdog_t *wdt, int cmd, void *args) +{ + rt_err_t status = RT_EOK; + struct bcm2835_wdt *bcm2835_wdt = raw_to_bcm2835_wdt(wdt); + + switch (cmd) + { + case RT_DEVICE_CTRL_WDT_GET_TIMEOUT: + *(rt_uint32_t *)args = bcm2835_wdt->timeout / USEC_PER_SEC; + break; + + case RT_DEVICE_CTRL_WDT_SET_TIMEOUT: + bcm2835_wdt->timeout = *(rt_uint32_t *)args * USEC_PER_SEC; + break; + + case RT_DEVICE_CTRL_WDT_GET_TIMELEFT: + *(rt_uint32_t *)args = bcm2835_wdt_get_timeleft(bcm2835_wdt) / USEC_PER_SEC; + break; + + case RT_DEVICE_CTRL_WDT_KEEPALIVE: + bcm2835_wdt->timeout = 0; + break; + + case RT_DEVICE_CTRL_WDT_START: + bcm2835_wdt_start(bcm2835_wdt); + break; + + case RT_DEVICE_CTRL_WDT_STOP: + bcm2835_wdt_stop(bcm2835_wdt); + break; + + default: + status = -RT_EINVAL; + } + + return status; +} + +static const struct rt_watchdog_ops bcm2835_wdt_ops = +{ + .init = bcm2835_wdt_init, + .control = bcm2835_wdt_control, +}; + +static void bcm2835_wdt_power_off(void) +{ + rt_uint32_t val; + struct bcm2835_wdt *bcm2835_wdt = bcm2835_power_off_wdt; + + /* + * We set the watchdog hard reset bit here to distinguish this reset from + * the normal (full) reset. bootcode.bin will not reboot after a hard reset. + */ + val = HWREG32(bcm2835_wdt->base + PM_RSTS); + val |= PM_PASSWORD | PM_RSTS_RASPBERRYPI_HALT; + HWREG32(bcm2835_wdt->base + PM_RSTS) = val; + + /* Continue with normal reset mechanism */ + bcm2835_restart(bcm2835_wdt); +} + +static void bcm2835_wdt_power_reboot(void) +{ + struct bcm2835_wdt *bcm2835_wdt = bcm2835_power_off_wdt; + + /* Continue with normal reset mechanism */ + bcm2835_restart(bcm2835_wdt); +} + +static rt_err_t bcm2835_wdt_probe(struct rt_platform_device *pdev) +{ + const char *dev_name; + struct bcm2835_pm *pm = pdev->priv; + struct rt_ofw_node *np = pm->ofw_node; + struct bcm2835_wdt *bcm2835_wdt = rt_calloc(1, sizeof(*bcm2835_wdt)); + + if (!bcm2835_wdt) + { + return -RT_ENOMEM; + } + + rt_spin_lock_init(&bcm2835_wdt->lock); + + bcm2835_power_off_wdt = bcm2835_wdt; + +#ifdef RT_USING_OFW + if (rt_ofw_prop_read_bool(np, "system-power-controller")) + { + if (!rt_dm_machine_shutdown) + { + rt_dm_machine_shutdown = bcm2835_wdt_power_off; + } + } +#endif /* RT_USING_OFW */ + + if (!rt_dm_machine_reset) + { + rt_dm_machine_reset = bcm2835_wdt_power_reboot; + } + + pdev->parent.user_data = bcm2835_wdt; + + bcm2835_wdt->parent.ops = &bcm2835_wdt_ops; + + rt_dm_dev_set_name_auto(&bcm2835_wdt->parent.parent, "wdt"); + dev_name = rt_dm_dev_get_name(&bcm2835_wdt->parent.parent); + rt_hw_watchdog_register(&bcm2835_wdt->parent, dev_name, 0, bcm2835_wdt); + + return RT_EOK; +} + +static rt_err_t bcm2835_wdt_remove(struct rt_platform_device *pdev) +{ + struct bcm2835_wdt *bcm2835_wdt = pdev->parent.user_data; + + bcm2835_wdt_stop(bcm2835_wdt); + + rt_device_unregister(&bcm2835_wdt->parent.parent); + + if (rt_dm_machine_shutdown == bcm2835_wdt_power_off) + { + rt_dm_machine_shutdown = RT_NULL; + } + + if (rt_dm_machine_reset == bcm2835_wdt_power_reboot) + { + rt_dm_machine_reset = RT_NULL; + } + + bcm2835_power_off_wdt = RT_NULL; + + rt_free(bcm2835_wdt); + + return RT_EOK; +} + +static struct rt_platform_driver bcm2835_wdt_driver = +{ + .name = "bcm2835-wdt", + .probe = bcm2835_wdt_probe, + .remove = bcm2835_wdt_remove, +}; +RT_PLATFORM_DRIVER_EXPORT(bcm2835_wdt_driver); diff --git a/components/drivers/watchdog/watchdog-dw.c b/components/drivers/watchdog/watchdog-dw.c new file mode 100644 index 000000000000..d6cb6141a9f9 --- /dev/null +++ b/components/drivers/watchdog/watchdog-dw.c @@ -0,0 +1,601 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-3-08 GuEe-GUI the first version + */ + +#include +#include +#include + +#define WDOG_CONTROL_REG_OFFSET 0x00 +#define WDOG_CONTROL_REG_WDT_EN_MASK 0x01 +#define WDOG_CONTROL_REG_RESP_MODE_MASK 0x02 +#define WDOG_TIMEOUT_RANGE_REG_OFFSET 0x04 +#define WDOG_TIMEOUT_RANGE_TOPINIT_SHIFT 4 +#define WDOG_CURRENT_COUNT_REG_OFFSET 0x08 +#define WDOG_COUNTER_RESTART_REG_OFFSET 0x0c +#define WDOG_COUNTER_RESTART_KICK_VALUE 0x76 +#define WDOG_INTERRUPT_STATUS_REG_OFFSET 0x10 +#define WDOG_INTERRUPT_CLEAR_REG_OFFSET 0x14 +#define WDOG_COMP_PARAMS_5_REG_OFFSET 0xe4 +#define WDOG_COMP_PARAMS_4_REG_OFFSET 0xe8 +#define WDOG_COMP_PARAMS_3_REG_OFFSET 0xec +#define WDOG_COMP_PARAMS_2_REG_OFFSET 0xf0 +#define WDOG_COMP_PARAMS_1_REG_OFFSET 0xf4 +#define WDOG_COMP_PARAMS_1_USE_FIX_TOP (1 << 6) +#define WDOG_COMP_VERSION_REG_OFFSET 0xf8 +#define WDOG_COMP_TYPE_REG_OFFSET 0xfc + +/* There are sixteen TOPs (timeout periods) that can be set in the watchdog. */ +#define DW_WDT_NUM_TOPS 16 +#define DW_WDT_FIX_TOP(idx) (1U << (16 + idx)) +#define DW_WDT_DEFAULT_SECONDS 30 + +#define MSEC_PER_SEC 1000L + +enum dw_wdt_rmod +{ + DW_WDT_RMOD_RESET = 1, + DW_WDT_RMOD_IRQ +}; + +struct dw_wdt_timeout +{ + rt_uint32_t top_val; + rt_uint32_t sec; + rt_uint32_t msec; +}; + +struct dw_wdt +{ + rt_watchdog_t parent; + + void *base; + int irq; + struct rt_clk *clk; + struct rt_reset_control *rstc; + + rt_ubase_t rate; + enum dw_wdt_rmod rmod; + struct dw_wdt_timeout timeouts[DW_WDT_NUM_TOPS]; + /* Save/Restore */ + rt_uint32_t user; + rt_uint32_t timeout; + rt_uint32_t pretimeout; + rt_uint32_t max_hw_heartbeat_ms; + + struct rt_event event; +}; + +#define raw_to_dw_wdt(raw) rt_container_of(raw, struct dw_wdt, parent) + +static const rt_uint32_t dw_wdt_fix_tops[DW_WDT_NUM_TOPS] = +{ + DW_WDT_FIX_TOP(0), DW_WDT_FIX_TOP(1), DW_WDT_FIX_TOP(2), DW_WDT_FIX_TOP(3), + DW_WDT_FIX_TOP(4), DW_WDT_FIX_TOP(5), DW_WDT_FIX_TOP(6), DW_WDT_FIX_TOP(7), + DW_WDT_FIX_TOP(8), DW_WDT_FIX_TOP(9), DW_WDT_FIX_TOP(10), DW_WDT_FIX_TOP(11), + DW_WDT_FIX_TOP(12), DW_WDT_FIX_TOP(13), DW_WDT_FIX_TOP(14), DW_WDT_FIX_TOP(15) +}; + +rt_inline int dw_wdt_is_enabled(struct dw_wdt *dw_wdt) +{ + return HWREG32(dw_wdt->base + WDOG_CONTROL_REG_OFFSET) & WDOG_CONTROL_REG_WDT_EN_MASK; +} + +static void dw_wdt_update_mode(struct dw_wdt *dw_wdt, enum dw_wdt_rmod rmod) +{ + rt_uint32_t val = HWREG32(dw_wdt->base + WDOG_CONTROL_REG_OFFSET); + + if (rmod == DW_WDT_RMOD_IRQ) + { + val |= WDOG_CONTROL_REG_RESP_MODE_MASK; + } + else + { + val &= ~WDOG_CONTROL_REG_RESP_MODE_MASK; + } + + HWREG32(dw_wdt->base + WDOG_CONTROL_REG_OFFSET) = val; + + dw_wdt->rmod = rmod; +} + +static rt_uint32_t dw_wdt_find_best_top(struct dw_wdt *dw_wdt, rt_uint32_t timeout, rt_uint32_t *top_val) +{ + int idx; + + /* + * Find a TOP with timeout greater or equal to the requested number. Note + * we'll select a TOP with maximum timeout if the requested timeout couldn't + * be reached. + */ + for (idx = 0; idx < DW_WDT_NUM_TOPS; ++idx) + { + if (dw_wdt->timeouts[idx].sec >= timeout) + { + break; + } + } + + if (idx == DW_WDT_NUM_TOPS) + { + --idx; + } + + *top_val = dw_wdt->timeouts[idx].top_val; + + return dw_wdt->timeouts[idx].sec; +} + +static rt_uint32_t dw_wdt_get_max_timeout_ms(struct dw_wdt *dw_wdt) +{ + rt_uint64_t msec; + struct dw_wdt_timeout *timeout = &dw_wdt->timeouts[DW_WDT_NUM_TOPS - 1]; + + msec = (rt_uint64_t)timeout->sec * MSEC_PER_SEC + timeout->msec; + + return msec < RT_UINT32_MAX ? msec : RT_UINT32_MAX; +} + +static rt_uint32_t dw_wdt_get_timeout(struct dw_wdt *dw_wdt) +{ + int idx; + int top_val = HWREG32(dw_wdt->base + WDOG_TIMEOUT_RANGE_REG_OFFSET) & 0xF; + + for (idx = 0; idx < DW_WDT_NUM_TOPS; ++idx) + { + if (dw_wdt->timeouts[idx].top_val == top_val) + { + break; + } + } + + /* + * In IRQ mode due to the two stages counter, the actual timeout is twice + * greater than the TOP setting. + */ + return dw_wdt->timeouts[idx].sec * dw_wdt->rmod; +} + +static int dw_wdt_keep_alive(struct dw_wdt *dw_wdt) +{ + HWREG32(dw_wdt->base + WDOG_COUNTER_RESTART_REG_OFFSET) = WDOG_COUNTER_RESTART_KICK_VALUE; + + return 0; +} + +static void dw_wdt_set_timeout(struct dw_wdt *dw_wdt, rt_uint32_t top_s) +{ + rt_uint32_t top_val; + rt_uint32_t timeout; + + /* + * Note IRQ mode being enabled means having a non-zero pre-timeout setup. + * In this case we try to find a TOP as close to the half of the requested + * timeout as possible since DW Watchdog IRQ mode is designed in two stages + * way - first timeout rises the pre-timeout interrupt, second timeout + * performs the system reset. So basically the effective watchdog-caused + * reset happens after two watchdog TOPs elapsed. + */ + timeout = dw_wdt_find_best_top(dw_wdt, RT_DIV_ROUND_UP(top_s, dw_wdt->rmod), &top_val); + + if (dw_wdt->rmod == DW_WDT_RMOD_IRQ) + { + dw_wdt->pretimeout = timeout; + } + else + { + dw_wdt->pretimeout = 0; + } + + /* + * Set the new value in the watchdog. Some versions of dw_wdt have have + * TOPINIT in the TIMEOUT_RANGE register (as per CP_WDT_DUAL_TOP in + * WDT_COMP_PARAMS_1). On those we effectively get a pat of the watchdog + * right here. + */ + HWREG32(dw_wdt->base + WDOG_TIMEOUT_RANGE_REG_OFFSET) = top_val | top_val << WDOG_TIMEOUT_RANGE_TOPINIT_SHIFT; + + dw_wdt_keep_alive(dw_wdt); + + /* + * In case users set bigger timeout value than HW can support, + * kernel(watchdog_dev.c) helps to feed watchdog before wdd->max_hw_heartbeat_ms + */ + if (top_s * 1000 <= dw_wdt->max_hw_heartbeat_ms) + { + dw_wdt->timeout = timeout * dw_wdt->rmod; + } + else + { + dw_wdt->timeout = top_s; + } +} + +static void dw_wdt_set_pretimeout(struct dw_wdt *dw_wdt, rt_uint32_t req) +{ + /* + * We ignore actual value of the timeout passed from user-space using it as + * a flag whether the pretimeout functionality is intended to be activated. + */ + dw_wdt_update_mode(dw_wdt, req ? DW_WDT_RMOD_IRQ : DW_WDT_RMOD_RESET); + dw_wdt_set_timeout(dw_wdt, dw_wdt->timeout); +} + +static void dw_wdt_arm_system_reset(struct dw_wdt *dw_wdt) +{ + rt_uint32_t val = HWREG32(dw_wdt->base + WDOG_CONTROL_REG_OFFSET); + + /* Disable/enable interrupt mode depending on the RMOD flag. */ + if (dw_wdt->rmod == DW_WDT_RMOD_IRQ) + { + val |= WDOG_CONTROL_REG_RESP_MODE_MASK; + } + else + { + val &= ~WDOG_CONTROL_REG_RESP_MODE_MASK; + } + + /* Enable watchdog. */ + HWREG32(dw_wdt->base + WDOG_CONTROL_REG_OFFSET) = val | WDOG_CONTROL_REG_WDT_EN_MASK; +} + +static int dw_wdt_start(struct dw_wdt *dw_wdt) +{ + rt_clk_enable(dw_wdt->clk); + + dw_wdt_set_timeout(dw_wdt, dw_wdt->timeout); + dw_wdt_keep_alive(dw_wdt); + dw_wdt_arm_system_reset(dw_wdt); + + return 0; +} + +static int dw_wdt_stop(struct dw_wdt *dw_wdt) +{ + /* + * The DesignWare watchdog cannot be stopped once it has been started so we + * do not implement a stop function. The watchdog core will continue to send + * heartbeat requests after the watchdog device has been closed. + */ + rt_clk_disable(dw_wdt->clk); + + rt_reset_control_assert(dw_wdt->rstc); + rt_reset_control_deassert(dw_wdt->rstc); + + return 0; +} + +static rt_uint32_t dw_wdt_get_timeleft(struct dw_wdt *dw_wdt) +{ + rt_uint32_t val, sec; + + val = HWREG32(dw_wdt->base + WDOG_CURRENT_COUNT_REG_OFFSET); + sec = val / dw_wdt->rate; + + if (dw_wdt->rmod == DW_WDT_RMOD_IRQ) + { + val = HWREG32(dw_wdt->base + WDOG_INTERRUPT_STATUS_REG_OFFSET); + + if (!val) + { + sec += dw_wdt->pretimeout; + } + } + + return sec; +} + +static rt_err_t dw_wdt_init_timeouts(struct dw_wdt *dw_wdt) +{ + int val, tidx; + rt_uint64_t msec; + struct dw_wdt_timeout tout, *dst; + const rt_uint32_t *tops = dw_wdt_fix_tops; + + /* + * Convert the specified TOPs into an array of watchdog timeouts. We walk + * over the passed TOPs array and calculate corresponding timeouts in + * seconds and milliseconds. The milliseconds granularity is needed to + * distinguish the TOPs with very close timeouts and to set the watchdog max + * heartbeat setting further. + */ + for (val = 0; val < DW_WDT_NUM_TOPS; ++val) + { + tout.top_val = val; + tout.sec = tops[val] / dw_wdt->rate; + msec = (rt_uint64_t)tops[val] * MSEC_PER_SEC; + rt_do_div(msec, dw_wdt->rate); + tout.msec = msec - ((rt_uint64_t)tout.sec * MSEC_PER_SEC); + + /* + * Find a suitable place for the current TOP in the timeouts array so + * that the list is remained in the ascending order. + */ + for (tidx = 0; tidx < val; ++tidx) + { + dst = &dw_wdt->timeouts[tidx]; + + if (tout.sec > dst->sec || (tout.sec == dst->sec && tout.msec >= dst->msec)) + { + continue; + } + else + { + struct dw_wdt_timeout tmp = *dst; + + *dst = tout; + tout = tmp; + } + } + + dw_wdt->timeouts[val] = tout; + } + + if (!dw_wdt->timeouts[DW_WDT_NUM_TOPS - 1].sec) + { + return -RT_ENOSYS; + } + + return RT_EOK; +} + +static rt_err_t dw_wdt_init(rt_watchdog_t *wdt) +{ + rt_err_t status = RT_EOK; + struct dw_wdt *dw_wdt = raw_to_dw_wdt(wdt); + + /* Enable normal reset without pre-timeout by default. */ + dw_wdt_update_mode(dw_wdt, DW_WDT_RMOD_RESET); + + if (dw_wdt_init_timeouts(dw_wdt)) + { + return -RT_ERROR; + } + + dw_wdt->max_hw_heartbeat_ms = dw_wdt_get_max_timeout_ms(dw_wdt); + + /* + * If the watchdog is already running, use its already configured timeout. + * Otherwise use the default or the value provided through devicetree. + */ + if (dw_wdt_is_enabled(dw_wdt)) + { + dw_wdt->timeout = dw_wdt_get_timeout(dw_wdt); + } + else + { + dw_wdt->timeout = DW_WDT_DEFAULT_SECONDS; + } + + return status; +} + +static rt_err_t dw_wdt_control(rt_watchdog_t *wdt, int cmd, void *args) +{ + rt_err_t status = RT_EOK; + struct dw_wdt *dw_wdt = raw_to_dw_wdt(wdt); + + switch (cmd) + { + case RT_DEVICE_CTRL_WDT_GET_TIMEOUT: + *(rt_uint32_t *)args = dw_wdt_get_timeout(dw_wdt); + break; + + case RT_DEVICE_CTRL_WDT_SET_TIMEOUT: + dw_wdt_set_timeout(dw_wdt, *(rt_uint32_t *)args); + break; + + case RT_DEVICE_CTRL_WDT_GET_TIMELEFT: + *(rt_uint32_t *)args = dw_wdt_get_timeleft(dw_wdt); + break; + + case RT_DEVICE_CTRL_WDT_KEEPALIVE: + dw_wdt_set_pretimeout(dw_wdt, dw_wdt->pretimeout); + dw_wdt_keep_alive(dw_wdt); + break; + + case RT_DEVICE_CTRL_WDT_START: + dw_wdt_start(dw_wdt); + dw_wdt->user++; + break; + + case RT_DEVICE_CTRL_WDT_STOP: + dw_wdt_stop(dw_wdt); + dw_wdt->user--; + break; + + default: + status = -RT_EINVAL; + } + + return status; +} + +static const struct rt_watchdog_ops dw_wdt_ops = +{ + .init = dw_wdt_init, + .control = dw_wdt_control, +}; + +#ifdef RT_USING_PM +static int dw_wdt_pm_suspend(const struct rt_device *device, rt_uint8_t mode) +{ + rt_watchdog_t *wdt = rt_container_of(device, rt_watchdog_t, parent); + struct dw_wdt *dw_wdt = raw_to_dw_wdt(wdt); + + dw_wdt->timeout = dw_wdt_get_timeleft(dw_wdt) / dw_wdt->rate; + dw_wdt_stop(dw_wdt); + + return RT_EOK; +} + +static void dw_wdt_pm_resume(const struct rt_device *device, rt_uint8_t mode) +{ + rt_watchdog_t *wdt = rt_container_of(device, rt_watchdog_t, parent); + struct dw_wdt *dw_wdt = raw_to_dw_wdt(wdt); + + if (!dw_wdt->user) + { + return; + } + + if (!dw_wdt_init(wdt)) + { + dw_wdt_start(dw_wdt); + } +} + +static const struct rt_device_pm_ops dw_wdt_pm_ops = +{ + .suspend = dw_wdt_pm_suspend, + .resume = dw_wdt_pm_resume, +}; +#endif /* RT_USING_PM */ + +static void dw_wdt_isr(int irqno, void *param) +{ + struct dw_wdt *wdt = (struct dw_wdt *)param; + + if (!HWREG32(wdt->base + WDOG_INTERRUPT_STATUS_REG_OFFSET)) + { + return; + } + + /* Clear the IRQ status (EOI) */ + rt_event_send(&wdt->event, HWREG32(wdt->base + WDOG_INTERRUPT_CLEAR_REG_OFFSET)); +} + +static void dw_wdt_free(struct dw_wdt *dw_wdt) +{ + if (dw_wdt->base) + { + rt_iounmap(dw_wdt->base); + } + + if (!rt_is_err_or_null(dw_wdt->clk)) + { + rt_clk_disable(dw_wdt->clk); + } + + if (!rt_is_err_or_null(dw_wdt->rstc)) + { + rt_reset_control_assert(dw_wdt->rstc); + rt_reset_control_put(dw_wdt->rstc); + } + + rt_free(dw_wdt); +} + +static rt_err_t dw_wdt_probe(struct rt_platform_device *pdev) +{ + rt_err_t err = RT_EOK; + const char *dev_name; + struct rt_device *dev = &pdev->parent; + struct dw_wdt *dw_wdt = rt_calloc(1, sizeof(*dw_wdt)); + + if (!dw_wdt) + { + return -RT_ENOMEM; + } + + dw_wdt->base = rt_dm_dev_iomap(dev, 0); + + if (!dw_wdt->base) + { + err = -RT_EIO; + goto _free_res; + } + + dw_wdt->irq = rt_dm_dev_get_irq(dev, 0); + + if (dw_wdt->irq < 0) + { + err = dw_wdt->irq; + goto _free_res; + } + + dw_wdt->clk = rt_clk_get_by_name(dev, "pclk"); + + if (rt_is_err(dw_wdt->clk)) + { + dw_wdt->clk = rt_clk_get_by_index(dev, 0); + + if (rt_is_err(dw_wdt->clk)) + { + err = rt_ptr_err(dw_wdt->clk); + goto _free_res; + } + } + + dw_wdt->rstc = rt_reset_control_get_by_index(dev, 0); + + if (rt_is_err(dw_wdt->rstc)) + { + err = rt_ptr_err(dw_wdt->rstc); + goto _free_res; + } + + rt_reset_control_deassert(dw_wdt->rstc); + + dev->user_data = dw_wdt; + + dw_wdt->rate = rt_clk_get_rate(dw_wdt->clk); + dw_wdt->parent.ops = &dw_wdt_ops; + + rt_dm_dev_set_name_auto(&dw_wdt->parent.parent, "wdt"); + dev_name = rt_dm_dev_get_name(&dw_wdt->parent.parent); + + rt_event_init(&dw_wdt->event, dev_name, RT_IPC_FLAG_PRIO); + + rt_hw_interrupt_install(dw_wdt->irq, dw_wdt_isr, dw_wdt, dev_name); + rt_hw_interrupt_umask(dw_wdt->irq); + +#ifdef RT_USING_PM + rt_pm_device_register(&dw_wdt->parent.parent, &dw_wdt_pm_ops); +#endif + + rt_hw_watchdog_register(&dw_wdt->parent, dev_name, 0, dw_wdt); + + return RT_EOK; + +_free_res: + dw_wdt_free(dw_wdt); + + return err; +} + +static rt_err_t dw_wdt_remove(struct rt_platform_device *pdev) +{ + struct dw_wdt *dw_wdt = pdev->parent.user_data; + + rt_hw_interrupt_mask(dw_wdt->irq); + rt_pic_detach_irq(dw_wdt->irq, dw_wdt); + + rt_device_unregister(&dw_wdt->parent.parent); + + dw_wdt_free(dw_wdt); + + return RT_EOK; +} + +static const struct rt_ofw_node_id dw_wdt_ofw_ids[] = +{ + { .compatible = "snps,dw-wdt" }, + { /* sentinel */ } +}; + +static struct rt_platform_driver dw_wdt_driver = +{ + .name = "dw-wdt", + .ids = dw_wdt_ofw_ids, + + .probe = dw_wdt_probe, + .remove = dw_wdt_remove, +}; +RT_PLATFORM_DRIVER_EXPORT(dw_wdt_driver); diff --git a/components/drivers/watchdog/watchdog-i6300esb.c b/components/drivers/watchdog/watchdog-i6300esb.c new file mode 100644 index 000000000000..dd5c0da75596 --- /dev/null +++ b/components/drivers/watchdog/watchdog-i6300esb.c @@ -0,0 +1,294 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-11-26 GuEe-GUI first version + */ + +#include +#include + +#define DBG_TAG "wdt.i6300esb" +#define DBG_LVL DBG_INFO +#include + +#define I6300ESB_REG_BAR 0 + +/* PCI configuration registers */ +#define ESB_CONFIG_PCI_REG 0x60 /* Config register */ +#define ESB_LOCK_PCI_REG 0x68 /* WDT lock register */ + +/* Memory mapped registers */ +#define ESB_TIMER1_REG 0x00 /* Timer1 value after each reset */ +#define ESB_TIMER2_REG 0x04 /* Timer2 value after each reset */ +#define ESB_GINTSR_REG 0x08 /* General Interrupt Status Reg */ +#define ESB_RELOAD_REG 0x0c /* Reload register */ + +/* Lock register bits */ +#define ESB_WDT_FUNC (0x01 << 2) /* Watchdog functionality */ +#define ESB_WDT_ENABLE (0x01 << 1) /* Enable WDT */ +#define ESB_WDT_LOCK (0x01 << 0) /* Lock (nowayout) */ + +/* Config register bits */ +#define ESB_WDT_REBOOT (0x01 << 5) /* Enable reboot on timeout */ +#define ESB_WDT_FREQ (0x01 << 2) /* Decrement frequency */ +#define ESB_WDT_INTTYPE (0x03 << 0) /* Interrupt type on timer1 timeout */ + +/* Reload register bits */ +#define ESB_WDT_TIMEOUT (0x01 << 9) /* Watchdog timed out */ +#define ESB_WDT_RELOAD (0x01 << 8) /* prevent timeout */ + +/* Magic constants */ +#define ESB_UNLOCK1 0x80 /* Step 1 to unlock reset registers */ +#define ESB_UNLOCK2 0x86 /* Step 2 to unlock reset registers */ + +/* 30 sec default heartbeat (1 < heartbeat < 2*1023) */ +#define ESB_HEARTBEAT_MIN 1 +#define ESB_HEARTBEAT_MAX 2046 +#define ESB_HEARTBEAT_DEFAULT 30 + +struct i6300esb_wdt +{ + rt_watchdog_t parent; + + void *regs; + rt_uint32_t timeout; + struct rt_pci_device *pdev; +}; + +#define raw_to_i6300esb_wdt(raw) rt_container_of(raw, struct i6300esb_wdt, parent) + +/* + * Prepare for reloading the timer by unlocking the proper registers. + * This is performed by first writing 0x80 followed by 0x86 to the + * reload register. After this the appropriate registers can be written + * to once before they need to be unlocked again. + */ +rt_inline void i6300esb_wdt_unlock_registers(struct i6300esb_wdt *esb) +{ + HWREG16(esb->regs + ESB_RELOAD_REG) = ESB_UNLOCK1; + HWREG16(esb->regs + ESB_RELOAD_REG) = ESB_UNLOCK2; +} + +static rt_uint32_t i6300esb_timer_start(struct i6300esb_wdt *esb) +{ + i6300esb_wdt_unlock_registers(esb); + HWREG16(esb->regs + ESB_RELOAD_REG) = ESB_WDT_RELOAD; + + rt_pci_write_config_u8(esb->pdev, ESB_LOCK_PCI_REG, ESB_WDT_ENABLE); + + return RT_EOK; +} + +static rt_uint32_t i6300esb_timer_stop(struct i6300esb_wdt *esb) +{ + rt_uint8_t val; + + /* First, reset timers as suggested by the docs */ + i6300esb_wdt_unlock_registers(esb); + HWREG16(esb->regs + ESB_RELOAD_REG) = ESB_WDT_RELOAD; + + /* Then disable the WDT */ + rt_pci_write_config_u8(esb->pdev, ESB_LOCK_PCI_REG, 0x0); + rt_pci_read_config_u8(esb->pdev, ESB_LOCK_PCI_REG, &val); + + /* Returns 0 if the timer was disabled, non-zero otherwise */ + return val & ESB_WDT_ENABLE; +} + +static rt_err_t esb_timer_keepalive(struct i6300esb_wdt *esb) +{ + i6300esb_wdt_unlock_registers(esb); + HWREG16(esb->regs + ESB_RELOAD_REG) = ESB_WDT_RELOAD; + + return RT_EOK; +} + +static rt_err_t i6300esb_timer_set_heartbeat(struct i6300esb_wdt *esb, rt_uint32_t time) +{ + rt_uint32_t val; + + /* + * We shift by 9, so if we are passed a value of 1 sec, + * val will be 1 << 9 = 512, then write that to two + * timers => 2 * 512 = 1024 (which is decremented at 1KHz) + */ + val = time << 9; + + /* Write timer 1 */ + i6300esb_wdt_unlock_registers(esb); + HWREG32(esb->regs + ESB_TIMER1_REG) = val; + + /* Write timer 2 */ + i6300esb_wdt_unlock_registers(esb); + HWREG32(esb->regs + ESB_TIMER2_REG) = val; + + /* Reload */ + i6300esb_wdt_unlock_registers(esb); + HWREG16(esb->regs + ESB_RELOAD_REG) = ESB_WDT_RELOAD; + + esb->timeout = time; + + return RT_EOK; +} + +static rt_err_t i6300esb_wdt_init(rt_watchdog_t *wdt) +{ + return RT_EOK; +} + +static rt_err_t i6300esb_wdt_control(rt_watchdog_t *wdt, int cmd, void *args) +{ + rt_err_t err = RT_EOK; + struct i6300esb_wdt *esb = raw_to_i6300esb_wdt(wdt); + + switch (cmd) + { + case RT_DEVICE_CTRL_WDT_GET_TIMEOUT: + *(rt_uint32_t *)args = esb->timeout; + break; + + case RT_DEVICE_CTRL_WDT_SET_TIMEOUT: + err = i6300esb_timer_set_heartbeat(esb, *(rt_uint32_t *)args); + break; + + case RT_DEVICE_CTRL_WDT_KEEPALIVE: + err = esb_timer_keepalive(esb); + break; + + case RT_DEVICE_CTRL_WDT_START: + err = i6300esb_timer_start(esb); + break; + + case RT_DEVICE_CTRL_WDT_STOP: + err = i6300esb_timer_stop(esb); + break; + + default: + err = -RT_EINVAL; + } + + return err; +} + +static const struct rt_watchdog_ops i6300esb_wdt_ops = +{ + .init = i6300esb_wdt_init, + .control = i6300esb_wdt_control, +}; + +static rt_err_t i6300esb_wdt_probe(struct rt_pci_device *pdev) +{ + rt_err_t err; + rt_uint8_t val1; + rt_uint16_t val2; + const char *dev_name; + struct i6300esb_wdt *esb = rt_calloc(1, sizeof(*esb)); + + if (!esb) + { + return -RT_ENOMEM; + } + + esb->regs = rt_pci_iomap(pdev, I6300ESB_REG_BAR); + + if (!esb->regs) + { + err = -RT_EIO; + + goto _fail; + } + + /* + * Config register: + * Bit 5 : 0 = Enable WDT_OUTPUT + * Bit 2 : 0 = set the timer frequency to the PCI clock + * divided by 2^15 (approx 1KHz). + * Bits 1:0 : 11 = WDT_INT_TYPE Disabled. + * The watchdog has two timers, it can be setup so that the + * expiry of timer1 results in an interrupt and the expiry of + * timer2 results in a reboot. We set it to not generate + * any interrupts as there is not much we can do with it + * right now. + */ + rt_pci_write_config_u16(pdev, ESB_CONFIG_PCI_REG, 0x0003); + + /* Check that the WDT isn't already locked */ + rt_pci_read_config_u8(pdev, ESB_LOCK_PCI_REG, &val1); + if (val1 & ESB_WDT_LOCK) + { + LOG_W("Nowayout already set"); + } + + /* Set the timer to watchdog mode and disable it for now */ + rt_pci_write_config_u8(pdev, ESB_LOCK_PCI_REG, 0x00); + + /* Check if the watchdog was previously triggered */ + i6300esb_wdt_unlock_registers(esb); + val2 = HWREG16(esb->regs + ESB_RELOAD_REG); + if (val2 & ESB_WDT_TIMEOUT) + { + LOG_D("Card previously reset the CPU"); + } + + /* Reset WDT_TIMEOUT flag and timers */ + i6300esb_wdt_unlock_registers(esb); + HWREG16(esb->regs + ESB_RELOAD_REG) = ESB_WDT_TIMEOUT | ESB_WDT_RELOAD; + + /* And set the correct timeout value */ + i6300esb_timer_set_heartbeat(esb, ESB_HEARTBEAT_DEFAULT); + + pdev->parent.user_data = esb; + + esb->pdev = pdev; + esb->parent.ops = &i6300esb_wdt_ops; + + rt_dm_dev_set_name_auto(&esb->parent.parent, "wdt"); + dev_name = rt_dm_dev_get_name(&esb->parent.parent); + rt_hw_watchdog_register(&esb->parent, dev_name, 0, esb); + + return RT_EOK; + +_fail: + if (esb->regs) + { + rt_iounmap(esb->regs); + } + + rt_free(esb); + + return err; +} + +static rt_err_t i6300esb_wdt_remove(struct rt_pci_device *pdev) +{ + struct i6300esb_wdt *esb = pdev->parent.user_data; + + i6300esb_timer_stop(esb); + + rt_device_unregister(&esb->parent.parent); + + rt_iounmap(esb->regs); + rt_free(esb); + + return RT_EOK; +} + +static struct rt_pci_device_id i6300esb_wdt_pci_ids[] = +{ + { RT_PCI_DEVICE_ID(PCI_VENDOR_ID_INTEL, 0x25ab), }, + { /* sentinel */ } +}; + +static struct rt_pci_driver i6300esb_wdt_driver = +{ + .name = "i6300esb-wdt", + + .ids = i6300esb_wdt_pci_ids, + .probe = i6300esb_wdt_probe, + .remove = i6300esb_wdt_remove, +}; +RT_PCI_DRIVER_EXPORT(i6300esb_wdt_driver); diff --git a/components/drivers/watchdog/watchdog-rk8xx.c b/components/drivers/watchdog/watchdog-rk8xx.c new file mode 100644 index 000000000000..099cc1cca4e5 --- /dev/null +++ b/components/drivers/watchdog/watchdog-rk8xx.c @@ -0,0 +1,195 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-11-26 GuEe-GUI first version + */ + +#include +#include + +#define DBG_TAG "wdt.rk8xx" +#define DBG_LVL DBG_INFO +#include + +#include "../mfd/rk8xx.h" + +struct rk8xx_watchdog +{ + rt_watchdog_t parent; + + int irq; + rt_uint8_t timeout_idx; + struct rk8xx *rk8xx; +}; + +#define raw_to_rk8xx_watchdog(raw) rt_container_of(raw, struct rk8xx_watchdog, parent) + +static const unsigned int time_ms_ranges[] = +{ + [0] = 50, /* 50 ms */ + [1] = 100, /* 100 ms */ + [2] = 500, /* 500 ms */ + [4] = 2 * 1000, /* 2 s */ + [5] = 10 * 1000, /* 10 s */ + [6] = 60 * 1000, /* 1 min */ + [7] = 10 * 60 * 10000, /* 10 min */ +}; + +static rt_err_t rk8xx_watchdog_enable(struct rk8xx_watchdog *rk8xx_wdt, + rt_bool_t enable) +{ + return rk8xx_update_bits(rk8xx_wdt->rk8xx, RK806_WDT, RK806_WDT_EN, !!enable); +} + +static rt_err_t rk8xx_watchdog_keep_alive(struct rk8xx_watchdog *rk8xx_wdt) +{ + rt_err_t err; + /* + * Should to clear the interruption of WDT after set time, + * otherwise the time will advance 1S. + */ + rk8xx_watchdog_enable(rk8xx_wdt, RT_FALSE); + + err = rk8xx_update_bits(rk8xx_wdt->rk8xx, RK806_WDT, RK806_WDT_SET, + rk8xx_wdt->timeout_idx); + + rk8xx_watchdog_enable(rk8xx_wdt, RT_TRUE); + + return err; +} + +static rt_err_t rk8xx_watchdog_init(rt_watchdog_t *wdt) +{ + return RT_EOK; +} + +static rt_err_t rk8xx_watchdog_control(rt_watchdog_t *wdt, int cmd, void *args) +{ + rt_err_t err = RT_EOK; + struct rk8xx_watchdog *rk8xx_wdt = raw_to_rk8xx_watchdog(wdt); + + switch (cmd) + { + case RT_DEVICE_CTRL_WDT_GET_TIMEOUT: + *(rt_uint32_t *)args = time_ms_ranges[rk8xx_wdt->timeout_idx] / 1000; + break; + + case RT_DEVICE_CTRL_WDT_SET_TIMEOUT: + { + rt_uint32_t timeout = *(rt_uint32_t *)args * 1000; + err = -RT_EINVAL; + + for (int i = RT_ARRAY_SIZE(time_ms_ranges) - 1; i >= 0; --i) + { + if (timeout >= time_ms_ranges[i]) + { + rk8xx_wdt->timeout_idx = i; + + err = RT_EOK; + break; + } + } + } + break; + + case RT_DEVICE_CTRL_WDT_KEEPALIVE: + err = rk8xx_watchdog_keep_alive(rk8xx_wdt); + break; + + case RT_DEVICE_CTRL_WDT_START: + err = rk8xx_watchdog_enable(rk8xx_wdt, RT_TRUE); + break; + + case RT_DEVICE_CTRL_WDT_STOP: + err = rk8xx_watchdog_enable(rk8xx_wdt, RT_FALSE); + break; + + default: + err = -RT_EINVAL; + } + + return err; +} + +static const struct rt_watchdog_ops rk8xx_watchdog_ops = +{ + .init = rk8xx_watchdog_init, + .control = rk8xx_watchdog_control, +}; + +static rt_err_t rk8xx_watchdog_probe(struct rt_platform_device *pdev) +{ + rt_err_t err; + const char *dev_name; + struct rk8xx *rk8xx = pdev->priv; + struct rt_device *dev = &pdev->parent; + struct rk8xx_watchdog *rk8xx_wdt = rt_calloc(1, sizeof(*rk8xx_wdt)); + + if (!rk8xx_wdt) + { + return -RT_ENOMEM; + } + + rk8xx_wdt->rk8xx = rk8xx; + + rk8xx_wdt->irq = rt_dm_dev_get_irq(dev, 0); + + if (rk8xx_wdt->irq < 0) + { + err = rk8xx_wdt->irq; + goto _fail; + } + + if ((err = rk8xx_update_bits(rk8xx, RK806_WDT, RK806_WDT_ACT, RK806_WDT_ACT_RESTART))) + { + goto _fail; + } + + dev->user_data = rk8xx_wdt; + + rk8xx_wdt->parent.ops = &rk8xx_watchdog_ops; + + rt_dm_dev_set_name_auto(&rk8xx_wdt->parent.parent, "wdt"); + dev_name = rt_dm_dev_get_name(&rk8xx_wdt->parent.parent); + + rt_hw_watchdog_register(&rk8xx_wdt->parent, dev_name, 0, rk8xx_wdt); + + return RT_EOK; + +_fail: + rt_free(rk8xx_wdt); + + return err; +} + +static rt_err_t rk8xx_watchdog_remove(struct rt_platform_device *pdev) +{ + struct rk8xx_watchdog *rk8xx_wdt = pdev->parent.user_data; + + rk8xx_watchdog_enable(rk8xx_wdt, RT_FALSE); + + rt_device_unregister(&rk8xx_wdt->parent.parent); + + rt_free(rk8xx_wdt); + + return RT_EOK; +} + +static struct rt_platform_driver rk8xx_watchdog_driver = +{ + .name = "rk8xx-watchdog", + .probe = rk8xx_watchdog_probe, + .remove = rk8xx_watchdog_remove, +}; + +static int rk8xx_watchdog_drv_register(void) +{ + rt_platform_driver_register(&rk8xx_watchdog_driver); + + return 0; +} +INIT_DRIVER_LATER_EXPORT(rk8xx_watchdog_drv_register); diff --git a/components/lwp/lwp_elf.h b/components/lwp/lwp_elf.h index e4817fe6f908..d0af13012dcc 100644 --- a/components/lwp/lwp_elf.h +++ b/components/lwp/lwp_elf.h @@ -1018,14 +1018,14 @@ typedef struct /* Note section contents. Each entry in the note section begins with a header of a fixed form. */ -typedef struct +typedef struct elf32_note { Elf32_Word n_namesz; /* Length of the note's name. */ Elf32_Word n_descsz; /* Length of the note's descriptor. */ Elf32_Word n_type; /* Type of the note. */ } Elf32_Nhdr; -typedef struct +typedef struct elf64_note { Elf64_Word n_namesz; /* Length of the note's name. */ Elf64_Word n_descsz; /* Length of the note's descriptor. */ diff --git a/components/mm/ioremap.c b/components/mm/ioremap.c index 25200c9cf072..9b062adf2bc5 100644 --- a/components/mm/ioremap.c +++ b/components/mm/ioremap.c @@ -87,10 +87,6 @@ rt_weak void *rt_ioremap_early(void *paddr, size_t size) return paddr; } -rt_weak void rt_iounmap_early(void *vaddr, size_t size) -{ -} - void *rt_ioremap(void *paddr, size_t size) { return _ioremap_type(paddr, size, MM_AREA_TYPE_PHY); diff --git a/components/mm/ioremap.h b/components/mm/ioremap.h index 25dd639e0ec8..f17b5d5fff71 100644 --- a/components/mm/ioremap.h +++ b/components/mm/ioremap.h @@ -31,7 +31,6 @@ extern "C" { */ void *rt_ioremap_early(void *paddr, size_t size); -void rt_iounmap_early(void *vaddr, size_t size); void *rt_ioremap(void *paddr, size_t size); void *rt_ioremap_nocache(void *paddr, size_t size); void *rt_ioremap_cached(void *paddr, size_t size); diff --git a/components/utilities/Kconfig b/components/utilities/Kconfig index 25f3608799dd..4463a51d1dbe 100644 --- a/components/utilities/Kconfig +++ b/components/utilities/Kconfig @@ -216,6 +216,7 @@ config RT_USING_VAR_EXPORT bool "Enable Var Export" default n +source "$RTT_DIR/components/utilities/crash_core/Kconfig" source "$RTT_DIR/components/utilities/libadt/Kconfig" source "$RTT_DIR/components/utilities/rt-link/Kconfig" diff --git a/components/utilities/crash_core/Kconfig b/components/utilities/crash_core/Kconfig new file mode 100644 index 000000000000..1d62369bc957 --- /dev/null +++ b/components/utilities/crash_core/Kconfig @@ -0,0 +1,3 @@ +menuconfig RT_USING_CRASH_CORE + bool "Enable Crash Core" + default n diff --git a/components/utilities/crash_core/SConscript b/components/utilities/crash_core/SConscript new file mode 100755 index 000000000000..4e32efbd145f --- /dev/null +++ b/components/utilities/crash_core/SConscript @@ -0,0 +1,15 @@ +from building import * + +Import('rtconfig') + +cwd = GetCurrentDir() +src = [] +CPPPATH = [cwd] +group = [] + +if GetDepend(['RT_USING_CRASH_CORE']): + src = Glob('*.c') + Env['LINKFLAGS'] += " -Wl,--build-id=sha1 " + +group = DefineGroup('LIBADT', src, depend = ['RT_USING_CRASH_CORE'], CPPPATH = CPPPATH) +Return('group') diff --git a/components/utilities/crash_core/crash_core.c b/components/utilities/crash_core/crash_core.c new file mode 100644 index 000000000000..71dfcf2eb839 --- /dev/null +++ b/components/utilities/crash_core/crash_core.c @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-02-25 GuEe-GUI the first version + */ + +#define DBG_TAG "crash.core" +#define DBG_LVL DBG_INFO +#include + +#include +#include + +#ifndef DIV_ROUND_UP +#define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d)) +#endif + +extern char __build_id_start; +extern char __build_id_end; + +char rtthread_build_id[40] = {}; + +/* vmcoreinfo stuff */ +rt_uint8_t *vmcoreinfo_data = RT_NULL; +rt_size_t vmcoreinfo_size = 0; +rt_uint32_t *vmcoreinfo_note = RT_NULL; + +void vmcoreinfo_append_str(const char *format, ...) +{ + va_list args; + rt_size_t r; + char buf[0x50]; + const rt_size_t max_size = (rt_size_t)VMCOREINFO_BYTES - vmcoreinfo_size; + + va_start(args, format); + r = rt_vsnprintf(buf, sizeof(buf), format, args); + va_end(args); + + if (r > max_size) + { + r = max_size; + } + + rt_memcpy(&vmcoreinfo_data[vmcoreinfo_size], buf, r); + + vmcoreinfo_size += r; + + if (vmcoreinfo_size == VMCOREINFO_BYTES) + { + LOG_W("vmcoreinfo data exceeds allocated size, truncating"); + } +} + +rt_weak rt_ubase_t vmcoreinfo_note_paddr(void) +{ + void *ptr = vmcoreinfo_note; + +#ifdef ARCH_MM_MMU + ptr = rt_kmem_v2p(ptr); +#endif + + return (rt_ubase_t)ptr; +} + +rt_weak void rt_hw_crash_save_vmcoreinfo(void) +{ +} + +static void update_vmcoreinfo_note(void) +{ + rt_uint32_t *buf; + struct elf_note *note; + + if (!vmcoreinfo_size) + { + return; + } + + buf = vmcoreinfo_note; + note = (struct elf_note *)buf; + + note->n_namesz = rt_strlen(VMCOREINFO_NOTE_NAME) + 1; + note->n_descsz = vmcoreinfo_size; + note->n_type = 0; + buf += DIV_ROUND_UP(sizeof(*note), sizeof(Elf_Word)); + rt_memcpy(buf, VMCOREINFO_NOTE_NAME, note->n_namesz); + buf += DIV_ROUND_UP(note->n_namesz, sizeof(Elf_Word)); + rt_memcpy(buf, vmcoreinfo_data, vmcoreinfo_size); + buf += DIV_ROUND_UP(vmcoreinfo_size, sizeof(Elf_Word)); + + rt_memset(buf, 0, sizeof(struct elf_note)); +} + +static int crash_save_vmcoreinfo_init(void) +{ + const char *build_id; + + vmcoreinfo_data = rt_pages_alloc(rt_page_bits(ARCH_PAGE_SIZE)); + + if (!vmcoreinfo_data) + { + LOG_W("Memory allocation for vmcoreinfo_%s failed", "data"); + + return -RT_ENOMEM; + } + + vmcoreinfo_note = rt_pages_alloc(rt_page_bits(ARCH_PAGE_SIZE)); + + if (!vmcoreinfo_note) + { + rt_pages_free(vmcoreinfo_data, rt_page_bits(ARCH_PAGE_SIZE)); + vmcoreinfo_data = RT_NULL; + + LOG_W("Memory allocation for vmcoreinfo_%s failed", "note"); + + return -RT_ENOMEM; + } + + rt_memset(vmcoreinfo_data, 0, ARCH_PAGE_SIZE); + + /* Skip GNU header (16 bytes) */ + build_id = &__build_id_start + 16; + + for (int i = 0; build_id < &__build_id_end; ++build_id) + { + const char *hex = "0123456789abcdef"; + + rtthread_build_id[i++] = hex[*build_id >> 4]; + rtthread_build_id[i++] = hex[*build_id & 0xf]; + } + + VMCOREINFO_BUILD_ID(); + VMCOREINFO_PAGESIZE(ARCH_PAGE_SIZE); + + rt_hw_crash_save_vmcoreinfo(); + update_vmcoreinfo_note(); + + return 0; +} +INIT_BOARD_EXPORT(crash_save_vmcoreinfo_init); diff --git a/components/utilities/crash_core/crash_core.h b/components/utilities/crash_core/crash_core.h new file mode 100644 index 000000000000..832241aafc48 --- /dev/null +++ b/components/utilities/crash_core/crash_core.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-02-25 GuEe-GUI the first version + */ + +#ifndef __CRASH_CORE_H__ +#define __CRASH_CORE_H__ + +#include +#include + +#define CRASH_CORE_NOTE_HEAD_BYTES RT_ALIGN(sizeof(struct elf_note), 4) + +#define VMCOREINFO_BYTES ARCH_PAGE_SIZE +#define VMCOREINFO_NOTE_NAME "VMCOREINFO" +#define VMCOREINFO_NOTE_NAME_BYTES RT_ALIGN(sizeof(VMCOREINFO_NOTE_NAME), 4) +#define VMCOREINFO_NOTE_SIZE ((CRASH_CORE_NOTE_HEAD_BYTES * 2) + VMCOREINFO_NOTE_NAME_BYTES + VMCOREINFO_BYTES) + +void vmcoreinfo_append_str(const char *format, ...); +rt_ubase_t vmcoreinfo_note_paddr(void); + +#define VMCOREINFO_OSRELEASE(value) \ + vmcoreinfo_append_str("OSRELEASE=%s\n", value) +#define VMCOREINFO_BUILD_ID() \ + vmcoreinfo_append_str("BUILD-ID=%40s\n", rtthread_build_id) +#define VMCOREINFO_PAGESIZE(value) \ + vmcoreinfo_append_str("PAGESIZE=%ld\n", value) +#define VMCOREINFO_SYMBOL(name) \ + vmcoreinfo_append_str("SYMBOL(%s)=%lx\n", #name, (rt_ubase_t)&name) +#define VMCOREINFO_SYMBOL_ARRAY(name) \ + vmcoreinfo_append_str("SYMBOL(%s)=%lx\n", #name, (rt_ubase_t)name) +#define VMCOREINFO_SIZE(name) \ + vmcoreinfo_append_str("SIZE(%s)=%lu\n", #name, (rt_ubase_t)sizeof(name)) +#define VMCOREINFO_STRUCT_SIZE(name) \ + vmcoreinfo_append_str("SIZE(%s)=%lu\n", #name, (rt_ubase_t)sizeof(struct name)) +#define VMCOREINFO_OFFSET(name, field) \ + vmcoreinfo_append_str("OFFSET(%s.%s)=%lu\n", #name, #field, (rt_ubase_t)&((struct name *)0)->field) +#define VMCOREINFO_TYPE_OFFSET(name, field) \ + vmcoreinfo_append_str("OFFSET(%s.%s)=%lu\n", #name, #field, (rt_ubase_t)&((name *)0)->field) +#define VMCOREINFO_LENGTH(name, value) \ + vmcoreinfo_append_str("LENGTH(%s)=%lu\n", #name, (rt_ubase_t)value) +#define VMCOREINFO_NUMBER(name) \ + vmcoreinfo_append_str("NUMBER(%s)=%ld\n", #name, (rt_base_t)name) +#define VMCOREINFO_CONFIG(name) \ + vmcoreinfo_append_str("CONFIG_%s=y\n", #name) + +#endif /* __CRASH_CORE_H__ */ diff --git a/include/rtatomic.h b/include/rtatomic.h index d3cd04dd4b60..4ace5307f16b 100644 --- a/include/rtatomic.h +++ b/include/rtatomic.h @@ -10,7 +10,7 @@ #ifndef __RT_ATOMIC_H__ #define __RT_ATOMIC_H__ -#include +#include rt_atomic_t rt_hw_atomic_load(volatile rt_atomic_t *ptr); void rt_hw_atomic_store(volatile rt_atomic_t *ptr, rt_atomic_t val); diff --git a/include/rtdef.h b/include/rtdef.h index e25590d22ab0..85f8bf7e658e 100644 --- a/include/rtdef.h +++ b/include/rtdef.h @@ -205,6 +205,7 @@ typedef __gnuc_va_list va_list; #define rt_section(x) __attribute__((section(x))) #define rt_used __attribute__((used)) #define rt_align(n) __attribute__((aligned(n))) +#define rt_packed __attribute__((packed)) #define rt_weak __attribute__((weak)) #define rt_inline static __inline #define RTT_API @@ -444,6 +445,12 @@ struct rt_object #endif /* RT_USING_SMART */ rt_list_t list; /**< list node of kernel object */ +#ifdef RT_USING_DFS_DIRECTFS + rt_list_t node; + rt_list_t child_nodes; + struct rt_object *parent; + void *objectfs; +#endif /* RT_USING_DFS_DIRECTFS */ }; typedef struct rt_object *rt_object_t; /**< Type for kernel objects. */ diff --git a/include/rthw.h b/include/rthw.h index 1dcfb311baa1..7dbc28fc08c6 100644 --- a/include/rthw.h +++ b/include/rthw.h @@ -163,6 +163,7 @@ void rt_hw_us_delay(rt_uint32_t us); #ifdef RT_USING_SMP #include /* for spinlock from arch */ +#include struct rt_spinlock { @@ -171,6 +172,17 @@ struct rt_spinlock void rt_hw_spin_lock_init(rt_hw_spinlock_t *lock); void rt_hw_spin_lock(rt_hw_spinlock_t *lock); +rt_inline rt_bool_t rt_hw_spin_trylock(rt_hw_spinlock_t *lock) +{ + volatile rt_atomic_t old = (rt_uint32_t)rt_atomic_load((rt_atomic_t *)lock); + + if ((old >> 16) != (old & 0xffff)) + { + return RT_FALSE; + } + + return rt_atomic_compare_exchange_strong((rt_atomic_t *)&lock->slock, &old, old + (1 << 16)); +} void rt_hw_spin_unlock(rt_hw_spinlock_t *lock); int rt_hw_cpu_id(void); diff --git a/include/rtthread.h b/include/rtthread.h index e64437b85a69..0222731c60ab 100644 --- a/include/rtthread.h +++ b/include/rtthread.h @@ -517,15 +517,19 @@ struct rt_spinlock; void rt_spin_lock_init(struct rt_spinlock *lock); void rt_spin_lock(struct rt_spinlock *lock); +rt_err_t rt_spin_trylock(struct rt_spinlock *lock); void rt_spin_unlock(struct rt_spinlock *lock); rt_base_t rt_spin_lock_irqsave(struct rt_spinlock *lock); +rt_err_t rt_spin_trylock_irqsave(struct rt_spinlock *lock, rt_ubase_t *out_level); void rt_spin_unlock_irqrestore(struct rt_spinlock *lock, rt_base_t level); #else #define rt_spin_lock_init(lock) /* nothing */ #define rt_spin_lock(lock) rt_enter_critical() +#define rt_spin_trylock(lock) ({ rt_spin_lock(lock), RT_EOK }) #define rt_spin_unlock(lock) (RT_UNUSED(lock), rt_exit_critical()) #define rt_spin_lock_irqsave(lock) (RT_UNUSED(lock), rt_hw_interrupt_disable()) +#define rt_spin_trylock_irqsave(lock, out_level)({ RT_UNUSED(lock), *out_level = rt_hw_interrupt_disable(); RT_EOK }) #define rt_spin_unlock_irqrestore(lock, level) rt_hw_interrupt_enable(level) #endif diff --git a/libcpu/aarch64/common/armv8.h b/libcpu/aarch64/common/armv8.h index d96a5af72ab5..b3edd4d9ca58 100644 --- a/libcpu/aarch64/common/armv8.h +++ b/libcpu/aarch64/common/armv8.h @@ -1,57 +1,62 @@ /* - * Copyright (c) 2006-2020, RT-Thread Development Team + * Copyright (c) 2006-2023, RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 * * Change Logs: * Date Author Notes * 2011-09-15 Bernard first version + * 2023-07-13 GuEe-GUI append fpu: Q16 ~ Q31 */ #ifndef __ARMV8_H__ #define __ARMV8_H__ +#include + +typedef struct { rt_uint64_t value[2]; } rt_uint128_t; + /* the exception stack without VFP registers */ struct rt_hw_exp_stack { - unsigned long pc; - unsigned long cpsr; - unsigned long sp_el0; - unsigned long x30; - unsigned long fpcr; - unsigned long fpsr; - unsigned long x28; - unsigned long x29; - unsigned long x26; - unsigned long x27; - unsigned long x24; - unsigned long x25; - unsigned long x22; - unsigned long x23; - unsigned long x20; - unsigned long x21; - unsigned long x18; - unsigned long x19; - unsigned long x16; - unsigned long x17; - unsigned long x14; - unsigned long x15; - unsigned long x12; - unsigned long x13; - unsigned long x10; - unsigned long x11; - unsigned long x8; - unsigned long x9; - unsigned long x6; - unsigned long x7; - unsigned long x4; - unsigned long x5; - unsigned long x2; - unsigned long x3; - unsigned long x0; - unsigned long x1; + rt_uint64_t pc; + rt_uint64_t cpsr; + rt_uint64_t sp_el0; + rt_uint64_t x30; + rt_uint64_t fpcr; + rt_uint64_t fpsr; + rt_uint64_t x28; + rt_uint64_t x29; + rt_uint64_t x26; + rt_uint64_t x27; + rt_uint64_t x24; + rt_uint64_t x25; + rt_uint64_t x22; + rt_uint64_t x23; + rt_uint64_t x20; + rt_uint64_t x21; + rt_uint64_t x18; + rt_uint64_t x19; + rt_uint64_t x16; + rt_uint64_t x17; + rt_uint64_t x14; + rt_uint64_t x15; + rt_uint64_t x12; + rt_uint64_t x13; + rt_uint64_t x10; + rt_uint64_t x11; + rt_uint64_t x8; + rt_uint64_t x9; + rt_uint64_t x6; + rt_uint64_t x7; + rt_uint64_t x4; + rt_uint64_t x5; + rt_uint64_t x2; + rt_uint64_t x3; + rt_uint64_t x0; + rt_uint64_t x1; - unsigned long long fpu[16]; + rt_uint128_t fpu[32]; }; #define SP_ELx ((unsigned long)0x01) diff --git a/libcpu/aarch64/common/asm_fpu.h b/libcpu/aarch64/common/asm_fpu.h index 8ac4ab9bd881..556d1847deaf 100644 --- a/libcpu/aarch64/common/asm_fpu.h +++ b/libcpu/aarch64/common/asm_fpu.h @@ -25,8 +25,40 @@ STR Q13, [\reg, #-0x10]! STR Q14, [\reg, #-0x10]! STR Q15, [\reg, #-0x10]! + STR Q16, [\reg, #-0x10]! + STR Q17, [\reg, #-0x10]! + STR Q18, [\reg, #-0x10]! + STR Q19, [\reg, #-0x10]! + STR Q20, [\reg, #-0x10]! + STR Q21, [\reg, #-0x10]! + STR Q22, [\reg, #-0x10]! + STR Q23, [\reg, #-0x10]! + STR Q24, [\reg, #-0x10]! + STR Q25, [\reg, #-0x10]! + STR Q26, [\reg, #-0x10]! + STR Q27, [\reg, #-0x10]! + STR Q28, [\reg, #-0x10]! + STR Q29, [\reg, #-0x10]! + STR Q30, [\reg, #-0x10]! + STR Q31, [\reg, #-0x10]! .endm .macro RESTORE_FPU, reg + LDR Q31, [\reg], #0x10 + LDR Q30, [\reg], #0x10 + LDR Q29, [\reg], #0x10 + LDR Q28, [\reg], #0x10 + LDR Q27, [\reg], #0x10 + LDR Q26, [\reg], #0x10 + LDR Q25, [\reg], #0x10 + LDR Q24, [\reg], #0x10 + LDR Q23, [\reg], #0x10 + LDR Q22, [\reg], #0x10 + LDR Q21, [\reg], #0x10 + LDR Q20, [\reg], #0x10 + LDR Q19, [\reg], #0x10 + LDR Q18, [\reg], #0x10 + LDR Q17, [\reg], #0x10 + LDR Q16, [\reg], #0x10 LDR Q15, [\reg], #0x10 LDR Q14, [\reg], #0x10 LDR Q13, [\reg], #0x10 diff --git a/libcpu/aarch64/common/atomic_aarch64.c b/libcpu/aarch64/common/atomic_aarch64.c index c55cd8a84ffb..4b3295161335 100644 --- a/libcpu/aarch64/common/atomic_aarch64.c +++ b/libcpu/aarch64/common/atomic_aarch64.c @@ -8,6 +8,7 @@ * 2023-05-18 GuEe-GUI first version */ +#include #include rt_atomic_t rt_hw_atomic_load(volatile rt_atomic_t *ptr) @@ -15,7 +16,7 @@ rt_atomic_t rt_hw_atomic_load(volatile rt_atomic_t *ptr) rt_atomic_t ret; __asm__ volatile ( - " ldr %w0, %1\n" + " ldr %0, %1\n" " dmb ish" : "=r" (ret) : "Q" (*ptr) @@ -27,7 +28,7 @@ rt_atomic_t rt_hw_atomic_load(volatile rt_atomic_t *ptr) void rt_hw_atomic_store(volatile rt_atomic_t *ptr, rt_atomic_t val) { __asm__ volatile ( - " stlr %w1, %0\n" + " str %1, %0\n" " dmb ish" : "=Q" (*ptr) : "r" (val) @@ -41,9 +42,9 @@ rt_atomic_t rt_hw_atomic_##op(volatile rt_atomic_t *ptr, rt_atomic_t in_val) \ __asm__ volatile ( \ " prfm pstl1strm, %3\n" \ - "1: ldxr %w0, %3\n" \ - " "#ins " %w1, %w0, %w4\n" \ - " stlxr %w2, %w1, %3\n" \ + "1: ldxr %0, %3\n" \ + " "#ins " %1, %0, %4\n" \ + " stlxr %w2, %1, %3\n" \ " cbnz %w2, 1b\n" \ " dmb ish" \ : "=&r" (result), "=&r" (val), "=&r" (tmp), "+Q" (*ptr) \ @@ -65,8 +66,8 @@ rt_atomic_t rt_hw_atomic_exchange(volatile rt_atomic_t *ptr, rt_atomic_t val) __asm__ volatile ( " prfm pstl1strm, %2\n" - "1: ldxr %w0, %2\n" - " stlxr %w1, %w3, %2\n" + "1: ldxr %0, %2\n" + " stlxr %w1, %3, %2\n" " cbnz %w1, 1b\n" " dmb ish" : "=&r" (ret), "=&r" (tmp), "+Q" (*ptr) @@ -92,10 +93,10 @@ rt_atomic_t rt_hw_atomic_compare_exchange_strong(volatile rt_atomic_t *ptr, rt_a __asm__ volatile ( " prfm pstl1strm, %2\n" - "1: ldxr %w0, %2\n" - " eor %w1, %w0, %w3\n" - " cbnz %w1, 2f\n" - " stlxr %w1, %w4, %2\n" + "1: ldxr %0, %2\n" + " eor %1, %0, %3\n" + " cbnz %1, 2f\n" + " stlxr %w1, %4, %2\n" " cbnz %w1, 1b\n" " dmb ish\n" "2:" @@ -103,6 +104,5 @@ rt_atomic_t rt_hw_atomic_compare_exchange_strong(volatile rt_atomic_t *ptr, rt_a : "Kr" (*old), "r" (new) : "memory"); - return oldval; + return oldval == *old; } - diff --git a/libcpu/aarch64/common/backtrace.c b/libcpu/aarch64/common/backtrace.c index d8985d0c09ea..0ff342f21ebf 100644 --- a/libcpu/aarch64/common/backtrace.c +++ b/libcpu/aarch64/common/backtrace.c @@ -56,13 +56,33 @@ void backtrace(unsigned long pc, unsigned long lr, unsigned long fp) rt_kprintf("\n"); } -int rt_backtrace(void) +int rt_backtrace_skipn(int level) { - unsigned long pc = (unsigned long)backtrace; - unsigned long ra = (unsigned long)__builtin_return_address(0U); - unsigned long fr = (unsigned long)__builtin_frame_address(0U); + unsigned long lr; + unsigned long fp = (unsigned long)__builtin_frame_address(0U); + + /* skip current frames */ + struct bt_frame frame; + frame.fp = fp; + + /* skip n frames */ + do + { + if (unwind_frame(&frame)) + return -RT_ERROR; + lr = frame.pc; + + /* INFO: level is signed integer */ + } while (level-- > 0); - backtrace(pc, ra, fr); + backtrace(0, lr, frame.fp); + return 0; +} + +int rt_backtrace(void) +{ + /* skip rt_backtrace itself */ + rt_backtrace_skipn(1); return 0; } -MSH_CMD_EXPORT_ALIAS(rt_backtrace, bt_test, backtrace test); +MSH_CMD_EXPORT_ALIAS(rt_backtrace, bt_test, backtrace test); \ No newline at end of file diff --git a/libcpu/aarch64/common/cpu.c b/libcpu/aarch64/common/cpu.c index d3291961a2bd..d7d10427c332 100644 --- a/libcpu/aarch64/common/cpu.c +++ b/libcpu/aarch64/common/cpu.c @@ -74,50 +74,4 @@ const char *rt_hw_cpu_arch(void) return "aarch64"; } -/** shutdown CPU */ -rt_weak void rt_hw_cpu_shutdown() -{ - register rt_ubase_t level; - - rt_kprintf("shutdown...\n"); - - rt_pic_irq_finit(); - - /* Power management shutdown */ - if (rt_pm_shutdown) - { - rt_pm_shutdown(); - } - - level = rt_hw_interrupt_disable(); - while (level) - { - RT_ASSERT(0); - } -} -MSH_CMD_EXPORT_ALIAS(rt_hw_cpu_shutdown, shutdown, shutdown machine); - -/** reset CPU */ -rt_weak void rt_hw_cpu_reset() -{ - register rt_ubase_t level; - - rt_kprintf("reset...\n"); - - rt_pic_irq_finit(); - - /* Power management reset */ - if (rt_pm_reset) - { - rt_pm_reset(); - } - - level = rt_hw_interrupt_disable(); - while (level) - { - RT_ASSERT(0); - } -} -MSH_CMD_EXPORT_ALIAS(rt_hw_cpu_reset, reset, reset...); - /*@}*/ diff --git a/libcpu/aarch64/common/cpu.h b/libcpu/aarch64/common/cpu.h index 196805636a5f..bc380870170f 100644 --- a/libcpu/aarch64/common/cpu.h +++ b/libcpu/aarch64/common/cpu.h @@ -37,6 +37,163 @@ struct cpu_ops_t #define MPIDR_AFFINITY_LEVEL(mpidr, level) (((mpidr) >> MPIDR_LEVEL_SHIFT(level)) & MPIDR_LEVEL_MASK) +#define MIDR_REVISION_SHIFT 0 +#define MIDR_REVISION_MASK (0xf << MIDR_REVISION_SHIFT) +#define MIDR_PARTNUM_SHIFT 4 +#define MIDR_PARTNUM_MASK (0xfff << MIDR_PARTNUM_SHIFT) +#define MIDR_VARIANT_SHIFT 20 +#define MIDR_VARIANT_MASK (0xf << MIDR_VARIANT_SHIFT) +#define MIDR_ARCHITECTURE_SHIFT 16 +#define MIDR_ARCHITECTURE_MASK (0xf << MIDR_ARCHITECTURE_SHIFT) +#define MIDR_IMPLEMENTOR_SHIFT 24 +#define MIDR_IMPLEMENTOR_MASK (0xffU << MIDR_IMPLEMENTOR_SHIFT) + +#define MIDR_CPU_MODEL(imp, partnum) \ + (((rt_uint32_t)imp << MIDR_IMPLEMENTOR_SHIFT) | \ + (0xf << MIDR_ARCHITECTURE_SHIFT) | \ + ((partnum) << MIDR_PARTNUM_SHIFT)) + +#define ARM_CPU_IMP_ARM 0x41 +#define ARM_CPU_IMP_APM 0x50 +#define ARM_CPU_IMP_CAVIUM 0x43 +#define ARM_CPU_IMP_BRCM 0x42 +#define ARM_CPU_IMP_QCOM 0x51 +#define ARM_CPU_IMP_NVIDIA 0x4e +#define ARM_CPU_IMP_FUJITSU 0x46 +#define ARM_CPU_IMP_HISI 0x48 +#define ARM_CPU_IMP_APPLE 0x61 +#define ARM_CPU_IMP_AMPERE 0xc0 + +#define ARM_CPU_PART_AEM_V8 0xd0f +#define ARM_CPU_PART_FOUNDATION 0xd00 +#define ARM_CPU_PART_CORTEX_A57 0xd07 +#define ARM_CPU_PART_CORTEX_A72 0xd08 +#define ARM_CPU_PART_CORTEX_A53 0xd03 +#define ARM_CPU_PART_CORTEX_A73 0xd09 +#define ARM_CPU_PART_CORTEX_A75 0xd0a +#define ARM_CPU_PART_CORTEX_A35 0xd04 +#define ARM_CPU_PART_CORTEX_A55 0xd05 +#define ARM_CPU_PART_CORTEX_A76 0xd0b +#define ARM_CPU_PART_NEOVERSE_N1 0xd0c +#define ARM_CPU_PART_CORTEX_A77 0xd0d +#define ARM_CPU_PART_NEOVERSE_V1 0xd40 +#define ARM_CPU_PART_CORTEX_A78 0xd41 +#define ARM_CPU_PART_CORTEX_A78AE 0xd42 +#define ARM_CPU_PART_CORTEX_X1 0xd44 +#define ARM_CPU_PART_CORTEX_A510 0xd46 +#define ARM_CPU_PART_CORTEX_A710 0xd47 +#define ARM_CPU_PART_CORTEX_A715 0xd4d +#define ARM_CPU_PART_CORTEX_X2 0xd48 +#define ARM_CPU_PART_NEOVERSE_N2 0xd49 +#define ARM_CPU_PART_CORTEX_A78C 0xd4b + +#define APM_CPU_PART_POTENZA 0x000 + +#define CAVIUM_CPU_PART_THUNDERX 0x0a1 +#define CAVIUM_CPU_PART_THUNDERX_81XX 0x0a2 +#define CAVIUM_CPU_PART_THUNDERX_83XX 0x0a3 +#define CAVIUM_CPU_PART_THUNDERX2 0x0af +/* OcteonTx2 series */ +#define CAVIUM_CPU_PART_OCTX2_98XX 0x0b1 +#define CAVIUM_CPU_PART_OCTX2_96XX 0x0b2 +#define CAVIUM_CPU_PART_OCTX2_95XX 0x0b3 +#define CAVIUM_CPU_PART_OCTX2_95XXN 0x0b4 +#define CAVIUM_CPU_PART_OCTX2_95XXMM 0x0b5 +#define CAVIUM_CPU_PART_OCTX2_95XXO 0x0b6 + +#define BRCM_CPU_PART_BRAHMA_B53 0x100 +#define BRCM_CPU_PART_VULCAN 0x516 + +#define QCOM_CPU_PART_FALKOR_V1 0x800 +#define QCOM_CPU_PART_FALKOR 0xc00 +#define QCOM_CPU_PART_KRYO 0x200 +#define QCOM_CPU_PART_KRYO_2XX_GOLD 0x800 +#define QCOM_CPU_PART_KRYO_2XX_SILVER 0x801 +#define QCOM_CPU_PART_KRYO_3XX_SILVER 0x803 +#define QCOM_CPU_PART_KRYO_4XX_GOLD 0x804 +#define QCOM_CPU_PART_KRYO_4XX_SILVER 0x805 + +#define NVIDIA_CPU_PART_DENVER 0x003 +#define NVIDIA_CPU_PART_CARMEL 0x004 + +#define FUJITSU_CPU_PART_A64FX 0x001 + +#define HISI_CPU_PART_TSV110 0xd01 + +#define APPLE_CPU_PART_M1_ICESTORM 0x022 +#define APPLE_CPU_PART_M1_FIRESTORM 0x023 +#define APPLE_CPU_PART_M1_ICESTORM_PRO 0x024 +#define APPLE_CPU_PART_M1_FIRESTORM_PRO 0x025 +#define APPLE_CPU_PART_M1_ICESTORM_MAX 0x028 +#define APPLE_CPU_PART_M1_FIRESTORM_MAX 0x029 +#define APPLE_CPU_PART_M2_BLIZZARD 0x032 +#define APPLE_CPU_PART_M2_AVALANCHE 0x033 +#define APPLE_CPU_PART_M2_BLIZZARD_PRO 0x034 +#define APPLE_CPU_PART_M2_AVALANCHE_PRO 0x035 +#define APPLE_CPU_PART_M2_BLIZZARD_MAX 0x038 +#define APPLE_CPU_PART_M2_AVALANCHE_MAX 0x039 + +#define AMPERE_CPU_PART_AMPERE1 0xac3 + +#define MIDR_CORTEX_A53 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A53) +#define MIDR_CORTEX_A57 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A57) +#define MIDR_CORTEX_A72 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A72) +#define MIDR_CORTEX_A73 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A73) +#define MIDR_CORTEX_A75 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A75) +#define MIDR_CORTEX_A35 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A35) +#define MIDR_CORTEX_A55 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A55) +#define MIDR_CORTEX_A76 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A76) +#define MIDR_NEOVERSE_N1 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_NEOVERSE_N1) +#define MIDR_CORTEX_A77 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A77) +#define MIDR_NEOVERSE_V1 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_NEOVERSE_V1) +#define MIDR_CORTEX_A78 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A78) +#define MIDR_CORTEX_A78AE MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A78AE) +#define MIDR_CORTEX_X1 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_X1) +#define MIDR_CORTEX_A510 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A510) +#define MIDR_CORTEX_A710 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A710) +#define MIDR_CORTEX_A715 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A715) +#define MIDR_CORTEX_X2 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_X2) +#define MIDR_NEOVERSE_N2 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_NEOVERSE_N2) +#define MIDR_CORTEX_A78C MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A78C) +#define MIDR_THUNDERX MIDR_CPU_MODEL(ARM_CPU_IMP_CAVIUM, CAVIUM_CPU_PART_THUNDERX) +#define MIDR_THUNDERX_81XX MIDR_CPU_MODEL(ARM_CPU_IMP_CAVIUM, CAVIUM_CPU_PART_THUNDERX_81XX) +#define MIDR_THUNDERX_83XX MIDR_CPU_MODEL(ARM_CPU_IMP_CAVIUM, CAVIUM_CPU_PART_THUNDERX_83XX) +#define MIDR_OCTX2_98XX MIDR_CPU_MODEL(ARM_CPU_IMP_CAVIUM, CAVIUM_CPU_PART_OCTX2_98XX) +#define MIDR_OCTX2_96XX MIDR_CPU_MODEL(ARM_CPU_IMP_CAVIUM, CAVIUM_CPU_PART_OCTX2_96XX) +#define MIDR_OCTX2_95XX MIDR_CPU_MODEL(ARM_CPU_IMP_CAVIUM, CAVIUM_CPU_PART_OCTX2_95XX) +#define MIDR_OCTX2_95XXN MIDR_CPU_MODEL(ARM_CPU_IMP_CAVIUM, CAVIUM_CPU_PART_OCTX2_95XXN) +#define MIDR_OCTX2_95XXMM MIDR_CPU_MODEL(ARM_CPU_IMP_CAVIUM, CAVIUM_CPU_PART_OCTX2_95XXMM) +#define MIDR_OCTX2_95XXO MIDR_CPU_MODEL(ARM_CPU_IMP_CAVIUM, CAVIUM_CPU_PART_OCTX2_95XXO) +#define MIDR_CAVIUM_THUNDERX2 MIDR_CPU_MODEL(ARM_CPU_IMP_CAVIUM, CAVIUM_CPU_PART_THUNDERX2) +#define MIDR_BRAHMA_B53 MIDR_CPU_MODEL(ARM_CPU_IMP_BRCM, BRCM_CPU_PART_BRAHMA_B53) +#define MIDR_BRCM_VULCAN MIDR_CPU_MODEL(ARM_CPU_IMP_BRCM, BRCM_CPU_PART_VULCAN) +#define MIDR_QCOM_FALKOR_V1 MIDR_CPU_MODEL(ARM_CPU_IMP_QCOM, QCOM_CPU_PART_FALKOR_V1) +#define MIDR_QCOM_FALKOR MIDR_CPU_MODEL(ARM_CPU_IMP_QCOM, QCOM_CPU_PART_FALKOR) +#define MIDR_QCOM_KRYO MIDR_CPU_MODEL(ARM_CPU_IMP_QCOM, QCOM_CPU_PART_KRYO) +#define MIDR_QCOM_KRYO_2XX_GOLD MIDR_CPU_MODEL(ARM_CPU_IMP_QCOM, QCOM_CPU_PART_KRYO_2XX_GOLD) +#define MIDR_QCOM_KRYO_2XX_SILVER MIDR_CPU_MODEL(ARM_CPU_IMP_QCOM, QCOM_CPU_PART_KRYO_2XX_SILVER) +#define MIDR_QCOM_KRYO_3XX_SILVER MIDR_CPU_MODEL(ARM_CPU_IMP_QCOM, QCOM_CPU_PART_KRYO_3XX_SILVER) +#define MIDR_QCOM_KRYO_4XX_GOLD MIDR_CPU_MODEL(ARM_CPU_IMP_QCOM, QCOM_CPU_PART_KRYO_4XX_GOLD) +#define MIDR_QCOM_KRYO_4XX_SILVER MIDR_CPU_MODEL(ARM_CPU_IMP_QCOM, QCOM_CPU_PART_KRYO_4XX_SILVER) +#define MIDR_NVIDIA_DENVER MIDR_CPU_MODEL(ARM_CPU_IMP_NVIDIA, NVIDIA_CPU_PART_DENVER) +#define MIDR_NVIDIA_CARMEL MIDR_CPU_MODEL(ARM_CPU_IMP_NVIDIA, NVIDIA_CPU_PART_CARMEL) +#define MIDR_FUJITSU_A64FX MIDR_CPU_MODEL(ARM_CPU_IMP_FUJITSU, FUJITSU_CPU_PART_A64FX) +#define MIDR_HISI_TSV110 MIDR_CPU_MODEL(ARM_CPU_IMP_HISI, HISI_CPU_PART_TSV110) +#define MIDR_APPLE_M1_ICESTORM MIDR_CPU_MODEL(ARM_CPU_IMP_APPLE, APPLE_CPU_PART_M1_ICESTORM) +#define MIDR_APPLE_M1_FIRESTORM MIDR_CPU_MODEL(ARM_CPU_IMP_APPLE, APPLE_CPU_PART_M1_FIRESTORM) +#define MIDR_APPLE_M1_ICESTORM_PRO MIDR_CPU_MODEL(ARM_CPU_IMP_APPLE, APPLE_CPU_PART_M1_ICESTORM_PRO) +#define MIDR_APPLE_M1_FIRESTORM_PRO MIDR_CPU_MODEL(ARM_CPU_IMP_APPLE, APPLE_CPU_PART_M1_FIRESTORM_PRO) +#define MIDR_APPLE_M1_ICESTORM_MAX MIDR_CPU_MODEL(ARM_CPU_IMP_APPLE, APPLE_CPU_PART_M1_ICESTORM_MAX) +#define MIDR_APPLE_M1_FIRESTORM_MAX MIDR_CPU_MODEL(ARM_CPU_IMP_APPLE, APPLE_CPU_PART_M1_FIRESTORM_MAX) +#define MIDR_APPLE_M2_BLIZZARD MIDR_CPU_MODEL(ARM_CPU_IMP_APPLE, APPLE_CPU_PART_M2_BLIZZARD) +#define MIDR_APPLE_M2_AVALANCHE MIDR_CPU_MODEL(ARM_CPU_IMP_APPLE, APPLE_CPU_PART_M2_AVALANCHE) +#define MIDR_APPLE_M2_BLIZZARD_PRO MIDR_CPU_MODEL(ARM_CPU_IMP_APPLE, APPLE_CPU_PART_M2_BLIZZARD_PRO) +#define MIDR_APPLE_M2_AVALANCHE_PRO MIDR_CPU_MODEL(ARM_CPU_IMP_APPLE, APPLE_CPU_PART_M2_AVALANCHE_PRO) +#define MIDR_APPLE_M2_BLIZZARD_MAX MIDR_CPU_MODEL(ARM_CPU_IMP_APPLE, APPLE_CPU_PART_M2_BLIZZARD_MAX) +#define MIDR_APPLE_M2_AVALANCHE_MAX MIDR_CPU_MODEL(ARM_CPU_IMP_APPLE, APPLE_CPU_PART_M2_AVALANCHE_MAX) +#define MIDR_AMPERE1 MIDR_CPU_MODEL(ARM_CPU_IMP_AMPERE, AMPERE_CPU_PART_AMPERE1) +#define MIDR_QEMU MIDR_CPU_MODEL(0, 'Q') + /* GIC registers */ #define ICC_IAR0_SYS sysreg_64(0, c12, c8, 0) #define ICC_IAR1_SYS sysreg_64(0, c12, c12, 0) diff --git a/libcpu/aarch64/common/cpuport.h b/libcpu/aarch64/common/cpuport.h index 2cc4e0b1fd87..282114f93d03 100644 --- a/libcpu/aarch64/common/cpuport.h +++ b/libcpu/aarch64/common/cpuport.h @@ -44,4 +44,35 @@ typedef union { #define sysreg_read(sysreg, val) \ __asm__ volatile ("mrs %0, "RT_STRINGIFY(sysreg)"":"=r"((val))) +rt_inline unsigned long __rt_clz(unsigned long word) +{ +#ifdef __GNUC__ + return __builtin_clz(word); +#else + unsigned long val; + + __asm__ volatile ("clz %0, %1" + :"=r"(val) + :"r"(word)); + + return val; +#endif +} + +rt_inline unsigned long __rt_ffsl(unsigned long word) +{ +#ifdef __GNUC__ + return __builtin_ffsl(word); +#else + if (!word) + { + return 0; + } + + __asm__ volatile ("rbit %0, %0" : "+r" (word)); + + return __rt_clz(word); +#endif +} + #endif /*CPUPORT_H__*/ diff --git a/libcpu/aarch64/common/mmu.c b/libcpu/aarch64/common/mmu.c index 6152428051cc..ceff4f799a31 100644 --- a/libcpu/aarch64/common/mmu.c +++ b/libcpu/aarch64/common/mmu.c @@ -489,10 +489,14 @@ int rt_hw_mmu_map_init(rt_aspace_t aspace, void *v_address, size_t size, void mmu_tcr_init(void) { unsigned long val64; + unsigned long pa_range; val64 = 0x00447fUL; __asm__ volatile("msr MAIR_EL1, %0\n dsb sy\n" ::"r"(val64)); + __asm__ volatile ("mrs %0, ID_AA64MMFR0_EL1":"=r"(val64)); + pa_range = val64 & 0xf; /* PARange */ + /* TCR_EL1 */ val64 = (16UL << 0) /* t0sz 48bit */ | (0x0UL << 6) /* reserved */ @@ -508,7 +512,7 @@ void mmu_tcr_init(void) | (0x3UL << 26) /* t1 outer wb cacheable */ | (0x2UL << 28) /* t1 outer shareable */ | (0x2UL << 30) /* t1 4k */ - | (0x1UL << 32) /* 001b 64GB PA */ + | (pa_range << 32) /* PA range */ | (0x0UL << 35) /* reserved */ | (0x1UL << 36) /* as: 0:8bit 1:16bit */ | (0x0UL << 37) /* tbi0 */ @@ -584,26 +588,34 @@ static int _map_single_page_2M(unsigned long *lv0_tbl, unsigned long va, void *rt_ioremap_early(void *paddr, size_t size) { - static void *ttbr0 = RT_NULL; + size_t count; + rt_ubase_t base; + static void *tbl = RT_NULL; if (!size) { return RT_NULL; } - if (!ttbr0) + if (!tbl) { - __asm__ volatile ("mrs %0, ttbr0_el1":"=r"(ttbr0)); + tbl = rt_hw_mmu_tbl_get(); } - _map_single_page_2M(ttbr0, (unsigned long)paddr, (unsigned long)paddr, MMU_MAP_K_DEVICE); + count = (size + ARCH_SECTION_MASK) >> ARCH_SECTION_SHIFT; + base = (rt_ubase_t)paddr & (~ARCH_SECTION_MASK); - return paddr; -} + while (count --> 0) + { + if (_map_single_page_2M(tbl, base, base, MMU_MAP_K_DEVICE)) + { + return RT_NULL; + } -void rt_iounmap_early(void *vaddr, size_t size) -{ + base += ARCH_SECTION_SIZE; + } + return paddr; } static int _init_map_2M(unsigned long *lv0_tbl, unsigned long va, diff --git a/libcpu/aarch64/common/setup.c b/libcpu/aarch64/common/setup.c index d1fe9d305d5a..47f6f096a386 100644 --- a/libcpu/aarch64/common/setup.c +++ b/libcpu/aarch64/common/setup.c @@ -21,10 +21,17 @@ #ifdef RT_USING_DFS #include +#ifdef RT_USING_DFS_DIRECTFS +#include +#endif +#ifdef RT_USING_FINSH +#include +#endif #endif #ifdef RT_USING_SMART #include #endif +#include #include #include #include @@ -33,6 +40,8 @@ #include #include +#define phys_to_virt(pa) ((pa) - PV_OFFSET) + extern void _secondary_cpu_entry(void); extern void rt_hw_builtin_fdt(); @@ -76,14 +85,95 @@ void rt_hw_fdt_install_early(void *fdt) } } +static void read_cpuid(char *ids, rt_uint64_t midr) +{ + const char *id; +#define IS_CPU(ID, NAME) case MIDR_##ID: id = NAME; break + switch (midr & ~(MIDR_VARIANT_MASK | MIDR_REVISION_MASK)) + { + IS_CPU(CORTEX_A53, "Cortex-A53"); + IS_CPU(CORTEX_A57, "Cortex-A57"); + IS_CPU(CORTEX_A72, "Cortex-A72"); + IS_CPU(CORTEX_A73, "Cortex-A73"); + IS_CPU(CORTEX_A75, "Cortex-A75"); + IS_CPU(CORTEX_A35, "Cortex-A35"); + IS_CPU(CORTEX_A55, "Cortex-A55"); + IS_CPU(CORTEX_A76, "Cortex-A76"); + IS_CPU(NEOVERSE_N1, "Neoverse-N1"); + IS_CPU(CORTEX_A77, "Cortex-A77"); + IS_CPU(NEOVERSE_V1, "Neoverse-V1"); + IS_CPU(CORTEX_A78, "Cortex-A78"); + IS_CPU(CORTEX_A78AE, "Cortex-A78AE"); + IS_CPU(CORTEX_X1, "Cortex-X1"); + IS_CPU(CORTEX_A510, "Cortex-A510"); + IS_CPU(CORTEX_A710, "Cortex-A710"); + IS_CPU(CORTEX_A715, "Cortex-A715"); + IS_CPU(CORTEX_X2, "Cortex-X2"); + IS_CPU(NEOVERSE_N2, "Neoverse-N2"); + IS_CPU(CORTEX_A78C, "Cortex-A78C"); + IS_CPU(THUNDERX, "Thunderx"); + IS_CPU(THUNDERX_81XX, "Thunderx-81XX"); + IS_CPU(THUNDERX_83XX, "Thunderx-83XX"); + IS_CPU(OCTX2_98XX, "Octx2-98XX"); + IS_CPU(OCTX2_96XX, "Octx2-96XX"); + IS_CPU(OCTX2_95XX, "Octx2-95XX"); + IS_CPU(OCTX2_95XXN, "Octx2-95XXN"); + IS_CPU(OCTX2_95XXMM, "Octx2-95XXMM"); + IS_CPU(OCTX2_95XXO, "Octx2-95XXO"); + IS_CPU(CAVIUM_THUNDERX2, "Cavium-Thunderx2"); + IS_CPU(BRAHMA_B53, "Brahma-B53"); + IS_CPU(BRCM_VULCAN, "Brcm-Vulcan"); + IS_CPU(QCOM_FALKOR, "Qcom-Falkor"); + IS_CPU(QCOM_KRYO, "Qcom-Kryo"); + IS_CPU(QCOM_KRYO_2XX_GOLD, "Qcom-Kryo-2XX-Gold"); + IS_CPU(QCOM_KRYO_2XX_SILVER, "Qcom-Kryo-2XX-Silver"); + IS_CPU(QCOM_KRYO_3XX_SILVER, "qcom-Kryo-3XX-Silver"); + IS_CPU(QCOM_KRYO_4XX_GOLD, "Qcom-Kryo-4XX-Gold"); + IS_CPU(QCOM_KRYO_4XX_SILVER, "Qcom-Kryo-4XX-Silver"); + IS_CPU(NVIDIA_DENVER, "Nvidia_Denver"); + IS_CPU(NVIDIA_CARMEL, "Nvidia_Carmel"); + IS_CPU(FUJITSU_A64FX, "Fujitsu_A64FX"); + IS_CPU(HISI_TSV110, "Hisi_Tsv110"); + IS_CPU(APPLE_M1_ICESTORM, "Apple-M1-Icestorm"); + IS_CPU(APPLE_M1_FIRESTORM, "Apple-M1-Firestorm"); + IS_CPU(APPLE_M1_ICESTORM_PRO, "Apple-M1-Icestorm-Pro"); + IS_CPU(APPLE_M1_FIRESTORM_PRO, "Apple-M1-Firestorm-Pro"); + IS_CPU(APPLE_M1_ICESTORM_MAX, "Apple-M1-Icestorm-Max"); + IS_CPU(APPLE_M1_FIRESTORM_MAX, "Apple-M1-Firestorm-Max"); + IS_CPU(APPLE_M2_BLIZZARD, "Apple-M2-Blizzard"); + IS_CPU(APPLE_M2_AVALANCHE, "Apple-M2-Avalanche"); + IS_CPU(APPLE_M2_BLIZZARD_PRO, "Apple-M2-Blizzard-Pro"); + IS_CPU(APPLE_M2_AVALANCHE_PRO, "Apple-M2-Avalanche-Pro"); + IS_CPU(APPLE_M2_BLIZZARD_MAX, "Apple-M2-Blizzard-Max"); + IS_CPU(APPLE_M2_AVALANCHE_MAX, "Apple-M2-Avalanche-Max"); + IS_CPU(AMPERE1, "Ampere1"); + IS_CPU(QEMU, "Qemu"); + default: + id = "ARMv8"; + break; + } + + if (midr & (MIDR_VARIANT_MASK | MIDR_REVISION_MASK)) + { + rt_sprintf(ids, "%s v%dr%d", id, + (midr & MIDR_VARIANT_MASK) >> MIDR_VARIANT_SHIFT, + (midr & MIDR_REVISION_MASK) >> MIDR_REVISION_SHIFT); + } + else + { + rt_sprintf(ids, "%s", id); + } +#undef IS_CPU +} + rt_err_t rt_fdt_boot_dump(void) { - rt_uint64_t mpidr, midr_el1; + rt_uint64_t mpidr, midr; sysreg_read(mpidr_el1, mpidr); - sysreg_read(midr_el1, midr_el1); + sysreg_read(midr_el1, midr); - LOG_I("Booting RT-Thread on physical CPU 0x%010x [0x%08x]", mpidr & MPIDR_AFFINITY_MASK, midr_el1); + LOG_I("Booting RT-Thread on physical CPU 0x%010x [0x%08x]", mpidr & MPIDR_AFFINITY_MASK, midr); return RT_EOK; } @@ -93,6 +183,68 @@ void rt_hw_console_output(const char *str) rt_fdt_earlycon_output(str); } +#ifdef RT_USING_HWTIMER +static rt_ubase_t loops_per_tick[RT_CPUS_NR]; + +static rt_ubase_t cpu_get_cycles(void) +{ + rt_ubase_t cycles; + + sysreg_read(cntpct_el0, cycles); + + return cycles; +} + +static void cpu_loops_per_tick_init(void) +{ + rt_ubase_t offset; + volatile rt_ubase_t freq, step, cycles_end1, cycles_end2; + volatile rt_uint32_t cycles_count1 = 0, cycles_count2 = 0; + + sysreg_read(cntfrq_el0, freq); + step = freq / RT_TICK_PER_SECOND; + + cycles_end1 = cpu_get_cycles() + step; + + while (cpu_get_cycles() < cycles_end1) + { + __asm__ volatile ("nop"); + __asm__ volatile ("add %0, %0, #1":"=r"(cycles_count1)); + } + + cycles_end2 = cpu_get_cycles() + step; + + while (cpu_get_cycles() < cycles_end2) + { + __asm__ volatile ("add %0, %0, #1":"=r"(cycles_count2)); + } + + if ((rt_int32_t)(cycles_count2 - cycles_count1) > 0) + { + offset = cycles_count2 - cycles_count1; + } + else + { + /* Impossible, but prepared for any eventualities */ + offset = cycles_count2 / 4; + } + + loops_per_tick[rt_hw_cpu_id()] = offset; +} + +static void cpu_us_delay(rt_uint32_t us) +{ + volatile rt_base_t start = cpu_get_cycles(), cycles; + + cycles = ((us * 0x10c7UL) * loops_per_tick[rt_hw_cpu_id()] * RT_TICK_PER_SECOND) >> 32; + + while ((cpu_get_cycles() - start) < cycles) + { + rt_hw_cpu_relax(); + } +} +#endif /* RT_USING_HWTIMER */ + rt_weak void rt_hw_idle_wfi(void) { __asm__ volatile ("wfi"); @@ -103,14 +255,19 @@ static void system_vectors_init(void) rt_hw_set_current_vbar((rt_ubase_t)&system_vectors); } -static void cpu_info_init(void) +rt_inline void cpu_info_init(void) { int i = 0; - rt_uint64_t mpidr; + char cpuid[32]; + rt_uint64_t mpidr, midr; struct rt_ofw_node *np; /* get boot cpu info */ sysreg_read(mpidr_el1, mpidr); + sysreg_read(midr_el1, midr); + + read_cpuid(cpuid, midr); + LOG_I("Boot Processor ID: %s", cpuid); rt_ofw_foreach_cpu_node(np) { @@ -150,6 +307,56 @@ static void cpu_info_init(void) } rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, rt_cpu_mpidr_table, sizeof(rt_cpu_mpidr_table)); + +#ifdef RT_USING_HWTIMER + cpu_loops_per_tick_init(); + + if (!rt_device_hwtimer_us_delay) + { + rt_device_hwtimer_us_delay = &cpu_us_delay; + } +#endif /* RT_USING_HWTIMER */ +} + +rt_inline void directfs_init(void) +{ +#ifdef RT_USING_DFS_DIRECTFS + static char *directfs_root[] = + { + "bus", + "firmware", + }; + static struct rt_object objs[RT_ARRAY_SIZE(directfs_root)]; + + for (int i = 0; i < RT_ARRAY_SIZE(directfs_root); ++i) + { + dfs_directfs_create_link(RT_NULL, &objs[i], directfs_root[i]); + } +#endif +} + +rt_inline rt_bool_t is_kernel_aspace(const char *name) +{ + static char * const names[] = + { + "kernel", + "memheap", + }; + + if (!name) + { + return RT_FALSE; + } + + for (int i = 0; i < RT_ARRAY_SIZE(names); ++i) + { + if (!rt_strcmp(names[i], name)) + { + return RT_TRUE; + } + } + + return RT_FALSE; } void rt_hw_common_setup(void) @@ -160,6 +367,7 @@ void rt_hw_common_setup(void) rt_region_t *mem_region; rt_size_t page_best_start; rt_region_t platform_mem_region; + struct rt_ofw_node *ofw_chosen_node; static struct mem_desc platform_mem_desc; void *kernel_start, *kernel_end, *memheap_start = RT_NULL, *memheap_end = RT_NULL; @@ -203,19 +411,19 @@ void rt_hw_common_setup(void) #else /* * The bootloader doesn't know the region of our memheap so maybe load the - * device tree to the memheap, it's safety to move to the end of memheap + * device tree to the memheap, it's safety to move to the end of the memheap */ if (memheap_end && fdt_ptr > kernel_start) { - rt_memmove(memheap_end, fdt_ptr - PV_OFFSET, fdt_size); + rt_memmove(phys_to_virt(memheap_end), phys_to_virt(fdt_ptr), fdt_size); fdt_ptr = memheap_end; - /* Now, we use page in the end of fdt */ + /* Now, we use the page in the end of the fdt */ page_best_start = (rt_size_t)fdt_ptr + fdt_size; } - /* Reserved fdt region */ + /* Reserved the fdt region */ rt_fdt_commit_memregion_early(&(rt_region_t) { .name = "fdt", @@ -223,12 +431,11 @@ void rt_hw_common_setup(void) .end = (rt_size_t)(fdt_ptr + fdt_size), }, RT_TRUE); - /* Fixup fdt pointer to kernel address space */ - fdt_ptr -= PV_OFFSET; - + /* Fixup the fdt pointer to kernel address space */ + fdt_ptr = phys_to_virt(fdt_ptr); #endif /* !RT_USING_BUILTIN_FDT */ - /* Reserved kernel image region */ + /* Reserved the kernel image region */ rt_fdt_commit_memregion_early(&(rt_region_t) { .name = "kernel", @@ -250,13 +457,13 @@ void rt_hw_common_setup(void) /* Reserved your memory block before here */ rt_fdt_scan_memory(); - /* Init system memheap */ + /* Init the system memheap */ if (memheap_start && memheap_end) { - rt_system_heap_init(memheap_start - PV_OFFSET, memheap_end - PV_OFFSET); + rt_system_heap_init(phys_to_virt(memheap_start), phys_to_virt(memheap_end)); } - /* Find SoC memory limit */ + /* Find the SoC memory limit */ platform_mem_region.start = ~0UL; platform_mem_region.end = 0; @@ -266,14 +473,17 @@ void rt_hw_common_setup(void) while (mem_region_nr --> 0) { - if (platform_mem_region.start > mem_region->start) - { - platform_mem_region.start = mem_region->start; - } - - if (platform_mem_region.end < mem_region->end) + if (is_kernel_aspace(mem_region->name)) { - platform_mem_region.end = mem_region->end; + if (platform_mem_region.start > mem_region->start) + { + platform_mem_region.start = mem_region->start; + } + + if (platform_mem_region.end < mem_region->end) + { + platform_mem_region.end = mem_region->end; + } } LOG_I(" %-*.s [%p, %p]", RT_NAME_MAX, mem_region->name, mem_region->start, mem_region->end); @@ -289,7 +499,7 @@ void rt_hw_common_setup(void) LOG_I("Usable memory:"); - /* Now, we will find the best page region by for each usable memory */ + /* Now, we will find the best page region by for each the usable memory */ for (int i = 0; i < mem_region_nr; ++i, ++mem_region) { if (!mem_region->name) @@ -320,50 +530,59 @@ void rt_hw_common_setup(void) LOG_I(" %-*.s [%p, %p]", RT_NAME_MAX, mem_region->name, mem_region->start, mem_region->end); } - /* Init kernel page pool */ + /* Init the kernel page pool */ RT_ASSERT(page_region != RT_NULL); - init_page_region.start = page_region->start - PV_OFFSET; - init_page_region.end = page_region->end - PV_OFFSET; + init_page_region.start = phys_to_virt(page_region->start); + init_page_region.end = phys_to_virt(page_region->end); rt_page_init(init_page_region); - /* Init mmu address space config */ + /* Init the mmu address space config */ + platform_mem_region.start = RT_ALIGN(platform_mem_region.start, ARCH_PAGE_SIZE); + platform_mem_region.end = RT_ALIGN_DOWN(platform_mem_region.end, ARCH_PAGE_SIZE); RT_ASSERT(platform_mem_region.end - platform_mem_region.start != 0); platform_mem_desc.paddr_start = platform_mem_region.start; - platform_mem_desc.vaddr_start = platform_mem_region.start - PV_OFFSET; - platform_mem_desc.vaddr_end = platform_mem_region.end - PV_OFFSET - 1; + platform_mem_desc.vaddr_start = phys_to_virt(platform_mem_region.start); + platform_mem_desc.vaddr_end = phys_to_virt(platform_mem_region.end) - 1; platform_mem_desc.attr = NORMAL_MEM; + rt_hw_mmu_setup(&rt_kernel_space, &platform_mem_desc, 1); /* MMU config was changed, update the mmio map in earlycon */ rt_fdt_earlycon_kick(FDT_EARLYCON_KICK_UPDATE); - /* Install all usable memory to memory system */ + /* Install all usable memory into memory system */ mem_region = usable_mem_region; for (int i = 0; i < mem_region_nr; ++i, ++mem_region) { - if (mem_region != page_region) + if (mem_region != page_region && mem_region->name) { rt_page_install(*mem_region); } } } + directfs_init(); + rt_fdt_unflatten(); cpu_info_init(); - /* Init hardware interrupt */ + /* Init the hardware interrupt */ rt_pic_init(); rt_hw_interrupt_init(); - if (!rt_ofw_prop_read_string(rt_ofw_find_node_by_path("/chosen"), "bootargs", &bootargs)) + ofw_chosen_node = rt_ofw_find_node_by_path("/chosen"); + + if (!rt_ofw_prop_read_string(ofw_chosen_node, "bootargs", &bootargs)) { LOG_I("Command line: %s", bootargs); } + rt_ofw_node_put(ofw_chosen_node); + #ifdef RT_USING_COMPONENTS_INIT rt_components_board_init(); #endif @@ -375,7 +594,7 @@ void rt_hw_common_setup(void) rt_thread_idle_sethook(rt_hw_idle_wfi); #ifdef RT_USING_SMP - /* install IPI handle */ + /* Install the IPI handle */ rt_hw_ipi_handler_install(RT_SCHEDULE_IPI, rt_scheduler_ipi_handler); rt_hw_ipi_handler_install(RT_STOP_IPI, rt_scheduler_ipi_handler); rt_hw_interrupt_umask(RT_SCHEDULE_IPI); @@ -387,8 +606,10 @@ void rt_hw_common_setup(void) static int rootfs_mnt_init(void) { void *fsdata = RT_NULL; + const char *cpio_type = "cpio"; const char *dev = rt_ofw_bootargs_select("root=", 0); const char *fstype = rt_ofw_bootargs_select("rootfstype=", 0); + const char *rw = rt_ofw_bootargs_select("rw", 0); if ((!dev || !fstype) && initrd_ranges[0] && initrd_ranges[1]) { @@ -399,26 +620,111 @@ static int rootfs_mnt_init(void) if (fsdata) { - fstype = "cpio"; - initrd_ranges[3] = (rt_uint64_t)fsdata; + fstype = cpio_type; + initrd_ranges[2] = (rt_uint64_t)fsdata; + } + } + + if (fstype != cpio_type && dev) + { + rt_tick_t timeout = 0; + const char *rootwait, *rootdelay = RT_NULL; + + rootwait = rt_ofw_bootargs_select("rootwait", 0); + + /* Maybe it is undefined or 'rootwaitABC' */ + if (!rootwait || *rootwait) + { + rootdelay = rt_ofw_bootargs_select("rootdelay=", 0); + + if (rootdelay) + { + timeout = rt_tick_from_millisecond(atoi(rootdelay)); + } + + rootwait = RT_NULL; + } + + /* + * Delays in boot flow is a terrible behavior in RTOS, but the RT-Thread + * SDIO framework init the devices in a task that we need to wait for + * SDIO devices to init complete... + * + * WHAT THE F*CK PROBLEMS WILL HAPPENED? + * + * Your main PE, applications, services that depend on the root FS and + * the multi cores setup, init will delay, too... + * + * So, you can try to link this function to `INIT_APP_EXPORT` even later + * and remove the delays if you want to optimize the boot time and mount + * the FS auto. + */ + for (; rootdelay || rootwait; --timeout) + { + if (!rootwait && timeout == 0) + { + LOG_E("Wait for /dev/%s init time out", dev); + + /* + * We don't return at once because the device driver may init OK + * when we break from this point, might as well give it another + * try. + */ + break; + } + + if (rt_device_find(dev)) + { + break; + } + + rt_thread_mdelay(1); } } if (fstype) { - if (!dfs_mount(dev, "/", fstype, 0, fsdata)) + if (!dfs_mount(dev, "/", fstype, rw ? 0 : ~0, fsdata)) { - LOG_I("Mount root %s%s type=%s %s", dev ? "on /dev/" : "", dev ? dev : "", fstype, "done"); + LOG_I("Mount root %s%s type=%s %s", + (dev && *dev) ? "on /dev/" : "", + (dev && *dev) ? dev : "\b", + fstype, "done"); } else { - LOG_W("Mount root %s%s type=%s %s", dev ? "on /dev/" : "", dev ? dev : "", fstype, "fail"); + LOG_W("Mount root %s%s type=%s %s", + (dev && *dev) ? "on /dev/" : "", + (dev && *dev) ? dev : "\b", + fstype, "fail"); } } return 0; } INIT_ENV_EXPORT(rootfs_mnt_init); + +static int fstab_mnt_init(void) +{ +#ifdef RT_USING_DFS_DIRECTFS + mkdir("/direct", 0755); + + if (!dfs_mount(RT_NULL, "/direct", "direct", 0, 0)) + { + LOG_I("Mount %s %s%s type=%s %s", "direct", "/direct", "", "direct", "done"); + } +#endif + + mkdir("/mnt", 0755); + +#ifdef RT_USING_FINSH + /* Try mount by table */ + msh_exec_script("fstab.sh", 16); +#endif + + return 0; +} +INIT_FS_EXPORT(fstab_mnt_init); #endif /* RT_USING_DFS */ #ifdef RT_USING_SMP @@ -460,13 +766,15 @@ rt_weak void rt_hw_secondary_cpu_up(void) if (err) { - LOG_W("Call cpu %d on %s", i, "failed"); + LOG_W("Call CPU%d on failed", i); } } } rt_weak void secondary_cpu_c_start(void) { + char cpuid[32]; + rt_uint64_t midr; int cpu_id = rt_hw_cpu_id(); system_vectors_init(); @@ -475,6 +783,7 @@ rt_weak void secondary_cpu_c_start(void) /* Save all mpidr */ sysreg_read(mpidr_el1, rt_cpu_mpidr_table[cpu_id]); + sysreg_read(midr_el1, midr); rt_hw_mmu_ktbl_set((unsigned long)MMUTable); @@ -484,7 +793,15 @@ rt_weak void secondary_cpu_c_start(void) rt_hw_interrupt_umask(RT_SCHEDULE_IPI); rt_hw_interrupt_umask(RT_STOP_IPI); - LOG_I("Call cpu %d on %s", cpu_id, "success"); + read_cpuid(cpuid, midr); + LOG_I("Call CPU%d [%s] on success", cpu_id, cpuid); + +#ifdef RT_USING_HWTIMER + if (rt_device_hwtimer_us_delay == &cpu_us_delay) + { + cpu_loops_per_tick_init(); + } +#endif rt_system_scheduler_start(); } diff --git a/libcpu/aarch64/common/stack.c b/libcpu/aarch64/common/stack.c index eaa9a4d1588b..19b3e5cb00d9 100644 --- a/libcpu/aarch64/common/stack.c +++ b/libcpu/aarch64/common/stack.c @@ -31,38 +31,12 @@ rt_uint8_t *rt_hw_stack_init(void *tentry, void *parameter, stk = (rt_ubase_t *)stack_addr; - *(--stk) = (rt_ubase_t)0; /* Q0 */ - *(--stk) = (rt_ubase_t)0; /* Q0 */ - *(--stk) = (rt_ubase_t)0; /* Q1 */ - *(--stk) = (rt_ubase_t)0; /* Q1 */ - *(--stk) = (rt_ubase_t)0; /* Q2 */ - *(--stk) = (rt_ubase_t)0; /* Q2 */ - *(--stk) = (rt_ubase_t)0; /* Q3 */ - *(--stk) = (rt_ubase_t)0; /* Q3 */ - *(--stk) = (rt_ubase_t)0; /* Q4 */ - *(--stk) = (rt_ubase_t)0; /* Q4 */ - *(--stk) = (rt_ubase_t)0; /* Q5 */ - *(--stk) = (rt_ubase_t)0; /* Q5 */ - *(--stk) = (rt_ubase_t)0; /* Q6 */ - *(--stk) = (rt_ubase_t)0; /* Q6 */ - *(--stk) = (rt_ubase_t)0; /* Q7 */ - *(--stk) = (rt_ubase_t)0; /* Q7 */ - *(--stk) = (rt_ubase_t)0; /* Q8 */ - *(--stk) = (rt_ubase_t)0; /* Q8 */ - *(--stk) = (rt_ubase_t)0; /* Q9 */ - *(--stk) = (rt_ubase_t)0; /* Q9 */ - *(--stk) = (rt_ubase_t)0; /* Q10 */ - *(--stk) = (rt_ubase_t)0; /* Q10 */ - *(--stk) = (rt_ubase_t)0; /* Q11 */ - *(--stk) = (rt_ubase_t)0; /* Q11 */ - *(--stk) = (rt_ubase_t)0; /* Q12 */ - *(--stk) = (rt_ubase_t)0; /* Q12 */ - *(--stk) = (rt_ubase_t)0; /* Q13 */ - *(--stk) = (rt_ubase_t)0; /* Q13 */ - *(--stk) = (rt_ubase_t)0; /* Q14 */ - *(--stk) = (rt_ubase_t)0; /* Q14 */ - *(--stk) = (rt_ubase_t)0; /* Q15 */ - *(--stk) = (rt_ubase_t)0; /* Q15 */ + for (int i = 0; i < 32; ++i) + { + stk -= sizeof(rt_uint128_t) / sizeof(rt_ubase_t); + + *(rt_uint128_t *)stk = (rt_uint128_t) { 0 }; + } *(--stk) = (rt_ubase_t)1; /* X1 */ *(--stk) = (rt_ubase_t)parameter; /* X0 */ diff --git a/libcpu/aarch64/cortex-a/entry_point.S b/libcpu/aarch64/cortex-a/entry_point.S index 969b48230f15..3060f476d402 100644 --- a/libcpu/aarch64/cortex-a/entry_point.S +++ b/libcpu/aarch64/cortex-a/entry_point.S @@ -10,11 +10,36 @@ * 2023-06-04 GuEe-GUI support cpu init by device-tree */ +#ifndef __ASSEMBLY__ #define __ASSEMBLY__ +#endif #include #include +#define ARM64_IMAGE_FLAG_BE_SHIFT 0 +#define ARM64_IMAGE_FLAG_PAGE_SIZE_SHIFT (ARM64_IMAGE_FLAG_BE_SHIFT + 1) +#define ARM64_IMAGE_FLAG_PHYS_BASE_SHIFT (ARM64_IMAGE_FLAG_PAGE_SIZE_SHIFT + 2) + +#define ARM64_IMAGE_FLAG_LE 0 +#define ARM64_IMAGE_FLAG_BE 1 +#define ARM64_IMAGE_FLAG_PAGE_SIZE_4K 1 +#define ARM64_IMAGE_FLAG_PAGE_SIZE_16K 2 +#define ARM64_IMAGE_FLAG_PAGE_SIZE_64K 3 +#define ARM64_IMAGE_FLAG_PHYS_BASE 1 + +#define _HEAD_FLAG(field) (_HEAD_FLAG_##field << ARM64_IMAGE_FLAG_##field##_SHIFT) + +#ifdef ARCH_CPU_BIG_ENDIAN +#define _HEAD_FLAG_BE ARM64_IMAGE_FLAG_BE +#else +#define _HEAD_FLAG_BE ARM64_IMAGE_FLAG_LE +#endif +#define _HEAD_FLAG_PAGE_SIZE ((ARCH_PAGE_SHIFT - 10) / 2) +#define _HEAD_FLAG_PHYS_BASE 1 + +#define _HEAD_FLAGS (_HEAD_FLAG(BE) | _HEAD_FLAG(PAGE_SIZE) | _HEAD_FLAG(PHYS_BASE)) + .macro get_phy, reg, symbol adrp \reg, \symbol add \reg, \reg, #:lo12:\symbol @@ -31,14 +56,14 @@ /* * Our goal is to boot the rt-thread as possible without modifying the * bootloader's config, so we use the kernel's boot header for ARM64: - * https://www.kernel.org/doc/html/latest/arm64/booting.html#call-the-kernel-image + * https://www.kernel.org/doc/html/latest/arch/arm64/booting.html#call-the-kernel-image */ _head: b _start /* Executable code */ .long 0 /* Executable code */ .quad _text_offset /* Image load offset from start of RAM, little endian */ .quad _end - _head /* Effective Image size, little endian (_end defined in link.lds) */ - .quad 0xa /* Kernel flags, little endian */ + .quad _HEAD_FLAGS /* Kernel flags, little endian */ .quad 0 /* Reserved */ .quad 0 /* Reserved */ .quad 0 /* Reserved */ @@ -84,13 +109,9 @@ _start: b init_mmu_early kernel_start: - /* Set sp to current cpu's stack top to visual address */ - get_pvoff x1 x0 - mov x1, stack_top - add x1, x1, x0 - mov sp, x1 - /* jump to the PE's system entry */ + mov x29, xzr + mov x30, x8 br x8 cpu_idle: @@ -260,6 +281,15 @@ enable_mmu_early: bl mmu_tcr_init + /* + * OK, now, we don't use sp before jump to kernel, set sp to current cpu's + * stack top to visual address + */ + get_pvoff x1 x0 + mov x1, stack_top + sub x1, x1, x0 + mov sp, x1 + ldr x30, =kernel_start /* Set LR to kernel_start function, it's virtual addresses */ mrs x1, sctlr_el1 diff --git a/libcpu/aarch64/link.lds.S b/libcpu/aarch64/link.lds.S index 9b78fb0261a9..8fee814e5f38 100644 --- a/libcpu/aarch64/link.lds.S +++ b/libcpu/aarch64/link.lds.S @@ -126,6 +126,15 @@ SECTIONS PROVIDE(__dtors_end = .); } + .note.gnu : + { + #ifdef RT_USING_CRASH_CORE + PROVIDE(__build_id_start = .); + *(.note.gnu.build-id) + PROVIDE(__build_id_end = .); + #endif + } + . = ALIGN(16); .bss : { diff --git a/src/cpu.c b/src/cpu.c index 77ce65d69f05..def05cb9e18d 100644 --- a/src/cpu.c +++ b/src/cpu.c @@ -102,6 +102,32 @@ void rt_spin_lock(struct rt_spinlock *lock) } RTM_EXPORT(rt_spin_lock) +/** + * @brief This function will try to lock the spinlock. + * + * @note If the spinlock is locked, the current CPU will return error. + * + * @param lock is a pointer to the spinlock. + */ +rt_err_t rt_spin_trylock(struct rt_spinlock *lock) +{ + rt_err_t err; +#ifdef RT_USING_SMP + _cpu_preempt_disable(); + + if ((err = rt_hw_spin_trylock(&lock->lock))) + { + _cpu_preempt_enable(); + } +#else + rt_enter_critical(); + err = RT_EOK; +#endif + + return err; +} +RTM_EXPORT(rt_spin_trylock) + /** * @brief This function will unlock the spinlock. * @@ -145,6 +171,36 @@ rt_base_t rt_spin_lock_irqsave(struct rt_spinlock *lock) } RTM_EXPORT(rt_spin_lock_irqsave) +#ifdef RT_USING_SMP +/** + * @brief This function will try to lock the spinlock. + * + * @note If the spinlock is locked, the current CPU will return error. + * + * @param lock is a pointer to the spinlock. + */ +rt_err_t rt_spin_trylock_irqsave(struct rt_spinlock *lock, rt_ubase_t *out_level) +{ + rt_err_t res; + + rt_ubase_t level = rt_hw_local_irq_disable(); + + RT_ASSERT(out_level != RT_NULL); + res = rt_spin_trylock(lock); + + if (!res) + { + *out_level = level; + } + else + { + rt_hw_local_irq_enable(level); + } + + return res; +} +#endif + /** * @brief This function will unlock the spinlock and then restore current cpu interrupt status. * diff --git a/src/kservice.c b/src/kservice.c index 3ee2d1422bca..9e8e344a7c06 100644 --- a/src/kservice.c +++ b/src/kservice.c @@ -1565,8 +1565,9 @@ rt_device_t rt_console_set_device(const char *name) else { console_register("console", new_iodev); - _console_device = rt_device_find("console"); - rt_device_open(_console_device, RT_DEVICE_OFLAG_RDWR | RT_DEVICE_FLAG_STREAM); + new_iodev = rt_device_find("console"); + rt_device_open(new_iodev, RT_DEVICE_OFLAG_RDWR | RT_DEVICE_FLAG_STREAM); + _console_device = new_iodev; } } diff --git a/tools/building.py b/tools/building.py index 9d661ad2cda8..99ecc83cd1ca 100644 --- a/tools/building.py +++ b/tools/building.py @@ -802,7 +802,9 @@ def local_group(group, objects): CCFLAGS = Env.get('CCFLAGS', '') + group.get('LOCAL_CCFLAGS', '') CXXFLAGS = Env.get('CXXFLAGS', '') + group.get('LOCAL_CXXFLAGS', '') CPPPATH = Env.get('CPPPATH', ['']) + group.get('LOCAL_CPPPATH', ['']) - CPPDEFINES = Env.get('CPPDEFINES', ['']) + group.get('LOCAL_CPPDEFINES', ['']) + CPPDEFINES = Env.get('CPPDEFINES', ['']) + if len(group.get('LOCAL_CPPDEFINES', [''])) != 0: + CPPDEFINES += group.get('LOCAL_CPPDEFINES', ['']) ASFLAGS = Env.get('ASFLAGS', '') + group.get('LOCAL_ASFLAGS', '') for source in group['src']: diff --git a/tools/dtc.py b/tools/dtc.py index b80e279bebc8..0e63518b4e2c 100644 --- a/tools/dtc.py +++ b/tools/dtc.py @@ -25,4 +25,4 @@ def dts_to_dtb(RTT_ROOT, dts_list): dtb = path + dts.replace('.dts', '.dtb') dts = path + dts if not os.path.exists(dtb) or os.path.getmtime(dtb) < os.path.getmtime(dts): - os.system("\"{}\" -I dts -O dtb {} -o {}".format(dtc_cmd, dts, dtb)) + os.system("\"{}\" -I dts -O dtb -@ -A {} -o {}".format(dtc_cmd, dts, dtb))