@@ -0,0 +1,190 @@
+/*
+ * uio-cmt-test-20200919.c - UIO CMT example test code, 20200919 Magnus Damm
+ *
+ * A small linux program that programs the CMT timer and waits for IRQ
+ *
+ * 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;
+}
+
+#define CMT_CMCLKE 0x1000 /* start/stop register, some bits reserved as 1 */
+#define CMT_CMCLKE_CH0 0x20 /* set bit to one to start channel */
+#define CMT_CMCSR 0x510 /* 0x124 enables interrupts and selects CLK/8 */
+#define CMT_CMCNT 0x514 /* counting up, set to 0 */
+#define CMT_CMCOR 0x518 /* 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 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;
+
+ printf("CLKE\n");
+ /* Stop timer channel */
+ {
+ unsigned long *p = (uio_mmio.iomem + CMT_CMCLKE);
+
+ *p = *p & ~CMT_CMCLKE_CH0;
+ }
+
+ printf("CMCSR\n");
+ /* CMCSR */
+ {
+ unsigned long *p = (uio_mmio.iomem + CMT_CMCSR);
+
+ *p = 0x124;
+ }
+
+ printf("CMCOR\n");
+ /* CMCOR */
+ {
+ unsigned long *p = (uio_mmio.iomem + CMT_CMCOR);
+
+ *p = (32768 / 8) * 2; /* interrupt after about 2s */
+ }
+
+ printf("waiting 10s");
+ sleep(10);
+
+ printf("UIO write\n");
+ /* Enable interrupt in UIO driver */
+ {
+ unsigned long enable = 1;
+
+ write(uio_dev.fd, &enable, sizeof(u_long));
+ }
+
+ printf("CLKE\n");
+ /* Start timer channel */
+ {
+ unsigned long *p = (uio_mmio.iomem + CMT_CMCLKE);
+
+ *p = *p | CMT_CMCLKE_CH0;
+ }
+
+ printf("UIO read\n");
+ /* Wait for interrupt */
+ {
+ unsigned long n_pending;
+
+ read(uio_dev.fd, &n_pending, sizeof(u_long));
+
+ printf("got CMT IRQ\n");
+ }
+
+ printf("CLKE\n");
+ /* Stop timer channel */
+ {
+ unsigned long *p = (uio_mmio.iomem + CMT_CMCLKE);
+
+ *p = *p & ~CMT_CMCLKE_CH0;
+ }
+
+ return 0;
+}