/* Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 *
 * TPSChrome powerinfo commands.
 */

#include "battery.h"
#include "console.h"
#include "extpower.h"
#include "host_command.h"
#include "pmu_tpschrome.h"
#include "util.h"

/*
 * All these constants are specific to the Pit board.  If we reuse this command
 * on other boards, we'll need to move the table to board.c.
 */
static const struct {
	const char *name;
	int voltage_mv;
	int current_range_ma;
} pmu_fet[] = {
	{"backlight", 11400, 1100},
	{"video"    ,  5000,  220},
	{"wwan"     ,  3300, 3300},
	{"sdcard"   ,  3300, 1100},
	{"camera"   ,  3300, 1100},
	{"lcd"      ,  3300, 1100},
	{"video_add",  5000, 1100},
}, pmu_dcdc[] = {
	{"p5000", 5050, 5000},
	{"p3300", 3333, 5000},
	{"p1350", 1350, 5000},
};

/* These constants may be Pit-specific as well. */
static const int pmu_voltage_range_mv = 17000;
static const int pmu_ac_sense_range_mv = 33;
static const int pmu_bat_sense_range_mv = 40;
static const int pmu_adc_resolution = 1024;
static const int pmu_sense_resistor_bat = CONFIG_CHARGER_SENSE_RESISTOR;
static const int pmu_sense_resistor_ac = CONFIG_CHARGER_SENSE_RESISTOR_AC;

static inline int calc_voltage(int adc_value, int range_mv)
{
	return adc_value * range_mv / pmu_adc_resolution;
}

static inline int calc_current(int adc_value, int range_ma)
{
	return adc_value * range_ma / pmu_adc_resolution;
}

static inline int calc_current_sr(int adc_value, int sense_resistor_mohm,
				  int range_mv)
{
	return adc_value * range_mv * 1000 / sense_resistor_mohm /
		pmu_adc_resolution;
}

static int command_powerinfo(int argc, char **argv)
{
	int voltage, current;
	int index;

	ccputs("[pmu powerinfo]\n");
	/* DC to DC converter */
	for (index = 0; index < ARRAY_SIZE(pmu_dcdc); index++) {
		current = calc_current(
			pmu_adc_read(ADC_IDCDC1 + index, ADC_FLAG_KEEP_ON),
			pmu_dcdc[index].current_range_ma);
		voltage = pmu_dcdc[index].voltage_mv;
		ccprintf("DCDC%d:%6d mV,%4d mA,%4d mW %s\n",
			 index + 1, voltage, current, voltage * current / 1000,
			 pmu_dcdc[index].name);
	}

	/* FET */
	for (index = 0; index < ARRAY_SIZE(pmu_fet); index++) {
		current = calc_current(
			pmu_adc_read(ADC_IFET1 + index, ADC_FLAG_KEEP_ON),
			pmu_fet[index].current_range_ma);
		voltage = pmu_fet[index].voltage_mv;
		ccprintf("FET%d :%6d mV,%4d mA,%4d mW %s\n",
			 index + 1, voltage, current, voltage * current / 1000,
			 pmu_fet[index].name);
	}

	/* Battery charging */
	voltage = calc_voltage(
		pmu_adc_read(ADC_VBAT, ADC_FLAG_KEEP_ON),
		pmu_voltage_range_mv);
	current = calc_current_sr(
		pmu_adc_read(ADC_IBAT, ADC_FLAG_KEEP_ON),
		pmu_sense_resistor_bat, pmu_bat_sense_range_mv);
	ccprintf("Chg  :%6d mV,%4d mA,%4d mW\n", voltage, current,
		 voltage * current / 1000);

	/* AC input */
	voltage = calc_voltage(
		pmu_adc_read(ADC_VAC, ADC_FLAG_KEEP_ON),
		pmu_voltage_range_mv);
	current = calc_current_sr(
		pmu_adc_read(ADC_IAC, 0),
		pmu_sense_resistor_ac, pmu_ac_sense_range_mv);
	ccprintf("AC   :%6d mV,%4d mA,%4d mW\n", voltage, current,
		 voltage * current / 1000);


	return EC_SUCCESS;
}
DECLARE_CONSOLE_COMMAND(powerinfo, command_powerinfo,
		NULL,
		"Show PMU power info",
		NULL);

/**
 * Host command to get power info from PMU
 *
 * This reuses the same EC_CMD_POWER_INFO host command as Spring, but doesn't
 * provide the full set of information because Pit doesn't take power over USB.
 */
static int power_command_info(struct host_cmd_handler_args *args)
{
	struct ec_response_power_info *r = args->response;

	r->voltage_ac = calc_voltage(
		pmu_adc_read(ADC_VAC, ADC_FLAG_KEEP_ON),
		pmu_voltage_range_mv);
	if (extpower_is_present()) {
		/* Power source = AC */
		r->voltage_system = r->voltage_ac;
		r->current_system = calc_current_sr(
			pmu_adc_read(ADC_IAC, ADC_FLAG_KEEP_ON),
			pmu_sense_resistor_ac, pmu_ac_sense_range_mv);
	} else {
		/* Power source == battery */
		struct batt_params batt;

		r->voltage_system = calc_voltage(
			pmu_adc_read(ADC_VBAT, ADC_FLAG_KEEP_ON),
			pmu_voltage_range_mv);
		/*
		 * PMU reads charging current. When battery is discharging, ADC
		 * returns 0. Use battery gas gauge output instead.
		 */
		battery_get_params(&batt);
		r->current_system = -batt.current;
	}

	/* Ignore USB powerinfo fields. */
	r->usb_dev_type = 0;
	r->usb_current_limit = 0;

	args->response_size = sizeof(*r);

	return EC_RES_SUCCESS;
}
DECLARE_HOST_COMMAND(EC_CMD_POWER_INFO, power_command_info, EC_VER_MASK(0));
