summaryrefslogtreecommitdiff
path: root/drivers/edp
diff options
context:
space:
mode:
authorSivaram Nair <sivaramn@nvidia.com>2012-07-12 10:06:58 +0300
committerSimone Willett <swillett@nvidia.com>2012-08-02 13:51:36 -0700
commitb8118a5fa5b482228d0c2bc9df069ea74b871110 (patch)
tree31413fde5794da8333d190aa531ace19c326c790 /drivers/edp
parent84f0303b3f62e821c175611e926624c824540c14 (diff)
pm: EDP: loan handling
Added EDP loan APIs and single-borrower implementation. Change-Id: Ib4c3777d7173d16d5d002fd28e8f74f1b6add239 Signed-off-by: Sivaram Nair <sivaramn@nvidia.com> Reviewed-on: http://git-master/r/115713 Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: Diwakar Tundlam <dtundlam@nvidia.com> Reviewed-by: Juha Tukkinen <jtukkinen@nvidia.com> Reviewed-by: Peter De Schrijver <pdeschrijver@nvidia.com>
Diffstat (limited to 'drivers/edp')
-rw-r--r--drivers/edp/edp.c206
1 files changed, 199 insertions, 7 deletions
diff --git a/drivers/edp/edp.c b/drivers/edp/edp.c
index 63a7df1b3f10..900c2289adc6 100644
--- a/drivers/edp/edp.c
+++ b/drivers/edp/edp.c
@@ -21,8 +21,15 @@
#include <linux/string.h>
#include <linux/mutex.h>
#include <linux/errno.h>
+#include <linux/slab.h>
#include <linux/edp.h>
+struct loan_client {
+ struct list_head link;
+ struct edp_client *client;
+ unsigned int size;
+};
+
DEFINE_MUTEX(edp_lock);
static LIST_HEAD(edp_managers);
@@ -159,6 +166,10 @@ static int register_client(struct edp_manager *mgr, struct edp_client *client)
client->manager = mgr;
client->req = NULL;
client->cur = NULL;
+ INIT_LIST_HEAD(&client->borrowers);
+ client->num_borrowers = 0;
+ client->num_loans = 0;
+ client->ithreshold = client->states[0];
return 0;
}
@@ -175,6 +186,32 @@ int edp_register_client(struct edp_manager *mgr, struct edp_client *client)
}
EXPORT_SYMBOL(edp_register_client);
+static void update_loans(struct edp_client *client)
+{
+ struct loan_client *p;
+ unsigned int size;
+
+ if (!client->cur || list_empty(&client->borrowers))
+ return;
+
+ size = *client->cur < client->ithreshold ? 0 :
+ *client->cur - client->ithreshold;
+
+ /* TODO: multi-party loans */
+ if (!list_is_singular(&client->borrowers)) {
+ WARN_ON(1);
+ return;
+ }
+
+ p = list_first_entry(&client->borrowers, struct loan_client, link);
+
+ /* avoid spurious change notifications */
+ if (size != p->size) {
+ p->size = size;
+ p->client->notify_loan_update(size, client);
+ }
+}
+
static int mod_request(struct edp_client *client, const unsigned int *req)
{
unsigned int old = client->cur ? *client->cur : 0;
@@ -192,17 +229,45 @@ static int mod_request(struct edp_client *client, const unsigned int *req)
client->cur = req;
}
+ update_loans(client);
+
return 0;
}
+static void del_borrower(struct edp_client *lender, struct loan_client *pcl)
+{
+ pcl->client->notify_loan_close(lender);
+ lender->num_borrowers--;
+ pcl->client->num_loans--;
+ list_del(&pcl->link);
+ kfree(pcl);
+}
+
+static void close_all_loans(struct edp_client *client)
+{
+ struct loan_client *p;
+
+ while (!list_empty(&client->borrowers)) {
+ p = list_first_entry(&client->borrowers, struct loan_client,
+ link);
+ del_borrower(client, p);
+ }
+}
+
+static inline bool registered_client(struct edp_client *client)
+{
+ return client ? client->manager : false;
+}
+
static int unregister_client(struct edp_client *client)
{
- if (!client)
+ if (!registered_client(client))
return -EINVAL;
- if (!client->manager)
- return -ENODEV;
+ if (client->num_loans)
+ return -EBUSY;
+ close_all_loans(client);
mod_request(client, NULL);
list_del(&client->link);
client->manager = NULL;
@@ -227,12 +292,9 @@ static int update_client_request(struct edp_client *client, unsigned int req,
{
int r;
- if (!client)
+ if (!registered_client(client))
return -EINVAL;
- if (!client->manager)
- return -ENODEV;
-
if (req >= client->num_states)
return -EINVAL;
@@ -255,3 +317,133 @@ int edp_update_client_request(struct edp_client *client, unsigned int req,
return r;
}
EXPORT_SYMBOL(edp_update_client_request);
+
+static struct edp_client *get_client(const char *name)
+{
+ struct edp_client *client;
+ struct edp_manager *mgr;
+
+ if (!name)
+ return NULL;
+
+ list_for_each_entry(mgr, &edp_managers, link) {
+ client = find_client(mgr, name);
+ if (client)
+ return client;
+ }
+
+ return NULL;
+}
+
+struct edp_client *edp_get_client(const char *name)
+{
+ struct edp_client *client;
+
+ mutex_lock(&edp_lock);
+ client = get_client(name);
+ mutex_unlock(&edp_lock);
+
+ return client;
+}
+EXPORT_SYMBOL(edp_get_client);
+
+static struct loan_client *find_borrower(struct edp_client *lender,
+ struct edp_client *borrower)
+{
+ struct loan_client *p;
+
+ list_for_each_entry(p, &lender->borrowers, link)
+ if (p->client == borrower)
+ return p;
+ return NULL;
+}
+
+static int register_loan(struct edp_client *lender, struct edp_client *borrower)
+{
+ struct loan_client *p;
+
+ if (!registered_client(lender) || !registered_client(borrower))
+ return -EINVAL;
+
+ if (lender->manager != borrower->manager ||
+ !borrower->notify_loan_update ||
+ !borrower->notify_loan_close)
+ return -EINVAL;
+
+ if (find_borrower(lender, borrower))
+ return -EEXIST;
+
+ if (lender->num_borrowers >= lender->max_borrowers)
+ return -EBUSY;
+
+ p = kzalloc(sizeof(*p), GFP_KERNEL);
+ if (!p)
+ return -ENOMEM;
+
+ p->client = borrower;
+ lender->num_borrowers++;
+ borrower->num_loans++;
+ list_add_tail(&p->link, &lender->borrowers);
+
+ update_loans(lender);
+ return 0;
+}
+
+int edp_register_loan(struct edp_client *lender, struct edp_client *borrower)
+{
+ int r;
+
+ mutex_lock(&edp_lock);
+ r = register_loan(lender, borrower);
+ mutex_unlock(&edp_lock);
+
+ return r;
+}
+EXPORT_SYMBOL(edp_register_loan);
+
+static int unregister_loan(struct edp_client *lender,
+ struct edp_client *borrower)
+{
+ struct loan_client *p;
+
+ if (!registered_client(lender) || !registered_client(borrower))
+ return -EINVAL;
+
+ p = find_borrower(lender, borrower);
+ if (!p)
+ return -EINVAL;
+
+ del_borrower(lender, p);
+ update_loans(lender);
+ return 0;
+}
+
+int edp_unregister_loan(struct edp_client *lender, struct edp_client *borrower)
+{
+ int r;
+
+ mutex_lock(&edp_lock);
+ r = unregister_loan(lender, borrower);
+ mutex_unlock(&edp_lock);
+
+ return r;
+}
+EXPORT_SYMBOL(edp_unregister_loan);
+
+int edp_update_loan_threshold(struct edp_client *client, unsigned int threshold)
+{
+ int r = -EINVAL;
+
+ mutex_lock(&edp_lock);
+
+ if (registered_client(client)) {
+ client->ithreshold = threshold;
+ update_loans(client);
+ r = 0;
+ }
+
+ mutex_unlock(&edp_lock);
+
+ return r;
+}
+EXPORT_SYMBOL(edp_update_loan_threshold);