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