diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 5a16a742a5ff..60888609217a 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -2018,6 +2018,14 @@ config MFD_PCA9450 if you say yes here you get support for the PCA9450 Power Management chips. +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. + menu "Multimedia Capabilities Port drivers" depends on ARCH_SA1100 diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 417beb3c7aed..8719fb8db633 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -262,3 +262,4 @@ obj-$(CONFIG_MFD_ROHM_BD718XX) += rohm-bd718x7.o obj-$(CONFIG_MFD_STMFX) += stmfx.o obj-$(CONFIG_MFD_PCA9450) += pca9450.o +obj-$(CONFIG_MFD_BD7181X) += bd7181x.o diff --git a/drivers/mfd/bd7181x.c b/drivers/mfd/bd7181x.c new file mode 100644 index 000000000000..afcba3c817a5 --- /dev/null +++ b/drivers/mfd/bd7181x.c @@ -0,0 +1,491 @@ +/* + * @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 + * Copyright 2014 Embest Technology Co. Ltd. Inc. + */ +#define DEBUG +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** @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 + +static void bd7181x_hw_init(struct bd7181x *bd7181x) +{ + /* Clear LPSR_MODE bit to get into SNVS state when PWRON goes low */ + bd7181x_clear_bits(bd7181x, BD7181X_REG_PWRCTRL, BIT(4)); + + /* Change the default to turn off LDO3 in SNVS state */ + bd7181x_clear_bits(bd7181x, BD7181X_REG_LDO_MODE2, BIT(7)); + + /* Turn off BUCK1 (VDD_ARM_1V1) in LPSR and SUSPEND mode */ + bd7181x_clear_bits(bd7181x, BD7181X_REG_BUCK1_MODE, BIT(1) | BIT(0)); + + /* Turn off BUCK2 (VDD_SOC_1V0) in LPSR mode */ + bd7181x_clear_bits(bd7181x, BD7181X_REG_BUCK2_MODE, BIT(1)); + + /* Keep BUCK3 (VDD_1V8) on in LPSR mode, as it supplies DDR VDD1 */ + bd7181x_set_bits(bd7181x, BD7181X_REG_BUCK3_MODE, BIT(1)); + + /* Keep BUCK4 (VDD_DRAM_1V2) on in LPSR mode for DDR retention */ + bd7181x_set_bits(bd7181x, BD7181X_REG_BUCK4_MODE, BIT(1)); + + /* Turn off BUCK5 (VDD_3V3) in LPSR mode */ + bd7181x_clear_bits(bd7181x, BD7181X_REG_BUCK5_MODE, BIT(1)); + + /* Turn off LDO1_3V3 (NVCC_GPIO2) in LPSR mode */ + bd7181x_clear_bits(bd7181x, BD7181X_REG_LDO_MODE1, BIT(5)); + + /* + * Keep LDO2_3V3 (NVCC_GPIO1) on in LPSR mode, as we have wakeup + * source from there. + */ + bd7181x_set_bits(bd7181x, BD7181X_REG_LDO_MODE2, BIT(1)); + + /* Turn off LDO3 (VDDA_USB_3P3) in SNVS, LPSR and SUSPEND mode */ + bd7181x_clear_bits(bd7181x, BD7181X_REG_LDO_MODE2, BIT(7) | BIT(5) | BIT(4)); + + /* Turn off LDO4_3V3 (EPD screen) in LPSR and SUSPEND mode */ + bd7181x_clear_bits(bd7181x, BD7181X_REG_LDO_MODE3, BIT(1) | BIT(0)); + + /* Turn off LDO5_1V8 (SD/MMC) in LPSR mode */ + bd7181x_clear_bits(bd7181x, BD7181X_REG_LDO_MODE3, BIT(5)); + + /* Keep LDO_DVREF (LPDDR_VREF_0V6) on in LPSR mode */ + bd7181x_set_bits(bd7181x, BD7181X_REG_LDO_MODE4, BIT(5)); + + /* + * Misc settings for a bit more power saving by disabling + * unneeded stuff. + * + * Set LDO4_REG_MODE bit to get LDO4 controlled by register than + * external pin (LDO4VEN). + */ + bd7181x_set_bits(bd7181x, BD7181X_REG_LDO_MODE1, BIT(3)); + + /* Enable OUT32K and have it in cmos mode */ + bd7181x_set_bits(bd7181x, BD7181X_REG_OUT32K, BIT(1) | BIT(0)); + + /* Disable all charger stuff */ + bd7181x_reg_write(bd7181x, BD7181X_REG_CHG_SET1, 0x0); + + /* Disable charger thermal shutdown and battery detection */ + bd7181x_clear_bits(bd7181x, BD7181X_REG_CHG_SET2, BIT(7) | BIT(4)); + + /* Disable LED lighting */ + bd7181x_reg_write(bd7181x, BD7181X_REG_CHG_LED_1, 0x17); + + /* Disable coulomb counter */ + bd7181x_clear_bits(bd7181x, BD7181X_REG_CC_CTRL, BIT(6)); + + /* Disable Relax State detection */ + bd7181x_clear_bits(bd7181x, BD7181X_REG_REX_CTRL_1, BIT(3)); +} + +/** @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; + + bd7181x_hw_init(bd7181x); + + 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 int bd7181x_suspend(struct device *dev) +{ + struct i2c_client *i2c = to_i2c_client(dev); + struct bd7181x *bd7181x = i2c_get_clientdata(i2c); + + /* Set LPSR_MODE bit to get into LPSR state when PWRON goes low */ + bd7181x_set_bits(bd7181x, BD7181X_REG_PWRCTRL, BIT(4)); + + pinctrl_pm_select_sleep_state(dev); + + return 0; +} + +static int bd7181x_resume(struct device *dev) +{ + struct i2c_client *i2c = to_i2c_client(dev); + struct bd7181x *bd7181x = i2c_get_clientdata(i2c); + + pinctrl_pm_select_default_state(dev); + + /* Flip LPSR_MODE setting back to SNVS state */ + bd7181x_clear_bits(bd7181x, BD7181X_REG_PWRCTRL, BIT(4)); + + return 0; +} + +SIMPLE_DEV_PM_OPS(bd7181x_pm_ops, bd7181x_suspend, bd7181x_resume); + +static struct i2c_driver bd7181x_i2c_driver = { + .driver = { + .name = "bd7181x", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(bd7181x_of_match), + .pm = &bd7181x_pm_ops, + }, + .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 "); +MODULE_AUTHOR("Peter Yang "); +MODULE_DESCRIPTION("BD71815/BD71817 chip multi-function driver"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/mfd/bd7181x.h b/include/linux/mfd/bd7181x.h new file mode 100644 index 000000000000..7afec81c4334 --- /dev/null +++ b/include/linux/mfd/bd7181x.h @@ -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 + +// 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 */