1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * Hardware monitoring driver for Intersil ISL68137 4 * 5 * Copyright (c) 2017 Google Inc 6 * 7 */ 8 9 #include <linux/err.h> 10 #include <linux/hwmon-sysfs.h> 11 #include <linux/i2c.h> 12 #include <linux/init.h> 13 #include <linux/kernel.h> 14 #include <linux/module.h> 15 #include <linux/string.h> 16 #include <linux/sysfs.h> 17 #include "pmbus.h" 18 19 #define ISL68137_VOUT_AVS 0x30 20 21 static ssize_t isl68137_avs_enable_show_page(struct i2c_client *client, 22 int page, 23 char *buf) 24 { 25 int val = pmbus_read_byte_data(client, page, PMBUS_OPERATION); 26 27 return sprintf(buf, "%d\n", 28 (val & ISL68137_VOUT_AVS) == ISL68137_VOUT_AVS ? 1 : 0); 29 } 30 31 static ssize_t isl68137_avs_enable_store_page(struct i2c_client *client, 32 int page, 33 const char *buf, size_t count) 34 { 35 int rc, op_val; 36 bool result; 37 38 rc = kstrtobool(buf, &result); 39 if (rc) 40 return rc; 41 42 op_val = result ? ISL68137_VOUT_AVS : 0; 43 44 /* 45 * Writes to VOUT setpoint over AVSBus will persist after the VRM is 46 * switched to PMBus control. Switching back to AVSBus control 47 * restores this persisted setpoint rather than re-initializing to 48 * PMBus VOUT_COMMAND. Writing VOUT_COMMAND first over PMBus before 49 * enabling AVS control is the workaround. 50 */ 51 if (op_val == ISL68137_VOUT_AVS) { 52 rc = pmbus_read_word_data(client, page, 0xff, 53 PMBUS_VOUT_COMMAND); 54 if (rc < 0) 55 return rc; 56 57 rc = pmbus_write_word_data(client, page, PMBUS_VOUT_COMMAND, 58 rc); 59 if (rc < 0) 60 return rc; 61 } 62 63 rc = pmbus_update_byte_data(client, page, PMBUS_OPERATION, 64 ISL68137_VOUT_AVS, op_val); 65 66 return (rc < 0) ? rc : count; 67 } 68 69 static ssize_t isl68137_avs_enable_show(struct device *dev, 70 struct device_attribute *devattr, 71 char *buf) 72 { 73 struct i2c_client *client = to_i2c_client(dev->parent); 74 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); 75 76 return isl68137_avs_enable_show_page(client, attr->index, buf); 77 } 78 79 static ssize_t isl68137_avs_enable_store(struct device *dev, 80 struct device_attribute *devattr, 81 const char *buf, size_t count) 82 { 83 struct i2c_client *client = to_i2c_client(dev->parent); 84 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); 85 86 return isl68137_avs_enable_store_page(client, attr->index, buf, count); 87 } 88 89 static SENSOR_DEVICE_ATTR_RW(avs0_enable, isl68137_avs_enable, 0); 90 static SENSOR_DEVICE_ATTR_RW(avs1_enable, isl68137_avs_enable, 1); 91 92 static struct attribute *enable_attrs[] = { 93 &sensor_dev_attr_avs0_enable.dev_attr.attr, 94 &sensor_dev_attr_avs1_enable.dev_attr.attr, 95 NULL, 96 }; 97 98 static const struct attribute_group enable_group = { 99 .attrs = enable_attrs, 100 }; 101 102 static const struct attribute_group *attribute_groups[] = { 103 &enable_group, 104 NULL, 105 }; 106 107 static struct pmbus_driver_info isl68137_info = { 108 .pages = 2, 109 .format[PSC_VOLTAGE_IN] = direct, 110 .format[PSC_VOLTAGE_OUT] = direct, 111 .format[PSC_CURRENT_IN] = direct, 112 .format[PSC_CURRENT_OUT] = direct, 113 .format[PSC_POWER] = direct, 114 .format[PSC_TEMPERATURE] = direct, 115 .m[PSC_VOLTAGE_IN] = 1, 116 .b[PSC_VOLTAGE_IN] = 0, 117 .R[PSC_VOLTAGE_IN] = 3, 118 .m[PSC_VOLTAGE_OUT] = 1, 119 .b[PSC_VOLTAGE_OUT] = 0, 120 .R[PSC_VOLTAGE_OUT] = 3, 121 .m[PSC_CURRENT_IN] = 1, 122 .b[PSC_CURRENT_IN] = 0, 123 .R[PSC_CURRENT_IN] = 2, 124 .m[PSC_CURRENT_OUT] = 1, 125 .b[PSC_CURRENT_OUT] = 0, 126 .R[PSC_CURRENT_OUT] = 1, 127 .m[PSC_POWER] = 1, 128 .b[PSC_POWER] = 0, 129 .R[PSC_POWER] = 0, 130 .m[PSC_TEMPERATURE] = 1, 131 .b[PSC_TEMPERATURE] = 0, 132 .R[PSC_TEMPERATURE] = 0, 133 .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_IIN | PMBUS_HAVE_PIN 134 | PMBUS_HAVE_STATUS_INPUT | PMBUS_HAVE_TEMP | PMBUS_HAVE_TEMP2 135 | PMBUS_HAVE_TEMP3 | PMBUS_HAVE_STATUS_TEMP 136 | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT 137 | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | PMBUS_HAVE_POUT, 138 .func[1] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT 139 | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | PMBUS_HAVE_POUT, 140 .groups = attribute_groups, 141 }; 142 143 static int isl68137_probe(struct i2c_client *client, 144 const struct i2c_device_id *id) 145 { 146 return pmbus_do_probe(client, id, &isl68137_info); 147 } 148 149 static const struct i2c_device_id isl68137_id[] = { 150 {"isl68137", 0}, 151 {} 152 }; 153 154 MODULE_DEVICE_TABLE(i2c, isl68137_id); 155 156 /* This is the driver that will be inserted */ 157 static struct i2c_driver isl68137_driver = { 158 .driver = { 159 .name = "isl68137", 160 }, 161 .probe = isl68137_probe, 162 .remove = pmbus_do_remove, 163 .id_table = isl68137_id, 164 }; 165 166 module_i2c_driver(isl68137_driver); 167 168 MODULE_AUTHOR("Maxim Sloyko <maxims@google.com>"); 169 MODULE_DESCRIPTION("PMBus driver for Intersil ISL68137"); 170 MODULE_LICENSE("GPL"); 171