summaryrefslogtreecommitdiff
path: root/src/superio/fintek/f81803a
diff options
context:
space:
mode:
authorRichard Spiegel <richard.spiegel@amd.corp-partner.google.com>2019-06-19 13:57:55 -0700
committerFelix Held <felix-coreboot@felixheld.de>2019-07-26 20:33:16 +0000
commitae5b3671b39f26b79685496d79845f9a4f3975db (patch)
treeecf8c1767c981718967af471c3df5d8b415d8758 /src/superio/fintek/f81803a
parent150a61e103c9c8001a0fc7905c02d0c065b7f126 (diff)
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>
Diffstat (limited to 'src/superio/fintek/f81803a')
-rw-r--r--src/superio/fintek/f81803a/Kconfig28
-rw-r--r--src/superio/fintek/f81803a/Makefile.inc30
-rw-r--r--src/superio/fintek/f81803a/acpi/superio.asl266
-rw-r--r--src/superio/fintek/f81803a/f81803a.h71
-rw-r--r--src/superio/fintek/f81803a/f81803a_hwm.h72
-rw-r--r--src/superio/fintek/f81803a/fan_control.c362
-rw-r--r--src/superio/fintek/f81803a/superio.c78
7 files changed, 907 insertions, 0 deletions
diff --git a/src/superio/fintek/f81803a/Kconfig b/src/superio/fintek/f81803a/Kconfig
new file mode 100644
index 0000000000..e1aa537e78
--- /dev/null
+++ b/src/superio/fintek/f81803a/Kconfig
@@ -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
diff --git a/src/superio/fintek/f81803a/Makefile.inc b/src/superio/fintek/f81803a/Makefile.inc
new file mode 100644
index 0000000000..6fe13aaab7
--- /dev/null
+++ b/src/superio/fintek/f81803a/Makefile.inc
@@ -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
diff --git a/src/superio/fintek/f81803a/acpi/superio.asl b/src/superio/fintek/f81803a/acpi/superio.asl
new file mode 100644
index 0000000000..ae8e6dc242
--- /dev/null
+++ b/src/superio/fintek/f81803a/acpi/superio.asl
@@ -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
+}
diff --git a/src/superio/fintek/f81803a/f81803a.h b/src/superio/fintek/f81803a/f81803a.h
new file mode 100644
index 0000000000..c986cb806e
--- /dev/null
+++ b/src/superio/fintek/f81803a/f81803a.h
@@ -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 */
diff --git a/src/superio/fintek/f81803a/f81803a_hwm.h b/src/superio/fintek/f81803a/f81803a_hwm.h
new file mode 100644
index 0000000000..a7647057d1
--- /dev/null
+++ b/src/superio/fintek/f81803a/f81803a_hwm.h
@@ -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 */
diff --git a/src/superio/fintek/f81803a/fan_control.c b/src/superio/fintek/f81803a/fan_control.c
new file mode 100644
index 0000000000..17ae9c6a20
--- /dev/null
+++ b/src/superio/fintek/f81803a/fan_control.c
@@ -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;
+}
diff --git a/src/superio/fintek/f81803a/superio.c b/src/superio/fintek/f81803a/superio.c
new file mode 100644
index 0000000000..a07b69e97e
--- /dev/null
+++ b/src/superio/fintek/f81803a/superio.c
@@ -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
+};