1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* |
3 | * Hardware monitoring driver for Renesas Digital Multiphase Voltage Regulators |
4 | * |
5 | * Copyright (c) 2017 Google Inc |
6 | * Copyright (c) 2020 Renesas Electronics America |
7 | * |
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> |
18 | |
19 | #include "pmbus.h" |
20 | |
21 | #define ISL68137_VOUT_AVS 0x30 |
22 | #define RAA_DMPVR2_READ_VMON 0xc8 |
23 | |
24 | enum chips { |
25 | isl68137, |
26 | 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 | |
66 | enum variants { |
67 | raa_dmpvr1_2rail, |
68 | raa_dmpvr2_1rail, |
69 | raa_dmpvr2_2rail, |
70 | raa_dmpvr2_2rail_nontc, |
71 | raa_dmpvr2_3rail, |
72 | raa_dmpvr2_hv, |
73 | }; |
74 | |
75 | static const struct i2c_device_id raa_dmpvr_id[]; |
76 | |
77 | static 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, reg: PMBUS_OPERATION); |
82 | |
83 | return sprintf(buf, fmt: "%d\n" , |
84 | (val & ISL68137_VOUT_AVS) == ISL68137_VOUT_AVS ? 1 : 0); |
85 | } |
86 | |
87 | static 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(s: buf, res: &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) { |
108 | rc = pmbus_read_word_data(client, page, phase: 0xff, |
109 | reg: PMBUS_VOUT_COMMAND); |
110 | if (rc < 0) |
111 | return rc; |
112 | |
113 | rc = pmbus_write_word_data(client, page, reg: PMBUS_VOUT_COMMAND, |
114 | word: rc); |
115 | if (rc < 0) |
116 | return rc; |
117 | } |
118 | |
119 | rc = pmbus_update_byte_data(client, page, reg: PMBUS_OPERATION, |
120 | ISL68137_VOUT_AVS, value: op_val); |
121 | |
122 | return (rc < 0) ? rc : count; |
123 | } |
124 | |
125 | static 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, page: attr->index, buf); |
133 | } |
134 | |
135 | static 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, page: attr->index, buf, count); |
143 | } |
144 | |
145 | static SENSOR_DEVICE_ATTR_RW(avs0_enable, isl68137_avs_enable, 0); |
146 | static SENSOR_DEVICE_ATTR_RW(avs1_enable, isl68137_avs_enable, 1); |
147 | |
148 | static 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 | |
154 | static const struct attribute_group enable_group = { |
155 | .attrs = enable_attrs, |
156 | }; |
157 | |
158 | static const struct attribute_group *isl68137_attribute_groups[] = { |
159 | &enable_group, |
160 | NULL, |
161 | }; |
162 | |
163 | static 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 | |
181 | static struct pmbus_driver_info raa_dmpvr_info = { |
182 | .pages = 3, |
183 | .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, |
191 | .R[PSC_VOLTAGE_IN] = 2, |
192 | .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 |
211 | | 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, |
221 | }; |
222 | |
223 | static int isl68137_probe(struct i2c_client *client) |
224 | { |
225 | struct pmbus_driver_info *info; |
226 | |
227 | info = devm_kzalloc(dev: &client->dev, size: sizeof(*info), GFP_KERNEL); |
228 | if (!info) |
229 | return -ENOMEM; |
230 | memcpy(info, &raa_dmpvr_info, sizeof(*info)); |
231 | |
232 | switch (i2c_match_id(id: raa_dmpvr_id, client)->driver_data) { |
233 | case raa_dmpvr1_2rail: |
234 | 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; |
246 | case raa_dmpvr2_2rail_nontc: |
247 | info->func[0] &= ~PMBUS_HAVE_TEMP3; |
248 | info->func[1] &= ~PMBUS_HAVE_TEMP3; |
249 | fallthrough; |
250 | 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 | |
271 | return pmbus_do_probe(client, info); |
272 | } |
273 | |
274 | static const struct i2c_device_id raa_dmpvr_id[] = { |
275 | {"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}, |
313 | {"raa228228" , raa_dmpvr2_2rail_nontc}, |
314 | {"raa229001" , raa_dmpvr2_2rail}, |
315 | {"raa229004" , raa_dmpvr2_2rail}, |
316 | {} |
317 | }; |
318 | |
319 | MODULE_DEVICE_TABLE(i2c, raa_dmpvr_id); |
320 | |
321 | /* This is the driver that will be inserted */ |
322 | static struct i2c_driver isl68137_driver = { |
323 | .driver = { |
324 | .name = "isl68137" , |
325 | }, |
326 | .probe = isl68137_probe, |
327 | .id_table = raa_dmpvr_id, |
328 | }; |
329 | |
330 | module_i2c_driver(isl68137_driver); |
331 | |
332 | MODULE_AUTHOR("Maxim Sloyko <maxims@google.com>" ); |
333 | MODULE_DESCRIPTION("PMBus driver for Renesas digital multiphase voltage regulators" ); |
334 | MODULE_LICENSE("GPL" ); |
335 | MODULE_IMPORT_NS(PMBUS); |
336 | |