diff options
author | Sivaram Nair <sivaramn@nvidia.com> | 2012-07-12 10:06:58 +0300 |
---|---|---|
committer | Simone Willett <swillett@nvidia.com> | 2012-08-02 13:51:36 -0700 |
commit | b8118a5fa5b482228d0c2bc9df069ea74b871110 (patch) | |
tree | 31413fde5794da8333d190aa531ace19c326c790 /drivers/edp | |
parent | 84f0303b3f62e821c175611e926624c824540c14 (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.c | 206 |
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); |