diff mbox series

[2/2] UIO CMT test program

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

Commit Message

Magnus Damm March 21, 2021, 11:38 a.m. UTC

Comments

Geert Uytterhoeven March 21, 2021, 2:13 p.m. UTC | #1
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
diff mbox series

Patch

--- /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;
+}