From 96786e4dad75b307222afb0ee616b3d6b011fbe1 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Thu, 11 Jun 2015 15:48:43 +0200 Subject: mfd: da9063: add da9063 watchdog and system restart driver Signed-off-by: Philipp Zabel --- drivers/mfd/Kconfig | 4 ++ drivers/mfd/Makefile | 1 + drivers/mfd/da9063.c | 169 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 174 insertions(+) create mode 100644 drivers/mfd/da9063.c (limited to 'drivers/mfd') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 3af904dd6d..3eb26b53fe 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -4,6 +4,10 @@ config MFD_ACT8846 depends on I2C bool "ACT8846 driver" +config MFD_DA9063 + depends on I2C + bool "DA9063 PMIC driver" + config MFD_LP3972 depends on I2C bool "LP3972 driver" diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 49b9e35663..4eed247c69 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -1,4 +1,5 @@ obj-$(CONFIG_MFD_ACT8846) += act8846.o +obj-$(CONFIG_MFD_DA9063) += da9063.o obj-$(CONFIG_MFD_LP3972) += lp3972.o obj-$(CONFIG_MFD_MC13XXX) += mc13xxx.o obj-$(CONFIG_MFD_MC34704) += mc34704.o diff --git a/drivers/mfd/da9063.c b/drivers/mfd/da9063.c new file mode 100644 index 0000000000..4a335c9983 --- /dev/null +++ b/drivers/mfd/da9063.c @@ -0,0 +1,169 @@ +/* + * Copyright (C) 2015 Pengutronix, Philipp Zabel + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +struct da9063 { + struct restart_handler restart; + struct watchdog wd; + struct i2c_client *client; + struct device_d *dev; +}; + +/* System Control and Event Registers */ +#define DA9063_REG_FAULT_LOG 0x05 +#define DA9063_REG_CONTROL_D 0x11 +#define DA9063_REG_CONTROL_F 0x13 + +/* DA9063_REG_FAULT_LOG (addr=0x05) */ +#define DA9063_TWD_ERROR 0x01 +#define DA9063_POR 0x02 +#define DA9063_NSHUTDOWN 0x40 + +/* DA9063_REG_CONTROL_D (addr=0x11) */ +#define DA9063_TWDSCALE_MASK 0x07 + +/* DA9063_REG_CONTROL_F (addr=0x13) */ +#define DA9063_SHUTDOWN 0x02 + +static int da9063_watchdog_set_timeout(struct watchdog *wd, unsigned timeout) +{ + struct da9063 *priv = container_of(wd, struct da9063, wd); + struct device_d *dev = priv->dev; + unsigned int 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 = i2c_read_reg(priv->client, DA9063_REG_CONTROL_D, &val, 1); + if (ret < 0) + return ret; + + val &= ~DA9063_TWDSCALE_MASK; + val |= scale; + + ret = i2c_write_reg(priv->client, DA9063_REG_CONTROL_D, &val, 1); + if (ret < 0) + return ret; + + return 0; +} + +static void da9063_detect_reset_source(struct da9063 *priv) +{ + int ret; + u8 val; + unsigned int priority; + enum reset_src_type type; + + ret = i2c_read_reg(priv->client, DA9063_REG_FAULT_LOG, &val, 1); + if (ret < 0) + return; + + /* Write one to clear */ + i2c_write_reg(priv->client, DA9063_REG_FAULT_LOG, &val, 1); + + if (val & DA9063_TWD_ERROR) + type = RESET_WDG; + else if (val & DA9063_POR) + type = RESET_POR; + else if (val & DA9063_NSHUTDOWN) + type = RESET_RST; + else + return; + + priority = of_get_reset_source_priority(priv->dev->device_node); + + reset_source_set_priority(type, priority); +} + +static void da9063_restart(struct restart_handler *rst) +{ + struct da9063 *priv = container_of(rst, struct da9063, restart); + + u8 val = DA9063_SHUTDOWN; + + i2c_write_reg(priv->client, DA9063_REG_CONTROL_F, &val, 1); +} + +static int da9063_probe(struct device_d *dev) +{ + struct da9063 *priv = NULL; + int ret; + + priv = xzalloc(sizeof(struct da9063)); + priv->wd.priority = of_get_watchdog_priority(dev->device_node); + priv->wd.set_timeout = da9063_watchdog_set_timeout; + priv->wd.dev = dev; + priv->client = to_i2c_client(dev); + priv->dev = dev; + + ret = watchdog_register(&priv->wd); + if (ret) + goto on_error; + + da9063_detect_reset_source(priv); + + priv->restart.priority = of_get_restart_priority(dev->device_node); + priv->restart.name = "da9063"; + priv->restart.restart = &da9063_restart; + + restart_handler_register(&priv->restart); + + return 0; + +on_error: + if (priv) + free(priv); + return ret; +} + +static struct platform_device_id da9063_id[] = { + { "da9063", }, + { } +}; + +static struct driver_d da9063_driver = { + .name = "da9063", + .probe = da9063_probe, + .id_table = da9063_id, +}; + +static int da9063_init(void) +{ + return i2c_driver_register(&da9063_driver); +} + +device_initcall(da9063_init); -- cgit v1.2.3