From owner-svn-src-all@freebsd.org Fri Dec 4 14:57:13 2020 Return-Path: Delivered-To: svn-src-all@mailman.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mailman.nyi.freebsd.org (Postfix) with ESMTP id 906E54A2EA7; Fri, 4 Dec 2020 14:57:13 +0000 (UTC) (envelope-from mmel@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "Let's Encrypt Authority X3" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 4CnbQd3gHLz4pmT; Fri, 4 Dec 2020 14:57:13 +0000 (UTC) (envelope-from mmel@FreeBSD.org) Received: from repo.freebsd.org (repo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id 705035893; Fri, 4 Dec 2020 14:57:13 +0000 (UTC) (envelope-from mmel@FreeBSD.org) Received: from repo.freebsd.org ([127.0.1.37]) by repo.freebsd.org (8.15.2/8.15.2) with ESMTP id 0B4EvDaZ029436; Fri, 4 Dec 2020 14:57:13 GMT (envelope-from mmel@FreeBSD.org) Received: (from mmel@localhost) by repo.freebsd.org (8.15.2/8.15.2/Submit) id 0B4EvCxS029432; Fri, 4 Dec 2020 14:57:12 GMT (envelope-from mmel@FreeBSD.org) Message-Id: <202012041457.0B4EvCxS029432@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: mmel set sender to mmel@FreeBSD.org using -f From: Michal Meloun Date: Fri, 4 Dec 2020 14:57:12 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r368331 - head/sys/dev/iicbus/pmic X-SVN-Group: head X-SVN-Commit-Author: mmel X-SVN-Commit-Paths: head/sys/dev/iicbus/pmic X-SVN-Commit-Revision: 368331 X-SVN-Commit-Repository: base MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: "SVN commit messages for the entire src tree \(except for " user" and " projects" \)" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Fri, 04 Dec 2020 14:57:13 -0000 Author: mmel Date: Fri Dec 4 14:57:12 2020 New Revision: 368331 URL: https://svnweb.freebsd.org/changeset/base/368331 Log: Add a driver for ACT8846 used as PMIC for RK3288 SoC. Added: head/sys/dev/iicbus/pmic/ head/sys/dev/iicbus/pmic/act8846.c (contents, props changed) head/sys/dev/iicbus/pmic/act8846.h (contents, props changed) head/sys/dev/iicbus/pmic/act8846_reg.h (contents, props changed) head/sys/dev/iicbus/pmic/act8846_regulator.c (contents, props changed) Added: head/sys/dev/iicbus/pmic/act8846.c ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/sys/dev/iicbus/pmic/act8846.c Fri Dec 4 14:57:12 2020 (r368331) @@ -0,0 +1,258 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2019 Michal Meloun + * + * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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 +__FBSDID("$FreeBSD$"); + +/* + * ACT8846 PMIC driver + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include "regdev_if.h" + +static struct ofw_compat_data compat_data[] = { + {"active-semi,act8846", 1}, + {NULL, 0} +}; + +#define LOCK(_sc) sx_xlock(&(_sc)->lock) +#define UNLOCK(_sc) sx_xunlock(&(_sc)->lock) +#define LOCK_INIT(_sc) sx_init(&(_sc)->lock, "act8846") +#define LOCK_DESTROY(_sc) sx_destroy(&(_sc)->lock); +#define ASSERT_LOCKED(_sc) sx_assert(&(_sc)->lock, SA_XLOCKED); +#define ASSERT_UNLOCKED(_sc) sx_assert(&(_sc)->lock, SA_UNLOCKED); + + +/* + * Raw register access function. + */ +int +act8846_read(struct act8846_softc *sc, uint8_t reg, uint8_t *val) +{ + uint8_t addr; + int rv; + struct iic_msg msgs[2] = { + {0, IIC_M_WR, 1, &addr}, + {0, IIC_M_RD, 1, val}, + }; + + msgs[0].slave = sc->bus_addr; + msgs[1].slave = sc->bus_addr; + addr = reg; + + rv = iicbus_transfer_excl(sc->dev, msgs, 2, IIC_INTRWAIT); + if (rv != 0) { + device_printf(sc->dev, + "Error when reading reg 0x%02X, rv: %d\n", reg, rv); + return (EIO); + } + + return (0); +} + +int act8846_read_buf(struct act8846_softc *sc, uint8_t reg, uint8_t *buf, + size_t size) +{ + uint8_t addr; + int rv; + struct iic_msg msgs[2] = { + {0, IIC_M_WR, 1, &addr}, + {0, IIC_M_RD, size, buf}, + }; + + msgs[0].slave = sc->bus_addr; + msgs[1].slave = sc->bus_addr; + addr = reg; + + rv = iicbus_transfer_excl(sc->dev, msgs, 2, IIC_INTRWAIT); + if (rv != 0) { + device_printf(sc->dev, + "Error when reading reg 0x%02X, rv: %d\n", reg, rv); + return (EIO); + } + + return (0); +} + +int +act8846_write(struct act8846_softc *sc, uint8_t reg, uint8_t val) +{ + uint8_t data[2]; + int rv; + + struct iic_msg msgs[1] = { + {0, IIC_M_WR, 2, data}, + }; + + msgs[0].slave = sc->bus_addr; + data[0] = reg; + data[1] = val; + + rv = iicbus_transfer_excl(sc->dev, msgs, 1, IIC_INTRWAIT); + if (rv != 0) { + device_printf(sc->dev, + "Error when writing reg 0x%02X, rv: %d\n", reg, rv); + return (EIO); + } + return (0); +} + +int act8846_write_buf(struct act8846_softc *sc, uint8_t reg, uint8_t *buf, + size_t size) +{ + uint8_t data[1]; + int rv; + struct iic_msg msgs[2] = { + {0, IIC_M_WR, 1, data}, + {0, IIC_M_WR | IIC_M_NOSTART, size, buf}, + }; + + msgs[0].slave = sc->bus_addr; + msgs[1].slave = sc->bus_addr; + data[0] = reg; + + rv = iicbus_transfer_excl(sc->dev, msgs, 2, IIC_INTRWAIT); + if (rv != 0) { + device_printf(sc->dev, + "Error when writing reg 0x%02X, rv: %d\n", reg, rv); + return (EIO); + } + return (0); +} + +int +act8846_modify(struct act8846_softc *sc, uint8_t reg, uint8_t clear, uint8_t set) +{ + uint8_t val; + int rv; + + rv = act8846_read(sc, reg, &val); + if (rv != 0) + return (rv); + + val &= ~clear; + val |= set; + + rv = act8846_write(sc, reg, val); + if (rv != 0) + return (rv); + + return (0); +} + +static int +act8846_probe(device_t dev) +{ + + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data) + return (ENXIO); + + device_set_desc(dev, "ACT8846 PMIC"); + return (BUS_PROBE_DEFAULT); +} + +static int +act8846_attach(device_t dev) +{ + struct act8846_softc *sc; + int rv; + phandle_t node; + + sc = device_get_softc(dev); + sc->dev = dev; + sc->bus_addr = iicbus_get_addr(dev); + node = ofw_bus_get_node(sc->dev); + rv = 0; + LOCK_INIT(sc); + + + rv = act8846_regulator_attach(sc, node); + if (rv != 0) + goto fail; + + return (bus_generic_attach(dev)); + +fail: + LOCK_DESTROY(sc); + return (rv); +} + +static int +act8846_detach(device_t dev) +{ + struct act8846_softc *sc; + + sc = device_get_softc(dev); + LOCK_DESTROY(sc); + + return (bus_generic_detach(dev)); +} + +static device_method_t act8846_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, act8846_probe), + DEVMETHOD(device_attach, act8846_attach), + DEVMETHOD(device_detach, act8846_detach), + + /* Regdev interface */ + DEVMETHOD(regdev_map, act8846_regulator_map), + + DEVMETHOD_END +}; + +static devclass_t act8846_devclass; +static DEFINE_CLASS_0(act8846_pmu, act8846_driver, act8846_methods, + sizeof(struct act8846_softc)); +EARLY_DRIVER_MODULE(act8846_pmic, iicbus, act8846_driver, act8846_devclass, + NULL, NULL, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LAST); +MODULE_VERSION(act8846_pmic, 1); +MODULE_DEPEND(act8846_pmic, iicbus, IICBUS_MINVER, IICBUS_PREFVER, + IICBUS_MAXVER); +IICBUS_FDT_PNP_INFO(compat_data); \ No newline at end of file Added: head/sys/dev/iicbus/pmic/act8846.h ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/sys/dev/iicbus/pmic/act8846.h Fri Dec 4 14:57:12 2020 (r368331) @@ -0,0 +1,67 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2019 Michal Meloun + * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. + * + * $FreeBSD$ + */ + +#ifndef _ACT8846_H_ +#define _ACT8846_H_ + +#include + +struct act8846_reg_sc; +struct act8846_gpio_pin; + +struct act8846_softc { + device_t dev; + struct sx lock; + int bus_addr; + + /* Regulators. */ + struct act8846_reg_sc **regs; + int nregs; +}; + +#define RD1(sc, reg, val) act8846_read(sc, reg, val) +#define WR1(sc, reg, val) act8846_write(sc, reg, val) +#define RM1(sc, reg, clr, set) act8846_modify(sc, reg, clr, set) + +int act8846_read(struct act8846_softc *sc, uint8_t reg, uint8_t *val); +int act8846_write(struct act8846_softc *sc, uint8_t reg, uint8_t val); +int act8846_modify(struct act8846_softc *sc, uint8_t reg, uint8_t clear, + uint8_t set); +int act8846_read_buf(struct act8846_softc *sc, uint8_t reg, uint8_t *buf, + size_t size); +int act8846_write_buf(struct act8846_softc *sc, uint8_t reg, uint8_t *buf, + size_t size); + +/* Regulators */ +int act8846_regulator_attach(struct act8846_softc *sc, phandle_t node); +int act8846_regulator_map(device_t dev, phandle_t xref, int ncells, + pcell_t *cells, int *num); + +#endif /* _ACT8846_H_ */ Added: head/sys/dev/iicbus/pmic/act8846_reg.h ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/sys/dev/iicbus/pmic/act8846_reg.h Fri Dec 4 14:57:12 2020 (r368331) @@ -0,0 +1,83 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2019 Michal Meloun + * + * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. + * + * $FreeBSD$ + */ + + +#ifndef _ACT8846_REG_H_ +#define _ACT8846_REG_H_ + + +/* ACT8846 registers. */ +#define ACT8846_SYS0 0x00 +#define ACT8846_SYS1 0x01 +#define ACT8846_REG1_CTRL 0x12 +#define ACT8846_REG2_VSET0 0x20 +#define ACT8846_REG2_VSET1 0x21 +#define ACT8846_REG2_CTRL 0x22 +#define ACT8846_REG3_VSET0 0x30 +#define ACT8846_REG3_VSET1 0x31 +#define ACT8846_REG3_CTRL 0x32 +#define ACT8846_REG4_VSET0 0x40 +#define ACT8846_REG4_VSET1 0x41 +#define ACT8846_REG4_CTRL 0x42 +#define ACT8846_REG5_VSET 0x50 +#define ACT8846_REG5_CTRL 0x51 +#define ACT8846_REG6_VSET 0x58 +#define ACT8846_REG6_CTRL 0x59 +#define ACT8846_REG7_VSET 0x60 +#define ACT8846_REG7_CTRL 0x61 +#define ACT8846_REG8_VSET 0x68 +#define ACT8846_REG8_CTRL 0x69 +#define ACT8846_REG9_VSET 0x70 +#define ACT8846_REG9_CTRL 0x71 +#define ACT8846_REG10_VSET 0x80 +#define ACT8846_REG10_CTRL 0x81 +#define ACT8846_REG11_VSET 0x90 +#define ACT8846_REG11_CTRL 0x91 +#define ACT8846_REG12_VSET 0xa0 +#define ACT8846_REG12_CTRL 0xa1 +#define ACT8846_REG13_CTRL 0xb1 +#define ACT8846_PB0 0xc0 +#define ACT8846_PB1 0xc1 +#define ACT8846_PB2 0xc2 +#define ACT8846_PB3 0xc3 +#define ACT8846_PB4 0xc4 +#define ACT8846_GPIO6 0xe3 +#define ACT8846_GPIO5 0xe4 +#define ACT8846_GPIO3 0xf4 +#define ACT8846_GPIO4 0xf5 + +/* Common REGxx_CTRL bits */ +#define ACT8846_CTRL_ENA 0x80 +#define ACT8846_CTRL_OK 0x01 + +/* Common REGxx_VSEL bits */ +#define ACT8846_VSEL_MASK 0x3f + + +#endif /* _ACT8846_REG_H_ */ Added: head/sys/dev/iicbus/pmic/act8846_regulator.c ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/sys/dev/iicbus/pmic/act8846_regulator.c Fri Dec 4 14:57:12 2020 (r368331) @@ -0,0 +1,506 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2019 Michal Meloun + * + * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include + +#include + +#include "regdev_if.h" + +MALLOC_DEFINE(M_ACT8846_REG, "ACT8846 regulator", "ACT8846 power regulator"); + +#if 0 +#define dprintf(sc, format, arg...) + device_printf(sc->base_sc->dev, "%s: " format, __func__, arg) */ +#else +#define dprintf(sc, format, arg...) +#endif + +enum act8846_reg_id { + ACT8846_REG_ID_REG1, + ACT8846_REG_ID_REG2, + ACT8846_REG_ID_REG3, + ACT8846_REG_ID_REG4, + ACT8846_REG_ID_REG5, + ACT8846_REG_ID_REG6, + ACT8846_REG_ID_REG7, + ACT8846_REG_ID_REG8, + ACT8846_REG_ID_REG9, + ACT8846_REG_ID_REG10, + ACT8846_REG_ID_REG11, + ACT8846_REG_ID_REG12, + ACT8846_REG_ID_REG13, +}; + +struct act8846_regdef { + intptr_t id; /* ID */ + char *name; /* Regulator name */ + char *supply_name; /* Source property name */ + uint8_t enable_reg; + uint8_t enable_mask; + uint8_t voltage_reg; + uint8_t voltage_mask; + struct regulator_range *ranges; + int nranges; +}; +struct act8846_softc; + +struct act8846_reg_sc { + struct regnode *regnode; + struct act8846_softc *base_sc; + struct act8846_regdef *def; + phandle_t xref; + struct regnode_std_param *param; +}; + + +static struct regulator_range act8846_ranges[] = { + REG_RANGE_INIT( 0, 23, 600000, 25000), + REG_RANGE_INIT( 24, 47, 1200000, 50000), + REG_RANGE_INIT( 48, 64, 2400000, 100000), +}; + +static struct act8846_regdef act8846_regdefs[] = { + { + .id = ACT8846_REG_ID_REG1, + .name = "REG1", + .supply_name = "vp1", + .enable_reg = ACT8846_REG1_CTRL, + .enable_mask = ACT8846_CTRL_ENA, + }, + { + .id = ACT8846_REG_ID_REG2, + .name = "REG2", + .supply_name = "vp2", + .enable_reg = ACT8846_REG2_CTRL, + .enable_mask = ACT8846_CTRL_ENA, + .voltage_reg = ACT8846_REG2_VSET0, + .voltage_mask = ACT8846_VSEL_MASK, + .ranges = act8846_ranges, + .nranges = nitems(act8846_ranges), + }, + { + .id = ACT8846_REG_ID_REG3, + .name = "REG3", + .supply_name = "vp3", + .enable_reg = ACT8846_REG3_CTRL, + .enable_mask = ACT8846_CTRL_ENA, + .voltage_reg = ACT8846_REG3_VSET0, + .voltage_mask = ACT8846_VSEL_MASK, + .ranges = act8846_ranges, + .nranges = nitems(act8846_ranges), + }, + { + .id = ACT8846_REG_ID_REG4, + .name = "REG4", + .supply_name = "vp4", + .enable_reg = ACT8846_REG4_CTRL, + .enable_mask = ACT8846_CTRL_ENA, + .voltage_reg = ACT8846_REG4_VSET0, + .voltage_mask = ACT8846_VSEL_MASK, + .ranges = act8846_ranges, + .nranges = nitems(act8846_ranges), + }, + { + .id = ACT8846_REG_ID_REG5, + .name = "REG5", + .supply_name = "inl1", + .enable_reg = ACT8846_REG5_CTRL, + .enable_mask = ACT8846_CTRL_ENA, + .voltage_reg = ACT8846_REG5_VSET, + .voltage_mask = ACT8846_VSEL_MASK, + .ranges = act8846_ranges, + .nranges = nitems(act8846_ranges), + }, + { + .id = ACT8846_REG_ID_REG6, + .name = "REG6", + .supply_name = "inl1", + .enable_reg = ACT8846_REG6_CTRL, + .enable_mask = ACT8846_CTRL_ENA, + .voltage_reg = ACT8846_REG6_VSET, + .voltage_mask = ACT8846_VSEL_MASK, + .ranges = act8846_ranges, + .nranges = nitems(act8846_ranges), + }, + { + .id = ACT8846_REG_ID_REG7, + .name = "REG7", + .supply_name = "inl1", + .enable_reg = ACT8846_REG7_CTRL, + .enable_mask = ACT8846_CTRL_ENA, + .voltage_reg = ACT8846_REG7_VSET, + .voltage_mask = ACT8846_VSEL_MASK, + .ranges = act8846_ranges, + .nranges = nitems(act8846_ranges), + }, + { + .id = ACT8846_REG_ID_REG8, + .name = "REG8", + .supply_name = "inl2", + .enable_reg = ACT8846_REG8_CTRL, + .enable_mask = ACT8846_CTRL_ENA, + .voltage_reg = ACT8846_REG8_VSET, + .voltage_mask = ACT8846_VSEL_MASK, + .ranges = act8846_ranges, + .nranges = nitems(act8846_ranges), + }, + { + .id = ACT8846_REG_ID_REG9, + .name = "REG9", + .supply_name = "inl2", + .enable_reg = ACT8846_REG9_CTRL, + .enable_mask = ACT8846_CTRL_ENA, + .voltage_reg = ACT8846_REG9_VSET, + .voltage_mask = ACT8846_VSEL_MASK, + .ranges = act8846_ranges, + .nranges = nitems(act8846_ranges), + }, + { + .id = ACT8846_REG_ID_REG10, + .name = "REG10", + .supply_name = "inl3", + .enable_reg = ACT8846_REG10_CTRL, + .enable_mask = ACT8846_CTRL_ENA, + .voltage_reg = ACT8846_REG10_VSET, + .voltage_mask = ACT8846_VSEL_MASK, + .ranges = act8846_ranges, + .nranges = nitems(act8846_ranges), + }, + { + .id = ACT8846_REG_ID_REG11, + .name = "REG11", + .supply_name = "inl3", + .enable_reg = ACT8846_REG11_CTRL, + .enable_mask = ACT8846_CTRL_ENA, + .voltage_reg = ACT8846_REG11_VSET, + .voltage_mask = ACT8846_VSEL_MASK, + .ranges = act8846_ranges, + .nranges = nitems(act8846_ranges), + }, + { + .id = ACT8846_REG_ID_REG12, + .name = "REG12", + .supply_name = "inl3", + .enable_reg = ACT8846_REG12_CTRL, + .enable_mask = ACT8846_CTRL_ENA, + .voltage_reg = ACT8846_REG12_VSET, + .voltage_mask = ACT8846_VSEL_MASK, + .ranges = act8846_ranges, + .nranges = nitems(act8846_ranges), + }, + { + .id = ACT8846_REG_ID_REG13, + .name = "REG13", + .supply_name = "inl1", + .enable_reg = ACT8846_REG13_CTRL, + .enable_mask = ACT8846_CTRL_ENA, + }, +}; + +static int +act8846_read_sel(struct act8846_reg_sc *sc, uint8_t *sel) +{ + int rv; + + rv = RD1(sc->base_sc, sc->def->voltage_reg, sel); + if (rv != 0) + return (rv); + *sel &= sc->def->voltage_mask; + *sel >>= ffs(sc->def->voltage_mask) - 1; + return (0); +} + +static int +act8846_write_sel(struct act8846_reg_sc *sc, uint8_t sel) +{ + int rv; + + sel <<= ffs(sc->def->voltage_mask) - 1; + sel &= sc->def->voltage_mask; + + rv = RM1(sc->base_sc, sc->def->voltage_reg, + sc->def->voltage_mask, sel); + if (rv != 0) + return (rv); + return (rv); +} + +static int +act8846_regnode_init(struct regnode *regnode) +{ + return (0); +} + +static int +act8846_regnode_enable(struct regnode *regnode, bool enable, int *udelay) +{ + struct act8846_reg_sc *sc; + int rv; + + sc = regnode_get_softc(regnode); + + dprintf(sc, "%sabling regulator %s\n", + enable ? "En" : "Dis", + sc->def->name); + rv = RM1(sc->base_sc, sc->def->enable_reg, + sc->def->enable_mask, enable ? sc->def->enable_mask: 0); + *udelay = sc->param->enable_delay; + + return (rv); +} + +static int +act8846_regnode_set_voltage(struct regnode *regnode, int min_uvolt, + int max_uvolt, int *udelay) +{ + struct act8846_reg_sc *sc; + uint8_t sel; + int uvolt, rv; + + sc = regnode_get_softc(regnode); + + if (sc->def->ranges == NULL) + return (ENXIO); + + dprintf(sc, "Setting %s to %d<->%d uvolts\n", + sc->def->name, + min_uvolt, + max_uvolt); + rv = regulator_range_volt_to_sel8(sc->def->ranges, sc->def->nranges, + min_uvolt, max_uvolt, &sel); + if (rv != 0) + return (rv); + *udelay = sc->param->ramp_delay; + rv = act8846_write_sel(sc, sel); + + act8846_read_sel(sc, &sel); + regulator_range_sel8_to_volt(sc->def->ranges, sc->def->nranges, + sel, &uvolt); + dprintf(sc, "Regulator %s set to %d uvolt\n", sc->def->name, + uvolt); + + return (rv); +} + +static int +act8846_regnode_get_voltage(struct regnode *regnode, int *uvolt) +{ + struct act8846_reg_sc *sc; + uint8_t sel; + int rv; + + sc = regnode_get_softc(regnode); + + if (sc->def->ranges == NULL) { + if (sc->def->id == ACT8846_REG_ID_REG13) { + *uvolt = 1800000; + return (0); + } + return (ENXIO); + } + + rv = act8846_read_sel(sc, &sel); + if (rv != 0) + return (rv); + rv = regulator_range_sel8_to_volt(sc->def->ranges, sc->def->nranges, + sel, uvolt); + dprintf(sc, "Regulator %s is at %d uvolt\n", sc->def->name, + *uvolt); + + return (rv); +} + +static regnode_method_t act8846_regnode_methods[] = { + /* Regulator interface */ + REGNODEMETHOD(regnode_init, act8846_regnode_init), + REGNODEMETHOD(regnode_enable, act8846_regnode_enable), + REGNODEMETHOD(regnode_set_voltage, act8846_regnode_set_voltage), + REGNODEMETHOD(regnode_get_voltage, act8846_regnode_get_voltage), + REGNODEMETHOD_END +}; +DEFINE_CLASS_1(act8846_regnode, act8846_regnode_class, act8846_regnode_methods, + sizeof(struct act8846_reg_sc), regnode_class); + +static int +act8846_fdt_parse(struct act8846_softc *sc, phandle_t pnode, phandle_t node, + struct act8846_regdef *def, struct regnode_init_def *init_def) +{ + int rv; + phandle_t supply_node; + char prop_name[64]; /* Maximum OFW property name length. */ + + rv = regulator_parse_ofw_stdparam(sc->dev, node, init_def); + + /* Get parent supply. */ + if (def->supply_name == NULL) + return (0); + + snprintf(prop_name, sizeof(prop_name), "%s-supply", + def->supply_name); + rv = OF_getencprop(pnode, prop_name, &supply_node, + sizeof(supply_node)); + if (rv <= 0) + return (rv); + supply_node = OF_node_from_xref(supply_node); + rv = OF_getprop_alloc(supply_node, "regulator-name", + (void **)&init_def->parent_name); + if (rv <= 0) + init_def->parent_name = NULL; + return (0); +} + +static struct act8846_reg_sc * +act8846_attach(struct act8846_softc *sc, phandle_t pnode, phandle_t node, + struct act8846_regdef *def) +{ + struct act8846_reg_sc *reg_sc; + struct regnode_init_def initdef; + struct regnode *regnode; + + memset(&initdef, 0, sizeof(initdef)); + if (act8846_fdt_parse(sc, pnode, node, def, &initdef) != 0) { + device_printf(sc->dev, "cannot parse FDT data for regulator\n"); + return (NULL); + } + initdef.id = def->id; + initdef.ofw_node = node; + + regnode = regnode_create(sc->dev, &act8846_regnode_class, &initdef); + if (regnode == NULL) { + device_printf(sc->dev, "cannot create regulator\n"); + return (NULL); + } + + reg_sc = regnode_get_softc(regnode); + reg_sc->base_sc = sc; + reg_sc->def = def; + reg_sc->xref = OF_xref_from_node(node); + reg_sc->param = regnode_get_stdparam(regnode); + + regnode_register(regnode); + + if (bootverbose) { + int volt, rv; + regnode_topo_slock(); + rv = regnode_get_voltage(regnode, &volt); + if (rv == ENODEV) { + device_printf(sc->dev, + " Regulator %s: parent doesn't exist yet.\n", + regnode_get_name(regnode)); + } else if (rv != 0) { + device_printf(sc->dev, + " Regulator %s: voltage: INVALID!!!\n", + regnode_get_name(regnode)); + } else { + device_printf(sc->dev, + " Regulator %s: voltage: %d uV\n", + regnode_get_name(regnode), volt); + } + regnode_topo_unlock(); + } + + return (reg_sc); +} + + +int +act8846_regulator_attach(struct act8846_softc *sc, phandle_t node) +{ + struct act8846_reg_sc *reg; + phandle_t child, rnode; + int i; + + rnode = ofw_bus_find_child(node, "regulators"); + if (rnode <= 0) { + device_printf(sc->dev, " Cannot find regulators subnode\n"); + return (ENXIO); + } + + /* ACT8846 specific definitio. */ + sc->nregs = nitems(act8846_regdefs); + sc->regs = malloc(sizeof(struct act8846_reg_sc *) * sc->nregs, + M_ACT8846_REG, M_WAITOK | M_ZERO); + + + /* Attach all known regulators if exist in DT. */ + for (i = 0; i < sc->nregs; i++) { + child = ofw_bus_find_child(rnode, act8846_regdefs[i].name); + if (child == 0) { + if (bootverbose) + device_printf(sc->dev, + "Regulator %s missing in DT\n", + act8846_regdefs[i].name); + continue; + } + reg = act8846_attach(sc, node, child, act8846_regdefs + i); + if (reg == NULL) { + device_printf(sc->dev, "Cannot attach regulator: %s\n", + act8846_regdefs[i].name); + return (ENXIO); + } + sc->regs[i] = reg; + } + return (0); +} + +int +act8846_regulator_map(device_t dev, phandle_t xref, int ncells, + pcell_t *cells, int *num) +{ + struct act8846_softc *sc; + int i; + + sc = device_get_softc(dev); + for (i = 0; i < sc->nregs; i++) { + if (sc->regs[i] == NULL) + continue; + if (sc->regs[i]->xref == xref) { + *num = sc->regs[i]->def->id; + return (0); + } + } + return (ENXIO); +}