162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Flex Timer Module Quadrature decoder
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * This module implements a driver for decoding the FTM quadrature
662306a36Sopenharmony_ci * of ex. a LS1021A
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/fsl/ftm.h>
1062306a36Sopenharmony_ci#include <linux/module.h>
1162306a36Sopenharmony_ci#include <linux/platform_device.h>
1262306a36Sopenharmony_ci#include <linux/of.h>
1362306a36Sopenharmony_ci#include <linux/io.h>
1462306a36Sopenharmony_ci#include <linux/mutex.h>
1562306a36Sopenharmony_ci#include <linux/counter.h>
1662306a36Sopenharmony_ci#include <linux/bitfield.h>
1762306a36Sopenharmony_ci#include <linux/types.h>
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#define FTM_FIELD_UPDATE(ftm, offset, mask, val)			\
2062306a36Sopenharmony_ci	({								\
2162306a36Sopenharmony_ci		uint32_t flags;						\
2262306a36Sopenharmony_ci		ftm_read(ftm, offset, &flags);				\
2362306a36Sopenharmony_ci		flags &= ~mask;						\
2462306a36Sopenharmony_ci		flags |= FIELD_PREP(mask, val);				\
2562306a36Sopenharmony_ci		ftm_write(ftm, offset, flags);				\
2662306a36Sopenharmony_ci	})
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_cistruct ftm_quaddec {
2962306a36Sopenharmony_ci	struct platform_device *pdev;
3062306a36Sopenharmony_ci	void __iomem *ftm_base;
3162306a36Sopenharmony_ci	bool big_endian;
3262306a36Sopenharmony_ci	struct mutex ftm_quaddec_mutex;
3362306a36Sopenharmony_ci};
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_cistatic void ftm_read(struct ftm_quaddec *ftm, uint32_t offset, uint32_t *data)
3662306a36Sopenharmony_ci{
3762306a36Sopenharmony_ci	if (ftm->big_endian)
3862306a36Sopenharmony_ci		*data = ioread32be(ftm->ftm_base + offset);
3962306a36Sopenharmony_ci	else
4062306a36Sopenharmony_ci		*data = ioread32(ftm->ftm_base + offset);
4162306a36Sopenharmony_ci}
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_cistatic void ftm_write(struct ftm_quaddec *ftm, uint32_t offset, uint32_t data)
4462306a36Sopenharmony_ci{
4562306a36Sopenharmony_ci	if (ftm->big_endian)
4662306a36Sopenharmony_ci		iowrite32be(data, ftm->ftm_base + offset);
4762306a36Sopenharmony_ci	else
4862306a36Sopenharmony_ci		iowrite32(data, ftm->ftm_base + offset);
4962306a36Sopenharmony_ci}
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci/* Hold mutex before modifying write protection state */
5262306a36Sopenharmony_cistatic void ftm_clear_write_protection(struct ftm_quaddec *ftm)
5362306a36Sopenharmony_ci{
5462306a36Sopenharmony_ci	uint32_t flag;
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	/* First see if it is enabled */
5762306a36Sopenharmony_ci	ftm_read(ftm, FTM_FMS, &flag);
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	if (flag & FTM_FMS_WPEN)
6062306a36Sopenharmony_ci		FTM_FIELD_UPDATE(ftm, FTM_MODE, FTM_MODE_WPDIS, 1);
6162306a36Sopenharmony_ci}
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_cistatic void ftm_set_write_protection(struct ftm_quaddec *ftm)
6462306a36Sopenharmony_ci{
6562306a36Sopenharmony_ci	FTM_FIELD_UPDATE(ftm, FTM_FMS, FTM_FMS_WPEN, 1);
6662306a36Sopenharmony_ci}
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_cistatic void ftm_reset_counter(struct ftm_quaddec *ftm)
6962306a36Sopenharmony_ci{
7062306a36Sopenharmony_ci	/* Reset hardware counter to CNTIN */
7162306a36Sopenharmony_ci	ftm_write(ftm, FTM_CNT, 0x0);
7262306a36Sopenharmony_ci}
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_cistatic void ftm_quaddec_init(struct ftm_quaddec *ftm)
7562306a36Sopenharmony_ci{
7662306a36Sopenharmony_ci	ftm_clear_write_protection(ftm);
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	/*
7962306a36Sopenharmony_ci	 * Do not write in the region from the CNTIN register through the
8062306a36Sopenharmony_ci	 * PWMLOAD register when FTMEN = 0.
8162306a36Sopenharmony_ci	 * Also reset other fields to zero
8262306a36Sopenharmony_ci	 */
8362306a36Sopenharmony_ci	ftm_write(ftm, FTM_MODE, FTM_MODE_FTMEN);
8462306a36Sopenharmony_ci	ftm_write(ftm, FTM_CNTIN, 0x0000);
8562306a36Sopenharmony_ci	ftm_write(ftm, FTM_MOD, 0xffff);
8662306a36Sopenharmony_ci	ftm_write(ftm, FTM_CNT, 0x0);
8762306a36Sopenharmony_ci	/* Set prescaler, reset other fields to zero */
8862306a36Sopenharmony_ci	ftm_write(ftm, FTM_SC, FTM_SC_PS_1);
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	/* Select quad mode, reset other fields to zero */
9162306a36Sopenharmony_ci	ftm_write(ftm, FTM_QDCTRL, FTM_QDCTRL_QUADEN);
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	/* Unused features and reset to default section */
9462306a36Sopenharmony_ci	ftm_write(ftm, FTM_POL, 0x0);
9562306a36Sopenharmony_ci	ftm_write(ftm, FTM_FLTCTRL, 0x0);
9662306a36Sopenharmony_ci	ftm_write(ftm, FTM_SYNCONF, 0x0);
9762306a36Sopenharmony_ci	ftm_write(ftm, FTM_SYNC, 0xffff);
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	/* Lock the FTM */
10062306a36Sopenharmony_ci	ftm_set_write_protection(ftm);
10162306a36Sopenharmony_ci}
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_cistatic void ftm_quaddec_disable(void *ftm)
10462306a36Sopenharmony_ci{
10562306a36Sopenharmony_ci	struct ftm_quaddec *ftm_qua = ftm;
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	ftm_clear_write_protection(ftm_qua);
10862306a36Sopenharmony_ci	ftm_write(ftm_qua, FTM_MODE, 0);
10962306a36Sopenharmony_ci	ftm_write(ftm_qua, FTM_QDCTRL, 0);
11062306a36Sopenharmony_ci	/*
11162306a36Sopenharmony_ci	 * This is enough to disable the counter. No clock has been
11262306a36Sopenharmony_ci	 * selected by writing to FTM_SC in init()
11362306a36Sopenharmony_ci	 */
11462306a36Sopenharmony_ci	ftm_set_write_protection(ftm_qua);
11562306a36Sopenharmony_ci}
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_cistatic int ftm_quaddec_get_prescaler(struct counter_device *counter,
11862306a36Sopenharmony_ci				     struct counter_count *count, u32 *cnt_mode)
11962306a36Sopenharmony_ci{
12062306a36Sopenharmony_ci	struct ftm_quaddec *ftm = counter_priv(counter);
12162306a36Sopenharmony_ci	uint32_t scflags;
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	ftm_read(ftm, FTM_SC, &scflags);
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	*cnt_mode = FIELD_GET(FTM_SC_PS_MASK, scflags);
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	return 0;
12862306a36Sopenharmony_ci}
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_cistatic int ftm_quaddec_set_prescaler(struct counter_device *counter,
13162306a36Sopenharmony_ci				     struct counter_count *count, u32 cnt_mode)
13262306a36Sopenharmony_ci{
13362306a36Sopenharmony_ci	struct ftm_quaddec *ftm = counter_priv(counter);
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	mutex_lock(&ftm->ftm_quaddec_mutex);
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	ftm_clear_write_protection(ftm);
13862306a36Sopenharmony_ci	FTM_FIELD_UPDATE(ftm, FTM_SC, FTM_SC_PS_MASK, cnt_mode);
13962306a36Sopenharmony_ci	ftm_set_write_protection(ftm);
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	/* Also resets the counter as it is undefined anyway now */
14262306a36Sopenharmony_ci	ftm_reset_counter(ftm);
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	mutex_unlock(&ftm->ftm_quaddec_mutex);
14562306a36Sopenharmony_ci	return 0;
14662306a36Sopenharmony_ci}
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_cistatic const char * const ftm_quaddec_prescaler[] = {
14962306a36Sopenharmony_ci	"1", "2", "4", "8", "16", "32", "64", "128"
15062306a36Sopenharmony_ci};
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_cistatic const enum counter_synapse_action ftm_quaddec_synapse_actions[] = {
15362306a36Sopenharmony_ci	COUNTER_SYNAPSE_ACTION_BOTH_EDGES
15462306a36Sopenharmony_ci};
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_cistatic const enum counter_function ftm_quaddec_count_functions[] = {
15762306a36Sopenharmony_ci	COUNTER_FUNCTION_QUADRATURE_X4
15862306a36Sopenharmony_ci};
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_cistatic int ftm_quaddec_count_read(struct counter_device *counter,
16162306a36Sopenharmony_ci				  struct counter_count *count,
16262306a36Sopenharmony_ci				  u64 *val)
16362306a36Sopenharmony_ci{
16462306a36Sopenharmony_ci	struct ftm_quaddec *const ftm = counter_priv(counter);
16562306a36Sopenharmony_ci	uint32_t cntval;
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	ftm_read(ftm, FTM_CNT, &cntval);
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	*val = cntval;
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	return 0;
17262306a36Sopenharmony_ci}
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_cistatic int ftm_quaddec_count_write(struct counter_device *counter,
17562306a36Sopenharmony_ci				   struct counter_count *count,
17662306a36Sopenharmony_ci				   const u64 val)
17762306a36Sopenharmony_ci{
17862306a36Sopenharmony_ci	struct ftm_quaddec *const ftm = counter_priv(counter);
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	if (val != 0) {
18162306a36Sopenharmony_ci		dev_warn(&ftm->pdev->dev, "Can only accept '0' as new counter value\n");
18262306a36Sopenharmony_ci		return -EINVAL;
18362306a36Sopenharmony_ci	}
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	ftm_reset_counter(ftm);
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	return 0;
18862306a36Sopenharmony_ci}
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_cistatic int ftm_quaddec_count_function_read(struct counter_device *counter,
19162306a36Sopenharmony_ci					   struct counter_count *count,
19262306a36Sopenharmony_ci					   enum counter_function *function)
19362306a36Sopenharmony_ci{
19462306a36Sopenharmony_ci	*function = COUNTER_FUNCTION_QUADRATURE_X4;
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	return 0;
19762306a36Sopenharmony_ci}
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_cistatic int ftm_quaddec_action_read(struct counter_device *counter,
20062306a36Sopenharmony_ci				   struct counter_count *count,
20162306a36Sopenharmony_ci				   struct counter_synapse *synapse,
20262306a36Sopenharmony_ci				   enum counter_synapse_action *action)
20362306a36Sopenharmony_ci{
20462306a36Sopenharmony_ci	*action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES;
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	return 0;
20762306a36Sopenharmony_ci}
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_cistatic const struct counter_ops ftm_quaddec_cnt_ops = {
21062306a36Sopenharmony_ci	.count_read = ftm_quaddec_count_read,
21162306a36Sopenharmony_ci	.count_write = ftm_quaddec_count_write,
21262306a36Sopenharmony_ci	.function_read = ftm_quaddec_count_function_read,
21362306a36Sopenharmony_ci	.action_read = ftm_quaddec_action_read,
21462306a36Sopenharmony_ci};
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_cistatic struct counter_signal ftm_quaddec_signals[] = {
21762306a36Sopenharmony_ci	{
21862306a36Sopenharmony_ci		.id = 0,
21962306a36Sopenharmony_ci		.name = "Channel 1 Phase A"
22062306a36Sopenharmony_ci	},
22162306a36Sopenharmony_ci	{
22262306a36Sopenharmony_ci		.id = 1,
22362306a36Sopenharmony_ci		.name = "Channel 1 Phase B"
22462306a36Sopenharmony_ci	}
22562306a36Sopenharmony_ci};
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_cistatic struct counter_synapse ftm_quaddec_count_synapses[] = {
22862306a36Sopenharmony_ci	{
22962306a36Sopenharmony_ci		.actions_list = ftm_quaddec_synapse_actions,
23062306a36Sopenharmony_ci		.num_actions = ARRAY_SIZE(ftm_quaddec_synapse_actions),
23162306a36Sopenharmony_ci		.signal = &ftm_quaddec_signals[0]
23262306a36Sopenharmony_ci	},
23362306a36Sopenharmony_ci	{
23462306a36Sopenharmony_ci		.actions_list = ftm_quaddec_synapse_actions,
23562306a36Sopenharmony_ci		.num_actions = ARRAY_SIZE(ftm_quaddec_synapse_actions),
23662306a36Sopenharmony_ci		.signal = &ftm_quaddec_signals[1]
23762306a36Sopenharmony_ci	}
23862306a36Sopenharmony_ci};
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_cistatic DEFINE_COUNTER_ENUM(ftm_quaddec_prescaler_enum, ftm_quaddec_prescaler);
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_cistatic struct counter_comp ftm_quaddec_count_ext[] = {
24362306a36Sopenharmony_ci	COUNTER_COMP_COUNT_ENUM("prescaler", ftm_quaddec_get_prescaler,
24462306a36Sopenharmony_ci				ftm_quaddec_set_prescaler,
24562306a36Sopenharmony_ci				ftm_quaddec_prescaler_enum),
24662306a36Sopenharmony_ci};
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_cistatic struct counter_count ftm_quaddec_counts = {
24962306a36Sopenharmony_ci	.id = 0,
25062306a36Sopenharmony_ci	.name = "Channel 1 Count",
25162306a36Sopenharmony_ci	.functions_list = ftm_quaddec_count_functions,
25262306a36Sopenharmony_ci	.num_functions = ARRAY_SIZE(ftm_quaddec_count_functions),
25362306a36Sopenharmony_ci	.synapses = ftm_quaddec_count_synapses,
25462306a36Sopenharmony_ci	.num_synapses = ARRAY_SIZE(ftm_quaddec_count_synapses),
25562306a36Sopenharmony_ci	.ext = ftm_quaddec_count_ext,
25662306a36Sopenharmony_ci	.num_ext = ARRAY_SIZE(ftm_quaddec_count_ext)
25762306a36Sopenharmony_ci};
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_cistatic int ftm_quaddec_probe(struct platform_device *pdev)
26062306a36Sopenharmony_ci{
26162306a36Sopenharmony_ci	struct counter_device *counter;
26262306a36Sopenharmony_ci	struct ftm_quaddec *ftm;
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	struct device_node *node = pdev->dev.of_node;
26562306a36Sopenharmony_ci	struct resource *io;
26662306a36Sopenharmony_ci	int ret;
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	counter = devm_counter_alloc(&pdev->dev, sizeof(*ftm));
26962306a36Sopenharmony_ci	if (!counter)
27062306a36Sopenharmony_ci		return -ENOMEM;
27162306a36Sopenharmony_ci	ftm = counter_priv(counter);
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	io = platform_get_resource(pdev, IORESOURCE_MEM, 0);
27462306a36Sopenharmony_ci	if (!io) {
27562306a36Sopenharmony_ci		dev_err(&pdev->dev, "Failed to get memory region\n");
27662306a36Sopenharmony_ci		return -ENODEV;
27762306a36Sopenharmony_ci	}
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	ftm->pdev = pdev;
28062306a36Sopenharmony_ci	ftm->big_endian = of_property_read_bool(node, "big-endian");
28162306a36Sopenharmony_ci	ftm->ftm_base = devm_ioremap(&pdev->dev, io->start, resource_size(io));
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci	if (!ftm->ftm_base) {
28462306a36Sopenharmony_ci		dev_err(&pdev->dev, "Failed to map memory region\n");
28562306a36Sopenharmony_ci		return -EINVAL;
28662306a36Sopenharmony_ci	}
28762306a36Sopenharmony_ci	counter->name = dev_name(&pdev->dev);
28862306a36Sopenharmony_ci	counter->parent = &pdev->dev;
28962306a36Sopenharmony_ci	counter->ops = &ftm_quaddec_cnt_ops;
29062306a36Sopenharmony_ci	counter->counts = &ftm_quaddec_counts;
29162306a36Sopenharmony_ci	counter->num_counts = 1;
29262306a36Sopenharmony_ci	counter->signals = ftm_quaddec_signals;
29362306a36Sopenharmony_ci	counter->num_signals = ARRAY_SIZE(ftm_quaddec_signals);
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	mutex_init(&ftm->ftm_quaddec_mutex);
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci	ftm_quaddec_init(ftm);
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci	ret = devm_add_action_or_reset(&pdev->dev, ftm_quaddec_disable, ftm);
30062306a36Sopenharmony_ci	if (ret)
30162306a36Sopenharmony_ci		return ret;
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci	ret = devm_counter_add(&pdev->dev, counter);
30462306a36Sopenharmony_ci	if (ret)
30562306a36Sopenharmony_ci		return dev_err_probe(&pdev->dev, ret, "Failed to add counter\n");
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	return 0;
30862306a36Sopenharmony_ci}
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_cistatic const struct of_device_id ftm_quaddec_match[] = {
31162306a36Sopenharmony_ci	{ .compatible = "fsl,ftm-quaddec" },
31262306a36Sopenharmony_ci	{},
31362306a36Sopenharmony_ci};
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_cistatic struct platform_driver ftm_quaddec_driver = {
31662306a36Sopenharmony_ci	.driver = {
31762306a36Sopenharmony_ci		.name = "ftm-quaddec",
31862306a36Sopenharmony_ci		.of_match_table = ftm_quaddec_match,
31962306a36Sopenharmony_ci	},
32062306a36Sopenharmony_ci	.probe = ftm_quaddec_probe,
32162306a36Sopenharmony_ci};
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_cimodule_platform_driver(ftm_quaddec_driver);
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ciMODULE_LICENSE("GPL");
32662306a36Sopenharmony_ciMODULE_AUTHOR("Kjeld Flarup <kfa@deif.com>");
32762306a36Sopenharmony_ciMODULE_AUTHOR("Patrick Havelange <patrick.havelange@essensium.com>");
32862306a36Sopenharmony_ciMODULE_IMPORT_NS(COUNTER);
329