xref: /openbmc/linux/drivers/hwmon/pc87360.c (revision 26d0dfbb16fcb17d128a79dc70f3020ea6992af0)
174ba9207SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
28d5d45fbSJean Delvare /*
38d5d45fbSJean Delvare  *  pc87360.c - Part of lm_sensors, Linux kernel modules
48d5d45fbSJean Delvare  *              for hardware monitoring
57c81c60fSJean Delvare  *  Copyright (C) 2004, 2007 Jean Delvare <jdelvare@suse.de>
68d5d45fbSJean Delvare  *
78d5d45fbSJean Delvare  *  Copied from smsc47m1.c:
88d5d45fbSJean Delvare  *  Copyright (C) 2002 Mark D. Studebaker <mdsxyz123@yahoo.com>
98d5d45fbSJean Delvare  *
108d5d45fbSJean Delvare  *  Supports the following chips:
118d5d45fbSJean Delvare  *
128d5d45fbSJean Delvare  *  Chip        #vin    #fan    #pwm    #temp   devid
138d5d45fbSJean Delvare  *  PC87360     -       2       2       -       0xE1
148d5d45fbSJean Delvare  *  PC87363     -       2       2       -       0xE8
158d5d45fbSJean Delvare  *  PC87364     -       3       3       -       0xE4
168d5d45fbSJean Delvare  *  PC87365     11      3       3       2       0xE5
178d5d45fbSJean Delvare  *  PC87366     11      3       3       3-4     0xE9
188d5d45fbSJean Delvare  *
198d5d45fbSJean Delvare  *  This driver assumes that no more than one chip is present, and one of
208d5d45fbSJean Delvare  *  the standard Super-I/O addresses is used (0x2E/0x2F or 0x4E/0x4F).
218d5d45fbSJean Delvare  */
228d5d45fbSJean Delvare 
239e991c6fSJoe Perches #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
249e991c6fSJoe Perches 
258d5d45fbSJean Delvare #include <linux/module.h>
268d5d45fbSJean Delvare #include <linux/init.h>
278d5d45fbSJean Delvare #include <linux/slab.h>
288d5d45fbSJean Delvare #include <linux/jiffies.h>
29f641b588SJean Delvare #include <linux/platform_device.h>
30943b0830SMark M. Hoffman #include <linux/hwmon.h>
31f0986bd8SJim Cromie #include <linux/hwmon-sysfs.h>
32303760b4SJean Delvare #include <linux/hwmon-vid.h>
33943b0830SMark M. Hoffman #include <linux/err.h>
349a61bf63SIngo Molnar #include <linux/mutex.h>
35b9acb64aSJean Delvare #include <linux/acpi.h>
366055fae8SH Hartley Sweeten #include <linux/io.h>
378d5d45fbSJean Delvare 
3802e05005SUwe Kleine-König #define DRIVER_NAME "pc87360"
3902e05005SUwe Kleine-König 
40070affa8SUwe Kleine-König /* (temp & vin) channel conversion status register flags (pdf sec.11.5.12) */
41070affa8SUwe Kleine-König #define CHAN_CNVRTD	0x80	/* new data ready */
42070affa8SUwe Kleine-König #define CHAN_ENA	0x01	/* enabled channel (temp or vin) */
43070affa8SUwe Kleine-König #define CHAN_ALM_ENA	0x10	/* propagate to alarms-reg ?? (chk val!) */
44070affa8SUwe Kleine-König #define CHAN_READY	(CHAN_ENA|CHAN_CNVRTD) /* sample ready mask */
45070affa8SUwe Kleine-König 
46070affa8SUwe Kleine-König #define TEMP_OTS_OE	0x20	/* OTS Output Enable */
47070affa8SUwe Kleine-König #define VIN_RW1C_MASK	(CHAN_READY|CHAN_ALM_MAX|CHAN_ALM_MIN)   /* 0x87 */
48070affa8SUwe Kleine-König #define TEMP_RW1C_MASK	(VIN_RW1C_MASK|TEMP_ALM_CRIT|TEMP_FAULT) /* 0xCF */
49070affa8SUwe Kleine-König 
508d5d45fbSJean Delvare static u8 devid;
51f641b588SJean Delvare static struct platform_device *pdev;
522d8672c5SJean Delvare static unsigned short extra_isa[3];
538d5d45fbSJean Delvare static u8 confreg[4];
548d5d45fbSJean Delvare 
558d5d45fbSJean Delvare static int init = 1;
568d5d45fbSJean Delvare module_param(init, int, 0);
578d5d45fbSJean Delvare MODULE_PARM_DESC(init,
588d5d45fbSJean Delvare "Chip initialization level:\n"
598d5d45fbSJean Delvare " 0: None\n"
608d5d45fbSJean Delvare "*1: Forcibly enable internal voltage and temperature channels, except in9\n"
618d5d45fbSJean Delvare " 2: Forcibly enable all voltage and temperature channels, except in9\n"
628d5d45fbSJean Delvare " 3: Forcibly enable all voltage and temperature channels, including in9");
638d5d45fbSJean Delvare 
6467b671bcSJean Delvare static unsigned short force_id;
6567b671bcSJean Delvare module_param(force_id, ushort, 0);
6667b671bcSJean Delvare MODULE_PARM_DESC(force_id, "Override the detected device ID");
6767b671bcSJean Delvare 
688d5d45fbSJean Delvare /*
698d5d45fbSJean Delvare  * Super-I/O registers and operations
708d5d45fbSJean Delvare  */
718d5d45fbSJean Delvare 
728d5d45fbSJean Delvare #define DEV	0x07	/* Register: Logical device select */
738d5d45fbSJean Delvare #define DEVID	0x20	/* Register: Device ID */
748d5d45fbSJean Delvare #define ACT	0x30	/* Register: Device activation */
758d5d45fbSJean Delvare #define BASE	0x60	/* Register: Base address */
768d5d45fbSJean Delvare 
778d5d45fbSJean Delvare #define FSCM	0x09	/* Logical device: fans */
788d5d45fbSJean Delvare #define VLM	0x0d	/* Logical device: voltages */
798d5d45fbSJean Delvare #define TMS	0x0e	/* Logical device: temperatures */
802a32ec25SJim Cromie #define LDNI_MAX 3
812a32ec25SJim Cromie static const u8 logdev[LDNI_MAX] = { FSCM, VLM, TMS };
828d5d45fbSJean Delvare 
838d5d45fbSJean Delvare #define LD_FAN		0
848d5d45fbSJean Delvare #define LD_IN		1
858d5d45fbSJean Delvare #define LD_TEMP		2
868d5d45fbSJean Delvare 
superio_outb(int sioaddr,int reg,int val)878d5d45fbSJean Delvare static inline void superio_outb(int sioaddr, int reg, int val)
888d5d45fbSJean Delvare {
898d5d45fbSJean Delvare 	outb(reg, sioaddr);
908d5d45fbSJean Delvare 	outb(val, sioaddr + 1);
918d5d45fbSJean Delvare }
928d5d45fbSJean Delvare 
superio_inb(int sioaddr,int reg)938d5d45fbSJean Delvare static inline int superio_inb(int sioaddr, int reg)
948d5d45fbSJean Delvare {
958d5d45fbSJean Delvare 	outb(reg, sioaddr);
968d5d45fbSJean Delvare 	return inb(sioaddr + 1);
978d5d45fbSJean Delvare }
988d5d45fbSJean Delvare 
superio_exit(int sioaddr)998d5d45fbSJean Delvare static inline void superio_exit(int sioaddr)
1008d5d45fbSJean Delvare {
1018d5d45fbSJean Delvare 	outb(0x02, sioaddr);
1028d5d45fbSJean Delvare 	outb(0x02, sioaddr + 1);
1038d5d45fbSJean Delvare }
1048d5d45fbSJean Delvare 
1058d5d45fbSJean Delvare /*
1068d5d45fbSJean Delvare  * Logical devices
1078d5d45fbSJean Delvare  */
1088d5d45fbSJean Delvare 
1098d5d45fbSJean Delvare #define PC87360_EXTENT		0x10
1108d5d45fbSJean Delvare #define PC87365_REG_BANK	0x09
1118d5d45fbSJean Delvare #define NO_BANK			0xff
1128d5d45fbSJean Delvare 
1138d5d45fbSJean Delvare /*
1148d5d45fbSJean Delvare  * Fan registers and conversions
1158d5d45fbSJean Delvare  */
1168d5d45fbSJean Delvare 
1178d5d45fbSJean Delvare /* nr has to be 0 or 1 (PC87360/87363) or 2 (PC87364/87365/87366) */
1188d5d45fbSJean Delvare #define PC87360_REG_PRESCALE(nr)	(0x00 + 2 * (nr))
1198d5d45fbSJean Delvare #define PC87360_REG_PWM(nr)		(0x01 + 2 * (nr))
1208d5d45fbSJean Delvare #define PC87360_REG_FAN_MIN(nr)		(0x06 + 3 * (nr))
1218d5d45fbSJean Delvare #define PC87360_REG_FAN(nr)		(0x07 + 3 * (nr))
1228d5d45fbSJean Delvare #define PC87360_REG_FAN_STATUS(nr)	(0x08 + 3 * (nr))
1238d5d45fbSJean Delvare 
1248d5d45fbSJean Delvare #define FAN_FROM_REG(val, div)		((val) == 0 ? 0 : \
1258d5d45fbSJean Delvare 					 480000 / ((val) * (div)))
1268d5d45fbSJean Delvare #define FAN_TO_REG(val, div)		((val) <= 100 ? 0 : \
1278d5d45fbSJean Delvare 					 480000 / ((val) * (div)))
128449a7a07SGuenter Roeck #define FAN_DIV_FROM_REG(val)		(1 << (((val) >> 5) & 0x03))
1298d5d45fbSJean Delvare #define FAN_STATUS_FROM_REG(val)	((val) & 0x07)
1308d5d45fbSJean Delvare 
131449a7a07SGuenter Roeck #define FAN_CONFIG_MONITOR(val, nr)	(((val) >> (2 + (nr) * 3)) & 1)
132449a7a07SGuenter Roeck #define FAN_CONFIG_CONTROL(val, nr)	(((val) >> (3 + (nr) * 3)) & 1)
133449a7a07SGuenter Roeck #define FAN_CONFIG_INVERT(val, nr)	(((val) >> (4 + (nr) * 3)) & 1)
1348d5d45fbSJean Delvare 
1358d5d45fbSJean Delvare #define PWM_FROM_REG(val, inv)		((inv) ? 255 - (val) : (val))
PWM_TO_REG(int val,int inv)1368d5d45fbSJean Delvare static inline u8 PWM_TO_REG(int val, int inv)
1378d5d45fbSJean Delvare {
1388d5d45fbSJean Delvare 	if (inv)
1398d5d45fbSJean Delvare 		val = 255 - val;
1408d5d45fbSJean Delvare 	if (val < 0)
1418d5d45fbSJean Delvare 		return 0;
1428d5d45fbSJean Delvare 	if (val > 255)
1438d5d45fbSJean Delvare 		return 255;
1448d5d45fbSJean Delvare 	return val;
1458d5d45fbSJean Delvare }
1468d5d45fbSJean Delvare 
1478d5d45fbSJean Delvare /*
1488d5d45fbSJean Delvare  * Voltage registers and conversions
1498d5d45fbSJean Delvare  */
1508d5d45fbSJean Delvare 
1518d5d45fbSJean Delvare #define PC87365_REG_IN_CONVRATE		0x07
1528d5d45fbSJean Delvare #define PC87365_REG_IN_CONFIG		0x08
1538d5d45fbSJean Delvare #define PC87365_REG_IN			0x0B
1548d5d45fbSJean Delvare #define PC87365_REG_IN_MIN		0x0D
1558d5d45fbSJean Delvare #define PC87365_REG_IN_MAX		0x0C
1568d5d45fbSJean Delvare #define PC87365_REG_IN_STATUS		0x0A
1578d5d45fbSJean Delvare #define PC87365_REG_IN_ALARMS1		0x00
1588d5d45fbSJean Delvare #define PC87365_REG_IN_ALARMS2		0x01
1598d5d45fbSJean Delvare #define PC87365_REG_VID			0x06
1608d5d45fbSJean Delvare 
1618d5d45fbSJean Delvare #define IN_FROM_REG(val, ref)		(((val) * (ref) + 128) / 256)
1628d5d45fbSJean Delvare #define IN_TO_REG(val, ref)		((val) < 0 ? 0 : \
1638d5d45fbSJean Delvare 					 (val) * 256 >= (ref) * 255 ? 255 : \
1648d5d45fbSJean Delvare 					 ((val) * 256 + (ref) / 2) / (ref))
1658d5d45fbSJean Delvare 
1668d5d45fbSJean Delvare /*
1678d5d45fbSJean Delvare  * Temperature registers and conversions
1688d5d45fbSJean Delvare  */
1698d5d45fbSJean Delvare 
1708d5d45fbSJean Delvare #define PC87365_REG_TEMP_CONFIG		0x08
1718d5d45fbSJean Delvare #define PC87365_REG_TEMP		0x0B
1728d5d45fbSJean Delvare #define PC87365_REG_TEMP_MIN		0x0D
1738d5d45fbSJean Delvare #define PC87365_REG_TEMP_MAX		0x0C
1748d5d45fbSJean Delvare #define PC87365_REG_TEMP_CRIT		0x0E
1758d5d45fbSJean Delvare #define PC87365_REG_TEMP_STATUS		0x0A
1768d5d45fbSJean Delvare #define PC87365_REG_TEMP_ALARMS		0x00
1778d5d45fbSJean Delvare 
1788d5d45fbSJean Delvare #define TEMP_FROM_REG(val)		((val) * 1000)
1798d5d45fbSJean Delvare #define TEMP_TO_REG(val)		((val) < -55000 ? -55 : \
1808d5d45fbSJean Delvare 					 (val) > 127000 ? 127 : \
1818d5d45fbSJean Delvare 					 (val) < 0 ? ((val) - 500) / 1000 : \
1828d5d45fbSJean Delvare 					 ((val) + 500) / 1000)
1838d5d45fbSJean Delvare 
1848d5d45fbSJean Delvare /*
185f641b588SJean Delvare  * Device data
1868d5d45fbSJean Delvare  */
1878d5d45fbSJean Delvare 
1888d5d45fbSJean Delvare struct pc87360_data {
189f641b588SJean Delvare 	const char *name;
1901beeffe4STony Jones 	struct device *hwmon_dev;
1919a61bf63SIngo Molnar 	struct mutex lock;
1929a61bf63SIngo Molnar 	struct mutex update_lock;
193952a11caSPaul Fertser 	bool valid;		/* true if following fields are valid */
1948d5d45fbSJean Delvare 	unsigned long last_updated;	/* In jiffies */
1958d5d45fbSJean Delvare 
1968d5d45fbSJean Delvare 	int address[3];
1978d5d45fbSJean Delvare 
1988d5d45fbSJean Delvare 	u8 fannr, innr, tempnr;
1998d5d45fbSJean Delvare 
2008d5d45fbSJean Delvare 	u8 fan[3];		/* Register value */
2018d5d45fbSJean Delvare 	u8 fan_min[3];		/* Register value */
2028d5d45fbSJean Delvare 	u8 fan_status[3];	/* Register value */
2038d5d45fbSJean Delvare 	u8 pwm[3];		/* Register value */
2048d5d45fbSJean Delvare 	u16 fan_conf;		/* Configuration register values, combined */
2058d5d45fbSJean Delvare 
2068d5d45fbSJean Delvare 	u16 in_vref;		/* 1 mV/bit */
2078d5d45fbSJean Delvare 	u8 in[14];		/* Register value */
2088d5d45fbSJean Delvare 	u8 in_min[14];		/* Register value */
2098d5d45fbSJean Delvare 	u8 in_max[14];		/* Register value */
2108d5d45fbSJean Delvare 	u8 in_crit[3];		/* Register value */
2118d5d45fbSJean Delvare 	u8 in_status[14];	/* Register value */
2128d5d45fbSJean Delvare 	u16 in_alarms;		/* Register values, combined, masked */
2138d5d45fbSJean Delvare 	u8 vid_conf;		/* Configuration register value */
2148d5d45fbSJean Delvare 	u8 vrm;
2158d5d45fbSJean Delvare 	u8 vid;			/* Register value */
2168d5d45fbSJean Delvare 
2178d5d45fbSJean Delvare 	s8 temp[3];		/* Register value */
2188d5d45fbSJean Delvare 	s8 temp_min[3];		/* Register value */
2198d5d45fbSJean Delvare 	s8 temp_max[3];		/* Register value */
2208d5d45fbSJean Delvare 	s8 temp_crit[3];	/* Register value */
2218d5d45fbSJean Delvare 	u8 temp_status[3];	/* Register value */
2228d5d45fbSJean Delvare 	u8 temp_alarms;		/* Register value, masked */
2238d5d45fbSJean Delvare };
2248d5d45fbSJean Delvare 
2258d5d45fbSJean Delvare /*
226070affa8SUwe Kleine-König  * ldi is the logical device index
227070affa8SUwe Kleine-König  * bank is for voltages and temperatures only
2288d5d45fbSJean Delvare  */
pc87360_read_value(struct pc87360_data * data,u8 ldi,u8 bank,u8 reg)2298d5d45fbSJean Delvare static int pc87360_read_value(struct pc87360_data *data, u8 ldi, u8 bank,
230070affa8SUwe Kleine-König 			      u8 reg)
231070affa8SUwe Kleine-König {
232070affa8SUwe Kleine-König 	int res;
233070affa8SUwe Kleine-König 
234070affa8SUwe Kleine-König 	mutex_lock(&(data->lock));
235070affa8SUwe Kleine-König 	if (bank != NO_BANK)
236070affa8SUwe Kleine-König 		outb_p(bank, data->address[ldi] + PC87365_REG_BANK);
237070affa8SUwe Kleine-König 	res = inb_p(data->address[ldi] + reg);
238070affa8SUwe Kleine-König 	mutex_unlock(&(data->lock));
239070affa8SUwe Kleine-König 
240070affa8SUwe Kleine-König 	return res;
241070affa8SUwe Kleine-König }
242070affa8SUwe Kleine-König 
pc87360_write_value(struct pc87360_data * data,u8 ldi,u8 bank,u8 reg,u8 value)2438d5d45fbSJean Delvare static void pc87360_write_value(struct pc87360_data *data, u8 ldi, u8 bank,
244070affa8SUwe Kleine-König 				u8 reg, u8 value)
245070affa8SUwe Kleine-König {
246070affa8SUwe Kleine-König 	mutex_lock(&(data->lock));
247070affa8SUwe Kleine-König 	if (bank != NO_BANK)
248070affa8SUwe Kleine-König 		outb_p(bank, data->address[ldi] + PC87365_REG_BANK);
249070affa8SUwe Kleine-König 	outb_p(value, data->address[ldi] + reg);
250070affa8SUwe Kleine-König 	mutex_unlock(&(data->lock));
251070affa8SUwe Kleine-König }
2528d5d45fbSJean Delvare 
pc87360_autodiv(struct device * dev,int nr)253070affa8SUwe Kleine-König static void pc87360_autodiv(struct device *dev, int nr)
254f0986bd8SJim Cromie {
255f641b588SJean Delvare 	struct pc87360_data *data = dev_get_drvdata(dev);
256070affa8SUwe Kleine-König 	u8 old_min = data->fan_min[nr];
257449a7a07SGuenter Roeck 
258070affa8SUwe Kleine-König 	/* Increase clock divider if needed and possible */
259070affa8SUwe Kleine-König 	if ((data->fan_status[nr] & 0x04) /* overflow flag */
260070affa8SUwe Kleine-König 	 || (data->fan[nr] >= 224)) { /* next to overflow */
261070affa8SUwe Kleine-König 		if ((data->fan_status[nr] & 0x60) != 0x60) {
262070affa8SUwe Kleine-König 			data->fan_status[nr] += 0x20;
263070affa8SUwe Kleine-König 			data->fan_min[nr] >>= 1;
264070affa8SUwe Kleine-König 			data->fan[nr] >>= 1;
265070affa8SUwe Kleine-König 			dev_dbg(dev,
266070affa8SUwe Kleine-König 				"Increasing clock divider to %d for fan %d\n",
267070affa8SUwe Kleine-König 				FAN_DIV_FROM_REG(data->fan_status[nr]), nr + 1);
268070affa8SUwe Kleine-König 		}
269070affa8SUwe Kleine-König 	} else {
270070affa8SUwe Kleine-König 		/* Decrease clock divider if possible */
271070affa8SUwe Kleine-König 		while (!(data->fan_min[nr] & 0x80) /* min "nails" divider */
272070affa8SUwe Kleine-König 		 && data->fan[nr] < 85 /* bad accuracy */
273070affa8SUwe Kleine-König 		 && (data->fan_status[nr] & 0x60) != 0x00) {
274070affa8SUwe Kleine-König 			data->fan_status[nr] -= 0x20;
275070affa8SUwe Kleine-König 			data->fan_min[nr] <<= 1;
276070affa8SUwe Kleine-König 			data->fan[nr] <<= 1;
277070affa8SUwe Kleine-König 			dev_dbg(dev,
278070affa8SUwe Kleine-König 				"Decreasing clock divider to %d for fan %d\n",
279070affa8SUwe Kleine-König 				FAN_DIV_FROM_REG(data->fan_status[nr]),
280070affa8SUwe Kleine-König 				nr + 1);
281070affa8SUwe Kleine-König 		}
282070affa8SUwe Kleine-König 	}
283070affa8SUwe Kleine-König 
284070affa8SUwe Kleine-König 	/* Write new fan min if it changed */
285070affa8SUwe Kleine-König 	if (old_min != data->fan_min[nr]) {
286070affa8SUwe Kleine-König 		pc87360_write_value(data, LD_FAN, NO_BANK,
287070affa8SUwe Kleine-König 				    PC87360_REG_FAN_MIN(nr),
288070affa8SUwe Kleine-König 				    data->fan_min[nr]);
289070affa8SUwe Kleine-König 	}
290070affa8SUwe Kleine-König }
291070affa8SUwe Kleine-König 
pc87360_update_device(struct device * dev)292070affa8SUwe Kleine-König static struct pc87360_data *pc87360_update_device(struct device *dev)
293070affa8SUwe Kleine-König {
294070affa8SUwe Kleine-König 	struct pc87360_data *data = dev_get_drvdata(dev);
295070affa8SUwe Kleine-König 	u8 i;
29611be27eaSJim Cromie 
2979a61bf63SIngo Molnar 	mutex_lock(&data->update_lock);
29811be27eaSJim Cromie 
299070affa8SUwe Kleine-König 	if (time_after(jiffies, data->last_updated + HZ * 2) || !data->valid) {
300070affa8SUwe Kleine-König 		dev_dbg(dev, "Data update\n");
301070affa8SUwe Kleine-König 
302070affa8SUwe Kleine-König 		/* Fans */
303070affa8SUwe Kleine-König 		for (i = 0; i < data->fannr; i++) {
304070affa8SUwe Kleine-König 			if (FAN_CONFIG_MONITOR(data->fan_conf, i)) {
305070affa8SUwe Kleine-König 				data->fan_status[i] =
306070affa8SUwe Kleine-König 					pc87360_read_value(data, LD_FAN,
307070affa8SUwe Kleine-König 					NO_BANK, PC87360_REG_FAN_STATUS(i));
308070affa8SUwe Kleine-König 				data->fan[i] = pc87360_read_value(data, LD_FAN,
309070affa8SUwe Kleine-König 					       NO_BANK, PC87360_REG_FAN(i));
310070affa8SUwe Kleine-König 				data->fan_min[i] = pc87360_read_value(data,
311070affa8SUwe Kleine-König 						   LD_FAN, NO_BANK,
312070affa8SUwe Kleine-König 						   PC87360_REG_FAN_MIN(i));
313070affa8SUwe Kleine-König 				/* Change clock divider if needed */
314070affa8SUwe Kleine-König 				pc87360_autodiv(dev, i);
315070affa8SUwe Kleine-König 				/* Clear bits and write new divider */
316070affa8SUwe Kleine-König 				pc87360_write_value(data, LD_FAN, NO_BANK,
317070affa8SUwe Kleine-König 						    PC87360_REG_FAN_STATUS(i),
318070affa8SUwe Kleine-König 						    data->fan_status[i]);
31911be27eaSJim Cromie 			}
320070affa8SUwe Kleine-König 			if (FAN_CONFIG_CONTROL(data->fan_conf, i))
321070affa8SUwe Kleine-König 				data->pwm[i] = pc87360_read_value(data, LD_FAN,
322070affa8SUwe Kleine-König 					       NO_BANK, PC87360_REG_PWM(i));
323070affa8SUwe Kleine-König 		}
32411be27eaSJim Cromie 
325070affa8SUwe Kleine-König 		/* Voltages */
326*cbd7ab7dSKees Cook 		/*
327*cbd7ab7dSKees Cook 		 * The min() below does not have any practical meaning and is
328*cbd7ab7dSKees Cook 		 * only needed to silence a warning observed with gcc 12+.
329*cbd7ab7dSKees Cook 		 */
330*cbd7ab7dSKees Cook 		for (i = 0; i < min(data->innr, ARRAY_SIZE(data->in)); i++) {
331070affa8SUwe Kleine-König 			data->in_status[i] = pc87360_read_value(data, LD_IN, i,
332070affa8SUwe Kleine-König 					     PC87365_REG_IN_STATUS);
333070affa8SUwe Kleine-König 			/* Clear bits */
334070affa8SUwe Kleine-König 			pc87360_write_value(data, LD_IN, i,
335070affa8SUwe Kleine-König 					    PC87365_REG_IN_STATUS,
336070affa8SUwe Kleine-König 					    data->in_status[i]);
337070affa8SUwe Kleine-König 			if ((data->in_status[i] & CHAN_READY) == CHAN_READY) {
338070affa8SUwe Kleine-König 				data->in[i] = pc87360_read_value(data, LD_IN,
339070affa8SUwe Kleine-König 					      i, PC87365_REG_IN);
340070affa8SUwe Kleine-König 			}
341070affa8SUwe Kleine-König 			if (data->in_status[i] & CHAN_ENA) {
342070affa8SUwe Kleine-König 				data->in_min[i] = pc87360_read_value(data,
343070affa8SUwe Kleine-König 						  LD_IN, i,
344070affa8SUwe Kleine-König 						  PC87365_REG_IN_MIN);
345070affa8SUwe Kleine-König 				data->in_max[i] = pc87360_read_value(data,
346070affa8SUwe Kleine-König 						  LD_IN, i,
347070affa8SUwe Kleine-König 						  PC87365_REG_IN_MAX);
348070affa8SUwe Kleine-König 				if (i >= 11)
349070affa8SUwe Kleine-König 					data->in_crit[i-11] =
350070affa8SUwe Kleine-König 						pc87360_read_value(data, LD_IN,
351070affa8SUwe Kleine-König 						i, PC87365_REG_TEMP_CRIT);
352070affa8SUwe Kleine-König 			}
353070affa8SUwe Kleine-König 		}
354070affa8SUwe Kleine-König 		if (data->innr) {
355070affa8SUwe Kleine-König 			data->in_alarms = pc87360_read_value(data, LD_IN,
356070affa8SUwe Kleine-König 					  NO_BANK, PC87365_REG_IN_ALARMS1)
357070affa8SUwe Kleine-König 					| ((pc87360_read_value(data, LD_IN,
358070affa8SUwe Kleine-König 					    NO_BANK, PC87365_REG_IN_ALARMS2)
359070affa8SUwe Kleine-König 					    & 0x07) << 8);
360070affa8SUwe Kleine-König 			data->vid = (data->vid_conf & 0xE0) ?
361070affa8SUwe Kleine-König 				    pc87360_read_value(data, LD_IN,
362070affa8SUwe Kleine-König 				    NO_BANK, PC87365_REG_VID) : 0x1F;
363070affa8SUwe Kleine-König 		}
364070affa8SUwe Kleine-König 
365070affa8SUwe Kleine-König 		/* Temperatures */
366070affa8SUwe Kleine-König 		for (i = 0; i < data->tempnr; i++) {
367070affa8SUwe Kleine-König 			data->temp_status[i] = pc87360_read_value(data,
368070affa8SUwe Kleine-König 					       LD_TEMP, i,
369070affa8SUwe Kleine-König 					       PC87365_REG_TEMP_STATUS);
370070affa8SUwe Kleine-König 			/* Clear bits */
371070affa8SUwe Kleine-König 			pc87360_write_value(data, LD_TEMP, i,
372070affa8SUwe Kleine-König 					    PC87365_REG_TEMP_STATUS,
373070affa8SUwe Kleine-König 					    data->temp_status[i]);
374070affa8SUwe Kleine-König 			if ((data->temp_status[i] & CHAN_READY) == CHAN_READY) {
375070affa8SUwe Kleine-König 				data->temp[i] = pc87360_read_value(data,
376070affa8SUwe Kleine-König 						LD_TEMP, i,
377070affa8SUwe Kleine-König 						PC87365_REG_TEMP);
378070affa8SUwe Kleine-König 			}
379070affa8SUwe Kleine-König 			if (data->temp_status[i] & CHAN_ENA) {
380070affa8SUwe Kleine-König 				data->temp_min[i] = pc87360_read_value(data,
381070affa8SUwe Kleine-König 						    LD_TEMP, i,
382070affa8SUwe Kleine-König 						    PC87365_REG_TEMP_MIN);
383070affa8SUwe Kleine-König 				data->temp_max[i] = pc87360_read_value(data,
384070affa8SUwe Kleine-König 						    LD_TEMP, i,
385070affa8SUwe Kleine-König 						    PC87365_REG_TEMP_MAX);
386070affa8SUwe Kleine-König 				data->temp_crit[i] = pc87360_read_value(data,
387070affa8SUwe Kleine-König 						     LD_TEMP, i,
388070affa8SUwe Kleine-König 						     PC87365_REG_TEMP_CRIT);
389070affa8SUwe Kleine-König 			}
390070affa8SUwe Kleine-König 		}
391070affa8SUwe Kleine-König 		if (data->tempnr) {
392070affa8SUwe Kleine-König 			data->temp_alarms = pc87360_read_value(data, LD_TEMP,
393070affa8SUwe Kleine-König 					    NO_BANK, PC87365_REG_TEMP_ALARMS)
394070affa8SUwe Kleine-König 					    & 0x3F;
395070affa8SUwe Kleine-König 		}
396070affa8SUwe Kleine-König 
397070affa8SUwe Kleine-König 		data->last_updated = jiffies;
398070affa8SUwe Kleine-König 		data->valid = true;
399070affa8SUwe Kleine-König 	}
400070affa8SUwe Kleine-König 
4019a61bf63SIngo Molnar 	mutex_unlock(&data->update_lock);
40211be27eaSJim Cromie 
403070affa8SUwe Kleine-König 	return data;
404f0986bd8SJim Cromie }
405f0986bd8SJim Cromie 
in_input_show(struct device * dev,struct device_attribute * devattr,char * buf)406eba42d30SGuenter Roeck static ssize_t in_input_show(struct device *dev,
407449a7a07SGuenter Roeck 			     struct device_attribute *devattr, char *buf)
408f0986bd8SJim Cromie {
409f0986bd8SJim Cromie 	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
410f0986bd8SJim Cromie 	struct pc87360_data *data = pc87360_update_device(dev);
411f0986bd8SJim Cromie 	return sprintf(buf, "%u\n", IN_FROM_REG(data->in[attr->index],
412f0986bd8SJim Cromie 		       data->in_vref));
413f0986bd8SJim Cromie }
414070affa8SUwe Kleine-König 
415070affa8SUwe Kleine-König static struct sensor_device_attribute in_input[] = {
416070affa8SUwe Kleine-König 	SENSOR_ATTR_RO(in0_input, in_input, 0),
417070affa8SUwe Kleine-König 	SENSOR_ATTR_RO(in1_input, in_input, 1),
418070affa8SUwe Kleine-König 	SENSOR_ATTR_RO(in2_input, in_input, 2),
419070affa8SUwe Kleine-König 	SENSOR_ATTR_RO(in3_input, in_input, 3),
420070affa8SUwe Kleine-König 	SENSOR_ATTR_RO(in4_input, in_input, 4),
421070affa8SUwe Kleine-König 	SENSOR_ATTR_RO(in5_input, in_input, 5),
422070affa8SUwe Kleine-König 	SENSOR_ATTR_RO(in6_input, in_input, 6),
423070affa8SUwe Kleine-König 	SENSOR_ATTR_RO(in7_input, in_input, 7),
424070affa8SUwe Kleine-König 	SENSOR_ATTR_RO(in8_input, in_input, 8),
425070affa8SUwe Kleine-König 	SENSOR_ATTR_RO(in9_input, in_input, 9),
426070affa8SUwe Kleine-König 	SENSOR_ATTR_RO(in10_input, in_input, 10),
427070affa8SUwe Kleine-König };
428070affa8SUwe Kleine-König 
in_status_show(struct device * dev,struct device_attribute * devattr,char * buf)429070affa8SUwe Kleine-König static ssize_t in_status_show(struct device *dev,
430070affa8SUwe Kleine-König 			      struct device_attribute *devattr, char *buf)
431070affa8SUwe Kleine-König {
432070affa8SUwe Kleine-König 	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
433070affa8SUwe Kleine-König 	struct pc87360_data *data = pc87360_update_device(dev);
434070affa8SUwe Kleine-König 	return sprintf(buf, "%u\n", data->in_status[attr->index]);
435070affa8SUwe Kleine-König }
436070affa8SUwe Kleine-König 
437070affa8SUwe Kleine-König static struct sensor_device_attribute in_status[] = {
438070affa8SUwe Kleine-König 	SENSOR_ATTR_RO(in0_status, in_status, 0),
439070affa8SUwe Kleine-König 	SENSOR_ATTR_RO(in1_status, in_status, 1),
440070affa8SUwe Kleine-König 	SENSOR_ATTR_RO(in2_status, in_status, 2),
441070affa8SUwe Kleine-König 	SENSOR_ATTR_RO(in3_status, in_status, 3),
442070affa8SUwe Kleine-König 	SENSOR_ATTR_RO(in4_status, in_status, 4),
443070affa8SUwe Kleine-König 	SENSOR_ATTR_RO(in5_status, in_status, 5),
444070affa8SUwe Kleine-König 	SENSOR_ATTR_RO(in6_status, in_status, 6),
445070affa8SUwe Kleine-König 	SENSOR_ATTR_RO(in7_status, in_status, 7),
446070affa8SUwe Kleine-König 	SENSOR_ATTR_RO(in8_status, in_status, 8),
447070affa8SUwe Kleine-König 	SENSOR_ATTR_RO(in9_status, in_status, 9),
448070affa8SUwe Kleine-König 	SENSOR_ATTR_RO(in10_status, in_status, 10),
449070affa8SUwe Kleine-König };
450070affa8SUwe Kleine-König 
in_min_show(struct device * dev,struct device_attribute * devattr,char * buf)451eba42d30SGuenter Roeck static ssize_t in_min_show(struct device *dev,
452449a7a07SGuenter Roeck 			   struct device_attribute *devattr, char *buf)
453f0986bd8SJim Cromie {
454f0986bd8SJim Cromie 	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
455f0986bd8SJim Cromie 	struct pc87360_data *data = pc87360_update_device(dev);
456f0986bd8SJim Cromie 	return sprintf(buf, "%u\n", IN_FROM_REG(data->in_min[attr->index],
457f0986bd8SJim Cromie 		       data->in_vref));
458f0986bd8SJim Cromie }
459070affa8SUwe Kleine-König 
in_min_store(struct device * dev,struct device_attribute * devattr,const char * buf,size_t count)460eba42d30SGuenter Roeck static ssize_t in_min_store(struct device *dev,
461eba42d30SGuenter Roeck 			    struct device_attribute *devattr, const char *buf,
462eba42d30SGuenter Roeck 			    size_t count)
463f0986bd8SJim Cromie {
464f0986bd8SJim Cromie 	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
465f641b588SJean Delvare 	struct pc87360_data *data = dev_get_drvdata(dev);
466449a7a07SGuenter Roeck 	long val;
467449a7a07SGuenter Roeck 	int err;
468449a7a07SGuenter Roeck 
469449a7a07SGuenter Roeck 	err = kstrtol(buf, 10, &val);
470449a7a07SGuenter Roeck 	if (err)
471449a7a07SGuenter Roeck 		return err;
472f0986bd8SJim Cromie 
4739a61bf63SIngo Molnar 	mutex_lock(&data->update_lock);
474f0986bd8SJim Cromie 	data->in_min[attr->index] = IN_TO_REG(val, data->in_vref);
475f0986bd8SJim Cromie 	pc87360_write_value(data, LD_IN, attr->index, PC87365_REG_IN_MIN,
476f0986bd8SJim Cromie 			    data->in_min[attr->index]);
4779a61bf63SIngo Molnar 	mutex_unlock(&data->update_lock);
478f0986bd8SJim Cromie 	return count;
479f0986bd8SJim Cromie }
480070affa8SUwe Kleine-König 
481070affa8SUwe Kleine-König static struct sensor_device_attribute in_min[] = {
482070affa8SUwe Kleine-König 	SENSOR_ATTR_RW(in0_min, in_min, 0),
483070affa8SUwe Kleine-König 	SENSOR_ATTR_RW(in1_min, in_min, 1),
484070affa8SUwe Kleine-König 	SENSOR_ATTR_RW(in2_min, in_min, 2),
485070affa8SUwe Kleine-König 	SENSOR_ATTR_RW(in3_min, in_min, 3),
486070affa8SUwe Kleine-König 	SENSOR_ATTR_RW(in4_min, in_min, 4),
487070affa8SUwe Kleine-König 	SENSOR_ATTR_RW(in5_min, in_min, 5),
488070affa8SUwe Kleine-König 	SENSOR_ATTR_RW(in6_min, in_min, 6),
489070affa8SUwe Kleine-König 	SENSOR_ATTR_RW(in7_min, in_min, 7),
490070affa8SUwe Kleine-König 	SENSOR_ATTR_RW(in8_min, in_min, 8),
491070affa8SUwe Kleine-König 	SENSOR_ATTR_RW(in9_min, in_min, 9),
492070affa8SUwe Kleine-König 	SENSOR_ATTR_RW(in10_min, in_min, 10),
493070affa8SUwe Kleine-König };
494070affa8SUwe Kleine-König 
in_max_show(struct device * dev,struct device_attribute * devattr,char * buf)495070affa8SUwe Kleine-König static ssize_t in_max_show(struct device *dev,
496070affa8SUwe Kleine-König 			   struct device_attribute *devattr, char *buf)
497070affa8SUwe Kleine-König {
498070affa8SUwe Kleine-König 	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
499070affa8SUwe Kleine-König 	struct pc87360_data *data = pc87360_update_device(dev);
500070affa8SUwe Kleine-König 	return sprintf(buf, "%u\n", IN_FROM_REG(data->in_max[attr->index],
501070affa8SUwe Kleine-König 		       data->in_vref));
502070affa8SUwe Kleine-König }
503070affa8SUwe Kleine-König 
in_max_store(struct device * dev,struct device_attribute * devattr,const char * buf,size_t count)504eba42d30SGuenter Roeck static ssize_t in_max_store(struct device *dev,
505eba42d30SGuenter Roeck 			    struct device_attribute *devattr, const char *buf,
506eba42d30SGuenter Roeck 			    size_t count)
507f0986bd8SJim Cromie {
508f0986bd8SJim Cromie 	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
509f641b588SJean Delvare 	struct pc87360_data *data = dev_get_drvdata(dev);
510449a7a07SGuenter Roeck 	long val;
511449a7a07SGuenter Roeck 	int err;
512449a7a07SGuenter Roeck 
513449a7a07SGuenter Roeck 	err = kstrtol(buf, 10, &val);
514449a7a07SGuenter Roeck 	if (err)
515449a7a07SGuenter Roeck 		return err;
516f0986bd8SJim Cromie 
5179a61bf63SIngo Molnar 	mutex_lock(&data->update_lock);
518f0986bd8SJim Cromie 	data->in_max[attr->index] = IN_TO_REG(val,
519f0986bd8SJim Cromie 			       data->in_vref);
520f0986bd8SJim Cromie 	pc87360_write_value(data, LD_IN, attr->index, PC87365_REG_IN_MAX,
521f0986bd8SJim Cromie 			    data->in_max[attr->index]);
5229a61bf63SIngo Molnar 	mutex_unlock(&data->update_lock);
523f0986bd8SJim Cromie 	return count;
524f0986bd8SJim Cromie }
525f0986bd8SJim Cromie 
526dedc6a78SJim Cromie static struct sensor_device_attribute in_max[] = {
527eba42d30SGuenter Roeck 	SENSOR_ATTR_RW(in0_max, in_max, 0),
528eba42d30SGuenter Roeck 	SENSOR_ATTR_RW(in1_max, in_max, 1),
529eba42d30SGuenter Roeck 	SENSOR_ATTR_RW(in2_max, in_max, 2),
530eba42d30SGuenter Roeck 	SENSOR_ATTR_RW(in3_max, in_max, 3),
531eba42d30SGuenter Roeck 	SENSOR_ATTR_RW(in4_max, in_max, 4),
532eba42d30SGuenter Roeck 	SENSOR_ATTR_RW(in5_max, in_max, 5),
533eba42d30SGuenter Roeck 	SENSOR_ATTR_RW(in6_max, in_max, 6),
534eba42d30SGuenter Roeck 	SENSOR_ATTR_RW(in7_max, in_max, 7),
535eba42d30SGuenter Roeck 	SENSOR_ATTR_RW(in8_max, in_max, 8),
536eba42d30SGuenter Roeck 	SENSOR_ATTR_RW(in9_max, in_max, 9),
537eba42d30SGuenter Roeck 	SENSOR_ATTR_RW(in10_max, in_max, 10),
538dedc6a78SJim Cromie };
5398d5d45fbSJean Delvare 
54028f74e71SJim Cromie /* (temp & vin) channel status register alarm bits (pdf sec.11.5.12) */
54128f74e71SJim Cromie #define CHAN_ALM_MIN	0x02	/* min limit crossed */
54228f74e71SJim Cromie #define CHAN_ALM_MAX	0x04	/* max limit exceeded */
54328f74e71SJim Cromie #define TEMP_ALM_CRIT	0x08	/* temp crit exceeded (temp only) */
54428f74e71SJim Cromie 
5453af2861eSGuenter Roeck /*
5463af2861eSGuenter Roeck  * show_in_min/max_alarm() reads data from the per-channel status
5473af2861eSGuenter Roeck  * register (sec 11.5.12), not the vin event status registers (sec
5483af2861eSGuenter Roeck  * 11.5.2) that (legacy) show_in_alarm() resds (via data->in_alarms)
5493af2861eSGuenter Roeck  */
550492e9657SJim Cromie 
in_min_alarm_show(struct device * dev,struct device_attribute * devattr,char * buf)551eba42d30SGuenter Roeck static ssize_t in_min_alarm_show(struct device *dev,
552492e9657SJim Cromie 				 struct device_attribute *devattr, char *buf)
553492e9657SJim Cromie {
554492e9657SJim Cromie 	struct pc87360_data *data = pc87360_update_device(dev);
555492e9657SJim Cromie 	unsigned nr = to_sensor_dev_attr(devattr)->index;
556492e9657SJim Cromie 
557492e9657SJim Cromie 	return sprintf(buf, "%u\n", !!(data->in_status[nr] & CHAN_ALM_MIN));
558492e9657SJim Cromie }
559492e9657SJim Cromie 
560492e9657SJim Cromie static struct sensor_device_attribute in_min_alarm[] = {
561eba42d30SGuenter Roeck 	SENSOR_ATTR_RO(in0_min_alarm, in_min_alarm, 0),
562eba42d30SGuenter Roeck 	SENSOR_ATTR_RO(in1_min_alarm, in_min_alarm, 1),
563eba42d30SGuenter Roeck 	SENSOR_ATTR_RO(in2_min_alarm, in_min_alarm, 2),
564eba42d30SGuenter Roeck 	SENSOR_ATTR_RO(in3_min_alarm, in_min_alarm, 3),
565eba42d30SGuenter Roeck 	SENSOR_ATTR_RO(in4_min_alarm, in_min_alarm, 4),
566eba42d30SGuenter Roeck 	SENSOR_ATTR_RO(in5_min_alarm, in_min_alarm, 5),
567eba42d30SGuenter Roeck 	SENSOR_ATTR_RO(in6_min_alarm, in_min_alarm, 6),
568eba42d30SGuenter Roeck 	SENSOR_ATTR_RO(in7_min_alarm, in_min_alarm, 7),
569eba42d30SGuenter Roeck 	SENSOR_ATTR_RO(in8_min_alarm, in_min_alarm, 8),
570eba42d30SGuenter Roeck 	SENSOR_ATTR_RO(in9_min_alarm, in_min_alarm, 9),
571eba42d30SGuenter Roeck 	SENSOR_ATTR_RO(in10_min_alarm, in_min_alarm, 10),
572492e9657SJim Cromie };
573070affa8SUwe Kleine-König 
in_max_alarm_show(struct device * dev,struct device_attribute * devattr,char * buf)574070affa8SUwe Kleine-König static ssize_t in_max_alarm_show(struct device *dev,
575070affa8SUwe Kleine-König 				 struct device_attribute *devattr, char *buf)
576070affa8SUwe Kleine-König {
577070affa8SUwe Kleine-König 	struct pc87360_data *data = pc87360_update_device(dev);
578070affa8SUwe Kleine-König 	unsigned nr = to_sensor_dev_attr(devattr)->index;
579070affa8SUwe Kleine-König 
580070affa8SUwe Kleine-König 	return sprintf(buf, "%u\n", !!(data->in_status[nr] & CHAN_ALM_MAX));
581070affa8SUwe Kleine-König }
582070affa8SUwe Kleine-König 
583492e9657SJim Cromie static struct sensor_device_attribute in_max_alarm[] = {
584eba42d30SGuenter Roeck 	SENSOR_ATTR_RO(in0_max_alarm, in_max_alarm, 0),
585eba42d30SGuenter Roeck 	SENSOR_ATTR_RO(in1_max_alarm, in_max_alarm, 1),
586eba42d30SGuenter Roeck 	SENSOR_ATTR_RO(in2_max_alarm, in_max_alarm, 2),
587eba42d30SGuenter Roeck 	SENSOR_ATTR_RO(in3_max_alarm, in_max_alarm, 3),
588eba42d30SGuenter Roeck 	SENSOR_ATTR_RO(in4_max_alarm, in_max_alarm, 4),
589eba42d30SGuenter Roeck 	SENSOR_ATTR_RO(in5_max_alarm, in_max_alarm, 5),
590eba42d30SGuenter Roeck 	SENSOR_ATTR_RO(in6_max_alarm, in_max_alarm, 6),
591eba42d30SGuenter Roeck 	SENSOR_ATTR_RO(in7_max_alarm, in_max_alarm, 7),
592eba42d30SGuenter Roeck 	SENSOR_ATTR_RO(in8_max_alarm, in_max_alarm, 8),
593eba42d30SGuenter Roeck 	SENSOR_ATTR_RO(in9_max_alarm, in_max_alarm, 9),
594eba42d30SGuenter Roeck 	SENSOR_ATTR_RO(in10_max_alarm, in_max_alarm, 10),
595492e9657SJim Cromie };
596492e9657SJim Cromie 
597941c5c05SJim Cromie #define VIN_UNIT_ATTRS(X) \
598941c5c05SJim Cromie 	&in_input[X].dev_attr.attr,	\
599941c5c05SJim Cromie 	&in_status[X].dev_attr.attr,	\
600941c5c05SJim Cromie 	&in_min[X].dev_attr.attr,	\
601492e9657SJim Cromie 	&in_max[X].dev_attr.attr,	\
602492e9657SJim Cromie 	&in_min_alarm[X].dev_attr.attr,	\
603492e9657SJim Cromie 	&in_max_alarm[X].dev_attr.attr
604941c5c05SJim Cromie 
cpu0_vid_show(struct device * dev,struct device_attribute * attr,char * buf)605e33110d0SJulia Lawall static ssize_t cpu0_vid_show(struct device *dev,
606e33110d0SJulia Lawall 			     struct device_attribute *attr, char *buf)
60744646c19SJim Cromie {
60844646c19SJim Cromie 	struct pc87360_data *data = pc87360_update_device(dev);
60944646c19SJim Cromie 	return sprintf(buf, "%u\n", vid_from_reg(data->vid, data->vrm));
61044646c19SJim Cromie }
611e33110d0SJulia Lawall static DEVICE_ATTR_RO(cpu0_vid);
61244646c19SJim Cromie 
vrm_show(struct device * dev,struct device_attribute * attr,char * buf)613e33110d0SJulia Lawall static ssize_t vrm_show(struct device *dev, struct device_attribute *attr,
614449a7a07SGuenter Roeck 			char *buf)
61544646c19SJim Cromie {
61690d6619aSJean Delvare 	struct pc87360_data *data = dev_get_drvdata(dev);
61744646c19SJim Cromie 	return sprintf(buf, "%u\n", data->vrm);
61844646c19SJim Cromie }
619070affa8SUwe Kleine-König 
vrm_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)620e33110d0SJulia Lawall static ssize_t vrm_store(struct device *dev, struct device_attribute *attr,
621449a7a07SGuenter Roeck 			 const char *buf, size_t count)
62244646c19SJim Cromie {
623f641b588SJean Delvare 	struct pc87360_data *data = dev_get_drvdata(dev);
624449a7a07SGuenter Roeck 	unsigned long val;
625449a7a07SGuenter Roeck 	int err;
626449a7a07SGuenter Roeck 
627449a7a07SGuenter Roeck 	err = kstrtoul(buf, 10, &val);
628449a7a07SGuenter Roeck 	if (err)
629449a7a07SGuenter Roeck 		return err;
630449a7a07SGuenter Roeck 
6315e3b5610SAxel Lin 	if (val > 255)
6325e3b5610SAxel Lin 		return -EINVAL;
6335e3b5610SAxel Lin 
634449a7a07SGuenter Roeck 	data->vrm = val;
63544646c19SJim Cromie 	return count;
63644646c19SJim Cromie }
637e33110d0SJulia Lawall static DEVICE_ATTR_RW(vrm);
63844646c19SJim Cromie 
alarms_in_show(struct device * dev,struct device_attribute * attr,char * buf)639e33110d0SJulia Lawall static ssize_t alarms_in_show(struct device *dev,
640449a7a07SGuenter Roeck 			      struct device_attribute *attr, char *buf)
64144646c19SJim Cromie {
64244646c19SJim Cromie 	struct pc87360_data *data = pc87360_update_device(dev);
64344646c19SJim Cromie 	return sprintf(buf, "%u\n", data->in_alarms);
64444646c19SJim Cromie }
645e33110d0SJulia Lawall static DEVICE_ATTR_RO(alarms_in);
64644646c19SJim Cromie 
647941c5c05SJim Cromie static struct attribute *pc8736x_vin_attr_array[] = {
648941c5c05SJim Cromie 	VIN_UNIT_ATTRS(0),
649941c5c05SJim Cromie 	VIN_UNIT_ATTRS(1),
650941c5c05SJim Cromie 	VIN_UNIT_ATTRS(2),
651941c5c05SJim Cromie 	VIN_UNIT_ATTRS(3),
652941c5c05SJim Cromie 	VIN_UNIT_ATTRS(4),
653941c5c05SJim Cromie 	VIN_UNIT_ATTRS(5),
654941c5c05SJim Cromie 	VIN_UNIT_ATTRS(6),
655941c5c05SJim Cromie 	VIN_UNIT_ATTRS(7),
656941c5c05SJim Cromie 	VIN_UNIT_ATTRS(8),
657941c5c05SJim Cromie 	VIN_UNIT_ATTRS(9),
658941c5c05SJim Cromie 	VIN_UNIT_ATTRS(10),
659941c5c05SJim Cromie 	&dev_attr_cpu0_vid.attr,
660941c5c05SJim Cromie 	&dev_attr_vrm.attr,
661941c5c05SJim Cromie 	&dev_attr_alarms_in.attr,
662941c5c05SJim Cromie 	NULL
663941c5c05SJim Cromie };
664941c5c05SJim Cromie static const struct attribute_group pc8736x_vin_group = {
665941c5c05SJim Cromie 	.attrs = pc8736x_vin_attr_array,
666941c5c05SJim Cromie };
667941c5c05SJim Cromie 
therm_input_show(struct device * dev,struct device_attribute * devattr,char * buf)668eba42d30SGuenter Roeck static ssize_t therm_input_show(struct device *dev,
669449a7a07SGuenter Roeck 				struct device_attribute *devattr, char *buf)
670f0986bd8SJim Cromie {
671f0986bd8SJim Cromie 	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
672f0986bd8SJim Cromie 	struct pc87360_data *data = pc87360_update_device(dev);
673694fa056SJim Cromie 	return sprintf(buf, "%u\n", IN_FROM_REG(data->in[attr->index],
674f0986bd8SJim Cromie 		       data->in_vref));
675f0986bd8SJim Cromie }
676070affa8SUwe Kleine-König 
677070affa8SUwe Kleine-König /*
678070affa8SUwe Kleine-König  * the +11 term below reflects the fact that VLM units 11,12,13 are
679070affa8SUwe Kleine-König  * used in the chip to measure voltage across the thermistors
680070affa8SUwe Kleine-König  */
681070affa8SUwe Kleine-König static struct sensor_device_attribute therm_input[] = {
682070affa8SUwe Kleine-König 	SENSOR_ATTR_RO(temp4_input, therm_input, 0 + 11),
683070affa8SUwe Kleine-König 	SENSOR_ATTR_RO(temp5_input, therm_input, 1 + 11),
684070affa8SUwe Kleine-König 	SENSOR_ATTR_RO(temp6_input, therm_input, 2 + 11),
685070affa8SUwe Kleine-König };
686070affa8SUwe Kleine-König 
therm_status_show(struct device * dev,struct device_attribute * devattr,char * buf)687070affa8SUwe Kleine-König static ssize_t therm_status_show(struct device *dev,
688070affa8SUwe Kleine-König 				 struct device_attribute *devattr, char *buf)
689070affa8SUwe Kleine-König {
690070affa8SUwe Kleine-König 	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
691070affa8SUwe Kleine-König 	struct pc87360_data *data = pc87360_update_device(dev);
692070affa8SUwe Kleine-König 	return sprintf(buf, "%u\n", data->in_status[attr->index]);
693070affa8SUwe Kleine-König }
694070affa8SUwe Kleine-König 
695070affa8SUwe Kleine-König static struct sensor_device_attribute therm_status[] = {
696070affa8SUwe Kleine-König 	SENSOR_ATTR_RO(temp4_status, therm_status, 0 + 11),
697070affa8SUwe Kleine-König 	SENSOR_ATTR_RO(temp5_status, therm_status, 1 + 11),
698070affa8SUwe Kleine-König 	SENSOR_ATTR_RO(temp6_status, therm_status, 2 + 11),
699070affa8SUwe Kleine-König };
700070affa8SUwe Kleine-König 
therm_min_show(struct device * dev,struct device_attribute * devattr,char * buf)701eba42d30SGuenter Roeck static ssize_t therm_min_show(struct device *dev,
702449a7a07SGuenter Roeck 			      struct device_attribute *devattr, char *buf)
703f0986bd8SJim Cromie {
704f0986bd8SJim Cromie 	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
705f0986bd8SJim Cromie 	struct pc87360_data *data = pc87360_update_device(dev);
706694fa056SJim Cromie 	return sprintf(buf, "%u\n", IN_FROM_REG(data->in_min[attr->index],
707f0986bd8SJim Cromie 		       data->in_vref));
708f0986bd8SJim Cromie }
709449a7a07SGuenter Roeck 
therm_min_store(struct device * dev,struct device_attribute * devattr,const char * buf,size_t count)710eba42d30SGuenter Roeck static ssize_t therm_min_store(struct device *dev,
711449a7a07SGuenter Roeck 			       struct device_attribute *devattr,
712449a7a07SGuenter Roeck 			       const char *buf, size_t count)
713f0986bd8SJim Cromie {
714f0986bd8SJim Cromie 	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
715f641b588SJean Delvare 	struct pc87360_data *data = dev_get_drvdata(dev);
716449a7a07SGuenter Roeck 	long val;
717449a7a07SGuenter Roeck 	int err;
718449a7a07SGuenter Roeck 
719449a7a07SGuenter Roeck 	err = kstrtol(buf, 10, &val);
720449a7a07SGuenter Roeck 	if (err)
721449a7a07SGuenter Roeck 		return err;
722f0986bd8SJim Cromie 
7239a61bf63SIngo Molnar 	mutex_lock(&data->update_lock);
724694fa056SJim Cromie 	data->in_min[attr->index] = IN_TO_REG(val, data->in_vref);
725694fa056SJim Cromie 	pc87360_write_value(data, LD_IN, attr->index, PC87365_REG_TEMP_MIN,
726694fa056SJim Cromie 			    data->in_min[attr->index]);
7279a61bf63SIngo Molnar 	mutex_unlock(&data->update_lock);
728f0986bd8SJim Cromie 	return count;
729f0986bd8SJim Cromie }
730449a7a07SGuenter Roeck 
731070affa8SUwe Kleine-König static struct sensor_device_attribute therm_min[] = {
732070affa8SUwe Kleine-König 	SENSOR_ATTR_RW(temp4_min, therm_min, 0 + 11),
733070affa8SUwe Kleine-König 	SENSOR_ATTR_RW(temp5_min, therm_min, 1 + 11),
734070affa8SUwe Kleine-König 	SENSOR_ATTR_RW(temp6_min, therm_min, 2 + 11),
735070affa8SUwe Kleine-König };
736070affa8SUwe Kleine-König 
therm_max_show(struct device * dev,struct device_attribute * devattr,char * buf)737070affa8SUwe Kleine-König static ssize_t therm_max_show(struct device *dev,
738070affa8SUwe Kleine-König 			      struct device_attribute *devattr, char *buf)
739070affa8SUwe Kleine-König {
740070affa8SUwe Kleine-König 	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
741070affa8SUwe Kleine-König 	struct pc87360_data *data = pc87360_update_device(dev);
742070affa8SUwe Kleine-König 	return sprintf(buf, "%u\n", IN_FROM_REG(data->in_max[attr->index],
743070affa8SUwe Kleine-König 		       data->in_vref));
744070affa8SUwe Kleine-König }
745070affa8SUwe Kleine-König 
therm_max_store(struct device * dev,struct device_attribute * devattr,const char * buf,size_t count)746eba42d30SGuenter Roeck static ssize_t therm_max_store(struct device *dev,
747449a7a07SGuenter Roeck 			       struct device_attribute *devattr,
748449a7a07SGuenter Roeck 			       const char *buf, size_t count)
749f0986bd8SJim Cromie {
750f0986bd8SJim Cromie 	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
751f641b588SJean Delvare 	struct pc87360_data *data = dev_get_drvdata(dev);
752449a7a07SGuenter Roeck 	long val;
753449a7a07SGuenter Roeck 	int err;
754449a7a07SGuenter Roeck 
755449a7a07SGuenter Roeck 	err = kstrtol(buf, 10, &val);
756449a7a07SGuenter Roeck 	if (err)
757449a7a07SGuenter Roeck 		return err;
758f0986bd8SJim Cromie 
7599a61bf63SIngo Molnar 	mutex_lock(&data->update_lock);
760694fa056SJim Cromie 	data->in_max[attr->index] = IN_TO_REG(val, data->in_vref);
761694fa056SJim Cromie 	pc87360_write_value(data, LD_IN, attr->index, PC87365_REG_TEMP_MAX,
762694fa056SJim Cromie 			    data->in_max[attr->index]);
7639a61bf63SIngo Molnar 	mutex_unlock(&data->update_lock);
764f0986bd8SJim Cromie 	return count;
765f0986bd8SJim Cromie }
766070affa8SUwe Kleine-König 
767070affa8SUwe Kleine-König static struct sensor_device_attribute therm_max[] = {
768070affa8SUwe Kleine-König 	SENSOR_ATTR_RW(temp4_max, therm_max, 0 + 11),
769070affa8SUwe Kleine-König 	SENSOR_ATTR_RW(temp5_max, therm_max, 1 + 11),
770070affa8SUwe Kleine-König 	SENSOR_ATTR_RW(temp6_max, therm_max, 2 + 11),
771070affa8SUwe Kleine-König };
772070affa8SUwe Kleine-König 
therm_crit_show(struct device * dev,struct device_attribute * devattr,char * buf)773070affa8SUwe Kleine-König static ssize_t therm_crit_show(struct device *dev,
774070affa8SUwe Kleine-König 			       struct device_attribute *devattr, char *buf)
775070affa8SUwe Kleine-König {
776070affa8SUwe Kleine-König 	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
777070affa8SUwe Kleine-König 	struct pc87360_data *data = pc87360_update_device(dev);
778070affa8SUwe Kleine-König 	return sprintf(buf, "%u\n", IN_FROM_REG(data->in_crit[attr->index-11],
779070affa8SUwe Kleine-König 		       data->in_vref));
780070affa8SUwe Kleine-König }
781070affa8SUwe Kleine-König 
therm_crit_store(struct device * dev,struct device_attribute * devattr,const char * buf,size_t count)782eba42d30SGuenter Roeck static ssize_t therm_crit_store(struct device *dev,
783449a7a07SGuenter Roeck 				struct device_attribute *devattr,
784449a7a07SGuenter Roeck 				const char *buf, size_t count)
785f0986bd8SJim Cromie {
786f0986bd8SJim Cromie 	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
787f641b588SJean Delvare 	struct pc87360_data *data = dev_get_drvdata(dev);
788449a7a07SGuenter Roeck 	long val;
789449a7a07SGuenter Roeck 	int err;
790449a7a07SGuenter Roeck 
791449a7a07SGuenter Roeck 	err = kstrtol(buf, 10, &val);
792449a7a07SGuenter Roeck 	if (err)
793449a7a07SGuenter Roeck 		return err;
794f0986bd8SJim Cromie 
7959a61bf63SIngo Molnar 	mutex_lock(&data->update_lock);
796694fa056SJim Cromie 	data->in_crit[attr->index-11] = IN_TO_REG(val, data->in_vref);
797694fa056SJim Cromie 	pc87360_write_value(data, LD_IN, attr->index, PC87365_REG_TEMP_CRIT,
798694fa056SJim Cromie 			    data->in_crit[attr->index-11]);
7999a61bf63SIngo Molnar 	mutex_unlock(&data->update_lock);
800f0986bd8SJim Cromie 	return count;
801f0986bd8SJim Cromie }
802f0986bd8SJim Cromie 
803dedc6a78SJim Cromie static struct sensor_device_attribute therm_crit[] = {
804eba42d30SGuenter Roeck 	SENSOR_ATTR_RW(temp4_crit, therm_crit, 0 + 11),
805eba42d30SGuenter Roeck 	SENSOR_ATTR_RW(temp5_crit, therm_crit, 1 + 11),
806eba42d30SGuenter Roeck 	SENSOR_ATTR_RW(temp6_crit, therm_crit, 2 + 11),
807dedc6a78SJim Cromie };
8088d5d45fbSJean Delvare 
8093af2861eSGuenter Roeck /*
8103af2861eSGuenter Roeck  * show_therm_min/max_alarm() reads data from the per-channel voltage
8113af2861eSGuenter Roeck  * status register (sec 11.5.12)
8123af2861eSGuenter Roeck  */
therm_min_alarm_show(struct device * dev,struct device_attribute * devattr,char * buf)813eba42d30SGuenter Roeck static ssize_t therm_min_alarm_show(struct device *dev,
814eba42d30SGuenter Roeck 				    struct device_attribute *devattr,
815eba42d30SGuenter Roeck 				    char *buf)
816865c2953SJim Cromie {
817865c2953SJim Cromie 	struct pc87360_data *data = pc87360_update_device(dev);
818865c2953SJim Cromie 	unsigned nr = to_sensor_dev_attr(devattr)->index;
819865c2953SJim Cromie 
820865c2953SJim Cromie 	return sprintf(buf, "%u\n", !!(data->in_status[nr] & CHAN_ALM_MIN));
821865c2953SJim Cromie }
822070affa8SUwe Kleine-König 
823070affa8SUwe Kleine-König static struct sensor_device_attribute therm_min_alarm[] = {
824070affa8SUwe Kleine-König 	SENSOR_ATTR_RO(temp4_min_alarm, therm_min_alarm, 0 + 11),
825070affa8SUwe Kleine-König 	SENSOR_ATTR_RO(temp5_min_alarm, therm_min_alarm, 1 + 11),
826070affa8SUwe Kleine-König 	SENSOR_ATTR_RO(temp6_min_alarm, therm_min_alarm, 2 + 11),
827070affa8SUwe Kleine-König };
828070affa8SUwe Kleine-König 
therm_max_alarm_show(struct device * dev,struct device_attribute * devattr,char * buf)829eba42d30SGuenter Roeck static ssize_t therm_max_alarm_show(struct device *dev,
830eba42d30SGuenter Roeck 				    struct device_attribute *devattr,
831eba42d30SGuenter Roeck 				    char *buf)
832865c2953SJim Cromie {
833865c2953SJim Cromie 	struct pc87360_data *data = pc87360_update_device(dev);
834865c2953SJim Cromie 	unsigned nr = to_sensor_dev_attr(devattr)->index;
835865c2953SJim Cromie 
836865c2953SJim Cromie 	return sprintf(buf, "%u\n", !!(data->in_status[nr] & CHAN_ALM_MAX));
837865c2953SJim Cromie }
838070affa8SUwe Kleine-König 
839070affa8SUwe Kleine-König static struct sensor_device_attribute therm_max_alarm[] = {
840070affa8SUwe Kleine-König 	SENSOR_ATTR_RO(temp4_max_alarm, therm_max_alarm, 0 + 11),
841070affa8SUwe Kleine-König 	SENSOR_ATTR_RO(temp5_max_alarm, therm_max_alarm, 1 + 11),
842070affa8SUwe Kleine-König 	SENSOR_ATTR_RO(temp6_max_alarm, therm_max_alarm, 2 + 11),
843070affa8SUwe Kleine-König };
844070affa8SUwe Kleine-König 
therm_crit_alarm_show(struct device * dev,struct device_attribute * devattr,char * buf)845eba42d30SGuenter Roeck static ssize_t therm_crit_alarm_show(struct device *dev,
846eba42d30SGuenter Roeck 				     struct device_attribute *devattr,
847eba42d30SGuenter Roeck 				     char *buf)
848865c2953SJim Cromie {
849865c2953SJim Cromie 	struct pc87360_data *data = pc87360_update_device(dev);
850865c2953SJim Cromie 	unsigned nr = to_sensor_dev_attr(devattr)->index;
851865c2953SJim Cromie 
852865c2953SJim Cromie 	return sprintf(buf, "%u\n", !!(data->in_status[nr] & TEMP_ALM_CRIT));
853865c2953SJim Cromie }
854865c2953SJim Cromie 
855865c2953SJim Cromie static struct sensor_device_attribute therm_crit_alarm[] = {
856eba42d30SGuenter Roeck 	SENSOR_ATTR_RO(temp4_crit_alarm, therm_crit_alarm, 0 + 11),
857eba42d30SGuenter Roeck 	SENSOR_ATTR_RO(temp5_crit_alarm, therm_crit_alarm, 1 + 11),
858eba42d30SGuenter Roeck 	SENSOR_ATTR_RO(temp6_crit_alarm, therm_crit_alarm, 2 + 11),
859865c2953SJim Cromie };
860865c2953SJim Cromie 
861941c5c05SJim Cromie #define THERM_UNIT_ATTRS(X) \
862941c5c05SJim Cromie 	&therm_input[X].dev_attr.attr,	\
863941c5c05SJim Cromie 	&therm_status[X].dev_attr.attr,	\
864941c5c05SJim Cromie 	&therm_min[X].dev_attr.attr,	\
865941c5c05SJim Cromie 	&therm_max[X].dev_attr.attr,	\
866865c2953SJim Cromie 	&therm_crit[X].dev_attr.attr,	\
867865c2953SJim Cromie 	&therm_min_alarm[X].dev_attr.attr, \
868865c2953SJim Cromie 	&therm_max_alarm[X].dev_attr.attr, \
869865c2953SJim Cromie 	&therm_crit_alarm[X].dev_attr.attr
870941c5c05SJim Cromie 
871941c5c05SJim Cromie static struct attribute *pc8736x_therm_attr_array[] = {
872941c5c05SJim Cromie 	THERM_UNIT_ATTRS(0),
873941c5c05SJim Cromie 	THERM_UNIT_ATTRS(1),
874941c5c05SJim Cromie 	THERM_UNIT_ATTRS(2),
875941c5c05SJim Cromie 	NULL
876941c5c05SJim Cromie };
877941c5c05SJim Cromie static const struct attribute_group pc8736x_therm_group = {
878941c5c05SJim Cromie 	.attrs = pc8736x_therm_attr_array,
879941c5c05SJim Cromie };
880941c5c05SJim Cromie 
temp_input_show(struct device * dev,struct device_attribute * devattr,char * buf)881eba42d30SGuenter Roeck static ssize_t temp_input_show(struct device *dev,
882449a7a07SGuenter Roeck 			       struct device_attribute *devattr, char *buf)
883f0986bd8SJim Cromie {
884f0986bd8SJim Cromie 	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
885f0986bd8SJim Cromie 	struct pc87360_data *data = pc87360_update_device(dev);
886694fa056SJim Cromie 	return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp[attr->index]));
887f0986bd8SJim Cromie }
888449a7a07SGuenter Roeck 
889070affa8SUwe Kleine-König static struct sensor_device_attribute temp_input[] = {
890070affa8SUwe Kleine-König 	SENSOR_ATTR_RO(temp1_input, temp_input, 0),
891070affa8SUwe Kleine-König 	SENSOR_ATTR_RO(temp2_input, temp_input, 1),
892070affa8SUwe Kleine-König 	SENSOR_ATTR_RO(temp3_input, temp_input, 2),
893070affa8SUwe Kleine-König };
894449a7a07SGuenter Roeck 
temp_status_show(struct device * dev,struct device_attribute * devattr,char * buf)895eba42d30SGuenter Roeck static ssize_t temp_status_show(struct device *dev,
896449a7a07SGuenter Roeck 				struct device_attribute *devattr, char *buf)
897f0986bd8SJim Cromie {
898f0986bd8SJim Cromie 	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
899f0986bd8SJim Cromie 	struct pc87360_data *data = pc87360_update_device(dev);
900694fa056SJim Cromie 	return sprintf(buf, "%d\n", data->temp_status[attr->index]);
901f0986bd8SJim Cromie }
902449a7a07SGuenter Roeck 
903070affa8SUwe Kleine-König static struct sensor_device_attribute temp_status[] = {
904070affa8SUwe Kleine-König 	SENSOR_ATTR_RO(temp1_status, temp_status, 0),
905070affa8SUwe Kleine-König 	SENSOR_ATTR_RO(temp2_status, temp_status, 1),
906070affa8SUwe Kleine-König 	SENSOR_ATTR_RO(temp3_status, temp_status, 2),
907070affa8SUwe Kleine-König };
908070affa8SUwe Kleine-König 
temp_min_show(struct device * dev,struct device_attribute * devattr,char * buf)909070affa8SUwe Kleine-König static ssize_t temp_min_show(struct device *dev,
910070affa8SUwe Kleine-König 			     struct device_attribute *devattr, char *buf)
911070affa8SUwe Kleine-König {
912070affa8SUwe Kleine-König 	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
913070affa8SUwe Kleine-König 	struct pc87360_data *data = pc87360_update_device(dev);
914070affa8SUwe Kleine-König 	return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_min[attr->index]));
915070affa8SUwe Kleine-König }
916070affa8SUwe Kleine-König 
temp_min_store(struct device * dev,struct device_attribute * devattr,const char * buf,size_t count)917eba42d30SGuenter Roeck static ssize_t temp_min_store(struct device *dev,
918449a7a07SGuenter Roeck 			      struct device_attribute *devattr,
919449a7a07SGuenter Roeck 			      const char *buf, size_t count)
920f0986bd8SJim Cromie {
921f0986bd8SJim Cromie 	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
922f641b588SJean Delvare 	struct pc87360_data *data = dev_get_drvdata(dev);
923449a7a07SGuenter Roeck 	long val;
924449a7a07SGuenter Roeck 	int err;
925449a7a07SGuenter Roeck 
926449a7a07SGuenter Roeck 	err = kstrtol(buf, 10, &val);
927449a7a07SGuenter Roeck 	if (err)
928449a7a07SGuenter Roeck 		return err;
929f0986bd8SJim Cromie 
9309a61bf63SIngo Molnar 	mutex_lock(&data->update_lock);
931694fa056SJim Cromie 	data->temp_min[attr->index] = TEMP_TO_REG(val);
932694fa056SJim Cromie 	pc87360_write_value(data, LD_TEMP, attr->index, PC87365_REG_TEMP_MIN,
933694fa056SJim Cromie 			    data->temp_min[attr->index]);
9349a61bf63SIngo Molnar 	mutex_unlock(&data->update_lock);
935f0986bd8SJim Cromie 	return count;
936f0986bd8SJim Cromie }
937449a7a07SGuenter Roeck 
938070affa8SUwe Kleine-König static struct sensor_device_attribute temp_min[] = {
939070affa8SUwe Kleine-König 	SENSOR_ATTR_RW(temp1_min, temp_min, 0),
940070affa8SUwe Kleine-König 	SENSOR_ATTR_RW(temp2_min, temp_min, 1),
941070affa8SUwe Kleine-König 	SENSOR_ATTR_RW(temp3_min, temp_min, 2),
942070affa8SUwe Kleine-König };
943070affa8SUwe Kleine-König 
temp_max_show(struct device * dev,struct device_attribute * devattr,char * buf)944070affa8SUwe Kleine-König static ssize_t temp_max_show(struct device *dev,
945070affa8SUwe Kleine-König 			     struct device_attribute *devattr, char *buf)
946070affa8SUwe Kleine-König {
947070affa8SUwe Kleine-König 	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
948070affa8SUwe Kleine-König 	struct pc87360_data *data = pc87360_update_device(dev);
949070affa8SUwe Kleine-König 	return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_max[attr->index]));
950070affa8SUwe Kleine-König }
951070affa8SUwe Kleine-König 
temp_max_store(struct device * dev,struct device_attribute * devattr,const char * buf,size_t count)952eba42d30SGuenter Roeck static ssize_t temp_max_store(struct device *dev,
953449a7a07SGuenter Roeck 			      struct device_attribute *devattr,
954449a7a07SGuenter Roeck 			      const char *buf, size_t count)
955f0986bd8SJim Cromie {
956f0986bd8SJim Cromie 	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
957f641b588SJean Delvare 	struct pc87360_data *data = dev_get_drvdata(dev);
958449a7a07SGuenter Roeck 	long val;
959449a7a07SGuenter Roeck 	int err;
960449a7a07SGuenter Roeck 
961449a7a07SGuenter Roeck 	err = kstrtol(buf, 10, &val);
962449a7a07SGuenter Roeck 	if (err)
963449a7a07SGuenter Roeck 		return err;
964f0986bd8SJim Cromie 
9659a61bf63SIngo Molnar 	mutex_lock(&data->update_lock);
966694fa056SJim Cromie 	data->temp_max[attr->index] = TEMP_TO_REG(val);
967694fa056SJim Cromie 	pc87360_write_value(data, LD_TEMP, attr->index, PC87365_REG_TEMP_MAX,
968694fa056SJim Cromie 			    data->temp_max[attr->index]);
9699a61bf63SIngo Molnar 	mutex_unlock(&data->update_lock);
970f0986bd8SJim Cromie 	return count;
971f0986bd8SJim Cromie }
972449a7a07SGuenter Roeck 
973070affa8SUwe Kleine-König static struct sensor_device_attribute temp_max[] = {
974070affa8SUwe Kleine-König 	SENSOR_ATTR_RW(temp1_max, temp_max, 0),
975070affa8SUwe Kleine-König 	SENSOR_ATTR_RW(temp2_max, temp_max, 1),
976070affa8SUwe Kleine-König 	SENSOR_ATTR_RW(temp3_max, temp_max, 2),
977070affa8SUwe Kleine-König };
978070affa8SUwe Kleine-König 
temp_crit_show(struct device * dev,struct device_attribute * devattr,char * buf)979070affa8SUwe Kleine-König static ssize_t temp_crit_show(struct device *dev,
980070affa8SUwe Kleine-König 			      struct device_attribute *devattr, char *buf)
981070affa8SUwe Kleine-König {
982070affa8SUwe Kleine-König 	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
983070affa8SUwe Kleine-König 	struct pc87360_data *data = pc87360_update_device(dev);
984070affa8SUwe Kleine-König 	return sprintf(buf, "%d\n",
985070affa8SUwe Kleine-König 		       TEMP_FROM_REG(data->temp_crit[attr->index]));
986070affa8SUwe Kleine-König }
987070affa8SUwe Kleine-König 
temp_crit_store(struct device * dev,struct device_attribute * devattr,const char * buf,size_t count)988eba42d30SGuenter Roeck static ssize_t temp_crit_store(struct device *dev,
989eba42d30SGuenter Roeck 			       struct device_attribute *devattr,
990eba42d30SGuenter Roeck 			       const char *buf, size_t count)
991f0986bd8SJim Cromie {
992f0986bd8SJim Cromie 	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
993f641b588SJean Delvare 	struct pc87360_data *data = dev_get_drvdata(dev);
994449a7a07SGuenter Roeck 	long val;
995449a7a07SGuenter Roeck 	int err;
996449a7a07SGuenter Roeck 
997449a7a07SGuenter Roeck 	err = kstrtol(buf, 10, &val);
998449a7a07SGuenter Roeck 	if (err)
999449a7a07SGuenter Roeck 		return err;
1000f0986bd8SJim Cromie 
10019a61bf63SIngo Molnar 	mutex_lock(&data->update_lock);
1002694fa056SJim Cromie 	data->temp_crit[attr->index] = TEMP_TO_REG(val);
1003694fa056SJim Cromie 	pc87360_write_value(data, LD_TEMP, attr->index, PC87365_REG_TEMP_CRIT,
1004694fa056SJim Cromie 			    data->temp_crit[attr->index]);
10059a61bf63SIngo Molnar 	mutex_unlock(&data->update_lock);
1006f0986bd8SJim Cromie 	return count;
1007f0986bd8SJim Cromie }
1008f0986bd8SJim Cromie 
1009dedc6a78SJim Cromie static struct sensor_device_attribute temp_crit[] = {
1010eba42d30SGuenter Roeck 	SENSOR_ATTR_RW(temp1_crit, temp_crit, 0),
1011eba42d30SGuenter Roeck 	SENSOR_ATTR_RW(temp2_crit, temp_crit, 1),
1012eba42d30SGuenter Roeck 	SENSOR_ATTR_RW(temp3_crit, temp_crit, 2),
1013dedc6a78SJim Cromie };
10148d5d45fbSJean Delvare 
10153af2861eSGuenter Roeck /*
1016070affa8SUwe Kleine-König  * temp_min/max_alarm_show() reads data from the per-channel status
10173af2861eSGuenter Roeck  * register (sec 12.3.7), not the temp event status registers (sec
10183af2861eSGuenter Roeck  * 12.3.2) that show_temp_alarm() reads (via data->temp_alarms)
10193af2861eSGuenter Roeck  */
temp_min_alarm_show(struct device * dev,struct device_attribute * devattr,char * buf)1020eba42d30SGuenter Roeck static ssize_t temp_min_alarm_show(struct device *dev,
1021eba42d30SGuenter Roeck 				   struct device_attribute *devattr,
1022eba42d30SGuenter Roeck 				   char *buf)
1023b267e8cdSJim Cromie {
1024b267e8cdSJim Cromie 	struct pc87360_data *data = pc87360_update_device(dev);
1025b267e8cdSJim Cromie 	unsigned nr = to_sensor_dev_attr(devattr)->index;
1026b267e8cdSJim Cromie 
1027b267e8cdSJim Cromie 	return sprintf(buf, "%u\n", !!(data->temp_status[nr] & CHAN_ALM_MIN));
1028b267e8cdSJim Cromie }
1029449a7a07SGuenter Roeck 
1030070affa8SUwe Kleine-König static struct sensor_device_attribute temp_min_alarm[] = {
1031070affa8SUwe Kleine-König 	SENSOR_ATTR_RO(temp1_min_alarm, temp_min_alarm, 0),
1032070affa8SUwe Kleine-König 	SENSOR_ATTR_RO(temp2_min_alarm, temp_min_alarm, 1),
1033070affa8SUwe Kleine-König 	SENSOR_ATTR_RO(temp3_min_alarm, temp_min_alarm, 2),
1034070affa8SUwe Kleine-König };
1035070affa8SUwe Kleine-König 
temp_max_alarm_show(struct device * dev,struct device_attribute * devattr,char * buf)1036eba42d30SGuenter Roeck static ssize_t temp_max_alarm_show(struct device *dev,
1037eba42d30SGuenter Roeck 				   struct device_attribute *devattr,
1038eba42d30SGuenter Roeck 				   char *buf)
1039b267e8cdSJim Cromie {
1040b267e8cdSJim Cromie 	struct pc87360_data *data = pc87360_update_device(dev);
1041b267e8cdSJim Cromie 	unsigned nr = to_sensor_dev_attr(devattr)->index;
1042b267e8cdSJim Cromie 
1043b267e8cdSJim Cromie 	return sprintf(buf, "%u\n", !!(data->temp_status[nr] & CHAN_ALM_MAX));
1044b267e8cdSJim Cromie }
1045449a7a07SGuenter Roeck 
1046070affa8SUwe Kleine-König static struct sensor_device_attribute temp_max_alarm[] = {
1047070affa8SUwe Kleine-König 	SENSOR_ATTR_RO(temp1_max_alarm, temp_max_alarm, 0),
1048070affa8SUwe Kleine-König 	SENSOR_ATTR_RO(temp2_max_alarm, temp_max_alarm, 1),
1049070affa8SUwe Kleine-König 	SENSOR_ATTR_RO(temp3_max_alarm, temp_max_alarm, 2),
1050070affa8SUwe Kleine-König };
1051070affa8SUwe Kleine-König 
temp_crit_alarm_show(struct device * dev,struct device_attribute * devattr,char * buf)1052eba42d30SGuenter Roeck static ssize_t temp_crit_alarm_show(struct device *dev,
1053eba42d30SGuenter Roeck 				    struct device_attribute *devattr,
1054eba42d30SGuenter Roeck 				    char *buf)
1055b267e8cdSJim Cromie {
1056b267e8cdSJim Cromie 	struct pc87360_data *data = pc87360_update_device(dev);
1057b267e8cdSJim Cromie 	unsigned nr = to_sensor_dev_attr(devattr)->index;
1058b267e8cdSJim Cromie 
1059b267e8cdSJim Cromie 	return sprintf(buf, "%u\n", !!(data->temp_status[nr] & TEMP_ALM_CRIT));
1060b267e8cdSJim Cromie }
1061b267e8cdSJim Cromie 
1062b267e8cdSJim Cromie static struct sensor_device_attribute temp_crit_alarm[] = {
1063eba42d30SGuenter Roeck 	SENSOR_ATTR_RO(temp1_crit_alarm, temp_crit_alarm, 0),
1064eba42d30SGuenter Roeck 	SENSOR_ATTR_RO(temp2_crit_alarm, temp_crit_alarm, 1),
1065eba42d30SGuenter Roeck 	SENSOR_ATTR_RO(temp3_crit_alarm, temp_crit_alarm, 2),
1066b267e8cdSJim Cromie };
1067b267e8cdSJim Cromie 
1068b267e8cdSJim Cromie #define TEMP_FAULT	0x40	/* open diode */
temp_fault_show(struct device * dev,struct device_attribute * devattr,char * buf)1069eba42d30SGuenter Roeck static ssize_t temp_fault_show(struct device *dev,
1070b267e8cdSJim Cromie 			       struct device_attribute *devattr, char *buf)
1071b267e8cdSJim Cromie {
1072b267e8cdSJim Cromie 	struct pc87360_data *data = pc87360_update_device(dev);
1073b267e8cdSJim Cromie 	unsigned nr = to_sensor_dev_attr(devattr)->index;
1074b267e8cdSJim Cromie 
1075b267e8cdSJim Cromie 	return sprintf(buf, "%u\n", !!(data->temp_status[nr] & TEMP_FAULT));
1076b267e8cdSJim Cromie }
1077070affa8SUwe Kleine-König 
1078b267e8cdSJim Cromie static struct sensor_device_attribute temp_fault[] = {
1079eba42d30SGuenter Roeck 	SENSOR_ATTR_RO(temp1_fault, temp_fault, 0),
1080eba42d30SGuenter Roeck 	SENSOR_ATTR_RO(temp2_fault, temp_fault, 1),
1081eba42d30SGuenter Roeck 	SENSOR_ATTR_RO(temp3_fault, temp_fault, 2),
1082b267e8cdSJim Cromie };
1083b267e8cdSJim Cromie 
1084941c5c05SJim Cromie #define TEMP_UNIT_ATTRS(X)			\
1085bce2778dSGuenter Roeck {	&temp_input[X].dev_attr.attr,		\
1086941c5c05SJim Cromie 	&temp_status[X].dev_attr.attr,		\
1087941c5c05SJim Cromie 	&temp_min[X].dev_attr.attr,		\
1088941c5c05SJim Cromie 	&temp_max[X].dev_attr.attr,		\
1089b267e8cdSJim Cromie 	&temp_crit[X].dev_attr.attr,		\
1090b267e8cdSJim Cromie 	&temp_min_alarm[X].dev_attr.attr,	\
1091b267e8cdSJim Cromie 	&temp_max_alarm[X].dev_attr.attr,	\
1092b267e8cdSJim Cromie 	&temp_crit_alarm[X].dev_attr.attr,	\
1093bce2778dSGuenter Roeck 	&temp_fault[X].dev_attr.attr,		\
1094bce2778dSGuenter Roeck 	NULL					\
1095bce2778dSGuenter Roeck }
1096941c5c05SJim Cromie 
1097bce2778dSGuenter Roeck static struct attribute *pc8736x_temp_attr[][10] = {
1098941c5c05SJim Cromie 	TEMP_UNIT_ATTRS(0),
1099941c5c05SJim Cromie 	TEMP_UNIT_ATTRS(1),
1100bce2778dSGuenter Roeck 	TEMP_UNIT_ATTRS(2)
1101941c5c05SJim Cromie };
1102449a7a07SGuenter Roeck 
1103bce2778dSGuenter Roeck static const struct attribute_group pc8736x_temp_attr_group[] = {
1104bce2778dSGuenter Roeck 	{ .attrs = pc8736x_temp_attr[0] },
1105bce2778dSGuenter Roeck 	{ .attrs = pc8736x_temp_attr[1] },
1106bce2778dSGuenter Roeck 	{ .attrs = pc8736x_temp_attr[2] }
1107941c5c05SJim Cromie };
1108941c5c05SJim Cromie 
alarms_temp_show(struct device * dev,struct device_attribute * attr,char * buf)1109070affa8SUwe Kleine-König static ssize_t alarms_temp_show(struct device *dev,
1110070affa8SUwe Kleine-König 				struct device_attribute *attr, char *buf)
1111070affa8SUwe Kleine-König {
1112070affa8SUwe Kleine-König 	struct pc87360_data *data = pc87360_update_device(dev);
1113070affa8SUwe Kleine-König 	return sprintf(buf, "%u\n", data->temp_alarms);
1114070affa8SUwe Kleine-König }
1115070affa8SUwe Kleine-König 
1116070affa8SUwe Kleine-König static DEVICE_ATTR_RO(alarms_temp);
1117070affa8SUwe Kleine-König 
fan_input_show(struct device * dev,struct device_attribute * devattr,char * buf)1118070affa8SUwe Kleine-König static ssize_t fan_input_show(struct device *dev,
1119070affa8SUwe Kleine-König 			      struct device_attribute *devattr, char *buf)
1120070affa8SUwe Kleine-König {
1121070affa8SUwe Kleine-König 	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
1122070affa8SUwe Kleine-König 	struct pc87360_data *data = pc87360_update_device(dev);
1123070affa8SUwe Kleine-König 	return sprintf(buf, "%u\n", FAN_FROM_REG(data->fan[attr->index],
1124070affa8SUwe Kleine-König 		       FAN_DIV_FROM_REG(data->fan_status[attr->index])));
1125070affa8SUwe Kleine-König }
1126070affa8SUwe Kleine-König 
1127070affa8SUwe Kleine-König static struct sensor_device_attribute fan_input[] = {
1128070affa8SUwe Kleine-König 	SENSOR_ATTR_RO(fan1_input, fan_input, 0),
1129070affa8SUwe Kleine-König 	SENSOR_ATTR_RO(fan2_input, fan_input, 1),
1130070affa8SUwe Kleine-König 	SENSOR_ATTR_RO(fan3_input, fan_input, 2),
1131070affa8SUwe Kleine-König };
1132070affa8SUwe Kleine-König 
fan_status_show(struct device * dev,struct device_attribute * devattr,char * buf)1133070affa8SUwe Kleine-König static ssize_t fan_status_show(struct device *dev,
1134070affa8SUwe Kleine-König 			       struct device_attribute *devattr, char *buf)
1135070affa8SUwe Kleine-König {
1136070affa8SUwe Kleine-König 	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
1137070affa8SUwe Kleine-König 	struct pc87360_data *data = pc87360_update_device(dev);
1138070affa8SUwe Kleine-König 	return sprintf(buf, "%u\n",
1139070affa8SUwe Kleine-König 		       FAN_STATUS_FROM_REG(data->fan_status[attr->index]));
1140070affa8SUwe Kleine-König }
1141070affa8SUwe Kleine-König 
1142070affa8SUwe Kleine-König static struct sensor_device_attribute fan_status[] = {
1143070affa8SUwe Kleine-König 	SENSOR_ATTR_RO(fan1_status, fan_status, 0),
1144070affa8SUwe Kleine-König 	SENSOR_ATTR_RO(fan2_status, fan_status, 1),
1145070affa8SUwe Kleine-König 	SENSOR_ATTR_RO(fan3_status, fan_status, 2),
1146070affa8SUwe Kleine-König };
1147070affa8SUwe Kleine-König 
fan_div_show(struct device * dev,struct device_attribute * devattr,char * buf)1148070affa8SUwe Kleine-König static ssize_t fan_div_show(struct device *dev,
1149070affa8SUwe Kleine-König 			    struct device_attribute *devattr, char *buf)
1150070affa8SUwe Kleine-König {
1151070affa8SUwe Kleine-König 	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
1152070affa8SUwe Kleine-König 	struct pc87360_data *data = pc87360_update_device(dev);
1153070affa8SUwe Kleine-König 	return sprintf(buf, "%u\n",
1154070affa8SUwe Kleine-König 		       FAN_DIV_FROM_REG(data->fan_status[attr->index]));
1155070affa8SUwe Kleine-König }
1156070affa8SUwe Kleine-König 
1157070affa8SUwe Kleine-König static struct sensor_device_attribute fan_div[] = {
1158070affa8SUwe Kleine-König 	SENSOR_ATTR_RO(fan1_div, fan_div, 0),
1159070affa8SUwe Kleine-König 	SENSOR_ATTR_RO(fan2_div, fan_div, 1),
1160070affa8SUwe Kleine-König 	SENSOR_ATTR_RO(fan3_div, fan_div, 2),
1161070affa8SUwe Kleine-König };
1162070affa8SUwe Kleine-König 
fan_min_show(struct device * dev,struct device_attribute * devattr,char * buf)1163070affa8SUwe Kleine-König static ssize_t fan_min_show(struct device *dev,
1164070affa8SUwe Kleine-König 			    struct device_attribute *devattr, char *buf)
1165070affa8SUwe Kleine-König {
1166070affa8SUwe Kleine-König 	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
1167070affa8SUwe Kleine-König 	struct pc87360_data *data = pc87360_update_device(dev);
1168070affa8SUwe Kleine-König 	return sprintf(buf, "%u\n", FAN_FROM_REG(data->fan_min[attr->index],
1169070affa8SUwe Kleine-König 		       FAN_DIV_FROM_REG(data->fan_status[attr->index])));
1170070affa8SUwe Kleine-König }
1171070affa8SUwe Kleine-König 
fan_min_store(struct device * dev,struct device_attribute * devattr,const char * buf,size_t count)1172070affa8SUwe Kleine-König static ssize_t fan_min_store(struct device *dev,
1173070affa8SUwe Kleine-König 			     struct device_attribute *devattr,
1174070affa8SUwe Kleine-König 			     const char *buf, size_t count)
1175070affa8SUwe Kleine-König {
1176070affa8SUwe Kleine-König 	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
1177070affa8SUwe Kleine-König 	struct pc87360_data *data = dev_get_drvdata(dev);
1178070affa8SUwe Kleine-König 	long fan_min;
1179070affa8SUwe Kleine-König 	int err;
1180070affa8SUwe Kleine-König 
1181070affa8SUwe Kleine-König 	err = kstrtol(buf, 10, &fan_min);
1182070affa8SUwe Kleine-König 	if (err)
1183070affa8SUwe Kleine-König 		return err;
1184070affa8SUwe Kleine-König 
1185070affa8SUwe Kleine-König 	mutex_lock(&data->update_lock);
1186070affa8SUwe Kleine-König 	fan_min = FAN_TO_REG(fan_min,
1187070affa8SUwe Kleine-König 			     FAN_DIV_FROM_REG(data->fan_status[attr->index]));
1188070affa8SUwe Kleine-König 
1189070affa8SUwe Kleine-König 	/* If it wouldn't fit, change clock divisor */
1190070affa8SUwe Kleine-König 	while (fan_min > 255
1191070affa8SUwe Kleine-König 	    && (data->fan_status[attr->index] & 0x60) != 0x60) {
1192070affa8SUwe Kleine-König 		fan_min >>= 1;
1193070affa8SUwe Kleine-König 		data->fan[attr->index] >>= 1;
1194070affa8SUwe Kleine-König 		data->fan_status[attr->index] += 0x20;
1195070affa8SUwe Kleine-König 	}
1196070affa8SUwe Kleine-König 	data->fan_min[attr->index] = fan_min > 255 ? 255 : fan_min;
1197070affa8SUwe Kleine-König 	pc87360_write_value(data, LD_FAN, NO_BANK,
1198070affa8SUwe Kleine-König 			    PC87360_REG_FAN_MIN(attr->index),
1199070affa8SUwe Kleine-König 			    data->fan_min[attr->index]);
1200070affa8SUwe Kleine-König 
1201070affa8SUwe Kleine-König 	/* Write new divider, preserve alarm bits */
1202070affa8SUwe Kleine-König 	pc87360_write_value(data, LD_FAN, NO_BANK,
1203070affa8SUwe Kleine-König 			    PC87360_REG_FAN_STATUS(attr->index),
1204070affa8SUwe Kleine-König 			    data->fan_status[attr->index] & 0xF9);
1205070affa8SUwe Kleine-König 	mutex_unlock(&data->update_lock);
1206070affa8SUwe Kleine-König 
1207070affa8SUwe Kleine-König 	return count;
1208070affa8SUwe Kleine-König }
1209070affa8SUwe Kleine-König 
1210070affa8SUwe Kleine-König static struct sensor_device_attribute fan_min[] = {
1211070affa8SUwe Kleine-König 	SENSOR_ATTR_RW(fan1_min, fan_min, 0),
1212070affa8SUwe Kleine-König 	SENSOR_ATTR_RW(fan2_min, fan_min, 1),
1213070affa8SUwe Kleine-König 	SENSOR_ATTR_RW(fan3_min, fan_min, 2),
1214070affa8SUwe Kleine-König };
1215070affa8SUwe Kleine-König 
1216070affa8SUwe Kleine-König #define FAN_UNIT_ATTRS(X)		\
1217070affa8SUwe Kleine-König {	&fan_input[X].dev_attr.attr,	\
1218070affa8SUwe Kleine-König 	&fan_status[X].dev_attr.attr,	\
1219070affa8SUwe Kleine-König 	&fan_div[X].dev_attr.attr,	\
1220070affa8SUwe Kleine-König 	&fan_min[X].dev_attr.attr,	\
1221070affa8SUwe Kleine-König 	NULL				\
1222070affa8SUwe Kleine-König }
1223070affa8SUwe Kleine-König 
1224070affa8SUwe Kleine-König static struct attribute *pc8736x_fan_attr[][5] = {
1225070affa8SUwe Kleine-König 	FAN_UNIT_ATTRS(0),
1226070affa8SUwe Kleine-König 	FAN_UNIT_ATTRS(1),
1227070affa8SUwe Kleine-König 	FAN_UNIT_ATTRS(2)
1228070affa8SUwe Kleine-König };
1229070affa8SUwe Kleine-König 
1230070affa8SUwe Kleine-König static const struct attribute_group pc8736x_fan_attr_group[] = {
1231070affa8SUwe Kleine-König 	{ .attrs = pc8736x_fan_attr[0], },
1232070affa8SUwe Kleine-König 	{ .attrs = pc8736x_fan_attr[1], },
1233070affa8SUwe Kleine-König 	{ .attrs = pc8736x_fan_attr[2], },
1234070affa8SUwe Kleine-König };
1235070affa8SUwe Kleine-König 
pwm_show(struct device * dev,struct device_attribute * devattr,char * buf)1236070affa8SUwe Kleine-König static ssize_t pwm_show(struct device *dev, struct device_attribute *devattr,
1237070affa8SUwe Kleine-König 			char *buf)
1238070affa8SUwe Kleine-König {
1239070affa8SUwe Kleine-König 	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
1240070affa8SUwe Kleine-König 	struct pc87360_data *data = pc87360_update_device(dev);
1241070affa8SUwe Kleine-König 	return sprintf(buf, "%u\n",
1242070affa8SUwe Kleine-König 		       PWM_FROM_REG(data->pwm[attr->index],
1243070affa8SUwe Kleine-König 				    FAN_CONFIG_INVERT(data->fan_conf,
1244070affa8SUwe Kleine-König 						      attr->index)));
1245070affa8SUwe Kleine-König }
1246070affa8SUwe Kleine-König 
pwm_store(struct device * dev,struct device_attribute * devattr,const char * buf,size_t count)1247070affa8SUwe Kleine-König static ssize_t pwm_store(struct device *dev, struct device_attribute *devattr,
1248070affa8SUwe Kleine-König 			 const char *buf, size_t count)
1249070affa8SUwe Kleine-König {
1250070affa8SUwe Kleine-König 	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
1251070affa8SUwe Kleine-König 	struct pc87360_data *data = dev_get_drvdata(dev);
1252070affa8SUwe Kleine-König 	long val;
1253070affa8SUwe Kleine-König 	int err;
1254070affa8SUwe Kleine-König 
1255070affa8SUwe Kleine-König 	err = kstrtol(buf, 10, &val);
1256070affa8SUwe Kleine-König 	if (err)
1257070affa8SUwe Kleine-König 		return err;
1258070affa8SUwe Kleine-König 
1259070affa8SUwe Kleine-König 	mutex_lock(&data->update_lock);
1260070affa8SUwe Kleine-König 	data->pwm[attr->index] = PWM_TO_REG(val,
1261070affa8SUwe Kleine-König 			      FAN_CONFIG_INVERT(data->fan_conf, attr->index));
1262070affa8SUwe Kleine-König 	pc87360_write_value(data, LD_FAN, NO_BANK, PC87360_REG_PWM(attr->index),
1263070affa8SUwe Kleine-König 			    data->pwm[attr->index]);
1264070affa8SUwe Kleine-König 	mutex_unlock(&data->update_lock);
1265070affa8SUwe Kleine-König 	return count;
1266070affa8SUwe Kleine-König }
1267070affa8SUwe Kleine-König 
1268070affa8SUwe Kleine-König static struct sensor_device_attribute pwm[] = {
1269070affa8SUwe Kleine-König 	SENSOR_ATTR_RW(pwm1, pwm, 0),
1270070affa8SUwe Kleine-König 	SENSOR_ATTR_RW(pwm2, pwm, 1),
1271070affa8SUwe Kleine-König 	SENSOR_ATTR_RW(pwm3, pwm, 2),
1272070affa8SUwe Kleine-König };
1273070affa8SUwe Kleine-König 
name_show(struct device * dev,struct device_attribute * devattr,char * buf)1274e33110d0SJulia Lawall static ssize_t name_show(struct device *dev,
1275b267e8cdSJim Cromie 			struct device_attribute *devattr, char *buf)
1276f641b588SJean Delvare {
1277f641b588SJean Delvare 	struct pc87360_data *data = dev_get_drvdata(dev);
1278f641b588SJean Delvare 	return sprintf(buf, "%s\n", data->name);
1279f641b588SJean Delvare }
1280449a7a07SGuenter Roeck 
1281e33110d0SJulia Lawall static DEVICE_ATTR_RO(name);
1282f641b588SJean Delvare 
pc87360_remove_files(struct device * dev)1283bce2778dSGuenter Roeck static void pc87360_remove_files(struct device *dev)
1284bce2778dSGuenter Roeck {
1285bce2778dSGuenter Roeck 	int i;
1286bce2778dSGuenter Roeck 
1287bce2778dSGuenter Roeck 	device_remove_file(dev, &dev_attr_name);
1288bce2778dSGuenter Roeck 	device_remove_file(dev, &dev_attr_alarms_temp);
1289bce2778dSGuenter Roeck 	for (i = 0; i < ARRAY_SIZE(pc8736x_temp_attr_group); i++)
1290bce2778dSGuenter Roeck 		sysfs_remove_group(&dev->kobj, &pc8736x_temp_attr_group[i]);
1291bce2778dSGuenter Roeck 	for (i = 0; i < ARRAY_SIZE(pc8736x_fan_attr_group); i++) {
1292bce2778dSGuenter Roeck 		sysfs_remove_group(&pdev->dev.kobj, &pc8736x_fan_attr_group[i]);
1293bce2778dSGuenter Roeck 		device_remove_file(dev, &pwm[i].dev_attr);
1294bce2778dSGuenter Roeck 	}
1295bce2778dSGuenter Roeck 	sysfs_remove_group(&dev->kobj, &pc8736x_therm_group);
1296bce2778dSGuenter Roeck 	sysfs_remove_group(&dev->kobj, &pc8736x_vin_group);
1297bce2778dSGuenter Roeck }
1298bce2778dSGuenter Roeck 
pc87360_init_device(struct platform_device * pdev,int use_thermistors)1299070affa8SUwe Kleine-König static void pc87360_init_device(struct platform_device *pdev,
1300070affa8SUwe Kleine-König 				int use_thermistors)
1301070affa8SUwe Kleine-König {
1302070affa8SUwe Kleine-König 	struct pc87360_data *data = platform_get_drvdata(pdev);
1303070affa8SUwe Kleine-König 	int i, nr;
1304070affa8SUwe Kleine-König 	const u8 init_in[14] = { 2, 2, 2, 2, 2, 2, 2, 1, 1, 3, 1, 2, 2, 2 };
1305070affa8SUwe Kleine-König 	const u8 init_temp[3] = { 2, 2, 1 };
1306070affa8SUwe Kleine-König 	u8 reg;
1307070affa8SUwe Kleine-König 
1308070affa8SUwe Kleine-König 	if (init >= 2 && data->innr) {
1309070affa8SUwe Kleine-König 		reg = pc87360_read_value(data, LD_IN, NO_BANK,
1310070affa8SUwe Kleine-König 					 PC87365_REG_IN_CONVRATE);
1311070affa8SUwe Kleine-König 		dev_info(&pdev->dev,
1312070affa8SUwe Kleine-König 			 "VLM conversion set to 1s period, 160us delay\n");
1313070affa8SUwe Kleine-König 		pc87360_write_value(data, LD_IN, NO_BANK,
1314070affa8SUwe Kleine-König 				    PC87365_REG_IN_CONVRATE,
1315070affa8SUwe Kleine-König 				    (reg & 0xC0) | 0x11);
1316070affa8SUwe Kleine-König 	}
1317070affa8SUwe Kleine-König 
1318070affa8SUwe Kleine-König 	nr = data->innr < 11 ? data->innr : 11;
1319070affa8SUwe Kleine-König 	for (i = 0; i < nr; i++) {
1320070affa8SUwe Kleine-König 		reg = pc87360_read_value(data, LD_IN, i,
1321070affa8SUwe Kleine-König 					 PC87365_REG_IN_STATUS);
1322070affa8SUwe Kleine-König 		dev_dbg(&pdev->dev, "bios in%d status:0x%02x\n", i, reg);
1323070affa8SUwe Kleine-König 		if (init >= init_in[i]) {
1324070affa8SUwe Kleine-König 			/* Forcibly enable voltage channel */
1325070affa8SUwe Kleine-König 			if (!(reg & CHAN_ENA)) {
1326070affa8SUwe Kleine-König 				dev_dbg(&pdev->dev, "Forcibly enabling in%d\n",
1327070affa8SUwe Kleine-König 					i);
1328070affa8SUwe Kleine-König 				pc87360_write_value(data, LD_IN, i,
1329070affa8SUwe Kleine-König 						    PC87365_REG_IN_STATUS,
1330070affa8SUwe Kleine-König 						    (reg & 0x68) | 0x87);
1331070affa8SUwe Kleine-König 			}
1332070affa8SUwe Kleine-König 		}
1333070affa8SUwe Kleine-König 	}
1334070affa8SUwe Kleine-König 
1335070affa8SUwe Kleine-König 	/*
1336070affa8SUwe Kleine-König 	 * We can't blindly trust the Super-I/O space configuration bit,
1337070affa8SUwe Kleine-König 	 * most BIOS won't set it properly
1338070affa8SUwe Kleine-König 	 */
1339070affa8SUwe Kleine-König 	dev_dbg(&pdev->dev, "bios thermistors:%d\n", use_thermistors);
1340070affa8SUwe Kleine-König 	for (i = 11; i < data->innr; i++) {
1341070affa8SUwe Kleine-König 		reg = pc87360_read_value(data, LD_IN, i,
1342070affa8SUwe Kleine-König 					 PC87365_REG_TEMP_STATUS);
1343070affa8SUwe Kleine-König 		use_thermistors = use_thermistors || (reg & CHAN_ENA);
1344070affa8SUwe Kleine-König 		/* thermistors are temp[4-6], measured on vin[11-14] */
1345070affa8SUwe Kleine-König 		dev_dbg(&pdev->dev, "bios temp%d_status:0x%02x\n", i-7, reg);
1346070affa8SUwe Kleine-König 	}
1347070affa8SUwe Kleine-König 	dev_dbg(&pdev->dev, "using thermistors:%d\n", use_thermistors);
1348070affa8SUwe Kleine-König 
1349070affa8SUwe Kleine-König 	i = use_thermistors ? 2 : 0;
1350070affa8SUwe Kleine-König 	for (; i < data->tempnr; i++) {
1351070affa8SUwe Kleine-König 		reg = pc87360_read_value(data, LD_TEMP, i,
1352070affa8SUwe Kleine-König 					 PC87365_REG_TEMP_STATUS);
1353070affa8SUwe Kleine-König 		dev_dbg(&pdev->dev, "bios temp%d_status:0x%02x\n", i + 1, reg);
1354070affa8SUwe Kleine-König 		if (init >= init_temp[i]) {
1355070affa8SUwe Kleine-König 			/* Forcibly enable temperature channel */
1356070affa8SUwe Kleine-König 			if (!(reg & CHAN_ENA)) {
1357070affa8SUwe Kleine-König 				dev_dbg(&pdev->dev,
1358070affa8SUwe Kleine-König 					"Forcibly enabling temp%d\n", i + 1);
1359070affa8SUwe Kleine-König 				pc87360_write_value(data, LD_TEMP, i,
1360070affa8SUwe Kleine-König 						    PC87365_REG_TEMP_STATUS,
1361070affa8SUwe Kleine-König 						    0xCF);
1362070affa8SUwe Kleine-König 			}
1363070affa8SUwe Kleine-König 		}
1364070affa8SUwe Kleine-König 	}
1365070affa8SUwe Kleine-König 
1366070affa8SUwe Kleine-König 	if (use_thermistors) {
1367070affa8SUwe Kleine-König 		for (i = 11; i < data->innr; i++) {
1368070affa8SUwe Kleine-König 			if (init >= init_in[i]) {
1369070affa8SUwe Kleine-König 				/*
1370070affa8SUwe Kleine-König 				 * The pin may already be used by thermal
1371070affa8SUwe Kleine-König 				 * diodes
1372070affa8SUwe Kleine-König 				 */
1373070affa8SUwe Kleine-König 				reg = pc87360_read_value(data, LD_TEMP,
1374070affa8SUwe Kleine-König 				      (i - 11) / 2, PC87365_REG_TEMP_STATUS);
1375070affa8SUwe Kleine-König 				if (reg & CHAN_ENA) {
1376070affa8SUwe Kleine-König 					dev_dbg(&pdev->dev,
1377070affa8SUwe Kleine-König 			"Skipping temp%d, pin already in use by temp%d\n",
1378070affa8SUwe Kleine-König 						i - 7, (i - 11) / 2);
1379070affa8SUwe Kleine-König 					continue;
1380070affa8SUwe Kleine-König 				}
1381070affa8SUwe Kleine-König 
1382070affa8SUwe Kleine-König 				/* Forcibly enable thermistor channel */
1383070affa8SUwe Kleine-König 				reg = pc87360_read_value(data, LD_IN, i,
1384070affa8SUwe Kleine-König 							 PC87365_REG_IN_STATUS);
1385070affa8SUwe Kleine-König 				if (!(reg & CHAN_ENA)) {
1386070affa8SUwe Kleine-König 					dev_dbg(&pdev->dev,
1387070affa8SUwe Kleine-König 						"Forcibly enabling temp%d\n",
1388070affa8SUwe Kleine-König 						i - 7);
1389070affa8SUwe Kleine-König 					pc87360_write_value(data, LD_IN, i,
1390070affa8SUwe Kleine-König 						PC87365_REG_TEMP_STATUS,
1391070affa8SUwe Kleine-König 						(reg & 0x60) | 0x8F);
1392070affa8SUwe Kleine-König 				}
1393070affa8SUwe Kleine-König 			}
1394070affa8SUwe Kleine-König 		}
1395070affa8SUwe Kleine-König 	}
1396070affa8SUwe Kleine-König 
1397070affa8SUwe Kleine-König 	if (data->innr) {
1398070affa8SUwe Kleine-König 		reg = pc87360_read_value(data, LD_IN, NO_BANK,
1399070affa8SUwe Kleine-König 					 PC87365_REG_IN_CONFIG);
1400070affa8SUwe Kleine-König 		dev_dbg(&pdev->dev, "bios vin-cfg:0x%02x\n", reg);
1401070affa8SUwe Kleine-König 		if (reg & CHAN_ENA) {
1402070affa8SUwe Kleine-König 			dev_dbg(&pdev->dev,
1403070affa8SUwe Kleine-König 				"Forcibly enabling monitoring (VLM)\n");
1404070affa8SUwe Kleine-König 			pc87360_write_value(data, LD_IN, NO_BANK,
1405070affa8SUwe Kleine-König 					    PC87365_REG_IN_CONFIG,
1406070affa8SUwe Kleine-König 					    reg & 0xFE);
1407070affa8SUwe Kleine-König 		}
1408070affa8SUwe Kleine-König 	}
1409070affa8SUwe Kleine-König 
1410070affa8SUwe Kleine-König 	if (data->tempnr) {
1411070affa8SUwe Kleine-König 		reg = pc87360_read_value(data, LD_TEMP, NO_BANK,
1412070affa8SUwe Kleine-König 					 PC87365_REG_TEMP_CONFIG);
1413070affa8SUwe Kleine-König 		dev_dbg(&pdev->dev, "bios temp-cfg:0x%02x\n", reg);
1414070affa8SUwe Kleine-König 		if (reg & CHAN_ENA) {
1415070affa8SUwe Kleine-König 			dev_dbg(&pdev->dev,
1416070affa8SUwe Kleine-König 				"Forcibly enabling monitoring (TMS)\n");
1417070affa8SUwe Kleine-König 			pc87360_write_value(data, LD_TEMP, NO_BANK,
1418070affa8SUwe Kleine-König 					    PC87365_REG_TEMP_CONFIG,
1419070affa8SUwe Kleine-König 					    reg & 0xFE);
1420070affa8SUwe Kleine-König 		}
1421070affa8SUwe Kleine-König 
1422070affa8SUwe Kleine-König 		if (init >= 2) {
1423070affa8SUwe Kleine-König 			/* Chip config as documented by National Semi. */
1424070affa8SUwe Kleine-König 			pc87360_write_value(data, LD_TEMP, 0xF, 0xA, 0x08);
1425070affa8SUwe Kleine-König 			/*
1426070affa8SUwe Kleine-König 			 * We voluntarily omit the bank here, in case the
1427070affa8SUwe Kleine-König 			 * sequence itself matters. It shouldn't be a problem,
1428070affa8SUwe Kleine-König 			 * since nobody else is supposed to access the
1429070affa8SUwe Kleine-König 			 * device at that point.
1430070affa8SUwe Kleine-König 			 */
1431070affa8SUwe Kleine-König 			pc87360_write_value(data, LD_TEMP, NO_BANK, 0xB, 0x04);
1432070affa8SUwe Kleine-König 			pc87360_write_value(data, LD_TEMP, NO_BANK, 0xC, 0x35);
1433070affa8SUwe Kleine-König 			pc87360_write_value(data, LD_TEMP, NO_BANK, 0xD, 0x05);
1434070affa8SUwe Kleine-König 			pc87360_write_value(data, LD_TEMP, NO_BANK, 0xE, 0x05);
1435070affa8SUwe Kleine-König 		}
1436070affa8SUwe Kleine-König 	}
1437070affa8SUwe Kleine-König }
1438070affa8SUwe Kleine-König 
pc87360_probe(struct platform_device * pdev)14396c931ae1SBill Pemberton static int pc87360_probe(struct platform_device *pdev)
14408d5d45fbSJean Delvare {
14418d5d45fbSJean Delvare 	int i;
14428d5d45fbSJean Delvare 	struct pc87360_data *data;
14438d5d45fbSJean Delvare 	int err = 0;
14445b581575SJean Delvare 	const char *name;
14458d5d45fbSJean Delvare 	int use_thermistors = 0;
1446f641b588SJean Delvare 	struct device *dev = &pdev->dev;
14478d5d45fbSJean Delvare 
144861d4d17eSGuenter Roeck 	data = devm_kzalloc(dev, sizeof(struct pc87360_data), GFP_KERNEL);
1449449a7a07SGuenter Roeck 	if (!data)
14508d5d45fbSJean Delvare 		return -ENOMEM;
14518d5d45fbSJean Delvare 
14528d5d45fbSJean Delvare 	switch (devid) {
14535b581575SJean Delvare 	default:
14545b581575SJean Delvare 		name = "pc87360";
14555b581575SJean Delvare 		data->fannr = 2;
14565b581575SJean Delvare 		break;
14578d5d45fbSJean Delvare 	case 0xe8:
14588d5d45fbSJean Delvare 		name = "pc87363";
14595b581575SJean Delvare 		data->fannr = 2;
14608d5d45fbSJean Delvare 		break;
14618d5d45fbSJean Delvare 	case 0xe4:
14628d5d45fbSJean Delvare 		name = "pc87364";
14638d5d45fbSJean Delvare 		data->fannr = 3;
14648d5d45fbSJean Delvare 		break;
14658d5d45fbSJean Delvare 	case 0xe5:
14668d5d45fbSJean Delvare 		name = "pc87365";
14678d5d45fbSJean Delvare 		data->fannr = extra_isa[0] ? 3 : 0;
14688d5d45fbSJean Delvare 		data->innr = extra_isa[1] ? 11 : 0;
14698d5d45fbSJean Delvare 		data->tempnr = extra_isa[2] ? 2 : 0;
14708d5d45fbSJean Delvare 		break;
14718d5d45fbSJean Delvare 	case 0xe9:
14728d5d45fbSJean Delvare 		name = "pc87366";
14738d5d45fbSJean Delvare 		data->fannr = extra_isa[0] ? 3 : 0;
14748d5d45fbSJean Delvare 		data->innr = extra_isa[1] ? 14 : 0;
14758d5d45fbSJean Delvare 		data->tempnr = extra_isa[2] ? 3 : 0;
14768d5d45fbSJean Delvare 		break;
14778d5d45fbSJean Delvare 	}
14788d5d45fbSJean Delvare 
1479f641b588SJean Delvare 	data->name = name;
1480f641b588SJean Delvare 	mutex_init(&data->lock);
14819a61bf63SIngo Molnar 	mutex_init(&data->update_lock);
1482f641b588SJean Delvare 	platform_set_drvdata(pdev, data);
14838d5d45fbSJean Delvare 
14842a32ec25SJim Cromie 	for (i = 0; i < LDNI_MAX; i++) {
1485449a7a07SGuenter Roeck 		data->address[i] = extra_isa[i];
1486449a7a07SGuenter Roeck 		if (data->address[i]
148761d4d17eSGuenter Roeck 		 && !devm_request_region(dev, extra_isa[i], PC87360_EXTENT,
148802e05005SUwe Kleine-König 					 DRIVER_NAME)) {
1489b55f3757SGuenter Roeck 			dev_err(dev,
1490b55f3757SGuenter Roeck 				"Region 0x%x-0x%x already in use!\n",
1491b55f3757SGuenter Roeck 				extra_isa[i], extra_isa[i]+PC87360_EXTENT-1);
149261d4d17eSGuenter Roeck 			return -EBUSY;
14938d5d45fbSJean Delvare 		}
14948d5d45fbSJean Delvare 	}
14958d5d45fbSJean Delvare 
14968d5d45fbSJean Delvare 	/* Retrieve the fans configuration from Super-I/O space */
14978d5d45fbSJean Delvare 	if (data->fannr)
14988d5d45fbSJean Delvare 		data->fan_conf = confreg[0] | (confreg[1] << 8);
14998d5d45fbSJean Delvare 
15003af2861eSGuenter Roeck 	/*
15013af2861eSGuenter Roeck 	 * Use the correct reference voltage
15023af2861eSGuenter Roeck 	 * Unless both the VLM and the TMS logical devices agree to
15033af2861eSGuenter Roeck 	 * use an external Vref, the internal one is used.
15043af2861eSGuenter Roeck 	 */
15058d5d45fbSJean Delvare 	if (data->innr) {
15068d5d45fbSJean Delvare 		i = pc87360_read_value(data, LD_IN, NO_BANK,
15078d5d45fbSJean Delvare 				       PC87365_REG_IN_CONFIG);
15088d5d45fbSJean Delvare 		if (data->tempnr) {
15098d5d45fbSJean Delvare 			i &= pc87360_read_value(data, LD_TEMP, NO_BANK,
15108d5d45fbSJean Delvare 						PC87365_REG_TEMP_CONFIG);
15118d5d45fbSJean Delvare 		}
15128d5d45fbSJean Delvare 		data->in_vref = (i&0x02) ? 3025 : 2966;
1513f641b588SJean Delvare 		dev_dbg(dev, "Using %s reference voltage\n",
15148d5d45fbSJean Delvare 			(i&0x02) ? "external" : "internal");
15158d5d45fbSJean Delvare 
15168d5d45fbSJean Delvare 		data->vid_conf = confreg[3];
15173d7f9a86SJim Cromie 		data->vrm = vid_which_vrm();
15188d5d45fbSJean Delvare 	}
15198d5d45fbSJean Delvare 
15208d5d45fbSJean Delvare 	/* Fan clock dividers may be needed before any data is read */
15218d5d45fbSJean Delvare 	for (i = 0; i < data->fannr; i++) {
15228d5d45fbSJean Delvare 		if (FAN_CONFIG_MONITOR(data->fan_conf, i))
15238d5d45fbSJean Delvare 			data->fan_status[i] = pc87360_read_value(data,
15248d5d45fbSJean Delvare 					      LD_FAN, NO_BANK,
15258d5d45fbSJean Delvare 					      PC87360_REG_FAN_STATUS(i));
15268d5d45fbSJean Delvare 	}
15278d5d45fbSJean Delvare 
15288d5d45fbSJean Delvare 	if (init > 0) {
15298d5d45fbSJean Delvare 		if (devid == 0xe9 && data->address[1]) /* PC87366 */
15308d5d45fbSJean Delvare 			use_thermistors = confreg[2] & 0x40;
15318d5d45fbSJean Delvare 
1532f641b588SJean Delvare 		pc87360_init_device(pdev, use_thermistors);
15338d5d45fbSJean Delvare 	}
15348d5d45fbSJean Delvare 
1535f3722d5bSJim Cromie 	/* Register all-or-nothing sysfs groups */
1536f3722d5bSJim Cromie 
1537449a7a07SGuenter Roeck 	if (data->innr) {
1538449a7a07SGuenter Roeck 		err = sysfs_create_group(&dev->kobj, &pc8736x_vin_group);
1539449a7a07SGuenter Roeck 		if (err)
154061d4d17eSGuenter Roeck 			goto error;
1541449a7a07SGuenter Roeck 	}
1542f3722d5bSJim Cromie 
1543449a7a07SGuenter Roeck 	if (data->innr == 14) {
1544449a7a07SGuenter Roeck 		err = sysfs_create_group(&dev->kobj, &pc8736x_therm_group);
1545449a7a07SGuenter Roeck 		if (err)
154661d4d17eSGuenter Roeck 			goto error;
1547449a7a07SGuenter Roeck 	}
1548f3722d5bSJim Cromie 
1549f3722d5bSJim Cromie 	/* create device attr-files for varying sysfs groups */
1550f3722d5bSJim Cromie 
1551f3722d5bSJim Cromie 	if (data->tempnr) {
1552f3722d5bSJim Cromie 		for (i = 0; i < data->tempnr; i++) {
1553bce2778dSGuenter Roeck 			err = sysfs_create_group(&dev->kobj,
1554bce2778dSGuenter Roeck 						 &pc8736x_temp_attr_group[i]);
1555bce2778dSGuenter Roeck 			if (err)
155661d4d17eSGuenter Roeck 				goto error;
1557f3722d5bSJim Cromie 		}
1558449a7a07SGuenter Roeck 		err = device_create_file(dev, &dev_attr_alarms_temp);
1559449a7a07SGuenter Roeck 		if (err)
156061d4d17eSGuenter Roeck 			goto error;
1561f3722d5bSJim Cromie 	}
1562f3722d5bSJim Cromie 
1563f3722d5bSJim Cromie 	for (i = 0; i < data->fannr; i++) {
1564bce2778dSGuenter Roeck 		if (FAN_CONFIG_MONITOR(data->fan_conf, i)) {
1565bce2778dSGuenter Roeck 			err = sysfs_create_group(&dev->kobj,
1566bce2778dSGuenter Roeck 						 &pc8736x_fan_attr_group[i]);
1567bce2778dSGuenter Roeck 			if (err)
156861d4d17eSGuenter Roeck 				goto error;
1569bce2778dSGuenter Roeck 		}
1570449a7a07SGuenter Roeck 		if (FAN_CONFIG_CONTROL(data->fan_conf, i)) {
1571449a7a07SGuenter Roeck 			err = device_create_file(dev, &pwm[i].dev_attr);
1572449a7a07SGuenter Roeck 			if (err)
157361d4d17eSGuenter Roeck 				goto error;
1574f3722d5bSJim Cromie 		}
1575449a7a07SGuenter Roeck 	}
1576f3722d5bSJim Cromie 
1577449a7a07SGuenter Roeck 	err = device_create_file(dev, &dev_attr_name);
1578449a7a07SGuenter Roeck 	if (err)
157961d4d17eSGuenter Roeck 		goto error;
1580f641b588SJean Delvare 
15811beeffe4STony Jones 	data->hwmon_dev = hwmon_device_register(dev);
15821beeffe4STony Jones 	if (IS_ERR(data->hwmon_dev)) {
15831beeffe4STony Jones 		err = PTR_ERR(data->hwmon_dev);
158461d4d17eSGuenter Roeck 		goto error;
1585943b0830SMark M. Hoffman 	}
15868d5d45fbSJean Delvare 	return 0;
15878d5d45fbSJean Delvare 
158861d4d17eSGuenter Roeck error:
1589bce2778dSGuenter Roeck 	pc87360_remove_files(dev);
15908d5d45fbSJean Delvare 	return err;
15918d5d45fbSJean Delvare }
15928d5d45fbSJean Delvare 
pc87360_remove(struct platform_device * pdev)1593281dfd0bSBill Pemberton static int pc87360_remove(struct platform_device *pdev)
15948d5d45fbSJean Delvare {
1595f641b588SJean Delvare 	struct pc87360_data *data = platform_get_drvdata(pdev);
15968d5d45fbSJean Delvare 
15971beeffe4STony Jones 	hwmon_device_unregister(data->hwmon_dev);
1598bce2778dSGuenter Roeck 	pc87360_remove_files(&pdev->dev);
15998d5d45fbSJean Delvare 
16008d5d45fbSJean Delvare 	return 0;
16018d5d45fbSJean Delvare }
16028d5d45fbSJean Delvare 
16033af2861eSGuenter Roeck /*
1604070affa8SUwe Kleine-König  * Driver data
16053af2861eSGuenter Roeck  */
1606070affa8SUwe Kleine-König static struct platform_driver pc87360_driver = {
1607070affa8SUwe Kleine-König 	.driver = {
1608070affa8SUwe Kleine-König 		.name	= DRIVER_NAME,
1609070affa8SUwe Kleine-König 	},
1610070affa8SUwe Kleine-König 	.probe		= pc87360_probe,
1611070affa8SUwe Kleine-König 	.remove		= pc87360_remove,
1612070affa8SUwe Kleine-König };
16138d5d45fbSJean Delvare 
16143af2861eSGuenter Roeck /*
1615070affa8SUwe Kleine-König  * Device detection, registration and update
16163af2861eSGuenter Roeck  */
16178d5d45fbSJean Delvare 
pc87360_find(int sioaddr,u8 * devid,unsigned short * addresses)1618070affa8SUwe Kleine-König static int __init pc87360_find(int sioaddr, u8 *devid,
1619070affa8SUwe Kleine-König 			       unsigned short *addresses)
1620070affa8SUwe Kleine-König {
1621070affa8SUwe Kleine-König 	u16 val;
1622070affa8SUwe Kleine-König 	int i;
1623070affa8SUwe Kleine-König 	int nrdev; /* logical device count */
16248d5d45fbSJean Delvare 
1625070affa8SUwe Kleine-König 	/* No superio_enter */
1626070affa8SUwe Kleine-König 
1627070affa8SUwe Kleine-König 	/* Identify device */
1628070affa8SUwe Kleine-König 	val = force_id ? force_id : superio_inb(sioaddr, DEVID);
1629070affa8SUwe Kleine-König 	switch (val) {
1630070affa8SUwe Kleine-König 	case 0xE1: /* PC87360 */
1631070affa8SUwe Kleine-König 	case 0xE8: /* PC87363 */
1632070affa8SUwe Kleine-König 	case 0xE4: /* PC87364 */
1633070affa8SUwe Kleine-König 		nrdev = 1;
1634070affa8SUwe Kleine-König 		break;
1635070affa8SUwe Kleine-König 	case 0xE5: /* PC87365 */
1636070affa8SUwe Kleine-König 	case 0xE9: /* PC87366 */
1637070affa8SUwe Kleine-König 		nrdev = 3;
1638070affa8SUwe Kleine-König 		break;
1639070affa8SUwe Kleine-König 	default:
1640070affa8SUwe Kleine-König 		superio_exit(sioaddr);
1641070affa8SUwe Kleine-König 		return -ENODEV;
1642070affa8SUwe Kleine-König 	}
1643070affa8SUwe Kleine-König 	/* Remember the device id */
1644070affa8SUwe Kleine-König 	*devid = val;
1645070affa8SUwe Kleine-König 
1646070affa8SUwe Kleine-König 	for (i = 0; i < nrdev; i++) {
1647070affa8SUwe Kleine-König 		/* select logical device */
1648070affa8SUwe Kleine-König 		superio_outb(sioaddr, DEV, logdev[i]);
1649070affa8SUwe Kleine-König 
1650070affa8SUwe Kleine-König 		val = superio_inb(sioaddr, ACT);
1651070affa8SUwe Kleine-König 		if (!(val & 0x01)) {
1652070affa8SUwe Kleine-König 			pr_info("Device 0x%02x not activated\n", logdev[i]);
16538d5d45fbSJean Delvare 			continue;
16548d5d45fbSJean Delvare 		}
16558d5d45fbSJean Delvare 
1656070affa8SUwe Kleine-König 		val = (superio_inb(sioaddr, BASE) << 8)
1657070affa8SUwe Kleine-König 		    | superio_inb(sioaddr, BASE + 1);
1658070affa8SUwe Kleine-König 		if (!val) {
1659070affa8SUwe Kleine-König 			pr_info("Base address not set for device 0x%02x\n",
1660070affa8SUwe Kleine-König 				logdev[i]);
1661070affa8SUwe Kleine-König 			continue;
16628d5d45fbSJean Delvare 		}
16638d5d45fbSJean Delvare 
1664070affa8SUwe Kleine-König 		addresses[i] = val;
16658d5d45fbSJean Delvare 
1666070affa8SUwe Kleine-König 		if (i == 0) { /* Fans */
1667070affa8SUwe Kleine-König 			confreg[0] = superio_inb(sioaddr, 0xF0);
1668070affa8SUwe Kleine-König 			confreg[1] = superio_inb(sioaddr, 0xF1);
16698d5d45fbSJean Delvare 
1670070affa8SUwe Kleine-König 			pr_debug("Fan %d: mon=%d ctrl=%d inv=%d\n", 1,
1671070affa8SUwe Kleine-König 				 (confreg[0] >> 2) & 1, (confreg[0] >> 3) & 1,
1672070affa8SUwe Kleine-König 				 (confreg[0] >> 4) & 1);
1673070affa8SUwe Kleine-König 			pr_debug("Fan %d: mon=%d ctrl=%d inv=%d\n", 2,
1674070affa8SUwe Kleine-König 				 (confreg[0] >> 5) & 1, (confreg[0] >> 6) & 1,
1675070affa8SUwe Kleine-König 				 (confreg[0] >> 7) & 1);
1676070affa8SUwe Kleine-König 			pr_debug("Fan %d: mon=%d ctrl=%d inv=%d\n", 3,
1677070affa8SUwe Kleine-König 				 confreg[1] & 1, (confreg[1] >> 1) & 1,
1678070affa8SUwe Kleine-König 				 (confreg[1] >> 2) & 1);
1679070affa8SUwe Kleine-König 		} else if (i == 1) { /* Voltages */
1680070affa8SUwe Kleine-König 			/* Are we using thermistors? */
1681070affa8SUwe Kleine-König 			if (*devid == 0xE9) { /* PC87366 */
16823af2861eSGuenter Roeck 				/*
1683070affa8SUwe Kleine-König 				 * These registers are not logical-device
1684070affa8SUwe Kleine-König 				 * specific, just that we won't need them if
1685070affa8SUwe Kleine-König 				 * we don't use the VLM device
16863af2861eSGuenter Roeck 				 */
1687070affa8SUwe Kleine-König 				confreg[2] = superio_inb(sioaddr, 0x2B);
1688070affa8SUwe Kleine-König 				confreg[3] = superio_inb(sioaddr, 0x25);
1689070affa8SUwe Kleine-König 
1690070affa8SUwe Kleine-König 				if (confreg[2] & 0x40) {
1691070affa8SUwe Kleine-König 					pr_info("Using thermistors for temperature monitoring\n");
1692070affa8SUwe Kleine-König 				}
1693070affa8SUwe Kleine-König 				if (confreg[3] & 0xE0) {
1694070affa8SUwe Kleine-König 					pr_info("VID inputs routed (mode %u)\n",
1695070affa8SUwe Kleine-König 						confreg[3] >> 5);
1696070affa8SUwe Kleine-König 				}
16978d5d45fbSJean Delvare 			}
16988d5d45fbSJean Delvare 		}
16998d5d45fbSJean Delvare 	}
17008d5d45fbSJean Delvare 
1701070affa8SUwe Kleine-König 	superio_exit(sioaddr);
1702070affa8SUwe Kleine-König 	return 0;
17038d5d45fbSJean Delvare }
17048d5d45fbSJean Delvare 
pc87360_device_add(unsigned short address)1705f641b588SJean Delvare static int __init pc87360_device_add(unsigned short address)
1706f641b588SJean Delvare {
1707b9783dceSJean Delvare 	struct resource res[3];
1708b9783dceSJean Delvare 	int err, i, res_count;
1709f641b588SJean Delvare 
1710f641b588SJean Delvare 	pdev = platform_device_alloc("pc87360", address);
1711f641b588SJean Delvare 	if (!pdev) {
1712f641b588SJean Delvare 		err = -ENOMEM;
17139e991c6fSJoe Perches 		pr_err("Device allocation failed\n");
1714f641b588SJean Delvare 		goto exit;
1715f641b588SJean Delvare 	}
1716f641b588SJean Delvare 
1717b9783dceSJean Delvare 	memset(res, 0, 3 * sizeof(struct resource));
1718b9783dceSJean Delvare 	res_count = 0;
1719f641b588SJean Delvare 	for (i = 0; i < 3; i++) {
1720f641b588SJean Delvare 		if (!extra_isa[i])
1721f641b588SJean Delvare 			continue;
1722b9783dceSJean Delvare 		res[res_count].start = extra_isa[i];
1723b9783dceSJean Delvare 		res[res_count].end = extra_isa[i] + PC87360_EXTENT - 1;
172494c08e06SZheng Yongjun 		res[res_count].name = "pc87360";
172594c08e06SZheng Yongjun 		res[res_count].flags = IORESOURCE_IO;
1726b9acb64aSJean Delvare 
1727b9783dceSJean Delvare 		err = acpi_check_resource_conflict(&res[res_count]);
1728b9acb64aSJean Delvare 		if (err)
1729b9acb64aSJean Delvare 			goto exit_device_put;
1730b9acb64aSJean Delvare 
1731b9783dceSJean Delvare 		res_count++;
1732f641b588SJean Delvare 	}
1733b9783dceSJean Delvare 
1734b9783dceSJean Delvare 	err = platform_device_add_resources(pdev, res, res_count);
1735b9783dceSJean Delvare 	if (err) {
17369e991c6fSJoe Perches 		pr_err("Device resources addition failed (%d)\n", err);
1737b9783dceSJean Delvare 		goto exit_device_put;
1738f641b588SJean Delvare 	}
1739f641b588SJean Delvare 
1740f641b588SJean Delvare 	err = platform_device_add(pdev);
1741f641b588SJean Delvare 	if (err) {
17429e991c6fSJoe Perches 		pr_err("Device addition failed (%d)\n", err);
1743f641b588SJean Delvare 		goto exit_device_put;
1744f641b588SJean Delvare 	}
1745f641b588SJean Delvare 
1746f641b588SJean Delvare 	return 0;
1747f641b588SJean Delvare 
1748f641b588SJean Delvare exit_device_put:
1749f641b588SJean Delvare 	platform_device_put(pdev);
1750f641b588SJean Delvare exit:
1751f641b588SJean Delvare 	return err;
1752f641b588SJean Delvare }
1753f641b588SJean Delvare 
pc87360_init(void)17548d5d45fbSJean Delvare static int __init pc87360_init(void)
17558d5d45fbSJean Delvare {
1756f641b588SJean Delvare 	int err, i;
1757f641b588SJean Delvare 	unsigned short address = 0;
17588d5d45fbSJean Delvare 
17598d5d45fbSJean Delvare 	if (pc87360_find(0x2e, &devid, extra_isa)
17608d5d45fbSJean Delvare 	 && pc87360_find(0x4e, &devid, extra_isa)) {
17619e991c6fSJoe Perches 		pr_warn("PC8736x not detected, module not inserted\n");
17628d5d45fbSJean Delvare 		return -ENODEV;
17638d5d45fbSJean Delvare 	}
17648d5d45fbSJean Delvare 
17658d5d45fbSJean Delvare 	/* Arbitrarily pick one of the addresses */
17668d5d45fbSJean Delvare 	for (i = 0; i < 3; i++) {
17678d5d45fbSJean Delvare 		if (extra_isa[i] != 0x0000) {
17682d8672c5SJean Delvare 			address = extra_isa[i];
17698d5d45fbSJean Delvare 			break;
17708d5d45fbSJean Delvare 		}
17718d5d45fbSJean Delvare 	}
17728d5d45fbSJean Delvare 
17732d8672c5SJean Delvare 	if (address == 0x0000) {
17749e991c6fSJoe Perches 		pr_warn("No active logical device, module not inserted\n");
17758d5d45fbSJean Delvare 		return -ENODEV;
17768d5d45fbSJean Delvare 	}
17778d5d45fbSJean Delvare 
1778f641b588SJean Delvare 	err = platform_driver_register(&pc87360_driver);
1779f641b588SJean Delvare 	if (err)
1780f641b588SJean Delvare 		goto exit;
1781f641b588SJean Delvare 
1782f641b588SJean Delvare 	/* Sets global pdev as a side effect */
1783f641b588SJean Delvare 	err = pc87360_device_add(address);
1784f641b588SJean Delvare 	if (err)
1785f641b588SJean Delvare 		goto exit_driver;
1786f641b588SJean Delvare 
1787f641b588SJean Delvare 	return 0;
1788f641b588SJean Delvare 
1789f641b588SJean Delvare  exit_driver:
1790f641b588SJean Delvare 	platform_driver_unregister(&pc87360_driver);
1791f641b588SJean Delvare  exit:
1792f641b588SJean Delvare 	return err;
17938d5d45fbSJean Delvare }
17948d5d45fbSJean Delvare 
pc87360_exit(void)17958d5d45fbSJean Delvare static void __exit pc87360_exit(void)
17968d5d45fbSJean Delvare {
1797f641b588SJean Delvare 	platform_device_unregister(pdev);
1798f641b588SJean Delvare 	platform_driver_unregister(&pc87360_driver);
17998d5d45fbSJean Delvare }
18008d5d45fbSJean Delvare 
18017c81c60fSJean Delvare MODULE_AUTHOR("Jean Delvare <jdelvare@suse.de>");
18028d5d45fbSJean Delvare MODULE_DESCRIPTION("PC8736x hardware monitor");
18038d5d45fbSJean Delvare MODULE_LICENSE("GPL");
180402e05005SUwe Kleine-König MODULE_ALIAS("platform:" DRIVER_NAME);
18058d5d45fbSJean Delvare 
18068d5d45fbSJean Delvare module_init(pc87360_init);
18078d5d45fbSJean Delvare module_exit(pc87360_exit);
1808