From patchwork Tue Apr 15 20:14:36 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Doug Anderson X-Patchwork-Id: 3995411 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 1EABEBFF02 for ; Tue, 15 Apr 2014 20:17:00 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id F325320259 for ; Tue, 15 Apr 2014 20:16:58 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id BEDB120179 for ; Tue, 15 Apr 2014 20:16:57 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753322AbaDOUQ4 (ORCPT ); Tue, 15 Apr 2014 16:16:56 -0400 Received: from mail-qa0-f73.google.com ([209.85.216.73]:42362 "EHLO mail-qa0-f73.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751446AbaDOUOy (ORCPT ); Tue, 15 Apr 2014 16:14:54 -0400 Received: by mail-qa0-f73.google.com with SMTP id hw13so1468086qab.2 for ; Tue, 15 Apr 2014 13:14:54 -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=r2LxSzQTauKyj8dAbPqzFQVjDuRFpwrsi3EC0+PLe6c=; b=IAJCgamkDqNvdvBhtgd8C7gyeunlx7rNu063109NaFct98H6+L0b5TXV/UaPOkNfnn WqEIiweCqeiEF2QKBbrjD/7vPL+WTHI8YDTQYVtQk5C/3aO+TvVKpokc+i6pLDz82c4j jvRY/cu+53yewmYVXzlzKzi2FiaUzzKGrd8YyAf6w11VLJpcApwzT5BKeOckleR++uxE qH+Pdan2QjjxqrvqsGA/9DN/seq/TyqdpOGVHS1AlT1eTusDnq2QzG6L99B1R2ZcXQ3s v6ie18FdttwSMu2s6bXsvj8yqWgsliGvFQsIY4RHYm6FVxmfJnnIXX9o/7v3YuVev5IV 6R3A== X-Gm-Message-State: ALoCoQnIv9tL0VkS58SRtBi9W60+N1miBfiRljyOT3NVL03VWmJfjwp4pj8akJDPo0HiEnjCy0r/xkhxTbf6jNCKV1r71CAVx/RXys6wUh8MaU+9ZOlEKbfkRm0LzbcVh2iU0H7h7YZrJaz++JSKdJuXD0TfzmgWuznVW5svABxDj5LZtmMUouDZAwDfHvpjkVZzlj010hK+GNXx1Vh1G19KeODaNtWIOA== X-Received: by 10.236.159.99 with SMTP id r63mr1725590yhk.7.1397592893862; Tue, 15 Apr 2014 13:14:53 -0700 (PDT) Received: from corp2gmr1-2.hot.corp.google.com (corp2gmr1-2.hot.corp.google.com [172.24.189.93]) by gmr-mx.google.com with ESMTPS id x22si2835316yhd.5.2014.04.15.13.14.53 for (version=TLSv1.1 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Tue, 15 Apr 2014 13:14:53 -0700 (PDT) Received: from tictac.mtv.corp.google.com (tictac.mtv.corp.google.com [172.22.72.141]) by corp2gmr1-2.hot.corp.google.com (Postfix) with ESMTP id 6CDF95A42C8; Tue, 15 Apr 2014 13:14:53 -0700 (PDT) Received: by tictac.mtv.corp.google.com (Postfix, from userid 121310) id 12C6B80967; Tue, 15 Apr 2014 13:14:53 -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 , Simon Glass , Michael Spang , Sean Paul , Rob Herring , Pawel Moll , Mark Rutland , Ian Campbell , Kumar Gala , Randy Dunlap , Liam Girdwood , Mark Brown , Samuel Ortiz , Lee Jones , devicetree@vger.kernel.org, linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH 3/3] regulator: tps65090: Make FETs more reliable Date: Tue, 15 Apr 2014 13:14:36 -0700 Message-Id: <1397592876-5741-4-git-send-email-dianders@chromium.org> X-Mailer: git-send-email 1.9.1.423.g4596e3a In-Reply-To: <1397592876-5741-1-git-send-email-dianders@chromium.org> References: <1397592876-5741-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=unavailable 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 An issue was discovered with tps65090 where sometimes the FETs wouldn't actually turn on when requested. The most problematic FET was the one use for the LCD backlight on the Samsung ARM Chromebook (FET1). Problems were especially prevalent when the device was plugged in to AC power, making the backlight voltage higher. Mitigate the problem by: * Allow setting the overcurrent wait time so devices with this problem can set it to the max. * Add retry logic on enables of FETs. Signed-off-by: Doug Anderson Signed-off-by: Simon Glass Signed-off-by: Michael Spang Signed-off-by: Sean Paul --- .../devicetree/bindings/regulator/tps65090.txt | 4 + drivers/regulator/tps65090-regulator.c | 197 +++++++++++++++++++-- include/linux/mfd/tps65090.h | 5 + 3 files changed, 194 insertions(+), 12 deletions(-) diff --git a/Documentation/devicetree/bindings/regulator/tps65090.txt b/Documentation/devicetree/bindings/regulator/tps65090.txt index 313a60b..34098023 100644 --- a/Documentation/devicetree/bindings/regulator/tps65090.txt +++ b/Documentation/devicetree/bindings/regulator/tps65090.txt @@ -21,6 +21,10 @@ Optional properties: number should be provided. If it is externally controlled and no GPIO entry then driver will just configure this rails as external control and will not provide any enable/disable APIs. +- ti,overcurrent-wait: This is applicable to FET registers, which have a + poorly defined "overcurrent wait" field. If this property is present it + should be between 0 - 3. If this property isn't present we won't touch the + "overcurrent wait" field and we'll leave it to the BIOS/EC to deal with. Each regulator is defined using the standard binding for regulators. diff --git a/drivers/regulator/tps65090-regulator.c b/drivers/regulator/tps65090-regulator.c index 2e92ef6..e8d1c62 100644 --- a/drivers/regulator/tps65090-regulator.c +++ b/drivers/regulator/tps65090-regulator.c @@ -17,6 +17,7 @@ */ #include +#include #include #include #include @@ -28,21 +29,186 @@ #include #include +#define MAX_CTRL_READ_TRIES 5 +#define MAX_FET_ENABLE_TRIES 1000 + +#define CTRL_EN_BIT 0 /* Regulator enable bit, active high */ +#define CTRL_WT_BIT 2 /* Regulator wait time 0 bit */ +#define CTRL_PG_BIT 4 /* Regulator power good bit, 1=good */ +#define CTRL_TO_BIT 7 /* Regulator timeout bit, 1=wait */ + +#define MAX_OVERCURRENT_WAIT 3 /* Overcurrent wait must be less than this */ + +/** + * struct tps65090_regulator - Per-regulator data for a tps65090 regulator + * + * @dev: Pointer to our device. + * @desc: The struct regulator_desc for the regulator. + * @rdev: The struct regulator_dev for the regulator. + * @overcurrent_wait_valid: True if overcurrent_wait is valid. + * @overcurrent_wait: For FETs, the value to put in the WTFET bitfield. + */ + struct tps65090_regulator { struct device *dev; struct regulator_desc *desc; struct regulator_dev *rdev; + bool overcurrent_wait_valid; + int overcurrent_wait; }; static struct regulator_ops tps65090_ext_control_ops = { }; -static struct regulator_ops tps65090_reg_contol_ops = { +/** + * tps65090_fet_is_enabled - Tell if a fet is enabled + * + * @rdev: Regulator device + * @return true if the fet is enabled and its power is good; false otherwise. + */ +static int tps65090_fet_is_enabled(struct regulator_dev *rdev) +{ + unsigned int control; + unsigned int expected = rdev->desc->enable_mask | BIT(CTRL_PG_BIT); + int ret; + + ret = regmap_read(rdev->regmap, rdev->desc->enable_reg, &control); + if (ret != 0) + return ret; + + return (control & expected) == expected; +} + +/** + * tps65090_reg_set_overcurrent_wait - Setup overcurrent wait + * + * This will set the overcurrent wait time based on what's in the regulator + * info. + * + * @rdev: Regulator device + * @return 0 if no error, non-zero if there was an error writing the register. + */ +static int tps65090_reg_set_overcurrent_wait(struct regulator_dev *rdev) +{ + struct tps65090_regulator *ri = rdev_get_drvdata(rdev); + int ret; + + if (!ri->overcurrent_wait_valid) + return 0; + + ret = regmap_update_bits(rdev->regmap, rdev->desc->enable_reg, + MAX_OVERCURRENT_WAIT << CTRL_WT_BIT, + ri->overcurrent_wait << CTRL_WT_BIT); + if (ret) { + dev_err(&rdev->dev, "Error updating overcurrent wait %#x\n", + rdev->desc->enable_reg); + } + + return ret; +} + +/** + * tps6090_try_enable_fet - Try to enable a FET + * + * @rdev: Regulator device + * @return 0 if ok, -ENOTRECOVERABLE if the FET power good bit did not get set, + * or some other -ve value if another error occurred (e.g. i2c error) + */ +static int tps6090_try_enable_fet(struct regulator_dev *rdev) +{ + unsigned int control; + int ret, i; + + ret = regmap_update_bits(rdev->regmap, rdev->desc->enable_reg, + rdev->desc->enable_mask, + rdev->desc->enable_mask); + if (ret < 0) { + dev_err(&rdev->dev, "Error in updating reg %#x\n", + rdev->desc->enable_reg); + return ret; + } + + for (i = 0; i < MAX_CTRL_READ_TRIES; i++) { + ret = regmap_read(rdev->regmap, rdev->desc->enable_reg, + &control); + if (ret < 0) + return ret; + + if (!(control & BIT(CTRL_TO_BIT))) + break; + + usleep_range(1000, 1500); + } + if (!(control & BIT(CTRL_PG_BIT))) + return -ENOTRECOVERABLE; + + return 0; +} + +/** + * tps65090_fet_enable - Enable a FET, trying a few times if it fails + * + * Some versions of the tps65090 have issues when turning on the FETs. + * This function goes through several steps to ensure the best chance of the + * FET going on. Specifically: + * - We'll make sure that we bump the "overcurrent wait" to the maximum, which + * increases the chances that we'll turn on properly. + * - We'll retry turning the FET on multiple times (turning off in between). + * + * @rdev: Regulator device + * @return 0 if ok, non-zero if it fails. + */ +static int tps65090_fet_enable(struct regulator_dev *rdev) +{ + int ret, tries; + + ret = tps65090_reg_set_overcurrent_wait(rdev); + if (ret) + goto err; + + /* + * Try enabling multiple times until we succeed since sometimes the + * first try times out. + */ + for (tries = 0; ; tries++) { + ret = tps6090_try_enable_fet(rdev); + if (!ret) + break; + if (ret != -ENOTRECOVERABLE || tries == MAX_FET_ENABLE_TRIES) + goto err; + + /* Try turning the FET off (and then on again) */ + ret = regmap_update_bits(rdev->regmap, rdev->desc->enable_reg, + rdev->desc->enable_mask, 0); + if (ret) + goto err; + } + + if (tries) { + dev_warn(&rdev->dev, "reg %#x enable ok after %d tries\n", + rdev->desc->enable_reg, tries); + } + + return 0; +err: + dev_warn(&rdev->dev, "reg %#x enable failed\n", rdev->desc->enable_reg); + WARN_ON(1); + + return ret; +} + +static struct regulator_ops tps65090_reg_control_ops = { .enable = regulator_enable_regmap, .disable = regulator_disable_regmap, .is_enabled = regulator_is_enabled_regmap, }; +static struct regulator_ops tps65090_fet_control_ops = { + .enable = tps65090_fet_enable, + .disable = regulator_disable_regmap, + .is_enabled = tps65090_fet_is_enabled, +}; + static struct regulator_ops tps65090_ldo_ops = { }; @@ -53,22 +219,22 @@ static struct regulator_ops tps65090_ldo_ops = { .id = TPS65090_REGULATOR_##_id, \ .ops = &_ops, \ .enable_reg = _en_reg, \ - .enable_mask = BIT(0), \ + .enable_mask = BIT(CTRL_EN_BIT), \ .type = REGULATOR_VOLTAGE, \ .owner = THIS_MODULE, \ } static struct regulator_desc tps65090_regulator_desc[] = { - tps65090_REG_DESC(DCDC1, "vsys1", 0x0C, tps65090_reg_contol_ops), - tps65090_REG_DESC(DCDC2, "vsys2", 0x0D, tps65090_reg_contol_ops), - tps65090_REG_DESC(DCDC3, "vsys3", 0x0E, tps65090_reg_contol_ops), - tps65090_REG_DESC(FET1, "infet1", 0x0F, tps65090_reg_contol_ops), - tps65090_REG_DESC(FET2, "infet2", 0x10, tps65090_reg_contol_ops), - tps65090_REG_DESC(FET3, "infet3", 0x11, tps65090_reg_contol_ops), - tps65090_REG_DESC(FET4, "infet4", 0x12, tps65090_reg_contol_ops), - tps65090_REG_DESC(FET5, "infet5", 0x13, tps65090_reg_contol_ops), - tps65090_REG_DESC(FET6, "infet6", 0x14, tps65090_reg_contol_ops), - tps65090_REG_DESC(FET7, "infet7", 0x15, tps65090_reg_contol_ops), + tps65090_REG_DESC(DCDC1, "vsys1", 0x0C, tps65090_reg_control_ops), + tps65090_REG_DESC(DCDC2, "vsys2", 0x0D, tps65090_reg_control_ops), + tps65090_REG_DESC(DCDC3, "vsys3", 0x0E, tps65090_reg_control_ops), + tps65090_REG_DESC(FET1, "infet1", 0x0F, tps65090_fet_control_ops), + tps65090_REG_DESC(FET2, "infet2", 0x10, tps65090_fet_control_ops), + tps65090_REG_DESC(FET3, "infet3", 0x11, tps65090_fet_control_ops), + tps65090_REG_DESC(FET4, "infet4", 0x12, tps65090_fet_control_ops), + tps65090_REG_DESC(FET5, "infet5", 0x13, tps65090_fet_control_ops), + tps65090_REG_DESC(FET6, "infet6", 0x14, tps65090_fet_control_ops), + tps65090_REG_DESC(FET7, "infet7", 0x15, tps65090_fet_control_ops), tps65090_REG_DESC(LDO1, "vsys-l1", 0, tps65090_ldo_ops), tps65090_REG_DESC(LDO2, "vsys-l2", 0, tps65090_ldo_ops), }; @@ -209,6 +375,11 @@ static struct tps65090_platform_data *tps65090_parse_dt_reg_data( rpdata->gpio = of_get_named_gpio(np, "dcdc-ext-control-gpios", 0); + if (of_property_read_u32(tps65090_matches[idx].of_node, + "ti,overcurrent-wait", + &rpdata->overcurrent_wait) == 0) + rpdata->overcurrent_wait_valid = true; + tps65090_pdata->reg_pdata[idx] = rpdata; } return tps65090_pdata; @@ -258,6 +429,8 @@ static int tps65090_regulator_probe(struct platform_device *pdev) ri = &pmic[num]; ri->dev = &pdev->dev; ri->desc = &tps65090_regulator_desc[num]; + ri->overcurrent_wait_valid = tps_pdata->overcurrent_wait_valid; + ri->overcurrent_wait = tps_pdata->overcurrent_wait; /* * TPS5090 DCDC support the control from external digital input. diff --git a/include/linux/mfd/tps65090.h b/include/linux/mfd/tps65090.h index 3f43069..f25adfa 100644 --- a/include/linux/mfd/tps65090.h +++ b/include/linux/mfd/tps65090.h @@ -78,11 +78,16 @@ struct tps65090 { * DCDC1, DCDC2 and DCDC3. * @gpio: Gpio number if external control is enabled and controlled through * gpio. + * @overcurrent_wait_valid: True if the overcurrent_wait should be applied. + * @overcurrent_wait: Value to set as the overcurrent wait time. This is the + * actual bitfield value, not a time in ms (valid value are 0 - 3). */ struct tps65090_regulator_plat_data { struct regulator_init_data *reg_init_data; bool enable_ext_control; int gpio; + bool overcurrent_wait_valid; + int overcurrent_wait; }; struct tps65090_platform_data {