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, PMBUS_VOUT_COMMAND); 53 if (rc < 0) 54 return rc; 55 56 rc = pmbus_write_word_data(client, page, PMBUS_VOUT_COMMAND, 57 rc); 58 if (rc < 0) 59 return rc; 60 } 61 62 rc = pmbus_update_byte_data(client, page, PMBUS_OPERATION, 63 ISL68137_VOUT_AVS, op_val); 64 65 return (rc < 0) ? rc : count; 66 } 67 68 static ssize_t isl68137_avs_enable_show(struct device *dev, 69 struct device_attribute *devattr, 70 char *buf) 71 { 72 struct i2c_client *client = to_i2c_client(dev->parent); 73 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); 74 75 return isl68137_avs_enable_show_page(client, attr->index, buf); 76 } 77 78 static ssize_t isl68137_avs_enable_store(struct device *dev, 79 struct device_attribute *devattr, 80 const char *buf, size_t count) 81 { 82 struct i2c_client *client = to_i2c_client(dev->parent); 83 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); 84 85 return isl68137_avs_enable_store_page(client, attr->index, buf, count); 86 } 87 88 static SENSOR_DEVICE_ATTR_RW(avs0_enable, isl68137_avs_enable, 0); 89 static SENSOR_DEVICE_ATTR_RW(avs1_enable, isl68137_avs_enable, 1); 90 91 static struct attribute *enable_attrs[] = { 92 &sensor_dev_attr_avs0_enable.dev_attr.attr, 93 &sensor_dev_attr_avs1_enable.dev_attr.attr, 94 NULL, 95 }; 96 97 static const struct attribute_group enable_group = { 98 .attrs = enable_attrs, 99 }; 100 101 static const struct attribute_group *attribute_groups[] = { 102 &enable_group, 103 NULL, 104 }; 105 106 static struct pmbus_driver_info isl68137_info = { 107 .pages = 2, 108 .format[PSC_VOLTAGE_IN] = direct, 109 .format[PSC_VOLTAGE_OUT] = direct, 110 .format[PSC_CURRENT_IN] = direct, 111 .format[PSC_CURRENT_OUT] = direct, 112 .format[PSC_POWER] = direct, 113 .format[PSC_TEMPERATURE] = direct, 114 .m[PSC_VOLTAGE_IN] = 1, 115 .b[PSC_VOLTAGE_IN] = 0, 116 .R[PSC_VOLTAGE_IN] = 3, 117 .m[PSC_VOLTAGE_OUT] = 1, 118 .b[PSC_VOLTAGE_OUT] = 0, 119 .R[PSC_VOLTAGE_OUT] = 3, 120 .m[PSC_CURRENT_IN] = 1, 121 .b[PSC_CURRENT_IN] = 0, 122 .R[PSC_CURRENT_IN] = 2, 123 .m[PSC_CURRENT_OUT] = 1, 124 .b[PSC_CURRENT_OUT] = 0, 125 .R[PSC_CURRENT_OUT] = 1, 126 .m[PSC_POWER] = 1, 127 .b[PSC_POWER] = 0, 128 .R[PSC_POWER] = 0, 129 .m[PSC_TEMPERATURE] = 1, 130 .b[PSC_TEMPERATURE] = 0, 131 .R[PSC_TEMPERATURE] = 0, 132 .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_IIN | PMBUS_HAVE_PIN 133 | PMBUS_HAVE_STATUS_INPUT | PMBUS_HAVE_TEMP | PMBUS_HAVE_TEMP2 134 | PMBUS_HAVE_TEMP3 | PMBUS_HAVE_STATUS_TEMP 135 | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT 136 | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | PMBUS_HAVE_POUT, 137 .func[1] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT 138 | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | PMBUS_HAVE_POUT, 139 .groups = attribute_groups, 140 }; 141 142 static int isl68137_probe(struct i2c_client *client, 143 const struct i2c_device_id *id) 144 { 145 return pmbus_do_probe(client, id, &isl68137_info); 146 } 147 148 static const struct i2c_device_id isl68137_id[] = { 149 {"isl68137", 0}, 150 {} 151 }; 152 153 MODULE_DEVICE_TABLE(i2c, isl68137_id); 154 155 /* This is the driver that will be inserted */ 156 static struct i2c_driver isl68137_driver = { 157 .driver = { 158 .name = "isl68137", 159 }, 160 .probe = isl68137_probe, 161 .remove = pmbus_do_remove, 162 .id_table = isl68137_id, 163 }; 164 165 module_i2c_driver(isl68137_driver); 166 167 MODULE_AUTHOR("Maxim Sloyko <maxims@google.com>"); 168 MODULE_DESCRIPTION("PMBus driver for Intersil ISL68137"); 169 MODULE_LICENSE("GPL"); 170