summaryrefslogtreecommitdiff
path: root/drivers/mfd
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mfd')
-rw-r--r--drivers/mfd/Kconfig44
-rw-r--r--drivers/mfd/Makefile4
-rw-r--r--drivers/mfd/ab8500-core.c17
-rw-r--r--drivers/mfd/arizona-core.c20
-rw-r--r--drivers/mfd/atc260x-core.c310
-rw-r--r--drivers/mfd/atc260x-i2c.c64
-rw-r--r--drivers/mfd/intel_pmt.c112
-rw-r--r--drivers/mfd/ntxec.c221
-rw-r--r--drivers/mfd/rohm-bd71828.c486
-rw-r--r--drivers/mfd/rohm-bd718x7.c43
10 files changed, 1095 insertions, 226 deletions
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index f0c9529e7bfd..6e31210915c7 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -967,6 +967,17 @@ config MFD_VIPERBOARD
You need to select the mfd cell drivers separately.
The drivers do not support all features the board exposes.
+config MFD_NTXEC
+ tristate "Netronix embedded controller (EC)"
+ depends on OF || COMPILE_TEST
+ depends on I2C
+ select REGMAP_I2C
+ select MFD_CORE
+ help
+ Say yes here if you want to support the embedded controller found in
+ certain e-book readers designed by the original design manufacturer
+ Netronix.
+
config MFD_RETU
tristate "Nokia Retu and Tahvo multi-function device"
select MFD_CORE
@@ -1975,19 +1986,20 @@ config MFD_ROHM_BD70528
charger.
config MFD_ROHM_BD71828
- tristate "ROHM BD71828 Power Management IC"
+ tristate "ROHM BD71828 and BD71815 Power Management IC"
depends on I2C=y
depends on OF
select REGMAP_I2C
select REGMAP_IRQ
select MFD_CORE
help
- Select this option to get support for the ROHM BD71828 Power
- Management IC. BD71828GW is a single-chip power management IC for
- battery-powered portable devices. The IC integrates 7 buck
- converters, 7 LDOs, and a 1500 mA single-cell linear charger.
- Also included is a Coulomb counter, a real-time clock (RTC), and
- a 32.768 kHz clock gate.
+ Select this option to get support for the ROHM BD71828 and BD71815
+ Power Management ICs. BD71828GW and BD71815AGW are single-chip power
+ management ICs mainly for battery-powered portable devices.
+ The BD71828 integrates 7 buck converters and 7 LDOs. The BD71815
+ has 5 bucks, 7 LDOs, and a boost for driving LEDs. Both ICs provide
+ also a single-cell linear charger, a Coulomb counter, a real-time
+ clock (RTC), GPIOs and a 32.768 kHz clock gate.
config MFD_ROHM_BD957XMUF
tristate "ROHM BD9576MUF and BD9573MUF Power Management ICs"
@@ -2066,6 +2078,24 @@ config MFD_WCD934X
This driver provides common support WCD934x audio codec and its
associated Pin Controller, Soundwire Controller and Audio codec.
+config MFD_ATC260X
+ tristate
+ select MFD_CORE
+ select REGMAP
+ select REGMAP_IRQ
+
+config MFD_ATC260X_I2C
+ tristate "Actions Semi ATC260x PMICs with I2C"
+ select MFD_ATC260X
+ select REGMAP_I2C
+ depends on I2C
+ help
+ Support for the Actions Semi ATC260x PMICs controlled via I2C.
+
+ This driver provides common support for accessing the ATC2603C
+ and ATC2609A chip variants, additional drivers must be enabled
+ in order to use the functionality of the device.
+
config MFD_KHADAS_MCU
tristate "Support for Khadas System control Microcontroller"
depends on I2C
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index d93f6f361fd3..279b80822147 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -218,6 +218,7 @@ obj-$(CONFIG_MFD_INTEL_PMC_BXT) += intel_pmc_bxt.o
obj-$(CONFIG_MFD_INTEL_PMT) += intel_pmt.o
obj-$(CONFIG_MFD_PALMAS) += palmas.o
obj-$(CONFIG_MFD_VIPERBOARD) += viperboard.o
+obj-$(CONFIG_MFD_NTXEC) += ntxec.o
obj-$(CONFIG_MFD_RC5T583) += rc5t583.o rc5t583-irq.o
obj-$(CONFIG_MFD_RK808) += rk808.o
obj-$(CONFIG_MFD_RN5T618) += rn5t618.o
@@ -269,3 +270,6 @@ obj-$(CONFIG_MFD_ACER_A500_EC) += acer-ec-a500.o
obj-$(CONFIG_SGI_MFD_IOC3) += ioc3.o
obj-$(CONFIG_MFD_SIMPLE_MFD_I2C) += simple-mfd-i2c.o
obj-$(CONFIG_MFD_INTEL_M10_BMC) += intel-m10-bmc.o
+
+obj-$(CONFIG_MFD_ATC260X) += atc260x-core.o
+obj-$(CONFIG_MFD_ATC260X_I2C) += atc260x-i2c.o
diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c
index a9037911162b..22c0e3d87629 100644
--- a/drivers/mfd/ab8500-core.c
+++ b/drivers/mfd/ab8500-core.c
@@ -19,7 +19,6 @@
#include <linux/mfd/core.h>
#include <linux/mfd/abx500.h>
#include <linux/mfd/abx500/ab8500.h>
-#include <linux/mfd/abx500/ab8500-bm.h>
#include <linux/mfd/dbx500-prcmu.h>
#include <linux/of.h>
#include <linux/of_device.h>
@@ -609,14 +608,14 @@ int ab8500_suspend(struct ab8500 *ab8500)
}
static const struct mfd_cell ab8500_bm_devs[] = {
- MFD_CELL_OF("ab8500-charger", NULL, &ab8500_bm_data,
- sizeof(ab8500_bm_data), 0, "stericsson,ab8500-charger"),
- MFD_CELL_OF("ab8500-btemp", NULL, &ab8500_bm_data,
- sizeof(ab8500_bm_data), 0, "stericsson,ab8500-btemp"),
- MFD_CELL_OF("ab8500-fg", NULL, &ab8500_bm_data,
- sizeof(ab8500_bm_data), 0, "stericsson,ab8500-fg"),
- MFD_CELL_OF("ab8500-chargalg", NULL, &ab8500_bm_data,
- sizeof(ab8500_bm_data), 0, "stericsson,ab8500-chargalg"),
+ MFD_CELL_OF("ab8500-charger", NULL, NULL, 0, 0,
+ "stericsson,ab8500-charger"),
+ MFD_CELL_OF("ab8500-btemp", NULL, NULL, 0, 0,
+ "stericsson,ab8500-btemp"),
+ MFD_CELL_OF("ab8500-fg", NULL, NULL, 0, 0,
+ "stericsson,ab8500-fg"),
+ MFD_CELL_OF("ab8500-chargalg", NULL, NULL, 0, 0,
+ "stericsson,ab8500-chargalg"),
};
static const struct mfd_cell ab8500_devs[] = {
diff --git a/drivers/mfd/arizona-core.c b/drivers/mfd/arizona-core.c
index 75f1bc671d59..ce6fe6de34f8 100644
--- a/drivers/mfd/arizona-core.c
+++ b/drivers/mfd/arizona-core.c
@@ -881,11 +881,6 @@ static const char * const wm5102_supplies[] = {
static const struct mfd_cell wm5102_devs[] = {
{ .name = "arizona-micsupp" },
{ .name = "arizona-gpio" },
- {
- .name = "arizona-extcon",
- .parent_supplies = wm5102_supplies,
- .num_parent_supplies = 1, /* We only need MICVDD */
- },
{ .name = "arizona-haptics" },
{ .name = "arizona-pwm" },
{
@@ -898,11 +893,6 @@ static const struct mfd_cell wm5102_devs[] = {
static const struct mfd_cell wm5110_devs[] = {
{ .name = "arizona-micsupp" },
{ .name = "arizona-gpio" },
- {
- .name = "arizona-extcon",
- .parent_supplies = wm5102_supplies,
- .num_parent_supplies = 1, /* We only need MICVDD */
- },
{ .name = "arizona-haptics" },
{ .name = "arizona-pwm" },
{
@@ -939,11 +929,6 @@ static const char * const wm8997_supplies[] = {
static const struct mfd_cell wm8997_devs[] = {
{ .name = "arizona-micsupp" },
{ .name = "arizona-gpio" },
- {
- .name = "arizona-extcon",
- .parent_supplies = wm8997_supplies,
- .num_parent_supplies = 1, /* We only need MICVDD */
- },
{ .name = "arizona-haptics" },
{ .name = "arizona-pwm" },
{
@@ -956,11 +941,6 @@ static const struct mfd_cell wm8997_devs[] = {
static const struct mfd_cell wm8998_devs[] = {
{ .name = "arizona-micsupp" },
{ .name = "arizona-gpio" },
- {
- .name = "arizona-extcon",
- .parent_supplies = wm5102_supplies,
- .num_parent_supplies = 1, /* We only need MICVDD */
- },
{ .name = "arizona-haptics" },
{ .name = "arizona-pwm" },
{
diff --git a/drivers/mfd/atc260x-core.c b/drivers/mfd/atc260x-core.c
new file mode 100644
index 000000000000..7148ff5b05b1
--- /dev/null
+++ b/drivers/mfd/atc260x-core.c
@@ -0,0 +1,310 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Core support for ATC260x PMICs
+ *
+ * Copyright (C) 2019 Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
+ * Copyright (C) 2020 Cristian Ciocaltea <cristian.ciocaltea@gmail.com>
+ */
+
+#include <linux/interrupt.h>
+#include <linux/mfd/atc260x/core.h>
+#include <linux/mfd/core.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/regmap.h>
+
+#define ATC260X_CHIP_REV_MAX 31
+
+struct atc260x_init_regs {
+ unsigned int cmu_devrst;
+ unsigned int cmu_devrst_ints;
+ unsigned int ints_msk;
+ unsigned int pad_en;
+ unsigned int pad_en_extirq;
+};
+
+static void regmap_lock_mutex(void *__mutex)
+{
+ struct mutex *mutex = __mutex;
+
+ /*
+ * Using regmap within an atomic context (e.g. accessing a PMIC when
+ * powering system down) is normally allowed only if the regmap type
+ * is MMIO and the regcache type is either REGCACHE_NONE or
+ * REGCACHE_FLAT. For slow buses like I2C and SPI, the regmap is
+ * internally protected by a mutex which is acquired non-atomically.
+ *
+ * Let's improve this by using a customized locking scheme inspired
+ * from I2C atomic transfer. See i2c_in_atomic_xfer_mode() for a
+ * starting point.
+ */
+ if (system_state > SYSTEM_RUNNING && irqs_disabled())
+ mutex_trylock(mutex);
+ else
+ mutex_lock(mutex);
+}
+
+static void regmap_unlock_mutex(void *__mutex)
+{
+ struct mutex *mutex = __mutex;
+
+ mutex_unlock(mutex);
+}
+
+static const struct regmap_config atc2603c_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 16,
+ .max_register = ATC2603C_SADDR,
+ .cache_type = REGCACHE_NONE,
+};
+
+static const struct regmap_config atc2609a_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 16,
+ .max_register = ATC2609A_SADDR,
+ .cache_type = REGCACHE_NONE,
+};
+
+static const struct regmap_irq atc2603c_regmap_irqs[] = {
+ REGMAP_IRQ_REG(ATC2603C_IRQ_AUDIO, 0, ATC2603C_INTS_MSK_AUDIO),
+ REGMAP_IRQ_REG(ATC2603C_IRQ_OV, 0, ATC2603C_INTS_MSK_OV),
+ REGMAP_IRQ_REG(ATC2603C_IRQ_OC, 0, ATC2603C_INTS_MSK_OC),
+ REGMAP_IRQ_REG(ATC2603C_IRQ_OT, 0, ATC2603C_INTS_MSK_OT),
+ REGMAP_IRQ_REG(ATC2603C_IRQ_UV, 0, ATC2603C_INTS_MSK_UV),
+ REGMAP_IRQ_REG(ATC2603C_IRQ_ALARM, 0, ATC2603C_INTS_MSK_ALARM),
+ REGMAP_IRQ_REG(ATC2603C_IRQ_ONOFF, 0, ATC2603C_INTS_MSK_ONOFF),
+ REGMAP_IRQ_REG(ATC2603C_IRQ_SGPIO, 0, ATC2603C_INTS_MSK_SGPIO),
+ REGMAP_IRQ_REG(ATC2603C_IRQ_IR, 0, ATC2603C_INTS_MSK_IR),
+ REGMAP_IRQ_REG(ATC2603C_IRQ_REMCON, 0, ATC2603C_INTS_MSK_REMCON),
+ REGMAP_IRQ_REG(ATC2603C_IRQ_POWER_IN, 0, ATC2603C_INTS_MSK_POWERIN),
+};
+
+static const struct regmap_irq atc2609a_regmap_irqs[] = {
+ REGMAP_IRQ_REG(ATC2609A_IRQ_AUDIO, 0, ATC2609A_INTS_MSK_AUDIO),
+ REGMAP_IRQ_REG(ATC2609A_IRQ_OV, 0, ATC2609A_INTS_MSK_OV),
+ REGMAP_IRQ_REG(ATC2609A_IRQ_OC, 0, ATC2609A_INTS_MSK_OC),
+ REGMAP_IRQ_REG(ATC2609A_IRQ_OT, 0, ATC2609A_INTS_MSK_OT),
+ REGMAP_IRQ_REG(ATC2609A_IRQ_UV, 0, ATC2609A_INTS_MSK_UV),
+ REGMAP_IRQ_REG(ATC2609A_IRQ_ALARM, 0, ATC2609A_INTS_MSK_ALARM),
+ REGMAP_IRQ_REG(ATC2609A_IRQ_ONOFF, 0, ATC2609A_INTS_MSK_ONOFF),
+ REGMAP_IRQ_REG(ATC2609A_IRQ_WKUP, 0, ATC2609A_INTS_MSK_WKUP),
+ REGMAP_IRQ_REG(ATC2609A_IRQ_IR, 0, ATC2609A_INTS_MSK_IR),
+ REGMAP_IRQ_REG(ATC2609A_IRQ_REMCON, 0, ATC2609A_INTS_MSK_REMCON),
+ REGMAP_IRQ_REG(ATC2609A_IRQ_POWER_IN, 0, ATC2609A_INTS_MSK_POWERIN),
+};
+
+static const struct regmap_irq_chip atc2603c_regmap_irq_chip = {
+ .name = "atc2603c",
+ .irqs = atc2603c_regmap_irqs,
+ .num_irqs = ARRAY_SIZE(atc2603c_regmap_irqs),
+ .num_regs = 1,
+ .status_base = ATC2603C_INTS_PD,
+ .mask_base = ATC2603C_INTS_MSK,
+ .mask_invert = true,
+};
+
+static const struct regmap_irq_chip atc2609a_regmap_irq_chip = {
+ .name = "atc2609a",
+ .irqs = atc2609a_regmap_irqs,
+ .num_irqs = ARRAY_SIZE(atc2609a_regmap_irqs),
+ .num_regs = 1,
+ .status_base = ATC2609A_INTS_PD,
+ .mask_base = ATC2609A_INTS_MSK,
+ .mask_invert = true,
+};
+
+static const struct resource atc2603c_onkey_resources[] = {
+ DEFINE_RES_IRQ(ATC2603C_IRQ_ONOFF),
+};
+
+static const struct resource atc2609a_onkey_resources[] = {
+ DEFINE_RES_IRQ(ATC2609A_IRQ_ONOFF),
+};
+
+static const struct mfd_cell atc2603c_mfd_cells[] = {
+ { .name = "atc260x-regulator" },
+ { .name = "atc260x-pwrc" },
+ {
+ .name = "atc260x-onkey",
+ .num_resources = ARRAY_SIZE(atc2603c_onkey_resources),
+ .resources = atc2603c_onkey_resources,
+ },
+};
+
+static const struct mfd_cell atc2609a_mfd_cells[] = {
+ { .name = "atc260x-regulator" },
+ { .name = "atc260x-pwrc" },
+ {
+ .name = "atc260x-onkey",
+ .num_resources = ARRAY_SIZE(atc2609a_onkey_resources),
+ .resources = atc2609a_onkey_resources,
+ },
+};
+
+static const struct atc260x_init_regs atc2603c_init_regs = {
+ .cmu_devrst = ATC2603C_CMU_DEVRST,
+ .cmu_devrst_ints = ATC2603C_CMU_DEVRST_INTS,
+ .ints_msk = ATC2603C_INTS_MSK,
+ .pad_en = ATC2603C_PAD_EN,
+ .pad_en_extirq = ATC2603C_PAD_EN_EXTIRQ,
+};
+
+static const struct atc260x_init_regs atc2609a_init_regs = {
+ .cmu_devrst = ATC2609A_CMU_DEVRST,
+ .cmu_devrst_ints = ATC2609A_CMU_DEVRST_INTS,
+ .ints_msk = ATC2609A_INTS_MSK,
+ .pad_en = ATC2609A_PAD_EN,
+ .pad_en_extirq = ATC2609A_PAD_EN_EXTIRQ,
+};
+
+static void atc260x_cmu_reset(struct atc260x *atc260x)
+{
+ const struct atc260x_init_regs *regs = atc260x->init_regs;
+
+ /* Assert reset */
+ regmap_update_bits(atc260x->regmap, regs->cmu_devrst,
+ regs->cmu_devrst_ints, ~regs->cmu_devrst_ints);
+
+ /* De-assert reset */
+ regmap_update_bits(atc260x->regmap, regs->cmu_devrst,
+ regs->cmu_devrst_ints, regs->cmu_devrst_ints);
+}
+
+static void atc260x_dev_init(struct atc260x *atc260x)
+{
+ const struct atc260x_init_regs *regs = atc260x->init_regs;
+
+ /* Initialize interrupt block */
+ atc260x_cmu_reset(atc260x);
+
+ /* Disable all interrupt sources */
+ regmap_write(atc260x->regmap, regs->ints_msk, 0);
+
+ /* Enable EXTIRQ pad */
+ regmap_update_bits(atc260x->regmap, regs->pad_en,
+ regs->pad_en_extirq, regs->pad_en_extirq);
+}
+
+/**
+ * atc260x_match_device(): Setup ATC260x variant related fields
+ *
+ * @atc260x: ATC260x device to setup (.dev field must be set)
+ * @regmap_cfg: regmap config associated with this ATC260x device
+ *
+ * This lets the ATC260x core configure the MFD cells and register maps
+ * for later use.
+ */
+int atc260x_match_device(struct atc260x *atc260x, struct regmap_config *regmap_cfg)
+{
+ struct device *dev = atc260x->dev;
+ const void *of_data;
+
+ of_data = of_device_get_match_data(dev);
+ if (!of_data)
+ return -ENODEV;
+
+ atc260x->ic_type = (unsigned long)of_data;
+
+ switch (atc260x->ic_type) {
+ case ATC2603C:
+ *regmap_cfg = atc2603c_regmap_config;
+ atc260x->regmap_irq_chip = &atc2603c_regmap_irq_chip;
+ atc260x->cells = atc2603c_mfd_cells;
+ atc260x->nr_cells = ARRAY_SIZE(atc2603c_mfd_cells);
+ atc260x->type_name = "atc2603c";
+ atc260x->rev_reg = ATC2603C_CHIP_VER;
+ atc260x->init_regs = &atc2603c_init_regs;
+ break;
+ case ATC2609A:
+ *regmap_cfg = atc2609a_regmap_config;
+ atc260x->regmap_irq_chip = &atc2609a_regmap_irq_chip;
+ atc260x->cells = atc2609a_mfd_cells;
+ atc260x->nr_cells = ARRAY_SIZE(atc2609a_mfd_cells);
+ atc260x->type_name = "atc2609a";
+ atc260x->rev_reg = ATC2609A_CHIP_VER;
+ atc260x->init_regs = &atc2609a_init_regs;
+ break;
+ default:
+ dev_err(dev, "Unsupported ATC260x device type: %u\n",
+ atc260x->ic_type);
+ return -EINVAL;
+ }
+
+ atc260x->regmap_mutex = devm_kzalloc(dev, sizeof(*atc260x->regmap_mutex),
+ GFP_KERNEL);
+ if (!atc260x->regmap_mutex)
+ return -ENOMEM;
+
+ mutex_init(atc260x->regmap_mutex);
+
+ regmap_cfg->lock = regmap_lock_mutex,
+ regmap_cfg->unlock = regmap_unlock_mutex,
+ regmap_cfg->lock_arg = atc260x->regmap_mutex;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(atc260x_match_device);
+
+/**
+ * atc260x_device_probe(): Probe a configured ATC260x device
+ *
+ * @atc260x: ATC260x device to probe (must be configured)
+ *
+ * This function lets the ATC260x core register the ATC260x MFD devices
+ * and IRQCHIP. The ATC260x device passed in must be fully configured
+ * with atc260x_match_device, its IRQ set, and regmap created.
+ */
+int atc260x_device_probe(struct atc260x *atc260x)
+{
+ struct device *dev = atc260x->dev;
+ unsigned int chip_rev;
+ int ret;
+
+ if (!atc260x->irq) {
+ dev_err(dev, "No interrupt support\n");
+ return -EINVAL;
+ }
+
+ /* Initialize the hardware */
+ atc260x_dev_init(atc260x);
+
+ ret = regmap_read(atc260x->regmap, atc260x->rev_reg, &chip_rev);
+ if (ret) {
+ dev_err(dev, "Failed to get chip revision\n");
+ return ret;
+ }
+
+ if (chip_rev > ATC260X_CHIP_REV_MAX) {
+ dev_err(dev, "Unknown chip revision: %u\n", chip_rev);
+ return -EINVAL;
+ }
+
+ atc260x->ic_ver = __ffs(chip_rev + 1U);
+
+ dev_info(dev, "Detected chip type %s rev.%c\n",
+ atc260x->type_name, 'A' + atc260x->ic_ver);
+
+ ret = devm_regmap_add_irq_chip(dev, atc260x->regmap, atc260x->irq, IRQF_ONESHOT,
+ -1, atc260x->regmap_irq_chip, &atc260x->irq_data);
+ if (ret) {
+ dev_err(dev, "Failed to add IRQ chip: %d\n", ret);
+ return ret;
+ }
+
+ ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE,
+ atc260x->cells, atc260x->nr_cells, NULL, 0,
+ regmap_irq_get_domain(atc260x->irq_data));
+ if (ret) {
+ dev_err(dev, "Failed to add child devices: %d\n", ret);
+ regmap_del_irq_chip(atc260x->irq, atc260x->irq_data);
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(atc260x_device_probe);
+
+MODULE_DESCRIPTION("ATC260x PMICs Core support");
+MODULE_AUTHOR("Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>");
+MODULE_AUTHOR("Cristian Ciocaltea <cristian.ciocaltea@gmail.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/atc260x-i2c.c b/drivers/mfd/atc260x-i2c.c
new file mode 100644
index 000000000000..362005703367
--- /dev/null
+++ b/drivers/mfd/atc260x-i2c.c
@@ -0,0 +1,64 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * I2C bus interface for ATC260x PMICs
+ *
+ * Copyright (C) 2019 Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
+ * Copyright (C) 2020 Cristian Ciocaltea <cristian.ciocaltea@gmail.com>
+ */
+
+#include <linux/i2c.h>
+#include <linux/mfd/atc260x/core.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+
+static int atc260x_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct atc260x *atc260x;
+ struct regmap_config regmap_cfg;
+ int ret;
+
+ atc260x = devm_kzalloc(&client->dev, sizeof(*atc260x), GFP_KERNEL);
+ if (!atc260x)
+ return -ENOMEM;
+
+ atc260x->dev = &client->dev;
+ atc260x->irq = client->irq;
+
+ ret = atc260x_match_device(atc260x, &regmap_cfg);
+ if (ret)
+ return ret;
+
+ i2c_set_clientdata(client, atc260x);
+
+ atc260x->regmap = devm_regmap_init_i2c(client, &regmap_cfg);
+ if (IS_ERR(atc260x->regmap)) {
+ ret = PTR_ERR(atc260x->regmap);
+ dev_err(&client->dev, "failed to init regmap: %d\n", ret);
+ return ret;
+ }
+
+ return atc260x_device_probe(atc260x);
+}
+
+const struct of_device_id atc260x_i2c_of_match[] = {
+ { .compatible = "actions,atc2603c", .data = (void *)ATC2603C },
+ { .compatible = "actions,atc2609a", .data = (void *)ATC2609A },
+ { }
+};
+MODULE_DEVICE_TABLE(of, atc260x_i2c_of_match);
+
+static struct i2c_driver atc260x_i2c_driver = {
+ .driver = {
+ .name = "atc260x",
+ .of_match_table = of_match_ptr(atc260x_i2c_of_match),
+ },
+ .probe = atc260x_i2c_probe,
+};
+module_i2c_driver(atc260x_i2c_driver);
+
+MODULE_DESCRIPTION("ATC260x PMICs I2C bus interface");
+MODULE_AUTHOR("Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>");
+MODULE_AUTHOR("Cristian Ciocaltea <cristian.ciocaltea@gmail.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/intel_pmt.c b/drivers/mfd/intel_pmt.c
index 744b230cdcca..dd7eb614c28e 100644
--- a/drivers/mfd/intel_pmt.c
+++ b/drivers/mfd/intel_pmt.c
@@ -49,10 +49,14 @@ enum pmt_quirks {
/* Use shift instead of mask to read discovery table offset */
PMT_QUIRK_TABLE_SHIFT = BIT(2),
+
+ /* DVSEC not present (provided in driver data) */
+ PMT_QUIRK_NO_DVSEC = BIT(3),
};
struct pmt_platform_info {
unsigned long quirks;
+ struct intel_dvsec_header **capabilities;
};
static const struct pmt_platform_info tgl_info = {
@@ -60,6 +64,26 @@ static const struct pmt_platform_info tgl_info = {
PMT_QUIRK_TABLE_SHIFT,
};
+/* DG1 Platform with DVSEC quirk*/
+static struct intel_dvsec_header dg1_telemetry = {
+ .length = 0x10,
+ .id = 2,
+ .num_entries = 1,
+ .entry_size = 3,
+ .tbir = 0,
+ .offset = 0x466000,
+};
+
+static struct intel_dvsec_header *dg1_capabilities[] = {
+ &dg1_telemetry,
+ NULL
+};
+
+static const struct pmt_platform_info dg1_info = {
+ .quirks = PMT_QUIRK_NO_DVSEC,
+ .capabilities = dg1_capabilities,
+};
+
static int pmt_add_dev(struct pci_dev *pdev, struct intel_dvsec_header *header,
unsigned long quirks)
{
@@ -79,19 +103,18 @@ static int pmt_add_dev(struct pci_dev *pdev, struct intel_dvsec_header *header,
case DVSEC_INTEL_ID_WATCHER:
if (quirks & PMT_QUIRK_NO_WATCHER) {
dev_info(dev, "Watcher not supported\n");
- return 0;
+ return -EINVAL;
}
name = "pmt_watcher";
break;
case DVSEC_INTEL_ID_CRASHLOG:
if (quirks & PMT_QUIRK_NO_CRASHLOG) {
dev_info(dev, "Crashlog not supported\n");
- return 0;
+ return -EINVAL;
}
name = "pmt_crashlog";
break;
default:
- dev_err(dev, "Unrecognized PMT capability: %d\n", id);
return -EINVAL;
}
@@ -148,41 +171,54 @@ static int pmt_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
if (info)
quirks = info->quirks;
- do {
- struct intel_dvsec_header header;
- u32 table;
- u16 vid;
-
- pos = pci_find_next_ext_capability(pdev, pos, PCI_EXT_CAP_ID_DVSEC);
- if (!pos)
- break;
-
- pci_read_config_word(pdev, pos + PCI_DVSEC_HEADER1, &vid);
- if (vid != PCI_VENDOR_ID_INTEL)
- continue;
-
- pci_read_config_word(pdev, pos + PCI_DVSEC_HEADER2,
- &header.id);
- pci_read_config_byte(pdev, pos + INTEL_DVSEC_ENTRIES,
- &header.num_entries);
- pci_read_config_byte(pdev, pos + INTEL_DVSEC_SIZE,
- &header.entry_size);
- pci_read_config_dword(pdev, pos + INTEL_DVSEC_TABLE,
- &table);
-
- header.tbir = INTEL_DVSEC_TABLE_BAR(table);
- header.offset = INTEL_DVSEC_TABLE_OFFSET(table);
-
- ret = pmt_add_dev(pdev, &header, quirks);
- if (ret) {
- dev_warn(&pdev->dev,
- "Failed to add device for DVSEC id %d\n",
- header.id);
- continue;
- }
+ if (info && (info->quirks & PMT_QUIRK_NO_DVSEC)) {
+ struct intel_dvsec_header **header;
+
+ header = info->capabilities;
+ while (*header) {
+ ret = pmt_add_dev(pdev, *header, quirks);
+ if (ret)
+ dev_warn(&pdev->dev,
+ "Failed to add device for DVSEC id %d\n",
+ (*header)->id);
+ else
+ found_devices = true;
- found_devices = true;
- } while (true);
+ ++header;
+ }
+ } else {
+ do {
+ struct intel_dvsec_header header;
+ u32 table;
+ u16 vid;
+
+ pos = pci_find_next_ext_capability(pdev, pos, PCI_EXT_CAP_ID_DVSEC);
+ if (!pos)
+ break;
+
+ pci_read_config_word(pdev, pos + PCI_DVSEC_HEADER1, &vid);
+ if (vid != PCI_VENDOR_ID_INTEL)
+ continue;
+
+ pci_read_config_word(pdev, pos + PCI_DVSEC_HEADER2,
+ &header.id);
+ pci_read_config_byte(pdev, pos + INTEL_DVSEC_ENTRIES,
+ &header.num_entries);
+ pci_read_config_byte(pdev, pos + INTEL_DVSEC_SIZE,
+ &header.entry_size);
+ pci_read_config_dword(pdev, pos + INTEL_DVSEC_TABLE,
+ &table);
+
+ header.tbir = INTEL_DVSEC_TABLE_BAR(table);
+ header.offset = INTEL_DVSEC_TABLE_OFFSET(table);
+
+ ret = pmt_add_dev(pdev, &header, quirks);
+ if (ret)
+ continue;
+
+ found_devices = true;
+ } while (true);
+ }
if (!found_devices)
return -ENODEV;
@@ -200,10 +236,12 @@ static void pmt_pci_remove(struct pci_dev *pdev)
}
#define PCI_DEVICE_ID_INTEL_PMT_ADL 0x467d
+#define PCI_DEVICE_ID_INTEL_PMT_DG1 0x490e
#define PCI_DEVICE_ID_INTEL_PMT_OOBMSM 0x09a7
#define PCI_DEVICE_ID_INTEL_PMT_TGL 0x9a0d
static const struct pci_device_id pmt_pci_ids[] = {
{ PCI_DEVICE_DATA(INTEL, PMT_ADL, &tgl_info) },
+ { PCI_DEVICE_DATA(INTEL, PMT_DG1, &dg1_info) },
{ PCI_DEVICE_DATA(INTEL, PMT_OOBMSM, NULL) },
{ PCI_DEVICE_DATA(INTEL, PMT_TGL, &tgl_info) },
{ }
diff --git a/drivers/mfd/ntxec.c b/drivers/mfd/ntxec.c
new file mode 100644
index 000000000000..957de2b03529
--- /dev/null
+++ b/drivers/mfd/ntxec.c
@@ -0,0 +1,221 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * The Netronix embedded controller is a microcontroller found in some
+ * e-book readers designed by the original design manufacturer Netronix, Inc.
+ * It contains RTC, battery monitoring, system power management, and PWM
+ * functionality.
+ *
+ * This driver implements register access, version detection, and system
+ * power-off/reset.
+ *
+ * Copyright 2020 Jonathan Neuschäfer <j.neuschaefer@gmx.net>
+ */
+
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/i2c.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/ntxec.h>
+#include <linux/module.h>
+#include <linux/pm.h>
+#include <linux/reboot.h>
+#include <linux/regmap.h>
+#include <linux/types.h>
+#include <asm/unaligned.h>
+
+#define NTXEC_REG_VERSION 0x00
+#define NTXEC_REG_POWEROFF 0x50
+#define NTXEC_REG_POWERKEEP 0x70
+#define NTXEC_REG_RESET 0x90
+
+#define NTXEC_POWEROFF_VALUE 0x0100
+#define NTXEC_POWERKEEP_VALUE 0x0800
+#define NTXEC_RESET_VALUE 0xff00
+
+static struct i2c_client *poweroff_restart_client;
+
+static void ntxec_poweroff(void)
+{
+ int res;
+ u8 buf[3] = { NTXEC_REG_POWEROFF };
+ struct i2c_msg msgs[] = {
+ {
+ .addr = poweroff_restart_client->addr,
+ .flags = 0,
+ .len = sizeof(buf),
+ .buf = buf,
+ },
+ };
+
+ put_unaligned_be16(NTXEC_POWEROFF_VALUE, buf + 1);
+
+ res = i2c_transfer(poweroff_restart_client->adapter, msgs, ARRAY_SIZE(msgs));
+ if (res < 0)
+ dev_warn(&poweroff_restart_client->dev,
+ "Failed to power off (err = %d)\n", res);
+
+ /*
+ * The time from the register write until the host CPU is powered off
+ * has been observed to be about 2.5 to 3 seconds. Sleep long enough to
+ * safely avoid returning from the poweroff handler.
+ */
+ msleep(5000);
+}
+
+static int ntxec_restart(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ int res;
+ u8 buf[3] = { NTXEC_REG_RESET };
+ /*
+ * NOTE: The lower half of the reset value is not sent, because sending
+ * it causes an I2C error. (The reset handler in the downstream driver
+ * does send the full two-byte value, but doesn't check the result).
+ */
+ struct i2c_msg msgs[] = {
+ {
+ .addr = poweroff_restart_client->addr,
+ .flags = 0,
+ .len = sizeof(buf) - 1,
+ .buf = buf,
+ },
+ };
+
+ put_unaligned_be16(NTXEC_RESET_VALUE, buf + 1);
+
+ res = i2c_transfer(poweroff_restart_client->adapter, msgs, ARRAY_SIZE(msgs));
+ if (res < 0)
+ dev_warn(&poweroff_restart_client->dev,
+ "Failed to restart (err = %d)\n", res);
+
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block ntxec_restart_handler = {
+ .notifier_call = ntxec_restart,
+ .priority = 128,
+};
+
+static const struct regmap_config regmap_config = {
+ .name = "ntxec",
+ .reg_bits = 8,
+ .val_bits = 16,
+ .cache_type = REGCACHE_NONE,
+ .val_format_endian = REGMAP_ENDIAN_BIG,
+};
+
+static const struct mfd_cell ntxec_subdevices[] = {
+ { .name = "ntxec-rtc" },
+ { .name = "ntxec-pwm" },
+};
+
+static int ntxec_probe(struct i2c_client *client)
+{
+ struct ntxec *ec;
+ unsigned int version;
+ int res;
+
+ ec = devm_kmalloc(&client->dev, sizeof(*ec), GFP_KERNEL);
+ if (!ec)
+ return -ENOMEM;
+
+ ec->dev = &client->dev;
+
+ ec->regmap = devm_regmap_init_i2c(client, &regmap_config);
+ if (IS_ERR(ec->regmap)) {
+ dev_err(ec->dev, "Failed to set up regmap for device\n");
+ return PTR_ERR(ec->regmap);
+ }
+
+ /* Determine the firmware version */
+ res = regmap_read(ec->regmap, NTXEC_REG_VERSION, &version);
+ if (res < 0) {
+ dev_err(ec->dev, "Failed to read firmware version number\n");
+ return res;
+ }
+
+ /* Bail out if we encounter an unknown firmware version */
+ switch (version) {
+ case NTXEC_VERSION_KOBO_AURA:
+ break;
+ default:
+ dev_err(ec->dev,
+ "Netronix embedded controller version %04x is not supported.\n",
+ version);
+ return -ENODEV;
+ }
+
+ dev_info(ec->dev,
+ "Netronix embedded controller version %04x detected.\n", version);
+
+ if (of_device_is_system_power_controller(ec->dev->of_node)) {
+ /*
+ * Set the 'powerkeep' bit. This is necessary on some boards
+ * in order to keep the system running.
+ */
+ res = regmap_write(ec->regmap, NTXEC_REG_POWERKEEP,
+ NTXEC_POWERKEEP_VALUE);
+ if (res < 0)
+ return res;
+
+ if (poweroff_restart_client)
+ /*
+ * Another instance of the driver already took
+ * poweroff/restart duties.
+ */
+ dev_err(ec->dev, "poweroff_restart_client already assigned\n");
+ else
+ poweroff_restart_client = client;
+
+ if (pm_power_off)
+ /* Another driver already registered a poweroff handler. */
+ dev_err(ec->dev, "pm_power_off already assigned\n");
+ else
+ pm_power_off = ntxec_poweroff;
+
+ res = register_restart_handler(&ntxec_restart_handler);
+ if (res)
+ dev_err(ec->dev,
+ "Failed to register restart handler: %d\n", res);
+ }
+
+ i2c_set_clientdata(client, ec);
+
+ res = devm_mfd_add_devices(ec->dev, PLATFORM_DEVID_NONE, ntxec_subdevices,
+ ARRAY_SIZE(ntxec_subdevices), NULL, 0, NULL);
+ if (res)
+ dev_err(ec->dev, "Failed to add subdevices: %d\n", res);
+
+ return res;
+}
+
+static int ntxec_remove(struct i2c_client *client)
+{
+ if (client == poweroff_restart_client) {
+ poweroff_restart_client = NULL;
+ pm_power_off = NULL;
+ unregister_restart_handler(&ntxec_restart_handler);
+ }
+
+ return 0;
+}
+
+static const struct of_device_id of_ntxec_match_table[] = {
+ { .compatible = "netronix,ntxec", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, of_ntxec_match_table);
+
+static struct i2c_driver ntxec_driver = {
+ .driver = {
+ .name = "ntxec",
+ .of_match_table = of_ntxec_match_table,
+ },
+ .probe_new = ntxec_probe,
+ .remove = ntxec_remove,
+};
+module_i2c_driver(ntxec_driver);
+
+MODULE_AUTHOR("Jonathan Neuschäfer <j.neuschaefer@gmx.net>");
+MODULE_DESCRIPTION("Core driver for Netronix EC");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/rohm-bd71828.c b/drivers/mfd/rohm-bd71828.c
index 210261d026f2..714d9fcbf07b 100644
--- a/drivers/mfd/rohm-bd71828.c
+++ b/drivers/mfd/rohm-bd71828.c
@@ -2,7 +2,7 @@
//
// Copyright (C) 2019 ROHM Semiconductors
//
-// ROHM BD71828 PMIC driver
+// ROHM BD71828/BD71815 PMIC driver
#include <linux/gpio_keys.h>
#include <linux/i2c.h>
@@ -11,7 +11,9 @@
#include <linux/ioport.h>
#include <linux/irq.h>
#include <linux/mfd/core.h>
+#include <linux/mfd/rohm-bd71815.h>
#include <linux/mfd/rohm-bd71828.h>
+#include <linux/mfd/rohm-generic.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/regmap.h>
@@ -29,12 +31,84 @@ static struct gpio_keys_platform_data bd71828_powerkey_data = {
.name = "bd71828-pwrkey",
};
-static const struct resource rtc_irqs[] = {
+static const struct resource bd71815_rtc_irqs[] = {
+ DEFINE_RES_IRQ_NAMED(BD71815_INT_RTC0, "bd71815-rtc-alm-0"),
+ DEFINE_RES_IRQ_NAMED(BD71815_INT_RTC1, "bd71815-rtc-alm-1"),
+ DEFINE_RES_IRQ_NAMED(BD71815_INT_RTC2, "bd71815-rtc-alm-2"),
+};
+
+static const struct resource bd71828_rtc_irqs[] = {
DEFINE_RES_IRQ_NAMED(BD71828_INT_RTC0, "bd71828-rtc-alm-0"),
DEFINE_RES_IRQ_NAMED(BD71828_INT_RTC1, "bd71828-rtc-alm-1"),
DEFINE_RES_IRQ_NAMED(BD71828_INT_RTC2, "bd71828-rtc-alm-2"),
};
+static struct resource bd71815_power_irqs[] = {
+ DEFINE_RES_IRQ_NAMED(BD71815_INT_DCIN_RMV, "bd71815-dcin-rmv"),
+ DEFINE_RES_IRQ_NAMED(BD71815_INT_CLPS_OUT, "bd71815-clps-out"),
+ DEFINE_RES_IRQ_NAMED(BD71815_INT_CLPS_IN, "bd71815-clps-in"),
+ DEFINE_RES_IRQ_NAMED(BD71815_INT_DCIN_OVP_RES, "bd71815-dcin-ovp-res"),
+ DEFINE_RES_IRQ_NAMED(BD71815_INT_DCIN_OVP_DET, "bd71815-dcin-ovp-det"),
+ DEFINE_RES_IRQ_NAMED(BD71815_INT_DCIN_MON_RES, "bd71815-dcin-mon-res"),
+ DEFINE_RES_IRQ_NAMED(BD71815_INT_DCIN_MON_DET, "bd71815-dcin-mon-det"),
+ DEFINE_RES_IRQ_NAMED(BD71815_INT_VSYS_UV_RES, "bd71815-vsys-uv-res"),
+ DEFINE_RES_IRQ_NAMED(BD71815_INT_VSYS_UV_DET, "bd71815-vsys-uv-det"),
+ DEFINE_RES_IRQ_NAMED(BD71815_INT_VSYS_LOW_RES, "bd71815-vsys-low-res"),
+ DEFINE_RES_IRQ_NAMED(BD71815_INT_VSYS_LOW_DET, "bd71815-vsys-low-det"),
+ DEFINE_RES_IRQ_NAMED(BD71815_INT_VSYS_MON_RES, "bd71815-vsys-mon-res"),
+ DEFINE_RES_IRQ_NAMED(BD71815_INT_VSYS_MON_RES, "bd71815-vsys-mon-det"),
+ DEFINE_RES_IRQ_NAMED(BD71815_INT_CHG_WDG_TEMP, "bd71815-chg-wdg-temp"),
+ DEFINE_RES_IRQ_NAMED(BD71815_INT_CHG_WDG_TIME, "bd71815-chg-wdg"),
+ DEFINE_RES_IRQ_NAMED(BD71815_INT_CHG_RECHARGE_RES, "bd71815-rechg-res"),
+ DEFINE_RES_IRQ_NAMED(BD71815_INT_CHG_RECHARGE_DET, "bd71815-rechg-det"),
+ DEFINE_RES_IRQ_NAMED(BD71815_INT_CHG_RANGED_TEMP_TRANSITION, "bd71815-ranged-temp-transit"),
+ DEFINE_RES_IRQ_NAMED(BD71815_INT_CHG_STATE_TRANSITION, "bd71815-chg-state-change"),
+ DEFINE_RES_IRQ_NAMED(BD71815_INT_BAT_TEMP_NORMAL, "bd71815-bat-temp-normal"),
+ DEFINE_RES_IRQ_NAMED(BD71815_INT_BAT_TEMP_ERANGE, "bd71815-bat-temp-erange"),
+ DEFINE_RES_IRQ_NAMED(BD71815_INT_BAT_REMOVED, "bd71815-bat-rmv"),
+ DEFINE_RES_IRQ_NAMED(BD71815_INT_BAT_DETECTED, "bd71815-bat-det"),
+ DEFINE_RES_IRQ_NAMED(BD71815_INT_THERM_REMOVED, "bd71815-therm-rmv"),
+ DEFINE_RES_IRQ_NAMED(BD71815_INT_THERM_DETECTED, "bd71815-therm-det"),
+ DEFINE_RES_IRQ_NAMED(BD71815_INT_BAT_DEAD, "bd71815-bat-dead"),
+ DEFINE_RES_IRQ_NAMED(BD71815_INT_BAT_SHORTC_RES, "bd71815-bat-short-res"),
+ DEFINE_RES_IRQ_NAMED(BD71815_INT_BAT_SHORTC_DET, "bd71815-bat-short-det"),
+ DEFINE_RES_IRQ_NAMED(BD71815_INT_BAT_LOW_VOLT_RES, "bd71815-bat-low-res"),
+ DEFINE_RES_IRQ_NAMED(BD71815_INT_BAT_LOW_VOLT_DET, "bd71815-bat-low-det"),
+ DEFINE_RES_IRQ_NAMED(BD71815_INT_BAT_OVER_VOLT_RES, "bd71815-bat-over-res"),
+ DEFINE_RES_IRQ_NAMED(BD71815_INT_BAT_OVER_VOLT_DET, "bd71815-bat-over-det"),
+ DEFINE_RES_IRQ_NAMED(BD71815_INT_BAT_MON_RES, "bd71815-bat-mon-res"),
+ DEFINE_RES_IRQ_NAMED(BD71815_INT_BAT_MON_DET, "bd71815-bat-mon-det"),
+ DEFINE_RES_IRQ_NAMED(BD71815_INT_BAT_CC_MON1, "bd71815-bat-cc-mon1"),
+ DEFINE_RES_IRQ_NAMED(BD71815_INT_BAT_CC_MON2, "bd71815-bat-cc-mon2"),
+ DEFINE_RES_IRQ_NAMED(BD71815_INT_BAT_CC_MON3, "bd71815-bat-cc-mon3"),
+ DEFINE_RES_IRQ_NAMED(BD71815_INT_BAT_OVER_CURR_1_RES, "bd71815-bat-oc1-res"),
+ DEFINE_RES_IRQ_NAMED(BD71815_INT_BAT_OVER_CURR_1_DET, "bd71815-bat-oc1-det"),
+ DEFINE_RES_IRQ_NAMED(BD71815_INT_BAT_OVER_CURR_2_RES, "bd71815-bat-oc2-res"),
+ DEFINE_RES_IRQ_NAMED(BD71815_INT_BAT_OVER_CURR_2_DET, "bd71815-bat-oc2-det"),
+ DEFINE_RES_IRQ_NAMED(BD71815_INT_BAT_OVER_CURR_3_RES, "bd71815-bat-oc3-res"),
+ DEFINE_RES_IRQ_NAMED(BD71815_INT_BAT_OVER_CURR_3_DET, "bd71815-bat-oc3-det"),
+ DEFINE_RES_IRQ_NAMED(BD71815_INT_TEMP_BAT_LOW_RES, "bd71815-bat-low-res"),
+ DEFINE_RES_IRQ_NAMED(BD71815_INT_TEMP_BAT_LOW_DET, "bd71815-bat-low-det"),
+ DEFINE_RES_IRQ_NAMED(BD71815_INT_TEMP_BAT_HI_RES, "bd71815-bat-hi-res"),
+ DEFINE_RES_IRQ_NAMED(BD71815_INT_TEMP_BAT_HI_DET, "bd71815-bat-hi-det"),
+};
+
+static struct mfd_cell bd71815_mfd_cells[] = {
+ { .name = "bd71815-pmic", },
+ { .name = "bd71815-clk", },
+ { .name = "bd71815-gpo", },
+ {
+ .name = "bd71815-power",
+ .num_resources = ARRAY_SIZE(bd71815_power_irqs),
+ .resources = &bd71815_power_irqs[0],
+ },
+ {
+ .name = "bd71815-rtc",
+ .num_resources = ARRAY_SIZE(bd71815_rtc_irqs),
+ .resources = &bd71815_rtc_irqs[0],
+ },
+};
+
static struct mfd_cell bd71828_mfd_cells[] = {
{ .name = "bd71828-pmic", },
{ .name = "bd71828-gpio", },
@@ -47,8 +121,8 @@ static struct mfd_cell bd71828_mfd_cells[] = {
{ .name = "bd71827-power", },
{
.name = "bd71828-rtc",
- .resources = rtc_irqs,
- .num_resources = ARRAY_SIZE(rtc_irqs),
+ .resources = bd71828_rtc_irqs,
+ .num_resources = ARRAY_SIZE(bd71828_rtc_irqs),
}, {
.name = "gpio-keys",
.platform_data = &bd71828_powerkey_data,
@@ -56,7 +130,35 @@ static struct mfd_cell bd71828_mfd_cells[] = {
},
};
-static const struct regmap_range volatile_ranges[] = {
+static const struct regmap_range bd71815_volatile_ranges[] = {
+ {
+ .range_min = BD71815_REG_SEC,
+ .range_max = BD71815_REG_YEAR,
+ }, {
+ .range_min = BD71815_REG_CONF,
+ .range_max = BD71815_REG_BAT_TEMP,
+ }, {
+ .range_min = BD71815_REG_VM_IBAT_U,
+ .range_max = BD71815_REG_CC_CTRL,
+ }, {
+ .range_min = BD71815_REG_CC_STAT,
+ .range_max = BD71815_REG_CC_CURCD_L,
+ }, {
+ .range_min = BD71815_REG_VM_BTMP_MON,
+ .range_max = BD71815_REG_VM_BTMP_MON,
+ }, {
+ .range_min = BD71815_REG_INT_STAT,
+ .range_max = BD71815_REG_INT_UPDATE,
+ }, {
+ .range_min = BD71815_REG_VM_VSYS_U,
+ .range_max = BD71815_REG_REX_CTRL_1,
+ }, {
+ .range_min = BD71815_REG_FULL_CCNTD_3,
+ .range_max = BD71815_REG_CCNTD_CHG_2,
+ },
+};
+
+static const struct regmap_range bd71828_volatile_ranges[] = {
{
.range_min = BD71828_REG_PS_CTRL_1,
.range_max = BD71828_REG_PS_CTRL_1,
@@ -80,15 +182,28 @@ static const struct regmap_range volatile_ranges[] = {
},
};
-static const struct regmap_access_table volatile_regs = {
- .yes_ranges = &volatile_ranges[0],
- .n_yes_ranges = ARRAY_SIZE(volatile_ranges),
+static const struct regmap_access_table bd71815_volatile_regs = {
+ .yes_ranges = &bd71815_volatile_ranges[0],
+ .n_yes_ranges = ARRAY_SIZE(bd71815_volatile_ranges),
+};
+
+static const struct regmap_access_table bd71828_volatile_regs = {
+ .yes_ranges = &bd71828_volatile_ranges[0],
+ .n_yes_ranges = ARRAY_SIZE(bd71828_volatile_ranges),
+};
+
+static const struct regmap_config bd71815_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .volatile_table = &bd71815_volatile_regs,
+ .max_register = BD71815_MAX_REGISTER - 1,
+ .cache_type = REGCACHE_RBTREE,
};
-static struct regmap_config bd71828_regmap = {
+static const struct regmap_config bd71828_regmap = {
.reg_bits = 8,
.val_bits = 8,
- .volatile_table = &volatile_regs,
+ .volatile_table = &bd71828_volatile_regs,
.max_register = BD71828_MAX_REGISTER,
.cache_type = REGCACHE_RBTREE,
};
@@ -96,7 +211,7 @@ static struct regmap_config bd71828_regmap = {
/*
* Mapping of main IRQ register bits to sub-IRQ register offsets so that we can
* access corect sub-IRQ registers based on bits that are set in main IRQ
- * register.
+ * register. BD71815 and BD71828 have same sub-register-block offests.
*/
static unsigned int bit0_offsets[] = {11}; /* RTC IRQ */
@@ -108,7 +223,7 @@ static unsigned int bit5_offsets[] = {3}; /* VSYS IRQ */
static unsigned int bit6_offsets[] = {1, 2}; /* DCIN IRQ */
static unsigned int bit7_offsets[] = {0}; /* BUCK IRQ */
-static struct regmap_irq_sub_irq_map bd71828_sub_irq_offsets[] = {
+static struct regmap_irq_sub_irq_map bd718xx_sub_irq_offsets[] = {
REGMAP_IRQ_MAIN_REG_OFFSET(bit0_offsets),
REGMAP_IRQ_MAIN_REG_OFFSET(bit1_offsets),
REGMAP_IRQ_MAIN_REG_OFFSET(bit2_offsets),
@@ -119,6 +234,88 @@ static struct regmap_irq_sub_irq_map bd71828_sub_irq_offsets[] = {
REGMAP_IRQ_MAIN_REG_OFFSET(bit7_offsets),
};
+static const struct regmap_irq bd71815_irqs[] = {
+ REGMAP_IRQ_REG(BD71815_INT_BUCK1_OCP, 0, BD71815_INT_BUCK1_OCP_MASK),
+ REGMAP_IRQ_REG(BD71815_INT_BUCK2_OCP, 0, BD71815_INT_BUCK2_OCP_MASK),
+ REGMAP_IRQ_REG(BD71815_INT_BUCK3_OCP, 0, BD71815_INT_BUCK3_OCP_MASK),
+ REGMAP_IRQ_REG(BD71815_INT_BUCK4_OCP, 0, BD71815_INT_BUCK4_OCP_MASK),
+ REGMAP_IRQ_REG(BD71815_INT_BUCK5_OCP, 0, BD71815_INT_BUCK5_OCP_MASK),
+ REGMAP_IRQ_REG(BD71815_INT_LED_OVP, 0, BD71815_INT_LED_OVP_MASK),
+ REGMAP_IRQ_REG(BD71815_INT_LED_OCP, 0, BD71815_INT_LED_OCP_MASK),
+ REGMAP_IRQ_REG(BD71815_INT_LED_SCP, 0, BD71815_INT_LED_SCP_MASK),
+ /* DCIN1 interrupts */
+ REGMAP_IRQ_REG(BD71815_INT_DCIN_RMV, 1, BD71815_INT_DCIN_RMV_MASK),
+ REGMAP_IRQ_REG(BD71815_INT_CLPS_OUT, 1, BD71815_INT_CLPS_OUT_MASK),
+ REGMAP_IRQ_REG(BD71815_INT_CLPS_IN, 1, BD71815_INT_CLPS_IN_MASK),
+ REGMAP_IRQ_REG(BD71815_INT_DCIN_OVP_RES, 1, BD71815_INT_DCIN_OVP_RES_MASK),
+ REGMAP_IRQ_REG(BD71815_INT_DCIN_OVP_DET, 1, BD71815_INT_DCIN_OVP_DET_MASK),
+ /* DCIN2 interrupts */
+ REGMAP_IRQ_REG(BD71815_INT_DCIN_MON_RES, 2, BD71815_INT_DCIN_MON_RES_MASK),
+ REGMAP_IRQ_REG(BD71815_INT_DCIN_MON_DET, 2, BD71815_INT_DCIN_MON_DET_MASK),
+ REGMAP_IRQ_REG(BD71815_INT_WDOG, 2, BD71815_INT_WDOG_MASK),
+ /* Vsys */
+ REGMAP_IRQ_REG(BD71815_INT_VSYS_UV_RES, 3, BD71815_INT_VSYS_UV_RES_MASK),
+ REGMAP_IRQ_REG(BD71815_INT_VSYS_UV_DET, 3, BD71815_INT_VSYS_UV_DET_MASK),
+ REGMAP_IRQ_REG(BD71815_INT_VSYS_LOW_RES, 3, BD71815_INT_VSYS_LOW_RES_MASK),
+ REGMAP_IRQ_REG(BD71815_INT_VSYS_LOW_DET, 3, BD71815_INT_VSYS_LOW_DET_MASK),
+ REGMAP_IRQ_REG(BD71815_INT_VSYS_MON_RES, 3, BD71815_INT_VSYS_MON_RES_MASK),
+ REGMAP_IRQ_REG(BD71815_INT_VSYS_MON_DET, 3, BD71815_INT_VSYS_MON_DET_MASK),
+ /* Charger */
+ REGMAP_IRQ_REG(BD71815_INT_CHG_WDG_TEMP, 4, BD71815_INT_CHG_WDG_TEMP_MASK),
+ REGMAP_IRQ_REG(BD71815_INT_CHG_WDG_TIME, 4, BD71815_INT_CHG_WDG_TIME_MASK),
+ REGMAP_IRQ_REG(BD71815_INT_CHG_RECHARGE_RES, 4, BD71815_INT_CHG_RECHARGE_RES_MASK),
+ REGMAP_IRQ_REG(BD71815_INT_CHG_RECHARGE_DET, 4, BD71815_INT_CHG_RECHARGE_DET_MASK),
+ REGMAP_IRQ_REG(BD71815_INT_CHG_RANGED_TEMP_TRANSITION, 4,
+ BD71815_INT_CHG_RANGED_TEMP_TRANSITION_MASK),
+ REGMAP_IRQ_REG(BD71815_INT_CHG_STATE_TRANSITION, 4, BD71815_INT_CHG_STATE_TRANSITION_MASK),
+ /* Battery */
+ REGMAP_IRQ_REG(BD71815_INT_BAT_TEMP_NORMAL, 5, BD71815_INT_BAT_TEMP_NORMAL_MASK),
+ REGMAP_IRQ_REG(BD71815_INT_BAT_TEMP_ERANGE, 5, BD71815_INT_BAT_TEMP_ERANGE_MASK),
+ REGMAP_IRQ_REG(BD71815_INT_BAT_REMOVED, 5, BD71815_INT_BAT_REMOVED_MASK),
+ REGMAP_IRQ_REG(BD71815_INT_BAT_DETECTED, 5, BD71815_INT_BAT_DETECTED_MASK),
+ REGMAP_IRQ_REG(BD71815_INT_THERM_REMOVED, 5, BD71815_INT_THERM_REMOVED_MASK),
+ REGMAP_IRQ_REG(BD71815_INT_THERM_DETECTED, 5, BD71815_INT_THERM_DETECTED_MASK),
+ /* Battery Mon 1 */
+ REGMAP_IRQ_REG(BD71815_INT_BAT_DEAD, 6, BD71815_INT_BAT_DEAD_MASK),
+ REGMAP_IRQ_REG(BD71815_INT_BAT_SHORTC_RES, 6, BD71815_INT_BAT_SHORTC_RES_MASK),
+ REGMAP_IRQ_REG(BD71815_INT_BAT_SHORTC_DET, 6, BD71815_INT_BAT_SHORTC_DET_MASK),
+ REGMAP_IRQ_REG(BD71815_INT_BAT_LOW_VOLT_RES, 6, BD71815_INT_BAT_LOW_VOLT_RES_MASK),
+ REGMAP_IRQ_REG(BD71815_INT_BAT_LOW_VOLT_DET, 6, BD71815_INT_BAT_LOW_VOLT_DET_MASK),
+ REGMAP_IRQ_REG(BD71815_INT_BAT_OVER_VOLT_RES, 6, BD71815_INT_BAT_OVER_VOLT_RES_MASK),
+ REGMAP_IRQ_REG(BD71815_INT_BAT_OVER_VOLT_DET, 6, BD71815_INT_BAT_OVER_VOLT_DET_MASK),
+ /* Battery Mon 2 */
+ REGMAP_IRQ_REG(BD71815_INT_BAT_MON_RES, 7, BD71815_INT_BAT_MON_RES_MASK),
+ REGMAP_IRQ_REG(BD71815_INT_BAT_MON_DET, 7, BD71815_INT_BAT_MON_DET_MASK),
+ /* Battery Mon 3 (Coulomb counter) */
+ REGMAP_IRQ_REG(BD71815_INT_BAT_CC_MON1, 8, BD71815_INT_BAT_CC_MON1_MASK),
+ REGMAP_IRQ_REG(BD71815_INT_BAT_CC_MON2, 8, BD71815_INT_BAT_CC_MON2_MASK),
+ REGMAP_IRQ_REG(BD71815_INT_BAT_CC_MON3, 8, BD71815_INT_BAT_CC_MON3_MASK),
+ /* Battery Mon 4 */
+ REGMAP_IRQ_REG(BD71815_INT_BAT_OVER_CURR_1_RES, 9, BD71815_INT_BAT_OVER_CURR_1_RES_MASK),
+ REGMAP_IRQ_REG(BD71815_INT_BAT_OVER_CURR_1_DET, 9, BD71815_INT_BAT_OVER_CURR_1_DET_MASK),
+ REGMAP_IRQ_REG(BD71815_INT_BAT_OVER_CURR_2_RES, 9, BD71815_INT_BAT_OVER_CURR_2_RES_MASK),
+ REGMAP_IRQ_REG(BD71815_INT_BAT_OVER_CURR_2_DET, 9, BD71815_INT_BAT_OVER_CURR_2_DET_MASK),
+ REGMAP_IRQ_REG(BD71815_INT_BAT_OVER_CURR_3_RES, 9, BD71815_INT_BAT_OVER_CURR_3_RES_MASK),
+ REGMAP_IRQ_REG(BD71815_INT_BAT_OVER_CURR_3_DET, 9, BD71815_INT_BAT_OVER_CURR_3_DET_MASK),
+ /* Temperature */
+ REGMAP_IRQ_REG(BD71815_INT_TEMP_BAT_LOW_RES, 10, BD71815_INT_TEMP_BAT_LOW_RES_MASK),
+ REGMAP_IRQ_REG(BD71815_INT_TEMP_BAT_LOW_DET, 10, BD71815_INT_TEMP_BAT_LOW_DET_MASK),
+ REGMAP_IRQ_REG(BD71815_INT_TEMP_BAT_HI_RES, 10, BD71815_INT_TEMP_BAT_HI_RES_MASK),
+ REGMAP_IRQ_REG(BD71815_INT_TEMP_BAT_HI_DET, 10, BD71815_INT_TEMP_BAT_HI_DET_MASK),
+ REGMAP_IRQ_REG(BD71815_INT_TEMP_CHIP_OVER_125_RES, 10,
+ BD71815_INT_TEMP_CHIP_OVER_125_RES_MASK),
+ REGMAP_IRQ_REG(BD71815_INT_TEMP_CHIP_OVER_125_DET, 10,
+ BD71815_INT_TEMP_CHIP_OVER_125_DET_MASK),
+ REGMAP_IRQ_REG(BD71815_INT_TEMP_CHIP_OVER_VF_RES, 10,
+ BD71815_INT_TEMP_CHIP_OVER_VF_RES_MASK),
+ REGMAP_IRQ_REG(BD71815_INT_TEMP_CHIP_OVER_VF_DET, 10,
+ BD71815_INT_TEMP_CHIP_OVER_VF_DET_MASK),
+ /* RTC Alarm */
+ REGMAP_IRQ_REG(BD71815_INT_RTC0, 11, BD71815_INT_RTC0_MASK),
+ REGMAP_IRQ_REG(BD71815_INT_RTC1, 11, BD71815_INT_RTC1_MASK),
+ REGMAP_IRQ_REG(BD71815_INT_RTC2, 11, BD71815_INT_RTC2_MASK),
+};
+
static struct regmap_irq bd71828_irqs[] = {
REGMAP_IRQ_REG(BD71828_INT_BUCK1_OCP, 0, BD71828_INT_BUCK1_OCP_MASK),
REGMAP_IRQ_REG(BD71828_INT_BUCK2_OCP, 0, BD71828_INT_BUCK2_OCP_MASK),
@@ -134,10 +331,8 @@ static struct regmap_irq bd71828_irqs[] = {
REGMAP_IRQ_REG(BD71828_INT_CLPS_OUT, 1, BD71828_INT_CLPS_OUT_MASK),
REGMAP_IRQ_REG(BD71828_INT_CLPS_IN, 1, BD71828_INT_CLPS_IN_MASK),
/* DCIN2 interrupts */
- REGMAP_IRQ_REG(BD71828_INT_DCIN_MON_RES, 2,
- BD71828_INT_DCIN_MON_RES_MASK),
- REGMAP_IRQ_REG(BD71828_INT_DCIN_MON_DET, 2,
- BD71828_INT_DCIN_MON_DET_MASK),
+ REGMAP_IRQ_REG(BD71828_INT_DCIN_MON_RES, 2, BD71828_INT_DCIN_MON_RES_MASK),
+ REGMAP_IRQ_REG(BD71828_INT_DCIN_MON_DET, 2, BD71828_INT_DCIN_MON_DET_MASK),
REGMAP_IRQ_REG(BD71828_INT_LONGPUSH, 2, BD71828_INT_LONGPUSH_MASK),
REGMAP_IRQ_REG(BD71828_INT_MIDPUSH, 2, BD71828_INT_MIDPUSH_MASK),
REGMAP_IRQ_REG(BD71828_INT_SHORTPUSH, 2, BD71828_INT_SHORTPUSH_MASK),
@@ -145,102 +340,59 @@ static struct regmap_irq bd71828_irqs[] = {
REGMAP_IRQ_REG(BD71828_INT_WDOG, 2, BD71828_INT_WDOG_MASK),
REGMAP_IRQ_REG(BD71828_INT_SWRESET, 2, BD71828_INT_SWRESET_MASK),
/* Vsys */
- REGMAP_IRQ_REG(BD71828_INT_VSYS_UV_RES, 3,
- BD71828_INT_VSYS_UV_RES_MASK),
- REGMAP_IRQ_REG(BD71828_INT_VSYS_UV_DET, 3,
- BD71828_INT_VSYS_UV_DET_MASK),
- REGMAP_IRQ_REG(BD71828_INT_VSYS_LOW_RES, 3,
- BD71828_INT_VSYS_LOW_RES_MASK),
- REGMAP_IRQ_REG(BD71828_INT_VSYS_LOW_DET, 3,
- BD71828_INT_VSYS_LOW_DET_MASK),
- REGMAP_IRQ_REG(BD71828_INT_VSYS_HALL_IN, 3,
- BD71828_INT_VSYS_HALL_IN_MASK),
- REGMAP_IRQ_REG(BD71828_INT_VSYS_HALL_TOGGLE, 3,
- BD71828_INT_VSYS_HALL_TOGGLE_MASK),
- REGMAP_IRQ_REG(BD71828_INT_VSYS_MON_RES, 3,
- BD71828_INT_VSYS_MON_RES_MASK),
- REGMAP_IRQ_REG(BD71828_INT_VSYS_MON_DET, 3,
- BD71828_INT_VSYS_MON_DET_MASK),
+ REGMAP_IRQ_REG(BD71828_INT_VSYS_UV_RES, 3, BD71828_INT_VSYS_UV_RES_MASK),
+ REGMAP_IRQ_REG(BD71828_INT_VSYS_UV_DET, 3, BD71828_INT_VSYS_UV_DET_MASK),
+ REGMAP_IRQ_REG(BD71828_INT_VSYS_LOW_RES, 3, BD71828_INT_VSYS_LOW_RES_MASK),
+ REGMAP_IRQ_REG(BD71828_INT_VSYS_LOW_DET, 3, BD71828_INT_VSYS_LOW_DET_MASK),
+ REGMAP_IRQ_REG(BD71828_INT_VSYS_HALL_IN, 3, BD71828_INT_VSYS_HALL_IN_MASK),
+ REGMAP_IRQ_REG(BD71828_INT_VSYS_HALL_TOGGLE, 3, BD71828_INT_VSYS_HALL_TOGGLE_MASK),
+ REGMAP_IRQ_REG(BD71828_INT_VSYS_MON_RES, 3, BD71828_INT_VSYS_MON_RES_MASK),
+ REGMAP_IRQ_REG(BD71828_INT_VSYS_MON_DET, 3, BD71828_INT_VSYS_MON_DET_MASK),
/* Charger */
- REGMAP_IRQ_REG(BD71828_INT_CHG_DCIN_ILIM, 4,
- BD71828_INT_CHG_DCIN_ILIM_MASK),
- REGMAP_IRQ_REG(BD71828_INT_CHG_TOPOFF_TO_DONE, 4,
- BD71828_INT_CHG_TOPOFF_TO_DONE_MASK),
- REGMAP_IRQ_REG(BD71828_INT_CHG_WDG_TEMP, 4,
- BD71828_INT_CHG_WDG_TEMP_MASK),
- REGMAP_IRQ_REG(BD71828_INT_CHG_WDG_TIME, 4,
- BD71828_INT_CHG_WDG_TIME_MASK),
- REGMAP_IRQ_REG(BD71828_INT_CHG_RECHARGE_RES, 4,
- BD71828_INT_CHG_RECHARGE_RES_MASK),
- REGMAP_IRQ_REG(BD71828_INT_CHG_RECHARGE_DET, 4,
- BD71828_INT_CHG_RECHARGE_DET_MASK),
+ REGMAP_IRQ_REG(BD71828_INT_CHG_DCIN_ILIM, 4, BD71828_INT_CHG_DCIN_ILIM_MASK),
+ REGMAP_IRQ_REG(BD71828_INT_CHG_TOPOFF_TO_DONE, 4, BD71828_INT_CHG_TOPOFF_TO_DONE_MASK),
+ REGMAP_IRQ_REG(BD71828_INT_CHG_WDG_TEMP, 4, BD71828_INT_CHG_WDG_TEMP_MASK),
+ REGMAP_IRQ_REG(BD71828_INT_CHG_WDG_TIME, 4, BD71828_INT_CHG_WDG_TIME_MASK),
+ REGMAP_IRQ_REG(BD71828_INT_CHG_RECHARGE_RES, 4, BD71828_INT_CHG_RECHARGE_RES_MASK),
+ REGMAP_IRQ_REG(BD71828_INT_CHG_RECHARGE_DET, 4, BD71828_INT_CHG_RECHARGE_DET_MASK),
REGMAP_IRQ_REG(BD71828_INT_CHG_RANGED_TEMP_TRANSITION, 4,
BD71828_INT_CHG_RANGED_TEMP_TRANSITION_MASK),
- REGMAP_IRQ_REG(BD71828_INT_CHG_STATE_TRANSITION, 4,
- BD71828_INT_CHG_STATE_TRANSITION_MASK),
+ REGMAP_IRQ_REG(BD71828_INT_CHG_STATE_TRANSITION, 4, BD71828_INT_CHG_STATE_TRANSITION_MASK),
/* Battery */
- REGMAP_IRQ_REG(BD71828_INT_BAT_TEMP_NORMAL, 5,
- BD71828_INT_BAT_TEMP_NORMAL_MASK),
- REGMAP_IRQ_REG(BD71828_INT_BAT_TEMP_ERANGE, 5,
- BD71828_INT_BAT_TEMP_ERANGE_MASK),
- REGMAP_IRQ_REG(BD71828_INT_BAT_TEMP_WARN, 5,
- BD71828_INT_BAT_TEMP_WARN_MASK),
- REGMAP_IRQ_REG(BD71828_INT_BAT_REMOVED, 5,
- BD71828_INT_BAT_REMOVED_MASK),
- REGMAP_IRQ_REG(BD71828_INT_BAT_DETECTED, 5,
- BD71828_INT_BAT_DETECTED_MASK),
- REGMAP_IRQ_REG(BD71828_INT_THERM_REMOVED, 5,
- BD71828_INT_THERM_REMOVED_MASK),
- REGMAP_IRQ_REG(BD71828_INT_THERM_DETECTED, 5,
- BD71828_INT_THERM_DETECTED_MASK),
+ REGMAP_IRQ_REG(BD71828_INT_BAT_TEMP_NORMAL, 5, BD71828_INT_BAT_TEMP_NORMAL_MASK),
+ REGMAP_IRQ_REG(BD71828_INT_BAT_TEMP_ERANGE, 5, BD71828_INT_BAT_TEMP_ERANGE_MASK),
+ REGMAP_IRQ_REG(BD71828_INT_BAT_TEMP_WARN, 5, BD71828_INT_BAT_TEMP_WARN_MASK),
+ REGMAP_IRQ_REG(BD71828_INT_BAT_REMOVED, 5, BD71828_INT_BAT_REMOVED_MASK),
+ REGMAP_IRQ_REG(BD71828_INT_BAT_DETECTED, 5, BD71828_INT_BAT_DETECTED_MASK),
+ REGMAP_IRQ_REG(BD71828_INT_THERM_REMOVED, 5, BD71828_INT_THERM_REMOVED_MASK),
+ REGMAP_IRQ_REG(BD71828_INT_THERM_DETECTED, 5, BD71828_INT_THERM_DETECTED_MASK),
/* Battery Mon 1 */
REGMAP_IRQ_REG(BD71828_INT_BAT_DEAD, 6, BD71828_INT_BAT_DEAD_MASK),
- REGMAP_IRQ_REG(BD71828_INT_BAT_SHORTC_RES, 6,
- BD71828_INT_BAT_SHORTC_RES_MASK),
- REGMAP_IRQ_REG(BD71828_INT_BAT_SHORTC_DET, 6,
- BD71828_INT_BAT_SHORTC_DET_MASK),
- REGMAP_IRQ_REG(BD71828_INT_BAT_LOW_VOLT_RES, 6,
- BD71828_INT_BAT_LOW_VOLT_RES_MASK),
- REGMAP_IRQ_REG(BD71828_INT_BAT_LOW_VOLT_DET, 6,
- BD71828_INT_BAT_LOW_VOLT_DET_MASK),
- REGMAP_IRQ_REG(BD71828_INT_BAT_OVER_VOLT_RES, 6,
- BD71828_INT_BAT_OVER_VOLT_RES_MASK),
- REGMAP_IRQ_REG(BD71828_INT_BAT_OVER_VOLT_DET, 6,
- BD71828_INT_BAT_OVER_VOLT_DET_MASK),
+ REGMAP_IRQ_REG(BD71828_INT_BAT_SHORTC_RES, 6, BD71828_INT_BAT_SHORTC_RES_MASK),
+ REGMAP_IRQ_REG(BD71828_INT_BAT_SHORTC_DET, 6, BD71828_INT_BAT_SHORTC_DET_MASK),
+ REGMAP_IRQ_REG(BD71828_INT_BAT_LOW_VOLT_RES, 6, BD71828_INT_BAT_LOW_VOLT_RES_MASK),
+ REGMAP_IRQ_REG(BD71828_INT_BAT_LOW_VOLT_DET, 6, BD71828_INT_BAT_LOW_VOLT_DET_MASK),
+ REGMAP_IRQ_REG(BD71828_INT_BAT_OVER_VOLT_RES, 6, BD71828_INT_BAT_OVER_VOLT_RES_MASK),
+ REGMAP_IRQ_REG(BD71828_INT_BAT_OVER_VOLT_DET, 6, BD71828_INT_BAT_OVER_VOLT_DET_MASK),
/* Battery Mon 2 */
- REGMAP_IRQ_REG(BD71828_INT_BAT_MON_RES, 7,
- BD71828_INT_BAT_MON_RES_MASK),
- REGMAP_IRQ_REG(BD71828_INT_BAT_MON_DET, 7,
- BD71828_INT_BAT_MON_DET_MASK),
+ REGMAP_IRQ_REG(BD71828_INT_BAT_MON_RES, 7, BD71828_INT_BAT_MON_RES_MASK),
+ REGMAP_IRQ_REG(BD71828_INT_BAT_MON_DET, 7, BD71828_INT_BAT_MON_DET_MASK),
/* Battery Mon 3 (Coulomb counter) */
- REGMAP_IRQ_REG(BD71828_INT_BAT_CC_MON1, 8,
- BD71828_INT_BAT_CC_MON1_MASK),
- REGMAP_IRQ_REG(BD71828_INT_BAT_CC_MON2, 8,
- BD71828_INT_BAT_CC_MON2_MASK),
- REGMAP_IRQ_REG(BD71828_INT_BAT_CC_MON3, 8,
- BD71828_INT_BAT_CC_MON3_MASK),
+ REGMAP_IRQ_REG(BD71828_INT_BAT_CC_MON1, 8, BD71828_INT_BAT_CC_MON1_MASK),
+ REGMAP_IRQ_REG(BD71828_INT_BAT_CC_MON2, 8, BD71828_INT_BAT_CC_MON2_MASK),
+ REGMAP_IRQ_REG(BD71828_INT_BAT_CC_MON3, 8, BD71828_INT_BAT_CC_MON3_MASK),
/* Battery Mon 4 */
- REGMAP_IRQ_REG(BD71828_INT_BAT_OVER_CURR_1_RES, 9,
- BD71828_INT_BAT_OVER_CURR_1_RES_MASK),
- REGMAP_IRQ_REG(BD71828_INT_BAT_OVER_CURR_1_DET, 9,
- BD71828_INT_BAT_OVER_CURR_1_DET_MASK),
- REGMAP_IRQ_REG(BD71828_INT_BAT_OVER_CURR_2_RES, 9,
- BD71828_INT_BAT_OVER_CURR_2_RES_MASK),
- REGMAP_IRQ_REG(BD71828_INT_BAT_OVER_CURR_2_DET, 9,
- BD71828_INT_BAT_OVER_CURR_2_DET_MASK),
- REGMAP_IRQ_REG(BD71828_INT_BAT_OVER_CURR_3_RES, 9,
- BD71828_INT_BAT_OVER_CURR_3_RES_MASK),
- REGMAP_IRQ_REG(BD71828_INT_BAT_OVER_CURR_3_DET, 9,
- BD71828_INT_BAT_OVER_CURR_3_DET_MASK),
+ REGMAP_IRQ_REG(BD71828_INT_BAT_OVER_CURR_1_RES, 9, BD71828_INT_BAT_OVER_CURR_1_RES_MASK),
+ REGMAP_IRQ_REG(BD71828_INT_BAT_OVER_CURR_1_DET, 9, BD71828_INT_BAT_OVER_CURR_1_DET_MASK),
+ REGMAP_IRQ_REG(BD71828_INT_BAT_OVER_CURR_2_RES, 9, BD71828_INT_BAT_OVER_CURR_2_RES_MASK),
+ REGMAP_IRQ_REG(BD71828_INT_BAT_OVER_CURR_2_DET, 9, BD71828_INT_BAT_OVER_CURR_2_DET_MASK),
+ REGMAP_IRQ_REG(BD71828_INT_BAT_OVER_CURR_3_RES, 9, BD71828_INT_BAT_OVER_CURR_3_RES_MASK),
+ REGMAP_IRQ_REG(BD71828_INT_BAT_OVER_CURR_3_DET, 9, BD71828_INT_BAT_OVER_CURR_3_DET_MASK),
/* Temperature */
- REGMAP_IRQ_REG(BD71828_INT_TEMP_BAT_LOW_RES, 10,
- BD71828_INT_TEMP_BAT_LOW_RES_MASK),
- REGMAP_IRQ_REG(BD71828_INT_TEMP_BAT_LOW_DET, 10,
- BD71828_INT_TEMP_BAT_LOW_DET_MASK),
- REGMAP_IRQ_REG(BD71828_INT_TEMP_BAT_HI_RES, 10,
- BD71828_INT_TEMP_BAT_HI_RES_MASK),
- REGMAP_IRQ_REG(BD71828_INT_TEMP_BAT_HI_DET, 10,
- BD71828_INT_TEMP_BAT_HI_DET_MASK),
+ REGMAP_IRQ_REG(BD71828_INT_TEMP_BAT_LOW_RES, 10, BD71828_INT_TEMP_BAT_LOW_RES_MASK),
+ REGMAP_IRQ_REG(BD71828_INT_TEMP_BAT_LOW_DET, 10, BD71828_INT_TEMP_BAT_LOW_DET_MASK),
+ REGMAP_IRQ_REG(BD71828_INT_TEMP_BAT_HI_RES, 10, BD71828_INT_TEMP_BAT_HI_RES_MASK),
+ REGMAP_IRQ_REG(BD71828_INT_TEMP_BAT_HI_DET, 10, BD71828_INT_TEMP_BAT_HI_DET_MASK),
REGMAP_IRQ_REG(BD71828_INT_TEMP_CHIP_OVER_125_RES, 10,
BD71828_INT_TEMP_CHIP_OVER_125_RES_MASK),
REGMAP_IRQ_REG(BD71828_INT_TEMP_CHIP_OVER_125_DET, 10,
@@ -267,57 +419,133 @@ static struct regmap_irq_chip bd71828_irq_chip = {
.init_ack_masked = true,
.num_regs = 12,
.num_main_regs = 1,
- .sub_reg_offsets = &bd71828_sub_irq_offsets[0],
+ .sub_reg_offsets = &bd718xx_sub_irq_offsets[0],
+ .num_main_status_bits = 8,
+ .irq_reg_stride = 1,
+};
+
+static struct regmap_irq_chip bd71815_irq_chip = {
+ .name = "bd71815_irq",
+ .main_status = BD71815_REG_INT_STAT,
+ .irqs = &bd71815_irqs[0],
+ .num_irqs = ARRAY_SIZE(bd71815_irqs),
+ .status_base = BD71815_REG_INT_STAT_01,
+ .mask_base = BD71815_REG_INT_EN_01,
+ .ack_base = BD71815_REG_INT_STAT_01,
+ .mask_invert = true,
+ .init_ack_masked = true,
+ .num_regs = 12,
+ .num_main_regs = 1,
+ .sub_reg_offsets = &bd718xx_sub_irq_offsets[0],
.num_main_status_bits = 8,
.irq_reg_stride = 1,
};
+static int set_clk_mode(struct device *dev, struct regmap *regmap,
+ int clkmode_reg)
+{
+ int ret;
+ unsigned int open_drain;
+
+ ret = of_property_read_u32(dev->of_node, "rohm,clkout-open-drain", &open_drain);
+ if (ret) {
+ if (ret == -EINVAL)
+ return 0;
+ return ret;
+ }
+ if (open_drain > 1) {
+ dev_err(dev, "bad clk32kout mode configuration");
+ return -EINVAL;
+ }
+
+ if (open_drain)
+ return regmap_update_bits(regmap, clkmode_reg, OUT32K_MODE,
+ OUT32K_MODE_OPEN_DRAIN);
+
+ return regmap_update_bits(regmap, clkmode_reg, OUT32K_MODE,
+ OUT32K_MODE_CMOS);
+}
+
static int bd71828_i2c_probe(struct i2c_client *i2c)
{
- struct rohm_regmap_dev *chip;
struct regmap_irq_chip_data *irq_data;
int ret;
+ struct regmap *regmap;
+ const struct regmap_config *regmap_config;
+ struct regmap_irq_chip *irqchip;
+ unsigned int chip_type;
+ struct mfd_cell *mfd;
+ int cells;
+ int button_irq;
+ int clkmode_reg;
if (!i2c->irq) {
dev_err(&i2c->dev, "No IRQ configured\n");
return -EINVAL;
}
- chip = devm_kzalloc(&i2c->dev, sizeof(*chip), GFP_KERNEL);
- if (!chip)
- return -ENOMEM;
-
- dev_set_drvdata(&i2c->dev, chip);
+ chip_type = (unsigned int)(uintptr_t)
+ of_device_get_match_data(&i2c->dev);
+ switch (chip_type) {
+ case ROHM_CHIP_TYPE_BD71828:
+ mfd = bd71828_mfd_cells;
+ cells = ARRAY_SIZE(bd71828_mfd_cells);
+ regmap_config = &bd71828_regmap;
+ irqchip = &bd71828_irq_chip;
+ clkmode_reg = BD71828_REG_OUT32K;
+ button_irq = BD71828_INT_SHORTPUSH;
+ break;
+ case ROHM_CHIP_TYPE_BD71815:
+ mfd = bd71815_mfd_cells;
+ cells = ARRAY_SIZE(bd71815_mfd_cells);
+ regmap_config = &bd71815_regmap;
+ irqchip = &bd71815_irq_chip;
+ clkmode_reg = BD71815_REG_OUT32K;
+ /*
+ * If BD71817 support is needed we should be able to handle it
+ * with proper DT configs + BD71815 drivers + power-button.
+ * BD71815 data-sheet does not list the power-button IRQ so we
+ * don't use it.
+ */
+ button_irq = 0;
+ break;
+ default:
+ dev_err(&i2c->dev, "Unknown device type");
+ return -EINVAL;
+ }
- chip->regmap = devm_regmap_init_i2c(i2c, &bd71828_regmap);
- if (IS_ERR(chip->regmap)) {
+ regmap = devm_regmap_init_i2c(i2c, regmap_config);
+ if (IS_ERR(regmap)) {
dev_err(&i2c->dev, "Failed to initialize Regmap\n");
- return PTR_ERR(chip->regmap);
+ return PTR_ERR(regmap);
}
- ret = devm_regmap_add_irq_chip(&i2c->dev, chip->regmap,
- i2c->irq, IRQF_ONESHOT, 0,
- &bd71828_irq_chip, &irq_data);
+ ret = devm_regmap_add_irq_chip(&i2c->dev, regmap, i2c->irq,
+ IRQF_ONESHOT, 0, irqchip, &irq_data);
if (ret) {
dev_err(&i2c->dev, "Failed to add IRQ chip\n");
return ret;
}
dev_dbg(&i2c->dev, "Registered %d IRQs for chip\n",
- bd71828_irq_chip.num_irqs);
+ irqchip->num_irqs);
- ret = regmap_irq_get_virq(irq_data, BD71828_INT_SHORTPUSH);
- if (ret < 0) {
- dev_err(&i2c->dev, "Failed to get the power-key IRQ\n");
- return ret;
+ if (button_irq) {
+ ret = regmap_irq_get_virq(irq_data, button_irq);
+ if (ret < 0) {
+ dev_err(&i2c->dev, "Failed to get the power-key IRQ\n");
+ return ret;
+ }
+
+ button.irq = ret;
}
- button.irq = ret;
+ ret = set_clk_mode(&i2c->dev, regmap, clkmode_reg);
+ if (ret)
+ return ret;
- ret = devm_mfd_add_devices(&i2c->dev, PLATFORM_DEVID_AUTO,
- bd71828_mfd_cells,
- ARRAY_SIZE(bd71828_mfd_cells), NULL, 0,
- regmap_irq_get_domain(irq_data));
+ ret = devm_mfd_add_devices(&i2c->dev, PLATFORM_DEVID_AUTO, mfd, cells,
+ NULL, 0, regmap_irq_get_domain(irq_data));
if (ret)
dev_err(&i2c->dev, "Failed to create subdevices\n");
@@ -325,7 +553,13 @@ static int bd71828_i2c_probe(struct i2c_client *i2c)
}
static const struct of_device_id bd71828_of_match[] = {
- { .compatible = "rohm,bd71828", },
+ {
+ .compatible = "rohm,bd71828",
+ .data = (void *)ROHM_CHIP_TYPE_BD71828,
+ }, {
+ .compatible = "rohm,bd71815",
+ .data = (void *)ROHM_CHIP_TYPE_BD71815,
+ },
{ },
};
MODULE_DEVICE_TABLE(of, bd71828_of_match);
diff --git a/drivers/mfd/rohm-bd718x7.c b/drivers/mfd/rohm-bd718x7.c
index c32c1b6c98fa..bfd81f78beae 100644
--- a/drivers/mfd/rohm-bd718x7.c
+++ b/drivers/mfd/rohm-bd718x7.c
@@ -91,9 +91,9 @@ static const struct regmap_config bd718xx_regmap_config = {
.cache_type = REGCACHE_RBTREE,
};
-static int bd718xx_init_press_duration(struct bd718xx *bd718xx)
+static int bd718xx_init_press_duration(struct regmap *regmap,
+ struct device *dev)
{
- struct device* dev = bd718xx->chip.dev;
u32 short_press_ms, long_press_ms;
u32 short_press_value, long_press_value;
int ret;
@@ -102,8 +102,7 @@ static int bd718xx_init_press_duration(struct bd718xx *bd718xx)
&short_press_ms);
if (!ret) {
short_press_value = min(15u, (short_press_ms + 250) / 500);
- ret = regmap_update_bits(bd718xx->chip.regmap,
- BD718XX_REG_PWRONCONFIG0,
+ ret = regmap_update_bits(regmap, BD718XX_REG_PWRONCONFIG0,
BD718XX_PWRBTN_PRESS_DURATION_MASK,
short_press_value);
if (ret) {
@@ -116,8 +115,7 @@ static int bd718xx_init_press_duration(struct bd718xx *bd718xx)
&long_press_ms);
if (!ret) {
long_press_value = min(15u, (long_press_ms + 500) / 1000);
- ret = regmap_update_bits(bd718xx->chip.regmap,
- BD718XX_REG_PWRONCONFIG1,
+ ret = regmap_update_bits(regmap, BD718XX_REG_PWRONCONFIG1,
BD718XX_PWRBTN_PRESS_DURATION_MASK,
long_press_value);
if (ret) {
@@ -132,7 +130,8 @@ static int bd718xx_init_press_duration(struct bd718xx *bd718xx)
static int bd718xx_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
- struct bd718xx *bd718xx;
+ struct regmap *regmap;
+ struct regmap_irq_chip_data *irq_data;
int ret;
unsigned int chip_type;
struct mfd_cell *mfd;
@@ -142,13 +141,6 @@ static int bd718xx_i2c_probe(struct i2c_client *i2c,
dev_err(&i2c->dev, "No IRQ configured\n");
return -EINVAL;
}
-
- bd718xx = devm_kzalloc(&i2c->dev, sizeof(struct bd718xx), GFP_KERNEL);
-
- if (!bd718xx)
- return -ENOMEM;
-
- bd718xx->chip_irq = i2c->irq;
chip_type = (unsigned int)(uintptr_t)
of_device_get_match_data(&i2c->dev);
switch (chip_type) {
@@ -164,29 +156,26 @@ static int bd718xx_i2c_probe(struct i2c_client *i2c,
dev_err(&i2c->dev, "Unknown device type");
return -EINVAL;
}
- bd718xx->chip.dev = &i2c->dev;
- dev_set_drvdata(&i2c->dev, bd718xx);
- bd718xx->chip.regmap = devm_regmap_init_i2c(i2c,
- &bd718xx_regmap_config);
- if (IS_ERR(bd718xx->chip.regmap)) {
+ regmap = devm_regmap_init_i2c(i2c, &bd718xx_regmap_config);
+ if (IS_ERR(regmap)) {
dev_err(&i2c->dev, "regmap initialization failed\n");
- return PTR_ERR(bd718xx->chip.regmap);
+ return PTR_ERR(regmap);
}
- ret = devm_regmap_add_irq_chip(&i2c->dev, bd718xx->chip.regmap,
- bd718xx->chip_irq, IRQF_ONESHOT, 0,
- &bd718xx_irq_chip, &bd718xx->irq_data);
+ ret = devm_regmap_add_irq_chip(&i2c->dev, regmap, i2c->irq,
+ IRQF_ONESHOT, 0, &bd718xx_irq_chip,
+ &irq_data);
if (ret) {
dev_err(&i2c->dev, "Failed to add irq_chip\n");
return ret;
}
- ret = bd718xx_init_press_duration(bd718xx);
+ ret = bd718xx_init_press_duration(regmap, &i2c->dev);
if (ret)
return ret;
- ret = regmap_irq_get_virq(bd718xx->irq_data, BD718XX_INT_PWRBTN_S);
+ ret = regmap_irq_get_virq(irq_data, BD718XX_INT_PWRBTN_S);
if (ret < 0) {
dev_err(&i2c->dev, "Failed to get the IRQ\n");
@@ -195,9 +184,9 @@ static int bd718xx_i2c_probe(struct i2c_client *i2c,
button.irq = ret;
- ret = devm_mfd_add_devices(bd718xx->chip.dev, PLATFORM_DEVID_AUTO,
+ ret = devm_mfd_add_devices(&i2c->dev, PLATFORM_DEVID_AUTO,
mfd, cells, NULL, 0,
- regmap_irq_get_domain(bd718xx->irq_data));
+ regmap_irq_get_domain(irq_data));
if (ret)
dev_err(&i2c->dev, "Failed to create subdevices\n");