/* * Copyright (c) 2014 WonderMedia Technologies, Inc. * * This program is free software: you can redistribute it and/or modify it under the * terms of the GNU General Public License as published by the Free Software Foundation, * either version 2 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNU General Public License for more details. * You should have received a copy of the GNU General Public License along with * this program. If not, see . * * WonderMedia Technologies, Inc. * 10F, 529, Chung-Cheng Road, Hsin-Tien, Taipei 231, R.O.C. */ #include #include #include #include "../include/wmt_iomux.h" #include "wmt_battery.h" #define return_val_if_failed(val, expr) \ do { \ if (!(expr)) { \ printf(" %s, %d, Warnning: " #expr " failed.\n", \ __FUNCTION__, __LINE__); \ return val; \ } \ } while(0) struct battery_param { struct battery_dev *bat_dev; }; struct charger_param { struct charger_dev *chg_dev; }; static struct battery_param battery_param; static struct charger_param charger_param; static int full_by_hw = 0; static struct battery_dev *battery_devices[] = { &vt1603_battery_dev, &ug31xx_battery_dev, &ug3102_battery_dev, &sp2541_battery_dev, &bq_battery_dev, NULL, }; static struct charger_dev *charger_devices[] = { &mp2625_charger_dev, &g2214_charger_dev, NULL, }; static int parse_charger_param(struct charger_param *cp) { static const char uboot_env[] = "wmt.charger.param"; struct charger_dev **dev = &charger_devices[0]; char *env; if (!(env = getenv((char *)uboot_env))) { printf("please setenv %s\n", uboot_env); return -EINVAL; } while (*dev) { if ((*dev)->name && !strncmp((*dev)->name, env, strlen((*dev)->name))) break; ++dev; } if (!(*dev)) return -EINVAL; cp->chg_dev = *dev; printf("Charger matched: %s\n", cp->chg_dev->name); return 0; } static int parse_battery_param(struct battery_param *bp) { static const char uboot_env[] = "wmt.battery.param"; struct battery_dev **dev = &battery_devices[0]; char *env; if (!(env = getenv((char *)uboot_env))) { printf("please setenv %s\n", uboot_env); return -EINVAL; } while (*dev) { if ((*dev)->name && !strncmp((*dev)->name, env, strlen((*dev)->name))) break; ++dev; } if (!(*dev)) return -EINVAL; bp->bat_dev = *dev; printf("Battery matched: %s\n", bp->bat_dev->name); return 0; } static int parse_battery_full_param(void) { static const char uboot_env[] = "wmt.bat.hwfull"; char *env; if (!(env = getenv((char *)uboot_env))) { full_by_hw = 0; return -EINVAL; } full_by_hw = simple_strtol(env, NULL, 10); printf("full_by_hw %d\n", full_by_hw); return 0; } static struct { int charging; int full; int led_power; int led_gpio_level; } charger_led; static int parse_charger_led(void) { enum { idx_id, idx_v1, idx_v2, idx_max, }; long ps[idx_max]; char *p, *endp; int i = 0; // initialize as invalid gpio charger_led.charging = -1; charger_led.full = -1; charger_led.led_power = -1; p = getenv("wmt.charger.led"); if (!p) return -EINVAL; while (i < idx_max) { ps[i++] = simple_strtol(p, &endp, 10); if (*endp == '\0') break; p = endp + 1; if (*p == '\0') break; } if (i != 3) return -EINVAL; switch (ps[idx_id]) { case 0: charger_led.led_power = ps[idx_v1]; charger_led.led_gpio_level = ps[idx_v2]; printf("charger led power: %d (%d)\n", charger_led.led_power, charger_led.led_gpio_level); break; case 1: charger_led.charging = ps[idx_v1]; charger_led.full = ps[idx_v2]; printf("charger led pin: charging %d, full %d\n", charger_led.charging, charger_led.full); break; default: printf("no valid charger led found\n"); return -EINVAL; } return 0; } int wmt_power_supply_init(void) { struct charger_param *cp = &charger_param; struct battery_param *bp = &battery_param; static int inited = 0; if (inited) return 0; ++inited; if (parse_charger_param(cp)) { pr_err("charger not found\n"); return -ENODEV; } if (cp->chg_dev->init()) { pr_err("%s init failed\n", cp->chg_dev->name); return -EIO; } if (parse_battery_param(bp)) { pr_err("cattery not found\n"); return -ENODEV; } if (bp->bat_dev->init()) { pr_err("%s init failed\n", bp->bat_dev->name); return -EIO; } parse_charger_led(); parse_battery_full_param(); return 0; } int pmic_init(void) { if (is_g2214_avail() != -1) g2214_pmic_init(); return 0; } static inline void led_enable(int gpio, int enable) { if (gpio_is_valid(gpio)) { if (enable) gpio_direction_output(gpio, 1); else { gpio_direction_output(gpio, 0); /* walkaround for hardware bug. * set gpio to input to let led off. */ gpio_direction_input(gpio); } } } static inline void led_power_enable(int enable) { int gpio = charger_led.led_power; if (gpio_is_valid(gpio)) { if (enable) gpio_direction_output(gpio, charger_led.led_gpio_level); else gpio_direction_output(gpio, !charger_led.led_gpio_level); } } int wmt_charger_event_callback(enum event_type event) { struct charger_dev *dev = charger_param.chg_dev; return_val_if_failed(-1, dev != NULL); return_val_if_failed(-1, dev->event_proc); printf(" ## %s, %d\n", __func__, event); switch (event) { case POWER_EVENT_DCDET_ADAPTER: case POWER_EVENT_CHARGING: led_power_enable(1); if (wmt_battery_is_charging_full()) { led_enable(charger_led.charging, 0); led_enable(charger_led.full, 1); } else { led_enable(charger_led.charging, 1); led_enable(charger_led.full, 0); } break; case POWER_EVENT_DCDET_USBPC: if (wmt_charger_pc_charging()) { led_power_enable(1); if (wmt_battery_is_charging_full()) { led_enable(charger_led.charging, 0); led_enable(charger_led.full, 1); } else led_enable(charger_led.charging, 1); } else { led_power_enable(0); led_enable(charger_led.charging, 0); } led_enable(charger_led.full, 0); break; case POWER_EVENT_DCDET_PLUGOUT: case POWER_EVENT_DISCHARGING: led_power_enable(0); led_enable(charger_led.charging, 0); led_enable(charger_led.full, 0); break; case POWER_EVENT_CHARGING_FULL: led_power_enable(1); led_enable(charger_led.charging, 0); led_enable(charger_led.full, 1); break; default: break; } return dev->event_proc(event); } int wmt_charger_cable_type(void) { struct charger_dev *dev = charger_param.chg_dev; return_val_if_failed(-1, dev != NULL); return_val_if_failed(-1, dev->cable_type); return dev->cable_type(); } int wmt_charger_pc_charging(void) { struct charger_dev *dev = charger_param.chg_dev; return_val_if_failed(-1, dev != NULL); return (dev->pc_charging) ? dev->pc_charging() : 0; } int wmt_battery_get_capacity(void) { struct battery_dev *dev = battery_param.bat_dev; static unsigned int ref_full = 0; int capacity; return_val_if_failed(-1, dev != NULL); return_val_if_failed(-1, dev->get_capacity); capacity = dev->get_capacity(); if (!wmt_battery_is_gauge()) { if (full_by_hw) { if (wmt_battery_is_charging_full()) return capacity; else if (capacity == 100) return 99; } if (capacity == 100 && !wmt_battery_is_charging_full()) { if (ref_full < 30 * 60) { capacity = 99; ref_full++; } else printf("ref full %d, 30 minutes, report full\n", ref_full); } else ref_full = 0; printf("-> %d, ref full %d\n", capacity, ref_full); } return capacity; } int wmt_battery_get_voltage(void) { struct battery_dev *dev = battery_param.bat_dev; return_val_if_failed(-1, dev != NULL); return_val_if_failed(-1, dev->get_voltage); return dev->get_voltage(); } int wmt_battery_get_current(void) { struct battery_dev *dev = battery_param.bat_dev; return_val_if_failed(-1, dev != NULL); return_val_if_failed(-1, dev->get_current); return dev->get_current(); } int wmt_battery_is_lowlevel(void) { struct battery_dev *dev = battery_param.bat_dev; return_val_if_failed(-1, dev != NULL); return_val_if_failed(-1, dev->check_batlow); return dev->check_batlow(); } int wmt_battery_is_charging_full(void) { struct charger_dev *dev = charger_param.chg_dev; return_val_if_failed(-1, dev != NULL); return_val_if_failed(-1, dev->check_full); if (!wmt_is_dc_plugin()) return -1; return dev->check_full(); } int wmt_battery_is_gauge(void) { struct battery_dev *dev = battery_param.bat_dev; return_val_if_failed(-1, dev != NULL); return dev->is_gauge; } static int do_batt(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) { wmt_power_supply_init(); printf(" voltage %d mV\n", wmt_battery_get_voltage()); printf(" capacity %d\n", wmt_battery_get_capacity()); printf(" dinc %d\n", wmt_is_dc_plugin()); return 0; } U_BOOT_CMD( batt, 1, 1, do_batt, "batt - print Board Info structure\n", NULL );