Path: blob/main/sys/arm64/nvidia/tegra210/max77620.c
48266 views
/*-1* Copyright (c) 2019 Michal Meloun <[email protected]>2*3* Redistribution and use in source and binary forms, with or without4* modification, are permitted provided that the following conditions5* are met:6* 1. Redistributions of source code must retain the above copyright7* notice, this list of conditions and the following disclaimer.8* 2. Redistributions in binary form must reproduce the above copyright9* notice, this list of conditions and the following disclaimer in the10* documentation and/or other materials provided with the distribution.11*12* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND13* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE14* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE15* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE16* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL17* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS18* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)19* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT20* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY21* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF22* SUCH DAMAGE.23*/2425#include <sys/cdefs.h>26/*27* MAX77620 PMIC driver28*/2930#include <sys/param.h>31#include <sys/systm.h>32#include <sys/bus.h>33#include <sys/gpio.h>34#include <sys/kernel.h>35#include <sys/module.h>36#include <sys/malloc.h>37#include <sys/rman.h>38#include <sys/sx.h>3940#include <machine/bus.h>4142#include <dev/regulator/regulator.h>43#include <dev/fdt/fdt_pinctrl.h>44#include <dev/gpio/gpiobusvar.h>45#include <dev/iicbus/iiconf.h>46#include <dev/iicbus/iicbus.h>47#include <dev/ofw/ofw_bus.h>48#include <dev/ofw/ofw_bus_subr.h>4950#include <dt-bindings/mfd/max77620.h>5152#include "clock_if.h"53#include "regdev_if.h"5455#include "max77620.h"5657static struct ofw_compat_data compat_data[] = {58{"maxim,max77620", 1},59{NULL, 0},60};6162#define LOCK(_sc) sx_xlock(&(_sc)->lock)63#define UNLOCK(_sc) sx_xunlock(&(_sc)->lock)64#define LOCK_INIT(_sc) sx_init(&(_sc)->lock, "max77620")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);6869#define MAX77620_DEVICE_ID 0x0C7071/*72* Raw register access function.73*/74int75max77620_read(struct max77620_softc *sc, uint8_t reg, uint8_t *val)76{77uint8_t addr;78int rv;79struct iic_msg msgs[2] = {80{0, IIC_M_WR, 1, &addr},81{0, IIC_M_RD, 1, val},82};8384msgs[0].slave = sc->bus_addr;85msgs[1].slave = sc->bus_addr;86addr = reg;8788rv = iicbus_transfer(sc->dev, msgs, 2);89if (rv != 0) {90device_printf(sc->dev,91"Error when reading reg 0x%02X, rv: %d\n", reg, rv);92return (EIO);93}9495return (0);96}9798int max77620_read_buf(struct max77620_softc *sc, uint8_t reg, uint8_t *buf,99size_t size)100{101uint8_t addr;102int rv;103struct iic_msg msgs[2] = {104{0, IIC_M_WR, 1, &addr},105{0, IIC_M_RD, size, buf},106};107108msgs[0].slave = sc->bus_addr;109msgs[1].slave = sc->bus_addr;110addr = reg;111112rv = iicbus_transfer(sc->dev, msgs, 2);113if (rv != 0) {114device_printf(sc->dev,115"Error when reading reg 0x%02X, rv: %d\n", reg, rv);116return (EIO);117}118119return (0);120}121122int123max77620_write(struct max77620_softc *sc, uint8_t reg, uint8_t val)124{125uint8_t data[2];126int rv;127128struct iic_msg msgs[1] = {129{0, IIC_M_WR, 2, data},130};131132msgs[0].slave = sc->bus_addr;133data[0] = reg;134data[1] = val;135136rv = iicbus_transfer(sc->dev, msgs, 1);137if (rv != 0) {138device_printf(sc->dev,139"Error when writing reg 0x%02X, rv: %d\n", reg, rv);140return (EIO);141}142return (0);143}144145int146max77620_write_buf(struct max77620_softc *sc, uint8_t reg, uint8_t *buf,147size_t size)148{149uint8_t data[1];150int rv;151struct iic_msg msgs[2] = {152{0, IIC_M_WR, 1, data},153{0, IIC_M_WR | IIC_M_NOSTART, size, buf},154};155156msgs[0].slave = sc->bus_addr;157msgs[1].slave = sc->bus_addr;158data[0] = reg;159160rv = iicbus_transfer(sc->dev, msgs, 2);161if (rv != 0) {162device_printf(sc->dev,163"Error when writing reg 0x%02X, rv: %d\n", reg, rv);164return (EIO);165}166return (0);167}168169int170max77620_modify(struct max77620_softc *sc, uint8_t reg, uint8_t clear,171uint8_t set)172{173uint8_t val;174int rv;175176rv = max77620_read(sc, reg, &val);177if (rv != 0)178return (rv);179180val &= ~clear;181val |= set;182183rv = max77620_write(sc, reg, val);184if (rv != 0)185return (rv);186187return (0);188}189190static int191max77620_parse_fps(struct max77620_softc *sc, int id, phandle_t node)192{193int val;194195if (OF_getencprop(node, "maxim,shutdown-fps-time-period-us", &val,196sizeof(val)) >= 0) {197val = min(val, MAX77620_FPS_PERIOD_MAX_US);198val = max(val, MAX77620_FPS_PERIOD_MIN_US);199sc->shutdown_fps[id] = val;200}201if (OF_getencprop(node, "maxim,suspend-fps-time-period-us", &val,202sizeof(val)) >= 0) {203val = min(val, MAX77620_FPS_PERIOD_MAX_US);204val = max(val, MAX77620_FPS_PERIOD_MIN_US);205sc->suspend_fps[id] = val;206}207if (OF_getencprop(node, "maxim,fps-event-source", &val,208sizeof(val)) >= 0) {209if (val > 2) {210device_printf(sc->dev, "Invalid 'fps-event-source' "211"value: %d\n", val);212return (EINVAL);213}214sc->event_source[id] = val;215}216return (0);217}218219static int220max77620_parse_fdt(struct max77620_softc *sc, phandle_t node)221{222phandle_t fpsnode;223char fps_name[6];224int i, rv;225226for (i = 0; i < MAX77620_FPS_COUNT; i++) {227sc->shutdown_fps[i] = -1;228sc->suspend_fps[i] = -1;229sc->event_source[i] = -1;230}231232fpsnode = ofw_bus_find_child(node, "fps");233if (fpsnode > 0) {234for (i = 0; i < MAX77620_FPS_COUNT; i++) {235sprintf(fps_name, "fps%d", i);236node = ofw_bus_find_child(node, fps_name);237if (node <= 0)238continue;239rv = max77620_parse_fps(sc, i, node);240if (rv != 0)241return (rv);242}243}244return (0);245}246247static int248max77620_get_version(struct max77620_softc *sc)249{250uint8_t buf[6];251int i;252int rv;253254/* Verify ID string (5 bytes ). */255for (i = 0; i <= 6; i++) {256rv = RD1(sc, MAX77620_REG_CID0 + i , buf + i);257if (rv != 0) {258device_printf(sc->dev, "Cannot read chip ID: %d\n", rv);259return (ENXIO);260}261}262if (bootverbose) {263device_printf(sc->dev,264" ID: [0x%02X, 0x%02X, 0x%02X, 0x%02X]\n",265buf[0], buf[1], buf[2], buf[3]);266}267device_printf(sc->dev, " MAX77620 version - OTP: 0x%02X, ES: 0x%02X\n",268buf[4], buf[5]);269270return (0);271}272273static uint8_t274max77620_encode_fps_period(struct max77620_softc *sc, int val)275{276uint8_t i;277int period;278279period = MAX77620_FPS_PERIOD_MIN_US;280for (i = 0; i < 7; i++) {281if (period >= val)282return (i);283period *= 2;284}285return (i);286}287288static int289max77620_init(struct max77620_softc *sc)290{291uint8_t mask, val, tmp;292int i, rv;293294mask = 0;295val = 0;296for (i = 0; i < MAX77620_FPS_COUNT; i++) {297if (sc->shutdown_fps[i] != -1) {298mask |= MAX77620_FPS_TIME_PERIOD_MASK;299tmp = max77620_encode_fps_period(sc,300sc->shutdown_fps[i]);301val |= (tmp << MAX77620_FPS_TIME_PERIOD_SHIFT) &302MAX77620_FPS_TIME_PERIOD_MASK;303}304305if (sc->event_source[i] != -1) {306mask |= MAX77620_FPS_EN_SRC_MASK;307tmp = sc->event_source[i];308val |= (tmp << MAX77620_FPS_EN_SRC_SHIFT) &309MAX77620_FPS_EN_SRC_MASK;310if (sc->event_source[i] == 2) {311mask |= MAX77620_FPS_ENFPS_SW_MASK;312val |= MAX77620_FPS_ENFPS_SW;313}314315}316rv = RM1(sc, MAX77620_REG_FPS_CFG0 + i, mask, val);317if (rv != 0) {318device_printf(sc->dev, "I/O error: %d\n", rv);319return (ENXIO);320}321}322323/* Global mask interrupts */324rv = RM1(sc, MAX77620_REG_INTENLBT, 0x81, 0x81);325rv = RM1(sc, MAX77620_REG_IRQTOPM, 0x81, 0x81);326if (rv != 0)327return (ENXIO);328return (0);329}330#ifdef notyet331static void332max77620_intr(void *arg)333{334struct max77620_softc *sc;335uint8_t intenlbt, intlbt, irqtop, irqtopm, irqsd, irqmasksd;336uint8_t irq_lvl2_l0_7, irq_lvl2_l8, irq_lvl2_gpio, irq_msk_l0_7, irq_msk_l8;337uint8_t onoffirq, onoffirqm;338339sc = (struct max77620_softc *)arg;340/* XXX Finish temperature alarms. */341RD1(sc, MAX77620_REG_INTENLBT, &intenlbt);342RD1(sc, MAX77620_REG_INTLBT, &intlbt);343344RD1(sc, MAX77620_REG_IRQTOP, &irqtop);345RD1(sc, MAX77620_REG_IRQTOPM, &irqtopm);346RD1(sc, MAX77620_REG_IRQSD, &irqsd);347RD1(sc, MAX77620_REG_IRQMASKSD, &irqmasksd);348RD1(sc, MAX77620_REG_IRQ_LVL2_L0_7, &irq_lvl2_l0_7);349RD1(sc, MAX77620_REG_IRQ_MSK_L0_7, &irq_msk_l0_7);350RD1(sc, MAX77620_REG_IRQ_LVL2_L8, &irq_lvl2_l8);351RD1(sc, MAX77620_REG_IRQ_MSK_L8, &irq_msk_l8);352RD1(sc, MAX77620_REG_IRQ_LVL2_GPIO, &irq_lvl2_gpio);353RD1(sc, MAX77620_REG_ONOFFIRQ, &onoffirq);354RD1(sc, MAX77620_REG_ONOFFIRQM, &onoffirqm);355printf("%s: intlbt: 0x%02X, intenlbt: 0x%02X\n", __func__, intlbt, intenlbt);356printf("%s: irqtop: 0x%02X, irqtopm: 0x%02X\n", __func__, irqtop, irqtopm);357printf("%s: irqsd: 0x%02X, irqmasksd: 0x%02X\n", __func__, irqsd, irqmasksd);358printf("%s: onoffirq: 0x%02X, onoffirqm: 0x%02X\n", __func__, onoffirq, onoffirqm);359printf("%s: irq_lvl2_l0_7: 0x%02X, irq_msk_l0_7: 0x%02X\n", __func__, irq_lvl2_l0_7, irq_msk_l0_7);360printf("%s: irq_lvl2_l8: 0x%02X, irq_msk_l8: 0x%02X\n", __func__, irq_lvl2_l8, irq_msk_l8);361printf("%s: irq_lvl2_gpio: 0x%02X\n", __func__, irq_lvl2_gpio);362}363#endif364365static int366max77620_probe(device_t dev)367{368369if (!ofw_bus_status_okay(dev))370return (ENXIO);371372if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data)373return (ENXIO);374375device_set_desc(dev, "MAX77620 PMIC");376return (BUS_PROBE_DEFAULT);377}378379static int380max77620_attach(device_t dev)381{382struct max77620_softc *sc;383int rv, rid;384phandle_t node;385386sc = device_get_softc(dev);387sc->dev = dev;388sc->bus_addr = iicbus_get_addr(dev);389node = ofw_bus_get_node(sc->dev);390rv = 0;391LOCK_INIT(sc);392393rid = 0;394sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,395RF_ACTIVE);396#ifdef notyet /* Interrupt parent is not implemented */397if (sc->irq_res == NULL) {398device_printf(dev, "Cannot allocate interrupt.\n");399rv = ENXIO;400goto fail;401}402#endif403rv = max77620_parse_fdt(sc, node);404if (rv != 0)405goto fail;406407rv = max77620_get_version(sc);408if (rv != 0)409goto fail;410411rv = max77620_init(sc);412if (rv != 0)413goto fail;414rv = max77620_regulator_attach(sc, node);415if (rv != 0)416goto fail;417rv = max77620_gpio_attach(sc, node);418if (rv != 0)419goto fail;420421rv = max77620_rtc_create(sc, node);422if (rv != 0)423goto fail;424425fdt_pinctrl_register(dev, NULL);426fdt_pinctrl_configure_by_name(dev, "default");427428/* Setup interrupt. */429#ifdef notyet430rv = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC | INTR_MPSAFE,431NULL, max77620_intr, sc, &sc->irq_h);432if (rv) {433device_printf(dev, "Cannot setup interrupt.\n");434goto fail;435}436#endif437bus_attach_children(dev);438return (0);439440fail:441if (sc->irq_h != NULL)442bus_teardown_intr(dev, sc->irq_res, sc->irq_h);443if (sc->irq_res != NULL)444bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res);445LOCK_DESTROY(sc);446return (rv);447}448449static int450max77620_detach(device_t dev)451{452struct max77620_softc *sc;453int error;454455error = bus_generic_detach(dev);456if (error != 0)457return (error);458459sc = device_get_softc(dev);460if (sc->irq_h != NULL)461bus_teardown_intr(dev, sc->irq_res, sc->irq_h);462if (sc->irq_res != NULL)463bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res);464LOCK_DESTROY(sc);465466return (0);467}468469static phandle_t470max77620_gpio_get_node(device_t bus, device_t dev)471{472473/* We only have one child, the GPIO bus, which needs our own node. */474return (ofw_bus_get_node(bus));475}476477static device_method_t max77620_methods[] = {478/* Device interface */479DEVMETHOD(device_probe, max77620_probe),480DEVMETHOD(device_attach, max77620_attach),481DEVMETHOD(device_detach, max77620_detach),482483/* Regdev interface */484DEVMETHOD(regdev_map, max77620_regulator_map),485486/* GPIO protocol interface */487DEVMETHOD(gpio_get_bus, max77620_gpio_get_bus),488DEVMETHOD(gpio_pin_max, max77620_gpio_pin_max),489DEVMETHOD(gpio_pin_getname, max77620_gpio_pin_getname),490DEVMETHOD(gpio_pin_getflags, max77620_gpio_pin_getflags),491DEVMETHOD(gpio_pin_getcaps, max77620_gpio_pin_getcaps),492DEVMETHOD(gpio_pin_setflags, max77620_gpio_pin_setflags),493DEVMETHOD(gpio_pin_get, max77620_gpio_pin_get),494DEVMETHOD(gpio_pin_set, max77620_gpio_pin_set),495DEVMETHOD(gpio_pin_toggle, max77620_gpio_pin_toggle),496DEVMETHOD(gpio_map_gpios, max77620_gpio_map_gpios),497498/* fdt_pinctrl interface */499DEVMETHOD(fdt_pinctrl_configure, max77620_pinmux_configure),500501/* ofw_bus interface */502DEVMETHOD(ofw_bus_get_node, max77620_gpio_get_node),503504DEVMETHOD_END505};506507static DEFINE_CLASS_0(gpio, max77620_driver, max77620_methods,508sizeof(struct max77620_softc));509EARLY_DRIVER_MODULE(max77620, iicbus, max77620_driver, NULL, NULL, 74);510511512