From patchwork Fri Jun 8 22:36:54 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Brian Norris X-Patchwork-Id: 10455291 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id D75676037F for ; Fri, 8 Jun 2018 22:41:34 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id D310A29308 for ; Fri, 8 Jun 2018 22:41:34 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id C7D1A295AF; Fri, 8 Jun 2018 22:41:34 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-8.0 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,MAILING_LIST_MULTI,RCVD_IN_DNSWL_HI autolearn=unavailable version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 3CF2729308 for ; Fri, 8 Jun 2018 22:41:34 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753150AbeFHWlE (ORCPT ); Fri, 8 Jun 2018 18:41:04 -0400 Received: from mail-pg0-f65.google.com ([74.125.83.65]:42074 "EHLO mail-pg0-f65.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753133AbeFHWlC (ORCPT ); Fri, 8 Jun 2018 18:41:02 -0400 Received: by mail-pg0-f65.google.com with SMTP id c10-v6so1386737pgu.9 for ; Fri, 08 Jun 2018 15:41:02 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org; s=google; h=from:to:cc:subject:date:message-id; bh=+69TXg+Wsvl5slpBB6Z/uUH/Ghx+qEPNqIf1Y39kTO4=; b=m2yy1kvPnNf71aosiEzOYrHf156mNqDunGxQp5vwypmyLi7nW05QTIxIhY/zggyyVp VFOgGOmJSlyuySNJLKGAA8DFZe5v4ejlo5L2o7kO5fjw0xW3qm2f74op6XjPO5TPu44T i5soAlW3Y/esoQqnGCx6MMpG9le59Kygerpak= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id; bh=+69TXg+Wsvl5slpBB6Z/uUH/Ghx+qEPNqIf1Y39kTO4=; b=RwoH68CM1yOnHysriHt6vG2ARYIqdFHbblMWr3XFBdrCsnks8/DPz/SS1J3Rd/Abtm EoIpQcbrBL/+UUR6Fr62an7TmsRjTGokcQMJ/49M1hZLDbcAzN9d5MQkXjqKW3vmTUCL OmhBl9UW8qBZAH/ktcqTyg6wiXWkccum5lqMDE0hTj70BxFDBjbpoVf4o7VYNV3BJlnE IxR5AVG8fkLJmPNrciRJOZ12H3yjMb7HY3X6k804xcXH/UdQO3ZBkv046Klm5Xj3rJHU LrEUra9ECPd79VnPJAcCtQn50TJZz1bKZOrDN4kNvOvaVl69kw6L5BvFEo2lM35SLJP+ 6mfg== X-Gm-Message-State: APt69E1eDK7azrqDeYU6T96noCnRH/tyXTIQmICi3cJdhVHYnXu650Is uByHc1JerJAoSCjYgz8kcVC3nA== X-Google-Smtp-Source: ADUXVKI2G2PRhC9pnGvUCDJ38Oj6RhS/SMZyA5YeWCRnySCMP64mO6pjxQDQZA5l9NsmL3wY4mbBVg== X-Received: by 2002:a62:119d:: with SMTP id 29-v6mr7841815pfr.102.1528497662080; Fri, 08 Jun 2018 15:41:02 -0700 (PDT) Received: from ban.mtv.corp.google.com ([2620:0:1000:1501:bc2f:3082:9938:5d41]) by smtp.gmail.com with ESMTPSA id g4-v6sm61164550pfg.38.2018.06.08.15.41.00 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Fri, 08 Jun 2018 15:41:00 -0700 (PDT) From: Brian Norris To: Sebastian Reichel Cc: , Rob Herring , linux-pm@vger.kernel.org, devicetree@vger.kernel.org, Rhyland Klein , Alexandru Stan , Guenter Roeck , Doug Anderson , Phil Reid , Brian Norris Subject: [PATCH v4 1/2] power: supply: sbs-battery: don't assume MANUFACTURER_DATA formats Date: Fri, 8 Jun 2018 15:36:54 -0700 Message-Id: <20180608223655.33565-1-briannorris@chromium.org> X-Mailer: git-send-email 2.18.0.rc1.242.g61856ae69a-goog Sender: linux-pm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-pm@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP This driver was originally submitted for the TI BQ20Z75 battery IC (commit a7640bfa10c5 ("power_supply: Add driver for TI BQ20Z75 gas gauge IC")) and later renamed to express generic SBS support. While it's mostly true that this driver implemented a standard SBS command set, it takes liberties with the REG_MANUFACTURER_DATA register. This register is specified in the SBS spec, but it doesn't make any mention of what its actual contents are. We've sort of noticed this optionality previously, with commit 17c6d3979e5b ("sbs-battery: make writes to ManufacturerAccess optional"), where we found that some batteries NAK writes to this register. What this really means is that so far, we've just been lucky that most batteries have either been compatible with the TI chip, or else at least haven't reported highly-unexpected values. For instance, one battery I have here seems to report either 0x0000 or 0x0100 to the MANUFACTURER_ACCESS_STATUS command -- while this seems to match either Wake Up (bits[11:8] = 0000b) or Normal Discharge (bits[11:8] = 0001b) status for the TI part [1], they don't seem to actually correspond to real states (for instance, I never see 0101b = Charge, even when charging). On other batteries, I'm getting apparently random data in return, which means that occasionally, we interpret this as "battery not present" or "battery is not healthy". All in all, it seems to be a really bad idea to make assumptions about REG_MANUFACTURER_DATA, unless we already know what battery we're using. Therefore, this patch reimplements the "present" and "health" checks to the following on most SBS batteries: 1. HEALTH: report "unknown" -- I couldn't find a standard SBS command that gives us much useful here 2. PRESENT: just send a REG_STATUS command; if it succeeds, then the battery is present Also, we stop sending MANUFACTURER_ACCESS_SLEEP to non-TI parts. I have no proof that this is useful and supported. If someone explicitly provided a 'ti,bq20z75' compatible property, then we continue to use the existing TI command behaviors, and we effectively revert commit 17c6d3979e5b ("sbs-battery: make writes to ManufacturerAccess optional") to again make these commands required. [1] http://www.ti.com/lit/er/sluu265a/sluu265a.pdf Signed-off-by: Brian Norris Reviewed-by: Guenter Roeck Acked-by: Rhyland Klein Reviewed-by: Phil Reid --- v2: * don't stub out POWER_SUPPLY_PROP_PRESENT from sbs_data[] * use if/else instead of switch/case v3: * pull 'return 0' out of if/else, to satisfy braindead tooling v4: * make ManufacturerAccess non-optional for TI parts again --- drivers/power/supply/sbs-battery.c | 67 ++++++++++++++++++++++++------ 1 file changed, 54 insertions(+), 13 deletions(-) diff --git a/drivers/power/supply/sbs-battery.c b/drivers/power/supply/sbs-battery.c index 83d7b4115857..8ba6abf584de 100644 --- a/drivers/power/supply/sbs-battery.c +++ b/drivers/power/supply/sbs-battery.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -156,6 +157,9 @@ static enum power_supply_property sbs_properties[] = { POWER_SUPPLY_PROP_MODEL_NAME }; +/* Supports special manufacturer commands from TI BQ20Z75 IC. */ +#define SBS_FLAGS_TI_BQ20Z75 BIT(0) + struct sbs_info { struct i2c_client *client; struct power_supply *power_supply; @@ -168,6 +172,7 @@ struct sbs_info { u32 poll_retry_count; struct delayed_work work; struct mutex mode_lock; + u32 flags; }; static char model_name[I2C_SMBUS_BLOCK_MAX + 1]; @@ -315,17 +320,41 @@ static int sbs_status_correct(struct i2c_client *client, int *intval) static int sbs_get_battery_presence_and_health( struct i2c_client *client, enum power_supply_property psp, union power_supply_propval *val) +{ + int ret; + + if (psp == POWER_SUPPLY_PROP_PRESENT) { + /* Dummy command; if it succeeds, battery is present. */ + ret = sbs_read_word_data(client, sbs_data[REG_STATUS].addr); + if (ret < 0) + val->intval = 0; /* battery disconnected */ + else + val->intval = 1; /* battery present */ + } else { /* POWER_SUPPLY_PROP_HEALTH */ + /* SBS spec doesn't have a general health command. */ + val->intval = POWER_SUPPLY_HEALTH_UNKNOWN; + } + + return 0; +} + +static int sbs_get_ti_battery_presence_and_health( + struct i2c_client *client, enum power_supply_property psp, + union power_supply_propval *val) { s32 ret; /* * Write to ManufacturerAccess with ManufacturerAccess command - * and then read the status. Do not check for error on the write - * since not all batteries implement write access to this command, - * while others mandate it. + * and then read the status. */ - sbs_write_word_data(client, sbs_data[REG_MANUFACTURER_DATA].addr, - MANUFACTURER_ACCESS_STATUS); + ret = sbs_write_word_data(client, sbs_data[REG_MANUFACTURER_DATA].addr, + MANUFACTURER_ACCESS_STATUS); + if (ret < 0) { + if (psp == POWER_SUPPLY_PROP_PRESENT) + val->intval = 0; /* battery removed */ + return ret; + } ret = sbs_read_word_data(client, sbs_data[REG_MANUFACTURER_DATA].addr); if (ret < 0) { @@ -600,7 +629,12 @@ static int sbs_get_property(struct power_supply *psy, switch (psp) { case POWER_SUPPLY_PROP_PRESENT: case POWER_SUPPLY_PROP_HEALTH: - ret = sbs_get_battery_presence_and_health(client, psp, val); + if (client->flags & SBS_FLAGS_TI_BQ20Z75) + ret = sbs_get_ti_battery_presence_and_health(client, + psp, val); + else + ret = sbs_get_battery_presence_and_health(client, psp, + val); if (psp == POWER_SUPPLY_PROP_PRESENT) return 0; break; @@ -806,6 +840,7 @@ static int sbs_probe(struct i2c_client *client, if (!chip) return -ENOMEM; + chip->flags = (u32)(uintptr_t)of_device_get_match_data(&client->dev); chip->client = client; chip->enable_detection = false; psy_cfg.of_node = client->dev.of_node; @@ -911,16 +946,19 @@ static int sbs_suspend(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); struct sbs_info *chip = i2c_get_clientdata(client); + int ret; if (chip->poll_time > 0) cancel_delayed_work_sync(&chip->work); - /* - * Write to manufacturer access with sleep command. - * Support is manufacturer dependend, so ignore errors. - */ - sbs_write_word_data(client, sbs_data[REG_MANUFACTURER_DATA].addr, - MANUFACTURER_ACCESS_SLEEP); + if (chip->flags & SBS_FLAGS_TI_BQ20Z75) { + /* Write to manufacturer access with sleep command. */ + ret = sbs_write_word_data(client, + sbs_data[REG_MANUFACTURER_DATA].addr, + MANUFACTURER_ACCESS_SLEEP); + if (chip->is_present && ret < 0) + return ret; + } return 0; } @@ -941,7 +979,10 @@ MODULE_DEVICE_TABLE(i2c, sbs_id); static const struct of_device_id sbs_dt_ids[] = { { .compatible = "sbs,sbs-battery" }, - { .compatible = "ti,bq20z75" }, + { + .compatible = "ti,bq20z75", + .data = (void *)SBS_FLAGS_TI_BQ20Z75, + }, { } }; MODULE_DEVICE_TABLE(of, sbs_dt_ids);