Message ID | 161632673124.9191.8069161888906800635.sendpatchset@octo (mailing list archive) |
---|---|
State | RFC |
Delegated to: | Geert Uytterhoeven |
Headers | show |
Series | sh73a0 CMT1 test setup using UIO | expand |
Hi Magnus, Thanks for your patch! On Sun, Mar 21, 2021 at 1:12 PM Magnus Damm <damm@opensource.se> wrote: > --- /dev/null 2019-10-16 00:27:13.659405289 +0900 > +++ uio-cmt-test-20210321.c 2021-03-21 19:41:24.469083859 +0900 > @@ -0,0 +1,179 @@ > +/* > + * uio-cmt-test-20210321.c - UIO CMT example test code, 20210321 Magnus Damm > + * > + * A small linux program that programs the CMT timer and waits for IRQs > + * > + * Compile for Linux using: > + * $ cross-gcc -o uio-cmt-test uio-cmt-test.c > + * > + * Designed to work with the Linux UIO kernel driver uio_pdrv_genirq.c > + */ > + > +#include <stdio.h> > +#include <stdlib.h> > +#include <unistd.h> > +#include <string.h> > +#include <sys/types.h> > +#include <sys/stat.h> > +#include <sys/mman.h> > +#include <fcntl.h> > + > +static int fgets_with_openclose(char *fname, char *buf, size_t maxlen) { > + FILE *fp; > + > + if ((fp = fopen(fname, "r")) != NULL) { > + fgets(buf, maxlen, fp); > + fclose(fp); > + return strlen(buf); If buf[] contains no terminating NUL character, this will read beyond the end of the buffer. > + } else { > + return -1; > + } > +} > + > +struct uio_device { > + char *name; > + char *path; > + int fd; > +}; > + > +#define MAXUIOIDS 100 > +#define MAXNAMELEN 256 > + > +static int locate_uio_device(char *name, struct uio_device *udp) > +{ > + char fname[MAXNAMELEN], buf[MAXNAMELEN]; > + int uio_id, i; > + > + for (uio_id = 0; uio_id < MAXUIOIDS; uio_id++) { > + sprintf(fname, "/sys/class/uio/uio%d/name", uio_id); asprintf()? > + if (fgets_with_openclose(fname, buf, MAXNAMELEN) < 0) > + continue; > + if (strncmp(name, buf, strlen(name)) == 0) > + break; > + } > + > + if (uio_id >= MAXUIOIDS) > + return -1; > + > + udp->name = strdup(buf); > + udp->path = strdup(fname); > + udp->path[strlen(udp->path) - 4] = '\0'; > + > + sprintf(buf, "/dev/uio%d", uio_id); asprintf() > + udp->fd = open(buf, O_RDWR|O_SYNC /*| O_NONBLOCK*/); > + > + if (udp->fd < 0) { > + perror("open"); > + return -1; > + } > + > + return 0; > +} > + > +/* offsets apply to sh73a0 */ > +#define CMT_CMSTR 0x000 /* start/stop register, some bits reserved as 1 */ > +#define CMT_CMSTR_CH0 (1 << 0) /* set bit to one to start channel */ > +#define CMT_CMCSR 0x10 /* 0x124 enables interrupts and selects CLK/8 */ > +#define CMT_CMCSR_CMF (1 << 15) /* clear bit to ack compare match event */ > +#define CMT_CMCNT 0x14 /* counting up, set to 0 */ > +#define CMT_CMCOR 0x18 /* match value, set to ~0 */ > + > +struct uio_map { > + unsigned long address; > + unsigned long size; > + void *iomem; > +}; > + > +static int setup_uio_map(struct uio_device *udp, int nr, struct uio_map *ump) > +{ > + char fname[MAXNAMELEN], buf[MAXNAMELEN]; > + > + sprintf(fname, "%s/maps/map%d/addr", udp->path, nr); asprintf() > + if (fgets_with_openclose(fname, buf, MAXNAMELEN) <= 0) > + return -1; > + > + ump->address = strtoul(buf, NULL, 0); > + > + sprintf(fname, "%s/maps/map%d/size", udp->path, nr); asprintf() > + if (fgets_with_openclose(fname, buf, MAXNAMELEN) <= 0) > + return -1; > + > + ump->size = strtoul(buf, NULL, 0); > + > + ump->iomem = mmap(0, ump->size, > + PROT_READ|PROT_WRITE, MAP_SHARED, > + udp->fd, nr * getpagesize()); > + > + if (ump->iomem == MAP_FAILED) > + return -1; > + > + return 0; > +} > + > +struct uio_device uio_dev; > +struct uio_map uio_mmio; > + > +int main(int argc, char *argv[]) > +{ > + int k; unsigned int > + int ret; > + > + ret = locate_uio_device("timer", &uio_dev); > + if (ret < 0) > + return ret; > + > + printf("found matching UIO device at %s\n", uio_dev.path); > + > + ret = setup_uio_map(&uio_dev, 0, &uio_mmio); > + if (ret < 0) > + return ret; > + > + { > + unsigned long *cmstr = (uio_mmio.iomem + CMT_CMSTR); > + unsigned short *cmcsr = (uio_mmio.iomem + CMT_CMCSR); > + unsigned long *cmcor = (uio_mmio.iomem + CMT_CMCOR); This is not portable across 32-bit/64-bit platforms. uint32_t *... > + > + /* Stop timer channel */ > + *cmstr &= ~CMT_CMSTR_CH0; > + > + /* Initialize CMCSR */ > + *cmcsr = 0x124; > + > + /* Initialize CMCOR */ > + *cmcor = (32768 / 8) * 2; /* interrupt after about 2s */ > + > + /* Enable interrupt in UIO driver */ > + { > + unsigned long enable = 1; uint32_t > + write(uio_dev.fd, &enable, sizeof(u_long)); > + } > + > + /* Start timer channel */ > + *cmstr |= CMT_CMSTR_CH0; > + > + /* test by processing 3 interrupts */ > + for (k = 0; k < 3; k++) { > + /* Wait for interrupt */ > + { > + unsigned long n_pending; uint32_t > + read(uio_dev.fd, &n_pending, sizeof(u_long)); > + } > + > + printf("IRQ nr %d\n", k); > + > + /* ack match in CMCSR */ > + *cmcsr &= ~CMT_CMCSR_CMF; > + > + /* Enable interrupt in UIO driver */ > + { > + unsigned long enable = 1; > + write(uio_dev.fd, &enable, sizeof(u_long)); > + } > + } > + > + /* Stop timer channel */ > + *cmstr &= ~CMT_CMSTR_CH0; > + } > + > + return 0; > +} Gr{oetje,eeting}s, Geert
--- /dev/null 2019-10-16 00:27:13.659405289 +0900 +++ uio-cmt-test-20210321.c 2021-03-21 19:41:24.469083859 +0900 @@ -0,0 +1,179 @@ +/* + * uio-cmt-test-20210321.c - UIO CMT example test code, 20210321 Magnus Damm + * + * A small linux program that programs the CMT timer and waits for IRQs + * + * Compile for Linux using: + * $ cross-gcc -o uio-cmt-test uio-cmt-test.c + * + * Designed to work with the Linux UIO kernel driver uio_pdrv_genirq.c + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/mman.h> +#include <fcntl.h> + +static int fgets_with_openclose(char *fname, char *buf, size_t maxlen) { + FILE *fp; + + if ((fp = fopen(fname, "r")) != NULL) { + fgets(buf, maxlen, fp); + fclose(fp); + return strlen(buf); + } else { + return -1; + } +} + +struct uio_device { + char *name; + char *path; + int fd; +}; + +#define MAXUIOIDS 100 +#define MAXNAMELEN 256 + +static int locate_uio_device(char *name, struct uio_device *udp) +{ + char fname[MAXNAMELEN], buf[MAXNAMELEN]; + int uio_id, i; + + for (uio_id = 0; uio_id < MAXUIOIDS; uio_id++) { + sprintf(fname, "/sys/class/uio/uio%d/name", uio_id); + if (fgets_with_openclose(fname, buf, MAXNAMELEN) < 0) + continue; + if (strncmp(name, buf, strlen(name)) == 0) + break; + } + + if (uio_id >= MAXUIOIDS) + return -1; + + udp->name = strdup(buf); + udp->path = strdup(fname); + udp->path[strlen(udp->path) - 4] = '\0'; + + sprintf(buf, "/dev/uio%d", uio_id); + udp->fd = open(buf, O_RDWR|O_SYNC /*| O_NONBLOCK*/); + + if (udp->fd < 0) { + perror("open"); + return -1; + } + + return 0; +} + +/* offsets apply to sh73a0 */ +#define CMT_CMSTR 0x000 /* start/stop register, some bits reserved as 1 */ +#define CMT_CMSTR_CH0 (1 << 0) /* set bit to one to start channel */ +#define CMT_CMCSR 0x10 /* 0x124 enables interrupts and selects CLK/8 */ +#define CMT_CMCSR_CMF (1 << 15) /* clear bit to ack compare match event */ +#define CMT_CMCNT 0x14 /* counting up, set to 0 */ +#define CMT_CMCOR 0x18 /* match value, set to ~0 */ + +struct uio_map { + unsigned long address; + unsigned long size; + void *iomem; +}; + +static int setup_uio_map(struct uio_device *udp, int nr, struct uio_map *ump) +{ + char fname[MAXNAMELEN], buf[MAXNAMELEN]; + + sprintf(fname, "%s/maps/map%d/addr", udp->path, nr); + if (fgets_with_openclose(fname, buf, MAXNAMELEN) <= 0) + return -1; + + ump->address = strtoul(buf, NULL, 0); + + sprintf(fname, "%s/maps/map%d/size", udp->path, nr); + if (fgets_with_openclose(fname, buf, MAXNAMELEN) <= 0) + return -1; + + ump->size = strtoul(buf, NULL, 0); + + ump->iomem = mmap(0, ump->size, + PROT_READ|PROT_WRITE, MAP_SHARED, + udp->fd, nr * getpagesize()); + + if (ump->iomem == MAP_FAILED) + return -1; + + return 0; +} + +struct uio_device uio_dev; +struct uio_map uio_mmio; + +int main(int argc, char *argv[]) +{ + int k; + int ret; + + ret = locate_uio_device("timer", &uio_dev); + if (ret < 0) + return ret; + + printf("found matching UIO device at %s\n", uio_dev.path); + + ret = setup_uio_map(&uio_dev, 0, &uio_mmio); + if (ret < 0) + return ret; + + { + unsigned long *cmstr = (uio_mmio.iomem + CMT_CMSTR); + unsigned short *cmcsr = (uio_mmio.iomem + CMT_CMCSR); + unsigned long *cmcor = (uio_mmio.iomem + CMT_CMCOR); + + /* Stop timer channel */ + *cmstr &= ~CMT_CMSTR_CH0; + + /* Initialize CMCSR */ + *cmcsr = 0x124; + + /* Initialize CMCOR */ + *cmcor = (32768 / 8) * 2; /* interrupt after about 2s */ + + /* Enable interrupt in UIO driver */ + { + unsigned long enable = 1; + write(uio_dev.fd, &enable, sizeof(u_long)); + } + + /* Start timer channel */ + *cmstr |= CMT_CMSTR_CH0; + + /* test by processing 3 interrupts */ + for (k = 0; k < 3; k++) { + /* Wait for interrupt */ + { + unsigned long n_pending; + read(uio_dev.fd, &n_pending, sizeof(u_long)); + } + + printf("IRQ nr %d\n", k); + + /* ack match in CMCSR */ + *cmcsr &= ~CMT_CMCSR_CMF; + + /* Enable interrupt in UIO driver */ + { + unsigned long enable = 1; + write(uio_dev.fd, &enable, sizeof(u_long)); + } + } + + /* Stop timer channel */ + *cmstr &= ~CMT_CMSTR_CH0; + } + + return 0; +}