xref: /openbmc/linux/drivers/counter/ftm-quaddec.c (revision fe7498ef)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Flex Timer Module Quadrature decoder
4  *
5  * This module implements a driver for decoding the FTM quadrature
6  * of ex. a LS1021A
7  */
8 
9 #include <linux/fsl/ftm.h>
10 #include <linux/module.h>
11 #include <linux/platform_device.h>
12 #include <linux/of.h>
13 #include <linux/io.h>
14 #include <linux/mutex.h>
15 #include <linux/counter.h>
16 #include <linux/bitfield.h>
17 #include <linux/types.h>
18 
19 #define FTM_FIELD_UPDATE(ftm, offset, mask, val)			\
20 	({								\
21 		uint32_t flags;						\
22 		ftm_read(ftm, offset, &flags);				\
23 		flags &= ~mask;						\
24 		flags |= FIELD_PREP(mask, val);				\
25 		ftm_write(ftm, offset, flags);				\
26 	})
27 
28 struct ftm_quaddec {
29 	struct counter_device counter;
30 	struct platform_device *pdev;
31 	void __iomem *ftm_base;
32 	bool big_endian;
33 	struct mutex ftm_quaddec_mutex;
34 };
35 
36 static void ftm_read(struct ftm_quaddec *ftm, uint32_t offset, uint32_t *data)
37 {
38 	if (ftm->big_endian)
39 		*data = ioread32be(ftm->ftm_base + offset);
40 	else
41 		*data = ioread32(ftm->ftm_base + offset);
42 }
43 
44 static void ftm_write(struct ftm_quaddec *ftm, uint32_t offset, uint32_t data)
45 {
46 	if (ftm->big_endian)
47 		iowrite32be(data, ftm->ftm_base + offset);
48 	else
49 		iowrite32(data, ftm->ftm_base + offset);
50 }
51 
52 /* Hold mutex before modifying write protection state */
53 static void ftm_clear_write_protection(struct ftm_quaddec *ftm)
54 {
55 	uint32_t flag;
56 
57 	/* First see if it is enabled */
58 	ftm_read(ftm, FTM_FMS, &flag);
59 
60 	if (flag & FTM_FMS_WPEN)
61 		FTM_FIELD_UPDATE(ftm, FTM_MODE, FTM_MODE_WPDIS, 1);
62 }
63 
64 static void ftm_set_write_protection(struct ftm_quaddec *ftm)
65 {
66 	FTM_FIELD_UPDATE(ftm, FTM_FMS, FTM_FMS_WPEN, 1);
67 }
68 
69 static void ftm_reset_counter(struct ftm_quaddec *ftm)
70 {
71 	/* Reset hardware counter to CNTIN */
72 	ftm_write(ftm, FTM_CNT, 0x0);
73 }
74 
75 static void ftm_quaddec_init(struct ftm_quaddec *ftm)
76 {
77 	ftm_clear_write_protection(ftm);
78 
79 	/*
80 	 * Do not write in the region from the CNTIN register through the
81 	 * PWMLOAD register when FTMEN = 0.
82 	 * Also reset other fields to zero
83 	 */
84 	ftm_write(ftm, FTM_MODE, FTM_MODE_FTMEN);
85 	ftm_write(ftm, FTM_CNTIN, 0x0000);
86 	ftm_write(ftm, FTM_MOD, 0xffff);
87 	ftm_write(ftm, FTM_CNT, 0x0);
88 	/* Set prescaler, reset other fields to zero */
89 	ftm_write(ftm, FTM_SC, FTM_SC_PS_1);
90 
91 	/* Select quad mode, reset other fields to zero */
92 	ftm_write(ftm, FTM_QDCTRL, FTM_QDCTRL_QUADEN);
93 
94 	/* Unused features and reset to default section */
95 	ftm_write(ftm, FTM_POL, 0x0);
96 	ftm_write(ftm, FTM_FLTCTRL, 0x0);
97 	ftm_write(ftm, FTM_SYNCONF, 0x0);
98 	ftm_write(ftm, FTM_SYNC, 0xffff);
99 
100 	/* Lock the FTM */
101 	ftm_set_write_protection(ftm);
102 }
103 
104 static void ftm_quaddec_disable(void *ftm)
105 {
106 	struct ftm_quaddec *ftm_qua = ftm;
107 
108 	ftm_clear_write_protection(ftm_qua);
109 	ftm_write(ftm_qua, FTM_MODE, 0);
110 	ftm_write(ftm_qua, FTM_QDCTRL, 0);
111 	/*
112 	 * This is enough to disable the counter. No clock has been
113 	 * selected by writing to FTM_SC in init()
114 	 */
115 	ftm_set_write_protection(ftm_qua);
116 }
117 
118 static int ftm_quaddec_get_prescaler(struct counter_device *counter,
119 				     struct counter_count *count, u32 *cnt_mode)
120 {
121 	struct ftm_quaddec *ftm = counter->priv;
122 	uint32_t scflags;
123 
124 	ftm_read(ftm, FTM_SC, &scflags);
125 
126 	*cnt_mode = FIELD_GET(FTM_SC_PS_MASK, scflags);
127 
128 	return 0;
129 }
130 
131 static int ftm_quaddec_set_prescaler(struct counter_device *counter,
132 				     struct counter_count *count, u32 cnt_mode)
133 {
134 	struct ftm_quaddec *ftm = counter->priv;
135 
136 	mutex_lock(&ftm->ftm_quaddec_mutex);
137 
138 	ftm_clear_write_protection(ftm);
139 	FTM_FIELD_UPDATE(ftm, FTM_SC, FTM_SC_PS_MASK, cnt_mode);
140 	ftm_set_write_protection(ftm);
141 
142 	/* Also resets the counter as it is undefined anyway now */
143 	ftm_reset_counter(ftm);
144 
145 	mutex_unlock(&ftm->ftm_quaddec_mutex);
146 	return 0;
147 }
148 
149 static const char * const ftm_quaddec_prescaler[] = {
150 	"1", "2", "4", "8", "16", "32", "64", "128"
151 };
152 
153 static const enum counter_synapse_action ftm_quaddec_synapse_actions[] = {
154 	COUNTER_SYNAPSE_ACTION_BOTH_EDGES
155 };
156 
157 static const enum counter_function ftm_quaddec_count_functions[] = {
158 	COUNTER_FUNCTION_QUADRATURE_X4
159 };
160 
161 static int ftm_quaddec_count_read(struct counter_device *counter,
162 				  struct counter_count *count,
163 				  u64 *val)
164 {
165 	struct ftm_quaddec *const ftm = counter->priv;
166 	uint32_t cntval;
167 
168 	ftm_read(ftm, FTM_CNT, &cntval);
169 
170 	*val = cntval;
171 
172 	return 0;
173 }
174 
175 static int ftm_quaddec_count_write(struct counter_device *counter,
176 				   struct counter_count *count,
177 				   const u64 val)
178 {
179 	struct ftm_quaddec *const ftm = counter->priv;
180 
181 	if (val != 0) {
182 		dev_warn(&ftm->pdev->dev, "Can only accept '0' as new counter value\n");
183 		return -EINVAL;
184 	}
185 
186 	ftm_reset_counter(ftm);
187 
188 	return 0;
189 }
190 
191 static int ftm_quaddec_count_function_read(struct counter_device *counter,
192 					   struct counter_count *count,
193 					   enum counter_function *function)
194 {
195 	*function = COUNTER_FUNCTION_QUADRATURE_X4;
196 
197 	return 0;
198 }
199 
200 static int ftm_quaddec_action_read(struct counter_device *counter,
201 				   struct counter_count *count,
202 				   struct counter_synapse *synapse,
203 				   enum counter_synapse_action *action)
204 {
205 	*action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES;
206 
207 	return 0;
208 }
209 
210 static const struct counter_ops ftm_quaddec_cnt_ops = {
211 	.count_read = ftm_quaddec_count_read,
212 	.count_write = ftm_quaddec_count_write,
213 	.function_read = ftm_quaddec_count_function_read,
214 	.action_read = ftm_quaddec_action_read,
215 };
216 
217 static struct counter_signal ftm_quaddec_signals[] = {
218 	{
219 		.id = 0,
220 		.name = "Channel 1 Phase A"
221 	},
222 	{
223 		.id = 1,
224 		.name = "Channel 1 Phase B"
225 	}
226 };
227 
228 static struct counter_synapse ftm_quaddec_count_synapses[] = {
229 	{
230 		.actions_list = ftm_quaddec_synapse_actions,
231 		.num_actions = ARRAY_SIZE(ftm_quaddec_synapse_actions),
232 		.signal = &ftm_quaddec_signals[0]
233 	},
234 	{
235 		.actions_list = ftm_quaddec_synapse_actions,
236 		.num_actions = ARRAY_SIZE(ftm_quaddec_synapse_actions),
237 		.signal = &ftm_quaddec_signals[1]
238 	}
239 };
240 
241 static DEFINE_COUNTER_ENUM(ftm_quaddec_prescaler_enum, ftm_quaddec_prescaler);
242 
243 static struct counter_comp ftm_quaddec_count_ext[] = {
244 	COUNTER_COMP_COUNT_ENUM("prescaler", ftm_quaddec_get_prescaler,
245 				ftm_quaddec_set_prescaler,
246 				ftm_quaddec_prescaler_enum),
247 };
248 
249 static struct counter_count ftm_quaddec_counts = {
250 	.id = 0,
251 	.name = "Channel 1 Count",
252 	.functions_list = ftm_quaddec_count_functions,
253 	.num_functions = ARRAY_SIZE(ftm_quaddec_count_functions),
254 	.synapses = ftm_quaddec_count_synapses,
255 	.num_synapses = ARRAY_SIZE(ftm_quaddec_count_synapses),
256 	.ext = ftm_quaddec_count_ext,
257 	.num_ext = ARRAY_SIZE(ftm_quaddec_count_ext)
258 };
259 
260 static int ftm_quaddec_probe(struct platform_device *pdev)
261 {
262 	struct ftm_quaddec *ftm;
263 
264 	struct device_node *node = pdev->dev.of_node;
265 	struct resource *io;
266 	int ret;
267 
268 	ftm = devm_kzalloc(&pdev->dev, sizeof(*ftm), GFP_KERNEL);
269 	if (!ftm)
270 		return -ENOMEM;
271 
272 	platform_set_drvdata(pdev, ftm);
273 
274 	io = platform_get_resource(pdev, IORESOURCE_MEM, 0);
275 	if (!io) {
276 		dev_err(&pdev->dev, "Failed to get memory region\n");
277 		return -ENODEV;
278 	}
279 
280 	ftm->pdev = pdev;
281 	ftm->big_endian = of_property_read_bool(node, "big-endian");
282 	ftm->ftm_base = devm_ioremap(&pdev->dev, io->start, resource_size(io));
283 
284 	if (!ftm->ftm_base) {
285 		dev_err(&pdev->dev, "Failed to map memory region\n");
286 		return -EINVAL;
287 	}
288 	ftm->counter.name = dev_name(&pdev->dev);
289 	ftm->counter.parent = &pdev->dev;
290 	ftm->counter.ops = &ftm_quaddec_cnt_ops;
291 	ftm->counter.counts = &ftm_quaddec_counts;
292 	ftm->counter.num_counts = 1;
293 	ftm->counter.signals = ftm_quaddec_signals;
294 	ftm->counter.num_signals = ARRAY_SIZE(ftm_quaddec_signals);
295 	ftm->counter.priv = ftm;
296 
297 	mutex_init(&ftm->ftm_quaddec_mutex);
298 
299 	ftm_quaddec_init(ftm);
300 
301 	ret = devm_add_action_or_reset(&pdev->dev, ftm_quaddec_disable, ftm);
302 	if (ret)
303 		return ret;
304 
305 	ret = devm_counter_register(&pdev->dev, &ftm->counter);
306 	if (ret)
307 		return ret;
308 
309 	return 0;
310 }
311 
312 static const struct of_device_id ftm_quaddec_match[] = {
313 	{ .compatible = "fsl,ftm-quaddec" },
314 	{},
315 };
316 
317 static struct platform_driver ftm_quaddec_driver = {
318 	.driver = {
319 		.name = "ftm-quaddec",
320 		.of_match_table = ftm_quaddec_match,
321 	},
322 	.probe = ftm_quaddec_probe,
323 };
324 
325 module_platform_driver(ftm_quaddec_driver);
326 
327 MODULE_LICENSE("GPL");
328 MODULE_AUTHOR("Kjeld Flarup <kfa@deif.com>");
329 MODULE_AUTHOR("Patrick Havelange <patrick.havelange@essensium.com>");
330