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