From: NeilBrown <neil@brown.name>
To: Sebastian Reichel <sre@kernel.org>
Cc: Samuel Ortiz <sameo@linux.intel.com>,
linux-pm@vger.kernel.org, Tony Lindgren <tony@atomide.com>,
David Woodhouse <dwmw2@infradead.org>,
linux-kernel@vger.kernel.org,
real GTA04 owners <gta04-owner@goldelico.com>,
Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>,
Pavel Machek <pavel@ucw.cz>,
linux-omap@vger.kernel.org, Lee Jones <lee.jones@linaro.org>
Subject: [PATCH 06/13] twl4030_charger: allow fine control of charger current.
Date: Thu, 30 Jul 2015 10:11:24 +1000 [thread overview]
Message-ID: <20150730001124.4012.75604.stgit@noble> (raw)
In-Reply-To: <20150730001113.4012.18086.stgit@noble>
The twl4030 allows control of the incoming current.
Part of this control is a 'CGAIN' setting which doubles
the range for half the precision. This control affects
several different current setting, so all need to be updated
at once when CGAIN is changed.
With this patch, all of these current setting are managed
by the driver, but most are left at their default settings.
The current drawn is set to 500mA if the allow_usb module parameter is
set, and to 100mA otherwise.
More fine control will appear in later patches.
Acked-by: Pavel Machek <pavel@ucw.cz>
Signed-off-by: NeilBrown <neil@brown.name>
---
drivers/power/twl4030_charger.c | 168 +++++++++++++++++++++++++++++++++++++--
1 file changed, 160 insertions(+), 8 deletions(-)
diff --git a/drivers/power/twl4030_charger.c b/drivers/power/twl4030_charger.c
index 29984b263a35..3b7cc631bb8a 100644
--- a/drivers/power/twl4030_charger.c
+++ b/drivers/power/twl4030_charger.c
@@ -31,6 +31,11 @@
#define TWL4030_BCIMFSTS4 0x10
#define TWL4030_BCICTL1 0x23
#define TWL4030_BB_CFG 0x12
+#define TWL4030_BCIIREF1 0x27
+#define TWL4030_BCIIREF2 0x28
+#define TWL4030_BCIMFKEY 0x11
+#define TWL4030_BCIMFTH8 0x1d
+#define TWL4030_BCIMFTH9 0x1e
#define TWL4030_BCIMFSTS1 0x01
@@ -95,6 +100,12 @@ struct twl4030_bci {
int irq_bci;
int usb_enabled;
+ /*
+ * ichg values in uA. If any are 'large', we set CGAIN to
+ * '1' which doubles the range for half the precision.
+ */
+ unsigned int ichg_eoc, ichg_lo, ichg_hi, cur;
+
unsigned long event;
};
@@ -211,6 +222,146 @@ static int ua2regval(int ua, bool cgain)
return ret;
}
+static int twl4030_charger_update_current(struct twl4030_bci *bci)
+{
+ int status;
+ unsigned reg, cur_reg;
+ u8 bcictl1, oldreg, fullreg;
+ bool cgain = false;
+ u8 boot_bci;
+
+ /* First, check thresholds and see if cgain is needed */
+ if (bci->ichg_eoc >= 200000)
+ cgain = true;
+ if (bci->ichg_lo >= 400000)
+ cgain = true;
+ if (bci->ichg_hi >= 820000)
+ cgain = true;
+ if (bci->cur > 852000)
+ cgain = true;
+
+ status = twl4030_bci_read(TWL4030_BCICTL1, &bcictl1);
+ if (status < 0)
+ return status;
+ if (twl_i2c_read_u8(TWL_MODULE_PM_MASTER, &boot_bci,
+ TWL4030_PM_MASTER_BOOT_BCI) < 0)
+ boot_bci = 0;
+ boot_bci &= 7;
+
+ if ((!!cgain) != !!(bcictl1 & TWL4030_CGAIN))
+ /* Need to turn for charging while we change the
+ * CGAIN bit. Leave it off while everything is
+ * updated.
+ */
+ twl4030_clear_set_boot_bci(boot_bci, 0);
+
+ /*
+ * For ichg_eoc, the hardware only supports reg values matching
+ * 100XXXX000, and requires the XXXX be stored in the high nibble
+ * of TWL4030_BCIMFTH8.
+ */
+ reg = ua2regval(bci->ichg_eoc, cgain);
+ if (reg > 0x278)
+ reg = 0x278;
+ if (reg < 0x200)
+ reg = 0x200;
+ reg = (reg >> 3) & 0xf;
+ fullreg = reg << 4;
+
+ /*
+ * For ichg_lo, reg value must match 10XXXX0000.
+ * XXXX is stored in low nibble of TWL4030_BCIMFTH8.
+ */
+ reg = ua2regval(bci->ichg_lo, cgain);
+ if (reg > 0x2F0)
+ reg = 0x2F0;
+ if (reg < 0x200)
+ reg = 0x200;
+ reg = (reg >> 4) & 0xf;
+ fullreg |= reg;
+
+ /* ichg_eoc and ichg_lo live in same register */
+ status = twl4030_bci_read(TWL4030_BCIMFTH8, &oldreg);
+ if (status < 0)
+ return status;
+ if (oldreg != fullreg) {
+ status = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, 0xF4,
+ TWL4030_BCIMFKEY);
+ if (status < 0)
+ return status;
+ twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE,
+ fullreg, TWL4030_BCIMFTH8);
+ }
+
+ /* ichg_hi threshold must be 1XXXX01100 (I think) */
+ reg = ua2regval(bci->ichg_hi, cgain);
+ if (reg > 0x3E0)
+ reg = 0x3E0;
+ if (reg < 0x200)
+ reg = 0x200;
+ fullreg = (reg >> 5) & 0xF;
+ fullreg <<= 4;
+ status = twl4030_bci_read(TWL4030_BCIMFTH9, &oldreg);
+ if (status < 0)
+ return status;
+ if ((oldreg & 0xF0) != fullreg) {
+ fullreg |= (oldreg & 0x0F);
+ status = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, 0xE7,
+ TWL4030_BCIMFKEY);
+ if (status < 0)
+ return status;
+ twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE,
+ fullreg, TWL4030_BCIMFTH9);
+ }
+
+ /*
+ * And finally, set the current. This is stored in
+ * two registers.
+ */
+ reg = ua2regval(bci->cur, cgain);
+ /* we have only 10 bits */
+ if (reg > 0x3ff)
+ reg = 0x3ff;
+ status = twl4030_bci_read(TWL4030_BCIIREF1, &oldreg);
+ if (status < 0)
+ return status;
+ cur_reg = oldreg;
+ status = twl4030_bci_read(TWL4030_BCIIREF2, &oldreg);
+ if (status < 0)
+ return status;
+ cur_reg |= oldreg << 8;
+ if (reg != oldreg) {
+ /* disable write protection for one write access for
+ * BCIIREF */
+ status = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, 0xE7,
+ TWL4030_BCIMFKEY);
+ if (status < 0)
+ return status;
+ status = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE,
+ (reg & 0x100) ? 3 : 2,
+ TWL4030_BCIIREF2);
+ if (status < 0)
+ return status;
+ /* disable write protection for one write access for
+ * BCIIREF */
+ status = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, 0xE7,
+ TWL4030_BCIMFKEY);
+ if (status < 0)
+ return status;
+ status = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE,
+ reg & 0xff,
+ TWL4030_BCIIREF1);
+ }
+ if ((!!cgain) != !!(bcictl1 & TWL4030_CGAIN)) {
+ /* Flip CGAIN and re-enable charging */
+ bcictl1 ^= TWL4030_CGAIN;
+ twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE,
+ bcictl1, TWL4030_BCICTL1);
+ twl4030_clear_set_boot_bci(0, boot_bci);
+ }
+ return 0;
+}
+
/*
* Enable/Disable USB Charge functionality.
*/
@@ -219,14 +370,6 @@ static int twl4030_charger_enable_usb(struct twl4030_bci *bci, bool enable)
int ret;
if (enable && !IS_ERR_OR_NULL(bci->transceiver)) {
- /*
- * Until we can find out what current the device can provide,
- * require a module param to enable USB charging.
- */
- if (!allow_usb) {
- dev_warn(bci->dev, "USB charging is disabled.\n");
- return -EACCES;
- }
/* Need to keep phy powered */
if (!bci->usb_enabled) {
@@ -578,6 +721,14 @@ static int twl4030_bci_probe(struct platform_device *pdev)
if (!pdata)
pdata = twl4030_bci_parse_dt(&pdev->dev);
+ bci->ichg_eoc = 80100; /* Stop charging when current drops to here */
+ bci->ichg_lo = 241000; /* Low threshold */
+ bci->ichg_hi = 500000; /* High threshold */
+ if (allow_usb)
+ bci->cur = 500000; /* 500mA */
+ else
+ bci->cur = 100000; /* 100mA */
+
bci->dev = &pdev->dev;
bci->irq_chg = platform_get_irq(pdev, 0);
bci->irq_bci = platform_get_irq(pdev, 1);
@@ -657,6 +808,7 @@ static int twl4030_bci_probe(struct platform_device *pdev)
if (ret < 0)
dev_warn(&pdev->dev, "failed to unmask interrupts: %d\n", ret);
+ twl4030_charger_update_current(bci);
twl4030_charger_enable_ac(true);
if (!IS_ERR_OR_NULL(bci->transceiver))
twl4030_bci_usb_ncb(&bci->usb_nb,
next prev parent reply other threads:[~2015-07-30 0:11 UTC|newest]
Thread overview: 41+ messages / expand[flat|nested] mbox.gz Atom feed top
2015-07-30 0:11 [PATCH 00/13] Enhance twl4030_charger functionality. - V3 NeilBrown
2015-07-30 0:11 ` [PATCH 04/13] twl4030_charger: trust phy to determine when USB power is available NeilBrown
2015-09-15 11:30 ` Pavel Machek
2015-07-30 0:11 ` [PATCH 09/13] twl4030_charger: enable manual enable/disable of usb charging NeilBrown
2015-07-30 0:11 ` NeilBrown [this message]
2015-07-30 0:11 ` [PATCH 10/13] twl4030_charger: add software controlled linear charging mode NeilBrown
2015-09-15 11:28 ` Pavel Machek
2015-09-15 12:08 ` [Gta04-owner] " Christ van Willegen
2015-10-01 6:14 ` Neil Brown
2015-10-06 14:34 ` Pavel Machek
2015-10-29 16:20 ` [Gta04-owner] " Andreas Kemnade
2015-11-14 18:12 ` Pavel Machek
2015-11-24 22:31 ` Andreas Kemnade
2015-07-30 0:11 ` [PATCH 12/13] twl4030_charger: Increase current carefully while watching voltage NeilBrown
2015-07-30 0:11 ` [PATCH 07/13] twl4030_charger: distinguish between USB current and 'AC' current NeilBrown
2015-08-07 3:11 ` Tony Lindgren
2015-08-07 3:45 ` NeilBrown
2015-08-07 4:21 ` Tony Lindgren
2015-08-07 5:13 ` Sebastian Reichel
2015-08-07 5:29 ` NeilBrown
2015-07-30 0:11 ` [PATCH 08/13] twl4030_charger: allow max_current to be managed via sysfs NeilBrown
2015-07-30 0:11 ` [PATCH 01/13] twl4030_charger: use runtime_pm to keep usb phy active while charging NeilBrown
2015-09-15 11:29 ` Pavel Machek
2015-07-30 0:11 ` [PATCH 05/13] twl4030_charger: split uA calculation into a function NeilBrown
2015-07-30 0:11 ` [PATCH 03/13] twl4030_charger: correctly handle -EPROBE_DEFER from devm_usb_get_phy_by_node NeilBrown
2015-08-18 8:07 ` Tony Lindgren
2015-08-19 0:28 ` NeilBrown
2015-08-19 6:25 ` Tony Lindgren
2015-08-27 20:51 ` Kevin Hilman
2015-09-02 3:25 ` Kevin Hilman
2015-09-02 6:19 ` Neil Brown
2015-09-02 13:07 ` Tony Lindgren
2015-09-08 18:32 ` Kevin Hilman
2015-09-08 20:14 ` Tony Lindgren
2015-09-10 8:08 ` Sebastian Reichel
2015-09-10 20:27 ` Sebastian Reichel
2015-09-10 20:43 ` Tony Lindgren
2015-07-30 0:11 ` [PATCH 02/13] twl4030_charger: convert to module_platform_driver instead of ..._probe NeilBrown
2015-07-30 0:11 ` [PATCH 11/13] twl4030_charger: add ac/mode to match usb/mode NeilBrown
2015-07-30 0:11 ` [PATCH 13/13] twl4030_charger: assume a 'charger' can supply maximum current NeilBrown
2015-08-05 3:35 ` [PATCH 00/13] Enhance twl4030_charger functionality. - V3 Sebastian Reichel
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20150730001124.4012.75604.stgit@noble \
--to=neil@brown.name \
--cc=dbaryshkov@gmail.com \
--cc=dwmw2@infradead.org \
--cc=gta04-owner@goldelico.com \
--cc=lee.jones@linaro.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-omap@vger.kernel.org \
--cc=linux-pm@vger.kernel.org \
--cc=pavel@ucw.cz \
--cc=sameo@linux.intel.com \
--cc=sre@kernel.org \
--cc=tony@atomide.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).