diff options
Diffstat (limited to 'drivers/mfd')
-rw-r--r-- | drivers/mfd/Kconfig | 12 | ||||
-rw-r--r-- | drivers/mfd/Makefile | 3 | ||||
-rw-r--r-- | drivers/mfd/da9063-core.c | 129 | ||||
-rw-r--r-- | drivers/mfd/da9063-i2c.c | 182 |
4 files changed, 326 insertions, 0 deletions
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 633ee43efae6..e0e46f50f95d 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -139,6 +139,18 @@ config MFD_DA9055 This driver can be built as a module. If built as a module it will be called "da9055" +config MFD_DA9063 + bool "Dialog Semiconductor DA9063 PMIC Support" + select MFD_CORE + select REGMAP_I2C + select REGMAP_IRQ + depends on I2C=y && GENERIC_HARDIRQS + help + Say yes here for support for the Dialog Semiconductor DA9063 PMIC. + This includes the I2C driver and core APIs. + Additional drivers must be enabled in order to use the functionality + of the device. + config MFD_MC13783 tristate diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 3c90051ffa5a..0bb84d59f3fa 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -107,6 +107,9 @@ obj-$(CONFIG_MFD_LP8788) += lp8788.o lp8788-irq.o da9055-objs := da9055-core.o da9055-i2c.o obj-$(CONFIG_MFD_DA9055) += da9055.o +da9063-objs := da9063-core.o da9063-i2c.o +obj-$(CONFIG_MFD_DA9063) += da9063.o + obj-$(CONFIG_MFD_MAX77686) += max77686.o max77686-irq.o obj-$(CONFIG_MFD_MAX77693) += max77693.o max77693-irq.o obj-$(CONFIG_MFD_MAX8907) += max8907.o diff --git a/drivers/mfd/da9063-core.c b/drivers/mfd/da9063-core.c new file mode 100644 index 000000000000..29f3ed1f076c --- /dev/null +++ b/drivers/mfd/da9063-core.c @@ -0,0 +1,129 @@ +/* + * da9063-core.c: Device access for Dialog DA9063 modules + * + * Copyright 2012 Dialog Semiconductors Ltd. + * Copyright 2013 Philipp Zabel, Pengutronix + * + * Author: Krystian Garbaciak <krystian.garbaciak@diasemi.com>, + * Michal Hajduk <michal.hajduk@diasemi.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/device.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/mutex.h> +#include <linux/mfd/core.h> +#include <linux/regmap.h> + +#include <linux/mfd/da9063/core.h> +#include <linux/mfd/da9063/pdata.h> +#include <linux/mfd/da9063/registers.h> + +#include <linux/proc_fs.h> +#include <linux/kthread.h> +#include <linux/uaccess.h> + + +static struct mfd_cell da9063_devs[] = { + { + .name = DA9063_DRVNAME_REGULATORS, + }, + { + .name = DA9063_DRVNAME_LEDS, + }, + { + .name = DA9063_DRVNAME_WATCHDOG, + }, + { + .name = DA9063_DRVNAME_HWMON, + }, + { + .name = DA9063_DRVNAME_ONKEY, + }, + { + .name = DA9063_DRVNAME_RTC, + }, + { + .name = DA9063_DRVNAME_VIBRATION, + }, +}; + +int da9063_device_init(struct da9063 *da9063, unsigned int irq) +{ + struct da9063_pdata *pdata = da9063->dev->platform_data; + int model, revision; + int ret; + + if (pdata) { + da9063->flags = pdata->flags; + da9063->irq_base = pdata->irq_base; + } else { + da9063->flags = 0; + da9063->irq_base = 0; + } + da9063->chip_irq = irq; + + if (pdata && pdata->init != NULL) { + ret = pdata->init(da9063); + if (ret != 0) { + dev_err(da9063->dev, + "Platform initialization failed.\n"); + return ret; + } + } + + ret = regmap_read(da9063->regmap, DA9063_REG_CHIP_ID, &model); + if (ret < 0) { + dev_err(da9063->dev, "Cannot read chip model id.\n"); + return -EIO; + } + if (model != PMIC_DA9063) { + dev_err(da9063->dev, "Invalid chip model id: 0x%02x\n", model); + return -ENODEV; + } + + ret = regmap_read(da9063->regmap, DA9063_REG_CHIP_VARIANT, &revision); + if (ret < 0) { + dev_err(da9063->dev, "Cannot read chip revision id.\n"); + return -EIO; + } + revision >>= DA9063_CHIP_VARIANT_SHIFT; + if (revision != 3) { + dev_err(da9063->dev, "Unknown chip revision: %d\n", revision); + return -ENODEV; + } + + da9063->model = model; + da9063->revision = revision; + + dev_info(da9063->dev, + "Device detected (model-ID: 0x%02X rev-ID: 0x%02X)\n", + model, revision); + + ret = mfd_add_devices(da9063->dev, -1, da9063_devs, + ARRAY_SIZE(da9063_devs), NULL, da9063->irq_base, + NULL); + if (ret) + dev_err(da9063->dev, "Cannot add MFD cells\n"); + + return ret; +} + +void da9063_device_exit(struct da9063 *da9063) +{ + mfd_remove_devices(da9063->dev); +} + +MODULE_DESCRIPTION("PMIC driver for Dialog DA9063"); +MODULE_AUTHOR("Krystian Garbaciak <krystian.garbaciak@diasemi.com>, Michal Hajduk <michal.hajduk@diasemi.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/da9063-i2c.c b/drivers/mfd/da9063-i2c.c new file mode 100644 index 000000000000..8db5c805c64f --- /dev/null +++ b/drivers/mfd/da9063-i2c.c @@ -0,0 +1,182 @@ +/* da9063-i2c.c: Interrupt support for Dialog DA9063 + * + * Copyright 2012 Dialog Semiconductor Ltd. + * Copyright 2013 Philipp Zabel, Pengutronix + * + * Author: Krystian Garbaciak <krystian.garbaciak@diasemi.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/i2c.h> +#include <linux/regmap.h> +#include <linux/delay.h> +#include <linux/slab.h> +#include <linux/err.h> + +#include <linux/mfd/core.h> +#include <linux/mfd/da9063/core.h> +#include <linux/mfd/da9063/pdata.h> +#include <linux/mfd/da9063/registers.h> + +static const struct regmap_range da9063_readable_ranges[] = { + { + .range_min = DA9063_REG_PAGE_CON, + .range_max = DA9063_REG_SECOND_D, + }, { + .range_min = DA9063_REG_SEQ, + .range_max = DA9063_REG_ID_32_31, + }, { + .range_min = DA9063_REG_SEQ_A, + .range_max = DA9063_REG_AUTO3_LOW, + }, { + .range_min = DA9063_REG_T_OFFSET, + .range_max = DA9063_REG_GP_ID_19, + }, { + .range_min = DA9063_REG_CHIP_ID, + .range_max = DA9063_REG_CHIP_VARIANT, + }, +}; + +static const struct regmap_range da9063_writeable_ranges[] = { + { + .range_min = DA9063_REG_PAGE_CON, + .range_max = DA9063_REG_PAGE_CON, + }, { + .range_min = DA9063_REG_FAULT_LOG, + .range_max = DA9063_REG_VSYS_MON, + }, { + .range_min = DA9063_REG_COUNT_S, + .range_max = DA9063_REG_ALARM_Y, + }, { + .range_min = DA9063_REG_SEQ, + .range_max = DA9063_REG_ID_32_31, + }, { + .range_min = DA9063_REG_SEQ_A, + .range_max = DA9063_REG_AUTO3_LOW, + }, { + .range_min = DA9063_REG_CONFIG_I, + .range_max = DA9063_REG_MON_REG_4, + }, { + .range_min = DA9063_REG_GP_ID_0, + .range_max = DA9063_REG_GP_ID_19, + }, +}; + +static const struct regmap_range da9063_volatile_ranges[] = { + { + .range_min = DA9063_REG_STATUS_A, + .range_max = DA9063_REG_EVENT_D, + }, { + .range_min = DA9063_REG_CONTROL_F, + .range_max = DA9063_REG_CONTROL_F, + }, { + .range_min = DA9063_REG_ADC_MAN, + .range_max = DA9063_REG_ADC_MAN, + }, { + .range_min = DA9063_REG_ADC_RES_L, + .range_max = DA9063_REG_SECOND_D, + }, { + .range_min = DA9063_REG_MON_REG_5, + .range_max = DA9063_REG_MON_REG_6, + }, +}; + +static const struct regmap_access_table da9063_readable_table = { + .yes_ranges = da9063_readable_ranges, + .n_yes_ranges = ARRAY_SIZE(da9063_readable_ranges), +}; + +static const struct regmap_access_table da9063_writeable_table = { + .yes_ranges = da9063_writeable_ranges, + .n_yes_ranges = ARRAY_SIZE(da9063_writeable_ranges), +}; + +static const struct regmap_access_table da9063_volatile_table = { + .yes_ranges = da9063_volatile_ranges, + .n_yes_ranges = ARRAY_SIZE(da9063_volatile_ranges), +}; + +static const struct regmap_range_cfg da9063_range_cfg[] = { + { + .range_min = DA9063_REG_PAGE_CON, + .range_max = DA9063_REG_CHIP_VARIANT, + .selector_reg = DA9063_REG_PAGE_CON, + .selector_mask = 1 << DA9063_I2C_PAGE_SEL_SHIFT, + .selector_shift = DA9063_I2C_PAGE_SEL_SHIFT, + .window_start = 0, + .window_len = 256, + } +}; + +static struct regmap_config da9063_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .ranges = da9063_range_cfg, + .num_ranges = ARRAY_SIZE(da9063_range_cfg), + .max_register = DA9063_REG_CHIP_VARIANT, + + .cache_type = REGCACHE_RBTREE, + + .rd_table = &da9063_readable_table, + .wr_table = &da9063_writeable_table, + .volatile_table = &da9063_volatile_table, +}; + +static int da9063_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct da9063 *da9063; + int ret; + + da9063 = devm_kzalloc(&i2c->dev, sizeof(struct da9063), GFP_KERNEL); + if (da9063 == NULL) + return -ENOMEM; + + i2c_set_clientdata(i2c, da9063); + da9063->dev = &i2c->dev; + da9063->chip_irq = i2c->irq; + + da9063->regmap = devm_regmap_init_i2c(i2c, &da9063_regmap_config); + if (IS_ERR(da9063->regmap)) { + ret = PTR_ERR(da9063->regmap); + dev_err(da9063->dev, "Failed to allocate register map: %d\n", + ret); + return ret; + } + + return da9063_device_init(da9063, i2c->irq); +} + +static int da9063_i2c_remove(struct i2c_client *i2c) +{ + struct da9063 *da9063 = i2c_get_clientdata(i2c); + + da9063_device_exit(da9063); + + return 0; +} + +static const struct i2c_device_id da9063_i2c_id[] = { + {"da9063", PMIC_DA9063}, + {}, +}; +MODULE_DEVICE_TABLE(i2c, da9063_i2c_id); + +static struct i2c_driver da9063_i2c_driver = { + .driver = { + .name = "da9063", + .owner = THIS_MODULE, + }, + .probe = da9063_i2c_probe, + .remove = da9063_i2c_remove, + .id_table = da9063_i2c_id, +}; + +module_i2c_driver(da9063_i2c_driver); |