[PATCH 6/6] MFD/DA9053: da9053: add basic da9053 driver

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Signed-off-by: Jan Luebbe <jlu@xxxxxxxxxxxxxx>
Signed-off-by: Juergen Borleis <jbe@xxxxxxxxxxxxxx>
---
 drivers/mfd/Kconfig  |   8 ++
 drivers/mfd/Makefile |   1 +
 drivers/mfd/da9053.c | 336 +++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 345 insertions(+)
 create mode 100644 drivers/mfd/da9053.c

diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 3af904d..6ec66ac 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -4,6 +4,14 @@ config MFD_ACT8846
 	depends on I2C
 	bool "ACT8846 driver"
 
+config MFD_DA9053
+	depends on I2C
+	bool "DA9053 PMIC driver"
+	help
+	  This power management controller provides configureable power supplies,
+	  a machine restart feature and a watchdog which are effective machine
+	  wide.
+
 config MFD_LP3972
 	depends on I2C
 	bool "LP3972 driver"
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 49b9e35..2899dde 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -1,4 +1,5 @@
 obj-$(CONFIG_MFD_ACT8846)	+= act8846.o
+obj-$(CONFIG_MFD_DA9053)	+= da9053.o
 obj-$(CONFIG_MFD_LP3972)	+= lp3972.o
 obj-$(CONFIG_MFD_MC13XXX)	+= mc13xxx.o
 obj-$(CONFIG_MFD_MC34704)	+= mc34704.o
diff --git a/drivers/mfd/da9053.c b/drivers/mfd/da9053.c
new file mode 100644
index 0000000..601458d
--- /dev/null
+++ b/drivers/mfd/da9053.c
@@ -0,0 +1,336 @@
+/*
+ * Copyright (C) 2013 Jan Luebbe <jlu@xxxxxxxxxxxxxx>
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ *
+ */
+
+#include <common.h>
+#include <init.h>
+#include <driver.h>
+#include <xfuncs.h>
+#include <errno.h>
+#include <watchdog.h>
+
+#include <i2c/i2c.h>
+#include <reset_source.h>
+#include <restart.h>
+
+#define DRIVERNAME		"da9053"
+
+/* STATUS REGISTERS */
+#define DA9053_STATUS_A_REG		1
+#define DA9053_STATUS_B_REG		2
+#define DA9053_STATUS_C_REG		3
+#define DA9053_STATUS_D_REG		4
+
+/* PARK REGISTER */
+#define DA9053_PARK_REGISTER		DA9053_STATUS_D_REG
+
+/* EVENT REGISTERS */
+#define DA9053_EVENT_A_REG		5
+#define DA9053_EVENT_B_REG		6
+#define DA9053_EVENT_C_REG		7
+#define DA9053_EVENT_D_REG		8
+#define DA9053_FAULTLOG_REG		9
+
+/* IRQ REGISTERS */
+#define DA9053_IRQ_MASK_A_REG		10
+#define DA9053_IRQ_MASK_B_REG		11
+#define DA9053_IRQ_MASK_C_REG		12
+#define DA9053_IRQ_MASK_D_REG		13
+
+/* CONTROL REGISTERS */
+#define DA9053_CONTROL_A_REG		14
+#define DA9053_CONTROL_B_REG		15
+#define DA9053_CONTROL_C_REG		16
+#define DA9053_CONTROL_D_REG		17
+
+#define DA9053_PDDIS_REG		18
+#define DA9053_INTERFACE_REG		19
+#define DA9053_RESET_REG		20
+
+/* FAULT LOG REGISTER BITS */
+#define DA9053_FAULTLOG_WAITSET		0X80
+#define DA9053_FAULTLOG_NSDSET		0X40
+#define DA9053_FAULTLOG_KEYSHUT		0X20
+#define DA9053_FAULTLOG_TEMPOVER	0X08
+#define DA9053_FAULTLOG_VDDSTART	0X04
+#define DA9053_FAULTLOG_VDDFAULT	0X02
+#define DA9053_FAULTLOG_TWDERROR	0X01
+
+/* CONTROL REGISTER B BITS */
+#define DA9053_CONTROLB_SHUTDOWN	0X80
+#define DA9053_CONTROLB_DEEPSLEEP	0X40
+#define DA9053_CONTROLB_WRITEMODE	0X20
+#define DA9053_CONTROLB_BBATEN		0X10
+#define DA9053_CONTROLB_OTPREADEN	0X08
+#define DA9053_CONTROLB_AUTOBOOT	0X04
+#define DA9053_CONTROLB_ACTDIODE	0X02
+#define DA9053_CONTROLB_BUCKMERGE	0X01
+
+/* CONTROL REGISTER D BITS */
+#define DA9053_CONTROLD_WATCHDOG	0X80
+#define DA9053_CONTROLD_ACCDETEN	0X40
+#define DA9053_CONTROLD_GPI1415SD	0X20
+#define DA9053_CONTROLD_NONKEYSD	0X10
+#define DA9053_CONTROLD_KEEPACTEN	0X08
+#define DA9053_CONTROLD_TWDSCALE	0X07
+
+struct da9053_priv {
+	struct cdev		cdev;
+	struct watchdog		wd;
+	struct i2c_client	*client;
+};
+
+#define cdev_to_da9053_priv(x)	container_of(x, struct da9053_priv, cdev)
+#define wd_to_da9053_priv(x)	container_of(x, struct da9053_priv, wd)
+
+static int da9053_reg_read(struct da9053_priv *da9053, u32 reg, u8 *val)
+{
+	int ret;
+
+	ret = i2c_read_reg(da9053->client, reg, val, 1);
+
+	return ret == 1 ? 0 : ret;
+}
+
+static int da9053_reg_write(struct da9053_priv *da9053, u32 reg, u8 val)
+{
+	int ret;
+
+	ret = i2c_write_reg(da9053->client, reg, &val, 1);
+
+	return ret == 1 ? 0 : ret;
+}
+
+static int da9053_park(struct da9053_priv *da9053)
+{
+	int ret;
+	u8 val;
+
+	ret = i2c_read_reg(da9053->client, DA9053_PARK_REGISTER, &val, 1);
+
+	return ret == 1 ? 0 : ret;
+}
+
+static ssize_t da9053_read(struct cdev *cdev, void *_buf, size_t count, loff_t offset, ulong flags)
+{
+	struct da9053_priv *da9053 = cdev_to_da9053_priv(cdev);
+	u8 *buf = _buf;
+	size_t i = count;
+	int err;
+
+	while (i) {
+		err = da9053_reg_read(da9053, offset, buf);
+		if (err) {
+			da9053_park(da9053);
+			return (ssize_t)err;
+		}
+		buf++;
+		i--;
+		offset++;
+	}
+
+	if (da9053_park(da9053) < 0)
+		return -EIO;
+
+	return count;
+}
+
+static int da9053_enable_multiwrite(struct da9053_priv *da9053)
+{
+	int ret;
+	u8 val;
+
+	ret = da9053_reg_read(da9053, DA9053_CONTROL_B_REG, &val);
+	if (ret < 0)
+		return ret;
+
+	if (val & DA9053_CONTROLB_WRITEMODE) {
+		val &= ~DA9053_CONTROLB_WRITEMODE;
+		ret = da9053_reg_write(da9053, DA9053_CONTROL_B_REG, val);
+		if (ret < 0)
+			return ret;
+	}
+
+	ret = da9053_park(da9053);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static struct file_operations da9053_fops = {
+	.lseek	= dev_lseek_default,
+	.read	= da9053_read,
+};
+
+static int da9053_set_timeout(struct watchdog *wd, unsigned timeout)
+{
+	struct da9053_priv *da9053 = wd_to_da9053_priv(wd);
+	struct device_d *dev = da9053->cdev.dev;
+	unsigned scale = 0;
+	int ret;
+	u8 val;
+
+	if (timeout > 131)
+		return -EINVAL;
+
+	if (timeout) {
+		timeout *= 1000; /* convert to ms */
+		scale = 0;
+		while (timeout > (2048 << scale) && scale <= 6)
+			scale++;
+		dev_dbg(dev, "calculated TWDSCALE=%u (req=%ims calc=%ims)\n",
+		        scale, timeout, 2048 << scale);
+		scale++; /* scale 0 disables the WD */
+	}
+
+	ret = da9053_reg_read(da9053, DA9053_CONTROL_D_REG, &val);
+	if (ret < 0)
+		return ret;
+
+	dev_dbg(dev, "read watchdog (val=0x%02x)\n", val);
+
+	if (scale && scale == (val & DA9053_CONTROLD_TWDSCALE)) {
+		/* trigger WD */
+		val |= DA9053_CONTROLD_WATCHDOG;
+		ret = da9053_reg_write(da9053, DA9053_CONTROL_D_REG, val);
+		if (ret < 0)
+			return ret;
+		dev_dbg(dev, "triggered watchdog (val=0x%02x)\n", val);
+	} else {
+		/* disable WD first */
+		val &= ~DA9053_CONTROLD_TWDSCALE;
+		ret = da9053_reg_write(da9053, DA9053_CONTROL_D_REG, val);
+		if (ret < 0)
+			return ret;
+
+		dev_dbg(dev, "disabled watchdog (val=0x%02x)\n", val);
+		if (scale) {
+			/* park before waiting */
+			ret = da9053_park(da9053);
+			if (ret < 0)
+				return ret;
+
+			/* wait required time */
+			udelay(150);
+
+			/* enable WD with new timeout */
+			val |= scale;
+			ret = da9053_reg_write(da9053, DA9053_CONTROL_D_REG, val);
+			if (ret < 0)
+				return ret;
+			dev_dbg(dev, "enabled watchdog (val=0x%02x)\n", val);
+		}
+	}
+
+	ret = da9053_park(da9053);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static void da9053_detect_reset_source(struct da9053_priv *da9053)
+{
+	int ret;
+	u8 val;
+
+	ret = da9053_reg_read(da9053, DA9053_FAULTLOG_REG, &val);
+	if (ret < 0)
+		return;
+
+	ret = da9053_park(da9053);
+	if (ret < 0)
+		return;
+
+	if (val & DA9053_FAULTLOG_TWDERROR) {
+		reset_source_set(RESET_WDG, FEATURE_SCOPE_MACHINE);
+		return;
+	}
+
+	if (val & DA9053_FAULTLOG_VDDFAULT) {
+		reset_source_set(RESET_POR, FEATURE_SCOPE_MACHINE);
+		return;
+	}
+
+	if (val & DA9053_FAULTLOG_NSDSET) {
+		reset_source_set(RESET_RST, FEATURE_SCOPE_MACHINE);
+		return;
+	}
+
+	/* else keep the default 'unknown' state */
+}
+
+static void __noreturn da9053_force_system_reset(struct device_d *dev)
+{
+	struct da9053_priv *da9053 = dev->priv;
+	u8 val;
+	int ret;
+
+	ret = da9053_reg_read(da9053, DA9053_CONTROL_B_REG, &val);
+	if (ret < 0)
+		hang();
+
+	val |= DA9053_CONTROLB_SHUTDOWN;
+	ret = da9053_reg_write(da9053, DA9053_CONTROL_B_REG, val);
+	if (ret < 0)
+		hang();
+
+	da9053_park(da9053);
+
+	hang();
+}
+
+static int da9053_probe(struct device_d *dev)
+{
+	struct da9053_priv *da9053;
+	int ret;
+
+	da9053 = xzalloc(sizeof(struct da9053_priv));
+	dev->priv = da9053;
+	da9053->cdev.name = DRIVERNAME;
+	da9053->client = to_i2c_client(dev);
+	da9053->cdev.size = 128;
+	da9053->cdev.dev = dev;
+	da9053->cdev.ops = &da9053_fops;
+	da9053->wd.set_timeout = da9053_set_timeout;
+
+	ret = da9053_enable_multiwrite(da9053);
+	if (ret < 0)
+		return ret;
+
+	ret = watchdog_register(&da9053->wd, FEATURE_SCOPE_MACHINE);
+	if (ret)
+		return ret;
+
+	da9053_detect_reset_source(da9053);
+
+	restart_add_scope(da9053_force_system_reset, dev, FEATURE_SCOPE_MACHINE);
+
+	return 0;
+}
+
+static struct driver_d da9053_driver = {
+	.name  = DRIVERNAME,
+	.probe = da9053_probe,
+};
+
+static int da9053_init(void)
+{
+	i2c_driver_register(&da9053_driver);
+	return 0;
+}
+
+device_initcall(da9053_init);
-- 
2.1.4


_______________________________________________
barebox mailing list
barebox@xxxxxxxxxxxxxxxxxxx
http://lists.infradead.org/mailman/listinfo/barebox



[Index of Archives]     [Linux Embedded]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [XFree86]

  Powered by Linux