Most devices integrated on SoCs have a well-defined block of
registers.  But sometimes a driver for such a device needs to touch a
register outside that block.  In those cases the device tree contains
a reference to the device that "owns" the registers.  The diff below
implements a way to access those registers.  The device that owns the
registers needs to register a "regmap".  Other device can then lookup
that regmap through a phandle and read or write to registers.  The
size of the region is store when the regmap is registered, and checked
upon access.  Only 32-bit access is implemented for now.

The diff also add a syscon(4) driver that uses the regmap
functionality to implement reboot and poweroff functionality.

ok?


Index: dev/ofw/ofw_misc.c
===================================================================
RCS file: dev/ofw/ofw_misc.c
diff -N dev/ofw/ofw_misc.c
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ dev/ofw/ofw_misc.c  8 Mar 2017 21:58:30 -0000
@@ -0,0 +1,81 @@
+/*     $OpenBSD$       */
+/*
+ * Copyright (c) 2017 Mark Kettenis
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+
+#include <machine/bus.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_pinctrl.h>
+
+struct regmap {
+       uint32_t                rm_phandle;
+       bus_space_tag_t         rm_tag;
+       bus_space_handle_t      rm_handle;
+       bus_size_t              rm_size;
+       
+       LIST_ENTRY(regmap)      rm_list;
+};
+
+LIST_HEAD(, regmap) regmaps = LIST_HEAD_INITIALIZER(regmap);
+
+void
+regmap_register(int node, bus_space_tag_t tag, bus_space_handle_t handle,
+    bus_size_t size)
+{
+       struct regmap *rm;
+       uint32_t phandle;
+
+       phandle = OF_getpropint(node, "phandle", 0);
+       if (phandle) {
+               rm = malloc(sizeof(struct regmap), M_DEVBUF, M_WAITOK);
+               rm->rm_phandle = phandle;
+               rm->rm_tag = tag;
+               rm->rm_handle = handle;
+               rm->rm_size = size;
+               LIST_INSERT_HEAD(&regmaps, rm, rm_list);
+       }
+}
+
+struct regmap *
+regmap_byphandle(uint32_t phandle)
+{
+       struct regmap *rm;
+
+       LIST_FOREACH(rm, &regmaps, rm_list) {
+               if (rm->rm_phandle == phandle)
+                       return rm;
+       }
+
+       return NULL;
+}
+
+void
+regmap_write_4(struct regmap *rm, bus_size_t offset, uint32_t value)
+{
+       KASSERT(offset <= rm->rm_size - sizeof(uint32_t));
+       bus_space_write_4(rm->rm_tag, rm->rm_handle, offset, value);
+}
+
+uint32_t
+regmap_read_4(struct regmap *rm, bus_size_t offset)
+{
+       KASSERT(offset <= rm->rm_size - sizeof(uint32_t));
+       return bus_space_read_4(rm->rm_tag, rm->rm_handle, offset);
+}
Index: dev/ofw/ofw_misc.h
===================================================================
RCS file: dev/ofw/ofw_misc.h
diff -N dev/ofw/ofw_misc.h
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ dev/ofw/ofw_misc.h  8 Mar 2017 21:58:30 -0000
@@ -0,0 +1,29 @@
+/*     $OpenBSD$       */
+/*
+ * Copyright (c) 2017 Mark Kettenis
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _DEV_OFW_MISC_H_
+#define _DEV_OFW_MISC_H_
+
+void   regmap_register(int, bus_space_tag_t, bus_space_handle_t, bus_size_t);
+
+struct regmap;
+struct regmap *regmap_byphandle(uint32_t);
+
+uint32_t regmap_read_4(struct regmap *, bus_size_t);
+void   regmap_write_4(struct regmap *, bus_size_t, uint32_t);
+
+#endif /* _DEV_OFW_MISC_H_ */
Index: dev/fdt/files.fdt
===================================================================
RCS file: /cvs/src/sys/dev/fdt/files.fdt,v
retrieving revision 1.6
diff -u -p -r1.6 files.fdt
--- dev/fdt/files.fdt   22 Feb 2017 23:01:15 -0000      1.6
+++ dev/fdt/files.fdt   8 Mar 2017 21:58:30 -0000
@@ -46,3 +46,7 @@ file  dev/fdt/virtio_mmio.c           virtio_mmio
 # Advanced Host Controller Interface for Serial ATA
 attach ahci at fdt with ahci_fdt
 file   dev/fdt/ahci_fdt.c              ahci_fdt
+
+device syscon
+attach syscon at fdt
+file   dev/fdt/syscon.c                syscon
Index: dev/fdt/syscon.c
===================================================================
RCS file: dev/fdt/syscon.c
diff -N dev/fdt/syscon.c
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ dev/fdt/syscon.c    8 Mar 2017 21:58:30 -0000
@@ -0,0 +1,119 @@
+/*     $OpenBSD$       */
+/*
+ * Copyright (c) 2016 Mark Kettenis
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/device.h>
+
+#include <machine/bus.h>
+#include <machine/fdt.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_misc.h>
+#include <dev/ofw/fdt.h>
+
+extern void (*cpuresetfn)(void);
+extern void (*powerdownfn)(void);
+
+struct syscon_softc {
+       struct device   sc_dev;
+       uint32_t        sc_regmap;
+       bus_size_t      sc_offset;
+       uint32_t        sc_mask;
+};
+
+struct syscon_softc *syscon_reboot_sc;
+struct syscon_softc *syscon_poweroff_sc;
+
+int    syscon_match(struct device *, void *, void *);
+void   syscon_attach(struct device *, struct device *, void *);
+
+struct cfattach syscon_ca = {
+       sizeof(struct syscon_softc), syscon_match, syscon_attach
+};
+
+struct cfdriver syscon_cd = {
+       NULL, "syscon", DV_DULL
+};
+
+void   syscon_reset(void);
+void   syscon_powerdown(void);
+
+int
+syscon_match(struct device *parent, void *match, void *aux)
+{
+       struct fdt_attach_args *faa = aux;
+
+       return (OF_is_compatible(faa->fa_node, "syscon-reboot") ||
+           OF_is_compatible(faa->fa_node, "syscon-poweroff"));
+}
+
+void
+syscon_attach(struct device *parent, struct device *self, void *aux)
+{
+       struct syscon_softc *sc = (struct syscon_softc *)self;
+       struct fdt_attach_args *faa = aux;
+
+       printf("\n");
+
+       sc->sc_regmap = OF_getpropint(faa->fa_node, "regmap", 0);
+       if (sc->sc_regmap == 0)
+               return;
+
+       if (OF_getproplen(faa->fa_node, "offset") != sizeof(uint32_t) ||
+           OF_getproplen(faa->fa_node, "mask") != sizeof(uint32_t))
+               return;
+
+       sc->sc_offset = OF_getpropint(faa->fa_node, "offset", 0);
+       sc->sc_mask = OF_getpropint(faa->fa_node, "mask", 0);
+
+       if (OF_is_compatible(faa->fa_node, "syscon-reboot")) {
+               syscon_reboot_sc = sc;
+               cpuresetfn = syscon_reset;
+       } else {
+               syscon_poweroff_sc = sc;
+               powerdownfn = syscon_powerdown;
+       }
+}
+
+void
+syscon_reset(void)
+{
+       struct syscon_softc *sc = syscon_reboot_sc;
+       struct regmap *rm;
+
+       rm = regmap_byphandle(sc->sc_regmap);
+       if (rm == NULL)
+               return;
+
+       regmap_write_4(rm, sc->sc_offset, sc->sc_mask);
+       delay(1000000);
+}
+
+void
+syscon_powerdown(void)
+{
+       struct syscon_softc *sc = syscon_poweroff_sc;
+       struct regmap *rm;
+
+       rm = regmap_byphandle(sc->sc_regmap);
+       if (rm == NULL)
+               return;
+
+       regmap_write_4(rm, sc->sc_offset, sc->sc_mask);
+       delay(1000000);
+}
Index: arch/armv7/conf/GENERIC
===================================================================
RCS file: /cvs/src/sys/arch/armv7/conf/GENERIC,v
retrieving revision 1.76
diff -u -p -r1.76 GENERIC
--- arch/armv7/conf/GENERIC     6 Mar 2017 07:41:58 -0000       1.76
+++ arch/armv7/conf/GENERIC     8 Mar 2017 21:58:30 -0000
@@ -107,6 +107,7 @@ plrtc*              at fdt?
 virtio*                at fdt?
 
 psci*          at fdt?
+syscon*                at fdt?
 
 simplefb*      at fdt?
 wsdisplay*     at simplefb?
Index: arch/armv7/conf/files.armv7
===================================================================
RCS file: /cvs/src/sys/arch/armv7/conf/files.armv7,v
retrieving revision 1.31
diff -u -p -r1.31 files.armv7
--- arch/armv7/conf/files.armv7 25 Jan 2017 10:14:40 -0000      1.31
+++ arch/armv7/conf/files.armv7 8 Mar 2017 21:58:30 -0000
@@ -27,6 +27,7 @@ file  arch/arm/arm/disksubr.c                 disk
 # FDT support
 file   dev/ofw/ofw_clock.c
 file   dev/ofw/ofw_gpio.c
+file   dev/ofw/ofw_misc.c
 file   dev/ofw/ofw_pinctrl.c
 file   dev/ofw/ofw_regulator.c
 
Index: arch/armv7/exynos/exdog.c
===================================================================
RCS file: /cvs/src/sys/arch/armv7/exynos/exdog.c,v
retrieving revision 1.5
diff -u -p -r1.5 exdog.c
--- arch/armv7/exynos/exdog.c   4 Mar 2017 18:17:24 -0000       1.5
+++ arch/armv7/exynos/exdog.c   8 Mar 2017 21:58:30 -0000
@@ -94,7 +94,8 @@ exdog_attach(struct device *parent, stru
        printf("\n");
 
        exdog_sc = sc;
-       cpuresetfn = exdog_reset;
+       if (cpuresetfn == NULL)
+               cpuresetfn = exdog_reset;
 }
 
 void
Index: arch/armv7/exynos/expower.c
===================================================================
RCS file: /cvs/src/sys/arch/armv7/exynos/expower.c,v
retrieving revision 1.5
diff -u -p -r1.5 expower.c
--- arch/armv7/exynos/expower.c 4 Mar 2017 18:17:24 -0000       1.5
+++ arch/armv7/exynos/expower.c 8 Mar 2017 21:58:30 -0000
@@ -24,6 +24,7 @@
 #include <machine/fdt.h>
 
 #include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_misc.h>
 #include <dev/ofw/fdt.h>
 
 #include <armv7/exynos/expowervar.h>
@@ -85,6 +86,8 @@ expower_attach(struct device *parent, st
 
        printf("\n");
 
+       regmap_register(faa->fa_node, sc->sc_iot, sc->sc_ioh,
+           faa->fa_reg[0].size);
        expower_sc = sc;
 }
 

Reply via email to