1296936Smmel/*- 2296936Smmel * Copyright (c) 2016 Michal Meloun <mmel@FreeBSD.org> 3296936Smmel * All rights reserved. 4296936Smmel * 5296936Smmel * Redistribution and use in source and binary forms, with or without 6296936Smmel * modification, are permitted provided that the following conditions 7296936Smmel * are met: 8296936Smmel * 1. Redistributions of source code must retain the above copyright 9296936Smmel * notice, this list of conditions and the following disclaimer. 10296936Smmel * 2. Redistributions in binary form must reproduce the above copyright 11296936Smmel * notice, this list of conditions and the following disclaimer in the 12296936Smmel * documentation and/or other materials provided with the distribution. 13296936Smmel * 14296936Smmel * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15296936Smmel * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16296936Smmel * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17296936Smmel * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18296936Smmel * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19296936Smmel * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20296936Smmel * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21296936Smmel * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22296936Smmel * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23296936Smmel * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24296936Smmel * SUCH DAMAGE. 25296936Smmel */ 26296936Smmel 27296936Smmel#include <sys/cdefs.h> 28296936Smmel__FBSDID("$FreeBSD: stable/11/sys/arm/nvidia/as3722.c 308335 2016-11-05 10:56:32Z mmel $"); 29296936Smmel 30296936Smmel/* 31296936Smmel * AS3722 PMIC driver 32296936Smmel */ 33296936Smmel 34296936Smmel#include <sys/param.h> 35296936Smmel#include <sys/systm.h> 36296936Smmel#include <sys/bus.h> 37296936Smmel#include <sys/gpio.h> 38296936Smmel#include <sys/kernel.h> 39296936Smmel#include <sys/module.h> 40296936Smmel#include <sys/malloc.h> 41296936Smmel#include <sys/rman.h> 42296936Smmel#include <sys/sx.h> 43296936Smmel 44296936Smmel#include <machine/bus.h> 45296936Smmel 46296936Smmel#include <dev/extres/regulator/regulator.h> 47296936Smmel#include <dev/fdt/fdt_pinctrl.h> 48296936Smmel#include <dev/gpio/gpiobusvar.h> 49296936Smmel#include <dev/iicbus/iiconf.h> 50296936Smmel#include <dev/iicbus/iicbus.h> 51296936Smmel#include <dev/ofw/ofw_bus.h> 52296936Smmel#include <dev/ofw/ofw_bus_subr.h> 53296936Smmel 54296936Smmel#include <gnu/dts/include/dt-bindings/mfd/as3722.h> 55296936Smmel 56296936Smmel#include "clock_if.h" 57296936Smmel#include "regdev_if.h" 58296936Smmel 59296936Smmel#include "as3722.h" 60296936Smmel 61296936Smmelstatic struct ofw_compat_data compat_data[] = { 62296936Smmel {"ams,as3722", 1}, 63296936Smmel {NULL, 0}, 64296936Smmel}; 65296936Smmel 66296936Smmel#define LOCK(_sc) sx_xlock(&(_sc)->lock) 67296936Smmel#define UNLOCK(_sc) sx_xunlock(&(_sc)->lock) 68296936Smmel#define LOCK_INIT(_sc) sx_init(&(_sc)->lock, "as3722") 69296936Smmel#define LOCK_DESTROY(_sc) sx_destroy(&(_sc)->lock); 70296936Smmel#define ASSERT_LOCKED(_sc) sx_assert(&(_sc)->lock, SA_XLOCKED); 71296936Smmel#define ASSERT_UNLOCKED(_sc) sx_assert(&(_sc)->lock, SA_UNLOCKED); 72296936Smmel 73296936Smmel#define AS3722_DEVICE_ID 0x0C 74296936Smmel 75296936Smmel/* 76296936Smmel * Raw register access function. 77296936Smmel */ 78296936Smmelint 79296936Smmelas3722_read(struct as3722_softc *sc, uint8_t reg, uint8_t *val) 80296936Smmel{ 81296936Smmel uint8_t addr; 82296936Smmel int rv; 83296936Smmel struct iic_msg msgs[2] = { 84296936Smmel {0, IIC_M_WR, 1, &addr}, 85296936Smmel {0, IIC_M_RD, 1, val}, 86296936Smmel }; 87296936Smmel 88296936Smmel msgs[0].slave = sc->bus_addr; 89296936Smmel msgs[1].slave = sc->bus_addr; 90296936Smmel addr = reg; 91296936Smmel 92296936Smmel rv = iicbus_transfer(sc->dev, msgs, 2); 93296936Smmel if (rv != 0) { 94296936Smmel device_printf(sc->dev, 95296936Smmel "Error when reading reg 0x%02X, rv: %d\n", reg, rv); 96296936Smmel return (EIO); 97296936Smmel } 98296936Smmel 99296936Smmel return (0); 100296936Smmel} 101296936Smmel 102296936Smmelint as3722_read_buf(struct as3722_softc *sc, uint8_t reg, uint8_t *buf, 103296936Smmel size_t size) 104296936Smmel{ 105296936Smmel uint8_t addr; 106296936Smmel int rv; 107296936Smmel struct iic_msg msgs[2] = { 108296936Smmel {0, IIC_M_WR, 1, &addr}, 109296936Smmel {0, IIC_M_RD, size, buf}, 110296936Smmel }; 111296936Smmel 112296936Smmel msgs[0].slave = sc->bus_addr; 113296936Smmel msgs[1].slave = sc->bus_addr; 114296936Smmel addr = reg; 115296936Smmel 116296936Smmel rv = iicbus_transfer(sc->dev, msgs, 2); 117296936Smmel if (rv != 0) { 118296936Smmel device_printf(sc->dev, 119296936Smmel "Error when reading reg 0x%02X, rv: %d\n", reg, rv); 120296936Smmel return (EIO); 121296936Smmel } 122296936Smmel 123296936Smmel return (0); 124296936Smmel} 125296936Smmel 126296936Smmelint 127296936Smmelas3722_write(struct as3722_softc *sc, uint8_t reg, uint8_t val) 128296936Smmel{ 129296936Smmel uint8_t data[2]; 130296936Smmel int rv; 131296936Smmel 132296936Smmel struct iic_msg msgs[1] = { 133296936Smmel {0, IIC_M_WR, 2, data}, 134296936Smmel }; 135296936Smmel 136296936Smmel msgs[0].slave = sc->bus_addr; 137296936Smmel data[0] = reg; 138296936Smmel data[1] = val; 139296936Smmel 140296936Smmel rv = iicbus_transfer(sc->dev, msgs, 1); 141296936Smmel if (rv != 0) { 142296936Smmel device_printf(sc->dev, 143296936Smmel "Error when writing reg 0x%02X, rv: %d\n", reg, rv); 144296936Smmel return (EIO); 145296936Smmel } 146296936Smmel return (0); 147296936Smmel} 148296936Smmel 149296936Smmelint as3722_write_buf(struct as3722_softc *sc, uint8_t reg, uint8_t *buf, 150296936Smmel size_t size) 151296936Smmel{ 152296936Smmel uint8_t data[1]; 153296936Smmel int rv; 154296936Smmel struct iic_msg msgs[2] = { 155296936Smmel {0, IIC_M_WR, 1, data}, 156296936Smmel {0, IIC_M_WR | IIC_M_NOSTART, size, buf}, 157296936Smmel }; 158296936Smmel 159296936Smmel msgs[0].slave = sc->bus_addr; 160296936Smmel msgs[1].slave = sc->bus_addr; 161296936Smmel data[0] = reg; 162296936Smmel 163296936Smmel rv = iicbus_transfer(sc->dev, msgs, 2); 164296936Smmel if (rv != 0) { 165296936Smmel device_printf(sc->dev, 166296936Smmel "Error when writing reg 0x%02X, rv: %d\n", reg, rv); 167296936Smmel return (EIO); 168296936Smmel } 169296936Smmel return (0); 170296936Smmel} 171296936Smmel 172296936Smmelint 173296936Smmelas3722_modify(struct as3722_softc *sc, uint8_t reg, uint8_t clear, uint8_t set) 174296936Smmel{ 175296936Smmel uint8_t val; 176296936Smmel int rv; 177296936Smmel 178296936Smmel rv = as3722_read(sc, reg, &val); 179296936Smmel if (rv != 0) 180296936Smmel return (rv); 181296936Smmel 182296936Smmel val &= ~clear; 183296936Smmel val |= set; 184296936Smmel 185296936Smmel rv = as3722_write(sc, reg, val); 186296936Smmel if (rv != 0) 187296936Smmel return (rv); 188296936Smmel 189296936Smmel return (0); 190296936Smmel} 191296936Smmel 192296936Smmelstatic int 193296936Smmelas3722_get_version(struct as3722_softc *sc) 194296936Smmel{ 195296936Smmel uint8_t reg; 196296936Smmel int rv; 197296936Smmel 198296936Smmel /* Verify AS3722 ID and version. */ 199296936Smmel rv = RD1(sc, AS3722_ASIC_ID1, ®); 200296936Smmel if (rv != 0) 201296936Smmel return (ENXIO); 202296936Smmel 203296936Smmel if (reg != AS3722_DEVICE_ID) { 204296936Smmel device_printf(sc->dev, "Invalid chip ID is 0x%x\n", reg); 205296936Smmel return (ENXIO); 206296936Smmel } 207296936Smmel 208296936Smmel rv = RD1(sc, AS3722_ASIC_ID2, &sc->chip_rev); 209296936Smmel if (rv != 0) 210296936Smmel return (ENXIO); 211296936Smmel 212296936Smmel if (bootverbose) 213296936Smmel device_printf(sc->dev, "AS3722 rev: 0x%x\n", sc->chip_rev); 214296936Smmel return (0); 215296936Smmel} 216296936Smmel 217296936Smmelstatic int 218296936Smmelas3722_init(struct as3722_softc *sc) 219296936Smmel{ 220296936Smmel uint32_t reg; 221296936Smmel int rv; 222296936Smmel 223296936Smmel reg = 0; 224296936Smmel if (sc->int_pullup) 225296936Smmel reg |= AS3722_INT_PULL_UP; 226296936Smmel if (sc->i2c_pullup) 227296936Smmel reg |= AS3722_I2C_PULL_UP; 228296936Smmel 229296936Smmel rv = RM1(sc, AS3722_IO_VOLTAGE, 230296936Smmel AS3722_INT_PULL_UP | AS3722_I2C_PULL_UP, reg); 231296936Smmel if (rv != 0) 232296936Smmel return (ENXIO); 233296936Smmel 234296936Smmel /* mask interrupts */ 235296936Smmel rv = WR1(sc, AS3722_INTERRUPT_MASK1, 0); 236296936Smmel if (rv != 0) 237296936Smmel return (ENXIO); 238296936Smmel rv = WR1(sc, AS3722_INTERRUPT_MASK2, 0); 239296936Smmel if (rv != 0) 240296936Smmel return (ENXIO); 241296936Smmel rv = WR1(sc, AS3722_INTERRUPT_MASK3, 0); 242296936Smmel if (rv != 0) 243296936Smmel return (ENXIO); 244296936Smmel rv = WR1(sc, AS3722_INTERRUPT_MASK4, 0); 245296936Smmel if (rv != 0) 246296936Smmel return (ENXIO); 247296936Smmel return (0); 248296936Smmel} 249296936Smmel 250296936Smmelstatic int 251296936Smmelas3722_parse_fdt(struct as3722_softc *sc, phandle_t node) 252296936Smmel{ 253296936Smmel 254296936Smmel sc->int_pullup = 255296936Smmel OF_hasprop(node, "ams,enable-internal-int-pullup") ? 1 : 0; 256296936Smmel sc->i2c_pullup = 257296936Smmel OF_hasprop(node, "ams,enable-internal-i2c-pullup") ? 1 : 0; 258296936Smmel return 0; 259296936Smmel} 260296936Smmel 261296936Smmelstatic void 262296936Smmelas3722_intr(void *arg) 263296936Smmel{ 264296936Smmel struct as3722_softc *sc; 265296936Smmel 266296936Smmel sc = (struct as3722_softc *)arg; 267296936Smmel /* XXX Finish temperature alarms. */ 268296936Smmel} 269296936Smmel 270296936Smmelstatic int 271296936Smmelas3722_probe(device_t dev) 272296936Smmel{ 273296936Smmel 274296936Smmel if (!ofw_bus_status_okay(dev)) 275296936Smmel return (ENXIO); 276296936Smmel 277296936Smmel if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data) 278296936Smmel return (ENXIO); 279296936Smmel 280296936Smmel device_set_desc(dev, "AS3722 PMIC"); 281296936Smmel return (BUS_PROBE_DEFAULT); 282296936Smmel} 283296936Smmel 284296936Smmelstatic int 285296936Smmelas3722_attach(device_t dev) 286296936Smmel{ 287296936Smmel struct as3722_softc *sc; 288296936Smmel const char *dname; 289296936Smmel int dunit, rv, rid; 290296936Smmel phandle_t node; 291296936Smmel 292296936Smmel sc = device_get_softc(dev); 293296936Smmel sc->dev = dev; 294296936Smmel sc->bus_addr = iicbus_get_addr(dev); 295296936Smmel node = ofw_bus_get_node(sc->dev); 296296936Smmel dname = device_get_name(dev); 297296936Smmel dunit = device_get_unit(dev); 298296936Smmel rv = 0; 299296936Smmel LOCK_INIT(sc); 300296936Smmel 301296936Smmel rid = 0; 302296936Smmel sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, 303296936Smmel RF_ACTIVE); 304296936Smmel if (sc->irq_res == NULL) { 305296936Smmel device_printf(dev, "Cannot allocate interrupt.\n"); 306296936Smmel rv = ENXIO; 307296936Smmel goto fail; 308296936Smmel } 309296936Smmel 310296936Smmel rv = as3722_parse_fdt(sc, node); 311296936Smmel if (rv != 0) 312296936Smmel goto fail; 313296936Smmel rv = as3722_get_version(sc); 314296936Smmel if (rv != 0) 315296936Smmel goto fail; 316296936Smmel rv = as3722_init(sc); 317296936Smmel if (rv != 0) 318296936Smmel goto fail; 319296936Smmel rv = as3722_regulator_attach(sc, node); 320296936Smmel if (rv != 0) 321296936Smmel goto fail; 322296936Smmel rv = as3722_gpio_attach(sc, node); 323296936Smmel if (rv != 0) 324296936Smmel goto fail; 325296936Smmel rv = as3722_rtc_attach(sc, node); 326296936Smmel if (rv != 0) 327296936Smmel goto fail; 328296936Smmel 329296936Smmel fdt_pinctrl_register(dev, NULL); 330296936Smmel fdt_pinctrl_configure_by_name(dev, "default"); 331296936Smmel 332296936Smmel /* Setup interrupt. */ 333296936Smmel rv = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC | INTR_MPSAFE, 334296936Smmel NULL, as3722_intr, sc, &sc->irq_h); 335296936Smmel if (rv) { 336296936Smmel device_printf(dev, "Cannot setup interrupt.\n"); 337296936Smmel goto fail; 338296936Smmel } 339296936Smmel return (bus_generic_attach(dev)); 340296936Smmel 341296936Smmelfail: 342296936Smmel if (sc->irq_h != NULL) 343296936Smmel bus_teardown_intr(dev, sc->irq_res, sc->irq_h); 344296936Smmel if (sc->irq_res != NULL) 345296936Smmel bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res); 346296936Smmel LOCK_DESTROY(sc); 347296936Smmel return (rv); 348296936Smmel} 349296936Smmel 350296936Smmelstatic int 351296936Smmelas3722_detach(device_t dev) 352296936Smmel{ 353296936Smmel struct as3722_softc *sc; 354296936Smmel 355296936Smmel sc = device_get_softc(dev); 356296936Smmel if (sc->irq_h != NULL) 357296936Smmel bus_teardown_intr(dev, sc->irq_res, sc->irq_h); 358296936Smmel if (sc->irq_res != NULL) 359296936Smmel bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res); 360296936Smmel LOCK_DESTROY(sc); 361296936Smmel 362296936Smmel return (bus_generic_detach(dev)); 363296936Smmel} 364296936Smmel 365296936Smmelstatic phandle_t 366296936Smmelas3722_gpio_get_node(device_t bus, device_t dev) 367296936Smmel{ 368296936Smmel 369296936Smmel /* We only have one child, the GPIO bus, which needs our own node. */ 370296936Smmel return (ofw_bus_get_node(bus)); 371296936Smmel} 372296936Smmel 373296936Smmelstatic device_method_t as3722_methods[] = { 374296936Smmel /* Device interface */ 375296936Smmel DEVMETHOD(device_probe, as3722_probe), 376296936Smmel DEVMETHOD(device_attach, as3722_attach), 377296936Smmel DEVMETHOD(device_detach, as3722_detach), 378296936Smmel 379296936Smmel /* Regdev interface */ 380296936Smmel DEVMETHOD(regdev_map, as3722_regulator_map), 381296936Smmel 382296936Smmel /* RTC interface */ 383296936Smmel DEVMETHOD(clock_gettime, as3722_rtc_gettime), 384296936Smmel DEVMETHOD(clock_settime, as3722_rtc_settime), 385296936Smmel 386296936Smmel /* GPIO protocol interface */ 387296936Smmel DEVMETHOD(gpio_get_bus, as3722_gpio_get_bus), 388296936Smmel DEVMETHOD(gpio_pin_max, as3722_gpio_pin_max), 389296936Smmel DEVMETHOD(gpio_pin_getname, as3722_gpio_pin_getname), 390296936Smmel DEVMETHOD(gpio_pin_getflags, as3722_gpio_pin_getflags), 391296936Smmel DEVMETHOD(gpio_pin_getcaps, as3722_gpio_pin_getcaps), 392296936Smmel DEVMETHOD(gpio_pin_setflags, as3722_gpio_pin_setflags), 393296936Smmel DEVMETHOD(gpio_pin_get, as3722_gpio_pin_get), 394296936Smmel DEVMETHOD(gpio_pin_set, as3722_gpio_pin_set), 395296936Smmel DEVMETHOD(gpio_pin_toggle, as3722_gpio_pin_toggle), 396296936Smmel DEVMETHOD(gpio_map_gpios, as3722_gpio_map_gpios), 397296936Smmel 398296936Smmel /* fdt_pinctrl interface */ 399296936Smmel DEVMETHOD(fdt_pinctrl_configure, as3722_pinmux_configure), 400296936Smmel 401296936Smmel /* ofw_bus interface */ 402296936Smmel DEVMETHOD(ofw_bus_get_node, as3722_gpio_get_node), 403296936Smmel 404296936Smmel DEVMETHOD_END 405296936Smmel}; 406296936Smmel 407296936Smmelstatic devclass_t as3722_devclass; 408308335Smmelstatic DEFINE_CLASS_0(gpio, as3722_driver, as3722_methods, 409296936Smmel sizeof(struct as3722_softc)); 410296936SmmelEARLY_DRIVER_MODULE(as3722, iicbus, as3722_driver, as3722_devclass, 411308335Smmel NULL, NULL, 74); 412