diff --git a/cmd/Kconfig b/cmd/Kconfig index 71c6ce59a17..2759bcefcf3 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -877,6 +877,13 @@ config CMD_ADC Shows ADC device info and permit printing one-shot analog converted data from a named Analog to Digital Converter. +config CMD_AXP + bool "axp - Read information from AXP813 PMIC" + depends on AXP818_POWER + help + Shows A/D voltage, battery presence, battery capacity, charger + presence based on register values inside AXP813. + config CMD_BCB bool "bcb" depends on MMC diff --git a/cmd/Makefile b/cmd/Makefile index 1530173ce35..3e451484e30 100644 --- a/cmd/Makefile +++ b/cmd/Makefile @@ -13,6 +13,7 @@ obj-y += version.o # command obj-$(CONFIG_CMD_ACPI) += acpi.o +obj-$(CONFIG_CMD_AXP) += axp.o obj-$(CONFIG_CMD_AES) += aes.o obj-$(CONFIG_CMD_AB_SELECT) += ab_select.o obj-$(CONFIG_CMD_ADC) += adc.o diff --git a/cmd/axp.c b/cmd/axp.c new file mode 100644 index 00000000000..75deea64d74 --- /dev/null +++ b/cmd/axp.c @@ -0,0 +1,187 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2018 Ondrej Jirman + */ +#include +#include +#include +#include + +static int do_axp(cmd_tbl_t *cmdtp, int flag, int argc, + char *const argv[]) +{ + int ret = 0, val; + bool val_bool; + char buf[64]; + + if (argc < 2) + return CMD_RET_USAGE; + + if (!strcmp(argv[1], "dump")) { + printf("AXP status:\n"); + + ret = axp_is_charging(&val_bool); + if (!ret) + printf(" charging: %s\n", val_bool ? "yes" : "no"); + + ret = axp_is_vbus_present(&val_bool); + if (!ret) + printf(" vbus_ok: %s\n", val_bool ? "yes" : "no"); + + ret = axp_is_battery_present(&val_bool); + if (!ret) + printf(" bat_ok: %s\n", val_bool ? "yes" : "no"); + + ret = axp_get_battery_voltage(&val); + if (!ret) + printf(" bat_voltage: %d uV\n", val); + + ret = axp_get_battery_current(&val); + if (!ret) + printf(" bat_current: %d uA\n", val); + + ret = axp_battery_get_capacity(&val); + if (!ret) + printf(" bat_capacity: %d %%\n", val); + + ret = axp_usb_get_max_current(&val); + if (!ret) + printf(" vbus_imax: %d mA\n", val); + } else if (!strcmp(argv[1], "charging")) { + ret = axp_is_charging(&val_bool); + if (ret) + return CMD_RET_FAILURE; + + snprintf(buf, sizeof buf, "ret=%d", !!val_bool); + set_local_var(buf, 1); + + //int axp_usb_set_max_current(int mA); + } else if (!strcmp(argv[1], "vbus_ok")) { + ret = axp_is_vbus_present(&val_bool); + if (ret) + return CMD_RET_FAILURE; + + snprintf(buf, sizeof buf, "ret=%d", !!val_bool); + set_local_var(buf, 1); + } else if (!strcmp(argv[1], "bat_ok")) { + ret = axp_is_battery_present(&val_bool); + if (ret) + return CMD_RET_FAILURE; + + snprintf(buf, sizeof buf, "ret=%d", !!val_bool); + set_local_var(buf, 1); + } else if (!strcmp(argv[1], "bat_voltage")) { + ret = axp_get_battery_voltage(&val); + if (ret) + return CMD_RET_FAILURE; + + snprintf(buf, sizeof buf, "ret=%d", val); + set_local_var(buf, 1); + } else if (!strcmp(argv[1], "bat_current")) { + ret = axp_get_battery_current(&val); + if (ret) + return CMD_RET_FAILURE; + + snprintf(buf, sizeof buf, "ret=%d", val); + set_local_var(buf, 1); + } else if (!strcmp(argv[1], "bat_capacity") + || !strcmp(argv[1], "bat_capacity5")) { + ret = axp_battery_get_capacity(&val); + if (ret) + return CMD_RET_FAILURE; + + if (!strcmp(argv[1], "bat_capacity5")) { + // round to multiple of 5 + int rem = val % 5; + if (rem > 2) + val += (5 - rem); + else + val -= rem; + } + + snprintf(buf, sizeof buf, "ret=%d", val); + set_local_var(buf, 1); + } else if (!strcmp(argv[1], "vbus_imax")) { + if (argc == 3) { + val = simple_strtoul(argv[2], NULL, 10); + if (val == 0) + return CMD_RET_USAGE; + + ret = axp_usb_set_max_current(val); + if (ret) + return CMD_RET_FAILURE; + } else if (argc == 2) { + ret = axp_usb_get_max_current(&val); + if (ret) + return CMD_RET_FAILURE; + + snprintf(buf, sizeof buf, "ret=%d", val); + set_local_var(buf, 1); + } else + return CMD_RET_USAGE; + } else if (!strcmp(argv[1], "led_set")) { + if (argc < 3) + return CMD_RET_USAGE; + + if (strlen(argv[2]) != 1) + return CMD_RET_USAGE; + switch (argv[2][0]) { + case '0': + ret = axp_led_set(0); + break; + case '1': + ret = axp_led_set(1); + break; + case 'a': + ret = axp_led_set(2); + break; + case 'b': + ret = axp_led_set(3); + break; + default: + return CMD_RET_USAGE; + } + + if (ret) + return CMD_RET_FAILURE; + } else if (!strcmp(argv[1], "led_chgctl")) { + ret = axp_led_set_charger_controlled(); + if (ret) + return CMD_RET_FAILURE; + } else if (!strcmp(argv[1], "clr_reason")) { + ret = axp_clear_startup_reason(); + if (ret) + return CMD_RET_FAILURE; + } else if (!strcmp(argv[1], "pok_fast")) { + ret = axp_pok_set_quick(); + if (ret) + return CMD_RET_FAILURE; + } else if (!strcmp(argv[1], "bc_en")) { + ret = axp_set_bc_en(); + if (ret) + return CMD_RET_FAILURE; + } else + return CMD_RET_USAGE; + + return CMD_RET_SUCCESS; +} + +static char axp_help_text[] = + "dump - dump AXP device info\n" + "axp charging - Check if battery is charging\n" + "axp vbus_ok - Check if connected to USB charger\n" + "axp bat_ok - Check if battery is present/OK\n" + "axp bat_voltage - Return battery voltage in uV\n" + "axp bat_current - Return battery current in uA\n" + "axp bat_capacity - Return battery capacity in %\n" + "axp bat_capacity5 - As above rounded to multiples of 5\n" + "axp vbus_imax - Return max USB current in mA\n" + "axp vbus_imax - Set max USB current to value\n" + "axp led_set 1|0 - Turn charger led on/off\n" + "axp led_chgctl - Defer LED control to the charger\n" + // Especially useful for escaping UV lockout + "axp clr_reason - Clear startup/shutdown reason register\n" + "axp bc_en - Charger detection enable\n" + "axp pok_fast - Set power on/off key to low latency mode"; + +U_BOOT_CMD(axp, 4, 1, do_axp, "AXP813 PMIC access", axp_help_text); diff --git a/drivers/power/axp818.c b/drivers/power/axp818.c index cd7fa5d2b51..afa5c9d964e 100644 --- a/drivers/power/axp818.c +++ b/drivers/power/axp818.c @@ -272,8 +272,6 @@ int axp_gpio0_enable_ldo_set_voltage(u32 mV) else val = (mV - 700) / 100; - printf("setting LDO0 voltage: %u\n", mV); - ret = pmic_bus_write(AXP_GPIO0_LDO_CTRL, val); if (ret) return ret; @@ -312,3 +310,255 @@ int do_poweroff(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) /* not reached */ return 0; } + +// battery/charger related functions + +int axp_is_charging(bool* charging) +{ + int ret; + u8 cs; + + ret = pmic_bus_read(AXP_CHARGER_STATUS, &cs); + if (ret) + return ret; + + *charging = (cs & AXP_CHARGER_STATUS_CHARGING); + return 0; +} + +int axp_is_vbus_present(bool* vbus) +{ + int ret; + u8 ps; + + ret = pmic_bus_read(AXP_POWER_STATUS, &ps); + if (ret) + return ret; + + *vbus = (ps & AXP_POWER_STATUS_VBUS_PRESENT) && + (ps & AXP_POWER_STATUS_VBUS_VALID); + return 0; +} + +int axp_is_battery_present(bool* vbus) +{ + int ret; + u8 cs; + + ret = pmic_bus_read(AXP_CHARGER_STATUS, &cs); + if (ret) + return ret; + + *vbus = (cs & AXP_CHARGER_STATUS_BAT_PRESENT_VALID) && + (cs & AXP_CHARGER_STATUS_BAT_PRESENT); + return 0; +} + +int axp_clear_startup_reason(void) +{ + return pmic_bus_write(AXP_POWER_UP_DOWN_REASON, 0xff); +} + +static int axp_read_adc(u8 addr_msb, u8 addr_lsb, u32* raw) +{ + u8 val; + int ret; + + ret = pmic_bus_read(addr_lsb, &val); + if (ret) + return ret; + + *raw = val & 0xf; + + ret = pmic_bus_read(addr_msb, &val); + if (ret) + return ret; + + *raw |= (u32)val << 4; + return 0; +} + +int axp_get_battery_voltage(int* uV) +{ + u32 raw; + int ret; + + ret = axp_read_adc(AXP_AD_BAT_VOLTAGE_MSB8, AXP_AD_BAT_VOLTAGE_LSB4, + &raw); + if (ret) + return ret; + + *uV = (raw) * 1100; + return 0; +} + +int axp_get_battery_current(int* uA) +{ + u32 raw; + int ret; + bool charging; + + ret = axp_is_charging(&charging); + if (ret) + return ret; + + if (charging) + ret = axp_read_adc(AXP_AD_BAT_CHG_CURRENT_MSB8, + AXP_AD_BAT_CHG_CURRENT_LSB4, &raw); + else + ret = axp_read_adc(AXP_AD_BAT_DIS_CURRENT_MSB8, + AXP_AD_BAT_DIS_CURRENT_LSB4, &raw); + if (ret) + return ret; + + *uA = raw * 1000; + return 0; +} + +int axp_battery_get_capacity(int* capacity) +{ + int ret; + u8 val; + + ret = pmic_bus_read(AXP_BATTERY_CAPACITY, &val); + if (ret) + return ret; + + if (!(val & 0x80)) + return -EINVAL; + + *capacity = val & 0x7f; + return 0; +} + +struct { + u8 vbus_i; + int mA; +} vbus_currents[] = { + { AXP_CHARGER_CTRL3_VBUS_I_100, 100 }, + { AXP_CHARGER_CTRL3_VBUS_I_500, 500 }, + { AXP_CHARGER_CTRL3_VBUS_I_900, 900 }, + { AXP_CHARGER_CTRL3_VBUS_I_1500, 1500 }, + { AXP_CHARGER_CTRL3_VBUS_I_2000, 2000 }, + { AXP_CHARGER_CTRL3_VBUS_I_2500, 2500 }, + { AXP_CHARGER_CTRL3_VBUS_I_3000, 3000 }, + { AXP_CHARGER_CTRL3_VBUS_I_3500, 3500 }, + { AXP_CHARGER_CTRL3_VBUS_I_4000, 4000 }, +}; + +int axp_usb_get_max_current(int* mA) +{ + int i, ret; + u8 val; + + ret = pmic_bus_read(AXP_CHARGER_CTRL3, &val); + if (ret) + return ret; + + val &= AXP_CHARGER_CTRL3_VBUS_I_MASK; + + *mA = 4000; + for (i = 0; i < ARRAY_SIZE(vbus_currents); i++) { + if (vbus_currents[i].vbus_i == val) { + *mA = vbus_currents[i].mA; + break; + } + } + + return 0; +} + +int axp_usb_set_max_current(int mA) +{ + int i, ret; + u8 val; + + ret = pmic_bus_read(AXP_CHARGER_CTRL3, &val); + if (ret) + return ret; + + val &= ~AXP_CHARGER_CTRL3_VBUS_I_MASK; + + for (i = ARRAY_SIZE(vbus_currents) - 1; i >= 0; i--) { + if (vbus_currents[i].mA <= mA || i == 0) { + val |= vbus_currents[i].vbus_i; + break; + } + } + + return pmic_bus_write(AXP_CHARGER_CTRL3, val); +} + +int axp_led_set_charger_controlled(void) +{ + int ret; + u8 val; + + ret = pmic_bus_read(AXP_CHGLED, &val); + if (ret) + return ret; + + val |= AXP_CHGLED_CTRL_CHARGER; + + return pmic_bus_write(AXP_CHGLED, val); +} + +int axp_led_set(int mode) +{ + int ret; + u8 val; + + ret = pmic_bus_read(AXP_CHGLED, &val); + if (ret) + return ret; + + val &= ~(AXP_CHGLED_CTRL_CHARGER | AXP_CHGLED_SETUP_MASK); + switch (mode) { + case 0: + val |= AXP_CHGLED_SETUP_OFF; + break; + case 1: + val |= AXP_CHGLED_SETUP_ON; + break; + case 2: + val |= AXP_CHGLED_SETUP_BLINK_FAST; + break; + case 3: + val |= AXP_CHGLED_SETUP_BLINK_SLOW; + break; + default: + return -1; + } + + return pmic_bus_write(AXP_CHGLED, val); +} + +int axp_pok_set_quick(void) +{ + int ret; + u8 val; + + ret = pmic_bus_read(AXP_POK, &val); + if (ret) + return ret; + + val &= ~(AXP_POK_ON_MASK | AXP_POK_OFF_MASK); + val |= AXP_POK_ON_128MS | AXP_POK_OFF_4S; + + return pmic_bus_write(AXP_POK, val); +} + +int axp_set_bc_en(void) +{ + int ret; + u8 val; + + /* Enable USB Battery Charging specification detection */ + ret = pmic_bus_read(AXP_BC_GLOBAL, &val); + if (ret) + return ret; + + val |= AXP_BC_GLOBAL_EN; + + return pmic_bus_write(AXP_BC_GLOBAL, val); +} diff --git a/include/axp818.h b/include/axp818.h index 18e69c605b7..f4ffd13d5c2 100644 --- a/include/axp818.h +++ b/include/axp818.h @@ -60,6 +60,10 @@ /* For axp_gpio.c */ #define AXP_POWER_STATUS 0x00 #define AXP_POWER_STATUS_VBUS_PRESENT (1 << 5) +#define AXP_POWER_STATUS_VBUS_VALID (1 << 4) +#define AXP_POWER_STATUS_VBAT_GT_3_5V (1 << 3) +#define AXP_POWER_STATUS_VBUS_STARTUP (1 << 0) + #define AXP_VBUS_IPSOUT 0x30 #define AXP_VBUS_IPSOUT_DRIVEBUS (1 << 2) #define AXP_MISC_CTRL 0x8f @@ -74,5 +78,77 @@ #define AXP_GPIO_STATE 0x94 #define AXP_GPIO_STATE_OFFSET 0 +#define AXP_CHARGER_STATUS 0x01 +#define AXP_CHARGER_STATUS_BAT_PRESENT (1 << 5) +#define AXP_CHARGER_STATUS_BAT_PRESENT_VALID (1 << 4) +#define AXP_CHARGER_STATUS_CHARGING (1 << 6) +#define AXP_CHARGER_STATUS_OVER_TEMP (1 << 7) + +#define AXP_POWER_UP_DOWN_REASON 0x02 +#define AXP_POWER_UP_REASON_POK (1 << 0) +#define AXP_POWER_UP_REASON_CHARGER (1 << 1) +#define AXP_POWER_UP_REASON_GLOBAL_RESET (1 << 3) +#define AXP_POWER_UP_REASON_COLD_RESET (1 << 4) +#define AXP_POWER_DOWN_REASON_UVLO (1 << 5) +#define AXP_POWER_DOWN_REASON_COLD_OFF (1 << 6) +#define AXP_POWER_DOWN_REASON_POK_OR (1 << 7) + +#define AXP_BC_GLOBAL 0x2c +#define AXP_BC_GLOBAL_EN (1 << 0) + +#define AXP_CHGLED 0x32 +#define AXP_CHGLED_CTRL_CHARGER (1 << 3) +#define AXP_CHGLED_SETUP_MASK (0x3 << 4) +#define AXP_CHGLED_SETUP_OFF (0 << 4) +#define AXP_CHGLED_SETUP_BLINK_FAST (1 << 4) +#define AXP_CHGLED_SETUP_BLINK_SLOW (2 << 4) +#define AXP_CHGLED_SETUP_ON (3 << 4) + +#define AXP_POK 0x36 +#define AXP_POK_ON_MASK (3 << 6) +#define AXP_POK_ON_128MS (0 << 6) +#define AXP_POK_ON_1S (1 << 6) +#define AXP_POK_ON_2S (2 << 6) +#define AXP_POK_ON_3S (3 << 6) +#define AXP_POK_OFF_MASK (3 << 0) +#define AXP_POK_OFF_4S (0 << 6) +#define AXP_POK_OFF_6S (1 << 6) +#define AXP_POK_OFF_8S (2 << 6) +#define AXP_POK_OFF_10S (3 << 6) + +#define AXP_CHARGER_CTRL3 0x35 +#define AXP_CHARGER_CTRL3_VBUS_I_MASK 0xf0 +#define AXP_CHARGER_CTRL3_VBUS_I_100 0x00 +#define AXP_CHARGER_CTRL3_VBUS_I_500 0x10 +#define AXP_CHARGER_CTRL3_VBUS_I_900 0x20 +#define AXP_CHARGER_CTRL3_VBUS_I_1500 0x30 +#define AXP_CHARGER_CTRL3_VBUS_I_2000 0x40 +#define AXP_CHARGER_CTRL3_VBUS_I_2500 0x50 +#define AXP_CHARGER_CTRL3_VBUS_I_3000 0x60 +#define AXP_CHARGER_CTRL3_VBUS_I_3500 0x70 +#define AXP_CHARGER_CTRL3_VBUS_I_4000 0x80 + +#define AXP_AD_BAT_VOLTAGE_MSB8 0x78 +#define AXP_AD_BAT_VOLTAGE_LSB4 0x79 +#define AXP_AD_BAT_CHG_CURRENT_MSB8 0x7a +#define AXP_AD_BAT_CHG_CURRENT_LSB4 0x7b +#define AXP_AD_BAT_DIS_CURRENT_MSB8 0x7c +#define AXP_AD_BAT_DIS_CURRENT_LSB4 0x7d + +#define AXP_BATTERY_CAPACITY 0xb9 + int axp_gpio0_enable_ldo_set_voltage(u32 mV); +int axp_is_charging(bool* charging); +int axp_is_vbus_present(bool* vbus); +int axp_is_battery_present(bool* vbus); +int axp_clear_startup_reason(void); +int axp_get_battery_voltage(int* uV); +int axp_get_battery_current(int* uA); +int axp_battery_get_capacity(int* capacity); +int axp_usb_get_max_current(int* mA); +int axp_usb_set_max_current(int mA); +int axp_led_set_charger_controlled(void); +int axp_led_set(int mode); +int axp_pok_set_quick(void); +int axp_set_bc_en(void);