From patchwork Tue Apr 2 00:04:12 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 232843 X-Patchwork-Delegate: promsoft@gmail.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from theia.denx.de (theia.denx.de [85.214.87.163]) by ozlabs.org (Postfix) with ESMTP id D92252C012C for ; Tue, 2 Apr 2013 11:06:34 +1100 (EST) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 67C2A4A101; Tue, 2 Apr 2013 02:06:28 +0200 (CEST) X-Virus-Scanned: Debian amavisd-new at theia.denx.de Received: from theia.denx.de ([127.0.0.1]) by localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id D1KT34GRH7qP; Tue, 2 Apr 2013 02:06:28 +0200 (CEST) Received: from theia.denx.de (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 7CB0D4A11B; Tue, 2 Apr 2013 02:06:21 +0200 (CEST) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 1B1124A101 for ; Tue, 2 Apr 2013 02:06:18 +0200 (CEST) X-Virus-Scanned: Debian amavisd-new at theia.denx.de Received: from theia.denx.de ([127.0.0.1]) by localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id gK9byRorA7QA for ; Tue, 2 Apr 2013 02:06:14 +0200 (CEST) X-policyd-weight: NOT_IN_SBL_XBL_SPAMHAUS=-1.5 NOT_IN_SPAMCOP=-1.5 NOT_IN_BL_NJABL=-1.5 (only DNSBL check requested) Received: from mail-bk0-f74.google.com (mail-bk0-f74.google.com [209.85.214.74]) by theia.denx.de (Postfix) with ESMTPS id 1B1D94A105 for ; Tue, 2 Apr 2013 02:06:12 +0200 (CEST) Received: by mail-bk0-f74.google.com with SMTP id y8so203621bkt.1 for ; Mon, 01 Apr 2013 17:06:12 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20120113; h=x-received:from:to:cc:subject:date:message-id:x-mailer:in-reply-to :references:x-gm-message-state; bh=PcbxQgTX6XYiNdYPt472pRP3FfYRJnNEKIReSwI/HuI=; b=mPP3i8LQa/dMextvCFq9wPK2GliEfUHouS1G0dGXEVTyY2IAHszJJ386nLqZq5C8sl Y4To/Xd8/7yRy5QxU0ZIWbEf8afkr/LPxnReNX/vzKzr3FNveT609UNt1pNl53/wSxlw 2PX+O4srfF/hc9SGN6ZO8Ertbp3uKFn7Tw2y/LjCZlnvhOJkFUu8DSDdzSYrijlo27CH 6GlWsPRu3TMlvr2MRsUpPb0ZC0xalX3dd+4nlTBNKQYOpEw+H6KK/OZfCb+1Ry1mc+XL f+VPzjIO7CKwkr12Y/TadUB16xNCqJlmt+8BZPkZ8rSiRb3ntr7t3VqG1JNcfIC2pUtz hMrw== X-Received: by 10.14.221.3 with SMTP id q3mr17668221eep.5.1364861172310; Mon, 01 Apr 2013 17:06:12 -0700 (PDT) Received: from corp2gmr1-1.eem.corp.google.com (corp2gmr1-1.eem.corp.google.com [172.25.138.99]) by gmr-mx.google.com with ESMTPS id 6si4072764eej.0.2013.04.01.17.06.12 (version=TLSv1.1 cipher=AES128-SHA bits=128/128); Mon, 01 Apr 2013 17:06:12 -0700 (PDT) Received: from kaka.mtv.corp.google.com (kaka.mtv.corp.google.com [172.22.83.1]) by corp2gmr1-1.eem.corp.google.com (Postfix) with ESMTP id B0ED11CA1D3; Mon, 1 Apr 2013 17:06:11 -0700 (PDT) Received: by kaka.mtv.corp.google.com (Postfix, from userid 121222) id 1602A1607D0; Mon, 1 Apr 2013 17:06:11 -0700 (PDT) From: Simon Glass To: U-Boot Mailing List Date: Mon, 1 Apr 2013 17:04:12 -0700 Message-Id: <1364861055-21670-2-git-send-email-sjg@chromium.org> X-Mailer: git-send-email 1.8.1.3 In-Reply-To: <1364861055-21670-1-git-send-email-sjg@chromium.org> References: <1364861055-21670-1-git-send-email-sjg@chromium.org> X-Gm-Message-State: ALoCoQnDNtVnJrENG8n5fOZ1T5VlVGmStezbElggl4BNKcTyFNHtBuYNLKq7hUFtbKMw5vgEO71pBNMsgKPUQ6xzEPfJYokZ6KPkKD4j2ppj+qXAR0cGe/kj4U4O7oTc/Iik96CzwVbQrQHUk4qTeemCJ2mcXoq64pQ+L5jUJvbmolD8bcSYIVv1ReoLYC/LxTyiavl3U7+x Cc: Hatim Ali , Aaron Durbin , Vincent Palatin , u-boot-review@google.com, Rong Chang , Katie Roberts-Hoffman , Sean Paul , Rajeshwari Shinde , Tom Rini Subject: [U-Boot] [PATCH 1/4] power: Add support for TPS65090 PMU chip. X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.11 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: u-boot-bounces@lists.denx.de Errors-To: u-boot-bounces@lists.denx.de From: Tom Wai-Hong Tam This adds driver support for the TPS65090 PMU. Support includes hooking into the pmic infrastructure so that the pmic commands can be used on the console. The TPS65090 supports the following functionality: - fet enable/disable/querying - getting and setting of charge state Even though it is connected to the pmic infrastructure it does not hook into the pmic charging charging infrastructure. Signed-off-by: Tom Wai-Hong Tam Signed-off-by: Simon Glass Signed-off-by: Hatim Ali Signed-off-by: Katie Roberts-Hoffman Signed-off-by: Rong Chang Signed-off-by: Sean Paul Signed-off-by: Vincent Palatin Signed-off-by: Aaron Durbin --- doc/device-tree-bindings/power/tps65090.txt | 21 ++ drivers/power/pmic/Makefile | 1 + drivers/power/pmic/pmic_tps65090.c | 296 ++++++++++++++++++++++++++++ include/fdtdec.h | 1 + include/power/tps65090_pmic.h | 83 ++++++++ lib/fdtdec.c | 1 + 6 files changed, 403 insertions(+) create mode 100644 doc/device-tree-bindings/power/tps65090.txt create mode 100644 drivers/power/pmic/pmic_tps65090.c create mode 100644 include/power/tps65090_pmic.h diff --git a/doc/device-tree-bindings/power/tps65090.txt b/doc/device-tree-bindings/power/tps65090.txt new file mode 100644 index 0000000..6a8a884 --- /dev/null +++ b/doc/device-tree-bindings/power/tps65090.txt @@ -0,0 +1,21 @@ +TPSchrome binding +================= + +The device tree node which describes the operation of the Texas Instrument +TPS65090 power regulator chip is as follows: + +Required properties : +- compatible : "ti,tps65090" +- reg : slave address on the i2c bus + +The tps65090 node should appear as a subnode of the i2c bus that connects it. + +Example +======= + + i2c@12ca0000 { + power-regulator@48 { + compatible = "ti,tps65090" + reg = <0x48>; + }; + }; diff --git a/drivers/power/pmic/Makefile b/drivers/power/pmic/Makefile index 14d426f..a66d940 100644 --- a/drivers/power/pmic/Makefile +++ b/drivers/power/pmic/Makefile @@ -29,6 +29,7 @@ COBJS-$(CONFIG_POWER_MAX8998) += pmic_max8998.o COBJS-$(CONFIG_POWER_MAX8997) += pmic_max8997.o COBJS-$(CONFIG_POWER_MUIC_MAX8997) += muic_max8997.o COBJS-$(CONFIG_POWER_MAX77686) += pmic_max77686.o +COBJS-$(CONFIG_POWER_TPS65090) += pmic_tps65090.o COBJS := $(COBJS-y) SRCS := $(COBJS:.o=.c) diff --git a/drivers/power/pmic/pmic_tps65090.c b/drivers/power/pmic/pmic_tps65090.c new file mode 100644 index 0000000..ef9f911 --- /dev/null +++ b/drivers/power/pmic/pmic_tps65090.c @@ -0,0 +1,296 @@ +/* + * Copyright (c) 2012 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + */ + +#include +#include +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +#define TPS65090_NAME "TPS65090_PMIC" + +/* TPS65090 register addresses */ +enum { + REG_CG_CTRL0 = 4, + REG_CG_STATUS1 = 0xa, + REG_FET1_CTRL = 0x0f, + REG_FET2_CTRL, + REG_FET3_CTRL, + REG_FET4_CTRL, + REG_FET5_CTRL, + REG_FET6_CTRL, + REG_FET7_CTRL, + TPS65090_NUM_REGS, +}; + +enum { + CG_CTRL0_ENC_MASK = 0x01, + + MAX_FET_NUM = 7, + MAX_CTRL_READ_TRIES = 5, + + /* TPS65090 FET_CTRL register values */ + FET_CTRL_TOFET = 1 << 7, /* Timeout, startup, overload */ + FET_CTRL_PGFET = 1 << 4, /* Power good for FET status */ + FET_CTRL_WAIT = 3 << 2, /* Overcurrent timeout max */ + FET_CTRL_ADENFET = 1 << 1, /* Enable output auto discharge */ + FET_CTRL_ENFET = 1 << 0, /* Enable FET */ +}; + +/** + * Checks for a valid FET number + * + * @param fet_id FET number to check + * @return 0 if ok, -1 if FET value is out of range + */ +static int tps65090_check_fet(unsigned int fet_id) +{ + if (fet_id == 0 || fet_id > MAX_FET_NUM) { + debug("parameter fet_id is out of range, %u not in 1 ~ %u\n", + fet_id, MAX_FET_NUM); + return -1; + } + + return 0; +} + +/** + * Set the power state for a FET + * + * @param pmic pmic structure for the tps65090 + * @param fet_id Fet number to set (1..MAX_FET_NUM) + * @param set 1 to power on FET, 0 to power off + * @return FET_ERR_COMMS if we got a comms error, FET_ERR_NOT_READY if the + * FET failed to change state. If all is ok, returns 0. + */ +static int tps65090_fet_set(struct pmic *pmic, int fet_id, int set) +{ + int retry; + u32 reg, value; + + value = FET_CTRL_ADENFET | FET_CTRL_WAIT; + if (set) + value |= FET_CTRL_ENFET; + + if (pmic_reg_write(pmic, REG_FET1_CTRL + fet_id - 1, value)) + return FET_ERR_COMMS; + /* Try reading until we get a result */ + for (retry = 0; retry < MAX_CTRL_READ_TRIES; retry++) { + if (pmic_reg_read(pmic, REG_FET1_CTRL + fet_id - 1, ®)) + return FET_ERR_COMMS; + + /* Check that the fet went into the expected state */ + if (!!(reg & FET_CTRL_PGFET) == set) + return 0; + + /* If we got a timeout, there is no point in waiting longer */ + if (reg & FET_CTRL_TOFET) + break; + + mdelay(1); + } + + debug("FET %d: Power good should have set to %d but reg=%#02x\n", + fet_id, set, reg); + return FET_ERR_NOT_READY; +} + +int tps65090_fet_enable(unsigned int fet_id) +{ + int loops; + ulong start; + struct pmic *pmic; + int ret = 0; + + if (tps65090_check_fet(fet_id)) + return -1; + + pmic = pmic_get(TPS65090_NAME); + if (pmic == NULL) + return -1; + + start = get_timer(0); + for (loops = 0;; loops++) { + ret = tps65090_fet_set(pmic, fet_id, 1); + if (!ret) + break; + + if (get_timer(start) > 100) + break; + + /* Turn it off and try again until we time out */ + tps65090_fet_set(pmic, fet_id, 0); + } + + if (ret) { + debug("%s: FET%d failed to power on: time=%lums, loops=%d\n", + __func__, fet_id, get_timer(start), loops); + } else if (loops) { + debug("%s: FET%d powered on after %lums, loops=%d\n", + __func__, fet_id, get_timer(start), loops); + } + /* + * Unfortunately, there are some conditions where the power + * good bit will be 0, but the fet still comes up. One such + * case occurs with the lcd backlight. We'll just return 0 here + * and assume that the fet will eventually come up. + */ + if (ret == FET_ERR_NOT_READY) + ret = 0; + + return ret; +} + +int tps65090_fet_disable(unsigned int fet_id) +{ + int ret; + struct pmic *pmic; + + if (tps65090_check_fet(fet_id)) + return -1; + + pmic = pmic_get(TPS65090_NAME); + if (pmic == NULL) + return -1; + ret = tps65090_fet_set(pmic, fet_id, 0); + + return ret; +} + +int tps65090_fet_is_enabled(unsigned int fet_id) +{ + u32 reg; + int ret; + struct pmic *pmic; + + if (tps65090_check_fet(fet_id)) + return -1; + pmic = pmic_get(TPS65090_NAME); + if (pmic == NULL) + return -1; + ret = pmic_reg_read(pmic, REG_FET1_CTRL + fet_id - 1, ®); + if (ret) { + debug("fail to read FET%u_CTRL register over I2C", fet_id); + return -2; + } + + return reg & FET_CTRL_ENFET; +} + +int tps65090_get_charging(void) +{ + u32 val; + int ret; + struct pmic *pmic; + + pmic = pmic_get(TPS65090_NAME); + if (pmic == NULL) + return -1; + ret = pmic_reg_read(pmic, REG_CG_CTRL0, &val); + if (ret) + return ret; + return val & CG_CTRL0_ENC_MASK ? 1 : 0; +} + +int tps65090_set_charge_enable(int enable) +{ + u32 val; + int ret; + struct pmic *pmic; + + pmic = pmic_get(TPS65090_NAME); + if (pmic == NULL) + return -1; + + ret = pmic_reg_read(pmic, REG_CG_CTRL0, &val); + if (!ret) { + if (enable) + val |= CG_CTRL0_ENC_MASK; + else + val &= ~CG_CTRL0_ENC_MASK; + ret = pmic_reg_write(pmic, REG_CG_CTRL0, val); + } + if (ret) { + debug("%s: Failed to read/write register\n", __func__); + return ret; + } + return 0; +} + +int tps65090_get_status(void) +{ + u32 val; + int ret; + struct pmic *pmic; + + pmic = pmic_get(TPS65090_NAME); + + if (pmic == NULL) + return -1; + ret = pmic_reg_read(pmic, REG_CG_STATUS1, &val); + if (ret) + return ret; + return val; +} + +int tps65090_init(void) +{ + int bus; + int addr; + struct pmic *p; + +#ifdef CONFIG_OF_CONTROL + const void *blob = gd->fdt_blob; + int node, parent; + + node = fdtdec_next_compatible(blob, 0, COMPAT_TI_TPS65090); + if (node < 0) { + debug("PMIC: No node for PMIC Chip in device tree\n"); + debug("node = %d\n", node); + return -ENODEV; + } + + parent = fdt_parent_offset(blob, node); + if (parent < 0) { + debug("%s: Cannot find node parent\n", __func__); + return -1; + } + + bus = i2c_get_bus_num_fdt(parent); + if (p->bus < 0) { + debug("%s: Cannot find I2C bus\n", __func__); + return -1; + } + addr = fdtdec_get_int(blob, node, "reg", TPS65090_I2C_ADDR); +#else + bus = CONFIG_POWER_TPS65090_BUS; + addr = TPS65090_I2C_ADDR; +#endif + p = pmic_alloc(); + + if (!p) { + printf("%s: POWER allocation error!\n", __func__); + return -ENOMEM; + } + + p->name = TPS65090_NAME; + p->bus = bus; + p->interface = PMIC_I2C; + p->number_of_regs = TPS65090_NUM_REGS; + p->hw.i2c.addr = addr; + p->hw.i2c.tx_num = 1; + + puts("TPS65090 PMIC init\n"); + + return 0; +} diff --git a/include/fdtdec.h b/include/fdtdec.h index e7e3ff9..92506fc 100644 --- a/include/fdtdec.h +++ b/include/fdtdec.h @@ -90,6 +90,7 @@ enum fdt_compat_id { COMPAT_MAXIM_MAX77686_PMIC, /* MAX77686 PMIC */ COMPAT_GENERIC_SPI_FLASH, /* Generic SPI Flash chip */ COMPAT_MAXIM_98095_CODEC, /* MAX98095 Codec */ + COMPAT_TI_TPS65090, /* Texas Instrument TPS65090 */ COMPAT_COUNT, }; diff --git a/include/power/tps65090_pmic.h b/include/power/tps65090_pmic.h new file mode 100644 index 0000000..fde30eb --- /dev/null +++ b/include/power/tps65090_pmic.h @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2012 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + */ + +#ifndef __TPS65090_PMIC_H_ +#define __TPS65090_PMIC_H_ + +/* I2C device address for TPS65090 PMU */ +#define TPS65090_I2C_ADDR 0x48 + +enum { + /* Status register fields */ + TPS65090_ST1_OTC = 1 << 0, + TPS65090_ST1_OCC = 1 << 1, + TPS65090_ST1_STATE_SHIFT = 4, + TPS65090_ST1_STATE_MASK = 0xf << TPS65090_ST1_STATE_SHIFT, +}; + +/* FET errors */ +enum { + FET_ERR_COMMS = -1, /* FET comms error */ + FET_ERR_NOT_READY = -2, /* FET is not yet ready - retry */ +}; + +/** + * Enable FET + * + * @param fet_id FET ID, value between 1 and 7 + * @return 0 on success, non-0 on failure + */ +int tps65090_fet_enable(unsigned int fet_id); + +/** + * Disable FET + * + * @param fet_id FET ID, value between 1 and 7 + * @return 0 on success, non-0 on failure + */ +int tps65090_fet_disable(unsigned int fet_id); + +/** + * Is FET enabled? + * + * @param fet_id FET ID, value between 1 and 7 + * @return 1 enabled, 0 disabled, negative value on failure + */ +int tps65090_fet_is_enabled(unsigned int fet_id); + +/** + * Enable / disable the battery charger + * + * @param enable 0 to disable charging, non-zero to enable + */ +int tps65090_set_charge_enable(int enable); + +/** + * Check whether we have enabled battery charging + * + * @return 1 if enabled, 0 if disabled + */ +int tps65090_get_charging(void); + +/** + * Return the value of the status register + * + * @return status register value, or -1 on error + */ +int tps65090_get_status(void); + +/** + * Initialize the TPS65090 PMU. + * + * @return 0 on success, non-0 on failure + */ +int tps65090_init(void); + +#endif /* __TPS65090_PMIC_H_ */ diff --git a/lib/fdtdec.c b/lib/fdtdec.c index e17dd00..d247366 100644 --- a/lib/fdtdec.c +++ b/lib/fdtdec.c @@ -63,6 +63,7 @@ static const char * const compat_names[COMPAT_COUNT] = { COMPAT(MAXIM_MAX77686_PMIC, "maxim,max77686_pmic"), COMPAT(GENERIC_SPI_FLASH, "spi-flash"), COMPAT(MAXIM_98095_CODEC, "maxim,max98095-codec"), + COMPAT(TI_TPS65090, "ti,tps65090"), }; const char *fdtdec_get_compatible(enum fdt_compat_id id)