diff --git a/bsp/spacemit/dm/Kconfig b/bsp/spacemit/dm/Kconfig new file mode 100755 index 00000000000..b96e3d809ab --- /dev/null +++ b/bsp/spacemit/dm/Kconfig @@ -0,0 +1,23 @@ +SOC_DM_ADC_DIR = $(SOC_DM_DIR)/adc +SOC_DM_CLK_DIR = $(SOC_DM_DIR)/clk +SOC_DM_DMA_DIR = $(SOC_DM_DIR)/dma +SOC_DM_HWCRYPTO_DIR = $(SOC_DM_DIR)/hwcrypto +SOC_DM_HWTIMER_DIR = $(SOC_DM_DIR)/hwtimer +SOC_DM_I2C_DIR = $(SOC_DM_DIR)/i2c +SOC_DM_INPUT_MISC_DIR = $(SOC_DM_DIR)/input/misc +SOC_DM_MBOX_DIR = $(SOC_DM_DIR)/mailbox +SOC_DM_MFD_DIR = $(SOC_DM_DIR)/mfd +SOC_DM_PIC_DIR = $(RTT_DIR)/libcpu/risc-v/common/pic +SOC_DM_PIN_DIR = $(SOC_DM_DIR)/pin +SOC_DM_PINCTRL_DIR = $(SOC_DM_DIR)/pinctrl +SOC_DM_PMDOMAIN_DIR = $(SOC_DM_DIR)/pmdomain +SOC_DM_POWER_RESET_DIR = $(SOC_DM_DIR)/power/reset +SOC_DM_PWM_DIR = $(SOC_DM_DIR)/pwm +SOC_DM_REGULATOR_DIR = $(SOC_DM_DIR)/regulator +SOC_DM_RESET_DIR = $(SOC_DM_DIR)/reset +SOC_DM_RTC_DIR = $(SOC_DM_DIR)/rtc +SOC_DM_SERIAL_DIR = $(SOC_DM_DIR)/serial +SOC_DM_SOC_DIR = $(SOC_DM_DIR)/soc +SOC_DM_SPI_DIR = $(SOC_DM_DIR)/spi +SOC_DM_THERMAL_DIR = $(SOC_DM_DIR)/thermal +SOC_DM_WDT_DIR = $(SOC_DM_DIR)/watchdog diff --git a/bsp/spacemit/dm/SConscript b/bsp/spacemit/dm/SConscript new file mode 100755 index 00000000000..3d15055d62d --- /dev/null +++ b/bsp/spacemit/dm/SConscript @@ -0,0 +1,13 @@ +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/spacemit/dm/adc/Kconfig b/bsp/spacemit/dm/adc/Kconfig new file mode 100755 index 00000000000..b93a301f8e6 --- /dev/null +++ b/bsp/spacemit/dm/adc/Kconfig @@ -0,0 +1,6 @@ +config RT_ADC_SPACEMIT_PMIC + bool "Spacemit PMIC ADC driver" + depends on RT_USING_DM + depends on RT_USING_ADC + depends on RT_MFD_SPACEMIT_PMIC + default n diff --git a/bsp/spacemit/dm/adc/SConscript b/bsp/spacemit/dm/adc/SConscript new file mode 100755 index 00000000000..a8ffbf9482d --- /dev/null +++ b/bsp/spacemit/dm/adc/SConscript @@ -0,0 +1,14 @@ +from building import * + +group = [] +cwd = GetCurrentDir() +CPPPATH = [cwd + '/../include'] + +src = [] + +if GetDepend(['RT_ADC_SPACEMIT_PMIC']): + src += ['adc-spacemit-pmic.c'] + +group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') diff --git a/bsp/spacemit/dm/adc/adc-spacemit-pmic.c b/bsp/spacemit/dm/adc/adc-spacemit-pmic.c new file mode 100755 index 00000000000..da4a95e8631 --- /dev/null +++ b/bsp/spacemit/dm/adc/adc-spacemit-pmic.c @@ -0,0 +1,228 @@ +/* + * Copyright (c) 2006-2024, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2024-5-1 GuEe-GUI first version + */ + +#define DBG_TAG "adc.spacemit-pmic" +#define DBG_LVL DBG_INFO +#include + +#define __SPACEMIT_ADC_INTERNAL +#include + +struct spacemit_pmic_adc +{ + struct rt_adc_device parent; + + int irq; + + struct spacemit_pmic *pmic; + const struct spacemit_pmic_adc_data *data; + + struct rt_mutex lock; + struct rt_completion completion; +}; + +#define raw_to_spacemit_pmic_adc(raw) rt_container_of(raw, struct spacemit_pmic_adc, parent) + +static rt_err_t spacemit_pmic_adc_enabled(struct rt_adc_device *device, + rt_int8_t channel, rt_bool_t enabled) +{ + struct spacemit_pmic_adc *pmic_adc = raw_to_spacemit_pmic_adc(device); + + if (channel > pmic_adc->data->iio_desc_nr) + { + return -RT_EINVAL; + } + + return RT_EOK; +} + +static rt_err_t spacemit_pmic_adc_convert(struct rt_adc_device *device, + rt_int8_t channel, rt_uint32_t *value) +{ + rt_err_t err; + rt_uint32_t tmp, adc_val_h, adc_val_l; + struct spacemit_pmic_adc *pmic_adc = raw_to_spacemit_pmic_adc(device); + struct spacemit_pmic *pmic = pmic_adc->pmic; + + if (channel > pmic_adc->data->iio_desc_nr) + { + return -RT_EINVAL; + } + + rt_mutex_take(&pmic_adc->lock, RT_WAITING_FOREVER); + + + /* Reset the ADC auto register */ + spacemit_pmic_update_bits(pmic, SPM8821_ADC_AUTO_REG, + SPM8821_ADC_AUTO_BIT_MSK, 0); + + /* Enable the ADC : ADC_CTRL[0] */ + spacemit_pmic_update_bits(pmic, SPM8821_ADC_CTRL_REG, + SPM8821_ADC_CTRL_BIT_MSK, (1 << SPM8821_ADC_CTRL_EN_BIT_OFFSET)); + + /* Choose the channel of adc : ADC_CFG[1] */ + spacemit_pmic_update_bits(pmic, SPM8821_ADC_CFG1_REG, + SPM8821_ADC_CFG1_ADC_CHNNL_SEL_BIT_MSK, + (channel + SPM8821_ADC_EXTERNAL_CHANNEL_OFFSET) << + SPM8821_ADC_CFG1_ADC_CHNNL_SEL_BIT_OFFSET); + + /* ADC go */ + spacemit_pmic_update_bits(pmic, SPM8821_ADC_CTRL_REG, + SPM8821_ADC_CTRL_BIT_MSK, + (1 << SPM8821_ADC_CTRL_GO_BIT_OFFSET) | + (1 << SPM8821_ADC_CTRL_EN_BIT_OFFSET)); + + if ((err = rt_completion_wait(&pmic_adc->completion, RT_WAITING_FOREVER))) + { + goto _out_lock; + } + + adc_val_h = spacemit_pmic_read(pmic, SPM8821_ADCIN0_RES_H_REG + channel * 2); + adc_val_l = spacemit_pmic_read(pmic, SPM8821_ADCIN0_RES_L_REG + channel * 2); + tmp = spacemit_pmic_read(pmic, SPM8821_VERSION_ID_REG); + + *value = (adc_val_h << (__rt_ffs(SPM8821_ADCIN0_REG_L_BIT_MSK) - 1)) | + ((adc_val_l & SPM8821_ADCIN0_REG_L_BIT_MSK) >> + (__rt_ffs(SPM8821_ADCIN0_REG_L_BIT_MSK) - 1)); + + if (tmp == 0) + { + /* + * if the version of P1 is A, the data read from + * the register is the inverse of the real data + * and the conversion accuracy of P1 is 12 bits + */ + *value = 4095 - *value; + } + +_out_lock: + rt_mutex_release(&pmic_adc->lock); + + return err; +} + +static rt_uint8_t spacemit_pmic_adc_get_resolution(struct rt_adc_device *device) +{ + return 12; +} + +static rt_int16_t spacemit_pmic_adc_get_vref(struct rt_adc_device *device) +{ + return 3000; +} + +static const struct rt_adc_ops spacemit_pmic_adc_ops = +{ + .enabled = spacemit_pmic_adc_enabled, + .convert = spacemit_pmic_adc_convert, + .get_resolution = spacemit_pmic_adc_get_resolution, + .get_vref = spacemit_pmic_adc_get_vref, +}; + +static void spacemit_pmic_adc_isr(int irq, void *param) +{ + struct spacemit_pmic_adc *pmic_adc = param; + + rt_completion_done(&pmic_adc->completion); +} + +static rt_err_t spacemit_pmic_adc_probe(struct rt_platform_device *pdev) +{ + rt_err_t err; + const char *dev_name; + struct rt_device *dev = &pdev->parent; + struct spacemit_pmic *pmic = pdev->priv; + struct spacemit_pmic_adc *pmic_adc = rt_calloc(1, sizeof(*pmic_adc)); + const struct spacemit_pmic_adc_data *data = pdev->id->data; + + if (!pmic_adc) + { + return -RT_ENOMEM; + } + + pmic_adc->pmic = pmic; + pmic_adc->data = data; + + if ((pmic_adc->irq = rt_dm_dev_get_irq(dev, 0)) < 0) + { + err = pmic_adc->irq; + goto _fail; + } + + /* Enable chop */ + spacemit_pmic_update_bits(pmic, + SPM8821_ADC_CFG1_REG, + SPM8821_ADC_CFG1_ADC_CHOP_EN_BIT_MSK, + 1 << SPM8821_ADC_CFG1_ADC_CHOP_EN_BIT_OFFSET); + + /* Set the vref: 3v3 */ + spacemit_pmic_update_bits(pmic, + SPM8821_ADC_CFG2_REG, + SPM8821_ADC_CFG2_REF_SEL_BIT_MSK, + SPM8821_ADC_CFG2_3V3_REF << SPM8821_ADC_CFG2_REF_SEL_BIT_OFFSET); + + /* Set adc deb num: 7 */ + spacemit_pmic_update_bits(pmic, + SPM8821_ADC_CFG2_REG, + SPM8821_ADC_CFG2_DEB_NUM_BIT_MSK, + SPM8821_ADC_CFG2_7_DEB_NUM << SPM8821_ADC_CFG2_DEB_NUM_BIT_OFFSET); + + dev->user_data = pmic_adc; + + rt_dm_dev_set_name_auto(&pmic_adc->parent.parent, "adc"); + dev_name = rt_dm_dev_get_name(&pmic_adc->parent.parent); + + rt_mutex_init(&pmic_adc->lock, dev_name, RT_IPC_FLAG_PRIO); + rt_completion_init(&pmic_adc->completion); + + rt_hw_adc_register(&pmic_adc->parent, dev_name, &spacemit_pmic_adc_ops, pmic_adc); + + rt_hw_interrupt_install(pmic_adc->irq, spacemit_pmic_adc_isr, pmic_adc, "adc-spacemit-pmic"); + rt_hw_interrupt_umask(pmic_adc->irq); + + rt_dm_dev_bind_fwdata(dev, RT_NULL, pmic_adc); + + return RT_EOK; + +_fail: + rt_free(pmic_adc); + + return err; +} + +static rt_err_t spacemit_pmic_adc_remove(struct rt_platform_device *pdev) +{ + struct spacemit_pmic_adc *pmic_adc = pdev->parent.user_data; + + rt_hw_interrupt_mask(pmic_adc->irq); + rt_pic_detach_irq(pmic_adc->irq, pmic_adc); + + rt_device_unregister(&pmic_adc->parent.parent); + + rt_free(pmic_adc); + + return RT_EOK; +} + +static const struct rt_ofw_node_id spacemit_pmic_adc_ofw_ids[] = +{ + { .compatible = "pmic,adc,spm8821", .data = &spm8821_adc_data }, + { /* sentinel */ } +}; + +static struct rt_platform_driver spacemit_pmic_adc_driver = +{ + .name = "spacemit-pmic-adc", + .ids = spacemit_pmic_adc_ofw_ids, + + .probe = spacemit_pmic_adc_probe, + .remove = spacemit_pmic_adc_remove, +}; +RT_PLATFORM_DRIVER_EXPORT(spacemit_pmic_adc_driver); diff --git a/bsp/spacemit/dm/clk/Kconfig b/bsp/spacemit/dm/clk/Kconfig new file mode 100755 index 00000000000..abc9ab79a3c --- /dev/null +++ b/bsp/spacemit/dm/clk/Kconfig @@ -0,0 +1,9 @@ +menuconfig RT_CLK_SPACEMIT + bool "Spacemit clock controller common" + depends on RT_USING_OFW + default y + +config RT_CLK_SPACEMIT_K1X + bool "Spacemit k1x clock controller support" + depends on RT_CLK_SPACEMIT + default n diff --git a/bsp/spacemit/dm/clk/SConscript b/bsp/spacemit/dm/clk/SConscript new file mode 100755 index 00000000000..3051d69dfb6 --- /dev/null +++ b/bsp/spacemit/dm/clk/SConscript @@ -0,0 +1,15 @@ +from building import * + +cwd = GetCurrentDir() +src = [] +CPPPATH = [cwd + '/../include'] + +if GetDepend(['RT_CLK_SPACEMIT']): + src += ['ccu_ddn.c', 'ccu_ddr.c', 'ccu_dpll.c', 'ccu_mix.c', 'ccu_pll.c'] + + if GetDepend(['RT_CLK_SPACEMIT_K1X']): + src += ['ccu-spacemit-k1x.c'] + +group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') diff --git a/bsp/spacemit/dm/clk/ccu-spacemit-k1x.c b/bsp/spacemit/dm/clk/ccu-spacemit-k1x.c new file mode 100755 index 00000000000..69cee1d4487 --- /dev/null +++ b/bsp/spacemit/dm/clk/ccu-spacemit-k1x.c @@ -0,0 +1,1244 @@ +/* + * Copyright (c) 2006-2024, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2024-5-1 GuEe-GUI first version + */ + +#include +#include + +#include "ccu_ddn.h" +#include "ccu_ddr.h" +#include "ccu_mix.h" +#include "ccu_dpll.h" +#include "ccu_pll.h" + +#include +#include + +struct k1x_clk +{ + struct rt_clk_node parent; + + union + { + struct + { + void *mpmu_base; + void *apmu_base; + void *apbc_base; + void *apbs_base; + void *ciu_base; + void *dciu_base; + void *ddrc_base; + void *apbc2_base; + void *rcpu_base; + void *rcpu2_base; + void *audpmu_base; + void *audio_ctrl_base; + }; + void *mmio[CRU_BASE_TYPE_MAX]; + }; +}; + +static const struct ccu_pll_rate_tbl pll2_rate_tbl[] = +{ + PLL_RATE(3000000000UL, 0x66, 0xdd, 0x50, 0x00, 0x3f, 0xe00000), + PLL_RATE(3200000000UL, 0x67, 0xdd, 0x50, 0x00, 0x43, 0xeaaaab), + PLL_RATE(2457600000UL, 0x64, 0xdd, 0x50, 0x00, 0x33, 0x0ccccd), + PLL_RATE(2800000000UL, 0x66, 0xdd, 0x50, 0x00, 0x3a, 0x155555), +}; + +static const struct ccu_pll_rate_tbl pll3_rate_tbl[] = +{ + PLL_RATE(1600000000UL, 0x61, 0xcd, 0x50, 0x00, 0x43, 0xeaaaab), + PLL_RATE(1800000000UL, 0x61, 0xcd, 0x50, 0x00, 0x4b, 0x000000), + PLL_RATE(2000000000UL, 0x62, 0xdd, 0x50, 0x00, 0x2a, 0xeaaaab), + PLL_RATE(3000000000UL, 0x66, 0xdd, 0x50, 0x00, 0x3f, 0xe00000), + PLL_RATE(3200000000UL, 0x67, 0xdd, 0x50, 0x00, 0x43, 0xeaaaab), + PLL_RATE(2457600000UL, 0x64, 0xdd, 0x50, 0x00, 0x33, 0x0ccccd), +}; + +static SPACEMIT_CCU_PLL(pll2, "pll2", &pll2_rate_tbl, RT_ARRAY_SIZE(pll2_rate_tbl), + CRU_BASE_TYPE_APBS, APB_SPARE7_REG, APB_SPARE8_REG, APB_SPARE9_REG, + MPMU_POSR, POSR_PLL2_LOCK, 1, RT_CLK_F_IGNORE_UNUSED); + +static SPACEMIT_CCU_PLL(pll3, "pll3", &pll3_rate_tbl, RT_ARRAY_SIZE(pll3_rate_tbl), + CRU_BASE_TYPE_APBS, APB_SPARE10_REG, APB_SPARE11_REG, APB_SPARE12_REG, + MPMU_POSR, POSR_PLL3_LOCK, 1, RT_CLK_F_IGNORE_UNUSED); + +/* PLL1 */ +static SPACEMIT_CCU_GATE_FACTOR(pll1_d2, "pll1_d2", "pll1_2457p6_vco", + CRU_BASE_TYPE_APBS, APB_SPARE2_REG, RT_BIT(1), RT_BIT(1), 0x0, 2, 1, RT_CLK_F_IGNORE_UNUSED); +static SPACEMIT_CCU_GATE_FACTOR(pll1_d3, "pll1_d3", "pll1_2457p6_vco", + CRU_BASE_TYPE_APBS, APB_SPARE2_REG, RT_BIT(2), RT_BIT(2), 0x0, 3, 1, RT_CLK_F_IGNORE_UNUSED); +static SPACEMIT_CCU_GATE_FACTOR(pll1_d4, "pll1_d4", "pll1_2457p6_vco", + CRU_BASE_TYPE_APBS, APB_SPARE2_REG, RT_BIT(3), RT_BIT(3), 0x0, 4, 1, RT_CLK_F_IGNORE_UNUSED); +static SPACEMIT_CCU_GATE_FACTOR(pll1_d5, "pll1_d5", "pll1_2457p6_vco", + CRU_BASE_TYPE_APBS, APB_SPARE2_REG, RT_BIT(4), RT_BIT(4), 0x0, 5, 1, RT_CLK_F_IGNORE_UNUSED); +static SPACEMIT_CCU_GATE_FACTOR(pll1_d6, "pll1_d6", "pll1_2457p6_vco", + CRU_BASE_TYPE_APBS, APB_SPARE2_REG, RT_BIT(5), RT_BIT(5), 0x0, 6, 1, RT_CLK_F_IGNORE_UNUSED); +static SPACEMIT_CCU_GATE_FACTOR(pll1_d7, "pll1_d7", "pll1_2457p6_vco", + CRU_BASE_TYPE_APBS, APB_SPARE2_REG, RT_BIT(6), RT_BIT(6), 0x0, 7, 1, RT_CLK_F_IGNORE_UNUSED); +static SPACEMIT_CCU_GATE_FACTOR(pll1_d8, "pll1_d8", "pll1_2457p6_vco", + CRU_BASE_TYPE_APBS, APB_SPARE2_REG, RT_BIT(7), RT_BIT(7), 0x0, 8, 1, RT_CLK_F_IGNORE_UNUSED); +static SPACEMIT_CCU_GATE_FACTOR(pll1_d11_223p4, "pll1_d11_223p4", "pll1_2457p6_vco", + CRU_BASE_TYPE_APBS, APB_SPARE2_REG, RT_BIT(15), RT_BIT(15), 0x0, 11, 1, RT_CLK_F_IGNORE_UNUSED); +static SPACEMIT_CCU_GATE_FACTOR(pll1_d13_189, "pll1_d13_189", "pll1_2457p6_vco", + CRU_BASE_TYPE_APBS, APB_SPARE2_REG, RT_BIT(16), RT_BIT(16), 0x0, 13, 1, RT_CLK_F_IGNORE_UNUSED); +static SPACEMIT_CCU_GATE_FACTOR(pll1_d23_106p8, "pll1_d23_106p8", "pll1_2457p6_vco", + CRU_BASE_TYPE_APBS, APB_SPARE2_REG, RT_BIT(20), RT_BIT(20), 0x0, 23, 1, RT_CLK_F_IGNORE_UNUSED); +static SPACEMIT_CCU_GATE_FACTOR(pll1_d64_38p4, "pll1_d64_38p4", "pll1_2457p6_vco", + CRU_BASE_TYPE_APBS, APB_SPARE2_REG, RT_BIT(0), RT_BIT(0), 0x0, 64, 1, RT_CLK_F_IGNORE_UNUSED); +static SPACEMIT_CCU_GATE_FACTOR(pll1_aud_245p7, "pll1_aud_245p7", "pll1_2457p6_vco", + CRU_BASE_TYPE_APBS, APB_SPARE2_REG, RT_BIT(10), RT_BIT(10), 0x0, 10, 1, RT_CLK_F_IGNORE_UNUSED); +static SPACEMIT_CCU_GATE_FACTOR(pll1_aud_24p5, "pll1_aud_24p5", "pll1_2457p6_vco", + CRU_BASE_TYPE_APBS, APB_SPARE2_REG, RT_BIT(11), RT_BIT(11), 0x0, 100, 1, RT_CLK_F_IGNORE_UNUSED); + +/* PLL2 */ +static SPACEMIT_CCU_GATE_FACTOR(pll2_d1, "pll2_d1", "pll2", + CRU_BASE_TYPE_APBS, APB_SPARE8_REG, RT_BIT(0), RT_BIT(0), 0x0, 1, 1, RT_CLK_F_IGNORE_UNUSED); +static SPACEMIT_CCU_GATE_FACTOR(pll2_d2, "pll2_d2", "pll2", + CRU_BASE_TYPE_APBS, APB_SPARE8_REG, RT_BIT(1), RT_BIT(1), 0x0, 2, 1, RT_CLK_F_IGNORE_UNUSED); +static SPACEMIT_CCU_GATE_FACTOR(pll2_d3, "pll2_d3", "pll2", + CRU_BASE_TYPE_APBS, APB_SPARE8_REG, RT_BIT(2), RT_BIT(2), 0x0, 3, 1, RT_CLK_F_IGNORE_UNUSED); +static SPACEMIT_CCU_GATE_FACTOR(pll2_d4, "pll2_d4", "pll2", + CRU_BASE_TYPE_APBS, APB_SPARE8_REG, RT_BIT(3), RT_BIT(3), 0x0, 4, 1, RT_CLK_F_IGNORE_UNUSED); +static SPACEMIT_CCU_GATE_FACTOR(pll2_d5, "pll2_d5", "pll2", + CRU_BASE_TYPE_APBS, APB_SPARE8_REG, RT_BIT(4), RT_BIT(4), 0x0, 5, 1, RT_CLK_F_IGNORE_UNUSED); +static SPACEMIT_CCU_GATE_FACTOR(pll2_d6, "pll2_d6", "pll2", + CRU_BASE_TYPE_APBS, APB_SPARE8_REG, RT_BIT(5), RT_BIT(5), 0x0, 6, 1, RT_CLK_F_IGNORE_UNUSED); +static SPACEMIT_CCU_GATE_FACTOR(pll2_d7, "pll2_d7", "pll2", + CRU_BASE_TYPE_APBS, APB_SPARE8_REG, RT_BIT(6), RT_BIT(6), 0x0, 7, 1, RT_CLK_F_IGNORE_UNUSED); +static SPACEMIT_CCU_GATE_FACTOR(pll2_d8, "pll2_d8", "pll2", + CRU_BASE_TYPE_APBS, APB_SPARE8_REG, RT_BIT(7), RT_BIT(7), 0x0, 8, 1, RT_CLK_F_IGNORE_UNUSED); + +/* PLL3 */ +static SPACEMIT_CCU_GATE_FACTOR(pll3_d1, "pll3_d1", "pll3", + CRU_BASE_TYPE_APBS, APB_SPARE11_REG, RT_BIT(0), RT_BIT(0), 0x0, 1, 1, RT_CLK_F_IGNORE_UNUSED); +static SPACEMIT_CCU_GATE_FACTOR(pll3_d2, "pll3_d2", "pll3", + CRU_BASE_TYPE_APBS, APB_SPARE11_REG, RT_BIT(1), RT_BIT(1), 0x0, 2, 1, RT_CLK_F_IGNORE_UNUSED); +static SPACEMIT_CCU_GATE_FACTOR(pll3_d3, "pll3_d3", "pll3", + CRU_BASE_TYPE_APBS, APB_SPARE11_REG, RT_BIT(2), RT_BIT(2), 0x0, 3, 1, RT_CLK_F_IGNORE_UNUSED); +static SPACEMIT_CCU_GATE_FACTOR(pll3_d4, "pll3_d4", "pll3", + CRU_BASE_TYPE_APBS, APB_SPARE11_REG, RT_BIT(3), RT_BIT(3), 0x0, 4, 1, RT_CLK_F_IGNORE_UNUSED); +static SPACEMIT_CCU_GATE_FACTOR(pll3_d5, "pll3_d5", "pll3", + CRU_BASE_TYPE_APBS, APB_SPARE11_REG, RT_BIT(4), RT_BIT(4), 0x0, 5, 1, RT_CLK_F_IGNORE_UNUSED); +static SPACEMIT_CCU_GATE_FACTOR(pll3_d6, "pll3_d6", "pll3", + CRU_BASE_TYPE_APBS, APB_SPARE11_REG, RT_BIT(5), RT_BIT(5), 0x0, 6, 1, RT_CLK_F_IGNORE_UNUSED); +static SPACEMIT_CCU_GATE_FACTOR(pll3_d7, "pll3_d7", "pll3", + CRU_BASE_TYPE_APBS, APB_SPARE11_REG, RT_BIT(6), RT_BIT(6), 0x0, 7, 1, RT_CLK_F_IGNORE_UNUSED); +static SPACEMIT_CCU_GATE_FACTOR(pll3_d8, "pll3_d8", "pll3", + CRU_BASE_TYPE_APBS, APB_SPARE11_REG, RT_BIT(7), RT_BIT(7), 0x0, 8, 1, RT_CLK_F_IGNORE_UNUSED); + +/* PLL3_DIV */ +static SPACEMIT_CCU_FACTOR(pll3_80, "pll3_80", "pll3_d8", 5, 1); +static SPACEMIT_CCU_FACTOR(pll3_40, "pll3_40", "pll3_d8", 10, 1); +static SPACEMIT_CCU_FACTOR(pll3_20, "pll3_20", "pll3_d8", 20, 1); + +/* PLL1_D8 */ +static SPACEMIT_CCU_GATE(pll1_d8_307p2, "pll1_d8_307p2", "pll1_d8", + CRU_BASE_TYPE_MPMU, MPMU_ACGR, RT_BIT(13), RT_BIT(13), 0x0, RT_CLK_F_IGNORE_UNUSED); +static SPACEMIT_CCU_FACTOR(pll1_d32_76p8, "pll1_d32_76p8", "pll1_d8_307p2", 4, 1); +static SPACEMIT_CCU_FACTOR(pll1_d40_61p44, "pll1_d40_61p44", "pll1_d8_307p2", 5, 1); +static SPACEMIT_CCU_FACTOR(pll1_d16_153p6, "pll1_d16_153p6", "pll1_d8", 2, 1); +static SPACEMIT_CCU_GATE_FACTOR(pll1_d24_102p4, "pll1_d24_102p4", "pll1_d8", + CRU_BASE_TYPE_MPMU, MPMU_ACGR, RT_BIT(12), RT_BIT(12), 0x0, 3, 1, RT_CLK_F_IGNORE_UNUSED); +static SPACEMIT_CCU_GATE_FACTOR(pll1_d48_51p2, "pll1_d48_51p2", "pll1_d8", + CRU_BASE_TYPE_MPMU, MPMU_ACGR, RT_BIT(7), RT_BIT(7), 0x0, 6, 1, RT_CLK_F_IGNORE_UNUSED); +static SPACEMIT_CCU_GATE_FACTOR(pll1_d48_51p2_ap, "pll1_d48_51p2_ap", "pll1_d8", + CRU_BASE_TYPE_MPMU, MPMU_ACGR, RT_BIT(11), RT_BIT(11), 0x0, 6, 1, RT_CLK_F_IGNORE_UNUSED); +static SPACEMIT_CCU_GATE_FACTOR(pll1_m3d128_57p6, "pll1_m3d128_57p6", "pll1_d8", + CRU_BASE_TYPE_MPMU, MPMU_ACGR, RT_BIT(8), RT_BIT(8), 0x0, 16, 3, RT_CLK_F_IGNORE_UNUSED); +static SPACEMIT_CCU_GATE_FACTOR(pll1_d96_25p6, "pll1_d96_25p6", "pll1_d8", + CRU_BASE_TYPE_MPMU, MPMU_ACGR, RT_BIT(4), RT_BIT(4), 0x0, 12, 1, RT_CLK_F_IGNORE_UNUSED); +static SPACEMIT_CCU_GATE_FACTOR(pll1_d192_12p8, "pll1_d192_12p8", "pll1_d8", + CRU_BASE_TYPE_MPMU, MPMU_ACGR, RT_BIT(3), RT_BIT(3), 0x0, 24, 1, RT_CLK_F_IGNORE_UNUSED); +static SPACEMIT_CCU_GATE_FACTOR(pll1_d192_12p8_wdt, "pll1_d192_12p8_wdt", "pll1_d8", + CRU_BASE_TYPE_MPMU, MPMU_ACGR, RT_BIT(19), RT_BIT(19), 0x0, 24, 1, RT_CLK_F_IGNORE_UNUSED); +static SPACEMIT_CCU_GATE_FACTOR(pll1_d384_6p4, "pll1_d384_6p4", "pll1_d8", + CRU_BASE_TYPE_MPMU, MPMU_ACGR, RT_BIT(2), RT_BIT(2), 0x0, 48, 1, RT_CLK_F_IGNORE_UNUSED); +static SPACEMIT_CCU_FACTOR(pll1_d768_3p2, "pll1_d768_3p2", "pll1_d384_6p4", 2, 1); +static SPACEMIT_CCU_FACTOR(pll1_d1536_1p6, "pll1_d1536_1p6", "pll1_d384_6p4", 4, 1); +static SPACEMIT_CCU_FACTOR(pll1_d3072_0p8, "pll1_d3072_0p8", "pll1_d384_6p4", 8, 1); +/* PLL1_D7 */ +static SPACEMIT_CCU_FACTOR(pll1_d7_351p08, "pll1_d7_351p08", "pll1_d7", 1, 1); +/* PLL1_D6 */ +static SPACEMIT_CCU_GATE(pll1_d6_409p6, "pll1_d6_409p6", "pll1_d6", + CRU_BASE_TYPE_MPMU, MPMU_ACGR, RT_BIT(0), RT_BIT(0), 0x0, RT_CLK_F_IGNORE_UNUSED); +static SPACEMIT_CCU_GATE_FACTOR(pll1_d12_204p8, "pll1_d12_204p8", "pll1_d6", + CRU_BASE_TYPE_MPMU, MPMU_ACGR, RT_BIT(5), RT_BIT(5), 0x0, 2, 1, RT_CLK_F_IGNORE_UNUSED); +/* PLL1_D5 */ +static SPACEMIT_CCU_GATE(pll1_d5_491p52, "pll1_d5_491p52", "pll1_d5", + CRU_BASE_TYPE_MPMU, MPMU_ACGR, RT_BIT(21), RT_BIT(21), 0x0, RT_CLK_F_IGNORE_UNUSED); +static SPACEMIT_CCU_GATE_FACTOR(pll1_d10_245p76, "pll1_d10_245p76", "pll1_d5", + CRU_BASE_TYPE_MPMU, MPMU_ACGR, RT_BIT(18), RT_BIT(18), 0x0, 2, 1, RT_CLK_F_IGNORE_UNUSED); +/* PLL1_D4 */ +static SPACEMIT_CCU_GATE(pll1_d4_614p4, "pll1_d4_614p4", "pll1_d4", + CRU_BASE_TYPE_MPMU, MPMU_ACGR, RT_BIT(15), RT_BIT(15), 0x0, RT_CLK_F_IGNORE_UNUSED); +static SPACEMIT_CCU_GATE_FACTOR(pll1_d52_47p26, "pll1_d52_47p26", "pll1_d4", + CRU_BASE_TYPE_MPMU, MPMU_ACGR, RT_BIT(10), RT_BIT(10), 0x0, 13, 1, RT_CLK_F_IGNORE_UNUSED); +static SPACEMIT_CCU_GATE_FACTOR(pll1_d78_31p5, "pll1_d78_31p5", "pll1_d4", + CRU_BASE_TYPE_MPMU, MPMU_ACGR, RT_BIT(6), RT_BIT(6), 0x0, 39, 2, RT_CLK_F_IGNORE_UNUSED); +/* PLL1_D3 */ +static SPACEMIT_CCU_GATE(pll1_d3_819p2, "pll1_d3_819p2", "pll1_d3", + CRU_BASE_TYPE_MPMU, MPMU_ACGR, RT_BIT(14), RT_BIT(14), 0x0, RT_CLK_F_IGNORE_UNUSED); +/* PLL1_D2 */ +static SPACEMIT_CCU_GATE(pll1_d2_1228p8, "pll1_d2_1228p8", "pll1_d2", + CRU_BASE_TYPE_MPMU, MPMU_ACGR, RT_BIT(16), RT_BIT(16), 0x0, RT_CLK_F_IGNORE_UNUSED); + +/* DPLL */ +static const struct ccu_dpll_rate_tbl dpll1_rate_tbl[] = +{ + DPLL_RATE(2400000000UL, 0x00, 0x00, 0x20, 0x2a, 0x32, 0x64, 0xdd, 0x50), /* 5000 ppm */ + DPLL_RATE(2400000000UL, 0x00, 0x3b, 0x20, 0x2a, 0x32, 0x64, 0xdd, 0x50), /* 5000 ppm + pre-setting */ +}; + +static const struct ccu_dpll_rate_tbl dpll2_rate_tbl[] = +{ + DPLL_RATE(3200000000UL, 0x55, 0x55, 0x3d, 0x2a, 0x43, 0x67, 0xdd, 0x50), /* 5000ppm */ +}; + +static SPACEMIT_CCU_DPLL(dpll1, "dpll1", &dpll1_rate_tbl, RT_ARRAY_SIZE(dpll1_rate_tbl), + CRU_BASE_TYPE_APMU, APMU_DPLL1_CLK_CTRL1, APMU_DPLL1_CLK_CTRL2, 0, RT_CLK_F_IGNORE_UNUSED); + +static SPACEMIT_CCU_DPLL(dpll2, "dpll2", &dpll2_rate_tbl, RT_ARRAY_SIZE(dpll2_rate_tbl), + CRU_BASE_TYPE_APMU, APMU_DPLL2_CLK_CTRL1, APMU_DPLL2_CLK_CTRL2, 0, RT_CLK_F_IGNORE_UNUSED); + +static const char *const dfc_lvl_parents[] = +{ + "dpll2", "dpll1" +}; + +static SPACEMIT_CCU_DIV_MUX(dfc_lvl0, "dfc_lvl0", dfc_lvl_parents, + CRU_BASE_TYPE_APMU, APMU_DFC_LEVEL0, 14, 2, 8, 1, 0); +static SPACEMIT_CCU_DIV_MUX(dfc_lvl1, "dfc_lvl1", dfc_lvl_parents, + CRU_BASE_TYPE_APMU, APMU_DFC_LEVEL1, 14, 2, 8, 1, 0); +static SPACEMIT_CCU_DIV_MUX(dfc_lvl2, "dfc_lvl2", dfc_lvl_parents, + CRU_BASE_TYPE_APMU, APMU_DFC_LEVEL2, 14, 2, 8, 1, 0); +static SPACEMIT_CCU_DIV_MUX(dfc_lvl3, "dfc_lvl3", dfc_lvl_parents, + CRU_BASE_TYPE_APMU, APMU_DFC_LEVEL3, 14, 2, 8, 1, 0); +static SPACEMIT_CCU_DIV_MUX(dfc_lvl4, "dfc_lvl4", dfc_lvl_parents, + CRU_BASE_TYPE_APMU, APMU_DFC_LEVEL4, 14, 2, 8, 1, 0); +static SPACEMIT_CCU_DIV_MUX(dfc_lvl5, "dfc_lvl5", dfc_lvl_parents, + CRU_BASE_TYPE_APMU, APMU_DFC_LEVEL5, 14, 2, 8, 1, 0); +static SPACEMIT_CCU_DIV_MUX(dfc_lvl6, "dfc_lvl6", dfc_lvl_parents, + CRU_BASE_TYPE_APMU, APMU_DFC_LEVEL6, 14, 2, 8, 1, 0); +static SPACEMIT_CCU_DIV_MUX(dfc_lvl7, "dfc_lvl7", dfc_lvl_parents, + CRU_BASE_TYPE_APMU, APMU_DFC_LEVEL7, 14, 2, 8, 1, 0); +static const char *const ddr_clk_parents[] = +{ + "dfc_lvl0", "dfc_lvl1", "dfc_lvl2", "dfc_lvl3", "dfc_lvl4", "dfc_lvl5", "dfc_lvl6", "dfc_lvl7" +}; +static SPACEMIT_CCU_DDR_FC(ddr, "ddr", ddr_clk_parents, + CRU_BASE_TYPE_APMU, APMU_DFC_AP, RT_BIT(0), 1, 3, 0); + +/* MPMU */ +static const struct ccu_ddn_info uart_ddn_mask_info = +{ + .factor = 2, + .num_mask = 0x1fff, + .den_mask = 0x1fff, + .num_shift = 16, + .den_shift = 0, +}; +static struct ccu_ddn_tbl slow_uart1_tbl[] = +{ + { .num = 125, .den = 24 }, /* rate = parent_rate * 24/125/2) */ +}; +static struct ccu_ddn_tbl slow_uart2_tbl[] = +{ + { .num = 6144, .den = 960 }, /* rate = parent_rate * 960/6144/2) */ +}; + +static SPACEMIT_CCU_GATE_NO_PARENT(slow_uart, "slow_uart", RT_NULL, + CRU_BASE_TYPE_MPMU, MPMU_ACGR, RT_BIT(1), RT_BIT(1), 0x0, 0); +static SPACEMIT_CCU_DDN(slow_uart1_14p74, "slow_uart1_14p74", "pll1_d16_153p6", + &uart_ddn_mask_info, &slow_uart1_tbl, RT_ARRAY_SIZE(slow_uart1_tbl), + CRU_BASE_TYPE_MPMU, MPMU_SUCCR, RT_CLK_F_IGNORE_UNUSED); +static SPACEMIT_CCU_DDN(slow_uart2_48, "slow_uart2_48", "pll1_d4_614p4", + &uart_ddn_mask_info, &slow_uart2_tbl, RT_ARRAY_SIZE(slow_uart2_tbl), + CRU_BASE_TYPE_MPMU, MPMU_SUCCR_1, RT_CLK_F_IGNORE_UNUSED); + +/* rate = parent_rate * den / num / 2) */ +static struct ccu_ddn_info mn_ddn_mask_info = +{ + .factor = 2, + .num_mask = 0x1fff, + .den_mask = 0x1fff, + .num_shift = 16, + .den_shift = 0, +}; + +/* parent_rate = 614400000Hz */ +static struct ccu_ddn_tbl mn_tbl[] = +{ + { .num = 6144, .den = 120 * 2 }, + { .num = 6144, .den = 200 * 2 }, + { .num = 6144, .den = 240 * 2 }, + { .num = 6144, .den = 250 * 2 }, + { .num = 6144, .den = 260 * 2 }, + { .num = 6144, .den = 270 * 2 }, + { .num = 6144, .den = 300 * 2 }, + { .num = 6144, .den = 480 * 2 }, +}; + +static const char *const mn_parent_names[] = +{ + "vctcxo_24", "pll1_d4_614p4" +}; + +static SPACEMIT_CCU_MUX_GATE(mn_src_clk, "mn_src_clk", mn_parent_names, + CRU_BASE_TYPE_MPMU, MPMU_PM_MN, 1, 1, RT_BIT(0), RT_BIT(0), 0x0, 0); + +static SPACEMIT_CCU_DDN(mn_clk, "mn_clk", "mn_src_clk", + &mn_ddn_mask_info, &mn_tbl, RT_ARRAY_SIZE(mn_tbl), CRU_BASE_TYPE_MPMU, MPMU_PM_MN_GPCR, 0); + +/* parent_rate = 600000000Hz */ +static struct ccu_ddn_tbl mn2_tbl[] = +{ + {.num = 6000, .den = 120 * 2}, + {.num = 6000, .den = 200 * 2}, + {.num = 6000, .den = 240 * 2}, + {.num = 6000, .den = 250 * 2}, + {.num = 6000, .den = 260 * 2}, + {.num = 6000, .den = 270 * 2}, + {.num = 6000, .den = 300 * 2}, + {.num = 6000, .den = 480 * 2}, +}; + +static const char *const mn2_parent_names[] = +{ + "vctcxo_24", "pll2_d5" +}; + +static SPACEMIT_CCU_MUX_GATE(mn2_src_clk, "mn2_src_clk", mn2_parent_names, + CRU_BASE_TYPE_MPMU, MPMU_PM_MN, 5, 1, RT_BIT(4), RT_BIT(4), 0x0, 0); + +static SPACEMIT_CCU_DDN(mn2_clk, "mn2_clk", "mn2_src_clk", + &mn_ddn_mask_info, &mn2_tbl, RT_ARRAY_SIZE(mn2_tbl), CRU_BASE_TYPE_MPMU, MPMU_PM_MN_GPCR2, 0); + +/* APBC */ +static const char *const uart_parent_names[] = +{ + "pll1_m3d128_57p6", "slow_uart1_14p74", "slow_uart2_48" +}; +static SPACEMIT_CCU_MUX_GATE(uart1_clk, "uart1_clk", uart_parent_names, + CRU_BASE_TYPE_APBC, APBC_UART1_CLK_RST, 4, 3, 0x3, 0x3, 0x0, 0); +static SPACEMIT_CCU_MUX_GATE(uart2_clk, "uart2_clk", uart_parent_names, + CRU_BASE_TYPE_APBC, APBC_UART2_CLK_RST, 4, 3, 0x3, 0x3, 0x0, 0); +static SPACEMIT_CCU_MUX_GATE(uart3_clk, "uart3_clk", uart_parent_names, + CRU_BASE_TYPE_APBC, APBC_UART3_CLK_RST, 4, 3, 0x3, 0x3, 0x0, 0); +static SPACEMIT_CCU_MUX_GATE(uart4_clk, "uart4_clk", uart_parent_names, + CRU_BASE_TYPE_APBC, APBC_UART4_CLK_RST, 4, 3, 0x3, 0x3, 0x0, 0); +static SPACEMIT_CCU_MUX_GATE(uart5_clk, "uart5_clk", uart_parent_names, + CRU_BASE_TYPE_APBC, APBC_UART5_CLK_RST, 4, 3, 0x3, 0x3, 0x0, 0); +static SPACEMIT_CCU_MUX_GATE(uart6_clk, "uart6_clk", uart_parent_names, + CRU_BASE_TYPE_APBC, APBC_UART6_CLK_RST, 4, 3, 0x3, 0x3, 0x0, 0); +static SPACEMIT_CCU_MUX_GATE(uart7_clk, "uart7_clk", uart_parent_names, + CRU_BASE_TYPE_APBC, APBC_UART7_CLK_RST, 4, 3, 0x3, 0x3, 0x0, 0); +static SPACEMIT_CCU_MUX_GATE(uart8_clk, "uart8_clk", uart_parent_names, + CRU_BASE_TYPE_APBC, APBC_UART8_CLK_RST, 4, 3, 0x3, 0x3, 0x0, 0); +static SPACEMIT_CCU_MUX_GATE(uart9_clk, "uart9_clk", uart_parent_names, + CRU_BASE_TYPE_APBC, APBC_UART9_CLK_RST, 4, 3, 0x3, 0x3, 0x0, 0); +static SPACEMIT_CCU_GATE(gpio_clk, "gpio_clk", "vctcxo_24", + CRU_BASE_TYPE_APBC, APBC_GPIO_CLK_RST, 0x3, 0x3, 0x0, 0); +static const char *pwm_parent_names[] = +{ + "pll1_d192_12p8", "clk_32k" +}; +static SPACEMIT_CCU_MUX_GATE(pwm0_clk, "pwm0_clk", pwm_parent_names, + CRU_BASE_TYPE_APBC, APBC_PWM0_CLK_RST, 4, 3, 0x2, 0x2, 0x0, 0); +static SPACEMIT_CCU_MUX_GATE(pwm1_clk, "pwm1_clk", pwm_parent_names, + CRU_BASE_TYPE_APBC, APBC_PWM1_CLK_RST, 4, 3, 0x2, 0x2, 0x0, 0); +static SPACEMIT_CCU_MUX_GATE(pwm2_clk, "pwm2_clk", pwm_parent_names, + CRU_BASE_TYPE_APBC, APBC_PWM2_CLK_RST, 4, 3, 0x2, 0x2, 0x0, 0); +static SPACEMIT_CCU_MUX_GATE(pwm3_clk, "pwm3_clk", pwm_parent_names, + CRU_BASE_TYPE_APBC, APBC_PWM3_CLK_RST, 4, 3, 0x2, 0x2, 0x0, 0); +static SPACEMIT_CCU_MUX_GATE(pwm4_clk, "pwm4_clk", pwm_parent_names, + CRU_BASE_TYPE_APBC, APBC_PWM4_CLK_RST, 4, 3, 0x2, 0x2, 0x0, 0); +static SPACEMIT_CCU_MUX_GATE(pwm5_clk, "pwm5_clk", pwm_parent_names, + CRU_BASE_TYPE_APBC, APBC_PWM5_CLK_RST, 4, 3, 0x2, 0x2, 0x0, 0); +static SPACEMIT_CCU_MUX_GATE(pwm6_clk, "pwm6_clk", pwm_parent_names, + CRU_BASE_TYPE_APBC, APBC_PWM6_CLK_RST, 4, 3, 0x2, 0x2, 0x0, 0); +static SPACEMIT_CCU_MUX_GATE(pwm7_clk, "pwm7_clk", pwm_parent_names, + CRU_BASE_TYPE_APBC, APBC_PWM7_CLK_RST, 4, 3, 0x2, 0x2, 0x0, 0); +static SPACEMIT_CCU_MUX_GATE(pwm8_clk, "pwm8_clk", pwm_parent_names, + CRU_BASE_TYPE_APBC, APBC_PWM8_CLK_RST, 4, 3, 0x2, 0x2, 0x0, 0); +static SPACEMIT_CCU_MUX_GATE(pwm9_clk, "pwm9_clk", pwm_parent_names, + CRU_BASE_TYPE_APBC, APBC_PWM9_CLK_RST, 4, 3, 0x2, 0x2, 0x0, 0); +static SPACEMIT_CCU_MUX_GATE(pwm10_clk, "pwm10_clk", pwm_parent_names, + CRU_BASE_TYPE_APBC, APBC_PWM10_CLK_RST, 4, 3, 0x2, 0x2, 0x0, 0); +static SPACEMIT_CCU_MUX_GATE(pwm11_clk, "pwm11_clk", pwm_parent_names, + CRU_BASE_TYPE_APBC, APBC_PWM11_CLK_RST, 4, 3, 0x2, 0x2, 0x0, 0); +static SPACEMIT_CCU_MUX_GATE(pwm12_clk, "pwm12_clk", pwm_parent_names, + CRU_BASE_TYPE_APBC, APBC_PWM12_CLK_RST, 4, 3, 0x2, 0x2, 0x0, 0); +static SPACEMIT_CCU_MUX_GATE(pwm13_clk, "pwm13_clk", pwm_parent_names, + CRU_BASE_TYPE_APBC, APBC_PWM13_CLK_RST, 4, 3, 0x2, 0x2, 0x0, 0); +static SPACEMIT_CCU_MUX_GATE(pwm14_clk, "pwm14_clk", pwm_parent_names, + CRU_BASE_TYPE_APBC, APBC_PWM14_CLK_RST, 4, 3, 0x2, 0x2, 0x0, 0); +static SPACEMIT_CCU_MUX_GATE(pwm15_clk, "pwm15_clk", pwm_parent_names, + CRU_BASE_TYPE_APBC, APBC_PWM15_CLK_RST, 4, 3, 0x2, 0x2, 0x0, 0); +static SPACEMIT_CCU_MUX_GATE(pwm16_clk, "pwm16_clk", pwm_parent_names, + CRU_BASE_TYPE_APBC, APBC_PWM16_CLK_RST, 4, 3, 0x2, 0x2, 0x0, 0); +static SPACEMIT_CCU_MUX_GATE(pwm17_clk, "pwm17_clk", pwm_parent_names, + CRU_BASE_TYPE_APBC, APBC_PWM17_CLK_RST, 4, 3, 0x2, 0x2, 0x0, 0); +static SPACEMIT_CCU_MUX_GATE(pwm18_clk, "pwm18_clk", pwm_parent_names, + CRU_BASE_TYPE_APBC, APBC_PWM18_CLK_RST, 4, 3, 0x2, 0x2, 0x0, 0); +static SPACEMIT_CCU_MUX_GATE(pwm19_clk, "pwm19_clk", pwm_parent_names, + CRU_BASE_TYPE_APBC, APBC_PWM19_CLK_RST, 4, 3, 0x2, 0x2, 0x0, 0); +static const char *ssp_parent_names[] = +{ + "pll1_d384_6p4", "pll1_d192_12p8", "pll1_d96_25p6", "pll1_d48_51p2", + "pll1_d768_3p2", "pll1_d1536_1p6", "pll1_d3072_0p8", "vctcxo_1" +}; +static SPACEMIT_CCU_MUX_GATE(ssp3_clk, "ssp3_clk", ssp_parent_names, + CRU_BASE_TYPE_APBC, APBC_SSP3_CLK_RST, 4, 3, 0x3, 0x3, 0x0, 0); +static SPACEMIT_CCU_GATE(rtc_clk, "rtc_clk", "clk_32k", + CRU_BASE_TYPE_APBC, APBC_RTC_CLK_RST, 0x83, 0x83, 0x0, 0); +static const char *twsi_parent_names[] = +{ + "pll1_d78_31p5", "pll1_d48_51p2", "pll1_d40_61p44" +}; +static SPACEMIT_CCU_MUX_GATE(twsi0_clk, "twsi0_clk", twsi_parent_names, + CRU_BASE_TYPE_APBC, APBC_TWSI0_CLK_RST, 4, 3, 0x3, 0x3, 0x0, 0); +static SPACEMIT_CCU_MUX_GATE(twsi1_clk, "twsi1_clk", twsi_parent_names, + CRU_BASE_TYPE_APBC, APBC_TWSI1_CLK_RST, 4, 3, 0x3, 0x3, 0x0, 0); +static SPACEMIT_CCU_MUX_GATE(twsi2_clk, "twsi2_clk", twsi_parent_names, + CRU_BASE_TYPE_APBC, APBC_TWSI2_CLK_RST, 4, 3, 0x3, 0x3, 0x0, 0); +static SPACEMIT_CCU_MUX_GATE(twsi4_clk, "twsi4_clk", twsi_parent_names, + CRU_BASE_TYPE_APBC, APBC_TWSI4_CLK_RST, 4, 3, 0x3, 0x3, 0x0, 0); +static SPACEMIT_CCU_MUX_GATE(twsi5_clk, "twsi5_clk", twsi_parent_names, + CRU_BASE_TYPE_APBC, APBC_TWSI5_CLK_RST, 4, 3, 0x3, 0x3, 0x0, 0); +static SPACEMIT_CCU_MUX_GATE(twsi6_clk, "twsi6_clk", twsi_parent_names, + CRU_BASE_TYPE_APBC, APBC_TWSI6_CLK_RST, 4, 3, 0x3, 0x3, 0x0, 0); +static SPACEMIT_CCU_MUX_GATE(twsi7_clk, "twsi7_clk", twsi_parent_names, + CRU_BASE_TYPE_APBC, APBC_TWSI7_CLK_RST, 4, 3, 0x3, 0x3, 0x0, 0); +static SPACEMIT_CCU_MUX_GATE(twsi8_clk, "twsi8_clk", twsi_parent_names, + CRU_BASE_TYPE_APBC, APBC_TWSI8_CLK_RST, 4, 3, 0x7, 0x3, 0x4, 0); +static const char *timer_parent_names[] = +{ + "pll1_d192_12p8", "clk_32k", "pll1_d384_6p4", "vctcxo_3", "vctcxo_1" +}; +static SPACEMIT_CCU_MUX_GATE(timers1_clk, "timers1_clk", timer_parent_names, + CRU_BASE_TYPE_APBC, APBC_TIMERS1_CLK_RST, 4, 3, 0x3, 0x3, 0x0, 0); +static SPACEMIT_CCU_MUX_GATE(timers2_clk, "timers2_clk", timer_parent_names, + CRU_BASE_TYPE_APBC, APBC_TIMERS2_CLK_RST, 4, 3, 0x3, 0x3, 0x0, 0); +static SPACEMIT_CCU_GATE(aib_clk, "aib_clk", "vctcxo_24", + CRU_BASE_TYPE_APBC, APBC_AIB_CLK_RST, 0x3, 0x3, 0x0, 0); +static SPACEMIT_CCU_GATE_NO_PARENT(onewire_clk, "onewire_clk", RT_NULL, + CRU_BASE_TYPE_APBC, APBC_ONEWIRE_CLK_RST, 0x3, 0x3, 0x0, 0); + +static SPACEMIT_CCU_GATE_FACTOR(i2s_sysclk, "i2s_sysclk", "pll1_d8_307p2", + CRU_BASE_TYPE_MPMU, MPMU_ISCCR, RT_BIT(31), RT_BIT(31), 0x0, 200, 1, 0); +static SPACEMIT_CCU_GATE_FACTOR(i2s_bclk, "i2s_bclk", "i2s_sysclk", + CRU_BASE_TYPE_MPMU, MPMU_ISCCR, RT_BIT(29), RT_BIT(29), 0x0, 1, 1, 0); +static const char *sspa_parent_names[] = +{ + "pll1_d384_6p4", "pll1_d192_12p8", "pll1_d96_25p6", + "pll1_d48_51p2", "pll1_d768_3p2", "pll1_d1536_1p6", "pll1_d3072_0p8", "i2s_bclk" +}; +static SPACEMIT_CCU_MUX_GATE(sspa0_clk, "sspa0_clk", sspa_parent_names, + CRU_BASE_TYPE_APBC, APBC_SSPA0_CLK_RST, 4, 3, 0x3, 0x3, 0x0, 0); +static SPACEMIT_CCU_MUX_GATE(sspa1_clk, "sspa1_clk", sspa_parent_names, + CRU_BASE_TYPE_APBC, APBC_SSPA1_CLK_RST, 4, 3, 0x3, 0x3, 0x0, 0); +static SPACEMIT_CCU_GATE_NO_PARENT(dro_clk, "dro_clk", RT_NULL, + CRU_BASE_TYPE_APBC, APBC_DRO_CLK_RST, 0x1, 0x1, 0x0, 0); +static SPACEMIT_CCU_GATE_NO_PARENT(ir_clk, "ir_clk", RT_NULL, + CRU_BASE_TYPE_APBC, APBC_IR_CLK_RST, 0x1, 0x1, 0x0, 0); +static SPACEMIT_CCU_GATE_NO_PARENT(tsen_clk, "tsen_clk", RT_NULL, + CRU_BASE_TYPE_APBC, APBC_TSEN_CLK_RST, 0x3, 0x3, 0x0, 0); +static SPACEMIT_CCU_GATE_NO_PARENT(ipc_ap2aud_clk, "ipc_ap2aud_clk", RT_NULL, + CRU_BASE_TYPE_APBC, APBC_IPC_AP2AUD_CLK_RST, 0x3, 0x3, 0x0, 0); +static const char *can_parent_names[] = +{ + "pll3_20", "pll3_40", "pll3_80" +}; +static SPACEMIT_CCU_MUX_GATE(can0_clk, "can0_clk", can_parent_names, + CRU_BASE_TYPE_APBC, APBC_CAN0_CLK_RST, 4, 3, RT_BIT(1), RT_BIT(1), 0x0, 0); +static SPACEMIT_CCU_GATE_NO_PARENT(can0_bus_clk, "can0_bus_clk", RT_NULL, + CRU_BASE_TYPE_APBC, APBC_CAN0_CLK_RST, RT_BIT(0), RT_BIT(0), 0x0, 0); + +/* MPMU */ +static SPACEMIT_CCU_GATE(wdt_clk, "wdt_clk", "pll1_d96_25p6", + CRU_BASE_TYPE_MPMU, MPMU_WDTPCR, 0x3, 0x3, 0x0, 0); +static SPACEMIT_CCU_GATE_NO_PARENT(ripc_clk, "ripc_clk", RT_NULL, + CRU_BASE_TYPE_MPMU, MPMU_RIPCCR, 0x3, 0x3, 0x0, 0); + +/* APMU */ +static const char *const jpg_parent_names[] = +{ + "pll1_d4_614p4", "pll1_d6_409p6", "pll1_d5_491p52", "pll1_d3_819p2", + "pll1_d2_1228p8", "pll2_d4", "pll2_d3" +}; +static SPACEMIT_CCU_DIV_FC_MUX_GATE(jpg_clk, "jpg_clk", jpg_parent_names, + CRU_BASE_TYPE_APMU, APMU_JPG_CLK_RES_CTRL, + 5, 3, RT_BIT(15), 2, 3, RT_BIT(1), RT_BIT(1), 0x0, 0); +static SPACEMIT_CCU_GATE_NO_PARENT(jpg_4kafbc_clk, "jpg_4kafbc_clk", RT_NULL, + CRU_BASE_TYPE_APMU, APMU_JPG_CLK_RES_CTRL, RT_BIT(16), RT_BIT(16), 0x0, 0); +static SPACEMIT_CCU_GATE_NO_PARENT(jpg_2kafbc_clk, "jpg_2kafbc_clk", RT_NULL, + CRU_BASE_TYPE_APMU, APMU_JPG_CLK_RES_CTRL, RT_BIT(17), RT_BIT(17), 0x0, 0); +static const char *const ccic2phy_parent_names[] = +{ + "pll1_d24_102p4", "pll1_d48_51p2_ap" +}; +static SPACEMIT_CCU_MUX_GATE(ccic2phy_clk, "ccic2phy_clk", ccic2phy_parent_names, + CRU_BASE_TYPE_APMU, APMU_CSI_CCIC2_CLK_RES_CTRL, 7, 1, RT_BIT(5), RT_BIT(5), 0x0, 0); +static const char *const ccic3phy_parent_names[] = +{ + "pll1_d24_102p4", "pll1_d48_51p2_ap" +}; +static SPACEMIT_CCU_MUX_GATE(ccic3phy_clk, "ccic3phy_clk", ccic3phy_parent_names, + CRU_BASE_TYPE_APMU, APMU_CSI_CCIC2_CLK_RES_CTRL, 31, 1, RT_BIT(30), RT_BIT(30), 0x0, 0); +static const char *const csi_parent_names[] = +{ + "pll1_d5_491p52", "pll1_d6_409p6", "pll1_d4_614p4", "pll1_d3_819p2", + "pll2_d2", "pll2_d3", "pll2_d4", "pll1_d2_1228p8" +}; +static SPACEMIT_CCU_DIV_FC_MUX_GATE(csi_clk, "csi_clk", csi_parent_names, + CRU_BASE_TYPE_APMU, APMU_CSI_CCIC2_CLK_RES_CTRL, + 20, 3, RT_BIT(15), 16, 3, RT_BIT(4), RT_BIT(4), 0x0, 0); +static const char *const camm_parent_names[] = +{ + "pll1_d8_307p2", "pll2_d5", "pll1_d6_409p6", "vctcxo_24" +}; +static SPACEMIT_CCU_DIV_MUX_GATE(camm0_clk, "camm0_clk", camm_parent_names, + CRU_BASE_TYPE_APMU, APMU_CSI_CCIC2_CLK_RES_CTRL, + 23, 4, 8, 2, RT_BIT(28), RT_BIT(28), 0x0, 0); +static SPACEMIT_CCU_DIV_MUX_GATE(camm1_clk, "camm1_clk", camm_parent_names, + CRU_BASE_TYPE_APMU, APMU_CSI_CCIC2_CLK_RES_CTRL, + 23, 4, 8, 2, RT_BIT(6), RT_BIT(6), 0x0, 0); +static SPACEMIT_CCU_DIV_MUX_GATE(camm2_clk, "camm2_clk", camm_parent_names, + CRU_BASE_TYPE_APMU, APMU_CSI_CCIC2_CLK_RES_CTRL, + 23, 4, 8, 2, RT_BIT(3), RT_BIT(3), 0x0, 0); +static const char *const isp_cpp_parent_names[] = +{ + "pll1_d8_307p2", "pll1_d6_409p6" +}; +static SPACEMIT_CCU_DIV_MUX_GATE(isp_cpp_clk, "isp_cpp_clk", isp_cpp_parent_names, + CRU_BASE_TYPE_APMU, APMU_ISP_CLK_RES_CTRL, + 24, 2, 26, 1, RT_BIT(28), RT_BIT(28), 0x0, 0); +static const char *const isp_bus_parent_names[] = +{ + "pll1_d6_409p6", "pll1_d5_491p52", "pll1_d8_307p2", "pll1_d10_245p76" +}; +static SPACEMIT_CCU_DIV_FC_MUX_GATE(isp_bus_clk, "isp_bus_clk", isp_bus_parent_names, + CRU_BASE_TYPE_APMU, APMU_ISP_CLK_RES_CTRL, + 18, 3, RT_BIT(23), 21, 2, RT_BIT(17), RT_BIT(17), 0x0, 0); +static const char *const isp_parent_names[] = +{ + "pll1_d6_409p6", "pll1_d5_491p52", "pll1_d4_614p4", "pll1_d8_307p2" +}; +static SPACEMIT_CCU_DIV_FC_MUX_GATE(isp_clk, "isp_clk", isp_parent_names, + CRU_BASE_TYPE_APMU, APMU_ISP_CLK_RES_CTRL, + 4, 3, RT_BIT(7), 8, 2, RT_BIT(1), RT_BIT(1), 0x0, 0); +static const char *const dpumclk_parent_names[] = +{ + "pll1_d6_409p6", "pll1_d5_491p52", "pll1_d4_614p4", "pll1_d8_307p2" +}; +static SPACEMIT_CCU_DIV2_FC_MUX_GATE(dpu_mclk, "dpu_mclk", dpumclk_parent_names, + CRU_BASE_TYPE_APMU, APMU_LCD_CLK_RES_CTRL1, APMU_LCD_CLK_RES_CTRL2, + 1, 4, RT_BIT(29), 5, 3, RT_BIT(0), RT_BIT(0), 0x0, 0); +static const char *const dpuesc_parent_names[] = +{ + "pll1_d48_51p2_ap", "pll1_d52_47p26", "pll1_d96_25p6", "pll1_d32_76p8" +}; +static SPACEMIT_CCU_MUX_GATE(dpu_esc_clk, "dpu_esc_clk", dpuesc_parent_names, + CRU_BASE_TYPE_APMU, APMU_LCD_CLK_RES_CTRL1, 0, 2, RT_BIT(2), RT_BIT(2), 0x0, 0); +static const char *const dpubit_parent_names[] = +{ + "pll1_d3_819p2", "pll2_d2", "pll2_d3", + "pll1_d2_1228p8", "pll2_d4", "pll2_d5", "pll2_d8", "pll2_d8" +}; +static SPACEMIT_CCU_DIV_FC_MUX_GATE(dpu_bit_clk, "dpu_bit_clk", dpubit_parent_names, + CRU_BASE_TYPE_APMU, APMU_LCD_CLK_RES_CTRL1, + 17, 3, RT_BIT(31), 20, 3, RT_BIT(16), RT_BIT(16), 0x0, 0); +static const char *const dpupx_parent_names[] = +{ + "pll1_d6_409p6", "pll1_d5_491p52", "pll1_d4_614p4", "pll1_d8_307p2", "pll2_d7", "pll2_d8" +}; +static SPACEMIT_CCU_DIV2_FC_MUX_GATE(dpu_pxclk, "dpu_pxclk", dpupx_parent_names, + CRU_BASE_TYPE_APMU, APMU_LCD_CLK_RES_CTRL1, APMU_LCD_CLK_RES_CTRL2, + 17, 4, RT_BIT(30), 21, 3, RT_BIT(16), RT_BIT(16), 0x0, 0); +static SPACEMIT_CCU_GATE_NO_PARENT(dpu_hclk, "dpu_hclk", RT_NULL, + CRU_BASE_TYPE_APMU, APMU_LCD_CLK_RES_CTRL1, RT_BIT(5), RT_BIT(5), 0x0, 0); +static const char *const dpu_spi_parent_names[] = +{ + "pll1_d8_307p2", "pll1_d6_409p6", "pll1_d10_245p76", "pll1_d11_223p4", + "pll1_d13_189", "pll1_d23_106p8", "pll2_d3", "pll2_d5" +}; +static SPACEMIT_CCU_DIV_FC_MUX_GATE(dpu_spi_clk, "dpu_spi_clk", dpu_spi_parent_names, + CRU_BASE_TYPE_APMU, APMU_LCD_SPI_CLK_RES_CTRL, + 8, 3, RT_BIT(7), 12, 3, RT_BIT(1), RT_BIT(1), 0x0, 0); +static SPACEMIT_CCU_GATE_NO_PARENT(dpu_spi_hbus_clk, "dpu_spi_hbus_clk", RT_NULL, + CRU_BASE_TYPE_APMU, APMU_LCD_SPI_CLK_RES_CTRL, RT_BIT(3), RT_BIT(3), 0x0, 0); +static SPACEMIT_CCU_GATE_NO_PARENT(dpu_spi_bus_clk, "dpu_spi_bus_clk", RT_NULL, + CRU_BASE_TYPE_APMU, APMU_LCD_SPI_CLK_RES_CTRL, RT_BIT(5), RT_BIT(5), 0x0, 0); +static SPACEMIT_CCU_GATE_NO_PARENT(dpu_spi_aclk, "dpu_spi_aclk", RT_NULL, + CRU_BASE_TYPE_APMU, APMU_LCD_SPI_CLK_RES_CTRL, RT_BIT(6), RT_BIT(6), 0x0, 0); +static const char *const v2d_parent_names[] = +{ + "pll1_d5_491p52", "pll1_d6_409p6", "pll1_d8_307p2", "pll1_d4_614p4", +}; +static SPACEMIT_CCU_DIV_FC_MUX_GATE(v2d_clk, "v2d_clk", v2d_parent_names, + CRU_BASE_TYPE_APMU, APMU_LCD_CLK_RES_CTRL1, + 9, 3, RT_BIT(28), 12, 2, RT_BIT(8), RT_BIT(8), 0x0, 0); +static const char *const ccic_4x_parent_names[] = +{ + "pll1_d5_491p52", "pll1_d6_409p6", "pll1_d4_614p4", "pll1_d3_819p2", + "pll2_d2", "pll2_d3", "pll2_d4", "pll1_d2_1228p8" +}; +static SPACEMIT_CCU_DIV_FC_MUX_GATE(ccic_4x_clk, "ccic_4x_clk", ccic_4x_parent_names, + CRU_BASE_TYPE_APMU, APMU_CCIC_CLK_RES_CTRL, + 18, 3, RT_BIT(15), 23, 2, RT_BIT(4), RT_BIT(4), 0x0, 0); +static const char *const ccic1phy_parent_names[] = +{ + "pll1_d24_102p4", "pll1_d48_51p2_ap" +}; +static SPACEMIT_CCU_MUX_GATE(ccic1phy_clk, "ccic1phy_clk", ccic1phy_parent_names, + CRU_BASE_TYPE_APMU, APMU_CCIC_CLK_RES_CTRL, 7, 1, RT_BIT(5), RT_BIT(5), 0x0, 0); +static SPACEMIT_CCU_GATE_NO_PARENT(sdh_axi_aclk, "sdh_axi_aclk", RT_NULL, + CRU_BASE_TYPE_APMU, APMU_SDH0_CLK_RES_CTRL, RT_BIT(3), RT_BIT(3), 0x0, 0); +static const char *const sdh01_parent_names[] = +{ + "pll1_d6_409p6", "pll1_d4_614p4", "pll2_d8", "pll2_d5", + "pll1_d11_223p4", "pll1_d13_189", "pll1_d23_106p8" +}; + +static SPACEMIT_CCU_DIV_FC_MUX_GATE(sdh0_clk, "sdh0_clk", sdh01_parent_names, + CRU_BASE_TYPE_APMU, APMU_SDH0_CLK_RES_CTRL, + 8, 3, RT_BIT(11), 5, 3, RT_BIT(4), RT_BIT(4), 0x0, 0); +static SPACEMIT_CCU_DIV_FC_MUX_GATE(sdh1_clk, "sdh1_clk", sdh01_parent_names, + CRU_BASE_TYPE_APMU, APMU_SDH1_CLK_RES_CTRL, + 8, 3, RT_BIT(11), 5, 3, RT_BIT(4), RT_BIT(4), 0x0, 0); +static const char *const sdh2_parent_names[] = +{ + "pll1_d6_409p6", "pll1_d4_614p4", "pll2_d8", "pll1_d3_819p2", + "pll1_d11_223p4", "pll1_d13_189", "pll1_d23_106p8" +}; + +static SPACEMIT_CCU_DIV_FC_MUX_GATE(sdh2_clk, "sdh2_clk", sdh2_parent_names, + CRU_BASE_TYPE_APMU, APMU_SDH2_CLK_RES_CTRL, + 8, 3, RT_BIT(11), 5, 3, RT_BIT(4), RT_BIT(4), 0x0, 0); +static SPACEMIT_CCU_GATE_NO_PARENT(usb_axi_clk, "usb_axi_clk", RT_NULL, + CRU_BASE_TYPE_APMU, APMU_USB_CLK_RES_CTRL, RT_BIT(1), RT_BIT(1), 0x0, 0); +static SPACEMIT_CCU_GATE_NO_PARENT(usb_p1_aclk, "usb_p1_aclk", RT_NULL, + CRU_BASE_TYPE_APMU, APMU_USB_CLK_RES_CTRL, RT_BIT(5), RT_BIT(5), 0x0, 0); +static SPACEMIT_CCU_GATE_NO_PARENT(usb30_clk, "usb30_clk", RT_NULL, + CRU_BASE_TYPE_APMU, APMU_USB_CLK_RES_CTRL, RT_BIT(8), RT_BIT(8), 0x0, 0); +static const char *const qspi_parent_names[] = +{ + "pll1_d6_409p6", "pll2_d8", "pll1_d8_307p2", "pll1_d10_245p76", + "pll1_d11_223p4", "pll1_d23_106p8", "pll1_d5_491p52", "pll1_d13_189" +}; +static SPACEMIT_CCU_DIV_MUX_GATE(qspi_clk, "qspi_clk", qspi_parent_names, + CRU_BASE_TYPE_APMU, APMU_QSPI_CLK_RES_CTRL, + 9, 3, 6, 3, RT_BIT(4), RT_BIT(4), 0x0, 0); +static SPACEMIT_CCU_GATE_NO_PARENT(qspi_bus_clk, "qspi_bus_clk", RT_NULL, + CRU_BASE_TYPE_APMU, APMU_QSPI_CLK_RES_CTRL, RT_BIT(3), RT_BIT(3), 0x0, 0); +static SPACEMIT_CCU_GATE_NO_PARENT(dma_clk, "dma_clk", RT_NULL, + CRU_BASE_TYPE_APMU, APMU_DMA_CLK_RES_CTRL, RT_BIT(3), RT_BIT(3), 0x0, 0); +static const char *const aes_parent_names[] = +{ + "pll1_d12_204p8", "pll1_d24_102p4" +}; +static SPACEMIT_CCU_MUX_GATE(aes_clk, "aes_clk", aes_parent_names, + CRU_BASE_TYPE_APMU, APMU_AES_CLK_RES_CTRL, 6, 1, RT_BIT(5), RT_BIT(5), 0x0, 0); +static const char *const vpu_parent_names[] = +{ + "pll1_d4_614p4", "pll1_d5_491p52", "pll1_d3_819p2", "pll1_d6_409p6", + "pll3_d6", "pll2_d3", "pll2_d4", "pll2_d5" +}; +static SPACEMIT_CCU_DIV_FC_MUX_GATE(vpu_clk, "vpu_clk", vpu_parent_names, + CRU_BASE_TYPE_APMU, APMU_VPU_CLK_RES_CTRL, + 13, 3, RT_BIT(21), 10, 3, RT_BIT(3), RT_BIT(3), 0x0, 0); +static const char *const gpu_parent_names[] = +{ + "pll1_d4_614p4", "pll1_d5_491p52", "pll1_d3_819p2", "pll1_d6_409p6", + "pll3_d6", "pll2_d3", "pll2_d4", "pll2_d5" +}; +static SPACEMIT_CCU_DIV_FC_MUX_GATE(gpu_clk, "gpu_clk", gpu_parent_names, + CRU_BASE_TYPE_APMU, APMU_GPU_CLK_RES_CTRL, + 12, 3, RT_BIT(15), 18, 3, RT_BIT(4), RT_BIT(4), 0x0, 0); +static const char *const emmc_parent_names[] = +{ + "pll1_d6_409p6", "pll1_d4_614p4", "pll1_d52_47p26", "pll1_d3_819p2" +}; +static SPACEMIT_CCU_DIV_FC_MUX_GATE(emmc_clk, "emmc_clk", emmc_parent_names, + CRU_BASE_TYPE_APMU, APMU_PMUA_EM_CLK_RES_CTRL, + 8, 3, RT_BIT(11), 6, 2, 0x18, 0x18, 0x0, 0); +static SPACEMIT_CCU_DIV_GATE(emmc_x_clk, "emmc_x_clk", "pll1_d2_1228p8", + CRU_BASE_TYPE_APMU, APMU_PMUA_EM_CLK_RES_CTRL, + 12, 3, RT_BIT(15), RT_BIT(15), 0x0, 0); +static const char *const audio_parent_names[] = +{ + "pll1_aud_245p7", "pll1_d8_307p2", "pll1_d6_409p6" +}; +static SPACEMIT_CCU_DIV_FC_MUX_GATE(audio_clk, "audio_clk", audio_parent_names, + CRU_BASE_TYPE_APMU, APMU_AUDIO_CLK_RES_CTRL, + 4, 3, RT_BIT(15), 7, 3, RT_BIT(12), RT_BIT(12), 0x0, 0); +static const char *const hdmi_parent_names[] = +{ + "pll1_d6_409p6", "pll1_d5_491p52", "pll1_d4_614p4", "pll1_d8_307p2" +}; +static SPACEMIT_CCU_DIV_FC_MUX_GATE(hdmi_mclk, "hdmi_mclk", hdmi_parent_names, + CRU_BASE_TYPE_APMU, APMU_HDMI_CLK_RES_CTRL, + 1, 4, RT_BIT(29), 5, 3, RT_BIT(0), RT_BIT(0), 0x0, 0); +static const char *const cci550_parent_names[] = +{ + "pll1_d5_491p52", "pll1_d4_614p4", "pll1_d3_819p2", "pll2_d3" +}; +static SPACEMIT_CCU_DIV_FC_MUX(cci550_clk, "cci550_clk", cci550_parent_names, + CRU_BASE_TYPE_APMU, APMU_CCI550_CLK_CTRL, 8, 3, RT_BIT(12), 0, 2, 0); +static const char *const pmua_aclk_parent_names[] = +{ + "pll1_d10_245p76", "pll1_d8_307p2" +}; +static SPACEMIT_CCU_DIV_FC_MUX(pmua_aclk, "pmua_aclk", pmua_aclk_parent_names, + CRU_BASE_TYPE_APMU, APMU_ACLK_CLK_CTRL, 1, 2, RT_BIT(4), 0, 1, 0); +static const char *const cpu_c0_hi_parent_names[] = +{ + "pll3_d2", "pll3_d1" +}; +static SPACEMIT_CCU_MUX(cpu_c0_hi_clk, "cpu_c0_hi_clk", cpu_c0_hi_parent_names, + CRU_BASE_TYPE_APMU, APMU_CPU_C0_CLK_CTRL, 13, 1, 0); +static const char *const cpu_c0_parent_names[] = +{ + "pll1_d4_614p4", "pll1_d3_819p2", "pll1_d6_409p6", + "pll1_d5_491p52", "pll1_d2_1228p8", "pll3_d3", "pll2_d3", "cpu_c0_hi_clk" +}; +static SPACEMIT_CCU_MUX_FC(cpu_c0_core_clk, "cpu_c0_core_clk", cpu_c0_parent_names, + CRU_BASE_TYPE_APMU, APMU_CPU_C0_CLK_CTRL, RT_BIT(12), 0, 3, 0); +static SPACEMIT_CCU_DIV(cpu_c0_ace_clk, "cpu_c0_ace_clk", "cpu_c0_core_clk", + CRU_BASE_TYPE_APMU, APMU_CPU_C0_CLK_CTRL, 6, 3, 0); +static SPACEMIT_CCU_DIV(cpu_c0_tcm_clk, "cpu_c0_tcm_clk", "cpu_c0_core_clk", + CRU_BASE_TYPE_APMU, APMU_CPU_C0_CLK_CTRL, 9, 3, 0); +static const char *const cpu_c1_hi_parent_names[] = +{ + "pll3_d2", "pll3_d1" +}; +static SPACEMIT_CCU_MUX(cpu_c1_hi_clk, "cpu_c1_hi_clk", cpu_c1_hi_parent_names, + CRU_BASE_TYPE_APMU, APMU_CPU_C1_CLK_CTRL, 13, 1, 0); +static const char *const cpu_c1_parent_names[] = +{ + "pll1_d4_614p4", "pll1_d3_819p2", "pll1_d6_409p6", + "pll1_d5_491p52", "pll1_d2_1228p8", "pll3_d3", "pll2_d3", "cpu_c1_hi_clk" +}; +static SPACEMIT_CCU_MUX_FC(cpu_c1_pclk, "cpu_c1_pclk", cpu_c1_parent_names, + CRU_BASE_TYPE_APMU, APMU_CPU_C1_CLK_CTRL, RT_BIT(12), 0, 3, 0); +static SPACEMIT_CCU_DIV(cpu_c1_ace_clk, "cpu_c1_ace_clk", "cpu_c1_pclk", + CRU_BASE_TYPE_APMU, APMU_CPU_C1_CLK_CTRL, 6, 3, 0); +static SPACEMIT_CCU_GATE_NO_PARENT(pcie0_clk, "pcie0_clk", RT_NULL, + CRU_BASE_TYPE_APMU, APMU_PCIE_CLK_RES_CTRL_0, 0x7, 0x7, 0x0, 0); +static SPACEMIT_CCU_GATE_NO_PARENT(pcie1_clk, "pcie1_clk", RT_NULL, + CRU_BASE_TYPE_APMU, APMU_PCIE_CLK_RES_CTRL_1, 0x7, 0x7, 0x0, 0); +static SPACEMIT_CCU_GATE_NO_PARENT(pcie2_clk, "pcie2_clk", RT_NULL, + CRU_BASE_TYPE_APMU, APMU_PCIE_CLK_RES_CTRL_2, 0x7, 0x7, 0x0, 0); +static SPACEMIT_CCU_GATE_NO_PARENT(emac0_bus_clk, "emac0_bus_clk", RT_NULL, + CRU_BASE_TYPE_APMU, APMU_EMAC0_CLK_RES_CTRL, RT_BIT(0), RT_BIT(0), 0x0, 0); +static SPACEMIT_CCU_GATE(emac0_ptp_clk, "emac0_ptp_clk", "pll2_d6", + CRU_BASE_TYPE_APMU, APMU_EMAC0_CLK_RES_CTRL, RT_BIT(15), RT_BIT(15), 0x0, 0); +static SPACEMIT_CCU_GATE_NO_PARENT(emac1_bus_clk, "emac1_bus_clk", RT_NULL, + CRU_BASE_TYPE_APMU, APMU_EMAC1_CLK_RES_CTRL, RT_BIT(0), RT_BIT(0), 0x0, 0); +static SPACEMIT_CCU_GATE(emac1_ptp_clk, "emac1_ptp_clk", "pll2_d6", + CRU_BASE_TYPE_APMU, APMU_EMAC1_CLK_RES_CTRL, RT_BIT(15), RT_BIT(15), 0x0, 0); + +/* APBC2 */ +static const char *const uart1_sec_parent_names[] = +{ + "pll1_m3d128_57p6", "slow_uart1_14p74", "slow_uart2_48" +}; +static SPACEMIT_CCU_MUX_GATE(uart1_sec_clk, "uart1_sec_clk", uart1_sec_parent_names, + CRU_BASE_TYPE_APBC2, APBC2_UART1_CLK_RST, 4, 3, 0x3, 0x3, 0x0, 0); + +static const char *ssp2_sec_parent_names[] = +{ + "pll1_d384_6p4", "pll1_d192_12p8", "pll1_d96_25p6", + "pll1_d48_51p2", "pll1_d768_3p2", "pll1_d1536_1p6", "pll1_d3072_0p8" +}; +static SPACEMIT_CCU_MUX_GATE(ssp2_sec_clk, "ssp2_sec_clk", ssp2_sec_parent_names, + CRU_BASE_TYPE_APBC2, APBC2_SSP2_CLK_RST, 4, 3, 0x3, 0x3, 0x0, 0); +static const char *twsi3_sec_parent_names[] = +{ + "pll1_d78_31p5", "pll1_d48_51p2", "pll1_d40_61p44" +}; +static SPACEMIT_CCU_MUX_GATE(twsi3_sec_clk, "twsi3_sec_clk", twsi3_sec_parent_names, + CRU_BASE_TYPE_APBC2, APBC2_TWSI3_CLK_RST, 4, 3, 0x3, 0x3, 0x0, 0); +static SPACEMIT_CCU_GATE(rtc_sec_clk, "rtc_sec_clk", "clk_32k", + CRU_BASE_TYPE_APBC2, APBC2_RTC_CLK_RST, 0x83, 0x83, 0x0, 0); +static const char *timer_sec_parent_names[] = +{ + "pll1_d192_12p8", "clk_32k", "pll1_d384_6p4", "vctcxo_3", "vctcxo_1" +}; +static SPACEMIT_CCU_MUX_GATE(timers0_sec_clk, "timers0_sec_clk", timer_sec_parent_names, + CRU_BASE_TYPE_APBC2, APBC2_TIMERS0_CLK_RST, 4, 3, 0x3, 0x3, 0x0, 0); +static const char *kpc_sec_parent_names[] = +{ + "pll1_d192_12p8", "clk_32k", "pll1_d384_6p4", "vctcxo_3", "vctcxo_1" +}; +static SPACEMIT_CCU_MUX_GATE(kpc_sec_clk, "kpc_sec_clk", kpc_sec_parent_names, + CRU_BASE_TYPE_APBC2, APBC2_KPC_CLK_RST, 4, 3, 0x3, 0x3, 0x0, 0); +static SPACEMIT_CCU_GATE(gpio_sec_clk, "gpio_sec_clk", "vctcxo_24", + CRU_BASE_TYPE_APBC2, APBC2_GPIO_CLK_RST, 0x3, 0x3, 0x0, 0); + +static const char *const apb_parent_names[] = +{ + "pll1_d96_25p6", "pll1_d48_51p2", "pll1_d96_25p6", "pll1_d24_102p4" +}; +static SPACEMIT_CCU_MUX(apb_clk, "apb_clk", apb_parent_names, + CRU_BASE_TYPE_MPMU, MPMU_APBCSCR, 0, 2, 0); +/* RCPU */ +static const char *rhdmi_audio_parent_names[] = +{ + "pll1_aud_24p5", "pll1_aud_245p7" +}; +static SPACEMIT_CCU_DIV_MUX_GATE(rhdmi_audio_clk, "rhdmi_audio_clk", rhdmi_audio_parent_names, + CRU_BASE_TYPE_RCPU, RCPU_HDMI_CLK_RST, 4, 11, 16, 2, 0x6, 0x6, 0x0, 0); + +static const char *rcan_parent_names[] = +{ + "pll3_20", "pll3_40", "pll3_80" +}; +static SPACEMIT_CCU_DIV_MUX_GATE(rcan_clk, "rcan_clk", rcan_parent_names, + CRU_BASE_TYPE_RCPU, RCPU_CAN_CLK_RST, 8, 11, 4, 2, RT_BIT(1), RT_BIT(1), 0x0, 0); +static SPACEMIT_CCU_GATE_NO_PARENT(rcan_bus_clk, "rcan_bus_clk", RT_NULL, + CRU_BASE_TYPE_RCPU, RCPU_CAN_CLK_RST, RT_BIT(2), RT_BIT(2), 0x0, 0); +/* RCPU2 */ +static const char *rpwm_parent_names[] = +{ + "pll1_aud_245p7", "pll1_aud_24p5" +}; +static SPACEMIT_CCU_DIV_MUX_GATE(rpwm0_clk, "rpwm0_clk", rpwm_parent_names, + CRU_BASE_TYPE_RCPU2, RCPU2_PWM0_CLK_RST, 8, 11, 4, 2, RT_BIT(1), RT_BIT(1), 0x0, 0); +static SPACEMIT_CCU_DIV_MUX_GATE(rpwm1_clk, "rpwm1_clk", rpwm_parent_names, + CRU_BASE_TYPE_RCPU2, RCPU2_PWM1_CLK_RST, 8, 11, 4, 2, RT_BIT(1), RT_BIT(1), 0x0, 0); +static SPACEMIT_CCU_DIV_MUX_GATE(rpwm2_clk, "rpwm2_clk", rpwm_parent_names, + CRU_BASE_TYPE_RCPU2, RCPU2_PWM2_CLK_RST, 8, 11, 4, 2, RT_BIT(1), RT_BIT(1), 0x0, 0); +static SPACEMIT_CCU_DIV_MUX_GATE(rpwm3_clk, "rpwm3_clk", rpwm_parent_names, + CRU_BASE_TYPE_RCPU2, RCPU2_PWM3_CLK_RST, 8, 11, 4, 2, RT_BIT(1), RT_BIT(1), 0x0, 0); +static SPACEMIT_CCU_DIV_MUX_GATE(rpwm4_clk, "rpwm4_clk", rpwm_parent_names, + CRU_BASE_TYPE_RCPU2, RCPU2_PWM4_CLK_RST, 8, 11, 4, 2, RT_BIT(1), RT_BIT(1), 0x0, 0); +static SPACEMIT_CCU_DIV_MUX_GATE(rpwm5_clk, "rpwm5_clk", rpwm_parent_names, + CRU_BASE_TYPE_RCPU2, RCPU2_PWM5_CLK_RST, 8, 11, 4, 2, RT_BIT(1), RT_BIT(1), 0x0, 0); +static SPACEMIT_CCU_DIV_MUX_GATE(rpwm6_clk, "rpwm6_clk", rpwm_parent_names, + CRU_BASE_TYPE_RCPU2, RCPU2_PWM6_CLK_RST, 8, 11, 4, 2, RT_BIT(1), RT_BIT(1), 0x0, 0); +static SPACEMIT_CCU_DIV_MUX_GATE(rpwm7_clk, "rpwm7_clk", rpwm_parent_names, + CRU_BASE_TYPE_RCPU2, RCPU2_PWM7_CLK_RST, 8, 11, 4, 2, RT_BIT(1), RT_BIT(1), 0x0, 0); +static SPACEMIT_CCU_DIV_MUX_GATE(rpwm8_clk, "rpwm8_clk", rpwm_parent_names, + CRU_BASE_TYPE_RCPU2, RCPU2_PWM8_CLK_RST, 8, 11, 4, 2, RT_BIT(1), RT_BIT(1), 0x0, 0); +static SPACEMIT_CCU_DIV_MUX_GATE(rpwm9_clk, "rpwm9_clk", rpwm_parent_names, + CRU_BASE_TYPE_RCPU2, RCPU2_PWM9_CLK_RST, 8, 11, 4, 2, RT_BIT(1), RT_BIT(1), 0x0, 0); + +static const char *ri2c_parent_names[] = +{ + "pll1_d40_61p44", "pll1_d96_25p6", "pll1_d192_12p8", "vctcxo_3" +}; +static SPACEMIT_CCU_DIV_MUX_GATE(ri2c0_clk, "ri2c0_clk", ri2c_parent_names, + CRU_BASE_TYPE_RCPU, RCPU_I2C0_CLK_RST, 8, 11, 4, 2, 0x6, 0x6, 0x0, 0); + +static const char *rssp0_parent_names[] = +{ + "pll1_d40_61p44", "pll1_d96_25p6", "pll1_d192_12p8", "vctcxo_3" +}; +static SPACEMIT_CCU_DIV_MUX_GATE(rssp0_clk, "rssp0_clk", rssp0_parent_names, + CRU_BASE_TYPE_RCPU, RCPU_SSP0_CLK_RST, 8, 11, 4, 2, 0x6, 0x6, 0x0, 0); +static SPACEMIT_CCU_GATE_NO_PARENT(rir_clk, "rir_clk", RT_NULL, + CRU_BASE_TYPE_RCPU, RCPU_IR_CLK_RST, RT_BIT(2), RT_BIT(2), 0x0, 0); +static const char *ruart0_parent_names[] = +{ + "pll1_aud_24p5", "pll1_aud_245p7", "vctcxo_24", "vctcxo_3" +}; +static SPACEMIT_CCU_DIV_MUX_GATE(ruart0_clk, "ruart0_clk", ruart0_parent_names, + CRU_BASE_TYPE_RCPU, RCPU_UART0_CLK_RST, 8, 11, 4, 2, 0x6, 0x6, 0x0, 0); +static const char *ruart1_parent_names[] = +{ + "pll1_aud_24p5", "pll1_aud_245p7", "vctcxo_24", "vctcxo_3" +}; +static SPACEMIT_CCU_DIV_MUX_GATE(ruart1_clk, "ruart1_clk", ruart1_parent_names, + CRU_BASE_TYPE_RCPU, RCPU_UART1_CLK_RST, 8, 11, 4, 2, 0x6, 0x6, 0x0, 0); + +static SPACEMIT_CCU_DIV_FLAG(audio_apb_clk, "audio_apb_clk", "audio_clk", + CRU_BASE_TYPE_AUDPMU, AUDPMU_AUDIO_BUS_CLK_CTRL, + 4, 3, CLK_DIVIDER_POWER_OF_TWO | CLK_DIVIDER_ALLOW_ZERO, 0); + +static SPACEMIT_CCU_DIV_FLAG(audio_axi_clk, "audio_axi_clk", "audio_clk", + CRU_BASE_TYPE_AUDPMU, AUDPMU_AUDIO_BUS_CLK_CTRL, + 0, 2, CLK_DIVIDER_POWER_OF_TWO | CLK_DIVIDER_ALLOW_ZERO, 0); + +static const rt_uint32_t bootup_enable_clk_table[] = +{ + CLK_PLL1_307P2, + CLK_PLL1_409P6, + CLK_PLL1_491, + CLK_PLL1_614, + CLK_PLL1_819, + CLK_PLL1_1228, + CLK_PLL1_245P76, + CLK_PLL1_51P2, + CLK_PLL1_51P2_AP, + CLK_PLL1_25P6, + CLK_PLL3_D1, + CLK_PLL3_D2, + CLK_PLL3_D3, + CLK_PLL2_D3, + CLK_APB, + CLK_PMUA_ACLK, + CLK_DMA, +}; + +static const rt_ubase_t bootup_set_rate_clk_table[] = +{ + CLK_PM_MN_SRC, 614400000, + CLK_PM_MN2_SRC, 600000000, +}; + +static struct rt_clk_cell *spacemit_k1x_ccu_cell[] = +{ + [CLK_PLL2] = &pll2.common.cell, + [CLK_PLL3] = &pll3.common.cell, + [CLK_PLL1_D2] = &pll1_d2.common.cell, + [CLK_PLL1_D3] = &pll1_d3.common.cell, + [CLK_PLL1_D4] = &pll1_d4.common.cell, + [CLK_PLL1_D5] = &pll1_d5.common.cell, + [CLK_PLL1_D6] = &pll1_d6.common.cell, + [CLK_PLL1_D7] = &pll1_d7.common.cell, + [CLK_PLL1_D8] = &pll1_d8.common.cell, + [CLK_PLL1_D11] = &pll1_d11_223p4.common.cell, + [CLK_PLL1_D13] = &pll1_d13_189.common.cell, + [CLK_PLL1_D23] = &pll1_d23_106p8.common.cell, + [CLK_PLL1_D64] = &pll1_d64_38p4.common.cell, + [CLK_PLL1_D10_AUD] = &pll1_aud_245p7.common.cell, + [CLK_PLL1_D100_AUD] = &pll1_aud_24p5.common.cell, + [CLK_PLL2_D1] = &pll2_d1.common.cell, + [CLK_PLL2_D2] = &pll2_d2.common.cell, + [CLK_PLL2_D3] = &pll2_d3.common.cell, + [CLK_PLL2_D4] = &pll2_d4.common.cell, + [CLK_PLL2_D5] = &pll2_d5.common.cell, + [CLK_PLL2_D6] = &pll2_d6.common.cell, + [CLK_PLL2_D7] = &pll2_d7.common.cell, + [CLK_PLL2_D8] = &pll2_d8.common.cell, + [CLK_PLL3_D1] = &pll3_d1.common.cell, + [CLK_PLL3_D2] = &pll3_d2.common.cell, + [CLK_PLL3_D3] = &pll3_d3.common.cell, + [CLK_PLL3_D4] = &pll3_d4.common.cell, + [CLK_PLL3_D5] = &pll3_d5.common.cell, + [CLK_PLL3_D6] = &pll3_d6.common.cell, + [CLK_PLL3_D7] = &pll3_d7.common.cell, + [CLK_PLL3_D8] = &pll3_d8.common.cell, + [CLK_PLL3_80] = &pll3_80.common.cell, + [CLK_PLL3_40] = &pll3_40.common.cell, + [CLK_PLL3_20] = &pll3_20.common.cell, + [CLK_PLL1_307P2] = &pll1_d8_307p2.common.cell, + [CLK_PLL1_76P8] = &pll1_d32_76p8.common.cell, + [CLK_PLL1_61P44] = &pll1_d40_61p44.common.cell, + [CLK_PLL1_153P6] = &pll1_d16_153p6.common.cell, + [CLK_PLL1_102P4] = &pll1_d24_102p4.common.cell, + [CLK_PLL1_51P2] = &pll1_d48_51p2.common.cell, + [CLK_PLL1_51P2_AP] = &pll1_d48_51p2_ap.common.cell, + [CLK_PLL1_57P6] = &pll1_m3d128_57p6.common.cell, + [CLK_PLL1_25P6] = &pll1_d96_25p6.common.cell, + [CLK_PLL1_12P8] = &pll1_d192_12p8.common.cell, + [CLK_PLL1_12P8_WDT] = &pll1_d192_12p8_wdt.common.cell, + [CLK_PLL1_6P4] = &pll1_d384_6p4.common.cell, + [CLK_PLL1_3P2] = &pll1_d768_3p2.common.cell, + [CLK_PLL1_1P6] = &pll1_d1536_1p6.common.cell, + [CLK_PLL1_0P8] = &pll1_d3072_0p8.common.cell, + [CLK_PLL1_351] = &pll1_d7_351p08.common.cell, + [CLK_PLL1_409P6] = &pll1_d6_409p6.common.cell, + [CLK_PLL1_204P8] = &pll1_d12_204p8.common.cell, + [CLK_PLL1_491] = &pll1_d5_491p52.common.cell, + [CLK_PLL1_245P76] = &pll1_d10_245p76.common.cell, + [CLK_PLL1_614] = &pll1_d4_614p4.common.cell, + [CLK_PLL1_47P26] = &pll1_d52_47p26.common.cell, + [CLK_PLL1_31P5] = &pll1_d78_31p5.common.cell, + [CLK_PLL1_819] = &pll1_d3_819p2.common.cell, + [CLK_PLL1_1228] = &pll1_d2_1228p8.common.cell, + [CLK_SLOW_UART1] = &slow_uart1_14p74.common.cell, + [CLK_SLOW_UART2] = &slow_uart2_48.common.cell, + [CLK_UART1] = &uart1_clk.common.cell, + [CLK_UART2] = &uart2_clk.common.cell, + [CLK_UART3] = &uart3_clk.common.cell, + [CLK_UART4] = &uart4_clk.common.cell, + [CLK_UART5] = &uart5_clk.common.cell, + [CLK_UART6] = &uart6_clk.common.cell, + [CLK_UART7] = &uart7_clk.common.cell, + [CLK_UART8] = &uart8_clk.common.cell, + [CLK_UART9] = &uart9_clk.common.cell, + [CLK_GPIO] = &gpio_clk.common.cell, + [CLK_PWM0] = &pwm0_clk.common.cell, + [CLK_PWM1] = &pwm1_clk.common.cell, + [CLK_PWM2] = &pwm2_clk.common.cell, + [CLK_PWM3] = &pwm3_clk.common.cell, + [CLK_PWM4] = &pwm4_clk.common.cell, + [CLK_PWM5] = &pwm5_clk.common.cell, + [CLK_PWM6] = &pwm6_clk.common.cell, + [CLK_PWM7] = &pwm7_clk.common.cell, + [CLK_PWM8] = &pwm8_clk.common.cell, + [CLK_PWM9] = &pwm9_clk.common.cell, + [CLK_PWM10] = &pwm10_clk.common.cell, + [CLK_PWM11] = &pwm11_clk.common.cell, + [CLK_PWM12] = &pwm12_clk.common.cell, + [CLK_PWM13] = &pwm13_clk.common.cell, + [CLK_PWM14] = &pwm14_clk.common.cell, + [CLK_PWM15] = &pwm15_clk.common.cell, + [CLK_PWM16] = &pwm16_clk.common.cell, + [CLK_PWM17] = &pwm17_clk.common.cell, + [CLK_PWM18] = &pwm18_clk.common.cell, + [CLK_PWM19] = &pwm19_clk.common.cell, + [CLK_SSP3] = &ssp3_clk.common.cell, + [CLK_RTC] = &rtc_clk.common.cell, + [CLK_TWSI0] = &twsi0_clk.common.cell, + [CLK_TWSI1] = &twsi1_clk.common.cell, + [CLK_TWSI2] = &twsi2_clk.common.cell, + [CLK_TWSI4] = &twsi4_clk.common.cell, + [CLK_TWSI5] = &twsi5_clk.common.cell, + [CLK_TWSI6] = &twsi6_clk.common.cell, + [CLK_TWSI7] = &twsi7_clk.common.cell, + [CLK_TWSI8] = &twsi8_clk.common.cell, + [CLK_TIMERS1] = &timers1_clk.common.cell, + [CLK_TIMERS2] = &timers2_clk.common.cell, + [CLK_AIB] = &aib_clk.common.cell, + [CLK_ONEWIRE] = &onewire_clk.common.cell, + [CLK_SSPA0] = &sspa0_clk.common.cell, + [CLK_SSPA1] = &sspa1_clk.common.cell, + [CLK_DRO] = &dro_clk.common.cell, + [CLK_IR] = &ir_clk.common.cell, + [CLK_TSEN] = &tsen_clk.common.cell, + [CLK_IPC_AP2AUD] = &ipc_ap2aud_clk.common.cell, + [CLK_CAN0] = &can0_clk.common.cell, + [CLK_CAN0_BUS] = &can0_bus_clk.common.cell, + [CLK_WDT] = &wdt_clk.common.cell, + [CLK_RIPC] = &ripc_clk.common.cell, + [CLK_JPG] = &jpg_clk.common.cell, + [CLK_JPF_4KAFBC] = &jpg_4kafbc_clk.common.cell, + [CLK_JPF_2KAFBC] = &jpg_2kafbc_clk.common.cell, + [CLK_CCIC2PHY] = &ccic2phy_clk.common.cell, + [CLK_CCIC3PHY] = &ccic3phy_clk.common.cell, + [CLK_CSI] = &csi_clk.common.cell, + [CLK_CAMM0] = &camm0_clk.common.cell, + [CLK_CAMM1] = &camm1_clk.common.cell, + [CLK_CAMM2] = &camm2_clk.common.cell, + [CLK_ISP_CPP] = &isp_cpp_clk.common.cell, + [CLK_ISP_BUS] = &isp_bus_clk.common.cell, + [CLK_ISP] = &isp_clk.common.cell, + [CLK_DPU_MCLK] = &dpu_mclk.common.cell, + [CLK_DPU_ESC] = &dpu_esc_clk.common.cell, + [CLK_DPU_BIT] = &dpu_bit_clk.common.cell, + [CLK_DPU_PXCLK] = &dpu_pxclk.common.cell, + [CLK_DPU_HCLK] = &dpu_hclk.common.cell, + [CLK_DPU_SPI] = &dpu_spi_clk.common.cell, + [CLK_DPU_SPI_HBUS] = &dpu_spi_hbus_clk.common.cell, + [CLK_DPU_SPIBUS] = &dpu_spi_bus_clk.common.cell, + [CLK_SPU_SPI_ACLK] = &dpu_spi_aclk.common.cell, + [CLK_V2D] = &v2d_clk.common.cell, + [CLK_CCIC_4X] = &ccic_4x_clk.common.cell, + [CLK_CCIC1PHY] = &ccic1phy_clk.common.cell, + [CLK_SDH_AXI] = &sdh_axi_aclk.common.cell, + [CLK_SDH0] = &sdh0_clk.common.cell, + [CLK_SDH1] = &sdh1_clk.common.cell, + [CLK_SDH2] = &sdh2_clk.common.cell, + [CLK_USB_P1] = &usb_p1_aclk.common.cell, + [CLK_USB_AXI] = &usb_axi_clk.common.cell, + [CLK_USB30] = &usb30_clk.common.cell, + [CLK_QSPI] = &qspi_clk.common.cell, + [CLK_QSPI_BUS] = &qspi_bus_clk.common.cell, + [CLK_DMA] = &dma_clk.common.cell, + [CLK_AES] = &aes_clk.common.cell, + [CLK_VPU] = &vpu_clk.common.cell, + [CLK_GPU] = &gpu_clk.common.cell, + [CLK_EMMC] = &emmc_clk.common.cell, + [CLK_EMMC_X] = &emmc_x_clk.common.cell, + [CLK_AUDIO] = &audio_clk.common.cell, + [CLK_HDMI] = &hdmi_mclk.common.cell, + [CLK_CCI550] = &cci550_clk.common.cell, + [CLK_PMUA_ACLK] = &pmua_aclk.common.cell, + [CLK_CPU_C0_HI] = &cpu_c0_hi_clk.common.cell, + [CLK_CPU_C0_CORE] = &cpu_c0_core_clk.common.cell, + [CLK_CPU_C0_ACE] = &cpu_c0_ace_clk.common.cell, + [CLK_CPU_C0_TCM] = &cpu_c0_tcm_clk.common.cell, + [CLK_CPU_C1_HI] = &cpu_c1_hi_clk.common.cell, + [CLK_CPU_C1_CORE] = &cpu_c1_pclk.common.cell, + [CLK_CPU_C1_ACE] = &cpu_c1_ace_clk.common.cell, + [CLK_PCIE0] = &pcie0_clk.common.cell, + [CLK_PCIE1] = &pcie1_clk.common.cell, + [CLK_PCIE2] = &pcie2_clk.common.cell, + [CLK_EMAC0_BUS] = &emac0_bus_clk.common.cell, + [CLK_EMAC0_PTP] = &emac0_ptp_clk.common.cell, + [CLK_EMAC1_BUS] = &emac1_bus_clk.common.cell, + [CLK_EMAC1_PTP] = &emac1_ptp_clk.common.cell, + [CLK_SEC_UART1] = &uart1_sec_clk.common.cell, + [CLK_SEC_SSP2] = &ssp2_sec_clk.common.cell, + [CLK_SEC_TWSI3] = &twsi3_sec_clk.common.cell, + [CLK_SEC_RTC] = &rtc_sec_clk.common.cell, + [CLK_SEC_TIMERS0] = &timers0_sec_clk.common.cell, + [CLK_SEC_KPC] = &kpc_sec_clk.common.cell, + [CLK_SEC_GPIO] = &gpio_sec_clk.common.cell, + [CLK_APB] = &apb_clk.common.cell, + [CLK_SLOW_UART] = &slow_uart.common.cell, + [CLK_I2S_SYSCLK] = &i2s_sysclk.common.cell, + [CLK_I2S_BCLK] = &i2s_bclk.common.cell, + [CLK_RCPU_HDMIAUDIO] = &rhdmi_audio_clk.common.cell, + [CLK_RCPU_CAN] = &rcan_clk.common.cell, + [CLK_RCPU_CAN_BUS] = &rcan_bus_clk.common.cell, + [CLK_RCPU_I2C0] = &ri2c0_clk.common.cell, + [CLK_RCPU_SSP0] = &rssp0_clk.common.cell, + [CLK_RCPU_IR] = &rir_clk.common.cell, + [CLK_RCPU_UART0] = &ruart0_clk.common.cell, + [CLK_RCPU_UART1] = &ruart1_clk.common.cell, + [CLK_DPLL1] = &dpll1.common.cell, + [CLK_DPLL2] = &dpll2.common.cell, + [CLK_DFC_LVL0] = &dfc_lvl0.common.cell, + [CLK_DFC_LVL1] = &dfc_lvl1.common.cell, + [CLK_DFC_LVL2] = &dfc_lvl2.common.cell, + [CLK_DFC_LVL3] = &dfc_lvl3.common.cell, + [CLK_DFC_LVL4] = &dfc_lvl4.common.cell, + [CLK_DFC_LVL5] = &dfc_lvl5.common.cell, + [CLK_DFC_LVL6] = &dfc_lvl6.common.cell, + [CLK_DFC_LVL7] = &dfc_lvl7.common.cell, + [CLK_DDR] = &ddr.common.cell, + [CLK_RCPU2_PWM0] = &rpwm0_clk.common.cell, + [CLK_RCPU2_PWM1] = &rpwm1_clk.common.cell, + [CLK_RCPU2_PWM2] = &rpwm2_clk.common.cell, + [CLK_RCPU2_PWM3] = &rpwm3_clk.common.cell, + [CLK_RCPU2_PWM4] = &rpwm4_clk.common.cell, + [CLK_RCPU2_PWM5] = &rpwm5_clk.common.cell, + [CLK_RCPU2_PWM6] = &rpwm6_clk.common.cell, + [CLK_RCPU2_PWM7] = &rpwm7_clk.common.cell, + [CLK_RCPU2_PWM8] = &rpwm8_clk.common.cell, + [CLK_RCPU2_PWM9] = &rpwm9_clk.common.cell, + [CLK_AUDIO_APB] = &audio_apb_clk.common.cell, + [CLK_AUDIO_AXI] = &audio_axi_clk.common.cell, + [CLK_PM_MN_SRC] = &mn_src_clk.common.cell, + [CLK_PM_MN] = &mn_clk.common.cell, + [CLK_PM_MN2_SRC] = &mn2_src_clk.common.cell, + [CLK_PM_MN2] = &mn2_clk.common.cell, +}; + +static rt_err_t spacemit_k1x_ccu_probe(struct rt_platform_device *pdev) +{ + rt_err_t err; + int reg_count; + struct ccu_common *common; + struct rt_clk *clk; + struct rt_clk_cell *cell; + struct rt_device *dev = &pdev->parent; + struct k1x_clk *kclk = rt_calloc(1, sizeof(*kclk)); + + if (!kclk) + { + return -RT_ENOMEM; + } + + reg_count = rt_dm_dev_get_address_count(dev); + + for (int i = 0; i < reg_count; ++i) + { + kclk->mmio[i] = rt_dm_dev_iomap(dev, i); + + if (!kclk->mmio[i]) + { + err = -RT_EIO; + goto _fail; + } + } + + for (int i = 0; i < RT_ARRAY_SIZE(spacemit_k1x_ccu_cell); ++i) + { + if (!(cell = spacemit_k1x_ccu_cell[i])) + { + continue; + } + + common = cell_to_ccu_common(cell); + + common->lock = &k1_cru_lock; + + if (common->base_type < reg_count) + { + common->base = kclk->mmio[common->base_type]; + } + else + { + common->base = kclk->apbc_base; + } + + if (common->is_pll) + { + struct ccu_pll *pll = rt_container_of(common, struct ccu_pll, common); + + pll->pll.lock_base = kclk->mpmu_base; + } + } + + kclk->parent.dev = dev; + kclk->parent.cells = spacemit_k1x_ccu_cell; + kclk->parent.cells_nr = RT_ARRAY_SIZE(spacemit_k1x_ccu_cell); + + if ((err = rt_clk_register(&kclk->parent))) + { + goto _fail; + } + + /* Enable some clocks */ + for (int i = 0; i < RT_ARRAY_SIZE(bootup_enable_clk_table); ++i) + { + cell = spacemit_k1x_ccu_cell[bootup_enable_clk_table[i]]; + + if (cell && (clk = rt_clk_cell_get_clk(cell, RT_NULL))) + { + rt_clk_prepare_enable(clk); + } + } + + /* Init some clocks' rate */ + for (int i = 0; i < RT_ARRAY_SIZE(bootup_set_rate_clk_table); ++i) + { + cell = spacemit_k1x_ccu_cell[bootup_set_rate_clk_table[i++]]; + + if (cell && (clk = rt_clk_cell_get_clk(cell, RT_NULL))) + { + rt_clk_set_rate(clk, bootup_set_rate_clk_table[i]); + } + } + + return RT_EOK; + +_fail: + for (int i = 0; i < CRU_BASE_TYPE_AUDC; ++i) + { + if (kclk->mmio[i]) + { + rt_iounmap(kclk->mmio[i]); + } + } + + rt_free(kclk); + + return err; +} + +static const struct rt_ofw_node_id spacemit_k1x_ccu_ofw_ids[] = +{ + { .compatible = "spacemit,k1x-clock" }, + { /* sentinel */ } +}; + +static struct rt_platform_driver spacemit_k1x_ccu_driver = +{ + .name = "clk-spacemit-k1x", + .ids = spacemit_k1x_ccu_ofw_ids, + + .probe = spacemit_k1x_ccu_probe, +}; + +static int spacemit_k1x_ccu_register(void) +{ + rt_platform_driver_register(&spacemit_k1x_ccu_driver); + + return 0; +} +INIT_SUBSYS_EXPORT(spacemit_k1x_ccu_register); diff --git a/bsp/spacemit/dm/clk/ccu.h b/bsp/spacemit/dm/clk/ccu.h new file mode 100755 index 00000000000..6aba95c70eb --- /dev/null +++ b/bsp/spacemit/dm/clk/ccu.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2006-2024, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2024-5-1 GuEe-GUI first version + */ + +#ifndef __CCU_H__ +#define __CCU_H__ + +#include +#include + +#define CLK_DIVIDER_POWER_OF_TWO RT_BIT(1) +#define CLK_DIVIDER_ALLOW_ZERO RT_BIT(2) + +enum +{ + CLK_DIV_TYPE_1REG_NOFC_V1 = 0, + CLK_DIV_TYPE_1REG_FC_V2, + CLK_DIV_TYPE_2REG_NOFC_V3, + CLK_DIV_TYPE_2REG_FC_V4, + CLK_DIV_TYPE_1REG_FC_DIV_V5, + CLK_DIV_TYPE_1REG_FC_MUX_V6, +}; + +struct ccu_common +{ + void *base; + + rt_bool_t is_pll; + rt_uint32_t base_type; + + rt_uint32_t reg_type; + rt_uint32_t reg_ctrl; + rt_uint32_t reg_sel; + rt_uint32_t reg_xtc; + rt_uint32_t fc; + + struct rt_spinlock *lock; + + rt_ubase_t rate; + + struct rt_clk_cell cell; +}; + +#define cell_to_ccu_common(cell) rt_container_of(cell, struct ccu_common, cell); + +#endif /* __CCU_H__ */ diff --git a/bsp/spacemit/dm/clk/ccu_ddn.c b/bsp/spacemit/dm/clk/ccu_ddn.c new file mode 100755 index 00000000000..1ff0b0c38ba --- /dev/null +++ b/bsp/spacemit/dm/clk/ccu_ddn.c @@ -0,0 +1,198 @@ +/* + * Copyright (c) 2006-2024, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2024-5-1 GuEe-GUI first version + */ + +#include "ccu_ddn.h" + +#define DBG_TAG "clk.ccu.ddn" +#define DBG_LVL DBG_INFO +#include + +/* + * It is M/N clock + * + * Fout from synthesizer can be given from two equations: + * numerator/denominator = Fin / (Fout * factor) + */ + +static void ccu_ddn_disable(struct rt_clk_cell *cell) +{ + rt_uint32_t reg; + rt_ubase_t level; + struct ccu_ddn *ddn = cell_to_ccu_ddn(cell); + struct ccu_common *common = &ddn->common; + + if (!ddn->gate) + { + return; + } + + level = rt_spin_lock_irqsave(common->lock); + + reg = HWREG32(common->base + common->reg_sel); + HWREG32(common->base + common->reg_sel) = reg & ~ddn->gate; + + rt_spin_unlock_irqrestore(common->lock, level); +} + +static rt_err_t ccu_ddn_enable(struct rt_clk_cell *cell) +{ + rt_uint32_t reg; + rt_ubase_t level; + struct ccu_ddn *ddn = cell_to_ccu_ddn(cell); + struct ccu_common *common = &ddn->common; + + if (!ddn->gate) + { + return RT_EOK; + } + + level = rt_spin_lock_irqsave(common->lock); + + reg = HWREG32(common->base + common->reg_sel); + HWREG32(common->base + common->reg_sel) = reg | ddn->gate; + + rt_spin_unlock_irqrestore(common->lock, level); + + return RT_EOK; +} + +static rt_bool_t ccu_ddn_is_enabled(struct rt_clk_cell *cell) +{ + struct ccu_ddn *ddn = cell_to_ccu_ddn(cell); + struct ccu_common *common = &ddn->common; + + if (!ddn->gate) + { + return RT_TRUE; + } + + return HWREG32(common->base + common->reg_sel) & ddn->gate; +} + +static rt_ubase_t ccu_ddn_recalc_rate(struct rt_clk_cell *cell, rt_ubase_t parent_rate) +{ + rt_ubase_t rate; + rt_uint32_t val, num, den; + struct ccu_ddn *ddn = cell_to_ccu_ddn(cell); + struct ccu_ddn_config *params = &ddn->ddn; + + val = HWREG32(ddn->common.base + ddn->common.reg_ctrl); + + /* Calculate numerator */ + num = (val >> params->info->num_shift) & params->info->num_mask; + + /* Calculate denominator */ + den = (val >> params->info->den_shift) & params->info->den_mask; + + if (!den) + { + return 0; + } + + rate = (((parent_rate / 10000) * den) / (num * params->info->factor)) * 10000; + + return rate; +} + +static rt_base_t ccu_ddn_round_rate(struct rt_clk_cell *cell, rt_ubase_t drate, rt_ubase_t *prate) +{ + int i; + rt_ubase_t result, rate = 0, prev_rate; + struct ccu_ddn *ddn = cell_to_ccu_ddn(cell); + struct ccu_ddn_config *params = &ddn->ddn; + + for (i = 0; i < params->tbl_size; ++i) + { + prev_rate = rate; + rate = (((*prate / 10000) * params->tbl[i].den) / + (params->tbl[i].num * params->info->factor)) * 10000; + + if (rate > drate) + { + break; + } + } + + if (i == 0 || i == params->tbl_size) + { + result = rate; + } + else + { + if (drate - prev_rate > rate - drate) + { + result = rate; + } + else + { + result = prev_rate; + } + } + + return result; +} + +/* Configures new clock rate */ +static rt_err_t ccu_ddn_set_rate(struct rt_clk_cell *cell, rt_ubase_t drate, rt_ubase_t prate) +{ + int i; + rt_ubase_t val, rate = 0, level; + struct ccu_ddn *ddn = cell_to_ccu_ddn(cell); + struct ccu_common *common = &ddn->common; + struct ccu_ddn_config *params = &ddn->ddn; + + for (i = 0; i < params->tbl_size; ++i) + { + rate = (((prate / 10000) * params->tbl[i].den) / + (params->tbl[i].num * params->info->factor)) * 10000; + + if (rate > drate) + { + break; + } + } + + if (i > 0) + { + --i; + } + + if (ddn->common.lock) + { + level = rt_spin_lock_irqsave(common->lock); + } + + val = HWREG32(ddn->common.base + ddn->common.reg_ctrl); + + val &= ~(params->info->num_mask << params->info->num_shift); + val |= (params->tbl[i].num & params->info->num_mask) << params->info->num_shift; + + val &= ~(params->info->den_mask << params->info->den_shift); + val |= (params->tbl[i].den & params->info->den_mask) << params->info->den_shift; + + HWREG32(ddn->common.base + ddn->common.reg_ctrl) = val; + + if (ddn->common.lock) + { + rt_spin_unlock_irqrestore(common->lock, level); + } + + return RT_EOK; +} + +const struct rt_clk_ops ccu_ddn_ops = +{ + .disable = ccu_ddn_disable, + .enable = ccu_ddn_enable, + .is_enabled = ccu_ddn_is_enabled, + .recalc_rate = ccu_ddn_recalc_rate, + .round_rate = ccu_ddn_round_rate, + .set_rate = ccu_ddn_set_rate, +}; diff --git a/bsp/spacemit/dm/clk/ccu_ddn.h b/bsp/spacemit/dm/clk/ccu_ddn.h new file mode 100755 index 00000000000..c0f5a7f755b --- /dev/null +++ b/bsp/spacemit/dm/clk/ccu_ddn.h @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2006-2024, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2024-5-1 GuEe-GUI first version + */ + +#ifndef __CCU_DDN_H__ +#define __CCU_DDN_H__ + +#include "ccu.h" + +struct ccu_ddn_tbl +{ + rt_uint32_t num; + rt_uint32_t den; +}; + +struct ccu_ddn_info +{ + rt_uint32_t factor; + rt_uint32_t num_mask; + rt_uint32_t den_mask; + rt_uint32_t num_shift; + rt_uint32_t den_shift; +}; + +struct ccu_ddn_config +{ + rt_uint32_t tbl_size; + struct ccu_ddn_tbl *tbl; + struct ccu_ddn_info *info; +}; + +struct ccu_ddn +{ + struct ccu_common common; + struct ccu_ddn_config ddn; + rt_uint32_t gate; +}; + +#define PLL_DDN_TBL(_num, _den) \ +{ \ + .num = (_num), \ + .den = (_den), \ +} + +#define _SPACEMIT_CCU_DDN_CONFIG(_info, _table, _size) \ +{ \ + .info = (struct ccu_ddn_info *)_info, \ + .tbl = (struct ccu_ddn_tbl *)_table, \ + .tbl_size = _size, \ +} + +#define SPACEMIT_CCU_DDN(_struct, _name, _parent, _info, _table, \ + _size, _base_type, _reg_ctrl, _flags) \ +struct ccu_ddn _struct = \ +{ \ + .ddn = _SPACEMIT_CCU_DDN_CONFIG(_info, _table, _size), \ + .common = \ + { \ + .reg_ctrl = _reg_ctrl, \ + .base_type = _base_type, \ + .cell.name = _name, \ + .cell.parent_name = _parent, \ + .cell.parents_nr = 1, \ + .cell.ops = &ccu_ddn_ops, \ + .cell.flags = _flags, \ + }, \ +} + +#define SPACEMIT_CCU_DDN_GATE(_struct, _name, _parent, _info, _table, _size, \ + _base_type, _reg_ddn, _reg_gate, _gate_mask, _flags) \ +struct ccu_ddn _struct = \ +{ \ + .gate = _gate_mask, \ + .ddn = _SPACEMIT_CCU_DDN_CONFIG(_info, _table, _size), \ + .common = \ + { \ + .reg_ctrl = _reg_ddn, \ + .reg_sel = _reg_gate, \ + .base_type = _base_type, \ + .cell.name = _name, \ + .cell.parent_name = _parent, \ + .cell.parents_nr = 1, \ + .cell.ops = &ccu_ddn_ops, \ + .cell.flags = _flags, \ + } \ +} + +extern const struct rt_clk_ops ccu_ddn_ops; + +rt_inline struct ccu_ddn *cell_to_ccu_ddn(struct rt_clk_cell *cell) +{ + struct ccu_common *common = cell_to_ccu_common(cell); + + return rt_container_of(common, struct ccu_ddn, common); +} + +#endif /* __CCU_DDN_H__ */ diff --git a/bsp/spacemit/dm/clk/ccu_ddr.c b/bsp/spacemit/dm/clk/ccu_ddr.c new file mode 100755 index 00000000000..4401fa5eb0f --- /dev/null +++ b/bsp/spacemit/dm/clk/ccu_ddr.c @@ -0,0 +1,298 @@ +/* + * Copyright (c) 2006-2024, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2024-5-1 GuEe-GUI first version + */ + +#include "ccu_ddr.h" + +#define DBG_TAG "clk.ccu.ddr" +#define DBG_LVL DBG_INFO +#include + +#define PMU_AP_IMR 0x098 +#define AP_DCLK_FC_DONE_INT_MSK RT_BIT(15) +#define DCLK_FC_DONE_INT_MSK RT_BIT(4) + +#define PMU_AP_ISR 0x0a0 +#define AP_DCLK_FC_DONE_INT_STS RT_BIT(15) +#define DCLK_FC_DONE_INT_STS RT_BIT(4) +#define AP_FC_STS RT_BIT(1) + +#define DFC_AP 0x180 +#define DFC_FREQ_LV 0x1 +#define DFC_REQ RT_BIT(0) + +#define DFC_STATUS 0x188 +#define DFC_CAUSE_SHIFT 0x7 +#define DFC_STS RT_BIT(0) + +/* Enable/disable ddr frequency change done interrupt */ +static void ccu_ddr_enable_dfc_int(struct ccu_common *common, rt_bool_t enable) +{ + rt_uint32_t val; + rt_ubase_t level; + + level = rt_spin_lock_irqsave(common->lock); + + val = HWREG32(common->base + PMU_AP_IMR); + if (enable) + { + val |= AP_DCLK_FC_DONE_INT_MSK; + } + else + { + val &= ~AP_DCLK_FC_DONE_INT_MSK; + } + HWREG32(common->base + PMU_AP_IMR) = val; + + rt_spin_unlock_irqrestore(common->lock, level); +} + +/* clear ddr frequency change done interrupt status*/ +static void ccu_ddr_clear_dfc_int_status(struct ccu_common *common) +{ + rt_uint32_t val; + rt_ubase_t level; + + level = rt_spin_lock_irqsave(common->lock); + + val = HWREG32(common->base + PMU_AP_ISR); + val &= ~(AP_DCLK_FC_DONE_INT_STS | AP_FC_STS); + HWREG32(common->base + PMU_AP_ISR) = val; + + rt_spin_unlock_irqrestore(common->lock, level); +} + +static rt_err_t ccu_ddr_wait_freq_change_done(struct ccu_common *common) +{ + int timeout = 100; + + while (--timeout) + { + rt_hw_us_delay(10); + + if (HWREG32(common->base + PMU_AP_ISR) & AP_DCLK_FC_DONE_INT_STS) + { + break; + } + } + + if (!timeout) + { + LOG_E("%s:%s timeout, can not wait dfc done interrupt", __func__, common->cell.name); + return -RT_EBUSY; + } + + return RT_EOK; +} + +static rt_err_t ccu_ddr_freq_chg(struct ccu_common *common, + struct ccu_mux_config *mux, rt_uint8_t level_no) +{ + rt_ubase_t level; + rt_uint32_t reg, timeout; + + if (level_no > MAX_FREQ_LV) + { + LOG_E("%s:%s invalid %d freq level", __func__, common->cell.name, level_no); + return -RT_EINVAL; + } + + /* Check if dfc in progress */ + timeout = 1000; + while (--timeout) + { + if (!(HWREG32(common->base + DFC_STATUS) & DFC_STS)) + { + break; + } + rt_hw_us_delay(10); + } + + if (!timeout) + { + LOG_E("%s:%s another dfc is in pregress. status: %#x", + __func__, common->cell.name, HWREG32(common->base + DFC_STATUS)); + return -RT_EBUSY; + } + + level = rt_spin_lock_irqsave(common->lock); + + reg = HWREG32(common->base + common->reg_sel); + reg &= ~RT_GENMASK(mux->width + mux->shift - 1, mux->shift); + HWREG32(common->base + common->reg_sel) = reg | (level_no << mux->shift) | common->fc; + + rt_spin_unlock_irqrestore(common->lock, level); + + timeout = 1000; + while (--timeout) + { + rt_hw_us_delay(10); + + if (!(HWREG32(common->base + DFC_STATUS) & DFC_STS)) + { + break; + } + } + + if (!timeout) + { + LOG_E("%s dfc error! status: %#x", common->cell.name, HWREG32(common->base + DFC_STATUS)); + return -RT_EBUSY; + } + + return RT_EOK; +} + +static rt_ubase_t ccu_ddr_calc_best_rate(struct rt_clk_cell *cell, + rt_ubase_t rate, rt_uint32_t *mux_val) +{ + rt_ubase_t parent_rate = 0, best_rate = 0; + struct rt_clk_cell *parent; + + for (int i = 0; i < cell->parents_nr; ++i) + { + parent = rt_clk_cell_get_parent_by_index(cell, i); + + if (!parent) + { + continue; + } + + parent_rate = rt_clk_cell_get_rate(parent); + + if (rt_abs(parent_rate - rate) < rt_abs(best_rate - rate)) + { + best_rate = parent_rate; + *mux_val = i; + } + } + + return best_rate; +} + +static rt_ubase_t ccu_ddr_recalc_rate(struct rt_clk_cell *cell, rt_ubase_t parent_rate) +{ + return parent_rate; +} + +static rt_base_t ccu_ddr_round_rate(struct rt_clk_cell *cell, rt_ubase_t drate, rt_ubase_t *prate) +{ + return drate; +} + +static rt_err_t ccu_ddr_set_rate(struct rt_clk_cell *cell, rt_ubase_t rate, rt_ubase_t parent_rate) +{ + rt_uint32_t cur_mux, mux_val = 0, reg = 0; + struct ccu_ddr *ddr = cell_to_ccu_ddr(cell); + struct ccu_common *common = &ddr->common; + struct ccu_mux_config *mux = ddr->mux ? : RT_NULL; + + if (!mux) + { + return RT_EOK; + } + + ccu_ddr_calc_best_rate(cell, rate, &mux_val); + + reg = HWREG32(common->base + common->reg_sel); + + if (mux) + { + cur_mux = reg >> mux->shift; + cur_mux &= (1 << mux->width) - 1; + + if (cur_mux != mux_val) + { + rt_clk_cell_set_parent(cell, rt_clk_cell_get_parent_by_index(cell, mux_val)); + } + } + + return RT_EOK; +} + +static rt_err_t ccu_ddr_set_parent(struct rt_clk_cell *cell, rt_uint8_t idx) +{ + rt_err_t err; + struct ccu_ddr *ddr = cell_to_ccu_ddr(cell); + struct ccu_common *common = &ddr->common; + struct ccu_mux_config *mux = ddr->mux; + + if (!mux) + { + return RT_EOK; + } + + if (mux->table) + { + idx = mux->table[idx]; + } + + /* Request change begin */ + ccu_ddr_enable_dfc_int(common, RT_TRUE); + + /* Change parent*/ + if ((err = ccu_ddr_freq_chg(common, mux, idx))) + { + LOG_E("%s:%s ddr_freq_chg fail", __func__, common->cell.name); + return err; + } + + /* Wait for frequency change done */ + if ((err = ccu_ddr_wait_freq_change_done(common))) + { + LOG_E("%s: %s timeout", __func__, "wait_freq_change_done"); + return err; + } + + ccu_ddr_clear_dfc_int_status(common); + ccu_ddr_enable_dfc_int(common, RT_FALSE); + + return RT_EOK; +} + +static rt_uint8_t ccu_ddr_get_parent(struct rt_clk_cell *cell) +{ + rt_uint32_t reg; + rt_uint8_t parent; + struct ccu_ddr *ddr = cell_to_ccu_ddr(cell); + struct ccu_common *common = &ddr->common; + struct ccu_mux_config *mux = ddr->mux; + + if (!mux) + { + return 0; + } + + reg = HWREG32(common->base + common->reg_sel); + + parent = reg >> mux->shift; + parent &= (1 << mux->width) - 1; + + if (mux->table) + { + for (int i = 0; i < common->cell.parents_nr; ++i) + { + if (mux->table[i] == parent) + { + return i; + } + } + } + + return parent; +} + +const struct rt_clk_ops ccu_ddr_ops = +{ + .recalc_rate = ccu_ddr_recalc_rate, + .round_rate = ccu_ddr_round_rate, + .set_rate = ccu_ddr_set_rate, + .set_parent = ccu_ddr_set_parent, + .get_parent = ccu_ddr_get_parent, +}; diff --git a/bsp/spacemit/dm/clk/ccu_ddr.h b/bsp/spacemit/dm/clk/ccu_ddr.h new file mode 100755 index 00000000000..f11dce6de44 --- /dev/null +++ b/bsp/spacemit/dm/clk/ccu_ddr.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2006-2024, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2024-5-1 GuEe-GUI first version + */ + +#ifndef __CCU_DDR_H__ +#define __CCU_DDR_H__ + +#include "ccu.h" +#include "ccu_mix.h" + +struct ccu_ddr +{ + struct ccu_common common; + struct ccu_mux_config *mux; +}; + +#define MAX_FREQ_LV 7 /* level 0~7 */ + +#define SPACEMIT_CCU_DDR_FC(_struct, _name, _parents, _base_type, \ + _reg, _fc, _shift, _width, _flags) \ +struct ccu_ddr _struct = \ +{ \ + .mux = CCU_MUX_INIT(_shift, _width, RT_NULL), \ + .common = \ + { \ + .reg_sel = _reg, \ + .fc = _fc, \ + .base_type = _base_type, \ + .cell.name = _name, \ + .cell.parent_names = _parents, \ + .cell.parents_nr = RT_ARRAY_SIZE(_parents), \ + .cell.ops = &ccu_ddr_ops, \ + .cell.flags = _flags, \ + }, \ +} + +extern const struct rt_clk_ops ccu_ddr_ops; + +rt_inline struct ccu_ddr *cell_to_ccu_ddr(struct rt_clk_cell *cell) +{ + struct ccu_common *common = cell_to_ccu_common(cell); + + return rt_container_of(common, struct ccu_ddr, common); +} + +#endif /* __CCU_DDR_H__ */ diff --git a/bsp/spacemit/dm/clk/ccu_dpll.c b/bsp/spacemit/dm/clk/ccu_dpll.c new file mode 100755 index 00000000000..80cc0ba7406 --- /dev/null +++ b/bsp/spacemit/dm/clk/ccu_dpll.c @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2006-2024, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2024-5-1 GuEe-GUI first version + */ + +#include "ccu_dpll.h" + +#define DBG_TAG "clk.ccu.dpll" +#define DBG_LVL DBG_INFO +#include + +#define DPLL_MIN_FREQ 1700000000 +#define DPLL_MAX_FREQ 3400000000 + +#define pll_readl(reg) HWREG32(reg) +#define pll_readl_pll_swcr1(p) pll_readl(p.base + p.reg_ctrl) +#define pll_readl_pll_swcr2(p) pll_readl(p.base + p.reg_sel) + +#define pll_writel(val, reg) HWREG32(reg) = val +#define pll_writel_pll_swcr1(val, p) pll_writel(val, p.base + p.reg_ctrl) +#define pll_writel_pll_swcr2(val, p) pll_writel(val, p.base + p.reg_sel) + +/* Unified dpllx_swcr1 for dpll1~2 */ +union dpllx_swcr1 +{ + struct + { + rt_uint32_t reg0:8; + rt_uint32_t reg1:8; + rt_uint32_t reg2:8; + rt_uint32_t reg3:8; + } b; + rt_uint32_t v; +}; + +/* Unified dpllx_swcr2 for dpll1~2 */ +union dpllx_swcr2 +{ + struct + { + rt_uint32_t reg4:8; + rt_uint32_t reg5:8; + rt_uint32_t reg6:8; + rt_uint32_t reg7:8; + } b; + rt_uint32_t v; +}; + +/* Frequency unit Mhz, return pll vco freq */ +static rt_ubase_t __get_vco_freq_raw(struct rt_clk_cell *cell) +{ + rt_uint32_t reg0, reg1, reg2, reg3, reg4, reg5, reg6, reg7, size; + union dpllx_swcr1 swcr1; + union dpllx_swcr2 swcr2; + struct ccu_dpll_rate_tbl *freq_pll_regs_table; + struct ccu_dpll *p = cell_to_ccu_dpll(cell); + + swcr1.v = pll_readl_pll_swcr1(p->common); + swcr2.v = pll_readl_pll_swcr2(p->common); + + reg0 = swcr1.b.reg0; + reg1 = swcr1.b.reg1; + reg2 = swcr1.b.reg2; + reg3 = swcr1.b.reg3; + reg4 = swcr2.b.reg4; + reg5 = swcr2.b.reg5; + reg6 = swcr2.b.reg6; + reg7 = swcr2.b.reg7; + + freq_pll_regs_table = p->dpll.rate_tbl; + size = p->dpll.tbl_size; + + for (int i = 0; i < size; ++i) + { + struct ccu_dpll_rate_tbl *item = &freq_pll_regs_table[i]; + + if (item->reg0 == reg0 && + item->reg1 == reg1 && + item->reg2 == reg2 && + item->reg3 == reg3 && + item->reg4 == reg4 && + item->reg5 == reg5 && + item->reg6 == reg6 && + item->reg7 == reg7) + { + return item->rate; + } + } + + LOG_E("Unknown rate for clock %s", cell->name); + + return 0; +} + +static rt_ubase_t ccu_dpll_recalc_rate(struct rt_clk_cell *cell, rt_ubase_t parent_rate) +{ + return __get_vco_freq_raw(cell); +} + +static rt_base_t ccu_dpll_round_rate(struct rt_clk_cell *cell, rt_ubase_t rate, rt_ubase_t *prate) +{ + rt_ubase_t max_rate = 0; + struct ccu_dpll *p = cell_to_ccu_dpll(cell); + struct ccu_dpll_config *params = &p->dpll; + + if (rate > DPLL_MAX_FREQ || rate < DPLL_MIN_FREQ) + { + LOG_E("%s %lu rate out of range", cell->name, rate); + return -RT_EINVAL; + } + + if (params->rate_tbl) + { + for (int i = 0; i < params->tbl_size; ++i) + { + struct ccu_dpll_rate_tbl *item = ¶ms->rate_tbl[i]; + + if (item->rate <= rate) + { + if (max_rate < item->rate) + { + max_rate = item->rate; + } + } + } + } + else + { + LOG_E("%s don't find freq table for pll", cell->name); + } + + return max_rate; +} + +const struct rt_clk_ops ccu_dpll_ops = +{ + .recalc_rate = ccu_dpll_recalc_rate, + .round_rate = ccu_dpll_round_rate, +}; diff --git a/bsp/spacemit/dm/clk/ccu_dpll.h b/bsp/spacemit/dm/clk/ccu_dpll.h new file mode 100755 index 00000000000..7fc6014e588 --- /dev/null +++ b/bsp/spacemit/dm/clk/ccu_dpll.h @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2006-2024, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2024-5-1 GuEe-GUI first version + */ + +#ifndef __CCU_DPLL_H__ +#define __CCU_DPLL_H__ + +#include "ccu.h" + +struct ccu_dpll_rate_tbl +{ + rt_uint64_t rate; + rt_uint32_t reg0; + rt_uint32_t reg1; + rt_uint32_t reg2; + rt_uint32_t reg3; + rt_uint32_t reg4; + rt_uint32_t reg5; + rt_uint32_t reg6; + rt_uint32_t reg7; +}; + +struct ccu_dpll_config +{ + rt_uint32_t tbl_size; + struct ccu_dpll_rate_tbl *rate_tbl; +}; + +struct ccu_dpll +{ + struct ccu_common common; + struct ccu_dpll_config dpll; +}; + +#define DPLL_RATE(_rate, _reg0, _reg1, _reg2, _reg3, _reg4, _reg5, _reg6, _reg7) \ +{ \ + .rate = (_rate), \ + .reg0 = (_reg0), \ + .reg1 = (_reg1), \ + .reg2 = (_reg2), \ + .reg3 = (_reg3), \ + .reg4 = (_reg4), \ + .reg5 = (_reg5), \ + .reg6 = (_reg6), \ + .reg7 = (_reg7), \ +} + +#define _SPACEMIT_CCU_DPLL_CONFIG(_table, _size) \ +{ \ + .rate_tbl = (struct ccu_dpll_rate_tbl *)_table, \ + .tbl_size = _size, \ +} + +#define SPACEMIT_CCU_DPLL(_struct, _name, _table, _size, \ + _base_type, _reg_ctrl, _reg_sel, _is_pll, _flags) \ +struct ccu_dpll _struct = \ +{ \ + .dpll = _SPACEMIT_CCU_DPLL_CONFIG(_table, _size), \ + .common = \ + { \ + .reg_ctrl = _reg_ctrl, \ + .reg_sel = _reg_sel, \ + .base_type = _base_type, \ + .is_pll = RT_FALSE, \ + .cell.name = _name, \ + .cell.ops = &ccu_dpll_ops, \ + .cell.flags = _flags, \ + }, \ +} + +extern const struct rt_clk_ops ccu_dpll_ops; + +rt_inline struct ccu_dpll *cell_to_ccu_dpll(struct rt_clk_cell *cell) +{ + struct ccu_common *common = cell_to_ccu_common(cell); + + return rt_container_of(common, struct ccu_dpll, common); +} + +#endif /* __CCU_DPLL_H__ */ diff --git a/bsp/spacemit/dm/clk/ccu_mix.c b/bsp/spacemit/dm/clk/ccu_mix.c new file mode 100755 index 00000000000..9cc6a85511e --- /dev/null +++ b/bsp/spacemit/dm/clk/ccu_mix.c @@ -0,0 +1,712 @@ +/* + * Copyright (c) 2006-2024, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2024-5-1 GuEe-GUI first version + */ + +#include "ccu_mix.h" + +#define DBG_TAG "clk.ccu.mix" +#define DBG_LVL DBG_INFO +#include + +#define TIMEOUT_LIMIT 20000 /* Max timeout 10000us */ + +static int twsi8_reg_val = 0x04; +static const char *tswi8_clk_name = "twsi8_clk"; + +static void ccu_mix_disable(struct rt_clk_cell *cell) +{ + rt_uint32_t tmp; + rt_ubase_t level, rate; + struct ccu_mix *mix = cell_to_ccu_mix(cell); + struct ccu_common *common = &mix->common; + struct ccu_gate_config *gate = mix->gate; + + if (!gate) + { + return; + } + + if (!rt_strcmp(cell->name, tswi8_clk_name)) + { + twsi8_reg_val &= ~gate->gate_mask; + twsi8_reg_val |= gate->val_disable; + tmp = twsi8_reg_val; + + if (common->reg_type == CLK_DIV_TYPE_2REG_NOFC_V3 || + common->reg_type == CLK_DIV_TYPE_2REG_FC_V4) + { + HWREG32(common->base + common->reg_sel) = tmp; + } + else + { + HWREG32(common->base + common->reg_ctrl) = tmp; + } + + return; + } + + if (common->lock) + { + level = rt_spin_lock_irqsave(common->lock); + } + + if (common->reg_type == CLK_DIV_TYPE_2REG_NOFC_V3 || + common->reg_type == CLK_DIV_TYPE_2REG_FC_V4) + { + tmp = HWREG32(common->base + common->reg_sel); + } + else + { + tmp = HWREG32(common->base + common->reg_ctrl); + } + + tmp &= ~gate->gate_mask; + tmp |= gate->val_disable; + + if (common->reg_type == CLK_DIV_TYPE_2REG_NOFC_V3 || + common->reg_type == CLK_DIV_TYPE_2REG_FC_V4) + { + HWREG32(common->base + common->reg_sel) = tmp; + } + else + { + HWREG32(common->base + common->reg_ctrl) = tmp; + } + + if (common->lock) + { + rt_spin_unlock_irqrestore(common->lock, level); + } + + if (gate->flags & SPACEMIT_CLK_GATE_NEED_DELAY) + { + rate = rt_clk_cell_get_rate(cell); + + if (rate == 0) + { + LOG_E("%s clock rate is 0", cell->name); + } + else + { + /* Need delay 2M cycles. */ + rt_hw_us_delay(RT_DIV_ROUND_UP(2000000, rate)); + } + } +} + +static rt_err_t ccu_mix_enable(struct rt_clk_cell *cell) +{ + rt_ubase_t level, rate; + rt_uint32_t tmp, val = 0, timeout_power = 1; + struct ccu_mix *mix = cell_to_ccu_mix(cell); + struct ccu_common *common = &mix->common; + struct ccu_gate_config *gate = mix->gate; + + if (!gate) + { + return RT_EOK; + } + + if (!rt_strcmp(cell->name, tswi8_clk_name)) + { + twsi8_reg_val &= ~gate->gate_mask; + twsi8_reg_val |= gate->val_enable; + tmp = twsi8_reg_val; + + if (common->reg_type == CLK_DIV_TYPE_2REG_NOFC_V3 || + common->reg_type == CLK_DIV_TYPE_2REG_FC_V4) + { + HWREG32(common->base + common->reg_sel) = tmp; + } + else + { + HWREG32(common->base + common->reg_ctrl) = tmp; + } + + return RT_EOK; + } + + if (common->lock) + { + level = rt_spin_lock_irqsave(common->lock); + } + + if (common->reg_type == CLK_DIV_TYPE_2REG_NOFC_V3 || + common->reg_type == CLK_DIV_TYPE_2REG_FC_V4) + { + tmp = HWREG32(common->base + common->reg_sel); + } + else + { + tmp = HWREG32(common->base + common->reg_ctrl); + } + + tmp &= ~gate->gate_mask; + tmp |= gate->val_enable; + + if (common->reg_type == CLK_DIV_TYPE_2REG_NOFC_V3 || + common->reg_type == CLK_DIV_TYPE_2REG_FC_V4) + { + HWREG32(common->base + common->reg_sel) = tmp; + } + else + { + HWREG32(common->base + common->reg_ctrl) = tmp; + } + + if (common->reg_type == CLK_DIV_TYPE_2REG_NOFC_V3 || + common->reg_type == CLK_DIV_TYPE_2REG_FC_V4) + { + val = HWREG32(common->base + common->reg_sel); + } + else + { + val = HWREG32(common->base + common->reg_ctrl); + } + + if (common->lock) + { + rt_spin_unlock_irqrestore(common->lock, level); + } + + while ((val & gate->gate_mask) != gate->val_enable && timeout_power < TIMEOUT_LIMIT) + { + rt_hw_us_delay(timeout_power); + + if (common->reg_type == CLK_DIV_TYPE_2REG_NOFC_V3 || + common->reg_type == CLK_DIV_TYPE_2REG_FC_V4) + { + val = HWREG32(common->base + common->reg_sel); + } + else + { + val = HWREG32(common->base + common->reg_ctrl); + } + + timeout_power *= 10; + } + + if (timeout_power > 1) + { + if (val == tmp) + { + LOG_E("%s write clk_gate timeout occur, read pass after %d us delay", + cell->name, timeout_power); + } + else + { + LOG_E("%s write clk_gate timeout after %d us", cell->name, timeout_power); + } + } + + if (gate->flags & SPACEMIT_CLK_GATE_NEED_DELAY) + { + rate = rt_clk_cell_get_rate(cell); + + if (rate == 0) + { + LOG_E("%s clock rate is 0", cell->name); + } + else + { + /* Need delay 2M cycles. */ + rt_hw_us_delay(RT_DIV_ROUND_UP(2000000, rate)); + } + } + + return RT_EOK; +} + +static rt_bool_t ccu_mix_is_enabled(struct rt_clk_cell *cell) +{ + rt_uint32_t tmp; + rt_ubase_t level; + struct ccu_mix *mix = cell_to_ccu_mix(cell); + struct ccu_common *common = &mix->common; + struct ccu_gate_config *gate = mix->gate; + + if (!gate) + { + return RT_TRUE; + } + + if (!rt_strcmp(cell->name, tswi8_clk_name)) + { + return (twsi8_reg_val & gate->gate_mask) == gate->val_enable; + } + + if (common->lock) + { + level = rt_spin_lock_irqsave(common->lock); + } + + if (common->reg_type == CLK_DIV_TYPE_2REG_NOFC_V3 || + common->reg_type == CLK_DIV_TYPE_2REG_FC_V4) + { + tmp = HWREG32(common->base + common->reg_sel); + } + else + { + tmp = HWREG32(common->base + common->reg_ctrl); + } + + if (common->lock) + { + rt_spin_unlock_irqrestore(common->lock, level); + } + + return (tmp & gate->gate_mask) == gate->val_enable; +} + +static rt_ubase_t ccu_mix_recalc_rate(struct rt_clk_cell *cell, rt_ubase_t parent_rate) +{ + rt_ubase_t val; + rt_uint32_t reg, div_val = 0; + struct ccu_mix *mix = cell_to_ccu_mix(cell); + struct ccu_common *common = &mix->common; + struct ccu_div_config *div = mix->div; + struct ccu_mux_config *mux = mix->mux ? mix->mux : RT_NULL; + + if (!div) + { + if (mux && common->rate && common->rate != parent_rate) + { + parent_rate = common->rate; + } + + if (mix->factor) + { + val = parent_rate * mix->factor->mul / mix->factor->div; + } + else + { + val = parent_rate; + } + + goto _end; + } + + if (common->reg_type == CLK_DIV_TYPE_2REG_NOFC_V3 || + common->reg_type == CLK_DIV_TYPE_2REG_FC_V4) + { + reg = HWREG32(common->base + common->reg_sel); + } + else + { + reg = HWREG32(common->base + common->reg_ctrl); + } + + val = reg >> div->shift; + val &= (1 << div->width) - 1; + + if (div->clk_flags & CLK_DIVIDER_POWER_OF_TWO) + { + div_val = 1 << val; + } + else if (div->table) + { + const struct ccu_div_table *clkt; + + for (clkt = div->table; clkt->div; ++clkt) + { + if (clkt->val == val) + { + div_val = clkt->div; + break; + } + } + } + else + { + div_val = val + 1; + } + + if (!div_val) + { + val = parent_rate; + } + else + { + val = RT_DIV_ROUND_UP_ULL((rt_uint64_t)parent_rate, div_val); + } + +_end: + common->rate = val; + + return val; +} + +static rt_base_t ccu_mix_round_rate(struct rt_clk_cell *cell, rt_ubase_t drate, rt_ubase_t *prate) +{ + return drate; +} + +static rt_err_t ccu_mix_trigger_fc(struct rt_clk_cell *cell) +{ + rt_ubase_t val = 0; + rt_err_t err = RT_EOK; + rt_uint32_t timeout = 50; + struct ccu_mix *mix = cell_to_ccu_mix(cell); + struct ccu_common *common = &mix->common; + + if (common->reg_type == CLK_DIV_TYPE_1REG_FC_V2 || + common->reg_type == CLK_DIV_TYPE_2REG_FC_V4 || + common->reg_type == CLK_DIV_TYPE_1REG_FC_DIV_V5 || + common->reg_type == CLK_DIV_TYPE_1REG_FC_MUX_V6) + { + timeout = 50; + val = HWREG32(common->base + common->reg_ctrl); + val |= common->fc; + HWREG32(common->base + common->reg_ctrl) = val; + + do { + val = HWREG32(common->base + common->reg_ctrl); + --timeout; + + if (!(val & common->fc)) + { + break; + } + } while (timeout); + + if (timeout == 0) + { + timeout = 5000; + + do { + val = HWREG32(common->base + common->reg_ctrl); + --timeout; + + if (!(val & common->fc)) + { + break; + } + } while (timeout); + + if (timeout == 0) + { + err = -RT_ETIMEOUT; + } + } + } + + return err; +} + +static rt_ubase_t ccu_mix_calc_best_rate(struct rt_clk_cell *cell, + rt_ubase_t rate, rt_uint32_t *mux_val, rt_uint32_t *div_val) +{ + rt_uint32_t div_max; + rt_ubase_t parent_rate = 0, best_rate = 0; + struct rt_clk_cell *parent; + struct ccu_mix *mix = cell_to_ccu_mix(cell); + struct ccu_div_config *div = mix->div? mix->div: RT_NULL; + + for (int i = 0; i < cell->parents_nr; ++i) + { + parent = rt_clk_cell_get_parent_by_index(cell, i); + + if (!parent) + { + continue; + } + + parent_rate = rt_clk_cell_get_rate(parent); + + if (div) + { + div_max = 1 << div->width; + } + else + { + div_max = 1; + } + + for (int j = 1; j <= div_max; ++j) + { + if (div && div->clk_flags & CLK_DIVIDER_POWER_OF_TWO) + { + int d; + + if (div->clk_flags & CLK_DIVIDER_ALLOW_ZERO) + { + d = j - 1; + } + + if (rt_abs(parent_rate / RT_BIT(d) - rate) < rt_abs(best_rate - rate)) + { + best_rate = RT_DIV_ROUND_UP_ULL(parent_rate, RT_BIT(d)); + *mux_val = i; + *div_val = d; + } + + continue; + } + + if (rt_abs(parent_rate / j - rate) < rt_abs(best_rate - rate)) + { + best_rate = RT_DIV_ROUND_UP_ULL(parent_rate, j); + *mux_val = i; + *div_val = j - 1; + } + } + } + + return best_rate; +} + +static rt_err_t ccu_mix_set_rate(struct rt_clk_cell *cell, rt_ubase_t rate, rt_ubase_t parent_rate) +{ + rt_err_t err = RT_EOK; + rt_ubase_t level, best_rate; + rt_uint32_t cur_mux, cur_div, mux_val = 0, div_val = 0, reg = 0; + struct ccu_mix *mix = cell_to_ccu_mix(cell); + struct ccu_common *common = &mix->common; + struct ccu_div_config *div = mix->div? mix->div: RT_NULL; + struct ccu_mux_config *mux = mix->mux? mix->mux: RT_NULL; + + if (!div && !mux) + { + return RT_EOK; + } + + best_rate = ccu_mix_calc_best_rate(cell, rate, &mux_val, &div_val); + common->rate = best_rate; + + if (!rt_strcmp(cell->name, tswi8_clk_name)) + { + if (mux) + { + cur_mux = twsi8_reg_val >> mux->shift; + cur_mux &= (1 << mux->width) - 1; + + if (cur_mux != mux_val) + { + rt_clk_cell_set_parent(cell, rt_clk_cell_get_parent_by_index(cell, mux_val)); + } + } + + return RT_EOK; + } + + if (common->reg_type == CLK_DIV_TYPE_2REG_NOFC_V3 || + common->reg_type == CLK_DIV_TYPE_2REG_FC_V4) + { + reg = HWREG32(common->base + common->reg_sel); + } + else + { + reg = HWREG32(common->base + common->reg_ctrl); + } + + if (mux) + { + cur_mux = reg >> mux->shift; + cur_mux &= (1 << mux->width) - 1; + + if (cur_mux != mux_val) + { + rt_clk_cell_set_parent(cell, rt_clk_cell_get_parent_by_index(cell, mux_val)); + } + } + + if (div) + { + cur_div = reg >> div->shift; + cur_div &= (1 << div->width) - 1; + + if (cur_div == div_val) + { + return RT_EOK; + } + } + else + { + return RT_EOK; + } + + level = rt_spin_lock_irqsave(common->lock); + + if (common->reg_type == CLK_DIV_TYPE_2REG_NOFC_V3 || + common->reg_type == CLK_DIV_TYPE_2REG_FC_V4) + { + reg = HWREG32(common->base + common->reg_sel); + } + else + { + reg = HWREG32(common->base + common->reg_ctrl); + } + + reg &= ~RT_GENMASK(div->width + div->shift - 1, div->shift); + + if (common->reg_type == CLK_DIV_TYPE_2REG_NOFC_V3 || + common->reg_type == CLK_DIV_TYPE_2REG_FC_V4) + { + HWREG32(common->base + common->reg_sel) = reg | (div_val << div->shift); + } + else + { + HWREG32(common->base + common->reg_ctrl) = reg | (div_val << div->shift); + } + + if (common->reg_type == CLK_DIV_TYPE_1REG_FC_V2 || + common->reg_type == CLK_DIV_TYPE_2REG_FC_V4 || + common->reg_type == CLK_DIV_TYPE_1REG_FC_DIV_V5) + { + err = ccu_mix_trigger_fc(cell); + } + + rt_spin_unlock_irqrestore(common->lock, level); + + if (err) + { + LOG_E("%s:%s timeout", __func__, cell->name); + } + + return RT_EOK; +} + +static rt_err_t ccu_mix_set_parent(struct rt_clk_cell *cell, rt_uint8_t idx) +{ + rt_ubase_t level; + rt_uint32_t reg = 0; + rt_err_t err = RT_EOK; + struct ccu_mix *mix = cell_to_ccu_mix(cell); + struct ccu_common *common = &mix->common; + struct ccu_mux_config *mux = mix->mux; + + if (!mux) + { + return RT_EOK; + } + + if (mux->table) + { + idx = mux->table[idx]; + } + + if (!rt_strcmp(cell->name, tswi8_clk_name)) + { + twsi8_reg_val &= ~RT_GENMASK(mux->width + mux->shift - 1, mux->shift); + twsi8_reg_val |= (idx << mux->shift); + reg = twsi8_reg_val; + + if (common->reg_type == CLK_DIV_TYPE_2REG_NOFC_V3 || + common->reg_type == CLK_DIV_TYPE_2REG_FC_V4) + { + HWREG32(common->base + common->reg_sel) = reg; + } + else + { + HWREG32(common->base + common->reg_ctrl) = reg; + } + + return RT_EOK; + } + + level = rt_spin_lock_irqsave(common->lock); + + if (common->reg_type == CLK_DIV_TYPE_2REG_NOFC_V3 || + common->reg_type == CLK_DIV_TYPE_2REG_FC_V4) + { + reg = HWREG32(common->base + common->reg_sel); + } + else + { + reg = HWREG32(common->base + common->reg_ctrl); + } + + reg &= ~RT_GENMASK(mux->width + mux->shift - 1, mux->shift); + + if (common->reg_type == CLK_DIV_TYPE_2REG_NOFC_V3 || + common->reg_type == CLK_DIV_TYPE_2REG_FC_V4) + { + HWREG32(common->base + common->reg_sel) = reg | (idx << mux->shift); + } + else + { + HWREG32(common->base + common->reg_ctrl) = reg | (idx << mux->shift); + } + + if (common->reg_type == CLK_DIV_TYPE_1REG_FC_V2 || + common->reg_type == CLK_DIV_TYPE_2REG_FC_V4 || + common->reg_type == CLK_DIV_TYPE_1REG_FC_MUX_V6) + { + err = ccu_mix_trigger_fc(cell); + } + + rt_spin_unlock_irqrestore(common->lock, level); + + if (err) + { + LOG_E("%s:%s timeout", __func__, cell->name); + } + + return RT_EOK; +} + +static rt_uint8_t ccu_mix_get_parent(struct rt_clk_cell *cell) +{ + rt_uint32_t reg; + rt_uint8_t parent; + struct ccu_mix *mix = cell_to_ccu_mix(cell); + struct ccu_common *common = &mix->common; + struct ccu_mux_config *mux = mix->mux; + + if (!mux) + { + return 0; + } + + if (!rt_strcmp(cell->name, tswi8_clk_name)) + { + parent = twsi8_reg_val >> mux->shift; + parent &= (1 << mux->width) - 1; + + return parent; + } + + if (common->reg_type == CLK_DIV_TYPE_2REG_NOFC_V3 || + common->reg_type == CLK_DIV_TYPE_2REG_FC_V4) + { + reg = HWREG32(common->base + common->reg_sel); + } + else + { + reg = HWREG32(common->base + common->reg_ctrl); + } + + parent = reg >> mux->shift; + parent &= (1 << mux->width) - 1; + + if (mux->table) + { + for (int i = 0; i < cell->parents_nr; ++i) + { + if (mux->table[i] == parent) + { + return i; + } + } + } + + return parent; +} + +const struct rt_clk_ops ccu_mix_ops = +{ + .disable = ccu_mix_disable, + .enable = ccu_mix_enable, + .is_enabled = ccu_mix_is_enabled, + .recalc_rate = ccu_mix_recalc_rate, + .round_rate = ccu_mix_round_rate, + .set_rate = ccu_mix_set_rate, + .set_parent = ccu_mix_set_parent, + .get_parent = ccu_mix_get_parent, +}; diff --git a/bsp/spacemit/dm/clk/ccu_mix.h b/bsp/spacemit/dm/clk/ccu_mix.h new file mode 100755 index 00000000000..88c0c9cacc2 --- /dev/null +++ b/bsp/spacemit/dm/clk/ccu_mix.h @@ -0,0 +1,422 @@ +/* + * Copyright (c) 2006-2024, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2024-5-1 GuEe-GUI first version + */ + +#ifndef __CCU_MIX_H__ +#define __CCU_MIX_H__ + +#include "ccu.h" + +struct ccu_gate_config +{ + rt_uint32_t gate_mask; + rt_uint32_t val_enable; + rt_uint32_t val_disable; +#define SPACEMIT_CLK_GATE_NEED_DELAY RT_BIT(0) + rt_uint32_t flags; +}; + +struct ccu_factor_config +{ + rt_uint32_t div; + rt_uint32_t mul; +}; + +struct ccu_mux_config +{ + rt_uint8_t shift; + rt_uint8_t width; + const rt_uint8_t *table; +}; + +struct ccu_div_table +{ + rt_uint32_t val; + rt_uint32_t div; +}; + +struct ccu_div_config +{ + rt_uint8_t shift; + rt_uint8_t width; + rt_uint32_t max; + rt_uint32_t offset; + rt_uint32_t clk_flags; + struct ccu_div_table *table; +}; + +struct ccu_mix +{ + struct ccu_common common; + struct ccu_gate_config *gate; + struct ccu_factor_config *factor; + struct ccu_div_config *div; + struct ccu_mux_config *mux; +}; + +#define CCU_GATE_INIT(_gate_mask, _val_enable, _val_disable, _flags) \ +(&(struct ccu_gate_config) \ +{ \ + .gate_mask = _gate_mask, \ + .val_enable = _val_enable, \ + .val_disable = _val_disable, \ + .flags = _flags, \ +}) + +#define CCU_FACTOR_INIT(_div, _mul) \ +(&(struct ccu_factor_config)\ +{ \ + .div = _div, \ + .mul = _mul, \ +}) + +#define CCU_MUX_INIT(_shift, _width, _table) \ +(&(struct ccu_mux_config) \ +{ \ + .shift = _shift, \ + .width = _width, \ + .table = _table, \ +}) + +#define CCU_DIV_INIT(_shift, _width, _table, _clk_flags) \ +(&(struct ccu_div_config) \ +{ \ + .shift = _shift, \ + .width = _width, \ + .table = _table, \ + .clk_flags = _clk_flags, \ +}) + +#define SPACEMIT_CCU_GATE(_struct, _name, _parent, _base_type, _reg, \ + _gate_mask, _val_enable, _val_disable, _flags) \ +struct ccu_mix _struct = \ +{ \ + .gate = CCU_GATE_INIT(_gate_mask, _val_enable, _val_disable, 0), \ + .common = \ + { \ + .reg_ctrl = _reg, \ + .base_type = _base_type, \ + .cell.name = _name, \ + .cell.parent_name = _parent, \ + .cell.parents_nr = 1, \ + .cell.ops = &ccu_mix_ops, \ + .cell.flags = _flags, \ + }, \ +} + +#define SPACEMIT_CCU_GATE_NO_PARENT(_struct, _name, _parent, _base_type, _reg, \ + _gate_mask, _val_enable, _val_disable, _flags) \ +struct ccu_mix _struct = \ +{ \ + .gate = CCU_GATE_INIT(_gate_mask, _val_enable, _val_disable, 0), \ + .common = \ + { \ + .reg_ctrl = _reg, \ + .base_type = _base_type, \ + .cell.name = _name, \ + .cell.parent_name = _parent, \ + .cell.parents_nr = 1, \ + .cell.ops = &ccu_mix_ops, \ + .cell.flags = _flags, \ + }, \ +} + +#define SPACEMIT_CCU_FACTOR(_struct, _name, _parent, _div, _mul) \ +struct ccu_mix _struct = \ +{ \ + .factor = CCU_FACTOR_INIT(_div, _mul), \ + .common = \ + { \ + .cell.name = _name, \ + .cell.parent_name = _parent, \ + .cell.parents_nr = 1, \ + .cell.ops = &ccu_mix_ops, \ + }, \ +} + +#define SPACEMIT_CCU_MUX(_struct, _name, _parents, _base_type, _reg, \ + _shift, _width, _flags) \ +struct ccu_mix _struct = \ +{ \ + .mux = CCU_MUX_INIT(_shift, _width, RT_NULL),\ + .common = \ + { \ + .reg_ctrl = _reg, \ + .base_type = _base_type, \ + .cell.name = _name, \ + .cell.parent_names = _parents, \ + .cell.parents_nr = RT_ARRAY_SIZE(_parents), \ + .cell.ops = &ccu_mix_ops, \ + .cell.flags = _flags, \ + } \ +} + +#define SPACEMIT_CCU_DIV(_struct, _name, _parent, _base_type, _reg, \ + _shift, _width, _flags) \ +struct ccu_mix _struct = \ +{ \ + .div = CCU_DIV_INIT(_shift, _width, RT_NULL, 0), \ + .common = \ + { \ + .reg_ctrl = _reg, \ + .base_type = _base_type, \ + .cell.name = _name, \ + .cell.parent_name = _parent, \ + .cell.parents_nr = 1, \ + .cell.ops = &ccu_mix_ops, \ + .cell.flags = _flags, \ + } \ +} + +#define SPACEMIT_CCU_DIV_FLAG(_struct, _name, _parent, _base_type, _reg, \ + _shift, _width, _div_flags, _flags) \ +struct ccu_mix _struct = \ +{ \ + .div = CCU_DIV_INIT(_shift, _width, RT_NULL, _div_flags), \ + .common = \ + { \ + .reg_ctrl = _reg, \ + .base_type = _base_type, \ + .cell.name = _name, \ + .cell.parent_name = _parent, \ + .cell.parents_nr = 1, \ + .cell.ops = &ccu_mix_ops, \ + .cell.flags = _flags, \ + } \ +} + +#define SPACEMIT_CCU_GATE_FACTOR(_struct, _name, _parent, _base_type, _reg, \ + _gate_mask, _val_enable, _val_disable, _div, _mul, _flags) \ +struct ccu_mix _struct = \ +{ \ + .gate = CCU_GATE_INIT(_gate_mask, _val_enable, _val_disable, 0), \ + .factor = CCU_FACTOR_INIT(_div, _mul), \ + .common = \ + { \ + .reg_ctrl = _reg, \ + .base_type = _base_type, \ + .cell.name = _name, \ + .cell.parent_name = _parent, \ + .cell.parents_nr = 1, \ + .cell.ops = &ccu_mix_ops, \ + .cell.flags = _flags, \ + } \ +} + +#define SPACEMIT_CCU_MUX_GATE(_struct, _name, _parents, _base_type, _reg, \ + _shift, _width, _gate_mask, _val_enable, _val_disable, _flags) \ +struct ccu_mix _struct = \ +{ \ + .gate = CCU_GATE_INIT(_gate_mask, _val_enable, _val_disable, 0), \ + .mux = CCU_MUX_INIT(_shift, _width, RT_NULL), \ + .common = \ + { \ + .reg_ctrl = _reg, \ + .base_type = _base_type, \ + .cell.name = _name, \ + .cell.parent_names = _parents, \ + .cell.parents_nr = RT_ARRAY_SIZE(_parents), \ + .cell.ops = &ccu_mix_ops, \ + .cell.flags = _flags, \ + } \ +} + +#define SPACEMIT_CCU_DIV_GATE(_struct, _name, _parent, _base_type, _reg, \ + _shift, _width, _gate_mask, _val_enable, _val_disable, _flags) \ +struct ccu_mix _struct = \ +{ \ + .gate = CCU_GATE_INIT(_gate_mask, _val_enable, _val_disable, 0), \ + .div = CCU_DIV_INIT(_shift, _width, RT_NULL, 0), \ + .common = \ + { \ + .reg_ctrl = _reg, \ + .base_type = _base_type, \ + .cell.name = _name, \ + .cell.parent_name = _parent, \ + .cell.parents_nr = 1, \ + .cell.ops = &ccu_mix_ops, \ + .cell.flags = _flags, \ + } \ +} + +#define SPACEMIT_CCU_DIV_MUX_GATE(_struct, _name, _parents, _base_type, _reg_ctrl, \ + _mshift, _mwidth, _muxshift, _muxwidth, \ + _gate_mask, _val_enable, _val_disable, _flags) \ +struct ccu_mix _struct = \ +{ \ + .gate = CCU_GATE_INIT(_gate_mask, _val_enable, _val_disable, 0), \ + .div = CCU_DIV_INIT(_mshift, _mwidth, RT_NULL, 0), \ + .mux = CCU_MUX_INIT(_muxshift, _muxwidth, RT_NULL), \ + .common = \ + { \ + .reg_ctrl = _reg_ctrl, \ + .base_type = _base_type, \ + .cell.name = _name, \ + .cell.parent_names = _parents, \ + .cell.parents_nr = RT_ARRAY_SIZE(_parents), \ + .cell.ops = &ccu_mix_ops, \ + .cell.flags = _flags, \ + }, \ +} + +#define SPACEMIT_CCU_DIV2_FC_MUX_GATE(_struct, _name, _parents, _base_type, _reg_ctrl, _reg_sel, \ + _mshift, _mwidth, _fc, _muxshift, _muxwidth, _gate_mask, \ + _val_enable, _val_disable, _flags) \ +struct ccu_mix _struct = \ +{ \ + .gate = CCU_GATE_INIT(_gate_mask, _val_enable, _val_disable, 0), \ + .div = CCU_DIV_INIT(_mshift, _mwidth, RT_NULL, 0), \ + .mux = CCU_MUX_INIT(_muxshift, _muxwidth, RT_NULL), \ + .common = \ + { \ + .reg_type = CLK_DIV_TYPE_2REG_FC_V4, \ + .reg_ctrl = _reg_ctrl, \ + .reg_sel = _reg_sel, \ + .fc = _fc, \ + .base_type = _base_type, \ + .cell.name = _name, \ + .cell.parent_names = _parents, \ + .cell.parents_nr = RT_ARRAY_SIZE(_parents), \ + .cell.ops = &ccu_mix_ops, \ + .cell.flags = _flags, \ + }, \ +} + + +#define SPACEMIT_CCU_DIV_FC_MUX_GATE(_struct, _name, _parents, _base_type, _reg_ctrl, \ + _mshift, _mwidth, _fc, _muxshift, _muxwidth, _gate_mask, \ + _val_enable, _val_disable, _flags) \ +struct ccu_mix _struct = \ +{ \ + .gate = CCU_GATE_INIT(_gate_mask, _val_enable, _val_disable, 0), \ + .div = CCU_DIV_INIT(_mshift, _mwidth, RT_NULL, 0), \ + .mux = CCU_MUX_INIT(_muxshift, _muxwidth, RT_NULL), \ + .common = \ + { \ + .reg_type = CLK_DIV_TYPE_1REG_FC_V2, \ + .reg_ctrl = _reg_ctrl, \ + .fc = _fc, \ + .base_type = _base_type, \ + .cell.name = _name, \ + .cell.parent_names = _parents, \ + .cell.parents_nr = RT_ARRAY_SIZE(_parents), \ + .cell.ops = &ccu_mix_ops, \ + .cell.flags = _flags, \ + }, \ +} + +#define SPACEMIT_CCU_DIV_MFC_MUX_GATE(_struct, _name, _parents, _base_type, _reg_ctrl, \ + _mshift, _mwidth, _fc, _muxshift, _muxwidth, _gate_mask, \ + _val_enable, _val_disable, _flags) \ +struct ccu_mix _struct = \ +{ \ + .gate = CCU_GATE_INIT(_gate_mask, _val_enable, _val_disable, 0), \ + .div = CCU_DIV_INIT(_mshift, _mwidth, RT_NULL, 0), \ + .mux = CCU_MUX_INIT(_muxshift, _muxwidth, RT_NULL), \ + .common = \ + { \ + .reg_type = CLK_DIV_TYPE_1REG_FC_MUX_V6, \ + .reg_ctrl = _reg_ctrl, \ + .fc = _fc, \ + .base_type = _base_type, \ + .cell.name = _name, \ + .cell.parent_names = _parents, \ + .cell.parents_nr = RT_ARRAY_SIZE(_parents), \ + .cell.ops = &ccu_mix_ops, \ + .cell.flags = _flags, \ + }, \ +} + +#define SPACEMIT_CCU_DIV_FC_WITH_GATE(_struct, _name, _parent, _base_type, _reg_ctrl, \ + _mshift, _mwidth, _fc, _gate_mask, _val_enable, _val_disable, _flags) \ +struct ccu_mix _struct = \ +{ \ + .gate = CCU_GATE_INIT(_gate_mask, _val_enable, _val_disable, 0), \ + .div = CCU_DIV_INIT(_mshift, _mwidth, RT_NULL, 0), \ + .common = \ + { \ + .reg_type = CLK_DIV_TYPE_1REG_FC_V2, \ + .reg_ctrl = _reg_ctrl, \ + .fc = _fc, \ + .base_type = _base_type, \ + .cell.name = _name, \ + .cell.parent_name = _parent, \ + .cell.parents_nr = 1, \ + .cell.ops = &ccu_mix_ops, \ + .cell.flags = _flags, \ + }, \ +} + +#define SPACEMIT_CCU_DIV_MUX(_struct, _name, _parents, _base_type, _reg_ctrl, \ + _mshift, _mwidth, _muxshift, _muxwidth, _flags) \ +struct ccu_mix _struct = \ +{ \ + .div = CCU_DIV_INIT(_mshift, _mwidth, RT_NULL, 0), \ + .mux = CCU_MUX_INIT(_muxshift, _muxwidth, RT_NULL), \ + .common = \ + { \ + .reg_ctrl = _reg_ctrl, \ + .base_type = _base_type, \ + .cell.name = _name, \ + .cell.parent_names = _parents, \ + .cell.parents_nr = RT_ARRAY_SIZE(_parents), \ + .cell.ops = &ccu_mix_ops, \ + .cell.flags = _flags, \ + }, \ +} + +#define SPACEMIT_CCU_DIV_FC_MUX(_struct, _name, _parents, _base_type, _reg_ctrl, \ + _mshift, _mwidth, _fc, _muxshift, _muxwidth, _flags) \ +struct ccu_mix _struct = \ +{ \ + .div = CCU_DIV_INIT(_mshift, _mwidth, RT_NULL, 0), \ + .mux = CCU_MUX_INIT(_muxshift, _muxwidth, RT_NULL), \ + .common = \ + { \ + .reg_type = CLK_DIV_TYPE_1REG_FC_V2, \ + .reg_ctrl = _reg_ctrl, \ + .fc = _fc, \ + .base_type = _base_type, \ + .cell.name = _name, \ + .cell.parent_names = _parents, \ + .cell.parents_nr = RT_ARRAY_SIZE(_parents), \ + .cell.ops = &ccu_mix_ops, \ + .cell.flags = _flags, \ + }, \ +} + +#define SPACEMIT_CCU_MUX_FC(_struct, _name, _parents, _base_type, _reg_ctrl, \ + _fc, _muxshift, _muxwidth, _flags) \ +struct ccu_mix _struct = \ +{ \ + .mux = CCU_MUX_INIT(_muxshift, _muxwidth, RT_NULL), \ + .common = \ + { \ + .reg_type = CLK_DIV_TYPE_1REG_FC_V2, \ + .reg_ctrl = _reg_ctrl, \ + .fc = _fc, \ + .base_type = _base_type, \ + .cell.name = _name, \ + .cell.parent_names = _parents, \ + .cell.parents_nr = RT_ARRAY_SIZE(_parents), \ + .cell.ops = &ccu_mix_ops, \ + .cell.flags = _flags, \ + }, \ +} + +extern const struct rt_clk_ops ccu_mix_ops; + +rt_inline struct ccu_mix *cell_to_ccu_mix(struct rt_clk_cell *cell) +{ + struct ccu_common *common = cell_to_ccu_common(cell); + + return rt_container_of(common, struct ccu_mix, common); +} + +#endif /* __CCU_MIX_H__ */ diff --git a/bsp/spacemit/dm/clk/ccu_pll.c b/bsp/spacemit/dm/clk/ccu_pll.c new file mode 100755 index 00000000000..a1b0270fb3c --- /dev/null +++ b/bsp/spacemit/dm/clk/ccu_pll.c @@ -0,0 +1,321 @@ +/* + * Copyright (c) 2006-2024, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2024-5-1 GuEe-GUI first version + */ + +#include "ccu_pll.h" + +#define DBG_TAG "clk.ccu.pll" +#define DBG_LVL DBG_INFO +#include + +#define PLL_MIN_FREQ 600000000 +#define PLL_MAX_FREQ 3400000000 +#define PLL_DELAYTIME 590 /* 590 * 5us */ + +#define pll_readl(reg) HWREG32(reg) +#define pll_readl_pll_swcr1(p) pll_readl(p.base + p.reg_ctrl) +#define pll_readl_pll_swcr2(p) pll_readl(p.base + p.reg_sel) +#define pll_readl_pll_swcr3(p) pll_readl(p.base + p.reg_xtc) + +#define pll_writel(val, reg) HWREG32(reg) = val; +#define pll_writel_pll_swcr1(val, p) pll_writel(val, p.base + p.reg_ctrl) +#define pll_writel_pll_swcr2(val, p) pll_writel(val, p.base + p.reg_sel) +#define pll_writel_pll_swcr3(val, p) pll_writel(val, p.base + p.reg_xtc) + +/* Unified pllx_swcr1 for pll1~3 */ +union pllx_swcr1 +{ + struct + { + rt_uint32_t reg5:8; + rt_uint32_t reg6:8; + rt_uint32_t reg7:8; + rt_uint32_t reg8:8; + } b; + rt_uint32_t v; +}; + +/* Unified pllx_swcr2 for pll1~3 */ +union pllx_swcr2 +{ + struct + { + rt_uint32_t div1_en:1; + rt_uint32_t div2_en:1; + rt_uint32_t div3_en:1; + rt_uint32_t div4_en:1; + rt_uint32_t div5_en:1; + rt_uint32_t div6_en:1; + rt_uint32_t div7_en:1; + rt_uint32_t div8_en:1; + rt_uint32_t reserved1:4; + rt_uint32_t atest_en:1; + rt_uint32_t cktest_en:1; + rt_uint32_t dtest_en:1; + rt_uint32_t rdo:2; + rt_uint32_t mon_cfg:4; + rt_uint32_t reserved2:11; + } b; + rt_uint32_t v; +}; + +/* Unified pllx_swcr3 for pll1~3 */ +union pllx_swcr3 +{ + struct + { + rt_uint32_t div_frc:24; + rt_uint32_t div_int:7; + rt_uint32_t pll_en:1; + } b; + rt_uint32_t v; +}; + +static rt_bool_t ccu_pll_is_enabled(struct rt_clk_cell *cell) +{ + rt_bool_t enabled; + union pllx_swcr3 swcr3; + struct ccu_pll *p = cell_to_ccu_pll(cell); + + swcr3.v = pll_readl_pll_swcr3(p->common); + enabled = !!swcr3.b.pll_en; + + return enabled; +} + +/* Frequency unit Mhz, return pll vco freq */ +static rt_ubase_t __get_vco_freq(struct rt_clk_cell *cell) +{ + rt_uint32_t div_int, div_frc, reg5, reg6, reg7, reg8, size; + union pllx_swcr1 swcr1; + union pllx_swcr3 swcr3; + struct ccu_pll_rate_tbl *freq_pll_regs_table; + struct ccu_pll *p = cell_to_ccu_pll(cell); + + swcr1.v = pll_readl_pll_swcr1(p->common); + swcr3.v = pll_readl_pll_swcr3(p->common); + + reg5 = swcr1.b.reg5; + reg6 = swcr1.b.reg6; + reg7 = swcr1.b.reg7; + reg8 = swcr1.b.reg8; + + div_int = swcr3.b.div_int; + div_frc = swcr3.b.div_frc; + + freq_pll_regs_table = p->pll.rate_tbl; + size = p->pll.tbl_size; + + for (int i = 0; i < size; ++i) + { + struct ccu_pll_rate_tbl *item = &freq_pll_regs_table[i]; + + if (item->reg5 == reg5 && item->reg6 == reg6 && + item->reg7 == reg7 && item->reg8 == reg8 && + item->div_int == div_int && item->div_frac == div_frc) + { + return item->rate; + } + } + + LOG_E("Unknown rate for clock %s", cell->name); + + return 0; +} + +static rt_err_t ccu_pll_enable(struct rt_clk_cell *cell) +{ + rt_ubase_t level; + rt_uint32_t delaytime = PLL_DELAYTIME; + union pllx_swcr3 swcr3; + struct ccu_pll *p = cell_to_ccu_pll(cell); + + if (ccu_pll_is_enabled(cell)) + { + return RT_EOK; + } + + level = rt_spin_lock_irqsave(p->common.lock); + + swcr3.v = pll_readl_pll_swcr3(p->common); + swcr3.b.pll_en = 1; + pll_writel_pll_swcr3(swcr3.v, p->common); + + rt_spin_unlock_irqrestore(p->common.lock, level); + + /* Check lock status */ + rt_hw_us_delay(50); + + while ((!(HWREG32(p->pll.lock_base + p->pll.reg_lock) & p->pll.lock_enable_bit)) && delaytime) + { + rt_hw_us_delay(5); + --delaytime; + } + + if (!delaytime) + { + LOG_E("%s enabling didn't get stable within 3000us", cell->name); + } + + return RT_EOK; +} + +static void ccu_pll_disable(struct rt_clk_cell *cell) +{ + rt_ubase_t level; + union pllx_swcr3 swcr3; + struct ccu_pll *p = cell_to_ccu_pll(cell); + + level = rt_spin_lock_irqsave(p->common.lock); + + swcr3.v = pll_readl_pll_swcr3(p->common); + swcr3.b.pll_en = 0; + pll_writel_pll_swcr3(swcr3.v, p->common); + + rt_spin_unlock_irqrestore(p->common.lock, level); +} + +static rt_ubase_t ccu_pll_recalc_rate(struct rt_clk_cell *cell, rt_ubase_t parent_rate) +{ + return __get_vco_freq(cell); +} + +static rt_base_t ccu_pll_round_rate(struct rt_clk_cell *cell, rt_ubase_t drate, rt_ubase_t *prate) +{ + rt_ubase_t max_rate = 0; + struct ccu_pll *p = cell_to_ccu_pll(cell); + struct ccu_pll_config *params = &p->pll; + + if (drate > PLL_MAX_FREQ || drate < PLL_MIN_FREQ) + { + LOG_E("%s %lu rate out of range", cell->name, drate); + return -RT_EINVAL; + } + + if (params->rate_tbl) + { + for (int i = 0; i < params->tbl_size; ++i) + { + struct ccu_pll_rate_tbl *item = ¶ms->rate_tbl[i]; + + if (item->rate <= drate) + { + if (max_rate < item->rate) + { + max_rate = item->rate; + } + } + } + } + else + { + LOG_E("%s don't find freq table for pll", cell->name); + } + + return max_rate; +} + +/* + * PLL rate change requires sequence: + * clock off -> change rate setting -> clock on + * This function doesn't really change rate, but cache the config + */ +static rt_err_t ccu_pll_set_rate(struct rt_clk_cell *cell, rt_ubase_t rate, rt_ubase_t parent_rate) +{ + rt_uint32_t div_int, div_frc, reg5 = 0, reg6 = 0, reg7 = 0, reg8 = 0; + rt_bool_t found = RT_FALSE, pll_enabled = RT_FALSE; + rt_ubase_t level; + union pllx_swcr1 swcr1; + union pllx_swcr3 swcr3; + struct ccu_pll *p = cell_to_ccu_pll(cell); + struct ccu_pll_config *params = &p->pll; + + if (ccu_pll_is_enabled(cell)) + { + pll_enabled = RT_TRUE; + ccu_pll_disable(cell); + } + + __get_vco_freq(cell); + + /* Setp 1: calculate fbd frcd kvco and band */ + if (params->rate_tbl) + { + for (int i = 0; i < params->tbl_size; ++i) + { + struct ccu_pll_rate_tbl *item = ¶ms->rate_tbl[i]; + + if (rate == item->rate) + { + found = RT_TRUE; + + reg5 = item->reg5; + reg6 = item->reg6; + reg7 = item->reg7; + reg8 = item->reg8; + div_int = item->div_int; + div_frc = item->div_frac; + + break; + } + } + + if (!found) + { + return -RT_EINVAL; + } + } + else + { + LOG_E("%s don't find freq table for pll", cell->name); + + if (pll_enabled) + { + ccu_pll_enable(cell); + } + + return -RT_EINVAL; + } + + level = rt_spin_lock_irqsave(p->common.lock); + + /* Setp 2: set pll kvco/band and fbd/frcd setting */ + swcr1.v = pll_readl_pll_swcr1(p->common); + swcr1.b.reg5 = reg5; + swcr1.b.reg6 = reg6; + swcr1.b.reg7 = reg7; + swcr1.b.reg8 = reg8; + pll_writel_pll_swcr1(swcr1.v, p->common); + + swcr3.v = pll_readl_pll_swcr3(p->common); + swcr3.b.div_int = div_int; + swcr3.b.div_frc = div_frc; + pll_writel_pll_swcr3(swcr3.v, p->common); + + rt_spin_unlock_irqrestore(p->common.lock, level); + + if (pll_enabled) + { + ccu_pll_enable(cell); + } + + LOG_D("%s: %s rate %lu->%lu", __func__, clk->name, old_rate, new_rate); + + return RT_EOK; +} + +const struct rt_clk_ops ccu_pll_ops = +{ + .enable = ccu_pll_enable, + .disable = ccu_pll_disable, + .is_enabled = ccu_pll_is_enabled, + .recalc_rate = ccu_pll_recalc_rate, + .round_rate = ccu_pll_round_rate, + .set_rate = ccu_pll_set_rate, +}; diff --git a/bsp/spacemit/dm/clk/ccu_pll.h b/bsp/spacemit/dm/clk/ccu_pll.h new file mode 100755 index 00000000000..531a9363198 --- /dev/null +++ b/bsp/spacemit/dm/clk/ccu_pll.h @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2006-2024, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2024-5-1 GuEe-GUI first version + */ + +#ifndef __CCU_PLL_H__ +#define __CCU_PLL_H__ + +#include "ccu.h" + +struct ccu_pll_rate_tbl +{ + rt_uint64_t rate; + rt_uint32_t reg5; + rt_uint32_t reg6; + rt_uint32_t reg7; + rt_uint32_t reg8; + rt_uint32_t div_int; + rt_uint32_t div_frac; +}; + +struct ccu_pll_config +{ + rt_uint32_t tbl_size; + struct ccu_pll_rate_tbl *rate_tbl; + + void *lock_base; + rt_uint32_t reg_lock; + rt_uint32_t lock_enable_bit; +}; + +struct ccu_pll +{ + struct ccu_common common; + struct ccu_pll_config pll; +}; + +#define PLL_RATE(_rate, _reg5, _reg6, _reg7, _reg8, _div_int, _div_frac) \ +{ \ + .rate = (_rate), \ + .reg5 = (_reg5), \ + .reg6 = (_reg6), \ + .reg7 = (_reg7), \ + .reg8 = (_reg8), \ + .div_int = (_div_int), \ + .div_frac = (_div_frac), \ +} + +#define _SPACEMIT_CCU_PLL_CONFIG(_table, _size, _reg_lock, _lock_enable_bit) \ +{ \ + .rate_tbl = (struct ccu_pll_rate_tbl *)_table, \ + .tbl_size = _size, \ + .reg_lock = _reg_lock, \ + .lock_enable_bit = _lock_enable_bit, \ +} + +#define SPACEMIT_CCU_PLL(_struct, _name, _table, _size, _base_type, \ + _reg_ctrl, _reg_sel, _reg_xtc, \ + _reg_lock, _lock_enable_bit, _is_pll, _flags) \ +struct ccu_pll _struct = \ +{ \ + .pll = _SPACEMIT_CCU_PLL_CONFIG(_table, _size, _reg_lock, _lock_enable_bit), \ + .common = \ + { \ + .reg_ctrl = _reg_ctrl, \ + .reg_sel = _reg_sel, \ + .reg_xtc = _reg_xtc, \ + .base_type = _base_type, \ + .is_pll = _is_pll, \ + .cell.name = _name, \ + .cell.ops = &ccu_pll_ops, \ + .cell.flags = _flags, \ + }, \ +} + +extern const struct rt_clk_ops ccu_pll_ops; + +rt_inline struct ccu_pll *cell_to_ccu_pll(struct rt_clk_cell *cell) +{ + struct ccu_common *common = cell_to_ccu_common(cell); + + return rt_container_of(common, struct ccu_pll, common); +} + +#endif /* __CCU_PLL_H__ */ diff --git a/bsp/spacemit/dm/dma/Kconfig b/bsp/spacemit/dm/dma/Kconfig new file mode 100755 index 00000000000..da72fad16b7 --- /dev/null +++ b/bsp/spacemit/dm/dma/Kconfig @@ -0,0 +1,8 @@ +config RT_DMA_MMP_PDMA + bool "Spacemit K1X MMP PDMA" + default n + +config RT_DMA_MMP_PDMA_SUPPORT_64BIT + bool "Support 64bit" + depends on RT_DMA_MMP_PDMA + default n diff --git a/bsp/spacemit/dm/dma/SConscript b/bsp/spacemit/dm/dma/SConscript new file mode 100755 index 00000000000..e8fd891561a --- /dev/null +++ b/bsp/spacemit/dm/dma/SConscript @@ -0,0 +1,13 @@ +from building import * + +group = [] +src = [] +cwd = GetCurrentDir() +CPPPATH = [cwd + '/../include'] + +if GetDepend(['RT_DMA_MMP_PDMA']): + src += ['dma-mmp-pdma.c'] + +group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') diff --git a/bsp/spacemit/dm/dma/dma-mmp-pdma.c b/bsp/spacemit/dm/dma/dma-mmp-pdma.c new file mode 100755 index 00000000000..5e38d12eb03 --- /dev/null +++ b/bsp/spacemit/dm/dma/dma-mmp-pdma.c @@ -0,0 +1,956 @@ +/* + * Copyright (c) 2006-2024, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2024-5-1 GuEe-GUI first version + */ + +#include +#include +#include + +#define DBG_TAG "dma.mmp-pdma" +#define DBG_LVL DBG_INFO +#include + +#include + +#define DDADRH(n) (0x0300 + ((n) << 4)) +#define DSADRH(n) (0x0304 + ((n) << 4)) +#define DTADRH(n) (0x0308 + ((n) << 4)) +#define DCSR_LPAEEN RT_BIT(21) /* Long Physical Address Extension enable */ +#define DRCMR_INVALID 100 /* Max DMA request number + 1 */ +#define DCMD_BURST64 (4 << 16) /* 64 byte burst */ + +#define DCSR 0x0000 +#define DALGN 0x00a0 +#define DINT 0x00f0 +#define DDADR 0x0200 +#define DSADR(n) (0x0204 + ((n) << 4)) +#define DTADR(n) (0x0208 + ((n) << 4)) +#define DCMD 0x020c + +#define DCSR_RUN RT_BIT(31) /* Run Bit (read / write) */ +#define DCSR_NODESC RT_BIT(30) /* No-Descriptor Fetch (read / write) */ +#define DCSR_STOPIRQEN RT_BIT(29) /* Stop Interrupt Enable (read / write) */ +#define DCSR_REQPEND RT_BIT(8) /* Request Pending (read-only) */ +#define DCSR_STOPSTATE RT_BIT(3) /* Stop State (read-only) */ +#define DCSR_ENDINTR RT_BIT(2) /* End Interrupt (read / write) */ +#define DCSR_STARTINTR RT_BIT(1) /* Start Interrupt (read / write) */ +#define DCSR_BUSERR RT_BIT(0) /* Bus Error Interrupt (read / write) */ + +#define DCSR_EORIRQEN RT_BIT(28) /* End of Receive Interrupt Enable (R/W) */ +#define DCSR_EORJMPEN RT_BIT(27) /* Jump to next descriptor on EOR */ +#define DCSR_EORSTOPEN RT_BIT(26) /* STOP on an EOR */ +#define DCSR_SETCMPST RT_BIT(25) /* Set Descriptor Compare Status */ +#define DCSR_CLRCMPST RT_BIT(24) /* Clear Descriptor Compare Status */ +#define DCSR_CMPST RT_BIT(10) /* The Descriptor Compare Status */ +#define DCSR_EORINTR RT_BIT(9) /* The end of Receive */ + +#define DRCMR(n) ((((n) < 64) ? 0x0100 : 0x1100) + (((n) & 0x3f) << 2)) +#define DRCMR_MAPVLD RT_BIT(7) /* Map Valid (read / write) */ +#define DRCMR_CHLNUM 0x1f /* mask for Channel Number (read / write) */ + +#define DDADR_DESCADDR 0xfffffff0 /* Address of next descriptor (mask) */ +#define DDADR_STOP RT_BIT(0) /* Stop (read / write) */ + +#define DCMD_INCSRCADDR RT_BIT(31) /* Source Address Increment Setting. */ +#define DCMD_INCTRGADDR RT_BIT(30) /* Target Address Increment Setting. */ +#define DCMD_FLOWSRC RT_BIT(29) /* Flow Control by the source. */ +#define DCMD_FLOWTRG RT_BIT(28) /* Flow Control by the target. */ +#define DCMD_STARTIRQEN RT_BIT(22) /* Start Interrupt Enable */ +#define DCMD_ENDIRQEN RT_BIT(21) /* End Interrupt Enable */ +#define DCMD_ENDIAN RT_BIT(18) /* Device Endian-ness. */ +#define DCMD_BURST8 (1 << 16) /* 8 byte burst */ +#define DCMD_BURST16 (2 << 16) /* 16 byte burst */ +#define DCMD_BURST32 (3 << 16) /* 32 byte burst */ +#define DCMD_WIDTH1 (1 << 14) /* 1 byte width */ +#define DCMD_WIDTH2 (2 << 14) /* 2 byte width (HalfWord) */ +#define DCMD_WIDTH4 (3 << 14) /* 4 byte width (Word) */ +#define DCMD_LENGTH 0x01fff /* length mask (max = 8K - 1) */ + +#define DEFAULT_MAX_BURST_SIZE 32 +#define INVALID_BURST_SETTING -1 + +#define PDMA_MAX_DESC_BYTES DCMD_LENGTH + +#ifdef RT_DMA_MMP_PDMA_SUPPORT_64BIT +struct mmp_pdma_desc_hw +{ + rt_uint32_t ddadr; /* Points to the next descriptor + flags */ + rt_uint32_t dsadr; /* DSADR value for the current transfer */ + rt_uint32_t dtadr; /* DTADR value for the current transfer */ + rt_uint32_t dcmd; /* DCMD value for the current transfer */ + rt_uint32_t ddadrh; /* Points to the next descriptor + flags */ + rt_uint32_t dsadrh; /* DSADR value for the current transfer */ + rt_uint32_t dtadrh; /* DTADR value for the current transfer */ + rt_uint32_t rsvd; /* DCMD value for the current transfer */ +} rt_align(64); +#else +struct mmp_pdma_desc_hw +{ + rt_uint32_t ddadr; /* Points to the next descriptor + flags */ + rt_uint32_t dsadr; /* DSADR value for the current transfer */ + rt_uint32_t dtadr; /* DTADR value for the current transfer */ + rt_uint32_t dcmd; /* DCMD value for the current transfer */ +} rt_align(32); +#endif + +struct mmp_pdma_chan +{ + struct rt_dma_chan parent; + + rt_size_t desc_nr; + rt_ubase_t desc_dma; + struct mmp_pdma_desc_hw *desc; + + rt_uint32_t dcmd; + rt_uint32_t drcmr; + rt_uint32_t dev_addr; + + rt_size_t size; + rt_uint32_t bytes_residue; + rt_bool_t byte_align; + rt_bool_t pausing; + enum rt_dma_transfer_direction dir; +}; + +struct reserved_chan +{ + rt_uint32_t chan_id; + rt_uint32_t drcmr; +}; + +struct mmp_pdma_device +{ + struct rt_dma_controller parent; + + struct rt_clk *clk; + struct rt_reset_control *rstc; + + int irq; + void *base; + + rt_uint32_t max_burst_size; + + rt_uint32_t dma_channels; + struct mmp_pdma_chan *chan; + + rt_uint32_t reserved_channels_nr; + struct reserved_chan *reserved_channels; + + struct mmp_pdma_phy *phy; +}; +#define raw_to_mmp_pdma_device(raw) rt_container_of(raw, struct mmp_pdma_device, parent) +#define raw_to_mmp_pdma_device_chan(raw) rt_container_of(raw, struct mmp_pdma_chan, parent) + +static int get_max_burst_setting(rt_uint32_t max_burst_size) +{ + switch (max_burst_size) + { + case 8: + return DCMD_BURST8; + case 16: + return DCMD_BURST16; + case 32: + return DCMD_BURST32; + case 64: + return DCMD_BURST64; + default: + return INVALID_BURST_SETTING; + } +} + +static void enable_chan(struct mmp_pdma_chan *chan) +{ + int idx; + rt_uint32_t reg, dalgn, dcsr; + struct mmp_pdma_device *pdma = raw_to_mmp_pdma_device(chan->parent.ctrl); + + idx = chan - pdma->chan; + + if (chan->drcmr >= 0) + { + reg = DRCMR(chan->drcmr); + HWREG32(pdma->base + reg) = DRCMR_MAPVLD | idx; + } + + dalgn = HWREG32(pdma->base + DALGN); + if (chan->byte_align) + { + dalgn |= 1 << idx; + } + else + { + dalgn &= ~(1 << idx); + } + HWREG32(pdma->base + DALGN) = dalgn; + + reg = (idx << 2) + DCSR; + + dcsr = HWREG32(pdma->base + reg); + dcsr |= DCSR_RUN | DCSR_EORIRQEN | DCSR_EORSTOPEN; +#ifdef RT_DMA_MMP_PDMA_SUPPORT_64BIT + /* Use long descriptor mode: set DCSR_LPAEEN bit */ + dcsr |= DCSR_LPAEEN; +#endif + HWREG32(pdma->base + reg) = dcsr; +} + +static void disable_chan(struct mmp_pdma_chan *chan) +{ + int idx; + rt_uint32_t reg, dcsr, cnt = 1000; + struct mmp_pdma_device *pdma = raw_to_mmp_pdma_device(chan->parent.ctrl); + + idx = chan - pdma->chan; + + reg = (idx << 2) + DCSR; + + dcsr = HWREG32(pdma->base + reg); + dcsr &= ~(DCSR_RUN | DCSR_EORIRQEN | DCSR_EORSTOPEN); +#ifdef RT_DMA_MMP_PDMA_SUPPORT_64BIT + /* Use long descriptor mode: set DCSR_LPAEEN bit */ + dcsr &= ~DCSR_LPAEEN; +#endif + HWREG32(pdma->base + reg) = dcsr; + + /* Ensure dma is stopped. */ + dcsr = HWREG32(pdma->base + reg); + while (!(dcsr & (0x1 << 3)) && --cnt) + { + rt_hw_us_delay(10); + dcsr = HWREG32(pdma->base + reg); + } +} + +static void mmp_pdma_config_write(struct mmp_pdma_chan *chan, + struct rt_dma_slave_config *cfg, + enum rt_dma_transfer_direction direction) +{ + rt_uint32_t maxburst = 0, addr = 0; + enum rt_dma_slave_buswidth width = RT_DMA_SLAVE_BUSWIDTH_UNDEFINED; + + if (direction == RT_DMA_DEV_TO_MEM) + { + chan->dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC; + maxburst = cfg->src_maxburst; + width = cfg->src_addr_width; + addr = cfg->src_addr; + } + else if (direction == RT_DMA_MEM_TO_DEV) + { + chan->dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG; + maxburst = cfg->dst_maxburst; + width = cfg->dst_addr_width; + addr = cfg->dst_addr; + } + + if (width == RT_DMA_SLAVE_BUSWIDTH_1_BYTE) + { + chan->dcmd |= DCMD_WIDTH1; + } + else if (width == RT_DMA_SLAVE_BUSWIDTH_2_BYTES) + { + chan->dcmd |= DCMD_WIDTH2; + } + else if (width == RT_DMA_SLAVE_BUSWIDTH_4_BYTES) + { + chan->dcmd |= DCMD_WIDTH4; + } + + if (maxburst == 8) + { + chan->dcmd |= DCMD_BURST8; + } + else if (maxburst == 16) + { + chan->dcmd |= DCMD_BURST16; + } + else if (maxburst == 32) + { + chan->dcmd |= DCMD_BURST32; + } + + chan->dir = direction; + chan->dev_addr = addr; +} + +static struct rt_dma_chan *mmp_pdma_request_chan(struct rt_dma_controller *ctrl, + struct rt_device *slave, void *fw_data) +{ + int drcmr; + struct mmp_pdma_chan *chan = RT_NULL; + struct mmp_pdma_device *pdma = raw_to_mmp_pdma_device(ctrl); + struct rt_ofw_cell_args *args = fw_data; + + drcmr = args ? args->args[0] : -1; + + for (int i = 0; i < pdma->dma_channels; ++i) + { + if (!pdma->chan[i].desc) + { + for (int j = 0; j < pdma->reserved_channels_nr; ++j) + { + if (pdma->reserved_channels[i].chan_id == i && + (pdma->reserved_channels[i].drcmr != drcmr || drcmr >= 0)) + { + goto _find_next; + } + } + + chan = &pdma->chan[i]; + break; + } + _find_next: + ; + } + + if (!chan) + { + return rt_err_ptr(-RT_EBUSY); + } + + chan->drcmr = drcmr; + chan->pausing = RT_FALSE; + chan->desc_nr = ARCH_PAGE_SIZE / sizeof(*chan->desc); + chan->desc = rt_dma_alloc_coherent(slave, ARCH_PAGE_SIZE, &chan->desc_dma); + + if (!chan->desc) + { + return rt_err_ptr(-RT_ENOMEM); + } + + return &chan->parent; +} + +static rt_err_t mmp_pdma_release_chan(struct rt_dma_chan *raw_chan) +{ + struct mmp_pdma_chan *chan = raw_to_mmp_pdma_device_chan(raw_chan); + + rt_dma_free_coherent(raw_chan->slave, ARCH_PAGE_SIZE, chan->desc, chan->desc_dma); + chan->desc = RT_NULL; + + return RT_EOK; +} + +static rt_err_t mmp_pdma_start(struct rt_dma_chan *raw_chan) +{ + struct mmp_pdma_chan *chan = raw_to_mmp_pdma_device_chan(raw_chan); + + if (!chan->pausing) + { + int idx; + rt_uint32_t reg; + struct mmp_pdma_device *pdma = raw_to_mmp_pdma_device(chan->parent.ctrl); + + idx = chan - pdma->chan; + reg = (idx << 4) + DDADR; + + HWREG32(pdma->base + reg) = chan->desc_dma & 0xffffffff; + #ifdef RT_DMA_MMP_PDMA_SUPPORT_64BIT + /* Config higher bits for desc address */ + HWREG32(pdma->base + DDADRH(idx)) = (rt_uint32_t)(chan->desc_dma >> 32); + #endif + } + + enable_chan(chan); + chan->bytes_residue = 0; + chan->pausing = RT_FALSE; + + return RT_EOK; +} + +static rt_err_t mmp_pdma_pause(struct rt_dma_chan *raw_chan) +{ + struct mmp_pdma_chan *chan = raw_to_mmp_pdma_device_chan(raw_chan); + + disable_chan(chan); + chan->pausing = RT_TRUE; + + return RT_EOK; +} + +static rt_err_t mmp_pdma_stop(struct rt_dma_chan *raw_chan) +{ + rt_uint32_t reg; + struct mmp_pdma_chan *chan = raw_to_mmp_pdma_device_chan(raw_chan); + struct mmp_pdma_device *pdma = raw_to_mmp_pdma_device(raw_chan->ctrl); + + disable_chan(chan); + + if (chan->drcmr >= 0) + { + /* Clear the channel mapping in DRCMR */ + reg = DRCMR(chan->drcmr); + HWREG32(pdma->base + reg) = 0; + } + + chan->bytes_residue = 0; + chan->pausing = RT_FALSE; + + return RT_EOK; +} + +static rt_err_t mmp_pdma_config(struct rt_dma_chan *raw_chan, + struct rt_dma_slave_config *conf) +{ + if (conf->src_addr_width == RT_DMA_SLAVE_BUSWIDTH_UNDEFINED) + { + conf->src_addr_width = RT_DMA_SLAVE_BUSWIDTH_4_BYTES; + } + if (conf->dst_addr_width == RT_DMA_SLAVE_BUSWIDTH_UNDEFINED) + { + conf->dst_addr_width = RT_DMA_SLAVE_BUSWIDTH_4_BYTES; + } + + return RT_EOK; +} + +static rt_err_t mmp_pdma_prep_memcpy(struct rt_dma_chan *raw_chan, + rt_ubase_t dma_addr_src, rt_ubase_t dma_addr_dst, rt_size_t len) +{ + int idx; + rt_size_t copy = 0; + rt_ubase_t desc_dma; + struct mmp_pdma_desc_hw *desc, *prev_desc; + struct mmp_pdma_chan *chan = raw_to_mmp_pdma_device_chan(raw_chan); + struct mmp_pdma_device *pdma = raw_to_mmp_pdma_device(raw_chan->ctrl); + + if (len > chan->desc_nr * PDMA_MAX_DESC_BYTES) + { + return -RT_EEMPTY; + } + + chan->byte_align = RT_FALSE; + + if (!chan->dir) + { + chan->dir = RT_DMA_MEM_TO_MEM; + chan->dcmd = DCMD_INCTRGADDR | DCMD_INCSRCADDR | get_max_burst_setting(pdma->max_burst_size); + } + + chan->size = len; + + for (idx = 0, desc_dma = chan->desc_dma; len; ++idx, desc_dma += sizeof(*desc)) + { + copy = rt_min_t(rt_size_t, len, PDMA_MAX_DESC_BYTES); + + if ((dma_addr_src & 0x7) || (dma_addr_dst & 0x7)) + { + chan->byte_align = RT_TRUE; + } + + desc = &chan->desc[idx]; + desc->dcmd = chan->dcmd | (DCMD_LENGTH & copy); + + /* + * Check whether descriptor/source-addr/target-addr is in + * region higher than 4G. If so, set related higher bits to 1. + */ + if (chan->dir == RT_DMA_MEM_TO_DEV) + { + desc->dsadr = dma_addr_src & 0xffffffff; + desc->dtadr = dma_addr_dst; + #ifdef RT_DMA_MMP_PDMA_SUPPORT_64BIT + desc->dsadrh = dma_addr_src >> 32; + desc->dtadrh = 0; + #endif + } + else if (chan->dir == RT_DMA_DEV_TO_MEM) + { + desc->dsadr = dma_addr_src; + desc->dtadr = dma_addr_dst & 0xffffffff; + #ifdef RT_DMA_MMP_PDMA_SUPPORT_64BIT + desc->dsadrh = 0; + desc->dtadrh = dma_addr_dst >> 32; + #endif + } + else if (chan->dir == RT_DMA_MEM_TO_MEM) + { + desc->dsadr = dma_addr_src & 0xffffffff; + desc->dtadr = dma_addr_dst & 0xffffffff; + #ifdef RT_DMA_MMP_PDMA_SUPPORT_64BIT + desc->dsadrh = dma_addr_src >> 32; + desc->dtadrh = dma_addr_dst >> 32; + #endif + } + + len -= copy; + + if (chan->dir == RT_DMA_MEM_TO_DEV) + { + dma_addr_src += copy; + } + else if (chan->dir == RT_DMA_DEV_TO_MEM) + { + dma_addr_dst += copy; + } + else if (chan->dir == RT_DMA_MEM_TO_MEM) + { + dma_addr_src += copy; + dma_addr_dst += copy; + } + + if (idx) + { + prev_desc = &chan->desc[idx - 1]; + + prev_desc->ddadr = desc_dma; + #ifdef RT_DMA_MMP_PDMA_SUPPORT_64BIT + prev_desc->ddadrh = desc_dma >> 32; + #endif + } + } + + prev_desc = &chan->desc[idx - 1]; + + prev_desc->ddadr = DDADR_STOP; + prev_desc->dcmd |= DCMD_ENDIRQEN; + + return RT_EOK; +} + +static rt_err_t mmp_pdma_prep_cyclic(struct rt_dma_chan *raw_chan, + rt_ubase_t dma_buf_addr, rt_size_t buf_len, rt_size_t period_len, + enum rt_dma_transfer_direction dir) +{ + int idx; + rt_ubase_t desc_dma; + rt_ubase_t dma_addr_src, dma_addr_dst; +#ifdef RT_DMA_MMP_PDMA_SUPPORT_64BIT + rt_ubase_t dma_addr_srch, dma_addr_dsth; +#endif + struct mmp_pdma_desc_hw *desc, *prev_desc; + struct mmp_pdma_chan *chan = raw_to_mmp_pdma_device_chan(raw_chan); + + if (buf_len % period_len != 0) + { + return -RT_EINVAL; + } + + if (period_len > PDMA_MAX_DESC_BYTES) + { + return -RT_EINVAL; + } + + if (buf_len > chan->desc_nr * PDMA_MAX_DESC_BYTES) + { + return -RT_EEMPTY; + } + + mmp_pdma_config_write(chan, &raw_chan->conf, dir); + + switch (dir) + { + case RT_DMA_MEM_TO_DEV: + dma_addr_src = dma_buf_addr & 0xffffffff; + dma_addr_dst = chan->dev_addr; + #ifdef RT_DMA_MMP_PDMA_SUPPORT_64BIT + dma_addr_srch = dma_buf_addr >> 32; + dma_addr_dsth = 0; + #endif + break; + + case RT_DMA_DEV_TO_MEM: + dma_addr_dst = dma_buf_addr & 0xffffffff; + dma_addr_src = chan->dev_addr; + #ifdef RT_DMA_MMP_PDMA_SUPPORT_64BIT + dma_addr_dsth = dma_buf_addr >> 32; + dma_addr_srch = 0; + #endif + break; + + default: + LOG_E("Unsupported direction for cyclic DMA"); + return -RT_EINVAL; + } + + chan->dir = dir; + chan->size = buf_len; + + for (idx = 0, desc_dma = chan->desc_dma; buf_len; ++idx, desc_dma += sizeof(*desc)) + { + desc = &chan->desc[idx]; + + desc->dcmd = chan->dcmd | DCMD_ENDIRQEN | (DCMD_LENGTH & period_len); + desc->dsadr = dma_addr_src; + desc->dtadr = dma_addr_dst; + #ifdef RT_DMA_MMP_PDMA_SUPPORT_64BIT + desc->dsadrh = dma_addr_dsth; + desc->dtadrh = dma_addr_srch; + #endif + + buf_len -= period_len; + + if (chan->dir == RT_DMA_MEM_TO_DEV) + { + dma_addr_src += period_len; + } + else + { + dma_addr_dst += period_len; + } + + if (idx) + { + prev_desc = &chan->desc[idx - 1]; + + prev_desc->ddadr = desc_dma; + #ifdef RT_DMA_MMP_PDMA_SUPPORT_64BIT + prev_desc->ddadrh = desc_dma >> 32; + #endif + } + }; + + prev_desc = &chan->desc[idx - 1]; + + /* Make the cyclic link */ + prev_desc->ddadr = chan->desc_dma; + + return RT_EOK; +} + +static rt_err_t mmp_pdma_prep_single(struct rt_dma_chan *raw_chan, + rt_ubase_t dma_buf_addr, rt_size_t buf_len, + enum rt_dma_transfer_direction dir) +{ + int idx; + rt_size_t copy = 0; + rt_ubase_t desc_dma; + struct mmp_pdma_desc_hw *desc, *prev_desc; + struct mmp_pdma_chan *chan = raw_to_mmp_pdma_device_chan(raw_chan); + + if (buf_len > chan->desc_nr * PDMA_MAX_DESC_BYTES) + { + return -RT_EEMPTY; + } + + chan->byte_align = RT_TRUE; + + mmp_pdma_config_write(chan, &raw_chan->conf, dir); + + chan->dir = dir; + chan->size = buf_len; + + for (idx = 0, desc_dma = chan->desc_dma; buf_len; ++idx, desc_dma += sizeof(*desc)) + { + copy = rt_min_t(rt_size_t, buf_len, PDMA_MAX_DESC_BYTES); + + if (dma_buf_addr & 0x7) + { + chan->byte_align = RT_TRUE; + } + + desc = &chan->desc[idx]; + desc->dcmd = chan->dcmd | (DCMD_LENGTH & copy); + + /* + * Check whether descriptor/source-addr/target-addr is in + * region higher than 4G. If so, set related higher bits to 1. + */ + if (dir == RT_DMA_MEM_TO_DEV) + { + desc->dsadr = dma_buf_addr & 0xffffffff; + desc->dtadr = chan->dev_addr; + #ifdef RT_DMA_MMP_PDMA_SUPPORT_64BIT + desc->dsadrh = dma_buf_addr >> 32; + desc->dtadrh = 0; + #endif + } + else if (dir == RT_DMA_DEV_TO_MEM) + { + desc->dsadr = chan->dev_addr; + desc->dtadr = dma_buf_addr & 0xffffffff; + #ifdef RT_DMA_MMP_PDMA_SUPPORT_64BIT + desc->dsadrh = 0; + desc->dtadrh = dma_buf_addr >> 32; + #endif + } + else + { + return -RT_EINVAL; + } + + dma_buf_addr += copy; + buf_len -= copy; + + if (idx) + { + prev_desc = &chan->desc[idx - 1]; + + prev_desc->ddadr = desc_dma; + #ifdef RT_DMA_MMP_PDMA_SUPPORT_64BIT + prev_desc->ddadrh = desc_dma >> 32; + #endif + } + } + + prev_desc = &chan->desc[idx - 1]; + + prev_desc->ddadr = DDADR_STOP; + prev_desc->dcmd |= DCMD_ENDIRQEN; + + return RT_EOK; +} + +static const struct rt_dma_controller_ops mmp_pdma_ops = +{ + .request_chan = mmp_pdma_request_chan, + .release_chan = mmp_pdma_release_chan, + .start = mmp_pdma_start, + .pause = mmp_pdma_pause, + .stop = mmp_pdma_stop, + .config = mmp_pdma_config, + .prep_memcpy = mmp_pdma_prep_memcpy, + .prep_cyclic = mmp_pdma_prep_cyclic, + .prep_single = mmp_pdma_prep_single, +}; + +static void mmp_pdma_isr(int irq, void *param) +{ + rt_uint32_t i, dint, dcsr; + struct mmp_pdma_chan *chan; + struct mmp_pdma_device *pdma = param; + + dint = HWREG32(pdma->base + DINT); + + while (dint) + { + if ((i = __rt_ffs(dint) - 1) >= pdma->dma_channels) + { + break; + } + + chan = &pdma->chan[i]; + + /* Clear IRQ */ + dcsr = HWREG32(pdma->base + (i << 2) + DCSR); + HWREG32(pdma->base + (i << 2) + DCSR) = dcsr; + + rt_dma_chan_done(&chan->parent, chan->size); + + dint &= (dint - 1); + } +} + +#ifdef RT_USING_PM +static rt_err_t mmp_pdma_pm_suspend(const struct rt_device *device, rt_uint8_t mode) +{ + struct mmp_pdma_device *pdma = device->user_data; + + rt_clk_disable_unprepare(pdma->clk); + + return RT_EOK; +} + +static void mmp_pdma_pm_resume(const struct rt_device *device, rt_uint8_t mode) +{ + struct mmp_pdma_device *pdma = device->user_data; + + rt_clk_prepare_enable(pdma->clk); +} + +static const struct rt_device_pm_ops mmp_pdma_pm_ops = +{ + .suspend = mmp_pdma_pm_suspend, + .resume = mmp_pdma_pm_resume, +}; +#endif /* RT_USING_PM */ + +static void mmp_pdma_free(struct mmp_pdma_device *pdma, struct rt_device *dev) +{ + if (pdma->base) + { + rt_iounmap(pdma->base); + } + + if (!rt_is_err_or_null(pdma->clk)) + { + rt_clk_disable_unprepare(pdma->clk); + rt_clk_put(pdma->clk); + } + + if (!rt_is_err_or_null(pdma->rstc)) + { + rt_reset_control_put(pdma->rstc); + } + + if (pdma->reserved_channels) + { + rt_free(pdma->reserved_channels); + } + + if (pdma->chan) + { + rt_free(pdma->chan); + } + + rt_free(pdma); +} + +static rt_err_t mmp_pdma_probe(struct rt_platform_device *pdev) +{ + rt_err_t err; + struct rt_device *dev = &pdev->parent; + struct mmp_pdma_device *pdma = rt_calloc(1, sizeof(*pdma)); + + if (!pdma) + { + return -RT_ENOMEM; + } + + if (!(pdma->base = rt_dm_dev_iomap(dev, 0))) + { + err = -RT_EIO; + goto _fail; + } + + if ((pdma->irq = rt_dm_dev_get_irq(dev, 0)) < 0) + { + err = pdma->irq; + goto _fail; + } + + pdma->clk = rt_clk_get_by_index(dev, 0); + if (rt_is_err(pdma->clk)) + { + err = rt_ptr_err(pdma->clk); + goto _fail; + } + + if ((err = rt_clk_prepare_enable(pdma->clk))) + { + goto _fail; + } + + pdma->rstc = rt_reset_control_get_by_index(dev, 0); + if (rt_is_err(pdma->rstc)) + { + err = rt_ptr_err(pdma->rstc); + goto _fail; + } + + if ((err = rt_reset_control_deassert(pdma->rstc))) + { + goto _fail; + } + + if ((err = rt_dm_dev_prop_read_u32(dev, "#dma-channels", &pdma->dma_channels))) + { + goto _fail; + } + + if (rt_dm_dev_prop_read_u32(dev, "max-burst-size", &pdma->max_burst_size) || + get_max_burst_setting(pdma->max_burst_size) == INVALID_BURST_SETTING) + { + pdma->max_burst_size = DEFAULT_MAX_BURST_SIZE; + } + + pdma->reserved_channels_nr = rt_dm_dev_prop_count_of_size(dev, + "reserved-channels", sizeof(rt_uint32_t) * 2); + + if (pdma->reserved_channels_nr > 0) + { + pdma->reserved_channels = rt_calloc(pdma->reserved_channels_nr, sizeof(struct reserved_chan)); + + if (!pdma->reserved_channels) + { + err = -RT_ENOMEM; + goto _fail; + } + + for (int i = 0; i < pdma->reserved_channels_nr; ++i) + { + rt_dm_dev_prop_read_u32_index(dev, "reserved-channels", i * 2, + &pdma->reserved_channels[i].chan_id); + rt_dm_dev_prop_read_u32_index(dev, "reserved-channels", i * 2 + 1, + &pdma->reserved_channels[i].drcmr); + } + } + + if (!(pdma->chan = rt_calloc(pdma->dma_channels, sizeof(*pdma->chan)))) + { + err = -RT_ENOMEM; + goto _fail; + } + + pdma->parent.dev = dev; + pdma->parent.ops = &mmp_pdma_ops; + rt_dma_controller_add_direction(&pdma->parent, RT_DMA_MEM_TO_MEM); + rt_dma_controller_add_direction(&pdma->parent, RT_DMA_MEM_TO_DEV); + rt_dma_controller_add_direction(&pdma->parent, RT_DMA_DEV_TO_MEM); +#ifdef RT_DMA_MMP_PDMA_SUPPORT_64BIT + rt_dma_controller_set_addr_mask(&pdma->parent, RT_DMA_ADDR_MASK(64)); +#else + rt_dma_controller_set_addr_mask(&pdma->parent, RT_DMA_ADDR_MASK(32)); +#endif + + if ((err = rt_dma_controller_register(&pdma->parent))) + { + goto _fail; + } + + rt_hw_interrupt_install(pdma->irq, mmp_pdma_isr, pdma, "pdma"); + rt_hw_interrupt_umask(pdma->irq); + + dev->user_data = pdma; + +#ifdef RT_USING_PM + rt_pm_device_register(dev, &mmp_pdma_pm_ops); +#endif + + return RT_EOK; + +_fail: + mmp_pdma_free(pdma, dev); + + return err; +} + +static rt_err_t mmp_pdma_remove(struct rt_platform_device *pdev) +{ + struct rt_device *dev = &pdev->parent; + struct mmp_pdma_device *pdma = dev->user_data; + +#ifdef RT_USING_PM + rt_pm_device_unregister(dev); +#endif + + rt_dma_controller_unregister(&pdma->parent); + + rt_hw_interrupt_mask(pdma->irq); + rt_pic_detach_irq(pdma->irq, pdma); + + rt_reset_control_assert(pdma->rstc); + + mmp_pdma_free(pdma, dev); + + return RT_EOK; +} + +static const struct rt_ofw_node_id mmp_pdma_ofw_ids[] = +{ + { .compatible = "spacemit,pdma-1.0" }, + { /* sentinel */ } +}; + +static struct rt_platform_driver mmp_pdma_driver = +{ + .name = "dma-mmp-pdma", + .ids = mmp_pdma_ofw_ids, + + .probe = mmp_pdma_probe, + .remove = mmp_pdma_remove, +}; + +static int mmp_pdma_drv_register(void) +{ + rt_platform_driver_register(&mmp_pdma_driver); + + return 0; +} +INIT_SUBSYS_EXPORT(mmp_pdma_drv_register); diff --git a/bsp/spacemit/dm/firmware/SConscript b/bsp/spacemit/dm/firmware/SConscript new file mode 100755 index 00000000000..4cf3fd818f6 --- /dev/null +++ b/bsp/spacemit/dm/firmware/SConscript @@ -0,0 +1,12 @@ +from building import * + +group = [] +src = [] +cwd = GetCurrentDir() +CPPPATH = [cwd + '/../include'] + +src += ['firmware-spacemit.c'] + +group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') diff --git a/bsp/spacemit/dm/firmware/firmware-spacemit.c b/bsp/spacemit/dm/firmware/firmware-spacemit.c new file mode 100755 index 00000000000..0593e8a1113 --- /dev/null +++ b/bsp/spacemit/dm/firmware/firmware-spacemit.c @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2006-2024, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2024-5-1 GuEe-GUI first version + */ + +#include +#include + +#include +#include + +rt_weak rt_ubase_t spacemit_firmwares_start, spacemit_firmwares_end; + +rt_bool_t spacemit_firmware_find(const char *name, rt_ubase_t *out_base, rt_size_t *out_size) +{ + rt_ubase_t *start = &spacemit_firmwares_start, *end = &spacemit_firmwares_end; + + while (start < end) + { + if (!rt_strcmp((const char *)(*start), name)) + { + ++start; + + *out_base = *start++; + *out_size = *start++; + *out_size -= *out_base; + + return RT_TRUE; + } + + start += 3; + } + + return RT_FALSE; +} + +rt_err_t spacemit_firmware_load_elf(const char *name, rt_ubase_t phy_addr) +{ + void *vma; + rt_ubase_t base, size, load_base, seg_phys, seg_src, memsz, filesz, copy, chunk; + Elf32_Ehdr *ehdr; + Elf32_Phdr *phdr; + const rt_ubase_t MAX_IOMAP_SIZE = 128 * SIZE_MB; + + if (!spacemit_firmware_find(name, &base, &size)) + { + return -RT_EEMPTY; + } + + ehdr = (Elf32_Ehdr *)base; + + if (rt_memcmp(ehdr->e_ident, ELFMAG, SELFMAG) != 0) + { + return -RT_EINVAL; + } + + if (ehdr->e_ident[EI_CLASS] != ELFCLASS32) + { + return -RT_EINVAL; + } + + if (ehdr->e_phoff == 0 || ehdr->e_phnum == 0) + { + return -RT_EINVAL; + } + + phdr = (Elf32_Phdr *)(base + ehdr->e_phoff); + load_base = phy_addr - ehdr->e_entry; + + for (int i = 0; i < ehdr->e_phnum; ++i) + { + Elf32_Phdr *p = &phdr[i]; + + if (p->p_type != PT_LOAD) + { + continue; + } + + if (p->p_offset + p->p_filesz > size) + { + return -RT_EINVAL; + } + + seg_phys = load_base + p->p_paddr; + seg_src = base + p->p_offset; + memsz = p->p_memsz; + filesz = p->p_filesz; + + while (memsz > 0) + { + chunk = rt_min_t(rt_ubase_t, memsz, MAX_IOMAP_SIZE); + + if (!(vma = rt_ioremap((void *)seg_phys, chunk))) + { + return -RT_ENOMEM; + } + + copy = filesz > chunk ? chunk : filesz; + + if (copy > 0) + { + rt_memcpy((void *)vma, (const void *)seg_src, copy); + seg_src += copy; + filesz -= copy; + } + + if (chunk > copy) + { + rt_memset((void *)((rt_ubase_t)vma + copy), 0, chunk - copy); + } + + rt_iounmap(vma); + + seg_phys += chunk; + memsz -= chunk; + } + } + + return RT_EOK; +} diff --git a/bsp/spacemit/dm/hwcrypto/Kconfig b/bsp/spacemit/dm/hwcrypto/Kconfig new file mode 100755 index 00000000000..77c12e09997 --- /dev/null +++ b/bsp/spacemit/dm/hwcrypto/Kconfig @@ -0,0 +1,17 @@ +config RT_HWCRYPTO_SPACEMIT + bool "Spacemit Crypto Engine" + depends on RT_USING_CLK + depends on RT_USING_RESET + depends on RT_USING_HWCRYPTO + depends on RT_HWCRYPTO_USING_AES + depends on RT_HWCRYPTO_USING_AES_ECB + depends on RT_HWCRYPTO_USING_AES_CBC + default y + +config RT_HWCRYPTO_RNG_SPACEMIT + bool "Spacemit RNG Engine" + depends on RT_USING_CLK + depends on RT_USING_RESET + depends on RT_USING_HWCRYPTO + depends on RT_HWCRYPTO_USING_RNG + default y diff --git a/bsp/spacemit/dm/hwcrypto/SConscript b/bsp/spacemit/dm/hwcrypto/SConscript new file mode 100755 index 00000000000..d6348b17720 --- /dev/null +++ b/bsp/spacemit/dm/hwcrypto/SConscript @@ -0,0 +1,15 @@ +from building import * + +cwd = GetCurrentDir() +src = [] +CPPPATH = [cwd + '/../include'] + +if GetDepend(['RT_HWCRYPTO_SPACEMIT']): + src += ['hw-crypto-spacemit.c'] + +if GetDepend(['RT_HWCRYPTO_RNG_SPACEMIT']): + src += ['hw-rng-spacemit.c'] + +group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') diff --git a/bsp/spacemit/dm/hwcrypto/hw-crypto-spacemit.c b/bsp/spacemit/dm/hwcrypto/hw-crypto-spacemit.c new file mode 100755 index 00000000000..5217d7e0c2e --- /dev/null +++ b/bsp/spacemit/dm/hwcrypto/hw-crypto-spacemit.c @@ -0,0 +1,1189 @@ +/* + * Copyright (c) 2006-2024, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2024-5-1 GuEe-GUI first version + */ + +#include +#include + +#include "spacemit.h" + +#define SPACEMIT_AES_BUFFER_LEN (1024 * 256) + +#define WORK_BUF_SIZE 2048 +#define CTR_COUNTER_LITTLE_ENDIAN 0x0000 +#define CTR_COUNTER_BIG_ENDIAN 0x1000 +#define BYTES_TO_BITS 8 +#define SPACEMIT_SECENG_SIZE 0x3000 + +#define ENGINE_DMA_ADDR_HIGH_OFFSET 0x14c +#define SW_RESETN RT_BIT(0) +#define MASTER_CLK_EN RT_BIT(1) +#define SLAVE_CLK_EN RT_BIT(2) +#define WADDR_BIT32 RT_BIT(4) +#define RADDR_BIT32 RT_BIT(5) + +#define CE_BIU_REG_OFFSET 0x00000000L +#define CE_ADEC_REG_OFFSET 0x00000400L +#define CE_DMA_REG_OFFSET 0x00000800L +#define CE_ABUS_REG_OFFSET 0x00000c00L +#define CE_CRYPTO_REG_OFFSET 0x00001000L +#define CE_HASH_REG_OFFSET 0x00001800L + +#define CE_ADEC_CTRL 0x0000 +#define CE_ADEC_CTRL2 0x0004 +#define CE_AXI_SL_CTRL 0x0008 +#define CE_ADEC_INT 0x000c +#define CE_ADEC_INT_MSK 0x0010 +#define CE_ADEC_ACC_ERR_ADR 0x0014 +#define CE_ADEC_MP_FIFO_ERR_ADR 0x0018 + +#define CE_ABUS_BUS_CTRL 0x0000 + +#define SP_HST_INTERRUPT_MASK 0x0cc +#define SP_INTERRUPT_RST 0x218 +#define SP_INTERRUPT_MASK 0x21c +#define SP_CONTROL 0x220 + +#define CE_HASH_CONFIG_REG 0x0000 +#define CE_HASH_CONTROL_REG 0x0004 +#define CE_HASH_COMMAND_REG 0x0008 +#define CE_HASH_STATUS_REG 0x000c +#define CE_HASH_INCOME_SEG_SZ_REG 0x0010 +#define CE_HASH_TOTAL_MSG_SZ_L_REG 0x0018 +#define CE_HASH_TOTAL_MSG_SZ_H_REG 0x001c +#define CE_HASH_DIGEST_BASE 0x0020 +#define CE_HASH_DIGEST_REG(a) (CE_HASH_DIGEST_BASE + (a << 2)) +#define CE_HASH_DIGEST_H_BASE 0x0040 +#define CE_HASH_DIGEST_H_REG(a) (CE_HASH_DIGEST_H_BASE + (a << 2)) +#define CE_HASH_CONTEXTO_BASE 0x0064 +#define CE_HASH_CONTEXTO_REG(a) (CE_HASH_CONTEXTO_BASE + (a << 2)) +#define CE_HASH_CONTEXTO_H_BASE 0x0080 +#define CE_HASH_CONTEXTO_H_REG(a) (CE_HASH_CONTEXTO_H_BASE + (a << 2)) +#define CE_HASH_KEY_BASE 0x00a4 +#define CE_HASH_KEY_REG(a) (CE_HASH_KEY_BASE + (a << 2)) + +#define CE_DMA_IN_CTRL 0x0000 +#define CE_DMA_IN_STATUS 0x0004 +#define CE_DMA_IN_SRC_ADR 0x0008 +#define CE_DMA_IN_XFER_CNTR 0x000c +#define CE_DMA_IN_NX_LL_ADR 0x0010 +#define CE_DMA_IN_INT 0x0014 +#define CE_DMA_IN_INT_MASK 0x0018 +#define CE_DMA_OUT_CTRL 0x001c +#define CE_DMA_OUT_STATUS 0x0020 +#define CE_DMA_OUT_DEST_ADR 0x0024 +#define CE_DMA_OUT_XFER_CNTR 0x0028 +#define CE_DMA_OUT_NX_LL_ADR 0x002c +#define CE_DMA_OUT_INT 0x0030 +#define CE_DMA_OUT_INT_MASK 0x0034 +#define CE_DMA_AXI_CTRL 0x0038 +#define CE_DMA_IF_RCOUNT 0x003c +#define CE_DMA_IF_RD_PTR_ERR 0x0040 +#define CE_DMA_OF_SPACE 0x0044 +#define CE_DMA_OF_RD_PTR_ERR 0x0048 +#define CE_DMA_IF_RAM_BASE 0x0100 +#define CE_DMA_IF_RAM_REG(a) (CE_DMA_IF_RAM_BASE + a*0x4) +#define CE_DMA_OF_RAM_BASE 0x0300 +#define CE_DMA_OF_RAM_REG(a) (CE_DMA_OF_RAM_BASE + a*0x4) + +#define CE_BIU_HST_INTERRUPT_MASK 0x00cc +#define CE_BIU_SP_INTERRUPT_MASK 0x021c +#define CE_BIU_SP_CONTROL 0x0220 + +#define CE_CRYPTO_AES_CONFIG_REG 0x0000 +#define CE_CRYPTO_AES_CONTROL_REG 0x0004 +#define CE_CRYPTO_AES_COMMAND_REG 0x0008 +#define CE_CRYPTO_AES_STATUS_REG 0x000c +#define CE_CRYPTO_AES_INTRPT_SRC_REG 0x0010 +#define CE_CRYPTO_AES_INTRPT_SRC_EN_REG 0x0014 +#define CE_CRYPTO_AES_STREAM_SIZE_REG 0x0018 +#define CE_CRYPTO_ENGINE_SEL_REG 0x00a8 + +#define CE_CRYPTO_K2_BASE 0x0058 +#define CE_CRYPTO_K2_W_REG(a) (CE_CRYPTO_K2_BASE + a * 0x4) +#define CE_CRYPTO_K1_BASE 0x0078 +#define CE_CRYPTO_K1_W_REG(a) (CE_CRYPTO_K1_BASE + a*0x4) +#define CE_CRYPTO_IV_BASE 0x0098 +#define CE_CRYPTO_IV_REG(a) (CE_CRYPTO_IV_BASE + a*0x4) + +#define AES_INTERRUPT_FLAG RT_BIT(0) +#define AES_ERR1_INTERRUPT_FLAG RT_BIT(1) +#define AES_ERR2_INTERRUPT_FLAG RT_BIT(2) +#define AES_INTERRUPT_MASK (AES_INTERRUPT_FLAG | AES_ERR1_INTERRUPT_FLAG | \ + AES_ERR2_INTERRUPT_FLAG) + +#define BIT_DMA_INOUT_DONE RT_BIT(0) +#define BIT_DMA_INOUT_BUS_ERR RT_BIT(1) +#define BIT_DMA_INOUT_LL_ERR RT_BIT(2) +#define BIT_DMA_INOUT_PAR_ERR RT_BIT(3) +#define BIT_DMA_INOUT_PAUSE_CMPL_ERR RT_BIT(4) +#define BIT_DMA_INOUT_DATA_PAR_ERR RT_BIT(5) +#define DMA_INTERRUPT_MASK (BIT_DMA_INOUT_DONE | BIT_DMA_INOUT_BUS_ERR | \ + BIT_DMA_INOUT_LL_ERR | BIT_DMA_INOUT_PAR_ERR | \ + BIT_DMA_INOUT_PAUSE_CMPL_ERR | BIT_DMA_INOUT_DATA_PAR_ERR) + +#define BIU_MASK RT_BIT(0) +#define ADEC_MASK (RT_BIT(1) | RT_BIT(5)) + +#define AES_BLOCK_SIZE 16 + +#define ENGINE_MAX 2 + +enum adec_acc_eng +{ + /* Reset bit */ + E_ACC_ENG_DMA = 1, + E_ACC_ENG_HASH = 5, + E_ACC_ENG_CRYPTO = 3, + E_ACC_ENG_ALL, +}; + +enum abus_grp_a +{ + E_ABUS_GRP_A_HASH = 0x0, +}; + +enum abus_grp_b +{ + E_ABUS_GRP_B_AES = 0x0, + E_ABUS_GRP_B_BYPASS = 0x2, +}; + +enum abus_cross_bar +{ + E_ABUS_STRAIGHT = 0, + E_ABUS_CROSS, +}; + +enum crypto_eng_sel +{ + E_ENG_AES = 0, +}; + +enum crypto_aes_status +{ + AES_INVALID, + AES_DONE, + AES_ERROR +}; + +enum crypto_dma_status +{ + DMA_INVALID, + DMA_INOUT_DONE, + DMA_INOUT_ERROR +}; + +struct spacemit_crypto; + +struct spacemit_crypto_engine +{ + struct rt_hwcrypto_device parent; + + struct spacemit_crypto *crypto; + + int irq; + void *base; + + void *in_buffer, *out_buffer; + rt_ubase_t in_buffer_dma, out_buffer_dma; + + enum crypto_aes_status aes_status; + enum crypto_dma_status dma_in_status; + enum crypto_dma_status dma_out_status; + + struct rt_completion aes_done; + struct rt_completion dma_output_done; + struct rt_completion dma_input_done; + + struct rt_mutex lock; + + rt_uint8_t internal_working_buffer[WORK_BUF_SIZE * 2] rt_align(32); +}; + +struct spacemit_crypto +{ + struct rt_clk *clk; + struct rt_reset_control *rstc; + struct rt_syscon *ciu_regmap; + + rt_uint32_t engine_nr; + struct spacemit_crypto_engine engine[ENGINE_MAX]; +}; + +#define raw_to_spacemit_crypto_engine(raw) rt_container_of((raw)->parent.device, struct spacemit_crypto_engine, parent) + +static void dma_write32(struct spacemit_crypto_engine *ce, int offset, rt_uint32_t value) +{ + tcm_override_writel(ce->base + CE_DMA_REG_OFFSET + offset, value); +} + +static rt_uint32_t dma_read32(struct spacemit_crypto_engine *ce, int offset) +{ + return tcm_override_readl(ce->base + CE_DMA_REG_OFFSET + offset); +} + +static void biu_write32(struct spacemit_crypto_engine *ce, int offset, rt_uint32_t value) +{ + tcm_override_writel(ce->base + CE_BIU_REG_OFFSET + offset, value); +} + +static rt_uint32_t biu_read32(struct spacemit_crypto_engine *ce, int offset) +{ + return tcm_override_readl(ce->base + CE_BIU_REG_OFFSET + offset); +} + +static void adec_write32(struct spacemit_crypto_engine *ce, int offset, rt_uint32_t value) +{ + tcm_override_writel(ce->base + CE_ADEC_REG_OFFSET + offset, value); +} + +static rt_uint32_t adec_read32(struct spacemit_crypto_engine *ce, int offset) +{ + return tcm_override_readl(ce->base + CE_ADEC_REG_OFFSET + offset); +} + +static void abus_write32(struct spacemit_crypto_engine *ce, int offset, rt_uint32_t value) +{ + tcm_override_writel(ce->base + CE_ABUS_REG_OFFSET + offset, value); +} + +static rt_uint32_t abus_read32(struct spacemit_crypto_engine *ce, int offset) +{ + return tcm_override_readl(ce->base + CE_ABUS_REG_OFFSET + offset); +} + +static void crypto_write32(struct spacemit_crypto_engine *ce, int offset, rt_uint32_t value) +{ + tcm_override_writel(ce->base + CE_CRYPTO_REG_OFFSET + offset, value); +} + +static rt_uint32_t crypto_read32(struct spacemit_crypto_engine *ce, int offset) +{ + return tcm_override_readl(ce->base + CE_CRYPTO_REG_OFFSET + offset); +} + +static void dma_output_config(struct spacemit_crypto_engine *ce, int wid_ext, int wid) +{ + rt_uint32_t val; + + val = dma_read32(ce, CE_DMA_OUT_CTRL); + val &= 0x0f0f0000; + val |= (0x7 << 28) | /* Dis error check */ + ((wid_ext & 0xf) << 20) | /* Rid ext */ + (0x1 << 18) | /* Dis out-of-order */ + (0x1 << 17) | /* Data 64 Byte aligned */ + (0x1 << 15) | /* FIFO bus size 64bit */ + (0x1 << 13) | /* Burst type: Inc */ + (0x8 << 8) | /* Burst len */ + ((wid & 0xf) << 4); + + dma_write32(ce, CE_DMA_OUT_CTRL, val); +} + +static void dma_output_address(struct spacemit_crypto_engine *ce, + rt_uint32_t dst_addr, rt_uint32_t dst_size, rt_bool_t chained) +{ + if (chained) + { + dma_write32(ce, CE_DMA_OUT_NX_LL_ADR, dst_addr); + dma_write32(ce, CE_DMA_OUT_DEST_ADR, 0x0); + dma_write32(ce, CE_DMA_OUT_XFER_CNTR, 0x0); + } + else + { + dma_write32(ce, CE_DMA_OUT_NX_LL_ADR, 0x0); + dma_write32(ce, CE_DMA_OUT_DEST_ADR, dst_addr); + dma_write32(ce, CE_DMA_OUT_XFER_CNTR, dst_size); + } +} + +static void dma_output_start(struct spacemit_crypto_engine *ce) +{ + rt_uint32_t val; + + val = dma_read32(ce, CE_DMA_OUT_INT); + dma_write32(ce, CE_DMA_OUT_INT, val); + + val = dma_read32(ce, CE_DMA_OUT_CTRL); + val |= 0x1; + dma_write32(ce, CE_DMA_OUT_CTRL, val); +} + +static void dma_output_stop(struct spacemit_crypto_engine *ce) +{ + rt_uint32_t val; + + val = dma_read32(ce, CE_DMA_OUT_CTRL); + val &= ~0x1; + dma_write32(ce, CE_DMA_OUT_CTRL, val); +} + +static void dma_input_config(struct spacemit_crypto_engine *ce, int rid_ext, int rid) +{ + rt_uint32_t val; + + val = dma_read32(ce, CE_DMA_IN_CTRL); + val &= 0x0f0f0000; + val |= (0x7 << 28) | /* Dis error check */ + ((rid_ext & 0xf) << 20) | /* Rid ext */ + (0x1 << 18) | /* Dis out-of-order */ + (0x1 << 17) | /* Data 64 Byte aligned */ + (0x1 << 15) | /* FIFO bus size 64bit */ + (0x1 << 13) | /* Burst type: Inc */ + (0x8 << 8) | /* Burst len */ + ((rid & 0xf) << 4); + + dma_write32(ce, CE_DMA_IN_CTRL, val); +} + +static void dma_input_address(struct spacemit_crypto_engine *ce, + rt_uint32_t src_addr, rt_uint32_t src_size, rt_bool_t chained) +{ + if (chained) + { + dma_write32(ce, CE_DMA_IN_NX_LL_ADR, src_addr); + dma_write32(ce, CE_DMA_IN_SRC_ADR, 0x0); + dma_write32(ce, CE_DMA_IN_XFER_CNTR, 0x0); + } + else + { + dma_write32(ce, CE_DMA_IN_NX_LL_ADR, 0x0); + dma_write32(ce, CE_DMA_IN_SRC_ADR, src_addr); + dma_write32(ce, CE_DMA_IN_XFER_CNTR, src_size); + } +} + +static void dma_input_start(struct spacemit_crypto_engine *ce) +{ + rt_uint32_t val; + + val = dma_read32(ce, CE_DMA_IN_INT); + dma_write32(ce, CE_DMA_IN_INT, val); + + val = dma_read32(ce, CE_DMA_IN_CTRL); + val |= 0x1; + dma_write32(ce, CE_DMA_IN_CTRL, val); +} + +static void dma_input_stop(struct spacemit_crypto_engine *ce) +{ + rt_uint32_t val; + + val = dma_read32(ce, CE_DMA_IN_CTRL); + val &= ~0x1; + dma_write32(ce, CE_DMA_IN_CTRL, val); +} + +static rt_err_t dma_wait_int_input_finish(struct spacemit_crypto_engine *ce) +{ + rt_completion_wait(&ce->dma_input_done, RT_WAITING_FOREVER); + + if (ce->dma_in_status != DMA_INOUT_DONE) + { + return -RT_EINVAL; + } + + return RT_EOK; +} + +static rt_err_t dma_wait_int_output_finish(struct spacemit_crypto_engine *ce) +{ + rt_completion_wait(&ce->dma_output_done, RT_WAITING_FOREVER); + + if (ce->dma_out_status != DMA_INOUT_DONE) + { + return -RT_EINVAL; + } + + return RT_EOK; +} + +static void adec_biu_clear_int_flag(struct spacemit_crypto_engine *ce) +{ + volatile rt_uint32_t val; + + val = adec_read32(ce, CE_ADEC_INT); + adec_write32(ce, CE_ADEC_INT, val); + + val = biu_read32(ce, SP_INTERRUPT_RST); + biu_write32(ce, SP_INTERRUPT_RST, val); +} + +static rt_err_t adec_engine_hw_reset(struct spacemit_crypto_engine *ce, enum adec_acc_eng engine) +{ + rt_uint32_t val, tmp; + + if (engine == E_ACC_ENG_ALL) + { + tmp = 0xffff; + } + else + { + tmp = 1 << engine; + } + + val = adec_read32(ce, CE_ADEC_CTRL); + val |= tmp; + adec_write32(ce, CE_ADEC_CTRL, val); + val &= ~tmp; + adec_write32(ce, CE_ADEC_CTRL, val); + + if (engine == E_ACC_ENG_DMA) + { + rt_syscon_update_bits(ce->crypto->ciu_regmap, + ENGINE_DMA_ADDR_HIGH_OFFSET, WADDR_BIT32 | RADDR_BIT32, 0); + } + + return RT_EOK; +} + +static rt_err_t abus_set_mode(struct spacemit_crypto_engine *ce, + enum abus_grp_a grp_a_mode, enum abus_grp_b grp_b_mode, + enum abus_cross_bar input_bar, enum abus_cross_bar output_bar) +{ + uint32_t val; + + val = abus_read32(ce, CE_ABUS_BUS_CTRL); + + val &= ~(0x77 << 0x4); + val |= (grp_a_mode << 0x4) | (grp_b_mode << 0x8); + + if (input_bar == E_ABUS_STRAIGHT) + { + val &= ~(0x1 << 0x0); + } + else if (input_bar == E_ABUS_CROSS) + { + val |= 0x1 << 0x0; + } + else + { + return -RT_EINVAL; + } + + if (output_bar == E_ABUS_STRAIGHT) + { + val &= ~(0x1 << 0x2); + } + else if (output_bar == E_ABUS_CROSS) + { + val |= 0x1 << 0x2; + } + else + { + return -RT_EINVAL; + } + + abus_write32(ce, CE_ABUS_BUS_CTRL, val); + + return RT_EOK; +} + +static void enable_biu_mask(struct spacemit_crypto_engine *ce) +{ + rt_uint32_t val; + val = biu_read32(ce, SP_INTERRUPT_MASK); + val &=~BIU_MASK; + biu_write32(ce, SP_INTERRUPT_MASK, val); +} + +static void enable_adec_mask(struct spacemit_crypto_engine *ce) +{ + rt_uint32_t val; + val = adec_read32(ce, CE_ADEC_INT_MSK); + val &=~ADEC_MASK; + adec_write32(ce, CE_ADEC_INT_MSK, val); +} + +static void enable_engine_irq(struct spacemit_crypto_engine *ce) +{ + rt_uint32_t val; + + /* Clear aes INT */ + val = crypto_read32(ce, CE_CRYPTO_AES_INTRPT_SRC_REG); + crypto_write32(ce, CE_CRYPTO_AES_INTRPT_SRC_REG, val); + + val = crypto_read32(ce, CE_CRYPTO_AES_INTRPT_SRC_EN_REG); + val |= AES_INTERRUPT_MASK; + crypto_write32(ce, CE_CRYPTO_AES_INTRPT_SRC_EN_REG, val); + + val = dma_read32(ce, CE_DMA_OUT_INT_MASK); + val &= ~DMA_INTERRUPT_MASK; + dma_write32(ce, CE_DMA_OUT_INT_MASK, val); + + val = dma_read32(ce, CE_DMA_IN_INT_MASK); + val &= ~DMA_INTERRUPT_MASK; + dma_write32(ce, CE_DMA_IN_INT_MASK, val); +} + +static rt_err_t select_engine_crypto(struct spacemit_crypto_engine *ce, + enum crypto_eng_sel engine) +{ + rt_uint32_t val; + + val = crypto_read32(ce, CE_CRYPTO_ENGINE_SEL_REG); + val &= ~0x3; + + switch (engine) + { + case E_ENG_AES: val |= 0x1; break; + default: + return -RT_EINVAL; + } + + crypto_write32(ce, CE_CRYPTO_ENGINE_SEL_REG, val); + + return RT_EOK; +} + +static void crypto_aes_sw_reset(struct spacemit_crypto_engine *ce) +{ + crypto_write32(ce, CE_CRYPTO_AES_CONTROL_REG, 0x1); + crypto_write32(ce, CE_CRYPTO_AES_CONTROL_REG, 0x0); +} + +static void crypto_aes_start(struct spacemit_crypto_engine *ce) +{ + crypto_write32(ce, CE_CRYPTO_AES_COMMAND_REG, 0x1); +} + +static rt_err_t crypto_aes_wait(struct spacemit_crypto_engine *ce) +{ + rt_completion_wait(&ce->aes_done, RT_WAITING_FOREVER); + + if (ce->aes_status != AES_DONE) + { + return -RT_EINVAL; + } + + return RT_EOK; +} + +static rt_err_t crypto_aes_set_mode(struct spacemit_crypto_engine *ce, + struct hwcrypto_symmetric *symmetric_ctx, + struct hwcrypto_symmetric_info *symmetric_info, + rt_bool_t use_rkey) +{ + rt_uint32_t val; + + select_engine_crypto(ce, E_ENG_AES); + + val = crypto_read32(ce, CE_CRYPTO_AES_CONFIG_REG); + val &= ~(0x7 << 0x3); + + switch (symmetric_ctx->parent.type) + { + case HWCRYPTO_TYPE_AES_ECB: val |= 0x0 << 0x3; break; + case HWCRYPTO_TYPE_AES_CBC: val |= 0x1 << 0x3; break; + default: + break; + } + + val &= ~(0x3 << 0x1); + + switch (symmetric_ctx->key_bitlen) + { + case 128: val |= 0x0 << 0x1; break; + case 192: val |= 0x2 << 0x1; break; + case 256: val |= 0x1 << 0x1; break; + default: + return -RT_EINVAL; + } + + val &= ~(0x1 << 0x0); + + if (symmetric_info->mode == HWCRYPTO_MODE_DECRYPT) + { + val |= 0x1 << 0x0; + } + else + { + val |= 0x0 << 0x0; + } + + val &= ~(0x1 << 0x6); + + if (!use_rkey) + { + val |= 0x0 << 0x6; + } + else + { + val |= 0x1 << 0x6; + } + + crypto_write32(ce, CE_CRYPTO_AES_CONFIG_REG, val); + + return RT_EOK; +} + +static rt_err_t crypto_aes_set_key1(struct spacemit_crypto_engine *ce, + const rt_uint8_t *key, rt_uint16_t keylen) +{ + rt_uint32_t val, key_end; + + if (!key) + { + return RT_EOK; + } + + switch (keylen) + { + case 128: key_end = 4; break; + case 192: key_end = 6; break; + case 256: key_end = 8; break; + default: + return -RT_EINVAL; + } + + for (int reg_index = 0; reg_index < 8; ++reg_index) + { + if (reg_index < key_end) + { + val = ((key[0 + (reg_index << 2)] & 0xff) << 0) | + ((key[1 + (reg_index << 2)] & 0xff) << 8) | + ((key[2 + (reg_index << 2)] & 0xff) << 16) | + ((key[3 + (reg_index << 2)] & 0xff) << 24); + } + else + { + val = 0; + } + + crypto_write32(ce, CE_CRYPTO_K1_W_REG(reg_index), val); + } + + return RT_EOK; +} + + +static void crypto_aes_set_iv(struct spacemit_crypto_engine *ce, const rt_uint8_t *iv) +{ + rt_uint32_t val; + + for (int reg_index = 0; reg_index < 4; ++reg_index) + { + val = ((iv[(reg_index << 2) + 0] & 0xff) << 0) | + ((iv[(reg_index << 2) + 1] & 0xff) << 8) | + ((iv[(reg_index << 2) + 2] & 0xff) << 16) | + ((iv[(reg_index << 2) + 3] & 0xff) << 24); + + crypto_write32(ce, CE_CRYPTO_IV_REG(reg_index), val); + } +} + +static rt_err_t aes_process(struct spacemit_crypto_engine *ce, + struct hwcrypto_symmetric *symmetric_ctx, + struct hwcrypto_symmetric_info *symmetric_info, + rt_uint8_t *buf_in, rt_uint8_t *buf_out, rt_size_t blocks) +{ + rt_err_t err; + rt_uint32_t val; + rt_ubase_t dma_addr_in_low, dma_addr_in_high; + rt_ubase_t dma_addr_out_low, dma_addr_out_high; + struct spacemit_crypto *crypto = ce->crypto; + + rt_memcpy(ce->in_buffer, buf_in, blocks << 4); + + dma_addr_in_high = rt_upper_32_bits(ce->in_buffer_dma); + dma_addr_in_low = rt_lower_32_bits(ce->in_buffer_dma); + dma_addr_out_high = rt_upper_32_bits(ce->out_buffer_dma); + dma_addr_out_low = rt_lower_32_bits(ce->out_buffer_dma); + + /* Reset the HW before using it */ + adec_engine_hw_reset(ce, E_ACC_ENG_DMA); + adec_engine_hw_reset(ce, E_ACC_ENG_CRYPTO); + abus_set_mode(ce, E_ABUS_GRP_A_HASH, E_ABUS_GRP_B_AES, E_ABUS_STRAIGHT, E_ABUS_STRAIGHT); + crypto_aes_sw_reset(ce); + + rt_syscon_read(crypto->ciu_regmap, ENGINE_DMA_ADDR_HIGH_OFFSET, &val); + switch (ce - ce->crypto->engine) + { + case 0: + val &= ~(WADDR_BIT32 | RADDR_BIT32); + val |= ((dma_addr_out_high & 0x1) << 4 | (dma_addr_in_high & 0x1) << 5); + break; + + case 1: + val &= ~0xff00; + val |= ((dma_addr_out_high & 0xf) << 8 | (dma_addr_in_high & 0xf) << 12);; + break; + + default: + return -RT_EINVAL; + } + rt_syscon_write(crypto->ciu_regmap, ENGINE_DMA_ADDR_HIGH_OFFSET, val); + + if (ce->in_buffer_dma & 0x3 || ce->out_buffer_dma & 0x3) + { + return -RT_EINVAL; + } + + enable_biu_mask(ce); + enable_adec_mask(ce); + enable_engine_irq(ce); + + dma_input_config(ce, 0, 0); + dma_output_config(ce, 0, 1); + + dma_input_address(ce, dma_addr_in_low, blocks << 2, RT_FALSE); + dma_output_address(ce, dma_addr_out_low, blocks << 2, RT_FALSE); + + /* Process KEY */ + if ((err = crypto_aes_set_mode(ce, symmetric_ctx, symmetric_info, RT_FALSE))) + { + return err; + } + + if ((err = crypto_aes_set_key1(ce, symmetric_ctx->key, symmetric_ctx->key_bitlen))) + { + return err; + } + + /* Process IV */ + switch (symmetric_ctx->parent.type) + { + case HWCRYPTO_TYPE_AES_CBC: + crypto_aes_set_iv(ce, symmetric_ctx->iv); + break; + + default: + break; + } + + crypto_write32(ce, CE_CRYPTO_AES_STREAM_SIZE_REG, blocks << 4); + + dma_input_start(ce); + dma_output_start(ce); + crypto_aes_start(ce); + + if ((err = dma_wait_int_input_finish(ce))) + { + return err; + } + + if ((err = crypto_aes_wait(ce))) + { + return err; + } + + if ((err = dma_wait_int_output_finish(ce))) + { + return err; + } + + /* Readback IV after operation */ + switch (symmetric_ctx->parent.type) + { + case HWCRYPTO_TYPE_AES_CBC: + crypto_aes_set_iv(ce, symmetric_ctx->iv); + break; + + default: + break; + } + + rt_memcpy(buf_out, ce->out_buffer, blocks << 4); + + return RT_EOK; +} + +static rt_err_t spacemit_crypto_symmetric_crypt( + struct hwcrypto_symmetric *symmetric_ctx, + struct hwcrypto_symmetric_info *symmetric_info) +{ + rt_err_t err; + rt_uint32_t blocks; + rt_uint8_t *buf_in, *buf_out; + struct spacemit_crypto_engine *ce = raw_to_spacemit_crypto_engine(symmetric_ctx); + + if (!symmetric_ctx->iv_len) + { + symmetric_ctx->iv_len = sizeof(symmetric_ctx->iv); + rt_memset(symmetric_ctx->iv, 0, symmetric_ctx->iv_len); + } + + switch (symmetric_ctx->parent.type) + { + case HWCRYPTO_TYPE_AES_ECB: + case HWCRYPTO_TYPE_AES_CBC: + break; + default: + return -RT_ENOSYS; + } + + if (symmetric_info->mode != HWCRYPTO_MODE_ENCRYPT && + symmetric_info->mode != HWCRYPTO_MODE_DECRYPT) + { + return -RT_ENOSYS; + } + + buf_in = (void *)symmetric_info->in; + buf_out = symmetric_info->out; + blocks = symmetric_info->length / AES_BLOCK_SIZE; + + rt_mutex_take(&ce->lock, RT_WAITING_FOREVER); + + if ((rt_ubase_t)buf_in & 0x3 || (rt_ubase_t)buf_out & 0x3) + { + int len_bytes = 0, step_bytes = 0; + rt_uint8_t *in_cpy, *out_cpy, *in_work, *out_work; + rt_uint8_t *aligned_buf_1 = &ce->internal_working_buffer[0]; + rt_uint8_t *aligned_buf_2 = &ce->internal_working_buffer[WORK_BUF_SIZE]; + + len_bytes = blocks << 4; + in_cpy = (rt_uint8_t *)buf_in; + out_cpy = (rt_uint8_t *)buf_out; + + while (len_bytes) + { + step_bytes = len_bytes > WORK_BUF_SIZE ? WORK_BUF_SIZE : len_bytes; + + if ((rt_ubase_t)buf_in & 0x3) + { + rt_memcpy(aligned_buf_1, in_cpy, step_bytes); + in_work = aligned_buf_1; + } + else + { + in_work = in_cpy; + } + + len_bytes -= step_bytes; + in_cpy += step_bytes; + + if ((rt_ubase_t)buf_out & 0x3) + { + rt_memset(aligned_buf_2, 0x0, WORK_BUF_SIZE); + out_work = aligned_buf_2; + } + else + { + out_work = out_cpy; + } + + if ((err = aes_process(ce, symmetric_ctx, symmetric_info, in_work, out_work, step_bytes >> 4))) + { + goto _out_lock; + } + + if ((rt_ubase_t)buf_out & 0x3) + { + rt_memcpy(out_cpy, aligned_buf_2, step_bytes); + } + + out_cpy += step_bytes; + } + } + else + { + err = aes_process(ce, symmetric_ctx, symmetric_info, buf_in, buf_out, blocks); + } + +_out_lock: + rt_mutex_release(&ce->lock); + + return err; +} + +static const struct hwcrypto_symmetric_ops spacemit_crypto_symmetric_ops = +{ + .crypt = spacemit_crypto_symmetric_crypt, +}; + +static rt_err_t spacemit_crypto_create(struct rt_hwcrypto_ctx *ctx) +{ + struct hwcrypto_symmetric *crypto_symmetric; + + if ((ctx->type & HWCRYPTO_MAIN_TYPE_MASK) != HWCRYPTO_TYPE_AES) + { + return -RT_ENOSYS; + } + + crypto_symmetric = rt_container_of(ctx, struct hwcrypto_symmetric, parent); + + crypto_symmetric->ops = &spacemit_crypto_symmetric_ops; + + return RT_EOK; +} + +static void spacemit_crypto_destroy(struct rt_hwcrypto_ctx *ctx) +{ + if (!ctx->contex) + { + return; + } + + if ((ctx->type & HWCRYPTO_MAIN_TYPE_MASK) == HWCRYPTO_TYPE_AES) + { + rt_free(ctx->contex); + } +} + +static rt_err_t spacemit_crypto_copy(struct rt_hwcrypto_ctx *des, + const struct rt_hwcrypto_ctx *src) +{ + if ((src->type & HWCRYPTO_MAIN_TYPE_MASK) != HWCRYPTO_TYPE_AES) + { + return -RT_ENOSYS; + } + + return RT_EOK; +} + +static void spacemit_crypto_reset(struct rt_hwcrypto_ctx *ctx) +{ +} + +static const struct rt_hwcrypto_ops spacemit_crypto_ops = +{ + .create = spacemit_crypto_create, + .destroy = spacemit_crypto_destroy, + .copy = spacemit_crypto_copy, + .reset = spacemit_crypto_reset, +}; + +static void spacemit_crypto_isr(int irq, void *param) +{ + volatile rt_uint32_t val_aes; + struct spacemit_crypto_engine *ce = param; + + /* AES */ + val_aes = crypto_read32(ce, CE_CRYPTO_AES_INTRPT_SRC_REG); + + if (val_aes & AES_INTERRUPT_MASK) + { + crypto_write32(ce, CE_CRYPTO_AES_INTRPT_SRC_REG, val_aes); + adec_biu_clear_int_flag(ce); + + ce->aes_status = (val_aes & AES_INTERRUPT_FLAG) ? AES_DONE : AES_ERROR; + rt_completion_done(&ce->aes_done); + + return; + } + + /* DMA output */ + val_aes = dma_read32(ce, CE_DMA_OUT_INT); + + if (val_aes & DMA_INTERRUPT_MASK) + { + dma_output_stop(ce); + dma_write32(ce, CE_DMA_OUT_INT, val_aes); + adec_biu_clear_int_flag(ce); + + ce->dma_out_status = (val_aes & BIT_DMA_INOUT_DONE) ? DMA_INOUT_DONE : DMA_INOUT_ERROR; + rt_completion_done(&ce->dma_output_done); + + return; + } + + /* DMA input */ + val_aes = dma_read32(ce, CE_DMA_IN_INT); + + if (val_aes & DMA_INTERRUPT_MASK) + { + dma_input_stop(ce); + dma_write32(ce, CE_DMA_IN_INT, val_aes); + adec_biu_clear_int_flag(ce); + + ce->dma_in_status = (val_aes & BIT_DMA_INOUT_DONE) ? DMA_INOUT_DONE : DMA_INOUT_ERROR; + rt_completion_done(&ce->dma_input_done); + + return; + } +} + +static void spacemit_crypto_free(struct spacemit_crypto *crypto, struct rt_device *dev) +{ + if (!rt_is_err_or_null(crypto->clk)) + { + rt_clk_disable_unprepare(crypto->clk); + rt_clk_put(crypto->clk); + } + + if (!rt_is_err_or_null(crypto->rstc)) + { + rt_reset_control_assert(crypto->rstc); + rt_reset_control_put(crypto->rstc); + } + + for (int i = 0; i < crypto->engine_nr; ++i) + { + struct spacemit_crypto_engine *ce = &crypto->engine[i]; + + if (ce->base) + { + rt_iounmap(ce->base); + } + + if (ce->in_buffer) + { + rt_dma_free_coherent(dev, SPACEMIT_AES_BUFFER_LEN, ce->in_buffer, ce->in_buffer_dma); + } + + if (ce->out_buffer) + { + rt_dma_free_coherent(dev, SPACEMIT_AES_BUFFER_LEN, ce->out_buffer, ce->out_buffer_dma); + } + + if (ce->crypto) + { + rt_mutex_detach(&ce->lock); + + if (rt_device_find(rt_dm_dev_get_name(&ce->parent.parent))) + { + rt_hw_interrupt_mask(ce->irq); + rt_pic_detach_irq(ce->irq, ce); + + rt_device_unregister(&ce->parent.parent); + } + } + } + + rt_free(crypto); +} + +static rt_err_t spacemit_crypto_probe(struct rt_platform_device *pdev) +{ + rt_err_t err; + char name[32]; + struct rt_device *dev = &pdev->parent; + struct spacemit_crypto *crypto = rt_calloc(1, sizeof(*crypto)); + + if (!crypto) + { + return -RT_ENOMEM; + } + + if ((err = rt_dm_dev_prop_read_u32(dev, "num-engines", &crypto->engine_nr))) + { + goto _fail; + } + + if (!(crypto->ciu_regmap = rt_syscon_find_by_ofw_compatible("spacemit,ciu"))) + { + err = -RT_EIO; + goto _fail; + } + + crypto->clk = rt_clk_get_by_index(dev, 0); + if (rt_is_err(crypto->clk)) + { + err = rt_ptr_err(crypto->clk); + goto _fail; + } + rt_clk_prepare_enable(crypto->clk); + + crypto->rstc = rt_reset_control_get_by_index(dev, 0); + if (rt_is_err(crypto->rstc)) + { + err = rt_ptr_err(crypto->rstc); + goto _fail; + } + rt_reset_control_deassert(crypto->rstc); + + for (int i = 0; i < crypto->engine_nr; ++i) + { + rt_uint32_t addr_range[2]; + struct spacemit_crypto_engine *ce = &crypto->engine[i]; + + rt_snprintf(name, sizeof(name), "spacemit-crypto-engine-%d", i); + + if ((err = rt_dm_dev_prop_read_u32_array_index(dev, name, 0, 2, &addr_range[0])) <= 0) + { + goto _fail; + } + + if (!(ce->base = rt_ioremap((void *)(rt_ubase_t)addr_range[0], addr_range[1]))) + { + err = -RT_EIO; + goto _fail; + } + + if ((ce->irq = rt_dm_dev_get_irq(dev, i)) < 0) + { + err = ce->irq; + goto _fail; + } + + ce->in_buffer = rt_dma_alloc_coherent(dev, SPACEMIT_AES_BUFFER_LEN, &ce->in_buffer_dma); + if (!ce->in_buffer) + { + err = -RT_ENOMEM; + goto _fail; + } + + ce->out_buffer = rt_dma_alloc_coherent(dev, SPACEMIT_AES_BUFFER_LEN, &ce->out_buffer_dma); + if (!ce->out_buffer) + { + err = -RT_ENOMEM; + goto _fail; + } + + ce->crypto = crypto; + + rt_completion_init(&ce->aes_done); + rt_completion_init(&ce->dma_output_done); + rt_completion_init(&ce->dma_input_done); + + rt_snprintf(name, sizeof(name), "%s%d", RT_HWCRYPTO_DEFAULT_NAME, i); + rt_mutex_init(&ce->lock, name, RT_IPC_FLAG_PRIO); + + ce->parent.ops = &spacemit_crypto_ops; + ce->parent.id = __LINE__; + + if ((err = rt_hwcrypto_register(&ce->parent, name))) + { + goto _fail; + } + + rt_hw_interrupt_install(ce->irq, spacemit_crypto_isr, ce, name); + rt_hw_interrupt_umask(ce->irq); + } + + rt_syscon_update_bits(crypto->ciu_regmap, ENGINE_DMA_ADDR_HIGH_OFFSET, + SW_RESETN | MASTER_CLK_EN | SLAVE_CLK_EN, + SW_RESETN | MASTER_CLK_EN | SLAVE_CLK_EN); + + dev->user_data = crypto; + + return RT_EOK; + +_fail: + spacemit_crypto_free(crypto, dev); + + return err; +} + +static rt_err_t spacemit_crypto_remove(struct rt_platform_device *pdev) +{ + struct rt_device *dev = &pdev->parent; + struct spacemit_crypto *crypto = dev->user_data; + + spacemit_crypto_free(crypto, dev); + + return RT_EOK; +} + +static const struct rt_ofw_node_id spacemit_crypto_ofw_ids[] = +{ + { .compatible = "spacemit,crypto_engine" }, + { /* sentinel */ } +}; + +static struct rt_platform_driver spacemit_crypto_driver = +{ + .name = "hwcrypto-spacemit", + .ids = spacemit_crypto_ofw_ids, + + .probe = spacemit_crypto_probe, + .remove = spacemit_crypto_remove, +}; +RT_PLATFORM_DRIVER_EXPORT(spacemit_crypto_driver); diff --git a/bsp/spacemit/dm/hwcrypto/hw-rng-spacemit.c b/bsp/spacemit/dm/hwcrypto/hw-rng-spacemit.c new file mode 100755 index 00000000000..e4384a2a1d6 --- /dev/null +++ b/bsp/spacemit/dm/hwcrypto/hw-rng-spacemit.c @@ -0,0 +1,314 @@ +/* + * Copyright (c) 2006-2024, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2024-5-1 GuEe-GUI first version + */ + +#include +#include + +#include "spacemit.h" + +#define RNG_BYTE_CNT 0x04 +#define RNG_SRC_ADDR 0x14 +#define RNG_DEST_ADDR 0x24 +#define RNG_NEXT_DESC_PTR 0x34 +#define RNG_SQU_CTRL 0x44 +#define RNG_CURR_DESC_PTR 0x74 +#define RNG_INT_MASK 0x84 +#define RNG_REST_SEL 0x94 +#define RNG_INT_STATUS 0xa4 +#define RNG_CTRL 0xc0 +#define RNG_DATA 0xc4 +#define RNG_SEED_VAL 0xc8 +#define RNG_CTRL_EXT 0xd0 + +#define SQU_CTRL_FIFO_CLR RT_BIT(30) +#define SQU_CTRL_ENABLE_DMA 0x03c60383 +#define RNG_INT_DONE_MASK RT_BIT(0) +#define RESET_SEL_MASK RT_BIT(0) +#define CTRL_RNG_EN RT_BIT(0) +#define CTRL_RNG_VALID RT_BIT(31) +#define CTRL_RNG_SEED_VALID RT_BIT(30) +#define CTRL_RNG_SEED_EN RT_BIT(1) +#define RNG_REG_SEL RT_BIT(4) +#define RNG_REG_LDO_PU RT_BIT(6) +#define RNG_SUCCESS 0x55 +#define RNG_FAIL 0xaa + +#define DMA_MAX_SIZE 0xfff0 + +struct spacemit_rng +{ + struct rt_hwcrypto_device parent; + + void *base; + struct rt_clk *clk; + struct rt_reset_control *rstc; + + struct rt_spinlock lock; +}; + +static void spacemit_rng_clr_fifo(struct spacemit_rng *rng) +{ + rt_uint32_t val; + + val = HWREG32(rng->base + RNG_SQU_CTRL); + val |= SQU_CTRL_FIFO_CLR; + HWREG32(rng->base + RNG_SQU_CTRL) = val; +} + +static void spacemit_rng_reset_status(struct spacemit_rng *rng) +{ + rt_uint32_t val; + + /* Clear squ fifo */ + spacemit_rng_clr_fifo(rng); + + /* Clear rng and seed ctrl bit */ + val = HWREG32(rng->base + RNG_CTRL); + val &= ~(CTRL_RNG_EN | CTRL_RNG_VALID); + val &= ~(CTRL_RNG_SEED_EN | CTRL_RNG_SEED_VALID); + HWREG32(rng->base + RNG_CTRL) = val; +} + +static rt_err_t spacemit_rng_reset_seed(struct spacemit_rng *rng) +{ + rt_uint32_t val; + rt_tick_t timeout = rt_tick_from_millisecond(100); + + for (int i = 0; i < 2; ++i) + { + /* Generate software seed */ + HWREG32(rng->base + RNG_SEED_VAL) = rt_tick_get(); + + val = HWREG32(rng->base + RNG_CTRL); + val |= CTRL_RNG_SEED_EN; + HWREG32(rng->base + RNG_CTRL) = val; + + timeout += rt_tick_get(); + + do { + val = HWREG32(rng->base + RNG_CTRL); + + if (val & CTRL_RNG_SEED_VALID) + { + return RT_EOK; + } + + if (rt_tick_get() >= timeout) + { + break; + } + } while (RT_TRUE); + } + + spacemit_rng_reset_status(rng); + + return -RT_EIO; +} + +static rt_uint32_t spacemit_rng_rand(struct hwcrypto_rng *ctx) +{ + rt_uint32_t val; + rt_tick_t timeout = rt_tick_from_millisecond(100); + struct spacemit_rng *rng = rt_container_of(ctx->parent.device, struct spacemit_rng, parent); + + rt_hw_spin_lock(&rng->lock.lock); + + if (spacemit_rng_reset_seed(rng)) + { + goto _unlock; + } + + /* Generate random value */ + val = HWREG32(rng->base + RNG_CTRL); + val |= CTRL_RNG_EN; + HWREG32(rng->base + RNG_CTRL) = val; + + for (int i = 0; i < 2; ++i) + { + timeout += rt_tick_get(); + + do { + if (HWREG32(rng->base + RNG_CTRL) & CTRL_RNG_VALID) + { + /* Clear squ fifo */ + spacemit_rng_clr_fifo(rng); + val = HWREG32(rng->base + RNG_DATA); + + goto _unlock; + } + + rt_hw_us_delay(200); + + if (rt_tick_get() >= timeout) + { + break; + } + } while (RT_TRUE); + } + + val = 0; + spacemit_rng_reset_status(rng); + +_unlock: + rt_hw_spin_unlock(&rng->lock.lock); + + return val; +} + +static const struct hwcrypto_rng_ops rng_ops = +{ + .update = spacemit_rng_rand, +}; + +static rt_err_t spacemit_rng_create(struct rt_hwcrypto_ctx *ctx) +{ + if ((ctx->type & HWCRYPTO_MAIN_TYPE_MASK) == HWCRYPTO_TYPE_RNG) + { + struct hwcrypto_rng *rng; + + ctx->contex = RT_NULL; + + rng = rt_container_of(ctx, struct hwcrypto_rng, parent); + rng->ops = &rng_ops; + + return RT_EOK; + } + + return -RT_ENOSYS; +} + +static void spacemit_rng_destroy(struct rt_hwcrypto_ctx *ctx) +{ +} + +static rt_err_t spacemit_rng_copy(struct rt_hwcrypto_ctx *des, + const struct rt_hwcrypto_ctx *src) +{ + if ((src->type & HWCRYPTO_MAIN_TYPE_MASK) == HWCRYPTO_TYPE_RNG) + { + return RT_EOK; + } + + return -RT_ENOSYS; +} + +static void spacemit_rng_reset(struct rt_hwcrypto_ctx *ctx) +{ +} + +static const struct rt_hwcrypto_ops spacemit_rng_ops = +{ + .create = spacemit_rng_create, + .destroy = spacemit_rng_destroy, + .copy = spacemit_rng_copy, + .reset = spacemit_rng_reset, +}; + +static void spacemit_crypto_free(struct spacemit_rng *rng) +{ + if (rng->base) + { + rt_iounmap(rng->base); + } + + if (!rt_is_err_or_null(rng->rstc)) + { + rt_reset_control_assert(rng->rstc); + rt_reset_control_put(rng->rstc); + } + + if (!rt_is_err_or_null(rng->clk)) + { + rt_clk_disable_unprepare(rng->clk); + rt_clk_put(rng->clk); + } + + rt_free(rng); +} + +static rt_err_t spacemit_rng_probe(struct rt_platform_device *pdev) +{ + rt_err_t err; + struct rt_device *dev = &pdev->parent; + struct spacemit_rng *rng = rt_calloc(1, sizeof(*rng)); + + if (!rng) + { + return -RT_ENOMEM; + } + + rng->base = rt_dm_dev_iomap(dev, 0); + if (!rng->base) + { + err = -RT_EIO; + goto _fail; + } + + rng->clk = rt_clk_get_by_index(dev, 0); + if (rt_is_err(rng->clk)) + { + err = rt_ptr_err(rng->clk); + goto _fail; + } + rt_clk_prepare_enable(rng->clk); + + rng->rstc = rt_reset_control_get_by_index(dev, 0); + if (rt_is_err(rng->rstc)) + { + err = rt_ptr_err(rng->rstc); + goto _fail; + } + rt_reset_control_deassert(rng->rstc); + + rt_spin_lock_init(&rng->lock); + rng->parent.ops = &spacemit_rng_ops; + rng->parent.id = __LINE__; + + if ((err = rt_hwcrypto_register(&rng->parent, "hwrng"))) + { + goto _fail; + } + + dev->user_data = rng; + + return RT_EOK; + +_fail: + spacemit_crypto_free(rng); + + return err; +} + +static rt_err_t spacemit_rng_remove(struct rt_platform_device *pdev) +{ + struct spacemit_rng *rng = pdev->parent.user_data; + + rt_device_unregister(&rng->parent.parent); + + spacemit_crypto_free(rng); + + return RT_EOK; +} + +static const struct rt_ofw_node_id spacemit_rng_ofw_ids[] = +{ + { .compatible = "spacemit,hw_crng" }, + { /* sentinel */ } +}; + +static struct rt_platform_driver spacemit_rng_driver = +{ + .name = "hwrng-spacemit", + .ids = spacemit_rng_ofw_ids, + + .probe = spacemit_rng_probe, + .remove = spacemit_rng_remove, +}; +RT_PLATFORM_DRIVER_EXPORT(spacemit_rng_driver); diff --git a/bsp/spacemit/dm/hwtimer/Kconfig b/bsp/spacemit/dm/hwtimer/Kconfig new file mode 100755 index 00000000000..6d6c223ed34 --- /dev/null +++ b/bsp/spacemit/dm/hwtimer/Kconfig @@ -0,0 +1,3 @@ +config RT_HWTIMER_SPACEMIT_K1X + bool "Spacemit k1x Timer" + default n diff --git a/bsp/spacemit/dm/hwtimer/SConscript b/bsp/spacemit/dm/hwtimer/SConscript new file mode 100755 index 00000000000..8557e4fd718 --- /dev/null +++ b/bsp/spacemit/dm/hwtimer/SConscript @@ -0,0 +1,12 @@ +from building import * + +cwd = GetCurrentDir() +src = [] +CPPPATH = [cwd + '/../include'] + +if GetDepend(['RT_HWTIMER_SPACEMIT_K1X']): + src += ['hwtimer-k1x.c'] + +group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') diff --git a/bsp/spacemit/dm/hwtimer/hwtimer-k1x.c b/bsp/spacemit/dm/hwtimer/hwtimer-k1x.c new file mode 100755 index 00000000000..82f7d2edf4c --- /dev/null +++ b/bsp/spacemit/dm/hwtimer/hwtimer-k1x.c @@ -0,0 +1,650 @@ +/* + * Copyright (c) 2006-2024, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2024-5-1 GuEe-GUI first version + */ + +#include +#include +#include + +#define DBG_TAG "hwtimer.k1x" +#define DBG_LVL DBG_INFO +#include + +#define TMR_CCR 0x000c +#define TMR_TN_MM(n, m) (0x0010 + ((n) << 4) + ((m) << 2)) +#define TMR_CR(n) (0x0090 + ((n) << 2)) +#define TMR_SR(n) (0x0080 + ((n) << 2)) +#define TMR_IER(n) (0x0060 + ((n) << 2)) +#define TMR_PLVR(n) (0x0040 + ((n) << 2)) +#define TMR_PLCR(n) (0x0050 + ((n) << 2)) +#define TMR_WMER 0x0068 +#define TMR_WMR 0x006c +#define TMR_WVR 0x00cc +#define TMR_WSR 0x00c0 +#define TMR_ICR(n) (0x0070 + ((n) << 2)) +#define TMR_WICR 0x00c4 +#define TMR_CER 0x0000 +#define TMR_CMR 0x0004 +#define TMR_WCR 0x00c8 +#define TMR_WFAR 0x00b0 +#define TMR_WSAR 0x00b4 +#define TMR_CRSR 0x0008 + +#define TMR_CCR_CS_0(x) (((x) & 0x3) << 0) +#define TMR_CCR_CS_1(x) (((x) & 0x3) << 2) +#define TMR_CCR_CS_2(x) (((x) & 0x3) << 5) + +#define MAX_DELTA 0xfffffffe +#define MIN_DELTA 5 + +#define SPACEMIT_MAX_COUNTER 3 +#define SPACEMIT_MAX_TIMER 3 + +#define TMR_CER_COUNTER(cid) (1 << (cid)) +#define SPACEMIT_ALL_COUNTERS ((1 << SPACEMIT_MAX_COUNTER) - 1) + +#define SPACEMIT_TIMER_ALL_CPU 0xffffffff + +#define CLOCK_32KHZ 32768 + +struct spacemit_timer; + +struct spacemit_timer_counter +{ + struct rt_hwtimer_device parent; + struct rt_hwtimer_info info; + + struct spacemit_timer *timer; + + int irq; + rt_uint32_t cid; + rt_uint32_t cycle; + rt_uint32_t freq; +}; + +#define raw_to_spacemit_timer_counter(raw) rt_container_of(raw, struct spacemit_timer_counter, parent) + +struct spacemit_timer +{ + void *base; + struct rt_clk *clk; + struct rt_reset_control *rstc; + + rt_uint32_t tid; + rt_uint32_t freq; + rt_uint32_t fc_freq; + rt_uint32_t apb_freq; + rt_uint32_t loop_delay_fastclk; + + struct spacemit_timer_counter counter[SPACEMIT_MAX_COUNTER]; + struct rt_spinlock tm_lock; +}; + +static rt_err_t timer_counter_switch_clock(struct spacemit_timer *timer, rt_uint32_t freq); + +static void timer_write_check(struct spacemit_timer *timer, + rt_uint32_t reg, rt_uint32_t val, rt_uint32_t mask, + rt_bool_t clr, rt_bool_t clk_switch) +{ + int loop = 3, retry = 100; + rt_uint32_t t_read, t_check = clr ? !val : val; + +_retry: + HWREG32(timer->base + reg) = val; + + if (clk_switch) + { + timer_counter_switch_clock(timer, timer->fc_freq); + } + + t_read = HWREG32(timer->base + reg); + + while (((t_read & mask) != (t_check & mask)) && loop) + { + /* Avoid trying frequently to worsen bus contention */ + rt_hw_us_delay(30); + + t_read = HWREG32(timer->base + reg); + --loop; + + if (!loop) + { + loop = 3; + + if (--retry) + { + goto _retry; + } + else + { + return; + } + } + } +} + +static rt_err_t timer_counter_switch_clock(struct spacemit_timer *timer, rt_uint32_t freq) +{ + rt_uint32_t ccr, val, mask; + + ccr = HWREG32(timer->base + TMR_CCR); + + switch (timer->tid) + { + case 0: + mask = TMR_CCR_CS_0(3); + break; + case 1: + mask = TMR_CCR_CS_1(3); + break; + case 2: + mask = TMR_CCR_CS_2(3); + break; + default: + return -RT_EINVAL; + } + + ccr &= ~mask; + + if (freq == timer->fc_freq) + { + val = 0; + } + else if (freq == CLOCK_32KHZ) + { + val = 1; + } + else + { + LOG_E("Timer %d: invalid clock rate %u", timer->tid, freq); + return -RT_EINVAL; + } + + switch (timer->tid) + { + case 0: + ccr |= TMR_CCR_CS_0(val); + break; + case 1: + ccr |= TMR_CCR_CS_1(val); + break; + case 2: + ccr |= TMR_CCR_CS_2(val); + break; + } + + timer_write_check(timer, TMR_CCR, ccr, mask, RT_FALSE, RT_FALSE); + + return RT_EOK; +} + +static void timer_counter_disable(struct spacemit_timer_counter *cnt) +{ + rt_uint32_t cer; + rt_bool_t clk_switch = RT_FALSE; + struct spacemit_timer *timer = cnt->timer; + + if (cnt->freq != timer->fc_freq) + { + clk_switch = RT_TRUE; + } + + /* Disable counter */ + cer = HWREG32(timer->base + TMR_CER); + timer_write_check(timer, TMR_CER, (cer & ~(1 << cnt->cid)), (1 << cnt->cid), RT_FALSE, clk_switch); +} + +static void timer_counter_enable(struct spacemit_timer_counter *cnt) +{ + rt_uint32_t cer; + struct spacemit_timer *timer = cnt->timer; + + /* Switch to original clock */ + if (cnt->freq != timer->fc_freq) + { + timer_counter_switch_clock(timer, cnt->freq); + } + + /* Enable timer */ + cer = HWREG32(timer->base + TMR_CER); + + timer_write_check(timer, TMR_CER, (cer | (1 << cnt->cid)), (1 << cnt->cid), RT_FALSE, RT_FALSE); +} + +static void spacemit_timer_init(struct rt_hwtimer_device *hwtimer, rt_uint32_t state) +{ +} + +static rt_err_t spacemit_timer_start(struct rt_hwtimer_device *hwtimer, + rt_uint32_t cycle, rt_hwtimer_mode_t mode) +{ + rt_ubase_t level; + struct spacemit_timer_counter *cnt = raw_to_spacemit_timer_counter(hwtimer); + + if (mode != HWTIMER_MODE_ONESHOT) + { + return -RT_ENOSYS; + } + + level = rt_spin_lock_irqsave(&cnt->timer->tm_lock); + + /* If the timer counter is enabled, first disable it. */ + if (HWREG32(cnt->timer->base + TMR_CER) & (1 << cnt->cid)) + { + timer_counter_disable(cnt); + } + + /* Setup new counter value */ + timer_write_check(cnt->timer, TMR_TN_MM(cnt->cid, 0), (cycle - 1), (rt_uint32_t)(-1), RT_FALSE, RT_FALSE); + + /* enable the matching interrupt */ + timer_write_check(cnt->timer, TMR_IER(cnt->cid), 0x1, 0x1, RT_FALSE, RT_FALSE); + + timer_counter_enable(cnt); + + cnt->cycle = cycle; + + rt_spin_unlock_irqrestore(&cnt->timer->tm_lock, level); + + return RT_EOK; +} + +static void spacemit_timer_stop(struct rt_hwtimer_device *hwtimer) +{ + rt_ubase_t level; + struct spacemit_timer_counter *cnt = raw_to_spacemit_timer_counter(hwtimer); + + level = rt_spin_lock_irqsave(&cnt->timer->tm_lock); + + timer_counter_disable(cnt); + + rt_spin_unlock_irqrestore(&cnt->timer->tm_lock, level); +} + +static rt_err_t spacemit_timer_ctrl(struct rt_hwtimer_device *hwtimer, + rt_uint32_t cmd, void *args) +{ + rt_ubase_t level; + rt_err_t err = RT_EOK; + struct spacemit_timer_counter *cnt = raw_to_spacemit_timer_counter(hwtimer); + + switch (cmd) + { + case HWTIMER_CTRL_FREQ_SET: + level = rt_spin_lock_irqsave(&cnt->timer->tm_lock); + err = timer_counter_switch_clock(cnt->timer, *(rt_int32_t *)args); + rt_spin_unlock_irqrestore(&cnt->timer->tm_lock, level); + break; + + case HWTIMER_CTRL_STOP: + spacemit_timer_stop(hwtimer); + break; + + case HWTIMER_CTRL_INFO_GET: + if (args) + { + rt_memcpy(args, &cnt->info, sizeof(cnt->info)); + } + else + { + err = -RT_ERROR; + } + break; + + case HWTIMER_CTRL_MODE_SET: + err = spacemit_timer_start(hwtimer, cnt->cycle, (rt_hwtimer_mode_t)args); + break; + + default: + err = -RT_EINVAL; + break; + } + + return err; +} + +const static struct rt_hwtimer_ops spacemit_timer_ops = +{ + .init = spacemit_timer_init, + .start = spacemit_timer_start, + .stop = spacemit_timer_stop, + .control = spacemit_timer_ctrl, +}; + +static void spacemit_timer_isr(int irqno, void *param) +{ + rt_ubase_t level; + struct spacemit_timer_counter *cnt = param; + + level = rt_spin_lock_irqsave(&cnt->timer->tm_lock); + + /* We only use match #0 for the counter. */ + if (HWREG32(cnt->timer->base + TMR_SR(cnt->cid)) & 0x1) + { + timer_counter_disable(cnt); + + /* Disable the interrupt. */ + timer_write_check(cnt->timer, TMR_IER(cnt->cid), 0, 0x7, RT_FALSE, RT_FALSE); + /* Clear interrupt status */ + timer_write_check(cnt->timer, TMR_ICR(cnt->cid), 0x1, 0x7, RT_TRUE, RT_FALSE); + + rt_spin_unlock_irqrestore(&cnt->timer->tm_lock, level); + + rt_device_hwtimer_isr(&cnt->parent); + + return; + } + + rt_spin_unlock_irqrestore(&cnt->timer->tm_lock, level); +} + +static void spacemit_timer_free(struct spacemit_timer *timer) +{ + if (timer->base) + { + rt_iounmap(timer->base); + } + + if (!rt_is_err_or_null(timer->clk)) + { + rt_clk_put(timer->clk); + } + + rt_free(timer); +} + +static rt_err_t spacemit_timer_probe(struct rt_platform_device *pdev) +{ + rt_err_t err; + const char *dev_name; + rt_uint32_t val, delay; + struct rt_device *dev = &pdev->parent; + struct rt_ofw_node *np = dev->ofw_node, *child_np; + struct spacemit_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; + } + + if (rt_is_err(timer->clk = rt_clk_get_by_index(dev, 0))) + { + err = rt_ptr_err(timer->clk); + goto _fail; + } + + if (rt_is_err(timer->rstc = rt_reset_control_get_by_index(dev, 0))) + { + err = rt_ptr_err(timer->rstc); + goto _fail; + } + + if ((err = rt_dm_dev_prop_read_u32(dev, "spacemit,timer-id", &timer->tid))) + { + goto _fail; + } + + if (timer->tid >= SPACEMIT_MAX_TIMER) + { + err = -RT_EFULL; + goto _fail; + } + + /* Timer's fast clock and apb frequency */ + if ((err = rt_dm_dev_prop_read_u32(dev, "spacemit,timer-fastclk-frequency", &timer->fc_freq))) + { + goto _fail; + } + + if ((err = rt_dm_dev_prop_read_u32(dev, "spacemit,timer-apb-frequency", &timer->apb_freq))) + { + goto _fail; + } + + if ((err = rt_dm_dev_prop_read_u32(dev, "spacemit,timer-frequency", &timer->freq))) + { + goto _fail; + } + + /* + * Need use loop for more safe register's accessing, + * so at here dynamically calculate the loop time. + */ + if (!timer->fc_freq || !timer->apb_freq) + { + err = -RT_EINVAL; + goto _fail; + } + + if ((err = rt_clk_prepare_enable(timer->clk))) + { + goto _fail; + } + + if ((err = rt_clk_set_rate(timer->clk, timer->fc_freq))) + { + goto _fail; + } + + rt_reset_control_deassert(timer->rstc); + + /* + * The calculation formula for the loop cycle is: + * + * (1) need wait for 2 timer's clock cycle: + * 1 2 + * ------- x 2 = ------- + * fc_freq fc_freqtimer_counter_enable(evt); + * + * (2) convert to apb clock cycle: + * 2 1 apb_freq * 2 + * ------- / -------- = ---------------- + * fc_freq apb_freq fc_freq + * + * (3) every apb register's accessing will take 8 apb clock cycle, + * also consider add extral one more time for safe way; + * so finally need loop times for the apb register accessing: + * + * (apb_freq * 2) + * ------------------ / 8 + 1 + * fc_freq + */ + delay = ((timer->apb_freq * 2) / timer->fc_freq / 8) + 1; + timer->loop_delay_fastclk = delay; + + rt_spin_lock_init(&timer->tm_lock); + + /* Switch to fastclk before disable all counters. */ + timer_counter_switch_clock(timer, timer->fc_freq); + + /* Disalbe all counters */ + val = HWREG32(timer->base + TMR_CER) & ~SPACEMIT_ALL_COUNTERS; + HWREG32(timer->base + TMR_CER) = val; + + /* Disable matching interrupt */ + HWREG32(timer->base + TMR_IER(0)) = 0x0; + HWREG32(timer->base + TMR_IER(1)) = 0x0; + HWREG32(timer->base + TMR_IER(2)) = 0x0; + + while (delay--) + { + /* Clear pending interrupt status */ + HWREG32(timer->base + TMR_ICR(0)) = 0x1; + HWREG32(timer->base + TMR_ICR(1)) = 0x1; + HWREG32(timer->base + TMR_ICR(2)) = 0x1; + HWREG32(timer->base + TMR_CER) = val; + } + + rt_ofw_foreach_available_child_node(np, child_np) + { + rt_uint32_t cpu, ratio; + struct spacemit_timer_counter *cnt; + + if (!rt_ofw_node_is_compatible(child_np, "spacemit,timer-match")) + { + continue; + } + + if (rt_ofw_prop_read_u32(child_np, "spacemit,timer-counter-id", &val)) + { + continue; + } + + if (val >= SPACEMIT_MAX_TIMER) + { + LOG_W("Found a counter %s with id = %d > %d", + rt_ofw_node_full_name(child_np), val, SPACEMIT_MAX_TIMER); + continue; + } + + cnt = &timer->counter[val]; + cnt->cid = val; + cnt->freq = timer->freq; + + if (cnt->timer) + { + LOG_W("Found a repeat counter %s with id = %d", + rt_ofw_node_full_name(child_np), cnt->cid); + continue; + } + + if (cnt->timer) + { + LOG_W("Found a repeat counter %s with id = %d", + rt_ofw_node_full_name(child_np), cnt->cid); + continue; + } + + if (rt_ofw_prop_read_bool(child_np, "spacemit,timer-broadcast")) + { + cpu = SPACEMIT_TIMER_ALL_CPU; + } + else + { + cpu = rt_hw_cpu_id(); + rt_ofw_prop_read_u32(child_np, "spacemit,timer-counter-cpu", &cpu); + } + + if ((cnt->irq = rt_ofw_get_irq(child_np, 0)) < 0) + { + continue; + } + + /* Hareware init */ + if (timer_counter_switch_clock(timer, cnt->freq)) + { + continue; + } + + ratio = timer->fc_freq / timer->freq; + delay = timer->loop_delay_fastclk * ratio; + + /* Set timer to free-running mode */ + val = HWREG32(timer->base + TMR_CMR) | TMR_CER_COUNTER(cnt->cid); + HWREG32(timer->base + TMR_CMR) = val; + + /* Free-running */ + HWREG32(timer->base + TMR_PLCR(cnt->cid)) = 0x0; + /* Clear status */ + HWREG32(timer->base + TMR_ICR(cnt->cid)) = 0x7; + + /* Enable counter */ + val = HWREG32(timer->base + TMR_CER) | TMR_CER_COUNTER(cnt->cid); + HWREG32(timer->base + TMR_CER) = val; + + while (delay--) + { + HWREG32(timer->base + TMR_CER) = val; + } + + cnt->timer = timer; + + cnt->parent.ops = &spacemit_timer_ops; + cnt->parent.info = &cnt->info; + + cnt->info.maxfreq = timer->fc_freq; + cnt->info.minfreq = CLOCK_32KHZ; + cnt->info.maxcnt = 0xffffffff; + cnt->info.cntmode = HWTIMER_CNTMODE_UP; + + rt_dm_dev_set_name_auto(&cnt->parent.parent, "timer"); + dev_name = rt_dm_dev_get_name(&cnt->parent.parent); + + rt_hw_interrupt_install(cnt->irq, spacemit_timer_isr, cnt, dev_name); + rt_hw_interrupt_umask(cnt->irq); + + if (cpu != SPACEMIT_TIMER_ALL_CPU) + { + RT_IRQ_AFFINITY_DECLARE(affinity) = { 0 }; + + RT_IRQ_AFFINITY_SET(affinity, cpu); + + rt_pic_irq_set_affinity(cnt->irq, affinity); + } + + rt_device_hwtimer_register(&cnt->parent, dev_name, RT_NULL); + } + + dev->user_data = timer; + + return RT_EOK; + +_fail: + spacemit_timer_free(timer); + + return err; +} + +static rt_err_t spacemit_timer_remove(struct rt_platform_device *pdev) +{ + struct spacemit_timer *timer = pdev->parent.user_data; + + for (int i = 0; i < SPACEMIT_MAX_TIMER; ++i) + { + struct spacemit_timer_counter *cnt = &timer->counter[i]; + + if (cnt->timer) + { + continue; + } + + rt_hw_interrupt_mask(cnt->irq); + rt_pic_detach_irq(cnt->irq, cnt); + + rt_device_unregister(&cnt->parent.parent); + } + + spacemit_timer_free(timer); + + return RT_EOK; +} + +static const struct rt_ofw_node_id spacemit_timer_ofw_ids[] = +{ + { .compatible = "spacemit,soc-timer" }, + { /* sentinel */ } +}; + +static struct rt_platform_driver spacemit_timer_driver = +{ + .name = "hwtimer-spacemit", + .ids = spacemit_timer_ofw_ids, + + .probe = spacemit_timer_probe, + .remove = spacemit_timer_remove, +}; +RT_PLATFORM_DRIVER_EXPORT(spacemit_timer_driver); diff --git a/bsp/spacemit/dm/i2c/Kconfig b/bsp/spacemit/dm/i2c/Kconfig new file mode 100755 index 00000000000..b8821169dbc --- /dev/null +++ b/bsp/spacemit/dm/i2c/Kconfig @@ -0,0 +1,3 @@ +config RT_I2C_SPACEMIT_K1X + bool "Spacemit k1x I2C controller" + default n diff --git a/bsp/spacemit/dm/i2c/SConscript b/bsp/spacemit/dm/i2c/SConscript new file mode 100755 index 00000000000..3f36c4f80bd --- /dev/null +++ b/bsp/spacemit/dm/i2c/SConscript @@ -0,0 +1,13 @@ +from building import * + +group = [] +src = [] +cwd = GetCurrentDir() +CPPPATH = [cwd + '/../include'] + +if GetDepend(['RT_I2C_SPACEMIT_K1X']): + src += ['i2c-k1x.c'] + +group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') diff --git a/bsp/spacemit/dm/i2c/i2c-k1x.c b/bsp/spacemit/dm/i2c/i2c-k1x.c new file mode 100755 index 00000000000..ed4b32f9c43 --- /dev/null +++ b/bsp/spacemit/dm/i2c/i2c-k1x.c @@ -0,0 +1,1411 @@ +/* + * Copyright (c) 2006-2024, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2024-5-1 GuEe-GUI first version + */ + +#include +#include +#include + +#define DBG_TAG "i2c.rk3x" +#define DBG_LVL DBG_INFO +#include + +#include + +/* Spacemit i2c registers */ +#define REG_CR 0x0 /* Control Register */ +#define REG_SR 0x4 /* Status Register */ +#define REG_SAR 0x8 /* Slave Address Register */ +#define REG_DBR 0xc /* Data Buffer Register */ +#define REG_LCR 0x10 /* Load Count Register */ +#define REG_WCR 0x14 /* Wait Count Register */ +#define REG_RST_CYC 0x18 /* Bus reset cycle counter */ +#define REG_BMR 0x1c /* Bus monitor register */ +#define REG_WFIFO 0x20 /* Write FIFO Register */ +#define REG_WFIFO_WPTR 0x24 /* Write FIFO Write Pointer Register */ +#define REG_WFIFO_RPTR 0x28 /* Write FIFO Read Pointer Register */ +#define REG_RFIFO 0x2c /* Read FIFO Register */ +#define REG_RFIFO_WPTR 0x30 /* Read FIFO Write Pointer Register */ +#define REG_RFIFO_RPTR 0x34 /* Read FIFO Read Pointer Register */ + +/* Register REG_CR fields */ +#define CR_START RT_BIT(0) /* Start bit */ +#define CR_STOP RT_BIT(1) /* Stop bit */ +#define CR_ACKNAK RT_BIT(2) /* Send ACK(0) or NAK(1) */ +#define CR_TB RT_BIT(3) /* Transfer byte bit */ +#define CR_TXBEGIN RT_BIT(4) /* Transaction begin */ +#define CR_FIFOEN RT_BIT(5) /* Enable FIFO mode */ +#define CR_GPIOEN RT_BIT(6) /* Enable GPIO mode for SCL in HS */ +#define CR_DMAEN RT_BIT(7) /* Enable DMA for TX and RX FIFOs */ +#define CR_MODE_FAST RT_BIT(8) /* Bus mode (master operation) */ +#define CR_MODE_HIGH RT_BIT(9) /* Bus mode (master operation) */ +#define CR_UR RT_BIT(10) /* Unit reset */ +#define CR_RSTREQ RT_BIT(11) /* I2c bus reset request */ +#define CR_MA RT_BIT(12) /* Master abort */ +#define CR_SCLE RT_BIT(13) /* Master clock enable */ +#define CR_IUE RT_BIT(14) /* Unit enable */ +#define CR_HS_STRETCH RT_BIT(16) /* I2C hs stretch */ +#define CR_ALDIE RT_BIT(18) /* Enable arbitration interrupt */ +#define CR_DTEIE RT_BIT(19) /* Enable tx interrupts */ +#define CR_DRFIE RT_BIT(20) /* Enable rx interrupts */ +#define CR_GCD RT_BIT(21) /* General call disable */ +#define CR_BEIE RT_BIT(22) /* Enable bus error ints */ +#define CR_SADIE RT_BIT(23) /* Slave address detected int enable */ +#define CR_SSDIE RT_BIT(24) /* Slave STOP detected int enable */ +#define CR_MSDIE RT_BIT(25) /* Master STOP detected int enable */ +#define CR_MSDE RT_BIT(26) /* Master STOP detected enable */ +#define CR_TXDONEIE RT_BIT(27) /* Transaction done int enable */ +#define CR_TXEIE RT_BIT(28) /* Transmit FIFO empty int enable */ +#define CR_RXHFIE RT_BIT(29) /* Receive FIFO half-full int enable */ +#define CR_RXFIE RT_BIT(30) /* Receive FIFO full int enable */ +#define CR_RXOVIE RT_BIT(31) /* Receive FIFO overrun int enable */ + +/* Register REG_SR fields */ +#define SR_RWM RT_BIT(13) /* Read/write mode */ +#define SR_ACKNAK RT_BIT(14) /* ACK/NACK status */ +#define SR_UB RT_BIT(15) /* Unit busy */ +#define SR_IBB RT_BIT(16) /* I2c bus busy */ +#define SR_EBB RT_BIT(17) /* Early bus busy */ +#define SR_ALD RT_BIT(18) /* Arbitration loss detected */ +#define SR_ITE RT_BIT(19) /* Tx buffer empty */ +#define SR_IRF RT_BIT(20) /* Rx buffer full */ +#define SR_GCAD RT_BIT(21) /* General call address detected */ +#define SR_BED RT_BIT(22) /* Bus error no ACK/NAK */ +#define SR_SAD RT_BIT(23) /* Slave address detected */ +#define SR_SSD RT_BIT(24) /* Slave stop detected */ +#define SR_MSD RT_BIT(26) /* Master stop detected */ +#define SR_TXDONE RT_BIT(27) /* Transaction done */ +#define SR_TXE RT_BIT(28) /* Tx FIFO empty */ +#define SR_RXHF RT_BIT(29) /* Rx FIFO half-full */ +#define SR_RXF RT_BIT(30) /* Rx FIFO full */ +#define SR_RXOV RT_BIT(31) /* RX FIFO overrun */ + +/* Register REG_LCR fields */ +#define LCR_SLV 0x000001ff /* SLV: bit[8:0] */ +#define LCR_FLV 0x0003fe00 /* FLV: bit[17:9] */ +#define LCR_HLVH 0x07fc0000 /* HLVH: bit[26:18] */ +#define LCR_HLVL 0xf8000000 /* HLVL: bit[31:27] */ + +/* Register REG_WCR fields */ +#define WCR_COUNT 0x0000001f /* COUNT: bit[4:0] */ +#define WCR_COUNT1 0x000003e0 /* HS_COUNT1: bit[9:5] */ +#define WCR_COUNT2 0x00007c00 /* HS_COUNT2: bit[14:10] */ + +/* Register REG_BMR fields */ +#define BMR_SDA RT_BIT(0) /* SDA line level */ +#define BMR_SCL RT_BIT(1) /* SCL line level */ + +/* Register REG_WFIFO fields */ +#define WFIFO_DATA_MSK 0x000000ff /* data: bit[7:0] */ +#define WFIFO_CTRL_MSK 0x000003e0 /* control: bit[11:8] */ +#define WFIFO_CTRL_START RT_BIT(8) /* start bit */ +#define WFIFO_CTRL_STOP RT_BIT(9) /* stop bit */ +#define WFIFO_CTRL_ACKNAK RT_BIT(10) /* send ACK(0) or NAK(1) */ +#define WFIFO_CTRL_TB RT_BIT(11) /* transfer byte bit */ + +#define USEC_PER_SEC 1000000L +#define USEC_PER_MSEC 1000L +#define I2C_SMBUS_BLOCK_MAX 32 + +/* Status register init value */ +#define SPACEMIT_I2C_INT_STATUS_MASK 0xfffc0000 /* SR bits[31:18] */ +#define SPACEMIT_I2C_INT_CTRL_MASK (CR_ALDIE | CR_DTEIE | CR_DRFIE | CR_BEIE | CR_TXDONEIE | \ + CR_TXEIE | CR_RXHFIE | CR_RXFIE | CR_RXOVIE | CR_MSDIE) + +/* I2C transfer mode */ +enum spacemit_i2c_xfer_mode +{ + SPACEMIT_I2C_MODE_INTERRUPT, + SPACEMIT_I2C_MODE_FIFO, + SPACEMIT_I2C_MODE_DMA, + SPACEMIT_I2C_MODE_PIO, + SPACEMIT_I2C_MODE_INVALID, +}; + +/* I2C transfer phase during transaction */ +enum spacemit_i2c_xfer_phase +{ + SPACEMIT_I2C_XFER_MASTER_CODE, + SPACEMIT_I2C_XFER_SLAVE_ADDR, + SPACEMIT_I2C_XFER_BODY, + SPACEMIT_I2C_XFER_IDLE, +}; + +/* i2c controller FIFO depth */ +#define SPACEMIT_I2C_RX_FIFO_DEPTH 8 +#define SPACEMIT_I2C_TX_FIFO_DEPTH 8 + +/* i2c bus recover timeout: us */ +#define SPACEMIT_I2C_BUS_RECOVER_TIMEOUT 100000 + +/* i2c bus active timeout: us */ +#define SPACEMIT_I2C_BUS_ACTIVE_TIMEOUT 100000 + +/* scatter list size for DMA mode, equals to max number of i2c messages */ +#define SPACEMIT_I2C_SCATTERLIST_SIZE 42 + +/* for DMA mode, limit one message's length less than 512 bytes */ +#define SPACEMIT_I2C_MAX_MSG_LEN 512 +#define SPACEMIT_I2C_DMA_TX_BUF_LEN ((SPACEMIT_I2C_MAX_MSG_LEN + 2) * SPACEMIT_I2C_SCATTERLIST_SIZE) +#define SPACEMIT_I2C_DMA_RX_BUF_LEN (SPACEMIT_I2C_MAX_MSG_LEN * SPACEMIT_I2C_SCATTERLIST_SIZE) + +#define SPACEMIT_I2C_APB_CLOCK_26M 26000000 +#define SPACEMIT_I2C_APB_CLOCK_52M 52000000 + +#define I2C_SDA_GLITCH_FIX_BYPASS RT_BIT(7) + +/* i2c-spacemit driver's main struct */ +struct spacemit_i2c +{ + struct rt_i2c_bus_device parent; + + int irq; + void *regs; + struct rt_clk *clk; + struct rt_reset_control *rstc; + + rt_uint32_t drv_retries; + + rt_bool_t clk_always_on; + rt_bool_t dma_disable; + + /* Speed mode selection */ + rt_bool_t fast_mode; + rt_bool_t high_mode; + + /* APB clock */ + rt_uint32_t apb_clock; + + /* Master code for high-speed mode */ + rt_uint8_t master_code; + rt_uint32_t clk_rate; + rt_uint32_t i2c_lcr; + rt_uint32_t i2c_wcr; + + /* Slave address with read/write flag */ + rt_uint32_t slave_addr_rw; + + struct rt_i2c_msg *msgs; + int num; + + struct rt_i2c_msg *cur_msg; + int msg_idx; + + rt_uint8_t *msg_buf; + rt_bool_t is_rx; + rt_size_t rx_cnt; + rt_size_t tx_cnt; + int rx_total; + rt_bool_t is_xfer_start; + + struct rt_completion complete; + struct rt_spinlock fifo_lock; + + rt_tick_t timeout; + enum spacemit_i2c_xfer_mode xfer_mode; + enum spacemit_i2c_xfer_phase phase; + rt_uint32_t i2c_ctrl_reg_value; + rt_uint32_t i2c_status; + rt_uint32_t i2c_err; +}; + +#define raw_to_spacemit_i2c(raw) rt_container_of(raw, struct spacemit_i2c, parent) + +static rt_err_t spacemit_i2c_byte_xfer(struct spacemit_i2c *i2c); +static rt_err_t spacemit_i2c_byte_xfer_next_msg(struct spacemit_i2c *i2c); + +rt_inline rt_uint32_t spacemit_i2c_read(struct spacemit_i2c *i2c, int offset) +{ + return HWREG32(i2c->regs + offset); +} + +rt_inline void spacemit_i2c_write(struct spacemit_i2c *i2c, int offset, rt_uint32_t val) +{ + HWREG32(i2c->regs + offset) = val; +} + +static void spacemit_i2c_enable(struct spacemit_i2c *i2c) +{ + spacemit_i2c_write(i2c, REG_CR, spacemit_i2c_read(i2c, REG_CR) | CR_IUE); +} + +static void spacemit_i2c_disable(struct spacemit_i2c *i2c) +{ + i2c->i2c_ctrl_reg_value = spacemit_i2c_read(i2c, REG_CR) & ~CR_IUE; + spacemit_i2c_write(i2c, REG_CR, i2c->i2c_ctrl_reg_value); +} + +static void spacemit_i2c_flush_fifo_buffer(struct spacemit_i2c *i2c) +{ + /* Flush REG_WFIFO_WPTR and REG_WFIFO_RPTR */ + spacemit_i2c_write(i2c, REG_WFIFO_WPTR, 0); + spacemit_i2c_write(i2c, REG_WFIFO_RPTR, 0); + + /* Flush REG_RFIFO_WPTR and REG_RFIFO_RPTR */ + spacemit_i2c_write(i2c, REG_RFIFO_WPTR, 0); + spacemit_i2c_write(i2c, REG_RFIFO_RPTR, 0); +} + +static void spacemit_i2c_controller_reset(struct spacemit_i2c *i2c) +{ + /* Controller reset */ + spacemit_i2c_write(i2c, REG_CR, CR_UR); + rt_hw_us_delay(5); + spacemit_i2c_write(i2c, REG_CR, 0); + + /* Set load counter register */ + if (i2c->i2c_lcr) + { + spacemit_i2c_write(i2c, REG_LCR, i2c->i2c_lcr); + } + + /* Set wait counter register */ + if (i2c->i2c_wcr) + { + spacemit_i2c_write(i2c, REG_WCR, i2c->i2c_wcr); + } +} + +static void spacemit_i2c_bus_reset(struct spacemit_i2c *i2c) +{ + rt_uint32_t bus_status, val; + + /* If bus is locked, reset unit. 0: locked */ + bus_status = spacemit_i2c_read(i2c, REG_BMR); + if (!(bus_status & BMR_SDA) || !(bus_status & BMR_SCL)) + { + spacemit_i2c_controller_reset(i2c); + rt_hw_us_delay(15); + } + + for (int clk_cnt = 0; clk_cnt < 9; ++clk_cnt) + { + /* check whether the SDA is still locked by slave */ + bus_status = spacemit_i2c_read(i2c, REG_BMR); + if (bus_status & BMR_SDA) + { + break; + } + + /* If still locked, send one clk to slave to request release */ + val = spacemit_i2c_read(i2c, REG_RST_CYC); + spacemit_i2c_write(i2c, REG_RST_CYC, val | 0x1); + spacemit_i2c_write(i2c, REG_CR, CR_RSTREQ); + rt_hw_us_delay(25); + } +} + +static void spacemit_i2c_reset(struct spacemit_i2c *i2c) +{ + spacemit_i2c_controller_reset(i2c); +} + +static rt_err_t spacemit_i2c_recover_bus_busy(struct spacemit_i2c *i2c) +{ + rt_err_t err; + int cnt, timeout = i2c->high_mode ? 1000 : 1500; /* us */ + + cnt = SPACEMIT_I2C_BUS_RECOVER_TIMEOUT / timeout; + + if (!(spacemit_i2c_read(i2c, REG_SR) & (SR_UB | SR_IBB))) + { + return RT_EOK; + } + + /* Wait unit and bus to recover idle */ + while (spacemit_i2c_read(i2c, REG_SR) & (SR_UB | SR_IBB)) + { + if (cnt-- <= 0) + { + break; + } + + rt_hw_us_delay((timeout / 2 + timeout) / 2); + } + + if (cnt <= 0) + { + /* Reset controller */ + spacemit_i2c_reset(i2c); + err = -RT_EIO; + } + else + { + err = RT_EOK; + } + + return err; +} + +static void spacemit_i2c_check_bus_release(struct spacemit_i2c *i2c) +{ + /* In case bus is not released after transfer completes */ + if (spacemit_i2c_read(i2c, REG_SR) & SR_EBB) + { + spacemit_i2c_bus_reset(i2c); + rt_hw_us_delay(120); + } +} + +static void spacemit_i2c_unit_init(struct spacemit_i2c *i2c) +{ + rt_uint32_t cr_val = 0; + + /* + * Unmask interrupt bits for all xfer mode: + * bus error, arbitration loss detected. + * For transaction complete signal, we use master stop + * interrupt, so we don't need to unmask CR_TXDONEIE. + */ + cr_val |= CR_BEIE | CR_ALDIE; + + if (i2c->xfer_mode == SPACEMIT_I2C_MODE_INTERRUPT) + { + /* + * Unmask interrupt bits for interrupt xfer mode: DBR rx full. + * For tx empty interrupt CR_DTEIE, we only need to enable when + * trigger byte transfer to start data sending. + */ + cr_val |= CR_DRFIE; + } + else if (i2c->xfer_mode == SPACEMIT_I2C_MODE_FIFO) + { + /* Enable i2c FIFO mode*/ + cr_val |= CR_FIFOEN; + } + else if (i2c->xfer_mode == SPACEMIT_I2C_MODE_DMA) + { + /* Enable i2c DMA mode*/ + cr_val |= CR_DMAEN | CR_FIFOEN; + } + + /* Set speed bits */ + if (i2c->fast_mode) + { + cr_val |= CR_MODE_FAST; + } + if (i2c->high_mode) + { + cr_val |= CR_MODE_HIGH | CR_GPIOEN; + } + + /* Disable response to general call */ + cr_val |= CR_GCD; + + /* Enable SCL clock output */ + cr_val |= CR_SCLE; + + /* Enable master stop detected */ + cr_val |= CR_MSDE | CR_MSDIE; + + /* Disable int to use pio xfer mode*/ + if (i2c->xfer_mode == SPACEMIT_I2C_MODE_PIO) + { + cr_val &= ~(CR_ALDIE | CR_BEIE | CR_MSDIE | CR_DTEIE); + } + + spacemit_i2c_write(i2c, REG_CR, cr_val); +} + +static void spacemit_i2c_trigger_byte_xfer(struct spacemit_i2c *i2c) +{ + rt_uint32_t cr_val; + + /* Send start pulse */ + cr_val = spacemit_i2c_read(i2c, REG_CR); + cr_val &= ~CR_STOP; + if (i2c->xfer_mode == SPACEMIT_I2C_MODE_PIO) + { + cr_val |= CR_START | CR_TB; + } + else + { + cr_val |= CR_START | CR_TB | CR_DTEIE; + } + spacemit_i2c_write(i2c, REG_CR, cr_val); +} + +rt_inline void spacemit_i2c_clear_int_status(struct spacemit_i2c *i2c, rt_uint32_t mask) +{ + spacemit_i2c_write(i2c, REG_SR, mask & SPACEMIT_I2C_INT_STATUS_MASK); +} + +static rt_bool_t spacemit_i2c_is_last_byte_to_send(struct spacemit_i2c *i2c) +{ + return i2c->tx_cnt == i2c->cur_msg->len && i2c->msg_idx == i2c->num - 1; +} + +static rt_bool_t spacemit_i2c_is_last_byte_to_receive(struct spacemit_i2c *i2c) +{ + return i2c->rx_cnt == i2c->cur_msg->len - 1 && i2c->msg_idx == i2c->num - 1; +} + +static void spacemit_i2c_mark_rw_flag(struct spacemit_i2c *i2c) +{ + if (i2c->cur_msg->flags & RT_I2C_RD) + { + i2c->is_rx = RT_TRUE; + i2c->slave_addr_rw = ((i2c->cur_msg->addr & 0x7f) << 1) | 1; + } + else + { + i2c->is_rx = RT_FALSE; + i2c->slave_addr_rw = (i2c->cur_msg->addr & 0x7f) << 1; + } +} + +static void spacemit_i2c_byte_xfer_send_master_code(struct spacemit_i2c *i2c) +{ + rt_uint32_t cr_val = spacemit_i2c_read(i2c, REG_CR); + + i2c->phase = SPACEMIT_I2C_XFER_MASTER_CODE; + + spacemit_i2c_write(i2c, REG_DBR, i2c->master_code); + + cr_val &= ~(CR_STOP | CR_ALDIE); + + /* High mode: enable gpio to let I2C core generates SCL clock */ + cr_val |= CR_GPIOEN | CR_START | CR_TB | CR_DTEIE; + spacemit_i2c_write(i2c, REG_CR, cr_val); +} + +static void spacemit_i2c_byte_xfer_send_slave_addr(struct spacemit_i2c *i2c) +{ + i2c->phase = SPACEMIT_I2C_XFER_SLAVE_ADDR; + + /* Write slave address to DBR for interrupt mode */ + spacemit_i2c_write(i2c, REG_DBR, i2c->slave_addr_rw); + + spacemit_i2c_trigger_byte_xfer(i2c); +} + +static rt_err_t spacemit_i2c_byte_xfer_body(struct spacemit_i2c *i2c) +{ + rt_err_t err = RT_EOK; + rt_uint32_t cr_val = spacemit_i2c_read(i2c, REG_CR); + + cr_val &= ~(CR_TB | CR_ACKNAK | CR_STOP | CR_START); + i2c->phase = SPACEMIT_I2C_XFER_BODY; + + if (i2c->i2c_status & SR_IRF) /* Receive full */ + { + /* If current is transmit mode, ignore this signal */ + if (!i2c->is_rx) + { + return RT_EOK; + } + + if (i2c->rx_cnt < i2c->cur_msg->len) + { + *i2c->msg_buf++ = spacemit_i2c_read(i2c, REG_DBR); + i2c->rx_cnt++; + } + + /* If transfer completes, ISR will handle it */ + if (i2c->i2c_status & (SR_MSD | SR_ACKNAK)) + { + return RT_EOK; + } + + /* Trigger next byte receive */ + if (i2c->rx_cnt < i2c->cur_msg->len) + { + /* Send stop pulse for last byte of last msg */ + if (spacemit_i2c_is_last_byte_to_receive(i2c)) + { + cr_val |= CR_STOP | CR_ACKNAK; + } + + cr_val |= CR_ALDIE | CR_TB; + spacemit_i2c_write(i2c, REG_CR, cr_val); + } + else if (i2c->msg_idx < i2c->num - 1) + { + err = spacemit_i2c_byte_xfer_next_msg(i2c); + } + } + else if (i2c->i2c_status & SR_ITE) /* Transmit empty */ + { + /* MSD comes with ITE */ + if (i2c->i2c_status & SR_MSD) + { + return err; + } + + if (i2c->i2c_status & SR_RWM) /* Receive mode */ + { + /* If current is transmit mode, ignore this signal */ + if (!i2c->is_rx) + { + return RT_EOK; + } + + if (spacemit_i2c_is_last_byte_to_receive(i2c)) + { + cr_val |= CR_STOP | CR_ACKNAK; + } + + /* Trigger next byte receive */ + cr_val |= CR_ALDIE | CR_TB; + + /* + * Mask transmit empty interrupt to avoid useless tx + * interrupt signal after switch to receive mode, the + * next expected is receive full interrupt signal. + */ + cr_val &= ~CR_DTEIE; + spacemit_i2c_write(i2c, REG_CR, cr_val); + } + else /* Transmit mode */ + { + /* If current is receive mode, ignore this signal */ + if (i2c->is_rx) + { + return RT_EOK; + } + + if (i2c->tx_cnt < i2c->cur_msg->len) + { + spacemit_i2c_write(i2c, REG_DBR, *i2c->msg_buf++); + i2c->tx_cnt++; + + /* Send stop pulse for last byte of last msg */ + if (spacemit_i2c_is_last_byte_to_send(i2c)) + { + cr_val |= CR_STOP; + } + + cr_val |= CR_ALDIE | CR_TB; + spacemit_i2c_write(i2c, REG_CR, cr_val); + } + else if (i2c->msg_idx < i2c->num - 1) + { + err = spacemit_i2c_byte_xfer_next_msg(i2c); + } + } + } + + return err; +} + +static rt_err_t spacemit_i2c_byte_xfer_next_msg(struct spacemit_i2c *i2c) +{ + if (i2c->msg_idx == i2c->num - 1) + { + return RT_EOK; + } + + i2c->msg_idx++; + i2c->cur_msg = i2c->msgs + i2c->msg_idx; + i2c->msg_buf = i2c->cur_msg->buf; + i2c->rx_cnt = 0; + i2c->tx_cnt = 0; + i2c->i2c_err = 0; + i2c->i2c_status = 0; + i2c->phase = SPACEMIT_I2C_XFER_IDLE; + + spacemit_i2c_mark_rw_flag(i2c); + + return spacemit_i2c_byte_xfer(i2c); +} + +static void spacemit_i2c_fifo_xfer_fill_buffer(struct spacemit_i2c *i2c) +{ + rt_ubase_t level; + int data_cnt = 0, finish, count = 0, fill = 0; + rt_uint32_t data = 0, data_buf[SPACEMIT_I2C_TX_FIFO_DEPTH * 2]; + + while (i2c->msg_idx < i2c->num) + { + spacemit_i2c_mark_rw_flag(i2c); + + if (i2c->is_rx) + { + finish = i2c->rx_cnt; + } + else + { + finish = i2c->tx_cnt; + } + + /* Write master code to fifo buffer */ + if (i2c->high_mode && i2c->is_xfer_start) + { + data = i2c->master_code; + data |= WFIFO_CTRL_TB | WFIFO_CTRL_START; + data_buf[data_cnt++] = data; + + fill += 2; + count = rt_min_t(rt_size_t, i2c->cur_msg->len - finish, SPACEMIT_I2C_TX_FIFO_DEPTH - fill); + } + else + { + fill += 1; + count = rt_min_t(rt_size_t, i2c->cur_msg->len - finish, SPACEMIT_I2C_TX_FIFO_DEPTH - fill); + } + + i2c->is_xfer_start = RT_FALSE; + fill += count; + data = i2c->slave_addr_rw; + data |= WFIFO_CTRL_TB | WFIFO_CTRL_START; + + /* Write slave address to fifo buffer */ + data_buf[data_cnt++] = data; + + if (i2c->is_rx) + { + i2c->rx_cnt += count; + + if (i2c->rx_cnt == i2c->cur_msg->len && i2c->msg_idx == i2c->num - 1) + { + count -= 1; + } + + while (count > 0) + { + data = *i2c->msg_buf | WFIFO_CTRL_TB; + data_buf[data_cnt++] = data; + i2c->msg_buf++; + count--; + } + + if (i2c->rx_cnt == i2c->cur_msg->len && i2c->msg_idx == i2c->num - 1) + { + data = *i2c->msg_buf++; + data = i2c->slave_addr_rw | WFIFO_CTRL_TB | WFIFO_CTRL_STOP | WFIFO_CTRL_ACKNAK; + data_buf[data_cnt++] = data; + } + } + else + { + i2c->tx_cnt += count; + if (spacemit_i2c_is_last_byte_to_send(i2c)) + { + count -= 1; + } + + while (count > 0) + { + data = *i2c->msg_buf | WFIFO_CTRL_TB; + data_buf[data_cnt++] = data; + i2c->msg_buf++; + count--; + } + if (spacemit_i2c_is_last_byte_to_send(i2c)) + { + data = *i2c->msg_buf | WFIFO_CTRL_TB | WFIFO_CTRL_STOP; + data_buf[data_cnt++] = data; + } + } + + if (i2c->tx_cnt == i2c->cur_msg->len || i2c->rx_cnt == i2c->cur_msg->len) + { + i2c->msg_idx++; + if (i2c->msg_idx == i2c->num) + { + break; + } + + i2c->cur_msg = i2c->msgs + i2c->msg_idx; + i2c->msg_buf = i2c->cur_msg->buf; + i2c->rx_cnt = 0; + i2c->tx_cnt = 0; + } + + if (fill == SPACEMIT_I2C_TX_FIFO_DEPTH) + { + break; + } + } + + level = rt_spin_lock_irqsave(&i2c->fifo_lock); + + for (int i = 0; i < data_cnt; ++i) + { + spacemit_i2c_write(i2c, REG_WFIFO, data_buf[i]); + } + + rt_spin_unlock_irqrestore(&i2c->fifo_lock, level); +} + +static void spacemit_i2c_fifo_xfer_copy_buffer(struct spacemit_i2c *i2c) +{ + int idx = 0, cnt = 0; + struct rt_i2c_msg *msg; + + /* Copy the rx FIFO buffer to msg */ + while (idx < i2c->num) + { + msg = i2c->msgs + idx; + if (msg->flags & RT_I2C_RD) + { + cnt = msg->len; + while (cnt > 0) + { + *(msg->buf + msg->len - cnt) = spacemit_i2c_read(i2c, REG_RFIFO); + cnt--; + } + } + idx++; + } +} + +static rt_err_t spacemit_i2c_fifo_xfer(struct spacemit_i2c *i2c) +{ + rt_err_t err = RT_EOK; + + spacemit_i2c_fifo_xfer_fill_buffer(i2c); + + if ((err = rt_completion_wait(&i2c->complete, i2c->timeout))) + { + spacemit_i2c_bus_reset(i2c); + goto _err_out; + } + + if (i2c->i2c_err) + { + err = -RT_ERROR; + spacemit_i2c_flush_fifo_buffer(i2c); + goto _err_out; + } + + spacemit_i2c_fifo_xfer_copy_buffer(i2c); + +_err_out: + return err; +} + +static rt_err_t spacemit_i2c_byte_xfer(struct spacemit_i2c *i2c) +{ + rt_err_t err = RT_EOK; + + if (i2c->i2c_err) + { + return -RT_ERROR; + } + + if (i2c->phase == SPACEMIT_I2C_XFER_IDLE) + { + if (i2c->high_mode && i2c->is_xfer_start) + { + spacemit_i2c_byte_xfer_send_master_code(i2c); + } + else + { + spacemit_i2c_byte_xfer_send_slave_addr(i2c); + } + + i2c->is_xfer_start = RT_FALSE; + } + else if (i2c->phase == SPACEMIT_I2C_XFER_MASTER_CODE) + { + spacemit_i2c_byte_xfer_send_slave_addr(i2c); + } + else + { + err = spacemit_i2c_byte_xfer_body(i2c); + } + + return err; +} + +static void spacemit_i2c_choose_xfer_mode(struct spacemit_i2c *i2c) +{ + unsigned long timeout; + int idx = 0, cnt = 0, freq; + rt_bool_t block = RT_FALSE; + + /* Scan msgs */ + if (i2c->high_mode) + { + cnt++; + } + + i2c->rx_total = 0; + + while (idx < i2c->num) + { + cnt += (i2c->msgs + idx)->len + 1; + + if ((i2c->msgs + idx)->flags & RT_I2C_RD) + { + i2c->rx_total += (i2c->msgs + idx)->len; + } + + idx++; + } + + if (i2c->dma_disable || block) + { + i2c->xfer_mode = SPACEMIT_I2C_MODE_INTERRUPT; + } + else + { + if (cnt <= SPACEMIT_I2C_TX_FIFO_DEPTH) + { + i2c->xfer_mode = SPACEMIT_I2C_MODE_FIFO; + } + else + { + i2c->xfer_mode = SPACEMIT_I2C_MODE_DMA; + } + + /* Flush fifo buffer */ + spacemit_i2c_flush_fifo_buffer(i2c); + } + + /* + * if total message length is too large to over the allocated MDA + * total buf length, use interrupt mode. This may happens in the + * syzkaller test. + */ + if (cnt > (SPACEMIT_I2C_MAX_MSG_LEN * SPACEMIT_I2C_SCATTERLIST_SIZE) || + i2c->rx_total > SPACEMIT_I2C_DMA_RX_BUF_LEN) + { + i2c->xfer_mode = SPACEMIT_I2C_MODE_INTERRUPT; + } + + /* calculate timeout */ + if (i2c->high_mode) + { + freq = 1500000; + } + else if (i2c->fast_mode) + { + freq = 400000; + } + else + { + freq = 100000; + } + + timeout = cnt * 9 * USEC_PER_SEC / freq; + + if (i2c->xfer_mode == SPACEMIT_I2C_MODE_INTERRUPT || + i2c->xfer_mode == SPACEMIT_I2C_MODE_PIO) + { + timeout += (cnt - 1) * 220; + } + + timeout /= USEC_PER_MSEC; + + if (i2c->xfer_mode == SPACEMIT_I2C_MODE_INTERRUPT) + { + i2c->timeout = rt_tick_from_millisecond(timeout + 500); + } + else + { + i2c->timeout = rt_tick_from_millisecond(timeout + 100); + } +} + +static void spacemit_i2c_init_xfer_params(struct spacemit_i2c *i2c) +{ + /* Initialize transfer parameters */ + i2c->msg_idx = 0; + i2c->cur_msg = i2c->msgs; + i2c->msg_buf = i2c->cur_msg->buf; + i2c->rx_cnt = 0; + i2c->tx_cnt = 0; + i2c->i2c_err = 0; + i2c->i2c_status = 0; + i2c->phase = SPACEMIT_I2C_XFER_IDLE; + + /* Only send master code once for high speed mode */ + i2c->is_xfer_start = RT_TRUE; +} + +rt_used +static rt_ssize_t spacemit_i2c_pio_xfer(struct spacemit_i2c *i2c) +{ + rt_ssize_t res; + int xfer_try = 0; + rt_uint32_t status; + signed long timeout; + +_xfer_retry: + /* calculate timeout */ + spacemit_i2c_choose_xfer_mode(i2c); + i2c->xfer_mode = SPACEMIT_I2C_MODE_PIO; + timeout = i2c->timeout * (1000U / RT_TICK_PER_SECOND) * USEC_PER_MSEC; + + if (!i2c->clk_always_on) + { + rt_clk_enable(i2c->clk); + } + + spacemit_i2c_controller_reset(i2c); + rt_hw_us_delay(2); + + spacemit_i2c_unit_init(i2c); + + spacemit_i2c_clear_int_status(i2c, SPACEMIT_I2C_INT_STATUS_MASK); + + spacemit_i2c_init_xfer_params(i2c); + + spacemit_i2c_mark_rw_flag(i2c); + + spacemit_i2c_enable(i2c); + + if ((res = spacemit_i2c_byte_xfer(i2c)) < 0) + { + goto _out; + } + + while (i2c->num > 0 && timeout > 0) + { + status = spacemit_i2c_read(i2c, REG_SR); + spacemit_i2c_clear_int_status(i2c, status); + i2c->i2c_status = status; + + /* Bus error, arbitration lost */ + i2c->i2c_err = status & (SR_BED | SR_ALD); + if (i2c->i2c_err) + { + res = -RT_ERROR; + break; + } + + /* Receive full */ + if (status & SR_IRF) + { + if ((res = spacemit_i2c_byte_xfer(i2c)) < 0) + { + break; + } + } + + /* Transmit empty */ + if (status & SR_ITE) + { + if ((res = spacemit_i2c_byte_xfer(i2c)) < 0) + { + break; + } + } + + /* Transaction done */ + if (status & SR_MSD) + { + break; + } + + rt_hw_us_delay(10); + timeout -= 10; + } + + spacemit_i2c_disable(i2c); + + if (!i2c->clk_always_on) + { + rt_clk_disable(i2c->clk); + } + + if (timeout <= 0) + { + spacemit_i2c_bus_reset(i2c); + rt_hw_us_delay(100); + res = -RT_ETIMEOUT; + goto _out; + } + + /* Process i2c error */ + if (i2c->i2c_err) + { + res = -RT_EIO; + } + +_out: + xfer_try++; + /* Retry i2c transfer 3 times for timeout and bus busy */ + if ((res == -RT_ETIMEOUT || res == -RT_EIO) && xfer_try <= i2c->drv_retries) + { + rt_hw_us_delay(150); + res = 0; + goto _xfer_retry; + } + + return res < 0 ? res : i2c->num; +} + +static rt_ssize_t spacemit_i2c_xfer(struct rt_i2c_bus_device *bus, + struct rt_i2c_msg msgs[], rt_uint32_t num) +{ + int xfer_try = 0; + rt_ssize_t res = 0; + rt_bool_t clk_directly = RT_FALSE; + struct spacemit_i2c *i2c = raw_to_spacemit_i2c(bus); + + i2c->msgs = msgs; + i2c->num = num; + +_xfer_retry: + /* If unit keeps the last control status, don't need to do reset */ + if (spacemit_i2c_read(i2c, REG_CR) != i2c->i2c_ctrl_reg_value) + { + /* Controller & bus reset */ + spacemit_i2c_reset(i2c); + } + + /* Choose transfer mode */ + spacemit_i2c_choose_xfer_mode(i2c); + + /* Unit init */ + spacemit_i2c_unit_init(i2c); + + /* Clear all interrupt status */ + spacemit_i2c_clear_int_status(i2c, SPACEMIT_I2C_INT_STATUS_MASK); + + spacemit_i2c_init_xfer_params(i2c); + + res = spacemit_i2c_read(i2c, REG_RST_CYC); + spacemit_i2c_write(i2c, REG_RST_CYC, I2C_SDA_GLITCH_FIX_BYPASS | res); + + spacemit_i2c_mark_rw_flag(i2c); + + spacemit_i2c_enable(i2c); + + /* Wait for bus busy */ + if ((res = spacemit_i2c_recover_bus_busy(i2c))) + { + goto _err_recover; + } + + /* Msg transmit */ + if (i2c->xfer_mode == SPACEMIT_I2C_MODE_INTERRUPT) + { + res = spacemit_i2c_byte_xfer(i2c); + } + else if (i2c->xfer_mode == SPACEMIT_I2C_MODE_FIFO) + { + res = spacemit_i2c_fifo_xfer(i2c); + } + else + { + /* DMA */ + res = -RT_ENOSYS; + } + + if (res < 0) + { + if (res != -RT_ETIMEOUT) + { + res = -RT_EINVAL; + } + goto _err_xfer; + } + + if (i2c->xfer_mode == SPACEMIT_I2C_MODE_INTERRUPT) + { + if ((res = rt_completion_wait(&i2c->complete, i2c->timeout))) + { + spacemit_i2c_bus_reset(i2c); + spacemit_i2c_reset(i2c); + goto _timeout_xfex; + } + } + +_err_xfer: + if (!res) + { + spacemit_i2c_check_bus_release(i2c); + } + +_err_recover: +_timeout_xfex: + /* Disable spacemit i2c */ + spacemit_i2c_disable(i2c); + + /* Process i2c error */ + if (i2c->i2c_err) + { + if (i2c->i2c_err & (SR_BED | SR_ALD)) + { + spacemit_i2c_reset(i2c); + } + + /* Try transfer again */ + if (i2c->i2c_err & (SR_RXOV | SR_ALD)) + { + spacemit_i2c_flush_fifo_buffer(i2c); + } + + res = -RT_EIO; + } + + xfer_try++; + /* Retry i2c transfer 3 times for timeout and bus busy */ + if ((res == -RT_ETIMEOUT || res == -RT_EIO) && xfer_try <= i2c->drv_retries) + { + rt_hw_us_delay(175); + res = 0; + goto _xfer_retry; + } + + if (clk_directly) + { + /* If clock is enabled directly, here disable it */ + rt_clk_disable(i2c->clk); + } + + return res < 0 ? res : num; +} + +const static struct rt_i2c_bus_device_ops spacemit_i2c_ops = +{ + .master_xfer = spacemit_i2c_xfer, +}; + +static void spacemit_i2c_isr(int irq, void *param) +{ + rt_ssize_t res = 0; + rt_uint32_t status, ctrl; + struct spacemit_i2c *i2c = param; + + /* Record i2c status */ + status = spacemit_i2c_read(i2c, REG_SR); + i2c->i2c_status = status; + + /* Check if a valid interrupt status */ + if (!status) + { + return; + } + + /* Bus error, rx overrun, arbitration lost */ + i2c->i2c_err = status & (SR_BED | SR_RXOV | SR_ALD); + + /* Clear interrupt status bits[31:18]*/ + spacemit_i2c_clear_int_status(i2c, status); + + /* Error happens */ + if (i2c->i2c_err) + { + goto _err_out; + } + + /* Process interrupt mode */ + if (i2c->xfer_mode == SPACEMIT_I2C_MODE_INTERRUPT) + { + res = spacemit_i2c_byte_xfer(i2c); + } + +_err_out: + /* Send transaction complete signal: error happens, detect master stop */ + if (i2c->i2c_err || res < 0 || (status & SR_MSD)) + { + /* + * Here the transaction is already done, we don't need any + * other interrupt signals from now, in case any interrupt + * happens before spacemit_i2c_xfer to disable irq and i2c unit, + * we mask all the interrupt signals and clear the interrupt status. + */ + ctrl = spacemit_i2c_read(i2c, REG_CR); + ctrl &= ~SPACEMIT_I2C_INT_CTRL_MASK; + spacemit_i2c_write(i2c, REG_CR, ctrl); + + spacemit_i2c_clear_int_status(i2c, SPACEMIT_I2C_INT_STATUS_MASK); + + rt_completion_done(&i2c->complete); + } +} + +static void spacemit_i2c_free(struct spacemit_i2c *i2c, struct rt_device *dev) +{ + if (i2c->regs) + { + rt_iounmap(i2c->regs); + } + + if (!rt_is_err_or_null(i2c->rstc)) + { + rt_reset_control_assert(i2c->rstc); + rt_reset_control_put(i2c->rstc); + } + + if (!rt_is_err_or_null(i2c->clk)) + { + rt_clk_disable_unprepare(i2c->clk); + rt_clk_put(i2c->clk); + } + + rt_free(i2c); +} + +static rt_err_t spacemit_i2c_probe(struct rt_platform_device *pdev) +{ + rt_err_t err; + const char *dev_name; + struct rt_device *dev = &pdev->parent; + struct spacemit_i2c *i2c = rt_calloc(1, sizeof(*i2c)); + + if (!i2c) + { + return -RT_ENOMEM; + } + + 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; + } + + i2c->rstc = rt_reset_control_get_by_index(dev, 0); + if (rt_is_err(i2c->rstc)) + { + goto _fail; + } + + /* Reset the i2c controller */ + rt_reset_control_assert(i2c->rstc); + rt_hw_us_delay(200); + rt_reset_control_deassert(i2c->rstc); + + /* Enable fast speed mode */ + i2c->fast_mode = rt_dm_dev_prop_read_bool(dev, "spacemit,i2c-fast-mode"); + /* Enable high speed mode */ + i2c->high_mode = rt_dm_dev_prop_read_bool(dev, "spacemit,i2c-high-mode"); + + if (i2c->high_mode) + { + /* Get master code for high speed mode */ + if (rt_dm_dev_prop_read_u8(dev, "spacemit,i2c-master-code", &i2c->master_code)) + { + i2c->master_code = 0x0e; + LOG_W("Failed to get i2c master code, use default: %#x", i2c->master_code); + } + } + + if ((err = rt_dm_dev_prop_read_u32(dev, "spacemit,i2c-clk-rate", &i2c->clk_rate))) + { + goto _fail; + } + + if ((err = rt_dm_dev_prop_read_u32(dev, "spacemit,i2c-lcr", &i2c->i2c_lcr))) + { + goto _fail; + } + + if ((err = rt_dm_dev_prop_read_u32(dev, "spacemit,i2c-wcr", &i2c->i2c_wcr))) + { + goto _fail; + } + + if ((err = rt_dm_dev_prop_read_u32(dev, "spacemit,adapter-id", (rt_uint32_t *)&pdev->dev_id))) + { + pdev->dev_id = -1; + } + + /* Disable DMA transfer mode, default: interrupt mode */ + i2c->dma_disable = RT_TRUE; + i2c->xfer_mode = i2c->dma_disable ? SPACEMIT_I2C_MODE_INTERRUPT : SPACEMIT_I2C_MODE_DMA; + + i2c->drv_retries = 3; + + /* The clock will always on and not use runtime mechanism */ + i2c->clk_always_on = rt_dm_dev_prop_read_bool(dev, "spacemit,clk-always-on"); + + /* APB clock: 26MHz or 52MHz */ + if ((err = rt_dm_dev_prop_read_u32(dev, "spacemit,apb_clock", &i2c->apb_clock))) + { + goto _fail; + } + else if (i2c->apb_clock != SPACEMIT_I2C_APB_CLOCK_26M && + i2c->apb_clock != SPACEMIT_I2C_APB_CLOCK_52M) + { + err = -RT_EINVAL; + goto _fail; + } + + i2c->clk = rt_clk_get_by_index(dev, 0); + if (rt_is_err(i2c->clk)) + { + err = rt_ptr_err(i2c->clk); + goto _fail; + } + rt_clk_prepare_enable(i2c->clk); + + rt_completion_init(&i2c->complete); + rt_spin_lock_init(&i2c->fifo_lock); + + if (pdev->dev_id >= 0) + { + rt_dm_dev_set_name(&i2c->parent.parent, "i2c%d", pdev->dev_id); + } + else + { + 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, spacemit_i2c_isr, i2c, dev_name); + rt_hw_interrupt_umask(i2c->irq); + + dev->user_data = i2c; + + i2c->parent.ops = &spacemit_i2c_ops; + i2c->parent.parent.ofw_node = dev->ofw_node; + + if ((err = rt_i2c_bus_device_register(&i2c->parent, dev_name))) + { + goto _free_irq; + } + + rt_dm_dev_bind_fwdata(dev, RT_NULL, i2c); + + return RT_EOK; + +_free_irq: + rt_hw_interrupt_mask(i2c->irq); + rt_pic_detach_irq(i2c->irq, i2c); + +_fail: + spacemit_i2c_free(i2c, dev); + + return err; +} + +static rt_err_t spacemit_i2c_remove(struct rt_platform_device *pdev) +{ + struct rt_device *dev = &pdev->parent; + struct spacemit_i2c *i2c = dev->user_data; + + rt_hw_interrupt_mask(i2c->irq); + rt_pic_detach_irq(i2c->irq, i2c); + + rt_device_unregister(&i2c->parent.parent); + + spacemit_i2c_free(i2c, dev); + + return RT_EOK; +} + +static const struct rt_ofw_node_id spacemit_i2c_ofw_ids[] = +{ + { .compatible = "spacemit,k1x-i2c" }, + { /* sentinel */ } +}; + +static struct rt_platform_driver spacemit_i2c_driver = +{ + .name = "spacemit-k1x-i2c", + .ids = spacemit_i2c_ofw_ids, + + .probe = spacemit_i2c_probe, + .remove = spacemit_i2c_remove, +}; +RT_PLATFORM_DRIVER_EXPORT(spacemit_i2c_driver); diff --git a/bsp/spacemit/dm/include/dt-bindings/clock/k1x.h b/bsp/spacemit/dm/include/dt-bindings/clock/k1x.h new file mode 100755 index 00000000000..9e2a5e0f800 --- /dev/null +++ b/bsp/spacemit/dm/include/dt-bindings/clock/k1x.h @@ -0,0 +1,242 @@ +/* + * Copyright (c) 2006-2025, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __DT_BINDINGS_CLOCK_K1X_H__ +#define __DT_BINDINGS_CLOCK_K1X_H__ + +#define CLK_PLL2 0 +#define CLK_PLL3 1 +#define CLK_PLL1_D2 2 +#define CLK_PLL1_D3 3 +#define CLK_PLL1_D4 4 +#define CLK_PLL1_D5 5 +#define CLK_PLL1_D6 6 +#define CLK_PLL1_D7 7 +#define CLK_PLL1_D8 8 +#define CLK_PLL1_D11 9 +#define CLK_PLL1_D13 10 +#define CLK_PLL1_D23 11 +#define CLK_PLL1_D64 12 +#define CLK_PLL1_D10_AUD 13 +#define CLK_PLL1_D100_AUD 14 +#define CLK_PLL2_D1 15 +#define CLK_PLL2_D2 16 +#define CLK_PLL2_D3 17 +#define CLK_PLL2_D4 18 +#define CLK_PLL2_D5 19 +#define CLK_PLL2_D6 20 +#define CLK_PLL2_D7 21 +#define CLK_PLL2_D8 22 +#define CLK_PLL3_D1 23 +#define CLK_PLL3_D2 24 +#define CLK_PLL3_D3 25 +#define CLK_PLL3_D4 26 +#define CLK_PLL3_D5 27 +#define CLK_PLL3_D6 28 +#define CLK_PLL3_D7 29 +#define CLK_PLL3_D8 30 +#define CLK_PLL1_307P2 31 +#define CLK_PLL1_76P8 32 +#define CLK_PLL1_61P44 33 +#define CLK_PLL1_153P6 34 +#define CLK_PLL1_102P4 35 +#define CLK_PLL1_51P2 36 +#define CLK_PLL1_51P2_AP 37 +#define CLK_PLL1_57P6 38 +#define CLK_PLL1_25P6 39 +#define CLK_PLL1_12P8 40 +#define CLK_PLL1_12P8_WDT 41 +#define CLK_PLL1_6P4 42 +#define CLK_PLL1_3P2 43 +#define CLK_PLL1_1P6 44 +#define CLK_PLL1_0P8 45 +#define CLK_PLL1_351 46 +#define CLK_PLL1_409P6 47 +#define CLK_PLL1_204P8 48 +#define CLK_PLL1_491 49 +#define CLK_PLL1_245P76 50 +#define CLK_PLL1_614 51 +#define CLK_PLL1_47P26 52 +#define CLK_PLL1_31P5 53 +#define CLK_PLL1_819 54 +#define CLK_PLL1_1228 55 +#define CLK_SLOW_UART1 56 +#define CLK_SLOW_UART2 57 +#define CLK_UART1 58 +#define CLK_UART2 59 +#define CLK_UART3 60 +#define CLK_UART4 61 +#define CLK_UART5 62 +#define CLK_UART6 63 +#define CLK_UART7 64 +#define CLK_UART8 65 +#define CLK_UART9 66 +#define CLK_GPIO 67 +#define CLK_PWM0 68 +#define CLK_PWM1 69 +#define CLK_PWM2 70 +#define CLK_PWM3 71 +#define CLK_PWM4 72 +#define CLK_PWM5 73 +#define CLK_PWM6 74 +#define CLK_PWM7 75 +#define CLK_PWM8 76 +#define CLK_PWM9 77 +#define CLK_PWM10 78 +#define CLK_PWM11 79 +#define CLK_PWM12 80 +#define CLK_PWM13 81 +#define CLK_PWM14 82 +#define CLK_PWM15 83 +#define CLK_PWM16 84 +#define CLK_PWM17 85 +#define CLK_PWM18 86 +#define CLK_PWM19 87 +#define CLK_SSP3 88 +#define CLK_RTC 89 +#define CLK_TWSI0 90 +#define CLK_TWSI1 91 +#define CLK_TWSI2 92 +#define CLK_TWSI4 93 +#define CLK_TWSI5 94 +#define CLK_TWSI6 95 +#define CLK_TWSI7 96 +#define CLK_TWSI8 97 +#define CLK_TIMERS1 98 +#define CLK_TIMERS2 99 +#define CLK_AIB 100 +#define CLK_ONEWIRE 101 +#define CLK_SSPA0 102 +#define CLK_SSPA1 103 +#define CLK_DRO 104 +#define CLK_IR 105 +#define CLK_TSEN 106 +#define CLK_IPC_AP2AUD 107 +#define CLK_CAN0 108 +#define CLK_CAN0_BUS 109 +#define CLK_WDT 110 +#define CLK_RIPC 111 +#define CLK_JPG 112 +#define CLK_JPF_4KAFBC 113 +#define CLK_JPF_2KAFBC 114 +#define CLK_CCIC2PHY 115 +#define CLK_CCIC3PHY 116 +#define CLK_CSI 117 +#define CLK_CAMM0 118 +#define CLK_CAMM1 119 +#define CLK_CAMM2 120 +#define CLK_ISP_CPP 121 +#define CLK_ISP_BUS 122 +#define CLK_ISP 123 +#define CLK_DPU_MCLK 124 +#define CLK_DPU_ESC 125 +#define CLK_DPU_BIT 126 +#define CLK_DPU_PXCLK 127 +#define CLK_DPU_HCLK 128 +#define CLK_DPU_SPI 129 +#define CLK_DPU_SPI_HBUS 130 +#define CLK_DPU_SPIBUS 131 +#define CLK_SPU_SPI_ACLK 132 +#define CLK_V2D 133 +#define CLK_CCIC_4X 134 +#define CLK_CCIC1PHY 135 +#define CLK_SDH_AXI 136 +#define CLK_SDH0 137 +#define CLK_SDH1 138 +#define CLK_SDH2 139 +#define CLK_USB_P1 140 +#define CLK_USB_AXI 141 +#define CLK_USB30 142 +#define CLK_QSPI 143 +#define CLK_QSPI_BUS 144 +#define CLK_DMA 145 +#define CLK_AES 146 +#define CLK_VPU 147 +#define CLK_GPU 148 +#define CLK_EMMC 149 +#define CLK_EMMC_X 150 +#define CLK_AUDIO 151 +#define CLK_HDMI 152 +#define CLK_CCI550 153 +#define CLK_PMUA_ACLK 154 +#define CLK_CPU_C0_HI 155 +#define CLK_CPU_C0_CORE 156 +#define CLK_CPU_C0_ACE 157 +#define CLK_CPU_C0_TCM 158 +#define CLK_CPU_C1_HI 159 +#define CLK_CPU_C1_CORE 160 +#define CLK_CPU_C1_ACE 161 +#define CLK_PCIE0 162 +#define CLK_PCIE1 163 +#define CLK_PCIE2 164 +#define CLK_EMAC0_BUS 165 +#define CLK_EMAC0_PTP 166 +#define CLK_EMAC1_BUS 167 +#define CLK_EMAC1_PTP 168 + +#define CLK_SEC_UART1 169 +#define CLK_SEC_SSP2 170 +#define CLK_SEC_TWSI3 171 +#define CLK_SEC_RTC 172 +#define CLK_SEC_TIMERS0 173 +#define CLK_SEC_KPC 174 +#define CLK_SEC_GPIO 175 + +#define CLK_APB 176 + +#define CLK_PLL3_80 177 +#define CLK_PLL3_40 178 +#define CLK_PLL3_20 179 + +#define CLK_SLOW_UART 180 + +#define CLK_I2S_SYSCLK 181 +#define CLK_I2S_BCLK 182 +#define CLK_RCPU_HDMIAUDIO 183 +#define CLK_RCPU_CAN 184 +#define CLK_RCPU_CAN_BUS 185 + +#define CLK_RCPU_I2C0 186 +#define CLK_RCPU_SSP0 187 +#define CLK_RCPU_IR 188 +#define CLK_RCPU_UART0 189 +#define CLK_RCPU_UART1 190 + +#define CLK_DPLL1 191 +#define CLK_DPLL2 192 + +#define CLK_DFC_LVL0 193 +#define CLK_DFC_LVL1 194 +#define CLK_DFC_LVL2 195 +#define CLK_DFC_LVL3 196 +#define CLK_DFC_LVL4 197 +#define CLK_DFC_LVL5 198 +#define CLK_DFC_LVL6 199 +#define CLK_DFC_LVL7 200 +#define CLK_DDR 201 + +#define CLK_RCPU2_PWM0 202 +#define CLK_RCPU2_PWM1 203 +#define CLK_RCPU2_PWM2 204 +#define CLK_RCPU2_PWM3 205 +#define CLK_RCPU2_PWM4 206 +#define CLK_RCPU2_PWM5 207 +#define CLK_RCPU2_PWM6 208 +#define CLK_RCPU2_PWM7 209 +#define CLK_RCPU2_PWM8 210 +#define CLK_RCPU2_PWM9 211 + +#define CLK_AUDIO_APB 212 +#define CLK_AUDIO_AXI 213 + +#define CLK_PM_MN_SRC 214 +#define CLK_PM_MN 215 +#define CLK_PM_MN2_SRC 216 +#define CLK_PM_MN2 217 + +#define CLK_MAX_NO 218 + +#endif /* __DT_BINDINGS_CLOCK_K1X_H__ */ diff --git a/bsp/spacemit/dm/include/dt-bindings/pinctrl/k1x.h b/bsp/spacemit/dm/include/dt-bindings/pinctrl/k1x.h new file mode 100755 index 00000000000..101fcba595d --- /dev/null +++ b/bsp/spacemit/dm/include/dt-bindings/pinctrl/k1x.h @@ -0,0 +1,211 @@ +/* + * Copyright (c) 2006-2025, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __DT_BINDINGS_PINCTRL_K1X_H__ +#define __DT_BINDINGS_PINCTRL_K1X_H__ + +/* Pin offset */ +#define PINID(x) ((x) + 1) + +#define GPIO_00 PINID(0) +#define GPIO_01 PINID(1) +#define GPIO_02 PINID(2) +#define GPIO_03 PINID(3) +#define GPIO_04 PINID(4) +#define GPIO_05 PINID(5) +#define GPIO_06 PINID(6) +#define GPIO_07 PINID(7) +#define GPIO_08 PINID(8) +#define GPIO_09 PINID(9) +#define GPIO_10 PINID(10) +#define GPIO_11 PINID(11) +#define GPIO_12 PINID(12) +#define GPIO_13 PINID(13) +#define GPIO_14 PINID(14) +#define GPIO_15 PINID(15) +#define GPIO_16 PINID(16) +#define GPIO_17 PINID(17) +#define GPIO_18 PINID(18) +#define GPIO_19 PINID(19) +#define GPIO_20 PINID(20) +#define GPIO_21 PINID(21) +#define GPIO_22 PINID(22) +#define GPIO_23 PINID(23) +#define GPIO_24 PINID(24) +#define GPIO_25 PINID(25) +#define GPIO_26 PINID(26) +#define GPIO_27 PINID(27) +#define GPIO_28 PINID(28) +#define GPIO_29 PINID(29) +#define GPIO_30 PINID(30) +#define GPIO_31 PINID(31) + +#define GPIO_32 PINID(32) +#define GPIO_33 PINID(33) +#define GPIO_34 PINID(34) +#define GPIO_35 PINID(35) +#define GPIO_36 PINID(36) +#define GPIO_37 PINID(37) +#define GPIO_38 PINID(38) +#define GPIO_39 PINID(39) +#define GPIO_40 PINID(40) +#define GPIO_41 PINID(41) +#define GPIO_42 PINID(42) +#define GPIO_43 PINID(43) +#define GPIO_44 PINID(44) +#define GPIO_45 PINID(45) +#define GPIO_46 PINID(46) +#define GPIO_47 PINID(47) +#define GPIO_48 PINID(48) +#define GPIO_49 PINID(49) +#define GPIO_50 PINID(50) +#define GPIO_51 PINID(51) +#define GPIO_52 PINID(52) +#define GPIO_53 PINID(53) +#define GPIO_54 PINID(54) +#define GPIO_55 PINID(55) +#define GPIO_56 PINID(56) +#define GPIO_57 PINID(57) +#define GPIO_58 PINID(58) +#define GPIO_59 PINID(59) +#define GPIO_60 PINID(60) +#define GPIO_61 PINID(61) +#define GPIO_62 PINID(62) +#define GPIO_63 PINID(63) + +#define GPIO_64 PINID(64) +#define GPIO_65 PINID(65) +#define GPIO_66 PINID(66) +#define GPIO_67 PINID(67) +#define GPIO_68 PINID(68) +#define GPIO_69 PINID(69) +#define PRI_TDI PINID(70) +#define PRI_TMS PINID(71) +#define PRI_TCK PINID(72) +#define PRI_TDO PINID(73) +#define GPIO_74 PINID(74) +#define GPIO_75 PINID(75) +#define GPIO_76 PINID(76) +#define GPIO_77 PINID(77) +#define GPIO_78 PINID(78) +#define GPIO_79 PINID(79) +#define GPIO_80 PINID(80) +#define GPIO_81 PINID(81) +#define GPIO_82 PINID(82) +#define GPIO_83 PINID(83) +#define GPIO_84 PINID(84) +#define GPIO_85 PINID(85) + +#define QSPI_DAT0 PINID(89) +#define QSPI_DAT1 PINID(90) +#define QSPI_DAT2 PINID(91) +#define QSPI_DAT3 PINID(92) +#define QSPI_CSI PINID(93) +#define QSPI_CLK PINID(94) + +#define MMC1_DAT3 PINID(109) +#define MMC1_DAT2 PINID(110) +#define MMC1_DAT1 PINID(111) +#define MMC1_DAT0 PINID(112) +#define MMC1_CMD PINID(113) +#define MMC1_CLK PINID(114) +#define GPIO_110 PINID(115) +#define PWR_SCL PINID(116) +#define PWR_SDA PINID(117) +#define VCXO_EN PINID(118) +#define DVL0 PINID(119) +#define DVL1 PINID(120) +#define PMIC_INT_N PINID(121) +#define GPIO_86 PINID(122) +#define GPIO_87 PINID(123) +#define GPIO_88 PINID(124) +#define GPIO_89 PINID(125) +#define GPIO_90 PINID(126) +#define GPIO_91 PINID(127) +#define GPIO_92 PINID(128) + +#define GPIO_111 PINID(130) +#define GPIO_112 PINID(131) +#define GPIO_113 PINID(132) +#define GPIO_114 PINID(133) +#define GPIO_115 PINID(134) +#define GPIO_116 PINID(135) +#define GPIO_117 PINID(136) +#define GPIO_118 PINID(137) +#define GPIO_119 PINID(138) +#define GPIO_120 PINID(139) +#define GPIO_121 PINID(140) +#define GPIO_122 PINID(141) +#define GPIO_123 PINID(142) +#define GPIO_124 PINID(143) +#define GPIO_125 PINID(144) +#define GPIO_126 PINID(145) +#define GPIO_127 PINID(146) + +#define EMMC_D0 PINID(147) +#define EMMC_D1 PINID(148) +#define EMMC_D2 PINID(149) +#define EMMC_D3 PINID(150) +#define EMMC_D4 PINID(151) +#define EMMC_D5 PINID(152) +#define EMMC_D6 PINID(153) +#define EMMC_D7 PINID(154) +#define EMMC_DS PINID(155) +#define EMMC_CLK PINID(156) +#define EMMC_CMD PINID(157) + +/* Pin mux */ +#define MUX_MODE0 0 +#define MUX_MODE1 1 +#define MUX_MODE2 2 +#define MUX_MODE3 3 +#define MUX_MODE4 4 +#define MUX_MODE5 5 +#define MUX_MODE6 6 +#define MUX_MODE7 7 + +/* Strong pull resistor */ +#define SPU_EN (1 << 3) + +/* Edge detect */ +#define EDGE_NONE (1 << 6) +#define EDGE_RISE (1 << 4) +#define EDGE_FALL (1 << 5) +#define EDGE_BOTH (3 << 4) + +/* Slew rate output control */ +#define SLE_EN (1 << 7) + +/* Schmitter trigger input threshhold */ +#define ST00 (0 << 8) +#define ST01 (1 << 8) +#define ST02 (2 << 8) +#define ST03 (3 << 8) + +/* Driver strength*/ +#define PAD_1V8_DS0 (0 << 11) +#define PAD_1V8_DS1 (1 << 11) +#define PAD_1V8_DS2 (2 << 11) +#define PAD_1V8_DS3 (3 << 11) + +/* Ds2 ---> bit10, ds1 ----> bit12, ds0 ----> bit11 */ +#define PAD_3V_DS0 (0 << 10) /* bit[12:10] 000 */ +#define PAD_3V_DS1 (2 << 10) /* bit[12:10] 010 */ +#define PAD_3V_DS2 (4 << 10) /* bit[12:10] 100 */ +#define PAD_3V_DS3 (6 << 10) /* bit[12:10] 110 */ +#define PAD_3V_DS4 (1 << 10) /* bit[12:10] 001 */ +#define PAD_3V_DS5 (3 << 10) /* bit[12:10] 011 */ +#define PAD_3V_DS6 (5 << 10) /* bit[12:10] 101 */ +#define PAD_3V_DS7 (7 << 10) /* bit[12:10] 111 */ + +/* Pull up/down */ +#define PULL_DIS (0 << 13) /* bit[15:13] 000 */ +#define PULL_UP (6 << 13) /* bit[15:13] 110 */ +#define PULL_DOWN (5 << 13) /* bit[15:13] 101 */ + +#define K1X_PADCONF(pinid, conf, mux) ((pinid) * 4) (conf) (mux) + +#endif /* __DT_BINDINGS_PINCTRL_K1X_H__ */ diff --git a/bsp/spacemit/dm/include/dt-bindings/power/k1x_pmu.h b/bsp/spacemit/dm/include/dt-bindings/power/k1x_pmu.h new file mode 100755 index 00000000000..7281265d26c --- /dev/null +++ b/bsp/spacemit/dm/include/dt-bindings/power/k1x_pmu.h @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2006-2025, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __DT_BINDINGS_POWER_K1X_PMU_H__ +#define __DT_BINDINGS_POWER_K1X_PMU_H__ + +#define K1X_PMU_BUS_PWR_DOMAIN 0 +#define K1X_PMU_VPU_PWR_DOMAIN 1 +#define K1X_PMU_GPU_PWR_DOMAIN 2 +#define K1X_PMU_LCD_PWR_DOMAIN 3 +#define K1X_PMU_ISP_PWR_DOMAIN 4 +#define K1X_PMU_AUD_PWR_DOMAIN 5 +#define K1X_PMU_GNSS_PWR_DOMAIN 6 +#define K1X_PMU_HDMI_PWR_DOMAIN 7 +#define K1X_PMU_DUMMY_PWR_DOMAIN 8 + +#endif /* __DT_BINDINGS_POWER_K1X_PMU_H__ */ diff --git a/bsp/spacemit/dm/include/dt-bindings/reset/k1x.h b/bsp/spacemit/dm/include/dt-bindings/reset/k1x.h new file mode 100755 index 00000000000..69bf0ce1479 --- /dev/null +++ b/bsp/spacemit/dm/include/dt-bindings/reset/k1x.h @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2006-2025, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __DT_BINDINGS_RESET_K1X_H__ +#define __DT_BINDINGS_RESET_K1X_H__ + +/* APBC */ +#define RESET_UART1 1 +#define RESET_UART2 2 +#define RESET_GPIO 3 +#define RESET_PWM0 4 +#define RESET_PWM1 5 +#define RESET_PWM2 6 +#define RESET_PWM3 7 +#define RESET_PWM4 8 +#define RESET_PWM5 9 +#define RESET_PWM6 10 +#define RESET_PWM7 11 +#define RESET_PWM8 12 +#define RESET_PWM9 13 +#define RESET_PWM10 14 +#define RESET_PWM11 15 +#define RESET_PWM12 16 +#define RESET_PWM13 17 +#define RESET_PWM14 18 +#define RESET_PWM15 19 +#define RESET_PWM16 20 +#define RESET_PWM17 21 +#define RESET_PWM18 22 +#define RESET_PWM19 23 +#define RESET_SSP3 24 +#define RESET_UART3 25 +#define RESET_RTC 26 +#define RESET_TWSI0 27 + +#define RESET_TIMERS1 28 +#define RESET_AIB 29 +#define RESET_TIMERS2 30 +#define RESET_ONEWIRE 31 +#define RESET_SSPA0 32 +#define RESET_SSPA1 33 +#define RESET_DRO 34 +#define RESET_IR 35 +#define RESET_TWSI1 36 + +#define RESET_TSEN 37 +#define RESET_TWSI2 38 +#define RESET_TWSI4 39 +#define RESET_TWSI5 40 +#define RESET_TWSI6 41 +#define RESET_TWSI7 42 +#define RESET_TWSI8 43 +#define RESET_IPC_AP2AUD 44 +#define RESET_UART4 45 +#define RESET_UART5 46 +#define RESET_UART6 47 +#define RESET_UART7 48 +#define RESET_UART8 49 +#define RESET_UART9 50 +#define RESET_CAN0 51 + +/* MPMU */ +#define RESET_WDT 52 + +/* APMU */ +#define RESET_JPG 53 +#define RESET_CSI 54 +#define RESET_CCIC2_PHY 55 +#define RESET_CCIC3_PHY 56 +#define RESET_ISP 57 +#define RESET_ISP_AHB 58 +#define RESET_ISP_CI 59 +#define RESET_ISP_CPP 60 +#define RESET_LCD 61 +#define RESET_DSI_ESC 62 +#define RESET_V2D 63 +#define RESET_MIPI 64 +#define RESET_LCD_SPI 65 +#define RESET_LCD_SPI_BUS 66 +#define RESET_LCD_SPI_HBUS 67 +#define RESET_LCD_MCLK 68 +#define RESET_CCIC_4X 69 +#define RESET_CCIC1_PHY 70 +#define RESET_SDH_AXI 71 +#define RESET_SDH0 72 +#define RESET_SDH1 73 +#define RESET_USB_AXI 74 +#define RESET_USBP1_AXI 75 +#define RESET_COMBO_PHY 76 +#define RESET_USB3_0 77 +#define RESET_QSPI 78 +#define RESET_QSPI_BUS 79 +#define RESET_DMA 80 +#define RESET_AES 81 +#define RESET_VPU 82 +#define RESET_GPU 83 +#define RESET_SDH2 84 +#define RESET_MC 85 +#define RESET_EM_AXI 86 +#define RESET_EM 87 +#define RESET_AUDIO_SYS 88 +#define RESET_HDMI 89 +#define RESET_PCIE0 90 +#define RESET_PCIE1 91 +#define RESET_PCIE2 92 +#define RESET_EMAC0 93 +#define RESET_EMAC1 94 + +/* APBC2 */ +#define RESET_SEC_UART1 95 +#define RESET_SEC_SSP2 96 +#define RESET_SEC_TWSI3 97 +#define RESET_SEC_RTC 98 +#define RESET_SEC_TIMERS0 99 +#define RESET_SEC_KPC 100 +#define RESET_SEC_GPIO 101 + +#define RESET_RCPU_HDMIAUDIO 102 +#define RESET_RCPU_CAN 103 + +#define RESET_RCPU_I2C0 104 +#define RESET_RCPU_SSP0 105 +#define RESET_RCPU_IR 106 +#define RESET_RCPU_UART0 107 +#define RESET_RCPU_UART1 108 + +#define RESET_RCPU2_PWM0 109 +#define RESET_RCPU2_PWM1 110 +#define RESET_RCPU2_PWM2 111 +#define RESET_RCPU2_PWM3 112 +#define RESET_RCPU2_PWM4 113 +#define RESET_RCPU2_PWM5 114 +#define RESET_RCPU2_PWM6 115 +#define RESET_RCPU2_PWM7 116 +#define RESET_RCPU2_PWM8 117 +#define RESET_RCPU2_PWM9 118 + +#define RESET_NUMBER 119 + +#endif /* __DT_BINDINGS_RESET_K1X_H__ */ diff --git a/bsp/spacemit/dm/include/firmware-spacemit.h b/bsp/spacemit/dm/include/firmware-spacemit.h new file mode 100755 index 00000000000..54379e1211f --- /dev/null +++ b/bsp/spacemit/dm/include/firmware-spacemit.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2006-2024, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2024-5-1 GuEe-GUI first version + */ + +#ifndef __FIRMWARE_SPACEMIT_H__ +#define __FIRMWARE_SPACEMIT_H__ + +#ifdef __ASSEMBLY__ +#include + +.macro SPACEMIT_FIRMWARE_DEFINE name + .align RISCV_SZPTR +firmware_\name: + .ascii "\name" "\0" +firmware_\name\()_start: + .incbin "\name" +firmware_\name\()_end: +.endm + +.macro SPACEMIT_FIRMWARE_REF name + RISCV_PTR firmware_\name + RISCV_PTR firmware_\name\()_start + RISCV_PTR firmware_\name\()_end +.endm + +.macro SPACEMIT_FIRMWARE_START + .align RISCV_SZPTR + .globl spacemit_firmwares_start +spacemit_firmwares_start: +.endm + +.macro SPACEMIT_FIRMWARE_END + .globl spacemit_firmwares_end +spacemit_firmwares_end: +.endm +#else +#include + +rt_bool_t spacemit_firmware_find(const char *name, rt_ubase_t *out_base, rt_size_t *out_size); +rt_err_t spacemit_firmware_load_elf(const char *name, rt_ubase_t phy_addr); + +#endif /* __ASSEMBLY__ */ + +#endif /* __FIRMWARE_SPACEMIT_H__ */ diff --git a/bsp/spacemit/dm/include/k1-cru.h b/bsp/spacemit/dm/include/k1-cru.h new file mode 100755 index 00000000000..d53950633a2 --- /dev/null +++ b/bsp/spacemit/dm/include/k1-cru.h @@ -0,0 +1,232 @@ +/* + * Copyright (c) 2006-2024, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2024-5-1 GuEe-GUI first version + */ + +#ifndef __K1_CLK_RST_H__ +#define __K1_CLK_RST_H__ + +#include +#include +#include + +/* APBS register offset */ +/* PLL1 */ +#define APB_SPARE1_REG 0x100 +#define APB_SPARE2_REG 0x104 +#define APB_SPARE3_REG 0x108 +/* PLL2 */ +#define APB_SPARE7_REG 0x118 +#define APB_SPARE8_REG 0x11c +#define APB_SPARE9_REG 0x120 +/* PLL3 */ +#define APB_SPARE10_REG 0x124 +#define APB_SPARE11_REG 0x128 +#define APB_SPARE12_REG 0x12c +/* end of APBS register offset */ + +/* APBC register offset */ +#define APBC_UART1_CLK_RST 0x0 +#define APBC_UART2_CLK_RST 0x4 +#define APBC_GPIO_CLK_RST 0x8 +#define APBC_PWM0_CLK_RST 0xc +#define APBC_PWM1_CLK_RST 0x10 +#define APBC_PWM2_CLK_RST 0x14 +#define APBC_PWM3_CLK_RST 0x18 +#define APBC_TWSI8_CLK_RST 0x20 +#define APBC_UART3_CLK_RST 0x24 +#define APBC_RTC_CLK_RST 0x28 +#define APBC_TWSI0_CLK_RST 0x2c +#define APBC_TWSI1_CLK_RST 0x30 +#define APBC_TIMERS1_CLK_RST 0x34 +#define APBC_TWSI2_CLK_RST 0x38 +#define APBC_AIB_CLK_RST 0x3c +#define APBC_TWSI4_CLK_RST 0x40 +#define APBC_TIMERS2_CLK_RST 0x44 +#define APBC_ONEWIRE_CLK_RST 0x48 +#define APBC_TWSI5_CLK_RST 0x4c +#define APBC_DRO_CLK_RST 0x58 +#define APBC_IR_CLK_RST 0x5c +#define APBC_TWSI6_CLK_RST 0x60 +#define APBC_COUNTER_CLK_SEL 0x64 +#define APBC_TWSI7_CLK_RST 0x68 +#define APBC_TSEN_CLK_RST 0x6c + +#define APBC_UART4_CLK_RST 0x70 +#define APBC_UART5_CLK_RST 0x74 +#define APBC_UART6_CLK_RST 0x78 +#define APBC_SSP3_CLK_RST 0x7c + +#define APBC_SSPA0_CLK_RST 0x80 +#define APBC_SSPA1_CLK_RST 0x84 + +#define APBC_IPC_AP2AUD_CLK_RST 0x90 +#define APBC_UART7_CLK_RST 0x94 +#define APBC_UART8_CLK_RST 0x98 +#define APBC_UART9_CLK_RST 0x9c + +#define APBC_CAN0_CLK_RST 0xa0 +#define APBC_PWM4_CLK_RST 0xa8 +#define APBC_PWM5_CLK_RST 0xac +#define APBC_PWM6_CLK_RST 0xb0 +#define APBC_PWM7_CLK_RST 0xb4 +#define APBC_PWM8_CLK_RST 0xb8 +#define APBC_PWM9_CLK_RST 0xbc +#define APBC_PWM10_CLK_RST 0xc0 +#define APBC_PWM11_CLK_RST 0xc4 +#define APBC_PWM12_CLK_RST 0xc8 +#define APBC_PWM13_CLK_RST 0xcc +#define APBC_PWM14_CLK_RST 0xd0 +#define APBC_PWM15_CLK_RST 0xd4 +#define APBC_PWM16_CLK_RST 0xd8 +#define APBC_PWM17_CLK_RST 0xdc +#define APBC_PWM18_CLK_RST 0xe0 +#define APBC_PWM19_CLK_RST 0xe4 +/* End of APBC register offset */ + +/* MPMU register offset */ +#define MPMU_POSR 0x10 +#define POSR_PLL1_LOCK RT_BIT(27) +#define POSR_PLL2_LOCK RT_BIT(28) +#define POSR_PLL3_LOCK RT_BIT(29) + +#define MPMU_VRCR 0x18 +#define MPMU_VRCR_REQ_EN0 RT_BIT(0) +#define MPMU_VRCR_REQ_EN2 RT_BIT(2) +#define MPMU_VRCR_REQ_POL2 RT_BIT(6) +#define MPMU_VRCR_VCXO_OUT_REQ_EN2 RT_BIT(14) + +#define MPMU_WDTPCR 0x200 +#define MPMU_RIPCCR 0x210 +#define MPMU_ACGR 0x1024 +#define MPMU_SUCCR 0x14 +#define MPMU_ISCCR 0x44 +#define MPMU_SUCCR_1 0x10b0 +#define MPMU_APBCSCR 0x1050 + +#define MPMU_PM_MN 0x10a4 +#define MPMU_PM_MN_GPCR2 0x48 +#define MPMU_PM_MN_GPCR 0x30 +/* End of MPMU register offset */ + +/* APMU register offset */ +#define APMU_JPG_CLK_RES_CTRL 0x20 +#define APMU_CSI_CCIC2_CLK_RES_CTRL 0x24 +#define APMU_ISP_CLK_RES_CTRL 0x38 +#define APMU_LCD_CLK_RES_CTRL1 0x44 +#define APMU_LCD_SPI_CLK_RES_CTRL 0x48 +#define APMU_LCD_CLK_RES_CTRL2 0x4c +#define APMU_CCIC_CLK_RES_CTRL 0x50 +#define APMU_SDH0_CLK_RES_CTRL 0x54 +#define APMU_SDH1_CLK_RES_CTRL 0x58 +#define APMU_USB_CLK_RES_CTRL 0x5c +#define APMU_QSPI_CLK_RES_CTRL 0x60 +#define APMU_USB_CLK_RES_CTRL 0x5c +#define APMU_DMA_CLK_RES_CTRL 0x64 +#define APMU_AES_CLK_RES_CTRL 0x68 +#define APMU_VPU_CLK_RES_CTRL 0xa4 +#define APMU_GPU_CLK_RES_CTRL 0xcc +#define APMU_SDH2_CLK_RES_CTRL 0xe0 +#define APMU_PMUA_MC_CTRL 0xe8 +#define APMU_PMU_CC2_AP 0x100 +#define APMU_PMUA_EM_CLK_RES_CTRL 0x104 + +#define APMU_AUDIO_CLK_RES_CTRL 0x14c +#define APMU_HDMI_CLK_RES_CTRL 0x1b8 +#define APMU_CCI550_CLK_CTRL 0x300 +#define APMU_ACLK_CLK_CTRL 0x388 +#define APMU_CPU_C0_CLK_CTRL 0x38c +#define APMU_CPU_C1_CLK_CTRL 0x390 + +#define APMU_PCIE_CLK_RES_CTRL_0 0x3cc +#define APMU_PCIE_CLK_RES_CTRL_1 0x3d4 +#define APMU_PCIE_CLK_RES_CTRL_2 0x3dc + +#define APMU_EMAC0_CLK_RES_CTRL 0x3e4 +#define APMU_EMAC1_CLK_RES_CTRL 0x3ec + +#define APMU_DFC_AP 0x180 +#define APMU_DFC_STATUS 0x188 + +#define APMU_DFC_LEVEL0 0x190 +#define APMU_DFC_LEVEL1 0x194 +#define APMU_DFC_LEVEL2 0x198 +#define APMU_DFC_LEVEL3 0x19c +#define APMU_DFC_LEVEL4 0x1a0 +#define APMU_DFC_LEVEL5 0x1a4 +#define APMU_DFC_LEVEL6 0x1a8 +#define APMU_DFC_LEVEL7 0x1ac + +#define APMU_DPLL1_CLK_CTRL1 0x39c +#define APMU_DPLL1_CLK_CTRL2 0x3a0 +#define APMU_DPLL2_CLK_CTRL1 0x3a8 +#define APMU_DPLL2_CLK_CTRL2 0x3ac +/* End of APMU register offset */ + +/* APBC2 register offset */ +#define APBC2_UART1_CLK_RST 0x00 +#define APBC2_SSP2_CLK_RST 0x04 +#define APBC2_TWSI3_CLK_RST 0x08 +#define APBC2_RTC_CLK_RST 0x0c +#define APBC2_TIMERS0_CLK_RST 0x10 +#define APBC2_KPC_CLK_RST 0x14 +#define APBC2_GPIO_CLK_RST 0x1c +/* End of APBC2 register offset */ + +/* RCPU register offset */ +#define RCPU_HDMI_CLK_RST 0x2044 +#define RCPU_CAN_CLK_RST 0x4c +#define RCPU_I2C0_CLK_RST 0x30 + +#define RCPU_SSP0_CLK_RST 0x28 +#define RCPU_IR_CLK_RST 0x48 +#define RCPU_UART0_CLK_RST 0xd8 +#define RCPU_UART1_CLK_RST 0x3c +/* End of RCPU register offset */ + +/* RCPU2 register offset */ +#define RCPU2_PWM0_CLK_RST 0x00 +#define RCPU2_PWM1_CLK_RST 0x04 +#define RCPU2_PWM2_CLK_RST 0x08 +#define RCPU2_PWM3_CLK_RST 0x0c +#define RCPU2_PWM4_CLK_RST 0x10 +#define RCPU2_PWM5_CLK_RST 0x14 +#define RCPU2_PWM6_CLK_RST 0x18 +#define RCPU2_PWM7_CLK_RST 0x1c +#define RCPU2_PWM8_CLK_RST 0x20 +#define RCPU2_PWM9_CLK_RST 0x24 +/* End of RCPU2 register offset */ + +/* AUDPMU register offset */ +#define AUDPMU_AUDIO_BUS_CLK_CTRL 0x38 +/* end of AUDPMU register offset */ + +enum cru_type +{ + CRU_BASE_TYPE_MPMU = 0, + CRU_BASE_TYPE_APMU, + CRU_BASE_TYPE_APBC, + CRU_BASE_TYPE_APBS, + CRU_BASE_TYPE_CIU, + CRU_BASE_TYPE_DCIU, + CRU_BASE_TYPE_DDRC, + CRU_BASE_TYPE_APBC2, + CRU_BASE_TYPE_RCPU, + CRU_BASE_TYPE_RCPU2, + CRU_BASE_TYPE_AUDPMU, + CRU_BASE_TYPE_AUDC, + CRU_BASE_TYPE_MAX, +}; + +#ifdef __CCU_H__ +RT_DEFINE_SPINLOCK(k1_cru_lock); +#else +extern struct rt_spinlock k1_cru_lock; +#endif + +#endif /* __K1_CLK_RST_H__ */ diff --git a/bsp/spacemit/dm/include/k1-tcm.h b/bsp/spacemit/dm/include/k1-tcm.h new file mode 100755 index 00000000000..6a135eed30a --- /dev/null +++ b/bsp/spacemit/dm/include/k1-tcm.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2006-2024, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2024-5-1 GuEe-GUI first version + */ + +#ifndef __K1_TCM_H__ +#define __K1_TCM_H__ + +#include +#include + +void *tcm_alloc_cpu(int cpuid, rt_size_t size); +void tcm_free_cpu(int cpuid, void *ptr); + +rt_inline void *tcm_alloc(rt_size_t size) +{ + return tcm_alloc_cpu(rt_hw_cpu_id(), size); +} + +rt_inline void tcm_free(void *ptr) +{ + return tcm_free_cpu(rt_hw_cpu_id(), ptr); +} + +#endif /* __K1_TCM_H__ */ diff --git a/bsp/spacemit/dm/include/mfd/pm853.h b/bsp/spacemit/dm/include/mfd/pm853.h new file mode 100755 index 00000000000..8ab8d691938 --- /dev/null +++ b/bsp/spacemit/dm/include/mfd/pm853.h @@ -0,0 +1,336 @@ +/* + * Copyright (c) 2006-2024, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2024-5-1 GuEe-GUI first version + */ + +#ifndef __PM853_H__ +#define __PM853_H__ + +enum +{ + PM853_ID_DCDC1, + PM853_ID_DCDC2, + PM853_ID_DCDC3, + PM853_ID_DCDC4, + PM853_ID_DCDC5, + PM853_ID_LDO1, + PM853_ID_LDO2, + PM853_ID_LDO3, + PM853_ID_LDO4, + PM853_ID_LDO5, + PM853_ID_LDO6, + PM853_ID_LDO7, + PM853_ID_LDO8, + PM853_ID_LDO9, + PM853_ID_LDO10, + PM853_ID_LDO11, + PM853_ID_LDO12, + PM853_ID_LDO13, + PM853_ID_LDO14, + PM853_ID_LDO15, + PM853_ID_LDO16, + PM853_ID_LDO17, + PM853_ID_LDO18, + PM853_ID_LDO19, + PM853_ID_LDO20, + PM853_ID_LDO21, + PM853_ID_LDO22, + PM853_ID_LDO5_SW, +}; + +#define PM853_LDO_BUCK_EN_REG0 0x11 +#define PM853_LDO_BUCK_EN_REG1 0x12 +#define PM853_LDO_BUCK_EN_REG2 0x13 +#define PM853_LDO_BUCK_EN_REG3 0x14 + +#define PM853_BUCK1_EN_MSK 0x1 +#define PM853_BUCK2_EN_MSK 0x2 +#define PM853_BUCK3_EN_MSK 0x4 +#define PM853_BUCK4_EN_MSK 0x8 +#define PM853_BUCK5_EN_MSK 0x10 + +#define PM853_LDO1_EN_MSK 0x20 +#define PM853_LDO2_EN_MSK 0x40 +#define PM853_LDO3_EN_MSK 0x80 +#define PM853_LDO4_EN_MSK 0x1 +#define PM853_LDO5_EN_MSK 0x2 +#define PM853_LDO6_EN_MSK 0x4 +#define PM853_LDO7_EN_MSK 0x8 +#define PM853_LDO8_EN_MSK 0x10 +#define PM853_LDO9_EN_MSK 0x20 +#define PM853_LDO10_EN_MSK 0x40 +#define PM853_LDO11_EN_MSK 0x80 +#define PM853_LDO12_EN_MSK 0x1 +#define PM853_LDO13_EN_MSK 0x2 +#define PM853_LDO14_EN_MSK 0x4 +#define PM853_LDO15_EN_MSK 0x8 +#define PM853_LDO16_EN_MSK 0x10 +#define PM853_LDO17_EN_MSK 0x20 +#define PM853_LDO18_EN_MSK 0x40 +#define PM853_LDO19_EN_MSK 0x80 +#define PM853_LDO20_EN_MSK 0x1 +#define PM853_LDO21_EN_MSK 0x2 +#define PM853_LDO22_EN_MSK 0x4 +#define PM853_SW_EN_MSK 0x8 + + +#define PM853_BUCK1_VSEL_REG 0x30 +#define PM853_BUCK2_VSEL_REG 0x50 +#define PM853_BUCK3_VSEL_REG 0x60 +#define PM853_BUCK4_VSEL_REG 0x80 +#define PM853_BUCK5_VSEL_REG 0x90 + +#define PM853_BUCK1_VSEL_MSK 0x7f +#define PM853_BUCK2_VSEL_MSK 0x7f +#define PM853_BUCK3_VSEL_MSK 0x7f +#define PM853_BUCK4_VSEL_MSK 0x7f +#define PM853_BUCK5_VSEL_MSK 0x7f + +#define PM853_LDO_VSEL_MSK 0xf +#define PM853_LDO1_VSEL_REG 0xb1 +#define PM853_LDO2_VSEL_REG 0xb4 +#define PM853_LDO3_VSEL_REG 0xb7 +#define PM853_LDO4_VSEL_REG 0xba +#define PM853_LDO5_VSEL_REG 0xbd +#define PM853_LDO6_VSEL_REG 0xc0 +#define PM853_LDO7_VSEL_REG 0xc3 +#define PM853_LDO8_VSEL_REG 0xc6 +#define PM853_LDO9_VSEL_REG 0xc9 +#define PM853_LDO10_VSEL_REG 0xcc +#define PM853_LDO11_VSEL_REG 0xcf +#define PM853_LDO12_VSEL_REG 0xd2 +#define PM853_LDO13_VSEL_REG 0xd5 +#define PM853_LDO14_VSEL_REG 0xd8 +#define PM853_LDO15_VSEL_REG 0xdb +#define PM853_LDO16_VSEL_REG 0xde +#define PM853_LDO17_VSEL_REG 0xe1 +#define PM853_LDO18_VSEL_REG 0xe4 +#define PM853_LDO19_VSEL_REG 0xe7 +#define PM853_LDO20_VSEL_REG 0xea +#define PM853_LDO21_VSEL_REG 0xed +#define PM853_LDO22_VSEL_REG 0xf0 + +#define SPACEMIT_PM853_MAX_REG 0xf1 + +#define PM853_DESC(_id, _match, _supply, _nv, _vr, _vm, _er, _em, _lr) \ + SPACEMIT_PMIC_REGULATOR_DESC_COMMON(_id, _match, _supply, _nv, _vr, _vm, _er, _em, _lr, pmic_dcdc_ldo_ops) + +#define PM853_DESC_SWITCH(_id, _match, _supply, _ereg, _emask) \ + SPACEMIT_PMIC_REGULATOR_DESC_SWITCH_COM(_id, _match, _supply, _ereg, _emask, 0, 0, pmic_switch_ops) + +#ifdef __SPACEMIT_REGULATOR_INTERNAL +static const struct spacemit_pmic_regulator_range pm853_buck_ranges1[] = +{ + SPACEMIT_PMIC_REGULATOR_RANGE(480000, 0x0, 0x50, 10000), + SPACEMIT_PMIC_REGULATOR_RANGE(1320000, 0x51, 0x7F, 40000), +}; + +static const struct spacemit_pmic_regulator_range pm853_buck_ranges2[] = +{ + SPACEMIT_PMIC_REGULATOR_RANGE(600000, 0x0, 0x50, 12500), + SPACEMIT_PMIC_REGULATOR_RANGE(1650000, 0x51, 0x7F, 50000), +}; + +static const struct spacemit_pmic_regulator_range pm853_ldo_ranges1[] = +{ + SPACEMIT_PMIC_REGULATOR_RANGE(1200000, 0x0, 0x6, 100000), + SPACEMIT_PMIC_REGULATOR_RANGE(1850000, 0x7, 0x8, 50000), + SPACEMIT_PMIC_REGULATOR_RANGE(2750000, 0x9, 0xc, 50000), + SPACEMIT_PMIC_REGULATOR_RANGE(3000000, 13, 14, 100000), + SPACEMIT_PMIC_REGULATOR_RANGE(3300000, 15, 15, 0), +}; + +static const struct spacemit_pmic_regulator_range pm853_ldo_ranges2[] = +{ + SPACEMIT_PMIC_REGULATOR_RANGE(1600000, 0x0, 0x3, 100000), +}; + +static const struct spacemit_pmic_regulator_range pm853_ldo_ranges3[] = +{ + SPACEMIT_PMIC_REGULATOR_RANGE(1200000, 0x0, 0xf, 50000), +}; + +static const struct spacemit_pmic_regulator_range pm853_ldo_ranges4[] = +{ + SPACEMIT_PMIC_REGULATOR_RANGE(1000000, 0x0, 0x7, 50000), +}; + +static const struct spacemit_pmic_regulator_desc pm853_reg[] = +{ + /* BUCK */ + PM853_DESC(PM853_ID_DCDC1, "DCDC_REG1", "vcc_sys", + 128, PM853_BUCK1_VSEL_REG, PM853_BUCK1_VSEL_MSK, + PM853_LDO_BUCK_EN_REG0, PM853_BUCK1_EN_MSK, + pm853_buck_ranges1), + + PM853_DESC(PM853_ID_DCDC2, "DCDC_REG2", "vcc_sys", + 128, PM853_BUCK2_VSEL_REG, PM853_BUCK2_VSEL_MSK, + PM853_LDO_BUCK_EN_REG0, PM853_BUCK2_EN_MSK, + pm853_buck_ranges2), + + PM853_DESC(PM853_ID_DCDC3, "DCDC_REG3", "vcc_sys", + 128, PM853_BUCK3_VSEL_REG, PM853_BUCK3_VSEL_MSK, + PM853_LDO_BUCK_EN_REG0, PM853_BUCK3_EN_MSK, + pm853_buck_ranges2), + + PM853_DESC(PM853_ID_DCDC4, "DCDC_REG4", "vcc_sys", + 128, PM853_BUCK4_VSEL_REG, PM853_BUCK4_VSEL_MSK, + PM853_LDO_BUCK_EN_REG0, PM853_BUCK4_EN_MSK, + pm853_buck_ranges1), + + PM853_DESC(PM853_ID_DCDC5, "DCDC_REG5", "vcc_sys", + 128, PM853_BUCK5_VSEL_REG, PM853_BUCK5_VSEL_MSK, + PM853_LDO_BUCK_EN_REG0, PM853_BUCK5_EN_MSK, + pm853_buck_ranges2), + + /* ALDO */ + PM853_DESC(PM853_ID_LDO1, "LDO_REG1", "vcc_sys", + 16, PM853_LDO1_VSEL_REG, PM853_LDO_VSEL_MSK, + PM853_LDO_BUCK_EN_REG0, PM853_LDO1_EN_MSK, + pm853_ldo_ranges1), + + PM853_DESC(PM853_ID_LDO2, "LDO_REG2", "vcc_sys", + 16, PM853_LDO2_VSEL_REG, PM853_LDO_VSEL_MSK, + PM853_LDO_BUCK_EN_REG0, PM853_LDO2_EN_MSK, + pm853_ldo_ranges1), + + PM853_DESC(PM853_ID_LDO3, "LDO_REG3", "vcc_sys", + 16, PM853_LDO3_VSEL_REG, PM853_LDO_VSEL_MSK, + PM853_LDO_BUCK_EN_REG0, PM853_LDO3_EN_MSK, + pm853_ldo_ranges1), + + PM853_DESC(PM853_ID_LDO4, "LDO_REG4", "vcc_sys", + 16, PM853_LDO4_VSEL_REG, PM853_LDO_VSEL_MSK, + PM853_LDO_BUCK_EN_REG1, PM853_LDO4_EN_MSK, + pm853_ldo_ranges1), + + PM853_DESC(PM853_ID_LDO5, "LDO_REG5", "vcc_sys", + 4, PM853_LDO5_VSEL_REG, PM853_LDO_VSEL_MSK, + PM853_LDO_BUCK_EN_REG1, PM853_LDO5_EN_MSK, + pm853_ldo_ranges2), + + PM853_DESC(PM853_ID_LDO6, "LDO_REG6", "vcc_sys", + 16, PM853_LDO6_VSEL_REG, PM853_LDO_VSEL_MSK, + PM853_LDO_BUCK_EN_REG1, PM853_LDO6_EN_MSK, + pm853_ldo_ranges1), + + PM853_DESC(PM853_ID_LDO7, "LDO_REG7", "dcdc2", + 16, PM853_LDO7_VSEL_REG, PM853_LDO_VSEL_MSK, + PM853_LDO_BUCK_EN_REG1, PM853_LDO7_EN_MSK, + pm853_ldo_ranges3), + + PM853_DESC(PM853_ID_LDO8, "LDO_REG8", "vcc_sys", + 16, PM853_LDO8_VSEL_REG, PM853_LDO_VSEL_MSK, + PM853_LDO_BUCK_EN_REG1, PM853_LDO8_EN_MSK, + pm853_ldo_ranges1), + + PM853_DESC(PM853_ID_LDO9, "LDO_REG9", "vcc_sys", + 16, PM853_LDO9_VSEL_REG, PM853_LDO_VSEL_MSK, + PM853_LDO_BUCK_EN_REG1, PM853_LDO9_EN_MSK, + pm853_ldo_ranges1), + + PM853_DESC(PM853_ID_LDO10, "LDO_REG10", "vcc_sys", + 16, PM853_LDO10_VSEL_REG, PM853_LDO_VSEL_MSK, + PM853_LDO_BUCK_EN_REG1, PM853_LDO10_EN_MSK, + pm853_ldo_ranges1), + + PM853_DESC(PM853_ID_LDO11, "LDO_REG11", "dcdc2", + 16, PM853_LDO11_VSEL_REG, PM853_LDO_VSEL_MSK, + PM853_LDO_BUCK_EN_REG1, PM853_LDO11_EN_MSK, + pm853_ldo_ranges3), + + PM853_DESC(PM853_ID_LDO12, "LDO_REG12", "vcc_sys", + 16, PM853_LDO12_VSEL_REG, PM853_LDO_VSEL_MSK, + PM853_LDO_BUCK_EN_REG2, PM853_LDO12_EN_MSK, + pm853_ldo_ranges1), + + PM853_DESC(PM853_ID_LDO13, "LDO_REG13", "vcc_sys", + 16, PM853_LDO13_VSEL_REG, PM853_LDO_VSEL_MSK, + PM853_LDO_BUCK_EN_REG2, PM853_LDO13_EN_MSK, + pm853_ldo_ranges1), + + PM853_DESC(PM853_ID_LDO14, "LDO_REG14", "vcc_sys", + 16, PM853_LDO14_VSEL_REG, PM853_LDO_VSEL_MSK, + PM853_LDO_BUCK_EN_REG2, PM853_LDO14_EN_MSK, + pm853_ldo_ranges1), + + PM853_DESC(PM853_ID_LDO15, "LDO_REG15", "dcdc2", + 16, PM853_LDO15_VSEL_REG, PM853_LDO_VSEL_MSK, + PM853_LDO_BUCK_EN_REG2, PM853_LDO15_EN_MSK, + pm853_ldo_ranges3), + + PM853_DESC(PM853_ID_LDO16, "LDO_REG16", "vcc_sys", + 16, PM853_LDO16_VSEL_REG, PM853_LDO_VSEL_MSK, + PM853_LDO_BUCK_EN_REG2, PM853_LDO16_EN_MSK, + pm853_ldo_ranges1), + + PM853_DESC(PM853_ID_LDO17, "LDO_REG17", "dcdc5", + 8, PM853_LDO17_VSEL_REG, PM853_LDO_VSEL_MSK, + PM853_LDO_BUCK_EN_REG2, PM853_LDO17_EN_MSK, + pm853_ldo_ranges4), + + PM853_DESC(PM853_ID_LDO18, "LDO_REG18", "dcdc2", + 16, PM853_LDO18_VSEL_REG, PM853_LDO_VSEL_MSK, + PM853_LDO_BUCK_EN_REG2, PM853_LDO18_EN_MSK, + pm853_ldo_ranges3), + + PM853_DESC(PM853_ID_LDO19, "LDO_REG19", "dcdc5", + 8, PM853_LDO19_VSEL_REG, PM853_LDO_VSEL_MSK, + PM853_LDO_BUCK_EN_REG2, PM853_LDO19_EN_MSK, + pm853_ldo_ranges4), + + PM853_DESC(PM853_ID_LDO20, "LDO_REG20", "dcdc5", + 8, PM853_LDO20_VSEL_REG, PM853_LDO_VSEL_MSK, + PM853_LDO_BUCK_EN_REG3, PM853_LDO20_EN_MSK, + pm853_ldo_ranges4), + + PM853_DESC(PM853_ID_LDO21, "LDO_REG21", "dcdc2", + 16, PM853_LDO21_VSEL_REG, PM853_LDO_VSEL_MSK, + PM853_LDO_BUCK_EN_REG3, PM853_LDO21_EN_MSK, + pm853_ldo_ranges3), + + PM853_DESC(PM853_ID_LDO22, "LDO_REG22", "dcdc5", + 8, PM853_LDO22_VSEL_REG, PM853_LDO_VSEL_MSK, + PM853_LDO_BUCK_EN_REG3, PM853_LDO22_EN_MSK, + pm853_ldo_ranges4), + + /* PWR SWITCH */ + PM853_DESC_SWITCH(PM853_ID_LDO5_SW, "SWITCH_REG1", "vcc_sys", + PM853_LDO_BUCK_EN_REG3, PM853_SW_EN_MSK), +}; + +static struct spacemit_pmic_regulator_data pm853_regulator_data = +{ + .desc = pm853_reg, + .desc_nr = RT_ARRAY_SIZE(pm853_reg), +}; +#endif /* __SPACEMIT_REGULATOR_INTERNAL */ + +#ifdef __SPACEMIT_PMIC_INTERNAL +static struct spacemit_pmic_mfd_endpoint pm853_mfd_endpoint[] = +{ + { + .ofw_name = "spacemit-regulator@pm853", + .ofw_compatible = "pmic,regulator,pm853", + }, +}; + +static struct spacemit_pmic_mfd_data pm853_mfd_data = +{ + .regmap_cfg = + { + .reg_bits = 8, + .val_bits = 8, + .max_register = SPACEMIT_PM853_MAX_REG, + }, + .endpoint = pm853_mfd_endpoint, + .endpoint_num = RT_ARRAY_SIZE(pm853_mfd_endpoint), +}; +#endif /* __SPACEMIT_PMIC_INTERNAL */ + +#endif /* __PM853_H__ */ diff --git a/bsp/spacemit/dm/include/mfd/spacemit-pmic.h b/bsp/spacemit/dm/include/mfd/spacemit-pmic.h new file mode 100755 index 00000000000..650293b2e52 --- /dev/null +++ b/bsp/spacemit/dm/include/mfd/spacemit-pmic.h @@ -0,0 +1,477 @@ +/* + * Copyright (c) 2006-2024, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2024-5-1 GuEe-GUI first version + */ + +#ifndef __SPACEMIT_PMIC_H__ +#define __SPACEMIT_PMIC_H__ + +#include +#include + +struct spacemit_pmic_mfd_data; + +struct spacemit_pmic +{ + int irq; + int addr_offset; + struct rt_device *dev; + const struct spacemit_pmic_mfd_data *mfd_data; + + unsigned (*read)(struct spacemit_pmic *pmic, unsigned reg); + rt_err_t (*write)(struct spacemit_pmic *pmic, unsigned reg, unsigned data); + rt_err_t (*write_bit)(struct spacemit_pmic *pmic, unsigned reg, unsigned mask, unsigned data); + rt_err_t (*update_bits)(struct spacemit_pmic *pmic, unsigned reg, unsigned mask, unsigned data); +}; + +struct spacemit_pmic_irq_map +{ + rt_uint32_t reg_offset; + rt_uint32_t mask; +}; + +struct spacemit_pmic_irqchip +{ + struct rt_pic parent; + + struct spacemit_pmic *pmic; + + rt_uint32_t *irq_status; + + int num_regs; + int reg_stride; + int mask_base; + int status_base; + int ack_base; + + rt_bool_t init_ack_masked; + rt_bool_t mask_unmask_non_inverted; + + int irqs_nr; + const struct spacemit_pmic_irq_map *irqs_map; + + struct rt_thread *irq_thread; +}; + +#define raw_to_spacemit_pmic_irqchip(raw) rt_container_of(raw, struct spacemit_pmic_irqchip, parent) + +struct spacemit_pmic_regmap_config +{ + rt_uint8_t reg_bits; + rt_uint8_t val_bits; + rt_uint16_t max_register; +}; + +struct spacemit_pmic_mfd_endpoint +{ + const char *ofw_name; + const char *ofw_compatible; + + int irqs_nr; + const int *irqs_list; +}; + +struct spacemit_pmic_mfd_data +{ + struct spacemit_pmic_regmap_config regmap_cfg; + + struct spacemit_pmic_mfd_endpoint *endpoint; + rt_uint32_t endpoint_num; + + struct spacemit_pmic_irqchip *irqchip; + + /* Shutdown - reboot support */ + struct + { + rt_uint8_t reg; + rt_uint8_t bit; + } shutdown; + + struct + { + rt_uint32_t reg; + rt_uint8_t bit; + } reboot; + + /* Value will be kept in register while reset pmic */ + struct + { + rt_uint32_t reg; + rt_uint8_t bit; + } non_reset; +}; + +/* Regulator */ +struct spacemit_pmic_regulator_range +{ + int min; + int min_sel; + int max_sel; + int step; +}; + +#define SPACEMIT_PMIC_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 spacemit_pmic_regulator_desc +{ + const char *name; + const char *match; + const char *supply_name; + + const struct rt_regulator_ops *ops; + + rt_uint32_t id; + + int voltages_nr; + int uvolt_min; + int uvolt_step; + + int ranges_nr; + const struct spacemit_pmic_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 SPACEMIT_PMIC_REGULATOR_DESC(ID, 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, \ + .id = ID, \ + .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 SPACEMIT_PMIC_REGULATOR_DESC_COMMON(ID, \ + NAME, SUPPLY_NAME, VOLTAGES_NR, VSEL_REG, \ + VSEL_MASK, ENABLE_REG, ENABLE_MASK, RANGES, \ + OPS) \ + SPACEMIT_PMIC_REGULATOR_DESC(ID, NAME, \ + SUPPLY_NAME, OPS, \ + VOLTAGES_NR, 0, 0, \ + RT_ARRAY_SIZE(RANGES), RANGES, \ + VSEL_REG, VSEL_MASK, ENABLE_REG, \ + ENABLE_MASK, 0, 0) + +#define SPACEMIT_PMIC_REGULATOR_DESC_SWITCH_COM(\ + ID, NAME, SUPPLY_NAME, \ + ENABLE_REG, ENABLE_MASK, \ + ENABLE_VAL, DISABLE_VAL, OPS) \ + SPACEMIT_PMIC_REGULATOR_DESC(ID, NAME, \ + SUPPLY_NAME, OPS, \ + 0, 0, 0, \ + 0, RT_NULL, \ + 0, 0, ENABLE_REG, \ + ENABLE_MASK, ENABLE_VAL, DISABLE_VAL) + +struct spacemit_pmic_regulator_data +{ + const struct spacemit_pmic_regulator_desc *desc; + rt_uint32_t desc_nr; + int sleep_reg_offset; +}; + +/* Pinctrl */ +struct spacemit_pmic_pin_func_desc +{ + const char *name; + rt_uint8_t pin_id; + rt_uint8_t func_reg; + rt_uint8_t func_mask; + rt_uint8_t en_val; + rt_uint8_t ha_sub; + rt_uint8_t sub_reg; + rt_uint8_t sub_mask; + rt_uint8_t sube_val; +}; + +#define SPM8XX_DESC_PIN_FUNC_COM(_pin_id, _match, \ + _ereg, _emask, _enval, \ + _hsub, _subreg, _submask, _subenval) \ +{ \ + .name = (_match), \ + .pin_id = (_pin_id), \ + .func_reg = (_ereg), \ + .func_mask = (_emask), \ + .en_val = (_enval), \ + .ha_sub = (_hsub), \ + .sub_reg = (_subreg), \ + .sub_mask = (_submask), \ + .sube_val = (_subenval), \ +} + +struct spacemit_pmic_pin_config_desc +{ + rt_uint32_t pin_id; + /* Input config desc */ + struct + { + rt_uint8_t reg; + rt_uint8_t msk; + } input; + + /* Output config desc */ + struct + { + rt_uint8_t reg; + rt_uint8_t msk; + } output; + + /* Pull-down desc */ + struct + { + rt_uint8_t reg; + rt_uint8_t msk; + } pup; + + /* Deb */ + struct + { + rt_uint8_t reg; + rt_uint8_t timemsk; + + struct + { + rt_uint8_t msk; + } en; + } deb; + + /* OD */ + struct + { + rt_uint8_t reg; + rt_uint8_t msk; + } od; + + struct + { + rt_uint8_t reg; + rt_uint8_t msk; + } itype; +}; + +#define SPM8XX_DESC_PIN_CONFIG_COM(_pin_id, \ + _ireg, _imsk, _oreg, _omsk, \ + _pureg, _pumsk, \ + _debreg, _debtmsk, _debemsk, \ + _odreg, _odmsk, \ + _itypereg, _itypemsk) \ +{ \ + .pin_id = (_pin_id), \ + .input = \ + { \ + .reg = (_ireg), \ + .msk = (_imsk), \ + }, \ + .output = \ + { \ + .reg = (_oreg), \ + .msk = (_omsk), \ + }, \ + .pup = \ + { \ + .reg = (_pureg), \ + .msk = (_pumsk), \ + }, \ + .deb = \ + { \ + .reg = (_debreg), \ + .timemsk = (_debtmsk), \ + .en.msk = (_debemsk) \ + }, \ + .od = \ + { \ + .reg = (_odreg), \ + .msk = (_odmsk), \ + }, \ + .itype = \ + { \ + .reg = (_itypereg), \ + .msk = (_itypemsk), \ + }, \ +} + +struct spacemit_pmic_pin_data +{ + int pin_mux_nr; + const char **pinmux_funcs; + + int pin_fuc_desc_nr; + const struct spacemit_pmic_pin_func_desc *pinfunc_desc; + + int pin_conf_desc_nr; + const struct spacemit_pmic_pin_config_desc *pinconf_desc; +}; + +/* RTC */ +union spacemit_pmic_rtc_ctl_desc +{ + rt_uint32_t val; + struct + { + rt_uint32_t crystal_en:1; + rt_uint32_t out_32k_en:1; + rt_uint32_t rtc_en:1; + rt_uint32_t rtc_clk_sel:1; + rt_uint32_t tick_type:1; + rt_uint32_t alarm_en:1; + rt_uint32_t tick_en:1; + rt_uint32_t reserved:25; + } bits; +}; + +struct spacemit_pmic_rtc_regdesc +{ + /* Seconds */ + struct + { + rt_uint8_t reg; + rt_uint8_t msk; + } cnt_s; + + /* Mini */ + struct + { + rt_uint8_t reg; + rt_uint8_t msk; + } cnt_mi; + + /* Hour */ + struct + { + rt_uint8_t reg; + rt_uint8_t msk; + } cnt_h; + + /* Day */ + struct + { + rt_uint8_t reg; + rt_uint8_t msk; + } cnt_d; + + /* Mounth */ + struct + { + rt_uint8_t reg; + rt_uint8_t msk; + } cnt_mo; + + /* Year */ + struct + { + rt_uint8_t reg; + rt_uint8_t msk; + } cnt_y; + + struct + { + rt_uint8_t reg; + rt_uint8_t msk; + } alarm_s; + + struct + { + rt_uint8_t reg; + rt_uint8_t msk; + } alarm_mi; + + struct + { + rt_uint8_t reg; + rt_uint8_t msk; + } alarm_h; + + struct + { + rt_uint8_t reg; + rt_uint8_t msk; + } alarm_d; + + struct + { + rt_uint8_t reg; + rt_uint8_t msk; + } alarm_mo; + + struct + { + rt_uint8_t reg; + rt_uint8_t msk; + } alarm_y; + + struct + { + rt_uint8_t reg; + } rtc_ctl; +}; + +/* IIO */ +struct spacemit_pmic_iio_chan_spec +{ + int channel; +}; + +struct spacemit_pmic_adc_data +{ + int iio_desc_nr; + struct spacemit_pmic_iio_chan_spec *iio_desc; +}; + +#include "spm8821.h" +#include "pm853.h" +#include "sy8810l.h" + +rt_inline unsigned spacemit_pmic_read(struct spacemit_pmic *pmic, unsigned reg) +{ + return pmic->read(pmic, reg); +} + +rt_inline rt_err_t spacemit_pmic_write(struct spacemit_pmic *pmic, unsigned reg, unsigned data) +{ + return pmic->write(pmic, reg, data); +} + +rt_inline rt_err_t spacemit_pmic_write_bit(struct spacemit_pmic *pmic, unsigned reg, + unsigned mask, unsigned data) +{ + return pmic->write_bit(pmic, reg, mask, data); +} + +rt_inline rt_err_t spacemit_pmic_update_bits(struct spacemit_pmic *pmic, unsigned reg, + unsigned mask, unsigned data) +{ + return pmic->update_bits(pmic, reg, mask, data); +} + +#endif /* __SPACEMIT_PMIC_H__ */ diff --git a/bsp/spacemit/dm/include/mfd/spm8821.h b/bsp/spacemit/dm/include/mfd/spm8821.h new file mode 100755 index 00000000000..c3716807eab --- /dev/null +++ b/bsp/spacemit/dm/include/mfd/spm8821.h @@ -0,0 +1,885 @@ +/* + * Copyright (c) 2006-2024, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2024-5-1 GuEe-GUI first version + */ + +#ifndef __SMP8821_H__ +#define __SMP8821_H__ + +enum +{ + SPM8821_ID_DCDC1, + SPM8821_ID_DCDC2, + SPM8821_ID_DCDC3, + SPM8821_ID_DCDC4, + SPM8821_ID_DCDC5, + SPM8821_ID_DCDC6, + SPM8821_ID_LDO1, + SPM8821_ID_LDO2, + SPM8821_ID_LDO3, + SPM8821_ID_LDO4, + SPM8821_ID_LDO5, + SPM8821_ID_LDO6, + SPM8821_ID_LDO7, + SPM8821_ID_LDO8, + SPM8821_ID_LDO9, + SPM8821_ID_LDO10, + SPM8821_ID_LDO11, + SPM8821_ID_SWITCH1, +}; + +#define SPACEMIT_SPM8821_MAX_REG 0xb0 + +#define SPM8821_VERSION_ID_REG 0xa1 + +#define SPM8821_BUCK_VSEL_MASK 0xff +#define SMP8821_BUCK_EN_MASK 0x1 + +#define SPM8821_BUCK1_CTRL_REG 0x47 +#define SPM8821_BUCK2_CTRL_REG 0x4a +#define SPM8821_BUCK3_CTRL_REG 0x4d +#define SPM8821_BUCK4_CTRL_REG 0x50 +#define SPM8821_BUCK5_CTRL_REG 0x53 +#define SPM8821_BUCK6_CTRL_REG 0x56 + +#define SPM8821_BUCK1_VSEL_REG 0x48 +#define SPM8821_BUCK2_VSEL_REG 0x4b +#define SPM8821_BUCK3_VSEL_REG 0x4e +#define SPM8821_BUCK4_VSEL_REG 0x51 +#define SPM8821_BUCK5_VSEL_REG 0x54 +#define SPM8821_BUCK6_VSEL_REG 0x57 + +#define SPM8821_ALDO1_CTRL_REG 0x5b +#define SPM8821_ALDO2_CTRL_REG 0x5e +#define SPM8821_ALDO3_CTRL_REG 0x61 +#define SPM8821_ALDO4_CTRL_REG 0x64 + +#define SPM8821_ALDO1_VOLT_REG 0x5c +#define SPM8821_ALDO2_VOLT_REG 0x5f +#define SPM8821_ALDO3_VOLT_REG 0x62 +#define SPM8821_ALDO4_VOLT_REG 0x65 + +#define SPM8821_ALDO_EN_MASK 0x1 +#define SPM8821_ALDO_VSEL_MASK 0x7f + +#define SPM8821_DLDO1_CTRL_REG 0x67 +#define SPM8821_DLDO2_CTRL_REG 0x6a +#define SPM8821_DLDO3_CTRL_REG 0x6d +#define SPM8821_DLDO4_CTRL_REG 0x70 +#define SPM8821_DLDO5_CTRL_REG 0x73 +#define SPM8821_DLDO6_CTRL_REG 0x76 +#define SPM8821_DLDO7_CTRL_REG 0x79 + +#define SPM8821_DLDO1_VOLT_REG 0x68 +#define SPM8821_DLDO2_VOLT_REG 0x6b +#define SPM8821_DLDO3_VOLT_REG 0x6e +#define SPM8821_DLDO4_VOLT_REG 0x71 +#define SPM8821_DLDO5_VOLT_REG 0x74 +#define SPM8821_DLDO6_VOLT_REG 0x77 +#define SPM8821_DLDO7_VOLT_REG 0x7a + +#define SPM8821_DLDO_EN_MASK 0x1 +#define SPM8821_DLDO_VSEL_MASK 0x7f + +#define SPM8821_SWITCH_CTRL_REG 0x59 +#define SPM8821_SWTICH_EN_MASK 0x1 + +#define SPM8821_PWR_CTRL2 0x7e +#define SPM8821_SW_SHUTDOWN_BIT_MSK 0x4 +#define SPM8821_SW_RESET_BIT_MSK 0x2 + +#define SPM8821_NON_RESET_REG 0xab +#define SPM8821_RESTART_CFG_BIT_MSK 0x7 + +#define SPM8821_SLEEP_REG_OFFSET 0x1 + +#define SPM8821_ADC_AUTO_REG 0x22 +#define SPM8821_ADC_AUTO_BIT_MSK 0x7f + +#define SPM8821_ADC_CTRL_REG 0x1e +#define SPM8821_ADC_CTRL_BIT_MSK 0x3 +#define SPM8821_ADC_CTRL_EN_BIT_OFFSET 0x0 +#define SPM8821_ADC_CTRL_GO_BIT_OFFSET 0x1 + +#define SPM8821_ADC_CFG1_REG 0x20 + +#define SPM8821_ADC_CFG1_ADC_CHOP_EN_BIT_OFFSET 0x6 +#define SPM8821_ADC_CFG1_ADC_CHOP_EN_BIT_MSK 0x40 + +#define SPM8821_ADC_CFG1_ADC_CHNNL_SEL_BIT_OFFSET 0x3 +#define SPM8821_ADC_CFG1_ADC_CHNNL_SEL_BIT_MSK 0x38 + +#define SPM8821_ADC_CFG2_REG 0x21 +#define SPM8821_ADC_CFG2_REF_SEL_BIT_OFFSET 0x0 +#define SPM8821_ADC_CFG2_REF_SEL_BIT_MSK 0x3 +#define SPM8821_ADC_CFG2_3V3_REF 0x2 + +#define SPM8821_ADC_CFG2_7_DEB_NUM 0x7 +#define SPM8821_ADC_CFG2_DEB_NUM_BIT_MSK 0x70 +#define SPM8821_ADC_CFG2_DEB_NUM_BIT_OFFSET 0x4 + +#define SPM8821_ADC_EXTERNAL_CHANNEL_OFFSET 2 + +#define SPM8821_ADCIN0_RES_H_REG 0x2a +#define SPM8821_ADCIN0_RES_L_REG 0x2b +#define SPM8821_ADCIN0_REG_L_BIT_MSK 0xf0 + +#define SPM8821_DESC(_id, _match, _supply, _nv, _vr, _vm, _er, _em, _lr) \ + SPACEMIT_PMIC_REGULATOR_DESC_COMMON(_id, _match, _supply, _nv, _vr, _vm, _er, _em, _lr, pmic_dcdc_ldo_ops) + +#define SPM8821_DESC_SWITCH(_id, _match, _supply, _ereg, _emask) \ + SPACEMIT_PMIC_REGULATOR_DESC_SWITCH_COM(_id, _match, _supply, _ereg, _emask, 0, 0, pmic_switch_ops) + +#ifdef __SPACEMIT_REGULATOR_INTERNAL +static const struct spacemit_pmic_regulator_range spm8821_buck_ranges[] = +{ + SPACEMIT_PMIC_REGULATOR_RANGE(500000, 0x0, 0xaa, 5000), + SPACEMIT_PMIC_REGULATOR_RANGE(1375000, 0xab, 0xff, 25000), +}; + +static const struct spacemit_pmic_regulator_range spm8821_ldo_ranges[] = +{ + SPACEMIT_PMIC_REGULATOR_RANGE(500000, 0xb, 0x7f, 25000), +}; + +static const struct spacemit_pmic_regulator_desc spm8821_reg[] = +{ + /* BUCK */ + SPM8821_DESC(SPM8821_ID_DCDC1, "DCDC_REG1", "vcc_sys", + 255, SPM8821_BUCK1_VSEL_REG, SPM8821_BUCK_VSEL_MASK, + SPM8821_BUCK1_CTRL_REG, SMP8821_BUCK_EN_MASK, + spm8821_buck_ranges), + + SPM8821_DESC(SPM8821_ID_DCDC2, "DCDC_REG2", "vcc_sys", + 255, SPM8821_BUCK2_VSEL_REG, SPM8821_BUCK_VSEL_MASK, + SPM8821_BUCK2_CTRL_REG, SMP8821_BUCK_EN_MASK, + spm8821_buck_ranges), + + SPM8821_DESC(SPM8821_ID_DCDC3, "DCDC_REG3", "vcc_sys", + 255, SPM8821_BUCK3_VSEL_REG, SPM8821_BUCK_VSEL_MASK, + SPM8821_BUCK3_CTRL_REG, SMP8821_BUCK_EN_MASK, + spm8821_buck_ranges), + + SPM8821_DESC(SPM8821_ID_DCDC4, "DCDC_REG4", "vcc_sys", + 255, SPM8821_BUCK4_VSEL_REG, SPM8821_BUCK_VSEL_MASK, + SPM8821_BUCK4_CTRL_REG, SMP8821_BUCK_EN_MASK, + spm8821_buck_ranges), + + SPM8821_DESC(SPM8821_ID_DCDC5, "DCDC_REG5", "vcc_sys", + 255, SPM8821_BUCK5_VSEL_REG, SPM8821_BUCK_VSEL_MASK, + SPM8821_BUCK5_CTRL_REG, SMP8821_BUCK_EN_MASK, + spm8821_buck_ranges), + + SPM8821_DESC(SPM8821_ID_DCDC6, "DCDC_REG6", "vcc_sys", + 255, SPM8821_BUCK6_VSEL_REG, SPM8821_BUCK_VSEL_MASK, + SPM8821_BUCK6_CTRL_REG, SMP8821_BUCK_EN_MASK, + spm8821_buck_ranges), + /* ALDO */ + SPM8821_DESC(SPM8821_ID_LDO1, "LDO_REG1", "vcc_sys", + 128, SPM8821_ALDO1_VOLT_REG, SPM8821_ALDO_VSEL_MASK, + SPM8821_ALDO1_CTRL_REG, SPM8821_ALDO_EN_MASK, spm8821_ldo_ranges), + + SPM8821_DESC(SPM8821_ID_LDO2, "LDO_REG2", "vcc_sys", + 128, SPM8821_ALDO2_VOLT_REG, SPM8821_ALDO_VSEL_MASK, + SPM8821_ALDO2_CTRL_REG, SPM8821_ALDO_EN_MASK, spm8821_ldo_ranges), + + SPM8821_DESC(SPM8821_ID_LDO3, "LDO_REG3", "vcc_sys", + 128, SPM8821_ALDO3_VOLT_REG, SPM8821_ALDO_VSEL_MASK, + SPM8821_ALDO3_CTRL_REG, SPM8821_ALDO_EN_MASK, spm8821_ldo_ranges), + + SPM8821_DESC(SPM8821_ID_LDO4, "LDO_REG4", "vcc_sys", + 128, SPM8821_ALDO4_VOLT_REG, SPM8821_ALDO_VSEL_MASK, + SPM8821_ALDO4_CTRL_REG, SPM8821_ALDO_EN_MASK, spm8821_ldo_ranges), + + /* DLDO */ + SPM8821_DESC(SPM8821_ID_LDO5, "LDO_REG5", "dcdc5", + 128, SPM8821_DLDO1_VOLT_REG, SPM8821_DLDO_VSEL_MASK, + SPM8821_DLDO1_CTRL_REG, SPM8821_DLDO_EN_MASK, spm8821_ldo_ranges), + + SPM8821_DESC(SPM8821_ID_LDO6, "LDO_REG6", "dcdc5", + 128, SPM8821_DLDO2_VOLT_REG, SPM8821_DLDO_VSEL_MASK, + SPM8821_DLDO2_CTRL_REG, SPM8821_DLDO_EN_MASK, spm8821_ldo_ranges), + + SPM8821_DESC(SPM8821_ID_LDO7, "LDO_REG7", "dcdc5", + 128, SPM8821_DLDO3_VOLT_REG, SPM8821_DLDO_VSEL_MASK, + SPM8821_DLDO3_CTRL_REG, SPM8821_DLDO_EN_MASK, spm8821_ldo_ranges), + + SPM8821_DESC(SPM8821_ID_LDO8, "LDO_REG8", "dcdc5", + 128, SPM8821_DLDO4_VOLT_REG, SPM8821_DLDO_VSEL_MASK, + SPM8821_DLDO4_CTRL_REG, SPM8821_DLDO_EN_MASK, spm8821_ldo_ranges), + + SPM8821_DESC(SPM8821_ID_LDO9, "LDO_REG9", "dcdc5", + 128, SPM8821_DLDO5_VOLT_REG, SPM8821_DLDO_VSEL_MASK, + SPM8821_DLDO5_CTRL_REG, SPM8821_DLDO_EN_MASK, spm8821_ldo_ranges), + + SPM8821_DESC(SPM8821_ID_LDO10, "LDO_REG10", "dcdc5", + 128, SPM8821_DLDO6_VOLT_REG, SPM8821_DLDO_VSEL_MASK, + SPM8821_DLDO6_CTRL_REG, SPM8821_DLDO_EN_MASK, spm8821_ldo_ranges), + + SPM8821_DESC(SPM8821_ID_LDO11, "LDO_REG11", "dcdc5", + 128, SPM8821_DLDO7_VOLT_REG, SPM8821_DLDO_VSEL_MASK, + SPM8821_DLDO7_CTRL_REG, SPM8821_DLDO_EN_MASK, spm8821_ldo_ranges), + + /* PWR SWITCH */ + SPM8821_DESC_SWITCH(SPM8821_ID_SWITCH1, "SWITCH_REG1", "vcc_sys", + SPM8821_SWITCH_CTRL_REG, SPM8821_SWTICH_EN_MASK), +}; + +static struct spacemit_pmic_regulator_data spm8821_regulator_data = +{ + .desc = spm8821_reg, + .desc_nr = RT_ARRAY_SIZE(spm8821_reg), + .sleep_reg_offset = SPM8821_SLEEP_REG_OFFSET, +}; +#endif /* __SPACEMIT_REGULATOR_INTERNAL */ + +#ifdef __SPACEMIT_PINCTRL_INTERNAL +const char *spm8821_pinmux_functions[] = +{ + "gpioin", "gpioout", "exten", "pwrctrl", "sleep", "nreset", "adcin" +}; + +static const struct spacemit_pmic_pin_func_desc spm8821_pinfunc_desc[] = +{ + /* PIN0 gpioin */ + SPM8XX_DESC_PIN_FUNC_COM(0, "gpioin", 0x8, 0x3, 0, 0, 0, 0, 0), + /* PIN0 gpioout*/ + SPM8XX_DESC_PIN_FUNC_COM(0, "gpioout", 0x8, 0x3, 1, 0, 0, 0, 0), + /* PIN0 exten */ + SPM8XX_DESC_PIN_FUNC_COM(0, "exten", 0x8, 0x3, 0x3, 1, 0xa, 0x7, 0x0), + /* PIN0 pwrctrl */ + SPM8XX_DESC_PIN_FUNC_COM(0, "pwrctrl", 0x8, 0x3, 0x3, 1, 0xa, 0x7, 0x1), + /* PIN0 sleep */ + SPM8XX_DESC_PIN_FUNC_COM(0, "sleep", 0x8, 0x3, 0x3, 1, 0xa, 0x7, 0x2), + /* PIN0 nreset */ + SPM8XX_DESC_PIN_FUNC_COM(0, "nreset", 0x8, 0x3, 0x3, 1, 0xa, 0x7, 0x3), + /* PIN0 adcin */ + SPM8XX_DESC_PIN_FUNC_COM(0, "adcin", 0x8, 0x3, 0x3, 1, 0xa, 0x7, 0x4), + /* PIN1 gpioin */ + SPM8XX_DESC_PIN_FUNC_COM(1, "gpioin", 0x8, 0xc, 0, 0, 0, 0, 0), + /* PIN1 gpioout*/ + SPM8XX_DESC_PIN_FUNC_COM(1, "gpioout", 0x8, 0xc, 1, 0, 0, 0, 0), + /* PIN1 exten */ + SPM8XX_DESC_PIN_FUNC_COM(1, "exten", 0x8, 0xc, 0x3, 1, 0xa, 0x38, 0x0), + /* PIN1 pwrctrl */ + SPM8XX_DESC_PIN_FUNC_COM(1, "pwrctrl", 0x8, 0xc, 0x3, 1, 0xa, 0x38, 0x1), + /* PIN1 sleep */ + SPM8XX_DESC_PIN_FUNC_COM(1, "sleep", 0x8, 0xc, 0x3, 1, 0xa, 0x38, 0x2), + /* PIN1 nreset */ + SPM8XX_DESC_PIN_FUNC_COM(1, "nreset", 0x8, 0xc, 0x3, 1, 0xa, 0x38, 0x3), + /* PIN1 adcin */ + SPM8XX_DESC_PIN_FUNC_COM(1, "adcin", 0x8, 0xc, 0x3, 1, 0xa, 0x38, 0x4), + /* PIN2 gpioin */ + SPM8XX_DESC_PIN_FUNC_COM(2, "gpioin", 0x8, 0x30, 0, 0, 0, 0, 0), + /* PIN2 gpioout*/ + SPM8XX_DESC_PIN_FUNC_COM(2, "gpioout", 0x8, 0x30, 1, 0, 0, 0, 0), + /* PIN2 exten */ + SPM8XX_DESC_PIN_FUNC_COM(2, "exten", 0x8, 0x30, 0x3, 1, 0xb, 0x7, 0x0), + /* PIN2 pwrctrl */ + SPM8XX_DESC_PIN_FUNC_COM(2, "pwrctrl", 0x8, 0x30, 0x3, 1, 0xb, 0x7, 0x1), + /* PIN2 sleep */ + SPM8XX_DESC_PIN_FUNC_COM(2, "sleep", 0x8, 0x30, 0x3, 1, 0xb, 0x7, 0x2), + /* PIN2 nreset */ + SPM8XX_DESC_PIN_FUNC_COM(2, "nreset", 0x8, 0x30, 0x3, 1, 0xb, 0x7, 0x3), + /* PIN2 adcin */ + SPM8XX_DESC_PIN_FUNC_COM(2, "adcin", 0x8, 0x30, 0x3, 1, 0xb, 0x7, 0x4), + /* PIN3 gpioin */ + SPM8XX_DESC_PIN_FUNC_COM(3, "gpioin", 0x9, 0x3, 0, 0, 0, 0, 0), + /* PIN3 gpioout*/ + SPM8XX_DESC_PIN_FUNC_COM(3, "gpioout", 0x9, 0x3, 1, 0, 0, 0, 0), + /* PIN3 exten */ + SPM8XX_DESC_PIN_FUNC_COM(3, "exten", 0x9, 0x3, 0x3, 1, 0xb, 0x38, 0x0), + /* PIN3 pwrctrl */ + SPM8XX_DESC_PIN_FUNC_COM(3, "pwrctrl", 0x9, 0x3, 0x3, 1, 0xb, 0x38, 0x1), + /* PIN3 sleep */ + SPM8XX_DESC_PIN_FUNC_COM(3, "sleep", 0x9, 0x3, 0x3, 1, 0xb, 0x38, 0x2), + /* PIN3 nreset */ + SPM8XX_DESC_PIN_FUNC_COM(3, "nreset", 0x9, 0x3, 0x3, 1, 0xb, 0x38, 0x3), + /* PIN3 adcin */ + SPM8XX_DESC_PIN_FUNC_COM(3, "adcin", 0x9, 0x3, 0x3, 1, 0xb, 0x38, 0x4), + /* PIN4 gpioin */ + SPM8XX_DESC_PIN_FUNC_COM(4, "gpioin", 0x9, 0xc, 0, 0, 0, 0, 0), + /* PIN4 gpioout*/ + SPM8XX_DESC_PIN_FUNC_COM(4, "gpioout", 0x9, 0xc, 1, 0, 0, 0, 0), + /* PIN4 exten */ + SPM8XX_DESC_PIN_FUNC_COM(4, "exten", 0x9, 0xc, 0x3, 1, 0xc, 0x7, 0x0), + /* PIN4 pwrctrl */ + SPM8XX_DESC_PIN_FUNC_COM(4, "pwrctrl", 0x9, 0xc, 0x3, 1, 0xc, 0x7, 0x1), + /* PIN4 sleep */ + SPM8XX_DESC_PIN_FUNC_COM(4, "sleep", 0x9, 0xc, 0x3, 1, 0xc, 0x7, 0x2), + /* PIN4 nreset */ + SPM8XX_DESC_PIN_FUNC_COM(4, "nreset", 0x9, 0xc, 0x3, 1, 0xc, 0x7, 0x3), + /* PIN4 adcin */ + SPM8XX_DESC_PIN_FUNC_COM(4, "adcin", 0x9, 0xc, 0x3, 1, 0xc, 0x7, 0x4), + /* PIN5 gpioin */ + SPM8XX_DESC_PIN_FUNC_COM(5, "gpioin", 0x9, 0x30, 0, 0, 0, 0, 0), + /* PIN5 gpioout*/ + SPM8XX_DESC_PIN_FUNC_COM(5, "gpioout", 0x9, 0x30, 1, 0, 0, 0, 0), + /* PIN5 exten */ + SPM8XX_DESC_PIN_FUNC_COM(5, "exten", 0x9, 0x30, 0x3, 1, 0xc, 0x38, 0x0), + /* PIN5 pwrctrl */ + SPM8XX_DESC_PIN_FUNC_COM(5, "pwrctrl", 0x9, 0x30, 0x3, 1, 0xc, 0x38, 0x1), + /* PIN5 sleep */ + SPM8XX_DESC_PIN_FUNC_COM(5, "sleep", 0x9, 0x30, 0x3, 1, 0xc, 0x38, 0x2), + /* PIN5 nreset */ + SPM8XX_DESC_PIN_FUNC_COM(5, "nreset", 0x9, 0x30, 0x3, 1, 0xc, 0x38, 0x3), + /* PIN5 adcin */ + SPM8XX_DESC_PIN_FUNC_COM(5, "adcin", 0x9, 0x30, 0x3, 1, 0xc, 0x38, 0x4), +}; + +static const struct spacemit_pmic_pin_config_desc spm8821_pinconfig_desc[] = +{ + SPM8XX_DESC_PIN_CONFIG_COM(0, 0x0, 0x1, 0x1, 0x1, 0x2, 0x3, 0x4, 0xc0, 0x1, 0x5, 0x1, 0x6, 0x3), + SPM8XX_DESC_PIN_CONFIG_COM(1, 0x0, 0x2, 0x1, 0x2, 0x2, 0xc, 0x4, 0xc0, 0x2, 0x5, 0x2, 0x6, 0xc), + SPM8XX_DESC_PIN_CONFIG_COM(2, 0x0, 0x4, 0x1, 0x4, 0x2, 0x30, 0x4, 0xc0, 0x4, 0x5, 0x4, 0x6, 0x30), + SPM8XX_DESC_PIN_CONFIG_COM(3, 0x0, 0x8, 0x1, 0x8, 0x3, 0x3, 0x4, 0xc0, 0x8, 0x5, 0x8, 0x7, 0x3), + SPM8XX_DESC_PIN_CONFIG_COM(4, 0x0, 0x10, 0x1, 0x10, 0x3, 0xc, 0x4, 0xc0, 0x10, 0x5, 0x10, 0x7, 0xc), + SPM8XX_DESC_PIN_CONFIG_COM(5, 0x0, 0x20, 0x1, 0x20, 0x3, 0x30, 0x4, 0xc0, 0x20, 0x5, 0x20, 0x7, 0x30), +}; + +static const struct spacemit_pmic_pin_data spm8821_pinctrl_data = +{ + .pin_mux_nr = RT_ARRAY_SIZE(spm8821_pinmux_functions), + .pinmux_funcs = spm8821_pinmux_functions, + .pin_fuc_desc_nr = RT_ARRAY_SIZE(spm8821_pinfunc_desc), + .pinfunc_desc = spm8821_pinfunc_desc, + .pin_conf_desc_nr = RT_ARRAY_SIZE(spm8821_pinconfig_desc), + .pinconf_desc = spm8821_pinconfig_desc, +}; +#endif /* __SPACEMIT_PINCTRL_INTERNAL */ + +#ifdef __SPACEMIT_ADC_INTERNAL +static struct spacemit_pmic_iio_chan_spec spm8821_iio_desc[] = +{ + { + .channel = 0, + }, + { + .channel = 1, + }, + { + .channel = 2, + }, + { + .channel = 3, + }, + { + .channel = 4, + }, + { + .channel = 5, + }, +}; + +static const struct spacemit_pmic_adc_data spm8821_adc_data = +{ + .iio_desc = spm8821_iio_desc, + .iio_desc_nr = RT_ARRAY_SIZE(spm8821_iio_desc), +}; +#endif /* __SPACEMIT_ADC_INTERNAL */ + +#ifdef __SPACEMIT_RTC_INTERNAL +static const struct spacemit_pmic_rtc_regdesc spm8821_regdesc = +{ + .cnt_s = + { + .reg = 0xd, + .msk = 0x3f, + }, + .cnt_mi = + { + .reg = 0xe, + .msk = 0x3f, + }, + .cnt_h = + { + .reg = 0xf, + .msk = 0x1f, + }, + .cnt_d = + { + .reg = 0x10, + .msk = 0x1f, + }, + .cnt_mo = + { + .reg = 0x11, + .msk = 0xf, + }, + .cnt_y = + { + .reg = 0x12, + .msk = 0x3f, + }, + .alarm_s = + { + .reg = 0x13, + .msk = 0x3f, + }, + .alarm_mi = + { + .reg = 0x14, + .msk = 0x3f, + }, + .alarm_h = + { + .reg = 0x15, + .msk = 0x1f, + }, + .alarm_d = + { + .reg = 0x16, + .msk = 0x1f, + }, + .alarm_mo = + { + .reg = 0x17, + .msk = 0xf, + }, + .alarm_y = + { + .reg = 0x18, + .msk = 0x3f, + }, + .rtc_ctl = + { + .reg = 0x1d, + }, +}; +#endif /* __SPACEMIT_RTC_INTERNAL */ + +#ifdef __SPACEMIT_PMIC_INTERNAL +enum +{ + /* reg: 0x91 */ + SPM8821_E_GPI0, + SPM8821_E_GPI1, + SPM8821_E_GPI2, + SPM8821_E_GPI3, + SPM8821_E_GPI4, + SPM8821_E_GPI5, + + /* reg: 0x92 */ + SPM8821_E_ADC_TEMP, + SPM8821_E_ADC_EOC, + SPM8821_E_ADC_EOS, + SPM8821_E_WDT_TO, + SPM8821_E_ALARM, + SPM8821_E_TICK, + + /* reg: 0x93 */ + SPM8821_E_LDO_OV, + SPM8821_E_LDO_UV, + SPM8821_E_LDO_SC, + SPM8821_E_SW_SC, + SPM8821_E_TEMP_WARN, + SPM8821_E_TEMP_SEVERE, + SPM8821_E_TEMP_CRIT, + + /* reg: 0x94 */ + SPM8821_E_BUCK1_OV, + SPM8821_E_BUCK2_OV, + SPM8821_E_BUCK3_OV, + SPM8821_E_BUCK4_OV, + SPM8821_E_BUCK5_OV, + SPM8821_E_BUCK6_OV, + + /* reg: 0x95 */ + SPM8821_E_BUCK1_UV, + SPM8821_E_BUCK2_UV, + SPM8821_E_BUCK3_UV, + SPM8821_E_BUCK4_UV, + SPM8821_E_BUCK5_UV, + SPM8821_E_BUCK6_UV, + + /* reg: 0x96 */ + SPM8821_E_BUCK1_SC, + SPM8821_E_BUCK2_SC, + SPM8821_E_BUCK3_SC, + SPM8821_E_BUCK4_SC, + SPM8821_E_BUCK5_SC, + SPM8821_E_BUCK6_SC, + + /* reg: 0x97 */ + SPM8821_E_PWRON_RINTR, + SPM8821_E_PWRON_FINTR, + SPM8821_E_PWRON_SINTR, + SPM8821_E_PWRON_LINTR, + SPM8821_E_PWRON_SDINTR, + SPM8821_E_VSYS_OV, +}; + +#define SPM8821_E_GPI0_MSK RT_BIT(0) +#define SPM8821_E_GPI1_MSK RT_BIT(1) +#define SPM8821_E_GPI2_MSK RT_BIT(2) +#define SPM8821_E_GPI3_MSK RT_BIT(3) +#define SPM8821_E_GPI4_MSK RT_BIT(4) +#define SPM8821_E_GPI5_MSK RT_BIT(5) + +#define SPM8821_E_ADC_TEMP_MSK RT_BIT(0) +#define SPM8821_E_ADC_EOC_MSK RT_BIT(1) +#define SPM8821_E_ADC_EOS_MSK RT_BIT(2) +#define SPM8821_E_WDT_TO_MSK RT_BIT(3) +#define SPM8821_E_ALARM_MSK RT_BIT(4) +#define SPM8821_E_TICK_MSK RT_BIT(5) + +#define SPM8821_E_LDO_OV_MSK RT_BIT(0) +#define SPM8821_E_LDO_UV_MSK RT_BIT(1) +#define SPM8821_E_LDO_SC_MSK RT_BIT(2) +#define SPM8821_E_SW_SC_MSK RT_BIT(3) +#define SPM8821_E_TEMP_WARN_MSK RT_BIT(4) +#define SPM8821_E_TEMP_SEVERE_MSK RT_BIT(5) +#define SPM8821_E_TEMP_CRIT_MSK RT_BIT(6) + +#define SPM8821_E_BUCK1_OV_MSK RT_BIT(0) +#define SPM8821_E_BUCK2_OV_MSK RT_BIT(1) +#define SPM8821_E_BUCK3_OV_MSK RT_BIT(2) +#define SPM8821_E_BUCK4_OV_MSK RT_BIT(3) +#define SPM8821_E_BUCK5_OV_MSK RT_BIT(4) +#define SPM8821_E_BUCK6_OV_MSK RT_BIT(5) + +#define SPM8821_E_BUCK1_UV_MSK RT_BIT(0) +#define SPM8821_E_BUCK2_UV_MSK RT_BIT(1) +#define SPM8821_E_BUCK3_UV_MSK RT_BIT(2) +#define SPM8821_E_BUCK4_UV_MSK RT_BIT(3) +#define SPM8821_E_BUCK5_UV_MSK RT_BIT(4) +#define SPM8821_E_BUCK6_UV_MSK RT_BIT(5) + +#define SPM8821_E_BUCK1_SC_MSK RT_BIT(0) +#define SPM8821_E_BUCK2_SC_MSK RT_BIT(1) +#define SPM8821_E_BUCK3_SC_MSK RT_BIT(2) +#define SPM8821_E_BUCK4_SC_MSK RT_BIT(3) +#define SPM8821_E_BUCK5_SC_MSK RT_BIT(4) +#define SPM8821_E_BUCK6_SC_MSK RT_BIT(5) + +#define SPM8821_E_PWRON_RINTR_MSK RT_BIT(0) +#define SPM8821_E_PWRON_FINTR_MSK RT_BIT(1) +#define SPM8821_E_PWRON_SINTR_MSK RT_BIT(2) +#define SPM8821_E_PWRON_LINTR_MSK RT_BIT(3) +#define SPM8821_E_PWRON_SDINTR_MSK RT_BIT(4) +#define SPM8821_E_VSYS_OV_MSK RT_BIT(5) + +#define SPM8821_E_STATUS_REG_BASE 0x91 +#define SPM8821_E_EN_REG_BASE 0x98 + +static const struct spacemit_pmic_irq_map spm8821_irqs[] = +{ + [SPM8821_E_GPI0] = + { + .mask = SPM8821_E_GPI0_MSK, + .reg_offset = 0, + }, + [SPM8821_E_GPI1] = + { + .mask = SPM8821_E_GPI1_MSK, + .reg_offset = 0, + }, + [SPM8821_E_GPI2] = + { + .mask = SPM8821_E_GPI2_MSK, + .reg_offset = 0, + }, + [SPM8821_E_GPI3] = + { + .mask = SPM8821_E_GPI3_MSK, + .reg_offset = 0, + }, + [SPM8821_E_GPI4] = + { + .mask = SPM8821_E_GPI4_MSK, + .reg_offset = 0, + }, + [SPM8821_E_GPI5] = + { + .mask = SPM8821_E_GPI5_MSK, + .reg_offset = 0, + }, + [SPM8821_E_ADC_TEMP] = + { + .mask = SPM8821_E_ADC_TEMP_MSK, + .reg_offset = 1, + }, + [SPM8821_E_ADC_EOC] = + { + .mask = SPM8821_E_ADC_EOC_MSK, + .reg_offset = 1, + }, + [SPM8821_E_ADC_EOS] = + { + .mask = SPM8821_E_ADC_EOS_MSK, + .reg_offset = 1, + }, + [SPM8821_E_WDT_TO] = + { + .mask = SPM8821_E_WDT_TO_MSK, + .reg_offset = 1, + }, + [SPM8821_E_ALARM] = + { + .mask = SPM8821_E_ALARM_MSK, + .reg_offset = 1, + }, + [SPM8821_E_TICK] = + { + .mask = SPM8821_E_TICK_MSK, + .reg_offset = 1, + }, + [SPM8821_E_LDO_OV] = + { + .mask = SPM8821_E_LDO_OV_MSK, + .reg_offset = 2, + }, + [SPM8821_E_LDO_UV] = + { + .mask = SPM8821_E_LDO_UV_MSK, + .reg_offset = 2, + }, + [SPM8821_E_LDO_SC] = + { + .mask = SPM8821_E_LDO_SC_MSK, + .reg_offset = 2, + }, + [SPM8821_E_SW_SC] = + { + .mask = SPM8821_E_SW_SC_MSK, + .reg_offset = 2, + }, + [SPM8821_E_TEMP_WARN] = + { + .mask = SPM8821_E_TEMP_WARN_MSK, + .reg_offset = 2, + }, + [SPM8821_E_TEMP_SEVERE] = + { + .mask = SPM8821_E_TEMP_SEVERE_MSK, + .reg_offset = 2, + }, + [SPM8821_E_TEMP_CRIT] = + { + .mask = SPM8821_E_TEMP_CRIT_MSK, + .reg_offset = 2, + }, + [SPM8821_E_BUCK1_OV] = + { + .mask = SPM8821_E_BUCK1_OV_MSK, + .reg_offset = 3, + }, + [SPM8821_E_BUCK2_OV] = + { + .mask = SPM8821_E_BUCK2_OV_MSK, + .reg_offset = 3, + }, + [SPM8821_E_BUCK3_OV] = + { + .mask = SPM8821_E_BUCK3_OV_MSK, + .reg_offset = 3, + }, + [SPM8821_E_BUCK4_OV] = + { + .mask = SPM8821_E_BUCK4_OV_MSK, + .reg_offset = 3, + }, + [SPM8821_E_BUCK5_OV] = + { + .mask = SPM8821_E_BUCK5_OV_MSK, + .reg_offset = 3, + }, + [SPM8821_E_BUCK6_OV] = + { + .mask = SPM8821_E_BUCK6_OV_MSK, + .reg_offset = 3, + }, + [SPM8821_E_BUCK1_UV] = + { + .mask = SPM8821_E_BUCK1_UV_MSK, + .reg_offset = 4, + }, + [SPM8821_E_BUCK2_UV] = + { + .mask = SPM8821_E_BUCK2_UV_MSK, + .reg_offset = 4, + }, + [SPM8821_E_BUCK3_UV] = + { + .mask = SPM8821_E_BUCK3_UV_MSK, + .reg_offset = 4, + }, + [SPM8821_E_BUCK4_UV] = + { + .mask = SPM8821_E_BUCK4_UV_MSK, + .reg_offset = 4, + }, + [SPM8821_E_BUCK5_UV] = + { + .mask = SPM8821_E_BUCK5_UV_MSK, + .reg_offset = 4, + }, + [SPM8821_E_BUCK6_UV] = + { + .mask = SPM8821_E_BUCK6_UV_MSK, + .reg_offset = 4, + }, + [SPM8821_E_BUCK1_SC] = + { + .mask = SPM8821_E_BUCK1_SC_MSK, + .reg_offset = 5, + }, + [SPM8821_E_BUCK2_SC] = + { + .mask = SPM8821_E_BUCK2_SC_MSK, + .reg_offset = 5, + }, + [SPM8821_E_BUCK3_SC] = + { + .mask = SPM8821_E_BUCK3_SC_MSK, + .reg_offset = 5, + }, + [SPM8821_E_BUCK4_SC] = + { + .mask = SPM8821_E_BUCK4_SC_MSK, + .reg_offset = 5, + }, + [SPM8821_E_BUCK5_SC] = + { + .mask = SPM8821_E_BUCK5_SC_MSK, + .reg_offset = 5, + }, + [SPM8821_E_BUCK6_SC] = + { + .mask = SPM8821_E_BUCK6_SC_MSK, + .reg_offset = 5, + }, + [SPM8821_E_PWRON_RINTR] = + { + .mask = SPM8821_E_PWRON_RINTR_MSK, + .reg_offset = 6, + }, + [SPM8821_E_PWRON_FINTR] = + { + .mask = SPM8821_E_PWRON_FINTR_MSK, + .reg_offset = 6, + }, + [SPM8821_E_PWRON_SINTR] = + { + .mask = SPM8821_E_PWRON_SINTR_MSK, + .reg_offset = 6, + }, + [SPM8821_E_PWRON_LINTR] = + { + .mask = SPM8821_E_PWRON_LINTR_MSK, + .reg_offset = 6, + }, + [SPM8821_E_PWRON_SDINTR] = + { + .mask = SPM8821_E_PWRON_SDINTR_MSK, + .reg_offset = 6, + }, + [SPM8821_E_VSYS_OV] = + { + .mask = SPM8821_E_VSYS_OV_MSK, + .reg_offset = 6, + }, +}; + +static rt_uint32_t spm8821_irq_status[7]; + +static struct spacemit_pmic_irqchip spm8821_irq_chip = +{ + .num_regs = RT_ARRAY_SIZE(spm8821_irq_status), + .irq_status = spm8821_irq_status, + .reg_stride = 1, + .status_base = SPM8821_E_STATUS_REG_BASE, + .mask_base = SPM8821_E_EN_REG_BASE, + .ack_base = SPM8821_E_STATUS_REG_BASE, + .init_ack_masked = RT_TRUE, + .mask_unmask_non_inverted = RT_TRUE, + .irqs_map = spm8821_irqs, + .irqs_nr = RT_ARRAY_SIZE(spm8821_irqs), +}; + +static const int spm8821_pwrkey_irqs[] = +{ + SPM8821_E_PWRON_RINTR, + SPM8821_E_PWRON_FINTR, + SPM8821_E_PWRON_SINTR, + SPM8821_E_PWRON_LINTR, +}; + +static const int spm8821_rtc_irqs[] = +{ + SPM8821_E_ALARM, +}; + +static const int spm8821_adc_irqs[] = +{ + SPM8821_E_ADC_EOC, +}; + +static struct spacemit_pmic_mfd_endpoint spm8821_mfd_endpoint[] = +{ + { + .ofw_name = "spacemit-regulator@spm8821", + .ofw_compatible = "pmic,regulator,spm8821", + }, + { + .ofw_name = "spacemit-pinctrl@spm8821", + .ofw_compatible = "pmic,pinctrl,spm8821", + }, + { + .ofw_name = "spacemit-pwrkey@spm8821", + .ofw_compatible = "pmic,pwrkey,spm8821", + .irqs_nr = RT_ARRAY_SIZE(spm8821_pwrkey_irqs), + .irqs_list = spm8821_pwrkey_irqs, + }, + { + .ofw_name = "spacemit-rtc@spm8821", + .ofw_compatible = "pmic,rtc,spm8821", + .irqs_nr = RT_ARRAY_SIZE(spm8821_rtc_irqs), + .irqs_list = spm8821_rtc_irqs, + }, + { + .ofw_name = "spacemit-adc@spm8821", + .ofw_compatible = "pmic,adc,spm8821", + .irqs_nr = RT_ARRAY_SIZE(spm8821_adc_irqs), + .irqs_list = spm8821_adc_irqs, + }, +}; + +static struct spacemit_pmic_mfd_data spm8821_mfd_data = +{ + .regmap_cfg = + { + .reg_bits = 8, + .val_bits = 8, + .max_register = SPACEMIT_SPM8821_MAX_REG, + }, + + .endpoint = spm8821_mfd_endpoint, + .endpoint_num = RT_ARRAY_SIZE(spm8821_mfd_endpoint), + + .irqchip = &spm8821_irq_chip, + + .shutdown = + { + .reg = SPM8821_PWR_CTRL2, + .bit = SPM8821_SW_SHUTDOWN_BIT_MSK, + }, + .reboot = + { + .reg = SPM8821_PWR_CTRL2, + .bit = SPM8821_SW_RESET_BIT_MSK, + }, + .non_reset = + { + .reg = SPM8821_NON_RESET_REG, + .bit = SPM8821_RESTART_CFG_BIT_MSK, + }, +}; +#endif /* __SPACEMIT_PMIC_INTERNAL */ + +#endif /* __SPM8821_H__ */ diff --git a/bsp/spacemit/dm/include/mfd/sy8810l.h b/bsp/spacemit/dm/include/mfd/sy8810l.h new file mode 100755 index 00000000000..e80a9c5ba2c --- /dev/null +++ b/bsp/spacemit/dm/include/mfd/sy8810l.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2006-2024, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2024-5-1 GuEe-GUI first version + */ + +#ifndef __SY8810L_H__ +#define __SY8810L_H__ + +enum +{ + SY8810L_ID_DCDC1, +}; + +#define SPACEMIT_SY8810L_MAX_REG 0x2 + +#define SY8810L_BUCK_VSEL_MASK 0x3f +#define SY8810L_BUCK_EN_MASK 0x80 + +#define SY8810L_BUCK_CTRL_REG 0x1 +#define SY8810L_BUCK_VSEL_REG 0x0 + +#define SY8810L_DESC(_id, _match, _supply, _nv, _vr, _vm, _er, _em, _lr) \ + SPACEMIT_PMIC_REGULATOR_DESC_COMMON(_id, _match, _supply, _nv, _vr, _vm, _er, _em, _lr, pmic_dcdc_ldo_ops) + +#ifdef __SPACEMIT_REGULATOR_INTERNAL +static const struct spacemit_pmic_regulator_range sy8810l_buck_ranges[] = +{ + SPACEMIT_PMIC_REGULATOR_RANGE(600000, 0x0, 0x5a, 10000), +}; + +static const struct spacemit_pmic_regulator_desc sy8810l_reg[] = +{ + /* BUCK */ + SY8810L_DESC(SY8810L_ID_DCDC1, "EDCDC_REG1", "dcdc1", + 91, SY8810L_BUCK_VSEL_REG, SY8810L_BUCK_VSEL_MASK, + SY8810L_BUCK_CTRL_REG, SY8810L_BUCK_EN_MASK, sy8810l_buck_ranges), +}; + +static struct spacemit_pmic_regulator_data sy8810l_regulator_data = +{ + .desc = sy8810l_reg, + .desc_nr = RT_ARRAY_SIZE(sy8810l_reg), +}; +#endif /* __SPACEMIT_REGULATOR_INTERNAL */ + +#ifdef __SPACEMIT_PMIC_INTERNAL +static struct spacemit_pmic_mfd_endpoint sy8810l_mfd_endpoint[] = +{ + { + .ofw_name = "spacemit-regulator@sy8810l", + .ofw_compatible = "pmic,regulator,sy8810l", + }, +}; + +static struct spacemit_pmic_mfd_data sy8810l_mfd_data = +{ + .regmap_cfg = + { + .reg_bits = 8, + .val_bits = 8, + .max_register = SPACEMIT_SY8810L_MAX_REG, + }, + .endpoint = sy8810l_mfd_endpoint, + .endpoint_num = RT_ARRAY_SIZE(sy8810l_mfd_endpoint), +}; +#endif /* __SPACEMIT_PMIC_INTERNAL */ + +#endif /* __SY8810L_H__ */ diff --git a/bsp/spacemit/dm/include/spacemit.h b/bsp/spacemit/dm/include/spacemit.h new file mode 100755 index 00000000000..587d5a3cd60 --- /dev/null +++ b/bsp/spacemit/dm/include/spacemit.h @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2006-2024, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2024-5-1 GuEe-GUI first version + */ + +#ifndef __SPACEMIT_H__ +#define __SPACEMIT_H__ + +#include + +#define is_aligned(x, a) (((x) & ((typeof(x))(a) - 1)) == 0) + +#define readx_poll_timeout(OP, 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) = OP(ADDR); \ + if (COND) \ + { \ + break; \ + } \ + if (timeout_us && left_ns < 0) \ + { \ + (VAL) = OP(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; \ +}) + +#define readl_poll_timeout(ADDR, VAL, COND, DELAY_US, TIMEOUT_US) \ + readx_poll_timeout(HWREG32, ADDR, VAL, COND, DELAY_US, TIMEOUT_US) + +rt_inline rt_uint32_t fls(rt_uint32_t val) +{ + rt_uint32_t bit = 32; + + if (!val) + return 0; + if (!(val & 0xffff0000u)) + { + val <<= 16; + bit -= 16; + } + if (!(val & 0xff000000u)) + { + val <<= 8; + bit -= 8; + } + if (!(val & 0xf0000000u)) + { + val <<= 4; + bit -= 4; + } + if (!(val & 0xc0000000u)) + { + val <<= 2; + bit -= 2; + } + if (!(val & 0x80000000u)) + { + bit -= 1; + } + + return bit; +} + +#ifdef ARCH_RISCV +#include + +/* TCM enable register */ +#define CSR_TCMCFG 0x5db +#define TCM_EN _UL(0x00000001) /* TCM Access Enable */ + +rt_inline rt_uint32_t tcm_override_readl(const volatile void *addr) +{ + rt_uint32_t val; + rt_ubase_t level, tcm_csr; + + level = rt_hw_local_irq_disable(); + tcm_csr = csr_read_clear(CSR_TCMCFG, TCM_EN); + val = HWREG32(addr); + csr_set(CSR_TCMCFG, tcm_csr); + rt_hw_local_irq_enable(level); + + return val; +} + +/* i/o write on the tcm override area */ +rt_inline void tcm_override_writel(volatile void *addr, rt_uint32_t val) +{ + rt_ubase_t level, tcm_csr; + + level = rt_hw_local_irq_disable(); + tcm_csr = csr_read_clear(CSR_TCMCFG, TCM_EN); + HWREG32(addr) = val; + csr_set(CSR_TCMCFG, tcm_csr); + rt_hw_local_irq_enable(level); +} +#endif /* ARCH_RISCV */ + +#endif /* __SPACEMIT_H__ */ diff --git a/bsp/spacemit/dm/input/SConscript b/bsp/spacemit/dm/input/SConscript new file mode 100755 index 00000000000..3d15055d62d --- /dev/null +++ b/bsp/spacemit/dm/input/SConscript @@ -0,0 +1,13 @@ +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/spacemit/dm/input/misc/Kconfig b/bsp/spacemit/dm/input/misc/Kconfig new file mode 100755 index 00000000000..64e5bd720f6 --- /dev/null +++ b/bsp/spacemit/dm/input/misc/Kconfig @@ -0,0 +1,5 @@ +config RT_INPUT_MISC_PWRKEY_SPACEMIT_PMIC + bool "Spacemit PMIC power key support" + depends on RT_INPUT_MISC + depends on RT_MFD_SPACEMIT_PMIC + default n diff --git a/bsp/spacemit/dm/input/misc/SConscript b/bsp/spacemit/dm/input/misc/SConscript new file mode 100755 index 00000000000..c1f5f17ab6e --- /dev/null +++ b/bsp/spacemit/dm/input/misc/SConscript @@ -0,0 +1,13 @@ +from building import * + +group = [] +src = [] +cwd = GetCurrentDir() +CPPPATH = [cwd + '/../../include'] + +if GetDepend(['RT_INPUT_MISC_PWRKEY_SPACEMIT_PMIC']): + src += ['pwrkey-spacemit-pmic.c'] + +group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') diff --git a/bsp/spacemit/dm/input/misc/pwrkey-spacemit-pmic.c b/bsp/spacemit/dm/input/misc/pwrkey-spacemit-pmic.c new file mode 100755 index 00000000000..58ef21a054d --- /dev/null +++ b/bsp/spacemit/dm/input/misc/pwrkey-spacemit-pmic.c @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2006-2024, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2024-5-1 GuEe-GUI first version + */ + +#define __SPACEMIT_PWRKEY_INTERNAL +#include + +struct spacemit_pmic_pwrkey +{ + struct rt_input_device parent; + + int fall_irq, rise_irq; + int s_key_irq, l_key_irq; +}; + +static void spacemit_pmic_pwrkey_fall_irq(int irqno, void *param) +{ + struct spacemit_pmic_pwrkey *pwr = param; + + rt_interrupt_enter(); + + rt_input_report_key(&pwr->parent, KEY_POWER, 1); + rt_input_sync(&pwr->parent); + + rt_interrupt_leave(); +} + +static void spacemit_pmic_pwrkey_rise_irq(int irqno, void *param) +{ + struct spacemit_pmic_pwrkey *pwr = param; + + rt_interrupt_enter(); + + rt_input_report_key(&pwr->parent, KEY_POWER, 0); + rt_input_sync(&pwr->parent); + + rt_interrupt_leave(); +} + +static void spacemit_pmic_pwrkey_skey_irq(int irqno, void *param) +{ + /* Do nothing by now */ +} + +static void spacemit_pmic_pwrkey_lkey_irq(int irqno, void *param) +{ + /* Do nothing by now */ +} + +static rt_err_t spacemit_pmic_pwrkey_probe(struct rt_platform_device *pdev) +{ + rt_err_t err; + struct rt_device *dev = &pdev->parent; + struct spacemit_pmic_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; + } + + if ((pwr->s_key_irq = rt_dm_dev_get_irq(dev, 2)) < 0) + { + err = pwr->s_key_irq; + goto _fail; + } + + if ((pwr->l_key_irq = rt_dm_dev_get_irq(dev, 3)) < 0) + { + err = pwr->l_key_irq; + goto _fail; + } + + rt_input_set_capability(&pwr->parent, EV_KEY, KEY_POWER); + + if ((err = rt_input_device_register(&pwr->parent))) + { + goto _fail; + } + + dev->user_data = pwr; + + rt_hw_interrupt_install(pwr->fall_irq, spacemit_pmic_pwrkey_fall_irq, pwr, "pwrkey-pmic-fall"); + rt_hw_interrupt_umask(pwr->fall_irq); + + rt_hw_interrupt_install(pwr->rise_irq, spacemit_pmic_pwrkey_rise_irq, pwr, "pwrkey-pmic-rise"); + rt_hw_interrupt_umask(pwr->rise_irq); + + rt_hw_interrupt_install(pwr->s_key_irq, spacemit_pmic_pwrkey_skey_irq, pwr, "pwrkey-pmic-skey"); + rt_hw_interrupt_umask(pwr->s_key_irq); + + rt_hw_interrupt_install(pwr->l_key_irq, spacemit_pmic_pwrkey_lkey_irq, pwr, "pwrkey-pmic-lkey"); + rt_hw_interrupt_umask(pwr->l_key_irq); + + return RT_EOK; + +_fail: + rt_free(pwr); + + return err; +} + +static rt_err_t spacemit_pmic_pwrkey_remove(struct rt_platform_device *pdev) +{ + struct spacemit_pmic_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_hw_interrupt_mask(pwr->s_key_irq); + rt_pic_detach_irq(pwr->s_key_irq, pwr); + + rt_hw_interrupt_mask(pwr->l_key_irq); + rt_pic_detach_irq(pwr->l_key_irq, pwr); + + rt_input_device_unregister(&pwr->parent); + + rt_free(pwr); + + return RT_EOK; +} + +static const struct rt_ofw_node_id spacemit_pmic_pwrkey_ofw_ids[] = +{ + { .compatible = "pmic,pwrkey,spm8821" }, + { /* sentinel */ } +}; + +static struct rt_platform_driver spacemit_pmic_pwrkey_driver = +{ + .name = "spacemit-pmic-pwrkey", + .ids = spacemit_pmic_pwrkey_ofw_ids, + + .probe = spacemit_pmic_pwrkey_probe, + .remove = spacemit_pmic_pwrkey_remove, +}; +RT_PLATFORM_DRIVER_EXPORT(spacemit_pmic_pwrkey_driver); diff --git a/bsp/spacemit/dm/mailbox/Kconfig b/bsp/spacemit/dm/mailbox/Kconfig new file mode 100755 index 00000000000..b4f54b2dee3 --- /dev/null +++ b/bsp/spacemit/dm/mailbox/Kconfig @@ -0,0 +1,3 @@ +config RT_MBOX_K1X + bool "Spacemit K1pro Mailbox Support" + default n diff --git a/bsp/spacemit/dm/mailbox/SConscript b/bsp/spacemit/dm/mailbox/SConscript new file mode 100755 index 00000000000..176d261ed8a --- /dev/null +++ b/bsp/spacemit/dm/mailbox/SConscript @@ -0,0 +1,12 @@ +from building import * + +cwd = GetCurrentDir() +src = [] +CPPPATH = [cwd + '/../include'] + +if GetDepend(['RT_MBOX_K1X']): + src += ['mailbox-k1x.c'] + +group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') diff --git a/bsp/spacemit/dm/mailbox/mailbox-k1x.c b/bsp/spacemit/dm/mailbox/mailbox-k1x.c new file mode 100755 index 00000000000..6daac5f0a47 --- /dev/null +++ b/bsp/spacemit/dm/mailbox/mailbox-k1x.c @@ -0,0 +1,248 @@ +/* + * Copyright (c) 2006-2024, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2024-5-1 GuEe-GUI first version + */ + +#include +#include + +#define DBG_TAG "mailbox.rockchip" +#define DBG_LVL DBG_INFO +#include + +#define SPACEMIT_NUM_CHANNELS 4 +#define SPACEMIT_TX_ACK_OFFSET 4 + +#define MBOX_REG_IPC_DW 0x00 +#define MBOX_REG_IPC_WDR 0x04 +#define MBOX_REG_ISRW 0x08 +#define MBOX_REG_ICR 0x0C +#define MBOX_REG_IIR 0x10 +#define MBOX_REG_RDR 0x14 + +struct spacemit_mailbox +{ + struct rt_mbox_controller parent; + + int irq; + void *regs; + struct rt_reset_control *rstc; + + struct rt_mbox_chan chans[]; +}; + +#define raw_to_spacemit_mailbox(raw) rt_container_of(raw, struct spacemit_mailbox, parent) + +static rt_err_t spacemit_mailbox_request(struct rt_mbox_chan *chan) +{ + return RT_EOK; +} + +static void spacemit_mailbox_release(struct rt_mbox_chan *chan) +{ + struct spacemit_mailbox *sp_mbox = raw_to_spacemit_mailbox(chan->ctrl); + + HWREG32(sp_mbox->regs + MBOX_REG_ICR) = RT_BIT(chan - sp_mbox->chans); +} + +static rt_err_t spacemit_mailbox_send(struct rt_mbox_chan *chan, const void *data) +{ + struct spacemit_mailbox *sp_mbox = raw_to_spacemit_mailbox(chan->ctrl); + + HWREG32(sp_mbox->regs + MBOX_REG_ISRW) = RT_BIT(chan - sp_mbox->chans); + + return RT_EOK; +} + +static rt_bool_t spacemit_mailbox_peek(struct rt_mbox_chan *chan) +{ + struct spacemit_mailbox *sp_mbox = raw_to_spacemit_mailbox(chan->ctrl); + + return !!(HWREG32(sp_mbox->regs + MBOX_REG_RDR) & RT_BIT(chan - sp_mbox->chans)); +} + +static const struct rt_mbox_controller_ops spacemit_mailbox_ops = +{ + .request = spacemit_mailbox_request, + .release = spacemit_mailbox_release, + .send = spacemit_mailbox_send, + .peek = spacemit_mailbox_peek, +}; + +static void spacemit_mailbox_isr(int irqno, void *param) +{ + rt_uint32_t status, msg = 0; + struct rt_mbox_chan *chan; + struct spacemit_mailbox *sp_mbox = param; + + HWREG32(sp_mbox->regs + MBOX_REG_IIR) = 0; + + status = HWREG32(sp_mbox->regs + MBOX_REG_IIR); + + if (!(status & 0xff)) + { + return; + } + + for (int i = SPACEMIT_TX_ACK_OFFSET; i < SPACEMIT_NUM_CHANNELS + SPACEMIT_TX_ACK_OFFSET; ++i) + { + chan = &sp_mbox->chans[i - SPACEMIT_TX_ACK_OFFSET]; + + if (!(status & RT_BIT(i))) + { + continue; + } + + /* Clear the irq pending */ + HWREG32(sp_mbox->regs + MBOX_REG_ICR) = 1 << i; + + rt_mbox_send_done(chan, 0); + } + + for (int i = 0; i < SPACEMIT_NUM_CHANNELS; ++i) + { + chan = &sp_mbox->chans[i]; + + if (!(status & RT_BIT(i))) + { + continue; + } + + rt_mbox_recv(chan, &msg); + + /* Clear the irq pending */ + HWREG32(sp_mbox->regs + MBOX_REG_ICR) = RT_BIT(i); + + /* Then send an ack */ + HWREG32(sp_mbox->regs + MBOX_REG_ISRW) = RT_BIT(i + SPACEMIT_TX_ACK_OFFSET); + } +} + +static void spacemit_mailbox_free_resource(struct spacemit_mailbox *sp_mbox) +{ + if (sp_mbox->regs) + { + rt_iounmap(sp_mbox->regs); + } + + if (!rt_is_err_or_null(sp_mbox->rstc)) + { + rt_reset_control_put(sp_mbox->rstc); + } + + rt_free(sp_mbox); +} + +static rt_err_t spacemit_mailbox_probe(struct rt_platform_device *pdev) +{ + rt_err_t err; + struct rt_device *dev = &pdev->parent; + struct spacemit_mailbox *sp_mbox = rt_calloc(1, sizeof(*sp_mbox) + + SPACEMIT_NUM_CHANNELS * sizeof(struct rt_mbox_chan)); + + if (!sp_mbox) + { + return -RT_ENOMEM; + } + + for (int i = 0; i < SPACEMIT_NUM_CHANNELS; ++i) + { + sp_mbox->chans[i].priv = sp_mbox; + } + + sp_mbox->regs = rt_dm_dev_iomap(dev, 0); + + if (!sp_mbox->regs) + { + err = -RT_EIO; + goto _fail; + } + + sp_mbox->rstc = rt_reset_control_get_by_name(dev, "core_reset"); + + if (rt_is_err(sp_mbox->rstc)) + { + err = rt_ptr_err(sp_mbox->rstc); + goto _fail; + } + + if ((err = rt_reset_control_deassert(sp_mbox->rstc))) + { + goto _fail; + } + + sp_mbox->irq = rt_dm_dev_get_irq(dev, 0); + + if (sp_mbox->irq < 0) + { + err = sp_mbox->irq; + goto _fail; + } + + rt_hw_interrupt_install(sp_mbox->irq, spacemit_mailbox_isr, sp_mbox, "sp_mbox"); + rt_hw_interrupt_umask(sp_mbox->irq); + + dev->user_data = sp_mbox; + + sp_mbox->parent.dev = dev; + sp_mbox->parent.num_chans = SPACEMIT_NUM_CHANNELS; + sp_mbox->parent.ops = &spacemit_mailbox_ops; + + if ((err = rt_mbox_controller_register(&sp_mbox->parent))) + { + goto _free_irq; + } + + return RT_EOK; + +_free_irq: + rt_hw_interrupt_mask(sp_mbox->irq); + rt_pic_detach_irq(sp_mbox->irq, sp_mbox); + +_fail: + spacemit_mailbox_free_resource(sp_mbox); + + return err; +} + +static rt_err_t spacemit_mailbox_remove(struct rt_platform_device *pdev) +{ + struct spacemit_mailbox *sp_mbox = pdev->parent.user_data; + + rt_hw_interrupt_mask(sp_mbox->irq); + rt_pic_detach_irq(sp_mbox->irq, sp_mbox); + + rt_mbox_controller_unregister(&sp_mbox->parent); + + spacemit_mailbox_free_resource(sp_mbox); + + return RT_EOK; +} + +static const struct rt_ofw_node_id spacemit_mailbox_ofw_ids[] = +{ + { .compatible = "spacemit,k1-x-mailbox", }, + { /* sentinel */ } +}; + +static struct rt_platform_driver spacemit_mailbox_driver = +{ + .name = "mailbox-spacemit", + .ids = spacemit_mailbox_ofw_ids, + + .probe = spacemit_mailbox_probe, + .remove = spacemit_mailbox_remove, +}; + +static int spacemit_mailbox_drv_register(void) +{ + rt_platform_driver_register(&spacemit_mailbox_driver); + + return 0; +} +INIT_SUBSYS_EXPORT(spacemit_mailbox_drv_register); diff --git a/bsp/spacemit/dm/mfd/Kconfig b/bsp/spacemit/dm/mfd/Kconfig new file mode 100755 index 00000000000..017cd0cad80 --- /dev/null +++ b/bsp/spacemit/dm/mfd/Kconfig @@ -0,0 +1,7 @@ +config RT_MFD_SPACEMIT_PMIC + bool "Spacemit Power Management Chip" + depends on RT_USING_PIC + depends on RT_USING_I2C + depends on RT_USING_OFW + select RT_MFD_SYSCON + default y diff --git a/bsp/spacemit/dm/mfd/SConscript b/bsp/spacemit/dm/mfd/SConscript new file mode 100755 index 00000000000..e188ee12112 --- /dev/null +++ b/bsp/spacemit/dm/mfd/SConscript @@ -0,0 +1,12 @@ +from building import * + +cwd = GetCurrentDir() +src = [] +CPPPATH = [cwd + '/../include'] + +if GetDepend(['RT_MFD_SPACEMIT_PMIC']): + src += ['spacemit-pmic.c'] + +group = DefineGroup('DeviceDrivers', src, depend = [], CPPPATH = CPPPATH) + +Return('group') diff --git a/bsp/spacemit/dm/mfd/spacemit-pmic.c b/bsp/spacemit/dm/mfd/spacemit-pmic.c new file mode 100755 index 00000000000..64a2497f7fa --- /dev/null +++ b/bsp/spacemit/dm/mfd/spacemit-pmic.c @@ -0,0 +1,584 @@ +/* + * Copyright (c) 2006-2024, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2024-5-1 GuEe-GUI first version + */ + +#include +#include + +#define DBG_TAG "mfd.spacemit-pmic" +#define DBG_LVL DBG_INFO +#include + +#include +#define __SPACEMIT_PMIC_INTERNAL +#include + +struct restart_config_info +{ + const char *cmd; + uint32_t value; +}; + +static const struct restart_config_info config_info[] = +{ + /* Enter uboot fastboot mode after restart */ + { "fastboot", 1 }, + /* Enter uboot shell after restart */ + { "uboot", 2 }, +}; + +static rt_err_t spacemit_pmic_reboot(struct rt_device *dev, char *cmd) +{ + struct spacemit_pmic *pmic = dev->user_data; + const struct spacemit_pmic_mfd_data *mfd_data = pmic->mfd_data; + + if (cmd) + { + for (int i = 0; i < RT_ARRAY_SIZE(config_info); ++i) + { + if (!rt_strcmp(cmd, config_info[i].cmd)) + { + spacemit_pmic_update_bits(pmic, + mfd_data->non_reset.reg, + mfd_data->non_reset.bit, + config_info[i].value); + break; + } + } + } + + if (spacemit_pmic_update_bits(pmic, + mfd_data->reboot.reg, + mfd_data->reboot.bit, + mfd_data->reboot.bit)) + { + LOG_E("Failed to %s device", "reboot"); + } + + return RT_EOK; +} + +static rt_err_t spacemit_pmic_shutdown(struct rt_device *dev) +{ + struct spacemit_pmic *pmic = dev->user_data; + const struct spacemit_pmic_mfd_data *mfd_data = pmic->mfd_data; + + if (spacemit_pmic_update_bits(pmic, + mfd_data->shutdown.reg, + mfd_data->shutdown.bit, + mfd_data->shutdown.bit)) + { + LOG_E("Failed to %s device", "shutdown"); + } + + return RT_EOK; +} + +static void spacemit_pmic_irq_mask(struct rt_pic_irq *pirq) +{ + unsigned value; + struct spacemit_pmic_irqchip *irqchip = raw_to_spacemit_pmic_irqchip(pirq->pic); + const struct spacemit_pmic_irq_map *irq_map = &irqchip->irqs_map[pirq->hwirq]; + + value = irqchip->mask_base + (irq_map->reg_offset / irqchip->reg_stride); + + spacemit_pmic_update_bits(irqchip->pmic, value, irq_map->mask, + irqchip->mask_unmask_non_inverted ? 0 : irq_map->mask); +} + +static void spacemit_pmic_irq_unmask(struct rt_pic_irq *pirq) +{ + unsigned value; + struct spacemit_pmic_irqchip *irqchip = raw_to_spacemit_pmic_irqchip(pirq->pic); + const struct spacemit_pmic_irq_map *irq_map = &irqchip->irqs_map[pirq->hwirq]; + + value = irqchip->mask_base + (irq_map->reg_offset / irqchip->reg_stride); + + spacemit_pmic_update_bits(irqchip->pmic, value, irq_map->mask, + irqchip->mask_unmask_non_inverted ? irq_map->mask : 0); +} + +static int spacemit_pmic_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 spacemit_pmic_irqchip *irqchip = raw_to_spacemit_pmic_irqchip(pic); + + irq = rt_pic_config_irq(pic, hwirq, hwirq); + pirq->mode = mode; + rt_pic_cascade(pirq, irqchip->pmic->irq); + } + + return irq; +} + +static rt_err_t spacemit_pmic_irq_parse(struct rt_pic *pic, struct rt_ofw_cell_args *args, struct rt_pic_irq *out_pirq) +{ + struct spacemit_pmic_irqchip *irqchip = raw_to_spacemit_pmic_irqchip(pic); + + if (args->args_count != 1) + { + return -RT_EINVAL; + } + + out_pirq->mode = rt_pic_irq_get_triger_mode(irqchip->pmic->irq); + out_pirq->hwirq = args->args[0]; + + return RT_EOK; +} + +const static struct rt_pic_ops spacemit_pmic_irq_ops = +{ + .name = "Spacemit-PMIC", + .irq_mask = spacemit_pmic_irq_mask, + .irq_unmask = spacemit_pmic_irq_unmask, + .irq_map = spacemit_pmic_irq_map, + .irq_parse = spacemit_pmic_irq_parse, +}; + +static void spacemit_pmic_thread_isr(void *param) +{ + rt_uint32_t *status; + rt_uint16_t base, reg; + struct rt_pic_irq *pirq; + struct spacemit_pmic *pmic; + struct spacemit_pmic_irqchip *irqchip = param; + + pmic = irqchip->pmic; + status = irqchip->irq_status; + + while (RT_TRUE) + { + rt_thread_suspend(irqchip->irq_thread); + rt_schedule(); + + base = irqchip->status_base; + rt_memset(status, 0, sizeof(*status) * irqchip->num_regs); + + for (int i = 0; i < irqchip->num_regs; ++i) + { + reg = base + i * irqchip->reg_stride; + status[i] = spacemit_pmic_read(pmic, reg); + + if ((rt_err_t)status[i] < 0) + { + LOG_E("Read IRQ status failed error = %s", rt_strerror(status[i])); + + goto _end; + } + } + + base = irqchip->ack_base; + + for (int i = 0; i < irqchip->num_regs; ++i) + { + rt_err_t err; + + reg = base + i * irqchip->reg_stride; + err = spacemit_pmic_write(pmic, reg, status[i]); + + if (err) + { + LOG_E("ACK IRQ failed error = %s", rt_strerror(err)); + + goto _end; + } + } + + for (int i = 0; i < irqchip->irqs_nr; ++i) + { + base = irqchip->irqs_map[i].reg_offset / irqchip->reg_stride; + + if (status[base] & irqchip->irqs_map[i].mask) + { + pirq = rt_pic_find_irq(&irqchip->parent, i); + + if (pirq && pirq->pic) + { + rt_pic_handle_isr(pirq); + } + } + } + _end: + rt_hw_interrupt_umask(pmic->irq); + } +} + +static void spacemit_pmic_isr(int irqno, void *param) +{ + struct spacemit_pmic_irqchip *irqchip = param; + + rt_hw_interrupt_mask(irqchip->pmic->irq); + + rt_thread_resume(irqchip->irq_thread); +} + +#define spacemit_pmic_to_i2c_client(pmic) rt_container_of((pmic)->dev, struct rt_i2c_client, parent) + +static unsigned spacemit_pmic_i2c_read(struct spacemit_pmic *pmic, unsigned reg) +{ + rt_uint8_t data = 0; + rt_uint8_t send_buf[2]; + struct rt_i2c_msg msg[2]; + struct rt_i2c_client *client = spacemit_pmic_to_i2c_client(pmic); + + send_buf[0] = (reg & 0xff); + + msg[0].addr = client->client_addr + pmic->addr_offset; + msg[0].flags = RT_I2C_WR; + msg[0].len = 1; + msg[0].buf = send_buf; + + msg[1].addr = client->client_addr + pmic->addr_offset; + 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 (unsigned)-RT_ERROR; + } +} + +static rt_err_t spacemit_pmic_i2c_write(struct spacemit_pmic *pmic, + unsigned reg, unsigned data) +{ + rt_uint8_t send_buf[2]; + struct rt_i2c_msg msg; + struct rt_i2c_client *client = spacemit_pmic_to_i2c_client(pmic); + + send_buf[0] = reg & 0xff; + send_buf[1] = data; + + msg.addr = client->client_addr + pmic->addr_offset; + 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 spacemit_pmic_i2c_write_bit(struct spacemit_pmic *pmic, + unsigned reg, unsigned mask, unsigned data) +{ + rt_uint32_t val = spacemit_pmic_i2c_read(pmic, reg); + + if ((rt_int32_t)val < 0) + { + return -RT_ERROR; + } + + if (data) + { + val |= mask; + } + else + { + val &= ~mask; + } + + return spacemit_pmic_i2c_write(pmic, reg, val); +} + +static rt_err_t spacemit_pmic_i2c_update_bits(struct spacemit_pmic *pmic, unsigned reg, + unsigned mask, unsigned data) +{ + rt_uint32_t old, tmp; + + old = spacemit_pmic_i2c_read(pmic, reg); + + if (old < 0) + { + return old; + } + + tmp = old & ~mask; + tmp |= (data & mask); + + return spacemit_pmic_i2c_write(pmic, reg, tmp); +} + +static rt_err_t create_spacemit_pmic_platform_device(rt_bus_t platform_bus, + struct spacemit_pmic *pmic, 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 = pmic; + + err = rt_bus_add_device(platform_bus, &pdev->parent); + + if (err && err != -RT_ENOSYS) + { + LOG_E("Add %s - %s error = %s", "SPACEMIT-PMIC", name, rt_strerror(err)); + } + + return err; +} + +static rt_err_t spacemit_pmic_ofw_bind_irq(struct spacemit_pmic_irqchip *irqchip, + struct rt_ofw_node *dev_np, struct rt_ofw_node *np, + const struct spacemit_pmic_mfd_endpoint *ep) +{ + /* + * ic: spacemit-pmic-interrupt-controller { + * #interrupt-cells = <1>; + * + * spacemit-pmic-endpoint0 { + * interrupts-extended = <&ic 0>; + * } + * + * spacemit-pmic-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, "spacemit-pmic-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) = &irqchip->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; +} + +static rt_err_t spacemit_pmic_i2c_probe(struct rt_i2c_client *client) +{ + rt_err_t err; + rt_bus_t platform_bus; + struct rt_ofw_node *np, *dev_np; + struct rt_device *dev = &client->parent; + struct spacemit_pmic *pmic; + struct spacemit_pmic_mfd_endpoint *pmic_ep; + const struct spacemit_pmic_mfd_data *mfd_data = rt_i2c_client_id_data(client); + + platform_bus = rt_bus_find_by_name("platform"); + + if (!platform_bus) + { + return -RT_EIO; + } + + if (!(pmic = rt_calloc(1, sizeof(*pmic)))) + { + return -RT_ENOMEM; + } + pmic->dev = dev; + pmic->mfd_data = mfd_data; + pmic->read = spacemit_pmic_i2c_read; + pmic->write = spacemit_pmic_i2c_write; + pmic->write_bit = spacemit_pmic_i2c_write_bit; + pmic->update_bits = spacemit_pmic_i2c_update_bits; + + if (mfd_data->irqchip) + { + struct spacemit_pmic_irqchip *irqchip = mfd_data->irqchip; + + if ((pmic->irq = rt_dm_dev_get_irq(dev, 0)) < 0) + { + err = pmic->irq; + goto _fail; + } + + irqchip->pmic = pmic; + irqchip->parent.priv_data = irqchip; + irqchip->parent.ops = &spacemit_pmic_irq_ops; + + if ((err = rt_pic_linear_irq(&irqchip->parent, irqchip->irqs_nr))) + { + LOG_E("Init %s IRQ chip failed error = %s", "SPACEMIT-PMIC", rt_strerror(err)); + goto _fail; + } + + /* Clear all interrupts */ + for (int i = 0; i < irqchip->num_regs; ++i) + { + spacemit_pmic_write(pmic, irqchip->ack_base + i * irqchip->reg_stride, 0xff); + } + + rt_pic_user_extends(&irqchip->parent); + + irqchip->irq_thread = rt_thread_create("spacemit-pmic", &spacemit_pmic_thread_isr, + irqchip, DM_THREAD_STACK_SIZE, RT_THREAD_PRIORITY_MAX / 2, 10); + + if (!irqchip->irq_thread) + { + rt_pic_cancel_irq(&irqchip->parent); + err = -RT_ENOMEM; + goto _fail; + } + + rt_thread_startup(irqchip->irq_thread); + + rt_hw_interrupt_install(pmic->irq, spacemit_pmic_isr, irqchip, "spacemit-pmic"); + rt_hw_interrupt_umask(pmic->irq); + } + + dev->user_data = pmic; + + if (mfd_data->shutdown.reg) + { + if (rt_dm_power_off_handler(dev, RT_DM_POWER_OFF_MODE_SHUTDOWN, + RT_DM_POWER_OFF_PRIO_PLATFORM, spacemit_pmic_shutdown)) + { + LOG_W("Add %s failed", "shutdown"); + } + } + + if (mfd_data->reboot.reg) + { + if (rt_dm_reboot_mode_register(dev, spacemit_pmic_reboot)) + { + LOG_W("Add %s failed", "reboot"); + } + } + + dev_np = dev->ofw_node; + pmic_ep = mfd_data->endpoint; + + for (int i = 0; i < mfd_data->endpoint_num; ++i, ++pmic_ep) + { + np = RT_NULL; + + if (!(np = rt_ofw_get_child_by_tag(dev_np, pmic_ep->ofw_name))) + { + if (pmic_ep->ofw_compatible) + { + np = rt_ofw_get_child_by_compatible(dev_np, pmic_ep->ofw_compatible); + } + + if (!np) + { + LOG_D("%s not found", pmic_ep->ofw_name); + + continue; + } + } + + if (!rt_ofw_node_is_available(np)) + { + continue; + } + + if (pmic_ep->irqs_nr) + { + if (!np && !(np = rt_ofw_append_child(dev_np, pmic_ep->ofw_name))) + { + continue; + } + + err = spacemit_pmic_ofw_bind_irq(mfd_data->irqchip, dev_np, np, pmic_ep); + + if (err == -RT_ENOMEM) + { + goto _out_put; + } + } + + err = create_spacemit_pmic_platform_device(platform_bus, pmic, pmic_ep->ofw_name, np); + + _out_put: + rt_ofw_node_put(np); + + if (err == -RT_ENOMEM) + { + LOG_E("No memory to create %s", pmic_ep->ofw_name); + break; + } + } + + return RT_EOK; + +_fail: + rt_free(pmic); + + return err; +} + +static const struct rt_ofw_node_id spacemit_pmic_i2c_ofw_ids[] = +{ + { .compatible = "spacemit,spm8821", .data = &spm8821_mfd_data }, + { .compatible = "spacemit,pm853", .data = &pm853_mfd_data }, + { .compatible = "spacemit,sy8810l", .data = &sy8810l_mfd_data }, + { /* sentinel */ }, +}; + +static struct rt_i2c_driver spacemit_pmic_i2c_driver = +{ + .ofw_ids = spacemit_pmic_i2c_ofw_ids, + + .probe = spacemit_pmic_i2c_probe, +}; + +static int spacemit_pmic_i2c_drv_register(void) +{ + rt_i2c_driver_register(&spacemit_pmic_i2c_driver); + + return 0; +} +INIT_SUBSYS_EXPORT(spacemit_pmic_i2c_drv_register); diff --git a/bsp/spacemit/dm/pin/Kconfig b/bsp/spacemit/dm/pin/Kconfig new file mode 100755 index 00000000000..b4a0fbd0fcf --- /dev/null +++ b/bsp/spacemit/dm/pin/Kconfig @@ -0,0 +1,5 @@ +config RT_PIN_K1X + bool "Spacemit K1X GPIO GPIO support" + depends on RT_USING_OFW + depends on RT_USING_ADT_BITMAP + default y diff --git a/bsp/spacemit/dm/pin/SConscript b/bsp/spacemit/dm/pin/SConscript new file mode 100755 index 00000000000..b91f5b9d9cc --- /dev/null +++ b/bsp/spacemit/dm/pin/SConscript @@ -0,0 +1,13 @@ +from building import * + +group = [] +src = [] +cwd = GetCurrentDir() +CPPPATH = [cwd + '/../include', cwd + '/../../../../components/drivers/pin'] + +if GetDepend(['RT_PIN_K1X']): + src += ['pin-k1x.c'] + +group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') diff --git a/bsp/spacemit/dm/pin/pin-k1x.c b/bsp/spacemit/dm/pin/pin-k1x.c new file mode 100755 index 00000000000..7b3ce46b4da --- /dev/null +++ b/bsp/spacemit/dm/pin/pin-k1x.c @@ -0,0 +1,421 @@ +/* + * Copyright (c) 2006-2024, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2024-5-1 GuEe-GUI first version + */ + +#include +#include +#include +#include + +#define DBG_TAG "pin.k1" +#define DBG_LVL DBG_LOG +#include + +#include "dev_pin_dm.h" + +#define GPLR 0x0 +#define GPDR 0xc +#define GPSR 0x18 +#define GPCR 0x24 +#define GRER 0x30 +#define GFER 0x3c +#define GEDR 0x48 +#define GSDR 0x54 +#define GCDR 0x60 +#define GSRER 0x6c +#define GCRER 0x78 +#define GSFER 0x84 +#define GCFER 0x90 +#define GAPMASK 0x9c +#define GCPMASK 0xa8 + +#define K1X_BANK_GPIO_NUMBER 32 +#define BANK_GPIO_MASK (K1X_BANK_GPIO_NUMBER - 1) + +#define k1x_gpio_to_bank_idx(gpio) ((gpio) / K1X_BANK_GPIO_NUMBER) +#define k1x_gpio_to_bank_offset(gpio) ((gpio) & BANK_GPIO_MASK) + +struct k1x_gpio_bank +{ + void *reg_bank; + rt_uint32_t irq_mask; + rt_uint32_t irq_rising_edge; + rt_uint32_t irq_falling_edge; + + RT_BITMAP_DECLARE(value, K1X_BANK_GPIO_NUMBER); +}; + +struct k1x_gpio_chip +{ + struct rt_device_pin parent; + + int irq; + void *reg_base; + + unsigned int nbank; + struct k1x_gpio_bank *banks; +}; + +#define raw_to_k1x_gpio_chip(raw) rt_container_of(raw, struct k1x_gpio_chip, parent) + +static void k1x_pin_mode(struct rt_device *device, rt_base_t pin, rt_uint8_t mode) +{ + rt_uint32_t bit, offset; + struct k1x_gpio_chip *k1x_chip = raw_to_k1x_gpio_chip(device); + struct k1x_gpio_bank *bank = &k1x_chip->banks[k1x_gpio_to_bank_idx(pin)]; + + offset = k1x_gpio_to_bank_offset(pin); + bit = RT_BIT(offset); + + switch (mode) + { + case PIN_MODE_OUTPUT: + case PIN_MODE_OUTPUT_OD: + HWREG32(bank->reg_bank + (rt_bitmap_test_bit(bank->value, offset) ? GPSR : GPCR)) = bit; + HWREG32(bank->reg_bank + GSDR) = bit; + break; + + case PIN_MODE_INPUT: + case PIN_MODE_INPUT_PULLUP: + case PIN_MODE_INPUT_PULLDOWN: + HWREG32(bank->reg_bank + GCDR) = bit; + break; + + default: + break; + } +} + +static void k1x_pin_write(struct rt_device *device, rt_base_t pin, rt_uint8_t value) +{ + rt_uint32_t bit, offset, reg; + struct k1x_gpio_chip *k1x_chip = raw_to_k1x_gpio_chip(device); + struct k1x_gpio_bank *bank = &k1x_chip->banks[k1x_gpio_to_bank_idx(pin)]; + + offset = k1x_gpio_to_bank_offset(pin); + bit = RT_BIT(offset); + + if (HWREG32(bank->reg_bank + GPDR) & bit) + { + if (value) + { + reg = GPSR; + rt_bitmap_set_bit(bank->value, offset); + } + else + { + reg = GPCR; + rt_bitmap_clear_bit(bank->value, offset); + } + + HWREG32(bank->reg_bank + reg) = bit; + } +} + +static rt_ssize_t k1x_pin_read(struct rt_device *device, rt_base_t pin) +{ + rt_uint32_t bit; + struct k1x_gpio_chip *k1x_chip = raw_to_k1x_gpio_chip(device); + struct k1x_gpio_bank *bank = &k1x_chip->banks[k1x_gpio_to_bank_idx(pin)]; + + bit = RT_BIT(k1x_gpio_to_bank_offset(pin)); + + return !!(HWREG32(bank->reg_bank + GPLR) & bit); +} + +static rt_err_t k1x_pin_irq_enable(struct rt_device *device, rt_base_t pin, rt_uint8_t enabled) +{ + rt_uint32_t bit; + struct k1x_gpio_chip *k1x_chip = raw_to_k1x_gpio_chip(device); + struct k1x_gpio_bank *bank = &k1x_chip->banks[k1x_gpio_to_bank_idx(pin)]; + + bit = RT_BIT(k1x_gpio_to_bank_offset(pin)); + + if (enabled) + { + bank->irq_mask |= bit; + + /* Set the bit of rising and falling edge detection if the gpio has. */ + HWREG32(bank->reg_bank + GSRER) = bit & bank->irq_rising_edge; + HWREG32(bank->reg_bank + GSFER) = bit & bank->irq_falling_edge; + } + else + { + bank->irq_mask &= ~bit; + + /* Clear the bit of rising and falling edge detection. */ + HWREG32(bank->reg_bank + GCRER) = bit; + HWREG32(bank->reg_bank + GCFER) = bit; + } + + return RT_EOK; +} + +static rt_err_t k1x_pin_irq_mode(struct rt_device *device, rt_base_t pin, rt_uint8_t mode) +{ + rt_uint32_t bit; + struct k1x_gpio_chip *k1x_chip = raw_to_k1x_gpio_chip(device); + struct k1x_gpio_bank *bank = &k1x_chip->banks[k1x_gpio_to_bank_idx(pin)]; + + bit = RT_BIT(k1x_gpio_to_bank_offset(pin)); + + if (mode == PIN_IRQ_MODE_RISING) + { + bank->irq_rising_edge |= bit; + HWREG32(bank->reg_bank + GSRER) = bit; + } + else + { + bank->irq_rising_edge &= ~bit; + HWREG32(bank->reg_bank + GCRER) = bit; + } + + if (mode == PIN_IRQ_MODE_FALLING) + { + bank->irq_falling_edge |= bit; + HWREG32(bank->reg_bank + GSFER) = bit; + } + else + { + bank->irq_falling_edge &= ~bit; + HWREG32(bank->reg_bank + GCFER) = bit; + } + + return RT_EOK; +} + +static rt_ssize_t k1x_pin_parse(struct rt_device *device, + struct rt_ofw_cell_args *args, rt_uint32_t *flags) +{ + rt_base_t gpio_pin; + rt_err_t err = RT_EOK; + + if (flags) + { + *flags = args->args[1]; + } + + gpio_pin = args->args[0]; + + for (int i = 0;; ++i) + { + rt_base_t gpio_start, pad_start; + struct rt_device_pin *pinctrl; + struct rt_ofw_cell_args gpio_ranges; + + if (rt_ofw_parse_phandle_cells(device->ofw_node, "gpio-ranges", + "#gpio-range-cells", i, &gpio_ranges)) + { + break; + } + + gpio_start = gpio_ranges.args[0]; + pad_start = gpio_ranges.args[1]; + + if (gpio_pin < gpio_start || gpio_pin > gpio_start + gpio_ranges.args[2]) + { + rt_ofw_node_put(gpio_ranges.data); + continue; + } + + if (!(pinctrl = rt_ofw_data(gpio_ranges.data))) + { + err = -RT_EIO; + goto _end; + } + + err = pin_gpio_request(pinctrl, pad_start + (gpio_pin - gpio_start), args->args[1]); + + _end: + rt_ofw_node_put(gpio_ranges.data); + + break; + } + + return err ? : gpio_pin; +} + +static const struct rt_pin_ops k1x_pin_ops = +{ + .pin_mode = k1x_pin_mode, + .pin_write = k1x_pin_write, + .pin_read = k1x_pin_read, + .pin_irq_enable = k1x_pin_irq_enable, + .pin_irq_mode = k1x_pin_irq_mode, + .pin_parse = k1x_pin_parse, +}; + +static void k1x_gpio_isr(int irqno, void *param) +{ + int n; + rt_uint32_t gedr; + rt_bitmap_t pending; + struct k1x_gpio_bank *bank; + struct k1x_gpio_chip *k1x_chip = param; + + for (int i = 0; i < k1x_chip->nbank; ++i) + { + bank = &k1x_chip->banks[i]; + + gedr = HWREG32(bank->reg_bank + GEDR); + if (!gedr) + { + continue; + } + + HWREG32(bank->reg_bank + GEDR) = gedr; + gedr = gedr & bank->irq_mask; + + if (!gedr) + { + continue; + } + pending = gedr; + + rt_bitmap_for_each_set_bit(&pending, n, RT_BITS_PER_LONG) + { + rt_base_t pin = (i * K1X_BANK_GPIO_NUMBER) | (n & BANK_GPIO_MASK); + + HWREG32(bank->reg_bank + GEDR) = (1 << k1x_gpio_to_bank_offset(pin)); + + pin_pic_handle_isr(&k1x_chip->parent, pin); + } + } +} + +static rt_err_t k1x_gpio_probe(struct rt_platform_device *pdev) +{ + int i = 0; + rt_err_t err; + const char *name; + struct rt_clk *clk = RT_NULL; + struct rt_device *dev = &pdev->parent; + struct rt_ofw_node *np = dev->ofw_node, *child; + struct k1x_gpio_bank *bank; + struct k1x_gpio_chip *k1x_chip = rt_calloc(1, sizeof(*k1x_chip)); + + if (!k1x_chip) + { + return -RT_ENOMEM; + } + + if (!(k1x_chip->reg_base = rt_dm_dev_iomap(dev, 0))) + { + err = -RT_EIO; + goto _fail; + } + + if ((k1x_chip->irq = rt_dm_dev_get_irq(dev, 0)) < 0) + { + err = k1x_chip->irq; + goto _fail; + } + + k1x_chip->nbank = rt_ofw_get_child_count(np); + k1x_chip->banks = rt_calloc(k1x_chip->nbank, sizeof(*k1x_chip->banks)); + + if (!k1x_chip->banks) + { + err = -RT_ENOMEM; + goto _fail; + } + + rt_ofw_foreach_child_node(np, child) + { + rt_uint32_t offset; + + if ((err = rt_ofw_prop_read_u32(child, "reg-offset", &offset))) + { + rt_ofw_node_put(child); + goto _fail; + } + + k1x_chip->banks[i++].reg_bank = k1x_chip->reg_base + offset; + } + + clk = rt_clk_get_by_index(dev, 0); + if (rt_is_err(clk)) + { + err = rt_ptr_err(clk); + goto _fail; + } + + if ((err = rt_clk_prepare_enable(clk))) + { + LOG_E("Fail to enable gpio clock"); + goto _fail; + } + + rt_dm_dev_set_name_auto(&k1x_chip->parent.parent, "gpio"); + name = rt_dm_dev_get_name(&k1x_chip->parent.parent); + + rt_hw_interrupt_install(k1x_chip->irq, k1x_gpio_isr, k1x_chip, name); + rt_hw_interrupt_umask(k1x_chip->irq); + + k1x_chip->parent.ops = &k1x_pin_ops; + k1x_chip->parent.parent.ofw_node = np; + pin_api_init(&k1x_chip->parent, k1x_chip->nbank * K1X_BANK_GPIO_NUMBER); + pin_pic_init(&k1x_chip->parent, k1x_chip->irq); + + rt_ofw_data(np) = &k1x_chip->parent; + + /* Clear all GPIO edge detects */ + for (i = 0; i < k1x_chip->nbank; ++i) + { + bank = &k1x_chip->banks[i]; + HWREG32(bank->reg_bank + GCFER) = 0xffffffff; + HWREG32(bank->reg_bank + GCRER) = 0xffffffff; + /* Unmask edge detection to AP. */ + HWREG32(bank->reg_bank + GAPMASK) = 0xffffffff; + } + + return RT_EOK; +_fail: + if (!rt_is_err_or_null(clk)) + { + rt_clk_disable_unprepare(clk); + rt_clk_put(clk); + } + + if (k1x_chip->reg_base) + { + rt_iounmap(k1x_chip->reg_base); + } + + if (k1x_chip->banks) + { + rt_free(k1x_chip->banks); + } + + rt_free(k1x_chip); + + return err; +} + +static const struct rt_ofw_node_id k1x_gpio_ofw_ids[] = +{ + { .compatible = "spacemit,k1x-gpio" }, + { /* sentinel */ } +}; + +static struct rt_platform_driver k1x_gpio_driver = +{ + .name = "k1x-gpio", + .ids = k1x_gpio_ofw_ids, + + .probe = k1x_gpio_probe, +}; + +static int k1x_gpio_register(void) +{ + rt_platform_driver_register(&k1x_gpio_driver); + + return 0; +} +INIT_SUBSYS_EXPORT(k1x_gpio_register); diff --git a/bsp/spacemit/dm/pinctrl/Kconfig b/bsp/spacemit/dm/pinctrl/Kconfig new file mode 100755 index 00000000000..4b2512a158f --- /dev/null +++ b/bsp/spacemit/dm/pinctrl/Kconfig @@ -0,0 +1,5 @@ +config RT_PINCTRL_SPACEMIT_PMIC + bool "Pinctrl and GPIO driver for Spacemit PMIC" + depends on RT_USING_PINCTRL + depends on RT_MFD_SPACEMIT_PMIC + default n diff --git a/bsp/spacemit/dm/pinctrl/SConscript b/bsp/spacemit/dm/pinctrl/SConscript new file mode 100755 index 00000000000..1f89d504400 --- /dev/null +++ b/bsp/spacemit/dm/pinctrl/SConscript @@ -0,0 +1,13 @@ +from building import * + +group = [] +src = [] +cwd = GetCurrentDir() +CPPPATH = [cwd + '/../include'] + +if GetDepend(['RT_PINCTRL_SPACEMIT_PMIC']): + src += ['spacemit-pmic-pinctrl.c'] + +group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') diff --git a/bsp/spacemit/dm/pinctrl/spacemit-pmic-pinctrl.c b/bsp/spacemit/dm/pinctrl/spacemit-pmic-pinctrl.c new file mode 100755 index 00000000000..8d54ef2c297 --- /dev/null +++ b/bsp/spacemit/dm/pinctrl/spacemit-pmic-pinctrl.c @@ -0,0 +1,364 @@ +/* + * 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.spacemit-pmic" +#define DBG_LVL DBG_INFO +#include + +#define __SPACEMIT_PINCTRL_INTERNAL +#include + +struct spacemit_pmic_pinctrl +{ + struct rt_device_pin parent; + + rt_uint32_t npins; + + struct spacemit_pmic *pmic; + const struct spacemit_pmic_pin_data *data; +}; + +#define raw_to_spacemit_pmic_pinctrl(raw) rt_container_of(raw, struct spacemit_pmic_pinctrl, parent) + +static rt_err_t spacemit_pmic_pinctrl_set_mux(struct spacemit_pmic_pinctrl *pmic_pinctrl, + rt_uint32_t function, rt_base_t pin) +{ + int i; + rt_err_t err; + const char *funcname; + struct spacemit_pmic *pmic = pmic_pinctrl->pmic; + const struct spacemit_pmic_pin_data *data = pmic_pinctrl->data; + const struct spacemit_pmic_pin_func_desc *pinfunc_desc = data->pinfunc_desc; + + funcname = data->pinmux_funcs[function]; + + for (i = 0; i < data->pin_mux_nr; ++i) + { + if (!rt_strcmp(funcname, pinfunc_desc[i].name) && pin == pinfunc_desc[i].pin_id) + { + /* Set the first */ + err = spacemit_pmic_update_bits(pmic, + pinfunc_desc[i].func_reg, + pinfunc_desc[i].func_mask, + pinfunc_desc[i].en_val << (__rt_ffs(pinfunc_desc[i].func_mask) - 1)); + if (err) + { + LOG_E("Set PIN%d, function:%s, failed", pin, funcname); + return err; + } + + /* Set the next if it have */ + if (pinfunc_desc[i].ha_sub) + { + err = spacemit_pmic_update_bits(pmic, + pinfunc_desc[i].sub_reg, + pinfunc_desc[i].sub_mask, + pinfunc_desc[i].sube_val << (__rt_ffs(pinfunc_desc[i].sub_mask) - 1)); + if (err) + { + LOG_E("Set PIN%d, function:%s, failed", pin, funcname); + return err; + } + } + + break; + } + } + + if (i >= data->pin_mux_nr) + { + LOG_E("Unsupported PIN%d, function:%s", pin, funcname); + return -RT_EINVAL; + } + + return RT_EOK; +} + +static void spacemit_pmic_pinctrl_mode(struct rt_device *device, rt_base_t pin, rt_uint8_t mode) +{ + struct spacemit_pmic_pinctrl *pmic_pinctrl = raw_to_spacemit_pmic_pinctrl(device); + + switch (mode) + { + case PIN_MODE_OUTPUT: + spacemit_pmic_pinctrl_set_mux(pmic_pinctrl, 1, pin); + break; + + case PIN_MODE_INPUT: + spacemit_pmic_pinctrl_set_mux(pmic_pinctrl, 0, pin); + break; + + default: + break; + } +} + +static void spacemit_pmic_pinctrl_write(struct rt_device *device, rt_base_t pin, + rt_uint8_t value) +{ + rt_err_t err; + const struct spacemit_pmic_pin_config_desc *pinconf_desc; + struct spacemit_pmic_pinctrl *pmic_pinctrl = raw_to_spacemit_pmic_pinctrl(device); + + pinconf_desc = pmic_pinctrl->data->pinconf_desc; + + err = spacemit_pmic_update_bits(pmic_pinctrl->pmic, + pinconf_desc[pin].output.reg, + pinconf_desc[pin].output.msk, + value ? pinconf_desc[pin].output.msk : 0); + + if (err) + { + LOG_E("Set PIN%d, val:%d, failed", pin, value); + } +} + +static rt_ssize_t spacemit_pmic_pinctrl_read(struct rt_device *device, rt_base_t pin) +{ + rt_ssize_t val; + const struct spacemit_pmic_pin_config_desc *pinconf_desc; + struct spacemit_pmic_pinctrl *pmic_pinctrl = raw_to_spacemit_pmic_pinctrl(device); + + pinconf_desc = pmic_pinctrl->data->pinconf_desc; + + if ((val = spacemit_pmic_read(pmic_pinctrl->pmic, pinconf_desc[pin].input.reg)) < 0) + { + LOG_E("Get PIN%d, direction failed", pin); + return val; + } + + val = val & pinconf_desc[pin].input.msk; + val >>= __rt_ffs(pinconf_desc[pin].input.msk) - 1; + + return val; +} + +static rt_ssize_t spacemit_pmic_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 const struct rt_pin_ctrl_conf_params spacemit_pmic_pinctrl_params[] = +{ + { "bias-disable", PIN_CONFIG_BIAS_DISABLE }, + { "bias-pull-down", PIN_CONFIG_BIAS_PULL_DOWN }, + { "bias-pull-up", PIN_CONFIG_BIAS_PULL_UP }, + { "drive-open-drain", PIN_CONFIG_DRIVE_OPEN_DRAIN }, + { "drive-push-pull", PIN_CONFIG_DRIVE_PUSH_PULL }, + { "drive-open-source", PIN_CONFIG_DRIVE_OPEN_SOURCE }, + { "input-debounce", PIN_CONFIG_INPUT_DEBOUNCE }, + { "input-schmitt-disable", PIN_CONFIG_INPUT_SCHMITT_ENABLE }, + { "input-schmitt-enable", PIN_CONFIG_INPUT_SCHMITT_ENABLE }, + { "output-high", PIN_CONFIG_OUTPUT }, + { "output-low", PIN_CONFIG_OUTPUT }, +}; + +static rt_bool_t spacemit_pmic_pinctrl_param_parse(const char *name, rt_uint32_t *out_param) +{ + for (int i = 0; i < RT_ARRAY_SIZE(spacemit_pmic_pinctrl_params); ++i) + { + if (!rt_strcmp(spacemit_pmic_pinctrl_params[i].propname, "name")) + { + *out_param = spacemit_pmic_pinctrl_params[i].param; + return RT_TRUE; + } + } + + return RT_FALSE; +} + +static rt_err_t spacemit_pmic_pinctrl_confs_apply(struct rt_device *device, void *fw_conf_np) +{ + rt_err_t err; + rt_ubase_t pin; + rt_uint32_t function, param; + const char *string; + struct rt_ofw_prop *pins_prop, *prop; + struct spacemit_pmic_pinctrl *pmic_pinctrl = raw_to_spacemit_pmic_pinctrl(device); + const struct spacemit_pmic_pin_data *data = pmic_pinctrl->data; + const struct spacemit_pmic_pin_config_desc *pinconf_desc = data->pinconf_desc; + struct spacemit_pmic *pmic = pmic_pinctrl->pmic; + + /** + * led_pins: led-pins { + * pins = "PIN3"; + * function = "sleep"; + * bias-disable = <0>; + * drive-open-drain = <0x1>; + * }; + */ + + if ((err = rt_ofw_prop_read_string(fw_conf_np, "function", &string))) + { + return err; + } + + function = data->pin_mux_nr; + + for (int i = 0; i < data->pin_mux_nr; ++i) + { + if (!rt_strcmp(data->pinmux_funcs[i], string)) + { + function = i; + } + } + + if (function >= data->pin_mux_nr) + { + return -RT_EINVAL; + } + + rt_ofw_foreach_prop_string(fw_conf_np, "pins", pins_prop, string) + { + pin = 0; + string += sizeof("PIN") - 1; + + while (*string) + { + pin *= 10; + pin += *string - '0'; + + ++string; + } + + if (spacemit_pmic_pinctrl_set_mux(pmic_pinctrl, function, pin)) + { + return err; + } + + rt_ofw_foreach_prop(((struct rt_ofw_node *)fw_conf_np), prop) + { + rt_uint32_t reg, msk; + + if (!rt_strcmp(prop->name, "pins") || !rt_strcmp(prop->name, "function")) + { + continue; + } + + if (!spacemit_pmic_pinctrl_param_parse(prop->name, ¶m)) + { + return -RT_ENOSYS; + } + + switch (param) + { + case PIN_CONFIG_BIAS_DISABLE: + case PIN_CONFIG_BIAS_PULL_DOWN: + case PIN_CONFIG_BIAS_PULL_UP: + reg = pinconf_desc[pin].pup.reg; + msk = pinconf_desc[pin].pup.msk; + break; + case PIN_CONFIG_DRIVE_OPEN_DRAIN: + case PIN_CONFIG_DRIVE_PUSH_PULL: + case PIN_CONFIG_DRIVE_OPEN_SOURCE: + reg = pinconf_desc[pin].od.reg; + msk = pinconf_desc[pin].od.msk; + break; + case PIN_CONFIG_INPUT_DEBOUNCE: + reg = pinconf_desc[pin].deb.reg; + msk = pinconf_desc[pin].deb.timemsk; + break; + case PIN_CONFIG_INPUT_SCHMITT_ENABLE: + reg = pinconf_desc[pin].deb.reg; + msk = pinconf_desc[pin].deb.en.msk; + break; + case PIN_CONFIG_OUTPUT: + reg = pinconf_desc[pin].output.reg; + msk = pinconf_desc[pin].output.msk; + break; + default: + LOG_E("To fixed your code"); + RT_ASSERT(0); + break; + } + + err = spacemit_pmic_update_bits(pmic, reg, msk, + fdt32_to_cpu(*(fdt32_t *)prop->value) << (__rt_ffs(msk) - 1)); + if (err) + { + LOG_E("Set reg:%x, msk:%x failed", reg, msk); + return -RT_EINVAL; + } + } + } + + return RT_EOK; +} + +static const struct rt_pin_ops spacemit_pmic_pinctrl_ops = +{ + .pin_mode = spacemit_pmic_pinctrl_mode, + .pin_write = spacemit_pmic_pinctrl_write, + .pin_read = spacemit_pmic_pinctrl_read, + .pin_parse = spacemit_pmic_pin_parse, + .pin_ctrl_confs_apply = spacemit_pmic_pinctrl_confs_apply, +}; + +static rt_err_t spacemit_pmic_pinctrl_probe(struct rt_platform_device *pdev) +{ + rt_err_t err; + struct rt_device *dev = &pdev->parent; + struct spacemit_pmic *pmic = pdev->priv; + struct spacemit_pmic_pinctrl *pmic_pinctrl = rt_calloc(1, sizeof(*pmic_pinctrl)); + + if (!pmic_pinctrl) + { + return -RT_ENOMEM; + } + + pmic_pinctrl->pmic = pmic; + pmic_pinctrl->data = pdev->id->data; + + if ((err = rt_dm_dev_prop_read_u32(dev, "spacemit,npins", &pmic_pinctrl->npins))) + { + goto _fail; + } + + pmic_pinctrl->parent.ops = &spacemit_pmic_pinctrl_ops; + rt_ofw_data(dev->ofw_node) = &pmic_pinctrl->parent; + + return RT_EOK; + +_fail: + rt_free(pmic_pinctrl); + + return err; +} + +static const struct rt_ofw_node_id spacemit_pmic_pinctrl_ofw_ids[] = +{ + { .compatible = "pmic,pinctrl,spm8821", .data = &spm8821_pinctrl_data }, + { /* sentinel */ } +}; + +static struct rt_platform_driver spacemit_pmic_pinctrl_driver = +{ + .name = "spacemit-pmic-pinctrl", + .ids = spacemit_pmic_pinctrl_ofw_ids, + + .probe = spacemit_pmic_pinctrl_probe, +}; + +static int spacemit_pmic_pinctrl_register(void) +{ + rt_platform_driver_register(&spacemit_pmic_pinctrl_driver); + + return 0; +} +INIT_SUBSYS_EXPORT(spacemit_pmic_pinctrl_register); diff --git a/bsp/spacemit/dm/pmdomain/Kconfig b/bsp/spacemit/dm/pmdomain/Kconfig new file mode 100755 index 00000000000..fc0b03ed886 --- /dev/null +++ b/bsp/spacemit/dm/pmdomain/Kconfig @@ -0,0 +1,6 @@ +config RT_PMDOMAIN_K1X + bool "Spacemit k1x" + depends on RT_MFD_SYSCON + depends on RT_USING_CLK + depends on RT_USING_REGULATOR + default y diff --git a/bsp/spacemit/dm/pmdomain/SConscript b/bsp/spacemit/dm/pmdomain/SConscript new file mode 100755 index 00000000000..13765f24355 --- /dev/null +++ b/bsp/spacemit/dm/pmdomain/SConscript @@ -0,0 +1,12 @@ +from building import * + +cwd = GetCurrentDir() +src = [] +CPPPATH = [cwd + '/../include'] + +if GetDepend(['RT_PMDOMAIN_K1X']): + src += ['pm-domain-k1x.c'] + +group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') diff --git a/bsp/spacemit/dm/pmdomain/pm-domain-k1x.c b/bsp/spacemit/dm/pmdomain/pm-domain-k1x.c new file mode 100755 index 00000000000..b6ae6a0a67c --- /dev/null +++ b/bsp/spacemit/dm/pmdomain/pm-domain-k1x.c @@ -0,0 +1,582 @@ +/* + * Copyright (c) 2006-2024, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2024-5-1 GuEe-GUI first version + */ + +#include +#include +#include + +#include +#include + +#define DBG_TAG "pm-domain.spacemit.k1x" +#define DBG_LVL DBG_INFO +#include + +#define MAX_REGULATOR_PER_DOMAIN 5 + +#define APMU_POWER_STATUS_REG 0xf0 +#define MPMU_AWUCRM_REG 0x104c + +struct spacemit_pmu; + +struct spacemit_pm_domain_param +{ + rt_uint32_t reg_pwr_ctrl; + rt_uint32_t pm_qos; + rt_uint32_t bit_hw_mode; + rt_uint32_t bit_sleep2; + rt_uint32_t bit_sleep1; + rt_uint32_t bit_isolation; + rt_uint32_t bit_auto_pwr_on; + rt_uint32_t bit_hw_pwr_stat; + rt_uint32_t bit_pwr_stat; + rt_uint32_t use_hw; +}; + +struct spacemit_pm_domain +{ + struct rt_dm_power_domain parent; + + int index; + int supply_count; + int ref_handle_pm_domain; + struct spacemit_pmu *pmu; + struct rt_regulator *supply[MAX_REGULATOR_PER_DOMAIN]; + + struct spacemit_pm_domain_param param; +}; + +#define raw_to_spacemit_pm_domain(raw) rt_container_of(raw, struct spacemit_pm_domain, parent) + +struct spacemit_pmu +{ + struct rt_dm_power_domain_proxy parent; + + struct rt_syscon *mpmu_regmap; + struct rt_syscon *apmu_regmap; + + rt_uint32_t num_domains; + struct spacemit_pm_domain *domains; +}; + +#define raw_to_spacemit_pmu(raw) rt_container_of(raw, struct spacemit_pmu, parent) + +static rt_err_t spacemit_pd_power_on(struct rt_dm_power_domain *domain) +{ + int loop; + rt_err_t err; + rt_uint32_t val; + struct spacemit_pmu *pmu; + struct spacemit_pm_domain *spd = raw_to_spacemit_pm_domain(domain); + + if (spd->param.reg_pwr_ctrl == 0) + { + return RT_EOK; + } + + if (spd->ref_handle_pm_domain > 0) + { + return RT_EOK; + } + + /* Enable the supply */ + for (int loop = 0; loop < spd->supply_count; ++loop) + { + if ((err = rt_regulator_enable(spd->supply[loop])) && spd->supply[loop]) + { + return err; + } + } + + pmu = spd->pmu; + + rt_syscon_read(pmu->apmu_regmap, APMU_POWER_STATUS_REG, &val); + + if (val & (1 << spd->param.bit_pwr_stat)) + { + if (spd->index == K1X_PMU_LCD_PWR_DOMAIN) + { + return RT_EOK; + } + + if (!spd->param.use_hw) + { + /* This is the sw type */ + rt_syscon_read(pmu->apmu_regmap, spd->param.reg_pwr_ctrl, &val); + val &= ~(1 << spd->param.bit_isolation); + rt_syscon_write(pmu->apmu_regmap, spd->param.reg_pwr_ctrl, val); + + rt_hw_us_delay(12); + + /* MCU power off */ + rt_syscon_read(pmu->apmu_regmap, spd->param.reg_pwr_ctrl, &val); + val &= ~((1 << spd->param.bit_sleep1) | (1 << spd->param.bit_sleep2)); + rt_syscon_write(pmu->apmu_regmap, spd->param.reg_pwr_ctrl, val); + + rt_hw_us_delay(12); + + for (loop = 10000; loop >= 0; --loop) + { + rt_syscon_read(pmu->apmu_regmap, APMU_POWER_STATUS_REG, &val); + + if ((val & (1 << spd->param.bit_pwr_stat)) == 0) + { + break; + } + + rt_hw_us_delay(5); + } + } + else + { + /* LCD */ + rt_syscon_read(pmu->apmu_regmap, spd->param.reg_pwr_ctrl, &val); + val &= ~(1 << spd->param.bit_auto_pwr_on); + val &= ~(1 << spd->param.bit_hw_mode); + rt_syscon_write(pmu->apmu_regmap, spd->param.reg_pwr_ctrl, val); + + rt_hw_us_delay(20); + + for (loop = 10000; loop >= 0; --loop) + { + rt_syscon_read(pmu->apmu_regmap, APMU_POWER_STATUS_REG, &val); + + if ((val & (1 << spd->param.bit_hw_pwr_stat)) == 0) + { + break; + } + + rt_hw_us_delay(5); + } + } + + if (loop < 0) + { + LOG_E("Power on Domain(%d) fail", spd->index); + return -RT_EBUSY; + } + } + + if (!spd->param.use_hw) + { + /* mcu power on */ + rt_syscon_read(pmu->apmu_regmap, spd->param.reg_pwr_ctrl, &val); + val |= (1 << spd->param.bit_sleep1); + rt_syscon_write(pmu->apmu_regmap, spd->param.reg_pwr_ctrl, val); + + rt_hw_us_delay(22); + + rt_syscon_read(pmu->apmu_regmap, spd->param.reg_pwr_ctrl, &val); + val |= (1 << spd->param.bit_sleep2) | (1 << spd->param.bit_sleep1); + rt_syscon_write(pmu->apmu_regmap, spd->param.reg_pwr_ctrl, val); + + rt_hw_us_delay(22); + + /* disable isolation */ + rt_syscon_read(pmu->apmu_regmap, spd->param.reg_pwr_ctrl, &val); + val |= (1 << spd->param.bit_isolation); + rt_syscon_write(pmu->apmu_regmap, spd->param.reg_pwr_ctrl, val); + + rt_hw_us_delay(12); + + for (loop = 10000; loop >= 0; --loop) + { + rt_syscon_read(pmu->apmu_regmap, APMU_POWER_STATUS_REG, &val); + + if (val & (1 << spd->param.bit_pwr_stat)) + { + break; + } + + rt_hw_us_delay(5); + } + } + else + { + /* LCD */ + rt_syscon_read(pmu->apmu_regmap, spd->param.reg_pwr_ctrl, &val); + val |= (1 << spd->param.bit_auto_pwr_on); + val |= (1 << spd->param.bit_hw_mode); + rt_syscon_write(pmu->apmu_regmap, spd->param.reg_pwr_ctrl, val); + + rt_hw_us_delay(300); + + for (loop = 10000; loop >= 0; --loop) + { + rt_syscon_read(pmu->apmu_regmap, APMU_POWER_STATUS_REG, &val); + + if (val & (1 << spd->param.bit_hw_pwr_stat)) + { + break; + } + + rt_hw_us_delay(5); + } + } + + if (loop < 0) + { + LOG_E("Power on Domain(%d) fail", spd->index); + return -RT_EBUSY; + } + + return RT_EOK; +} + +static rt_err_t spacemit_pd_power_off(struct rt_dm_power_domain *domain) +{ + int loop; + rt_err_t err; + rt_uint32_t val; + struct spacemit_pmu *pmu; + struct spacemit_pm_domain *spd = raw_to_spacemit_pm_domain(domain); + + if (spd->param.reg_pwr_ctrl == 0) + { + return RT_EOK; + } + + if (spd->ref_handle_pm_domain > 0) + { + return RT_EOK; + } + + pmu = spd->pmu; + + if (!spd->param.use_hw) + { + /* this is the sw type */ + rt_syscon_read(pmu->apmu_regmap, spd->param.reg_pwr_ctrl, &val); + val &= ~(1 << spd->param.bit_isolation); + rt_syscon_write(pmu->apmu_regmap, spd->param.reg_pwr_ctrl, val); + + rt_hw_us_delay(12); + + /* mcu power off */ + rt_syscon_read(pmu->apmu_regmap, spd->param.reg_pwr_ctrl, &val); + val &= ~((1 << spd->param.bit_sleep1) | (1 << spd->param.bit_sleep2)); + rt_syscon_write(pmu->apmu_regmap, spd->param.reg_pwr_ctrl, val); + + rt_hw_us_delay(12); + + for (loop = 10000; loop >= 0; --loop) + { + rt_syscon_read(pmu->apmu_regmap, APMU_POWER_STATUS_REG, &val); + + if ((val & (1 << spd->param.bit_pwr_stat)) == 0) + { + break; + } + + rt_hw_us_delay(5); + } + } + else + { + /* LCD */ + rt_syscon_read(pmu->apmu_regmap, spd->param.reg_pwr_ctrl, &val); + val &= ~(1 << spd->param.bit_auto_pwr_on); + val &= ~(1 << spd->param.bit_hw_mode); + rt_syscon_write(pmu->apmu_regmap, spd->param.reg_pwr_ctrl, val); + + rt_hw_us_delay(20); + + for (loop = 10000; loop >= 0; --loop) + { + rt_syscon_read(pmu->apmu_regmap, APMU_POWER_STATUS_REG, &val); + + if ((val & (1 << spd->param.bit_hw_pwr_stat)) == 0) + { + break; + } + + rt_hw_us_delay(5); + } + } + + if (loop < 0) + { + LOG_E("Powero ff Domain(%d) fail", spd->index); + return -RT_EBUSY; + } + + /* enable the supply */ + for (loop = 0; loop < spd->supply_count; ++loop) + { + if ((err = rt_regulator_disable(spd->supply[loop])) && spd->supply[loop]) + { + return err; + } + } + + return RT_EOK; +} + +static rt_err_t spacemit_pd_attach_dev(struct rt_dm_power_domain *domain, + struct rt_device *dev) +{ + struct spacemit_pm_domain *spd = raw_to_spacemit_pm_domain(domain); + + if (rt_ofw_prop_read_bool(dev->ofw_node, "pwr-domain,pm-runtime,no-sleep")) + { + ++spd->ref_handle_pm_domain; + } + + return RT_EOK; +} + +static rt_err_t spacemit_pd_detach_dev(struct rt_dm_power_domain *domain, + struct rt_device *dev) +{ + struct spacemit_pm_domain *spd = raw_to_spacemit_pm_domain(domain); + + if (rt_ofw_prop_read_bool(dev->ofw_node, "pwr-domain,pm-runtime,no-sleep")) + { + --spd->ref_handle_pm_domain; + } + + return RT_EOK; +} + +static struct rt_dm_power_domain *spacemit_pmdomain_proxy_ofw_parse( + struct rt_dm_power_domain_proxy *proxy, struct rt_ofw_cell_args *args) +{ + struct spacemit_pmu *pmu = raw_to_spacemit_pmu(proxy); + + return &pmu->domains[args->args[0]].parent; +} + +static rt_err_t spacemit_pm_add_one_domain(struct spacemit_pmu *pmu, + struct rt_ofw_node *np) +{ + rt_err_t err; + rt_uint32_t idx; + struct rt_device dev; + struct spacemit_pm_domain *spd; + struct rt_dm_power_domain *domain; + const char *strings[MAX_REGULATOR_PER_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; + } + + spd = &pmu->domains[idx]; + spd->index = idx; + + if ((err = rt_ofw_prop_read_u32(np, "pm_qos", &spd->param.pm_qos))) + { + return -RT_EINVAL; + } + + rt_ofw_prop_read_u32(np, "reg_pwr_ctrl", &spd->param.reg_pwr_ctrl); + rt_ofw_prop_read_u32(np, "bit_hw_mode", &spd->param.bit_hw_mode); + rt_ofw_prop_read_u32(np, "bit_sleep2", &spd->param.bit_sleep2); + rt_ofw_prop_read_u32(np, "bit_sleep1", &spd->param.bit_sleep1); + rt_ofw_prop_read_u32(np, "bit_isolation", &spd->param.bit_isolation); + rt_ofw_prop_read_u32(np, "bit_auto_pwr_on", &spd->param.bit_auto_pwr_on); + rt_ofw_prop_read_u32(np, "bit_hw_pwr_stat", &spd->param.bit_hw_pwr_stat); + rt_ofw_prop_read_u32(np, "bit_pwr_stat", &spd->param.bit_pwr_stat); + rt_ofw_prop_read_u32(np, "use_hw", &spd->param.use_hw); + + spd->supply_count = rt_ofw_prop_read_string_array_index(np, "vin-supply-names", + 0, RT_ARRAY_SIZE(strings), strings); + + if (spd->supply_count > 0) + { + dev.ofw_node = np; + + for (int i = 0; i < spd->supply_count; ++i) + { + spd->supply[i] = rt_regulator_get(&dev, strings[i]); + + if (rt_is_err(spd->supply[i])) + { + LOG_E("Regulator supply %s get failed", strings[i]); + return rt_ptr_err(spd->supply[i]); + } + } + } + else + { + spd->supply_count = 0; + } + + spd->pmu = pmu; + + domain = &spd->parent; + domain->power_on = spacemit_pd_power_on; + domain->power_off = spacemit_pd_power_off; + domain->attach_dev = spacemit_pd_attach_dev; + domain->detach_dev = spacemit_pd_detach_dev; + + if ((err = rt_dm_power_domain_register(domain))) + { + return err; + } + + rt_ofw_data(np) = domain; + + return RT_EOK; +} + +static rt_err_t spacemit_pm_add_subdomain(struct spacemit_pmu *pmu, + struct rt_ofw_node *parent) +{ + rt_err_t err = RT_EOK; + rt_uint32_t idx; + struct rt_ofw_node *np; + struct spacemit_pm_domain *child_spd, *parent_spd; + + rt_ofw_foreach_child_node(parent, np) + { + 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(parent), rt_strerror(err)); + goto _err; + } + + parent_spd = &pmu->domains[idx]; + + if ((err = spacemit_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_spd = &pmu->domains[idx]; + + if ((err = rt_dm_power_domain_register_child(&parent_spd->parent, + &child_spd->parent))) + { + LOG_E("Failed to handle subdomain node %s error = %s", + rt_ofw_node_full_name(np), rt_strerror(err)); + } + + spacemit_pm_add_subdomain(pmu, np); + } + +_err: + rt_ofw_node_put(np); + + return err; +} + +static rt_err_t spacemit_pmdomain_probe(struct rt_platform_device *pdev) +{ + rt_err_t err; + struct rt_device *dev = &pdev->parent; + struct rt_ofw_node *np = dev->ofw_node, *child; + struct spacemit_pmu *pmu = rt_calloc(1, sizeof(*pmu)); + + if (!pmu) + { + return -RT_ENOMEM; + } + + pmu->mpmu_regmap = rt_syscon_find_by_ofw_compatible("spacemit,spacemit-mpmu"); + pmu->apmu_regmap = rt_syscon_find_by_ofw_compatible("spacemit,spacemit-apmu"); + + if (!pmu->mpmu_regmap || !pmu->apmu_regmap) + { + err = -RT_EINVAL; + goto _fail; + } + + if (rt_dm_dev_prop_read_u32(dev, "domains", &pmu->num_domains)) + { + err = -RT_EINVAL; + goto _fail; + } + + if (!(pmu->domains = rt_calloc(pmu->num_domains, sizeof(*pmu->domains)))) + { + err = -RT_EINVAL; + goto _fail; + } + + rt_ofw_foreach_available_child_node(np, child) + { + if ((err = spacemit_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 _fail; + } + + if ((err = spacemit_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 _fail; + } + } + + pmu->parent.ofw_parse = spacemit_pmdomain_proxy_ofw_parse; + rt_dm_power_domain_proxy_ofw_bind(&pmu->parent, np); + + return RT_EOK; + +_fail: + if (pmu->domains) + { + for (int i = 0; i < pmu->num_domains; ++i) + { + struct spacemit_pm_domain *domain = &pmu->domains[i]; + + if (domain) + { + rt_dm_power_domain_unregister(&domain->parent); + } + } + + rt_free(pmu->domains); + } + rt_free(pmu); + + return err; +} + +static const struct rt_ofw_node_id spacemit_pmdomain_ofw_ids[] = +{ + { .compatible = "spacemit,power-controller", }, + { /* sentinel */ } +}; + +static struct rt_platform_driver spacemit_pmdomain_driver = +{ + .name = "spacemit-pmdomain", + .ids = spacemit_pmdomain_ofw_ids, + + .probe = spacemit_pmdomain_probe, +}; + +static int spacemit_pmdomain_drv_register(void) +{ + rt_platform_driver_register(&spacemit_pmdomain_driver); + + return 0; +} +INIT_SUBSYS_EXPORT(spacemit_pmdomain_drv_register); diff --git a/bsp/spacemit/dm/power/SConscript b/bsp/spacemit/dm/power/SConscript new file mode 100755 index 00000000000..0b84689e91c --- /dev/null +++ b/bsp/spacemit/dm/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/bsp/spacemit/dm/power/reset/Kconfig b/bsp/spacemit/dm/power/reset/Kconfig new file mode 100755 index 00000000000..d6d5e69849f --- /dev/null +++ b/bsp/spacemit/dm/power/reset/Kconfig @@ -0,0 +1,2 @@ +config RT_POWER_RESET_REBOOT_SPACEMIT + bool "Spacemit reboot" diff --git a/bsp/spacemit/dm/power/reset/SConscript b/bsp/spacemit/dm/power/reset/SConscript new file mode 100755 index 00000000000..943fda9cbaf --- /dev/null +++ b/bsp/spacemit/dm/power/reset/SConscript @@ -0,0 +1,12 @@ +from building import * + +cwd = GetCurrentDir() +src = [] +CPPPATH = [cwd + '/../../include'] + +if GetDepend(['RT_POWER_RESET_REBOOT_SPACEMIT']): + src += ['reboot-spacemit.c'] + +group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') diff --git a/bsp/spacemit/dm/power/reset/reboot-spacemit.c b/bsp/spacemit/dm/power/reset/reboot-spacemit.c new file mode 100755 index 00000000000..3fe266be065 --- /dev/null +++ b/bsp/spacemit/dm/power/reset/reboot-spacemit.c @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2006-2024, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2024-5-1 GuEe-GUI first version + */ + +#include +#include + +#define DBG_TAG "reset.reboot.spacemit" +#define DBG_LVL DBG_INFO +#include + +struct spacemit_reboot +{ + void *regs; +}; + +static rt_err_t spacemit_notify_reboot(struct rt_device *dev, char *cmd) +{ + struct spacemit_reboot *reboot = dev->user_data; + + if (cmd) + { + if (!rt_strcmp(cmd, "fastboot")) + { + HWREG32(reboot->regs) = 0x55a; + } + else if (!rt_strcmp(cmd, "uboot")) + { + HWREG32(reboot->regs) = 0x55f; + } + } + + return RT_EOK; +} + +static rt_err_t spacemit_reboot_probe(struct rt_platform_device *pdev) +{ + rt_err_t err; + struct rt_device *dev = &pdev->parent; + struct spacemit_reboot *reboot = rt_calloc(1, sizeof(*reboot)); + + if (!reboot) + { + return -RT_ENOMEM; + } + + if (!(reboot->regs = rt_dm_dev_iomap(dev, 0))) + { + err = -RT_ENOMEM; + goto _fail; + } + + if ((err = rt_dm_reboot_mode_register(dev, spacemit_notify_reboot))) + { + goto _fail; + } + + dev->user_data = reboot; + + return RT_EOK; + +_fail: + if (reboot->regs) + { + rt_iounmap(reboot->regs); + } + + rt_free(reboot); + + return err; +} + +static rt_err_t spacemit_reboot_remove(struct rt_platform_device *pdev) +{ + struct spacemit_reboot *reboot = pdev->parent.user_data; + + rt_dm_reboot_mode_unregister(&pdev->parent); + + rt_iounmap(reboot->regs); + + rt_free(reboot); + + return RT_EOK; +} + +static const struct rt_ofw_node_id spacemit_reboot_ofw_ids[] = +{ + { .compatible = "spacemit,k1x-reboot", }, + { /* sentinel */ } +}; + +static struct rt_platform_driver spacemit_reboot_driver = +{ + .name = "spacemit-reboot", + .ids = spacemit_reboot_ofw_ids, + + .probe = spacemit_reboot_probe, + .remove = spacemit_reboot_remove, +}; + +static int spacemit_reboot_drv_register(void) +{ + rt_platform_driver_register(&spacemit_reboot_driver); + + return 0; +} +INIT_SUBSYS_EXPORT(spacemit_reboot_drv_register); diff --git a/bsp/spacemit/dm/pwm/Kconfig b/bsp/spacemit/dm/pwm/Kconfig new file mode 100755 index 00000000000..152c82d8a29 --- /dev/null +++ b/bsp/spacemit/dm/pwm/Kconfig @@ -0,0 +1,4 @@ +config RT_PWM_PXA + bool "PXA PWM support" + depends on RT_USING_PWM + default n diff --git a/bsp/spacemit/dm/pwm/SConscript b/bsp/spacemit/dm/pwm/SConscript new file mode 100755 index 00000000000..e9e880eae3e --- /dev/null +++ b/bsp/spacemit/dm/pwm/SConscript @@ -0,0 +1,14 @@ +from building import * + +group = [] +cwd = GetCurrentDir() +CPPPATH = [cwd + '/../include'] + +src = [] + +if GetDepend(['RT_PWM_PXA']): + src += ['pwm-pxa.c'] + +group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') diff --git a/bsp/spacemit/dm/pwm/pwm-pxa.c b/bsp/spacemit/dm/pwm/pwm-pxa.c new file mode 100755 index 00000000000..4c9a0201847 --- /dev/null +++ b/bsp/spacemit/dm/pwm/pwm-pxa.c @@ -0,0 +1,331 @@ +/* + * Copyright (c) 2006-2024, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2024-5-1 GuEe-GUI first version + */ + +#include +#include + +#define DBG_TAG "pwm.pxa" +#define DBG_LVL DBG_INFO +#include + +#define PWMCR 0x00 +#define PWMDCR 0x04 +#define PWMPCR 0x08 + +#define PWMCR_SD RT_BIT(6) +#define PWMDCR_FD RT_BIT(10) + +struct pxa_pwm +{ + struct rt_device_pwm parent; + + void *regs; + struct rt_clk *clk; + struct rt_reset_control *rstc; + + int dcr_fd; /* Controller PWM_DCR FD feature */ + int rcpu_pwm; /* PWM in rcpu domain */ + rt_bool_t enabled; + +#define QUIRK_HAS_SECONDARY_PWM 0 +#define QUIRK_HAS_PWM_DCR_FD 1 + rt_bitmap_t quirks; +}; + +#define raw_to_pxa_pwm(raw) rt_container_of(raw, struct pxa_pwm, parent) + +static rt_err_t pxa_pwm_toggle(struct pxa_pwm *pc, rt_bool_t enabled) +{ + rt_err_t err = RT_EOK; + + if (pc->enabled == enabled) + { + return err; + } + + if (pc->enabled) + { + rt_clk_disable_unprepare(pc->clk); + } + else + { + err = rt_clk_prepare_enable(pc->clk); + } + + pc->enabled = enabled; + + return err; +} + +static rt_err_t pxa_pwm_apply(struct pxa_pwm *pc, struct rt_pwm_configuration *pwm_cfg) +{ + rt_err_t err; + rt_bool_t enabled; + rt_uint32_t offset; + rt_uint64_t clk, duty_ns, period_ns; + rt_ubase_t period_cycles, prescale, pv, dc; + + if (pwm_cfg->complementary) + { + return -RT_EINVAL; + } + + enabled = pc->enabled; + + if ((err = pxa_pwm_toggle(pc, RT_FALSE))) + { + return err; + } + + /* + * period_ns = 10^9 * (PRESCALE + 1) * (PV + 1) / PWM_CLK_RATE + * duty_ns = 10^9 * (PRESCALE + 1) * DC / PWM_CLK_RATE + */ + duty_ns = enabled ? pwm_cfg->pulse : 0; + period_ns = pwm_cfg->period; + offset = pwm_cfg->channel ? 0x10 : 0; + + clk = rt_clk_get_rate(pc->clk); + clk *= period_ns; + rt_do_div(clk, 1000000000); + period_cycles = clk; + + if (period_cycles < 1) + { + period_cycles = 1; + } + prescale = (period_cycles - 1) / 1024; + pv = period_cycles / (prescale + 1) - 1; + + if (prescale > 63) + { + err = -RT_EINVAL; + goto _end; + } + + if (duty_ns == period_ns) + { + if (pc->quirks & RT_BIT(QUIRK_HAS_PWM_DCR_FD)) + { + if (pc->dcr_fd) + { + dc = PWMDCR_FD; + } + else + { + dc = (pv + 1) * duty_ns / period_ns; + + if (dc >= PWMDCR_FD) + { + dc = PWMDCR_FD - 1; + pv = dc - 1; + } + } + } + else + { + dc = PWMDCR_FD; + } + } + else + { + dc = ((rt_uint64_t)(pv + 1) * duty_ns) / period_ns; + } + + if (pc->quirks & RT_BIT(QUIRK_HAS_PWM_DCR_FD)) + { + prescale |= PWMCR_SD; + } + + HWREG32(pc->regs + offset + PWMCR) = prescale | PWMCR_SD; + HWREG32(pc->regs + offset + PWMDCR) = dc; + HWREG32(pc->regs + offset + PWMPCR) = pv; + +_end: + if (pc->enabled != enabled) + { + err = pxa_pwm_toggle(pc, RT_TRUE); + } + + return err; +} + +static rt_err_t pxa_pwm_control(struct rt_device_pwm *pwm, int cmd, void *args) +{ + rt_err_t err; + struct pxa_pwm *pc = raw_to_pxa_pwm(pwm); + struct rt_pwm_configuration *pwm_cfg = args; + + if (pwm_cfg->channel > 0) + { + if (pwm_cfg->channel > 1 || !pc->quirks & RT_BIT(QUIRK_HAS_SECONDARY_PWM)) + { + return -RT_EINVAL; + } + } + + /* RT_PWM framework have check args */ + switch (cmd) + { + case PWM_CMD_ENABLE: + case PWM_CMD_DISABLE: + err = pxa_pwm_toggle(pc, cmd == PWM_CMD_ENABLE); + break; + + case PWM_CMD_SET: + case PWM_CMD_SET_PERIOD: + case PWM_CMD_SET_PULSE: + err = pxa_pwm_apply(pc, pwm_cfg); + break; + + case PWM_CMD_GET: + err = -RT_ENOSYS; + break; + + default: + err = -RT_EINVAL; + break; + } + + return err; +} + +const static struct rt_pwm_ops pxa_pwm_ops = +{ + .control = pxa_pwm_control, +}; + +static void pxa_pwm_free(struct pxa_pwm *pc) +{ + if (pc->regs) + { + rt_iounmap(pc->regs); + } + + if (rt_is_err_or_null(pc->clk)) + { + rt_clk_put(pc->clk); + } + + if (rt_is_err_or_null(pc->rstc)) + { + rt_reset_control_assert(pc->rstc); + rt_reset_control_put(pc->rstc); + } + + rt_free(pc); +} + +static rt_err_t pxa_pwm_probe(struct rt_platform_device *pdev) +{ + int id; + rt_err_t err; + struct rt_device *dev = &pdev->parent; + struct pxa_pwm *pc = rt_calloc(1, sizeof(*pc)); + + if (!pc) + { + return -RT_ENOMEM; + } + + pc->quirks = pdev->id->data ? *(rt_ubase_t *)pdev->id->data : 0; + + if (pc->quirks & RT_BIT(QUIRK_HAS_PWM_DCR_FD)) + { + pc->dcr_fd = rt_dm_dev_prop_read_bool(dev, "k1x,pwm-disable-fd") ? 0 : 1; + pc->dcr_fd = rt_dm_dev_prop_read_bool(dev, "rcpu-pwm") ? 1 : 0; + } + + pc->regs = rt_dm_dev_iomap(dev, 0); + if (!pc->regs) + { + err = -RT_EIO; + goto _fail; + } + + pc->clk = rt_clk_get_by_index(dev, 0); + if (rt_is_err(pc->clk)) + { + err = rt_ptr_err(pc->clk); + goto _fail; + } + + pc->rstc = rt_reset_control_get_by_index(dev, 0); + if (rt_is_err(pc->rstc)) + { + err = rt_ptr_err(pc->rstc); + goto _fail; + } + rt_reset_control_deassert(pc->rstc); + + dev->user_data = pc; + + pc->parent.parent.ofw_node = dev->ofw_node; + + if ((id = pdev->dev_id) >= 0) + { + rt_dm_dev_set_name(&pc->parent.parent, "pwm%u", id); + } + else + { + rt_dm_dev_set_name_auto(&pc->parent.parent, "pwm"); + } + + rt_device_pwm_register(&pc->parent, + rt_dm_dev_get_name(&pc->parent.parent), &pxa_pwm_ops, pc); + + rt_dm_dev_bind_fwdata(dev, RT_NULL, pc); + + return RT_EOK; + +_fail: + pxa_pwm_free(pc); + + return err; +} + +static rt_err_t pxa_pwm_remove(struct rt_platform_device *pdev) +{ + struct rt_device *dev = &pdev->parent; + struct pxa_pwm *pc = dev->user_data; + + pxa_pwm_toggle(pc, RT_FALSE); + + rt_dm_dev_unbind_fwdata(dev, RT_NULL); + + rt_device_unregister(&pc->parent.parent); + + pxa_pwm_free(pc); + + return RT_EOK; +} + +static rt_bitmap_t has_secondary_pwm_quirk = RT_BIT(QUIRK_HAS_SECONDARY_PWM); +static rt_bitmap_t has_pwm_dcr_fd_quirk = RT_BIT(QUIRK_HAS_PWM_DCR_FD); + +static const struct rt_ofw_node_id pxa_pwm_ofw_ids[] = +{ + { .compatible = "marvell,pxa250-pwm", }, + { .compatible = "marvell,pxa270-pwm", .data = &has_secondary_pwm_quirk }, + { .compatible = "marvell,pxa168-pwm", }, + { .compatible = "marvell,pxa910-pwm", }, + { .compatible = "spacemit,k1x-pwm", .data = &has_pwm_dcr_fd_quirk }, + { /* sentinel */ } +}; + +static struct rt_platform_driver pxa_pwm_driver = +{ + .name = "pxa-pwm", + .ids = pxa_pwm_ofw_ids, + + .probe = pxa_pwm_probe, + .remove = pxa_pwm_remove, +}; +RT_PLATFORM_DRIVER_EXPORT(pxa_pwm_driver); diff --git a/bsp/spacemit/dm/regulator/Kconfig b/bsp/spacemit/dm/regulator/Kconfig new file mode 100755 index 00000000000..ad93c981864 --- /dev/null +++ b/bsp/spacemit/dm/regulator/Kconfig @@ -0,0 +1,5 @@ +config RT_REGULATOR_SPACEMIT_PMIC + bool "Spacemit PMIC Power regulators" + depends on RT_MFD_SPACEMIT_PMIC + depends on RT_USING_PIN + default y diff --git a/bsp/spacemit/dm/regulator/SConscript b/bsp/spacemit/dm/regulator/SConscript new file mode 100755 index 00000000000..48bce18d883 --- /dev/null +++ b/bsp/spacemit/dm/regulator/SConscript @@ -0,0 +1,12 @@ +from building import * + +cwd = GetCurrentDir() +src = [] +CPPPATH = [cwd + '/../include', cwd + '/../../../../components/drivers/regulator'] + +if GetDepend(['RT_REGULATOR_SPACEMIT_PMIC']): + src += ['regulator-spacemit-pmic.c'] + +group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') diff --git a/bsp/spacemit/dm/regulator/regulator-spacemit-pmic.c b/bsp/spacemit/dm/regulator/regulator-spacemit-pmic.c new file mode 100755 index 00000000000..731c083da10 --- /dev/null +++ b/bsp/spacemit/dm/regulator/regulator-spacemit-pmic.c @@ -0,0 +1,413 @@ +/* + * Copyright (c) 2006-2024, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2024-5-1 GuEe-GUI first version + */ + +#define DBG_TAG "regulator.spacemit-pmic" +#define DBG_LVL DBG_INFO +#include + +#include "regulator_dm.h" + +#define __SPACEMIT_REGULATOR_INTERNAL +static const struct rt_regulator_ops pmic_dcdc_ldo_ops, pmic_switch_ops; + +#include + +struct spacemit_pmic_regulator +{ + struct rt_regulator_node parent; + struct rt_regulator_param param; + + struct rt_device device; + struct spacemit_pmic *pmic; + struct rt_ofw_node *ofw_node; + + const struct spacemit_pmic_regulator_desc *desc; +}; + +#define raw_to_spacemit_pmic_regulator(raw) rt_container_of(raw, struct spacemit_pmic_regulator, parent) + +static int regulator_voltage_from_selector(struct spacemit_pmic_regulator *pmic_reg, + int selector) +{ + int uvolt = -1; + const struct spacemit_pmic_regulator_desc *desc = pmic_reg->desc; + + if (desc->ranges_nr) + { + const struct spacemit_pmic_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 spacemit_pmic_regulator *pmic_reg, + int min_uvolt, int max_uvolt) +{ + int selector = 0, max, uvolt, i; + const struct spacemit_pmic_regulator_range *range; + const struct spacemit_pmic_regulator_desc *desc = pmic_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(pmic_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 spacemit_pmic_regulator *pmic_reg, + int min_uvolt, int max_uvolt) +{ + int selector, uvolt; + + /* Allow uvolt_step to be 0 for fixed voltage */ + if (pmic_reg->desc->voltages_nr == 1 && pmic_reg->desc->uvolt_step == 0) + { + if (min_uvolt <= pmic_reg->desc->uvolt_min && pmic_reg->desc->uvolt_min <= max_uvolt) + { + return 0; + } + else + { + return -RT_EINVAL; + } + } + + if (!pmic_reg->desc->uvolt_step) + { + return -RT_EINVAL; + } + + if (min_uvolt < pmic_reg->desc->uvolt_min) + { + min_uvolt = pmic_reg->desc->uvolt_min; + } + + selector = RT_DIV_ROUND_UP(min_uvolt - pmic_reg->desc->uvolt_min, pmic_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(pmic_reg, selector); + + if (uvolt < min_uvolt || uvolt > max_uvolt) + { + return -RT_EINVAL; + } + + return selector; +} + +static rt_err_t spacemit_pmic_regulator_enable(struct rt_regulator_node *reg_np) +{ + rt_uint32_t val; + struct spacemit_pmic_regulator *pmic_reg = raw_to_spacemit_pmic_regulator(reg_np); + + val = pmic_reg->desc->enable_val; + + if (!val) + { + val = pmic_reg->desc->enable_mask; + } + + return spacemit_pmic_update_bits(pmic_reg->pmic, pmic_reg->desc->enable_reg, + pmic_reg->desc->enable_mask, val); +} + +static rt_err_t spacemit_pmic_regulator_disable(struct rt_regulator_node *reg_np) +{ + rt_uint32_t val; + struct spacemit_pmic_regulator *pmic_reg = raw_to_spacemit_pmic_regulator(reg_np); + + val = pmic_reg->desc->disable_val; + + return spacemit_pmic_update_bits(pmic_reg->pmic, pmic_reg->desc->enable_reg, + pmic_reg->desc->enable_mask, val); +} + +static rt_bool_t spacemit_pmic_regulator_is_enabled(struct rt_regulator_node *reg_np) +{ + rt_uint32_t val; + struct spacemit_pmic_regulator *pmic_reg = raw_to_spacemit_pmic_regulator(reg_np); + + val = spacemit_pmic_read(pmic_reg->pmic, pmic_reg->desc->enable_reg); + + if ((rt_err_t)val < 0) + { + return RT_FALSE; + } + + val &= pmic_reg->desc->enable_mask; + + if (pmic_reg->desc->enable_val) + { + return val == pmic_reg->desc->enable_val; + } + + return val != 0; +} + +static rt_err_t spacemit_pmic_regulator_set_voltage(struct rt_regulator_node *reg_np, + int min_uvolt, int max_uvolt) +{ + int selector; + struct spacemit_pmic_regulator *pmic_reg = raw_to_spacemit_pmic_regulator(reg_np); + + selector = regulator_voltage_to_selector(pmic_reg, min_uvolt, max_uvolt); + + if (selector < 0) + { + if (selector == -RT_EINVAL) + { + selector = regulator_voltage_to_selector_range(pmic_reg, + min_uvolt, max_uvolt); + + if (selector < 0) + { + return -RT_EINVAL; + } + } + } + + selector <<= __rt_ffs(pmic_reg->desc->vsel_mask) - 1; + + return spacemit_pmic_update_bits(pmic_reg->pmic, pmic_reg->desc->vsel_reg, + pmic_reg->desc->vsel_mask, selector); +} + +static int spacemit_pmic_regulator_get_voltage(struct rt_regulator_node *reg_np) +{ + int uvolt; + rt_uint32_t val; + struct spacemit_pmic_regulator *pmic_reg = raw_to_spacemit_pmic_regulator(reg_np); + + val = spacemit_pmic_read(pmic_reg->pmic, pmic_reg->desc->vsel_reg); + + if ((rt_err_t)val < 0) + { + return val; + } + + val &= pmic_reg->desc->vsel_mask; + val >>= __rt_ffs(pmic_reg->desc->vsel_mask) - 1; + + uvolt = regulator_voltage_from_selector(pmic_reg, val); + + return uvolt < 0 ? -RT_EINVAL : uvolt; +} + +static const struct rt_regulator_ops pmic_dcdc_ldo_ops = +{ + .enable = spacemit_pmic_regulator_enable, + .disable = spacemit_pmic_regulator_disable, + .is_enabled = spacemit_pmic_regulator_is_enabled, + .set_voltage = spacemit_pmic_regulator_set_voltage, + .get_voltage = spacemit_pmic_regulator_get_voltage, +}; + +static const struct rt_regulator_ops pmic_switch_ops = +{ + .enable = spacemit_pmic_regulator_enable, + .disable = spacemit_pmic_regulator_disable, + .is_enabled = spacemit_pmic_regulator_is_enabled, +}; + +static rt_err_t append_spacemit_pmic_regulator(struct spacemit_pmic *pmic, + struct rt_ofw_node *np, const struct spacemit_pmic_regulator_desc *desc, int id) +{ + rt_err_t err = RT_EOK; + struct rt_regulator_node *rgp; + struct spacemit_pmic_regulator *pmic_reg; + + pmic_reg = rt_calloc(1, sizeof(*pmic_reg)); + + if (!pmic_reg) + { + return -RT_ENOMEM; + } + + pmic_reg->pmic = pmic; + pmic_reg->desc = &desc[id]; + + regulator_ofw_parse(np, &pmic_reg->param); + + rgp = &pmic_reg->parent; + rgp->supply_name = pmic_reg->param.name; + rgp->ops = pmic_reg->desc->ops; + rgp->param = &pmic_reg->param; + rgp->dev = &pmic_reg->device; + + rgp->dev->ofw_node = np; + + if ((err = rt_regulator_register(rgp))) + { + goto _fail; + } + + return RT_EOK; + +_fail: + rt_free(pmic_reg); + + return err; +} + +static rt_err_t spacemit_pmic_regulator_probe(struct rt_platform_device *pdev) +{ + rt_err_t err; + struct rt_ofw_node *np, *regulators_np = pdev->parent.ofw_node; + struct spacemit_pmic *pmic = pdev->priv, *pmic_sub; + const struct spacemit_pmic_regulator_data *data = pdev->id->data; + struct spacemit_pmic_regulator *pmic_reg = rt_calloc(1, sizeof(*pmic_reg)); + + if (!pmic_reg) + { + return -RT_ENOMEM; + } + + if (rt_ofw_node_is_compatible(regulators_np, "pmic,regulator,pm853")) + { + if (!(pmic_sub = rt_calloc(1, sizeof(*pmic_sub)))) + { + err = -RT_ENOMEM; + goto _fail; + } + + rt_memcpy(pmic_sub, pmic, sizeof(*pmic)); + pmic_sub->addr_offset = 1; + + pmic_reg->pmic = pmic_sub; + } + else + { + pmic_reg->pmic = pmic; + } + + for (int i = 0; i < data->desc_nr; ++i) + { + const char *name = data->desc[i].name; + + if (!(np = rt_ofw_get_child_by_tag(regulators_np, name))) + { + continue; + } + + rt_ofw_node_put(np); + + if ((err = append_spacemit_pmic_regulator(pmic, np, data->desc, i))) + { + LOG_E("Append spacemit PMIC regulator %s fail error = %s", + rt_ofw_node_full_name(np), rt_strerror(err)); + + if (err == -RT_ENOMEM) + { + return err; + } + } + } + + return RT_EOK; +_fail: + rt_free(pmic_reg); + + return err; +} + +static const struct rt_ofw_node_id spacemit_pmic_regulator_ofw_ids[] = +{ + { .compatible = "pmic,regulator,spm8821", .data = &spm8821_regulator_data }, + { .compatible = "pmic,regulator,pm853", .data = &pm853_regulator_data }, + { .compatible = "pmic,regulator,sy8810l", .data = &sy8810l_regulator_data }, + { /* sentinel */ } +}; + +static struct rt_platform_driver spacemit_pmic_regulator_driver = +{ + .name = "spacemit-pmic-regulator", + .ids = spacemit_pmic_regulator_ofw_ids, + + .probe = spacemit_pmic_regulator_probe, +}; + +static int spacemit_pmic_regulator_register(void) +{ + rt_platform_driver_register(&spacemit_pmic_regulator_driver); + + return 0; +} +INIT_SUBSYS_EXPORT(spacemit_pmic_regulator_register); diff --git a/bsp/spacemit/dm/reset/Kconfig b/bsp/spacemit/dm/reset/Kconfig new file mode 100755 index 00000000000..b0b81e00898 --- /dev/null +++ b/bsp/spacemit/dm/reset/Kconfig @@ -0,0 +1,4 @@ +config RT_RESET_SPACEMIT_K1 + bool "Reset controller driver for Spacemit K1" + depends on RT_USING_RESET + default y diff --git a/bsp/spacemit/dm/reset/SConscript b/bsp/spacemit/dm/reset/SConscript new file mode 100755 index 00000000000..3994acfe37b --- /dev/null +++ b/bsp/spacemit/dm/reset/SConscript @@ -0,0 +1,13 @@ +from building import * + +group = [] +src = [] +cwd = GetCurrentDir() +CPPPATH = [cwd + '/../include'] + +if GetDepend(['RT_RESET_SPACEMIT_K1']): + src += ['reset-spacemit-k1x.c'] + +group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') diff --git a/bsp/spacemit/dm/reset/reset-spacemit-k1x.c b/bsp/spacemit/dm/reset/reset-spacemit-k1x.c new file mode 100755 index 00000000000..909d170387d --- /dev/null +++ b/bsp/spacemit/dm/reset/reset-spacemit-k1x.c @@ -0,0 +1,333 @@ +/* + * Copyright (c) 2006-2024, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2024-5-1 GuEe-GUI first version + */ + +#include +#include + +#define DBG_TAG "reset.spacemit.k1x" +#define DBG_LVL DBG_INFO +#include + +#include +#include + +struct spacemit_reset_signal +{ + rt_uint32_t offset; + rt_uint32_t mask; + rt_uint32_t deassert_val; + rt_uint32_t assert_val; + enum cru_type type; +}; + +struct spacemit_reset +{ + struct rt_reset_controller parent; + + union + { + struct + { + void *mpmu_base; + void *apmu_base; + void *apbc_base; + void *apbs_base; + void *ciu_base; + void *dciu_base; + void *ddrc_base; + void *apbc2_base; + void *rcpu_base; + void *rcpu2_base; + void *audpmu_base; + void *audio_ctrl_base; + }; + void *mmio[CRU_BASE_TYPE_MAX]; + }; + + rt_uint32_t signals_nr; + const struct spacemit_reset_signal *signals; +}; + +#define RESET_SIGNAL(name, offset, mask, deassert_val, assert_val, type) \ +[RESET_##name] = \ +{ \ + offset, mask, deassert_val, assert_val, CRU_BASE_TYPE_##type, \ +} \ + +static const struct spacemit_reset_signal k1x_reset_signals[RESET_NUMBER] = +{ + RESET_SIGNAL(UART1, APBC_UART1_CLK_RST, RT_BIT(2), 0, RT_BIT(2), APBC), + RESET_SIGNAL(UART2, APBC_UART2_CLK_RST, RT_BIT(2), 0, RT_BIT(2), APBC), + RESET_SIGNAL(GPIO, APBC_GPIO_CLK_RST, RT_BIT(2), 0, RT_BIT(2), APBC), + RESET_SIGNAL(PWM0, APBC_PWM0_CLK_RST, RT_BIT(2) | RT_BIT(0), RT_BIT(0), RT_BIT(2), APBC), + RESET_SIGNAL(PWM1, APBC_PWM1_CLK_RST, RT_BIT(2) | RT_BIT(0), RT_BIT(0), RT_BIT(2), APBC), + RESET_SIGNAL(PWM2, APBC_PWM2_CLK_RST, RT_BIT(2) | RT_BIT(0), RT_BIT(0), RT_BIT(2), APBC), + RESET_SIGNAL(PWM3, APBC_PWM3_CLK_RST, RT_BIT(2) | RT_BIT(0), RT_BIT(0), RT_BIT(2), APBC), + RESET_SIGNAL(PWM4, APBC_PWM4_CLK_RST, RT_BIT(2) | RT_BIT(0), RT_BIT(0), RT_BIT(2), APBC), + RESET_SIGNAL(PWM5, APBC_PWM5_CLK_RST, RT_BIT(2) | RT_BIT(0), RT_BIT(0), RT_BIT(2), APBC), + RESET_SIGNAL(PWM6, APBC_PWM6_CLK_RST, RT_BIT(2) | RT_BIT(0), RT_BIT(0), RT_BIT(2), APBC), + RESET_SIGNAL(PWM7, APBC_PWM7_CLK_RST, RT_BIT(2) | RT_BIT(0), RT_BIT(0), RT_BIT(2), APBC), + RESET_SIGNAL(PWM8, APBC_PWM8_CLK_RST, RT_BIT(2) | RT_BIT(0), RT_BIT(0), RT_BIT(2), APBC), + RESET_SIGNAL(PWM9, APBC_PWM9_CLK_RST, RT_BIT(2) | RT_BIT(0), RT_BIT(0), RT_BIT(2), APBC), + RESET_SIGNAL(PWM10, APBC_PWM10_CLK_RST, RT_BIT(2) | RT_BIT(0), RT_BIT(0), RT_BIT(2), APBC), + RESET_SIGNAL(PWM11, APBC_PWM11_CLK_RST, RT_BIT(2) | RT_BIT(0), RT_BIT(0), RT_BIT(2), APBC), + RESET_SIGNAL(PWM12, APBC_PWM12_CLK_RST, RT_BIT(2) | RT_BIT(0), RT_BIT(0), RT_BIT(2), APBC), + RESET_SIGNAL(PWM13, APBC_PWM13_CLK_RST, RT_BIT(2) | RT_BIT(0), RT_BIT(0), RT_BIT(2), APBC), + RESET_SIGNAL(PWM14, APBC_PWM14_CLK_RST, RT_BIT(2) | RT_BIT(0), RT_BIT(0), RT_BIT(2), APBC), + RESET_SIGNAL(PWM15, APBC_PWM15_CLK_RST, RT_BIT(2) | RT_BIT(0), RT_BIT(0), RT_BIT(2), APBC), + RESET_SIGNAL(PWM16, APBC_PWM16_CLK_RST, RT_BIT(2) | RT_BIT(0), RT_BIT(0), RT_BIT(2), APBC), + RESET_SIGNAL(PWM17, APBC_PWM17_CLK_RST, RT_BIT(2) | RT_BIT(0), RT_BIT(0), RT_BIT(2), APBC), + RESET_SIGNAL(PWM18, APBC_PWM18_CLK_RST, RT_BIT(2) | RT_BIT(0), RT_BIT(0), RT_BIT(2), APBC), + RESET_SIGNAL(PWM19, APBC_PWM19_CLK_RST, RT_BIT(2) | RT_BIT(0), RT_BIT(0), RT_BIT(2), APBC), + RESET_SIGNAL(SSP3, APBC_SSP3_CLK_RST, RT_BIT(2), 0, RT_BIT(2), APBC), + RESET_SIGNAL(UART3, APBC_UART3_CLK_RST, RT_BIT(2), 0, RT_BIT(2), APBC), + RESET_SIGNAL(RTC, APBC_RTC_CLK_RST, RT_BIT(2), 0, RT_BIT(2), APBC), + RESET_SIGNAL(TWSI0, APBC_TWSI0_CLK_RST, RT_BIT(2), 0, RT_BIT(2), APBC), + RESET_SIGNAL(TIMERS1, APBC_TIMERS1_CLK_RST, RT_BIT(2), 0, RT_BIT(2), APBC), + RESET_SIGNAL(AIB, APBC_AIB_CLK_RST, RT_BIT(2), 0, RT_BIT(2), APBC), + RESET_SIGNAL(TIMERS2, APBC_TIMERS2_CLK_RST, RT_BIT(2), 0, RT_BIT(2), APBC), + RESET_SIGNAL(ONEWIRE, APBC_ONEWIRE_CLK_RST, RT_BIT(2), 0, RT_BIT(2), APBC), + RESET_SIGNAL(SSPA0, APBC_SSPA0_CLK_RST, RT_BIT(2), 0, RT_BIT(2), APBC), + RESET_SIGNAL(SSPA1, APBC_SSPA1_CLK_RST, RT_BIT(2), 0, RT_BIT(2), APBC), + RESET_SIGNAL(DRO, APBC_DRO_CLK_RST, RT_BIT(2), 0, RT_BIT(2), APBC), + RESET_SIGNAL(IR, APBC_IR_CLK_RST, RT_BIT(2), 0, RT_BIT(2), APBC), + RESET_SIGNAL(TWSI1, APBC_TWSI1_CLK_RST, RT_BIT(2), 0, RT_BIT(2), APBC), + RESET_SIGNAL(TSEN, APBC_TSEN_CLK_RST, RT_BIT(2), 0, RT_BIT(2), APBC), + RESET_SIGNAL(TWSI2, APBC_TWSI2_CLK_RST, RT_BIT(2), 0, RT_BIT(2), APBC), + RESET_SIGNAL(TWSI4, APBC_TWSI4_CLK_RST, RT_BIT(2), 0, RT_BIT(2), APBC), + RESET_SIGNAL(TWSI5, APBC_TWSI5_CLK_RST, RT_BIT(2), 0, RT_BIT(2), APBC), + RESET_SIGNAL(TWSI6, APBC_TWSI6_CLK_RST, RT_BIT(2), 0, RT_BIT(2), APBC), + RESET_SIGNAL(TWSI7, APBC_TWSI7_CLK_RST, RT_BIT(2), 0, RT_BIT(2), APBC), + RESET_SIGNAL(TWSI8, APBC_TWSI8_CLK_RST, RT_BIT(2), 0, RT_BIT(2), APBC), + RESET_SIGNAL(IPC_AP2AUD, APBC_IPC_AP2AUD_CLK_RST, RT_BIT(2), 0, RT_BIT(2), APBC), + RESET_SIGNAL(UART4, APBC_UART4_CLK_RST, RT_BIT(2), 0, RT_BIT(2), APBC), + RESET_SIGNAL(UART5, APBC_UART5_CLK_RST, RT_BIT(2), 0, RT_BIT(2), APBC), + RESET_SIGNAL(UART6, APBC_UART6_CLK_RST, RT_BIT(2), 0, RT_BIT(2), APBC), + RESET_SIGNAL(UART7, APBC_UART7_CLK_RST, RT_BIT(2), 0, RT_BIT(2), APBC), + RESET_SIGNAL(UART8, APBC_UART8_CLK_RST, RT_BIT(2), 0, RT_BIT(2), APBC), + RESET_SIGNAL(UART9, APBC_UART9_CLK_RST, RT_BIT(2), 0, RT_BIT(2), APBC), + RESET_SIGNAL(CAN0, APBC_CAN0_CLK_RST, RT_BIT(2), 0, RT_BIT(2), APBC), + /* MPMU */ + RESET_SIGNAL(WDT, MPMU_WDTPCR, RT_BIT(2), 0, RT_BIT(2), MPMU), + /* APMU */ + RESET_SIGNAL(JPG, APMU_JPG_CLK_RES_CTRL, RT_BIT(0), RT_BIT(0), 0, APMU), + RESET_SIGNAL(CSI, APMU_CSI_CCIC2_CLK_RES_CTRL, RT_BIT(1), RT_BIT(1), 0, APMU), + RESET_SIGNAL(CCIC2_PHY, APMU_CSI_CCIC2_CLK_RES_CTRL, RT_BIT(2), RT_BIT(2), 0, APMU), + RESET_SIGNAL(CCIC3_PHY, APMU_CSI_CCIC2_CLK_RES_CTRL, RT_BIT(29), RT_BIT(29), 0, APMU), + RESET_SIGNAL(ISP, APMU_ISP_CLK_RES_CTRL, RT_BIT(0), RT_BIT(0), 0, APMU), + RESET_SIGNAL(ISP_AHB, APMU_ISP_CLK_RES_CTRL, RT_BIT(3), RT_BIT(3), 0, APMU), + RESET_SIGNAL(ISP_CI, APMU_ISP_CLK_RES_CTRL, RT_BIT(16), RT_BIT(16), 0, APMU), + RESET_SIGNAL(ISP_CPP, APMU_ISP_CLK_RES_CTRL, RT_BIT(27), RT_BIT(27), 0, APMU), + RESET_SIGNAL(LCD, APMU_LCD_CLK_RES_CTRL1, RT_BIT(4), RT_BIT(4), 0, APMU), + RESET_SIGNAL(DSI_ESC, APMU_LCD_CLK_RES_CTRL1, RT_BIT(3), RT_BIT(3), 0, APMU), + RESET_SIGNAL(V2D, APMU_LCD_CLK_RES_CTRL1, RT_BIT(27), RT_BIT(27), 0, APMU), + RESET_SIGNAL(MIPI, APMU_LCD_CLK_RES_CTRL1, RT_BIT(15), RT_BIT(15), 0, APMU), + RESET_SIGNAL(LCD_SPI, APMU_LCD_SPI_CLK_RES_CTRL, RT_BIT(0), RT_BIT(0), 0, APMU), + RESET_SIGNAL(LCD_SPI_BUS, APMU_LCD_SPI_CLK_RES_CTRL, RT_BIT(4), RT_BIT(4), 0, APMU), + RESET_SIGNAL(LCD_SPI_HBUS, APMU_LCD_SPI_CLK_RES_CTRL, RT_BIT(2), RT_BIT(2), 0, APMU), + RESET_SIGNAL(LCD_MCLK, APMU_LCD_CLK_RES_CTRL2, RT_BIT(9), RT_BIT(9), 0, APMU), + RESET_SIGNAL(CCIC_4X, APMU_CCIC_CLK_RES_CTRL, RT_BIT(1), RT_BIT(1), 0, APMU), + RESET_SIGNAL(CCIC1_PHY, APMU_CCIC_CLK_RES_CTRL, RT_BIT(2), RT_BIT(2), 0, APMU), + RESET_SIGNAL(SDH_AXI, APMU_SDH0_CLK_RES_CTRL, RT_BIT(0), RT_BIT(0), 0, APMU), + RESET_SIGNAL(SDH0, APMU_SDH0_CLK_RES_CTRL, RT_BIT(1), RT_BIT(1), 0, APMU), + RESET_SIGNAL(SDH1, APMU_SDH1_CLK_RES_CTRL, RT_BIT(1), RT_BIT(1), 0, APMU), + RESET_SIGNAL(USB_AXI, APMU_USB_CLK_RES_CTRL, RT_BIT(0), RT_BIT(0), 0, APMU), + RESET_SIGNAL(USBP1_AXI, APMU_USB_CLK_RES_CTRL, RT_BIT(4), RT_BIT(4), 0, APMU), + RESET_SIGNAL(COMBO_PHY, APMU_PCIE_CLK_RES_CTRL_0, RT_BIT(8), 0, RT_BIT(8), APMU), + RESET_SIGNAL(USB3_0, APMU_USB_CLK_RES_CTRL, RT_BIT(9) | RT_BIT(10) | RT_BIT(11), RT_BIT(9) | RT_BIT(10) | RT_BIT(11), 0, APMU), + RESET_SIGNAL(QSPI, APMU_QSPI_CLK_RES_CTRL, RT_BIT(1), RT_BIT(1), 0, APMU), + RESET_SIGNAL(QSPI_BUS, APMU_QSPI_CLK_RES_CTRL, RT_BIT(0), RT_BIT(0), 0, APMU), + RESET_SIGNAL(DMA, APMU_DMA_CLK_RES_CTRL, RT_BIT(0), RT_BIT(0), 0, APMU), + RESET_SIGNAL(AES, APMU_AES_CLK_RES_CTRL, RT_BIT(4), RT_BIT(4), 0, APMU), + RESET_SIGNAL(VPU, APMU_VPU_CLK_RES_CTRL, RT_BIT(0), RT_BIT(0), 0, APMU), + RESET_SIGNAL(GPU, APMU_GPU_CLK_RES_CTRL, RT_BIT(1), RT_BIT(1), 0, APMU), + RESET_SIGNAL(SDH2, APMU_SDH2_CLK_RES_CTRL, RT_BIT(1), RT_BIT(1), 0, APMU), + RESET_SIGNAL(MC, APMU_PMUA_MC_CTRL, RT_BIT(0), RT_BIT(0), 0, APMU), + RESET_SIGNAL(EM_AXI, APMU_PMUA_EM_CLK_RES_CTRL, RT_BIT(0), RT_BIT(0), 0, APMU), + RESET_SIGNAL(EM, APMU_PMUA_EM_CLK_RES_CTRL, RT_BIT(1), RT_BIT(1), 0, APMU), + RESET_SIGNAL(AUDIO_SYS, APMU_AUDIO_CLK_RES_CTRL, RT_BIT(0) | RT_BIT(2) | RT_BIT(3), RT_BIT(0) | RT_BIT(2) | RT_BIT(3), 0, APMU), + RESET_SIGNAL(HDMI, APMU_HDMI_CLK_RES_CTRL, RT_BIT(9), RT_BIT(9), 0, APMU), + RESET_SIGNAL(PCIE0, APMU_PCIE_CLK_RES_CTRL_0, RT_BIT(3) | RT_BIT(4) | RT_BIT(5) | RT_BIT(8), RT_BIT(3) | RT_BIT(4) | RT_BIT(5), RT_BIT(8), APMU), + RESET_SIGNAL(PCIE1, APMU_PCIE_CLK_RES_CTRL_1, RT_BIT(3) | RT_BIT(4) | RT_BIT(5) | RT_BIT(8), RT_BIT(3) | RT_BIT(4) | RT_BIT(5), RT_BIT(8), APMU), + RESET_SIGNAL(PCIE2, APMU_PCIE_CLK_RES_CTRL_2, 0x138, 0x38, 0x100, APMU), + RESET_SIGNAL(EMAC0, APMU_EMAC0_CLK_RES_CTRL, RT_BIT(1), RT_BIT(1), 0, APMU), + RESET_SIGNAL(EMAC1, APMU_EMAC1_CLK_RES_CTRL, RT_BIT(1), RT_BIT(1), 0, APMU), + /* APBC2 */ + RESET_SIGNAL(SEC_UART1, APBC2_UART1_CLK_RST, RT_BIT(2), 0, RT_BIT(2), APBC2), + RESET_SIGNAL(SEC_SSP2, APBC2_SSP2_CLK_RST, RT_BIT(2), 0, RT_BIT(2), APBC2), + RESET_SIGNAL(SEC_TWSI3, APBC2_TWSI3_CLK_RST, RT_BIT(2), 0, RT_BIT(2), APBC2), + RESET_SIGNAL(SEC_RTC, APBC2_RTC_CLK_RST, RT_BIT(2), 0, RT_BIT(2), APBC2), + RESET_SIGNAL(SEC_TIMERS0, APBC2_TIMERS0_CLK_RST, RT_BIT(2), 0, RT_BIT(2), APBC2), + RESET_SIGNAL(SEC_KPC, APBC2_KPC_CLK_RST, RT_BIT(2), 0, RT_BIT(2), APBC2), + RESET_SIGNAL(SEC_GPIO, APBC2_GPIO_CLK_RST, RT_BIT(2), 0, RT_BIT(2), APBC2), + /* RCPU */ + RESET_SIGNAL(RCPU_HDMIAUDIO, RCPU_HDMI_CLK_RST, RT_BIT(0), RT_BIT(0), 0, RCPU), + RESET_SIGNAL(RCPU_CAN, RCPU_CAN_CLK_RST, RT_BIT(0), RT_BIT(0), 0, RCPU), + /* RCPU */ + RESET_SIGNAL(RCPU_I2C0, RCPU_I2C0_CLK_RST, RT_BIT(0), RT_BIT(0), 0, RCPU), + RESET_SIGNAL(RCPU_SSP0, RCPU_SSP0_CLK_RST, RT_BIT(0), RT_BIT(0), 0, RCPU), + RESET_SIGNAL(RCPU_IR, RCPU_IR_CLK_RST, RT_BIT(0), RT_BIT(0), 0, RCPU), + RESET_SIGNAL(RCPU_UART0, RCPU_UART0_CLK_RST, RT_BIT(0), RT_BIT(0), 0, RCPU), + RESET_SIGNAL(RCPU_UART1, RCPU_UART1_CLK_RST, RT_BIT(0), RT_BIT(0), 0, RCPU), + /* RCPU2 */ + RESET_SIGNAL(RCPU2_PWM0, RCPU2_PWM0_CLK_RST, RT_BIT(2) | RT_BIT(0), RT_BIT(0), RT_BIT(2), RCPU2), + RESET_SIGNAL(RCPU2_PWM1, RCPU2_PWM1_CLK_RST, RT_BIT(2) | RT_BIT(0), RT_BIT(0), RT_BIT(2), RCPU2), + RESET_SIGNAL(RCPU2_PWM2, RCPU2_PWM2_CLK_RST, RT_BIT(2) | RT_BIT(0), RT_BIT(0), RT_BIT(2), RCPU2), + RESET_SIGNAL(RCPU2_PWM3, RCPU2_PWM3_CLK_RST, RT_BIT(2) | RT_BIT(0), RT_BIT(0), RT_BIT(2), RCPU2), + RESET_SIGNAL(RCPU2_PWM4, RCPU2_PWM4_CLK_RST, RT_BIT(2) | RT_BIT(0), RT_BIT(0), RT_BIT(2), RCPU2), + RESET_SIGNAL(RCPU2_PWM5, RCPU2_PWM5_CLK_RST, RT_BIT(2) | RT_BIT(0), RT_BIT(0), RT_BIT(2), RCPU2), + RESET_SIGNAL(RCPU2_PWM6, RCPU2_PWM6_CLK_RST, RT_BIT(2) | RT_BIT(0), RT_BIT(0), RT_BIT(2), RCPU2), + RESET_SIGNAL(RCPU2_PWM7, RCPU2_PWM7_CLK_RST, RT_BIT(2) | RT_BIT(0), RT_BIT(0), RT_BIT(2), RCPU2), + RESET_SIGNAL(RCPU2_PWM8, RCPU2_PWM8_CLK_RST, RT_BIT(2) | RT_BIT(0), RT_BIT(0), RT_BIT(2), RCPU2), + RESET_SIGNAL(RCPU2_PWM9, RCPU2_PWM9_CLK_RST, RT_BIT(2) | RT_BIT(0), RT_BIT(0), RT_BIT(2), RCPU2), +}; + +static rt_uint32_t spacemit_reset_read(struct spacemit_reset *reset, + rt_uint32_t id) +{ + const struct spacemit_reset_signal *signal = &reset->signals[id]; + + return HWREG32(reset->mmio[signal->type] + signal->offset); +} + +static void spacemit_reset_write(struct spacemit_reset *reset, + rt_uint32_t value, rt_uint32_t id) +{ + const struct spacemit_reset_signal *signal = &reset->signals[id]; + + HWREG32(reset->mmio[signal->type] + signal->offset) = value; +} + +static void spacemit_reset_set(struct spacemit_reset *reset, + rt_uint32_t id, rt_bool_t assert) +{ + rt_uint32_t value = spacemit_reset_read(reset, id); + + value &= ~reset->signals[id].mask; + + if (assert) + { + value |=reset->signals[id].assert_val; + } + else + { + value |= reset->signals[id].deassert_val; + } + + spacemit_reset_write(reset, value, id); +} + +static rt_err_t spacemit_reset_update(struct rt_reset_control *rstc, + rt_ubase_t id, rt_bool_t assert) +{ + rt_ubase_t level; + struct spacemit_reset *reset = rstc->rstcer->priv; + + if (id == RESET_TWSI8) + { + return RT_EOK; + } + + level = rt_spin_lock_irqsave(&k1_cru_lock); + + spacemit_reset_set(reset, id, assert); + + rt_spin_unlock_irqrestore(&k1_cru_lock, level); + + return RT_EOK; +} + +static rt_err_t spacemit_reset_assert(struct rt_reset_control *rstc) +{ + return spacemit_reset_update(rstc, rstc->id, RT_TRUE); +} + +static rt_err_t spacemit_reset_deassert(struct rt_reset_control *rstc) +{ + return spacemit_reset_update(rstc, rstc->id, RT_FALSE); +} + +const static struct rt_reset_control_ops spacemit_reset_ops = +{ + .assert = spacemit_reset_assert, + .deassert = spacemit_reset_deassert, +}; + +static rt_err_t spacemit_reset_probe(struct rt_platform_device *pdev) +{ + rt_err_t err; + struct rt_device *dev = &pdev->parent; + struct spacemit_reset *reset = rt_calloc(1, sizeof(*reset)); + + if (!reset) + { + return -RT_ENOMEM; + } + + for (int i = 0; i < CRU_BASE_TYPE_AUDPMU; ++i) + { + reset->mmio[i] = rt_dm_dev_iomap(dev, i); + + if (!reset->mmio[i]) + { + err = -RT_EIO; + goto _fail; + } + } + + reset->signals_nr = RT_ARRAY_SIZE(k1x_reset_signals); + reset->signals = k1x_reset_signals; + + reset->parent.priv = reset; + reset->parent.ofw_node = dev->ofw_node; + reset->parent.ops = &spacemit_reset_ops; + + if ((err = rt_reset_controller_register(&reset->parent))) + { + goto _fail; + } + + return RT_EOK; + +_fail: + for (int i = 0; i < CRU_BASE_TYPE_AUDPMU; ++i) + { + if (reset->mmio[i]) + { + rt_iounmap(reset->mmio[i]); + } + } + + rt_free(reset); + + return err; +} + +static const struct rt_ofw_node_id spacemit_reset_ofw_ids[] = +{ + { .compatible = "spacemit,k1x-reset" }, + { /* sentinel */ } +}; + +static struct rt_platform_driver spacemit_reset_driver = +{ + .name = "reset-spacemit", + .ids = spacemit_reset_ofw_ids, + + .probe = spacemit_reset_probe, +}; + +static int spacemit_reset_register(void) +{ + rt_platform_driver_register(&spacemit_reset_driver); + + return 0; +} +INIT_SUBSYS_EXPORT(spacemit_reset_register); diff --git a/bsp/spacemit/dm/rtc/Kconfig b/bsp/spacemit/dm/rtc/Kconfig new file mode 100755 index 00000000000..cb30ec14fa2 --- /dev/null +++ b/bsp/spacemit/dm/rtc/Kconfig @@ -0,0 +1,4 @@ +config RT_RTC_SPACEMIT_PMIC + bool "Spacemit PMIC RTC" + depends on RT_MFD_SPACEMIT_PMIC + default y diff --git a/bsp/spacemit/dm/rtc/SConscript b/bsp/spacemit/dm/rtc/SConscript new file mode 100755 index 00000000000..f8c1b2efb11 --- /dev/null +++ b/bsp/spacemit/dm/rtc/SConscript @@ -0,0 +1,13 @@ +from building import * + +group = [] +src = [] +cwd = GetCurrentDir() +CPPPATH = [cwd + '/../include', cwd + '/../../../../components/drivers/rtc'] + +if GetDepend(['RT_RTC_SPACEMIT_PMIC']): + src += ['rtc-spacemit-pmic.c'] + +group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') diff --git a/bsp/spacemit/dm/rtc/rtc-spacemit-pmic.c b/bsp/spacemit/dm/rtc/rtc-spacemit-pmic.c new file mode 100755 index 00000000000..add50784682 --- /dev/null +++ b/bsp/spacemit/dm/rtc/rtc-spacemit-pmic.c @@ -0,0 +1,568 @@ +/* + * Copyright (c) 2006-2024, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2024-5-1 GuEe-GUI first version + */ + +#define DBG_TAG "rtc.spacemit-pmic" +#define DBG_LVL DBG_INFO +#include + +#include "rtc_dm.h" + +#define __SPACEMIT_RTC_INTERNAL +#include + +struct spacemit_pmic_rtc +{ + struct rt_device parent; + + int irq; + struct spacemit_pmic *pmic; + const struct spacemit_pmic_rtc_regdesc *desc; +}; + +#define raw_to_spacemit_pmic_rtc(raw) rt_container_of(raw, struct spacemit_pmic_rtc, parent) + +static rt_err_t spacemit_pmic_rtc_read_time(struct spacemit_pmic_rtc *pmic_rtc, time_t *sec) +{ + unsigned int v[6], pre_v[6] = {0}; + struct tm tm; + struct spacemit_pmic *pmic = pmic_rtc->pmic; + + if ((signed)(pre_v[0] = spacemit_pmic_read(pmic, pmic_rtc->desc->cnt_s.reg)) < 0) + { + LOG_E("Failed to read second: %s", rt_strerror(pre_v[0])); + return -RT_EINVAL; + } + + if ((signed)(pre_v[1] = spacemit_pmic_read(pmic, pmic_rtc->desc->cnt_mi.reg)) < 0) + { + LOG_E("Failed to read minute: %s", rt_strerror(pre_v[1])); + return -RT_EINVAL; + } + + if ((signed)(pre_v[2] = spacemit_pmic_read(pmic, pmic_rtc->desc->cnt_h.reg)) < 0) + { + LOG_E("Failed to read hour: %s", rt_strerror(pre_v[2])); + return -RT_EINVAL; + } + + if ((signed)(pre_v[3] = spacemit_pmic_read(pmic, pmic_rtc->desc->cnt_d.reg)) < 0) + { + LOG_E("Failed to read day: %s", rt_strerror(pre_v[3])); + return -RT_EINVAL; + } + + if ((signed)(pre_v[4] = spacemit_pmic_read(pmic, pmic_rtc->desc->cnt_mo.reg)) < 0) + { + LOG_E("Failed to read month: %s", rt_strerror(pre_v[4])); + return -RT_EINVAL; + } + + if ((signed)(pre_v[5] = spacemit_pmic_read(pmic, pmic_rtc->desc->cnt_y.reg)) < 0) + { + LOG_E("Failed to read year: %s", rt_strerror(pre_v[5])); + return -RT_EINVAL; + } + + while (RT_TRUE) + { + if ((signed)(v[0] = spacemit_pmic_read(pmic, pmic_rtc->desc->cnt_s.reg)) < 0) + { + LOG_E("Failed to read second: %s", rt_strerror(v[0])); + return -RT_EINVAL; + } + + if ((signed)(v[1] = spacemit_pmic_read(pmic, pmic_rtc->desc->cnt_mi.reg)) < 0) + { + LOG_E("Failed to read minute: %s", rt_strerror(v[1])); + return -RT_EINVAL; + } + + if ((signed)(v[2] = spacemit_pmic_read(pmic, pmic_rtc->desc->cnt_h.reg)) < 0) + { + LOG_E("Failed to read hour: %s", rt_strerror(v[2])); + return -RT_EINVAL; + } + + if ((signed)(v[3] = spacemit_pmic_read(pmic, pmic_rtc->desc->cnt_d.reg)) < 0) + { + LOG_E("Failed to read day: %s", rt_strerror(v[3])); + return -RT_EINVAL; + } + + if ((signed)(v[4] = spacemit_pmic_read(pmic, pmic_rtc->desc->cnt_mo.reg)) < 0) + { + LOG_E("Failed to read month: %s", rt_strerror(v[4])); + return -RT_EINVAL; + } + + if ((signed)(v[5] = spacemit_pmic_read(pmic, pmic_rtc->desc->cnt_y.reg)) < 0) + { + LOG_E("Failed to read year: %s", rt_strerror(v[5])); + return -RT_EINVAL; + } + + if ((pre_v[0] == v[0]) && (pre_v[1] == v[1]) && + (pre_v[2] == v[2]) && (pre_v[3] == v[3]) && + (pre_v[4] == v[4]) && (pre_v[5] == v[5])) + { + break; + } + else + { + pre_v[0] = v[0]; + pre_v[1] = v[1]; + pre_v[2] = v[2]; + pre_v[3] = v[3]; + pre_v[4] = v[4]; + pre_v[5] = v[5]; + } + } + + tm.tm_sec = v[0] & pmic_rtc->desc->cnt_s.msk; + tm.tm_min = v[1] & pmic_rtc->desc->cnt_mi.msk; + tm.tm_hour = v[2] & pmic_rtc->desc->cnt_h.msk; + tm.tm_mday = (v[3] & pmic_rtc->desc->cnt_d.msk) + 1; + tm.tm_mon = (v[4] & pmic_rtc->desc->cnt_mo.msk); + tm.tm_year = (v[5] & pmic_rtc->desc->cnt_y.msk) + 100; + + *sec = mktime(&tm); + + return RT_EOK; +} + +static rt_err_t spacemit_pmic_rtc_set_time(struct spacemit_pmic_rtc *pmic_rtc, time_t *sec) +{ + rt_err_t err; + unsigned int v[6]; + struct tm *tm; + union spacemit_pmic_rtc_ctl_desc rtc_ctl; + struct spacemit_pmic *pmic = pmic_rtc->pmic; + + tm = localtime(sec); + + if ((signed)(rtc_ctl.val = spacemit_pmic_read(pmic, pmic_rtc->desc->rtc_ctl.reg)) < 0) + { + LOG_E("Failed to read rtc ctrl: %s", rt_strerror(rtc_ctl.val)); + return -RT_EINVAL; + } + + /* Disbale rtc first */ + rtc_ctl.bits.rtc_en = 0; + + if ((err = spacemit_pmic_write_bit(pmic, pmic_rtc->desc->rtc_ctl.reg, 0xff, rtc_ctl.val))) + { + LOG_E("Failed to set rtc ctrl register: %s", rt_strerror(err)); + return -RT_EINVAL; + } + + while (RT_TRUE) + { + if ((err = spacemit_pmic_write_bit(pmic, + pmic_rtc->desc->cnt_s.reg, pmic_rtc->desc->cnt_s.msk, tm->tm_sec))) + { + LOG_E("Failed to update second: %s", rt_strerror(err)); + return -RT_EINVAL; + } + + if ((err = spacemit_pmic_write_bit(pmic, + pmic_rtc->desc->cnt_mi.reg, pmic_rtc->desc->cnt_mi.msk, tm->tm_min))) + { + LOG_E("Failed to update minutes: %s", rt_strerror(err)); + return -RT_EINVAL; + } + + if ((err = spacemit_pmic_write_bit(pmic, + pmic_rtc->desc->cnt_h.reg, pmic_rtc->desc->cnt_h.msk, tm->tm_hour))) + { + LOG_E("Failed to update hour: %s", rt_strerror(err)); + return -RT_EINVAL; + } + + if ((err = spacemit_pmic_write_bit(pmic, + pmic_rtc->desc->cnt_d.reg, pmic_rtc->desc->cnt_d.msk, tm->tm_mday - 1))) + { + LOG_E("Failed to update day: %s", rt_strerror(err)); + return -RT_EINVAL; + } + + if ((err = spacemit_pmic_write_bit(pmic, + pmic_rtc->desc->cnt_mo.reg, pmic_rtc->desc->cnt_mo.msk, tm->tm_mon))) + { + LOG_E("Failed to update month: %s", rt_strerror(err)); + return -RT_EINVAL; + } + + if ((err = spacemit_pmic_write_bit(pmic, + pmic_rtc->desc->cnt_y.reg, pmic_rtc->desc->cnt_y.msk, tm->tm_year - 100))) + { + LOG_E("Failed to update month: %s", rt_strerror(err)); + return -RT_EINVAL; + } + + /* Read again */ + if ((signed)(v[0] = spacemit_pmic_read(pmic, pmic_rtc->desc->cnt_s.reg)) < 0) + { + LOG_E("Failed to read second: %s", rt_strerror(v[0])); + return -RT_EINVAL; + } + v[0] = v[0] & pmic_rtc->desc->cnt_s.msk; + + if ((signed)(v[1] = spacemit_pmic_read(pmic, pmic_rtc->desc->cnt_mi.reg)) < 0) + { + LOG_E("Failed to read minute: %s", rt_strerror(v[1])); + return -RT_EINVAL; + } + v[1] = v[1] & pmic_rtc->desc->cnt_mi.msk; + + if ((signed)(v[2] = spacemit_pmic_read(pmic, pmic_rtc->desc->cnt_h.reg)) < 0) + { + LOG_E("Failed to read hour: %s", rt_strerror(v[2])); + return -RT_EINVAL; + } + v[2] = v[2] & pmic_rtc->desc->cnt_h.msk; + + if ((signed)(v[3] = spacemit_pmic_read(pmic, pmic_rtc->desc->cnt_d.reg)) < 0) + { + LOG_E("Failed to read day: %s", rt_strerror(v[3])); + return -RT_EINVAL; + } + v[3] = v[3] & pmic_rtc->desc->cnt_d.msk; + + if ((signed)(v[4] = spacemit_pmic_read(pmic, pmic_rtc->desc->cnt_mo.reg)) < 0) + { + LOG_E("Failed to read month: %s", rt_strerror(v[4])); + return -RT_EINVAL; + } + v[4] = v[4] & pmic_rtc->desc->cnt_mo.msk; + + if ((signed)(v[5] = spacemit_pmic_read(pmic, pmic_rtc->desc->cnt_y.reg)) < 0) + { + LOG_E("Failed to read year: %s", rt_strerror(v[5])); + return -RT_EINVAL; + } + v[5] = v[5] & pmic_rtc->desc->cnt_y.msk; + + if ((v[0] == (pmic_rtc->desc->cnt_s.msk & tm->tm_sec)) && + (v[1] == (pmic_rtc->desc->cnt_mi.msk & tm->tm_min)) && + (v[2] == (pmic_rtc->desc->cnt_h.msk & tm->tm_hour)) && + ((v[3] + 1) == (pmic_rtc->desc->cnt_d.msk & tm->tm_mday)) && + (v[4] == (pmic_rtc->desc->cnt_mo.msk & tm->tm_mon)) && + (v[5] == (pmic_rtc->desc->cnt_y.msk & (tm->tm_year - 100)))) + { + break; + } + } + + /* Enable rtc last */ + rtc_ctl.bits.rtc_en = 1; + + if ((err = spacemit_pmic_write_bit(pmic, pmic_rtc->desc->rtc_ctl.reg, 0xff, rtc_ctl.val))) + { + LOG_E("Failed to set rtc ctrl register: %s", rt_strerror(err)); + return -RT_EINVAL; + } + + return RT_EOK; +} + +static rt_err_t spacemit_pmic_rtc_read_alarm(struct spacemit_pmic_rtc *pmic_rtc, + struct rt_rtc_wkalarm *alarm) +{ + unsigned int v[6]; + union spacemit_pmic_rtc_ctl_desc rtc_ctl; + struct spacemit_pmic *pmic = pmic_rtc->pmic; + + if ((signed)(v[0] = spacemit_pmic_read(pmic, pmic_rtc->desc->alarm_s.reg)) < 0) + { + LOG_E("Failed to read alarm second: %s", rt_strerror(v[0])); + return -RT_EINVAL; + } + + if ((signed)(v[1] = spacemit_pmic_read(pmic, pmic_rtc->desc->alarm_mi.reg)) < 0) + { + LOG_E("Failed to read alarm minute: %s", rt_strerror(v[1])); + return -RT_EINVAL; + } + + if ((signed)(v[2] = spacemit_pmic_read(pmic, pmic_rtc->desc->alarm_h.reg)) < 0) + { + LOG_E("Failed to read alarm hour: %s", rt_strerror(v[2])); + return -RT_EINVAL; + } + + if ((signed)(v[3] = spacemit_pmic_read(pmic, pmic_rtc->desc->alarm_d.reg)) < 0) + { + LOG_E("Failed to read alarm day: %s", rt_strerror(v[3])); + return -RT_EINVAL; + } + + if ((signed)(v[4] = spacemit_pmic_read(pmic, pmic_rtc->desc->alarm_mo.reg)) < 0) + { + LOG_E("Failed to read alarm month: %s", rt_strerror(v[4])); + return -RT_EINVAL; + } + + if ((signed)(v[5] = spacemit_pmic_read(pmic, pmic_rtc->desc->alarm_y.reg)) < 0) + { + LOG_E("Failed to read alarm year: %s", rt_strerror(v[5])); + return -RT_EINVAL; + } + + /* 2000:1:1:0:0:0 */ + alarm->tm_sec = v[0] & pmic_rtc->desc->alarm_s.msk; + alarm->tm_min = v[1] & pmic_rtc->desc->alarm_mi.msk; + alarm->tm_hour = v[2] & pmic_rtc->desc->alarm_h.msk; + alarm->tm_mday = (v[3] & pmic_rtc->desc->alarm_d.msk) + 1; + alarm->tm_mon = (v[4] & pmic_rtc->desc->alarm_mo.msk); + alarm->tm_year = (v[5] & pmic_rtc->desc->alarm_y.msk) + 100; + + if ((signed)(rtc_ctl.val = spacemit_pmic_read(pmic, pmic_rtc->desc->rtc_ctl.reg)) < 0) + { + LOG_E("Failed to read alarm second: %s", rt_strerror(rtc_ctl.val)); + return -RT_EINVAL; + } + + alarm->enable = !!rtc_ctl.bits.alarm_en; + + return RT_EOK; +} + +static rt_err_t spacemit_pmic_rtc_set_alarm(struct spacemit_pmic_rtc *pmic_rtc, + struct rt_rtc_wkalarm *alarm) +{ + rt_err_t err; + union spacemit_pmic_rtc_ctl_desc rtc_ctl; + struct spacemit_pmic *pmic = pmic_rtc->pmic; + + /* Disable the alrm function first */ + if ((signed)(rtc_ctl.val = spacemit_pmic_read(pmic, pmic_rtc->desc->rtc_ctl.reg)) < 0) + { + LOG_E("Failed to read rtc ctrl register: %s", rt_strerror(rtc_ctl.val)); + return -RT_EINVAL; + } + + rtc_ctl.bits.alarm_en = 0; + + if ((err = spacemit_pmic_write_bit(pmic, pmic_rtc->desc->rtc_ctl.reg, 0xff, rtc_ctl.val))) + { + LOG_E("Failed to set rtc ctrl register: %s", rt_strerror(err)); + return -RT_EINVAL; + } + + if ((err = spacemit_pmic_write_bit(pmic, + pmic_rtc->desc->alarm_s.reg, pmic_rtc->desc->alarm_s.msk, alarm->tm_sec))) + { + LOG_E("Failed to update alrm second: %s", rt_strerror(err)); + return -RT_EINVAL; + } + + if ((err = spacemit_pmic_write_bit(pmic, + pmic_rtc->desc->alarm_mi.reg, pmic_rtc->desc->alarm_mi.msk, alarm->tm_min))) + { + LOG_E("Failed to update alarm minutes: %s", rt_strerror(err)); + return -RT_EINVAL; + } + + if ((err = spacemit_pmic_write_bit(pmic, + pmic_rtc->desc->alarm_h.reg, pmic_rtc->desc->alarm_h.msk, alarm->tm_hour))) + { + LOG_E("Failed to update alarm hour: %s", rt_strerror(err)); + return -RT_EINVAL; + } + + if ((err = spacemit_pmic_write_bit(pmic, + pmic_rtc->desc->alarm_d.reg, pmic_rtc->desc->alarm_d.msk, alarm->tm_mday - 1))) + { + LOG_E("Failed to update alarm day: %s", rt_strerror(err)); + return -RT_EINVAL; + } + + if ((err = spacemit_pmic_write_bit(pmic, + pmic_rtc->desc->alarm_mo.reg, pmic_rtc->desc->alarm_mo.msk, alarm->tm_mon))) + { + LOG_E("Failed to update alarm month: %s", rt_strerror(err)); + return -RT_EINVAL; + } + + if ((err = spacemit_pmic_write_bit(pmic, + pmic_rtc->desc->alarm_y.reg, pmic_rtc->desc->alarm_y.msk, alarm->tm_year - 100))) + { + LOG_E("Failed to update month: %s", rt_strerror(err)); + return -RT_EINVAL; + } + + if (alarm->enable) + { + rtc_ctl.bits.alarm_en = 1; + + if ((err = spacemit_pmic_write_bit(pmic, pmic_rtc->desc->rtc_ctl.reg, 0xff, rtc_ctl.val))) + { + LOG_E("Failed to set rtc ctrl register: %s", rt_strerror(err)); + return -RT_EINVAL; + } + } + + return RT_EOK; +} + +static rt_err_t spacemit_pmic_rtc_control(rt_device_t dev, int cmd, void *args) +{ + rt_err_t err = RT_EOK; + struct spacemit_pmic_rtc *pmic_rtc = raw_to_spacemit_pmic_rtc(dev); + + if (!args) + { + return -RT_EINVAL; + } + + switch (cmd) + { + case RT_DEVICE_CTRL_RTC_GET_TIME: + err = spacemit_pmic_rtc_read_time(pmic_rtc, args); + break; + + case RT_DEVICE_CTRL_RTC_SET_TIME: + err = spacemit_pmic_rtc_set_time(pmic_rtc, args); + break; + + case RT_DEVICE_CTRL_RTC_GET_TIMEVAL: + err = spacemit_pmic_rtc_read_time(pmic_rtc, (time_t *)&((struct timeval *)args)->tv_sec); + break; + + case RT_DEVICE_CTRL_RTC_SET_TIMEVAL: + err = spacemit_pmic_rtc_set_time(pmic_rtc, (time_t *)&((struct timeval *)args)->tv_sec); + break; + + case RT_DEVICE_CTRL_RTC_GET_ALARM: + err = spacemit_pmic_rtc_read_alarm(pmic_rtc, args); + break; + + case RT_DEVICE_CTRL_RTC_SET_ALARM: + err = spacemit_pmic_rtc_set_alarm(pmic_rtc, args); + break; + + default: + err = -RT_EINVAL; + break; + } + + return err; +} + +#ifdef RT_USING_DEVICE_OPS +const static struct rt_device_ops spacemit_pmic_rtc_ops = +{ + .control = spacemit_pmic_rtc_control, +}; +#endif + +static void spacemit_pmic_rtc_isr(int irq, void *param) +{ + struct spacemit_pmic_rtc *pmic_rtc = param; + + rt_alarm_update(&pmic_rtc->parent, 1); +} + +static rt_err_t spacemit_pmic_rtc_probe(struct rt_platform_device *pdev) +{ + rt_err_t err; + const char *dev_name; + struct rt_device *dev = &pdev->parent; + struct spacemit_pmic *pmic = pdev->priv; + union spacemit_pmic_rtc_ctl_desc rtc_ctl; + struct spacemit_pmic_rtc *pmic_rtc = rt_calloc(1, sizeof(*pmic_rtc)); + + if (!pmic_rtc) + { + return -RT_ENOMEM; + } + + pmic_rtc->pmic = pmic; + pmic_rtc->desc = pdev->id->data; + + if ((pmic_rtc->irq = rt_dm_dev_get_irq(dev, 0)) < 0) + { + err = pmic_rtc->irq; + goto _fail; + } + + /* Enable the rtc function */ + if ((signed)(rtc_ctl.val = spacemit_pmic_read(pmic, pmic_rtc->desc->rtc_ctl.reg)) < 0) + { + err = rtc_ctl.val; + goto _fail; + } + + /* Internal 32k clk */ + rtc_ctl.bits.rtc_clk_sel = 1; + /* Enable rtc */ + rtc_ctl.bits.rtc_en = 1; + /* RTC clk out enable */ + rtc_ctl.bits.out_32k_en = 1; + /* Enable external crystal */ + rtc_ctl.bits.crystal_en = 1; + + if ((err = spacemit_pmic_update_bits(pmic, pmic_rtc->desc->rtc_ctl.reg, 0xff, rtc_ctl.val))) + { + goto _fail; + } + + pmic_rtc->parent.type = RT_Device_Class_RTC; +#ifdef RT_USING_DEVICE_OPS + pmic_rtc->parent.ops = &spacemit_pmic_rtc_ops; +#else + pmic_rtc->parent.control = spacemit_pmic_rtc_control; +#endif + + rtc_dev_set_name(&pmic_rtc->parent); + dev_name = rt_dm_dev_get_name(&pmic_rtc->parent); + rt_device_register(&pmic_rtc->parent, dev_name, RT_DEVICE_FLAG_RDWR); + + rt_hw_interrupt_install(pmic_rtc->irq, spacemit_pmic_rtc_isr, pmic_rtc, "rtc-spacemit-pmic"); + rt_hw_interrupt_umask(pmic_rtc->irq); + + dev->user_data = pmic_rtc; + + return RT_EOK; +_fail: + rt_free(pmic_rtc); + + return err; +} + +static rt_err_t spacemit_pmic_rtc_remove(struct rt_platform_device *pdev) +{ + struct spacemit_pmic_rtc *pmic_rtc = pdev->parent.user_data; + + rt_hw_interrupt_mask(pmic_rtc->irq); + rt_pic_detach_irq(pmic_rtc->irq, pmic_rtc); + + rt_device_unregister(&pmic_rtc->parent); + + rt_free(pmic_rtc); + + return RT_EOK; +} + +static const struct rt_ofw_node_id spacemit_pmic_rtc_ofw_ids[] = +{ + { .compatible = "pmic,rtc,spm8821", .data = &spm8821_regdesc }, + { /* sentinel */ } +}; + +static struct rt_platform_driver spacemit_pmic_rtc_driver = +{ + .name = "spacemit-pmic-rtc", + .ids = spacemit_pmic_rtc_ofw_ids, + + .probe = spacemit_pmic_rtc_probe, + .remove = spacemit_pmic_rtc_remove, +}; +RT_PLATFORM_DRIVER_EXPORT(spacemit_pmic_rtc_driver); diff --git a/bsp/spacemit/dm/serial/8250-pxa_k1x.c b/bsp/spacemit/dm/serial/8250-pxa_k1x.c new file mode 100755 index 00000000000..c0a0c38a218 --- /dev/null +++ b/bsp/spacemit/dm/serial/8250-pxa_k1x.c @@ -0,0 +1,362 @@ +/* + * Copyright (c) 2006-2024, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2024-5-1 GuEe-GUI first version + */ + +#include +#include +#include +#include + +#include +#include "8250/8250.h" + +#define UART_XMIT_SIZE RT_DMA_PAGE_SIZE +#define DMA_BLOCK UART_XMIT_SIZE +#define DMA_BURST_SIZE 8 + +struct serial_pxa +{ + struct serial8250 parent; + + rt_bool_t device_ctrl_rts; + + struct rt_dma_slave_config rx_config; + struct rt_dma_slave_config tx_config; + struct rt_dma_slave_transfer rx_transfer; + struct rt_dma_slave_transfer tx_transfer; + + struct rt_dma_chan *dma_rx; + struct rt_dma_chan *dma_tx; + + struct rt_clk *fclk; + struct rt_clk *gclk; + struct rt_reset_control *rstc; +}; + +#define raw_to_serial_pxa(raw) rt_container_of(raw, struct serial_pxa, parent) + +static rt_err_t serial_pxa_isr(struct serial8250 *serial, int irq) +{ + rt_uint8_t iir; + rt_uint32_t ier; + + while (!((iir = serial->serial_in(serial, UART_IIR)) & UART_IIR_NO_INT)) + { + switch (iir & UART_IIR_ID) + { + /* RX data available */ + case UART_IIR_RDI: + /* RX timeout pending */ + case UART_IIR_RX_TIMEOUT: + while (serial->serial_in(serial, UART_LSR) & UART_LSR_DR) + { + rt_hw_serial_isr(&serial->parent, RT_SERIAL_EVENT_RX_IND); + } + break; + + /* TX holding register empty */ + case UART_IIR_THRI: + ier = serial->serial_in(serial, UART_IER); + ier &= ~UART_IER_THRI; /* Disable THRI */ + serial->serial_out(serial, UART_IER, ier); + break; + + /* Line status */ + case UART_IIR_RLSI: + /* Read to clear */ + serial->serial_in(serial, UART_LSR); + break; + } + } + + return RT_EOK; +} + +static rt_err_t serial_pxa_dma_enable(struct serial8250 *serial, rt_bool_t enabled) +{ + struct serial_pxa *pxa = raw_to_serial_pxa(serial); + + if (enabled) + { + rt_dma_chan_pause(pxa->dma_rx); + rt_dma_chan_pause(pxa->dma_tx); + } + else + { + rt_dma_chan_stop(pxa->dma_rx); + rt_dma_chan_stop(pxa->dma_tx); + } + + return RT_EOK; +} + +static rt_ssize_t serial_pxa_dma_tx(struct serial8250 *serial, const rt_uint8_t *buf, rt_size_t size) +{ + rt_err_t err; + struct serial_pxa *pxa = raw_to_serial_pxa(serial); + + pxa->tx_config.src_addr = (rt_ubase_t)rt_kmem_v2p((void *)buf); + + if ((err = rt_dma_chan_config(pxa->dma_tx, &pxa->tx_config))) + { + return err; + } + + pxa->tx_transfer.buffer_len = size; + pxa->tx_transfer.src_addr = pxa->tx_config.src_addr; + + if ((err = rt_dma_prep_single(pxa->dma_tx, &pxa->tx_transfer))) + { + return err; + } + + if ((err = rt_dma_chan_start(pxa->dma_tx))) + { + return err; + } + + return size; +} + +static rt_ssize_t serial_pxa_dma_rx(struct serial8250 *serial, rt_uint8_t *buf, rt_size_t size) +{ + rt_err_t err; + struct serial_pxa *pxa = raw_to_serial_pxa(serial); + + pxa->rx_config.dst_addr = (rt_ubase_t)rt_kmem_v2p(buf); + + if ((err = rt_dma_chan_config(pxa->dma_rx, &pxa->rx_config))) + { + return err; + } + + pxa->rx_transfer.buffer_len = size; + pxa->rx_transfer.dst_addr = pxa->rx_config.dst_addr; + + if ((err = rt_dma_prep_single(pxa->dma_rx, &pxa->rx_transfer))) + { + return err; + } + + if ((err = rt_dma_chan_start(pxa->dma_rx))) + { + return err; + } + + return size; +} + +static rt_err_t serial_pxa_early_setup(struct rt_fdt_earlycon *con, const char *options) +{ + struct serial8250 *serial = &early_serial8250; + + serial->base = (void *)con->mmio; + serial->size = con->size; + serial->iotype = PORT_MMIO32; + serial->regshift = 2; + fdt_getprop_u32(con->fdt, con->nodeoffset, "reg-shift", &serial->regshift, RT_NULL); + + return serial8250_early_fdt_setup(serial, con, options); +} +RT_FDT_EARLYCON_EXPORT(serial_pxa, "uart8250", "spacemit,pxa-uart", serial_pxa_early_setup); + +static void serial_pxa_8250_remove(struct serial8250 *serial) +{ + struct serial_pxa *pxa = rt_container_of(serial, struct serial_pxa, parent); + + if (serial->base) + { + rt_iounmap(serial->base); + } + + if (!rt_is_err_or_null(pxa->rstc)) + { + rt_reset_control_assert(pxa->rstc); + rt_reset_control_put(pxa->rstc); + } + + if (!rt_is_err_or_null(pxa->fclk)) + { + rt_clk_unprepare(pxa->fclk); + rt_clk_put(pxa->fclk); + } + + if (!rt_is_err_or_null(pxa->gclk)) + { + rt_clk_unprepare(pxa->gclk); + rt_clk_put(pxa->gclk); + } + + if (!rt_is_err_or_null(pxa->dma_tx)) + { + rt_dma_chan_release(pxa->dma_tx); + } + + if (!rt_is_err_or_null(pxa->dma_rx)) + { + rt_dma_chan_release(pxa->dma_rx); + } + + rt_free(pxa); +} + +static rt_err_t serial_pxa_probe(struct rt_platform_device *pdev) +{ + rt_err_t err; + rt_ubase_t fifo_addr_phys; + struct serial8250 *serial; + struct rt_device *dev = &pdev->parent; + struct serial_pxa *pxa = serial8250_alloc(pxa); + struct serial_configure config = RT_SERIAL_CONFIG_DEFAULT; + + if (!pxa) + { + return -RT_ENOMEM; + } + serial = &pxa->parent; + + serial->base = rt_dm_dev_iomap(dev, 0); + if (!serial->base) + { + err = -RT_EIO; + goto _fail; + } + + serial->irq = rt_dm_dev_get_irq(dev, 0); + if (serial->irq < 0) + { + err = serial->irq; + goto _fail; + } + + pxa->gclk = rt_clk_get_by_name(dev, "gate"); + if (rt_is_err(pxa->gclk)) + { + err = rt_ptr_err(pxa->gclk); + goto _fail; + } + + pxa->fclk = rt_clk_get_by_name(dev, "func"); + if (rt_is_err(pxa->fclk)) + { + err = rt_ptr_err(pxa->fclk); + goto _fail; + } + + if ((err = rt_clk_prepare(pxa->gclk))) + { + goto _fail; + } + + if ((err = rt_clk_prepare(pxa->fclk))) + { + goto _fail; + } + + pxa->rstc = rt_reset_control_get_by_index(dev, 0); + if (rt_is_err(pxa->rstc)) + { + err = rt_ptr_err(pxa->rstc); + goto _fail; + } + rt_reset_control_deassert(pxa->rstc); + + fifo_addr_phys = (rt_ubase_t)rt_kmem_v2p(serial->base); + pxa->device_ctrl_rts = rt_dm_dev_prop_read_bool(dev, "device-control-rts"); + + pxa->dma_tx = rt_dma_chan_request(dev, "tx"); + + if (!rt_is_err_or_null(pxa->dma_tx)) + { + pxa->tx_config.dst_addr_width = RT_DMA_SLAVE_BUSWIDTH_1_BYTE; + pxa->tx_config.dst_maxburst = DMA_BURST_SIZE; + pxa->tx_config.direction = RT_DMA_MEM_TO_DEV; + pxa->tx_config.dst_addr = fifo_addr_phys; + + pxa->tx_transfer.dst_addr = pxa->tx_config.dst_addr; + } + + pxa->dma_rx = rt_dma_chan_request(dev, "rx"); + + if (!rt_is_err_or_null(pxa->dma_rx)) + { + pxa->rx_config.src_addr_width = RT_DMA_SLAVE_BUSWIDTH_1_BYTE; + pxa->rx_config.src_maxburst = DMA_BURST_SIZE; + pxa->rx_config.direction = RT_DMA_MEM_TO_DEV; + pxa->rx_config.src_addr = fifo_addr_phys; + + pxa->rx_transfer.src_addr = pxa->rx_config.src_addr; + } + + serial->clk = pxa->fclk; + serial->freq = rt_clk_get_rate(pxa->fclk); + serial->regshift = 2; + serial->iotype = PORT_MMIO32; + serial->parent.ops = &serial8250_uart_ops; + serial->parent.config = config; + serial->handle_irq = serial_pxa_isr; + serial->remove = serial_pxa_8250_remove; + if (!rt_is_err_or_null(pxa->dma_rx) && !rt_is_err_or_null(pxa->dma_tx)) + { + serial->serial_dma_enable = serial_pxa_dma_enable; + serial->serial_dma_tx = serial_pxa_dma_tx; + serial->serial_dma_rx = serial_pxa_dma_rx; + } + serial->data = dev; + + if ((err = serial8250_setup(serial))) + { + goto _fail; + } + + rt_dm_dev_bind_fwdata(&serial->parent.parent, dev->ofw_node, &serial->parent); + + dev->user_data = pxa; + + return RT_EOK; + +_fail: + pxa->parent.data = dev; + serial_pxa_8250_remove(&pxa->parent); + + return err; +} + +static rt_err_t serial_pxa_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 serial_pxa_ofw_ids[] = +{ + { .type = "ttyS", .compatible = "spacemit,pxa-uart" }, + { /* sentinel */ } +}; + +static struct rt_platform_driver serial_pxa_driver = +{ + .name = "serial-pxa", + .ids = serial_pxa_ofw_ids, + + .probe = serial_pxa_probe, + .remove = serial_pxa_remove, +}; + +static int serial_pxa_drv_register(void) +{ + rt_platform_driver_register(&serial_pxa_driver); + + return 0; +} +INIT_PLATFORM_EXPORT(serial_pxa_drv_register); diff --git a/bsp/spacemit/dm/serial/Kconfig b/bsp/spacemit/dm/serial/Kconfig new file mode 100755 index 00000000000..e63d197d773 --- /dev/null +++ b/bsp/spacemit/dm/serial/Kconfig @@ -0,0 +1,7 @@ +config RT_SERIAL_PXA + bool "PXA serial" + depends on RT_USING_DMA + depends on RT_SERIAL_8250 + default n + +source "$(RTT_DIR)/libcpu/risc-v/common64/serial/Kconfig" diff --git a/bsp/spacemit/dm/serial/SConscript b/bsp/spacemit/dm/serial/SConscript new file mode 100755 index 00000000000..9f7f3c91f45 --- /dev/null +++ b/bsp/spacemit/dm/serial/SConscript @@ -0,0 +1,13 @@ +from building import * + +group = [] +src = [] +cwd = GetCurrentDir() +CPPPATH = [cwd + '/../include', cwd + '/../../../../components/drivers/serial/device/'] + +if GetDepend(['RT_SERIAL_PXA']): + src += ['8250-pxa_k1x.c'] + +group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') diff --git a/bsp/spacemit/dm/soc/Kconfig b/bsp/spacemit/dm/soc/Kconfig new file mode 100755 index 00000000000..30303d80dfd --- /dev/null +++ b/bsp/spacemit/dm/soc/Kconfig @@ -0,0 +1,13 @@ +config RT_SOC_SPACEMIT_K1X_RPROC + bool "Spacemit k1x remoteproc Driver" + default y + +config RT_SOC_SPACEMIT_K1X_TCM + bool "Spacemit k1x generic TCM alloc management Driver" + depends on RT_USING_MEMHEAP + default n + +config RT_SOC_SPACEMIT_SOCINFO + bool "Spacemit SoC information Driver" + depends on RT_USING_NVMEM + default n diff --git a/bsp/spacemit/dm/soc/SConscript b/bsp/spacemit/dm/soc/SConscript new file mode 100755 index 00000000000..35cb0e7a679 --- /dev/null +++ b/bsp/spacemit/dm/soc/SConscript @@ -0,0 +1,25 @@ +from building import * + +group = [] + +cwd = GetCurrentDir() +list = os.listdir(cwd) +CPPPATH = [cwd + '/../include'] + +src = [] + +if GetDepend(['RT_SOC_SPACEMIT_K1X_RPROC']): + src += ['k1x-rproc.c'] + +if GetDepend(['RT_SOC_SPACEMIT_K1X_TCM']): + src += ['k1x-tcm.c'] + +if GetDepend(['ARCH_RISCV']): + src += ['sbi-ipi.c'] + +if GetDepend(['RT_SOC_SPACEMIT_SOCINFO']): + src += ['spacemit-socinfo.c'] + +group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') diff --git a/bsp/spacemit/dm/soc/k1x-rproc.c b/bsp/spacemit/dm/soc/k1x-rproc.c new file mode 100755 index 00000000000..ee0ec6ca55d --- /dev/null +++ b/bsp/spacemit/dm/soc/k1x-rproc.c @@ -0,0 +1,336 @@ +/* + * Copyright (c) 2006-2024, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2024-5-1 GuEe-GUI first version + */ + +#include +#include + +#define DBG_TAG "soc.k1x-rproc" +#define DBG_LVL DBG_INFO +#include + +#include + +#define K1X_MBOX_VQ0_ID 0 +#define K1X_MBOX_VQ1_ID 1 + +#define FW_BOOT_ENTRY_REG_OFFSET 0x88 +#define FW_BOOTUP_REG_OFFSET 0x30 +#define FW_AON_PER_CLK_RST_CTL_REG 0x2c + +#define FW_DDR_REGMAP_BASE_REG_OFFSET 0xc0 + +#define AUDIO_PMU_VOTE_REG_OFFSET 0x20 + +#define DEV_PM_QOS_CLK_GATE 1 +#define DEV_PM_QOS_REGULATOR_GATE 2 +#define DEV_PM_QOS_PM_DOMAIN_GATE 4 +#define DEV_PM_QOS_DEFAULT 7 + +static char *const mb_name[] = +{ + [K1X_MBOX_VQ0_ID] = "vq0", + [K1X_MBOX_VQ1_ID] = "vq1", +}; + +struct spacemit_rproc_mbox +{ + struct rt_mbox_client parent; + + struct rt_mbox_chan *chan; + + int vq_id; + struct rt_completion mb_comp; +}; + +struct spacemit_rproc +{ + struct rt_device parent; + + void *bootc_mem_base; + void *sysctrl_mem_base; + + struct rt_clk *core_clk; + struct rt_clk *apb_clk; + struct rt_reset_control *core_rstc; + + rt_uint32_t apb_clk_rate; + rt_uint32_t apb_clk_rate_default; + rt_uint32_t ddr_remap_base; + rt_uint32_t fw_entry_point; + struct spacemit_rproc_mbox mb[RT_ARRAY_SIZE(mb_name)]; +}; + +static rt_ssize_t spacemit_rproc_write(rt_device_t dev, rt_off_t pos, + const void *buffer, rt_size_t size) +{ + rt_err_t err; + struct spacemit_rproc *rproc = rt_container_of(dev, struct spacemit_rproc, parent); + + if (pos >= RT_ARRAY_SIZE(rproc->mb)) + { + return -RT_EINVAL; + } + + err = rt_mbox_send(rproc->mb[pos].chan, buffer ? : "kick", RT_WAITING_FOREVER); + + return err ? err : size; +} + +#ifdef RT_USING_DEVICE_OPS +const static struct rt_device_ops spacemit_rproc_ops = +{ + .write = spacemit_rproc_write, +}; +#endif + +static rt_err_t spacemit_rproc_prepare(struct spacemit_rproc *rproc) +{ + /* De-assert the clk */ + rt_reset_control_assert(rproc->core_rstc); + rt_reset_control_deassert(rproc->core_rstc); + + /* Enable clocks explicitly (RT-Thread may keep them gated by default) */ + rt_clk_enable(rproc->core_clk); + rt_clk_enable(rproc->apb_clk); + + /* Set apb clk rate */ + rt_clk_set_rate(rproc->apb_clk, rproc->apb_clk_rate); + + return RT_EOK; +} + +static rt_err_t spacemit_rproc_start(struct spacemit_rproc *rproc) +{ + /* Enable ipc2ap clk & reset--> rcpu side */ + HWREG32(rproc->bootc_mem_base + FW_AON_PER_CLK_RST_CTL_REG) = 0xff; + + /* Set the boot-entry */ + HWREG32(rproc->sysctrl_mem_base + FW_BOOT_ENTRY_REG_OFFSET) = rproc->fw_entry_point; + + /* Set ddr map */ + HWREG32(rproc->sysctrl_mem_base + FW_DDR_REGMAP_BASE_REG_OFFSET) = rproc->ddr_remap_base; + + /* Lanching up firmware */ + HWREG32(rproc->bootc_mem_base + FW_BOOTUP_REG_OFFSET) = 1; + + return RT_EOK; +} + +static rt_err_t spacemit_rproc_stop(struct spacemit_rproc *rproc) +{ + /* Hold the rcpu */ + HWREG32(rproc->bootc_mem_base + FW_BOOTUP_REG_OFFSET) = 0; + + rt_reset_control_assert(rproc->core_rstc); + + /* Gate clocks after reset asserted */ + rt_clk_disable(rproc->apb_clk); + rt_clk_disable(rproc->core_clk); + + return RT_EOK; +} + +static void spacemit_rproc_mbox_rx_callback(struct rt_mbox_client *client, void *data) +{ + struct spacemit_rproc_mbox *mb = rt_container_of(client, struct spacemit_rproc_mbox, parent); + + rt_completion_done(&mb->mb_comp); +} + +static rt_err_t spacemit_rproc_probe(struct rt_platform_device *pdev) +{ + rt_err_t err; + const char *fw_name, *name; + struct rt_device *dev = &pdev->parent; + struct spacemit_rproc *rproc = rt_calloc(1, sizeof(*rproc)); + + if (!rproc) + { + return -RT_ENOMEM; + } + + rproc->bootc_mem_base = rt_dm_dev_iomap(dev, 0); + if (!rproc->bootc_mem_base) + { + err = -RT_EIO; + goto _fail; + } + + rproc->sysctrl_mem_base = rt_dm_dev_iomap(dev, 1); + if (!rproc->sysctrl_mem_base) + { + err = -RT_EIO; + goto _fail; + } + + rproc->core_rstc = rt_reset_control_get_by_index(dev, 0); + if (rt_is_err(rproc->core_rstc)) + { + err = rt_ptr_err(rproc->core_rstc); + goto _fail; + } + + rproc->core_clk = rt_clk_get_by_name(dev, "core"); + if (rt_is_err(rproc->core_clk)) + { + err = rt_ptr_err(rproc->core_clk); + goto _fail; + } + + rproc->apb_clk = rt_clk_get_by_name(dev, "apb"); + if (rt_is_err(rproc->apb_clk)) + { + err = rt_ptr_err(rproc->apb_clk); + goto _fail; + } + + if ((err = rt_dm_dev_prop_read_u32(dev, "apb-clk-rate", &rproc->apb_clk_rate))) + { + goto _fail; + } + + if ((err = rt_dm_dev_prop_read_u32(dev, "ddr-remap-base", &rproc->ddr_remap_base))) + { + goto _fail; + } + + if ((err = rt_dm_dev_prop_read_string(dev, "firmware-name", &fw_name))) + { + goto _fail; + } + + if (!(name = rt_dm_dev_get_prop_fuzzy_name(dev, "-entry-point$"))) + { + err = -RT_EINVAL; + goto _fail; + } + + if ((err = rt_dm_dev_prop_read_u32(dev, name, &rproc->fw_entry_point))) + { + goto _fail; + } + + for (int i = 0; i < RT_ARRAY_SIZE(rproc->mb); ++i) + { + struct spacemit_rproc_mbox *mb = &rproc->mb[i]; + + rt_completion_init(&mb->mb_comp); + + mb->parent.dev = dev; + mb->parent.rx_callback = spacemit_rproc_mbox_rx_callback; + + mb->chan = rt_mbox_request_by_name(&mb->parent, mb_name[i]); + + if (rt_is_err_or_null(mb->chan)) + { + err = -RT_EINVAL; + LOG_E("Request mailbox fail"); + goto _fail; + } + } + + spacemit_rproc_prepare(rproc); + + if ((err = spacemit_firmware_load_elf(fw_name, rproc->ddr_remap_base))) + { + LOG_E("Run firmware %s fail", fw_name); + goto _fail; + } + + spacemit_rproc_start(rproc); + + rt_dm_dev_set_name(&rproc->parent, "rproc-%s", fw_name); + name = rt_dm_dev_get_name(&rproc->parent); + + rproc->parent.type = RT_Device_Class_Char; +#ifdef RT_USING_DEVICE_OPS + rproc->parent.ops = &spacemit_rproc_ops; +#else + rproc->parent.write = spacemit_rproc_write; +#endif + + if ((err = rt_device_register(&rproc->parent, name, RT_DEVICE_FLAG_WRONLY))) + { + goto _fail; + } + + dev->user_data = rproc; + + return RT_EOK; + +_fail: + if (rproc->bootc_mem_base) + { + rt_iounmap(rproc->bootc_mem_base); + } + if (rproc->sysctrl_mem_base) + { + rt_iounmap(rproc->sysctrl_mem_base); + } + + if (!rt_is_err_or_null(rproc->core_rstc)) + { + rt_reset_control_put(rproc->core_rstc); + } + if (!rt_is_err_or_null(rproc->core_clk)) + { + rt_clk_put(rproc->core_clk); + } + if (!rt_is_err_or_null(rproc->apb_clk)) + { + rt_clk_put(rproc->apb_clk); + } + + for (int i = 0; i < RT_ARRAY_SIZE(rproc->mb); ++i) + { + struct spacemit_rproc_mbox *mb = &rproc->mb[i]; + + if (!rt_is_err_or_null(mb->chan)) + { + rt_mbox_release(mb->chan); + } + } + + rt_free(rproc); + + return err; +} + +static rt_err_t spacemit_rproc_shutdown(struct rt_platform_device *pdev) +{ + struct spacemit_rproc *rproc = pdev->parent.user_data; + + spacemit_rproc_stop(rproc); + + return RT_EOK; +} + +static const struct rt_ofw_node_id spacemit_rproc_ofw_ids[] = +{ + { .compatible = "spacemit,k1-x-rproc" }, + { /* sentinel */ } +}; + +static struct rt_platform_driver spacemit_rproc_driver = +{ + .name = "spacemit-rproc", + .ids = spacemit_rproc_ofw_ids, + + .probe = spacemit_rproc_probe, + .shutdown = spacemit_rproc_shutdown, +}; + +static int spacemit_rproc_drv_register(void) +{ + rt_platform_driver_register(&spacemit_rproc_driver); + + return 0; +} +INIT_SUBSYS_EXPORT(spacemit_rproc_drv_register); diff --git a/bsp/spacemit/dm/soc/k1x-tcm.c b/bsp/spacemit/dm/soc/k1x-tcm.c new file mode 100755 index 00000000000..df1d97cac36 --- /dev/null +++ b/bsp/spacemit/dm/soc/k1x-tcm.c @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2006-2024, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2024-5-1 GuEe-GUI first version + */ + +#include +#include + +#define DBG_TAG "soc.k1x-tcm" +#define DBG_LVL DBG_INFO +#include + +#include +#include + +static struct rt_memheap *tcm_heap[RT_CPUS_NR]; + +void *tcm_alloc_cpu(int cpuid, rt_size_t size) +{ + if (cpuid < RT_ARRAY_SIZE(tcm_heap) && tcm_heap[cpuid]) + { + return rt_memheap_alloc(tcm_heap[cpuid], size); + } + + return RT_NULL; +} + +void tcm_free_cpu(int cpuid, void *ptr) +{ + if (cpuid < RT_ARRAY_SIZE(tcm_heap) && tcm_heap[cpuid]) + { + rt_memheap_free(ptr); + } +} + +static rt_err_t spacemit_tcm_probe(struct rt_platform_device *pdev) +{ + struct rt_ofw_node *np = pdev->parent.ofw_node, *tcm_np; + + csr_write(CSR_TCMCFG, TCM_EN); + + rt_ofw_foreach_child_node(np, tcm_np) + { + int cpuid = 0; + void *tcm_base; + rt_uint64_t addr, size; + const char *string, *node_name; + + if (rt_ofw_get_address(tcm_np, 0, &addr, &size)) + { + continue; + } + + string = node_name = rt_fdt_node_name(tcm_np->full_name); + string += sizeof("core") - 1; + + while (*string >= '0' && *string <= '9') + { + cpuid *= 10; + cpuid += *string - '0'; + + ++string; + } + + tcm_heap[cpuid] = rt_calloc(1, sizeof(struct rt_memheap)); + + if (!tcm_heap[cpuid]) + { + return -RT_ENOMEM; + } + + if (!(tcm_base = rt_ioremap_cached((void *)addr, size))) + { + rt_free(tcm_heap[cpuid]); + return -RT_EIO; + } + + if (rt_memheap_init(tcm_heap[cpuid], node_name, tcm_base, size)) + { + rt_free(tcm_heap[cpuid]); + tcm_heap[cpuid] = RT_NULL; + } + } + + return RT_EOK; +} + +static rt_err_t spacemit_tcm_shutdown(struct rt_platform_device *pdev) +{ + csr_clear(CSR_TCMCFG, TCM_EN); + + return RT_EOK; +} + +static const struct rt_ofw_node_id spacemit_tcm_ofw_ids[] = +{ + { .compatible = "spacemit,k1-x-tcm" }, + { .compatible = "spacemit,k1-pro-tcm" }, + { /* sentinel */ } +}; + +static struct rt_platform_driver spacemit_tcm_driver = +{ + .name = "spacemit-tcm", + .ids = spacemit_tcm_ofw_ids, + + .probe = spacemit_tcm_probe, + .shutdown = spacemit_tcm_shutdown, +}; + +static int spacemit_tcm_drv_register(void) +{ + rt_platform_driver_register(&spacemit_tcm_driver); + + return 0; +} +INIT_SUBSYS_EXPORT(spacemit_tcm_drv_register); diff --git a/bsp/spacemit/dm/soc/sbi-ipi.c b/bsp/spacemit/dm/soc/sbi-ipi.c new file mode 100755 index 00000000000..6ff7bdd319c --- /dev/null +++ b/bsp/spacemit/dm/soc/sbi-ipi.c @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2006-2024, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2024-5-1 GuEe-GUI first version + */ + +#include + +#include + +static int spacemit_sbi_ipi_init(void) +{ + csr_set(CSR_IE, IE_SIE); + + return 0; +} +INIT_BOARD_EXPORT(spacemit_sbi_ipi_init); + +static int spacemit_sbi_ipi_secondary_cpu_init(void) +{ + return spacemit_sbi_ipi_init(); +} +INIT_SECONDARY_CPU_EXPORT(spacemit_sbi_ipi_secondary_cpu_init); diff --git a/bsp/spacemit/dm/soc/spacemit-socinfo.c b/bsp/spacemit/dm/soc/spacemit-socinfo.c new file mode 100755 index 00000000000..62601687c7e --- /dev/null +++ b/bsp/spacemit/dm/soc/spacemit-socinfo.c @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2006-2024, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2024-5-1 GuEe-GUI first version + */ + +#include +#include + +#define DBG_TAG "soc.info" +#define DBG_LVL DBG_INFO +#include + +struct spacemit_soc_info +{ + rt_uint32_t die_id; /* Die/wafer id */ + rt_uint32_t ver_id; /* Die/wafer version */ + rt_uint32_t pack_id; /* Package id */ + rt_uint32_t svtdro; /* Dro vaule */ + rt_uint64_t chipid; /* Chip serial number */ + rt_uint32_t soc_id; /* SoC id */ + char *name; /* Chip name */ +}; + +struct spacemit_soc_id +{ + const char *name; + rt_uint32_t id; +}; + +static const struct spacemit_soc_id soc_ids[] = +{ + { "unknown", 0x00000000 }, + /* k1 serial soc */ + { "M1-8571", 0x36070000 }, + { "K1-6370", 0x36070009 }, + { "K1-6350", 0x36070012 }, + { "K1-6371", 0x36070040 }, + { "M103-6370", 0x36070109 }, + { "M103-6371", 0x36070140 }, +}; + +static const char *soc_id_to_chip_name(rt_uint32_t die_id, rt_uint32_t ver_id, rt_uint32_t pack_id) +{ + rt_uint32_t soc_id = (die_id << 16) | (pack_id); + + for (int i = 0; i < RT_ARRAY_SIZE(soc_ids); ++i) + { + if (soc_id == soc_ids[i].id) + { + return soc_ids[i].name; + } + } + + /* The soc id is unkown */ + return soc_ids[0].name; +} + +static rt_ssize_t socinfo_get_nvparam(struct rt_device *dev, char *cell_name, void *val, rt_size_t size) +{ + rt_ssize_t res; + struct rt_nvmem_cell *cell; + + cell = rt_nvmem_get_cell_by_name(dev, cell_name); + if (rt_is_err(cell)) + { + LOG_E("NVMEM cell get %s failed", cell_name); + return rt_ptr_err(cell); + } + + res = rt_nvmem_cell_read(cell, val, rt_min_t(rt_size_t, cell->bytes, size)); + if (res < 0) + { + LOG_E("NVMEM cell read %s failed", cell_name); + goto _end; + } + +_end: + rt_nvmem_put_cell(cell); + + return res; +} + +static rt_err_t spacemit_socinfo_probe(struct rt_platform_device *pdev) +{ + rt_ssize_t res; + struct rt_device *dev = &pdev->parent; + struct spacemit_soc_info socinfo; + + if ((res = socinfo_get_nvparam(dev, "soc_die_id", + &socinfo.die_id, sizeof(socinfo.die_id))) <= 0) + { + LOG_E("Try to get soc_%s from efuse failed", "die_id"); + } + + if ((res = socinfo_get_nvparam(dev, "soc_ver_id", + &socinfo.ver_id, sizeof(socinfo.ver_id))) <= 0) + { + LOG_E("Try to get soc_%s from efuse failed", "ver_id"); + } + + if ((res = socinfo_get_nvparam(dev, "soc_pack_id", + &socinfo.pack_id, sizeof(socinfo.pack_id))) <= 0) + { + LOG_E("Try to get soc_%s from efuse failed", "pack_id"); + } + + if ((res = socinfo_get_nvparam(dev, "soc_svt_dro", + &socinfo.svtdro, sizeof(socinfo.svtdro))) <= 0) + { + LOG_E("Try to get soc_%s from efuse failed", "svt_dro"); + } + + if ((res = socinfo_get_nvparam(dev, "soc_chip_id", + &socinfo.chipid, sizeof(socinfo.chipid))) <= 0) + { + LOG_E("Try to get soc_%s from efuse failed", "chip_id"); + } + + socinfo.soc_id = (socinfo.die_id << 16) | socinfo.pack_id; + + LOG_I("Revision : %c", (socinfo.ver_id + 'A') & 0xff); + LOG_I("ID : %s", soc_id_to_chip_name(socinfo.die_id, socinfo.ver_id, socinfo.pack_id)); + LOG_I("Serial No: %016llX", socinfo.chipid); + + return RT_EOK; +} + +static const struct rt_ofw_node_id spacemit_socinfo_ofw_ids[] = +{ + { .compatible = "spacemit,socinfo-k1x" }, + { /* sentinel */ } +}; + +static struct rt_platform_driver spacemit_socinfo_driver = +{ + .name = "spacemit-socinfo", + .ids = spacemit_socinfo_ofw_ids, + + .probe = spacemit_socinfo_probe, +}; +RT_PLATFORM_DRIVER_EXPORT(spacemit_socinfo_driver); diff --git a/bsp/spacemit/dm/spi/Kconfig b/bsp/spacemit/dm/spi/Kconfig new file mode 100755 index 00000000000..a72ffb88ba3 --- /dev/null +++ b/bsp/spacemit/dm/spi/Kconfig @@ -0,0 +1,18 @@ +config RT_SPI_K1X_QSPI + bool "K1X QuadSPI" + depends on RT_USING_DM + depends on RT_USING_SPI + depends on RT_USING_QSPI + depends on RT_USING_DMA + depends on RT_USING_PIN + depends on RT_USING_PINCTRL + default n + +config RT_SPI_K1X + bool "K1X SPI" + depends on RT_USING_DM + depends on RT_USING_SPI + depends on RT_USING_DMA + depends on RT_USING_PIN + depends on RT_USING_PINCTRL + default n diff --git a/bsp/spacemit/dm/spi/SConscript b/bsp/spacemit/dm/spi/SConscript new file mode 100755 index 00000000000..040f3f3e3df --- /dev/null +++ b/bsp/spacemit/dm/spi/SConscript @@ -0,0 +1,16 @@ +from building import * + +group = [] +src = [] +cwd = GetCurrentDir() +CPPPATH = [cwd + '/../include'] + +if GetDepend('RT_SPI_K1X_QSPI'): + src += ['spi-k1x-qspi.c'] + +if GetDepend('RT_SPI_K1X'): + src += ['spi-k1x.c'] + +group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') diff --git a/bsp/spacemit/dm/spi/spi-k1x-qspi.c b/bsp/spacemit/dm/spi/spi-k1x-qspi.c new file mode 100755 index 00000000000..0ea31d2a533 --- /dev/null +++ b/bsp/spacemit/dm/spi/spi-k1x-qspi.c @@ -0,0 +1,1384 @@ +/* + * Copyright (c) 2006-2024, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2024-5-1 GuEe-GUI first version + */ + +#include "dev_spi_dm.h" + +#define DBG_TAG "spi.k1x.qspi" +#define DBG_LVL DBG_INFO +#include + +#include +#include +#include +#include + +#define QSPI_WAIT_TIMEOUT 300 /* ms */ +#define QSPI_AUTOSUSPEND_TIMEOUT 2000 +#define K1X_MPMU_ACGR 0xd4051024 + +/* QSPI PMUap register */ +#define PMUA_QSPI_CLK_RES_CTRL 0xd4282860 +#define QSPI_CLK_SEL(x) ((x) << 6) +#define QSPI_CLK_SEL_MASK RT_GENMASK(8, 6) +#define QSPI_CLK_EN RT_BIT(4) +#define QSPI_BUS_CLK_EN RT_BIT(3) +#define QSPI_CLK_RST RT_BIT(1) +#define QSPI_BUS_RST RT_BIT(0) + +/* QSPI memory base */ +#define QSPI_AMBA_BASE(qspi) ((qspi)->memmap_base) +#define QSPI_FLASH_A1_BASE(qspi) QSPI_AMBA_BASE(qspi) +#define QSPI_FLASH_A1_TOP(qspi) (QSPI_FLASH_A1_BASE(qspi) + 0x4000000) +#define QSPI_FLASH_A2_BASE(qspi) QSPI_FLASH_A1_TOP(qspi) +#define QSPI_FLASH_A2_TOP(qspi) (QSPI_FLASH_A2_BASE(qspi) + 0x100000) +#define QSPI_FLASH_B1_BASE(qspi) QSPI_FLASH_A2_TOP(qspi) +#define QSPI_FLASH_B1_TOP(qspi) (QSPI_FLASH_B1_BASE(qspi) + 0x100000) +#define QSPI_FLASH_B2_BASE(qspi) QSPI_FLASH_B1_TOP(qspi) +#define QSPI_FLASH_B2_TOP(qspi) (QSPI_FLASH_B2_BASE(qspi) + 0x100000) + +/* TX/RX/ABH buffer max */ +#define QSPI_RX_BUFF_MAX 128 +#define QSPI_TX_BUFF_MAX 256 +#define QSPI_TX_BUFF_POP_MIN 16 +#define QSPI_AHB_BUFF_MAX_SIZE 512 +#define QSPI_TX_DMA_BURST 16 + +#define QSPI_WAIT_BIT_CLEAR 0 +#define QSPI_WAIT_BIT_SET 1 + +/* QSPI Host Registers used by the driver */ +#define QSPI_MCR 0x00 +#define QSPI_MCR_ISD_MASK RT_GENMASK(19, 16) +#define QSPI_MCR_MDIS_MASK RT_BIT(14) +#define QSPI_MCR_CLR_TXF_MASK RT_BIT(11) +#define QSPI_MCR_CLR_RXF_MASK RT_BIT(10) +#define QSPI_MCR_DDR_EN_MASK RT_BIT(7) +#define QSPI_MCR_END_CFG_MASK RT_GENMASK(3, 2) +#define QSPI_MCR_SWRSTHD_MASK RT_BIT(1) +#define QSPI_MCR_SWRSTSD_MASK RT_BIT(0) + +#define QSPI_TCR 0x04 +#define QSPI_IPCR 0x08 +#define QSPI_IPCR_SEQID(x) ((x) << 24) + +#define QSPI_FLSHCR 0x0c + +#define QSPI_BUF0CR 0x10 +#define QSPI_BUF1CR 0x14 +#define QSPI_BUF2CR 0x18 +#define QSPI_BUF3CR 0x1c +#define QSPI_BUF3CR_ALLMST_MASK RT_BIT(31) +#define QSPI_BUF3CR_ADATSZ(x) ((x) << 8) +#define QSPI_BUF3CR_ADATSZ_MASK RT_GENMASK(15, 8) + +#define QSPI_BFGENCR 0x20 +#define QSPI_BFGENCR_SEQID(x) ((x) << 12) + +#define QSPI_SOCCR 0x24 + +#define QSPI_BUF0IND 0x30 +#define QSPI_BUF1IND 0x34 +#define QSPI_BUF2IND 0x38 + +#define QSPI_SFAR 0x100 +#define QSPI_SFACR 0x104 + +#define QSPI_SMPR 0x108 +#define QSPI_SMPR_DDRSMP_MASK RT_GENMASK(18, 16) +#define QSPI_SMPR_FSDLY_MASK RT_BIT(6) +#define QSPI_SMPR_FSPHS_MASK RT_BIT(5) +#define QSPI_SMPR_FSPHS_CLK (416000000) +#define QSPI_SMPR_HSENA_MASK RT_BIT(0) + +#define QSPI_RBSR 0x10c + +#define QSPI_RBCT 0x110 +#define QSPI_RBCT_WMRK_MASK RT_GENMASK(4, 0) +#define QSPI_RBCT_RXBRD_MASK RT_BIT(8) + +#define QSPI_TBSR 0x150 +#define QSPI_TBDR 0x154 +#define QSPI_TBCT 0x158 +#define QSPI_TX_WMRK (QSPI_TX_DMA_BURST / 4 - 1) + +#define QSPI_SR 0x15c +#define QSPI_SR_BUSY RT_BIT(0) +#define QSPI_SR_IP_ACC_MASK RT_BIT(1) +#define QSPI_SR_AHB_ACC_MASK RT_BIT(2) +#define QSPI_SR_TXFULL RT_BIT(27) + +#define QSPI_FR 0x160 +#define QSPI_FR_TFF_MASK RT_BIT(0) +#define QSPI_FR_IPGEF RT_BIT(4) +#define QSPI_FR_IPIEF RT_BIT(6) +#define QSPI_FR_IPAEF RT_BIT(7) +#define QSPI_FR_IUEF RT_BIT(11) +#define QSPI_FR_ABOF RT_BIT(12) +#define QSPI_FR_AIBSEF RT_BIT(13) +#define QSPI_FR_AITEF RT_BIT(14) +#define QSPI_FR_ABSEF RT_BIT(15) +#define QSPI_FR_RBDF RT_BIT(16) +#define QSPI_FR_RBOF RT_BIT(17) +#define QSPI_FR_ILLINE RT_BIT(23) +#define QSPI_FR_TBUF RT_BIT(26) +#define QSPI_FR_TBFF RT_BIT(27) +#define BUFFER_FR_FLAG (QSPI_FR_ABOF| QSPI_FR_RBOF| QSPI_FR_TBUF) + +#define COMMAND_FR_FLAG (QSPI_FR_ABSEF | QSPI_FR_AITEF | QSPI_FR_AIBSEF | QSPI_FR_IUEF | \ + QSPI_FR_IPAEF |QSPI_FR_IPIEF | QSPI_FR_IPGEF) + +#define QSPI_RSER 0x164 +#define QSPI_RSER_TFIE RT_BIT(0) +#define QSPI_RSER_IPGEIE RT_BIT(4) +#define QSPI_RSER_IPIEIE RT_BIT(6) +#define QSPI_RSER_IPAEIE RT_BIT(7) +#define QSPI_RSER_IUEIE RT_BIT(11) +#define QSPI_RSER_ABOIE RT_BIT(12) +#define QSPI_RSER_AIBSIE RT_BIT(13) +#define QSPI_RSER_AITIE RT_BIT(14) +#define QSPI_RSER_ABSEIE RT_BIT(15) +#define QSPI_RSER_RBDIE RT_BIT(16) +#define QSPI_RSER_RBOIE RT_BIT(17) +#define QSPI_RSER_RBDDE RT_BIT(21) +#define QSPI_RSER_ILLINIE RT_BIT(23) +#define QSPI_RSER_TBFDE RT_BIT(25) +#define QSPI_RSER_TBUIE RT_BIT(26) +#define QSPI_RSER_TBFIE RT_BIT(27) +#define BUFFER_ERROR_INT (QSPI_RSER_ABOIE| QSPI_RSER_RBOIE | QSPI_RSER_TBUIE) + +#define COMMAND_ERROR_INT (QSPI_RSER_ABSEIE | QSPI_RSER_AITIE | QSPI_RSER_AIBSIE | QSPI_RSER_IUEIE | \ + QSPI_RSER_IPAEIE |QSPI_RSER_IPIEIE | QSPI_RSER_IPGEIE) + +#define QSPI_SPNDST 0x168 +#define QSPI_SPTRCLR 0x16c +#define QSPI_SPTRCLR_IPPTRC RT_BIT(8) +#define QSPI_SPTRCLR_BFPTRC RT_BIT(0) + +#define QSPI_SFA1AD 0x180 +#define QSPI_SFA2AD 0x184 +#define QSPI_SFB1AD 0x188 +#define QSPI_SFB2AD 0x18c +#define QSPI_DLPR 0x190 +#define QSPI_RBDR(x) (0x200 + ((x) * 4)) + +#define QSPI_LUTKEY 0x300 +#define QSPI_LUTKEY_VALUE 0x5af05af0 + +#define QSPI_LCKCR 0x304 +#define QSPI_LCKER_LOCK RT_BIT(0) +#define QSPI_LCKER_UNLOCK RT_BIT(1) + +#define QSPI_LUT_BASE 0x310 +/* 16Bytes per sequence */ +#define QSPI_LUT_REG(seqid, i) (QSPI_LUT_BASE + (seqid) * 16 + (i) * 4) + +/* + * QSPI Sequence index. + * index 0 is preset at boot for AHB read, + * index 1 is used for other command. + */ +#define SEQID_LUT_AHBREAD_ID 0 +#define SEQID_LUT_SHARED_ID 1 + +/* QSPI Instruction set for the LUT register */ +#define LUT_INSTR_STOP 0 +#define LUT_INSTR_CMD 1 +#define LUT_INSTR_ADDR 2 +#define LUT_INSTR_DUMMY 3 +#define LUT_INSTR_MODE 4 +#define LUT_INSTR_MODE2 5 +#define LUT_INSTR_MODE4 6 +#define LUT_INSTR_READ 7 +#define LUT_INSTR_WRITE 8 +#define LUT_INSTR_JMP_ON_CS 9 +#define LUT_INSTR_ADDR_DDR 10 +#define LUT_INSTR_MODE_DDR 11 +#define LUT_INSTR_MODE2_DDR 12 +#define LUT_INSTR_MODE4_DDR 13 +#define LUT_INSTR_READ_DDR 14 +#define LUT_INSTR_WRITE_DDR 15 +#define LUT_INSTR_DATA_LEARN 16 + +/* + * The PAD definitions for LUT register. + * + * The pad stands for the number of IO lines [0:3]. + * For example, the quad read needs four IO lines, + * so you should use LUT_PAD(4). + */ +#define LUT_PAD(x) (fls(x) - 1) + +/* + * One sequence must be consisted of 4 LUT enteries(16Bytes). + * LUT entries with the following register layout: + * b'31 b'0 + * --------------------------------------------------------------------------- + * |INSTR1[15~10]|PAD1[9~8]|OPRND1[7~0] | INSTR0[15~10]|PAD0[9~8]|OPRND0[7~0]| + * --------------------------------------------------------------------------- + */ +#define LUT_DEF(idx, ins, pad, opr) ((((ins) << 10) | ((pad) << 8) | (opr)) << (((idx) & 0x1) * 16)) + +#define READ_FROM_CACHE_OP 0x03 +#define READ_FROM_CACHE_OP_Fast 0x0b +#define READ_FROM_CACHE_OP_X2 0x3b +#define READ_FROM_CACHE_OP_X4 0x6b +#define READ_FROM_CACHE_OP_DUALIO 0xbb +#define READ_FROM_CACHE_OP_QUADIO 0xeb + +enum qpsi_cs +{ + QSPI_CS_A1 = 0, + QSPI_CS_A2, + QSPI_CS_B1, + QSPI_CS_B2, + QSPI_CS_MAX, + QSPI_DEFAULT_CS = QSPI_CS_A1, +}; + +enum qpsi_mode +{ + QSPI_NORMAL_MODE = 0, + QSPI_DISABLE_MODE, + QSPI_STOP_MODE, +}; + +struct k1x_qspi +{ + struct rt_spi_bus parent; + + struct rt_clk *clk; + struct rt_clk *bus_clk; + struct rt_reset_control *rstc; + + int irq; + void *io_map; + rt_ubase_t io_phys; + + void *ahb_map; + rt_ubase_t memmap_base; + rt_ubase_t memmap_size; + + rt_uint32_t sfa1ad; + rt_uint32_t sfa2ad; + rt_uint32_t sfb1ad; + rt_uint32_t sfb2ad; + + void *pmuap_addr; + void *mpmu_acgr; + rt_uint32_t pmuap_reg; + rt_uint32_t mpmu_acgr_reg; + + rt_uint32_t rx_buf_size; + rt_uint32_t tx_buf_size; + rt_uint32_t ahb_buf_size; + rt_uint32_t ahb_read_enable; + rt_uint32_t tx_unit_size; + rt_uint32_t rx_unit_size; + + rt_uint32_t cmd_interrupt; + rt_uint32_t fr_error_flag; + + rt_uint32_t cs_selected; + rt_uint32_t max_hz; + rt_uint32_t endian_xchg; + rt_uint32_t dma_enable; + + rt_uint32_t tx_wmrk; + rt_uint32_t tx_dma_enable; + rt_uint32_t rx_dma_enable; + + struct rt_dma_slave_config rx_config; + struct rt_dma_slave_config tx_config; + struct rt_dma_slave_transfer rx_transfer; + struct rt_dma_slave_transfer tx_transfer; + + struct rt_dma_chan *dma_rx; + struct rt_dma_chan *dma_tx; + + struct rt_completion cmd_completion; + struct rt_completion dma_completion; +}; + +#define raw_to_k1x_qspi(raw) rt_container_of(raw, struct k1x_qspi, parent) + +static void qspi_writel(struct k1x_qspi *qspi, rt_uint32_t val, void *addr) +{ + if (qspi->endian_xchg) + { + HWREG32(addr) = rt_cpu_to_be32(val); + } + else + { + HWREG32(addr) = val; + } +} + +static rt_uint32_t qspi_readl(struct k1x_qspi *qspi, void *addr) +{ + if (qspi->endian_xchg) + { + return rt_be32_to_cpu(HWREG32(addr)); + } + else + { + return HWREG32(addr); + } +} + +static rt_err_t k1x_qspi_readl_poll_tout(struct k1x_qspi *qspi, void *base, + rt_uint32_t mask, rt_uint32_t timeout_us, rt_uint8_t wait_set) +{ + rt_uint32_t reg; + + if (qspi->endian_xchg) + { + mask = __builtin_bswap32(mask); + } + + if (wait_set) + { + return readl_poll_timeout(base, reg, (reg & mask), 10, timeout_us); + } + else + { + return readl_poll_timeout(base, reg, !(reg & mask), 10, timeout_us); + } +} + +static void qspi_reset(struct k1x_qspi *qspi) +{ + rt_uint32_t reg; + + /* QSPI_SR[QSPI_SR_BUSY] must be 0 */ + if (k1x_qspi_readl_poll_tout(qspi, qspi->io_map + QSPI_SR, + QSPI_SR_BUSY, QSPI_WAIT_TIMEOUT*1000, QSPI_WAIT_BIT_CLEAR)) + { + return; + } + + /* qspi softreset first */ + reg = qspi_readl(qspi, qspi->io_map + QSPI_MCR); + reg |= QSPI_MCR_SWRSTHD_MASK | QSPI_MCR_SWRSTSD_MASK; + qspi_writel(qspi, reg, qspi->io_map + QSPI_MCR); + reg = qspi_readl(qspi, qspi->io_map + QSPI_MCR); + + if ((reg & 0x3) != 0x3) + { + LOG_I("Reset ignored %#x", reg); + } + + rt_hw_us_delay(1); + reg &= ~(QSPI_MCR_SWRSTHD_MASK | QSPI_MCR_SWRSTSD_MASK); + qspi_writel(qspi, reg, qspi->io_map + QSPI_MCR); +} + +static void qspi_enter_mode(struct k1x_qspi *qspi, rt_uint32_t mode) +{ + rt_uint32_t mcr; + + mcr = qspi_readl(qspi, qspi->io_map + QSPI_MCR); + if (mode == QSPI_NORMAL_MODE) + { + mcr &= ~QSPI_MCR_MDIS_MASK; + } + else if (mode == QSPI_DISABLE_MODE) + { + mcr |= QSPI_MCR_MDIS_MASK; + } + qspi_writel(qspi, mcr, qspi->io_map + QSPI_MCR); +} + +static void qspi_write_sfar(struct k1x_qspi *qspi, rt_uint32_t val) +{ + /* QSPI_SR[IP_ACC] must be 0 */ + if (k1x_qspi_readl_poll_tout(qspi, qspi->io_map + QSPI_SR, + QSPI_SR_IP_ACC_MASK, QSPI_WAIT_TIMEOUT*1000, QSPI_WAIT_BIT_CLEAR)) + { + return; + } + qspi_writel(qspi, val, qspi->io_map + QSPI_SFAR); +} + +/* + * IP Command Trigger could not be executed Error Flag may happen for write + * access to RBCT/SFAR register, need retry for these two register + */ +static void qspi_write_rbct(struct k1x_qspi *qspi, rt_uint32_t val) +{ + /* QSPI_SR[IP_ACC] must be 0 */ + if (k1x_qspi_readl_poll_tout(qspi, qspi->io_map + QSPI_SR, + QSPI_SR_IP_ACC_MASK, QSPI_WAIT_TIMEOUT*1000, QSPI_WAIT_BIT_CLEAR)) + { + return; + } + + qspi_writel(qspi, val, qspi->io_map + QSPI_RBCT); +} + +/* + * If the slave device content being changed by Write/Erase, need to + * invalidate the AHB buffer. This can be achieved by doing the reset + * of controller after setting MCR0[SWRESET] bit. + */ +rt_inline void k1x_qspi_invalid(struct k1x_qspi *qspi) +{ + rt_uint32_t reg; + + reg = qspi_readl(qspi, qspi->io_map + QSPI_MCR); + reg |= QSPI_MCR_SWRSTHD_MASK | QSPI_MCR_SWRSTSD_MASK; + qspi_writel(qspi, reg, qspi->io_map + QSPI_MCR); + + /* + * The minimum delay : 1 AHB + 2 SFCK clocks. + * Delay 1 us is enough. + */ + rt_hw_us_delay(1); + + reg &= ~(QSPI_MCR_SWRSTHD_MASK | QSPI_MCR_SWRSTSD_MASK); + qspi_writel(qspi, reg, qspi->io_map + QSPI_MCR); +} + +static void k1x_qspi_prepare_lut(struct k1x_qspi *qspi, + struct rt_qspi_message *qxfer, rt_uint32_t seq_id) +{ + int lutidx = 0; + rt_uint32_t lutval[4] = {}; + + /* qspi cmd */ + lutval[0] |= LUT_DEF(lutidx, LUT_INSTR_CMD, + LUT_PAD(qxfer->instruction.qspi_lines), qxfer->instruction.content); + lutidx++; + + /* addr bytes */ + if (qxfer->address.size) + { + lutval[lutidx / 2] |= LUT_DEF(lutidx, LUT_INSTR_ADDR, + LUT_PAD(qxfer->address.qspi_lines), qxfer->address.size * 8); + lutidx++; + } + + /* dummy bytes, if needed */ + if (qxfer->alternate_bytes.size) + { + lutval[lutidx / 2] |= LUT_DEF(lutidx, LUT_INSTR_DUMMY, + LUT_PAD(qxfer->alternate_bytes.qspi_lines), + qxfer->alternate_bytes.size * 8 / qxfer->alternate_bytes.qspi_lines); + lutidx++; + } + + /* read/write data bytes */ + if (qxfer->parent.length) + { + lutval[lutidx / 2] |= LUT_DEF(lutidx, + qxfer->parent.recv_buf ? LUT_INSTR_READ : LUT_INSTR_WRITE, + LUT_PAD(qxfer->qspi_data_lines), 0); + lutidx++; + } + + /* stop condition. */ + lutval[lutidx / 2] |= LUT_DEF(lutidx, LUT_INSTR_STOP, 0, 0); + + /* unlock LUT */ + qspi_writel(qspi, QSPI_LUTKEY_VALUE, qspi->io_map + QSPI_LUTKEY); + qspi_writel(qspi, QSPI_LCKER_UNLOCK, qspi->io_map + QSPI_LCKCR); + + /* fill LUT register */ + for (int i = 0; i < RT_ARRAY_SIZE(lutval); ++i) + { + qspi_writel(qspi, lutval[i], qspi->io_map + QSPI_LUT_REG(seq_id, i)); + } + + /* lock LUT */ + qspi_writel(qspi, QSPI_LUTKEY_VALUE, qspi->io_map + QSPI_LUTKEY); + qspi_writel(qspi, QSPI_LCKER_LOCK, qspi->io_map + QSPI_LCKCR); +} + +static void k1x_qspi_enable_interrupt(struct k1x_qspi *qspi, rt_uint32_t val) +{ + rt_uint32_t resr = 0; + + resr = qspi_readl(qspi, qspi->io_map + QSPI_RSER); + resr |= val; + qspi_writel(qspi, resr, qspi->io_map + QSPI_RSER); +} + +static void k1x_qspi_disable_interrupt(struct k1x_qspi *qspi, rt_uint32_t val) +{ + rt_uint32_t resr = 0; + + resr = qspi_readl(qspi, qspi->io_map + QSPI_RSER); + resr &= ~val; + qspi_writel(qspi, resr, qspi->io_map + QSPI_RSER); +} + +static void k1x_qspi_dma_callback(struct rt_dma_chan *chan, rt_size_t size) +{ + struct k1x_qspi *qspi = chan->priv; + + rt_completion_done(&qspi->dma_completion); +} + +static rt_err_t k1x_qspi_tx_dma_exec(struct k1x_qspi *qspi, struct rt_qspi_message *qxfer) +{ + rt_err_t err; + + qspi->tx_config.src_addr = (rt_ubase_t)rt_kmem_v2p((void *)qxfer->parent.send_buf); + + if ((err = rt_dma_chan_config(qspi->dma_tx, &qspi->tx_config))) + { + return err; + } + + qspi->tx_transfer.buffer_len = qxfer->parent.length; + qspi->tx_transfer.src_addr = qspi->tx_config.src_addr; + + if ((err = rt_dma_prep_single(qspi->dma_tx, &qspi->tx_transfer))) + { + return err; + } + + if ((err = rt_dma_chan_start(qspi->dma_tx))) + { + return err; + } + + return rt_completion_wait(&qspi->dma_completion, RT_WAITING_FOREVER); +} + +static rt_err_t k1x_qspi_rx_dma_exec(struct k1x_qspi *qspi, struct rt_qspi_message *qxfer) +{ + rt_err_t err; + + qspi->rx_config.dst_addr = (rt_ubase_t)rt_kmem_v2p(qxfer->parent.recv_buf); + + if ((err = rt_dma_chan_config(qspi->dma_rx, &qspi->rx_config))) + { + return err; + } + + qspi->rx_transfer.buffer_len = qxfer->parent.length; + qspi->rx_transfer.dst_addr = qspi->rx_config.dst_addr; + + if ((err = rt_dma_prep_memcpy(qspi->dma_rx, &qspi->rx_transfer))) + { + return err; + } + + if ((err = rt_dma_chan_start(qspi->dma_rx))) + { + return err; + } + + return rt_completion_wait(&qspi->dma_completion, + rt_tick_from_millisecond(qxfer->parent.length)); +} + +static rt_err_t k1x_qspi_ahb_read(struct k1x_qspi *qspi, struct rt_qspi_message *qxfer) +{ + rt_err_t err; + rt_uint32_t len = qxfer->parent.length, from = qxfer->address.content; + + /* Read out the data directly from the AHB buffer. */ + if (from + len > qspi->memmap_size) + { + return -RT_ENOSYS; + } + + /* Firstly try the DMA */ + if (qspi->rx_dma_enable) + { + if (!(err = k1x_qspi_rx_dma_exec(qspi, qxfer))) + { + return RT_EOK; + } + else + { + LOG_D("Read rx dma fallback to memcpy read"); + } + } + + rt_memcpy(qxfer->parent.recv_buf, (qspi->ahb_map + qxfer->address.content), len); + + return RT_EOK; +} + +static rt_err_t k1x_qspi_fill_txfifo(struct k1x_qspi *qspi, struct rt_qspi_message *qxfer) +{ + int i; + void *base = qspi->io_map; + rt_uint32_t val, tbsr, wait_cnt; + rt_size_t length = qxfer->parent.length; + + if (!qspi->tx_dma_enable || (length % QSPI_TX_BUFF_POP_MIN)) + { + qspi->tx_wmrk = 0; + for (i = 0; i < RT_ALIGN_DOWN(length, 4); i += 4) + { + rt_memcpy(&val, qxfer->parent.send_buf + i, 4); + qspi_writel(qspi, val, base + QSPI_TBDR); + } + + if (i < length) + { + rt_memcpy(&val, qxfer->parent.send_buf + i, length - i); + qspi_writel(qspi, val, base + QSPI_TBDR); + } + + /* + * There must be at least 128bit data available in TX FIFO + * for any pop operation otherwise QSPI_FR[TBUF] will be set + */ + for (i = length; i < RT_ALIGN_DOWN(length + (QSPI_TX_BUFF_POP_MIN - 1), QSPI_TX_BUFF_POP_MIN); i += 4) + { + qspi_writel(qspi, 0, base + QSPI_TBDR); + } + } + else + { + /* + * Note that the number of bytes per DMA loop is determined + * by thee size of the QSPI_TBCT[WMRK]. + * bytes per DMA loop = (QSPI_TBCT[WMRK] + 1) * 4. + * set QSPI_TX_WMRK as the TX watermark. + */ + qspi->tx_wmrk = QSPI_TX_WMRK; + qspi_writel(qspi, qspi->tx_wmrk, base + QSPI_TBCT); + + /* Config DMA channel and start */ + if (k1x_qspi_tx_dma_exec(qspi, qxfer)) + { + qspi->tx_wmrk = 0; + LOG_E("Failed to start tx dma"); + return -RT_EIO; + } + + /* Enable DMA request */ + k1x_qspi_enable_interrupt(qspi, QSPI_RSER_TBFDE); + + /* + * before trigger qspi to send data to external bus, TX bufer + * need to have some data, or underrun error may happen. + * DMA need some time to write data to TX buffer, so add + * a delay here for this requirement. + */ + wait_cnt = 0; + tbsr = qspi_readl(qspi, base + QSPI_TBSR); + + while (4 * (tbsr >> 16) < rt_min_t(unsigned int, qspi->tx_buf_size, length)) + { + rt_hw_us_delay(1); + tbsr = qspi_readl(qspi, base + QSPI_TBSR); + + if (wait_cnt++ >= 100) + { + rt_thread_mdelay(100); + tbsr = qspi_readl(qspi, base + QSPI_TBSR); + + if (4 * (tbsr >> 16) < rt_min_t(unsigned int, qspi->tx_buf_size, length)) + { + LOG_E("Failed to fill tx dma"); + /* Disable all interrupts */ + qspi_writel(qspi, 0, qspi->io_map + QSPI_RSER); + rt_dma_chan_stop(qspi->dma_tx); + qspi->tx_wmrk = 0; + + return -RT_EIO; + } + else + { + break; + } + } + } + } + + return RT_EOK; +} + +static void k1x_qspi_read_rxfifo(struct k1x_qspi *qspi, struct rt_qspi_message *qxfer) +{ + int i; + rt_uint32_t val; + rt_uint8_t *buf; + void *base = qspi->io_map; + rt_size_t length = qxfer->parent.length; + + buf = qxfer->parent.recv_buf; + + for (i = 0; i < RT_ALIGN_DOWN(length, 4); i += 4) + { + val = qspi_readl(qspi, base + QSPI_RBDR(i / 4)); + rt_memcpy(buf + i, &val, 4); + } + + if (i < length) + { + val = qspi_readl(qspi, base + QSPI_RBDR(i / 4)); + rt_memcpy(buf + i, &val, length - i); + } +} + +static rt_err_t k1x_qspi_do_op(struct k1x_qspi *qspi, struct rt_qspi_message *qxfer) +{ + rt_uint32_t mcr; + rt_err_t err = RT_EOK; + void *base = qspi->io_map; + rt_size_t length = qxfer->parent.length; + + if (qspi->cmd_interrupt) + { + k1x_qspi_enable_interrupt(qspi, QSPI_RSER_TFIE | BUFFER_ERROR_INT | COMMAND_ERROR_INT); + } + + /* Trigger LUT */ + qspi_writel(qspi, length | QSPI_IPCR_SEQID(SEQID_LUT_SHARED_ID), base + QSPI_IPCR); + + /* Wait for the transaction complete */ + if (qspi->cmd_interrupt) + { + rt_completion_wait(&qspi->cmd_completion, RT_WAITING_FOREVER); + } + else + { + err = k1x_qspi_readl_poll_tout(qspi, base + QSPI_FR, QSPI_FR_TFF_MASK, + QSPI_WAIT_TIMEOUT * 1000, QSPI_WAIT_BIT_SET); + } + + if (err) + { + goto _tx_dma_unmap; + } + + if ((err = k1x_qspi_readl_poll_tout(qspi, base + QSPI_SR, QSPI_SR_BUSY, + QSPI_WAIT_TIMEOUT * 1000, QSPI_WAIT_BIT_CLEAR))) + { + goto _tx_dma_unmap; + } + + /* Read RX buffer for IP command read */ + if (length && qxfer->parent.recv_buf) + { + k1x_qspi_read_rxfifo(qspi, qxfer); + } + + if (qspi->fr_error_flag & QSPI_FR_TBUF) + { + /* Abort current dma transfer */ + if (qspi->tx_dma_enable) + { + rt_dma_chan_stop(qspi->dma_tx); + } + + /* Clear TX buf */ + mcr = qspi_readl(qspi, qspi->io_map + QSPI_MCR); + mcr |= QSPI_MCR_CLR_TXF_MASK ; + qspi_writel(qspi, mcr, qspi->io_map + QSPI_MCR); + + /* Reduce tx unit size and retry */ + if (qspi->tx_dma_enable) + { + qspi->tx_unit_size = qspi->tx_buf_size; + } + + err = -RT_EIO; + } + else + { + if (qspi->tx_dma_enable) + { + qspi->tx_unit_size = qspi->tx_buf_size; + } + } + +_tx_dma_unmap: + if (qspi->tx_wmrk) + { + /* Disable TBFDE interrupt and dma unmap */ + k1x_qspi_disable_interrupt(qspi, QSPI_RSER_TBFDE); + qspi->tx_wmrk = 0; + } + + return err; +} + +static rt_bool_t is_read_from_cache_opcode(rt_uint8_t opcode) +{ + return ((opcode == READ_FROM_CACHE_OP) || + (opcode == READ_FROM_CACHE_OP_Fast) || + (opcode == READ_FROM_CACHE_OP_X2) || + (opcode == READ_FROM_CACHE_OP_X4) || + (opcode == READ_FROM_CACHE_OP_DUALIO) || + (opcode == READ_FROM_CACHE_OP_QUADIO)); +} + +static rt_err_t k1x_qspi_configure(struct rt_spi_device *device, + struct rt_spi_configuration *conf) +{ + return RT_EOK; +} + +static rt_ssize_t k1x_qspi_xfer(struct rt_spi_device *device, + struct rt_spi_message *xfer) +{ + void *base; + rt_err_t err = RT_EOK; + rt_uint32_t mask, reg; + struct k1x_qspi *qspi = raw_to_k1x_qspi(device->bus); + struct rt_qspi_message *qxfer = rt_container_of(xfer, struct rt_qspi_message, parent); + + base = qspi->io_map; + + /* Wait for controller being ready */ + mask = QSPI_SR_BUSY | QSPI_SR_IP_ACC_MASK | QSPI_SR_AHB_ACC_MASK; + if ((err = k1x_qspi_readl_poll_tout(qspi, + base + QSPI_SR, mask, QSPI_WAIT_TIMEOUT * 1000, QSPI_WAIT_BIT_CLEAR))) + { + LOG_E("Controller not ready"); + return err; + } + + /* Clear TX/RX buffer before transaction */ + reg = qspi_readl(qspi, base + QSPI_MCR); + reg |= QSPI_MCR_CLR_TXF_MASK | QSPI_MCR_CLR_RXF_MASK; + qspi_writel(qspi, reg, base + QSPI_MCR); + + /* + * Reset the sequence pointers whenever the sequence ID is changed by + * updating the SEDID filed in QSPI_IPCR OR QSPI_BFGENCR. + */ + reg = qspi_readl(qspi, base + QSPI_SPTRCLR); + reg |= (QSPI_SPTRCLR_IPPTRC | QSPI_SPTRCLR_BFPTRC); + qspi_writel(qspi, reg, base + QSPI_SPTRCLR); + + /* Set the flash address into the QSPI_SFAR */ + qspi_write_sfar(qspi, qspi->memmap_base + qxfer->address.content); + + /* Clear QSPI_FR before trigger LUT command */ + if ((reg = qspi_readl(qspi, base + QSPI_FR))) + { + qspi_writel(qspi, reg, base + QSPI_FR); + } + qspi->fr_error_flag = 0; + + /* + * Read page command 13h must be done by IP command. + * read from cache through the AHB bus by accessing the mapped memory. + * In all other cases we use IP commands to access the flash. + */ + if (xfer->length > (qspi->rx_buf_size - 4) && + xfer->recv_buf && qspi->ahb_read_enable && + is_read_from_cache_opcode(qxfer->instruction.content)) + { + k1x_qspi_prepare_lut(qspi, qxfer, SEQID_LUT_AHBREAD_ID); + err = k1x_qspi_ahb_read(qspi, qxfer); + } + else + { + /* IP command */ + k1x_qspi_prepare_lut(qspi, qxfer, SEQID_LUT_SHARED_ID); + + if (xfer->length && xfer->send_buf) + { + err = k1x_qspi_fill_txfifo(qspi, qxfer); + } + if (!err) + { + err = k1x_qspi_do_op(qspi, qxfer); + } + } + + /* Invalidate the data in the AHB buffer. */ + k1x_qspi_invalid(qspi); + + return err ? : xfer->length; +} + +static const struct rt_spi_ops k1x_qspi_ops = +{ + .configure = k1x_qspi_configure, + .xfer = k1x_qspi_xfer, +}; + +static void k1x_qspi_isr(int irq, void *param) +{ + rt_uint32_t fr; + struct k1x_qspi *qspi = param; + + /* Disable all interrupts */ + qspi_writel(qspi, 0, qspi->io_map + QSPI_RSER); + + fr = qspi_readl(qspi, qspi->io_map + QSPI_FR); + + /* Check QSPI_FR error flag */ + if (fr & (COMMAND_FR_FLAG | BUFFER_FR_FLAG)) + { + qspi->fr_error_flag = fr & (COMMAND_FR_FLAG | BUFFER_FR_FLAG); + + if (fr & QSPI_FR_IPGEF) + { + LOG_E("IP command trigger during AHB grant"); + } + if (fr & QSPI_FR_IPIEF) + { + LOG_E("IP command trigger could not be executed"); + } + if (fr & QSPI_FR_IPAEF) + { + LOG_E("IP command trigger during AHB access"); + } + if (fr & QSPI_FR_IUEF) + { + LOG_E("IP command usage error"); + } + if (fr & QSPI_FR_AIBSEF) + { + LOG_E("AHB illegal burst size error"); + } + if (fr & QSPI_FR_AITEF) + { + LOG_E("AHB illegal trancaction error"); + } + if (fr & QSPI_FR_ABSEF) + { + LOG_E("AHB sequence error"); + } + + if (fr & QSPI_FR_TBUF) + { + /* Disable TBFDE interrupt */ + k1x_qspi_disable_interrupt(qspi, QSPI_RSER_TBFDE); + LOG_E("TX buffer underrun"); + } + if (fr & QSPI_FR_RBOF) + { + LOG_E("RX buffer overflow"); + } + if (fr & QSPI_FR_ABOF) + { + LOG_E("AHB buffer overflow"); + } + } + + if (qspi->cmd_interrupt && (fr & (QSPI_FR_TFF_MASK | COMMAND_FR_FLAG | BUFFER_FR_FLAG))) + { + rt_completion_done(&qspi->cmd_completion); + } +} + +static void k1x_qspi_free(struct k1x_qspi *qspi, struct rt_device *dev) +{ + /* Set disable mode */ + qspi_writel(qspi, QSPI_MCR_MDIS_MASK, qspi->io_map + QSPI_MCR); + qspi_writel(qspi, 0x0, qspi->io_map + QSPI_RSER); + + if (qspi->io_map) + { + rt_iounmap(qspi->io_map); + } + + if (qspi->pmuap_addr) + { + rt_iounmap(qspi->pmuap_addr); + } + + if (!rt_is_err_or_null(qspi->dma_tx)) + { + rt_dma_chan_release(qspi->dma_tx); + } + + if (!rt_is_err_or_null(qspi->dma_rx)) + { + rt_dma_chan_release(qspi->dma_rx); + } + + if (!rt_is_err_or_null(qspi->rstc)) + { + rt_reset_control_assert(qspi->rstc); + rt_reset_control_put(qspi->rstc); + } + + if (!rt_is_err_or_null(qspi->clk)) + { + rt_clk_disable_unprepare(qspi->clk); + rt_clk_put(qspi->clk); + } + + if (!rt_is_err_or_null(qspi->bus_clk)) + { + rt_clk_disable_unprepare(qspi->bus_clk); + rt_clk_put(qspi->bus_clk); + } + + rt_free(qspi); +} + +static rt_err_t k1x_qspi_probe(struct rt_platform_device *pdev) +{ + void *base; + rt_err_t err; + rt_uint32_t reg; + const char *bus_name; + rt_uint64_t addr, size; + rt_uint32_t qspi_bus_num = 0; + struct rt_device *dev = &pdev->parent; + struct k1x_qspi *qspi = rt_calloc(1, sizeof(*qspi)); + + if (!qspi) + { + return -RT_ENOMEM; + } + + if (!(qspi->io_map = rt_dm_dev_iomap_by_name(dev, "qspi-base"))) + { + err = -RT_EIO; + goto _fail; + } + qspi->io_phys = (rt_ubase_t)rt_kmem_v2p(qspi->io_map); + + if ((err = rt_dm_dev_get_address_by_name(dev, "qspi-mmap", &addr, &size))) + { + err = -RT_EIO; + goto _fail; + } + qspi->ahb_map = rt_ioremap((void *)addr, size); + qspi->memmap_base = addr; + qspi->memmap_size = size; + + if ((qspi->irq = rt_dm_dev_get_irq(dev, 0)) < 0) + { + err = qspi->irq; + goto _fail; + } + + if ((err = rt_dm_dev_prop_read_u32(dev, "k1x,qspi-freq", &qspi->max_hz))) + { + goto _fail; + } + + if (rt_dm_dev_prop_read_u32(dev, "k1x,qspi-sfa1ad", &qspi->sfa1ad)) + { + qspi->sfa1ad = QSPI_FLASH_A1_TOP(qspi); + } + else + { + qspi->sfa1ad += qspi->memmap_base; + } + if (rt_dm_dev_prop_read_u32(dev, "k1x,qspi-sfa2ad", &qspi->sfa2ad)) + { + qspi->sfa2ad = QSPI_FLASH_A2_TOP(qspi); + } + else + { + qspi->sfa2ad += qspi->sfa1ad; + } + if (rt_dm_dev_prop_read_u32(dev, "k1x,qspi-sfb1ad", &qspi->sfb1ad)) + { + qspi->sfb1ad = QSPI_FLASH_B1_TOP(qspi); + } + else + { + qspi->sfb1ad = qspi->sfa2ad; + } + if (rt_dm_dev_prop_read_u32(dev, "k1x,qspi-sfb2ad", &qspi->sfb2ad)) + { + qspi->sfb2ad = QSPI_FLASH_B2_TOP(qspi); + } + else + { + qspi->sfb2ad += qspi->sfb1ad; + } + + /* map QSPI PMUap register address */ + if (rt_dm_dev_prop_read_u32(dev, "k1x,qspi-pmuap-reg", &qspi->pmuap_reg)) + { + qspi->pmuap_reg = PMUA_QSPI_CLK_RES_CTRL; + } + qspi->pmuap_addr = rt_ioremap((void *)(rt_ubase_t)qspi->pmuap_reg, 4); + + if (rt_dm_dev_prop_read_u32(dev, "k1x,qspi-mpmu-acgr-reg", &qspi->mpmu_acgr_reg)) + { + qspi->mpmu_acgr_reg = K1X_MPMU_ACGR; + } + qspi->mpmu_acgr = rt_ioremap((void *)(rt_ubase_t)qspi->mpmu_acgr_reg, 4); + + if (rt_dm_dev_prop_read_u32(dev, "k1x,qspi-rx-buf", &qspi->rx_buf_size)) + { + qspi->rx_buf_size = QSPI_RX_BUFF_MAX; + } + + if (rt_dm_dev_prop_read_u32(dev, "k1x,qspi-tx-buf", &qspi->tx_buf_size)) + { + qspi->tx_buf_size = QSPI_TX_BUFF_MAX; + } + + if (rt_dm_dev_prop_read_u32(dev, "k1x,qspi-ahb-buf", &qspi->ahb_buf_size)) + { + qspi->ahb_buf_size = QSPI_AHB_BUFF_MAX_SIZE; + } + + if (rt_dm_dev_prop_read_u32(dev, "k1x,qspi-ahb-enable", &qspi->ahb_read_enable)) + { + qspi->ahb_read_enable = 1; + } + + if (rt_dm_dev_prop_read_u32(dev, "k1x,qspi-interrupt", &qspi->cmd_interrupt)) + { + qspi->cmd_interrupt = 1; + } + + if (rt_dm_dev_prop_read_u32(dev, "k1x,qspi-endian-xchg", &qspi->endian_xchg)) + { + qspi->endian_xchg = 0; + } + + if (rt_dm_dev_prop_read_u32(dev, "k1x,qspi-cs", &qspi->cs_selected)) + { + qspi->cs_selected = QSPI_DEFAULT_CS; + } + + if (rt_dm_dev_prop_read_u32(dev, "k1x,qspi-tx-dma", &qspi->tx_dma_enable)) + { + qspi->tx_dma_enable = 0; + } + + if (rt_dm_dev_prop_read_u32(dev, "k1x,qspi-rx-dma", &qspi->rx_dma_enable)) + { + qspi->rx_dma_enable = 0; + } + + rt_dm_dev_prop_read_u32(dev, "k1x,qspi-id", &qspi_bus_num); + + /* Prepare DMA */ + if (qspi->rx_dma_enable) + { + qspi->dma_rx = rt_dma_chan_request(dev, RT_NULL); + + if (!rt_is_err_or_null(qspi->dma_rx)) + { + qspi->dma_rx->callback = k1x_qspi_dma_callback; + + qspi->rx_config.direction = RT_DMA_MEM_TO_MEM; + qspi->rx_config.src_addr = qspi->memmap_base; + + qspi->rx_transfer.src_addr = qspi->rx_config.src_addr; + } + else + { + qspi->dma_rx = RT_NULL; + qspi->rx_dma_enable = 0; + } + } + + if (qspi->tx_dma_enable) + { + qspi->dma_tx = rt_dma_chan_request(dev, "tx-dma"); + + if (!rt_is_err_or_null(qspi->dma_tx)) + { + qspi->dma_tx->callback = k1x_qspi_dma_callback; + + qspi->tx_config.direction = RT_DMA_MEM_TO_DEV; + qspi->tx_config.dst_addr_width = RT_DMA_SLAVE_BUSWIDTH_4_BYTES; + qspi->tx_config.dst_addr = qspi->io_phys + QSPI_TBDR - 4; + qspi->tx_config.dst_maxburst = QSPI_TX_DMA_BURST; + + qspi->tx_transfer.dst_addr = qspi->tx_config.dst_addr; + } + else + { + qspi->dma_tx = RT_NULL; + qspi->tx_dma_enable = 0; + } + } + + if (qspi->dma_tx || qspi->dma_rx) + { + rt_completion_init(&qspi->dma_completion); + } + + if (qspi->tx_dma_enable) + { + qspi->tx_unit_size = qspi->tx_buf_size; + } + else + { + qspi->tx_unit_size = qspi->tx_buf_size; + } + + if (qspi->ahb_read_enable) + { + qspi->rx_unit_size = 4 * SIZE_KB; + } + else + { + qspi->rx_unit_size = qspi->rx_buf_size; + } + + /* QSPI init start */ + base = qspi->io_map; + + qspi->rstc = rt_reset_control_get_by_index(dev, 0); + if (rt_is_err(qspi->rstc)) + { + err = rt_ptr_err(qspi->rstc); + goto _fail; + } + + rt_reset_control_assert(qspi->rstc); + + /* Set PMUap */ + qspi->clk = rt_clk_get_by_name(dev, "qspi_clk"); + if (rt_is_err(qspi->clk)) + { + err = rt_ptr_err(qspi->clk); + goto _fail; + } + + qspi->bus_clk = rt_clk_get_by_name(dev, "qspi_bus_clk"); + if (rt_is_err(qspi->bus_clk)) + { + err = rt_ptr_err(qspi->bus_clk); + goto _fail; + } + + if ((err = rt_clk_set_rate(qspi->clk, qspi->max_hz))) + { + LOG_E("Fail to set clk"); + goto _fail; + } + + if ((err = rt_clk_prepare_enable(qspi->clk))) + { + LOG_E("Fail to enable clk"); + goto _fail; + } + + rt_clk_prepare_enable(qspi->bus_clk); + + rt_reset_control_deassert(qspi->rstc); + + /* Rest qspi */ + qspi_reset(qspi); + + /* Clock settings */ + qspi_enter_mode(qspi, QSPI_DISABLE_MODE); + + /* Sampled by sfif_clk_b; half cycle delay; */ + if (qspi->max_hz < (QSPI_SMPR_FSPHS_CLK >> 2)) + { + qspi_writel(qspi, 0x0, base + QSPI_SMPR); + } + else + { + qspi_writel(qspi, QSPI_SMPR_FSPHS_MASK, base + QSPI_SMPR); + } + + /* Fix wirte failure issue*/ + qspi_writel(qspi, 0x8, base + QSPI_SOCCR); + + /* Set the default source address QSPI_AMBA_BASE*/ + qspi_write_sfar(qspi, qspi->memmap_base); + qspi_writel(qspi, 0x0, base + QSPI_SFACR); + + /* Config ahb read start */ + /* Disable BUF0~BUF1, use BUF3 for all masters */ + qspi_writel(qspi, 0, base + QSPI_BUF0IND); + qspi_writel(qspi, 0, base + QSPI_BUF1IND); + qspi_writel(qspi, 0, base + QSPI_BUF2IND); + + /* AHB Master port */ + qspi_writel(qspi, 0xe, base + QSPI_BUF0CR); + qspi_writel(qspi, 0xe, base + QSPI_BUF1CR); + qspi_writel(qspi, 0xe, base + QSPI_BUF2CR); + qspi_writel(qspi, + QSPI_BUF3CR_ALLMST_MASK | QSPI_BUF3CR_ADATSZ((qspi->ahb_buf_size / 8)), + base + QSPI_BUF3CR); + + /* Set AHB read sequence id */ + qspi_writel(qspi, + QSPI_BFGENCR_SEQID(SEQID_LUT_AHBREAD_ID), + base + QSPI_BFGENCR); + /* Config ahb read end */ + + /* Set flash memory map */ + qspi_writel(qspi, qspi->sfa1ad & 0xfffffc00, base + QSPI_SFA1AD); + qspi_writel(qspi, qspi->sfa2ad & 0xfffffc00, base + QSPI_SFA2AD); + qspi_writel(qspi, qspi->sfb1ad & 0xfffffc00, base + QSPI_SFB1AD); + qspi_writel(qspi, qspi->sfb2ad & 0xfffffc00, base + QSPI_SFB2AD); + + /* ISD3FB, ISD2FB, ISD3FA, ISD2FA = 1; END_CFG=0x3 */ + reg = qspi_readl(qspi, base + QSPI_MCR); + reg |= QSPI_MCR_END_CFG_MASK | QSPI_MCR_ISD_MASK; + qspi_writel(qspi, reg, base + QSPI_MCR); + + /* Module enabled */ + qspi_enter_mode(qspi, QSPI_NORMAL_MODE); + + /* Read using the IP Bus registers QSPI_RBDR0 to QSPI_RBDR31*/ + qspi_write_rbct(qspi, QSPI_RBCT_RXBRD_MASK); + + /* Clear all interrupt status */ + qspi_writel(qspi, 0xffffffff, base + QSPI_FR); + /* QSPI init end */ + + rt_completion_init(&qspi->cmd_completion); + + dev->user_data = qspi; + + qspi->parent.parent.ofw_node = dev->ofw_node; + + rt_dm_dev_set_name(&qspi->parent.parent, "qspi%d", qspi_bus_num); + bus_name = rt_dm_dev_get_name(&qspi->parent.parent); + + rt_hw_interrupt_install(qspi->irq, k1x_qspi_isr, qspi, bus_name); + rt_hw_interrupt_umask(qspi->irq); + + if ((err = rt_qspi_bus_register(&qspi->parent, bus_name, &k1x_qspi_ops))) + { + goto _fail; + } + + return RT_EOK; +_fail: + k1x_qspi_free(qspi, dev); + + return err; +} + +static rt_err_t k1x_qspi_remove(struct rt_platform_device *pdev) +{ + struct rt_device *dev = &pdev->parent; + struct k1x_qspi *qspi = dev->user_data; + + rt_hw_interrupt_mask(qspi->irq); + rt_pic_detach_irq(qspi->irq, qspi); + + rt_device_unregister(&qspi->parent.parent); + + k1x_qspi_free(qspi, dev); + + return RT_EOK; +} + +static const struct rt_ofw_node_id k1x_qspi_ofw_ids[] = +{ + { .compatible = "spacemit,k1x-qspi", }, + { /* sentinel */ } +}; + +static struct rt_platform_driver k1x_qspi_driver = +{ + .name = "k1x-qspi", + .ids = k1x_qspi_ofw_ids, + + .probe = k1x_qspi_probe, + .remove = k1x_qspi_remove, +}; +RT_PLATFORM_DRIVER_EXPORT(k1x_qspi_driver); diff --git a/bsp/spacemit/dm/spi/spi-k1x.c b/bsp/spacemit/dm/spi/spi-k1x.c new file mode 100755 index 00000000000..f669747b275 --- /dev/null +++ b/bsp/spacemit/dm/spi/spi-k1x.c @@ -0,0 +1,1142 @@ +/* + * Copyright (c) 2006-2024, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2024-5-1 GuEe-GUI first version + */ + +#include "dev_spi_dm.h" + +#define DBG_TAG "spi.k1x.spi" +#define DBG_LVL DBG_INFO +#include + +#include +#include +#include +#include + +/* Spacemit k1x SPI Registers */ +#define TOP_CTRL 0x00 /* SSP Top Control Register */ +#define FIFO_CTRL 0x04 /* SSP FIFO Control Register */ +#define INT_EN 0x08 /* SSP Interrupt Enable Register */ +#define TO 0x0c /* SSP Time Out Register */ +#define DATAR 0x10 /* SSP Data Register */ +#define STATUS 0x14 /* SSP Stauts Register */ +#define PSP_CTRL 0x18 /* SSP Programmable Serial Protocal Control Register */ +#define NET_WORK_CTRL 0x1c /* SSP NET Work Control Register */ +#define NET_WORK_STATUS 0x20 /* SSP Net Work Status Register */ +#define RWOT_CTRL 0x24 /* SSP RWOT Control Register */ +#define RWOT_CCM 0x28 /* SSP RWOT Counter Cycles Match Register */ +#define RWOT_CVWRn 0x2c /* SSP RWOT Counter Value Write for Read Request Register */ +#define CLK_PHASE_ADJ 0x30 /* SSP clock phase adjustment for debug */ + +/* 0x00 TOP_CTRL */ +#define TOP_TTELP RT_BIT(18) +#define TOP_TTE RT_BIT(17) +#define TOP_SCFR RT_BIT(16) +#define TOP_IFS RT_BIT(15) +#define TOP_HOLD_FRAME_LOW RT_BIT(14) +#define TOP_TRAIL RT_BIT(13) +#define TOP_LBM RT_BIT(12) +#define TOP_SPH RT_BIT(11) +#define TOP_SPO RT_BIT(10) +#define TOP_DSS(x) ((x - 1) << 5) +#define TOP_DSS_MASK (0x1f << 5) +#define TOP_SFRMDIR RT_BIT(4) +#define TOP_SCLKDIR RT_BIT(3) +#define TOP_FRF_MASK (0x3 << 1) +#define TOP_FRF_Motorola (0x0 << 1) /* Motorola's Serial Peripheral Interface (SPI) */ +#define TOP_FRF_TI (0x1 << 1) /* Texas Instruments' Synchronous Serial Protocol (SSP) */ +#define TOP_FRF_National (0x2 << 1) /* National Microwire */ +#define TOP_FRF_PSP (0x3 << 1) /* Programmable Serial Protocol(PSP) */ +#define TOP_SSE RT_BIT(0) + +/* 0x04 FIFO_CTRL */ +#define FIFO_STRF RT_BIT(19) +#define FIFO_EFWR RT_BIT(18) +#define FIFO_RXFIFO_AUTO_FULL_CTRL RT_BIT(17) +#define FIFO_FPCKE RT_BIT(16) +#define FIFO_TXFIFO_WR_ENDIAN_MASK (0x3 << 14) +#define FIFO_RXFIFO_RD_ENDIAN_MASK (0x3 << 12) +#define FIFO_WR_ENDIAN_16BITS RT_BIT(14) /* Swap first 16 bits and last 16 bits */ +#define FIFO_WR_ENDIAN_8BITS (2 << 14) /* Swap all 4 bytes */ +#define FIFO_RD_ENDIAN_16BITS RT_BIT(12) /* Swap first 16 bits and last 16 bits */ +#define FIFO_RD_ENDIAN_8BITS (2 << 12) /* Swap all 4 bytes */ +#define FIFO_RSRE RT_BIT(11) +#define FIFO_TSRE RT_BIT(10) + +/* 0x08 INT_EN */ +#define INT_EN_EBCEI RT_BIT(6) +#define INT_EN_TIM RT_BIT(5) +#define INT_EN_RIM RT_BIT(4) +#define INT_EN_TIE RT_BIT(3) +#define INT_EN_RIE RT_BIT(2) +#define INT_EN_TINTE RT_BIT(1) +#define INT_EN_PINTE RT_BIT(0) + +/* 0x0C TO */ +#define TIMEOUT(x) ((x) << 0) + +/* 0x10 DATAR */ +#define DATA(x) ((x) << 0) + +/* 0x14 STATUS */ +#define STATUS_OSS RT_BIT(23) +#define STATUS_TX_OSS RT_BIT(22) +#define STATUS_BCE RT_BIT(21) +#define STATUS_ROR RT_BIT(20) +#define STATUS_RNE RT_BIT(14) +#define STATUS_RFS RT_BIT(13) +#define STATUS_TUR RT_BIT(12) +#define STATUS_TNF RT_BIT(6) +#define STATUS_TFS RT_BIT(5) +#define STATUS_EOC RT_BIT(4) +#define STATUS_TINT RT_BIT(3) +#define STATUS_PINT RT_BIT(2) +#define STATUS_CSS RT_BIT(1) +#define STATUS_BSY RT_BIT(0) + +/* 0x18 PSP_CTRL */ +#define PSP_EDMYSTOP(x) ((x) << 27) +#define PSP_EMYSTOP(x) ((x) << 25) +#define PSP_EDMYSTRT(x) ((x) << 23) +#define PSP_DMYSTRT(x) ((x) << 21) +#define PSP_STRTDLY(x) ((x) << 18) +#define PSP_SFRMWDTH(x) ((x) << 12) +#define PSP_SFRMDLY(x) ((x) << 5) +#define PSP_SFRMP (1 << 4) +#define PSP_FSRT (1 << 3) +#define PSP_ETDS (1 << 2) +#define PSP_SCMODE(x) ((x) << 0) + +/* 0x1C NET_WORK_CTRL */ +#define RTSA(x) ((x) << 12) +#define RTSA_MASK (0xff << 12) +#define TTSA(x) ((x) << 4) +#define TTSA_MASK (0xff << 4) +#define NET_FRDC(x) ((x) << 1) +#define NET_WORK_MODE (1 << 0) + +/* 0x20 NET_WORK_STATUS */ +#define NET_SATUS_NMBSY (1 << 3) +#define NET_STATUS_TSS(x) ((x) << 0) + +/* 0x24 RWOT_CTRL */ +#define RWOT_MASK_RWOT_LAST_SAMPLE RT_BIT(4) +#define RWOT_CLR_RWOT_CYCLE RT_BIT(3) +#define RWOT_SET_RWOT_CYCLE RT_BIT(2) +#define RWOT_CYCLE_RWOT_EN RT_BIT(1) +#define RWOT_RWOT RT_BIT(0) + +#define TIMOUT_DFLT 3000 +#define TIMOUT_DFLT_SLAVE 0x40000 + +#define RX_THRESH_DFLT 9 +#define TX_THRESH_DFLT 8 +/* 0x14 */ +#define STATUS_TFL_MASK (0x1f << 7) /* Transmit FIFO Level mask */ +#define STATUS_RFL_MASK (0x1f << 15) /* Receive FIFO Level mask */ +/* 0x4 */ +#define FIFO_TFT (0x0000001f) /* Transmit FIFO Threshold (mask) */ +#define FIFO_TxTresh(x) (((x) - 1) << 0) /* level [1..32] */ +#define FIFO_RFT (0x000003e0) /* Receive FIFO Threshold (mask) */ +#define FIFO_RxTresh(x) (((x) - 1) << 5) /* level [1..32] */ +/* + * Select the right DMA implementation. + */ +#define MAX_DMA_LEN (512 * SIZE_KB) +#define DEFAULT_DMA_FIFO_CTRL (FIFO_TSRE | FIFO_RSRE) +#define DEFAULT_DMA_TOP_CTRL (TOP_TRAIL) + +struct k1x_spi; + +struct chip_data +{ + rt_uint32_t top_ctrl; + rt_uint32_t fifo_ctrl; + rt_uint32_t timeout; + rt_uint8_t n_bytes; + rt_uint32_t dma_burst_size; + rt_uint32_t threshold; + rt_uint32_t dma_threshold; + rt_uint8_t enable_dma; + rt_bool_t (*write)(struct k1x_spi *spi); + rt_bool_t (*read)(struct k1x_spi *spi); +}; + +struct k1x_spi +{ + struct rt_spi_bus parent; + + struct rt_clk *clk; + struct rt_reset_control *rstc; + + int irq; + void *ioaddr; + rt_uint32_t ssdr_physical; + rt_uint32_t max_speed_hz; + + /* SSP masks*/ + rt_uint32_t dma_fifo_ctrl; + rt_uint32_t dma_top_ctrl; + rt_uint32_t int_cr; + rt_uint32_t dma_cr; + rt_uint32_t clear_sr; + rt_uint32_t mask_sr; + + rt_bool_t enable_dma; + rt_atomic_t dma_running; + struct rt_dma_chan *rx_chan; + struct rt_dma_chan *tx_chan; + struct rt_dma_slave_config rx_config; + struct rt_dma_slave_config tx_config; + struct rt_dma_slave_transfer rx_transfer; + struct rt_dma_slave_transfer tx_transfer; + + rt_bool_t (*write)(struct k1x_spi *spi); + rt_bool_t (*read)(struct k1x_spi *spi); + void (*transfer_handler)(struct k1x_spi *spi); + + void *tx, *tx_end; + void *rx, *rx_end; + rt_uint8_t n_bytes; + struct rt_spi_message *cur_msg; + + struct chip_data cur_chip; + struct rt_completion cur_msg_completion; + + /* Support RX FIFO auto full control and endian swap */ + rt_bool_t slave_mode; + rt_uint32_t ssp_enhancement; + struct rt_timer slave_rx_timer; +}; + +#define raw_to_k1x_spi(raw) rt_container_of(raw, struct k1x_spi, parent) + +rt_inline rt_uint32_t k1x_spi_read(struct k1x_spi *spi, int reg) +{ + return HWREG32(spi->ioaddr + reg); +} + +rt_inline void k1x_spi_write(struct k1x_spi *spi, int reg, rt_uint32_t val) +{ + HWREG32(spi->ioaddr + reg) = val; +} + +static void k1x_spi_dma_transfer_complete(struct k1x_spi *spi, rt_bool_t error) +{ + if (!rt_atomic_dec_and_test(&spi->dma_running)) + { + return; + } + + /* + * If the other CPU is still handling the ROR interrupt we + * might not know about the error yet. So we re-check the + * ROR bit here before we clear the status register. + */ + if (!error) + { + rt_uint32_t status = k1x_spi_read(spi, STATUS) & spi->mask_sr; + + error = status & STATUS_ROR; + } + + /* Clear status & disable interrupts */ + k1x_spi_write(spi, FIFO_CTRL, k1x_spi_read(spi, FIFO_CTRL) & ~spi->dma_fifo_ctrl); + k1x_spi_write(spi, TOP_CTRL, k1x_spi_read(spi, TOP_CTRL) & ~spi->dma_top_ctrl); + k1x_spi_write(spi, STATUS, spi->clear_sr); + k1x_spi_write(spi, TO, 0); + + if (error) + { + /* In case we got an error we disable the SSP now */ + k1x_spi_write(spi, TOP_CTRL, k1x_spi_read(spi, TOP_CTRL) & ~TOP_SSE); + } +} + +static void k1x_spi_dma_callback(struct rt_dma_chan *chan, rt_size_t size) +{ + struct k1x_spi *spi = chan->priv; + + k1x_spi_dma_transfer_complete(spi, RT_FALSE); +} + +static void k1x_spi_dma_transfer(struct k1x_spi *spi) +{ + rt_uint32_t status; + + status = k1x_spi_read(spi, STATUS) & spi->mask_sr; + + if ((spi->slave_mode && status & STATUS_TINT) || (status & STATUS_ROR)) + { + if (spi->rx) + { + rt_dma_chan_stop(spi->rx_chan); + } + if (spi->tx) + { + rt_dma_chan_stop(spi->tx_chan); + } + k1x_spi_dma_transfer_complete(spi, RT_TRUE); + } +} + +static rt_err_t k1x_spi_set_dma_burst_and_threshold(struct chip_data *chip, + struct rt_spi_device *device, + rt_uint8_t bits_per_word, rt_uint32_t *burst_code, rt_uint32_t *threshold) +{ + /* + * If the DMA burst size is given in chip_info we use + * that, otherwise we set it to half of FIFO size; SPI + * FIFO has 16 entry, so FIFO size = 16*bits_per_word/8; + * Also we use the default FIFO thresholds for now. + */ + if (chip && chip->dma_burst_size) + { + *burst_code = chip->dma_burst_size; + } + else if (bits_per_word <= 8) + { + *burst_code = 8; + } + else if (bits_per_word <= 16) + { + *burst_code = 16; + } + else + { + *burst_code = 32; + } + + *threshold = FIFO_RxTresh(RX_THRESH_DFLT) | FIFO_TxTresh(TX_THRESH_DFLT); + + return RT_EOK; +} + +static rt_bool_t k1x_spi_txfifo_full(struct k1x_spi *spi) +{ + return !(k1x_spi_read(spi, STATUS) & STATUS_TNF); +} + +/* Clear all rx fifo useless data */ +static int k1x_spi_flush(struct k1x_spi *spi) +{ + unsigned long limit = RT_TICK_PER_SECOND << 1; + + do { + while (k1x_spi_read(spi, STATUS) & STATUS_RNE) + { + k1x_spi_read(spi, DATAR); + } + } while ((k1x_spi_read(spi, STATUS) & STATUS_BSY) && --limit); + + k1x_spi_write(spi, STATUS, STATUS_ROR); + + return limit; +} + +static rt_bool_t null_writer(struct k1x_spi *spi) +{ + rt_uint8_t n_bytes = spi->n_bytes; + + if (k1x_spi_txfifo_full(spi) || spi->tx == spi->tx_end) + { + return RT_FALSE; + } + + k1x_spi_write(spi, DATAR, 0); + spi->tx += n_bytes; + + return RT_TRUE; +} + +static rt_bool_t null_reader(struct k1x_spi *spi) +{ + rt_uint8_t n_bytes = spi->n_bytes; + + while ((k1x_spi_read(spi, STATUS) & STATUS_RNE) && (spi->rx < spi->rx_end)) + { + k1x_spi_read(spi, DATAR); + spi->rx += n_bytes; + } + + return spi->rx == spi->rx_end; +} + +static rt_bool_t u8_writer(struct k1x_spi *spi) +{ + if (k1x_spi_txfifo_full(spi) || spi->tx == spi->tx_end) + { + return RT_FALSE; + } + + k1x_spi_write(spi, DATAR, *(rt_uint8_t *)(spi->tx)); + ++spi->tx; + + return RT_TRUE; +} + +static rt_bool_t u8_reader(struct k1x_spi *spi) +{ + while ((k1x_spi_read(spi, STATUS) & STATUS_RNE) && spi->rx < spi->rx_end) + { + *(rt_uint8_t *)(spi->rx) = k1x_spi_read(spi, DATAR); + ++spi->rx; + } + + return spi->rx == spi->rx_end; +} + +static rt_bool_t u16_writer(struct k1x_spi *spi) +{ + if (k1x_spi_txfifo_full(spi) || spi->tx == spi->tx_end) + { + return RT_FALSE; + } + + k1x_spi_write(spi, DATAR, *(rt_uint16_t *)(spi->tx)); + spi->tx += 2; + + return RT_TRUE; +} + +static rt_bool_t u16_reader(struct k1x_spi *spi) +{ + while ((k1x_spi_read(spi, STATUS) & STATUS_RNE) && spi->rx < spi->rx_end) + { + *(rt_uint16_t *)(spi->rx) = k1x_spi_read(spi, DATAR); + spi->rx += 2; + } + + return spi->rx == spi->rx_end; +} + +static rt_bool_t u32_writer(struct k1x_spi *spi) +{ + if (k1x_spi_txfifo_full(spi) || spi->tx == spi->tx_end) + { + return RT_FALSE; + } + + k1x_spi_write(spi, DATAR, *(rt_uint32_t *)(spi->tx)); + spi->tx += 4; + + return RT_TRUE; +} + +static rt_bool_t u32_reader(struct k1x_spi *spi) +{ + while ((k1x_spi_read(spi, STATUS) & STATUS_RNE) && spi->rx < spi->rx_end) + { + *(rt_uint32_t *)(spi->rx) = k1x_spi_read(spi, DATAR); + spi->rx += 4; + } + + return spi->rx == spi->rx_end; +} + +static void reset_fifo_ctrl(struct k1x_spi *spi) +{ + rt_uint32_t fifo_ctrl = 0; + struct chip_data *chip = &spi->cur_chip; + + fifo_ctrl |= chip->threshold; + k1x_spi_write(spi, FIFO_CTRL, fifo_ctrl); +} + +static void int_transfer_stop(struct k1x_spi *spi) +{ + rt_uint32_t int_en = 0; + + /* Stop SSP */ + k1x_spi_write(spi, STATUS, spi->clear_sr); + reset_fifo_ctrl(spi); + + int_en = k1x_spi_read(spi, INT_EN); + int_en &= ~spi->int_cr; + k1x_spi_write(spi, INT_EN, int_en); + + k1x_spi_write(spi, TO, 0); +} + +static void interrupt_transfer(struct k1x_spi *spi) +{ + rt_uint32_t irq_mask, irq_status; + + irq_mask = (k1x_spi_read(spi, INT_EN) & INT_EN_TIE) ? + spi->mask_sr : spi->mask_sr & ~STATUS_TFS; + irq_status = k1x_spi_read(spi, STATUS) & irq_mask; + + if (irq_status & STATUS_ROR) + { + /* Stop and rstc SSP */ + int_transfer_stop(spi); + k1x_spi_flush(spi); + k1x_spi_write(spi, TOP_CTRL, k1x_spi_read(spi, TOP_CTRL) & ~(TOP_SSE | TOP_HOLD_FRAME_LOW)); + return; + } + + if (irq_status & STATUS_TINT) + { + k1x_spi_write(spi, STATUS, STATUS_TINT); + + if (spi->read(spi)) + { + int_transfer_stop(spi); + return; + } + } + + /* Drain rx fifo, Fill tx fifo and prevent overruns */ + do { + if (spi->read(spi)) + { + int_transfer_stop(spi); + return; + } + } while (spi->write(spi)); + + if (spi->read(spi)) + { + int_transfer_stop(spi); + return; + } + + if (spi->tx == spi->tx_end) + { + rt_uint32_t int_en; + + int_en = k1x_spi_read(spi, INT_EN); + int_en &= ~INT_EN_TIE; + + k1x_spi_write(spi, INT_EN, int_en); + } +} + +static void slave_rx_timer_expired(void *param) +{ + struct k1x_spi *spi = param; + + if (spi->rx) + { + rt_dma_chan_stop(spi->rx_chan); + } + if (spi->tx) + { + rt_dma_chan_stop(spi->tx_chan); + } + k1x_spi_dma_transfer_complete(spi, RT_TRUE); +} + +static rt_err_t k1x_spi_spi_configure(struct rt_spi_device *device, + struct rt_spi_configuration *conf) +{ + rt_uint32_t tx_thres, rx_thres; + struct k1x_spi *spi = raw_to_k1x_spi(device->bus); + struct chip_data *chip = &spi->cur_chip; + + tx_thres = TX_THRESH_DFLT; + rx_thres = RX_THRESH_DFLT; + + chip->top_ctrl = 0; + chip->fifo_ctrl = 0; + chip->enable_dma = spi->enable_dma; + + if (spi->slave_mode) + { + chip->dma_burst_size = 32; + } + + if (chip->enable_dma) + { + /* Set up legal burst and threshold for dma */ + if (k1x_spi_set_dma_burst_and_threshold(chip, device, + device->config.data_width, &chip->dma_burst_size, &chip->dma_threshold)) + { + LOG_W("DMA burst size reduced to match bits_per_word"); + } + } + chip->threshold = (FIFO_RxTresh(rx_thres) & FIFO_RFT) | (FIFO_TxTresh(tx_thres) & FIFO_TFT); + + chip->top_ctrl &= ~(TOP_SPO | TOP_SPH); + chip->top_ctrl |= (((device->config.mode & RT_SPI_CPHA) != 0) ? TOP_SPH : 0) + | (((device->config.mode & RT_SPI_CPOL) != 0) ? TOP_SPO : 0); + + /* Enable rx fifo auto full control */ + if (spi->ssp_enhancement) + { + chip->fifo_ctrl |= FIFO_RXFIFO_AUTO_FULL_CTRL; + } + + if (device->config.data_width <= 8) + { + chip->n_bytes = 1; + chip->read = u8_reader; + chip->write = u8_writer; + } + else if (device->config.data_width <= 16) + { + chip->n_bytes = 2; + chip->read = u16_reader; + chip->write = u16_writer; + } + else if (device->config.data_width <= 32) + { + chip->n_bytes = 4; + chip->read = u32_reader; + chip->write = u32_writer; + } + + if (rt_clk_get_rate(spi->clk) != spi->max_speed_hz) + { + rt_clk_set_rate(spi->clk, spi->max_speed_hz); + } + + return RT_EOK; +} + +static rt_ssize_t k1x_spi_spi_xfer(struct rt_spi_device *device, + struct rt_spi_message *msg) +{ + rt_err_t err; + rt_uint8_t bits = 0; + rt_uint32_t top_ctrl, fifo_ctrl, int_en = 0, dma_thresh, dma_burst; + struct k1x_spi *spi = raw_to_k1x_spi(device->bus); + struct chip_data *chip = &spi->cur_chip; + + dma_thresh = chip->dma_threshold; + dma_burst = chip->dma_burst_size; + + if (spi->slave_mode) + { + rt_tick_t tick = rt_tick_from_millisecond(1000); + + rt_timer_control(&spi->slave_rx_timer, RT_TIMER_CTRL_SET_TIME, &tick); + + rt_timer_start(&spi->slave_rx_timer); + } + + /* Setup the transfer state based on the type of transfer */ + if (k1x_spi_flush(spi) == 0) + { + rt_timer_stop(&spi->slave_rx_timer); + + LOG_E("Flush failed"); + return -RT_EIO; + } + + if (device->config.max_hz && device->config.max_hz != rt_clk_get_rate(spi->clk)) + { + rt_clk_set_rate(spi->clk, device->config.max_hz); + } + + spi->n_bytes = chip->n_bytes; + spi->tx = (void *)msg->send_buf; + spi->tx_end = spi->tx + msg->length; + spi->rx = msg->recv_buf; + spi->rx_end = spi->rx + msg->length; + spi->write = spi->tx ? chip->write : null_writer; + spi->read = spi->rx ? chip->read : null_reader; + spi->cur_msg = msg; + + /* Change speed and bit per word on a per transfer */ + bits = device->config.data_width; + + if (bits <= 8) + { + spi->n_bytes = 1; + spi->read = spi->read != null_reader ? u8_reader : null_reader; + spi->write = spi->write != null_writer ? u8_writer : null_writer; + } + else if (bits <= 16) + { + spi->n_bytes = 2; + spi->read = spi->read != null_reader ? u16_reader : null_reader; + spi->write = spi->write != null_writer ? u16_writer : null_writer; + } + else if (bits <= 32) + { + spi->n_bytes = 4; + spi->read = spi->read != null_reader ? u32_reader : null_reader; + spi->write = spi->write != null_writer ? u32_writer : null_writer; + } + + /* + * If bits/word is changed in dma mode, then must check the + * thresholds and burst also + */ + if (chip->enable_dma) + { + if (k1x_spi_set_dma_burst_and_threshold(chip, + device, bits, &dma_burst, &dma_thresh)) + { + LOG_W("DMA burst size reduced to match bits_per_word"); + } + } + + /* + * Configure topctrl: + * set Motorola Frame Format + * set DSS + */ + top_ctrl = TOP_FRF_Motorola | TOP_DSS(bits); + LOG_D("%u Hz, %s", spi->max_speed_hz, chip->enable_dma ? "DMA" : "PIO"); + + top_ctrl |= chip->top_ctrl; + fifo_ctrl = chip->fifo_ctrl; + + if (spi->ssp_enhancement) + { + /* + * If transfer length is times of 4, then use + * 32 bit fifo width with endian swap support + */ + if (msg->length % 4 == 0 && bits <= 16) + { + if (bits <= 8) + { + fifo_ctrl |= FIFO_WR_ENDIAN_8BITS | FIFO_RD_ENDIAN_8BITS; + } + else if (bits <= 16) + { + fifo_ctrl |= FIFO_WR_ENDIAN_16BITS | FIFO_RD_ENDIAN_16BITS; + } + + bits = 32; + spi->n_bytes = 4; + if (msg->recv_buf) + { + spi->read = u32_reader; + } + if (msg->send_buf) + { + spi->write = u32_writer; + } + + if (chip->enable_dma) + { + if (k1x_spi_set_dma_burst_and_threshold(chip, + device, bits, &dma_burst, &dma_thresh)) + { + LOG_W("DMA burst size reduced to match bits_per_word"); + } + } + + top_ctrl &= ~TOP_DSS_MASK; + top_ctrl |= TOP_DSS(32); + } + } + + /* Check DMA is possible */ + if (msg->length <= MAX_DMA_LEN) + { + enum rt_dma_slave_buswidth width; + + /* Ensure we have the correct interrupt handler */ + spi->transfer_handler = k1x_spi_dma_transfer; + + switch (spi->n_bytes) + { + case 1: + width = RT_DMA_SLAVE_BUSWIDTH_1_BYTE; + break; + case 2: + width = RT_DMA_SLAVE_BUSWIDTH_2_BYTES; + break; + default: + width = RT_DMA_SLAVE_BUSWIDTH_4_BYTES; + break; + } + + if (spi->rx) + { + spi->rx_config.dst_addr = (rt_ubase_t)rt_kmem_v2p(spi->rx); + spi->rx_config.dst_addr_width = width; + spi->rx_config.dst_maxburst = msg->length; + + if ((err = rt_dma_chan_config(spi->rx_chan, &spi->rx_config))) + { + return err; + } + + spi->rx_transfer.buffer_len = msg->length; + spi->rx_transfer.dst_addr = spi->rx_config.dst_addr; + + if ((err = rt_dma_prep_single(spi->rx_chan, &spi->rx_transfer))) + { + return err; + } + + if ((err = rt_dma_chan_start(spi->rx_chan))) + { + return err; + } + } + + if (spi->tx) + { + spi->tx_config.src_addr = (rt_ubase_t)rt_kmem_v2p(spi->tx); + spi->tx_config.src_addr_width = width; + spi->tx_config.src_maxburst = chip->dma_burst_size; + + if ((err = rt_dma_chan_config(spi->tx_chan, &spi->tx_config))) + { + return err; + } + + spi->tx_transfer.buffer_len = msg->length; + spi->tx_transfer.src_addr = spi->tx_config.src_addr; + + if ((err = rt_dma_prep_single(spi->tx_chan, &spi->tx_transfer))) + { + return err; + } + + if ((err = rt_dma_chan_start(spi->tx_chan))) + { + return err; + } + } + + /* Clear status and start DMA engine */ + fifo_ctrl |= chip->fifo_ctrl | dma_thresh | spi->dma_fifo_ctrl; + top_ctrl |= chip->top_ctrl | spi->dma_top_ctrl; + k1x_spi_write(spi, STATUS, spi->clear_sr); + + rt_atomic_store(&spi->dma_running, 1); + + int_en = k1x_spi_read(spi, INT_EN) | spi->dma_cr; + } + else + { + /* Ensure we have the correct interrupt handler */ + spi->transfer_handler = interrupt_transfer; + + fifo_ctrl = fifo_ctrl | chip->fifo_ctrl | chip->threshold; + int_en = k1x_spi_read(spi, INT_EN) | spi->int_cr; + k1x_spi_write(spi, STATUS, spi->clear_sr); + } + + k1x_spi_write(spi, TO, chip->timeout); + + if (spi->slave_mode) + { + top_ctrl |= TOP_SSE | TOP_SCLKDIR | TOP_SFRMDIR; + } + else + { + top_ctrl |= TOP_HOLD_FRAME_LOW; + } + + /* + * This part changed the logic + * 1. clear SSE + * 2. write TOP_CTRL and other register + * 3. set SSE in the end of this function + */ + top_ctrl &= ~TOP_SSE; + k1x_spi_write(spi, TOP_CTRL, top_ctrl); + k1x_spi_write(spi, FIFO_CTRL, fifo_ctrl); + k1x_spi_write(spi, INT_EN, int_en); + top_ctrl |= TOP_SSE; + k1x_spi_write(spi, TOP_CTRL, top_ctrl); + + rt_completion_wait(&spi->cur_msg_completion, RT_WAITING_FOREVER); + + /* Disable the SSP now */ + k1x_spi_write(spi, TOP_CTRL, k1x_spi_read(spi, TOP_CTRL) & ~(TOP_SSE | TOP_HOLD_FRAME_LOW)); + + return msg->length; +} + +static struct rt_spi_ops k1x_spi_spi_ops = +{ + .configure = k1x_spi_spi_configure, + .xfer = k1x_spi_spi_xfer, +}; + +static void k1x_spi_spi_isr(int irq, void *param) +{ + rt_uint32_t int_en, mask, isr; + struct k1x_spi *spi = param; + + /* + * If the device is not yet in RPM suspended state and we get an + * interrupt that is meant for another device, check if status bits + * are all set to one. That means that the device is already powered off. + */ + isr = k1x_spi_read(spi, STATUS); + if (isr == ~0) + { + return; + } + + int_en = k1x_spi_read(spi, INT_EN); + mask = spi->mask_sr; + + /* Ignore possible writes if we don't need to write */ + if (!(int_en & INT_EN_TIE)) + { + mask &= ~STATUS_TFS; + } + + /* Ignore RX timeout interrupt if it is disabled */ + if (!(int_en & INT_EN_TINTE)) + { + mask &= ~STATUS_TINT; + } + + if (!(isr & mask)) + { + return; + } + + if (!spi->cur_msg) + { + k1x_spi_write(spi, TOP_CTRL, + k1x_spi_read(spi, TOP_CTRL) & ~(TOP_SSE | TOP_HOLD_FRAME_LOW)); + k1x_spi_write(spi, INT_EN, + k1x_spi_read(spi, INT_EN) & ~spi->int_cr); + k1x_spi_write(spi, TO, 0); + k1x_spi_write(spi, STATUS, spi->clear_sr); + + LOG_E("Bad message state in interrupt handler"); + + /* Never fail */ + return; + } + + return spi->transfer_handler(spi); +} + +static void k1x_spi_free(struct k1x_spi *spi, struct rt_device *dev) +{ + if (!rt_is_err_or_null(spi->rstc)) + { + rt_reset_control_assert(spi->rstc); + rt_reset_control_put(spi->rstc); + } + + if (!rt_is_err_or_null(spi->clk)) + { + rt_clk_disable_unprepare(spi->clk); + rt_clk_put(spi->clk); + } + + if (!rt_is_err_or_null(spi->rx_chan)) + { + rt_dma_chan_release(spi->rx_chan); + } + + if (!rt_is_err_or_null(spi->tx_chan)) + { + rt_dma_chan_release(spi->tx_chan); + } +} + +static rt_err_t k1x_spi_probe(struct rt_platform_device *pdev) +{ + rt_err_t err; + rt_uint32_t value, bus_num; + const char *bus_name; + struct rt_device *dev = &pdev->parent; + struct k1x_spi *spi = rt_calloc(1, sizeof(*spi)); + + if (!spi) + { + return -RT_ENOMEM; + } + + if (!(spi->ioaddr = rt_dm_dev_iomap(dev, 0))) + { + err = -RT_EIO; + goto _fail; + } + spi->ssdr_physical = (rt_ubase_t)rt_kmem_v2p(spi->ioaddr) + DATAR; + + if ((spi->irq = rt_dm_dev_get_irq(dev, 0)) < 0) + { + err = spi->irq; + goto _fail; + } + + spi->clk = rt_clk_get_by_index(dev, 0); + if (rt_is_err(spi->clk)) + { + err = rt_ptr_err(spi->clk); + goto _fail; + } + + spi->rstc = rt_reset_control_get_by_index(dev, 0); + if (rt_is_err(spi->clk)) + { + err = rt_ptr_err(spi->clk); + goto _fail; + } + + spi->enable_dma = !rt_dm_dev_prop_read_bool(dev, "k1x,ssp-disable-dma"); + spi->ssp_enhancement = rt_dm_dev_prop_read_bool(dev, "k1x,ssp-enhancement"); + spi->slave_mode = rt_dm_dev_prop_read_bool(dev, "k1x,ssp-slave-mode"); + + if ((err = rt_dm_dev_prop_read_u32(dev, "k1x,ssp-clock-rate", &spi->max_speed_hz))) + { + goto _fail; + } + + spi->int_cr = INT_EN_TIE | INT_EN_RIE | INT_EN_TINTE; /* INT_EN */ + spi->dma_cr = spi->slave_mode ? INT_EN_TINTE : 0; + spi->clear_sr = STATUS_ROR | STATUS_TINT; + spi->mask_sr = STATUS_TINT | STATUS_RFS | STATUS_TFS | STATUS_ROR; + spi->dma_top_ctrl = DEFAULT_DMA_TOP_CTRL; + spi->dma_fifo_ctrl = DEFAULT_DMA_FIFO_CTRL; + + /* Setup DMA if requested */ + if (spi->enable_dma) + { + spi->tx_chan = rt_dma_chan_request(dev, "tx"); + + if (!rt_is_err_or_null(spi->tx_chan)) + { + spi->tx_chan->callback = k1x_spi_dma_callback; + + spi->tx_config.direction = RT_DMA_MEM_TO_DEV; + spi->tx_config.dst_addr = spi->ssdr_physical; + + spi->tx_transfer.dst_addr = spi->tx_config.dst_addr; + } + else + { + goto _fail; + } + + spi->rx_chan = rt_dma_chan_request(dev, "rx"); + + if (!rt_is_err_or_null(spi->rx_chan)) + { + spi->rx_chan->callback = k1x_spi_dma_callback; + + spi->rx_config.direction = RT_DMA_MEM_TO_DEV; + spi->rx_config.src_addr = spi->ssdr_physical; + + spi->rx_transfer.src_addr = spi->rx_config.src_addr; + } + else + { + goto _fail; + } + } + + rt_clk_set_rate(spi->clk, spi->max_speed_hz); + spi->max_speed_hz = rt_clk_get_rate(spi->clk); + + rt_clk_prepare_enable(spi->clk); + rt_reset_control_deassert(spi->rstc); + + bus_num = 0; + rt_dm_dev_prop_read_u32(dev, "k1x,ssp-id", &bus_num); + + if ((bus_num == 2 || bus_num == 3) && + rt_dm_dev_prop_read_bool(dev, "k1x,ssp-enable-clk-phase-adj")) + { + k1x_spi_write(spi, CLK_PHASE_ADJ, 0x1); + } + + /* Load default SSP configuration */ + k1x_spi_write(spi, TOP_CTRL, 0); + k1x_spi_write(spi, FIFO_CTRL, 0); + value = FIFO_RxTresh(RX_THRESH_DFLT) | FIFO_TxTresh(TX_THRESH_DFLT); + k1x_spi_write(spi, FIFO_CTRL, value); + value = TOP_FRF_Motorola | TOP_DSS(8); + k1x_spi_write(spi, TOP_CTRL, value); + k1x_spi_write(spi, TO, 0); + + k1x_spi_write(spi, PSP_CTRL, 0); + + rt_completion_init(&spi->cur_msg_completion); + + dev->user_data = spi; + + spi->parent.parent.ofw_node = dev->ofw_node; + + if (pdev->dev_id >= 0) + { + rt_dm_dev_set_name(&spi->parent.parent, "spi%u", pdev->dev_id); + } + else + { + if (bus_num >= 0) + { + rt_dm_dev_set_name(&spi->parent.parent, "spi%u", bus_num); + } + else + { + rt_dm_dev_set_name_auto(&spi->parent.parent, "spi"); + } + } + bus_name = rt_dm_dev_get_name(&spi->parent.parent); + + if (spi->slave_mode) + { + rt_timer_init(&spi->slave_rx_timer, bus_name, slave_rx_timer_expired, spi, + 0, RT_TIMER_FLAG_PERIODIC); + } + + rt_hw_interrupt_install(spi->irq, k1x_spi_spi_isr, spi, bus_name); + rt_hw_interrupt_umask(spi->irq); + + if ((err = rt_spi_bus_register(&spi->parent, bus_name, &k1x_spi_spi_ops))) + { + goto _free_irq; + } + + return RT_EOK; + +_free_irq: + rt_hw_interrupt_mask(spi->irq); + rt_pic_detach_irq(spi->irq, spi); + +_fail: + k1x_spi_free(spi, dev); + + return err; +} + +static rt_err_t k1x_spi_remove(struct rt_platform_device *pdev) +{ + struct rt_device *dev = &pdev->parent; + struct k1x_spi *spi = dev->user_data; + + rt_hw_interrupt_mask(spi->irq); + rt_pic_detach_irq(spi->irq, spi); + + rt_device_unregister(&spi->parent.parent); + + /* Disable the SSP at the peripheral and SOC level */ + k1x_spi_write(spi, TOP_CTRL, 0); + k1x_spi_write(spi, FIFO_CTRL, 0); + + k1x_spi_free(spi, dev); + + return RT_EOK; +} + +static const struct rt_ofw_node_id k1x_spi_ofw_ids[] = +{ + { .compatible = "spacemit,k1x-spi" }, + { /* sentinel */ } +}; + +static struct rt_platform_driver k1x_spi_driver = +{ + .name = "k1x-spi", + .ids = k1x_spi_ofw_ids, + + .probe = k1x_spi_probe, + .remove = k1x_spi_remove, +}; +RT_PLATFORM_DRIVER_EXPORT(k1x_spi_driver); diff --git a/bsp/spacemit/dm/thermal/Kconfig b/bsp/spacemit/dm/thermal/Kconfig new file mode 100755 index 00000000000..38ce5540f8a --- /dev/null +++ b/bsp/spacemit/dm/thermal/Kconfig @@ -0,0 +1,3 @@ +config RT_THERMAL_K1X + bool "Spacemit K1X" + default n diff --git a/bsp/spacemit/dm/thermal/SConscript b/bsp/spacemit/dm/thermal/SConscript new file mode 100755 index 00000000000..78fbfcc7fa8 --- /dev/null +++ b/bsp/spacemit/dm/thermal/SConscript @@ -0,0 +1,13 @@ +from building import * + +group = [] +src = [] +cwd = GetCurrentDir() +CPPPATH = [cwd + '/../include'] + +if GetDepend(['RT_THERMAL_K1X']): + src += ['thermal-k1x.c'] + +group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') diff --git a/bsp/spacemit/dm/thermal/thermal-k1x.c b/bsp/spacemit/dm/thermal/thermal-k1x.c new file mode 100755 index 00000000000..7c894316734 --- /dev/null +++ b/bsp/spacemit/dm/thermal/thermal-k1x.c @@ -0,0 +1,482 @@ +/* + * Copyright (c) 2006-2024, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2024-5-1 GuEe-GUI first version + */ + +#include +#include +#include + +#define DBG_TAG "thermal.k1x" +#define DBG_LVL DBG_INFO +#include + +#define BITS(_start, _end) ((RT_BIT(_end) - RT_BIT(_start)) + RT_BIT(_end)) + +#define MAX_SENSOR_NUMBER 5 +#define TEMPERATURE_OFFSET 278 + +#define REG_TSEN_INT_MASK 0x14 +#define TSEN_EMERGENT_INT_OFFSET 23 +#define REG_EMERGENT_REBOOT_TEMP_THR 0x68 +#define REG_EMERGENT_REBOOT_TEMP_THR_MSK 0xffff + +#define REG_TSEN_TIME_CTRL 0x0c +#define BITS_TIME_CTRL_MASK BITS(0, 23) +#define VALUE_FILTER_PERIOD (0x3000 << 8) +#define BITS_RST_ADC_CNT BITS(4, 7) +#define VALUE_WAIT_REF_CNT (0xf << 0) + +#define BIT_TSEN_RAW_SEL RT_BIT(7) +#define BIT_TEMP_MODE RT_BIT(3) +#define BIT_EN_SENSOR RT_BIT(0) +#define BITS_TSEN_SW_CTRL BITS(18, 21) +#define BITS_CTUNE BITS(8, 11) +#define REG_TSEN_PCTRL 0x00 + +#define REG_TSEN_PCTRL2 0x04 +#define BITS_SDM_CLK_SEL BITS(14, 15) +#define BITS_SDM_CLK_SEL_24M (0 << 14) + +#define TSEN_INT_MASK RT_BIT(0) +#define BIT_HW_AUTO_MODE RT_BIT(23) + +#define MAX_SENSOR_NUMBER 5 + +struct sensor_enable +{ + rt_uint32_t bjt_en; + rt_uint32_t offset; + rt_uint32_t bit_msk; + rt_uint32_t en_val; +}; + +struct sensor_data +{ + rt_uint32_t data_reg; + rt_uint32_t offset; + rt_uint32_t bit_msk; +}; + +struct sensor_thrsh +{ + rt_uint32_t temp_thrsh; + rt_uint32_t low_offset; + rt_uint32_t high_offset; +}; + +struct k1x_thermal_sensor_desc +{ + rt_uint32_t int_msk; + rt_uint32_t int_clr; + rt_uint32_t int_sta; + rt_uint32_t offset; + rt_uint32_t bit_msk; + struct sensor_enable se; + struct sensor_data sd; + struct sensor_thrsh sr; +}; + +struct k1x_thermal; + +struct k1x_thermal_sensor +{ + struct rt_thermal_zone_device parent; + + void *regs; + struct k1x_thermal_sensor_desc *desc; +}; + +#define raw_to_k1x_thermal_sensor(raw) rt_container_of(raw, struct k1x_thermal_sensor, parent) + +struct k1x_thermal +{ + int irq; + void *regs; + struct rt_clk *clk; + struct rt_reset_control *rstc; + + /* Sensor range */ + rt_uint32_t sr[2]; + struct k1x_thermal_sensor *sensors; +}; + +static rt_err_t k1x_thermal_get_temp(struct rt_thermal_zone_device *zdev, int *out_temp) +{ + struct k1x_thermal_sensor *sensor = raw_to_k1x_thermal_sensor(zdev); + struct k1x_thermal_sensor_desc *desc = sensor->desc; + + *out_temp = HWREG32(sensor->regs + desc->sd.data_reg); + *out_temp &= desc->sd.bit_msk; + *out_temp >>= desc->sd.offset; + *out_temp -= TEMPERATURE_OFFSET; + *out_temp *= 1000; + + return RT_EOK; +} + +static rt_err_t k1x_thermal_set_trips(struct rt_thermal_zone_device *zdev, + int low_temp, int high_temp) +{ + rt_uint32_t temp; + int over_thrsh = high_temp, under_thrsh = low_temp; + struct k1x_thermal_sensor *sensor = raw_to_k1x_thermal_sensor(zdev); + struct k1x_thermal_sensor_desc *desc = sensor->desc; + + /* Set overflow */ + over_thrsh /= 1000; + over_thrsh += TEMPERATURE_OFFSET; + + temp = HWREG32(sensor->regs + desc->sr.temp_thrsh); + temp &= ~0xffff0000; + temp |= (over_thrsh << desc->sr.high_offset); + HWREG32(sensor->regs + desc->sr.temp_thrsh) = temp; + + /* set underflow */ + if (low_temp < 0) + { + under_thrsh = 0; + } + + under_thrsh /= 1000; + under_thrsh += TEMPERATURE_OFFSET; + temp = HWREG32(sensor->regs + desc->sr.temp_thrsh); + temp &= ~0xffff; + temp |= (under_thrsh << desc->sr.low_offset); + HWREG32(sensor->regs + desc->sr.temp_thrsh) = temp; + + return RT_EOK; +} + +static const struct rt_thermal_zone_ops k1x_thermal_ops = +{ + .get_temp = k1x_thermal_get_temp, + .set_trips = k1x_thermal_set_trips, +}; + +static void k1x_thermal_isr(int irqno, void *param) +{ + rt_uint32_t status, msk; + struct k1x_thermal_sensor *sensor = param; + struct k1x_thermal_sensor_desc *desc = sensor->desc; + + /* Get the status */ + status = HWREG32(sensor->regs + desc->int_sta); + status &= desc->bit_msk; + + if (!status) + { + return; + } + + /* Then clear the pending */ + msk = HWREG32(sensor->regs + desc->int_clr); + msk |= status; + HWREG32(sensor->regs + desc->int_clr) = msk; + + rt_thermal_zone_device_update(&sensor->parent, RT_THERMAL_MSG_EVENT_UNSPECIFIED); +} + +static rt_err_t init_sensors(struct rt_device *dev, struct k1x_thermal *thermal, + struct k1x_thermal_sensor_desc *desc) +{ + rt_err_t err; + rt_uint32_t temp; + + /* Read the sensor range */ + err = rt_dm_dev_prop_read_u32_array_index(dev, "sensor_range", 0, 2, thermal->sr); + if (err < 0) + { + LOG_E("Get sensor range error"); + return err; + } + + if (thermal->sr[1] >= MAX_SENSOR_NUMBER) + { + LOG_E("Un-fitable sensor range"); + return -RT_EINVAL; + } + + /* First: disable all the interrupts */ + HWREG32(thermal->regs + REG_TSEN_INT_MASK) = 0xffffffff; + + /* + * Decrease filter period time from 0x4000 to 0x3000, that + * means decrease 1/4 ADC sampling time for each sensor. + */ + temp = HWREG32(thermal->regs + REG_TSEN_TIME_CTRL); + temp &= ~BITS_TIME_CTRL_MASK; + temp |= VALUE_FILTER_PERIOD; + temp |= BITS_RST_ADC_CNT; + temp |= VALUE_WAIT_REF_CNT; + HWREG32(thermal->regs + REG_TSEN_TIME_CTRL) = temp; + + /* + * Enable all sensors' auto mode, enable dither control, + * consecutive mode, and power up sensor. + */ + temp = HWREG32(thermal->regs + REG_TSEN_PCTRL); + temp |= BIT_TSEN_RAW_SEL | BIT_TEMP_MODE | BIT_EN_SENSOR; + temp &= ~BITS_TSEN_SW_CTRL; + temp &= ~BITS_CTUNE; + HWREG32(thermal->regs + REG_TSEN_PCTRL) = temp; + + /* Select 24M clk for high speed mode */ + temp = HWREG32(thermal->regs + REG_TSEN_PCTRL2); + temp &= ~BITS_SDM_CLK_SEL; + temp |= BITS_SDM_CLK_SEL_24M; + HWREG32(thermal->regs + REG_TSEN_PCTRL2) = temp; + + /* Enable the sensor interrupt */ + for (int i = thermal->sr[0]; i <= thermal->sr[1]; ++i) + { + temp = HWREG32(thermal->regs + desc->se.bjt_en); + temp &= ~desc->se.bit_msk; + temp |= (desc->se.en_val << desc->se.offset); + HWREG32(thermal->regs + desc->se.bjt_en) = temp; + } + + return 0; +} + +static void enable_sensors(struct k1x_thermal *thermal) +{ + HWREG32(thermal->regs + REG_TSEN_INT_MASK) = + HWREG32(thermal->regs + REG_TSEN_INT_MASK) | TSEN_INT_MASK; + HWREG32(thermal->regs + REG_TSEN_PCTRL) = + HWREG32(thermal->regs + REG_TSEN_PCTRL) | BIT_HW_AUTO_MODE; +} + +static void enable_sensor_irq(struct k1x_thermal_sensor *sensor) +{ + rt_uint32_t temp; + struct k1x_thermal_sensor_desc *desc = sensor->desc; + + /* Clear the interrupt */ + temp = HWREG32(sensor->regs + desc->int_clr); + temp |= desc->bit_msk; + HWREG32(sensor->regs + desc->int_clr) = temp; + + /* Enable the interrupt */ + temp = HWREG32(sensor->regs + desc->int_msk); + temp &= ~desc->bit_msk; + HWREG32(sensor->regs + desc->int_msk) = temp; +} + +static void k1x_thermal_free(struct k1x_thermal *thermal) +{ + if (!thermal->regs) + { + rt_iounmap(thermal->regs); + } + + if (!rt_is_err_or_null(thermal->clk)) + { + rt_clk_disable_unprepare(thermal->clk); + rt_clk_put(thermal->clk); + } + + if (!rt_is_err_or_null(thermal->rstc)) + { + rt_reset_control_assert(thermal->rstc); + rt_reset_control_put(thermal->rstc); + } + + if (thermal->sensors) + { + rt_free(thermal->sensors); + } + + rt_free(thermal); +} + +static rt_err_t k1x_thermal_probe(struct rt_platform_device *pdev) +{ + rt_err_t err; + struct rt_device *dev = &pdev->parent; + struct k1x_thermal *thermal; + struct k1x_thermal_sensor_desc *desc = (void *)pdev->id->data; + + if (!desc) + { + return -RT_EINVAL; + } + + if (!(thermal = rt_calloc(1, sizeof(*thermal)))) + { + return -RT_ENOMEM; + } + + thermal->regs = rt_dm_dev_iomap(dev, 0); + if (!thermal->regs) + { + err = -RT_EINVAL; + goto _fail; + } + + thermal->irq = rt_dm_dev_get_irq(dev, 0); + if (thermal->irq < 0) + { + err = thermal->irq; + goto _fail; + } + + thermal->rstc = rt_reset_control_get_by_index(dev, 0); + if (rt_is_err(thermal->rstc)) + { + err = rt_ptr_err(thermal->rstc); + goto _fail; + } + + rt_reset_control_deassert(thermal->rstc); + + thermal->clk = rt_clk_get_by_index(dev, 0); + if (rt_is_err(thermal->clk)) + { + err = rt_ptr_err(thermal->clk); + goto _fail; + } + + rt_clk_prepare_enable(thermal->clk); + + /* Initialize the sensors */ + if ((err = init_sensors(dev, thermal, desc))) + { + goto _fail; + } + + thermal->sensors = rt_calloc(thermal->sr[1] - thermal->sr[0], sizeof(*thermal->sensors)); + if (!thermal->sensors) + { + err = -RT_ENOMEM; + goto _fail; + } + + /* Then register the thermal zone */ + for (int i = thermal->sr[0]; i <= thermal->sr[1]; ++i) + { + struct k1x_thermal_sensor *sensor; + struct rt_thermal_zone_device *tz; + + sensor = &thermal->sensors[i - thermal->sr[0]]; + sensor->regs = thermal->regs; + sensor->desc = desc; + + tz = &sensor->parent; + tz->ops = &k1x_thermal_ops; + tz->parent.ofw_node = dev->ofw_node; + + rt_dm_dev_set_name(&tz->parent, "tk1x-tz-%d", i); + rt_thermal_zone_device_register(tz); + + rt_hw_interrupt_install(thermal->irq, k1x_thermal_isr, sensor, + rt_dm_dev_get_name(&tz->parent)); + + /* Enable sensor low & higth threshold interrupt */ + enable_sensor_irq(sensor); + } + + /* Enable the sensor interrupt & using auto mode */ + enable_sensors(thermal); + + rt_hw_interrupt_umask(thermal->irq); + + dev->user_data = thermal; + + return RT_EOK; + +_fail: + k1x_thermal_free(thermal); + + return err; +} + +static rt_err_t k1x_thermal_remove(struct rt_platform_device *pdev) +{ + struct k1x_thermal *thermal = pdev->parent.user_data; + + rt_hw_interrupt_mask(thermal->irq); + + for (int i = thermal->sr[0]; i <= thermal->sr[1]; ++i) + { + struct k1x_thermal_sensor *sensor; + + sensor = &thermal->sensors[i - thermal->sr[0]]; + + rt_pic_detach_irq(thermal->irq, sensor); + rt_thermal_zone_device_unregister(&sensor->parent); + } + + k1x_thermal_free(thermal); + + return RT_EOK; +} + +static struct k1x_thermal_sensor_desc gsdesc[] = +{ + /* bjt0: local, sensor internal temperature */ + [0] = + { + .int_msk = 0x14, .int_clr = 0x10, .int_sta = 0x18, .offset = 0x0, .bit_msk = 0x6, + .se = { .bjt_en = 0x8, .en_val = 0x1, .offset = 0x0, .bit_msk = 0x1, }, + .sd = { .data_reg = 0x20, .offset = 0x0, .bit_msk = 0xffff, }, + .sr = { .temp_thrsh = 0x40, .low_offset = 0x0, .high_offset = 16, }, + }, + + /* bjt1: top */ + [1] = + { + .int_msk = 0x14, .int_clr = 0x10, .int_sta = 0x18, .offset = 0x3, .bit_msk = 0x18, + .se = { .bjt_en = 0x8, .en_val = 0x1, .offset = 0x1, .bit_msk = 0x2, }, + .sd = { .data_reg = 0x20, .offset = 16, .bit_msk = 0xffff0000, }, + .sr = { .temp_thrsh = 0x44, .low_offset = 0x0, .high_offset = 16, }, + }, + + /* bjt2: gpu */ + [2] = + { + .int_msk = 0x14, .int_clr = 0x10, .int_sta = 0x18, .offset = 0x5, .bit_msk = 0x60, + .se = { .bjt_en = 0x8, .en_val = 0x1, .offset = 0x2, .bit_msk = 0x4, }, + .sd = { .data_reg = 0x24, .offset = 0, .bit_msk = 0xffff, }, + .sr = { .temp_thrsh = 0x48, .low_offset = 0x0, .high_offset = 16, }, + }, + + /* bjt3: cluster0 */ + [3] = + { + .int_msk = 0x14, .int_clr = 0x10, .int_sta = 0x18, .offset = 0x7, .bit_msk = 0x180, + .se = { .bjt_en = 0x8, .en_val = 0x1, .offset = 0x3, .bit_msk = 0x8, }, + .sd = { .data_reg = 0x24, .offset = 16, .bit_msk = 0xffff0000, }, + .sr = { .temp_thrsh = 0x4c, .low_offset = 0x0, .high_offset = 16, }, + }, + + /* bjt4: cluster1 */ + [4] = + { + .int_msk = 0x14, .int_clr = 0x10, .int_sta = 0x18, .offset = 0x9, .bit_msk = 0x600, + .se = { .bjt_en = 0x8, .en_val = 0x1, .offset = 0x4, .bit_msk = 0x10, }, + .sd = { .data_reg = 0x28, .offset = 0, .bit_msk = 0xffff, }, + .sr = { .temp_thrsh = 0x50, .low_offset = 0x0, .high_offset = 16, }, + }, +}; + +static const struct rt_ofw_node_id k1x_thermal_ofw_ids[] = +{ + { .compatible = "spacemit,k1x-tsensor", .data = gsdesc, }, + { /* sentinel */ } +}; + +static struct rt_platform_driver k1x_thermal_driver = +{ + .name = "k1x-thermal", + .ids = k1x_thermal_ofw_ids, + + .probe = k1x_thermal_probe, + .remove = k1x_thermal_remove, +}; +RT_PLATFORM_DRIVER_EXPORT(k1x_thermal_driver); diff --git a/bsp/spacemit/dm/watchdog/Kconfig b/bsp/spacemit/dm/watchdog/Kconfig new file mode 100755 index 00000000000..e84991358e4 --- /dev/null +++ b/bsp/spacemit/dm/watchdog/Kconfig @@ -0,0 +1,3 @@ +config RT_WDT_SPACEMIT_WATCHDOG + bool "Spacemit-k1x SoC Watchdog" + default n diff --git a/bsp/spacemit/dm/watchdog/SConscript b/bsp/spacemit/dm/watchdog/SConscript new file mode 100755 index 00000000000..5b3ece5b2e2 --- /dev/null +++ b/bsp/spacemit/dm/watchdog/SConscript @@ -0,0 +1,13 @@ +from building import * + +group = [] +src = [] +cwd = GetCurrentDir() +CPPPATH = [cwd + '/../include'] + +if GetDepend(['RT_WDT_SPACEMIT_WATCHDOG']): + src += ['watchdog-k1x.c'] + +group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') diff --git a/bsp/spacemit/dm/watchdog/watchdog-k1x.c b/bsp/spacemit/dm/watchdog/watchdog-k1x.c new file mode 100755 index 00000000000..a0a7b519d2e --- /dev/null +++ b/bsp/spacemit/dm/watchdog/watchdog-k1x.c @@ -0,0 +1,470 @@ +/* + * Copyright (c) 2006-2024, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2024-5-1 GuEe-GUI first version + */ + +#include +#include + +#define DBG_TAG "wdt.k1x" +#define DBG_LVL DBG_INFO +#include + +/* Watchdog Timer Registers Offset */ +#define WDT_WMER 0x00b8 +#define WDT_WMR 0x00bc +#define WDT_WVR 0x00cc +#define WDT_WCR 0x00c8 +#define WDT_WSR 0x00c0 +#define WDT_WFAR 0x00b0 +#define WDT_WSAR 0x00b4 +#define WDT_WICR 0x00c4 + +/* Default timeout is 60s */ +#define SPACEMIT_WATCHDOG_MAX_TIMEOUT 255 +#define SPACEMIT_WATCHDOG_EXPIRE_TIME 100 +/* Touch watchdog every 30s */ +#define SPACEMIT_WATCHDOG_FEED_TIMEOUT 30 + +#define MPMU_APRR 0x1020 +#define MPMU_APRR_WDTR RT_BIT(4) +#define DEFAULT_SHIFT 8 +/* + * MPMU_APSR is a dummy reg which is used to handle reboot + * cmds. Its layout is: + * bit0~7: untouchable + * bit8~11: set to 0x1 when normal boot with no parameter + * set to 0x5 for other valid cmds. + */ +#define MPMU_ARSR 0x1028 +#define MPMU_ARSR_REBOOT_CMD(x) ((x) << 8) +#define MPMU_ARSR_SWR_MASK (0xf << 8) +#define REBOOT_CMD_NORMAL 0x1 +#define REBOOT_CMD_VALID 0x5 + +struct spa_wdt +{ + rt_watchdog_t parent; + + void *wdt_base; + void *mpmu_base; + + struct rt_clk *clk; + struct rt_reset_control *rstc; + + rt_uint32_t timeout; + + rt_bool_t ctrl; + rt_bool_t wdt_clk_open; + rt_bool_t enable_restart_handler; + + struct rt_spinlock wdt_lock; +}; + +#define raw_to_spa_wdt(raw) rt_container_of(raw, struct spa_wdt, parent) + +rt_inline rt_uint32_t spa_wdt_read(struct spa_wdt *swdt, unsigned reg) +{ + return HWREG32(swdt->wdt_base + reg); +} + +rt_inline void spa_wdt_write_access(struct spa_wdt *swdt) +{ + HWREG32(swdt->wdt_base + WDT_WFAR) = 0xbaba; + HWREG32(swdt->wdt_base + WDT_WSAR) = 0xeb10; +} + +rt_inline void spa_wdt_write(struct spa_wdt *swdt, unsigned reg, rt_uint32_t val) +{ + spa_wdt_write_access(swdt); + HWREG32(swdt->wdt_base + reg) = val; +} + +static rt_err_t spa_wdt_set_timeout(struct spa_wdt *swdt, unsigned timeout) +{ + /* + * the wdt timer is 16 bit, + * frequence is 256HZ + */ + unsigned int tick = timeout << DEFAULT_SHIFT; + + if ((long long)tick > 0xffff) + { + timeout = SPACEMIT_WATCHDOG_MAX_TIMEOUT; + tick = timeout << DEFAULT_SHIFT; + } + + spa_wdt_write(swdt, WDT_WMR, tick); + + swdt->timeout = timeout; + + return RT_EOK; +} + +static void spa_enable_wdt_clk(struct spa_wdt *swdt) +{ + rt_spin_lock(&swdt->wdt_lock); + + if (!swdt->wdt_clk_open) + { + rt_clk_prepare_enable(swdt->clk); + rt_reset_control_deassert(swdt->rstc); + swdt->wdt_clk_open = RT_TRUE; + } + + rt_spin_unlock(&swdt->wdt_lock); +} + +static void spa_disable_wdt_clk(struct spa_wdt *swdt) +{ + rt_spin_lock(&swdt->wdt_lock); + + if (swdt->wdt_clk_open) + { + rt_clk_disable_unprepare(swdt->clk); + rt_reset_control_assert(swdt->rstc); + swdt->wdt_clk_open = RT_FALSE; + } + + rt_spin_unlock(&swdt->wdt_lock); +} + +static rt_err_t spa_wdt_stop(struct spa_wdt *swdt) +{ + rt_spin_lock(&swdt->wdt_lock); + + /* Reset counter */ + spa_wdt_write(swdt, WDT_WCR, 0x1); + + /* Disable WDT */ + spa_wdt_write(swdt, WDT_WMER, 0x0); + + swdt->ctrl = RT_FALSE; + + rt_spin_unlock(&swdt->wdt_lock); + + rt_thread_mdelay(3); + + spa_disable_wdt_clk(swdt); + + return RT_EOK; +} + +static rt_err_t spa_wdt_start(struct spa_wdt *swdt) +{ + void *mpmu_aprr; + rt_uint32_t reg; + + spa_enable_wdt_clk(swdt); + + rt_spin_lock(&swdt->wdt_lock); + + /* Set timeout = 100s */ + spa_wdt_set_timeout(swdt, SPACEMIT_WATCHDOG_EXPIRE_TIME); + + /* Enable counter and rstc/interrupt */ + spa_wdt_write(swdt, WDT_WMER, 0x3); + + /* Negate hardware rstc to the WDT after system rstc */ + mpmu_aprr = swdt->mpmu_base + MPMU_APRR; + reg = HWREG32(mpmu_aprr); + reg |= MPMU_APRR_WDTR; + HWREG32(mpmu_aprr) = reg; + + /* Clear previous WDT status */ + spa_wdt_write(swdt, WDT_WSR, 0x0); + + swdt->ctrl = RT_TRUE; + + rt_spin_unlock(&swdt->wdt_lock); + + return RT_EOK; +} + +static rt_err_t spa_wdt_ping(struct spa_wdt *swdt) +{ + rt_spin_lock(&swdt->wdt_lock); + + spa_wdt_write(swdt, WDT_WCR, 0x1); + + rt_spin_unlock(&swdt->wdt_lock); + + return RT_EOK; +} + +static rt_err_t spa_wdt_init(rt_watchdog_t *wdt) +{ + return RT_EOK; +} + +static rt_err_t spa_wdt_control(rt_watchdog_t *wdt, int cmd, void *args) +{ + rt_err_t err = RT_EOK; + struct spa_wdt *swdt = raw_to_spa_wdt(wdt); + + switch (cmd) + { + case RT_DEVICE_CTRL_WDT_GET_TIMEOUT: + *(rt_uint32_t *)args = swdt->timeout; + break; + + case RT_DEVICE_CTRL_WDT_SET_TIMEOUT: + spa_wdt_set_timeout(swdt, *(rt_uint32_t *)args); + break; + + case RT_DEVICE_CTRL_WDT_GET_TIMELEFT: + err = -RT_ENOSYS; + break; + + case RT_DEVICE_CTRL_WDT_KEEPALIVE: + spa_wdt_ping(swdt); + break; + + case RT_DEVICE_CTRL_WDT_START: + spa_wdt_start(swdt); + break; + + case RT_DEVICE_CTRL_WDT_STOP: + spa_wdt_stop(swdt); + break; + + default: + err = -RT_EINVAL; + } + + return err; +} + +static const struct rt_watchdog_ops spa_wdt_ops = +{ + .init = spa_wdt_init, + .control = spa_wdt_control, +}; + +static rt_err_t spa_wdt_restart_handler(struct rt_device *dev, char *cmd) +{ + rt_uint32_t reg; + void *mpmu_arsr, *mpmu_aprr; + rt_watchdog_t *wdt = rt_container_of(dev, rt_watchdog_t, parent); + struct spa_wdt *swdt = raw_to_spa_wdt(wdt); + + rt_hw_spin_lock(&swdt->wdt_lock.lock); + + mpmu_arsr = swdt->mpmu_base + MPMU_ARSR; + reg = HWREG32(mpmu_arsr); + reg &= ~MPMU_ARSR_SWR_MASK; + + if (!cmd) + { + reg |= MPMU_ARSR_REBOOT_CMD(REBOOT_CMD_NORMAL); + } + else + { + reg |= MPMU_ARSR_REBOOT_CMD(REBOOT_CMD_VALID); + } + + HWREG32(mpmu_arsr) = reg; + + spa_enable_wdt_clk(swdt); + + spa_wdt_write(swdt, WDT_WSR, 0x0); + spa_wdt_write(swdt, WDT_WMR, 0x1); + spa_wdt_write(swdt, WDT_WMER, 0x3); + spa_wdt_write(swdt, WDT_WCR, 0x1); + + mpmu_aprr = swdt->mpmu_base + MPMU_APRR; + reg = HWREG32(mpmu_aprr); + reg |= MPMU_APRR_WDTR; + HWREG32(mpmu_aprr) = reg; + + rt_thread_mdelay(5000); + + rt_hw_spin_unlock(&swdt->wdt_lock.lock); + + return RT_EOK; +} + +#ifdef RT_USING_PM +static rt_err_t spa_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 spa_wdt *swdt = raw_to_spa_wdt(wdt); + + if (swdt->ctrl) + { + spa_wdt_stop(swdt); + } + + return RT_EOK; +} + +static void spa_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 spa_wdt *swdt = raw_to_spa_wdt(wdt); + + if (swdt->ctrl) + { + spa_wdt_start(swdt); + } +} + +static const struct rt_device_pm_ops spa_wdt_pm_ops = +{ + .suspend = spa_wdt_pm_suspend, + .resume = spa_wdt_pm_resume, +}; +#endif /* RT_USING_PM */ + +static void spa_wdt_free(struct spa_wdt *swdt) +{ + if (swdt->ctrl) + { + spa_wdt_stop(swdt); + } + + spa_disable_wdt_clk(swdt); + + if (!rt_is_err_or_null(swdt->clk)) + { + rt_clk_put(swdt->clk); + } + + if (!rt_is_err_or_null(swdt->rstc)) + { + rt_reset_control_put(swdt->rstc); + } +} + +static rt_err_t spa_wdt_probe(struct rt_platform_device *pdev) +{ + rt_err_t err; + void *mpmu_arsr; + const char *dev_name; + struct rt_device *dev = &pdev->parent; + struct spa_wdt *swdt = rt_calloc(1, sizeof(*swdt)); + + if (!swdt) + { + return -RT_ENOMEM; + } + + if (!(swdt->wdt_base = rt_dm_dev_iomap(dev, 0))) + { + err = -RT_EIO; + goto _fail; + } + + if (!(swdt->mpmu_base = rt_dm_dev_iomap(dev, 1))) + { + err = -RT_EIO; + goto _fail; + } + + mpmu_arsr = swdt->mpmu_base + MPMU_ARSR; + HWREG32(mpmu_arsr) = HWREG32(mpmu_arsr) & ~MPMU_ARSR_SWR_MASK; + + swdt->clk = rt_clk_get_by_index(dev, 0); + if (rt_is_err(swdt->clk)) + { + err = rt_ptr_err(swdt->clk); + goto _fail; + } + + swdt->rstc = rt_reset_control_get_by_index(dev, 0); + if (rt_is_err(swdt->rstc)) + { + err = rt_ptr_err(swdt->rstc); + goto _fail; + } + + swdt->enable_restart_handler = rt_dm_dev_prop_read_bool(dev, "spa,wdt-enable-restart-handler"); + + /* + * The writing of some WDT registers must be + * under the condition of that WDT clock is on + */ + spa_enable_wdt_clk(swdt); + + rt_spin_lock_init(&swdt->wdt_lock); + + dev->user_data = swdt; + + swdt->parent.ops = &spa_wdt_ops; + + rt_dm_dev_set_name_auto(&swdt->parent.parent, "wdt"); + dev_name = rt_dm_dev_get_name(&swdt->parent.parent); + + if ((err = rt_hw_watchdog_register(&swdt->parent, dev_name, 0, swdt))) + { + goto _fail; + } + +#ifdef RT_USING_PM + rt_pm_device_register(&swdt->parent.parent, &spa_wdt_pm_ops); +#endif + + if (swdt->enable_restart_handler) + { + rt_dm_reboot_mode_register(&swdt->parent.parent, spa_wdt_restart_handler); + } + + return RT_EOK; + +_fail: + spa_wdt_free(swdt); + + return err; +} + +static rt_err_t spa_wdt_remove(struct rt_platform_device *pdev) +{ + struct spa_wdt *swdt = pdev->parent.user_data; + +#ifdef RT_USING_PM + rt_pm_device_unregister(&swdt->parent.parent); +#endif + + rt_device_unregister(&swdt->parent.parent); + + spa_wdt_free(swdt); + + return RT_EOK; +} + +static rt_err_t spa_wdt_shutdown(struct rt_platform_device *pdev) +{ + struct spa_wdt *swdt = pdev->parent.user_data; + + spa_wdt_stop(swdt); + + /* No need to disable clk if enable restart_handler */ + if (swdt->enable_restart_handler) + { + spa_enable_wdt_clk(swdt); + } + + return RT_EOK; +} + +static const struct rt_ofw_node_id spa_wdt_ofw_ids[] = +{ + { .compatible = "spacemit,soc-wdt" }, + { /* sentinel */ } +}; + +static struct rt_platform_driver spa_wdt_driver = +{ + .name = "spa-wdt", + .ids = spa_wdt_ofw_ids, + + .probe = spa_wdt_probe, + .remove = spa_wdt_remove, + .shutdown = spa_wdt_shutdown, +}; +RT_PLATFORM_DRIVER_EXPORT(spa_wdt_driver); diff --git a/bsp/spacemit/k1/.config b/bsp/spacemit/k1/.config new file mode 100644 index 00000000000..352dc11031c --- /dev/null +++ b/bsp/spacemit/k1/.config @@ -0,0 +1,1763 @@ + +# +# RT-Thread Kernel +# + +# +# klibc options +# + +# +# rt_vsnprintf options +# +# CONFIG_RT_KLIBC_USING_LIBC_VSNPRINTF is not set +CONFIG_RT_KLIBC_USING_VSNPRINTF_LONGLONG=y +# CONFIG_RT_KLIBC_USING_VSNPRINTF_STANDARD is not set +# end of rt_vsnprintf options + +# +# rt_vsscanf options +# +# CONFIG_RT_KLIBC_USING_LIBC_VSSCANF is not set +# end of rt_vsscanf options + +# +# rt_memset options +# +# CONFIG_RT_KLIBC_USING_USER_MEMSET is not set +# CONFIG_RT_KLIBC_USING_LIBC_MEMSET is not set +# CONFIG_RT_KLIBC_USING_TINY_MEMSET is not set +# end of rt_memset options + +# +# rt_memcpy options +# +# CONFIG_RT_KLIBC_USING_USER_MEMCPY is not set +# CONFIG_RT_KLIBC_USING_LIBC_MEMCPY is not set +# CONFIG_RT_KLIBC_USING_TINY_MEMCPY is not set +# end of rt_memcpy options + +# +# rt_memmove options +# +# CONFIG_RT_KLIBC_USING_USER_MEMMOVE is not set +# CONFIG_RT_KLIBC_USING_LIBC_MEMMOVE is not set +# end of rt_memmove options + +# +# rt_memcmp options +# +# CONFIG_RT_KLIBC_USING_USER_MEMCMP is not set +# CONFIG_RT_KLIBC_USING_LIBC_MEMCMP is not set +# end of rt_memcmp options + +# +# rt_strstr options +# +# CONFIG_RT_KLIBC_USING_USER_STRSTR is not set +# CONFIG_RT_KLIBC_USING_LIBC_STRSTR is not set +# end of rt_strstr options + +# +# rt_strcasecmp options +# +# CONFIG_RT_KLIBC_USING_USER_STRCASECMP is not set +# end of rt_strcasecmp options + +# +# rt_strncpy options +# +# CONFIG_RT_KLIBC_USING_USER_STRNCPY is not set +# CONFIG_RT_KLIBC_USING_LIBC_STRNCPY is not set +# end of rt_strncpy options + +# +# rt_strcpy options +# +# CONFIG_RT_KLIBC_USING_USER_STRCPY is not set +# CONFIG_RT_KLIBC_USING_LIBC_STRCPY is not set +# end of rt_strcpy options + +# +# rt_strncmp options +# +# CONFIG_RT_KLIBC_USING_USER_STRNCMP is not set +# CONFIG_RT_KLIBC_USING_LIBC_STRNCMP is not set +# end of rt_strncmp options + +# +# rt_strcmp options +# +# CONFIG_RT_KLIBC_USING_USER_STRCMP is not set +# CONFIG_RT_KLIBC_USING_LIBC_STRCMP is not set +# end of rt_strcmp options + +# +# rt_strlen options +# +# CONFIG_RT_KLIBC_USING_USER_STRLEN is not set +# CONFIG_RT_KLIBC_USING_LIBC_STRLEN is not set +# end of rt_strlen options + +# +# rt_strnlen options +# +# CONFIG_RT_KLIBC_USING_USER_STRNLEN is not set +# end of rt_strnlen options +# end of klibc options + +CONFIG_RT_NAME_MAX=24 +# CONFIG_RT_USING_ARCH_DATA_TYPE is not set +# CONFIG_RT_USING_NANO is not set +# CONFIG_RT_USING_SMART is not set +# CONFIG_RT_USING_AMP is not set +# CONFIG_RT_USING_SMP is not set +CONFIG_RT_CPUS_NR=1 +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_HOOKLIST is not set +CONFIG_RT_USING_IDLE_HOOK=y +CONFIG_RT_IDLE_HOOK_LIST_SIZE=4 +CONFIG_IDLE_THREAD_STACK_SIZE=16384 +CONFIG_RT_USING_TIMER_SOFT=y +CONFIG_RT_TIMER_THREAD_PRIO=4 +CONFIG_RT_TIMER_THREAD_STACK_SIZE=16384 +# CONFIG_RT_USING_TIMER_ALL_SOFT is not set +CONFIG_RT_USING_CPU_USAGE_TRACER=y + +# +# kservice options +# +# CONFIG_RT_USING_TINY_FFS is not set +# end of kservice options + +CONFIG_RT_USING_DEBUG=y +CONFIG_RT_DEBUGING_ASSERT=y +CONFIG_RT_DEBUGING_COLOR=y +CONFIG_RT_DEBUGING_CONTEXT=y +# CONFIG_RT_DEBUGING_AUTO_INIT is not set +# CONFIG_RT_USING_CI_ACTION 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_MESSAGEQUEUE_PRIORITY is not set +CONFIG_RT_USING_SIGNALS=y +# end of Inter-Thread communication + +# +# Memory Management +# +CONFIG_RT_USING_MEMPOOL=y +# CONFIG_RT_USING_SMALL_MEM is not set +CONFIG_RT_USING_SLAB=y +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 is not set +# CONFIG_RT_USING_MEMHEAP_AS_HEAP is not set +CONFIG_RT_USING_SLAB_AS_HEAP=y +# 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 +# end of Memory Management + +CONFIG_RT_USING_DEVICE=y +CONFIG_RT_USING_DEVICE_OPS=y +CONFIG_RT_USING_INTERRUPT_INFO=y +# CONFIG_RT_USING_THREADSAFE_PRINTF is not set +CONFIG_RT_USING_CONSOLE=y +CONFIG_RT_CONSOLEBUF_SIZE=256 +CONFIG_RT_CONSOLE_DEVICE_NAME="uart0" +CONFIG_RT_VER_NUM=0x50300 +CONFIG_RT_USING_STDC_ATOMIC=y +CONFIG_RT_BACKTRACE_LEVEL_MAX_NR=32 +# end of RT-Thread Kernel + +CONFIG_ARCH_TEXT_OFFSET=0x200000 +CONFIG_ARCH_RAM_OFFSET=0 +CONFIG_ARCH_SECONDARY_CPU_STACK_SIZE=4096 +CONFIG_ARCH_HAVE_EFFICIENT_UNALIGNED_ACCESS=y +CONFIG_ARCH_HEAP_SIZE=0x4000000 +CONFIG_ARCH_INIT_PAGE_SIZE=0x400000 +CONFIG_ARCH_CPU_64BIT=y +CONFIG_RT_USING_CACHE=y +CONFIG_RT_USING_CPU_FFS=y +CONFIG_ARCH_MM_MMU=y +CONFIG_ARCH_RISCV=y +CONFIG_ARCH_RISCV64=y + +# +# RISC-V Architecture Configuration +# +# end of RISC-V Architecture Configuration + +CONFIG_ARCH_USING_NEW_CTX_SWITCH=y +CONFIG_ARCH_USING_RISCV_COMMON64=y +CONFIG_ARCH_REMAP_KERNEL=y + +# +# RT-Thread Components +# +CONFIG_RT_USING_COMPONENTS_INIT=y +CONFIG_RT_USING_USER_MAIN=y +CONFIG_RT_MAIN_THREAD_STACK_SIZE=16384 +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=16384 +CONFIG_FINSH_USING_HISTORY=y +CONFIG_FINSH_HISTORY_LINES=10 +# CONFIG_FINSH_USING_WORD_OPERATION is not set +# CONFIG_FINSH_USING_FUNC_EXT is not set +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 +CONFIG_FINSH_USING_OPTION_COMPLETION=y + +# +# DFS: device virtual file system +# +CONFIG_RT_USING_DFS=y +CONFIG_DFS_USING_POSIX=y +CONFIG_DFS_USING_WORKDIR=y +CONFIG_DFS_FD_MAX=32 +# CONFIG_RT_USING_DFS_V1 is not set +CONFIG_RT_USING_DFS_V2=y +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_DFS_ELM_USE_EXFAT is not set +# end of elm-chan's FatFs, Generic FAT Filesystem Module + +CONFIG_RT_USING_DFS_DEVFS=y +CONFIG_RT_USING_DFS_ROMFS=y +# CONFIG_RT_USING_DFS_CROMFS is not set +# CONFIG_RT_USING_DFS_TMPFS is not set +# CONFIG_RT_USING_DFS_MQUEUE is not set +# end of DFS: device virtual file system + +# CONFIG_RT_USING_FAL is not set + +# +# Device Drivers +# +CONFIG_RT_USING_DM=y +# CONFIG_RT_USING_DEV_BUS 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_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=64 +# CONFIG_RT_USING_SERIAL_BYPASS is not set +# CONFIG_RT_SERIAL_EARLY_HVC is not set +# CONFIG_RT_SERIAL_PL011 is not set +CONFIG_RT_SERIAL_8250=y +# CONFIG_RT_SERIAL_8250_DW is not set +# CONFIG_RT_SERIAL_8250_PCI is not set +CONFIG_RT_SERIAL_PXA=y +CONFIG_RT_SERIAL_EARLY_SBI=y +# CONFIG_RT_USING_CAN is not set +CONFIG_RT_USING_CPUTIME=y +CONFIG_RT_USING_CPUTIME_RISCV=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_USING_SOFT_I2C is not set +CONFIG_RT_I2C_SPACEMIT_K1X=y +# CONFIG_RT_USING_PHY is not set +CONFIG_RT_USING_PHY_V2=y +CONFIG_RT_USING_ADC=y +CONFIG_RT_ADC_SPACEMIT_PMIC=y +# 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=y +CONFIG_RT_PWM_PXA=y +# CONFIG_RT_USING_PULSE_ENCODER is not set +# CONFIG_RT_USING_INPUT_CAPTURE is not set +CONFIG_RT_USING_MTD_NOR=y +# CONFIG_RT_USING_MTD_NOR_CFI is not set +CONFIG_RT_USING_MTD_NOR_SPI=y +# CONFIG_RT_USING_MTD_NAND is not set +# CONFIG_RT_USING_PM is not set +CONFIG_RT_USING_RTC=y +CONFIG_RT_USING_ALARM=y +CONFIG_RT_ALARM_STACK_SIZE=16384 +CONFIG_RT_ALARM_TIMESLICE=5 +CONFIG_RT_ALARM_PRIORITY=10 +# CONFIG_RT_ALARM_USING_LOCAL_TIME is not set +# CONFIG_RT_USING_SOFT_RTC is not set +# CONFIG_RT_RTC_DS1302 is not set +# CONFIG_RT_RTC_DS1307 is not set +# CONFIG_RT_RTC_GOLDFISH is not set +# CONFIG_RT_RTC_HYM8563 is not set +# CONFIG_RT_RTC_PCF8523 is not set +# CONFIG_RT_RTC_PCF8563 is not set +# CONFIG_RT_RTC_PL031 is not set +# CONFIG_RT_RTC_RX8010 is not set +CONFIG_RT_RTC_SPACEMIT_PMIC=y +CONFIG_RT_USING_SDIO=y +CONFIG_RT_SDIO_STACK_SIZE=16384 +CONFIG_RT_SDIO_THREAD_PRIORITY=15 +CONFIG_RT_MMCSD_STACK_SIZE=16384 +CONFIG_RT_MMCSD_THREAD_PRIORITY=22 +CONFIG_RT_MMCSD_MAX_PARTITION=16 +CONFIG_RT_SDIO_DEBUG=y +CONFIG_RT_USING_SDHCI=y +# CONFIG_RT_SDIO_SDHCI_PCI is not set +# CONFIG_RT_SDIO_DW_MMC is not set +CONFIG_RT_USING_SPI=y +CONFIG_RT_USING_SPI_ISR=y +# CONFIG_RT_USING_SPI_BITOPS is not set +# CONFIG_RT_USING_SOFT_SPI is not set +CONFIG_RT_USING_QSPI=y +# CONFIG_RT_USING_SPI_MSD is not set +CONFIG_RT_USING_SFUD=y +CONFIG_RT_SFUD_USING_SFDP=y +CONFIG_RT_SFUD_USING_FLASH_INFO_TABLE=y +CONFIG_RT_SFUD_USING_QSPI=y +CONFIG_RT_SFUD_SPI_MAX_HZ=50000000 +# CONFIG_RT_DEBUG_SFUD is not set +# CONFIG_RT_USING_ENC28J60 is not set +# CONFIG_RT_USING_SPI_WIFI is not set +CONFIG_RT_SPI_K1X_QSPI=y +CONFIG_RT_SPI_K1X=y +CONFIG_RT_USING_WDT=y +# CONFIG_RT_WDT_DW is not set +# CONFIG_RT_WDT_I6300ESB is not set +CONFIG_RT_WDT_SPACEMIT_WATCHDOG=y +# 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=y +CONFIG_RT_USING_GRAPHIC=y +CONFIG_RT_GRAPHIC_BACKLIGHT=y +# CONFIG_RT_GRAPHIC_BACKLIGHT_GPIO is not set +CONFIG_RT_GRAPHIC_BACKLIGHT_PWM=y +CONFIG_RT_GRAPHIC_FB=y +# CONFIG_RT_GRAPHIC_FB_SIMPLE is not set +CONFIG_RT_GRAPHIC_LOGO=y +# CONFIG_RT_GRAPHIC_LOGO_NONE is not set +CONFIG_RT_GRAPHIC_LOGO_RT_THREAD_CLUT224=y +# CONFIG_RT_GRAPHIC_LOGO_RT_THREAD_WHITE_CLUT224 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=y +CONFIG_RT_HWCRYPTO_USING_AES_ECB=y +CONFIG_RT_HWCRYPTO_USING_AES_CBC=y +# CONFIG_RT_HWCRYPTO_USING_AES_CFB is not set +# CONFIG_RT_HWCRYPTO_USING_AES_CTR is not set +# CONFIG_RT_HWCRYPTO_USING_AES_OFB 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_SPACEMIT=y +CONFIG_RT_HWCRYPTO_RNG_SPACEMIT=y +# CONFIG_RT_USING_WIFI is not set +CONFIG_RT_USING_LED=y +CONFIG_RT_LED_GPIO=y +# CONFIG_RT_LED_PWM is not set +# CONFIG_RT_LED_SYSCON is not set +CONFIG_RT_USING_INPUT=y +CONFIG_RT_INPUT_POWER=y +# CONFIG_RT_INPUT_UAPI is not set +# CONFIG_RT_INPUT_JOYSTICK is not set +# CONFIG_RT_INPUT_KEYBOARD is not set +CONFIG_RT_INPUT_MISC=y +# CONFIG_RT_INPUT_MISC_BUTTON_E3X0 is not set +CONFIG_RT_INPUT_MISC_PWRKEY_SPACEMIT_PMIC=y +# CONFIG_RT_INPUT_TOUCHSCREEN is not set +CONFIG_RT_USING_MBOX=y +# CONFIG_RT_MBOX_PIC is not set +CONFIG_RT_MBOX_K1X=y +# CONFIG_RT_USING_HWSPINLOCK is not set +# CONFIG_RT_USING_PHYE is not set +CONFIG_RT_USING_ATA=y +CONFIG_RT_ATA_AHCI=y +CONFIG_RT_ATA_AHCI_PCI=y +CONFIG_RT_USING_NVME=y +CONFIG_RT_USING_NVME_IO_QUEUE=4 +CONFIG_RT_NVME_PCI=y +CONFIG_RT_USING_BLK=y + +# +# Partition Types +# +CONFIG_RT_BLK_PARTITION_DFS=y +CONFIG_RT_BLK_PARTITION_EFI=y +# end of Partition Types + +CONFIG_RT_USING_SCSI=y +CONFIG_RT_SCSI_SD=y +# CONFIG_RT_SCSI_CDROM is not set +# CONFIG_RT_USING_FIRMWARE is not set +# CONFIG_RT_USING_HWCACHE is not set +CONFIG_RT_USING_REGULATOR=y +CONFIG_RT_REGULATOR_FIXED=y +CONFIG_RT_REGULATOR_GPIO=y +CONFIG_RT_REGULATOR_SPACEMIT_PMIC=y +CONFIG_RT_USING_RESET=y +# CONFIG_RT_RESET_SIMPLE is not set +CONFIG_RT_RESET_SPACEMIT_K1=y + +# +# Power Management (PM) Domains device drivers +# +CONFIG_RT_PMDOMAIN_K1X=y +# end of Power Management (PM) Domains device drivers + +CONFIG_RT_USING_POWER_RESET=y +# CONFIG_RT_POWER_RESET_GPIO_POWEROFF is not set +# CONFIG_RT_POWER_RESET_GPIO_RESTART is not set +# CONFIG_RT_POWER_RESET_SYSCON_POWEROFF is not set +# CONFIG_RT_POWER_RESET_SYSCON_REBOOT_MODE is not set +# CONFIG_RT_POWER_RESET_SYSCON_REBOOT is not set +CONFIG_RT_POWER_RESET_REBOOT_SPACEMIT=y +# CONFIG_RT_USING_POWER_SUPPLY is not set +CONFIG_RT_USING_THERMAL=y + +# +# Thermal Sensors Drivers +# +CONFIG_RT_THERMAL_K1X=y + +# +# Thermal Cool Drivers +# +CONFIG_RT_THERMAL_COOL_PWM_FAN=y +# CONFIG_RT_USING_VIRTIO is not set +CONFIG_RT_USING_NVMEM=y +CONFIG_RT_USING_DMA=y +# CONFIG_RT_DMA_PL330 is not set +CONFIG_RT_DMA_MMP_PDMA=y +CONFIG_RT_DMA_MMP_PDMA_SUPPORT_64BIT=y +CONFIG_RT_USING_MFD=y +# CONFIG_RT_MFD_EDU is not set +CONFIG_RT_MFD_SYSCON=y +CONFIG_RT_MFD_SPACEMIT_PMIC=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_BUS_RANGES_NUMBER=8 +CONFIG_RT_USING_PCI=y +CONFIG_RT_PCI_MSI=y +CONFIG_RT_PCI_ENDPOINT=y +CONFIG_RT_PCI_SYS_64BIT=y +CONFIG_RT_PCI_CACHE_LINE_SIZE=8 +# CONFIG_RT_PCI_LOCKLESS is not set + +# +# PCI Device Drivers +# +# CONFIG_RT_PCI_ECAM is not set +CONFIG_RT_PCI_DW=y +CONFIG_RT_USING_PIC=y +# CONFIG_RT_USING_PIC_STATISTICS is not set +CONFIG_MAX_HANDLERS=1024 +# CONFIG_RT_PIC_ARM_GIC is not set +# CONFIG_RT_PIC_ARM_GIC_V3 is not set +CONFIG_RT_USING_PIN=y +# CONFIG_RT_PIN_PL061 is not set +CONFIG_RT_PIN_K1X=y +CONFIG_RT_USING_PINCTRL=y +CONFIG_RT_PINCTRL_SINGLE=y +CONFIG_RT_PINCTRL_SPACEMIT_PMIC=y +CONFIG_RT_USING_KTIME=y +CONFIG_RT_USING_CLK=y +CONFIG_RT_CLK_SPACEMIT=y +CONFIG_RT_CLK_SPACEMIT_K1X=y +CONFIG_RT_USING_HWTIMER=y +CONFIG_RT_HWTIMER_SPACEMIT_K1X=y +# CONFIG_RT_USING_CHERRYUSB is not set +# end of Device Drivers + +# +# C/C++ and POSIX layer +# + +# +# ISO-ANSI C layer +# + +# +# Timezone and Daylight Saving Time +# +# CONFIG_RT_LIBC_USING_FULL_TZ_DST is not set +CONFIG_RT_LIBC_USING_LIGHT_TZ_DST=y +CONFIG_RT_LIBC_TZ_DEFAULT_HOUR=8 +CONFIG_RT_LIBC_TZ_DEFAULT_MIN=0 +CONFIG_RT_LIBC_TZ_DEFAULT_SEC=0 +# end of Timezone and Daylight Saving Time +# end of ISO-ANSI C layer + +# +# 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=y +CONFIG_RT_USING_POSIX_SELECT=y +# CONFIG_RT_USING_POSIX_EVENTFD is not set +# CONFIG_RT_USING_POSIX_TIMERFD is not set +# CONFIG_RT_USING_POSIX_SOCKET is not set +CONFIG_RT_USING_POSIX_TERMIOS=y +CONFIG_RT_USING_POSIX_AIO=y +# 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=y +CONFIG_RT_USING_POSIX_PIPE_SIZE=512 +# CONFIG_RT_USING_POSIX_MESSAGE_QUEUE is not set +# CONFIG_RT_USING_POSIX_MESSAGE_SEMAPHORE is not set + +# +# Socket is in the 'Network' category +# +# end of Interprocess Communication (IPC) +# end of POSIX (Portable Operating System Interface) layer + +# CONFIG_RT_USING_CPLUSPLUS is not set +# end of C/C++ and POSIX layer + +# +# Network +# +CONFIG_RT_USING_SAL=y +CONFIG_SAL_INTERNET_CHECK=y +CONFIG_SOCKET_TABLE_STEP_LEN=4 + +# +# Docking with protocol stacks +# +CONFIG_SAL_USING_LWIP=y +# CONFIG_SAL_USING_AT is not set +# CONFIG_SAL_USING_TLS is not set +# end of Docking with protocol stacks + +CONFIG_SAL_USING_POSIX=y +CONFIG_RT_USING_NETDEV=y +CONFIG_NETDEV_USING_IFCONFIG=y +CONFIG_NETDEV_USING_PING=y +CONFIG_NETDEV_USING_NETSTAT=y +CONFIG_NETDEV_USING_AUTO_DEFAULT=y +# CONFIG_NETDEV_USING_LINK_STATUS_CALLBACK is not set +# CONFIG_NETDEV_USING_IPV6 is not set +CONFIG_NETDEV_IPV4=1 +CONFIG_NETDEV_IPV6=0 +CONFIG_RT_USING_LWIP=y +# CONFIG_RT_USING_LWIP_LOCAL_VERSION is not set +# CONFIG_RT_USING_LWIP141 is not set +CONFIG_RT_USING_LWIP203=y +# CONFIG_RT_USING_LWIP212 is not set +# CONFIG_RT_USING_LWIP_LATEST is not set +CONFIG_RT_USING_LWIP_VER_NUM=0x20003 +# CONFIG_RT_USING_LWIP_IPV6 is not set +CONFIG_RT_LWIP_MEM_ALIGNMENT=4 +CONFIG_RT_LWIP_IGMP=y +CONFIG_RT_LWIP_ICMP=y +# CONFIG_RT_LWIP_SNMP is not set +CONFIG_RT_LWIP_DNS=y +CONFIG_RT_LWIP_DHCP=y +CONFIG_IP_SOF_BROADCAST=1 +CONFIG_IP_SOF_BROADCAST_RECV=1 + +# +# Static IPv4 Address +# +CONFIG_RT_LWIP_IPADDR="192.168.1.30" +CONFIG_RT_LWIP_GWADDR="192.168.1.1" +CONFIG_RT_LWIP_MSKADDR="255.255.255.0" +# end of Static IPv4 Address + +CONFIG_RT_LWIP_UDP=y +CONFIG_RT_LWIP_TCP=y +CONFIG_RT_LWIP_RAW=y +# CONFIG_RT_LWIP_PPP is not set +CONFIG_RT_MEMP_NUM_NETCONN=8 +CONFIG_RT_LWIP_PBUF_NUM=16 +CONFIG_RT_LWIP_RAW_PCB_NUM=4 +CONFIG_RT_LWIP_UDP_PCB_NUM=4 +CONFIG_RT_LWIP_TCP_PCB_NUM=4 +CONFIG_RT_LWIP_TCP_SEG_NUM=40 +CONFIG_RT_LWIP_TCP_SND_BUF=8196 +CONFIG_RT_LWIP_TCP_WND=8196 +CONFIG_RT_LWIP_TCPTHREAD_PRIORITY=10 +CONFIG_RT_LWIP_TCPTHREAD_MBOX_SIZE=8 +CONFIG_RT_LWIP_TCPTHREAD_STACKSIZE=8192 +# CONFIG_LWIP_NO_RX_THREAD is not set +# CONFIG_LWIP_NO_TX_THREAD is not set +CONFIG_RT_LWIP_ETHTHREAD_PRIORITY=12 +CONFIG_RT_LWIP_ETHTHREAD_STACKSIZE=8192 +CONFIG_RT_LWIP_ETHTHREAD_MBOX_SIZE=8 +# CONFIG_RT_LWIP_REASSEMBLY_FRAG is not set +CONFIG_LWIP_NETIF_STATUS_CALLBACK=1 +CONFIG_LWIP_NETIF_LINK_CALLBACK=1 +CONFIG_RT_LWIP_NETIF_NAMESIZE=6 +CONFIG_SO_REUSE=1 +CONFIG_LWIP_SO_RCVTIMEO=1 +CONFIG_LWIP_SO_SNDTIMEO=1 +CONFIG_LWIP_SO_RCVBUF=1 +CONFIG_LWIP_SO_LINGER=0 +# CONFIG_RT_LWIP_NETIF_LOOPBACK is not set +CONFIG_LWIP_NETIF_LOOPBACK=0 +# CONFIG_RT_LWIP_STATS is not set +# CONFIG_RT_LWIP_USING_HW_CHECKSUM is not set +CONFIG_RT_LWIP_USING_PING=y +# CONFIG_LWIP_USING_DHCPD is not set +# CONFIG_RT_LWIP_ENABLE_USER_HOOKS is not set +# CONFIG_RT_LWIP_DEBUG is not set +# CONFIG_RT_USING_AT is not set +# end of Network + +# +# Memory protection +# +# CONFIG_RT_USING_MEM_PROTECTION is not set +# CONFIG_RT_USING_HW_STACK_GUARD is not set +# end of Memory protection + +# +# 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_RESOURCE_ID=y +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 +# end of Utilities + +# +# Memory management +# +CONFIG_RT_PAGE_MPR_SIZE_DYNAMIC=y +CONFIG_RT_PAGE_AFFINITY_BLOCK_SIZE=0x1000 +CONFIG_RT_PAGE_MAX_ORDER=11 +CONFIG_RT_USING_MEMBLOCK=y +CONFIG_RT_INIT_MEMORY_REGIONS=128 + +# +# Debugging +# +# CONFIG_RT_DEBUGGING_ALIASING is not set +# CONFIG_RT_DEBUGING_PAGE_LEAK is not set +# CONFIG_RT_DEBUGGING_PAGE_POISON is not set +# end of Debugging +# end of Memory management + +# +# Using USB legacy version +# +# CONFIG_RT_USING_USB_HOST is not set +# CONFIG_RT_USING_USB_DEVICE is not set +# end of Using USB legacy version + +# CONFIG_RT_USING_FDT is not set +# CONFIG_RT_USING_RUST is not set +# end of RT-Thread Components + +# +# RT-Thread Utestcases +# +# CONFIG_RT_USING_UTESTCASES is not set +# end of RT-Thread Utestcases + +# +# RT-Thread online packages +# + +# +# IoT - internet of things +# +# 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_FREEMODBUS is not set +# CONFIG_PKG_USING_NANOPB is not set +# CONFIG_PKG_USING_WIFI_HOST_DRIVER is not set +# CONFIG_PKG_USING_ESP_HOSTED is not set + +# +# Wi-Fi +# + +# +# Marvell WiFi +# +# CONFIG_PKG_USING_WLANMARVELL is not set +# end of Marvell WiFi + +# +# Wiced WiFi +# +# CONFIG_PKG_USING_WLAN_WICED is not set +# end of Wiced WiFi + +# CONFIG_PKG_USING_RW007 is not set + +# +# CYW43012 WiFi +# +# CONFIG_PKG_USING_WLAN_CYW43012 is not set +# end of CYW43012 WiFi + +# +# BL808 WiFi +# +# CONFIG_PKG_USING_WLAN_BL808 is not set +# end of BL808 WiFi + +# +# CYW43439 WiFi +# +# CONFIG_PKG_USING_WLAN_CYW43439 is not set +# end of CYW43439 WiFi +# end of Wi-Fi + +# 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_IOTSHARP_SDK is not set +# end of IoT Cloud + +# 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_BT_CYW43012 is not set +# CONFIG_PKG_USING_CYW43XX 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 +# CONFIG_PKG_USING_MATTER_ADAPTATION_LAYER is not set +# CONFIG_PKG_USING_LHC_MODBUS is not set +# CONFIG_PKG_USING_QMODBUS is not set +# CONFIG_PKG_USING_PNET is not set +# CONFIG_PKG_USING_OPENER is not set +# CONFIG_PKG_USING_FREEMQTT is not set +# end of IoT - internet of things + +# +# 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 +# end of security packages + +# +# 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 +# CONFIG_PKG_USING_RYAN_JSON is not set +# end of JSON: JavaScript Object Notation, a lightweight data-interchange format + +# +# XML: Extensible Markup Language +# +# CONFIG_PKG_USING_SIMPLE_XML is not set +# CONFIG_PKG_USING_EZXML is not set +# end of XML: Extensible Markup Language + +# 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 +# end of language packages + +# +# multimedia packages +# + +# +# LVGL: powerful and easy-to-use embedded GUI library +# +# CONFIG_PKG_USING_LVGL is not set +# CONFIG_PKG_USING_LV_MUSIC_DEMO is not set +# CONFIG_PKG_USING_GUI_GUIDER_DEMO is not set +# end of LVGL: powerful and easy-to-use embedded GUI library + +# +# u8g2: a monochrome graphic library +# +# CONFIG_PKG_USING_U8G2_OFFICIAL is not set +# CONFIG_PKG_USING_U8G2 is not set +# end of u8g2: a monochrome graphic library + +# 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 +# end of multimedia packages + +# +# tools packages +# +# CONFIG_PKG_USING_CMBACKTRACE is not set +# CONFIG_PKG_USING_MCOREDUMP 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 +# CONFIG_PKG_USING_ZDEBUG is not set +# CONFIG_PKG_USING_RVBACKTRACE is not set +# CONFIG_PKG_USING_HPATCHLITE is not set +# CONFIG_PKG_USING_THREAD_METRIC is not set +# end of tools packages + +# +# system packages +# + +# +# enhanced kernel services +# +# CONFIG_PKG_USING_RT_MEMCPY_CM is not set +# CONFIG_PKG_USING_RT_KPRINTF_THREADSAFE is not set +# end of enhanced kernel services + +# CONFIG_PKG_USING_AUNITY is not set + +# +# 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 +# end of acceleration: Assembly language or algorithmic acceleration packages + +# +# CMSIS: ARM Cortex-M Microcontroller Software Interface Standard +# +# CONFIG_PKG_USING_CMSIS_5 is not set +# CONFIG_PKG_USING_CMSIS_CORE is not set +# CONFIG_PKG_USING_CMSIS_NN is not set +# CONFIG_PKG_USING_CMSIS_RTOS1 is not set +# CONFIG_PKG_USING_CMSIS_RTOS2 is not set +# end of CMSIS: ARM Cortex-M Microcontroller Software Interface Standard + +# +# 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 +# end of Micrium: Micrium software products porting for RT-Thread + +# CONFIG_PKG_USING_FREERTOS_WRAPPER is not set +# CONFIG_PKG_USING_LITEOS_SDK is not set +# CONFIG_PKG_USING_TZ_DATABASE 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_FILEX is not set +# CONFIG_PKG_USING_LEVELX 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_RPMSG_LITE 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_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 +# CONFIG_PKG_USING_TASK_MSG_BUS is not set +# CONFIG_PKG_USING_UART_FRAMEWORK is not set +# CONFIG_PKG_USING_SFDB is not set +# CONFIG_PKG_USING_RTP is not set +# CONFIG_PKG_USING_REB is not set +# CONFIG_PKG_USING_RMP is not set +# CONFIG_PKG_USING_R_RHEALSTONE is not set +# CONFIG_PKG_USING_HEARTBEAT is not set +# CONFIG_PKG_USING_MICRO_ROS_RTTHREAD_PACKAGE is not set +# end of system packages + +# +# peripheral libraries and drivers +# + +# +# HAL & SDK Drivers +# + +# +# STM32 HAL & SDK Drivers +# +# CONFIG_PKG_USING_STM32F0_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32F0_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32F1_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32F1_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32F2_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32F2_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32F3_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32F3_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32F4_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32F4_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32F7_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32F7_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32G0_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32G0_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32G4_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32G4_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32H5_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32H5_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32H7_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32H7_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32H7RS_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32H7RS_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32L0_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32L0_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32L4_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32L4_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32L5_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32L5_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32U5_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32U5_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32WB55_SDK is not set +# CONFIG_PKG_USING_STM32_SDIO is not set +# CONFIG_PKG_USING_STM32WL_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32WL_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32WB_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32WB_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32MP1_M4_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32MP1_M4_CMSIS_DRIVER is not set +# end of STM32 HAL & SDK Drivers + +# +# Infineon HAL Packages +# +# CONFIG_PKG_USING_INFINEON_CAT1CM0P is not set +# CONFIG_PKG_USING_INFINEON_CMSIS is not set +# CONFIG_PKG_USING_INFINEON_CORE_LIB is not set +# CONFIG_PKG_USING_INFINEON_MTB_HAL_CAT1 is not set +# CONFIG_PKG_USING_INFINEON_MTB_PDL_CAT1 is not set +# CONFIG_PKG_USING_INFINEON_RETARGET_IO is not set +# CONFIG_PKG_USING_INFINEON_CAPSENSE is not set +# CONFIG_PKG_USING_INFINEON_CSDIDAC is not set +# CONFIG_PKG_USING_INFINEON_SERIAL_FLASH is not set +# CONFIG_PKG_USING_INFINEON_USBDEV is not set +# end of Infineon HAL Packages + +# CONFIG_PKG_USING_BLUETRUM_SDK is not set +# CONFIG_PKG_USING_EMBARC_BSP is not set +# CONFIG_PKG_USING_ESP_IDF is not set + +# +# Kendryte SDK +# +# CONFIG_PKG_USING_K210_SDK is not set +# CONFIG_PKG_USING_KENDRYTE_SDK is not set +# end of Kendryte SDK + +# CONFIG_PKG_USING_NRF5X_SDK is not set +# CONFIG_PKG_USING_NRFX is not set +# CONFIG_PKG_USING_NUCLEI_SDK is not set +# CONFIG_PKG_USING_RASPBERRYPI_PICO_RP2350_SDK is not set +# CONFIG_PKG_USING_RASPBERRYPI_PICO_SDK is not set +# CONFIG_PKG_USING_MM32 is not set + +# +# WCH HAL & SDK Drivers +# +# CONFIG_PKG_USING_CH32V20x_SDK is not set +# CONFIG_PKG_USING_CH32V307_SDK is not set +# end of WCH HAL & SDK Drivers + +# +# AT32 HAL & SDK Drivers +# +# CONFIG_PKG_USING_AT32A403A_HAL_DRIVER is not set +# CONFIG_PKG_USING_AT32A403A_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_AT32A423_HAL_DRIVER is not set +# CONFIG_PKG_USING_AT32A423_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_AT32F45x_HAL_DRIVER is not set +# CONFIG_PKG_USING_AT32F45x_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_AT32F402_405_HAL_DRIVER is not set +# CONFIG_PKG_USING_AT32F402_405_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_AT32F403A_407_HAL_DRIVER is not set +# CONFIG_PKG_USING_AT32F403A_407_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_AT32F413_HAL_DRIVER is not set +# CONFIG_PKG_USING_AT32F413_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_AT32F415_HAL_DRIVER is not set +# CONFIG_PKG_USING_AT32F415_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_AT32F421_HAL_DRIVER is not set +# CONFIG_PKG_USING_AT32F421_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_AT32F423_HAL_DRIVER is not set +# CONFIG_PKG_USING_AT32F423_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_AT32F425_HAL_DRIVER is not set +# CONFIG_PKG_USING_AT32F425_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_AT32F435_437_HAL_DRIVER is not set +# CONFIG_PKG_USING_AT32F435_437_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_AT32M412_416_HAL_DRIVER is not set +# CONFIG_PKG_USING_AT32M412_416_CMSIS_DRIVER is not set +# end of AT32 HAL & SDK Drivers + +# +# HC32 DDL Drivers +# +# CONFIG_PKG_USING_HC32F3_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_HC32F3_SERIES_DRIVER is not set +# CONFIG_PKG_USING_HC32F4_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_HC32F4_SERIES_DRIVER is not set +# end of HC32 DDL Drivers + +# +# NXP HAL & SDK Drivers +# +# CONFIG_PKG_USING_NXP_MCX_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_NXP_MCX_SERIES_DRIVER is not set +# CONFIG_PKG_USING_NXP_LPC_DRIVER is not set +# CONFIG_PKG_USING_NXP_LPC55S_DRIVER is not set +# CONFIG_PKG_USING_NXP_IMX6SX_DRIVER is not set +# CONFIG_PKG_USING_NXP_IMX6UL_DRIVER is not set +# CONFIG_PKG_USING_NXP_IMXRT_DRIVER is not set +# end of NXP HAL & SDK Drivers + +# +# NUVOTON Drivers +# +# CONFIG_PKG_USING_NUVOTON_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_NUVOTON_SERIES_DRIVER is not set +# CONFIG_PKG_USING_NUVOTON_ARM926_LIB is not set +# end of NUVOTON Drivers + +# +# GD32 Drivers +# +# CONFIG_PKG_USING_GD32_ARM_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_GD32_ARM_SERIES_DRIVER is not set +# end of GD32 Drivers +# end of HAL & SDK 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_MAX31855 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_MLX90382 is not set +# CONFIG_PKG_USING_MLX90393 is not set +# CONFIG_PKG_USING_MLX90392 is not set +# CONFIG_PKG_USING_MLX90394 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_SHT4X 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 +# CONFIG_PKG_USING_P3T1755 is not set +# CONFIG_PKG_USING_QMI8658 is not set +# CONFIG_PKG_USING_ICM20948 is not set +# end of sensors drivers + +# +# 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_CST816X is not set +# CONFIG_PKG_USING_CST812T is not set +# end of touch drivers + +# CONFIG_PKG_USING_REALTEK_AMEBA 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_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_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_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_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_LRF_NV7LIDAR is not set +# CONFIG_PKG_USING_AIP650 is not set +# CONFIG_PKG_USING_FINGERPRINT is not set +# CONFIG_PKG_USING_BT_ECB02C is not set +# CONFIG_PKG_USING_UAT is not set +# CONFIG_PKG_USING_ST7789 is not set +# CONFIG_PKG_USING_VS1003 is not set +# CONFIG_PKG_USING_X9555 is not set +# CONFIG_PKG_USING_SYSTEM_RUN_LED is not set +# CONFIG_PKG_USING_BT_MX01 is not set +# CONFIG_PKG_USING_RGPOWER is not set +# CONFIG_PKG_USING_BT_MX02 is not set +# CONFIG_PKG_USING_GC9A01 is not set +# CONFIG_PKG_USING_IK485 is not set +# CONFIG_PKG_USING_SERVO is not set +# CONFIG_PKG_USING_SEAN_WS2812B is not set +# CONFIG_PKG_USING_IC74HC165 is not set +# CONFIG_PKG_USING_IST8310 is not set +# CONFIG_PKG_USING_ST7789_SPI is not set +# CONFIG_PKG_USING_SPI_TOOLS is not set +# end of peripheral libraries and drivers + +# +# 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_R_TINYMAIX is not set +# CONFIG_PKG_USING_LLMCHAT is not set +# end of AI packages + +# +# Signal Processing and Control Algorithm Packages +# +# CONFIG_PKG_USING_APID is not set +# CONFIG_PKG_USING_FIRE_PID_CURVE is not set +# CONFIG_PKG_USING_QPID is not set +# CONFIG_PKG_USING_UKAL is not set +# CONFIG_PKG_USING_DIGITALCTRL is not set +# CONFIG_PKG_USING_KISSFFT is not set +# end of Signal Processing and Control Algorithm Packages + +# +# miscellaneous packages +# + +# +# project laboratory +# +# end of 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 +# end of samples: kernel and components samples + +# +# 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 +# end of entertainment: terminal games and other interesting software packages + +# 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_RALARAM 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_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_LIBCRC 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 +# CONFIG_PKG_USING_DRMP is not set +# end of miscellaneous packages + +# +# Arduino libraries +# +# CONFIG_PKG_USING_RTDUINO is not set + +# +# Projects and Demos +# +# CONFIG_PKG_USING_ARDUINO_MSGQ_C_CPP_DEMO is not set +# CONFIG_PKG_USING_ARDUINO_SKETCH_LOADER_DEMO is not set +# CONFIG_PKG_USING_ARDUINO_ULTRASOUND_RADAR is not set +# CONFIG_PKG_USING_ARDUINO_RTDUINO_SENSORFUSION_SHIELD is not set +# CONFIG_PKG_USING_ARDUINO_NINEINONE_SENSOR_SHIELD is not set +# CONFIG_PKG_USING_ARDUINO_SENSOR_KIT is not set +# CONFIG_PKG_USING_ARDUINO_MATLAB_SUPPORT is not set +# end of Projects and Demos + +# +# Sensors +# +# CONFIG_PKG_USING_ARDUINO_SENSOR_DEVICE_DRIVERS is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SENSOR is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SENSORLAB 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_VL6180X is not set +# CONFIG_PKG_USING_ARDUINO_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_ARDUINO_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_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_ARDUINO_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_ARDUINO_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 +# CONFIG_PKG_USING_ARDUINO_CAPACITIVESENSOR is not set +# CONFIG_PKG_USING_ARDUINO_JARZEBSKI_MPU6050 is not set +# end of Sensors + +# +# Display +# +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_GFX_LIBRARY is not set +# CONFIG_PKG_USING_ARDUINO_U8G2 is not set +# CONFIG_PKG_USING_ARDUINO_TFT_ESPI is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_ST7735 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SSD1306 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_ILI9341 is not set +# CONFIG_PKG_USING_SEEED_TM1637 is not set +# end of Display + +# +# Timing +# +# CONFIG_PKG_USING_ARDUINO_RTCLIB is not set +# CONFIG_PKG_USING_ARDUINO_MSTIMER2 is not set +# CONFIG_PKG_USING_ARDUINO_TICKER is not set +# CONFIG_PKG_USING_ARDUINO_TASKSCHEDULER is not set +# end of Timing + +# +# Data Processing +# +# CONFIG_PKG_USING_ARDUINO_KALMANFILTER is not set +# CONFIG_PKG_USING_ARDUINO_ARDUINOJSON is not set +# CONFIG_PKG_USING_ARDUINO_TENSORFLOW_LITE_MICRO is not set +# CONFIG_PKG_USING_ARDUINO_RUNNINGMEDIAN is not set +# end of Data Processing + +# +# Data Storage +# + +# +# Communication +# +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_PN532 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SI4713 is not set +# end of Communication + +# +# Device Control +# +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_PCF8574 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_PCA9685 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 +# CONFIG_PKG_USING_ARDUINO_SEEED_PCF85063TP is not set +# end of Device Control + +# +# Other +# +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MFRC630 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SI5351 is not set +# end of Other + +# +# 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 +# end of Signal IO + +# +# Uncategorized +# +# end of Arduino libraries +# end of RT-Thread online packages + +CONFIG_SOC_SPACEMIT_K1=y diff --git a/bsp/spacemit/k1/Kconfig b/bsp/spacemit/k1/Kconfig new file mode 100755 index 00000000000..30d87c07dfa --- /dev/null +++ b/bsp/spacemit/k1/Kconfig @@ -0,0 +1,26 @@ +mainmenu "RT-Thread Project Configuration" + +BSP_DIR := . + +RTT_DIR := ../../.. + +PKGS_DIR := packages + +SOC_DM_DIR := $BSP_DIR/../dm/ + +source "$(SOC_DM_DIR)/Kconfig" +source "$(RTT_DIR)/Kconfig" +osource "$PKGS_DIR/Kconfig" + +config SOC_SPACEMIT_K1 + bool + select ARCH_RISCV64 + select ARCH_CPU_64BIT + select ARCH_USING_RISCV_COMMON64 + select RT_USING_COMPONENTS_INIT + select RT_USING_USER_MAIN + select RT_USING_CACHE + select ARCH_MM_MMU + select ARCH_REMAP_KERNEL + select RT_USING_CPU_FFS + default y diff --git a/bsp/spacemit/k1/README.md b/bsp/spacemit/k1/README.md new file mode 100755 index 00000000000..8a11442a219 --- /dev/null +++ b/bsp/spacemit/k1/README.md @@ -0,0 +1,10 @@ +https://archive.spacemit.com/toolchain/ + +```shell +setenv boot_rtt 'setenv knl_name rtthread.bin;run bootcmd' +saveenv +``` + +```shell +run boot_rtt +``` diff --git a/bsp/spacemit/k1/SConscript b/bsp/spacemit/k1/SConscript new file mode 100755 index 00000000000..3b4078130e7 --- /dev/null +++ b/bsp/spacemit/k1/SConscript @@ -0,0 +1,17 @@ +# 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')) + +objs = objs + SConscript(cwd + '/../dm/SConscript', variant_dir = 'dm', duplicate = 0) + +Return('objs') + \ No newline at end of file diff --git a/bsp/spacemit/k1/SConstruct b/bsp/spacemit/k1/SConstruct new file mode 100755 index 00000000000..cf25bd5ecc3 --- /dev/null +++ b/bsp/spacemit/k1/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, CCFLAGS = 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/spacemit/k1/applications/SConscript b/bsp/spacemit/k1/applications/SConscript new file mode 100755 index 00000000000..c583d3016e0 --- /dev/null +++ b/bsp/spacemit/k1/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/spacemit/k1/applications/main.c b/bsp/spacemit/k1/applications/main.c new file mode 100755 index 00000000000..4450b5b4b0a --- /dev/null +++ b/bsp/spacemit/k1/applications/main.c @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2017-5-30 bernard the first version + */ + +#include + +int main(int argc, char** argv) +{ + rt_kprintf("Hi, this is RT-Thread!!\n"); + + return 0; +} diff --git a/bsp/spacemit/k1/drivers/SConscript b/bsp/spacemit/k1/drivers/SConscript new file mode 100755 index 00000000000..0022841c24d --- /dev/null +++ b/bsp/spacemit/k1/drivers/SConscript @@ -0,0 +1,11 @@ +# RT-Thread building script for component + +from building import * + +cwd = GetCurrentDir() +src = Glob('*.c') + Glob('*.S') +CPPPATH = [cwd] + +group = DefineGroup('Drivers', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') diff --git a/bsp/spacemit/k1/drivers/board.c b/bsp/spacemit/k1/drivers/board.c new file mode 100755 index 00000000000..c2fecf6a781 --- /dev/null +++ b/bsp/spacemit/k1/drivers/board.c @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2006-2024, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2024-5-1 GuEe-GUI first version + */ + +#include +#include +#include + +void rt_hw_board_init(void) +{ + rt_hw_common_setup(); +} + +void rt_hw_fdt_vendor_install_early(void *fdt) +{ + int node; + + node = fdt_path_offset(fdt, "/cpus"); + fdt_setprop_u32(fdt, node, "cpu-boot-delay-us", 100); +} diff --git a/bsp/spacemit/k1/drivers/board.h b/bsp/spacemit/k1/drivers/board.h new file mode 100755 index 00000000000..c1e8fa6f895 --- /dev/null +++ b/bsp/spacemit/k1/drivers/board.h @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2006-2024, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2024-5-1 GuEe-GUI first version + */ + +#ifndef __BOARD_H__ +#define __BOARD_H__ + +extern unsigned int __bss_start; +extern unsigned int __bss_end; + +void rt_hw_board_init(void); + +#endif /* __BOARD_H__ */ diff --git a/bsp/spacemit/k1/drivers/firmware.S b/bsp/spacemit/k1/drivers/firmware.S new file mode 100755 index 00000000000..a419183f9ec --- /dev/null +++ b/bsp/spacemit/k1/drivers/firmware.S @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2006-2024, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2024-5-1 GuEe-GUI first version + */ + +#include + + .section ".rodata" + SPACEMIT_FIRMWARE_DEFINE esos.elf + +SPACEMIT_FIRMWARE_START + SPACEMIT_FIRMWARE_REF esos.elf +SPACEMIT_FIRMWARE_END diff --git a/bsp/spacemit/k1/rtconfig.h b/bsp/spacemit/k1/rtconfig.h new file mode 100755 index 00000000000..b799bd7c8ad --- /dev/null +++ b/bsp/spacemit/k1/rtconfig.h @@ -0,0 +1,679 @@ +#ifndef RT_CONFIG_H__ +#define RT_CONFIG_H__ + +/* RT-Thread Kernel */ + +/* klibc options */ + +/* rt_vsnprintf options */ + +#define RT_KLIBC_USING_VSNPRINTF_LONGLONG +/* end of rt_vsnprintf options */ + +/* rt_vsscanf options */ + +/* end of rt_vsscanf options */ + +/* rt_memset options */ + +/* end of rt_memset options */ + +/* rt_memcpy options */ + +/* end of rt_memcpy options */ + +/* rt_memmove options */ + +/* end of rt_memmove options */ + +/* rt_memcmp options */ + +/* end of rt_memcmp options */ + +/* rt_strstr options */ + +/* end of rt_strstr options */ + +/* rt_strcasecmp options */ + +/* end of rt_strcasecmp options */ + +/* rt_strncpy options */ + +/* end of rt_strncpy options */ + +/* rt_strcpy options */ + +/* end of rt_strcpy options */ + +/* rt_strncmp options */ + +/* end of rt_strncmp options */ + +/* rt_strcmp options */ + +/* end of rt_strcmp options */ + +/* rt_strlen options */ + +/* end of rt_strlen options */ + +/* rt_strnlen options */ + +/* end of rt_strnlen options */ +/* end of klibc options */ +#define RT_NAME_MAX 24 +#define RT_CPUS_NR 1 +#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 16384 +#define RT_USING_TIMER_SOFT +#define RT_TIMER_THREAD_PRIO 4 +#define RT_TIMER_THREAD_STACK_SIZE 16384 +#define RT_USING_CPU_USAGE_TRACER + +/* kservice options */ + +/* end of kservice options */ +#define RT_USING_DEBUG +#define RT_DEBUGING_ASSERT +#define RT_DEBUGING_COLOR +#define RT_DEBUGING_CONTEXT + +/* Inter-Thread communication */ + +#define RT_USING_SEMAPHORE +#define RT_USING_MUTEX +#define RT_USING_EVENT +#define RT_USING_MAILBOX +#define RT_USING_MESSAGEQUEUE +#define RT_USING_SIGNALS +/* end of Inter-Thread communication */ + +/* Memory Management */ + +#define RT_USING_MEMPOOL +#define RT_USING_SLAB +#define RT_USING_MEMHEAP +#define RT_MEMHEAP_FAST_MODE +#define RT_USING_SLAB_AS_HEAP +#define RT_USING_MEMTRACE +#define RT_USING_HEAP +/* end of Memory Management */ +#define RT_USING_DEVICE +#define RT_USING_DEVICE_OPS +#define RT_USING_INTERRUPT_INFO +#define RT_USING_CONSOLE +#define RT_CONSOLEBUF_SIZE 256 +#define RT_CONSOLE_DEVICE_NAME "uart0" +#define RT_VER_NUM 0x50300 +#define RT_USING_STDC_ATOMIC +#define RT_BACKTRACE_LEVEL_MAX_NR 32 +/* end of RT-Thread Kernel */ +#define ARCH_TEXT_OFFSET 0x200000 +#define ARCH_RAM_OFFSET 0 +#define ARCH_SECONDARY_CPU_STACK_SIZE 4096 +#define ARCH_HAVE_EFFICIENT_UNALIGNED_ACCESS +#define ARCH_HEAP_SIZE 0x4000000 +#define ARCH_INIT_PAGE_SIZE 0x400000 +#define ARCH_CPU_64BIT +#define RT_USING_CACHE +#define RT_USING_CPU_FFS +#define ARCH_MM_MMU +#define ARCH_RISCV +#define ARCH_RISCV64 + +/* RISC-V Architecture Configuration */ + +/* end of RISC-V Architecture Configuration */ +#define ARCH_USING_NEW_CTX_SWITCH +#define ARCH_USING_RISCV_COMMON64 +#define ARCH_REMAP_KERNEL + +/* RT-Thread Components */ + +#define RT_USING_COMPONENTS_INIT +#define RT_USING_USER_MAIN +#define RT_MAIN_THREAD_STACK_SIZE 16384 +#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 16384 +#define FINSH_USING_HISTORY +#define FINSH_HISTORY_LINES 10 +#define FINSH_USING_SYMTAB +#define FINSH_CMD_SIZE 80 +#define MSH_USING_BUILT_IN_COMMANDS +#define FINSH_USING_DESCRIPTION +#define FINSH_ARG_MAX 10 +#define FINSH_USING_OPTION_COMPLETION + +/* DFS: device virtual file system */ + +#define RT_USING_DFS +#define DFS_USING_POSIX +#define DFS_USING_WORKDIR +#define DFS_FD_MAX 32 +#define RT_USING_DFS_V2 +#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 +/* end of elm-chan's FatFs, Generic FAT Filesystem Module */ +#define RT_USING_DFS_DEVFS +#define RT_USING_DFS_ROMFS +/* end of DFS: device virtual file system */ + +/* Device Drivers */ + +#define RT_USING_DM +#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_SERIAL +#define RT_USING_SERIAL_V1 +#define RT_SERIAL_USING_DMA +#define RT_SERIAL_RB_BUFSZ 64 +#define RT_SERIAL_8250 +#define RT_SERIAL_PXA +#define RT_SERIAL_EARLY_SBI +#define RT_USING_CPUTIME +#define RT_USING_CPUTIME_RISCV +#define RT_USING_I2C +#define RT_USING_I2C_BITOPS +#define RT_I2C_SPACEMIT_K1X +#define RT_USING_PHY_V2 +#define RT_USING_ADC +#define RT_ADC_SPACEMIT_PMIC +#define RT_USING_NULL +#define RT_USING_ZERO +#define RT_USING_RANDOM +#define RT_USING_PWM +#define RT_PWM_PXA +#define RT_USING_MTD_NOR +#define RT_USING_MTD_NOR_SPI +#define RT_USING_RTC +#define RT_USING_ALARM +#define RT_ALARM_STACK_SIZE 16384 +#define RT_ALARM_TIMESLICE 5 +#define RT_ALARM_PRIORITY 10 +#define RT_RTC_SPACEMIT_PMIC +#define RT_USING_SDIO +#define RT_SDIO_STACK_SIZE 16384 +#define RT_SDIO_THREAD_PRIORITY 15 +#define RT_MMCSD_STACK_SIZE 16384 +#define RT_MMCSD_THREAD_PRIORITY 22 +#define RT_MMCSD_MAX_PARTITION 16 +#define RT_SDIO_DEBUG +#define RT_USING_SDHCI +#define RT_USING_SPI +#define RT_USING_SPI_ISR +#define RT_USING_QSPI +#define RT_USING_SFUD +#define RT_SFUD_USING_SFDP +#define RT_SFUD_USING_FLASH_INFO_TABLE +#define RT_SFUD_USING_QSPI +#define RT_SFUD_SPI_MAX_HZ 50000000 +#define RT_SPI_K1X_QSPI +#define RT_SPI_K1X +#define RT_USING_WDT +#define RT_WDT_SPACEMIT_WATCHDOG +#define RT_USING_LCD +#define RT_USING_GRAPHIC +#define RT_GRAPHIC_BACKLIGHT +#define RT_GRAPHIC_BACKLIGHT_PWM +#define RT_GRAPHIC_FB +#define RT_GRAPHIC_LOGO +#define RT_GRAPHIC_LOGO_RT_THREAD_CLUT224 +#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_AES +#define RT_HWCRYPTO_USING_AES_ECB +#define RT_HWCRYPTO_USING_AES_CBC +#define RT_HWCRYPTO_USING_RNG +#define RT_HWCRYPTO_SPACEMIT +#define RT_HWCRYPTO_RNG_SPACEMIT +#define RT_USING_LED +#define RT_LED_GPIO +#define RT_USING_INPUT +#define RT_INPUT_POWER +#define RT_INPUT_MISC +#define RT_INPUT_MISC_PWRKEY_SPACEMIT_PMIC +#define RT_USING_MBOX +#define RT_MBOX_K1X +#define RT_USING_ATA +#define RT_ATA_AHCI +#define RT_ATA_AHCI_PCI +#define RT_USING_NVME +#define RT_USING_NVME_IO_QUEUE 4 +#define RT_NVME_PCI +#define RT_USING_BLK + +/* Partition Types */ + +#define RT_BLK_PARTITION_DFS +#define RT_BLK_PARTITION_EFI +/* end of Partition Types */ +#define RT_USING_SCSI +#define RT_SCSI_SD +#define RT_USING_REGULATOR +#define RT_REGULATOR_FIXED +#define RT_REGULATOR_GPIO +#define RT_REGULATOR_SPACEMIT_PMIC +#define RT_USING_RESET +#define RT_RESET_SPACEMIT_K1 + +/* Power Management (PM) Domains device drivers */ + +#define RT_PMDOMAIN_K1X +/* end of Power Management (PM) Domains device drivers */ +#define RT_USING_POWER_RESET +#define RT_POWER_RESET_REBOOT_SPACEMIT +#define RT_USING_THERMAL + +/* Thermal Sensors Drivers */ + +#define RT_THERMAL_K1X + +/* Thermal Cool Drivers */ + +#define RT_THERMAL_COOL_PWM_FAN +#define RT_USING_NVMEM +#define RT_USING_DMA +#define RT_DMA_MMP_PDMA +#define RT_DMA_MMP_PDMA_SUPPORT_64BIT +#define RT_USING_MFD +#define RT_MFD_SYSCON +#define RT_MFD_SPACEMIT_PMIC +#define RT_USING_OFW +#define RT_FDT_EARLYCON_MSG_SIZE 128 +#define RT_USING_OFW_BUS_RANGES_NUMBER 8 +#define RT_USING_PCI +#define RT_PCI_MSI +#define RT_PCI_ENDPOINT +#define RT_PCI_SYS_64BIT +#define RT_PCI_CACHE_LINE_SIZE 8 + +/* PCI Device Drivers */ + +#define RT_PCI_DW +#define RT_USING_PIC +#define MAX_HANDLERS 1024 +#define RT_USING_PIN +#define RT_PIN_K1X +#define RT_USING_PINCTRL +#define RT_PINCTRL_SINGLE +#define RT_PINCTRL_SPACEMIT_PMIC +#define RT_USING_KTIME +#define RT_USING_CLK +#define RT_CLK_SPACEMIT +#define RT_CLK_SPACEMIT_K1X +#define RT_USING_HWTIMER +#define RT_HWTIMER_SPACEMIT_K1X +/* end of Device Drivers */ + +/* C/C++ and POSIX layer */ + +/* ISO-ANSI C layer */ + +/* Timezone and Daylight Saving Time */ + +#define RT_LIBC_USING_LIGHT_TZ_DST +#define RT_LIBC_TZ_DEFAULT_HOUR 8 +#define RT_LIBC_TZ_DEFAULT_MIN 0 +#define RT_LIBC_TZ_DEFAULT_SEC 0 +/* end of Timezone and Daylight Saving Time */ +/* end of ISO-ANSI C layer */ + +/* 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_POLL +#define RT_USING_POSIX_SELECT +#define RT_USING_POSIX_TERMIOS +#define RT_USING_POSIX_AIO +#define RT_USING_POSIX_DELAY +#define RT_USING_POSIX_CLOCK +#define RT_USING_POSIX_TIMER + +/* Interprocess Communication (IPC) */ + +#define RT_USING_POSIX_PIPE +#define RT_USING_POSIX_PIPE_SIZE 512 + +/* Socket is in the 'Network' category */ + +/* end of Interprocess Communication (IPC) */ +/* end of POSIX (Portable Operating System Interface) layer */ +/* end of C/C++ and POSIX layer */ + +/* Network */ + +#define RT_USING_SAL +#define SAL_INTERNET_CHECK +#define SOCKET_TABLE_STEP_LEN 4 + +/* Docking with protocol stacks */ + +#define SAL_USING_LWIP +/* end of Docking with protocol stacks */ +#define SAL_USING_POSIX +#define RT_USING_NETDEV +#define NETDEV_USING_IFCONFIG +#define NETDEV_USING_PING +#define NETDEV_USING_NETSTAT +#define NETDEV_USING_AUTO_DEFAULT +#define NETDEV_IPV4 1 +#define NETDEV_IPV6 0 +#define RT_USING_LWIP +#define RT_USING_LWIP203 +#define RT_USING_LWIP_VER_NUM 0x20003 +#define RT_LWIP_MEM_ALIGNMENT 4 +#define RT_LWIP_IGMP +#define RT_LWIP_ICMP +#define RT_LWIP_DNS +#define RT_LWIP_DHCP +#define IP_SOF_BROADCAST 1 +#define IP_SOF_BROADCAST_RECV 1 + +/* Static IPv4 Address */ + +#define RT_LWIP_IPADDR "192.168.1.30" +#define RT_LWIP_GWADDR "192.168.1.1" +#define RT_LWIP_MSKADDR "255.255.255.0" +/* end of Static IPv4 Address */ +#define RT_LWIP_UDP +#define RT_LWIP_TCP +#define RT_LWIP_RAW +#define RT_MEMP_NUM_NETCONN 8 +#define RT_LWIP_PBUF_NUM 16 +#define RT_LWIP_RAW_PCB_NUM 4 +#define RT_LWIP_UDP_PCB_NUM 4 +#define RT_LWIP_TCP_PCB_NUM 4 +#define RT_LWIP_TCP_SEG_NUM 40 +#define RT_LWIP_TCP_SND_BUF 8196 +#define RT_LWIP_TCP_WND 8196 +#define RT_LWIP_TCPTHREAD_PRIORITY 10 +#define RT_LWIP_TCPTHREAD_MBOX_SIZE 8 +#define RT_LWIP_TCPTHREAD_STACKSIZE 8192 +#define RT_LWIP_ETHTHREAD_PRIORITY 12 +#define RT_LWIP_ETHTHREAD_STACKSIZE 8192 +#define RT_LWIP_ETHTHREAD_MBOX_SIZE 8 +#define LWIP_NETIF_STATUS_CALLBACK 1 +#define LWIP_NETIF_LINK_CALLBACK 1 +#define RT_LWIP_NETIF_NAMESIZE 6 +#define SO_REUSE 1 +#define LWIP_SO_RCVTIMEO 1 +#define LWIP_SO_SNDTIMEO 1 +#define LWIP_SO_RCVBUF 1 +#define LWIP_SO_LINGER 0 +#define LWIP_NETIF_LOOPBACK 0 +#define RT_LWIP_USING_PING +/* end of Network */ + +/* Memory protection */ + +/* end of Memory protection */ + +/* Utilities */ + +#define RT_USING_RESOURCE_ID +#define RT_USING_ADT +#define RT_USING_ADT_AVL +#define RT_USING_ADT_BITMAP +#define RT_USING_ADT_HASHMAP +#define RT_USING_ADT_REF +/* end of Utilities */ + +/* Memory management */ + +#define RT_PAGE_MPR_SIZE_DYNAMIC +#define RT_PAGE_AFFINITY_BLOCK_SIZE 0x1000 +#define RT_PAGE_MAX_ORDER 11 +#define RT_USING_MEMBLOCK +#define RT_INIT_MEMORY_REGIONS 128 + +/* Debugging */ + +/* end of Debugging */ +/* end of Memory management */ + +/* Using USB legacy version */ + +/* end of Using USB legacy version */ +/* end of RT-Thread Components */ + +/* RT-Thread Utestcases */ + +/* end of RT-Thread Utestcases */ + +/* RT-Thread online packages */ + +/* IoT - internet of things */ + + +/* Wi-Fi */ + +/* Marvell WiFi */ + +/* end of Marvell WiFi */ + +/* Wiced WiFi */ + +/* end of Wiced WiFi */ + +/* CYW43012 WiFi */ + +/* end of CYW43012 WiFi */ + +/* BL808 WiFi */ + +/* end of BL808 WiFi */ + +/* CYW43439 WiFi */ + +/* end of CYW43439 WiFi */ +/* end of Wi-Fi */ + +/* IoT Cloud */ + +/* end of IoT Cloud */ +/* end of IoT - internet of things */ + +/* security packages */ + +/* end of security packages */ + +/* language packages */ + +/* JSON: JavaScript Object Notation, a lightweight data-interchange format */ + +/* end of JSON: JavaScript Object Notation, a lightweight data-interchange format */ + +/* XML: Extensible Markup Language */ + +/* end of XML: Extensible Markup Language */ +/* end of language packages */ + +/* multimedia packages */ + +/* LVGL: powerful and easy-to-use embedded GUI library */ + +/* end of LVGL: powerful and easy-to-use embedded GUI library */ + +/* u8g2: a monochrome graphic library */ + +/* end of u8g2: a monochrome graphic library */ +/* end of multimedia packages */ + +/* tools packages */ + +/* end of tools packages */ + +/* system packages */ + +/* enhanced kernel services */ + +/* end of enhanced kernel services */ + +/* acceleration: Assembly language or algorithmic acceleration packages */ + +/* end of acceleration: Assembly language or algorithmic acceleration packages */ + +/* CMSIS: ARM Cortex-M Microcontroller Software Interface Standard */ + +/* end of CMSIS: ARM Cortex-M Microcontroller Software Interface Standard */ + +/* Micrium: Micrium software products porting for RT-Thread */ + +/* end of Micrium: Micrium software products porting for RT-Thread */ +/* end of system packages */ + +/* peripheral libraries and drivers */ + +/* HAL & SDK Drivers */ + +/* STM32 HAL & SDK Drivers */ + +/* end of STM32 HAL & SDK Drivers */ + +/* Infineon HAL Packages */ + +/* end of Infineon HAL Packages */ + +/* Kendryte SDK */ + +/* end of Kendryte SDK */ + +/* WCH HAL & SDK Drivers */ + +/* end of WCH HAL & SDK Drivers */ + +/* AT32 HAL & SDK Drivers */ + +/* end of AT32 HAL & SDK Drivers */ + +/* HC32 DDL Drivers */ + +/* end of HC32 DDL Drivers */ + +/* NXP HAL & SDK Drivers */ + +/* end of NXP HAL & SDK Drivers */ + +/* NUVOTON Drivers */ + +/* end of NUVOTON Drivers */ + +/* GD32 Drivers */ + +/* end of GD32 Drivers */ +/* end of HAL & SDK Drivers */ + +/* sensors drivers */ + +/* end of sensors drivers */ + +/* touch drivers */ + +/* end of touch drivers */ +/* end of peripheral libraries and drivers */ + +/* AI packages */ + +/* end of AI packages */ + +/* Signal Processing and Control Algorithm Packages */ + +/* end of Signal Processing and Control Algorithm Packages */ + +/* miscellaneous packages */ + +/* project laboratory */ + +/* end of project laboratory */ + +/* samples: kernel and components samples */ + +/* end of samples: kernel and components samples */ + +/* entertainment: terminal games and other interesting software packages */ + +/* end of entertainment: terminal games and other interesting software packages */ +/* end of miscellaneous packages */ + +/* Arduino libraries */ + + +/* Projects and Demos */ + +/* end of Projects and Demos */ + +/* Sensors */ + +/* end of Sensors */ + +/* Display */ + +/* end of Display */ + +/* Timing */ + +/* end of Timing */ + +/* Data Processing */ + +/* end of Data Processing */ + +/* Data Storage */ + +/* Communication */ + +/* end of Communication */ + +/* Device Control */ + +/* end of Device Control */ + +/* Other */ + +/* end of Other */ + +/* Signal IO */ + +/* end of Signal IO */ + +/* Uncategorized */ + +/* end of Arduino libraries */ +/* end of RT-Thread online packages */ +#define SOC_SPACEMIT_K1 + +#endif diff --git a/bsp/spacemit/k1/rtconfig.py b/bsp/spacemit/k1/rtconfig.py new file mode 100755 index 00000000000..722dedd4a56 --- /dev/null +++ b/bsp/spacemit/k1/rtconfig.py @@ -0,0 +1,54 @@ +import os + +# toolchains options +ARCH ='risc-v' +CPU ='virt64' +CROSS_TOOL ='gcc' + +RTT_ROOT = os.getenv('RTT_ROOT') or os.path.join(os.getcwd(), '..', '..', '..') + +if os.getenv('RTT_CC'): + CROSS_TOOL = os.getenv('RTT_CC') + +if CROSS_TOOL == 'gcc': + PLATFORM = 'gcc' + EXEC_PATH = os.getenv('RTT_EXEC_PATH') or '/usr/bin' +else: + print('Please make sure your toolchains is GNU GCC!') + exit(0) + +BUILD = 'debug' + +if PLATFORM == 'gcc': + # toolchains + PREFIX = os.getenv('RTT_CC_PREFIX') or 'riscv64-unknown-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 = ' -mcmodel=medany -march=rv64imafdc -mabi=lp64 ' + # DEVICE = ' -mcpu=spacemit-x60 -march=rv64gc_zba_zbb_zbc_zbs -mabi=lp64d ' + CPPFLAGS= ' -nostdinc -undef -E -P -x assembler-with-cpp' + CFLAGS = DEVICE + '-ffreestanding -flax-vector-conversions -Wall -Wno-cpp -fno-common -ffunction-sections -fdata-sections -fstrict-volatile-bitfields -fdiagnostics-color=always' + AFLAGS = ' -c' + DEVICE + ' -x assembler-with-cpp -D__ASSEMBLY__ ' + LFLAGS = DEVICE + ' -nostartfiles -Wl,--gc-sections,-Map=rtthread.map,-cref,-u,_start -T link.lds' + ' -lsupc++ -lgcc -static' + CPATH = '' + LPATH = '' + + if BUILD == 'debug': + CFLAGS += ' -O0 -ggdb -fvar-tracking ' + AFLAGS += ' -ggdb' + else: + CFLAGS += ' -O2 -Os' + + CXXFLAGS = CFLAGS + +DUMP_ACTION = OBJDUMP + ' -D -S $TARGET > rtthread.asm\n' +POST_ACTION = OBJCPY + ' -O binary $TARGET rtthread.bin\n' + SIZE + ' $TARGET \n' diff --git a/components/drivers/cputime/Kconfig b/components/drivers/cputime/Kconfig index 97c2c462593..631ac133031 100644 --- a/components/drivers/cputime/Kconfig +++ b/components/drivers/cputime/Kconfig @@ -29,6 +29,7 @@ if RT_USING_CPUTIME help Some RISCV64 MCU Use rdtime instructions read CPU time. config CPUTIME_TIMER_FREQ + depends on !RT_USING_DM int "CPUTIME timer freq" default 0 endif diff --git a/components/drivers/cputime/cputime_riscv.c b/components/drivers/cputime/cputime_riscv.c index 597157c226e..86e35a80781 100644 --- a/components/drivers/cputime/cputime_riscv.c +++ b/components/drivers/cputime/cputime_riscv.c @@ -4,6 +4,12 @@ #include +#ifdef RT_USING_DM +extern rt_uint32_t riscv_timer_get_frequency(void); + +#define CPUTIME_TIMER_FREQ riscv_timer_get_frequency() +#endif + /* Use Cycle counter of Data Watchpoint and Trace Register for CPU time */ static uint64_t riscv_cputime_getres(void) diff --git a/components/drivers/ktime/src/risc-v/virt64/cputimer.c b/components/drivers/ktime/src/risc-v/virt64/cputimer.c index 70c133aa2e7..87bc9079fba 100644 --- a/components/drivers/ktime/src/risc-v/virt64/cputimer.c +++ b/components/drivers/ktime/src/risc-v/virt64/cputimer.c @@ -10,6 +10,12 @@ #include "ktime.h" +#ifdef RT_USING_DM +extern rt_uint32_t riscv_timer_get_frequency(void); + +#define CPUTIME_TIMER_FREQ riscv_timer_get_frequency() +#endif + static volatile unsigned long _init_cnt = 0; rt_uint64_t rt_ktime_cputimer_getres(void) diff --git a/libcpu/Kconfig b/libcpu/Kconfig index b089b61725c..5a536d0255f 100644 --- a/libcpu/Kconfig +++ b/libcpu/Kconfig @@ -257,6 +257,10 @@ config ARCH_RISCV64 bool if ARCH_RISCV64 +if RT_USING_DM +orsource "./risc-v/Kconfig" +endif + config ARCH_USING_NEW_CTX_SWITCH bool default y diff --git a/libcpu/risc-v/.gitignore b/libcpu/risc-v/.gitignore new file mode 100755 index 00000000000..feb550d1903 --- /dev/null +++ b/libcpu/risc-v/.gitignore @@ -0,0 +1 @@ +link.lds diff --git a/libcpu/risc-v/Kconfig b/libcpu/risc-v/Kconfig new file mode 100755 index 00000000000..12d730d6a8d --- /dev/null +++ b/libcpu/risc-v/Kconfig @@ -0,0 +1,30 @@ +menu "RISC-V Architecture Configuration" + config ARCH_TEXT_OFFSET + hex "Text offset" + default 0x200000 + help + We use the area as the DM ioremap address space in 32 bits RT-Thread: + [ARCH_TEXT_OFFSET - IOREMAP_SIZE, ARCH_TEXT_OFFSET) + So you SHOULD config the value bigger than this area. + The `IOREMAP_SIZE` is defined a default value in common/setup.c. + + config ARCH_RAM_OFFSET + hex "RAM offset" + default 0 + + config ARCH_SECONDARY_CPU_STACK_SIZE + int "Secondary CPU stack size" + default 4096 + + config ARCH_HAVE_EFFICIENT_UNALIGNED_ACCESS + bool + default y + + config ARCH_HEAP_SIZE + hex "Size of system heap" + default 0x4000000 + + config ARCH_INIT_PAGE_SIZE + hex "Size of init page region" + default 0x200000 +endmenu diff --git a/libcpu/risc-v/SConscript b/libcpu/risc-v/SConscript index fafb8524ec5..48fbe9b4aeb 100644 --- a/libcpu/risc-v/SConscript +++ b/libcpu/risc-v/SConscript @@ -9,6 +9,14 @@ common64_arch = ['virt64', 'c906', 'c908','ur-cp100'] cwd = GetCurrentDir() group = [] list = os.listdir(cwd) +bsp_path = Dir('#').abspath + +if not os.path.exists(bsp_path + "/link.lds"): + Env['LINKFLAGS'] = Env['LINKFLAGS'].replace('link.lds', cwd + "/link.lds") + Preprocessing("link.lds.S", ".lds", CPPPATH=[bsp_path, cwd + '/common64/include']) + +# fix the linker with crtx.o +Env['LINKFLAGS'] += ' -nostartfiles' # add common code files if rtconfig.CPU in common64_arch : diff --git a/libcpu/risc-v/common64/SConscript b/libcpu/risc-v/common64/SConscript index 553a7429db7..f4f201fcc44 100644 --- a/libcpu/risc-v/common64/SConscript +++ b/libcpu/risc-v/common64/SConscript @@ -2,13 +2,28 @@ from building import * cwd = GetCurrentDir() src = Glob('*.c') + Glob('*.cpp') + Glob('*_gcc.S') -CPPPATH = [cwd] +CPPPATH = [cwd, cwd + '/include'] if not GetDepend('ARCH_USING_ASID'): SrcRemove(src, ['asid.c']) +if GetDepend('RT_USING_DM'): + SrcRemove(src, ['interrupt.c']) +else: + SrcRemove(src, ['cache.c', 'setup.c']) + src.append('../common/atomic_riscv.c') group = DefineGroup('CPU', src, depend = [''], CPPPATH = CPPPATH) +# build for sub-directory +list = os.listdir(cwd) +objs = [] + +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')) +group = group + objs + Return('group') diff --git a/libcpu/risc-v/common64/asm-generic.h b/libcpu/risc-v/common64/asm-generic.h deleted file mode 100644 index 625af2ce1a3..00000000000 --- a/libcpu/risc-v/common64/asm-generic.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (c) 2006-2023 RT-Thread Development Team - * - * SPDX-License-Identifier: Apache-2.0 - * - * Change Logs: - * Date Author Notes - * 2023-03-12 WangXiaoyao the first version - */ -#ifndef __ASM_GENERIC_H__ -#define __ASM_GENERIC_H__ - -/* use to mark a start point where every task start from */ -#define START_POINT(funcname) \ - .global funcname; \ - .type funcname, %function; \ - funcname: \ - .cfi_sections .debug_frame, .eh_frame; \ - .cfi_startproc; \ - .cfi_undefined ra - -#define START_POINT_END(name) \ - .cfi_endproc; \ - .size name, .-name; - -#endif /* __ASM_GENERIC_H__ */ diff --git a/libcpu/risc-v/common64/builtin_fdt_gcc.S b/libcpu/risc-v/common64/builtin_fdt_gcc.S new file mode 100755 index 00000000000..834f50f3f2d --- /dev/null +++ b/libcpu/risc-v/common64/builtin_fdt_gcc.S @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2006-2025, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2025-04-20 GuEe-GUI The first version + */ + +#include + + .section ".data" + + .align RISCV_SZPTR + .globl rt_hw_builtin_fdt +rt_hw_builtin_fdt: +#ifdef RT_BUILTIN_FDT_PATH + .incbin RT_BUILTIN_FDT_PATH +#endif diff --git a/libcpu/risc-v/common64/cache.c b/libcpu/risc-v/common64/cache.c new file mode 100755 index 00000000000..f4f5ba91f78 --- /dev/null +++ b/libcpu/risc-v/common64/cache.c @@ -0,0 +1,245 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2021-01-29 lizhirui first version + * 2025-04-20 GuEe-GUI Port to the DM + * 2025-04-26 GuEe-GUI Follow the specific and vendor + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#ifdef RT_USING_SMP +#include +#endif +#ifdef RT_USING_HWCACHE +#include +#endif + +/** + * @brief RISC-V instruction formats + */ + +#define OPCODE_MISC_MEM 15 +#define OPCODE_SYSTEM 115 + +#ifndef __OPC_INSN_FORMAT_I +/** + * I type: .insn i opcode6, func3, rd, rs1, simm12 + * + * +--------+-----+-------+----+---------+ + * | simm12 | rs1 | func3 | rd | opcode6 | + * +--------+-----+-------+----+---------+ + * 31 20 15 12 7 0 + */ +#define __OPC_INSN_FORMAT_I(opcode, func3, rd, rs1, simm12) \ + ".insn i "RT_STRINGIFY(opcode)","RT_STRINGIFY(func3)","RT_STRINGIFY(rd)","RT_STRINGIFY(rs1)","RT_STRINGIFY(simm12)"\n\t" +#endif /* !__OPC_INSN_FORMAT_I */ + +#ifndef OPC_CBO_INVAL +#define OPC_CBO_INVAL(base) __OPC_INSN_FORMAT_I(OPCODE_MISC_MEM, 2, x0, base, 0) +#endif + +#ifndef OPC_CBO_CLEAN +#define OPC_CBO_CLEAN(base) __OPC_INSN_FORMAT_I(OPCODE_MISC_MEM, 2, x0, base, 1) +#endif + +static rt_uint32_t riscv_cbom_block_size; + +void riscv_cache_set_cbom_block_size(rt_uint32_t size) +{ + if (!riscv_isa_ext_test(ZICBOM)) + { + RT_ASSERT(0); + return; + } + + HWREG32(&riscv_cbom_block_size) = size; + rt_hw_wmb(); +} + +rt_uint32_t riscv_cache_get_cbom_block_size(void) +{ + rt_hw_rmb(); + return HWREG32(&riscv_cbom_block_size); +} + +rt_always_inline void local_flush_icache_all(void) +{ + __asm__ volatile (OPC_FENCE_I:::"memory"); +} + +rt_inline void ipi_remote_fence_i(void *data) +{ + RT_UNUSED(data); + + local_flush_icache_all(); +} + +rt_weak void rt_hw_cpu_icache_enable(void) +{ +#ifdef RT_USING_HWCACHE + rt_hwcache_icache_enable(); +#endif +} + +rt_weak void rt_hw_cpu_icache_disable(void) +{ +#ifdef RT_USING_HWCACHE + rt_hwcache_icache_disable(); +#endif +} + +rt_weak rt_base_t rt_hw_cpu_icache_status(void) +{ +#ifdef RT_USING_HWCACHE + return rt_hwcache_icache_status(); +#elif !defined(ARCH_RISCV_DISABLE_FENCE_I) + return 0; +#else /* !ARCH_RISCV_DISABLE_FENCE_I */ + return !0; +#endif /* RT_USING_HWCACHE */ +} + +rt_weak void rt_hw_cpu_icache_ops(int ops, void *addr, int size) +{ +#ifdef RT_USING_HWCACHE + rt_hwcache_icache_ops(ops, addr, size); +#elif !defined(ARCH_RISCV_DISABLE_FENCE_I) + /* + * RISC-V doesn't have an instruction to flush parts of the icache, + * so instead we just flush the whole thing. + */ + if (ops == RT_HW_CACHE_INVALIDATE) + { + local_flush_icache_all(); + + #if (defined(RT_USING_SMP) && RT_CPUS_NR > 1) || defined(RT_USING_AMP) + #ifndef ARCH_RISCV_M_MODE + if (has_ipi_extension) + { + sbi_remote_fence_i(RT_NULL); + } + else + #endif /* !ARCH_RISCV_M_MODE */ + { + rt_smp_call_each_cpu(ipi_remote_fence_i, RT_NULL, SMP_CALL_WAIT_ALL); + } + #endif /* RT_USING_SMP */ + } + RT_UNUSED(addr); + RT_UNUSED(size); +#else /* !ARCH_RISCV_DISABLE_FENCE_I */ + RT_UNUSED(ops); + RT_UNUSED(addr); + RT_UNUSED(size); +#endif /* RT_USING_HWCACHE */ +} + +rt_weak void rt_hw_cpu_dcache_enable(void) +{ +#ifdef RT_USING_HWCACHE + rt_hwcache_dcache_enable(); +#endif +} + +rt_weak void rt_hw_cpu_dcache_disable(void) +{ +#ifdef RT_USING_HWCACHE + rt_hwcache_dcache_disable(); +#endif +} + +rt_weak rt_base_t rt_hw_cpu_dcache_status(void) +{ +#ifdef RT_USING_HWCACHE + return rt_hwcache_dcache_status(); +#else + return riscv_cache_get_cbom_block_size() == 0; +#endif +} + +rt_weak void rt_hw_cpu_dcache_ops(int ops, void *addr, int size) +{ +#ifdef RT_USING_HWCACHE + rt_hwcache_dcache_ops(ops, addr, size); +#else + rt_uint32_t cbom_block_size; + + cbom_block_size = riscv_cache_get_cbom_block_size(); + + if (cbom_block_size) + { + if (ops == RT_HW_CACHE_FLUSH) + { + __asm__ volatile ( + " mv a0, %1\n\t" + " j 2f\n\t" + "3:\n\t" + OPC_CBO_CLEAN(a0) + " add a0, a0, %0\n\t" + "2:\n\t" + " bltu a0, %2, 3b\n\t" + ::"r"(cbom_block_size), + "r"((rt_ubase_t)addr & ~(cbom_block_size - 1UL)), + "r"((rt_ubase_t)addr + size) + : "a0"); + } + else if (ops == RT_HW_CACHE_INVALIDATE) + { + __asm__ volatile ( + " mv a0, %1\n\t" + " j 2f\n\t" + "3:\n\t" + OPC_CBO_INVAL(a0) + " add a0, a0, %0\n\t" + "2:\n\t" + " bltu a0, %2, 3b\n\t" + ::"r"(cbom_block_size), + "r"((rt_ubase_t)addr & ~(cbom_block_size - 1UL)), + "r"((rt_ubase_t)addr + size) + : "a0"); + } + } + else + { + __asm__ volatile (".rept 6\n nop\n .endr\n"); + } +#endif /* RT_USING_HWCACHE */ +} + +rt_weak void rt_hw_cpu_icache_invalidate_all(void) +{ + rt_hw_cpu_icache_ops(RT_HW_CACHE_INVALIDATE, RT_NULL, ~0U); +} + +rt_weak void rt_hw_cpu_icache_invalidate(void *addr, rt_size_t size) +{ + rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, addr, size); +} + +rt_weak void rt_hw_cpu_dcache_clean_and_invalidate(void *addr, rt_size_t size) +{ + rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, addr, size); +} + +rt_weak void rt_hw_icache_invalidate_all(void) +{ + rt_hw_cpu_icache_ops(RT_HW_CACHE_INVALIDATE, RT_NULL, ~0U); +} + +rt_weak void rt_hw_sync_cache_local(void *addr, int size) +{ + rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, addr, size); + rt_hw_cpu_icache_ops(RT_HW_CACHE_INVALIDATE, addr, size); +} diff --git a/libcpu/risc-v/common64/cpu_sbi.c b/libcpu/risc-v/common64/cpu_sbi.c new file mode 100755 index 00000000000..8dca3a3e4da --- /dev/null +++ b/libcpu/risc-v/common64/cpu_sbi.c @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2006-2025, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2025-04-20 GuEe-GUI The first version + */ + +#include +#include +#include + +static int sbi_cpu_boot(rt_uint32_t cpuid, rt_uint64_t entry) +{ + rt_ubase_t hartid = cpuid; + + return sbi_hsm_hart_start(hartid, entry, entry); +} + +static void sbi_cpu_shutdown(void) +{ + sbi_hsm_hart_stop(); +} + +struct cpu_ops_t cpu_sbi_ops = +{ + .method = "sbi", + .cpu_boot = sbi_cpu_boot, + .cpu_shutdown = sbi_cpu_shutdown, +}; diff --git a/libcpu/risc-v/common64/cpu_spinwait.c b/libcpu/risc-v/common64/cpu_spinwait.c new file mode 100755 index 00000000000..628046ccde0 --- /dev/null +++ b/libcpu/risc-v/common64/cpu_spinwait.c @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2006-2025, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2025-04-20 GuEe-GUI The first version + */ + +#include + +#include + +rt_uint8_t riscv_spinwait_table = 0; + +static int spinwait_cpu_boot(rt_uint32_t cpuid, rt_uint64_t entry) +{ + rt_hw_barrier(); + + HWREG8(&riscv_spinwait_table) = RT_UINT8_MAX; + rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, &riscv_spinwait_table, sizeof(riscv_spinwait_table)); + + return 0; +} + +struct cpu_ops_t cpu_spinwait_ops = +{ + .method = "spinwait", + .cpu_boot = spinwait_cpu_boot, +}; diff --git a/libcpu/risc-v/common64/cpuport.c b/libcpu/risc-v/common64/cpuport.c index 440195eb3a1..72b51b94aa9 100644 --- a/libcpu/risc-v/common64/cpuport.c +++ b/libcpu/risc-v/common64/cpuport.c @@ -74,6 +74,36 @@ void *_rt_hw_stack_init(rt_ubase_t *sp, rt_ubase_t ra, rt_ubase_t sstatus) return (void *)frame; } +#ifdef RT_USING_CPU_FFS + +int __rt_ffs(int value) +{ +#ifdef __GNUC__ + return __builtin_ffs(value); +#else +#error Unsupport ffs +#endif +} + +unsigned long __rt_ffsl(unsigned long value) +{ +#ifdef __GNUC__ + return __builtin_ffsl(value); +#else +#error Unsupport ffsl +#endif +} + +unsigned long __rt_clz(unsigned long value) +{ +#ifdef __GNUC__ + return __builtin_clz(value); +#else +#error Unsupport clz +#endif +} +#endif /* RT_USING_CPU_FFS */ + int rt_hw_cpu_id(void) { #ifndef RT_USING_SMP @@ -157,6 +187,7 @@ void rt_hw_context_switch_interrupt(void *context, rt_ubase_t from, rt_ubase_t t } #endif /* end of RT_USING_SMP */ +#ifndef RT_USING_DM /** shutdown CPU */ void rt_hw_cpu_shutdown(void) { @@ -170,6 +201,7 @@ void rt_hw_cpu_shutdown(void) while (1) ; } +#endif /* RT_USING_DM */ void rt_hw_set_process_id(int pid) { diff --git a/libcpu/risc-v/common64/hwcache/Kconfig b/libcpu/risc-v/common64/hwcache/Kconfig new file mode 100755 index 00000000000..918b6253434 --- /dev/null +++ b/libcpu/risc-v/common64/hwcache/Kconfig @@ -0,0 +1,3 @@ +config RT_CACHE_SIFIVE_CCACHE + bool "Sifive Composable Cache controller" + depends on RT_USING_HWCACHE diff --git a/libcpu/risc-v/common64/hwcache/SConscript b/libcpu/risc-v/common64/hwcache/SConscript new file mode 100755 index 00000000000..d7b5123ca56 --- /dev/null +++ b/libcpu/risc-v/common64/hwcache/SConscript @@ -0,0 +1,17 @@ +from building import * + +group = [] + +if not GetDepend(['RT_USING_HWCACHE']): + Return('group') + +src = [] +cwd = GetCurrentDir() +CPPPATH = [cwd] + +if GetDepend(['RT_CACHE_SIFIVE_CCACHE']): + src += ['hwcache-sifive_ccache.c'] + +group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') diff --git a/libcpu/risc-v/common64/hwcache/hwcache-sifive_ccache.c b/libcpu/risc-v/common64/hwcache/hwcache-sifive_ccache.c new file mode 100755 index 00000000000..6019fed0e7f --- /dev/null +++ b/libcpu/risc-v/common64/hwcache/hwcache-sifive_ccache.c @@ -0,0 +1,250 @@ +/* + * 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 "cache.sifive_ccache" +#define DBG_LVL DBG_INFO +#include + +#include + +#define SIFIVE_CCACHE_DIRECCFIX_LOW 0x100 +#define SIFIVE_CCACHE_DIRECCFIX_HIGH 0x104 +#define SIFIVE_CCACHE_DIRECCFIX_COUNT 0x108 + +#define SIFIVE_CCACHE_DIRECCFAIL_LOW 0x120 +#define SIFIVE_CCACHE_DIRECCFAIL_HIGH 0x124 +#define SIFIVE_CCACHE_DIRECCFAIL_COUNT 0x128 + +#define SIFIVE_CCACHE_DATECCFIX_LOW 0x140 +#define SIFIVE_CCACHE_DATECCFIX_HIGH 0x144 +#define SIFIVE_CCACHE_DATECCFIX_COUNT 0x148 + +#define SIFIVE_CCACHE_DATECCFAIL_LOW 0x160 +#define SIFIVE_CCACHE_DATECCFAIL_HIGH 0x164 +#define SIFIVE_CCACHE_DATECCFAIL_COUNT 0x168 + +#define SIFIVE_CCACHE_CONFIG 0x00 +#define SIFIVE_CCACHE_CONFIG_BANK_MASK RT_GENMASK_ULL(7, 0) +#define SIFIVE_CCACHE_CONFIG_WAYS_MASK RT_GENMASK_ULL(15, 8) +#define SIFIVE_CCACHE_CONFIG_SETS_MASK RT_GENMASK_ULL(23, 16) +#define SIFIVE_CCACHE_CONFIG_BLKS_MASK RT_GENMASK_ULL(31, 24) + +#define SIFIVE_CCACHE_FLUSH64 0x200 +#define SIFIVE_CCACHE_FLUSH32 0x240 + +#define SIFIVE_CCACHE_WAYENABLE 0x08 +#define SIFIVE_CCACHE_ECCINJECTERR 0x40 + +#define SIFIVE_CCACHE_MAX_ECCINTR 4 +#define SIFIVE_CCACHE_LINE_SIZE 64 + +#define SIFIVE_CCACHE_TRUNKCLOCKGATE 0x1000 +#define SIFIVE_CCACHE_TRUNKCLOCKGATE_DISABLE RT_BIT(0) +#define SIFIVE_CCACHE_REGIONCLOCKGATE_DISABLE RT_BIT(1) + +enum +{ + DIR_CORR = 0, + DATA_CORR, + DATA_UNCORR, + DIR_UNCORR, +}; + +enum +{ + QUIRK_BROKEN_DATA_UNCORR = RT_BIT(0), + QUIRK_HAS_CG = RT_BIT(1), +}; + +static void *ccache_base; +static rt_ubase_t ccache_quirk; +static rt_uint32_t ccache_level; +static int ccache_irq[SIFIVE_CCACHE_MAX_ECCINTR]; +static struct rt_ofw_node *ccache_np; + +static void ccache_enable(void) +{ + rt_uint32_t config, ways; + + /* Enable all ways of composable cache */ + config = HWREG32(ccache_base + SIFIVE_CCACHE_CONFIG); + ways = RT_FIELD_GET(SIFIVE_CCACHE_CONFIG_WAYS_MASK, config); + + HWREG32(ccache_base + SIFIVE_CCACHE_WAYENABLE) = ways - 1; + + if (ccache_quirk & QUIRK_HAS_CG) + { + /* Enable clock gating bits */ + config = HWREG32(ccache_base + SIFIVE_CCACHE_TRUNKCLOCKGATE); + config &= ~(SIFIVE_CCACHE_TRUNKCLOCKGATE_DISABLE | SIFIVE_CCACHE_REGIONCLOCKGATE_DISABLE); + HWREG32(ccache_base + SIFIVE_CCACHE_TRUNKCLOCKGATE) = config; + } +} + +static void ccache_disable(void) +{ + rt_uint32_t config; + + HWREG32(ccache_base + SIFIVE_CCACHE_WAYENABLE) = 0; + + if (ccache_quirk & QUIRK_HAS_CG) + { + /* Disable clock gating bits */ + config = HWREG32(ccache_base + SIFIVE_CCACHE_TRUNKCLOCKGATE); + config |= SIFIVE_CCACHE_TRUNKCLOCKGATE_DISABLE | SIFIVE_CCACHE_REGIONCLOCKGATE_DISABLE; + HWREG32(ccache_base + SIFIVE_CCACHE_TRUNKCLOCKGATE) = config; + } +} + +static void ccache_flush_range(void *vaddr, rt_size_t size) +{ + rt_ubase_t start = (rt_ubase_t)rt_kmem_v2p(vaddr); + rt_ubase_t end = start + size; + + if (!size) + { + return; + } + + rt_hw_barrier(); + + for (rt_ubase_t line = RT_ALIGN_DOWN(start, SIFIVE_CCACHE_LINE_SIZE); + line < end; + line += SIFIVE_CCACHE_LINE_SIZE) + { + #ifdef ARCH_CPU_64BIT + HWREG64(ccache_base + SIFIVE_CCACHE_FLUSH64) = line; + #else + HWREG32(ccache_base + SIFIVE_CCACHE_FLUSH32) = line >> 4; + #endif + rt_hw_barrier(); + } +} + +static struct rt_hwcache_ops ccache_mgmt = +{ + .name = "ccache", + .enable = ccache_enable, + .disable = ccache_disable, + .flush = ccache_flush_range, + .invalidate = ccache_flush_range, +}; + +static void sifive_ccache_isr(int irq, void *param) +{ + rt_uint32_t add_h, add_l; + + if (irq == ccache_irq[DIR_CORR]) + { + add_h = HWREG32(ccache_base + SIFIVE_CCACHE_DIRECCFIX_HIGH); + add_l = HWREG32(ccache_base + SIFIVE_CCACHE_DIRECCFIX_LOW); + LOG_E("Dir error: 0x%08x%08x", add_h, add_l); + + /* Reading this register clears the DirError interrupt sig */ + HWREG32(ccache_base + SIFIVE_CCACHE_DIRECCFIX_COUNT); + } + else if (irq == ccache_irq[DIR_UNCORR]) + { + add_h = HWREG32(ccache_base + SIFIVE_CCACHE_DIRECCFAIL_HIGH); + add_l = HWREG32(ccache_base + SIFIVE_CCACHE_DIRECCFAIL_LOW); + + /* Reading this register clears the DirFail interrupt sig */ + HWREG32(ccache_base + SIFIVE_CCACHE_DIRECCFAIL_COUNT); + LOG_E("Dir fail: 0x%08x%08x", add_h, add_l); + RT_ASSERT(0); + } + else if (irq == ccache_irq[DATA_CORR]) + { + add_h = HWREG32(ccache_base + SIFIVE_CCACHE_DATECCFIX_HIGH); + add_l = HWREG32(ccache_base + SIFIVE_CCACHE_DATECCFIX_LOW); + LOG_E("Data error: 0x%08x.%08x", add_h, add_l); + + /* Reading this register clears the DataError interrupt sig */ + HWREG32(ccache_base + SIFIVE_CCACHE_DATECCFIX_COUNT); + } + else if (irq == ccache_irq[DATA_UNCORR]) + { + add_h = HWREG32(ccache_base + SIFIVE_CCACHE_DATECCFAIL_HIGH); + add_l = HWREG32(ccache_base + SIFIVE_CCACHE_DATECCFAIL_LOW); + LOG_E("Data fail: 0x%08x%08x", add_h, add_l); + + /* Reading this register clears the DataFail interrupt sig */ + HWREG32(ccache_base + SIFIVE_CCACHE_DATECCFAIL_COUNT); + } +} + +static void ccache_irq_init(void) +{ + if (!ccache_np) + { + return; + } + + for (int i = 0; i < RT_ARRAY_SIZE(ccache_irq); ++i) + { + int irq; + + if (i == DATA_UNCORR && (ccache_quirk & QUIRK_BROKEN_DATA_UNCORR)) + { + continue; + } + + irq = rt_ofw_get_irq(ccache_np, i); + + if (irq >= 0) + { + rt_hw_interrupt_install(irq, sifive_ccache_isr, ccache_np, "ccache"); + rt_hw_interrupt_umask(irq); + + ccache_irq[i] = irq; + } + } +} +INIT_SUBSYS_EXPORT(ccache_irq_init); + +static rt_err_t sifive_ccache_ofw_init(struct rt_ofw_node *np, + const struct rt_ofw_node_id *id) +{ + ccache_quirk = *(rt_ubase_t *)id->data; + + ccache_base = rt_ofw_iomap(np, 0); + + if (!ccache_base) + { + return -RT_EIO; + } + + ccache_np = np; + + if (!rt_dm_cpu_dcache_ops) + { + rt_dm_cpu_dcache_ops = &ccache_mgmt; + } + + return RT_EOK; +} + +static rt_ubase_t ccache0_quirk = QUIRK_HAS_CG; +static rt_ubase_t jh7100_quirk = QUIRK_BROKEN_DATA_UNCORR; + +static const struct rt_ofw_node_id sifive_ccache_ofw_ids[] = +{ + { .compatible = "sifive,fu540-c000-ccache" }, + { .compatible = "sifive,fu740-c000-ccache" }, + { .compatible = "starfive,jh7100-ccache", .data = &jh7100_quirk }, + { .compatible = "sifive,ccache0", .data = &ccache0_quirk }, + { /* sentinel */ } +}; +RT_HWCACHE_OFW_DECLARE(sifive_ccache, sifive_ccache_ofw_ids, sifive_ccache_ofw_init); diff --git a/libcpu/risc-v/common64/include/asm-generic.h b/libcpu/risc-v/common64/include/asm-generic.h new file mode 100755 index 00000000000..25e225a642e --- /dev/null +++ b/libcpu/risc-v/common64/include/asm-generic.h @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2006-2025 RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-03-12 WangXiaoyao the first version + * 2025-04-20 GuEe-GUI Merge 64 and 32 mode + */ + +#ifndef __ASM_GENERIC_H__ +#define __ASM_GENERIC_H__ + +#include + +#ifdef __ASSEMBLY__ +/* use to mark a start point where every task start from */ +#define START_POINT(funcname) \ + .global funcname; \ + .type funcname, %function; \ + funcname: \ + .cfi_sections .debug_frame, .eh_frame; \ + .cfi_startproc; \ + .cfi_undefined ra + +#define START_POINT_END(name) \ + .cfi_endproc; \ + .size name, .-name; + +#define _AC(X,Y) X +#define _AT(T,X) X +#define __ASM_STR(x) x +#else +#define _AC(X,Y) (X##Y) +#define _AT(T,X) ((T)(X)) +#define __ASM_STR(x) #x +#endif + +#define _UL(x) (_AC(x, UL)) +#define _ULL(x) (_AC(x, ULL)) + +#if __riscv_xlen == 64 +#define BITS_PER_LONG 8 +#define __REG_SEL(a, b) __ASM_STR(a) +#elif __riscv_xlen == 32 +#define BITS_PER_LONG 4 +#define __REG_SEL(a, b) __ASM_STR(b) +#else +#error "Unexpected __riscv_xlen" +#endif + +#ifndef BIT +#define BIT(x) (_UL(1) << (x)) +#endif + +#ifndef GENMASK +#define GENMASK(h, l) (((_UL(~0)) << (l)) & (_UL(~0) >> (BITS_PER_LONG - 1 - (h)))) +#endif + +#define REG_WIDTH __REG_SEL(d, w) +#define REG_L __REG_SEL(ld, lw) +#define REG_S __REG_SEL(sd, sw) +#define REG_SC __REG_SEL(sc.d, sc.w) +#define REG_AMOSWAP_AQ __REG_SEL(amoswap.d.aq, amoswap.w.aq) +#define REG_ASM __REG_SEL(.dword, .word) +#define SZREG __REG_SEL(8, 4) +#define LGREG __REG_SEL(3, 2) +#define FREG_L __REG_SEL(fld, flw) +#define FREG_S __REG_SEL(fsd, fsw) + +#if __riscv_xlen == 64 +#ifdef __ASSEMBLY__ +#define RISCV_PTR .dword +#define RISCV_SZPTR 8 +#define RISCV_LGPTR 3 +#else +#define RISCV_PTR ".dword" +#define RISCV_SZPTR "8" +#define RISCV_LGPTR "3" +#endif +#elif __riscv_xlen == 32 +#ifdef __ASSEMBLY__ +#define RISCV_PTR .word +#define RISCV_SZPTR 4 +#define RISCV_LGPTR 2 +#else +#define RISCV_PTR ".word" +#define RISCV_SZPTR "4" +#define RISCV_LGPTR "2" +#endif +#else +#error "Unexpected __riscv_xlen" +#endif + +#endif /* __ASM_GENERIC_H__ */ diff --git a/libcpu/risc-v/common64/include/cache.h b/libcpu/risc-v/common64/include/cache.h new file mode 100755 index 00000000000..a8224a9e59d --- /dev/null +++ b/libcpu/risc-v/common64/include/cache.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2006-2025 RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2025-04-20 GuEe-GUI the first version + */ + +#ifndef __CACHE_H__ +#define __CACHE_H__ + +#define L1_CACHE_SHIFT 6 +#define L1_CACHE_BYTES (1 << L1_CACHE_SHIFT) + +#ifndef __ASSEMBLY__ +#include + +void riscv_cache_set_cbom_block_size(rt_uint32_t size); +rt_uint32_t riscv_cache_get_cbom_block_size(void); + +void rt_hw_cpu_icache_invalidate_all(void); +void rt_hw_cpu_icache_invalidate(void *addr, rt_size_t size); +void rt_hw_cpu_dcache_clean_and_invalidate(void *addr, rt_size_t size); + +void rt_hw_icache_invalidate_all(void); + +void rt_hw_sync_cache_local(void *addr, int size); +#endif /* !__ASSEMBLY__ */ + +#endif /* __CACHE_H__ */ diff --git a/libcpu/risc-v/common64/include/cpu.h b/libcpu/risc-v/common64/include/cpu.h new file mode 100755 index 00000000000..0604135a599 --- /dev/null +++ b/libcpu/risc-v/common64/include/cpu.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2006-2025, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2025-04-20 GuEe-GUI The first version + */ + +#ifndef __CPU_H__ +#define __CPU_H__ + +#include + +#ifndef RT_CPUS_NR +#define RT_CPUS_NR 1 +#endif + +struct cpu_ops_t +{ + const char *method; + int (*cpu_init)(rt_uint32_t id, void *param); + int (*cpu_boot)(rt_uint32_t id, rt_uint64_t entry); + void (*cpu_shutdown)(void); +}; + +#endif /* __CPU_H__ */ diff --git a/libcpu/risc-v/common64/cpuport.h b/libcpu/risc-v/common64/include/cpuport.h similarity index 63% rename from libcpu/risc-v/common64/cpuport.h rename to libcpu/risc-v/common64/include/cpuport.h index 9830b0dc266..f1a9318ac10 100644 --- a/libcpu/risc-v/common64/cpuport.h +++ b/libcpu/risc-v/common64/include/cpuport.h @@ -28,6 +28,13 @@ typedef union { #include +#define RISCV_FENCE(p, s) __asm__ volatile ("fence " #p "," #s:::"memory") + +#define rt_hw_barrier(...) RISCV_FENCE(iorw, iorw) +#define rt_hw_wfi() __asm__ volatile ("wfi") +#define rt_hw_wmb() RISCV_FENCE(ir, ir) +#define rt_hw_rmb() RISCV_FENCE(ow, ow) + rt_inline void rt_hw_dsb(void) { __asm__ volatile("fence":::"memory"); @@ -43,6 +50,16 @@ rt_inline void rt_hw_isb(void) __asm__ volatile(OPC_FENCE_I:::"memory"); } +rt_inline void rt_hw_cpu_relax(void) +{ +#ifdef ARCH_TOOLCHAIN_HAS_ZIHINTPAUSE + __asm__ volatile ("pause"); +#else /* !ARCH_TOOLCHAIN_HAS_ZIHINTPAUSE */ + __asm__ volatile (".4byte 0x0100000f"); +#endif /* ARCH_TOOLCHAIN_HAS_ZIHINTPAUSE */ + rt_hw_barrier(); +} + #ifdef ARCH_MM_MMU void rt_hw_percpu_hartid_init(rt_ubase_t *percpu_ptr, rt_ubase_t hartid); #endif diff --git a/libcpu/risc-v/common64/include/csr.h b/libcpu/risc-v/common64/include/csr.h new file mode 100755 index 00000000000..df76731123f --- /dev/null +++ b/libcpu/risc-v/common64/include/csr.h @@ -0,0 +1,572 @@ +/* + * Copyright (c) 2006-2025, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2025-04-20 GuEe-GUI The first version + */ + +#ifndef __CSR_H__ +#define __CSR_H__ + +#include + +/* Status register flags */ +#define SR_SIE _UL(0x00000002) /* Supervisor Interrupt Enable */ +#define SR_MIE _UL(0x00000008) /* Machine Interrupt Enable */ +#define SR_SPIE _UL(0x00000020) /* Previous Supervisor IE */ +#define SR_MPIE _UL(0x00000080) /* Previous Machine IE */ +#define SR_SPP _UL(0x00000100) /* Previously Supervisor */ +#define SR_MPP _UL(0x00001800) /* Previously Machine */ +#define SR_SUM _UL(0x00040000) /* Supervisor User Memory Access */ +#define SR_MXR _UL(0x00080000) /* Make eXecutable Readable */ + +#define SR_FS _UL(0x00006000) /* Floating-point Status */ +#define SR_FS_OFF _UL(0x00000000) +#define SR_FS_INITIAL _UL(0x00002000) +#define SR_FS_CLEAN _UL(0x00004000) +#define SR_FS_DIRTY _UL(0x00006000) + +#define SR_VS _UL(0x00000600) /* Vector Status */ +#define SR_VS_OFF _UL(0x00000000) +#define SR_VS_INITIAL _UL(0x00000200) +#define SR_VS_CLEAN _UL(0x00000400) +#define SR_VS_DIRTY _UL(0x00000600) + +#define SR_XS _UL(0x00018000) /* Extension Status */ +#define SR_XS_OFF _UL(0x00000000) +#define SR_XS_INITIAL _UL(0x00008000) +#define SR_XS_CLEAN _UL(0x00010000) +#define SR_XS_DIRTY _UL(0x00018000) + +#define SR_FS_VS (SR_FS | SR_VS) /* Vector and Floating-Point Unit */ + +#ifndef ARCH_CPU_64BIT +#define SR_SD _UL(0x80000000) /* FS/VS/XS dirty */ +#else +#define SR_SD _UL(0x8000000000000000) /* FS/VS/XS dirty */ +#endif + +#ifdef ARCH_CPU_64BIT +#define SR_UXL _UL(0x300000000) /* XLEN mask for U-mode */ +#define SR_UXL_32 _UL(0x100000000) /* XLEN = 32 for U-mode */ +#define SR_UXL_64 _UL(0x200000000) /* XLEN = 64 for U-mode */ +#endif + +/* SATP flags */ +#ifndef ARCH_CPU_64BIT +#define SATP_PPN _UL(0x003fffff) +#define SATP_MODE_BARE _UL(0x00000000) +#define SATP_MODE_32 _UL(0x80000000) +#define SATP_MODE_SHIFT 31 +#define SATP_MODE_MASK (_UL(0x1) << SATP_MODE_SHIFT) +#define SATP_ASID_BITS 9 +#define SATP_ASID_SHIFT 22 +#define SATP_ASID_MASK _UL(0x1ff) +#else +#define SATP_PPN _UL(0x00000fffffffffff) +#define SATP_MODE_BARE _UL(0x0000000000000000) +#define SATP_MODE_39 _UL(0x8000000000000000) +#define SATP_MODE_48 _UL(0x9000000000000000) +#define SATP_MODE_57 _UL(0xa000000000000000) +#define SATP_MODE_64 _UL(0xb000000000000000) +#define SATP_MODE_SHIFT 60 +#define SATP_MODE_MASK (_UL(0xf) << SATP_MODE_SHIFT) +#define SATP_ASID_BITS 16 +#define SATP_ASID_SHIFT 44 +#define SATP_ASID_MASK _UL(0xffff) +#endif + +/* Exception cause high bit - is an interrupt if set */ +#define CAUSE_IRQ_FLAG (_UL(1) << (__riscv_xlen - 1)) + +/* Interrupt causes (minus the high bit) */ +#define IRQ_S_SOFT 1 +#define IRQ_VS_SOFT 2 +#define IRQ_M_SOFT 3 +#define IRQ_S_TIMER 5 +#define IRQ_VS_TIMER 6 +#define IRQ_M_TIMER 7 +#define IRQ_S_EXT 9 +#define IRQ_VS_EXT 10 +#define IRQ_M_EXT 11 +#define IRQ_S_GEXT 12 +#define IRQ_PMU_OVF 13 +#define IRQ_LOCAL_MAX (IRQ_PMU_OVF + 1) +#define IRQ_LOCAL_MASK GENMASK((IRQ_LOCAL_MAX - 1), 0) + +/* Exception causes */ +#define EXC_INST_MISALIGNED 0 +#define EXC_INST_ACCESS 1 +#define EXC_INST_ILLEGAL 2 +#define EXC_BREAKPOINT 3 +#define EXC_LOAD_MISALIGNED 4 +#define EXC_LOAD_ACCESS 5 +#define EXC_STORE_MISALIGNED 6 +#define EXC_STORE_ACCESS 7 +#define EXC_SYSCALL 8 +#define EXC_HYPERVISOR_SYSCALL 9 +#define EXC_SUPERVISOR_SYSCALL 10 +#define EXC_INST_PAGE_FAULT 12 +#define EXC_LOAD_PAGE_FAULT 13 +#define EXC_STORE_PAGE_FAULT 15 +#define EXC_INST_GUEST_PAGE_FAULT 20 +#define EXC_LOAD_GUEST_PAGE_FAULT 21 +#define EXC_VIRTUAL_INST_FAULT 22 +#define EXC_STORE_GUEST_PAGE_FAULT 23 + +/* PMP configuration */ +#define PMP_R 0x01 +#define PMP_W 0x02 +#define PMP_X 0x04 +#define PMP_A 0x18 +#define PMP_A_TOR 0x08 +#define PMP_A_NA4 0x10 +#define PMP_A_NAPOT 0x18 +#define PMP_L 0x80 + +/* HSTATUS flags */ +#ifdef ARCH_CPU_64BIT +#define HSTATUS_VSXL _UL(0x300000000) +#define HSTATUS_VSXL_SHIFT 32 +#endif +#define HSTATUS_VTSR _UL(0x00400000) +#define HSTATUS_VTW _UL(0x00200000) +#define HSTATUS_VTVM _UL(0x00100000) +#define HSTATUS_VGEIN _UL(0x0003f000) +#define HSTATUS_VGEIN_SHIFT 12 +#define HSTATUS_HU _UL(0x00000200) +#define HSTATUS_SPVP _UL(0x00000100) +#define HSTATUS_SPV _UL(0x00000080) +#define HSTATUS_GVA _UL(0x00000040) +#define HSTATUS_VSBE _UL(0x00000020) + +/* HGATP flags */ +#define HGATP_MODE_OFF _UL(0) +#define HGATP_MODE_SV32X4 _UL(1) +#define HGATP_MODE_SV39X4 _UL(8) +#define HGATP_MODE_SV48X4 _UL(9) +#define HGATP_MODE_SV57X4 _UL(10) + +#define HGATP32_MODE_SHIFT 31 +#define HGATP32_VMID_SHIFT 22 +#define HGATP32_VMID GENMASK(28, 22) +#define HGATP32_PPN GENMASK(21, 0) + +#define HGATP64_MODE_SHIFT 60 +#define HGATP64_VMID_SHIFT 44 +#define HGATP64_VMID GENMASK(57, 44) +#define HGATP64_PPN GENMASK(43, 0) + +#define HGATP_PAGE_SHIFT 12 + +#ifdef ARCH_CPU_64BIT +#define HGATP_PPN HGATP64_PPN +#define HGATP_VMID_SHIFT HGATP64_VMID_SHIFT +#define HGATP_VMID HGATP64_VMID +#define HGATP_MODE_SHIFT HGATP64_MODE_SHIFT +#else +#define HGATP_PPN HGATP32_PPN +#define HGATP_VMID_SHIFT HGATP32_VMID_SHIFT +#define HGATP_VMID HGATP32_VMID +#define HGATP_MODE_SHIFT HGATP32_MODE_SHIFT +#endif + +/* VSIP & HVIP relation */ +#define VSIP_TO_HVIP_SHIFT (IRQ_VS_SOFT - IRQ_S_SOFT) +#define VSIP_VALID_MASK ((_UL(1) << IRQ_S_SOFT) | \ + (_UL(1) << IRQ_S_TIMER) | \ + (_UL(1) << IRQ_S_EXT) | \ + (_UL(1) << IRQ_PMU_OVF)) + +/* AIA CSR bits */ +#define TOPI_IID_SHIFT 16 +#define TOPI_IID_MASK GENMASK(11, 0) +#define TOPI_IPRIO_MASK GENMASK(7, 0) +#define TOPI_IPRIO_BITS 8 + +#define TOPEI_ID_SHIFT 16 +#define TOPEI_ID_MASK GENMASK(10, 0) +#define TOPEI_PRIO_MASK GENMASK(10, 0) + +#define ISELECT_IPRIO0 0x30 +#define ISELECT_IPRIO15 0x3f +#define ISELECT_MASK GENMASK(8, 0) + +#define HVICTL_VTI BIT(30) +#define HVICTL_IID GENMASK(27, 16) +#define HVICTL_IID_SHIFT 16 +#define HVICTL_DPR BIT(9) +#define HVICTL_IPRIOM BIT(8) +#define HVICTL_IPRIO GENMASK(7, 0) + +/* xENVCFG flags */ +#define ENVCFG_STCE (_ULL(1) << 63) +#define ENVCFG_PBMTE (_ULL(1) << 62) +#define ENVCFG_CBZE (_UL(1) << 7) +#define ENVCFG_CBCFE (_UL(1) << 6) +#define ENVCFG_CBIE_SHIFT 4 +#define ENVCFG_CBIE (_UL(0x3) << ENVCFG_CBIE_SHIFT) +#define ENVCFG_CBIE_ILL _UL(0x0) +#define ENVCFG_CBIE_FLUSH _UL(0x1) +#define ENVCFG_CBIE_INV _UL(0x3) +#define ENVCFG_FIOM _UL(0x1) + +/* Smstateen bits */ +#define SMSTATEEN0_AIA_IMSIC_SHIFT 58 +#define SMSTATEEN0_AIA_IMSIC (_ULL(1) << SMSTATEEN0_AIA_IMSIC_SHIFT) +#define SMSTATEEN0_AIA_SHIFT 59 +#define SMSTATEEN0_AIA (_ULL(1) << SMSTATEEN0_AIA_SHIFT) +#define SMSTATEEN0_AIA_ISEL_SHIFT 60 +#define SMSTATEEN0_AIA_ISEL (_ULL(1) << SMSTATEEN0_AIA_ISEL_SHIFT) +#define SMSTATEEN0_HSENVCFG_SHIFT 62 +#define SMSTATEEN0_HSENVCFG (_ULL(1) << SMSTATEEN0_HSENVCFG_SHIFT) +#define SMSTATEEN0_SSTATEEN0_SHIFT 63 +#define SMSTATEEN0_SSTATEEN0 (_ULL(1) << SMSTATEEN0_SSTATEEN0_SHIFT) + +/* symbolic CSR names: */ +#define CSR_CYCLE 0xc00 +#define CSR_TIME 0xc01 +#define CSR_INSTRET 0xc02 +#define CSR_HPMCOUNTER3 0xc03 +#define CSR_HPMCOUNTER4 0xc04 +#define CSR_HPMCOUNTER5 0xc05 +#define CSR_HPMCOUNTER6 0xc06 +#define CSR_HPMCOUNTER7 0xc07 +#define CSR_HPMCOUNTER8 0xc08 +#define CSR_HPMCOUNTER9 0xc09 +#define CSR_HPMCOUNTER10 0xc0a +#define CSR_HPMCOUNTER11 0xc0b +#define CSR_HPMCOUNTER12 0xc0c +#define CSR_HPMCOUNTER13 0xc0d +#define CSR_HPMCOUNTER14 0xc0e +#define CSR_HPMCOUNTER15 0xc0f +#define CSR_HPMCOUNTER16 0xc10 +#define CSR_HPMCOUNTER17 0xc11 +#define CSR_HPMCOUNTER18 0xc12 +#define CSR_HPMCOUNTER19 0xc13 +#define CSR_HPMCOUNTER20 0xc14 +#define CSR_HPMCOUNTER21 0xc15 +#define CSR_HPMCOUNTER22 0xc16 +#define CSR_HPMCOUNTER23 0xc17 +#define CSR_HPMCOUNTER24 0xc18 +#define CSR_HPMCOUNTER25 0xc19 +#define CSR_HPMCOUNTER26 0xc1a +#define CSR_HPMCOUNTER27 0xc1b +#define CSR_HPMCOUNTER28 0xc1c +#define CSR_HPMCOUNTER29 0xc1d +#define CSR_HPMCOUNTER30 0xc1e +#define CSR_HPMCOUNTER31 0xc1f +#define CSR_CYCLEH 0xc80 +#define CSR_TIMEH 0xc81 +#define CSR_INSTRETH 0xc82 +#define CSR_HPMCOUNTER3H 0xc83 +#define CSR_HPMCOUNTER4H 0xc84 +#define CSR_HPMCOUNTER5H 0xc85 +#define CSR_HPMCOUNTER6H 0xc86 +#define CSR_HPMCOUNTER7H 0xc87 +#define CSR_HPMCOUNTER8H 0xc88 +#define CSR_HPMCOUNTER9H 0xc89 +#define CSR_HPMCOUNTER10H 0xc8a +#define CSR_HPMCOUNTER11H 0xc8b +#define CSR_HPMCOUNTER12H 0xc8c +#define CSR_HPMCOUNTER13H 0xc8d +#define CSR_HPMCOUNTER14H 0xc8e +#define CSR_HPMCOUNTER15H 0xc8f +#define CSR_HPMCOUNTER16H 0xc90 +#define CSR_HPMCOUNTER17H 0xc91 +#define CSR_HPMCOUNTER18H 0xc92 +#define CSR_HPMCOUNTER19H 0xc93 +#define CSR_HPMCOUNTER20H 0xc94 +#define CSR_HPMCOUNTER21H 0xc95 +#define CSR_HPMCOUNTER22H 0xc96 +#define CSR_HPMCOUNTER23H 0xc97 +#define CSR_HPMCOUNTER24H 0xc98 +#define CSR_HPMCOUNTER25H 0xc99 +#define CSR_HPMCOUNTER26H 0xc9a +#define CSR_HPMCOUNTER27H 0xc9b +#define CSR_HPMCOUNTER28H 0xc9c +#define CSR_HPMCOUNTER29H 0xc9d +#define CSR_HPMCOUNTER30H 0xc9e +#define CSR_HPMCOUNTER31H 0xc9f + +#define CSR_SCOUNTOVF 0xda0 + +#define CSR_SSTATUS 0x100 +#define CSR_SIE 0x104 +#define CSR_STVEC 0x105 +#define CSR_SCOUNTEREN 0x106 +#define CSR_SENVCFG 0x10a +#define CSR_SSTATEEN0 0x10c +#define CSR_SSCRATCH 0x140 +#define CSR_SEPC 0x141 +#define CSR_SCAUSE 0x142 +#define CSR_STVAL 0x143 +#define CSR_SIP 0x144 +#define CSR_SATP 0x180 + +#define CSR_STIMECMP 0x14d +#define CSR_STIMECMPH 0x15d + +/* Supervisor-Level Window to Indirectly Accessed Registers (AIA) */ +#define CSR_SISELECT 0x150 +#define CSR_SIREG 0x151 + +/* Supervisor-Level Interrupts (AIA) */ +#define CSR_STOPEI 0x15c +#define CSR_STOPI 0xdb0 + +/* Supervisor-Level High-Half CSRs (AIA) */ +#define CSR_SIEH 0x114 +#define CSR_SIPH 0x154 + +#define CSR_VSSTATUS 0x200 +#define CSR_VSIE 0x204 +#define CSR_VSTVEC 0x205 +#define CSR_VSSCRATCH 0x240 +#define CSR_VSEPC 0x241 +#define CSR_VSCAUSE 0x242 +#define CSR_VSTVAL 0x243 +#define CSR_VSIP 0x244 +#define CSR_VSATP 0x280 +#define CSR_VSTIMECMP 0x24d +#define CSR_VSTIMECMPH 0x25d + +#define CSR_HSTATUS 0x600 +#define CSR_HEDELEG 0x602 +#define CSR_HIDELEG 0x603 +#define CSR_HIE 0x604 +#define CSR_HTIMEDELTA 0x605 +#define CSR_HCOUNTEREN 0x606 +#define CSR_HGEIE 0x607 +#define CSR_HENVCFG 0x60a +#define CSR_HTIMEDELTAH 0x615 +#define CSR_HENVCFGH 0x61a +#define CSR_HTVAL 0x643 +#define CSR_HIP 0x644 +#define CSR_HVIP 0x645 +#define CSR_HTINST 0x64a +#define CSR_HGATP 0x680 +#define CSR_HGEIP 0xe12 + +/* Virtual Interrupts and Interrupt Priorities (H-extension with AIA) */ +#define CSR_HVIEN 0x608 +#define CSR_HVICTL 0x609 +#define CSR_HVIPRIO1 0x646 +#define CSR_HVIPRIO2 0x647 + +/* VS-Level Window to Indirectly Accessed Registers (H-extension with AIA) */ +#define CSR_VSISELECT 0x250 +#define CSR_VSIREG 0x251 + +/* VS-Level Interrupts (H-extension with AIA) */ +#define CSR_VSTOPEI 0x25c +#define CSR_VSTOPI 0xeb0 + +/* Hypervisor and VS-Level High-Half CSRs (H-extension with AIA) */ +#define CSR_HIDELEGH 0x613 +#define CSR_HVIENH 0x618 +#define CSR_HVIPH 0x655 +#define CSR_HVIPRIO1H 0x656 +#define CSR_HVIPRIO2H 0x657 +#define CSR_VSIEH 0x214 +#define CSR_VSIPH 0x254 + +/* Hypervisor stateen CSRs */ +#define CSR_HSTATEEN0 0x60c +#define CSR_HSTATEEN0H 0x61c + +#define CSR_MSTATUS 0x300 +#define CSR_MISA 0x301 +#define CSR_MIDELEG 0x303 +#define CSR_MIE 0x304 +#define CSR_MTVEC 0x305 +#define CSR_MENVCFG 0x30a +#define CSR_MENVCFGH 0x31a +#define CSR_MSCRATCH 0x340 +#define CSR_MEPC 0x341 +#define CSR_MCAUSE 0x342 +#define CSR_MTVAL 0x343 +#define CSR_MIP 0x344 +#define CSR_PMPCFG0 0x3a0 +#define CSR_PMPADDR0 0x3b0 +#define CSR_MVENDORID 0xf11 +#define CSR_MARCHID 0xf12 +#define CSR_MIMPID 0xf13 +#define CSR_MHARTID 0xf14 + +/* Machine-Level Interrupts (CLIC) */ +#define CSR_MTVT 0x307 +#define CSR_MNXTI 0x345 + +/* Machine-Level Window to Indirectly Accessed Registers (AIA) */ +#define CSR_MISELECT 0x350 +#define CSR_MIREG 0x351 + +/* Machine-Level Interrupts (AIA) */ +#define CSR_MTOPEI 0x35c +#define CSR_MTOPI 0xfb0 + +/* Virtual Interrupts for Supervisor Level (AIA) */ +#define CSR_MVIEN 0x308 +#define CSR_MVIP 0x309 + +/* Machine-Level High-Half CSRs (AIA) */ +#define CSR_MIDELEGH 0x313 +#define CSR_MIEH 0x314 +#define CSR_MVIENH 0x318 +#define CSR_MVIPH 0x319 +#define CSR_MIPH 0x354 + +#define CSR_VSTART 0x8 +#define CSR_VCSR 0xf +#define CSR_VL 0xc20 +#define CSR_VTYPE 0xc21 +#define CSR_VLENB 0xc22 + +/* Scalar Crypto Extension - Entropy */ +#define CSR_SEED 0x015 +#define SEED_OPST_MASK _UL(0xc0000000) +#define SEED_OPST_BIST _UL(0x00000000) +#define SEED_OPST_WAIT _UL(0x40000000) +#define SEED_OPST_ES16 _UL(0x80000000) +#define SEED_OPST_DEAD _UL(0xc0000000) +#define SEED_ENTROPY_MASK _UL(0xffff) + +#ifdef ARCH_RISCV_M_MODE +#define CSR_STATUS CSR_MSTATUS +#define CSR_IE CSR_MIE +#define CSR_TVEC CSR_MTVEC +#define CSR_ENVCFG CSR_MENVCFG +#define CSR_SCRATCH CSR_MSCRATCH +#define CSR_EPC CSR_MEPC +#define CSR_CAUSE CSR_MCAUSE +#define CSR_TVAL CSR_MTVAL +#define CSR_IP CSR_MIP + +#define CSR_IEH CSR_MIEH +#define CSR_ISELECT CSR_MISELECT +#define CSR_IREG CSR_MIREG +#define CSR_IPH CSR_MIPH +#define CSR_TOPEI CSR_MTOPEI +#define CSR_TOPI CSR_MTOPI + +#define SR_IE SR_MIE +#define SR_PIE SR_MPIE +#define SR_PP SR_MPP + +#define RV_IRQ_SOFT IRQ_M_SOFT +#define RV_IRQ_TIMER IRQ_M_TIMER +#define RV_IRQ_EXT IRQ_M_EXT +#else /* ARCH_RISCV_M_MODE */ +#define CSR_STATUS CSR_SSTATUS +#define CSR_IE CSR_SIE +#define CSR_TVEC CSR_STVEC +#define CSR_ENVCFG CSR_SENVCFG +#define CSR_SCRATCH CSR_SSCRATCH +#define CSR_EPC CSR_SEPC +#define CSR_CAUSE CSR_SCAUSE +#define CSR_TVAL CSR_STVAL +#define CSR_IP CSR_SIP + +#define CSR_IEH CSR_SIEH +#define CSR_ISELECT CSR_SISELECT +#define CSR_IREG CSR_SIREG +#define CSR_IPH CSR_SIPH +#define CSR_TOPEI CSR_STOPEI +#define CSR_TOPI CSR_STOPI + +#define SR_IE SR_SIE +#define SR_PIE SR_SPIE +#define SR_PP SR_SPP + +#define RV_IRQ_SOFT IRQ_S_SOFT +#define RV_IRQ_TIMER IRQ_S_TIMER +#define RV_IRQ_EXT IRQ_S_EXT +#define RV_IRQ_PMU IRQ_PMU_OVF +#define SIP_LCOFIP (_AC(0x1, UL) << IRQ_PMU_OVF) +#endif /* !ARCH_RISCV_M_MODE */ + +/* IE/IP (Supervisor/Machine Interrupt Enable/Pending) flags */ +#define IE_SIE (_UL(0x1) << RV_IRQ_SOFT) +#define IE_TIE (_UL(0x1) << RV_IRQ_TIMER) +#define IE_EIE (_UL(0x1) << RV_IRQ_EXT) + +#ifndef __ASSEMBLY__ + +#ifdef __GNUC__ +#define csr_swap(csr, val) \ +({ \ + rt_ubase_t __v = (rt_ubase_t)(val); \ + __asm__ volatile ("csrrw %0, " __ASM_STR(csr) ", %1" \ + : "=r" (__v) : "rK" (__v) \ + : "memory"); \ + __v; \ +}) + +#define csr_read(csr) \ +({ \ + register rt_ubase_t __v; \ + __asm__ volatile ("csrr %0, " __ASM_STR(csr) \ + : "=r" (__v) : \ + : "memory"); \ + __v; \ +}) + +#define csr_write(csr, val) \ +({ \ + rt_ubase_t __v = (rt_ubase_t)(val); \ + __asm__ volatile ("csrw " __ASM_STR(csr) ", %0" \ + : : "rK" (__v) \ + : "memory"); \ +}) + +#define csr_read_set(csr, val) \ +({ \ + rt_ubase_t __v = (rt_ubase_t)(val); \ + __asm__ volatile ("csrrs %0, " __ASM_STR(csr) ", %1" \ + : "=r" (__v) : "rK" (__v) \ + : "memory"); \ + __v; \ +}) + +#define csr_read_or(csr, val) \ +({ \ + rt_ubase_t __v = (rt_ubase_t)(val); \ + __asm__ volatile ("csrrsi %0, " __ASM_STR(csr) ", %1" \ + : "=r" (__v) : "i" (__v) \ + : "memory"); \ + __v; \ +}) + +#define csr_set(csr, val) \ +({ \ + rt_ubase_t __v = (rt_ubase_t)(val); \ + __asm__ volatile ("csrs " __ASM_STR(csr) ", %0" \ + : : "rK" (__v) \ + : "memory"); \ +}) + +#define csr_read_clear(csr, val) \ +({ \ + rt_ubase_t __v = (rt_ubase_t)(val); \ + __asm__ volatile ("csrrc %0, " __ASM_STR(csr) ", %1" \ + : "=r" (__v) : "rK" (__v) \ + : "memory"); \ + __v; \ +}) + +#define csr_clear(csr, val) \ +({ \ + rt_ubase_t __v = (rt_ubase_t)(val); \ + __asm__ volatile ("csrc " __ASM_STR(csr) ", %0" \ + : : "rK" (__v) \ + : "memory"); \ +}) +#endif /* __GNUC__ */ + +#endif /* __ASSEMBLY__ */ + +#endif /* __CSR_H__ */ diff --git a/libcpu/risc-v/common64/include/isa_ext.h b/libcpu/risc-v/common64/include/isa_ext.h new file mode 100755 index 00000000000..f806421abac --- /dev/null +++ b/libcpu/risc-v/common64/include/isa_ext.h @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2006-2025, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2025-04-20 GuEe-GUI The first version + */ + +#ifndef __ISA_EXT_H__ +#define __ISA_EXT_H__ + +#include + +#define RISCV_ISA_EXT_I 0 +#define RISCV_ISA_EXT_M 1 +#define RISCV_ISA_EXT_A 2 +#define RISCV_ISA_EXT_F 3 +#define RISCV_ISA_EXT_D 4 +#define RISCV_ISA_EXT_Q 5 +#define RISCV_ISA_EXT_C 6 +#define RISCV_ISA_EXT_V 7 +#define RISCV_ISA_EXT_H 8 + +#define RISCV_ISA_EXT_BASE 26 + +#define RISCV_ISA_EXT_SSCOFPMF 26 +#define RISCV_ISA_EXT_SSTC 27 +#define RISCV_ISA_EXT_SVINVAL 28 +#define RISCV_ISA_EXT_SVPBMT 29 +#define RISCV_ISA_EXT_ZBB 30 +#define RISCV_ISA_EXT_ZICBOM 31 +#define RISCV_ISA_EXT_ZIHINTPAUSE 32 +#define RISCV_ISA_EXT_SVNAPOT 33 +#define RISCV_ISA_EXT_ZICBOZ 34 +#define RISCV_ISA_EXT_SMAIA 35 +#define RISCV_ISA_EXT_SSAIA 36 +#define RISCV_ISA_EXT_ZBA 37 +#define RISCV_ISA_EXT_ZBS 38 +#define RISCV_ISA_EXT_ZICNTR 39 +#define RISCV_ISA_EXT_ZICSR 40 +#define RISCV_ISA_EXT_ZIFENCEI 41 +#define RISCV_ISA_EXT_ZIHPM 42 +#define RISCV_ISA_EXT_SMSTATEEN 43 +#define RISCV_ISA_EXT_ZICOND 44 +#define RISCV_ISA_EXT_ZBC 45 +#define RISCV_ISA_EXT_ZBKB 46 +#define RISCV_ISA_EXT_ZBKC 47 +#define RISCV_ISA_EXT_ZBKX 48 +#define RISCV_ISA_EXT_ZKND 49 +#define RISCV_ISA_EXT_ZKNE 50 +#define RISCV_ISA_EXT_ZKNH 51 +#define RISCV_ISA_EXT_ZKR 52 +#define RISCV_ISA_EXT_ZKSED 53 +#define RISCV_ISA_EXT_ZKSH 54 +#define RISCV_ISA_EXT_ZKT 55 +#define RISCV_ISA_EXT_ZVBB 56 +#define RISCV_ISA_EXT_ZVBC 57 +#define RISCV_ISA_EXT_ZVKB 58 +#define RISCV_ISA_EXT_ZVKG 59 +#define RISCV_ISA_EXT_ZVKNED 60 +#define RISCV_ISA_EXT_ZVKNHA 61 +#define RISCV_ISA_EXT_ZVKNHB 62 +#define RISCV_ISA_EXT_ZVKSED 63 +#define RISCV_ISA_EXT_ZVKSH 64 +#define RISCV_ISA_EXT_ZVKT 65 +#define RISCV_ISA_EXT_ZFH 66 +#define RISCV_ISA_EXT_ZFHMIN 67 +#define RISCV_ISA_EXT_ZIHINTNTL 68 +#define RISCV_ISA_EXT_ZVFH 69 +#define RISCV_ISA_EXT_ZVFHMIN 70 +#define RISCV_ISA_EXT_ZFA 71 +#define RISCV_ISA_EXT_ZTSO 72 +#define RISCV_ISA_EXT_ZACAS 73 +#define RISCV_ISA_EXT_XANDESPMU 74 + +#define RISCV_ISA_EXT_MAX 75 +#define RISCV_ISA_EXT_INVALID RT_UINT32_MAX + +#ifdef ARCH_RISCV_M_MODE +#define RISCV_ISA_EXT_SxAIA RISCV_ISA_EXT_SMAIA +#else +#define RISCV_ISA_EXT_SxAIA RISCV_ISA_EXT_SSAIA +#endif + +void riscv_isa_ext_set_raw(rt_uint32_t isa); +void riscv_isa_ext_clear_raw(rt_uint32_t isa); +rt_bool_t riscv_isa_ext_test_raw(rt_uint32_t isa); + +#define riscv_isa_ext_set(isa) riscv_isa_ext_set_raw(RISCV_ISA_EXT_##isa) +#define riscv_isa_ext_clear(isa) riscv_isa_ext_clear_raw(RISCV_ISA_EXT_##isa) +#define riscv_isa_ext_test(isa) riscv_isa_ext_test_raw(RISCV_ISA_EXT_##isa) + +#endif /* __ISA_EXT_H__ */ diff --git a/libcpu/risc-v/common64/include/page_def.h b/libcpu/risc-v/common64/include/page_def.h new file mode 100755 index 00000000000..ba68d85ee77 --- /dev/null +++ b/libcpu/risc-v/common64/include/page_def.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2006-2025, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2025-04-20 GuEe-GUI The first version + */ + +#ifndef __PAGE_DEF_H__ +#define __PAGE_DEF_H__ + +#include + +#ifndef ARCH_PAGE_SHIFT +#define ARCH_PAGE_SHIFT 12 +#elif ARCH_PAGE_SHIFT != 12 +#warning Make sure you fully understand what you are doing +#endif +#define ARCH_PAGE_SIZE (_UL(1) << ARCH_PAGE_SHIFT) +#define ARCH_PAGE_MASK (ARCH_PAGE_SIZE - 1) + +#define SUPPER_PAGE_SIZE 0x200000 +#define SUPPER_PAGE_MASK (SUPPER_PAGE_SIZE - 1) + +#endif /* __PAGE_DEF_H__ */ diff --git a/libcpu/risc-v/common64/sbi.h b/libcpu/risc-v/common64/include/sbi.h similarity index 92% rename from libcpu/risc-v/common64/sbi.h rename to libcpu/risc-v/common64/include/sbi.h index 45853871a89..b7171d4fcd6 100644 --- a/libcpu/risc-v/common64/sbi.h +++ b/libcpu/risc-v/common64/include/sbi.h @@ -49,6 +49,7 @@ #define _MACHINE_SBI_H_ #include +#include #include /* SBI Specification Version */ @@ -102,6 +103,14 @@ #define SBI_RFNC_REMOTE_HFENCE_VVMA_ASID 5 #define SBI_RFNC_REMOTE_HFENCE_VVMA 6 +#define SBI_EXT_ID_SRST 0x53525354 +#define SBI_SRST_FID_RESET 0 +#define SBI_SRST_RESET_TYPE_SHUTDOWN 0 +#define SBI_SRST_RESET_TYPE_COLD_REBOOT 1 +#define SBI_SRST_RESET_TYPE_WARM_REBOOT 2 +#define SBI_SRST_RESET_REASON_NONE 0 +#define SBI_SRST_RESET_REASON_SYS_FAILURE 1 + /* Hart State Management (HSM) Extension */ #define SBI_EXT_ID_HSM 0x48534D #define SBI_HSM_HART_START 0 @@ -171,6 +180,11 @@ extern unsigned long sbi_spec_version; extern unsigned long sbi_impl_id; extern unsigned long sbi_impl_version; +extern bool has_time_extension; +extern bool has_ipi_extension; +extern bool has_rfnc_extension; +extern bool has_srst_extension; + static __inline long sbi_probe_extension(long id) { @@ -215,6 +229,18 @@ void sbi_hsm_hart_stop(void); */ int sbi_hsm_hart_status(unsigned long hart); +/* + * Reboot the system. The type will be one of: + * - SBI_SRST_RESET_TYPE_WARM_REBOOT + * - SBI_SRST_RESET_TYPE_COLD_REBOOT + */ +void sbi_srst_reboot(unsigned long reset_type); + +/* + * Power off the system. + */ +void sbi_srst_power_off(void); + /* Legacy extension functions. */ static __inline void sbi_console_putchar(int ch) diff --git a/libcpu/risc-v/common64/include/setup.h b/libcpu/risc-v/common64/include/setup.h new file mode 100755 index 00000000000..f04d839a11c --- /dev/null +++ b/libcpu/risc-v/common64/include/setup.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-21 GuEe-GUI first version + */ + +#ifndef __SETUP_H__ +#define __SETUP_H__ + +void rt_hw_common_setup(void); + +#endif /* __SETUP_H__ */ diff --git a/libcpu/risc-v/common64/isa_ext.c b/libcpu/risc-v/common64/isa_ext.c new file mode 100755 index 00000000000..ffbb676c034 --- /dev/null +++ b/libcpu/risc-v/common64/isa_ext.c @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2006-2025, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2025-04-20 GuEe-GUI The first version + */ + +#include +#include + +static RT_BITMAP_DECLARE(isa_map, RISCV_ISA_EXT_MAX); + +void riscv_isa_ext_set_raw(rt_uint32_t isa) +{ + rt_bitmap_set_bit(isa_map, isa); +} + +void riscv_isa_ext_clear_raw(rt_uint32_t isa) +{ + rt_bitmap_clear_bit(isa_map, isa); +} + +rt_bool_t riscv_isa_ext_test_raw(rt_uint32_t isa) +{ + return rt_bitmap_test_bit(isa_map, isa); +} diff --git a/libcpu/risc-v/common64/mmu.c b/libcpu/risc-v/common64/mmu.c index 1fb67741b69..771ae7f97ce 100644 --- a/libcpu/risc-v/common64/mmu.c +++ b/libcpu/risc-v/common64/mmu.c @@ -36,6 +36,10 @@ #define USER_VADDR_START 0 #endif +#ifdef RT_USING_DM +#define rt_hw_cpu_dcache_clean(addr, size) rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, addr, size) +#endif + static size_t _unmap_area(struct rt_aspace *aspace, void *v_addr); /* Define the structure of early page table */ diff --git a/libcpu/risc-v/common64/pic/Kconfig b/libcpu/risc-v/common64/pic/Kconfig new file mode 100755 index 00000000000..662e54a23b4 --- /dev/null +++ b/libcpu/risc-v/common64/pic/Kconfig @@ -0,0 +1,18 @@ +config RT_PIC_RISCV_AIA + bool "RISC-V AIA (Advanced Interrupt Architecture)" + depends on RT_USING_PIC + help + with APLIC (Advanced Platform-Level Interrupt Controller) and + IMSIC (Incoming MSI Controller) + +config RT_PIC_RISCV_CLIC + bool "RISC-V CLIC (Core-Local Interrupt Controller)" + depends on RT_USING_PIC + +config RT_PIC_RISCV_INTC + bool "RISC-V CPU interrupt controller" + depends on RT_USING_PIC + +config RT_PIC_SIFIVE_PLIC + bool "SiFive PLIC (Platform-Level Interrupt Controller)" + depends on RT_USING_PIC diff --git a/libcpu/risc-v/common64/pic/SConscript b/libcpu/risc-v/common64/pic/SConscript new file mode 100755 index 00000000000..21658b16718 --- /dev/null +++ b/libcpu/risc-v/common64/pic/SConscript @@ -0,0 +1,23 @@ +from building import * + +group = [] + +if not GetDepend(['RT_USING_PIC']): + Return('group') + +src = [] +cwd = GetCurrentDir() +CPPPATH = [cwd] + +if GetDepend(['RT_PIC_RISCV_AIA']): + src += ['pic-riscv-aplic.c', 'pic-riscv-imsic.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/libcpu/risc-v/common64/pic/pic-riscv-aplic.c b/libcpu/risc-v/common64/pic/pic-riscv-aplic.c new file mode 100755 index 00000000000..3d1b8f2b60c --- /dev/null +++ b/libcpu/risc-v/common64/pic/pic-riscv-aplic.c @@ -0,0 +1,691 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-01-30 GuEe-GUI first version + */ + +#include +#include +#include + +#define DBG_TAG "pic.aplic" +#define DBG_LVL DBG_INFO +#include + +#include +#include +#include "pic-riscv-imsic.h" + +#define APLIC_MAX_IDC RT_BIT(14) +#define APLIC_MAX_SOURCE 1024 + +#define APLIC_DOMAINCFG 0x0000 +#define APLIC_DOMAINCFG_RDONLY 0x80000000 +#define APLIC_DOMAINCFG_IE RT_BIT(8) +#define APLIC_DOMAINCFG_DM RT_BIT(2) +#define APLIC_DOMAINCFG_BE RT_BIT(0) + +#define APLIC_SOURCECFG_BASE 0x0004 +#define APLIC_SOURCECFG_D RT_BIT(10) +#define APLIC_SOURCECFG_CHILDIDX_MASK 0x000003ff +#define APLIC_SOURCECFG_SM_MASK 0x00000007 +#define APLIC_SOURCECFG_SM_INACTIVE 0x0 +#define APLIC_SOURCECFG_SM_DETACH 0x1 +#define APLIC_SOURCECFG_SM_EDGE_RISE 0x4 +#define APLIC_SOURCECFG_SM_EDGE_FALL 0x5 +#define APLIC_SOURCECFG_SM_LEVEL_HIGH 0x6 +#define APLIC_SOURCECFG_SM_LEVEL_LOW 0x7 + +#define APLIC_MMSICFGADDR 0x1bc0 +#define APLIC_MMSICFGADDRH 0x1bc4 +#define APLIC_SMSICFGADDR 0x1bc8 +#define APLIC_SMSICFGADDRH 0x1bcc + +#ifdef ARCH_RISCV_M_MODE +#define APLIC_xMSICFGADDR APLIC_MMSICFGADDR +#define APLIC_xMSICFGADDRH APLIC_MMSICFGADDRH +#else +#define APLIC_xMSICFGADDR APLIC_SMSICFGADDR +#define APLIC_xMSICFGADDRH APLIC_SMSICFGADDRH +#endif + +#define APLIC_xMSICFGADDRH_L RT_BIT(31) +#define APLIC_xMSICFGADDRH_HHXS_MASK 0x1f +#define APLIC_xMSICFGADDRH_HHXS_SHIFT 24 +#define APLIC_xMSICFGADDRH_HHXS (APLIC_xMSICFGADDRH_HHXS_MASK << APLIC_xMSICFGADDRH_HHXS_SHIFT) +#define APLIC_xMSICFGADDRH_LHXS_MASK 0x7 +#define APLIC_xMSICFGADDRH_LHXS_SHIFT 20 +#define APLIC_xMSICFGADDRH_LHXS (APLIC_xMSICFGADDRH_LHXS_MASK << APLIC_xMSICFGADDRH_LHXS_SHIFT) +#define APLIC_xMSICFGADDRH_HHXW_MASK 0x7 +#define APLIC_xMSICFGADDRH_HHXW_SHIFT 16 +#define APLIC_xMSICFGADDRH_HHXW (APLIC_xMSICFGADDRH_HHXW_MASK << APLIC_xMSICFGADDRH_HHXW_SHIFT) +#define APLIC_xMSICFGADDRH_LHXW_MASK 0xf +#define APLIC_xMSICFGADDRH_LHXW_SHIFT 12 +#define APLIC_xMSICFGADDRH_LHXW (APLIC_xMSICFGADDRH_LHXW_MASK << APLIC_xMSICFGADDRH_LHXW_SHIFT) +#define APLIC_xMSICFGADDRH_BAPPN_MASK 0xfff +#define APLIC_xMSICFGADDRH_BAPPN_SHIFT 0 +#define APLIC_xMSICFGADDRH_BAPPN (APLIC_xMSICFGADDRH_BAPPN_MASK << APLIC_xMSICFGADDRH_BAPPN_SHIFT) + +#define APLIC_xMSICFGADDR_PPN_SHIFT 12 + +#define APLIC_xMSICFGADDR_PPN_HART(__lhxs) \ + (RT_BIT(__lhxs) - 1) + +#define APLIC_xMSICFGADDR_PPN_LHX_MASK(__lhxw) \ + (RT_BIT(__lhxw) - 1) +#define APLIC_xMSICFGADDR_PPN_LHX_SHIFT(__lhxs) \ + ((__lhxs)) +#define APLIC_xMSICFGADDR_PPN_LHX(__lhxw, __lhxs) \ + (APLIC_xMSICFGADDR_PPN_LHX_MASK(__lhxw) << APLIC_xMSICFGADDR_PPN_LHX_SHIFT(__lhxs)) + +#define APLIC_xMSICFGADDR_PPN_HHX_MASK(__hhxw) \ + (RT_BIT(__hhxw) - 1) +#define APLIC_xMSICFGADDR_PPN_HHX_SHIFT(__hhxs) \ + ((__hhxs) + APLIC_xMSICFGADDR_PPN_SHIFT) +#define APLIC_xMSICFGADDR_PPN_HHX(__hhxw, __hhxs) \ + (APLIC_xMSICFGADDR_PPN_HHX_MASK(__hhxw) << APLIC_xMSICFGADDR_PPN_HHX_SHIFT(__hhxs)) + +#define APLIC_IRQBITS_PER_REG 32 + +#define APLIC_SETIP_BASE 0x1c00 +#define APLIC_SETIPNUM 0x1cdc + +#define APLIC_CLRIP_BASE 0x1d00 +#define APLIC_CLRIPNUM 0x1ddc + +#define APLIC_SETIE_BASE 0x1e00 +#define APLIC_SETIENUM 0x1edc + +#define APLIC_CLRIE_BASE 0x1f00 +#define APLIC_CLRIENUM 0x1fdc + +#define APLIC_SETIPNUM_LE 0x2000 +#define APLIC_SETIPNUM_BE 0x2004 + +#define APLIC_GENMSI 0x3000 + +#define APLIC_TARGET_BASE 0x3004 +#define APLIC_TARGET_HART_IDX_SHIFT 18 +#define APLIC_TARGET_HART_IDX_MASK 0x3fff +#define APLIC_TARGET_HART_IDX (APLIC_TARGET_HART_IDX_MASK << APLIC_TARGET_HART_IDX_SHIFT) +#define APLIC_TARGET_GUEST_IDX_SHIFT 12 +#define APLIC_TARGET_GUEST_IDX_MASK 0x3f +#define APLIC_TARGET_GUEST_IDX (APLIC_TARGET_GUEST_IDX_MASK << APLIC_TARGET_GUEST_IDX_SHIFT) +#define APLIC_TARGET_IPRIO_SHIFT 0 +#define APLIC_TARGET_IPRIO_MASK 0xff +#define APLIC_TARGET_IPRIO (APLIC_TARGET_IPRIO_MASK << APLIC_TARGET_IPRIO_SHIFT) +#define APLIC_TARGET_EIID_SHIFT 0 +#define APLIC_TARGET_EIID_MASK 0x7ff +#define APLIC_TARGET_EIID (APLIC_TARGET_EIID_MASK << APLIC_TARGET_EIID_SHIFT) + +#define APLIC_IDC_BASE 0x4000 +#define APLIC_IDC_SIZE 32 + +#define APLIC_IDC_IDELIVERY 0x00 + +#define APLIC_IDC_IFORCE 0x04 + +#define APLIC_IDC_ITHRESHOLD 0x08 + +#define APLIC_IDC_TOPI 0x18 +#define APLIC_IDC_TOPI_ID_SHIFT 16 +#define APLIC_IDC_TOPI_ID_MASK 0x3ff +#define APLIC_IDC_TOPI_ID (APLIC_IDC_TOPI_ID_MASK << APLIC_IDC_TOPI_ID_SHIFT) +#define APLIC_IDC_TOPI_PRIO_SHIFT 0 +#define APLIC_IDC_TOPI_PRIO_MASK 0xff +#define APLIC_IDC_TOPI_PRIO (APLIC_IDC_TOPI_PRIO_MASK << APLIC_IDC_TOPI_PRIO_SHIFT) + +#define APLIC_IDC_CLAIMI 0x1c + +#define APLIC_ENABLE_IDELIVERY 1 +#define APLIC_ENABLE_ITHRESHOLD 0 + +#define APLIC_DEFAULT_PRIORITY 1 + +struct riscv_aplic_idc +{ + void *regs; +}; + +struct riscv_aplic_msicfg +{ + rt_ubase_t base_ppn; + rt_uint32_t hhxs; + rt_uint32_t hhxw; + rt_uint32_t lhxs; + rt_uint32_t lhxw; +}; + +struct riscv_aplic +{ + struct rt_pic parent; + struct rt_pic *msi_ic; + + struct rt_ofw_node *np; + + void *regs; + int parent_irq; + int init_cpu_id; + + rt_uint32_t gsi_base; + rt_uint32_t nr_irqs; + rt_uint32_t nr_idcs; + + struct riscv_aplic_msicfg msicfg; + struct riscv_aplic_idc idc[RT_CPUS_NR]; +}; + +rt_always_inline rt_uint32_t irq_cfg_offset(int hwirq) +{ + return (hwirq - 1) * sizeof(rt_uint32_t); +} + +static void aplic_handler(int hwirq, void *data); + +static rt_err_t aplic_irq_init(struct rt_pic *pic) +{ + struct riscv_aplic *aplic = pic->priv_data; + + if (!aplic->msi_ic) + { + if (aplic->parent_irq < 0) + { + struct rt_ofw_cell_args irq_args; + + if (rt_ofw_parse_irq_cells(aplic->np, 0, &irq_args)) + { + LOG_E("%s: Parent IRQ is not available", rt_ofw_node_full_name(aplic->np)); + return RT_EOK; + } + + /* Create mapping */ + irq_args.args[0] = RV_IRQ_EXT; + + aplic->parent_irq = rt_ofw_map_irq(&irq_args); + + rt_hw_interrupt_install(aplic->parent_irq, aplic_handler, aplic, "APLIC"); + } + + rt_pic_irq_unmask(aplic->parent_irq); + } + + return RT_EOK; +} + +static void aplic_irq_unmask(struct rt_pic_irq *pirq) +{ + struct riscv_aplic *aplic = pirq->pic->priv_data; + + HWREG32(aplic->regs + APLIC_SETIENUM) = pirq->hwirq; +} + +static void aplic_irq_mask(struct rt_pic_irq *pirq) +{ + struct riscv_aplic *aplic = pirq->pic->priv_data; + + HWREG32(aplic->regs + APLIC_CLRIENUM) = pirq->hwirq; +} + +static void aplic_msi_irq_eoi(struct rt_pic_irq *pirq) +{ + struct riscv_aplic *aplic = pirq->pic->priv_data; + + switch (pirq->mode) + { + case RT_IRQ_MODE_LEVEL_HIGH: + case RT_IRQ_MODE_LEVEL_LOW: + HWREG32(aplic->regs + APLIC_SETIPNUM_LE) = pirq->hwirq; + break; + + default: + break; + } +} + +static rt_err_t aplic_direct_irq_set_priority(struct rt_pic_irq *pirq, rt_uint32_t priority) +{ + rt_uint32_t value; + struct riscv_aplic *aplic = pirq->pic->priv_data; + + /* Start with 1 not 0 */ + priority += 1; + priority = rt_min_t(rt_uint32_t, priority, APLIC_TARGET_IPRIO_MASK); + + value = HWREG32(aplic->regs + APLIC_TARGET_BASE + irq_cfg_offset(pirq->hwirq)); + value &= ~APLIC_TARGET_IPRIO; + value |= priority << APLIC_TARGET_IPRIO_SHIFT; + + HWREG32(aplic->regs + APLIC_TARGET_BASE + irq_cfg_offset(pirq->hwirq)) = value; + + return RT_EOK; +} + +static rt_err_t aplic_direct_irq_set_affinity(struct rt_pic_irq *pirq, rt_bitmap_t *affinity) +{ + rt_uint32_t value; + struct riscv_aplic *aplic = pirq->pic->priv_data; + + value = RT_FIELD_PREP(APLIC_TARGET_HART_IDX, rt_bitmap_next_set_bit(affinity, 0, RT_CPUS_NR)); + value |= RT_FIELD_PREP(APLIC_TARGET_IPRIO, pirq->priority); + + HWREG32(aplic->regs + APLIC_TARGET_BASE + irq_cfg_offset(pirq->hwirq)) = value; + + return RT_EOK; +} + +static rt_err_t aplic_msi_irq_set_affinity(struct rt_pic_irq *pirq, rt_bitmap_t *affinity) +{ + int cpu_id; + rt_uint32_t value; + struct riscv_aplic *aplic = pirq->pic->priv_data; + + cpu_id = rt_bitmap_next_set_bit(affinity, 0, RT_CPUS_NR); + value = RT_FIELD_PREP(APLIC_TARGET_HART_IDX, cpu_id); + value |= RT_FIELD_PREP(APLIC_TARGET_EIID, pirq->parent->hwirq); + + HWREG32(aplic->regs + APLIC_TARGET_BASE + irq_cfg_offset(pirq->hwirq)) = value; + + return RT_EOK; +} + +static rt_err_t aplic_irq_set_triger_mode(struct rt_pic_irq *pirq, rt_uint32_t mode) +{ + struct riscv_aplic *aplic = pirq->pic->priv_data; + + switch (mode) + { + case RT_IRQ_MODE_NONE: + mode = APLIC_SOURCECFG_SM_INACTIVE; + break; + case RT_IRQ_MODE_LEVEL_LOW: + mode = APLIC_SOURCECFG_SM_LEVEL_LOW; + break; + case RT_IRQ_MODE_LEVEL_HIGH: + mode = APLIC_SOURCECFG_SM_LEVEL_HIGH; + break; + case RT_IRQ_MODE_EDGE_FALLING: + mode = APLIC_SOURCECFG_SM_EDGE_FALL; + break; + case RT_IRQ_MODE_EDGE_RISING: + mode = APLIC_SOURCECFG_SM_EDGE_RISE; + break; + default: + return -RT_EINVAL; + } + + HWREG32(aplic->regs + APLIC_SOURCECFG_BASE + irq_cfg_offset(pirq->hwirq)) = mode; + + return RT_EOK; +} + +static void aplic_msi_irq_write_msg(struct rt_pic_irq *pirq, struct rt_pci_msi_msg *msg) +{ + rt_ubase_t tppn, tbppn, msg_addr; + rt_uint32_t group_index, hart_index, guest_index, val; + struct riscv_aplic *aplic = pirq->pic->priv_data; + struct riscv_aplic_msicfg *msicfg = &aplic->msicfg; + + /* For zeroed MSI, simply write zero into the target register */ + if (!msg->address_hi && !msg->address_lo && !msg->data) + { + HWREG32(aplic->regs + APLIC_TARGET_BASE + irq_cfg_offset(pirq->hwirq)) = 0; + return; + } + + /* Sanity check on message data */ + if (msg->data > APLIC_TARGET_EIID_MASK) + { + LOG_W("MSI msg out of EIID mask"); + } + + /* Compute target MSI address */ + msg_addr = (((rt_uint64_t)msg->address_hi) << 32) | msg->address_lo; + tppn = msg_addr >> APLIC_xMSICFGADDR_PPN_SHIFT; + + /* Compute target HART Base PPN */ + tbppn = tppn; + tbppn &= ~APLIC_xMSICFGADDR_PPN_HART(msicfg->lhxs); + tbppn &= ~APLIC_xMSICFGADDR_PPN_LHX(msicfg->lhxw, msicfg->lhxs); + tbppn &= ~APLIC_xMSICFGADDR_PPN_HHX(msicfg->hhxw, msicfg->hhxs); + + /* Compute target group and hart indexes */ + group_index = (tppn >> APLIC_xMSICFGADDR_PPN_HHX_SHIFT(msicfg->hhxs)) & + APLIC_xMSICFGADDR_PPN_HHX_MASK(msicfg->hhxw); + hart_index = (tppn >> APLIC_xMSICFGADDR_PPN_LHX_SHIFT(msicfg->lhxs)) & + APLIC_xMSICFGADDR_PPN_LHX_MASK(msicfg->lhxw); + hart_index |= (group_index << msicfg->lhxw); + + if (hart_index > APLIC_TARGET_HART_IDX_MASK) + { + LOG_W("HART index out of IDX mask"); + } + + /* Compute target guest index */ + guest_index = tppn & APLIC_xMSICFGADDR_PPN_HART(msicfg->lhxs); + + if (guest_index > APLIC_TARGET_GUEST_IDX_MASK) + { + LOG_W("Guest index out of IDX mask"); + } + + /* Update IRQ TARGET register */ + val = RT_FIELD_PREP(APLIC_TARGET_HART_IDX, hart_index); + val |= RT_FIELD_PREP(APLIC_TARGET_GUEST_IDX, guest_index); + val |= RT_FIELD_PREP(APLIC_TARGET_EIID, msg->data); + + HWREG32(aplic->regs + APLIC_TARGET_BASE + irq_cfg_offset(pirq->hwirq)) = val; +} + +static int aplic_irq_map(struct rt_pic *pic, int hwirq, rt_uint32_t mode) +{ + int irq; + struct riscv_aplic *aplic = pic->priv_data; + struct rt_pic_irq *pirq = rt_pic_find_irq(pic, hwirq); + + if (aplic->msi_ic) + { + int msi_irq = aplic->msi_ic->ops->irq_alloc_msi(aplic->msi_ic, RT_NULL); + + if (msi_irq < 0) + { + return msi_irq; + } + + irq = rt_pic_config_irq(pic, hwirq, hwirq); + + rt_pic_cascade(pirq, msi_irq); + } + else + { + irq = rt_pic_config_irq(pic, hwirq, hwirq); + } + + rt_memset(pirq->affinity, 0, sizeof(pirq->affinity)); + + RT_IRQ_AFFINITY_SET(pirq->affinity, aplic->init_cpu_id); + pic->ops->irq_set_affinity(pirq, pirq->affinity); + + pirq->mode = mode; + pic->ops->irq_set_triger_mode(pirq, mode); + + return irq; +} + +static rt_err_t aplic_irq_parse(struct rt_pic *pic, struct rt_ofw_cell_args *args, struct rt_pic_irq *out_pirq) +{ + struct riscv_aplic *aplic = pic->priv_data; + + if (args->args_count < 2 || !args->args[0]) + { + return -RT_EINVAL; + } + + out_pirq->hwirq = args->args[0] - aplic->gsi_base; + out_pirq->mode = args->args[1] & RT_IRQ_MODE_MASK; + + return RT_EOK; +} + +const static struct rt_pic_ops aplic_direct_ops = +{ + .name = "APLIC-DIRECT", + .irq_init = aplic_irq_init, + .irq_mask = aplic_irq_mask, + .irq_unmask = aplic_irq_unmask, + .irq_set_priority = aplic_direct_irq_set_priority, + .irq_set_affinity = aplic_direct_irq_set_affinity, + .irq_set_triger_mode = aplic_irq_set_triger_mode, + .irq_map = aplic_irq_map, + .irq_parse = aplic_irq_parse, +}; + +const static struct rt_pic_ops aplic_msi_ops = +{ + .name = "APLIC-MSI", + .irq_init = aplic_irq_init, + .irq_mask = aplic_irq_mask, + .irq_unmask = aplic_irq_unmask, + .irq_eoi = aplic_msi_irq_eoi, + .irq_set_affinity = aplic_msi_irq_set_affinity, + .irq_set_triger_mode = aplic_irq_set_triger_mode, + .irq_write_msi_msg = aplic_msi_irq_write_msg, + .irq_map = aplic_irq_map, + .irq_parse = aplic_irq_parse, + .flags = RT_PIC_F_IRQ_ROUTING, +}; + +static void aplic_handler(int hwirq, void *data) +{ + struct rt_pic_irq *pirq; + struct riscv_aplic *aplic = data; + struct riscv_aplic_idc *idc = &aplic->idc[rt_hw_cpu_id()]; + + while ((hwirq = HWREG32(idc->regs + APLIC_IDC_CLAIMI))) + { + hwirq >>= APLIC_IDC_TOPI_ID_SHIFT; + + pirq = rt_pic_find_irq(&aplic->parent, hwirq); + + rt_pic_handle_isr(pirq); + } +} + +static rt_err_t aplic_ofw_init(struct rt_ofw_node *np, const struct rt_ofw_node_id *id) +{ + rt_err_t err; + rt_uint32_t val; + struct rt_ofw_node *msi_np; + struct riscv_aplic *aplic = rt_calloc(1, sizeof(*aplic)); + + aplic->regs = rt_ofw_iomap(np, 0); + + if (!aplic->regs) + { + err = -RT_ENOMEM; + goto _fail; + } + + if ((err = rt_ofw_prop_read_u32(np, "riscv,num-sources", &aplic->nr_irqs))) + { + goto _fail; + } + + msi_np = rt_ofw_parse_phandle(np, "msi-parent", 0); + + /* Disable all interrupts */ + for (int i = 0; i <= aplic->nr_irqs; i += 32) + { + HWREG32(aplic->regs + APLIC_CLRIE_BASE + (i / 32) * sizeof(rt_uint32_t)) = RT_UINT32_MAX; + } + + /* Clear APLIC domaincfg */ + HWREG32(aplic->regs + APLIC_DOMAINCFG) = 0; + + aplic->np = np; + aplic->init_cpu_id = rt_hw_cpu_id(); + rt_ofw_data(np) = &aplic->parent; + + if (msi_np) + { + const struct imsic_global_config *imsic_global; + struct riscv_aplic_msicfg *msicfg = &aplic->msicfg; + + if ((err = imsic_ofw_probe(msi_np, np, id))) + { + goto _fail; + } + + if (!(imsic_global = imsic_global_config_read(msi_np))) + { + LOG_E("%s: IMSIC global config not found", rt_ofw_node_full_name(np)); + err = -RT_EINVAL; + goto _fail; + } + + /* Find number of guest index bits (LHXS) */ + msicfg->lhxs = imsic_global->guest_index_bits; + if (APLIC_xMSICFGADDRH_LHXS_MASK < msicfg->lhxs) + { + LOG_E("%s: IMSIC guest index bits big for APLIC LHXS", rt_ofw_node_full_name(np)); + err = -RT_EINVAL; + goto _fail; + } + + /* Find number of HART index bits (LHXW) */ + msicfg->lhxw = imsic_global->hart_index_bits; + if (APLIC_xMSICFGADDRH_LHXW_MASK < msicfg->lhxw) + { + LOG_E("%s: IMSIC hart index bits big for APLIC LHXW", rt_ofw_node_full_name(np)); + err = -RT_EINVAL; + goto _fail; + } + + /* Find number of group index bits (HHXW) */ + msicfg->hhxw = imsic_global->group_index_bits; + if (APLIC_xMSICFGADDRH_HHXW_MASK < msicfg->hhxw) + { + LOG_E("%s: IMSIC group index bits big for APLIC HHXW", rt_ofw_node_full_name(np)); + err = -RT_EINVAL; + goto _fail; + } + + /* Find first bit position of group index (HHXS) */ + msicfg->hhxs = imsic_global->group_index_shift; + if (msicfg->hhxs < (2 * APLIC_xMSICFGADDR_PPN_SHIFT)) + { + LOG_E("%s: IMSIC group index shift should be >= %d", + rt_ofw_node_full_name(np), (2 * APLIC_xMSICFGADDR_PPN_SHIFT)); + err = -RT_EINVAL; + goto _fail; + } + msicfg->hhxs -= (2 * APLIC_xMSICFGADDR_PPN_SHIFT); + if (APLIC_xMSICFGADDRH_HHXS_MASK < msicfg->hhxs) + { + LOG_E("%s: IMSIC group index shift big for APLIC HHXS", rt_ofw_node_full_name(np)); + err = -RT_EINVAL; + goto _fail; + } + + /* Compute PPN base */ + msicfg->base_ppn = imsic_global->base_addr >> APLIC_xMSICFGADDR_PPN_SHIFT; + msicfg->base_ppn &= ~APLIC_xMSICFGADDR_PPN_HART(msicfg->lhxs); + msicfg->base_ppn &= ~APLIC_xMSICFGADDR_PPN_LHX(msicfg->lhxw, msicfg->lhxs); + msicfg->base_ppn &= ~APLIC_xMSICFGADDR_PPN_HHX(msicfg->hhxw, msicfg->hhxs); + } + else + { + int hwirq, cpu_id; + rt_ubase_t hartid; + struct riscv_aplic_idc *idc; + struct rt_ofw_cell_args idcs; + struct rt_ofw_node *cpu_np; + + while (!rt_ofw_parse_irq_cells(np, aplic->nr_idcs, &idcs)) + { + cpu_np = rt_ofw_get_parent(idcs.data); + + hwirq = idcs.args[0]; + hartid = rt_ofw_get_cpu_hwid(cpu_np, 0); + + rt_ofw_node_put(cpu_np); + + if (hwirq != RV_IRQ_EXT) + { + continue; + } + + cpu_id = riscv_hartid_to_cpu_id(hartid); + + if (cpu_id >= RT_CPUS_NR) + { + continue; + } + + idc = &aplic->idc[cpu_id]; + idc->regs = aplic->regs + APLIC_IDC_BASE + aplic->nr_idcs * APLIC_IDC_SIZE; + + /* Priority must be less than threshold for interrupt triggering */ + HWREG32(idc->regs + APLIC_IDC_ITHRESHOLD) = APLIC_ENABLE_ITHRESHOLD; + + /* Delivery must be set to 1 for interrupt triggering */ + HWREG32(idc->regs + APLIC_IDC_IDELIVERY) = APLIC_ENABLE_IDELIVERY; + + ++aplic->nr_idcs; + } + } + + /* Setup global config and interrupt delivery */ +#ifdef ARCH_RISCV_M_MODE + if (msi_np) + { + rt_uint32_t valh; + + val = rt_lower_32_bits(aplic->msicfg.base_ppn); + valh = RT_FIELD_PREP(APLIC_xMSICFGADDRH_BAPPN, rt_upper_32_bits(aplic->msicfg.base_ppn)); + valh |= RT_FIELD_PREP(APLIC_xMSICFGADDRH_LHXW, aplic->msicfg.lhxw); + valh |= RT_FIELD_PREP(APLIC_xMSICFGADDRH_HHXW, aplic->msicfg.hhxw); + valh |= RT_FIELD_PREP(APLIC_xMSICFGADDRH_LHXS, aplic->msicfg.lhxs); + valh |= RT_FIELD_PREP(APLIC_xMSICFGADDRH_HHXS, aplic->msicfg.hhxs); + + HWREG32(aplic->regs + APLIC_xMSICFGADDR) = val; + HWREG32(aplic->regs + APLIC_xMSICFGADDRH) = valh; + } +#endif /* ARCH_RISCV_M_MODE */ + + /* Setup APLIC domaincfg register */ + val = HWREG32(aplic->regs + APLIC_DOMAINCFG); + val |= APLIC_DOMAINCFG_IE; + + if (msi_np) + { + val |= APLIC_DOMAINCFG_DM; + } + + HWREG32(aplic->regs + APLIC_DOMAINCFG) = val; + + if (HWREG32(aplic->regs + APLIC_DOMAINCFG) != val) + { + LOG_W("Unable to write 0x%x in domaincfg", val); + } + + aplic->parent_irq = -1; + + aplic->parent.priv_data = aplic; + + if (msi_np) + { + aplic->parent.ops = &aplic_msi_ops; + aplic->msi_ic = rt_ofw_data(msi_np); + } + else + { + aplic->parent.ops = &aplic_direct_ops; + } + + rt_pic_linear_irq(&aplic->parent, aplic->nr_irqs); + + rt_pic_user_extends(&aplic->parent); + + return RT_EOK; + +_fail: + if (aplic->regs) + { + rt_iounmap(aplic->regs); + } + + rt_free(aplic); + + return err; +} + +static const struct rt_ofw_node_id aplic_ofw_ids[] = +{ + { .compatible = "riscv,aplic" }, + { /* sentinel */ } +}; +RT_PIC_OFW_DECLARE(aplic, aplic_ofw_ids, aplic_ofw_init); diff --git a/libcpu/risc-v/common64/pic/pic-riscv-clic.c b/libcpu/risc-v/common64/pic/pic-riscv-clic.c new file mode 100755 index 00000000000..9a95f0c518a --- /dev/null +++ b/libcpu/risc-v/common64/pic/pic-riscv-clic.c @@ -0,0 +1,500 @@ +/* + * Copyright (c) 2006-2025, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2025-04-20 GuEe-GUI The first version + */ + +#include +#include +#include +#include + +#define DBG_TAG "pic.clic" +#define DBG_LVL DBG_INFO +#include + +#include +#include +#include + +#define CLIC_CFG 0x0000 +#define CLIC_INFO 0x0004 +#define CLIC_VENDOR_MINTTHRESH 0x0008 +#define CLIC_INT_TRIG 0x0040 +#define CLIC_INT_IP 0x1000 +#define CLIC_INT_IE 0x1001 +#define CLIC_INT_ATTR 0x1002 +#define CLIC_INT_CTL 0x1003 + +#define CLIC_CFG_VECTORED_ENABLE BIT(0) +#define CLIC_CFG_LEVEL_SHIFT 1 +#define CLIC_CFG_LEVEL_MASK GENMASK(4, CLIC_CFG_LEVEL_SHIFT) +#define CLIC_CFG_MODE_SHIFT 5 +#define CLIC_CFG_MODE_MASK GENMASK(6, CLIC_CFG_MODE_SHIFT) + +#define CLIC_INFO_NUM_INTERRUPT(val) (val & GENMASK(12, 0)) +#define CLIC_INFO_VERSION(val) ((val & GENMASK(20, 13)) >> 13) +#define CLIC_INFO_VERSION_ARCH(val) ((val & GENMASK(20, 17)) >> 17) +#define CLIC_INFO_VERSION_IMPL(val) ((val & GENMASK(16, 13)) >> 13) +#define CLIC_INFO_INT_CTL_BITS(val) ((val & GENMASK(24, 21)) >> 21) +#define CLIC_INFO_NUM_TRIGGER(val) ((val & GENMASK(30, 25)) >> 25) + +#define CLIC_INT_TRIG_ENABLE BIT(31) +#define CLIC_INT_TRIG_IRQ_SHIFT 0 +#define CLIC_INT_TRIG_IRQ_MASK GENMASK(12, 0) + +#define CLIC_INT_IP_PENDING 1 + +#define CLIC_INT_IE_ENABLE 1 + +#define CLIC_INT_ATTR_MODE_SHIFT 6 +#define CLIC_INT_ATTR_MODE_MACHINE (0x11 << CLIC_INT_ATTR_MODE_SHIFT) +#define CLIC_INT_ATTR_MODE_SUPERVISOR (0x01 << CLIC_INT_ATTR_MODE_SHIFT) +#define CLIC_INT_ATTR_MODE_USER (0x00 << CLIC_INT_ATTR_MODE_SHIFT) +#define CLIC_INT_ATTR_TRIGGERED_SHIFT 1 +#define CLIC_INT_ATTR_TRIGGERED_MASK GENMASK(2, CLIC_INT_ATTR_TRIGGERED_SHIFT) +#define CLIC_INT_ATTR_TRIGGERED_LEVEL (0x0 << CLIC_INT_ATTR_TRIGGERED_SHIFT) +#define CLIC_INT_ATTR_TRIGGERED_EDGE (0x1 << CLIC_INT_ATTR_TRIGGERED_SHIFT) +#define CLIC_INT_ATTR_SHV BIT(0) + +#define CLIC_INT_CTL_LEVEL(nlbits, val) ((val) << (nlbits)) +#define CLIC_INT_CTL_LEVEL_MASK(nlbits) GENMASK(8, nlbits) +#define CLIC_INT_CTL_PRIORITY(nlbits, val) (val) +#define CLIC_INT_CTL_PRIORITY_MASK(nlbits) GENMASK(nlbits, 0) + +#define CLIC_INT_ID_USIP 0 /* User software Interrupt */ +#define CLIC_INT_ID_SSIP 1 /* Supervisor software Interrupt */ +#define CLIC_INT_ID_MSIP 3 /* Machine software interrupt */ +#define CLIC_INT_ID_UTIP 4 /* User timer interrupt */ +#define CLIC_INT_ID_STIP 5 /* Supervisor timer interrupt */ +#define CLIC_INT_ID_MTIP 7 /* Machine timer interrupt */ +#define CLIC_INT_ID_UEIP 8 /* User external (PLIC) interrupt */ +#define CLIC_INT_ID_SEIP 9 /* Supervisor external (PLIC) interrupt */ +#define CLIC_INT_ID_MEIP 11 /* Machine external (PLIC) interrupt */ +#define CLIC_INT_ID_CSIP 12 /* CLIC software interrupt */ +#define CLIC_INT_ID_EXT_BASE 16 /* CLIC external base */ + +#ifdef ARCH_RISCV_M_MODE +#define CLIC_INT_ID_xSIP CLIC_INT_ID_SSIP +#define CLIC_INT_ID_xTIP CLIC_INT_ID_MTIP +#define CLIC_INT_ID_xEIP CLIC_INT_ID_MEIP +#else +#define CLIC_INT_ID_xSIP CLIC_INT_ID_MSIP +#define CLIC_INT_ID_xTIP CLIC_INT_ID_STIP +#define CLIC_INT_ID_xEIP CLIC_INT_ID_SEIP +#endif + +#define CLIC_QUIRK_MODE_FIXED 0 +#define CLIC_QUIRK_MINTTHRESH 1 + +#ifdef ARCH_RISCV_M_MODE +#include "pic-riscv-clint.c" +#endif + +struct riscv_clic +{ +#ifndef PIC_DISABLE_DM + struct rt_pic parent; +#endif + + void *regs; + + rt_bitmap_t quirks; + rt_bool_t shv; + rt_uint32_t irqs_count:16; + rt_uint32_t int_ctl_bits:8; + +#ifdef RT_USING_SMP + rt_uint32_t ipi_base; + rt_atomic_t ipi_enable[RT_CPUS_NR]; + rt_atomic_t ipi_pending[RT_CPUS_NR]; +#endif +}; + +extern void fast_irq_vectors(void); + +static void riscv_clic_set_mintthresh(struct riscv_clic *clic, rt_uint32_t mintthresh) +{ + if (rt_bitmap_test_bit(&clic->quirks, CLIC_QUIRK_MINTTHRESH)) + { + HWREG32(clic->regs + CLIC_VENDOR_MINTTHRESH) = mintthresh; + } +} + +static void riscv_clic_irq_ack_raw(struct riscv_clic *clic, int hwirq) +{ + if (clic->shv) + { + return; + } + + hwirq = csr_swap(CSR_MNXTI, 0UL); +} + +static void riscv_clic_irq_mask_raw(struct riscv_clic *clic, int hwirq) +{ +#ifdef RT_USING_SMP + if (hwirq >= clic->ipi_base) + { + rt_atomic_t *ipi_enable = &clic->ipi_enable[rt_hw_cpu_id()]; + + rt_atomic_and(ipi_enable, ~BIT(hwirq - clic->ipi_base)); + + if (rt_atomic_load(ipi_enable) != 0) + { + return; + } + + hwirq = CLIC_INT_ID_xSIP; + } +#endif /* RT_USING_SMP */ + + HWREG8(clic->regs + CLIC_INT_IE + 4 * hwirq) = CLIC_INT_IE_ENABLE; +} + +static void riscv_clic_irq_unmask_raw(struct riscv_clic *clic, int hwirq) +{ +#ifdef RT_USING_SMP + if (hwirq >= clic->ipi_base) + { + rt_atomic_t *ipi_enable = &clic->ipi_enable[rt_hw_cpu_id()]; + + rt_atomic_or(ipi_enable, BIT(hwirq - clic->ipi_base)); + hwirq = CLIC_INT_ID_xSIP; + } +#endif /* RT_USING_SMP */ + + HWREG8(clic->regs + CLIC_INT_IE + 4 * hwirq) = !CLIC_INT_IE_ENABLE; +} + +static void riscv_clic_irq_send_ipi_raw(struct riscv_clic *clic, int ipi, rt_bitmap_t *cpumask) +{ + int cpu; + + rt_bitmap_for_each_set_bit(cpumask, cpu, RT_CPUS_NR) + { + rt_atomic_or(&clic->ipi_pending[cpu], BIT(ipi)); + + #ifdef ARCH_RISCV_M_MODE + clint_send_ipi(cpu); + #endif + } + +#ifndef ARCH_RISCV_M_MODE + sbi_send_ipi((const unsigned long *)cpumask); +#endif +} + +static rt_bool_t riscv_clic_handler_isr_raw(struct riscv_clic *clic, int hwirq, + rt_bool_t (*callback)(int hwirq, void *data), void *data) +{ + if (hwirq >= CLIC_INT_ID_EXT_BASE) + { + return callback(hwirq, data); + } + else if (hwirq == CLIC_INT_ID_xTIP) + { + riscv_timer_isr(); + return RT_TRUE; + } +#ifdef RT_USING_SMP + else if (hwirq == CLIC_INT_ID_xSIP) + { + int ipi; + rt_bool_t res; + rt_bitmap_t pending; + + #ifdef ARCH_RISCV_M_MODE + clint_clear_ipi(); + #endif + + pending = rt_atomic_exchange(&clic->ipi_pending[rt_hw_cpu_id()], 0); + + rt_bitmap_for_each_set_bit(&pending, ipi, RT_MAX_IPI) + { + res = callback(clic->ipi_base + ipi, data); + } + + return res; + } +#endif /* RT_USING_SMP */ + else if (hwirq != CLIC_INT_ID_xEIP) + { + LOG_E("Unhandled IRQ: %d", hwirq); + } + + return RT_FALSE; +} + +static void riscv_clic_percpu_common_init(struct riscv_clic *clic) +{ + RT_UNUSED(clic); + + csr_set(CSR_MTVT, &fast_irq_vectors); +} + +static struct riscv_clic *riscv_clic_common_init(rt_ubase_t clic_base, rt_size_t clic_size, + rt_bitmap_t quirks) +{ + rt_uint32_t value; + struct riscv_clic *clic = rt_calloc(1, sizeof(*clic)); + + if (!clic) + { + rt_set_errno(-RT_ENOMEM); + return RT_NULL; + } + + clic->regs = rt_ioremap((void *)clic_base, clic_size); + + if (!clic->regs) + { + rt_set_errno(-RT_EIO); + + rt_free(clic); + return RT_NULL; + } + + clic->quirks = quirks; + + riscv_clic_set_mintthresh(clic, 0); + + value = HWREG32(clic->regs + CLIC_INFO); + clic->int_ctl_bits = CLIC_INFO_INT_CTL_BITS(value); + clic->irqs_count = CLIC_INFO_NUM_INTERRUPT(value); + + HWREG32(clic->regs + CLIC_CFG) |= CLIC_CFG_VECTORED_ENABLE; + HWREG32(clic->regs + CLIC_CFG) |= 8 << CLIC_CFG_LEVEL_SHIFT; + HWREG32(clic->regs + CLIC_CFG) |= CLIC_CFG_MODE_MASK << CLIC_CFG_MODE_SHIFT; + + clic->shv = !!(HWREG32(clic->regs + CLIC_CFG) & CLIC_CFG_VECTORED_ENABLE); + + LOG_D("Version %d.%d, %d IRQs implemented", + CLIC_INFO_VERSION_ARCH(value), CLIC_INFO_VERSION_IMPL(value), + clic->irqs_count); + +#ifdef RT_USING_SMP + clic->irqs_count += RT_MAX_IPI; +#endif + + value = clic->shv ? CLIC_INT_ATTR_SHV : 0; +#ifdef ARCH_RISCV_M_MODE + value |= CLIC_INT_ATTR_MODE_MACHINE << CLIC_INT_ATTR_MODE_SHIFT; +#else + value |= CLIC_INT_ATTR_MODE_SUPERVISOR << CLIC_INT_ATTR_MODE_SHIFT; +#endif + + for (int hwirq = 0; hwirq < clic->irqs_count; ++hwirq) + { + HWREG8(clic->regs + CLIC_INT_ATTR + 4 * hwirq) &= CLIC_INT_ATTR_TRIGGERED_MASK; + HWREG8(clic->regs + CLIC_INT_ATTR + 4 * hwirq) |= value; + } + + return clic; +} + +#ifndef PIC_DISABLE_DM +static rt_err_t riscv_clic_irq_init(struct rt_pic *pic) +{ + struct riscv_clic *clic = pirq->pic->priv_data; + + riscv_clic_percpu_common_init(clic); + + return RT_EOK; +} + +static void riscv_clic_irq_ack(struct rt_pic_irq *pirq) +{ + return riscv_clic_irq_ack_raw(pirq->hwirq); +} + +static void riscv_clic_irq_mask(struct rt_pic_irq *pirq) +{ + struct riscv_clic *clic = pirq->pic->priv_data; + + return riscv_clic_irq_mask_raw(clic, pirq->hwirq); +} + +static void riscv_clic_irq_unmask(struct rt_pic_irq *pirq) +{ + struct riscv_clic *clic = pirq->pic->priv_data; + + return riscv_clic_irq_unmask_raw(clic, pirq->hwirq); +} + +static rt_err_t riscv_clic_irq_set_priority(struct rt_pic_irq *pirq, rt_uint32_t priority) +{ + struct riscv_clic *clic = pirq->pic->priv_data; + + priority = rt_min_t(rt_uint32_t, priority, GENMASK(8, 8 - clic->int_ctl_bits)); + + HWREG8(clic->regs + CLIC_INT_CTL + pirq->hwirq * 4) = + CLIC_INT_CTL_LEVEL_MASK(clic->int_ctl_bits) | + CLIC_INT_CTL_PRIORITY(clic->int_ctl_bits, priority); + + return RT_EOK; +} + +static rt_err_t riscv_clic_irq_set_triger_mode(struct rt_pic_irq *pirq, rt_uint32_t mode) +{ + int hwirq = pirq->hwirq; + struct riscv_clic *clic = pirq->pic->priv_data; + + if (rt_bitmap_test_bit(&clic->quirks, PLIC_QUIRK_EDGE_INTERRUPT)) + { + return -RT_ENOSYS; + } + + if (mode == RT_IRQ_MODE_EDGE_RISING) + { + HWREG8(clic->regs + CLIC_INT_ATTR + 4 * hwirq) &= CLIC_INT_ATTR_TRIGGERED_MASK; + HWREG8(clic->regs + CLIC_INT_ATTR + 4 * hwirq) |= CLIC_INT_ATTR_TRIGGERED_EDGE; + + return RT_EOK; + } + else if (mode == RT_IRQ_MODE_LEVEL_HIGH) + { + HWREG8(clic->regs + CLIC_INT_ATTR + 4 * hwirq) &= CLIC_INT_ATTR_TRIGGERED_MASK; + HWREG8(clic->regs + CLIC_INT_ATTR + 4 * hwirq) |= CLIC_INT_ATTR_TRIGGERED_LEVEL; + + return RT_EOK; + } + + return -RT_ENOSYS; +} + +static void riscv_clic_irq_send_ipi(struct rt_pic_irq *pirq, rt_bitmap_t *cpumask) +{ + struct riscv_clic *clic = pirq->pic->priv_data; + + riscv_clic_irq_send_ipi_raw(clic, pirq->hwirq, cpumask); +} + +static int riscv_clic_irq_map(struct rt_pic *pic, int hwirq, rt_uint32_t mode) +{ + return rt_pic_config_irq(pic, hwirq, hwirq); +} + +static rt_err_t riscv_clic_irq_parse(struct rt_pic *pic, struct rt_ofw_cell_args *args, struct rt_pic_irq *out_pirq) +{ + struct riscv_clic *clic = pic->priv_data; + + out_pirq->hwirq = args->args[0]; + + if (rt_bitmap_test_bit(&clic->quirks, CLIC_QUIRK_MODE_FIXED)) + { + out_pirq->mode = args->args[1]; + out_pirq->priority = args->args[2]; + } + else + { + out_pirq->mode = RT_IRQ_MODE_LEVEL_HIGH; + out_pirq->priority = args->args[1]; + } + + return RT_EOK; +} + +const static struct rt_pic_ops riscv_clic_ops = +{ + .name = "CLIC", + .irq_init = riscv_clic_irq_init, + .irq_ack = riscv_clic_irq_ack, + .irq_mask = riscv_clic_irq_mask, + .irq_unmask = riscv_clic_irq_unmask, + .irq_set_priority = riscv_clic_irq_set_priority, + .irq_set_triger_mode = riscv_clic_irq_set_triger_mode, + .irq_send_ipi = riscv_clic_irq_send_ipi, + .irq_map = riscv_clic_irq_map, + .irq_parse = riscv_clic_irq_parse, +}; + +static rt_bool_t clic_common_handler_isr(int hwirq, void *data) +{ + struct rt_pic_irq *pirq; + struct riscv_clic *clic = data; + + if (hwirq < clic->ipi_base) + { + pirq = rt_pic_find_irq(&clic->parent, hwirq); + } + else + { + pirq = rt_pic_find_ipi(&clic->parent, hwirq - clic->ipi_base); + } + + riscv_clic_irq_ack(pirq); + + return rt_pic_handle_isr(pirq); +} + +static rt_bool_t clic_handler(void *data) +{ + rt_ubase_t cause; + struct riscv_clic *clic = data; + + cause = csr_read(CSR_CAUSE) & ~CAUSE_IRQ_FLAG; + + return riscv_clic_handler_isr_raw(clic, cause, clic_common_handler_isr, clic); +} + +static rt_err_t riscv_clic_ofw_init(struct rt_ofw_node *np, const struct rt_ofw_node_id *id) +{ + rt_err_t err; + rt_uint64_t clic_base, clic_size; + rt_bitmap_t quirks; + struct riscv_clic *clic; + + if ((err = rt_ofw_get_address(np, 0, &clic_base, &clic_size))) + { + return err; + } + + quirks = id->data ? *(rt_ubase_t *)id->data : 0; + + if (!(clic = clic_common_init(clic_base, clic_size, quirks))) + { + return rt_get_errno(); + } + +#ifdef ARCH_RISCV_M_MODE + riscv_clint_ofw_init(); +#endif + + clic->parent.priv_data = clic; + clic->parent.ops = &riscv_clic_ops; + rt_ofw_data(np) = &clic->parent; + + rt_pic_linear_irq(&clic->parent, clic->irqs_count); + + rt_pic_add_traps(clic_handler, clic); + +#ifdef RT_USING_SMP + clic->ipi_base = clic->irqs_count - RT_MAX_IPI; + + for (int ipi = 0; ipi < RT_MAX_IPI; ++ipi) + { + rt_pic_config_ipi(&clic->parent, ipi, clic->ipi_base + ipi); + } +#endif /* RT_USING_SMP */ + + rt_pic_user_extends(&clic->parent); + + return RT_EOK; +} + +static rt_bitmap_t xtensa_clic_quirk = RT_BIT(CLIC_QUIRK_MODE_FIXED) | RT_BIT(CLIC_QUIRK_MINTTHRESH); + +static const struct rt_ofw_node_id clic_ofw_ids[] = +{ + { .compatible = "cdns,xtensa-core-intc", .data = &xtensa_clic_quirk }, + { .compatible = "nuclei,eclic" }, + { .compatible = "nordic,nrf-clic" }, + { /* sentinel */ } +}; +RT_PIC_OFW_DECLARE(riscv_clic, riscv_clic_ofw_ids, riscv_clic_ofw_init); +#endif /* PIC_DISABLE_DM */ diff --git a/libcpu/risc-v/common64/pic/pic-riscv-clint.c b/libcpu/risc-v/common64/pic/pic-riscv-clint.c new file mode 100755 index 00000000000..a03c886e081 --- /dev/null +++ b/libcpu/risc-v/common64/pic/pic-riscv-clint.c @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-01-30 GuEe-GUI first version + */ + +#include +#include + +#include +#include + +#define CLINT_IPI_OFF 0 +#define CLINT_TIMER_CMP_OFF 0x4000 +#define CLINT_TIMER_VAL_OFF 0xbff8 + +#define CLINT_QUICKS_IPI_FORCE 0 + +rt_uint32_t *riscv_clint_ipi_base, *riscv_aclint_ipi_base; + +rt_inline void clint_send_ipi(int cpu) +{ + if (riscv_clint_ipi_base) + { + HWREG32(riscv_clint_ipi_base + riscv_cpu_id_to_hartid(cpu)) = 1; + } +} + +rt_inline void clint_clear_ipi(void) +{ + if (riscv_clint_ipi_base) + { + HWREG32(riscv_clint_ipi_base + csr_read(CSR_MHARTID)) = 0; + } +} + +static rt_err_t riscv_clint_common_init(rt_ubase_t *mmio_ranges, unsigned mmio_count) +{ + void *clint_timer_val, *clint_timer_cmp; + + if (mmio_count == 1) + { + void *base = rt_ioremap((void *)mmio_ranges[0], mmio_ranges[1]); + + if (!base) + { + return -RT_EIO; + } + + riscv_clint_ipi_base = base + CLINT_IPI_OFF; + clint_timer_cmp = base + CLINT_TIMER_CMP_OFF; + clint_timer_val = base + CLINT_TIMER_VAL_OFF; + } + else + { + riscv_clint_ipi_base = RT_NULL; + + clint_timer_val = rt_ioremap((void *)mmio_ranges[0], mmio_ranges[1]); + + if (!clint_timer_val) + { + return -RT_EIO; + } + + clint_timer_cmp = rt_ioremap((void *)mmio_ranges[2], mmio_ranges[3]); + + if (!clint_timer_cmp) + { + rt_iounmap(clint_timer_val); + return -RT_EIO; + } + } + + riscv_timer_probe(clint_timer_cmp, clint_timer_val); + + return RT_EOK; +} + +#ifndef PIC_DISABLE_DM +static rt_ubase_t aclint_quicks = RT_BIT(CLINT_QUICKS_IPI_FORCE); + +static const struct rt_ofw_node_id riscv_clint_ofw_ids[] = +{ + { .compatible = "riscv,clint0", }, + { .compatible = "sifive,clint0", }, +#ifdef ARCH_RISCV_M_MODE + { .compatible = "riscv,aclint-mtimer", }, + { .compatible = "riscv,aclint-mswi", .data = &aclint_quicks }, +#else + { .compatible = "riscv,aclint-sswi", .data = &aclint_quicks }, +#endif + { /* sentinel */ } +}; + +static rt_err_t riscv_clint_ofw_init(void) +{ + rt_err_t err; + rt_ubase_t quicks; + unsigned mmio_count; + rt_uint64_t addr, size; + rt_ubase_t mmio_ranges[4]; + struct rt_ofw_node *np; + const struct rt_ofw_node_id *id; + + if (!(np = rt_ofw_find_node_by_ids_r(RT_NULL, riscv_clint_ofw_ids, &id))) + { + return -RT_ENOSYS; + } + quicks = id->data ? *(rt_ubase_t *)id->data : 0; + + if ((err = rt_ofw_get_address(np, 0, &addr, &size))) + { + return err; + } + + mmio_ranges[0] = addr; + mmio_ranges[1] = size; + mmio_count = 1; + + if (!rt_ofw_get_address(np, 1, &addr, &size)) + { + mmio_ranges[2] = addr; + mmio_ranges[3] = size; + mmio_count = 2; + } + + rt_ofw_node_set_flag(np, RT_OFW_F_READLY); + + if (!(err = riscv_clint_common_init(mmio_ranges, mmio_count))) + { + if (quicks & CLINT_QUICKS_IPI_FORCE) + { + riscv_aclint_ipi_base = riscv_clint_ipi_base; + } + } + + return err; +} +#endif /* !PIC_DISABLE_DM */ diff --git a/libcpu/risc-v/common64/pic/pic-riscv-imsic.c b/libcpu/risc-v/common64/pic/pic-riscv-imsic.c new file mode 100755 index 00000000000..4cc73cbd17e --- /dev/null +++ b/libcpu/risc-v/common64/pic/pic-riscv-imsic.c @@ -0,0 +1,738 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-01-30 GuEe-GUI first version + */ + +#include +#include +#include + +#define DBG_TAG "pic.imsic" +#define DBG_LVL DBG_INFO +#include + +#include +#include +#include "pic-riscv-imsic.h" + +#define IMSIC_ID_START 1 +#define IMSIC_IPI_ID IMSIC_ID_START + +#define IMSIC_ENABLE_EIDELIVERY 1 +#define IMSIC_DISABLE_EIDELIVERY 0 +#define IMSIC_ENABLE_EITHRESHOLD 0 +#define IMSIC_DISABLE_EITHRESHOLD 1 + +struct riscv_imsic_mmio +{ + rt_uint64_t addr; + rt_uint64_t size; + + void *mapped; +}; + +struct riscv_imsic +{ + struct rt_pic parent; + struct rt_pic *parent_ic; + + struct rt_ofw_node *np; + + int parent_irq; + int msi_id_start; + int init_cpu_id; + + struct imsic_global_config global; + + rt_bitmap_t *vectors; + struct rt_spinlock lock; +}; + +extern rt_uint32_t *riscv_aclint_ipi_base; + +rt_inline unsigned int __fls(unsigned long word) +{ + unsigned int num = RT_BITS_PER_LONG - 1; + +#if RT_BITS_PER_LONG == 64 + if (!(word & (~0UL << 32))) + { + num -= 32; + word <<= 32; + } +#endif + if (!(word & (~0UL << (RT_BITS_PER_LONG - 16)))) + { + num -= 16; + word <<= 16; + } + if (!(word & (~0UL << (RT_BITS_PER_LONG - 8)))) + { + num -= 8; + word <<= 8; + } + if (!(word & (~0UL << (RT_BITS_PER_LONG - 4)))) + { + num -= 4; + word <<= 4; + } + if (!(word & (~0UL << (RT_BITS_PER_LONG - 2)))) + { + num -= 2; + word <<= 2; + } + if (!(word & (~0UL << (RT_BITS_PER_LONG - 1)))) + { + num -= 1; + } + return num; +} + +rt_inline void imsic_csr_write(rt_ubase_t reg, rt_ubase_t val) +{ + csr_write(CSR_ISELECT, reg); + csr_write(CSR_IREG, val); +} + +rt_inline rt_ubase_t imsic_csr_read(rt_ubase_t reg) +{ + csr_write(CSR_ISELECT, reg); + return csr_read(CSR_IREG); +} + +rt_inline rt_ubase_t imsic_csr_read_clear(rt_ubase_t reg, rt_ubase_t val) +{ + csr_write(CSR_ISELECT, reg); + return csr_read_clear(CSR_IREG, val); +} + +rt_inline void imsic_csr_set(rt_ubase_t reg, rt_ubase_t val) +{ + csr_write(CSR_ISELECT, reg); + csr_set(CSR_IREG, val); +} + +rt_inline void imsic_csr_clear(rt_ubase_t reg, rt_ubase_t val) +{ + csr_write(CSR_ISELECT, reg); + csr_clear(CSR_IREG, val); +} + +static void imsic_eix_update(rt_ubase_t base_id, rt_ubase_t num_id, rt_bool_t pend, rt_bool_t val) +{ + rt_ubase_t id = base_id, last_id = base_id + num_id, isel, ireg; + + while (id < last_id) + { + isel = id / RT_BITS_PER_LONG; + isel *= RT_BITS_PER_LONG / IMSIC_EIPx_BITS; + isel += pend ? IMSIC_EIP0 : IMSIC_EIE0; + + /* + * Prepare the ID mask to be programmed in the + * IMSIC EIEx and EIPx registers. These registers + * are XLEN-wide and we must not touch IDs which + * are < base_id and >= (base_id + num_id). + */ + ireg = 0; + for (rt_ubase_t i = id & (__riscv_xlen - 1); id < last_id && i < __riscv_xlen; ++i) + { + ireg |= RT_BIT(i); + id++; + } + + /* + * The IMSIC EIEx and EIPx registers are indirectly + * accessed via using ISELECT and IREG CSRs so we + * need to access these CSRs without getting preempted. + * + * All existing users of this function call this + * function with local IRQs disabled so we don't + * need to do anything special here. + */ + if (val) + { + imsic_csr_set(isel, ireg); + } + else + { + imsic_csr_clear(isel, ireg); + } + } +} + +rt_inline void imsic_id_clear_enable(rt_ubase_t id) +{ + imsic_eix_update(id, 1, RT_FALSE, RT_FALSE); +} + +rt_inline void imsic_id_set_enable(rt_ubase_t id) +{ + imsic_eix_update(id, 1, RT_FALSE, RT_TRUE); +} + +rt_inline void imsic_local_delivery(rt_bool_t enable) +{ + if (enable) + { + imsic_csr_write(IMSIC_EITHRESHOLD, IMSIC_ENABLE_EITHRESHOLD); + imsic_csr_write(IMSIC_EIDELIVERY, IMSIC_ENABLE_EIDELIVERY); + } + else + { + imsic_csr_write(IMSIC_EIDELIVERY, IMSIC_DISABLE_EIDELIVERY); + imsic_csr_write(IMSIC_EITHRESHOLD, IMSIC_DISABLE_EITHRESHOLD); + } +} + +static void imsic_handler(int hwirq, void *data); + +static rt_err_t riscv_imsic_irq_init(struct rt_pic *pic) +{ + struct riscv_imsic *imsic = pic->priv_data; + + imsic_local_delivery(RT_TRUE); + + for (int hwirq = IMSIC_ID_START; hwirq < imsic->global.nr_ids; ++hwirq) + { + /* + * Enable all ID in current CPU + * that will not sync by IPI in mask/umask. + */ + imsic_id_set_enable(hwirq); + } + + if (imsic->parent_irq < 0) + { + struct rt_ofw_cell_args irq_args; + + if (rt_ofw_parse_irq_cells(imsic->np, 0, &irq_args)) + { + LOG_E("%s: Parent IRQ is not available", rt_ofw_node_full_name(imsic->np)); + return RT_EOK; + } + + /* Create mapping */ + irq_args.args[0] = RV_IRQ_EXT; + + imsic->parent_irq = rt_ofw_map_irq(&irq_args); + + rt_hw_interrupt_install(imsic->parent_irq, imsic_handler, imsic, "IMSIC"); + } + + rt_pic_irq_unmask(imsic->parent_irq); + + return RT_EOK; +} + +static void riscv_imsic_irq_mask(struct rt_pic_irq *pirq) +{ + if (pirq->msi_desc) + { + rt_pci_msi_mask_irq(pirq); + } + +#if defined(RT_USING_SMP) && defined(ARCH_RISCV_M_MODE) + if (pirq->irq < RT_MAX_IPI) + { + imsic_id_clear_enable(pirq->hwirq); + } +#endif +} + +static void riscv_imsic_irq_unmask(struct rt_pic_irq *pirq) +{ + if (pirq->msi_desc) + { + rt_pci_msi_unmask_irq(pirq); + } + +#if defined(RT_USING_SMP) && defined(ARCH_RISCV_M_MODE) + if (pirq->irq < RT_MAX_IPI) + { + imsic_id_set_enable(pirq->hwirq); + } +#endif +} + +static rt_err_t riscv_imsic_irq_set_affinity(struct rt_pic_irq *pirq, rt_bitmap_t *affinity) +{ + struct rt_pci_msi_desc *desc = pirq->msi_desc; + + if (desc) + { + int old_cpu, cpu; + + cpu = rt_bitmap_next_set_bit(affinity, 0, RT_CPUS_NR); + + /* IMSIC supports only one HART affinity */ + if (rt_bitmap_next_set_bit(affinity, cpu, RT_CPUS_NR) != RT_CPUS_NR) + { + return -RT_ENOSYS; + } + + old_cpu = rt_bitmap_next_set_bit(pirq->affinity, 0, RT_CPUS_NR); + + if (cpu == old_cpu) + { + return RT_EOK; + } + + RT_IRQ_AFFINITY_CLEAR(pirq->affinity, old_cpu); + RT_IRQ_AFFINITY_SET(pirq->affinity, cpu); + + /* Update MSI info */ + pirq->pic->ops->irq_compose_msi_msg(pirq, &desc->msg); + + /* Update for PCI device */ + rt_pci_msi_write_msg(desc, &desc->msg); + + return RT_EOK; + } + + return -RT_EINVAL; +} + +#ifdef ARCH_RISCV_M_MODE +static void riscv_imsic_irq_send_ipi(struct rt_pic_irq *pirq, rt_bitmap_t *cpumask) +{ + int cpu_id; + struct imsic_local_config *local; + struct riscv_imsic *imsic = pirq->pic->priv_data; + + rt_bitmap_for_each_set_bit(cpumask, cpu_id, RT_CPUS_NR) + { + local = &imsic->global.local[cpu_id]; + + HWREG32(local->msi) = pirq->hwirq; + } +} +#endif /* ARCH_RISCV_M_MODE */ + +static void riscv_imsic_irq_compose_msi_msg(struct rt_pic_irq *pirq, struct rt_pci_msi_msg *msg) +{ + struct imsic_local_config *local; + struct imsic_global_config *global; + struct riscv_imsic *imsic = pirq->pic->priv_data; + + global = &imsic->global; + + if (RT_BIT(global->guest_index_bits) <= 0) + { + LOG_W("Global guest disabled"); + return; + } + + local = &global->local[rt_bitmap_next_set_bit(pirq->affinity, 0, RT_CPUS_NR)]; + + msg->address_hi = rt_upper_32_bits(local->msi_phy); + msg->address_lo = rt_lower_32_bits(local->msi_phy); + msg->data = pirq->hwirq; +} + +static void riscv_imsic_irq_write_msi_msg(struct rt_pic_irq *pirq, struct rt_pci_msi_msg *msg) +{ + struct rt_pic_irq virtual_pirq; + struct riscv_imsic *imsic = pirq->pic->priv_data; + + virtual_pirq.pic = imsic->parent_ic; + virtual_pirq.hwirq = pirq->hwirq; + + imsic->parent_ic->ops->irq_write_msi_msg(&virtual_pirq, msg); +} + +static int riscv_imsic_irq_alloc_msi(struct rt_pic *pic, struct rt_pci_msi_desc *msi_desc) +{ + rt_ubase_t level; + int irq, hwirq_index; + struct rt_pic_irq *pirq; + struct imsic_global_config *global; + struct riscv_imsic *imsic = pic->priv_data; + + global = &imsic->global; + + level = rt_spin_lock_irqsave(&imsic->lock); + hwirq_index = rt_bitmap_next_clear_bit(imsic->vectors, 0, global->nr_ids); + + if (hwirq_index >= global->nr_ids) + { + irq = -RT_EEMPTY; + goto _out_lock; + } + + irq = rt_pic_config_irq(pic, hwirq_index, hwirq_index + IMSIC_ID_START); + if (irq < 0) + { + goto _out_lock; + } + pirq = rt_pic_find_irq(pic, hwirq_index); + pirq->mode = RT_IRQ_MODE_EDGE_RISING; + RT_IRQ_AFFINITY_SET(pirq->affinity, imsic->init_cpu_id); + + rt_bitmap_set_bit(imsic->vectors, hwirq_index); + +_out_lock: + rt_spin_unlock_irqrestore(&imsic->lock, level); + + return irq; +} + +static void riscv_imsic_irq_free_msi(struct rt_pic *pic, int irq) +{ + rt_ubase_t level; + struct rt_pic_irq *pirq; + struct riscv_imsic *imsic; + + pirq = rt_pic_find_pirq(pic, irq); + + if (!pirq) + { + return; + } + + imsic = pirq->pic->priv_data; + + level = rt_spin_lock_irqsave(&imsic->lock); + rt_bitmap_clear_bit(imsic->vectors, pirq->hwirq - IMSIC_ID_START); + rt_spin_unlock_irqrestore(&imsic->lock, level); +} + +const static struct rt_pic_ops riscv_imsic_ops = +{ + .name = "IMSIC", + .irq_init = riscv_imsic_irq_init, + .irq_mask = riscv_imsic_irq_mask, + .irq_unmask = riscv_imsic_irq_unmask, + .irq_set_affinity = riscv_imsic_irq_set_affinity, +#ifdef ARCH_RISCV_M_MODE + .irq_send_ipi = riscv_imsic_irq_send_ipi, +#endif + .irq_compose_msi_msg = riscv_imsic_irq_compose_msi_msg, + .irq_write_msi_msg = riscv_imsic_irq_write_msi_msg, + .irq_alloc_msi = riscv_imsic_irq_alloc_msi, + .irq_free_msi = riscv_imsic_irq_free_msi, +}; + +static void imsic_handler(int hwirq, void *data) +{ + rt_ubase_t local_id; + struct rt_pic_irq *pirq; + struct riscv_imsic *imsic = data; + + while ((local_id = csr_swap(CSR_TOPEI, 0))) + { + local_id >>= TOPEI_ID_SHIFT; + + if (local_id >= imsic->msi_id_start) + { + pirq = rt_pic_find_irq(&imsic->parent, local_id - IMSIC_ID_START); + } + else + { + #if defined(RT_USING_SMP) && defined(ARCH_RISCV_M_MODE) + pirq = rt_pic_find_ipi(&imsic->parent, local_id - IMSIC_IPI_ID); + #else + LOG_E("It's is impossible"); + RT_ASSERT(0); + #endif + } + + rt_pic_handle_isr(pirq); + } +} + +const struct imsic_global_config *imsic_global_config_read(struct rt_ofw_node *np) +{ + struct riscv_imsic *imsic = rt_container_of(rt_ofw_data(np), struct riscv_imsic, parent); + + return imsic ? &imsic->global : RT_NULL; +} + +static const struct rt_ofw_node_id riscv_imsic_ofw_ids[] = +{ + { .compatible = "riscv,imsics" }, + { /* sentinel */ } +}; + +rt_err_t imsic_ofw_probe(struct rt_ofw_node *np, + struct rt_ofw_node *ic_np, const struct rt_ofw_node_id *ic_id) +{ + rt_err_t err; + int irq_count, mmio_count, i; + struct rt_ofw_node_id *id; + struct riscv_imsic *imsic; + struct imsic_global_config *global; + struct riscv_imsic_mmio *mmio = RT_NULL; + + if (!(id = rt_ofw_node_match(np, riscv_imsic_ofw_ids))) + { + return -RT_EINVAL; + } + + if (!(imsic = rt_calloc(1, sizeof(*imsic)))) + { + return -RT_ENOMEM; + } + global = &imsic->global; + + irq_count = rt_ofw_get_irq_count(np); + + rt_ofw_prop_read_u32(np, "riscv,guest-index-bits", &global->guest_index_bits); + + if (rt_ofw_prop_read_u32(np, "riscv,hart-index-bits", &global->hart_index_bits)) + { + global->hart_index_bits = __fls(irq_count); + + if (RT_BIT(global->hart_index_bits) < irq_count) + { + ++global->hart_index_bits; + } + } + + rt_ofw_prop_read_u32(np, "riscv,group-index-bits", &global->guest_index_bits); + + if (rt_ofw_prop_read_u32(np, "riscv,group-index-shift", &global->group_index_shift)) + { + global->group_index_shift = IMSIC_MMIO_PAGE_SHIFT * 2; + } + + if ((err = rt_ofw_prop_read_u32(np, "riscv,num-ids", &global->nr_ids))) + { + LOG_E("%s: Number of IRQ is unknown", rt_ofw_node_full_name(np)); + goto _fail; + } + + if (rt_ofw_prop_read_u32(np, "riscv,num-guest-ids", &global->nr_guest_ids)) + { + global->nr_guest_ids = global->nr_ids; + } + + /* Sanity check guest index bits */ + i = RT_BITS_PER_LONG - IMSIC_MMIO_PAGE_SHIFT; + if (i < global->guest_index_bits) + { + LOG_E("%s: %s index %s too big", "Guest", "bits", rt_ofw_node_full_name(np)); + + err = -RT_EINVAL; + goto _fail; + } + + /* Sanity check HART index bits */ + i = RT_BITS_PER_LONG - IMSIC_MMIO_PAGE_SHIFT - global->guest_index_bits; + if (i < global->hart_index_bits) + { + LOG_E("%s: %s index %s too big", "HART", "bits", rt_ofw_node_full_name(np)); + + err = -RT_EINVAL; + goto _fail; + } + + /* Sanity check group index bits */ + i = RT_BITS_PER_LONG - IMSIC_MMIO_PAGE_SHIFT - global->guest_index_bits - global->hart_index_bits; + if (i < global->group_index_bits) + { + LOG_E("%s: %s index %s too big", "Group", "bits", rt_ofw_node_full_name(np)); + + err = -RT_EINVAL; + goto _fail; + } + + /* Sanity check group index shift */ + i = global->group_index_bits + global->group_index_shift - 1; + if (i >= RT_BITS_PER_LONG) + { + LOG_E("%s: %s index %s too big", "Group", "shift", rt_ofw_node_full_name(np)); + + err = -RT_EINVAL; + goto _fail; + } + + /* Sanity check number of interrupt identities */ + if (global->nr_ids < IMSIC_MIN_ID || + global->nr_ids >= IMSIC_MAX_ID || + (global->nr_ids & IMSIC_MIN_ID) != IMSIC_MIN_ID) + { + LOG_E("%s: Invalid number of %sinterrupt identities", "", rt_ofw_node_full_name(np)); + + err = -RT_EINVAL; + goto _fail; + } + + /* Sanity check number of guest interrupt identities */ + if (global->nr_guest_ids < IMSIC_MIN_ID || + global->nr_guest_ids >= IMSIC_MAX_ID || + (global->nr_guest_ids & IMSIC_MIN_ID) != IMSIC_MIN_ID) + { + LOG_E("%s: Invalid number of %sinterrupt identities", "guest ", rt_ofw_node_full_name(np)); + + err = -RT_EINVAL; + goto _fail; + } + + mmio_count = rt_ofw_get_address_count(np); + mmio = rt_calloc(mmio_count, sizeof(*mmio)); + + if (!mmio) + { + err = -RT_ENOMEM; + goto _fail; + } + + for (int i = 0; i < mmio_count; ++i) + { + rt_ubase_t base_addr; + + if ((err = rt_ofw_get_address(np, 0, &mmio[i].addr, &mmio[i].size))) + { + goto _fail; + } + + if (i == 0) + { + global->base_addr = mmio[i].addr; + global->base_addr &= ~(RT_BIT(global->guest_index_bits + + global->hart_index_bits + IMSIC_MMIO_PAGE_SHIFT) - 1); + global->base_addr &= ~((RT_BIT(global->group_index_bits) - 1) << + global->group_index_shift); + } + + base_addr = mmio[i].addr; + base_addr &= ~(RT_BIT(global->guest_index_bits + + global->hart_index_bits + IMSIC_MMIO_PAGE_SHIFT) - 1); + base_addr &= ~((RT_BIT(global->group_index_bits) - 1) << + global->group_index_shift); + + if (base_addr != global->base_addr) + { + err = -RT_EINVAL; + goto _fail; + } + + mmio[i].mapped = rt_ioremap((void *)mmio[i].addr, mmio[i].size); + + if (!mmio[i].mapped) + { + err = -RT_EIO; + goto _fail; + } + } + + for (int i = 0; i < irq_count; ++i) + { + int cpu_id; + int index, reloff; + rt_ubase_t hartid; + struct rt_ofw_node *cpu_np; + struct rt_ofw_cell_args arg; + struct imsic_local_config *local; + + if (rt_ofw_parse_irq_cells(np, i, &arg)) + { + continue; + } + + cpu_np = rt_ofw_get_parent(arg.data); + hartid = rt_ofw_get_cpu_hwid(cpu_np, 0); + rt_ofw_node_put(cpu_np); + + cpu_id = riscv_hartid_to_cpu_id(hartid); + + if (cpu_id >= RT_CPUS_NR) + { + continue; + } + + /* Find MMIO location of MSI page */ + index = mmio_count; + reloff = i * RT_BIT(global->guest_index_bits) * IMSIC_MMIO_PAGE_SZ; + for (int j = 0; mmio_count; ++j) + { + if (reloff < mmio[j].size) + { + index = j; + break; + } + + /* + * MMIO region size may not be aligned to + * RT_BIT(global->guest_index_bits) * IMSIC_MMIO_PAGE_SZ + * if holes are present. + */ + reloff -= RT_ALIGN(mmio[j].size, RT_BIT(global->guest_index_bits) * IMSIC_MMIO_PAGE_SZ); + } + + if (index >= mmio_count) + { + LOG_E("%s: MMIO not found for parent IRQ[%d]", rt_ofw_node_full_name(np), i); + continue; + } + + local = &global->local[cpu_id]; + local->msi_phy = mmio[index].addr + reloff; + local->msi = mmio[index].mapped + reloff; + } + rt_free(mmio); + + if (!(imsic->vectors = rt_calloc(1, RT_BITMAP_LEN(global->nr_ids) * sizeof(rt_bitmap_t)))) + { + err = -RT_ENOMEM; + goto _fail; + } + + rt_spin_lock_init(&imsic->lock); + + imsic->np = np; + imsic->parent_irq = -1; + imsic->init_cpu_id = rt_hw_cpu_id(); + + imsic->parent.priv_data = imsic; + imsic->parent.ops = &riscv_imsic_ops; + + imsic->parent_ic = rt_ofw_data(ic_np); + + rt_ofw_data(np) = &imsic->parent; + rt_ofw_node_set_flag(np, RT_OFW_F_READLY); + + rt_pic_linear_irq(&imsic->parent, global->nr_ids); + + imsic->msi_id_start = IMSIC_ID_START; + +#if defined(RT_USING_SMP) && defined(ARCH_RISCV_M_MODE) + if (!riscv_aclint_ipi_base) + { + for (int ipi = 0; ipi < RT_MAX_IPI; ++ipi) + { + int hwirq = IMSIC_IPI_ID + ipi; + + rt_bitmap_set_bit(imsic->vectors, hwirq); + rt_pic_config_ipi(&imsic->parent, ipi, hwirq); + } + + imsic->msi_id_start += RT_MAX_IPI; + } +#endif /* RT_USING_SMP && ARCH_RISCV_M_MODE */ + + rt_pic_user_extends(&imsic->parent); + + return RT_EOK; + +_fail: + if (mmio) + { + for (int i = 0; mmio[i].mapped && i < mmio_count; ++i) + { + rt_iounmap(mmio[i].mapped); + } + + rt_free(mmio); + } + + rt_free(imsic); + + return err; +} diff --git a/libcpu/risc-v/common64/pic/pic-riscv-imsic.h b/libcpu/risc-v/common64/pic/pic-riscv-imsic.h new file mode 100755 index 00000000000..523526c70a1 --- /dev/null +++ b/libcpu/risc-v/common64/pic/pic-riscv-imsic.h @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-01-30 GuEe-GUI first version + */ + +#ifndef __PIC_RISCV_IMSIC_H__ +#define __PIC_RISCV_IMSIC_H__ + +#include +#include +#include + +#define IMSIC_MMIO_PAGE_SHIFT 12 +#define IMSIC_MMIO_PAGE_SZ RT_BIT(IMSIC_MMIO_PAGE_SHIFT) +#define IMSIC_MMIO_PAGE_LE 0x00 +#define IMSIC_MMIO_PAGE_BE 0x04 + +#define IMSIC_MIN_ID 63 +#define IMSIC_MAX_ID 2048 + +#define IMSIC_EIDELIVERY 0x70 + +#define IMSIC_EITHRESHOLD 0x72 + +#define IMSIC_EIP0 0x80 +#define IMSIC_EIP63 0xbf +#define IMSIC_EIPx_BITS 32 + +#define IMSIC_EIE0 0xc0 +#define IMSIC_EIE63 0xff +#define IMSIC_EIEx_BITS 32 + +#define IMSIC_FIRST IMSIC_EIDELIVERY +#define IMSIC_LAST IMSIC_EIE63 + +#define IMSIC_MMIO_SETIPNUM_LE 0x00 +#define IMSIC_MMIO_SETIPNUM_BE 0x04 + +struct imsic_local_config +{ + void *msi; + rt_uint64_t msi_phy; +}; + +struct imsic_global_config +{ + /* + * MSI Target Address Scheme + * + * XLEN-1 12 0 + * | | | + * ------------------------------------------------------------- + * |xxxxxx|Group Index|xxxxxxxxxxx|HART Index|Guest Index| 0 | + * ------------------------------------------------------------- + */ + + /* Bits representing Guest index, HART index, and Group index */ + rt_uint32_t guest_index_bits; + rt_uint32_t hart_index_bits; + rt_uint32_t group_index_bits; + rt_uint32_t group_index_shift; + + /* Global base address matching all target MSI addresses */ + rt_ubase_t base_addr; + + /* Number of interrupt identities */ + rt_uint32_t nr_ids; + + /* Number of guest interrupt identities */ + rt_uint32_t nr_guest_ids; + + /* IMSIC addresses */ + struct imsic_local_config local[RT_CPUS_NR]; +}; + +const struct imsic_global_config *imsic_global_config_read(struct rt_ofw_node *np); +rt_err_t imsic_ofw_probe(struct rt_ofw_node *np, + struct rt_ofw_node *ic_np, const struct rt_ofw_node_id *ic_id); + +#endif /* __PIC_RISCV_IMSIC_H__ */ diff --git a/libcpu/risc-v/common64/pic/pic-riscv-intc-andes.h b/libcpu/risc-v/common64/pic/pic-riscv-intc-andes.h new file mode 100755 index 00000000000..e9358510db6 --- /dev/null +++ b/libcpu/risc-v/common64/pic/pic-riscv-intc-andes.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-01-30 GuEe-GUI first version + */ + +#ifndef __PIC_RISCV_INTC_ANDES_H__ +#define __PIC_RISCV_INTC_ANDES_H__ + +/* Andes PMU irq number */ +#define ANDES_RV_IRQ_PMOVI 18 +#define ANDES_RV_IRQ_LAST ANDES_RV_IRQ_PMOVI +#define ANDES_SLI_CAUSE_BASE 256 + +/* Andes PMU related registers */ +#define ANDES_CSR_SLIE 0x9c4 +#define ANDES_CSR_SLIP 0x9c5 +#define ANDES_CSR_SCOUNTEROF 0x9d4 + +#endif /* __PIC_RISCV_INTC_ANDES_H__ */ diff --git a/libcpu/risc-v/common64/pic/pic-riscv-intc.c b/libcpu/risc-v/common64/pic/pic-riscv-intc.c new file mode 100755 index 00000000000..5b4bad15ab3 --- /dev/null +++ b/libcpu/risc-v/common64/pic/pic-riscv-intc.c @@ -0,0 +1,490 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-01-30 GuEe-GUI first version + */ + +#include +#include +#include +#include + +#define DBG_TAG "pic.riscv" +#define DBG_LVL DBG_INFO +#include + +#include +#include +#include +#include +#include + +#include "pic-riscv-intc-andes.h" + +#include "pic-riscv-clint.c" + +struct riscv_intc +{ +#ifndef PIC_DISABLE_DM + struct rt_pic parent; +#endif /* !PIC_DISABLE_DM */ + + rt_uint32_t irqs_base; + rt_uint32_t irqs_count; + rt_uint32_t irqs_def_count; + +#ifdef RT_USING_SMP + rt_uint32_t ipi_base; + rt_atomic_t ipi_enable[RT_CPUS_NR]; + rt_atomic_t ipi_pending[RT_CPUS_NR]; +#endif +}; + +static struct riscv_intc system_rv_intc; + +rt_inline void riscv_intc_irq_mask_raw(struct riscv_intc *rv_intc, int hwirq) +{ +#ifdef RT_USING_SMP + if (hwirq >= rv_intc->ipi_base) + { + rt_atomic_t *ipi_enable = &rv_intc->ipi_enable[rt_hw_cpu_id()]; + + rt_atomic_and(ipi_enable, ~BIT(hwirq - rv_intc->ipi_base)); + + if (rt_atomic_load(ipi_enable) != 0) + { + return; + } + + hwirq = RV_IRQ_SOFT; + } +#endif /* RT_USING_SMP */ + +#if BITS_PER_LONG == 32 + if (hwirq >= BITS_PER_LONG) + { + csr_clear(CSR_IEH, BIT(hwirq - BITS_PER_LONG)); + } + else +#endif /* BITS_PER_LONG == 32 */ + { + csr_clear(CSR_IE, BIT(hwirq)); + } +} + +rt_inline void riscv_intc_irq_unmask_raw(struct riscv_intc *rv_intc, int hwirq) +{ +#ifdef RT_USING_SMP + if (hwirq >= rv_intc->ipi_base) + { + rt_atomic_t *ipi_enable = &rv_intc->ipi_enable[rt_hw_cpu_id()]; + + rt_atomic_or(ipi_enable, BIT(hwirq - rv_intc->ipi_base)); + hwirq = RV_IRQ_SOFT; + } +#endif /* RT_USING_SMP */ + +#if BITS_PER_LONG == 32 + if (hwirq >= BITS_PER_LONG) + { + csr_set(CSR_IEH, BIT(hwirq - BITS_PER_LONG)); + } + else +#endif /* BITS_PER_LONG == 32 */ + { + csr_set(CSR_IE, BIT(hwirq)); + } +} + +rt_inline void andes_intc_irq_mask_raw(struct riscv_intc *rv_intc, int hwirq) +{ + /* + * Andes specific S-mode local interrupt causes (hwirq) + * are defined as (256 + n) and controlled by n-th bit of SLIE. + */ + rt_uint32_t mask; + +#ifdef RT_USING_SMP + if (hwirq >= rv_intc->ipi_base) + { + rt_atomic_t *ipi_enable = &rv_intc->ipi_enable[rt_hw_cpu_id()]; + + rt_atomic_and(ipi_enable, ~BIT(hwirq - rv_intc->ipi_base)); + + if (rt_atomic_load(ipi_enable) != 0) + { + return; + } + + hwirq = RV_IRQ_SOFT; + } +#endif /* RT_USING_SMP */ + + mask = BIT(hwirq % BITS_PER_LONG); + + if (hwirq < ANDES_SLI_CAUSE_BASE) + { + csr_clear(CSR_IE, mask); + } + else + { + csr_clear(ANDES_CSR_SLIE, mask); + } +} + +rt_inline void andes_intc_irq_unmask_raw(struct riscv_intc *rv_intc, int hwirq) +{ + rt_uint32_t mask; + +#ifdef RT_USING_SMP + if (hwirq >= rv_intc->ipi_base) + { + rt_atomic_t *ipi_enable = &rv_intc->ipi_enable[rt_hw_cpu_id()]; + + rt_atomic_or(ipi_enable, BIT(hwirq - rv_intc->ipi_base)); + hwirq = RV_IRQ_SOFT; + } +#endif /* RT_USING_SMP */ + + mask = BIT(hwirq % BITS_PER_LONG); + + if (hwirq < ANDES_SLI_CAUSE_BASE) + { + csr_set(CSR_IE, mask); + } + else + { + csr_set(ANDES_CSR_SLIE, mask); + } +} + +rt_inline void riscv_intc_irq_send_ipi_raw(struct riscv_intc *rv_intc, int ipi, rt_bitmap_t *cpumask) +{ +#if defined(RT_USING_SMP) || defined(ARCH_RISCV_M_MODE) + int cpu; + + rt_bitmap_for_each_set_bit(cpumask, cpu, RT_CPUS_NR) + { + #ifdef RT_USING_SMP + rt_atomic_or(&rv_intc->ipi_pending[cpu], BIT(ipi)); + #endif + + #ifndef ARCH_RISCV_M_MODE + if (riscv_aclint_ipi_base) + #endif + { + clint_send_ipi(cpu); + } + } +#endif /* RT_USING_SMP || ARCH_RISCV_M_MODE */ + +#ifndef ARCH_RISCV_M_MODE + if (!riscv_aclint_ipi_base) + { + sbi_send_ipi((const unsigned long *)cpumask); + } +#endif /* !ARCH_RISCV_M_MODE */ +} + +rt_inline rt_bool_t riscv_intc_common_handler_raw(struct riscv_intc *rv_intc, int hwirq, + rt_bool_t (*callback)(int hwirq, void *data), void *data) +{ +#ifdef RT_USING_SMP + if (hwirq != RV_IRQ_SOFT) +#endif + { + if (hwirq != RV_IRQ_TIMER) + { + return callback(hwirq, data); + } + else + { + riscv_timer_isr(); + return RT_TRUE; + } + } +#ifdef RT_USING_SMP + else + { + int ipi; + rt_bool_t res; + rt_bitmap_t pending; + + #ifndef ARCH_RISCV_M_MODE + if (riscv_aclint_ipi_base) + #endif + { + clint_clear_ipi(); + } + #ifndef ARCH_RISCV_M_MODE + else + { + csr_clear(CSR_IP, IE_SIE); + } + #endif + + pending = rt_atomic_exchange(&rv_intc->ipi_pending[rt_hw_cpu_id()], 0); + + rt_bitmap_for_each_set_bit(&pending, ipi, RT_MAX_IPI) + { + res = callback(rv_intc->ipi_base + ipi, data); + } + + return res; + } +#endif /* RT_USING_SMP */ +} + +rt_inline rt_bool_t riscv_intc_handler_raw(struct riscv_intc *rv_intc, rt_ubase_t scause, + rt_bool_t (*callback)(int hwirq, void *data), void *data) +{ + return riscv_intc_common_handler_raw(rv_intc, scause & ~CAUSE_IRQ_FLAG, callback, data); +} + +rt_inline rt_bool_t riscv_intc_aia_handler_raw(struct riscv_intc *rv_intc, + rt_bool_t (*callback)(int hwirq, void *data), void *data) +{ + rt_ubase_t topi; + rt_bool_t res = RT_FALSE; + + while ((topi = csr_read(CSR_TOPI) >> TOPI_IID_SHIFT)) + { + res = riscv_intc_common_handler_raw(rv_intc, topi, callback, data); + } + + return res; +} + +static void riscv_intc_common_init(struct riscv_intc *rv_intc, rt_bool_t is_andes) +{ + rt_memset(rv_intc, 0, sizeof(*rv_intc)); + rv_intc->irqs_def_count = RT_BITS_PER_LONG; + + if (is_andes) + { + rv_intc->irqs_base = ANDES_SLI_CAUSE_BASE; + rv_intc->irqs_count = ANDES_RV_IRQ_LAST; + } + else + { + rv_intc->irqs_base = BITS_PER_LONG; + rv_intc->irqs_count = 0; + } + + if (riscv_isa_ext_test(SxAIA)) + { + rv_intc->irqs_def_count = 64; + } + else + { + rv_intc->irqs_def_count = IRQ_LOCAL_MAX; + } + +#ifdef RT_USING_SMP + rv_intc->irqs_def_count += RT_MAX_IPI; +#endif +} + +#ifndef PIC_DISABLE_DM +static void riscv_intc_irq_mask(struct rt_pic_irq *pirq) +{ + RT_IRQ_AFFINITY_CLEAR(pirq->affinity, rt_hw_cpu_id()); + riscv_intc_irq_mask_raw(pirq->pic->priv_data, pirq->hwirq); +} + +static void riscv_intc_irq_unmask(struct rt_pic_irq *pirq) +{ + RT_IRQ_AFFINITY_SET(pirq->affinity, rt_hw_cpu_id()); + riscv_intc_irq_unmask_raw(pirq->pic->priv_data, pirq->hwirq); +} + +static void andes_intc_irq_mask(struct rt_pic_irq *pirq) +{ + andes_intc_irq_mask_raw(pirq->pic->priv_data, pirq->hwirq); + RT_IRQ_AFFINITY_CLEAR(pirq->affinity, rt_hw_cpu_id()); +} + +static void andes_intc_irq_unmask(struct rt_pic_irq *pirq) +{ + RT_IRQ_AFFINITY_SET(pirq->affinity, rt_hw_cpu_id()); + andes_intc_irq_unmask_raw(pirq->pic->priv_data, pirq->hwirq); +} + +static void riscv_intc_irq_send_ipi(struct rt_pic_irq *pirq, rt_bitmap_t *cpumask) +{ + struct riscv_intc *rv_intc = pirq->pic->priv_data; + + riscv_intc_irq_send_ipi_raw(rv_intc, pirq->hwirq, cpumask); +} + +static int riscv_intc_irq_map(struct rt_pic *pic, int hwirq, rt_uint32_t mode) +{ + int irq, irq_index = -1; + struct riscv_intc *rv_intc = rt_container_of(pic, struct riscv_intc, parent); + + if (hwirq >= rv_intc->irqs_def_count && + (hwirq < rv_intc->irqs_base || hwirq >= rv_intc->irqs_base + rv_intc->irqs_count)) + { + return -RT_EINVAL; + } + + if (hwirq < rv_intc->irqs_def_count) + { + irq_index = hwirq; + } + else if (hwirq >= rv_intc->irqs_base && + hwirq < rv_intc->irqs_base + rv_intc->irqs_count) + { + irq_index = rv_intc->irqs_def_count + hwirq - rv_intc->irqs_base; + } + + if (irq_index >= 0) + { + irq = rt_pic_config_irq(pic, hwirq, irq_index); + } + else + { + irq = -1; + } + + return irq; +} + +static rt_err_t riscv_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; + + if (args->args_count == 1) + { + out_pirq->hwirq = args->args[0]; + out_pirq->mode = RT_IRQ_MODE_EDGE_RISING; + } + else + { + err = -RT_EINVAL; + } + + return err; +} + +const static struct rt_pic_ops riscv_intc_ops = +{ + .name = "RISC-V-INTC", + .irq_mask = riscv_intc_irq_mask, + .irq_unmask = riscv_intc_irq_unmask, + .irq_send_ipi = riscv_intc_irq_send_ipi, + .irq_map = riscv_intc_irq_map, + .irq_parse = riscv_intc_irq_parse, +}; + +const static struct rt_pic_ops andes_intc_ops = +{ + .name = "ANDES-INTC", + .irq_mask = andes_intc_irq_mask, + .irq_unmask = andes_intc_irq_unmask, + .irq_send_ipi = riscv_intc_irq_send_ipi, + .irq_map = riscv_intc_irq_map, + .irq_parse = riscv_intc_irq_parse, +}; + +static rt_bool_t riscv_intc_common_handler_isr(int hwirq, void *data) +{ + struct rt_pic_irq *pirq; + struct riscv_intc *rv_intc = data; + +#ifdef RT_USING_SMP + if (hwirq < rv_intc->ipi_base) +#endif + { + pirq = rt_pic_find_irq(&rv_intc->parent, hwirq); + } +#ifdef RT_USING_SMP + else + { + pirq = rt_pic_find_ipi(&rv_intc->parent, hwirq - rv_intc->ipi_base); + } +#endif + + return rt_pic_handle_isr(pirq) == RT_EOK; +} + +static rt_bool_t riscv_intc_handler(void *data) +{ + rt_ubase_t cause; + struct riscv_intc *rv_intc = data; + + cause = csr_read(CSR_CAUSE); + + return riscv_intc_handler_raw(rv_intc, cause, riscv_intc_common_handler_isr, data); +} + +static rt_bool_t riscv_intc_aia_handler(void *data) +{ + struct riscv_intc *rv_intc = data; + + return riscv_intc_aia_handler_raw(rv_intc, riscv_intc_common_handler_isr, data); +} + +static rt_err_t riscv_intc_ofw_init(struct rt_ofw_node *np, const struct rt_ofw_node_id *id) +{ + const struct rt_pic_ops *ops; + struct riscv_intc *rv_intc = &system_rv_intc; + struct rt_ofw_node *pnp = rt_ofw_get_parent(np); + + /* Only boot CPU to init */ + if (rt_ofw_get_cpu_hwid(pnp, 0) != boot_cpu_hartid) + { + rt_ofw_node_put(pnp); + rt_ofw_data(np) = &rv_intc->parent; + + return RT_EOK; + } + rt_ofw_node_put(pnp); + + ops = id->data; + riscv_intc_common_init(rv_intc, ops == &andes_intc_ops); + + riscv_clint_ofw_init(); + + if (riscv_isa_ext_test(SxAIA)) + { + rt_pic_add_traps(riscv_intc_aia_handler, rv_intc); + } + else + { + rt_pic_add_traps(riscv_intc_handler, rv_intc); + } + + rv_intc->parent.priv_data = rv_intc; + rv_intc->parent.ops = ops; + rt_ofw_data(np) = &rv_intc->parent; + + rt_pic_linear_irq(&rv_intc->parent, rv_intc->irqs_def_count + rv_intc->irqs_count); + +#ifdef RT_USING_SMP + rv_intc->ipi_base = rv_intc->parent.irq_nr - RT_MAX_IPI; + + for (int ipi = 0; ipi < RT_MAX_IPI; ++ipi) + { + rt_pic_config_ipi(&rv_intc->parent, ipi, rv_intc->ipi_base + ipi); + } +#endif /* RT_USING_SMP */ + + rt_pic_user_extends(&rv_intc->parent); + + return RT_EOK; +} + +static const struct rt_ofw_node_id riscv_intc_ofw_ids[] = +{ + { .compatible = "riscv,cpu-intc", (void *)&riscv_intc_ops }, + { .compatible = "andestech,cpu-intc", (void *)&andes_intc_ops }, + { /* sentinel */ } +}; +RT_PIC_OFW_DECLARE(riscv_intc, riscv_intc_ofw_ids, riscv_intc_ofw_init); +#endif /* !PIC_DISABLE_DM */ diff --git a/libcpu/risc-v/common64/pic/pic-sifive-plic.c b/libcpu/risc-v/common64/pic/pic-sifive-plic.c new file mode 100755 index 00000000000..bdc37461527 --- /dev/null +++ b/libcpu/risc-v/common64/pic/pic-sifive-plic.c @@ -0,0 +1,460 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-01-30 GuEe-GUI first version + */ + +#include +#include +#include + +#define DBG_TAG "pic.plic" +#define DBG_LVL DBG_INFO +#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 + */ + +#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 + +#ifdef ARCH_RISCV_M_MODE +#define CONTEXT_ENABLE_MODE_OFFSET 0 +#define CONTEXT_BASE_MODE_OFFSET 0 +#else +#define CONTEXT_ENABLE_MODE_OFFSET 0x80 +#define CONTEXT_BASE_MODE_OFFSET 0x1000 +#endif + +/* + * 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 + CONTEXT_ENABLE_MODE_OFFSET) +#define CONTEXT_ENABLE_SIZE(hartid) ((2 * hartid) * 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 + CONTEXT_BASE_MODE_OFFSET) +#define CONTEXT_SIZE(hartid) ((2 * hartid) * 0x1000) +#define CONTEXT_THRESHOLD 0x00 +#define CONTEXT_CLAIM 0x04 + +#define PLIC_DISABLE_THRESHOLD 0x7 +#define PLIC_ENABLE_THRESHOLD 0 + +#define PLIC_HWIRQ_START 1 + +#define PLIC_QUIRK_EDGE_INTERRUPT 0 + +struct plic +{ +#ifndef PIC_DISABLE_DM + struct rt_pic parent; + struct rt_ofw_node *np; + + int parent_irq; +#endif + + void *regs; + + rt_bitmap_t quirks; + + rt_uint32_t irqs_count; +}; + +struct plic_percpu +{ + void *hart_base; + void *enable_base; + + struct rt_spinlock enable_lock; +}; + +static struct plic_percpu plic_percpu[RT_CPUS_NR] rt_align(L1_CACHE_BYTES); + +static void plic_set_threshold(struct plic_percpu *percpu, rt_uint32_t threshold) +{ + /* Priority must be > threshold to trigger an interrupt */ + HWREG32(percpu->hart_base + CONTEXT_THRESHOLD) = threshold; +} + +static void plic_toggle(struct plic_percpu *percpu, int hwirq, rt_bool_t enable) +{ + rt_ubase_t level; + rt_uint32_t *reg, hwirq_mask; + + level = rt_spin_lock_irqsave(&percpu->enable_lock); + + reg = percpu->enable_base + (hwirq / 32) * sizeof(rt_uint32_t); + hwirq_mask = 1 << (hwirq % 32); + + if (enable) + { + HWREG32(reg) |= hwirq_mask; + } + else + { + HWREG32(reg) &= ~hwirq_mask; + } + + rt_spin_unlock_irqrestore(&percpu->enable_lock, level); +} + +static void plic_irq_unmask_raw(int cpu, int hwirq) +{ + struct plic_percpu *percpu = &plic_percpu[cpu]; + + plic_toggle(percpu, hwirq, RT_TRUE); +} + +static void plic_irq_mask_raw(int cpu, int hwirq) +{ + struct plic_percpu *percpu = &plic_percpu[cpu]; + + plic_toggle(percpu, hwirq, RT_FALSE); +} + +static void plic_irq_ack_or_eoi(int cpu, int hwirq) +{ + struct plic_percpu *percpu = &plic_percpu[cpu]; + + HWREG32(percpu->hart_base + CONTEXT_CLAIM) = hwirq; +} + +static rt_bool_t plic_handler_isr_raw(struct plic *plic, + rt_bool_t (*callback)(int hwirq, void *data), void *data) +{ + int hwirq; + void *claim; + rt_bool_t res = RT_FALSE; + struct plic_percpu *percpu = &plic_percpu[rt_hw_cpu_id()]; + + claim = percpu->hart_base + CONTEXT_CLAIM; + + while ((hwirq = HWREG32(claim))) + { + res = callback(hwirq, data); + } + + return res; +} + +static void plic_percpu_common_init(struct plic *plic) +{ + struct plic_percpu *percpu = &plic_percpu[rt_hw_cpu_id()]; + + plic_set_threshold(percpu, PLIC_ENABLE_THRESHOLD); +} + +static struct plic *plic_common_init(rt_ubase_t plic_base, rt_size_t plic_size, + rt_size_t ndev, rt_bitmap_t quirks) +{ + struct plic *plic = rt_calloc(1, sizeof(*plic)); + + if (!plic) + { + rt_set_errno(-RT_ENOMEM); + return RT_NULL; + } + + plic->regs = rt_ioremap((void *)plic_base, plic_size); + + if (!plic->regs) + { + rt_set_errno(-RT_EIO); + + rt_free(plic); + return RT_NULL; + } + + plic->quirks = quirks; + plic->irqs_count = ndev; + + for (int cpu = 0, hartid; cpu < RT_CPUS_NR; ++cpu) + { + struct plic_percpu *percpu = &plic_percpu[cpu]; + + #ifndef RT_USING_SMP + hartid = boot_cpu_hartid; + #else + hartid = riscv_cpu_id_to_hartid(cpu); + #endif + + percpu->hart_base = plic->regs + CONTEXT_BASE + CONTEXT_SIZE(hartid); + percpu->enable_base = plic->regs + CONTEXT_ENABLE_BASE + CONTEXT_ENABLE_SIZE(hartid); + + rt_spin_lock_init(&percpu->enable_lock); + } + + /* Set default priority */ + for (int hwirq = PLIC_HWIRQ_START; hwirq <= plic->irqs_count; ++hwirq) + { + HWREG32(plic->regs + PRIORITY_BASE + hwirq * PRIORITY_PER_ID) = PLIC_ENABLE_THRESHOLD + 1; + } + + return plic; +} + +#ifndef PIC_DISABLE_DM +static void plic_handler(int hwirq, void *data); + +static rt_err_t plic_irq_init(struct rt_pic *pic) +{ + struct plic *plic = pic->priv_data; + + if (plic->parent_irq < 0) + { + struct rt_ofw_cell_args irq_args; + + if (rt_ofw_parse_irq_cells(plic->np, 0, &irq_args)) + { + LOG_E("%s: Parent IRQ is not available", rt_ofw_node_full_name(plic->np)); + goto _percpu_init; + } + + /* Create mapping */ + irq_args.args[0] = RV_IRQ_EXT; + + plic->parent_irq = rt_ofw_map_irq(&irq_args); + rt_hw_interrupt_install(plic->parent_irq, plic_handler, plic, "PLIC"); + } + + rt_hw_interrupt_umask(plic->parent_irq); + +_percpu_init: + plic_percpu_common_init(plic); + + return RT_EOK; +} + +static void plic_irq_ack(struct rt_pic_irq *pirq) +{ + if (pirq->mode == RT_IRQ_MODE_EDGE_RISING) + { + plic_irq_ack_or_eoi(rt_hw_cpu_id(), pirq->hwirq); + } +} + +static void plic_irq_unmask(struct rt_pic_irq *pirq) +{ + int cpu, count = 0; + + rt_bitmap_for_each_set_bit(pirq->affinity, cpu, RT_CPUS_NR) + { + plic_irq_unmask_raw(cpu, pirq->hwirq); + ++count; + } + + if (!count) + { + int boot_cpu_id = riscv_hartid_to_cpu_id(boot_cpu_hartid); + + RT_IRQ_AFFINITY_SET(pirq->affinity, boot_cpu_id); + plic_irq_unmask_raw(boot_cpu_id, pirq->hwirq); + } +} + +static void plic_irq_mask(struct rt_pic_irq *pirq) +{ + int cpu; + + rt_bitmap_for_each_set_bit(pirq->affinity, cpu, RT_CPUS_NR) + { + plic_irq_mask_raw(cpu, pirq->hwirq); + } +} + +static void plic_irq_eoi(struct rt_pic_irq *pirq) +{ + if (pirq->mode == RT_IRQ_MODE_LEVEL_HIGH) + { + plic_irq_ack_or_eoi(rt_hw_cpu_id(), pirq->hwirq); + } +} + +static rt_err_t plic_irq_set_priority(struct rt_pic_irq *pirq, rt_uint32_t priority) +{ + struct plic *plic = pirq->pic->priv_data; + + priority = rt_min_t(rt_uint32_t, priority, PLIC_DISABLE_THRESHOLD); + + HWREG32(plic->regs + PRIORITY_BASE + pirq->hwirq * PRIORITY_PER_ID) = priority; + + return RT_EOK; +} + +static rt_err_t plic_irq_set_affinity(struct rt_pic_irq *pirq, rt_bitmap_t *affinity) +{ + int cpu; + + rt_bitmap_for_each_clear_bit(affinity, cpu, RT_CPUS_NR) + { + plic_toggle(&plic_percpu[cpu], pirq->hwirq, RT_FALSE); + } + + rt_bitmap_for_each_set_bit(affinity, cpu, RT_CPUS_NR) + { + plic_toggle(&plic_percpu[cpu], pirq->hwirq, RT_TRUE); + } + + return RT_EOK; +} + +static rt_err_t plic_irq_set_triger_mode(struct rt_pic_irq *pirq, rt_uint32_t mode) +{ + struct plic *plic = pirq->pic->priv_data; + + if (rt_bitmap_test_bit(&plic->quirks, PLIC_QUIRK_EDGE_INTERRUPT)) + { + return -RT_ENOSYS; + } + + if (mode == RT_IRQ_MODE_EDGE_RISING || mode == RT_IRQ_MODE_LEVEL_HIGH) + { + return RT_EOK; + } + + return -RT_ENOSYS; +} + +static int plic_irq_map(struct rt_pic *pic, int hwirq, rt_uint32_t mode) +{ + struct rt_pic_irq *pirq = rt_pic_find_irq(pic, hwirq - PLIC_HWIRQ_START); + + pirq->mode = mode; + + return rt_pic_config_irq(pic, hwirq - PLIC_HWIRQ_START, hwirq); +} + +static rt_err_t plic_irq_parse(struct rt_pic *pic, struct rt_ofw_cell_args *args, struct rt_pic_irq *out_pirq) +{ + struct plic *plic = pic->priv_data; + + out_pirq->hwirq = args->args[0]; + + if (rt_bitmap_test_bit(&plic->quirks, PLIC_QUIRK_EDGE_INTERRUPT)) + { + out_pirq->mode = args->args[1]; + } + else + { + out_pirq->mode = RT_IRQ_MODE_LEVEL_HIGH; + } + + return RT_EOK; +} + +const static struct rt_pic_ops plic_ops = +{ + .name = "SiFive-PLIC", + .irq_init = plic_irq_init, + .irq_ack = plic_irq_ack, + .irq_mask = plic_irq_mask, + .irq_unmask = plic_irq_unmask, + .irq_eoi = plic_irq_eoi, + .irq_set_priority = plic_irq_set_priority, + .irq_set_affinity = plic_irq_set_affinity, + .irq_set_triger_mode = plic_irq_set_triger_mode, + .irq_map = plic_irq_map, + .irq_parse = plic_irq_parse, +}; + +static rt_bool_t plic_common_handler_isr(int hwirq, void *data) +{ + rt_bool_t res; + struct plic *plic = data; + struct rt_pic_irq *pirq = rt_pic_find_irq(&plic->parent, hwirq - PLIC_HWIRQ_START); + + plic_irq_ack(pirq); + + res = rt_pic_handle_isr(pirq); + + plic_irq_eoi(pirq); + + return res; +} + +static void plic_handler(int hwirq, void *data) +{ + struct plic *plic = data; + + plic_handler_isr_raw(plic, plic_common_handler_isr, plic); +} + +static rt_err_t plic_ofw_init(struct rt_ofw_node *np, const struct rt_ofw_node_id *id) +{ + rt_err_t err; + rt_uint32_t irqs_count; + rt_uint64_t plic_base, plic_size; + rt_bitmap_t quirks; + struct plic *plic; + + if ((err = rt_ofw_get_address(np, 0, &plic_base, &plic_size))) + { + return err; + } + + if ((err = rt_ofw_prop_read_u32(np, "riscv,ndev", &irqs_count))) + { + return err; + } + + quirks = id->data ? *(rt_ubase_t *)id->data : 0; + + if (!(plic = plic_common_init(plic_base, plic_size, irqs_count, quirks))) + { + return rt_get_errno(); + } + + plic->np = np; + plic->parent_irq = -1; + + plic->parent.priv_data = plic; + plic->parent.ops = &plic_ops; + rt_ofw_data(np) = &plic->parent; + + rt_pic_linear_irq(&plic->parent, plic->irqs_count); + + rt_pic_user_extends(&plic->parent); + + return RT_EOK; +} + +static rt_bitmap_t andestech_nceplic100_quirk_edge = RT_BIT(PLIC_QUIRK_EDGE_INTERRUPT); +static rt_bitmap_t thead_c900_quirk_edge = RT_BIT(PLIC_QUIRK_EDGE_INTERRUPT); + +static const struct rt_ofw_node_id plic_ofw_ids[] = +{ + { .compatible = "sifive,plic-1.0.0" }, + { .compatible = "riscv,plic0" }, + { .compatible = "andestech,nceplic100", .data = &andestech_nceplic100_quirk_edge }, + { .compatible = "thead,c900-plic", .data = &thead_c900_quirk_edge }, + { /* sentinel */ } +}; +RT_PIC_OFW_DECLARE(plic, plic_ofw_ids, plic_ofw_init); +#endif /* PIC_DISABLE_DM */ diff --git a/libcpu/risc-v/common64/sbi.c b/libcpu/risc-v/common64/sbi.c index b2b1e8e7881..e676023f73b 100644 --- a/libcpu/risc-v/common64/sbi.c +++ b/libcpu/risc-v/common64/sbi.c @@ -35,9 +35,9 @@ * SUCH DAMAGE. */ -#include "sbi.h" #include -#include + +#include /* SBI Implementation-Specific Definitions */ #define OPENSBI_VERSION_MAJOR_OFFSET 16 @@ -47,9 +47,10 @@ unsigned long sbi_spec_version; unsigned long sbi_impl_id; unsigned long sbi_impl_version; -static bool has_time_extension = false; -static bool has_ipi_extension = false; -static bool has_rfnc_extension = false; +bool has_time_extension = false; +bool has_ipi_extension = false; +bool has_rfnc_extension = false; +bool has_srst_extension = false; static struct sbi_ret sbi_get_spec_version(void) { @@ -225,6 +226,21 @@ int sbi_hsm_hart_status(unsigned long hart) return (ret.error != 0 ? (int)ret.error : (int)ret.value); } +static void sbi_srst_reset(unsigned long type, unsigned long reason) +{ + SBI_CALL2(SBI_EXT_ID_SRST, SBI_SRST_FID_RESET, type, reason); +} + +void sbi_srst_reboot(unsigned long reset_type) +{ + sbi_srst_reset(reset_type, SBI_SRST_RESET_REASON_NONE); +} + +void sbi_srst_power_off(void) +{ + sbi_srst_reset(SBI_SRST_RESET_TYPE_SHUTDOWN, SBI_SRST_RESET_REASON_NONE); +} + void sbi_init(void) { struct sbi_ret sret; @@ -253,12 +269,6 @@ void sbi_init(void) has_ipi_extension = true; if (sbi_probe_extension(SBI_EXT_ID_RFNC) != 0) has_rfnc_extension = true; -} - -void rt_hw_console_output(const char *str) -{ - while (*str) - { - sbi_console_putchar(*str++); - } + if (sbi_probe_extension(SBI_EXT_ID_SRST) != 0) + has_srst_extension = true; } diff --git a/libcpu/risc-v/common64/serial/Kconfig b/libcpu/risc-v/common64/serial/Kconfig new file mode 100755 index 00000000000..1ef9725983d --- /dev/null +++ b/libcpu/risc-v/common64/serial/Kconfig @@ -0,0 +1,5 @@ +config RT_SERIAL_EARLY_SBI + bool "Early SBI" + depends on RT_USING_SERIAL + depends on !ARCH_RISCV_M_MODE + default y diff --git a/libcpu/risc-v/common64/serial/SConscript b/libcpu/risc-v/common64/serial/SConscript new file mode 100755 index 00000000000..675b16a71f8 --- /dev/null +++ b/libcpu/risc-v/common64/serial/SConscript @@ -0,0 +1,17 @@ +from building import * + +group = [] + +if not GetDepend(['RT_USING_SERIAL']): + Return('group') + +src = [] +cwd = GetCurrentDir() +CPPPATH = [cwd] + +if GetDepend(['RT_SERIAL_EARLY_SBI']): + src += ['serial-early-sbi.c'] + +group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') diff --git a/libcpu/risc-v/common64/serial/serial-early-sbi.c b/libcpu/risc-v/common64/serial/serial-early-sbi.c new file mode 100755 index 00000000000..afbe8d39ab2 --- /dev/null +++ b/libcpu/risc-v/common64/serial/serial-early-sbi.c @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-01-30 GuEe-GUI first version + */ + +#include + +#include + +static void sbi_early_console_putchar(void *data, char c) +{ + sbi_console_putchar(c); +} + +static rt_err_t sbi_early_setup(struct rt_fdt_earlycon *con, const char *options) +{ + con->console_putc = sbi_early_console_putchar; + + return RT_EOK; +} +RT_FDT_EARLYCON_EXPORT(sbi, "sbi", "riscv,sbi-console", sbi_early_setup); diff --git a/libcpu/risc-v/common64/setup.c b/libcpu/risc-v/common64/setup.c new file mode 100755 index 00000000000..18f24140161 --- /dev/null +++ b/libcpu/risc-v/common64/setup.c @@ -0,0 +1,702 @@ +/* + * Copyright (c) 2006-2025, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2025-04-20 GuEe-GUI the first version + */ + +#include +#include + +#if __riscv_xlen == 128 +#define DBG_TAG "cpu.rv128" +#elif __riscv_xlen == 64 +#define DBG_TAG "cpu.rv64" +#elif __riscv_xlen == 32 +#define DBG_TAG "cpu.rv32" +#else +#error "Unexpected __riscv_xlen" +#endif +#define DBG_LVL DBG_INFO +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef RT_USING_LWP +#include +#include +#endif + +#ifndef IOREMAP_SIZE +#define IOREMAP_SIZE 0x20000000 +#endif + +extern rt_ubase_t _start, _end; +extern void rt_hw_builtin_fdt(); +extern size_t MMUTable[]; + +static void *fdt_ptr = RT_NULL; +static rt_size_t fdt_size = 0; + +static rt_ubase_t boot_cpu_hartid = 0; + +#ifdef RT_USING_SMP +static rt_uint32_t cpu_boot_delay_us = 0; + +extern struct cpu_ops_t cpu_sbi_ops; +extern struct cpu_ops_t cpu_spinwait_ops; + +static struct cpu_ops_t *cpu_ops = &cpu_spinwait_ops; +static struct rt_ofw_node *cpu_np[RT_CPUS_NR] = { }; +#endif /* RT_USING_SMP */ + +#if __riscv_xlen == 32 +rt_weak rt_uint8_t rt_hw_arch_vaddr_width = 32; +#else +rt_weak rt_uint8_t rt_hw_arch_vaddr_width = 39; +#endif + +static struct { rt_uint8_t size; const char name[11]; } mmu_type[] = +{ + { 0, "riscv,none" }, +#if __riscv_xlen == 32 + { 32, "riscv,sv32" }, +#else + { 39, "riscv,sv39" }, + { 48, "riscv,sv48" }, + { 57, "riscv,sv57" }, + { 64, "riscv,sv64" }, +#endif +}; + +extern void riscv_timer_us_delay(rt_uint32_t us); +extern void riscv_timer_set_frequency(rt_uint32_t freq); + +rt_weak void rt_hw_fdt_vendor_install_early(void *fdt) +{ + RT_UNUSED(fdt); +} + +void rt_hw_fdt_install_early(void *fdt) +{ + int cpu_id = 0, cpus, cpu, size; + +#ifndef RT_USING_BUILTIN_FDT + if (fdt != RT_NULL && !fdt_check_header(fdt)) + { + fdt_ptr = fdt; + fdt_size = fdt_totalsize(fdt); + } +#else + fdt = &rt_hw_builtin_fdt; +#endif + + rt_hw_fdt_vendor_install_early(fdt); + + rt_hw_arch_vaddr_width = ARCH_ADDRESS_WIDTH_BITS; + + cpus = fdt_path_offset(fdt, "/cpus"); + + fdt_for_each_subnode(cpu, fdt, cpus) + { + rt_uint32_t value; + const char *name = fdt_getprop(fdt, cpu, "mmu-type", RT_NULL); + + if (!name || cpu_id >= RT_CPUS_NR) + { + break; + } + + fdt_getprop_u32(fdt, cpu, "reg", &value, RT_NULL); + + if (value == boot_cpu_hartid) + { + if (!fdt_getprop_u32(fdt, cpu, "riscv,cbom-block-size", &value, RT_NULL) && value > 0) + { + riscv_isa_ext_set(ZICBOM); + riscv_cache_set_cbom_block_size(value); + } + } + + size = rt_hw_arch_vaddr_width; + + for (int i = 0; i < RT_ARRAY_SIZE(mmu_type); ++i) + { + rt_typeof(mmu_type[0]) *type = &mmu_type[i]; + + if (!rt_strcmp(name, type->name)) + { + size = type->size; + break; + } + } + + if (size < rt_hw_arch_vaddr_width) + { + rt_hw_arch_vaddr_width = size; + } + ++cpu_id; + } +} + +rt_weak void rt_hw_idle_wfi(void) +{ + rt_hw_wfi(); +} + +static struct { const char *name; rt_uint8_t size; } isa_ext[] = +{ +#define DECLARE_ISA(isa, name) [RISCV_ISA_EXT_##isa] = { name, sizeof(name) - 1 }, + DECLARE_ISA(I, "i") + DECLARE_ISA(M, "m") + DECLARE_ISA(A, "a") + DECLARE_ISA(F, "f") + DECLARE_ISA(D, "d") + DECLARE_ISA(Q, "q") + DECLARE_ISA(C, "c") + DECLARE_ISA(V, "v") + DECLARE_ISA(H, "h") + DECLARE_ISA(SSCOFPMF, "sscofpmf") + DECLARE_ISA(SSTC, "sstc") + DECLARE_ISA(SVINVAL, "svinval") + DECLARE_ISA(SVPBMT, "svpbmt") + DECLARE_ISA(ZBB, "zbb") + DECLARE_ISA(ZICBOM, "zicbom") + DECLARE_ISA(ZIHINTPAUSE, "zihintpause") + DECLARE_ISA(SVNAPOT, "svnapot") + DECLARE_ISA(ZICBOZ, "zicboz") + DECLARE_ISA(SMAIA, "smaia") + DECLARE_ISA(SSAIA, "ssaia") + DECLARE_ISA(ZBA, "zba") + DECLARE_ISA(ZBS, "zbs") + DECLARE_ISA(ZICNTR, "zicntr") + DECLARE_ISA(ZICSR, "zicsr") + DECLARE_ISA(ZIFENCEI, "zifencei") + DECLARE_ISA(ZIHPM, "zihpm") + DECLARE_ISA(SMSTATEEN, "smstateen") + DECLARE_ISA(ZICOND, "zicond") + DECLARE_ISA(ZBC, "zbc") + DECLARE_ISA(ZBKB, "zbkb") + DECLARE_ISA(ZBKC, "zbkc") + DECLARE_ISA(ZBKX, "zbkx") + DECLARE_ISA(ZKND, "zknd") + DECLARE_ISA(ZKNE, "zkne") + DECLARE_ISA(ZKNH, "zknh") + DECLARE_ISA(ZKR, "zkr") + DECLARE_ISA(ZKSED, "zksed") + DECLARE_ISA(ZKSH, "zksh") + DECLARE_ISA(ZKT, "zkt") + DECLARE_ISA(ZVBB, "zvbb") + DECLARE_ISA(ZVBC, "zvbc") + DECLARE_ISA(ZVKB, "zvkb") + DECLARE_ISA(ZVKG, "zvkg") + DECLARE_ISA(ZVKNED, "zvkned") + DECLARE_ISA(ZVKNHA, "zvknha") + DECLARE_ISA(ZVKNHB, "zvknhb") + DECLARE_ISA(ZVKSED, "zvksed") + DECLARE_ISA(ZVKSH, "zvksh") + DECLARE_ISA(ZVKT, "zvkt") + DECLARE_ISA(ZFH, "zfh") + DECLARE_ISA(ZFHMIN, "zfhmin") + DECLARE_ISA(ZIHINTNTL, "zihintntl") + DECLARE_ISA(ZVFH, "zvfh") + DECLARE_ISA(ZVFHMIN, "zvfhmin") + DECLARE_ISA(ZFA, "zfa") + DECLARE_ISA(ZTSO, "ztso") + DECLARE_ISA(ZACAS, "zacas") + DECLARE_ISA(XANDESPMU, "xandespmu") +#undef DECLARE_ISA +}; + +rt_inline void cpu_info_init(void) +{ + int i = 0; + rt_uint32_t value; + const char *string; + struct rt_ofw_prop *prop; + struct rt_ofw_node *np, *boot_cpu_np = RT_NULL; + + if ((np = rt_ofw_find_node_by_path("/cpus")) && + !rt_ofw_prop_read_u32(np, "timebase-frequency", &value)) + { + riscv_timer_set_frequency(value); + + #ifdef RT_USING_HWTIMER + if (!rt_device_hwtimer_us_delay) + { + rt_device_hwtimer_us_delay = &riscv_timer_us_delay; + } + #endif /* RT_USING_HWTIMER */ + } + else + { + LOG_E("No '%s' in DTS", "timebase-frequency"); + } + +#ifdef RT_USING_SMP + rt_ofw_prop_read_u32(np, "cpu-boot-delay-us", &cpu_boot_delay_us); +#endif + + rt_ofw_node_put(np); + +#if defined(RT_USING_SMP) && !defined(ARCH_RISCV_M_MODE) + if (sbi_probe_extension(SBI_EXT_ID_HSM)) + { + LOG_I("SBI HSM extension detected"); + cpu_ops = &cpu_sbi_ops; + } +#endif /* RT_USING_SMP && !ARCH_RISCV_M_MODE */ + + rt_ofw_foreach_cpu_node(np) + { + if ((rt_ubase_t)rt_ofw_get_cpu_hwid(np, 0) == boot_cpu_hartid) + { + boot_cpu_np = np; + rt_ofw_node_get(boot_cpu_np); + } + + #ifdef RT_USING_SMP + if (i < RT_CPUS_NR) + { + cpu_np[i] = np; + + if (cpu_ops->cpu_init) + { + cpu_ops->cpu_init(i, np); + } + } + else + { + break; + } + ++i; + #endif /* RT_USING_SMP */ + } + + if (!(np = boot_cpu_np)) + { + LOG_E("No '%s' in DTS", "Boot CPU"); + return; + } + + rt_ofw_foreach_prop_string(np, "riscv,isa-extensions", prop, string) + { + for (i = 0; i < RT_ARRAY_SIZE(isa_ext); ++i) + { + rt_typeof(isa_ext[0]) *ext = &isa_ext[i]; + + if (ext->name && !rt_strcmp(ext->name, string)) + { + riscv_isa_ext_set_raw(i); + break; + } + } + } + + if (!rt_ofw_prop_read_bool(np, "riscv,isa-base")) + { + if (rt_ofw_prop_read_string(np, "riscv,isa", &string)) + { + LOG_E("%s ISA no found", rt_ofw_node_full_name(np)); + goto _exit; + } + + if ((!rt_strncmp(string, "rv32", 4) && __riscv_xlen != 32) || + (!rt_strncmp(string, "rv64", 4) && __riscv_xlen != 64)) + { + LOG_E("%s ISA %.4s is not supported", rt_ofw_node_full_name(np), string); + goto _exit; + } + + string += 4; + + /* ISA: 'i', 'm', 'a', 'f', 'd', 'q', 'c', 'v', 'h'... */ + while (*string && *string != '_') + { + for (i = 0; i < RISCV_ISA_EXT_BASE; ++i) + { + rt_typeof(isa_ext[0]) *ext = &isa_ext[i]; + + if (ext->name && ext->name[0] == *string) + { + riscv_isa_ext_set_raw(i); + break; + } + } + + ++string; + } + + /* Skip '_' */ + while (*string && *(++string)) + { + int size = 0; + + for (i = RISCV_ISA_EXT_BASE; i < RT_ARRAY_SIZE(isa_ext); ++i) + { + rt_typeof(isa_ext[0]) *ext = &isa_ext[i]; + + if (ext->name && !rt_strncmp(ext->name, string, ext->size)) + { + size = ext->size; + riscv_isa_ext_set_raw(i); + break; + } + } + + if (!size) + { + const char *skip = rt_strstr(string, "_"); + + size = skip ? skip - string : rt_strlen(string); + + LOG_E("Unknow ISA %.*s", size, string); + } + + string += size; + } + } + +_exit: + rt_ofw_node_put(np); +} + +rt_inline rt_size_t string_to_size(const char *string, const char *who) +{ + char unit; + rt_size_t size; + const char *cp = string; + + size = atoi(cp); + + while (*cp >= '0' && *cp <= '9') + { + ++cp; + } + + unit = *cp & '_'; + + if (unit == 'M') + { + size *= SIZE_MB; + } + else if (unit == 'K') + { + size *= SIZE_KB; + } + else if (unit == 'G') + { + size *= SIZE_GB; + } + else + { + LOG_W("Unknown unit of '%c' in `%s`", unit, who); + } + + return size; +} + +void rt_hw_common_setup(void) +{ + rt_uint64_t initrd_ranges[3]; + rt_size_t kernel_start, kernel_end; + rt_size_t heap_start, heap_end; + rt_size_t init_page_start, init_page_end; + rt_size_t fdt_start, fdt_end; + rt_region_t init_page_region = { 0 }; + rt_region_t platform_mem_region = { 0 }; + static struct mem_desc platform_mem_desc; + const rt_ubase_t pv_off = PV_OFFSET; + +#ifdef RT_USING_SMART +#if KERNEL_VADDR_START > USER_VADDR_START + rt_hw_mmu_map_init(&rt_kernel_space, (void *)(0UL - IOREMAP_SIZE), IOREMAP_SIZE, MMUTable, pv_off); +#else + rt_hw_mmu_map_init(&rt_kernel_space, (void *)(USER_VADDR_START - IOREMAP_SIZE), IOREMAP_SIZE, MMUTable, pv_off); +#endif +#else +#if __riscv_xlen == 32 + rt_hw_mmu_map_init(&rt_kernel_space, (void *)((rt_ubase_t)&_start - IOREMAP_SIZE), IOREMAP_SIZE, MMUTable, 0); + rt_memblock_reserve_memory("ioremap", (rt_size_t)&_start - IOREMAP_SIZE, (rt_size_t)&_start, MEMBLOCK_NONE); +#else + rt_hw_mmu_map_init(&rt_kernel_space, (void *)(ARCH_PAGE_MASK << RT_ALIGN_DOWN(rt_hw_arch_vaddr_width - ARCH_PAGE_SHIFT, 4)), IOREMAP_SIZE, MMUTable, 0); +#endif +#endif /* RT_USING_SMART */ + + kernel_start = RT_ALIGN_DOWN((rt_size_t)&_start + pv_off - 64, ARCH_PAGE_SIZE); + kernel_end = RT_ALIGN((rt_size_t)&_end + pv_off, ARCH_PAGE_SIZE); + heap_start = kernel_end; + heap_end = RT_ALIGN(heap_start + ARCH_HEAP_SIZE, ARCH_PAGE_SIZE); + init_page_start = heap_end; + init_page_end = RT_ALIGN(init_page_start + ARCH_INIT_PAGE_SIZE, ARCH_PAGE_SIZE); + fdt_start = init_page_end; + fdt_end = RT_ALIGN(fdt_start + fdt_size, ARCH_PAGE_SIZE); + + platform_mem_region.start = kernel_start; +#ifndef RT_USING_BUILTIN_FDT + platform_mem_region.end = fdt_end; +#else + platform_mem_region.end = init_page_end; + (void)fdt_start; + (void)fdt_end; +#endif + + rt_memblock_reserve_memory("kernel", kernel_start, kernel_end, MEMBLOCK_NONE); + rt_memblock_reserve_memory("memheap", heap_start, heap_end, MEMBLOCK_NONE); + rt_memblock_reserve_memory("init-page", init_page_start, init_page_end, MEMBLOCK_NONE); +#ifndef RT_USING_BUILTIN_FDT + rt_memblock_reserve_memory("fdt", fdt_start, fdt_end, MEMBLOCK_NONE); + + /* To virtual address */ + fdt_ptr = (void *)(fdt_ptr - pv_off); +#ifdef KERNEL_VADDR_START + if ((rt_ubase_t)fdt_ptr + fdt_size - KERNEL_VADDR_START > ARCH_EARLY_MAP_SIZE) + { + fdt_ptr = rt_ioremap_early(fdt_ptr + pv_off, fdt_size); + + RT_ASSERT(fdt_ptr != RT_NULL); + } +#endif /* KERNEL_VADDR_START */ + rt_memmove((void *)(fdt_start - pv_off), fdt_ptr, fdt_size); + fdt_ptr = (void *)fdt_start - pv_off; +#else + fdt_ptr = &rt_hw_builtin_fdt; + fdt_size = fdt_totalsize(fdt_ptr); +#endif /* RT_USING_BUILTIN_FDT */ + + rt_system_heap_init((void *)(heap_start - pv_off), (void *)(heap_end - pv_off)); + + init_page_region.start = init_page_start - pv_off; + init_page_region.end = init_page_end - pv_off; + rt_page_init(init_page_region); + + /* Create MMU mapping of kernel memory */ + platform_mem_region.start = RT_ALIGN_DOWN(platform_mem_region.start, ARCH_PAGE_SIZE); + platform_mem_region.end = RT_ALIGN(platform_mem_region.end, ARCH_PAGE_SIZE); + + platform_mem_desc.paddr_start = platform_mem_region.start; + platform_mem_desc.vaddr_start = platform_mem_region.start - pv_off; + platform_mem_desc.vaddr_end = platform_mem_region.end - pv_off - 1; + platform_mem_desc.attr = NORMAL_MEM; + + rt_hw_mmu_setup(&rt_kernel_space, &platform_mem_desc, 1); + + if (rt_fdt_prefetch(fdt_ptr)) + { + /* Platform cannot be initialized */ + RT_ASSERT(0); + } + +#ifdef RT_USING_HWCACHE + rt_hwcache_init(); + + rt_hwcache_icache_enable(); + rt_hwcache_dcache_enable(); +#endif /* RT_USING_HWCACHE */ + + rt_fdt_scan_chosen_stdout(); + + rt_fdt_scan_initrd(initrd_ranges); + + rt_fdt_scan_memory(); + +#ifdef RT_USING_DMA + do { + const char *bootargs; + rt_ubase_t dma_pool_base; + rt_size_t cma_size = 0, coherent_pool_size = 0; + + if (!rt_fdt_bootargs_select("cma=", 0, &bootargs)) + { + cma_size = string_to_size(bootargs, "cma"); + } + + if (!rt_fdt_bootargs_select("coherent_pool=", 0, &bootargs)) + { + coherent_pool_size = string_to_size(bootargs, "coherent-pool"); + } + + if (cma_size <= coherent_pool_size) + { + if (cma_size || coherent_pool_size) + { + LOG_W("DMA pool %s=%u > %s=%u", + "CMA", cma_size, "coherent-pool", coherent_pool_size); + } + + cma_size = 8 * SIZE_MB; + coherent_pool_size = 2 * SIZE_MB; + } + + dma_pool_base = platform_mem_region.end; + rt_memblock_reserve_memory("dma-pool", + dma_pool_base, dma_pool_base + cma_size + coherent_pool_size, MEMBLOCK_NONE); + + if (rt_dma_pool_extract(cma_size, coherent_pool_size)) + { + LOG_E("Alloc DMA pool %s=%u, %s=%u fail", + "CMA", cma_size, "coherent-pool", coherent_pool_size); + } + } while (0); +#endif /* RT_USING_DMA */ + + rt_memblock_setup_memory_environment(); + + rt_fdt_earlycon_kick(FDT_EARLYCON_KICK_UPDATE); + + rt_fdt_unflatten(); + + cpu_info_init(); + + rt_pic_init(); + rt_pic_irq_init(); + + rt_hw_tick_init(); + +#ifdef RT_USING_COMPONENTS_INIT + rt_components_board_init(); +#endif + +#if defined(RT_USING_CONSOLE) && defined(RT_USING_DEVICE) + rt_ofw_console_setup(); +#endif + + rt_thread_idle_sethook(rt_hw_idle_wfi); + +#ifdef RT_USING_SMP + rt_smp_call_init(); + /* 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_ipi_handler_install(RT_SMP_CALL_IPI, rt_smp_call_ipi_handler); + rt_hw_interrupt_umask(RT_SCHEDULE_IPI); + rt_hw_interrupt_umask(RT_STOP_IPI); + rt_hw_interrupt_umask(RT_SMP_CALL_IPI); +#endif /* RT_USING_SMP */ +} + +#ifndef ARCH_RISCV_M_MODE +static rt_err_t sbi_srst_reboot_mode(struct rt_device *dev, char *cmd) +{ + if (!rt_strcmp(cmd, "software") || !rt_strcmp(cmd, "warm")) + { + sbi_srst_reboot(SBI_SRST_RESET_TYPE_WARM_REBOOT); + } + else + { + sbi_srst_reboot(SBI_SRST_RESET_TYPE_COLD_REBOOT); + } + + return RT_EOK; +} + +static int system_power_init(void) +{ + rt_dm_machine_shutdown = rt_dm_machine_shutdown ? : sbi_shutdown; + + if (has_srst_extension) + { + static struct rt_device pm; + + rt_dm_machine_shutdown = sbi_srst_power_off; + + rt_dm_dev_set_name(&pm, "SBI-SRST"); + rt_dm_reboot_mode_register(&pm, sbi_srst_reboot_mode); + } + + return 0; +} +INIT_PREV_EXPORT(system_power_init); +#endif /* ARCH_RISCV_M_MODE */ + +#ifdef RT_USING_SMP +rt_weak void rt_hw_secondary_cpu_up(void) +{ + int cpu_id = rt_hw_cpu_id(); + rt_ubase_t entry = (rt_ubase_t)rt_kmem_v2p(_secondary_cpu_entry); + + if (!entry) + { + LOG_E("Failed to translate '_secondary_cpu_entry' to physical address"); + RT_ASSERT(0); + } + + /* Maybe we are no in the first cpu */ + for (int i = 0; i < RT_ARRAY_SIZE(cpu_np); ++i) + { + int err; + + if (!cpu_np[i] || i == cpu_id) + { + continue; + } + + if (cpu_boot_delay_us) + { + rt_hw_us_delay(cpu_boot_delay_us); + } + + if ((err = cpu_ops->cpu_boot(i, entry))) + { + LOG_W("Call cpu %d on %s", i, "failed"); + } + } +} + +rt_weak void rt_hw_secondary_cpu_bsp_start(void) +{ + int cpu_id = rt_hw_cpu_id(); + + rt_hw_spin_lock(&_cpus_lock); + + rt_hw_mmu_tbl_set((unsigned long)rt_kernel_space.page_table); + + rt_pic_irq_init(); + + riscv_timer_init(); + + rt_dm_secondary_cpu_init(); + + rt_hw_interrupt_umask(RT_SCHEDULE_IPI); + rt_hw_interrupt_umask(RT_STOP_IPI); + rt_hw_interrupt_umask(RT_SMP_CALL_IPI); + + LOG_I("Call cpu %d on %s", cpu_id, "success"); + + rt_system_scheduler_start(); +} + +rt_weak void rt_hw_secondary_cpu_idle_exec(void) +{ + rt_hw_idle_wfi(); +} +#endif /* RT_USING_SMP */ + +void rt_hw_console_output(const char *str) +{ + rt_fdt_earlycon_output(str); +} + +void primary_cpu_entry(void) +{ + /* disable global interrupt */ + rt_hw_interrupt_disable(); + + entry(); +} diff --git a/libcpu/risc-v/common64/startup_gcc.S b/libcpu/risc-v/common64/startup_gcc.S index dde3c092408..6ec569233d9 100644 --- a/libcpu/risc-v/common64/startup_gcc.S +++ b/libcpu/risc-v/common64/startup_gcc.S @@ -19,9 +19,74 @@ boot_hartid: .word 0xdeadbeef +#ifndef RT_USING_DM .global _start .section ".start", "ax" _start: +#else +#define __STACKSIZE__ ARCH_SECONDARY_CPU_STACK_SIZE + +#define RISCV_IMAGE_FLAG_BE_SHIFT 0 +#define RISCV_IMAGE_FLAG_BE_MASK 0x1 + +#define RISCV_IMAGE_FLAG_LE 0 +#define RISCV_IMAGE_FLAG_BE 1 + +#define RISCV_HEADER_VERSION_MAJOR 0 +#define RISCV_HEADER_VERSION_MINOR 2 + +#ifdef ARCH_CPU_BIG_ENDIAN +#error conversion of header fields to LE not yet implemented +#else +#define __HEAD_FLAG_BE RISCV_IMAGE_FLAG_LE +#endif + +#define __HEAD_FLAG(field) (__HEAD_FLAG_##field << RISCV_IMAGE_FLAG_##field##_SHIFT) + +#define __HEAD_FLAGS (__HEAD_FLAG(BE)) + +#define __HEAD_VERSION (RISCV_HEADER_VERSION_MAJOR << 16 | RISCV_HEADER_VERSION_MINOR) + + .section ".text.start","ax" + +#ifdef RT_USING_OFW +/* + * We follow the kernel's boot in RISC-V: + * https://www.kernel.org/doc/html/latest/arch/riscv/boot-image-header.html + */ +_head: + j _start /* Executable code */ + .word 0 /* Executable code */ + .balign 8 +#if __riscv_xlen == 64 + .dword 0x200000 /* Image load offset(2MB) from start of RAM */ +#elif __riscv_xlen == 32 + .dword 0x400000 /* Image load offset(4MB) from start of RAM */ +#else +#error "Unexpected __riscv_xlen" +#endif + .dword _end - _head /* Effective Image size, little endian (_end defined in link.lds) */ + .dword __HEAD_FLAGS /* kernel flags, little endian */ + .word __HEAD_VERSION /* Version of this header */ + .word 0 /* Reserved */ + .dword 0 /* Reserved */ + .ascii "RISCV\0\0\0" /* Magic number, little endian */ + .balign 4 + .ascii "RSC\x05" /* Magic number 2, little endian */ + .word 0 /* Reserved for PE COFF offset */ +#endif /* RT_USING_OFW */ + + .global _start +_start: +/* + * Boot CPU general-purpose register settings: + * https://www.kernel.org/doc/html/latest/arch/riscv/boot.html#pre-kernel-requirements-and-constraints + * a0 = hartid of the current core. + * a1 = physical address of device tree blob (dtb) in system RAM. + */ + mv s0, a0 + mv s1, a1 +#endif j 1f .word 0xdeadbeef .align 3 @@ -71,7 +136,10 @@ system_init: li x6, 0 li x7, 0 li x8, 0 +#ifndef RT_USING_OFW + /* In the DM OFW, s1 will be used again later */ li x9, 0 +#endif #ifndef RT_USING_SMP /* In the SMP architecture, a0 will be used again later */ li x10,0 @@ -132,6 +200,12 @@ system_init: bne t0, t1, early_secondary_cpu_entry #endif /* RT_USING_SMP */ +#ifdef RT_USING_OFW + /* Save devicetree info */ + mv a0, s1 + call rt_hw_fdt_install_early +#endif + /** * sscratch is always zero on kernel mode */ diff --git a/libcpu/risc-v/common64/tick.c b/libcpu/risc-v/common64/tick.c index 8e8ff86a748..b6e971cc46a 100644 --- a/libcpu/risc-v/common64/tick.c +++ b/libcpu/risc-v/common64/tick.c @@ -29,8 +29,25 @@ int tick_isr(void) return 0; } +#ifdef RT_USING_DM +static rt_uint32_t timebase_frequency; +#define CPUTIME_TIMER_FREQ timebase_frequency + +void riscv_timer_set_frequency(rt_uint32_t freq) +{ + HWREG32(&timebase_frequency) = freq; + rt_hw_wmb(); +} + +rt_uint32_t riscv_timer_get_frequency(void) +{ + rt_hw_rmb(); + return HWREG32(&timebase_frequency); +} +#else /* BSP should config clockbase frequency */ RT_STATIC_ASSERT(defined_clockbase_freq, CPUTIME_TIMER_FREQ != 0); +#endif /* Sets and enable the timer interrupt */ int rt_hw_tick_init(void) @@ -61,7 +78,7 @@ int rt_hw_tick_init(void) * * @param us the delay time of us */ -void rt_hw_us_delay(rt_uint32_t us) +void riscv_timer_us_delay(rt_uint32_t us) { unsigned long start_time; unsigned long end_time; @@ -74,3 +91,15 @@ void rt_hw_us_delay(rt_uint32_t us) run_time = clock_cpu_gettime(); } while(run_time < end_time); } + +#if !defined(RT_USING_DM) || !defined(RT_USING_HWTIMER) +/** + * This function will delay for some us. + * + * @param us the delay time of us + */ +void rt_hw_us_delay(rt_uint32_t us) +{ + riscv_timer_us_delay(us); +} +#endif /* !RT_USING_DM || !RT_USING_HWTIMER */ diff --git a/libcpu/risc-v/common64/trap.c b/libcpu/risc-v/common64/trap.c index 4cfc9d82804..baca6f6baa6 100644 --- a/libcpu/risc-v/common64/trap.c +++ b/libcpu/risc-v/common64/trap.c @@ -19,6 +19,9 @@ #include #include #include +#ifdef RT_USING_DM +#include +#endif #include #include @@ -313,7 +316,11 @@ void handle_trap(rt_ubase_t scause, rt_ubase_t stval, rt_ubase_t sepc, SCAUSE_S_EXTERNAL_INTR == (scause & 0xff)) { rt_interrupt_enter(); + #ifndef RT_USING_DM plic_handle_irq(); + #else + rt_pic_do_traps(); + #endif rt_interrupt_leave(); } else if ((SCAUSE_INTERRUPT | SCAUSE_S_TIMER_INTR) == scause) diff --git a/libcpu/risc-v/link.lds.S b/libcpu/risc-v/link.lds.S new file mode 100755 index 00000000000..568c6eb6e0b --- /dev/null +++ b/libcpu/risc-v/link.lds.S @@ -0,0 +1,256 @@ +/* + * Copyright (c) 2006-2025, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Date Author Notes + * 2020-12-12 bernard first version + */ + +#define __ASSEMBLY__ + +#include "rtconfig.h" + +#ifndef __riscv_xlen +#ifdef ARCH_CPU_64BIT +#define __riscv_xlen 64 +#else +#define __riscv_xlen 32 +#endif +#endif + +#include "page_def.h" + +OUTPUT_ARCH(riscv) + +#ifndef ARCH_TEXT_OFFSET +#if __riscv_xlen == 64 +#define ARCH_TEXT_OFFSET 0x200000 +#elif __riscv_xlen == 32 +#define ARCH_TEXT_OFFSET 0x400000 +#else +#error "Unexpected __riscv_xlen" +#endif +#endif + +#ifndef ARCH_RAM_OFFSET +#define ARCH_RAM_OFFSET 0 +#endif + +PHDRS +{ + text PT_LOAD FLAGS(5); /* PF_R(4) | PF_X(1) = 0x5 (RX) */ + data PT_LOAD FLAGS(6); /* PF_R(4) | PF_W(2) = 0x6 (RW) */ +} + +SECTIONS +{ + _text_offset = ARCH_TEXT_OFFSET; + +#ifdef RT_USING_SMART + . = KERNEL_VADDR_START + _text_offset; +#else + . = ARCH_RAM_OFFSET + _text_offset; +#endif + + .text : + { + PROVIDE(__text_start = .); + + KEEP(*(.text.start)) /* The start point */ + *(.text) /* Remaining code */ + *(.text.*) /* Remaining code */ + + *(.rodata) /* Read-only data (constants) */ + *(.rodata*) + *(.glue_7) + *(.glue_7t) + *(.gnu.linkonce.t*) + + /* section information for utest */ + . = ALIGN(RISCV_SZPTR); + PROVIDE(__rt_utest_tc_tab_start = .); + KEEP(*(UtestTcTab)) + PROVIDE(__rt_utest_tc_tab_end = .); + + /* section information for finsh shell */ + . = ALIGN(RISCV_SZPTR); + PROVIDE(__fsymtab_start = .); + KEEP(*(FSymTab)) + PROVIDE(__fsymtab_end = .); + . = ALIGN(RISCV_SZPTR); + PROVIDE(__vsymtab_start = .); + KEEP(*(VSymTab)) + PROVIDE(__vsymtab_end = .); + . = ALIGN(RISCV_SZPTR); + + /* section information for modules */ + . = ALIGN(RISCV_SZPTR); + PROVIDE(__rtmsymtab_start = .); + KEEP(*(RTMSymTab)) + PROVIDE(__rtmsymtab_end = .); + + /* section information for initialization */ + . = ALIGN(RISCV_SZPTR); + PROVIDE(__rt_init_start = .); + KEEP(*(SORT(.rti_fn*))) + PROVIDE(__rt_init_end = .); + + /* section information for rt_ofw. */ + . = ALIGN(RISCV_SZPTR); + PROVIDE(__rt_ofw_data_start = .); + KEEP(*(SORT(.rt_ofw_data.*))) + PROVIDE(__rt_ofw_data_end = .); + . = ALIGN(RISCV_SZPTR); + + /* section information for usb usbh_class_info */ + . = ALIGN(RISCV_SZPTR); + __usbh_class_info_start__ = .; + KEEP(*(.usbh_class_info)) + . = ALIGN(RISCV_SZPTR); + __usbh_class_info_end__ = .; + + PROVIDE(__text_end = .); + } :text + + .eh_frame_hdr : + { + *(.eh_frame_hdr) + *(.eh_frame_entry) + } + .eh_frame : ONLY_IF_RO { KEEP (*(.eh_frame)) } + + . = ALIGN(RISCV_SZPTR); + .data : + { + *(.data) + *(.data.*) + + *(.data1) + *(.data1.*) + + . = ALIGN(ARCH_PAGE_SIZE); + PROVIDE(__global_pointer$ = . + 0x800); + + *(.sdata) + *(.sdata.*) + + *(.rel.local) + } :data + + . = ALIGN(RISCV_SZPTR); + .ctors : + { + PROVIDE(__ctors_start__ = .); + KEEP(*(SORT(.init_array.*))) + KEEP(*(.init_array)) + PROVIDE(__ctors_end__ = .); + } + + .dtors : + { + PROVIDE(__dtors_start__ = .); + KEEP(*(SORT(.fini_array.*))) + KEEP(*(.fini_array)) + PROVIDE(__dtors_end__ = .); + } + + .bss : + { + /* + * We need some free space to page, move .bss.noclean.* + * to optimize size. + */ + PROVIDE(__bss_noclean_start = .); + *(.bss.noclean.*) + PROVIDE(__bss_noclean_end = .); + . = ALIGN(RISCV_SZPTR); + PROVIDE(__bss_start = .); + *(.bss) + *(.bss.*) + *(.sbss) + *(.sbss.*) + *(.dynbss) + *(COMMON) + . = ALIGN(RISCV_SZPTR); + PROVIDE(__bss_end = .); + } + + .stack (NOLOAD) : + { + . = ALIGN(64); + __stack_start__ = .; + /* Dynamically allocate stack areas according to RT_CPUS_NR */ + . += (ARCH_SECONDARY_CPU_STACK_SIZE * RT_CPUS_NR); + __stack_end__ = .; + } + + .percpu (NOLOAD) : + { + #if defined(RT_USING_SMP) && defined(ARCH_MM_MMU) + /* Align for MMU early map */ + . = ALIGN(SUPPER_PAGE_SIZE); + #endif /* RT_USING_SMP && ARCH_MM_MMU */ + PROVIDE(__percpu_start = .); + *(.percpu) + #if defined(RT_USING_SMP) && defined(ARCH_MM_MMU) + . = ALIGN(ARCH_PAGE_SIZE); + #endif /* RT_USING_SMP && ARCH_MM_MMU */ + PROVIDE(__percpu_end = .); + + /* Clone the area */ + . = __percpu_end + (__percpu_end - __percpu_start) * (RT_CPUS_NR - 1); + #if defined(RT_USING_SMP) && defined(ARCH_MM_MMU) + /* Align for MMU early map */ + . = ALIGN(SUPPER_PAGE_SIZE); + #endif /* RT_USING_SMP && ARCH_MM_MMU */ + PROVIDE(__percpu_real_end = .); + + #if defined(RT_USING_SMP) && defined(ARCH_MM_MMU) + ASSERT(((__percpu_real_end - __percpu_start) / ARCH_PAGE_SIZE) <= (ARCH_PAGE_SIZE / RISCV_SZPTR), "Not enough L0 pages to map per-CPU"); + #endif /* RT_USING_SMP && ARCH_MM_MMU */ + } + + /* + * We should make the bootloader know the size of memory we need, + * so we MUST calc the image's size with section '.percpu'. + */ + _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) } + + __data_size = SIZEOF(.data); + __bss_size = SIZEOF(.bss); +} diff --git a/libcpu/risc-v/virt64/SConscript b/libcpu/risc-v/virt64/SConscript index 1562b9c14f2..ee174e7de70 100644 --- a/libcpu/risc-v/virt64/SConscript +++ b/libcpu/risc-v/virt64/SConscript @@ -5,6 +5,9 @@ cwd = GetCurrentDir() src = Glob('*.c') + Glob('*.cpp') + Glob('*_gcc.S') CPPPATH = [cwd] +if GetDepend('RT_USING_DM'): + SrcRemove(src, ['cache.c', 'interrupt.c', 'plic.c']) + group = DefineGroup('CPU', src, depend = [''], CPPPATH = CPPPATH) Return('group') diff --git a/libcpu/risc-v/virt64/interrupt.h b/libcpu/risc-v/virt64/interrupt.h index a32cac10881..b115130d4a5 100644 --- a/libcpu/risc-v/virt64/interrupt.h +++ b/libcpu/risc-v/virt64/interrupt.h @@ -11,11 +11,13 @@ #ifndef INTERRUPT_H__ #define INTERRUPT_H__ -#define MAX_HANDLERS 128 - #include #include "stack.h" +#ifndef MAX_HANDLERS +#define MAX_HANDLERS 128 +#endif + enum { EP_INSTRUCTION_ADDRESS_MISALIGNED = 0,