lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <Z5efcokgHix-k3lW@ishi>
Date: Tue, 28 Jan 2025 00:00:02 +0900
From: William Breathitt Gray <wbg@...nel.org>
To: Csókás Bence <csokas.bence@...lan.hu>
Cc: linux-iio@...r.kernel.org,
	"linux-kernel@...r.kernel.org" <linux-kernel@...r.kernel.org>,
	linux-arm-kernel@...ts.infradead.org, timestamp@...ts.linux.dev,
	Jonathan Cameron <jic23@...nel.org>,
	Lars-Peter Clausen <lars@...afoo.de>,
	Daniel Lezcano <daniel.lezcano@...aro.org>,
	Thomas Gleixner <tglx@...utronix.de>,
	Dipen Patel <dipenp@...dia.com>, dlechner@...libre.com
Subject: Re: [Q] Frequency & duty cycle measurement?

On Tue, Jan 21, 2025 at 04:19:27PM +0100, Csókás Bence wrote:
> Hi all,
> 
> we want to measure the frequency and duty cycle of a signal (relating to
> power consumption) using a hardware timer in our SoC (Microchip SAMA5D2).
> The hardware is capable of taking a snapshot of the timer value into another
> dedicated register pair (RA, RB) on the rising/falling edges, and a small
> `devmem`-based userspace utility was created as a working PoC. Now we want
> to move to a "proper" kernelspace solution.
> 
> However, none of the existing drivers seem to be able to do this; the
> closest was `drivers/counter/microchip-tcb-capture.c`, but that only seems
> to count one type of edge (rising/falling), and cannot give us the time
> between them, which would be needed for duty cycle calculation. The only
> other driver I could find was `drivers/clocksource/timer-atmel-tcb.c`, which
> again seems incapable of such measurements. Therefore, a new module will
> probably be needed; the question then becomes: which API to implement.
> 
> As `microchip-tcb-capture.c` uses the Generic Counter Interface, that was
> obviously a first stop. However, from what I could see, you can only
> represent (1) the number of times an edge has been encountered, and (2)
> rotary encodings (quadrature and direction-step decoders); and not the time
> between edges.
> 
> IIO_ALTVOLTAGE and IIO_CHAN_INFO_FREQUENCY/_PHASE also seemed promising
> (although the lack of IIO_CHAN_INFO_DUTY_CYCLE already posed a problem),
> until I saw that all current drivers are frequency *generators*, and not
> measurers, the latter seems to be completely unimplemented.
> 
> The only other contender I could find was the Hardware Timestamping Engine
> (HTE), but again, it's not clear whether (1) the API is even capable of
> relaying duty cycle information to userspace and (2) if it is, how would one
> go about implementing it.
> 
> It is also entirely possible I missed a driver or API that could handle this
> better, if so, please don't keep it to yourselves.
> 
> So, how could one go about implementing such a driver?
> 
> Bence

Hi Bence,

You could use the Counter chrdev interface to get frequency of counter
events such as rising/falling edges, as well as associating timer values
and snapshots. Do you get an interrupt on each timer snapshot, or is it
just a circular array buffer that gets filled until your purge it? The
implementation might look something like the following.

In the kernel driver, you would push a counter event for each capture; I
assume your hardware issues an interrupt each time a rising/falling edge
occurs:

    static irqreturn_t sama5d2_isr(int irq, void *dev_id)
    {
            struct counter_device *counter = dev_id;
            counter_push_event(counter, COUNTER_EVENT_CAPTURE, 0);
            return IRQ_HANDLED;
    }

In the userspace application, you would setup a Counter "watch" to
collect each desired timer value on the respective Counter events; I
assume RA and RB are Count 0 and Count 1 respectively, but if they
represent something else please let me know:

    static struct counter_watch watches[2] = {
            {
                    /* Component data: Count 0 count */
                    .component.type = COUNTER_COMPONENT_COUNT,
                    .component.scope = COUNTER_SCOPE_COUNT,
                    .component.parent = 0,
                    /* Event type: Capture */
                    .event = COUNTER_EVENT_CAPTURE,
                    /* Device event channel 0 */
                    .channel = 0,
            },
            {
                    /* Component data: Count 1 count */
                    .component.type = COUNTER_COMPONENT_COUNT,
                    .component.scope = COUNTER_SCOPE_COUNT,
                    .component.parent = 1,
                    /* Event type: Capture */
                    .event = COUNTER_EVENT_CAPTURE,
                    /* Device event channel 0 */
                    .channel = 0,
            },
    };
    ...
    int main(void)
    {
            int fd;
            int i;
	    unsigned long long delta_ts, delta_ra, delta_rb;
	    double ra_frequency, rb_frequency, rb_ra;
            struct counter_event first_capture[2], second_capture[2];
            
            /* Open Counter chrdev */
            fd = open("/dev/counter0", O_RDWR);
            
            for (i = 0; i < 2; i++) {
	            /* Register all Counter watches */
                    ioctl(fd, COUNTER_ADD_WATCH_IOCTL, watches + i);
            }
	    /* Start collecting Counter events */
            ioctl(fd, COUNTER_ENABLE_EVENTS_IOCTL);
            
            for (;;) {
	        /* Read first Counter event capture */
            	read(fd, first_capture, sizeof(first_capture));
	        /* Read second Counter event capture */
            	read(fd, second_capture, sizeof(second_capture));
                
		/* Within each capture, timestamp is the same so only
		 * first element of each capture needs to be compared */
		delta_ts = second_capture[0].timestamp - first_capture[0].timestamp;
		/* Compute deltas of timer register pair RA and RB.
		delta_ra = second_capture[0].value - first_capture[0].value;
		delta_rb = second_capture[1].value - first_capture[1].value;
                
		ra_frequency = (double)delta_ra / delta_ts;
		rb_frequency = (double)delta_rb / delta_ts;
		rb_ra = (double)delta_rb / delta_ra;
                
            	printf("RA frequency: %ld\n"
		       "RB frequency: %ld\n"
		       "RB per RA: %ld\n"
            	       ra_frequency, rb_frequency, rb_ra);
            }
            
            return 0;
    }

If RA and RB are provided as a memory buffer on your device, you can
instead expose them via DEFINE_COUNTER_ARRAY_CAPTURE() such as the
ti-ecap-capture driver does, then perform your userspace computations
by utilizing those respective "capture" array attribute values (via
chrdev like the example above or alternatively via sysfs).

Do you think this design would work for your needs?

William Breathitt Gray

Download attachment "signature.asc" of type "application/pgp-signature" (229 bytes)

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ