leds: leds-qpnp: allocate ordered work queue for led
From user space, the call procedures of red led blink are as below: 1. turn off red led. 2. blink red led. Each above step will be transitioned from user space to kernel driver and trigger a led WORK. The order from user space is very important because if the step 2 completes before step 1, then the red led will be turned off, while the user wants to blink it. On kernel version 3.4, below sequences will cause the order from user space fail: 1. CPU0 schedule a led WORK on system_wq, which is to turn off the red led. 2. CPU1 schedule a led WORK on system_wq, which is to blink the red led. 3. Although the first WORK is queued before the second WORK, both of them can executed concurrently on CPU0 and CPU1. 4. CPU0's workload is very heavy because it will handle almost all the hardware interrupt, so it is probably that the first WORK thread is scheduled out for some time. At that moment, the second WORK can complete faster than the first WORK. This finally cause the red led is first blinking then been turned off. To solve this issue on Kernel version 3.4, we can create an ordered workqueue which will promise us that the same led WORK will not be scheduled on different cpu and cannot be executed on different cpu concurrently. On kernel version 3.10, because the default system_wq has already promised the concurrency of the same WORK, so we don't need to use ordered workqueue for led module. Change-Id: I23fda20f2951bfcebb7ce7c9ecea542435496efe CRs-Fixed: 714466 703170 Signed-off-by: Mao Li <maol@codeaurora.org> Signed-off-by: Abinaya P <abinayap@codeaurora.org>
This commit is contained in:
parent
005e67e0ba
commit
1ff1376a87
|
@ -19,6 +19,9 @@ Required properties for each child node, WLED, Flash and RGB:
|
|||
- qcom,max-current : maximum current that the LED can sustain in mA
|
||||
- linux,name : name of the led that is used in led framework
|
||||
|
||||
Optional properties for each child node, WLED, Flash, MPP and RGB:
|
||||
- qcom,in-order-command-processing : specify if user space requests leds in order
|
||||
|
||||
WLED is primarily used as display backlight. Display subsystem uses
|
||||
LED triggers for WLED to control the brightness as needed.
|
||||
|
||||
|
|
|
@ -455,6 +455,7 @@ struct rgb_config_data {
|
|||
* struct qpnp_led_data - internal led data structure
|
||||
* @led_classdev - led class device
|
||||
* @delayed_work - delayed work for turning off the LED
|
||||
* @workqueue - dedicated workqueue to handle concurrency
|
||||
* @work - workqueue for led
|
||||
* @id - led index
|
||||
* @base_reg - base register given in device tree
|
||||
|
@ -469,6 +470,7 @@ struct qpnp_led_data {
|
|||
struct led_classdev cdev;
|
||||
struct spmi_device *spmi_dev;
|
||||
struct delayed_work dwork;
|
||||
struct workqueue_struct *workqueue;
|
||||
struct work_struct work;
|
||||
int id;
|
||||
u16 base;
|
||||
|
@ -482,6 +484,7 @@ struct qpnp_led_data {
|
|||
struct mpp_config_data *mpp_cfg;
|
||||
int max_current;
|
||||
bool default_on;
|
||||
bool in_order_command_processing;
|
||||
int turn_off_delay_ms;
|
||||
};
|
||||
|
||||
|
@ -1420,7 +1423,10 @@ static void qpnp_led_set(struct led_classdev *led_cdev,
|
|||
value = led->cdev.max_brightness;
|
||||
|
||||
led->cdev.brightness = value;
|
||||
schedule_work(&led->work);
|
||||
if (led->in_order_command_processing)
|
||||
queue_work(led->workqueue, &led->work);
|
||||
else
|
||||
schedule_work(&led->work);
|
||||
}
|
||||
|
||||
static void __qpnp_led_work(struct qpnp_led_data *led,
|
||||
|
@ -2197,6 +2203,7 @@ static void led_blink(struct qpnp_led_data *led,
|
|||
{
|
||||
int rc;
|
||||
|
||||
flush_work(&led->work);
|
||||
mutex_lock(&led->lock);
|
||||
if (pwm_cfg->use_blink) {
|
||||
if (led->cdev.brightness) {
|
||||
|
@ -3373,6 +3380,24 @@ static int __devinit qpnp_leds_probe(struct spmi_device *spmi)
|
|||
if (led->id != QPNP_ID_FLASH1_LED0 &&
|
||||
led->id != QPNP_ID_FLASH1_LED1)
|
||||
mutex_init(&led->lock);
|
||||
|
||||
led->in_order_command_processing = of_property_read_bool
|
||||
(temp, "qcom,in-order-command-processing");
|
||||
|
||||
if (led->in_order_command_processing) {
|
||||
/*
|
||||
* the command order from user space needs to be
|
||||
* maintained use ordered workqueue to prevent
|
||||
* concurrency
|
||||
*/
|
||||
led->workqueue = alloc_ordered_workqueue
|
||||
("led_workqueue", 0);
|
||||
if (!led->workqueue) {
|
||||
rc = -ENOMEM;
|
||||
goto fail_id_check;
|
||||
}
|
||||
}
|
||||
|
||||
INIT_WORK(&led->work, qpnp_led_work);
|
||||
|
||||
rc = qpnp_led_initialize(led);
|
||||
|
@ -3470,6 +3495,8 @@ fail_id_check:
|
|||
if (led_array[i].id != QPNP_ID_FLASH1_LED0 &&
|
||||
led_array[i].id != QPNP_ID_FLASH1_LED1)
|
||||
mutex_destroy(&led_array[i].lock);
|
||||
if (led_array[i].in_order_command_processing)
|
||||
destroy_workqueue(led_array[i].workqueue);
|
||||
led_classdev_unregister(&led_array[i].cdev);
|
||||
}
|
||||
|
||||
|
@ -3487,6 +3514,8 @@ static int __devexit qpnp_leds_remove(struct spmi_device *spmi)
|
|||
led_array[i].id != QPNP_ID_FLASH1_LED1)
|
||||
mutex_destroy(&led_array[i].lock);
|
||||
|
||||
if (led_array[i].in_order_command_processing)
|
||||
destroy_workqueue(led_array[i].workqueue);
|
||||
led_classdev_unregister(&led_array[i].cdev);
|
||||
switch (led_array[i].id) {
|
||||
case QPNP_ID_WLED:
|
||||
|
|
Loading…
Reference in New Issue