diff options
Diffstat (limited to 'drivers/video/b2r2/b2r2_core.c')
-rw-r--r-- | drivers/video/b2r2/b2r2_core.c | 699 |
1 files changed, 315 insertions, 384 deletions
diff --git a/drivers/video/b2r2/b2r2_core.c b/drivers/video/b2r2/b2r2_core.c index ed5f52598da..02071d5f989 100644 --- a/drivers/video/b2r2/b2r2_core.c +++ b/drivers/video/b2r2/b2r2_core.c @@ -48,73 +48,18 @@ #include <linux/regulator/consumer.h> #include <linux/slab.h> #include <linux/err.h> +#include <linux/kref.h> #include "b2r2_internal.h" #include "b2r2_core.h" #include "b2r2_global.h" #include "b2r2_structures.h" -#include "b2r2_internal.h" +#include "b2r2_control.h" #include "b2r2_profiler_api.h" #include "b2r2_timing.h" #include "b2r2_debug.h" /** - * B2R2_DRIVER_TIMEOUT_VALUE - Busy loop timeout after soft reset - */ -#define B2R2_DRIVER_TIMEOUT_VALUE (1500) - -/** - * B2R2_CLK_FLAG - Value to write into clock reg to turn clock on - */ -#define B2R2_CLK_FLAG (0x125) - -/** - * DEBUG_CHECK_ADDREF_RELEASE - Define this to enable addref / release debug - */ -#define DEBUG_CHECK_ADDREF_RELEASE 1 - -#ifdef CONFIG_DEBUG_FS -/** - * HANDLE_TIMEOUTED_JOBS - Define this to check jobs for timeout and cancel them - */ -#define HANDLE_TIMEOUTED_JOBS 1 -#endif - -/** - * B2R2_CLOCK_ALWAYS_ON - Define this to disable power save clock turn off - */ -/* #define B2R2_CLOCK_ALWAYS_ON 1 */ - -/** - * START_SENTINEL - Watch guard to detect job overwrites - */ -#define START_SENTINEL 0xBABEDEEA - -/** - * STOP_SENTINEL - Watch guard to detect job overwrites - */ -#define END_SENTINEL 0xDADBDCDD - -/** - * B2R2_CORE_LOWEST_PRIO - Lowest prio allowed - */ -#define B2R2_CORE_LOWEST_PRIO -19 -/** - * B2R2_CORE_HIGHEST_PRIO - Highest prio allowed - */ -#define B2R2_CORE_HIGHEST_PRIO 20 - -/** - * B2R2_DOMAIN_DISABLE - - */ -#define B2R2_DOMAIN_DISABLE_TIMEOUT (HZ/100) - -/** - * B2R2_REGULATOR_RETRY_COUNT - - */ -#define B2R2_REGULATOR_RETRY_COUNT 10 - -/** * B2R2 Hardware defines below */ @@ -179,134 +124,6 @@ #define B2R2BLT_STA1AccessType (INITIAL_TEST) #define B2R2BLT_STA1 (0xa08) - -#ifdef DEBUG_CHECK_ADDREF_RELEASE - -/** - * struct addref_release - Represents one addref or release. Used - * to debug addref / release problems - * - * @addref: true if this represents an addref else it represents - * a release. - * @job: The job that was referenced - * @caller: The caller of the addref or release - * @ref_count: The job reference count after addref / release - */ -struct addref_release { - bool addref; - struct b2r2_core_job *job; - const char *caller; - int ref_count; -}; - -#endif - -/** - * struct b2r2_core - Administration data for B2R2 core - * - * @lock: Spin lock protecting the b2r2_core structure and the B2R2 HW - * @hw: B2R2 registers memory mapped - * @pmu_b2r2_clock: Control of B2R2 clock - * @log_dev: Device used for logging via dev_... functions - * - * @prio_queue: Queue of jobs sorted in priority order - * @active_jobs: Array containing pointer to zero or one job per queue - * @n_active_jobs: Number of active jobs - * @jiffies_last_active: jiffie value when adding last active job - * @jiffies_last_irq: jiffie value when last irq occured - * @timeout_work: Work structure for timeout work - * - * @next_job_id: Contains the job id that will be assigned to the next - * added job. - * - * @clock_request_count: When non-zero, clock is on - * @clock_off_timer: Kernel timer to handle delayed turn off of clock - * - * @work_queue: Work queue to handle done jobs (callbacks) and timeouts in - * non-interrupt context. - * - * @stat_n_irq: Number of interrupts (statistics) - * @stat_n_jobs_added: Number of jobs added (statistics) - * @stat_n_jobs_removed: Number of jobs removed (statistics) - * @stat_n_jobs_in_prio_list: Number of jobs in prio list (statistics) - * - * @debugfs_root_dir: Root directory for B2R2 debugfs - * - * @ar: Circular array of addref / release debug structs - * @ar_write: Where next write will occur - * @ar_read: First valid place to read. When ar_read == ar_write then - * the array is empty. - */ -struct b2r2_core { - spinlock_t lock; - - struct b2r2_memory_map *hw; - - u8 op_size; - u8 ch_size; - u8 pg_size; - u8 mg_size; - u16 min_req_time; - int irq; - - char name[16]; - struct device *dev; - - struct list_head prio_queue; - - struct b2r2_core_job *active_jobs[B2R2_CORE_QUEUE_NO_OF]; - unsigned long n_active_jobs; - - unsigned long jiffies_last_active; - unsigned long jiffies_last_irq; -#ifdef HANDLE_TIMEOUTED_JOBS - struct delayed_work timeout_work; -#endif - int next_job_id; - - unsigned long clock_request_count; - struct timer_list clock_off_timer; - - struct workqueue_struct *work_queue; - - /* Statistics */ - unsigned long stat_n_irq; - unsigned long stat_n_jobs_added; - unsigned long stat_n_jobs_removed; - - unsigned long stat_n_jobs_in_prio_list; - -#ifdef CONFIG_DEBUG_FS - struct dentry *debugfs_root_dir; - struct dentry *debugfs_core_root_dir; - struct dentry *debugfs_regs_dir; -#endif - -#ifdef DEBUG_CHECK_ADDREF_RELEASE - /* Tracking release bug...*/ - struct addref_release ar[100]; - int ar_write; - int ar_read; -#endif - - /* Power management variables */ - struct mutex domain_lock; - struct delayed_work domain_disable_work; - - /* - * We need to keep track of both the number of domain_enable/disable() - * calls and whether the power was actually turned off, since the - * power off is done in a delayed job. - */ - bool domain_enabled; - int domain_request_count; - - struct clk *b2r2_clock; - struct regulator *b2r2_reg; - - struct b2r2_control *control; -}; - /** * b2r2_core - Quick link to administration data for B2R2 */ @@ -318,7 +135,6 @@ static void clear_interrupts(struct b2r2_core *core); static void trigger_job(struct b2r2_core *core, struct b2r2_core_job *job); static void exit_job_list(struct b2r2_core *core, struct list_head *job_list); -static int get_next_job_id(struct b2r2_core *core); static void job_work_function(struct work_struct *ptr); static void init_job(struct b2r2_core_job *job); static void insert_into_prio_list(struct b2r2_core *core, @@ -439,19 +255,21 @@ static void internal_job_addref(struct b2r2_core *core, { u32 ref_count; - b2r2_log_info(core->dev, "%s (%p, %p) (from %s)\n", - __func__, core, job, caller); - /* Sanity checks */ + BUG_ON(core == NULL); BUG_ON(job == NULL); + b2r2_log_info(core->dev, "%s (core: %p, job: %p) (from %s)\n", + __func__, core, job, caller); + + if (job->start_sentinel != START_SENTINEL || job->end_sentinel != END_SENTINEL || job->ref_count == 0 || job->ref_count > 10) { - b2r2_log_info(core->dev, "%s: (%p, %p) start=%X end=%X " - "ref_count=%d\n", __func__, core, job, - job->start_sentinel, job->end_sentinel, - job->ref_count); + b2r2_log_info(core->dev, "%s: (core: %p, job: %p) " + "start=%X end=%X ref_count=%d\n", + __func__, core, job, job->start_sentinel, + job->end_sentinel, job->ref_count); /* Something is wrong, print the addref / release array */ #ifdef DEBUG_CHECK_ADDREF_RELEASE @@ -471,8 +289,8 @@ static void internal_job_addref(struct b2r2_core *core, ar_add(core, job, caller, true); #endif - b2r2_log_info(core->dev, "%s called from %s (%p, %p): Ref Count is " - "%d\n", __func__, caller, core, job, job->ref_count); + b2r2_log_info(core->dev, "%s called from %s (core: %p, job: %p): Ref " + "Count is %d\n", __func__, caller, core, job, job->ref_count); } /** @@ -496,14 +314,14 @@ static bool internal_job_release(struct b2r2_core *core, /* Sanity checks */ BUG_ON(job == NULL); - b2r2_log_info(core->dev, "%s (%p, %p) (from %s)\n", + b2r2_log_info(core->dev, "%s (core: %p, job: %p) (from %s)\n", __func__, core, job, caller); if (job->start_sentinel != START_SENTINEL || job->end_sentinel != END_SENTINEL || job->ref_count == 0 || job->ref_count > 10) { - b2r2_log_info(core->dev, "%s: (%p, %p) start=%X end=%X " - "ref_count=%d\n", __func__, core, job, + b2r2_log_info(core->dev, "%s: (core: %p, job: %p) start=%X " + "end=%X ref_count=%d\n", __func__, core, job, job->start_sentinel, job->end_sentinel, job->ref_count); @@ -521,8 +339,8 @@ static bool internal_job_release(struct b2r2_core *core, #ifdef DEBUG_CHECK_ADDREF_RELEASE ar_add(core, job, caller, false); #endif - b2r2_log_info(core->dev, "%s called from %s (%p, %p) Ref Count is " - "%d\n", __func__, caller, core, job, ref_count); + b2r2_log_info(core->dev, "%s called from %s (core: %p, job: %p) " + "Ref Count is %d\n", __func__, caller, core, job, ref_count); if (!ref_count && job->release) { call_release = true; @@ -543,11 +361,10 @@ static bool internal_job_release(struct b2r2_core *core, void b2r2_core_job_addref(struct b2r2_core_job *job, const char *caller) { unsigned long flags; - struct b2r2_blt_instance *instance; struct b2r2_core *core; - instance = (struct b2r2_blt_instance *) job->tag; - core = instance->control->data; + BUG_ON(job == NULL || job->data == 0); + core = (struct b2r2_core *) job->data; spin_lock_irqsave(&core->lock, flags); internal_job_addref(core, job, caller); @@ -561,11 +378,10 @@ void b2r2_core_job_release(struct b2r2_core_job *job, const char *caller) { unsigned long flags; bool call_release = false; - struct b2r2_blt_instance *instance; struct b2r2_core *core; - instance = (struct b2r2_blt_instance *) job->tag; - core = instance->control->data; + BUG_ON(job == NULL || job->data == 0); + core = (struct b2r2_core *) job->data; spin_lock_irqsave(&core->lock, flags); call_release = internal_job_release(core, job, caller); @@ -584,12 +400,19 @@ int b2r2_core_job_add(struct b2r2_control *control, unsigned long flags; struct b2r2_core *core = control->data; - b2r2_log_info(core->dev, "%s (%p, %p)\n", __func__, control, job); + b2r2_log_info(core->dev, "%s (core: %p, job: %p)\n", + __func__, core, job); /* Enable B2R2 */ domain_enable(core); spin_lock_irqsave(&core->lock, flags); + /* Check that we have not been powered down */ + if (!core->domain_enabled) { + spin_unlock_irqrestore(&core->lock, flags); + return -ENOSYS; + } + core->stat_n_jobs_added++; /* Initialise internal job data */ @@ -605,7 +428,7 @@ int b2r2_core_job_add(struct b2r2_control *control, check_prio_list(core, false); spin_unlock_irqrestore(&core->lock, flags); - return 0; + return job->job_id; } /** @@ -618,7 +441,8 @@ struct b2r2_core_job *b2r2_core_job_find(struct b2r2_control *control, struct b2r2_core_job *job; struct b2r2_core *core = control->data; - b2r2_log_info(core->dev, "%s (%p, %d)\n", __func__, control, job_id); + b2r2_log_info(core->dev, "%s (core: %p, job_id: %d)\n", + __func__, core, job_id); spin_lock_irqsave(&core->lock, flags); /* Look through prio queue */ @@ -642,7 +466,8 @@ struct b2r2_core_job *b2r2_core_job_find_first_with_tag( struct b2r2_core_job *job; struct b2r2_core *core = control->data; - b2r2_log_info(core->dev, "%s (%p, %d)\n", __func__, control, tag); + b2r2_log_info(core->dev, + "%s (core: %p, tag: %d)\n", __func__, core, tag); spin_lock_irqsave(&core->lock, flags); /* Look through prio queue */ @@ -669,11 +494,7 @@ static bool is_job_done(struct b2r2_core_job *job) { unsigned long flags; bool job_is_done; - struct b2r2_blt_instance *instance; - struct b2r2_core *core; - - instance = (struct b2r2_blt_instance *) job->tag; - core = instance->control->data; + struct b2r2_core *core = (struct b2r2_core *) job->data; spin_lock_irqsave(&core->lock, flags); job_is_done = @@ -694,13 +515,12 @@ static bool is_job_done(struct b2r2_core_job *job) int b2r2_core_job_wait(struct b2r2_core_job *job) { int ret = 0; - struct b2r2_blt_instance *instance; - struct b2r2_core *core; - - instance = (struct b2r2_blt_instance *) job->tag; - core = instance->control->data; +#ifdef CONFIG_B2R2_DEBUG + struct b2r2_core *core = (struct b2r2_core *) job->data; +#endif - b2r2_log_info(core->dev, "%s (%p)\n", __func__, job); + b2r2_log_info(core->dev, "%s (core: %p, job: %p)\n", + __func__, core, job); /* Check that we have the job */ if (job->job_state == B2R2_CORE_JOB_IDLE) { /* Never or not queued */ @@ -779,14 +599,10 @@ int b2r2_core_job_cancel(struct b2r2_core_job *job) { unsigned long flags; int ret = 0; - struct b2r2_blt_instance *instance; - struct b2r2_core *core; - - instance = (struct b2r2_blt_instance *) job->tag; - core = instance->control->data; + struct b2r2_core *core = (struct b2r2_core *) job->data; - b2r2_log_info(core->dev, "%s (%p) (%d)\n", - __func__, job, job->job_state); + b2r2_log_info(core->dev, "%s (core: %p, job: %p) (st: %d)\n", + __func__, core, job, job->job_state); /* Check that we have the job */ if (job->job_state == B2R2_CORE_JOB_IDLE) { /* Never or not queued */ @@ -819,6 +635,7 @@ static void domain_disable_work_function(struct work_struct *work) return; if (core->domain_request_count == 0) { + core->valid = false; exit_hw(core); clk_disable(core->b2r2_clock); regulator_disable(core->b2r2_reg); @@ -853,10 +670,16 @@ again: else if (ret < 0) goto regulator_enable_failed; - clk_enable(core->b2r2_clock); + ret = clk_enable(core->b2r2_clock); + if (ret < 0) { + b2r2_log_err(core->dev, + "%s: Could not enable clock\n", __func__); + goto enable_clk_failed; + } if (init_hw(core) < 0) goto init_hw_failed; core->domain_enabled = true; + core->valid = true; } mutex_unlock(&core->domain_lock); @@ -866,9 +689,9 @@ again: init_hw_failed: b2r2_log_err(core->dev, "%s: Could not initialize hardware!\n", __func__); - clk_disable(core->b2r2_clock); +enable_clk_failed: if (regulator_disable(core->b2r2_reg) < 0) b2r2_log_err(core->dev, "%s: regulator_disable failed!\n", __func__); @@ -946,22 +769,6 @@ static void exit_job_list(struct b2r2_core *core, } /** - * get_next_job_id() - Return a new job id. - * - * @core: The b2r2 core entity - */ -static int get_next_job_id(struct b2r2_core *core) -{ - int job_id; - - if (core->next_job_id < 1) - core->next_job_id = 1; - job_id = core->next_job_id++; - - return job_id; -} - -/** * job_work_function() - Work queue function that calls callback(s) and * checks if B2R2 can accept a new job * @@ -972,11 +779,7 @@ static void job_work_function(struct work_struct *ptr) unsigned long flags; struct b2r2_core_job *job = container_of(ptr, struct b2r2_core_job, work); - struct b2r2_blt_instance *instance; - struct b2r2_core *core; - - instance = (struct b2r2_blt_instance *) job->tag; - core = instance->control->data; + struct b2r2_core *core = (struct b2r2_core *) job->data; /* Disable B2R2 */ domain_disable(core); @@ -1025,10 +828,12 @@ static void timeout_work_function(struct work_struct *ptr) if (core->n_active_jobs > 0) { unsigned long diff = (long) jiffies - (long) core->jiffies_last_irq; - if (diff > HZ/2) { + if (diff > JOB_TIMEOUT) { /* Active jobs and more than a second since last irq! */ int i; + b2r2_core_print_stats(core); + /* Look for timeout:ed jobs and put them in tmp list. * It's important that the application queues are * killed in order of decreasing priority */ @@ -1077,7 +882,7 @@ static void timeout_work_function(struct work_struct *ptr) if (core->n_active_jobs) queue_delayed_work( core->work_queue, - &core->timeout_work, HZ/2); + &core->timeout_work, JOB_TIMEOUT); spin_unlock_irqrestore(&core->lock, flags); } @@ -1194,18 +999,10 @@ static void stop_hw_timer(struct b2r2_core *core, struct b2r2_core_job *job) */ static void init_job(struct b2r2_core_job *job) { - struct b2r2_blt_instance *instance; - struct b2r2_core *core; - - instance = (struct b2r2_blt_instance *) job->tag; - core = instance->control->data; job->start_sentinel = START_SENTINEL; job->end_sentinel = END_SENTINEL; - /* Get a job id*/ - job->job_id = get_next_job_id(core); - /* Job is idle, never queued */ job->job_state = B2R2_CORE_JOB_IDLE; @@ -1361,7 +1158,7 @@ static void check_prio_list(struct b2r2_core *core, bool atomic) #ifdef HANDLE_TIMEOUTED_JOBS /* Check in one half second if it hangs */ queue_delayed_work(core->work_queue, - &core->timeout_work, HZ/2); + &core->timeout_work, JOB_TIMEOUT); #endif } else { /* No resources */ @@ -1395,9 +1192,7 @@ static struct b2r2_core_job *find_job_in_list(int job_id, struct b2r2_core_job *job = list_entry( ptr, struct b2r2_core_job, list); if (job->job_id == job_id) { - struct b2r2_blt_instance *instance = - (struct b2r2_blt_instance *) job->tag; - struct b2r2_core *core = instance->control->data; + struct b2r2_core *core = (struct b2r2_core *) job->data; /* Increase reference count, should be released by the caller of b2r2_core_job_find */ internal_job_addref(core, job, __func__); @@ -1503,7 +1298,7 @@ static struct b2r2_core_job *find_tag_in_active_jobs(struct b2r2_core *core, */ static int hw_reset(struct b2r2_core *core) { - u32 uTimeOut = B2R2_DRIVER_TIMEOUT_VALUE; + u32 uTimeOut = B2R2_RESET_TIMEOUT_VALUE; /* Tell B2R2 to reset */ writel(readl(&core->hw->BLT_CTL) | B2R2BLT_CTLGLOBAL_soft_reset, @@ -1761,17 +1556,23 @@ static void process_events(struct b2r2_core *core, u32 status) * b2r2_irq_handler() - B2R2 interrupt handler * * @irq: Interrupt number (not used) - * @x: ??? (Not used) + * @dev_id: A pointer to the b2r2 core entity */ -static irqreturn_t b2r2_irq_handler(int irq, void *x) +static irqreturn_t b2r2_irq_handler(int irq, void *dev_id) { unsigned long flags; - struct b2r2_core* core = (struct b2r2_core *) x; + struct b2r2_core* core = (struct b2r2_core *) dev_id; /* Spin lock is need in irq handler (SMP) */ spin_lock_irqsave(&core->lock, flags); - /* Make sure that we have a clock */ + /* Make a quick exit if this device was not interrupting */ + if (!core->valid || + ((readl(&core->hw->BLT_ITS) & B2R2_ITS_MASK) == 0)) { + core->stat_n_irq_skipped++; + spin_unlock_irqrestore(&core->lock, flags); + return IRQ_NONE; + } /* Remember time for last irq (for timeout mgmt) */ core->jiffies_last_irq = jiffies; @@ -1783,6 +1584,8 @@ static irqreturn_t b2r2_irq_handler(int irq, void *x) /* Check if we can dispatch new jobs */ check_prio_list(core, true); + core->stat_n_irq_exit++; + spin_unlock_irqrestore(&core->lock, flags); return IRQ_HANDLED; @@ -2032,9 +1835,9 @@ static int debugfs_b2r2_reg_read(struct file *filp, char __user *buf, size_t dev_size; int ret = 0; unsigned long value; - char *Buf = kmalloc(sizeof(char) * 4096, GFP_KERNEL); + char *tmpbuf = kmalloc(sizeof(char) * 4096, GFP_KERNEL); - if (Buf == NULL) { + if (tmpbuf == NULL) { ret = -ENOMEM; goto out; } @@ -2044,7 +1847,7 @@ static int debugfs_b2r2_reg_read(struct file *filp, char __user *buf, filp->f_dentry->d_inode->i_private); /* Build the string */ - dev_size = sprintf(Buf, "%8lX\n", value); + dev_size = sprintf(tmpbuf, "%8lX\n", value); /* No more to read if offset != 0 */ if (*f_pos > dev_size) @@ -2054,14 +1857,14 @@ static int debugfs_b2r2_reg_read(struct file *filp, char __user *buf, count = dev_size - *f_pos; /* Return it to user space */ - if (copy_to_user(buf, Buf, count)) + if (copy_to_user(buf, tmpbuf, count)) ret = -EINVAL; *f_pos += count; ret = count; out: - if (Buf != NULL) - kfree(Buf); + if (tmpbuf != NULL) + kfree(tmpbuf); return ret; } @@ -2078,19 +1881,19 @@ out: static int debugfs_b2r2_reg_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos) { - char Buf[80]; + char tmpbuf[80]; u32 reg_value; int ret = 0; /* Adjust count */ - if (count >= sizeof(Buf)) - count = sizeof(Buf) - 1; + if (count >= sizeof(tmpbuf)) + count = sizeof(tmpbuf) - 1; /* Get it from user space */ - if (copy_from_user(Buf, buf, count)) + if (copy_from_user(tmpbuf, buf, count)) return -EINVAL; - Buf[count] = 0; + tmpbuf[count] = 0; /* Convert from hex string */ - if (sscanf(Buf, "%8lX", (unsigned long *) ®_value) != 1) + if (sscanf(tmpbuf, "%8lX", (unsigned long *) ®_value) != 1) return -EINVAL; writel(reg_value, (u32 *) @@ -2127,9 +1930,9 @@ static int debugfs_b2r2_regs_read(struct file *filp, char __user *buf, size_t dev_size = 0; int ret = 0; int i; - char *Buf = kmalloc(sizeof(char) * 4096, GFP_KERNEL); + char *tmpbuf = kmalloc(sizeof(char) * 4096, GFP_KERNEL); - if (Buf == NULL) { + if (tmpbuf == NULL) { ret = -ENOMEM; goto out; } @@ -2140,7 +1943,7 @@ static int debugfs_b2r2_regs_read(struct file *filp, char __user *buf, readl((u32 *) (((u8 *) filp->f_dentry->d_inode->i_private) + debugfs_regs[i].offset)); - dev_size += sprintf(Buf + dev_size, "%s: %08lX\n", + dev_size += sprintf(tmpbuf + dev_size, "%s: %08lX\n", debugfs_regs[i].name, value); } @@ -2152,14 +1955,14 @@ static int debugfs_b2r2_regs_read(struct file *filp, char __user *buf, if (*f_pos + count > dev_size) count = dev_size - *f_pos; - if (copy_to_user(buf, Buf, count)) + if (copy_to_user(buf, tmpbuf, count)) ret = -EINVAL; *f_pos += count; ret = count; out: - if (Buf != NULL) - kfree(Buf); + if (tmpbuf != NULL) + kfree(tmpbuf); return ret; } @@ -2187,30 +1990,30 @@ static int debugfs_b2r2_stat_read(struct file *filp, char __user *buf, size_t dev_size = 0; int ret = 0; int i = 0; - char *Buf = kmalloc(sizeof(char) * 4096, GFP_KERNEL); + char *tmpbuf = kmalloc(sizeof(char) * 4096, GFP_KERNEL); struct b2r2_core *core = filp->f_dentry->d_inode->i_private; - if (Buf == NULL) { + if (tmpbuf == NULL) { ret = -ENOMEM; goto out; } /* Build a string containing all statistics */ - dev_size += sprintf(Buf + dev_size, "Interrupts : %lu\n", + dev_size += sprintf(tmpbuf + dev_size, "Interrupts : %lu\n", core->stat_n_irq); - dev_size += sprintf(Buf + dev_size, "Added jobs : %lu\n", + dev_size += sprintf(tmpbuf + dev_size, "Added jobs : %lu\n", core->stat_n_jobs_added); - dev_size += sprintf(Buf + dev_size, "Removed jobs : %lu\n", + dev_size += sprintf(tmpbuf + dev_size, "Removed jobs : %lu\n", core->stat_n_jobs_removed); - dev_size += sprintf(Buf + dev_size, "Jobs in prio list : %lu\n", + dev_size += sprintf(tmpbuf + dev_size, "Jobs in prio list : %lu\n", core->stat_n_jobs_in_prio_list); - dev_size += sprintf(Buf + dev_size, "Active jobs : %lu\n", + dev_size += sprintf(tmpbuf + dev_size, "Active jobs : %lu\n", core->n_active_jobs); for (i = 0; i < ARRAY_SIZE(core->active_jobs); i++) - dev_size += sprintf(Buf + dev_size, + dev_size += sprintf(tmpbuf + dev_size, " Job in queue %d : 0x%08lx\n", i, (unsigned long) core->active_jobs[i]); - dev_size += sprintf(Buf + dev_size, "Clock requests : %lu\n", + dev_size += sprintf(tmpbuf + dev_size, "Clock requests : %lu\n", core->clock_request_count); /* No more to read if offset != 0 */ @@ -2220,14 +2023,14 @@ static int debugfs_b2r2_stat_read(struct file *filp, char __user *buf, if (*f_pos + count > dev_size) count = dev_size - *f_pos; - if (copy_to_user(buf, Buf, count)) + if (copy_to_user(buf, tmpbuf, count)) ret = -EINVAL; *f_pos += count; ret = count; out: - if (Buf != NULL) - kfree(Buf); + if (tmpbuf != NULL) + kfree(tmpbuf); return ret; } @@ -2254,14 +2057,14 @@ static int debugfs_b2r2_clock_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) { /* 10 characters hex number + newline + string terminator; */ - char Buf[10+2]; + char tmpbuf[10+2]; size_t dev_size; int ret = 0; struct b2r2_core *core = filp->f_dentry->d_inode->i_private; unsigned long value = clk_get_rate(core->b2r2_clock); - dev_size = sprintf(Buf, "%#010lx\n", value); + dev_size = sprintf(tmpbuf, "%#010lx\n", value); /* No more to read if offset != 0 */ if (*f_pos > dev_size) @@ -2270,7 +2073,7 @@ static int debugfs_b2r2_clock_read(struct file *filp, char __user *buf, if (*f_pos + count > dev_size) count = dev_size - *f_pos; - if (copy_to_user(buf, Buf, count)) + if (copy_to_user(buf, tmpbuf, count)) ret = -EINVAL; *f_pos += count; ret = count; @@ -2292,16 +2095,16 @@ out: static int debugfs_b2r2_clock_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos) { - char Buf[80]; + char tmpbuf[80]; u32 reg_value; int ret = 0; - if (count >= sizeof(Buf)) - count = sizeof(Buf) - 1; - if (copy_from_user(Buf, buf, count)) + if (count >= sizeof(tmpbuf)) + count = sizeof(tmpbuf) - 1; + if (copy_from_user(tmpbuf, buf, count)) return -EINVAL; - Buf[count] = 0; - if (sscanf(Buf, "%8lX", (unsigned long *) ®_value) != 1) + tmpbuf[count] = 0; + if (sscanf(tmpbuf, "%8lX", (unsigned long *) ®_value) != 1) return -EINVAL; /*not working yet*/ @@ -2322,6 +2125,88 @@ static const struct file_operations debugfs_b2r2_clock_fops = { .write = debugfs_b2r2_clock_write, }; +/** + * debugfs_b2r2_enabled_read() - Implements debugfs read for + * B2R2 Core Enable/Disable + * @filp: File pointer + * @buf: User space buffer + * @count: Number of bytes to read + * @f_pos: File position + * + * Returns number of bytes read or negative error code + */ +static int debugfs_b2r2_enabled_read(struct file *filp, char __user *buf, + size_t count, loff_t *f_pos) +{ + /* 4 characters hex number + newline + string terminator; */ + char tmpbuf[4+2]; + size_t dev_size; + int ret = 0; + struct b2r2_core *core = filp->f_dentry->d_inode->i_private; + + dev_size = sprintf(tmpbuf, "%02X\n", core->control->enabled); + + /* No more to read if offset != 0 */ + if (*f_pos > dev_size) + goto out; + + if (*f_pos + count > dev_size) + count = dev_size - *f_pos; + + if (copy_to_user(buf, tmpbuf, count)) + ret = -EINVAL; + *f_pos += count; + ret = count; +out: + return ret; +} + +/** + * debugfs_b2r2_enabled_write() - Implements debugfs write for + * B2R2 Core Enable/Disable + * @filp: File pointer + * @buf: User space buffer + * @count: Number of bytes to write + * @f_pos: File position + * + * Returns number of bytes written or negative error code + */ +static int debugfs_b2r2_enabled_write(struct file *filp, const char __user *buf, + size_t count, loff_t *f_pos) +{ + char tmpbuf[80]; + unsigned int enable; + int ret = 0; + struct b2r2_core *core = filp->f_dentry->d_inode->i_private; + + if (count >= sizeof(tmpbuf)) + count = sizeof(tmpbuf) - 1; + if (copy_from_user(tmpbuf, buf, count)) + return -EINVAL; + tmpbuf[count] = 0; + if (sscanf(tmpbuf, "%02X", &enable) != 1) + return -EINVAL; + + if (enable) + core->control->enabled = true; + else + core->control->enabled = false; + + *f_pos += count; + ret = count; + + return ret; +} + +/** + * debugfs_b2r2_enabled_fops() - File operations for B2R2 Core Enable/Disable debugfs + */ +static const struct file_operations debugfs_b2r2_enabled_fops = { + .owner = THIS_MODULE, + .read = debugfs_b2r2_enabled_read, + .write = debugfs_b2r2_enabled_write, +}; + #endif /** @@ -2348,7 +2233,7 @@ static const struct file_operations debugfs_b2r2_clock_fops = { static int init_hw(struct b2r2_core *core) { int result = 0; - u32 uTimeOut = B2R2_DRIVER_TIMEOUT_VALUE; + u32 uTimeOut = B2R2_RESET_TIMEOUT_VALUE; /* Put B2R2 into reset */ clear_interrupts(core); @@ -2357,7 +2242,7 @@ static int init_hw(struct b2r2_core *core) &core->hw->BLT_CTL); /* Set up interrupt handler */ - result = request_irq(core->irq, b2r2_irq_handler, 0, + result = request_irq(core->irq, b2r2_irq_handler, IRQF_SHARED, "b2r2-interrupt", core); if (result) { b2r2_log_err(core->dev, @@ -2400,7 +2285,7 @@ static int init_hw(struct b2r2_core *core) debugfs_create_file(debugfs_regs[i].name, 0666, core->debugfs_regs_dir, (void *)(((u8 *) core->hw) + - debugfs_regs[i].offset), + debugfs_regs[i].offset), &debugfs_b2r2_reg_fops); } #endif @@ -2474,18 +2359,22 @@ static void exit_hw(struct b2r2_core *core) static int b2r2_probe(struct platform_device *pdev) { int ret = 0; - struct resource *res; - struct b2r2_core *core; - struct b2r2_control *control; + struct resource *res = NULL; + struct b2r2_core *core = NULL; + struct b2r2_control *control = NULL; + struct b2r2_platform_data *pdata = NULL; + int debug_init = 0; BUG_ON(pdev == NULL); BUG_ON(pdev->id < 0 || pdev->id >= B2R2_MAX_NBR_DEVICES); + pdata = pdev->dev.platform_data; + core = kzalloc(sizeof(*core), GFP_KERNEL); if (!core) { dev_err(&pdev->dev, "b2r2 core alloc failed\n"); ret = -EINVAL; - goto b2r2_probe_core_alloc_fail; + goto error_exit; } core->dev = &pdev->dev; @@ -2512,24 +2401,25 @@ static int b2r2_probe(struct platform_device *pdev) core->work_queue = create_workqueue("B2R2"); if (!core->work_queue) { ret = -ENOMEM; - goto b2r2_probe_no_work_queue; + goto error_exit; } /* Get the clock for B2R2 */ - core->b2r2_clock = clk_get(core->dev, "b2r2"); + core->b2r2_clock = clk_get(core->dev, pdata->clock_id); if (IS_ERR(core->b2r2_clock)) { ret = PTR_ERR(core->b2r2_clock); - dev_err(&pdev->dev, "clk_get b2r2 failed\n"); - goto b2r2_probe_no_clk; + dev_err(&pdev->dev, "clk_get %s failed\n", pdata->clock_id); + goto error_exit; } /* Get the B2R2 regulator */ - core->b2r2_reg = regulator_get(core->dev, "vsupply"); + core->b2r2_reg = regulator_get(core->dev, pdata->regulator_id); if (IS_ERR(core->b2r2_reg)) { ret = PTR_ERR(core->b2r2_reg); - dev_err(&pdev->dev, "regulator_get vsupply failed " - "(dev_name=%s)\n", dev_name(core->dev)); - goto b2r2_probe_no_reg; + dev_err(&pdev->dev, "regulator_get %s failed " + "(dev_name=%s)\n", pdata->regulator_id, + dev_name(core->dev)); + goto error_exit; } /* Init power management */ @@ -2537,18 +2427,19 @@ static int b2r2_probe(struct platform_device *pdev) INIT_DELAYED_WORK_DEFERRABLE(&core->domain_disable_work, domain_disable_work_function); core->domain_enabled = false; + core->valid = false; /* Map B2R2 into kernel virtual memory space */ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (res == NULL) - goto b2r2_probe_no_res; + goto error_exit; /* Hook up irq */ core->irq = platform_get_irq(pdev, 0); if (core->irq <= 0) { dev_err(&pdev->dev, "%s: Failed to request irq (irq=%d)\n", __func__, core->irq); - goto b2r2_failed_irq_get; + goto error_exit; } core->hw = (struct b2r2_memory_map *) ioremap(res->start, @@ -2556,7 +2447,7 @@ static int b2r2_probe(struct platform_device *pdev) if (core->hw == NULL) { dev_err(&pdev->dev, "%s: ioremap failed\n", __func__); ret = -ENOMEM; - goto b2r2_probe_ioremap_failed; + goto error_exit; } dev_dbg(core->dev, "b2r2 structure address %p\n", core->hw); @@ -2565,14 +2456,12 @@ static int b2r2_probe(struct platform_device *pdev) if (!control) { dev_err(&pdev->dev, "b2r2 control alloc failed\n"); ret = -EINVAL; - goto b2r2_probe_control_alloc_fail; + goto error_exit; } - control->miscdev.parent = core->dev; control->data = (void *)core; control->id = pdev->id; control->dev = &pdev->dev; /* Temporary device */ - snprintf(control->name, sizeof(control->name), "%s_blt", core->name); core->op_size = B2R2_PLUG_OPCODE_SIZE_DEFAULT; core->ch_size = B2R2_PLUG_CHUNK_SIZE_DEFAULT; @@ -2598,6 +2487,9 @@ static int b2r2_probe(struct platform_device *pdev) core, &debugfs_b2r2_stat_fops); debugfs_create_file("clock", 0666, core->debugfs_core_root_dir, core, &debugfs_b2r2_clock_fops); + debugfs_create_file("enabled", 0666, + core->debugfs_core_root_dir, + core, &debugfs_b2r2_enabled_fops); debugfs_create_u8("op_size", 0666, core->debugfs_core_root_dir, &core->op_size); debugfs_create_u8("ch_size", 0666, core->debugfs_core_root_dir, @@ -2614,39 +2506,44 @@ static int b2r2_probe(struct platform_device *pdev) ret = b2r2_debug_init(control); if (ret < 0) { dev_err(&pdev->dev, "b2r2_debug_init failed\n"); - goto b2r2_probe_debug_init_failed; + goto error_exit; } + debug_init = 1; - /* Initialize b2r2_blt module. FIXME: Module of it's own - or perhaps a dedicated module init c file? */ - ret = b2r2_blt_module_init(control); + /* Initialize b2r2_control */ + ret = b2r2_control_init(control); if (ret < 0) { - b2r2_log_err(&pdev->dev, "b2r2_blt_module_init failed\n"); - goto b2r2_probe_blt_init_fail; + b2r2_log_err(&pdev->dev, "b2r2_control_init failed\n"); + goto error_exit; } - core->control = control; + + /* Add the control to the blitter */ + kref_init(&control->ref); + control->enabled = true; + b2r2_blt_add_control(control); + b2r2_core[pdev->id] = core; - dev_info(&pdev->dev, "init done.\n"); + dev_info(&pdev->dev, "%s done.\n", __func__); return ret; /** Recover from any error if something fails */ -b2r2_probe_blt_init_fail: +error_exit: kfree(control); -b2r2_probe_control_alloc_fail: -b2r2_probe_ioremap_failed: -b2r2_failed_irq_get: -b2r2_probe_no_res: - regulator_put(core->b2r2_reg); -b2r2_probe_no_reg: - clk_put(core->b2r2_clock); -b2r2_probe_no_clk: - destroy_workqueue(core->work_queue); - core->work_queue = NULL; -b2r2_probe_no_work_queue: - b2r2_debug_exit(); -b2r2_probe_debug_init_failed: + + if (!IS_ERR_OR_NULL(core->b2r2_reg)) + regulator_put(core->b2r2_reg); + + if (!IS_ERR_OR_NULL(core->b2r2_clock)) + clk_put(core->b2r2_clock); + + if (!IS_ERR_OR_NULL(core->work_queue)) + destroy_workqueue(core->work_queue); + + if (debug_init) + b2r2_debug_exit(); + #ifdef CONFIG_DEBUG_FS if (!IS_ERR_OR_NULL(core->debugfs_root_dir)) { debugfs_remove_recursive(core->debugfs_root_dir); @@ -2654,44 +2551,29 @@ b2r2_probe_debug_init_failed: } #endif kfree(core); -b2r2_probe_core_alloc_fail: - dev_info(&pdev->dev, "init done with errors.\n"); + + dev_info(&pdev->dev, "%s done with errors (%d).\n", __func__, ret); return ret; } - - -/** - * b2r2_remove - This routine unloads b2r2 driver - * - * @pdev: platform device. - */ -static int b2r2_remove(struct platform_device *pdev) +void b2r2_core_release(struct kref *control_ref) { + struct b2r2_control *control = container_of( + control_ref, struct b2r2_control, ref); + struct b2r2_core *core = control->data; + int id = control->id; unsigned long flags; - struct b2r2_core *core; - - BUG_ON(pdev == NULL); - - core = dev_get_drvdata(&pdev->dev); - BUG_ON(core == NULL); - b2r2_log_info(&pdev->dev, "%s: Started\n", __func__); - -#ifdef CONFIG_DEBUG_FS - if (!IS_ERR_OR_NULL(core->debugfs_root_dir)) { - debugfs_remove_recursive(core->debugfs_root_dir); - core->debugfs_root_dir = NULL; - } +#ifdef CONFIG_B2R2_DEBUG + struct device *dev = core->dev; #endif - /* Flush B2R2 work queue (call all callbacks) */ - flush_workqueue(core->work_queue); - - /* Exit b2r2 blt module */ - b2r2_blt_module_exit(core->control); + b2r2_log_info(dev, "%s: enter\n", __func__); - kfree(core->control); + /* Exit b2r2 control module */ + b2r2_control_exit(control); + kfree(control); + b2r2_debug_exit(); #ifdef HANDLE_TIMEOUTED_JOBS cancel_delayed_work(&core->timeout_work); @@ -2705,7 +2587,7 @@ static int b2r2_remove(struct platform_device *pdev) cancel_delayed_work_sync(&core->domain_disable_work); /** Unmap B2R2 registers */ - b2r2_log_info(&pdev->dev, "unmap b2r2 registers..\n"); + b2r2_log_info(dev, "%s: unmap b2r2 registers..\n", __func__); if (core->hw) { iounmap(core->hw); core->hw = NULL; @@ -2723,9 +2605,41 @@ static int b2r2_remove(struct platform_device *pdev) core->dev = NULL; kfree(core); - b2r2_core[pdev->id] = NULL; + b2r2_core[id] = NULL; - b2r2_debug_exit(); + b2r2_log_info(dev, "%s: exit\n", __func__); +} + + +/** + * b2r2_remove - This routine unloads b2r2 driver + * + * @pdev: platform device. + */ +static int b2r2_remove(struct platform_device *pdev) +{ + struct b2r2_core *core; + + BUG_ON(pdev == NULL); + + core = dev_get_drvdata(&pdev->dev); + BUG_ON(core == NULL); + b2r2_log_info(&pdev->dev, "%s: Started\n", __func__); + +#ifdef CONFIG_DEBUG_FS + if (!IS_ERR_OR_NULL(core->debugfs_root_dir)) { + debugfs_remove_recursive(core->debugfs_root_dir); + core->debugfs_root_dir = NULL; + } +#endif + + /* Flush B2R2 work queue (call all callbacks) */ + flush_workqueue(core->work_queue); + + /* Remove control from blitter */ + core->control->enabled = false; + b2r2_blt_remove_control(core->control); + kref_put(&core->control->ref, b2r2_core_release); b2r2_log_info(&pdev->dev, "%s: Ended\n", __func__); @@ -2785,6 +2699,23 @@ int b2r2_resume(struct platform_device *pdev) return 0; } +void b2r2_core_print_stats(struct b2r2_core *core) +{ + b2r2_log_info(core->dev, + "%s: n_irq %ld, n_irq_exit %ld, n_irq_skipped %ld,\n" + "n_jobs_added %ld, n_active_jobs %ld, " + "n_jobs_in_prio_list %ld,\n" + "n_jobs_removed %ld\n", + __func__, + core->stat_n_irq, + core->stat_n_irq_exit, + core->stat_n_irq_skipped, + core->stat_n_jobs_added, + core->n_active_jobs, + core->stat_n_jobs_in_prio_list, + core->stat_n_jobs_removed); +} + /** * struct platform_b2r2_driver - Platform driver configuration for the * B2R2 core driver |