pm: EDP: adding client registration
Sivaram Nair [Tue, 3 Jul 2012 14:20:58 +0000 (17:20 +0300)]
This patch adds client registration functionality to the existing EDP
framework.

Bug ID: 917926

Change-Id: I8c9fbe3e1d934a6d95745f3c3933df4c1cbea4e7
Signed-off-by: Sivaram Nair <sivaramn@nvidia.com>
Reviewed-on: http://git-master/r/115706
Reviewed-by: Automatic_Commit_Validation_User
Reviewed-by: Matthew Longnecker <mlongnecker@nvidia.com>
Reviewed-by: Aleksandr Frid <afrid@nvidia.com>
Reviewed-by: Bo Yan <byan@nvidia.com>
Reviewed-by: Diwakar Tundlam <dtundlam@nvidia.com>

drivers/edp/edp.c
include/linux/edp.h

index 97ae21d..fcb9428 100644 (file)
@@ -51,6 +51,7 @@ int edp_register_manager(struct edp_manager *mgr)
        if (!find_manager(mgr->name)) {
                list_add_tail(&mgr->link, &edp_managers);
                mgr->registered = true;
+               INIT_LIST_HEAD(&mgr->clients);
                r = 0;
        }
        mutex_unlock(&edp_lock);
@@ -61,16 +62,19 @@ EXPORT_SYMBOL(edp_register_manager);
 
 int edp_unregister_manager(struct edp_manager *mgr)
 {
-       int r = -ENODEV;
+       int r = 0;
 
        if (!mgr)
                return -EINVAL;
 
        mutex_lock(&edp_lock);
-       if (mgr->registered) {
+       if (!mgr->registered) {
+               r = -ENODEV;
+       } else if (!list_empty(&mgr->clients)) {
+               r = -EBUSY;
+       } else {
                list_del(&mgr->link);
                mgr->registered = false;
-               r = 0;
        }
        mutex_unlock(&edp_lock);
 
@@ -89,3 +93,110 @@ struct edp_manager *edp_get_manager(const char *name)
        return mgr;
 }
 EXPORT_SYMBOL(edp_get_manager);
+
+static struct edp_client *find_client(struct edp_manager *mgr,
+               const char *name)
+{
+       struct edp_client *p;
+
+       if (!name)
+               return NULL;
+
+       list_for_each_entry(p, &mgr->clients, link)
+               if (!strcmp(p->name, name))
+                       return p;
+
+       return NULL;
+}
+
+static unsigned int e0_current_sum(struct edp_manager *mgr)
+{
+       struct edp_client *p;
+       unsigned int sum = 0;
+
+       list_for_each_entry(p, &mgr->clients, link)
+               sum += p->states[p->e0_index];
+
+       return sum;
+}
+
+static bool states_ok(struct edp_client *client)
+{
+       int i;
+
+       if (!client->states || !client->num_states ||
+                       client->e0_index >= client->num_states)
+               return false;
+
+       /* state array should be sorted in descending order */
+       for (i = 1; i < client->num_states; i++)
+               if (client->states[i] >= client->states[i - 1])
+                       return false;
+
+       return true;
+}
+
+static int register_client(struct edp_manager *mgr, struct edp_client *client)
+{
+       if (!mgr || !client)
+               return -EINVAL;
+
+       if (client->manager || find_client(mgr, client->name))
+               return -EEXIST;
+
+       if (!mgr->registered)
+               return -ENODEV;
+
+       if (!states_ok(client))
+               return -EINVAL;
+
+       /* make sure that we can satisfy E0 for all registered clients */
+       if (e0_current_sum(mgr) + client->states[client->e0_index] > mgr->imax)
+               return -E2BIG;
+
+       list_add_tail(&client->link, &mgr->clients);
+       client->manager = mgr;
+
+       return 0;
+}
+
+int edp_register_client(struct edp_manager *mgr, struct edp_client *client)
+{
+       int r;
+
+       mutex_lock(&edp_lock);
+       r = register_client(mgr, client);
+       mutex_unlock(&edp_lock);
+
+       return r;
+}
+EXPORT_SYMBOL(edp_register_client);
+
+static int unregister_client(struct edp_client *client)
+{
+       if (!client)
+               return -EINVAL;
+
+       if (!client->manager)
+               return -ENODEV;
+
+       if (!client->manager->registered)
+               return -ENODEV;
+
+       list_del(&client->link);
+       client->manager = NULL;
+
+       return 0;
+}
+
+int edp_unregister_client(struct edp_client *client)
+{
+       int r;
+
+       mutex_lock(&edp_lock);
+       r = unregister_client(client);
+       mutex_unlock(&edp_lock);
+
+       return r;
+}
+EXPORT_SYMBOL(edp_unregister_client);
index 1f4c12d..6d31123 100644 (file)
 
 struct edp_manager {
        const char name[EDP_NAME_LEN];
-       const unsigned int max_current;
+       const unsigned int imax;
 
        /* internal */
        struct list_head link;
+       struct list_head clients;
        bool registered;
 };
 
+/*
+ * @states: EDP state array holding the IMAX for each state.
+ *     This must be sorted in descending order.
+ * @num_states: length of the above array
+ * @e0_index: index of the E0 state in the above array
+ * Note that each EDP client is tied to a single EDP manager
+ */
+struct edp_client {
+       const char name[EDP_NAME_LEN];
+       const unsigned int *const states;
+       const unsigned int num_states;
+       const unsigned int e0_index;
+
+       /* internal */
+       struct list_head link;
+       struct edp_manager *manager;
+};
+
 #ifdef CONFIG_EDP_FRAMEWORK
 extern int edp_register_manager(struct edp_manager *mgr);
 extern int edp_unregister_manager(struct edp_manager *mgr);
 extern struct edp_manager *edp_get_manager(const char *name);
+
+extern int edp_register_client(struct edp_manager *mgr,
+               struct edp_client *client);
+extern int edp_unregister_client(struct edp_client *client);
 #else
 static inline int edp_register_manager(struct edp_manager *mgr)
 { return -ENODEV; }
@@ -44,6 +67,11 @@ static inline int edp_unregister_manager(struct edp_manager *mgr)
 { return -ENODEV; }
 static inline struct edp_manager *edp_get_manager(const char *name)
 { return NULL; }
+static inline int edp_register_client(struct edp_manager *mgr,
+               struct edp_client *client)
+{ return -ENODEV; }
+static inline int edp_unregister_client(struct edp_client *client)
+{ return -ENODEV; }
 #endif
 
 #endif