|
From: <abe...@us...> - 2011-11-26 04:52:30
|
Revision: 5271
http://astlinux.svn.sourceforge.net/astlinux/?rev=5271&view=rev
Author: abelbeck
Date: 2011-11-26 04:52:22 +0000 (Sat, 26 Nov 2011)
Log Message:
-----------
kernel, add f71882fg patch to support newer Fintek chips found in Jetway boards
Added Paths:
-----------
branches/1.0/project/astlinux/kernel-patches/linux-500-hwmon-f71882fg.patch
Added: branches/1.0/project/astlinux/kernel-patches/linux-500-hwmon-f71882fg.patch
===================================================================
--- branches/1.0/project/astlinux/kernel-patches/linux-500-hwmon-f71882fg.patch (rev 0)
+++ branches/1.0/project/astlinux/kernel-patches/linux-500-hwmon-f71882fg.patch 2011-11-26 04:52:22 UTC (rev 5271)
@@ -0,0 +1,1324 @@
+--- a/drivers/hwmon/f71882fg.c 2011-04-28 10:21:24.000000000 -0500
++++ b/drivers/hwmon/f71882fg.c 2011-11-25 20:27:18.000000000 -0600
+@@ -1,6 +1,6 @@
+ /***************************************************************************
+ * Copyright (C) 2006 by Hans Edgington <ha...@ed...> *
+- * Copyright (C) 2007-2009 Hans de Goede <hde...@re...> *
++ * Copyright (C) 2007-2011 Hans de Goede <hde...@re...> *
+ * *
+ * 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 *
+@@ -18,6 +18,8 @@
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+
++#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
++
+ #include <linux/module.h>
+ #include <linux/init.h>
+ #include <linux/slab.h>
+@@ -28,14 +30,73 @@
+ #include <linux/err.h>
+ #include <linux/mutex.h>
+ #include <linux/io.h>
++#include <linux/version.h>
++
++/*
++ * Compatibility glue
++ */
++
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)
+ #include <linux/acpi.h>
++#else
++static inline int acpi_check_resource_conflict(struct resource *res)
++{
++ return 0;
++}
++#endif
++
++#ifndef request_muxed_region
++#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 28)
++#define request_muxed_region(start,n,name) __request_region(&ioport_resource, (start), (n), (name))
++#else
++#define IORESOURCE_MUXED 0x00400000
++#define request_muxed_region(start,n,name) __request_region(&ioport_resource, (start), (n), (name), IORESOURCE_MUXED)
++#endif
++#endif
++
++#ifndef pr_warning
++#define pr_warning(fmt,arg...) \
++ printk(KERN_WARNING fmt,##arg)
++#endif
++
++#ifndef pr_err
++#define pr_err(fmt,arg...) \
++ printk(KERN_ERR fmt,##arg)
++#endif
++
++#ifndef pr_warn
++#define pr_warn pr_warning
++#endif
++
++/* Red Hat EL5 includes backports of these functions, so we can't redefine
++ * our own. */
++#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 24)
++#if !(defined RHEL_MAJOR && RHEL_MAJOR == 5 && RHEL_MINOR >= 5)
++static inline int strict_strtoul(const char *cp, unsigned int base,
++ unsigned long *res)
++{
++ *res = simple_strtoul(cp, NULL, base);
++ return 0;
++}
++
++static inline int strict_strtol(const char *cp, unsigned int base, long *res)
++{
++ *res = simple_strtol(cp, NULL, base);
++ return 0;
++}
++#endif
++#endif
++
++/*
++ * End of compatibility glue
++ */
+
+ #define DRVNAME "f71882fg"
+
+ #define SIO_F71858FG_LD_HWM 0x02 /* Hardware monitor logical device */
+ #define SIO_F71882FG_LD_HWM 0x04 /* Hardware monitor logical device */
+ #define SIO_UNLOCK_KEY 0x87 /* Key to enable Super-I/O */
+-#define SIO_LOCK_KEY 0xAA /* Key to diasble Super-I/O */
++#define SIO_LOCK_KEY 0xAA /* Key to disable Super-I/O */
+
+ #define SIO_REG_LDSEL 0x07 /* Logical device select */
+ #define SIO_REG_DEVID 0x20 /* Device ID (2 bytes) */
+@@ -45,22 +106,27 @@
+ #define SIO_REG_ADDR 0x60 /* Logical device address (2 bytes) */
+
+ #define SIO_FINTEK_ID 0x1934 /* Manufacturers ID */
++#define SIO_F71808E_ID 0x0901 /* Chipset ID */
++#define SIO_F71808A_ID 0x1001 /* Chipset ID */
+ #define SIO_F71858_ID 0x0507 /* Chipset ID */
+ #define SIO_F71862_ID 0x0601 /* Chipset ID */
++#define SIO_F71869_ID 0x0814 /* Chipset ID */
++#define SIO_F71869A_ID 0x1007 /* Chipset ID */
+ #define SIO_F71882_ID 0x0541 /* Chipset ID */
+ #define SIO_F71889_ID 0x0723 /* Chipset ID */
++#define SIO_F71889E_ID 0x0909 /* Chipset ID */
++#define SIO_F71889A_ID 0x1005 /* Chipset ID */
+ #define SIO_F8000_ID 0x0581 /* Chipset ID */
++#define SIO_F81865_ID 0x0704 /* Chipset ID */
+
+ #define REGION_LENGTH 8
+ #define ADDR_REG_OFFSET 5
+ #define DATA_REG_OFFSET 6
+
+-#define F71882FG_REG_PECI 0x0A
+-
+-#define F71882FG_REG_IN_STATUS 0x12 /* f71882fg only */
+-#define F71882FG_REG_IN_BEEP 0x13 /* f71882fg only */
++#define F71882FG_REG_IN_STATUS 0x12 /* f7188x only */
++#define F71882FG_REG_IN_BEEP 0x13 /* f7188x only */
+ #define F71882FG_REG_IN(nr) (0x20 + (nr))
+-#define F71882FG_REG_IN1_HIGH 0x32 /* f71882fg only */
++#define F71882FG_REG_IN1_HIGH 0x32 /* f7188x only */
+
+ #define F71882FG_REG_FAN(nr) (0xA0 + (16 * (nr)))
+ #define F71882FG_REG_FAN_TARGET(nr) (0xA2 + (16 * (nr)))
+@@ -84,26 +150,130 @@
+
+ #define F71882FG_REG_FAN_HYST(nr) (0x98 + (nr))
+
++#define F71882FG_REG_FAN_FAULT_T 0x9F
++#define F71882FG_FAN_NEG_TEMP_EN 0x20
++#define F71882FG_FAN_PROG_SEL 0x80
++
+ #define F71882FG_REG_POINT_PWM(pwm, point) (0xAA + (point) + (16 * (pwm)))
+ #define F71882FG_REG_POINT_TEMP(pwm, point) (0xA6 + (point) + (16 * (pwm)))
+ #define F71882FG_REG_POINT_MAPPING(nr) (0xAF + 16 * (nr))
+
+ #define F71882FG_REG_START 0x01
+
++#define F71882FG_MAX_INS 9
++
+ #define FAN_MIN_DETECT 366 /* Lowest detectable fanspeed */
+
+ static unsigned short force_id;
+ module_param(force_id, ushort, 0);
+ MODULE_PARM_DESC(force_id, "Override the detected device ID");
+
+-enum chips { f71858fg, f71862fg, f71882fg, f71889fg, f8000 };
++enum chips { f71808e, f71808a, f71858fg, f71862fg, f71869, f71869a, f71882fg,
++ f71889fg, f71889ed, f71889a, f8000, f81865f };
+
+ static const char *f71882fg_names[] = {
++ "f71808e",
++ "f71808a",
+ "f71858fg",
+ "f71862fg",
++ "f71869", /* Both f71869f and f71869e, reg. compatible and same id */
++ "f71869a",
+ "f71882fg",
+- "f71889fg",
++ "f71889fg", /* f81801u too, same id */
++ "f71889ed",
++ "f71889a",
+ "f8000",
++ "f81865f",
++};
++
++static const char f71882fg_has_in[][F71882FG_MAX_INS] = {
++ [f71808e] = { 1, 1, 1, 1, 1, 1, 0, 1, 1 },
++ [f71808a] = { 1, 1, 1, 1, 0, 0, 0, 1, 1 },
++ [f71858fg] = { 1, 1, 1, 0, 0, 0, 0, 0, 0 },
++ [f71862fg] = { 1, 1, 1, 1, 1, 1, 1, 1, 1 },
++ [f71869] = { 1, 1, 1, 1, 1, 1, 1, 1, 1 },
++ [f71869a] = { 1, 1, 1, 1, 1, 1, 1, 1, 1 },
++ [f71882fg] = { 1, 1, 1, 1, 1, 1, 1, 1, 1 },
++ [f71889fg] = { 1, 1, 1, 1, 1, 1, 1, 1, 1 },
++ [f71889ed] = { 1, 1, 1, 1, 1, 1, 1, 1, 1 },
++ [f71889a] = { 1, 1, 1, 1, 1, 1, 1, 1, 1 },
++ [f8000] = { 1, 1, 1, 0, 0, 0, 0, 0, 0 },
++ [f81865f] = { 1, 1, 1, 1, 1, 1, 1, 0, 0 },
++};
++
++static const char f71882fg_has_in1_alarm[] = {
++ [f71808e] = 0,
++ [f71808a] = 0,
++ [f71858fg] = 0,
++ [f71862fg] = 0,
++ [f71869] = 0,
++ [f71869a] = 0,
++ [f71882fg] = 1,
++ [f71889fg] = 1,
++ [f71889ed] = 1,
++ [f71889a] = 1,
++ [f8000] = 0,
++ [f81865f] = 1,
++};
++
++static const char f71882fg_fan_has_beep[] = {
++ [f71808e] = 0,
++ [f71808a] = 0,
++ [f71858fg] = 0,
++ [f71862fg] = 1,
++ [f71869] = 1,
++ [f71869a] = 1,
++ [f71882fg] = 1,
++ [f71889fg] = 1,
++ [f71889ed] = 1,
++ [f71889a] = 1,
++ [f8000] = 0,
++ [f81865f] = 1,
++};
++
++static const char f71882fg_nr_fans[] = {
++ [f71808e] = 3,
++ [f71808a] = 2, /* +1 fan which is monitor + simple pwm only */
++ [f71858fg] = 3,
++ [f71862fg] = 3,
++ [f71869] = 3,
++ [f71869a] = 3,
++ [f71882fg] = 4,
++ [f71889fg] = 3,
++ [f71889ed] = 3,
++ [f71889a] = 3,
++ [f8000] = 3, /* +1 fan which is monitor only */
++ [f81865f] = 2,
++};
++
++static const char f71882fg_temp_has_beep[] = {
++ [f71808e] = 0,
++ [f71808a] = 1,
++ [f71858fg] = 0,
++ [f71862fg] = 1,
++ [f71869] = 1,
++ [f71869a] = 1,
++ [f71882fg] = 1,
++ [f71889fg] = 1,
++ [f71889ed] = 1,
++ [f71889a] = 1,
++ [f8000] = 0,
++ [f81865f] = 1,
++};
++
++static const char f71882fg_nr_temps[] = {
++ [f71808e] = 2,
++ [f71808a] = 2,
++ [f71858fg] = 3,
++ [f71862fg] = 3,
++ [f71869] = 3,
++ [f71869a] = 3,
++ [f71882fg] = 3,
++ [f71889fg] = 3,
++ [f71889ed] = 3,
++ [f71889a] = 3,
++ [f8000] = 3,
++ [f81865f] = 2,
+ };
+
+ static struct platform_device *f71882fg_pdev;
+@@ -111,7 +281,7 @@
+ /* Super-I/O Function prototypes */
+ static inline int superio_inb(int base, int reg);
+ static inline int superio_inw(int base, int reg);
+-static inline void superio_enter(int base);
++static inline int superio_enter(int base);
+ static inline void superio_select(int base, int ld);
+ static inline void superio_exit(int base);
+
+@@ -122,16 +292,21 @@
+ struct f71882fg_data {
+ unsigned short addr;
+ enum chips type;
++#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 23)
++ struct class_device *hwmon_dev;
++#else
+ struct device *hwmon_dev;
++#endif
+
+ struct mutex update_lock;
+ int temp_start; /* temp numbering start (0 or 1) */
+ char valid; /* !=0 if following fields are valid */
++ char auto_point_temp_signed;
+ unsigned long last_updated; /* In jiffies */
+ unsigned long last_limits; /* In jiffies */
+
+ /* Register Values */
+- u8 in[9];
++ u8 in[F71882FG_MAX_INS];
+ u8 in1_max;
+ u8 in_status;
+ u8 in_beep;
+@@ -140,7 +315,7 @@
+ u16 fan_full_speed[4];
+ u8 fan_status;
+ u8 fan_beep;
+- /* Note: all models have only 3 temperature channels, but on some
++ /* Note: all models have max 3 temperature channels, but on some
+ they are addressed as 0-2 and on others as 1-3, so for coding
+ convenience we reserve space for 4 channels */
+ u16 temp[4];
+@@ -218,6 +393,10 @@
+ char *buf);
+ static ssize_t store_pwm(struct device *dev, struct device_attribute *devattr,
+ const char *buf, size_t count);
++static ssize_t show_simple_pwm(struct device *dev,
++ struct device_attribute *devattr, char *buf);
++static ssize_t store_simple_pwm(struct device *dev,
++ struct device_attribute *devattr, const char *buf, size_t count);
+ static ssize_t show_pwm_enable(struct device *dev,
+ struct device_attribute *devattr, char *buf);
+ static ssize_t store_pwm_enable(struct device *dev,
+@@ -260,13 +439,9 @@
+
+ static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
+
+-/* Temp and in attr for the f71858fg, the f71858fg is special as it
+- has its temperature indexes start at 0 (the others start at 1) and
+- it only has 3 voltage inputs */
+-static struct sensor_device_attribute_2 f71858fg_in_temp_attr[] = {
+- SENSOR_ATTR_2(in0_input, S_IRUGO, show_in, NULL, 0, 0),
+- SENSOR_ATTR_2(in1_input, S_IRUGO, show_in, NULL, 0, 1),
+- SENSOR_ATTR_2(in2_input, S_IRUGO, show_in, NULL, 0, 2),
++/* Temp attr for the f71858fg, the f71858fg is special as it has its
++ temperature indexes start at 0 (the others start at 1) */
++static struct sensor_device_attribute_2 f71858fg_temp_attr[] = {
+ SENSOR_ATTR_2(temp1_input, S_IRUGO, show_temp, NULL, 0, 0),
+ SENSOR_ATTR_2(temp1_max, S_IRUGO|S_IWUSR, show_temp_max,
+ store_temp_max, 0, 0),
+@@ -290,7 +465,6 @@
+ SENSOR_ATTR_2(temp2_crit_hyst, S_IRUGO, show_temp_crit_hyst, NULL,
+ 0, 1),
+ SENSOR_ATTR_2(temp2_crit_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 5),
+- SENSOR_ATTR_2(temp2_type, S_IRUGO, show_temp_type, NULL, 0, 1),
+ SENSOR_ATTR_2(temp2_fault, S_IRUGO, show_temp_fault, NULL, 0, 1),
+ SENSOR_ATTR_2(temp3_input, S_IRUGO, show_temp, NULL, 0, 2),
+ SENSOR_ATTR_2(temp3_max, S_IRUGO|S_IWUSR, show_temp_max,
+@@ -306,17 +480,8 @@
+ SENSOR_ATTR_2(temp3_fault, S_IRUGO, show_temp_fault, NULL, 0, 2),
+ };
+
+-/* Temp and in attr common to the f71862fg, f71882fg and f71889fg */
+-static struct sensor_device_attribute_2 fxxxx_in_temp_attr[] = {
+- SENSOR_ATTR_2(in0_input, S_IRUGO, show_in, NULL, 0, 0),
+- SENSOR_ATTR_2(in1_input, S_IRUGO, show_in, NULL, 0, 1),
+- SENSOR_ATTR_2(in2_input, S_IRUGO, show_in, NULL, 0, 2),
+- SENSOR_ATTR_2(in3_input, S_IRUGO, show_in, NULL, 0, 3),
+- SENSOR_ATTR_2(in4_input, S_IRUGO, show_in, NULL, 0, 4),
+- SENSOR_ATTR_2(in5_input, S_IRUGO, show_in, NULL, 0, 5),
+- SENSOR_ATTR_2(in6_input, S_IRUGO, show_in, NULL, 0, 6),
+- SENSOR_ATTR_2(in7_input, S_IRUGO, show_in, NULL, 0, 7),
+- SENSOR_ATTR_2(in8_input, S_IRUGO, show_in, NULL, 0, 8),
++/* Temp attr for the standard models */
++static struct sensor_device_attribute_2 fxxxx_temp_attr[3][9] = { {
+ SENSOR_ATTR_2(temp1_input, S_IRUGO, show_temp, NULL, 0, 1),
+ SENSOR_ATTR_2(temp1_max, S_IRUGO|S_IWUSR, show_temp_max,
+ store_temp_max, 0, 1),
+@@ -326,17 +491,14 @@
+ the max and crit alarms separately and lm_sensors v2 depends on the
+ presence of temp#_alarm files. The same goes for temp2/3 _alarm. */
+ SENSOR_ATTR_2(temp1_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 1),
+- SENSOR_ATTR_2(temp1_max_beep, S_IRUGO|S_IWUSR, show_temp_beep,
+- store_temp_beep, 0, 1),
+ SENSOR_ATTR_2(temp1_crit, S_IRUGO|S_IWUSR, show_temp_crit,
+ store_temp_crit, 0, 1),
+ SENSOR_ATTR_2(temp1_crit_hyst, S_IRUGO, show_temp_crit_hyst, NULL,
+ 0, 1),
+ SENSOR_ATTR_2(temp1_crit_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 5),
+- SENSOR_ATTR_2(temp1_crit_beep, S_IRUGO|S_IWUSR, show_temp_beep,
+- store_temp_beep, 0, 5),
+ SENSOR_ATTR_2(temp1_type, S_IRUGO, show_temp_type, NULL, 0, 1),
+ SENSOR_ATTR_2(temp1_fault, S_IRUGO, show_temp_fault, NULL, 0, 1),
++}, {
+ SENSOR_ATTR_2(temp2_input, S_IRUGO, show_temp, NULL, 0, 2),
+ SENSOR_ATTR_2(temp2_max, S_IRUGO|S_IWUSR, show_temp_max,
+ store_temp_max, 0, 2),
+@@ -344,17 +506,14 @@
+ store_temp_max_hyst, 0, 2),
+ /* Should be temp2_max_alarm, see temp1_alarm note */
+ SENSOR_ATTR_2(temp2_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 2),
+- SENSOR_ATTR_2(temp2_max_beep, S_IRUGO|S_IWUSR, show_temp_beep,
+- store_temp_beep, 0, 2),
+ SENSOR_ATTR_2(temp2_crit, S_IRUGO|S_IWUSR, show_temp_crit,
+ store_temp_crit, 0, 2),
+ SENSOR_ATTR_2(temp2_crit_hyst, S_IRUGO, show_temp_crit_hyst, NULL,
+ 0, 2),
+ SENSOR_ATTR_2(temp2_crit_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 6),
+- SENSOR_ATTR_2(temp2_crit_beep, S_IRUGO|S_IWUSR, show_temp_beep,
+- store_temp_beep, 0, 6),
+ SENSOR_ATTR_2(temp2_type, S_IRUGO, show_temp_type, NULL, 0, 2),
+ SENSOR_ATTR_2(temp2_fault, S_IRUGO, show_temp_fault, NULL, 0, 2),
++}, {
+ SENSOR_ATTR_2(temp3_input, S_IRUGO, show_temp, NULL, 0, 3),
+ SENSOR_ATTR_2(temp3_max, S_IRUGO|S_IWUSR, show_temp_max,
+ store_temp_max, 0, 3),
+@@ -362,37 +521,39 @@
+ store_temp_max_hyst, 0, 3),
+ /* Should be temp3_max_alarm, see temp1_alarm note */
+ SENSOR_ATTR_2(temp3_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 3),
+- SENSOR_ATTR_2(temp3_max_beep, S_IRUGO|S_IWUSR, show_temp_beep,
+- store_temp_beep, 0, 3),
+ SENSOR_ATTR_2(temp3_crit, S_IRUGO|S_IWUSR, show_temp_crit,
+ store_temp_crit, 0, 3),
+ SENSOR_ATTR_2(temp3_crit_hyst, S_IRUGO, show_temp_crit_hyst, NULL,
+ 0, 3),
+ SENSOR_ATTR_2(temp3_crit_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 7),
+- SENSOR_ATTR_2(temp3_crit_beep, S_IRUGO|S_IWUSR, show_temp_beep,
+- store_temp_beep, 0, 7),
+ SENSOR_ATTR_2(temp3_type, S_IRUGO, show_temp_type, NULL, 0, 3),
+ SENSOR_ATTR_2(temp3_fault, S_IRUGO, show_temp_fault, NULL, 0, 3),
+-};
++} };
+
+-/* For models with in1 alarm capability */
+-static struct sensor_device_attribute_2 fxxxx_in1_alarm_attr[] = {
+- SENSOR_ATTR_2(in1_max, S_IRUGO|S_IWUSR, show_in_max, store_in_max,
+- 0, 1),
+- SENSOR_ATTR_2(in1_beep, S_IRUGO|S_IWUSR, show_in_beep, store_in_beep,
+- 0, 1),
+- SENSOR_ATTR_2(in1_alarm, S_IRUGO, show_in_alarm, NULL, 0, 1),
+-};
++/* Temp attr for models which can beep on temp alarm */
++static struct sensor_device_attribute_2 fxxxx_temp_beep_attr[3][2] = { {
++ SENSOR_ATTR_2(temp1_max_beep, S_IRUGO|S_IWUSR, show_temp_beep,
++ store_temp_beep, 0, 1),
++ SENSOR_ATTR_2(temp1_crit_beep, S_IRUGO|S_IWUSR, show_temp_beep,
++ store_temp_beep, 0, 5),
++}, {
++ SENSOR_ATTR_2(temp2_max_beep, S_IRUGO|S_IWUSR, show_temp_beep,
++ store_temp_beep, 0, 2),
++ SENSOR_ATTR_2(temp2_crit_beep, S_IRUGO|S_IWUSR, show_temp_beep,
++ store_temp_beep, 0, 6),
++}, {
++ SENSOR_ATTR_2(temp3_max_beep, S_IRUGO|S_IWUSR, show_temp_beep,
++ store_temp_beep, 0, 3),
++ SENSOR_ATTR_2(temp3_crit_beep, S_IRUGO|S_IWUSR, show_temp_beep,
++ store_temp_beep, 0, 7),
++} };
+
+-/* Temp and in attr for the f8000
++/* Temp attr for the f8000
+ Note on the f8000 temp_ovt (crit) is used as max, and temp_high (max)
+ is used as hysteresis value to clear alarms
+ Also like the f71858fg its temperature indexes start at 0
+ */
+-static struct sensor_device_attribute_2 f8000_in_temp_attr[] = {
+- SENSOR_ATTR_2(in0_input, S_IRUGO, show_in, NULL, 0, 0),
+- SENSOR_ATTR_2(in1_input, S_IRUGO, show_in, NULL, 0, 1),
+- SENSOR_ATTR_2(in2_input, S_IRUGO, show_in, NULL, 0, 2),
++static struct sensor_device_attribute_2 f8000_temp_attr[] = {
+ SENSOR_ATTR_2(temp1_input, S_IRUGO, show_temp, NULL, 0, 0),
+ SENSOR_ATTR_2(temp1_max, S_IRUGO|S_IWUSR, show_temp_crit,
+ store_temp_crit, 0, 0),
+@@ -406,7 +567,6 @@
+ SENSOR_ATTR_2(temp2_max_hyst, S_IRUGO|S_IWUSR, show_temp_max,
+ store_temp_max, 0, 1),
+ SENSOR_ATTR_2(temp2_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 5),
+- SENSOR_ATTR_2(temp2_type, S_IRUGO, show_temp_type, NULL, 0, 1),
+ SENSOR_ATTR_2(temp2_fault, S_IRUGO, show_temp_fault, NULL, 0, 1),
+ SENSOR_ATTR_2(temp3_input, S_IRUGO, show_temp, NULL, 0, 2),
+ SENSOR_ATTR_2(temp3_max, S_IRUGO|S_IWUSR, show_temp_crit,
+@@ -417,6 +577,28 @@
+ SENSOR_ATTR_2(temp3_fault, S_IRUGO, show_temp_fault, NULL, 0, 2),
+ };
+
++/* in attr for all models */
++static struct sensor_device_attribute_2 fxxxx_in_attr[] = {
++ SENSOR_ATTR_2(in0_input, S_IRUGO, show_in, NULL, 0, 0),
++ SENSOR_ATTR_2(in1_input, S_IRUGO, show_in, NULL, 0, 1),
++ SENSOR_ATTR_2(in2_input, S_IRUGO, show_in, NULL, 0, 2),
++ SENSOR_ATTR_2(in3_input, S_IRUGO, show_in, NULL, 0, 3),
++ SENSOR_ATTR_2(in4_input, S_IRUGO, show_in, NULL, 0, 4),
++ SENSOR_ATTR_2(in5_input, S_IRUGO, show_in, NULL, 0, 5),
++ SENSOR_ATTR_2(in6_input, S_IRUGO, show_in, NULL, 0, 6),
++ SENSOR_ATTR_2(in7_input, S_IRUGO, show_in, NULL, 0, 7),
++ SENSOR_ATTR_2(in8_input, S_IRUGO, show_in, NULL, 0, 8),
++};
++
++/* For models with in1 alarm capability */
++static struct sensor_device_attribute_2 fxxxx_in1_alarm_attr[] = {
++ SENSOR_ATTR_2(in1_max, S_IRUGO|S_IWUSR, show_in_max, store_in_max,
++ 0, 1),
++ SENSOR_ATTR_2(in1_beep, S_IRUGO|S_IWUSR, show_in_beep, store_in_beep,
++ 0, 1),
++ SENSOR_ATTR_2(in1_alarm, S_IRUGO, show_in_alarm, NULL, 0, 1),
++};
++
+ /* Fan / PWM attr common to all models */
+ static struct sensor_device_attribute_2 fxxxx_fan_attr[4][6] = { {
+ SENSOR_ATTR_2(fan1_input, S_IRUGO, show_fan, NULL, 0, 0),
+@@ -464,6 +646,14 @@
+ show_pwm_interpolate, store_pwm_interpolate, 0, 3),
+ } };
+
++/* Attr for the third fan of the f71808a, which only has manual pwm */
++static struct sensor_device_attribute_2 f71808a_fan3_attr[] = {
++ SENSOR_ATTR_2(fan3_input, S_IRUGO, show_fan, NULL, 0, 2),
++ SENSOR_ATTR_2(fan3_alarm, S_IRUGO, show_fan_alarm, NULL, 0, 2),
++ SENSOR_ATTR_2(pwm3, S_IRUGO|S_IWUSR,
++ show_simple_pwm, store_simple_pwm, 0, 2),
++};
++
+ /* Attr for models which can beep on Fan alarm */
+ static struct sensor_device_attribute_2 fxxxx_fan_beep_attr[] = {
+ SENSOR_ATTR_2(fan1_beep, S_IRUGO|S_IWUSR, show_fan_beep,
+@@ -477,7 +667,7 @@
+ };
+
+ /* PWM attr for the f71862fg, fewer pwms and fewer zones per pwm than the
+- f71858fg / f71882fg / f71889fg */
++ standard models */
+ static struct sensor_device_attribute_2 f71862fg_auto_pwm_attr[] = {
+ SENSOR_ATTR_2(pwm1_auto_channels_temp, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_channel,
+@@ -546,7 +736,87 @@
+ show_pwm_auto_point_temp_hyst, NULL, 3, 2),
+ };
+
+-/* PWM attr common to the f71858fg, f71882fg and f71889fg */
++/* PWM attr for the f71808e/f71869, almost identical to the f71862fg, but the
++ pwm setting when the temperature is above the pwmX_auto_point1_temp can be
++ programmed instead of being hardcoded to 0xff */
++static struct sensor_device_attribute_2 f71869_auto_pwm_attr[] = {
++ SENSOR_ATTR_2(pwm1_auto_channels_temp, S_IRUGO|S_IWUSR,
++ show_pwm_auto_point_channel,
++ store_pwm_auto_point_channel, 0, 0),
++ SENSOR_ATTR_2(pwm1_auto_point1_pwm, S_IRUGO|S_IWUSR,
++ show_pwm_auto_point_pwm, store_pwm_auto_point_pwm,
++ 0, 0),
++ SENSOR_ATTR_2(pwm1_auto_point2_pwm, S_IRUGO|S_IWUSR,
++ show_pwm_auto_point_pwm, store_pwm_auto_point_pwm,
++ 1, 0),
++ SENSOR_ATTR_2(pwm1_auto_point3_pwm, S_IRUGO|S_IWUSR,
++ show_pwm_auto_point_pwm, store_pwm_auto_point_pwm,
++ 4, 0),
++ SENSOR_ATTR_2(pwm1_auto_point1_temp, S_IRUGO|S_IWUSR,
++ show_pwm_auto_point_temp, store_pwm_auto_point_temp,
++ 0, 0),
++ SENSOR_ATTR_2(pwm1_auto_point2_temp, S_IRUGO|S_IWUSR,
++ show_pwm_auto_point_temp, store_pwm_auto_point_temp,
++ 3, 0),
++ SENSOR_ATTR_2(pwm1_auto_point1_temp_hyst, S_IRUGO|S_IWUSR,
++ show_pwm_auto_point_temp_hyst,
++ store_pwm_auto_point_temp_hyst,
++ 0, 0),
++ SENSOR_ATTR_2(pwm1_auto_point2_temp_hyst, S_IRUGO,
++ show_pwm_auto_point_temp_hyst, NULL, 3, 0),
++
++ SENSOR_ATTR_2(pwm2_auto_channels_temp, S_IRUGO|S_IWUSR,
++ show_pwm_auto_point_channel,
++ store_pwm_auto_point_channel, 0, 1),
++ SENSOR_ATTR_2(pwm2_auto_point1_pwm, S_IRUGO|S_IWUSR,
++ show_pwm_auto_point_pwm, store_pwm_auto_point_pwm,
++ 0, 1),
++ SENSOR_ATTR_2(pwm2_auto_point2_pwm, S_IRUGO|S_IWUSR,
++ show_pwm_auto_point_pwm, store_pwm_auto_point_pwm,
++ 1, 1),
++ SENSOR_ATTR_2(pwm2_auto_point3_pwm, S_IRUGO|S_IWUSR,
++ show_pwm_auto_point_pwm, store_pwm_auto_point_pwm,
++ 4, 1),
++ SENSOR_ATTR_2(pwm2_auto_point1_temp, S_IRUGO|S_IWUSR,
++ show_pwm_auto_point_temp, store_pwm_auto_point_temp,
++ 0, 1),
++ SENSOR_ATTR_2(pwm2_auto_point2_temp, S_IRUGO|S_IWUSR,
++ show_pwm_auto_point_temp, store_pwm_auto_point_temp,
++ 3, 1),
++ SENSOR_ATTR_2(pwm2_auto_point1_temp_hyst, S_IRUGO|S_IWUSR,
++ show_pwm_auto_point_temp_hyst,
++ store_pwm_auto_point_temp_hyst,
++ 0, 1),
++ SENSOR_ATTR_2(pwm2_auto_point2_temp_hyst, S_IRUGO,
++ show_pwm_auto_point_temp_hyst, NULL, 3, 1),
++
++ SENSOR_ATTR_2(pwm3_auto_channels_temp, S_IRUGO|S_IWUSR,
++ show_pwm_auto_point_channel,
++ store_pwm_auto_point_channel, 0, 2),
++ SENSOR_ATTR_2(pwm3_auto_point1_pwm, S_IRUGO|S_IWUSR,
++ show_pwm_auto_point_pwm, store_pwm_auto_point_pwm,
++ 0, 2),
++ SENSOR_ATTR_2(pwm3_auto_point2_pwm, S_IRUGO|S_IWUSR,
++ show_pwm_auto_point_pwm, store_pwm_auto_point_pwm,
++ 1, 2),
++ SENSOR_ATTR_2(pwm3_auto_point3_pwm, S_IRUGO|S_IWUSR,
++ show_pwm_auto_point_pwm, store_pwm_auto_point_pwm,
++ 4, 2),
++ SENSOR_ATTR_2(pwm3_auto_point1_temp, S_IRUGO|S_IWUSR,
++ show_pwm_auto_point_temp, store_pwm_auto_point_temp,
++ 0, 2),
++ SENSOR_ATTR_2(pwm3_auto_point2_temp, S_IRUGO|S_IWUSR,
++ show_pwm_auto_point_temp, store_pwm_auto_point_temp,
++ 3, 2),
++ SENSOR_ATTR_2(pwm3_auto_point1_temp_hyst, S_IRUGO|S_IWUSR,
++ show_pwm_auto_point_temp_hyst,
++ store_pwm_auto_point_temp_hyst,
++ 0, 2),
++ SENSOR_ATTR_2(pwm3_auto_point2_temp_hyst, S_IRUGO,
++ show_pwm_auto_point_temp_hyst, NULL, 3, 2),
++};
++
++/* PWM attr for the standard models */
+ static struct sensor_device_attribute_2 fxxxx_auto_pwm_attr[4][14] = { {
+ SENSOR_ATTR_2(pwm1_auto_channels_temp, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_channel,
+@@ -861,11 +1131,19 @@
+ return val;
+ }
+
+-static inline void superio_enter(int base)
++static inline int superio_enter(int base)
+ {
++ /* Don't step on other drivers' I/O space by accident */
++ if (!request_muxed_region(base, 2, DRVNAME)) {
++ pr_err("I/O address 0x%04x already in use\n", base);
++ return -EBUSY;
++ }
++
+ /* according to the datasheet the key must be send twice! */
+ outb(SIO_UNLOCK_KEY, base);
+ outb(SIO_UNLOCK_KEY, base);
++
++ return 0;
+ }
+
+ static inline void superio_select(int base, int ld)
+@@ -877,6 +1155,7 @@
+ static inline void superio_exit(int base)
+ {
+ outb(SIO_LOCK_KEY, base);
++ release_region(base, 2);
+ }
+
+ static inline int fan_from_reg(u16 reg)
+@@ -932,16 +1211,16 @@
+ static struct f71882fg_data *f71882fg_update_device(struct device *dev)
+ {
+ struct f71882fg_data *data = dev_get_drvdata(dev);
+- int nr, reg = 0, reg2;
+- int nr_fans = (data->type == f71882fg) ? 4 : 3;
+- int nr_ins = (data->type == f71858fg || data->type == f8000) ? 3 : 9;
++ int nr_fans = f71882fg_nr_fans[data->type];
++ int nr_temps = f71882fg_nr_temps[data->type];
++ int nr, reg, point;
+
+ mutex_lock(&data->update_lock);
+
+ /* Update once every 60 seconds */
+ if (time_after(jiffies, data->last_limits + 60 * HZ) ||
+ !data->valid) {
+- if (data->type == f71882fg || data->type == f71889fg) {
++ if (f71882fg_has_in1_alarm[data->type]) {
+ data->in1_max =
+ f71882fg_read8(data, F71882FG_REG_IN1_HIGH);
+ data->in_beep =
+@@ -949,7 +1228,8 @@
+ }
+
+ /* Get High & boundary temps*/
+- for (nr = data->temp_start; nr < 3 + data->temp_start; nr++) {
++ for (nr = data->temp_start; nr < nr_temps + data->temp_start;
++ nr++) {
+ data->temp_ovt[nr] = f71882fg_read8(data,
+ F71882FG_REG_TEMP_OVT(nr));
+ data->temp_high[nr] = f71882fg_read8(data,
+@@ -962,45 +1242,21 @@
+ data->temp_hyst[1] = f71882fg_read8(data,
+ F71882FG_REG_TEMP_HYST(1));
+ }
++ /* All but the f71858fg / f8000 have this register */
++ if ((data->type != f71858fg) && (data->type != f8000)) {
++ reg = f71882fg_read8(data, F71882FG_REG_TEMP_TYPE);
++ data->temp_type[1] = (reg & 0x02) ? 2 : 4;
++ data->temp_type[2] = (reg & 0x04) ? 2 : 4;
++ data->temp_type[3] = (reg & 0x08) ? 2 : 4;
++ }
+
+- if (data->type == f71862fg || data->type == f71882fg ||
+- data->type == f71889fg) {
++ if (f71882fg_fan_has_beep[data->type])
+ data->fan_beep = f71882fg_read8(data,
+ F71882FG_REG_FAN_BEEP);
++
++ if (f71882fg_temp_has_beep[data->type])
+ data->temp_beep = f71882fg_read8(data,
+ F71882FG_REG_TEMP_BEEP);
+- /* Have to hardcode type, because temp1 is special */
+- reg = f71882fg_read8(data, F71882FG_REG_TEMP_TYPE);
+- data->temp_type[2] = (reg & 0x04) ? 2 : 4;
+- data->temp_type[3] = (reg & 0x08) ? 2 : 4;
+- }
+- /* Determine temp index 1 sensor type */
+- if (data->type == f71889fg) {
+- reg2 = f71882fg_read8(data, F71882FG_REG_START);
+- switch ((reg2 & 0x60) >> 5) {
+- case 0x00: /* BJT / Thermistor */
+- data->temp_type[1] = (reg & 0x02) ? 2 : 4;
+- break;
+- case 0x01: /* AMDSI */
+- data->temp_type[1] = 5;
+- break;
+- case 0x02: /* PECI */
+- case 0x03: /* Ibex Peak ?? Report as PECI for now */
+- data->temp_type[1] = 6;
+- break;
+- }
+- } else {
+- reg2 = f71882fg_read8(data, F71882FG_REG_PECI);
+- if ((reg2 & 0x03) == 0x01)
+- data->temp_type[1] = 6; /* PECI */
+- else if ((reg2 & 0x03) == 0x02)
+- data->temp_type[1] = 5; /* AMDSI */
+- else if (data->type == f71862fg ||
+- data->type == f71882fg)
+- data->temp_type[1] = (reg & 0x02) ? 2 : 4;
+- else /* f71858fg and f8000 only support BJT */
+- data->temp_type[1] = 2;
+- }
+
+ data->pwm_enable = f71882fg_read8(data,
+ F71882FG_REG_PWM_ENABLE);
+@@ -1014,8 +1270,8 @@
+ f71882fg_read8(data,
+ F71882FG_REG_POINT_MAPPING(nr));
+
+- if (data->type != f71862fg) {
+- int point;
++ switch (data->type) {
++ default:
+ for (point = 0; point < 5; point++) {
+ data->pwm_auto_point_pwm[nr][point] =
+ f71882fg_read8(data,
+@@ -1028,7 +1284,14 @@
+ F71882FG_REG_POINT_TEMP
+ (nr, point));
+ }
+- } else {
++ break;
++ case f71808e:
++ case f71869:
++ data->pwm_auto_point_pwm[nr][0] =
++ f71882fg_read8(data,
++ F71882FG_REG_POINT_PWM(nr, 0));
++ /* Fall through */
++ case f71862fg:
+ data->pwm_auto_point_pwm[nr][1] =
+ f71882fg_read8(data,
+ F71882FG_REG_POINT_PWM
+@@ -1045,6 +1308,7 @@
+ f71882fg_read8(data,
+ F71882FG_REG_POINT_TEMP
+ (nr, 3));
++ break;
+ }
+ }
+ data->last_limits = jiffies;
+@@ -1056,7 +1320,8 @@
+ F71882FG_REG_TEMP_STATUS);
+ data->temp_diode_open = f71882fg_read8(data,
+ F71882FG_REG_TEMP_DIODE_OPEN);
+- for (nr = data->temp_start; nr < 3 + data->temp_start; nr++)
++ for (nr = data->temp_start; nr < nr_temps + data->temp_start;
++ nr++)
+ data->temp[nr] = f71882fg_read_temp(data, nr);
+
+ data->fan_status = f71882fg_read8(data,
+@@ -1072,17 +1337,24 @@
+ data->pwm[nr] =
+ f71882fg_read8(data, F71882FG_REG_PWM(nr));
+ }
+-
+- /* The f8000 can monitor 1 more fan, but has no pwm for it */
++ /* Some models have 1 more fan with limited capabilities */
++ if (data->type == f71808a) {
++ data->fan[2] = f71882fg_read16(data,
++ F71882FG_REG_FAN(2));
++ data->pwm[2] = f71882fg_read8(data,
++ F71882FG_REG_PWM(2));
++ }
+ if (data->type == f8000)
+ data->fan[3] = f71882fg_read16(data,
+ F71882FG_REG_FAN(3));
+- if (data->type == f71882fg || data->type == f71889fg)
++
++ if (f71882fg_has_in1_alarm[data->type])
+ data->in_status = f71882fg_read8(data,
+ F71882FG_REG_IN_STATUS);
+- for (nr = 0; nr < nr_ins; nr++)
+- data->in[nr] = f71882fg_read8(data,
+- F71882FG_REG_IN(nr));
++ for (nr = 0; nr < F71882FG_MAX_INS; nr++)
++ if (f71882fg_has_in[data->type][nr])
++ data->in[nr] = f71882fg_read8(data,
++ F71882FG_REG_IN(nr));
+
+ data->last_updated = jiffies;
+ data->valid = 1;
+@@ -1561,6 +1833,38 @@
+ return count;
+ }
+
++static ssize_t show_simple_pwm(struct device *dev,
++ struct device_attribute *devattr, char *buf)
++{
++ struct f71882fg_data *data = f71882fg_update_device(dev);
++ int val, nr = to_sensor_dev_attr_2(devattr)->index;
++
++ val = data->pwm[nr];
++ return sprintf(buf, "%d\n", val);
++}
++
++static ssize_t store_simple_pwm(struct device *dev,
++ struct device_attribute *devattr,
++ const char *buf, size_t count)
++{
++ struct f71882fg_data *data = dev_get_drvdata(dev);
++ int err, nr = to_sensor_dev_attr_2(devattr)->index;
++ long val;
++
++ err = strict_strtol(buf, 10, &val);
++ if (err)
++ return err;
++
++ val = SENSORS_LIMIT(val, 0, 255);
++
++ mutex_lock(&data->update_lock);
++ f71882fg_write8(data, F71882FG_REG_PWM(nr), val);
++ data->pwm[nr] = val;
++ mutex_unlock(&data->update_lock);
++
++ return count;
++}
++
+ static ssize_t show_pwm_enable(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+ {
+@@ -1871,7 +2175,7 @@
+
+ val /= 1000;
+
+- if (data->type == f71889fg)
++ if (data->auto_point_temp_signed)
+ val = SENSORS_LIMIT(val, -128, 127);
+ else
+ val = SENSORS_LIMIT(val, 0, 127);
+@@ -1917,8 +2221,10 @@
+ {
+ struct f71882fg_data *data;
+ struct f71882fg_sio_data *sio_data = pdev->dev.platform_data;
+- int err, i, nr_fans = (sio_data->type == f71882fg) ? 4 : 3;
+- u8 start_reg;
++ int nr_fans = f71882fg_nr_fans[sio_data->type];
++ int nr_temps = f71882fg_nr_temps[sio_data->type];
++ int err, i;
++ u8 start_reg, reg;
+
+ data = kzalloc(sizeof(struct f71882fg_data), GFP_KERNEL);
+ if (!data)
+@@ -1957,37 +2263,75 @@
+ /* The f71858fg temperature alarms behave as
+ the f8000 alarms in this mode */
+ err = f71882fg_create_sysfs_files(pdev,
+- f8000_in_temp_attr,
+- ARRAY_SIZE(f8000_in_temp_attr));
++ f8000_temp_attr,
++ ARRAY_SIZE(f8000_temp_attr));
+ else
+ err = f71882fg_create_sysfs_files(pdev,
+- f71858fg_in_temp_attr,
+- ARRAY_SIZE(f71858fg_in_temp_attr));
+- break;
+- case f71882fg:
+- case f71889fg:
+- err = f71882fg_create_sysfs_files(pdev,
+- fxxxx_in1_alarm_attr,
+- ARRAY_SIZE(fxxxx_in1_alarm_attr));
+- if (err)
+- goto exit_unregister_sysfs;
+- /* fall through! */
+- case f71862fg:
+- err = f71882fg_create_sysfs_files(pdev,
+- fxxxx_in_temp_attr,
+- ARRAY_SIZE(fxxxx_in_temp_attr));
++ f71858fg_temp_attr,
++ ARRAY_SIZE(f71858fg_temp_attr));
+ break;
+ case f8000:
+ err = f71882fg_create_sysfs_files(pdev,
+- f8000_in_temp_attr,
+- ARRAY_SIZE(f8000_in_temp_attr));
++ f8000_temp_attr,
++ ARRAY_SIZE(f8000_temp_attr));
+ break;
++ default:
++ err = f71882fg_create_sysfs_files(pdev,
++ &fxxxx_temp_attr[0][0],
++ ARRAY_SIZE(fxxxx_temp_attr[0]) * nr_temps);
+ }
+ if (err)
+ goto exit_unregister_sysfs;
++
++ if (f71882fg_temp_has_beep[data->type]) {
++ err = f71882fg_create_sysfs_files(pdev,
++ &fxxxx_temp_beep_attr[0][0],
++ ARRAY_SIZE(fxxxx_temp_beep_attr[0])
++ * nr_temps);
++ if (err)
++ goto exit_unregister_sysfs;
++ }
++
++ for (i = 0; i < F71882FG_MAX_INS; i++) {
++ if (f71882fg_has_in[data->type][i]) {
++ err = device_create_file(&pdev->dev,
++ &fxxxx_in_attr[i].dev_attr);
++ if (err)
++ goto exit_unregister_sysfs;
++ }
++ }
++ if (f71882fg_has_in1_alarm[data->type]) {
++ err = f71882fg_create_sysfs_files(pdev,
++ fxxxx_in1_alarm_attr,
++ ARRAY_SIZE(fxxxx_in1_alarm_attr));
++ if (err)
++ goto exit_unregister_sysfs;
++ }
+ }
+
+ if (start_reg & 0x02) {
++ switch (data->type) {
++ case f71808e:
++ case f71808a:
++ case f71869:
++ case f71869a:
++ /* These always have signed auto point temps */
++ data->auto_point_temp_signed = 1;
++ /* Fall through to select correct fan/pwm reg bank! */
++ case f71889fg:
++ case f71889ed:
++ case f71889a:
++ reg = f71882fg_read8(data, F71882FG_REG_FAN_FAULT_T);
++ if (reg & F71882FG_FAN_NEG_TEMP_EN)
++ data->auto_point_temp_signed = 1;
++ /* Ensure banked pwm registers point to right bank */
++ reg &= ~F71882FG_FAN_PROG_SEL;
++ f71882fg_write8(data, F71882FG_REG_FAN_FAULT_T, reg);
++ break;
++ default:
++ break;
++ }
++
+ data->pwm_enable =
+ f71882fg_read8(data, F71882FG_REG_PWM_ENABLE);
+
+@@ -2002,13 +2346,12 @@
+ case f71862fg:
+ err = (data->pwm_enable & 0x15) != 0x15;
+ break;
+- case f71882fg:
+- case f71889fg:
+- err = 0;
+- break;
+ case f8000:
+ err = data->pwm_enable & 0x20;
+ break;
++ default:
++ err = 0;
++ break;
+ }
+ if (err) {
+ dev_err(&pdev->dev,
+@@ -2023,8 +2366,7 @@
+ if (err)
+ goto exit_unregister_sysfs;
+
+- if (data->type == f71862fg || data->type == f71882fg ||
+- data->type == f71889fg) {
++ if (f71882fg_fan_has_beep[data->type]) {
+ err = f71882fg_create_sysfs_files(pdev,
+ fxxxx_fan_beep_attr, nr_fans);
+ if (err)
+@@ -2032,11 +2374,55 @@
+ }
+
+ switch (data->type) {
++ case f71808e:
++ case f71808a:
++ case f71869:
++ case f71869a:
++ case f71889fg:
++ case f71889ed:
++ case f71889a:
++ for (i = 0; i < nr_fans; i++) {
++ data->pwm_auto_point_mapping[i] =
++ f71882fg_read8(data,
++ F71882FG_REG_POINT_MAPPING(i));
++ if ((data->pwm_auto_point_mapping[i] & 0x80) ||
++ (data->pwm_auto_point_mapping[i] & 3) == 0)
++ break;
++ }
++ if (i != nr_fans) {
++ dev_warn(&pdev->dev,
++ "Auto pwm controlled by raw digital "
++ "data, disabling pwm auto_point "
++ "sysfs attributes\n");
++ goto no_pwm_auto_point;
++ }
++ break;
++ default:
++ break;
++ }
++
++ switch (data->type) {
++ case f71808a:
++ err = f71882fg_create_sysfs_files(pdev,
++ &fxxxx_auto_pwm_attr[0][0],
++ ARRAY_SIZE(fxxxx_auto_pwm_attr[0]) * nr_fans);
++ if (err)
++ goto exit_unregister_sysfs;
++ err = f71882fg_create_sysfs_files(pdev,
++ f71808a_fan3_attr,
++ ARRAY_SIZE(f71808a_fan3_attr));
++ break;
+ case f71862fg:
+ err = f71882fg_create_sysfs_files(pdev,
+ f71862fg_auto_pwm_attr,
+ ARRAY_SIZE(f71862fg_auto_pwm_attr));
+ break;
++ case f71808e:
++ case f71869:
++ err = f71882fg_create_sysfs_files(pdev,
++ f71869_auto_pwm_attr,
++ ARRAY_SIZE(f71869_auto_pwm_attr));
++ break;
+ case f8000:
+ err = f71882fg_create_sysfs_files(pdev,
+ f8000_fan_attr,
+@@ -2047,23 +2433,7 @@
+ f8000_auto_pwm_attr,
+ ARRAY_SIZE(f8000_auto_pwm_attr));
+ break;
+- case f71889fg:
+- for (i = 0; i < nr_fans; i++) {
+- data->pwm_auto_point_mapping[i] =
+- f71882fg_read8(data,
+- F71882FG_REG_POINT_MAPPING(i));
+- if (data->pwm_auto_point_mapping[i] & 0x80)
+- break;
+- }
+- if (i != nr_fans) {
+- dev_warn(&pdev->dev,
+- "Auto pwm controlled by raw digital "
+- "data, disabling pwm auto_point "
+- "sysfs attributes\n");
+- break;
+- }
+- /* fall through */
+- default: /* f71858fg / f71882fg */
++ default:
+ err = f71882fg_create_sysfs_files(pdev,
+ &fxxxx_auto_pwm_attr[0][0],
+ ARRAY_SIZE(fxxxx_auto_pwm_attr[0]) * nr_fans);
+@@ -2071,6 +2441,7 @@
+ if (err)
+ goto exit_unregister_sysfs;
+
++no_pwm_auto_point:
+ for (i = 0; i < nr_fans; i++)
+ dev_info(&pdev->dev, "Fan: %d is in %s mode\n", i + 1,
+ (data->pwm_enable & (1 << 2 * i)) ?
+@@ -2097,7 +2468,9 @@
+ static int f71882fg_remove(struct platform_device *pdev)
+ {
+ struct f71882fg_data *data = platform_get_drvdata(pdev);
+- int nr_fans = (data->type == f71882fg) ? 4 : 3;
++ int nr_fans = f71882fg_nr_fans[data->type];
++ int nr_temps = f71882fg_nr_temps[data->type];
++ int i;
+ u8 start_reg = f71882fg_read8(data, F71882FG_REG_START);
+
+ if (data->hwmon_dev)
+@@ -2110,29 +2483,39 @@
+ case f71858fg:
+ if (data->temp_config & 0x10)
+ f71882fg_remove_sysfs_files(pdev,
+- f8000_in_temp_attr,
+- ARRAY_SIZE(f8000_in_temp_attr));
++ f8000_temp_attr,
++ ARRAY_SIZE(f8000_temp_attr));
+ else
+ f71882fg_remove_sysfs_files(pdev,
+- f71858fg_in_temp_attr,
+- ARRAY_SIZE(f71858fg_in_temp_attr));
+- break;
+- case f71882fg:
+- case f71889fg:
+- f71882fg_remove_sysfs_files(pdev,
+- fxxxx_in1_alarm_attr,
+- ARRAY_SIZE(fxxxx_in1_alarm_attr));
+- /* fall through! */
+- case f71862fg:
+- f71882fg_remove_sysfs_files(pdev,
+- fxxxx_in_temp_attr,
+- ARRAY_SIZE(fxxxx_in_temp_attr));
++ f71858fg_temp_attr,
++ ARRAY_SIZE(f71858fg_temp_attr));
+ break;
+ case f8000:
+ f71882fg_remove_sysfs_files(pdev,
+- f8000_in_temp_attr,
+- ARRAY_SIZE(f8000_in_temp_attr));
++ f8000_temp_attr,
++ ARRAY_SIZE(f8000_temp_attr));
+ break;
++ default:
++ f71882fg_remove_sysfs_files(pdev,
++ &fxxxx_temp_attr[0][0],
++ ARRAY_SIZE(fxxxx_temp_attr[0]) * nr_temps);
++ }
++ if (f71882fg_temp_has_beep[data->type]) {
++ f71882fg_remove_sysfs_files(pdev,
++ &fxxxx_temp_beep_attr[0][0],
++ ARRAY_SIZE(fxxxx_temp_beep_attr[0]) * nr_temps);
++ }
++
++ for (i = 0; i < F71882FG_MAX_INS; i++) {
++ if (f71882fg_has_in[data->type][i]) {
++ device_remove_file(&pdev->dev,
++ &fxxxx_in_attr[i].dev_attr);
++ }
++ }
++ if (f71882fg_has_in1_alarm[data->type]) {
++ f71882fg_remove_sysfs_files(pdev,
++ fxxxx_in1_alarm_attr,
++ ARRAY_SIZE(fxxxx_in1_alarm_attr));
+ }
+ }
+
+@@ -2140,17 +2523,31 @@
+ f71882fg_remove_sysfs_files(pdev, &fxxxx_fan_attr[0][0],
+ ARRAY_SIZE(fxxxx_fan_attr[0]) * nr_fans);
+
+- if (data->type == f71862fg || data->type == f71882fg ||
+- data->type == f71889fg)
++ if (f71882fg_fan_has_beep[data->type]) {
+ f71882fg_remove_sysfs_files(pdev,
+ fxxxx_fan_beep_attr, nr_fans);
++ }
+
+ switch (data->type) {
++ case f71808a:
++ f71882fg_remove_sysfs_files(pdev,
++ &fxxxx_auto_pwm_attr[0][0],
++ ARRAY_SIZE(fxxxx_auto_pwm_attr[0]) * nr_fans);
++ f71882fg_remove_sysfs_files(pdev,
++ f71808a_fan3_attr,
++ ARRAY_SIZE(f71808a_fan3_attr));
++ break;
+ case f71862fg:
+ f71882fg_remove_sysfs_files(pdev,
+ f71862fg_auto_pwm_attr,
+ ARRAY_SIZE(f71862fg_auto_pwm_attr));
+ break;
++ case f71808e:
++ case f71869:
++ f71882fg_remove_sysfs_files(pdev,
++ f71869_auto_pwm_attr,
++ ARRAY_SIZE(f71869_auto_pwm_attr));
++ break;
+ case f8000:
+ f71882fg_remove_sysfs_files(pdev,
+ f8000_fan_attr,
+@@ -2159,7 +2556,7 @@
+ f8000_auto_pwm_attr,
+ ARRAY_SIZE(f8000_auto_pwm_attr));
+ break;
+- default: /* f71858fg / f71882fg / f71889fg */
++ default:
+ f71882fg_remove_sysfs_files(pdev,
+ &fxxxx_auto_pwm_attr[0][0],
+ ARRAY_SIZE(fxxxx_auto_pwm_attr[0]) * nr_fans);
+@@ -2175,44 +2572,60 @@
+ static int __init f71882fg_find(int sioaddr, unsigned short *address,
+ struct f71882fg_sio_data *sio_data)
+ {
+- int err = -ENODEV;
+ u16 devid;
+-
+- /* Don't step on other drivers' I/O space by accident */
+- if (!request_region(sioaddr, 2, DRVNAME)) {
+- printk(KERN_ERR DRVNAME ": I/O address 0x%04x already in use\n",
+- (int)sioaddr);
+- return -EBUSY;
+- }
+-
+- superio_enter(sioaddr);
++ int err = superio_enter(sioaddr);
++ if (err)
++ return err;
+
+ devid = superio_inw(sioaddr, SIO_REG_MANID);
+ if (devid != SIO_FINTEK_ID) {
+- pr_debug(DRVNAME ": Not a Fintek device\n");
++ pr_debug("Not a Fintek device\n");
++ err = -ENODEV;
+ goto exit;
+ }
+
+ devid = force_id ? force_id : superio_inw(sioaddr, SIO_REG_DEVID);
+ switch (devid) {
++ case SIO_F71808E_ID:
++ sio_data->type = f71808e;
++ break;
++ case SIO_F71808A_ID:
++ sio_data->type = f71808a;
++ break;
+ case SIO_F71858_ID:
+ sio_data->type = f71858fg;
+ break;
+ case SIO_F71862_ID:
+ sio_data->type = f71862fg;
+ break;
++ case SIO_F71869_ID:
++ sio_data->type = f71869;
++ break;
++ case SIO_F71869A_ID:
++ sio_data->type = f71869a;
++ break;
+ case SIO_F71882_ID:
+ sio_data->type = f71882fg;
+ break;
+ case SIO_F71889_ID:
+ sio_data->type = f71889fg;
+ break;
++ case SIO_F71889E_ID:
++ sio_data->type = f71889ed;
++ break;
++ case SIO_F71889A_ID:
++ sio_data->type = f71889a;
++ break;
+ case SIO_F8000_ID:
+ sio_data->type = f8000;
+ break;
++ case SIO_F81865_ID:
++ sio_data->type = f81865f;
++ break;
+ default:
+- printk(KERN_INFO DRVNAME ": Unsupported Fintek device: %04x\n",
+- (unsigned int)devid);
++ pr_info("Unsupported Fintek device: %04x\n",
++ (unsigned int)devid);
++ err = -ENODEV;
+ goto exit;
+ }
+
+@@ -2222,29 +2635,34 @@
+ superio_select(sioaddr, SIO_F71882FG_LD_HWM);
+
+ if (!(superio_inb(sioaddr, SIO_REG_ENABLE) & 0x01)) {
+- printk(KERN_WARNING DRVNAME ": Device not activated\n");
++ pr_warn("Device not activated\n");
++ err = -ENODEV;
+ goto exit;
+ }
+
+ *address = superio_inw(sioaddr, SIO_REG_ADDR);
+ if (*address == 0) {
+- printk(KERN_WARNING DRVNAME ": Base address not set\n");
++ pr_warn("Base address not set\n");
++ err = -ENODEV;
+ goto exit;
+ }
+ *address &= ~(REGION_LENGTH - 1); /* Ignore 3 LSB */
+
+ err = 0;
+- printk(KERN_INFO DRVNAME ": Found %s chip at %#x, revision %d\n",
++ pr_info("Found %s chip at %#x, revision %d\n",
+ f71882fg_names[sio_data->type], (unsigned int)*address,
+ (int)superio_inb(sioaddr, SIO_REG_DEVREV));
+ exit:
+ superio_exit(sioaddr);
+- release_region(sioaddr, 2);
+ return err;
+ }
+
+ static int __init f71882fg_device_add(unsigned short address,
++#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 19)
++ struct f71882fg_sio_data *sio_data)
++#else
+ const struct f71882fg_sio_data *sio_data)
++#endif
+ {
+ struct resource res = {
+ .start = address,
+@@ -2264,20 +2682,20 @@
+
+ err = platform_device_add_resources(f71882fg_pdev, &res, 1);
+ if (err) {
+- printk(KERN_ERR DRVNAME ": Device resource addition failed\n");
++ pr_err("Device resource addition failed\n");
+ goto exit_device_put;
+ }
+
+ err = platform_device_add_data(f71882fg_pdev, sio_data,
+ sizeof(struct f71882fg_sio_data));
+ if (err) {
+- printk(KERN_ERR DRVNAME ": Platform data allocation failed\n");
++ pr_err("Platform data allocation failed\n");
+ goto exit_device_put;
+ }
+
+ err = platform_device_add(f71882fg_pdev);
+ if (err) {
+- printk(KERN_ERR DRVNAME ": Device addition failed\n");
++ pr_err("Device addition failed\n");
+ goto exit_device_put;
+ }
+
+@@ -2324,7 +2742,7 @@
+ }
+
+ MODULE_DESCRIPTION("F71882FG Hardware Monitoring Driver");
+-MODULE_AUTHOR("Hans Edgington, Hans de Goede (hde...@re...)");
++MODULE_AUTHOR("Hans Edgington, Hans de Goede <hde...@re...>");
+ MODULE_LICENSE("GPL");
+
+ module_init(f71882fg_init);
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|