linux-renesas-soc.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/2] r8a77961 CMT test setup using UIO
@ 2020-09-20  0:33 Magnus Damm
  2020-09-20  0:33 ` [PATCH 1/2] r8a77961 CMT0 device exposed via UIO Magnus Damm
                   ` (2 more replies)
  0 siblings, 3 replies; 7+ messages in thread
From: Magnus Damm @ 2020-09-20  0:33 UTC (permalink / raw)
  To: linux-renesas-soc; +Cc: Magnus Damm

r8a77961 CMT test setup using UIO

[PATCH 1/2] r8a77961 CMT0 device exposed via UIO
[PATCH 2/2] UIO CMT test program

These two patches contain kernel and user space modificatons to allow some
basic testing from user space of the CMT0 device included on the r8a77961 SoC.

For the kernel, simply apply the kernel patch (including a few debug printouts)
and make sure to extend your kernel config with CONFIG_UIO_PDRV_GENIRQ=y.

The following log shows how to execute the test program on the target:

# ./uio-cmt-test
found matching UIO device at /sys/class/uio/uio0/
CLKE
CMCSR
CMCOR
[   12.284451] irqhandler 20
waiting 10s
UIO write
CLKE
UIO read
got CMT IRQ
CLKE
#

Please note that only a few registers of a single channel of the CMT0 device
has been exercised. The kernel debug printout and /proc/interrupts may be
used to verify that at least one interrupt has been delivered.

One limitation with the current setup is that the UIO kernel driver only
supports a single interrupt however the CMT devices come with one interrupt
per channel on R-Car Gen3. Currently the code only uses a single IRQ.

If it turns out that the current test coverage should be extended then perhaps
it would be wise to also extend the UIO kernel driver with support for multiple
interrupts as well. To maintain the same user space interface the UIO driver
can simply have a list of interrupts associated with each device and then
enable/disable all of them on each IRQ. Not fast but good enough for testing.

The code applies cleanly on renesas-drivers-2020-09-15-v5.9-rc5 from the
renesas-drivers repo on kernel.org. Testing has been performed remotely on
r8a77961 Salvator-XS but should be easy enough to move to more or less any
other SoC with any kind of memory mapped device. Devices that do not support
bus mastering (like the CMT) can be safely used without any IOMMU.

Not-Yet-Signed-off-by: Magnus Damm <damm+renesas@opensource.se>
---

 arch/arm64/boot/dts/renesas/r8a77961.dtsi |   10 +
 drivers/uio/uio.c                         |    3 
 drivers/uio/uio_pdrv_genirq.c             |   10 +
 uio-cmt-test-20200919.c                   |  190 +++++++++++++++++++++++++++++
 4 files changed, 210 insertions(+), 3 deletions(-)

^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH 1/2] r8a77961 CMT0 device exposed via UIO
  2020-09-20  0:33 [PATCH 0/2] r8a77961 CMT test setup using UIO Magnus Damm
@ 2020-09-20  0:33 ` Magnus Damm
  2020-09-23 13:48   ` Geert Uytterhoeven
  2020-09-20  0:33 ` [PATCH 2/2] UIO CMT test program Magnus Damm
  2020-09-23 13:52 ` [PATCH 0/2] r8a77961 CMT test setup using UIO Geert Uytterhoeven
  2 siblings, 1 reply; 7+ messages in thread
From: Magnus Damm @ 2020-09-20  0:33 UTC (permalink / raw)
  To: linux-renesas-soc; +Cc: Magnus Damm

From: Magnus Damm <damm+renesas@opensource.se>

Device Tree modifications to expose CMT0 via UIO, code to add UIO driver
debug code and also adjust the compat string matching in MODULE_DEVICE_TABLE()

Signed-off-by: Magnus Damm <damm+renesas@opensource.se>
---

 arch/arm64/boot/dts/renesas/r8a77961.dtsi |   10 ++++++++++
 drivers/uio/uio.c                         |    3 ++-
 drivers/uio/uio_pdrv_genirq.c             |   10 ++++++++--
 3 files changed, 20 insertions(+), 3 deletions(-)

--- 0001/arch/arm64/boot/dts/renesas/r8a77961.dtsi
+++ work/arch/arm64/boot/dts/renesas/r8a77961.dtsi	2020-09-20 06:37:14.578864063 +0900
@@ -453,6 +453,16 @@
 			reg = <0 0xe6060000 0 0x50c>;
 		};
 
+		cmt0: timer@e60f0000 {
+			compatible = "uio_pdrv_genirq";
+			reg = <0 0xe60f0000 0 0x1004>;
+			interrupts = <GIC_SPI 142 IRQ_TYPE_LEVEL_HIGH>;
+			clocks = <&cpg CPG_MOD 303>;
+			clock-names = "fck";
+			power-domains = <&sysc R8A77961_PD_ALWAYS_ON>;
+			resets = <&cpg 303>;
+		};
+
 		cpg: clock-controller@e6150000 {
 			compatible = "renesas,r8a77961-cpg-mssr";
 			reg = <0 0xe6150000 0 0x1000>;
--- 0001/drivers/uio/uio.c
+++ work/drivers/uio/uio.c	2020-09-20 07:58:51.295172430 +0900
@@ -11,7 +11,7 @@
  *
  * Base Functions
  */
-
+#define DEBUG
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/poll.h>
@@ -975,6 +975,7 @@ int __uio_register_device(struct module
 		 * FDs at the time of unregister and therefore may not be
 		 * freed until they are released.
 		 */
+		pr_debug("uio request_irq %lu\n", info->irq);
 		ret = request_irq(info->irq, uio_interrupt,
 				  info->irq_flags, info->name, idev);
 		if (ret) {
--- 0001/drivers/uio/uio_pdrv_genirq.c
+++ work/drivers/uio/uio_pdrv_genirq.c	2020-09-20 07:58:07.417169667 +0900
@@ -10,7 +10,7 @@
  * Copyright (C) 2008 by Digi International Inc.
  * All rights reserved.
  */
-
+#define DEBUG
 #include <linux/platform_device.h>
 #include <linux/uio_driver.h>
 #include <linux/spinlock.h>
@@ -66,6 +66,8 @@ static irqreturn_t uio_pdrv_genirq_handl
 	 * remember the state so we can allow user space to enable it later.
 	 */
 
+	pr_debug("irqhandler %d\n", irq);
+
 	spin_lock(&priv->lock);
 	if (!__test_and_set_bit(UIO_IRQ_DISABLED, &priv->flags))
 		disable_irq_nosync(irq);
@@ -87,6 +89,8 @@ static int uio_pdrv_genirq_irqcontrol(st
 	 * with irq handler on SMP systems.
 	 */
 
+	pr_debug("irqcontrol %d\n", irq_on);
+	
 	spin_lock_irqsave(&priv->lock, flags);
 	if (irq_on) {
 		if (__test_and_clear_bit(UIO_IRQ_DISABLED, &priv->flags))
@@ -172,6 +176,8 @@ static int uio_pdrv_genirq_probe(struct
 		}
 	}
 
+	pr_debug("uio irq %lu\n", uioinfo->irq);
+	
 	if (uioinfo->irq) {
 		struct irq_data *irq_data = irq_get_irq_data(uioinfo->irq);
 
@@ -276,7 +282,7 @@ static const struct dev_pm_ops uio_pdrv_
 
 #ifdef CONFIG_OF
 static struct of_device_id uio_of_genirq_match[] = {
-	{ /* This is filled with module_parm */ },
+	{ .compatible = "uio_pdrv_genirq", },
 	{ /* Sentinel */ },
 };
 MODULE_DEVICE_TABLE(of, uio_of_genirq_match);

^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH 2/2] UIO CMT test program
  2020-09-20  0:33 [PATCH 0/2] r8a77961 CMT test setup using UIO Magnus Damm
  2020-09-20  0:33 ` [PATCH 1/2] r8a77961 CMT0 device exposed via UIO Magnus Damm
@ 2020-09-20  0:33 ` Magnus Damm
  2020-09-23 13:52 ` [PATCH 0/2] r8a77961 CMT test setup using UIO Geert Uytterhoeven
  2 siblings, 0 replies; 7+ messages in thread
From: Magnus Damm @ 2020-09-20  0:33 UTC (permalink / raw)
  To: linux-renesas-soc; +Cc: Magnus Damm

--- /dev/null	2010-09-19 00:27:13.659405289 +0900
+++ uio-cmt-test-20200919.c	2020-09-19 08:55:12.571385363 +0900
@@ -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;
+}

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH 1/2] r8a77961 CMT0 device exposed via UIO
  2020-09-20  0:33 ` [PATCH 1/2] r8a77961 CMT0 device exposed via UIO Magnus Damm
@ 2020-09-23 13:48   ` Geert Uytterhoeven
  0 siblings, 0 replies; 7+ messages in thread
From: Geert Uytterhoeven @ 2020-09-23 13:48 UTC (permalink / raw)
  To: Magnus Damm; +Cc: Linux-Renesas

Hi Magnus,

On Sun, Sep 20, 2020 at 3:04 AM Magnus Damm <damm@opensource.se> wrote:
> From: Magnus Damm <damm+renesas@opensource.se>
>
> Device Tree modifications to expose CMT0 via UIO, code to add UIO driver
> debug code and also adjust the compat string matching in MODULE_DEVICE_TABLE()
>
> Signed-off-by: Magnus Damm <damm+renesas@opensource.se>

Thanks for your patch!

>  arch/arm64/boot/dts/renesas/r8a77961.dtsi |   10 ++++++++++
>  drivers/uio/uio.c                         |    3 ++-
>  drivers/uio/uio_pdrv_genirq.c             |   10 ++++++++--
>  3 files changed, 20 insertions(+), 3 deletions(-)

Please don't mix DTS and driver changes in a single patch.

> --- 0001/arch/arm64/boot/dts/renesas/r8a77961.dtsi
> +++ work/arch/arm64/boot/dts/renesas/r8a77961.dtsi      2020-09-20 06:37:14.578864063 +0900
> @@ -453,6 +453,16 @@
>                         reg = <0 0xe6060000 0 0x50c>;
>                 };
>
> +               cmt0: timer@e60f0000 {
> +                       compatible = "uio_pdrv_genirq";

Please use an appropriate compatible value, describing the device.
DT describes hardware, not software policy.

> +                       reg = <0 0xe60f0000 0 0x1004>;
> +                       interrupts = <GIC_SPI 142 IRQ_TYPE_LEVEL_HIGH>;
> +                       clocks = <&cpg CPG_MOD 303>;
> +                       clock-names = "fck";
> +                       power-domains = <&sysc R8A77961_PD_ALWAYS_ON>;
> +                       resets = <&cpg 303>;

status = "disabled"; ?

> +               };

While at it, please add the other CMT instances, too.

> +
>                 cpg: clock-controller@e6150000 {
>                         compatible = "renesas,r8a77961-cpg-mssr";
>                         reg = <0 0xe6150000 0 0x1000>;
> --- 0001/drivers/uio/uio.c
> +++ work/drivers/uio/uio.c      2020-09-20 07:58:51.295172430 +0900
> @@ -11,7 +11,7 @@
>   *
>   * Base Functions
>   */
> -
> +#define DEBUG

I don't think this is appropriate for upstream inclusion.

> --- 0001/drivers/uio/uio_pdrv_genirq.c
> +++ work/drivers/uio/uio_pdrv_genirq.c  2020-09-20 07:58:07.417169667 +0900
> @@ -10,7 +10,7 @@
>   * Copyright (C) 2008 by Digi International Inc.
>   * All rights reserved.
>   */
> -
> +#define DEBUG

Likewise.

>  #include <linux/platform_device.h>
>  #include <linux/uio_driver.h>
>  #include <linux/spinlock.h>

> @@ -276,7 +282,7 @@ static const struct dev_pm_ops uio_pdrv_
>
>  #ifdef CONFIG_OF
>  static struct of_device_id uio_of_genirq_match[] = {
> -       { /* This is filled with module_parm */ },
> +       { .compatible = "uio_pdrv_genirq", },

This does not look like a proper compatible value.

>         { /* Sentinel */ },
>  };
>  MODULE_DEVICE_TABLE(of, uio_of_genirq_match);

Note that you can bind this driver to your device without modifying this
driver, by using one of:
  1. modprobe uio_pdrv_genirq of_id=my-compatible-value
  2. echo uio_pdrv_genirq >
/sys/bus/platform/devices/e60f0000.timer/driver_override
     echo e60f0000.timer > sys/bus/platform/drivers/uio_pdrv_genirq/bind

Gr{oetje,eeting}s,

                        Geert

-- 
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
                                -- Linus Torvalds

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH 0/2] r8a77961 CMT test setup using UIO
  2020-09-20  0:33 [PATCH 0/2] r8a77961 CMT test setup using UIO Magnus Damm
  2020-09-20  0:33 ` [PATCH 1/2] r8a77961 CMT0 device exposed via UIO Magnus Damm
  2020-09-20  0:33 ` [PATCH 2/2] UIO CMT test program Magnus Damm
@ 2020-09-23 13:52 ` Geert Uytterhoeven
  2 siblings, 0 replies; 7+ messages in thread
From: Geert Uytterhoeven @ 2020-09-23 13:52 UTC (permalink / raw)
  To: Magnus Damm; +Cc: Linux-Renesas

Hi Magnus,

On Sun, Sep 20, 2020 at 3:04 AM Magnus Damm <damm@opensource.se> wrote:
> r8a77961 CMT test setup using UIO
>
> [PATCH 1/2] r8a77961 CMT0 device exposed via UIO
> [PATCH 2/2] UIO CMT test program
>
> These two patches contain kernel and user space modificatons to allow some
> basic testing from user space of the CMT0 device included on the r8a77961 SoC.
>
> For the kernel, simply apply the kernel patch (including a few debug printouts)
> and make sure to extend your kernel config with CONFIG_UIO_PDRV_GENIRQ=y.
>
> The following log shows how to execute the test program on the target:
>
> # ./uio-cmt-test
> found matching UIO device at /sys/class/uio/uio0/
> CLKE
> CMCSR
> CMCOR
> [   12.284451] irqhandler 20
> waiting 10s
> UIO write
> CLKE
> UIO read
> got CMT IRQ
> CLKE
> #

I guess you could test this device using the sh_cmt driver, and a generic
userspace timer test program (from tools/testing/selftests/?), too?

Gr{oetje,eeting}s,

                        Geert

-- 
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
                                -- Linus Torvalds

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH 2/2] UIO CMT test program
  2021-03-21 11:38 ` [PATCH 2/2] UIO CMT test program Magnus Damm
@ 2021-03-21 14:13   ` Geert Uytterhoeven
  0 siblings, 0 replies; 7+ messages in thread
From: Geert Uytterhoeven @ 2021-03-21 14:13 UTC (permalink / raw)
  To: Magnus Damm; +Cc: Linux-Renesas

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

-- 
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
                                -- Linus Torvalds

^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH 2/2] UIO CMT test program
  2021-03-21 11:38 [PATCH 0/2] sh73a0 CMT1 " Magnus Damm
@ 2021-03-21 11:38 ` Magnus Damm
  2021-03-21 14:13   ` Geert Uytterhoeven
  0 siblings, 1 reply; 7+ messages in thread
From: Magnus Damm @ 2021-03-21 11:38 UTC (permalink / raw)
  To: linux-renesas-soc; +Cc: Magnus Damm, geert+renesas

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

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2021-03-21 14:15 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-09-20  0:33 [PATCH 0/2] r8a77961 CMT test setup using UIO Magnus Damm
2020-09-20  0:33 ` [PATCH 1/2] r8a77961 CMT0 device exposed via UIO Magnus Damm
2020-09-23 13:48   ` Geert Uytterhoeven
2020-09-20  0:33 ` [PATCH 2/2] UIO CMT test program Magnus Damm
2020-09-23 13:52 ` [PATCH 0/2] r8a77961 CMT test setup using UIO Geert Uytterhoeven
2021-03-21 11:38 [PATCH 0/2] sh73a0 CMT1 " Magnus Damm
2021-03-21 11:38 ` [PATCH 2/2] UIO CMT test program Magnus Damm
2021-03-21 14:13   ` Geert Uytterhoeven

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).