2020-01-10 17:20:57 +08:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2020 Libre Solar Technologies GmbH
|
|
|
|
*
|
|
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
|
|
*/
|
|
|
|
|
|
|
|
#define DT_DRV_COMPAT st_stm32_dac
|
|
|
|
|
|
|
|
#include <errno.h>
|
|
|
|
|
|
|
|
#include <drivers/dac.h>
|
|
|
|
#include <device.h>
|
|
|
|
#include <kernel.h>
|
|
|
|
#include <init.h>
|
|
|
|
#include <soc.h>
|
|
|
|
|
|
|
|
#define LOG_LEVEL CONFIG_DAC_LOG_LEVEL
|
|
|
|
#include <logging/log.h>
|
|
|
|
LOG_MODULE_REGISTER(dac_stm32);
|
|
|
|
|
|
|
|
#include <drivers/clock_control/stm32_clock_control.h>
|
|
|
|
|
|
|
|
/* some low-end MCUs have DAC with only one channel */
|
|
|
|
#ifdef LL_DAC_CHANNEL_2
|
|
|
|
#define STM32_CHANNEL_COUNT 2
|
|
|
|
#else
|
|
|
|
#define STM32_CHANNEL_COUNT 1
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* first channel always named 1 */
|
|
|
|
#define STM32_FIRST_CHANNEL 1
|
|
|
|
|
|
|
|
#define CHAN(n) LL_DAC_CHANNEL_##n
|
2020-05-28 00:26:57 +08:00
|
|
|
static const uint32_t table_channels[] = {
|
2020-01-10 17:20:57 +08:00
|
|
|
CHAN(1),
|
|
|
|
#ifdef LL_DAC_CHANNEL_2
|
|
|
|
CHAN(2),
|
|
|
|
#endif
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Read-only driver configuration */
|
|
|
|
struct dac_stm32_cfg {
|
|
|
|
DAC_TypeDef *base;
|
|
|
|
struct stm32_pclken pclken;
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Runtime driver data */
|
|
|
|
struct dac_stm32_data {
|
2020-05-28 00:26:57 +08:00
|
|
|
uint8_t channel_count;
|
|
|
|
uint8_t resolution;
|
2020-01-10 17:20:57 +08:00
|
|
|
};
|
|
|
|
|
2020-05-01 02:33:38 +08:00
|
|
|
static int dac_stm32_write_value(const struct device *dev,
|
2020-05-28 00:26:57 +08:00
|
|
|
uint8_t channel, uint32_t value)
|
2020-01-10 17:20:57 +08:00
|
|
|
{
|
2020-05-29 03:23:02 +08:00
|
|
|
struct dac_stm32_data *data = dev->data;
|
2020-05-29 02:44:16 +08:00
|
|
|
const struct dac_stm32_cfg *cfg = dev->config;
|
2020-01-10 17:20:57 +08:00
|
|
|
|
|
|
|
if (channel - STM32_FIRST_CHANNEL >= data->channel_count ||
|
|
|
|
channel < STM32_FIRST_CHANNEL) {
|
|
|
|
LOG_ERR("Channel %d is not valid", channel);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (data->resolution == 8) {
|
|
|
|
LL_DAC_ConvertData8RightAligned(cfg->base,
|
|
|
|
table_channels[channel - STM32_FIRST_CHANNEL], value);
|
|
|
|
} else if (data->resolution == 12) {
|
|
|
|
LL_DAC_ConvertData12RightAligned(cfg->base,
|
|
|
|
table_channels[channel - STM32_FIRST_CHANNEL], value);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-05-01 02:33:38 +08:00
|
|
|
static int dac_stm32_channel_setup(const struct device *dev,
|
2020-01-10 17:20:57 +08:00
|
|
|
const struct dac_channel_cfg *channel_cfg)
|
|
|
|
{
|
2020-05-29 03:23:02 +08:00
|
|
|
struct dac_stm32_data *data = dev->data;
|
2020-05-29 02:44:16 +08:00
|
|
|
const struct dac_stm32_cfg *cfg = dev->config;
|
2020-01-10 17:20:57 +08:00
|
|
|
|
|
|
|
if ((channel_cfg->channel_id - STM32_FIRST_CHANNEL >=
|
|
|
|
data->channel_count) ||
|
|
|
|
(channel_cfg->channel_id < STM32_FIRST_CHANNEL)) {
|
|
|
|
LOG_ERR("Channel %d is not valid", channel_cfg->channel_id);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((channel_cfg->resolution == 8) ||
|
|
|
|
(channel_cfg->resolution == 12)) {
|
|
|
|
data->resolution = channel_cfg->resolution;
|
|
|
|
} else {
|
|
|
|
LOG_ERR("Resolution not supported");
|
|
|
|
return -ENOTSUP;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* enable output buffer by default */
|
|
|
|
LL_DAC_SetOutputBuffer(cfg->base,
|
|
|
|
table_channels[channel_cfg->channel_id - STM32_FIRST_CHANNEL],
|
|
|
|
LL_DAC_OUTPUT_BUFFER_ENABLE);
|
|
|
|
|
|
|
|
LL_DAC_Enable(cfg->base,
|
|
|
|
table_channels[channel_cfg->channel_id - STM32_FIRST_CHANNEL]);
|
|
|
|
|
|
|
|
LOG_DBG("Channel setup succeeded!");
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-05-01 02:33:38 +08:00
|
|
|
static int dac_stm32_init(const struct device *dev)
|
2020-01-10 17:20:57 +08:00
|
|
|
{
|
2020-05-29 02:44:16 +08:00
|
|
|
const struct dac_stm32_cfg *cfg = dev->config;
|
2020-01-10 17:20:57 +08:00
|
|
|
|
|
|
|
/* enable clock for subsystem */
|
2020-05-01 02:33:38 +08:00
|
|
|
const struct device *clk =
|
2020-01-10 17:20:57 +08:00
|
|
|
device_get_binding(STM32_CLOCK_CONTROL_NAME);
|
|
|
|
|
|
|
|
if (clock_control_on(clk,
|
|
|
|
(clock_control_subsys_t *) &cfg->pclken) != 0) {
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct dac_driver_api api_stm32_driver_api = {
|
|
|
|
.channel_setup = dac_stm32_channel_setup,
|
|
|
|
.write_value = dac_stm32_write_value
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
#define STM32_DAC_INIT(index) \
|
|
|
|
\
|
|
|
|
static const struct dac_stm32_cfg dac_stm32_cfg_##index = { \
|
|
|
|
.base = (DAC_TypeDef *)DT_INST_REG_ADDR(index), \
|
|
|
|
.pclken = { \
|
|
|
|
.enr = DT_INST_CLOCKS_CELL(index, bits), \
|
|
|
|
.bus = DT_INST_CLOCKS_CELL(index, bus), \
|
|
|
|
}, \
|
|
|
|
}; \
|
|
|
|
static struct dac_stm32_data dac_stm32_data_##index = { \
|
|
|
|
.channel_count = STM32_CHANNEL_COUNT \
|
|
|
|
}; \
|
|
|
|
\
|
|
|
|
DEVICE_AND_API_INIT(dac_##index, DT_INST_LABEL(index), \
|
|
|
|
&dac_stm32_init, &dac_stm32_data_##index, \
|
|
|
|
&dac_stm32_cfg_##index, POST_KERNEL, \
|
|
|
|
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, \
|
2020-05-06 07:06:32 +08:00
|
|
|
&api_stm32_driver_api);
|
2020-01-10 17:20:57 +08:00
|
|
|
|
2020-05-07 02:23:07 +08:00
|
|
|
DT_INST_FOREACH_STATUS_OKAY(STM32_DAC_INIT)
|