18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/**
38c2ecf20Sopenharmony_ci * Copyright (C) 2020 Microchip
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Author: Kamel Bouhara <kamel.bouhara@bootlin.com>
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci#include <linux/clk.h>
88c2ecf20Sopenharmony_ci#include <linux/counter.h>
98c2ecf20Sopenharmony_ci#include <linux/mfd/syscon.h>
108c2ecf20Sopenharmony_ci#include <linux/module.h>
118c2ecf20Sopenharmony_ci#include <linux/mutex.h>
128c2ecf20Sopenharmony_ci#include <linux/of.h>
138c2ecf20Sopenharmony_ci#include <linux/of_device.h>
148c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
158c2ecf20Sopenharmony_ci#include <linux/regmap.h>
168c2ecf20Sopenharmony_ci#include <soc/at91/atmel_tcb.h>
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#define ATMEL_TC_CMR_MASK	(ATMEL_TC_LDRA_RISING | ATMEL_TC_LDRB_FALLING | \
198c2ecf20Sopenharmony_ci				 ATMEL_TC_ETRGEDG_RISING | ATMEL_TC_LDBDIS | \
208c2ecf20Sopenharmony_ci				 ATMEL_TC_LDBSTOP)
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci#define ATMEL_TC_QDEN			BIT(8)
238c2ecf20Sopenharmony_ci#define ATMEL_TC_POSEN			BIT(9)
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_cistruct mchp_tc_data {
268c2ecf20Sopenharmony_ci	const struct atmel_tcb_config *tc_cfg;
278c2ecf20Sopenharmony_ci	struct counter_device counter;
288c2ecf20Sopenharmony_ci	struct regmap *regmap;
298c2ecf20Sopenharmony_ci	int qdec_mode;
308c2ecf20Sopenharmony_ci	int num_channels;
318c2ecf20Sopenharmony_ci	int channel[2];
328c2ecf20Sopenharmony_ci};
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_cienum mchp_tc_count_function {
358c2ecf20Sopenharmony_ci	MCHP_TC_FUNCTION_INCREASE,
368c2ecf20Sopenharmony_ci	MCHP_TC_FUNCTION_QUADRATURE,
378c2ecf20Sopenharmony_ci};
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_cistatic enum counter_count_function mchp_tc_count_functions[] = {
408c2ecf20Sopenharmony_ci	[MCHP_TC_FUNCTION_INCREASE] = COUNTER_COUNT_FUNCTION_INCREASE,
418c2ecf20Sopenharmony_ci	[MCHP_TC_FUNCTION_QUADRATURE] = COUNTER_COUNT_FUNCTION_QUADRATURE_X4,
428c2ecf20Sopenharmony_ci};
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_cienum mchp_tc_synapse_action {
458c2ecf20Sopenharmony_ci	MCHP_TC_SYNAPSE_ACTION_NONE = 0,
468c2ecf20Sopenharmony_ci	MCHP_TC_SYNAPSE_ACTION_RISING_EDGE,
478c2ecf20Sopenharmony_ci	MCHP_TC_SYNAPSE_ACTION_FALLING_EDGE,
488c2ecf20Sopenharmony_ci	MCHP_TC_SYNAPSE_ACTION_BOTH_EDGE
498c2ecf20Sopenharmony_ci};
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_cistatic enum counter_synapse_action mchp_tc_synapse_actions[] = {
528c2ecf20Sopenharmony_ci	[MCHP_TC_SYNAPSE_ACTION_NONE] = COUNTER_SYNAPSE_ACTION_NONE,
538c2ecf20Sopenharmony_ci	[MCHP_TC_SYNAPSE_ACTION_RISING_EDGE] = COUNTER_SYNAPSE_ACTION_RISING_EDGE,
548c2ecf20Sopenharmony_ci	[MCHP_TC_SYNAPSE_ACTION_FALLING_EDGE] = COUNTER_SYNAPSE_ACTION_FALLING_EDGE,
558c2ecf20Sopenharmony_ci	[MCHP_TC_SYNAPSE_ACTION_BOTH_EDGE] = COUNTER_SYNAPSE_ACTION_BOTH_EDGES,
568c2ecf20Sopenharmony_ci};
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_cistatic struct counter_signal mchp_tc_count_signals[] = {
598c2ecf20Sopenharmony_ci	{
608c2ecf20Sopenharmony_ci		.id = 0,
618c2ecf20Sopenharmony_ci		.name = "Channel A",
628c2ecf20Sopenharmony_ci	},
638c2ecf20Sopenharmony_ci	{
648c2ecf20Sopenharmony_ci		.id = 1,
658c2ecf20Sopenharmony_ci		.name = "Channel B",
668c2ecf20Sopenharmony_ci	}
678c2ecf20Sopenharmony_ci};
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_cistatic struct counter_synapse mchp_tc_count_synapses[] = {
708c2ecf20Sopenharmony_ci	{
718c2ecf20Sopenharmony_ci		.actions_list = mchp_tc_synapse_actions,
728c2ecf20Sopenharmony_ci		.num_actions = ARRAY_SIZE(mchp_tc_synapse_actions),
738c2ecf20Sopenharmony_ci		.signal = &mchp_tc_count_signals[0]
748c2ecf20Sopenharmony_ci	},
758c2ecf20Sopenharmony_ci	{
768c2ecf20Sopenharmony_ci		.actions_list = mchp_tc_synapse_actions,
778c2ecf20Sopenharmony_ci		.num_actions = ARRAY_SIZE(mchp_tc_synapse_actions),
788c2ecf20Sopenharmony_ci		.signal = &mchp_tc_count_signals[1]
798c2ecf20Sopenharmony_ci	}
808c2ecf20Sopenharmony_ci};
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_cistatic int mchp_tc_count_function_get(struct counter_device *counter,
838c2ecf20Sopenharmony_ci				      struct counter_count *count,
848c2ecf20Sopenharmony_ci				      size_t *function)
858c2ecf20Sopenharmony_ci{
868c2ecf20Sopenharmony_ci	struct mchp_tc_data *const priv = counter->priv;
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	if (priv->qdec_mode)
898c2ecf20Sopenharmony_ci		*function = MCHP_TC_FUNCTION_QUADRATURE;
908c2ecf20Sopenharmony_ci	else
918c2ecf20Sopenharmony_ci		*function = MCHP_TC_FUNCTION_INCREASE;
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	return 0;
948c2ecf20Sopenharmony_ci}
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_cistatic int mchp_tc_count_function_set(struct counter_device *counter,
978c2ecf20Sopenharmony_ci				      struct counter_count *count,
988c2ecf20Sopenharmony_ci				      size_t function)
998c2ecf20Sopenharmony_ci{
1008c2ecf20Sopenharmony_ci	struct mchp_tc_data *const priv = counter->priv;
1018c2ecf20Sopenharmony_ci	u32 bmr, cmr;
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	regmap_read(priv->regmap, ATMEL_TC_BMR, &bmr);
1048c2ecf20Sopenharmony_ci	regmap_read(priv->regmap, ATMEL_TC_REG(priv->channel[0], CMR), &cmr);
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	/* Set capture mode */
1078c2ecf20Sopenharmony_ci	cmr &= ~ATMEL_TC_WAVE;
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	switch (function) {
1108c2ecf20Sopenharmony_ci	case MCHP_TC_FUNCTION_INCREASE:
1118c2ecf20Sopenharmony_ci		priv->qdec_mode = 0;
1128c2ecf20Sopenharmony_ci		/* Set highest rate based on whether soc has gclk or not */
1138c2ecf20Sopenharmony_ci		bmr &= ~(ATMEL_TC_QDEN | ATMEL_TC_POSEN);
1148c2ecf20Sopenharmony_ci		if (!priv->tc_cfg->has_gclk)
1158c2ecf20Sopenharmony_ci			cmr |= ATMEL_TC_TIMER_CLOCK2;
1168c2ecf20Sopenharmony_ci		else
1178c2ecf20Sopenharmony_ci			cmr |= ATMEL_TC_TIMER_CLOCK1;
1188c2ecf20Sopenharmony_ci		/* Setup the period capture mode */
1198c2ecf20Sopenharmony_ci		cmr |=  ATMEL_TC_CMR_MASK;
1208c2ecf20Sopenharmony_ci		cmr &= ~(ATMEL_TC_ABETRG | ATMEL_TC_XC0);
1218c2ecf20Sopenharmony_ci		break;
1228c2ecf20Sopenharmony_ci	case MCHP_TC_FUNCTION_QUADRATURE:
1238c2ecf20Sopenharmony_ci		if (!priv->tc_cfg->has_qdec)
1248c2ecf20Sopenharmony_ci			return -EINVAL;
1258c2ecf20Sopenharmony_ci		/* In QDEC mode settings both channels 0 and 1 are required */
1268c2ecf20Sopenharmony_ci		if (priv->num_channels < 2 || priv->channel[0] != 0 ||
1278c2ecf20Sopenharmony_ci		    priv->channel[1] != 1) {
1288c2ecf20Sopenharmony_ci			pr_err("Invalid channels number or id for quadrature mode\n");
1298c2ecf20Sopenharmony_ci			return -EINVAL;
1308c2ecf20Sopenharmony_ci		}
1318c2ecf20Sopenharmony_ci		priv->qdec_mode = 1;
1328c2ecf20Sopenharmony_ci		bmr |= ATMEL_TC_QDEN | ATMEL_TC_POSEN;
1338c2ecf20Sopenharmony_ci		cmr |= ATMEL_TC_ETRGEDG_RISING | ATMEL_TC_ABETRG | ATMEL_TC_XC0;
1348c2ecf20Sopenharmony_ci		break;
1358c2ecf20Sopenharmony_ci	}
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	regmap_write(priv->regmap, ATMEL_TC_BMR, bmr);
1388c2ecf20Sopenharmony_ci	regmap_write(priv->regmap, ATMEL_TC_REG(priv->channel[0], CMR), cmr);
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	/* Enable clock and trigger counter */
1418c2ecf20Sopenharmony_ci	regmap_write(priv->regmap, ATMEL_TC_REG(priv->channel[0], CCR),
1428c2ecf20Sopenharmony_ci		     ATMEL_TC_CLKEN | ATMEL_TC_SWTRG);
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	if (priv->qdec_mode) {
1458c2ecf20Sopenharmony_ci		regmap_write(priv->regmap,
1468c2ecf20Sopenharmony_ci			     ATMEL_TC_REG(priv->channel[1], CMR), cmr);
1478c2ecf20Sopenharmony_ci		regmap_write(priv->regmap,
1488c2ecf20Sopenharmony_ci			     ATMEL_TC_REG(priv->channel[1], CCR),
1498c2ecf20Sopenharmony_ci			     ATMEL_TC_CLKEN | ATMEL_TC_SWTRG);
1508c2ecf20Sopenharmony_ci	}
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	return 0;
1538c2ecf20Sopenharmony_ci}
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_cistatic int mchp_tc_count_signal_read(struct counter_device *counter,
1568c2ecf20Sopenharmony_ci				     struct counter_signal *signal,
1578c2ecf20Sopenharmony_ci				     enum counter_signal_value *val)
1588c2ecf20Sopenharmony_ci{
1598c2ecf20Sopenharmony_ci	struct mchp_tc_data *const priv = counter->priv;
1608c2ecf20Sopenharmony_ci	bool sigstatus;
1618c2ecf20Sopenharmony_ci	u32 sr;
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	regmap_read(priv->regmap, ATMEL_TC_REG(priv->channel[0], SR), &sr);
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	if (signal->id == 1)
1668c2ecf20Sopenharmony_ci		sigstatus = (sr & ATMEL_TC_MTIOB);
1678c2ecf20Sopenharmony_ci	else
1688c2ecf20Sopenharmony_ci		sigstatus = (sr & ATMEL_TC_MTIOA);
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci	*val = sigstatus ? COUNTER_SIGNAL_HIGH : COUNTER_SIGNAL_LOW;
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	return 0;
1738c2ecf20Sopenharmony_ci}
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_cistatic int mchp_tc_count_action_get(struct counter_device *counter,
1768c2ecf20Sopenharmony_ci				    struct counter_count *count,
1778c2ecf20Sopenharmony_ci				    struct counter_synapse *synapse,
1788c2ecf20Sopenharmony_ci				    size_t *action)
1798c2ecf20Sopenharmony_ci{
1808c2ecf20Sopenharmony_ci	struct mchp_tc_data *const priv = counter->priv;
1818c2ecf20Sopenharmony_ci	u32 cmr;
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	if (priv->qdec_mode) {
1848c2ecf20Sopenharmony_ci		*action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES;
1858c2ecf20Sopenharmony_ci		return 0;
1868c2ecf20Sopenharmony_ci	}
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	/* Only TIOA signal is evaluated in non-QDEC mode */
1898c2ecf20Sopenharmony_ci	if (synapse->signal->id != 0) {
1908c2ecf20Sopenharmony_ci		*action = COUNTER_SYNAPSE_ACTION_NONE;
1918c2ecf20Sopenharmony_ci		return 0;
1928c2ecf20Sopenharmony_ci	}
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	regmap_read(priv->regmap, ATMEL_TC_REG(priv->channel[0], CMR), &cmr);
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	switch (cmr & ATMEL_TC_ETRGEDG) {
1978c2ecf20Sopenharmony_ci	default:
1988c2ecf20Sopenharmony_ci		*action = MCHP_TC_SYNAPSE_ACTION_NONE;
1998c2ecf20Sopenharmony_ci		break;
2008c2ecf20Sopenharmony_ci	case ATMEL_TC_ETRGEDG_RISING:
2018c2ecf20Sopenharmony_ci		*action = MCHP_TC_SYNAPSE_ACTION_RISING_EDGE;
2028c2ecf20Sopenharmony_ci		break;
2038c2ecf20Sopenharmony_ci	case ATMEL_TC_ETRGEDG_FALLING:
2048c2ecf20Sopenharmony_ci		*action = MCHP_TC_SYNAPSE_ACTION_FALLING_EDGE;
2058c2ecf20Sopenharmony_ci		break;
2068c2ecf20Sopenharmony_ci	case ATMEL_TC_ETRGEDG_BOTH:
2078c2ecf20Sopenharmony_ci		*action = MCHP_TC_SYNAPSE_ACTION_BOTH_EDGE;
2088c2ecf20Sopenharmony_ci		break;
2098c2ecf20Sopenharmony_ci	}
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	return 0;
2128c2ecf20Sopenharmony_ci}
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_cistatic int mchp_tc_count_action_set(struct counter_device *counter,
2158c2ecf20Sopenharmony_ci				    struct counter_count *count,
2168c2ecf20Sopenharmony_ci				    struct counter_synapse *synapse,
2178c2ecf20Sopenharmony_ci				    size_t action)
2188c2ecf20Sopenharmony_ci{
2198c2ecf20Sopenharmony_ci	struct mchp_tc_data *const priv = counter->priv;
2208c2ecf20Sopenharmony_ci	u32 edge = ATMEL_TC_ETRGEDG_NONE;
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	/* QDEC mode is rising edge only; only TIOA handled in non-QDEC mode */
2238c2ecf20Sopenharmony_ci	if (priv->qdec_mode || synapse->signal->id != 0)
2248c2ecf20Sopenharmony_ci		return -EINVAL;
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	switch (action) {
2278c2ecf20Sopenharmony_ci	case MCHP_TC_SYNAPSE_ACTION_NONE:
2288c2ecf20Sopenharmony_ci		edge = ATMEL_TC_ETRGEDG_NONE;
2298c2ecf20Sopenharmony_ci		break;
2308c2ecf20Sopenharmony_ci	case MCHP_TC_SYNAPSE_ACTION_RISING_EDGE:
2318c2ecf20Sopenharmony_ci		edge = ATMEL_TC_ETRGEDG_RISING;
2328c2ecf20Sopenharmony_ci		break;
2338c2ecf20Sopenharmony_ci	case MCHP_TC_SYNAPSE_ACTION_FALLING_EDGE:
2348c2ecf20Sopenharmony_ci		edge = ATMEL_TC_ETRGEDG_FALLING;
2358c2ecf20Sopenharmony_ci		break;
2368c2ecf20Sopenharmony_ci	case MCHP_TC_SYNAPSE_ACTION_BOTH_EDGE:
2378c2ecf20Sopenharmony_ci		edge = ATMEL_TC_ETRGEDG_BOTH;
2388c2ecf20Sopenharmony_ci		break;
2398c2ecf20Sopenharmony_ci	}
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci	return regmap_write_bits(priv->regmap,
2428c2ecf20Sopenharmony_ci				ATMEL_TC_REG(priv->channel[0], CMR),
2438c2ecf20Sopenharmony_ci				ATMEL_TC_ETRGEDG, edge);
2448c2ecf20Sopenharmony_ci}
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_cistatic int mchp_tc_count_read(struct counter_device *counter,
2478c2ecf20Sopenharmony_ci			      struct counter_count *count,
2488c2ecf20Sopenharmony_ci			      unsigned long *val)
2498c2ecf20Sopenharmony_ci{
2508c2ecf20Sopenharmony_ci	struct mchp_tc_data *const priv = counter->priv;
2518c2ecf20Sopenharmony_ci	u32 cnt;
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	regmap_read(priv->regmap, ATMEL_TC_REG(priv->channel[0], CV), &cnt);
2548c2ecf20Sopenharmony_ci	*val = cnt;
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci	return 0;
2578c2ecf20Sopenharmony_ci}
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_cistatic struct counter_count mchp_tc_counts[] = {
2608c2ecf20Sopenharmony_ci	{
2618c2ecf20Sopenharmony_ci		.id = 0,
2628c2ecf20Sopenharmony_ci		.name = "Timer Counter",
2638c2ecf20Sopenharmony_ci		.functions_list = mchp_tc_count_functions,
2648c2ecf20Sopenharmony_ci		.num_functions = ARRAY_SIZE(mchp_tc_count_functions),
2658c2ecf20Sopenharmony_ci		.synapses = mchp_tc_count_synapses,
2668c2ecf20Sopenharmony_ci		.num_synapses = ARRAY_SIZE(mchp_tc_count_synapses),
2678c2ecf20Sopenharmony_ci	},
2688c2ecf20Sopenharmony_ci};
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_cistatic const struct counter_ops mchp_tc_ops = {
2718c2ecf20Sopenharmony_ci	.signal_read  = mchp_tc_count_signal_read,
2728c2ecf20Sopenharmony_ci	.count_read   = mchp_tc_count_read,
2738c2ecf20Sopenharmony_ci	.function_get = mchp_tc_count_function_get,
2748c2ecf20Sopenharmony_ci	.function_set = mchp_tc_count_function_set,
2758c2ecf20Sopenharmony_ci	.action_get   = mchp_tc_count_action_get,
2768c2ecf20Sopenharmony_ci	.action_set   = mchp_tc_count_action_set
2778c2ecf20Sopenharmony_ci};
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_cistatic const struct atmel_tcb_config tcb_rm9200_config = {
2808c2ecf20Sopenharmony_ci		.counter_width = 16,
2818c2ecf20Sopenharmony_ci};
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_cistatic const struct atmel_tcb_config tcb_sam9x5_config = {
2848c2ecf20Sopenharmony_ci		.counter_width = 32,
2858c2ecf20Sopenharmony_ci};
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_cistatic const struct atmel_tcb_config tcb_sama5d2_config = {
2888c2ecf20Sopenharmony_ci		.counter_width = 32,
2898c2ecf20Sopenharmony_ci		.has_gclk = true,
2908c2ecf20Sopenharmony_ci		.has_qdec = true,
2918c2ecf20Sopenharmony_ci};
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_cistatic const struct atmel_tcb_config tcb_sama5d3_config = {
2948c2ecf20Sopenharmony_ci		.counter_width = 32,
2958c2ecf20Sopenharmony_ci		.has_qdec = true,
2968c2ecf20Sopenharmony_ci};
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_cistatic const struct of_device_id atmel_tc_of_match[] = {
2998c2ecf20Sopenharmony_ci	{ .compatible = "atmel,at91rm9200-tcb", .data = &tcb_rm9200_config, },
3008c2ecf20Sopenharmony_ci	{ .compatible = "atmel,at91sam9x5-tcb", .data = &tcb_sam9x5_config, },
3018c2ecf20Sopenharmony_ci	{ .compatible = "atmel,sama5d2-tcb", .data = &tcb_sama5d2_config, },
3028c2ecf20Sopenharmony_ci	{ .compatible = "atmel,sama5d3-tcb", .data = &tcb_sama5d3_config, },
3038c2ecf20Sopenharmony_ci	{ /* sentinel */ }
3048c2ecf20Sopenharmony_ci};
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_cistatic void mchp_tc_clk_remove(void *ptr)
3078c2ecf20Sopenharmony_ci{
3088c2ecf20Sopenharmony_ci	clk_disable_unprepare((struct clk *)ptr);
3098c2ecf20Sopenharmony_ci}
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_cistatic int mchp_tc_probe(struct platform_device *pdev)
3128c2ecf20Sopenharmony_ci{
3138c2ecf20Sopenharmony_ci	struct device_node *np = pdev->dev.of_node;
3148c2ecf20Sopenharmony_ci	const struct atmel_tcb_config *tcb_config;
3158c2ecf20Sopenharmony_ci	const struct of_device_id *match;
3168c2ecf20Sopenharmony_ci	struct mchp_tc_data *priv;
3178c2ecf20Sopenharmony_ci	char clk_name[7];
3188c2ecf20Sopenharmony_ci	struct regmap *regmap;
3198c2ecf20Sopenharmony_ci	struct clk *clk[3];
3208c2ecf20Sopenharmony_ci	int channel;
3218c2ecf20Sopenharmony_ci	int ret, i;
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
3248c2ecf20Sopenharmony_ci	if (!priv)
3258c2ecf20Sopenharmony_ci		return -ENOMEM;
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, priv);
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	match = of_match_node(atmel_tc_of_match, np->parent);
3308c2ecf20Sopenharmony_ci	tcb_config = match->data;
3318c2ecf20Sopenharmony_ci	if (!tcb_config) {
3328c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "No matching parent node found\n");
3338c2ecf20Sopenharmony_ci		return -ENODEV;
3348c2ecf20Sopenharmony_ci	}
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	regmap = syscon_node_to_regmap(np->parent);
3378c2ecf20Sopenharmony_ci	if (IS_ERR(regmap))
3388c2ecf20Sopenharmony_ci		return PTR_ERR(regmap);
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci	/* max. channels number is 2 when in QDEC mode */
3418c2ecf20Sopenharmony_ci	priv->num_channels = of_property_count_u32_elems(np, "reg");
3428c2ecf20Sopenharmony_ci	if (priv->num_channels < 0) {
3438c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Invalid or missing channel\n");
3448c2ecf20Sopenharmony_ci		return -EINVAL;
3458c2ecf20Sopenharmony_ci	}
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci	/* Register channels and initialize clocks */
3488c2ecf20Sopenharmony_ci	for (i = 0; i < priv->num_channels; i++) {
3498c2ecf20Sopenharmony_ci		ret = of_property_read_u32_index(np, "reg", i, &channel);
3508c2ecf20Sopenharmony_ci		if (ret < 0 || channel > 2)
3518c2ecf20Sopenharmony_ci			return -ENODEV;
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci		priv->channel[i] = channel;
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci		snprintf(clk_name, sizeof(clk_name), "t%d_clk", channel);
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci		clk[i] = of_clk_get_by_name(np->parent, clk_name);
3588c2ecf20Sopenharmony_ci		if (IS_ERR(clk[i])) {
3598c2ecf20Sopenharmony_ci			/* Fallback to t0_clk */
3608c2ecf20Sopenharmony_ci			clk[i] = of_clk_get_by_name(np->parent, "t0_clk");
3618c2ecf20Sopenharmony_ci			if (IS_ERR(clk[i]))
3628c2ecf20Sopenharmony_ci				return PTR_ERR(clk[i]);
3638c2ecf20Sopenharmony_ci		}
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci		ret = clk_prepare_enable(clk[i]);
3668c2ecf20Sopenharmony_ci		if (ret)
3678c2ecf20Sopenharmony_ci			return ret;
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_ci		ret = devm_add_action_or_reset(&pdev->dev,
3708c2ecf20Sopenharmony_ci					       mchp_tc_clk_remove,
3718c2ecf20Sopenharmony_ci					       clk[i]);
3728c2ecf20Sopenharmony_ci		if (ret)
3738c2ecf20Sopenharmony_ci			return ret;
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci		dev_dbg(&pdev->dev,
3768c2ecf20Sopenharmony_ci			"Initialized capture mode on channel %d\n",
3778c2ecf20Sopenharmony_ci			channel);
3788c2ecf20Sopenharmony_ci	}
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	priv->tc_cfg = tcb_config;
3818c2ecf20Sopenharmony_ci	priv->regmap = regmap;
3828c2ecf20Sopenharmony_ci	priv->counter.name = dev_name(&pdev->dev);
3838c2ecf20Sopenharmony_ci	priv->counter.parent = &pdev->dev;
3848c2ecf20Sopenharmony_ci	priv->counter.ops = &mchp_tc_ops;
3858c2ecf20Sopenharmony_ci	priv->counter.num_counts = ARRAY_SIZE(mchp_tc_counts);
3868c2ecf20Sopenharmony_ci	priv->counter.counts = mchp_tc_counts;
3878c2ecf20Sopenharmony_ci	priv->counter.num_signals = ARRAY_SIZE(mchp_tc_count_signals);
3888c2ecf20Sopenharmony_ci	priv->counter.signals = mchp_tc_count_signals;
3898c2ecf20Sopenharmony_ci	priv->counter.priv = priv;
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci	return devm_counter_register(&pdev->dev, &priv->counter);
3928c2ecf20Sopenharmony_ci}
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_cistatic const struct of_device_id mchp_tc_dt_ids[] = {
3958c2ecf20Sopenharmony_ci	{ .compatible = "microchip,tcb-capture", },
3968c2ecf20Sopenharmony_ci	{ /* sentinel */ },
3978c2ecf20Sopenharmony_ci};
3988c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, mchp_tc_dt_ids);
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_cistatic struct platform_driver mchp_tc_driver = {
4018c2ecf20Sopenharmony_ci	.probe = mchp_tc_probe,
4028c2ecf20Sopenharmony_ci	.driver = {
4038c2ecf20Sopenharmony_ci		.name = "microchip-tcb-capture",
4048c2ecf20Sopenharmony_ci		.of_match_table = mchp_tc_dt_ids,
4058c2ecf20Sopenharmony_ci	},
4068c2ecf20Sopenharmony_ci};
4078c2ecf20Sopenharmony_cimodule_platform_driver(mchp_tc_driver);
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ciMODULE_AUTHOR("Kamel Bouhara <kamel.bouhara@bootlin.com>");
4108c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Microchip TCB Capture driver");
4118c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
412