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 <sys/__assert.h>
15 #include <sys/util.h>
16 #include <device.h>
17 #include <init.h>
18 #include <soc.h>
19 #include <drivers/sensor.h>
20
21 #include <logging/log.h>
22 LOG_MODULE_REGISTER(qdec_sam, CONFIG_SENSOR_LOG_LEVEL);
23
24 /* Device constant configuration parameters */
25 struct qdec_sam_dev_cfg {
26 Tc *regs;
27 const struct soc_gpio_pin *pin_list;
28 uint8_t pin_list_size;
29 uint8_t periph_id[TCCHANNEL_NUMBER];
30 };
31
32 /* Device run time data */
33 struct qdec_sam_dev_data {
34 uint16_t position;
35 };
36
37 #define DEV_NAME(dev) ((dev)->name)
38 #define DEV_CFG(dev) \
39 ((const struct qdec_sam_dev_cfg *const)(dev)->config)
40 #define DEV_DATA(dev) \
41 ((struct qdec_sam_dev_data *const)(dev)->data)
42
qdec_sam_fetch(const struct device * dev,enum sensor_channel chan)43 static int qdec_sam_fetch(const struct device *dev, enum sensor_channel chan)
44 {
45 const struct qdec_sam_dev_cfg *const dev_cfg = DEV_CFG(dev);
46 struct qdec_sam_dev_data *const dev_data = DEV_DATA(dev);
47 Tc *const tc = dev_cfg->regs;
48 TcChannel *tc_ch0 = &tc->TcChannel[0];
49
50 /* Read position register content */
51 dev_data->position = tc_ch0->TC_CV;
52
53 return 0;
54 }
55
qdec_sam_get(const struct device * dev,enum sensor_channel chan,struct sensor_value * val)56 static int qdec_sam_get(const struct device *dev, enum sensor_channel chan,
57 struct sensor_value *val)
58 {
59 struct qdec_sam_dev_data *const dev_data = DEV_DATA(dev);
60
61 if (chan == SENSOR_CHAN_ROTATION) {
62 val->val1 = dev_data->position;
63 val->val2 = 0;
64 } else {
65 return -ENOTSUP;
66 }
67
68 return 0;
69 }
70
qdec_sam_start(Tc * const tc)71 static void qdec_sam_start(Tc *const tc)
72 {
73 TcChannel *tc_ch0 = &tc->TcChannel[0];
74
75 /* Enable Channel 0 Clock and reset counter*/
76 tc_ch0->TC_CCR = TC_CCR_CLKEN
77 | TC_CCR_SWTRG;
78 }
79
qdec_sam_configure(const struct device * dev)80 static void qdec_sam_configure(const struct device *dev)
81 {
82 const struct qdec_sam_dev_cfg *const dev_cfg = DEV_CFG(dev);
83 Tc *const tc = dev_cfg->regs;
84 TcChannel *tc_ch0 = &tc->TcChannel[0];
85
86 /* Clock, Trigger Edge, Trigger and Mode Selection */
87 tc_ch0->TC_CMR = TC_CMR_TCCLKS_XC0
88 | TC_CMR_ETRGEDG_RISING
89 | TC_CMR_ABETRG;
90
91 /* Enable QDEC in Position Mode*/
92 tc->TC_BMR = TC_BMR_QDEN
93 | TC_BMR_POSEN
94 | TC_BMR_EDGPHA
95 | TC_BMR_MAXFILT(1);
96
97 qdec_sam_start(tc);
98 }
99
qdec_sam_initialize(const struct device * dev)100 static int qdec_sam_initialize(const struct device *dev)
101 {
102 __ASSERT_NO_MSG(dev != NULL);
103 const struct qdec_sam_dev_cfg *const dev_cfg = DEV_CFG(dev);
104
105 /* Connect pins to the peripheral */
106 soc_gpio_list_configure(dev_cfg->pin_list, dev_cfg->pin_list_size);
107
108 for (int i = 0; i < ARRAY_SIZE(dev_cfg->periph_id); i++) {
109 /* Enable module's clock */
110 soc_pmc_peripheral_enable(dev_cfg->periph_id[i]);
111 }
112
113 qdec_sam_configure(dev);
114
115 LOG_INF("Device %s initialized", DEV_NAME(dev));
116
117 return 0;
118 }
119
120 static const struct sensor_driver_api 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 static const struct soc_gpio_pin pins_tc##n[] = ATMEL_SAM_DT_INST_PINS(n); \
127 \
128 static const struct qdec_sam_dev_cfg qdec##n##_sam_config = { \
129 .regs = (Tc *)DT_INST_REG_ADDR(n), \
130 .pin_list = pins_tc##n, \
131 .pin_list_size = ARRAY_SIZE(pins_tc##n), \
132 .periph_id = DT_INST_PROP(n, peripheral_id), \
133 }; \
134 \
135 static struct qdec_sam_dev_data qdec##n##_sam_data; \
136 \
137 DEVICE_DT_INST_DEFINE(n, qdec_sam_initialize, NULL, \
138 &qdec##n##_sam_data, &qdec##n##_sam_config, \
139 POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY, \
140 &qdec_sam_driver_api);
141
142 DT_INST_FOREACH_STATUS_OKAY(QDEC_SAM_INIT)
143