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