Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8e685483 authored by Krystian Garbaciak's avatar Krystian Garbaciak Committed by Samuel Ortiz
Browse files

mfd: da9063: Add Dialog DA9063 core driver



This is MFD module providing access to registers and interrupts of DA906x
series PMIC. It is used by other functional modules, registered as MFD cells.
Driver uses regmap with paging to access extended register list. Register map
is divided into two pages, where the second page is used during initialisation.

This module provides support to following functional cells:
 - Regulators
 - RTC
 - HWMON
 - OnKey (power key misc input device)
 - Vibration (force-feedback input device)
 - Watchdog
 - LEDs

Signed-off-by: default avatarKrystian Garbaciak <krystian.garbaciak@diasemi.com>
Signed-off-by: default avatarPhilipp Zabel <p.zabel@pengutronix.de>
Reviewed-by: default avatarMark Brown <broonie@linaro.org>
Signed-off-by: default avatarSamuel Ortiz <sameo@linux.intel.com>
parent 36ec66e0
Loading
Loading
Loading
Loading
+12 −0
Original line number Diff line number Diff line
@@ -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

+3 −0
Original line number Diff line number Diff line
@@ -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
+129 −0
Original line number Diff line number Diff line
/*
 * 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");
+182 −0
Original line number Diff line number Diff line
/* 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);
+55 −0
Original line number Diff line number Diff line
/*
 * Definitions for DA9063 MFD driver
 *
 * Copyright 2012 Dialog Semiconductor Ltd.
 *
 * Author: Michal Hajduk <michal.hajduk@diasemi.com>
 *	   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.
 *
 */

#ifndef __MFD_DA9063_CORE_H__
#define __MFD_DA9063_CORE_H__

#include <linux/interrupt.h>
#include <linux/mfd/da9063/registers.h>

/* DA9063 modules */
#define DA9063_DRVNAME_CORE		"da9063-core"
#define DA9063_DRVNAME_REGULATORS	"da9063-regulators"
#define DA9063_DRVNAME_LEDS		"da9063-leds"
#define DA9063_DRVNAME_WATCHDOG		"da9063-watchdog"
#define DA9063_DRVNAME_HWMON		"da9063-hwmon"
#define DA9063_DRVNAME_ONKEY		"da9063-onkey"
#define DA9063_DRVNAME_RTC		"da9063-rtc"
#define DA9063_DRVNAME_VIBRATION	"da9063-vibration"

enum da9063_models {
	PMIC_DA9063 = 0x61,
};

struct da9063 {
	/* Device */
	struct device	*dev;
	unsigned short	model;
	unsigned short	revision;
	unsigned int	flags;

	/* Control interface */
	struct regmap	*regmap;

	/* Interrupts */
	int		chip_irq;
	unsigned int	irq_base;
};

int da9063_device_init(struct da9063 *da9063, unsigned int irq);

void da9063_device_exit(struct da9063 *da9063);

#endif /* __MFD_DA9063_CORE_H__ */
Loading