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(®maps, rm, rm_list); + } +} + +struct regmap * +regmap_byphandle(uint32_t phandle) +{ + struct regmap *rm; + + LIST_FOREACH(rm, ®maps, 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; }