/arch/arm/mach-fsm/npa.c
C | 1572 lines | 1166 code | 246 blank | 160 comment | 157 complexity | 08f28696f4476f1e8ac612cff21e3393 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.0, AGPL-1.0
/* Copyright (c) 2009-2010, Code Aurora Forum. 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 version 2 and * only version 2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * */ /* * Node Power Architecture (NPA) implementation. */ #include <linux/kernel.h> #include <linux/init.h> #include <linux/module.h> #include <linux/err.h> #include <linux/string.h> #include <linux/workqueue.h> #include <linux/spinlock_types.h> #include "npa.h" #include "npa_resource.h" #define NPA_STR_NPA_INTERNAL "NPA-Internal" #define NPA_STR_USER_CALLBACK "User-Callback" /* Resource lock could be called with NULL mutex, when only the container * is created and the node is not defined yet. */ #ifdef CONFIG_MSM_NPA_LOG #define RESOURCE_LOCK(r) do { \ BUG_ON(!r); \ if ((r)->definition)\ npa_log(NPA_LOG_MASK_LOCKS, r,\ "NPA: Resource [%s] level [%u] locked [%p] at "\ "line [%d] by process [%p]\n",\ (r)->definition->name, (r)->level, (r)->resource_lock,\ __LINE__, current);\ if ((r)->resource_lock)\ mutex_lock_nested((r)->resource_lock, (r)->level);\ } \ while (0) #define RESOURCE_UNLOCK(r) do { \ BUG_ON(!r); \ if ((r)->definition)\ npa_log(NPA_LOG_MASK_LOCKS, r,\ "NPA: Resource [%s] level [%u] unlocked [%p] at "\ "line [%d] by process [%p]\n",\ (r)->definition->name, (r)->level, (r)->resource_lock,\ __LINE__, current);\ if ((r)->resource_lock)\ mutex_unlock((r)->resource_lock);\ } \ while (0) #else #define RESOURCE_LOCK(r) do { \ BUG_ON(!r); \ if ((r)->resource_lock) \ mutex_lock_nested((r)->resource_lock, (r)->level) ;\ } \ while (0) #define RESOURCE_UNLOCK(r) do { \ BUG_ON(!r); \ if ((r)->resource_lock) \ mutex_unlock((r)->resource_lock); \ } \ while (0) #endif #ifdef CONFIG_MSM_NPA_LOG int npa_log_mask; EXPORT_SYMBOL(npa_log_mask); char npa_log_resource_name[NPA_NAME_MAX] = {0}; EXPORT_SYMBOL(npa_log_resource_name); int npa_log_reset; EXPORT_SYMBOL(npa_log_reset); module_param_named(log_mask, npa_log_mask, int, S_IRUGO | S_IWUSR | S_IWGRP); MODULE_PARM_DESC(npa_log_mask, "Specify NPA logging for any or all of " "resource, client states, events, lists, plugin or locks."); module_param_string(log_resource_name, npa_log_resource_name, sizeof(npa_log_resource_name), S_IRUGO | S_IWUSR | S_IWGRP); MODULE_PARM_DESC(npa_log_resource_name, "Specify logging for a specific NPA resource."); module_param_named(log_reset, npa_log_reset, int, S_IRUGO | S_IWUSR | S_IWGRP); MODULE_PARM_DESC(npa_log_reset, "Reset NPA module parameters."); struct npa_resource *npa_log_resource; #endif /* Maps resource name to alias name.*/ struct npa_alias_list { struct list_head list; const char *alias; struct npa_resource *resource; }; static LIST_HEAD(alias_list); /* List of aliases */ static LIST_HEAD(active_list); /* Resources defined and active */ static LIST_HEAD(waiting_list); /* Resources pending definition or waiting on dependencies. */ static DEFINE_RWLOCK(list_lock);/* RW lock for locking active and waiting lists */ static struct workqueue_struct *npa_wq; static struct npa_resource *__get_resource(const char *resource_name); static void resource_creation_handler(void *data, unsigned int state, void *called_data, unsigned int value); static void send_single_event(struct work_struct *work); static void send_update_events(struct work_struct *work); #ifdef CONFIG_MSM_NPA_LOG void _npa_log(int log_mask, struct npa_resource *res, const char *fmt, ...) { if (npa_log_reset) { npa_log_mask = 0; npa_log_resource = NULL; npa_log_resource_name[0] = 0; npa_log_reset = 0; } if (npa_log_resource_name[0] && !npa_log_resource) { npa_log_resource = __get_resource(npa_log_resource_name); if (npa_log_resource) pr_info("NPA: Logging for resource [%s]\n", npa_log_resource_name); } if (!npa_log_resource || (npa_log_resource && (res == npa_log_resource))) if (log_mask & npa_log_mask) { va_list ap; va_start(ap, fmt); vprintk(fmt, ap); va_end(ap); } } void __print_resource(struct npa_resource *r) { struct npa_client *client = NULL; struct npa_event *e = NULL; struct npa_alias_list *a = NULL; const char *rname = r->definition->name; if (!(npa_log_mask & NPA_LOG_MASK_RESOURCE)) return; list_for_each_entry(a, &alias_list, list) { if (!strncmp(a->resource->definition->name, r->definition->name, NPA_NAME_MAX)) { pr_info("NPA: Resource [%s] Alias: [%s]\n", rname, a->alias); } } pr_info("NPA: Resource [%s] Units: [%s]\n", rname, r->definition->units); pr_info("NPA: Resource [%s] Node name: [%s]\n", rname, r->node->name); pr_info("NPA: Resource [%s] Level: [%u]\n", rname, r->level); list_for_each_entry(client, &r->clients, list) { pr_info("NPA: Resource [%s] client [%s: %u]\n", rname, client->name, ACTIVE_STATE(client)); } list_for_each_entry(e, &r->events, list) { pr_info("NPA: Reource [%s] event [%s, %d]\n", rname, e->handler_name, e->type); } pr_info("NPA: Resource [%s] Active state: [%u]\n", rname, r->active_state); pr_info("NPA: Resource [%s] Active max: [%u]\n", rname, r->active_max); pr_info("NPA: Resource [%s] Active headroom: [%u]\n", rname, r->active_headroom); } EXPORT_SYMBOL(__print_resource); void __print_resources(void) { struct npa_resource *resource = NULL; if (!(npa_log_mask & NPA_LOG_MASK_RESOURCE)) return; list_for_each_entry(resource, &active_list, list) __print_resource(resource); list_for_each_entry(resource, &waiting_list, list) __print_resource(resource); } EXPORT_SYMBOL(__print_resources); void __print_client_states(struct npa_resource *resource) { struct npa_client *client = NULL; if (!(npa_log_mask & NPA_LOG_MASK_LIST)) return; list_for_each_entry(client, &resource->clients, list) { pr_info("NPA: Resource [%s] client [%s: %u]\n", resource->definition->name, client->name, ACTIVE_STATE(client)); } } EXPORT_SYMBOL(__print_client_states); void __print_aliases(void) { struct npa_alias_list *a = NULL; if (npa_log_mask & NPA_LOG_MASK_LIST) return; list_for_each_entry(a, &alias_list, list) { pr_info("NPA: Alias[%s --> %s]\n", a->alias, a->resource->definition->name); } } EXPORT_SYMBOL(__print_aliases); #endif /* Returns the resource, if the name matches any element in the list. * NULL if not found. * @resource_name can be original resource name or an alias. */ static struct npa_resource *__get_resource(const char *resource_name) { struct npa_alias_list *alias = NULL; struct npa_alias_list *temp = NULL; list_for_each_entry_safe(alias, temp, &alias_list, list) { if (!strncmp(resource_name, alias->alias, NPA_NAME_MAX)) return alias->resource; } return NULL; } static int __is_active_resource(struct npa_resource *resource) { struct npa_resource *res_itr = NULL; if (resource && !list_empty(&active_list)) { list_for_each_entry(res_itr, &active_list, list) { if (resource == res_itr) return 1; } } return 0; } static struct npa_resource *active_resource(const char *resource_name) { struct npa_resource *resource = NULL; read_lock(&list_lock); resource = __get_resource(resource_name); if (resource) resource = __is_active_resource(resource) ? resource : NULL; read_unlock(&list_lock); return resource; } /* Creates a resource object with the definition provided. The created * resource is automatically added to the waiting list. create_resource can be * called with an node pointer or NULL, indicating the node has to be created. * If the node already exists, then the resource definition and the resource * objects will be replaced with the ones provided. * Node = NULL, resource_defn = valid, name = valid: * Create a new resource for the first time and create a node for it. * Node = valid, resource_defn = valid, name = valid: * Link a new node to an existing resource. */ static struct npa_resource *create_resource(struct npa_node_definition *node, struct npa_resource_definition *resource_defn, const char *name) { struct npa_alias_list *new_alias = NULL; struct npa_resource *new_resource = NULL; struct npa_resource *resource = NULL; struct npa_node_definition *old_node = NULL; struct npa_node_definition *new_node = NULL; struct npa_resource_definition *old_defn = NULL; struct npa_resource_definition *new_defn = NULL; if (!node) { new_node = kzalloc(sizeof(struct npa_node_definition), GFP_KERNEL); new_defn = kzalloc(sizeof(struct npa_resource_definition), GFP_KERNEL); new_defn->name = name; new_node->resources = new_defn; new_node->resource_count = 1; new_node->dependencies = NULL; new_node->dependency_count = 0; } else { new_node = node; new_defn = resource_defn; } write_lock(&list_lock); resource = __get_resource(name); if (resource && !node) goto done; if (!resource) { new_resource = kzalloc(sizeof(struct npa_resource), GFP_ATOMIC); new_alias = kzalloc(sizeof(struct npa_alias_list), GFP_ATOMIC); new_alias->alias = name; new_alias->resource = new_resource; INIT_LIST_HEAD(&new_alias->list); new_resource->node = new_node; new_resource->definition = new_defn; INIT_LIST_HEAD(&new_resource->list); INIT_LIST_HEAD(&new_resource->clients); INIT_LIST_HEAD(&new_resource->events); INIT_LIST_HEAD(&new_resource->watermarks); INIT_WORK(&new_resource->work, send_update_events); list_add(&new_alias->list, &alias_list); list_add(&new_resource->list, &waiting_list); resource = new_resource; } else { /* Resource exists */ old_node = resource->node; old_defn = resource->definition; /* Replace the alias name pointer to use new defn. */ if (old_defn) { struct npa_alias_list *a = NULL; list_for_each_entry(a, &alias_list, list) { if (a->alias == old_defn->name) a->alias = new_defn->name; } } /* Replace the node and resource defn. */ resource->node = new_node; resource->definition = new_defn; } done: write_unlock(&list_lock); npa_log(NPA_LOG_MASK_RESOURCE, resource, "NPA: Resource [%s] definition created\n", resource->definition->name); if (resource != new_resource) { kfree(old_defn); kfree(old_node); if (!node) { kfree(new_node); kfree(new_defn); } kfree(new_resource); kfree(new_alias); } return resource; } /* Register callback function for a resource creation event * (NPA_EVENT_RESERVED1). The event object malloc'd in this function is * released is to be released in the handler. NPA_EVENT_RESERVED1 events * are use and throw events and should be removed from the list once notified. */ static int register_resource_event(struct npa_resource *resource, npa_cb_fn callback, void *data) { struct npa_event *event = NULL; if (!callback) return 0; /* Nothing to wait for */ event = kzalloc(sizeof(struct npa_event), GFP_KERNEL); INIT_LIST_HEAD(&event->list); INIT_WORK(&event->work, send_single_event); event->type = NPA_EVENT_RESERVED1; event->resource = resource; /* Helpful identification string to distinguish user callbacks * from NPA internal resource completion events. */ if (callback == resource_creation_handler) event->handler_name = NPA_STR_NPA_INTERNAL; else event->handler_name = NPA_STR_USER_CALLBACK; event->callback = callback; event->user_data = data; RESOURCE_LOCK(resource); list_add(&event->list, &resource->events); RESOURCE_UNLOCK(resource); npa_log(NPA_LOG_MASK_RESOURCE, resource, "NPA: Registered for NPA_EVENT_RESERVED1 from resource [%s]\n", resource->definition->name); return 0; } /* Sends notification to this particular event object. This is mostly used * when the event is added to the resource. */ static void send_single_event(struct work_struct *work) { struct npa_event *event = container_of(work, struct npa_event, work); struct npa_resource *resource = event->resource; struct npa_event_data event_data; /* Read the state when it is not being modified. */ RESOURCE_LOCK(resource); event_data.resource_name = resource->definition->name; event_data.state = resource->active_state; event_data.headroom = resource->active_headroom; RESOURCE_UNLOCK(resource); npa_log(NPA_LOG_MASK_EVENT, resource, "NPA: Sending single event [%s] for resource [%s]\n", event->handler_name, resource->definition->name); event->callback(event->user_data, event->type, &event_data, 0); } /* Executes callbacks on all event objects. Callbacks are called in the * sequence of the event objects. This function does not entertain any * priority. If a callback blocks, it will affect all others that are * pending in the event list, as well as blocking the NPA work queue. */ static void send_update_events(struct work_struct *work) { int expired = 0; struct npa_event *event = NULL; struct npa_event *temp = NULL; struct npa_resource *resource = container_of(work, struct npa_resource, work); struct npa_event_data event_data; npa_log(NPA_LOG_MASK_EVENT, resource, "NPA: Resource [%s] sending change events\n", resource->definition->name); /* Read the state when it is not being modified. */ RESOURCE_LOCK(resource); event_data.resource_name = resource->definition->name; event_data.state = resource->active_state; event_data.headroom = resource->active_headroom; RESOURCE_UNLOCK(resource); list_for_each_entry_safe(event, temp, &resource->events, list) { expired = 0; switch (event->type) { case NPA_EVENT_RESERVED1: /* Destroy after execution. */ list_del(&event->list); expired = 1; break; case NPA_EVENT_CHANGE: break; default: BUG(); break; } npa_log(NPA_LOG_MASK_EVENT, resource, "NPA: Resource [%s] sending event [%d] to [%s] " "with state [%u] headroom [%d] \n", resource->definition->name, event->type, event->handler_name, event_data.state, event_data.headroom); event->callback(event->user_data, event->type, &event_data, 0); if (expired) kfree(event); } npa_log(NPA_LOG_MASK_EVENT, resource, "NPA: Resource [%s] sending watermark events\n", resource->definition->name); /* Send the watermark events */ list_for_each_entry_safe(event, temp, &resource->watermarks, list) { if (event_data.state <= event->lo_watermark) { npa_log(NPA_LOG_MASK_EVENT, resource, "NPA: Resource [%s] sending " "low watermark event to [%s] " "with state [%u] headroom [%d]\n", resource->definition->name, event->handler_name, event_data.state, event_data.headroom); event->callback(event->user_data, NPA_EVENT_LO_WATERMARK, &event_data, 0); } if (event_data.state >= event->hi_watermark) { npa_log(NPA_LOG_MASK_EVENT, resource, "NPA: Resource [%s] sending " "high watermark event to [%s] " "with state [%u] headroom [%d]\n", resource->definition->name, event->handler_name, event_data.state, event_data.headroom); event->callback(event->user_data, NPA_EVENT_HI_WATERMARK, &event_data, 0); } } } /* Schedules a work to notify all those registered for events of the change * in resource state. */ static void publish_resource_state(struct npa_resource *resource) { npa_log(NPA_LOG_MASK_EVENT, resource, "NPA: Queueing work for resource [%s]\n", resource->definition->name); queue_work(npa_wq, &resource->work); } /* Activate this resource and all other resources that are part of this node. * Resources when created by default are put in waiting lists. * This function is to be called, when all the dependencies of this * resources are available and the resource is ready to be used. */ static int activate_node(struct npa_resource *resource) { struct npa_resource_definition *def = NULL; struct npa_node_dependency *node_dep = NULL; struct npa_client dummy = { .type = NPA_CLIENT_REQUIRED, .name = NPA_STR_NPA_INTERNAL, .resource_name = resource->definition->name, }; unsigned int count = 0; unsigned int node_level = 0; npa_log(NPA_LOG_MASK_RESOURCE, resource, "NPA: Resource [%s] being activated\n", resource->definition->name); /* Create clients for all the dependencies, so that the driver * function can send the requests to these resources as well. */ RESOURCE_LOCK(resource); node_dep = resource->node->dependencies; count = resource->node->dependency_count; while (count) { struct npa_client *client = npa_create_sync_client(node_dep->name, resource->node->name, node_dep->client_type); BUG_ON((client == NULL) || IS_ERR(client)); node_dep->handle = client; if (client->resource->level > node_level) node_level = client->resource->level; count--; node_dep++; } def = resource->node->resources; count = resource->node->resource_count; while (count) { write_lock(&list_lock); list_del(&def->resource->list); /* Remove from waiting list */ list_add(&def->resource->list, &active_list); write_unlock(&list_lock); def->resource->active_state = def->resource->node->driver_fn(def->resource, &dummy, def->resource->requested_state); def->resource->active_headroom = def->resource->active_max - def->resource->active_state; def->resource->level = node_level + 1; count--; def++; } RESOURCE_UNLOCK(resource); /* Schedule a work to signal all dependencies that may depend * on this resource. */ def = resource->node->resources; count = resource->node->resource_count; while (count) { publish_resource_state(def->resource); def++; count--; } return 0; } static void request_state(struct npa_resource *resource, struct npa_client *client, unsigned int state) { unsigned int new_state; npa_log(NPA_LOG_MASK_CLIENT, resource, "NPA: Resource [%s] client [%s] requested state [%u]\n", resource->definition->name, client->name, state); RESOURCE_LOCK(resource); PENDING_STATE(client) = state; new_state = resource->active_plugin->update_fn(resource, client); ACTIVE_STATE(client) = PENDING_STATE(client); resource->requested_state = new_state; if (new_state > resource->active_max) new_state = resource->active_max; if ((resource->definition->attributes & NPA_RESOURCE_REPORT_ALL_REQS) || new_state != resource->active_state) { resource->active_state = resource->node->driver_fn(resource, client, new_state); resource->active_headroom = resource->active_max - resource->active_state; } RESOURCE_UNLOCK(resource); npa_log(NPA_LOG_MASK_CLIENT, resource, "NPA: Resource [%s] state set to [%u]\n", resource->definition->name, resource->active_state); publish_resource_state(resource); } /* Return if any dependency of this resource is still not active. * Returns NULL if there are no pending dependencies. */ static struct npa_node_dependency *get_first_pending_dependency( struct npa_resource *resource) { struct npa_node_dependency *dep = NULL; unsigned int count = 0; dep = resource->node->dependencies; count = resource->node->dependency_count; while (count) { if (!active_resource(dep->name)) return dep; count--; dep++; } return NULL; } /* Callback function for NPA_EVENT_RESERVED1. This function registers * itself with the event queue for the first pending dependency of the * resource. If there are no dependency, then this activates the resource * as well. */ static void resource_creation_handler(void *data, unsigned int type, void *event_data, unsigned int value) { struct npa_resource *resource = (struct npa_resource *)data; struct npa_node_dependency *dep = NULL; npa_log(NPA_LOG_MASK_RESOURCE, resource, "NPA: Resource [%s] creation handler called\n", resource->definition->name); dep = get_first_pending_dependency(resource); if (dep) { npa_log(NPA_LOG_MASK_RESOURCE, resource, "NPA: Resource [%s] has dependency on resource [%s]\n", resource->definition->name, dep->name); npa_resource_available(dep->name, resource_creation_handler, resource); return; } /* All dependencies are complete. Finish the resource completion. */ activate_node(resource); } static void resource_destruction_handler(void *data, unsigned int value, void *event_data, unsigned int type) { struct npa_resource *resource = data; BUG_ON(!list_empty(&resource->clients)); if (!list_empty(&resource->events)) { struct npa_event *event = NULL; struct npa_event *temp = NULL; list_for_each_entry_safe(event, temp, &resource->events, list) { list_del(&event->list); npa_log(NPA_LOG_MASK_EVENT, resource, "NPA: Resource [%s] sending " "event [%d] to [%s]\n", resource->definition->name, event->type, event->handler_name); event->callback(event->user_data, value, NULL, 0); kfree(event); } } BUG_ON(!list_empty(&resource->watermarks)); kfree(resource->definition); kfree(resource->node); kfree(resource); } /* Defines the node and individual resources. * All resources need to be defined by an external entity before the resource * can be used. This function checks for any pending dependency. This function * adds the callback to its event queue and when the resource is completely * defined, the callback is invoked. */ int npa_define_node(struct npa_node_definition *node, unsigned int initial_state[], npa_cb_fn callback, void *user_data) { struct npa_resource *resource = NULL; struct npa_resource_definition *res_def = NULL; struct npa_node_dependency *node_dep = NULL; struct mutex *common_lock = NULL; unsigned int count = 0; int ret = 0; int idx = 0; if (!node || !node->name[0] || !node->resources || !node->driver_fn) return -EINVAL; npa_log(NPA_LOG_MASK_RESOURCE, NULL, "NPA: Defining node: [%s]\n", node->name); common_lock = kzalloc(sizeof(struct mutex), GFP_KERNEL); mutex_init(common_lock); /* For each resource definition in this node definition, see if a * npa_resource object already exists, if not create and add to the * waiting list. We then check for dependencies and wait on those * using the first resource object that we have. Once the dependencies * are satisfied for this resource, we go ahead and activate all * the resources of this node. */ res_def = node->resources; count = node->resource_count; while (count) { BUG_ON(!res_def->plugin); resource = create_resource(node, res_def, res_def->name); BUG_ON(__is_active_resource(resource)); resource->definition = res_def; resource->node = node; resource->requested_state = initial_state[idx++]; resource->active_max = res_def->max; resource->active_plugin = res_def->plugin; resource->resource_lock = common_lock; res_def->resource = resource; npa_log(NPA_LOG_MASK_RESOURCE, resource, "NPA: Defining resource: [%s]\n", res_def->name); count--; res_def++; } /* Get the first resource in the node definition */ resource = node->resources[0].resource; /* Save the callback function to be called if the resource is ready.*/ ret = register_resource_event(resource, callback, user_data); if (ret) return ret; node_dep = get_first_pending_dependency(resource); if (node_dep) { npa_log(NPA_LOG_MASK_RESOURCE, resource, "NPA: Resource [%s] has dependency on resource [%s]\n", resource->definition->name, node_dep->name); return npa_resource_available(node_dep->name, resource_creation_handler, (void *)resource); } /* Only executed when there are no pending dependencies of this node.*/ activate_node(resource); return ret; } EXPORT_SYMBOL(npa_define_node); /* Checks if the resource is available in the NPA active or waiting lists. * If not, then creates a dummy resource and adds it to the waiting list. * When the resource is available, the callback function is executed when * the events are fired. */ int npa_resource_available(const char *resource_name, npa_cb_fn callback, void *user_data) { struct npa_resource *resource = NULL; if (!resource_name || !resource_name[0] || !callback) return -EINVAL; npa_log(NPA_LOG_MASK_RESOURCE, resource, "NPA: Checking if resource [%s] is available\n", resource_name); resource = create_resource(NULL, NULL, resource_name); read_lock(&list_lock); if (!__is_active_resource(resource)) { read_unlock(&list_lock); /* Register for NPA_EVENT_RESERVED1 event callback and exit */ return register_resource_event(resource, callback, user_data); } read_unlock(&list_lock); npa_log(NPA_LOG_MASK_RESOURCE, resource, "NPA: Resource [%s] is available\n", resource_name); /* Resource is active. Call in the current context. */ callback(user_data, resource->active_state, NULL, 0); return 0; } EXPORT_SYMBOL(npa_resource_available); /* Define an alias name for a resource. */ int npa_alias_resource(const char *resource_name, const char *alias_name, npa_cb_fn callback, void *user_data) { struct npa_resource *parent = NULL; struct npa_resource *resource = NULL; struct npa_alias_list *ralias = NULL; if (!resource_name || !resource_name[0] || !alias_name || !alias_name[0] || !callback) return -EINVAL; npa_log(NPA_LOG_MASK_RESOURCE, NULL, "NPA: Creating alias [%s] for resource [%s]\n", alias_name, resource_name); ralias = kzalloc(sizeof(struct npa_alias_list), GFP_KERNEL); parent = create_resource(NULL, NULL, resource_name); write_lock(&list_lock); resource = __get_resource(alias_name); if (resource) { struct npa_alias_list *alias = NULL; list_for_each_entry(alias, &alias_list, list) { if (alias->resource == resource) alias->resource = parent; } list_del(&resource->list); } else { INIT_LIST_HEAD(&ralias->list); ralias->alias = alias_name; ralias->resource = parent; list_add(&ralias->list, &alias_list); } write_unlock(&list_lock); read_lock(&list_lock); if (__is_active_resource(parent)) { read_unlock(&list_lock); if (resource) resource_destruction_handler(resource, parent->active_state, NULL, 0); callback(user_data, 0, NULL, 0); } else { read_unlock(&list_lock); register_resource_event(parent, callback, user_data); if (resource) register_resource_event(parent, resource_destruction_handler, resource); } #ifdef CONFIG_MSM_NPA_LOG /* If we were looking to log an alias, then re-acquire the resource* */ npa_log_resource = __get_resource(npa_log_resource_name); #endif npa_log(NPA_LOG_MASK_RESOURCE, parent, "NPA: Alias [%s] created for resource [%s]\n", alias_name, parent->definition->name); return 0; } EXPORT_SYMBOL(npa_alias_resource); /* Update the resource state with this state. The driver function is expected * to update the underlying h/w state or whatever this resource represents. * This function sends update events to the listeners. */ int npa_assign_resource_state(struct npa_resource *resource, unsigned int state) { npa_log(NPA_LOG_MASK_RESOURCE, resource, "NPA: Resource [%s] is assigned state [%u]\n", resource->definition->name, state); resource->active_state = state; resource->active_headroom = resource->active_max - state; publish_resource_state(resource); return 0; } EXPORT_SYMBOL(npa_assign_resource_state); /* Create a sync client for this resource. * Returns an handle to the NPA client object if the resource is active, * error condition otherwise. */ struct npa_client *npa_create_sync_client(const char *resource_name, const char *client_name, enum npa_client_type type) { struct npa_client *client = NULL; struct npa_resource *resource = NULL; int flag = 0; if (!resource_name || !resource_name[0] || !client_name || !client_name[0]) return ERR_PTR(-EINVAL); resource = active_resource(resource_name); if (!resource) { /* Clients for pending resources are not allowed. */ npa_log(NPA_LOG_MASK_CLIENT, resource, "NPA: Resource [%s] requested by " "client [%s] not active\n", resource_name, client_name); return ERR_PTR(-ENODEV); } RESOURCE_LOCK(resource); if (!(type & resource->active_plugin->supported_clients)) flag = -1; if ((resource->definition->attributes & NPA_RESOURCE_SINGLE_CLIENT) && !list_empty(&resource->clients)) flag = -2; RESOURCE_UNLOCK(resource); if (flag == -1) { npa_log(NPA_LOG_MASK_CLIENT, resource, "NPA: Client [%s, %d] is not " "supported by resource [%s]\n", client_name, type, resource_name); return ERR_PTR(-EINVAL); } if (flag == -2) { npa_log(NPA_LOG_MASK_CLIENT, resource, "NPA: Resource [%s] supports only a single" "client. Client [%s] create failed\n", resource_name, client_name); return ERR_PTR(-EBUSY); } npa_log(NPA_LOG_MASK_CLIENT, resource, "NPA: Creating sync client [%s] for " "resource [%s]\n", client_name, resource_name); client = kzalloc(sizeof(struct npa_client), GFP_KERNEL); INIT_LIST_HEAD(&client->list); client->name = client_name; client->resource_name = resource_name; client->resource = resource; client->type = type; RESOURCE_LOCK(resource); list_add(&client->list, &resource->clients); if (resource->active_plugin->create_client_fn) resource->active_plugin->create_client_fn(client); RESOURCE_UNLOCK(resource); npa_log(NPA_LOG_MASK_CLIENT, resource, "NPA: Client [%s, %d] for resource [%s] created\n", client_name, type, resource_name); return client; } EXPORT_SYMBOL(npa_create_sync_client); /* Removes a client from a resource. */ void npa_destroy_client(struct npa_client *client) { struct npa_resource *resource = NULL; if (client) { npa_log(NPA_LOG_MASK_CLIENT, client->resource, "NPA: Resource [%s] client [%s, %d] destroyed\n", client->resource->definition->name, client->name, client->type); resource = client->resource; RESOURCE_LOCK(resource); if (resource->active_plugin->destroy_client_fn) resource->active_plugin->destroy_client_fn(client); list_del(&client->list); RESOURCE_UNLOCK(resource); kfree(client); } } EXPORT_SYMBOL(npa_destroy_client); /* Issues a required workflow request call to the client. If the update function * returns a state different from the current resource state, then the resource * driver function is called in the same context to update the resource and the * update events will be scheduled to fire. */ int npa_issue_required_request(struct npa_client *client, unsigned int state) { if (client->type != NPA_CLIENT_REQUIRED) return -EINVAL; npa_log(NPA_LOG_MASK_CLIENT, client->resource, "NPA: Resource [%s] received request from client [%s, %u]\n", client->resource->definition->name, client->name, state); request_state(client->resource, client, state); return 0; } EXPORT_SYMBOL(npa_issue_required_request); int npa_modify_required_request(struct npa_client *client, int delta) { if (client->type != NPA_CLIENT_REQUIRED) return -EINVAL; /* Check if the active state of the client is non-zero. */ if (!ACTIVE_STATE(client)) return -EINVAL; npa_log(NPA_LOG_MASK_CLIENT, client->resource, "NPA: Resource [%s] modify request by client [%s, %d]\n", client->resource->definition->name, client->name, delta); request_state(client->resource, client, ACTIVE_STATE(client) + delta); return 0; } EXPORT_SYMBOL(npa_modify_required_request); int npa_issue_impulse_request(struct npa_client *client) { if (client->type != NPA_CLIENT_IMPULSE) return -EINVAL; npa_log(NPA_LOG_MASK_CLIENT, client->resource, "NPA: Resource [%s] impulse request by client [%s]\n", client->resource->definition->name, client->name); request_state(client->resource, client, client->resource->definition->max); return 0; } EXPORT_SYMBOL(npa_issue_impulse_request); int npa_issue_isoc_request(struct npa_client *client, unsigned int duration, unsigned int level) { if (client->type != NPA_CLIENT_ISOCHRONOUS) return -EINVAL; npa_log(NPA_LOG_MASK_CLIENT, client->resource, "NPA: Resource [%s] isoc request by " "client [%s] with duration [%u], level [%u]\n", client->resource->definition->name, client->name, duration, level); /* TODO: Update work struct with time values */ client->work[PENDING_REQUEST].state = level; request_state(client->resource, client, PENDING_STATE(client)); return 0; } EXPORT_SYMBOL(npa_issue_isoc_request); int npa_issue_limit_max_request(struct npa_client *client, unsigned int max) { int val = max; struct npa_resource *resource = client->resource; struct npa_client *cl_itr = NULL; if (client->type != NPA_CLIENT_LIMIT_MAX) return -EINVAL; npa_log(NPA_LOG_MASK_CLIENT, client->resource, "NPA: Resource [%s] limit max [%u] request by client [%s] \n", client->resource->definition->name, max, client->name); RESOURCE_LOCK(resource); list_for_each_entry(cl_itr, &resource->clients, list) { if ((cl_itr->type & NPA_CLIENT_LIMIT_MAX)) if (ACTIVE_STATE(cl_itr) && (ACTIVE_STATE(cl_itr) < val)) val = ACTIVE_STATE(cl_itr); } resource->active_max = val; RESOURCE_UNLOCK(resource); /* Issue the request to the update_fn and limit the current state if * needed and call the driver fn. Send events if resource state is * changed. */ request_state(client->resource, client, max); return 0; } EXPORT_SYMBOL(npa_issue_limit_max_request); void npa_complete_request(struct npa_client *client) { npa_log(NPA_LOG_MASK_CLIENT, client->resource, "NPA: Client [%s] completed request\n", client->name); /* Complete this request. */ request_state(client->resource, client, 0); } EXPORT_SYMBOL(npa_complete_request); void npa_cancel_request(struct npa_client *client) { npa_log(NPA_LOG_MASK_CLIENT, client->resource, "NPA: Client [%s] cancelled request\n", client->name); /* TODO: In future flush any history for this client that may have * been captured. And complete this request. */ request_state(client->resource, client, 0); } EXPORT_SYMBOL(npa_cancel_request); unsigned int npa_get_state(struct npa_client *client) { return client->resource->active_state; } EXPORT_SYMBOL(npa_get_state); /* Create an event object and attach to the resource event list. * This function expects that the resource be active for the event to * be registered with the resource. */ struct npa_event *npa_create_change_event(const char *resource_name, const char *handler_name, npa_cb_fn event_cb, void *user_data) { struct npa_event *event = NULL; struct npa_resource *resource = NULL; if (!resource_name || !resource_name[0] || !handler_name || !event_cb) return ERR_PTR(-EINVAL); resource = active_resource(resource_name); if (!resource) return ERR_PTR(-ENODEV); event = kzalloc(sizeof(struct npa_event), GFP_KERNEL); INIT_LIST_HEAD(&event->list); INIT_WORK(&event->work, send_single_event); event->type = NPA_EVENT_CHANGE; event->handler_name = handler_name; event->resource = resource; event->callback = event_cb; event->user_data = user_data; RESOURCE_LOCK(resource); list_add(&event->list, &resource->events); RESOURCE_UNLOCK(resource); npa_log(NPA_LOG_MASK_EVENT, resource, "NPA: Resource [%s] adding change event for [%s]\n", resource->definition->name, handler_name); /* Callback with the current resource state only for this event */ queue_work(npa_wq, &event->work); return event; } EXPORT_SYMBOL(npa_create_change_event); struct npa_event *npa_create_watermark_event(const char *resource_name, const char *handler_name, npa_cb_fn event_cb, void *user_data) { struct npa_event *event = NULL; struct npa_resource *resource = NULL; if (!resource_name || !resource_name[0] || !handler_name || !event_cb) return ERR_PTR(-EINVAL); resource = active_resource(resource_name); if (!resource) return ERR_PTR(-ENODEV); event = kzalloc(sizeof(struct npa_event), GFP_KERNEL); INIT_LIST_HEAD(&event->list); INIT_WORK(&event->work, send_single_event); event->type = NPA_EVENT_LO_WATERMARK; event->handler_name = handler_name; event->resource = resource; event->callback = event_cb; event->user_data = user_data; RESOURCE_LOCK(resource); list_add(&event->list, &resource->watermarks); RESOURCE_UNLOCK(resource); npa_log(NPA_LOG_MASK_EVENT, resource, "NPA: Resource [%s] adding watermark events for [%s]\n", resource->definition->name, handler_name); return event; } EXPORT_SYMBOL(npa_create_watermark_event); /* Removes an event from the event queue of this resource. */ void npa_destroy_event(struct npa_event *event) { /* TODO BUG: Possible race condition with send-events(), event * list being modified outside. */ RESOURCE_LOCK(event->resource); list_del(&event->list); RESOURCE_UNLOCK(event->resource); npa_log(NPA_LOG_MASK_EVENT, event->resource, "NPA: Resource [%s] event [%s] destroyed\n", event->resource->definition->name, event->handler_name); kfree(event); } EXPORT_SYMBOL(npa_destroy_event); /* Set watermarks for this event. The events are fired only if the resource * state lie outside the watermarks. */ int npa_set_event_watermarks(struct npa_event *event, int lo_watermark, int hi_watermark) { unsigned int state; int publish_event = 0; if (!event) return -EINVAL; RESOURCE_LOCK(event->resource); state = event->resource->active_state; event->lo_watermark = lo_watermark; event->hi_watermark = hi_watermark; if (state <= event->lo_watermark) { event->type = NPA_EVENT_LO_WATERMARK; publish_event = 1; } if (state >= event->hi_watermark) { event->type = NPA_EVENT_HI_WATERMARK; publish_event = 1; } RESOURCE_UNLOCK(event->resource); npa_log(NPA_LOG_MASK_EVENT, event->resource, "NPA: Resource [%s] event [%s] watermarks [%d, %d] set\n", event->resource->definition->name, event->handler_name, lo_watermark, hi_watermark); if (publish_event) queue_work(npa_wq, &event->work); return 0; } EXPORT_SYMBOL(npa_set_event_watermarks); /* Pre-defined NPA update functions. * Currently we only consider "required" client requests. */ static unsigned int binary_update(struct npa_resource *resource, struct npa_client *client) { struct npa_client *cl_itr = NULL; unsigned int val = PENDING_STATE(client); __print_client_states(resource); if (!(client->type & NPA_CLIENT_REQUIRED)) return resource->active_state; if ((resource->active_state == ACTIVE_STATE(client)) && (ACTIVE_STATE(client) != val)) return val; list_for_each_entry(cl_itr, &resource->clients, list) { if ((cl_itr->type & NPA_CLIENT_REQUIRED) && (cl_itr != client)) val |= ACTIVE_STATE(cl_itr); } npa_log(NPA_LOG_MASK_PLUGIN, resource, "NPA: Binary plugin: Calculated [%u] for resource [%s] " "client [%s]\n", val, resource->definition->name, client->name); return val; } static unsigned int min_update(struct npa_resource *resource, struct npa_client *client) { struct npa_client *cl_itr = NULL; unsigned int val = PENDING_STATE(client); __print_client_states(resource); if (!(client->type & NPA_CLIENT_REQUIRED)) return resource->active_state; if (val && (resource->active_state == ACTIVE_STATE(client)) && (val <= ACTIVE_STATE(client))) { npa_log(NPA_LOG_MASK_PLUGIN, resource, "NPA: Min plugin: Current request [%u] is min for " "resource [%s]\n", val, resource->definition->name); return val; } list_for_each_entry(cl_itr, &resource->clients, list) { if (!(cl_itr->type & NPA_CLIENT_REQUIRED)) continue; if ((cl_itr != client) && ACTIVE_STATE(cl_itr)) { if (!val) val = ACTIVE_STATE(cl_itr); if (ACTIVE_STATE(cl_itr) < val) val = ACTIVE_STATE(cl_itr); } } npa_log(NPA_LOG_MASK_PLUGIN, resource, "NPA: Min plugin: Calculated [%u] for resource [%s] " "client [%s]\n", val, resource->definition->name, client->name); return val; } static unsigned int max_update(struct npa_resource *resource, struct npa_client *client) { struct npa_client *cl_itr = NULL; unsigned int val = PENDING_STATE(client); __print_client_states(resource); if (!(client->type & NPA_CLIENT_REQUIRED)) return resource->active_state; if ((val != 0) && (resource->active_state == ACTIVE_STATE(client)) && (val >= ACTIVE_STATE(client))) { npa_log(NPA_LOG_MASK_PLUGIN, resource, "NPA: Max plugin: Current request [%u] is max for " "resource [%s]\n", val, resource->definition->name); return val; } list_for_each_entry(cl_itr, &resource->clients, list) { if (!(cl_itr->type & NPA_CLIENT_REQUIRED)) continue; if ((cl_itr != client) && ACTIVE_STATE(cl_itr)) { if (!val) val = ACTIVE_STATE(cl_itr); if (ACTIVE_STATE(cl_itr) > val) val = ACTIVE_STATE(cl_itr); } } npa_log(NPA_LOG_MASK_PLUGIN, resource, "NPA: Max plugin: Calculated [%u] for resource [%s] " "client [%s]\n", val, resource->definition->name, client->name); return val; } static unsigned int sum_update(struct npa_resource *resource, struct npa_client *client) { unsigned int val = resource->active_state; __print_client_states(resource); if (!(client->type & NPA_CLIENT_REQUIRED)) return resource->active_state; val -= ACTIVE_STATE(client); val += PENDING_STATE(client); npa_log(NPA_LOG_MASK_PLUGIN, resource, "NPA: Sum plugin: Calculated [%u] for resource [%s] " "client [%s]\n", val, resource->definition->name, client->name); return val; } static unsigned int always_update(struct npa_resource *resource, struct npa_client *client) { unsigned int val = 1; __print_client_states(resource); if (!(client->type & NPA_CLIENT_REQUIRED)) return resource->active_state; npa_log(NPA_LOG_MASK_PLUGIN, resource, "NPA: Always on plugin: Calculated [%u] for resource [%s] " "client [%s]\n", val, resource->definition->name, client->name); return val; } const struct npa_resource_plugin_ops npa_binary_plugin = { .update_fn = binary_update, .supported_clients = NPA_CLIENT_REQUIRED, .create_client_fn = NULL, .destroy_client_fn = NULL, }; EXPORT_SYMBOL(npa_binary_plugin); const struct npa_resource_plugin_ops npa_min_plugin = { .update_fn = min_update, .supported_clients = NPA_CLIENT_REQUIRED, .create_client_fn = NULL, .destroy_client_fn = NULL, }; EXPORT_SYMBOL(npa_min_plugin); const struct npa_resource_plugin_ops npa_max_plugin = { .update_fn = max_update, .supported_clients = NPA_CLIENT_REQUIRED, .create_client_fn = NULL, .destroy_client_fn = NULL, }; EXPORT_SYMBOL(npa_max_plugin); const struct npa_resource_plugin_ops npa_sum_plugin = { .update_fn = sum_update, .supported_clients = NPA_CLIENT_REQUIRED, .create_client_fn = NULL, .destroy_client_fn = NULL, }; EXPORT_SYMBOL(npa_sum_plugin); const struct npa_resource_plugin_ops npa_always_on_plugin = { .update_fn = always_update, .supported_clients = NPA_CLIENT_REQUIRED, .create_client_fn = NULL, .destroy_client_fn = NULL, }; EXPORT_SYMBOL(npa_always_on_plugin); /* NPA Init function. */ static int npa_init(void) { npa_wq = create_workqueue("npa"); BUG_ON(!npa_wq); pr_info("NPA: Init done.\n"); return 0; } #ifdef CONFIG_MSM_NPA_DEBUG /* ** DEBUG use only ** * NPA reset function. Releases all resources and resource states. * This function should be called only when there all the clients and events * are destroyed. No request should be made during this call. */ void npa_reset(void) { struct npa_alias_list *a = NULL; struct npa_alias_list *b = NULL; struct npa_alias_list *temp_a = NULL; struct npa_event *e = NULL; struct npa_event *temp_e = NULL; struct npa_client *c = NULL; struct npa_client *temp_c = NULL; struct npa_resource *resource = NULL; struct npa_resource_definition *def = NULL; unsigned int count = 0; write_lock(&list_lock); list_for_each_entry_safe(a, temp_a, &alias_list, list) { list_del(&a->list); kfree(a); if (!resource) continue; resource = a->resource; list_for_each_entry_safe(e, temp_e, &resource->events, list) { list_del(&e->list); kfree(e); } list_for_each_entry_safe(e, temp_e, &resource->watermarks, list){ list_del(&e->list); kfree(e); } list_for_each_entry_safe(c, temp_c, &resource->clients, list) { list_del(&c->list); kfree(c); } if (!__is_active_resource(resource)) { kfree(resource->definition); kfree(resource->node); } list_for_each_entry(b, &alias_list, list) { if (b->resource == resource) b->resource = NULL; } resource->definition->resource = NULL; kfree(resource->resource_lock); def = resource->node->resources; count = resource->node->resource_count; while (count) { if (def->resource) def->resource->resource_lock = NULL; count--; def++; } kfree(resource); } write_unlock(&list_lock); } EXPORT_SYMBOL(npa_reset); #endif /* NPA needs to be made available before the resources. Should be way up in the * initialization list of the kernel. */ postcore_initcall(npa_init);