Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Merge tag 'counter-updates-for-6.15' of ssh://gitolite.kernel.org/pub/scm/linux/kernel/git/wbg/counter into char-misc-next

William writes:

Counter updates for 6.15

counter:
- Introduce the COUNTER_EVENT_DIRECTION_CHANGE event
- Introduce the COUNTER_COMP_COMPARE helper macro
microchip-tcb-cpature:
- Add IRQ handling
- Add support for capture extensions
- Add support for compare extension
ti-eqep:
- Add support for reading and detecting changes in direction
tools/counter:
- Add counter_watch_events executable to .gitignore
- Support COUNTER_EVENT_DIRECTION_CHANGE in counter_watch_events tool

* tag 'counter-updates-for-6.15' of ssh://gitolite.kernel.org/pub/scm/linux/kernel/git/wbg/counter:
counter: microchip-tcb-capture: Add support for RC Compare
counter: Introduce the compare component
counter: microchip-tcb-capture: Add capture extensions for registers RA/RB
counter: microchip-tcb-capture: Add IRQ handling
counter: ti-eqep: add direction support
tools/counter: add direction change event to watcher
counter: add direction change event
tools/counter: gitignore counter_watch_events

+253
+9
Documentation/ABI/testing/sysfs-bus-counter
··· 34 34 Description: 35 35 Count data of Count Y represented as a string. 36 36 37 + What: /sys/bus/counter/devices/counterX/countY/compare 38 + KernelVersion: 6.15 39 + Contact: linux-iio@vger.kernel.org 40 + Description: 41 + If the counter device supports compare registers -- registers 42 + used to compare counter channels against a particular count -- 43 + the compare count for channel Y is provided by this attribute. 44 + 37 45 What: /sys/bus/counter/devices/counterX/countY/capture 38 46 KernelVersion: 6.1 39 47 Contact: linux-iio@vger.kernel.org ··· 309 301 310 302 What: /sys/bus/counter/devices/counterX/cascade_counts_enable_component_id 311 303 What: /sys/bus/counter/devices/counterX/external_input_phase_clock_select_component_id 304 + What: /sys/bus/counter/devices/counterX/countY/compare_component_id 312 305 What: /sys/bus/counter/devices/counterX/countY/capture_component_id 313 306 What: /sys/bus/counter/devices/counterX/countY/ceiling_component_id 314 307 What: /sys/bus/counter/devices/counterX/countY/floor_component_id
+1
MAINTAINERS
··· 15644 15644 L: linux-iio@vger.kernel.org 15645 15645 S: Maintained 15646 15646 F: drivers/counter/microchip-tcb-capture.c 15647 + F: include/uapi/linux/counter/microchip-tcb-capture.h 15647 15648 15648 15649 MICROCHIP USB251XB DRIVER 15649 15650 M: Richard Leitner <richard.leitner@skidata.com>
+160
drivers/counter/microchip-tcb-capture.c
··· 6 6 */ 7 7 #include <linux/clk.h> 8 8 #include <linux/counter.h> 9 + #include <linux/interrupt.h> 9 10 #include <linux/mfd/syscon.h> 10 11 #include <linux/module.h> 11 12 #include <linux/mutex.h> 12 13 #include <linux/of.h> 14 + #include <linux/of_irq.h> 13 15 #include <linux/platform_device.h> 14 16 #include <linux/regmap.h> 17 + #include <uapi/linux/counter/microchip-tcb-capture.h> 15 18 #include <soc/at91/atmel_tcb.h> 16 19 17 20 #define ATMEL_TC_CMR_MASK (ATMEL_TC_LDRA_RISING | ATMEL_TC_LDRB_FALLING | \ 18 21 ATMEL_TC_ETRGEDG_RISING | ATMEL_TC_LDBDIS | \ 19 22 ATMEL_TC_LDBSTOP) 23 + 24 + #define ATMEL_TC_DEF_IRQS (ATMEL_TC_ETRGS | ATMEL_TC_COVFS | \ 25 + ATMEL_TC_LDRAS | ATMEL_TC_LDRBS | ATMEL_TC_CPCS) 20 26 21 27 #define ATMEL_TC_QDEN BIT(8) 22 28 #define ATMEL_TC_POSEN BIT(9) ··· 253 247 return 0; 254 248 } 255 249 250 + static int mchp_tc_count_cap_read(struct counter_device *counter, 251 + struct counter_count *count, size_t idx, u64 *val) 252 + { 253 + struct mchp_tc_data *const priv = counter_priv(counter); 254 + u32 cnt; 255 + int ret; 256 + 257 + switch (idx) { 258 + case COUNTER_MCHP_EXCAP_RA: 259 + ret = regmap_read(priv->regmap, ATMEL_TC_REG(priv->channel[0], RA), &cnt); 260 + break; 261 + case COUNTER_MCHP_EXCAP_RB: 262 + ret = regmap_read(priv->regmap, ATMEL_TC_REG(priv->channel[0], RB), &cnt); 263 + break; 264 + default: 265 + return -EINVAL; 266 + } 267 + 268 + if (ret < 0) 269 + return ret; 270 + 271 + *val = cnt; 272 + 273 + return 0; 274 + } 275 + 276 + static int mchp_tc_count_cap_write(struct counter_device *counter, 277 + struct counter_count *count, size_t idx, u64 val) 278 + { 279 + struct mchp_tc_data *const priv = counter_priv(counter); 280 + int ret; 281 + 282 + if (val > U32_MAX) 283 + return -ERANGE; 284 + 285 + switch (idx) { 286 + case COUNTER_MCHP_EXCAP_RA: 287 + ret = regmap_write(priv->regmap, ATMEL_TC_REG(priv->channel[0], RA), val); 288 + break; 289 + case COUNTER_MCHP_EXCAP_RB: 290 + ret = regmap_write(priv->regmap, ATMEL_TC_REG(priv->channel[0], RB), val); 291 + break; 292 + default: 293 + return -EINVAL; 294 + } 295 + 296 + return ret; 297 + } 298 + 299 + static int mchp_tc_count_compare_read(struct counter_device *counter, struct counter_count *count, 300 + u64 *val) 301 + { 302 + struct mchp_tc_data *const priv = counter_priv(counter); 303 + u32 cnt; 304 + int ret; 305 + 306 + ret = regmap_read(priv->regmap, ATMEL_TC_REG(priv->channel[0], RC), &cnt); 307 + if (ret < 0) 308 + return ret; 309 + 310 + *val = cnt; 311 + 312 + return 0; 313 + } 314 + 315 + static int mchp_tc_count_compare_write(struct counter_device *counter, struct counter_count *count, 316 + u64 val) 317 + { 318 + struct mchp_tc_data *const priv = counter_priv(counter); 319 + 320 + if (val > U32_MAX) 321 + return -ERANGE; 322 + 323 + return regmap_write(priv->regmap, ATMEL_TC_REG(priv->channel[0], RC), val); 324 + } 325 + 326 + static DEFINE_COUNTER_ARRAY_CAPTURE(mchp_tc_cnt_cap_array, 2); 327 + 328 + static struct counter_comp mchp_tc_count_ext[] = { 329 + COUNTER_COMP_ARRAY_CAPTURE(mchp_tc_count_cap_read, mchp_tc_count_cap_write, 330 + mchp_tc_cnt_cap_array), 331 + COUNTER_COMP_COMPARE(mchp_tc_count_compare_read, mchp_tc_count_compare_write), 332 + }; 333 + 256 334 static struct counter_count mchp_tc_counts[] = { 257 335 { 258 336 .id = 0, ··· 345 255 .num_functions = ARRAY_SIZE(mchp_tc_count_functions), 346 256 .synapses = mchp_tc_count_synapses, 347 257 .num_synapses = ARRAY_SIZE(mchp_tc_count_synapses), 258 + .ext = mchp_tc_count_ext, 259 + .num_ext = ARRAY_SIZE(mchp_tc_count_ext), 348 260 }, 349 261 }; 350 262 ··· 385 293 { .compatible = "atmel,sama5d3-tcb", .data = &tcb_sama5d3_config, }, 386 294 { /* sentinel */ } 387 295 }; 296 + 297 + static irqreturn_t mchp_tc_isr(int irq, void *dev_id) 298 + { 299 + struct counter_device *const counter = dev_id; 300 + struct mchp_tc_data *const priv = counter_priv(counter); 301 + u32 sr, mask; 302 + 303 + regmap_read(priv->regmap, ATMEL_TC_REG(priv->channel[0], SR), &sr); 304 + regmap_read(priv->regmap, ATMEL_TC_REG(priv->channel[0], IMR), &mask); 305 + 306 + sr &= mask; 307 + if (!(sr & ATMEL_TC_ALL_IRQ)) 308 + return IRQ_NONE; 309 + 310 + if (sr & ATMEL_TC_ETRGS) 311 + counter_push_event(counter, COUNTER_EVENT_CHANGE_OF_STATE, 312 + COUNTER_MCHP_EVCHN_CV); 313 + if (sr & ATMEL_TC_LDRAS) 314 + counter_push_event(counter, COUNTER_EVENT_CAPTURE, 315 + COUNTER_MCHP_EVCHN_RA); 316 + if (sr & ATMEL_TC_LDRBS) 317 + counter_push_event(counter, COUNTER_EVENT_CAPTURE, 318 + COUNTER_MCHP_EVCHN_RB); 319 + if (sr & ATMEL_TC_CPCS) 320 + counter_push_event(counter, COUNTER_EVENT_THRESHOLD, 321 + COUNTER_MCHP_EVCHN_RC); 322 + if (sr & ATMEL_TC_COVFS) 323 + counter_push_event(counter, COUNTER_EVENT_OVERFLOW, 324 + COUNTER_MCHP_EVCHN_CV); 325 + 326 + return IRQ_HANDLED; 327 + } 328 + 329 + static void mchp_tc_irq_remove(void *ptr) 330 + { 331 + struct mchp_tc_data *priv = ptr; 332 + 333 + regmap_write(priv->regmap, ATMEL_TC_REG(priv->channel[0], IDR), ATMEL_TC_DEF_IRQS); 334 + } 335 + 336 + static int mchp_tc_irq_enable(struct counter_device *const counter, int irq) 337 + { 338 + struct mchp_tc_data *const priv = counter_priv(counter); 339 + int ret = devm_request_irq(counter->parent, irq, mchp_tc_isr, 0, 340 + dev_name(counter->parent), counter); 341 + 342 + if (ret < 0) 343 + return ret; 344 + 345 + ret = regmap_write(priv->regmap, ATMEL_TC_REG(priv->channel[0], IER), ATMEL_TC_DEF_IRQS); 346 + if (ret < 0) 347 + return ret; 348 + 349 + ret = devm_add_action_or_reset(counter->parent, mchp_tc_irq_remove, priv); 350 + if (ret < 0) 351 + return ret; 352 + 353 + return 0; 354 + } 388 355 389 356 static void mchp_tc_clk_remove(void *ptr) 390 357 { ··· 528 377 counter->counts = mchp_tc_counts; 529 378 counter->num_signals = ARRAY_SIZE(mchp_tc_count_signals); 530 379 counter->signals = mchp_tc_count_signals; 380 + 381 + i = of_irq_get(np->parent, 0); 382 + if (i == -EPROBE_DEFER) 383 + return -EPROBE_DEFER; 384 + if (i > 0) { 385 + ret = mchp_tc_irq_enable(counter, i); 386 + if (ret < 0) 387 + return dev_err_probe(&pdev->dev, ret, "Failed to set up IRQ"); 388 + } 531 389 532 390 ret = devm_counter_add(&pdev->dev, counter); 533 391 if (ret < 0)
+32
drivers/counter/ti-eqep.c
··· 107 107 #define QCLR_PCE BIT(1) 108 108 #define QCLR_INT BIT(0) 109 109 110 + #define QEPSTS_UPEVNT BIT(7) 111 + #define QEPSTS_FDF BIT(6) 112 + #define QEPSTS_QDF BIT(5) 113 + #define QEPSTS_QDLF BIT(4) 114 + #define QEPSTS_COEF BIT(3) 115 + #define QEPSTS_CDEF BIT(2) 116 + #define QEPSTS_FIMF BIT(1) 117 + #define QEPSTS_PCEF BIT(0) 118 + 110 119 /* EQEP Inputs */ 111 120 enum { 112 121 TI_EQEP_SIGNAL_QEPA, /* QEPA/XCLK */ ··· 295 286 case COUNTER_EVENT_UNDERFLOW: 296 287 qeint |= QEINT_PCU; 297 288 break; 289 + case COUNTER_EVENT_DIRECTION_CHANGE: 290 + qeint |= QEINT_QDC; 291 + break; 298 292 } 299 293 } 300 294 ··· 310 298 switch (watch->event) { 311 299 case COUNTER_EVENT_OVERFLOW: 312 300 case COUNTER_EVENT_UNDERFLOW: 301 + case COUNTER_EVENT_DIRECTION_CHANGE: 313 302 if (watch->channel != 0) 314 303 return -EINVAL; 315 304 ··· 381 368 return 0; 382 369 } 383 370 371 + static int ti_eqep_direction_read(struct counter_device *counter, 372 + struct counter_count *count, 373 + enum counter_count_direction *direction) 374 + { 375 + struct ti_eqep_cnt *priv = counter_priv(counter); 376 + u32 qepsts; 377 + 378 + regmap_read(priv->regmap16, QEPSTS, &qepsts); 379 + 380 + *direction = (qepsts & QEPSTS_QDF) ? COUNTER_COUNT_DIRECTION_FORWARD 381 + : COUNTER_COUNT_DIRECTION_BACKWARD; 382 + 383 + return 0; 384 + } 385 + 384 386 static struct counter_comp ti_eqep_position_ext[] = { 385 387 COUNTER_COMP_CEILING(ti_eqep_position_ceiling_read, 386 388 ti_eqep_position_ceiling_write), 387 389 COUNTER_COMP_ENABLE(ti_eqep_position_enable_read, 388 390 ti_eqep_position_enable_write), 391 + COUNTER_COMP_DIRECTION(ti_eqep_direction_read), 389 392 }; 390 393 391 394 static struct counter_signal ti_eqep_signals[] = { ··· 467 438 468 439 if (qflg & QFLG_PCU) 469 440 counter_push_event(counter, COUNTER_EVENT_UNDERFLOW, 0); 441 + 442 + if (qflg & QFLG_QDC) 443 + counter_push_event(counter, COUNTER_EVENT_DIRECTION_CHANGE, 0); 470 444 471 445 regmap_write(priv->regmap16, QCLR, qflg); 472 446
+3
include/linux/counter.h
··· 580 580 #define COUNTER_COMP_CEILING(_read, _write) \ 581 581 COUNTER_COMP_COUNT_U64("ceiling", _read, _write) 582 582 583 + #define COUNTER_COMP_COMPARE(_read, _write) \ 584 + COUNTER_COMP_COUNT_U64("compare", _read, _write) 585 + 583 586 #define COUNTER_COMP_COUNT_MODE(_read, _write, _available) \ 584 587 { \ 585 588 .type = COUNTER_COMP_COUNT_MODE, \
+2
include/uapi/linux/counter.h
··· 65 65 COUNTER_EVENT_CHANGE_OF_STATE, 66 66 /* Count value captured */ 67 67 COUNTER_EVENT_CAPTURE, 68 + /* Direction change detected */ 69 + COUNTER_EVENT_DIRECTION_CHANGE, 68 70 }; 69 71 70 72 /**
+40
include/uapi/linux/counter/microchip-tcb-capture.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ 2 + /* 3 + * Channel numbers used by the microchip-tcb-capture driver 4 + * Copyright (C) 2025 Bence Csókás 5 + */ 6 + #ifndef _UAPI_COUNTER_MCHP_TCB_H_ 7 + #define _UAPI_COUNTER_MCHP_TCB_H_ 8 + 9 + /* 10 + * The driver defines the following components: 11 + * 12 + * Count 0 13 + * \__ Synapse 0 -- Signal 0 (Channel A, i.e. TIOA) 14 + * \__ Synapse 1 -- Signal 1 (Channel B, i.e. TIOB) 15 + * \__ Extension capture0 (RA register) 16 + * \__ Extension capture1 (RB register) 17 + * 18 + * It also supports the following events: 19 + * 20 + * Channel 0: 21 + * - CV register changed 22 + * - CV overflowed 23 + * - RA captured 24 + * Channel 1: 25 + * - RB captured 26 + * Channel 2: 27 + * - RC compare triggered 28 + */ 29 + 30 + /* Capture extensions */ 31 + #define COUNTER_MCHP_EXCAP_RA 0 32 + #define COUNTER_MCHP_EXCAP_RB 1 33 + 34 + /* Event channels */ 35 + #define COUNTER_MCHP_EVCHN_CV 0 36 + #define COUNTER_MCHP_EVCHN_RA 0 37 + #define COUNTER_MCHP_EVCHN_RB 1 38 + #define COUNTER_MCHP_EVCHN_RC 2 39 + 40 + #endif /* _UAPI_COUNTER_MCHP_TCB_H_ */
+1
tools/counter/.gitignore
··· 1 1 /counter_example 2 + /counter_watch_events 2 3 /include/linux/counter.h
+5
tools/counter/counter_watch_events.c
··· 38 38 "COUNTER_EVENT_INDEX", 39 39 "COUNTER_EVENT_CHANGE_OF_STATE", 40 40 "COUNTER_EVENT_CAPTURE", 41 + "COUNTER_EVENT_DIRECTION_CHANGE", 41 42 }; 42 43 43 44 static const char * const counter_component_type_name[] = { ··· 119 118 " evt_index (COUNTER_EVENT_INDEX)\n" 120 119 " evt_change_of_state (COUNTER_EVENT_CHANGE_OF_STATE)\n" 121 120 " evt_capture (COUNTER_EVENT_CAPTURE)\n" 121 + " evt_direction_change (COUNTER_EVENT_DIRECTION_CHANGE)\n" 122 122 "\n" 123 123 " chan=<n> channel <n> for this watch [default: 0]\n" 124 124 " id=<n> component id <n> for this watch [default: 0]\n" ··· 159 157 WATCH_EVENT_INDEX, 160 158 WATCH_EVENT_CHANGE_OF_STATE, 161 159 WATCH_EVENT_CAPTURE, 160 + WATCH_EVENT_DIRECTION_CHANGE, 162 161 WATCH_CHANNEL, 163 162 WATCH_ID, 164 163 WATCH_PARENT, ··· 186 183 [WATCH_EVENT_INDEX] = "evt_index", 187 184 [WATCH_EVENT_CHANGE_OF_STATE] = "evt_change_of_state", 188 185 [WATCH_EVENT_CAPTURE] = "evt_capture", 186 + [WATCH_EVENT_DIRECTION_CHANGE] = "evt_direction_change", 189 187 /* channel, id, parent */ 190 188 [WATCH_CHANNEL] = "chan", 191 189 [WATCH_ID] = "id", ··· 282 278 case WATCH_EVENT_INDEX: 283 279 case WATCH_EVENT_CHANGE_OF_STATE: 284 280 case WATCH_EVENT_CAPTURE: 281 + case WATCH_EVENT_DIRECTION_CHANGE: 285 282 /* match counter_event_type: subtract enum value */ 286 283 ret -= WATCH_EVENT_OVERFLOW; 287 284 watches[i].event = ret;