From patchwork Fri Jan 25 10:01:54 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Florian Vaussard X-Patchwork-Id: 2042531 Return-Path: X-Original-To: patchwork-linux-arm@patchwork.kernel.org Delivered-To: patchwork-process-083081@patchwork1.kernel.org Received: from merlin.infradead.org (merlin.infradead.org [205.233.59.134]) by patchwork1.kernel.org (Postfix) with ESMTP id 37DD93FD1A for ; Fri, 25 Jan 2013 10:04:59 +0000 (UTC) Received: from localhost ([::1] helo=merlin.infradead.org) by merlin.infradead.org with esmtp (Exim 4.76 #1 (Red Hat Linux)) id 1Tyg7I-0004ki-IO; Fri, 25 Jan 2013 10:02:36 +0000 Received: from slb-mail0.epfl.ch ([2001:620:618:1e0:1:80b2:e059:1] helo=smtp4.epfl.ch) by merlin.infradead.org with smtp (Exim 4.76 #1 (Red Hat Linux)) id 1Tyg6n-0004Wz-34 for linux-arm-kernel@lists.infradead.org; Fri, 25 Jan 2013 10:02:06 +0000 Received: (qmail 5607 invoked by uid 107); 25 Jan 2013 10:02:02 -0000 X-Virus-Scanned: ClamAV Received: from lsro1pc340.epfl.ch (HELO lsro1pc340.epfl.ch) (128.178.145.154) (authenticated) by smtp4.epfl.ch (AngelmatoPhylax SMTP proxy) with ESMTPA; Fri, 25 Jan 2013 11:02:02 +0100 From: Florian Vaussard To: Bryan Wu , Richard Purdie , Thierry Reding Subject: [PATCH 3/3] leds: leds-pwm: Defer led_pwm_set() if PWM can sleep Date: Fri, 25 Jan 2013 11:01:54 +0100 Message-Id: <1359108114-16998-4-git-send-email-florian.vaussard@epfl.ch> X-Mailer: git-send-email 1.7.5.4 In-Reply-To: <1359108114-16998-1-git-send-email-florian.vaussard@epfl.ch> References: <1359108114-16998-1-git-send-email-florian.vaussard@epfl.ch> X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20130125_050205_388793_9408DFA6 X-CRM114-Status: GOOD ( 13.72 ) X-Spam-Score: -2.6 (--) X-Spam-Report: SpamAssassin version 3.3.2 on merlin.infradead.org summary: Content analysis details: (-2.6 points) pts rule name description ---- ---------------------- -------------------------------------------------- -0.7 RP_MATCHES_RCVD Envelope sender domain matches handover relay domain -1.9 BAYES_00 BODY: Bayes spam probability is 0 to 1% [score: 0.0000] Cc: Peter Ujfalusi , Florian Vaussard , linux-arm-kernel@lists.infradead.org, linux-leds@vger.kernel.org, linux-kernel@vger.kernel.org X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: linux-arm-kernel-bounces@lists.infradead.org Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org Call to led_pwm_set() can happen inside atomic context, like triggers. If the PWM call can sleep, defer using a worker. Signed-off-by: Florian Vaussard --- drivers/leds/leds-pwm.c | 45 +++++++++++++++++++++++++++++++++++++++------ 1 files changed, 39 insertions(+), 6 deletions(-) diff --git a/drivers/leds/leds-pwm.c b/drivers/leds/leds-pwm.c index a1ea5f6..1ffb6ba 100644 --- a/drivers/leds/leds-pwm.c +++ b/drivers/leds/leds-pwm.c @@ -23,12 +23,15 @@ #include #include #include +#include struct led_pwm_data { struct led_classdev cdev; struct pwm_device *pwm; + struct work_struct work; unsigned int active_low; unsigned int period; + int duty; }; struct led_pwm_priv { @@ -36,6 +39,20 @@ struct led_pwm_priv { struct led_pwm_data leds[0]; }; +static void led_pwm_work(struct work_struct *work) +{ + struct led_pwm_data *led_dat = + container_of(work, struct led_pwm_data, work); + int new_duty = led_dat->duty; + + pwm_config(led_dat->pwm, new_duty, led_dat->period); + + if (new_duty == 0) + pwm_disable(led_dat->pwm); + else + pwm_enable(led_dat->pwm); +} + static void led_pwm_set(struct led_classdev *led_cdev, enum led_brightness brightness) { @@ -43,13 +60,23 @@ static void led_pwm_set(struct led_classdev *led_cdev, container_of(led_cdev, struct led_pwm_data, cdev); unsigned int max = led_dat->cdev.max_brightness; unsigned int period = led_dat->period; + int duty; - if (brightness == 0) { - pwm_config(led_dat->pwm, 0, period); - pwm_disable(led_dat->pwm); + if (brightness == 0) + duty = 0; + else + duty = brightness * period / max; + + if (pwm_can_sleep(led_dat->pwm)) { + led_dat->duty = duty; + schedule_work(&led_dat->work); } else { - pwm_config(led_dat->pwm, brightness * period / max, period); - pwm_enable(led_dat->pwm); + pwm_config(led_dat->pwm, duty, period); + + if (duty == 0) + pwm_disable(led_dat->pwm); + else + pwm_enable(led_dat->pwm); } } @@ -100,6 +127,8 @@ static struct led_pwm_priv *led_pwm_create_of(struct platform_device *pdev) led_dat->cdev.brightness = LED_OFF; led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME; + INIT_WORK(&led_dat->work, led_pwm_work); + ret = led_classdev_register(&pdev->dev, &led_dat->cdev); if (ret < 0) { dev_err(&pdev->dev, "failed to register for %s\n", @@ -153,6 +182,8 @@ static int led_pwm_probe(struct platform_device *pdev) led_dat->cdev.max_brightness = cur_led->max_brightness; led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME; + INIT_WORK(&led_dat->work, led_pwm_work); + ret = led_classdev_register(&pdev->dev, &led_dat->cdev); if (ret < 0) goto err; @@ -180,8 +211,10 @@ static int led_pwm_remove(struct platform_device *pdev) struct led_pwm_priv *priv = platform_get_drvdata(pdev); int i; - for (i = 0; i < priv->num_leds; i++) + for (i = 0; i < priv->num_leds; i++) { led_classdev_unregister(&priv->leds[i].cdev); + cancel_work_sync(&priv->leds[i].work); + } return 0; }