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