From cc2c60c531572ac27f42f419d3da1a65e4e67262 Mon Sep 17 00:00:00 2001 From: Enrico Scholz Date: Wed, 3 Aug 2016 16:14:51 +0200 Subject: mfd: da9063: reset watchdog timer Setting the new timeout does not suffice for restarting the watchdog; the self-clearing bit in CONTROL_F must be set too. barebox does not provide a ping function in its watchdog api to restart the watchdog timer. Detect ping vs. set_timeout() by comparing the actual with the previous timeout value. Signed-off-by: Enrico Scholz Tested-by: Stefan Christ Signed-off-by: Christian Hemp --- drivers/mfd/da9063.c | 36 ++++++++++++++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/da9063.c b/drivers/mfd/da9063.c index ac303611c0..74ad5bfdab 100644 --- a/drivers/mfd/da9063.c +++ b/drivers/mfd/da9063.c @@ -28,8 +28,13 @@ struct da9063 { struct watchdog wd; struct i2c_client *client; struct device_d *dev; + unsigned int timeout; }; +/* forbidden/impossible value; timeout will be set to this value initially to + * detect ping vs. set_timeout() operations. */ +#define DA9063_INITIAL_TIMEOUT (~0u) + /* System Control and Event Registers */ #define DA9063_REG_FAULT_LOG 0x05 #define DA9063_REG_CONTROL_D 0x11 @@ -44,8 +49,25 @@ struct da9063 { #define DA9063_TWDSCALE_MASK 0x07 /* DA9063_REG_CONTROL_F (addr=0x13) */ +#define DA9063_WATCHDOG 0x01 #define DA9063_SHUTDOWN 0x02 +static int da9063_watchdog_ping(struct da9063 *priv) +{ + int ret; + u8 val; + + dev_dbg(priv->dev, "ping\n"); + + /* reset watchdog timer; register is self clearing */ + val = DA9063_WATCHDOG; + ret = i2c_write_reg(priv->client, DA9063_REG_CONTROL_F, &val, 1); + if (ret < 0) + return ret; + + return 0; +} + static int da9063_watchdog_set_timeout(struct watchdog *wd, unsigned timeout) { struct da9063 *priv = container_of(wd, struct da9063, wd); @@ -57,8 +79,14 @@ static int da9063_watchdog_set_timeout(struct watchdog *wd, unsigned timeout) if (timeout > 131) return -EINVAL; + timeout *= 1000; /* convert to ms */ + + if (timeout == priv->timeout) + /* set_timeout called with previous parameter; just ping the + * watchdog */ + goto out; + if (timeout) { - timeout *= 1000; /* convert to ms */ scale = 0; while (timeout > (2048 << scale) && scale <= 6) scale++; @@ -78,7 +106,10 @@ static int da9063_watchdog_set_timeout(struct watchdog *wd, unsigned timeout) if (ret < 0) return ret; - return 0; + priv->timeout = timeout; + +out: + return da9063_watchdog_ping(priv); } static void da9063_detect_reset_source(struct da9063 *priv) @@ -127,6 +158,7 @@ static int da9063_probe(struct device_d *dev) priv->wd.priority = of_get_watchdog_priority(dev->device_node); priv->wd.set_timeout = da9063_watchdog_set_timeout; priv->wd.hwdev = dev; + priv->timeout = DA9063_INITIAL_TIMEOUT; priv->client = to_i2c_client(dev); priv->dev = dev; -- cgit v1.2.3 From 13ce6c24f0b5217bb9f787b0ddd82a8eceb8894a Mon Sep 17 00:00:00 2001 From: Enrico Scholz Date: Wed, 3 Aug 2016 16:14:52 +0200 Subject: mfd: da9063: added generic reg_update() function Used to set or clear bits of a register. The calculation of 'client' might look odd and unnecessary complicated. But style prepares access to other register banks which is added in following patches. Signed-off-by: Enrico Scholz Tested-by: Stefan Christ Signed-off-by: Christian Hemp --- drivers/mfd/da9063.c | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) (limited to 'drivers/mfd') diff --git a/drivers/mfd/da9063.c b/drivers/mfd/da9063.c index 74ad5bfdab..68b0723b61 100644 --- a/drivers/mfd/da9063.c +++ b/drivers/mfd/da9063.c @@ -52,6 +52,43 @@ struct da9063 { #define DA9063_WATCHDOG 0x01 #define DA9063_SHUTDOWN 0x02 +static int da906x_reg_update(struct da9063 *priv, unsigned int reg, + uint8_t mask, uint8_t val) +{ + struct i2c_client *client; + uint8_t tmp; + int ret; + + if (reg < 0x100) + client = priv->client; + else + /* this should/can not happen because function is usually + * called with a static register number; warn about it + * below */ + client = NULL; + + if (WARN_ON(!client)) + return -EINVAL; + + ret = i2c_read_reg(client, reg & 0xffu, &tmp, 1); + if (ret < 0) { + dev_warn(priv->dev, "failed to read reg %02x\n", reg); + return ret; + } + + tmp &= ~mask; + tmp |= val; + + ret = i2c_write_reg(client, reg & 0xffu, &tmp, 1); + if (ret < 0) { + dev_warn(priv->dev, "failed to write %02x into reg %02x\n", + tmp, reg); + return ret; + } + + return 0; +} + static int da9063_watchdog_ping(struct da9063 *priv) { int ret; -- cgit v1.2.3 From 0369cc0f922f7c795a9d9dafc0904b8aa81c2c9c Mon Sep 17 00:00:00 2001 From: Enrico Scholz Date: Wed, 3 Aug 2016 16:14:53 +0200 Subject: mfd: da9063: use da906x_reg_update() Use the previously added da906x_reg_update() function. NOTE: patch depends on another patchset ("da9063: reset watchdog timer"), Signed-off-by: Enrico Scholz Tested-by: Stefan Christ Signed-off-by: Christian Hemp --- drivers/mfd/da9063.c | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/da9063.c b/drivers/mfd/da9063.c index 68b0723b61..4f09bd384e 100644 --- a/drivers/mfd/da9063.c +++ b/drivers/mfd/da9063.c @@ -111,7 +111,6 @@ static int da9063_watchdog_set_timeout(struct watchdog *wd, unsigned timeout) struct device_d *dev = priv->dev; unsigned int scale = 0; int ret; - u8 val; if (timeout > 131) return -EINVAL; @@ -132,14 +131,8 @@ static int da9063_watchdog_set_timeout(struct watchdog *wd, unsigned timeout) 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); + ret = da906x_reg_update(priv, DA9063_REG_CONTROL_D, + DA9063_TWDSCALE_MASK, scale); if (ret < 0) return ret; -- cgit v1.2.3 From 9a31a4765473ff23adaff5bf72b06f87a70b6fb2 Mon Sep 17 00:00:00 2001 From: Enrico Scholz Date: Wed, 3 Aug 2016 16:14:54 +0200 Subject: mfd: da9063: add device specific init function Provide some more information about the used PMIC type; later patches will add support for DA9062 which needs a custom init function. Signed-off-by: Enrico Scholz Tested-by: Stefan Christ Signed-off-by: Christian Hemp --- drivers/mfd/da9063.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/da9063.c b/drivers/mfd/da9063.c index 4f09bd384e..a1c748610a 100644 --- a/drivers/mfd/da9063.c +++ b/drivers/mfd/da9063.c @@ -52,6 +52,10 @@ struct da9063 { #define DA9063_WATCHDOG 0x01 #define DA9063_SHUTDOWN 0x02 +struct da906x_device_data { + int (*init)(struct da9063 *priv); +}; + static int da906x_reg_update(struct da9063 *priv, unsigned int reg, uint8_t mask, uint8_t val) { @@ -182,8 +186,13 @@ static void da9063_restart(struct restart_handler *rst) static int da9063_probe(struct device_d *dev) { struct da9063 *priv = NULL; + struct da906x_device_data const *dev_data; + void const *dev_data_tmp; int ret; + ret = dev_get_drvdata(dev, &dev_data_tmp); + dev_data = ret < 0 ? NULL : dev_data_tmp; + priv = xzalloc(sizeof(struct da9063)); priv->wd.priority = of_get_watchdog_priority(dev->device_node); priv->wd.set_timeout = da9063_watchdog_set_timeout; @@ -192,6 +201,12 @@ static int da9063_probe(struct device_d *dev) priv->client = to_i2c_client(dev); priv->dev = dev; + if (dev_data && dev_data->init) { + ret = dev_data->init(priv); + if (ret < 0) + goto on_error; + } + ret = watchdog_register(&priv->wd); if (ret) goto on_error; @@ -213,7 +228,7 @@ on_error: } static struct platform_device_id da9063_id[] = { - { "da9063", }, + { "da9063", (uintptr_t)(NULL) }, { } }; -- cgit v1.2.3 From 6c9a8b5d7f6a7d5d46d58707f1aa9c16db4a14b1 Mon Sep 17 00:00:00 2001 From: Enrico Scholz Date: Wed, 3 Aug 2016 16:14:55 +0200 Subject: mfd: da9063: added of_device_id information Signed-off-by: Enrico Scholz Tested-by: Stefan Christ Signed-off-by: Christian Hemp --- drivers/mfd/da9063.c | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) (limited to 'drivers/mfd') diff --git a/drivers/mfd/da9063.c b/drivers/mfd/da9063.c index a1c748610a..00a61279f3 100644 --- a/drivers/mfd/da9063.c +++ b/drivers/mfd/da9063.c @@ -27,6 +27,8 @@ struct da9063 { struct restart_handler restart; struct watchdog wd; struct i2c_client *client; + /* dummy client for accessing bank #1 */ + struct i2c_client *client1; struct device_d *dev; unsigned int timeout; }; @@ -40,6 +42,9 @@ struct da9063 { #define DA9063_REG_CONTROL_D 0x11 #define DA9063_REG_CONTROL_F 0x13 +/* bank1: control register I */ +#define DA9063_REG1_CONFIG_I 0x10e + /* DA9063_REG_FAULT_LOG (addr=0x05) */ #define DA9063_TWD_ERROR 0x01 #define DA9063_POR 0x02 @@ -52,6 +57,9 @@ struct da9063 { #define DA9063_WATCHDOG 0x01 #define DA9063_SHUTDOWN 0x02 +/* DA9063_REG_CONTROL_I (addr=0x10e) */ +#define DA9062_WATCHDOG_SD BIT(3) + struct da906x_device_data { int (*init)(struct da9063 *priv); }; @@ -65,6 +73,8 @@ static int da906x_reg_update(struct da9063 *priv, unsigned int reg, if (reg < 0x100) client = priv->client; + else if (reg < 0x200) + client = priv->client1; else /* this should/can not happen because function is usually * called with a static register number; warn about it @@ -183,6 +193,30 @@ static void da9063_restart(struct restart_handler *rst) i2c_write_reg(priv->client, DA9063_REG_CONTROL_F, &val, 1); } +static int da9062_device_init(struct da9063 *priv) +{ + int ret; + + priv->client1 = i2c_new_dummy(priv->client->adapter, + priv->client->addr + 1); + if (!priv) { + dev_warn(priv->dev, "failed to create bank 1 device\n"); + /* TODO: return -EINVAL; i2c api does not return more + * details */ + return -EINVAL; + } + + /* clear CONFIG_I[WATCHDOG_SD] */ + ret = da906x_reg_update(priv, DA9063_REG1_CONFIG_I, + DA9062_WATCHDOG_SD, DA9062_WATCHDOG_SD); + + return ret; +} + +static struct da906x_device_data const da9062_device_data = { + .init = da9062_device_init, +}; + static int da9063_probe(struct device_d *dev) { struct da9063 *priv = NULL; @@ -229,13 +263,26 @@ on_error: static struct platform_device_id da9063_id[] = { { "da9063", (uintptr_t)(NULL) }, + { "da9062", (uintptr_t)(&da9062_device_data) }, { } }; +static struct of_device_id const da906x_dt_ids[] = { + { + .compatible = "dlg,da9063", + .data = NULL, + }, { + .compatible = "dlg,da9062", + .data = &da9062_device_data, + }, { + } +}; + static struct driver_d da9063_driver = { .name = "da9063", .probe = da9063_probe, .id_table = da9063_id, + .of_compatible = DRV_OF_COMPAT(da906x_dt_ids), }; static int da9063_init(void) -- cgit v1.2.3 From 7d7e5be85f713789481c0d74e0207086ab914e34 Mon Sep 17 00:00:00 2001 From: Enrico Scholz Date: Wed, 3 Aug 2016 16:14:56 +0200 Subject: mfd: da9063: read out and report device id just some kind of debugging atm... Signed-off-by: Enrico Scholz Tested-by: Stefan Christ Signed-off-by: Christian Hemp --- drivers/mfd/da9063.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'drivers/mfd') diff --git a/drivers/mfd/da9063.c b/drivers/mfd/da9063.c index 00a61279f3..0c27bf495f 100644 --- a/drivers/mfd/da9063.c +++ b/drivers/mfd/da9063.c @@ -45,6 +45,8 @@ struct da9063 { /* bank1: control register I */ #define DA9063_REG1_CONFIG_I 0x10e +#define DA9062AA_DEVICE_ID 0x181 + /* DA9063_REG_FAULT_LOG (addr=0x05) */ #define DA9063_TWD_ERROR 0x01 #define DA9063_POR 0x02 @@ -196,6 +198,7 @@ static void da9063_restart(struct restart_handler *rst) static int da9062_device_init(struct da9063 *priv) { int ret; + uint8_t id[4]; priv->client1 = i2c_new_dummy(priv->client->adapter, priv->client->addr + 1); @@ -206,6 +209,16 @@ static int da9062_device_init(struct da9063 *priv) return -EINVAL; } + ret = i2c_read_reg(priv->client1, DA9062AA_DEVICE_ID & 0xffu, + id, sizeof id); + if (ret < 0) { + dev_warn(priv->dev, "failed to read ID: %d\n", ret); + return ret; + } + + dev_info(priv->dev, "da9062 with id %02x.%02x.%02x.%02x detected\n", + id[0], id[1], id[2], id[3]); + /* clear CONFIG_I[WATCHDOG_SD] */ ret = da906x_reg_update(priv, DA9063_REG1_CONFIG_I, DA9062_WATCHDOG_SD, DA9062_WATCHDOG_SD); -- cgit v1.2.3 From f24883c66d7ae25655488c3c363d5bf229ac9225 Mon Sep 17 00:00:00 2001 From: Stefan Christ Date: Wed, 3 Aug 2016 16:14:58 +0200 Subject: mfd: da9063: update Kconfig for DA9062 Signed-off-by: Stefan Christ Signed-off-by: Christian Hemp --- drivers/mfd/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 417c9ce96c..521cf042ae 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -13,7 +13,7 @@ config MFD_DA9053 config MFD_DA9063 depends on I2C - bool "DA9063 PMIC driver" + bool "DA9063/DA9062 PMIC driver" config MFD_LP3972 depends on I2C -- cgit v1.2.3