linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v2 0/3] power_supply: modelgauge_battery: Add Maxim ModelGauge ICs gauge
@ 2014-02-01 22:23 Vladimir Barinov
  2014-02-01 22:23 ` [PATCH v2 1/3] power_supply: modelgauge_battery: " Vladimir Barinov
                   ` (3 more replies)
  0 siblings, 4 replies; 7+ 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] 7+ messages in thread

* [PATCH v2 1/3] power_supply: modelgauge_battery: Maxim ModelGauge ICs gauge
  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
  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
                   ` (2 subsequent siblings)
  3 siblings, 2 replies; 7+ 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, &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, &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, &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, &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, &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, &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, &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] 7+ messages in thread

* [PATCH v2 2/3] dt: Document ModelGauge gauge bindings
  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 ` [PATCH v2 1/3] power_supply: modelgauge_battery: " 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-26 10:21 ` [PATCH v2 0/3] power_supply: modelgauge_battery: Add Maxim ModelGauge ICs gauge Vladimir Barinov
  3 siblings, 0 replies; 7+ 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] 7+ messages in thread

* [PATCH v2 3/3] power_supply: modelgauge_battery: Remove Maxim MAX17040 gauge
  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 ` [PATCH v2 1/3] power_supply: modelgauge_battery: " Vladimir Barinov
  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-26 10:21 ` [PATCH v2 0/3] power_supply: modelgauge_battery: Add Maxim ModelGauge ICs gauge Vladimir Barinov
  3 siblings, 0 replies; 7+ 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] 7+ messages in thread

* Re: [PATCH v2 0/3] power_supply: modelgauge_battery: Add Maxim ModelGauge ICs gauge
  2014-02-01 22:23 [PATCH v2 0/3] power_supply: modelgauge_battery: Add Maxim ModelGauge ICs gauge Vladimir Barinov
                   ` (2 preceding siblings ...)
  2014-02-01 22:23 ` [PATCH v2 3/3] power_supply: modelgauge_battery: Remove Maxim MAX17040 gauge Vladimir Barinov
@ 2014-02-26 10:21 ` Vladimir Barinov
  3 siblings, 0 replies; 7+ messages in thread
From: Vladimir Barinov @ 2014-02-26 10:21 UTC (permalink / raw)
  To: dbaryshkov
  Cc: anton, 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] 7+ messages in thread

* Re: [PATCH v2 1/3] power_supply: modelgauge_battery: Maxim ModelGauge ICs gauge
  2014-02-01 22:23 ` [PATCH v2 1/3] power_supply: modelgauge_battery: " Vladimir Barinov
@ 2014-03-04 10:48   ` Krzysztof Kozlowski
  2014-03-04 12:05   ` Krzysztof Kozlowski
  1 sibling, 0 replies; 7+ 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] 7+ messages in thread

* Re: [PATCH v2 1/3] power_supply: modelgauge_battery: Maxim ModelGauge ICs gauge
  2014-02-01 22:23 ` [PATCH v2 1/3] power_supply: modelgauge_battery: " Vladimir Barinov
  2014-03-04 10:48   ` Krzysztof Kozlowski
@ 2014-03-04 12:05   ` Krzysztof Kozlowski
  1 sibling, 0 replies; 7+ 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, &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, &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, &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] 7+ messages in thread

end of thread, other threads:[~2014-03-04 12:05 UTC | newest]

Thread overview: 7+ 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 ` [PATCH v2 1/3] power_supply: modelgauge_battery: " 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 ` [PATCH v2 3/3] power_supply: modelgauge_battery: Remove Maxim MAX17040 gauge Vladimir Barinov
2014-02-26 10:21 ` [PATCH v2 0/3] power_supply: modelgauge_battery: Add Maxim ModelGauge ICs gauge Vladimir Barinov

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).