zephyr/drivers/sensor/qdec_sam/qdec_sam.c

141 lines
3.4 KiB
C

/*
* Copyright (c) 2021, Piotr Mienkowski
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT atmel_sam_tc_qdec
/** @file
* @brief Atmel SAM MCU family Quadrature Decoder (QDEC/TC) driver.
*/
#include <errno.h>
#include <zephyr/sys/__assert.h>
#include <zephyr/sys/util.h>
#include <zephyr/device.h>
#include <zephyr/init.h>
#include <soc.h>
#include <zephyr/drivers/sensor.h>
#include <zephyr/drivers/pinctrl.h>
#include <zephyr/drivers/clock_control/atmel_sam_pmc.h>
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(qdec_sam, CONFIG_SENSOR_LOG_LEVEL);
/* Device constant configuration parameters */
struct qdec_sam_dev_cfg {
Tc *regs;
const struct atmel_sam_pmc_config clock_cfg[TCCHANNEL_NUMBER];
const struct pinctrl_dev_config *pcfg;
};
/* Device run time data */
struct qdec_sam_dev_data {
uint16_t position;
};
static int qdec_sam_fetch(const struct device *dev, enum sensor_channel chan)
{
const struct qdec_sam_dev_cfg *const dev_cfg = dev->config;
struct qdec_sam_dev_data *const dev_data = dev->data;
Tc *const tc = dev_cfg->regs;
TcChannel *tc_ch0 = &tc->TcChannel[0];
/* Read position register content */
dev_data->position = tc_ch0->TC_CV;
return 0;
}
static int qdec_sam_get(const struct device *dev, enum sensor_channel chan,
struct sensor_value *val)
{
struct qdec_sam_dev_data *const dev_data = dev->data;
if (chan == SENSOR_CHAN_ROTATION) {
val->val1 = dev_data->position;
val->val2 = 0;
} else {
return -ENOTSUP;
}
return 0;
}
static void qdec_sam_start(Tc *const tc)
{
TcChannel *tc_ch0 = &tc->TcChannel[0];
/* Enable Channel 0 Clock and reset counter*/
tc_ch0->TC_CCR = TC_CCR_CLKEN
| TC_CCR_SWTRG;
}
static void qdec_sam_configure(const struct device *dev)
{
const struct qdec_sam_dev_cfg *const dev_cfg = dev->config;
Tc *const tc = dev_cfg->regs;
TcChannel *tc_ch0 = &tc->TcChannel[0];
/* Clock, Trigger Edge, Trigger and Mode Selection */
tc_ch0->TC_CMR = TC_CMR_TCCLKS_XC0
| TC_CMR_ETRGEDG_NONE
| TC_CMR_ABETRG;
/* Enable QDEC in Position Mode*/
tc->TC_BMR = TC_BMR_QDEN
| TC_BMR_POSEN
| TC_BMR_EDGPHA
| TC_BMR_MAXFILT(1);
qdec_sam_start(tc);
}
static int qdec_sam_initialize(const struct device *dev)
{
__ASSERT_NO_MSG(dev != NULL);
const struct qdec_sam_dev_cfg *const dev_cfg = dev->config;
int retval;
/* Connect pins to the peripheral */
retval = pinctrl_apply_state(dev_cfg->pcfg, PINCTRL_STATE_DEFAULT);
if (retval < 0) {
return retval;
}
for (int i = 0; i < ARRAY_SIZE(dev_cfg->clock_cfg); i++) {
/* Enable TC clock in PMC */
(void)clock_control_on(SAM_DT_PMC_CONTROLLER,
(clock_control_subsys_t)&dev_cfg->clock_cfg[i]);
}
qdec_sam_configure(dev);
LOG_INF("Device %s initialized", dev->name);
return 0;
}
static const struct sensor_driver_api qdec_sam_driver_api = {
.sample_fetch = qdec_sam_fetch,
.channel_get = qdec_sam_get,
};
#define QDEC_SAM_INIT(n) \
PINCTRL_DT_INST_DEFINE(n); \
static const struct qdec_sam_dev_cfg qdec##n##_sam_config = { \
.regs = (Tc *)DT_REG_ADDR(DT_INST_PARENT(n)), \
.pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \
.clock_cfg = SAM_DT_CLOCKS_PMC_CFG(DT_INST_PARENT(n)), \
}; \
\
static struct qdec_sam_dev_data qdec##n##_sam_data; \
\
SENSOR_DEVICE_DT_INST_DEFINE(n, qdec_sam_initialize, NULL, \
&qdec##n##_sam_data, &qdec##n##_sam_config, \
POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY, \
&qdec_sam_driver_api);
DT_INST_FOREACH_STATUS_OKAY(QDEC_SAM_INIT)