blob: 2bee930d39002150c3854b937a22553c27a1413a [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,
Grant Peltier51fb91e2020-08-10 13:36:27 -050070 raa_dmpvr2_2rail_nontc,
Grant Peltierf621d612020-03-20 11:16:21 -050071 raa_dmpvr2_3rail,
72 raa_dmpvr2_hv,
73};
Maxim Sloyko038a9c32019-04-15 15:28:07 -070074
Stephen Kittdd431932020-08-08 23:00:04 +020075static const struct i2c_device_id raa_dmpvr_id[];
76
Maxim Sloyko038a9c32019-04-15 15:28:07 -070077static ssize_t isl68137_avs_enable_show_page(struct i2c_client *client,
78 int page,
79 char *buf)
80{
81 int val = pmbus_read_byte_data(client, page, PMBUS_OPERATION);
82
83 return sprintf(buf, "%d\n",
84 (val & ISL68137_VOUT_AVS) == ISL68137_VOUT_AVS ? 1 : 0);
85}
86
87static ssize_t isl68137_avs_enable_store_page(struct i2c_client *client,
88 int page,
89 const char *buf, size_t count)
90{
91 int rc, op_val;
92 bool result;
93
94 rc = kstrtobool(buf, &result);
95 if (rc)
96 return rc;
97
98 op_val = result ? ISL68137_VOUT_AVS : 0;
99
100 /*
101 * Writes to VOUT setpoint over AVSBus will persist after the VRM is
102 * switched to PMBus control. Switching back to AVSBus control
103 * restores this persisted setpoint rather than re-initializing to
104 * PMBus VOUT_COMMAND. Writing VOUT_COMMAND first over PMBus before
105 * enabling AVS control is the workaround.
106 */
107 if (op_val == ISL68137_VOUT_AVS) {
Guenter Roeck43f33b62020-01-14 09:49:27 -0800108 rc = pmbus_read_word_data(client, page, 0xff,
109 PMBUS_VOUT_COMMAND);
Maxim Sloyko038a9c32019-04-15 15:28:07 -0700110 if (rc < 0)
111 return rc;
112
113 rc = pmbus_write_word_data(client, page, PMBUS_VOUT_COMMAND,
114 rc);
115 if (rc < 0)
116 return rc;
117 }
118
119 rc = pmbus_update_byte_data(client, page, PMBUS_OPERATION,
120 ISL68137_VOUT_AVS, op_val);
121
122 return (rc < 0) ? rc : count;
123}
124
125static ssize_t isl68137_avs_enable_show(struct device *dev,
126 struct device_attribute *devattr,
127 char *buf)
128{
129 struct i2c_client *client = to_i2c_client(dev->parent);
130 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
131
132 return isl68137_avs_enable_show_page(client, attr->index, buf);
133}
134
135static ssize_t isl68137_avs_enable_store(struct device *dev,
136 struct device_attribute *devattr,
137 const char *buf, size_t count)
138{
139 struct i2c_client *client = to_i2c_client(dev->parent);
140 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
141
142 return isl68137_avs_enable_store_page(client, attr->index, buf, count);
143}
144
145static SENSOR_DEVICE_ATTR_RW(avs0_enable, isl68137_avs_enable, 0);
146static SENSOR_DEVICE_ATTR_RW(avs1_enable, isl68137_avs_enable, 1);
147
148static struct attribute *enable_attrs[] = {
149 &sensor_dev_attr_avs0_enable.dev_attr.attr,
150 &sensor_dev_attr_avs1_enable.dev_attr.attr,
151 NULL,
152};
153
154static const struct attribute_group enable_group = {
155 .attrs = enable_attrs,
156};
157
Grant Peltierf621d612020-03-20 11:16:21 -0500158static const struct attribute_group *isl68137_attribute_groups[] = {
Maxim Sloyko038a9c32019-04-15 15:28:07 -0700159 &enable_group,
160 NULL,
161};
162
Grant Peltierf621d612020-03-20 11:16:21 -0500163static int raa_dmpvr2_read_word_data(struct i2c_client *client, int page,
164 int phase, int reg)
165{
166 int ret;
167
168 switch (reg) {
169 case PMBUS_VIRT_READ_VMON:
170 ret = pmbus_read_word_data(client, page, phase,
171 RAA_DMPVR2_READ_VMON);
172 break;
173 default:
174 ret = -ENODATA;
175 break;
176 }
177
178 return ret;
179}
180
181static struct pmbus_driver_info raa_dmpvr_info = {
182 .pages = 3,
Maxim Sloyko038a9c32019-04-15 15:28:07 -0700183 .format[PSC_VOLTAGE_IN] = direct,
184 .format[PSC_VOLTAGE_OUT] = direct,
185 .format[PSC_CURRENT_IN] = direct,
186 .format[PSC_CURRENT_OUT] = direct,
187 .format[PSC_POWER] = direct,
188 .format[PSC_TEMPERATURE] = direct,
189 .m[PSC_VOLTAGE_IN] = 1,
190 .b[PSC_VOLTAGE_IN] = 0,
Grant Peltierf621d612020-03-20 11:16:21 -0500191 .R[PSC_VOLTAGE_IN] = 2,
Maxim Sloyko038a9c32019-04-15 15:28:07 -0700192 .m[PSC_VOLTAGE_OUT] = 1,
193 .b[PSC_VOLTAGE_OUT] = 0,
194 .R[PSC_VOLTAGE_OUT] = 3,
195 .m[PSC_CURRENT_IN] = 1,
196 .b[PSC_CURRENT_IN] = 0,
197 .R[PSC_CURRENT_IN] = 2,
198 .m[PSC_CURRENT_OUT] = 1,
199 .b[PSC_CURRENT_OUT] = 0,
200 .R[PSC_CURRENT_OUT] = 1,
201 .m[PSC_POWER] = 1,
202 .b[PSC_POWER] = 0,
203 .R[PSC_POWER] = 0,
204 .m[PSC_TEMPERATURE] = 1,
205 .b[PSC_TEMPERATURE] = 0,
206 .R[PSC_TEMPERATURE] = 0,
207 .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_IIN | PMBUS_HAVE_PIN
208 | PMBUS_HAVE_STATUS_INPUT | PMBUS_HAVE_TEMP | PMBUS_HAVE_TEMP2
209 | PMBUS_HAVE_TEMP3 | PMBUS_HAVE_STATUS_TEMP
210 | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT
Grant Peltierf621d612020-03-20 11:16:21 -0500211 | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | PMBUS_HAVE_POUT
212 | PMBUS_HAVE_VMON,
213 .func[1] = PMBUS_HAVE_IIN | PMBUS_HAVE_PIN | PMBUS_HAVE_STATUS_INPUT
214 | PMBUS_HAVE_TEMP | PMBUS_HAVE_TEMP3 | PMBUS_HAVE_STATUS_TEMP
215 | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_IOUT
216 | PMBUS_HAVE_STATUS_IOUT | PMBUS_HAVE_POUT,
217 .func[2] = PMBUS_HAVE_IIN | PMBUS_HAVE_PIN | PMBUS_HAVE_STATUS_INPUT
218 | PMBUS_HAVE_TEMP | PMBUS_HAVE_TEMP3 | PMBUS_HAVE_STATUS_TEMP
219 | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_IOUT
220 | PMBUS_HAVE_STATUS_IOUT | PMBUS_HAVE_POUT,
Maxim Sloyko038a9c32019-04-15 15:28:07 -0700221};
222
Stephen Kittdd431932020-08-08 23:00:04 +0200223static int isl68137_probe(struct i2c_client *client)
Maxim Sloyko038a9c32019-04-15 15:28:07 -0700224{
Grant Peltierf621d612020-03-20 11:16:21 -0500225 struct pmbus_driver_info *info;
226
227 info = devm_kzalloc(&client->dev, sizeof(*info), GFP_KERNEL);
228 if (!info)
229 return -ENOMEM;
230 memcpy(info, &raa_dmpvr_info, sizeof(*info));
231
Stephen Kittdd431932020-08-08 23:00:04 +0200232 switch (i2c_match_id(raa_dmpvr_id, client)->driver_data) {
Guenter Roeck37d59d12020-04-01 08:24:56 -0700233 case raa_dmpvr1_2rail:
Grant Peltierf621d612020-03-20 11:16:21 -0500234 info->pages = 2;
235 info->R[PSC_VOLTAGE_IN] = 3;
236 info->func[0] &= ~PMBUS_HAVE_VMON;
237 info->func[1] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT
238 | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT
239 | PMBUS_HAVE_POUT;
240 info->groups = isl68137_attribute_groups;
241 break;
242 case raa_dmpvr2_1rail:
243 info->pages = 1;
244 info->read_word_data = raa_dmpvr2_read_word_data;
245 break;
Grant Peltier51fb91e2020-08-10 13:36:27 -0500246 case raa_dmpvr2_2rail_nontc:
247 info->func[0] &= ~PMBUS_HAVE_TEMP;
248 info->func[1] &= ~PMBUS_HAVE_TEMP;
249 fallthrough;
Grant Peltierf621d612020-03-20 11:16:21 -0500250 case raa_dmpvr2_2rail:
251 info->pages = 2;
252 info->read_word_data = raa_dmpvr2_read_word_data;
253 break;
254 case raa_dmpvr2_3rail:
255 info->read_word_data = raa_dmpvr2_read_word_data;
256 break;
257 case raa_dmpvr2_hv:
258 info->pages = 1;
259 info->R[PSC_VOLTAGE_IN] = 1;
260 info->m[PSC_VOLTAGE_OUT] = 2;
261 info->R[PSC_VOLTAGE_OUT] = 2;
262 info->m[PSC_CURRENT_IN] = 2;
263 info->m[PSC_POWER] = 2;
264 info->R[PSC_POWER] = -1;
265 info->read_word_data = raa_dmpvr2_read_word_data;
266 break;
267 default:
268 return -ENODEV;
269 }
270
Stephen Kittdd431932020-08-08 23:00:04 +0200271 return pmbus_do_probe(client, info);
Maxim Sloyko038a9c32019-04-15 15:28:07 -0700272}
273
Grant Peltierf621d612020-03-20 11:16:21 -0500274static const struct i2c_device_id raa_dmpvr_id[] = {
Guenter Roeck37d59d12020-04-01 08:24:56 -0700275 {"isl68137", raa_dmpvr1_2rail},
276 {"isl68220", raa_dmpvr2_2rail},
277 {"isl68221", raa_dmpvr2_3rail},
278 {"isl68222", raa_dmpvr2_2rail},
279 {"isl68223", raa_dmpvr2_2rail},
280 {"isl68224", raa_dmpvr2_3rail},
281 {"isl68225", raa_dmpvr2_2rail},
282 {"isl68226", raa_dmpvr2_3rail},
283 {"isl68227", raa_dmpvr2_1rail},
284 {"isl68229", raa_dmpvr2_3rail},
285 {"isl68233", raa_dmpvr2_2rail},
286 {"isl68239", raa_dmpvr2_3rail},
287
288 {"isl69222", raa_dmpvr2_2rail},
289 {"isl69223", raa_dmpvr2_3rail},
290 {"isl69224", raa_dmpvr2_2rail},
291 {"isl69225", raa_dmpvr2_2rail},
292 {"isl69227", raa_dmpvr2_3rail},
293 {"isl69228", raa_dmpvr2_3rail},
294 {"isl69234", raa_dmpvr2_2rail},
295 {"isl69236", raa_dmpvr2_2rail},
296 {"isl69239", raa_dmpvr2_3rail},
297 {"isl69242", raa_dmpvr2_2rail},
298 {"isl69243", raa_dmpvr2_1rail},
299 {"isl69247", raa_dmpvr2_2rail},
300 {"isl69248", raa_dmpvr2_2rail},
301 {"isl69254", raa_dmpvr2_2rail},
302 {"isl69255", raa_dmpvr2_2rail},
303 {"isl69256", raa_dmpvr2_2rail},
304 {"isl69259", raa_dmpvr2_2rail},
305 {"isl69260", raa_dmpvr2_2rail},
306 {"isl69268", raa_dmpvr2_2rail},
307 {"isl69269", raa_dmpvr2_3rail},
308 {"isl69298", raa_dmpvr2_2rail},
309
310 {"raa228000", raa_dmpvr2_hv},
311 {"raa228004", raa_dmpvr2_hv},
312 {"raa228006", raa_dmpvr2_hv},
Grant Peltier51fb91e2020-08-10 13:36:27 -0500313 {"raa228228", raa_dmpvr2_2rail_nontc},
Guenter Roeck37d59d12020-04-01 08:24:56 -0700314 {"raa229001", raa_dmpvr2_2rail},
315 {"raa229004", raa_dmpvr2_2rail},
Maxim Sloyko038a9c32019-04-15 15:28:07 -0700316 {}
317};
318
Grant Peltierf621d612020-03-20 11:16:21 -0500319MODULE_DEVICE_TABLE(i2c, raa_dmpvr_id);
Maxim Sloyko038a9c32019-04-15 15:28:07 -0700320
321/* This is the driver that will be inserted */
322static struct i2c_driver isl68137_driver = {
323 .driver = {
324 .name = "isl68137",
325 },
Stephen Kittdd431932020-08-08 23:00:04 +0200326 .probe_new = isl68137_probe,
Grant Peltierf621d612020-03-20 11:16:21 -0500327 .id_table = raa_dmpvr_id,
Maxim Sloyko038a9c32019-04-15 15:28:07 -0700328};
329
330module_i2c_driver(isl68137_driver);
331
332MODULE_AUTHOR("Maxim Sloyko <maxims@google.com>");
Grant Peltierf621d612020-03-20 11:16:21 -0500333MODULE_DESCRIPTION("PMBus driver for Renesas digital multiphase voltage regulators");
Maxim Sloyko038a9c32019-04-15 15:28:07 -0700334MODULE_LICENSE("GPL");