aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEdward A. James <eajames@us.ibm.com>2017-12-11 16:32:50 -0500
committerGuenter Roeck <linux@roeck-us.net>2018-01-02 18:05:34 -0500
commitd6bb645a1704cba3884bf03d5a8bd86915b5e650 (patch)
tree80e254a0e9f65c3a6b4312461b1a70cc29beed01
parenteb6489b696ad22a8464e20502e18014434b4b0ea (diff)
hwmon: (pmbus) cffps: Add debugfs entries
Add debugfs entries for additional power supply data, including part number, serial number, FRU number, firmware revision, ccin, and the input history of the power supply. The input history is 10 minutes of input power data in the form of twenty 30-second packets. Each packet contains average and maximum power for that 30 second period. Signed-off-by: Edward A. James <eajames@us.ibm.com> [groeck: Fixed endianness problem] Signed-off-by: Guenter Roeck <linux@roeck-us.net>
-rw-r--r--drivers/hwmon/pmbus/ibm-cffps.c202
1 files changed, 201 insertions, 1 deletions
diff --git a/drivers/hwmon/pmbus/ibm-cffps.c b/drivers/hwmon/pmbus/ibm-cffps.c
index cb56da6834e5..de2547476253 100644
--- a/drivers/hwmon/pmbus/ibm-cffps.c
+++ b/drivers/hwmon/pmbus/ibm-cffps.c
@@ -8,12 +8,26 @@
8 */ 8 */
9 9
10#include <linux/bitops.h> 10#include <linux/bitops.h>
11#include <linux/debugfs.h>
11#include <linux/device.h> 12#include <linux/device.h>
13#include <linux/fs.h>
12#include <linux/i2c.h> 14#include <linux/i2c.h>
15#include <linux/jiffies.h>
13#include <linux/module.h> 16#include <linux/module.h>
17#include <linux/mutex.h>
14 18
15#include "pmbus.h" 19#include "pmbus.h"
16 20
21#define CFFPS_FRU_CMD 0x9A
22#define CFFPS_PN_CMD 0x9B
23#define CFFPS_SN_CMD 0x9E
24#define CFFPS_CCIN_CMD 0xBD
25#define CFFPS_FW_CMD_START 0xFA
26#define CFFPS_FW_NUM_BYTES 4
27
28#define CFFPS_INPUT_HISTORY_CMD 0xD6
29#define CFFPS_INPUT_HISTORY_SIZE 100
30
17/* STATUS_MFR_SPECIFIC bits */ 31/* STATUS_MFR_SPECIFIC bits */
18#define CFFPS_MFR_FAN_FAULT BIT(0) 32#define CFFPS_MFR_FAN_FAULT BIT(0)
19#define CFFPS_MFR_THERMAL_FAULT BIT(1) 33#define CFFPS_MFR_THERMAL_FAULT BIT(1)
@@ -24,6 +38,144 @@
24#define CFFPS_MFR_VAUX_FAULT BIT(6) 38#define CFFPS_MFR_VAUX_FAULT BIT(6)
25#define CFFPS_MFR_CURRENT_SHARE_WARNING BIT(7) 39#define CFFPS_MFR_CURRENT_SHARE_WARNING BIT(7)
26 40
41enum {
42 CFFPS_DEBUGFS_INPUT_HISTORY = 0,
43 CFFPS_DEBUGFS_FRU,
44 CFFPS_DEBUGFS_PN,
45 CFFPS_DEBUGFS_SN,
46 CFFPS_DEBUGFS_CCIN,
47 CFFPS_DEBUGFS_FW,
48 CFFPS_DEBUGFS_NUM_ENTRIES
49};
50
51struct ibm_cffps_input_history {
52 struct mutex update_lock;
53 unsigned long last_update;
54
55 u8 byte_count;
56 u8 data[CFFPS_INPUT_HISTORY_SIZE];
57};
58
59struct ibm_cffps {
60 struct i2c_client *client;
61
62 struct ibm_cffps_input_history input_history;
63
64 int debugfs_entries[CFFPS_DEBUGFS_NUM_ENTRIES];
65};
66
67#define to_psu(x, y) container_of((x), struct ibm_cffps, debugfs_entries[(y)])
68
69static ssize_t ibm_cffps_read_input_history(struct ibm_cffps *psu,
70 char __user *buf, size_t count,
71 loff_t *ppos)
72{
73 int rc;
74 u8 msgbuf0[1] = { CFFPS_INPUT_HISTORY_CMD };
75 u8 msgbuf1[CFFPS_INPUT_HISTORY_SIZE + 1] = { 0 };
76 struct i2c_msg msg[2] = {
77 {
78 .addr = psu->client->addr,
79 .flags = psu->client->flags,
80 .len = 1,
81 .buf = msgbuf0,
82 }, {
83 .addr = psu->client->addr,
84 .flags = psu->client->flags | I2C_M_RD,
85 .len = CFFPS_INPUT_HISTORY_SIZE + 1,
86 .buf = msgbuf1,
87 },
88 };
89
90 if (!*ppos) {
91 mutex_lock(&psu->input_history.update_lock);
92 if (time_after(jiffies, psu->input_history.last_update + HZ)) {
93 /*
94 * Use a raw i2c transfer, since we need more bytes
95 * than Linux I2C supports through smbus xfr (only 32).
96 */
97 rc = i2c_transfer(psu->client->adapter, msg, 2);
98 if (rc < 0) {
99 mutex_unlock(&psu->input_history.update_lock);
100 return rc;
101 }
102
103 psu->input_history.byte_count = msgbuf1[0];
104 memcpy(psu->input_history.data, &msgbuf1[1],
105 CFFPS_INPUT_HISTORY_SIZE);
106 psu->input_history.last_update = jiffies;
107 }
108
109 mutex_unlock(&psu->input_history.update_lock);
110 }
111
112 return simple_read_from_buffer(buf, count, ppos,
113 psu->input_history.data,
114 psu->input_history.byte_count);
115}
116
117static ssize_t ibm_cffps_debugfs_op(struct file *file, char __user *buf,
118 size_t count, loff_t *ppos)
119{
120 u8 cmd;
121 int i, rc;
122 int *idxp = file->private_data;
123 int idx = *idxp;
124 struct ibm_cffps *psu = to_psu(idxp, idx);
125 char data[I2C_SMBUS_BLOCK_MAX] = { 0 };
126
127 switch (idx) {
128 case CFFPS_DEBUGFS_INPUT_HISTORY:
129 return ibm_cffps_read_input_history(psu, buf, count, ppos);
130 case CFFPS_DEBUGFS_FRU:
131 cmd = CFFPS_FRU_CMD;
132 break;
133 case CFFPS_DEBUGFS_PN:
134 cmd = CFFPS_PN_CMD;
135 break;
136 case CFFPS_DEBUGFS_SN:
137 cmd = CFFPS_SN_CMD;
138 break;
139 case CFFPS_DEBUGFS_CCIN:
140 rc = i2c_smbus_read_word_swapped(psu->client, CFFPS_CCIN_CMD);
141 if (rc < 0)
142 return rc;
143
144 rc = snprintf(data, 5, "%04X", rc);
145 goto done;
146 case CFFPS_DEBUGFS_FW:
147 for (i = 0; i < CFFPS_FW_NUM_BYTES; ++i) {
148 rc = i2c_smbus_read_byte_data(psu->client,
149 CFFPS_FW_CMD_START + i);
150 if (rc < 0)
151 return rc;
152
153 snprintf(&data[i * 2], 3, "%02X", rc);
154 }
155
156 rc = i * 2;
157 goto done;
158 default:
159 return -EINVAL;
160 }
161
162 rc = i2c_smbus_read_block_data(psu->client, cmd, data);
163 if (rc < 0)
164 return rc;
165
166done:
167 data[rc] = '\n';
168 rc += 2;
169
170 return simple_read_from_buffer(buf, count, ppos, data, rc);
171}
172
173static const struct file_operations ibm_cffps_fops = {
174 .llseek = noop_llseek,
175 .read = ibm_cffps_debugfs_op,
176 .open = simple_open,
177};
178
27static int ibm_cffps_read_byte_data(struct i2c_client *client, int page, 179static int ibm_cffps_read_byte_data(struct i2c_client *client, int page,
28 int reg) 180 int reg)
29{ 181{
@@ -119,7 +271,55 @@ static struct pmbus_driver_info ibm_cffps_info = {
119static int ibm_cffps_probe(struct i2c_client *client, 271static int ibm_cffps_probe(struct i2c_client *client,
120 const struct i2c_device_id *id) 272 const struct i2c_device_id *id)
121{ 273{
122 return pmbus_do_probe(client, id, &ibm_cffps_info); 274 int i, rc;
275 struct dentry *debugfs;
276 struct dentry *ibm_cffps_dir;
277 struct ibm_cffps *psu;
278
279 rc = pmbus_do_probe(client, id, &ibm_cffps_info);
280 if (rc)
281 return rc;
282
283 /* Don't fail the probe if we can't create debugfs */
284 debugfs = pmbus_get_debugfs_dir(client);
285 if (!debugfs)
286 return 0;
287
288 ibm_cffps_dir = debugfs_create_dir(client->name, debugfs);
289 if (!ibm_cffps_dir)
290 return 0;
291
292 psu = devm_kzalloc(&client->dev, sizeof(*psu), GFP_KERNEL);
293 if (!psu)
294 return 0;
295
296 psu->client = client;
297 mutex_init(&psu->input_history.update_lock);
298 psu->input_history.last_update = jiffies - HZ;
299
300 for (i = 0; i < CFFPS_DEBUGFS_NUM_ENTRIES; ++i)
301 psu->debugfs_entries[i] = i;
302
303 debugfs_create_file("input_history", 0444, ibm_cffps_dir,
304 &psu->debugfs_entries[CFFPS_DEBUGFS_INPUT_HISTORY],
305 &ibm_cffps_fops);
306 debugfs_create_file("fru", 0444, ibm_cffps_dir,
307 &psu->debugfs_entries[CFFPS_DEBUGFS_FRU],
308 &ibm_cffps_fops);
309 debugfs_create_file("part_number", 0444, ibm_cffps_dir,
310 &psu->debugfs_entries[CFFPS_DEBUGFS_PN],
311 &ibm_cffps_fops);
312 debugfs_create_file("serial_number", 0444, ibm_cffps_dir,
313 &psu->debugfs_entries[CFFPS_DEBUGFS_SN],
314 &ibm_cffps_fops);
315 debugfs_create_file("ccin", 0444, ibm_cffps_dir,
316 &psu->debugfs_entries[CFFPS_DEBUGFS_CCIN],
317 &ibm_cffps_fops);
318 debugfs_create_file("fw_version", 0444, ibm_cffps_dir,
319 &psu->debugfs_entries[CFFPS_DEBUGFS_FW],
320 &ibm_cffps_fops);
321
322 return 0;
123} 323}
124 324
125static const struct i2c_device_id ibm_cffps_id[] = { 325static const struct i2c_device_id ibm_cffps_id[] = {