From patchwork Thu Sep 27 20:08:24 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mathieu Poirier X-Patchwork-Id: 11780 Return-Path: X-Original-To: patchwork@peony.canonical.com Delivered-To: patchwork@peony.canonical.com Received: from fiordland.canonical.com (fiordland.canonical.com [91.189.94.145]) by peony.canonical.com (Postfix) with ESMTP id C7A7D24140 for ; Thu, 27 Sep 2012 20:09:30 +0000 (UTC) Received: from mail-ie0-f180.google.com (mail-ie0-f180.google.com [209.85.223.180]) by fiordland.canonical.com (Postfix) with ESMTP id 846E9A18351 for ; Thu, 27 Sep 2012 20:09:30 +0000 (UTC) Received: by mail-ie0-f180.google.com with SMTP id e10so5234855iej.11 for ; Thu, 27 Sep 2012 13:09:30 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20120113; h=x-forwarded-to:x-forwarded-for:delivered-to:received-spf:from:to :subject:date:message-id:x-mailer:in-reply-to:references :x-gm-message-state; bh=9yysMQvAa7KaT+B2vRQJYfJGZbNhnjIObdEXrUtLsp0=; b=fDXUj3wkPhs7eXJ2vlSm5yQGOHqYg4NUaTnthv77CTmE5c9saGhpftzNUAL6eDJdq9 klaKyRp15bIRPCXYAylnoFovUbu4GekOa2+RmnyYajPLLBDzVUWMQsf/XKAsufzjjW2K //oDna4hvcYI85PLHh7VYLisbZnppyTvTMHK2Ow+29paxdcavhgv6djb2PEr74LidVWQ nNUOaXvO7dBLsWLE+kNeQIK8+vjHP+cpCeEpUdA/jZ5UYMq9PxSwkgmuZf0mwL3+coDQ b3AxdpUDf79fsu/YS7A7otVV8G8ON5kgGhJwQ1cYPNpWm5MFDkYJd8mCZtCeZVxAJCii hIvQ== Received: by 10.50.160.165 with SMTP id xl5mr5873514igb.0.1348776570275; Thu, 27 Sep 2012 13:09:30 -0700 (PDT) X-Forwarded-To: linaro-patchwork@canonical.com X-Forwarded-For: patch@linaro.org linaro-patchwork@canonical.com Delivered-To: patches@linaro.org Received: by 10.50.184.232 with SMTP id ex8csp436273igc; Thu, 27 Sep 2012 13:09:29 -0700 (PDT) Received: by 10.68.223.164 with SMTP id qv4mr14450349pbc.20.1348776569543; Thu, 27 Sep 2012 13:09:29 -0700 (PDT) Received: from mail-pa0-f50.google.com (mail-pa0-f50.google.com [209.85.220.50]) by mx.google.com with ESMTPS id vn9si8390651pbc.17.2012.09.27.13.09.29 (version=TLSv1/SSLv3 cipher=OTHER); Thu, 27 Sep 2012 13:09:29 -0700 (PDT) Received-SPF: neutral (google.com: 209.85.220.50 is neither permitted nor denied by best guess record for domain of mathieu.poirier@linaro.org) client-ip=209.85.220.50; Authentication-Results: mx.google.com; spf=neutral (google.com: 209.85.220.50 is neither permitted nor denied by best guess record for domain of mathieu.poirier@linaro.org) smtp.mail=mathieu.poirier@linaro.org Received: by mail-pa0-f50.google.com with SMTP id hz11so1847989pad.37 for ; Thu, 27 Sep 2012 13:09:29 -0700 (PDT) Received: by 10.68.232.70 with SMTP id tm6mr14420707pbc.104.1348776569302; Thu, 27 Sep 2012 13:09:29 -0700 (PDT) Received: from localhost.localdomain (S0106002369de4dac.cg.shawcable.net. [70.73.24.112]) by mx.google.com with ESMTPS id sa2sm1587890pbc.4.2012.09.27.13.09.27 (version=TLSv1/SSLv3 cipher=OTHER); Thu, 27 Sep 2012 13:09:28 -0700 (PDT) From: mathieu.poirier@linaro.org To: patches@linaro.org Subject: [PATCH 07/57] power: ab8500_bm: Detect removed charger Date: Thu, 27 Sep 2012 14:08:24 -0600 Message-Id: <1348776554-10019-8-git-send-email-mathieu.poirier@linaro.org> X-Mailer: git-send-email 1.7.5.4 In-Reply-To: <1348776554-10019-1-git-send-email-mathieu.poirier@linaro.org> References: <1348776554-10019-1-git-send-email-mathieu.poirier@linaro.org> X-Gm-Message-State: ALoCoQnJEEUtc95v3evst+0L0UiHB0NCmIrQqClTyBeDL1Vly9dbEyuC0WQRJLz4zQk1znnAgAER From: Jonas Aaberg Signed-off-by: Jonas Aaberg Signed-off-by: Mathieu Poirier --- drivers/power/ab8500_charger.c | 122 +++++++++++++++++++++++++++++++++++++++- 1 files changed, 121 insertions(+), 1 deletions(-) diff --git a/drivers/power/ab8500_charger.c b/drivers/power/ab8500_charger.c index a7d0c3a..f7bba07 100644 --- a/drivers/power/ab8500_charger.c +++ b/drivers/power/ab8500_charger.c @@ -66,6 +66,11 @@ #define MAIN_CH_NOK 0x01 #define VBUS_DET 0x80 +#define MAIN_CH_STATUS2_MAINCHGDROP 0x80 +#define MAIN_CH_STATUS2_MAINCHARGERDETDBNC 0x40 +#define USB_CH_VBUSDROP 0x40 +#define USB_CH_VBUSDETDBNC 0x01 + /* UsbLineStatus register bit masks */ #define AB8500_USB_LINK_STATUS 0x78 #define AB8500_STD_HOST_SUSP 0x18 @@ -80,6 +85,8 @@ /* Step up/down delay in us */ #define STEP_UDELAY 1000 +#define CHARGER_STATUS_POLL 10 /* in ms */ + /* UsbLineStatus register - usb types */ enum ab8500_charger_link_status { USB_STAT_NOT_CONFIGURED, @@ -201,6 +208,10 @@ struct ab8500_charger_usb_state { * @check_usbchgnotok_work: Work for checking USB charger not ok status * @kick_wd_work: Work for kicking the charger watchdog in case * of ABB rev 1.* due to the watchog logic bug + * @ac_charger_attached_work: Work for checking if AC charger is still + * connected + * @usb_charger_attached_work: Work for checking if USB charger is still + * connected * @ac_work: Work for checking AC charger connection * @detect_usb_type_work: Work for detecting the USB type connected * @usb_link_status_work: Work for checking the new USB link status @@ -237,6 +248,8 @@ struct ab8500_charger { struct delayed_work check_hw_failure_work; struct delayed_work check_usbchgnotok_work; struct delayed_work kick_wd_work; + struct delayed_work ac_charger_attached_work; + struct delayed_work usb_charger_attached_work; struct work_struct ac_work; struct work_struct detect_usb_type_work; struct work_struct usb_link_status_work; @@ -347,6 +360,15 @@ static void ab8500_charger_set_usb_connected(struct ab8500_charger *di, dev_dbg(di->dev, "USB connected:%i\n", connected); di->usb.charger_connected = connected; sysfs_notify(&di->usb_chg.psy.dev->kobj, NULL, "present"); + + if (connected) { + queue_delayed_work(di->charger_wq, + &di->usb_charger_attached_work, + HZ); + } else { + cancel_delayed_work_sync + (&di->usb_charger_attached_work); + } } } @@ -1703,6 +1725,81 @@ static void ab8500_charger_ac_work(struct work_struct *work) sysfs_notify(&di->ac_chg.psy.dev->kobj, NULL, "present"); } +static void ab8500_charger_usb_attached_work(struct work_struct *work) +{ + int i; + int ret; + u8 statval; + struct ab8500_charger *di = container_of(work, + struct ab8500_charger, + usb_charger_attached_work.work); + + for (i = 0 ; i < 10; i++) { + ret = abx500_get_register_interruptible(di->dev, + AB8500_CHARGER, + AB8500_CH_USBCH_STAT1_REG, + &statval); + if (ret < 0) { + dev_err(di->dev, "ab8500 read failed %d\n", + __LINE__); + goto reschedule; + } + if ((statval & (USB_CH_VBUSDROP | + USB_CH_VBUSDETDBNC)) != + (USB_CH_VBUSDROP | USB_CH_VBUSDETDBNC)) + goto reschedule; + + msleep(CHARGER_STATUS_POLL); + } + + (void) ab8500_charger_usb_en(&di->usb_chg, 0, 0, 0); + + return; +reschedule: + queue_delayed_work(di->charger_wq, + &di->usb_charger_attached_work, + HZ); +} + +static void ab8500_charger_ac_attached_work(struct work_struct *work) +{ + + int i; + int ret; + u8 statval; + struct ab8500_charger *di = container_of(work, + struct ab8500_charger, + ac_charger_attached_work.work); + + for (i = 0 ; i < 10; i++) { + ret = abx500_get_register_interruptible(di->dev, + AB8500_CHARGER, + AB8500_CH_STATUS2_REG, + &statval); + if (ret < 0) { + dev_err(di->dev, "ab8500 read failed %d\n", + __LINE__); + goto reschedule; + } + if ((statval & (MAIN_CH_STATUS2_MAINCHGDROP | + MAIN_CH_STATUS2_MAINCHARGERDETDBNC)) != + (MAIN_CH_STATUS2_MAINCHGDROP | + MAIN_CH_STATUS2_MAINCHARGERDETDBNC)) + goto reschedule; + + msleep(CHARGER_STATUS_POLL); + } + + (void) ab8500_charger_ac_en(&di->ac_chg, 0, 0, 0); + queue_work(di->charger_wq, &di->ac_work); + + return; +reschedule: + queue_delayed_work(di->charger_wq, + &di->ac_charger_attached_work, + HZ); +} + /** * ab8500_charger_detect_usb_type_work() - work to detect USB type * @work: Pointer to the work_struct structure @@ -1983,6 +2080,8 @@ static irqreturn_t ab8500_charger_mainchunplugdet_handler(int irq, void *_di) dev_dbg(di->dev, "Main charger unplugged\n"); queue_work(di->charger_wq, &di->ac_work); + cancel_delayed_work_sync(&di->ac_charger_attached_work); + return IRQ_HANDLED; } @@ -2000,6 +2099,9 @@ static irqreturn_t ab8500_charger_mainchplugdet_handler(int irq, void *_di) dev_dbg(di->dev, "Main charger plugged\n"); queue_work(di->charger_wq, &di->ac_work); + queue_delayed_work(di->charger_wq, + &di->ac_charger_attached_work, + HZ); return IRQ_HANDLED; } @@ -2626,7 +2728,7 @@ static int __devexit ab8500_charger_remove(struct platform_device *pdev) static int __devinit ab8500_charger_probe(struct platform_device *pdev) { - int irq, i, charger_status, ret = 0; + int irq, i, charger_status, ret = 0, ch_stat; struct ab8500_platform_data *plat_data; struct ab8500_charger *di; @@ -2713,6 +2815,11 @@ static int __devinit ab8500_charger_probe(struct platform_device *pdev) INIT_DELAYED_WORK_DEFERRABLE(&di->check_usbchgnotok_work, ab8500_charger_check_usbchargernotok_work); + INIT_DELAYED_WORK(&di->ac_charger_attached_work, + ab8500_charger_ac_attached_work); + INIT_DELAYED_WORK(&di->usb_charger_attached_work, + ab8500_charger_usb_attached_work); + /* * For ABB revision 1.0 and 1.1 there is a bug in the watchdog * logic. That means we have to continously kick the charger @@ -2826,6 +2933,19 @@ static int __devinit ab8500_charger_probe(struct platform_device *pdev) platform_set_drvdata(pdev, di); + ch_stat = ab8500_charger_detect_chargers(di); + + if ((ch_stat & AC_PW_CONN) == AC_PW_CONN) { + queue_delayed_work(di->charger_wq, + &di->ac_charger_attached_work, + HZ); + } + if ((ch_stat & USB_PW_CONN) == USB_PW_CONN) { + queue_delayed_work(di->charger_wq, + &di->usb_charger_attached_work, + HZ); + } + return ret; free_irq: