* [PATCH v2 0/3] power_supply: modelgauge_battery: Add Maxim ModelGauge ICs gauge
@ 2014-02-01 22:23 ` Vladimir Barinov
0 siblings, 0 replies; 11+ messages in thread
From: Vladimir Barinov @ 2014-02-01 22:23 UTC (permalink / raw)
To: anton, dwmw2, linux-kernel, devicetree
Cc: mk7.kang, k.kozlowski, mark.rutland
Hello.
This adds the folowing:
- Maxim ModelGauge ICs gauge driver for MAX17040/41/43/44/48/49/58/59 chips
- Document DT bindings
- Remove superseded Maxim MAX17040 gauge driver
Vladimir Barinov (3):
[1/3] power_supply: modelgauge_battery: Maxim ModelGauge ICs gauge
[2/3] dt: Document ModelGauge gauge bindings
[3/3] power_supply: modelgauge_battery: Remove Maxim MAX17040 gauge
---
This patchset is against the 'kernel/git/torvalds/linux.git' repo.
Changes since v1:
- switched to REGMAP API
- replaced request_threaded_irq with devm_request_threaded_irq
- replaced cancel_delayed_work with _sync version
- moved "empty_alert_threshold, soc_change_alert, hibernate_threshold,
active_threshold, undervoltage, overvoltage, resetvoltage" parameters
out from platform_data and DT
- removed unused parameters "empty_adjustment, empty_adjustment"
- added return value checks for of_property_read_XX functions
- removed irrelevant bindings
- fixed dt properties naming in documentation
- added binding size description in documentation
- removed satelite include file include/linux/max17040_battery.h
Documentation/devicetree/bindings/power_supply/modelgauge_battery.txt | 61
drivers/power/Kconfig | 17
drivers/power/Makefile | 2
drivers/power/max17040_battery.c | 297 ---
drivers/power/modelgauge_battery.c | 838 ++++++++++
include/linux/max17040_battery.h | 19
include/linux/platform_data/battery-modelgauge.h | 31
7 files changed, 940 insertions(+), 325 deletions(-)
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v2 0/3] power_supply: modelgauge_battery: Add Maxim ModelGauge ICs gauge
@ 2014-02-01 22:23 ` Vladimir Barinov
0 siblings, 0 replies; 11+ messages in thread
From: Vladimir Barinov @ 2014-02-01 22:23 UTC (permalink / raw)
To: anton, dwmw2, linux-kernel, devicetree
Cc: mk7.kang, k.kozlowski, mark.rutland
Hello.
This adds the folowing:
- Maxim ModelGauge ICs gauge driver for MAX17040/41/43/44/48/49/58/59 chips
- Document DT bindings
- Remove superseded Maxim MAX17040 gauge driver
Vladimir Barinov (3):
[1/3] power_supply: modelgauge_battery: Maxim ModelGauge ICs gauge
[2/3] dt: Document ModelGauge gauge bindings
[3/3] power_supply: modelgauge_battery: Remove Maxim MAX17040 gauge
---
This patchset is against the 'kernel/git/torvalds/linux.git' repo.
Changes since v1:
- switched to REGMAP API
- replaced request_threaded_irq with devm_request_threaded_irq
- replaced cancel_delayed_work with _sync version
- moved "empty_alert_threshold, soc_change_alert, hibernate_threshold,
active_threshold, undervoltage, overvoltage, resetvoltage" parameters
out from platform_data and DT
- removed unused parameters "empty_adjustment, empty_adjustment"
- added return value checks for of_property_read_XX functions
- removed irrelevant bindings
- fixed dt properties naming in documentation
- added binding size description in documentation
- removed satelite include file include/linux/max17040_battery.h
Documentation/devicetree/bindings/power_supply/modelgauge_battery.txt | 61
drivers/power/Kconfig | 17
drivers/power/Makefile | 2
drivers/power/max17040_battery.c | 297 ---
drivers/power/modelgauge_battery.c | 838 ++++++++++
include/linux/max17040_battery.h | 19
include/linux/platform_data/battery-modelgauge.h | 31
7 files changed, 940 insertions(+), 325 deletions(-)
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v2 1/3] power_supply: modelgauge_battery: Maxim ModelGauge ICs gauge
2014-02-01 22:23 ` Vladimir Barinov
@ 2014-02-01 22:23 ` Vladimir Barinov
-1 siblings, 0 replies; 11+ messages in thread
From: Vladimir Barinov @ 2014-02-01 22:23 UTC (permalink / raw)
To: anton-9xeibp6oKSgdnm+yROfE0A, dwmw2-wEGCiKHe2LqWVfeAwA7xHQ,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA
Cc: mk7.kang-Sze3O3UU22JBDgjK7y7TUQ,
k.kozlowski-Sze3O3UU22JBDgjK7y7TUQ, mark.rutland-5wv7dgnIgG8
Add Maxim ModelGauge ICs gauge driver for MAX17040/41/43/44/48/49/58/59 chips
Signed-off-by: Vladimir Barinov <vladimir.barinov-M4DtvfQ/ZS1MRgGoP+s0PdBPR1lH4CV8@public.gmane.org>
---
drivers/power/Kconfig | 9
drivers/power/Makefile | 1
drivers/power/modelgauge_battery.c | 838 +++++++++++++++++++++++
include/linux/platform_data/battery-modelgauge.h | 31
4 files changed, 879 insertions(+)
Index: battery-2.6/drivers/power/Kconfig
===================================================================
--- battery-2.6.orig/drivers/power/Kconfig 2014-02-02 01:29:07.434614235 +0400
+++ battery-2.6/drivers/power/Kconfig 2014-02-02 01:29:29.070614750 +0400
@@ -205,6 +205,15 @@
with MAX17042. This driver also supports max17047/50 chips which are
improved version of max17042.
+config BATTERY_MODELGAUGE
+ tristate "Maxim ModelGauge ICs fuel gauge"
+ depends on I2C
+ select REGMAP_I2C
+ help
+ ModelGauge(TM) is a Maxim algorithm incorporated in
+ MAX17040/41/43/44/48/49/58/59 fuel-gauges for lithium-ion (Li+)
+ batteries.
+
config BATTERY_Z2
tristate "Z2 battery driver"
depends on I2C && MACH_ZIPIT2
Index: battery-2.6/drivers/power/Makefile
===================================================================
--- battery-2.6.orig/drivers/power/Makefile 2014-02-02 01:29:07.462614237 +0400
+++ battery-2.6/drivers/power/Makefile 2014-02-02 01:29:12.978614368 +0400
@@ -32,6 +32,7 @@
obj-$(CONFIG_BATTERY_DA9052) += da9052-battery.o
obj-$(CONFIG_BATTERY_MAX17040) += max17040_battery.o
obj-$(CONFIG_BATTERY_MAX17042) += max17042_battery.o
+obj-$(CONFIG_BATTERY_MODELGAUGE) += modelgauge_battery.o
obj-$(CONFIG_BATTERY_Z2) += z2_battery.o
obj-$(CONFIG_BATTERY_S3C_ADC) += s3c_adc_battery.o
obj-$(CONFIG_BATTERY_TWL4030_MADC) += twl4030_madc_battery.o
Index: battery-2.6/drivers/power/modelgauge_battery.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ battery-2.6/drivers/power/modelgauge_battery.c 2014-02-02 01:29:34.314614874 +0400
@@ -0,0 +1,838 @@
+/*
+ * Maxim ModelGauge ICs fuel gauge driver
+ *
+ * Author: Vladimir Barinov <source-M4DtvfQ/ZS1MRgGoP+s0PdBPR1lH4CV8@public.gmane.org>
+ * Copyright (C) 2013 Cogent Embedded, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/power_supply.h>
+#include <linux/platform_device.h>
+#include <linux/platform_data/battery-modelgauge.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+#define DRV_NAME "modelgauge"
+
+/* Register offsets for ModelGauge ICs */
+#define MODELGAUGE_VCELL_REG 0x02
+#define MODELGAUGE_SOC_REG 0x04
+#define MODELGAUGE_MODE_REG 0x06
+#define MODELGAUGE_VERSION_REG 0x08
+#define MODELGAUGE_HIBRT_REG 0x0A
+#define MODELGAUGE_CONFIG_REG 0x0C
+#define MODELGAUGE_OCV_REG 0x0E
+#define MODELGAUGE_VALRT_REG 0x14
+#define MODELGAUGE_CRATE_REG 0x16
+#define MODELGAUGE_VRESETID_REG 0x18
+#define MODELGAUGE_STATUS_REG 0x1A
+#define MODELGAUGE_UNLOCK_REG 0x3E
+#define MODELGAUGE_TABLE_REG 0x40
+#define MODELGAUGE_RCOMPSEG_REG 0x80
+#define MODELGAUGE_CMD_REG 0xFE
+
+/* MODE register bits */
+#define MODELGAUGE_MODE_QUICKSTART (1 << 14)
+#define MODELGAUGE_MODE_ENSLEEP (1 << 13)
+#define MODELGAUGE_MODE_HIBSTAT (1 << 12)
+
+/* CONFIG register bits */
+#define MODELGAUGE_CONFIG_SLEEP (1 << 7)
+#define MODELGAUGE_CONFIG_ALSC (1 << 6)
+#define MODELGAUGE_CONFIG_ALRT (1 << 5)
+#define MODELGAUGE_CONFIG_ATHD_MASK 0x1f
+
+/* STATUS register bits */
+#define MODELGAUGE_STATUS_ENVR (1 << 14)
+#define MODELGAUGE_STATUS_SC (1 << 13)
+#define MODELGAUGE_STATUS_HD (1 << 12)
+#define MODELGAUGE_STATUS_VR (1 << 11)
+#define MODELGAUGE_STATUS_VL (1 << 10)
+#define MODELGAUGE_STATUS_VH (1 << 9)
+#define MODELGAUGE_STATUS_RI (1 << 8)
+
+/* VRESETID register bits */
+#define MODELGAUGE_VRESETID_DIS (1 << 8)
+
+#define MODELGAUGE_UNLOCK_VALUE 0x4a57
+#define MODELGAUGE_RESET_VALUE 0x5400
+
+#define MODELGAUGE_RCOMP_UPDATE_DELAY 60000
+
+/* Capacity threshold where an interrupt is generated on the ALRT pin */
+#define MODELGAUGE_EMPTY_ATHD 15
+/* Enable alert for 1% soc change */
+#define MODELGAUGE_SOC_CHANGE_ALERT 1
+/* Hibernate threshold (crate), where IC enters hibernate mode */
+#define MODELGAUGE_HIBRT_THD 20
+/* Active threshold (mV), where IC exits hibernate mode */
+#define MODELGAUGE_ACTIVE_THD 60
+/* Voltage (mV), when IC alerts if battery voltage less then undervoltage */
+#define MODELGAUGE_UV 0
+/* Voltage (mV), when IC alerts if battery voltage greater then overvoltage */
+#define MODELGAUGE_OV 5120
+/*
+ * Voltage threshold (mV) below which the IC resets itself.
+ * Used to detect battery removal and reinsertion
+ */
+#define MODELGAUGE_RV 0
+
+enum chip_id {
+ ID_MAX17040,
+ ID_MAX17041,
+ ID_MAX17043,
+ ID_MAX17044,
+ ID_MAX17048,
+ ID_MAX17049,
+ ID_MAX17058,
+ ID_MAX17059,
+};
+
+struct modelgauge_priv {
+ struct device *dev;
+ struct regmap *regmap;
+ struct power_supply battery;
+ struct modelgauge_platform_data *pdata;
+ enum chip_id chip;
+ struct work_struct load_work;
+ struct delayed_work rcomp_work;
+ int soc_shift;
+};
+
+static void modelgauge_write_block(struct regmap *regmap, u8 adr, u8 size,
+ u16 *data)
+{
+ int k;
+
+ /* RAM has different endianness then registers */
+ for (k = 0; k < size; k += 2, adr += 2, data++)
+ regmap_write(regmap, adr, cpu_to_be16(*data));
+}
+
+static int modelgauge_lsb_to_uvolts(struct modelgauge_priv *priv, int lsb)
+{
+ switch (priv->chip) {
+ case ID_MAX17040:
+ case ID_MAX17043:
+ return (lsb >> 4) * 1250; /* 1.25mV per bit */
+ case ID_MAX17041:
+ case ID_MAX17044:
+ return (lsb >> 4) * 2500; /* 2.5mV per bit */
+ case ID_MAX17048:
+ case ID_MAX17058:
+ return lsb * 625 / 8; /* 78.125uV per bit */
+ case ID_MAX17049:
+ case ID_MAX17059:
+ return lsb * 625 / 4; /* 156.25uV per bit */
+ default:
+ return -EINVAL;
+ }
+}
+
+static enum power_supply_property modelgauge_battery_props[] = {
+ POWER_SUPPLY_PROP_STATUS,
+ POWER_SUPPLY_PROP_VOLTAGE_NOW,
+ POWER_SUPPLY_PROP_VOLTAGE_OCV,
+ POWER_SUPPLY_PROP_CAPACITY,
+ POWER_SUPPLY_PROP_TEMP,
+};
+
+static int modelgauge_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct modelgauge_priv *priv = container_of(psy,
+ struct modelgauge_priv,
+ battery);
+ struct regmap *regmap = priv->regmap;
+ struct modelgauge_platform_data *pdata = priv->pdata;
+ int reg;
+ int ret;
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_STATUS:
+ if (pdata && pdata->get_charging_status)
+ val->intval = pdata->get_charging_status();
+ else
+ val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+ ret = regmap_read(regmap, MODELGAUGE_VCELL_REG, ®);
+ if (ret < 0)
+ return ret;
+
+ val->intval = modelgauge_lsb_to_uvolts(priv, reg);
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_OCV:
+ /* Unlock model access */
+ regmap_write(regmap, MODELGAUGE_UNLOCK_REG,
+ MODELGAUGE_UNLOCK_VALUE);
+ ret = regmap_read(regmap, MODELGAUGE_OCV_REG, ®);
+ /* Lock model access */
+ regmap_write(regmap, MODELGAUGE_UNLOCK_REG, 0);
+ if (ret < 0)
+ return ret;
+
+ val->intval = modelgauge_lsb_to_uvolts(priv, reg);
+ break;
+ case POWER_SUPPLY_PROP_CAPACITY:
+ ret = regmap_read(regmap, MODELGAUGE_SOC_REG, ®);
+ if (ret < 0)
+ return ret;
+
+ val->intval = reg / (1 << priv->soc_shift);
+ break;
+ case POWER_SUPPLY_PROP_TEMP:
+ if (pdata && pdata->get_temperature)
+ val->intval = pdata->get_temperature();
+ else
+ val->intval = 25;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static void modelgauge_update_rcomp(struct modelgauge_priv *priv)
+{
+ struct regmap *regmap = priv->regmap;
+ struct modelgauge_platform_data *pdata = priv->pdata;
+ u16 rcomp;
+ int temp;
+
+ if (pdata->get_temperature)
+ temp = pdata->get_temperature();
+ else
+ temp = 25;
+
+ if (!pdata->temp_co_up)
+ pdata->temp_co_up = -500;
+ if (!pdata->temp_co_down)
+ pdata->temp_co_down = -5000;
+
+ rcomp = pdata->rcomp0;
+ if (temp > 20)
+ rcomp += (temp - 20) * pdata->temp_co_up / 1000;
+ else
+ rcomp += (temp - 20) * pdata->temp_co_down / 1000;
+
+ /* Update RCOMP */
+ regmap_update_bits(regmap, MODELGAUGE_CONFIG_REG, 0xff, rcomp << 8);
+}
+
+static void modelgauge_update_rcomp_work(struct work_struct *work)
+{
+ struct modelgauge_priv *priv = container_of(work,
+ struct modelgauge_priv,
+ rcomp_work.work);
+
+ modelgauge_update_rcomp(priv);
+ schedule_delayed_work(&priv->rcomp_work,
+ msecs_to_jiffies(MODELGAUGE_RCOMP_UPDATE_DELAY));
+}
+
+static int modelgauge_load_model(struct modelgauge_priv *priv)
+{
+ struct regmap *regmap = priv->regmap;
+ struct modelgauge_platform_data *pdata = priv->pdata;
+ int ret = -EINVAL;
+ int timeout, k;
+ int ocv, config, soc;
+
+ /* Save CONFIG */
+ regmap_read(regmap, MODELGAUGE_CONFIG_REG, &config);
+
+ for (timeout = 0; timeout < 100; timeout++) {
+ /* Unlock model access */
+ regmap_write(regmap, MODELGAUGE_UNLOCK_REG,
+ MODELGAUGE_UNLOCK_VALUE);
+
+ /* Read OCV */
+ regmap_read(regmap, MODELGAUGE_OCV_REG, &ocv);
+ if (ocv != 0xffff)
+ break;
+ }
+
+ if (timeout >= 100) {
+ dev_err(priv->dev, "timeout to unlock model access\n");
+ ret = -EIO;
+ goto exit;
+ }
+
+ switch (priv->chip) {
+ case ID_MAX17058:
+ case ID_MAX17059:
+ /* Reset chip transaction does not provide ACK */
+ regmap_write(regmap, MODELGAUGE_CMD_REG,
+ MODELGAUGE_RESET_VALUE);
+ msleep(150);
+
+ for (timeout = 0; timeout < 100; timeout++) {
+ int reg;
+
+ /* Unlock Model Access */
+ regmap_write(regmap, MODELGAUGE_UNLOCK_REG,
+ MODELGAUGE_UNLOCK_VALUE);
+
+ /* Read OCV */
+ regmap_read(regmap, MODELGAUGE_OCV_REG, ®);
+ if (reg != 0xffff)
+ break;
+ }
+
+ if (timeout >= 100) {
+ dev_err(priv->dev, "timeout to unlock model access\n");
+ ret = -EIO;
+ goto exit;
+ }
+ break;
+ default:
+ break;
+ }
+
+ switch (priv->chip) {
+ case ID_MAX17040:
+ case ID_MAX17041:
+ case ID_MAX17043:
+ case ID_MAX17044:
+ /* Write OCV */
+ regmap_write(regmap, MODELGAUGE_OCV_REG, pdata->ocvtest);
+ /* Write RCOMP to its maximum value */
+ regmap_write(regmap, MODELGAUGE_CONFIG_REG, 0xff00);
+ break;
+ default:
+ break;
+ }
+
+ /* Write the model */
+ modelgauge_write_block(regmap, MODELGAUGE_TABLE_REG,
+ MODELGAUGE_TABLE_SIZE,
+ (u16 *)pdata->model_data);
+
+ switch (priv->chip) {
+ case ID_MAX17048:
+ case ID_MAX17049: {
+ u16 buf[16];
+
+ if (!pdata->rcomp_seg)
+ pdata->rcomp_seg = 0x80;
+
+ for (k = 0; k < 16; k++)
+ *buf = pdata->rcomp_seg;
+
+ /* Write RCOMPSeg */
+ modelgauge_write_block(regmap, MODELGAUGE_RCOMPSEG_REG,
+ 32, buf);
+ }
+ break;
+ default:
+ break;
+ }
+
+ switch (priv->chip) {
+ case ID_MAX17040:
+ case ID_MAX17041:
+ case ID_MAX17043:
+ case ID_MAX17044:
+ /* Delay at least 150ms */
+ msleep(150);
+ break;
+ default:
+ break;
+ }
+
+ /* Write OCV */
+ regmap_write(regmap, MODELGAUGE_OCV_REG, pdata->ocvtest);
+
+ switch (priv->chip) {
+ case ID_MAX17048:
+ case ID_MAX17049:
+ /* Disable Hibernate */
+ regmap_write(regmap, MODELGAUGE_HIBRT_REG, 0);
+ /* fall-through */
+ case ID_MAX17058:
+ case ID_MAX17059:
+ /* Lock Model Access */
+ regmap_write(regmap, MODELGAUGE_UNLOCK_REG, 0);
+ break;
+ default:
+ break;
+ }
+
+ /* Delay between 150ms and 600ms */
+ msleep(200);
+
+ /* Read SOC Register and compare to expected result */
+ regmap_read(regmap, MODELGAUGE_SOC_REG, &soc);
+ soc >>= 8;
+ if (soc >= pdata->soc_check_a && soc <= pdata->soc_check_b)
+ ret = 0;
+
+ switch (priv->chip) {
+ case ID_MAX17048:
+ case ID_MAX17049:
+ case ID_MAX17058:
+ case ID_MAX17059:
+ /* Unlock model access */
+ regmap_write(regmap, MODELGAUGE_UNLOCK_REG,
+ MODELGAUGE_UNLOCK_VALUE);
+ break;
+ default:
+ break;
+ }
+
+ /* Restore CONFIG and OCV */
+ regmap_write(regmap, MODELGAUGE_CONFIG_REG, config);
+ regmap_write(regmap, MODELGAUGE_OCV_REG, ocv);
+
+ switch (priv->chip) {
+ case ID_MAX17048:
+ case ID_MAX17049:
+ /* Restore Hibernate */
+ regmap_write(regmap, MODELGAUGE_HIBRT_REG,
+ (MODELGAUGE_HIBRT_THD << 8) |
+ MODELGAUGE_ACTIVE_THD);
+ break;
+ default:
+ break;
+ }
+
+exit:
+ /* Lock model access */
+ regmap_write(regmap, MODELGAUGE_UNLOCK_REG, 0);
+
+ /* Wait 150ms minimum */
+ msleep(150);
+
+ return ret;
+}
+
+static void modelgauge_load_model_work(struct work_struct *work)
+{
+ struct modelgauge_priv *priv = container_of(work,
+ struct modelgauge_priv,
+ load_work);
+ struct regmap *regmap = priv->regmap;
+ int ret;
+ int timeout;
+
+ for (timeout = 0; timeout < 10; timeout++) {
+ /* Load custom model data */
+ ret = modelgauge_load_model(priv);
+ if (!ret)
+ break;
+ }
+
+ if (timeout >= 10) {
+ dev_info(priv->dev, "failed to load custom model\n");
+ return;
+ }
+
+ switch (priv->chip) {
+ case ID_MAX17048:
+ case ID_MAX17049:
+ case ID_MAX17058:
+ case ID_MAX17059:
+ /* Clear reset indicator bit */
+ regmap_update_bits(regmap, MODELGAUGE_STATUS_REG,
+ MODELGAUGE_STATUS_RI, 0);
+ break;
+ default:
+ break;
+ }
+}
+
+static irqreturn_t modelgauge_irq_handler(int id, void *dev)
+{
+ struct modelgauge_priv *priv = dev;
+
+ /* clear alert status bit */
+ regmap_update_bits(priv->regmap, MODELGAUGE_CONFIG_REG,
+ MODELGAUGE_CONFIG_ALRT, 0);
+
+ power_supply_changed(&priv->battery);
+ return IRQ_HANDLED;
+}
+
+static int modelgauge_init(struct modelgauge_priv *priv)
+{
+ struct regmap *regmap = priv->regmap;
+ struct modelgauge_platform_data *pdata = priv->pdata;
+ int ret;
+ int reg;
+
+ ret = regmap_read(regmap, MODELGAUGE_VERSION_REG, ®);
+ if (ret < 0)
+ return -ENODEV;
+
+ dev_info(priv->dev, "IC production version 0x%04x\n", reg);
+
+ /* SOC=0 means unrecoverable IC fault, reset is a workaround */
+ regmap_read(regmap, MODELGAUGE_SOC_REG, ®);
+ if (!reg) {
+ dev_info(priv->dev, "Reset chip, SOC measurement stall\n");
+ /* Reset chip transaction does not provide ACK */
+ regmap_write(regmap, MODELGAUGE_CMD_REG,
+ MODELGAUGE_RESET_VALUE);
+ msleep(150);
+ }
+
+ /* Default model uses 8 bits per percent */
+ priv->soc_shift = 8;
+
+ if (!priv->pdata) {
+ dev_info(priv->dev, "no platform data provided\n");
+ return 0;
+ }
+
+ switch (pdata->bits) {
+ case 19:
+ priv->soc_shift = 9;
+ break;
+ case 18:
+ default:
+ priv->soc_shift = 8;
+ break;
+ }
+
+ /* Set RCOMP */
+ modelgauge_update_rcomp(priv);
+ if (pdata->get_temperature) {
+ /* Schedule update RCOMP */
+ schedule_delayed_work(&priv->rcomp_work,
+ msecs_to_jiffies(MODELGAUGE_RCOMP_UPDATE_DELAY));
+ }
+
+ /* Clear alert status bit, wake-up, set alert threshold */
+ reg = 0;
+ switch (priv->chip) {
+ case ID_MAX17048:
+ case ID_MAX17049:
+ reg |= MODELGAUGE_SOC_CHANGE_ALERT ? MODELGAUGE_CONFIG_ALSC : 0;
+ /* fall-through */
+ case ID_MAX17043:
+ case ID_MAX17044:
+ case ID_MAX17058:
+ case ID_MAX17059:
+ reg |= 32 - (MODELGAUGE_EMPTY_ATHD << (priv->soc_shift - 8));
+ break;
+ default:
+ break;
+ }
+ regmap_update_bits(regmap, MODELGAUGE_CONFIG_REG,
+ MODELGAUGE_CONFIG_ALRT | MODELGAUGE_CONFIG_SLEEP |
+ MODELGAUGE_CONFIG_ALSC | MODELGAUGE_CONFIG_ATHD_MASK,
+ reg);
+
+ switch (priv->chip) {
+ case ID_MAX17048:
+ case ID_MAX17049:
+ /* Set Hibernate thresholds */
+ reg = (MODELGAUGE_HIBRT_THD * 125 / 26) & 0xff;
+ reg <<= 8;
+ reg |= (MODELGAUGE_ACTIVE_THD * 4 / 5) & 0xff;
+ regmap_write(regmap, MODELGAUGE_HIBRT_REG, reg);
+
+ /* Set undervoltage/overvoltage alerts */
+ reg = (MODELGAUGE_UV / 20) & 0xff;
+ reg <<= 8;
+ reg |= (MODELGAUGE_OV / 20) & 0xff;
+ regmap_write(regmap, MODELGAUGE_VALRT_REG, reg);
+ /* fall-through */
+ case ID_MAX17058:
+ case ID_MAX17059:
+ /* Disable sleep mode and quick start */
+ regmap_write(regmap, MODELGAUGE_MODE_REG, 0);
+
+ /* Setup reset voltage threshold */
+ if (MODELGAUGE_RV)
+ reg = ((MODELGAUGE_RV / 40) & 0x7f) << 9;
+ else
+ reg = MODELGAUGE_VRESETID_DIS;
+ regmap_write(regmap, MODELGAUGE_VRESETID_REG, reg);
+
+ /* Skip load model if reset indicator cleared */
+ regmap_read(regmap, MODELGAUGE_STATUS_REG, ®);
+ /* Skip load custom model */
+ if (!(reg & MODELGAUGE_STATUS_RI))
+ return 0;
+ break;
+ default:
+ break;
+ }
+
+ /* Schedule load custom model work */
+ if (pdata->model_data)
+ schedule_work(&priv->load_work);
+
+ return 0;
+}
+
+static struct modelgauge_platform_data *modelgauge_parse_dt(struct device *dev)
+{
+ struct device_node *np = dev->of_node;
+ struct modelgauge_platform_data *pdata;
+ struct property *prop;
+ int ret;
+
+ pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return NULL;
+
+ ret = of_property_read_u8(np, "maxim,rcomp0", &pdata->rcomp0);
+ if (ret)
+ pdata->rcomp0 = 25;
+
+ ret = of_property_read_u32(np, "maxim,temp-co-up", &pdata->temp_co_up);
+ if (ret)
+ pdata->temp_co_up = -500;
+
+ ret = of_property_read_u32(np, "maxim,temp-co-down",
+ &pdata->temp_co_down);
+ if (ret)
+ pdata->temp_co_down = -5000;
+
+ ret = of_property_read_u16(np, "maxim,ocvtest", &pdata->ocvtest);
+ if (ret)
+ pdata->ocvtest = 0;
+
+ ret = of_property_read_u8(np, "maxim,soc-check-a", &pdata->soc_check_a);
+ if (ret)
+ pdata->soc_check_a = 0;
+
+ ret = of_property_read_u8(np, "maxim,soc-check-b", &pdata->soc_check_b);
+ if (ret)
+ pdata->soc_check_b = 0;
+
+ ret = of_property_read_u8(np, "maxim,bits", &pdata->bits);
+ if (ret)
+ pdata->bits = 18;
+
+ ret = of_property_read_u16(np, "maxim,rcomp-seg", &pdata->rcomp_seg);
+ if (ret)
+ pdata->rcomp_seg = 0;
+
+ prop = of_find_property(np, "maxim,model-data", NULL);
+ if (prop && prop->length == MODELGAUGE_TABLE_SIZE) {
+ pdata->model_data = devm_kzalloc(dev, MODELGAUGE_TABLE_SIZE,
+ GFP_KERNEL);
+ if (!pdata->model_data)
+ goto out;
+
+ ret = of_property_read_u8_array(np, "maxim,model-data",
+ pdata->model_data,
+ MODELGAUGE_TABLE_SIZE);
+ if (ret) {
+ dev_warn(dev, "failed to get model_data %d\n", ret);
+ devm_kfree(dev, pdata->model_data);
+ pdata->model_data = NULL;
+ }
+ }
+
+out:
+ return pdata;
+}
+
+static const struct regmap_config modelgauge_regmap = {
+ .reg_bits = 8,
+ .val_bits = 16,
+};
+
+static int modelgauge_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
+ struct modelgauge_priv *priv;
+ int ret;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA))
+ return -EIO;
+
+ priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ if (client->dev.of_node)
+ priv->pdata = modelgauge_parse_dt(&client->dev);
+ else
+ priv->pdata = client->dev.platform_data;
+
+ priv->dev = &client->dev;
+ priv->chip = id->driver_data;
+
+ i2c_set_clientdata(client, priv);
+
+ priv->regmap = devm_regmap_init_i2c(client, &modelgauge_regmap);
+ if (IS_ERR(priv->regmap))
+ return PTR_ERR(priv->regmap);
+
+ priv->battery.name = "modelgauge_battery";
+ priv->battery.type = POWER_SUPPLY_TYPE_BATTERY;
+ priv->battery.get_property = modelgauge_get_property;
+ priv->battery.properties = modelgauge_battery_props;
+ priv->battery.num_properties = ARRAY_SIZE(modelgauge_battery_props);
+
+ INIT_WORK(&priv->load_work, modelgauge_load_model_work);
+ INIT_DELAYED_WORK(&priv->rcomp_work, modelgauge_update_rcomp_work);
+
+ ret = modelgauge_init(priv);
+ if (ret)
+ return ret;
+
+ ret = power_supply_register(&client->dev, &priv->battery);
+ if (ret) {
+ dev_err(priv->dev, "failed: power supply register\n");
+ goto err_supply;
+ }
+
+ if (client->irq) {
+ switch (priv->chip) {
+ case ID_MAX17040:
+ case ID_MAX17041:
+ dev_err(priv->dev, "alert line is not supported\n");
+ ret = -EINVAL;
+ goto err_irq;
+ default:
+ ret = devm_request_threaded_irq(priv->dev, client->irq,
+ NULL,
+ modelgauge_irq_handler,
+ IRQF_TRIGGER_FALLING,
+ priv->battery.name,
+ priv);
+ if (ret) {
+ dev_err(priv->dev, "failed to request irq %d\n",
+ client->irq);
+ goto err_irq;
+ }
+ }
+ }
+
+ return 0;
+
+err_irq:
+ power_supply_unregister(&priv->battery);
+err_supply:
+ cancel_work_sync(&priv->load_work);
+ cancel_delayed_work_sync(&priv->rcomp_work);
+ return ret;
+}
+
+static int modelgauge_remove(struct i2c_client *client)
+{
+ struct modelgauge_priv *priv = i2c_get_clientdata(client);
+
+ cancel_work_sync(&priv->load_work);
+ cancel_delayed_work_sync(&priv->rcomp_work);
+
+ power_supply_unregister(&priv->battery);
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int modelgauge_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct modelgauge_priv *priv = i2c_get_clientdata(client);
+ struct modelgauge_platform_data *pdata = priv->pdata;
+
+ if (pdata && pdata->get_temperature)
+ cancel_delayed_work_sync(&priv->rcomp_work);
+
+ switch (priv->chip) {
+ case ID_MAX17040:
+ case ID_MAX17041:
+ return 0;
+ default:
+ if (client->irq) {
+ disable_irq(client->irq);
+ enable_irq_wake(client->irq);
+ }
+ }
+
+ return 0;
+}
+
+static int modelgauge_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct modelgauge_priv *priv = i2c_get_clientdata(client);
+ struct modelgauge_platform_data *pdata = priv->pdata;
+
+ if (pdata && pdata->get_temperature)
+ schedule_delayed_work(&priv->rcomp_work,
+ msecs_to_jiffies(MODELGAUGE_RCOMP_UPDATE_DELAY));
+
+ switch (priv->chip) {
+ case ID_MAX17040:
+ case ID_MAX17041:
+ return 0;
+ default:
+ if (client->irq) {
+ disable_irq_wake(client->irq);
+ enable_irq(client->irq);
+ }
+ }
+
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(modelgauge_pm_ops,
+ modelgauge_suspend, modelgauge_resume);
+#define MODELGAUGE_PM_OPS (&modelgauge_pm_ops)
+#else
+#define MODELGAUGE_PM_OPS NULL
+#endif /* CONFIG_PM_SLEEP */
+
+static const struct of_device_id modelgauge_match[] = {
+ { .compatible = "maxim,max17040" },
+ { .compatible = "maxim,max17041" },
+ { .compatible = "maxim,max17043" },
+ { .compatible = "maxim,max17044" },
+ { .compatible = "maxim,max17048" },
+ { .compatible = "maxim,max17049" },
+ { .compatible = "maxim,max17058" },
+ { .compatible = "maxim,max17059" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, modelgauge_match);
+
+static const struct i2c_device_id modelgauge_id[] = {
+ { "max17040", ID_MAX17040 },
+ { "max17041", ID_MAX17041 },
+ { "max17043", ID_MAX17043 },
+ { "max17044", ID_MAX17044 },
+ { "max17048", ID_MAX17048 },
+ { "max17049", ID_MAX17049 },
+ { "max17058", ID_MAX17058 },
+ { "max17059", ID_MAX17059 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, modelgauge_id);
+
+static struct i2c_driver modelgauge_i2c_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ .of_match_table = of_match_ptr(modelgauge_match),
+ .pm = MODELGAUGE_PM_OPS,
+ },
+ .probe = modelgauge_probe,
+ .remove = modelgauge_remove,
+ .id_table = modelgauge_id,
+};
+module_i2c_driver(modelgauge_i2c_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Vladimir Barinov");
+MODULE_DESCRIPTION("Maxim ModelGauge fuel gauge");
Index: battery-2.6/include/linux/platform_data/battery-modelgauge.h
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ battery-2.6/include/linux/platform_data/battery-modelgauge.h 2014-02-02 01:29:34.314614874 +0400
@@ -0,0 +1,31 @@
+/*
+ * Maxim ModelGauge ICs fuel gauge driver header file
+ *
+ * Author: Vladimir Barinov <source-M4DtvfQ/ZS1MRgGoP+s0PdBPR1lH4CV8@public.gmane.org>
+ * Copyright (C) 2013 Cogent Embedded, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#ifndef __BATTERY_MODELGAUGE_H_
+#define __BATTERY_MODELGAUGE_H_
+
+#define MODELGAUGE_TABLE_SIZE 64
+
+struct modelgauge_platform_data {
+ u8 rcomp0;
+ int temp_co_up;
+ int temp_co_down;
+ u16 ocvtest;
+ u8 soc_check_a;
+ u8 soc_check_b;
+ u8 bits;
+ u16 rcomp_seg;
+ u8 *model_data;
+ int (*get_temperature)(void);
+ int (*get_charging_status)(void);
+};
+#endif
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v2 1/3] power_supply: modelgauge_battery: Maxim ModelGauge ICs gauge
@ 2014-02-01 22:23 ` Vladimir Barinov
0 siblings, 0 replies; 11+ messages in thread
From: Vladimir Barinov @ 2014-02-01 22:23 UTC (permalink / raw)
To: anton, dwmw2, linux-kernel, devicetree
Cc: mk7.kang, k.kozlowski, mark.rutland
Add Maxim ModelGauge ICs gauge driver for MAX17040/41/43/44/48/49/58/59 chips
Signed-off-by: Vladimir Barinov <vladimir.barinov@cogentembedded.com>
---
drivers/power/Kconfig | 9
drivers/power/Makefile | 1
drivers/power/modelgauge_battery.c | 838 +++++++++++++++++++++++
include/linux/platform_data/battery-modelgauge.h | 31
4 files changed, 879 insertions(+)
Index: battery-2.6/drivers/power/Kconfig
===================================================================
--- battery-2.6.orig/drivers/power/Kconfig 2014-02-02 01:29:07.434614235 +0400
+++ battery-2.6/drivers/power/Kconfig 2014-02-02 01:29:29.070614750 +0400
@@ -205,6 +205,15 @@
with MAX17042. This driver also supports max17047/50 chips which are
improved version of max17042.
+config BATTERY_MODELGAUGE
+ tristate "Maxim ModelGauge ICs fuel gauge"
+ depends on I2C
+ select REGMAP_I2C
+ help
+ ModelGauge(TM) is a Maxim algorithm incorporated in
+ MAX17040/41/43/44/48/49/58/59 fuel-gauges for lithium-ion (Li+)
+ batteries.
+
config BATTERY_Z2
tristate "Z2 battery driver"
depends on I2C && MACH_ZIPIT2
Index: battery-2.6/drivers/power/Makefile
===================================================================
--- battery-2.6.orig/drivers/power/Makefile 2014-02-02 01:29:07.462614237 +0400
+++ battery-2.6/drivers/power/Makefile 2014-02-02 01:29:12.978614368 +0400
@@ -32,6 +32,7 @@
obj-$(CONFIG_BATTERY_DA9052) += da9052-battery.o
obj-$(CONFIG_BATTERY_MAX17040) += max17040_battery.o
obj-$(CONFIG_BATTERY_MAX17042) += max17042_battery.o
+obj-$(CONFIG_BATTERY_MODELGAUGE) += modelgauge_battery.o
obj-$(CONFIG_BATTERY_Z2) += z2_battery.o
obj-$(CONFIG_BATTERY_S3C_ADC) += s3c_adc_battery.o
obj-$(CONFIG_BATTERY_TWL4030_MADC) += twl4030_madc_battery.o
Index: battery-2.6/drivers/power/modelgauge_battery.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ battery-2.6/drivers/power/modelgauge_battery.c 2014-02-02 01:29:34.314614874 +0400
@@ -0,0 +1,838 @@
+/*
+ * Maxim ModelGauge ICs fuel gauge driver
+ *
+ * Author: Vladimir Barinov <source@cogentembedded.com>
+ * Copyright (C) 2013 Cogent Embedded, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/power_supply.h>
+#include <linux/platform_device.h>
+#include <linux/platform_data/battery-modelgauge.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+#define DRV_NAME "modelgauge"
+
+/* Register offsets for ModelGauge ICs */
+#define MODELGAUGE_VCELL_REG 0x02
+#define MODELGAUGE_SOC_REG 0x04
+#define MODELGAUGE_MODE_REG 0x06
+#define MODELGAUGE_VERSION_REG 0x08
+#define MODELGAUGE_HIBRT_REG 0x0A
+#define MODELGAUGE_CONFIG_REG 0x0C
+#define MODELGAUGE_OCV_REG 0x0E
+#define MODELGAUGE_VALRT_REG 0x14
+#define MODELGAUGE_CRATE_REG 0x16
+#define MODELGAUGE_VRESETID_REG 0x18
+#define MODELGAUGE_STATUS_REG 0x1A
+#define MODELGAUGE_UNLOCK_REG 0x3E
+#define MODELGAUGE_TABLE_REG 0x40
+#define MODELGAUGE_RCOMPSEG_REG 0x80
+#define MODELGAUGE_CMD_REG 0xFE
+
+/* MODE register bits */
+#define MODELGAUGE_MODE_QUICKSTART (1 << 14)
+#define MODELGAUGE_MODE_ENSLEEP (1 << 13)
+#define MODELGAUGE_MODE_HIBSTAT (1 << 12)
+
+/* CONFIG register bits */
+#define MODELGAUGE_CONFIG_SLEEP (1 << 7)
+#define MODELGAUGE_CONFIG_ALSC (1 << 6)
+#define MODELGAUGE_CONFIG_ALRT (1 << 5)
+#define MODELGAUGE_CONFIG_ATHD_MASK 0x1f
+
+/* STATUS register bits */
+#define MODELGAUGE_STATUS_ENVR (1 << 14)
+#define MODELGAUGE_STATUS_SC (1 << 13)
+#define MODELGAUGE_STATUS_HD (1 << 12)
+#define MODELGAUGE_STATUS_VR (1 << 11)
+#define MODELGAUGE_STATUS_VL (1 << 10)
+#define MODELGAUGE_STATUS_VH (1 << 9)
+#define MODELGAUGE_STATUS_RI (1 << 8)
+
+/* VRESETID register bits */
+#define MODELGAUGE_VRESETID_DIS (1 << 8)
+
+#define MODELGAUGE_UNLOCK_VALUE 0x4a57
+#define MODELGAUGE_RESET_VALUE 0x5400
+
+#define MODELGAUGE_RCOMP_UPDATE_DELAY 60000
+
+/* Capacity threshold where an interrupt is generated on the ALRT pin */
+#define MODELGAUGE_EMPTY_ATHD 15
+/* Enable alert for 1% soc change */
+#define MODELGAUGE_SOC_CHANGE_ALERT 1
+/* Hibernate threshold (crate), where IC enters hibernate mode */
+#define MODELGAUGE_HIBRT_THD 20
+/* Active threshold (mV), where IC exits hibernate mode */
+#define MODELGAUGE_ACTIVE_THD 60
+/* Voltage (mV), when IC alerts if battery voltage less then undervoltage */
+#define MODELGAUGE_UV 0
+/* Voltage (mV), when IC alerts if battery voltage greater then overvoltage */
+#define MODELGAUGE_OV 5120
+/*
+ * Voltage threshold (mV) below which the IC resets itself.
+ * Used to detect battery removal and reinsertion
+ */
+#define MODELGAUGE_RV 0
+
+enum chip_id {
+ ID_MAX17040,
+ ID_MAX17041,
+ ID_MAX17043,
+ ID_MAX17044,
+ ID_MAX17048,
+ ID_MAX17049,
+ ID_MAX17058,
+ ID_MAX17059,
+};
+
+struct modelgauge_priv {
+ struct device *dev;
+ struct regmap *regmap;
+ struct power_supply battery;
+ struct modelgauge_platform_data *pdata;
+ enum chip_id chip;
+ struct work_struct load_work;
+ struct delayed_work rcomp_work;
+ int soc_shift;
+};
+
+static void modelgauge_write_block(struct regmap *regmap, u8 adr, u8 size,
+ u16 *data)
+{
+ int k;
+
+ /* RAM has different endianness then registers */
+ for (k = 0; k < size; k += 2, adr += 2, data++)
+ regmap_write(regmap, adr, cpu_to_be16(*data));
+}
+
+static int modelgauge_lsb_to_uvolts(struct modelgauge_priv *priv, int lsb)
+{
+ switch (priv->chip) {
+ case ID_MAX17040:
+ case ID_MAX17043:
+ return (lsb >> 4) * 1250; /* 1.25mV per bit */
+ case ID_MAX17041:
+ case ID_MAX17044:
+ return (lsb >> 4) * 2500; /* 2.5mV per bit */
+ case ID_MAX17048:
+ case ID_MAX17058:
+ return lsb * 625 / 8; /* 78.125uV per bit */
+ case ID_MAX17049:
+ case ID_MAX17059:
+ return lsb * 625 / 4; /* 156.25uV per bit */
+ default:
+ return -EINVAL;
+ }
+}
+
+static enum power_supply_property modelgauge_battery_props[] = {
+ POWER_SUPPLY_PROP_STATUS,
+ POWER_SUPPLY_PROP_VOLTAGE_NOW,
+ POWER_SUPPLY_PROP_VOLTAGE_OCV,
+ POWER_SUPPLY_PROP_CAPACITY,
+ POWER_SUPPLY_PROP_TEMP,
+};
+
+static int modelgauge_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct modelgauge_priv *priv = container_of(psy,
+ struct modelgauge_priv,
+ battery);
+ struct regmap *regmap = priv->regmap;
+ struct modelgauge_platform_data *pdata = priv->pdata;
+ int reg;
+ int ret;
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_STATUS:
+ if (pdata && pdata->get_charging_status)
+ val->intval = pdata->get_charging_status();
+ else
+ val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+ ret = regmap_read(regmap, MODELGAUGE_VCELL_REG, ®);
+ if (ret < 0)
+ return ret;
+
+ val->intval = modelgauge_lsb_to_uvolts(priv, reg);
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_OCV:
+ /* Unlock model access */
+ regmap_write(regmap, MODELGAUGE_UNLOCK_REG,
+ MODELGAUGE_UNLOCK_VALUE);
+ ret = regmap_read(regmap, MODELGAUGE_OCV_REG, ®);
+ /* Lock model access */
+ regmap_write(regmap, MODELGAUGE_UNLOCK_REG, 0);
+ if (ret < 0)
+ return ret;
+
+ val->intval = modelgauge_lsb_to_uvolts(priv, reg);
+ break;
+ case POWER_SUPPLY_PROP_CAPACITY:
+ ret = regmap_read(regmap, MODELGAUGE_SOC_REG, ®);
+ if (ret < 0)
+ return ret;
+
+ val->intval = reg / (1 << priv->soc_shift);
+ break;
+ case POWER_SUPPLY_PROP_TEMP:
+ if (pdata && pdata->get_temperature)
+ val->intval = pdata->get_temperature();
+ else
+ val->intval = 25;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static void modelgauge_update_rcomp(struct modelgauge_priv *priv)
+{
+ struct regmap *regmap = priv->regmap;
+ struct modelgauge_platform_data *pdata = priv->pdata;
+ u16 rcomp;
+ int temp;
+
+ if (pdata->get_temperature)
+ temp = pdata->get_temperature();
+ else
+ temp = 25;
+
+ if (!pdata->temp_co_up)
+ pdata->temp_co_up = -500;
+ if (!pdata->temp_co_down)
+ pdata->temp_co_down = -5000;
+
+ rcomp = pdata->rcomp0;
+ if (temp > 20)
+ rcomp += (temp - 20) * pdata->temp_co_up / 1000;
+ else
+ rcomp += (temp - 20) * pdata->temp_co_down / 1000;
+
+ /* Update RCOMP */
+ regmap_update_bits(regmap, MODELGAUGE_CONFIG_REG, 0xff, rcomp << 8);
+}
+
+static void modelgauge_update_rcomp_work(struct work_struct *work)
+{
+ struct modelgauge_priv *priv = container_of(work,
+ struct modelgauge_priv,
+ rcomp_work.work);
+
+ modelgauge_update_rcomp(priv);
+ schedule_delayed_work(&priv->rcomp_work,
+ msecs_to_jiffies(MODELGAUGE_RCOMP_UPDATE_DELAY));
+}
+
+static int modelgauge_load_model(struct modelgauge_priv *priv)
+{
+ struct regmap *regmap = priv->regmap;
+ struct modelgauge_platform_data *pdata = priv->pdata;
+ int ret = -EINVAL;
+ int timeout, k;
+ int ocv, config, soc;
+
+ /* Save CONFIG */
+ regmap_read(regmap, MODELGAUGE_CONFIG_REG, &config);
+
+ for (timeout = 0; timeout < 100; timeout++) {
+ /* Unlock model access */
+ regmap_write(regmap, MODELGAUGE_UNLOCK_REG,
+ MODELGAUGE_UNLOCK_VALUE);
+
+ /* Read OCV */
+ regmap_read(regmap, MODELGAUGE_OCV_REG, &ocv);
+ if (ocv != 0xffff)
+ break;
+ }
+
+ if (timeout >= 100) {
+ dev_err(priv->dev, "timeout to unlock model access\n");
+ ret = -EIO;
+ goto exit;
+ }
+
+ switch (priv->chip) {
+ case ID_MAX17058:
+ case ID_MAX17059:
+ /* Reset chip transaction does not provide ACK */
+ regmap_write(regmap, MODELGAUGE_CMD_REG,
+ MODELGAUGE_RESET_VALUE);
+ msleep(150);
+
+ for (timeout = 0; timeout < 100; timeout++) {
+ int reg;
+
+ /* Unlock Model Access */
+ regmap_write(regmap, MODELGAUGE_UNLOCK_REG,
+ MODELGAUGE_UNLOCK_VALUE);
+
+ /* Read OCV */
+ regmap_read(regmap, MODELGAUGE_OCV_REG, ®);
+ if (reg != 0xffff)
+ break;
+ }
+
+ if (timeout >= 100) {
+ dev_err(priv->dev, "timeout to unlock model access\n");
+ ret = -EIO;
+ goto exit;
+ }
+ break;
+ default:
+ break;
+ }
+
+ switch (priv->chip) {
+ case ID_MAX17040:
+ case ID_MAX17041:
+ case ID_MAX17043:
+ case ID_MAX17044:
+ /* Write OCV */
+ regmap_write(regmap, MODELGAUGE_OCV_REG, pdata->ocvtest);
+ /* Write RCOMP to its maximum value */
+ regmap_write(regmap, MODELGAUGE_CONFIG_REG, 0xff00);
+ break;
+ default:
+ break;
+ }
+
+ /* Write the model */
+ modelgauge_write_block(regmap, MODELGAUGE_TABLE_REG,
+ MODELGAUGE_TABLE_SIZE,
+ (u16 *)pdata->model_data);
+
+ switch (priv->chip) {
+ case ID_MAX17048:
+ case ID_MAX17049: {
+ u16 buf[16];
+
+ if (!pdata->rcomp_seg)
+ pdata->rcomp_seg = 0x80;
+
+ for (k = 0; k < 16; k++)
+ *buf = pdata->rcomp_seg;
+
+ /* Write RCOMPSeg */
+ modelgauge_write_block(regmap, MODELGAUGE_RCOMPSEG_REG,
+ 32, buf);
+ }
+ break;
+ default:
+ break;
+ }
+
+ switch (priv->chip) {
+ case ID_MAX17040:
+ case ID_MAX17041:
+ case ID_MAX17043:
+ case ID_MAX17044:
+ /* Delay at least 150ms */
+ msleep(150);
+ break;
+ default:
+ break;
+ }
+
+ /* Write OCV */
+ regmap_write(regmap, MODELGAUGE_OCV_REG, pdata->ocvtest);
+
+ switch (priv->chip) {
+ case ID_MAX17048:
+ case ID_MAX17049:
+ /* Disable Hibernate */
+ regmap_write(regmap, MODELGAUGE_HIBRT_REG, 0);
+ /* fall-through */
+ case ID_MAX17058:
+ case ID_MAX17059:
+ /* Lock Model Access */
+ regmap_write(regmap, MODELGAUGE_UNLOCK_REG, 0);
+ break;
+ default:
+ break;
+ }
+
+ /* Delay between 150ms and 600ms */
+ msleep(200);
+
+ /* Read SOC Register and compare to expected result */
+ regmap_read(regmap, MODELGAUGE_SOC_REG, &soc);
+ soc >>= 8;
+ if (soc >= pdata->soc_check_a && soc <= pdata->soc_check_b)
+ ret = 0;
+
+ switch (priv->chip) {
+ case ID_MAX17048:
+ case ID_MAX17049:
+ case ID_MAX17058:
+ case ID_MAX17059:
+ /* Unlock model access */
+ regmap_write(regmap, MODELGAUGE_UNLOCK_REG,
+ MODELGAUGE_UNLOCK_VALUE);
+ break;
+ default:
+ break;
+ }
+
+ /* Restore CONFIG and OCV */
+ regmap_write(regmap, MODELGAUGE_CONFIG_REG, config);
+ regmap_write(regmap, MODELGAUGE_OCV_REG, ocv);
+
+ switch (priv->chip) {
+ case ID_MAX17048:
+ case ID_MAX17049:
+ /* Restore Hibernate */
+ regmap_write(regmap, MODELGAUGE_HIBRT_REG,
+ (MODELGAUGE_HIBRT_THD << 8) |
+ MODELGAUGE_ACTIVE_THD);
+ break;
+ default:
+ break;
+ }
+
+exit:
+ /* Lock model access */
+ regmap_write(regmap, MODELGAUGE_UNLOCK_REG, 0);
+
+ /* Wait 150ms minimum */
+ msleep(150);
+
+ return ret;
+}
+
+static void modelgauge_load_model_work(struct work_struct *work)
+{
+ struct modelgauge_priv *priv = container_of(work,
+ struct modelgauge_priv,
+ load_work);
+ struct regmap *regmap = priv->regmap;
+ int ret;
+ int timeout;
+
+ for (timeout = 0; timeout < 10; timeout++) {
+ /* Load custom model data */
+ ret = modelgauge_load_model(priv);
+ if (!ret)
+ break;
+ }
+
+ if (timeout >= 10) {
+ dev_info(priv->dev, "failed to load custom model\n");
+ return;
+ }
+
+ switch (priv->chip) {
+ case ID_MAX17048:
+ case ID_MAX17049:
+ case ID_MAX17058:
+ case ID_MAX17059:
+ /* Clear reset indicator bit */
+ regmap_update_bits(regmap, MODELGAUGE_STATUS_REG,
+ MODELGAUGE_STATUS_RI, 0);
+ break;
+ default:
+ break;
+ }
+}
+
+static irqreturn_t modelgauge_irq_handler(int id, void *dev)
+{
+ struct modelgauge_priv *priv = dev;
+
+ /* clear alert status bit */
+ regmap_update_bits(priv->regmap, MODELGAUGE_CONFIG_REG,
+ MODELGAUGE_CONFIG_ALRT, 0);
+
+ power_supply_changed(&priv->battery);
+ return IRQ_HANDLED;
+}
+
+static int modelgauge_init(struct modelgauge_priv *priv)
+{
+ struct regmap *regmap = priv->regmap;
+ struct modelgauge_platform_data *pdata = priv->pdata;
+ int ret;
+ int reg;
+
+ ret = regmap_read(regmap, MODELGAUGE_VERSION_REG, ®);
+ if (ret < 0)
+ return -ENODEV;
+
+ dev_info(priv->dev, "IC production version 0x%04x\n", reg);
+
+ /* SOC=0 means unrecoverable IC fault, reset is a workaround */
+ regmap_read(regmap, MODELGAUGE_SOC_REG, ®);
+ if (!reg) {
+ dev_info(priv->dev, "Reset chip, SOC measurement stall\n");
+ /* Reset chip transaction does not provide ACK */
+ regmap_write(regmap, MODELGAUGE_CMD_REG,
+ MODELGAUGE_RESET_VALUE);
+ msleep(150);
+ }
+
+ /* Default model uses 8 bits per percent */
+ priv->soc_shift = 8;
+
+ if (!priv->pdata) {
+ dev_info(priv->dev, "no platform data provided\n");
+ return 0;
+ }
+
+ switch (pdata->bits) {
+ case 19:
+ priv->soc_shift = 9;
+ break;
+ case 18:
+ default:
+ priv->soc_shift = 8;
+ break;
+ }
+
+ /* Set RCOMP */
+ modelgauge_update_rcomp(priv);
+ if (pdata->get_temperature) {
+ /* Schedule update RCOMP */
+ schedule_delayed_work(&priv->rcomp_work,
+ msecs_to_jiffies(MODELGAUGE_RCOMP_UPDATE_DELAY));
+ }
+
+ /* Clear alert status bit, wake-up, set alert threshold */
+ reg = 0;
+ switch (priv->chip) {
+ case ID_MAX17048:
+ case ID_MAX17049:
+ reg |= MODELGAUGE_SOC_CHANGE_ALERT ? MODELGAUGE_CONFIG_ALSC : 0;
+ /* fall-through */
+ case ID_MAX17043:
+ case ID_MAX17044:
+ case ID_MAX17058:
+ case ID_MAX17059:
+ reg |= 32 - (MODELGAUGE_EMPTY_ATHD << (priv->soc_shift - 8));
+ break;
+ default:
+ break;
+ }
+ regmap_update_bits(regmap, MODELGAUGE_CONFIG_REG,
+ MODELGAUGE_CONFIG_ALRT | MODELGAUGE_CONFIG_SLEEP |
+ MODELGAUGE_CONFIG_ALSC | MODELGAUGE_CONFIG_ATHD_MASK,
+ reg);
+
+ switch (priv->chip) {
+ case ID_MAX17048:
+ case ID_MAX17049:
+ /* Set Hibernate thresholds */
+ reg = (MODELGAUGE_HIBRT_THD * 125 / 26) & 0xff;
+ reg <<= 8;
+ reg |= (MODELGAUGE_ACTIVE_THD * 4 / 5) & 0xff;
+ regmap_write(regmap, MODELGAUGE_HIBRT_REG, reg);
+
+ /* Set undervoltage/overvoltage alerts */
+ reg = (MODELGAUGE_UV / 20) & 0xff;
+ reg <<= 8;
+ reg |= (MODELGAUGE_OV / 20) & 0xff;
+ regmap_write(regmap, MODELGAUGE_VALRT_REG, reg);
+ /* fall-through */
+ case ID_MAX17058:
+ case ID_MAX17059:
+ /* Disable sleep mode and quick start */
+ regmap_write(regmap, MODELGAUGE_MODE_REG, 0);
+
+ /* Setup reset voltage threshold */
+ if (MODELGAUGE_RV)
+ reg = ((MODELGAUGE_RV / 40) & 0x7f) << 9;
+ else
+ reg = MODELGAUGE_VRESETID_DIS;
+ regmap_write(regmap, MODELGAUGE_VRESETID_REG, reg);
+
+ /* Skip load model if reset indicator cleared */
+ regmap_read(regmap, MODELGAUGE_STATUS_REG, ®);
+ /* Skip load custom model */
+ if (!(reg & MODELGAUGE_STATUS_RI))
+ return 0;
+ break;
+ default:
+ break;
+ }
+
+ /* Schedule load custom model work */
+ if (pdata->model_data)
+ schedule_work(&priv->load_work);
+
+ return 0;
+}
+
+static struct modelgauge_platform_data *modelgauge_parse_dt(struct device *dev)
+{
+ struct device_node *np = dev->of_node;
+ struct modelgauge_platform_data *pdata;
+ struct property *prop;
+ int ret;
+
+ pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return NULL;
+
+ ret = of_property_read_u8(np, "maxim,rcomp0", &pdata->rcomp0);
+ if (ret)
+ pdata->rcomp0 = 25;
+
+ ret = of_property_read_u32(np, "maxim,temp-co-up", &pdata->temp_co_up);
+ if (ret)
+ pdata->temp_co_up = -500;
+
+ ret = of_property_read_u32(np, "maxim,temp-co-down",
+ &pdata->temp_co_down);
+ if (ret)
+ pdata->temp_co_down = -5000;
+
+ ret = of_property_read_u16(np, "maxim,ocvtest", &pdata->ocvtest);
+ if (ret)
+ pdata->ocvtest = 0;
+
+ ret = of_property_read_u8(np, "maxim,soc-check-a", &pdata->soc_check_a);
+ if (ret)
+ pdata->soc_check_a = 0;
+
+ ret = of_property_read_u8(np, "maxim,soc-check-b", &pdata->soc_check_b);
+ if (ret)
+ pdata->soc_check_b = 0;
+
+ ret = of_property_read_u8(np, "maxim,bits", &pdata->bits);
+ if (ret)
+ pdata->bits = 18;
+
+ ret = of_property_read_u16(np, "maxim,rcomp-seg", &pdata->rcomp_seg);
+ if (ret)
+ pdata->rcomp_seg = 0;
+
+ prop = of_find_property(np, "maxim,model-data", NULL);
+ if (prop && prop->length == MODELGAUGE_TABLE_SIZE) {
+ pdata->model_data = devm_kzalloc(dev, MODELGAUGE_TABLE_SIZE,
+ GFP_KERNEL);
+ if (!pdata->model_data)
+ goto out;
+
+ ret = of_property_read_u8_array(np, "maxim,model-data",
+ pdata->model_data,
+ MODELGAUGE_TABLE_SIZE);
+ if (ret) {
+ dev_warn(dev, "failed to get model_data %d\n", ret);
+ devm_kfree(dev, pdata->model_data);
+ pdata->model_data = NULL;
+ }
+ }
+
+out:
+ return pdata;
+}
+
+static const struct regmap_config modelgauge_regmap = {
+ .reg_bits = 8,
+ .val_bits = 16,
+};
+
+static int modelgauge_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
+ struct modelgauge_priv *priv;
+ int ret;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA))
+ return -EIO;
+
+ priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ if (client->dev.of_node)
+ priv->pdata = modelgauge_parse_dt(&client->dev);
+ else
+ priv->pdata = client->dev.platform_data;
+
+ priv->dev = &client->dev;
+ priv->chip = id->driver_data;
+
+ i2c_set_clientdata(client, priv);
+
+ priv->regmap = devm_regmap_init_i2c(client, &modelgauge_regmap);
+ if (IS_ERR(priv->regmap))
+ return PTR_ERR(priv->regmap);
+
+ priv->battery.name = "modelgauge_battery";
+ priv->battery.type = POWER_SUPPLY_TYPE_BATTERY;
+ priv->battery.get_property = modelgauge_get_property;
+ priv->battery.properties = modelgauge_battery_props;
+ priv->battery.num_properties = ARRAY_SIZE(modelgauge_battery_props);
+
+ INIT_WORK(&priv->load_work, modelgauge_load_model_work);
+ INIT_DELAYED_WORK(&priv->rcomp_work, modelgauge_update_rcomp_work);
+
+ ret = modelgauge_init(priv);
+ if (ret)
+ return ret;
+
+ ret = power_supply_register(&client->dev, &priv->battery);
+ if (ret) {
+ dev_err(priv->dev, "failed: power supply register\n");
+ goto err_supply;
+ }
+
+ if (client->irq) {
+ switch (priv->chip) {
+ case ID_MAX17040:
+ case ID_MAX17041:
+ dev_err(priv->dev, "alert line is not supported\n");
+ ret = -EINVAL;
+ goto err_irq;
+ default:
+ ret = devm_request_threaded_irq(priv->dev, client->irq,
+ NULL,
+ modelgauge_irq_handler,
+ IRQF_TRIGGER_FALLING,
+ priv->battery.name,
+ priv);
+ if (ret) {
+ dev_err(priv->dev, "failed to request irq %d\n",
+ client->irq);
+ goto err_irq;
+ }
+ }
+ }
+
+ return 0;
+
+err_irq:
+ power_supply_unregister(&priv->battery);
+err_supply:
+ cancel_work_sync(&priv->load_work);
+ cancel_delayed_work_sync(&priv->rcomp_work);
+ return ret;
+}
+
+static int modelgauge_remove(struct i2c_client *client)
+{
+ struct modelgauge_priv *priv = i2c_get_clientdata(client);
+
+ cancel_work_sync(&priv->load_work);
+ cancel_delayed_work_sync(&priv->rcomp_work);
+
+ power_supply_unregister(&priv->battery);
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int modelgauge_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct modelgauge_priv *priv = i2c_get_clientdata(client);
+ struct modelgauge_platform_data *pdata = priv->pdata;
+
+ if (pdata && pdata->get_temperature)
+ cancel_delayed_work_sync(&priv->rcomp_work);
+
+ switch (priv->chip) {
+ case ID_MAX17040:
+ case ID_MAX17041:
+ return 0;
+ default:
+ if (client->irq) {
+ disable_irq(client->irq);
+ enable_irq_wake(client->irq);
+ }
+ }
+
+ return 0;
+}
+
+static int modelgauge_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct modelgauge_priv *priv = i2c_get_clientdata(client);
+ struct modelgauge_platform_data *pdata = priv->pdata;
+
+ if (pdata && pdata->get_temperature)
+ schedule_delayed_work(&priv->rcomp_work,
+ msecs_to_jiffies(MODELGAUGE_RCOMP_UPDATE_DELAY));
+
+ switch (priv->chip) {
+ case ID_MAX17040:
+ case ID_MAX17041:
+ return 0;
+ default:
+ if (client->irq) {
+ disable_irq_wake(client->irq);
+ enable_irq(client->irq);
+ }
+ }
+
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(modelgauge_pm_ops,
+ modelgauge_suspend, modelgauge_resume);
+#define MODELGAUGE_PM_OPS (&modelgauge_pm_ops)
+#else
+#define MODELGAUGE_PM_OPS NULL
+#endif /* CONFIG_PM_SLEEP */
+
+static const struct of_device_id modelgauge_match[] = {
+ { .compatible = "maxim,max17040" },
+ { .compatible = "maxim,max17041" },
+ { .compatible = "maxim,max17043" },
+ { .compatible = "maxim,max17044" },
+ { .compatible = "maxim,max17048" },
+ { .compatible = "maxim,max17049" },
+ { .compatible = "maxim,max17058" },
+ { .compatible = "maxim,max17059" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, modelgauge_match);
+
+static const struct i2c_device_id modelgauge_id[] = {
+ { "max17040", ID_MAX17040 },
+ { "max17041", ID_MAX17041 },
+ { "max17043", ID_MAX17043 },
+ { "max17044", ID_MAX17044 },
+ { "max17048", ID_MAX17048 },
+ { "max17049", ID_MAX17049 },
+ { "max17058", ID_MAX17058 },
+ { "max17059", ID_MAX17059 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, modelgauge_id);
+
+static struct i2c_driver modelgauge_i2c_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ .of_match_table = of_match_ptr(modelgauge_match),
+ .pm = MODELGAUGE_PM_OPS,
+ },
+ .probe = modelgauge_probe,
+ .remove = modelgauge_remove,
+ .id_table = modelgauge_id,
+};
+module_i2c_driver(modelgauge_i2c_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Vladimir Barinov");
+MODULE_DESCRIPTION("Maxim ModelGauge fuel gauge");
Index: battery-2.6/include/linux/platform_data/battery-modelgauge.h
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ battery-2.6/include/linux/platform_data/battery-modelgauge.h 2014-02-02 01:29:34.314614874 +0400
@@ -0,0 +1,31 @@
+/*
+ * Maxim ModelGauge ICs fuel gauge driver header file
+ *
+ * Author: Vladimir Barinov <source@cogentembedded.com>
+ * Copyright (C) 2013 Cogent Embedded, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#ifndef __BATTERY_MODELGAUGE_H_
+#define __BATTERY_MODELGAUGE_H_
+
+#define MODELGAUGE_TABLE_SIZE 64
+
+struct modelgauge_platform_data {
+ u8 rcomp0;
+ int temp_co_up;
+ int temp_co_down;
+ u16 ocvtest;
+ u8 soc_check_a;
+ u8 soc_check_b;
+ u8 bits;
+ u16 rcomp_seg;
+ u8 *model_data;
+ int (*get_temperature)(void);
+ int (*get_charging_status)(void);
+};
+#endif
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v2 2/3] dt: Document ModelGauge gauge bindings
2014-02-01 22:23 ` Vladimir Barinov
@ 2014-02-01 22:23 ` Vladimir Barinov
-1 siblings, 0 replies; 11+ messages in thread
From: Vladimir Barinov @ 2014-02-01 22:23 UTC (permalink / raw)
To: anton-9xeibp6oKSgdnm+yROfE0A, dwmw2-wEGCiKHe2LqWVfeAwA7xHQ,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA
Cc: mk7.kang-Sze3O3UU22JBDgjK7y7TUQ,
k.kozlowski-Sze3O3UU22JBDgjK7y7TUQ, mark.rutland-5wv7dgnIgG8
These bindings can be used to register Maxim ModelGauge ICs fuel gauge
(MAX17040/41/43/44/48/49/58/59)
Signed-off-by: Vladimir Barinov <vladimir.barinov-M4DtvfQ/ZS1MRgGoP+s0PdBPR1lH4CV8@public.gmane.org>
---
Documentation/devicetree/bindings/power_supply/modelgauge_battery.txt | 61 ++++++++++
1 file changed, 61 insertions(+)
Index: battery-2.6/Documentation/devicetree/bindings/power_supply/modelgauge_battery.txt
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ battery-2.6/Documentation/devicetree/bindings/power_supply/modelgauge_battery.txt 2014-02-02 01:36:12.638624341 +0400
@@ -0,0 +1,61 @@
+modelgauge_battery
+~~~~~~~~~~~~~~~~~~
+
+Required properties:
+ - compatible : should contain one of the following:
+ - "maxim,max17040" for MAX17040
+ - "maxim,max17041" for MAX17041
+ - "maxim,max17043" for MAX17043
+ - "maxim,max17044" for MAX17044
+ - "maxim,max17048" for MAX17048
+ - "maxim,max17049" for MAX17049
+ - "maxim,max17058" for MAX17058
+ - "maxim,max17059" for MAX17059
+
+Optional properties:
+ - maxim,rcomp0 : ModelGauge RCOMP parameter, used for
+ temperature compensation (u8);
+ - maxim,temp-co-up : ModelGauge TempCoUp parameter, used for
+ temperature compensation (signed);
+ - maxim,temp-co-down : ModelGauge TempCoDown parameter, used for
+ temperature compensation (signed);
+ - maxim,ocvtest : ModelGauge OCVTest parameter, used for
+ verification of Custom Model calibration data
+ loaded into IC RAM (u16);
+ - maxim,soc-check-a : ModelGauge SOCCheckA parameter, used for
+ verification of Custom Model calibration data
+ loaded into IC RAM (u8);
+ - maxim,soc-check-b : ModelGauge SOCCheckB parameter, used for
+ verification of Custom Model calibration data
+ loaded into IC RAM (u8);
+ - maxim,bits : ModelGauge Bits parameter, used as
+ scaling parameter in Custom Model algorithm (u8);
+ - maxim,model-data : ModelGauge ModelData data,
+ Custom Model calibration data (array_u8[64]).
+
+Example:
+
+modelgauge@36 {
+ compatible = "maxim,max17058";
+ reg = <0x36>;
+ interrupt-parent = <&msmgpio>;
+ interrupts = <107 0x2>;
+
+ maxim,rcomp0 = /bits/ 8 <175>;
+ maxim,temp-co-up = <(-1100)>;
+ maxim,temp-co-down = <(-4000)>;
+ maxim,ocvtest = /bits/ 16 <56144>;
+ maxim,soc-check-a = /bits/ 8 <241>;
+ maxim,soc-check-b = /bits/ 8 <243>;
+ maxim,bits = /bits/ 8 <19>;
+
+ maxim,model-data = /bits/ 8 <
+ 0x9B 0x70 0xAB 0x30 0xB5 0xA0 0xB9 0xD0
+ 0xBB 0xA0 0xBC 0x00 0xBC 0xB0 0xBD 0x00
+ 0xBD 0x60 0xBE 0x40 0xBF 0x40 0xC1 0xF0
+ 0xC5 0x60 0xC8 0xA0 0xCD 0x00 0xD1 0x50
+ 0x00 0xE0 0x01 0x80 0x18 0x60 0x1C 0x20
+ 0x54 0x00 0x6A 0xC0 0x79 0x20 0x65 0xC0
+ 0x0B 0xE0 0x2A 0xC0 0x1D 0x00 0x17 0xE0
+ 0x15 0xE0 0x11 0xE0 0x11 0x00 0x11 0x00>;
+};
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v2 2/3] dt: Document ModelGauge gauge bindings
@ 2014-02-01 22:23 ` Vladimir Barinov
0 siblings, 0 replies; 11+ messages in thread
From: Vladimir Barinov @ 2014-02-01 22:23 UTC (permalink / raw)
To: anton, dwmw2, linux-kernel, devicetree
Cc: mk7.kang, k.kozlowski, mark.rutland
These bindings can be used to register Maxim ModelGauge ICs fuel gauge
(MAX17040/41/43/44/48/49/58/59)
Signed-off-by: Vladimir Barinov <vladimir.barinov@cogentembedded.com>
---
Documentation/devicetree/bindings/power_supply/modelgauge_battery.txt | 61 ++++++++++
1 file changed, 61 insertions(+)
Index: battery-2.6/Documentation/devicetree/bindings/power_supply/modelgauge_battery.txt
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ battery-2.6/Documentation/devicetree/bindings/power_supply/modelgauge_battery.txt 2014-02-02 01:36:12.638624341 +0400
@@ -0,0 +1,61 @@
+modelgauge_battery
+~~~~~~~~~~~~~~~~~~
+
+Required properties:
+ - compatible : should contain one of the following:
+ - "maxim,max17040" for MAX17040
+ - "maxim,max17041" for MAX17041
+ - "maxim,max17043" for MAX17043
+ - "maxim,max17044" for MAX17044
+ - "maxim,max17048" for MAX17048
+ - "maxim,max17049" for MAX17049
+ - "maxim,max17058" for MAX17058
+ - "maxim,max17059" for MAX17059
+
+Optional properties:
+ - maxim,rcomp0 : ModelGauge RCOMP parameter, used for
+ temperature compensation (u8);
+ - maxim,temp-co-up : ModelGauge TempCoUp parameter, used for
+ temperature compensation (signed);
+ - maxim,temp-co-down : ModelGauge TempCoDown parameter, used for
+ temperature compensation (signed);
+ - maxim,ocvtest : ModelGauge OCVTest parameter, used for
+ verification of Custom Model calibration data
+ loaded into IC RAM (u16);
+ - maxim,soc-check-a : ModelGauge SOCCheckA parameter, used for
+ verification of Custom Model calibration data
+ loaded into IC RAM (u8);
+ - maxim,soc-check-b : ModelGauge SOCCheckB parameter, used for
+ verification of Custom Model calibration data
+ loaded into IC RAM (u8);
+ - maxim,bits : ModelGauge Bits parameter, used as
+ scaling parameter in Custom Model algorithm (u8);
+ - maxim,model-data : ModelGauge ModelData data,
+ Custom Model calibration data (array_u8[64]).
+
+Example:
+
+modelgauge@36 {
+ compatible = "maxim,max17058";
+ reg = <0x36>;
+ interrupt-parent = <&msmgpio>;
+ interrupts = <107 0x2>;
+
+ maxim,rcomp0 = /bits/ 8 <175>;
+ maxim,temp-co-up = <(-1100)>;
+ maxim,temp-co-down = <(-4000)>;
+ maxim,ocvtest = /bits/ 16 <56144>;
+ maxim,soc-check-a = /bits/ 8 <241>;
+ maxim,soc-check-b = /bits/ 8 <243>;
+ maxim,bits = /bits/ 8 <19>;
+
+ maxim,model-data = /bits/ 8 <
+ 0x9B 0x70 0xAB 0x30 0xB5 0xA0 0xB9 0xD0
+ 0xBB 0xA0 0xBC 0x00 0xBC 0xB0 0xBD 0x00
+ 0xBD 0x60 0xBE 0x40 0xBF 0x40 0xC1 0xF0
+ 0xC5 0x60 0xC8 0xA0 0xCD 0x00 0xD1 0x50
+ 0x00 0xE0 0x01 0x80 0x18 0x60 0x1C 0x20
+ 0x54 0x00 0x6A 0xC0 0x79 0x20 0x65 0xC0
+ 0x0B 0xE0 0x2A 0xC0 0x1D 0x00 0x17 0xE0
+ 0x15 0xE0 0x11 0xE0 0x11 0x00 0x11 0x00>;
+};
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v2 3/3] power_supply: modelgauge_battery: Remove Maxim MAX17040 gauge
2014-02-01 22:23 ` Vladimir Barinov
@ 2014-02-01 22:23 ` Vladimir Barinov
-1 siblings, 0 replies; 11+ messages in thread
From: Vladimir Barinov @ 2014-02-01 22:23 UTC (permalink / raw)
To: anton, dwmw2, linux-kernel, devicetree
Cc: mk7.kang, k.kozlowski, mark.rutland
Remove Maxim MAX17040 gauge driver since it is superseded by full-functional
Maxim ModelGauge ICs gauge driver for MAX17040/41/43/44/48/49/58/59 chips
Signed-off-by: Vladimir Barinov <vladimir.barinov@cogentembedded.com>
---
drivers/power/Kconfig | 8 -
drivers/power/Makefile | 1
drivers/power/max17040_battery.c | 297 ---------------------------------------
include/linux/max17040_battery.h | 19 --
4 files changed, 325 deletions(-)
Index: linux-2.6.torvalds/drivers/power/Kconfig
===================================================================
--- linux-2.6.torvalds.orig/drivers/power/Kconfig 2014-02-02 01:37:35.374626307 +0400
+++ linux-2.6.torvalds/drivers/power/Kconfig 2014-02-02 01:38:21.966627415 +0400
@@ -185,14 +185,6 @@
Say Y here to enable support for batteries charger integrated into
DA9052 PMIC.
-config BATTERY_MAX17040
- tristate "Maxim MAX17040 Fuel Gauge"
- depends on I2C
- help
- MAX17040 is fuel-gauge systems for lithium-ion (Li+) batteries
- in handheld and portable equipment. The MAX17040 is configured
- to operate with a single lithium cell
-
config BATTERY_MAX17042
tristate "Maxim MAX17042/17047/17050/8997/8966 Fuel Gauge"
depends on I2C
Index: linux-2.6.torvalds/drivers/power/Makefile
===================================================================
--- linux-2.6.torvalds.orig/drivers/power/Makefile 2014-02-02 01:37:35.000000000 +0400
+++ linux-2.6.torvalds/drivers/power/Makefile 2014-02-02 01:38:21.966627415 +0400
@@ -30,7 +30,6 @@
obj-$(CONFIG_BATTERY_BQ27x00) += bq27x00_battery.o
obj-$(CONFIG_BATTERY_DA9030) += da9030_battery.o
obj-$(CONFIG_BATTERY_DA9052) += da9052-battery.o
-obj-$(CONFIG_BATTERY_MAX17040) += max17040_battery.o
obj-$(CONFIG_BATTERY_MAX17042) += max17042_battery.o
obj-$(CONFIG_BATTERY_MODELGAUGE) += modelgauge_battery.o
obj-$(CONFIG_BATTERY_Z2) += z2_battery.o
Index: linux-2.6.torvalds/drivers/power/max17040_battery.c
===================================================================
--- linux-2.6.torvalds.orig/drivers/power/max17040_battery.c 2014-02-02 01:38:29.614627597 +0400
+++ /dev/null 1970-01-01 00:00:00.000000000 +0000
@@ -1,297 +0,0 @@
-/*
- * max17040_battery.c
- * fuel-gauge systems for lithium-ion (Li+) batteries
- *
- * Copyright (C) 2009 Samsung Electronics
- * Minkyu Kang <mk7.kang@samsung.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/platform_device.h>
-#include <linux/mutex.h>
-#include <linux/err.h>
-#include <linux/i2c.h>
-#include <linux/delay.h>
-#include <linux/power_supply.h>
-#include <linux/max17040_battery.h>
-#include <linux/slab.h>
-
-#define MAX17040_VCELL_MSB 0x02
-#define MAX17040_VCELL_LSB 0x03
-#define MAX17040_SOC_MSB 0x04
-#define MAX17040_SOC_LSB 0x05
-#define MAX17040_MODE_MSB 0x06
-#define MAX17040_MODE_LSB 0x07
-#define MAX17040_VER_MSB 0x08
-#define MAX17040_VER_LSB 0x09
-#define MAX17040_RCOMP_MSB 0x0C
-#define MAX17040_RCOMP_LSB 0x0D
-#define MAX17040_CMD_MSB 0xFE
-#define MAX17040_CMD_LSB 0xFF
-
-#define MAX17040_DELAY 1000
-#define MAX17040_BATTERY_FULL 95
-
-struct max17040_chip {
- struct i2c_client *client;
- struct delayed_work work;
- struct power_supply battery;
- struct max17040_platform_data *pdata;
-
- /* State Of Connect */
- int online;
- /* battery voltage */
- int vcell;
- /* battery capacity */
- int soc;
- /* State Of Charge */
- int status;
-};
-
-static int max17040_get_property(struct power_supply *psy,
- enum power_supply_property psp,
- union power_supply_propval *val)
-{
- struct max17040_chip *chip = container_of(psy,
- struct max17040_chip, battery);
-
- switch (psp) {
- case POWER_SUPPLY_PROP_STATUS:
- val->intval = chip->status;
- break;
- case POWER_SUPPLY_PROP_ONLINE:
- val->intval = chip->online;
- break;
- case POWER_SUPPLY_PROP_VOLTAGE_NOW:
- val->intval = chip->vcell;
- break;
- case POWER_SUPPLY_PROP_CAPACITY:
- val->intval = chip->soc;
- break;
- default:
- return -EINVAL;
- }
- return 0;
-}
-
-static int max17040_write_reg(struct i2c_client *client, int reg, u8 value)
-{
- int ret;
-
- ret = i2c_smbus_write_byte_data(client, reg, value);
-
- if (ret < 0)
- dev_err(&client->dev, "%s: err %d\n", __func__, ret);
-
- return ret;
-}
-
-static int max17040_read_reg(struct i2c_client *client, int reg)
-{
- int ret;
-
- ret = i2c_smbus_read_byte_data(client, reg);
-
- if (ret < 0)
- dev_err(&client->dev, "%s: err %d\n", __func__, ret);
-
- return ret;
-}
-
-static void max17040_reset(struct i2c_client *client)
-{
- max17040_write_reg(client, MAX17040_CMD_MSB, 0x54);
- max17040_write_reg(client, MAX17040_CMD_LSB, 0x00);
-}
-
-static void max17040_get_vcell(struct i2c_client *client)
-{
- struct max17040_chip *chip = i2c_get_clientdata(client);
- u8 msb;
- u8 lsb;
-
- msb = max17040_read_reg(client, MAX17040_VCELL_MSB);
- lsb = max17040_read_reg(client, MAX17040_VCELL_LSB);
-
- chip->vcell = (msb << 4) + (lsb >> 4);
-}
-
-static void max17040_get_soc(struct i2c_client *client)
-{
- struct max17040_chip *chip = i2c_get_clientdata(client);
- u8 msb;
- u8 lsb;
-
- msb = max17040_read_reg(client, MAX17040_SOC_MSB);
- lsb = max17040_read_reg(client, MAX17040_SOC_LSB);
-
- chip->soc = msb;
-}
-
-static void max17040_get_version(struct i2c_client *client)
-{
- u8 msb;
- u8 lsb;
-
- msb = max17040_read_reg(client, MAX17040_VER_MSB);
- lsb = max17040_read_reg(client, MAX17040_VER_LSB);
-
- dev_info(&client->dev, "MAX17040 Fuel-Gauge Ver %d%d\n", msb, lsb);
-}
-
-static void max17040_get_online(struct i2c_client *client)
-{
- struct max17040_chip *chip = i2c_get_clientdata(client);
-
- if (chip->pdata->battery_online)
- chip->online = chip->pdata->battery_online();
- else
- chip->online = 1;
-}
-
-static void max17040_get_status(struct i2c_client *client)
-{
- struct max17040_chip *chip = i2c_get_clientdata(client);
-
- if (!chip->pdata->charger_online || !chip->pdata->charger_enable) {
- chip->status = POWER_SUPPLY_STATUS_UNKNOWN;
- return;
- }
-
- if (chip->pdata->charger_online()) {
- if (chip->pdata->charger_enable())
- chip->status = POWER_SUPPLY_STATUS_CHARGING;
- else
- chip->status = POWER_SUPPLY_STATUS_NOT_CHARGING;
- } else {
- chip->status = POWER_SUPPLY_STATUS_DISCHARGING;
- }
-
- if (chip->soc > MAX17040_BATTERY_FULL)
- chip->status = POWER_SUPPLY_STATUS_FULL;
-}
-
-static void max17040_work(struct work_struct *work)
-{
- struct max17040_chip *chip;
-
- chip = container_of(work, struct max17040_chip, work.work);
-
- max17040_get_vcell(chip->client);
- max17040_get_soc(chip->client);
- max17040_get_online(chip->client);
- max17040_get_status(chip->client);
-
- schedule_delayed_work(&chip->work, MAX17040_DELAY);
-}
-
-static enum power_supply_property max17040_battery_props[] = {
- POWER_SUPPLY_PROP_STATUS,
- POWER_SUPPLY_PROP_ONLINE,
- POWER_SUPPLY_PROP_VOLTAGE_NOW,
- POWER_SUPPLY_PROP_CAPACITY,
-};
-
-static int max17040_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
-{
- struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
- struct max17040_chip *chip;
- int ret;
-
- if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE))
- return -EIO;
-
- chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
- if (!chip)
- return -ENOMEM;
-
- chip->client = client;
- chip->pdata = client->dev.platform_data;
-
- i2c_set_clientdata(client, chip);
-
- chip->battery.name = "battery";
- chip->battery.type = POWER_SUPPLY_TYPE_BATTERY;
- chip->battery.get_property = max17040_get_property;
- chip->battery.properties = max17040_battery_props;
- chip->battery.num_properties = ARRAY_SIZE(max17040_battery_props);
-
- ret = power_supply_register(&client->dev, &chip->battery);
- if (ret) {
- dev_err(&client->dev, "failed: power supply register\n");
- return ret;
- }
-
- max17040_reset(client);
- max17040_get_version(client);
-
- INIT_DEFERRABLE_WORK(&chip->work, max17040_work);
- schedule_delayed_work(&chip->work, MAX17040_DELAY);
-
- return 0;
-}
-
-static int max17040_remove(struct i2c_client *client)
-{
- struct max17040_chip *chip = i2c_get_clientdata(client);
-
- power_supply_unregister(&chip->battery);
- cancel_delayed_work(&chip->work);
- return 0;
-}
-
-#ifdef CONFIG_PM_SLEEP
-
-static int max17040_suspend(struct device *dev)
-{
- struct i2c_client *client = to_i2c_client(dev);
- struct max17040_chip *chip = i2c_get_clientdata(client);
-
- cancel_delayed_work(&chip->work);
- return 0;
-}
-
-static int max17040_resume(struct device *dev)
-{
- struct i2c_client *client = to_i2c_client(dev);
- struct max17040_chip *chip = i2c_get_clientdata(client);
-
- schedule_delayed_work(&chip->work, MAX17040_DELAY);
- return 0;
-}
-
-static SIMPLE_DEV_PM_OPS(max17040_pm_ops, max17040_suspend, max17040_resume);
-#define MAX17040_PM_OPS (&max17040_pm_ops)
-
-#else
-
-#define MAX17040_PM_OPS NULL
-
-#endif /* CONFIG_PM_SLEEP */
-
-static const struct i2c_device_id max17040_id[] = {
- { "max17040", 0 },
- { }
-};
-MODULE_DEVICE_TABLE(i2c, max17040_id);
-
-static struct i2c_driver max17040_i2c_driver = {
- .driver = {
- .name = "max17040",
- .pm = MAX17040_PM_OPS,
- },
- .probe = max17040_probe,
- .remove = max17040_remove,
- .id_table = max17040_id,
-};
-module_i2c_driver(max17040_i2c_driver);
-
-MODULE_AUTHOR("Minkyu Kang <mk7.kang@samsung.com>");
-MODULE_DESCRIPTION("MAX17040 Fuel Gauge");
-MODULE_LICENSE("GPL");
Index: linux-2.6.torvalds/include/linux/max17040_battery.h
===================================================================
--- linux-2.6.torvalds.orig/include/linux/max17040_battery.h 2014-01-15 14:11:22.000000000 +0400
+++ /dev/null 1970-01-01 00:00:00.000000000 +0000
@@ -1,19 +0,0 @@
-/*
- * Copyright (C) 2009 Samsung Electronics
- * Minkyu Kang <mk7.kang@samsung.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#ifndef __MAX17040_BATTERY_H_
-#define __MAX17040_BATTERY_H_
-
-struct max17040_platform_data {
- int (*battery_online)(void);
- int (*charger_online)(void);
- int (*charger_enable)(void);
-};
-
-#endif
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v2 3/3] power_supply: modelgauge_battery: Remove Maxim MAX17040 gauge
@ 2014-02-01 22:23 ` Vladimir Barinov
0 siblings, 0 replies; 11+ messages in thread
From: Vladimir Barinov @ 2014-02-01 22:23 UTC (permalink / raw)
To: anton, dwmw2, linux-kernel, devicetree
Cc: mk7.kang, k.kozlowski, mark.rutland
Remove Maxim MAX17040 gauge driver since it is superseded by full-functional
Maxim ModelGauge ICs gauge driver for MAX17040/41/43/44/48/49/58/59 chips
Signed-off-by: Vladimir Barinov <vladimir.barinov@cogentembedded.com>
---
drivers/power/Kconfig | 8 -
drivers/power/Makefile | 1
drivers/power/max17040_battery.c | 297 ---------------------------------------
include/linux/max17040_battery.h | 19 --
4 files changed, 325 deletions(-)
Index: linux-2.6.torvalds/drivers/power/Kconfig
===================================================================
--- linux-2.6.torvalds.orig/drivers/power/Kconfig 2014-02-02 01:37:35.374626307 +0400
+++ linux-2.6.torvalds/drivers/power/Kconfig 2014-02-02 01:38:21.966627415 +0400
@@ -185,14 +185,6 @@
Say Y here to enable support for batteries charger integrated into
DA9052 PMIC.
-config BATTERY_MAX17040
- tristate "Maxim MAX17040 Fuel Gauge"
- depends on I2C
- help
- MAX17040 is fuel-gauge systems for lithium-ion (Li+) batteries
- in handheld and portable equipment. The MAX17040 is configured
- to operate with a single lithium cell
-
config BATTERY_MAX17042
tristate "Maxim MAX17042/17047/17050/8997/8966 Fuel Gauge"
depends on I2C
Index: linux-2.6.torvalds/drivers/power/Makefile
===================================================================
--- linux-2.6.torvalds.orig/drivers/power/Makefile 2014-02-02 01:37:35.000000000 +0400
+++ linux-2.6.torvalds/drivers/power/Makefile 2014-02-02 01:38:21.966627415 +0400
@@ -30,7 +30,6 @@
obj-$(CONFIG_BATTERY_BQ27x00) += bq27x00_battery.o
obj-$(CONFIG_BATTERY_DA9030) += da9030_battery.o
obj-$(CONFIG_BATTERY_DA9052) += da9052-battery.o
-obj-$(CONFIG_BATTERY_MAX17040) += max17040_battery.o
obj-$(CONFIG_BATTERY_MAX17042) += max17042_battery.o
obj-$(CONFIG_BATTERY_MODELGAUGE) += modelgauge_battery.o
obj-$(CONFIG_BATTERY_Z2) += z2_battery.o
Index: linux-2.6.torvalds/drivers/power/max17040_battery.c
===================================================================
--- linux-2.6.torvalds.orig/drivers/power/max17040_battery.c 2014-02-02 01:38:29.614627597 +0400
+++ /dev/null 1970-01-01 00:00:00.000000000 +0000
@@ -1,297 +0,0 @@
-/*
- * max17040_battery.c
- * fuel-gauge systems for lithium-ion (Li+) batteries
- *
- * Copyright (C) 2009 Samsung Electronics
- * Minkyu Kang <mk7.kang@samsung.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/platform_device.h>
-#include <linux/mutex.h>
-#include <linux/err.h>
-#include <linux/i2c.h>
-#include <linux/delay.h>
-#include <linux/power_supply.h>
-#include <linux/max17040_battery.h>
-#include <linux/slab.h>
-
-#define MAX17040_VCELL_MSB 0x02
-#define MAX17040_VCELL_LSB 0x03
-#define MAX17040_SOC_MSB 0x04
-#define MAX17040_SOC_LSB 0x05
-#define MAX17040_MODE_MSB 0x06
-#define MAX17040_MODE_LSB 0x07
-#define MAX17040_VER_MSB 0x08
-#define MAX17040_VER_LSB 0x09
-#define MAX17040_RCOMP_MSB 0x0C
-#define MAX17040_RCOMP_LSB 0x0D
-#define MAX17040_CMD_MSB 0xFE
-#define MAX17040_CMD_LSB 0xFF
-
-#define MAX17040_DELAY 1000
-#define MAX17040_BATTERY_FULL 95
-
-struct max17040_chip {
- struct i2c_client *client;
- struct delayed_work work;
- struct power_supply battery;
- struct max17040_platform_data *pdata;
-
- /* State Of Connect */
- int online;
- /* battery voltage */
- int vcell;
- /* battery capacity */
- int soc;
- /* State Of Charge */
- int status;
-};
-
-static int max17040_get_property(struct power_supply *psy,
- enum power_supply_property psp,
- union power_supply_propval *val)
-{
- struct max17040_chip *chip = container_of(psy,
- struct max17040_chip, battery);
-
- switch (psp) {
- case POWER_SUPPLY_PROP_STATUS:
- val->intval = chip->status;
- break;
- case POWER_SUPPLY_PROP_ONLINE:
- val->intval = chip->online;
- break;
- case POWER_SUPPLY_PROP_VOLTAGE_NOW:
- val->intval = chip->vcell;
- break;
- case POWER_SUPPLY_PROP_CAPACITY:
- val->intval = chip->soc;
- break;
- default:
- return -EINVAL;
- }
- return 0;
-}
-
-static int max17040_write_reg(struct i2c_client *client, int reg, u8 value)
-{
- int ret;
-
- ret = i2c_smbus_write_byte_data(client, reg, value);
-
- if (ret < 0)
- dev_err(&client->dev, "%s: err %d\n", __func__, ret);
-
- return ret;
-}
-
-static int max17040_read_reg(struct i2c_client *client, int reg)
-{
- int ret;
-
- ret = i2c_smbus_read_byte_data(client, reg);
-
- if (ret < 0)
- dev_err(&client->dev, "%s: err %d\n", __func__, ret);
-
- return ret;
-}
-
-static void max17040_reset(struct i2c_client *client)
-{
- max17040_write_reg(client, MAX17040_CMD_MSB, 0x54);
- max17040_write_reg(client, MAX17040_CMD_LSB, 0x00);
-}
-
-static void max17040_get_vcell(struct i2c_client *client)
-{
- struct max17040_chip *chip = i2c_get_clientdata(client);
- u8 msb;
- u8 lsb;
-
- msb = max17040_read_reg(client, MAX17040_VCELL_MSB);
- lsb = max17040_read_reg(client, MAX17040_VCELL_LSB);
-
- chip->vcell = (msb << 4) + (lsb >> 4);
-}
-
-static void max17040_get_soc(struct i2c_client *client)
-{
- struct max17040_chip *chip = i2c_get_clientdata(client);
- u8 msb;
- u8 lsb;
-
- msb = max17040_read_reg(client, MAX17040_SOC_MSB);
- lsb = max17040_read_reg(client, MAX17040_SOC_LSB);
-
- chip->soc = msb;
-}
-
-static void max17040_get_version(struct i2c_client *client)
-{
- u8 msb;
- u8 lsb;
-
- msb = max17040_read_reg(client, MAX17040_VER_MSB);
- lsb = max17040_read_reg(client, MAX17040_VER_LSB);
-
- dev_info(&client->dev, "MAX17040 Fuel-Gauge Ver %d%d\n", msb, lsb);
-}
-
-static void max17040_get_online(struct i2c_client *client)
-{
- struct max17040_chip *chip = i2c_get_clientdata(client);
-
- if (chip->pdata->battery_online)
- chip->online = chip->pdata->battery_online();
- else
- chip->online = 1;
-}
-
-static void max17040_get_status(struct i2c_client *client)
-{
- struct max17040_chip *chip = i2c_get_clientdata(client);
-
- if (!chip->pdata->charger_online || !chip->pdata->charger_enable) {
- chip->status = POWER_SUPPLY_STATUS_UNKNOWN;
- return;
- }
-
- if (chip->pdata->charger_online()) {
- if (chip->pdata->charger_enable())
- chip->status = POWER_SUPPLY_STATUS_CHARGING;
- else
- chip->status = POWER_SUPPLY_STATUS_NOT_CHARGING;
- } else {
- chip->status = POWER_SUPPLY_STATUS_DISCHARGING;
- }
-
- if (chip->soc > MAX17040_BATTERY_FULL)
- chip->status = POWER_SUPPLY_STATUS_FULL;
-}
-
-static void max17040_work(struct work_struct *work)
-{
- struct max17040_chip *chip;
-
- chip = container_of(work, struct max17040_chip, work.work);
-
- max17040_get_vcell(chip->client);
- max17040_get_soc(chip->client);
- max17040_get_online(chip->client);
- max17040_get_status(chip->client);
-
- schedule_delayed_work(&chip->work, MAX17040_DELAY);
-}
-
-static enum power_supply_property max17040_battery_props[] = {
- POWER_SUPPLY_PROP_STATUS,
- POWER_SUPPLY_PROP_ONLINE,
- POWER_SUPPLY_PROP_VOLTAGE_NOW,
- POWER_SUPPLY_PROP_CAPACITY,
-};
-
-static int max17040_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
-{
- struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
- struct max17040_chip *chip;
- int ret;
-
- if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE))
- return -EIO;
-
- chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
- if (!chip)
- return -ENOMEM;
-
- chip->client = client;
- chip->pdata = client->dev.platform_data;
-
- i2c_set_clientdata(client, chip);
-
- chip->battery.name = "battery";
- chip->battery.type = POWER_SUPPLY_TYPE_BATTERY;
- chip->battery.get_property = max17040_get_property;
- chip->battery.properties = max17040_battery_props;
- chip->battery.num_properties = ARRAY_SIZE(max17040_battery_props);
-
- ret = power_supply_register(&client->dev, &chip->battery);
- if (ret) {
- dev_err(&client->dev, "failed: power supply register\n");
- return ret;
- }
-
- max17040_reset(client);
- max17040_get_version(client);
-
- INIT_DEFERRABLE_WORK(&chip->work, max17040_work);
- schedule_delayed_work(&chip->work, MAX17040_DELAY);
-
- return 0;
-}
-
-static int max17040_remove(struct i2c_client *client)
-{
- struct max17040_chip *chip = i2c_get_clientdata(client);
-
- power_supply_unregister(&chip->battery);
- cancel_delayed_work(&chip->work);
- return 0;
-}
-
-#ifdef CONFIG_PM_SLEEP
-
-static int max17040_suspend(struct device *dev)
-{
- struct i2c_client *client = to_i2c_client(dev);
- struct max17040_chip *chip = i2c_get_clientdata(client);
-
- cancel_delayed_work(&chip->work);
- return 0;
-}
-
-static int max17040_resume(struct device *dev)
-{
- struct i2c_client *client = to_i2c_client(dev);
- struct max17040_chip *chip = i2c_get_clientdata(client);
-
- schedule_delayed_work(&chip->work, MAX17040_DELAY);
- return 0;
-}
-
-static SIMPLE_DEV_PM_OPS(max17040_pm_ops, max17040_suspend, max17040_resume);
-#define MAX17040_PM_OPS (&max17040_pm_ops)
-
-#else
-
-#define MAX17040_PM_OPS NULL
-
-#endif /* CONFIG_PM_SLEEP */
-
-static const struct i2c_device_id max17040_id[] = {
- { "max17040", 0 },
- { }
-};
-MODULE_DEVICE_TABLE(i2c, max17040_id);
-
-static struct i2c_driver max17040_i2c_driver = {
- .driver = {
- .name = "max17040",
- .pm = MAX17040_PM_OPS,
- },
- .probe = max17040_probe,
- .remove = max17040_remove,
- .id_table = max17040_id,
-};
-module_i2c_driver(max17040_i2c_driver);
-
-MODULE_AUTHOR("Minkyu Kang <mk7.kang@samsung.com>");
-MODULE_DESCRIPTION("MAX17040 Fuel Gauge");
-MODULE_LICENSE("GPL");
Index: linux-2.6.torvalds/include/linux/max17040_battery.h
===================================================================
--- linux-2.6.torvalds.orig/include/linux/max17040_battery.h 2014-01-15 14:11:22.000000000 +0400
+++ /dev/null 1970-01-01 00:00:00.000000000 +0000
@@ -1,19 +0,0 @@
-/*
- * Copyright (C) 2009 Samsung Electronics
- * Minkyu Kang <mk7.kang@samsung.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#ifndef __MAX17040_BATTERY_H_
-#define __MAX17040_BATTERY_H_
-
-struct max17040_platform_data {
- int (*battery_online)(void);
- int (*charger_online)(void);
- int (*charger_enable)(void);
-};
-
-#endif
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v2 0/3] power_supply: modelgauge_battery: Add Maxim ModelGauge ICs gauge
2014-02-01 22:23 ` Vladimir Barinov
` (2 preceding siblings ...)
(?)
@ 2014-02-26 10:21 ` Vladimir Barinov
-1 siblings, 0 replies; 11+ messages in thread
From: Vladimir Barinov @ 2014-02-26 10:21 UTC (permalink / raw)
To: dbaryshkov
Cc: anton@enomsg.org, dwmw2, linux-kernel, devicetree, mk7.kang,
k.kozlowski, mark.rutland
Hello Dmitry,
In accordance to this change you've taken the responsibility for power
supply maintainership.
https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/MAINTAINERS?id=573189354b7c97cd2256b87cf083ee435584594e
It passed almost month and no answer from you.
Does it make sense to apply this patch series?
Regards,
Vladimir
On 02/02/2014 02:23 AM, Vladimir Barinov wrote:
> Hello.
>
> This adds the folowing:
> - Maxim ModelGauge ICs gauge driver for MAX17040/41/43/44/48/49/58/59 chips
> - Document DT bindings
> - Remove superseded Maxim MAX17040 gauge driver
>
> Vladimir Barinov (3):
> [1/3] power_supply: modelgauge_battery: Maxim ModelGauge ICs gauge
> [2/3] dt: Document ModelGauge gauge bindings
> [3/3] power_supply: modelgauge_battery: Remove Maxim MAX17040 gauge
>
> ---
> This patchset is against the 'kernel/git/torvalds/linux.git' repo.
>
> Changes since v1:
> - switched to REGMAP API
> - replaced request_threaded_irq with devm_request_threaded_irq
> - replaced cancel_delayed_work with _sync version
> - moved "empty_alert_threshold, soc_change_alert, hibernate_threshold,
> active_threshold, undervoltage, overvoltage, resetvoltage" parameters
> out from platform_data and DT
> - removed unused parameters "empty_adjustment, empty_adjustment"
> - added return value checks for of_property_read_XX functions
> - removed irrelevant bindings
> - fixed dt properties naming in documentation
> - added binding size description in documentation
> - removed satelite include file include/linux/max17040_battery.h
>
> Documentation/devicetree/bindings/power_supply/modelgauge_battery.txt | 61
> drivers/power/Kconfig | 17
> drivers/power/Makefile | 2
> drivers/power/max17040_battery.c | 297 ---
> drivers/power/modelgauge_battery.c | 838 ++++++++++
> include/linux/max17040_battery.h | 19
> include/linux/platform_data/battery-modelgauge.h | 31
> 7 files changed, 940 insertions(+), 325 deletions(-)
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v2 1/3] power_supply: modelgauge_battery: Maxim ModelGauge ICs gauge
2014-02-01 22:23 ` Vladimir Barinov
(?)
@ 2014-03-04 10:48 ` Krzysztof Kozlowski
-1 siblings, 0 replies; 11+ messages in thread
From: Krzysztof Kozlowski @ 2014-03-04 10:48 UTC (permalink / raw)
To: Vladimir Barinov
Cc: anton, dwmw2, linux-kernel, devicetree, mk7.kang, mark.rutland
Hi,
On Sun, 2014-02-02 at 02:23 +0400, Vladimir Barinov wrote:
> Index: battery-2.6/drivers/power/modelgauge_battery.c
> ===================================================================
> --- /dev/null 1970-01-01 00:00:00.000000000 +0000
> +++ battery-2.6/drivers/power/modelgauge_battery.c 2014-02-02 01:29:34.314614874 +0400
> @@ -0,0 +1,838 @@
> +/*
> + * Maxim ModelGauge ICs fuel gauge driver
> + *
> + * Author: Vladimir Barinov <source@cogentembedded.com>
> + * Copyright (C) 2013 Cogent Embedded, Inc.
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License as published by the
> + * Free Software Foundation; either version 2 of the License, or (at your
> + * option) any later version.
> + */
(...)
> +static int modelgauge_probe(struct i2c_client *client,
> + const struct i2c_device_id *id)
> +{
> + struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
> + struct modelgauge_priv *priv;
> + int ret;
> +
> + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA))
> + return -EIO;
> +
> + priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL);
> + if (!priv)
> + return -ENOMEM;
> +
> + if (client->dev.of_node)
> + priv->pdata = modelgauge_parse_dt(&client->dev);
> + else
> + priv->pdata = client->dev.platform_data;
> +
> + priv->dev = &client->dev;
> + priv->chip = id->driver_data;
> +
> + i2c_set_clientdata(client, priv);
> +
> + priv->regmap = devm_regmap_init_i2c(client, &modelgauge_regmap);
> + if (IS_ERR(priv->regmap))
> + return PTR_ERR(priv->regmap);
> +
> + priv->battery.name = "modelgauge_battery";
> + priv->battery.type = POWER_SUPPLY_TYPE_BATTERY;
> + priv->battery.get_property = modelgauge_get_property;
> + priv->battery.properties = modelgauge_battery_props;
> + priv->battery.num_properties = ARRAY_SIZE(modelgauge_battery_props);
> +
> + INIT_WORK(&priv->load_work, modelgauge_load_model_work);
> + INIT_DELAYED_WORK(&priv->rcomp_work, modelgauge_update_rcomp_work);
> +
> + ret = modelgauge_init(priv);
> + if (ret)
> + return ret;
> +
> + ret = power_supply_register(&client->dev, &priv->battery);
> + if (ret) {
> + dev_err(priv->dev, "failed: power supply register\n");
> + goto err_supply;
> + }
> +
> + if (client->irq) {
> + switch (priv->chip) {
> + case ID_MAX17040:
> + case ID_MAX17041:
> + dev_err(priv->dev, "alert line is not supported\n");
> + ret = -EINVAL;
> + goto err_irq;
> + default:
> + ret = devm_request_threaded_irq(priv->dev, client->irq,
> + NULL,
> + modelgauge_irq_handler,
> + IRQF_TRIGGER_FALLING,
> + priv->battery.name,
> + priv);
Shouldn't it be also IRQF_ONESHOT? Without this you may get an error:
genirq: Threaded irq requested with handler=NULL and !ONESHOT for irq
> + if (ret) {
> + dev_err(priv->dev, "failed to request irq %d\n",
> + client->irq);
> + goto err_irq;
> + }
> + }
> + }
> +
> + return 0;
> +
> +err_irq:
> + power_supply_unregister(&priv->battery);
> +err_supply:
> + cancel_work_sync(&priv->load_work);
> + cancel_delayed_work_sync(&priv->rcomp_work);
> + return ret;
> +}
> +
> +static int modelgauge_remove(struct i2c_client *client)
> +{
> + struct modelgauge_priv *priv = i2c_get_clientdata(client);
> +
> + cancel_work_sync(&priv->load_work);
> + cancel_delayed_work_sync(&priv->rcomp_work);
> +
> + power_supply_unregister(&priv->battery);
> + return 0;
> +}
> +
> +#ifdef CONFIG_PM_SLEEP
> +static int modelgauge_suspend(struct device *dev)
> +{
> + struct i2c_client *client = to_i2c_client(dev);
> + struct modelgauge_priv *priv = i2c_get_clientdata(client);
> + struct modelgauge_platform_data *pdata = priv->pdata;
> +
> + if (pdata && pdata->get_temperature)
> + cancel_delayed_work_sync(&priv->rcomp_work);
> +
> + switch (priv->chip) {
> + case ID_MAX17040:
> + case ID_MAX17041:
> + return 0;
> + default:
> + if (client->irq) {
> + disable_irq(client->irq);
> + enable_irq_wake(client->irq);
> + }
> + }
> +
> + return 0;
> +}
> +
> +static int modelgauge_resume(struct device *dev)
> +{
> + struct i2c_client *client = to_i2c_client(dev);
> + struct modelgauge_priv *priv = i2c_get_clientdata(client);
> + struct modelgauge_platform_data *pdata = priv->pdata;
> +
> + if (pdata && pdata->get_temperature)
> + schedule_delayed_work(&priv->rcomp_work,
> + msecs_to_jiffies(MODELGAUGE_RCOMP_UPDATE_DELAY));
> +
> + switch (priv->chip) {
> + case ID_MAX17040:
> + case ID_MAX17041:
> + return 0;
> + default:
> + if (client->irq) {
> + disable_irq_wake(client->irq);
> + enable_irq(client->irq);
> + }
> + }
> +
> + return 0;
> +}
> +
> +static SIMPLE_DEV_PM_OPS(modelgauge_pm_ops,
> + modelgauge_suspend, modelgauge_resume);
> +#define MODELGAUGE_PM_OPS (&modelgauge_pm_ops)
> +#else
> +#define MODELGAUGE_PM_OPS NULL
> +#endif /* CONFIG_PM_SLEEP */
You can simplify this into:
+}
+#endif /* CONFIG_PM_SLEEP */
+
+static SIMPLE_DEV_PM_OPS(modelgauge_pm_ops,
+ modelgauge_suspend, modelgauge_resume);
And later:
+static struct i2c_driver modelgauge_i2c_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ .of_match_table = of_match_ptr(modelgauge_match),
+ .pm = &modelgauge_pm_ops,
Anyway, I like the idea of merging these drivers so I'm happy to test it
further.
Best regards,
Krzysztof
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v2 1/3] power_supply: modelgauge_battery: Maxim ModelGauge ICs gauge
2014-02-01 22:23 ` Vladimir Barinov
(?)
(?)
@ 2014-03-04 12:05 ` Krzysztof Kozlowski
-1 siblings, 0 replies; 11+ messages in thread
From: Krzysztof Kozlowski @ 2014-03-04 12:05 UTC (permalink / raw)
To: Vladimir Barinov
Cc: anton, dwmw2, linux-kernel, devicetree, mk7.kang, mark.rutland
And one more comment:
On Sun, 2014-02-02 at 02:23 +0400, Vladimir Barinov wrote:
> +static int modelgauge_get_property(struct power_supply *psy,
> + enum power_supply_property psp,
> + union power_supply_propval *val)
> +{
> + struct modelgauge_priv *priv = container_of(psy,
> + struct modelgauge_priv,
> + battery);
> + struct regmap *regmap = priv->regmap;
> + struct modelgauge_platform_data *pdata = priv->pdata;
> + int reg;
> + int ret;
> +
> + switch (psp) {
> + case POWER_SUPPLY_PROP_STATUS:
> + if (pdata && pdata->get_charging_status)
> + val->intval = pdata->get_charging_status();
> + else
> + val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
> + break;
> + case POWER_SUPPLY_PROP_VOLTAGE_NOW:
> + ret = regmap_read(regmap, MODELGAUGE_VCELL_REG, ®);
> + if (ret < 0)
> + return ret;
> +
> + val->intval = modelgauge_lsb_to_uvolts(priv, reg);
> + break;
> + case POWER_SUPPLY_PROP_VOLTAGE_OCV:
> + /* Unlock model access */
> + regmap_write(regmap, MODELGAUGE_UNLOCK_REG,
> + MODELGAUGE_UNLOCK_VALUE);
> + ret = regmap_read(regmap, MODELGAUGE_OCV_REG, ®);
> + /* Lock model access */
> + regmap_write(regmap, MODELGAUGE_UNLOCK_REG, 0);
> + if (ret < 0)
> + return ret;
> +
> + val->intval = modelgauge_lsb_to_uvolts(priv, reg);
> + break;
> + case POWER_SUPPLY_PROP_CAPACITY:
> + ret = regmap_read(regmap, MODELGAUGE_SOC_REG, ®);
> + if (ret < 0)
> + return ret;
> +
> + val->intval = reg / (1 << priv->soc_shift);
> + break;
> + case POWER_SUPPLY_PROP_TEMP:
> + if (pdata && pdata->get_temperature)
> + val->intval = pdata->get_temperature();
> + else
> + val->intval = 25;
If pdata->get_temperature() is not supplied then probably the driver
should not support POWER_SUPPLY_PROP_TEMP at all. I think it is better
not to report any temperature than to report a wrong one.
Best regards,
Krzysztof
^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2014-03-04 12:05 UTC | newest]
Thread overview: 11+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2014-02-01 22:23 [PATCH v2 0/3] power_supply: modelgauge_battery: Add Maxim ModelGauge ICs gauge Vladimir Barinov
2014-02-01 22:23 ` Vladimir Barinov
[not found] ` <1391293385-27539-1-git-send-email-vladimir.barinov-M4DtvfQ/ZS1MRgGoP+s0PdBPR1lH4CV8@public.gmane.org>
2014-02-01 22:23 ` [PATCH v2 1/3] power_supply: modelgauge_battery: " Vladimir Barinov
2014-02-01 22:23 ` Vladimir Barinov
2014-03-04 10:48 ` Krzysztof Kozlowski
2014-03-04 12:05 ` Krzysztof Kozlowski
2014-02-01 22:23 ` [PATCH v2 2/3] dt: Document ModelGauge gauge bindings Vladimir Barinov
2014-02-01 22:23 ` Vladimir Barinov
2014-02-01 22:23 ` [PATCH v2 3/3] power_supply: modelgauge_battery: Remove Maxim MAX17040 gauge Vladimir Barinov
2014-02-01 22:23 ` Vladimir Barinov
2014-02-26 10:21 ` [PATCH v2 0/3] power_supply: modelgauge_battery: Add Maxim ModelGauge ICs gauge Vladimir Barinov
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.