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