diff options
author | Christopher Bostic <cbostic@linux.vnet.ibm.com> | 2018-03-16 17:22:06 -0400 |
---|---|---|
committer | Guenter Roeck <linux@roeck-us.net> | 2018-03-16 21:52:35 -0400 |
commit | 72816cb06e7153c22aed8776949f558b635aa2c0 (patch) | |
tree | b4d51800cfe7f87be785036f3e5450f06232ea38 | |
parent | ca781fb7fd95ba90eda6936a27fc1e3202f99d6a (diff) |
hwmon: (ucd9000) Add debugfs attributes to provide mfr_status
Expose the gpiN_fault fields of mfr_status as individual debugfs
attributes. This provides a way for users to be easily notified of gpi
faults. Also provide the whole mfr_status register in debugfs.
Signed-off-by: Christopher Bostic <cbostic@linux.vnet.ibm.com>
Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
Signed-off-by: Eddie James <eajames@linux.vnet.ibm.com>
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
-rw-r--r-- | drivers/hwmon/pmbus/ucd9000.c | 138 |
1 files changed, 137 insertions, 1 deletions
diff --git a/drivers/hwmon/pmbus/ucd9000.c b/drivers/hwmon/pmbus/ucd9000.c index ef2c5bfc9cf8..70cecb06f93c 100644 --- a/drivers/hwmon/pmbus/ucd9000.c +++ b/drivers/hwmon/pmbus/ucd9000.c | |||
@@ -19,6 +19,7 @@ | |||
19 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | 19 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
20 | */ | 20 | */ |
21 | 21 | ||
22 | #include <linux/debugfs.h> | ||
22 | #include <linux/kernel.h> | 23 | #include <linux/kernel.h> |
23 | #include <linux/module.h> | 24 | #include <linux/module.h> |
24 | #include <linux/of_device.h> | 25 | #include <linux/of_device.h> |
@@ -37,6 +38,7 @@ enum chips { ucd9000, ucd90120, ucd90124, ucd90160, ucd9090, ucd90910 }; | |||
37 | #define UCD9000_NUM_PAGES 0xd6 | 38 | #define UCD9000_NUM_PAGES 0xd6 |
38 | #define UCD9000_FAN_CONFIG_INDEX 0xe7 | 39 | #define UCD9000_FAN_CONFIG_INDEX 0xe7 |
39 | #define UCD9000_FAN_CONFIG 0xe8 | 40 | #define UCD9000_FAN_CONFIG 0xe8 |
41 | #define UCD9000_MFR_STATUS 0xf3 | ||
40 | #define UCD9000_GPIO_SELECT 0xfa | 42 | #define UCD9000_GPIO_SELECT 0xfa |
41 | #define UCD9000_GPIO_CONFIG 0xfb | 43 | #define UCD9000_GPIO_CONFIG 0xfb |
42 | #define UCD9000_DEVICE_ID 0xfd | 44 | #define UCD9000_DEVICE_ID 0xfd |
@@ -64,15 +66,24 @@ enum chips { ucd9000, ucd90120, ucd90124, ucd90160, ucd9090, ucd90910 }; | |||
64 | #define UCD901XX_NUM_GPIOS 26 | 66 | #define UCD901XX_NUM_GPIOS 26 |
65 | #define UCD90910_NUM_GPIOS 26 | 67 | #define UCD90910_NUM_GPIOS 26 |
66 | 68 | ||
69 | #define UCD9000_DEBUGFS_NAME_LEN 24 | ||
70 | #define UCD9000_GPI_COUNT 8 | ||
71 | |||
67 | struct ucd9000_data { | 72 | struct ucd9000_data { |
68 | u8 fan_data[UCD9000_NUM_FAN][I2C_SMBUS_BLOCK_MAX]; | 73 | u8 fan_data[UCD9000_NUM_FAN][I2C_SMBUS_BLOCK_MAX]; |
69 | struct pmbus_driver_info info; | 74 | struct pmbus_driver_info info; |
70 | #ifdef CONFIG_GPIOLIB | 75 | #ifdef CONFIG_GPIOLIB |
71 | struct gpio_chip gpio; | 76 | struct gpio_chip gpio; |
72 | #endif | 77 | #endif |
78 | struct dentry *debugfs; | ||
73 | }; | 79 | }; |
74 | #define to_ucd9000_data(_info) container_of(_info, struct ucd9000_data, info) | 80 | #define to_ucd9000_data(_info) container_of(_info, struct ucd9000_data, info) |
75 | 81 | ||
82 | struct ucd9000_debugfs_entry { | ||
83 | struct i2c_client *client; | ||
84 | u8 index; | ||
85 | }; | ||
86 | |||
76 | static int ucd9000_get_fan_config(struct i2c_client *client, int fan) | 87 | static int ucd9000_get_fan_config(struct i2c_client *client, int fan) |
77 | { | 88 | { |
78 | int fan_config = 0; | 89 | int fan_config = 0; |
@@ -359,6 +370,122 @@ static void ucd9000_probe_gpio(struct i2c_client *client, | |||
359 | } | 370 | } |
360 | #endif /* CONFIG_GPIOLIB */ | 371 | #endif /* CONFIG_GPIOLIB */ |
361 | 372 | ||
373 | #ifdef CONFIG_DEBUG_FS | ||
374 | static int ucd9000_get_mfr_status(struct i2c_client *client, u8 *buffer) | ||
375 | { | ||
376 | int ret = pmbus_set_page(client, 0); | ||
377 | |||
378 | if (ret < 0) | ||
379 | return ret; | ||
380 | |||
381 | return i2c_smbus_read_block_data(client, UCD9000_MFR_STATUS, buffer); | ||
382 | } | ||
383 | |||
384 | static int ucd9000_debugfs_show_mfr_status_bit(void *data, u64 *val) | ||
385 | { | ||
386 | struct ucd9000_debugfs_entry *entry = data; | ||
387 | struct i2c_client *client = entry->client; | ||
388 | u8 buffer[I2C_SMBUS_BLOCK_MAX]; | ||
389 | int ret; | ||
390 | |||
391 | ret = ucd9000_get_mfr_status(client, buffer); | ||
392 | if (ret < 0) | ||
393 | return ret; | ||
394 | |||
395 | /* | ||
396 | * Attribute only created for devices with gpi fault bits at bits | ||
397 | * 16-23, which is the second byte of the response. | ||
398 | */ | ||
399 | *val = !!(buffer[1] & BIT(entry->index)); | ||
400 | |||
401 | return 0; | ||
402 | } | ||
403 | DEFINE_DEBUGFS_ATTRIBUTE(ucd9000_debugfs_mfr_status_bit, | ||
404 | ucd9000_debugfs_show_mfr_status_bit, NULL, "%1lld\n"); | ||
405 | |||
406 | static ssize_t ucd9000_debugfs_read_mfr_status(struct file *file, | ||
407 | char __user *buf, size_t count, | ||
408 | loff_t *ppos) | ||
409 | { | ||
410 | struct i2c_client *client = file->private_data; | ||
411 | u8 buffer[I2C_SMBUS_BLOCK_MAX]; | ||
412 | char str[(I2C_SMBUS_BLOCK_MAX * 2) + 2]; | ||
413 | char *res; | ||
414 | int rc; | ||
415 | |||
416 | rc = ucd9000_get_mfr_status(client, buffer); | ||
417 | if (rc < 0) | ||
418 | return rc; | ||
419 | |||
420 | res = bin2hex(str, buffer, min(rc, I2C_SMBUS_BLOCK_MAX)); | ||
421 | *res++ = '\n'; | ||
422 | *res = 0; | ||
423 | |||
424 | return simple_read_from_buffer(buf, count, ppos, str, res - str); | ||
425 | } | ||
426 | |||
427 | static const struct file_operations ucd9000_debugfs_show_mfr_status_fops = { | ||
428 | .llseek = noop_llseek, | ||
429 | .read = ucd9000_debugfs_read_mfr_status, | ||
430 | .open = simple_open, | ||
431 | }; | ||
432 | |||
433 | static int ucd9000_init_debugfs(struct i2c_client *client, | ||
434 | const struct i2c_device_id *mid, | ||
435 | struct ucd9000_data *data) | ||
436 | { | ||
437 | struct dentry *debugfs; | ||
438 | struct ucd9000_debugfs_entry *entries; | ||
439 | int i; | ||
440 | char name[UCD9000_DEBUGFS_NAME_LEN]; | ||
441 | |||
442 | debugfs = pmbus_get_debugfs_dir(client); | ||
443 | if (!debugfs) | ||
444 | return -ENOENT; | ||
445 | |||
446 | data->debugfs = debugfs_create_dir(client->name, debugfs); | ||
447 | if (!data->debugfs) | ||
448 | return -ENOENT; | ||
449 | |||
450 | /* | ||
451 | * Of the chips this driver supports, only the UCD9090, UCD90160, | ||
452 | * and UCD90910 report GPI faults in their MFR_STATUS register, so only | ||
453 | * create the GPI fault debugfs attributes for those chips. | ||
454 | */ | ||
455 | if (mid->driver_data == ucd9090 || mid->driver_data == ucd90160 || | ||
456 | mid->driver_data == ucd90910) { | ||
457 | entries = devm_kzalloc(&client->dev, | ||
458 | sizeof(*entries) * UCD9000_GPI_COUNT, | ||
459 | GFP_KERNEL); | ||
460 | if (!entries) | ||
461 | return -ENOMEM; | ||
462 | |||
463 | for (i = 0; i < UCD9000_GPI_COUNT; i++) { | ||
464 | entries[i].client = client; | ||
465 | entries[i].index = i; | ||
466 | scnprintf(name, UCD9000_DEBUGFS_NAME_LEN, | ||
467 | "gpi%d_alarm", i + 1); | ||
468 | debugfs_create_file(name, 0444, data->debugfs, | ||
469 | &entries[i], | ||
470 | &ucd9000_debugfs_mfr_status_bit); | ||
471 | } | ||
472 | } | ||
473 | |||
474 | scnprintf(name, UCD9000_DEBUGFS_NAME_LEN, "mfr_status"); | ||
475 | debugfs_create_file(name, 0444, data->debugfs, client, | ||
476 | &ucd9000_debugfs_show_mfr_status_fops); | ||
477 | |||
478 | return 0; | ||
479 | } | ||
480 | #else | ||
481 | static int ucd9000_init_debugfs(struct i2c_client *client, | ||
482 | const struct i2c_device_id *mid, | ||
483 | struct ucd9000_data *data) | ||
484 | { | ||
485 | return 0; | ||
486 | } | ||
487 | #endif /* CONFIG_DEBUG_FS */ | ||
488 | |||
362 | static int ucd9000_probe(struct i2c_client *client, | 489 | static int ucd9000_probe(struct i2c_client *client, |
363 | const struct i2c_device_id *id) | 490 | const struct i2c_device_id *id) |
364 | { | 491 | { |
@@ -475,7 +602,16 @@ static int ucd9000_probe(struct i2c_client *client, | |||
475 | 602 | ||
476 | ucd9000_probe_gpio(client, mid, data); | 603 | ucd9000_probe_gpio(client, mid, data); |
477 | 604 | ||
478 | return pmbus_do_probe(client, mid, info); | 605 | ret = pmbus_do_probe(client, mid, info); |
606 | if (ret) | ||
607 | return ret; | ||
608 | |||
609 | ret = ucd9000_init_debugfs(client, mid, data); | ||
610 | if (ret) | ||
611 | dev_warn(&client->dev, "Failed to register debugfs: %d\n", | ||
612 | ret); | ||
613 | |||
614 | return 0; | ||
479 | } | 615 | } |
480 | 616 | ||
481 | /* This is the driver that will be inserted */ | 617 | /* This is the driver that will be inserted */ |