From 9bd601584350f51f112b15a7369f9aa82f1d0919 Mon Sep 17 00:00:00 2001 From: Patrick Rudolph Date: Fri, 4 May 2018 09:01:38 +0200 Subject: superio/nuvoton/npcd378: Add PSU fan control Implement method to access the SuperIO's harware monitor (HWM) IO space. Set the PSU fan using a new CMOS option psu_fan_lvl. Add the CMOS option to all board that use NPCD378. In case no CMOS is set use the default fan level 3. The HWM space can be written to at any time, but the SuperIO has to be notified that a write is ongoing. After clearing the write-lock bit all changes are applied at once. Tested on HP Compaq 8200 SFF. Change-Id: I56ce7ad1df88638589a577b8a09d5d775557887b Signed-off-by: Patrick Rudolph Reviewed-on: https://review.coreboot.org/26050 Reviewed-by: Felix Held Reviewed-by: Paul Menzel Tested-by: build bot (Jenkins) --- .../hp/compaq_8200_elite_sff/cmos.default | 1 + src/mainboard/hp/compaq_8200_elite_sff/cmos.layout | 4 +- src/superio/nuvoton/npcd378/npcd378.h | 44 +++++++++++++++ src/superio/nuvoton/npcd378/superio.c | 64 +++++++++++++++++++++- 4 files changed, 111 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/mainboard/hp/compaq_8200_elite_sff/cmos.default b/src/mainboard/hp/compaq_8200_elite_sff/cmos.default index 390d231fb1..1d88415039 100644 --- a/src/mainboard/hp/compaq_8200_elite_sff/cmos.default +++ b/src/mainboard/hp/compaq_8200_elite_sff/cmos.default @@ -4,3 +4,4 @@ power_on_after_fail=Enable nmi=Enable sata_mode=AHCI gfx_uma_size=32M +psu_fan_lvl=3 diff --git a/src/mainboard/hp/compaq_8200_elite_sff/cmos.layout b/src/mainboard/hp/compaq_8200_elite_sff/cmos.layout index e90af85a09..f2d2331e63 100644 --- a/src/mainboard/hp/compaq_8200_elite_sff/cmos.layout +++ b/src/mainboard/hp/compaq_8200_elite_sff/cmos.layout @@ -51,7 +51,9 @@ entries # coreboot config options: console #392 3 r 0 unused 395 4 e 6 debug_level -#399 9 r 0 unused +#399 1 r 0 unused +400 3 h 0 psu_fan_lvl +#403 5 r 0 unused # coreboot config options: southbridge 408 1 e 1 nmi diff --git a/src/superio/nuvoton/npcd378/npcd378.h b/src/superio/nuvoton/npcd378/npcd378.h index 732dd000f3..53541a9efb 100644 --- a/src/superio/nuvoton/npcd378/npcd378.h +++ b/src/superio/nuvoton/npcd378/npcd378.h @@ -17,6 +17,50 @@ #ifndef SUPERIO_NUVOTON_NPCD378_H #define SUPERIO_NUVOTON_NPCD378_H +/* HWM at LDN8 */ +#define NPCD837_HWM_WRITE_LOCK_CTRL 0x4 +#define NPCD837_HWM_WRITE_LOCK_BIT 0x1 + +#define NPCD378_HWM_PSU_FAN_MIN 0x17 +#define NPCD378_HWM_PSU_FAN_MAX 0xf8 +#define NPCD378_HWM_PSU_FAN_PWM_CTRL 0x198 + +/* + * Read HWM register at specific page and offset. + * + * @param iobase IOBASE address of LDN8 + * @param reg MSB is page, LSB sets the offset in selected page + * + * @return Byte read from HWM + */ +uint8_t npcd378_hwm_read(const uint16_t iobase, const uint16_t reg); + +/* + * Write to HWM register at specific page and offset. + * + * @param iobase IOBASE address of LDN8 + * @param reg MSB is page, LSB sets the offset in selected page + * @param val The value to write to HWM register + */ +void npcd378_hwm_write(const uint16_t iobase, const uint16_t reg, + const uint8_t val); + +/* + * Notify SuperIO a host-to-device transfer is ongoing. + * Has to be called before any register in HWM is written to. + * + * @param iobase IOBASE address of LDN8 + */ +void npcd378_hwm_write_start(const uint16_t iobase); + +/* + * Notify SuperIO a host-to-device transfer has finished. + * Has to be called after any register in HWM was written to. + * + * @param iobase IOBASE address of LDN8 + */ +void npcd378_hwm_write_finished(const uint16_t iobase); + /* Logical Device Numbers (LDN). */ /* Default Nuvoton hardware: */ #define NPCD378_FDC 0x00 /* Floppy */ diff --git a/src/superio/nuvoton/npcd378/superio.c b/src/superio/nuvoton/npcd378/superio.c index d468884c82..013225de32 100644 --- a/src/superio/nuvoton/npcd378/superio.c +++ b/src/superio/nuvoton/npcd378/superio.c @@ -18,24 +18,86 @@ */ #include +#include #include #include +#include #include #include #include #include "npcd378.h" +uint8_t npcd378_hwm_read(const uint16_t iobase, const uint16_t reg) +{ + outb((reg >> 8) & 0xf, iobase + 0xff); + uint8_t reg8 = inb(iobase + (reg & 0xff)); + if (reg8 == 0xff) + reg8 = inb(iobase + (reg & 0xff)); + + outb(0, iobase + 0xff); + return reg8; +} + +void npcd378_hwm_write(const uint16_t iobase, const uint16_t reg, + const uint8_t val) +{ + outb((reg >> 8) & 0xf, iobase + 0xff); + outb(val, iobase + (reg & 0xff)); + + outb(0, iobase + 0xff); +} + +void npcd378_hwm_write_start(const uint16_t iobase) +{ + u8 reg8 = npcd378_hwm_read(iobase, NPCD837_HWM_WRITE_LOCK_CTRL); + reg8 &= ~NPCD837_HWM_WRITE_LOCK_BIT; + npcd378_hwm_write(iobase, NPCD837_HWM_WRITE_LOCK_CTRL, reg8); +} + +void npcd378_hwm_write_finished(const uint16_t iobase) +{ + u8 reg8 = npcd378_hwm_read(iobase, NPCD837_HWM_WRITE_LOCK_CTRL); + reg8 |= NPCD837_HWM_WRITE_LOCK_BIT; + npcd378_hwm_write(iobase, NPCD837_HWM_WRITE_LOCK_CTRL, reg8); +} + static void npcd378_init(struct device *dev) { + struct resource *res; + uint8_t pwm, fan_lvl; + if (!dev->enabled) return; switch (dev->path.pnp.device) { - /* TODO: Might potentially need code for HWM or FDC etc. */ + /* TODO: Might potentially need code for FDC etc. */ case NPCD378_KBC: pc_keyboard_init(PROBE_AUX_DEVICE); break; + case NPCD378_HWM: + res = find_resource(dev, PNP_IDX_IO0); + if (!res || !res->base) { + printk(BIOS_ERR, "NPCD378: LDN%u IOBASE not set.\n", + NPCD378_HWM); + break; + } + + npcd378_hwm_write_start(res->base); + + if (!get_option(&fan_lvl, "psu_fan_lvl") || fan_lvl > 7) + fan_lvl = 3; + + pwm = NPCD378_HWM_PSU_FAN_MIN + + (NPCD378_HWM_PSU_FAN_MAX - NPCD378_HWM_PSU_FAN_MIN) * + fan_lvl / 7; + + /* Set PSU fan PWM lvl */ + npcd378_hwm_write(res->base, NPCD378_HWM_PSU_FAN_PWM_CTRL, pwm); + printk(BIOS_INFO, "NPCD378: PSU fan PWM 0x%02x\n", pwm); + + npcd378_hwm_write_finished(res->base); + break; } } -- cgit v1.2.3