superio/fintek: Add f81803A

Add f81803A plus the capability to control the fan with any fintek SIO.
This will be done through a common API, though currently only F81803A will
have it implemented.

BUG=none.
TEST=Tested later with padmelon board.

Change-Id: I3d336e76bccc38452b1b1aefef5d4a4f7ee129a8
Signed-off-by: Richard Spiegel <richard.spiegel@silverbackltd.com>
Reviewed-on: https://review.coreboot.org/c/coreboot/+/33623
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Felix Held <felix-coreboot@felixheld.de>
This commit is contained in:
Richard Spiegel 2019-06-19 13:57:55 -07:00 committed by Felix Held
parent 150a61e103
commit ae5b3671b3
10 changed files with 1170 additions and 0 deletions

View File

@ -26,3 +26,4 @@ subdirs-y += f71872
subdirs-y += f81216h
subdirs-y += f81865f
subdirs-y += f81866d
subdirs-y += f81803a

View File

@ -0,0 +1,63 @@
/*
* This file is part of the coreboot project.
*
* Copyright (C) 2019 Richard Spiegel <richard.spiegel@silverbackltd.com>
* Copyright (C) 2019 Silverback ltd.
*
* 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; version 2 of the License.
*
* 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 <console/console.h>
#include "fan_control.h"
static int check_status(int status)
{
if (status < HWM_STATUS_SUCCESS)
return status;
return HWM_STATUS_SUCCESS; /* positive values are warnings only */
}
int set_fan(struct fintek_fan *fan_init)
{
int s;
s = set_sensor_type(CONFIG_HWM_PORT, fan_init->sensor, fan_init->stype);
if (check_status(s) != HWM_STATUS_SUCCESS)
return s;
s = set_fan_temperature_source(CONFIG_HWM_PORT, fan_init->fan, fan_init->temp_source);
if (check_status(s) != HWM_STATUS_SUCCESS)
return s;
s = set_fan_type_mode(CONFIG_HWM_PORT, fan_init->fan, fan_init->ftype, fan_init->fmode);
if (check_status(s) != HWM_STATUS_SUCCESS)
return s;
s = set_pwm_frequency(CONFIG_HWM_PORT, fan_init->fan, fan_init->fan_freq);
if (check_status(s) != HWM_STATUS_SUCCESS)
return s;
s = set_fan_speed_change_rate(CONFIG_HWM_PORT, fan_init->fan, fan_init->rate_up,
fan_init->rate_down);
if (check_status(s) != HWM_STATUS_SUCCESS)
return s;
s = set_fan_follow(CONFIG_HWM_PORT, fan_init->fan, fan_init->follow);
if (check_status(s) != HWM_STATUS_SUCCESS)
return s;
s = set_sections(CONFIG_HWM_PORT, fan_init->fan, fan_init->boundaries,
fan_init->sections);
if (check_status(s) != HWM_STATUS_SUCCESS)
return s;
printk(BIOS_DEBUG, "Fan %d completed\n", fan_init->fan);
return HWM_STATUS_SUCCESS;
}

View File

@ -0,0 +1,199 @@
/*
* This file is part of the coreboot project.
*
* Copyright (C) 2019 Richard Spiegel <richard.spiegel@silverbackltd.com>
* Copyright (C) 2019 Silverback ltd.
*
* 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; version 2 of the License.
*
* 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.
*/
#ifndef SUPERIO_FINTEK_FAN_CONTROL_H
#define SUPERIO_FINTEK_FAN_CONTROL_H
#include <stdint.h>
#include <arch/io.h>
typedef enum {
IGNORE_SENSOR = 0,
EXTERNAL_SENSOR1,
EXTERNAL_SENSOR2,
EXTERNAL_SENSOR3,
EXTERNAL_SENSOR4
} external_sensor;
typedef enum {
TEMP_SENSOR_THERMISTOR = 0,
TEMP_SENSOR_BJT,
TEMP_SENSOR_DEFAULT
} temp_sensor_type;
typedef enum {
FAN_TYPE_PWM_PUSH_PULL = 0,
FAN_TYPE_DAC_POWER,
FAN_TYPE_PWM_OPEN_DRAIN,
FAN_TYPE_RESERVED
} fan_type;
#define FAN_TYPE_PWM_CHECK 1 /* bit 0 must be 0 for PWM */
typedef enum {
FAN_MODE_AUTO_RPM = 0,
FAN_MODE_AUTO_PWM_DAC,
FAN_MODE_MANUAL_RPM,
FAN_MODE_MANUAL_PWM_DAC,
FAN_MODE_DEFAULT
} fan_mode;
typedef enum {
FAN_PWM_FREQ_23500 = 0,
FAN_PWM_FREQ_11750,
FAN_PWM_FREQ_5875,
FAN_PWM_FREQ_220
} fan_pwm_freq;
typedef enum {
FAN_TEMP_PECI = 0,
FAN_TEMP_EXTERNAL_1,
FAN_TEMP_EXTERNAL_2,
FAN_TEMP_TSI = 4,
FAN_TEMP_MXM,
} fan_temp_source;
typedef enum {
FAN_UP_RATE_2HZ = 0,
FAN_UP_RATE_5HZ,
FAN_UP_RATE_10HZ,
FAN_UP_RATE_20HZ,
FAN_UP_RATE_DEFAULT,
FAN_UP_RATE_JUMP = 8
} fan_rate_up;
typedef enum {
FAN_DOWN_RATE_2HZ = 0,
FAN_DOWN_RATE_5HZ,
FAN_DOWN_RATE_10HZ,
FAN_DOWN_RATE_20HZ,
FAN_DOWN_RATE_DEFAULT,
FAN_DOWN_RATE_SAME_AS_UP,
FAN_DOWN_RATE_JUMP = 8
} fan_rate_down;
typedef enum {
FAN_FOLLOW_STEP = 0,
FAN_FOLLOW_INTERPOLATION
} fan_follow;
struct fintek_fan {
uint8_t fan;
external_sensor sensor;
temp_sensor_type stype;
fan_temp_source temp_source;
fan_type ftype;
fan_mode fmode;
fan_pwm_freq fan_freq;
fan_rate_up rate_up;
fan_rate_down rate_down;
fan_follow follow;
uint8_t *boundaries;
uint8_t *sections;
};
#define HWM_STATUS_SUCCESS 0
#define HWM_STATUS_INVALID_FAN -1
#define HWM_STATUS_INVALID_TEMP_SOURCE -2
#define HWM_STATUS_INVALID_TYPE -3
#define HWM_STATUS_INVALID_MODE -4
#define HWM_STATUS_INVALID_RATE -5
#define HWM_STATUS_INVALID_FREQUENCY -6
#define HWM_STATUS_INVALID_TEMP_SENSOR -7
#define HWM_STATUS_INVALID_BOUNDARY_VALUE -8
#define HWM_STATUS_INVALID_SECTION_VALUE -9
#define HWM_STATUS_BOUNDARY_WRONG_ORDER -10
#define HWM_STATUS_SECTIONS_WRONG_ORDER -11
#define HWM_STATUS_WARNING_SENSOR_DISCONECTED 1
#define HWM_STATUS_WARNING_FAN_NOT_PWM 2
#define CPU_DAMAGE_TEMP 110
/*
* Boundaries order is from highest temp. to lowest. Values from 0 to 127.
* Boundaries should be defined as u8 boundaries[FINTEK_BOUNDARIES_SIZE].
*/
#define FINTEK_BOUNDARIES_SIZE 4
/*
* Section defines the duty_cycle/voltage to be used based on where the
* temperature lies with respect to the boundaries. There are 5 sections
* (4 boundaries) and the order must be from highest to lowest. Values
* from 0% to 100%, will be converted internally to percent of 255.
* Sections should be defined as u8 sections[FINTEK_SECTIONS_SIZE].
*/
#define FINTEK_SECTIONS_SIZE 5
/*
* When using external sensor, its type must be defined. When using PECI,
* TSI or MXM use IGNORE_SENSOR to indicate so.
*/
int set_sensor_type(u16 base_address, external_sensor sensor,
temp_sensor_type type);
/*
* Define the temperature source used to control a fan.
*/
int set_fan_temperature_source(u16 base_address, u8 fan,
fan_temp_source source);
/*
* Define if fan is controlled through PWM or absolute voltage powering it
* (DAC). Then, under mode, define if control is automatic (SIO) or manual
* (CPU, through ACPI). Notice there needs to be a match between type and
* mode (PWM with PWM or DAC with DAC).
*/
int set_fan_type_mode(u16 base_address, u8 fan, fan_type type, fan_mode mode);
/*
* For fans controlled through pulse width, define the base frequency used.
*/
int set_pwm_frequency(u16 base_address, u8 fan, fan_pwm_freq frequency);
/*
* For fintek SIO HWM there are 4 (temperature) boundaries points, defining
* 5 sections (1 fan speed per section). Start with the highest temperature/
* speed. Temperature is in Celsius, speed is in percentile of max speed. The
* highest speed should be 100%, no requirements for minimum speed, could be
* 0 or above 0.
*/
int set_sections(u16 base_address, u8 fan, u8 *boundaries, u8 *sections);
/*
* Define how often temperature is measured to change fan speed.
*/
int set_fan_speed_change_rate(u16 base_address, u8 fan, fan_rate_up rate_up,
fan_rate_down rate_down);
/*
* There a 2 ways a fan can be controlled: A single speed per section, or
* interpolation. Under interpolation, the section speed is the speed at the
* lowest temperature of the section (0 Celsius for the lowest section), and
* it's the speed of the next section at the boundary to the next section.
* In between these 2 points, it's a linear function. For example, midway
* between temperature points it'll have a speed that is midway between the
* section speed and next section speed. Obviously, there's no variation for
* the highest section, reason why it must be 100% max speed.
*/
int set_fan_follow(u16 base_address, u8 fan, fan_follow follow);
/*
* This is an upper level API which calls all the above APIs in the
* appropriate order. Any API failure will be displayed. Alerts will
* also be displayed, but will not interrupt the sequence, while errors
* will interrupt the sequence.
*/
int set_fan(struct fintek_fan *fan_init);
#endif /* SUPERIO_FINTEK_FAN_CONTROL_H */

View File

@ -0,0 +1,28 @@
##
## This file is part of the coreboot project.
##
## Copyright (C) 2009 Ronald G. Minnich
## Copyright (C) 2014 Edward O'Callaghan <eocallaghan@alterapraxis.com>
##
## 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; version 2 of the License.
##
## 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.
##
config SUPERIO_FINTEK_F81803A
bool
select SUPERIO_FINTEK_COMMON_PRE_RAM
config SUPERIO_FINTEK_FAN_CONTROL
bool
default n
config SUPERIO_FINTEK_FAN_API_CALL
depends on SUPERIO_FINTEK_FAN_CONTROL
bool
default n

View File

@ -0,0 +1,30 @@
##
## This file is part of the coreboot project.
##
## Copyright (C) 2011 Advanced Micro Devices, 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.
##
ifeq ($(CONFIG_BOOTBLOCK_CONSOLE),y)
bootblock-$(CONFIG_SUPERIO_FINTEK_F81803A) += ../common/early_serial.c
endif
## Notice: For fan control at romstage, HWM must be initialized before
## the API is called. Ramstage can use devicetree to initialize it.
romstage-$(CONFIG_SUPERIO_FINTEK_F81803A) += ../common/early_serial.c
romstage-$(CONFIG_SUPERIO_FINTEK_FAN_CONTROL) += fan_control.c
romstage-$(CONFIG_SUPERIO_FINTEK_FAN_API_CALL) += ../common/fan_api_call.c
ramstage-$(CONFIG_SUPERIO_FINTEK_F81803A) += superio.c
ramstage-$(CONFIG_SUPERIO_FINTEK_FAN_CONTROL) += fan_control.c
ramstage-$(CONFIG_SUPERIO_FINTEK_FAN_API_CALL) += ../common/fan_api_call.c

View File

@ -0,0 +1,266 @@
/*
* This file is part of the coreboot project.
*
* Copyright (C) 2011 Christoph Grenz <christophg+cb@grenz-bonn.de>
* Copyright (C) 2013 secunet Security Networks AG
* Copyright (C) 2019, Silverback, ltd.
*
* 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; version 2 of the License.
*
* 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 this file into a mainboard's DSDT _SB device tree and it will
* expose the F81803A SuperIO and some of its functionality.
*
* It allows the change of IO ports, IRQs and DMA settings on logical
* devices, disabling and reenabling logical devices and controlling power
* saving mode on logical devices or the whole chip.
*
* LDN State
* 0x1 UARTA Implemented, partially tested
* 0x2 UARTB Implemented, partially tested
* 0x4 HWM Not implemented
* 0x5 KBC Not implemented
* 0x6 GPIO6 Not implemented
* 0x7 WDT0&PLED Not implemented
* 0xa ACPI/PME/ERP Partially implemented
*
* Controllable through preprocessor defines:
* SUPERIO_DEV Device identifier for this SIO (e.g. SIO0)
* SUPERIO_PNP_BASE I/o address of the first PnP configuration register
* F81803A_SHOW_UARTA If defined, UARTA will be exposed.
* F81803A_SHOW_UARTB If defined, UARTB will be exposed.
* F81803A_SHOW_HWMON If defined, the hardware monitor will be exposed.
* F81803A_SHOW_PME If defined, the PME/EARP/ACPI will be exposed.
*
* Known issue:
* Do not enable UARTA and UARTB simultaneously, Linux boot will crash.
* Select one or the other.
*/
#undef SUPERIO_CHIP_NAME
#define SUPERIO_CHIP_NAME F81803A
#include <superio/acpi/pnp.asl>
#undef PNP_DEFAULT_PSC
#define PNP_DEFAULT_PSC Return (0) /* no power management */
Device(SUPERIO_DEV) {
Name (_HID, EisaId("PNP0A05"))
Name (_STR, Unicode("Fintek F81803A Super I/O"))
Name (_UID, SUPERIO_UID(SUPERIO_DEV,))
/* Mutex for accesses to the configuration ports */
Mutex(CRMX, 1)
/* SuperIO configuration ports */
OperationRegion (CREG, SystemIO, SUPERIO_PNP_BASE, 0x02)
Field (CREG, ByteAcc, NoLock, Preserve)
{
PNP_ADDR_REG, 8,
PNP_DATA_REG, 8
}
IndexField (ADDR, DATA, ByteAcc, NoLock, Preserve)
{
Offset (0x07),
PNP_LOGICAL_DEVICE, 8, /* Logical device selector */
Offset (0x30),
PNP_DEVICE_ACTIVE, 1, /* Logical device activation */
Offset (0x60),
PNP_IO0_HIGH_BYTE, 8, /* First I/O port base - high byte */
PNP_IO0_LOW_BYTE, 8, /* First I/O port base - low byte */
Offset (0x62),
PNP_IO1_HIGH_BYTE, 8, /* Second I/O port base - high byte */
PNP_IO1_LOW_BYTE, 8, /* Second I/O port base - low byte */
Offset (0x70),
PNP_IRQ0, 8, /* First IRQ */
offset(0xFB),
APC5, 8, /* PME ACPI Control Register 5 */
}
Method(_CRS)
{
/* Announce the used i/o ports to the OS */
Return (ResourceTemplate () {
IO (Decode16, SUPERIO_PNP_BASE, SUPERIO_PNP_BASE, 0x01, 0x02)
})
}
#undef PNP_ENTER_MAGIC_1ST
#undef PNP_ENTER_MAGIC_2ND
#undef PNP_ENTER_MAGIC_3RD
#undef PNP_ENTER_MAGIC_4TH
#undef PNP_EXIT_MAGIC_1ST
#undef PNP_EXIT_SPECIAL_REG
#undef PNP_EXIT_SPECIAL_VAL
#define PNP_ENTER_MAGIC_1ST 0x87
#define PNP_ENTER_MAGIC_2ND 0x87
#define PNP_EXIT_MAGIC_1ST 0xaa
#include <superio/acpi/pnp_config.asl>
#ifdef F81803A_SHOW_UARTA
#undef SUPERIO_UART_LDN
#undef SUPERIO_UART_PM_REG
#undef SUPERIO_UART_PM_VAL
#undef SUPERIO_UART_PM_LDN
#define SUPERIO_UART_LDN 1
Device (SUPERIO_ID(SER, SUPERIO_UART_LDN)) {
Name (_HID, EisaId ("PNP0501"))
Name (_UID, SUPERIO_UID(SER, SUPERIO_UART_LDN))
Method (_STA)
{
PNP_GENERIC_STA(SUPERIO_UART_LDN)
}
Method (_CRS, 0, Serialized)
{
Name (CRS, ResourceTemplate () {
IO (Decode16, 0x0000, 0x0000, 0x08, 0x08, IO0)
IRQNoFlags (IR0) {}
})
ENTER_CONFIG_MODE (SUPERIO_UART_LDN)
PNP_READ_IO(PNP_IO0, CRS, IO0)
PNP_READ_IRQ(PNP_IRQ0, CRS, IR0)
EXIT_CONFIG_MODE ()
Return (CRS)
}
Name (_PRS, ResourceTemplate ()
{
StartDependentFn (0,0) {
IO (Decode16, 0x03f8, 0x03f8, 0x08, 0x08)
IRQNoFlags () {3,4,5,7,9,10,11,12}
}
StartDependentFn (0,0) {
IO (Decode16, 0x02f8, 0x02f8, 0x08, 0x08)
IRQNoFlags () {3,4,5,7,9,10,11,12}
}
StartDependentFn (1,0) {
IO (Decode16, 0x03e8, 0x03e8, 0x08, 0x08)
IRQNoFlags () {3,4,5,7,9,10,11,12}
}
StartDependentFn (1,0) {
IO (Decode16, 0x02e8, 0x02e8, 0x08, 0x08)
IRQNoFlags () {3,4,5,7,9,10,11,12}
}
StartDependentFn (2,0) {
IO (Decode16, 0x0100, 0x0ff8, 0x08, 0x08)
IRQNoFlags () {3,4,5,7,9,10,11,12}
}
EndDependentFn()
})
Method (_SRS, 1, Serialized)
{
Name (TMPL, ResourceTemplate () {
IO (Decode16, 0x0000, 0x0000, 0x00, 0x00, IO0)
IRQNoFlags (IR0) {}
})
ENTER_CONFIG_MODE (SUPERIO_UART_LDN)
PNP_WRITE_IO(PNP_IO0, Arg0, IO0)
PNP_WRITE_IRQ(PNP_IRQ0, Arg0, IR0)
Store (One, PNP_DEVICE_ACTIVE)
EXIT_CONFIG_MODE ()
}
}
#endif
#ifdef F81803A_SHOW_UARTB
#undef SUPERIO_UART_LDN
#undef SUPERIO_UART_PM_REG
#undef SUPERIO_UART_PM_VAL
#undef SUPERIO_UART_PM_LDN
#define SUPERIO_UART_LDN 2
Device (SUPERIO_ID(SER, SUPERIO_UART_LDN)) {
Name (_HID, EisaId ("PNP0501"))
Name (_UID, SUPERIO_UID(SER, SUPERIO_UART_LDN))
Method (_STA)
{
PNP_GENERIC_STA(SUPERIO_UART_LDN)
}
Method (_CRS, 0, Serialized)
{
Name (CRS, ResourceTemplate () {
IO (Decode16, 0x0000, 0x0000, 0x08, 0x08, IO0)
IRQNoFlags (IR0) {}
})
ENTER_CONFIG_MODE (SUPERIO_UART_LDN)
PNP_READ_IO(PNP_IO0, CRS, IO0)
PNP_READ_IRQ(PNP_IRQ0, CRS, IR0)
EXIT_CONFIG_MODE ()
Return (CRS)
}
Name (_PRS, ResourceTemplate ()
{
StartDependentFn (0,0) {
IO (Decode16, 0x03f8, 0x03f8, 0x08, 0x08)
IRQNoFlags () {3,4,5,7,9,10,11,12}
}
StartDependentFn (0,0) {
IO (Decode16, 0x02f8, 0x02f8, 0x08, 0x08)
IRQNoFlags () {3,4,5,7,9,10,11,12}
}
StartDependentFn (1,0) {
IO (Decode16, 0x03e8, 0x03e8, 0x08, 0x08)
IRQNoFlags () {3,4,5,7,9,10,11,12}
}
StartDependentFn (1,0) {
IO (Decode16, 0x02e8, 0x02e8, 0x08, 0x08)
IRQNoFlags () {3,4,5,7,9,10,11,12}
}
StartDependentFn (2,0) {
IO (Decode16, 0x0100, 0x0ff8, 0x08, 0x08)
IRQNoFlags () {3,4,5,7,9,10,11,12}
}
EndDependentFn()
})
Method (_SRS, 1, Serialized)
{
Name (TMPL, ResourceTemplate () {
IO (Decode16, 0x0000, 0x0000, 0x00, 0x00, IO0)
IRQNoFlags (IR0) {}
})
ENTER_CONFIG_MODE (SUPERIO_UART_LDN)
PNP_WRITE_IO(PNP_IO0, Arg0, IO0)
PNP_WRITE_IRQ(PNP_IRQ0, Arg0, IR0)
Store (One, PNP_DEVICE_ACTIVE)
EXIT_CONFIG_MODE ()
}
}
#endif
#ifdef F81803A_SHOW_PME
#undef SUPERIO_PME_LDN
#define SUPERIO_PME_LDN 0x0A
OperationRegion(APCx, SystemIO, APC5, 0x01)
Field(APCx, ByteAcc, Nolock, Preserve) /* bits in PME ACPI CONTROL Reg 5*/
{
Offset(0x00), /*Control Reg 5 */
, 7,
PSIN, 1 /* PSIN_FLAG */
}
/* routine to clear PSIN_FLAG in ACPI_CONTROL_REG_5 of SIO */
Method(CPSI, 0, Serialized)
{
/* DBG0("SIO CPSI")*/
ENTER_CONFIG_MODE(SUPERIO_PME_LDN)
Store(1, PSIN)
EXIT_CONFIG_MODE()
}
#endif
}

View File

@ -0,0 +1,71 @@
/*
* This file is part of the coreboot project.
*
* Copyright (C) 2011 Advanced Micro Devices, 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.
*/
/*
* Datasheet:
* - Name: F81803A
*/
#ifndef SUPERIO_FINTEK_F81803_H
#define SUPERIO_FINTEK_F81803_H
#define LDN_REG 0x07 /* Not defined under PNP */
/* Logical Device Numbers (LDN) */
#define F81803A_SP1 0x01 /* UART1 */
#define F81803A_SP2 0x02 /* UART2 */
#define F81803A_HWM 0x04 /* Hardware Monitor */
#define F81803A_KBC 0x05 /* Keyboard/Mouse */
#define F81803A_GPIO 0x06 /* General Purpose I/O (GPIO) */
#define F81803A_WDT 0x07 /* Watch Dog Timer */
#define F81803A_PME 0x0a /* Power Management Events (PME) */
/* Global Control Registers */
#define CLOCK_SELECT_REG 0x26
#define FUNC_PROG_SELECT (1<<3)
#define PORT_SELECT_REG 0x27
#define TSI_LEVEL_SELECT_REG 0x28 /* FUNC_PROG_SEL = 0 */
#define TSI_PIN_SELECT_REG 0x28 /* FUNC_PROG_SEL = 1 */
#define MULTI_FUNC_SEL_REG1 0x29
#define MULTI_FUNC_SEL_REG2 0x2A
#define MULTI_FUNC_SEL_REG3 0x2B
#define MULTI_FUNC_SEL_REG 0x2C
#define WAKEUP_CONTROL_REG 0x2d
/* LUN A - PME, ACPI, ERP */
#define PME_DEVICE_ENABLE_REG 0x30
#define PME_ENABLE (1<<0)
#define PME_ERP_ENABLE_REG 0xE0
#define ERP_ENABLE (1<<7)
#define ERP_PME_EN (1<<1)
#define ERP_PSOUT_EN (1<<0)
#define PME_ERP_CONTROL_1_REG 0xE1
#define PME_ERP_CONTROL_2_REG 0xE2
#define PME_ERP_PSIN_DEBOUNCE_REG 0xE3
#define PME_ERP_WAKEUP_ENABLE_REG 0xE8
#define PME_ERP_MODE_SELECT_REG 0xEC
#define PME_EVENT_ENABLE_1_REG 0xF0
#define PME_EVENT_STATUS_1_REG 0xF1
#define PME_EVENT_ENABLE_2_REG 0xF2
#define PME_EVENT_STATUS_2_REG 0xF3
#define PME_ACPI_CONTROL_1_REG 0xF4
#define PME_ACPI_CONTROL_2_REG 0xF5
#define PME_ACPI_CONTROL_3_REG 0xF6
#define PME_ACPI_CONTROL_4_REG 0xF7
#define PME_ACPI_CONTROL_5_REG 0xFB
#define PME_ACPI_CONTROL_6_REG 0xFC
#endif /* SUPERIO_FINTEK_F81803_H */

View File

@ -0,0 +1,72 @@
/*
* This file is part of the coreboot project.
*
* Copyright (C) 2019 Richard Spiegel <richard.spiegel@silverbackltd.com>
* Copyright (C) 2019 Silverback ltd.
*
* 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; version 2 of the License.
*
* 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.
*/
#ifndef SUPERIO_FINTEK_F81803_HWM_H
#define SUPERIO_FINTEK_F81803_HWM_H
#define TP_SENSOR_TYPE 0x6b
#define TP_SENSOR1_TYPE_SHIFT 1
#define TP_SENSOR2_TYPE_SHIFT 2
#define TP_SENSOR_TYPE_MASK 0x01
#define TP_DIODE_STATUS 0x6f
#define TP_MMX_OPEN 0x40
#define TP_PECI_OPEN 0x20
#define TP_TSI_OPEN 0x10
#define TP_EXTERNAL_SENSOR2_OPEN 0x04
#define TP_EXTERNAL_SENSOR1_OPEN 0x02
#define FAN_TYPE_REG 0x94
#define FAN_TYPE_SHIFT(fan) ((fan - 1) * 2)
#define FAN_TYPE_MASK 0x03
#define FAN_MODE_REG 0x96
/* FUNC_PROG_SEL = 0 */
#define FAN_MODE_SHIFT(fan) ((fan - 1) * 4)
#define FAN_MODE_MASK 0x07
/* FUNC_PROG_SEL = 1 */
#define FAN1_ADJ_SEL_SHIFT 0
#define FAN1_ADJ_SEL_MASK 0x07
#define FAN_FREQ_SEL_ADD_SHIFT(fan) (fan + 2)
#define FAN_UP_RATE_REG 0x9a
#define FAN_RATE_SHIFT(fan) ((fan - 1) * 2)
#define FAN_RATE_MASK 0x03
#define FAN_DOWN_RATE_REG 0x9b
#define FAN_DOWN_RATE_DIFF_FROM_UP_SHIFT 7 /* FUNC_PROG_SEL = 1 */
#define FAN_DIRECT_LOAD_EN_SHIFT 6 /* FUNC_PROG_SEL = 1 */
#define FAN_FAULT_TIME_REG 0x9f
#define FAN_FUNC_PROG_SEL_SHIFT 7
#define FAN_BOUND_TEMP 0xa6 /* 4 temperatures */
#define FAN_SECTION_SPEED 0xaa /* 5 sections */
#define FAN_TMP_MAPPING 0xaf
#define FAN_TEMP_SEL_HIGH_SHIFT 7
#define FAN_PWM_FREQ_SEL_SHIFT 6
#define FAN_INTERPOLATION_SHIFT 4
#define FAN_JUMP_UP_SHIFT 3
#define FAN_JUMP_DOWN_SHIFT 2
#define FAN_TEMP_SEL_LOW_SHIFT 0
#define FAN_TEMP_SEL_LOW_MASK 0x03
#define FAN_BIT_MASK 0x01
#define FAN_ADJUST(fan, start) (((fan - 1) * 0x10) + start)
#define STATUS_INVALID_VALUE -1
#define STATUS_INVALID_ORDER -2
#define FIRST_FAN 1
#define LAST_FAN 2
#define MAX_DUTY 100
#endif /* SUPERIO_FINTEK_F81803_HWM_H */

View File

@ -0,0 +1,362 @@
/*
* This file is part of the coreboot project.
*
* Copyright (C) 2019 Richard Spiegel <richard.spiegel@silverbackltd.com>
* Copyright (C) 2019 Silverback ltd.
*
* 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; version 2 of the License.
*
* 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 <console/console.h>
#include "../common/fan_control.h"
#include "f81803a_hwm.h"
static const char msg_err_invalid[] = "Error: invalid";
static const char msg_err_wrong_order[] = "Error: wrong order,";
static const char msg_err_fan[] = "fan";
static const char msg_err_temp_source[] = "temperature source";
static const char msg_err_type[] = "type";
static const char msg_err_mode[] = "mode";
static const char msg_err_rate[] = "change rate";
static const char msg_err_frequency[] = "frequency";
static const char msg_err_temp_sensor[] = "temperature sensor";
static const char msg_err_bondary[] = "boundary";
static const char msg_err_section[] = "section";
static const char no_msg[] = "";
struct cross_ref {
int selection;
const char *message;
};
static struct cross_ref msg_table[] = {
{HWM_STATUS_INVALID_FAN, msg_err_fan},
{HWM_STATUS_INVALID_TEMP_SOURCE, msg_err_temp_source},
{HWM_STATUS_INVALID_TYPE, msg_err_type},
{HWM_STATUS_INVALID_MODE, msg_err_mode},
{HWM_STATUS_INVALID_RATE, msg_err_rate},
{HWM_STATUS_INVALID_FREQUENCY, msg_err_frequency},
{HWM_STATUS_INVALID_TEMP_SENSOR, msg_err_temp_sensor},
{0, NULL},
};
static const char *get_msg(int err)
{
int i = 0;
while (msg_table[i].selection) {
if (msg_table[i].selection == err)
return msg_table[i].message;
i++;
}
return no_msg;
}
static int message_invalid_1(int err, u8 fan)
{
if (err == HWM_STATUS_INVALID_FAN)
printk(BIOS_ERR, "%s %s %d!\n", msg_err_invalid, get_msg(err), fan);
else
printk(BIOS_ERR, "%s Fan %d %s!\n", msg_err_invalid, fan, get_msg(err));
return err;
}
static int message_invalid_2(int err, u8 fan)
{
switch (err) {
case HWM_STATUS_INVALID_BOUNDARY_VALUE:
printk(BIOS_ERR, "%s fan %d %s value!\n", msg_err_invalid, fan,
msg_err_bondary);
break;
case HWM_STATUS_INVALID_SECTION_VALUE:
printk(BIOS_ERR, "%s fan %d %s value!\n", msg_err_invalid, fan,
msg_err_section);
break;
case HWM_STATUS_BOUNDARY_WRONG_ORDER:
printk(BIOS_ERR, "%s fan %d %s!\n", msg_err_wrong_order, fan, msg_err_bondary);
break;
case HWM_STATUS_SECTIONS_WRONG_ORDER:
printk(BIOS_ERR, "%s fan %d %s!\n", msg_err_wrong_order, fan, msg_err_section);
break;
default:
break;
}
return err;
}
static void write_hwm_reg(u16 address, u8 index, u8 value)
{
u16 index_add, data_add;
index_add = address | 0x0001; /* force odd address */
data_add = index_add + 1;
outb(index, index_add);
outb(value, data_add);
}
static u8 read_hwm_reg(u16 address, u8 index)
{
u16 index_add, data_add;
index_add = address | 0x0001; /* force odd address */
data_add = index_add + 1;
outb(index, index_add);
return inb(data_add);
}
static void hwm_reg_modify(u16 address, u8 index, u8 shift, u8 mask,
u8 value)
{
u8 use_mask = mask << shift;
u8 use_value = (value & mask) << shift;
u8 temp = read_hwm_reg(address, index);
temp &= ~use_mask;
temp |= use_value;
write_hwm_reg(address, index, temp);
}
/*
* Registers 0x94,0x95, 0x96 and 0x9b have 2 versions (banks) selected through
* bit 7 of register 0x9f.
*/
static inline void select_hwm_bank(u16 address, u8 value)
{
hwm_reg_modify(address, FAN_FAULT_TIME_REG, FAN_FUNC_PROG_SEL_SHIFT,
FAN_BIT_MASK, value);
}
/*
* Boundaries and sections must be presented in the same order as in the HWM
* registers, that is, from highest value to lowest. This procedure checks for
* the correct order.
*/
static int check_value_seq(u8 *values, u8 count)
{
u8 last_value = CPU_DAMAGE_TEMP;
u8 current_value, i;
for (i = 0; i < count; i++) {
current_value = values[i];
if (current_value > CPU_DAMAGE_TEMP)
return STATUS_INVALID_VALUE;
if (current_value >= last_value)
return STATUS_INVALID_ORDER;
last_value = current_value;
}
return HWM_STATUS_SUCCESS;
}
int set_sensor_type(u16 base_address, external_sensor sensor,
temp_sensor_type type)
{
u8 sensor_status = read_hwm_reg(base_address, TP_DIODE_STATUS);
printk(BIOS_DEBUG, "%s\n", __func__);
switch (sensor) {
case EXTERNAL_SENSOR1:
if (sensor_status & TP_EXTERNAL_SENSOR1_OPEN) {
printk(BIOS_WARNING, "Sensor 1 disconected!\n");
return HWM_STATUS_WARNING_SENSOR_DISCONECTED;
}
hwm_reg_modify(base_address, TP_SENSOR_TYPE,
TP_SENSOR1_TYPE_SHIFT, TP_SENSOR_TYPE_MASK, type);
break;
case EXTERNAL_SENSOR2:
if (sensor_status & TP_EXTERNAL_SENSOR2_OPEN) {
printk(BIOS_WARNING, "Sensor 2 disconected!\n");
return HWM_STATUS_WARNING_SENSOR_DISCONECTED;
}
hwm_reg_modify(base_address, TP_SENSOR_TYPE,
TP_SENSOR2_TYPE_SHIFT, TP_SENSOR_TYPE_MASK, type);
break;
case IGNORE_SENSOR:
break;
default:
return message_invalid_1(HWM_STATUS_INVALID_TEMP_SENSOR, 0);
}
return HWM_STATUS_SUCCESS;
}
int set_fan_temperature_source(u16 base_address, u8 fan,
fan_temp_source source)
{
u8 index, high_value, low_value;
printk(BIOS_DEBUG, "%s\n", __func__);
if ((fan < FIRST_FAN) || (fan > LAST_FAN))
return message_invalid_1(HWM_STATUS_INVALID_FAN, fan);
index = FAN_ADJUST(fan, FAN_TMP_MAPPING);
high_value = (source >> 2) & FAN_BIT_MASK;
low_value = source & FAN_TEMP_SEL_LOW_MASK;
hwm_reg_modify(base_address, index, FAN_TEMP_SEL_HIGH_SHIFT,
FAN_BIT_MASK, high_value);
hwm_reg_modify(base_address, index, FAN_TEMP_SEL_LOW_SHIFT,
FAN_TEMP_SEL_LOW_MASK, low_value);
/*
* Fan 1 has a weight mechanism for adjusting for next fan speed. Basically the idea is
* to react more aggressively (normally CPU fan) based on how high another temperature
* (system, thermistor near the CPU, anything) is. This would be highly platform
* dependent, and by setting the weight temperature same as the control temperature.
* This code cancels the weight mechanism and make it work with any board. If a board
* wants to use the weight mechanism, OEM should implement it after calling the main
* HWM programming.
*/
if (fan == FIRST_FAN) {
select_hwm_bank(base_address, 1);
hwm_reg_modify(base_address, FAN_MODE_REG,
FAN1_ADJ_SEL_SHIFT, FAN1_ADJ_SEL_MASK, source);
select_hwm_bank(base_address, 0);
}
return HWM_STATUS_SUCCESS;
}
int set_fan_type_mode(u16 base_address, u8 fan, fan_type type, fan_mode mode)
{
u8 shift;
printk(BIOS_DEBUG, "%s\n", __func__);
if ((fan < FIRST_FAN) || (fan > LAST_FAN))
return message_invalid_1(HWM_STATUS_INVALID_FAN, fan);
select_hwm_bank(base_address, 0);
if (type < FAN_TYPE_RESERVED) {
shift = FAN_TYPE_SHIFT(fan);
hwm_reg_modify(base_address, FAN_TYPE_REG, shift,
FAN_TYPE_MASK, type);
}
if (mode < FAN_MODE_DEFAULT) {
shift = FAN_MODE_SHIFT(fan);
hwm_reg_modify(base_address, FAN_MODE_REG, shift,
FAN_MODE_MASK, mode);
}
return HWM_STATUS_SUCCESS;
}
int set_pwm_frequency(u16 base_address, u8 fan, fan_pwm_freq frequency)
{
u8 shift, index, byte;
printk(BIOS_DEBUG, "%s\n", __func__);
if ((fan < FIRST_FAN) || (fan > LAST_FAN))
return message_invalid_1(HWM_STATUS_INVALID_FAN, fan);
byte = read_hwm_reg(base_address, FAN_TYPE_REG);
shift = FAN_TYPE_SHIFT(fan);
if (((byte >> shift) & FAN_TYPE_PWM_CHECK) == FAN_TYPE_PWM_CHECK) {
printk(BIOS_WARNING, "Fan %d not programmed as PWM!\n", fan);
return HWM_STATUS_WARNING_FAN_NOT_PWM;
}
select_hwm_bank(base_address, 1);
shift = FAN_FREQ_SEL_ADD_SHIFT(fan);
byte = (frequency >> 1) & FAN_BIT_MASK;
hwm_reg_modify(base_address, FAN_MODE_REG, shift, FAN_BIT_MASK,
byte);
select_hwm_bank(base_address, 0);
index = FAN_ADJUST(fan, FAN_TMP_MAPPING);
byte = frequency & FAN_BIT_MASK;
hwm_reg_modify(base_address, index, FAN_PWM_FREQ_SEL_SHIFT,
FAN_BIT_MASK, byte);
return HWM_STATUS_SUCCESS;
}
int set_sections(u16 base_address, u8 fan, u8 *boundaries, u8 *sections)
{
int status, temp;
u8 i, index, value;
printk(BIOS_DEBUG, "%s\n", __func__);
if ((fan < FIRST_FAN) || (fan > LAST_FAN))
return message_invalid_1(HWM_STATUS_INVALID_FAN, fan);
status = check_value_seq(boundaries,
FINTEK_BOUNDARIES_SIZE);
if (status != HWM_STATUS_SUCCESS) {
if (status == STATUS_INVALID_VALUE)
return message_invalid_2(HWM_STATUS_INVALID_BOUNDARY_VALUE, fan);
return message_invalid_2(HWM_STATUS_BOUNDARY_WRONG_ORDER, fan);
}
status = check_value_seq(sections,
FINTEK_SECTIONS_SIZE);
if (status != HWM_STATUS_SUCCESS) {
if (status == STATUS_INVALID_VALUE)
return message_invalid_2(HWM_STATUS_INVALID_SECTION_VALUE, fan);
return message_invalid_2(HWM_STATUS_SECTIONS_WRONG_ORDER, fan);
}
index = FAN_ADJUST(fan, FAN_BOUND_TEMP);
for (i = 0; i < FINTEK_BOUNDARIES_SIZE; i++) {
value = boundaries[i];
write_hwm_reg(base_address, index, value);
index++;
}
index = FAN_ADJUST(fan, FAN_SECTION_SPEED);
for (i = 0; i < FINTEK_SECTIONS_SIZE; i++) {
value = sections[i];
if (value > 100)
return message_invalid_2(HWM_STATUS_INVALID_SECTION_VALUE, fan);
temp = (255 * value) / 100;
value = (u8) (temp & 0x00ff);
write_hwm_reg(base_address, index, value);
index++;
}
return HWM_STATUS_SUCCESS;
}
int set_fan_speed_change_rate(u16 base_address, u8 fan, fan_rate_up rate_up,
fan_rate_down rate_down)
{
u8 shift, index;
printk(BIOS_DEBUG, "%s\n", __func__);
if ((fan < FIRST_FAN) || (fan > LAST_FAN))
return message_invalid_1(HWM_STATUS_INVALID_FAN, fan);
index = FAN_ADJUST(fan, FAN_TMP_MAPPING);
shift = FAN_RATE_SHIFT(fan);
if (rate_up == FAN_UP_RATE_JUMP) {
hwm_reg_modify(base_address, index, FAN_JUMP_UP_SHIFT,
FAN_BIT_MASK, 1);
} else {
hwm_reg_modify(base_address, index, FAN_JUMP_UP_SHIFT,
FAN_BIT_MASK, 0);
if (rate_up < FAN_UP_RATE_DEFAULT) {
hwm_reg_modify(base_address, FAN_UP_RATE_REG,
shift, FAN_RATE_MASK, rate_up);
}
}
if (rate_down == FAN_DOWN_RATE_JUMP) {
hwm_reg_modify(base_address, index, FAN_JUMP_DOWN_SHIFT,
FAN_BIT_MASK, 1);
} else {
hwm_reg_modify(base_address, index, FAN_JUMP_UP_SHIFT,
FAN_BIT_MASK, 0);
select_hwm_bank(base_address, 0);
if (rate_down < FAN_DOWN_RATE_DEFAULT) {
hwm_reg_modify(base_address, FAN_DOWN_RATE_REG,
shift, FAN_RATE_MASK, rate_down);
hwm_reg_modify(base_address, FAN_DOWN_RATE_REG,
FAN_DOWN_RATE_DIFF_FROM_UP_SHIFT,
FAN_BIT_MASK, 0);
}
if (rate_down == FAN_DOWN_RATE_SAME_AS_UP) {
hwm_reg_modify(base_address, FAN_DOWN_RATE_REG,
FAN_DOWN_RATE_DIFF_FROM_UP_SHIFT,
FAN_BIT_MASK, 1);
}
}
return HWM_STATUS_SUCCESS;
}
int set_fan_follow(u16 base_address, u8 fan, fan_follow follow)
{
u8 index;
printk(BIOS_DEBUG, "%s\n", __func__);
if ((fan < FIRST_FAN) || (fan > LAST_FAN))
return message_invalid_1(HWM_STATUS_INVALID_FAN, fan);
index = FAN_ADJUST(fan, FAN_TMP_MAPPING);
hwm_reg_modify(base_address, index, FAN_INTERPOLATION_SHIFT,
FAN_BIT_MASK, follow);
return HWM_STATUS_SUCCESS;
}

View File

@ -0,0 +1,78 @@
/*
* This file is part of the coreboot project.
*
* Copyright (C) 2011 Advanced Micro Devices, 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.
*/
#include <device/device.h>
#include <device/pnp.h>
#include <superio/conf_mode.h>
#include <console/console.h>
#include <stdlib.h>
#include <pc80/keyboard.h>
#include "f81803a.h"
static void f81803a_pme_init(struct device *dev)
{
pnp_enter_conf_mode(dev);
pnp_write_config(dev, LDN_REG, F81803A_PME);
/* enable ERP function*/
/* also set PSIN to generate PSOUT*/
pnp_write_config(dev, PME_ERP_ENABLE_REG, ERP_ENABLE | ERP_PSOUT_EN);
pnp_exit_conf_mode(dev);
}
static void f81803a_init(struct device *dev)
{
if (!dev->enabled)
return;
switch (dev->path.pnp.device) {
/* TODO: Might potentially need code for GPIO or WDT. */
case F81803A_KBC:
pc_keyboard_init(NO_AUX_DEVICE);
break;
case F81803A_PME:
f81803a_pme_init(dev);
break;
}
}
static struct device_operations ops = {
.read_resources = pnp_read_resources,
.set_resources = pnp_set_resources,
.enable_resources = pnp_enable_resources,
.enable = pnp_alt_enable,
.init = f81803a_init,
.ops_pnp_mode = &pnp_conf_mode_8787_aa,
};
static struct pnp_info pnp_dev_info[] = {
{ &ops, F81803A_SP1, PNP_IO0 | PNP_IRQ0, 0x7f8, },
{ &ops, F81803A_SP2, PNP_IO0 | PNP_IRQ0, 0x7f8, },
{ &ops, F81803A_HWM, PNP_IO0 | PNP_IRQ0, 0xff8, },
{ &ops, F81803A_KBC, PNP_IO0 | PNP_IRQ0 | PNP_IRQ1, 0x07f8, },
{ &ops, F81803A_GPIO, PNP_IO0 | PNP_IRQ0, 0x7f8, },
{ &ops, F81803A_WDT, PNP_IO0, 0x7f8 },
{ &ops, F81803A_PME, },
};
static void enable_dev(struct device *dev)
{
pnp_enable_devices(dev, &ops, ARRAY_SIZE(pnp_dev_info), pnp_dev_info);
}
struct chip_operations superio_fintek_f81803a_ops = {
CHIP_NAME("Fintek F81803A Super I/O")
.enable_dev = enable_dev
};