1 // SPDX-License-Identifier: GPL-2.0
3 * Flex Timer Module Quadrature decoder
5 * This module implements a driver for decoding the FTM quadrature
9 #include <linux/fsl/ftm.h>
10 #include <linux/module.h>
11 #include <linux/platform_device.h>
14 #include <linux/mutex.h>
15 #include <linux/counter.h>
16 #include <linux/bitfield.h>
18 #define FTM_FIELD_UPDATE(ftm, offset, mask, val) \
21 ftm_read(ftm, offset, &flags); \
23 flags |= FIELD_PREP(mask, val); \
24 ftm_write(ftm, offset, flags); \
28 struct counter_device counter
;
29 struct platform_device
*pdev
;
30 void __iomem
*ftm_base
;
32 struct mutex ftm_quaddec_mutex
;
35 static void ftm_read(struct ftm_quaddec
*ftm
, uint32_t offset
, uint32_t *data
)
38 *data
= ioread32be(ftm
->ftm_base
+ offset
);
40 *data
= ioread32(ftm
->ftm_base
+ offset
);
43 static void ftm_write(struct ftm_quaddec
*ftm
, uint32_t offset
, uint32_t data
)
46 iowrite32be(data
, ftm
->ftm_base
+ offset
);
48 iowrite32(data
, ftm
->ftm_base
+ offset
);
51 /* Hold mutex before modifying write protection state */
52 static void ftm_clear_write_protection(struct ftm_quaddec
*ftm
)
56 /* First see if it is enabled */
57 ftm_read(ftm
, FTM_FMS
, &flag
);
59 if (flag
& FTM_FMS_WPEN
)
60 FTM_FIELD_UPDATE(ftm
, FTM_MODE
, FTM_MODE_WPDIS
, 1);
63 static void ftm_set_write_protection(struct ftm_quaddec
*ftm
)
65 FTM_FIELD_UPDATE(ftm
, FTM_FMS
, FTM_FMS_WPEN
, 1);
68 static void ftm_reset_counter(struct ftm_quaddec
*ftm
)
70 /* Reset hardware counter to CNTIN */
71 ftm_write(ftm
, FTM_CNT
, 0x0);
74 static void ftm_quaddec_init(struct ftm_quaddec
*ftm
)
76 ftm_clear_write_protection(ftm
);
79 * Do not write in the region from the CNTIN register through the
80 * PWMLOAD register when FTMEN = 0.
81 * Also reset other fields to zero
83 ftm_write(ftm
, FTM_MODE
, FTM_MODE_FTMEN
);
84 ftm_write(ftm
, FTM_CNTIN
, 0x0000);
85 ftm_write(ftm
, FTM_MOD
, 0xffff);
86 ftm_write(ftm
, FTM_CNT
, 0x0);
87 /* Set prescaler, reset other fields to zero */
88 ftm_write(ftm
, FTM_SC
, FTM_SC_PS_1
);
90 /* Select quad mode, reset other fields to zero */
91 ftm_write(ftm
, FTM_QDCTRL
, FTM_QDCTRL_QUADEN
);
93 /* Unused features and reset to default section */
94 ftm_write(ftm
, FTM_POL
, 0x0);
95 ftm_write(ftm
, FTM_FLTCTRL
, 0x0);
96 ftm_write(ftm
, FTM_SYNCONF
, 0x0);
97 ftm_write(ftm
, FTM_SYNC
, 0xffff);
100 ftm_set_write_protection(ftm
);
103 static void ftm_quaddec_disable(void *ftm
)
105 struct ftm_quaddec
*ftm_qua
= ftm
;
107 ftm_clear_write_protection(ftm_qua
);
108 ftm_write(ftm_qua
, FTM_MODE
, 0);
109 ftm_write(ftm_qua
, FTM_QDCTRL
, 0);
111 * This is enough to disable the counter. No clock has been
112 * selected by writing to FTM_SC in init()
114 ftm_set_write_protection(ftm_qua
);
117 static int ftm_quaddec_get_prescaler(struct counter_device
*counter
,
118 struct counter_count
*count
,
121 struct ftm_quaddec
*ftm
= counter
->priv
;
124 ftm_read(ftm
, FTM_SC
, &scflags
);
126 *cnt_mode
= FIELD_GET(FTM_SC_PS_MASK
, scflags
);
131 static int ftm_quaddec_set_prescaler(struct counter_device
*counter
,
132 struct counter_count
*count
,
135 struct ftm_quaddec
*ftm
= counter
->priv
;
137 mutex_lock(&ftm
->ftm_quaddec_mutex
);
139 ftm_clear_write_protection(ftm
);
140 FTM_FIELD_UPDATE(ftm
, FTM_SC
, FTM_SC_PS_MASK
, cnt_mode
);
141 ftm_set_write_protection(ftm
);
143 /* Also resets the counter as it is undefined anyway now */
144 ftm_reset_counter(ftm
);
146 mutex_unlock(&ftm
->ftm_quaddec_mutex
);
150 static const char * const ftm_quaddec_prescaler
[] = {
151 "1", "2", "4", "8", "16", "32", "64", "128"
154 static struct counter_count_enum_ext ftm_quaddec_prescaler_enum
= {
155 .items
= ftm_quaddec_prescaler
,
156 .num_items
= ARRAY_SIZE(ftm_quaddec_prescaler
),
157 .get
= ftm_quaddec_get_prescaler
,
158 .set
= ftm_quaddec_set_prescaler
161 enum ftm_quaddec_synapse_action
{
162 FTM_QUADDEC_SYNAPSE_ACTION_BOTH_EDGES
,
165 static enum counter_synapse_action ftm_quaddec_synapse_actions
[] = {
166 [FTM_QUADDEC_SYNAPSE_ACTION_BOTH_EDGES
] =
167 COUNTER_SYNAPSE_ACTION_BOTH_EDGES
170 enum ftm_quaddec_count_function
{
171 FTM_QUADDEC_COUNT_ENCODER_MODE_1
,
174 static const enum counter_count_function ftm_quaddec_count_functions
[] = {
175 [FTM_QUADDEC_COUNT_ENCODER_MODE_1
] =
176 COUNTER_COUNT_FUNCTION_QUADRATURE_X4
179 static int ftm_quaddec_count_read(struct counter_device
*counter
,
180 struct counter_count
*count
,
181 struct counter_count_read_value
*val
)
183 struct ftm_quaddec
*const ftm
= counter
->priv
;
186 ftm_read(ftm
, FTM_CNT
, &cntval
);
188 counter_count_read_value_set(val
, COUNTER_COUNT_POSITION
, &cntval
);
193 static int ftm_quaddec_count_write(struct counter_device
*counter
,
194 struct counter_count
*count
,
195 struct counter_count_write_value
*val
)
197 struct ftm_quaddec
*const ftm
= counter
->priv
;
201 err
= counter_count_write_value_get(&cnt
, COUNTER_COUNT_POSITION
, val
);
206 dev_warn(&ftm
->pdev
->dev
, "Can only accept '0' as new counter value\n");
210 ftm_reset_counter(ftm
);
215 static int ftm_quaddec_count_function_get(struct counter_device
*counter
,
216 struct counter_count
*count
,
219 *function
= FTM_QUADDEC_COUNT_ENCODER_MODE_1
;
224 static int ftm_quaddec_action_get(struct counter_device
*counter
,
225 struct counter_count
*count
,
226 struct counter_synapse
*synapse
,
229 *action
= FTM_QUADDEC_SYNAPSE_ACTION_BOTH_EDGES
;
234 static const struct counter_ops ftm_quaddec_cnt_ops
= {
235 .count_read
= ftm_quaddec_count_read
,
236 .count_write
= ftm_quaddec_count_write
,
237 .function_get
= ftm_quaddec_count_function_get
,
238 .action_get
= ftm_quaddec_action_get
,
241 static struct counter_signal ftm_quaddec_signals
[] = {
244 .name
= "Channel 1 Phase A"
248 .name
= "Channel 1 Phase B"
252 static struct counter_synapse ftm_quaddec_count_synapses
[] = {
254 .actions_list
= ftm_quaddec_synapse_actions
,
255 .num_actions
= ARRAY_SIZE(ftm_quaddec_synapse_actions
),
256 .signal
= &ftm_quaddec_signals
[0]
259 .actions_list
= ftm_quaddec_synapse_actions
,
260 .num_actions
= ARRAY_SIZE(ftm_quaddec_synapse_actions
),
261 .signal
= &ftm_quaddec_signals
[1]
265 static const struct counter_count_ext ftm_quaddec_count_ext
[] = {
266 COUNTER_COUNT_ENUM("prescaler", &ftm_quaddec_prescaler_enum
),
267 COUNTER_COUNT_ENUM_AVAILABLE("prescaler", &ftm_quaddec_prescaler_enum
),
270 static struct counter_count ftm_quaddec_counts
= {
272 .name
= "Channel 1 Count",
273 .functions_list
= ftm_quaddec_count_functions
,
274 .num_functions
= ARRAY_SIZE(ftm_quaddec_count_functions
),
275 .synapses
= ftm_quaddec_count_synapses
,
276 .num_synapses
= ARRAY_SIZE(ftm_quaddec_count_synapses
),
277 .ext
= ftm_quaddec_count_ext
,
278 .num_ext
= ARRAY_SIZE(ftm_quaddec_count_ext
)
281 static int ftm_quaddec_probe(struct platform_device
*pdev
)
283 struct ftm_quaddec
*ftm
;
285 struct device_node
*node
= pdev
->dev
.of_node
;
289 ftm
= devm_kzalloc(&pdev
->dev
, sizeof(*ftm
), GFP_KERNEL
);
293 platform_set_drvdata(pdev
, ftm
);
295 io
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
297 dev_err(&pdev
->dev
, "Failed to get memory region\n");
302 ftm
->big_endian
= of_property_read_bool(node
, "big-endian");
303 ftm
->ftm_base
= devm_ioremap(&pdev
->dev
, io
->start
, resource_size(io
));
305 if (!ftm
->ftm_base
) {
306 dev_err(&pdev
->dev
, "Failed to map memory region\n");
309 ftm
->counter
.name
= dev_name(&pdev
->dev
);
310 ftm
->counter
.parent
= &pdev
->dev
;
311 ftm
->counter
.ops
= &ftm_quaddec_cnt_ops
;
312 ftm
->counter
.counts
= &ftm_quaddec_counts
;
313 ftm
->counter
.num_counts
= 1;
314 ftm
->counter
.signals
= ftm_quaddec_signals
;
315 ftm
->counter
.num_signals
= ARRAY_SIZE(ftm_quaddec_signals
);
316 ftm
->counter
.priv
= ftm
;
318 mutex_init(&ftm
->ftm_quaddec_mutex
);
320 ftm_quaddec_init(ftm
);
322 ret
= devm_add_action_or_reset(&pdev
->dev
, ftm_quaddec_disable
, ftm
);
326 ret
= devm_counter_register(&pdev
->dev
, &ftm
->counter
);
333 static const struct of_device_id ftm_quaddec_match
[] = {
334 { .compatible
= "fsl,ftm-quaddec" },
338 static struct platform_driver ftm_quaddec_driver
= {
340 .name
= "ftm-quaddec",
341 .of_match_table
= ftm_quaddec_match
,
343 .probe
= ftm_quaddec_probe
,
346 module_platform_driver(ftm_quaddec_driver
);
348 MODULE_LICENSE("GPL");
349 MODULE_AUTHOR("Kjeld Flarup <kfa@deif.com>");
350 MODULE_AUTHOR("Patrick Havelange <patrick.havelange@essensium.com>");