diff options
Diffstat (limited to 'drivers/dsp/syslink/multicore_ipc/gatehwspinlock.c')
-rw-r--r-- | drivers/dsp/syslink/multicore_ipc/gatehwspinlock.c | 563 |
1 files changed, 563 insertions, 0 deletions
diff --git a/drivers/dsp/syslink/multicore_ipc/gatehwspinlock.c b/drivers/dsp/syslink/multicore_ipc/gatehwspinlock.c new file mode 100644 index 00000000000..57b53ad050e --- /dev/null +++ b/drivers/dsp/syslink/multicore_ipc/gatehwspinlock.c @@ -0,0 +1,563 @@ +/* + * gatehwspinlock.c + * + * Hardware-based spinlock gate for mutual exclusion of shared memory. + * + * Copyright (C) 2008-2009 Texas Instruments, Inc. + * + * This package is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE. + */ + +#include <linux/module.h> +#include <linux/types.h> +#include <linux/string.h> +#include <linux/list.h> +#include <linux/mutex.h> +#include <linux/slab.h> +#include <plat/hwspinlock.h> + +#include <syslink/atomic_linux.h> +#include <multiproc.h> +#include <sharedregion.h> +#include <gatemp.h> +#include <igatempsupport.h> +#include <igateprovider.h> +#include <iobject.h> +#include <gatehwspinlock.h> + + +/* ============================================================================= + * Macros + * ============================================================================= + */ + +/* Macro to make a correct module magic number with refCount */ +#define GATEHWSPINLOCK_MAKE_MAGICSTAMP(x) ((GATEHWSPINLOCK_MODULEID << 12u) \ + | (x)) +/* + * structure for gatehwspinlock module state + */ +struct gatehwspinlock_module_object { + atomic_t ref_count; /* Reference count */ + struct gatehwspinlock_config cfg; + struct gatehwspinlock_config default_cfg; + struct gatehwspinlock_params def_inst_params; /* default instance + paramters */ + u32 *base_addr; /* Base address of lock registers */ + u32 num_locks; /* Maximum number of locks */ + u32 num_reserved; /* Number of reserved locks */ + struct hwspinlock **hw_lock_handles; + /* Array of hwspinlock handles controlled by gatemp */ +}; + +/* + * Structure defining object for the Gate Spinlock + */ +struct gatehwspinlock_object { + IGATEPROVIDER_SUPEROBJECT; /* For inheritance from IGateProvider */ + IOBJECT_SUPEROBJECT; /* For inheritance for IObject */ + u32 lock_num; + u32 nested; + void *local_gate; + struct hwspinlock *hwhandle; +}; + +/* + * Variable for holding state of the gatehwspinlock module + */ +static struct gatehwspinlock_module_object gatehwspinlock_state = { + .default_cfg.default_protection = \ + gatehwspinlock_LOCALPROTECT_INTERRUPT, + .default_cfg.num_locks = 32u, + .def_inst_params.shared_addr = NULL, + .def_inst_params.resource_id = 0x0, + .def_inst_params.region_id = 0x0, + .hw_lock_handles = NULL, + .num_locks = 32u, + .num_reserved = 5 + /* GateMP controls SpinLocks 5-31. 0-3 are reserved for usage by I2C */ + /* SpinLock 4 is for ipu_pm module usage */ +}; + +static struct gatehwspinlock_module_object *gatehwspinlock_module = + &gatehwspinlock_state; + +/* ============================================================================= + * Internal functions + * ============================================================================= + */ + +/* TODO: figure these out */ +#define gate_enter_system() (int *)0 +#define gate_leave_system(key) {} + +/* ============================================================================= + * APIS + * ============================================================================= + */ +/* + * ======== gatehwspinlock_get_config ======== + * Purpose: + * This will get the default configuration parameters for gatehwspinlock + * module + */ +void gatehwspinlock_get_config(struct gatehwspinlock_config *config) +{ + int *key = NULL; + + if (WARN_ON(config == NULL)) + goto exit; + + key = gate_enter_system(); + if (atomic_cmpmask_and_lt(&(gatehwspinlock_module->ref_count), + GATEHWSPINLOCK_MAKE_MAGICSTAMP(0), + GATEHWSPINLOCK_MAKE_MAGICSTAMP(1)) == true) + memcpy(config, &gatehwspinlock_module->default_cfg, + sizeof(struct gatehwspinlock_config)); + else + memcpy(config, &gatehwspinlock_module->cfg, + sizeof(struct gatehwspinlock_config)); + gate_leave_system(key); + +exit: + return; +} +EXPORT_SYMBOL(gatehwspinlock_get_config); + +/* + * ======== gatehwspinlock_setup ======== + * Purpose: + * This will setup the gatehwspinlock module + */ +int gatehwspinlock_setup(const struct gatehwspinlock_config *config) +{ + struct gatehwspinlock_config tmp_cfg; + int *key = NULL; + struct hwspinlock *lock_handle; + int i; + s32 retval; + + key = gate_enter_system(); + + /* This sets the ref_count variable not initialized, upper 16 bits is + * written with module _id to ensure correctness of ref_count variable + */ + atomic_cmpmask_and_set(&gatehwspinlock_module->ref_count, + GATEHWSPINLOCK_MAKE_MAGICSTAMP(0), + GATEHWSPINLOCK_MAKE_MAGICSTAMP(0)); + + if (atomic_inc_return(&gatehwspinlock_module->ref_count) + != GATEHWSPINLOCK_MAKE_MAGICSTAMP(1)) { + gate_leave_system(key); + return 1; + } + + if (config == NULL) { + gatehwspinlock_get_config(&tmp_cfg); + config = &tmp_cfg; + } + gate_leave_system(key); + + gatehwspinlock_module->hw_lock_handles = kzalloc( + gatehwspinlock_module->num_locks * sizeof(struct hwspinlock *), + GFP_KERNEL); + if (gatehwspinlock_module->hw_lock_handles == NULL) { + retval = -ENOMEM; + goto exit; + } + + memcpy(&gatehwspinlock_module->cfg, config, + sizeof(struct gatehwspinlock_config)); + + gatehwspinlock_module->base_addr = (void *)config->base_addr; + gatehwspinlock_module->num_locks = config->num_locks; + for (i = gatehwspinlock_module->num_reserved; + i < gatehwspinlock_module->num_locks; i++) { + lock_handle = hwspinlock_request_specific(i); + if (lock_handle == NULL) { + retval = -EBUSY; + pr_err("hwspinlock_request failed for" + "id = %d", i); + goto spinlock_request_fail; + } + gatehwspinlock_module->hw_lock_handles[i] = lock_handle; + } + return 0; + +spinlock_request_fail: + for (i--; i >= gatehwspinlock_module->num_reserved; i--) { + hwspinlock_free(gatehwspinlock_module->hw_lock_handles[i]); + gatehwspinlock_module->hw_lock_handles[i] = NULL; + } +exit: + kfree(gatehwspinlock_module->hw_lock_handles); + atomic_dec_return(&gatehwspinlock_module->ref_count); + if (retval < 0) + pr_err("gatehwspinlock_setup failed! status = 0x%x", retval); + return retval; +} +EXPORT_SYMBOL(gatehwspinlock_setup); + +/* + * ======== gatehwspinlock_destroy ======== + * Purpose: + * This will destroy the gatehwspinlock module + */ +int gatehwspinlock_destroy(void) +{ + s32 retval = 0; + int *key = NULL; + struct hwspinlock *lock_handle; + int i; + + key = gate_enter_system(); + + if (atomic_cmpmask_and_lt(&(gatehwspinlock_module->ref_count), + GATEHWSPINLOCK_MAKE_MAGICSTAMP(0), + GATEHWSPINLOCK_MAKE_MAGICSTAMP(1)) == true) { + retval = -ENODEV; + goto exit; + } + + if (!(atomic_dec_return(&gatehwspinlock_module->ref_count) + == GATEHWSPINLOCK_MAKE_MAGICSTAMP(0))) { + gate_leave_system(key); + retval = 1; + goto exit; + } + gate_leave_system(key); + + for (i = gatehwspinlock_module->num_reserved; + i < gatehwspinlock_module->num_locks; i++) { + lock_handle = gatehwspinlock_module->hw_lock_handles[i]; + retval = hwspinlock_free(lock_handle); + if (retval < 0) + pr_err("hwspinlock_free failed for id = %d", i); + gatehwspinlock_module->hw_lock_handles[i] = NULL; + } + memset(&gatehwspinlock_module->cfg, 0, + sizeof(struct gatehwspinlock_config)); + kfree(gatehwspinlock_module->hw_lock_handles); + gatehwspinlock_module->hw_lock_handles = NULL; + return 0; + +exit: + if (retval < 0) + pr_err("gatehwspinlock_destroy failed status:%x\n", retval); + return retval; +} +EXPORT_SYMBOL(gatehwspinlock_destroy); + +/* + * ======== gatehwspinlock_get_num_instances ======== + * Purpose: + * Function to return the number of instances configured in the module. + */ +u32 gatehwspinlock_get_num_instances(void) +{ + return gatehwspinlock_module->num_locks; +} +EXPORT_SYMBOL(gatehwspinlock_get_num_instances); + +/* + * ======== gatehwspinlock_get_num_reserved ======== + * Purpose: + * Function to return the number of instances not under GateMP's control. + */ +u32 gatehwspinlock_get_num_reserved(void) +{ + return gatehwspinlock_module->num_reserved; +} +EXPORT_SYMBOL(gatehwspinlock_get_num_reserved); + +/* + * ======== gatepeterson_locks_init ======== + * Purpose: + * Function to initialize the locks. + */ +void gatehwspinlock_locks_init(void) +{ + u32 i; + + for (i = 0; i < gatehwspinlock_module->num_locks; i++) + gatehwspinlock_module->base_addr[i] = 0; +} +EXPORT_SYMBOL(gatehwspinlock_locks_init); + +/* + * ======== gatehwspinlock_params_init ======== + * Purpose: + * This will Initialize this config-params structure with + * supplier-specified defaults before instance creation + */ +void gatehwspinlock_params_init(struct gatehwspinlock_params *params) +{ + int *key = NULL; + + key = gate_enter_system(); + + if (WARN_ON(atomic_cmpmask_and_lt(&(gatehwspinlock_module->ref_count), + GATEHWSPINLOCK_MAKE_MAGICSTAMP(0), + GATEHWSPINLOCK_MAKE_MAGICSTAMP(1)) == true)) + goto exit; + if (WARN_ON(params == NULL)) + goto exit; + + gate_leave_system(key); + memcpy(params, &(gatehwspinlock_module->def_inst_params), + sizeof(struct gatehwspinlock_params)); + return; + +exit: + gate_leave_system(key); + return; +} +EXPORT_SYMBOL(gatehwspinlock_params_init); + +/* + * ======== gatehwspinlock_create ======== + * Purpose: + * This will creates a new instance of gatehwspinlock module + */ +void *gatehwspinlock_create(enum igatempsupport_local_protect local_protect, + const struct gatehwspinlock_params *params) +{ + void *handle = NULL; + struct gatehwspinlock_object *obj = NULL; + s32 retval = 0; + + if (atomic_cmpmask_and_lt(&(gatehwspinlock_module->ref_count), + GATEHWSPINLOCK_MAKE_MAGICSTAMP(0), + GATEHWSPINLOCK_MAKE_MAGICSTAMP(1)) == true) { + retval = -ENODEV; + goto exit; + } + if (WARN_ON(params == NULL)) { + retval = -EINVAL; + goto exit; + } + if (WARN_ON(params->shared_addr == NULL)) { + retval = -EINVAL; + goto exit; + } + + obj = kzalloc(sizeof(struct gatehwspinlock_object), GFP_KERNEL); + if (obj == NULL) { + retval = -ENOMEM; + goto exit; + } + + IGATEPROVIDER_OBJECTINITIALIZER(obj, gatehwspinlock); + + /* Create the local gate */ + obj->local_gate = + gatemp_create_local((enum gatemp_local_protect)local_protect); + if (obj->local_gate == NULL) { + retval = GATEHWSPINLOCK_E_FAIL; + goto free_obj; + } + + obj->lock_num = params->resource_id; + obj->nested = 0; + obj->hwhandle = \ + gatehwspinlock_module->hw_lock_handles[params->resource_id]; + if (obj->hwhandle == NULL) { + retval = -EBUSY; + pr_err("hwspinlock_request failed for id = %d", + params->resource_id); + goto free_obj; + } + handle = obj; + return handle; + +free_obj: + kfree(obj); +exit: + pr_err("gatehwspinlock_create failed status: %x\n", retval); + return NULL; +} +EXPORT_SYMBOL(gatehwspinlock_create); + +/* + * ======== gatehwspinlock_delete ======== + * Purpose: + * This will deletes an instance of gatehwspinlock module + */ +int gatehwspinlock_delete(void **gphandle) + +{ + struct gatehwspinlock_object *obj = NULL; + s32 retval; + + if (atomic_cmpmask_and_lt(&(gatehwspinlock_module->ref_count), + GATEHWSPINLOCK_MAKE_MAGICSTAMP(0), + GATEHWSPINLOCK_MAKE_MAGICSTAMP(1)) == true) { + retval = -ENODEV; + goto exit; + } + if (WARN_ON(gphandle == NULL)) { + retval = -EINVAL; + goto exit; + } + if (WARN_ON(*gphandle == NULL)) { + retval = -EINVAL; + goto exit; + } + + obj = (struct gatehwspinlock_object *)(*gphandle); + + /* No need to delete the local gate, as it is gatemp module wide + * local mutex. */ + obj->hwhandle = NULL; + kfree(obj); + *gphandle = NULL; + + return 0; + +exit: + pr_err("gatehwspinlock_delete failed status: %x\n", retval); + return retval; +} +EXPORT_SYMBOL(gatehwspinlock_delete); + + +/* + * ======== gatehwspinlock_enter ======== + * Purpose: + * This will enters the gatehwspinlock instance + */ +int *gatehwspinlock_enter(void *gphandle) +{ + struct gatehwspinlock_object *obj = NULL; + s32 retval = 0; + int *key = NULL; + + if (WARN_ON(atomic_cmpmask_and_lt(&(gatehwspinlock_module->ref_count), + GATEHWSPINLOCK_MAKE_MAGICSTAMP(0), + GATEHWSPINLOCK_MAKE_MAGICSTAMP(1)) == true)) { + retval = -ENODEV; + goto exit; + } + if (WARN_ON(gphandle == NULL)) { + retval = -EINVAL; + goto exit; + } + + obj = (struct gatehwspinlock_object *)gphandle; + + /* Enter local gate */ + if (obj->local_gate != NULL) { + retval = mutex_lock_interruptible( + (struct mutex *)obj->local_gate); + if (retval) + goto exit; + } + + /* If the gate object has already been entered, return the nested + * value */ + obj->nested++; + if (obj->nested > 1) + return key; + + /* Enter the spinlock */ + retval = hwspinlock_lock(obj->hwhandle); + if (retval < 0) { + obj->nested--; + mutex_unlock((struct mutex *)obj->local_gate); + } + +exit: + if (retval < 0) + pr_err("gatehwspinlock_enter failed! status = 0x%x", retval); + return key; +} +EXPORT_SYMBOL(gatehwspinlock_enter); + +/* + * ======== gatehwspinlock_leave ======== + * Purpose: + * This will leaves the gatehwspinlock instance + */ +void gatehwspinlock_leave(void *gphandle, int *key) +{ + struct gatehwspinlock_object *obj = NULL; + s32 retval = 0; + + if (WARN_ON(atomic_cmpmask_and_lt(&(gatehwspinlock_module->ref_count), + GATEHWSPINLOCK_MAKE_MAGICSTAMP(0), + GATEHWSPINLOCK_MAKE_MAGICSTAMP(1)) == true)) { + retval = -ENODEV; + goto exit; + } + if (WARN_ON(gphandle == NULL)) { + retval = -EINVAL; + goto exit; + } + + obj = (struct gatehwspinlock_object *)gphandle; + obj->nested--; + /* Leave the spinlock if the leave() is not nested */ + if (obj->nested == 0) { + retval = hwspinlock_unlock(obj->hwhandle); + if (retval < 0) { + obj->nested++; + goto exit; + } + } + /* Leave local gate */ + mutex_unlock(obj->local_gate); + +exit: + if (retval < 0) + pr_err("gatehwspinlock_leave failed! status = 0x%x", retval); + return; +} +EXPORT_SYMBOL(gatehwspinlock_leave); + +/* + * ======== gatehwspinlock_get_resource_id ======== + */ +static u32 gatehwspinlock_get_resource_id(void *handle) +{ + struct gatehwspinlock_object *obj = NULL; + s32 retval = 0; + + if (WARN_ON(atomic_cmpmask_and_lt(&(gatehwspinlock_module->ref_count), + GATEHWSPINLOCK_MAKE_MAGICSTAMP(0), + GATEHWSPINLOCK_MAKE_MAGICSTAMP(1)) == true)) { + retval = -ENODEV; + goto exit; + } + if (WARN_ON(handle == NULL)) { + retval = -EINVAL; + goto exit; + } + + obj = (struct gatehwspinlock_object *)handle; + + return obj->lock_num; + +exit: + pr_err("gatehwspinlock_get_resource_id failed status: %x\n", retval); + return (u32)-1; +} +EXPORT_SYMBOL(gatehwspinlock_get_resource_id); + +/* + * ======== gatehwspinlock_shared_memreq ======== + * Purpose: + * This will give the amount of shared memory required + * for creation of each instance + */ +u32 gatehwspinlock_shared_mem_req(const struct gatehwspinlock_params *params) +{ + return 0; +} +EXPORT_SYMBOL(gatehwspinlock_shared_mem_req); |