From patchwork Wed Apr 16 18:25:21 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Doug Anderson X-Patchwork-Id: 4003501 Return-Path: X-Original-To: patchwork-linux-samsung-soc@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork2.web.kernel.org (Postfix) with ESMTP id D7BC6BFF02 for ; Wed, 16 Apr 2014 18:25:39 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id CD29A20353 for ; Wed, 16 Apr 2014 18:25:38 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 021E720375 for ; Wed, 16 Apr 2014 18:25:37 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1755314AbaDPSZf (ORCPT ); Wed, 16 Apr 2014 14:25:35 -0400 Received: from mail-ig0-f201.google.com ([209.85.213.201]:63714 "EHLO mail-ig0-f201.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1754294AbaDPSZe (ORCPT ); Wed, 16 Apr 2014 14:25:34 -0400 Received: by mail-ig0-f201.google.com with SMTP id l13so115703iga.4 for ; Wed, 16 Apr 2014 11:25:33 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=/QmtU1TF4+VUYz7rOC/CcL2JpZA0g3a3nwn0n/QBElg=; b=lhRrb2kNldrT7mqu2WxuKMRWv3ZLkUFMevOc+2wX+dCVOuzci6sxCWsylHqbjdkapX DSbitsMaqoooDJnxHVgLfvb+hLa13MEsVhiUmeL+Sf5tYQko6G7apfF5sg5or9/vAf0R joBkukogfVOwhOcCquRR01zpR0Lujx/fnxpc+UdIKlZVYuU0xJ2bz7IFfN3hO0aShDQP 6lUpRVCAw/0qdfb5WxSo5ovezUofV1iFbh+l5rQzVwwvT1rXbkYRMTESXQJFiqfJtBcj /1welJVtCpVxsDOlYINWpibJvYwbZa3jJGwpE9JRXRysudZziWYsDYo6GsN8Htt0I/Tg vUvA== X-Gm-Message-State: ALoCoQnu8QRN0zLFa1KDo2RiB0pMDV5ZjpJTfUKkQOj8IZFfRDK4+2kj7/PGdve2kQQCUmejZ2oQLNjENhs9bf+Gvrjk+544jhhffij7/oOS08gYLeWqGOpiHX0fgNrSdU9dJuZWlL5kqIVapJRshiN8yZJcE10WJfEvtYhe9beaKMmpVJffQy27Ps1eFlqV7UUim02LPUde6xpWXvcbBeTc9e1A85ZutQ== X-Received: by 10.182.102.197 with SMTP id fq5mr4694717obb.8.1397672733677; Wed, 16 Apr 2014 11:25:33 -0700 (PDT) Received: from corp2gmr1-1.hot.corp.google.com (corp2gmr1-1.hot.corp.google.com [172.24.189.92]) by gmr-mx.google.com with ESMTPS id a44si3201661yhb.6.2014.04.16.11.25.33 for (version=TLSv1.1 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Wed, 16 Apr 2014 11:25:33 -0700 (PDT) Received: from tictac.mtv.corp.google.com (tictac.mtv.corp.google.com [172.22.72.141]) by corp2gmr1-1.hot.corp.google.com (Postfix) with ESMTP id 6903F31C268; Wed, 16 Apr 2014 11:25:33 -0700 (PDT) Received: by tictac.mtv.corp.google.com (Postfix, from userid 121310) id 18FEC80628; Wed, 16 Apr 2014 11:25:32 -0700 (PDT) From: Doug Anderson To: Anton Vorontsov Cc: Olof Johansson , Sachin Kamat , ajaykumar.rs@samsung.com, linux-samsung-soc@vger.kernel.org, Doug Anderson , Dmitry Eremin-Solenikov , David Woodhouse , linux-kernel@vger.kernel.org Subject: [PATCH v2 2/5] charger: tps65090: Allow charger module to be used when no irq Date: Wed, 16 Apr 2014 11:25:21 -0700 Message-Id: <1397672724-9063-3-git-send-email-dianders@chromium.org> X-Mailer: git-send-email 1.9.1.423.g4596e3a In-Reply-To: <1397672724-9063-1-git-send-email-dianders@chromium.org> References: <1397592876-5741-1-git-send-email-dianders@chromium.org> <1397672724-9063-1-git-send-email-dianders@chromium.org> Sender: linux-samsung-soc-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-samsung-soc@vger.kernel.org X-Spam-Status: No, score=-7.6 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_HI, RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=ham version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP On the ARM Chromebook tps65090 has two masters: the AP (the main processor running linux) and the EC (the embedded controller). The AP is allowed to mess with FETs but the EC is in charge of charge control. The tps65090 interupt line is routed to both the AP and the EC, which can cause quite a headache. Having two people adjusting masks and acking interrupts is a recipe for disaster. In the shipping kernel we had a hack to have the AP pay attention to the IRQ but not to ack it. It also wasn't supposed to configure the IRQ in any way. That hack allowed us to detect when the device was charging without messing with the EC's state. The current tps65090 infrastructure makes the above difficult, and it was a bit of a hack to begin with. Rather than uglify the driver to support it, just extend the driver's existing notion of "no irq" to the charger. This makes the charger code poll every 2 seconds for AC detect, which is sufficient. For proper functioning, requires (mfd: tps65090: Don't tell child devices we have an IRQ if we don't). If we don't have that patch we'll simply fail to probe on devices without an interrupt (just like we did before this patch). Signed-off-by: Doug Anderson --- Changes in v2: - Split noirq (polling mode) changes into MFD and charger drivers/power/tps65090-charger.c | 76 +++++++++++++++++++++++++++++++--------- 1 file changed, 59 insertions(+), 17 deletions(-) diff --git a/drivers/power/tps65090-charger.c b/drivers/power/tps65090-charger.c index 8fc9d6d..cc26c12 100644 --- a/drivers/power/tps65090-charger.c +++ b/drivers/power/tps65090-charger.c @@ -17,9 +17,11 @@ */ #include #include +#include #include #include #include +#include #include #include #include @@ -43,11 +45,15 @@ #define TPS65090_VACG BIT(1) #define TPS65090_NOITERM BIT(5) +#define POLL_INTERVAL (HZ * 2) /* Used when no irq */ + struct tps65090_charger { struct device *dev; int ac_online; int prev_ac_online; int irq; + struct task_struct *poll_task; + bool passive_mode; struct power_supply ac; struct tps65090_platform_data *pdata; }; @@ -60,6 +66,9 @@ static int tps65090_low_chrg_current(struct tps65090_charger *charger) { int ret; + if (charger->passive_mode) + return 0; + ret = tps65090_write(charger->dev->parent, TPS65090_REG_CG_CTRL5, TPS65090_NOITERM); if (ret < 0) { @@ -75,6 +84,9 @@ static int tps65090_enable_charging(struct tps65090_charger *charger) int ret; uint8_t ctrl0 = 0; + if (charger->passive_mode) + return 0; + ret = tps65090_read(charger->dev->parent, TPS65090_REG_CG_CTRL0, &ctrl0); if (ret < 0) { @@ -98,6 +110,9 @@ static int tps65090_config_charger(struct tps65090_charger *charger) uint8_t intrmask = 0; int ret; + if (charger->passive_mode) + return 0; + if (charger->pdata->enable_low_current_chrg) { ret = tps65090_low_chrg_current(charger); if (ret < 0) { @@ -175,10 +190,14 @@ static irqreturn_t tps65090_charger_isr(int irq, void *dev_id) } /* Clear interrupts. */ - ret = tps65090_write(charger->dev->parent, TPS65090_REG_INTR_STS, 0x00); - if (ret < 0) { - dev_err(charger->dev, "%s(): Error in writing reg 0x%x\n", + if (!charger->passive_mode) { + ret = tps65090_write(charger->dev->parent, + TPS65090_REG_INTR_STS, 0x00); + if (ret < 0) { + dev_err(charger->dev, + "%s(): Error in writing reg 0x%x\n", __func__, TPS65090_REG_INTR_STS); + } } if (charger->prev_ac_online != charger->ac_online) @@ -209,6 +228,18 @@ static struct tps65090_platform_data * } +static int tps65090_charger_poll_task(void *data) +{ + set_freezable(); + + while (!kthread_should_stop()) { + schedule_timeout_interruptible(POLL_INTERVAL); + try_to_freeze(); + tps65090_charger_isr(-1, data); + } + return 0; +} + static int tps65090_charger_probe(struct platform_device *pdev) { struct tps65090_charger *cdata; @@ -255,22 +286,10 @@ static int tps65090_charger_probe(struct platform_device *pdev) } irq = platform_get_irq(pdev, 0); - if (irq <= 0) { - dev_warn(&pdev->dev, "Unable to get charger irq = %d\n", irq); - ret = irq; - goto fail_unregister_supply; - } - + if (irq < 0) + irq = NO_IRQ; cdata->irq = irq; - ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, - tps65090_charger_isr, 0, "tps65090-charger", cdata); - if (ret) { - dev_err(cdata->dev, "Unable to register irq %d err %d\n", irq, - ret); - goto fail_unregister_supply; - } - ret = tps65090_config_charger(cdata); if (ret < 0) { dev_err(&pdev->dev, "charger config failed, err %d\n", ret); @@ -296,6 +315,27 @@ static int tps65090_charger_probe(struct platform_device *pdev) power_supply_changed(&cdata->ac); } + if (irq != NO_IRQ) { + ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, + tps65090_charger_isr, 0, "tps65090-charger", cdata); + if (ret) { + dev_err(cdata->dev, + "Unable to register irq %d err %d\n", irq, + ret); + goto fail_unregister_supply; + } + } else { + cdata->poll_task = kthread_run(tps65090_charger_poll_task, + cdata, "ktps65090charger"); + cdata->passive_mode = true; + if (IS_ERR(cdata->poll_task)) { + ret = PTR_ERR(cdata->poll_task); + dev_err(cdata->dev, + "Unable to run kthread err %d\n", ret); + goto fail_unregister_supply; + } + } + return 0; fail_unregister_supply: @@ -308,6 +348,8 @@ static int tps65090_charger_remove(struct platform_device *pdev) { struct tps65090_charger *cdata = platform_get_drvdata(pdev); + if (cdata->irq == NO_IRQ) + kthread_stop(cdata->poll_task); power_supply_unregister(&cdata->ac); return 0;