Message ID | 1667647544-12945-2-git-send-email-ivo.g.dimitrov.75@gmail.com |
---|---|
State | New |
Headers |
Return-Path: <linux-kernel-owner@vger.kernel.org> Delivered-To: ouuuleilei@gmail.com Received: by 2002:a5d:6687:0:0:0:0:0 with SMTP id l7csp940943wru; Sat, 5 Nov 2022 04:34:48 -0700 (PDT) X-Google-Smtp-Source: AMsMyM5B2XaDHtnLu1p0L2cNvbV3AtYdKwAl2sMM5tM+PXQa0U5DP+9yslLLiJravsi07XosxWoW X-Received: by 2002:aa7:c1d9:0:b0:463:aeaf:3383 with SMTP id d25-20020aa7c1d9000000b00463aeaf3383mr24998509edp.253.1667648087972; Sat, 05 Nov 2022 04:34:47 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1667648087; cv=none; d=google.com; s=arc-20160816; b=CZm7Wa7yYU7tQjsNBnz4oAr853/hSmbUUp7zVRHxPawtDwOK+nBFFqbj3WTjKTZAFA /5WmMz2lGqU+VWbet/t/Kv70Td3N6rDYEojnvyGd1lVgUoGCkIOvxmNoG562ynvory8D YbQNmN8+EAh1JAcqSCr5oNmyMJRQAX0c6jWvNjFHDgdTancJN2GR3ZXHqGsQ3tDeKquc RqLUoeCqN7EWtlQ6mR23/pJafMCw3c/HYm4xj6dgKvEYwi44rq/vjKMkIsKa3No5Aihd igSvLqN60qlHyASdRJfXsYz6Yj77uEtqrOHkAIft4KkxIo+oEVeiACDQJG/iU8aIrEFD xoIw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:to:from :dkim-signature; bh=PIJ7hf/fFBlHGb3gaHRXBAK5sDqMN3APy8LXApKcTCc=; b=QAp5JfXmnM4frLdk7+IpbnIoIZFR/UgdBk6A+XSN/q5AhkAc9YqKZ+fd3trDexTkMG j8/eo8A1sJ9gqgMipgBNIJk/AJwyhafqPrycx9Y0wcv42s0kkMnRny+IHpMYEFOOOctn hRgnh1jyY8/iPC6nEX836dJSUnKeiZpOIkgKTqPCF6lu4Etc6mgJXi1U84kf1c0KVwbt SETC25s5C5EmAzyOqwluDdoxw3xdB7Cn7qtaisD1yy5TTMIyKGFZqpt0uNjmaYJCbMUG 4bBtq4Z9EAOTYIjn+HqZouDQULycpmMaSSwzXtdj5L7l9Fzz6KEngics6Z45cHqljVCC DCpw== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@gmail.com header.s=20210112 header.b=hXEVWmvW; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id rv14-20020a17090710ce00b0073866c0672asi1705226ejb.73.2022.11.05.04.34.24; Sat, 05 Nov 2022 04:34:47 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; dkim=pass header.i=@gmail.com header.s=20210112 header.b=hXEVWmvW; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229801AbiKEL0Y (ORCPT <rfc822;hjfbswb@gmail.com> + 99 others); Sat, 5 Nov 2022 07:26:24 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:55068 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229770AbiKEL0S (ORCPT <rfc822;linux-kernel@vger.kernel.org>); Sat, 5 Nov 2022 07:26:18 -0400 Received: from mail-ej1-x62f.google.com (mail-ej1-x62f.google.com [IPv6:2a00:1450:4864:20::62f]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id D34C21CB16; Sat, 5 Nov 2022 04:26:16 -0700 (PDT) Received: by mail-ej1-x62f.google.com with SMTP id kt23so19234533ejc.7; Sat, 05 Nov 2022 04:26:16 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=PIJ7hf/fFBlHGb3gaHRXBAK5sDqMN3APy8LXApKcTCc=; b=hXEVWmvWnxe69ninDDQWYoIBl3qLZ9ZNzW8y40rtZJSMghjsDd4Vfzi7QTiExEYmJn rABOv+nXVLVMi6nqHFtaZrZ6pS7BQoLKUd3snMgUOsNVya9d6qsfLW9V5a+gP45JUHzY vWrkU9dhMLzE6Iq64tRYRjs5bRNFfutjuujlOCjhOyewPLvDkCvhLpLnJeYL9CehKLZA 3+5z3nAzqkDI61LlXRCwZtN3gqyoJbB/gwh1xWDuOFb3pRLLSBkzcftntcoJSRGMxTOj Y1CNujlBW+5ZjUXtLUMZ6gH5LyKluLehm3xTfqRNzcL9jRQlhV//CVbZKbPfIeo5mFCs D3fA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=PIJ7hf/fFBlHGb3gaHRXBAK5sDqMN3APy8LXApKcTCc=; b=JejPxMlVybff5IQqElGlLO7xj850CZBWayH0yZN7AxaD2nRCuZOO1DroA0eXbmNb4H VU/ZBfEjvGg5vzZ3BFs6xgGdF19M6y3oS6DScaCZRVHOtv0Fi9LsMX+SsHUZn+6RD2Ve MUDTnNZ1e64W/6MEqQoqN2c0x+pzgGAkGWgeYjwwdNffFwwpCMmE4go6PTaPqlT9rQpk 0GdHVjQGGGlXipjpqra0zDRipCfWZt/DOU/cTb/d8qJxP+b6skFAeZxb4LbkGlURivs4 4OzSjo5wV8XBpmH9ju9MKyXyCnoNzPoMItiNE6pQyrCOc3IBDj4W+m0CaUnN3plnTUDq EleQ== X-Gm-Message-State: ACrzQf1eVW7O3xehp7FK8FqNF+gtoW/sBGojM2GESEZG2+W5ONhnLCY3 7dyHQpVfFw7ZZAWUsc55FjY= X-Received: by 2002:a17:907:2063:b0:7ad:fa6b:e84b with SMTP id qp3-20020a170907206300b007adfa6be84bmr20372498ejb.69.1667647575280; Sat, 05 Nov 2022 04:26:15 -0700 (PDT) Received: from localhost.localdomain ([46.249.74.23]) by smtp.gmail.com with ESMTPSA id u18-20020a509512000000b004611c230bd0sm1050069eda.37.2022.11.05.04.26.14 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Sat, 05 Nov 2022 04:26:14 -0700 (PDT) From: Ivaylo Dimitrov <ivo.g.dimitrov.75@gmail.com> To: sre@kernel.org Cc: linux-pm@vger.kernel.org, linux-kernel@vger.kernel.org, tony@atomide.com, philipp@uvos.xyz, Ivaylo Dimitrov <ivo.g.dimitrov.75@gmail.com> Subject: [PATCH 1/3] power: cpcap-battery: Do not issue low signal too frequently Date: Sat, 5 Nov 2022 13:25:42 +0200 Message-Id: <1667647544-12945-2-git-send-email-ivo.g.dimitrov.75@gmail.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1667647544-12945-1-git-send-email-ivo.g.dimitrov.75@gmail.com> References: <1667647544-12945-1-git-send-email-ivo.g.dimitrov.75@gmail.com> MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-Spam-Status: No, score=-1.8 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,FREEMAIL_ENVFROM_END_DIGIT, FREEMAIL_FROM,RCVD_IN_DNSWL_NONE,SPF_HELO_NONE,SPF_PASS autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: <linux-kernel.vger.kernel.org> X-Mailing-List: linux-kernel@vger.kernel.org X-getmail-retrieved-from-mailbox: =?utf-8?q?INBOX?= X-GMAIL-THRID: =?utf-8?q?1748655761147984569?= X-GMAIL-MSGID: =?utf-8?q?1748655761147984569?= |
Series |
power: supply: cpcap-battery improvements
|
|
Commit Message
Ivaylo Dimitrov
Nov. 5, 2022, 11:25 a.m. UTC
It seems that low battery irq may be generated tens of times per second,
leading to userspace being flooded with unnecessary events.
Fix that by preventing such events being generated more than once every 30
seconds.
Signed-off-by: Ivaylo Dimitrov <ivo.g.dimitrov.75@gmail.com>
---
drivers/power/supply/cpcap-battery.c | 27 ++++++++++++++++++++++++++-
1 file changed, 26 insertions(+), 1 deletion(-)
Comments
Hi, On Sat, Nov 05, 2022 at 01:25:42PM +0200, Ivaylo Dimitrov wrote: > It seems that low battery irq may be generated tens of times per second, > leading to userspace being flooded with unnecessary events. > > Fix that by preventing such events being generated more than once every 30 > seconds. > > Signed-off-by: Ivaylo Dimitrov <ivo.g.dimitrov.75@gmail.com> > --- Concept looks ok to me, but the code is slightly racy, since the thread is flushed before the IRQ is disabled in the remove routine. > drivers/power/supply/cpcap-battery.c | 27 ++++++++++++++++++++++++++- > 1 file changed, 26 insertions(+), 1 deletion(-) > > diff --git a/drivers/power/supply/cpcap-battery.c b/drivers/power/supply/cpcap-battery.c > index 4676560..8869067 100644 > --- a/drivers/power/supply/cpcap-battery.c > +++ b/drivers/power/supply/cpcap-battery.c > @@ -137,6 +137,7 @@ struct cpcap_battery_ddata { > struct power_supply *psy; > struct cpcap_battery_config config; > struct cpcap_battery_state_data state[CPCAP_BATTERY_STATE_NR]; > + struct delayed_work low_irq_work; > u32 cc_lsb; /* μAms per LSB */ > atomic_t active; > int charge_full; > @@ -914,9 +915,13 @@ static irqreturn_t cpcap_battery_irq_thread(int irq, void *data) > dev_info(ddata->dev, "Coulomb counter calibration done\n"); > break; > case CPCAP_BATTERY_IRQ_ACTION_BATTERY_LOW: > - if (latest->current_ua >= 0) > + if (latest->current_ua >= 0 && > + !delayed_work_pending((&ddata->low_irq_work))) { > dev_warn(ddata->dev, "Battery low at %imV!\n", > latest->voltage / 1000); > + schedule_delayed_work(&ddata->low_irq_work, 30 * HZ); > + disable_irq_nosync(d->irq); > + } > break; > case CPCAP_BATTERY_IRQ_ACTION_POWEROFF: > if (latest->current_ua >= 0 && latest->voltage <= 3200000) { > @@ -1087,6 +1092,21 @@ static int cpcap_battery_calibrate(struct cpcap_battery_ddata *ddata) > return error; > } > > +static void cpcap_battery_lowbph_enable(struct work_struct *work) > +{ > + struct delayed_work *d_work = to_delayed_work(work); > + struct cpcap_battery_ddata *ddata = container_of(d_work, > + struct cpcap_battery_ddata, low_irq_work); > + struct cpcap_interrupt_desc *d; > + > + list_for_each_entry(d, &ddata->irq_list, node) { > + if (d->action == CPCAP_BATTERY_IRQ_ACTION_BATTERY_LOW) > + break; > + } > + > + enable_irq(d->irq); > +} > + > #ifdef CONFIG_OF > static const struct of_device_id cpcap_battery_id_table[] = { > { > @@ -1118,6 +1138,8 @@ static int cpcap_battery_probe(struct platform_device *pdev) > if (!ddata) > return -ENOMEM; > > + INIT_DELAYED_WORK(&ddata->low_irq_work, cpcap_battery_lowbph_enable); use devm_delayed_work_autocancel() and put it directly before cpcap_battery_init_interrupts(). > cpcap_battery_detect_battery_type(ddata); > > INIT_LIST_HEAD(&ddata->irq_list); > @@ -1185,6 +1207,9 @@ static int cpcap_battery_remove(struct platform_device *pdev) > if (error) > dev_err(&pdev->dev, "could not disable: %i\n", error); > > + /* make sure to call enable_irq() if needed */ > + flush_delayed_work(&ddata->low_irq_work); and this can be dropped afterwards. > + > return 0; > } Thanks, -- Sebastian
Hi, On 10.11.22 г. 17:55 ч., Sebastian Reichel wrote: > Hi, > > On Sat, Nov 05, 2022 at 01:25:42PM +0200, Ivaylo Dimitrov wrote: >> It seems that low battery irq may be generated tens of times per second, >> leading to userspace being flooded with unnecessary events. >> >> Fix that by preventing such events being generated more than once every 30 >> seconds. >> >> Signed-off-by: Ivaylo Dimitrov <ivo.g.dimitrov.75@gmail.com> >> --- > > Concept looks ok to me, but the code is slightly racy, since the > thread is flushed before the IRQ is disabled in the remove routine. > I did that on purpose, to have matching disable_irq()/enable_irq() calls. Maybe I over-engineered it, but my understanding is: When remove() is called, if we have delayed work pending, that means that lowbph IRQ was disabled in cpcap_battery_irq_thread() and we have to re-enable it. If delayed_work is not pending, flush_delayed_work() will do nothing, IIUC. Maybe I shall protect schedule_delayed_work() and disable_irq_nosync() calls with a mutex and wait for it before calling flush_delayed_work() in remove? That way there will be guarantee that if delayed_work is pending, IRQ is disabled too, while now maybe there is a small time window remove() to be called between schedule_delayed_work() and disable_irq_nosync(). >> drivers/power/supply/cpcap-battery.c | 27 ++++++++++++++++++++++++++- >> 1 file changed, 26 insertions(+), 1 deletion(-) >> >> diff --git a/drivers/power/supply/cpcap-battery.c b/drivers/power/supply/cpcap-battery.c >> index 4676560..8869067 100644 >> --- a/drivers/power/supply/cpcap-battery.c >> +++ b/drivers/power/supply/cpcap-battery.c >> @@ -137,6 +137,7 @@ struct cpcap_battery_ddata { >> struct power_supply *psy; >> struct cpcap_battery_config config; >> struct cpcap_battery_state_data state[CPCAP_BATTERY_STATE_NR]; >> + struct delayed_work low_irq_work; >> u32 cc_lsb; /* μAms per LSB */ >> atomic_t active; >> int charge_full; >> @@ -914,9 +915,13 @@ static irqreturn_t cpcap_battery_irq_thread(int irq, void *data) >> dev_info(ddata->dev, "Coulomb counter calibration done\n"); >> break; >> case CPCAP_BATTERY_IRQ_ACTION_BATTERY_LOW: >> - if (latest->current_ua >= 0) >> + if (latest->current_ua >= 0 && >> + !delayed_work_pending((&ddata->low_irq_work))) { >> dev_warn(ddata->dev, "Battery low at %imV!\n", >> latest->voltage / 1000); >> + schedule_delayed_work(&ddata->low_irq_work, 30 * HZ); >> + disable_irq_nosync(d->irq); >> + } >> break; >> case CPCAP_BATTERY_IRQ_ACTION_POWEROFF: >> if (latest->current_ua >= 0 && latest->voltage <= 3200000) { >> @@ -1087,6 +1092,21 @@ static int cpcap_battery_calibrate(struct cpcap_battery_ddata *ddata) >> return error; >> } >> >> +static void cpcap_battery_lowbph_enable(struct work_struct *work) >> +{ >> + struct delayed_work *d_work = to_delayed_work(work); >> + struct cpcap_battery_ddata *ddata = container_of(d_work, >> + struct cpcap_battery_ddata, low_irq_work); >> + struct cpcap_interrupt_desc *d; >> + >> + list_for_each_entry(d, &ddata->irq_list, node) { >> + if (d->action == CPCAP_BATTERY_IRQ_ACTION_BATTERY_LOW) >> + break; >> + } >> + >> + enable_irq(d->irq); >> +} >> + >> #ifdef CONFIG_OF >> static const struct of_device_id cpcap_battery_id_table[] = { >> { >> @@ -1118,6 +1138,8 @@ static int cpcap_battery_probe(struct platform_device *pdev) >> if (!ddata) >> return -ENOMEM; >> >> + INIT_DELAYED_WORK(&ddata->low_irq_work, cpcap_battery_lowbph_enable); > > use devm_delayed_work_autocancel() and put it directly before > cpcap_battery_init_interrupts(). > >> cpcap_battery_detect_battery_type(ddata); >> >> INIT_LIST_HEAD(&ddata->irq_list); >> @@ -1185,6 +1207,9 @@ static int cpcap_battery_remove(struct platform_device *pdev) >> if (error) >> dev_err(&pdev->dev, "could not disable: %i\n", error); >> >> + /* make sure to call enable_irq() if needed */ >> + flush_delayed_work(&ddata->low_irq_work); > > and this can be dropped afterwards. > Ok, but what will happen if we have lowbph IRQ already disabled? Wouldn't that cause issues, because of unbalanced disable_irq()/enable_irq() calls? Thanks, Ivo >> + >> return 0; >> } > > Thanks, > > -- Sebastian >
Hi Ivaylo, https://git-scm.com/docs/git-format-patch#_base_tree_information] url: https://github.com/intel-lab-lkp/linux/commits/Ivaylo-Dimitrov/power-supply-cpcap-battery-improvements/20221105-192758 base: https://git.kernel.org/pub/scm/linux/kernel/git/sre/linux-power-supply.git for-next patch link: https://lore.kernel.org/r/1667647544-12945-2-git-send-email-ivo.g.dimitrov.75%40gmail.com patch subject: [PATCH 1/3] power: cpcap-battery: Do not issue low signal too frequently config: m68k-randconfig-m041-20221110 compiler: m68k-linux-gcc (GCC) 12.1.0 If you fix the issue, kindly add following tag where applicable | Reported-by: kernel test robot <lkp@intel.com> | Reported-by: Dan Carpenter <error27@gmail.com> smatch warnings: drivers/power/supply/cpcap-battery.c:1084 cpcap_battery_lowbph_enable() warn: iterator used outside loop: 'd' vim +/d +1084 drivers/power/supply/cpcap-battery.c ce48d112324af4 Ivaylo Dimitrov 2022-11-05 1072 static void cpcap_battery_lowbph_enable(struct work_struct *work) ce48d112324af4 Ivaylo Dimitrov 2022-11-05 1073 { ce48d112324af4 Ivaylo Dimitrov 2022-11-05 1074 struct delayed_work *d_work = to_delayed_work(work); ce48d112324af4 Ivaylo Dimitrov 2022-11-05 1075 struct cpcap_battery_ddata *ddata = container_of(d_work, ce48d112324af4 Ivaylo Dimitrov 2022-11-05 1076 struct cpcap_battery_ddata, low_irq_work); ce48d112324af4 Ivaylo Dimitrov 2022-11-05 1077 struct cpcap_interrupt_desc *d; ce48d112324af4 Ivaylo Dimitrov 2022-11-05 1078 ce48d112324af4 Ivaylo Dimitrov 2022-11-05 1079 list_for_each_entry(d, &ddata->irq_list, node) { ce48d112324af4 Ivaylo Dimitrov 2022-11-05 1080 if (d->action == CPCAP_BATTERY_IRQ_ACTION_BATTERY_LOW) ce48d112324af4 Ivaylo Dimitrov 2022-11-05 1081 break; ce48d112324af4 Ivaylo Dimitrov 2022-11-05 1082 } ce48d112324af4 Ivaylo Dimitrov 2022-11-05 1083 ce48d112324af4 Ivaylo Dimitrov 2022-11-05 @1084 enable_irq(d->irq); If we exit the loop without hitting a break then "d" is not a valid pointer and "enable_irq(d->irq);" will do something bad. ce48d112324af4 Ivaylo Dimitrov 2022-11-05 1085 }
diff --git a/drivers/power/supply/cpcap-battery.c b/drivers/power/supply/cpcap-battery.c index 4676560..8869067 100644 --- a/drivers/power/supply/cpcap-battery.c +++ b/drivers/power/supply/cpcap-battery.c @@ -137,6 +137,7 @@ struct cpcap_battery_ddata { struct power_supply *psy; struct cpcap_battery_config config; struct cpcap_battery_state_data state[CPCAP_BATTERY_STATE_NR]; + struct delayed_work low_irq_work; u32 cc_lsb; /* μAms per LSB */ atomic_t active; int charge_full; @@ -914,9 +915,13 @@ static irqreturn_t cpcap_battery_irq_thread(int irq, void *data) dev_info(ddata->dev, "Coulomb counter calibration done\n"); break; case CPCAP_BATTERY_IRQ_ACTION_BATTERY_LOW: - if (latest->current_ua >= 0) + if (latest->current_ua >= 0 && + !delayed_work_pending((&ddata->low_irq_work))) { dev_warn(ddata->dev, "Battery low at %imV!\n", latest->voltage / 1000); + schedule_delayed_work(&ddata->low_irq_work, 30 * HZ); + disable_irq_nosync(d->irq); + } break; case CPCAP_BATTERY_IRQ_ACTION_POWEROFF: if (latest->current_ua >= 0 && latest->voltage <= 3200000) { @@ -1087,6 +1092,21 @@ static int cpcap_battery_calibrate(struct cpcap_battery_ddata *ddata) return error; } +static void cpcap_battery_lowbph_enable(struct work_struct *work) +{ + struct delayed_work *d_work = to_delayed_work(work); + struct cpcap_battery_ddata *ddata = container_of(d_work, + struct cpcap_battery_ddata, low_irq_work); + struct cpcap_interrupt_desc *d; + + list_for_each_entry(d, &ddata->irq_list, node) { + if (d->action == CPCAP_BATTERY_IRQ_ACTION_BATTERY_LOW) + break; + } + + enable_irq(d->irq); +} + #ifdef CONFIG_OF static const struct of_device_id cpcap_battery_id_table[] = { { @@ -1118,6 +1138,8 @@ static int cpcap_battery_probe(struct platform_device *pdev) if (!ddata) return -ENOMEM; + INIT_DELAYED_WORK(&ddata->low_irq_work, cpcap_battery_lowbph_enable); + cpcap_battery_detect_battery_type(ddata); INIT_LIST_HEAD(&ddata->irq_list); @@ -1185,6 +1207,9 @@ static int cpcap_battery_remove(struct platform_device *pdev) if (error) dev_err(&pdev->dev, "could not disable: %i\n", error); + /* make sure to call enable_irq() if needed */ + flush_delayed_work(&ddata->low_irq_work); + return 0; }