Module Name: src Committed By: jmcneill Date: Sun Dec 13 17:30:40 UTC 2015
Added Files: src/sys/dev/fdt: fdt_openfirm.c fdt_openfirm.h fdt_subr.c fdtbus.c fdtvar.h files.fdt fixedregulator.c simplebus.c Log Message: Add a framework for enumerating devices using a Flattened Device Tree (FDT). To generate a diff of this commit: cvs rdiff -u -r0 -r1.1 src/sys/dev/fdt/fdt_openfirm.c \ src/sys/dev/fdt/fdt_openfirm.h src/sys/dev/fdt/fdt_subr.c \ src/sys/dev/fdt/fdtbus.c src/sys/dev/fdt/fdtvar.h \ src/sys/dev/fdt/files.fdt src/sys/dev/fdt/fixedregulator.c \ src/sys/dev/fdt/simplebus.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Added files: Index: src/sys/dev/fdt/fdt_openfirm.c diff -u /dev/null src/sys/dev/fdt/fdt_openfirm.c:1.1 --- /dev/null Sun Dec 13 17:30:40 2015 +++ src/sys/dev/fdt/fdt_openfirm.c Sun Dec 13 17:30:40 2015 @@ -0,0 +1,359 @@ +/* $NetBSD: fdt_openfirm.c,v 1.1 2015/12/13 17:30:40 jmcneill Exp $ */ + +/*- + * Copyright (c) 2015 Jared D. McNeill <jmcne...@invisible.ca> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__KERNEL_RCSID(0, "$NetBSD: fdt_openfirm.c,v 1.1 2015/12/13 17:30:40 jmcneill Exp $"); + +#include <sys/param.h> + +#include <libfdt.h> +#include <dev/ofw/openfirm.h> +#include <dev/fdt/fdt_openfirm.h> + +static const void *fdt_data; + +bool +fdt_openfirm_set_data(const void *data) +{ + KASSERT(fdt_data == NULL); + if (fdt_check_header(data) != 0) { + return false; + } + fdt_data = data; + return true; +} + +const void * +fdt_openfirm_get_data(void) +{ + return fdt_data; +} + +int +fdt_openfirm_get_phandle(int offset) +{ + if (offset < 0) + return 0; + + return offset + fdt_off_dt_struct(fdt_data); +} + +int +fdt_openfirm_get_offset(int phandle) +{ + const int dtoff = fdt_off_dt_struct(fdt_data); + + if (phandle == -1) + phandle = dtoff; + + if (phandle < dtoff) + return -1; + + return phandle - dtoff; +} + +int +OF_peer(int phandle) +{ + int off, depth; + + if (fdt_data == NULL) { + return -1; + } + + if (phandle == 0) { + return fdt_openfirm_get_phandle(0); + } + + off = fdt_openfirm_get_offset(phandle); + if (off < 0) { + return 0; + } + + depth = 1; + for (off = fdt_next_node(fdt_data, off, &depth); + off >= 0 && depth >= 0; + off = fdt_next_node(fdt_data, off, &depth)) { + if (depth == 1) { + return fdt_openfirm_get_phandle(off); + } + } + + return 0; +} + +int +OF_child(int phandle) +{ + int off, depth; + + if (fdt_data == NULL) { + return -1; + } + + off = fdt_openfirm_get_offset(phandle); + if (off < 0) { + return 0; + } + + depth = 0; + for (off = fdt_next_node(fdt_data, off, &depth); + off >= 0 && depth > 0; + off = fdt_next_node(fdt_data, off, &depth)) { + if (depth == 1) { + return fdt_openfirm_get_phandle(off); + } + } + + return 0; +} + +int +OF_parent(int phandle) +{ + int off; + + if (fdt_data == NULL) { + return -1; + } + + off = fdt_openfirm_get_offset(phandle); + if (off < 0) { + return -1; + } + + off = fdt_parent_offset(fdt_data, off); + if (off < 0) { + return -1; + } + + return fdt_openfirm_get_phandle(off); +} + +int +OF_nextprop(int phandle, const char *prop, void *nextprop) +{ + const char *name; + const void *val; + int off, len; + + if (fdt_data == NULL) { + return -1; + } + + off = fdt_openfirm_get_offset(phandle); + if (off < 0) { + return -1; + } + + if (*prop == '\0') { + name = "name"; + } else { + off = fdt_first_property_offset(fdt_data, off); + if (off < 0) { + return 0; + } + if (strcmp(prop, "name") != 0) { + while (off >= 0) { + val = fdt_getprop_by_offset(fdt_data, off, + &name, &len); + if (val == NULL) { + return -1; + } + off = fdt_next_property_offset(fdt_data, off); + if (off < 0) { + return 0; + } + if (strcmp(name, prop) == 0) + break; + } + } + val = fdt_getprop_by_offset(fdt_data, off, &name, &len); + if (val == NULL) { + return -1; + } + } + + strlcpy(nextprop, name, 33); + + return 1; +} + +int +OF_getprop(int phandle, const char *prop, void *buf, int buflen) +{ + const char *name; + const void *val; + int off, len; + + if (fdt_data == NULL) { + return -1; + } + + off = fdt_openfirm_get_offset(phandle); + if (off < 0) { + return -1; + } + + if (strcmp(prop, "name") == 0) { + val = fdt_get_name(fdt_data, off, &len); + if (val) { + const char *p = strchr(val, '@'); + if (p) { + len = (uintptr_t)p - (uintptr_t)val + 1; + } else { + len += 1; + } + } + if (val == NULL || len > buflen) { + return -1; + } + char *s = buf; + memcpy(buf, val, len - 1); + s[len - 1] = '\0'; + } else { + off = fdt_first_property_offset(fdt_data, off); + if (off < 0) { + return -1; + } + while (off >= 0) { + val = fdt_getprop_by_offset(fdt_data, off, &name, &len); + if (val == NULL) { + return -1; + } + if (strcmp(name, prop) == 0) { + break; + } + off = fdt_next_property_offset(fdt_data, off); + if (off < 0) { + return -1; + } + } + if (val == NULL || len > buflen) { + return -1; + } + memcpy(buf, val, len); + } + + return len; +} + +int +OF_getproplen(int phandle, const char *prop) +{ + const char *name; + const void *val; + int off, len; + + if (fdt_data == NULL) { + return -1; + } + + off = fdt_openfirm_get_offset(phandle); + if (off < 0) { + return -1; + } + + if (strcmp(prop, "name") == 0) { + val = fdt_get_name(fdt_data, off, &len); + if (val) { + const char *p = strchr(val, '@'); + if (p) { + len = (uintptr_t)p - (uintptr_t)val + 1; + } else { + len += 1; + } + } + } else { + off = fdt_first_property_offset(fdt_data, off); + if (off < 0) { + return -1; + } + while (off >= 0) { + val = fdt_getprop_by_offset(fdt_data, off, &name, &len); + if (val == NULL) { + return -1; + } + if (strcmp(name, prop) == 0) { + break; + } + off = fdt_next_property_offset(fdt_data, off); + if (off < 0) { + return -1; + } + } + } + if (val == NULL) { + return -1; + } + + return len; +} + +int +OF_setprop(int phandle, const char *prop, const void *buf, int buflen) +{ + return -1; +} + +int +OF_finddevice(const char *name) +{ + int off; + + if (fdt_data == NULL) { + return -1; + } + + off = fdt_path_offset(fdt_data, name); + if (off < 0) { + return -1; + } + + return fdt_openfirm_get_phandle(off); +} + +int +OF_package_to_path(int phandle, char *buf, int buflen) +{ + int off; + + if (fdt_data == NULL) { + return -1; + } + + off = fdt_openfirm_get_offset(phandle); + if (off < 0) { + return -1; + } + + if (fdt_get_path(fdt_data, off, buf, buflen) != 0) + return -1; + + return strlen(buf); +} Index: src/sys/dev/fdt/fdt_openfirm.h diff -u /dev/null src/sys/dev/fdt/fdt_openfirm.h:1.1 --- /dev/null Sun Dec 13 17:30:40 2015 +++ src/sys/dev/fdt/fdt_openfirm.h Sun Dec 13 17:30:40 2015 @@ -0,0 +1,37 @@ +/* $NetBSD: fdt_openfirm.h,v 1.1 2015/12/13 17:30:40 jmcneill Exp $ */ + +/*- + * Copyright (c) 2015 Jared D. McNeill <jmcne...@invisible.ca> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _DEV_FDT_FDT_OPENFIRM_H +#define _DEV_FDT_FDT_OPENFIRM_H + +bool fdt_openfirm_set_data(const void *); +const void * fdt_openfirm_get_data(void); +int fdt_openfirm_get_offset(int); +int fdt_openfirm_get_phandle(int); + +#endif /* !_DEV_FDT_FDT_OPENFIRM_H */ Index: src/sys/dev/fdt/fdt_subr.c diff -u /dev/null src/sys/dev/fdt/fdt_subr.c:1.1 --- /dev/null Sun Dec 13 17:30:40 2015 +++ src/sys/dev/fdt/fdt_subr.c Sun Dec 13 17:30:40 2015 @@ -0,0 +1,547 @@ +/* $NetBSD: fdt_subr.c,v 1.1 2015/12/13 17:30:40 jmcneill Exp $ */ + +/*- + * Copyright (c) 2015 Jared D. McNeill <jmcne...@invisible.ca> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__KERNEL_RCSID(0, "$NetBSD: fdt_subr.c,v 1.1 2015/12/13 17:30:40 jmcneill Exp $"); + +#include <sys/param.h> +#include <sys/bus.h> +#include <sys/kmem.h> + +#include <libfdt.h> +#include <dev/ofw/openfirm.h> +#include <dev/fdt/fdt_openfirm.h> +#include <dev/fdt/fdtvar.h> + +struct fdtbus_interrupt_controller { + device_t ic_dev; + int ic_phandle; + const struct fdtbus_interrupt_controller_func *ic_funcs; + + struct fdtbus_interrupt_controller *ic_next; +}; + +static struct fdtbus_interrupt_controller *fdtbus_ic = NULL; + +struct fdtbus_i2c_controller { + device_t i2c_dev; + int i2c_phandle; + const struct fdtbus_i2c_controller_func *i2c_funcs; + + struct fdtbus_i2c_controller *i2c_next; +}; + +static struct fdtbus_i2c_controller *fdtbus_i2c = NULL; + +struct fdtbus_gpio_controller { + device_t gc_dev; + int gc_phandle; + const struct fdtbus_gpio_controller_func *gc_funcs; + + struct fdtbus_gpio_controller *gc_next; +}; + +static struct fdtbus_gpio_controller *fdtbus_gc = NULL; + +struct fdtbus_regulator_controller { + device_t rc_dev; + int rc_phandle; + const struct fdtbus_regulator_controller_func *rc_funcs; + + struct fdtbus_regulator_controller *rc_next; +}; + +static struct fdtbus_regulator_controller *fdtbus_rc = NULL; + +static int +fdtbus_get_addr_cells(int phandle) +{ + int val, addr_cells, error; + + const int parent = OF_parent(phandle); + if (parent == -1) + return -1; + + error = OF_getprop(parent, "#address-cells", &val, sizeof(val)); + if (error <= 0) { + addr_cells = 2; + } else { + addr_cells = fdt32_to_cpu(val); + } + + return addr_cells; +} + +static int +fdtbus_get_size_cells(int phandle) +{ + int val, size_cells, error; + + const int parent = OF_parent(phandle); + if (parent == -1) + return -1; + + error = OF_getprop(parent, "#size-cells", &val, sizeof(val)); + if (error <= 0) { + size_cells = 0; + } else { + size_cells = fdt32_to_cpu(val); + } + + return size_cells; +} + +static int +fdtbus_get_interrupt_parent(int phandle) +{ + u_int interrupt_parent; + int len; + + while (phandle >= 0) { + len = OF_getprop(phandle, "interrupt-parent", + &interrupt_parent, sizeof(interrupt_parent)); + if (len == sizeof(interrupt_parent)) { + break; + } + if (phandle == 0) { + return -1; + } + phandle = OF_parent(phandle); + } + if (phandle < 0) { + return -1; + } + + interrupt_parent = fdt32_to_cpu(interrupt_parent); + + const void *data = fdt_openfirm_get_data(); + const int off = fdt_node_offset_by_phandle(data, interrupt_parent); + if (off < 0) { + return -1; + } + + return fdt_openfirm_get_phandle(off); +} + +static struct fdtbus_interrupt_controller * +fdtbus_get_interrupt_controller(int phandle) +{ + struct fdtbus_interrupt_controller *ic; + + const int ic_phandle = fdtbus_get_interrupt_parent(phandle); + if (ic_phandle < 0) { + return NULL; + } + + for (ic = fdtbus_ic; ic; ic = ic->ic_next) { + if (ic->ic_phandle == ic_phandle) { + return ic; + } + } + + return NULL; +} + +int +fdtbus_register_interrupt_controller(device_t dev, int phandle, + const struct fdtbus_interrupt_controller_func *funcs) +{ + struct fdtbus_interrupt_controller *ic; + + ic = kmem_alloc(sizeof(*ic), KM_SLEEP); + ic->ic_dev = dev; + ic->ic_phandle = phandle; + ic->ic_funcs = funcs; + + ic->ic_next = fdtbus_ic; + fdtbus_ic = ic; + + return 0; +} + +void * +fdtbus_intr_establish(int phandle, u_int index, int ipl, int flags, + int (*func)(void *), void *arg) +{ + struct fdtbus_interrupt_controller *ic; + + ic = fdtbus_get_interrupt_controller(phandle); + if (ic == NULL) + return NULL; + + return ic->ic_funcs->establish(ic->ic_dev, phandle, index, ipl, + flags, func, arg); +} + +void +fdtbus_intr_disestablish(int phandle, void *ih) +{ + struct fdtbus_interrupt_controller *ic; + + ic = fdtbus_get_interrupt_controller(phandle); + KASSERT(ic != NULL); + + return ic->ic_funcs->disestablish(ic->ic_dev, ih); +} + +bool +fdtbus_intr_str(int phandle, u_int index, char *buf, size_t buflen) +{ + struct fdtbus_interrupt_controller *ic; + + ic = fdtbus_get_interrupt_controller(phandle); + if (ic == NULL) + return false; + + return ic->ic_funcs->intrstr(ic->ic_dev, phandle, index, buf, buflen); +} + +int +fdtbus_register_gpio_controller(device_t dev, int phandle, + const struct fdtbus_gpio_controller_func *funcs) +{ + struct fdtbus_gpio_controller *gc; + + gc = kmem_alloc(sizeof(*gc), KM_SLEEP); + gc->gc_dev = dev; + gc->gc_phandle = phandle; + gc->gc_funcs = funcs; + + gc->gc_next = fdtbus_gc; + fdtbus_gc = gc; + + return 0; +} + +static struct fdtbus_gpio_controller * +fdtbus_get_gpio_controller(int phandle) +{ + struct fdtbus_gpio_controller *gc; + + for (gc = fdtbus_gc; gc; gc = gc->gc_next) { + if (gc->gc_phandle == phandle) { + return gc; + } + } + + return NULL; +} + +struct fdtbus_gpio_pin * +fdtbus_gpio_acquire(int phandle, const char *prop, int flags) +{ + struct fdtbus_gpio_controller *gc; + struct fdtbus_gpio_pin *gp; + int gpio_phandle, len; + u_int *data; + + gpio_phandle = fdtbus_get_phandle(phandle, prop); + if (gpio_phandle == -1) { + return NULL; + } + + gc = fdtbus_get_gpio_controller(gpio_phandle); + if (gc == NULL) { + return NULL; + } + + len = OF_getproplen(phandle, prop); + if (len < 4) { + return NULL; + } + + data = kmem_alloc(len, KM_SLEEP); + if (OF_getprop(phandle, prop, data, len) != len) { + kmem_free(data, len); + return NULL; + } + + gp = kmem_alloc(sizeof(*gp), KM_SLEEP); + gp->gp_gc = gc; + gp->gp_priv = gc->gc_funcs->acquire(gc->gc_dev, data, len, flags); + if (gp->gp_priv == NULL) { + kmem_free(data, len); + return NULL; + } + + return gp; +} + +void +fdtbus_gpio_release(struct fdtbus_gpio_pin *gp) +{ + struct fdtbus_gpio_controller *gc = gp->gp_gc; + + gc->gc_funcs->release(gc->gc_dev, gp->gp_priv); + kmem_free(gp, sizeof(*gp)); +} + +int +fdtbus_gpio_read(struct fdtbus_gpio_pin *gp) +{ + struct fdtbus_gpio_controller *gc = gp->gp_gc; + + return gc->gc_funcs->read(gc->gc_dev, gp->gp_priv); +} + +void +fdtbus_gpio_write(struct fdtbus_gpio_pin *gp, int val) +{ + struct fdtbus_gpio_controller *gc = gp->gp_gc; + + gc->gc_funcs->write(gc->gc_dev, gp->gp_priv, val); +} + +int +fdtbus_register_regulator_controller(device_t dev, int phandle, + const struct fdtbus_regulator_controller_func *funcs) +{ + struct fdtbus_regulator_controller *rc; + + rc = kmem_alloc(sizeof(*rc), KM_SLEEP); + rc->rc_dev = dev; + rc->rc_phandle = phandle; + rc->rc_funcs = funcs; + + rc->rc_next = fdtbus_rc; + fdtbus_rc = rc; + + return 0; +} + +static struct fdtbus_regulator_controller * +fdtbus_get_regulator_controller(int phandle) +{ + struct fdtbus_regulator_controller *rc; + + for (rc = fdtbus_rc; rc; rc = rc->rc_next) { + if (rc->rc_phandle == phandle) { + return rc; + } + } + + return NULL; +} + +struct fdtbus_regulator * +fdtbus_regulator_acquire(int phandle, const char *prop) +{ + struct fdtbus_regulator_controller *rc; + struct fdtbus_regulator *reg; + int regulator_phandle; + int error; + + regulator_phandle = fdtbus_get_phandle(phandle, prop); + if (regulator_phandle == -1) { + return NULL; + } + + rc = fdtbus_get_regulator_controller(regulator_phandle); + if (rc == NULL) { + return NULL; + } + + error = rc->rc_funcs->acquire(rc->rc_dev); + if (error) { + return NULL; + } + + reg = kmem_alloc(sizeof(*reg), KM_SLEEP); + reg->reg_rc = rc; + + return reg; +} + +void +fdtbus_regulator_release(struct fdtbus_regulator *reg) +{ + struct fdtbus_regulator_controller *rc = reg->reg_rc; + + rc->rc_funcs->release(rc->rc_dev); + + kmem_free(reg, sizeof(*reg)); +} + +int +fdtbus_regulator_enable(struct fdtbus_regulator *reg) +{ + struct fdtbus_regulator_controller *rc = reg->reg_rc; + + return rc->rc_funcs->enable(rc->rc_dev, true); +} + +int +fdtbus_regulator_disable(struct fdtbus_regulator *reg) +{ + struct fdtbus_regulator_controller *rc = reg->reg_rc; + + return rc->rc_funcs->enable(rc->rc_dev, false); +} + +int +fdtbus_register_i2c_controller(device_t dev, int phandle, + const struct fdtbus_i2c_controller_func *funcs) +{ + struct fdtbus_i2c_controller *i2c; + + i2c = kmem_alloc(sizeof(*i2c), KM_SLEEP); + i2c->i2c_dev = dev; + i2c->i2c_phandle = phandle; + i2c->i2c_funcs = funcs; + + i2c->i2c_next = fdtbus_i2c; + fdtbus_i2c = i2c; + + return 0; +} + +static struct fdtbus_i2c_controller * +fdtbus_get_i2c_controller(int phandle) +{ + struct fdtbus_i2c_controller *i2c; + + for (i2c = fdtbus_i2c; i2c; i2c = i2c->i2c_next) { + if (i2c->i2c_phandle == phandle) { + return i2c; + } + } + + return NULL; +} + +i2c_tag_t +fdtbus_get_i2c_tag(int phandle) +{ + struct fdtbus_i2c_controller *i2c; + + i2c = fdtbus_get_i2c_controller(phandle); + if (i2c == NULL) + return NULL; + + return i2c->i2c_funcs->get_tag(i2c->i2c_dev); +} + +int +fdtbus_get_phandle(int phandle, const char *prop) +{ + u_int phandle_ref; + u_int *buf; + int len; + + len = OF_getproplen(phandle, prop); + if (len < sizeof(phandle_ref)) + return -1; + + buf = kmem_alloc(len, KM_SLEEP); + + if (OF_getprop(phandle, prop, buf, len) != len) { + kmem_free(buf, len); + return -1; + } + + phandle_ref = fdt32_to_cpu(buf[0]); + kmem_free(buf, len); + + const void *data = fdt_openfirm_get_data(); + const int off = fdt_node_offset_by_phandle(data, phandle_ref); + if (off < 0) { + return -1; + } + + return fdt_openfirm_get_phandle(off); +} + +int +fdtbus_get_reg(int phandle, u_int index, bus_addr_t *paddr, bus_size_t *psize) +{ + bus_addr_t addr; + bus_size_t size; + uint8_t *buf; + int len; + + const int addr_cells = fdtbus_get_addr_cells(phandle); + const int size_cells = fdtbus_get_size_cells(phandle); + if (addr_cells == -1 || size_cells == -1) + return -1; + + const u_int reglen = size_cells * 4 + addr_cells * 4; + if (reglen == 0) + return -1; + + len = OF_getproplen(phandle, "reg"); + if (len <= 0) + return -1; + + const u_int nregs = len / reglen; + + if (index >= nregs) + return -1; + + buf = kmem_alloc(len, KM_SLEEP); + if (buf == NULL) + return -1; + + len = OF_getprop(phandle, "reg", buf, len); + + switch (addr_cells) { + case 0: + addr = 0; + break; + case 1: + addr = be32dec(&buf[index * reglen + 0]); + break; + case 2: + addr = be64dec(&buf[index * reglen + 0]); + break; + default: + panic("fdtbus_get_reg: unsupported addr_cells %d", addr_cells); + } + + switch (size_cells) { + case 0: + size = 0; + break; + case 1: + size = be32dec(&buf[index * reglen + addr_cells * 4]); + break; + case 2: + size = be64dec(&buf[index * reglen + addr_cells * 4]); + break; + default: + panic("fdtbus_get_reg: unsupported size_cells %d", size_cells); + } + + if (paddr) + *paddr = addr; + if (psize) + *psize = size; + + kmem_free(buf, len); + + return 0; +} Index: src/sys/dev/fdt/fdtbus.c diff -u /dev/null src/sys/dev/fdt/fdtbus.c:1.1 --- /dev/null Sun Dec 13 17:30:40 2015 +++ src/sys/dev/fdt/fdtbus.c Sun Dec 13 17:30:40 2015 @@ -0,0 +1,161 @@ +/* $NetBSD: fdtbus.c,v 1.1 2015/12/13 17:30:40 jmcneill Exp $ */ + +/*- + * Copyright (c) 2015 Jared D. McNeill <jmcne...@invisible.ca> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__KERNEL_RCSID(0, "$NetBSD: fdtbus.c,v 1.1 2015/12/13 17:30:40 jmcneill Exp $"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/device.h> +#include <sys/kmem.h> + +#include <sys/bus.h> + +#include <dev/ofw/openfirm.h> + +#include <dev/fdt/fdtvar.h> + +static int fdt_match(device_t, cfdata_t, void *); +static void fdt_attach(device_t, device_t, void *); +static void fdt_scan(device_t, const struct fdt_attach_args *, const char *); +static bool fdt_is_init(const struct fdt_attach_args *, const char *); + +CFATTACH_DECL_NEW(fdt, 0, + fdt_match, fdt_attach, NULL, NULL); + +static int fdt_print(void *, const char *); + +static int +fdt_match(device_t parent, cfdata_t cf, void *aux) +{ + const struct fdt_attach_args *faa = aux; + + if (!OF_child(faa->faa_phandle)) + return 0; + + return 1; +} + +static void +fdt_attach(device_t parent, device_t self, void *aux) +{ + const struct fdt_attach_args *faa = aux; + const int phandle = faa->faa_phandle; + char *model; + int len, n; + + aprint_naive("\n"); + len = OF_getproplen(phandle, "model"); + if (len > 0) { + model = kmem_zalloc(len, KM_SLEEP); + if (OF_getprop(phandle, "model", model, len) == len) { + aprint_normal(": %s\n", model); + } else { + aprint_normal("\n"); + } + kmem_free(model, len); + } else { + aprint_normal("\n"); + } + + for (n = 0; n < faa->faa_ninit; n++) { + fdt_scan(self, faa, faa->faa_init[n]); + } + fdt_scan(self, faa, NULL); +} + +static void +fdt_scan(device_t self, const struct fdt_attach_args *faa, const char *devname) +{ + const int phandle = faa->faa_phandle; + int len, alen, child; + char *name, *status; + + for (child = OF_child(phandle); child; child = OF_peer(child)) { + struct fdt_attach_args nfaa = *faa; + nfaa.faa_phandle = child; + + /* Only attach to nodes with a compatible property */ + len = OF_getproplen(child, "compatible"); + if (len <= 0) + continue; + + /* If there is a "status" property, make sure it is "okay" */ + len = OF_getproplen(child, "status"); + if (len > 0) { + status = kmem_zalloc(len, KM_SLEEP); + alen = OF_getprop(child, "status", status, len); + KASSERT(alen == len); + const bool okay_p = strcmp(status, "okay") == 0; + kmem_free(status, len); + if (!okay_p) { + continue; + } + } + + /* Attach the device */ + len = OF_getproplen(child, "name"); + if (len <= 0) { + continue; + } + name = kmem_zalloc(len, KM_SLEEP); + if (OF_getprop(child, "name", name, len) == len) { + nfaa.faa_name = name; + if ((devname != NULL && strcmp(name, devname) == 0) || + (devname == NULL && !fdt_is_init(faa, name))) { + config_found(self, &nfaa, fdt_print); + } + } + kmem_free(name, len); + } +} + +static bool +fdt_is_init(const struct fdt_attach_args *faa, const char *devname) +{ + u_int n; + + for (n = 0; n < faa->faa_ninit; n++) { + if (strcmp(faa->faa_init[n], devname) == 0) + return true; + } + + return false; +} + +static int +fdt_print(void *aux, const char *pnp) +{ + const struct fdt_attach_args * const faa = aux; + + if (pnp) { + aprint_normal("%s at %s", faa->faa_name, pnp); + } + + return UNCONF; +} Index: src/sys/dev/fdt/fdtvar.h diff -u /dev/null src/sys/dev/fdt/fdtvar.h:1.1 --- /dev/null Sun Dec 13 17:30:40 2015 +++ src/sys/dev/fdt/fdtvar.h Sun Dec 13 17:30:40 2015 @@ -0,0 +1,115 @@ +/* $NetBSD: fdtvar.h,v 1.1 2015/12/13 17:30:40 jmcneill Exp $ */ + +/*- + * Copyright (c) 2015 Jared D. McNeill <jmcne...@invisible.ca> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _DEV_FDT_FDTVAR_H +#define _DEV_FDT_FDTVAR_H + +#include <sys/types.h> +#include <sys/bus.h> + +#include <dev/i2c/i2cvar.h> + +#include <dev/ofw/openfirm.h> + +struct fdt_attach_args { + const char *faa_name; + bus_space_tag_t faa_bst; + bus_space_tag_t faa_a4x_bst; + bus_dma_tag_t faa_dmat; + int faa_phandle; + + const char **faa_init; + int faa_ninit; +}; + +/* flags for fdtbus_intr_establish */ +#define FDT_INTR_MPSAFE __BIT(0) + +struct fdtbus_interrupt_controller_func { + void * (*establish)(device_t, int, u_int, int, int, + int (*)(void *), void *); + void (*disestablish)(device_t, void *); + bool (*intrstr)(device_t, int, u_int, char *, size_t); +}; + +struct fdtbus_i2c_controller_func { + i2c_tag_t (*get_tag)(device_t); +}; + +struct fdtbus_gpio_controller; + +struct fdtbus_gpio_pin { + struct fdtbus_gpio_controller *gp_gc; + void *gp_priv; +}; + +struct fdtbus_gpio_controller_func { + void * (*acquire)(device_t, const void *, size_t, int); + void (*release)(device_t, void *); + int (*read)(device_t, void *); + void (*write)(device_t, void *, int); +}; + +struct fdtbus_regulator_controller; + +struct fdtbus_regulator { + struct fdtbus_regulator_controller *reg_rc; +}; + +struct fdtbus_regulator_controller_func { + int (*acquire)(device_t); + void (*release)(device_t); + int (*enable)(device_t, bool); +}; + +int fdtbus_register_interrupt_controller(device_t, int, + const struct fdtbus_interrupt_controller_func *); +int fdtbus_register_i2c_controller(device_t, int, + const struct fdtbus_i2c_controller_func *); +int fdtbus_register_gpio_controller(device_t, int, + const struct fdtbus_gpio_controller_func *); +int fdtbus_register_regulator_controller(device_t, int, + const struct fdtbus_regulator_controller_func *); + +int fdtbus_get_reg(int, u_int, bus_addr_t *, bus_size_t *); +int fdtbus_get_phandle(int, const char *); +i2c_tag_t fdtbus_get_i2c_tag(int); +void * fdtbus_intr_establish(int, u_int, int, int, + int (*func)(void *), void *arg); +void fdtbus_intr_disestablish(int, void *); +bool fdtbus_intr_str(int, u_int, char *, size_t); +struct fdtbus_gpio_pin *fdtbus_gpio_acquire(int, const char *, int); +void fdtbus_gpio_release(struct fdtbus_gpio_pin *); +int fdtbus_gpio_read(struct fdtbus_gpio_pin *); +void fdtbus_gpio_write(struct fdtbus_gpio_pin *, int); +struct fdtbus_regulator *fdtbus_regulator_acquire(int, const char *); +void fdtbus_regulator_release(struct fdtbus_regulator *); +int fdtbus_regulator_enable(struct fdtbus_regulator *); +int fdtbus_regulator_disable(struct fdtbus_regulator *); + +#endif /* _DEV_FDT_FDTVAR_H */ Index: src/sys/dev/fdt/files.fdt diff -u /dev/null src/sys/dev/fdt/files.fdt:1.1 --- /dev/null Sun Dec 13 17:30:40 2015 +++ src/sys/dev/fdt/files.fdt Sun Dec 13 17:30:40 2015 @@ -0,0 +1,22 @@ +# $NetBSD: files.fdt,v 1.1 2015/12/13 17:30:40 jmcneill Exp $ + +include "external/bsd/libfdt/conf/files.libfdt" + +defflag opt_fdt.h FDT: libfdt + +define fdtbus { } + +device fdt { } +attach fdt at fdtbus +file dev/fdt/fdtbus.c fdt + +device simplebus : fdtbus +attach simplebus at fdt +file dev/fdt/simplebus.c simplebus + +device fregulator +attach fregulator at fdt +file dev/fdt/fixedregulator.c fregulator + +file dev/fdt/fdt_openfirm.c fdtbus +file dev/fdt/fdt_subr.c fdtbus Index: src/sys/dev/fdt/fixedregulator.c diff -u /dev/null src/sys/dev/fdt/fixedregulator.c:1.1 --- /dev/null Sun Dec 13 17:30:40 2015 +++ src/sys/dev/fdt/fixedregulator.c Sun Dec 13 17:30:40 2015 @@ -0,0 +1,151 @@ +/* $NetBSD: fixedregulator.c,v 1.1 2015/12/13 17:30:40 jmcneill Exp $ */ + +/*- + * Copyright (c) 2015 Jared D. McNeill <jmcne...@invisible.ca> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__KERNEL_RCSID(0, "$NetBSD: fixedregulator.c,v 1.1 2015/12/13 17:30:40 jmcneill Exp $"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/device.h> +#include <sys/kmem.h> +#include <sys/bus.h> +#include <sys/gpio.h> + +#include <dev/fdt/fdtvar.h> + +static int fixedregulator_match(device_t, cfdata_t, void *); +static void fixedregulator_attach(device_t, device_t, void *); + +static int fixedregulator_acquire(device_t); +static void fixedregulator_release(device_t); +static int fixedregulator_enable(device_t, bool); + +struct fdtbus_regulator_controller_func fixedregulator_funcs = { + .acquire = fixedregulator_acquire, + .release = fixedregulator_release, + .enable = fixedregulator_enable +}; + +struct fixedregulator_softc { + device_t sc_dev; + int sc_phandle; + + struct fdtbus_gpio_pin *sc_pin; + bool sc_always_on; + bool sc_enable_val; +}; + +CFATTACH_DECL_NEW(fregulator, sizeof(struct fixedregulator_softc), + fixedregulator_match, fixedregulator_attach, NULL, NULL); + +static int +fixedregulator_match(device_t parent, cfdata_t cf, void *aux) +{ + const char * const compatible[] = { "regulator-fixed", NULL }; + const struct fdt_attach_args *faa = aux; + + return of_match_compatible(faa->faa_phandle, compatible); +} + +static void +fixedregulator_attach(device_t parent, device_t self, void *aux) +{ + struct fixedregulator_softc * const sc = device_private(self); + const struct fdt_attach_args *faa = aux; + const int phandle = faa->faa_phandle; + char *name; + int len, gpioflags; + + sc->sc_dev = self; + sc->sc_phandle = phandle; + + aprint_naive("\n"); + + len = OF_getproplen(phandle, "regulator-name"); + if (len > 0) { + name = kmem_zalloc(len, KM_SLEEP); + if (OF_getprop(phandle, "regulator-name", name, len) == len) { + aprint_normal(": %s\n", name); + } else { + aprint_normal("\n"); + } + kmem_free(name, len); + } else { + aprint_normal("\n"); + } + + gpioflags = GPIO_PIN_OUTPUT; + if (OF_getproplen(phandle, "gpio-open-drain") >= 0) { + gpioflags |= GPIO_PIN_OPENDRAIN; + } + + sc->sc_pin = fdtbus_gpio_acquire(phandle, "gpio", gpioflags); + if (sc->sc_pin == NULL || + OF_getproplen(phandle, "regulator-always-on") >= 0) { + sc->sc_always_on = true; + } + if (OF_getproplen(phandle, "enable-active-high") >= 0) { + sc->sc_enable_val = 1; + } + + fdtbus_register_regulator_controller(self, phandle, + &fixedregulator_funcs); +} + +static int +fixedregulator_acquire(device_t dev) +{ + return 0; +} + +static void +fixedregulator_release(device_t dev) +{ +} + +static int +fixedregulator_enable(device_t dev, bool enable) +{ + struct fixedregulator_softc * const sc = device_private(dev); + + if (enable) { + if (sc->sc_always_on) { + return 0; + } else { + fdtbus_gpio_write(sc->sc_pin, sc->sc_enable_val); + return 0; + } + } else { + if (sc->sc_always_on) { + return EIO; + } else { + fdtbus_gpio_write(sc->sc_pin, !sc->sc_enable_val); + return 0; + } + } +} Index: src/sys/dev/fdt/simplebus.c diff -u /dev/null src/sys/dev/fdt/simplebus.c:1.1 --- /dev/null Sun Dec 13 17:30:40 2015 +++ src/sys/dev/fdt/simplebus.c Sun Dec 13 17:30:40 2015 @@ -0,0 +1,86 @@ +/* $NetBSD: simplebus.c,v 1.1 2015/12/13 17:30:40 jmcneill Exp $ */ + +/*- + * Copyright (c) 2015 Jared D. McNeill <jmcne...@invisible.ca> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__KERNEL_RCSID(0, "$NetBSD: simplebus.c,v 1.1 2015/12/13 17:30:40 jmcneill Exp $"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/device.h> +#include <sys/kmem.h> + +#include <sys/bus.h> + +#include <dev/fdt/fdtvar.h> + +static int simplebus_match(device_t, cfdata_t, void *); +static void simplebus_attach(device_t, device_t, void *); + +CFATTACH_DECL_NEW(simplebus, 0, + simplebus_match, simplebus_attach, NULL, NULL); + +static int +simplebus_match(device_t parent, cfdata_t cf, void *aux) +{ + const char * const compatible[] = { "simple-bus", NULL }; + const struct fdt_attach_args *faa = aux; + + return of_match_compatible(faa->faa_phandle, compatible); +} + +static void +simplebus_attach(device_t parent, device_t self, void *aux) +{ + const struct fdt_attach_args *faa = aux; + const int phandle = faa->faa_phandle; + char *name; + int len; + + aprint_naive("\n"); + + len = OF_getproplen(phandle, "name"); + if (len > 0) { + name = kmem_zalloc(len, KM_SLEEP); + if (OF_getprop(phandle, "name", name, len) == len) { + aprint_normal(": %s\n", name); + } else { + aprint_normal("\n"); + } + kmem_free(name, len); + } else { + aprint_normal("\n"); + } + + struct fdt_attach_args nfaa = *faa; + nfaa.faa_name = "simple-bus"; + nfaa.faa_init = NULL; + nfaa.faa_ninit = 0; + nfaa.faa_phandle = phandle; + + config_found(self, &nfaa, NULL); +}