18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Hardware monitoring driver for Maxim MAX8688 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2011 Ericsson AB. 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/bitops.h> 98c2ecf20Sopenharmony_ci#include <linux/kernel.h> 108c2ecf20Sopenharmony_ci#include <linux/module.h> 118c2ecf20Sopenharmony_ci#include <linux/init.h> 128c2ecf20Sopenharmony_ci#include <linux/err.h> 138c2ecf20Sopenharmony_ci#include <linux/i2c.h> 148c2ecf20Sopenharmony_ci#include "pmbus.h" 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#define MAX8688_MFR_VOUT_PEAK 0xd4 178c2ecf20Sopenharmony_ci#define MAX8688_MFR_IOUT_PEAK 0xd5 188c2ecf20Sopenharmony_ci#define MAX8688_MFR_TEMPERATURE_PEAK 0xd6 198c2ecf20Sopenharmony_ci#define MAX8688_MFG_STATUS 0xd8 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#define MAX8688_STATUS_OC_FAULT BIT(4) 228c2ecf20Sopenharmony_ci#define MAX8688_STATUS_OV_FAULT BIT(5) 238c2ecf20Sopenharmony_ci#define MAX8688_STATUS_OV_WARNING BIT(8) 248c2ecf20Sopenharmony_ci#define MAX8688_STATUS_UV_FAULT BIT(9) 258c2ecf20Sopenharmony_ci#define MAX8688_STATUS_UV_WARNING BIT(10) 268c2ecf20Sopenharmony_ci#define MAX8688_STATUS_UC_FAULT BIT(11) 278c2ecf20Sopenharmony_ci#define MAX8688_STATUS_OC_WARNING BIT(12) 288c2ecf20Sopenharmony_ci#define MAX8688_STATUS_OT_FAULT BIT(13) 298c2ecf20Sopenharmony_ci#define MAX8688_STATUS_OT_WARNING BIT(14) 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_cistatic int max8688_read_word_data(struct i2c_client *client, int page, 328c2ecf20Sopenharmony_ci int phase, int reg) 338c2ecf20Sopenharmony_ci{ 348c2ecf20Sopenharmony_ci int ret; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci if (page > 0) 378c2ecf20Sopenharmony_ci return -ENXIO; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci switch (reg) { 408c2ecf20Sopenharmony_ci case PMBUS_VIRT_READ_VOUT_MAX: 418c2ecf20Sopenharmony_ci ret = pmbus_read_word_data(client, 0, 0xff, 428c2ecf20Sopenharmony_ci MAX8688_MFR_VOUT_PEAK); 438c2ecf20Sopenharmony_ci break; 448c2ecf20Sopenharmony_ci case PMBUS_VIRT_READ_IOUT_MAX: 458c2ecf20Sopenharmony_ci ret = pmbus_read_word_data(client, 0, 0xff, 468c2ecf20Sopenharmony_ci MAX8688_MFR_IOUT_PEAK); 478c2ecf20Sopenharmony_ci break; 488c2ecf20Sopenharmony_ci case PMBUS_VIRT_READ_TEMP_MAX: 498c2ecf20Sopenharmony_ci ret = pmbus_read_word_data(client, 0, 0xff, 508c2ecf20Sopenharmony_ci MAX8688_MFR_TEMPERATURE_PEAK); 518c2ecf20Sopenharmony_ci break; 528c2ecf20Sopenharmony_ci case PMBUS_VIRT_RESET_VOUT_HISTORY: 538c2ecf20Sopenharmony_ci case PMBUS_VIRT_RESET_IOUT_HISTORY: 548c2ecf20Sopenharmony_ci case PMBUS_VIRT_RESET_TEMP_HISTORY: 558c2ecf20Sopenharmony_ci ret = 0; 568c2ecf20Sopenharmony_ci break; 578c2ecf20Sopenharmony_ci default: 588c2ecf20Sopenharmony_ci ret = -ENODATA; 598c2ecf20Sopenharmony_ci break; 608c2ecf20Sopenharmony_ci } 618c2ecf20Sopenharmony_ci return ret; 628c2ecf20Sopenharmony_ci} 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_cistatic int max8688_write_word_data(struct i2c_client *client, int page, int reg, 658c2ecf20Sopenharmony_ci u16 word) 668c2ecf20Sopenharmony_ci{ 678c2ecf20Sopenharmony_ci int ret; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci switch (reg) { 708c2ecf20Sopenharmony_ci case PMBUS_VIRT_RESET_VOUT_HISTORY: 718c2ecf20Sopenharmony_ci ret = pmbus_write_word_data(client, 0, MAX8688_MFR_VOUT_PEAK, 728c2ecf20Sopenharmony_ci 0); 738c2ecf20Sopenharmony_ci break; 748c2ecf20Sopenharmony_ci case PMBUS_VIRT_RESET_IOUT_HISTORY: 758c2ecf20Sopenharmony_ci ret = pmbus_write_word_data(client, 0, MAX8688_MFR_IOUT_PEAK, 768c2ecf20Sopenharmony_ci 0); 778c2ecf20Sopenharmony_ci break; 788c2ecf20Sopenharmony_ci case PMBUS_VIRT_RESET_TEMP_HISTORY: 798c2ecf20Sopenharmony_ci ret = pmbus_write_word_data(client, 0, 808c2ecf20Sopenharmony_ci MAX8688_MFR_TEMPERATURE_PEAK, 818c2ecf20Sopenharmony_ci 0xffff); 828c2ecf20Sopenharmony_ci break; 838c2ecf20Sopenharmony_ci default: 848c2ecf20Sopenharmony_ci ret = -ENODATA; 858c2ecf20Sopenharmony_ci break; 868c2ecf20Sopenharmony_ci } 878c2ecf20Sopenharmony_ci return ret; 888c2ecf20Sopenharmony_ci} 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_cistatic int max8688_read_byte_data(struct i2c_client *client, int page, int reg) 918c2ecf20Sopenharmony_ci{ 928c2ecf20Sopenharmony_ci int ret = 0; 938c2ecf20Sopenharmony_ci int mfg_status; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci if (page > 0) 968c2ecf20Sopenharmony_ci return -ENXIO; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci switch (reg) { 998c2ecf20Sopenharmony_ci case PMBUS_STATUS_VOUT: 1008c2ecf20Sopenharmony_ci mfg_status = pmbus_read_word_data(client, 0, 0xff, 1018c2ecf20Sopenharmony_ci MAX8688_MFG_STATUS); 1028c2ecf20Sopenharmony_ci if (mfg_status < 0) 1038c2ecf20Sopenharmony_ci return mfg_status; 1048c2ecf20Sopenharmony_ci if (mfg_status & MAX8688_STATUS_UV_WARNING) 1058c2ecf20Sopenharmony_ci ret |= PB_VOLTAGE_UV_WARNING; 1068c2ecf20Sopenharmony_ci if (mfg_status & MAX8688_STATUS_UV_FAULT) 1078c2ecf20Sopenharmony_ci ret |= PB_VOLTAGE_UV_FAULT; 1088c2ecf20Sopenharmony_ci if (mfg_status & MAX8688_STATUS_OV_WARNING) 1098c2ecf20Sopenharmony_ci ret |= PB_VOLTAGE_OV_WARNING; 1108c2ecf20Sopenharmony_ci if (mfg_status & MAX8688_STATUS_OV_FAULT) 1118c2ecf20Sopenharmony_ci ret |= PB_VOLTAGE_OV_FAULT; 1128c2ecf20Sopenharmony_ci break; 1138c2ecf20Sopenharmony_ci case PMBUS_STATUS_IOUT: 1148c2ecf20Sopenharmony_ci mfg_status = pmbus_read_word_data(client, 0, 0xff, 1158c2ecf20Sopenharmony_ci MAX8688_MFG_STATUS); 1168c2ecf20Sopenharmony_ci if (mfg_status < 0) 1178c2ecf20Sopenharmony_ci return mfg_status; 1188c2ecf20Sopenharmony_ci if (mfg_status & MAX8688_STATUS_UC_FAULT) 1198c2ecf20Sopenharmony_ci ret |= PB_IOUT_UC_FAULT; 1208c2ecf20Sopenharmony_ci if (mfg_status & MAX8688_STATUS_OC_WARNING) 1218c2ecf20Sopenharmony_ci ret |= PB_IOUT_OC_WARNING; 1228c2ecf20Sopenharmony_ci if (mfg_status & MAX8688_STATUS_OC_FAULT) 1238c2ecf20Sopenharmony_ci ret |= PB_IOUT_OC_FAULT; 1248c2ecf20Sopenharmony_ci break; 1258c2ecf20Sopenharmony_ci case PMBUS_STATUS_TEMPERATURE: 1268c2ecf20Sopenharmony_ci mfg_status = pmbus_read_word_data(client, 0, 0xff, 1278c2ecf20Sopenharmony_ci MAX8688_MFG_STATUS); 1288c2ecf20Sopenharmony_ci if (mfg_status < 0) 1298c2ecf20Sopenharmony_ci return mfg_status; 1308c2ecf20Sopenharmony_ci if (mfg_status & MAX8688_STATUS_OT_WARNING) 1318c2ecf20Sopenharmony_ci ret |= PB_TEMP_OT_WARNING; 1328c2ecf20Sopenharmony_ci if (mfg_status & MAX8688_STATUS_OT_FAULT) 1338c2ecf20Sopenharmony_ci ret |= PB_TEMP_OT_FAULT; 1348c2ecf20Sopenharmony_ci break; 1358c2ecf20Sopenharmony_ci default: 1368c2ecf20Sopenharmony_ci ret = -ENODATA; 1378c2ecf20Sopenharmony_ci break; 1388c2ecf20Sopenharmony_ci } 1398c2ecf20Sopenharmony_ci return ret; 1408c2ecf20Sopenharmony_ci} 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_cistatic struct pmbus_driver_info max8688_info = { 1438c2ecf20Sopenharmony_ci .pages = 1, 1448c2ecf20Sopenharmony_ci .format[PSC_VOLTAGE_IN] = direct, 1458c2ecf20Sopenharmony_ci .format[PSC_VOLTAGE_OUT] = direct, 1468c2ecf20Sopenharmony_ci .format[PSC_TEMPERATURE] = direct, 1478c2ecf20Sopenharmony_ci .format[PSC_CURRENT_OUT] = direct, 1488c2ecf20Sopenharmony_ci .m[PSC_VOLTAGE_IN] = 19995, 1498c2ecf20Sopenharmony_ci .b[PSC_VOLTAGE_IN] = 0, 1508c2ecf20Sopenharmony_ci .R[PSC_VOLTAGE_IN] = -1, 1518c2ecf20Sopenharmony_ci .m[PSC_VOLTAGE_OUT] = 19995, 1528c2ecf20Sopenharmony_ci .b[PSC_VOLTAGE_OUT] = 0, 1538c2ecf20Sopenharmony_ci .R[PSC_VOLTAGE_OUT] = -1, 1548c2ecf20Sopenharmony_ci .m[PSC_CURRENT_OUT] = 23109, 1558c2ecf20Sopenharmony_ci .b[PSC_CURRENT_OUT] = 0, 1568c2ecf20Sopenharmony_ci .R[PSC_CURRENT_OUT] = -2, 1578c2ecf20Sopenharmony_ci .m[PSC_TEMPERATURE] = -7612, 1588c2ecf20Sopenharmony_ci .b[PSC_TEMPERATURE] = 335, 1598c2ecf20Sopenharmony_ci .R[PSC_TEMPERATURE] = -3, 1608c2ecf20Sopenharmony_ci .func[0] = PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT | PMBUS_HAVE_TEMP 1618c2ecf20Sopenharmony_ci | PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_STATUS_IOUT 1628c2ecf20Sopenharmony_ci | PMBUS_HAVE_STATUS_TEMP, 1638c2ecf20Sopenharmony_ci .read_byte_data = max8688_read_byte_data, 1648c2ecf20Sopenharmony_ci .read_word_data = max8688_read_word_data, 1658c2ecf20Sopenharmony_ci .write_word_data = max8688_write_word_data, 1668c2ecf20Sopenharmony_ci}; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_cistatic int max8688_probe(struct i2c_client *client) 1698c2ecf20Sopenharmony_ci{ 1708c2ecf20Sopenharmony_ci return pmbus_do_probe(client, &max8688_info); 1718c2ecf20Sopenharmony_ci} 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_cistatic const struct i2c_device_id max8688_id[] = { 1748c2ecf20Sopenharmony_ci {"max8688", 0}, 1758c2ecf20Sopenharmony_ci { } 1768c2ecf20Sopenharmony_ci}; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, max8688_id); 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci/* This is the driver that will be inserted */ 1818c2ecf20Sopenharmony_cistatic struct i2c_driver max8688_driver = { 1828c2ecf20Sopenharmony_ci .driver = { 1838c2ecf20Sopenharmony_ci .name = "max8688", 1848c2ecf20Sopenharmony_ci }, 1858c2ecf20Sopenharmony_ci .probe_new = max8688_probe, 1868c2ecf20Sopenharmony_ci .remove = pmbus_do_remove, 1878c2ecf20Sopenharmony_ci .id_table = max8688_id, 1888c2ecf20Sopenharmony_ci}; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_cimodule_i2c_driver(max8688_driver); 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ciMODULE_AUTHOR("Guenter Roeck"); 1938c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("PMBus driver for Maxim MAX8688"); 1948c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 195