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