pm: EDP: enable updates via sysfs

This patch allows the user space to issue E-state and threshold change
requests. E-state updates are allowed only if the change is guaranteed
to be approved.

Change-Id: Id31f06ebb95f0b1fdfce205cb17038cb7a9eb30e
Signed-off-by: Sivaram Nair <sivaramn@nvidia.com>
Reviewed-on: http://git-master/r/145256
Reviewed-by: Automatic_Commit_Validation_User
GVS: Gerrit_Virtual_Submit
Reviewed-by: Diwakar Tundlam <dtundlam@nvidia.com>
Reviewed-by: Juha Tukkinen <jtukkinen@nvidia.com>

Rebase-Id: R91a3a085ffb54115e0e45297b1a6716d7529260c
diff --git a/drivers/edp/edp.c b/drivers/edp/edp.c
index b82375b..a2bd3ad 100644
--- a/drivers/edp/edp.c
+++ b/drivers/edp/edp.c
@@ -410,8 +410,8 @@
 }
 EXPORT_SYMBOL(edp_unregister_client);
 
-static int update_client_request(struct edp_client *client, unsigned int req,
-		int *approved)
+int edp_update_client_request_unlocked(struct edp_client *client,
+		unsigned int req, int *approved)
 {
 	int r;
 
@@ -434,7 +434,7 @@
 	int r;
 
 	mutex_lock(&edp_lock);
-	r = update_client_request(client, req, approved);
+	r = edp_update_client_request_unlocked(client, req, approved);
 	mutex_unlock(&edp_lock);
 
 	return r;
@@ -568,18 +568,23 @@
 }
 EXPORT_SYMBOL(edp_unregister_loan);
 
+int edp_update_loan_threshold_unlocked(struct edp_client *client,
+		unsigned int threshold)
+{
+	if (!registered_client(client))
+		return -EINVAL;
+
+	client->ithreshold = threshold;
+	update_loans(client);
+	return 0;
+}
+
 int edp_update_loan_threshold(struct edp_client *client, unsigned int threshold)
 {
-	int r = -EINVAL;
+	int r;
 
 	mutex_lock(&edp_lock);
-
-	if (registered_client(client)) {
-		client->ithreshold = threshold;
-		update_loans(client);
-		r = 0;
-	}
-
+	r = edp_update_loan_threshold_unlocked(client, threshold);
 	mutex_unlock(&edp_lock);
 
 	return r;
diff --git a/drivers/edp/edp_internal.h b/drivers/edp/edp_internal.h
index a0b9693..51b11ea 100644
--- a/drivers/edp/edp_internal.h
+++ b/drivers/edp/edp_internal.h
@@ -54,6 +54,10 @@
 extern struct mutex edp_lock;
 extern struct list_head edp_governors;
 
+int edp_update_client_request_unlocked(struct edp_client *client,
+		unsigned int req, int *approved);
+int edp_update_loan_threshold_unlocked(struct edp_client *client,
+		unsigned int threshold);
 struct edp_governor *edp_find_governor_unlocked(const char *s);
 int edp_set_governor_unlocked(struct edp_manager *mgr,
 		struct edp_governor *gov);
diff --git a/drivers/edp/sysfs.c b/drivers/edp/sysfs.c
index d23996b..c8baf88 100644
--- a/drivers/edp/sysfs.c
+++ b/drivers/edp/sysfs.c
@@ -174,6 +174,7 @@
 struct client_attr {
 	struct attribute attr;
 	ssize_t (*show)(struct edp_client *, char *);
+	ssize_t (*store)(struct edp_client *, const char *);
 };
 
 static ssize_t states_show(struct edp_client *c, char *s)
@@ -214,6 +215,23 @@
 	return scnprintf(s, PAGE_SIZE, "%u\n", req_level(c));
 }
 
+/* Allow only updates that are guaranteed to succeed */
+static ssize_t request_store(struct edp_client *c, const char *s)
+{
+	unsigned int id;
+
+	if (sscanf(s, "%u", &id) != 1)
+		return -EINVAL;
+
+	if (id >= c->num_states)
+		return -EINVAL;
+
+	if (id < c->e0_index && id < req_index(c))
+		return -EPERM;
+
+	return edp_update_client_request_unlocked(c, id, NULL);
+}
+
 static ssize_t current_show(struct edp_client *c, char *s)
 {
 	return scnprintf(s, PAGE_SIZE, "%u\n", cur_level(c));
@@ -221,8 +239,17 @@
 
 static ssize_t threshold_show(struct edp_client *c, char *s)
 {
-	return scnprintf(s, PAGE_SIZE, "%u\n",
-			c->num_loans ? c->ithreshold : 0);
+	return scnprintf(s, PAGE_SIZE, "%u\n", c->ithreshold);
+}
+
+static ssize_t threshold_store(struct edp_client *c, const char *s)
+{
+	unsigned int tv;
+
+	if (sscanf(s, "%u", &tv) != 1)
+		return -EINVAL;
+
+	return edp_update_loan_threshold_unlocked(c, tv);
 }
 
 static ssize_t borrowers_show(struct edp_client *c, char *s)
@@ -240,8 +267,10 @@
 struct client_attr attr_e0 = __ATTR_RO(e0);
 struct client_attr attr_max_borrowers = __ATTR_RO(max_borrowers);
 struct client_attr attr_priority = __ATTR_RO(priority);
-struct client_attr attr_request = __ATTR_RO(request);
-struct client_attr attr_threshold = __ATTR_RO(threshold);
+struct client_attr attr_request = __ATTR(request, 0644, request_show,
+		request_store);
+struct client_attr attr_threshold = __ATTR(threshold, 0644, threshold_show,
+		threshold_store);
 struct client_attr attr_borrowers = __ATTR_RO(borrowers);
 struct client_attr attr_loans = __ATTR_RO(loans);
 struct client_attr attr_current = {
@@ -286,8 +315,30 @@
 	return r;
 }
 
+static ssize_t client_state_store(struct kobject *kobj,
+		struct attribute *attr, const char *buf, size_t count)
+{
+	ssize_t r = -EINVAL;
+	struct edp_client *c;
+	struct client_attr *cattr;
+
+	mutex_lock(&edp_lock);
+
+	c = to_client(kobj);
+	cattr = container_of(attr, struct client_attr, attr);
+	if (c && cattr) {
+		if (cattr->store)
+			r = cattr->store(c, buf);
+	}
+
+	mutex_unlock(&edp_lock);
+
+	return r ?: count;
+}
+
 static const struct sysfs_ops client_sysfs_ops = {
-	.show = client_state_show
+	.show = client_state_show,
+	.store = client_state_store
 };
 
 static struct kobj_type ktype_client = {