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
49enum {
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
59struct 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
67struct 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
81static 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
129static 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
178done:
179 data[rc] = '\n';
180 rc += 2;
181
182 return simple_read_from_buffer(buf, count, ppos, data, rc);
183}
184
185static const struct file_operations ibm_cffps_fops = {
186 .llseek = noop_llseek,
187 .read = ibm_cffps_debugfs_op,
188 .open = simple_open,
189};
190
191static 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
241static 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
272static 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
294static 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
317static 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
335static 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
346static struct pmbus_platform_data ibm_cffps_pdata = {
347 .flags = PMBUS_SKIP_STATUS_CHECK,
348};
349
350static 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
411static const struct i2c_device_id ibm_cffps_id[] = {
412 { "ibm_cffps1", 1 },
413 {}
414};
415MODULE_DEVICE_TABLE(i2c, ibm_cffps_id);
416
417static const struct of_device_id ibm_cffps_of_match[] = {
418 { .compatible = "ibm,cffps1" },
419 {}
420};
421MODULE_DEVICE_TABLE(of, ibm_cffps_of_match);
422
423static 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
433module_i2c_driver(ibm_cffps_driver);
434
435MODULE_AUTHOR("Eddie James");
436MODULE_DESCRIPTION("PMBus driver for IBM Common Form Factor power supplies");
437MODULE_LICENSE("GPL");
438