xref: /openbmc/linux/drivers/hwmon/pmbus/ibm-cffps.c (revision f1575595)
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");
438