• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
ibm_cffps_read_input_history(struct ibm_cffps * psu,char __user * buf,size_t count,loff_t * ppos)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 
ibm_cffps_debugfs_op(struct file * file,char __user * buf,size_t count,loff_t * ppos)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 
ibm_cffps_read_byte_data(struct i2c_client * client,int page,int reg)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 
ibm_cffps_read_word_data(struct i2c_client * client,int page,int reg)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 
ibm_cffps_led_brightness_set(struct led_classdev * led_cdev,enum led_brightness brightness)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 
ibm_cffps_led_blink_set(struct led_classdev * led_cdev,unsigned long * delay_on,unsigned long * delay_off)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 
ibm_cffps_create_led_class(struct ibm_cffps * psu)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 
ibm_cffps_probe(struct i2c_client * client,const struct i2c_device_id * id)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");
440