18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Flex Timer Module Quadrature decoder
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * This module implements a driver for decoding the FTM quadrature
68c2ecf20Sopenharmony_ci * of ex. a LS1021A
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <linux/fsl/ftm.h>
108c2ecf20Sopenharmony_ci#include <linux/module.h>
118c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
128c2ecf20Sopenharmony_ci#include <linux/of.h>
138c2ecf20Sopenharmony_ci#include <linux/io.h>
148c2ecf20Sopenharmony_ci#include <linux/mutex.h>
158c2ecf20Sopenharmony_ci#include <linux/counter.h>
168c2ecf20Sopenharmony_ci#include <linux/bitfield.h>
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#define FTM_FIELD_UPDATE(ftm, offset, mask, val)			\
198c2ecf20Sopenharmony_ci	({								\
208c2ecf20Sopenharmony_ci		uint32_t flags;						\
218c2ecf20Sopenharmony_ci		ftm_read(ftm, offset, &flags);				\
228c2ecf20Sopenharmony_ci		flags &= ~mask;						\
238c2ecf20Sopenharmony_ci		flags |= FIELD_PREP(mask, val);				\
248c2ecf20Sopenharmony_ci		ftm_write(ftm, offset, flags);				\
258c2ecf20Sopenharmony_ci	})
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_cistruct ftm_quaddec {
288c2ecf20Sopenharmony_ci	struct counter_device counter;
298c2ecf20Sopenharmony_ci	struct platform_device *pdev;
308c2ecf20Sopenharmony_ci	void __iomem *ftm_base;
318c2ecf20Sopenharmony_ci	bool big_endian;
328c2ecf20Sopenharmony_ci	struct mutex ftm_quaddec_mutex;
338c2ecf20Sopenharmony_ci};
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_cistatic void ftm_read(struct ftm_quaddec *ftm, uint32_t offset, uint32_t *data)
368c2ecf20Sopenharmony_ci{
378c2ecf20Sopenharmony_ci	if (ftm->big_endian)
388c2ecf20Sopenharmony_ci		*data = ioread32be(ftm->ftm_base + offset);
398c2ecf20Sopenharmony_ci	else
408c2ecf20Sopenharmony_ci		*data = ioread32(ftm->ftm_base + offset);
418c2ecf20Sopenharmony_ci}
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_cistatic void ftm_write(struct ftm_quaddec *ftm, uint32_t offset, uint32_t data)
448c2ecf20Sopenharmony_ci{
458c2ecf20Sopenharmony_ci	if (ftm->big_endian)
468c2ecf20Sopenharmony_ci		iowrite32be(data, ftm->ftm_base + offset);
478c2ecf20Sopenharmony_ci	else
488c2ecf20Sopenharmony_ci		iowrite32(data, ftm->ftm_base + offset);
498c2ecf20Sopenharmony_ci}
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci/* Hold mutex before modifying write protection state */
528c2ecf20Sopenharmony_cistatic void ftm_clear_write_protection(struct ftm_quaddec *ftm)
538c2ecf20Sopenharmony_ci{
548c2ecf20Sopenharmony_ci	uint32_t flag;
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci	/* First see if it is enabled */
578c2ecf20Sopenharmony_ci	ftm_read(ftm, FTM_FMS, &flag);
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci	if (flag & FTM_FMS_WPEN)
608c2ecf20Sopenharmony_ci		FTM_FIELD_UPDATE(ftm, FTM_MODE, FTM_MODE_WPDIS, 1);
618c2ecf20Sopenharmony_ci}
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_cistatic void ftm_set_write_protection(struct ftm_quaddec *ftm)
648c2ecf20Sopenharmony_ci{
658c2ecf20Sopenharmony_ci	FTM_FIELD_UPDATE(ftm, FTM_FMS, FTM_FMS_WPEN, 1);
668c2ecf20Sopenharmony_ci}
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_cistatic void ftm_reset_counter(struct ftm_quaddec *ftm)
698c2ecf20Sopenharmony_ci{
708c2ecf20Sopenharmony_ci	/* Reset hardware counter to CNTIN */
718c2ecf20Sopenharmony_ci	ftm_write(ftm, FTM_CNT, 0x0);
728c2ecf20Sopenharmony_ci}
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_cistatic void ftm_quaddec_init(struct ftm_quaddec *ftm)
758c2ecf20Sopenharmony_ci{
768c2ecf20Sopenharmony_ci	ftm_clear_write_protection(ftm);
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	/*
798c2ecf20Sopenharmony_ci	 * Do not write in the region from the CNTIN register through the
808c2ecf20Sopenharmony_ci	 * PWMLOAD register when FTMEN = 0.
818c2ecf20Sopenharmony_ci	 * Also reset other fields to zero
828c2ecf20Sopenharmony_ci	 */
838c2ecf20Sopenharmony_ci	ftm_write(ftm, FTM_MODE, FTM_MODE_FTMEN);
848c2ecf20Sopenharmony_ci	ftm_write(ftm, FTM_CNTIN, 0x0000);
858c2ecf20Sopenharmony_ci	ftm_write(ftm, FTM_MOD, 0xffff);
868c2ecf20Sopenharmony_ci	ftm_write(ftm, FTM_CNT, 0x0);
878c2ecf20Sopenharmony_ci	/* Set prescaler, reset other fields to zero */
888c2ecf20Sopenharmony_ci	ftm_write(ftm, FTM_SC, FTM_SC_PS_1);
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	/* Select quad mode, reset other fields to zero */
918c2ecf20Sopenharmony_ci	ftm_write(ftm, FTM_QDCTRL, FTM_QDCTRL_QUADEN);
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	/* Unused features and reset to default section */
948c2ecf20Sopenharmony_ci	ftm_write(ftm, FTM_POL, 0x0);
958c2ecf20Sopenharmony_ci	ftm_write(ftm, FTM_FLTCTRL, 0x0);
968c2ecf20Sopenharmony_ci	ftm_write(ftm, FTM_SYNCONF, 0x0);
978c2ecf20Sopenharmony_ci	ftm_write(ftm, FTM_SYNC, 0xffff);
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	/* Lock the FTM */
1008c2ecf20Sopenharmony_ci	ftm_set_write_protection(ftm);
1018c2ecf20Sopenharmony_ci}
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_cistatic void ftm_quaddec_disable(void *ftm)
1048c2ecf20Sopenharmony_ci{
1058c2ecf20Sopenharmony_ci	struct ftm_quaddec *ftm_qua = ftm;
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	ftm_clear_write_protection(ftm_qua);
1088c2ecf20Sopenharmony_ci	ftm_write(ftm_qua, FTM_MODE, 0);
1098c2ecf20Sopenharmony_ci	ftm_write(ftm_qua, FTM_QDCTRL, 0);
1108c2ecf20Sopenharmony_ci	/*
1118c2ecf20Sopenharmony_ci	 * This is enough to disable the counter. No clock has been
1128c2ecf20Sopenharmony_ci	 * selected by writing to FTM_SC in init()
1138c2ecf20Sopenharmony_ci	 */
1148c2ecf20Sopenharmony_ci	ftm_set_write_protection(ftm_qua);
1158c2ecf20Sopenharmony_ci}
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_cistatic int ftm_quaddec_get_prescaler(struct counter_device *counter,
1188c2ecf20Sopenharmony_ci				     struct counter_count *count,
1198c2ecf20Sopenharmony_ci				     size_t *cnt_mode)
1208c2ecf20Sopenharmony_ci{
1218c2ecf20Sopenharmony_ci	struct ftm_quaddec *ftm = counter->priv;
1228c2ecf20Sopenharmony_ci	uint32_t scflags;
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	ftm_read(ftm, FTM_SC, &scflags);
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	*cnt_mode = FIELD_GET(FTM_SC_PS_MASK, scflags);
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	return 0;
1298c2ecf20Sopenharmony_ci}
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_cistatic int ftm_quaddec_set_prescaler(struct counter_device *counter,
1328c2ecf20Sopenharmony_ci				     struct counter_count *count,
1338c2ecf20Sopenharmony_ci				     size_t cnt_mode)
1348c2ecf20Sopenharmony_ci{
1358c2ecf20Sopenharmony_ci	struct ftm_quaddec *ftm = counter->priv;
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	mutex_lock(&ftm->ftm_quaddec_mutex);
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	ftm_clear_write_protection(ftm);
1408c2ecf20Sopenharmony_ci	FTM_FIELD_UPDATE(ftm, FTM_SC, FTM_SC_PS_MASK, cnt_mode);
1418c2ecf20Sopenharmony_ci	ftm_set_write_protection(ftm);
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	/* Also resets the counter as it is undefined anyway now */
1448c2ecf20Sopenharmony_ci	ftm_reset_counter(ftm);
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	mutex_unlock(&ftm->ftm_quaddec_mutex);
1478c2ecf20Sopenharmony_ci	return 0;
1488c2ecf20Sopenharmony_ci}
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_cistatic const char * const ftm_quaddec_prescaler[] = {
1518c2ecf20Sopenharmony_ci	"1", "2", "4", "8", "16", "32", "64", "128"
1528c2ecf20Sopenharmony_ci};
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_cistatic struct counter_count_enum_ext ftm_quaddec_prescaler_enum = {
1558c2ecf20Sopenharmony_ci	.items = ftm_quaddec_prescaler,
1568c2ecf20Sopenharmony_ci	.num_items = ARRAY_SIZE(ftm_quaddec_prescaler),
1578c2ecf20Sopenharmony_ci	.get = ftm_quaddec_get_prescaler,
1588c2ecf20Sopenharmony_ci	.set = ftm_quaddec_set_prescaler
1598c2ecf20Sopenharmony_ci};
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_cienum ftm_quaddec_synapse_action {
1628c2ecf20Sopenharmony_ci	FTM_QUADDEC_SYNAPSE_ACTION_BOTH_EDGES,
1638c2ecf20Sopenharmony_ci};
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_cistatic enum counter_synapse_action ftm_quaddec_synapse_actions[] = {
1668c2ecf20Sopenharmony_ci	[FTM_QUADDEC_SYNAPSE_ACTION_BOTH_EDGES] =
1678c2ecf20Sopenharmony_ci	COUNTER_SYNAPSE_ACTION_BOTH_EDGES
1688c2ecf20Sopenharmony_ci};
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_cienum ftm_quaddec_count_function {
1718c2ecf20Sopenharmony_ci	FTM_QUADDEC_COUNT_ENCODER_MODE_1,
1728c2ecf20Sopenharmony_ci};
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_cistatic const enum counter_count_function ftm_quaddec_count_functions[] = {
1758c2ecf20Sopenharmony_ci	[FTM_QUADDEC_COUNT_ENCODER_MODE_1] =
1768c2ecf20Sopenharmony_ci	COUNTER_COUNT_FUNCTION_QUADRATURE_X4
1778c2ecf20Sopenharmony_ci};
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_cistatic int ftm_quaddec_count_read(struct counter_device *counter,
1808c2ecf20Sopenharmony_ci				  struct counter_count *count,
1818c2ecf20Sopenharmony_ci				  unsigned long *val)
1828c2ecf20Sopenharmony_ci{
1838c2ecf20Sopenharmony_ci	struct ftm_quaddec *const ftm = counter->priv;
1848c2ecf20Sopenharmony_ci	uint32_t cntval;
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	ftm_read(ftm, FTM_CNT, &cntval);
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	*val = cntval;
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	return 0;
1918c2ecf20Sopenharmony_ci}
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_cistatic int ftm_quaddec_count_write(struct counter_device *counter,
1948c2ecf20Sopenharmony_ci				   struct counter_count *count,
1958c2ecf20Sopenharmony_ci				   const unsigned long val)
1968c2ecf20Sopenharmony_ci{
1978c2ecf20Sopenharmony_ci	struct ftm_quaddec *const ftm = counter->priv;
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	if (val != 0) {
2008c2ecf20Sopenharmony_ci		dev_warn(&ftm->pdev->dev, "Can only accept '0' as new counter value\n");
2018c2ecf20Sopenharmony_ci		return -EINVAL;
2028c2ecf20Sopenharmony_ci	}
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci	ftm_reset_counter(ftm);
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	return 0;
2078c2ecf20Sopenharmony_ci}
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_cistatic int ftm_quaddec_count_function_get(struct counter_device *counter,
2108c2ecf20Sopenharmony_ci					  struct counter_count *count,
2118c2ecf20Sopenharmony_ci					  size_t *function)
2128c2ecf20Sopenharmony_ci{
2138c2ecf20Sopenharmony_ci	*function = FTM_QUADDEC_COUNT_ENCODER_MODE_1;
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	return 0;
2168c2ecf20Sopenharmony_ci}
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_cistatic int ftm_quaddec_action_get(struct counter_device *counter,
2198c2ecf20Sopenharmony_ci				  struct counter_count *count,
2208c2ecf20Sopenharmony_ci				  struct counter_synapse *synapse,
2218c2ecf20Sopenharmony_ci				  size_t *action)
2228c2ecf20Sopenharmony_ci{
2238c2ecf20Sopenharmony_ci	*action = FTM_QUADDEC_SYNAPSE_ACTION_BOTH_EDGES;
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci	return 0;
2268c2ecf20Sopenharmony_ci}
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_cistatic const struct counter_ops ftm_quaddec_cnt_ops = {
2298c2ecf20Sopenharmony_ci	.count_read = ftm_quaddec_count_read,
2308c2ecf20Sopenharmony_ci	.count_write = ftm_quaddec_count_write,
2318c2ecf20Sopenharmony_ci	.function_get = ftm_quaddec_count_function_get,
2328c2ecf20Sopenharmony_ci	.action_get = ftm_quaddec_action_get,
2338c2ecf20Sopenharmony_ci};
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_cistatic struct counter_signal ftm_quaddec_signals[] = {
2368c2ecf20Sopenharmony_ci	{
2378c2ecf20Sopenharmony_ci		.id = 0,
2388c2ecf20Sopenharmony_ci		.name = "Channel 1 Phase A"
2398c2ecf20Sopenharmony_ci	},
2408c2ecf20Sopenharmony_ci	{
2418c2ecf20Sopenharmony_ci		.id = 1,
2428c2ecf20Sopenharmony_ci		.name = "Channel 1 Phase B"
2438c2ecf20Sopenharmony_ci	}
2448c2ecf20Sopenharmony_ci};
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_cistatic struct counter_synapse ftm_quaddec_count_synapses[] = {
2478c2ecf20Sopenharmony_ci	{
2488c2ecf20Sopenharmony_ci		.actions_list = ftm_quaddec_synapse_actions,
2498c2ecf20Sopenharmony_ci		.num_actions = ARRAY_SIZE(ftm_quaddec_synapse_actions),
2508c2ecf20Sopenharmony_ci		.signal = &ftm_quaddec_signals[0]
2518c2ecf20Sopenharmony_ci	},
2528c2ecf20Sopenharmony_ci	{
2538c2ecf20Sopenharmony_ci		.actions_list = ftm_quaddec_synapse_actions,
2548c2ecf20Sopenharmony_ci		.num_actions = ARRAY_SIZE(ftm_quaddec_synapse_actions),
2558c2ecf20Sopenharmony_ci		.signal = &ftm_quaddec_signals[1]
2568c2ecf20Sopenharmony_ci	}
2578c2ecf20Sopenharmony_ci};
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_cistatic const struct counter_count_ext ftm_quaddec_count_ext[] = {
2608c2ecf20Sopenharmony_ci	COUNTER_COUNT_ENUM("prescaler", &ftm_quaddec_prescaler_enum),
2618c2ecf20Sopenharmony_ci	COUNTER_COUNT_ENUM_AVAILABLE("prescaler", &ftm_quaddec_prescaler_enum),
2628c2ecf20Sopenharmony_ci};
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_cistatic struct counter_count ftm_quaddec_counts = {
2658c2ecf20Sopenharmony_ci	.id = 0,
2668c2ecf20Sopenharmony_ci	.name = "Channel 1 Count",
2678c2ecf20Sopenharmony_ci	.functions_list = ftm_quaddec_count_functions,
2688c2ecf20Sopenharmony_ci	.num_functions = ARRAY_SIZE(ftm_quaddec_count_functions),
2698c2ecf20Sopenharmony_ci	.synapses = ftm_quaddec_count_synapses,
2708c2ecf20Sopenharmony_ci	.num_synapses = ARRAY_SIZE(ftm_quaddec_count_synapses),
2718c2ecf20Sopenharmony_ci	.ext = ftm_quaddec_count_ext,
2728c2ecf20Sopenharmony_ci	.num_ext = ARRAY_SIZE(ftm_quaddec_count_ext)
2738c2ecf20Sopenharmony_ci};
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_cistatic int ftm_quaddec_probe(struct platform_device *pdev)
2768c2ecf20Sopenharmony_ci{
2778c2ecf20Sopenharmony_ci	struct ftm_quaddec *ftm;
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci	struct device_node *node = pdev->dev.of_node;
2808c2ecf20Sopenharmony_ci	struct resource *io;
2818c2ecf20Sopenharmony_ci	int ret;
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	ftm = devm_kzalloc(&pdev->dev, sizeof(*ftm), GFP_KERNEL);
2848c2ecf20Sopenharmony_ci	if (!ftm)
2858c2ecf20Sopenharmony_ci		return -ENOMEM;
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, ftm);
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	io = platform_get_resource(pdev, IORESOURCE_MEM, 0);
2908c2ecf20Sopenharmony_ci	if (!io) {
2918c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Failed to get memory region\n");
2928c2ecf20Sopenharmony_ci		return -ENODEV;
2938c2ecf20Sopenharmony_ci	}
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci	ftm->pdev = pdev;
2968c2ecf20Sopenharmony_ci	ftm->big_endian = of_property_read_bool(node, "big-endian");
2978c2ecf20Sopenharmony_ci	ftm->ftm_base = devm_ioremap(&pdev->dev, io->start, resource_size(io));
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci	if (!ftm->ftm_base) {
3008c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Failed to map memory region\n");
3018c2ecf20Sopenharmony_ci		return -EINVAL;
3028c2ecf20Sopenharmony_ci	}
3038c2ecf20Sopenharmony_ci	ftm->counter.name = dev_name(&pdev->dev);
3048c2ecf20Sopenharmony_ci	ftm->counter.parent = &pdev->dev;
3058c2ecf20Sopenharmony_ci	ftm->counter.ops = &ftm_quaddec_cnt_ops;
3068c2ecf20Sopenharmony_ci	ftm->counter.counts = &ftm_quaddec_counts;
3078c2ecf20Sopenharmony_ci	ftm->counter.num_counts = 1;
3088c2ecf20Sopenharmony_ci	ftm->counter.signals = ftm_quaddec_signals;
3098c2ecf20Sopenharmony_ci	ftm->counter.num_signals = ARRAY_SIZE(ftm_quaddec_signals);
3108c2ecf20Sopenharmony_ci	ftm->counter.priv = ftm;
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci	mutex_init(&ftm->ftm_quaddec_mutex);
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci	ftm_quaddec_init(ftm);
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	ret = devm_add_action_or_reset(&pdev->dev, ftm_quaddec_disable, ftm);
3178c2ecf20Sopenharmony_ci	if (ret)
3188c2ecf20Sopenharmony_ci		return ret;
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci	ret = devm_counter_register(&pdev->dev, &ftm->counter);
3218c2ecf20Sopenharmony_ci	if (ret)
3228c2ecf20Sopenharmony_ci		return ret;
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci	return 0;
3258c2ecf20Sopenharmony_ci}
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_cistatic const struct of_device_id ftm_quaddec_match[] = {
3288c2ecf20Sopenharmony_ci	{ .compatible = "fsl,ftm-quaddec" },
3298c2ecf20Sopenharmony_ci	{},
3308c2ecf20Sopenharmony_ci};
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_cistatic struct platform_driver ftm_quaddec_driver = {
3338c2ecf20Sopenharmony_ci	.driver = {
3348c2ecf20Sopenharmony_ci		.name = "ftm-quaddec",
3358c2ecf20Sopenharmony_ci		.of_match_table = ftm_quaddec_match,
3368c2ecf20Sopenharmony_ci	},
3378c2ecf20Sopenharmony_ci	.probe = ftm_quaddec_probe,
3388c2ecf20Sopenharmony_ci};
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_cimodule_platform_driver(ftm_quaddec_driver);
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
3438c2ecf20Sopenharmony_ciMODULE_AUTHOR("Kjeld Flarup <kfa@deif.com>");
3448c2ecf20Sopenharmony_ciMODULE_AUTHOR("Patrick Havelange <patrick.havelange@essensium.com>");
345