video: tegra: host: expose power management paramters via sysfs
Mayuresh Kulkarni [Tue, 12 Jun 2012 12:52:26 +0000 (17:52 +0530)]
this commit exposes following power management parameter through
sysfs:
- clockgate_delay: delay after which module is clock gated after
it goes idle. this read/write attribute & unit is ms.
- powergate_delay: delay after which module is power gated after
it is clock gated. this is read/write attribute & unit is ms.
- refcount: current reference count on the module. this is
read-only attribute.

path is: /sys/devices/host1x/<device-name>/acm/ where
<device-name> = name of device node like gr2d or gr3d etc

Bug 845598

Change-Id: I6011eb90ee85b5fc576320272e657ce31f9e264d
Signed-off-by: Mayuresh Kulkarni <mkulkarni@nvidia.com>
Reviewed-on: http://git-master/r/108827
Reviewed-by: Automatic_Commit_Validation_User
Reviewed-by: Francis Hart <fhart@nvidia.com>
Reviewed-by: Terje Bergstrom <tbergstrom@nvidia.com>

drivers/video/tegra/host/nvhost_acm.c
include/linux/nvhost.h

index dfd8432..37b81d7 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Tegra Graphics Host Automatic Clock Management
  *
- * Copyright (c) 2010-2012, NVIDIA Corporation.
+ * Copyright (c) 2010-2012, NVIDIA Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms and conditions of the GNU General Public License,
@@ -233,7 +233,6 @@ static void powerstate_down_handler(struct work_struct *work)
        mutex_unlock(&dev->lock);
 }
 
-
 void nvhost_module_idle_mult(struct nvhost_device *dev, int refs)
 {
        struct nvhost_driver *drv = to_nvhost_driver(dev->dev.driver);
@@ -360,9 +359,103 @@ void nvhost_module_remove_client(struct nvhost_device *dev, void *priv)
        mutex_unlock(&client_list_lock);
 }
 
+static ssize_t refcount_show(struct kobject *kobj,
+       struct kobj_attribute *attr, char *buf)
+{
+       int ret;
+       struct nvhost_device_power_attr *power_attribute =
+               container_of(attr, struct nvhost_device_power_attr, \
+                       power_attr[NVHOST_POWER_SYSFS_ATTRIB_REFCOUNT]);
+       struct nvhost_device *dev = power_attribute->ndev;
+
+       mutex_lock(&dev->lock);
+       ret = sprintf(buf, "%d\n", dev->refcount);
+       mutex_unlock(&dev->lock);
+
+       return ret;
+}
+
+static ssize_t powergate_delay_store(struct kobject *kobj,
+       struct kobj_attribute *attr, const char *buf, size_t count)
+{
+       int powergate_delay = 0, ret = 0;
+       struct nvhost_device_power_attr *power_attribute =
+               container_of(attr, struct nvhost_device_power_attr, \
+                       power_attr[NVHOST_POWER_SYSFS_ATTRIB_POWERGATE_DELAY]);
+       struct nvhost_device *dev = power_attribute->ndev;
+
+       if (!dev->can_powergate) {
+               dev_info(&dev->dev, "does not support power-gating\n");
+               return count;
+       }
+
+       mutex_lock(&dev->lock);
+       ret = sscanf(buf, "%d", &powergate_delay);
+       if (ret == 1 && powergate_delay >= 0)
+               dev->powergate_delay = powergate_delay;
+       else
+               dev_err(&dev->dev, "Invalid powergate delay\n");
+       mutex_unlock(&dev->lock);
+
+       return count;
+}
+
+static ssize_t powergate_delay_show(struct kobject *kobj,
+       struct kobj_attribute *attr, char *buf)
+{
+       int ret;
+       struct nvhost_device_power_attr *power_attribute =
+               container_of(attr, struct nvhost_device_power_attr, \
+                       power_attr[NVHOST_POWER_SYSFS_ATTRIB_POWERGATE_DELAY]);
+       struct nvhost_device *dev = power_attribute->ndev;
+
+       mutex_lock(&dev->lock);
+       ret = sprintf(buf, "%d\n", dev->powergate_delay);
+       mutex_unlock(&dev->lock);
+
+       return ret;
+}
+
+static ssize_t clockgate_delay_store(struct kobject *kobj,
+       struct kobj_attribute *attr, const char *buf, size_t count)
+{
+       int clockgate_delay = 0, ret = 0;
+       struct nvhost_device_power_attr *power_attribute =
+               container_of(attr, struct nvhost_device_power_attr, \
+                       power_attr[NVHOST_POWER_SYSFS_ATTRIB_CLOCKGATE_DELAY]);
+       struct nvhost_device *dev = power_attribute->ndev;
+
+       mutex_lock(&dev->lock);
+       ret = sscanf(buf, "%d", &clockgate_delay);
+       if (ret == 1 && clockgate_delay >= 0)
+               dev->clockgate_delay = clockgate_delay;
+       else
+               dev_err(&dev->dev, "Invalid clockgate delay\n");
+       mutex_unlock(&dev->lock);
+
+       return count;
+}
+
+static ssize_t clockgate_delay_show(struct kobject *kobj,
+       struct kobj_attribute *attr, char *buf)
+{
+       int ret;
+       struct nvhost_device_power_attr *power_attribute =
+               container_of(attr, struct nvhost_device_power_attr, \
+                       power_attr[NVHOST_POWER_SYSFS_ATTRIB_CLOCKGATE_DELAY]);
+       struct nvhost_device *dev = power_attribute->ndev;
+
+       mutex_lock(&dev->lock);
+       ret = sprintf(buf, "%d\n", dev->clockgate_delay);
+       mutex_unlock(&dev->lock);
+
+       return ret;
+}
+
 int nvhost_module_init(struct nvhost_device *dev)
 {
-       int i = 0;
+       int i = 0, err = 0;
+       struct kobj_attribute *attr = NULL;
 
        /* initialize clocks to known state */
        INIT_LIST_HEAD(&dev->client_list);
@@ -403,7 +496,71 @@ int nvhost_module_init(struct nvhost_device *dev)
                dev->powerstate = NVHOST_POWER_STATE_CLOCKGATED;
        }
 
+       /* Init the power sysfs attributes for this device */
+       dev->power_attrib = kzalloc(sizeof(struct nvhost_device_power_attr),
+               GFP_KERNEL);
+       if (!dev->power_attrib) {
+               dev_err(&dev->dev, "Unable to allocate sysfs attributes\n");
+               return -ENOMEM;
+       }
+       dev->power_attrib->ndev = dev;
+
+       dev->power_kobj = kobject_create_and_add("acm", &dev->dev.kobj);
+       if (!dev->power_kobj) {
+               dev_err(&dev->dev, "Could not add dir 'power'\n");
+               err = -EIO;
+               goto fail_attrib_alloc;
+       }
+
+       attr = &dev->power_attrib->power_attr[NVHOST_POWER_SYSFS_ATTRIB_CLOCKGATE_DELAY];
+       attr->attr.name = "clockgate_delay";
+       attr->attr.mode = S_IWUSR | S_IRUGO;
+       attr->show = clockgate_delay_show;
+       attr->store = clockgate_delay_store;
+       if (sysfs_create_file(dev->power_kobj, &attr->attr)) {
+               dev_err(&dev->dev, "Could not create sysfs attribute clockgate_delay\n");
+               err = -EIO;
+               goto fail_clockdelay;
+       }
+
+       attr = &dev->power_attrib->power_attr[NVHOST_POWER_SYSFS_ATTRIB_POWERGATE_DELAY];
+       attr->attr.name = "powergate_delay";
+       attr->attr.mode = S_IWUSR | S_IRUGO;
+       attr->show = powergate_delay_show;
+       attr->store = powergate_delay_store;
+       if (sysfs_create_file(dev->power_kobj, &attr->attr)) {
+               dev_err(&dev->dev, "Could not create sysfs attribute powergate_delay\n");
+               err = -EIO;
+               goto fail_powergatedelay;
+       }
+
+       attr = &dev->power_attrib->power_attr[NVHOST_POWER_SYSFS_ATTRIB_REFCOUNT];
+       attr->attr.name = "refcount";
+       attr->attr.mode = S_IRUGO;
+       attr->show = refcount_show;
+       if (sysfs_create_file(dev->power_kobj, &attr->attr)) {
+               dev_err(&dev->dev, "Could not create sysfs attribute refcount\n");
+               err = -EIO;
+               goto fail_refcount;
+       }
+
        return 0;
+
+fail_refcount:
+       attr = &dev->power_attrib->power_attr[NVHOST_POWER_SYSFS_ATTRIB_POWERGATE_DELAY];
+       sysfs_remove_file(dev->power_kobj, &attr->attr);
+
+fail_powergatedelay:
+       attr = &dev->power_attrib->power_attr[NVHOST_POWER_SYSFS_ATTRIB_CLOCKGATE_DELAY];
+       sysfs_remove_file(dev->power_kobj, &attr->attr);
+
+fail_clockdelay:
+       kobject_put(dev->power_kobj);
+
+fail_attrib_alloc:
+       kfree(dev->power_attrib);
+
+       return err;
 }
 
 static int is_module_idle(struct nvhost_device *dev)
index 382a54f..2f2d615 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Tegra graphics host driver
  *
- * Copyright (c) 2009-2012, NVIDIA Corporation.
+ * Copyright (c) 2009-2012, NVIDIA Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
 #include <linux/types.h>
 
 struct nvhost_master;
-
-#define NVHOST_MODULE_MAX_CLOCKS 3
-#define NVHOST_MODULE_MAX_POWERGATE_IDS 2
-#define NVHOST_MODULE_NO_POWERGATE_IDS .powergate_ids = {-1, -1}
-#define NVHOST_DEFAULT_CLOCKGATE_DELAY .clockgate_delay = 25
-#define NVHOST_NAME_SIZE       24
+struct nvhost_device_power_attr;
+
+#define NVHOST_MODULE_MAX_CLOCKS               3
+#define NVHOST_MODULE_MAX_POWERGATE_IDS        2
+#define NVHOST_MODULE_NO_POWERGATE_IDS         .powergate_ids = {-1, -1}
+#define NVHOST_DEFAULT_CLOCKGATE_DELAY         .clockgate_delay = 25
+#define NVHOST_NAME_SIZE                       24
+
+enum nvhost_power_sysfs_attributes {
+       NVHOST_POWER_SYSFS_ATTRIB_CLOCKGATE_DELAY = 0,
+       NVHOST_POWER_SYSFS_ATTRIB_POWERGATE_DELAY,
+       NVHOST_POWER_SYSFS_ATTRIB_REFCOUNT,
+       NVHOST_POWER_SYSFS_ATTRIB_MAX
+};
 
 struct nvhost_device_id {
        char name[NVHOST_NAME_SIZE];
@@ -89,6 +97,13 @@ struct nvhost_device {
        struct list_head client_list;   /* List of clients and rate requests */
 
        struct nvhost_channel *channel; /* Channel assigned for the module */
+       struct kobject *power_kobj;     /* kobject to hold power sysfs entries */
+       struct nvhost_device_power_attr *power_attrib;  /* sysfs attributes */
+};
+
+struct nvhost_device_power_attr {
+       struct nvhost_device *ndev;
+       struct kobj_attribute power_attr[NVHOST_POWER_SYSFS_ATTRIB_MAX];
 };
 
 /* Register devices to nvhost bus */