407 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (C) 2020 Microchip4 *5 * Author: Kamel Bouhara <kamel.bouhara@bootlin.com>6 */7#include <linux/clk.h>8#include <linux/counter.h>9#include <linux/mfd/syscon.h>10#include <linux/module.h>11#include <linux/mutex.h>12#include <linux/of.h>13#include <linux/platform_device.h>14#include <linux/regmap.h>15#include <soc/at91/atmel_tcb.h>16 17#define ATMEL_TC_CMR_MASK (ATMEL_TC_LDRA_RISING | ATMEL_TC_LDRB_FALLING | \18 ATMEL_TC_ETRGEDG_RISING | ATMEL_TC_LDBDIS | \19 ATMEL_TC_LDBSTOP)20 21#define ATMEL_TC_QDEN BIT(8)22#define ATMEL_TC_POSEN BIT(9)23 24struct mchp_tc_data {25 const struct atmel_tcb_config *tc_cfg;26 struct regmap *regmap;27 int qdec_mode;28 int num_channels;29 int channel[2];30};31 32static const enum counter_function mchp_tc_count_functions[] = {33 COUNTER_FUNCTION_INCREASE,34 COUNTER_FUNCTION_QUADRATURE_X4,35};36 37static const enum counter_synapse_action mchp_tc_synapse_actions[] = {38 COUNTER_SYNAPSE_ACTION_NONE,39 COUNTER_SYNAPSE_ACTION_RISING_EDGE,40 COUNTER_SYNAPSE_ACTION_FALLING_EDGE,41 COUNTER_SYNAPSE_ACTION_BOTH_EDGES,42};43 44static struct counter_signal mchp_tc_count_signals[] = {45 {46 .id = 0,47 .name = "Channel A",48 },49 {50 .id = 1,51 .name = "Channel B",52 }53};54 55static struct counter_synapse mchp_tc_count_synapses[] = {56 {57 .actions_list = mchp_tc_synapse_actions,58 .num_actions = ARRAY_SIZE(mchp_tc_synapse_actions),59 .signal = &mchp_tc_count_signals[0]60 },61 {62 .actions_list = mchp_tc_synapse_actions,63 .num_actions = ARRAY_SIZE(mchp_tc_synapse_actions),64 .signal = &mchp_tc_count_signals[1]65 }66};67 68static int mchp_tc_count_function_read(struct counter_device *counter,69 struct counter_count *count,70 enum counter_function *function)71{72 struct mchp_tc_data *const priv = counter_priv(counter);73 74 if (priv->qdec_mode)75 *function = COUNTER_FUNCTION_QUADRATURE_X4;76 else77 *function = COUNTER_FUNCTION_INCREASE;78 79 return 0;80}81 82static int mchp_tc_count_function_write(struct counter_device *counter,83 struct counter_count *count,84 enum counter_function function)85{86 struct mchp_tc_data *const priv = counter_priv(counter);87 u32 bmr, cmr;88 89 regmap_read(priv->regmap, ATMEL_TC_BMR, &bmr);90 regmap_read(priv->regmap, ATMEL_TC_REG(priv->channel[0], CMR), &cmr);91 92 /* Set capture mode */93 cmr &= ~ATMEL_TC_WAVE;94 95 switch (function) {96 case COUNTER_FUNCTION_INCREASE:97 priv->qdec_mode = 0;98 /* Set highest rate based on whether soc has gclk or not */99 bmr &= ~(ATMEL_TC_QDEN | ATMEL_TC_POSEN);100 if (!priv->tc_cfg->has_gclk)101 cmr |= ATMEL_TC_TIMER_CLOCK2;102 else103 cmr |= ATMEL_TC_TIMER_CLOCK1;104 /* Setup the period capture mode */105 cmr |= ATMEL_TC_CMR_MASK;106 cmr &= ~(ATMEL_TC_ABETRG | ATMEL_TC_XC0);107 break;108 case COUNTER_FUNCTION_QUADRATURE_X4:109 if (!priv->tc_cfg->has_qdec)110 return -EINVAL;111 /* In QDEC mode settings both channels 0 and 1 are required */112 if (priv->num_channels < 2 || priv->channel[0] != 0 ||113 priv->channel[1] != 1) {114 pr_err("Invalid channels number or id for quadrature mode\n");115 return -EINVAL;116 }117 priv->qdec_mode = 1;118 bmr |= ATMEL_TC_QDEN | ATMEL_TC_POSEN;119 cmr |= ATMEL_TC_ETRGEDG_RISING | ATMEL_TC_ABETRG | ATMEL_TC_XC0;120 break;121 default:122 /* should never reach this path */123 return -EINVAL;124 }125 126 regmap_write(priv->regmap, ATMEL_TC_BMR, bmr);127 regmap_write(priv->regmap, ATMEL_TC_REG(priv->channel[0], CMR), cmr);128 129 /* Enable clock and trigger counter */130 regmap_write(priv->regmap, ATMEL_TC_REG(priv->channel[0], CCR),131 ATMEL_TC_CLKEN | ATMEL_TC_SWTRG);132 133 if (priv->qdec_mode) {134 regmap_write(priv->regmap,135 ATMEL_TC_REG(priv->channel[1], CMR), cmr);136 regmap_write(priv->regmap,137 ATMEL_TC_REG(priv->channel[1], CCR),138 ATMEL_TC_CLKEN | ATMEL_TC_SWTRG);139 }140 141 return 0;142}143 144static int mchp_tc_count_signal_read(struct counter_device *counter,145 struct counter_signal *signal,146 enum counter_signal_level *lvl)147{148 struct mchp_tc_data *const priv = counter_priv(counter);149 bool sigstatus;150 u32 sr;151 152 regmap_read(priv->regmap, ATMEL_TC_REG(priv->channel[0], SR), &sr);153 154 if (signal->id == 1)155 sigstatus = (sr & ATMEL_TC_MTIOB);156 else157 sigstatus = (sr & ATMEL_TC_MTIOA);158 159 *lvl = sigstatus ? COUNTER_SIGNAL_LEVEL_HIGH : COUNTER_SIGNAL_LEVEL_LOW;160 161 return 0;162}163 164static int mchp_tc_count_action_read(struct counter_device *counter,165 struct counter_count *count,166 struct counter_synapse *synapse,167 enum counter_synapse_action *action)168{169 struct mchp_tc_data *const priv = counter_priv(counter);170 u32 cmr;171 172 if (priv->qdec_mode) {173 *action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES;174 return 0;175 }176 177 /* Only TIOA signal is evaluated in non-QDEC mode */178 if (synapse->signal->id != 0) {179 *action = COUNTER_SYNAPSE_ACTION_NONE;180 return 0;181 }182 183 regmap_read(priv->regmap, ATMEL_TC_REG(priv->channel[0], CMR), &cmr);184 185 switch (cmr & ATMEL_TC_ETRGEDG) {186 default:187 *action = COUNTER_SYNAPSE_ACTION_NONE;188 break;189 case ATMEL_TC_ETRGEDG_RISING:190 *action = COUNTER_SYNAPSE_ACTION_RISING_EDGE;191 break;192 case ATMEL_TC_ETRGEDG_FALLING:193 *action = COUNTER_SYNAPSE_ACTION_FALLING_EDGE;194 break;195 case ATMEL_TC_ETRGEDG_BOTH:196 *action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES;197 break;198 }199 200 return 0;201}202 203static int mchp_tc_count_action_write(struct counter_device *counter,204 struct counter_count *count,205 struct counter_synapse *synapse,206 enum counter_synapse_action action)207{208 struct mchp_tc_data *const priv = counter_priv(counter);209 u32 edge = ATMEL_TC_ETRGEDG_NONE;210 211 /* QDEC mode is rising edge only; only TIOA handled in non-QDEC mode */212 if (priv->qdec_mode || synapse->signal->id != 0)213 return -EINVAL;214 215 switch (action) {216 case COUNTER_SYNAPSE_ACTION_NONE:217 edge = ATMEL_TC_ETRGEDG_NONE;218 break;219 case COUNTER_SYNAPSE_ACTION_RISING_EDGE:220 edge = ATMEL_TC_ETRGEDG_RISING;221 break;222 case COUNTER_SYNAPSE_ACTION_FALLING_EDGE:223 edge = ATMEL_TC_ETRGEDG_FALLING;224 break;225 case COUNTER_SYNAPSE_ACTION_BOTH_EDGES:226 edge = ATMEL_TC_ETRGEDG_BOTH;227 break;228 default:229 /* should never reach this path */230 return -EINVAL;231 }232 233 return regmap_write_bits(priv->regmap,234 ATMEL_TC_REG(priv->channel[0], CMR),235 ATMEL_TC_ETRGEDG, edge);236}237 238static int mchp_tc_count_read(struct counter_device *counter,239 struct counter_count *count, u64 *val)240{241 struct mchp_tc_data *const priv = counter_priv(counter);242 u32 cnt;243 244 regmap_read(priv->regmap, ATMEL_TC_REG(priv->channel[0], CV), &cnt);245 *val = cnt;246 247 return 0;248}249 250static struct counter_count mchp_tc_counts[] = {251 {252 .id = 0,253 .name = "Timer Counter",254 .functions_list = mchp_tc_count_functions,255 .num_functions = ARRAY_SIZE(mchp_tc_count_functions),256 .synapses = mchp_tc_count_synapses,257 .num_synapses = ARRAY_SIZE(mchp_tc_count_synapses),258 },259};260 261static const struct counter_ops mchp_tc_ops = {262 .signal_read = mchp_tc_count_signal_read,263 .count_read = mchp_tc_count_read,264 .function_read = mchp_tc_count_function_read,265 .function_write = mchp_tc_count_function_write,266 .action_read = mchp_tc_count_action_read,267 .action_write = mchp_tc_count_action_write268};269 270static const struct atmel_tcb_config tcb_rm9200_config = {271 .counter_width = 16,272};273 274static const struct atmel_tcb_config tcb_sam9x5_config = {275 .counter_width = 32,276};277 278static const struct atmel_tcb_config tcb_sama5d2_config = {279 .counter_width = 32,280 .has_gclk = true,281 .has_qdec = true,282};283 284static const struct atmel_tcb_config tcb_sama5d3_config = {285 .counter_width = 32,286 .has_qdec = true,287};288 289static const struct of_device_id atmel_tc_of_match[] = {290 { .compatible = "atmel,at91rm9200-tcb", .data = &tcb_rm9200_config, },291 { .compatible = "atmel,at91sam9x5-tcb", .data = &tcb_sam9x5_config, },292 { .compatible = "atmel,sama5d2-tcb", .data = &tcb_sama5d2_config, },293 { .compatible = "atmel,sama5d3-tcb", .data = &tcb_sama5d3_config, },294 { /* sentinel */ }295};296 297static void mchp_tc_clk_remove(void *ptr)298{299 clk_disable_unprepare((struct clk *)ptr);300}301 302static int mchp_tc_probe(struct platform_device *pdev)303{304 struct device_node *np = pdev->dev.of_node;305 const struct atmel_tcb_config *tcb_config;306 const struct of_device_id *match;307 struct counter_device *counter;308 struct mchp_tc_data *priv;309 char clk_name[7];310 struct regmap *regmap;311 struct clk *clk[3];312 int channel;313 int ret, i;314 315 counter = devm_counter_alloc(&pdev->dev, sizeof(*priv));316 if (!counter)317 return -ENOMEM;318 priv = counter_priv(counter);319 320 match = of_match_node(atmel_tc_of_match, np->parent);321 tcb_config = match->data;322 if (!tcb_config) {323 dev_err(&pdev->dev, "No matching parent node found\n");324 return -ENODEV;325 }326 327 regmap = syscon_node_to_regmap(np->parent);328 if (IS_ERR(regmap))329 return PTR_ERR(regmap);330 331 /* max. channels number is 2 when in QDEC mode */332 priv->num_channels = of_property_count_u32_elems(np, "reg");333 if (priv->num_channels < 0) {334 dev_err(&pdev->dev, "Invalid or missing channel\n");335 return -EINVAL;336 }337 338 /* Register channels and initialize clocks */339 for (i = 0; i < priv->num_channels; i++) {340 ret = of_property_read_u32_index(np, "reg", i, &channel);341 if (ret < 0 || channel > 2)342 return -ENODEV;343 344 priv->channel[i] = channel;345 346 snprintf(clk_name, sizeof(clk_name), "t%d_clk", channel);347 348 clk[i] = of_clk_get_by_name(np->parent, clk_name);349 if (IS_ERR(clk[i])) {350 /* Fallback to t0_clk */351 clk[i] = of_clk_get_by_name(np->parent, "t0_clk");352 if (IS_ERR(clk[i]))353 return PTR_ERR(clk[i]);354 }355 356 ret = clk_prepare_enable(clk[i]);357 if (ret)358 return ret;359 360 ret = devm_add_action_or_reset(&pdev->dev,361 mchp_tc_clk_remove,362 clk[i]);363 if (ret)364 return ret;365 366 dev_dbg(&pdev->dev,367 "Initialized capture mode on channel %d\n",368 channel);369 }370 371 priv->tc_cfg = tcb_config;372 priv->regmap = regmap;373 counter->name = dev_name(&pdev->dev);374 counter->parent = &pdev->dev;375 counter->ops = &mchp_tc_ops;376 counter->num_counts = ARRAY_SIZE(mchp_tc_counts);377 counter->counts = mchp_tc_counts;378 counter->num_signals = ARRAY_SIZE(mchp_tc_count_signals);379 counter->signals = mchp_tc_count_signals;380 381 ret = devm_counter_add(&pdev->dev, counter);382 if (ret < 0)383 return dev_err_probe(&pdev->dev, ret, "Failed to add counter\n");384 385 return 0;386}387 388static const struct of_device_id mchp_tc_dt_ids[] = {389 { .compatible = "microchip,tcb-capture", },390 { /* sentinel */ },391};392MODULE_DEVICE_TABLE(of, mchp_tc_dt_ids);393 394static struct platform_driver mchp_tc_driver = {395 .probe = mchp_tc_probe,396 .driver = {397 .name = "microchip-tcb-capture",398 .of_match_table = mchp_tc_dt_ids,399 },400};401module_platform_driver(mchp_tc_driver);402 403MODULE_AUTHOR("Kamel Bouhara <kamel.bouhara@bootlin.com>");404MODULE_DESCRIPTION("Microchip TCB Capture driver");405MODULE_LICENSE("GPL v2");406MODULE_IMPORT_NS(COUNTER);407