zephyr/drivers/counter/counter_mchp_xec.c
Martí Bolívar 7e0eed9235 devicetree: allow access to all nodes
Usually, we want to operate only on "available" device
nodes ("available" means "status is okay and a matching binding is
found"), but that's not true in all cases.

Sometimes we want to operate on special nodes without matching
bindings, such as those describing memory.

To handle the distinction, change various additional devicetree APIs
making it clear that they operate only on available device nodes,
adjusting gen_defines and devicetree.h implementation details
accordingly:

- emit macros for all existing nodes in gen_defines.py, regardless
  of status or matching binding
- rename DT_NUM_INST to DT_NUM_INST_STATUS_OKAY
- rename DT_NODE_HAS_COMPAT to DT_NODE_HAS_COMPAT_STATUS_OKAY
- rename DT_INST_FOREACH to DT_INST_FOREACH_STATUS_OKAY
- rename DT_ANY_INST_ON_BUS to DT_ANY_INST_ON_BUS_STATUS_OKAY
- rewrite DT_HAS_NODE_STATUS_OKAY in terms of a new DT_NODE_HAS_STATUS
- resurrect DT_HAS_NODE in the form of DT_NODE_EXISTS
- remove DT_COMPAT_ON_BUS as a public API
- use the new default_prop_types edtlib parameter

Signed-off-by: Martí Bolívar <marti.bolivar@nordicsemi.no>
2020-05-08 19:37:18 -05:00

351 lines
8.5 KiB
C

/*
* Copyright (c) 2019 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT microchip_xec_timer
/**
* @file
* @brief Microchip XEC Counter driver
*
* This is the driver for the 16/32-bit counters on the Microchip SoCs.
*
* Notes:
* - The counters are running in down counting mode.
* - Interrupts are triggered (if enabled) when the counter
* reaches zero.
* - These are not free running counters where there are separate
* compare values for interrupts. When setting single shot alarms,
* the counter values are changed so that interrupts are triggered
* when the counters reach zero.
*/
#include <logging/log.h>
LOG_MODULE_REGISTER(counter_mchp_xec, CONFIG_COUNTER_LOG_LEVEL);
#include <drivers/counter.h>
#include <soc.h>
#include <errno.h>
#include <stdbool.h>
struct counter_xec_config {
struct counter_config_info info;
void (*config_func)(void);
u32_t base_address;
u16_t prescaler;
u8_t girq_id;
u8_t girq_bit;
};
struct counter_xec_data {
counter_alarm_callback_t alarm_cb;
counter_top_callback_t top_cb;
void *user_data;
};
#define COUNTER_XEC_REG_BASE(_dev) \
((BTMR_Type *) \
((const struct counter_xec_config * const) \
_dev->config_info)->base_address)
#define COUNTER_XEC_CONFIG(_dev) \
(((const struct counter_xec_config * const) \
_dev->config_info))
#define COUNTER_XEC_DATA(_dev) \
((struct counter_xec_data *)dev->driver_data)
static int counter_xec_start(struct device *dev)
{
BTMR_Type *counter = COUNTER_XEC_REG_BASE(dev);
if (counter->CTRL & MCHP_BTMR_CTRL_ENABLE) {
return -EALREADY;
}
counter->CTRL |= (MCHP_BTMR_CTRL_ENABLE | MCHP_BTMR_CTRL_START);
LOG_DBG("%p Counter started", dev);
return 0;
}
static int counter_xec_stop(struct device *dev)
{
BTMR_Type *counter = COUNTER_XEC_REG_BASE(dev);
uint32_t reg;
if (!(counter->CTRL & MCHP_BTMR_CTRL_ENABLE)) {
/* Already stopped, nothing to do */
return 0;
}
reg = counter->CTRL;
reg &= ~MCHP_BTMR_CTRL_ENABLE;
reg &= ~MCHP_BTMR_CTRL_START;
reg &= ~MCHP_BTMR_CTRL_HALT;
reg &= ~MCHP_BTMR_CTRL_RELOAD;
reg &= ~MCHP_BTMR_CTRL_AUTO_RESTART;
counter->CTRL = reg;
counter->IEN = MCHP_BTMR_INTDIS;
counter->CNT = counter->PRLD;
LOG_DBG("%p Counter stopped", dev);
return 0;
}
static int counter_xec_get_value(struct device *dev, u32_t *ticks)
{
BTMR_Type *counter = COUNTER_XEC_REG_BASE(dev);
*ticks = counter->CNT;
return 0;
}
static int counter_xec_set_alarm(struct device *dev, u8_t chan_id,
const struct counter_alarm_cfg *alarm_cfg)
{
BTMR_Type *counter = COUNTER_XEC_REG_BASE(dev);
struct counter_xec_data *data = COUNTER_XEC_DATA(dev);
if (chan_id != 0) {
LOG_ERR("Invalid channel id %u", chan_id);
return -ENOTSUP;
}
/* Interrupts are only triggered when the counter reaches 0.
* So only relative alarms are supported.
*/
if (alarm_cfg->flags & COUNTER_ALARM_CFG_ABSOLUTE) {
return -ENOTSUP;
}
if (data->alarm_cb != NULL) {
return -EBUSY;
}
if (!alarm_cfg->callback) {
return -EINVAL;
}
if (alarm_cfg->ticks > counter->PRLD) {
return -EINVAL;
}
counter->CNT = alarm_cfg->ticks;
data->alarm_cb = alarm_cfg->callback;
data->user_data = alarm_cfg->user_data;
counter->IEN = MCHP_BTMR_INTEN;
LOG_DBG("%p Counter alarm set to %u ticks", dev, alarm_cfg->ticks);
counter->CTRL |= MCHP_BTMR_CTRL_START;
return 0;
}
static int counter_xec_cancel_alarm(struct device *dev, u8_t chan_id)
{
BTMR_Type *counter = COUNTER_XEC_REG_BASE(dev);
struct counter_xec_data *data = COUNTER_XEC_DATA(dev);
if (chan_id != 0) {
LOG_ERR("Invalid channel id %u", chan_id);
return -ENOTSUP;
}
counter->CTRL &= ~MCHP_BTMR_CTRL_START;
counter->IEN = MCHP_BTMR_INTDIS;
data->alarm_cb = NULL;
data->user_data = NULL;
LOG_DBG("%p Counter alarm canceled", dev);
return 0;
}
static u32_t counter_xec_get_pending_int(struct device *dev)
{
BTMR_Type *counter = COUNTER_XEC_REG_BASE(dev);
return counter->STS;
}
static u32_t counter_xec_get_top_value(struct device *dev)
{
BTMR_Type *counter = COUNTER_XEC_REG_BASE(dev);
return counter->PRLD;
}
static int counter_xec_set_top_value(struct device *dev,
const struct counter_top_cfg *cfg)
{
BTMR_Type *counter = COUNTER_XEC_REG_BASE(dev);
const struct counter_xec_config *counter_cfg = COUNTER_XEC_CONFIG(dev);
struct counter_xec_data *data = COUNTER_XEC_DATA(dev);
int ret = 0;
bool restart;
if (data->alarm_cb) {
return -EBUSY;
}
if (cfg->ticks > counter_cfg->info.max_top_value) {
return -EINVAL;
}
restart = ((counter->CTRL & MCHP_BTMR_CTRL_START) != 0U);
counter->CTRL &= ~MCHP_BTMR_CTRL_START;
if (cfg->flags & COUNTER_TOP_CFG_DONT_RESET) {
if (counter->CNT > cfg->ticks) {
ret = -ETIME;
if (cfg->flags & COUNTER_TOP_CFG_RESET_WHEN_LATE) {
counter->CNT = cfg->ticks;
}
}
} else {
counter->CNT = cfg->ticks;
}
counter->PRLD = cfg->ticks;
data->top_cb = cfg->callback;
data->user_data = cfg->user_data;
if (data->top_cb) {
counter->IEN = MCHP_BTMR_INTEN;
counter->CTRL |= MCHP_BTMR_CTRL_AUTO_RESTART;
} else {
counter->IEN = MCHP_BTMR_INTDIS;
counter->CTRL &= ~MCHP_BTMR_CTRL_AUTO_RESTART;
}
LOG_DBG("%p Counter top value was set to %u", dev, cfg->ticks);
if (restart) {
counter->CTRL |= MCHP_BTMR_CTRL_START;
}
return ret;
}
static u32_t counter_xec_get_max_relative_alarm(struct device *dev)
{
const struct counter_xec_config *counter_cfg = COUNTER_XEC_CONFIG(dev);
return counter_cfg->info.max_top_value;
}
static void counter_xec_isr(struct device *dev)
{
BTMR_Type *counter = COUNTER_XEC_REG_BASE(dev);
const struct counter_xec_config *counter_cfg = COUNTER_XEC_CONFIG(dev);
struct counter_xec_data *data = COUNTER_XEC_DATA(dev);
counter_alarm_callback_t alarm_cb;
void *user_data;
counter->STS = MCHP_BTMR_STS_ACTIVE;
MCHP_GIRQ_SRC(counter_cfg->girq_id) = BIT(counter_cfg->girq_bit);
LOG_DBG("%p Counter ISR", dev);
if (data->alarm_cb) {
/* Alarm is one-shot, so disable interrupt and callback */
counter->IEN = MCHP_BTMR_INTDIS;
alarm_cb = data->alarm_cb;
data->alarm_cb = NULL;
user_data = data->user_data;
alarm_cb(dev, 0, counter->CNT, user_data);
} else if (data->top_cb) {
data->top_cb(dev, data->user_data);
}
}
static const struct counter_driver_api counter_xec_api = {
.start = counter_xec_start,
.stop = counter_xec_stop,
.get_value = counter_xec_get_value,
.set_alarm = counter_xec_set_alarm,
.cancel_alarm = counter_xec_cancel_alarm,
.set_top_value = counter_xec_set_top_value,
.get_pending_int = counter_xec_get_pending_int,
.get_top_value = counter_xec_get_top_value,
.get_max_relative_alarm = counter_xec_get_max_relative_alarm,
};
static int counter_xec_init(struct device *dev)
{
BTMR_Type *counter = COUNTER_XEC_REG_BASE(dev);
const struct counter_xec_config *counter_cfg = COUNTER_XEC_CONFIG(dev);
counter_xec_stop(dev);
counter->CTRL &= ~MCHP_BTMR_CTRL_COUNT_UP;
counter->CTRL |= (counter_cfg->prescaler << MCHP_BTMR_CTRL_PRESCALE_POS) &
MCHP_BTMR_CTRL_PRESCALE_MASK;
/* Set preload and actually pre-load the counter */
counter->PRLD = counter_cfg->info.max_top_value;
counter->CNT = counter_cfg->info.max_top_value;
MCHP_GIRQ_ENSET(counter_cfg->girq_id) = BIT(counter_cfg->girq_bit);
counter_cfg->config_func();
return 0;
}
#define COUNTER_XEC_INIT(inst) \
static void counter_xec_irq_config_##inst(void); \
\
static struct counter_xec_data counter_xec_dev_data_##inst; \
\
static struct counter_xec_config counter_xec_dev_config_##inst = { \
.info = { \
.max_top_value = DT_INST_PROP(inst, max_value), \
.freq = DT_INST_PROP(inst, clock_frequency) / \
(1 << DT_INST_PROP(inst, prescaler)), \
.flags = 0, \
.channels = 1, \
}, \
\
.config_func = counter_xec_irq_config_##inst, \
.base_address = DT_INST_REG_ADDR(inst), \
.prescaler = DT_INST_PROP(inst, prescaler), \
.girq_id = DT_INST_PROP(inst, girq), \
.girq_bit = DT_INST_PROP(inst, girq_bit), \
}; \
\
DEVICE_AND_API_INIT(counter_xec_##inst, DT_INST_LABEL(inst), \
counter_xec_init, \
&counter_xec_dev_data_##inst, \
&counter_xec_dev_config_##inst, \
POST_KERNEL, \
CONFIG_KERNEL_INIT_PRIORITY_DEVICE, \
&counter_xec_api); \
\
static void counter_xec_irq_config_##inst(void) \
{ \
IRQ_CONNECT(DT_INST_IRQN(inst), \
DT_INST_IRQ(inst, priority), \
counter_xec_isr, \
DEVICE_GET(counter_xec_##inst), 0); \
irq_enable(DT_INST_IRQN(inst)); \
}
DT_INST_FOREACH_STATUS_OKAY(COUNTER_XEC_INIT)