diff --git a/drivers/dac/CMakeLists.txt b/drivers/dac/CMakeLists.txt index e5355166440..f353b17ae07 100644 --- a/drivers/dac/CMakeLists.txt +++ b/drivers/dac/CMakeLists.txt @@ -11,4 +11,5 @@ zephyr_library_sources_ifdef(CONFIG_DAC_DACX0508 dac_dacx0508.c) zephyr_library_sources_ifdef(CONFIG_DAC_DACX3608 dac_dacx3608.c) zephyr_library_sources_ifdef(CONFIG_DAC_SHELL dac_shell.c) zephyr_library_sources_ifdef(CONFIG_DAC_MCP4725 dac_mcp4725.c) +zephyr_library_sources_ifdef(CONFIG_DAC_GD32 dac_gd32.c) zephyr_library_sources_ifdef(CONFIG_USERSPACE dac_handlers.c) diff --git a/drivers/dac/Kconfig b/drivers/dac/Kconfig index d1b19ddbc62..4618f21e0aa 100644 --- a/drivers/dac/Kconfig +++ b/drivers/dac/Kconfig @@ -44,4 +44,6 @@ source "drivers/dac/Kconfig.dacx3608" source "drivers/dac/Kconfig.mcp4725" +source "drivers/dac/Kconfig.gd32" + endif # DAC diff --git a/drivers/dac/Kconfig.gd32 b/drivers/dac/Kconfig.gd32 new file mode 100644 index 00000000000..d4db044d467 --- /dev/null +++ b/drivers/dac/Kconfig.gd32 @@ -0,0 +1,15 @@ +# DAC configuration options + +# Copyright (c) 2021 BrainCo Inc. +# +# SPDX-License-Identifier: Apache-2.0 + +# Workaround for not being able to have commas in macro arguments +DT_COMPAT_GD_GD32_DAC := gd,gd32-dac + +config DAC_GD32 + bool "GD32 DAC driver" + depends on SOC_FAMILY_GD32 + default $(dt_compat_enabled,$(DT_COMPAT_GD_GD32_DAC)) + help + Enable GigaDevice GD32 DAC driver diff --git a/drivers/dac/dac_gd32.c b/drivers/dac/dac_gd32.c new file mode 100644 index 00000000000..0dcd27b77d4 --- /dev/null +++ b/drivers/dac/dac_gd32.c @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2021 BrainCo Inc. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT gd_gd32_dac + +#include +#include +#include +#include + +#include +LOG_MODULE_REGISTER(dac_gd32, CONFIG_DAC_LOG_LEVEL); + +/** + * GD32 DAC HAL use different DAC0 interface for 2 or 1 output channels SoCs. + * Unify the DAC0 interface to DAC0_xx. + */ +#if DT_INST_PROP(0, num_channels) == 1 +#define DAC_CTL_DEN0 DAC_CTL_DEN +#define DAC0_R8DH DAC_R8DH +#define DAC0_R12DH DAC_R12DH +#endif + +struct dac_gd32_config { + uint32_t reg; + uint32_t rcu_periph_clock; + const struct pinctrl_dev_config *pcfg; + uint32_t num_channels; + uint32_t reset_val; +}; + +struct dac_gd32_data { + uint8_t resolutions[2]; +}; + +static void dac_gd32_enable(uint8_t dacx) +{ + switch (dacx) { + case 0U: + DAC_CTL |= DAC_CTL_DEN0; + break; +#if DT_INST_PROP(0, num_channels) == 2 + case 1U: + DAC_CTL |= DAC_CTL_DEN1; + break; +#endif + } +} + +static void dac_gd32_disable(uint8_t dacx) +{ + switch (dacx) { + case 0U: + DAC_CTL &= ~DAC_CTL_DEN0; + break; +#if DT_INST_PROP(0, num_channels) == 2 + case 1U: + DAC_CTL &= ~DAC_CTL_DEN1; + break; +#endif + } +} + +static void dac_gd32_write(struct dac_gd32_data *data, + uint8_t dacx, uint32_t value) +{ + switch (dacx) { + case 0U: + if (data->resolutions[dacx] == 8U) { + DAC0_R8DH = value; + } else { + DAC0_R12DH = value; + } + break; +#if DT_INST_PROP(0, num_channels) == 2 + case 1U: + if (data->resolutions[dacx] == 8U) { + DAC1_R8DH = value; + } else { + DAC1_R12DH = value; + } + break; +#endif + } +} + +static int dac_gd32_channel_setup(const struct device *dev, + const struct dac_channel_cfg *channel_cfg) +{ + struct dac_gd32_data *data = dev->data; + const struct dac_gd32_config *config = dev->config; + uint8_t dacx = channel_cfg->channel_id; + + if (dacx >= config->num_channels) { + return -ENOTSUP; + } + + /* GD32 DAC only support 8 or 12 bits resolution */ + if ((channel_cfg->resolution != 8U) && + (channel_cfg->resolution != 12U)) { + LOG_ERR("Only 8 and 12 bits resolutions are supported!"); + return -ENOTSUP; + } + + data->resolutions[dacx] = channel_cfg->resolution; + + dac_gd32_disable(dacx); + dac_gd32_write(data, dacx, config->reset_val); + dac_gd32_enable(dacx); + + return 0; +} + +static int dac_gd32_write_value(const struct device *dev, + uint8_t dacx, uint32_t value) +{ + struct dac_gd32_data *data = dev->data; + const struct dac_gd32_config *config = dev->config; + + if (dacx >= config->num_channels) { + return -ENOTSUP; + } + + dac_gd32_write(data, dacx, value); + + return 0; +} + +struct dac_driver_api dac_gd32_driver_api = { + .channel_setup = dac_gd32_channel_setup, + .write_value = dac_gd32_write_value +}; + +static int dac_gd32_init(const struct device *dev) +{ + const struct dac_gd32_config *cfg = dev->config; + int ret; + + ret = pinctrl_apply_state(cfg->pcfg, PINCTRL_STATE_DEFAULT); + if (ret < 0) { + LOG_ERR("Failed to apply pinctrl state"); + return ret; + } + + rcu_periph_clock_enable(cfg->rcu_periph_clock); + + return 0; +} + +PINCTRL_DT_INST_DEFINE(0) + +static struct dac_gd32_data dac_gd32_data_0; + +static const struct dac_gd32_config dac_gd32_cfg_0 = { + .reg = DT_INST_REG_ADDR(0), + .rcu_periph_clock = DT_INST_PROP(0, rcu_periph_clock), + .pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(0), + .num_channels = DT_INST_PROP(0, num_channels), + .reset_val = DT_INST_PROP(0, reset_val), +}; + +DEVICE_DT_INST_DEFINE(0, &dac_gd32_init, NULL, &dac_gd32_data_0, + &dac_gd32_cfg_0, POST_KERNEL, CONFIG_DAC_INIT_PRIORITY, + &dac_gd32_driver_api);