GNU Linux-libre 4.19.245-gnu1
[releases.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 int 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 rc;
290
291         led_cdev->brightness = brightness;
292
293         return 0;
294 }
295
296 static int ibm_cffps_led_blink_set(struct led_classdev *led_cdev,
297                                    unsigned long *delay_on,
298                                    unsigned long *delay_off)
299 {
300         int rc;
301         struct ibm_cffps *psu = container_of(led_cdev, struct ibm_cffps, led);
302
303         psu->led_state = CFFPS_LED_BLINK;
304
305         if (led_cdev->brightness == LED_OFF)
306                 return 0;
307
308         rc = i2c_smbus_write_byte_data(psu->client, CFFPS_SYS_CONFIG_CMD,
309                                        CFFPS_LED_BLINK);
310         if (rc < 0)
311                 return rc;
312
313         *delay_on = CFFPS_BLINK_RATE_MS;
314         *delay_off = CFFPS_BLINK_RATE_MS;
315
316         return 0;
317 }
318
319 static void ibm_cffps_create_led_class(struct ibm_cffps *psu)
320 {
321         int rc;
322         struct i2c_client *client = psu->client;
323         struct device *dev = &client->dev;
324
325         snprintf(psu->led_name, sizeof(psu->led_name), "%s-%02x", client->name,
326                  client->addr);
327         psu->led.name = psu->led_name;
328         psu->led.max_brightness = LED_FULL;
329         psu->led.brightness_set_blocking = ibm_cffps_led_brightness_set;
330         psu->led.blink_set = ibm_cffps_led_blink_set;
331
332         rc = devm_led_classdev_register(dev, &psu->led);
333         if (rc)
334                 dev_warn(dev, "failed to register led class: %d\n", rc);
335 }
336
337 static struct pmbus_driver_info ibm_cffps_info = {
338         .pages = 1,
339         .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT |
340                 PMBUS_HAVE_PIN | PMBUS_HAVE_FAN12 | PMBUS_HAVE_TEMP |
341                 PMBUS_HAVE_TEMP2 | PMBUS_HAVE_TEMP3 | PMBUS_HAVE_STATUS_VOUT |
342                 PMBUS_HAVE_STATUS_IOUT | PMBUS_HAVE_STATUS_INPUT |
343                 PMBUS_HAVE_STATUS_TEMP | PMBUS_HAVE_STATUS_FAN12,
344         .read_byte_data = ibm_cffps_read_byte_data,
345         .read_word_data = ibm_cffps_read_word_data,
346 };
347
348 static struct pmbus_platform_data ibm_cffps_pdata = {
349         .flags = PMBUS_SKIP_STATUS_CHECK,
350 };
351
352 static int ibm_cffps_probe(struct i2c_client *client,
353                            const struct i2c_device_id *id)
354 {
355         int i, rc;
356         struct dentry *debugfs;
357         struct dentry *ibm_cffps_dir;
358         struct ibm_cffps *psu;
359
360         client->dev.platform_data = &ibm_cffps_pdata;
361         rc = pmbus_do_probe(client, id, &ibm_cffps_info);
362         if (rc)
363                 return rc;
364
365         /*
366          * Don't fail the probe if there isn't enough memory for leds and
367          * debugfs.
368          */
369         psu = devm_kzalloc(&client->dev, sizeof(*psu), GFP_KERNEL);
370         if (!psu)
371                 return 0;
372
373         psu->client = client;
374         mutex_init(&psu->input_history.update_lock);
375         psu->input_history.last_update = jiffies - HZ;
376
377         ibm_cffps_create_led_class(psu);
378
379         /* Don't fail the probe if we can't create debugfs */
380         debugfs = pmbus_get_debugfs_dir(client);
381         if (!debugfs)
382                 return 0;
383
384         ibm_cffps_dir = debugfs_create_dir(client->name, debugfs);
385         if (!ibm_cffps_dir)
386                 return 0;
387
388         for (i = 0; i < CFFPS_DEBUGFS_NUM_ENTRIES; ++i)
389                 psu->debugfs_entries[i] = i;
390
391         debugfs_create_file("input_history", 0444, ibm_cffps_dir,
392                             &psu->debugfs_entries[CFFPS_DEBUGFS_INPUT_HISTORY],
393                             &ibm_cffps_fops);
394         debugfs_create_file("fru", 0444, ibm_cffps_dir,
395                             &psu->debugfs_entries[CFFPS_DEBUGFS_FRU],
396                             &ibm_cffps_fops);
397         debugfs_create_file("part_number", 0444, ibm_cffps_dir,
398                             &psu->debugfs_entries[CFFPS_DEBUGFS_PN],
399                             &ibm_cffps_fops);
400         debugfs_create_file("serial_number", 0444, ibm_cffps_dir,
401                             &psu->debugfs_entries[CFFPS_DEBUGFS_SN],
402                             &ibm_cffps_fops);
403         debugfs_create_file("ccin", 0444, ibm_cffps_dir,
404                             &psu->debugfs_entries[CFFPS_DEBUGFS_CCIN],
405                             &ibm_cffps_fops);
406         debugfs_create_file("fw_version", 0444, ibm_cffps_dir,
407                             &psu->debugfs_entries[CFFPS_DEBUGFS_FW],
408                             &ibm_cffps_fops);
409
410         return 0;
411 }
412
413 static const struct i2c_device_id ibm_cffps_id[] = {
414         { "ibm_cffps1", 1 },
415         {}
416 };
417 MODULE_DEVICE_TABLE(i2c, ibm_cffps_id);
418
419 static const struct of_device_id ibm_cffps_of_match[] = {
420         { .compatible = "ibm,cffps1" },
421         {}
422 };
423 MODULE_DEVICE_TABLE(of, ibm_cffps_of_match);
424
425 static struct i2c_driver ibm_cffps_driver = {
426         .driver = {
427                 .name = "ibm-cffps",
428                 .of_match_table = ibm_cffps_of_match,
429         },
430         .probe = ibm_cffps_probe,
431         .remove = pmbus_do_remove,
432         .id_table = ibm_cffps_id,
433 };
434
435 module_i2c_driver(ibm_cffps_driver);
436
437 MODULE_AUTHOR("Eddie James");
438 MODULE_DESCRIPTION("PMBus driver for IBM Common Form Factor power supplies");
439 MODULE_LICENSE("GPL");