greybus: svc: add AP power measurements debugfs support
authorDavid Lin <dtwlin@google.com>
Wed, 20 Apr 2016 23:55:08 +0000 (16:55 -0700)
committerGreg Kroah-Hartman <gregkh@google.com>
Thu, 21 Apr 2016 01:50:56 +0000 (10:50 +0900)
This change adds the AP Power Monitor functions to
read out all the rails power information monitored by
the SVC.

Testing Done:
- $ cat /d/greybus/1-svc/pwrmon/*/*
  and validate the output with the svc stub power
  monitor functions
- $ tree /d/greybus/1-svc/pwrmon
  | | | |---pwrmon
  | | | | |---DUMMY_RAIL_1
  | | | | | |---current_now
  | | | | | |---power_now
  | | | | | |---voltage_now
  | | | | |---DUMMY_RAIL_2
  | | | | | |---current_now
  | | | | | |---power_now
  | | | | | |---voltage_now
  | | | | |---DUMMY_RAIL_3
  | | | | | |---current_now
  | | | | | |---power_now
  | | | | | |---voltage_now
  | | | | |---DUMMY_RAIL_4
  | | | | | |---current_now
  | | | | | |---power_now
  | | | | | |---voltage_now

Signed-off-by: David Lin <dtwlin@google.com>
Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@google.com>
drivers/staging/greybus/greybus_protocols.h
drivers/staging/greybus/svc.c
drivers/staging/greybus/svc.h

index e0c0493605ca11fcd7fd42f8a4c8a152ddba9113..82798cf3ea2b100673d7cba415f280a941d2ebcd 100644 (file)
@@ -967,6 +967,18 @@ struct gb_svc_key_event_request {
 #define GB_SVC_KEY_PRESSED     0x01
 } __packed;
 
+#define GB_SVC_PWRMON_MAX_RAIL_COUNT           254
+
+struct gb_svc_pwrmon_rail_count_get_response {
+       __u8    rail_count;
+} __packed;
+
+#define GB_SVC_PWRMON_RAIL_NAME_BUFSIZE                32
+
+struct gb_svc_pwrmon_rail_names_get_response {
+       __u8    name[0][GB_SVC_PWRMON_RAIL_NAME_BUFSIZE];
+} __packed;
+
 #define GB_SVC_PWRMON_TYPE_CURR                        0x01
 #define GB_SVC_PWRMON_TYPE_VOL                 0x02
 #define GB_SVC_PWRMON_TYPE_PWR                 0x03
@@ -976,6 +988,16 @@ struct gb_svc_key_event_request {
 #define GB_SVC_PWRMON_GET_SAMPLE_NOSUPP                0x02
 #define GB_SVC_PWRMON_GET_SAMPLE_HWERR         0x03
 
+struct gb_svc_pwrmon_sample_get_request {
+       __u8    rail_id;
+       __u8    measurement_type;
+} __packed;
+
+struct gb_svc_pwrmon_sample_get_response {
+       __u8    result;
+       __le32  measurement;
+} __packed;
+
 struct gb_svc_pwrmon_intf_sample_get_request {
        __u8    intf_id;
        __u8    measurement_type;
index 516a452ab213e7d9a2da63b087ab884b5265faa7..1791dcce52174bd832e7de251a1e576c4f18a495 100644 (file)
@@ -7,6 +7,7 @@
  * Released under the GPLv2 only.
  */
 
+#include <linux/debugfs.h>
 #include <linux/input.h>
 #include <linux/workqueue.h>
 
@@ -99,6 +100,78 @@ static ssize_t watchdog_store(struct device *dev,
 }
 static DEVICE_ATTR_RW(watchdog);
 
+static int gb_svc_pwrmon_rail_count_get(struct gb_svc *svc, u8 *value)
+{
+       struct gb_svc_pwrmon_rail_count_get_response response;
+       int ret;
+
+       ret = gb_operation_sync(svc->connection,
+                               GB_SVC_TYPE_PWRMON_RAIL_COUNT_GET, NULL, 0,
+                               &response, sizeof(response));
+       if (ret) {
+               dev_err(&svc->dev, "failed to get rail count (%d)\n", ret);
+               return ret;
+       }
+
+       *value = response.rail_count;
+
+       return 0;
+}
+
+static int gb_svc_pwrmon_rail_names_get(struct gb_svc *svc,
+                                       struct gb_svc_pwrmon_rail_names_get_response *response,
+                                       size_t bufsize)
+{
+       int ret;
+
+       ret = gb_operation_sync(svc->connection,
+                               GB_SVC_TYPE_PWRMON_RAIL_NAMES_GET, NULL, 0,
+                               response, bufsize);
+       if (ret) {
+               dev_err(&svc->dev, "failed to get rail names (%d)\n", ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+static int gb_svc_pwrmon_sample_get(struct gb_svc *svc, u8 rail_id,
+                                   u8 measurement_type, u32 *value)
+{
+       struct gb_svc_pwrmon_sample_get_request request;
+       struct gb_svc_pwrmon_sample_get_response response;
+       int ret;
+
+       request.rail_id = rail_id;
+       request.measurement_type = measurement_type;
+
+       ret = gb_operation_sync(svc->connection, GB_SVC_TYPE_PWRMON_SAMPLE_GET,
+                               &request, sizeof(request),
+                               &response, sizeof(response));
+       if (ret) {
+               dev_err(&svc->dev, "failed to get rail sample (%d)\n", ret);
+               return ret;
+       }
+
+       if (response.result) {
+               dev_err(&svc->dev,
+                       "UniPro error while getting rail power sample (%d %d): %d\n",
+                       rail_id, measurement_type, response.result);
+               switch (response.result) {
+               case GB_SVC_PWRMON_GET_SAMPLE_INVAL:
+                       return -EINVAL;
+               case GB_SVC_PWRMON_GET_SAMPLE_NOSUPP:
+                       return -ENOSYS;
+               default:
+                       return -EIO;
+               }
+       }
+
+       *value = le32_to_cpu(response.measurement);
+
+       return 0;
+}
+
 int gb_svc_pwrmon_intf_sample_get(struct gb_svc *svc, u8 intf_id,
                                  u8 measurement_type, u32 *value)
 {
@@ -393,6 +466,161 @@ static int gb_svc_version_request(struct gb_operation *op)
        return 0;
 }
 
+static ssize_t pwr_debugfs_voltage_read(struct file *file, char __user *buf,
+                                       size_t len, loff_t *offset)
+{
+       struct svc_debugfs_pwrmon_rail *pwrmon_rails = file->f_inode->i_private;
+       struct gb_svc *svc = pwrmon_rails->svc;
+       int ret, desc;
+       u32 value;
+       char buff[16];
+
+       ret = gb_svc_pwrmon_sample_get(svc, pwrmon_rails->id,
+                                      GB_SVC_PWRMON_TYPE_VOL, &value);
+       if (ret) {
+               dev_err(&svc->dev,
+                       "failed to get voltage sample ret=%d id=%d\n",
+                       ret, pwrmon_rails->id);
+               return ret;
+       }
+
+       desc = scnprintf(buff, sizeof(buff), "%u\n", value);
+
+       return simple_read_from_buffer(buf, len, offset, buff, desc);
+}
+
+static ssize_t pwr_debugfs_current_read(struct file *file, char __user *buf,
+                                       size_t len, loff_t *offset)
+{
+       struct svc_debugfs_pwrmon_rail *pwrmon_rails = file->f_inode->i_private;
+       struct gb_svc *svc = pwrmon_rails->svc;
+       int ret, desc;
+       u32 value;
+       char buff[16];
+
+       ret = gb_svc_pwrmon_sample_get(svc, pwrmon_rails->id,
+                                      GB_SVC_PWRMON_TYPE_CURR, &value);
+       if (ret) {
+               dev_err(&svc->dev,
+                       "failed to get current sample ret=%d id=%d\n",
+                       ret, pwrmon_rails->id);
+               return ret;
+       }
+
+       desc = scnprintf(buff, sizeof(buff), "%u\n", value);
+
+       return simple_read_from_buffer(buf, len, offset, buff, desc);
+}
+
+static ssize_t pwr_debugfs_power_read(struct file *file, char __user *buf,
+                                     size_t len, loff_t *offset)
+{
+       struct svc_debugfs_pwrmon_rail *pwrmon_rails = file->f_inode->i_private;
+       struct gb_svc *svc = pwrmon_rails->svc;
+       int ret, desc;
+       u32 value;
+       char buff[16];
+
+       ret = gb_svc_pwrmon_sample_get(svc, pwrmon_rails->id,
+                                      GB_SVC_PWRMON_TYPE_PWR, &value);
+       if (ret) {
+               dev_err(&svc->dev, "failed to get power sample ret=%d id=%d\n",
+                       ret, pwrmon_rails->id);
+               return ret;
+       }
+
+       desc = scnprintf(buff, sizeof(buff), "%u\n", value);
+
+       return simple_read_from_buffer(buf, len, offset, buff, desc);
+}
+
+static const struct file_operations pwrmon_debugfs_voltage_fops = {
+       .read           = pwr_debugfs_voltage_read,
+};
+
+static const struct file_operations pwrmon_debugfs_current_fops = {
+       .read           = pwr_debugfs_current_read,
+};
+
+static const struct file_operations pwrmon_debugfs_power_fops = {
+       .read           = pwr_debugfs_power_read,
+};
+
+static void svc_pwrmon_debugfs_init(struct gb_svc *svc)
+{
+       int i;
+       size_t bufsize;
+       struct dentry *dent;
+
+       dent = debugfs_create_dir("pwrmon", svc->debugfs_dentry);
+       if (IS_ERR_OR_NULL(dent))
+               return;
+
+       if (gb_svc_pwrmon_rail_count_get(svc, &svc->rail_count))
+               goto err_pwrmon_debugfs;
+
+       if (!svc->rail_count || svc->rail_count > GB_SVC_PWRMON_MAX_RAIL_COUNT)
+               goto err_pwrmon_debugfs;
+
+       bufsize = GB_SVC_PWRMON_RAIL_NAME_BUFSIZE * svc->rail_count;
+
+       svc->rail_names = kzalloc(bufsize, GFP_KERNEL);
+       if (!svc->rail_names)
+               goto err_pwrmon_debugfs;
+
+       svc->pwrmon_rails = kcalloc(svc->rail_count, sizeof(*svc->pwrmon_rails),
+                                   GFP_KERNEL);
+       if (!svc->pwrmon_rails)
+               goto err_pwrmon_debugfs_free;
+
+       if (gb_svc_pwrmon_rail_names_get(svc, svc->rail_names, bufsize))
+               goto err_pwrmon_debugfs_free;
+
+       for (i = 0; i < svc->rail_count; i++) {
+               struct dentry *dir;
+               struct svc_debugfs_pwrmon_rail *rail = &svc->pwrmon_rails[i];
+               char fname[GB_SVC_PWRMON_RAIL_NAME_BUFSIZE];
+
+               snprintf(fname, sizeof(fname), "%s",
+                        (char *)&svc->rail_names->name[i]);
+
+               rail->id = i;
+               rail->svc = svc;
+
+               dir = debugfs_create_dir(fname, dent);
+               debugfs_create_file("voltage_now", S_IRUGO, dir, rail,
+                                   &pwrmon_debugfs_voltage_fops);
+               debugfs_create_file("current_now", S_IRUGO, dir, rail,
+                                   &pwrmon_debugfs_current_fops);
+               debugfs_create_file("power_now", S_IRUGO, dir, rail,
+                                   &pwrmon_debugfs_power_fops);
+       };
+       return;
+
+err_pwrmon_debugfs_free:
+       kfree(svc->rail_names);
+       svc->rail_names = NULL;
+
+       kfree(svc->pwrmon_rails);
+       svc->pwrmon_rails = NULL;
+
+err_pwrmon_debugfs:
+       debugfs_remove(dent);
+}
+
+static void svc_debugfs_init(struct gb_svc *svc)
+{
+       svc->debugfs_dentry = debugfs_create_dir(dev_name(&svc->dev),
+                                                gb_debugfs_get());
+       svc_pwrmon_debugfs_init(svc);
+}
+
+static void svc_debugfs_exit(struct gb_svc *svc)
+{
+       debugfs_remove_recursive(svc->debugfs_dentry);
+       kfree(svc->rail_names);
+}
+
 static int gb_svc_hello(struct gb_operation *op)
 {
        struct gb_connection *connection = op->connection;
@@ -432,6 +660,8 @@ static int gb_svc_hello(struct gb_operation *op)
                return ret;
        }
 
+       svc_debugfs_init(svc);
+
        return 0;
 }
 
@@ -882,6 +1112,7 @@ void gb_svc_del(struct gb_svc *svc)
         * from the request handler.
         */
        if (device_is_registered(&svc->dev)) {
+               svc_debugfs_exit(svc);
                gb_svc_watchdog_destroy(svc);
                input_unregister_device(svc->input);
                device_del(&svc->dev);
index 08f8e3705b435aaf02e64abaf5534c02e8d3bef8..72bc716bf2fe228f4bbb0019e4877f698dac0bd8 100644 (file)
@@ -22,6 +22,11 @@ enum gb_svc_state {
 
 struct gb_svc_watchdog;
 
+struct svc_debugfs_pwrmon_rail {
+       u8 id;
+       struct gb_svc *svc;
+};
+
 struct gb_svc {
        struct device           dev;
 
@@ -40,6 +45,11 @@ struct gb_svc {
        struct input_dev        *input;
        char                    *input_phys;
        struct gb_svc_watchdog  *watchdog;
+
+       struct dentry *debugfs_dentry;
+       struct svc_debugfs_pwrmon_rail *pwrmon_rails;
+       struct gb_svc_pwrmon_rail_names_get_response *rail_names;
+       u8 rail_count;
 };
 #define to_gb_svc(d) container_of(d, struct gb_svc, dev)