blob: 0c622711ef7e0e926a20980961e98322d7663d9e [file] [log] [blame]
Maxim Sloyko038a9c32019-04-15 15:28:07 -07001// SPDX-License-Identifier: GPL-2.0+
2/*
Grant Peltierf621d612020-03-20 11:16:21 -05003 * Hardware monitoring driver for Renesas Digital Multiphase Voltage Regulators
Maxim Sloyko038a9c32019-04-15 15:28:07 -07004 *
5 * Copyright (c) 2017 Google Inc
Grant Peltierf621d612020-03-20 11:16:21 -05006 * Copyright (c) 2020 Renesas Electronics America
Maxim Sloyko038a9c32019-04-15 15:28:07 -07007 *
8 */
9
10#include <linux/err.h>
11#include <linux/hwmon-sysfs.h>
12#include <linux/i2c.h>
13#include <linux/init.h>
14#include <linux/kernel.h>
15#include <linux/module.h>
16#include <linux/string.h>
17#include <linux/sysfs.h>
Grant Peltierf621d612020-03-20 11:16:21 -050018
Maxim Sloyko038a9c32019-04-15 15:28:07 -070019#include "pmbus.h"
20
21#define ISL68137_VOUT_AVS 0x30
Grant Peltierf621d612020-03-20 11:16:21 -050022#define RAA_DMPVR2_READ_VMON 0xc8
23
Guenter Roeck37d59d12020-04-01 08:24:56 -070024enum chips {
Grant Peltierf621d612020-03-20 11:16:21 -050025 isl68137,
Guenter Roeck37d59d12020-04-01 08:24:56 -070026 isl68220,
27 isl68221,
28 isl68222,
29 isl68223,
30 isl68224,
31 isl68225,
32 isl68226,
33 isl68227,
34 isl68229,
35 isl68233,
36 isl68239,
37 isl69222,
38 isl69223,
39 isl69224,
40 isl69225,
41 isl69227,
42 isl69228,
43 isl69234,
44 isl69236,
45 isl69239,
46 isl69242,
47 isl69243,
48 isl69247,
49 isl69248,
50 isl69254,
51 isl69255,
52 isl69256,
53 isl69259,
54 isl69260,
55 isl69268,
56 isl69269,
57 isl69298,
58 raa228000,
59 raa228004,
60 raa228006,
61 raa228228,
62 raa229001,
63 raa229004,
64};
65
66enum variants {
67 raa_dmpvr1_2rail,
Grant Peltierf621d612020-03-20 11:16:21 -050068 raa_dmpvr2_1rail,
69 raa_dmpvr2_2rail,
70 raa_dmpvr2_3rail,
71 raa_dmpvr2_hv,
72};
Maxim Sloyko038a9c32019-04-15 15:28:07 -070073
74static ssize_t isl68137_avs_enable_show_page(struct i2c_client *client,
75 int page,
76 char *buf)
77{
78 int val = pmbus_read_byte_data(client, page, PMBUS_OPERATION);
79
80 return sprintf(buf, "%d\n",
81 (val & ISL68137_VOUT_AVS) == ISL68137_VOUT_AVS ? 1 : 0);
82}
83
84static ssize_t isl68137_avs_enable_store_page(struct i2c_client *client,
85 int page,
86 const char *buf, size_t count)
87{
88 int rc, op_val;
89 bool result;
90
91 rc = kstrtobool(buf, &result);
92 if (rc)
93 return rc;
94
95 op_val = result ? ISL68137_VOUT_AVS : 0;
96
97 /*
98 * Writes to VOUT setpoint over AVSBus will persist after the VRM is
99 * switched to PMBus control. Switching back to AVSBus control
100 * restores this persisted setpoint rather than re-initializing to
101 * PMBus VOUT_COMMAND. Writing VOUT_COMMAND first over PMBus before
102 * enabling AVS control is the workaround.
103 */
104 if (op_val == ISL68137_VOUT_AVS) {
Guenter Roeck43f33b62020-01-14 09:49:27 -0800105 rc = pmbus_read_word_data(client, page, 0xff,
106 PMBUS_VOUT_COMMAND);
Maxim Sloyko038a9c32019-04-15 15:28:07 -0700107 if (rc < 0)
108 return rc;
109
110 rc = pmbus_write_word_data(client, page, PMBUS_VOUT_COMMAND,
111 rc);
112 if (rc < 0)
113 return rc;
114 }
115
116 rc = pmbus_update_byte_data(client, page, PMBUS_OPERATION,
117 ISL68137_VOUT_AVS, op_val);
118
119 return (rc < 0) ? rc : count;
120}
121
122static ssize_t isl68137_avs_enable_show(struct device *dev,
123 struct device_attribute *devattr,
124 char *buf)
125{
126 struct i2c_client *client = to_i2c_client(dev->parent);
127 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
128
129 return isl68137_avs_enable_show_page(client, attr->index, buf);
130}
131
132static ssize_t isl68137_avs_enable_store(struct device *dev,
133 struct device_attribute *devattr,
134 const char *buf, size_t count)
135{
136 struct i2c_client *client = to_i2c_client(dev->parent);
137 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
138
139 return isl68137_avs_enable_store_page(client, attr->index, buf, count);
140}
141
142static SENSOR_DEVICE_ATTR_RW(avs0_enable, isl68137_avs_enable, 0);
143static SENSOR_DEVICE_ATTR_RW(avs1_enable, isl68137_avs_enable, 1);
144
145static struct attribute *enable_attrs[] = {
146 &sensor_dev_attr_avs0_enable.dev_attr.attr,
147 &sensor_dev_attr_avs1_enable.dev_attr.attr,
148 NULL,
149};
150
151static const struct attribute_group enable_group = {
152 .attrs = enable_attrs,
153};
154
Grant Peltierf621d612020-03-20 11:16:21 -0500155static const struct attribute_group *isl68137_attribute_groups[] = {
Maxim Sloyko038a9c32019-04-15 15:28:07 -0700156 &enable_group,
157 NULL,
158};
159
Grant Peltierf621d612020-03-20 11:16:21 -0500160static int raa_dmpvr2_read_word_data(struct i2c_client *client, int page,
161 int phase, int reg)
162{
163 int ret;
164
165 switch (reg) {
166 case PMBUS_VIRT_READ_VMON:
167 ret = pmbus_read_word_data(client, page, phase,
168 RAA_DMPVR2_READ_VMON);
169 break;
170 default:
171 ret = -ENODATA;
172 break;
173 }
174
175 return ret;
176}
177
178static struct pmbus_driver_info raa_dmpvr_info = {
179 .pages = 3,
Maxim Sloyko038a9c32019-04-15 15:28:07 -0700180 .format[PSC_VOLTAGE_IN] = direct,
181 .format[PSC_VOLTAGE_OUT] = direct,
182 .format[PSC_CURRENT_IN] = direct,
183 .format[PSC_CURRENT_OUT] = direct,
184 .format[PSC_POWER] = direct,
185 .format[PSC_TEMPERATURE] = direct,
186 .m[PSC_VOLTAGE_IN] = 1,
187 .b[PSC_VOLTAGE_IN] = 0,
Grant Peltierf621d612020-03-20 11:16:21 -0500188 .R[PSC_VOLTAGE_IN] = 2,
Maxim Sloyko038a9c32019-04-15 15:28:07 -0700189 .m[PSC_VOLTAGE_OUT] = 1,
190 .b[PSC_VOLTAGE_OUT] = 0,
191 .R[PSC_VOLTAGE_OUT] = 3,
192 .m[PSC_CURRENT_IN] = 1,
193 .b[PSC_CURRENT_IN] = 0,
194 .R[PSC_CURRENT_IN] = 2,
195 .m[PSC_CURRENT_OUT] = 1,
196 .b[PSC_CURRENT_OUT] = 0,
197 .R[PSC_CURRENT_OUT] = 1,
198 .m[PSC_POWER] = 1,
199 .b[PSC_POWER] = 0,
200 .R[PSC_POWER] = 0,
201 .m[PSC_TEMPERATURE] = 1,
202 .b[PSC_TEMPERATURE] = 0,
203 .R[PSC_TEMPERATURE] = 0,
204 .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_IIN | PMBUS_HAVE_PIN
205 | PMBUS_HAVE_STATUS_INPUT | PMBUS_HAVE_TEMP | PMBUS_HAVE_TEMP2
206 | PMBUS_HAVE_TEMP3 | PMBUS_HAVE_STATUS_TEMP
207 | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT
Grant Peltierf621d612020-03-20 11:16:21 -0500208 | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | PMBUS_HAVE_POUT
209 | PMBUS_HAVE_VMON,
210 .func[1] = PMBUS_HAVE_IIN | PMBUS_HAVE_PIN | PMBUS_HAVE_STATUS_INPUT
211 | PMBUS_HAVE_TEMP | PMBUS_HAVE_TEMP3 | PMBUS_HAVE_STATUS_TEMP
212 | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_IOUT
213 | PMBUS_HAVE_STATUS_IOUT | PMBUS_HAVE_POUT,
214 .func[2] = PMBUS_HAVE_IIN | PMBUS_HAVE_PIN | PMBUS_HAVE_STATUS_INPUT
215 | PMBUS_HAVE_TEMP | PMBUS_HAVE_TEMP3 | PMBUS_HAVE_STATUS_TEMP
216 | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_IOUT
217 | PMBUS_HAVE_STATUS_IOUT | PMBUS_HAVE_POUT,
Maxim Sloyko038a9c32019-04-15 15:28:07 -0700218};
219
220static int isl68137_probe(struct i2c_client *client,
221 const struct i2c_device_id *id)
222{
Grant Peltierf621d612020-03-20 11:16:21 -0500223 struct pmbus_driver_info *info;
224
225 info = devm_kzalloc(&client->dev, sizeof(*info), GFP_KERNEL);
226 if (!info)
227 return -ENOMEM;
228 memcpy(info, &raa_dmpvr_info, sizeof(*info));
229
230 switch (id->driver_data) {
Guenter Roeck37d59d12020-04-01 08:24:56 -0700231 case raa_dmpvr1_2rail:
Grant Peltierf621d612020-03-20 11:16:21 -0500232 info->pages = 2;
233 info->R[PSC_VOLTAGE_IN] = 3;
234 info->func[0] &= ~PMBUS_HAVE_VMON;
235 info->func[1] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT
236 | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT
237 | PMBUS_HAVE_POUT;
238 info->groups = isl68137_attribute_groups;
239 break;
240 case raa_dmpvr2_1rail:
241 info->pages = 1;
242 info->read_word_data = raa_dmpvr2_read_word_data;
243 break;
244 case raa_dmpvr2_2rail:
245 info->pages = 2;
246 info->read_word_data = raa_dmpvr2_read_word_data;
247 break;
248 case raa_dmpvr2_3rail:
249 info->read_word_data = raa_dmpvr2_read_word_data;
250 break;
251 case raa_dmpvr2_hv:
252 info->pages = 1;
253 info->R[PSC_VOLTAGE_IN] = 1;
254 info->m[PSC_VOLTAGE_OUT] = 2;
255 info->R[PSC_VOLTAGE_OUT] = 2;
256 info->m[PSC_CURRENT_IN] = 2;
257 info->m[PSC_POWER] = 2;
258 info->R[PSC_POWER] = -1;
259 info->read_word_data = raa_dmpvr2_read_word_data;
260 break;
261 default:
262 return -ENODEV;
263 }
264
265 return pmbus_do_probe(client, id, info);
Maxim Sloyko038a9c32019-04-15 15:28:07 -0700266}
267
Grant Peltierf621d612020-03-20 11:16:21 -0500268static const struct i2c_device_id raa_dmpvr_id[] = {
Guenter Roeck37d59d12020-04-01 08:24:56 -0700269 {"isl68137", raa_dmpvr1_2rail},
270 {"isl68220", raa_dmpvr2_2rail},
271 {"isl68221", raa_dmpvr2_3rail},
272 {"isl68222", raa_dmpvr2_2rail},
273 {"isl68223", raa_dmpvr2_2rail},
274 {"isl68224", raa_dmpvr2_3rail},
275 {"isl68225", raa_dmpvr2_2rail},
276 {"isl68226", raa_dmpvr2_3rail},
277 {"isl68227", raa_dmpvr2_1rail},
278 {"isl68229", raa_dmpvr2_3rail},
279 {"isl68233", raa_dmpvr2_2rail},
280 {"isl68239", raa_dmpvr2_3rail},
281
282 {"isl69222", raa_dmpvr2_2rail},
283 {"isl69223", raa_dmpvr2_3rail},
284 {"isl69224", raa_dmpvr2_2rail},
285 {"isl69225", raa_dmpvr2_2rail},
286 {"isl69227", raa_dmpvr2_3rail},
287 {"isl69228", raa_dmpvr2_3rail},
288 {"isl69234", raa_dmpvr2_2rail},
289 {"isl69236", raa_dmpvr2_2rail},
290 {"isl69239", raa_dmpvr2_3rail},
291 {"isl69242", raa_dmpvr2_2rail},
292 {"isl69243", raa_dmpvr2_1rail},
293 {"isl69247", raa_dmpvr2_2rail},
294 {"isl69248", raa_dmpvr2_2rail},
295 {"isl69254", raa_dmpvr2_2rail},
296 {"isl69255", raa_dmpvr2_2rail},
297 {"isl69256", raa_dmpvr2_2rail},
298 {"isl69259", raa_dmpvr2_2rail},
299 {"isl69260", raa_dmpvr2_2rail},
300 {"isl69268", raa_dmpvr2_2rail},
301 {"isl69269", raa_dmpvr2_3rail},
302 {"isl69298", raa_dmpvr2_2rail},
303
304 {"raa228000", raa_dmpvr2_hv},
305 {"raa228004", raa_dmpvr2_hv},
306 {"raa228006", raa_dmpvr2_hv},
307 {"raa228228", raa_dmpvr2_2rail},
308 {"raa229001", raa_dmpvr2_2rail},
309 {"raa229004", raa_dmpvr2_2rail},
Maxim Sloyko038a9c32019-04-15 15:28:07 -0700310 {}
311};
312
Grant Peltierf621d612020-03-20 11:16:21 -0500313MODULE_DEVICE_TABLE(i2c, raa_dmpvr_id);
Maxim Sloyko038a9c32019-04-15 15:28:07 -0700314
315/* This is the driver that will be inserted */
316static struct i2c_driver isl68137_driver = {
317 .driver = {
318 .name = "isl68137",
319 },
320 .probe = isl68137_probe,
321 .remove = pmbus_do_remove,
Grant Peltierf621d612020-03-20 11:16:21 -0500322 .id_table = raa_dmpvr_id,
Maxim Sloyko038a9c32019-04-15 15:28:07 -0700323};
324
325module_i2c_driver(isl68137_driver);
326
327MODULE_AUTHOR("Maxim Sloyko <maxims@google.com>");
Grant Peltierf621d612020-03-20 11:16:21 -0500328MODULE_DESCRIPTION("PMBus driver for Renesas digital multiphase voltage regulators");
Maxim Sloyko038a9c32019-04-15 15:28:07 -0700329MODULE_LICENSE("GPL");