xref: /openbmc/linux/drivers/hwmon/jc42.c (revision 1ac731c529cd4d6adbce134754b51ff7d822b145)
174ba9207SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
24453d736SGuenter Roeck /*
34453d736SGuenter Roeck  * jc42.c - driver for Jedec JC42.4 compliant temperature sensors
44453d736SGuenter Roeck  *
54453d736SGuenter Roeck  * Copyright (c) 2010  Ericsson AB.
64453d736SGuenter Roeck  *
74453d736SGuenter Roeck  * Derived from lm77.c by Andras BALI <drewie@freemail.hu>.
84453d736SGuenter Roeck  *
94453d736SGuenter Roeck  * JC42.4 compliant temperature sensors are typically used on memory modules.
104453d736SGuenter Roeck  */
114453d736SGuenter Roeck 
1268615eb0SPeter Rosin #include <linux/bitops.h>
1378d448a3SMartin Blumenstingl #include <linux/bitfield.h>
144453d736SGuenter Roeck #include <linux/module.h>
154453d736SGuenter Roeck #include <linux/init.h>
164453d736SGuenter Roeck #include <linux/slab.h>
174453d736SGuenter Roeck #include <linux/jiffies.h>
184453d736SGuenter Roeck #include <linux/i2c.h>
194453d736SGuenter Roeck #include <linux/hwmon.h>
204453d736SGuenter Roeck #include <linux/err.h>
214453d736SGuenter Roeck #include <linux/mutex.h>
22803decceSGuenter Roeck #include <linux/of.h>
238f2fa472SMartin Blumenstingl #include <linux/regmap.h>
244453d736SGuenter Roeck 
254453d736SGuenter Roeck /* Addresses to scan */
264453d736SGuenter Roeck static const unsigned short normal_i2c[] = {
274453d736SGuenter Roeck 	0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, I2C_CLIENT_END };
284453d736SGuenter Roeck 
294453d736SGuenter Roeck /* JC42 registers. All registers are 16 bit. */
304453d736SGuenter Roeck #define JC42_REG_CAP		0x00
314453d736SGuenter Roeck #define JC42_REG_CONFIG		0x01
324453d736SGuenter Roeck #define JC42_REG_TEMP_UPPER	0x02
334453d736SGuenter Roeck #define JC42_REG_TEMP_LOWER	0x03
344453d736SGuenter Roeck #define JC42_REG_TEMP_CRITICAL	0x04
354453d736SGuenter Roeck #define JC42_REG_TEMP		0x05
364453d736SGuenter Roeck #define JC42_REG_MANID		0x06
374453d736SGuenter Roeck #define JC42_REG_DEVICEID	0x07
3868615eb0SPeter Rosin #define JC42_REG_SMBUS		0x22 /* NXP and Atmel, possibly others? */
394453d736SGuenter Roeck 
404453d736SGuenter Roeck /* Status bits in temperature register */
4178d448a3SMartin Blumenstingl #define JC42_ALARM_CRIT		BIT(15)
4278d448a3SMartin Blumenstingl #define JC42_ALARM_MAX		BIT(14)
4378d448a3SMartin Blumenstingl #define JC42_ALARM_MIN		BIT(13)
444453d736SGuenter Roeck 
454453d736SGuenter Roeck /* Configuration register defines */
4678d448a3SMartin Blumenstingl #define JC42_CFG_CRIT_ONLY	BIT(2)
4778d448a3SMartin Blumenstingl #define JC42_CFG_TCRIT_LOCK	BIT(6)
4878d448a3SMartin Blumenstingl #define JC42_CFG_EVENT_LOCK	BIT(7)
4978d448a3SMartin Blumenstingl #define JC42_CFG_SHUTDOWN	BIT(8)
5078d448a3SMartin Blumenstingl #define JC42_CFG_HYST_MASK	GENMASK(10, 9)
514453d736SGuenter Roeck 
524453d736SGuenter Roeck /* Capabilities */
5378d448a3SMartin Blumenstingl #define JC42_CAP_RANGE		BIT(2)
544453d736SGuenter Roeck 
554453d736SGuenter Roeck /* Manufacturer IDs */
564453d736SGuenter Roeck #define ADT_MANID		0x11d4  /* Analog Devices */
571bd612a2SGuenter Roeck #define ATMEL_MANID		0x001f  /* Atmel */
58175c490cSGuenter Roeck #define ATMEL_MANID2		0x1114	/* Atmel */
594453d736SGuenter Roeck #define MAX_MANID		0x004d  /* Maxim */
604453d736SGuenter Roeck #define IDT_MANID		0x00b3  /* IDT */
614453d736SGuenter Roeck #define MCP_MANID		0x0054  /* Microchip */
624453d736SGuenter Roeck #define NXP_MANID		0x1131  /* NXP Semiconductors */
634453d736SGuenter Roeck #define ONS_MANID		0x1b09  /* ON Semiconductor */
644453d736SGuenter Roeck #define STM_MANID		0x104a  /* ST Microelectronics */
65568003ceSGuenter Roeck #define GT_MANID		0x1c68	/* Giantec */
66568003ceSGuenter Roeck #define GT_MANID2		0x132d	/* Giantec, 2nd mfg ID */
67c7250b5dSOleksandr Shamray #define SI_MANID		0x1c85	/* Seiko Instruments */
684453d736SGuenter Roeck 
6968615eb0SPeter Rosin /* SMBUS register */
7068615eb0SPeter Rosin #define SMBUS_STMOUT		BIT(7)  /* SMBus time-out, active low */
7168615eb0SPeter Rosin 
724453d736SGuenter Roeck /* Supported chips */
734453d736SGuenter Roeck 
744453d736SGuenter Roeck /* Analog Devices */
754453d736SGuenter Roeck #define ADT7408_DEVID		0x0801
764453d736SGuenter Roeck #define ADT7408_DEVID_MASK	0xffff
774453d736SGuenter Roeck 
781bd612a2SGuenter Roeck /* Atmel */
791bd612a2SGuenter Roeck #define AT30TS00_DEVID		0x8201
801bd612a2SGuenter Roeck #define AT30TS00_DEVID_MASK	0xffff
811bd612a2SGuenter Roeck 
82175c490cSGuenter Roeck #define AT30TSE004_DEVID	0x2200
83175c490cSGuenter Roeck #define AT30TSE004_DEVID_MASK	0xffff
84175c490cSGuenter Roeck 
85568003ceSGuenter Roeck /* Giantec */
86568003ceSGuenter Roeck #define GT30TS00_DEVID		0x2200
87568003ceSGuenter Roeck #define GT30TS00_DEVID_MASK	0xff00
88568003ceSGuenter Roeck 
89568003ceSGuenter Roeck #define GT34TS02_DEVID		0x3300
90568003ceSGuenter Roeck #define GT34TS02_DEVID_MASK	0xff00
91568003ceSGuenter Roeck 
924453d736SGuenter Roeck /* IDT */
930ea2f1dbSGuenter Roeck #define TSE2004_DEVID		0x2200
940ea2f1dbSGuenter Roeck #define TSE2004_DEVID_MASK	0xff00
954453d736SGuenter Roeck 
960ea2f1dbSGuenter Roeck #define TS3000_DEVID		0x2900  /* Also matches TSE2002 */
970ea2f1dbSGuenter Roeck #define TS3000_DEVID_MASK	0xff00
980ea2f1dbSGuenter Roeck 
990ea2f1dbSGuenter Roeck #define TS3001_DEVID		0x3000
1000ea2f1dbSGuenter Roeck #define TS3001_DEVID_MASK	0xff00
1011bd612a2SGuenter Roeck 
1024453d736SGuenter Roeck /* Maxim */
1034453d736SGuenter Roeck #define MAX6604_DEVID		0x3e00
1044453d736SGuenter Roeck #define MAX6604_DEVID_MASK	0xffff
1054453d736SGuenter Roeck 
1064453d736SGuenter Roeck /* Microchip */
1071bd612a2SGuenter Roeck #define MCP9804_DEVID		0x0200
1081bd612a2SGuenter Roeck #define MCP9804_DEVID_MASK	0xfffc
1091bd612a2SGuenter Roeck 
110a31887dcSAlison Schofield #define MCP9808_DEVID		0x0400
111a31887dcSAlison Schofield #define MCP9808_DEVID_MASK	0xfffc
112a31887dcSAlison Schofield 
1134453d736SGuenter Roeck #define MCP98242_DEVID		0x2000
1144453d736SGuenter Roeck #define MCP98242_DEVID_MASK	0xfffc
1154453d736SGuenter Roeck 
1164453d736SGuenter Roeck #define MCP98243_DEVID		0x2100
1174453d736SGuenter Roeck #define MCP98243_DEVID_MASK	0xfffc
1184453d736SGuenter Roeck 
119d4768280SGuenter Roeck #define MCP98244_DEVID		0x2200
120d4768280SGuenter Roeck #define MCP98244_DEVID_MASK	0xfffc
121d4768280SGuenter Roeck 
1224453d736SGuenter Roeck #define MCP9843_DEVID		0x0000	/* Also matches mcp9805 */
1234453d736SGuenter Roeck #define MCP9843_DEVID_MASK	0xfffe
1244453d736SGuenter Roeck 
1254453d736SGuenter Roeck /* NXP */
1264453d736SGuenter Roeck #define SE97_DEVID		0xa200
1274453d736SGuenter Roeck #define SE97_DEVID_MASK		0xfffc
1284453d736SGuenter Roeck 
1294453d736SGuenter Roeck #define SE98_DEVID		0xa100
1304453d736SGuenter Roeck #define SE98_DEVID_MASK		0xfffc
1314453d736SGuenter Roeck 
1324453d736SGuenter Roeck /* ON Semiconductor */
1334453d736SGuenter Roeck #define CAT6095_DEVID		0x0800	/* Also matches CAT34TS02 */
1344453d736SGuenter Roeck #define CAT6095_DEVID_MASK	0xffe0
1354453d736SGuenter Roeck 
13699b981b2SGuenter Roeck #define CAT34TS02C_DEVID	0x0a00
13799b981b2SGuenter Roeck #define CAT34TS02C_DEVID_MASK	0xfff0
13899b981b2SGuenter Roeck 
139568003ceSGuenter Roeck #define CAT34TS04_DEVID		0x2200
140568003ceSGuenter Roeck #define CAT34TS04_DEVID_MASK	0xfff0
141568003ceSGuenter Roeck 
142bf4d8430SGuenter Roeck #define N34TS04_DEVID		0x2230
143bf4d8430SGuenter Roeck #define N34TS04_DEVID_MASK	0xfff0
144bf4d8430SGuenter Roeck 
1454453d736SGuenter Roeck /* ST Microelectronics */
1464453d736SGuenter Roeck #define STTS424_DEVID		0x0101
1474453d736SGuenter Roeck #define STTS424_DEVID_MASK	0xffff
1484453d736SGuenter Roeck 
1494453d736SGuenter Roeck #define STTS424E_DEVID		0x0000
1504453d736SGuenter Roeck #define STTS424E_DEVID_MASK	0xfffe
1514453d736SGuenter Roeck 
1524de86126SJean Delvare #define STTS2002_DEVID		0x0300
1534de86126SJean Delvare #define STTS2002_DEVID_MASK	0xffff
1544de86126SJean Delvare 
155175c490cSGuenter Roeck #define STTS2004_DEVID		0x2201
156175c490cSGuenter Roeck #define STTS2004_DEVID_MASK	0xffff
157175c490cSGuenter Roeck 
1584de86126SJean Delvare #define STTS3000_DEVID		0x0200
1594de86126SJean Delvare #define STTS3000_DEVID_MASK	0xffff
1604de86126SJean Delvare 
161c7250b5dSOleksandr Shamray /* Seiko Instruments */
162c7250b5dSOleksandr Shamray #define S34TS04A_DEVID		0x2221
163c7250b5dSOleksandr Shamray #define S34TS04A_DEVID_MASK	0xffff
164c7250b5dSOleksandr Shamray 
1654453d736SGuenter Roeck static u16 jc42_hysteresis[] = { 0, 1500, 3000, 6000 };
1664453d736SGuenter Roeck 
1674453d736SGuenter Roeck struct jc42_chips {
1684453d736SGuenter Roeck 	u16 manid;
1694453d736SGuenter Roeck 	u16 devid;
1704453d736SGuenter Roeck 	u16 devid_mask;
1714453d736SGuenter Roeck };
1724453d736SGuenter Roeck 
1734453d736SGuenter Roeck static struct jc42_chips jc42_chips[] = {
1744453d736SGuenter Roeck 	{ ADT_MANID, ADT7408_DEVID, ADT7408_DEVID_MASK },
1751bd612a2SGuenter Roeck 	{ ATMEL_MANID, AT30TS00_DEVID, AT30TS00_DEVID_MASK },
176175c490cSGuenter Roeck 	{ ATMEL_MANID2, AT30TSE004_DEVID, AT30TSE004_DEVID_MASK },
177568003ceSGuenter Roeck 	{ GT_MANID, GT30TS00_DEVID, GT30TS00_DEVID_MASK },
178568003ceSGuenter Roeck 	{ GT_MANID2, GT34TS02_DEVID, GT34TS02_DEVID_MASK },
1790ea2f1dbSGuenter Roeck 	{ IDT_MANID, TSE2004_DEVID, TSE2004_DEVID_MASK },
1800ea2f1dbSGuenter Roeck 	{ IDT_MANID, TS3000_DEVID, TS3000_DEVID_MASK },
1810ea2f1dbSGuenter Roeck 	{ IDT_MANID, TS3001_DEVID, TS3001_DEVID_MASK },
1824453d736SGuenter Roeck 	{ MAX_MANID, MAX6604_DEVID, MAX6604_DEVID_MASK },
1831bd612a2SGuenter Roeck 	{ MCP_MANID, MCP9804_DEVID, MCP9804_DEVID_MASK },
184a31887dcSAlison Schofield 	{ MCP_MANID, MCP9808_DEVID, MCP9808_DEVID_MASK },
1854453d736SGuenter Roeck 	{ MCP_MANID, MCP98242_DEVID, MCP98242_DEVID_MASK },
1864453d736SGuenter Roeck 	{ MCP_MANID, MCP98243_DEVID, MCP98243_DEVID_MASK },
187d4768280SGuenter Roeck 	{ MCP_MANID, MCP98244_DEVID, MCP98244_DEVID_MASK },
1884453d736SGuenter Roeck 	{ MCP_MANID, MCP9843_DEVID, MCP9843_DEVID_MASK },
1894453d736SGuenter Roeck 	{ NXP_MANID, SE97_DEVID, SE97_DEVID_MASK },
1904453d736SGuenter Roeck 	{ ONS_MANID, CAT6095_DEVID, CAT6095_DEVID_MASK },
19199b981b2SGuenter Roeck 	{ ONS_MANID, CAT34TS02C_DEVID, CAT34TS02C_DEVID_MASK },
192568003ceSGuenter Roeck 	{ ONS_MANID, CAT34TS04_DEVID, CAT34TS04_DEVID_MASK },
193bf4d8430SGuenter Roeck 	{ ONS_MANID, N34TS04_DEVID, N34TS04_DEVID_MASK },
1944453d736SGuenter Roeck 	{ NXP_MANID, SE98_DEVID, SE98_DEVID_MASK },
195c7250b5dSOleksandr Shamray 	{ SI_MANID,  S34TS04A_DEVID, S34TS04A_DEVID_MASK },
1964453d736SGuenter Roeck 	{ STM_MANID, STTS424_DEVID, STTS424_DEVID_MASK },
1974453d736SGuenter Roeck 	{ STM_MANID, STTS424E_DEVID, STTS424E_DEVID_MASK },
1984de86126SJean Delvare 	{ STM_MANID, STTS2002_DEVID, STTS2002_DEVID_MASK },
199175c490cSGuenter Roeck 	{ STM_MANID, STTS2004_DEVID, STTS2004_DEVID_MASK },
2004de86126SJean Delvare 	{ STM_MANID, STTS3000_DEVID, STTS3000_DEVID_MASK },
2014453d736SGuenter Roeck };
2024453d736SGuenter Roeck 
2034453d736SGuenter Roeck /* Each client has this additional data */
2044453d736SGuenter Roeck struct jc42_data {
2054453d736SGuenter Roeck 	struct mutex	update_lock;	/* protect register access */
2068f2fa472SMartin Blumenstingl 	struct regmap	*regmap;
2074453d736SGuenter Roeck 	bool		extended;	/* true if extended range supported */
2084453d736SGuenter Roeck 	bool		valid;
2094453d736SGuenter Roeck 	u16		orig_config;	/* original configuration */
2104453d736SGuenter Roeck 	u16		config;		/* current configuration */
2114453d736SGuenter Roeck };
2124453d736SGuenter Roeck 
2134453d736SGuenter Roeck #define JC42_TEMP_MIN_EXTENDED	(-40000)
2144453d736SGuenter Roeck #define JC42_TEMP_MIN		0
2154453d736SGuenter Roeck #define JC42_TEMP_MAX		125000
2164453d736SGuenter Roeck 
jc42_temp_to_reg(long temp,bool extended)2173a05633bSGuenter Roeck static u16 jc42_temp_to_reg(long temp, bool extended)
2184453d736SGuenter Roeck {
2192a844c14SGuenter Roeck 	int ntemp = clamp_val(temp,
2204453d736SGuenter Roeck 			      extended ? JC42_TEMP_MIN_EXTENDED :
2214453d736SGuenter Roeck 			      JC42_TEMP_MIN, JC42_TEMP_MAX);
2224453d736SGuenter Roeck 
2234453d736SGuenter Roeck 	/* convert from 0.001 to 0.0625 resolution */
2244453d736SGuenter Roeck 	return (ntemp * 2 / 125) & 0x1fff;
2254453d736SGuenter Roeck }
2264453d736SGuenter Roeck 
jc42_temp_from_reg(s16 reg)2274453d736SGuenter Roeck static int jc42_temp_from_reg(s16 reg)
2284453d736SGuenter Roeck {
229bca6a1adSGuenter Roeck 	reg = sign_extend32(reg, 12);
2304453d736SGuenter Roeck 
2314453d736SGuenter Roeck 	/* convert from 0.0625 to 0.001 resolution */
2324453d736SGuenter Roeck 	return reg * 125 / 2;
2334453d736SGuenter Roeck }
2344453d736SGuenter Roeck 
jc42_read(struct device * dev,enum hwmon_sensor_types type,u32 attr,int channel,long * val)235fcc448cfSGuenter Roeck static int jc42_read(struct device *dev, enum hwmon_sensor_types type,
236fcc448cfSGuenter Roeck 		     u32 attr, int channel, long *val)
23710192bc6SGuenter Roeck {
2388f2fa472SMartin Blumenstingl 	struct jc42_data *data = dev_get_drvdata(dev);
2398f2fa472SMartin Blumenstingl 	unsigned int regval;
2408f2fa472SMartin Blumenstingl 	int ret, temp, hyst;
2414453d736SGuenter Roeck 
2428f2fa472SMartin Blumenstingl 	mutex_lock(&data->update_lock);
2434453d736SGuenter Roeck 
244fcc448cfSGuenter Roeck 	switch (attr) {
245fcc448cfSGuenter Roeck 	case hwmon_temp_input:
2468f2fa472SMartin Blumenstingl 		ret = regmap_read(data->regmap, JC42_REG_TEMP, &regval);
2478f2fa472SMartin Blumenstingl 		if (ret)
2488f2fa472SMartin Blumenstingl 			break;
2498f2fa472SMartin Blumenstingl 
2508f2fa472SMartin Blumenstingl 		*val = jc42_temp_from_reg(regval);
2518f2fa472SMartin Blumenstingl 		break;
252fcc448cfSGuenter Roeck 	case hwmon_temp_min:
2538f2fa472SMartin Blumenstingl 		ret = regmap_read(data->regmap, JC42_REG_TEMP_LOWER, &regval);
2548f2fa472SMartin Blumenstingl 		if (ret)
2558f2fa472SMartin Blumenstingl 			break;
2568f2fa472SMartin Blumenstingl 
2578f2fa472SMartin Blumenstingl 		*val = jc42_temp_from_reg(regval);
2588f2fa472SMartin Blumenstingl 		break;
259fcc448cfSGuenter Roeck 	case hwmon_temp_max:
2608f2fa472SMartin Blumenstingl 		ret = regmap_read(data->regmap, JC42_REG_TEMP_UPPER, &regval);
2618f2fa472SMartin Blumenstingl 		if (ret)
2628f2fa472SMartin Blumenstingl 			break;
2638f2fa472SMartin Blumenstingl 
2648f2fa472SMartin Blumenstingl 		*val = jc42_temp_from_reg(regval);
2658f2fa472SMartin Blumenstingl 		break;
266fcc448cfSGuenter Roeck 	case hwmon_temp_crit:
2678f2fa472SMartin Blumenstingl 		ret = regmap_read(data->regmap, JC42_REG_TEMP_CRITICAL,
2688f2fa472SMartin Blumenstingl 				  &regval);
2698f2fa472SMartin Blumenstingl 		if (ret)
2708f2fa472SMartin Blumenstingl 			break;
2718f2fa472SMartin Blumenstingl 
2728f2fa472SMartin Blumenstingl 		*val = jc42_temp_from_reg(regval);
2738f2fa472SMartin Blumenstingl 		break;
274fcc448cfSGuenter Roeck 	case hwmon_temp_max_hyst:
2758f2fa472SMartin Blumenstingl 		ret = regmap_read(data->regmap, JC42_REG_TEMP_UPPER, &regval);
2768f2fa472SMartin Blumenstingl 		if (ret)
2778f2fa472SMartin Blumenstingl 			break;
2788f2fa472SMartin Blumenstingl 
2798f2fa472SMartin Blumenstingl 		temp = jc42_temp_from_reg(regval);
28078d448a3SMartin Blumenstingl 		hyst = jc42_hysteresis[FIELD_GET(JC42_CFG_HYST_MASK,
28178d448a3SMartin Blumenstingl 						 data->config)];
282fcc448cfSGuenter Roeck 		*val = temp - hyst;
2838f2fa472SMartin Blumenstingl 		break;
284fcc448cfSGuenter Roeck 	case hwmon_temp_crit_hyst:
2858f2fa472SMartin Blumenstingl 		ret = regmap_read(data->regmap, JC42_REG_TEMP_CRITICAL,
2868f2fa472SMartin Blumenstingl 				  &regval);
2878f2fa472SMartin Blumenstingl 		if (ret)
2888f2fa472SMartin Blumenstingl 			break;
2898f2fa472SMartin Blumenstingl 
2908f2fa472SMartin Blumenstingl 		temp = jc42_temp_from_reg(regval);
29178d448a3SMartin Blumenstingl 		hyst = jc42_hysteresis[FIELD_GET(JC42_CFG_HYST_MASK,
29278d448a3SMartin Blumenstingl 						 data->config)];
293fcc448cfSGuenter Roeck 		*val = temp - hyst;
2948f2fa472SMartin Blumenstingl 		break;
295fcc448cfSGuenter Roeck 	case hwmon_temp_min_alarm:
2968f2fa472SMartin Blumenstingl 		ret = regmap_read(data->regmap, JC42_REG_TEMP, &regval);
2978f2fa472SMartin Blumenstingl 		if (ret)
2988f2fa472SMartin Blumenstingl 			break;
2998f2fa472SMartin Blumenstingl 
30078d448a3SMartin Blumenstingl 		*val = FIELD_GET(JC42_ALARM_MIN, regval);
3018f2fa472SMartin Blumenstingl 		break;
302fcc448cfSGuenter Roeck 	case hwmon_temp_max_alarm:
3038f2fa472SMartin Blumenstingl 		ret = regmap_read(data->regmap, JC42_REG_TEMP, &regval);
3048f2fa472SMartin Blumenstingl 		if (ret)
3058f2fa472SMartin Blumenstingl 			break;
3068f2fa472SMartin Blumenstingl 
30778d448a3SMartin Blumenstingl 		*val = FIELD_GET(JC42_ALARM_MAX, regval);
3088f2fa472SMartin Blumenstingl 		break;
309fcc448cfSGuenter Roeck 	case hwmon_temp_crit_alarm:
3108f2fa472SMartin Blumenstingl 		ret = regmap_read(data->regmap, JC42_REG_TEMP, &regval);
3118f2fa472SMartin Blumenstingl 		if (ret)
3128f2fa472SMartin Blumenstingl 			break;
3138f2fa472SMartin Blumenstingl 
31478d448a3SMartin Blumenstingl 		*val = FIELD_GET(JC42_ALARM_CRIT, regval);
3158f2fa472SMartin Blumenstingl 		break;
316fcc448cfSGuenter Roeck 	default:
3178f2fa472SMartin Blumenstingl 		ret = -EOPNOTSUPP;
3188f2fa472SMartin Blumenstingl 		break;
319fcc448cfSGuenter Roeck 	}
3208f2fa472SMartin Blumenstingl 
3218f2fa472SMartin Blumenstingl 	mutex_unlock(&data->update_lock);
3228f2fa472SMartin Blumenstingl 
3238f2fa472SMartin Blumenstingl 	return ret;
3244453d736SGuenter Roeck }
3254453d736SGuenter Roeck 
jc42_write(struct device * dev,enum hwmon_sensor_types type,u32 attr,int channel,long val)326fcc448cfSGuenter Roeck static int jc42_write(struct device *dev, enum hwmon_sensor_types type,
327fcc448cfSGuenter Roeck 		      u32 attr, int channel, long val)
3284453d736SGuenter Roeck {
32910192bc6SGuenter Roeck 	struct jc42_data *data = dev_get_drvdata(dev);
3308f2fa472SMartin Blumenstingl 	unsigned int regval;
331fcc448cfSGuenter Roeck 	int diff, hyst;
332fcc448cfSGuenter Roeck 	int ret;
3334453d736SGuenter Roeck 
33410192bc6SGuenter Roeck 	mutex_lock(&data->update_lock);
3354453d736SGuenter Roeck 
336fcc448cfSGuenter Roeck 	switch (attr) {
337fcc448cfSGuenter Roeck 	case hwmon_temp_min:
3388f2fa472SMartin Blumenstingl 		ret = regmap_write(data->regmap, JC42_REG_TEMP_LOWER,
3398f2fa472SMartin Blumenstingl 				   jc42_temp_to_reg(val, data->extended));
340fcc448cfSGuenter Roeck 		break;
341fcc448cfSGuenter Roeck 	case hwmon_temp_max:
3428f2fa472SMartin Blumenstingl 		ret = regmap_write(data->regmap, JC42_REG_TEMP_UPPER,
3438f2fa472SMartin Blumenstingl 				   jc42_temp_to_reg(val, data->extended));
344fcc448cfSGuenter Roeck 		break;
345fcc448cfSGuenter Roeck 	case hwmon_temp_crit:
3468f2fa472SMartin Blumenstingl 		ret = regmap_write(data->regmap, JC42_REG_TEMP_CRITICAL,
3478f2fa472SMartin Blumenstingl 				   jc42_temp_to_reg(val, data->extended));
348fcc448cfSGuenter Roeck 		break;
349fcc448cfSGuenter Roeck 	case hwmon_temp_crit_hyst:
3508f2fa472SMartin Blumenstingl 		ret = regmap_read(data->regmap, JC42_REG_TEMP_CRITICAL,
3518f2fa472SMartin Blumenstingl 				  &regval);
3528f2fa472SMartin Blumenstingl 		if (ret)
353b744db17SYang Yingliang 			break;
3548f2fa472SMartin Blumenstingl 
3555d577dbaSGuenter Roeck 		/*
3565d577dbaSGuenter Roeck 		 * JC42.4 compliant chips only support four hysteresis values.
3575d577dbaSGuenter Roeck 		 * Pick best choice and go from there.
3585d577dbaSGuenter Roeck 		 */
359fcc448cfSGuenter Roeck 		val = clamp_val(val, (data->extended ? JC42_TEMP_MIN_EXTENDED
360fcc448cfSGuenter Roeck 						     : JC42_TEMP_MIN) - 6000,
361fcc448cfSGuenter Roeck 				JC42_TEMP_MAX);
3628f2fa472SMartin Blumenstingl 		diff = jc42_temp_from_reg(regval) - val;
3634453d736SGuenter Roeck 		hyst = 0;
3644453d736SGuenter Roeck 		if (diff > 0) {
3654453d736SGuenter Roeck 			if (diff < 2250)
3664453d736SGuenter Roeck 				hyst = 1;	/* 1.5 degrees C */
3674453d736SGuenter Roeck 			else if (diff < 4500)
3684453d736SGuenter Roeck 				hyst = 2;	/* 3.0 degrees C */
3694453d736SGuenter Roeck 			else
3704453d736SGuenter Roeck 				hyst = 3;	/* 6.0 degrees C */
3714453d736SGuenter Roeck 		}
372fcc448cfSGuenter Roeck 		data->config = (data->config & ~JC42_CFG_HYST_MASK) |
37378d448a3SMartin Blumenstingl 				FIELD_PREP(JC42_CFG_HYST_MASK, hyst);
3748f2fa472SMartin Blumenstingl 		ret = regmap_write(data->regmap, JC42_REG_CONFIG,
37590f4102cSJean Delvare 				   data->config);
376fcc448cfSGuenter Roeck 		break;
377fcc448cfSGuenter Roeck 	default:
378fcc448cfSGuenter Roeck 		ret = -EOPNOTSUPP;
379fcc448cfSGuenter Roeck 		break;
380fcc448cfSGuenter Roeck 	}
381fcc448cfSGuenter Roeck 
3824453d736SGuenter Roeck 	mutex_unlock(&data->update_lock);
383fcc448cfSGuenter Roeck 
3844453d736SGuenter Roeck 	return ret;
3854453d736SGuenter Roeck }
3864453d736SGuenter Roeck 
jc42_is_visible(const void * _data,enum hwmon_sensor_types type,u32 attr,int channel)387fcc448cfSGuenter Roeck static umode_t jc42_is_visible(const void *_data, enum hwmon_sensor_types type,
388fcc448cfSGuenter Roeck 			       u32 attr, int channel)
3894453d736SGuenter Roeck {
390fcc448cfSGuenter Roeck 	const struct jc42_data *data = _data;
3912c6315daSClemens Ladisch 	unsigned int config = data->config;
3924820d511SGuenter Roeck 	umode_t mode = 0444;
3932c6315daSClemens Ladisch 
394fcc448cfSGuenter Roeck 	switch (attr) {
395fcc448cfSGuenter Roeck 	case hwmon_temp_min:
396fcc448cfSGuenter Roeck 	case hwmon_temp_max:
397fcc448cfSGuenter Roeck 		if (!(config & JC42_CFG_EVENT_LOCK))
3984820d511SGuenter Roeck 			mode |= 0200;
399fcc448cfSGuenter Roeck 		break;
400fcc448cfSGuenter Roeck 	case hwmon_temp_crit:
401fcc448cfSGuenter Roeck 		if (!(config & JC42_CFG_TCRIT_LOCK))
4024820d511SGuenter Roeck 			mode |= 0200;
403fcc448cfSGuenter Roeck 		break;
404fcc448cfSGuenter Roeck 	case hwmon_temp_crit_hyst:
405fcc448cfSGuenter Roeck 		if (!(config & (JC42_CFG_EVENT_LOCK | JC42_CFG_TCRIT_LOCK)))
4064820d511SGuenter Roeck 			mode |= 0200;
407fcc448cfSGuenter Roeck 		break;
408fcc448cfSGuenter Roeck 	case hwmon_temp_input:
409fcc448cfSGuenter Roeck 	case hwmon_temp_max_hyst:
410fcc448cfSGuenter Roeck 	case hwmon_temp_min_alarm:
411fcc448cfSGuenter Roeck 	case hwmon_temp_max_alarm:
412fcc448cfSGuenter Roeck 	case hwmon_temp_crit_alarm:
413fcc448cfSGuenter Roeck 		break;
414fcc448cfSGuenter Roeck 	default:
415fcc448cfSGuenter Roeck 		mode = 0;
416fcc448cfSGuenter Roeck 		break;
4172c6315daSClemens Ladisch 	}
418fcc448cfSGuenter Roeck 	return mode;
419fcc448cfSGuenter Roeck }
4204453d736SGuenter Roeck 
4214453d736SGuenter Roeck /* Return 0 if detection is successful, -ENODEV otherwise */
jc42_detect(struct i2c_client * client,struct i2c_board_info * info)422f15df57dSGuenter Roeck static int jc42_detect(struct i2c_client *client, struct i2c_board_info *info)
4234453d736SGuenter Roeck {
424f15df57dSGuenter Roeck 	struct i2c_adapter *adapter = client->adapter;
4254453d736SGuenter Roeck 	int i, config, cap, manid, devid;
4264453d736SGuenter Roeck 
4274453d736SGuenter Roeck 	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA |
4284453d736SGuenter Roeck 				     I2C_FUNC_SMBUS_WORD_DATA))
4294453d736SGuenter Roeck 		return -ENODEV;
4304453d736SGuenter Roeck 
431f15df57dSGuenter Roeck 	cap = i2c_smbus_read_word_swapped(client, JC42_REG_CAP);
432f15df57dSGuenter Roeck 	config = i2c_smbus_read_word_swapped(client, JC42_REG_CONFIG);
433f15df57dSGuenter Roeck 	manid = i2c_smbus_read_word_swapped(client, JC42_REG_MANID);
434f15df57dSGuenter Roeck 	devid = i2c_smbus_read_word_swapped(client, JC42_REG_DEVICEID);
4354453d736SGuenter Roeck 
4364453d736SGuenter Roeck 	if (cap < 0 || config < 0 || manid < 0 || devid < 0)
4374453d736SGuenter Roeck 		return -ENODEV;
4384453d736SGuenter Roeck 
4394453d736SGuenter Roeck 	if ((cap & 0xff00) || (config & 0xf800))
4404453d736SGuenter Roeck 		return -ENODEV;
4414453d736SGuenter Roeck 
4424453d736SGuenter Roeck 	for (i = 0; i < ARRAY_SIZE(jc42_chips); i++) {
4434453d736SGuenter Roeck 		struct jc42_chips *chip = &jc42_chips[i];
4444453d736SGuenter Roeck 		if (manid == chip->manid &&
4454453d736SGuenter Roeck 		    (devid & chip->devid_mask) == chip->devid) {
446f2f394dbSWolfram Sang 			strscpy(info->type, "jc42", I2C_NAME_SIZE);
4474453d736SGuenter Roeck 			return 0;
4484453d736SGuenter Roeck 		}
4494453d736SGuenter Roeck 	}
4504453d736SGuenter Roeck 	return -ENODEV;
4514453d736SGuenter Roeck }
4524453d736SGuenter Roeck 
453*b3dc5eeeSKrzysztof Kozlowski static const struct hwmon_channel_info * const jc42_info[] = {
454032c1623SEduardo Valentin 	HWMON_CHANNEL_INFO(chip,
455032c1623SEduardo Valentin 			   HWMON_C_REGISTER_TZ | HWMON_C_UPDATE_INTERVAL),
4561eade10fSGuenter Roeck 	HWMON_CHANNEL_INFO(temp,
4571eade10fSGuenter Roeck 			   HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX |
4581eade10fSGuenter Roeck 			   HWMON_T_CRIT | HWMON_T_MAX_HYST |
4591eade10fSGuenter Roeck 			   HWMON_T_CRIT_HYST | HWMON_T_MIN_ALARM |
4601eade10fSGuenter Roeck 			   HWMON_T_MAX_ALARM | HWMON_T_CRIT_ALARM),
461fcc448cfSGuenter Roeck 	NULL
462fcc448cfSGuenter Roeck };
463fcc448cfSGuenter Roeck 
464fcc448cfSGuenter Roeck static const struct hwmon_ops jc42_hwmon_ops = {
465fcc448cfSGuenter Roeck 	.is_visible = jc42_is_visible,
466fcc448cfSGuenter Roeck 	.read = jc42_read,
467fcc448cfSGuenter Roeck 	.write = jc42_write,
468fcc448cfSGuenter Roeck };
469fcc448cfSGuenter Roeck 
470fcc448cfSGuenter Roeck static const struct hwmon_chip_info jc42_chip_info = {
471fcc448cfSGuenter Roeck 	.ops = &jc42_hwmon_ops,
472fcc448cfSGuenter Roeck 	.info = jc42_info,
473fcc448cfSGuenter Roeck };
474fcc448cfSGuenter Roeck 
jc42_readable_reg(struct device * dev,unsigned int reg)4758f2fa472SMartin Blumenstingl static bool jc42_readable_reg(struct device *dev, unsigned int reg)
4768f2fa472SMartin Blumenstingl {
4778f2fa472SMartin Blumenstingl 	return (reg >= JC42_REG_CAP && reg <= JC42_REG_DEVICEID) ||
4788f2fa472SMartin Blumenstingl 		reg == JC42_REG_SMBUS;
4798f2fa472SMartin Blumenstingl }
4808f2fa472SMartin Blumenstingl 
jc42_writable_reg(struct device * dev,unsigned int reg)4818f2fa472SMartin Blumenstingl static bool jc42_writable_reg(struct device *dev, unsigned int reg)
4828f2fa472SMartin Blumenstingl {
4838f2fa472SMartin Blumenstingl 	return (reg >= JC42_REG_CONFIG && reg <= JC42_REG_TEMP_CRITICAL) ||
4848f2fa472SMartin Blumenstingl 		reg == JC42_REG_SMBUS;
4858f2fa472SMartin Blumenstingl }
4868f2fa472SMartin Blumenstingl 
jc42_volatile_reg(struct device * dev,unsigned int reg)4878f2fa472SMartin Blumenstingl static bool jc42_volatile_reg(struct device *dev, unsigned int reg)
4888f2fa472SMartin Blumenstingl {
4898f2fa472SMartin Blumenstingl 	return reg == JC42_REG_CONFIG || reg == JC42_REG_TEMP;
4908f2fa472SMartin Blumenstingl }
4918f2fa472SMartin Blumenstingl 
4928f2fa472SMartin Blumenstingl static const struct regmap_config jc42_regmap_config = {
4938f2fa472SMartin Blumenstingl 	.reg_bits = 8,
4948f2fa472SMartin Blumenstingl 	.val_bits = 16,
4958f2fa472SMartin Blumenstingl 	.val_format_endian = REGMAP_ENDIAN_BIG,
4968f2fa472SMartin Blumenstingl 	.max_register = JC42_REG_SMBUS,
4978f2fa472SMartin Blumenstingl 	.writeable_reg = jc42_writable_reg,
4988f2fa472SMartin Blumenstingl 	.readable_reg = jc42_readable_reg,
4998f2fa472SMartin Blumenstingl 	.volatile_reg = jc42_volatile_reg,
5008f2fa472SMartin Blumenstingl 	.cache_type = REGCACHE_RBTREE,
5018f2fa472SMartin Blumenstingl };
5028f2fa472SMartin Blumenstingl 
jc42_probe(struct i2c_client * client)50367487038SStephen Kitt static int jc42_probe(struct i2c_client *client)
5044453d736SGuenter Roeck {
505f15df57dSGuenter Roeck 	struct device *dev = &client->dev;
50662f9a57cSGuenter Roeck 	struct device *hwmon_dev;
5078f2fa472SMartin Blumenstingl 	unsigned int config, cap;
50862f9a57cSGuenter Roeck 	struct jc42_data *data;
5098f2fa472SMartin Blumenstingl 	int ret;
5104453d736SGuenter Roeck 
511f15df57dSGuenter Roeck 	data = devm_kzalloc(dev, sizeof(struct jc42_data), GFP_KERNEL);
512f15df57dSGuenter Roeck 	if (!data)
513f15df57dSGuenter Roeck 		return -ENOMEM;
5144453d736SGuenter Roeck 
5158f2fa472SMartin Blumenstingl 	data->regmap = devm_regmap_init_i2c(client, &jc42_regmap_config);
5168f2fa472SMartin Blumenstingl 	if (IS_ERR(data->regmap))
5178f2fa472SMartin Blumenstingl 		return PTR_ERR(data->regmap);
5188f2fa472SMartin Blumenstingl 
519f15df57dSGuenter Roeck 	i2c_set_clientdata(client, data);
5204453d736SGuenter Roeck 	mutex_init(&data->update_lock);
5214453d736SGuenter Roeck 
5228f2fa472SMartin Blumenstingl 	ret = regmap_read(data->regmap, JC42_REG_CAP, &cap);
5238f2fa472SMartin Blumenstingl 	if (ret)
5248f2fa472SMartin Blumenstingl 		return ret;
525f15df57dSGuenter Roeck 
5264453d736SGuenter Roeck 	data->extended = !!(cap & JC42_CAP_RANGE);
5274453d736SGuenter Roeck 
52868615eb0SPeter Rosin 	if (device_property_read_bool(dev, "smbus-timeout-disable")) {
52968615eb0SPeter Rosin 		/*
53068615eb0SPeter Rosin 		 * Not all chips support this register, but from a
53168615eb0SPeter Rosin 		 * quick read of various datasheets no chip appears
53268615eb0SPeter Rosin 		 * incompatible with the below attempt to disable
53368615eb0SPeter Rosin 		 * the timeout. And the whole thing is opt-in...
53468615eb0SPeter Rosin 		 */
5358f2fa472SMartin Blumenstingl 		ret = regmap_set_bits(data->regmap, JC42_REG_SMBUS,
5368f2fa472SMartin Blumenstingl 				      SMBUS_STMOUT);
5378f2fa472SMartin Blumenstingl 		if (ret)
5388f2fa472SMartin Blumenstingl 			return ret;
53968615eb0SPeter Rosin 	}
54068615eb0SPeter Rosin 
5418f2fa472SMartin Blumenstingl 	ret = regmap_read(data->regmap, JC42_REG_CONFIG, &config);
5428f2fa472SMartin Blumenstingl 	if (ret)
5438f2fa472SMartin Blumenstingl 		return ret;
544f15df57dSGuenter Roeck 
5454453d736SGuenter Roeck 	data->orig_config = config;
5464453d736SGuenter Roeck 	if (config & JC42_CFG_SHUTDOWN) {
5474453d736SGuenter Roeck 		config &= ~JC42_CFG_SHUTDOWN;
5488f2fa472SMartin Blumenstingl 		regmap_write(data->regmap, JC42_REG_CONFIG, config);
5494453d736SGuenter Roeck 	}
5504453d736SGuenter Roeck 	data->config = config;
5514453d736SGuenter Roeck 
552c843b382SSascha Hauer 	hwmon_dev = devm_hwmon_device_register_with_info(dev, "jc42",
553fcc448cfSGuenter Roeck 							 data, &jc42_chip_info,
554fcc448cfSGuenter Roeck 							 NULL);
555650a2c02SFengguang Wu 	return PTR_ERR_OR_ZERO(hwmon_dev);
5564453d736SGuenter Roeck }
5574453d736SGuenter Roeck 
jc42_remove(struct i2c_client * client)558ed5c2f5fSUwe Kleine-König static void jc42_remove(struct i2c_client *client)
5594453d736SGuenter Roeck {
5604453d736SGuenter Roeck 	struct jc42_data *data = i2c_get_clientdata(client);
5615953e276SJean Delvare 
5625953e276SJean Delvare 	/* Restore original configuration except hysteresis */
5635953e276SJean Delvare 	if ((data->config & ~JC42_CFG_HYST_MASK) !=
5645953e276SJean Delvare 	    (data->orig_config & ~JC42_CFG_HYST_MASK)) {
5655953e276SJean Delvare 		int config;
5665953e276SJean Delvare 
5675953e276SJean Delvare 		config = (data->orig_config & ~JC42_CFG_HYST_MASK)
5685953e276SJean Delvare 		  | (data->config & JC42_CFG_HYST_MASK);
5698f2fa472SMartin Blumenstingl 		regmap_write(data->regmap, JC42_REG_CONFIG, config);
5705953e276SJean Delvare 	}
5714453d736SGuenter Roeck }
5724453d736SGuenter Roeck 
573d397276bSGuenter Roeck #ifdef CONFIG_PM
574d397276bSGuenter Roeck 
jc42_suspend(struct device * dev)575d397276bSGuenter Roeck static int jc42_suspend(struct device *dev)
5764453d736SGuenter Roeck {
57762f9a57cSGuenter Roeck 	struct jc42_data *data = dev_get_drvdata(dev);
5784453d736SGuenter Roeck 
579d397276bSGuenter Roeck 	data->config |= JC42_CFG_SHUTDOWN;
5808f2fa472SMartin Blumenstingl 	regmap_write(data->regmap, JC42_REG_CONFIG, data->config);
581084ed144SMartin Blumenstingl 
582084ed144SMartin Blumenstingl 	regcache_cache_only(data->regmap, true);
583084ed144SMartin Blumenstingl 	regcache_mark_dirty(data->regmap);
584084ed144SMartin Blumenstingl 
585d397276bSGuenter Roeck 	return 0;
586d397276bSGuenter Roeck }
5874453d736SGuenter Roeck 
jc42_resume(struct device * dev)588d397276bSGuenter Roeck static int jc42_resume(struct device *dev)
589d397276bSGuenter Roeck {
590d397276bSGuenter Roeck 	struct jc42_data *data = dev_get_drvdata(dev);
5914453d736SGuenter Roeck 
592084ed144SMartin Blumenstingl 	regcache_cache_only(data->regmap, false);
593084ed144SMartin Blumenstingl 
594d397276bSGuenter Roeck 	data->config &= ~JC42_CFG_SHUTDOWN;
5958f2fa472SMartin Blumenstingl 	regmap_write(data->regmap, JC42_REG_CONFIG, data->config);
596084ed144SMartin Blumenstingl 
597084ed144SMartin Blumenstingl 	/* Restore cached register values to hardware */
598084ed144SMartin Blumenstingl 	return regcache_sync(data->regmap);
5994453d736SGuenter Roeck }
6004453d736SGuenter Roeck 
601d397276bSGuenter Roeck static const struct dev_pm_ops jc42_dev_pm_ops = {
602d397276bSGuenter Roeck 	.suspend = jc42_suspend,
603d397276bSGuenter Roeck 	.resume = jc42_resume,
604d397276bSGuenter Roeck };
6054453d736SGuenter Roeck 
606d397276bSGuenter Roeck #define JC42_DEV_PM_OPS (&jc42_dev_pm_ops)
607d397276bSGuenter Roeck #else
608d397276bSGuenter Roeck #define JC42_DEV_PM_OPS NULL
609d397276bSGuenter Roeck #endif /* CONFIG_PM */
6104453d736SGuenter Roeck 
611d397276bSGuenter Roeck static const struct i2c_device_id jc42_id[] = {
612d397276bSGuenter Roeck 	{ "jc42", 0 },
613d397276bSGuenter Roeck 	{ }
614d397276bSGuenter Roeck };
615d397276bSGuenter Roeck MODULE_DEVICE_TABLE(i2c, jc42_id);
616d397276bSGuenter Roeck 
617803decceSGuenter Roeck #ifdef CONFIG_OF
618803decceSGuenter Roeck static const struct of_device_id jc42_of_ids[] = {
619803decceSGuenter Roeck 	{ .compatible = "jedec,jc-42.4-temp", },
620803decceSGuenter Roeck 	{ }
621803decceSGuenter Roeck };
622803decceSGuenter Roeck MODULE_DEVICE_TABLE(of, jc42_of_ids);
623803decceSGuenter Roeck #endif
624803decceSGuenter Roeck 
625d397276bSGuenter Roeck static struct i2c_driver jc42_driver = {
626eacc48ceSAlison Schofield 	.class		= I2C_CLASS_SPD | I2C_CLASS_HWMON,
627d397276bSGuenter Roeck 	.driver = {
628d397276bSGuenter Roeck 		.name	= "jc42",
629d397276bSGuenter Roeck 		.pm = JC42_DEV_PM_OPS,
630803decceSGuenter Roeck 		.of_match_table = of_match_ptr(jc42_of_ids),
631d397276bSGuenter Roeck 	},
63267487038SStephen Kitt 	.probe		= jc42_probe,
633d397276bSGuenter Roeck 	.remove		= jc42_remove,
634d397276bSGuenter Roeck 	.id_table	= jc42_id,
635d397276bSGuenter Roeck 	.detect		= jc42_detect,
636d397276bSGuenter Roeck 	.address_list	= normal_i2c,
637d397276bSGuenter Roeck };
6384453d736SGuenter Roeck 
639f0967eeaSAxel Lin module_i2c_driver(jc42_driver);
6404453d736SGuenter Roeck 
641bb9a80e5SGuenter Roeck MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>");
6424453d736SGuenter Roeck MODULE_DESCRIPTION("JC42 driver");
6434453d736SGuenter Roeck MODULE_LICENSE("GPL");
644