References for using mem/dev to configure TSC?

I’m using a Beaglebone Black Wireless running Debian. I want to use a Linux side C program to configure the TSC for ADC use. (three channels, three steps, one-shot, etc.) I have code that works well on PRU0 but I want to move the configuration of the TSC to the Linux side to save memory on the PRU0. Since the configuration is really just a startup once-and-done non-time critical function, I think having the Linux side do this makes sense. I’m starting to use mmap to configure this to replicate the code written in C for the PRU0. But I get segmentation errors when compiling the code from the PRU0 as is. So, I’m basically rewriting it using mmap. I could really use a reference because I’ve not used mmap before. Anyone know of a reference?

Walter

This works for me (using the am335x/sys_tscAdcSs.h header from the pru support package):

#include <stdint.h>
#include "sys_tscAdcSs.h"

static sysTscAdcSs volatile *adc;

// for complete compatibility with PRU
#undef ADC_TSC
#define ADC_TSC (*adc)

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>

int main()
{
	int fd = open( "/dev/mem", O_RDWR | O_CLOEXEC );
	if( fd < 0 ) {
		perror( "open /dev/mem" );
		return 1;
	}
	adc = mmap( NULL, 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0x44e0d000 );
	if( adc == MAP_FAILED ) {
		perror( "mmap" );
		return 1;
	}
	close( fd );  // optional

	printf( "ADC module identifier: 0x%08x\n", ADC_TSC.REVISION );
	ADC_TSC.ADC_CLKDIV = 7;  // configure clock divider to get 3 MHz adc clock
	// etc, do remaining initialization here

	return 0;
}

I would recommend using UIO instead of /dev/mem though, which is a mechanism that also allows userspace access to memory-mapped I/O, but limited to a single peripheral and without requiring root privileges. It also ensures the kernel will enable the module clock and if desired UIO can deliver interrupts to userspace.

I’ve added an example device tree overlay for this to my overlay-utils project: overlay-utils/adc.dtsi at master · mvduin/overlay-utils · GitHub

If you use this instead of whatever overlay you’re currently using to force-enable the ADC yet disable the ADC kernel driver (I’m assuming you do have one… right?) and, as mentioned in uio.h, install this uio.conf file into /etc/modprobe.d/, then after reboot there should be a uio device for the adc:

@beaglebone:~$ ls -l /dev/uio/adc
lrwxrwxrwx 1 root root 7 Sep 14 23:21 /dev/uio/adc -> ../uio0

(the actual uio device number may vary)

The only difference in usage is that you open the uio device instead of /dev/mem:

int fd = open( "/dev/uio/adc", O_RDWR | O_CLOEXEC );

and give offset 0 to mmap:

adc = mmap( NULL, 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0 );

Of course on the linux side you’re not restricted to using C, e.g. you could use my py-uio library, to which I’ve added a basic structure for the adc:

from uio.device import Uio
from uio.ti.subarctic.adc import Adc
adc_dev = Uio( "/dev/uio/adc" )
adc = adc_dev.map( Adc )
print( f'ADC module identifier: 0x{adc.ident:08x}' )
adc.clkdiv = 7  # configure clock divider to get 3 MHz adc clock
1 Like