/* * Copyright (c) 2020 Google LLC. * * SPDX-License-Identifier: Apache-2.0 */ #define DT_DRV_COMPAT atmel_sam0_dac #include #include #include #include #include LOG_MODULE_REGISTER(dac_sam0, CONFIG_DAC_LOG_LEVEL); /* * Maps between the DTS reference property names and register values. Note that * the ASF uses the 09/2015 names which differ from the 03/2020 datasheet. * * TODO(#21273): replace once improved support for enum values lands. */ #define SAM0_DAC_REFSEL_0 DAC_CTRLB_REFSEL_INT1V_Val #define SAM0_DAC_REFSEL_1 DAC_CTRLB_REFSEL_AVCC_Val #define SAM0_DAC_REFSEL_2 DAC_CTRLB_REFSEL_VREFP_Val struct dac_sam0_cfg { Dac *regs; const struct pinctrl_dev_config *pcfg; uint8_t pm_apbc_bit; uint8_t gclk_clkctrl_id; uint8_t refsel; }; /* Write to the DAC. */ static int dac_sam0_write_value(const struct device *dev, uint8_t channel, uint32_t value) { const struct dac_sam0_cfg *const cfg = dev->config; Dac *regs = cfg->regs; if (value >= BIT(12)) { LOG_ERR("value %d out of range", value); return -EINVAL; } regs->DATA.reg = (uint16_t)value; return 0; } /* * Setup the channel. As the SAM0 has one fixed width channel, this validates * the input and does nothing else. */ static int dac_sam0_channel_setup(const struct device *dev, const struct dac_channel_cfg *channel_cfg) { if (channel_cfg->channel_id != 0) { return -EINVAL; } if (channel_cfg->resolution != 10) { return -ENOTSUP; } if (channel_cfg->internal) { return -ENOSYS; } return 0; } /* Initialise and enable the DAC. */ static int dac_sam0_init(const struct device *dev) { const struct dac_sam0_cfg *const cfg = dev->config; Dac *regs = cfg->regs; int retval; /* Enable the GCLK */ GCLK->CLKCTRL.reg = cfg->gclk_clkctrl_id | GCLK_CLKCTRL_GEN_GCLK0 | GCLK_CLKCTRL_CLKEN; retval = pinctrl_apply_state(cfg->pcfg, PINCTRL_STATE_DEFAULT); if (retval < 0) { return retval; } /* Enable the clock in PM */ PM->APBCMASK.reg |= 1 << cfg->pm_apbc_bit; /* Reset then configure the DAC */ regs->CTRLA.bit.SWRST = 1; while (regs->STATUS.bit.SYNCBUSY) { } regs->CTRLB.bit.REFSEL = cfg->refsel; regs->CTRLB.bit.EOEN = 1; /* Enable */ regs->CTRLA.bit.ENABLE = 1; while (regs->STATUS.bit.SYNCBUSY) { } return 0; } static const struct dac_driver_api api_sam0_driver_api = { .channel_setup = dac_sam0_channel_setup, .write_value = dac_sam0_write_value }; #define SAM0_DAC_REFSEL(n) \ COND_CODE_1(DT_INST_NODE_HAS_PROP(n, reference), \ (DT_INST_ENUM_IDX(n, reference)), (0)) #define SAM0_DAC_INIT(n) \ PINCTRL_DT_INST_DEFINE(n); \ static const struct dac_sam0_cfg dac_sam0_cfg_##n = { \ .regs = (Dac *)DT_INST_REG_ADDR(n), \ .pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \ .pm_apbc_bit = DT_INST_CLOCKS_CELL_BY_NAME(n, pm, bit), \ .gclk_clkctrl_id = \ DT_INST_CLOCKS_CELL_BY_NAME(n, gclk, clkctrl_id), \ .refsel = UTIL_CAT(SAM0_DAC_REFSEL_, SAM0_DAC_REFSEL(n)), \ }; \ \ DEVICE_DT_INST_DEFINE(n, &dac_sam0_init, NULL, NULL, \ &dac_sam0_cfg_##n, POST_KERNEL, \ CONFIG_DAC_INIT_PRIORITY, \ &api_sam0_driver_api) DT_INST_FOREACH_STATUS_OKAY(SAM0_DAC_INIT);