18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * abituguru3.c
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (c) 2006-2008 Hans de Goede <hdegoede@redhat.com>
68c2ecf20Sopenharmony_ci * Copyright (c) 2008 Alistair John Strachan <alistair@devzero.co.uk>
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci/*
98c2ecf20Sopenharmony_ci * This driver supports the sensor part of revision 3 of the custom Abit uGuru
108c2ecf20Sopenharmony_ci * chip found on newer Abit uGuru motherboards. Note: because of lack of specs
118c2ecf20Sopenharmony_ci * only reading the sensors and their settings is supported.
128c2ecf20Sopenharmony_ci */
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#include <linux/module.h>
178c2ecf20Sopenharmony_ci#include <linux/init.h>
188c2ecf20Sopenharmony_ci#include <linux/slab.h>
198c2ecf20Sopenharmony_ci#include <linux/jiffies.h>
208c2ecf20Sopenharmony_ci#include <linux/mutex.h>
218c2ecf20Sopenharmony_ci#include <linux/err.h>
228c2ecf20Sopenharmony_ci#include <linux/delay.h>
238c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
248c2ecf20Sopenharmony_ci#include <linux/hwmon.h>
258c2ecf20Sopenharmony_ci#include <linux/hwmon-sysfs.h>
268c2ecf20Sopenharmony_ci#include <linux/dmi.h>
278c2ecf20Sopenharmony_ci#include <linux/io.h>
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci/* uGuru3 bank addresses */
308c2ecf20Sopenharmony_ci#define ABIT_UGURU3_SETTINGS_BANK		0x01
318c2ecf20Sopenharmony_ci#define ABIT_UGURU3_SENSORS_BANK		0x08
328c2ecf20Sopenharmony_ci#define ABIT_UGURU3_MISC_BANK			0x09
338c2ecf20Sopenharmony_ci#define ABIT_UGURU3_ALARMS_START		0x1E
348c2ecf20Sopenharmony_ci#define ABIT_UGURU3_SETTINGS_START		0x24
358c2ecf20Sopenharmony_ci#define ABIT_UGURU3_VALUES_START		0x80
368c2ecf20Sopenharmony_ci#define ABIT_UGURU3_BOARD_ID			0x0A
378c2ecf20Sopenharmony_ci/* uGuru3 sensor bank flags */			     /* Alarm if: */
388c2ecf20Sopenharmony_ci#define ABIT_UGURU3_TEMP_HIGH_ALARM_ENABLE	0x01 /*  temp over warn */
398c2ecf20Sopenharmony_ci#define ABIT_UGURU3_VOLT_HIGH_ALARM_ENABLE	0x02 /*  volt over max */
408c2ecf20Sopenharmony_ci#define ABIT_UGURU3_VOLT_LOW_ALARM_ENABLE	0x04 /*  volt under min */
418c2ecf20Sopenharmony_ci#define ABIT_UGURU3_TEMP_HIGH_ALARM_FLAG	0x10 /* temp is over warn */
428c2ecf20Sopenharmony_ci#define ABIT_UGURU3_VOLT_HIGH_ALARM_FLAG	0x20 /* volt is over max */
438c2ecf20Sopenharmony_ci#define ABIT_UGURU3_VOLT_LOW_ALARM_FLAG		0x40 /* volt is under min */
448c2ecf20Sopenharmony_ci#define ABIT_UGURU3_FAN_LOW_ALARM_ENABLE	0x01 /*   fan under min */
458c2ecf20Sopenharmony_ci#define ABIT_UGURU3_BEEP_ENABLE			0x08 /* beep if alarm */
468c2ecf20Sopenharmony_ci#define ABIT_UGURU3_SHUTDOWN_ENABLE		0x80 /* shutdown if alarm */
478c2ecf20Sopenharmony_ci/* sensor types */
488c2ecf20Sopenharmony_ci#define ABIT_UGURU3_IN_SENSOR			0
498c2ecf20Sopenharmony_ci#define ABIT_UGURU3_TEMP_SENSOR			1
508c2ecf20Sopenharmony_ci#define ABIT_UGURU3_FAN_SENSOR			2
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci/*
538c2ecf20Sopenharmony_ci * Timeouts / Retries, if these turn out to need a lot of fiddling we could
548c2ecf20Sopenharmony_ci * convert them to params. Determined by trial and error. I assume this is
558c2ecf20Sopenharmony_ci * cpu-speed independent, since the ISA-bus and not the CPU should be the
568c2ecf20Sopenharmony_ci * bottleneck.
578c2ecf20Sopenharmony_ci */
588c2ecf20Sopenharmony_ci#define ABIT_UGURU3_WAIT_TIMEOUT		250
598c2ecf20Sopenharmony_ci/*
608c2ecf20Sopenharmony_ci * Normally the 0xAC at the end of synchronize() is reported after the
618c2ecf20Sopenharmony_ci * first read, but sometimes not and we need to poll
628c2ecf20Sopenharmony_ci */
638c2ecf20Sopenharmony_ci#define ABIT_UGURU3_SYNCHRONIZE_TIMEOUT		5
648c2ecf20Sopenharmony_ci/* utility macros */
658c2ecf20Sopenharmony_ci#define ABIT_UGURU3_NAME			"abituguru3"
668c2ecf20Sopenharmony_ci#define ABIT_UGURU3_DEBUG(format, arg...)		\
678c2ecf20Sopenharmony_ci	do {						\
688c2ecf20Sopenharmony_ci		if (verbose)				\
698c2ecf20Sopenharmony_ci			pr_debug(format , ## arg);	\
708c2ecf20Sopenharmony_ci	} while (0)
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci/* Macros to help calculate the sysfs_names array length */
738c2ecf20Sopenharmony_ci#define ABIT_UGURU3_MAX_NO_SENSORS 26
748c2ecf20Sopenharmony_ci/*
758c2ecf20Sopenharmony_ci * sum of strlen +1 of: in??_input\0, in??_{min,max}\0, in??_{min,max}_alarm\0,
768c2ecf20Sopenharmony_ci * in??_{min,max}_alarm_enable\0, in??_beep\0, in??_shutdown\0, in??_label\0
778c2ecf20Sopenharmony_ci */
788c2ecf20Sopenharmony_ci#define ABIT_UGURU3_IN_NAMES_LENGTH \
798c2ecf20Sopenharmony_ci				(11 + 2 * 9 + 2 * 15 + 2 * 22 + 10 + 14 + 11)
808c2ecf20Sopenharmony_ci/*
818c2ecf20Sopenharmony_ci * sum of strlen +1 of: temp??_input\0, temp??_max\0, temp??_crit\0,
828c2ecf20Sopenharmony_ci * temp??_alarm\0, temp??_alarm_enable\0, temp??_beep\0, temp??_shutdown\0,
838c2ecf20Sopenharmony_ci * temp??_label\0
848c2ecf20Sopenharmony_ci */
858c2ecf20Sopenharmony_ci#define ABIT_UGURU3_TEMP_NAMES_LENGTH (13 + 11 + 12 + 13 + 20 + 12 + 16 + 13)
868c2ecf20Sopenharmony_ci/*
878c2ecf20Sopenharmony_ci * sum of strlen +1 of: fan??_input\0, fan??_min\0, fan??_alarm\0,
888c2ecf20Sopenharmony_ci * fan??_alarm_enable\0, fan??_beep\0, fan??_shutdown\0, fan??_label\0
898c2ecf20Sopenharmony_ci */
908c2ecf20Sopenharmony_ci#define ABIT_UGURU3_FAN_NAMES_LENGTH (12 + 10 + 12 + 19 + 11 + 15 + 12)
918c2ecf20Sopenharmony_ci/*
928c2ecf20Sopenharmony_ci * Worst case scenario 16 in sensors (longest names_length) and the rest
938c2ecf20Sopenharmony_ci * temp sensors (second longest names_length).
948c2ecf20Sopenharmony_ci */
958c2ecf20Sopenharmony_ci#define ABIT_UGURU3_SYSFS_NAMES_LENGTH (16 * ABIT_UGURU3_IN_NAMES_LENGTH + \
968c2ecf20Sopenharmony_ci	(ABIT_UGURU3_MAX_NO_SENSORS - 16) * ABIT_UGURU3_TEMP_NAMES_LENGTH)
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci/*
998c2ecf20Sopenharmony_ci * All the macros below are named identical to the openguru2 program
1008c2ecf20Sopenharmony_ci * reverse engineered by Louis Kruger, hence the names might not be 100%
1018c2ecf20Sopenharmony_ci * logical. I could come up with better names, but I prefer keeping the names
1028c2ecf20Sopenharmony_ci * identical so that this driver can be compared with his work more easily.
1038c2ecf20Sopenharmony_ci */
1048c2ecf20Sopenharmony_ci/* Two i/o-ports are used by uGuru */
1058c2ecf20Sopenharmony_ci#define ABIT_UGURU3_BASE			0x00E0
1068c2ecf20Sopenharmony_ci#define ABIT_UGURU3_CMD				0x00
1078c2ecf20Sopenharmony_ci#define ABIT_UGURU3_DATA			0x04
1088c2ecf20Sopenharmony_ci#define ABIT_UGURU3_REGION_LENGTH		5
1098c2ecf20Sopenharmony_ci/*
1108c2ecf20Sopenharmony_ci * The wait_xxx functions return this on success and the last contents
1118c2ecf20Sopenharmony_ci * of the DATA register (0-255) on failure.
1128c2ecf20Sopenharmony_ci */
1138c2ecf20Sopenharmony_ci#define ABIT_UGURU3_SUCCESS			-1
1148c2ecf20Sopenharmony_ci/* uGuru status flags */
1158c2ecf20Sopenharmony_ci#define ABIT_UGURU3_STATUS_READY_FOR_READ	0x01
1168c2ecf20Sopenharmony_ci#define ABIT_UGURU3_STATUS_BUSY			0x02
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci/* Structures */
1208c2ecf20Sopenharmony_cistruct abituguru3_sensor_info {
1218c2ecf20Sopenharmony_ci	const char *name;
1228c2ecf20Sopenharmony_ci	int port;
1238c2ecf20Sopenharmony_ci	int type;
1248c2ecf20Sopenharmony_ci	int multiplier;
1258c2ecf20Sopenharmony_ci	int divisor;
1268c2ecf20Sopenharmony_ci	int offset;
1278c2ecf20Sopenharmony_ci};
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci/* Avoid use of flexible array members */
1308c2ecf20Sopenharmony_ci#define ABIT_UGURU3_MAX_DMI_NAMES 2
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_cistruct abituguru3_motherboard_info {
1338c2ecf20Sopenharmony_ci	u16 id;
1348c2ecf20Sopenharmony_ci	const char *dmi_name[ABIT_UGURU3_MAX_DMI_NAMES + 1];
1358c2ecf20Sopenharmony_ci	/* + 1 -> end of sensors indicated by a sensor with name == NULL */
1368c2ecf20Sopenharmony_ci	struct abituguru3_sensor_info sensors[ABIT_UGURU3_MAX_NO_SENSORS + 1];
1378c2ecf20Sopenharmony_ci};
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci/*
1408c2ecf20Sopenharmony_ci * For the Abit uGuru, we need to keep some data in memory.
1418c2ecf20Sopenharmony_ci * The structure is dynamically allocated, at the same time when a new
1428c2ecf20Sopenharmony_ci * abituguru3 device is allocated.
1438c2ecf20Sopenharmony_ci */
1448c2ecf20Sopenharmony_cistruct abituguru3_data {
1458c2ecf20Sopenharmony_ci	struct device *hwmon_dev;	/* hwmon registered device */
1468c2ecf20Sopenharmony_ci	struct mutex update_lock;	/* protect access to data and uGuru */
1478c2ecf20Sopenharmony_ci	unsigned short addr;		/* uguru base address */
1488c2ecf20Sopenharmony_ci	char valid;			/* !=0 if following fields are valid */
1498c2ecf20Sopenharmony_ci	unsigned long last_updated;	/* In jiffies */
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	/*
1528c2ecf20Sopenharmony_ci	 * For convenience the sysfs attr and their names are generated
1538c2ecf20Sopenharmony_ci	 * automatically. We have max 10 entries per sensor (for in sensors)
1548c2ecf20Sopenharmony_ci	 */
1558c2ecf20Sopenharmony_ci	struct sensor_device_attribute_2 sysfs_attr[ABIT_UGURU3_MAX_NO_SENSORS
1568c2ecf20Sopenharmony_ci		* 10];
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	/* Buffer to store the dynamically generated sysfs names */
1598c2ecf20Sopenharmony_ci	char sysfs_names[ABIT_UGURU3_SYSFS_NAMES_LENGTH];
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	/* Pointer to the sensors info for the detected motherboard */
1628c2ecf20Sopenharmony_ci	const struct abituguru3_sensor_info *sensors;
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	/*
1658c2ecf20Sopenharmony_ci	 * The abituguru3 supports up to 48 sensors, and thus has registers
1668c2ecf20Sopenharmony_ci	 * sets for 48 sensors, for convenience reasons / simplicity of the
1678c2ecf20Sopenharmony_ci	 * code we always read and store all registers for all 48 sensors
1688c2ecf20Sopenharmony_ci	 */
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci	/* Alarms for all 48 sensors (1 bit per sensor) */
1718c2ecf20Sopenharmony_ci	u8 alarms[48/8];
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci	/* Value of all 48 sensors */
1748c2ecf20Sopenharmony_ci	u8 value[48];
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	/*
1778c2ecf20Sopenharmony_ci	 * Settings of all 48 sensors, note in and temp sensors (the first 32
1788c2ecf20Sopenharmony_ci	 * sensors) have 3 bytes of settings, while fans only have 2 bytes,
1798c2ecf20Sopenharmony_ci	 * for convenience we use 3 bytes for all sensors
1808c2ecf20Sopenharmony_ci	 */
1818c2ecf20Sopenharmony_ci	u8 settings[48][3];
1828c2ecf20Sopenharmony_ci};
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci/* Constants */
1868c2ecf20Sopenharmony_cistatic const struct abituguru3_motherboard_info abituguru3_motherboards[] = {
1878c2ecf20Sopenharmony_ci	{ 0x000C, { NULL } /* Unknown, need DMI string */, {
1888c2ecf20Sopenharmony_ci		{ "CPU Core",		 0, 0, 10, 1, 0 },
1898c2ecf20Sopenharmony_ci		{ "DDR",		 1, 0, 10, 1, 0 },
1908c2ecf20Sopenharmony_ci		{ "DDR VTT",		 2, 0, 10, 1, 0 },
1918c2ecf20Sopenharmony_ci		{ "CPU VTT 1.2V",	 3, 0, 10, 1, 0 },
1928c2ecf20Sopenharmony_ci		{ "MCH & PCIE 1.5V",	 4, 0, 10, 1, 0 },
1938c2ecf20Sopenharmony_ci		{ "MCH 2.5V",		 5, 0, 20, 1, 0 },
1948c2ecf20Sopenharmony_ci		{ "ICH 1.05V",		 6, 0, 10, 1, 0 },
1958c2ecf20Sopenharmony_ci		{ "ATX +12V (24-Pin)",	 7, 0, 60, 1, 0 },
1968c2ecf20Sopenharmony_ci		{ "ATX +12V (4-pin)",	 8, 0, 60, 1, 0 },
1978c2ecf20Sopenharmony_ci		{ "ATX +5V",		 9, 0, 30, 1, 0 },
1988c2ecf20Sopenharmony_ci		{ "+3.3V",		10, 0, 20, 1, 0 },
1998c2ecf20Sopenharmony_ci		{ "5VSB",		11, 0, 30, 1, 0 },
2008c2ecf20Sopenharmony_ci		{ "CPU",		24, 1, 1, 1, 0 },
2018c2ecf20Sopenharmony_ci		{ "System",		25, 1, 1, 1, 0 },
2028c2ecf20Sopenharmony_ci		{ "PWM",		26, 1, 1, 1, 0 },
2038c2ecf20Sopenharmony_ci		{ "CPU Fan",		32, 2, 60, 1, 0 },
2048c2ecf20Sopenharmony_ci		{ "NB Fan",		33, 2, 60, 1, 0 },
2058c2ecf20Sopenharmony_ci		{ "SYS FAN",		34, 2, 60, 1, 0 },
2068c2ecf20Sopenharmony_ci		{ "AUX1 Fan",		35, 2, 60, 1, 0 },
2078c2ecf20Sopenharmony_ci		{ NULL, 0, 0, 0, 0, 0 } }
2088c2ecf20Sopenharmony_ci	},
2098c2ecf20Sopenharmony_ci	{ 0x000D, { NULL } /* Abit AW8, need DMI string */, {
2108c2ecf20Sopenharmony_ci		{ "CPU Core",		 0, 0, 10, 1, 0 },
2118c2ecf20Sopenharmony_ci		{ "DDR",		 1, 0, 10, 1, 0 },
2128c2ecf20Sopenharmony_ci		{ "DDR VTT",		 2, 0, 10, 1, 0 },
2138c2ecf20Sopenharmony_ci		{ "CPU VTT 1.2V",	 3, 0, 10, 1, 0 },
2148c2ecf20Sopenharmony_ci		{ "MCH & PCIE 1.5V",	 4, 0, 10, 1, 0 },
2158c2ecf20Sopenharmony_ci		{ "MCH 2.5V",		 5, 0, 20, 1, 0 },
2168c2ecf20Sopenharmony_ci		{ "ICH 1.05V",		 6, 0, 10, 1, 0 },
2178c2ecf20Sopenharmony_ci		{ "ATX +12V (24-Pin)",	 7, 0, 60, 1, 0 },
2188c2ecf20Sopenharmony_ci		{ "ATX +12V (4-pin)",	 8, 0, 60, 1, 0 },
2198c2ecf20Sopenharmony_ci		{ "ATX +5V",		 9, 0, 30, 1, 0 },
2208c2ecf20Sopenharmony_ci		{ "+3.3V",		10, 0, 20, 1, 0 },
2218c2ecf20Sopenharmony_ci		{ "5VSB",		11, 0, 30, 1, 0 },
2228c2ecf20Sopenharmony_ci		{ "CPU",		24, 1, 1, 1, 0 },
2238c2ecf20Sopenharmony_ci		{ "System",		25, 1, 1, 1, 0 },
2248c2ecf20Sopenharmony_ci		{ "PWM1",		26, 1, 1, 1, 0 },
2258c2ecf20Sopenharmony_ci		{ "PWM2",		27, 1, 1, 1, 0 },
2268c2ecf20Sopenharmony_ci		{ "PWM3",		28, 1, 1, 1, 0 },
2278c2ecf20Sopenharmony_ci		{ "PWM4",		29, 1, 1, 1, 0 },
2288c2ecf20Sopenharmony_ci		{ "CPU Fan",		32, 2, 60, 1, 0 },
2298c2ecf20Sopenharmony_ci		{ "NB Fan",		33, 2, 60, 1, 0 },
2308c2ecf20Sopenharmony_ci		{ "SYS Fan",		34, 2, 60, 1, 0 },
2318c2ecf20Sopenharmony_ci		{ "AUX1 Fan",		35, 2, 60, 1, 0 },
2328c2ecf20Sopenharmony_ci		{ "AUX2 Fan",		36, 2, 60, 1, 0 },
2338c2ecf20Sopenharmony_ci		{ "AUX3 Fan",		37, 2, 60, 1, 0 },
2348c2ecf20Sopenharmony_ci		{ "AUX4 Fan",		38, 2, 60, 1, 0 },
2358c2ecf20Sopenharmony_ci		{ "AUX5 Fan",		39, 2, 60, 1, 0 },
2368c2ecf20Sopenharmony_ci		{ NULL, 0, 0, 0, 0, 0 } }
2378c2ecf20Sopenharmony_ci	},
2388c2ecf20Sopenharmony_ci	{ 0x000E, { NULL } /* AL-8, need DMI string */, {
2398c2ecf20Sopenharmony_ci		{ "CPU Core",		 0, 0, 10, 1, 0 },
2408c2ecf20Sopenharmony_ci		{ "DDR",		 1, 0, 10, 1, 0 },
2418c2ecf20Sopenharmony_ci		{ "DDR VTT",		 2, 0, 10, 1, 0 },
2428c2ecf20Sopenharmony_ci		{ "CPU VTT 1.2V",	 3, 0, 10, 1, 0 },
2438c2ecf20Sopenharmony_ci		{ "MCH & PCIE 1.5V",	 4, 0, 10, 1, 0 },
2448c2ecf20Sopenharmony_ci		{ "MCH 2.5V",		 5, 0, 20, 1, 0 },
2458c2ecf20Sopenharmony_ci		{ "ICH 1.05V",		 6, 0, 10, 1, 0 },
2468c2ecf20Sopenharmony_ci		{ "ATX +12V (24-Pin)",	 7, 0, 60, 1, 0 },
2478c2ecf20Sopenharmony_ci		{ "ATX +12V (4-pin)",	 8, 0, 60, 1, 0 },
2488c2ecf20Sopenharmony_ci		{ "ATX +5V",		 9, 0, 30, 1, 0 },
2498c2ecf20Sopenharmony_ci		{ "+3.3V",		10, 0, 20, 1, 0 },
2508c2ecf20Sopenharmony_ci		{ "5VSB",		11, 0, 30, 1, 0 },
2518c2ecf20Sopenharmony_ci		{ "CPU",		24, 1, 1, 1, 0 },
2528c2ecf20Sopenharmony_ci		{ "System",		25, 1, 1, 1, 0 },
2538c2ecf20Sopenharmony_ci		{ "PWM",		26, 1, 1, 1, 0 },
2548c2ecf20Sopenharmony_ci		{ "CPU Fan",		32, 2, 60, 1, 0 },
2558c2ecf20Sopenharmony_ci		{ "NB Fan",		33, 2, 60, 1, 0 },
2568c2ecf20Sopenharmony_ci		{ "SYS Fan",		34, 2, 60, 1, 0 },
2578c2ecf20Sopenharmony_ci		{ NULL, 0, 0, 0, 0, 0 } }
2588c2ecf20Sopenharmony_ci	},
2598c2ecf20Sopenharmony_ci	{ 0x000F, { NULL } /* Unknown, need DMI string */, {
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci		{ "CPU Core",		 0, 0, 10, 1, 0 },
2628c2ecf20Sopenharmony_ci		{ "DDR",		 1, 0, 10, 1, 0 },
2638c2ecf20Sopenharmony_ci		{ "DDR VTT",		 2, 0, 10, 1, 0 },
2648c2ecf20Sopenharmony_ci		{ "CPU VTT 1.2V",	 3, 0, 10, 1, 0 },
2658c2ecf20Sopenharmony_ci		{ "MCH & PCIE 1.5V",	 4, 0, 10, 1, 0 },
2668c2ecf20Sopenharmony_ci		{ "MCH 2.5V",		 5, 0, 20, 1, 0 },
2678c2ecf20Sopenharmony_ci		{ "ICH 1.05V",		 6, 0, 10, 1, 0 },
2688c2ecf20Sopenharmony_ci		{ "ATX +12V (24-Pin)",	 7, 0, 60, 1, 0 },
2698c2ecf20Sopenharmony_ci		{ "ATX +12V (4-pin)",	 8, 0, 60, 1, 0 },
2708c2ecf20Sopenharmony_ci		{ "ATX +5V",		 9, 0, 30, 1, 0 },
2718c2ecf20Sopenharmony_ci		{ "+3.3V",		10, 0, 20, 1, 0 },
2728c2ecf20Sopenharmony_ci		{ "5VSB",		11, 0, 30, 1, 0 },
2738c2ecf20Sopenharmony_ci		{ "CPU",		24, 1, 1, 1, 0 },
2748c2ecf20Sopenharmony_ci		{ "System",		25, 1, 1, 1, 0 },
2758c2ecf20Sopenharmony_ci		{ "PWM",		26, 1, 1, 1, 0 },
2768c2ecf20Sopenharmony_ci		{ "CPU Fan",		32, 2, 60, 1, 0 },
2778c2ecf20Sopenharmony_ci		{ "NB Fan",		33, 2, 60, 1, 0 },
2788c2ecf20Sopenharmony_ci		{ "SYS Fan",		34, 2, 60, 1, 0 },
2798c2ecf20Sopenharmony_ci		{ NULL, 0, 0, 0, 0, 0 } }
2808c2ecf20Sopenharmony_ci	},
2818c2ecf20Sopenharmony_ci	{ 0x0010, { NULL } /* Abit NI8 SLI GR, need DMI string */, {
2828c2ecf20Sopenharmony_ci		{ "CPU Core",		 0, 0, 10, 1, 0 },
2838c2ecf20Sopenharmony_ci		{ "DDR",		 1, 0, 10, 1, 0 },
2848c2ecf20Sopenharmony_ci		{ "DDR VTT",		 2, 0, 10, 1, 0 },
2858c2ecf20Sopenharmony_ci		{ "CPU VTT 1.2V",	 3, 0, 10, 1, 0 },
2868c2ecf20Sopenharmony_ci		{ "NB 1.4V",		 4, 0, 10, 1, 0 },
2878c2ecf20Sopenharmony_ci		{ "SB 1.5V",		 6, 0, 10, 1, 0 },
2888c2ecf20Sopenharmony_ci		{ "ATX +12V (24-Pin)",	 7, 0, 60, 1, 0 },
2898c2ecf20Sopenharmony_ci		{ "ATX +12V (4-pin)",	 8, 0, 60, 1, 0 },
2908c2ecf20Sopenharmony_ci		{ "ATX +5V",		 9, 0, 30, 1, 0 },
2918c2ecf20Sopenharmony_ci		{ "+3.3V",		10, 0, 20, 1, 0 },
2928c2ecf20Sopenharmony_ci		{ "5VSB",		11, 0, 30, 1, 0 },
2938c2ecf20Sopenharmony_ci		{ "CPU",		24, 1, 1, 1, 0 },
2948c2ecf20Sopenharmony_ci		{ "SYS",		25, 1, 1, 1, 0 },
2958c2ecf20Sopenharmony_ci		{ "PWM",		26, 1, 1, 1, 0 },
2968c2ecf20Sopenharmony_ci		{ "CPU Fan",		32, 2, 60, 1, 0 },
2978c2ecf20Sopenharmony_ci		{ "NB Fan",		33, 2, 60, 1, 0 },
2988c2ecf20Sopenharmony_ci		{ "SYS Fan",		34, 2, 60, 1, 0 },
2998c2ecf20Sopenharmony_ci		{ "AUX1 Fan",		35, 2, 60, 1, 0 },
3008c2ecf20Sopenharmony_ci		{ "OTES1 Fan",		36, 2, 60, 1, 0 },
3018c2ecf20Sopenharmony_ci		{ NULL, 0, 0, 0, 0, 0 } }
3028c2ecf20Sopenharmony_ci	},
3038c2ecf20Sopenharmony_ci	{ 0x0011, { "AT8 32X", NULL }, {
3048c2ecf20Sopenharmony_ci		{ "CPU Core",		 0, 0, 10, 1, 0 },
3058c2ecf20Sopenharmony_ci		{ "DDR",		 1, 0, 20, 1, 0 },
3068c2ecf20Sopenharmony_ci		{ "DDR VTT",		 2, 0, 10, 1, 0 },
3078c2ecf20Sopenharmony_ci		{ "CPU VDDA 2.5V",	 6, 0, 20, 1, 0 },
3088c2ecf20Sopenharmony_ci		{ "NB 1.8V",		 4, 0, 10, 1, 0 },
3098c2ecf20Sopenharmony_ci		{ "NB 1.8V Dual",	 5, 0, 10, 1, 0 },
3108c2ecf20Sopenharmony_ci		{ "HTV 1.2",		 3, 0, 10, 1, 0 },
3118c2ecf20Sopenharmony_ci		{ "PCIE 1.2V",		12, 0, 10, 1, 0 },
3128c2ecf20Sopenharmony_ci		{ "NB 1.2V",		13, 0, 10, 1, 0 },
3138c2ecf20Sopenharmony_ci		{ "ATX +12V (24-Pin)",	 7, 0, 60, 1, 0 },
3148c2ecf20Sopenharmony_ci		{ "ATX +12V (4-pin)",	 8, 0, 60, 1, 0 },
3158c2ecf20Sopenharmony_ci		{ "ATX +5V",		 9, 0, 30, 1, 0 },
3168c2ecf20Sopenharmony_ci		{ "+3.3V",		10, 0, 20, 1, 0 },
3178c2ecf20Sopenharmony_ci		{ "5VSB",		11, 0, 30, 1, 0 },
3188c2ecf20Sopenharmony_ci		{ "CPU",		24, 1, 1, 1, 0 },
3198c2ecf20Sopenharmony_ci		{ "NB",			25, 1, 1, 1, 0 },
3208c2ecf20Sopenharmony_ci		{ "System",		26, 1, 1, 1, 0 },
3218c2ecf20Sopenharmony_ci		{ "PWM",		27, 1, 1, 1, 0 },
3228c2ecf20Sopenharmony_ci		{ "CPU Fan",		32, 2, 60, 1, 0 },
3238c2ecf20Sopenharmony_ci		{ "NB Fan",		33, 2, 60, 1, 0 },
3248c2ecf20Sopenharmony_ci		{ "SYS Fan",		34, 2, 60, 1, 0 },
3258c2ecf20Sopenharmony_ci		{ "AUX1 Fan",		35, 2, 60, 1, 0 },
3268c2ecf20Sopenharmony_ci		{ "AUX2 Fan",		36, 2, 60, 1, 0 },
3278c2ecf20Sopenharmony_ci		{ "AUX3 Fan",		37, 2, 60, 1, 0 },
3288c2ecf20Sopenharmony_ci		{ NULL, 0, 0, 0, 0, 0 } }
3298c2ecf20Sopenharmony_ci	},
3308c2ecf20Sopenharmony_ci	{ 0x0012, { NULL } /* Abit AN8 32X, need DMI string */, {
3318c2ecf20Sopenharmony_ci		{ "CPU Core",		 0, 0, 10, 1, 0 },
3328c2ecf20Sopenharmony_ci		{ "DDR",		 1, 0, 20, 1, 0 },
3338c2ecf20Sopenharmony_ci		{ "DDR VTT",		 2, 0, 10, 1, 0 },
3348c2ecf20Sopenharmony_ci		{ "HyperTransport",	 3, 0, 10, 1, 0 },
3358c2ecf20Sopenharmony_ci		{ "CPU VDDA 2.5V",	 5, 0, 20, 1, 0 },
3368c2ecf20Sopenharmony_ci		{ "NB",			 4, 0, 10, 1, 0 },
3378c2ecf20Sopenharmony_ci		{ "SB",			 6, 0, 10, 1, 0 },
3388c2ecf20Sopenharmony_ci		{ "ATX +12V (24-Pin)",	 7, 0, 60, 1, 0 },
3398c2ecf20Sopenharmony_ci		{ "ATX +12V (4-pin)",	 8, 0, 60, 1, 0 },
3408c2ecf20Sopenharmony_ci		{ "ATX +5V",		 9, 0, 30, 1, 0 },
3418c2ecf20Sopenharmony_ci		{ "+3.3V",		10, 0, 20, 1, 0 },
3428c2ecf20Sopenharmony_ci		{ "5VSB",		11, 0, 30, 1, 0 },
3438c2ecf20Sopenharmony_ci		{ "CPU",		24, 1, 1, 1, 0 },
3448c2ecf20Sopenharmony_ci		{ "SYS",		25, 1, 1, 1, 0 },
3458c2ecf20Sopenharmony_ci		{ "PWM",		26, 1, 1, 1, 0 },
3468c2ecf20Sopenharmony_ci		{ "CPU Fan",		32, 2, 60, 1, 0 },
3478c2ecf20Sopenharmony_ci		{ "NB Fan",		33, 2, 60, 1, 0 },
3488c2ecf20Sopenharmony_ci		{ "SYS Fan",		34, 2, 60, 1, 0 },
3498c2ecf20Sopenharmony_ci		{ "AUX1 Fan",		36, 2, 60, 1, 0 },
3508c2ecf20Sopenharmony_ci		{ NULL, 0, 0, 0, 0, 0 } }
3518c2ecf20Sopenharmony_ci	},
3528c2ecf20Sopenharmony_ci	{ 0x0013, { NULL } /* Abit AW8D, need DMI string */, {
3538c2ecf20Sopenharmony_ci		{ "CPU Core",		 0, 0, 10, 1, 0 },
3548c2ecf20Sopenharmony_ci		{ "DDR",		 1, 0, 10, 1, 0 },
3558c2ecf20Sopenharmony_ci		{ "DDR VTT",		 2, 0, 10, 1, 0 },
3568c2ecf20Sopenharmony_ci		{ "CPU VTT 1.2V",	 3, 0, 10, 1, 0 },
3578c2ecf20Sopenharmony_ci		{ "MCH & PCIE 1.5V",	 4, 0, 10, 1, 0 },
3588c2ecf20Sopenharmony_ci		{ "MCH 2.5V",		 5, 0, 20, 1, 0 },
3598c2ecf20Sopenharmony_ci		{ "ICH 1.05V",		 6, 0, 10, 1, 0 },
3608c2ecf20Sopenharmony_ci		{ "ATX +12V (24-Pin)",	 7, 0, 60, 1, 0 },
3618c2ecf20Sopenharmony_ci		{ "ATX +12V (4-pin)",	 8, 0, 60, 1, 0 },
3628c2ecf20Sopenharmony_ci		{ "ATX +5V",		 9, 0, 30, 1, 0 },
3638c2ecf20Sopenharmony_ci		{ "+3.3V",		10, 0, 20, 1, 0 },
3648c2ecf20Sopenharmony_ci		{ "5VSB",		11, 0, 30, 1, 0 },
3658c2ecf20Sopenharmony_ci		{ "CPU",		24, 1, 1, 1, 0 },
3668c2ecf20Sopenharmony_ci		{ "System",		25, 1, 1, 1, 0 },
3678c2ecf20Sopenharmony_ci		{ "PWM1",		26, 1, 1, 1, 0 },
3688c2ecf20Sopenharmony_ci		{ "PWM2",		27, 1, 1, 1, 0 },
3698c2ecf20Sopenharmony_ci		{ "PWM3",		28, 1, 1, 1, 0 },
3708c2ecf20Sopenharmony_ci		{ "PWM4",		29, 1, 1, 1, 0 },
3718c2ecf20Sopenharmony_ci		{ "CPU Fan",		32, 2, 60, 1, 0 },
3728c2ecf20Sopenharmony_ci		{ "NB Fan",		33, 2, 60, 1, 0 },
3738c2ecf20Sopenharmony_ci		{ "SYS Fan",		34, 2, 60, 1, 0 },
3748c2ecf20Sopenharmony_ci		{ "AUX1 Fan",		35, 2, 60, 1, 0 },
3758c2ecf20Sopenharmony_ci		{ "AUX2 Fan",		36, 2, 60, 1, 0 },
3768c2ecf20Sopenharmony_ci		{ "AUX3 Fan",		37, 2, 60, 1, 0 },
3778c2ecf20Sopenharmony_ci		{ "AUX4 Fan",		38, 2, 60, 1, 0 },
3788c2ecf20Sopenharmony_ci		{ "AUX5 Fan",		39, 2, 60, 1, 0 },
3798c2ecf20Sopenharmony_ci		{ NULL, 0, 0, 0, 0, 0 } }
3808c2ecf20Sopenharmony_ci	},
3818c2ecf20Sopenharmony_ci	{ 0x0014, { "AB9", "AB9 Pro", NULL }, {
3828c2ecf20Sopenharmony_ci		{ "CPU Core",		 0, 0, 10, 1, 0 },
3838c2ecf20Sopenharmony_ci		{ "DDR",		 1, 0, 10, 1, 0 },
3848c2ecf20Sopenharmony_ci		{ "DDR VTT",		 2, 0, 10, 1, 0 },
3858c2ecf20Sopenharmony_ci		{ "CPU VTT 1.2V",	 3, 0, 10, 1, 0 },
3868c2ecf20Sopenharmony_ci		{ "MCH & PCIE 1.5V",	 4, 0, 10, 1, 0 },
3878c2ecf20Sopenharmony_ci		{ "MCH 2.5V",		 5, 0, 20, 1, 0 },
3888c2ecf20Sopenharmony_ci		{ "ICH 1.05V",		 6, 0, 10, 1, 0 },
3898c2ecf20Sopenharmony_ci		{ "ATX +12V (24-Pin)",	 7, 0, 60, 1, 0 },
3908c2ecf20Sopenharmony_ci		{ "ATX +12V (4-pin)",	 8, 0, 60, 1, 0 },
3918c2ecf20Sopenharmony_ci		{ "ATX +5V",		 9, 0, 30, 1, 0 },
3928c2ecf20Sopenharmony_ci		{ "+3.3V",		10, 0, 20, 1, 0 },
3938c2ecf20Sopenharmony_ci		{ "5VSB",		11, 0, 30, 1, 0 },
3948c2ecf20Sopenharmony_ci		{ "CPU",		24, 1, 1, 1, 0 },
3958c2ecf20Sopenharmony_ci		{ "System",		25, 1, 1, 1, 0 },
3968c2ecf20Sopenharmony_ci		{ "PWM",		26, 1, 1, 1, 0 },
3978c2ecf20Sopenharmony_ci		{ "CPU Fan",		32, 2, 60, 1, 0 },
3988c2ecf20Sopenharmony_ci		{ "NB Fan",		33, 2, 60, 1, 0 },
3998c2ecf20Sopenharmony_ci		{ "SYS Fan",		34, 2, 60, 1, 0 },
4008c2ecf20Sopenharmony_ci		{ NULL, 0, 0, 0, 0, 0 } }
4018c2ecf20Sopenharmony_ci	},
4028c2ecf20Sopenharmony_ci	{ 0x0015, { NULL } /* Unknown, need DMI string */, {
4038c2ecf20Sopenharmony_ci		{ "CPU Core",		 0, 0, 10, 1, 0 },
4048c2ecf20Sopenharmony_ci		{ "DDR",		 1, 0, 20, 1, 0 },
4058c2ecf20Sopenharmony_ci		{ "DDR VTT",		 2, 0, 10, 1, 0 },
4068c2ecf20Sopenharmony_ci		{ "HyperTransport",	 3, 0, 10, 1, 0 },
4078c2ecf20Sopenharmony_ci		{ "CPU VDDA 2.5V",	 5, 0, 20, 1, 0 },
4088c2ecf20Sopenharmony_ci		{ "NB",			 4, 0, 10, 1, 0 },
4098c2ecf20Sopenharmony_ci		{ "SB",			 6, 0, 10, 1, 0 },
4108c2ecf20Sopenharmony_ci		{ "ATX +12V (24-Pin)",	 7, 0, 60, 1, 0 },
4118c2ecf20Sopenharmony_ci		{ "ATX +12V (4-pin)",	 8, 0, 60, 1, 0 },
4128c2ecf20Sopenharmony_ci		{ "ATX +5V",		 9, 0, 30, 1, 0 },
4138c2ecf20Sopenharmony_ci		{ "+3.3V",		10, 0, 20, 1, 0 },
4148c2ecf20Sopenharmony_ci		{ "5VSB",		11, 0, 30, 1, 0 },
4158c2ecf20Sopenharmony_ci		{ "CPU",		24, 1, 1, 1, 0 },
4168c2ecf20Sopenharmony_ci		{ "SYS",		25, 1, 1, 1, 0 },
4178c2ecf20Sopenharmony_ci		{ "PWM",		26, 1, 1, 1, 0 },
4188c2ecf20Sopenharmony_ci		{ "CPU Fan",		32, 2, 60, 1, 0 },
4198c2ecf20Sopenharmony_ci		{ "NB Fan",		33, 2, 60, 1, 0 },
4208c2ecf20Sopenharmony_ci		{ "SYS Fan",		34, 2, 60, 1, 0 },
4218c2ecf20Sopenharmony_ci		{ "AUX1 Fan",		33, 2, 60, 1, 0 },
4228c2ecf20Sopenharmony_ci		{ "AUX2 Fan",		35, 2, 60, 1, 0 },
4238c2ecf20Sopenharmony_ci		{ "AUX3 Fan",		36, 2, 60, 1, 0 },
4248c2ecf20Sopenharmony_ci		{ NULL, 0, 0, 0, 0, 0 } }
4258c2ecf20Sopenharmony_ci	},
4268c2ecf20Sopenharmony_ci	{ 0x0016, { "AW9D-MAX", NULL }, {
4278c2ecf20Sopenharmony_ci		{ "CPU Core",		 0, 0, 10, 1, 0 },
4288c2ecf20Sopenharmony_ci		{ "DDR2",		 1, 0, 20, 1, 0 },
4298c2ecf20Sopenharmony_ci		{ "DDR2 VTT",		 2, 0, 10, 1, 0 },
4308c2ecf20Sopenharmony_ci		{ "CPU VTT 1.2V",	 3, 0, 10, 1, 0 },
4318c2ecf20Sopenharmony_ci		{ "MCH & PCIE 1.5V",	 4, 0, 10, 1, 0 },
4328c2ecf20Sopenharmony_ci		{ "MCH 2.5V",		 5, 0, 20, 1, 0 },
4338c2ecf20Sopenharmony_ci		{ "ICH 1.05V",		 6, 0, 10, 1, 0 },
4348c2ecf20Sopenharmony_ci		{ "ATX +12V (24-Pin)",	 7, 0, 60, 1, 0 },
4358c2ecf20Sopenharmony_ci		{ "ATX +12V (4-pin)",	 8, 0, 60, 1, 0 },
4368c2ecf20Sopenharmony_ci		{ "ATX +5V",		 9, 0, 30, 1, 0 },
4378c2ecf20Sopenharmony_ci		{ "+3.3V",		10, 0, 20, 1, 0 },
4388c2ecf20Sopenharmony_ci		{ "5VSB",		11, 0, 30, 1, 0 },
4398c2ecf20Sopenharmony_ci		{ "CPU",		24, 1, 1, 1, 0 },
4408c2ecf20Sopenharmony_ci		{ "System",		25, 1, 1, 1, 0 },
4418c2ecf20Sopenharmony_ci		{ "PWM1",		26, 1, 1, 1, 0 },
4428c2ecf20Sopenharmony_ci		{ "PWM2",		27, 1, 1, 1, 0 },
4438c2ecf20Sopenharmony_ci		{ "PWM3",		28, 1, 1, 1, 0 },
4448c2ecf20Sopenharmony_ci		{ "PWM4",		29, 1, 1, 1, 0 },
4458c2ecf20Sopenharmony_ci		{ "CPU Fan",		32, 2, 60, 1, 0 },
4468c2ecf20Sopenharmony_ci		{ "NB Fan",		33, 2, 60, 1, 0 },
4478c2ecf20Sopenharmony_ci		{ "SYS Fan",		34, 2, 60, 1, 0 },
4488c2ecf20Sopenharmony_ci		{ "AUX1 Fan",		35, 2, 60, 1, 0 },
4498c2ecf20Sopenharmony_ci		{ "AUX2 Fan",		36, 2, 60, 1, 0 },
4508c2ecf20Sopenharmony_ci		{ "AUX3 Fan",		37, 2, 60, 1, 0 },
4518c2ecf20Sopenharmony_ci		{ "OTES1 Fan",		38, 2, 60, 1, 0 },
4528c2ecf20Sopenharmony_ci		{ NULL, 0, 0, 0, 0, 0 } }
4538c2ecf20Sopenharmony_ci	},
4548c2ecf20Sopenharmony_ci	{ 0x0017, { NULL } /* Unknown, need DMI string */, {
4558c2ecf20Sopenharmony_ci		{ "CPU Core",		 0, 0, 10, 1, 0 },
4568c2ecf20Sopenharmony_ci		{ "DDR2",		 1, 0, 20, 1, 0 },
4578c2ecf20Sopenharmony_ci		{ "DDR2 VTT",		 2, 0, 10, 1, 0 },
4588c2ecf20Sopenharmony_ci		{ "HyperTransport",	 3, 0, 10, 1, 0 },
4598c2ecf20Sopenharmony_ci		{ "CPU VDDA 2.5V",	 6, 0, 20, 1, 0 },
4608c2ecf20Sopenharmony_ci		{ "NB 1.8V",		 4, 0, 10, 1, 0 },
4618c2ecf20Sopenharmony_ci		{ "NB 1.2V ",		13, 0, 10, 1, 0 },
4628c2ecf20Sopenharmony_ci		{ "SB 1.2V",		 5, 0, 10, 1, 0 },
4638c2ecf20Sopenharmony_ci		{ "PCIE 1.2V",		12, 0, 10, 1, 0 },
4648c2ecf20Sopenharmony_ci		{ "ATX +12V (24-Pin)",	 7, 0, 60, 1, 0 },
4658c2ecf20Sopenharmony_ci		{ "ATX +12V (4-pin)",	 8, 0, 60, 1, 0 },
4668c2ecf20Sopenharmony_ci		{ "ATX +5V",		 9, 0, 30, 1, 0 },
4678c2ecf20Sopenharmony_ci		{ "ATX +3.3V",		10, 0, 20, 1, 0 },
4688c2ecf20Sopenharmony_ci		{ "ATX 5VSB",		11, 0, 30, 1, 0 },
4698c2ecf20Sopenharmony_ci		{ "CPU",		24, 1, 1, 1, 0 },
4708c2ecf20Sopenharmony_ci		{ "System",		26, 1, 1, 1, 0 },
4718c2ecf20Sopenharmony_ci		{ "PWM",		27, 1, 1, 1, 0 },
4728c2ecf20Sopenharmony_ci		{ "CPU FAN",		32, 2, 60, 1, 0 },
4738c2ecf20Sopenharmony_ci		{ "SYS FAN",		34, 2, 60, 1, 0 },
4748c2ecf20Sopenharmony_ci		{ "AUX1 FAN",		35, 2, 60, 1, 0 },
4758c2ecf20Sopenharmony_ci		{ "AUX2 FAN",		36, 2, 60, 1, 0 },
4768c2ecf20Sopenharmony_ci		{ "AUX3 FAN",		37, 2, 60, 1, 0 },
4778c2ecf20Sopenharmony_ci		{ NULL, 0, 0, 0, 0, 0 } }
4788c2ecf20Sopenharmony_ci	},
4798c2ecf20Sopenharmony_ci	{ 0x0018, { "AB9 QuadGT", NULL }, {
4808c2ecf20Sopenharmony_ci		{ "CPU Core",		 0, 0, 10, 1, 0 },
4818c2ecf20Sopenharmony_ci		{ "DDR2",		 1, 0, 20, 1, 0 },
4828c2ecf20Sopenharmony_ci		{ "DDR2 VTT",		 2, 0, 10, 1, 0 },
4838c2ecf20Sopenharmony_ci		{ "CPU VTT",		 3, 0, 10, 1, 0 },
4848c2ecf20Sopenharmony_ci		{ "MCH 1.25V",		 4, 0, 10, 1, 0 },
4858c2ecf20Sopenharmony_ci		{ "ICHIO 1.5V",		 5, 0, 10, 1, 0 },
4868c2ecf20Sopenharmony_ci		{ "ICH 1.05V",		 6, 0, 10, 1, 0 },
4878c2ecf20Sopenharmony_ci		{ "ATX +12V (24-Pin)",	 7, 0, 60, 1, 0 },
4888c2ecf20Sopenharmony_ci		{ "ATX +12V (4-pin)",	 8, 0, 60, 1, 0 },
4898c2ecf20Sopenharmony_ci		{ "ATX +5V",		 9, 0, 30, 1, 0 },
4908c2ecf20Sopenharmony_ci		{ "+3.3V",		10, 0, 20, 1, 0 },
4918c2ecf20Sopenharmony_ci		{ "5VSB",		11, 0, 30, 1, 0 },
4928c2ecf20Sopenharmony_ci		{ "CPU",		24, 1, 1, 1, 0 },
4938c2ecf20Sopenharmony_ci		{ "System",		25, 1, 1, 1, 0 },
4948c2ecf20Sopenharmony_ci		{ "PWM Phase1",		26, 1, 1, 1, 0 },
4958c2ecf20Sopenharmony_ci		{ "PWM Phase2",		27, 1, 1, 1, 0 },
4968c2ecf20Sopenharmony_ci		{ "PWM Phase3",		28, 1, 1, 1, 0 },
4978c2ecf20Sopenharmony_ci		{ "PWM Phase4",		29, 1, 1, 1, 0 },
4988c2ecf20Sopenharmony_ci		{ "PWM Phase5",		30, 1, 1, 1, 0 },
4998c2ecf20Sopenharmony_ci		{ "CPU Fan",		32, 2, 60, 1, 0 },
5008c2ecf20Sopenharmony_ci		{ "SYS Fan",		34, 2, 60, 1, 0 },
5018c2ecf20Sopenharmony_ci		{ "AUX1 Fan",		33, 2, 60, 1, 0 },
5028c2ecf20Sopenharmony_ci		{ "AUX2 Fan",		35, 2, 60, 1, 0 },
5038c2ecf20Sopenharmony_ci		{ "AUX3 Fan",		36, 2, 60, 1, 0 },
5048c2ecf20Sopenharmony_ci		{ NULL, 0, 0, 0, 0, 0 } }
5058c2ecf20Sopenharmony_ci	},
5068c2ecf20Sopenharmony_ci	{ 0x0019, { "IN9 32X MAX", NULL }, {
5078c2ecf20Sopenharmony_ci		{ "CPU Core",		 7, 0, 10, 1, 0 },
5088c2ecf20Sopenharmony_ci		{ "DDR2",		13, 0, 20, 1, 0 },
5098c2ecf20Sopenharmony_ci		{ "DDR2 VTT",		14, 0, 10, 1, 0 },
5108c2ecf20Sopenharmony_ci		{ "CPU VTT",		 3, 0, 20, 1, 0 },
5118c2ecf20Sopenharmony_ci		{ "NB 1.2V",		 4, 0, 10, 1, 0 },
5128c2ecf20Sopenharmony_ci		{ "SB 1.5V",		 6, 0, 10, 1, 0 },
5138c2ecf20Sopenharmony_ci		{ "HyperTransport",	 5, 0, 10, 1, 0 },
5148c2ecf20Sopenharmony_ci		{ "ATX +12V (24-Pin)",	12, 0, 60, 1, 0 },
5158c2ecf20Sopenharmony_ci		{ "ATX +12V (4-pin)",	 8, 0, 60, 1, 0 },
5168c2ecf20Sopenharmony_ci		{ "ATX +5V",		 9, 0, 30, 1, 0 },
5178c2ecf20Sopenharmony_ci		{ "ATX +3.3V",		10, 0, 20, 1, 0 },
5188c2ecf20Sopenharmony_ci		{ "ATX 5VSB",		11, 0, 30, 1, 0 },
5198c2ecf20Sopenharmony_ci		{ "CPU",		24, 1, 1, 1, 0 },
5208c2ecf20Sopenharmony_ci		{ "System",		25, 1, 1, 1, 0 },
5218c2ecf20Sopenharmony_ci		{ "PWM Phase1",		26, 1, 1, 1, 0 },
5228c2ecf20Sopenharmony_ci		{ "PWM Phase2",		27, 1, 1, 1, 0 },
5238c2ecf20Sopenharmony_ci		{ "PWM Phase3",		28, 1, 1, 1, 0 },
5248c2ecf20Sopenharmony_ci		{ "PWM Phase4",		29, 1, 1, 1, 0 },
5258c2ecf20Sopenharmony_ci		{ "PWM Phase5",		30, 1, 1, 1, 0 },
5268c2ecf20Sopenharmony_ci		{ "CPU FAN",		32, 2, 60, 1, 0 },
5278c2ecf20Sopenharmony_ci		{ "SYS FAN",		34, 2, 60, 1, 0 },
5288c2ecf20Sopenharmony_ci		{ "AUX1 FAN",		33, 2, 60, 1, 0 },
5298c2ecf20Sopenharmony_ci		{ "AUX2 FAN",		35, 2, 60, 1, 0 },
5308c2ecf20Sopenharmony_ci		{ "AUX3 FAN",		36, 2, 60, 1, 0 },
5318c2ecf20Sopenharmony_ci		{ NULL, 0, 0, 0, 0, 0 } }
5328c2ecf20Sopenharmony_ci	},
5338c2ecf20Sopenharmony_ci	{ 0x001A, { "IP35 Pro", "IP35 Pro XE", NULL }, {
5348c2ecf20Sopenharmony_ci		{ "CPU Core",		 0, 0, 10, 1, 0 },
5358c2ecf20Sopenharmony_ci		{ "DDR2",		 1, 0, 20, 1, 0 },
5368c2ecf20Sopenharmony_ci		{ "DDR2 VTT",		 2, 0, 10, 1, 0 },
5378c2ecf20Sopenharmony_ci		{ "CPU VTT 1.2V",	 3, 0, 10, 1, 0 },
5388c2ecf20Sopenharmony_ci		{ "MCH 1.25V",		 4, 0, 10, 1, 0 },
5398c2ecf20Sopenharmony_ci		{ "ICHIO 1.5V",		 5, 0, 10, 1, 0 },
5408c2ecf20Sopenharmony_ci		{ "ICH 1.05V",		 6, 0, 10, 1, 0 },
5418c2ecf20Sopenharmony_ci		{ "ATX +12V (24-Pin)",	 7, 0, 60, 1, 0 },
5428c2ecf20Sopenharmony_ci		{ "ATX +12V (8-pin)",	 8, 0, 60, 1, 0 },
5438c2ecf20Sopenharmony_ci		{ "ATX +5V",		 9, 0, 30, 1, 0 },
5448c2ecf20Sopenharmony_ci		{ "+3.3V",		10, 0, 20, 1, 0 },
5458c2ecf20Sopenharmony_ci		{ "5VSB",		11, 0, 30, 1, 0 },
5468c2ecf20Sopenharmony_ci		{ "CPU",		24, 1, 1, 1, 0 },
5478c2ecf20Sopenharmony_ci		{ "System",		25, 1, 1, 1, 0 },
5488c2ecf20Sopenharmony_ci		{ "PWM",		26, 1, 1, 1, 0 },
5498c2ecf20Sopenharmony_ci		{ "PWM Phase2",		27, 1, 1, 1, 0 },
5508c2ecf20Sopenharmony_ci		{ "PWM Phase3",		28, 1, 1, 1, 0 },
5518c2ecf20Sopenharmony_ci		{ "PWM Phase4",		29, 1, 1, 1, 0 },
5528c2ecf20Sopenharmony_ci		{ "PWM Phase5",		30, 1, 1, 1, 0 },
5538c2ecf20Sopenharmony_ci		{ "CPU Fan",		32, 2, 60, 1, 0 },
5548c2ecf20Sopenharmony_ci		{ "SYS Fan",		34, 2, 60, 1, 0 },
5558c2ecf20Sopenharmony_ci		{ "AUX1 Fan",		33, 2, 60, 1, 0 },
5568c2ecf20Sopenharmony_ci		{ "AUX2 Fan",		35, 2, 60, 1, 0 },
5578c2ecf20Sopenharmony_ci		{ "AUX3 Fan",		36, 2, 60, 1, 0 },
5588c2ecf20Sopenharmony_ci		{ "AUX4 Fan",		37, 2, 60, 1, 0 },
5598c2ecf20Sopenharmony_ci		{ NULL, 0, 0, 0, 0, 0 } }
5608c2ecf20Sopenharmony_ci	},
5618c2ecf20Sopenharmony_ci	{ 0x001B, { NULL } /* Unknown, need DMI string */, {
5628c2ecf20Sopenharmony_ci		{ "CPU Core",		 0, 0, 10, 1, 0 },
5638c2ecf20Sopenharmony_ci		{ "DDR3",		 1, 0, 20, 1, 0 },
5648c2ecf20Sopenharmony_ci		{ "DDR3 VTT",		 2, 0, 10, 1, 0 },
5658c2ecf20Sopenharmony_ci		{ "CPU VTT",		 3, 0, 10, 1, 0 },
5668c2ecf20Sopenharmony_ci		{ "MCH 1.25V",		 4, 0, 10, 1, 0 },
5678c2ecf20Sopenharmony_ci		{ "ICHIO 1.5V",		 5, 0, 10, 1, 0 },
5688c2ecf20Sopenharmony_ci		{ "ICH 1.05V",		 6, 0, 10, 1, 0 },
5698c2ecf20Sopenharmony_ci		{ "ATX +12V (24-Pin)",	 7, 0, 60, 1, 0 },
5708c2ecf20Sopenharmony_ci		{ "ATX +12V (8-pin)",	 8, 0, 60, 1, 0 },
5718c2ecf20Sopenharmony_ci		{ "ATX +5V",		 9, 0, 30, 1, 0 },
5728c2ecf20Sopenharmony_ci		{ "+3.3V",		10, 0, 20, 1, 0 },
5738c2ecf20Sopenharmony_ci		{ "5VSB",		11, 0, 30, 1, 0 },
5748c2ecf20Sopenharmony_ci		{ "CPU",		24, 1, 1, 1, 0 },
5758c2ecf20Sopenharmony_ci		{ "System",		25, 1, 1, 1, 0 },
5768c2ecf20Sopenharmony_ci		{ "PWM Phase1",		26, 1, 1, 1, 0 },
5778c2ecf20Sopenharmony_ci		{ "PWM Phase2",		27, 1, 1, 1, 0 },
5788c2ecf20Sopenharmony_ci		{ "PWM Phase3",		28, 1, 1, 1, 0 },
5798c2ecf20Sopenharmony_ci		{ "PWM Phase4",		29, 1, 1, 1, 0 },
5808c2ecf20Sopenharmony_ci		{ "PWM Phase5",		30, 1, 1, 1, 0 },
5818c2ecf20Sopenharmony_ci		{ "CPU Fan",		32, 2, 60, 1, 0 },
5828c2ecf20Sopenharmony_ci		{ "SYS Fan",		34, 2, 60, 1, 0 },
5838c2ecf20Sopenharmony_ci		{ "AUX1 Fan",		33, 2, 60, 1, 0 },
5848c2ecf20Sopenharmony_ci		{ "AUX2 Fan",		35, 2, 60, 1, 0 },
5858c2ecf20Sopenharmony_ci		{ "AUX3 Fan",		36, 2, 60, 1, 0 },
5868c2ecf20Sopenharmony_ci		{ NULL, 0, 0, 0, 0, 0 } }
5878c2ecf20Sopenharmony_ci	},
5888c2ecf20Sopenharmony_ci	{ 0x001C, { "IX38 QuadGT", NULL }, {
5898c2ecf20Sopenharmony_ci		{ "CPU Core",		 0, 0, 10, 1, 0 },
5908c2ecf20Sopenharmony_ci		{ "DDR2",		 1, 0, 20, 1, 0 },
5918c2ecf20Sopenharmony_ci		{ "DDR2 VTT",		 2, 0, 10, 1, 0 },
5928c2ecf20Sopenharmony_ci		{ "CPU VTT",		 3, 0, 10, 1, 0 },
5938c2ecf20Sopenharmony_ci		{ "MCH 1.25V",		 4, 0, 10, 1, 0 },
5948c2ecf20Sopenharmony_ci		{ "ICHIO 1.5V",		 5, 0, 10, 1, 0 },
5958c2ecf20Sopenharmony_ci		{ "ICH 1.05V",		 6, 0, 10, 1, 0 },
5968c2ecf20Sopenharmony_ci		{ "ATX +12V (24-Pin)",	 7, 0, 60, 1, 0 },
5978c2ecf20Sopenharmony_ci		{ "ATX +12V (8-pin)",	 8, 0, 60, 1, 0 },
5988c2ecf20Sopenharmony_ci		{ "ATX +5V",		 9, 0, 30, 1, 0 },
5998c2ecf20Sopenharmony_ci		{ "+3.3V",		10, 0, 20, 1, 0 },
6008c2ecf20Sopenharmony_ci		{ "5VSB",		11, 0, 30, 1, 0 },
6018c2ecf20Sopenharmony_ci		{ "CPU",		24, 1, 1, 1, 0 },
6028c2ecf20Sopenharmony_ci		{ "System",		25, 1, 1, 1, 0 },
6038c2ecf20Sopenharmony_ci		{ "PWM Phase1",		26, 1, 1, 1, 0 },
6048c2ecf20Sopenharmony_ci		{ "PWM Phase2",		27, 1, 1, 1, 0 },
6058c2ecf20Sopenharmony_ci		{ "PWM Phase3",		28, 1, 1, 1, 0 },
6068c2ecf20Sopenharmony_ci		{ "PWM Phase4",		29, 1, 1, 1, 0 },
6078c2ecf20Sopenharmony_ci		{ "PWM Phase5",		30, 1, 1, 1, 0 },
6088c2ecf20Sopenharmony_ci		{ "CPU Fan",		32, 2, 60, 1, 0 },
6098c2ecf20Sopenharmony_ci		{ "SYS Fan",		34, 2, 60, 1, 0 },
6108c2ecf20Sopenharmony_ci		{ "AUX1 Fan",		33, 2, 60, 1, 0 },
6118c2ecf20Sopenharmony_ci		{ "AUX2 Fan",		35, 2, 60, 1, 0 },
6128c2ecf20Sopenharmony_ci		{ "AUX3 Fan",		36, 2, 60, 1, 0 },
6138c2ecf20Sopenharmony_ci		{ NULL, 0, 0, 0, 0, 0 } }
6148c2ecf20Sopenharmony_ci	},
6158c2ecf20Sopenharmony_ci	{ 0x0000, { NULL }, { { NULL, 0, 0, 0, 0, 0 } } }
6168c2ecf20Sopenharmony_ci};
6178c2ecf20Sopenharmony_ci
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_ci/* Insmod parameters */
6208c2ecf20Sopenharmony_cistatic bool force;
6218c2ecf20Sopenharmony_cimodule_param(force, bool, 0);
6228c2ecf20Sopenharmony_ciMODULE_PARM_DESC(force, "Set to one to force detection.");
6238c2ecf20Sopenharmony_ci/* Default verbose is 1, since this driver is still in the testing phase */
6248c2ecf20Sopenharmony_cistatic bool verbose = 1;
6258c2ecf20Sopenharmony_cimodule_param(verbose, bool, 0644);
6268c2ecf20Sopenharmony_ciMODULE_PARM_DESC(verbose, "Enable/disable verbose error reporting");
6278c2ecf20Sopenharmony_ci
6288c2ecf20Sopenharmony_cistatic const char *never_happen = "This should never happen.";
6298c2ecf20Sopenharmony_cistatic const char *report_this =
6308c2ecf20Sopenharmony_ci	"Please report this to the abituguru3 maintainer (see MAINTAINERS)";
6318c2ecf20Sopenharmony_ci
6328c2ecf20Sopenharmony_ci/* wait while the uguru is busy (usually after a write) */
6338c2ecf20Sopenharmony_cistatic int abituguru3_wait_while_busy(struct abituguru3_data *data)
6348c2ecf20Sopenharmony_ci{
6358c2ecf20Sopenharmony_ci	u8 x;
6368c2ecf20Sopenharmony_ci	int timeout = ABIT_UGURU3_WAIT_TIMEOUT;
6378c2ecf20Sopenharmony_ci
6388c2ecf20Sopenharmony_ci	while ((x = inb_p(data->addr + ABIT_UGURU3_DATA)) &
6398c2ecf20Sopenharmony_ci			ABIT_UGURU3_STATUS_BUSY) {
6408c2ecf20Sopenharmony_ci		timeout--;
6418c2ecf20Sopenharmony_ci		if (timeout == 0)
6428c2ecf20Sopenharmony_ci			return x;
6438c2ecf20Sopenharmony_ci		/*
6448c2ecf20Sopenharmony_ci		 * sleep a bit before our last try, to give the uGuru3 one
6458c2ecf20Sopenharmony_ci		 * last chance to respond.
6468c2ecf20Sopenharmony_ci		 */
6478c2ecf20Sopenharmony_ci		if (timeout == 1)
6488c2ecf20Sopenharmony_ci			msleep(1);
6498c2ecf20Sopenharmony_ci	}
6508c2ecf20Sopenharmony_ci	return ABIT_UGURU3_SUCCESS;
6518c2ecf20Sopenharmony_ci}
6528c2ecf20Sopenharmony_ci
6538c2ecf20Sopenharmony_ci/* wait till uguru is ready to be read */
6548c2ecf20Sopenharmony_cistatic int abituguru3_wait_for_read(struct abituguru3_data *data)
6558c2ecf20Sopenharmony_ci{
6568c2ecf20Sopenharmony_ci	u8 x;
6578c2ecf20Sopenharmony_ci	int timeout = ABIT_UGURU3_WAIT_TIMEOUT;
6588c2ecf20Sopenharmony_ci
6598c2ecf20Sopenharmony_ci	while (!((x = inb_p(data->addr + ABIT_UGURU3_DATA)) &
6608c2ecf20Sopenharmony_ci			ABIT_UGURU3_STATUS_READY_FOR_READ)) {
6618c2ecf20Sopenharmony_ci		timeout--;
6628c2ecf20Sopenharmony_ci		if (timeout == 0)
6638c2ecf20Sopenharmony_ci			return x;
6648c2ecf20Sopenharmony_ci		/*
6658c2ecf20Sopenharmony_ci		 * sleep a bit before our last try, to give the uGuru3 one
6668c2ecf20Sopenharmony_ci		 * last chance to respond.
6678c2ecf20Sopenharmony_ci		 */
6688c2ecf20Sopenharmony_ci		if (timeout == 1)
6698c2ecf20Sopenharmony_ci			msleep(1);
6708c2ecf20Sopenharmony_ci	}
6718c2ecf20Sopenharmony_ci	return ABIT_UGURU3_SUCCESS;
6728c2ecf20Sopenharmony_ci}
6738c2ecf20Sopenharmony_ci
6748c2ecf20Sopenharmony_ci/*
6758c2ecf20Sopenharmony_ci * This synchronizes us with the uGuru3's protocol state machine, this
6768c2ecf20Sopenharmony_ci * must be done before each command.
6778c2ecf20Sopenharmony_ci */
6788c2ecf20Sopenharmony_cistatic int abituguru3_synchronize(struct abituguru3_data *data)
6798c2ecf20Sopenharmony_ci{
6808c2ecf20Sopenharmony_ci	int x, timeout = ABIT_UGURU3_SYNCHRONIZE_TIMEOUT;
6818c2ecf20Sopenharmony_ci
6828c2ecf20Sopenharmony_ci	x = abituguru3_wait_while_busy(data);
6838c2ecf20Sopenharmony_ci	if (x != ABIT_UGURU3_SUCCESS) {
6848c2ecf20Sopenharmony_ci		ABIT_UGURU3_DEBUG("synchronize timeout during initial busy "
6858c2ecf20Sopenharmony_ci			"wait, status: 0x%02x\n", x);
6868c2ecf20Sopenharmony_ci		return -EIO;
6878c2ecf20Sopenharmony_ci	}
6888c2ecf20Sopenharmony_ci
6898c2ecf20Sopenharmony_ci	outb(0x20, data->addr + ABIT_UGURU3_DATA);
6908c2ecf20Sopenharmony_ci	x = abituguru3_wait_while_busy(data);
6918c2ecf20Sopenharmony_ci	if (x != ABIT_UGURU3_SUCCESS) {
6928c2ecf20Sopenharmony_ci		ABIT_UGURU3_DEBUG("synchronize timeout after sending 0x20, "
6938c2ecf20Sopenharmony_ci			"status: 0x%02x\n", x);
6948c2ecf20Sopenharmony_ci		return -EIO;
6958c2ecf20Sopenharmony_ci	}
6968c2ecf20Sopenharmony_ci
6978c2ecf20Sopenharmony_ci	outb(0x10, data->addr + ABIT_UGURU3_CMD);
6988c2ecf20Sopenharmony_ci	x = abituguru3_wait_while_busy(data);
6998c2ecf20Sopenharmony_ci	if (x != ABIT_UGURU3_SUCCESS) {
7008c2ecf20Sopenharmony_ci		ABIT_UGURU3_DEBUG("synchronize timeout after sending 0x10, "
7018c2ecf20Sopenharmony_ci			"status: 0x%02x\n", x);
7028c2ecf20Sopenharmony_ci		return -EIO;
7038c2ecf20Sopenharmony_ci	}
7048c2ecf20Sopenharmony_ci
7058c2ecf20Sopenharmony_ci	outb(0x00, data->addr + ABIT_UGURU3_CMD);
7068c2ecf20Sopenharmony_ci	x = abituguru3_wait_while_busy(data);
7078c2ecf20Sopenharmony_ci	if (x != ABIT_UGURU3_SUCCESS) {
7088c2ecf20Sopenharmony_ci		ABIT_UGURU3_DEBUG("synchronize timeout after sending 0x00, "
7098c2ecf20Sopenharmony_ci			"status: 0x%02x\n", x);
7108c2ecf20Sopenharmony_ci		return -EIO;
7118c2ecf20Sopenharmony_ci	}
7128c2ecf20Sopenharmony_ci
7138c2ecf20Sopenharmony_ci	x = abituguru3_wait_for_read(data);
7148c2ecf20Sopenharmony_ci	if (x != ABIT_UGURU3_SUCCESS) {
7158c2ecf20Sopenharmony_ci		ABIT_UGURU3_DEBUG("synchronize timeout waiting for read, "
7168c2ecf20Sopenharmony_ci			"status: 0x%02x\n", x);
7178c2ecf20Sopenharmony_ci		return -EIO;
7188c2ecf20Sopenharmony_ci	}
7198c2ecf20Sopenharmony_ci
7208c2ecf20Sopenharmony_ci	while ((x = inb(data->addr + ABIT_UGURU3_CMD)) != 0xAC) {
7218c2ecf20Sopenharmony_ci		timeout--;
7228c2ecf20Sopenharmony_ci		if (timeout == 0) {
7238c2ecf20Sopenharmony_ci			ABIT_UGURU3_DEBUG("synchronize timeout cmd does not "
7248c2ecf20Sopenharmony_ci				"hold 0xAC after synchronize, cmd: 0x%02x\n",
7258c2ecf20Sopenharmony_ci				x);
7268c2ecf20Sopenharmony_ci			return -EIO;
7278c2ecf20Sopenharmony_ci		}
7288c2ecf20Sopenharmony_ci		msleep(1);
7298c2ecf20Sopenharmony_ci	}
7308c2ecf20Sopenharmony_ci	return 0;
7318c2ecf20Sopenharmony_ci}
7328c2ecf20Sopenharmony_ci
7338c2ecf20Sopenharmony_ci/*
7348c2ecf20Sopenharmony_ci * Read count bytes from sensor sensor_addr in bank bank_addr and store the
7358c2ecf20Sopenharmony_ci * result in buf
7368c2ecf20Sopenharmony_ci */
7378c2ecf20Sopenharmony_cistatic int abituguru3_read(struct abituguru3_data *data, u8 bank, u8 offset,
7388c2ecf20Sopenharmony_ci	u8 count, u8 *buf)
7398c2ecf20Sopenharmony_ci{
7408c2ecf20Sopenharmony_ci	int i, x;
7418c2ecf20Sopenharmony_ci
7428c2ecf20Sopenharmony_ci	x = abituguru3_synchronize(data);
7438c2ecf20Sopenharmony_ci	if (x)
7448c2ecf20Sopenharmony_ci		return x;
7458c2ecf20Sopenharmony_ci
7468c2ecf20Sopenharmony_ci	outb(0x1A, data->addr + ABIT_UGURU3_DATA);
7478c2ecf20Sopenharmony_ci	x = abituguru3_wait_while_busy(data);
7488c2ecf20Sopenharmony_ci	if (x != ABIT_UGURU3_SUCCESS) {
7498c2ecf20Sopenharmony_ci		ABIT_UGURU3_DEBUG("read from 0x%02x:0x%02x timed out after "
7508c2ecf20Sopenharmony_ci			"sending 0x1A, status: 0x%02x\n", (unsigned int)bank,
7518c2ecf20Sopenharmony_ci			(unsigned int)offset, x);
7528c2ecf20Sopenharmony_ci		return -EIO;
7538c2ecf20Sopenharmony_ci	}
7548c2ecf20Sopenharmony_ci
7558c2ecf20Sopenharmony_ci	outb(bank, data->addr + ABIT_UGURU3_CMD);
7568c2ecf20Sopenharmony_ci	x = abituguru3_wait_while_busy(data);
7578c2ecf20Sopenharmony_ci	if (x != ABIT_UGURU3_SUCCESS) {
7588c2ecf20Sopenharmony_ci		ABIT_UGURU3_DEBUG("read from 0x%02x:0x%02x timed out after "
7598c2ecf20Sopenharmony_ci			"sending the bank, status: 0x%02x\n",
7608c2ecf20Sopenharmony_ci			(unsigned int)bank, (unsigned int)offset, x);
7618c2ecf20Sopenharmony_ci		return -EIO;
7628c2ecf20Sopenharmony_ci	}
7638c2ecf20Sopenharmony_ci
7648c2ecf20Sopenharmony_ci	outb(offset, data->addr + ABIT_UGURU3_CMD);
7658c2ecf20Sopenharmony_ci	x = abituguru3_wait_while_busy(data);
7668c2ecf20Sopenharmony_ci	if (x != ABIT_UGURU3_SUCCESS) {
7678c2ecf20Sopenharmony_ci		ABIT_UGURU3_DEBUG("read from 0x%02x:0x%02x timed out after "
7688c2ecf20Sopenharmony_ci			"sending the offset, status: 0x%02x\n",
7698c2ecf20Sopenharmony_ci			(unsigned int)bank, (unsigned int)offset, x);
7708c2ecf20Sopenharmony_ci		return -EIO;
7718c2ecf20Sopenharmony_ci	}
7728c2ecf20Sopenharmony_ci
7738c2ecf20Sopenharmony_ci	outb(count, data->addr + ABIT_UGURU3_CMD);
7748c2ecf20Sopenharmony_ci	x = abituguru3_wait_while_busy(data);
7758c2ecf20Sopenharmony_ci	if (x != ABIT_UGURU3_SUCCESS) {
7768c2ecf20Sopenharmony_ci		ABIT_UGURU3_DEBUG("read from 0x%02x:0x%02x timed out after "
7778c2ecf20Sopenharmony_ci			"sending the count, status: 0x%02x\n",
7788c2ecf20Sopenharmony_ci			(unsigned int)bank, (unsigned int)offset, x);
7798c2ecf20Sopenharmony_ci		return -EIO;
7808c2ecf20Sopenharmony_ci	}
7818c2ecf20Sopenharmony_ci
7828c2ecf20Sopenharmony_ci	for (i = 0; i < count; i++) {
7838c2ecf20Sopenharmony_ci		x = abituguru3_wait_for_read(data);
7848c2ecf20Sopenharmony_ci		if (x != ABIT_UGURU3_SUCCESS) {
7858c2ecf20Sopenharmony_ci			ABIT_UGURU3_DEBUG("timeout reading byte %d from "
7868c2ecf20Sopenharmony_ci				"0x%02x:0x%02x, status: 0x%02x\n", i,
7878c2ecf20Sopenharmony_ci				(unsigned int)bank, (unsigned int)offset, x);
7888c2ecf20Sopenharmony_ci			break;
7898c2ecf20Sopenharmony_ci		}
7908c2ecf20Sopenharmony_ci		buf[i] = inb(data->addr + ABIT_UGURU3_CMD);
7918c2ecf20Sopenharmony_ci	}
7928c2ecf20Sopenharmony_ci	return i;
7938c2ecf20Sopenharmony_ci}
7948c2ecf20Sopenharmony_ci
7958c2ecf20Sopenharmony_ci/*
7968c2ecf20Sopenharmony_ci * Sensor settings are stored 1 byte per offset with the bytes
7978c2ecf20Sopenharmony_ci * placed add consecutive offsets.
7988c2ecf20Sopenharmony_ci */
7998c2ecf20Sopenharmony_cistatic int abituguru3_read_increment_offset(struct abituguru3_data *data,
8008c2ecf20Sopenharmony_ci					    u8 bank, u8 offset, u8 count,
8018c2ecf20Sopenharmony_ci					    u8 *buf, int offset_count)
8028c2ecf20Sopenharmony_ci{
8038c2ecf20Sopenharmony_ci	int i, x;
8048c2ecf20Sopenharmony_ci
8058c2ecf20Sopenharmony_ci	for (i = 0; i < offset_count; i++) {
8068c2ecf20Sopenharmony_ci		x = abituguru3_read(data, bank, offset + i, count,
8078c2ecf20Sopenharmony_ci				    buf + i * count);
8088c2ecf20Sopenharmony_ci		if (x != count) {
8098c2ecf20Sopenharmony_ci			if (x < 0)
8108c2ecf20Sopenharmony_ci				return x;
8118c2ecf20Sopenharmony_ci			return i * count + x;
8128c2ecf20Sopenharmony_ci		}
8138c2ecf20Sopenharmony_ci	}
8148c2ecf20Sopenharmony_ci
8158c2ecf20Sopenharmony_ci	return i * count;
8168c2ecf20Sopenharmony_ci}
8178c2ecf20Sopenharmony_ci
8188c2ecf20Sopenharmony_ci/*
8198c2ecf20Sopenharmony_ci * Following are the sysfs callback functions. These functions expect:
8208c2ecf20Sopenharmony_ci * sensor_device_attribute_2->index:   index into the data->sensors array
8218c2ecf20Sopenharmony_ci * sensor_device_attribute_2->nr:      register offset, bitmask or NA.
8228c2ecf20Sopenharmony_ci */
8238c2ecf20Sopenharmony_cistatic struct abituguru3_data *abituguru3_update_device(struct device *dev);
8248c2ecf20Sopenharmony_ci
8258c2ecf20Sopenharmony_cistatic ssize_t show_value(struct device *dev,
8268c2ecf20Sopenharmony_ci	struct device_attribute *devattr, char *buf)
8278c2ecf20Sopenharmony_ci{
8288c2ecf20Sopenharmony_ci	int value;
8298c2ecf20Sopenharmony_ci	struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr);
8308c2ecf20Sopenharmony_ci	struct abituguru3_data *data = abituguru3_update_device(dev);
8318c2ecf20Sopenharmony_ci	const struct abituguru3_sensor_info *sensor;
8328c2ecf20Sopenharmony_ci
8338c2ecf20Sopenharmony_ci	if (!data)
8348c2ecf20Sopenharmony_ci		return -EIO;
8358c2ecf20Sopenharmony_ci
8368c2ecf20Sopenharmony_ci	sensor = &data->sensors[attr->index];
8378c2ecf20Sopenharmony_ci
8388c2ecf20Sopenharmony_ci	/* are we reading a setting, or is this a normal read? */
8398c2ecf20Sopenharmony_ci	if (attr->nr)
8408c2ecf20Sopenharmony_ci		value = data->settings[sensor->port][attr->nr];
8418c2ecf20Sopenharmony_ci	else
8428c2ecf20Sopenharmony_ci		value = data->value[sensor->port];
8438c2ecf20Sopenharmony_ci
8448c2ecf20Sopenharmony_ci	/* convert the value */
8458c2ecf20Sopenharmony_ci	value = (value * sensor->multiplier) / sensor->divisor +
8468c2ecf20Sopenharmony_ci		sensor->offset;
8478c2ecf20Sopenharmony_ci
8488c2ecf20Sopenharmony_ci	/*
8498c2ecf20Sopenharmony_ci	 * alternatively we could update the sensors settings struct for this,
8508c2ecf20Sopenharmony_ci	 * but then its contents would differ from the windows sw ini files
8518c2ecf20Sopenharmony_ci	 */
8528c2ecf20Sopenharmony_ci	if (sensor->type == ABIT_UGURU3_TEMP_SENSOR)
8538c2ecf20Sopenharmony_ci		value *= 1000;
8548c2ecf20Sopenharmony_ci
8558c2ecf20Sopenharmony_ci	return sprintf(buf, "%d\n", value);
8568c2ecf20Sopenharmony_ci}
8578c2ecf20Sopenharmony_ci
8588c2ecf20Sopenharmony_cistatic ssize_t show_alarm(struct device *dev,
8598c2ecf20Sopenharmony_ci	struct device_attribute *devattr, char *buf)
8608c2ecf20Sopenharmony_ci{
8618c2ecf20Sopenharmony_ci	int port;
8628c2ecf20Sopenharmony_ci	struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr);
8638c2ecf20Sopenharmony_ci	struct abituguru3_data *data = abituguru3_update_device(dev);
8648c2ecf20Sopenharmony_ci
8658c2ecf20Sopenharmony_ci	if (!data)
8668c2ecf20Sopenharmony_ci		return -EIO;
8678c2ecf20Sopenharmony_ci
8688c2ecf20Sopenharmony_ci	port = data->sensors[attr->index].port;
8698c2ecf20Sopenharmony_ci
8708c2ecf20Sopenharmony_ci	/*
8718c2ecf20Sopenharmony_ci	 * See if the alarm bit for this sensor is set and if a bitmask is
8728c2ecf20Sopenharmony_ci	 * given in attr->nr also check if the alarm matches the type of alarm
8738c2ecf20Sopenharmony_ci	 * we're looking for (for volt it can be either low or high). The type
8748c2ecf20Sopenharmony_ci	 * is stored in a few readonly bits in the settings of the sensor.
8758c2ecf20Sopenharmony_ci	 */
8768c2ecf20Sopenharmony_ci	if ((data->alarms[port / 8] & (0x01 << (port % 8))) &&
8778c2ecf20Sopenharmony_ci			(!attr->nr || (data->settings[port][0] & attr->nr)))
8788c2ecf20Sopenharmony_ci		return sprintf(buf, "1\n");
8798c2ecf20Sopenharmony_ci	else
8808c2ecf20Sopenharmony_ci		return sprintf(buf, "0\n");
8818c2ecf20Sopenharmony_ci}
8828c2ecf20Sopenharmony_ci
8838c2ecf20Sopenharmony_cistatic ssize_t show_mask(struct device *dev,
8848c2ecf20Sopenharmony_ci	struct device_attribute *devattr, char *buf)
8858c2ecf20Sopenharmony_ci{
8868c2ecf20Sopenharmony_ci	struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr);
8878c2ecf20Sopenharmony_ci	struct abituguru3_data *data = dev_get_drvdata(dev);
8888c2ecf20Sopenharmony_ci
8898c2ecf20Sopenharmony_ci	if (data->settings[data->sensors[attr->index].port][0] & attr->nr)
8908c2ecf20Sopenharmony_ci		return sprintf(buf, "1\n");
8918c2ecf20Sopenharmony_ci	else
8928c2ecf20Sopenharmony_ci		return sprintf(buf, "0\n");
8938c2ecf20Sopenharmony_ci}
8948c2ecf20Sopenharmony_ci
8958c2ecf20Sopenharmony_cistatic ssize_t show_label(struct device *dev,
8968c2ecf20Sopenharmony_ci	struct device_attribute *devattr, char *buf)
8978c2ecf20Sopenharmony_ci{
8988c2ecf20Sopenharmony_ci	struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr);
8998c2ecf20Sopenharmony_ci	struct abituguru3_data *data = dev_get_drvdata(dev);
9008c2ecf20Sopenharmony_ci
9018c2ecf20Sopenharmony_ci	return sprintf(buf, "%s\n", data->sensors[attr->index].name);
9028c2ecf20Sopenharmony_ci}
9038c2ecf20Sopenharmony_ci
9048c2ecf20Sopenharmony_cistatic ssize_t show_name(struct device *dev,
9058c2ecf20Sopenharmony_ci	struct device_attribute *devattr, char *buf)
9068c2ecf20Sopenharmony_ci{
9078c2ecf20Sopenharmony_ci	return sprintf(buf, "%s\n", ABIT_UGURU3_NAME);
9088c2ecf20Sopenharmony_ci}
9098c2ecf20Sopenharmony_ci
9108c2ecf20Sopenharmony_ci/* Sysfs attr templates, the real entries are generated automatically. */
9118c2ecf20Sopenharmony_cistatic const
9128c2ecf20Sopenharmony_cistruct sensor_device_attribute_2 abituguru3_sysfs_templ[3][10] = { {
9138c2ecf20Sopenharmony_ci	SENSOR_ATTR_2(in%d_input, 0444, show_value, NULL, 0, 0),
9148c2ecf20Sopenharmony_ci	SENSOR_ATTR_2(in%d_min, 0444, show_value, NULL, 1, 0),
9158c2ecf20Sopenharmony_ci	SENSOR_ATTR_2(in%d_max, 0444, show_value, NULL, 2, 0),
9168c2ecf20Sopenharmony_ci	SENSOR_ATTR_2(in%d_min_alarm, 0444, show_alarm, NULL,
9178c2ecf20Sopenharmony_ci		ABIT_UGURU3_VOLT_LOW_ALARM_FLAG, 0),
9188c2ecf20Sopenharmony_ci	SENSOR_ATTR_2(in%d_max_alarm, 0444, show_alarm, NULL,
9198c2ecf20Sopenharmony_ci		ABIT_UGURU3_VOLT_HIGH_ALARM_FLAG, 0),
9208c2ecf20Sopenharmony_ci	SENSOR_ATTR_2(in%d_beep, 0444, show_mask, NULL,
9218c2ecf20Sopenharmony_ci		ABIT_UGURU3_BEEP_ENABLE, 0),
9228c2ecf20Sopenharmony_ci	SENSOR_ATTR_2(in%d_shutdown, 0444, show_mask, NULL,
9238c2ecf20Sopenharmony_ci		ABIT_UGURU3_SHUTDOWN_ENABLE, 0),
9248c2ecf20Sopenharmony_ci	SENSOR_ATTR_2(in%d_min_alarm_enable, 0444, show_mask, NULL,
9258c2ecf20Sopenharmony_ci		ABIT_UGURU3_VOLT_LOW_ALARM_ENABLE, 0),
9268c2ecf20Sopenharmony_ci	SENSOR_ATTR_2(in%d_max_alarm_enable, 0444, show_mask, NULL,
9278c2ecf20Sopenharmony_ci		ABIT_UGURU3_VOLT_HIGH_ALARM_ENABLE, 0),
9288c2ecf20Sopenharmony_ci	SENSOR_ATTR_2(in%d_label, 0444, show_label, NULL, 0, 0)
9298c2ecf20Sopenharmony_ci	}, {
9308c2ecf20Sopenharmony_ci	SENSOR_ATTR_2(temp%d_input, 0444, show_value, NULL, 0, 0),
9318c2ecf20Sopenharmony_ci	SENSOR_ATTR_2(temp%d_max, 0444, show_value, NULL, 1, 0),
9328c2ecf20Sopenharmony_ci	SENSOR_ATTR_2(temp%d_crit, 0444, show_value, NULL, 2, 0),
9338c2ecf20Sopenharmony_ci	SENSOR_ATTR_2(temp%d_alarm, 0444, show_alarm, NULL, 0, 0),
9348c2ecf20Sopenharmony_ci	SENSOR_ATTR_2(temp%d_beep, 0444, show_mask, NULL,
9358c2ecf20Sopenharmony_ci		ABIT_UGURU3_BEEP_ENABLE, 0),
9368c2ecf20Sopenharmony_ci	SENSOR_ATTR_2(temp%d_shutdown, 0444, show_mask, NULL,
9378c2ecf20Sopenharmony_ci		ABIT_UGURU3_SHUTDOWN_ENABLE, 0),
9388c2ecf20Sopenharmony_ci	SENSOR_ATTR_2(temp%d_alarm_enable, 0444, show_mask, NULL,
9398c2ecf20Sopenharmony_ci		ABIT_UGURU3_TEMP_HIGH_ALARM_ENABLE, 0),
9408c2ecf20Sopenharmony_ci	SENSOR_ATTR_2(temp%d_label, 0444, show_label, NULL, 0, 0)
9418c2ecf20Sopenharmony_ci	}, {
9428c2ecf20Sopenharmony_ci	SENSOR_ATTR_2(fan%d_input, 0444, show_value, NULL, 0, 0),
9438c2ecf20Sopenharmony_ci	SENSOR_ATTR_2(fan%d_min, 0444, show_value, NULL, 1, 0),
9448c2ecf20Sopenharmony_ci	SENSOR_ATTR_2(fan%d_alarm, 0444, show_alarm, NULL, 0, 0),
9458c2ecf20Sopenharmony_ci	SENSOR_ATTR_2(fan%d_beep, 0444, show_mask, NULL,
9468c2ecf20Sopenharmony_ci		ABIT_UGURU3_BEEP_ENABLE, 0),
9478c2ecf20Sopenharmony_ci	SENSOR_ATTR_2(fan%d_shutdown, 0444, show_mask, NULL,
9488c2ecf20Sopenharmony_ci		ABIT_UGURU3_SHUTDOWN_ENABLE, 0),
9498c2ecf20Sopenharmony_ci	SENSOR_ATTR_2(fan%d_alarm_enable, 0444, show_mask, NULL,
9508c2ecf20Sopenharmony_ci		ABIT_UGURU3_FAN_LOW_ALARM_ENABLE, 0),
9518c2ecf20Sopenharmony_ci	SENSOR_ATTR_2(fan%d_label, 0444, show_label, NULL, 0, 0)
9528c2ecf20Sopenharmony_ci} };
9538c2ecf20Sopenharmony_ci
9548c2ecf20Sopenharmony_cistatic struct sensor_device_attribute_2 abituguru3_sysfs_attr[] = {
9558c2ecf20Sopenharmony_ci	SENSOR_ATTR_2(name, 0444, show_name, NULL, 0, 0),
9568c2ecf20Sopenharmony_ci};
9578c2ecf20Sopenharmony_ci
9588c2ecf20Sopenharmony_cistatic int abituguru3_probe(struct platform_device *pdev)
9598c2ecf20Sopenharmony_ci{
9608c2ecf20Sopenharmony_ci	const int no_sysfs_attr[3] = { 10, 8, 7 };
9618c2ecf20Sopenharmony_ci	int sensor_index[3] = { 0, 1, 1 };
9628c2ecf20Sopenharmony_ci	struct abituguru3_data *data;
9638c2ecf20Sopenharmony_ci	int i, j, type, used, sysfs_names_free, sysfs_attr_i, res = -ENODEV;
9648c2ecf20Sopenharmony_ci	char *sysfs_filename;
9658c2ecf20Sopenharmony_ci	u8 buf[2];
9668c2ecf20Sopenharmony_ci	u16 id;
9678c2ecf20Sopenharmony_ci
9688c2ecf20Sopenharmony_ci	data = devm_kzalloc(&pdev->dev, sizeof(struct abituguru3_data),
9698c2ecf20Sopenharmony_ci			    GFP_KERNEL);
9708c2ecf20Sopenharmony_ci	if (!data)
9718c2ecf20Sopenharmony_ci		return -ENOMEM;
9728c2ecf20Sopenharmony_ci
9738c2ecf20Sopenharmony_ci	data->addr = platform_get_resource(pdev, IORESOURCE_IO, 0)->start;
9748c2ecf20Sopenharmony_ci	mutex_init(&data->update_lock);
9758c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, data);
9768c2ecf20Sopenharmony_ci
9778c2ecf20Sopenharmony_ci	/* Read the motherboard ID */
9788c2ecf20Sopenharmony_ci	i = abituguru3_read(data, ABIT_UGURU3_MISC_BANK, ABIT_UGURU3_BOARD_ID,
9798c2ecf20Sopenharmony_ci			    2, buf);
9808c2ecf20Sopenharmony_ci	if (i != 2)
9818c2ecf20Sopenharmony_ci		goto abituguru3_probe_error;
9828c2ecf20Sopenharmony_ci
9838c2ecf20Sopenharmony_ci	/* Completely read the uGuru to see if one really is there */
9848c2ecf20Sopenharmony_ci	if (!abituguru3_update_device(&pdev->dev))
9858c2ecf20Sopenharmony_ci		goto abituguru3_probe_error;
9868c2ecf20Sopenharmony_ci
9878c2ecf20Sopenharmony_ci	/* lookup the ID in our motherboard table */
9888c2ecf20Sopenharmony_ci	id = ((u16)buf[0] << 8) | (u16)buf[1];
9898c2ecf20Sopenharmony_ci	for (i = 0; abituguru3_motherboards[i].id; i++)
9908c2ecf20Sopenharmony_ci		if (abituguru3_motherboards[i].id == id)
9918c2ecf20Sopenharmony_ci			break;
9928c2ecf20Sopenharmony_ci	if (!abituguru3_motherboards[i].id) {
9938c2ecf20Sopenharmony_ci		pr_err("error unknown motherboard ID: %04X. %s\n",
9948c2ecf20Sopenharmony_ci		       (unsigned int)id, report_this);
9958c2ecf20Sopenharmony_ci		goto abituguru3_probe_error;
9968c2ecf20Sopenharmony_ci	}
9978c2ecf20Sopenharmony_ci	data->sensors = abituguru3_motherboards[i].sensors;
9988c2ecf20Sopenharmony_ci
9998c2ecf20Sopenharmony_ci	pr_info("found Abit uGuru3, motherboard ID: %04X\n", (unsigned int)id);
10008c2ecf20Sopenharmony_ci
10018c2ecf20Sopenharmony_ci	/* Fill the sysfs attr array */
10028c2ecf20Sopenharmony_ci	sysfs_attr_i = 0;
10038c2ecf20Sopenharmony_ci	sysfs_filename = data->sysfs_names;
10048c2ecf20Sopenharmony_ci	sysfs_names_free = ABIT_UGURU3_SYSFS_NAMES_LENGTH;
10058c2ecf20Sopenharmony_ci	for (i = 0; data->sensors[i].name; i++) {
10068c2ecf20Sopenharmony_ci		/* Fail safe check, this should never happen! */
10078c2ecf20Sopenharmony_ci		if (i >= ABIT_UGURU3_MAX_NO_SENSORS) {
10088c2ecf20Sopenharmony_ci			pr_err("Fatal error motherboard has more sensors then ABIT_UGURU3_MAX_NO_SENSORS. %s %s\n",
10098c2ecf20Sopenharmony_ci			       never_happen, report_this);
10108c2ecf20Sopenharmony_ci			res = -ENAMETOOLONG;
10118c2ecf20Sopenharmony_ci			goto abituguru3_probe_error;
10128c2ecf20Sopenharmony_ci		}
10138c2ecf20Sopenharmony_ci		type = data->sensors[i].type;
10148c2ecf20Sopenharmony_ci		for (j = 0; j < no_sysfs_attr[type]; j++) {
10158c2ecf20Sopenharmony_ci			used = snprintf(sysfs_filename, sysfs_names_free,
10168c2ecf20Sopenharmony_ci				abituguru3_sysfs_templ[type][j].dev_attr.attr.
10178c2ecf20Sopenharmony_ci				name, sensor_index[type]) + 1;
10188c2ecf20Sopenharmony_ci			data->sysfs_attr[sysfs_attr_i] =
10198c2ecf20Sopenharmony_ci				abituguru3_sysfs_templ[type][j];
10208c2ecf20Sopenharmony_ci			data->sysfs_attr[sysfs_attr_i].dev_attr.attr.name =
10218c2ecf20Sopenharmony_ci				sysfs_filename;
10228c2ecf20Sopenharmony_ci			data->sysfs_attr[sysfs_attr_i].index = i;
10238c2ecf20Sopenharmony_ci			sysfs_filename += used;
10248c2ecf20Sopenharmony_ci			sysfs_names_free -= used;
10258c2ecf20Sopenharmony_ci			sysfs_attr_i++;
10268c2ecf20Sopenharmony_ci		}
10278c2ecf20Sopenharmony_ci		sensor_index[type]++;
10288c2ecf20Sopenharmony_ci	}
10298c2ecf20Sopenharmony_ci	/* Fail safe check, this should never happen! */
10308c2ecf20Sopenharmony_ci	if (sysfs_names_free < 0) {
10318c2ecf20Sopenharmony_ci		pr_err("Fatal error ran out of space for sysfs attr names. %s %s\n",
10328c2ecf20Sopenharmony_ci		       never_happen, report_this);
10338c2ecf20Sopenharmony_ci		res = -ENAMETOOLONG;
10348c2ecf20Sopenharmony_ci		goto abituguru3_probe_error;
10358c2ecf20Sopenharmony_ci	}
10368c2ecf20Sopenharmony_ci
10378c2ecf20Sopenharmony_ci	/* Register sysfs hooks */
10388c2ecf20Sopenharmony_ci	for (i = 0; i < sysfs_attr_i; i++)
10398c2ecf20Sopenharmony_ci		if (device_create_file(&pdev->dev,
10408c2ecf20Sopenharmony_ci				&data->sysfs_attr[i].dev_attr))
10418c2ecf20Sopenharmony_ci			goto abituguru3_probe_error;
10428c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(abituguru3_sysfs_attr); i++)
10438c2ecf20Sopenharmony_ci		if (device_create_file(&pdev->dev,
10448c2ecf20Sopenharmony_ci				&abituguru3_sysfs_attr[i].dev_attr))
10458c2ecf20Sopenharmony_ci			goto abituguru3_probe_error;
10468c2ecf20Sopenharmony_ci
10478c2ecf20Sopenharmony_ci	data->hwmon_dev = hwmon_device_register(&pdev->dev);
10488c2ecf20Sopenharmony_ci	if (IS_ERR(data->hwmon_dev)) {
10498c2ecf20Sopenharmony_ci		res = PTR_ERR(data->hwmon_dev);
10508c2ecf20Sopenharmony_ci		goto abituguru3_probe_error;
10518c2ecf20Sopenharmony_ci	}
10528c2ecf20Sopenharmony_ci
10538c2ecf20Sopenharmony_ci	return 0; /* success */
10548c2ecf20Sopenharmony_ci
10558c2ecf20Sopenharmony_ciabituguru3_probe_error:
10568c2ecf20Sopenharmony_ci	for (i = 0; data->sysfs_attr[i].dev_attr.attr.name; i++)
10578c2ecf20Sopenharmony_ci		device_remove_file(&pdev->dev, &data->sysfs_attr[i].dev_attr);
10588c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(abituguru3_sysfs_attr); i++)
10598c2ecf20Sopenharmony_ci		device_remove_file(&pdev->dev,
10608c2ecf20Sopenharmony_ci			&abituguru3_sysfs_attr[i].dev_attr);
10618c2ecf20Sopenharmony_ci	return res;
10628c2ecf20Sopenharmony_ci}
10638c2ecf20Sopenharmony_ci
10648c2ecf20Sopenharmony_cistatic int abituguru3_remove(struct platform_device *pdev)
10658c2ecf20Sopenharmony_ci{
10668c2ecf20Sopenharmony_ci	int i;
10678c2ecf20Sopenharmony_ci	struct abituguru3_data *data = platform_get_drvdata(pdev);
10688c2ecf20Sopenharmony_ci
10698c2ecf20Sopenharmony_ci	hwmon_device_unregister(data->hwmon_dev);
10708c2ecf20Sopenharmony_ci	for (i = 0; data->sysfs_attr[i].dev_attr.attr.name; i++)
10718c2ecf20Sopenharmony_ci		device_remove_file(&pdev->dev, &data->sysfs_attr[i].dev_attr);
10728c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(abituguru3_sysfs_attr); i++)
10738c2ecf20Sopenharmony_ci		device_remove_file(&pdev->dev,
10748c2ecf20Sopenharmony_ci			&abituguru3_sysfs_attr[i].dev_attr);
10758c2ecf20Sopenharmony_ci	return 0;
10768c2ecf20Sopenharmony_ci}
10778c2ecf20Sopenharmony_ci
10788c2ecf20Sopenharmony_cistatic struct abituguru3_data *abituguru3_update_device(struct device *dev)
10798c2ecf20Sopenharmony_ci{
10808c2ecf20Sopenharmony_ci	int i;
10818c2ecf20Sopenharmony_ci	struct abituguru3_data *data = dev_get_drvdata(dev);
10828c2ecf20Sopenharmony_ci
10838c2ecf20Sopenharmony_ci	mutex_lock(&data->update_lock);
10848c2ecf20Sopenharmony_ci	if (!data->valid || time_after(jiffies, data->last_updated + HZ)) {
10858c2ecf20Sopenharmony_ci		/* Clear data->valid while updating */
10868c2ecf20Sopenharmony_ci		data->valid = 0;
10878c2ecf20Sopenharmony_ci		/* Read alarms */
10888c2ecf20Sopenharmony_ci		if (abituguru3_read_increment_offset(data,
10898c2ecf20Sopenharmony_ci				ABIT_UGURU3_SETTINGS_BANK,
10908c2ecf20Sopenharmony_ci				ABIT_UGURU3_ALARMS_START,
10918c2ecf20Sopenharmony_ci				1, data->alarms, 48/8) != (48/8))
10928c2ecf20Sopenharmony_ci			goto LEAVE_UPDATE;
10938c2ecf20Sopenharmony_ci		/* Read in and temp sensors (3 byte settings / sensor) */
10948c2ecf20Sopenharmony_ci		for (i = 0; i < 32; i++) {
10958c2ecf20Sopenharmony_ci			if (abituguru3_read(data, ABIT_UGURU3_SENSORS_BANK,
10968c2ecf20Sopenharmony_ci					ABIT_UGURU3_VALUES_START + i,
10978c2ecf20Sopenharmony_ci					1, &data->value[i]) != 1)
10988c2ecf20Sopenharmony_ci				goto LEAVE_UPDATE;
10998c2ecf20Sopenharmony_ci			if (abituguru3_read_increment_offset(data,
11008c2ecf20Sopenharmony_ci					ABIT_UGURU3_SETTINGS_BANK,
11018c2ecf20Sopenharmony_ci					ABIT_UGURU3_SETTINGS_START + i * 3,
11028c2ecf20Sopenharmony_ci					1,
11038c2ecf20Sopenharmony_ci					data->settings[i], 3) != 3)
11048c2ecf20Sopenharmony_ci				goto LEAVE_UPDATE;
11058c2ecf20Sopenharmony_ci		}
11068c2ecf20Sopenharmony_ci		/* Read temp sensors (2 byte settings / sensor) */
11078c2ecf20Sopenharmony_ci		for (i = 0; i < 16; i++) {
11088c2ecf20Sopenharmony_ci			if (abituguru3_read(data, ABIT_UGURU3_SENSORS_BANK,
11098c2ecf20Sopenharmony_ci					ABIT_UGURU3_VALUES_START + 32 + i,
11108c2ecf20Sopenharmony_ci					1, &data->value[32 + i]) != 1)
11118c2ecf20Sopenharmony_ci				goto LEAVE_UPDATE;
11128c2ecf20Sopenharmony_ci			if (abituguru3_read_increment_offset(data,
11138c2ecf20Sopenharmony_ci					ABIT_UGURU3_SETTINGS_BANK,
11148c2ecf20Sopenharmony_ci					ABIT_UGURU3_SETTINGS_START + 32 * 3 +
11158c2ecf20Sopenharmony_ci						i * 2, 1,
11168c2ecf20Sopenharmony_ci					data->settings[32 + i], 2) != 2)
11178c2ecf20Sopenharmony_ci				goto LEAVE_UPDATE;
11188c2ecf20Sopenharmony_ci		}
11198c2ecf20Sopenharmony_ci		data->last_updated = jiffies;
11208c2ecf20Sopenharmony_ci		data->valid = 1;
11218c2ecf20Sopenharmony_ci	}
11228c2ecf20Sopenharmony_ciLEAVE_UPDATE:
11238c2ecf20Sopenharmony_ci	mutex_unlock(&data->update_lock);
11248c2ecf20Sopenharmony_ci	if (data->valid)
11258c2ecf20Sopenharmony_ci		return data;
11268c2ecf20Sopenharmony_ci	else
11278c2ecf20Sopenharmony_ci		return NULL;
11288c2ecf20Sopenharmony_ci}
11298c2ecf20Sopenharmony_ci
11308c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
11318c2ecf20Sopenharmony_cistatic int abituguru3_suspend(struct device *dev)
11328c2ecf20Sopenharmony_ci{
11338c2ecf20Sopenharmony_ci	struct abituguru3_data *data = dev_get_drvdata(dev);
11348c2ecf20Sopenharmony_ci	/*
11358c2ecf20Sopenharmony_ci	 * make sure all communications with the uguru3 are done and no new
11368c2ecf20Sopenharmony_ci	 * ones are started
11378c2ecf20Sopenharmony_ci	 */
11388c2ecf20Sopenharmony_ci	mutex_lock(&data->update_lock);
11398c2ecf20Sopenharmony_ci	return 0;
11408c2ecf20Sopenharmony_ci}
11418c2ecf20Sopenharmony_ci
11428c2ecf20Sopenharmony_cistatic int abituguru3_resume(struct device *dev)
11438c2ecf20Sopenharmony_ci{
11448c2ecf20Sopenharmony_ci	struct abituguru3_data *data = dev_get_drvdata(dev);
11458c2ecf20Sopenharmony_ci	mutex_unlock(&data->update_lock);
11468c2ecf20Sopenharmony_ci	return 0;
11478c2ecf20Sopenharmony_ci}
11488c2ecf20Sopenharmony_ci
11498c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(abituguru3_pm, abituguru3_suspend, abituguru3_resume);
11508c2ecf20Sopenharmony_ci#define ABIT_UGURU3_PM	(&abituguru3_pm)
11518c2ecf20Sopenharmony_ci#else
11528c2ecf20Sopenharmony_ci#define ABIT_UGURU3_PM	NULL
11538c2ecf20Sopenharmony_ci#endif /* CONFIG_PM */
11548c2ecf20Sopenharmony_ci
11558c2ecf20Sopenharmony_cistatic struct platform_driver abituguru3_driver = {
11568c2ecf20Sopenharmony_ci	.driver = {
11578c2ecf20Sopenharmony_ci		.name	= ABIT_UGURU3_NAME,
11588c2ecf20Sopenharmony_ci		.pm	= ABIT_UGURU3_PM
11598c2ecf20Sopenharmony_ci	},
11608c2ecf20Sopenharmony_ci	.probe	= abituguru3_probe,
11618c2ecf20Sopenharmony_ci	.remove	= abituguru3_remove,
11628c2ecf20Sopenharmony_ci};
11638c2ecf20Sopenharmony_ci
11648c2ecf20Sopenharmony_cistatic int __init abituguru3_dmi_detect(void)
11658c2ecf20Sopenharmony_ci{
11668c2ecf20Sopenharmony_ci	const char *board_vendor, *board_name;
11678c2ecf20Sopenharmony_ci	int i, err = (force) ? 1 : -ENODEV;
11688c2ecf20Sopenharmony_ci	const char *const *dmi_name;
11698c2ecf20Sopenharmony_ci	size_t sublen;
11708c2ecf20Sopenharmony_ci
11718c2ecf20Sopenharmony_ci	board_vendor = dmi_get_system_info(DMI_BOARD_VENDOR);
11728c2ecf20Sopenharmony_ci	if (!board_vendor || strcmp(board_vendor, "http://www.abit.com.tw/"))
11738c2ecf20Sopenharmony_ci		return err;
11748c2ecf20Sopenharmony_ci
11758c2ecf20Sopenharmony_ci	board_name = dmi_get_system_info(DMI_BOARD_NAME);
11768c2ecf20Sopenharmony_ci	if (!board_name)
11778c2ecf20Sopenharmony_ci		return err;
11788c2ecf20Sopenharmony_ci
11798c2ecf20Sopenharmony_ci	/*
11808c2ecf20Sopenharmony_ci	 * At the moment, we don't care about the part of the vendor
11818c2ecf20Sopenharmony_ci	 * DMI string contained in brackets. Truncate the string at
11828c2ecf20Sopenharmony_ci	 * the first occurrence of a bracket. Trim any trailing space
11838c2ecf20Sopenharmony_ci	 * from the substring.
11848c2ecf20Sopenharmony_ci	 */
11858c2ecf20Sopenharmony_ci	sublen = strcspn(board_name, "(");
11868c2ecf20Sopenharmony_ci	while (sublen > 0 && board_name[sublen - 1] == ' ')
11878c2ecf20Sopenharmony_ci		sublen--;
11888c2ecf20Sopenharmony_ci
11898c2ecf20Sopenharmony_ci	for (i = 0; abituguru3_motherboards[i].id; i++) {
11908c2ecf20Sopenharmony_ci		dmi_name = abituguru3_motherboards[i].dmi_name;
11918c2ecf20Sopenharmony_ci		for ( ; *dmi_name; dmi_name++) {
11928c2ecf20Sopenharmony_ci			if (strlen(*dmi_name) != sublen)
11938c2ecf20Sopenharmony_ci				continue;
11948c2ecf20Sopenharmony_ci			if (!strncasecmp(board_name, *dmi_name, sublen))
11958c2ecf20Sopenharmony_ci				return 0;
11968c2ecf20Sopenharmony_ci		}
11978c2ecf20Sopenharmony_ci	}
11988c2ecf20Sopenharmony_ci
11998c2ecf20Sopenharmony_ci	/* No match found */
12008c2ecf20Sopenharmony_ci	return 1;
12018c2ecf20Sopenharmony_ci}
12028c2ecf20Sopenharmony_ci
12038c2ecf20Sopenharmony_ci/*
12048c2ecf20Sopenharmony_ci * FIXME: Manual detection should die eventually; we need to collect stable
12058c2ecf20Sopenharmony_ci *        DMI model names first before we can rely entirely on CONFIG_DMI.
12068c2ecf20Sopenharmony_ci */
12078c2ecf20Sopenharmony_ci
12088c2ecf20Sopenharmony_cistatic int __init abituguru3_detect(void)
12098c2ecf20Sopenharmony_ci{
12108c2ecf20Sopenharmony_ci	/*
12118c2ecf20Sopenharmony_ci	 * See if there is an uguru3 there. An idle uGuru3 will hold 0x00 or
12128c2ecf20Sopenharmony_ci	 * 0x08 at DATA and 0xAC at CMD. Sometimes the uGuru3 will hold 0x05
12138c2ecf20Sopenharmony_ci	 * or 0x55 at CMD instead, why is unknown.
12148c2ecf20Sopenharmony_ci	 */
12158c2ecf20Sopenharmony_ci	u8 data_val = inb_p(ABIT_UGURU3_BASE + ABIT_UGURU3_DATA);
12168c2ecf20Sopenharmony_ci	u8 cmd_val = inb_p(ABIT_UGURU3_BASE + ABIT_UGURU3_CMD);
12178c2ecf20Sopenharmony_ci	if (((data_val == 0x00) || (data_val == 0x08)) &&
12188c2ecf20Sopenharmony_ci			((cmd_val == 0xAC) || (cmd_val == 0x05) ||
12198c2ecf20Sopenharmony_ci			 (cmd_val == 0x55)))
12208c2ecf20Sopenharmony_ci		return 0;
12218c2ecf20Sopenharmony_ci
12228c2ecf20Sopenharmony_ci	ABIT_UGURU3_DEBUG("no Abit uGuru3 found, data = 0x%02X, cmd = "
12238c2ecf20Sopenharmony_ci		"0x%02X\n", (unsigned int)data_val, (unsigned int)cmd_val);
12248c2ecf20Sopenharmony_ci
12258c2ecf20Sopenharmony_ci	if (force) {
12268c2ecf20Sopenharmony_ci		pr_info("Assuming Abit uGuru3 is present because of \"force\" parameter\n");
12278c2ecf20Sopenharmony_ci		return 0;
12288c2ecf20Sopenharmony_ci	}
12298c2ecf20Sopenharmony_ci
12308c2ecf20Sopenharmony_ci	/* No uGuru3 found */
12318c2ecf20Sopenharmony_ci	return -ENODEV;
12328c2ecf20Sopenharmony_ci}
12338c2ecf20Sopenharmony_ci
12348c2ecf20Sopenharmony_cistatic struct platform_device *abituguru3_pdev;
12358c2ecf20Sopenharmony_ci
12368c2ecf20Sopenharmony_cistatic int __init abituguru3_init(void)
12378c2ecf20Sopenharmony_ci{
12388c2ecf20Sopenharmony_ci	struct resource res = { .flags = IORESOURCE_IO };
12398c2ecf20Sopenharmony_ci	int err;
12408c2ecf20Sopenharmony_ci
12418c2ecf20Sopenharmony_ci	/* Attempt DMI detection first */
12428c2ecf20Sopenharmony_ci	err = abituguru3_dmi_detect();
12438c2ecf20Sopenharmony_ci	if (err < 0)
12448c2ecf20Sopenharmony_ci		return err;
12458c2ecf20Sopenharmony_ci
12468c2ecf20Sopenharmony_ci	/*
12478c2ecf20Sopenharmony_ci	 * Fall back to manual detection if there was no exact
12488c2ecf20Sopenharmony_ci	 * board name match, or force was specified.
12498c2ecf20Sopenharmony_ci	 */
12508c2ecf20Sopenharmony_ci	if (err > 0) {
12518c2ecf20Sopenharmony_ci		err = abituguru3_detect();
12528c2ecf20Sopenharmony_ci		if (err)
12538c2ecf20Sopenharmony_ci			return err;
12548c2ecf20Sopenharmony_ci
12558c2ecf20Sopenharmony_ci		pr_warn("this motherboard was not detected using DMI. "
12568c2ecf20Sopenharmony_ci			"Please send the output of \"dmidecode\" to the abituguru3 maintainer (see MAINTAINERS)\n");
12578c2ecf20Sopenharmony_ci	}
12588c2ecf20Sopenharmony_ci
12598c2ecf20Sopenharmony_ci	err = platform_driver_register(&abituguru3_driver);
12608c2ecf20Sopenharmony_ci	if (err)
12618c2ecf20Sopenharmony_ci		goto exit;
12628c2ecf20Sopenharmony_ci
12638c2ecf20Sopenharmony_ci	abituguru3_pdev = platform_device_alloc(ABIT_UGURU3_NAME,
12648c2ecf20Sopenharmony_ci						ABIT_UGURU3_BASE);
12658c2ecf20Sopenharmony_ci	if (!abituguru3_pdev) {
12668c2ecf20Sopenharmony_ci		pr_err("Device allocation failed\n");
12678c2ecf20Sopenharmony_ci		err = -ENOMEM;
12688c2ecf20Sopenharmony_ci		goto exit_driver_unregister;
12698c2ecf20Sopenharmony_ci	}
12708c2ecf20Sopenharmony_ci
12718c2ecf20Sopenharmony_ci	res.start = ABIT_UGURU3_BASE;
12728c2ecf20Sopenharmony_ci	res.end = ABIT_UGURU3_BASE + ABIT_UGURU3_REGION_LENGTH - 1;
12738c2ecf20Sopenharmony_ci	res.name = ABIT_UGURU3_NAME;
12748c2ecf20Sopenharmony_ci
12758c2ecf20Sopenharmony_ci	err = platform_device_add_resources(abituguru3_pdev, &res, 1);
12768c2ecf20Sopenharmony_ci	if (err) {
12778c2ecf20Sopenharmony_ci		pr_err("Device resource addition failed (%d)\n", err);
12788c2ecf20Sopenharmony_ci		goto exit_device_put;
12798c2ecf20Sopenharmony_ci	}
12808c2ecf20Sopenharmony_ci
12818c2ecf20Sopenharmony_ci	err = platform_device_add(abituguru3_pdev);
12828c2ecf20Sopenharmony_ci	if (err) {
12838c2ecf20Sopenharmony_ci		pr_err("Device addition failed (%d)\n", err);
12848c2ecf20Sopenharmony_ci		goto exit_device_put;
12858c2ecf20Sopenharmony_ci	}
12868c2ecf20Sopenharmony_ci
12878c2ecf20Sopenharmony_ci	return 0;
12888c2ecf20Sopenharmony_ci
12898c2ecf20Sopenharmony_ciexit_device_put:
12908c2ecf20Sopenharmony_ci	platform_device_put(abituguru3_pdev);
12918c2ecf20Sopenharmony_ciexit_driver_unregister:
12928c2ecf20Sopenharmony_ci	platform_driver_unregister(&abituguru3_driver);
12938c2ecf20Sopenharmony_ciexit:
12948c2ecf20Sopenharmony_ci	return err;
12958c2ecf20Sopenharmony_ci}
12968c2ecf20Sopenharmony_ci
12978c2ecf20Sopenharmony_cistatic void __exit abituguru3_exit(void)
12988c2ecf20Sopenharmony_ci{
12998c2ecf20Sopenharmony_ci	platform_device_unregister(abituguru3_pdev);
13008c2ecf20Sopenharmony_ci	platform_driver_unregister(&abituguru3_driver);
13018c2ecf20Sopenharmony_ci}
13028c2ecf20Sopenharmony_ci
13038c2ecf20Sopenharmony_ciMODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
13048c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Abit uGuru3 Sensor device");
13058c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
13068c2ecf20Sopenharmony_ci
13078c2ecf20Sopenharmony_cimodule_init(abituguru3_init);
13088c2ecf20Sopenharmony_cimodule_exit(abituguru3_exit);
1309