mfd: Add ROHM BD9571MWV-M MFD PMIC driver

Add the MFD part of the ROHM BD9571MWV-M PMIC driver and MAINTAINERS
entry. The MFD part only specifies the regmap bits for the PMIC and
binds the subdevs together.

Signed-off-by: Marek Vasut <marek.vasut+renesas@gmail.com>
Signed-off-by: Lee Jones <lee.jones@linaro.org>
This commit is contained in:
Marek Vasut
2017-07-17 22:45:12 +02:00
gecommit door Lee Jones
bovenliggende ec58871fb9
commit d3ea212720
5 gewijzigde bestanden met toevoegingen van 371 en 0 verwijderingen

Bestand weergeven

@@ -133,6 +133,20 @@ config MFD_BCM590XX
help
Support for the BCM590xx PMUs from Broadcom
config MFD_BD9571MWV
tristate "ROHM BD9571MWV PMIC"
select MFD_CORE
select REGMAP_I2C
select REGMAP_IRQ
depends on I2C
help
Support for the ROHM BD9571MWV PMIC, which contains single
voltage regulator, voltage sampling units, GPIO block and
watchdog block.
This driver can also be built as a module. If so, the module
will be called bd9571mwv.
config MFD_AC100
tristate "X-Powers AC100"
select MFD_CORE

Bestand weergeven

@@ -10,6 +10,7 @@ obj-$(CONFIG_MFD_ACT8945A) += act8945a.o
obj-$(CONFIG_MFD_SM501) += sm501.o
obj-$(CONFIG_MFD_ASIC3) += asic3.o tmio_core.o
obj-$(CONFIG_MFD_BCM590XX) += bcm590xx.o
obj-$(CONFIG_MFD_BD9571MWV) += bd9571mwv.o
cros_ec_core-objs := cros_ec.o
cros_ec_core-$(CONFIG_ACPI) += cros_ec_acpi_gpe.o
obj-$(CONFIG_MFD_CROS_EC) += cros_ec_core.o

230
drivers/mfd/bd9571mwv.c Normal file
Bestand weergeven

@@ -0,0 +1,230 @@
/*
* ROHM BD9571MWV-M MFD driver
*
* Copyright (C) 2017 Marek Vasut <marek.vasut+renesas@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
* kind, whether expressed or implied; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License version 2 for more details.
*
* Based on the TPS65086 driver
*/
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/mfd/core.h>
#include <linux/module.h>
#include <linux/mfd/bd9571mwv.h>
static const struct mfd_cell bd9571mwv_cells[] = {
{ .name = "bd9571mwv-regulator", },
{ .name = "bd9571mwv-gpio", },
};
static const struct regmap_range bd9571mwv_readable_yes_ranges[] = {
regmap_reg_range(BD9571MWV_VENDOR_CODE, BD9571MWV_PRODUCT_REVISION),
regmap_reg_range(BD9571MWV_AVS_SET_MONI, BD9571MWV_AVS_DVFS_VID(3)),
regmap_reg_range(BD9571MWV_VD18_VID, BD9571MWV_VD33_VID),
regmap_reg_range(BD9571MWV_DVFS_VINIT, BD9571MWV_DVFS_VINIT),
regmap_reg_range(BD9571MWV_DVFS_SETVMAX, BD9571MWV_DVFS_MONIVDAC),
regmap_reg_range(BD9571MWV_GPIO_IN, BD9571MWV_GPIO_IN),
regmap_reg_range(BD9571MWV_GPIO_INT, BD9571MWV_GPIO_INTMASK),
regmap_reg_range(BD9571MWV_INT_INTREQ, BD9571MWV_INT_INTMASK),
};
static const struct regmap_access_table bd9571mwv_readable_table = {
.yes_ranges = bd9571mwv_readable_yes_ranges,
.n_yes_ranges = ARRAY_SIZE(bd9571mwv_readable_yes_ranges),
};
static const struct regmap_range bd9571mwv_writable_yes_ranges[] = {
regmap_reg_range(BD9571MWV_AVS_VD09_VID(0), BD9571MWV_AVS_VD09_VID(3)),
regmap_reg_range(BD9571MWV_DVFS_SETVID, BD9571MWV_DVFS_SETVID),
regmap_reg_range(BD9571MWV_GPIO_DIR, BD9571MWV_GPIO_OUT),
regmap_reg_range(BD9571MWV_GPIO_INT_SET, BD9571MWV_GPIO_INTMASK),
regmap_reg_range(BD9571MWV_INT_INTREQ, BD9571MWV_INT_INTMASK),
};
static const struct regmap_access_table bd9571mwv_writable_table = {
.yes_ranges = bd9571mwv_writable_yes_ranges,
.n_yes_ranges = ARRAY_SIZE(bd9571mwv_writable_yes_ranges),
};
static const struct regmap_range bd9571mwv_volatile_yes_ranges[] = {
regmap_reg_range(BD9571MWV_GPIO_IN, BD9571MWV_GPIO_IN),
regmap_reg_range(BD9571MWV_GPIO_INT, BD9571MWV_GPIO_INT),
regmap_reg_range(BD9571MWV_INT_INTREQ, BD9571MWV_INT_INTREQ),
};
static const struct regmap_access_table bd9571mwv_volatile_table = {
.yes_ranges = bd9571mwv_volatile_yes_ranges,
.n_yes_ranges = ARRAY_SIZE(bd9571mwv_volatile_yes_ranges),
};
static const struct regmap_config bd9571mwv_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.cache_type = REGCACHE_RBTREE,
.rd_table = &bd9571mwv_readable_table,
.wr_table = &bd9571mwv_writable_table,
.volatile_table = &bd9571mwv_volatile_table,
.max_register = 0xff,
};
static const struct regmap_irq bd9571mwv_irqs[] = {
REGMAP_IRQ_REG(BD9571MWV_IRQ_MD1, 0,
BD9571MWV_INT_INTREQ_MD1_INT),
REGMAP_IRQ_REG(BD9571MWV_IRQ_MD2_E1, 0,
BD9571MWV_INT_INTREQ_MD2_E1_INT),
REGMAP_IRQ_REG(BD9571MWV_IRQ_MD2_E2, 0,
BD9571MWV_INT_INTREQ_MD2_E2_INT),
REGMAP_IRQ_REG(BD9571MWV_IRQ_PROT_ERR, 0,
BD9571MWV_INT_INTREQ_PROT_ERR_INT),
REGMAP_IRQ_REG(BD9571MWV_IRQ_GP, 0,
BD9571MWV_INT_INTREQ_GP_INT),
REGMAP_IRQ_REG(BD9571MWV_IRQ_128H_OF, 0,
BD9571MWV_INT_INTREQ_128H_OF_INT),
REGMAP_IRQ_REG(BD9571MWV_IRQ_WDT_OF, 0,
BD9571MWV_INT_INTREQ_WDT_OF_INT),
REGMAP_IRQ_REG(BD9571MWV_IRQ_BKUP_TRG, 0,
BD9571MWV_INT_INTREQ_BKUP_TRG_INT),
};
static struct regmap_irq_chip bd9571mwv_irq_chip = {
.name = "bd9571mwv",
.status_base = BD9571MWV_INT_INTREQ,
.mask_base = BD9571MWV_INT_INTMASK,
.ack_base = BD9571MWV_INT_INTREQ,
.init_ack_masked = true,
.num_regs = 1,
.irqs = bd9571mwv_irqs,
.num_irqs = ARRAY_SIZE(bd9571mwv_irqs),
};
static int bd9571mwv_identify(struct bd9571mwv *bd)
{
struct device *dev = bd->dev;
unsigned int value;
int ret;
ret = regmap_read(bd->regmap, BD9571MWV_VENDOR_CODE, &value);
if (ret) {
dev_err(dev, "Failed to read vendor code register (ret=%i)\n",
ret);
return ret;
}
if (value != BD9571MWV_VENDOR_CODE_VAL) {
dev_err(dev, "Invalid vendor code ID %02x (expected %02x)\n",
value, BD9571MWV_VENDOR_CODE_VAL);
return -EINVAL;
}
ret = regmap_read(bd->regmap, BD9571MWV_PRODUCT_CODE, &value);
if (ret) {
dev_err(dev, "Failed to read product code register (ret=%i)\n",
ret);
return ret;
}
if (value != BD9571MWV_PRODUCT_CODE_VAL) {
dev_err(dev, "Invalid product code ID %02x (expected %02x)\n",
value, BD9571MWV_PRODUCT_CODE_VAL);
return -EINVAL;
}
ret = regmap_read(bd->regmap, BD9571MWV_PRODUCT_REVISION, &value);
if (ret) {
dev_err(dev, "Failed to read revision register (ret=%i)\n",
ret);
return ret;
}
dev_info(dev, "Device: BD9571MWV rev. %d\n", value & 0xff);
return 0;
}
static int bd9571mwv_probe(struct i2c_client *client,
const struct i2c_device_id *ids)
{
struct bd9571mwv *bd;
int ret;
bd = devm_kzalloc(&client->dev, sizeof(*bd), GFP_KERNEL);
if (!bd)
return -ENOMEM;
i2c_set_clientdata(client, bd);
bd->dev = &client->dev;
bd->irq = client->irq;
bd->regmap = devm_regmap_init_i2c(client, &bd9571mwv_regmap_config);
if (IS_ERR(bd->regmap)) {
dev_err(bd->dev, "Failed to initialize register map\n");
return PTR_ERR(bd->regmap);
}
ret = bd9571mwv_identify(bd);
if (ret)
return ret;
ret = regmap_add_irq_chip(bd->regmap, bd->irq, IRQF_ONESHOT, 0,
&bd9571mwv_irq_chip, &bd->irq_data);
if (ret) {
dev_err(bd->dev, "Failed to register IRQ chip\n");
return ret;
}
ret = mfd_add_devices(bd->dev, PLATFORM_DEVID_AUTO, bd9571mwv_cells,
ARRAY_SIZE(bd9571mwv_cells), NULL, 0,
regmap_irq_get_domain(bd->irq_data));
if (ret) {
regmap_del_irq_chip(bd->irq, bd->irq_data);
return ret;
}
return 0;
}
static int bd9571mwv_remove(struct i2c_client *client)
{
struct bd9571mwv *bd = i2c_get_clientdata(client);
regmap_del_irq_chip(bd->irq, bd->irq_data);
return 0;
}
static const struct of_device_id bd9571mwv_of_match_table[] = {
{ .compatible = "rohm,bd9571mwv", },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, bd9571mwv_of_match_table);
static const struct i2c_device_id bd9571mwv_id_table[] = {
{ "bd9571mwv", 0 },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(i2c, bd9571mwv_id_table);
static struct i2c_driver bd9571mwv_driver = {
.driver = {
.name = "bd9571mwv",
.of_match_table = bd9571mwv_of_match_table,
},
.probe = bd9571mwv_probe,
.remove = bd9571mwv_remove,
.id_table = bd9571mwv_id_table,
};
module_i2c_driver(bd9571mwv_driver);
MODULE_AUTHOR("Marek Vasut <marek.vasut+renesas@gmail.com>");
MODULE_DESCRIPTION("BD9571MWV PMIC Driver");
MODULE_LICENSE("GPL v2");