xref: /openbmc/linux/drivers/hwmon/pmbus/max8688.c (revision 2612e3bbc0386368a850140a6c9b990cd496a5ec)
174ba9207SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
29d2ecfb7SGuenter Roeck /*
39d2ecfb7SGuenter Roeck  * Hardware monitoring driver for Maxim MAX8688
49d2ecfb7SGuenter Roeck  *
59d2ecfb7SGuenter Roeck  * Copyright (c) 2011 Ericsson AB.
69d2ecfb7SGuenter Roeck  */
79d2ecfb7SGuenter Roeck 
82c052d42SGuenter Roeck #include <linux/bitops.h>
99d2ecfb7SGuenter Roeck #include <linux/kernel.h>
109d2ecfb7SGuenter Roeck #include <linux/module.h>
119d2ecfb7SGuenter Roeck #include <linux/init.h>
129d2ecfb7SGuenter Roeck #include <linux/err.h>
139d2ecfb7SGuenter Roeck #include <linux/i2c.h>
149d2ecfb7SGuenter Roeck #include "pmbus.h"
159d2ecfb7SGuenter Roeck 
1670e94b27SGuenter Roeck #define MAX8688_MFR_VOUT_PEAK		0xd4
1770e94b27SGuenter Roeck #define MAX8688_MFR_IOUT_PEAK		0xd5
1870e94b27SGuenter Roeck #define MAX8688_MFR_TEMPERATURE_PEAK	0xd6
199d2ecfb7SGuenter Roeck #define MAX8688_MFG_STATUS		0xd8
209d2ecfb7SGuenter Roeck 
212c052d42SGuenter Roeck #define MAX8688_STATUS_OC_FAULT		BIT(4)
222c052d42SGuenter Roeck #define MAX8688_STATUS_OV_FAULT		BIT(5)
232c052d42SGuenter Roeck #define MAX8688_STATUS_OV_WARNING	BIT(8)
242c052d42SGuenter Roeck #define MAX8688_STATUS_UV_FAULT		BIT(9)
252c052d42SGuenter Roeck #define MAX8688_STATUS_UV_WARNING	BIT(10)
262c052d42SGuenter Roeck #define MAX8688_STATUS_UC_FAULT		BIT(11)
272c052d42SGuenter Roeck #define MAX8688_STATUS_OC_WARNING	BIT(12)
282c052d42SGuenter Roeck #define MAX8688_STATUS_OT_FAULT		BIT(13)
292c052d42SGuenter Roeck #define MAX8688_STATUS_OT_WARNING	BIT(14)
309d2ecfb7SGuenter Roeck 
max8688_read_word_data(struct i2c_client * client,int page,int phase,int reg)3143f33b6eSGuenter Roeck static int max8688_read_word_data(struct i2c_client *client, int page,
3243f33b6eSGuenter Roeck 				  int phase, int reg)
3370e94b27SGuenter Roeck {
3470e94b27SGuenter Roeck 	int ret;
3570e94b27SGuenter Roeck 
36a46f8cd6SGuenter Roeck 	if (page > 0)
37179144a0SGuenter Roeck 		return -ENXIO;
3870e94b27SGuenter Roeck 
3970e94b27SGuenter Roeck 	switch (reg) {
4070e94b27SGuenter Roeck 	case PMBUS_VIRT_READ_VOUT_MAX:
4143f33b6eSGuenter Roeck 		ret = pmbus_read_word_data(client, 0, 0xff,
4243f33b6eSGuenter Roeck 					   MAX8688_MFR_VOUT_PEAK);
4370e94b27SGuenter Roeck 		break;
4470e94b27SGuenter Roeck 	case PMBUS_VIRT_READ_IOUT_MAX:
4543f33b6eSGuenter Roeck 		ret = pmbus_read_word_data(client, 0, 0xff,
4643f33b6eSGuenter Roeck 					   MAX8688_MFR_IOUT_PEAK);
4770e94b27SGuenter Roeck 		break;
4870e94b27SGuenter Roeck 	case PMBUS_VIRT_READ_TEMP_MAX:
4943f33b6eSGuenter Roeck 		ret = pmbus_read_word_data(client, 0, 0xff,
5070e94b27SGuenter Roeck 					   MAX8688_MFR_TEMPERATURE_PEAK);
5170e94b27SGuenter Roeck 		break;
5270e94b27SGuenter Roeck 	case PMBUS_VIRT_RESET_VOUT_HISTORY:
5370e94b27SGuenter Roeck 	case PMBUS_VIRT_RESET_IOUT_HISTORY:
5470e94b27SGuenter Roeck 	case PMBUS_VIRT_RESET_TEMP_HISTORY:
5570e94b27SGuenter Roeck 		ret = 0;
5670e94b27SGuenter Roeck 		break;
5770e94b27SGuenter Roeck 	default:
5870e94b27SGuenter Roeck 		ret = -ENODATA;
5970e94b27SGuenter Roeck 		break;
6070e94b27SGuenter Roeck 	}
6170e94b27SGuenter Roeck 	return ret;
6270e94b27SGuenter Roeck }
6370e94b27SGuenter Roeck 
max8688_write_word_data(struct i2c_client * client,int page,int reg,u16 word)6470e94b27SGuenter Roeck static int max8688_write_word_data(struct i2c_client *client, int page, int reg,
6570e94b27SGuenter Roeck 				   u16 word)
6670e94b27SGuenter Roeck {
6770e94b27SGuenter Roeck 	int ret;
6870e94b27SGuenter Roeck 
6970e94b27SGuenter Roeck 	switch (reg) {
7070e94b27SGuenter Roeck 	case PMBUS_VIRT_RESET_VOUT_HISTORY:
7170e94b27SGuenter Roeck 		ret = pmbus_write_word_data(client, 0, MAX8688_MFR_VOUT_PEAK,
7270e94b27SGuenter Roeck 					    0);
7370e94b27SGuenter Roeck 		break;
7470e94b27SGuenter Roeck 	case PMBUS_VIRT_RESET_IOUT_HISTORY:
7570e94b27SGuenter Roeck 		ret = pmbus_write_word_data(client, 0, MAX8688_MFR_IOUT_PEAK,
7670e94b27SGuenter Roeck 					    0);
7770e94b27SGuenter Roeck 		break;
7870e94b27SGuenter Roeck 	case PMBUS_VIRT_RESET_TEMP_HISTORY:
7970e94b27SGuenter Roeck 		ret = pmbus_write_word_data(client, 0,
8070e94b27SGuenter Roeck 					    MAX8688_MFR_TEMPERATURE_PEAK,
8170e94b27SGuenter Roeck 					    0xffff);
8270e94b27SGuenter Roeck 		break;
8370e94b27SGuenter Roeck 	default:
8470e94b27SGuenter Roeck 		ret = -ENODATA;
8570e94b27SGuenter Roeck 		break;
8670e94b27SGuenter Roeck 	}
8770e94b27SGuenter Roeck 	return ret;
8870e94b27SGuenter Roeck }
8970e94b27SGuenter Roeck 
max8688_read_byte_data(struct i2c_client * client,int page,int reg)909d2ecfb7SGuenter Roeck static int max8688_read_byte_data(struct i2c_client *client, int page, int reg)
919d2ecfb7SGuenter Roeck {
929d2ecfb7SGuenter Roeck 	int ret = 0;
939d2ecfb7SGuenter Roeck 	int mfg_status;
949d2ecfb7SGuenter Roeck 
95da8e48abSGuenter Roeck 	if (page > 0)
96179144a0SGuenter Roeck 		return -ENXIO;
979d2ecfb7SGuenter Roeck 
989d2ecfb7SGuenter Roeck 	switch (reg) {
999d2ecfb7SGuenter Roeck 	case PMBUS_STATUS_VOUT:
10043f33b6eSGuenter Roeck 		mfg_status = pmbus_read_word_data(client, 0, 0xff,
1019d2ecfb7SGuenter Roeck 						  MAX8688_MFG_STATUS);
1029d2ecfb7SGuenter Roeck 		if (mfg_status < 0)
1039d2ecfb7SGuenter Roeck 			return mfg_status;
1049d2ecfb7SGuenter Roeck 		if (mfg_status & MAX8688_STATUS_UV_WARNING)
1059d2ecfb7SGuenter Roeck 			ret |= PB_VOLTAGE_UV_WARNING;
1069d2ecfb7SGuenter Roeck 		if (mfg_status & MAX8688_STATUS_UV_FAULT)
1079d2ecfb7SGuenter Roeck 			ret |= PB_VOLTAGE_UV_FAULT;
1089d2ecfb7SGuenter Roeck 		if (mfg_status & MAX8688_STATUS_OV_WARNING)
1099d2ecfb7SGuenter Roeck 			ret |= PB_VOLTAGE_OV_WARNING;
1109d2ecfb7SGuenter Roeck 		if (mfg_status & MAX8688_STATUS_OV_FAULT)
1119d2ecfb7SGuenter Roeck 			ret |= PB_VOLTAGE_OV_FAULT;
1129d2ecfb7SGuenter Roeck 		break;
1139d2ecfb7SGuenter Roeck 	case PMBUS_STATUS_IOUT:
11443f33b6eSGuenter Roeck 		mfg_status = pmbus_read_word_data(client, 0, 0xff,
1159d2ecfb7SGuenter Roeck 						  MAX8688_MFG_STATUS);
1169d2ecfb7SGuenter Roeck 		if (mfg_status < 0)
1179d2ecfb7SGuenter Roeck 			return mfg_status;
1189d2ecfb7SGuenter Roeck 		if (mfg_status & MAX8688_STATUS_UC_FAULT)
1199d2ecfb7SGuenter Roeck 			ret |= PB_IOUT_UC_FAULT;
1209d2ecfb7SGuenter Roeck 		if (mfg_status & MAX8688_STATUS_OC_WARNING)
1219d2ecfb7SGuenter Roeck 			ret |= PB_IOUT_OC_WARNING;
1229d2ecfb7SGuenter Roeck 		if (mfg_status & MAX8688_STATUS_OC_FAULT)
1239d2ecfb7SGuenter Roeck 			ret |= PB_IOUT_OC_FAULT;
1249d2ecfb7SGuenter Roeck 		break;
1259d2ecfb7SGuenter Roeck 	case PMBUS_STATUS_TEMPERATURE:
12643f33b6eSGuenter Roeck 		mfg_status = pmbus_read_word_data(client, 0, 0xff,
1279d2ecfb7SGuenter Roeck 						  MAX8688_MFG_STATUS);
1289d2ecfb7SGuenter Roeck 		if (mfg_status < 0)
1299d2ecfb7SGuenter Roeck 			return mfg_status;
1309d2ecfb7SGuenter Roeck 		if (mfg_status & MAX8688_STATUS_OT_WARNING)
1319d2ecfb7SGuenter Roeck 			ret |= PB_TEMP_OT_WARNING;
1329d2ecfb7SGuenter Roeck 		if (mfg_status & MAX8688_STATUS_OT_FAULT)
1339d2ecfb7SGuenter Roeck 			ret |= PB_TEMP_OT_FAULT;
1349d2ecfb7SGuenter Roeck 		break;
1359d2ecfb7SGuenter Roeck 	default:
1369d2ecfb7SGuenter Roeck 		ret = -ENODATA;
1379d2ecfb7SGuenter Roeck 		break;
1389d2ecfb7SGuenter Roeck 	}
1399d2ecfb7SGuenter Roeck 	return ret;
1409d2ecfb7SGuenter Roeck }
1419d2ecfb7SGuenter Roeck 
1429d2ecfb7SGuenter Roeck static struct pmbus_driver_info max8688_info = {
1439d2ecfb7SGuenter Roeck 	.pages = 1,
1441061d851SGuenter Roeck 	.format[PSC_VOLTAGE_IN] = direct,
1451061d851SGuenter Roeck 	.format[PSC_VOLTAGE_OUT] = direct,
1461061d851SGuenter Roeck 	.format[PSC_TEMPERATURE] = direct,
1471061d851SGuenter Roeck 	.format[PSC_CURRENT_OUT] = direct,
1489d2ecfb7SGuenter Roeck 	.m[PSC_VOLTAGE_IN] = 19995,
1499d2ecfb7SGuenter Roeck 	.b[PSC_VOLTAGE_IN] = 0,
1509d2ecfb7SGuenter Roeck 	.R[PSC_VOLTAGE_IN] = -1,
1519d2ecfb7SGuenter Roeck 	.m[PSC_VOLTAGE_OUT] = 19995,
1529d2ecfb7SGuenter Roeck 	.b[PSC_VOLTAGE_OUT] = 0,
1539d2ecfb7SGuenter Roeck 	.R[PSC_VOLTAGE_OUT] = -1,
1549d2ecfb7SGuenter Roeck 	.m[PSC_CURRENT_OUT] = 23109,
1559d2ecfb7SGuenter Roeck 	.b[PSC_CURRENT_OUT] = 0,
1569d2ecfb7SGuenter Roeck 	.R[PSC_CURRENT_OUT] = -2,
1579d2ecfb7SGuenter Roeck 	.m[PSC_TEMPERATURE] = -7612,
1589d2ecfb7SGuenter Roeck 	.b[PSC_TEMPERATURE] = 335,
1599d2ecfb7SGuenter Roeck 	.R[PSC_TEMPERATURE] = -3,
1609d2ecfb7SGuenter Roeck 	.func[0] = PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT | PMBUS_HAVE_TEMP
1619d2ecfb7SGuenter Roeck 		| PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_STATUS_IOUT
1629d2ecfb7SGuenter Roeck 		| PMBUS_HAVE_STATUS_TEMP,
1639d2ecfb7SGuenter Roeck 	.read_byte_data = max8688_read_byte_data,
16470e94b27SGuenter Roeck 	.read_word_data = max8688_read_word_data,
16570e94b27SGuenter Roeck 	.write_word_data = max8688_write_word_data,
1669d2ecfb7SGuenter Roeck };
1679d2ecfb7SGuenter Roeck 
max8688_probe(struct i2c_client * client)168dd431939SStephen Kitt static int max8688_probe(struct i2c_client *client)
1699d2ecfb7SGuenter Roeck {
170dd431939SStephen Kitt 	return pmbus_do_probe(client, &max8688_info);
1719d2ecfb7SGuenter Roeck }
1729d2ecfb7SGuenter Roeck 
1739d2ecfb7SGuenter Roeck static const struct i2c_device_id max8688_id[] = {
1749d2ecfb7SGuenter Roeck 	{"max8688", 0},
1759d2ecfb7SGuenter Roeck 	{ }
1769d2ecfb7SGuenter Roeck };
1779d2ecfb7SGuenter Roeck 
1789d2ecfb7SGuenter Roeck MODULE_DEVICE_TABLE(i2c, max8688_id);
1799d2ecfb7SGuenter Roeck 
1809d2ecfb7SGuenter Roeck /* This is the driver that will be inserted */
1819d2ecfb7SGuenter Roeck static struct i2c_driver max8688_driver = {
1829d2ecfb7SGuenter Roeck 	.driver = {
1839d2ecfb7SGuenter Roeck 		   .name = "max8688",
1849d2ecfb7SGuenter Roeck 		   },
185*1975d167SUwe Kleine-König 	.probe = max8688_probe,
1869d2ecfb7SGuenter Roeck 	.id_table = max8688_id,
1879d2ecfb7SGuenter Roeck };
1889d2ecfb7SGuenter Roeck 
189f0967eeaSAxel Lin module_i2c_driver(max8688_driver);
1909d2ecfb7SGuenter Roeck 
1919d2ecfb7SGuenter Roeck MODULE_AUTHOR("Guenter Roeck");
1929d2ecfb7SGuenter Roeck MODULE_DESCRIPTION("PMBus driver for Maxim MAX8688");
1939d2ecfb7SGuenter Roeck MODULE_LICENSE("GPL");
194b94ca77eSGuenter Roeck MODULE_IMPORT_NS(PMBUS);
195