greybus: svc: add AP power measurements debugfs support

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>
This commit is contained in:
David Lin 2016-04-20 16:55:08 -07:00 committed by Greg Kroah-Hartman
parent 05a849191f
commit 9504677c9a
3 changed files with 263 additions and 0 deletions

View File

@ -967,6 +967,18 @@ struct gb_svc_key_event_request {
#define GB_SVC_KEY_PRESSED 0x01 #define GB_SVC_KEY_PRESSED 0x01
} __packed; } __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_CURR 0x01
#define GB_SVC_PWRMON_TYPE_VOL 0x02 #define GB_SVC_PWRMON_TYPE_VOL 0x02
#define GB_SVC_PWRMON_TYPE_PWR 0x03 #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_NOSUPP 0x02
#define GB_SVC_PWRMON_GET_SAMPLE_HWERR 0x03 #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 { struct gb_svc_pwrmon_intf_sample_get_request {
__u8 intf_id; __u8 intf_id;
__u8 measurement_type; __u8 measurement_type;

View File

@ -7,6 +7,7 @@
* Released under the GPLv2 only. * Released under the GPLv2 only.
*/ */
#include <linux/debugfs.h>
#include <linux/input.h> #include <linux/input.h>
#include <linux/workqueue.h> #include <linux/workqueue.h>
@ -99,6 +100,78 @@ static ssize_t watchdog_store(struct device *dev,
} }
static DEVICE_ATTR_RW(watchdog); 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, int gb_svc_pwrmon_intf_sample_get(struct gb_svc *svc, u8 intf_id,
u8 measurement_type, u32 *value) u8 measurement_type, u32 *value)
{ {
@ -393,6 +466,161 @@ static int gb_svc_version_request(struct gb_operation *op)
return 0; 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) static int gb_svc_hello(struct gb_operation *op)
{ {
struct gb_connection *connection = op->connection; struct gb_connection *connection = op->connection;
@ -432,6 +660,8 @@ static int gb_svc_hello(struct gb_operation *op)
return ret; return ret;
} }
svc_debugfs_init(svc);
return 0; return 0;
} }
@ -882,6 +1112,7 @@ void gb_svc_del(struct gb_svc *svc)
* from the request handler. * from the request handler.
*/ */
if (device_is_registered(&svc->dev)) { if (device_is_registered(&svc->dev)) {
svc_debugfs_exit(svc);
gb_svc_watchdog_destroy(svc); gb_svc_watchdog_destroy(svc);
input_unregister_device(svc->input); input_unregister_device(svc->input);
device_del(&svc->dev); device_del(&svc->dev);

View File

@ -22,6 +22,11 @@ enum gb_svc_state {
struct gb_svc_watchdog; struct gb_svc_watchdog;
struct svc_debugfs_pwrmon_rail {
u8 id;
struct gb_svc *svc;
};
struct gb_svc { struct gb_svc {
struct device dev; struct device dev;
@ -40,6 +45,11 @@ struct gb_svc {
struct input_dev *input; struct input_dev *input;
char *input_phys; char *input_phys;
struct gb_svc_watchdog *watchdog; 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) #define to_gb_svc(d) container_of(d, struct gb_svc, dev)