1 /*
2 * Copyright (c) 2021, Piotr Mienkowski
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #define DT_DRV_COMPAT atmel_sam_tc_qdec
8
9 /** @file
10 * @brief Atmel SAM MCU family Quadrature Decoder (QDEC/TC) driver.
11 */
12
13 #include <errno.h>
14 #include <zephyr/sys/__assert.h>
15 #include <zephyr/sys/util.h>
16 #include <zephyr/device.h>
17 #include <zephyr/init.h>
18 #include <soc.h>
19 #include <zephyr/drivers/sensor.h>
20 #include <zephyr/drivers/pinctrl.h>
21 #include <zephyr/drivers/clock_control/atmel_sam_pmc.h>
22
23 #include <zephyr/logging/log.h>
24 LOG_MODULE_REGISTER(qdec_sam, CONFIG_SENSOR_LOG_LEVEL);
25
26 /* Device constant configuration parameters */
27 struct qdec_sam_dev_cfg {
28 Tc *regs;
29 const struct atmel_sam_pmc_config clock_cfg[TCCHANNEL_NUMBER];
30 const struct pinctrl_dev_config *pcfg;
31 };
32
33 /* Device run time data */
34 struct qdec_sam_dev_data {
35 uint16_t position;
36 };
37
qdec_sam_fetch(const struct device * dev,enum sensor_channel chan)38 static int qdec_sam_fetch(const struct device *dev, enum sensor_channel chan)
39 {
40 const struct qdec_sam_dev_cfg *const dev_cfg = dev->config;
41 struct qdec_sam_dev_data *const dev_data = dev->data;
42 Tc *const tc = dev_cfg->regs;
43 TcChannel *tc_ch0 = &tc->TcChannel[0];
44
45 /* Read position register content */
46 dev_data->position = tc_ch0->TC_CV;
47
48 return 0;
49 }
50
qdec_sam_get(const struct device * dev,enum sensor_channel chan,struct sensor_value * val)51 static int qdec_sam_get(const struct device *dev, enum sensor_channel chan,
52 struct sensor_value *val)
53 {
54 struct qdec_sam_dev_data *const dev_data = dev->data;
55
56 if (chan == SENSOR_CHAN_ROTATION) {
57 val->val1 = dev_data->position;
58 val->val2 = 0;
59 } else {
60 return -ENOTSUP;
61 }
62
63 return 0;
64 }
65
qdec_sam_start(Tc * const tc)66 static void qdec_sam_start(Tc *const tc)
67 {
68 TcChannel *tc_ch0 = &tc->TcChannel[0];
69
70 /* Enable Channel 0 Clock and reset counter*/
71 tc_ch0->TC_CCR = TC_CCR_CLKEN
72 | TC_CCR_SWTRG;
73 }
74
qdec_sam_configure(const struct device * dev)75 static void qdec_sam_configure(const struct device *dev)
76 {
77 const struct qdec_sam_dev_cfg *const dev_cfg = dev->config;
78 Tc *const tc = dev_cfg->regs;
79 TcChannel *tc_ch0 = &tc->TcChannel[0];
80
81 /* Clock, Trigger Edge, Trigger and Mode Selection */
82 tc_ch0->TC_CMR = TC_CMR_TCCLKS_XC0
83 | TC_CMR_ETRGEDG_NONE
84 | TC_CMR_ABETRG;
85
86 /* Enable QDEC in Position Mode*/
87 tc->TC_BMR = TC_BMR_QDEN
88 | TC_BMR_POSEN
89 | TC_BMR_EDGPHA
90 | TC_BMR_MAXFILT(1);
91
92 qdec_sam_start(tc);
93 }
94
qdec_sam_initialize(const struct device * dev)95 static int qdec_sam_initialize(const struct device *dev)
96 {
97 __ASSERT_NO_MSG(dev != NULL);
98 const struct qdec_sam_dev_cfg *const dev_cfg = dev->config;
99 int retval;
100
101 /* Connect pins to the peripheral */
102 retval = pinctrl_apply_state(dev_cfg->pcfg, PINCTRL_STATE_DEFAULT);
103 if (retval < 0) {
104 return retval;
105 }
106
107 for (int i = 0; i < ARRAY_SIZE(dev_cfg->clock_cfg); i++) {
108 /* Enable TC clock in PMC */
109 (void)clock_control_on(SAM_DT_PMC_CONTROLLER,
110 (clock_control_subsys_t)&dev_cfg->clock_cfg[i]);
111 }
112
113 qdec_sam_configure(dev);
114
115 LOG_INF("Device %s initialized", dev->name);
116
117 return 0;
118 }
119
120 static DEVICE_API(sensor, qdec_sam_driver_api) = {
121 .sample_fetch = qdec_sam_fetch,
122 .channel_get = qdec_sam_get,
123 };
124
125 #define QDEC_SAM_INIT(n) \
126 PINCTRL_DT_INST_DEFINE(n); \
127 static const struct qdec_sam_dev_cfg qdec##n##_sam_config = { \
128 .regs = (Tc *)DT_REG_ADDR(DT_INST_PARENT(n)), \
129 .pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \
130 .clock_cfg = SAM_DT_CLOCKS_PMC_CFG(DT_INST_PARENT(n)), \
131 }; \
132 \
133 static struct qdec_sam_dev_data qdec##n##_sam_data; \
134 \
135 SENSOR_DEVICE_DT_INST_DEFINE(n, qdec_sam_initialize, NULL, \
136 &qdec##n##_sam_data, &qdec##n##_sam_config, \
137 POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY, \
138 &qdec_sam_driver_api);
139
140 DT_INST_FOREACH_STATUS_OKAY(QDEC_SAM_INIT)
141