diff options
-rw-r--r-- | arch/arm/dts/imx6qdl-phytec-pfla02.dtsi | 6 | ||||
-rw-r--r-- | arch/arm/dts/imx6qdl-phytec-phycore-som.dtsi | 10 | ||||
-rw-r--r-- | drivers/mfd/Kconfig | 2 | ||||
-rw-r--r-- | drivers/mfd/da9063.c | 159 |
4 files changed, 165 insertions, 12 deletions
diff --git a/arch/arm/dts/imx6qdl-phytec-pfla02.dtsi b/arch/arm/dts/imx6qdl-phytec-pfla02.dtsi index 862d856d00..8bb9ec8db7 100644 --- a/arch/arm/dts/imx6qdl-phytec-pfla02.dtsi +++ b/arch/arm/dts/imx6qdl-phytec-pfla02.dtsi @@ -190,4 +190,10 @@ pagesize = <32>; reg = <0x50>; }; + + pmic@58 { + watchdog-priority = <500>; + restart-priority = <500>; + reset-source-priority = <500>; + }; }; diff --git a/arch/arm/dts/imx6qdl-phytec-phycore-som.dtsi b/arch/arm/dts/imx6qdl-phytec-phycore-som.dtsi index 15fed812b0..ec14415398 100644 --- a/arch/arm/dts/imx6qdl-phytec-phycore-som.dtsi +++ b/arch/arm/dts/imx6qdl-phytec-phycore-som.dtsi @@ -156,6 +156,16 @@ compatible = "24c32"; reg = <0x50>; }; + + pmic@58 { + compatible = "dlg,da9062"; + reg = <0x58>; + status = "okay"; + + watchdog-priority = <500>; + restart-priority = <500>; + reset-source-priority = <500>; + }; }; &iomuxc { 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 diff --git a/drivers/mfd/da9063.c b/drivers/mfd/da9063.c index ac303611c0..0c27bf495f 100644 --- a/drivers/mfd/da9063.c +++ b/drivers/mfd/da9063.c @@ -27,14 +27,26 @@ 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; }; +/* 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 #define DA9063_REG_CONTROL_F 0x13 +/* 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 @@ -44,21 +56,89 @@ struct da9063 { #define DA9063_TWDSCALE_MASK 0x07 /* DA9063_REG_CONTROL_F (addr=0x13) */ +#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); +}; + +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 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 + * 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; + 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); struct device_d *dev = priv->dev; unsigned int scale = 0; int ret; - u8 val; 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++; @@ -67,18 +147,15 @@ 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); + ret = da906x_reg_update(priv, DA9063_REG_CONTROL_D, + DA9063_TWDSCALE_MASK, scale); 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; + priv->timeout = timeout; - return 0; +out: + return da9063_watchdog_ping(priv); } static void da9063_detect_reset_source(struct da9063 *priv) @@ -118,18 +195,65 @@ 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; + uint8_t id[4]; + + 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; + } + + 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); + + 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; + 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; priv->wd.hwdev = dev; + priv->timeout = DA9063_INITIAL_TIMEOUT; 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; @@ -151,14 +275,27 @@ on_error: } static struct platform_device_id da9063_id[] = { - { "da9063", }, + { "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) |