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..76816f03e72d 100644 --- a/bsp/qemu-virt64-aarch64/applications/graphic.c +++ b/bsp/qemu-virt64-aarch64/applications/graphic.c @@ -1,14 +1,52 @@ -// /* -// * 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))) + { + err = rt_device_control(rom_fbdev, FBIOGET_FSCREENINFO, &fix); + } + + rt_device_close(rom_fbdev); + + return err; +} +INIT_SUBSYS_LATER_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..3e64fc5c6d65 100755 --- a/bsp/qemu-virt64-aarch64/qemu.py +++ b/bsp/qemu-virt64-aarch64/qemu.py @@ -1,84 +1,271 @@ #!/usr/bin/python # -*- coding: utf-8 -*- -import os,sys +import os, sys -opt=sys.argv +# 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) -graphic_cfg=""" \ - -serial stdio \ - -device virtio-gpu-device,xres=800,yres=600 \ +class QEMU_VERSION: + def __init__(self): + cmd = os.popen("qemu-system-aarch64 --version").readlines()[0] + version = cmd[cmd.find("version ") + 8: -1].split('.') + + self.major = version[0] + self.minor = version[1] + self.revision = version[2] + # == + def __eq__(self, version_in): + version = version_in.split('.') + return self.major == version[0] and self.minor == version[1] and self.revision == version[2] + # >= + def __ge__(self, version_in): + return self.__gt__(version_in) or self.__eq__(version_in) + # > + def __gt__(self, version_in): + version = version_in.split('.') + return self.major > version[0] or \ + (self.major == version[0] and self.minor > version[1]) or \ + (self.major == version[0] and self.minor == version[1] and self.revision > version[2]) + # <= + def __le__(self, version_in): + return self.__lt__(version_in) or self.__eq__(version_in) + # < + def __lt__(self, version_in): + return not self.__ge__(version_in) + # != + def __ne__(self, version_in): + return not self.__eq__(version_in) + +qemu_version = QEMU_VERSION() + +opt = sys.argv + +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 = "" +ufs_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 = "" +q_ufs = "ufs" 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] + if is_opt("ufs", inkey): q_ufs = 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] + +if qemu_version >= '8.2.0': + disk_list += [q_ufs] + ufs_cfg = """ \ + -drive if=none,file={}.qcow2,format=qcow2,id=ufs \ + -device ufs,serial=deadbeef \ + -device ufs-lu,drive=ufs \ + """.format(q_ufs) + +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, ufs_cfg)) 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..ac9e2cbd4e47 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" +config RT_USING_PM + bool "Using Power Management device drivers" default n -config 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 - -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 + if RT_USING_PM + config PM_TICKLESS_THRESHOLD_TIME + int "PM tickless threashold time" + default 2 - config RT_USING_QSPI - bool "Enable QSPI mode" + config PM_USING_CUSTOM_CONFIG + bool "PM using custom pm config" default n - config RT_USING_SPI_MSD - bool "Using SD/TF card driver with spi" - select RT_USING_DFS + config PM_ENABLE_DEBUG + bool "PM Enable Debug" default n - config RT_USING_SFUD - bool "Using Serial Flash Universal Driver" + config PM_ENABLE_SUSPEND_SLEEP_MODE + bool "PM Device suspend change sleep mode" 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 + 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 @@ -271,176 +147,6 @@ config RT_USING_TOUCH default n endif -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 +317,43 @@ 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/ivshmem/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/video/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..481b4d76f91c --- /dev/null +++ b/components/drivers/adc/adc-rockchip_saradc.c @@ -0,0 +1,526 @@ +/* + * 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); + + rt_dm_dev_bind_fwdata(dev, RT_NULL, rk_saradc); + + 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 rt_device *dev = &pdev->parent; + struct rockchip_saradc *rk_saradc = dev->user_data; + + rt_dm_dev_unbind_fwdata(dev, RT_NULL); + + 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..e9fb4c87c6a5 --- /dev/null +++ b/components/drivers/adc/adc-rp1.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-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); + + rt_dm_dev_bind_fwdata(dev, RT_NULL, 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 rt_device *dev = &pdev->parent; + struct rp1_adc *radc = dev->user_data; + + rt_dm_dev_unbind_fwdata(dev, RT_NULL); + + 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, +}; +RT_PLATFORM_DRIVER_EXPORT(rp1_adc_driver); 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..068c3846b03d --- /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_hw_us_delay(521); +} + +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..0d92675839a0 --- /dev/null +++ b/components/drivers/audio/rockchip/Kconfig @@ -0,0 +1,29 @@ +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_HEADSET + bool "Rockchips HeadSet support" + 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..78c1200401a7 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,200 @@ 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; } -rt_inline void clk_put(struct rt_clk *clk) +static void clk_free(struct rt_clk *clk) { - ref_put(&clk->ref, &clk_release); + 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 void clk_set_parent(struct rt_clk *clk, struct rt_clk *parent) +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) { - rt_spin_lock(&_clk_lock); + struct rt_clk *clk = clk_alloc(clk_np, dev_id, con_id, fw_node); - clk->parent = parent; + if (!rt_is_err(clk)) + { + clk_get(clk_np); - rt_list_insert_after(&parent->children_nodes, &clk->list); + 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; + } + } - rt_spin_unlock(&_clk_lock); + return 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) +{ + 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_node *clk_np, struct rt_clk_node *parent_np) +{ + rt_hw_spin_lock(&_clk_lock.lock); + + clk_np->parent = parent_np; + + rt_list_insert_after(&parent_np->children_nodes, &clk_np->list); + + 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); + #if RT_NAME_MAX > 0 + rt_strncpy(clk_np->rt_parent.name, RT_CLK_NODE_OBJ_NAME, RT_NAME_MAX); + #else + clk_np->rt_parent.name = RT_CLK_NODE_OBJ_NAME; + #endif - if (parent) + 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_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 +223,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 +291,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 +322,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 +355,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 +412,6 @@ rt_err_t rt_clk_prepare_enable(struct rt_clk *clk) } } } - else - { - err = -RT_EINVAL; - } return err; } @@ -325,30 +427,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; - if (clk->ops->set_rate) + rt_hw_spin_lock(&_clk_lock.lock); + + 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 +582,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 +592,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) + if (clk && clk->clk_np) { - err = rt_clk_set_rate_range(clk, rate, clk->max_rate); - } - else - { - 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 +606,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 +620,343 @@ 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; + + rt_hw_spin_lock(&_clk_lock.lock); - if (clk->ops->set_rate) + 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) { - return clk ? clk->rate : -1UL; + 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) +{ + struct rt_clk_array *clk_arr = RT_NULL; + #ifdef RT_USING_OFW -struct rt_clk *rt_ofw_get_clk(struct rt_ofw_node *np, int index) + 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; - if (np && index >= 0) +#ifdef RT_USING_OFW + 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; + +#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; - rt_spin_lock(&_clk_lock); + if (!rt_ofw_parse_phandle_cells(np, "clocks", "#clock-cells", index, &clk_args)) + { + int count; + struct rt_object *obj; + struct rt_clk_node *clk_np = RT_NULL; + struct rt_ofw_node *clk_ofw_np = clk_args.data; - if (!rt_ofw_prop_read_u32_index(np, "clocks", index, (rt_uint32_t *)&phandle)) + if (rt_ofw_data(clk_ofw_np) && (obj = rt_ofw_parse_object(clk_ofw_np, + RT_CLK_NODE_OBJ_NAME, "#clock-cells"))) { - struct rt_ofw_node *clk_np = rt_ofw_find_node_by_phandle(phandle); + clk_np = rt_container_of(obj, struct rt_clk_node, rt_parent); - if (clk_np) + count = rt_ofw_count_of_clk(clk_ofw_np); + } + + rt_ofw_node_put(clk_ofw_np); + + if (clk_np) + { + if (count > 1) { - clk = rt_ofw_data(clk_np); - rt_ofw_node_put(clk_np); + /* args[0] must be the index of CLK */ + clk_np = &clk_np[clk_args.args[0]]; + } + + 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); - if (clk) + for (i = 0; i < count; ++i) + { + const char *name = RT_NULL; + + 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 +968,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..9046c126a116 --- /dev/null +++ b/components/drivers/clk/rockchip/clk-rk3308.c @@ -0,0 +1,2081 @@ +/* + * 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_clk_node clk_parent; + struct rt_reset_controller rstc_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, 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->clk_parent.ops = &rk3308_clk_ops; + + if ((err = rt_clk_register(&rk_clk->clk_parent, RT_NULL))) + { + goto _fail; + } + + if ((err = rk_register_softrst(&rk_clk->rstc_parent, np, + softrst_regs, ROCKCHIP_SOFTRST_HIWORD_MASK))) + { + goto _fail; + } + + rt_ofw_data(np) = &rk_clk->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..068c978c64fa --- /dev/null +++ b/components/drivers/clk/rockchip/clk-rk3568.c @@ -0,0 +1,4778 @@ +/* + * 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_clk_node clk_parent; + struct rt_reset_controller rstc_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, 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->clk_parent, 0, sizeof(rk_clk->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->clk_parent.ops = &rk3568_clk_ops; + + if ((err = rt_clk_register(&rk_clk->clk_parent, RT_NULL))) + { + goto _fail; + } + + if ((err = rk_register_softrst(&rk_clk->rstc_parent, np, + softrst_regs, ROCKCHIP_SOFTRST_HIWORD_MASK))) + { + goto _fail; + } + + rt_ofw_data(np) = &rk_clk->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..def640757d9b 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', 'numa.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/numa.c b/components/drivers/core/numa.c new file mode 100644 index 000000000000..e8816ce55f9d --- /dev/null +++ b/components/drivers/core/numa.c @@ -0,0 +1,144 @@ +/* + * 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 +#include + +#define DBG_TAG "rtdm.numa" +#define DBG_LVL DBG_INFO +#include + +struct numa_memory +{ + rt_list_t list; + + int nid; + rt_ubase_t start; + rt_ubase_t end; + + union + { + void *ofw_node; + }; +}; + +static int cpu_numa_map[RT_CPUS_NR] = +{ + [0 ... RT_CPUS_NR - 1] = -RT_ENOSYS, +}; +static rt_list_t numa_memory_nodes = RT_LIST_OBJECT_INIT(numa_memory_nodes); + +int rt_numa_cpu_id(int cpuid) +{ + return cpuid < RT_ARRAY_SIZE(cpu_numa_map) ? cpu_numa_map[cpuid] : -RT_EINVAL; +} + +int rt_numa_device_id(struct rt_device *dev) +{ + rt_uint32_t nid = (rt_uint32_t)-RT_ENOSYS; + + return rt_dm_dev_prop_read_u32(dev, "numa-node-id", &nid) ? : (int)nid; +} + +rt_err_t rt_numa_memory_affinity(rt_ubase_t phy_addr, bitmap_t *out_affinity) +{ + struct numa_memory *nm; + + if (!out_affinity) + { + return -RT_EINVAL; + } + + rt_memset(out_affinity, 0, sizeof(*out_affinity) * BITMAP_LEN(RT_CPUS_NR)); + + rt_list_for_each_entry(nm, &numa_memory_nodes, list) + { + if (phy_addr >= nm->start && phy_addr < nm->end) + { + for (int i = 0; i < RT_ARRAY_SIZE(cpu_numa_map); ++i) + { + if (cpu_numa_map[i] == nm->nid) + { + bitmap_set_bit(out_affinity, i); + } + } + + return RT_EOK; + } + } + + return -RT_EEMPTY; +} + +#ifdef RT_USING_OFW +static int numa_ofw_init(void) +{ + int i = 0; + rt_uint32_t nid; + const char *numa_conf; + struct rt_ofw_node *np = RT_NULL; + + numa_conf = rt_ofw_bootargs_select("numa=", 0); + + if (!numa_conf || rt_strcmp(numa_conf, "on")) + { + return (int)RT_EOK; + } + + rt_ofw_foreach_cpu_node(np) + { + rt_ofw_prop_read_u32(np, "numa-node-id", (rt_uint32_t *)&cpu_numa_map[i]); + + if (++i >= RT_CPUS_NR) + { + break; + } + } + + rt_ofw_foreach_node_by_type(np, "memory") + { + if (!rt_ofw_prop_read_u32(np, "numa-node-id", &nid)) + { + int mem_nr = rt_ofw_get_address_count(np); + + for (i = 0; i < mem_nr; ++i) + { + rt_uint64_t addr, size; + struct numa_memory *nm; + + if (rt_ofw_get_address(np, i, &addr, &size)) + { + continue; + } + + nm = rt_malloc(sizeof(*nm)); + + if (rt_unlikely(!nm)) + { + LOG_E("No memory to record NUMA[%d] memory[%p, %p] info", + nid, addr, addr + size); + + return (int)-RT_ENOMEM; + } + + nm->start = (rt_ubase_t)addr; + nm->end = (rt_ubase_t)(addr + size); + nm->ofw_node = np; + + rt_list_init(&nm->list); + rt_list_insert_before(&numa_memory_nodes, &nm->list); + } + } + } + + return 0; +} +INIT_CORE_EXPORT(numa_ofw_init); +#endif /* RT_USING_OFW */ 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..d072a6e28a3a --- /dev/null +++ b/components/drivers/core/power_domain.c @@ -0,0 +1,473 @@ +/* + * 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, RT_POWER_DOMAIN_OBJ_NAME, RT_NAME_MAX); +#else + proxy->parent.name = RT_POWER_DOMAIN_OBJ_NAME; +#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, RT_POWER_DOMAIN_OBJ_NAME, RT_NAME_MAX); +#else + domain->parent.name = RT_POWER_DOMAIN_OBJ_NAME; +#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, RT_POWER_DOMAIN_OBJ_NAME)) + { + proxy = rt_container_of(obj, struct rt_dm_power_domain_proxy, parent); + domain = proxy->ofw_parse(proxy, args); + } + else if (!rt_strcmp(obj->name, RT_POWER_DOMAIN_OBJ_NAME)) + { + domain = rt_container_of(obj, struct rt_dm_power_domain, parent); + } + else if ((obj = rt_ofw_parse_object(power_domain_np, + RT_POWER_DOMAIN_PROXY_OBJ_NAME, "#power-domain-cells"))) + { + proxy = rt_container_of(obj, struct rt_dm_power_domain_proxy, parent); + domain = proxy->ofw_parse(proxy, args); + } + else if ((obj = rt_ofw_parse_object(power_domain_np, + RT_POWER_DOMAIN_OBJ_NAME, "#power-domain-cells"))) + { + 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..2392f0079d73 --- /dev/null +++ b/components/drivers/firmware/qemu/fw_cfg.c @@ -0,0 +1,732 @@ +/* + * 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; + /* + * We should use a mutex here, + * but in rt-thread the mutex must be used in the thread, + * so we have to use a spinlock. + */ + struct rt_spinlock 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_spin_lock(&fbdev->lock); + + 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; + fbdev->fix.line_length = (bpp / 8) * usr_var->xres; + + rt_memcpy(&fbdev->var, usr_var, sizeof(fbdev->var)); + } + 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 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 FBIOPAN_DISPLAY: + case FBIO_WAITFORVSYNC: + break; + + default: + err = -RT_EINVAL; + break; + } + + rt_spin_unlock(&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_spin_lock_init(&fbdev->lock); + + 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..bd24c213e148 --- /dev/null +++ b/components/drivers/i2c/busses/i2c-rk3x.c @@ -0,0 +1,1355 @@ +/* + * 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_hw_interrupt_mask(i2c->irq); + rt_pic_detach_irq(i2c->irq, i2c); + + 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/iio/SConscript b/components/drivers/iio/SConscript new file mode 100755 index 000000000000..577dab2af777 --- /dev/null +++ b/components/drivers/iio/SConscript @@ -0,0 +1,15 @@ +from building import * + +group = [] + +if not GetDepend(['RT_USING_DM']): + Return('group') + +cwd = GetCurrentDir() +CPPPATH = [cwd + '/../include'] + +src = ['iio.c',] + +group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') diff --git a/components/drivers/iio/iio.c b/components/drivers/iio/iio.c new file mode 100644 index 000000000000..43db4c358c59 --- /dev/null +++ b/components/drivers/iio/iio.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-3-08 GuEe-GUI the first version + */ + +#include +#include + +#ifdef RT_USING_OFW +static void *ofw_iio_channel_get_by_index(struct rt_ofw_node *np, int index, int *out_channel) +{ + void *iio = RT_NULL; + struct rt_ofw_cell_args iio_args; + + if (!rt_ofw_parse_phandle_cells(np, "io-channels", "#io-channel-cells", index, &iio_args)) + { + iio = rt_ofw_data(iio_args.data); + + rt_ofw_node_put(iio_args.data); + + if (out_channel) + { + *out_channel = iio_args.args[0]; + } + } + + return iio; +} +#else +static void *ofw_iio_channel_get_by_index(struct rt_ofw_node *np, int index, int *out_channel) +{ + return RT_NULL; +} +#endif /* RT_USING_OFW */ + +void *rt_iio_channel_get_by_index(struct rt_device *dev, int index, int *out_channel) +{ + void *iio = RT_NULL; + + if (!dev || index < 0) + { + return RT_NULL; + } + + if (dev->ofw_node) + { + iio = ofw_iio_channel_get_by_index(dev->ofw_node, index, out_channel); + } + + return iio; +} + +void *rt_iio_channel_get_by_name(struct rt_device *dev, const char *name, int *out_channel) +{ + int index; + + if (!dev || !name) + { + return RT_NULL; + } + + index = rt_dm_dev_prop_index_of_string(dev, "io-channel-names", name); + + return rt_iio_channel_get_by_index(dev, index, out_channel); +} 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..9cbd221892fe 100644 --- a/components/drivers/include/drivers/clk.h +++ b/components/drivers/include/drivers/clk.h @@ -16,36 +16,84 @@ #include #include +#define RT_CLK_NODE_OBJ_NAME "CLKNP" + 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. + */ + struct rt_object rt_parent; + 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 +101,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 +142,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/numa.h b/components/drivers/include/drivers/core/numa.h new file mode 100644 index 000000000000..8ed7b4ffa6f8 --- /dev/null +++ b/components/drivers/include/drivers/core/numa.h @@ -0,0 +1,25 @@ +/* + * 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_NUMA_H__ +#define __RT_DM_NUMA_H__ + +#include +#include + +#include + +/* NUMA: Non-Uniform Memory Access */ + +int rt_numa_cpu_id(int cpuid); +int rt_numa_device_id(struct rt_device *dev); +rt_err_t rt_numa_memory_affinity(rt_ubase_t phy_addr, bitmap_t *out_affinity); + +#endif /* __RT_DM_NUMA_H__ */ 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..a8ed69b73411 --- /dev/null +++ b/components/drivers/include/drivers/core/power_domain.h @@ -0,0 +1,82 @@ +/* + * 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 + +#define RT_POWER_DOMAIN_OBJ_NAME "PMD" +#define RT_POWER_DOMAIN_PROXY_OBJ_NAME "PMP" + +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..29c1540d50ad --- /dev/null +++ b/components/drivers/include/drivers/dma.h @@ -0,0 +1,138 @@ +/* + * 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) + +#define RT_DMA_PAGE_SIZE ARCH_PAGE_SIZE + +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/iio.h b/components/drivers/include/drivers/iio.h new file mode 100644 index 000000000000..fdbfb9eaa403 --- /dev/null +++ b/components/drivers/include/drivers/iio.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-3-08 GuEe-GUI the first version + */ + +#ifndef __IIO_H__ +#define __IIO_H__ + +#include + +void *rt_iio_channel_get_by_index(struct rt_device *dev, int index, int *out_channel); +void *rt_iio_channel_get_by_name(struct rt_device *dev, const char *name, int *out_channel); + +#endif /* __IIO_H__ */ 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..85b519683272 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) @@ -423,6 +435,8 @@ rt_inline rt_bool_t rt_ofw_node_is_type(const struct rt_ofw_node *np, const char 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); +struct rt_object *rt_ofw_parse_object(struct rt_ofw_node *np, const char *obj_name, const char *cells_name); + rt_err_t rt_ofw_console_setup(void); const char *rt_ofw_bootargs_select(const char *key, int index); 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..b3b285794898 100644 --- a/components/drivers/include/drivers/pci.h +++ b/components/drivers/include/drivers/pci.h @@ -6,4 +6,473 @@ * 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); + +#define RT_PCI_IRQ_F_LEGACY RT_BIT(0) /* Allow legacy interrupts */ +#define RT_PCI_IRQ_F_MSI RT_BIT(1) /* Allow MSI interrupts */ +#define RT_PCI_IRQ_F_MSIX RT_BIT(2) /* Allow MSI-X interrupts */ +#define RT_PCI_IRQ_F_AFFINITY RT_BIT(3) /* Auto-assign affinity */ +#define RT_PCI_IRQ_F_ALL_TYPES (RT_PCI_IRQ_F_LEGACY | RT_PCI_IRQ_F_MSI | RT_PCI_IRQ_F_MSIX) + +#ifdef RT_PCI_MSI +rt_err_t rt_pci_alloc_vector(struct rt_pci_device *pdev, int min, int max, rt_uint32_t flags, bitmap_t *affinity); +void rt_pci_free_vector(struct rt_pci_device *pdev); + +rt_ssize_t rt_pci_msi_vector_count(struct rt_pci_device *pdev); +rt_err_t rt_pci_msi_disable(struct rt_pci_device *pdev); +rt_err_t rt_pci_msi_enable(struct rt_pci_device *pdev); + +rt_ssize_t rt_pci_msix_vector_count(struct rt_pci_device *pdev); +rt_err_t rt_pci_msix_disable(struct rt_pci_device *pdev); +rt_err_t rt_pci_msix_enable_range(struct rt_pci_device *pdev, struct rt_pci_msix_entry *entries, int min, int max); +#else +rt_err_t rt_pci_alloc_vector(struct rt_pci_device *pdev, int min, int max, rt_uint32_t flags, bitmap_t *affinity) +{ + return -RT_ENOSYS; +} + +void rt_pci_free_vector(struct rt_pci_device *pdev) +{ + return; +} + +rt_ssize_t rt_pci_msi_vector_count(struct rt_pci_device *pdev) +{ + return 0; +} + +rt_err_t rt_pci_msi_disable(struct rt_pci_device *pdev) +{ + return RT_EOK; +} + +rt_err_t rt_pci_msi_enable(struct rt_pci_device *pdev) +{ + return -RT_ENOSYS; +} + +rt_ssize_t rt_pci_msix_vector_count(struct rt_pci_device *pdev) +{ + return 0; +} + +rt_err_t rt_pci_msix_disable(struct rt_pci_device *pdev) +{ + return RT_EOK; +} + +rt_err_t rt_pci_msix_enable_range(struct rt_pci_device *pdev, struct rt_pci_msix_entry *entries, int min, int max) +{ + return -RT_ENOSYS; +} +#endif /* RT_PCI_MSI */ + +rt_inline rt_err_t rt_pci_msix_enable(struct rt_pci_device *pdev, struct rt_pci_msix_entry *entries, int count) +{ + return rt_pci_msix_enable_range(pdev, entries, count, count); +} + +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..b4163e247796 100644 --- a/components/drivers/include/drivers/pci_msi.h +++ b/components/drivers/include/drivers/pci_msi.h @@ -6,4 +6,173 @@ * 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 + +/* + * MSI Format: + * T0: 32-bit Address + * T1: 64-bit Address + * T2: 32-bit Address with Per-Vector Masking + * T3: 64-bit Address with Per-Vector Masking + * + * 31 16 15 8 7 0 + * +---------------------------+-----------------+---------------+ + * | Message Control | Next Capability | Capability ID | DW0 + * | | Pointer | (05h) | + * +---------------------------+-----------------+---------------+ + * | Message Address [31:0] | DW1 + * +-------------------------------------------------------------+ + * | Message Address [63:32] | DW2 (T1: only 64-bit) + * +---------------------------+---------------------------------+ + * | Reserved | Message Data | DW3 + * +---------------------------+---------------------------------+ + * | Mask Bits | DW4 (T2/T3: only with Per-Vector Masking) + * +-------------------------------------------------------------+ + * | Pending Bits | DW5 (T2/T3: only with Per-Vector Masking) + * +-------------------------------------------------------------+ + * + * MSI Message Control: + * + * 15 9 8 7 6 4 3 1 0 + * +----------------------+---+---+---------------+----------+---+ + * | Reserved | | | | | | + * +----------------------+---+---+---------------+----------+---+ + * ^ ^ ^ ^ ^ + * | | | | | + * | | | | +---- MSI Enable (RW) + * | | | +----------- Multiple Message Capable (RO, 2 ^ n [n <= 5]) + * | | +------------------------- Multiple Message Enable (RW, 2 ^ n [n <= 5]) + * | +----------------------------------- 64-bit Address Capable + * +--------------------------------------- Per-Vector Masking Capable + */ + +struct rt_pci_msi_conf +{ + rt_uint32_t mask; + rt_uint8_t mask_pos; + rt_uint8_t pending_pos; + + struct + { + rt_uint8_t is_masking:1; + rt_uint8_t is_64bit:1; + rt_uint8_t multi_msg_max:3; + rt_uint8_t multi_msg_use:3; + } cap; +}; + +/* + * MSI-X Format: + * + * 31 16 15 8 7 0 + * +---------------------------+-----------------+---------------+ + * | Message Control | Next Capability | Capability ID | DW0 + * | | Pointer | (11h) | + * +---------------------------+-----------------+---+-----------+ + * | MSI-X Table Offset | Table BIR | DW1 (BIR: BAR Index Register) + * +-------------------------------------------------+-----------+ | + * | Pending Bit Array (PBA) Offset | PBA BIR | DW2 --------+ | + * +-------------------------------------------------+-----------+ | | + * | | + * MSI-X Message Control: | | + * | | + * 15 14 13 11 10 0 | | + * +---+---+----------+------------------------------------------+ | | + * | | | Reserved | Table Size in N-1 (RO) | | | + * +---+---+----------+------------------------------------------+ | | + * ^ ^ | | + * | | | | + * | +---- Function Mask (RW) | | + * +-------- MSI-X Enable (RW) | | + * | | + * MSI-X Table (BAR[Table BIR] + MSI-X Table Offset): | | + * | | + * DW3 DW2 DW1 DW0 | | + * +----------------+--------------+---------------+---------------+ <---------|-+ + * | Vector Control | Message Data | Upper Address | Lower Address | Entry 0 | + * +----------------+--------------+---------------+---------------+ | + * | Vector Control | Message Data | Upper Address | Lower Address | Entry 1 | + * +----------------+--------------+---------------+---------------+ | + * | ...... | ...... | ...... | ...... | | + * +----------------+--------------+---------------+---------------+ | + * | Vector Control | Message Data | Upper Address | Lower Address | Entry N-1 | + * +----------------+--------------+---------------+---------------+ | + * ^ | + * | | + * +---- Bit 0 is vector Mask Bit (R/W) | + * | + * MSI-X Pending Bit Array (BAR[PBA BIR] + Pending Bit Array Offset): | + * | + * DW1 DW0 | + * +-------------------------------+ <-----------------------------------------+ + * | Pending Bits 0 - 63 | QW 0 + * +-------------------------------+ + * | Pending Bits 64 - 127 | QW 1 + * +-------------------------------+ + * | ...... | + * +-------------------------------+ + * | Pending Bits | QW (N-1)/64 + * +-------------------------------+ + */ + +struct rt_pci_msix_conf +{ + int index; + rt_list_t list; + + rt_uint32_t msg_ctrl; + void *table_base; + void *pending_base; + + struct + { + rt_uint8_t is_masking:1; + rt_uint8_t is_64bit:1; + } cap; +}; + +struct rt_pci_msi_msg +{ + rt_uint32_t address_lo; + rt_uint32_t address_hi; + rt_uint32_t data; +}; + +struct rt_pci_msi_desc +{ + int irq; + rt_size_t vector_used; + rt_size_t vector_count; + + bitmap_t *affinity; + struct rt_pci_device *pdev; + struct rt_pci_msi_msg msg; + + void *write_msi_msg_data; + void (*write_msi_msg)(struct rt_pci_msi_desc *, void *); + + rt_bool_t is_msix; + union + { + struct rt_pci_msi_conf msi; + struct rt_pci_msix_conf msix; + }; + + void *priv; +}; + +#define rt_msi_for_each_desc(pdev, desc) \ + rt_list_for_each_entry(desc, &pdev->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/power_supply.h b/components/drivers/include/drivers/power_supply.h new file mode 100644 index 000000000000..34a879779319 --- /dev/null +++ b/components/drivers/include/drivers/power_supply.h @@ -0,0 +1,163 @@ +/* + * 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 __POWER_SUPPLY_H__ +#define __POWER_SUPPLY_H__ + +#include +#include +#include + +enum rt_power_supply_type +{ + RT_POWER_SUPPLY_TYPE_UNKNOWN = 0, + RT_POWER_SUPPLY_TYPE_BATTERY, + RT_POWER_SUPPLY_TYPE_UPS, + RT_POWER_SUPPLY_TYPE_MAINS, + RT_POWER_SUPPLY_TYPE_USB, /* Standard Downstream Port */ + RT_POWER_SUPPLY_TYPE_USB_DCP, /* Dedicated Charging Port */ + RT_POWER_SUPPLY_TYPE_USB_CDP, /* Charging Downstream Port */ + RT_POWER_SUPPLY_TYPE_USB_ACA, /* Accessory Charger Adapters */ + RT_POWER_SUPPLY_TYPE_USB_TYPE_C, /* Type C Port */ + RT_POWER_SUPPLY_TYPE_USB_PD, /* Power Delivery Port */ + RT_POWER_SUPPLY_TYPE_USB_PD_DRP, /* PD Dual Role Port */ + RT_POWER_SUPPLY_TYPE_APPLE_BRICK_ID, /* Apple Charging Method */ + RT_POWER_SUPPLY_TYPE_WIRELESS, /* Wireless */ +}; + +enum rt_power_supply_property +{ + /* Properties of type `int' */ + RT_POWER_SUPPLY_PROP_STATUS = 0, + RT_POWER_SUPPLY_PROP_CHARGE_TYPE, + RT_POWER_SUPPLY_PROP_HEALTH, + RT_POWER_SUPPLY_PROP_PRESENT, + RT_POWER_SUPPLY_PROP_ONLINE, + RT_POWER_SUPPLY_PROP_AUTHENTIC, + RT_POWER_SUPPLY_PROP_TECHNOLOGY, + RT_POWER_SUPPLY_PROP_CYCLE_COUNT, + RT_POWER_SUPPLY_PROP_VOLTAGE_MAX, + RT_POWER_SUPPLY_PROP_VOLTAGE_MIN, + RT_POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, + RT_POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, + RT_POWER_SUPPLY_PROP_VOLTAGE_NOW, + RT_POWER_SUPPLY_PROP_VOLTAGE_AVG, + RT_POWER_SUPPLY_PROP_VOLTAGE_OCV, + RT_POWER_SUPPLY_PROP_VOLTAGE_BOOT, + RT_POWER_SUPPLY_PROP_CURRENT_MAX, + RT_POWER_SUPPLY_PROP_CURRENT_NOW, + RT_POWER_SUPPLY_PROP_CURRENT_AVG, + RT_POWER_SUPPLY_PROP_CURRENT_BOOT, + RT_POWER_SUPPLY_PROP_POWER_NOW, + RT_POWER_SUPPLY_PROP_POWER_AVG, + RT_POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, + RT_POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN, + RT_POWER_SUPPLY_PROP_CHARGE_FULL, + RT_POWER_SUPPLY_PROP_CHARGE_EMPTY, + RT_POWER_SUPPLY_PROP_CHARGE_NOW, + RT_POWER_SUPPLY_PROP_CHARGE_AVG, + RT_POWER_SUPPLY_PROP_CHARGE_COUNTER, + RT_POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT, + RT_POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, + RT_POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE, + RT_POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX, + RT_POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT, + RT_POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX, + RT_POWER_SUPPLY_PROP_CHARGE_CONTROL_START_THRESHOLD, /* in percents! */ + RT_POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD, /* in percents! */ + RT_POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR, + RT_POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, + RT_POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT, + RT_POWER_SUPPLY_PROP_INPUT_POWER_LIMIT, + RT_POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN, + RT_POWER_SUPPLY_PROP_ENERGY_EMPTY_DESIGN, + RT_POWER_SUPPLY_PROP_ENERGY_FULL, + RT_POWER_SUPPLY_PROP_ENERGY_EMPTY, + RT_POWER_SUPPLY_PROP_ENERGY_NOW, + RT_POWER_SUPPLY_PROP_ENERGY_AVG, + RT_POWER_SUPPLY_PROP_CAPACITY, /* in percents! */ + RT_POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN, /* in percents! */ + RT_POWER_SUPPLY_PROP_CAPACITY_ALERT_MAX, /* in percents! */ + RT_POWER_SUPPLY_PROP_CAPACITY_ERROR_MARGIN, /* in percents! */ + RT_POWER_SUPPLY_PROP_CAPACITY_LEVEL, + RT_POWER_SUPPLY_PROP_TEMP, + RT_POWER_SUPPLY_PROP_TEMP_MAX, + RT_POWER_SUPPLY_PROP_TEMP_MIN, + RT_POWER_SUPPLY_PROP_TEMP_ALERT_MIN, + RT_POWER_SUPPLY_PROP_TEMP_ALERT_MAX, + RT_POWER_SUPPLY_PROP_TEMP_AMBIENT, + RT_POWER_SUPPLY_PROP_TEMP_AMBIENT_ALERT_MIN, + RT_POWER_SUPPLY_PROP_TEMP_AMBIENT_ALERT_MAX, + RT_POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, + RT_POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG, + RT_POWER_SUPPLY_PROP_TIME_TO_FULL_NOW, + RT_POWER_SUPPLY_PROP_TIME_TO_FULL_AVG, + RT_POWER_SUPPLY_PROP_TYPE, /* use power_supply.type instead */ + RT_POWER_SUPPLY_PROP_USB_TYPE, + RT_POWER_SUPPLY_PROP_SCOPE, + RT_POWER_SUPPLY_PROP_PRECHARGE_CURRENT, + RT_POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT, + RT_POWER_SUPPLY_PROP_CALIBRATE, + RT_POWER_SUPPLY_PROP_MANUFACTURE_YEAR, + RT_POWER_SUPPLY_PROP_MANUFACTURE_MONTH, + RT_POWER_SUPPLY_PROP_MANUFACTURE_DAY, + /* Properties of type `const char *' */ + RT_POWER_SUPPLY_PROP_MODEL_NAME, + RT_POWER_SUPPLY_PROP_MANUFACTURER, + RT_POWER_SUPPLY_PROP_SERIAL_NUMBER, +}; + +union rt_power_supply_property_val +{ + int intval; + const char *strval; +}; + +struct rt_power_supply_ops; + +struct rt_power_supply +{ + struct rt_device parent; + + enum power_supply_type type; + + rt_size_t properties_nr; + enum rt_power_supply_property *properties; + + const struct rt_power_supply_ops *ops; + +#ifdef RT_USING_THERMAL + struct rt_thermal_zone_device *thermal_dev; + struct rt_thermal_cooling_device *thermal_cool_dev; +#endif + +#ifdef RT_USING_LED + struct rt_led_device *charging_full_led; + struct rt_led_device *charging_led; + struct rt_led_device *full_led; + struct rt_led_device *online_led; + struct rt_led_device *charging_blink_full_solid_led; +#endif /* RT_USING_LED */ +} + +struct rt_power_supply_ops +{ + rt_err_t (*get_property)(struct rt_power_supply *, + enum power_supply_property prop, union rt_power_supply_property_val *val); + rt_err_t (*set_property)(struct rt_power_supply *, + enum power_supply_property prop, const union rt_power_supply_property_val *val); +}; + +rt_err_t rt_power_supply_register(struct rt_power_supply *psy); +rt_err_t rt_power_supply_unregister(struct rt_power_supply *psy); + +void rt_power_supply_changed(struct rt_power_supply *psy); + +#endif /* __POWER_SUPPLY_H__ */ 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..4e3733f690a9 --- /dev/null +++ b/components/drivers/include/drivers/reset.h @@ -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 + */ + +#ifndef __RESET_H__ +#define __RESET_H__ + +#include +#include +#include +#include + +#define RT_RESET_CONTROLLER_OBJ_NAME "RSTC" + +struct rt_reset_control_ops; + +struct rt_reset_controller +{ + struct rt_object parent; + + 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; +}; + +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..48d0e4ecc311 100644 --- a/components/drivers/include/rtdevice.h +++ b/components/drivers/include/rtdevice.h @@ -168,18 +168,50 @@ extern "C" { #ifdef RT_USING_DM #include "drivers/core/bus.h" #include "drivers/core/rtdm.h" +#include "drivers/core/numa.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 + +#include "drivers/iio.h" + #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 +224,33 @@ 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_POWER_SUPPLY +#include "drivers/power_supply.h" +#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..fbadf9458191 --- /dev/null +++ b/components/drivers/input/keyboard/Kconfig @@ -0,0 +1,16 @@ +menuconfig RT_INPUT_KEYBOARD + bool "Keyboards" + default n + +config RT_INPUT_KEYBOARD_ADC_KEYS + bool "ADC Ladder Buttons" + depends on RT_INPUT_KEYBOARD + select RT_USING_OFW + select RT_USING_ADC + 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..867b87ab9875 --- /dev/null +++ b/components/drivers/input/keyboard/SConscript @@ -0,0 +1,21 @@ +from building import * + +group = [] + +if not GetDepend(['RT_INPUT_KEYBOARD']): + Return('group') + +cwd = GetCurrentDir() +CPPPATH = [cwd + '/../../include'] + +src = [] + +if GetDepend(['RT_INPUT_KEYBOARD_ADC_KEYS']): + src += ['adc-keys.c'] + +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/adc-keys.c b/components/drivers/input/keyboard/adc-keys.c new file mode 100644 index 000000000000..dd6988bb1fe4 --- /dev/null +++ b/components/drivers/input/keyboard/adc-keys.c @@ -0,0 +1,224 @@ +/* + * 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.adc" +#define DBG_LVL DBG_INFO +#include + +struct adc_keys_button +{ + rt_uint32_t voltage; + rt_uint32_t keycode; +}; + +struct adc_keys +{ + struct rt_adc_device *adc_dev; + + struct rt_timer poll_work; + + int channel; + rt_uint32_t num_keys; + rt_uint32_t last_key; + rt_uint32_t keyup_voltage; + struct adc_keys_button kbtn[]; +}; + +static void adc_keys_poll(void *param) +{ + int value, keycode = 0; + rt_uint32_t diff, closest = 0xffffffff; + struct adc_keys *tk = param; + + value = rt_adc_read(tk->adc_dev, tk->channel); + + if (rt_unlikely(value < 0)) + { + /* Forcibly release key if any was pressed */ + value = tk->keyup_voltage; + } + else + { + for (int i = 0; i < tk->num_keys; ++i) + { + diff = rt_abs(tk->kbtn[i].voltage - value); + + if (diff < closest) + { + closest = diff; + keycode = tk->kbtn[i].keycode; + } + } + } + + if (rt_abs(tk->keyup_voltage - value) < closest) + { + keycode = 0; + } + + if (tk->last_key && tk->last_key != keycode) + { + /* Key up */ + switch (tk->last_key) + { + case KEY_POWER: + rt_hw_cpu_shutdown(); + break; + + case KEY_RESTART: + rt_hw_cpu_reset(); + break; + + default: + LOG_W("Unsupported keycode = %d", tk->last_key); + break; + } + } + + if (keycode) + { + /* Key down */ + } + + tk->last_key = keycode; +} + +static rt_err_t adc_key_probe(struct rt_platform_device *pdev) +{ + int i = 0; + rt_err_t err; + rt_uint32_t interval; + rt_uint32_t num_keys; + struct adc_keys *tk; + struct rt_device *dev = &pdev->parent; + struct rt_ofw_node *np = dev->ofw_node, *key_np; + + num_keys = rt_ofw_get_child_count(np); + + if (!num_keys) + { + LOG_E("Keymap is missing"); + + return -RT_EINVAL; + } + + tk = rt_calloc(1, sizeof(*tk) + sizeof(struct adc_keys_button) * num_keys); + + if (!tk) + { + return -RT_ENOMEM; + } + + tk->adc_dev = rt_iio_channel_get_by_name(dev, "buttons", &tk->channel); + + if (!tk->adc_dev) + { + LOG_E("ADC device not found"); + + err = -RT_EINVAL; + goto _fail; + } + + rt_ofw_foreach_child_node(np, key_np) + { + const char *propname; + + if (rt_ofw_prop_read_u32(key_np, "press-threshold-microvolt", + &tk->kbtn[i].voltage)) + { + LOG_E("%s: Key with invalid or missing %s", + rt_ofw_node_full_name(key_np), "voltage"); + rt_ofw_node_put(key_np); + + err = -RT_EINVAL; + goto _fail; + } + + tk->kbtn[i].voltage /= 1000; + + if (!(propname = rt_ofw_get_prop_fuzzy_name(key_np, ",code$")) || + rt_ofw_prop_read_u32(key_np, propname, &tk->kbtn[i].keycode)) + { + LOG_E("%s: Key with invalid or missing %s", + rt_ofw_node_full_name(key_np), "*,code"); + rt_ofw_node_put(key_np); + + err = -RT_EINVAL; + goto _fail; + } + + ++i; + } + + dev->user_data = tk; + + if (rt_ofw_prop_read_u32(np, "keyup-threshold-microvolt", &tk->keyup_voltage)) + { + LOG_E("Invalid or missing keyup voltage"); + + err = -RT_EINVAL; + goto _fail; + } + + if (rt_ofw_prop_read_u32(np, "poll-interval", &interval)) + { + interval = 200; + } + + rt_timer_init(&tk->poll_work, "adc-keys", adc_keys_poll, tk, + rt_tick_from_millisecond(interval), RT_TIMER_FLAG_PERIODIC); + rt_timer_start(&tk->poll_work); + + return RT_EOK; + +_fail: + rt_free(tk); + + return err; +} + +static rt_err_t adc_key_remove(struct rt_platform_device *pdev) +{ + struct adc_keys *tk = pdev->parent.user_data; + + rt_timer_stop(&tk->poll_work); + rt_timer_detach(&tk->poll_work); + + rt_free(tk); + + return RT_EOK; +} + +static const struct rt_ofw_node_id adc_key_ofw_ids[] = +{ + { .compatible = "adc-keys" }, + { /* sentinel */ } +}; + +static struct rt_platform_driver adc_key_driver = +{ + .name = "adc-keys", + .ids = adc_key_ofw_ids, + + .probe = adc_key_probe, + .remove = adc_key_remove, +}; + +static int adc_key_drv_register(void) +{ + rt_platform_driver_register(&adc_key_driver); + + return 0; +} +INIT_DRIVER_LATER_EXPORT(adc_key_drv_register); 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..2bd8aff41e88 --- /dev/null +++ b/components/drivers/input/misc/Kconfig @@ -0,0 +1,13 @@ +menuconfig RT_INPUT_MISC + bool "Misc" + default n + +config RT_INPUT_MISC_E3X0_BUTTON + bool "NI Ettus Research USRP E3xx Button support" + depends on RT_INPUT_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..ceeda3a69b72 --- /dev/null +++ b/components/drivers/input/misc/SConscript @@ -0,0 +1,21 @@ +from building import * + +group = [] + +if not GetDepend(['RT_INPUT_MISC']): + Return('group') + +cwd = GetCurrentDir() +CPPPATH = [cwd + '/../../include'] + +src = [] + +if GetDepend(['RT_INPUT_MISC_E3X0_BUTTON']): + src += ['e3x0-button.c'] + +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/e3x0-button.c b/components/drivers/input/misc/e3x0-button.c new file mode 100644 index 000000000000..c32bf1cba7cd --- /dev/null +++ b/components/drivers/input/misc/e3x0-button.c @@ -0,0 +1,129 @@ +/* + * 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 e3x0_button +{ + int press_irq, release_irq; + + struct rt_thread *poweroff_task; +}; + +static void e3x0_button_press_isr(int irqno, void *param) +{ + /* Key down */ +} + +static void e3x0_button_release_isr(int irqno, void *param) +{ + struct e3x0_button *btn = param; + + /* Key up */ + rt_thread_resume(btn->poweroff_task); +} + +static void e3x0_button_power_off_task(void *param) +{ + struct e3x0_button *btn = param; + + rt_thread_suspend(btn->poweroff_task); + rt_schedule(); + + rt_hw_interrupt_mask(btn->press_irq); + rt_hw_interrupt_mask(btn->release_irq); + + rt_hw_cpu_shutdown(); +} + +static rt_err_t e3x0_button_probe(struct rt_platform_device *pdev) +{ + rt_err_t err; + struct rt_device *dev = &pdev->parent; + struct e3x0_button *btn = rt_calloc(1, sizeof(*btn)); + + if (!btn) + { + return -RT_ENOMEM; + } + + if ((btn->press_irq = rt_dm_dev_get_irq_by_name(dev, "press")) < 0) + { + err = btn->press_irq; + goto _fail; + } + + if ((btn->release_irq = rt_dm_dev_get_irq_by_name(dev, "release")) < 0) + { + err = btn->release_irq; + goto _fail; + } + + btn->poweroff_task = rt_thread_create("button-e3x0", &e3x0_button_power_off_task, + btn, SYSTEM_THREAD_STACK_SIZE, RT_THREAD_PRIORITY_MAX / 2, 10); + + if (!btn->poweroff_task) + { + goto _fail; + } + + rt_thread_startup(btn->poweroff_task); + + dev->user_data = btn; + + rt_hw_interrupt_install(btn->press_irq, e3x0_button_press_isr, btn, "button-e3x0-press"); + rt_hw_interrupt_umask(btn->press_irq); + + rt_hw_interrupt_install(btn->release_irq, e3x0_button_release_isr, btn, "button-e3x0-release"); + rt_hw_interrupt_umask(btn->release_irq); + + return RT_EOK; + +_fail: + rt_free(btn); + + return err; +} + +static rt_err_t e3x0_button_remove(struct rt_platform_device *pdev) +{ + struct e3x0_button *btn = pdev->parent.user_data; + rt_thread_delete(btn->poweroff_task); + + rt_hw_interrupt_mask(btn->press_irq); + rt_pic_detach_irq(btn->press_irq, btn); + + rt_hw_interrupt_mask(btn->release_irq); + rt_pic_detach_irq(btn->release_irq, btn); + + rt_thread_delete(btn->poweroff_task); + + rt_free(btn); + + return RT_EOK; +} + +static const struct rt_ofw_node_id e3x0_button_ofw_ids[] = +{ + { .compatible = "ettus,e3x0-button" }, + { /* sentinel */ } +}; + +static struct rt_platform_driver e3x0_button_driver = +{ + .name = "e3x0-button", + .ids = e3x0_button_ofw_ids, + + .probe = e3x0_button_probe, + .remove = e3x0_button_remove, +}; +RT_PLATFORM_DRIVER_EXPORT(e3x0_button_driver); 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..4e39ddc50fcf --- /dev/null +++ b/components/drivers/input/touchscreen/raspberrypi-ts.c @@ -0,0 +1,338 @@ +/* + * 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; + + struct rt_timer poll_work; +}; + +#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; + + 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_touch_get_ts(); + + ++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_touch_get_ts(); + + ++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; + + case RT_TOUCH_CTRL_DISABLE_INT: + rt_timer_stop(&rts->poll_work); + break; + + case RT_TOUCH_CTRL_ENABLE_INT: + rt_timer_start(&rts->poll_work); + 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 void rpi_ts_poll(void *param) +{ + struct rpi_ts *rts = param; + 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; + } + + rt_hw_touch_isr(&rts->parent); +} + +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_timer_init(&rts->poll_work, dev_name, rpi_ts_poll, rts, + rt_tick_from_millisecond(RPI_TS_POLL_INTERVAL), RT_TIMER_FLAG_PERIODIC); + + rt_hw_touch_register(&rts->parent, dev_name, RT_DEVICE_FLAG_INT_RX, 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_timer_stop(&rts->poll_work); + rt_timer_detach(&rts->poll_work); + + rt_dm_dev_unbind_fwdata(dev, RT_NULL); + + 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/ivshmem/Kconfig b/components/drivers/ivshmem/Kconfig new file mode 100644 index 000000000000..c791d5e486d5 --- /dev/null +++ b/components/drivers/ivshmem/Kconfig @@ -0,0 +1,10 @@ +menuconfig RT_USING_IVSHMEM + bool "Using Inter-VM shared memory device drivers" + depends on RT_USING_DM + select RT_USING_PCI + default n + +config RT_IVSHMEM_QEMU + bool "QEMU" + depends on RT_USING_IVSHMEM + default n diff --git a/components/drivers/ivshmem/SConscript b/components/drivers/ivshmem/SConscript new file mode 100644 index 000000000000..c33709d34950 --- /dev/null +++ b/components/drivers/ivshmem/SConscript @@ -0,0 +1,18 @@ +from building import * + +group = [] + +if not GetDepend(['RT_USING_IVSHMEM']): + Return('group') + +cwd = GetCurrentDir() +CPPPATH = [cwd + '/../include'] + +src = ['ivshmem.c'] + +if GetDepend(['RT_IVSHMEM_QEMU']): + src += ['ivshmem-qemu.c'] + +group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') diff --git a/components/drivers/ivshmem/ivshmem-qemu.c b/components/drivers/ivshmem/ivshmem-qemu.c new file mode 100644 index 000000000000..531059234431 --- /dev/null +++ b/components/drivers/ivshmem/ivshmem-qemu.c @@ -0,0 +1,393 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-10-27 GuEe-GUI first version + */ + +#include "ivshmem.h" + +struct qemu_ivshmem +{ + struct ivshmem_device parent; + + rt_bool_t is_msx; + rt_uint32_t irq_status; + rt_event_t irq_event; + struct rt_spinlock rw_lock; +}; + +#define raw_to_qemu_ivshmem(raw) rt_container_of(raw, struct qemu_ivshmem, parent) + +static rt_bool_t rw_count_fixup(struct ivshmem_device *ivdev, rt_off_t pos, rt_size_t *count) +{ + if (pos < ivdev->shmem_size) + { + if (pos + *count > ivdev->shmem_size) + { + *count = ivdev->shmem_size - pos; + } + + return RT_TRUE; + } + else + { + *count = 0; + } + + return RT_FALSE; +} + +static rt_ssize_t qemu_ivshmem_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t count) +{ + struct ivshmem_device *ivdev = &raw_to_qemu_ivshmem(dev)->parent; + + if (rw_count_fixup(ivdev, pos, &count)) + { + rt_memcpy(buffer, (void *)(ivdev->shmem + pos), count); + } + + return count; +} + +static rt_ssize_t qemu_ivshmem_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t count) +{ + struct ivshmem_device *ivdev = &raw_to_qemu_ivshmem(dev)->parent; + + if (rw_count_fixup(ivdev, pos, &count)) + { + rt_memcpy((void *)(ivdev->shmem + pos), buffer, count); + } + + return count; +} + +static rt_err_t qemu_ivshmem_control(rt_device_t dev, int cmd, void *args) +{ + rt_err_t err = RT_EOK; + struct qemu_ivshmem *qemu_ivshmem = raw_to_qemu_ivshmem(dev); + struct ivshmem_device *ivdev = &qemu_ivshmem->parent; + + if (!args && cmd != RT_DEVICE_CTRL_CLR_INT) + { + cmd = 0; + } + + switch (cmd) + { + case RT_DEVICE_CTRL_CONFIG: + { + struct ivshmem_cmd *user_cmd = args; + + if (user_cmd->is_read) + { + user_cmd->value = HWREG32(ivdev->reg + user_cmd->reg_off); + } + else + { + HWREG32(ivdev->reg + user_cmd->reg_off) = user_cmd->value; + } + + break; + } + case RT_DEVICE_CTRL_SET_INT: + { + rt_ubase_t level = rt_spin_lock_irqsave(&qemu_ivshmem->rw_lock); + + qemu_ivshmem->irq_event = args; + + rt_spin_unlock_irqrestore(&qemu_ivshmem->rw_lock, level); + + break; + } + case RT_DEVICE_CTRL_CLR_INT: + { + rt_ubase_t level = rt_spin_lock_irqsave(&qemu_ivshmem->rw_lock); + + qemu_ivshmem->irq_event = RT_NULL; + + rt_spin_unlock_irqrestore(&qemu_ivshmem->rw_lock, level); + + break; + } + case RT_DEVICE_CTRL_GET_INT: + { + rt_ubase_t level = rt_spin_lock_irqsave(&qemu_ivshmem->rw_lock); + + *((rt_uint32_t *)args) = qemu_ivshmem->irq_status; + /* Only read once */ + qemu_ivshmem->irq_status = 0; + + rt_spin_unlock_irqrestore(&qemu_ivshmem->rw_lock, level); + + break; + } + default: + err = -RT_EINVAL; + break; + } + + return err; +} + +#ifdef RT_USING_DEVICE_OPS +const static struct rt_device_ops qemu_ivshmem_ops = +{ + .read = qemu_ivshmem_read, + .write = qemu_ivshmem_write, + .control = qemu_ivshmem_control +}; +#endif + +static rt_err_t qemu_ivshmem_isr(struct ivshmem_device *ivdev, int irq) +{ + struct qemu_ivshmem *qemu_ivshmem = raw_to_qemu_ivshmem(ivdev); + + rt_spin_lock(&qemu_ivshmem->rw_lock); + + if (qemu_ivshmem->is_msx) + { + qemu_ivshmem->irq_status |= 1 << (irq - qemu_ivshmem->parent.msix_entries[0].irq); + } + else + { + qemu_ivshmem->irq_status = RT_UINT32_MAX; + } + + if (qemu_ivshmem->irq_event) + { + rt_event_send(qemu_ivshmem->irq_event, qemu_ivshmem->irq_status); + } + + rt_spin_unlock(&qemu_ivshmem->rw_lock); + + return RT_EOK; +} + +static rt_err_t qemu_ivshmem_probe(struct rt_pci_device *pdev) +{ + rt_err_t err = RT_EOK; + const char *dev_name; + struct rt_device *parent; + struct qemu_ivshmem *qivdev = rt_malloc(sizeof(*qivdev)); + + if (!qivdev) + { + return -RT_ENOMEM; + } + + if ((err = ivshmem_pci_probe(pdev, &qivdev->parent))) + { + goto _fail; + } + + parent = &qivdev->parent.parent; + + rt_dm_dev_set_name_auto(parent, "ivshmem"); + dev_name = rt_dm_dev_get_name(parent); + + qivdev->parent.handle_irq = qemu_ivshmem_isr; + qivdev->irq_event = RT_NULL; + rt_spin_lock_init(&qivdev->rw_lock); + + /* rt_event_t supports only 32 events */ + if (!ivshmem_install_msix_vectors(&qivdev->parent, 32, dev_name)) + { + qivdev->is_msx = RT_TRUE; + } + else + { + qivdev->is_msx = RT_FALSE; + ivshmem_install_intx_vector(&qivdev->parent, dev_name); + } + + parent->type = RT_Device_Class_MTD; +#ifdef RT_USING_DEVICE_OPS + parent->ops = &qemu_ivshmem_ops; +#else + parent->read = qemu_ivshmem_read; + parent->write qemu_ivshmem_write; + parent->control = qemu_ivshmem_control; +#endif + + rt_device_register(parent, dev_name, RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_REMOVABLE); + + return RT_EOK; + +_fail: + rt_free(qivdev); + + return err; +} + +static rt_err_t qemu_ivshmem_remove(struct rt_pci_device *pdev) +{ + struct ivshmem_device *ivdev = ivshmem_pci_remove(pdev); + struct qemu_ivshmem *qivdev = raw_to_qemu_ivshmem(ivdev); + + rt_device_unregister(&qivdev->parent.parent); + rt_free(qivdev); + + return RT_EOK; +} + +static struct rt_pci_device_id qemu_ivshmem_pci_ids[] = +{ + { + RT_PCI_DEVICE_ID(PCI_VENDOR_ID_REDHAT_QUMRANET, 0x1110), + .class = PCIC_MEMORY << 16, + .class_mask = 0xff << 16, + }, + { /* sentinel */ } +}; + +static struct rt_pci_driver qemu_ivshmem_driver = +{ + .name = "ivshmem-qemu", + + .ids = qemu_ivshmem_pci_ids, + .probe = qemu_ivshmem_probe, + .remove = qemu_ivshmem_remove, +}; +RT_PCI_DRIVER_EXPORT(qemu_ivshmem_driver); + +#if defined(RT_USING_CONSOLE) && defined(RT_USING_MSH) +#include + +static int ivshmem_qemu(int argc, char**argv) +{ + rt_err_t err = -RT_EINVAL, res; + const char *dev_name, *opt; + struct ivshmem_cmd cmd; + struct rt_device *ivdev; + + if (argc < 3) + { + goto _help; + } + + dev_name = argv[1]; + ivdev = rt_device_find(dev_name); + + if (!ivdev) + { + rt_kprintf("dev '%s' not found\n", dev_name); + goto _help; + } + + if (rt_device_open(ivdev, 0)) + { + rt_kprintf("dev '%s' open fail\n", dev_name); + goto _help; + } + + err = RT_EOK; + opt = argv[2]; + + if (!rt_strcmp(opt, "info")) + { + cmd.is_read = RT_TRUE; + cmd.reg_off = IVSHMEM_IV_POSITION; + + res = rt_device_control(ivdev, RT_DEVICE_CTRL_CONFIG, &cmd); + + if (!res) + { + rt_kprintf("%s ivposition = %d\n", dev_name, cmd.value); + } + + goto _end; + } + + if (!rt_strcmp(opt, "set-int") && argc == 5) + { + cmd.is_read = RT_FALSE; + cmd.reg_off = IVSHMEM_DOORBELL; + cmd.value = ivshmem_doorbell(atol(argv[3]), atol(argv[4])); + + res = rt_device_control(ivdev, RT_DEVICE_CTRL_CONFIG, &cmd); + + if (!res) + { + rt_kprintf("%s set doorbell OK\n", dev_name); + } + + goto _end; + } + + if (!rt_strcmp(opt, "get-int")) + { + rt_event_t ev = rt_event_create("ivshmem", RT_IPC_FLAG_PRIO); + + if (!ev) + { + rt_kprintf("create event fail\n"); + } + + res = rt_device_control(ivdev, RT_DEVICE_CTRL_SET_INT, ev); + + if (!res) + { + rt_uint32_t irq_status; + + if (!rt_event_recv(ev, RT_UINT32_MAX, RT_EVENT_FLAG_OR, RT_WAITING_FOREVER, &irq_status)) + { + rt_kprintf("irq status = 0x%x\n", irq_status); + } + + rt_device_control(ivdev, RT_DEVICE_CTRL_CLR_INT, RT_NULL); + } + + rt_event_delete(ev); + + goto _end; + } + + if (!rt_strcmp(opt, "write") && argc == 5) + { + rt_device_write(ivdev, atol(argv[3]), argv[4], rt_strlen(argv[4])); + + goto _end; + } + + if (!rt_strcmp(opt, "read") && argc == 5) + { + long pos = atol(argv[3]); + long size = atol(argv[4]); + + while (size --> 0) + { + rt_uint8_t value; + rt_device_read(ivdev, pos, &value, 1); + + rt_kprintf("[%2d] = %02x (%c)\n", pos, value, value); + + ++pos; + } + + goto _end; + } + + err = -RT_EINVAL; + +_help: + if (err) + { + const char *cmd_prefix = "\t " RT_STRINGIFY(__FUNCTION__) " "; + + rt_kputs("Usage:\n"); + rt_kprintf("%s%s\n", cmd_prefix, "info"); + rt_kprintf("%s%s \n", cmd_prefix, "set-int"); + rt_kprintf("%s%s\n", cmd_prefix, "get-int"); + rt_kprintf("%s%s \n", cmd_prefix, "write"); + rt_kprintf("%s%s \n", cmd_prefix, "read"); + } + +_end: + return err; +} +MSH_CMD_EXPORT(ivshmem_qemu, ivshmem qemu device command); +#endif /* RT_USING_CONSOLE && RT_USING_MSH */ diff --git a/components/drivers/ivshmem/ivshmem.c b/components/drivers/ivshmem/ivshmem.c new file mode 100755 index 000000000000..3707c9f7b908 --- /dev/null +++ b/components/drivers/ivshmem/ivshmem.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-10-27 GuEe-GUI first version + */ + +#include +#include + +#define DBG_TAG "rtdm.ivshmem" +#define DBG_LVL DBG_INFO +#include + +#include +#include "ivshmem.h" + +static void ivshmem_isr(int irqno, void *param) +{ + rt_uint32_t status; + struct ivshmem_device *ivdev = param; + + status = HWREG32(ivdev->reg + IVSHMEM_INTR_STATUS); + + if ((status && status != 0xffffffff) || ivdev->nvectors) + { + if (ivdev->handle_irq) + { + ivdev->handle_irq(ivdev, ivdev->nvectors ? irqno : status); + } + else + { + LOG_E("IVSHMEM<%p> have a irq = %d, by no have irq handers", ivdev, irqno); + } + } +} + +rt_err_t ivshmem_install_msix_vectors(struct ivshmem_device *ivdev, int nvectors, const char *name) +{ + rt_err_t err = RT_EOK; + char irq_name[RT_NAME_MAX]; + + if (!ivdev || !name) + { + return -RT_EINVAL; + } + + if (ivdev->nvectors != 0) + { + LOG_E("IVSHMEM<%p> '%s' have already install msix", ivdev, name); + + return -RT_ERROR; + } + + if (!ivdev->handle_irq) + { + LOG_E("IVSHMEM<%p> '%s' no have irq handers", ivdev, name); + + return -RT_EINVAL; + } + + ivdev->msix_entries = rt_malloc(nvectors * sizeof(*ivdev->msix_entries)); + + if (!ivdev->msix_entries) + { + return -RT_ENOMEM; + } + + ivdev->nvectors = nvectors; + + for (int i = 0; i < nvectors; ++i) + { + ivdev->msix_entries[i].index = i; + } + + if ((err = rt_pci_msix_enable(ivdev->pdev, ivdev->msix_entries, ivdev->nvectors))) + { + rt_free(ivdev->msix_entries); + ivdev->nvectors = 0; + + return err; + } + + for (int i = 0; i < nvectors; i++) + { + int irq = ivdev->msix_entries[i].irq; + + rt_snprintf(irq_name, RT_NAME_MAX, "%s%d", name, irq); + + rt_hw_interrupt_install(irq, ivshmem_isr, ivdev, irq_name); + rt_hw_interrupt_umask(irq); + } + + return err; +} + +rt_err_t ivshmem_install_intx_vector(struct ivshmem_device *ivdev, const char *name) +{ + rt_err_t err = RT_EOK; + + if (!ivdev || !name) + { + return -RT_EINVAL; + } + + if (ivdev->nvectors != 0) + { + LOG_E("IVSHMEM<%p> '%s' have already install msix", ivdev, name); + + return -RT_ERROR; + } + + if (!ivdev->handle_irq) + { + LOG_E("IVSHMEM<%p> '%s' no have irq handers", ivdev, name); + + return -RT_EINVAL; + } + + ivdev->nvectors = 0; + ivdev->msix_entries = RT_NULL; + + rt_hw_interrupt_install(ivdev->irq, ivshmem_isr, ivdev, name); + rt_pci_irq_unmask(ivdev->pdev); + + return err; +} + +static void ivshmem_free_resource(struct ivshmem_device *ivdev) +{ + if (ivdev->pdev) + { + ivdev->pdev->sysdata = RT_NULL; + } + + if (ivdev->reg) + { + rt_iounmap(ivdev->reg); + } + + if (ivdev->shmem) + { + rt_iounmap(ivdev->shmem); + } +} + +rt_err_t ivshmem_pci_probe(struct rt_pci_device *pdev, struct ivshmem_device *ivdev) +{ + rt_err_t err = RT_EOK; + + if (!ivdev || !pdev || pdev->irq < 0) + { + return -RT_EINVAL; + } + + rt_memset(ivdev, 0, sizeof(*ivdev)); + + ivdev->pdev = pdev; + ivdev->irq = pdev->irq; + + pdev->sysdata = ivdev; + + ivdev->reg = rt_pci_iomap(pdev, IVSHMEM_REG_BAR); + + if (!ivdev->reg) + { + err = -RT_EIO; + goto _fail; + } + + /* Set all masks to on */ + HWREG32(ivdev->reg + IVSHMEM_INTR_MASK) = 0xffffffff; + + ivdev->shmem_size = pdev->resource[IVSHMEM_SHARE_MEM_BAR].size; + ivdev->shmem = (void *)pdev->resource[IVSHMEM_SHARE_MEM_BAR].base; + + if (!ivdev->shmem_size) + { + rt_pci_read_config_u32(pdev, IVSHMEM_JAILHOUSE_SHMEM_ADDR_LO, (rt_uint32_t *)&ivdev->shmem); + rt_pci_read_config_u32(pdev, IVSHMEM_JAILHOUSE_SHMEM_ADDR_HI, (rt_uint32_t *)&ivdev->shmem + 1); + rt_pci_read_config_u32(pdev, IVSHMEM_JAILHOUSE_SHMEM_SIZE_LO, (rt_uint32_t *)&ivdev->shmem_size); + rt_pci_read_config_u32(pdev, IVSHMEM_JAILHOUSE_SHMEM_SIZE_HI, (rt_uint32_t *)&ivdev->shmem_size + 1); + } + + ivdev->shmem = rt_ioremap(ivdev->shmem, ivdev->shmem_size); + + if (!ivdev->shmem) + { + err = -RT_ERROR; + goto _fail; + } + + return RT_EOK; + +_fail: + ivshmem_free_resource(ivdev); + + return err; +} + +struct ivshmem_device *ivshmem_pci_remove(struct rt_pci_device *pdev) +{ + struct ivshmem_device *ivdev = RT_NULL; + + if (!pdev || !pdev->sysdata) + { + return ivdev; + } + + ivdev = pdev->sysdata; + + if (ivdev->msix_entries) + { + for (int i = 0; i < ivdev->nvectors; ++i) + { + int irq = ivdev->msix_entries[i].irq; + + rt_hw_interrupt_umask(irq); + rt_hw_interrupt_uninstall(irq, ivshmem_isr, ivdev); + } + } + + rt_hw_interrupt_umask(ivdev->irq); + rt_hw_interrupt_uninstall(ivdev->irq, ivshmem_isr, ivdev); + + if (ivdev->nvectors) + { + rt_pci_msix_disable(ivdev->pdev); + rt_free(ivdev->msix_entries); + } + else + { + rt_pci_irq_unmask(ivdev->pdev); + } + + ivshmem_free_resource(ivdev); + + return ivdev; +} diff --git a/components/drivers/ivshmem/ivshmem.h b/components/drivers/ivshmem/ivshmem.h new file mode 100755 index 000000000000..03e03ae20eb1 --- /dev/null +++ b/components/drivers/ivshmem/ivshmem.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-10-27 GuEe-GUI first version + */ + +#ifndef __IVSHMEM_H__ +#define __IVSHMEM_H__ + +#include +#include + +/* Registers */ +#define IVSHMEM_INTR_MASK 0 +#define IVSHMEM_INTR_STATUS 4 +#define IVSHMEM_IV_POSITION 8 +#define IVSHMEM_DOORBELL 12 + +/* BAR */ +#define IVSHMEM_REG_BAR 0 +#define IVSHMEM_MSIX_BAR 1 +#define IVSHMEM_SHARE_MEM_BAR 2 + +#define IVSHMEM_MAX_VECTORS 255 + +/* JailHouse support */ +#define IVSHMEM_JAILHOUSE_SHMEM_ADDR_LO 0x40 +#define IVSHMEM_JAILHOUSE_SHMEM_ADDR_HI 0x44 +#define IVSHMEM_JAILHOUSE_SHMEM_SIZE_LO 0x48 +#define IVSHMEM_JAILHOUSE_SHMEM_SIZE_HI 0x4c + +struct ivshmem_cmd +{ + rt_bool_t is_read; + rt_uint32_t reg_off; + rt_uint32_t value; +}; + +struct ivshmem_device +{ + struct rt_device parent; + struct rt_pci_device *pdev; + + void *reg; + int irq; + + void *shmem; + rt_size_t shmem_size; + + int nvectors; + struct rt_pci_msix_entry *msix_entries; + + rt_err_t (*handle_irq)(struct ivshmem_device *, int irq); +}; + +#define to_ivshmem(dev) rt_container_of(dev, struct ivshmem_device, parent) + +rt_inline rt_uint32_t ivshmem_doorbell(int peer_id, int vector_index) +{ + return (rt_uint32_t)((peer_id << 16) | (vector_index & IVSHMEM_MAX_VECTORS)); +} + +rt_err_t ivshmem_install_msix_vectors(struct ivshmem_device *ivdev, int nvectors, const char *name); +rt_err_t ivshmem_install_intx_vector(struct ivshmem_device *ivdev, const char *name); + +rt_err_t ivshmem_pci_probe(struct rt_pci_device *pdev, struct ivshmem_device *ivdev); +struct ivshmem_device *ivshmem_pci_remove(struct rt_pci_device *pdev); + +#endif /* __IVSHMEM_H__ */ 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..204bdcde0e39 --- /dev/null +++ b/components/drivers/mfd/rk8xx.c @@ -0,0 +1,1067 @@ +/* + * 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 rk817_battery_irqs[] = +{ + RK817_IRQ_VB_LO, +}; + +static const int rk818_battery_irqs[] = +{ + RK818_IRQ_VB_LO, +}; + +static const int rk805_charger_irqs[] = +{ + RK805_IRQ_VB_LOW, +}; + +static const int rk817_charger_irqs[] = +{ + RK817_IRQ_PLUG_IN, + RK817_IRQ_PLUG_OUT, +}; + +static const int rk818_charger_irqs[] = +{ + RK818_IRQ_PLUG_IN, + RK818_IRQ_PLUG_OUT, +}; + +static const int rk817_codec_irqs[] = +{ + RK817_IRQ_CODEC_PD, + RK817_IRQ_CODEC_PO, +}; + +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, + }, + { + .name = "rk8xx-battery", + .ofw_name = "battery", + .irqs_nr = RT_ARRAY_SIZE(rk805_charger_irqs), + .irqs_list = rk805_charger_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, + }, + { + .name = "rk8xx-battery", + .ofw_name = "battery", + .irqs_nr = RT_ARRAY_SIZE(rk817_battery_irqs), + .irqs_list = rk817_battery_irqs, + }, + { + .name = "rk8xx-charger", + .ofw_name = "charger", + .irqs_nr = RT_ARRAY_SIZE(rk817_charger_irqs), + .irqs_list = rk817_charger_irqs, + }, + { + .name = "rk8xx-codec", + .ofw_name = "codec", + .irqs_nr = RT_ARRAY_SIZE(rk817_codec_irqs), + .irqs_list = rk817_codec_irqs, + }, +}; + +static const struct rk8xx_endpoint rk818_eps[] = +{ + { .name = "rk8xx-clkout", }, + { .name = "rk8xx-regulator", .ofw_name = "regulators" }, + { + .name = "rk8xx-battery", + .ofw_name = "battery", + .irqs_nr = RT_ARRAY_SIZE(rk818_battery_irqs), + .irqs_list = rk818_battery_irqs, + }, + { + .name = "rk8xx-charger", + .ofw_name = "charger", + .irqs_nr = RT_ARRAY_SIZE(rk818_charger_irqs), + .irqs_list = rk818_charger_irqs, + }, + { + .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..60e489ee431a --- /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 (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..dfff84da49a5 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) @@ -57,9 +58,102 @@ struct rt_ofw_stub *rt_ofw_stub_probe_range(struct rt_ofw_node *np, return (struct rt_ofw_stub *)stub; } +struct ofw_obj_cmp_list +{ + const char *cells_name; + const char *obj_name; + rt_size_t obj_size; +}; + +static const struct ofw_obj_cmp_list ofw_obj_cmp_list[] = +{ +#ifdef RT_USING_CLK + { "#clock-cells", RT_CLK_NODE_OBJ_NAME, sizeof(struct rt_clk_node) }, +#endif +#ifdef RT_USING_RESET + { "#reset-cells", RT_RESET_CONTROLLER_OBJ_NAME, sizeof(struct rt_reset_controller) }, +#endif + { "#power-domain-cells", RT_POWER_DOMAIN_PROXY_OBJ_NAME, sizeof(struct rt_dm_power_domain_proxy) }, + { "#power-domain-cells", RT_POWER_DOMAIN_OBJ_NAME, sizeof(struct rt_dm_power_domain) }, +}; + +static struct rt_object *ofw_parse_object(struct rt_ofw_node *np, const char *cells_name) +{ + const struct ofw_obj_cmp_list *item; + struct rt_object *obj = rt_ofw_data(np), *ret_obj = RT_NULL; + DECLARE_BITMAP(idx_mask, RT_ARRAY_SIZE(ofw_obj_cmp_list)) = {}; + + for (int i = 0; i < RT_ARRAY_SIZE(ofw_obj_cmp_list); ++i) + { + item = &ofw_obj_cmp_list[i]; + + if (!rt_ofw_prop_read_bool(np, item->cells_name)) + { + bitmap_set_bit(idx_mask, i); + } + } + + while (!ret_obj) + { + int i = 0; + + /* Is print ? */ + if (!((rt_uint32_t)(obj->name[0] - 0x20) < 0x5f)) + { + break; + } + + bitmap_for_each_clear_bit(idx_mask, i, RT_ARRAY_SIZE(ofw_obj_cmp_list)) + { + item = &ofw_obj_cmp_list[i]; + + if (!rt_strcmp(item->cells_name, cells_name)) + { + ret_obj = obj; + break; + } + + if (!rt_strncmp(item->obj_name, obj->name, RT_NAME_MAX)) + { + obj = (struct rt_object *)((rt_ubase_t)obj + item->obj_size); + break; + } + } + + if (rt_unlikely(i >= RT_ARRAY_SIZE(ofw_obj_cmp_list))) + { + LOG_E("Invalid rt_object = %s", obj->name); + + break; + } + } + + return ret_obj; +} + +struct rt_object *rt_ofw_parse_object(struct rt_ofw_node *np, const char *obj_name, const char *cells_name) +{ + struct rt_object *obj = RT_NULL, *test_obj; + + if (np && (test_obj = rt_ofw_data(np)) && cells_name) + { + /* The composite object is rare, so we try to find this object as much as possible at once. */ + if (obj_name && rt_strcmp(test_obj->name, obj_name)) + { + obj = test_obj; + } + else + { + obj = ofw_parse_object(np, cells_name); + } + } + + return obj; +} + 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 +177,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 +224,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 +245,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 +260,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 +282,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 +335,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 +392,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 +606,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 +647,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..658262522c36 --- /dev/null +++ b/components/drivers/phy/rockchip/phy-rockchip-naneng-combphy.c @@ -0,0 +1,857 @@ +/* + * 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 rt_device *dev = &pdev->parent; + struct rockchip_combphy *rk_cphy = dev->user_data; + + rt_dm_dev_unbind_fwdata(dev, RT_NULL); + + 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..2e02fbe6b1ba --- /dev/null +++ b/components/drivers/phy/rockchip/phy-rockchip-snps-pcie3.c @@ -0,0 +1,465 @@ +/* + * 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 rt_device *dev = &pdev->parent; + struct rockchip_pcie3_phy *rk_p3phy = dev->user_data; + + rt_dm_dev_unbind_fwdata(dev, RT_NULL); + + 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..19a26df4297a 100755 --- a/components/drivers/pic/pic-bcm2836.c +++ b/components/drivers/pic/pic-bcm2836.c @@ -10,8 +10,9 @@ #include #include +#include -#define DBG_TAG "irqchip.bcm2836" +#define DBG_TAG "pic.bcm2836" #define DBG_LVL DBG_INFO #include @@ -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..6a13d046d044 --- /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 "pic.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..4d2a2f50d5b1 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.gicv2m" #define DBG_LVL DBG_INFO #include +#include #include -#include -#include - -#include -#include -#include -#include -#include -#include +#include "pic-gic-common.h" /* * MSI_TYPER: @@ -56,322 +50,312 @@ 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; + rt_ubase_t addr; + struct gicv2m *v2m = raw_to_gicv2m(pirq->pic); - if (v2m) - { - rt_ubase_t addr = gicv2m_get_msi_addr(v2m, data->hwirq); + 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) - { - msg->data = 0; - } - else - { - msg->data = data->hwirq; - } + if (v2m->flags & GICV2M_GRAVITON_ADDRESS_ONLY) + { + msg->data = 0; + } + else + { + msg->data = pirq->hwirq; + } - if (v2m->flags & GICV2M_NEEDS_SPI_OFFSET) - { - msg->data -= v2m->spi_offset; - } + if (v2m->flags & GICV2M_NEEDS_SPI_OFFSET) + { + msg->data -= v2m->spi_offset; } } -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_spin_unlock_irqrestore(&_v2m_lock, level); + rt_pic_cascade(pirq, parent_irq); + + 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); + rt_ubase_t level; + struct rt_pic_irq *pirq; + struct gicv2m *v2m = raw_to_gicv2m(pic); - if (!rt_list_isempty(&_v2m_nodes)) - { - struct rt_irq_data irqdata = { .irq = -1 }; + pirq = rt_pic_find_pirq(pic, irq); - rt_irqchip_search_irq(irq, &irqdata); - - 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_spin_lock(&_v2m_lock); - rt_list_insert_after(&_v2m_nodes, &v2m->list); - rt_spin_unlock(&_v2m_lock); + rt_ofw_data(v2m_np) = &v2m->parent; + rt_ofw_node_set_flag(v2m_np, RT_OFW_F_READLY); - rt_of_data(v2m_np) = &v2m->parent; + continue; - rt_of_node_set_flag(v2m_np, OF_F_READY); + _fail: + rt_iounmap(v2m->base); + rt_free(v2m); + + 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..397fce3cdcf9 --- /dev/null +++ b/components/drivers/pmdomain/Kconfig @@ -0,0 +1,24 @@ +if RT_USING_DM +menu "Power Management (PM) Domains device drivers" + +config RT_PMDOMAIN_BCM2835 + bool "BCM2835" + select RT_USING_RESET + default n + +config RT_PMDOMAIN_RASPBERRYPI + bool "Raspberrypi" + default n + +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..a9dd502c3ba4 --- /dev/null +++ b/components/drivers/pmdomain/SConscript @@ -0,0 +1,27 @@ +from building import * + +group = [] + +if not GetDepend(['RT_USING_DM']): + Return('group') + +cwd = GetCurrentDir() +CPPPATH = [cwd + '/../include'] + +src = [] + +if GetDepend(['RT_PMDOMAIN_BCM2835']): + src += ['pm-domain-bcm2835.c'] + +if GetDepend(['RT_PMDOMAIN_RASPBERRYPI']): + src += ['pm-domain-raspberrypi.c'] + +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-bcm2835.c b/components/drivers/pmdomain/pm-domain-bcm2835.c new file mode 100644 index 000000000000..0303e7b5c286 --- /dev/null +++ b/components/drivers/pmdomain/pm-domain-bcm2835.c @@ -0,0 +1,740 @@ +/* + * 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 "pm-domain.bcm2835" +#define DBG_LVL DBG_INFO +#include + +#include +#include "../mfd/bcm2835-pm.h" +#include + +#define PM_GNRIC 0x00 +#define PM_AUDIO 0x04 +#define PM_STATUS 0x18 +#define PM_RSTC 0x1c +#define PM_RSTS 0x20 +#define PM_WDOG 0x24 +#define PM_PADS0 0x28 +#define PM_PADS2 0x2c +#define PM_PADS3 0x30 +#define PM_PADS4 0x34 +#define PM_PADS5 0x38 +#define PM_PADS6 0x3c +#define PM_CAM0 0x44 +#define PM_CAM0_LDOHPEN RT_BIT(2) +#define PM_CAM0_LDOLPEN RT_BIT(1) +#define PM_CAM0_CTRLEN RT_BIT(0) + +#define PM_CAM1 0x48 +#define PM_CAM1_LDOHPEN RT_BIT(2) +#define PM_CAM1_LDOLPEN RT_BIT(1) +#define PM_CAM1_CTRLEN RT_BIT(0) + +#define PM_CCP2TX 0x4c +#define PM_CCP2TX_LDOEN RT_BIT(1) +#define PM_CCP2TX_CTRLEN RT_BIT(0) + +#define PM_DSI0 0x50 +#define PM_DSI0_LDOHPEN RT_BIT(2) +#define PM_DSI0_LDOLPEN RT_BIT(1) +#define PM_DSI0_CTRLEN RT_BIT(0) + +#define PM_DSI1 0x54 +#define PM_DSI1_LDOHPEN RT_BIT(2) +#define PM_DSI1_LDOLPEN RT_BIT(1) +#define PM_DSI1_CTRLEN RT_BIT(0) + +#define PM_HDMI 0x58 +#define PM_HDMI_RSTDR RT_BIT(19) +#define PM_HDMI_LDOPD RT_BIT(1) +#define PM_HDMI_CTRLEN RT_BIT(0) + +#define PM_USB 0x5c +/* + * The power gates must be enabled with this bit before enabling the LDO in the + * USB block. + */ +#define PM_USB_CTRLEN RT_BIT(0) + +#define PM_PXLDO 0x60 +#define PM_PXBG 0x64 +#define PM_DFT 0x68 +#define PM_SMPS 0x6c +#define PM_XOSC 0x70 +#define PM_SPAREW 0x74 +#define PM_SPARER 0x78 +#define PM_AVS_RSTDR 0x7c +#define PM_AVS_STAT 0x80 +#define PM_AVS_EVENT 0x84 +#define PM_AVS_INTEN 0x88 +#define PM_DUMMY 0xfc + +#define PM_IMAGE 0x108 +#define PM_GRAFX 0x10c +#define PM_PROC 0x110 +#define PM_ENAB RT_BIT(12) +#define PM_ISPRSTN RT_BIT(8) +#define PM_H264RSTN RT_BIT(7) +#define PM_PERIRSTN RT_BIT(6) +#define PM_V3DRSTN RT_BIT(6) +#define PM_ISFUNC RT_BIT(5) +#define PM_MRDONE RT_BIT(4) +#define PM_MEMREP RT_BIT(3) +#define PM_ISPOW RT_BIT(2) +#define PM_POWOK RT_BIT(1) +#define PM_POWUP RT_BIT(0) +#define PM_INRUSH_SHIFT 13 +#define PM_INRUSH_3_5_MA 0 +#define PM_INRUSH_5_MA 1 +#define PM_INRUSH_10_MA 2 +#define PM_INRUSH_20_MA 3 +#define PM_INRUSH_MASK (3 << PM_INRUSH_SHIFT) + +#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 + +#define PM_READ(reg) HWREG32(power->base + (reg)) +#define PM_WRITE(reg, val) HWREG32(power->base + (reg)) = PM_PASSWORD | (val) + +#define ASB_BRDG_VERSION 0x00 +#define ASB_CPR_CTRL 0x04 + +#define ASB_V3D_S_CTRL 0x08 +#define ASB_V3D_M_CTRL 0x0c +#define ASB_ISP_S_CTRL 0x10 +#define ASB_ISP_M_CTRL 0x14 +#define ASB_H264_S_CTRL 0x18 +#define ASB_H264_M_CTRL 0x1c + +#define ASB_REQ_STOP RT_BIT(0) +#define ASB_ACK RT_BIT(1) +#define ASB_EMPTY RT_BIT(2) +#define ASB_FULL RT_BIT(3) + +#define ASB_AXI_BRDG_ID 0x20 + +#define BCM2835_BRDG_ID 0x62726467 + +struct bcm2835_power; + +struct bcm2835_power_domain +{ + struct rt_dm_power_domain parent; + const char *name; + + rt_uint32_t domain; + struct rt_clk *clk; + struct bcm2835_power *power; +}; + +#define raw_to_rpi_bcm2835_power_domain(raw) rt_container_of(raw, struct bcm2835_power_domain, parent) + +struct bcm2835_power +{ + struct rt_dm_power_domain_proxy proxy_parent; + struct rt_reset_controller rstc_parent; + + void *base; /* PM registers. */ + void *asb; /* AXI Async bridge registers. */ + void *rpivid_asb; /* RPiVid bridge registers. */ + + struct bcm2835_power_domain domains[BCM2835_POWER_DOMAIN_COUNT]; +}; + +#define raw_to_bcm2835_power(raw) rt_container_of(raw, struct bcm2835_power, proxy_parent) + +static rt_err_t bcm2835_asb_control(struct bcm2835_power *power, int reg, rt_bool_t enable) +{ + rt_uint32_t val; + rt_bool_t timeout; + void *base = power->asb; + + switch (reg) + { + case 0: + return RT_EOK; + + case ASB_V3D_S_CTRL: + case ASB_V3D_M_CTRL: + if (power->rpivid_asb) + { + base = power->rpivid_asb; + } + break; + + default: + return -RT_EINVAL; + } + + timeout = RT_FALSE; + + /* Enable the module's async AXI bridges. */ + if (enable) + { + val = HWREG32(base + reg) & ~ASB_REQ_STOP; + } + else + { + val = HWREG32(base + reg) | ASB_REQ_STOP; + } + + HWREG32(base + reg) = PM_PASSWORD | val; + + while (!!(HWREG32(base + reg) & ASB_ACK) == enable) + { + if (timeout) + { + return -RT_ETIMEOUT; + } + + rt_hw_us_delay(1); + timeout = RT_TRUE; + + rt_hw_cpu_relax(); + } + + return RT_EOK; +} + +static rt_err_t bcm2835_asb_enable(struct bcm2835_power *power, int reg) +{ + return bcm2835_asb_control(power, reg, RT_TRUE); +} + +static rt_err_t bcm2835_asb_disable(struct bcm2835_power *power, int reg) +{ + return bcm2835_asb_control(power, reg, RT_FALSE); +} + +static rt_err_t bcm2835_power_power_off(struct bcm2835_power_domain *pd, int pm_reg) +{ + struct bcm2835_power *power = pd->power; + + /* We don't run this on BCM2711 */ + if (power->rpivid_asb) + { + return RT_EOK; + } + + /* Enable functional isolation */ + PM_WRITE(pm_reg, PM_READ(pm_reg) & ~PM_ISFUNC); + + /* Enable electrical isolation */ + PM_WRITE(pm_reg, PM_READ(pm_reg) & ~PM_ISPOW); + + /* Open the power switches. */ + PM_WRITE(pm_reg, PM_READ(pm_reg) & ~PM_POWUP); + + return RT_EOK; +} + +static rt_err_t bcm2835_power_power_on(struct bcm2835_power_domain *pd, rt_uint32_t pm_reg) +{ + rt_err_t err; + int inrush; + rt_bool_t powok, timeout; + struct bcm2835_power *power = pd->power; + + /* We don't run this on BCM2711 */ + if (power->rpivid_asb) + { + return RT_EOK; + } + + /* If it was already powered on by the fw, leave it that way. */ + if (PM_READ(pm_reg) & PM_POWUP) + { + return RT_EOK; + } + + /* + * Enable power. Allowing too much current at once may result + * in POWOK never getting set, so start low and ramp it up as + * necessary to succeed. + */ + powok = RT_FALSE; + for (inrush = PM_INRUSH_3_5_MA; inrush <= PM_INRUSH_20_MA; ++inrush) + { + PM_WRITE(pm_reg, (PM_READ(pm_reg) & ~PM_INRUSH_MASK) | + (inrush << PM_INRUSH_SHIFT) | PM_POWUP); + + for (int i = 0; i < 3; ++i) + { + if ((powok = !!(PM_READ(pm_reg) & PM_POWOK))) + { + break; + } + + rt_hw_us_delay(1); + rt_hw_cpu_relax(); + } + } + + if (!powok) + { + LOG_E("Timeout waiting for %s power OK", pd->name); + + err = -RT_ETIMEOUT; + goto _err_disable_powup; + } + + /* Disable electrical isolation */ + PM_WRITE(pm_reg, PM_READ(pm_reg) | PM_ISPOW); + + /* Repair memory */ + PM_WRITE(pm_reg, PM_READ(pm_reg) | PM_MEMREP); + + timeout = RT_FALSE; + while (!(PM_READ(pm_reg) & PM_MRDONE)) + { + if (timeout) + { + LOG_E("Timeout waiting for %s memory repair", pd->name); + + err = -RT_ETIMEOUT; + goto _err_disable_ispow; + } + + rt_hw_us_delay(1); + timeout = RT_TRUE; + + rt_hw_cpu_relax(); + } + + /* Disable functional isolation */ + PM_WRITE(pm_reg, PM_READ(pm_reg) | PM_ISFUNC); + + return RT_EOK; + +_err_disable_ispow: + PM_WRITE(pm_reg, PM_READ(pm_reg) & ~PM_ISPOW); + +_err_disable_powup: + PM_WRITE(pm_reg, PM_READ(pm_reg) & ~(PM_POWUP | PM_INRUSH_MASK)); + + return err; +} + +static rt_err_t bcm2835_asb_power_on(struct bcm2835_power_domain *pd, + rt_uint32_t pm_reg, rt_uint32_t asb_m_reg, + rt_uint32_t asb_s_reg, rt_uint32_t reset_flags) +{ + rt_err_t err; + struct bcm2835_power *power = pd->power; + + if ((err = rt_clk_prepare_enable(pd->clk))) + { + LOG_E("Failed to enable clock for %s", pd->name); + return err; + } + + /* Wait 32 clocks for reset to propagate, 1 us will be enough */ + rt_hw_us_delay(1); + + rt_clk_disable_unprepare(pd->clk); + + /* Deassert the resets. */ + PM_WRITE(pm_reg, PM_READ(pm_reg) | reset_flags); + + if ((err = rt_clk_prepare_enable(pd->clk))) + { + LOG_E("Failed to enable clock for %s", pd->name); + goto _err_enable_resets; + } + + if ((err = bcm2835_asb_enable(power, asb_m_reg))) + { + LOG_E("Failed to enable ASB master for %s", pd->name); + goto _err_disable_clk; + } + + if ((err = bcm2835_asb_enable(power, asb_s_reg))) + { + LOG_E("Failed to enable ASB slave for %s", pd->name); + goto _err_disable_asb_master; + } + + return RT_EOK; + +_err_disable_asb_master: + bcm2835_asb_disable(power, asb_m_reg); + +_err_disable_clk: + rt_clk_disable_unprepare(pd->clk); + +_err_enable_resets: + PM_WRITE(pm_reg, PM_READ(pm_reg) & ~reset_flags); + + return err; +} + +static rt_err_t bcm2835_asb_power_off(struct bcm2835_power_domain *pd, + rt_uint32_t pm_reg, rt_uint32_t asb_m_reg, + rt_uint32_t asb_s_reg, rt_uint32_t reset_flags) +{ + rt_err_t err; + struct bcm2835_power *power = pd->power; + + if ((err = bcm2835_asb_disable(power, asb_s_reg))) + { + LOG_W("Failed to disable ASB slave for %s", pd->name); + + return err; + } + + if ((err = bcm2835_asb_disable(power, asb_m_reg))) + { + LOG_W("Failed to disable ASB master for %s", pd->name); + bcm2835_asb_enable(power, asb_s_reg); + + return err; + } + + rt_clk_disable_unprepare(pd->clk); + + /* Assert the resets. */ + PM_WRITE(pm_reg, PM_READ(pm_reg) & ~reset_flags); + + return 0; +} + +static rt_err_t bcm2835_power_pd_power_on(struct rt_dm_power_domain *domain) +{ + struct bcm2835_power_domain *pd = raw_to_rpi_bcm2835_power_domain(domain); + struct bcm2835_power *power = pd->power; + + switch (pd->domain) + { + case BCM2835_POWER_DOMAIN_GRAFX: + return bcm2835_power_power_on(pd, PM_GRAFX); + + case BCM2835_POWER_DOMAIN_GRAFX_V3D: + return bcm2835_asb_power_on(pd, PM_GRAFX, + ASB_V3D_M_CTRL, ASB_V3D_S_CTRL, PM_V3DRSTN); + + case BCM2835_POWER_DOMAIN_IMAGE: + return bcm2835_power_power_on(pd, PM_IMAGE); + + case BCM2835_POWER_DOMAIN_IMAGE_PERI: + return bcm2835_asb_power_on(pd, PM_IMAGE, 0, 0, PM_PERIRSTN); + + case BCM2835_POWER_DOMAIN_IMAGE_ISP: + return bcm2835_asb_power_on(pd, PM_IMAGE, + ASB_ISP_M_CTRL, ASB_ISP_S_CTRL, PM_ISPRSTN); + + case BCM2835_POWER_DOMAIN_IMAGE_H264: + return bcm2835_asb_power_on(pd, PM_IMAGE, + ASB_H264_M_CTRL, ASB_H264_S_CTRL, PM_H264RSTN); + + case BCM2835_POWER_DOMAIN_USB: + PM_WRITE(PM_USB, PM_USB_CTRLEN); + return RT_EOK; + + case BCM2835_POWER_DOMAIN_DSI0: + PM_WRITE(PM_DSI0, PM_DSI0_CTRLEN); + PM_WRITE(PM_DSI0, PM_DSI0_CTRLEN | PM_DSI0_LDOHPEN); + return RT_EOK; + + case BCM2835_POWER_DOMAIN_DSI1: + PM_WRITE(PM_DSI1, PM_DSI1_CTRLEN); + PM_WRITE(PM_DSI1, PM_DSI1_CTRLEN | PM_DSI1_LDOHPEN); + return RT_EOK; + + case BCM2835_POWER_DOMAIN_CCP2TX: + PM_WRITE(PM_CCP2TX, PM_CCP2TX_CTRLEN); + PM_WRITE(PM_CCP2TX, PM_CCP2TX_CTRLEN | PM_CCP2TX_LDOEN); + return RT_EOK; + + case BCM2835_POWER_DOMAIN_HDMI: + PM_WRITE(PM_HDMI, PM_READ(PM_HDMI) | PM_HDMI_RSTDR); + PM_WRITE(PM_HDMI, PM_READ(PM_HDMI) | PM_HDMI_CTRLEN); + PM_WRITE(PM_HDMI, PM_READ(PM_HDMI) & ~PM_HDMI_LDOPD); + + rt_hw_us_delay(150); + PM_WRITE(PM_HDMI, PM_READ(PM_HDMI) & ~PM_HDMI_RSTDR); + return RT_EOK; + + default: + LOG_E("Invalid domain %d", pd->domain); + + return -RT_EINVAL; + } +} + +static rt_err_t bcm2835_power_pd_power_off(struct rt_dm_power_domain *domain) +{ + struct bcm2835_power_domain *pd = raw_to_rpi_bcm2835_power_domain(domain); + struct bcm2835_power *power = pd->power; + + switch (pd->domain) + { + case BCM2835_POWER_DOMAIN_GRAFX: + return bcm2835_power_power_off(pd, PM_GRAFX); + + case BCM2835_POWER_DOMAIN_GRAFX_V3D: + return bcm2835_asb_power_off(pd, PM_GRAFX, + ASB_V3D_M_CTRL, ASB_V3D_S_CTRL, PM_V3DRSTN); + + case BCM2835_POWER_DOMAIN_IMAGE: + return bcm2835_power_power_off(pd, PM_IMAGE); + + case BCM2835_POWER_DOMAIN_IMAGE_PERI: + return bcm2835_asb_power_off(pd, PM_IMAGE, 0, 0, PM_PERIRSTN); + + case BCM2835_POWER_DOMAIN_IMAGE_ISP: + return bcm2835_asb_power_off(pd, PM_IMAGE, + ASB_ISP_M_CTRL, ASB_ISP_S_CTRL, PM_ISPRSTN); + + case BCM2835_POWER_DOMAIN_IMAGE_H264: + return bcm2835_asb_power_off(pd, PM_IMAGE, + ASB_H264_M_CTRL, ASB_H264_S_CTRL, PM_H264RSTN); + + case BCM2835_POWER_DOMAIN_USB: + PM_WRITE(PM_USB, 0); + return RT_EOK; + + case BCM2835_POWER_DOMAIN_DSI0: + PM_WRITE(PM_DSI0, PM_DSI0_CTRLEN); + PM_WRITE(PM_DSI0, 0); + return RT_EOK; + + case BCM2835_POWER_DOMAIN_DSI1: + PM_WRITE(PM_DSI1, PM_DSI1_CTRLEN); + PM_WRITE(PM_DSI1, 0); + return RT_EOK; + + case BCM2835_POWER_DOMAIN_CCP2TX: + PM_WRITE(PM_CCP2TX, PM_CCP2TX_CTRLEN); + PM_WRITE(PM_CCP2TX, 0); + return RT_EOK; + + case BCM2835_POWER_DOMAIN_HDMI: + PM_WRITE(PM_HDMI, PM_READ(PM_HDMI) | PM_HDMI_LDOPD); + PM_WRITE(PM_HDMI, PM_READ(PM_HDMI) & ~PM_HDMI_CTRLEN); + return RT_EOK; + + default: + LOG_E("Invalid domain %d", pd->domain); + + return -RT_EINVAL; + } +} + +static struct rt_dm_power_domain *bcm2835_power_domains_ofw_parse( + struct rt_dm_power_domain_proxy *proxy, struct rt_ofw_cell_args *args) +{ + struct bcm2835_power *power = raw_to_bcm2835_power(proxy); + + return &power->domains[args->args[0]].parent; +} + +static rt_err_t bcm2835_reset_reset(struct rt_reset_control *rstc) +{ + rt_err_t err; + struct bcm2835_power_domain *pd; + struct bcm2835_power *power = rstc->rstcer->priv; + + switch (rstc->id) + { + case BCM2835_RESET_V3D: + pd = &power->domains[BCM2835_POWER_DOMAIN_GRAFX_V3D]; + break; + + case BCM2835_RESET_H264: + pd = &power->domains[BCM2835_POWER_DOMAIN_IMAGE_H264]; + break; + + case BCM2835_RESET_ISP: + pd = &power->domains[BCM2835_POWER_DOMAIN_IMAGE_ISP]; + break; + + default: + LOG_E("Invalid reset id %u", rstc->id); + return -RT_EINVAL; + } + + if ((err = bcm2835_power_pd_power_off(&pd->parent))) + { + return err; + } + + return bcm2835_power_pd_power_on(&pd->parent); +} + +static int bcm2835_reset_status(struct rt_reset_control *rstc) +{ + struct bcm2835_power *power = rstc->rstcer->priv; + + switch (rstc->id) + { + case BCM2835_RESET_V3D: + return !PM_READ(PM_GRAFX & PM_V3DRSTN); + + case BCM2835_RESET_H264: + return !PM_READ(PM_IMAGE & PM_H264RSTN); + + case BCM2835_RESET_ISP: + return !PM_READ(PM_IMAGE & PM_ISPRSTN); + + default: + return -RT_EINVAL; + } +} + +static const struct rt_reset_control_ops bcm2835_reset_ops = +{ + .reset = bcm2835_reset_reset, + .status = bcm2835_reset_status, +}; + +static const char *const power_domain_names[] = +{ + [BCM2835_POWER_DOMAIN_GRAFX] = "grafx", + [BCM2835_POWER_DOMAIN_GRAFX_V3D] = "v3d", + + [BCM2835_POWER_DOMAIN_IMAGE] = "image", + [BCM2835_POWER_DOMAIN_IMAGE_PERI] = "peri_image", + [BCM2835_POWER_DOMAIN_IMAGE_H264] = "h264", + [BCM2835_POWER_DOMAIN_IMAGE_ISP] = "isp", + + [BCM2835_POWER_DOMAIN_USB] = "usb", + [BCM2835_POWER_DOMAIN_DSI0] = "dsi0", + [BCM2835_POWER_DOMAIN_DSI1] = "dsi1", + [BCM2835_POWER_DOMAIN_CAM0] = "cam0", + [BCM2835_POWER_DOMAIN_CAM1] = "cam1", + [BCM2835_POWER_DOMAIN_CCP2TX] = "ccp2tx", + [BCM2835_POWER_DOMAIN_HDMI] = "hdmi", +}; + +static rt_err_t bcm2835_init_power_domain(struct bcm2835_power *power, + struct bcm2835_pm *pm, int id) +{ + const char *name = power_domain_names[id]; + struct bcm2835_power_domain *dom = &power->domains[id]; + + dom->clk = rt_ofw_get_clk_by_name(pm->ofw_node, name); + + if (rt_is_err(dom->clk)) + { + return rt_ptr_err(dom->clk); + } + + dom->power = power; + dom->parent.power_on = bcm2835_power_pd_power_on; + dom->parent.power_off = bcm2835_power_pd_power_off; + + rt_dm_power_domain_register(&dom->parent); + + return RT_EOK; +} + +static rt_err_t bcm2835_power_probe(struct rt_platform_device *pdev) +{ + rt_err_t err; + rt_uint32_t id; + struct rt_reset_controller *rstcer; + struct bcm2835_pm *pm = pdev->priv; + struct bcm2835_power *power = rt_calloc(1, sizeof(*power)); + + if (!power) + { + return -RT_ENOMEM; + } + + power->base = pm->base; + power->asb = pm->asb; + power->rpivid_asb = pm->rpivid_asb; + + id = HWREG32(power->asb + ASB_AXI_BRDG_ID); + /* "BRDG" */ + if (id != BCM2835_BRDG_ID) + { + LOG_E("ASB register ID returned 0x%08x", id); + + err = -RT_EINVAL; + goto _fail; + } + + if (power->rpivid_asb) + { + id = HWREG32(power->rpivid_asb + ASB_AXI_BRDG_ID); + /* "BRDG" */ + if (id != BCM2835_BRDG_ID) + { + LOG_E("RPiVid ASB register ID returned 0x%08x", id); + + err = -RT_EINVAL; + goto _fail; + } + } + + for (int i = 0; i < RT_ARRAY_SIZE(power_domain_names); ++i) + { + if ((err = bcm2835_init_power_domain(power, pm, i))) + { + goto _free_power_domain; + } + } + + power->proxy_parent.ofw_parse = bcm2835_power_domains_ofw_parse; + rt_dm_power_domain_proxy_ofw_bind(&power->proxy_parent, pm->ofw_node); + + rstcer = &power->rstc_parent; + rstcer->priv = power; + rstcer->ofw_node = pm->ofw_node; + rstcer->ops = &bcm2835_reset_ops; + + rt_reset_controller_register(rstcer); + + return RT_EOK; + +_free_power_domain: + for (int i = 0; i < RT_ARRAY_SIZE(power_domain_names); ++i) + { + struct bcm2835_power_domain *dom = &power->domains[id]; + + if (!dom->power) + { + break; + } + + rt_dm_power_domain_unregister(&dom->parent); + } + +_fail: + rt_free(power); + + return err; +} + +static struct rt_platform_driver bcm2835_power_driver = +{ + .name = "bcm2835-power", + .probe = bcm2835_power_probe, +}; + +static int bcm2835_power_drv_register(void) +{ + rt_platform_driver_register(&bcm2835_power_driver); + + return 0; +} +INIT_SUBSYS_LATER_EXPORT(bcm2835_power_drv_register); diff --git a/components/drivers/pmdomain/pm-domain-raspberrypi.c b/components/drivers/pmdomain/pm-domain-raspberrypi.c new file mode 100644 index 000000000000..a4f522753eb1 --- /dev/null +++ b/components/drivers/pmdomain/pm-domain-raspberrypi.c @@ -0,0 +1,233 @@ +/* + * 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 "../firmware/raspberrypi/firmware.h" + +/* + * Firmware indices for the old power domains interface. Only a few + * of them were actually implemented. + */ +#define RPI_OLD_POWER_DOMAIN_USB 3 +#define RPI_OLD_POWER_DOMAIN_V3D 10 + +struct rpi_power_domain_packet +{ + rt_uint32_t domain; + rt_uint32_t on; +}; + +struct rpi_power_domain +{ + struct rt_dm_power_domain parent; + + rt_uint32_t domain; + rt_bool_t enabled; + rt_bool_t old_interface; + struct rpi_firmware *rpi_fw; +}; + +#define raw_to_rpi_power_domain(raw) rt_container_of(raw, struct rpi_power_domain, parent) + +struct rpi_power_domains +{ + struct rt_dm_power_domain_proxy parent; + + rt_bool_t has_new_interface; + struct rpi_firmware *rpi_fw; + struct rpi_power_domain domains[RPI_POWER_DOMAIN_COUNT]; +}; + +#define raw_to_rpi_power_domains(raw) rt_container_of(raw, struct rpi_power_domains, parent) + +static rt_err_t rpi_firmware_set_power(struct rpi_power_domain *rpi_domain, rt_bool_t on) +{ + struct rpi_power_domain_packet packet; + + packet.domain = rpi_domain->domain; + packet.on = on; + + return rpi_firmware_property(rpi_domain->rpi_fw, + rpi_domain->old_interface ? + RPI_FIRMWARE_SET_POWER_STATE : RPI_FIRMWARE_SET_DOMAIN_STATE, + &packet, sizeof(packet)); +} + +static rt_err_t rpi_domain_off(struct rt_dm_power_domain *domain) +{ + struct rpi_power_domain *rpi_domain = raw_to_rpi_power_domain(domain); + + return rpi_firmware_set_power(rpi_domain, RT_FALSE); +} + +static rt_err_t rpi_domain_on(struct rt_dm_power_domain *domain) +{ + struct rpi_power_domain *rpi_domain = raw_to_rpi_power_domain(domain); + + return rpi_firmware_set_power(rpi_domain, RT_TRUE); +} + +static struct rt_dm_power_domain *rpi_power_domains_ofw_parse( + struct rt_dm_power_domain_proxy *proxy, struct rt_ofw_cell_args *args) +{ + struct rpi_power_domains *rpi_domains = raw_to_rpi_power_domains(proxy); + + return &rpi_domains->domains[args->args[0]].parent; +} + +static void rpi_common_init_power_domain(struct rpi_power_domains *rpi_domains, + int id, const char *name) +{ + struct rpi_power_domain *dom = &rpi_domains->domains[id]; + + dom->rpi_fw = rpi_domains->rpi_fw; + + dom->parent.power_on = rpi_domain_on; + dom->parent.power_off = rpi_domain_off; + + rt_dm_power_domain_register(&dom->parent); +} + +static void rpi_init_power_domain(struct rpi_power_domains *rpi_domains, + int id, const char *name) +{ + struct rpi_power_domain *dom = &rpi_domains->domains[id]; + + if (!rpi_domains->has_new_interface) + { + return; + } + + /* The DT binding index is the firmware's domain index minus one. */ + dom->domain = id + 1; + + rpi_common_init_power_domain(rpi_domains, id, name); +} + +static void rpi_init_old_power_domain(struct rpi_power_domains *rpi_domains, + int id, int domain, const char *name) +{ + struct rpi_power_domain *dom = &rpi_domains->domains[id]; + + dom->old_interface = RT_TRUE; + dom->domain = domain; + + rpi_common_init_power_domain(rpi_domains, id, name); +} + +static rt_bool_t rpi_has_new_domain_support(struct rpi_power_domains *rpi_domains) +{ + struct rpi_power_domain_packet packet; + + packet.domain = RPI_POWER_DOMAIN_ARM; + packet.on = ~0; + + return !rpi_firmware_property(rpi_domains->rpi_fw, + RPI_FIRMWARE_GET_DOMAIN_STATE, &packet, sizeof(packet)) && packet.on != ~0; +} + +static rt_err_t rpi_power_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_power_domains *rpi_domains = rt_calloc(1, sizeof(*rpi_domains)); + + if (!rpi_domains) + { + return -RT_ENOMEM; + } + + fw_np = rt_ofw_parse_phandle(np, "firmware", 0); + + if (!fw_np) + { + err = -RT_EINVAL; + goto _fail; + } + + rpi_domains->rpi_fw = rpi_firmware_get(fw_np); + rt_ofw_node_put(fw_np); + + if (!rpi_domains->rpi_fw) + { + err = -RT_EINVAL; + goto _fail; + } + + rpi_domains->has_new_interface = rpi_has_new_domain_support(rpi_domains); + + rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_I2C0, "I2C0"); + rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_I2C1, "I2C1"); + rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_I2C2, "I2C2"); + rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_VIDEO_SCALER, "VIDEO_SCALER"); + rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_VPU1, "VPU1"); + rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_HDMI, "HDMI"); + + /* + * Use the old firmware interface for USB power, so that we + * can turn it on even if the firmware hasn't been updated. + */ + rpi_init_old_power_domain(rpi_domains, RPI_POWER_DOMAIN_USB, + RPI_OLD_POWER_DOMAIN_USB, "USB"); + + rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_VEC, "VEC"); + rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_JPEG, "JPEG"); + rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_H264, "H264"); + rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_V3D, "V3D"); + rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_ISP, "ISP"); + rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_UNICAM0, "UNICAM0"); + rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_UNICAM1, "UNICAM1"); + rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_CCP2RX, "CCP2RX"); + rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_CSI2, "CSI2"); + rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_CPI, "CPI"); + rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_DSI0, "DSI0"); + rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_DSI1, "DSI1"); + rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_TRANSPOSER, "TRANSPOSER"); + rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_CCP2TX, "CCP2TX"); + rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_CDP, "CDP"); + rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_ARM, "ARM"); + + rpi_domains->parent.ofw_parse = rpi_power_domains_ofw_parse; + rt_dm_power_domain_proxy_ofw_bind(&rpi_domains->parent, pdev->parent.ofw_node); + + return RT_EOK; + +_fail: + rt_free(rpi_domains); + + return err; +} + +static const struct rt_ofw_node_id rpi_power_ofw_ids[] = +{ + { .compatible = "raspberrypi,bcm2835-power" }, + { /* sentinel */ } +}; + +static struct rt_platform_driver rpi_power_driver = +{ + .name = "raspberrypi-power", + .ids = rpi_power_ofw_ids, + + .probe = rpi_power_probe, +}; + +static int rpi_power_drv_register(void) +{ + rt_platform_driver_register(&rpi_power_driver); + + return 0; +} +INIT_SUBSYS_LATER_EXPORT(rpi_power_drv_register); 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..c3e9629903dd --- /dev/null +++ b/components/drivers/power/Kconfig @@ -0,0 +1,2 @@ +source "$RTT_DIR/components/drivers/power/reset/Kconfig" +source "$RTT_DIR/components/drivers/power/supply/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/power/supply/Kconfig b/components/drivers/power/supply/Kconfig new file mode 100644 index 000000000000..8d076dc6c818 --- /dev/null +++ b/components/drivers/power/supply/Kconfig @@ -0,0 +1,3 @@ +menuconfig RT_USING_POWER_SUPPLY + bool "Using Power supply class support" + depends on RT_USING_DM diff --git a/components/drivers/power/supply/SConscript b/components/drivers/power/supply/SConscript new file mode 100644 index 000000000000..30fb4055881d --- /dev/null +++ b/components/drivers/power/supply/SConscript @@ -0,0 +1,15 @@ +from building import * + +group = [] + +if not GetDepend(['RT_USING_POWER_SUPPLY']): + Return('group') + +cwd = GetCurrentDir() +CPPPATH = [cwd + '/../../include'] + +src = [] + +group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') 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..00a8f96f37a7 --- /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..7e6c9525c6a3 --- /dev/null +++ b/components/drivers/reset/reset-brcmstb-rescal.c @@ -0,0 +1,165 @@ +/* + * 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; + + if ((err = rt_reset_controller_register(rstcer))) + { + goto _fail; + } + + 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..1c71afb47d3e --- /dev/null +++ b/components/drivers/reset/reset-brcmstb.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 + +#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; + + if ((err = rt_reset_controller_register(rstcer))) + { + goto _fail; + } + + 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..e5dd6e3bf6be --- /dev/null +++ b/components/drivers/reset/reset-raspberrypi.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 "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; + + if ((err = rt_reset_controller_register(rstcer))) + { + goto _fail; + } + + 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..f9ffcf05d564 --- /dev/null +++ b/components/drivers/reset/reset.c @@ -0,0 +1,424 @@ +/* + * 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; + } + +#if RT_NAME_MAX > 0 + rt_strncpy(rstcer->parent.name, RT_RESET_CONTROLLER_OBJ_NAME, RT_NAME_MAX); +#else + rstcer->parent.name = RT_RESET_CONTROLLER_OBJ_NAME; +#endif + + 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)) + { + struct rt_object *obj; + void *rt_data = rt_ofw_data(reset_args.data); + + if (rt_data && (obj = rt_ofw_parse_object(reset_args.data, + RT_RESET_CONTROLLER_OBJ_NAME, "#reset-cells"))) + { + rstcer = rt_container_of(obj, struct rt_reset_controller, parent); + } + + 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..533aa5a0c8ec 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,79 @@ 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) + if (grtc->irq < 0) { - err = goldfish_rtc_ofw_init(pdev, grtc); - } - else - { - 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 + dev->user_data = grtc; + + 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_device_register(&grtc->parent, "rtc", RT_DEVICE_FLAG_RDWR); + 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); - } - else + 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 +265,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..a3ac56cebf6b 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,103 @@ 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) + 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 +292,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..39ec33340da7 --- /dev/null +++ b/components/drivers/rtc/rtc-rpi.c @@ -0,0 +1,335 @@ +/* + * 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); + + 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..7aa1ce6cc0ca --- /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_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..51ddea131c22 --- /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_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..9cda4f19eb6d --- /dev/null +++ b/components/drivers/serial/8250/8250-bcm2835aux.c @@ -0,0 +1,163 @@ +/* + * 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->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 rt_device *dev = &pdev->parent; + struct serial8250 *serial = dev->user_data; + + rt_dm_dev_unbind_fwdata(dev, RT_NULL); + + 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-bcm7271.c b/components/drivers/serial/8250/8250-bcm7271.c new file mode 100644 index 000000000000..85304700060f --- /dev/null +++ b/components/drivers/serial/8250/8250-bcm7271.c @@ -0,0 +1,1091 @@ +/* + * 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 "serial.8250.bcm7271" +#define DBG_LVL DBG_INFO +#include + +#include "8250.h" + +/* Register definitions for UART DMA block. Version 1.1 or later. */ +#define UDMA_ARB_RX 0x00 +#define UDMA_ARB_TX 0x04 +#define UDMA_ARB_REQ 0x00000001 +#define UDMA_ARB_GRANT 0x00000002 + +#define UDMA_RX_REVISION 0x00 +#define UDMA_RX_REVISION_REQUIRED 0x00000101 +#define UDMA_RX_CTRL 0x04 +#define UDMA_RX_CTRL_BUF_CLOSE_MODE 0x00010000 +#define UDMA_RX_CTRL_MASK_WR_DONE 0x00008000 +#define UDMA_RX_CTRL_ENDIAN_OVERRIDE 0x00004000 +#define UDMA_RX_CTRL_ENDIAN 0x00002000 +#define UDMA_RX_CTRL_OE_IS_ERR 0x00001000 +#define UDMA_RX_CTRL_PE_IS_ERR 0x00000800 +#define UDMA_RX_CTRL_FE_IS_ERR 0x00000400 +#define UDMA_RX_CTRL_NUM_BUF_USED_MASK 0x000003c0 +#define UDMA_RX_CTRL_NUM_BUF_USED_SHIFT 6 +#define UDMA_RX_CTRL_BUF_CLOSE_CLK_SEL_SYS 0x00000020 +#define UDMA_RX_CTRL_BUF_CLOSE_ENA 0x00000010 +#define UDMA_RX_CTRL_TIMEOUT_CLK_SEL_SYS 0x00000008 +#define UDMA_RX_CTRL_TIMEOUT_ENA 0x00000004 +#define UDMA_RX_CTRL_ABORT 0x00000002 +#define UDMA_RX_CTRL_ENA 0x00000001 +#define UDMA_RX_STATUS 0x08 +#define UDMA_RX_STATUS_ACTIVE_BUF_MASK 0x0000000f +#define UDMA_RX_TRANSFER_LEN 0x0c +#define UDMA_RX_TRANSFER_TOTAL 0x10 +#define UDMA_RX_BUFFER_SIZE 0x14 +#define UDMA_RX_SRC_ADDR 0x18 +#define UDMA_RX_TIMEOUT 0x1c +#define UDMA_RX_BUFFER_CLOSE 0x20 +#define UDMA_RX_BLOCKOUT_COUNTER 0x24 +#define UDMA_RX_BUF0_PTR_LO 0x28 +#define UDMA_RX_BUF0_PTR_HI 0x2c +#define UDMA_RX_BUF0_STATUS 0x30 +#define UDMA_RX_BUFX_STATUS_OVERRUN_ERR 0x00000010 +#define UDMA_RX_BUFX_STATUS_FRAME_ERR 0x00000008 +#define UDMA_RX_BUFX_STATUS_PARITY_ERR 0x00000004 +#define UDMA_RX_BUFX_STATUS_CLOSE_EXPIRED 0x00000002 +#define UDMA_RX_BUFX_STATUS_DATA_RDY 0x00000001 +#define UDMA_RX_BUF0_DATA_LEN 0x34 +#define UDMA_RX_BUF1_PTR_LO 0x38 +#define UDMA_RX_BUF1_PTR_HI 0x3c +#define UDMA_RX_BUF1_STATUS 0x40 +#define UDMA_RX_BUF1_DATA_LEN 0x44 + +#define UDMA_TX_REVISION 0x00 +#define UDMA_TX_REVISION_REQUIRED 0x00000101 +#define UDMA_TX_CTRL 0x04 +#define UDMA_TX_CTRL_ENDIAN_OVERRIDE 0x00000080 +#define UDMA_TX_CTRL_ENDIAN 0x00000040 +#define UDMA_TX_CTRL_NUM_BUF_USED_MASK 0x00000030 +#define UDMA_TX_CTRL_NUM_BUF_USED_1 0x00000010 +#define UDMA_TX_CTRL_ABORT 0x00000002 +#define UDMA_TX_CTRL_ENA 0x00000001 +#define UDMA_TX_DST_ADDR 0x08 +#define UDMA_TX_BLOCKOUT_COUNTER 0x10 +#define UDMA_TX_TRANSFER_LEN 0x14 +#define UDMA_TX_TRANSFER_TOTAL 0x18 +#define UDMA_TX_STATUS 0x20 +#define UDMA_TX_BUF0_PTR_LO 0x24 +#define UDMA_TX_BUF0_PTR_HI 0x28 +#define UDMA_TX_BUF0_STATUS 0x2c +#define UDMA_TX_BUFX_LAST 0x00000002 +#define UDMA_TX_BUFX_EMPTY 0x00000001 +#define UDMA_TX_BUF0_DATA_LEN 0x30 +#define UDMA_TX_BUF0_DATA_SENT 0x34 +#define UDMA_TX_BUF1_PTR_LO 0x38 + +#define UDMA_INTR_STATUS 0x00 +#define UDMA_INTR_ARB_TX_GRANT 0x00040000 +#define UDMA_INTR_ARB_RX_GRANT 0x00020000 +#define UDMA_INTR_TX_ALL_EMPTY 0x00010000 +#define UDMA_INTR_TX_EMPTY_BUF1 0x00008000 +#define UDMA_INTR_TX_EMPTY_BUF0 0x00004000 +#define UDMA_INTR_TX_ABORT 0x00002000 +#define UDMA_INTR_TX_DONE 0x00001000 +#define UDMA_INTR_RX_ERROR 0x00000800 +#define UDMA_INTR_RX_TIMEOUT 0x00000400 +#define UDMA_INTR_RX_READY_BUF7 0x00000200 +#define UDMA_INTR_RX_READY_BUF6 0x00000100 +#define UDMA_INTR_RX_READY_BUF5 0x00000080 +#define UDMA_INTR_RX_READY_BUF4 0x00000040 +#define UDMA_INTR_RX_READY_BUF3 0x00000020 +#define UDMA_INTR_RX_READY_BUF2 0x00000010 +#define UDMA_INTR_RX_READY_BUF1 0x00000008 +#define UDMA_INTR_RX_READY_BUF0 0x00000004 +#define UDMA_INTR_RX_READY_MASK 0x000003fc +#define UDMA_INTR_RX_READY_SHIFT 2 +#define UDMA_INTR_RX_ABORT 0x00000002 +#define UDMA_INTR_RX_DONE 0x00000001 +#define UDMA_INTR_SET 0x04 +#define UDMA_INTR_CLEAR 0x08 +#define UDMA_INTR_MASK_STATUS 0x0c +#define UDMA_INTR_MASK_SET 0x10 +#define UDMA_INTR_MASK_CLEAR 0x14 + +#define UDMA_RX_INTERRUPTS ( \ + UDMA_INTR_RX_ERROR | \ + UDMA_INTR_RX_TIMEOUT | \ + UDMA_INTR_RX_READY_BUF0 | \ + UDMA_INTR_RX_READY_BUF1 | \ + UDMA_INTR_RX_READY_BUF2 | \ + UDMA_INTR_RX_READY_BUF3 | \ + UDMA_INTR_RX_READY_BUF4 | \ + UDMA_INTR_RX_READY_BUF5 | \ + UDMA_INTR_RX_READY_BUF6 | \ + UDMA_INTR_RX_READY_BUF7 | \ + UDMA_INTR_RX_ABORT | \ + UDMA_INTR_RX_DONE) + +#define UDMA_RX_ERR_INTERRUPTS ( \ + UDMA_INTR_RX_ERROR | \ + UDMA_INTR_RX_TIMEOUT | \ + UDMA_INTR_RX_ABORT | \ + UDMA_INTR_RX_DONE) + +#define UDMA_TX_INTERRUPTS ( \ + UDMA_INTR_TX_ABORT | \ + UDMA_INTR_TX_DONE) + +#define UDMA_IS_RX_INTERRUPT(status) ((status) & UDMA_RX_INTERRUPTS) +#define UDMA_IS_TX_INTERRUPT(status) ((status) & UDMA_TX_INTERRUPTS) + + +/* Current devices have 8 sets of RX buffer registers */ +#define UDMA_RX_BUFS_COUNT 8 +#define UDMA_RX_BUFS_REG_OFFSET (UDMA_RX_BUF1_PTR_LO - UDMA_RX_BUF0_PTR_LO) +#define UDMA_RX_BUFx_PTR_LO(x) (UDMA_RX_BUF0_PTR_LO + ((x) * UDMA_RX_BUFS_REG_OFFSET)) +#define UDMA_RX_BUFx_PTR_HI(x) (UDMA_RX_BUF0_PTR_HI + ((x) * UDMA_RX_BUFS_REG_OFFSET)) +#define UDMA_RX_BUFx_STATUS(x) (UDMA_RX_BUF0_STATUS + ((x) * UDMA_RX_BUFS_REG_OFFSET)) +#define UDMA_RX_BUFx_DATA_LEN(x) (UDMA_RX_BUF0_DATA_LEN + ((x) * UDMA_RX_BUFS_REG_OFFSET)) + +/* Current devices have 2 sets of TX buffer registers */ +#define UDMA_TX_BUFS_COUNT 2 +#define UDMA_TX_BUFS_REG_OFFSET (UDMA_TX_BUF1_PTR_LO - UDMA_TX_BUF0_PTR_LO) +#define UDMA_TX_BUFx_PTR_LO(x) (UDMA_TX_BUF0_PTR_LO + ((x) * UDMA_TX_BUFS_REG_OFFSET)) +#define UDMA_TX_BUFx_PTR_HI(x) (UDMA_TX_BUF0_PTR_HI + ((x) * UDMA_TX_BUFS_REG_OFFSET)) +#define UDMA_TX_BUFx_STATUS(x) (UDMA_TX_BUF0_STATUS + ((x) * UDMA_TX_BUFS_REG_OFFSET)) +#define UDMA_TX_BUFx_DATA_LEN(x) (UDMA_TX_BUF0_DATA_LEN + ((x) * UDMA_TX_BUFS_REG_OFFSET)) +#define UDMA_TX_BUFx_DATA_SENT(x) (UDMA_TX_BUF0_DATA_SENT + ((x) * UDMA_TX_BUFS_REG_OFFSET)) +#define REGS_8250 0 +#define REGS_DMA_RX 1 +#define REGS_DMA_TX 2 +#define REGS_DMA_ISR 3 +#define REGS_DMA_ARB 4 +#define REGS_MAX 5 + +#define TX_BUF_SIZE 4096 +#define RX_BUF_SIZE 4096 +#define RX_BUFS_COUNT 2 +#define UART_XMIT_SIZE RT_DMA_PAGE_SIZE +#define KHZ 1000 +#define MHZ(x) ((x) * KHZ * KHZ) +#define NSEC_PER_SEC 1000000000L +#define NSEC_PER_MSEC 1000000L + +static const rt_uint32_t brcmstb_rate_table[] = +{ + MHZ(81), + MHZ(108), + MHZ(64), /* Actually 64285715 for some chips */ + MHZ(48), +}; + +static const rt_uint32_t brcmstb_rate_table_7278[] = +{ + MHZ(81), + MHZ(108), + 0, + MHZ(48), +}; + +struct brcmuart +{ + struct serial8250 parent; + + int dma_irq; + void *regs[REGS_MAX]; + struct rt_device *dev; + + rt_ubase_t default_mux_rate; + rt_uint32_t real_rates[RT_ARRAY_SIZE(brcmstb_rate_table)]; + const rt_uint32_t *rate_table; + + rt_uint32_t char_wait_ms; + struct rt_timer char_wait_work; + + rt_bool_t dma_enabled; + + void *rx_bufs; + int rx_index; + int rx_next_buf; + rt_ubase_t rx_addr; + rt_size_t rx_size; + rt_bool_t rx_running; + + void *tx_buf; + rt_ubase_t tx_addr; + rt_size_t tx_size; + rt_bool_t tx_running; +}; + +#define to_brcmuart(serial8250) rt_container_of(serial8250, struct brcmuart, parent) + +static rt_uint32_t udma_readl(struct brcmuart *brcmuart, int reg_type, int offset) +{ + return HWREG32(brcmuart->regs[reg_type] + offset); +} + +static void udma_writel(struct brcmuart *brcmuart, int reg_type, int offset, rt_uint32_t value) +{ + HWREG32(brcmuart->regs[reg_type] + offset) = value; +} + +static void udma_set(struct brcmuart *brcmuart, int reg_type, int offset, rt_uint32_t bits) +{ + rt_uint32_t value; + void *reg = brcmuart->regs[reg_type] + offset; + + value = HWREG32(reg); + value |= bits; + HWREG32(reg) = value; +} + +static void udma_unset(struct brcmuart *brcmuart, int reg_type, int offset, rt_uint32_t bits) +{ + rt_uint32_t value; + void *reg = brcmuart->regs[reg_type] + offset; + + value = HWREG32(reg); + value &= ~bits; + HWREG32(reg) = value; +} + +/* + * The UART DMA engine hardware can be used by multiple UARTS, but + * only one at a time. Sharing is not currently supported so + * the first UART to request the DMA engine will get it and any + * subsequent requests by other UARTS will fail. + */ +static int brcmuart_arbitration(struct brcmuart *brcmuart, rt_bool_t acquire) +{ + int ret = 0; + rt_uint32_t rx_grant, tx_grant; + + if (acquire) + { + int waits = 1; + + udma_set(brcmuart, REGS_DMA_ARB, UDMA_ARB_RX, UDMA_ARB_REQ); + udma_set(brcmuart, REGS_DMA_ARB, UDMA_ARB_TX, UDMA_ARB_REQ); + + while (RT_TRUE) + { + rx_grant = udma_readl(brcmuart, REGS_DMA_ARB, UDMA_ARB_RX); + tx_grant = udma_readl(brcmuart, REGS_DMA_ARB, UDMA_ARB_TX); + + if (rx_grant & tx_grant & UDMA_ARB_GRANT) + { + return 0; + } + + if (waits-- == 0) + { + break; + } + + rt_thread_mdelay(1); + } + + ret = 1; + } + + udma_unset(brcmuart, REGS_DMA_ARB, UDMA_ARB_RX, UDMA_ARB_REQ); + udma_unset(brcmuart, REGS_DMA_ARB, UDMA_ARB_TX, UDMA_ARB_REQ); + + return ret; +} + +static void brcmuart_init_dma_hardware(struct brcmuart *brcmuart) +{ + rt_uint32_t daddr, value; + + /* Start with all interrupts disabled */ + udma_writel(brcmuart, REGS_DMA_ISR, UDMA_INTR_MASK_SET, 0xffffffff); + + udma_writel(brcmuart, REGS_DMA_RX, UDMA_RX_BUFFER_SIZE, RX_BUF_SIZE); + + /* + * Setup buffer close to happen when 32 character times have + * elapsed since the last character was received. + */ + udma_writel(brcmuart, REGS_DMA_RX, UDMA_RX_BUFFER_CLOSE, 16 * 10 *32); + value = (RX_BUFS_COUNT << UDMA_RX_CTRL_NUM_BUF_USED_SHIFT) + | UDMA_RX_CTRL_BUF_CLOSE_MODE + | UDMA_RX_CTRL_BUF_CLOSE_ENA; + udma_writel(brcmuart, REGS_DMA_RX, UDMA_RX_CTRL, value); + + udma_writel(brcmuart, REGS_DMA_RX, UDMA_RX_BLOCKOUT_COUNTER, 0); + daddr = brcmuart->rx_addr; + + for (int i = 0; i < RX_BUFS_COUNT; ++i) + { + /* Set RX transfer length to 0 for unknown */ + udma_writel(brcmuart, REGS_DMA_RX, UDMA_RX_TRANSFER_LEN, 0); + + udma_writel(brcmuart, REGS_DMA_RX, UDMA_RX_BUFx_PTR_LO(i), + rt_lower_32_bits(daddr)); + udma_writel(brcmuart, REGS_DMA_RX, UDMA_RX_BUFx_PTR_HI(i), + rt_upper_32_bits(daddr)); + daddr += RX_BUF_SIZE; + } + + daddr = brcmuart->tx_addr; + udma_writel(brcmuart, REGS_DMA_TX, UDMA_TX_BUFx_PTR_LO(0), + rt_lower_32_bits(daddr)); + udma_writel(brcmuart, REGS_DMA_TX, UDMA_TX_BUFx_PTR_HI(0), + rt_upper_32_bits(daddr)); + udma_writel(brcmuart, REGS_DMA_TX, UDMA_TX_CTRL, + UDMA_TX_CTRL_NUM_BUF_USED_1); + + /* Clear all interrupts then enable them */ + udma_writel(brcmuart, REGS_DMA_ISR, UDMA_INTR_CLEAR, 0xffffffff); + udma_writel(brcmuart, REGS_DMA_ISR, UDMA_INTR_MASK_CLEAR, + UDMA_RX_INTERRUPTS | UDMA_TX_INTERRUPTS); +} + +static void start_rx_dma(struct brcmuart *brcmuart) +{ + udma_unset(brcmuart, REGS_DMA_RX, UDMA_RX_CTRL, UDMA_RX_CTRL_ENA); + + /* Clear the RX ready bit for all buffers */ + for (int i = 0; i < RX_BUFS_COUNT; ++i) + { + udma_unset(brcmuart, REGS_DMA_RX, UDMA_RX_BUFx_STATUS(i), + UDMA_RX_BUFX_STATUS_DATA_RDY); + } + + /* Always start with buffer 0 */ + udma_unset(brcmuart, REGS_DMA_RX, UDMA_RX_STATUS, + UDMA_RX_STATUS_ACTIVE_BUF_MASK); + brcmuart->rx_next_buf = 0; + + udma_set(brcmuart, REGS_DMA_RX, UDMA_RX_CTRL, UDMA_RX_CTRL_ENA); + brcmuart->rx_running = RT_TRUE; +} + +static void stop_rx_dma(struct brcmuart *brcmuart) +{ + /* If RX is running, set the RX ABORT */ + if (brcmuart->rx_running) + { + udma_set(brcmuart, REGS_DMA_RX, UDMA_RX_CTRL, UDMA_RX_CTRL_ABORT); + } +} + +static int stop_tx_dma(struct brcmuart *brcmuart) +{ + rt_uint32_t value; + + /* If TX is running, set the TX ABORT */ + value = udma_readl(brcmuart, REGS_DMA_TX, UDMA_TX_CTRL); + + if (value & UDMA_TX_CTRL_ENA) + { + udma_set(brcmuart, REGS_DMA_TX, UDMA_TX_CTRL, UDMA_TX_CTRL_ABORT); + } + + brcmuart->tx_running = RT_FALSE; + return 0; +} + +static void brcmuart_rx_buf_done(struct brcmuart *brcmuart, int index) +{ + rt_uint32_t status, length; + + /* Make sure we're still in sync with the hardware */ + status = udma_readl(brcmuart, REGS_DMA_RX, UDMA_RX_BUFx_STATUS(index)); + length = udma_readl(brcmuart, REGS_DMA_RX, UDMA_RX_BUFx_DATA_LEN(index)); + + if ((status & UDMA_RX_BUFX_STATUS_DATA_RDY) == 0) + { + LOG_E("RX done interrupt but DATA_RDY not found"); + return; + } + + if (status & (UDMA_RX_BUFX_STATUS_OVERRUN_ERR | + UDMA_RX_BUFX_STATUS_FRAME_ERR | + UDMA_RX_BUFX_STATUS_PARITY_ERR)) + { + if (status & UDMA_RX_BUFX_STATUS_OVERRUN_ERR) + { + LOG_W("RX Overrun"); + } + if (status & UDMA_RX_BUFX_STATUS_FRAME_ERR) + { + LOG_W("RX Framing"); + } + if (status & UDMA_RX_BUFX_STATUS_PARITY_ERR) + { + LOG_W("RX Parity"); + } + } + + brcmuart->rx_index = index * RX_BUF_SIZE; + serial8250_dma_rx_done(&brcmuart->parent, length); +} + +static void brcmuart_rx_work(struct brcmuart *brcmuart, rt_uint32_t rx_isr) +{ + rt_uint32_t rx_done_isr, check_isr; + + rx_done_isr = (rx_isr & UDMA_INTR_RX_READY_MASK); + + while (rx_done_isr) + { + check_isr = UDMA_INTR_RX_READY_BUF0 << brcmuart->rx_next_buf; + + if (check_isr & rx_done_isr) + { + brcmuart_rx_buf_done(brcmuart, brcmuart->rx_next_buf); + } + else + { + LOG_E("RX buffer ready out of sequence, restarting RX DMA"); + start_rx_dma(brcmuart); + + break; + } + if (rx_isr & UDMA_RX_ERR_INTERRUPTS) + { + if (rx_isr & UDMA_INTR_RX_ERROR) + { + LOG_D("RX Error"); + } + if (rx_isr & UDMA_INTR_RX_TIMEOUT) + { + LOG_E("RX Timeout"); + } + if (rx_isr & UDMA_INTR_RX_ABORT) + { + LOG_D("RX Abort"); + } + + brcmuart->rx_running = RT_FALSE; + } + /* If not ABORT, re-enable RX buffer */ + if (!(rx_isr & UDMA_INTR_RX_ABORT)) + { + udma_unset(brcmuart, REGS_DMA_RX, + UDMA_RX_BUFx_STATUS(brcmuart->rx_next_buf), UDMA_RX_BUFX_STATUS_DATA_RDY); + } + + rx_done_isr &= ~check_isr; + brcmuart->rx_next_buf++; + + if (brcmuart->rx_next_buf == RX_BUFS_COUNT) + { + brcmuart->rx_next_buf = 0; + } + } +} + +static void set_clock_mux(struct brcmuart *brcmuart, rt_uint32_t baud) +{ + rt_tick_t tick; + int best_index = -1, real_baud, i; + rt_uint64_t hires_rate, hires_baud, hires_err; + rt_uint32_t percent, best_percent = RT_UINT32_MAX, quot, best_quot = 1, rate; + + /* Find the closest match for specified baud */ + for (i = 0; i < RT_ARRAY_SIZE(brcmuart->real_rates); ++i) + { + if (brcmuart->real_rates[i] == 0) + { + continue; + } + + rate = brcmuart->real_rates[i] / 16; + quot = RT_DIV_ROUND_CLOSEST(rate, baud); + if (!quot) + { + continue; + } + + /* increase resolution to get xx.xx percent */ + hires_rate = (rt_uint64_t)rate * 10000; + hires_baud = (rt_uint64_t)baud * 10000; + + hires_err = rt_div_u64(hires_rate, (rt_uint64_t)quot); + + /* get the delta */ + if (hires_err > hires_baud) + { + hires_err = hires_err - hires_baud; + } + else + { + hires_err = hires_baud - hires_err; + } + + percent = (rt_ubase_t)RT_DIV_ROUND_CLOSEST_ULL(hires_err, baud); + + LOG_D("Baud rate: %u, MUX Clk: %u, Error: %u.%u%%", + baud, brcmuart->real_rates[i], percent / 100, percent % 100); + + if (percent < best_percent) + { + best_percent = percent; + best_index = i; + best_quot = quot; + } + } + + if (best_index == -1) + { + LOG_E("%d BAUD rate is too fast", baud); + return; + } + + rate = brcmuart->real_rates[best_index]; + + if (rt_clk_set_rate(brcmuart->parent.clk, rate)) + { + LOG_E("Selecting BAUD MUX clock"); + } + + /* Error over 3 percent will cause data errors */ + if (best_percent > 300) + { + LOG_E("Baud: %d has %u.%u%% error", baud, percent / 100, percent % 100); + } + + real_baud = rate / 16 / best_quot; + LOG_D("Selecting BAUD MUX rate: %u", rate); + LOG_D("Requested baud: %u, Actual baud: %u", baud, real_baud); + + /* calc nanoseconds for 1.5 characters time at the given baud rate */ + i = NSEC_PER_SEC / real_baud / 10; + i += (i / 2); + brcmuart->char_wait_ms = NSEC_PER_MSEC / i; + + tick = rt_tick_from_millisecond(brcmuart->char_wait_ms); + rt_timer_control(&brcmuart->char_wait_work, RT_TIMER_CTRL_SET_TIME, &tick); + + brcmuart->parent.freq = rate; +} + +static rt_err_t brcmstb_serial_ios(struct serial8250 *serial, struct serial_configure *ios) +{ + struct brcmuart *brcmuart = to_brcmuart(serial); + + if (brcmuart->dma_enabled) + { + stop_rx_dma(brcmuart); + } + + if (!rt_is_err_or_null(serial->clk)) + { + set_clock_mux(brcmuart, ios->baud_rate); + } + + serial8250_ios(serial, ios); + + if (brcmuart->dma_enabled) + { + start_rx_dma(brcmuart); + } + + return RT_EOK; +} + +static rt_err_t brcmuart_serial_dma_enable(struct serial8250 *serial, rt_bool_t enabled) +{ + struct brcmuart *brcmuart = to_brcmuart(serial); + + if (!brcmuart->dma_enabled) + { + return RT_EOK; + } + + if (enabled) + { + rt_uint32_t ier; + + ier = serial->serial_in(serial, UART_IER); + serial->serial_out(serial, UART_IER, ier & ~UART_IER_RDI); + + brcmuart_init_dma_hardware(brcmuart); + start_rx_dma(brcmuart); + } + else + { + rt_ubase_t level = rt_spin_lock_irqsave(&serial->spinlock); + + stop_rx_dma(brcmuart); + stop_tx_dma(brcmuart); + + /* Disable all interrupts */ + udma_writel(brcmuart, REGS_DMA_ISR, UDMA_INTR_MASK_SET, + UDMA_RX_INTERRUPTS | UDMA_TX_INTERRUPTS); + + rt_spin_unlock_irqrestore(&serial->spinlock, level); + } + + return RT_EOK; +} + +static rt_ssize_t brcmuart_serial_dma_tx(struct serial8250 *serial, + const rt_uint8_t *buf, rt_size_t size) +{ + struct brcmuart *brcmuart = to_brcmuart(serial); + + if (brcmuart->tx_running) + { + return 0; + } + + size = rt_min_t(rt_size_t, size, brcmuart->tx_size); + rt_memcpy(brcmuart->tx_buf, buf, size); + + udma_writel(brcmuart, REGS_DMA_TX, UDMA_TX_TRANSFER_LEN, size); + udma_writel(brcmuart, REGS_DMA_TX, UDMA_TX_BUF0_DATA_LEN, size); + udma_unset(brcmuart, REGS_DMA_TX, UDMA_TX_BUF0_STATUS, UDMA_TX_BUFX_EMPTY); + udma_set(brcmuart, REGS_DMA_TX, UDMA_TX_CTRL, UDMA_TX_CTRL_ENA); + brcmuart->tx_running = RT_TRUE; + + return size; +} + +static rt_ssize_t brcmuart_serial_dma_rx(struct serial8250 *serial, + rt_uint8_t *buf, rt_size_t size) +{ + struct brcmuart *brcmuart = to_brcmuart(serial); + + size = rt_min_t(rt_size_t, size, brcmuart->rx_size - brcmuart->rx_index); + rt_memcpy(buf, brcmuart->rx_bufs + brcmuart->rx_index, size); + + return size; +} + +static rt_err_t brcmuart_serial_isr(struct serial8250 *serial, int irq) +{ + rt_ubase_t level; + rt_uint32_t iir, ier, mcr, status; + struct brcmuart *brcmuart = to_brcmuart(serial); + + iir = serial->serial_in(serial, UART_LSR); + + /* + * There's a bug in some 8250 cores where we get a timeout + * interrupt but there is no data ready. + */ + if (((iir & UART_IIR_ID) == UART_IIR_RX_TIMEOUT)) + { + level = rt_spin_lock_irqsave(&serial->spinlock); + + status = serial->serial_in(serial, UART_LSR); + + if ((status & UART_LSR_DR) == 0) + { + ier = serial->serial_in(serial, UART_IER); + mcr = serial->serial_in(serial, UART_MCR); + + /* + * if Receive Data Interrupt is enabled and + * we're uing hardware flow control, deassert + * RTS and wait for any chars in the pipline to + * arrive and then check for DR again. + */ + if ((ier & UART_IER_RDI) && (mcr & UART_MCR_AFE)) + { + ier &= ~(UART_IER_RLSI | UART_IER_RDI); + serial->serial_out(serial, UART_IER, ier); + + mcr = serial->serial_in(serial, UART_MCR); + mcr &= ~UART_MCR_RTS; + serial->serial_out(serial, UART_MCR, mcr); + + rt_timer_start(&brcmuart->char_wait_work); + } + else + { + serial->serial_in(serial, UART_RX); + } + } + + rt_spin_unlock_irqrestore(&serial->spinlock, level); + } + + return RT_EOK; +} + +static void brcmuart_dma_isr(int irqno, void *param) +{ + rt_ubase_t level; + rt_uint32_t interrupts, rval, tval; + struct brcmuart *brcmuart = param; + struct serial8250 *serial = &brcmuart->parent; + + interrupts = udma_readl(brcmuart, REGS_DMA_ISR, UDMA_INTR_STATUS); + + if (!interrupts) + { + return; + } + + level = rt_spin_lock_irqsave(&serial->spinlock); + + /* Clear all interrupts */ + udma_writel(brcmuart, REGS_DMA_ISR, UDMA_INTR_CLEAR, interrupts); + + if ((rval = UDMA_IS_RX_INTERRUPT(interrupts))) + { + brcmuart_rx_work(brcmuart, rval); + } + + if ((tval = UDMA_IS_TX_INTERRUPT(interrupts))) + { + if (tval & UDMA_INTR_TX_ABORT) + { + if (brcmuart->tx_running) + { + LOG_E("Unexpected TX_ABORT interrupt"); + } + } + else + { + brcmuart->tx_running = RT_FALSE; + serial8250_dma_tx_done(serial); + } + } + + if ((rval | tval) == 0) + { + LOG_W("Spurious interrupt: 0x%x", interrupts); + } + + rt_spin_unlock_irqrestore(&serial->spinlock, level); +} + +static void brcmuart_char_wait_work(void *param) +{ + rt_ubase_t level; + rt_uint32_t ier, status; + struct brcmuart *brcmuart = param; + struct serial8250 *serial = &brcmuart->parent; + + level = rt_spin_lock_irqsave(&serial->spinlock); + + status = serial->serial_in(serial, UART_LSR); + + /* + * If a character did not arrive after the timeout, clear the false + * receive timeout. + */ + if ((status & UART_LSR_DR) == 0) + { + serial->serial_in(serial, UART_RX); + } + ier = serial->serial_in(serial, UART_IER); + + /* re-enable receive unless upper layer has disabled it */ + if ((ier & (UART_IER_RLSI | UART_IER_RDI)) == (UART_IER_RLSI | UART_IER_RDI)) + { + status = serial->serial_in(serial, UART_IER); + status |= (UART_IER_RLSI | UART_IER_RDI); + serial->serial_out(serial, UART_IER, status); + status = serial->serial_in(serial, UART_MCR); + status |= UART_MCR_RTS; + serial->serial_out(serial, UART_MCR, status); + } + + rt_spin_unlock_irqrestore(&serial->spinlock, level); +} + +static void brcmuart_free_resource(struct brcmuart *brcmuart) +{ + struct serial8250 *serial = &brcmuart->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 (brcmuart->rx_bufs) + { + rt_dma_free_coherent(brcmuart->dev, brcmuart->rx_size, + brcmuart->rx_bufs, brcmuart->rx_addr); + } + if (brcmuart->tx_buf) + { + rt_dma_free_coherent(brcmuart->dev, brcmuart->tx_size, + brcmuart->tx_buf, brcmuart->tx_addr); + } + + if (brcmuart->dma_enabled) + { + brcmuart_arbitration(brcmuart, 0); + } + + rt_free(brcmuart); +} + +static void brcmuart_serial_remove(struct serial8250 *serial) +{ + struct brcmuart *brcmuart = to_brcmuart(serial); + + brcmuart_free_resource(brcmuart); + + rt_timer_detach(&brcmuart->char_wait_work); +} + +static rt_err_t brcmuart_probe(struct rt_platform_device *pdev) +{ + int ridx; + rt_err_t err; + const char *dev_name; + static const char * const reg_names[REGS_MAX] = + { + "uart", "dma_rx", "dma_tx", "dma_intr2", "dma_arb" + }; + struct serial8250 *serial; + struct rt_device *dev = &pdev->parent; + struct brcmuart *brcmuart = rt_calloc(1, sizeof((brcmuart))); + struct serial_configure config = RT_SERIAL_CONFIG_DEFAULT; + + if (!brcmuart) + { + return -RT_EINVAL; + } + + brcmuart->dev = dev; + + if (pdev->id) + { + brcmuart->rate_table = pdev->id->data; + } + else + { + brcmuart->rate_table = brcmstb_rate_table; + } + + serial = &brcmuart->parent; + + serial->irq = rt_dm_dev_get_irq(dev, 0); + + if (serial->irq < 0) + { + err = serial->irq; + + goto _free_res; + } + + for (ridx = 0; ridx < REGS_MAX; ++ridx) + { + brcmuart->regs[ridx] = rt_dm_dev_iomap_by_name(dev, reg_names[ridx]); + + if (!brcmuart->regs[ridx] && + rt_dm_dev_prop_index_of_string(dev, "reg-names", reg_names[ridx]) >= 0) + { + err = -RT_EIO; + goto _free_res; + } + + if (ridx == REGS_8250) + { + serial->base = brcmuart->regs[ridx]; + } + } + + if (ridx != 1 && ridx != REGS_MAX) + { + LOG_E("Registers %s not specified", reg_names[ridx]); + err = -RT_EINVAL; + goto _free_res; + } + + if (ridx > REGS_DMA_RX) + { + if (brcmuart_arbitration(brcmuart, 1) == 0) + { + rt_uint32_t txrev = 0, rxrev = 0; + + txrev = udma_readl(brcmuart, REGS_DMA_RX, UDMA_RX_REVISION); + rxrev = udma_readl(brcmuart, REGS_DMA_TX, UDMA_TX_REVISION); + + if (txrev >= UDMA_TX_REVISION_REQUIRED && + rxrev >= UDMA_RX_REVISION_REQUIRED) + { + /* Enable the use of the DMA hardware */ + brcmuart->dma_enabled = RT_TRUE; + } + else + { + brcmuart_arbitration(brcmuart, 0); + + LOG_E("Unsupported DMA Hardware Revision"); + } + } + else + { + LOG_E("Timeout arbitrating for UART DMA hardware"); + } + } + + if (brcmuart->dma_enabled) + { + brcmuart->dma_irq = rt_dm_dev_get_irq_by_name(dev, "dma"); + + if (brcmuart->dma_irq < 0) + { + err = brcmuart->dma_irq; + LOG_E("Get DMA IRQ error = %s", rt_strerror(err)); + + goto _free_res; + } + + brcmuart->rx_size = RX_BUF_SIZE * RX_BUFS_COUNT; + brcmuart->rx_bufs = rt_dma_alloc_coherent(dev, brcmuart->rx_size, &brcmuart->rx_addr); + + if (!brcmuart->rx_bufs) + { + err = -RT_ENOMEM; + goto _free_res; + } + + brcmuart->tx_size = UART_XMIT_SIZE; + brcmuart->tx_buf = rt_dma_alloc_coherent(dev, brcmuart->tx_size, &brcmuart->tx_addr); + + if (!brcmuart->tx_buf) + { + err = -RT_ENOMEM; + goto _free_res; + } + } + + rt_dm_dev_prop_read_u32(dev, "clock-frequency", &serial->freq); + + serial->clk = rt_clk_get_by_name(dev, "sw_baud"); + + if (rt_is_err(serial->clk)) + { + err = rt_ptr_err(serial->clk); + + goto _free_res; + } + else if (serial->clk) + { + if ((err = rt_clk_prepare_enable(serial->clk))) + { + goto _free_res; + } + + brcmuart->default_mux_rate = rt_clk_get_rate(serial->clk); + + for (int i = 0; i < RT_ARRAY_SIZE(brcmuart->real_rates); ++i) + { + if (!brcmuart->rate_table[i]) + { + brcmuart->real_rates[i] = 0; + + continue; + } + + if (rt_clk_set_rate(serial->clk, brcmuart->rate_table[i])) + { + LOG_E("Error selecting BAUD MUX clock for %u", brcmuart->rate_table[i]); + brcmuart->real_rates[i] = brcmuart->rate_table[i]; + } + else + { + brcmuart->real_rates[i] = rt_clk_get_rate(serial->clk); + } + } + + rt_clk_set_rate(serial->clk, brcmuart->default_mux_rate); + + serial->freq = brcmuart->default_mux_rate; + } + + if (!serial->freq) + { + LOG_E("clock-frequency or clk not defined"); + + err = -RT_EINVAL; + goto _free_res; + } + + serial->iotype = rt_dm_dev_prop_read_bool(dev, "big-endian") ? + PORT_MMIO32BE : PORT_MMIO32; + + serial->parent.ops = &serial8250_uart_ops; + serial->parent.config = config; + + serial->serial_ios = brcmstb_serial_ios; + if (brcmuart->dma_enabled) + { + serial->serial_dma_enable = brcmuart_serial_dma_enable; + serial->serial_dma_tx = brcmuart_serial_dma_tx; + serial->serial_dma_rx = brcmuart_serial_dma_rx; + } + serial->handle_irq = &brcmuart_serial_isr; + serial->remove = &brcmuart_serial_remove; + serial->data = brcmuart; + + pdev->parent.user_data = serial; + + if ((err = serial8250_setup(serial))) + { + goto _free_res; + } + + dev_name = rt_dm_dev_get_name(&serial->parent.parent); + + rt_timer_init(&brcmuart->char_wait_work, dev_name, brcmuart_char_wait_work, brcmuart, + 0, RT_TIMER_FLAG_PERIODIC); + + if (brcmuart->dma_enabled) + { + rt_hw_interrupt_install(brcmuart->dma_irq, brcmuart_dma_isr, brcmuart, dev_name); + rt_hw_interrupt_umask(brcmuart->dma_irq); + } + + return RT_EOK; + +_free_res: + brcmuart_free_resource(brcmuart); + + return err; +} + +static rt_err_t brcmuart_remove(struct rt_platform_device *pdev) +{ + struct rt_device *dev = &pdev->parent; + struct serial8250 *serial = dev->user_data; + + rt_dm_dev_unbind_fwdata(dev, RT_NULL); + + return serial8250_remove(serial); +} + +static const struct rt_ofw_node_id brcmuart_ofw_ids[] = +{ + { .type = "ttyS", .compatible = "brcm,bcm7278-uart", .data = brcmstb_rate_table_7278 }, + { .type = "ttyS", .compatible = "brcm,bcm7271-uart", .data = brcmstb_rate_table }, + { /* sentinel */ } +}; + +static struct rt_platform_driver brcmuart_driver = +{ + .name = "bcm7271-uart", + .ids = brcmuart_ofw_ids, + + .probe = brcmuart_probe, + .remove = brcmuart_remove, +}; + +static int brcmuart_drv_register(void) +{ + rt_platform_driver_register(&brcmuart_driver); + + return 0; +} +INIT_DRIVER_EARLY_EXPORT(brcmuart_drv_register); diff --git a/components/drivers/serial/8250/8250_dw.c b/components/drivers/serial/8250/8250-dw.c similarity index 59% rename from components/drivers/serial/8250/8250_dw.c rename to components/drivers/serial/8250/8250-dw.c index 995769cdf95d..0592788ae2b1 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,147 @@ 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)) + { + err = rt_ptr_err(dw8250->pclk); + + goto _free_res; + } + + 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->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))) { - ret = -RT_ENOMEM; + goto _free_res; } - return ret; + return RT_EOK; + +_free_res: + dw8250_free_resource(dw8250); + + return err; +} + +static rt_err_t dw8250_remove(struct rt_platform_device *pdev) +{ + struct rt_device *dev = &pdev->parent; + struct serial8250 *serial = dev->user_data; + + rt_dm_dev_unbind_fwdata(dev, RT_NULL); + + return serial8250_remove(serial); } static const struct dw8250_platform_data dw8250_dw_apb = @@ -244,7 +325,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 +334,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..163d9d70e5cb --- /dev/null +++ b/components/drivers/serial/8250/8250-ofw.c @@ -0,0 +1,215 @@ +/* + * 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->parent.ops = &serial8250_uart_ops; + serial->parent.config = config; + serial->remove = &op8250_remove; + serial->data = op8250; + + if ((err = serial8250_setup(serial))) + { + goto _fail; + } + + 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 rt_device *dev = &pdev->parent; + struct serial8250 *serial = dev->user_data; + + rt_dm_dev_unbind_fwdata(dev, RT_NULL); + + return serial8250_remove(serial); +} + +static const struct rt_ofw_node_id ofw_platform_8250_ofw_ids[] = +{ + { .type = "ttyS", .compatible = "ns16550a" }, + { .type = "ttyS", .compatible = "ns16550" }, +#ifndef RT_SERIAL_8250_BCM7271 + { .type = "ttyS", .compatible = "brcm,bcm7271-uart" }, +#endif + { /* 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..7884a2a00241 --- /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 = "serial-pci", + + .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 63% rename from components/drivers/serial/8250/serial8250.h rename to components/drivers/serial/8250/8250.h index dfb4358369bd..5702a5acfc89 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,10 +41,14 @@ 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_err_t (*serial_ios)(struct serial8250 *, struct serial_configure *ios); rt_uint32_t (*serial_in)(struct serial8250 *, int offset); void (*serial_out)(struct serial8250 *, int offset, int value); + rt_err_t (*serial_dma_enable)(struct serial8250 *, rt_bool_t enabled); + rt_ssize_t (*serial_dma_tx)(struct serial8250 *, const rt_uint8_t *buf, rt_size_t size); + rt_ssize_t (*serial_dma_rx)(struct serial8250 *, rt_uint8_t *buf, rt_size_t size); rt_err_t (*handle_irq)(struct serial8250 *, int irq); /* Free all resource (and parent) by child */ @@ -56,19 +61,25 @@ 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); +void serial8250_dma_tx_done(struct serial8250 *serial); +void serial8250_dma_rx_done(struct serial8250 *serial, int recv_len); +rt_err_t serial8250_ios(struct serial8250 *serial, struct serial_configure *cfg); rt_err_t serial8250_uart_configure(struct rt_serial_device *raw_serial, struct serial_configure *cfg); rt_err_t serial8250_uart_control(struct rt_serial_device *raw_serial, int cmd, void *arg); int serial8250_uart_putc(struct rt_serial_device *raw_serial, char c); int serial8250_uart_getc(struct rt_serial_device *raw_serial); +rt_ssize_t serial8250_uart_dma_transmit(struct rt_serial_device *raw_serial, + rt_uint8_t *buf, rt_size_t size, int direction); 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..920443851bf0 --- /dev/null +++ b/components/drivers/serial/8250/Kconfig @@ -0,0 +1,33 @@ +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_BCM7271 + bool "Broadcom 8250 based serial port" + depends on RT_SERIAL_8250 + select RT_USING_DMA + select RT_SERIAL_USING_DMA + 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..ddf572ab597d 100755 --- a/components/drivers/serial/8250/SConscript +++ b/components/drivers/serial/8250/SConscript @@ -1,12 +1,29 @@ 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_BCM7271']): + src += ['8250-bcm7271.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..a132d7d8e993 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)) { @@ -105,21 +101,45 @@ static void serial8250_isr(int irqno, void *param) } } +static rt_err_t serial8250_dma_enable_dummy(struct serial8250 *serial, rt_bool_t enabled) +{ + return RT_EOK; +} + +static rt_ssize_t serial8250_dma_tx_dummy(struct serial8250 *serial, + const rt_uint8_t *buf, rt_size_t size) +{ + return 0; +} + +static rt_ssize_t serial8250_dma_rx_dummy(struct serial8250 *serial, + rt_uint8_t *buf, rt_size_t size) +{ + return 0; +} + 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 ? serial->serial_in : &serial8250_in; - serial->serial_out = serial->serial_out ? serial->serial_out : &serial8250_out; + serial->serial_in = serial->serial_in ? : &serial8250_in; + serial->serial_out = serial->serial_out ? : &serial8250_out; + serial->serial_dma_enable = serial->serial_dma_enable ? : &serial8250_dma_enable_dummy; + serial->serial_dma_tx = serial->serial_dma_tx ? : &serial8250_dma_tx_dummy; + serial->serial_dma_rx = serial->serial_dma_rx ? : &serial8250_dma_rx_dummy; - rt_sprintf(dev_name, "uart%d", serial_next_id()); + serial_dev_set_name(&serial->parent); + uart_name = rt_dm_dev_get_name(&serial->parent.parent); - rt_hw_serial_register(&serial->parent, dev_name, RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX, serial->data); + rt_hw_serial_register(&serial->parent, uart_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 +150,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; @@ -189,80 +229,111 @@ 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) +void serial8250_dma_tx_done(struct serial8250 *serial) { - rt_err_t ret = RT_EOK; - struct serial8250 *serial = raw_to_serial8250(raw_serial); + rt_hw_serial_isr(&serial->parent, RT_SERIAL_EVENT_TX_DMADONE); +} - serial->serial_out(serial, UART_IER, !UART_IER_RDI); +void serial8250_dma_rx_done(struct serial8250 *serial, int recv_len) +{ + rt_hw_serial_isr(&serial->parent, RT_SERIAL_EVENT_RX_DMADONE | (recv_len << 8)); +} - /* Enable FIFO, Clear FIFO*/ - serial->serial_out(serial, UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); +rt_err_t serial8250_ios(struct serial8250 *serial, struct serial_configure *cfg) +{ + rt_err_t err = RT_EOK; /* Disable interrupt */ - serial->serial_out(serial, UART_IER, 0); + serial->serial_out(serial, UART_IER, !UART_IER_RDI); + + /* Enable FIFO, Clear FIFO */ + serial->serial_out(serial, UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); /* 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_configure(struct rt_serial_device *raw_serial, struct serial_configure *cfg) +{ + rt_err_t err; + struct serial8250 *serial = raw_to_serial8250(raw_serial); + + if (serial->serial_ios) + { + err = serial->serial_ios(serial, cfg); + } + else + { + err = serial8250_ios(serial, cfg); + } + + 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; + rt_ubase_t ctrl = (rt_ubase_t)arg; struct serial8250 *serial = raw_to_serial8250(raw_serial); switch (cmd) { - case RT_DEVICE_CTRL_CLR_INT: - /* disable rx irq */ + case RT_DEVICE_CTRL_CLR_INT: + if (ctrl == RT_DEVICE_FLAG_INT_RX) + { + /* Disable rx irq */ serial->serial_out(serial, UART_IER, !UART_IER_RDI); rt_hw_interrupt_mask(serial->irq); - break; + } + else if (ctrl == RT_DEVICE_FLAG_DMA_RX) + { + err = serial->serial_dma_enable(serial, RT_FALSE); + } + break; - case RT_DEVICE_CTRL_SET_INT: - /* enable rx irq */ + case RT_DEVICE_CTRL_SET_INT: + if (ctrl == RT_DEVICE_FLAG_INT_RX) + { + /* 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); + } + else if (ctrl == RT_DEVICE_FLAG_DMA_RX) + { + err = serial->serial_dma_enable(serial, RT_TRUE); + } - rt_device_unregister(&serial->parent.parent); + case RT_DEVICE_CTRL_CLOSE: + err = serial->serial_dma_enable(serial, RT_FALSE); + break; - if (serial->remove) - { - serial->remove(serial); - } - break; + default: + err = -RT_EINVAL; + break; } - return ret; + return err; } int serial8250_uart_putc(struct rt_serial_device *raw_serial, char c) @@ -271,6 +342,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); @@ -291,10 +363,29 @@ int serial8250_uart_getc(struct rt_serial_device *raw_serial) return ch; } +rt_ssize_t serial8250_uart_dma_transmit(struct rt_serial_device *raw_serial, + rt_uint8_t *buf, rt_size_t size, int direction) +{ + rt_ssize_t res = 0; + struct serial8250 *serial = raw_to_serial8250(raw_serial); + + if (direction == RT_SERIAL_DMA_TX) + { + res = serial->serial_dma_tx(serial, (const rt_uint8_t *)buf, size); + } + else if (direction == RT_SERIAL_DMA_RX) + { + res = serial->serial_dma_rx(serial, buf, size); + } + + return res; +} + 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, + .dma_transmit = serial8250_uart_dma_transmit, }; 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..46886490bb57 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,131 @@ 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; + } - serial_dev_set_name(&pl011->parent); - name = rt_dm_get_dev_name(&pl011->parent.parent); + pl011->pclk = rt_clk_get_by_name(dev, "apb_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); + if (rt_is_err(pl011->pclk)) + { + err = rt_ptr_err(pl011->pclk); + + 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 rt_device *dev = &pdev->parent; + struct pl011 *pl011 = dev->user_data; + + rt_dm_dev_unbind_fwdata(dev, RT_NULL); + + 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 +406,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 65% rename from components/drivers/serial/serial_internal.c rename to components/drivers/serial/serial_dm.c index 1c7486ebc4c8..34427665a477 --- a/components/drivers/serial/serial_internal.c +++ b/components/drivers/serial/serial_dm.c @@ -9,19 +9,48 @@ */ #include - -#include "serial_internal.h" +#include "serial_dm.h" int serial_dev_set_name(struct rt_serial_device *sdev) { - int id; + int id = -1; + static int uid_min = -1; static volatile rt_atomic_t uid = 0; - RT_ASSERT(sdev != RT_NULL) + RT_ASSERT(sdev != RT_NULL); + +#ifdef RT_USING_OFW + if (sdev->parent.ofw_node) + { + id = rt_ofw_get_alias_id(sdev->parent.ofw_node, "serial"); - id = (int)rt_hw_atomic_add(&uid, 1); + if (id < 0) + { + id = rt_ofw_get_alias_id(sdev->parent.ofw_node, "uart"); + } + + if (uid_min < 0) + { + uid_min = rt_ofw_get_alias_last_id("serial"); - return rt_dm_set_dev_name(&sdev->parent, "uart%u", id); + if (uid_min < 0) + { + uid_min = rt_ofw_get_alias_last_id("uart"); + } + + uid_min = uid_min < 0 ? 0 : (uid_min + 1); + + rt_hw_atomic_store(&uid, uid_min); + } + } +#endif + + if (id < 0) + { + id = (int)rt_hw_atomic_add(&uid, 1); + } + + return rt_dm_dev_set_name(&sdev->parent, "uart%u", id); } void *serial_base_from_args(char *str) @@ -63,10 +92,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 +134,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..b9b80231e211 --- /dev/null +++ b/components/drivers/soc/broadcom/raspberrypi-voltage-monitor.c @@ -0,0 +1,204 @@ +/* + * 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_timer_detach(&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/video/Kconfig b/components/drivers/video/Kconfig new file mode 100644 index 000000000000..f232ec12f34f --- /dev/null +++ b/components/drivers/video/Kconfig @@ -0,0 +1,12 @@ +config RT_USING_LCD + bool + default n + +menuconfig RT_USING_VIDEO + bool "Using Video Graphics device drivers" + default n + +if RT_USING_VIDEO +source "$RTT_DIR/components/drivers/video/framebuffer/Kconfig" +source "$RTT_DIR/components/drivers/video/logo/Kconfig" +endif diff --git a/components/drivers/video/SConscript b/components/drivers/video/SConscript new file mode 100644 index 000000000000..2eaebbee1199 --- /dev/null +++ b/components/drivers/video/SConscript @@ -0,0 +1,23 @@ +from building import * + +group = [] +objs = [] + +if not GetDepend(['RT_USING_VIDEO']): + 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/video/framebuffer/Kconfig b/components/drivers/video/framebuffer/Kconfig new file mode 100644 index 000000000000..0a7ad74d55eb --- /dev/null +++ b/components/drivers/video/framebuffer/Kconfig @@ -0,0 +1,12 @@ +menuconfig RT_VIDEO_FB + bool "LCD and Frame buffer support" + depends on RT_USING_VIDEO + select RT_USING_LCD + default y + +config RT_VIDEO_FB_SIMPLE + bool "Simple framebuffer support" + depends on RT_VIDEO_FB + select RT_USING_OFW + default y + diff --git a/components/drivers/video/framebuffer/SConscript b/components/drivers/video/framebuffer/SConscript new file mode 100644 index 000000000000..ec191c78182b --- /dev/null +++ b/components/drivers/video/framebuffer/SConscript @@ -0,0 +1,17 @@ +from building import * + +group = [] + +if not GetDepend(['RT_VIDEO_FB']): + Return('group') + +cwd = GetCurrentDir() +CPPPATH = [cwd + '/../../include'] + +src = [] + +if GetDepend(['RT_VIDEO_FB_SIMPLE']): + src += ['fb-simple.c'] + +group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) +Return('group') diff --git a/components/drivers/video/framebuffer/fb-simple.c b/components/drivers/video/framebuffer/fb-simple.c new file mode 100644 index 000000000000..b2a864e06ec5 --- /dev/null +++ b/components/drivers/video/framebuffer/fb-simple.c @@ -0,0 +1,456 @@ +/* + * 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 "fb.simple" +#define DBG_LVL DBG_INFO +#include + +struct simplefb_format +{ + const char *name; + rt_uint32_t bits_per_pixel; + struct fb_bitfield red; + struct fb_bitfield green; + struct fb_bitfield blue; + struct fb_bitfield transp; +}; + +struct simplefb_params +{ + rt_uint32_t width; + rt_uint32_t height; + rt_uint32_t stride; + struct simplefb_format *format; +}; + +struct simplefb +{ + struct rt_device parent; + + struct fb_var_screeninfo var; + struct fb_fix_screeninfo fix; + + void *screen_base; + + rt_ubase_t screen_addr; + rt_size_t screen_size; + +#ifdef RT_USING_CLK + rt_bool_t clk_arr_enabled; + struct rt_clk_array *clk_arr; +#endif +#ifdef RT_USING_REGULATOR + rt_bool_t supplys_enabled; + rt_size_t supplys_nr; + struct rt_regulator **supplys; +#endif +}; + +static rt_err_t simplefb_control(rt_device_t dev, int cmd, void *args) +{ + rt_err_t err; + struct simplefb *sfb = rt_container_of(dev, struct simplefb, parent); + + switch (cmd) + { + case FBIOGET_VSCREENINFO: + if (!args) + { + err = -RT_EINVAL; + break; + } + rt_memcpy(args, &sfb->var, sizeof(sfb->var)); + + break; + + case FBIOGET_FSCREENINFO: + if (!args) + { + err = -RT_EINVAL; + break; + } + rt_memcpy(args, &sfb->fix, sizeof(sfb->fix)); + + break; + + case FBIOPUT_VSCREENINFO: + case FBIOGETCMAP: + case FBIOPUTCMAP: + case FBIO_CURSOR: + case FBIOGET_CON2FBMAP: + case FBIOPUT_CON2FBMAP: + case FBIOBLANK: + case FBIOGET_VBLANK: + case FBIO_ALLOC: + case FBIO_FREE: + case FBIOGET_GLYPH: + case FBIOGET_HWCINFO: + case FBIOPUT_MODEINFO: + case FBIOGET_DISPINFO: + err = -RT_ENOSYS; + break; + + case FBIOPAN_DISPLAY: + case FBIO_WAITFORVSYNC: + break; + + default: + err = -RT_EINVAL; + break; + } + + return err; +} + +#ifdef RT_USING_DEVICE_OPS +const static struct rt_device_ops simplefb_ops = +{ + .control = simplefb_control, +}; +#endif + +#ifdef RT_USING_CLK +static rt_err_t simplefb_clk_probe(struct simplefb *sfb, + struct rt_platform_device *pdev) +{ + sfb->clk_arr = rt_clk_get_array(&pdev->parent); + + if (rt_is_err(sfb->clk_arr)) + { + return rt_ptr_err(sfb->clk_arr); + } + + return RT_EOK; +} + +static void simplefb_clk_enable(struct simplefb *sfb) +{ + rt_clk_array_prepare_enable(sfb->clk_arr); + sfb->clk_arr_enabled = RT_TRUE; +} + +static void simplefb_clk_remove(struct simplefb *sfb) +{ + if (!rt_is_err_or_null(sfb->clk_arr)) + { + if (sfb->clk_arr_enabled) + { + rt_clk_array_disable_unprepare(sfb->clk_arr); + } + + rt_clk_array_put(sfb->clk_arr); + } +} +#else +static rt_err_t simplefb_clk_probe(struct simplefb *sfb, + struct rt_platform_device *pdev) { return RT_EOK; } +static void simplefb_clk_enable(struct simplefb *sfb) { } +static void simplefb_clk_remove(struct simplefb *sfb) { } +#endif /* RT_USING_CLK */ + +#ifdef RT_USING_REGULATOR +#define SUPPLY_SUFFIX "-supply" + +static rt_err_t simplefb_regulator_probe(struct simplefb *sfb, + struct rt_platform_device *pdev) +{ + int i = 0; + const char *name; + struct rt_device *dev = &pdev->parent; + struct rt_ofw_prop *prop; + struct rt_ofw_node *np = dev->ofw_node; + + rt_ofw_foreach_prop(np, prop) + { + name = rt_strstr(prop->name, SUPPLY_SUFFIX); + + if (name && name != prop->name) + { + ++sfb->supplys_nr; + } + } + + sfb->supplys = rt_calloc(sfb->supplys_nr, sizeof(sfb->supplys[0])); + + if (!sfb->supplys) + { + return -RT_ENOMEM; + } + + rt_ofw_foreach_prop(np, prop) + { + name = rt_strstr(prop->name, SUPPLY_SUFFIX); + + if (name && name != prop->name) + { + char name[32]; + int len = name - prop->name; + + rt_strncpy(name, prop->name, len); + name[len] = '\0'; + + sfb->supplys[i] = rt_regulator_get_optional(dev, (const char *)name); + + if (rt_is_err(sfb->supplys[i])) + { + return rt_ptr_err(sfb->supplys[i]); + } + + ++i; + } + } + + return RT_EOK; +} + +static void simplefb_regulator_enable(struct simplefb *sfb) +{ + if (sfb->supplys) + { + for (int i = 0; i < sfb->supplys_nr; ++i) + { + rt_regulator_enable(sfb->supplys[i]); + } + + sfb->supplys_enabled = RT_TRUE; + } +} + +static void simplefb_regulator_remove(struct simplefb *sfb) +{ + if (sfb->supplys && sfb->supplys_enabled) + { + for (int i = 0; i < sfb->supplys_nr; ++i) + { + struct rt_regulator *supply = sfb->supplys[i]; + + if (!rt_is_err(supply)) + { + rt_regulator_disable(supply); + rt_regulator_put(supply); + } + } + + rt_free(sfb->supplys); + } +} +#else +static rt_err_t simplefb_regulator_probe(struct simplefb *sfb, + struct rt_platform_device *pdev) { return RT_EOK; } +static void simplefb_regulator_enable(struct simplefb *sfb) { } +static void simplefb_regulator_remove(struct simplefb *sfb) { } +#endif /* RT_USING_REGULATOR */ + +static struct simplefb_format simplefb_formats[] = +{ + { "r5g6b5", 16, {11, 5 }, { 5, 6 }, { 0, 5}, { 0, 0} }, + { "r5g5b5a1", 16, {11, 5 }, { 6, 5 }, { 1, 5}, { 0, 1} }, + { "x1r5g5b5", 16, {10, 5 }, { 5, 5 }, { 0, 5}, { 0, 0} }, + { "a1r5g5b5", 16, {10, 5 }, { 5, 5 }, { 0, 5}, {15, 1} }, + { "r8g8b8", 24, {16, 8 }, { 8, 8 }, { 0, 8}, { 0, 0} }, + { "x8r8g8b8", 32, {16, 8 }, { 8, 8 }, { 0, 8}, { 0, 0} }, + { "a8r8g8b8", 32, {16, 8 }, { 8, 8 }, { 0, 8}, {24, 8} }, + { "x8b8g8r8", 32, {0, 8 }, { 8, 8 }, {16, 8}, { 0, 0} }, + { "a8b8g8r8", 32, {0, 8 }, { 8, 8 }, {16, 8}, {24, 8} }, + { "x2r10g10b10", 32, {20, 10}, { 10, 10}, { 0, 10}, { 0, 0} }, + { "a2r10g10b10", 32, {20, 10}, { 10, 10}, { 0, 10}, {30, 2} }, +}; + +static rt_err_t simplefb_params_parse(struct simplefb_params *params, + struct rt_platform_device *pdev) +{ + rt_err_t err; + const char *format; + struct rt_device *dev = &pdev->parent; + + if ((err = rt_dm_dev_prop_read_u32(dev, "width", ¶ms->width))) + { + LOG_E("Can't parse width property"); + + return err; + } + + if ((err = rt_dm_dev_prop_read_u32(dev, "height", ¶ms->height))) + { + LOG_E("Can't parse height property"); + + return err; + } + + if ((err = rt_dm_dev_prop_read_u32(dev, "stride", ¶ms->stride))) + { + LOG_E("Can't parse stride property"); + + return err; + } + + if ((err = rt_dm_dev_prop_read_string(dev, "format", &format))) + { + LOG_E("Can't parse format property"); + + return err; + } + + for (int i = 0; i < RT_ARRAY_SIZE(simplefb_formats); ++i) + { + if (rt_strcmp(format, simplefb_formats[i].name)) + { + continue; + } + + params->format = &simplefb_formats[i]; + + return RT_EOK; + } + + LOG_E("Invalid format value"); + + return -RT_EINVAL; +} + +static rt_err_t simplefb_probe(struct rt_platform_device *pdev) +{ + rt_err_t err; + const char *dev_name; + rt_uint64_t addr, size; + struct fb_var_screeninfo *var; + struct fb_fix_screeninfo *fix; + struct simplefb_params params = {}; + struct simplefb *sfb = rt_calloc(1, sizeof(*sfb)); + + if (!sfb) + { + return -RT_ENOMEM; + } + + if ((err = simplefb_params_parse(¶ms, pdev))) + { + goto _fail; + } + + if ((err = rt_dm_dev_get_address(&pdev->parent, 0, &addr, &size))) + { + goto _fail; + } + + sfb->screen_addr = (rt_ubase_t)addr; + sfb->screen_size = (rt_size_t)size; + + sfb->screen_base = rt_ioremap_cached((void *)sfb->screen_addr, sfb->screen_size); + + if (!sfb->screen_base) + { + err = -RT_EIO; + goto _fail; + } + + if ((err = simplefb_clk_probe(sfb, pdev))) + { + LOG_E("Get %s error = %s", "clk", rt_strerror(err)); + + goto _fail; + } + + if ((err = simplefb_regulator_probe(sfb, pdev))) + { + LOG_E("Get %s error = %s", "regulator", rt_strerror(err)); + + goto _fail; + } + + simplefb_clk_enable(sfb); + simplefb_regulator_enable(sfb); + + fix = &sfb->fix; + fix->smem_start = sfb->screen_addr; + fix->smem_len = sfb->screen_size; + fix->line_length = params.stride; + + var = &sfb->var; + var->xres = params.width; + var->yres = params.height; + var->xres_virtual = params.width; + var->yres_virtual = params.height; + var->bits_per_pixel = params.format->bits_per_pixel; + var->red = params.format->red; + var->green = params.format->green; + var->blue = params.format->blue; + var->transp = params.format->transp; + + sfb->parent.type = RT_Device_Class_Graphic; +#ifdef RT_USING_DEVICE_OPS + sfb->parent.ops = &simplefb_ops; +#else + sfb->parent.control = simplefb_control; +#endif + + rt_dm_dev_set_name_auto(&sfb->parent, "fb"); + dev_name = rt_dm_dev_get_name(&sfb->parent); + + err = rt_device_register(&sfb->parent, dev_name, RT_DEVICE_FLAG_RDWR); + + return RT_EOK; + +_fail: + if (sfb->screen_base) + { + rt_iounmap(sfb->screen_base); + } + + simplefb_clk_remove(sfb); + simplefb_regulator_remove(sfb); + + rt_free(sfb); + + return err; +} + +static rt_err_t simplefb_remove(struct rt_platform_device *pdev) +{ + struct simplefb *sfb = pdev->parent.user_data; + + rt_device_unregister(&sfb->parent); + + simplefb_clk_remove(sfb); + simplefb_regulator_remove(sfb); + + rt_iounmap(sfb->screen_base); + + rt_free(sfb); + + return RT_EOK; +} + +static const struct rt_ofw_node_id simplefb_ofw_ids[] = +{ + { .compatible = "simple-framebuffer" }, + { /* sentinel */ } +}; + +static struct rt_platform_driver simplefb_driver = +{ + .name = "simple-framebuffer", + .ids = simplefb_ofw_ids, + + .probe = simplefb_probe, + .remove = simplefb_remove, +}; + +static int simplefb_drv_register(void) +{ + rt_platform_driver_register(&simplefb_driver); + + return 0; +} +INIT_SUBSYS_EXPORT(simplefb_drv_register); diff --git a/components/drivers/video/logo/.gitignore b/components/drivers/video/logo/.gitignore new file mode 100644 index 000000000000..e524629a37f9 --- /dev/null +++ b/components/drivers/video/logo/.gitignore @@ -0,0 +1 @@ +logo.inc \ No newline at end of file diff --git a/components/drivers/video/logo/Kconfig b/components/drivers/video/logo/Kconfig new file mode 100644 index 000000000000..b9c4140fe114 --- /dev/null +++ b/components/drivers/video/logo/Kconfig @@ -0,0 +1,33 @@ +menuconfig RT_VIDEO_LOGO + bool "Startup Logo" + depends on RT_USING_VIDEO + default y + +choice + prompt "Logo rendering stage" + default RT_VIDEO_LOGO_RENDERING_STAGE_DRIVER_EARLY + depends on RT_VIDEO_LOGO + + config RT_VIDEO_LOGO_RENDERING_STAGE_DRIVER_EARLY + bool "Device driver early" + + config RT_VIDEO_LOGO_RENDERING_STAGE_DRIVER + bool "Device driver" + + config RT_VIDEO_LOGO_RENDERING_STAGE_DRIVER_LATER + bool "Device driver later" + + config RT_VIDEO_LOGO_RENDERING_STAGE_COMPONENT + bool "Components" + + config RT_VIDEO_LOGO_RENDERING_STAGE_ENV + bool "Environment" + + config RT_VIDEO_LOGO_RENDERING_STAGE_APP + bool "Application" +endchoice + +config RT_VIDEO_LOGO_RT_THREAD_CLUT224 + bool "Standard 224-color RT-Thread logo" + depends on RT_VIDEO_LOGO + default y diff --git a/components/drivers/video/logo/SConscript b/components/drivers/video/logo/SConscript new file mode 100644 index 000000000000..1deaf81b2aa5 --- /dev/null +++ b/components/drivers/video/logo/SConscript @@ -0,0 +1,82 @@ +from building import * +import re + +group = [] + +if not GetDepend(['RT_VIDEO_LOGO']): + Return('group') + +cwd = GetCurrentDir() +CPPPATH = [cwd + '/../../include'] +CPPDEFINES = [] + +src = [] + +logo_path = None +logo_width = 0 +logo_height = 0 +logo_gray = 0 + +if GetDepend(['RT_VIDEO_LOGO_RT_THREAD_CLUT224']): + logo_path = cwd + '/logo-rt-thread-clut224.ppm' + +if logo_path != None: + with open(logo_path, 'rb') as ppm: + data = ppm.read().split(b'\n') + + # PPM: + magic = data[0].decode('utf-8') + + # PPM: + offset = 1 + while True: + comment = str(data[offset].decode('utf-8')) + if comment[0] != '#': + break + offset += 1 + + # PPM: + logo_width, logo_height = map(int, data[offset].split()) + + # PPM: + max_val = int(data[offset + 1]) + + # PPM: + ppm.seek(0) + pixels = b''.join(ppm.readlines()[offset + 2:]) + ppm.close() + + if magic == 'P1' or magic == 'P2' or magic == 'P3': + # ASCII + pixels = re.sub(b'\\s+', b'\n', pixels.strip()).decode('utf-8').split('\n') + + logo = open(cwd + '/logo.inc', "w") + + if max_val != 255: + logo_gray = 1 + for dy in range(logo_height): + for dx in range(logo_width): + pixel = pixels[dy * logo_width + dx] + logo.write(str(pixel) + ",") + logo.write("\n") + else: + for dy in range(logo_height): + for dx in range(logo_width): + index = (dy * logo_width + dx) * 3 + # Red + logo.write(str(pixels[index]).rjust(4) + ",") + # Green + logo.write(str(pixels[index + 1]).rjust(4) + ",") + # Blue + logo.write(str(pixels[index + 2]).rjust(4) + ",") + logo.write("\n") + + logo.close() + + CPPDEFINES += ['__RT_THREAD_STARTUP_LOGO_WIDTH__="' + str(logo_width) + '"'] + CPPDEFINES += ['__RT_THREAD_STARTUP_LOGO_HEIGHT__="' + str(logo_height) + '"'] + CPPDEFINES += ['__RT_THREAD_STARTUP_LOGO_GRAY__="' + str(logo_gray) + '"'] + src += ['logo.c'] + +group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH, CPPDEFINES = CPPDEFINES) +Return('group') diff --git a/components/drivers/video/logo/logo-rt-thread-clut224.ppm b/components/drivers/video/logo/logo-rt-thread-clut224.ppm new file mode 100644 index 000000000000..839c38abbb50 --- /dev/null +++ b/components/drivers/video/logo/logo-rt-thread-clut224.ppm @@ -0,0 +1,63 @@ +P3 +# Standard 224-color RT-Thread logo +209 59 +255 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 4 4 2 8 8 2 11 11 3 13 13 3 14 15 3 14 15 3 14 15 3 14 14 2 11 11 2 9 9 1 5 5 0 2 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 2 9 9 5 22 22 8 38 39 12 56 57 16 76 77 20 93 94 24 109 110 27 122 123 29 132 133 30 140 142 32 146 148 33 150 152 33 152 154 33 152 154 33 152 154 33 151 152 32 147 149 31 141 143 29 134 136 27 124 125 24 112 113 21 97 98 17 79 80 13 61 62 9 43 43 5 25 26 3 12 12 0 2 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 8 8 6 27 28 12 55 55 19 86 87 25 116 117 31 141 143 35 160 162 38 173 175 39 180 182 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 182 184 38 175 177 36 164 166 32 146 148 27 123 124 20 94 95 13 62 63 7 33 34 3 12 12 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 5 5 6 27 28 14 63 63 22 103 104 30 139 141 36 165 167 39 180 182 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 182 184 37 170 171 32 146 148 24 113 114 16 72 73 8 35 35 2 9 9 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 9 9 8 38 39 18 84 85 28 131 132 36 165 166 40 182 184 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 37 170 172 30 140 142 21 96 97 11 48 49 3 14 14 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 35 35 29 135 136 37 172 174 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 38 177 179 32 147 149 21 98 99 10 45 45 2 9 9 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 19 19 32 148 150 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 38 175 177 30 138 139 17 79 80 5 25 26 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 14 63 64 39 180 182 40 183 185 35 160 162 25 116 117 21 96 97 38 173 175 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 35 161 163 23 106 107 9 42 42 1 4 4 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 3 3 5 26 26 11 63 62 11 63 62 2 12 12 19 87 88 15 68 69 5 21 21 1 3 3 0 2 2 24 113 114 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 37 172 174 26 121 123 11 51 52 1 6 6 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 15 14 18 101 100 22 124 123 21 119 118 7 37 37 0 0 0 2 17 17 7 62 62 13 108 108 4 36 36 7 30 31 35 163 165 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 38 175 177 27 126 127 11 51 52 1 5 5 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 11 62 61 22 124 123 22 124 123 17 93 93 2 9 8 9 80 80 20 169 169 20 169 169 14 115 115 1 5 5 19 89 90 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 38 175 177 26 121 123 9 43 43 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 2 2 13 13 18 100 99 22 124 123 21 119 118 7 39 39 3 24 24 17 145 145 20 169 169 19 164 164 7 59 59 4 16 16 32 146 148 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 37 170 172 23 105 106 6 27 27 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 17 17 5 46 46 8 68 68 2 17 17 7 40 39 13 73 73 6 35 34 2 11 11 1 2 2 9 74 74 20 169 169 20 169 169 14 116 116 0 4 4 14 63 63 39 178 179 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 182 184 39 178 179 38 173 175 37 168 170 36 164 166 35 160 162 34 157 159 34 156 158 34 156 158 34 157 159 35 160 162 36 165 166 37 169 171 38 173 175 39 178 180 40 182 184 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 34 157 159 17 76 77 2 10 10 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 56 56 11 100 99 11 100 99 7 63 62 0 2 2 4 10 10 19 49 48 36 98 96 27 75 73 2 15 15 11 95 95 8 69 69 2 23 23 2 11 11 12 53 54 35 162 164 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 39 181 183 37 170 172 34 154 156 29 135 136 25 114 115 20 94 95 16 76 77 13 60 61 10 47 47 8 37 37 6 30 30 5 25 25 5 21 21 4 19 19 4 17 18 4 17 18 4 19 19 5 21 21 5 25 25 7 30 31 8 38 39 11 48 49 13 61 62 17 77 78 21 96 97 25 117 118 30 137 139 34 157 158 37 172 174 40 182 184 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 182 184 28 130 131 9 41 42 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 19 19 10 89 89 11 100 99 11 96 95 3 29 29 11 29 28 50 137 135 55 151 148 51 140 137 13 34 34 0 0 0 6 28 28 18 84 85 30 140 142 38 175 177 40 183 185 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 37 172 174 33 151 152 26 121 122 19 87 88 13 58 58 7 33 34 4 17 17 1 6 6 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 2 7 7 4 18 18 8 36 36 13 61 61 20 92 93 27 124 125 33 154 155 38 175 177 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 36 167 168 19 85 86 2 11 11 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 50 49 11 100 99 11 99 98 7 60 59 1 4 4 31 86 85 55 151 148 55 151 148 38 103 101 3 8 8 22 103 104 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 39 178 179 33 153 155 25 115 117 16 74 75 8 38 39 3 14 15 0 2 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 3 3 4 17 17 9 42 42 17 79 80 26 121 122 34 157 158 39 179 181 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 183 185 28 128 130 7 34 34 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 11 10 6 53 52 4 34 33 1 10 10 0 0 0 8 23 22 47 130 128 55 151 148 48 131 129 11 30 29 7 35 35 36 165 167 40 184 186 40 184 186 40 184 186 40 184 186 38 175 177 31 144 145 21 96 97 11 49 50 4 17 17 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 2 4 19 20 12 54 55 22 102 103 32 148 150 39 178 179 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 35 159 160 14 66 66 0 2 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 19 53 52 25 69 67 10 27 26 1 4 3 0 0 0 20 92 93 40 184 186 39 180 182 32 149 151 21 98 99 10 45 46 3 12 12 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 14 14 11 51 51 23 105 106 34 154 156 40 182 184 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 38 176 178 21 98 99 3 12 12 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 20 20 22 100 101 13 61 61 3 16 16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 19 20 14 66 67 28 128 129 37 172 174 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 27 126 127 6 26 26 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 5 5 9 41 42 23 105 106 35 162 164 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 32 146 148 9 41 42 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 29 29 20 92 93 34 157 159 40 184 186 40 184 186 40 184 186 40 184 186 35 160 162 12 56 56 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 25 25 20 91 92 35 159 161 40 184 186 40 184 186 40 184 186 37 168 170 15 68 69 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 27 28 22 100 101 36 167 169 40 184 186 40 184 186 38 173 175 16 76 77 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 38 39 26 120 121 39 178 179 40 184 186 38 175 177 17 80 81 0 2 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 5 5 13 61 62 32 147 149 40 184 186 39 178 179 17 79 80 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 19 20 21 99 100 38 173 175 39 178 179 16 74 75 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 11 52 53 32 146 147 38 173 175 14 66 66 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 19 20 24 108 109 35 160 162 12 53 54 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 4 4 16 71 72 29 135 136 8 39 39 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 5 5 14 66 67 18 84 85 18 82 83 18 82 83 18 82 83 6 29 29 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 9 42 42 21 98 99 5 23 23 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 15 15 17 76 77 18 83 84 18 82 83 18 83 84 17 76 77 3 15 15 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 12 12 32 149 151 40 184 186 40 184 186 40 184 186 40 184 186 14 65 66 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 22 22 11 51 52 1 6 7 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 34 34 37 172 174 40 184 186 40 184 186 40 184 186 37 172 174 7 33 34 +0 2 2 2 11 11 3 12 12 3 12 12 3 12 12 3 12 12 3 12 12 3 12 12 3 12 12 3 12 12 3 12 12 3 12 12 3 12 12 3 12 12 3 12 12 3 12 12 3 12 12 3 12 12 3 12 12 2 10 10 1 4 4 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 7 7 3 12 12 3 12 12 3 12 12 3 12 12 3 12 12 3 12 12 3 12 12 3 12 12 3 12 12 3 12 12 3 12 12 3 12 12 3 12 12 3 12 12 3 12 12 3 12 12 3 12 12 3 12 12 3 12 12 3 12 12 3 12 12 3 12 12 3 12 12 3 12 12 3 12 12 1 4 4 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 4 4 3 12 12 3 12 12 3 12 12 3 12 12 3 12 12 3 12 12 3 12 12 3 12 12 3 12 12 3 12 12 3 12 12 3 12 12 3 12 12 3 12 12 3 12 12 3 12 12 3 12 12 3 12 12 3 12 12 3 12 12 3 12 12 3 12 12 3 12 12 3 12 12 3 12 12 1 6 7 0 0 0 3 12 12 32 148 150 40 184 186 40 184 186 40 184 186 40 183 185 14 64 65 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 6 7 1 6 7 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 33 34 37 171 173 40 184 186 40 184 186 40 184 186 37 170 172 7 33 34 +6 29 29 30 139 140 32 148 150 32 147 149 32 147 149 32 147 149 32 147 149 32 147 149 32 147 149 32 147 149 32 147 149 32 147 149 32 147 149 32 147 149 32 147 149 32 147 149 32 147 149 32 147 149 32 147 149 31 144 146 28 127 128 19 86 87 6 30 30 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 20 91 92 33 150 152 32 147 149 32 147 149 32 147 149 32 147 149 32 147 149 32 147 149 32 147 149 32 147 149 32 147 149 32 147 149 32 147 149 32 147 149 32 147 149 32 147 149 32 147 149 32 147 149 32 147 149 32 147 149 32 147 149 32 147 149 32 147 149 32 147 149 32 147 149 32 145 147 10 47 47 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 12 56 57 32 148 150 32 147 149 32 147 149 32 147 149 32 147 149 32 147 149 32 147 149 32 147 149 32 147 149 32 147 149 32 147 149 32 147 149 32 147 149 32 147 149 32 147 149 32 147 149 32 147 149 32 147 149 32 147 149 32 147 149 32 147 149 32 147 149 32 147 149 32 147 149 32 149 151 18 81 82 0 0 0 3 12 12 32 148 150 40 184 186 40 184 186 40 184 186 40 183 185 14 64 65 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 33 34 37 171 173 40 184 186 40 184 186 40 184 186 37 170 172 7 33 34 +8 37 37 38 175 177 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 35 162 164 18 84 85 2 8 8 0 0 0 0 0 0 0 0 0 0 0 0 25 115 117 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 183 185 13 59 60 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 16 71 72 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 22 102 103 0 0 0 3 12 12 32 148 150 40 184 186 40 184 186 40 184 186 40 183 185 14 64 65 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 33 34 37 171 173 40 184 186 40 184 186 40 184 186 37 170 172 7 33 34 +8 36 36 38 173 175 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 182 184 23 105 106 2 7 7 0 0 0 0 0 0 0 0 0 25 114 115 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 39 181 183 13 58 59 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 15 71 71 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 22 101 102 0 0 0 3 12 12 32 148 150 40 184 186 40 184 186 40 184 186 40 183 185 14 64 65 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 33 34 37 171 173 40 184 186 40 184 186 40 184 186 37 170 172 7 33 34 +8 36 36 38 173 175 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 182 184 18 82 82 0 0 0 0 0 0 0 0 0 25 114 115 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 39 181 183 13 58 59 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 15 71 71 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 22 101 102 0 0 0 3 12 12 32 148 150 40 184 186 40 184 186 40 184 186 40 183 185 14 64 65 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 33 34 37 170 172 40 184 186 40 184 186 40 184 186 37 170 172 7 33 34 +8 36 36 38 173 175 40 184 186 40 184 186 40 184 186 40 184 186 40 183 185 40 183 185 40 183 185 40 183 185 40 183 185 40 183 185 40 183 185 40 183 185 40 183 185 40 183 185 40 183 185 40 183 185 40 183 185 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 35 160 162 6 27 27 0 0 0 0 0 0 25 113 115 40 184 186 40 183 185 40 183 185 40 183 185 40 183 185 40 183 185 40 183 185 40 183 185 40 183 185 40 183 185 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 183 185 40 183 185 40 183 185 40 183 185 40 183 185 40 183 185 40 183 185 40 183 185 40 183 185 39 180 182 13 58 58 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 15 70 71 40 183 185 40 183 185 40 183 185 40 183 185 40 183 185 40 183 185 40 183 185 40 183 185 40 183 185 40 183 185 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 183 185 40 183 185 40 183 185 40 183 185 40 183 185 40 183 185 40 183 185 40 183 185 40 183 185 40 184 186 22 100 101 0 0 0 3 12 12 32 148 150 40 184 186 40 184 186 40 184 186 40 183 185 14 66 66 0 0 0 0 2 2 0 2 2 0 2 2 0 2 2 0 2 2 0 2 2 0 2 2 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 2 2 0 2 2 0 2 2 0 2 2 0 2 2 0 2 2 0 2 2 0 2 2 0 2 2 0 2 2 0 2 2 0 2 2 0 2 2 0 2 2 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 2 0 2 2 0 2 2 0 2 2 0 2 2 0 2 2 0 2 2 0 2 2 0 2 2 0 2 2 0 2 2 0 2 2 0 2 2 0 2 2 0 1 1 0 0 0 0 0 0 0 2 2 0 2 2 0 2 2 0 2 2 0 2 2 0 2 2 0 2 2 0 2 2 0 2 2 0 2 2 0 2 2 0 2 2 0 2 2 0 2 2 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 2 2 0 2 2 0 2 2 0 2 2 0 2 2 0 2 2 0 2 2 0 0 0 8 35 36 37 171 173 40 184 186 40 184 186 40 184 186 37 170 172 7 33 34 +8 36 36 38 173 175 40 184 186 40 184 186 40 184 186 38 175 177 19 86 87 14 63 63 14 64 65 14 64 65 14 64 65 14 64 65 14 64 65 14 64 65 14 64 65 14 64 65 14 64 65 14 64 65 14 64 65 17 78 79 30 139 141 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 17 79 80 0 0 0 0 0 0 9 40 40 14 65 66 14 64 65 14 64 65 14 64 65 14 64 65 14 64 65 14 64 65 14 64 65 13 62 63 22 100 101 40 182 184 40 184 186 40 184 186 40 184 186 36 165 167 16 75 76 14 63 64 14 64 65 14 64 65 14 64 65 14 64 65 14 64 65 14 64 65 14 64 65 14 63 63 4 20 20 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 25 25 14 64 65 14 64 65 14 64 65 14 64 65 14 64 65 14 64 65 14 64 65 14 64 65 14 63 63 17 80 81 37 171 173 40 184 186 40 184 186 40 184 186 39 180 182 21 95 96 13 62 63 14 64 65 14 64 65 14 64 65 14 64 65 14 64 65 14 64 65 14 64 65 14 65 66 8 35 36 0 0 0 3 12 12 32 148 150 40 184 186 40 184 186 40 184 186 40 184 186 32 145 147 27 123 125 27 124 125 27 124 125 27 124 125 27 124 125 27 124 125 27 124 125 27 124 125 27 122 123 23 106 107 14 66 66 4 17 17 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 6 7 11 50 50 21 96 97 26 119 120 27 124 125 27 124 125 27 124 125 27 124 125 27 124 125 27 124 125 27 124 125 27 124 125 27 124 125 27 124 125 27 124 125 27 124 125 27 125 126 26 120 121 7 30 31 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 7 34 34 18 84 85 25 115 116 27 123 125 27 124 125 27 124 125 27 124 125 27 124 125 27 124 125 27 124 125 27 124 125 27 124 125 27 124 125 27 124 125 27 124 125 27 124 125 27 126 128 13 61 61 0 0 0 3 12 12 23 105 106 27 126 127 27 124 125 27 124 125 27 124 125 27 124 125 27 124 125 27 124 125 27 124 125 27 124 125 27 124 125 27 124 125 27 124 125 27 124 125 26 121 123 23 104 105 13 62 63 3 14 14 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 9 9 11 53 53 21 99 100 26 120 121 27 124 125 27 124 125 27 124 125 27 124 125 27 124 125 27 124 125 27 124 125 27 123 125 29 135 136 39 180 182 40 184 186 40 184 186 40 184 186 37 170 172 7 33 34 +8 36 36 38 173 175 40 184 186 40 184 186 40 184 186 37 170 172 7 31 31 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 20 20 31 142 144 40 184 186 40 184 186 40 184 186 40 184 186 26 120 121 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 11 52 53 39 180 182 40 184 186 40 184 186 40 184 186 34 155 157 3 14 14 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 22 22 35 163 165 40 184 186 40 184 186 40 184 186 39 178 179 10 45 45 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 12 12 32 148 150 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 182 184 32 145 147 12 53 54 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 27 28 26 121 123 38 176 178 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 39 180 182 10 46 47 0 0 0 0 0 0 0 0 0 2 10 10 20 92 93 36 167 168 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 20 92 93 0 0 0 4 17 17 35 159 160 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 39 181 183 31 141 142 10 47 47 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 33 34 28 127 128 39 178 180 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 37 170 172 7 33 34 +8 36 36 38 173 175 40 184 186 40 184 186 40 184 186 37 171 173 7 33 34 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 19 86 87 40 184 186 40 184 186 40 184 186 40 184 186 30 137 139 1 6 7 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 12 55 55 39 180 182 40 184 186 40 184 186 40 184 186 34 156 158 4 17 17 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 25 25 36 164 166 40 184 186 40 184 186 40 184 186 39 178 179 10 47 47 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 12 12 32 148 150 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 37 168 170 13 58 58 0 0 0 0 0 0 0 0 0 0 0 0 6 26 26 31 144 145 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 38 177 179 10 45 46 0 0 0 0 0 0 1 6 7 23 108 109 40 183 185 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 20 90 91 0 0 0 4 17 17 34 156 158 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 36 164 166 11 50 50 0 0 0 0 0 0 0 0 0 0 0 0 7 32 32 33 150 152 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 37 170 172 7 33 34 +8 36 36 38 173 175 40 184 186 40 184 186 40 184 186 37 171 173 7 33 34 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 20 92 93 40 184 186 40 184 186 40 184 186 40 184 186 29 136 137 1 6 6 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 12 55 55 39 180 182 40 184 186 40 184 186 40 184 186 34 156 158 4 17 17 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 25 25 36 164 166 40 184 186 40 184 186 40 184 186 39 178 179 10 47 47 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 12 12 32 148 150 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 33 153 155 5 24 24 0 0 0 0 0 0 1 4 4 25 115 117 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 38 177 179 10 45 46 0 0 0 0 0 0 15 70 71 39 180 182 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 20 90 91 0 0 0 4 17 17 34 156 158 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 32 146 147 4 18 18 0 0 0 0 0 0 2 7 7 27 125 126 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 37 170 172 7 33 34 +8 36 36 38 173 175 40 184 186 40 184 186 40 184 186 37 170 172 7 32 32 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 35 35 33 153 155 40 184 186 40 184 186 40 184 186 40 184 186 25 115 116 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 12 55 55 39 180 182 40 184 186 40 184 186 40 184 186 34 156 158 4 17 17 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 25 25 36 164 166 40 184 186 40 184 186 40 184 186 39 178 179 10 47 47 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 12 12 32 148 150 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 18 84 85 0 0 0 0 0 0 9 40 40 37 172 174 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 39 180 182 10 46 47 0 0 0 2 11 11 31 141 142 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 20 91 92 0 0 0 4 17 17 34 158 160 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 183 185 16 73 74 0 0 0 0 0 0 11 48 49 38 176 178 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 37 170 172 7 33 34 +8 36 36 38 173 175 40 184 186 40 184 186 40 184 186 39 178 179 24 110 111 20 92 93 20 93 94 20 93 94 20 93 94 20 93 94 20 93 94 20 93 94 20 93 94 20 93 94 20 93 94 20 93 94 20 93 94 23 106 107 34 157 159 40 184 186 40 184 186 40 184 186 40 184 186 40 183 185 15 71 71 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 12 55 55 39 180 182 40 184 186 40 184 186 40 184 186 34 156 158 4 17 17 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 2 14 66 66 21 95 96 20 93 94 20 93 94 20 93 94 20 93 94 20 93 94 20 93 94 20 93 94 21 95 96 13 60 61 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 25 25 36 164 166 40 184 186 40 184 186 40 184 186 39 178 179 10 47 47 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 12 12 32 148 150 40 184 186 40 184 186 40 184 186 40 183 185 28 127 128 21 95 96 21 96 97 21 96 97 21 96 97 21 96 97 21 96 97 21 96 97 21 95 96 22 103 104 35 162 164 40 184 186 40 184 186 40 184 186 40 184 186 28 128 130 1 4 4 0 0 0 18 81 82 40 184 186 40 184 186 40 184 186 40 184 186 38 176 178 25 117 118 21 95 96 21 96 97 21 96 97 21 96 97 21 96 97 21 96 97 21 96 97 21 96 97 21 96 97 21 96 97 21 96 97 21 96 97 21 96 97 20 92 93 5 24 24 0 0 0 8 36 36 37 172 174 40 184 186 40 184 186 40 184 186 40 183 185 30 137 139 21 96 97 21 96 97 21 96 97 21 96 97 21 96 97 21 96 97 21 96 97 21 96 97 21 96 97 21 96 97 21 96 97 21 96 97 21 96 97 21 97 98 10 47 47 0 0 0 2 9 9 18 81 82 21 97 98 21 96 97 21 96 97 21 96 97 21 96 97 21 96 97 21 96 97 21 96 97 21 96 97 21 96 97 21 96 97 21 96 97 21 95 96 23 106 107 36 166 168 40 184 186 40 184 186 40 184 186 40 184 186 26 118 120 0 1 1 0 0 0 20 92 93 40 184 186 40 184 186 40 184 186 40 184 186 38 173 175 25 113 115 21 95 96 21 96 97 21 96 97 21 96 97 21 96 97 21 96 97 21 96 97 21 95 96 24 112 113 39 178 179 40 184 186 40 184 186 40 184 186 37 170 172 7 33 34 +8 36 36 38 173 175 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 33 151 152 4 19 20 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 12 55 55 39 180 182 40 184 186 40 184 186 40 184 186 34 156 158 4 17 17 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 4 4 29 132 133 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 26 120 121 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 25 25 36 164 166 40 184 186 40 184 186 40 184 186 39 178 179 10 47 47 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 12 12 32 148 150 40 184 186 40 184 186 40 184 186 40 183 185 14 63 63 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 18 82 83 40 184 186 40 184 186 40 184 186 40 184 186 32 146 147 2 11 11 0 0 0 22 100 101 40 184 186 40 184 186 40 184 186 40 184 186 28 129 131 1 5 5 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 11 52 53 39 180 182 40 184 186 40 184 186 40 184 186 36 165 166 6 28 28 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 20 92 93 40 184 186 40 184 186 40 184 186 40 184 186 30 137 139 1 6 7 0 0 0 24 113 114 40 184 186 40 184 186 40 184 186 40 184 186 26 119 120 0 2 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 32 32 37 170 172 40 184 186 40 184 186 40 184 186 37 170 172 7 33 34 +8 36 36 38 173 175 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 38 176 178 14 65 66 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 12 55 55 39 180 182 40 184 186 40 184 186 40 184 186 34 156 158 4 17 17 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 4 4 28 130 131 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 26 118 119 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 25 25 36 164 166 40 184 186 40 184 186 40 184 186 39 178 179 10 47 47 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 12 12 32 148 150 40 184 186 40 184 186 40 184 186 40 183 185 14 64 65 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 14 63 64 40 183 185 40 184 186 40 184 186 40 184 186 32 148 150 3 12 12 0 0 0 22 101 102 40 184 186 40 184 186 40 184 186 40 184 186 25 115 117 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 12 55 55 39 180 182 40 184 186 40 184 186 40 184 186 34 158 160 7 31 31 3 16 16 4 17 17 4 17 17 4 17 17 4 17 17 4 17 17 4 17 17 4 17 17 4 17 17 4 17 17 4 17 17 4 17 17 4 17 17 4 17 17 2 8 8 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 9 9 3 16 16 4 17 17 4 17 17 4 17 17 4 17 17 4 17 17 4 17 17 4 17 17 3 14 14 18 84 85 40 184 186 40 184 186 40 184 186 40 184 186 30 139 141 2 7 7 0 0 0 25 116 117 40 184 186 40 184 186 40 184 186 40 184 186 23 105 106 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 33 34 37 171 173 40 184 186 40 184 186 40 184 186 37 170 172 7 33 34 +8 36 36 38 173 175 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 38 175 177 18 83 84 0 2 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 12 55 55 39 180 182 40 184 186 40 184 186 40 184 186 34 156 158 4 17 17 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 4 4 28 130 131 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 26 118 119 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 25 25 36 164 166 40 184 186 40 184 186 40 184 186 39 178 179 10 47 47 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 12 12 32 148 150 40 184 186 40 184 186 40 184 186 40 183 185 14 64 65 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 14 64 65 40 183 185 40 184 186 40 184 186 40 184 186 32 148 150 3 12 12 0 0 0 22 101 102 40 184 186 40 184 186 40 184 186 40 184 186 25 116 117 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 12 55 55 39 180 182 40 184 186 40 184 186 40 184 186 39 180 182 34 157 159 34 155 157 34 155 157 34 155 157 34 155 157 34 155 157 34 155 157 34 155 157 34 155 157 34 155 157 34 155 157 34 155 157 34 155 157 34 155 157 34 157 159 16 76 77 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 10 44 44 23 106 107 31 143 144 33 154 155 34 155 157 34 155 157 34 155 157 34 155 157 34 155 157 34 155 157 34 155 157 34 154 156 36 167 168 40 184 186 40 184 186 40 184 186 40 184 186 30 139 141 2 7 7 0 0 0 25 116 117 40 184 186 40 184 186 40 184 186 40 184 186 23 105 106 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 33 34 37 171 173 40 184 186 40 184 186 40 184 186 37 170 172 7 33 34 +8 36 36 38 173 175 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 39 180 182 31 144 146 13 60 61 0 2 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 12 55 55 39 180 182 40 184 186 40 184 186 40 184 186 34 156 158 4 17 17 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 4 4 29 132 133 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 26 120 121 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 25 25 36 164 166 40 184 186 40 184 186 40 184 186 39 178 179 10 47 47 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 12 12 32 148 150 40 184 186 40 184 186 40 184 186 40 183 185 14 64 65 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 14 64 65 40 183 185 40 184 186 40 184 186 40 184 186 32 148 150 3 12 12 0 0 0 22 101 102 40 184 186 40 184 186 40 184 186 40 184 186 25 116 117 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 12 55 55 39 180 182 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 20 91 92 0 0 0 0 0 0 0 0 0 2 7 7 20 92 93 37 171 173 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 30 139 141 2 7 7 0 0 0 25 116 117 40 184 186 40 184 186 40 184 186 40 184 186 23 105 106 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 33 34 37 171 173 40 184 186 40 184 186 40 184 186 37 170 172 7 33 34 +8 36 36 38 173 175 40 184 186 40 184 186 40 184 186 39 180 182 29 133 135 27 122 123 27 123 124 27 123 124 27 123 124 26 121 123 31 141 143 40 183 185 40 184 186 40 184 186 40 184 186 40 184 186 40 183 185 30 137 139 21 98 99 13 58 59 3 14 15 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 12 55 55 39 180 182 40 184 186 40 184 186 40 184 186 34 156 158 4 17 17 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 3 3 19 87 88 27 124 125 27 123 124 27 123 124 27 123 124 27 123 124 27 123 124 27 123 124 27 123 124 27 125 126 17 79 80 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 25 25 36 164 166 40 184 186 40 184 186 40 184 186 39 178 179 10 47 47 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 12 12 32 148 150 40 184 186 40 184 186 40 184 186 40 183 185 14 64 65 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 14 64 65 40 183 185 40 184 186 40 184 186 40 184 186 32 148 150 3 12 12 0 0 0 22 101 102 40 184 186 40 184 186 40 184 186 40 184 186 25 116 117 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 12 55 55 39 180 182 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 20 90 91 0 0 0 0 0 0 0 2 2 20 93 94 40 183 185 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 30 139 141 2 7 7 0 0 0 25 116 117 40 184 186 40 184 186 40 184 186 40 184 186 23 105 106 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 33 34 37 171 173 40 184 186 40 184 186 40 184 186 37 170 172 7 33 34 +8 36 36 38 173 175 40 184 186 40 184 186 40 184 186 37 171 173 8 35 35 0 0 0 0 1 1 0 1 1 0 1 1 0 1 1 4 19 20 30 139 140 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 25 116 117 1 5 5 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 12 55 55 39 180 182 40 184 186 40 184 186 40 184 186 34 156 158 4 17 17 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 2 2 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 2 2 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 25 25 36 164 166 40 184 186 40 184 186 40 184 186 39 178 179 10 47 47 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 12 12 32 148 150 40 184 186 40 184 186 40 184 186 40 183 185 14 64 65 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 14 64 65 40 183 185 40 184 186 40 184 186 40 184 186 32 148 150 3 12 12 0 0 0 22 101 102 40 184 186 40 184 186 40 184 186 40 184 186 25 116 117 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 12 55 55 39 180 182 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 20 90 91 0 0 0 0 0 0 10 45 46 37 172 174 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 30 139 141 2 7 7 0 0 0 25 116 117 40 184 186 40 184 186 40 184 186 40 184 186 23 105 106 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 33 34 37 171 173 40 184 186 40 184 186 40 184 186 37 170 172 7 33 34 +8 36 36 38 173 175 40 184 186 40 184 186 40 184 186 37 170 172 7 33 34 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 9 40 41 35 163 165 40 184 186 40 184 186 40 184 186 40 184 186 39 180 182 18 81 82 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 12 55 55 39 180 182 40 184 186 40 184 186 40 184 186 34 156 158 4 17 17 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 25 25 36 164 166 40 184 186 40 184 186 40 184 186 39 178 179 10 47 47 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 12 12 32 148 150 40 184 186 40 184 186 40 184 186 40 183 185 14 64 65 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 14 64 65 40 183 185 40 184 186 40 184 186 40 184 186 32 148 150 3 12 12 0 0 0 22 101 102 40 184 186 40 184 186 40 184 186 40 184 186 25 116 117 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 12 55 55 39 180 182 40 184 186 40 184 186 40 184 186 40 184 186 40 182 184 40 182 184 40 182 184 40 182 184 40 182 184 40 182 184 40 182 184 40 182 184 40 182 184 40 182 184 40 182 184 40 182 184 40 182 184 40 182 184 40 184 186 19 89 90 0 0 0 0 0 0 24 109 110 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 183 185 40 182 184 40 182 184 40 182 184 40 182 184 40 182 184 40 182 184 40 182 184 40 182 184 40 183 185 40 184 186 40 184 186 40 184 186 40 184 186 30 139 141 2 7 7 0 0 0 25 116 117 40 184 186 40 184 186 40 184 186 40 184 186 23 105 106 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 33 34 37 171 173 40 184 186 40 184 186 40 184 186 37 170 172 7 33 34 +8 36 36 38 173 175 40 184 186 40 184 186 40 184 186 37 171 173 7 33 34 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 16 71 72 39 178 180 40 184 186 40 184 186 40 184 186 40 184 186 37 168 170 11 48 49 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 12 55 55 39 180 182 40 184 186 40 184 186 40 184 186 34 156 158 4 17 17 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 25 25 36 164 166 40 184 186 40 184 186 40 184 186 39 178 179 10 47 47 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 12 12 32 148 150 40 184 186 40 184 186 40 184 186 40 183 185 14 64 65 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 14 64 65 40 183 185 40 184 186 40 184 186 40 184 186 32 148 150 3 12 12 0 0 0 22 101 102 40 184 186 40 184 186 40 184 186 40 184 186 25 116 117 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 12 55 55 39 180 182 40 184 186 40 184 186 40 184 186 36 165 166 15 70 71 13 58 58 13 58 59 13 58 59 13 58 59 13 58 59 13 58 59 13 58 59 13 58 59 13 58 59 13 58 59 13 58 59 13 58 59 13 58 59 13 59 60 6 29 29 0 0 0 2 11 11 32 145 147 40 184 186 40 184 186 40 184 186 40 184 186 30 136 138 14 63 64 13 58 59 13 58 59 13 58 59 13 58 59 13 58 59 13 58 59 13 58 59 12 56 57 24 110 111 40 184 186 40 184 186 40 184 186 40 184 186 30 139 141 2 7 7 0 0 0 25 116 117 40 184 186 40 184 186 40 184 186 40 184 186 23 105 106 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 33 34 37 171 173 40 184 186 40 184 186 40 184 186 37 170 172 7 33 34 +8 36 36 38 173 175 40 184 186 40 184 186 40 184 186 37 171 173 7 33 34 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 4 4 23 106 107 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 32 146 148 5 23 23 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 12 55 55 39 180 182 40 184 186 40 184 186 40 184 186 34 156 158 4 17 17 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 25 25 36 164 166 40 184 186 40 184 186 40 184 186 39 178 179 10 47 47 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 12 12 32 148 150 40 184 186 40 184 186 40 184 186 40 183 185 14 64 65 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 14 64 65 40 183 185 40 184 186 40 184 186 40 184 186 32 148 150 3 12 12 0 0 0 22 101 102 40 184 186 40 184 186 40 184 186 40 184 186 25 116 117 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 12 54 55 39 180 182 40 184 186 40 184 186 40 184 186 34 158 160 3 15 15 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 17 17 34 155 157 40 184 186 40 184 186 40 184 186 40 183 185 14 63 64 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 16 71 72 40 184 186 40 184 186 40 184 186 40 184 186 30 139 141 2 7 7 0 0 0 25 115 116 40 184 186 40 184 186 40 184 186 40 184 186 24 108 109 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 32 32 37 170 172 40 184 186 40 184 186 40 184 186 37 170 172 7 33 34 +8 36 36 38 173 175 40 184 186 40 184 186 40 184 186 37 171 173 7 33 34 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 17 18 30 139 140 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 25 116 117 2 7 7 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 12 55 55 39 180 182 40 184 186 40 184 186 40 184 186 34 156 158 4 17 17 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 25 25 36 164 166 40 184 186 40 184 186 40 184 186 39 178 179 10 47 47 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 12 12 32 148 150 40 184 186 40 184 186 40 184 186 40 183 185 14 64 65 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 14 64 65 40 183 185 40 184 186 40 184 186 40 184 186 32 148 150 3 12 12 0 0 0 22 101 102 40 184 186 40 184 186 40 184 186 40 184 186 25 116 117 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 9 43 44 38 176 178 40 184 186 40 184 186 40 184 186 39 179 181 20 92 93 9 41 42 9 42 42 9 42 42 9 42 42 9 42 42 9 42 42 9 42 42 9 42 42 9 42 42 9 42 42 9 42 42 9 42 42 9 42 42 9 43 43 4 20 20 0 0 0 3 12 12 32 147 149 40 184 186 40 184 186 40 184 186 40 184 186 27 125 126 10 46 47 9 42 42 9 42 42 9 42 42 9 42 42 9 42 42 9 42 42 9 42 42 9 40 40 22 100 101 40 184 186 40 184 186 40 184 186 40 184 186 30 139 141 2 7 7 0 0 0 22 102 103 40 184 186 40 184 186 40 184 186 40 184 186 34 156 158 13 60 61 9 41 42 9 42 42 9 42 42 9 42 42 9 42 42 9 42 42 9 42 42 9 40 41 15 68 69 38 174 176 40 184 186 40 184 186 40 184 186 37 170 172 7 33 34 +8 36 36 38 173 175 40 184 186 40 184 186 40 184 186 37 171 173 7 33 34 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 9 41 42 36 164 166 40 184 186 40 184 186 40 184 186 40 184 186 39 181 183 18 82 82 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 12 55 55 39 180 182 40 184 186 40 184 186 40 184 186 34 156 158 4 17 17 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 25 25 36 164 166 40 184 186 40 184 186 40 184 186 39 178 179 10 47 47 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 12 12 32 148 150 40 184 186 40 184 186 40 184 186 40 183 185 14 64 65 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 14 64 65 40 183 185 40 184 186 40 184 186 40 184 186 32 148 150 3 12 12 0 0 0 22 101 102 40 184 186 40 184 186 40 184 186 40 184 186 25 116 117 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 19 20 34 156 158 40 184 186 40 184 186 40 184 186 40 184 186 40 183 185 38 176 178 38 175 177 38 175 177 38 175 177 38 175 177 38 175 177 38 175 177 38 175 177 38 175 177 38 175 177 38 175 177 38 175 177 38 175 177 39 178 179 19 86 87 0 0 0 0 1 1 25 115 116 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 39 178 179 38 175 177 38 175 177 38 175 177 38 175 177 38 175 177 38 175 177 38 175 177 38 175 177 39 179 181 40 184 186 40 184 186 40 184 186 40 184 186 30 139 141 2 7 7 0 0 0 14 66 66 40 183 185 40 184 186 40 184 186 40 184 186 40 184 186 39 180 182 38 175 177 38 175 177 38 175 177 38 175 177 38 175 177 38 175 177 38 175 177 38 175 177 38 177 179 40 183 185 40 184 186 40 184 186 40 184 186 37 170 172 7 33 34 +8 36 36 38 173 175 40 184 186 40 184 186 40 184 186 37 171 173 7 33 34 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 16 72 73 39 178 180 40 184 186 40 184 186 40 184 186 40 184 186 37 169 171 11 48 49 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 12 55 55 39 180 182 40 184 186 40 184 186 40 184 186 34 156 158 4 17 17 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 25 25 36 164 166 40 184 186 40 184 186 40 184 186 39 178 179 10 47 47 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 12 12 32 148 150 40 184 186 40 184 186 40 184 186 40 183 185 14 64 65 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 14 64 65 40 183 185 40 184 186 40 184 186 40 184 186 32 148 150 3 12 12 0 0 0 22 101 102 40 184 186 40 184 186 40 184 186 40 184 186 25 116 117 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 21 99 100 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 20 90 91 0 0 0 0 0 0 11 53 53 38 176 178 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 30 139 141 2 7 7 0 0 0 4 19 19 33 150 152 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 37 170 172 7 33 34 +8 36 36 38 173 175 40 184 186 40 184 186 40 184 186 37 171 173 7 33 34 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 4 4 23 107 108 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 32 146 148 5 23 23 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 12 55 55 39 180 182 40 184 186 40 184 186 40 184 186 34 156 158 4 17 17 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 25 25 36 164 166 40 184 186 40 184 186 40 184 186 39 178 179 10 47 47 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 12 12 32 148 150 40 184 186 40 184 186 40 184 186 40 183 185 14 64 65 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 14 64 65 40 183 185 40 184 186 40 184 186 40 184 186 32 148 150 3 12 12 0 0 0 22 101 102 40 184 186 40 184 186 40 184 186 40 184 186 25 116 117 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 22 23 31 144 146 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 20 90 91 0 0 0 0 0 0 1 4 4 23 105 106 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 30 139 141 2 7 7 0 0 0 0 0 0 14 63 63 38 175 177 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 37 170 172 7 33 34 +8 37 37 38 174 176 40 184 186 40 184 186 40 184 186 37 172 174 7 34 34 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 17 18 30 139 141 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 26 118 119 2 8 8 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 12 55 55 39 181 183 40 184 186 40 184 186 40 184 186 34 157 158 4 17 17 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 25 26 36 165 166 40 184 186 40 184 186 40 184 186 39 179 181 10 48 48 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 12 12 32 149 150 40 184 186 40 184 186 40 184 186 40 184 186 14 65 66 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 14 65 66 40 184 186 40 184 186 40 184 186 40 184 186 32 149 150 3 12 12 0 0 0 22 102 103 40 184 186 40 184 186 40 184 186 40 184 186 25 117 118 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 35 35 30 139 140 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 20 90 91 0 0 0 0 0 0 0 0 0 3 12 12 23 108 109 39 178 180 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 30 140 142 2 7 7 0 0 0 0 0 0 0 1 1 16 73 74 36 166 168 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 40 184 186 37 172 174 7 33 34 +7 33 34 34 157 158 36 167 169 36 167 168 36 167 169 34 154 156 7 30 31 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 9 41 42 33 152 154 36 167 169 36 167 168 36 167 168 36 167 169 36 164 166 15 68 69 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 11 50 50 35 163 165 36 167 169 36 167 168 37 169 171 31 141 143 3 15 15 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 22 23 32 149 150 37 168 170 36 167 168 36 167 169 35 161 163 9 43 43 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 11 11 29 134 136 37 169 171 36 167 168 36 167 168 36 166 168 13 58 59 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 13 58 59 36 166 168 36 167 168 36 167 168 37 169 171 29 134 136 2 11 11 0 0 0 20 92 93 37 170 171 36 167 168 36 167 168 37 170 171 23 105 106 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 17 17 18 82 82 30 138 139 35 161 163 36 167 168 36 167 168 36 167 168 36 167 168 36 167 168 36 167 168 36 167 168 36 167 168 36 167 168 36 167 168 36 167 168 36 167 168 36 167 168 37 169 171 18 82 82 0 0 0 0 0 0 0 0 0 0 0 0 1 5 5 13 59 60 27 124 125 34 157 158 36 166 168 36 167 168 36 167 168 36 167 168 36 167 168 36 167 168 36 167 168 36 167 168 36 167 168 36 167 168 36 167 168 36 167 168 36 167 168 37 170 171 27 126 128 1 6 7 0 0 0 0 0 0 0 0 0 0 0 0 8 38 38 23 107 108 32 149 151 36 165 166 36 167 168 36 167 168 36 167 168 36 167 168 36 167 168 36 167 168 36 167 168 36 167 168 36 167 168 36 167 168 36 167 168 36 167 168 36 167 169 34 154 156 7 30 31 +1 6 6 6 27 27 6 29 29 6 28 28 6 29 29 6 27 27 1 5 5 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 21 21 6 29 29 6 28 28 6 28 28 6 28 28 6 29 29 4 20 20 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 9 9 6 28 28 6 28 28 6 28 28 6 29 29 5 24 24 1 3 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 4 4 5 25 26 6 29 29 6 28 28 6 28 28 6 27 28 2 7 7 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 5 23 23 6 29 29 6 28 28 6 28 28 6 28 28 2 10 10 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 10 10 6 28 28 6 28 28 6 28 28 6 29 29 5 23 23 0 1 1 0 0 0 3 16 16 6 29 29 6 28 28 6 28 28 6 29 29 4 18 18 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 8 8 5 22 22 6 28 28 6 28 28 6 28 28 6 28 28 6 28 28 6 28 28 6 28 28 6 28 28 6 28 28 6 28 28 6 28 28 6 28 28 6 28 28 6 29 29 3 14 14 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 4 4 4 18 18 6 27 27 6 28 28 6 28 28 6 28 28 6 28 28 6 28 28 6 28 28 6 28 28 6 28 28 6 28 28 6 28 28 6 28 28 6 28 28 6 29 29 5 22 22 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 14 14 5 25 26 6 28 28 6 28 28 6 28 28 6 28 28 6 28 28 6 28 28 6 28 28 6 28 28 6 28 28 6 28 28 6 28 28 6 28 28 6 29 29 6 26 26 1 5 5 diff --git a/components/drivers/video/logo/logo.c b/components/drivers/video/logo/logo.c new file mode 100644 index 000000000000..2a06d1626fe4 --- /dev/null +++ b/components/drivers/video/logo/logo.c @@ -0,0 +1,157 @@ +/* + * 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 "rtdm.logo" +#define DBG_LVL DBG_INFO +#include + +static rt_uint8_t startup_logo[] = +{ + #include "logo.inc" +}; + +static int startup_logo_width = __RT_THREAD_STARTUP_LOGO_WIDTH__; +static int startup_logo_height = __RT_THREAD_STARTUP_LOGO_HEIGHT__; +static int startup_logo_gray = __RT_THREAD_STARTUP_LOGO_GRAY__; + +static int logo_rendering(void) +{ + rt_err_t err = RT_EOK; + rt_uint8_t *fb, *logo, alpha; + int bpp, xlate, xoffset, yoffset; + int red_id, green_id, blue_id, alpha_size, color_nr; + rt_uint32_t red_off, green_off, blue_off; + rt_uint32_t red_mask, green_mask, blue_mask; + struct fb_var_screeninfo var = {}; + struct fb_fix_screeninfo fix = {}; + rt_device_t fbdev = rt_device_find("fb0"); + + if (!fbdev || (err = rt_device_open(fbdev, 0))) + { + return err ? : (int)-RT_EIO; + } + + if ((err = rt_device_control(fbdev, FBIOGET_VSCREENINFO, &var))) + { + LOG_E("Get framebuffer %s information error = %s", "var", rt_strerror(err)); + + goto _close_fbdev; + } + + if (var.bits_per_pixel == 32) + { + alpha_size = 1; + } + else if (var.bits_per_pixel == 24) + { + alpha_size = 0; + } + else + { + LOG_E("Only supported 32 or 24 bits"); + + err = (int)-RT_EINVAL; + goto _close_fbdev; + } + + if ((err = rt_device_control(fbdev, FBIOGET_FSCREENINFO, &fix))) + { + LOG_E("Get framebuffer %s information error = %s", "fix", rt_strerror(err)); + + goto _close_fbdev; + } + + if (startup_logo_width > var.xres || + startup_logo_height > var.yres) + { + LOG_E("PPM logo[%u, %u] Out of screen[%u, %u]", + startup_logo_width, startup_logo_height, var.xres, var.yres); + + err = (int)-RT_EINVAL; + goto _close_fbdev; + } + + bpp = var.bits_per_pixel / 8; + xlate = (var.xres - startup_logo_width + var.xoffset) * bpp; + xoffset = (var.xres - startup_logo_width) >> 1; + yoffset = (var.yres - startup_logo_height) >> 1; + + fb = (void *)fix.smem_start; + fb += (xoffset + var.xoffset) * bpp + (yoffset + var.yoffset) * fix.line_length; + + logo = startup_logo; + + red_off = var.red.offset; + red_mask = RT_GENMASK(var.red.length, 0); + green_off = var.green.offset; + green_mask = RT_GENMASK(var.green.length, 0); + blue_off = var.blue.offset; + blue_mask = RT_GENMASK(var.blue.length, 0); + + alpha = (0xff & RT_GENMASK(var.transp.length, 0)) << var.transp.offset; + + if (!startup_logo_gray) + { + color_nr = 3; + red_id = 0; + green_id = 1; + blue_id = 2; + } + else + { + color_nr = 1; + red_id = green_id = blue_id = 0; + } + + for (int dy = 0; dy < startup_logo_height; ++dy) + { + for (int dx = 0; dx < startup_logo_width; ++dx) + { + *(rt_uint32_t *)fb = ((logo[red_id] & red_mask) << red_off) | + ((logo[green_id] & green_mask) << green_off) | + ((logo[blue_id] & blue_mask) << blue_off) | + alpha; + + fb += alpha_size + 3; + logo += color_nr; + } + + fb += xlate; + } + + rt_device_control(fbdev, FBIOPAN_DISPLAY, RT_NULL); + +_close_fbdev: + rt_device_close(fbdev); + + return (int)err; +} +#ifdef RT_VIDEO_LOGO_RENDERING_STAGE_DRIVER_EARLY +INIT_DRIVER_EARLY_EXPORT(logo_rendering); +#endif +#ifdef RT_VIDEO_LOGO_RENDERING_STAGE_DRIVER +INIT_DEVICE_EXPORT(logo_rendering); +#endif +#ifdef RT_VIDEO_LOGO_RENDERING_STAGE_DRIVER_LATER +INIT_DRIVER_LATER_EXPORT(logo_rendering); +#endif +#ifdef RT_VIDEO_LOGO_RENDERING_STAGE_COMPONENT +INIT_COMPONENT_EXPORT(logo_rendering); +#endif +#ifdef RT_VIDEO_LOGO_RENDERING_STAGE_ENV +INIT_ENV_EXPORT(logo_rendering); +#endif +#ifdef RT_VIDEO_LOGO_RENDERING_STAGE_APP +INIT_APP_EXPORT(logo_rendering); +#endif 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..dd0f87aa9218 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); } @@ -197,9 +211,9 @@ rt_err_t rt_virtio_device_register(struct rt_virtio_device *vdev) RT_ASSERT(vdev != RT_NULL); RT_ASSERT(vdev->trans != RT_NULL); - vdev->idx = (int)rt_hw_atomic_add(&uid, 1); + vdev->idx = (int)rt_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..e09f7f26bde8 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,98 @@ 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; - } - } - else - { - err = -RT_EIO; + return -RT_ENOMEM; } - return err; -} + vio->base = rt_dm_dev_iomap(dev, 0); -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)); + if (!vio->base) + { + err = -RT_EIO; - do { - rt_uint32_t magic; + goto _fail; + } - if (!vio) - { - err = -RT_ENOMEM; - break; - } + vio->irq = rt_dm_dev_get_irq(dev, 0); - err = virtio_mmio_ofw_init(pdev, vio); + if (vio->irq < 0) + { + err = vio->irq; - if (err) - { - break; - } + goto _fail; + } - 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..6f72a13afb9a 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 @@ -288,8 +318,8 @@ enable_mmu_early: .cpus_stack: #if defined(RT_USING_SMP) && RT_CPUS_NR > 1 .space (ARCH_SECONDARY_CPU_STACK_SIZE * (RT_CPUS_NR - 1)) -.secondary_cpu_stack_top: #endif +.secondary_cpu_stack_top: .space ARCH_SECONDARY_CPU_STACK_SIZE .boot_cpu_stack_top: 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))