1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2019 Michal Meloun <mmel@FreeBSD.org> 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 */ 27 28 #include <sys/cdefs.h> 29 __FBSDID("$FreeBSD$"); 30 31 /* 32 * ACT8846 PMIC driver 33 */ 34 35 #include <sys/param.h> 36 #include <sys/systm.h> 37 #include <sys/bus.h> 38 #include <sys/kernel.h> 39 #include <sys/module.h> 40 #include <sys/malloc.h> 41 #include <sys/rman.h> 42 #include <sys/sx.h> 43 44 #include <machine/bus.h> 45 46 #include <dev/extres/regulator/regulator.h> 47 #include <dev/fdt/fdt_pinctrl.h> 48 #include <dev/iicbus/iiconf.h> 49 #include <dev/iicbus/iicbus.h> 50 #include <dev/ofw/ofw_bus.h> 51 #include <dev/ofw/ofw_bus_subr.h> 52 53 #include <dev/iicbus/pmic/act8846.h> 54 55 #include "regdev_if.h" 56 57 static struct ofw_compat_data compat_data[] = { 58 {"active-semi,act8846", 1}, 59 {NULL, 0} 60 }; 61 62 #define LOCK(_sc) sx_xlock(&(_sc)->lock) 63 #define UNLOCK(_sc) sx_xunlock(&(_sc)->lock) 64 #define LOCK_INIT(_sc) sx_init(&(_sc)->lock, "act8846") 65 #define LOCK_DESTROY(_sc) sx_destroy(&(_sc)->lock); 66 #define ASSERT_LOCKED(_sc) sx_assert(&(_sc)->lock, SA_XLOCKED); 67 #define ASSERT_UNLOCKED(_sc) sx_assert(&(_sc)->lock, SA_UNLOCKED); 68 69 70 /* 71 * Raw register access function. 72 */ 73 int 74 act8846_read(struct act8846_softc *sc, uint8_t reg, uint8_t *val) 75 { 76 uint8_t addr; 77 int rv; 78 struct iic_msg msgs[2] = { 79 {0, IIC_M_WR, 1, &addr}, 80 {0, IIC_M_RD, 1, val}, 81 }; 82 83 msgs[0].slave = sc->bus_addr; 84 msgs[1].slave = sc->bus_addr; 85 addr = reg; 86 87 rv = iicbus_transfer_excl(sc->dev, msgs, 2, IIC_INTRWAIT); 88 if (rv != 0) { 89 device_printf(sc->dev, 90 "Error when reading reg 0x%02X, rv: %d\n", reg, rv); 91 return (EIO); 92 } 93 94 return (0); 95 } 96 97 int act8846_read_buf(struct act8846_softc *sc, uint8_t reg, uint8_t *buf, 98 size_t size) 99 { 100 uint8_t addr; 101 int rv; 102 struct iic_msg msgs[2] = { 103 {0, IIC_M_WR, 1, &addr}, 104 {0, IIC_M_RD, size, buf}, 105 }; 106 107 msgs[0].slave = sc->bus_addr; 108 msgs[1].slave = sc->bus_addr; 109 addr = reg; 110 111 rv = iicbus_transfer_excl(sc->dev, msgs, 2, IIC_INTRWAIT); 112 if (rv != 0) { 113 device_printf(sc->dev, 114 "Error when reading reg 0x%02X, rv: %d\n", reg, rv); 115 return (EIO); 116 } 117 118 return (0); 119 } 120 121 int 122 act8846_write(struct act8846_softc *sc, uint8_t reg, uint8_t val) 123 { 124 uint8_t data[2]; 125 int rv; 126 127 struct iic_msg msgs[1] = { 128 {0, IIC_M_WR, 2, data}, 129 }; 130 131 msgs[0].slave = sc->bus_addr; 132 data[0] = reg; 133 data[1] = val; 134 135 rv = iicbus_transfer_excl(sc->dev, msgs, 1, IIC_INTRWAIT); 136 if (rv != 0) { 137 device_printf(sc->dev, 138 "Error when writing reg 0x%02X, rv: %d\n", reg, rv); 139 return (EIO); 140 } 141 return (0); 142 } 143 144 int act8846_write_buf(struct act8846_softc *sc, uint8_t reg, uint8_t *buf, 145 size_t size) 146 { 147 uint8_t data[1]; 148 int rv; 149 struct iic_msg msgs[2] = { 150 {0, IIC_M_WR, 1, data}, 151 {0, IIC_M_WR | IIC_M_NOSTART, size, buf}, 152 }; 153 154 msgs[0].slave = sc->bus_addr; 155 msgs[1].slave = sc->bus_addr; 156 data[0] = reg; 157 158 rv = iicbus_transfer_excl(sc->dev, msgs, 2, IIC_INTRWAIT); 159 if (rv != 0) { 160 device_printf(sc->dev, 161 "Error when writing reg 0x%02X, rv: %d\n", reg, rv); 162 return (EIO); 163 } 164 return (0); 165 } 166 167 int 168 act8846_modify(struct act8846_softc *sc, uint8_t reg, uint8_t clear, uint8_t set) 169 { 170 uint8_t val; 171 int rv; 172 173 rv = act8846_read(sc, reg, &val); 174 if (rv != 0) 175 return (rv); 176 177 val &= ~clear; 178 val |= set; 179 180 rv = act8846_write(sc, reg, val); 181 if (rv != 0) 182 return (rv); 183 184 return (0); 185 } 186 187 static int 188 act8846_probe(device_t dev) 189 { 190 191 if (!ofw_bus_status_okay(dev)) 192 return (ENXIO); 193 194 if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data) 195 return (ENXIO); 196 197 device_set_desc(dev, "ACT8846 PMIC"); 198 return (BUS_PROBE_DEFAULT); 199 } 200 201 static int 202 act8846_attach(device_t dev) 203 { 204 struct act8846_softc *sc; 205 int rv; 206 phandle_t node; 207 208 sc = device_get_softc(dev); 209 sc->dev = dev; 210 sc->bus_addr = iicbus_get_addr(dev); 211 node = ofw_bus_get_node(sc->dev); 212 rv = 0; 213 LOCK_INIT(sc); 214 215 216 rv = act8846_regulator_attach(sc, node); 217 if (rv != 0) 218 goto fail; 219 220 return (bus_generic_attach(dev)); 221 222 fail: 223 LOCK_DESTROY(sc); 224 return (rv); 225 } 226 227 static int 228 act8846_detach(device_t dev) 229 { 230 struct act8846_softc *sc; 231 232 sc = device_get_softc(dev); 233 LOCK_DESTROY(sc); 234 235 return (bus_generic_detach(dev)); 236 } 237 238 static device_method_t act8846_methods[] = { 239 /* Device interface */ 240 DEVMETHOD(device_probe, act8846_probe), 241 DEVMETHOD(device_attach, act8846_attach), 242 DEVMETHOD(device_detach, act8846_detach), 243 244 /* Regdev interface */ 245 DEVMETHOD(regdev_map, act8846_regulator_map), 246 247 DEVMETHOD_END 248 }; 249 250 static devclass_t act8846_devclass; 251 static DEFINE_CLASS_0(act8846_pmu, act8846_driver, act8846_methods, 252 sizeof(struct act8846_softc)); 253 EARLY_DRIVER_MODULE(act8846_pmic, iicbus, act8846_driver, act8846_devclass, 254 NULL, NULL, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LAST); 255 MODULE_VERSION(act8846_pmic, 1); 256 MODULE_DEPEND(act8846_pmic, iicbus, IICBUS_MINVER, IICBUS_PREFVER, 257 IICBUS_MAXVER); 258 IICBUS_FDT_PNP_INFO(compat_data);