diff --git a/drivers/staging/greybus/Documentation/sysfs-bus-greybus b/drivers/staging/greybus/Documentation/sysfs-bus-greybus index 17b1a2919ef9..281b05cb889c 100644 --- a/drivers/staging/greybus/Documentation/sysfs-bus-greybus +++ b/drivers/staging/greybus/Documentation/sysfs-bus-greybus @@ -164,3 +164,24 @@ Contact: Greg Kroah-Hartman Description: If the SVC watchdog is enabled or not. Writing 0 to this file will disable the watchdog, writing 1 will enable it. + +What: /sys/bus/greybus/device/N-I/voltage_now +Date: March 2016 +KernelVersion: 4.XX +Contact: Greg Kroah-Hartman +Description: + Voltage measurement of the interface in microvolts (uV) + +What: /sys/bus/greybus/device/N-I/current_now +Date: March 2016 +KernelVersion: 4.XX +Contact: Greg Kroah-Hartman +Description: + Current measurement of the interface in microamps (uA) + +What: /sys/bus/greybus/device/N-I/power_now +Date: March 2016 +KernelVersion: 4.XX +Contact: Greg Kroah-Hartman +Description: + Power measurement of the interface in microwatts (uW) diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 959ff1b7d8ce..e3ef3c96456d 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -798,6 +798,10 @@ struct gb_spi_transfer_response { #define GB_SVC_TYPE_INTF_EJECT 0x11 #define GB_SVC_TYPE_KEY_EVENT 0x12 #define GB_SVC_TYPE_PING 0x13 +#define GB_SVC_TYPE_PWRMON_RAIL_COUNT_GET 0x14 +#define GB_SVC_TYPE_PWRMON_RAIL_NAMES_GET 0x15 +#define GB_SVC_TYPE_PWRMON_SAMPLE_GET 0x16 +#define GB_SVC_TYPE_PWRMON_INTF_SAMPLE_GET 0x17 /* * SVC version request/response has the same payload as @@ -963,6 +967,25 @@ struct gb_svc_key_event_request { #define GB_SVC_KEY_PRESSED 0x01 } __packed; +#define GB_SVC_PWRMON_TYPE_CURR 0x01 +#define GB_SVC_PWRMON_TYPE_VOL 0x02 +#define GB_SVC_PWRMON_TYPE_PWR 0x03 + +#define GB_SVC_PWRMON_GET_SAMPLE_OK 0x00 +#define GB_SVC_PWRMON_GET_SAMPLE_INVAL 0x01 +#define GB_SVC_PWRMON_GET_SAMPLE_NOSUPP 0x02 +#define GB_SVC_PWRMON_GET_SAMPLE_HWERR 0x03 + +struct gb_svc_pwrmon_intf_sample_get_request { + __u8 intf_id; + __u8 measurement_type; +} __packed; + +struct gb_svc_pwrmon_intf_sample_get_response { + __u8 result; + __le32 measurement; +} __packed; + /* RAW */ /* Version of the Greybus raw protocol we support */ diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index 27dbd79d2e19..a4bee41feec4 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -263,6 +263,63 @@ static ssize_t version_show(struct device *dev, struct device_attribute *attr, } static DEVICE_ATTR_RO(version); +static ssize_t voltage_now_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct gb_interface *intf = to_gb_interface(dev); + int ret; + u32 measurement; + + ret = gb_svc_pwrmon_intf_sample_get(intf->hd->svc, intf->interface_id, + GB_SVC_PWRMON_TYPE_VOL, + &measurement); + if (ret) { + dev_err(&intf->dev, "failed to get voltage sample (%d)\n", ret); + return ret; + } + + return sprintf(buf, "%u\n", measurement); +} +static DEVICE_ATTR_RO(voltage_now); + +static ssize_t current_now_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct gb_interface *intf = to_gb_interface(dev); + int ret; + u32 measurement; + + ret = gb_svc_pwrmon_intf_sample_get(intf->hd->svc, intf->interface_id, + GB_SVC_PWRMON_TYPE_CURR, + &measurement); + if (ret) { + dev_err(&intf->dev, "failed to get current sample (%d)\n", ret); + return ret; + } + + return sprintf(buf, "%u\n", measurement); +} +static DEVICE_ATTR_RO(current_now); + +static ssize_t power_now_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct gb_interface *intf = to_gb_interface(dev); + int ret; + u32 measurement; + + ret = gb_svc_pwrmon_intf_sample_get(intf->hd->svc, intf->interface_id, + GB_SVC_PWRMON_TYPE_PWR, + &measurement); + if (ret) { + dev_err(&intf->dev, "failed to get power sample (%d)\n", ret); + return ret; + } + + return sprintf(buf, "%u\n", measurement); +} +static DEVICE_ATTR_RO(power_now); + static struct attribute *interface_attrs[] = { &dev_attr_ddbl1_manufacturer_id.attr, &dev_attr_ddbl1_product_id.attr, @@ -273,6 +330,9 @@ static struct attribute *interface_attrs[] = { &dev_attr_product_string.attr, &dev_attr_serial_number.attr, &dev_attr_version.attr, + &dev_attr_voltage_now.attr, + &dev_attr_current_now.attr, + &dev_attr_power_now.attr, NULL, }; ATTRIBUTE_GROUPS(interface); diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index a19e575de029..b79d81717360 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -99,6 +99,44 @@ static ssize_t watchdog_store(struct device *dev, } static DEVICE_ATTR_RW(watchdog); +int gb_svc_pwrmon_intf_sample_get(struct gb_svc *svc, u8 intf_id, + u8 measurement_type, u32 *value) +{ + struct gb_svc_pwrmon_intf_sample_get_request request; + struct gb_svc_pwrmon_intf_sample_get_response response; + int ret; + + request.intf_id = intf_id; + request.measurement_type = measurement_type; + + ret = gb_operation_sync(svc->connection, + GB_SVC_TYPE_PWRMON_INTF_SAMPLE_GET, + &request, sizeof(request), + &response, sizeof(response)); + if (ret) { + dev_err(&svc->dev, "failed to get intf sample (%d)\n", ret); + return ret; + } + + if (response.result) { + dev_err(&svc->dev, + "UniPro error while getting intf power sample (%d %d): %d\n", + intf_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; +} + static struct attribute *svc_attrs[] = { &dev_attr_endo_id.attr, &dev_attr_ap_intf_id.attr, diff --git a/drivers/staging/greybus/svc.h b/drivers/staging/greybus/svc.h index 8950baff9aef..08f8e3705b43 100644 --- a/drivers/staging/greybus/svc.h +++ b/drivers/staging/greybus/svc.h @@ -48,6 +48,8 @@ int gb_svc_add(struct gb_svc *svc); void gb_svc_del(struct gb_svc *svc); void gb_svc_put(struct gb_svc *svc); +int gb_svc_pwrmon_intf_sample_get(struct gb_svc *svc, u8 intf_id, + u8 measurement_type, u32 *value); int gb_svc_intf_device_id(struct gb_svc *svc, u8 intf_id, u8 device_id); int gb_svc_route_create(struct gb_svc *svc, u8 intf1_id, u8 dev1_id, u8 intf2_id, u8 dev2_id);