]> git.proxmox.com Git - mirror_ubuntu-kernels.git/blob - drivers/hwmon/pmbus/ibm-cffps.c
Merge remote-tracking branches 'regulator/fix/resume' and 'regulator/fix/stm32-vfrefb...
[mirror_ubuntu-kernels.git] / drivers / hwmon / pmbus / ibm-cffps.c
1 /*
2 * Copyright 2017 IBM Corp.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 */
9
10 #include <linux/bitops.h>
11 #include <linux/debugfs.h>
12 #include <linux/device.h>
13 #include <linux/fs.h>
14 #include <linux/i2c.h>
15 #include <linux/jiffies.h>
16 #include <linux/leds.h>
17 #include <linux/module.h>
18 #include <linux/mutex.h>
19 #include <linux/pmbus.h>
20
21 #include "pmbus.h"
22
23 #define CFFPS_FRU_CMD 0x9A
24 #define CFFPS_PN_CMD 0x9B
25 #define CFFPS_SN_CMD 0x9E
26 #define CFFPS_CCIN_CMD 0xBD
27 #define CFFPS_FW_CMD_START 0xFA
28 #define CFFPS_FW_NUM_BYTES 4
29 #define CFFPS_SYS_CONFIG_CMD 0xDA
30
31 #define CFFPS_INPUT_HISTORY_CMD 0xD6
32 #define CFFPS_INPUT_HISTORY_SIZE 100
33
34 /* STATUS_MFR_SPECIFIC bits */
35 #define CFFPS_MFR_FAN_FAULT BIT(0)
36 #define CFFPS_MFR_THERMAL_FAULT BIT(1)
37 #define CFFPS_MFR_OV_FAULT BIT(2)
38 #define CFFPS_MFR_UV_FAULT BIT(3)
39 #define CFFPS_MFR_PS_KILL BIT(4)
40 #define CFFPS_MFR_OC_FAULT BIT(5)
41 #define CFFPS_MFR_VAUX_FAULT BIT(6)
42 #define CFFPS_MFR_CURRENT_SHARE_WARNING BIT(7)
43
44 #define CFFPS_LED_BLINK BIT(0)
45 #define CFFPS_LED_ON BIT(1)
46 #define CFFPS_LED_OFF BIT(2)
47 #define CFFPS_BLINK_RATE_MS 250
48
49 enum {
50 CFFPS_DEBUGFS_INPUT_HISTORY = 0,
51 CFFPS_DEBUGFS_FRU,
52 CFFPS_DEBUGFS_PN,
53 CFFPS_DEBUGFS_SN,
54 CFFPS_DEBUGFS_CCIN,
55 CFFPS_DEBUGFS_FW,
56 CFFPS_DEBUGFS_NUM_ENTRIES
57 };
58
59 struct ibm_cffps_input_history {
60 struct mutex update_lock;
61 unsigned long last_update;
62
63 u8 byte_count;
64 u8 data[CFFPS_INPUT_HISTORY_SIZE];
65 };
66
67 struct ibm_cffps {
68 struct i2c_client *client;
69
70 struct ibm_cffps_input_history input_history;
71
72 int debugfs_entries[CFFPS_DEBUGFS_NUM_ENTRIES];
73
74 char led_name[32];
75 u8 led_state;
76 struct led_classdev led;
77 };
78
79 #define to_psu(x, y) container_of((x), struct ibm_cffps, debugfs_entries[(y)])
80
81 static ssize_t ibm_cffps_read_input_history(struct ibm_cffps *psu,
82 char __user *buf, size_t count,
83 loff_t *ppos)
84 {
85 int rc;
86 u8 msgbuf0[1] = { CFFPS_INPUT_HISTORY_CMD };
87 u8 msgbuf1[CFFPS_INPUT_HISTORY_SIZE + 1] = { 0 };
88 struct i2c_msg msg[2] = {
89 {
90 .addr = psu->client->addr,
91 .flags = psu->client->flags,
92 .len = 1,
93 .buf = msgbuf0,
94 }, {
95 .addr = psu->client->addr,
96 .flags = psu->client->flags | I2C_M_RD,
97 .len = CFFPS_INPUT_HISTORY_SIZE + 1,
98 .buf = msgbuf1,
99 },
100 };
101
102 if (!*ppos) {
103 mutex_lock(&psu->input_history.update_lock);
104 if (time_after(jiffies, psu->input_history.last_update + HZ)) {
105 /*
106 * Use a raw i2c transfer, since we need more bytes
107 * than Linux I2C supports through smbus xfr (only 32).
108 */
109 rc = i2c_transfer(psu->client->adapter, msg, 2);
110 if (rc < 0) {
111 mutex_unlock(&psu->input_history.update_lock);
112 return rc;
113 }
114
115 psu->input_history.byte_count = msgbuf1[0];
116 memcpy(psu->input_history.data, &msgbuf1[1],
117 CFFPS_INPUT_HISTORY_SIZE);
118 psu->input_history.last_update = jiffies;
119 }
120
121 mutex_unlock(&psu->input_history.update_lock);
122 }
123
124 return simple_read_from_buffer(buf, count, ppos,
125 psu->input_history.data,
126 psu->input_history.byte_count);
127 }
128
129 static ssize_t ibm_cffps_debugfs_op(struct file *file, char __user *buf,
130 size_t count, loff_t *ppos)
131 {
132 u8 cmd;
133 int i, rc;
134 int *idxp = file->private_data;
135 int idx = *idxp;
136 struct ibm_cffps *psu = to_psu(idxp, idx);
137 char data[I2C_SMBUS_BLOCK_MAX] = { 0 };
138
139 switch (idx) {
140 case CFFPS_DEBUGFS_INPUT_HISTORY:
141 return ibm_cffps_read_input_history(psu, buf, count, ppos);
142 case CFFPS_DEBUGFS_FRU:
143 cmd = CFFPS_FRU_CMD;
144 break;
145 case CFFPS_DEBUGFS_PN:
146 cmd = CFFPS_PN_CMD;
147 break;
148 case CFFPS_DEBUGFS_SN:
149 cmd = CFFPS_SN_CMD;
150 break;
151 case CFFPS_DEBUGFS_CCIN:
152 rc = i2c_smbus_read_word_swapped(psu->client, CFFPS_CCIN_CMD);
153 if (rc < 0)
154 return rc;
155
156 rc = snprintf(data, 5, "%04X", rc);
157 goto done;
158 case CFFPS_DEBUGFS_FW:
159 for (i = 0; i < CFFPS_FW_NUM_BYTES; ++i) {
160 rc = i2c_smbus_read_byte_data(psu->client,
161 CFFPS_FW_CMD_START + i);
162 if (rc < 0)
163 return rc;
164
165 snprintf(&data[i * 2], 3, "%02X", rc);
166 }
167
168 rc = i * 2;
169 goto done;
170 default:
171 return -EINVAL;
172 }
173
174 rc = i2c_smbus_read_block_data(psu->client, cmd, data);
175 if (rc < 0)
176 return rc;
177
178 done:
179 data[rc] = '\n';
180 rc += 2;
181
182 return simple_read_from_buffer(buf, count, ppos, data, rc);
183 }
184
185 static const struct file_operations ibm_cffps_fops = {
186 .llseek = noop_llseek,
187 .read = ibm_cffps_debugfs_op,
188 .open = simple_open,
189 };
190
191 static int ibm_cffps_read_byte_data(struct i2c_client *client, int page,
192 int reg)
193 {
194 int rc, mfr;
195
196 switch (reg) {
197 case PMBUS_STATUS_VOUT:
198 case PMBUS_STATUS_IOUT:
199 case PMBUS_STATUS_TEMPERATURE:
200 case PMBUS_STATUS_FAN_12:
201 rc = pmbus_read_byte_data(client, page, reg);
202 if (rc < 0)
203 return rc;
204
205 mfr = pmbus_read_byte_data(client, page,
206 PMBUS_STATUS_MFR_SPECIFIC);
207 if (mfr < 0)
208 /*
209 * Return the status register instead of an error,
210 * since we successfully read status.
211 */
212 return rc;
213
214 /* Add MFR_SPECIFIC bits to the standard pmbus status regs. */
215 if (reg == PMBUS_STATUS_FAN_12) {
216 if (mfr & CFFPS_MFR_FAN_FAULT)
217 rc |= PB_FAN_FAN1_FAULT;
218 } else if (reg == PMBUS_STATUS_TEMPERATURE) {
219 if (mfr & CFFPS_MFR_THERMAL_FAULT)
220 rc |= PB_TEMP_OT_FAULT;
221 } else if (reg == PMBUS_STATUS_VOUT) {
222 if (mfr & (CFFPS_MFR_OV_FAULT | CFFPS_MFR_VAUX_FAULT))
223 rc |= PB_VOLTAGE_OV_FAULT;
224 if (mfr & CFFPS_MFR_UV_FAULT)
225 rc |= PB_VOLTAGE_UV_FAULT;
226 } else if (reg == PMBUS_STATUS_IOUT) {
227 if (mfr & CFFPS_MFR_OC_FAULT)
228 rc |= PB_IOUT_OC_FAULT;
229 if (mfr & CFFPS_MFR_CURRENT_SHARE_WARNING)
230 rc |= PB_CURRENT_SHARE_FAULT;
231 }
232 break;
233 default:
234 rc = -ENODATA;
235 break;
236 }
237
238 return rc;
239 }
240
241 static int ibm_cffps_read_word_data(struct i2c_client *client, int page,
242 int reg)
243 {
244 int rc, mfr;
245
246 switch (reg) {
247 case PMBUS_STATUS_WORD:
248 rc = pmbus_read_word_data(client, page, reg);
249 if (rc < 0)
250 return rc;
251
252 mfr = pmbus_read_byte_data(client, page,
253 PMBUS_STATUS_MFR_SPECIFIC);
254 if (mfr < 0)
255 /*
256 * Return the status register instead of an error,
257 * since we successfully read status.
258 */
259 return rc;
260
261 if (mfr & CFFPS_MFR_PS_KILL)
262 rc |= PB_STATUS_OFF;
263 break;
264 default:
265 rc = -ENODATA;
266 break;
267 }
268
269 return rc;
270 }
271
272 static void ibm_cffps_led_brightness_set(struct led_classdev *led_cdev,
273 enum led_brightness brightness)
274 {
275 int rc;
276 struct ibm_cffps *psu = container_of(led_cdev, struct ibm_cffps, led);
277
278 if (brightness == LED_OFF) {
279 psu->led_state = CFFPS_LED_OFF;
280 } else {
281 brightness = LED_FULL;
282 if (psu->led_state != CFFPS_LED_BLINK)
283 psu->led_state = CFFPS_LED_ON;
284 }
285
286 rc = i2c_smbus_write_byte_data(psu->client, CFFPS_SYS_CONFIG_CMD,
287 psu->led_state);
288 if (rc < 0)
289 return;
290
291 led_cdev->brightness = brightness;
292 }
293
294 static int ibm_cffps_led_blink_set(struct led_classdev *led_cdev,
295 unsigned long *delay_on,
296 unsigned long *delay_off)
297 {
298 int rc;
299 struct ibm_cffps *psu = container_of(led_cdev, struct ibm_cffps, led);
300
301 psu->led_state = CFFPS_LED_BLINK;
302
303 if (led_cdev->brightness == LED_OFF)
304 return 0;
305
306 rc = i2c_smbus_write_byte_data(psu->client, CFFPS_SYS_CONFIG_CMD,
307 CFFPS_LED_BLINK);
308 if (rc < 0)
309 return rc;
310
311 *delay_on = CFFPS_BLINK_RATE_MS;
312 *delay_off = CFFPS_BLINK_RATE_MS;
313
314 return 0;
315 }
316
317 static void ibm_cffps_create_led_class(struct ibm_cffps *psu)
318 {
319 int rc;
320 struct i2c_client *client = psu->client;
321 struct device *dev = &client->dev;
322
323 snprintf(psu->led_name, sizeof(psu->led_name), "%s-%02x", client->name,
324 client->addr);
325 psu->led.name = psu->led_name;
326 psu->led.max_brightness = LED_FULL;
327 psu->led.brightness_set = ibm_cffps_led_brightness_set;
328 psu->led.blink_set = ibm_cffps_led_blink_set;
329
330 rc = devm_led_classdev_register(dev, &psu->led);
331 if (rc)
332 dev_warn(dev, "failed to register led class: %d\n", rc);
333 }
334
335 static struct pmbus_driver_info ibm_cffps_info = {
336 .pages = 1,
337 .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT |
338 PMBUS_HAVE_PIN | PMBUS_HAVE_FAN12 | PMBUS_HAVE_TEMP |
339 PMBUS_HAVE_TEMP2 | PMBUS_HAVE_TEMP3 | PMBUS_HAVE_STATUS_VOUT |
340 PMBUS_HAVE_STATUS_IOUT | PMBUS_HAVE_STATUS_INPUT |
341 PMBUS_HAVE_STATUS_TEMP | PMBUS_HAVE_STATUS_FAN12,
342 .read_byte_data = ibm_cffps_read_byte_data,
343 .read_word_data = ibm_cffps_read_word_data,
344 };
345
346 static struct pmbus_platform_data ibm_cffps_pdata = {
347 .flags = PMBUS_SKIP_STATUS_CHECK,
348 };
349
350 static int ibm_cffps_probe(struct i2c_client *client,
351 const struct i2c_device_id *id)
352 {
353 int i, rc;
354 struct dentry *debugfs;
355 struct dentry *ibm_cffps_dir;
356 struct ibm_cffps *psu;
357
358 client->dev.platform_data = &ibm_cffps_pdata;
359 rc = pmbus_do_probe(client, id, &ibm_cffps_info);
360 if (rc)
361 return rc;
362
363 /*
364 * Don't fail the probe if there isn't enough memory for leds and
365 * debugfs.
366 */
367 psu = devm_kzalloc(&client->dev, sizeof(*psu), GFP_KERNEL);
368 if (!psu)
369 return 0;
370
371 psu->client = client;
372 mutex_init(&psu->input_history.update_lock);
373 psu->input_history.last_update = jiffies - HZ;
374
375 ibm_cffps_create_led_class(psu);
376
377 /* Don't fail the probe if we can't create debugfs */
378 debugfs = pmbus_get_debugfs_dir(client);
379 if (!debugfs)
380 return 0;
381
382 ibm_cffps_dir = debugfs_create_dir(client->name, debugfs);
383 if (!ibm_cffps_dir)
384 return 0;
385
386 for (i = 0; i < CFFPS_DEBUGFS_NUM_ENTRIES; ++i)
387 psu->debugfs_entries[i] = i;
388
389 debugfs_create_file("input_history", 0444, ibm_cffps_dir,
390 &psu->debugfs_entries[CFFPS_DEBUGFS_INPUT_HISTORY],
391 &ibm_cffps_fops);
392 debugfs_create_file("fru", 0444, ibm_cffps_dir,
393 &psu->debugfs_entries[CFFPS_DEBUGFS_FRU],
394 &ibm_cffps_fops);
395 debugfs_create_file("part_number", 0444, ibm_cffps_dir,
396 &psu->debugfs_entries[CFFPS_DEBUGFS_PN],
397 &ibm_cffps_fops);
398 debugfs_create_file("serial_number", 0444, ibm_cffps_dir,
399 &psu->debugfs_entries[CFFPS_DEBUGFS_SN],
400 &ibm_cffps_fops);
401 debugfs_create_file("ccin", 0444, ibm_cffps_dir,
402 &psu->debugfs_entries[CFFPS_DEBUGFS_CCIN],
403 &ibm_cffps_fops);
404 debugfs_create_file("fw_version", 0444, ibm_cffps_dir,
405 &psu->debugfs_entries[CFFPS_DEBUGFS_FW],
406 &ibm_cffps_fops);
407
408 return 0;
409 }
410
411 static const struct i2c_device_id ibm_cffps_id[] = {
412 { "ibm_cffps1", 1 },
413 {}
414 };
415 MODULE_DEVICE_TABLE(i2c, ibm_cffps_id);
416
417 static const struct of_device_id ibm_cffps_of_match[] = {
418 { .compatible = "ibm,cffps1" },
419 {}
420 };
421 MODULE_DEVICE_TABLE(of, ibm_cffps_of_match);
422
423 static struct i2c_driver ibm_cffps_driver = {
424 .driver = {
425 .name = "ibm-cffps",
426 .of_match_table = ibm_cffps_of_match,
427 },
428 .probe = ibm_cffps_probe,
429 .remove = pmbus_do_remove,
430 .id_table = ibm_cffps_id,
431 };
432
433 module_i2c_driver(ibm_cffps_driver);
434
435 MODULE_AUTHOR("Eddie James");
436 MODULE_DESCRIPTION("PMBus driver for IBM Common Form Factor power supplies");
437 MODULE_LICENSE("GPL");