1
0
Fork 0

bd7181x: Add driver copied directly from vendor

NOTE: THIS WILL NOT BUILD, SEE NEXT COMMIT
pull/10/head
Lars Ivar Miljeteig 2019-05-03 22:01:32 +02:00 committed by Steinar Bakkemo
parent fba76239ff
commit c9ca7510b0
16 changed files with 4682 additions and 0 deletions

View File

@ -741,6 +741,12 @@ config GPIO_ADNP
enough to represent all pins, but the driver will assume a enough to represent all pins, but the driver will assume a
register layout for 64 pins (8 registers). register layout for 64 pins (8 registers).
config GPIO_BD7181X
tristate "BD7181X GPO"
depends on MFD_BD7181X
help
Say yes here to access the GPO signals of bd71815/bd71817 chip from ROHM.
config GPIO_MAX7300 config GPIO_MAX7300
tristate "Maxim MAX7300 GPIO expander" tristate "Maxim MAX7300 GPIO expander"
select GPIO_MAX730X select GPIO_MAX730X

View File

@ -34,6 +34,7 @@ obj-$(CONFIG_GPIO_ATH79) += gpio-ath79.o
obj-$(CONFIG_GPIO_ASPEED) += gpio-aspeed.o obj-$(CONFIG_GPIO_ASPEED) += gpio-aspeed.o
obj-$(CONFIG_GPIO_AXP209) += gpio-axp209.o obj-$(CONFIG_GPIO_AXP209) += gpio-axp209.o
obj-$(CONFIG_GPIO_BCM_KONA) += gpio-bcm-kona.o obj-$(CONFIG_GPIO_BCM_KONA) += gpio-bcm-kona.o
obj-$(CONFIG_GPIO_BD7181X) += gpio-bd7181x.o
obj-$(CONFIG_GPIO_BD9571MWV) += gpio-bd9571mwv.o obj-$(CONFIG_GPIO_BD9571MWV) += gpio-bd9571mwv.o
obj-$(CONFIG_GPIO_BRCMSTB) += gpio-brcmstb.o obj-$(CONFIG_GPIO_BRCMSTB) += gpio-brcmstb.o
obj-$(CONFIG_GPIO_BT8XX) += gpio-bt8xx.o obj-$(CONFIG_GPIO_BT8XX) += gpio-bt8xx.o

View File

@ -0,0 +1,201 @@
/*
* gpio-bd7181x.c
* @file Access to GPOs on ROHM BD7181XMWV chip
*
* Copyright 2014 Embest Technology Co. Ltd. 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.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#define DEBUG
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kthread.h>
#include <linux/irq.h>
#include <linux/gpio.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/mfd/bd7181x.h>
/** @brief bd7181x gpio chip core data */
static struct gpio_chip bd7181xgpo_chip;
/** @brief get gpo output value
* @param chip pointer to core data
* @param offset gpo number, start from 0
* @retval 0 success
* @retval negative error number
*/
static int bd7181xgpo_get(struct gpio_chip *chip, unsigned offset)
{
struct bd7181x *bd7181x = dev_get_drvdata(chip->dev->parent);
int ret = 0;
ret = bd7181x_reg_read(bd7181x, BD7181X_REG_GPO);
if (ret < 0)
return ret;
return (ret >> offset) & 1;
}
/** @brief set gpo direction as output
* @param chip pointer to core data
* @param offset gpo number, start from 0
* @param value output value when set direction out
* @retval 0 success
*/
static int bd7181xgpo_direction_out(struct gpio_chip *chip, unsigned offset,
int value)
{
/* This only drives GPOs, and can't change direction */
return 0;
}
/** @brief set gpo output value
* @param chip pointer to core data
* @param offset gpo number, start from 0
* @param value output value, not zero as high level, 0 as low level
* @retval 0 success
* @retval negative error number
*/
static void bd7181xgpo_set(struct gpio_chip *chip, unsigned offset, int value)
{
struct bd7181x *bd7181x = dev_get_drvdata(chip->dev->parent);
int ret;
u8 gpoctl;
ret = bd7181x_reg_read(bd7181x, BD7181X_REG_GPO);
if (ret < 0)
return;
if (value)
gpoctl = ret | (1 << offset);
else
gpoctl = ret & ~(1 << offset);
bd7181x_reg_write(bd7181x, BD7181X_REG_GPO, gpoctl);
}
/** @brief bd7181x gpio chip core data */
static struct gpio_chip bd7181xgpo_chip = {
.label = "bd7181x", ///< gpio chip name
.owner = THIS_MODULE,
.get = bd7181xgpo_get,
.direction_output = bd7181xgpo_direction_out,
.set = bd7181xgpo_set,
.can_sleep = 1,
};
/*----------------------------------------------------------------------*/
#ifdef CONFIG_OF
/** @brief retrive gpo platform data from device tree
* @param pdev platfrom device pointer
* @return pointer to platform data
* @retval NULL error
*/
static struct bd7181x_gpo_plat_data *of_gpio_bd7181x(
struct platform_device *pdev)
{
struct bd7181x_gpo_plat_data *platform_data;
struct device_node *np, *gpio_np;
platform_data = devm_kzalloc(&pdev->dev, sizeof(*platform_data), GFP_KERNEL);
if (!platform_data) {
return NULL;
}
np = of_node_get(pdev->dev.parent->of_node);
gpio_np = of_find_node_by_name(np, "gpo");
if (!gpio_np) {
dev_err(&pdev->dev, "gpio node not found\n");
return NULL;
}
pdev->dev.of_node = gpio_np;
if (of_property_read_u32(gpio_np, "rohm,mode", &platform_data->mode)) {
platform_data->mode = -1;
}
return platform_data;
}
#endif
/** @brief probe bd7181x gpo device
* @param pdev platfrom device pointer
* @retval 0 success
* @retval negative error number
*/
static int gpo_bd7181x_probe(struct platform_device *pdev)
{
struct bd7181x_gpo_plat_data *pdata = pdev->dev.platform_data;
struct device *mfd_dev = pdev->dev.parent;
struct bd7181x *bd7181x = dev_get_drvdata(mfd_dev);
int ret;
#ifdef CONFIG_OF
pdata = of_gpio_bd7181x(pdev);
#endif
if (pdata && pdata->gpio_base > 0)
bd7181xgpo_chip.base = pdata->gpio_base;
else
bd7181xgpo_chip.base = -1;
bd7181xgpo_chip.ngpio = 2; /* bd71815/bd71817 have 2 GPO */
bd7181xgpo_chip.dev = &pdev->dev;
ret = gpiochip_add(&bd7181xgpo_chip);
if (ret < 0) {
dev_err(&pdev->dev, "could not register gpiochip, %d\n", ret);
bd7181xgpo_chip.ngpio = 0;
return ret;
}
if (pdata && pdata->mode != -1UL) {
bd7181x_update_bits(bd7181x, BD7181X_REG_GPO, 0x70, pdata->mode);
}
return ret;
}
/** @brief remove bd7181x gpo device
* @param pdev platfrom device pointer
* @retval 0 success
* @retval negative error number
*/
static int gpo_bd7181x_remove(struct platform_device *pdev)
{
return gpiochip_remove(&bd7181xgpo_chip);
}
/* Note: this hardware lives inside an I2C-based multi-function device. */
MODULE_ALIAS("platform:bd7181x-gpo");
/** @brief bd7181x gpo driver core data */
static struct platform_driver gpo_bd7181x_driver = {
.driver = {
.name = "bd7181x-gpo",
.owner = THIS_MODULE,
},
.probe = gpo_bd7181x_probe,
.remove = gpo_bd7181x_remove,
};
module_platform_driver(gpo_bd7181x_driver);
MODULE_AUTHOR("Peter Yang <yanglsh@embest-tech.com>");
MODULE_DESCRIPTION("GPO interface for BD71815/BD71817");
MODULE_LICENSE("GPL");

View File

@ -1810,6 +1810,14 @@ config MFD_STM32_TIMERS
for PWM and IIO Timer. This driver allow to share the for PWM and IIO Timer. This driver allow to share the
registers between the others drivers. registers between the others drivers.
config MFD_BD7181X
bool "BD71815/BD71817 Power Management chip"
depends on I2C=y
select MFD_CORE
help
if you say yes here you get support for the BD71815/BD71817
Power Management chips.
config MFD_BD71837 config MFD_BD71837
bool "BD71837 Power Management chip" bool "BD71837 Power Management chip"
depends on I2C=y depends on I2C=y

View File

@ -231,4 +231,5 @@ obj-$(CONFIG_MFD_SUN4I_GPADC) += sun4i-gpadc.o
obj-$(CONFIG_MFD_STM32_LPTIMER) += stm32-lptimer.o obj-$(CONFIG_MFD_STM32_LPTIMER) += stm32-lptimer.o
obj-$(CONFIG_MFD_STM32_TIMERS) += stm32-timers.o obj-$(CONFIG_MFD_STM32_TIMERS) += stm32-timers.o
obj-$(CONFIG_MFD_MXS_LRADC) += mxs-lradc.o obj-$(CONFIG_MFD_MXS_LRADC) += mxs-lradc.o
obj-$(CONFIG_MFD_BD7181X) += bd7181x.o
obj-$(CONFIG_MFD_BD71837) += bd71837.o obj-$(CONFIG_MFD_BD71837) += bd71837.o

View File

@ -0,0 +1,389 @@
/*
* @file bd7181x.c -- RoHM BD7181X/BD71817 mfd driver
*
* 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.
*
* @author: Tony Luo <luofc@embedinfo.com>
* Copyright 2014 Embest Technology Co. Ltd. Inc.
*/
#define DEBUG
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/irqdomain.h>
#include <linux/gpio.h>
#include <linux/regmap.h>
#include <linux/of_device.h>
#include <linux/of_gpio.h>
#include <linux/mfd/core.h>
#include <linux/mfd/bd7181x.h>
/** @brief bd7181x irq resource */
static struct resource rtc_resources[] = {
{
.start = BD7181X_IRQ_ALARM_12,
.end = BD7181X_IRQ_ALARM_12,
.flags = IORESOURCE_IRQ,
}
};
static struct resource power_resources[] = {
// irq# 0
{
.start = BD7181X_IRQ_DCIN_03,
.end = BD7181X_IRQ_DCIN_03,
.flags = IORESOURCE_IRQ,
},
// irq# 1
{
.start = BD7181X_IRQ_BAT_MON_08,
.end = BD7181X_IRQ_BAT_MON_08,
.flags = IORESOURCE_IRQ,
},
// irq# 2
{
.start = BD7181X_IRQ_TEMPERATURE_11,
.end = BD7181X_IRQ_TEMPERATURE_11,
.flags = IORESOURCE_IRQ,
}
};
/** @brief bd7181x multi function cells */
static struct mfd_cell bd7181x_mfd_cells[] = {
{
.name = "bd7181x-pmic",
},
{
.name = "bd7181x-power",
.num_resources = ARRAY_SIZE(power_resources),
.resources = &power_resources[0],
},
{
.name = "bd7181x-gpo",
},
{
.name = "bd7181x-rtc",
.num_resources = ARRAY_SIZE(rtc_resources),
.resources = &rtc_resources[0],
},
};
/** @brief bd7181x irqs */
static const struct regmap_irq bd7181x_irqs[] = {
[BD7181X_IRQ_BUCK_01] = {
.mask = BD7181X_INT_EN_01_BUCKAST_MASK,
.reg_offset = 1,
},
[BD7181X_IRQ_DCIN_02] = {
.mask = BD7181X_INT_EN_02_DCINAST_MASK,
.reg_offset = 2,
},
[BD7181X_IRQ_DCIN_03] = {
.mask = BD7181X_INT_EN_03_DCINAST_MASK,
.reg_offset = 3,
},
[BD7181X_IRQ_VSYS_04] = {
.mask = BD7181X_INT_EN_04_VSYSAST_MASK,
.reg_offset = 4,
},
[BD7181X_IRQ_CHARGE_05] = {
.mask = BD7181X_INT_EN_05_CHGAST_MASK,
.reg_offset = 5,
},
[BD7181X_IRQ_BAT_06] = {
.mask = BD7181X_INT_EN_06_BATAST_MASK,
.reg_offset = 6,
},
[BD7181X_IRQ_BAT_MON_07] = {
.mask = BD7181X_INT_EN_07_BMONAST_MASK,
.reg_offset = 7,
},
[BD7181X_IRQ_BAT_MON_08] = {
.mask = BD7181X_INT_EN_08_BMONAST_MASK,
.reg_offset = 8,
},
[BD7181X_IRQ_BAT_MON_09] = {
.mask = BD7181X_INT_EN_09_BMONAST_MASK,
.reg_offset = 9,
},
[BD7181X_IRQ_BAT_MON_10] = {
.mask = BD7181X_INT_EN_10_BMONAST_MASK,
.reg_offset = 10,
},
[BD7181X_IRQ_TEMPERATURE_11] = {
.mask = BD7181X_INT_EN_11_TMPAST_MASK,
.reg_offset = 11,
},
[BD7181X_IRQ_ALARM_12] = {
.mask = BD7181X_INT_EN_12_ALMAST_MASK,
.reg_offset = 12,
},
};
/** @brief bd7181x irq chip definition */
static struct regmap_irq_chip bd7181x_irq_chip = {
.name = "bd7181x",
.irqs = bd7181x_irqs,
.num_irqs = ARRAY_SIZE(bd7181x_irqs),
.num_regs = 13,
.irq_reg_stride = 1,
.status_base = BD7181X_REG_INT_STAT,
.mask_base = BD7181X_REG_INT_EN_01 - 1,
.mask_invert = true,
// .ack_base = BD7181X_REG_INT_STAT_00,
};
/** @brief bd7181x irq initialize
* @param bd7181x bd7181x device to init
* @param bdinfo platform init data
* @retval 0 probe success
* @retval negative error number
*/
static int bd7181x_irq_init(struct bd7181x *bd7181x, struct bd7181x_board* bdinfo) {
int irq;
int ret = 0;
if (!bdinfo) {
dev_warn(bd7181x->dev, "No interrupt support, no pdata\n");
return -EINVAL;
}
irq = gpio_to_irq(bdinfo->gpio_intr);
bd7181x->chip_irq = irq;
printk("bd7181x->chip_irq=%d \n", bd7181x->chip_irq);
ret = regmap_add_irq_chip(bd7181x->regmap, bd7181x->chip_irq,
IRQF_ONESHOT | IRQF_TRIGGER_FALLING, bdinfo->irq_base,
&bd7181x_irq_chip, &bd7181x->irq_data);
if (ret < 0) {
dev_warn(bd7181x->dev, "Failed to add irq_chip %d\n", ret);
}
return ret;
}
/** @brief bd7181x irq initialize
* @param bd7181x bd7181x device to init
* @retval 0 probe success
* @retval negative error number
*/
static int bd7181x_irq_exit(struct bd7181x *bd7181x)
{
if (bd7181x->chip_irq > 0)
regmap_del_irq_chip(bd7181x->chip_irq, bd7181x->irq_data);
return 0;
}
/** @brief check whether volatile register
* @param dev kernel device pointer
* @param reg register index
*/
static bool is_volatile_reg(struct device *dev, unsigned int reg)
{
// struct bd7181x *bd7181x = dev_get_drvdata(dev);
/*
* Caching all regulator registers.
*/
return true;
}
/** @brief regmap configures */
static const struct regmap_config bd7181x_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.volatile_reg = is_volatile_reg,
.max_register = BD7181X_MAX_REGISTER - 1,
.cache_type = REGCACHE_RBTREE,
};
#ifdef CONFIG_OF
static struct of_device_id bd7181x_of_match[] = {
{ .compatible = "rohm,bd71815", .data = (void *)0},
{ .compatible = "rohm,bd71817", .data = (void *)1},
{ },
};
MODULE_DEVICE_TABLE(of, bd7181x_of_match);
/** @brief parse device tree data of bd7181x
* @param client client object provided by system
* @param chip_id return chip id back to caller
* @return board initialize data
*/
static struct bd7181x_board *bd7181x_parse_dt(struct i2c_client *client,
int *chip_id)
{
struct device_node *np = client->dev.of_node;
struct bd7181x_board *board_info;
unsigned int prop;
const struct of_device_id *match;
int r = 0;
match = of_match_device(bd7181x_of_match, &client->dev);
if (!match) {
dev_err(&client->dev, "Failed to find matching dt id\n");
return NULL;
}
*chip_id = (int)match->data;
board_info = devm_kzalloc(&client->dev, sizeof(*board_info),
GFP_KERNEL);
if (!board_info) {
dev_err(&client->dev, "Failed to allocate pdata\n");
return NULL;
}
board_info->gpio_intr = of_get_named_gpio(np, "gpio_intr", 0);
if (!gpio_is_valid(board_info->gpio_intr)) {
dev_err(&client->dev, "no pmic intr pin available\n");
goto err_intr;
}
r = of_property_read_u32(np, "irq_base", &prop);
if (!r) {
board_info->irq_base = prop;
} else {
board_info->irq_base = -1;
}
return board_info;
err_intr:
devm_kfree(&client->dev, board_info);
return NULL;
}
#else
static inline
struct bd7181x_board *bd7181x_parse_dt(struct i2c_client *client,
int *chip_id)
{
return NULL;
}
#endif
/** @brief probe bd7181x device
* @param i2c client object provided by system
* @param id chip id
* @retval 0 probe success
* @retval negative error number
*/
static int bd7181x_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct bd7181x *bd7181x;
struct bd7181x_board *pmic_plat_data;
struct bd7181x_board *of_pmic_plat_data = NULL;
int chip_id = id->driver_data;
int ret = 0;
pmic_plat_data = dev_get_platdata(&i2c->dev);
if (!pmic_plat_data && i2c->dev.of_node) {
pmic_plat_data = bd7181x_parse_dt(i2c, &chip_id);
of_pmic_plat_data = pmic_plat_data;
}
if (!pmic_plat_data)
return -EINVAL;
bd7181x = kzalloc(sizeof(struct bd7181x), GFP_KERNEL);
if (bd7181x == NULL)
return -ENOMEM;
bd7181x->of_plat_data = of_pmic_plat_data;
i2c_set_clientdata(i2c, bd7181x);
bd7181x->dev = &i2c->dev;
bd7181x->i2c_client = i2c;
bd7181x->id = chip_id;
mutex_init(&bd7181x->io_mutex);
bd7181x->regmap = devm_regmap_init_i2c(i2c, &bd7181x_regmap_config);
if (IS_ERR(bd7181x->regmap)) {
ret = PTR_ERR(bd7181x->regmap);
dev_err(&i2c->dev, "regmap initialization failed: %d\n", ret);
return ret;
}
ret = bd7181x_reg_read(bd7181x, BD7181X_REG_DEVICE);
if(ret < 0) {
dev_err(bd7181x->dev, "%s(): Read BD7181X_REG_DEVICE failed!\n", __func__);
goto err;
}
dev_info(bd7181x->dev, "BD7181x: Device ID=0x%X\n", ret);
bd7181x_irq_init(bd7181x, of_pmic_plat_data);
ret = mfd_add_devices(bd7181x->dev, -1,
bd7181x_mfd_cells, ARRAY_SIZE(bd7181x_mfd_cells),
NULL, 0,
regmap_irq_get_domain(bd7181x->irq_data));
if (ret < 0)
goto err;
return ret;
err:
mfd_remove_devices(bd7181x->dev);
kfree(bd7181x);
return ret;
}
/** @brief remove bd7181x device
* @param i2c client object provided by system
* @return 0
*/
static int bd7181x_i2c_remove(struct i2c_client *i2c)
{
struct bd7181x *bd7181x = i2c_get_clientdata(i2c);
bd7181x_irq_exit(bd7181x);
mfd_remove_devices(bd7181x->dev);
kfree(bd7181x);
return 0;
}
static const struct i2c_device_id bd7181x_i2c_id[] = {
{ "bd71815", 0 },
{ "bd71817", 1 },
{ }
};
MODULE_DEVICE_TABLE(i2c, bd7181x_i2c_id);
static struct i2c_driver bd7181x_i2c_driver = {
.driver = {
.name = "bd7181x",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(bd7181x_of_match),
},
.probe = bd7181x_i2c_probe,
.remove = bd7181x_i2c_remove,
.id_table = bd7181x_i2c_id,
};
static int __init bd7181x_i2c_init(void)
{
return i2c_add_driver(&bd7181x_i2c_driver);
}
/* init early so consumer devices can complete system boot */
subsys_initcall(bd7181x_i2c_init);
static void __exit bd7181x_i2c_exit(void)
{
i2c_del_driver(&bd7181x_i2c_driver);
}
module_exit(bd7181x_i2c_exit);
MODULE_AUTHOR("Tony Luo <luofc@embest-tech.com>");
MODULE_AUTHOR("Peter Yang <yanglsh@embest-tech.com>");
MODULE_DESCRIPTION("BD71815/BD71817 chip multi-function driver");
MODULE_LICENSE("GPL");

View File

@ -29,6 +29,12 @@ config APM_POWER
Say Y here to enable support APM status emulation using Say Y here to enable support APM status emulation using
battery class devices. battery class devices.
config BD7181X_POWER
tristate "ROHM BD71815/BD71817 Charger for Battery and Adapter Power"
depends on MFD_BD7181X
help
Say Y to enable support for the BD71815/BD71817 charger.
config GENERIC_ADC_BATTERY config GENERIC_ADC_BATTERY
tristate "Generic battery support using IIO" tristate "Generic battery support using IIO"
depends on IIO depends on IIO

View File

@ -10,6 +10,7 @@ obj-$(CONFIG_GENERIC_ADC_BATTERY) += generic-adc-battery.o
obj-$(CONFIG_PDA_POWER) += pda_power.o obj-$(CONFIG_PDA_POWER) += pda_power.o
obj-$(CONFIG_APM_POWER) += apm_power.o obj-$(CONFIG_APM_POWER) += apm_power.o
obj-$(CONFIG_BD7181X_POWER) += bd7181x-power.o
obj-$(CONFIG_AXP20X_POWER) += axp20x_usb_power.o obj-$(CONFIG_AXP20X_POWER) += axp20x_usb_power.o
obj-$(CONFIG_MAX8925_POWER) += max8925_power.o obj-$(CONFIG_MAX8925_POWER) += max8925_power.o
obj-$(CONFIG_WM831X_BACKUP) += wm831x_backup.o obj-$(CONFIG_WM831X_BACKUP) += wm831x_backup.o

File diff suppressed because it is too large Load Diff

View File

@ -171,6 +171,12 @@ config REGULATOR_BCM590XX
BCM590xx PMUs. This will enable support for the software BCM590xx PMUs. This will enable support for the software
controllable LDO/Switching regulators. controllable LDO/Switching regulators.
config REGULATOR_BD7181X
tristate "RoHM BD71815/BD71817 Power Regulator"
depends on MFD_BD7181X
help
This driver supports BD71815/BD71817 voltage regulator chips.
config REGULATOR_BD9571MWV config REGULATOR_BD9571MWV
tristate "ROHM BD9571MWV Regulators" tristate "ROHM BD9571MWV Regulators"
depends on MFD_BD9571MWV depends on MFD_BD9571MWV

View File

@ -26,6 +26,7 @@ obj-$(CONFIG_REGULATOR_AS3711) += as3711-regulator.o
obj-$(CONFIG_REGULATOR_AS3722) += as3722-regulator.o obj-$(CONFIG_REGULATOR_AS3722) += as3722-regulator.o
obj-$(CONFIG_REGULATOR_AXP20X) += axp20x-regulator.o obj-$(CONFIG_REGULATOR_AXP20X) += axp20x-regulator.o
obj-$(CONFIG_REGULATOR_BCM590XX) += bcm590xx-regulator.o obj-$(CONFIG_REGULATOR_BCM590XX) += bcm590xx-regulator.o
obj-$(CONFIG_REGULATOR_BD7181X) += bd7181x-regulator.o
obj-$(CONFIG_REGULATOR_BD9571MWV) += bd9571mwv-regulator.o obj-$(CONFIG_REGULATOR_BD9571MWV) += bd9571mwv-regulator.o
obj-$(CONFIG_REGULATOR_DA903X) += da903x.o obj-$(CONFIG_REGULATOR_DA903X) += da903x.o
obj-$(CONFIG_REGULATOR_DA9052) += da9052-regulator.o obj-$(CONFIG_REGULATOR_DA9052) += da9052-regulator.o

View File

@ -0,0 +1,791 @@
/*
* @file bd7181x-regulator.c RoHM BD71815/BD71817 regulator driver
*
* Copyright 2014 Embest Technology Co. Ltd. Inc.
*
* @author Tony Luo <luofc@embedinfo.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.
*
*/
#define DEBUG
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/err.h>
#include <linux/platform_device.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/machine.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/gpio.h>
#include <linux/mfd/bd7181x.h>
#include <linux/regulator/of_regulator.h>
#define BD7181X_VOL_OFFSET 0
#define BD7181X_STANDBY_OFFSET 0
#define BD7181X_DVS_BUCK_NUM 2
#define BD7181X_DVS_HIGH_LOW 2
struct bd7181x_buck_dvs {
int i2c_dvs_enable;
u32 voltage[BD7181X_DVS_HIGH_LOW];
};
struct bd7181x_regulator {
struct regulator_desc desc;
unsigned char stby_reg;
unsigned char stby_mask;
};
/** @brief bd7181x regulator type */
struct bd7181x_pmic {
struct bd7181x_regulator descs[BD7181X_REGULATOR_CNT]; /**< regulator description to system */
struct bd7181x *mfd; /**< parent device */
struct device *dev; /**< regulator kernel device */
struct regulator_dev *rdev[BD7181X_REGULATOR_CNT]; /**< regulator device of system */
struct bd7181x_buck_dvs buck_dvs[BD7181X_DVS_BUCK_NUM]; /**< buck1/2 dvs */
};
static const int bd7181x_wled_currents[] = {
// 0x00
10, 20, 30, 50,
70, 100, 200, 300,
500, 700, 1000, 2000,
3000, 4000, 5000, 6000,
// 0x10
7000, 8000, 9000, 10000,
11000, 12000, 13000, 14000,
15000, 16000, 17000, 18000,
19000, 20000, 21000, 22000,
// 0x20
23000, 24000, 25000,
};
/*
* BUCK1/2
* BUCK1RAMPRATE[1:0] BUCK1 DVS ramp rate setting
* 00: 10.00mV/usec 10mV 1uS
* 01: 5.00mV/usec 10mV 2uS
* 10: 2.50mV/usec 10mV 4uS
* 11: 1.25mV/usec 10mV 8uS
*/
static int bd7181x_buck12_set_ramp_delay(struct regulator_dev *rdev, int ramp_delay)
{
struct bd7181x_pmic *pmic = rdev_get_drvdata(rdev);
struct bd7181x *mfd = pmic->mfd;
int id = rdev->desc->id;
unsigned int ramp_value = BUCK1_RAMPRATE_10P00MV;
switch (ramp_delay) {
case 1 ... 1250:
ramp_value = BUCK1_RAMPRATE_1P25MV;
break;
case 1251 ... 2500:
ramp_value = BUCK1_RAMPRATE_2P50MV;
break;
case 2501 ... 5000:
ramp_value = BUCK1_RAMPRATE_5P00MV;
break;
case 5001 ... 10000:
ramp_value = BUCK1_RAMPRATE_10P00MV;
break;
default:
ramp_value = BUCK1_RAMPRATE_10P00MV;
dev_err(pmic->dev, "%s: ramp_delay: %d not supported, setting 10000mV//us\n",
rdev->desc->name, ramp_delay);
}
return regmap_update_bits(mfd->regmap, BD7181X_REG_BUCK1_MODE + id*0x1,
BUCK1_RAMPRATE_MASK, ramp_value << 6);
}
static int bd7181x_led_set_current_limit(struct regulator_dev *rdev,
int min_uA, int max_uA)
{
struct bd7181x_pmic* pmic = rdev_get_drvdata(rdev);
struct bd7181x* mfd = pmic->mfd;
u8 addr;
// int id = rdev_get_id(rdev);
int i;
addr = BD7181X_REG_LED_DIMM;
for (i = ARRAY_SIZE(bd7181x_wled_currents) - 1 ; i >= 0; i--) {
if (bd7181x_wled_currents[i] >= min_uA &&
bd7181x_wled_currents[i] <= max_uA)
return bd7181x_update_bits(mfd, addr, 0x3F, i);
}
return -EINVAL;
}
static int bd7181x_led_get_current_limit(struct regulator_dev *rdev)
{
struct bd7181x_pmic* pmic = rdev_get_drvdata(rdev);
struct bd7181x* mfd = pmic->mfd;
// int id = rdev_get_id(rdev);
u8 addr;
int r;
addr = BD7181X_REG_LED_DIMM;
r = bd7181x_reg_read(mfd, addr);
if (r < 0) {
return r;
}
r = r & 0x3F;
return (r < ARRAY_SIZE(bd7181x_wled_currents)) ?
bd7181x_wled_currents[r] : -EINVAL;
}
static int bd7181x_buck12_get_voltage_sel(struct regulator_dev *rdev)
{
struct bd7181x_pmic *pmic = rdev_get_drvdata(rdev);
int rid = rdev_get_id(rdev);
struct bd7181x *bd7181x = pmic->mfd;
int ret, val;
u8 regh = BD7181X_REG_BUCK1_VOLT_H + rid*0x2,
regl = BD7181X_REG_BUCK1_VOLT_L + rid*0x2;
ret = bd7181x_reg_read(bd7181x, regh);
if (ret < 0) {
return ret;
}
val = ret;
if((!(val & BUCK1_STBY_DVS)) && (!(val & BUCK1_DVSSEL))) {
ret = bd7181x_reg_read(bd7181x, regl);
if (ret < 0) {
return ret;
}
val = ret & BUCK1_L_MASK;
} else {
val &= BUCK1_H_MASK;
}
return val;
}
/*
* For Buck 1/2.
*
*/
static int bd7181x_buck12_set_voltage_sel(struct regulator_dev *rdev, unsigned sel)
{
struct bd7181x_pmic *pmic = rdev_get_drvdata(rdev);
int rid = rdev_get_id(rdev);
struct bd7181x *bd7181x = pmic->mfd;
int ret, val;
u8 regh = BD7181X_REG_BUCK1_VOLT_H + rid*0x2,
regl = BD7181X_REG_BUCK1_VOLT_L + rid*0x2;
ret = bd7181x_reg_read(bd7181x, regh);
if (ret < 0) {
return ret;
}
val = ret;
if((!(val & BUCK1_STBY_DVS)) && (!(val & BUCK1_DVSSEL))) {
ret = bd7181x_reg_write(bd7181x, regl, sel & BUCK1_L_MASK);
} else {
val = (val & 0xC0) | (sel & BUCK1_H_MASK);
ret = bd7181x_reg_write(bd7181x, regh, val);
}
return ret;
}
static struct regulator_ops bd7181x_ldo_regulator_ops = {
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
.is_enabled = regulator_is_enabled_regmap,
.list_voltage = regulator_list_voltage_linear,
.set_voltage_sel = regulator_set_voltage_sel_regmap,
.get_voltage_sel = regulator_get_voltage_sel_regmap,
};
static struct regulator_ops bd7181x_fixed_regulator_ops = {
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
.is_enabled = regulator_is_enabled_regmap,
.list_voltage = regulator_list_voltage_linear,
};
static struct regulator_ops bd7181x_buck_regulator_ops = {
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
.is_enabled = regulator_is_enabled_regmap,
.list_voltage = regulator_list_voltage_linear,
.set_voltage_sel = regulator_set_voltage_sel_regmap,
.get_voltage_sel = regulator_get_voltage_sel_regmap,
.set_voltage_time_sel = regulator_set_voltage_time_sel,
};
static struct regulator_ops bd7181x_buck12_regulator_ops = {
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
.is_enabled = regulator_is_enabled_regmap,
.list_voltage = regulator_list_voltage_linear,
.set_voltage_sel = bd7181x_buck12_set_voltage_sel,
.get_voltage_sel = bd7181x_buck12_get_voltage_sel,
.set_voltage_time_sel = regulator_set_voltage_time_sel,
.set_ramp_delay = bd7181x_buck12_set_ramp_delay,
};
static struct regulator_ops bd7181x_led_regulator_ops = {
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
.is_enabled = regulator_is_enabled_regmap,
.list_voltage = regulator_list_voltage_table,
.map_voltage = regulator_map_voltage_ascend,
.set_voltage_sel = regulator_set_voltage_sel_regmap,
.get_voltage_sel = regulator_get_voltage_sel_regmap,
.set_current_limit = bd7181x_led_set_current_limit,
.get_current_limit = bd7181x_led_get_current_limit,
};
#define BD7181X_FIXED_REG(_name, ereg, emsk, voltage) \
[BD7181X_ ## _name] = { \
.desc = { \
.name = #_name, \
.n_voltages = 1, \
.ops = &bd7181x_fixed_regulator_ops, \
.type = REGULATOR_VOLTAGE, \
.id = BD7181X_ ## _name, \
.owner = THIS_MODULE, \
.min_uV = (voltage), \
.enable_reg = (ereg), \
.enable_mask = (emsk), \
}, \
}
#define BD7181X_BUCK_REG(_name, base, ereg, min, max, step) \
[BD7181X_ ## _name] = { \
.desc = { \
.name = #_name,\
.n_voltages = ((max) - (min)) / (step) + 1, \
.ops = &bd7181x_buck_regulator_ops, \
.type = REGULATOR_VOLTAGE, \
.id = BD7181X_ ## _name, \
.owner = THIS_MODULE, \
.min_uV = (min), \
.uV_step = (step), \
.vsel_reg = (base) + BD7181X_VOL_OFFSET, \
.vsel_mask = 0x3f, \
.enable_reg = (ereg), \
.enable_mask = 0x04, \
}, \
.stby_reg = (base) + BD7181X_STANDBY_OFFSET, \
.stby_mask = 0x3f, \
}
#define BD7181X_BUCK12_REG(_name, base, ereg, min, max, step) \
[BD7181X_ ## _name] = { \
.desc = { \
.name = #_name,\
.n_voltages = ((max) - (min)) / (step) + 1, \
.ops = &bd7181x_buck12_regulator_ops, \
.type = REGULATOR_VOLTAGE, \
.id = BD7181X_ ## _name, \
.owner = THIS_MODULE, \
.min_uV = (min), \
.uV_step = (step), \
.vsel_reg = (base) + BD7181X_VOL_OFFSET, \
.vsel_mask = 0x3f, \
.enable_reg = (ereg), \
.enable_mask = 0x04, \
}, \
.stby_reg = (base) + BD7181X_STANDBY_OFFSET, \
.stby_mask = 0x3f, \
}
#define BD7181X_LED_REG(_name, base, mask, ereg, emsk, voltages) \
[BD7181X_ ## _name] = { \
.desc = { \
.name = #_name, \
.n_voltages = ARRAY_SIZE(voltages), \
.ops = &bd7181x_led_regulator_ops, \
.type = REGULATOR_CURRENT, \
.id = BD7181X_ ## _name, \
.owner = THIS_MODULE, \
.volt_table = voltages, \
.vsel_reg = (base), \
.vsel_mask = (mask), \
.enable_reg = (ereg), \
.enable_mask = (emsk), \
}, \
}
#define BD7181X_LDO_REG(_name, base, ereg, emsk, min, max, step) \
[BD7181X_ ## _name] = { \
.desc = { \
.name = #_name, \
.n_voltages = ((max) - (min)) / (step) + 1, \
.ops = &bd7181x_ldo_regulator_ops, \
.type = REGULATOR_VOLTAGE, \
.id = BD7181X_ ## _name, \
.owner = THIS_MODULE, \
.min_uV = (min), \
.uV_step = (step), \
.vsel_reg = (base), \
.vsel_mask = 0x3f, \
.enable_reg = (ereg), \
.enable_mask = (emsk), \
}, \
.stby_reg = (base), \
.stby_mask = 0x20, \
}
static struct bd7181x_regulator bd7181x_regulators[] = {
BD7181X_BUCK12_REG(BUCK1, BD7181X_REG_BUCK1_VOLT_H, BD7181X_REG_BUCK1_MODE, 800000, 2000000, 25000),
BD7181X_BUCK12_REG(BUCK2, BD7181X_REG_BUCK2_VOLT_H, BD7181X_REG_BUCK2_MODE, 800000, 2000000, 25000),
BD7181X_BUCK_REG(BUCK3, BD7181X_REG_BUCK3_VOLT, BD7181X_REG_BUCK3_MODE, 1200000, 2700000, 50000),
BD7181X_BUCK_REG(BUCK4, BD7181X_REG_BUCK4_VOLT, BD7181X_REG_BUCK4_MODE, 1100000, 1850000, 25000),
BD7181X_BUCK_REG(BUCK5, BD7181X_REG_BUCK5_VOLT, BD7181X_REG_BUCK5_MODE, 1800000, 3300000, 50000),
BD7181X_LDO_REG(LDO1, BD7181X_REG_LDO1_VOLT, BD7181X_REG_LDO_MODE1, 0x40, 800000, 3300000, 50000),
BD7181X_LDO_REG(LDO2, BD7181X_REG_LDO2_VOLT, BD7181X_REG_LDO_MODE2, 0x04, 800000, 3300000, 50000),
BD7181X_LDO_REG(LDO3, BD7181X_REG_LDO3_VOLT, BD7181X_REG_LDO_MODE2, 0x40, 800000, 3300000, 50000),
BD7181X_LDO_REG(LDO4, BD7181X_REG_LDO4_VOLT, BD7181X_REG_LDO_MODE3, 0x04, 800000, 3300000, 50000),
BD7181X_LDO_REG(LDO5, BD7181X_REG_LDO5_VOLT_H,BD7181X_REG_LDO_MODE3,0x40, 800000, 3300000, 50000),
BD7181X_FIXED_REG(LDODVREF, BD7181X_REG_LDO_MODE4, 0x40, 3000000),
BD7181X_FIXED_REG(LDOLPSR, BD7181X_REG_LDO_MODE4, 0x04, 1800000),
BD7181X_LED_REG(WLED, BD7181X_REG_LED_DIMM, 0x3F, BD7181X_REG_LED_CTRL, 0x04, bd7181x_wled_currents),
};
#ifdef CONFIG_OF
static struct of_regulator_match bd7181x_matches[] = {
{ .name = "buck1", },
{ .name = "buck2", },
{ .name = "buck3", },
{ .name = "buck4", },
{ .name = "buck5", },
{ .name = "ldo1", },
{ .name = "ldo2", },
{ .name = "ldo3", },
{ .name = "ldo4", },
{ .name = "ldo5", },
{ .name = "dvref", },
{ .name = "lpsr", },
{ .name = "wled", },
};
/**@brief parse bd7181x regulator device tree
* @param pdev platform device of bd7181x regulator
* @param bd7181x_reg_matches return regualtor matches
* @retval 0 parse success
* @retval NULL parse fail
*/
static int bd7181x_parse_dt_reg_data(
struct platform_device *pdev,
struct of_regulator_match **reg_matches)
{
// struct bd7181x *bd7181x = dev_get_drvdata(pdev->dev.parent);
struct device_node *np, *regulators;
struct of_regulator_match *matches;
int ret, count;
np = of_node_get(pdev->dev.parent->of_node);
regulators = of_find_node_by_name(np, "regulators");
if (!regulators) {
dev_err(&pdev->dev, "regulator node not found\n");
return -EINVAL;
}
count = ARRAY_SIZE(bd7181x_matches);
matches = bd7181x_matches;
ret = of_regulator_match(&pdev->dev, regulators, matches, count);
of_node_put(regulators);
if (ret < 0) {
dev_err(&pdev->dev, "Error parsing regulator init data: %d\n",
ret);
return ret;
}
*reg_matches = matches;
return 0;
}
#else
static inline int bd7181x_parse_dt_reg_data(
struct platform_device *pdev,
struct of_regulator_match **reg_matches)
{
*reg_matches = NULL;
return 0;
}
#endif
/** @brief out32k mode constants */
static const char* out32k_modes[] = {"open_drain", "cmos"};
/** @brief retrive out32k output mode */
static ssize_t show_mode(struct device *dev, struct device_attribute *attr, char *buf)
{
struct bd7181x_pmic *pmic = dev_get_drvdata(dev);
int o;
o = bd7181x_reg_read(pmic->mfd, BD7181X_REG_OUT32K);
o = (o & OUT32K_MODE) != 0;
return sprintf(buf, "%s\n", out32k_modes[o]);
}
/** @brief set out32k output mode */
static ssize_t set_mode(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct bd7181x_pmic *pmic = dev_get_drvdata(dev);
int o, r;
if (strncmp(buf, out32k_modes[0], strlen(out32k_modes[0])) == 0) {
o = 0;
} else {
o = OUT32K_MODE;
}
r = bd7181x_update_bits(pmic->mfd, BD7181X_REG_OUT32K, OUT32K_MODE, o);
if (r < 0) {
return r;
}
return count;
}
/** @brief retrive out32k output value */
static ssize_t show_value(struct device *dev, struct device_attribute *attr, char *buf)
{
struct bd7181x_pmic *pmic = dev_get_drvdata(dev);
int o;
o = bd7181x_reg_read(pmic->mfd, BD7181X_REG_OUT32K);
o = (o & OUT32K_EN) != 0;
return sprintf(buf, "%d\n", o);
}
/** @brief set o output value */
static ssize_t set_value(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct bd7181x_pmic *pmic = dev_get_drvdata(dev);
int o, r;
if (sscanf(buf, "%d", &o) < 1) {
return -EINVAL;
}
if (o != 0) {
o = OUT32K_EN;
}
r = bd7181x_update_bits(pmic->mfd, BD7181X_REG_OUT32K, OUT32K_EN, o);
if (r < 0) {
return r;
}
return count;
}
/** @brief list all supported modes */
static ssize_t available_modes(struct device *dev, struct device_attribute *attr, char *buf)
{
int i, r;
r = 0;
for (i = 0; i < ARRAY_SIZE(out32k_modes) && r >= 0; i++) {
r += sprintf(buf + r, "%s ", out32k_modes[i]);
}
r += sprintf(buf + r, "\n");
return r;
}
/** @brief list all supported values */
static ssize_t available_values(struct device *dev, struct device_attribute *attr, char *buf)
{
return sprintf(buf, "0 1 \n");
}
/** @brief retrive dvssel output value */
static ssize_t show_dvssel(struct device *dev, struct device_attribute *attr, char *buf)
{
struct bd7181x_pmic *pmic = dev_get_drvdata(dev);
int value, index = 0, i;
for (i = 0; i < BD7181X_DVS_BUCK_NUM; i++) {
value = bd7181x_reg_read(pmic->mfd, BD7181X_REG_BUCK1_VOLT_H + i*0x2);
if(value < 0)
return value;
value = (value & BUCK1_DVSSEL) != 0;
index += sprintf(buf+index, "BUCK%i: %d\n", i, value);
}
return index;
}
/** @brief set o output value */
static ssize_t set_dvssel(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct bd7181x_pmic *pmic = dev_get_drvdata(dev);
int devsel1, devsel2, ret;
if (sscanf(buf, "%d %d", &devsel1, &devsel2) < 1) {
return -EINVAL;
}
ret = bd7181x_update_bits(pmic->mfd, BD7181X_REG_BUCK1_VOLT_H, BUCK1_DVSSEL, devsel1<<7);
if(ret < 0)
return ret;
ret = bd7181x_update_bits(pmic->mfd, BD7181X_REG_BUCK2_VOLT_H, BUCK2_DVSSEL, devsel2<<7);
if(ret < 0)
return ret;
return count;
}
static DEVICE_ATTR(out32k_mode, S_IWUSR | S_IRUGO, show_mode, set_mode);
static DEVICE_ATTR(out32k_value, S_IWUSR | S_IRUGO, show_value, set_value);
static DEVICE_ATTR(available_mode, S_IWUSR | S_IRUGO, available_modes, NULL);
static DEVICE_ATTR(available_value, S_IWUSR | S_IRUGO, available_values, NULL);
static DEVICE_ATTR(dvssel, S_IWUSR | S_IRUGO, show_dvssel, set_dvssel);
/** @brief device sysfs attribute table, about o */
static struct attribute *gpo_attributes[] = {
&dev_attr_out32k_mode.attr,
&dev_attr_out32k_value.attr,
&dev_attr_available_mode.attr,
&dev_attr_available_value.attr,
&dev_attr_dvssel.attr,
NULL
};
static const struct attribute_group gpo_attr_group = {
.attrs = gpo_attributes,
};
/*----------------------------------------------------------------------*/
#ifdef CONFIG_OF
/** @brief buck1/2 dvs enable/voltage from device tree
* @param pdev platfrom device pointer
* @param buck_dvs pointer
* @return void
*/
static void of_bd7181x_buck_dvs(struct platform_device *pdev, struct bd7181x_buck_dvs *buck_dvs)
{
struct device_node *pmic_np;
pmic_np = of_node_get(pdev->dev.parent->of_node);
if (!pmic_np) {
dev_err(&pdev->dev, "could not find pmic sub-node\n");
return;
}
if (of_get_property(pmic_np, "bd7181x,pmic-buck1-uses-i2c-dvs", NULL)) {
buck_dvs[0].i2c_dvs_enable = 1;
if (of_property_read_u32_array(pmic_np,
"bd7181x,pmic-buck1-dvs-voltage",
&buck_dvs[0].voltage[0], 2)) {
dev_err(&pdev->dev, "buck1 voltages not specified\n");
}
}
if (of_get_property(pmic_np, "bd7181x,pmic-buck2-uses-i2c-dvs", NULL)) {
buck_dvs[1].i2c_dvs_enable = 1;
if (of_property_read_u32_array(pmic_np,
"bd7181x,pmic-buck2-dvs-voltage",
&buck_dvs[1].voltage[0], 2)) {
dev_err(&pdev->dev, "buck2 voltages not specified\n");
}
}
}
#else
static void of_bd7181x_buck_dvs(struct platform_device *pdev, struct bd7181x_buck_dvs *buck_dvs)
{
buck_dvs[0].i2c_dvs_enable = 0;
buck_dvs[0].voltage[0] = BUCK1_H_DEFAULT;
buck_dvs[0].voltage[1] = BUCK1_L_DEFAULT;
buck_dvs[1].i2c_dvs_enable = 0;
buck_dvs[1].voltage[0] = BUCK1_H_DEFAULT;
buck_dvs[1].voltage[1] = BUCK1_L_DEFAULT;
}
#endif
static int bd7181x_buck12_dvs_init(struct bd7181x_pmic *pmic)
{
struct bd7181x *bd7181x = pmic->mfd;
struct bd7181x_buck_dvs *buck_dvs = &pmic->buck_dvs[0];
int i, ret, val, selector = 0;
u8 regh, regl;
for(i = 0; i < BD7181X_DVS_BUCK_NUM; i++, buck_dvs++) {
regh = BD7181X_REG_BUCK1_VOLT_H + i*0x2;
regl = BD7181X_REG_BUCK1_VOLT_L + i*0x2;
val = BUCK1_DVSSEL;
if(buck_dvs->i2c_dvs_enable) {
dev_info(pmic->dev, "Buck%d: I2C DVS Enabled !\n", i);
val &= ~BUCK1_STBY_DVS;
}
dev_info(pmic->dev, "Buck%d: DVS High-Low[%d - %d].\n", i, buck_dvs->voltage[0], buck_dvs->voltage[1]);
selector = regulator_map_voltage_iterate(pmic->rdev[i], buck_dvs->voltage[0], buck_dvs->voltage[0]);
if(selector < 0) {
dev_err(pmic->dev, "%s(): not found selector for voltage [%d]\n", __func__, buck_dvs->voltage[0]);
} else {
ret = bd7181x_reg_write(bd7181x, regh, val | (selector & BUCK1_H_MASK));
if(ret < 0)
return ret;
}
selector = regulator_map_voltage_iterate(pmic->rdev[i], buck_dvs->voltage[1], buck_dvs->voltage[1]);
if(selector < 0) {
dev_err(pmic->dev, "%s(): not found selector for voltage [%d]\n", __func__, buck_dvs->voltage[1]);
} else {
ret = bd7181x_reg_write(bd7181x, regl, val | (selector & BUCK1_L_MASK));
if(ret < 0)
return ret;
}
}
return 0;
}
/**@brief probe bd7181x regulator device
@param pdev bd7181x regulator platform device
@retval 0 success
@retval negative fail
*/
static __init int bd7181x_probe(struct platform_device *pdev)
{
struct bd7181x_pmic *pmic;
struct bd7181x_board *pdata;
struct regulator_config config = {};
struct bd7181x *bd7181x = dev_get_drvdata(pdev->dev.parent);
struct of_regulator_match *matches = NULL;
int i, err;
pmic = kzalloc(sizeof(*pmic), GFP_KERNEL);
if (!pmic) {
dev_err(&pdev->dev, "Memory allocation failed for pmic\n");
return -ENOMEM;
}
memcpy(pmic->descs, bd7181x_regulators, sizeof(pmic->descs));
pmic->dev = &pdev->dev;
pmic->mfd = bd7181x;
platform_set_drvdata(pdev, pmic);
bd7181x_clear_bits(pmic->mfd, BD7181X_REG_PWRCTRL, RESTARTEN); // Disable to go to ship-mode
bd7181x_clear_bits(pmic->mfd, BD7181X_REG_GPO, RESTARTEN); // Turn OFF the green LED
bd7181x_set_bits(pmic->mfd, BD7181X_REG_CHG_SET1, CHG_EN); // Enable charger
pdata = dev_get_platdata(bd7181x->dev);
if (!pdata && bd7181x->dev->of_node) {
bd7181x_parse_dt_reg_data(pdev, &matches);
if (matches == NULL) {
dev_err(&pdev->dev, "Platform data not found\n");
return -EINVAL;
}
}
/* Get buck dvs parameters */
of_bd7181x_buck_dvs(pdev, &pmic->buck_dvs[0]);
for (i = 0; i < BD7181X_REGULATOR_CNT; i++) {
struct regulator_init_data *init_data;
struct regulator_desc *desc;
struct regulator_dev *rdev;
desc = &pmic->descs[i].desc;
desc->name = bd7181x_matches[i].name;
if (pdata) {
init_data = pdata->init_data[i];
} else {
init_data = matches[i].init_data;
}
config.dev = pmic->dev;
config.init_data = init_data;
config.driver_data = pmic;
config.regmap = bd7181x->regmap;
config.of_node = matches[i].of_node;
rdev = regulator_register(desc, &config);
if (IS_ERR(rdev)) {
dev_err(bd7181x->dev,
"failed to register %s regulator\n",
desc->name);
err = PTR_ERR(rdev);
goto err;
}
pmic->rdev[i] = rdev;
}
err = sysfs_create_group(&pdev->dev.kobj, &gpo_attr_group);
if (err != 0) {
dev_err(&pdev->dev, "Failed to create attribute group: %d\n", err);
goto err;
}
/* Init buck12 dvs */
err = bd7181x_buck12_dvs_init(pmic);
if (err != 0) {
dev_err(&pdev->dev, "Failed to buck12 dvs: %d\n", err);
goto err;
}
return 0;
err:
while (--i >= 0)
regulator_unregister(pmic->rdev[i]);
kfree(pmic);
return err;
}
/**@brief remove bd7181x regulator device
@param pdev bd7181x regulator platform device
@return 0
*/
static int __exit bd7181x_remove(struct platform_device *pdev)
{
struct bd7181x_pmic *pmic = platform_get_drvdata(pdev);
int i;
sysfs_remove_group(&pdev->dev.kobj, &gpo_attr_group);
for (i = 0; i < BD7181X_REGULATOR_CNT; i++)
regulator_unregister(pmic->rdev[i]);
kfree(pmic);
return 0;
}
static struct platform_driver bd7181x_driver = {
.driver = {
.name = "bd7181x-pmic",
.owner = THIS_MODULE,
},
.probe = bd7181x_probe,
.remove = bd7181x_remove,
};
/**@brief module initialize function */
static int __init bd7181x_init(void)
{
return platform_driver_register(&bd7181x_driver);
}
subsys_initcall(bd7181x_init);
/**@brief module deinitialize function */
static void __exit bd7181x_cleanup(void)
{
platform_driver_unregister(&bd7181x_driver);
}
module_exit(bd7181x_cleanup);
MODULE_AUTHOR("Tony Luo <luofc@embedinfo.com>");
MODULE_DESCRIPTION("BD71815/BD71817 voltage regulator driver");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:bd7181x-pmic");

View File

@ -631,6 +631,15 @@ config RTC_DRV_S5M
This driver can also be built as a module. If so, the module This driver can also be built as a module. If so, the module
will be called rtc-s5m. will be called rtc-s5m.
config RTC_DRV_BD7181X
tristate "BD71815/BD71817 RTC"
depends on RTC_CLASS && MFD_BD7181X
help
If you say yes here you get support for the RTC on the
RoHM BD71815/BD71817 chips.
This driver can also be built as a module.
endif # I2C endif # I2C
comment "SPI RTC drivers" comment "SPI RTC drivers"

View File

@ -37,6 +37,7 @@ obj-$(CONFIG_RTC_DRV_AT32AP700X)+= rtc-at32ap700x.o
obj-$(CONFIG_RTC_DRV_AT91RM9200)+= rtc-at91rm9200.o obj-$(CONFIG_RTC_DRV_AT91RM9200)+= rtc-at91rm9200.o
obj-$(CONFIG_RTC_DRV_AT91SAM9) += rtc-at91sam9.o obj-$(CONFIG_RTC_DRV_AT91SAM9) += rtc-at91sam9.o
obj-$(CONFIG_RTC_DRV_AU1XXX) += rtc-au1xxx.o obj-$(CONFIG_RTC_DRV_AU1XXX) += rtc-au1xxx.o
obj-$(CONFIG_RTC_DRV_BD7181X) += rtc-bd7181x.o
obj-$(CONFIG_RTC_DRV_BFIN) += rtc-bfin.o obj-$(CONFIG_RTC_DRV_BFIN) += rtc-bfin.o
obj-$(CONFIG_RTC_DRV_BRCMSTB) += rtc-brcmstb-waketimer.o obj-$(CONFIG_RTC_DRV_BRCMSTB) += rtc-brcmstb-waketimer.o
obj-$(CONFIG_RTC_DRV_BQ32K) += rtc-bq32k.o obj-$(CONFIG_RTC_DRV_BQ32K) += rtc-bq32k.o

View File

@ -0,0 +1,417 @@
/*
* @file RoHM BD71815/BD71817 Real Time Clock interface
*
* Copyright (C) 2014 Embest Technology Co. Ltd. Inc.
*
* @author Peter Yang <yanglsh@embest-tech.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.
*/
#define DEBUG
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/rtc.h>
#include <linux/bcd.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/of.h>
#include <linux/mfd/bd7181x.h>
/** @brief bd7181x rtc struct */
struct bd7181x_rtc {
struct rtc_device *rtc; /**< system rtc device */
int irq; /**< rtc irq */
};
/* @brief Total number of RTC registers needed to set time*/
// #define NUM_TIME_REGS (BD7181X_REG_YEAR - BD7181X_REG_SEC + 1)
/**@brief enable or disable rtc alarm irq
* @param dev rtc device of system
* @param enabled enable if non-zero
* @retval 0 success
* @retval negative error number
*/
static int bd7181x_rtc_alarm_irq_enable(struct device *dev, unsigned enabled)
{
struct bd7181x *mfd = dev_get_drvdata(dev->parent);
u8 val = 0;
if (enabled)
val = ALM0;
return regmap_write(mfd->regmap, BD7181X_REG_INT_EN_12, val);
}
/**@brief bd7181x rtc time convert to linux time
* @param tm linux rtc time
* @param hw_rtc bd7181x rtc time
* @return argument tm
*/
static struct rtc_time* hw_to_rtc_time(struct rtc_time* tm, const struct bd7181x_rtc_alarm* hw_rtc) {
u8 hour;
tm->tm_sec = bcd2bin(hw_rtc->sec);
tm->tm_min = bcd2bin(hw_rtc->min);
hour = hw_rtc->hour & ~HOUR_24HOUR;
tm->tm_hour = bcd2bin(hour);
tm->tm_mday = bcd2bin(hw_rtc->day);
tm->tm_mon = bcd2bin(hw_rtc->month) - 1;
tm->tm_year = bcd2bin(hw_rtc->year) + 100;
return tm;
}
/**@brief linux time convert bd7181x rtc time
* @param hw_rtc bd7181x rtc time
* @param tm linux rtc time
* @return argument hw_rtc
*/
static struct bd7181x_rtc_alarm* rtc_time_to_hw(struct bd7181x_rtc_alarm* hw_rtc, const struct rtc_time* tm) {
hw_rtc->sec = bin2bcd(tm->tm_sec);
hw_rtc->min = bin2bcd(tm->tm_min);
hw_rtc->hour = HOUR_24HOUR | bin2bcd(tm->tm_hour);
hw_rtc->day = bin2bcd(tm->tm_mday);
hw_rtc->month = bin2bcd(tm->tm_mon + 1);
hw_rtc->year = bin2bcd(tm->tm_year - 100);
return hw_rtc;
}
/*
* Gets current bd7181x RTC time and date parameters.
*
* The RTC's time/alarm representation is not what gmtime(3) requires
* Linux to use:
*
* - Months are 1..12 vs Linux 0-11
* - Years are 0..99 vs Linux 1900..N (we assume 21st century)
*/
/**@brief read date/time from bd7181x rtc
* @param dev rtc device of system
* @param tm date/time store target
* @retval 0 success
* @retval negative error number
*/
static int bd7181x_rtc_read_time(struct device *dev, struct rtc_time *tm)
{
struct bd7181x_rtc_alarm rtc_data[1];
struct bd7181x *mfd = dev_get_drvdata(dev->parent);
int ret;
ret = regmap_bulk_read(mfd->regmap, BD7181X_REG_SEC, rtc_data, sizeof rtc_data);
if (ret < 0) {
dev_err(dev, "reading from RTC failed with err:%d\n", ret);
return ret;
}
hw_to_rtc_time(tm, rtc_data);
return ret;
}
/**@brief write date/time to bd7181x rtc
* @param dev rtc device of system
* @param tm date/time source
* @retval 0 success
* @retval negative error number
*/
static int bd7181x_rtc_set_time(struct device *dev, struct rtc_time *tm)
{
struct bd7181x_rtc_alarm rtc_data[1];
struct bd7181x *mfd = dev_get_drvdata(dev->parent);
int ret;
rtc_time_to_hw(rtc_data, tm);
/* update all the time registers in one shot */
ret = regmap_bulk_write(mfd->regmap, BD7181X_REG_SEC, rtc_data, sizeof rtc_data);
if (ret < 0) {
dev_err(dev, "rtc_set_time error %d\n", ret);
return ret;
}
return ret;
}
/**@brief Gets current bd7181x RTC alarm time.
* @param dev rtc device of system
* @param alm alarm date/time store target
* @retval 0 success
* @retval negative error number
*/
static int bd7181x_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm)
{
struct bd7181x_rtc_alarm rtc_data[1];
u32 int_val;
struct bd7181x *mfd = dev_get_drvdata(dev->parent);
int ret;
ret = regmap_bulk_read(mfd->regmap, BD7181X_REG_ALM0_SEC, rtc_data, sizeof rtc_data);
if (ret < 0) {
dev_err(dev, "rtc_read_alarm error %d\n", ret);
return ret;
}
hw_to_rtc_time(&alm->time, rtc_data);
ret = regmap_read(mfd->regmap, BD7181X_REG_INT_EN_12, &int_val);
if (ret < 0)
return ret;
if (int_val & ALM0)
alm->enabled = 1;
return ret;
}
/**@brief Set current bd7181x RTC alarm time
* @param dev rtc device of system
* @param alm alarm date/time to set
* @retval 0 success
* @retval negative error number
*/
static int bd7181x_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm)
{
struct bd7181x_rtc_alarm rtc_data[1];
struct bd7181x *mfd = dev_get_drvdata(dev->parent);
int ret;
// printk("%s() L%d\n", __func__, __LINE__);
ret = bd7181x_rtc_alarm_irq_enable(dev, 0);
if (ret)
return ret;
rtc_time_to_hw(rtc_data, &alm->time);
/* update all the alarm registers in one shot */
ret = regmap_bulk_write(mfd->regmap, BD7181X_REG_ALM0_SEC, rtc_data, sizeof rtc_data);
if (ret) {
dev_err(dev, "rtc_set_alarm error %d\n", ret);
return ret;
}
if (alm->enabled)
ret = bd7181x_rtc_alarm_irq_enable(dev, 1);
return ret;
}
/**@brief bd7181x rtc alarm interrupt
* @param irq system irq
* @param rtc rtc device of system
* @retval IRQ_HANDLED success
* @retval IRQ_NONE error
*/
static irqreturn_t bd7181x_rtc_interrupt(int irq, void *rtc)
{
struct device *dev = rtc;
unsigned long events = 0;
struct bd7181x *mfd = dev_get_drvdata(dev->parent);
struct bd7181x_rtc *bd_rtc = dev_get_drvdata(dev);
int ret;
u32 rtc_reg;
dev_info(mfd->dev, "bd7181x_rtc_interrupt() in.\n");
ret = regmap_read(mfd->regmap, BD7181X_REG_INT_STAT_12, &rtc_reg);
if (ret)
return IRQ_NONE;
dev_info(mfd->dev, "BD7181X_REG_INT_STAT_12=0x%x\n", rtc_reg);
if (rtc_reg & ALM0)
events = RTC_IRQF | RTC_AF;
ret = regmap_write(mfd->regmap, BD7181X_REG_INT_STAT_12, rtc_reg);
if (ret)
return IRQ_NONE;
dev_info(mfd->dev, "\n~~~IRQ ALARM.\n");
/* Notify RTC core on event */
rtc_update_irq(bd_rtc->rtc, 1, events);
return IRQ_HANDLED;
}
/** @brief function operations definition */
static struct rtc_class_ops bd7181x_rtc_ops = {
.read_time = bd7181x_rtc_read_time,
.set_time = bd7181x_rtc_set_time,
.read_alarm = bd7181x_rtc_read_alarm,
.set_alarm = bd7181x_rtc_set_alarm,
.alarm_irq_enable = bd7181x_rtc_alarm_irq_enable,
};
/**@brief probe bd7181x rtc device
@param pdev bd7181x rtc platform device
@retval 0 success
@retval negative fail
*/
static int bd7181x_rtc_probe(struct platform_device *pdev)
{
struct bd7181x *bd7181x = NULL;
struct bd7181x_rtc *bd_rtc = NULL;
int ret;
int irq;
u32 rtc_reg;
bd7181x = dev_get_drvdata(pdev->dev.parent);
bd_rtc = devm_kzalloc(&pdev->dev, sizeof(struct bd7181x_rtc),
GFP_KERNEL);
if (!bd_rtc)
return -ENOMEM;
/* Clear pending interrupts */
ret = regmap_read(bd7181x->regmap, BD7181X_REG_INT_STAT_12, &rtc_reg);
if (ret < 0)
return ret;
ret = regmap_write(bd7181x->regmap, BD7181X_REG_INT_STAT_12, rtc_reg);
if (ret < 0)
return ret;
dev_dbg(&pdev->dev, "Enabling rtc-bd7181x.\n");
#if 0
/* Enable RTC alarm interrupt */
ret = regmap_update_bits(bd7181x->regmap, BD7181X_REG_INT_EN_00, ALMALE, ALMALE);
if (ret < 0)
return ret;
#endif
/* Enable ALM0_EN mask */
ret = regmap_write(bd7181x->regmap, BD7181X_REG_ALM0_MASK, ALM0);
if (ret < 0)
return ret;
irq = platform_get_irq(pdev, 0);
if (irq <= 0) {
dev_warn(&pdev->dev, "Wake up is not possible as irq = %d\n", irq);
return -ENXIO;
}
ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
bd7181x_rtc_interrupt, IRQF_TRIGGER_LOW | IRQF_EARLY_RESUME,
dev_name(&pdev->dev), &pdev->dev);
if (ret < 0) {
dev_err(&pdev->dev, "IRQ is not free.\n");
return ret;
}
bd_rtc->irq = irq;
device_set_wakeup_capable(&pdev->dev, 1);
bd_rtc->rtc = devm_rtc_device_register(&pdev->dev, pdev->name,
&bd7181x_rtc_ops, THIS_MODULE);
if (IS_ERR(bd_rtc->rtc)) {
ret = PTR_ERR(bd_rtc->rtc);
dev_err(&pdev->dev, "RTC device register: err %d\n", ret);
return ret;
}
platform_set_drvdata(pdev, bd_rtc);
return 0;
}
/*
* Disable bd7181x RTC interrupts.
* Sets status flag to free.
*/
/**@brief remove bd7181x rtc device
@param pdev bd7181x rtc platform device
@return 0
*/
static int bd7181x_rtc_remove(struct platform_device *pdev)
{
bd7181x_rtc_alarm_irq_enable(&pdev->dev, 0);
return 0;
}
/**@brief shutdown bd7181x rtc device
@param pdev bd7181x rtc platform device
@return void
*/
static void bd7181x_rtc_shutdown(struct platform_device *pdev)
{
/* mask timer interrupts, but leave alarm interrupts on to enable
power-on when alarm is triggered */
bd7181x_rtc_alarm_irq_enable(&pdev->dev, 0);
}
#ifdef CONFIG_PM_SLEEP
/**@brief suspend bd7181x rtc device
* @param dev rtc device of system
* @retval 0
*/
static int bd7181x_rtc_suspend(struct device *dev)
{
struct bd7181x_rtc *bd_rtc = dev_get_drvdata(dev);
if (device_may_wakeup(dev))
enable_irq_wake(bd_rtc->irq);
return 0;
}
/**@brief resume bd7181x rtc device
* @param dev rtc device of system
* @retval 0
*/
static int bd7181x_rtc_resume(struct device *dev)
{
struct bd7181x_rtc *bd_rtc = dev_get_drvdata(dev);
if (device_may_wakeup(dev))
disable_irq_wake(bd_rtc->irq);
return 0;
}
#endif
static SIMPLE_DEV_PM_OPS(bd7181x_rtc_pm_ops, bd7181x_rtc_suspend, bd7181x_rtc_resume);
#ifdef CONFIG_OF
static const struct of_device_id bd7181x_rtc_of_match[] = {
{.compatible = "ti,bd7181x-rtc", },
{ },
};
MODULE_DEVICE_TABLE(of, bd7181x_rtc_of_match);
#endif
static struct platform_driver bd7181xrtc_driver = {
.probe = bd7181x_rtc_probe,
.remove = bd7181x_rtc_remove,
.shutdown = bd7181x_rtc_shutdown,
.driver = {
.owner = THIS_MODULE,
.name = "bd7181x-rtc",
.pm = &bd7181x_rtc_pm_ops,
.of_match_table = of_match_ptr(bd7181x_rtc_of_match),
},
};
/**@brief module initialize function */
static int __init bd7181x_rtc_init(void)
{
return platform_driver_register(&bd7181xrtc_driver);
}
module_init(bd7181x_rtc_init);
/**@brief module deinitialize function */
static void __exit bd7181x_rtc_exit(void)
{
platform_driver_unregister(&bd7181xrtc_driver);
}
module_exit(bd7181x_rtc_exit);
MODULE_AUTHOR("Peter Yang <yanglsh@embest-tech.com>");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:bd7181x-rtc");

View File

@ -0,0 +1,601 @@
/**
* @file bd7181x.h ROHM BD71815GW/BD71817GW header file
*
* Copyright 2014 Embest Technology Co. Ltd. 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.
*
* @author yanglsh@embest-tech.com
*/
#ifndef __LINUX_MFD_BD7181X_H
#define __LINUX_MFD_BD7181X_H
#include <linux/regmap.h>
// LDO5VSEL_EQ_H
// define to 1 when LDO5VSEL connect to High
// define to 0 when LDO5VSEL connect to Low
#define LDO5VSEL_EQ_H 1
#ifndef LDO5VSEL_EQ_H
#error define LDO5VSEL_EQ_H to 1 when connect to High, to 0 when connect to Low
#else
#if LDO5VSEL_EQ_H == 1
#define BD7181X_REG_LDO5_VOLT BD7181X_REG_LDO5_VOLT_H
#elif LDO5VSEL_EQ_H == 0
#define BD7181X_REG_LDO5_VOLT BD7181X_REG_LDO5_VOLT_L
#else
#error Define LDO5VSEL_EQ_H only to 0 or 1
#endif
#endif
enum {
BD7181X_BUCK1 = 0,
BD7181X_BUCK2,
BD7181X_BUCK3,
BD7181X_BUCK4,
BD7181X_BUCK5,
// General Purpose
BD7181X_LDO1,
BD7181X_LDO2,
BD7181X_LDO3,
// LDOs for SD Card and SD Card Interface
BD7181X_LDO4,
BD7181X_LDO5,
// LDO for DDR Reference Voltage
BD7181X_LDODVREF,
// LDO for Secure Non-Volatile Storage
// BD7181X_LDOSNVS,
// LDO for Low-Power State Retention
BD7181X_LDOLPSR,
BD7181X_WLED,
BD7181X_REGULATOR_CNT,
};
#define BD7181X_SUPPLY_STATE_ENABLED 0x1
enum {
BD7181X_REG_DEVICE = 0,
BD7181X_REG_PWRCTRL,
BD7181X_REG_BUCK1_MODE,
BD7181X_REG_BUCK2_MODE,
BD7181X_REG_BUCK3_MODE,
BD7181X_REG_BUCK4_MODE,
BD7181X_REG_BUCK5_MODE,
BD7181X_REG_BUCK1_VOLT_H,
// 0x08
BD7181X_REG_BUCK1_VOLT_L,
BD7181X_REG_BUCK2_VOLT_H,
BD7181X_REG_BUCK2_VOLT_L,
BD7181X_REG_BUCK3_VOLT,
BD7181X_REG_BUCK4_VOLT,
BD7181X_REG_BUCK5_VOLT,
BD7181X_REG_LED_CTRL,
BD7181X_REG_LED_DIMM,
// 0x10
BD7181X_REG_LDO_MODE1,
BD7181X_REG_LDO_MODE2,
BD7181X_REG_LDO_MODE3,
BD7181X_REG_LDO_MODE4,
BD7181X_REG_LDO1_VOLT,
BD7181X_REG_LDO2_VOLT,
BD7181X_REG_LDO3_VOLT,
BD7181X_REG_LDO4_VOLT,
// 0x18
BD7181X_REG_LDO5_VOLT_H,
BD7181X_REG_LDO5_VOLT_L,
BD7181X_REG_BUCK_PD_DIS,
BD7181X_REG_LDO_PD_DIS,
BD7181X_REG_GPO,
BD7181X_REG_OUT32K,
BD7181X_REG_SEC,
BD7181X_REG_MIN,
// 0x20
BD7181X_REG_HOUR,
BD7181X_REG_WEEK,
BD7181X_REG_DAY,
BD7181X_REG_MONTH,
BD7181X_REG_YEAR,
BD7181X_REG_ALM0_SEC,
// 0x2C
BD7181X_REG_ALM1_SEC = 0x2C,
// 0x33
BD7181X_REG_ALM0_MASK = 0x33,
BD7181X_REG_ALM1_MASK,
BD7181X_REG_ALM2,
BD7181X_REG_TRIM,
BD7181X_REG_CONF,
// 0x38
BD7181X_REG_SYS_INIT,
BD7181X_REG_CHG_STATE,
BD7181X_REG_CHG_LAST_STATE,
BD7181X_REG_BAT_STAT,
BD7181X_REG_DCIN_STAT,
BD7181X_REG_VSYS_STAT,
BD7181X_REG_CHG_STAT,
BD7181X_REG_CHG_WDT_STAT,
// 0x40
BD7181X_REG_BAT_TEMP,
BD7181X_REG_IGNORE_0,
BD7181X_REG_INHIBIT_0,
BD7181X_REG_DCIN_CLPS,
BD7181X_REG_VSYS_REG,
BD7181X_REG_VSYS_MAX,
BD7181X_REG_VSYS_MIN,
BD7181X_REG_CHG_SET1,
// 0x48
BD7181X_REG_CHG_SET2,
BD7181X_REG_CHG_WDT_PRE,
BD7181X_REG_CHG_WDT_FST,
BD7181X_REG_CHG_IPRE,
BD7181X_REG_CHG_IFST,
BD7181X_REG_CHG_IFST_TERM,
BD7181X_REG_CHG_VPRE,
BD7181X_REG_CHG_VBAT_1,
// 0x50
BD7181X_REG_CHG_VBAT_2,
BD7181X_REG_CHG_VBAT_3,
BD7181X_REG_CHG_LED_1,
BD7181X_REG_VF_TH,
BD7181X_REG_BAT_SET_1,
BD7181X_REG_BAT_SET_2,
BD7181X_REG_BAT_SET_3,
BD7181X_REG_ALM_VBAT_TH_U,
// 0x58
BD7181X_REG_ALM_VBAT_TH_L,
BD7181X_REG_ALM_DCIN_TH,
BD7181X_REG_ALM_VSYS_TH,
BD7181X_REG_VM_IBAT_U,
BD7181X_REG_VM_IBAT_L,
BD7181X_REG_VM_VBAT_U,
BD7181X_REG_VM_VBAT_L,
BD7181X_REG_VM_BTMP,
// 0x60
BD7181X_REG_VM_VTH,
BD7181X_REG_VM_DCIN_U,
BD7181X_REG_VM_DCIN_L,
BD7181X_REG_VM_VSYS,
BD7181X_REG_VM_VF,
BD7181X_REG_VM_OCI_PRE_U,
BD7181X_REG_VM_OCI_PRE_L,
BD7181X_REG_VM_OCV_PRE_U,
// 0x68
BD7181X_REG_VM_OCV_PRE_L,
BD7181X_REG_VM_OCI_PST_U,
BD7181X_REG_VM_OCI_PST_L,
BD7181X_REG_VM_OCV_PST_U,
BD7181X_REG_VM_OCV_PST_L,
BD7181X_REG_VM_SA_VBAT_U,
BD7181X_REG_VM_SA_VBAT_L,
BD7181X_REG_VM_SA_IBAT_U,
// 0x70
BD7181X_REG_VM_SA_IBAT_L,
BD7181X_REG_CC_CTRL,
BD7181X_REG_CC_BATCAP1_TH_U,
BD7181X_REG_CC_BATCAP1_TH_L,
BD7181X_REG_CC_BATCAP2_TH_U,
BD7181X_REG_CC_BATCAP2_TH_L,
BD7181X_REG_CC_BATCAP3_TH_U,
BD7181X_REG_CC_BATCAP3_TH_L,
// 0x78
BD7181X_REG_CC_STAT,
BD7181X_REG_CC_CCNTD_3,
BD7181X_REG_CC_CCNTD_2,
BD7181X_REG_CC_CCNTD_1,
BD7181X_REG_CC_CCNTD_0,
BD7181X_REG_CC_CURCD_U,
BD7181X_REG_CC_CURCD_L,
BD7181X_REG_VM_OCUR_THR_1,
// 0x80
BD7181X_REG_VM_OCUR_DUR_1,
BD7181X_REG_VM_OCUR_THR_2,
BD7181X_REG_VM_OCUR_DUR_2,
BD7181X_REG_VM_OCUR_THR_3,
BD7181X_REG_VM_OCUR_DUR_3,
BD7181X_REG_VM_OCUR_MON,
BD7181X_REG_VM_BTMP_OV_THR,
BD7181X_REG_VM_BTMP_OV_DUR,
// 0x88
BD7181X_REG_VM_BTMP_LO_THR,
BD7181X_REG_VM_BTMP_LO_DUR,
BD7181X_REG_VM_BTMP_MON,
BD7181X_REG_INT_EN_01,
// 0x95
BD7181X_REG_INT_EN_11 = 0x95,
// 0x96
BD7181X_REG_INT_EN_12 = 0x96,
BD7181X_REG_INT_STAT,
// 0x98
BD7181X_REG_INT_STAT_01,
BD7181X_REG_INT_STAT_02,
BD7181X_REG_INT_STAT_03,
BD7181X_REG_INT_STAT_04,
BD7181X_REG_INT_STAT_05,
BD7181X_REG_INT_STAT_06,
BD7181X_REG_INT_STAT_07,
BD7181X_REG_INT_STAT_08,
// 0xA0
BD7181X_REG_INT_STAT_09,
BD7181X_REG_INT_STAT_10,
BD7181X_REG_INT_STAT_11,
BD7181X_REG_INT_STAT_12,
BD7181X_REG_INT_UPDATE,
// 0xC0
BD7181X_REG_VM_VSYS_U = 0xC0,
BD7181X_REG_VM_VSYS_L,
BD7181X_REG_VM_SA_VSYS_U,
BD7181X_REG_VM_SA_VSYS_L,
// 0xD0
BD7181X_REG_VM_SA_IBAT_MIN_U = 0xD0,
BD7181X_REG_VM_SA_IBAT_MIN_L,
BD7181X_REG_VM_SA_IBAT_MAX_U,
BD7181X_REG_VM_SA_IBAT_MAX_L,
BD7181X_REG_VM_SA_VBAT_MIN_U,
BD7181X_REG_VM_SA_VBAT_MIN_L,
BD7181X_REG_VM_SA_VBAT_MAX_U,
BD7181X_REG_VM_SA_VBAT_MAX_L,
BD7181X_REG_VM_SA_VSYS_MIN_U,
BD7181X_REG_VM_SA_VSYS_MIN_L,
BD7181X_REG_VM_SA_VSYS_MAX_U,
BD7181X_REG_VM_SA_VSYS_MAX_L,
BD7181X_REG_VM_SA_MINMAX_CLR,
// 0xE0
BD7181X_REG_REX_CCNTD_3 = 0xE0,
BD7181X_REG_REX_CCNTD_2,
BD7181X_REG_REX_CCNTD_1,
BD7181X_REG_REX_CCNTD_0,
BD7181X_REG_REX_SA_VBAT_U,
BD7181X_REG_REX_SA_VBAT_L,
BD7181X_REG_REX_CTRL_1,
BD7181X_REG_REX_CTRL_2,
BD7181X_REG_FULL_CCNTD_3,
BD7181X_REG_FULL_CCNTD_2,
BD7181X_REG_FULL_CCNTD_1,
BD7181X_REG_FULL_CCNTD_0,
BD7181X_REG_FULL_CTRL,
// 0xF0
BD7181X_REG_CCNTD_CHG_3 = 0xF0,
BD7181X_REG_CCNTD_CHG_2,
// 0xFE
BD7181X_REG_TEST_MODE = 0xFE,
BD7181X_MAX_REGISTER,
};
/* BD7181X_REG_BUCK1_MODE bits */
#define BUCK1_RAMPRATE_MASK 0xC0
#define BUCK1_RAMPRATE_10P00MV 0x0
#define BUCK1_RAMPRATE_5P00MV 0x1
#define BUCK1_RAMPRATE_2P50MV 0x2
#define BUCK1_RAMPRATE_1P25MV 0x3
/* BD7181X_REG_BUCK1_VOLT_H bits */
#define BUCK1_DVSSEL 0x80
#define BUCK1_STBY_DVS 0x40
#define BUCK1_H_MASK 0x3F
#define BUCK1_H_DEFAULT 0x14
/* BD7181X_REG_BUCK1_VOLT_L bits */
#define BUCK1_L_MASK 0x3F
#define BUCK1_L_DEFAULT 0x14
/* BD7181X_REG_BUCK2_VOLT_H bits */
#define BUCK2_DVSSEL 0x80
#define BUCK2_STBY_DVS 0x40
#define BUCK2_H_MASK 0x3F
#define BUCK2_H_DEFAULT 0x14
/* BD7181X_REG_BUCK2_VOLT_L bits */
#define BUCK2_L_MASK 0x3F
#define BUCK2_L_DEFAULT 0x14
/* BD7181X_REG_LDO1_CTRL bits */
#define LDO1_EN 0x01
#define LDO2_EN 0x02
#define LDO3_EN 0x04
#define DVREF_EN 0x08
#define VOSNVS_SW_EN 0x10
#define VOLT_MASK 0x3F
/* BD7181X_REG_OUT32K bits */
#define OUT32K_EN 0x01
#define OUT32K_MODE 0x02
/* BD7181X_REG_BAT_STAT bits */
#define BAT_DET 0x20
#define BAT_DET_OFFSET 5
#define BAT_DET_DONE 0x10
#define VBAT_OV 0x08
#define DBAT_DET 0x01
/* BD7181X_REG_VBUS_STAT bits */
#define VBUS_DET 0x01
#define BUCK1_RAMPRATE_10MV_US 0x0
#define BUCK1_RAMPRATE_5MV_US 0x1
#define BUCK1_RAMPRATE_2P5MV_US 0x2
#define BUCK1_RAMPRATE_1P25MV_US 0x3a
/* BD7181X_REG_ALM0_MASK bits */
#define A0_ONESEC 0x80
/* BD7181X_REG_INT_EN_00 bits */
#define ALMALE 0x1
/* BD7181X_REG_INT_STAT_03 bits */
#define DCIN_MON_DET 0x02
#define DCIN_MON_RES 0x01
#define POWERON_LONG 0x04
#define POWERON_MID 0x08
#define POWERON_SHORT 0x10
#define POWERON_PRESS 0x20
/* BD71805_REG_INT_STAT_08 bits */
#define VBAT_MON_DET 0x02
#define VBAT_MON_RES 0x01
/* BD71805_REG_INT_STAT_11 bits */
#define INT_STAT_11_VF_DET 0x80
#define INT_STAT_11_VF_RES 0x40
#define INT_STAT_11_VF125_DET 0x20
#define INT_STAT_11_VF125_RES 0x10
#define INT_STAT_11_OVTMP_DET 0x08
#define INT_STAT_11_OVTMP_RES 0x04
#define INT_STAT_11_LOTMP_DET 0x02
#define INT_STAT_11_LOTMP_RES 0x01
#define VBAT_MON_DET 0x02
#define VBAT_MON_RES 0x01
/* BD7181X_REG_PWRCTRL bits */
#define RESTARTEN 0x01
/* BD7181X_REG_GPO bits */
#define READY_FORCE_LOW 0x04
/* BD7181X_REG_CHG_SET1 bits */
#define CHG_EN 0x01
/* BD7181X interrupt masks */
enum {
BD7181X_INT_EN_01_BUCKAST_MASK = 0x0F,
BD7181X_INT_EN_02_DCINAST_MASK = 0x3E,
BD7181X_INT_EN_03_DCINAST_MASK = 0x3F,
BD7181X_INT_EN_04_VSYSAST_MASK = 0xCF,
BD7181X_INT_EN_05_CHGAST_MASK = 0xFC,
BD7181X_INT_EN_06_BATAST_MASK = 0xF3,
BD7181X_INT_EN_07_BMONAST_MASK = 0xFE,
BD7181X_INT_EN_08_BMONAST_MASK = 0x03,
BD7181X_INT_EN_09_BMONAST_MASK = 0x07,
BD7181X_INT_EN_10_BMONAST_MASK = 0x3F,
BD7181X_INT_EN_11_TMPAST_MASK = 0xFF,
BD7181X_INT_EN_12_ALMAST_MASK = 0x07,
};
/* BD7181X interrupt irqs */
enum {
BD7181X_IRQ_BUCK_01 = 0x0,
BD7181X_IRQ_DCIN_02,
BD7181X_IRQ_DCIN_03,
BD7181X_IRQ_VSYS_04,
BD7181X_IRQ_CHARGE_05,
BD7181X_IRQ_BAT_06,
BD7181X_IRQ_BAT_MON_07,
BD7181X_IRQ_BAT_MON_08,
BD7181X_IRQ_BAT_MON_09,
BD7181X_IRQ_BAT_MON_10,
BD7181X_IRQ_TEMPERATURE_11,
BD7181X_IRQ_ALARM_12,
};
/* BD7181X_REG_INT_EN_12 bits */
#define ALM0 0x1
/* BD7181X_REG_HOUR bits */
#define HOUR_24HOUR 0x80
/* BD7181X_REG_CC_CTRL bits */
#define CCNTRST 0x80
#define CCNTENB 0x40
#define CCCALIB 0x20
/* BD7181X_REG_CHG_SET1 bits */
#define WDT_AUTO 0x40
/* BD7181X_REG_CC_CURCD */
#define CURDIR_Discharging 0x8000
/* BD7181X_REG_VM_SA_IBAT */
#define IBAT_SA_DIR_Discharging 0x8000
/* BD7181X_REG_VM_SA_MINMAX_CLR bits */
#define VSYS_SA_MIN_CLR 0x10
#define VBAT_SA_MIN_CLR 0x01
/* BD7181X_REG_REX_CTRL_1 bits */
#define REX_CLR 0x10
/* BD7181X_REG_REX_CTRL_1 bits */
#define REX_PMU_STATE_MASK 0x04
/* BD7181X_REG_FULL_CTRL bits */
#define FULL_CLR 0x10
/* BD7181X_REG_LED_CTRL bits */
#define CHGDONE_LED_EN 0x10
/** @brief charge state enumuration */
enum CHG_STATE {
CHG_STATE_SUSPEND = 0x0, /**< suspend state */
CHG_STATE_TRICKLE_CHARGE, /**< trickle charge state */
CHG_STATE_PRE_CHARGE, /**< precharge state */
CHG_STATE_FAST_CHARGE, /**< fast charge state */
CHG_STATE_TOP_OFF, /**< top off state */
CHG_STATE_DONE, /**< charge complete */
};
/** @brief rtc or alarm registers structure */
struct bd7181x_rtc_alarm {
u8 sec;
u8 min;
u8 hour;
u8 week;
u8 day;
u8 month;
u8 year;
};
struct bd7181x;
/**
* @brief Board platform data may be used to initialize regulators.
*/
struct bd7181x_board {
struct regulator_init_data *init_data[BD7181X_REGULATOR_CNT];
/**< regulator initialize data */
int gpio_intr; /**< gpio connected to bd7181x INTB */
int irq_base; /**< bd7181x sub irqs base # */
};
/**
* @brief bd7181x sub-driver chip access routines
*/
struct bd7181x {
struct device *dev;
struct i2c_client *i2c_client;
struct regmap *regmap;
struct mutex io_mutex;
unsigned int id;
/* IRQ Handling */
int chip_irq; /**< bd7181x irq to host cpu */
struct regmap_irq_chip_data *irq_data;
/* Client devices */
struct bd7181x_pmic *pmic; /**< client device regulator */
struct bd7181x_power *power; /**< client device battery */
struct bd7181x_board *of_plat_data;
/**< Device node parsed board data */
};
static inline int bd7181x_chip_id(struct bd7181x *bd7181x)
{
return bd7181x->id;
}
/**
* @brief bd7181x_reg_read
* read single register's value of bd7181x
* @param bd7181x device to read
* @param reg register address
* @return register value if success
* error number if fail
*/
static inline int bd7181x_reg_read(struct bd7181x *bd7181x, u8 reg)
{
int r, val;
r = regmap_read(bd7181x->regmap, reg, &val);
if (r < 0) {
return r;
}
return val;
}
/**
* @brief bd7181x_reg_write
* write single register of bd7181x
* @param bd7181x device to write
* @param reg register address
* @param val value to write
* @retval 0 if success
* @retval negative error number if fail
*/
static inline int bd7181x_reg_write(struct bd7181x *bd7181x, u8 reg,
unsigned int val)
{
return regmap_write(bd7181x->regmap, reg, val);
}
/**
* @brief bd7181x_set_bits
* set bits in one register of bd7181x
* @param bd7181x device to read
* @param reg register address
* @param mask mask bits
* @retval 0 if success
* @retval negative error number if fail
*/
static inline int bd7181x_set_bits(struct bd7181x *bd7181x, u8 reg,
u8 mask)
{
return regmap_update_bits(bd7181x->regmap, reg, mask, mask);
}
/**
* @brief bd7181x_clear_bits
* clear bits in one register of bd7181x
* @param bd7181x device to read
* @param reg register address
* @param mask mask bits
* @retval 0 if success
* @retval negative error number if fail
*/
static inline int bd7181x_clear_bits(struct bd7181x *bd7181x, u8 reg,
u8 mask)
{
return regmap_update_bits(bd7181x->regmap, reg, mask, 0);
}
/**
* @brief bd7181x_update_bits
* update bits in one register of bd7181x
* @param bd7181x device to read
* @param reg register address
* @param mask mask bits
* @param val value to update
* @retval 0 if success
* @retval negative error number if fail
*/
static inline int bd7181x_update_bits(struct bd7181x *bd7181x, u8 reg,
u8 mask, u8 val)
{
return regmap_update_bits(bd7181x->regmap, reg, mask, val);
}
/**
* @brief bd7181x platform data type
*/
struct bd7181x_gpo_plat_data {
u32 mode; ///< gpo output mode
int gpio_base; ///< base gpio number in system
};
#define BD7181X_DBG0 0x0001
#define BD7181X_DBG1 0x0002
#define BD7181X_DBG2 0x0004
#define BD7181X_DBG3 0x0008
extern unsigned int bd7181x_debug_mask;
#define bd7181x_debug(debug, fmt, arg...) do { if(debug & bd7181x_debug_mask) printk("BD7181x:" fmt, ##arg);} while(0)
#endif /* __LINUX_MFD_BD7181X_H */