From e34e178ca3f31064e9c4cced4243524d08579007 Mon Sep 17 00:00:00 2001 From: Nico Huber Date: Thu, 29 Sep 2016 12:33:01 +0200 Subject: sio/ite/common: Add generic environment-controller driver The environment-controller entity is shared by many ITE super-i/o chips. There are some differences between the chips, though. To cover that, the super-i/o chip should select Kconfig options of this driver accordingly. The current implementation isn't exhaustive: It covers only those parts that are connected on boards I could test, plus those that are currently used by the IT8772F. The latter could be ported to use this driver if somebody minds to test it. Change-Id: I7a40f677f667d103ce1d09a3e468915729067803 Signed-off-by: Nico Huber Reviewed-on: https://review.coreboot.org/17284 Tested-by: build bot (Jenkins) Reviewed-by: Martin Roth --- src/superio/ite/common/env_ctrl.c | 250 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 250 insertions(+) create mode 100644 src/superio/ite/common/env_ctrl.c (limited to 'src/superio/ite/common/env_ctrl.c') diff --git a/src/superio/ite/common/env_ctrl.c b/src/superio/ite/common/env_ctrl.c new file mode 100644 index 0000000000..4ce6ccc50d --- /dev/null +++ b/src/superio/ite/common/env_ctrl.c @@ -0,0 +1,250 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2011 The ChromiumOS Authors. All rights reserved. + * Copyright (C) 2016 secunet Security Networks AG + * + * 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. + */ + +#include +#include +#include +#include + +#include "env_ctrl.h" +#include "env_ctrl_chip.h" + +static inline u8 ite_ec_read(const u16 base, const u8 addr) +{ + outb(addr, base + 5); + return inb(base + 6); +} + +static inline void ite_ec_write(const u16 base, const u8 addr, const u8 value) +{ + outb(addr, base + 5); + outb(value, base + 6); +} + +static void extemp_force_idle_status(const u16 base) +{ + u8 reg; + int retries = 10; + + /* Wait up to 10ms for non-busy state. */ + while (retries > 0) { + reg = ite_ec_read(base, ITE_EC_EXTEMP_STATUS); + + if ((reg & ITE_EC_EXTEMP_STATUS_HOST_BUSY) == 0x0) + break; + + retries--; + + mdelay(1); + } + + if (retries == 0 && (reg & ITE_EC_EXTEMP_STATUS_HOST_BUSY) == 0x1) { + /* + * SIO is busy due to unfinished peci transaction. + * Re-configure Register 0x8E to terminate processes. + */ + ite_ec_write(base, ITE_EC_EXTEMP_CONTROL, + ITE_EC_EXTEMP_CTRL_AUTO_4HZ | + ITE_EC_EXTEMP_CTRL_AUTO_START); + } +} + +/* + * Setup External Temperature to read via PECI into TMPINx register + */ +static void enable_peci(const u16 base, const u8 tmpin) +{ + if (tmpin == 0 || tmpin > ITE_EC_TMPIN_CNT) + return; + + /* Enable PECI interface */ + ite_ec_write(base, ITE_EC_INTERFACE_SELECT, + ITE_EC_INTERFACE_SEL_PECI | + ITE_EC_INTERFACE_SPEED_TOLERANCE); + + /* Setup External Temperature using PECI GetTemp */ + ite_ec_write(base, ITE_EC_EXTEMP_ADDRESS, + PECI_CLIENT_ADDRESS); + ite_ec_write(base, ITE_EC_EXTEMP_COMMAND, + PECI_GETTEMP_COMMAND); + ite_ec_write(base, ITE_EC_EXTEMP_WRITE_LENGTH, + PECI_GETTEMP_WRITE_LENGTH); + ite_ec_write(base, ITE_EC_EXTEMP_READ_LENGTH, + PECI_GETTEMP_READ_LENGTH); + ite_ec_write(base, ITE_EC_EXTEMP_CONTROL, + ITE_EC_EXTEMP_CTRL_AUTO_4HZ | + ITE_EC_EXTEMP_CTRL_AUTO_START); + + /* External Temperature reported in TMPINx register */ + ite_ec_write(base, ITE_EC_ADC_TEMP_CHANNEL_ENABLE, + (tmpin & 3) << 6); +} + +/* + * Set up External Temperature to read via thermal diode/resistor + * into TMPINx register + */ +static void enable_tmpin(const u16 base, const int tmpin, + const enum ite_ec_thermal_mode mode) +{ + u8 reg; + + reg = ite_ec_read(base, ITE_EC_ADC_TEMP_CHANNEL_ENABLE); + + switch (mode) { + case THERMAL_DIODE: + reg |= ITE_EC_ADC_TEMP_DIODE_MODE(tmpin); + break; + case THERMAL_RESISTOR: + reg |= ITE_EC_ADC_TEMP_RESISTOR_MODE(tmpin); + break; + default: + printk(BIOS_WARNING, + "Unsupported thermal mode 0x%x on TMPIN%d\n", + mode, tmpin); + return; + } + + ite_ec_write(base, ITE_EC_ADC_TEMP_CHANNEL_ENABLE, reg); + + /* Enable the startup of monitoring operation */ + reg = ite_ec_read(base, ITE_EC_CONFIGURATION); + reg |= ITE_EC_CONFIGURATION_START; + ite_ec_write(base, ITE_EC_CONFIGURATION, reg); +} + +static void fan_smartconfig(const u16 base, const u8 fan, + const enum ite_ec_fan_mode mode, + const struct ite_ec_fan_smartconfig *const conf) +{ + u8 pwm_ctrl; + u8 pwm_start = 0; + u8 pwm_auto = 0; + + if (mode == FAN_SMART_SOFTWARE) { + pwm_ctrl = ITE_EC_FAN_CTL_PWM_MODE_SOFTWARE; + + /* 50% duty cycle by default */ + const u8 duty = conf->pwm_start ? conf->pwm_start : 50; + if (IS_ENABLED(CONFIG_SUPERIO_ITE_ENV_CTRL_8BIT_PWM)) + pwm_start = ITE_EC_FAN_CTL_PWM_DUTY(duty); + else + pwm_ctrl |= ITE_EC_FAN_CTL_PWM_DUTY(duty); + } else { + pwm_ctrl = ITE_EC_FAN_CTL_PWM_MODE_AUTOMATIC; + pwm_ctrl |= ITE_EC_FAN_CTL_TEMPIN(conf->tmpin); + + pwm_start = ITE_EC_FAN_CTL_PWM_START_DUTY(conf->pwm_start); + pwm_start |= ITE_EC_FAN_CTL_PWM_SLOPE_BIT6(conf->slope); + + pwm_auto = ITE_EC_FAN_CTL_PWM_SLOPE_LOWER(conf->slope); + + ite_ec_write(base, ITE_EC_FAN_CTL_TEMP_LIMIT_OFF(fan), + conf->tmp_off); + ite_ec_write(base, ITE_EC_FAN_CTL_TEMP_LIMIT_START(fan), + conf->tmp_start); + /* Full speed above 127°C by default */ + ite_ec_write(base, ITE_EC_FAN_CTL_TEMP_LIMIT_FULL(fan), + conf->tmp_full ? conf->tmp_full : 127); + ite_ec_write(base, ITE_EC_FAN_CTL_DELTA_TEMP(fan), + ITE_EC_FAN_CTL_DELTA_TEMP_INTRVL(conf->tmp_delta)); + } + + ite_ec_write(base, ITE_EC_FAN_CTL_PWM_CONTROL(fan), pwm_ctrl); + ite_ec_write(base, ITE_EC_FAN_CTL_PWM_START(fan), pwm_start); + ite_ec_write(base, ITE_EC_FAN_CTL_PWM_AUTO(fan), pwm_auto); +} + +static void enable_fan(const u16 base, const u8 fan, + const struct ite_ec_fan_config *const conf) +{ + u8 reg; + + if (conf->mode == FAN_IGNORE) + return; + + /* FAN_CTL2 might have its own frequency setting */ + if (IS_ENABLED(CONFIG_SUPERIO_ITE_ENV_CTRL_PWM_FREQ2) && fan == 2) { + reg = ite_ec_read(base, ITE_EC_ADC_TEMP_EXTRA_CHANNEL_ENABLE); + reg &= ~ITE_EC_FAN_PWM_CLOCK_MASK; + reg |= ITE_EC_FAN_PWM_DEFAULT_CLOCK; + ite_ec_write(base, ITE_EC_ADC_TEMP_EXTRA_CHANNEL_ENABLE, reg); + } + + if (conf->mode >= FAN_SMART_SOFTWARE) { + fan_smartconfig(base, fan, conf->mode, &conf->smart); + } else { + reg = ite_ec_read(base, ITE_EC_FAN_CTL_MODE); + if (conf->mode == FAN_MODE_ON) + reg |= ITE_EC_FAN_CTL_ON(fan); + else + reg &= ~ITE_EC_FAN_CTL_ON(fan); + ite_ec_write(base, ITE_EC_FAN_CTL_MODE, reg); + } + + if (IS_ENABLED(SUPERIO_ITE_ENV_CTRL_FAN16_CONFIG) + && conf->mode >= FAN_MODE_ON) { + reg = ite_ec_read(base, ITE_EC_FAN_TAC_COUNTER_ENABLE); + reg |= ITE_EC_FAN_TAC_16BIT_ENABLE(fan); + ite_ec_write(base, ITE_EC_FAN_TAC_COUNTER_ENABLE, reg); + } + + reg = ite_ec_read(base, ITE_EC_FAN_MAIN_CTL); + if (conf->mode >= FAN_MODE_ON) + reg |= ITE_EC_FAN_MAIN_CTL_TAC_EN(fan); + else + reg &= ~ITE_EC_FAN_MAIN_CTL_TAC_EN(fan); + if (conf->mode >= FAN_SMART_SOFTWARE) + reg |= ITE_EC_FAN_MAIN_CTL_SMART(fan); + else + reg &= ~ITE_EC_FAN_MAIN_CTL_SMART(fan); + ite_ec_write(base, ITE_EC_FAN_MAIN_CTL, reg); +} + +void ite_ec_init(const u16 base, const struct ite_ec_config *const conf) +{ + size_t i; + + /* Configure 23.43kHz PWM active high output */ + u8 fan_ctl = ite_ec_read(base, ITE_EC_FAN_CTL_MODE); + fan_ctl &= ~ITE_EC_FAN_PWM_CLOCK_MASK; + fan_ctl |= ITE_EC_FAN_PWM_DEFAULT_CLOCK; + fan_ctl |= ITE_EC_FAN_CTL_POLARITY_HIGH; + ite_ec_write(base, ITE_EC_FAN_CTL_MODE, fan_ctl); + + /* Enable PECI if configured */ + enable_peci(base, conf->peci_tmpin); + + /* Enable HWM if configured */ + for (i = 0; i < ITE_EC_TMPIN_CNT; ++i) + enable_tmpin(base, i + 1, conf->tmpin_mode[i]); + + /* Enable reading of voltage pins */ + ite_ec_write(base, ITE_EC_ADC_VOLTAGE_CHANNEL_ENABLE, conf->vin_mask); + + /* Enable FANx if configured */ + for (i = 0; i < ITE_EC_FAN_CNT; ++i) + enable_fan(base, i + 1, &conf->fan[i]); + + /* + * System may get wrong temperature data when SIO is in + * busy state. Therefore, check the status and terminate + * processes if needed. + */ + if (conf->peci_tmpin != 0) + extemp_force_idle_status(base); +} -- cgit v1.2.3