diff --git a/boards/realtek/rts5912_evb/rts5912_evb.dts b/boards/realtek/rts5912_evb/rts5912_evb.dts index 15d3b5357f1..f58d75e36da 100644 --- a/boards/realtek/rts5912_evb/rts5912_evb.dts +++ b/boards/realtek/rts5912_evb/rts5912_evb.dts @@ -20,6 +20,17 @@ }; }; +&adc0 { + status = "okay"; + pinctrl-0 = <&adc0_gpio074 &adc1_gpio075 + &adc2_gpio076 &adc3_gpio077 + &adc4_gpio078 &adc5_gpio079 + &adc6_gpio080 &adc7_gpio081 + &adc8_gpio082 &adc9_gpio054 + &adc10_gpio098 &adc11_gpio024>; + pinctrl-names = "default"; +}; + &uart0 { status = "okay"; current-speed = <115200>; diff --git a/boards/realtek/rts5912_evb/rts5912_evb.yaml b/boards/realtek/rts5912_evb/rts5912_evb.yaml index e81e170e7d6..e781df48992 100644 --- a/boards/realtek/rts5912_evb/rts5912_evb.yaml +++ b/boards/realtek/rts5912_evb/rts5912_evb.yaml @@ -15,4 +15,5 @@ flash: 320 supported: - gpio - pinmux + - adc vendor: realtek diff --git a/boards/realtek/rts5912_evb/rts5912_evb_defconfig b/boards/realtek/rts5912_evb/rts5912_evb_defconfig index d821f0279dd..a454f6a87a0 100644 --- a/boards/realtek/rts5912_evb/rts5912_evb_defconfig +++ b/boards/realtek/rts5912_evb/rts5912_evb_defconfig @@ -20,3 +20,6 @@ CONFIG_GPIO=y # Input Driver CONFIG_INPUT=y + +# Enable ADC +CONFIG_ADC=y diff --git a/drivers/adc/CMakeLists.txt b/drivers/adc/CMakeLists.txt index e20cced556f..b5ae6b9f26d 100644 --- a/drivers/adc/CMakeLists.txt +++ b/drivers/adc/CMakeLists.txt @@ -61,3 +61,4 @@ zephyr_library_sources_ifdef(CONFIG_ADC_AD4114 adc_ad4114.c) zephyr_library_sources_ifdef(CONFIG_ADC_AD7124 adc_ad7124.c) zephyr_library_sources_ifdef(CONFIG_ADC_AD405X adc_ad405x.c) zephyr_library_sources_ifdef(CONFIG_ADC_AD4130 adc_ad4130.c) +zephyr_library_sources_ifdef(CONFIG_ADC_REALTEK_RTS5912 adc_realtek_rts5912.c) diff --git a/drivers/adc/Kconfig b/drivers/adc/Kconfig index 32b4e4175e1..a8a21ceeb3c 100644 --- a/drivers/adc/Kconfig +++ b/drivers/adc/Kconfig @@ -146,4 +146,6 @@ source "drivers/adc/Kconfig.ad405x" source "drivers/adc/Kconfig.ad4130" +source "drivers/adc/Kconfig.rts5912" + endif # ADC diff --git a/drivers/adc/Kconfig.rts5912 b/drivers/adc/Kconfig.rts5912 new file mode 100644 index 00000000000..fdceea6f43a --- /dev/null +++ b/drivers/adc/Kconfig.rts5912 @@ -0,0 +1,10 @@ +# Copyright (c) 2025, Realtek, SIBG-SD7 +# SPDX-License-Identifier: Apache-2.0 + +config ADC_REALTEK_RTS5912 + bool "Realtek RTS5912 ADC drivers" + default y + depends on DT_HAS_REALTEK_RTS5912_ADC_ENABLED + select PINCTRL + help + This option enables the ADC driver for Realtek RTS5912 of processors. diff --git a/drivers/adc/adc_realtek_rts5912.c b/drivers/adc/adc_realtek_rts5912.c new file mode 100644 index 00000000000..5f517209a7b --- /dev/null +++ b/drivers/adc/adc_realtek_rts5912.c @@ -0,0 +1,303 @@ +/* + * Copyright (c) 2025 Realtek, SIBG-SD7 + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT realtek_rts5912_adc + +#include +#include +#include +#include + +#include "reg/reg_adc.h" + +#define ADC_CONTEXT_USES_KERNEL_TIMER +#include "adc_context.h" + +#include +LOG_MODULE_REGISTER(adc_rts5912, CONFIG_ADC_LOG_LEVEL); + +#define RTS5912_ADC_MAX_CHAN 12 +#define RTS5912_ADC_POLLING_TIME_MS 1 +#define RTS5912_ADC_ENABLE_TIMEOUT 100 + +struct adc_rts5912_config { + volatile struct adc_regs *regs; + const struct pinctrl_dev_config *pcfg; +#ifdef CONFIG_CLOCK_CONTROL + const struct device *clk_dev; + struct rts5912_sccon_subsys sccon_cfg; +#endif +}; + +struct adc_rts5912_data { + struct adc_context ctx; + const struct device *adc_dev; + volatile uint16_t *buffer; + volatile uint16_t *repeat_buffer; + uint32_t channels; +}; + +static void adc_context_start_sampling(struct adc_context *ctx) +{ + struct adc_rts5912_data *data = CONTAINER_OF(ctx, struct adc_rts5912_data, ctx); + const struct device *adc_dev = data->adc_dev; + const struct adc_rts5912_config *const cfg = adc_dev->config; + volatile struct adc_regs *regs = cfg->regs; + + data->repeat_buffer = data->buffer; + + regs->ctrl |= ADC_CTRL_SGLDNINTEN; + regs->ctrl |= ADC_CTRL_START; +} + +static void adc_context_update_buffer_pointer(struct adc_context *ctx, bool repeat_sampling) +{ + struct adc_rts5912_data *data = CONTAINER_OF(ctx, struct adc_rts5912_data, ctx); + + if (repeat_sampling) { + data->buffer = data->repeat_buffer; + } +} + +static int adc_rts5912_channel_setup(const struct device *dev, + const struct adc_channel_cfg *channel_cfg) +{ + const struct adc_rts5912_config *const cfg = dev->config; + volatile struct adc_regs *regs = cfg->regs; + + if (channel_cfg->acquisition_time != ADC_ACQ_TIME_DEFAULT) { + LOG_ERR("Conversion time not supported!"); + return -EINVAL; + } + + if (channel_cfg->channel_id >= RTS5912_ADC_MAX_CHAN) { + LOG_ERR("Channel %d not supported!", channel_cfg->channel_id); + return -EINVAL; + } + + if (channel_cfg->gain != ADC_GAIN_1) { + LOG_ERR("ADC gain not supported!"); + return -EINVAL; + } + + uint8_t channel_id = channel_cfg->channel_id; + + regs->chctrl |= ((0x01ul << channel_id) | (ADC_CHCTRL_LPFBP << channel_id)); + LOG_DBG("CHCTRL = 0x%08x", regs->chctrl); + + return 0; +} + +static bool adc_rts5912_validate_buffer_size(const struct adc_sequence *sequence) +{ + int chan_count = 0; + size_t buff_need; + uint32_t chan_mask; + + for (chan_mask = 0x80; chan_mask != 0; chan_mask >>= 1) { + if (chan_mask & sequence->channels) { + chan_count++; + } + } + + buff_need = chan_count * sizeof(uint16_t); + + if (sequence->options) { + buff_need *= 1 + sequence->options->extra_samplings; + } + + if (buff_need > sequence->buffer_size) { + return false; + } + + return true; +} + +static int adc_rts5912_enable(const struct device *dev) +{ + const struct adc_rts5912_config *const cfg = dev->config; + volatile struct adc_regs *regs = cfg->regs; + int64_t st = k_uptime_get(); + + regs->ctrl |= ADC_CTRL_EN; + while ((k_uptime_get() - st) < RTS5912_ADC_ENABLE_TIMEOUT) { + if (regs->sts & ADC_STS_RDY) { + return 0; + } + k_msleep(RTS5912_ADC_POLLING_TIME_MS); + } + + LOG_ERR("ADC enable timeout"); + regs->ctrl &= ~ADC_CTRL_EN; + + return -EIO; +} + +static int adc_rts5912_start_read(const struct device *dev, const struct adc_sequence *sequence) +{ + struct adc_rts5912_data *const data = dev->data; + + if (sequence->channels & ~BIT_MASK(RTS5912_ADC_MAX_CHAN)) { + LOG_ERR("Incorrect channels, bitmask 0x%x", sequence->channels); + return -EINVAL; + } + + if (sequence->channels == 0UL) { + LOG_ERR("No channel selected"); + return -EINVAL; + } + + if (!adc_rts5912_validate_buffer_size(sequence)) { + LOG_ERR("Incorrect buffer size"); + return -ENOMEM; + } + + data->channels = sequence->channels; + data->buffer = sequence->buffer; + + if (adc_rts5912_enable(dev) < 0) { + return -EIO; + } + + adc_context_start_read(&data->ctx, sequence); + + return adc_context_wait_for_completion(&data->ctx); +} + +static int adc_rts5912_read(const struct device *dev, const struct adc_sequence *sequence) +{ + struct adc_rts5912_data *const data = dev->data; + int error; + + adc_context_lock(&data->ctx, false, NULL); + error = adc_rts5912_start_read(dev, sequence); + adc_context_release(&data->ctx, error); + + return error; +} + +static void rts5912_adc_get_sample(const struct device *dev) +{ + const struct adc_rts5912_config *const cfg = dev->config; + volatile struct adc_regs *regs = cfg->regs; + struct adc_rts5912_data *const data = dev->data; + uint32_t idx; + uint32_t channels = data->channels; + uint32_t bit; + + /* + * Using the enabled channel bit set, from + * lowest channel number to highest, find out + * which channel is enabled and copy the ADC + * values from hardware registers to the data + * buffer. + */ + bit = find_lsb_set(channels); + + while (bit != 0) { + idx = bit - 1; + + *data->buffer = ((uint16_t)regs->chdata[idx] & ADC_CHDATA_RESULT_Msk); + data->buffer++; + + LOG_DBG("idx=%d, data=%x", idx, regs->chdata[idx]); + + channels &= ~BIT(idx); + bit = find_lsb_set(channels); + } +} + +static void adc_rts5912_single_isr(const struct device *dev) +{ + const struct adc_rts5912_config *const cfg = dev->config; + volatile struct adc_regs *regs = cfg->regs; + struct adc_rts5912_data *const data = dev->data; + + if (regs->sts & ADC_STS_SGLDN) { + LOG_DBG("single done interrupt triggered."); + + regs->ctrl &= ~(ADC_CTRL_SGLDNINTEN); + regs->sts &= regs->sts; + + rts5912_adc_get_sample(dev); + + regs->ctrl &= ~ADC_CTRL_EN; + adc_context_on_sampling_done(&data->ctx, dev); + } +} + +static int adc_rts5912_init(const struct device *dev) +{ + const struct adc_rts5912_config *const cfg = dev->config; + struct adc_rts5912_data *const data = dev->data; + volatile struct adc_regs *regs = cfg->regs; + + int ret; + + data->adc_dev = dev; + + ret = pinctrl_apply_state(cfg->pcfg, PINCTRL_STATE_DEFAULT); + if (ret != 0) { + LOG_ERR("rts5912 ADC pinctrl setup failed (%d)", ret); + return ret; + } + +#ifdef CONFIG_CLOCK_CONTROL + if (!device_is_ready(cfg->clk_dev)) { + LOG_ERR("clock \"%s\" device not ready", cfg->clk_dev->name); + return -ENODEV; + } + + ret = clock_control_on(cfg->clk_dev, (clock_control_subsys_t)&cfg->sccon_cfg); + if (ret != 0) { + LOG_ERR("clock power on fail"); + return ret; + } +#endif + + regs->ctrl = ADC_CTRL_RST; + + IRQ_CONNECT(DT_INST_IRQN(0), DT_INST_IRQ(0, priority), adc_rts5912_single_isr, + DEVICE_DT_INST_GET(0), 0); + irq_enable(DT_INST_IRQN(0)); + + adc_context_unlock_unconditionally(&data->ctx); + + return 0; +} + +#define DEV_CONFIG_CLK_DEV_INIT(n) \ + .clk_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR(n)), \ + .sccon_cfg = { \ + .clk_grp = DT_INST_CLOCKS_CELL(n, clk_grp), \ + .clk_idx = DT_INST_CLOCKS_CELL(n, clk_idx), \ + } + +#define ADC_RTS5912_INIT(n) \ + PINCTRL_DT_INST_DEFINE(n); \ + \ + static DEVICE_API(adc, adc_rts5912_api_##n) = { \ + .channel_setup = adc_rts5912_channel_setup, \ + .read = adc_rts5912_read, \ + .ref_internal = DT_INST_PROP(n, vref_mv), \ + }; \ + \ + static struct adc_rts5912_config adc_rts5912_dev_cfg_##n = { \ + .regs = (struct adc_regs *)(DT_INST_REG_ADDR(n)), \ + .pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \ + DEV_CONFIG_CLK_DEV_INIT(n)}; \ + \ + static struct adc_rts5912_data adc_rts5912_dev_data_##n = { \ + ADC_CONTEXT_INIT_TIMER(adc_rts5912_dev_data_##n, ctx), \ + ADC_CONTEXT_INIT_LOCK(adc_rts5912_dev_data_##n, ctx), \ + ADC_CONTEXT_INIT_SYNC(adc_rts5912_dev_data_##n, ctx), \ + }; \ + \ + DEVICE_DT_INST_DEFINE(n, adc_rts5912_init, NULL, &adc_rts5912_dev_data_##n, \ + &adc_rts5912_dev_cfg_##n, PRE_KERNEL_1, CONFIG_ADC_INIT_PRIORITY, \ + &adc_rts5912_api_##n); + +DT_INST_FOREACH_STATUS_OKAY(ADC_RTS5912_INIT) diff --git a/dts/arm/realtek/ec/rts5912.dtsi b/dts/arm/realtek/ec/rts5912.dtsi index 408cdf321ce..771bddf3803 100644 --- a/dts/arm/realtek/ec/rts5912.dtsi +++ b/dts/arm/realtek/ec/rts5912.dtsi @@ -6,6 +6,7 @@ */ #include +#include #include #include @@ -184,6 +185,15 @@ status = "okay"; }; + adc0: adc@4000fe00 { + compatible = "realtek,rts5912-adc"; + reg = <0x4000fe00 0x38>; + clocks = <&sccon RTS5912_SCCON_ADC ADC0_CLKPWR>; + interrupts = <221 0>; + #io-channel-cells = <1>; + status = "disabled"; + }; + uart0: uart@40010100 { compatible = "ns16550"; reg = <0x40010100 0x100>; diff --git a/dts/bindings/adc/realtek,rts5912-adc.yaml b/dts/bindings/adc/realtek,rts5912-adc.yaml new file mode 100644 index 00000000000..473a5c8acec --- /dev/null +++ b/dts/bindings/adc/realtek,rts5912-adc.yaml @@ -0,0 +1,23 @@ +description: Realtek rts5912 ADC + +compatible: "realtek,rts5912-adc" + +include: [adc-controller.yaml, pinctrl-device.yaml] + +properties: + interrupts: + required: true + + pinctrl-0: + required: true + + pinctrl-names: + required: true + + vref-mv: + type: int + default: 3300 + description: The reference voltage of the ADC in mV. + +io-channel-cells: + - input diff --git a/soc/realtek/ec/rts5912/reg/reg_adc.h b/soc/realtek/ec/rts5912/reg/reg_adc.h new file mode 100644 index 00000000000..0b54d13c8af --- /dev/null +++ b/soc/realtek/ec/rts5912/reg/reg_adc.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2025 Realtek, SIBG-SD7 + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_SOC_REALTEK_RTS5912_REG_ADC_H +#define ZEPHYR_SOC_REALTEK_RTS5912_REG_ADC_H + +struct adc_regs { + uint32_t ctrl; + uint32_t chctrl; + uint32_t sts; + uint32_t chdata[12]; + uint32_t coeffa; + uint32_t coeffb; +}; + +/* CTRL */ +#define ADC_CTRL_EN BIT(0) +#define ADC_CTRL_START BIT(1) +#define ADC_CTRL_RST BIT(2) +#define ADC_CTRL_MDSEL BIT(3) +#define ADC_CTRL_SGLDNINTEN BIT(4) +#define ADC_CTRL_RPTDNINTEN BIT(5) + +/* CHCTRL */ +#define ADC_CHCTRL_CH0EN BIT(0) +#define ADC_CHCTRL_CH1EN BIT(1) +#define ADC_CHCTRL_CH2EN BIT(2) +#define ADC_CHCTRL_CH3EN BIT(3) +#define ADC_CHCTRL_CH4EN BIT(4) +#define ADC_CHCTRL_CH5EN BIT(5) +#define ADC_CHCTRL_CH6EN BIT(6) +#define ADC_CHCTRL_CH7EN BIT(7) +#define ADC_CHCTRL_CH8EN BIT(8) +#define ADC_CHCTRL_CH9EN BIT(9) +#define ADC_CHCTRL_CH10EN BIT(10) +#define ADC_CHCTRL_CH11EN BIT(11) +#define ADC_CHCTRL_LPFBP BIT(12) +#define ADC_CHCTRL_CALBP BIT(24) + +/* STS */ +#define ADC_STS_CH0DN BIT(0) +#define ADC_STS_CH1DN BIT(1) +#define ADC_STS_CH2DN BIT(2) +#define ADC_STS_CH3DN BIT(3) +#define ADC_STS_CH4DN BIT(4) +#define ADC_STS_CH5DN BIT(5) +#define ADC_STS_CH6DN BIT(6) +#define ADC_STS_CH7DN BIT(7) +#define ADC_STS_CH8DN BIT(8) +#define ADC_STS_CH9DN BIT(9) +#define ADC_STS_CH10DN BIT(10) +#define ADC_STS_CH11DN BIT(11) +#define ADC_STS_SGLDN BIT(12) +#define ADC_STS_RPTDN BIT(13) +#define ADC_STS_RDY BIT(16) +#define ADC_STS_LPFSTB BIT(17) + +/* CHDATA */ +#define ADC_CHDATA_RESULT_Pos (0U) +#define ADC_CHDATA_RESULT_Msk GENMASK(11, 0) + +#endif /* ZEPHYR_SOC_REALTEK_RTS5912_REG_ADC_H */ diff --git a/tests/drivers/adc/adc_api/boards/rts5912_evb.overlay b/tests/drivers/adc/adc_api/boards/rts5912_evb.overlay new file mode 100644 index 00000000000..6ac2c0ebd78 --- /dev/null +++ b/tests/drivers/adc/adc_api/boards/rts5912_evb.overlay @@ -0,0 +1,34 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright (c) 2025 Realtek Semiconductor Corporation, SIBG-SD7 + * + */ + +/ { + zephyr,user { + io-channels = <&adc0 0>, <&adc0 1>; + }; +}; + +&adc0 { + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; + + channel@0 { + reg = <0>; + zephyr,gain = "ADC_GAIN_1"; + zephyr,reference = "ADC_REF_INTERNAL"; + zephyr,acquisition-time = <0>; + zephyr,resolution = <10>; + }; + + channel@1 { + reg = <1>; + zephyr,gain = "ADC_GAIN_1"; + zephyr,reference = "ADC_REF_INTERNAL"; + zephyr,acquisition-time = <0>; + zephyr,resolution = <10>; + }; +}; diff --git a/tests/drivers/build_all/adc/testcase.yaml b/tests/drivers/build_all/adc/testcase.yaml index a0c7dda1326..f73b054761b 100644 --- a/tests/drivers/build_all/adc/testcase.yaml +++ b/tests/drivers/build_all/adc/testcase.yaml @@ -39,6 +39,8 @@ tests: platform_allow: disco_l475_iot1 drivers.adc.xec.build: platform_allow: mec15xxevb_assy6853 + drivers.adc.realtek.rts5912.build: + platform_allow: rts5912_evb drivers.adc.test.build: platform_allow: qemu_cortex_m3 drivers.adc.linker_generator.build: