mfd: Add support for DA9150 combined charger & fuel-gauge device

DA9150 is a combined Charger and Fuel-Gauge IC, with additional
GPIO and GPADC functionality.

Signed-off-by: Adam Thomson <Adam.Thomson.Opensource@diasemi.com>
Signed-off-by: Lee Jones <lee.jones@linaro.org>
This commit is contained in:
Adam Thomson 2014-12-22 16:51:06 +00:00 committed by Lee Jones
parent 1ae68f95de
commit b8fce55c09
5 changed files with 1649 additions and 1 deletions

View File

@ -195,6 +195,18 @@ config MFD_DA9063
Additional drivers must be enabled in order to use the functionality Additional drivers must be enabled in order to use the functionality
of the device. of the device.
config MFD_DA9150
tristate "Dialog Semiconductor DA9150 Charger Fuel-Gauge chip"
depends on I2C=y
select MFD_CORE
select REGMAP_I2C
select REGMAP_IRQ
help
This adds support for the DA9150 integrated charger and fuel-gauge
chip. This driver provides common support for accessing the device.
Additional drivers must be enabled in order to use the specific
features of the device.
config MFD_DLN2 config MFD_DLN2
tristate "Diolan DLN2 support" tristate "Diolan DLN2 support"
select MFD_CORE select MFD_CORE

View File

@ -113,7 +113,7 @@ obj-$(CONFIG_MFD_DA9055) += da9055.o
da9063-objs := da9063-core.o da9063-irq.o da9063-i2c.o da9063-objs := da9063-core.o da9063-irq.o da9063-i2c.o
obj-$(CONFIG_MFD_DA9063) += da9063.o obj-$(CONFIG_MFD_DA9063) += da9063.o
obj-$(CONFIG_MFD_DA9150) += da9150-core.o
obj-$(CONFIG_MFD_MAX14577) += max14577.o obj-$(CONFIG_MFD_MAX14577) += max14577.o
obj-$(CONFIG_MFD_MAX77686) += max77686.o obj-$(CONFIG_MFD_MAX77686) += max77686.o
obj-$(CONFIG_MFD_MAX77693) += max77693.o obj-$(CONFIG_MFD_MAX77693) += max77693.o

413
drivers/mfd/da9150-core.c Normal file
View File

@ -0,0 +1,413 @@
/*
* DA9150 Core MFD Driver
*
* Copyright (c) 2014 Dialog Semiconductor
*
* Author: Adam Thomson <Adam.Thomson.Opensource@diasemi.com>
*
* 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/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/i2c.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/mfd/core.h>
#include <linux/mfd/da9150/core.h>
#include <linux/mfd/da9150/registers.h>
static bool da9150_volatile_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case DA9150_PAGE_CON:
case DA9150_STATUS_A:
case DA9150_STATUS_B:
case DA9150_STATUS_C:
case DA9150_STATUS_D:
case DA9150_STATUS_E:
case DA9150_STATUS_F:
case DA9150_STATUS_G:
case DA9150_STATUS_H:
case DA9150_STATUS_I:
case DA9150_STATUS_J:
case DA9150_STATUS_K:
case DA9150_STATUS_L:
case DA9150_STATUS_N:
case DA9150_FAULT_LOG_A:
case DA9150_FAULT_LOG_B:
case DA9150_EVENT_E:
case DA9150_EVENT_F:
case DA9150_EVENT_G:
case DA9150_EVENT_H:
case DA9150_CONTROL_B:
case DA9150_CONTROL_C:
case DA9150_GPADC_MAN:
case DA9150_GPADC_RES_A:
case DA9150_GPADC_RES_B:
case DA9150_ADETVB_CFG_C:
case DA9150_ADETD_STAT:
case DA9150_ADET_CMPSTAT:
case DA9150_ADET_CTRL_A:
case DA9150_PPR_TCTR_B:
case DA9150_COREBTLD_STAT_A:
case DA9150_CORE_DATA_A:
case DA9150_CORE_DATA_B:
case DA9150_CORE_DATA_C:
case DA9150_CORE_DATA_D:
case DA9150_CORE2WIRE_STAT_A:
case DA9150_FW_CTRL_C:
case DA9150_FG_CTRL_B:
case DA9150_FW_CTRL_B:
case DA9150_GPADC_CMAN:
case DA9150_GPADC_CRES_A:
case DA9150_GPADC_CRES_B:
case DA9150_CC_ICHG_RES_A:
case DA9150_CC_ICHG_RES_B:
case DA9150_CC_IAVG_RES_A:
case DA9150_CC_IAVG_RES_B:
case DA9150_TAUX_CTRL_A:
case DA9150_TAUX_VALUE_H:
case DA9150_TAUX_VALUE_L:
case DA9150_TBAT_RES_A:
case DA9150_TBAT_RES_B:
return true;
default:
return false;
}
}
static const struct regmap_range_cfg da9150_range_cfg[] = {
{
.range_min = DA9150_PAGE_CON,
.range_max = DA9150_TBAT_RES_B,
.selector_reg = DA9150_PAGE_CON,
.selector_mask = DA9150_I2C_PAGE_MASK,
.selector_shift = DA9150_I2C_PAGE_SHIFT,
.window_start = 0,
.window_len = 256,
},
};
static struct regmap_config da9150_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.ranges = da9150_range_cfg,
.num_ranges = ARRAY_SIZE(da9150_range_cfg),
.max_register = DA9150_TBAT_RES_B,
.cache_type = REGCACHE_RBTREE,
.volatile_reg = da9150_volatile_reg,
};
u8 da9150_reg_read(struct da9150 *da9150, u16 reg)
{
int val, ret;
ret = regmap_read(da9150->regmap, reg, &val);
if (ret)
dev_err(da9150->dev, "Failed to read from reg 0x%x: %d\n",
reg, ret);
return (u8) val;
}
EXPORT_SYMBOL_GPL(da9150_reg_read);
void da9150_reg_write(struct da9150 *da9150, u16 reg, u8 val)
{
int ret;
ret = regmap_write(da9150->regmap, reg, val);
if (ret)
dev_err(da9150->dev, "Failed to write to reg 0x%x: %d\n",
reg, ret);
}
EXPORT_SYMBOL_GPL(da9150_reg_write);
void da9150_set_bits(struct da9150 *da9150, u16 reg, u8 mask, u8 val)
{
int ret;
ret = regmap_update_bits(da9150->regmap, reg, mask, val);
if (ret)
dev_err(da9150->dev, "Failed to set bits in reg 0x%x: %d\n",
reg, ret);
}
EXPORT_SYMBOL_GPL(da9150_set_bits);
void da9150_bulk_read(struct da9150 *da9150, u16 reg, int count, u8 *buf)
{
int ret;
ret = regmap_bulk_read(da9150->regmap, reg, buf, count);
if (ret)
dev_err(da9150->dev, "Failed to bulk read from reg 0x%x: %d\n",
reg, ret);
}
EXPORT_SYMBOL_GPL(da9150_bulk_read);
void da9150_bulk_write(struct da9150 *da9150, u16 reg, int count, const u8 *buf)
{
int ret;
ret = regmap_raw_write(da9150->regmap, reg, buf, count);
if (ret)
dev_err(da9150->dev, "Failed to bulk write to reg 0x%x %d\n",
reg, ret);
}
EXPORT_SYMBOL_GPL(da9150_bulk_write);
static struct regmap_irq da9150_irqs[] = {
[DA9150_IRQ_VBUS] = {
.reg_offset = 0,
.mask = DA9150_E_VBUS_MASK,
},
[DA9150_IRQ_CHG] = {
.reg_offset = 0,
.mask = DA9150_E_CHG_MASK,
},
[DA9150_IRQ_TCLASS] = {
.reg_offset = 0,
.mask = DA9150_E_TCLASS_MASK,
},
[DA9150_IRQ_TJUNC] = {
.reg_offset = 0,
.mask = DA9150_E_TJUNC_MASK,
},
[DA9150_IRQ_VFAULT] = {
.reg_offset = 0,
.mask = DA9150_E_VFAULT_MASK,
},
[DA9150_IRQ_CONF] = {
.reg_offset = 1,
.mask = DA9150_E_CONF_MASK,
},
[DA9150_IRQ_DAT] = {
.reg_offset = 1,
.mask = DA9150_E_DAT_MASK,
},
[DA9150_IRQ_DTYPE] = {
.reg_offset = 1,
.mask = DA9150_E_DTYPE_MASK,
},
[DA9150_IRQ_ID] = {
.reg_offset = 1,
.mask = DA9150_E_ID_MASK,
},
[DA9150_IRQ_ADP] = {
.reg_offset = 1,
.mask = DA9150_E_ADP_MASK,
},
[DA9150_IRQ_SESS_END] = {
.reg_offset = 1,
.mask = DA9150_E_SESS_END_MASK,
},
[DA9150_IRQ_SESS_VLD] = {
.reg_offset = 1,
.mask = DA9150_E_SESS_VLD_MASK,
},
[DA9150_IRQ_FG] = {
.reg_offset = 2,
.mask = DA9150_E_FG_MASK,
},
[DA9150_IRQ_GP] = {
.reg_offset = 2,
.mask = DA9150_E_GP_MASK,
},
[DA9150_IRQ_TBAT] = {
.reg_offset = 2,
.mask = DA9150_E_TBAT_MASK,
},
[DA9150_IRQ_GPIOA] = {
.reg_offset = 2,
.mask = DA9150_E_GPIOA_MASK,
},
[DA9150_IRQ_GPIOB] = {
.reg_offset = 2,
.mask = DA9150_E_GPIOB_MASK,
},
[DA9150_IRQ_GPIOC] = {
.reg_offset = 2,
.mask = DA9150_E_GPIOC_MASK,
},
[DA9150_IRQ_GPIOD] = {
.reg_offset = 2,
.mask = DA9150_E_GPIOD_MASK,
},
[DA9150_IRQ_GPADC] = {
.reg_offset = 2,
.mask = DA9150_E_GPADC_MASK,
},
[DA9150_IRQ_WKUP] = {
.reg_offset = 3,
.mask = DA9150_E_WKUP_MASK,
},
};
static struct regmap_irq_chip da9150_regmap_irq_chip = {
.name = "da9150_irq",
.status_base = DA9150_EVENT_E,
.mask_base = DA9150_IRQ_MASK_E,
.ack_base = DA9150_EVENT_E,
.num_regs = DA9150_NUM_IRQ_REGS,
.irqs = da9150_irqs,
.num_irqs = ARRAY_SIZE(da9150_irqs),
};
static struct resource da9150_gpadc_resources[] = {
{
.name = "GPADC",
.start = DA9150_IRQ_GPADC,
.end = DA9150_IRQ_GPADC,
.flags = IORESOURCE_IRQ,
},
};
static struct resource da9150_charger_resources[] = {
{
.name = "CHG_STATUS",
.start = DA9150_IRQ_CHG,
.end = DA9150_IRQ_CHG,
.flags = IORESOURCE_IRQ,
},
{
.name = "CHG_TJUNC",
.start = DA9150_IRQ_TJUNC,
.end = DA9150_IRQ_TJUNC,
.flags = IORESOURCE_IRQ,
},
{
.name = "CHG_VFAULT",
.start = DA9150_IRQ_VFAULT,
.end = DA9150_IRQ_VFAULT,
.flags = IORESOURCE_IRQ,
},
{
.name = "CHG_VBUS",
.start = DA9150_IRQ_VBUS,
.end = DA9150_IRQ_VBUS,
.flags = IORESOURCE_IRQ,
},
};
static struct mfd_cell da9150_devs[] = {
{
.name = "da9150-gpadc",
.of_compatible = "dlg,da9150-gpadc",
.resources = da9150_gpadc_resources,
.num_resources = ARRAY_SIZE(da9150_gpadc_resources),
},
{
.name = "da9150-charger",
.of_compatible = "dlg,da9150-charger",
.resources = da9150_charger_resources,
.num_resources = ARRAY_SIZE(da9150_charger_resources),
},
};
static int da9150_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct da9150 *da9150;
struct da9150_pdata *pdata = dev_get_platdata(&client->dev);
int ret;
da9150 = devm_kzalloc(&client->dev, sizeof(*da9150), GFP_KERNEL);
if (!da9150)
return -ENOMEM;
da9150->dev = &client->dev;
da9150->irq = client->irq;
i2c_set_clientdata(client, da9150);
da9150->regmap = devm_regmap_init_i2c(client, &da9150_regmap_config);
if (IS_ERR(da9150->regmap)) {
ret = PTR_ERR(da9150->regmap);
dev_err(da9150->dev, "Failed to allocate register map: %d\n",
ret);
return ret;
}
da9150->irq_base = pdata ? pdata->irq_base : -1;
ret = regmap_add_irq_chip(da9150->regmap, da9150->irq,
IRQF_TRIGGER_LOW | IRQF_ONESHOT,
da9150->irq_base, &da9150_regmap_irq_chip,
&da9150->regmap_irq_data);
if (ret)
return ret;
da9150->irq_base = regmap_irq_chip_get_base(da9150->regmap_irq_data);
enable_irq_wake(da9150->irq);
ret = mfd_add_devices(da9150->dev, -1, da9150_devs,
ARRAY_SIZE(da9150_devs), NULL,
da9150->irq_base, NULL);
if (ret) {
dev_err(da9150->dev, "Failed to add child devices: %d\n", ret);
regmap_del_irq_chip(da9150->irq, da9150->regmap_irq_data);
return ret;
}
return 0;
}
static int da9150_remove(struct i2c_client *client)
{
struct da9150 *da9150 = i2c_get_clientdata(client);
regmap_del_irq_chip(da9150->irq, da9150->regmap_irq_data);
mfd_remove_devices(da9150->dev);
return 0;
}
static void da9150_shutdown(struct i2c_client *client)
{
struct da9150 *da9150 = i2c_get_clientdata(client);
/* Make sure we have a wakup source for the device */
da9150_set_bits(da9150, DA9150_CONFIG_D,
DA9150_WKUP_PM_EN_MASK,
DA9150_WKUP_PM_EN_MASK);
/* Set device to DISABLED mode */
da9150_set_bits(da9150, DA9150_CONTROL_C,
DA9150_DISABLE_MASK, DA9150_DISABLE_MASK);
}
static const struct i2c_device_id da9150_i2c_id[] = {
{ "da9150", },
{ }
};
MODULE_DEVICE_TABLE(i2c, da9150_i2c_id);
static const struct of_device_id da9150_of_match[] = {
{ .compatible = "dlg,da9150", },
{ }
};
MODULE_DEVICE_TABLE(of, da9150_of_match);
static struct i2c_driver da9150_driver = {
.driver = {
.name = "da9150",
.of_match_table = of_match_ptr(da9150_of_match),
},
.probe = da9150_probe,
.remove = da9150_remove,
.shutdown = da9150_shutdown,
.id_table = da9150_i2c_id,
};
module_i2c_driver(da9150_driver);
MODULE_DESCRIPTION("MFD Core Driver for DA9150");
MODULE_AUTHOR("Adam Thomson <Adam.Thomson.Opensource@diasemi.com>");
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,68 @@
/*
* DA9150 MFD Driver - Core Data
*
* Copyright (c) 2014 Dialog Semiconductor
*
* Author: Adam Thomson <Adam.Thomson.Opensource@diasemi.com>
*
* 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 __DA9150_CORE_H
#define __DA9150_CORE_H
#include <linux/device.h>
#include <linux/interrupt.h>
#include <linux/regmap.h>
/* I2C address paging */
#define DA9150_REG_PAGE_SHIFT 8
#define DA9150_REG_PAGE_MASK 0xFF
/* IRQs */
#define DA9150_NUM_IRQ_REGS 4
#define DA9150_IRQ_VBUS 0
#define DA9150_IRQ_CHG 1
#define DA9150_IRQ_TCLASS 2
#define DA9150_IRQ_TJUNC 3
#define DA9150_IRQ_VFAULT 4
#define DA9150_IRQ_CONF 5
#define DA9150_IRQ_DAT 6
#define DA9150_IRQ_DTYPE 7
#define DA9150_IRQ_ID 8
#define DA9150_IRQ_ADP 9
#define DA9150_IRQ_SESS_END 10
#define DA9150_IRQ_SESS_VLD 11
#define DA9150_IRQ_FG 12
#define DA9150_IRQ_GP 13
#define DA9150_IRQ_TBAT 14
#define DA9150_IRQ_GPIOA 15
#define DA9150_IRQ_GPIOB 16
#define DA9150_IRQ_GPIOC 17
#define DA9150_IRQ_GPIOD 18
#define DA9150_IRQ_GPADC 19
#define DA9150_IRQ_WKUP 20
struct da9150_pdata {
int irq_base;
};
struct da9150 {
struct device *dev;
struct regmap *regmap;
struct regmap_irq_chip_data *regmap_irq_data;
int irq;
int irq_base;
};
/* Device I/O */
u8 da9150_reg_read(struct da9150 *da9150, u16 reg);
void da9150_reg_write(struct da9150 *da9150, u16 reg, u8 val);
void da9150_set_bits(struct da9150 *da9150, u16 reg, u8 mask, u8 val);
void da9150_bulk_read(struct da9150 *da9150, u16 reg, int count, u8 *buf);
void da9150_bulk_write(struct da9150 *da9150, u16 reg, int count, const u8 *buf);
#endif /* __DA9150_CORE_H */

File diff suppressed because it is too large Load Diff