diff options
author | Aleksej Makarov <aleksej.makarov@sonymobile.com> | 2015-04-14 10:39:05 +0200 |
---|---|---|
committer | Aleksej Makarov <aleksej.makarov@sonymobile.com> | 2015-04-15 15:43:56 +0000 |
commit | d03f13d450bf05c889390705aef9dda76fa954a5 (patch) | |
tree | 8ac262de1bc921414400e1f897842a15921326f3 | |
parent | b52013eab236191c9b4530f76ab1d4bf79a51bf1 (diff) | |
download | bcm-d03f13d450bf05c889390705aef9dda76fa954a5.tar.gz |
input: em718x: make IO operations faster
- parameters IO moved to worker thread ensuring
that this thread is not be blocked for a long time
by interrupt thread
- control interfaces are synchronized with control
work execution to unsure correct control sequence.
This will not delay since control
work is not blocked by irq thread anymore
Signed-off-by: Aleksej Makarov <aleksej.makarov@sonymobile.com>
Bug: 20094613
Issue: KIONE-2279
Change-Id: I469032a7885b07659694ff4b9b36606494218ecb
-rw-r--r-- | drivers/input/misc/em718x.c | 183 |
1 files changed, 124 insertions, 59 deletions
diff --git a/drivers/input/misc/em718x.c b/drivers/input/misc/em718x.c index 2d8ac02510c..39173d6ebc6 100644 --- a/drivers/input/misc/em718x.c +++ b/drivers/input/misc/em718x.c @@ -337,6 +337,13 @@ struct sensor_request { int fifo_size; }; +struct parameter_transfer { + u32 buf[127]; + int num; + int pos; + bool pending; +}; + struct em718x { struct i2c_client *client; int irq_gpio; @@ -353,8 +360,6 @@ struct em718x { struct wake_lock w_lock; struct notifier_block nb; bool suspend_prepared; - u32 *parameter_buf; - int parameter_num; struct em718x_parameters *dts_parameters; int dts_parameters_num; struct event_device ev_device; @@ -368,6 +373,11 @@ struct em718x { struct sensor_request sns_state[SNS_CNT]; struct mutex ctl_lock; struct work_struct sns_ctl_work; + struct parameter_transfer parameter_r; + struct parameter_transfer parameter_w; + wait_queue_head_t control_wq; + bool control_pending; + }; static int smbus_read_byte(struct i2c_client *client, @@ -698,66 +708,94 @@ exit: return rc; } +static void exec_parameters_transfer(struct em718x *em718x) +{ + if (em718x->parameter_r.pending) { + em718x->parameter_r.num = em718x_parameters_read(em718x, + em718x->parameter_r.buf, + em718x->parameter_r.pos, + em718x->parameter_r.num); + em718x->parameter_r.pending = false; + } + if (em718x->parameter_w.pending) { + int rc; + int i; + for (i = 0; i < em718x->parameter_w.num; i++) { + rc = em718x_parameter_load(em718x, + em718x->parameter_w.buf[i], + em718x->parameter_w.pos + i); + if (rc) + break; + } + em718x->parameter_w.num = rc; + em718x->parameter_w.pending = false; + } +} -static ssize_t em718x_set_parameters_read(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) +static void post_control_work_locked(struct em718x *em718x) { - struct em718x *em718x = dev_get_drvdata(dev); - s32 from; - s32 num; - int rc; - char op; + em718x->control_pending = true; + schedule_work(&em718x->sns_ctl_work); +} - if (3 != sscanf(buf, "%c,%i,%i", &op, &from, &num)) - return -EINVAL; - if (op == 'r') { - if (!num || from < 1 || (from + num - 1) > 127) - return -EINVAL; - if (em718x->parameter_buf) - kfree(em718x->parameter_buf); - em718x->parameter_buf = - kzalloc(num * sizeof(*em718x->parameter_buf), - GFP_KERNEL); - if (!em718x->parameter_buf) - return -ENOMEM; - em718x->parameter_num = num; - rc = em718x_parameters_read(em718x, em718x->parameter_buf, - from, num); - } else if (op == 'w') { - if (from < 1 || from > 127) - return -EINVAL; - mutex_lock(&em718x->lock); - rc = em718x_parameter_load(em718x, num, from); - mutex_unlock(&em718x->lock); - } else { +static ssize_t parameters_bin_read(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count) +{ + struct i2c_client *client = kobj_to_i2c_client(kobj); + struct em718x *em718x = dev_get_drvdata(&client->dev); + + if (pos + count >= sizeof(em718x->parameter_r.buf)) return -EINVAL; - } - return rc ? rc : count; + + mutex_lock(&em718x->ctl_lock); + em718x->parameter_r.pos = pos; + em718x->parameter_r.num = count; + em718x->parameter_r.pending = true; + post_control_work_locked(em718x); + mutex_unlock(&em718x->ctl_lock); + + wait_event_interruptible(em718x->control_wq, + !em718x->control_pending); + + if (em718x->parameter_r.num > 0) + memcpy(buf, em718x->parameter_r.buf, count); + return em718x->parameter_r.num < 0 ? em718x->parameter_r.num : count; } -static ssize_t em718x_get_parameters_read(struct device *dev, - struct device_attribute *attr, - char *buf) +static ssize_t parameters_bin_write(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count) { - struct em718x *em718x = dev_get_drvdata(dev); - int i; - char *p = buf; + struct i2c_client *client = kobj_to_i2c_client(kobj); + struct em718x *em718x = dev_get_drvdata(&client->dev); - if (!em718x->parameter_buf) + if (pos + count >= sizeof(em718x->parameter_w.buf)) return -EINVAL; - for (i = 0; i < em718x->parameter_num; i++) { - p += scnprintf(p, PAGE_SIZE - (p - buf), "0x%08x ", - em718x->parameter_buf[i]); - } - kfree(em718x->parameter_buf); - em718x->parameter_buf = NULL; - p += scnprintf(p, PAGE_SIZE - (p - buf), "\n"); - return (ssize_t)(p - buf); + + mutex_lock(&em718x->ctl_lock); + em718x->parameter_w.pos = pos; + em718x->parameter_w.num = count; + em718x->parameter_w.pending = true; + memcpy(em718x->parameter_w.buf, buf, count); + post_control_work_locked(em718x); + mutex_unlock(&em718x->ctl_lock); + + wait_event_interruptible(em718x->control_wq, + !em718x->control_pending); + + return em718x->parameter_w.num < 0 ? em718x->parameter_w.num : count; } -static DEVICE_ATTR(parameters, S_IWUSR | S_IRUSR, - em718x_get_parameters_read, em718x_set_parameters_read); +static struct bin_attribute parameter_bin_data = { + .attr = { + .name = "parameters", + .mode = S_IRUGO | S_IWUSR, + }, + .size = 127, + .read = parameters_bin_read, + .write = parameters_bin_write, +}; static ssize_t em718x_set_register(struct device *dev, struct device_attribute *attr, @@ -851,9 +889,14 @@ static ssize_t em718x_set_rate(struct device *dev, mutex_lock(&em718x->ctl_lock); if (em718x->sns_state[i].rate != rate) { em718x->sns_req[i].rate = rate; - schedule_work(&em718x->sns_ctl_work); + post_control_work_locked(em718x); + mutex_unlock(&em718x->ctl_lock); + wait_event_interruptible(em718x->control_wq, + !em718x->control_pending); + goto exit; } mutex_unlock(&em718x->ctl_lock); +exit: return count; } } @@ -898,9 +941,14 @@ static ssize_t em718x_set_report_enable(struct device *dev, mutex_lock(&em718x->ctl_lock); if (em718x->sns_state[i].report != enable) { em718x->sns_req[i].report = enable; - schedule_work(&em718x->sns_ctl_work); + post_control_work_locked(em718x); + mutex_unlock(&em718x->ctl_lock); + wait_event_interruptible(em718x->control_wq, + !em718x->control_pending); + goto exit; } mutex_unlock(&em718x->ctl_lock); +exit: return count; } } @@ -976,6 +1024,9 @@ static void sensor_control_work(struct work_struct *work) if (state->report != rq->report) (void)em718x_set_sensor_report(em718x, i, rq->report); } + exec_parameters_transfer(em718x); + em718x->control_pending = false; + wake_up_interruptible(&em718x->control_wq); mutex_unlock(&em718x->ctl_lock); mutex_unlock(&em718x->lock); } @@ -1006,9 +1057,14 @@ static ssize_t em718x_set_fifo_size(struct device *dev, mutex_lock(&em718x->ctl_lock); if (em718x->sns_state[SNS_ACC].fifo_size || fifo_size) { em718x->sns_req[SNS_ACC].fifo_size = fifo_size; - schedule_work(&em718x->sns_ctl_work); + post_control_work_locked(em718x); + mutex_unlock(&em718x->ctl_lock); + wait_event_interruptible(em718x->control_wq, + !em718x->control_pending); + goto exit; } mutex_unlock(&em718x->ctl_lock); +exit: return count; } @@ -1075,7 +1131,6 @@ static struct attribute *em718x_attributes[] = { &dev_attr_rate.attr, &dev_attr_report.attr, &dev_attr_reset.attr, - &dev_attr_parameters.attr, &dev_attr_fifo_size.attr, &dev_attr_fifo_size_max.attr, &dev_attr_wuff.attr, @@ -1828,10 +1883,14 @@ static void em718x_reset_work_func(struct work_struct *work) mutex_lock(&em718x->ctl_lock); memset(em718x->sns_state, 0, sizeof(em718x->sns_state)); - schedule_work(&em718x->sns_ctl_work); + post_control_work_locked(em718x); mutex_unlock(&em718x->ctl_lock); - + mutex_unlock(&em718x->lock); + wait_event_interruptible(em718x->control_wq, + !em718x->control_pending); dev_info(dev, "reset finished with no errors\n"); + return; + exit_rescheduled: mutex_unlock(&em718x->lock); return; @@ -1917,14 +1976,13 @@ static ssize_t em718x_cdev_read(struct file *filp, char __user *buf, for (len = 0; size >= sizeof(p->ev[0]) && p->ridx != p->widx; len += sizeof(p->ev[0])) { struct sensor_event ev = p->ev[p->ridx]; - size -= sizeof(p->ev[0]); p->ridx = (p->ridx + 1) % p->queue_size; spin_unlock_irqrestore(&p->lock, flags); if (copy_to_user(buf + len, &ev, sizeof(ev))) { len = -EFAULT; - break; + goto exit; } spin_lock_irqsave(&p->lock, flags); } @@ -1935,6 +1993,7 @@ static ssize_t em718x_cdev_read(struct file *filp, char __user *buf, dev_dbg(&em718x->client->dev, "removing wake lock\n"); } spin_unlock_irqrestore(&p->lock, flags); +exit: return len; } @@ -2059,6 +2118,7 @@ static int em718x_probe(struct i2c_client *client, mutex_init(&em718x->lock); mutex_init(&em718x->ctl_lock); wake_lock_init(&em718x->w_lock, WAKE_LOCK_SUSPEND, dev_name(dev)); + init_waitqueue_head(&em718x->control_wq); rc = em718x_check_id(em718x); if (rc) goto exit; @@ -2133,6 +2193,10 @@ static int em718x_probe(struct i2c_client *client, if (rc < 0) dev_err(dev, "could not create bin sysfs\n"); + rc = sysfs_create_bin_file(&dev->kobj, ¶meter_bin_data); + if (rc < 0) + dev_err(dev, "could not create parameter bin sysfs\n"); + rc = sysfs_create_group(&dev->kobj, &em718x_attribute_group); if (rc < 0) dev_err(dev, "could not create sysfs\n"); @@ -2165,6 +2229,7 @@ static int em718x_remove(struct i2c_client *client) struct em718x *em718x = dev_get_drvdata(&client->dev); unregister_pm_notifier(&em718x->nb); sysfs_remove_group(&client->dev.kobj, &em718x_attribute_group); + sysfs_remove_bin_file(&client->dev.kobj, ¶meter_bin_data); misc_deregister(&em718x->ev_device.cdev); misc_deregister(&em718x->wake_ev_device.cdev); wake_lock_destroy(&em718x->w_lock); |