diff options
Diffstat (limited to 'sys/dev')
-rw-r--r-- | sys/dev/fdt/axppmic.c | 320 | ||||
-rw-r--r-- | sys/dev/fdt/files.fdt | 4 |
2 files changed, 323 insertions, 1 deletions
diff --git a/sys/dev/fdt/axppmic.c b/sys/dev/fdt/axppmic.c new file mode 100644 index 00000000000..1f305b28752 --- /dev/null +++ b/sys/dev/fdt/axppmic.c @@ -0,0 +1,320 @@ +/* $OpenBSD: axppmic.c,v 1.1 2017/12/17 18:25:25 kettenis Exp $ */ +/* + * Copyright (c) 2017 Mark Kettenis <kettenis@openbsd.org> + * + * 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 <sys/malloc.h> + +#include <dev/fdt/rsbvar.h> + +#include <dev/ofw/openfirm.h> +#include <dev/ofw/ofw_regulator.h> +#include <dev/ofw/fdt.h> + +#define AXP806_REG_ADDR_EXT 0xff +#define AXP806_REG_ADDR_EXT_MASTER_MODE (0 << 4) +#define AXP806_REG_ADDR_EXT_SLAVE_MODE (1 << 4) + +struct axppmic_regdata { + const char *name; + uint8_t ereg, emask, eval, dval; + uint8_t vreg, vmask; + uint32_t base, delta; + uint32_t base2, delta2; +}; + +struct axppmic_regdata axp806_regdata[] = { + { "dcdca", 0x10, (1 << 0), (1 << 0), (0 << 0), + 0x12, 0x7f, 600000, 10000, 1120000, 20000 }, + { "dcdcb", 0x10, (1 << 1), (1 << 1), (0 << 1), + 0x13, 0x1f, 1000000, 50000 }, + { "dcdcc", 0x10, (1 << 2), (1 << 2), (0 << 2), + 0x14, 0x7f, 600000, 10000, 1120000, 20000 }, + { "dcdcd", 0x10, (1 << 3), (1 << 3), (0 << 3), + 0x15, 0x3f, 600000, 20000, 1600000, 100000 }, + { "dcdce", 0x10, (1 << 4), (1 << 4), (0 << 4), + 0x16, 0x1f, 1100000, 100000 }, + { "aldo1", 0x10, (1 << 5), (1 << 5), (0 << 5), + 0x17, 0x1f, 700000, 100000 }, + { "aldo2", 0x10, (1 << 6), (1 << 6), (0 << 6), + 0x18, 0x1f, 700000, 100000 }, + { "aldo3", 0x10, (1 << 7), (1 << 7), (0 << 7), + 0x19, 0x1f, 700000, 100000 }, + { "bldo1", 0x11, (1 << 0), (1 << 0), (0 << 0), + 0x20, 0x0f, 700000, 100000 }, + { "bldo2", 0x11, (1 << 1), (1 << 1), (0 << 1), + 0x21, 0x0f, 700000, 100000 }, + { "bldo3", 0x11, (1 << 2), (1 << 2), (0 << 2), + 0x22, 0x0f, 700000, 100000 }, + { "bldo4", 0x11, (1 << 3), (1 << 3), (0 << 3), + 0x23, 0x0f, 700000, 100000 }, + { "cldo1", 0x11, (1 << 4), (1 << 4), (0 << 4), + 0x24, 0x1f, 700000, 100000 }, + { "cldo2", 0x11, (1 << 5), (1 << 5), (0 << 5), + 0x25, 0x1f, 700000, 100000, 3600000, 200000 }, + { "cldo3", 0x11, (1 << 6), (1 << 6), (0 << 6), + 0x26, 0x1f, 700000, 100000 }, + { "sw", 0x11, (1 << 7), (1 << 7), (0 << 7) }, + { NULL } +}; + +struct axppmic_regdata axp809_regdata[] = { + { "dcdc1", 0x10, (1 << 1), (1 << 1), (0 << 1), + 0x21, 0x1f, 1600000, 100000 }, + { "dcdc2", 0x10, (1 << 2), (1 << 2), (0 << 2), + 0x22, 0x3f, 600000, 20000 }, + { "dcdc3", 0x10, (1 << 3), (1 << 3), (0 << 3), + 0x23, 0x3f, 600000, 20000 }, + { "dcdc4", 0x10, (1 << 4), (1 << 4), (0 << 4), + 0x24, 0x3f, 600000, 20000, 1800000, 100000 }, + { "dcdc5", 0x10, (1 << 5), (1 << 5), (0 << 5), + 0x25, 0x1f, 1000000, 50000 }, + { "dc5ldo", 0x10, (1 << 0), (1 << 0), (0 << 0), + 0x1c, 0x07, 700000, 100000 }, + { "aldo1", 0x10, (1 << 6), (1 << 6), (0 << 6), + 0x28, 0x1f, 700000, 100000 }, + { "aldo2", 0x10, (1 << 7), (1 << 7), (0 << 7), + 0x28, 0x1f, 700000, 100000 }, + { "aldo3", 0x12, (1 << 5), (1 << 5), (0 << 5), + 0x28, 0x1f, 700000, 100000 }, + { "dldo1", 0x12, (1 << 3), (1 << 3), (0 << 3), + 0x15, 0x1f, 700000, 100000 }, + { "dldo2", 0x12, (1 << 4), (1 << 4), (0 << 4), + 0x16, 0x1f, 700000, 100000 }, + { "eldo1", 0x12, (1 << 0), (1 << 0), (0 << 0), + 0x19, 0x1f, 700000, 100000 }, + { "eldo2", 0x12, (1 << 1), (1 << 1), (0 << 1), + 0x1a, 0x1f, 700000, 100000 }, + { "eldo3", 0x12, (1 << 2), (1 << 2), (0 << 2), + 0x19, 0x1f, 700000, 100000 }, + { "ldo_io0", 0x90, 0x07, 0x03, 0x04, + 0x91, 0x1f, 700000, 100000 }, + { "ldo_io1", 0x92, 0x07, 0x03, 0x04, + 0x93, 0x1f, 700000, 100000 }, + { NULL } +}; + +struct axppmic_device { + const char *name; + const char *chip; + struct axppmic_regdata *regdata; +}; + +struct axppmic_device axppmic_devices[] = { + { "x-powers,axp806", "AXP806", axp806_regdata }, + { "x-powers,axp809", "AXP809", axp809_regdata } +}; + +const struct axppmic_device * +axppmic_lookup(const char *name) +{ + int i; + + for (i = 0; i < nitems(axppmic_devices); i++) { + if (strcmp(name, axppmic_devices[i].name) == 0) + return &axppmic_devices[i]; + } + + return NULL; +} + +struct axppmic_softc { + struct device sc_dev; + void *sc_cookie; + uint16_t sc_rta; + + struct axppmic_regdata *sc_regdata; +}; + +inline uint8_t +axppmic_read_reg(struct axppmic_softc *sc, uint8_t reg) +{ + return rsb_read_1(sc->sc_cookie, sc->sc_rta, reg); +} + +inline void +axppmic_write_reg(struct axppmic_softc *sc, uint8_t reg, uint8_t value) +{ + rsb_write_1(sc->sc_cookie, sc->sc_rta, reg, value); +} + +int axppmic_match(struct device *, void *, void *); +void axppmic_attach(struct device *, struct device *, void *); + +struct cfattach axppmic_rsb_ca = { + sizeof(struct axppmic_softc), axppmic_match, axppmic_attach +}; + +struct cfdriver axppmic_rsb_cd = { + NULL, "axppmic", DV_DULL +}; + +void axppmic_attach_regulator(struct axppmic_softc *, int); + +int +axppmic_match(struct device *parent, void *match, void *aux) +{ + struct rsb_attach_args *ra = aux; + + if (axppmic_lookup(ra->ra_name)) + return 1; + return 0; +} + +void +axppmic_attach(struct device *parent, struct device *self, void *aux) +{ + struct axppmic_softc *sc = (struct axppmic_softc *)self; + const struct axppmic_device *device; + struct rsb_attach_args *ra = aux; + int node; + + sc->sc_cookie = ra->ra_cookie; + sc->sc_rta = ra->ra_rta; + + device = axppmic_lookup(ra->ra_name); + printf(": %s\n", device->chip); + + sc->sc_regdata = device->regdata; + + /* Switch AXP806 into master or slave mode. */ + if (strcmp(ra->ra_name, "x-powers,axp806") == 0) { + if (OF_getproplen(ra->ra_node, "x-powers,master-mode") == 0) { + axppmic_write_reg(sc, AXP806_REG_ADDR_EXT, + AXP806_REG_ADDR_EXT_MASTER_MODE); + } else { + axppmic_write_reg(sc, AXP806_REG_ADDR_EXT, + AXP806_REG_ADDR_EXT_SLAVE_MODE); + } + } + + node = OF_getnodebyname(ra->ra_node, "regulators"); + if (node == 0) + return; + for (node = OF_child(node); node; node = OF_peer(node)) + axppmic_attach_regulator(sc, node); +} + +struct axppmic_regulator { + struct axppmic_softc *ar_sc; + + uint8_t ar_ereg, ar_emask; + uint8_t ar_eval, ar_dval; + + uint8_t ar_vreg, ar_vmask; + uint32_t ar_base, ar_delta; + uint32_t ar_base2, ar_delta2; + + struct regulator_device ar_rd; +}; + +uint32_t axppmic_get_voltage(void *); +int axppmic_set_voltage(void *, uint32_t); +int axppmic_enable(void *, int); + +void +axppmic_attach_regulator(struct axppmic_softc *sc, int node) +{ + struct axppmic_regulator *ar; + char name[32]; + int i; + + name[0] = 0; + OF_getprop(node, "name", name, sizeof(name)); + name[sizeof(name) - 1] = 0; + for (i = 0; sc->sc_regdata[i].name; i++) { + if (strcmp(sc->sc_regdata[i].name, name) == 0) + break; + } + if (sc->sc_regdata[i].name == NULL) + return; + + ar = malloc(sizeof(*ar), M_DEVBUF, M_WAITOK | M_ZERO); + ar->ar_sc = sc; + + ar->ar_ereg = sc->sc_regdata[i].ereg; + ar->ar_emask = sc->sc_regdata[i].emask; + ar->ar_eval = sc->sc_regdata[i].eval; + ar->ar_dval = sc->sc_regdata[i].dval; + ar->ar_vreg = sc->sc_regdata[i].vreg; + ar->ar_vmask = sc->sc_regdata[i].vmask; + ar->ar_base = sc->sc_regdata[i].base; + ar->ar_delta = sc->sc_regdata[i].delta; + + ar->ar_rd.rd_node = node; + ar->ar_rd.rd_cookie = ar; + ar->ar_rd.rd_get_voltage = axppmic_get_voltage; + ar->ar_rd.rd_set_voltage = axppmic_set_voltage; + ar->ar_rd.rd_enable = axppmic_enable; + regulator_register(&ar->ar_rd); +} + +uint32_t +axppmic_get_voltage(void *cookie) +{ + struct axppmic_regulator *ar = cookie; + uint32_t voltage; + uint8_t value; + + value = axppmic_read_reg(ar->ar_sc, ar->ar_vreg); + value &= ar->ar_vmask; + voltage = ar->ar_base + value * ar->ar_delta; + if (ar->ar_base2 > 0 && voltage > ar->ar_base2) { + value -= (ar->ar_base2 - ar->ar_base) / ar->ar_delta; + voltage = ar->ar_base2 + value * ar->ar_delta2; + } + return voltage; +} + +int +axppmic_set_voltage(void *cookie, uint32_t voltage) +{ + struct axppmic_regulator *ar = cookie; + uint32_t value, reg; + + if (voltage < ar->ar_base) + return EINVAL; + value = (voltage - ar->ar_base) / ar->ar_delta; + if (ar->ar_base2 > 0 && voltage > ar->ar_base2) { + value = (ar->ar_base2 - ar->ar_base) / ar->ar_delta; + value += (voltage - ar->ar_base2) / ar->ar_delta2; + } + if (value > ar->ar_vmask) + return EINVAL; + + reg = axppmic_read_reg(ar->ar_sc, ar->ar_vreg); + reg &= ar->ar_vmask; + axppmic_write_reg(ar->ar_sc, ar->ar_vreg, reg | value); + return 0; +} + +int +axppmic_enable(void *cookie, int on) +{ + struct axppmic_regulator *ar = cookie; + uint8_t reg; + + reg = axppmic_read_reg(ar->ar_sc, ar->ar_ereg); + reg &= ~ar->ar_emask; + if (on) + reg |= ar->ar_eval; + else + reg |= ar->ar_dval; + axppmic_write_reg(ar->ar_sc, ar->ar_ereg, reg); + return 0; +} diff --git a/sys/dev/fdt/files.fdt b/sys/dev/fdt/files.fdt index b8b17cb99a4..d8838acfeba 100644 --- a/sys/dev/fdt/files.fdt +++ b/sys/dev/fdt/files.fdt @@ -1,4 +1,4 @@ -# $OpenBSD: files.fdt,v 1.28 2017/12/16 10:22:13 kettenis Exp $ +# $OpenBSD: files.fdt,v 1.29 2017/12/17 18:25:25 kettenis Exp $ # # Config file and device description for machine-independent FDT code. # Included by ports that need it. @@ -34,7 +34,9 @@ file dev/fdt/sxitwi.c sxitwi device axppmic attach axppmic at i2c +attach axppmic at sxirsb with axppmic_rsb file dev/fdt/axp20x.c axppmic +file dev/fdt/axppmic.c axppmic_rsb device bcmaux attach bcmaux at fdt |