162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Hardware monitoring driver for Renesas Digital Multiphase Voltage Regulators 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2017 Google Inc 662306a36Sopenharmony_ci * Copyright (c) 2020 Renesas Electronics America 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/err.h> 1162306a36Sopenharmony_ci#include <linux/hwmon-sysfs.h> 1262306a36Sopenharmony_ci#include <linux/i2c.h> 1362306a36Sopenharmony_ci#include <linux/init.h> 1462306a36Sopenharmony_ci#include <linux/kernel.h> 1562306a36Sopenharmony_ci#include <linux/module.h> 1662306a36Sopenharmony_ci#include <linux/string.h> 1762306a36Sopenharmony_ci#include <linux/sysfs.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#include "pmbus.h" 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#define ISL68137_VOUT_AVS 0x30 2262306a36Sopenharmony_ci#define RAA_DMPVR2_READ_VMON 0xc8 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_cienum chips { 2562306a36Sopenharmony_ci isl68137, 2662306a36Sopenharmony_ci isl68220, 2762306a36Sopenharmony_ci isl68221, 2862306a36Sopenharmony_ci isl68222, 2962306a36Sopenharmony_ci isl68223, 3062306a36Sopenharmony_ci isl68224, 3162306a36Sopenharmony_ci isl68225, 3262306a36Sopenharmony_ci isl68226, 3362306a36Sopenharmony_ci isl68227, 3462306a36Sopenharmony_ci isl68229, 3562306a36Sopenharmony_ci isl68233, 3662306a36Sopenharmony_ci isl68239, 3762306a36Sopenharmony_ci isl69222, 3862306a36Sopenharmony_ci isl69223, 3962306a36Sopenharmony_ci isl69224, 4062306a36Sopenharmony_ci isl69225, 4162306a36Sopenharmony_ci isl69227, 4262306a36Sopenharmony_ci isl69228, 4362306a36Sopenharmony_ci isl69234, 4462306a36Sopenharmony_ci isl69236, 4562306a36Sopenharmony_ci isl69239, 4662306a36Sopenharmony_ci isl69242, 4762306a36Sopenharmony_ci isl69243, 4862306a36Sopenharmony_ci isl69247, 4962306a36Sopenharmony_ci isl69248, 5062306a36Sopenharmony_ci isl69254, 5162306a36Sopenharmony_ci isl69255, 5262306a36Sopenharmony_ci isl69256, 5362306a36Sopenharmony_ci isl69259, 5462306a36Sopenharmony_ci isl69260, 5562306a36Sopenharmony_ci isl69268, 5662306a36Sopenharmony_ci isl69269, 5762306a36Sopenharmony_ci isl69298, 5862306a36Sopenharmony_ci raa228000, 5962306a36Sopenharmony_ci raa228004, 6062306a36Sopenharmony_ci raa228006, 6162306a36Sopenharmony_ci raa228228, 6262306a36Sopenharmony_ci raa229001, 6362306a36Sopenharmony_ci raa229004, 6462306a36Sopenharmony_ci}; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_cienum variants { 6762306a36Sopenharmony_ci raa_dmpvr1_2rail, 6862306a36Sopenharmony_ci raa_dmpvr2_1rail, 6962306a36Sopenharmony_ci raa_dmpvr2_2rail, 7062306a36Sopenharmony_ci raa_dmpvr2_2rail_nontc, 7162306a36Sopenharmony_ci raa_dmpvr2_3rail, 7262306a36Sopenharmony_ci raa_dmpvr2_hv, 7362306a36Sopenharmony_ci}; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_cistatic const struct i2c_device_id raa_dmpvr_id[]; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_cistatic ssize_t isl68137_avs_enable_show_page(struct i2c_client *client, 7862306a36Sopenharmony_ci int page, 7962306a36Sopenharmony_ci char *buf) 8062306a36Sopenharmony_ci{ 8162306a36Sopenharmony_ci int val = pmbus_read_byte_data(client, page, PMBUS_OPERATION); 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci return sprintf(buf, "%d\n", 8462306a36Sopenharmony_ci (val & ISL68137_VOUT_AVS) == ISL68137_VOUT_AVS ? 1 : 0); 8562306a36Sopenharmony_ci} 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_cistatic ssize_t isl68137_avs_enable_store_page(struct i2c_client *client, 8862306a36Sopenharmony_ci int page, 8962306a36Sopenharmony_ci const char *buf, size_t count) 9062306a36Sopenharmony_ci{ 9162306a36Sopenharmony_ci int rc, op_val; 9262306a36Sopenharmony_ci bool result; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci rc = kstrtobool(buf, &result); 9562306a36Sopenharmony_ci if (rc) 9662306a36Sopenharmony_ci return rc; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci op_val = result ? ISL68137_VOUT_AVS : 0; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci /* 10162306a36Sopenharmony_ci * Writes to VOUT setpoint over AVSBus will persist after the VRM is 10262306a36Sopenharmony_ci * switched to PMBus control. Switching back to AVSBus control 10362306a36Sopenharmony_ci * restores this persisted setpoint rather than re-initializing to 10462306a36Sopenharmony_ci * PMBus VOUT_COMMAND. Writing VOUT_COMMAND first over PMBus before 10562306a36Sopenharmony_ci * enabling AVS control is the workaround. 10662306a36Sopenharmony_ci */ 10762306a36Sopenharmony_ci if (op_val == ISL68137_VOUT_AVS) { 10862306a36Sopenharmony_ci rc = pmbus_read_word_data(client, page, 0xff, 10962306a36Sopenharmony_ci PMBUS_VOUT_COMMAND); 11062306a36Sopenharmony_ci if (rc < 0) 11162306a36Sopenharmony_ci return rc; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci rc = pmbus_write_word_data(client, page, PMBUS_VOUT_COMMAND, 11462306a36Sopenharmony_ci rc); 11562306a36Sopenharmony_ci if (rc < 0) 11662306a36Sopenharmony_ci return rc; 11762306a36Sopenharmony_ci } 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci rc = pmbus_update_byte_data(client, page, PMBUS_OPERATION, 12062306a36Sopenharmony_ci ISL68137_VOUT_AVS, op_val); 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci return (rc < 0) ? rc : count; 12362306a36Sopenharmony_ci} 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_cistatic ssize_t isl68137_avs_enable_show(struct device *dev, 12662306a36Sopenharmony_ci struct device_attribute *devattr, 12762306a36Sopenharmony_ci char *buf) 12862306a36Sopenharmony_ci{ 12962306a36Sopenharmony_ci struct i2c_client *client = to_i2c_client(dev->parent); 13062306a36Sopenharmony_ci struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci return isl68137_avs_enable_show_page(client, attr->index, buf); 13362306a36Sopenharmony_ci} 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_cistatic ssize_t isl68137_avs_enable_store(struct device *dev, 13662306a36Sopenharmony_ci struct device_attribute *devattr, 13762306a36Sopenharmony_ci const char *buf, size_t count) 13862306a36Sopenharmony_ci{ 13962306a36Sopenharmony_ci struct i2c_client *client = to_i2c_client(dev->parent); 14062306a36Sopenharmony_ci struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci return isl68137_avs_enable_store_page(client, attr->index, buf, count); 14362306a36Sopenharmony_ci} 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(avs0_enable, isl68137_avs_enable, 0); 14662306a36Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(avs1_enable, isl68137_avs_enable, 1); 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_cistatic struct attribute *enable_attrs[] = { 14962306a36Sopenharmony_ci &sensor_dev_attr_avs0_enable.dev_attr.attr, 15062306a36Sopenharmony_ci &sensor_dev_attr_avs1_enable.dev_attr.attr, 15162306a36Sopenharmony_ci NULL, 15262306a36Sopenharmony_ci}; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_cistatic const struct attribute_group enable_group = { 15562306a36Sopenharmony_ci .attrs = enable_attrs, 15662306a36Sopenharmony_ci}; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_cistatic const struct attribute_group *isl68137_attribute_groups[] = { 15962306a36Sopenharmony_ci &enable_group, 16062306a36Sopenharmony_ci NULL, 16162306a36Sopenharmony_ci}; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_cistatic int raa_dmpvr2_read_word_data(struct i2c_client *client, int page, 16462306a36Sopenharmony_ci int phase, int reg) 16562306a36Sopenharmony_ci{ 16662306a36Sopenharmony_ci int ret; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci switch (reg) { 16962306a36Sopenharmony_ci case PMBUS_VIRT_READ_VMON: 17062306a36Sopenharmony_ci ret = pmbus_read_word_data(client, page, phase, 17162306a36Sopenharmony_ci RAA_DMPVR2_READ_VMON); 17262306a36Sopenharmony_ci break; 17362306a36Sopenharmony_ci default: 17462306a36Sopenharmony_ci ret = -ENODATA; 17562306a36Sopenharmony_ci break; 17662306a36Sopenharmony_ci } 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci return ret; 17962306a36Sopenharmony_ci} 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_cistatic struct pmbus_driver_info raa_dmpvr_info = { 18262306a36Sopenharmony_ci .pages = 3, 18362306a36Sopenharmony_ci .format[PSC_VOLTAGE_IN] = direct, 18462306a36Sopenharmony_ci .format[PSC_VOLTAGE_OUT] = direct, 18562306a36Sopenharmony_ci .format[PSC_CURRENT_IN] = direct, 18662306a36Sopenharmony_ci .format[PSC_CURRENT_OUT] = direct, 18762306a36Sopenharmony_ci .format[PSC_POWER] = direct, 18862306a36Sopenharmony_ci .format[PSC_TEMPERATURE] = direct, 18962306a36Sopenharmony_ci .m[PSC_VOLTAGE_IN] = 1, 19062306a36Sopenharmony_ci .b[PSC_VOLTAGE_IN] = 0, 19162306a36Sopenharmony_ci .R[PSC_VOLTAGE_IN] = 2, 19262306a36Sopenharmony_ci .m[PSC_VOLTAGE_OUT] = 1, 19362306a36Sopenharmony_ci .b[PSC_VOLTAGE_OUT] = 0, 19462306a36Sopenharmony_ci .R[PSC_VOLTAGE_OUT] = 3, 19562306a36Sopenharmony_ci .m[PSC_CURRENT_IN] = 1, 19662306a36Sopenharmony_ci .b[PSC_CURRENT_IN] = 0, 19762306a36Sopenharmony_ci .R[PSC_CURRENT_IN] = 2, 19862306a36Sopenharmony_ci .m[PSC_CURRENT_OUT] = 1, 19962306a36Sopenharmony_ci .b[PSC_CURRENT_OUT] = 0, 20062306a36Sopenharmony_ci .R[PSC_CURRENT_OUT] = 1, 20162306a36Sopenharmony_ci .m[PSC_POWER] = 1, 20262306a36Sopenharmony_ci .b[PSC_POWER] = 0, 20362306a36Sopenharmony_ci .R[PSC_POWER] = 0, 20462306a36Sopenharmony_ci .m[PSC_TEMPERATURE] = 1, 20562306a36Sopenharmony_ci .b[PSC_TEMPERATURE] = 0, 20662306a36Sopenharmony_ci .R[PSC_TEMPERATURE] = 0, 20762306a36Sopenharmony_ci .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_IIN | PMBUS_HAVE_PIN 20862306a36Sopenharmony_ci | PMBUS_HAVE_STATUS_INPUT | PMBUS_HAVE_TEMP | PMBUS_HAVE_TEMP2 20962306a36Sopenharmony_ci | PMBUS_HAVE_TEMP3 | PMBUS_HAVE_STATUS_TEMP 21062306a36Sopenharmony_ci | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT 21162306a36Sopenharmony_ci | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | PMBUS_HAVE_POUT 21262306a36Sopenharmony_ci | PMBUS_HAVE_VMON, 21362306a36Sopenharmony_ci .func[1] = PMBUS_HAVE_IIN | PMBUS_HAVE_PIN | PMBUS_HAVE_STATUS_INPUT 21462306a36Sopenharmony_ci | PMBUS_HAVE_TEMP | PMBUS_HAVE_TEMP3 | PMBUS_HAVE_STATUS_TEMP 21562306a36Sopenharmony_ci | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_IOUT 21662306a36Sopenharmony_ci | PMBUS_HAVE_STATUS_IOUT | PMBUS_HAVE_POUT, 21762306a36Sopenharmony_ci .func[2] = PMBUS_HAVE_IIN | PMBUS_HAVE_PIN | PMBUS_HAVE_STATUS_INPUT 21862306a36Sopenharmony_ci | PMBUS_HAVE_TEMP | PMBUS_HAVE_TEMP3 | PMBUS_HAVE_STATUS_TEMP 21962306a36Sopenharmony_ci | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_IOUT 22062306a36Sopenharmony_ci | PMBUS_HAVE_STATUS_IOUT | PMBUS_HAVE_POUT, 22162306a36Sopenharmony_ci}; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_cistatic int isl68137_probe(struct i2c_client *client) 22462306a36Sopenharmony_ci{ 22562306a36Sopenharmony_ci struct pmbus_driver_info *info; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci info = devm_kzalloc(&client->dev, sizeof(*info), GFP_KERNEL); 22862306a36Sopenharmony_ci if (!info) 22962306a36Sopenharmony_ci return -ENOMEM; 23062306a36Sopenharmony_ci memcpy(info, &raa_dmpvr_info, sizeof(*info)); 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci switch (i2c_match_id(raa_dmpvr_id, client)->driver_data) { 23362306a36Sopenharmony_ci case raa_dmpvr1_2rail: 23462306a36Sopenharmony_ci info->pages = 2; 23562306a36Sopenharmony_ci info->R[PSC_VOLTAGE_IN] = 3; 23662306a36Sopenharmony_ci info->func[0] &= ~PMBUS_HAVE_VMON; 23762306a36Sopenharmony_ci info->func[1] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT 23862306a36Sopenharmony_ci | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT 23962306a36Sopenharmony_ci | PMBUS_HAVE_POUT; 24062306a36Sopenharmony_ci info->groups = isl68137_attribute_groups; 24162306a36Sopenharmony_ci break; 24262306a36Sopenharmony_ci case raa_dmpvr2_1rail: 24362306a36Sopenharmony_ci info->pages = 1; 24462306a36Sopenharmony_ci info->read_word_data = raa_dmpvr2_read_word_data; 24562306a36Sopenharmony_ci break; 24662306a36Sopenharmony_ci case raa_dmpvr2_2rail_nontc: 24762306a36Sopenharmony_ci info->func[0] &= ~PMBUS_HAVE_TEMP3; 24862306a36Sopenharmony_ci info->func[1] &= ~PMBUS_HAVE_TEMP3; 24962306a36Sopenharmony_ci fallthrough; 25062306a36Sopenharmony_ci case raa_dmpvr2_2rail: 25162306a36Sopenharmony_ci info->pages = 2; 25262306a36Sopenharmony_ci info->read_word_data = raa_dmpvr2_read_word_data; 25362306a36Sopenharmony_ci break; 25462306a36Sopenharmony_ci case raa_dmpvr2_3rail: 25562306a36Sopenharmony_ci info->read_word_data = raa_dmpvr2_read_word_data; 25662306a36Sopenharmony_ci break; 25762306a36Sopenharmony_ci case raa_dmpvr2_hv: 25862306a36Sopenharmony_ci info->pages = 1; 25962306a36Sopenharmony_ci info->R[PSC_VOLTAGE_IN] = 1; 26062306a36Sopenharmony_ci info->m[PSC_VOLTAGE_OUT] = 2; 26162306a36Sopenharmony_ci info->R[PSC_VOLTAGE_OUT] = 2; 26262306a36Sopenharmony_ci info->m[PSC_CURRENT_IN] = 2; 26362306a36Sopenharmony_ci info->m[PSC_POWER] = 2; 26462306a36Sopenharmony_ci info->R[PSC_POWER] = -1; 26562306a36Sopenharmony_ci info->read_word_data = raa_dmpvr2_read_word_data; 26662306a36Sopenharmony_ci break; 26762306a36Sopenharmony_ci default: 26862306a36Sopenharmony_ci return -ENODEV; 26962306a36Sopenharmony_ci } 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci return pmbus_do_probe(client, info); 27262306a36Sopenharmony_ci} 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_cistatic const struct i2c_device_id raa_dmpvr_id[] = { 27562306a36Sopenharmony_ci {"isl68137", raa_dmpvr1_2rail}, 27662306a36Sopenharmony_ci {"isl68220", raa_dmpvr2_2rail}, 27762306a36Sopenharmony_ci {"isl68221", raa_dmpvr2_3rail}, 27862306a36Sopenharmony_ci {"isl68222", raa_dmpvr2_2rail}, 27962306a36Sopenharmony_ci {"isl68223", raa_dmpvr2_2rail}, 28062306a36Sopenharmony_ci {"isl68224", raa_dmpvr2_3rail}, 28162306a36Sopenharmony_ci {"isl68225", raa_dmpvr2_2rail}, 28262306a36Sopenharmony_ci {"isl68226", raa_dmpvr2_3rail}, 28362306a36Sopenharmony_ci {"isl68227", raa_dmpvr2_1rail}, 28462306a36Sopenharmony_ci {"isl68229", raa_dmpvr2_3rail}, 28562306a36Sopenharmony_ci {"isl68233", raa_dmpvr2_2rail}, 28662306a36Sopenharmony_ci {"isl68239", raa_dmpvr2_3rail}, 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci {"isl69222", raa_dmpvr2_2rail}, 28962306a36Sopenharmony_ci {"isl69223", raa_dmpvr2_3rail}, 29062306a36Sopenharmony_ci {"isl69224", raa_dmpvr2_2rail}, 29162306a36Sopenharmony_ci {"isl69225", raa_dmpvr2_2rail}, 29262306a36Sopenharmony_ci {"isl69227", raa_dmpvr2_3rail}, 29362306a36Sopenharmony_ci {"isl69228", raa_dmpvr2_3rail}, 29462306a36Sopenharmony_ci {"isl69234", raa_dmpvr2_2rail}, 29562306a36Sopenharmony_ci {"isl69236", raa_dmpvr2_2rail}, 29662306a36Sopenharmony_ci {"isl69239", raa_dmpvr2_3rail}, 29762306a36Sopenharmony_ci {"isl69242", raa_dmpvr2_2rail}, 29862306a36Sopenharmony_ci {"isl69243", raa_dmpvr2_1rail}, 29962306a36Sopenharmony_ci {"isl69247", raa_dmpvr2_2rail}, 30062306a36Sopenharmony_ci {"isl69248", raa_dmpvr2_2rail}, 30162306a36Sopenharmony_ci {"isl69254", raa_dmpvr2_2rail}, 30262306a36Sopenharmony_ci {"isl69255", raa_dmpvr2_2rail}, 30362306a36Sopenharmony_ci {"isl69256", raa_dmpvr2_2rail}, 30462306a36Sopenharmony_ci {"isl69259", raa_dmpvr2_2rail}, 30562306a36Sopenharmony_ci {"isl69260", raa_dmpvr2_2rail}, 30662306a36Sopenharmony_ci {"isl69268", raa_dmpvr2_2rail}, 30762306a36Sopenharmony_ci {"isl69269", raa_dmpvr2_3rail}, 30862306a36Sopenharmony_ci {"isl69298", raa_dmpvr2_2rail}, 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci {"raa228000", raa_dmpvr2_hv}, 31162306a36Sopenharmony_ci {"raa228004", raa_dmpvr2_hv}, 31262306a36Sopenharmony_ci {"raa228006", raa_dmpvr2_hv}, 31362306a36Sopenharmony_ci {"raa228228", raa_dmpvr2_2rail_nontc}, 31462306a36Sopenharmony_ci {"raa229001", raa_dmpvr2_2rail}, 31562306a36Sopenharmony_ci {"raa229004", raa_dmpvr2_2rail}, 31662306a36Sopenharmony_ci {} 31762306a36Sopenharmony_ci}; 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, raa_dmpvr_id); 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci/* This is the driver that will be inserted */ 32262306a36Sopenharmony_cistatic struct i2c_driver isl68137_driver = { 32362306a36Sopenharmony_ci .driver = { 32462306a36Sopenharmony_ci .name = "isl68137", 32562306a36Sopenharmony_ci }, 32662306a36Sopenharmony_ci .probe = isl68137_probe, 32762306a36Sopenharmony_ci .id_table = raa_dmpvr_id, 32862306a36Sopenharmony_ci}; 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_cimodule_i2c_driver(isl68137_driver); 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ciMODULE_AUTHOR("Maxim Sloyko <maxims@google.com>"); 33362306a36Sopenharmony_ciMODULE_DESCRIPTION("PMBus driver for Renesas digital multiphase voltage regulators"); 33462306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 33562306a36Sopenharmony_ciMODULE_IMPORT_NS(PMBUS); 336