1 // SPDX-License-Identifier: GPL-2.0+
3 * Hardware monitoring driver for Renesas Digital Multiphase Voltage Regulators
5 * Copyright (c) 2017 Google Inc
6 * Copyright (c) 2020 Renesas Electronics America
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>
21 #define ISL68137_VOUT_AVS 0x30
22 #define RAA_DMPVR2_READ_VMON 0xc8
32 static ssize_t
isl68137_avs_enable_show_page(struct i2c_client
*client
,
36 int val
= pmbus_read_byte_data(client
, page
, PMBUS_OPERATION
);
38 return sprintf(buf
, "%d\n",
39 (val
& ISL68137_VOUT_AVS
) == ISL68137_VOUT_AVS
? 1 : 0);
42 static ssize_t
isl68137_avs_enable_store_page(struct i2c_client
*client
,
44 const char *buf
, size_t count
)
49 rc
= kstrtobool(buf
, &result
);
53 op_val
= result
? ISL68137_VOUT_AVS
: 0;
56 * Writes to VOUT setpoint over AVSBus will persist after the VRM is
57 * switched to PMBus control. Switching back to AVSBus control
58 * restores this persisted setpoint rather than re-initializing to
59 * PMBus VOUT_COMMAND. Writing VOUT_COMMAND first over PMBus before
60 * enabling AVS control is the workaround.
62 if (op_val
== ISL68137_VOUT_AVS
) {
63 rc
= pmbus_read_word_data(client
, page
, 0xff,
68 rc
= pmbus_write_word_data(client
, page
, PMBUS_VOUT_COMMAND
,
74 rc
= pmbus_update_byte_data(client
, page
, PMBUS_OPERATION
,
75 ISL68137_VOUT_AVS
, op_val
);
77 return (rc
< 0) ? rc
: count
;
80 static ssize_t
isl68137_avs_enable_show(struct device
*dev
,
81 struct device_attribute
*devattr
,
84 struct i2c_client
*client
= to_i2c_client(dev
->parent
);
85 struct sensor_device_attribute
*attr
= to_sensor_dev_attr(devattr
);
87 return isl68137_avs_enable_show_page(client
, attr
->index
, buf
);
90 static ssize_t
isl68137_avs_enable_store(struct device
*dev
,
91 struct device_attribute
*devattr
,
92 const char *buf
, size_t count
)
94 struct i2c_client
*client
= to_i2c_client(dev
->parent
);
95 struct sensor_device_attribute
*attr
= to_sensor_dev_attr(devattr
);
97 return isl68137_avs_enable_store_page(client
, attr
->index
, buf
, count
);
100 static SENSOR_DEVICE_ATTR_RW(avs0_enable
, isl68137_avs_enable
, 0);
101 static SENSOR_DEVICE_ATTR_RW(avs1_enable
, isl68137_avs_enable
, 1);
103 static struct attribute
*enable_attrs
[] = {
104 &sensor_dev_attr_avs0_enable
.dev_attr
.attr
,
105 &sensor_dev_attr_avs1_enable
.dev_attr
.attr
,
109 static const struct attribute_group enable_group
= {
110 .attrs
= enable_attrs
,
113 static const struct attribute_group
*isl68137_attribute_groups
[] = {
118 static int raa_dmpvr2_read_word_data(struct i2c_client
*client
, int page
,
124 case PMBUS_VIRT_READ_VMON
:
125 ret
= pmbus_read_word_data(client
, page
, phase
,
126 RAA_DMPVR2_READ_VMON
);
136 static struct pmbus_driver_info raa_dmpvr_info
= {
138 .format
[PSC_VOLTAGE_IN
] = direct
,
139 .format
[PSC_VOLTAGE_OUT
] = direct
,
140 .format
[PSC_CURRENT_IN
] = direct
,
141 .format
[PSC_CURRENT_OUT
] = direct
,
142 .format
[PSC_POWER
] = direct
,
143 .format
[PSC_TEMPERATURE
] = direct
,
144 .m
[PSC_VOLTAGE_IN
] = 1,
145 .b
[PSC_VOLTAGE_IN
] = 0,
146 .R
[PSC_VOLTAGE_IN
] = 2,
147 .m
[PSC_VOLTAGE_OUT
] = 1,
148 .b
[PSC_VOLTAGE_OUT
] = 0,
149 .R
[PSC_VOLTAGE_OUT
] = 3,
150 .m
[PSC_CURRENT_IN
] = 1,
151 .b
[PSC_CURRENT_IN
] = 0,
152 .R
[PSC_CURRENT_IN
] = 2,
153 .m
[PSC_CURRENT_OUT
] = 1,
154 .b
[PSC_CURRENT_OUT
] = 0,
155 .R
[PSC_CURRENT_OUT
] = 1,
159 .m
[PSC_TEMPERATURE
] = 1,
160 .b
[PSC_TEMPERATURE
] = 0,
161 .R
[PSC_TEMPERATURE
] = 0,
162 .func
[0] = PMBUS_HAVE_VIN
| PMBUS_HAVE_IIN
| PMBUS_HAVE_PIN
163 | PMBUS_HAVE_STATUS_INPUT
| PMBUS_HAVE_TEMP
| PMBUS_HAVE_TEMP2
164 | PMBUS_HAVE_TEMP3
| PMBUS_HAVE_STATUS_TEMP
165 | PMBUS_HAVE_VOUT
| PMBUS_HAVE_STATUS_VOUT
166 | PMBUS_HAVE_IOUT
| PMBUS_HAVE_STATUS_IOUT
| PMBUS_HAVE_POUT
168 .func
[1] = PMBUS_HAVE_IIN
| PMBUS_HAVE_PIN
| PMBUS_HAVE_STATUS_INPUT
169 | PMBUS_HAVE_TEMP
| PMBUS_HAVE_TEMP3
| PMBUS_HAVE_STATUS_TEMP
170 | PMBUS_HAVE_VOUT
| PMBUS_HAVE_STATUS_VOUT
| PMBUS_HAVE_IOUT
171 | PMBUS_HAVE_STATUS_IOUT
| PMBUS_HAVE_POUT
,
172 .func
[2] = PMBUS_HAVE_IIN
| PMBUS_HAVE_PIN
| PMBUS_HAVE_STATUS_INPUT
173 | PMBUS_HAVE_TEMP
| PMBUS_HAVE_TEMP3
| PMBUS_HAVE_STATUS_TEMP
174 | PMBUS_HAVE_VOUT
| PMBUS_HAVE_STATUS_VOUT
| PMBUS_HAVE_IOUT
175 | PMBUS_HAVE_STATUS_IOUT
| PMBUS_HAVE_POUT
,
178 static int isl68137_probe(struct i2c_client
*client
,
179 const struct i2c_device_id
*id
)
181 struct pmbus_driver_info
*info
;
183 info
= devm_kzalloc(&client
->dev
, sizeof(*info
), GFP_KERNEL
);
186 memcpy(info
, &raa_dmpvr_info
, sizeof(*info
));
188 switch (id
->driver_data
) {
191 info
->R
[PSC_VOLTAGE_IN
] = 3;
192 info
->func
[0] &= ~PMBUS_HAVE_VMON
;
193 info
->func
[1] = PMBUS_HAVE_VOUT
| PMBUS_HAVE_STATUS_VOUT
194 | PMBUS_HAVE_IOUT
| PMBUS_HAVE_STATUS_IOUT
196 info
->groups
= isl68137_attribute_groups
;
198 case raa_dmpvr2_1rail
:
200 info
->read_word_data
= raa_dmpvr2_read_word_data
;
202 case raa_dmpvr2_2rail
:
204 info
->read_word_data
= raa_dmpvr2_read_word_data
;
206 case raa_dmpvr2_3rail
:
207 info
->read_word_data
= raa_dmpvr2_read_word_data
;
211 info
->R
[PSC_VOLTAGE_IN
] = 1;
212 info
->m
[PSC_VOLTAGE_OUT
] = 2;
213 info
->R
[PSC_VOLTAGE_OUT
] = 2;
214 info
->m
[PSC_CURRENT_IN
] = 2;
215 info
->m
[PSC_POWER
] = 2;
216 info
->R
[PSC_POWER
] = -1;
217 info
->read_word_data
= raa_dmpvr2_read_word_data
;
223 return pmbus_do_probe(client
, id
, info
);
226 static const struct i2c_device_id raa_dmpvr_id
[] = {
227 {"isl68137", isl68137
},
228 {"raa_dmpvr2_1rail", raa_dmpvr2_1rail
},
229 {"raa_dmpvr2_2rail", raa_dmpvr2_2rail
},
230 {"raa_dmpvr2_3rail", raa_dmpvr2_3rail
},
231 {"raa_dmpvr2_hv", raa_dmpvr2_hv
},
235 MODULE_DEVICE_TABLE(i2c
, raa_dmpvr_id
);
237 /* This is the driver that will be inserted */
238 static struct i2c_driver isl68137_driver
= {
242 .probe
= isl68137_probe
,
243 .remove
= pmbus_do_remove
,
244 .id_table
= raa_dmpvr_id
,
247 module_i2c_driver(isl68137_driver
);
249 MODULE_AUTHOR("Maxim Sloyko <maxims@google.com>");
250 MODULE_DESCRIPTION("PMBus driver for Renesas digital multiphase voltage regulators");
251 MODULE_LICENSE("GPL");