/* Copyright 2019 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.
 */

/* Zork family-specific configuration */

#include "adc.h"
#include "adc_chip.h"
#include "button.h"
#include "cbi_ec_fw_config.h"
#include "charge_manager.h"
#include "charge_ramp.h"
#include "charge_state.h"
#include "charge_state_v2.h"
#include "common.h"
#include "compile_time_macros.h"
#include "console.h"
#include "cros_board_info.h"
#include "driver/accelgyro_bmi_common.h"
#include "driver/charger/isl9241.h"
#include "driver/retimer/pi3hdx1204.h"
#include "driver/usb_mux/amd_fp5.h"
#include "ec_commands.h"
#include "extpower.h"
#include "gpio.h"
#include "hooks.h"
#include "ioexpander.h"
#include "i2c.h"
#include "keyboard_scan.h"
#include "lid_switch.h"
#include "motion_sense.h"
#include "power.h"
#include "power_button.h"
#include "pwm.h"
#include "pwm_chip.h"
#include "registers.h"
#include "switch.h"
#include "system.h"
#include "task.h"
#include "tcpci.h"
#include "temp_sensor.h"
#include "thermistor.h"
#include "usb_mux.h"
#include "usb_pd.h"
#include "util.h"

#define SAFE_RESET_VBUS_MV 5000

/*
 * For legacy BC1.2 charging with CONFIG_CHARGE_RAMP_SW, ramp up input current
 * until voltage drops to 4.5V. Don't go lower than this to be kind to the
 * charger (see b/67964166).
 */
#define BC12_MIN_VOLTAGE 4500

const enum gpio_signal hibernate_wake_pins[] = {
	GPIO_LID_OPEN,
	GPIO_AC_PRESENT,
	GPIO_POWER_BUTTON_L,
	GPIO_EC_RST_ODL,
};
const int hibernate_wake_pins_used =  ARRAY_SIZE(hibernate_wake_pins);

/*
 * In the AOZ1380 PPC, there are no programmable features.  We use
 * the attached NCT3807 to control a GPIO to indicate 1A5 or 3A0
 * current limits.
 */
__overridable int board_aoz1380_set_vbus_source_current_limit(int port,
						enum tcpc_rp_value rp)
{
	int rv;

	/* Use the TCPC to set the current limit */
	rv = ioex_set_level(IOEX_USB_C0_PPC_ILIM_3A_EN,
			    (rp == TYPEC_RP_3A0) ? 1 : 0);

	return rv;
}

static void baseboard_chipset_suspend(void)
{
	/* Disable display and keyboard backlights. */
	gpio_set_level(GPIO_ENABLE_BACKLIGHT_L, 1);
	ioex_set_level(IOEX_KB_BL_EN, 0);
}
DECLARE_HOOK(HOOK_CHIPSET_SUSPEND, baseboard_chipset_suspend,
	     HOOK_PRIO_DEFAULT);

static void baseboard_chipset_resume(void)
{
	/* Enable display and keyboard backlights. */
	gpio_set_level(GPIO_ENABLE_BACKLIGHT_L, 0);
	ioex_set_level(IOEX_KB_BL_EN, 1);
}
DECLARE_HOOK(HOOK_CHIPSET_RESUME, baseboard_chipset_resume, HOOK_PRIO_DEFAULT);

__overridable void board_set_charge_limit(int port, int supplier, int charge_ma,
			    int max_ma, int charge_mv)
{
	charge_set_input_current_limit(MAX(charge_ma,
					   CONFIG_CHARGER_INPUT_CURRENT),
				       charge_mv);
}

/* Keyboard scan setting */
struct keyboard_scan_config keyscan_config = {
	/*
	 * F3 key scan cycle completed but scan input is not
	 * charging to logic high when EC start scan next
	 * column for "T" key, so we set .output_settle_us
	 * to 80us
	 */
	.output_settle_us = 80,
	.debounce_down_us = 6 * MSEC,
	.debounce_up_us = 30 * MSEC,
	.scan_period_us = 1500,
	.min_post_scan_delay_us = 1000,
	.poll_timeout_us = SECOND,
	.actual_key_mask = {
		0x3c, 0xff, 0xff, 0xff, 0xff, 0xf5, 0xff,
		0xa4, 0xff, 0xfe, 0x55, 0xfa, 0xca  /* full set */
	},
};

/*
 * We use 11 as the scaling factor so that the maximum mV value below (2761)
 * can be compressed to fit in a uint8_t.
 */
#define THERMISTOR_SCALING_FACTOR 11

/*
 * Values are calculated from the "Resistance VS. Temperature" table on the
 * Murata page for part NCP15WB473F03RC. Vdd=3.3V, R=30.9Kohm.
 */
const struct thermistor_data_pair thermistor_data[] = {
	{ 2761 / THERMISTOR_SCALING_FACTOR, 0},
	{ 2492 / THERMISTOR_SCALING_FACTOR, 10},
	{ 2167 / THERMISTOR_SCALING_FACTOR, 20},
	{ 1812 / THERMISTOR_SCALING_FACTOR, 30},
	{ 1462 / THERMISTOR_SCALING_FACTOR, 40},
	{ 1146 / THERMISTOR_SCALING_FACTOR, 50},
	{ 878 / THERMISTOR_SCALING_FACTOR, 60},
	{ 665 / THERMISTOR_SCALING_FACTOR, 70},
	{ 500 / THERMISTOR_SCALING_FACTOR, 80},
	{ 434 / THERMISTOR_SCALING_FACTOR, 85},
	{ 376 / THERMISTOR_SCALING_FACTOR, 90},
	{ 326 / THERMISTOR_SCALING_FACTOR, 95},
	{ 283 / THERMISTOR_SCALING_FACTOR, 100}
};

const struct thermistor_info thermistor_info = {
	.scaling_factor = THERMISTOR_SCALING_FACTOR,
	.num_pairs = ARRAY_SIZE(thermistor_data),
	.data = thermistor_data,
};

#ifndef TEST_BUILD
void lid_angle_peripheral_enable(int enable)
{
	if (ec_config_has_lid_angle_tablet_mode()) {
		int chipset_in_s0 = chipset_in_state(CHIPSET_STATE_ON);

		if (enable) {
			keyboard_scan_enable(1, KB_SCAN_DISABLE_LID_ANGLE);
		} else {
			/*
			 * Ensure that the chipset is off before disabling the
			 * keyboard. When the chipset is on, the EC keeps the
			 * keyboard enabled and the AP decides whether to
			 * ignore input devices or not.
			 */
			if (!chipset_in_s0)
				keyboard_scan_enable(0,
						     KB_SCAN_DISABLE_LID_ANGLE);
		}
	}
}
#endif

static void cbi_init(void)
{
	uint32_t val;

	if (cbi_get_board_version(&val) == EC_SUCCESS)
		ccprints("Board Version: %d (0x%x)", val, val);
	else
		ccprints("Board Version: not set in cbi");

	if (cbi_get_sku_id(&val) == EC_SUCCESS)
		ccprints("SKU ID: %d (0x%x)", val, val);
	else
		ccprints("SKU ID: not set in cbi");

	val = get_cbi_fw_config();
	if (val != UNINITIALIZED_FW_CONFIG)
		ccprints("FW Config: %d (0x%x)", val, val);
	else
		ccprints("FW Config: not set in cbi");
}
DECLARE_HOOK(HOOK_INIT, cbi_init, HOOK_PRIO_INIT_I2C + 1);

/*
 * Returns 1 for boards that are convertible into tablet mode, and zero for
 * clamshells.
 */
int board_is_lid_angle_tablet_mode(void)
{
	return ec_config_has_lid_angle_tablet_mode();
}

__override uint32_t board_override_feature_flags0(uint32_t flags0)
{
	/*
	 * Remove keyboard backlight feature for devices that don't support it.
	 */
	if (ec_config_has_pwm_keyboard_backlight() == PWM_KEYBOARD_BACKLIGHT_NO)
		return (flags0 & ~EC_FEATURE_MASK_0(EC_FEATURE_PWM_KEYB));
	else
		return flags0;
}

void board_hibernate(void)
{
	int port;

	/*
	 * If we are charging, then drop the Vbus level down to 5V to ensure
	 * that we don't get locked out of the 6.8V OVLO for our PPCs in
	 * dead-battery mode. This is needed when the TCPC/PPC rails go away.
	 * (b/79218851, b/143778351, b/147007265)
	 */
	port = charge_manager_get_active_charge_port();
	if (port != CHARGE_PORT_NONE) {
		pd_request_source_voltage(port, SAFE_RESET_VBUS_MV);

		/* Give PD task and PPC chip time to get to 5V */
		msleep(900);
	}
}

__overridable int check_hdmi_hpd_status(void)
{
	/* Default hdmi insert. */
	return 1;
}

void sbu_fault_interrupt(enum ioex_signal signal)
{
	int port = (signal == IOEX_USB_C0_SBU_FAULT_ODL) ? 0 : 1;

	pd_handle_overcurrent(port);
}

static void set_ac_prochot(void)
{
	isl9241_set_ac_prochot(CHARGER_SOLO, ZORK_AC_PROCHOT_CURRENT_MA);
}
DECLARE_HOOK(HOOK_INIT, set_ac_prochot, HOOK_PRIO_DEFAULT);

DECLARE_DEFERRED(board_print_temps);
int temps_interval;

void board_print_temps(void)
{
	int t, i;
	int rv;

	cprintf(CC_THERMAL, "[%pT ", PRINTF_TIMESTAMP_NOW);
	for (i = 0; i < TEMP_SENSOR_COUNT; ++i) {
		rv = temp_sensor_read(i, &t);
		if (rv == EC_SUCCESS)
			cprintf(CC_THERMAL, "%s=%dK (%dC) ",
				temp_sensors[i].name, t, K_TO_C(t));
	}
	cprintf(CC_THERMAL, "]\n");

	if (temps_interval > 0)
		hook_call_deferred(&board_print_temps_data,
				   temps_interval * SECOND);
}

static int command_temps_log(int argc, char **argv)
{
	char *e = NULL;

	if (argc != 2)
		return EC_ERROR_PARAM_COUNT;

	temps_interval = strtoi(argv[1], &e, 0);
	if (*e)
		return EC_ERROR_PARAM1;

	board_print_temps();

	return EC_SUCCESS;
}
DECLARE_CONSOLE_COMMAND(tempslog, command_temps_log,
			"seconds",
			"Print temp sensors periodically");

/*
 * b/164921478: On G3->S5, wait for RSMRST_L to be deasserted before asserting
 * PWRBTN_L.
 */
void board_pwrbtn_to_pch(int level)
{
	/* Add delay for G3 exit if asserting PWRBTN_L and S5_PGOOD is low. */
	if (!level && !gpio_get_level(GPIO_S5_PGOOD)) {
		/*
		 * From measurement, wait 80 ms for RSMRST_L to rise after
		 * S5_PGOOD.
		 */
		msleep(80);

		if (!gpio_get_level(GPIO_S5_PGOOD))
			ccprints("Error: pwrbtn S5_PGOOD low");
	}
	gpio_set_level(GPIO_PCH_PWRBTN_L, level);
}

/**
 * Return if VBUS is sagging too low
 */
int board_is_vbus_too_low(int port, enum chg_ramp_vbus_state ramp_state)
{
	int voltage = 0;
	int rv;

	rv = charger_get_vbus_voltage(port, &voltage);

	if (rv) {
		ccprints("%s rv=%d", __func__, rv);
		return 0;
	}

	/*
	 * b/168569046: The ISL9241 sometimes incorrectly reports 0 for unknown
	 * reason, causing ramp to stop at 0.5A. Workaround this by ignoring 0.
	 * This partly defeats the point of ramping, but will still catch
	 * VBUS below 4.5V and above 0V.
	 */
	if (voltage == 0) {
		ccprints("%s vbus=0", __func__);
		return 0;
	}

	if (voltage < BC12_MIN_VOLTAGE)
		ccprints("%s vbus=%d", __func__, voltage);

	return voltage < BC12_MIN_VOLTAGE;
}

/**
 * Always ramp up input current since AP needs higher power, even if battery is
 * very low or full. We can always re-ramp if input current increases beyond
 * what supplier can provide.
 */
__override int charge_is_consuming_full_input_current(void)
{
	return 1;
}
