/* $NetBSD: tps65217pmic.c,v 1.20 2021/08/07 16:19:11 thorpej Exp $ */ /*- * Copyright (c) 2013 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Radoslaw Kujawa. * * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. */ /* * Texas Instruments TPS65217 Power Management IC driver. * TODO: battery, sequencer, pgood */ #include "opt_fdt.h" #include __KERNEL_RCSID(0, "$NetBSD: tps65217pmic.c,v 1.20 2021/08/07 16:19:11 thorpej Exp $"); #include #include #include #include #include #include #include #include #include #include #include #ifdef FDT #include #endif #define NTPS_REG 7 #define SNUM_REGS NTPS_REG-1 #define SNUM_USBSTATUS NTPS_REG #define SNUM_ACSTATUS NTPS_REG+1 struct tps_reg_param; struct tps65217pmic_softc { device_t sc_dev; i2c_tag_t sc_tag; i2c_addr_t sc_addr; int sc_phandle; uint8_t sc_version; uint8_t sc_revision; kmutex_t sc_lock; bool sc_acstatus; bool sc_usbstatus; bool sc_acenabled; bool sc_usbenabled; callout_t sc_powerpollco; /* sysmon(4) stuff */ struct sysmon_envsys *sc_sme; envsys_data_t sc_regsensor[NTPS_REG]; envsys_data_t sc_acsensor; envsys_data_t sc_usbsensor; struct sysmon_pswitch sc_smpsw; }; struct tps65217reg_softc { device_t sc_dev; int sc_phandle; struct tps_reg_param *sc_param; }; struct tps65217reg_attach_args { struct tps_reg_param *reg_param; int reg_phandle; }; /* Voltage regulators */ enum tps_reg_num { TPS65217PMIC_LDO1, TPS65217PMIC_LDO2, TPS65217PMIC_LDO3LS, TPS65217PMIC_LDO4LS, TPS65217PMIC_DCDC1, TPS65217PMIC_DCDC2, TPS65217PMIC_DCDC3 }; struct tps_reg_param { /* parameters configured statically */ const char* name; uint16_t voltage_min; /* in mV */ uint16_t voltage_max; /* in mV */ const uint16_t *voltages; /* all possible voltage settings */ uint8_t nvoltages; /* number of voltage settings */ bool can_track; /* regulator can track U of other r. */ struct tps_reg_param *tracked_reg; /* ptr to tracked regulator */ bool can_xadj; /* voltage can be adjusted externally */ bool can_ls; /* can be a load switch instead of r. */ uint8_t defreg_num; /* DEF register */ uint8_t enable_bit; /* position in ENABLE register */ /* * Run-time parameters configured during attachment and later, these * probably should be split into separate struct that would be a part * of softc. But since we can have only one TPS chip, that should be * okay for now. */ bool is_enabled; /* regulator is enabled */ bool is_pg; /* regulator is "power good" */ bool is_tracking; /* voltage is tracking other reg. */ bool is_ls; /* is a load switch */ bool is_xadj; /* voltage is adjusted externally */ uint16_t current_voltage; /* in mV */ }; static int tps65217pmic_match(device_t, cfdata_t, void *); static void tps65217pmic_attach(device_t, device_t, void *); static int tps65217pmic_i2c_lock(struct tps65217pmic_softc *); static void tps65217pmic_i2c_unlock(struct tps65217pmic_softc *); static uint8_t tps65217pmic_reg_read(struct tps65217pmic_softc *, uint8_t); static void tps65217pmic_reg_write(struct tps65217pmic_softc *, uint8_t, uint8_t); static void tps65217pmic_reg_refresh(struct tps65217pmic_softc *); static uint16_t tps65217pmic_ppath_max_usb_current(uint8_t); static uint16_t tps65217pmic_ppath_max_ac_current(uint8_t); static void tps65217pmic_regulator_read_config(struct tps65217pmic_softc *, struct tps_reg_param *); static void tps65217pmic_print_ppath(struct tps65217pmic_softc *); static void tps65217pmic_print_ldos(struct tps65217pmic_softc *); static void tps65217pmic_version(struct tps65217pmic_softc *); static void tps65217pmic_envsys_register(struct tps65217pmic_softc *); static void tps65217pmic_envsys_refresh(struct sysmon_envsys *, envsys_data_t *); static void tps65217pmic_power_monitor_init(struct tps65217pmic_softc *); static void tps65217pmic_power_monitor(void *); static void tps65217pmic_wled_init(struct tps65217pmic_softc *, int, int, int); CFATTACH_DECL_NEW(tps65217pmic, sizeof (struct tps65217pmic_softc), tps65217pmic_match, tps65217pmic_attach, NULL, NULL); #ifdef FDT static void tps65217pmic_regulator_attach(struct tps65217pmic_softc *); #endif /* Possible settings of LDO1 in mV. */ static const uint16_t ldo1voltages[] = { 1000, 1100, 1200, 1250, 1300, 1350, 1400, 1500, 1600, 1800, 2500, 2750, 2800, 3000, 3100, 3300 }; /* Possible settings of LDO2, DCDC1, DCDC2, DCDC3 in mV. */ static const uint16_t ldo2voltages[] = { 900, 925, 950, 975, 1000, 1025, 1050, 1075, 1100, 1125, 1150, 1175, 1200, 1225, 1250, 1275, 1300, 1325, 1350, 1375, 1400, 1425, 1450, 1475, 1500, 1550, 1600, 1650, 1700, 1750, 1800, 1850, 1900, 1950, 2000, 2050, 2100, 2150, 2200, 2250, 2300, 2350, 2400, 2450, 2500, 2550, 2600, 2650, 2700, 2750, 2800, 2850, 2900, 3000, 3100, 3200, 3300, 3300, 3300, 3300, 3300, 3300, 3300, 3300 }; /* Possible settings of LDO3, LDO4 in mV. */ static const uint16_t ldo3voltages[] = { 1500, 1550, 1600, 1650, 1700, 1750, 1800, 1850, 1900, 2000, 2100, 2200, 2300, 2400, 2450, 2500, 2550, 2600, 2650, 2700, 2750, 2800, 2850, 2900,2950, 3000, 3050, 3100, 3150, 3200, 3250, 3300 }; static struct tps_reg_param tps_regulators[] = { { .name = "ldo1", .voltage_min = 1000, .voltage_max = 3300, .voltages = ldo1voltages, .nvoltages = 16, .can_track = false, .tracked_reg = NULL, .can_xadj = false, .can_ls = false, .defreg_num = TPS65217PMIC_DEFLDO1, .enable_bit = TPS65217PMIC_ENABLE_LDO1 }, { .name = "ldo2", .voltage_min = 900, .voltage_max = 3300, .voltages = ldo2voltages, .nvoltages = 64, .can_track = true, .tracked_reg = &(tps_regulators[TPS65217PMIC_DCDC3]), .can_xadj = false, .can_ls = false, .defreg_num = TPS65217PMIC_DEFLDO2, .enable_bit = TPS65217PMIC_ENABLE_LDO2 }, { .name = "ldo3", .voltage_min = 1500, .voltage_max = 3300, .voltages = ldo3voltages, .nvoltages = 32, .can_track = false, .tracked_reg = NULL, .can_xadj = false, .can_ls = true, .defreg_num = TPS65217PMIC_DEFLDO3, .enable_bit = TPS65217PMIC_ENABLE_LDO3 }, { .name = "ldo4", .voltage_min = 1500, .voltage_max = 3300, .voltages = ldo3voltages, .nvoltages = 32, .can_track = false, .tracked_reg = NULL, .can_xadj = false, .can_ls = true, .defreg_num = TPS65217PMIC_DEFLDO4, .enable_bit = TPS65217PMIC_ENABLE_LDO4 }, { .name = "dcdc1", .voltage_min = 900, .voltage_max = 3300, .voltages = ldo2voltages, .nvoltages = 64, .can_track = false, .tracked_reg = NULL, .can_xadj = true, .can_ls = false, .defreg_num = TPS65217PMIC_DEFDCDC1, .enable_bit = TPS65217PMIC_ENABLE_DCDC1 }, { .name = "dcdc2", .voltage_min = 900, .voltage_max = 3300, .voltages = ldo2voltages, .nvoltages = 64, .can_track = false, .tracked_reg = NULL, .can_xadj = true, .can_ls = false, .defreg_num = TPS65217PMIC_DEFDCDC2, .enable_bit = TPS65217PMIC_ENABLE_DCDC2 }, { .name = "dcdc3", .voltage_min = 900, .voltage_max = 3300, .voltages = ldo2voltages, .nvoltages = 64, .can_track = false, .tracked_reg = NULL, .can_xadj = true, .can_ls = false, .defreg_num = TPS65217PMIC_DEFDCDC3, .enable_bit = TPS65217PMIC_ENABLE_DCDC3 } }; static bool matched = false; static const struct device_compatible_entry compat_data[] = { { .compat = "ti,tps65217" }, DEVICE_COMPAT_EOL }; static int tps65217pmic_match(device_t parent, cfdata_t cf, void *aux) { struct i2c_attach_args *ia = aux; int match_result; if (iic_use_direct_match(ia, cf, compat_data, &match_result)) return match_result; if (ia->ia_addr == TPS65217PMIC_ADDR) { /* we can only have one */ if (matched) return 0; return I2C_MATCH_ADDRESS_ONLY; } return 0; } static void tps65217pmic_attach(device_t parent, device_t self, void *aux) { struct tps65217pmic_softc *sc = device_private(self); struct i2c_attach_args *ia = aux; prop_dictionary_t dict; int isel, fdim, brightness; /* XXXJRT But what if you have multiple i2c busses? */ matched = true; sc->sc_dev = self; sc->sc_addr = ia->ia_addr; sc->sc_phandle = ia->ia_cookie; sc->sc_tag = ia->ia_tag; dict = device_properties(self); if (prop_dictionary_get_int32(dict, "isel", &isel)) { prop_dictionary_get_int32(dict, "fdim", &fdim); prop_dictionary_get_int32(dict, "brightness", &brightness); } else isel = -1; tps65217pmic_version(sc); aprint_normal(": TPS65217"); switch (sc->sc_version) { case TPS65217PMIC_CHIPID_VER_A: aprint_normal("A"); break; case TPS65217PMIC_CHIPID_VER_B: aprint_normal("B"); break; case TPS65217PMIC_CHIPID_VER_C: aprint_normal("C"); break; case TPS65217PMIC_CHIPID_VER_D: aprint_normal("D"); break; default: /* unknown version */ break; } aprint_normal(" Power Management Multi-Channel IC (rev 1.%d)\n", sc->sc_revision); mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_NONE); sc->sc_smpsw.smpsw_name = device_xname(self); sc->sc_smpsw.smpsw_type = PSWITCH_TYPE_ACADAPTER; sysmon_pswitch_register(&sc->sc_smpsw); tps65217pmic_reg_refresh(sc); tps65217pmic_print_ppath(sc); tps65217pmic_print_ldos(sc); tps65217pmic_power_monitor_init(sc); if (isel != -1) tps65217pmic_wled_init(sc, isel, fdim, brightness); tps65217pmic_envsys_register(sc); #ifdef FDT tps65217pmic_regulator_attach(sc); #endif } static void tps65217pmic_power_monitor_init(struct tps65217pmic_softc *sc) { uint8_t intr, intrmask, status, ppath; intrmask = TPS65217PMIC_INT_USBM | TPS65217PMIC_INT_ACM | TPS65217PMIC_INT_PBM; if (tps65217pmic_i2c_lock(sc) != 0) { aprint_error_dev(sc->sc_dev, "failed to initialize power monitor\n"); return; } status = tps65217pmic_reg_read(sc, TPS65217PMIC_STATUS); ppath = tps65217pmic_reg_read(sc, TPS65217PMIC_PPATH); /* acknowledge and disregard whatever interrupt was generated earlier */ intr = tps65217pmic_reg_read(sc, TPS65217PMIC_INT); tps65217pmic_i2c_unlock(sc); sc->sc_usbstatus = status & TPS65217PMIC_STATUS_USBPWR; sc->sc_acstatus = status & TPS65217PMIC_STATUS_ACPWR; sc->sc_usbenabled = ppath & TPS65217PMIC_PPATH_USB_EN; sc->sc_acenabled = ppath & TPS65217PMIC_PPATH_AC_EN; if (intr & intrmask) aprint_normal_dev(sc->sc_dev, "WARNING: hardware interrupt enabled but not supported"); /* set up callout to poll for power source changes */ callout_init(&sc->sc_powerpollco, 0); callout_setfunc(&sc->sc_powerpollco, tps65217pmic_power_monitor, sc); callout_schedule(&sc->sc_powerpollco, hz); } static void tps65217pmic_power_monitor_task(void *aux) { struct tps65217pmic_softc *sc; uint8_t status; bool usbstatus, acstatus; sc = aux; mutex_enter(&sc->sc_lock); if (tps65217pmic_i2c_lock(sc) != 0) { device_printf(sc->sc_dev, "WARNING: unable to perform power monitor task.\n"); return; } status = tps65217pmic_reg_read(sc, TPS65217PMIC_STATUS); tps65217pmic_i2c_unlock(sc); usbstatus = status & TPS65217PMIC_STATUS_USBPWR; acstatus = status & TPS65217PMIC_STATUS_ACPWR; if (usbstatus != sc->sc_usbstatus) { sc->sc_usbstatus = usbstatus; pmf_event_inject(NULL, PMFE_POWER_CHANGED); if (usbstatus) aprint_normal_dev(sc->sc_dev, "USB power source connected\n"); else aprint_normal_dev(sc->sc_dev, "USB power source disconnected\n"); } if (acstatus != sc->sc_acstatus) { sc->sc_acstatus = acstatus; pmf_event_inject(NULL, PMFE_POWER_CHANGED); if (acstatus) { sysmon_pswitch_event(&sc->sc_smpsw, PSWITCH_EVENT_PRESSED); } else { sysmon_pswitch_event(&sc->sc_smpsw, PSWITCH_EVENT_RELEASED); } } mutex_exit(&sc->sc_lock); callout_schedule(&sc->sc_powerpollco, hz); } static void tps65217pmic_power_monitor(void *aux) { sysmon_task_queue_sched(0, tps65217pmic_power_monitor_task, aux); } static void tps65217pmic_wled_init(struct tps65217pmic_softc *sc, int isel, int fdim, int brightness) { uint8_t val = 0; switch (isel) { case 1: case 2: val |= ((isel - 1) << TPS65217PMIC_WLEDCTRL1_ISEL); break; default: aprint_error_dev(sc->sc_dev, "WLED ISET selection is 1 or 2: isel %d\n", isel); return; } switch (fdim) { case 100: val |= TPS65217PMIC_WLEDCTRL1_FDIM_100Hz; break; case 200: val |= TPS65217PMIC_WLEDCTRL1_FDIM_200Hz; break; case 500: val |= TPS65217PMIC_WLEDCTRL1_FDIM_500Hz; break; case 1000: val |= TPS65217PMIC_WLEDCTRL1_FDIM_1000Hz; break; default: aprint_error_dev(sc->sc_dev, "WLED PWM dimming frequency is 100, 200, 500 or 1000:" " fdim %d\n", fdim); return; } if (brightness > 100 || brightness < 0) { aprint_error_dev(sc->sc_dev, "invalid brightness: between 0 and 100: %d\n", brightness); return; } if (tps65217pmic_i2c_lock(sc) != 0) { device_printf(sc->sc_dev, "WARNING: unable to configure LED\n"); return; } tps65217pmic_reg_write(sc, TPS65217PMIC_WLEDCTRL1, val); tps65217pmic_reg_write(sc, TPS65217PMIC_WLEDCTRL2, (brightness - 1) & TPS65217PMIC_WLEDCTRL2_DUTY); val |= TPS65217PMIC_WLEDCTRL1_ISINK_EN; tps65217pmic_reg_write(sc, TPS65217PMIC_WLEDCTRL1, val); tps65217pmic_i2c_unlock(sc); } static void tps65217pmic_reg_refresh(struct tps65217pmic_softc *sc) { int i; struct tps_reg_param *c_reg; if (tps65217pmic_i2c_lock(sc) != 0) { device_printf(sc->sc_dev, "WARNING: unable to refresh regulators\n"); return; } for (i = 0; i < NTPS_REG; i++) { c_reg = &tps_regulators[i]; tps65217pmic_regulator_read_config(sc, c_reg); } tps65217pmic_i2c_unlock(sc); } /* Get version and revision of the chip. */ static void tps65217pmic_version(struct tps65217pmic_softc *sc) { uint8_t chipid; if (tps65217pmic_i2c_lock(sc) != 0) { device_printf(sc->sc_dev, "WARNING: unable to get chip ID\n"); return; } chipid = tps65217pmic_reg_read(sc, TPS65217PMIC_CHIPID); tps65217pmic_i2c_unlock(sc); sc->sc_version = chipid & TPS65217PMIC_CHIPID_VER_MASK; sc->sc_revision = chipid & TPS65217PMIC_CHIPID_REV_MASK; } static uint16_t tps65217pmic_ppath_max_ac_current(uint8_t ppath) { switch ((ppath & TPS65217PMIC_PPATH_IAC) >> TPS65217PMIC_PPATH_IAC_RSHFIT) { case TPS65217PMIC_PPATH_IAC_100MA: return 100; case TPS65217PMIC_PPATH_IAC_500MA: return 500; case TPS65217PMIC_PPATH_IAC_1300MA: return 1300; case TPS65217PMIC_PPATH_IAC_2500MA: return 2500; } return 0; } static uint16_t tps65217pmic_ppath_max_usb_current(uint8_t ppath) { switch (ppath & TPS65217PMIC_PPATH_IUSB) { case TPS65217PMIC_PPATH_IUSB_100MA: return 100; case TPS65217PMIC_PPATH_IUSB_500MA: return 500; case TPS65217PMIC_PPATH_IUSB_1300MA: return 1300; case TPS65217PMIC_PPATH_IUSB_1800MA: return 1800; } return 0; } /* Read regulator state and save it to tps_reg_param. */ static void tps65217pmic_regulator_read_config(struct tps65217pmic_softc *sc, struct tps_reg_param *regulator) { uint8_t defreg, regenable; uint16_t voltage; regenable = tps65217pmic_reg_read(sc, TPS65217PMIC_ENABLE); if (regenable & (regulator->enable_bit)) regulator->is_enabled = true; else { regulator->is_enabled = false; return; } defreg = tps65217pmic_reg_read(sc, regulator->defreg_num); switch (regulator->nvoltages) { case 16: voltage = regulator->voltages[defreg & TPS65217PMIC_DEFX_VOLTAGE_16]; break; case 32: voltage = regulator->voltages[defreg & TPS65217PMIC_DEFX_VOLTAGE_32]; break; case 64: voltage = regulator->voltages[defreg & TPS65217PMIC_DEFX_VOLTAGE_64]; break; default: /* unsupported number of voltage settings? */ voltage = 0; break; } /* Handle regulator tracking other regulator voltage. */ if (regulator->can_track) if (defreg & TPS65217PMIC_DEFX_TRACKING) { regulator->is_tracking = true; voltage = 0; /* see regulator->tracked_reg */ } /* Handle regulator configured into load switch mode. */ if (regulator->can_ls) if (!(defreg & TPS65217PMIC_DEFX_LS)) { regulator->is_ls = true; voltage = 0; } if (regulator->can_xadj) if (defreg & TPS65217PMIC_DEFX_XADJ) { regulator->is_xadj = true; voltage = 0; } /* TODO: add PGOOD checking */ regulator->current_voltage = voltage; } static void tps65217pmic_print_ldos(struct tps65217pmic_softc *sc) { int i; struct tps_reg_param *c_reg; aprint_normal_dev(sc->sc_dev, ""); for (i = 0; i < NTPS_REG; i++) { c_reg = &tps_regulators[i]; if (c_reg->is_enabled) { if (c_reg->is_ls) aprint_normal("[%s: LS] ", c_reg->name); else if (c_reg->is_xadj) aprint_normal("[%s: XADJ] ", c_reg->name); else aprint_normal("[%s: %d mV] ", c_reg->name, c_reg->current_voltage); } } aprint_normal("\n"); } static void tps65217pmic_print_ppath(struct tps65217pmic_softc *sc) { uint8_t status, ppath; ppath = tps65217pmic_reg_read(sc, TPS65217PMIC_PPATH); status = tps65217pmic_reg_read(sc, TPS65217PMIC_STATUS); aprint_normal_dev(sc->sc_dev, "power sources "); if (ppath & TPS65217PMIC_PPATH_USB_EN) { if (status & TPS65217PMIC_STATUS_USBPWR) aprint_normal("[USB] "); else aprint_normal("USB "); aprint_normal("max %d mA, ", tps65217pmic_ppath_max_usb_current(ppath)); } if (ppath & TPS65217PMIC_PPATH_AC_EN) { if (status & TPS65217PMIC_STATUS_ACPWR) aprint_normal("[AC] "); else aprint_normal("AC "); aprint_normal("max %d mA", tps65217pmic_ppath_max_ac_current(ppath)); } aprint_normal("\n"); } static int tps65217pmic_i2c_lock(struct tps65217pmic_softc *sc) { int error; error = iic_acquire_bus(sc->sc_tag, 0); if (error) { device_printf(sc->sc_dev, "unable to acquire i2c bus, error %d\n", error); } return error; } static void tps65217pmic_i2c_unlock(struct tps65217pmic_softc *sc) { iic_release_bus(sc->sc_tag, 0); } static uint8_t tps65217pmic_reg_read(struct tps65217pmic_softc *sc, uint8_t reg) { uint8_t wbuf[2]; uint8_t rv; wbuf[0] = reg; if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr, wbuf, 1, &rv, 1, 0)) { aprint_error_dev(sc->sc_dev, "cannot execute operation\n"); iic_release_bus(sc->sc_tag, 0); return 0; } return rv; } static void tps65217pmic_reg_write(struct tps65217pmic_softc *sc, uint8_t reg, uint8_t data) { uint8_t wbuf[2]; wbuf[0] = reg; wbuf[1] = data; if (iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, sc->sc_addr, NULL, 0, wbuf, 2, 0)) { aprint_error_dev(sc->sc_dev, "cannot execute I2C write\n"); } } static void tps65217pmic_reg_write_l2(struct tps65217pmic_softc *sc, uint8_t reg, uint8_t data) { uint8_t regpw = reg ^ TPS65217PMIC_PASSWORD_XOR; if (tps65217pmic_i2c_lock(sc)) return; tps65217pmic_reg_write(sc, TPS65217PMIC_PASSWORD, regpw); tps65217pmic_reg_write(sc, reg, data); tps65217pmic_reg_write(sc, TPS65217PMIC_PASSWORD, regpw); tps65217pmic_reg_write(sc, reg, data); tps65217pmic_i2c_unlock(sc); } static void tps65217pmic_envsys_register(struct tps65217pmic_softc *sc) { int i; sc->sc_sme = sysmon_envsys_create(); /* iterate over all regulators and attach them as sensors */ for(i = 0; i <= SNUM_REGS; i++) { /* set name */ strlcpy(sc->sc_regsensor[i].desc, tps_regulators[i].name, sizeof(sc->sc_regsensor[i].desc)); sc->sc_regsensor[i].units = ENVSYS_SVOLTS_DC; sc->sc_regsensor[i].state = ENVSYS_SINVALID; if (sysmon_envsys_sensor_attach(sc->sc_sme, &sc->sc_regsensor[i])) aprint_error_dev(sc->sc_dev, "error attaching regulator sensor %d\n", i); } /* attach power source indicators */ strcpy(sc->sc_usbsensor.desc, "USB power source"); /* SNUM_USBSTATUS */ sc->sc_usbsensor.units = ENVSYS_INDICATOR; sc->sc_usbsensor.state = ENVSYS_SINVALID; if (sysmon_envsys_sensor_attach(sc->sc_sme, &sc->sc_usbsensor)) aprint_error_dev(sc->sc_dev, "error attaching USB power source sensor\n"); strcpy(sc->sc_acsensor.desc, "AC power source"); /* SNUM_ACSTATUS */ sc->sc_acsensor.units = ENVSYS_INDICATOR; sc->sc_acsensor.state = ENVSYS_SINVALID; if (sysmon_envsys_sensor_attach(sc->sc_sme, &sc->sc_acsensor)) aprint_error_dev(sc->sc_dev, "error attaching AC power source sensor\n"); /* register everything in sysmon */ sc->sc_sme->sme_name = device_xname(sc->sc_dev); sc->sc_sme->sme_cookie = sc; sc->sc_sme->sme_refresh = tps65217pmic_envsys_refresh; if (sysmon_envsys_register(sc->sc_sme)) { aprint_error_dev(sc->sc_dev, "unable to register in sysmon\n"); sysmon_envsys_destroy(sc->sc_sme); } } static void tps65217pmic_envsys_refresh(struct sysmon_envsys *sme, envsys_data_t *edata) { struct tps65217pmic_softc *sc = sme->sme_cookie; mutex_enter(&sc->sc_lock); tps65217pmic_reg_refresh(sc); if (edata->sensor <= SNUM_REGS) { /* TODO: handle special cases like LS, XADJ... */ edata->value_cur = tps_regulators[edata->sensor].current_voltage * 1000; edata->state = ENVSYS_SVALID; } else if (edata->sensor == SNUM_USBSTATUS) { edata->value_cur = sc->sc_usbstatus && sc->sc_usbenabled; edata->state = ENVSYS_SVALID; } else if (edata->sensor == SNUM_ACSTATUS) { edata->value_cur = sc->sc_acstatus && sc->sc_acenabled; edata->state = ENVSYS_SVALID; } else aprint_error_dev(sc->sc_dev, "unknown sensor number\n"); mutex_exit(&sc->sc_lock); } int tps65217pmic_set_volt(device_t self, const char *name, int mvolt) { int i; struct tps65217pmic_softc *sc = device_private(self); struct tps_reg_param *regulator = NULL; uint8_t val; for (i = 0; i < __arraycount(tps_regulators); i++) { if (strcmp(name, tps_regulators[i].name) == 0) { regulator = &tps_regulators[i]; break; } } if (regulator == NULL) return EINVAL; if (regulator->voltage_min > mvolt || regulator->voltage_max < mvolt) return EINVAL; if (!regulator->is_enabled) return EINVAL; if (regulator->is_tracking) return EINVAL; if (regulator->is_xadj) return EINVAL; /* find closest voltage entry */ for (i = 0; i < regulator->nvoltages; i++) { if (mvolt <= regulator->voltages[i]) { break; } } KASSERT(i < regulator->nvoltages); tps65217pmic_reg_write_l2(sc, regulator->defreg_num, i); val = tps65217pmic_reg_read(sc, TPS65217PMIC_DEFSLEW); val |= TPS65217PMIC_DEFSLEW_GO; tps65217pmic_reg_write_l2(sc, TPS65217PMIC_DEFSLEW, val); while (val & TPS65217PMIC_DEFSLEW_GO) { val = tps65217pmic_reg_read(sc, TPS65217PMIC_DEFSLEW); } regulator->current_voltage = regulator->voltages[i]; return 0; } #ifdef FDT static struct tps_reg_param * tps65217pmic_get_params(const char *name) { int i; for (i = 0; i < __arraycount(tps_regulators); i++) { if (strcmp(name, tps_regulators[i].name) == 0) return &tps_regulators[i]; } return NULL; } static void tps65217pmic_regulator_attach(struct tps65217pmic_softc *sc) { struct tps65217reg_attach_args raa; struct tps_reg_param *param; const char *compat_name; int phandle, child; phandle = of_find_firstchild_byname(sc->sc_phandle, "regulators"); if (phandle <= 0) return; for (child = OF_child(phandle); child; child = OF_peer(child)) { compat_name = fdtbus_get_string(child, "regulator-compatible"); if (compat_name == NULL) continue; param = tps65217pmic_get_params(compat_name); if (param == NULL) continue; raa.reg_param = param; raa.reg_phandle = child; config_found(sc->sc_dev, &raa, NULL, CFARGS_NONE); } } static int tps65217reg_acquire(device_t dev) { return 0; } static void tps65217reg_release(device_t dev) { } static int tps65217reg_enable(device_t dev, bool enable) { struct tps65217reg_softc *sc = device_private(dev); struct tps65217pmic_softc *pmic_sc = device_private(device_parent(dev)); struct tps_reg_param *regulator = sc->sc_param; uint8_t val; int error; error = tps65217pmic_i2c_lock(pmic_sc); if (error != 0) return error; val = tps65217pmic_reg_read(pmic_sc, TPS65217PMIC_ENABLE); if (enable) val |= regulator->enable_bit; else val &= ~regulator->enable_bit; tps65217pmic_reg_write(pmic_sc, TPS65217PMIC_ENABLE, val); regulator->is_enabled = enable; tps65217pmic_i2c_unlock(pmic_sc); return 0; } static int tps65217reg_set_voltage(device_t dev, u_int min_uvol, u_int max_uvol) { struct tps65217reg_softc *sc = device_private(dev); struct tps65217pmic_softc *pmic_sc = device_private(device_parent(dev)); struct tps_reg_param *regulator = sc->sc_param; int error; error = tps65217pmic_i2c_lock(pmic_sc); if (error != 0) return error; error = tps65217pmic_set_volt(pmic_sc->sc_dev, regulator->name, min_uvol / 1000); tps65217pmic_i2c_unlock(pmic_sc); return error; } static int tps65217reg_get_voltage(device_t dev, u_int *puvol) { struct tps65217reg_softc *sc = device_private(dev); struct tps_reg_param *regulator = sc->sc_param; *puvol = (u_int)regulator->current_voltage * 1000; return 0; } static struct fdtbus_regulator_controller_func tps65217reg_funcs = { .acquire = tps65217reg_acquire, .release = tps65217reg_release, .enable = tps65217reg_enable, .set_voltage = tps65217reg_set_voltage, .get_voltage = tps65217reg_get_voltage, }; static int tps65217reg_match(device_t parent, cfdata_t match, void *aux) { return 1; } static void tps65217reg_attach(device_t parent, device_t self, void *aux) { struct tps65217reg_softc *sc = device_private(self); struct tps65217reg_attach_args *raa = aux; const char *regname; sc->sc_dev = self; sc->sc_phandle = raa->reg_phandle; sc->sc_param = raa->reg_param; fdtbus_register_regulator_controller(self, sc->sc_phandle, &tps65217reg_funcs); regname = fdtbus_get_string(sc->sc_phandle, "regulator-name"); if (regname == NULL) regname = fdtbus_get_string(sc->sc_phandle, "regulator-compatible"); aprint_naive("\n"); if (regname != NULL) aprint_normal(": %s\n", regname); else aprint_normal("\n"); } CFATTACH_DECL_NEW(tps65217reg, sizeof (struct tps65217reg_softc), tps65217reg_match, tps65217reg_attach, NULL, NULL); #endif