// SPDX-License-Identifier: GPL-2.0-or-later
/*
* f71805f.c - driver for the Fintek F71805F/FG and F71872F/FG Super-I/O
* chips integrated hardware monitoring features
* Copyright (C) 2005-2006 Jean Delvare <jdelvare@suse.de>
*
* The F71805F/FG is a LPC Super-I/O chip made by Fintek. It integrates
* complete hardware monitoring features: voltage, fan and temperature
* sensors, and manual and automatic fan speed control.
*
* The F71872F/FG is almost the same, with two more voltages monitored,
* and 6 VID inputs.
*
* The F71806F/FG is essentially the same as the F71872F/FG. It even has
* the same chip ID, so the driver can't differentiate between.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/jiffies.h>
#include <linux/platform_device.h>
#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
#include <linux/err.h>
#include <linux/mutex.h>
#include <linux/sysfs.h>
#include <linux/ioport.h>
#include <linux/acpi.h>
#include <linux/io.h>
static unsigned short force_id;
module_param(force_id, ushort, 0);
MODULE_PARM_DESC(force_id, "Override the detected device ID");
static struct platform_device *pdev;
#define DRVNAME "f71805f"
enum kinds { f71805f, f71872f };
/*
* Super-I/O constants and functions
*/
#define F71805F_LD_HWM 0x04
#define SIO_REG_LDSEL 0x07 /* Logical device select */
#define SIO_REG_DEVID 0x20 /* Device ID (2 bytes) */
#define SIO_REG_DEVREV 0x22 /* Device revision */
#define SIO_REG_MANID 0x23 /* Fintek ID (2 bytes) */
#define SIO_REG_FNSEL1 0x29 /* Multi Function Select 1 (F71872F) */
#define SIO_REG_ENABLE 0x30 /* Logical device enable */
#define SIO_REG_ADDR 0x60 /* Logical device address (2 bytes) */
#define SIO_FINTEK_ID 0x1934
#define SIO_F71805F_ID 0x0406
#define SIO_F71872F_ID 0x0341
static inline int
superio_inb(int base, int reg)
{
outb(reg, base);
return inb(base + 1);
}
static int
superio_inw(int base, int reg)
{
int val;
outb(reg++, base);
val = inb(base + 1) << 8;
outb(reg, base);
val |= inb(base + 1);
return val;
}
static inline void
superio_select(int base, int ld)
{
outb(SIO_REG_LDSEL, base);
outb(ld, base + 1);
}
static inline int
superio_enter(int base)
{
if (!request_muxed_region(base, 2, DRVNAME))
return -EBUSY;
outb(0x87, base);
outb(0x87, base);
return 0;
}
static inline void
superio_exit(int base)
{
outb(0xaa, base);
release_region(base, 2);
}
/*
* ISA constants
*/
#define REGION_LENGTH 8
#define ADDR_REG_OFFSET 5
#define DATA_REG_OFFSET 6
/*
* Registers
*/
/* in nr from 0 to 10 (8-bit values) */
#define F71805F_REG_IN(nr) (0x10 + (nr))
#define F71805F_REG_IN_HIGH(nr) ((nr) < 10 ? 0x40 + 2 * (nr) : 0x2E)
#define F71805F_REG_IN_LOW(nr) ((nr) < 10 ? 0x41 + 2 * (nr) : 0x2F)
/* fan nr from 0 to 2 (12-bit values, two registers) */
#define F71805F_REG_FAN(nr) (0x20 + 2 * (nr))
#define F71805F_REG_FAN_LOW(nr) (0x28 + 2 * (nr))
#define F71805F_REG_FAN_TARGET(nr) (0x69 + 16 * (nr))
#define F71805F_REG_FAN_CTRL(nr) (0x60 + 16 * (nr))
#define F71805F_REG_PWM_FREQ(nr) (0x63 + 16 * (nr))
#define F71805F_REG_PWM_DUTY(nr) (0x6B + 16 * (nr))
/* temp nr from 0 to 2 (8-bit values) */
#define F71805F_REG_TEMP(nr) (0x1B + (nr))
#define F71805F_REG_TEMP_HIGH(nr) (0x54 + 2 * (nr))
#define F71805F_REG_TEMP_HYST(nr) (0x55 + 2 * (nr))
#define F71805F_REG_TEMP_MODE 0x01
/* pwm/fan pwmnr from 0 to 2, auto point apnr from 0 to 2 */
/* map Fintek numbers to our numbers as follows: 9->0, 5->1, 1->2 */
#define F71805F_REG_PWM_AUTO_POINT_TEMP(pwmnr, apnr) \
(0xA0 + 0x10 * (pwmnr) + (2 - (apnr
|