summaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/aiodev/Kconfig6
-rw-r--r--drivers/aiodev/Makefile1
-rw-r--r--drivers/aiodev/mc13xxx_adc.c234
-rw-r--r--drivers/clk/imx/clk-imx6.c26
-rw-r--r--drivers/mfd/Kconfig10
-rw-r--r--drivers/mfd/Makefile1
-rw-r--r--drivers/mfd/da9063.c159
-rw-r--r--drivers/mfd/mc13xxx.c15
-rw-r--r--drivers/mfd/rave-sp.c758
-rw-r--r--drivers/net/phy/micrel.c1
-rw-r--r--drivers/net/usb/usbnet.c2
-rw-r--r--drivers/pinctrl/pinctrl-vf610.c6
-rw-r--r--drivers/serial/serial_lpuart.c4
-rw-r--r--drivers/video/fb.c3
-rw-r--r--drivers/video/imx-ipu-v3/imx-ldb.c2
-rw-r--r--drivers/video/tc358767.c1
-rw-r--r--drivers/watchdog/Kconfig5
-rw-r--r--drivers/watchdog/Makefile1
-rw-r--r--drivers/watchdog/omap_wdt.c1
-rw-r--r--drivers/watchdog/rave-sp-wdt.c426
-rw-r--r--drivers/watchdog/wd_core.c6
21 files changed, 1644 insertions, 24 deletions
diff --git a/drivers/aiodev/Kconfig b/drivers/aiodev/Kconfig
index 1c5fabe01a..8bad946920 100644
--- a/drivers/aiodev/Kconfig
+++ b/drivers/aiodev/Kconfig
@@ -20,4 +20,10 @@ config LM75
help
Support for LM75 and similar devices
+config MC13XXX_ADC
+ tristate "MC13XXX ADC driver"
+ depends on MFD_MC13XXX
+ help
+ Support for MC13783, MC13892, MC34708 ADC
+
endif
diff --git a/drivers/aiodev/Makefile b/drivers/aiodev/Makefile
index c3d2b081a9..1dcf6cdc4b 100644
--- a/drivers/aiodev/Makefile
+++ b/drivers/aiodev/Makefile
@@ -2,3 +2,4 @@
obj-$(CONFIG_AIODEV) += core.o
obj-$(CONFIG_IMX_THERMAL) += imx_thermal.o
obj-$(CONFIG_LM75) += lm75.o
+obj-$(CONFIG_MC13XXX_ADC) += mc13xxx_adc.o
diff --git a/drivers/aiodev/mc13xxx_adc.c b/drivers/aiodev/mc13xxx_adc.c
new file mode 100644
index 0000000000..4e72048169
--- /dev/null
+++ b/drivers/aiodev/mc13xxx_adc.c
@@ -0,0 +1,234 @@
+/*
+ * mc13xxx_adc
+ *
+ * Copyright (c) 2018 Zodiac Inflight Innovation
+ * Author: Andrey Gusakov <andrey.gusakov@cogentembedded.com>
+ * Based on the code of analogous driver from Linux:
+ * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright (C) 2009 Sascha Hauer, Pengutronix
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * 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 <malloc.h>
+#include <driver.h>
+#include <xfuncs.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <io.h>
+#include <aiodev.h>
+#include <mfd/mc13xxx.h>
+#include <linux/err.h>
+
+#define MC13XXX_ADC0_LICELLCON (1 << 0)
+#define MC13XXX_ADC0_CHRGICON (1 << 1)
+#define MC13XXX_ADC0_BATICON (1 << 2)
+#define MC13XXX_ADC0_BUFEN (1 << 3)
+#define MC13XXX_ADC0_ADIN7SEL_DIE (1 << 4)
+#define MC13XXX_ADC0_ADIN7SEL_UID (2 << 4)
+#define MC13XXX_ADC0_ADREFEN (1 << 10)
+#define MC13XXX_ADC0_TSMOD0 (1 << 12)
+#define MC13XXX_ADC0_TSMOD1 (1 << 13)
+#define MC13XXX_ADC0_TSMOD2 (1 << 14)
+#define MC13XXX_ADC0_ADINC1 (1 << 16)
+#define MC13XXX_ADC0_ADINC2 (1 << 17)
+
+#define MC13XXX_ADC0_TSMOD_MASK (MC13XXX_ADC0_TSMOD0 | \
+ MC13XXX_ADC0_TSMOD1 | \
+ MC13XXX_ADC0_TSMOD2)
+
+#define MC13XXX_ADC0_CONFIG_MASK (MC13XXX_ADC0_TSMOD_MASK | \
+ MC13XXX_ADC0_LICELLCON | \
+ MC13XXX_ADC0_CHRGICON | \
+ MC13XXX_ADC0_BATICON)
+
+#define MC13XXX_ADC1_ADEN (1 << 0)
+#define MC13XXX_ADC1_RAND (1 << 1)
+#define MC13XXX_ADC1_ADSEL (1 << 3)
+#define MC13XXX_ADC1_CHAN0_SHIFT 5
+#define MC13XXX_ADC1_CHAN1_SHIFT 8
+#define MC13XXX_ADC1_ASC (1 << 20)
+#define MC13XXX_ADC1_ADTRIGIGN (1 << 21)
+
+struct mc13xx_adc_data {
+ struct mc13xxx *mc_dev;
+
+ struct aiodevice aiodev;
+ struct aiochannel *aiochan;
+};
+
+static inline struct mc13xx_adc_data *
+to_mc13xx_adc_data(struct aiochannel *chan)
+{
+ return container_of(chan->aiodev, struct mc13xx_adc_data, aiodev);
+}
+
+int mc13xxx_adc_do_conversion(struct mc13xxx *mc13xxx,
+ unsigned int channel, unsigned int *sample)
+{
+ int i;
+ int timeout = 100;
+ u32 adc0, adc1, old_adc0;
+
+ mc13xxx_reg_read(mc13xxx, MC13783_REG_ADC(0), &old_adc0);
+
+ adc0 = MC13XXX_ADC0_ADINC1 | MC13XXX_ADC0_ADINC2 | MC13XXX_ADC0_BUFEN;
+ adc1 = MC13XXX_ADC1_ADEN | MC13XXX_ADC1_ADTRIGIGN | MC13XXX_ADC1_ASC;
+
+ /* Channels mapped through ADIN7:
+ * 7 - General purpose ADIN7
+ * 16 - UID
+ * 17 - Die temperature */
+ if (channel > 7 && channel < 16) {
+ adc1 |= MC13XXX_ADC1_ADSEL;
+ } else if (channel == 16) {
+ adc0 |= MC13XXX_ADC0_ADIN7SEL_UID;
+ channel = 7;
+ } else if (channel == 17) {
+ adc0 |= MC13XXX_ADC0_ADIN7SEL_DIE;
+ channel = 7;
+ }
+
+ adc0 |= old_adc0 & MC13XXX_ADC0_CONFIG_MASK;
+ adc1 |= (channel & 0x7) << MC13XXX_ADC1_CHAN0_SHIFT;
+ adc1 |= MC13XXX_ADC1_RAND;
+
+ mc13xxx_reg_write(mc13xxx, MC13783_REG_ADC(0), adc0);
+ mc13xxx_reg_write(mc13xxx, MC13783_REG_ADC(1), adc1);
+
+ /* wait for completion. ASC will set to zero */
+ do {
+ mc13xxx_reg_read(mc13xxx, MC13783_REG_ADC(1), &adc1);
+ } while ((adc1 & MC13XXX_ADC1_ASC) && (--timeout > 0));
+
+ if (timeout == 0)
+ return -ETIMEDOUT;
+
+ for (i = 0; i < 4; ++i) {
+ mc13xxx_reg_read(mc13xxx,
+ MC13783_REG_ADC(2), &sample[i]);
+ }
+
+ return 0;
+}
+
+static int mc13xx_adc_read(struct aiochannel *chan, int *val)
+{
+ int i;
+ int ret;
+ int mc_type;
+ unsigned int sample[4];
+ struct mc13xx_adc_data *mc13xxx_adc;
+ int acc = 0;
+ int index = chan->index;
+
+ mc13xxx_adc = to_mc13xx_adc_data(chan);
+ mc_type = mc13xxx_type(mc13xxx_adc->mc_dev);
+
+ /* add offset for all 8 channel devices becouse t and UID
+ * inputs are mapped to channels 16 and 17 */
+ if ((mc_type != MC13783_TYPE) && (chan->index > 7))
+ index += 8;
+
+ ret = mc13xxx_adc_do_conversion(mc13xxx_adc->mc_dev, index, sample);
+ if (ret < 0)
+ goto err;
+
+ for (i = 0; i < 4; i++) {
+ acc += (sample[i] >> 2 & 0x3ff);
+ acc += (sample[i] >> 14 & 0x3ff);
+ }
+ /* div 8 */
+ acc = acc >> 3;
+
+ if (index == 16) {
+ /* UID */
+ if (mc_type == MC13892_TYPE) {
+ /* MC13892 have 1/2 divider
+ * input range is [0, 4.800V] */
+ acc = DIV_ROUND_CLOSEST(acc * 4800, 1024);
+ } else {
+ /* MC13783 have 0.9 divider
+ *input range is [0, 2.555V] */
+ acc = DIV_ROUND_CLOSEST(acc * 2555, 1024);
+ }
+ } else if (index == 17) {
+ /* Die temperature */
+ if (mc_type == MC13892_TYPE) {
+ /* MC13892:
+ * Die Temperature Read Out Code at 25C 680
+ * Temperature change per LSB +0.4244C */
+ acc = DIV_ROUND_CLOSEST(-2635920 + acc * 4244, 10);
+ } else {
+ /* MC13783:
+ * Die Temperature Read Out Code at 25C 282
+ * Temperature change per LSB -1.14C */
+ acc = 346480 - 1140 * acc;
+ }
+ } else {
+ /* GP input
+ * input range is [0, 2.3V], value has 10 bits */
+ acc = DIV_ROUND_CLOSEST(acc * 2300, 1024);
+ }
+
+ *val = acc;
+err:
+ return ret;
+}
+
+int mc13xxx_adc_probe(struct device_d *dev, struct mc13xxx *mc_dev)
+{
+ int i;
+ int ret;
+ int chans;
+ struct mc13xx_adc_data *mc13xxx_adc;
+
+ mc13xxx_adc = xzalloc(sizeof(*mc13xxx_adc));
+
+ if (mc13xxx_type(mc_dev) == MC13783_TYPE) {
+ /* mc13783 has 16 channels */
+ chans = 16 + 2;
+ } else {
+ chans = 8 + 2;
+ }
+
+ mc13xxx_adc->mc_dev = mc_dev;
+ mc13xxx_adc->aiodev.num_channels = chans;
+ mc13xxx_adc->aiochan = xmalloc(mc13xxx_adc->aiodev.num_channels *
+ sizeof(*mc13xxx_adc->aiochan));
+ mc13xxx_adc->aiodev.hwdev = dev;
+ mc13xxx_adc->aiodev.channels =
+ xmalloc(mc13xxx_adc->aiodev.num_channels *
+ sizeof(mc13xxx_adc->aiodev.channels[0]));
+ /* all channels are voltage inputs, expect last one */
+ for (i = 0; i < chans - 1; i++) {
+ mc13xxx_adc->aiodev.channels[i] = &mc13xxx_adc->aiochan[i];
+ mc13xxx_adc->aiochan[i].unit = "mV";
+ }
+ /* temperature input */
+ mc13xxx_adc->aiodev.channels[i] = &mc13xxx_adc->aiochan[i];
+ mc13xxx_adc->aiochan[i].unit = "mC";
+
+ mc13xxx_adc->aiodev.read = mc13xx_adc_read;
+
+ ret = aiodevice_register(&mc13xxx_adc->aiodev);
+ if (!ret)
+ goto done;
+
+ dev_err(dev, "Failed to register AIODEV: %d\n", ret);
+ kfree(mc13xxx_adc);
+done:
+ return ret;
+}
diff --git a/drivers/clk/imx/clk-imx6.c b/drivers/clk/imx/clk-imx6.c
index d0571bce5e..8c3bb46a48 100644
--- a/drivers/clk/imx/clk-imx6.c
+++ b/drivers/clk/imx/clk-imx6.c
@@ -59,6 +59,11 @@
static struct clk *clks[IMX6QDL_CLK_END];
static struct clk_onecell_data clk_data;
+static inline int cpu_is_plus(void)
+{
+ return cpu_is_mx6qp() || cpu_is_mx6dp();
+}
+
static const char *step_sels[] = {
"osc",
"pll2_pfd2_396m",
@@ -109,6 +114,15 @@ static const char *enfc_sels[] = {
"pll2_pfd2_396m",
};
+static const char *enfc_sels_plus[] = {
+ "pll2_pfd0_352m",
+ "pll2_bus",
+ "pll3_usb_otg",
+ "pll2_pfd2_396m",
+ "pll3_pfd3_454m",
+ "dummy",
+};
+
static const char *eim_sels[] = {
"axi",
"pll3_usb_otg",
@@ -404,7 +418,10 @@ static int imx6_ccm_probe(struct device_d *dev)
clks[IMX6QDL_CLK_USDHC2_SEL] = imx_clk_mux("usdhc2_sel", base + 0x1c, 17, 1, usdhc_sels, ARRAY_SIZE(usdhc_sels));
clks[IMX6QDL_CLK_USDHC3_SEL] = imx_clk_mux("usdhc3_sel", base + 0x1c, 18, 1, usdhc_sels, ARRAY_SIZE(usdhc_sels));
clks[IMX6QDL_CLK_USDHC4_SEL] = imx_clk_mux("usdhc4_sel", base + 0x1c, 19, 1, usdhc_sels, ARRAY_SIZE(usdhc_sels));
- clks[IMX6QDL_CLK_ENFC_SEL] = imx_clk_mux("enfc_sel", base + 0x2c, 16, 2, enfc_sels, ARRAY_SIZE(enfc_sels));
+ if (cpu_is_plus())
+ clks[IMX6QDL_CLK_ENFC_SEL] = imx_clk_mux("enfc_sel", base + 0x2c, 16, 2, enfc_sels_plus, ARRAY_SIZE(enfc_sels_plus));
+ else
+ clks[IMX6QDL_CLK_ENFC_SEL] = imx_clk_mux("enfc_sel", base + 0x2c, 16, 2, enfc_sels, ARRAY_SIZE(enfc_sels));
clks[IMX6QDL_CLK_EIM_SEL] = imx_clk_mux("eim_sel", base + 0x1c, 27, 2, eim_sels, ARRAY_SIZE(eim_sels));
clks[IMX6QDL_CLK_EIM_SLOW_SEL] = imx_clk_mux("eim_slow_sel", base + 0x1c, 29, 2, eim_sels, ARRAY_SIZE(eim_sels));
clks[IMX6QDL_CLK_VDO_AXI_SEL] = imx_clk_mux("vdo_axi_sel", base + 0x18, 11, 1, vdo_axi_sels, ARRAY_SIZE(vdo_axi_sels));
@@ -514,6 +531,13 @@ static int imx6_ccm_probe(struct device_d *dev)
clk_set_parent(clks[IMX6QDL_CLK_LVDS1_SEL], clks[IMX6QDL_CLK_SATA_REF_100M]);
+ /*
+ * The gpmi needs 100MHz frequency in the EDO/Sync mode,
+ * We can not get the 100MHz from the pll2_pfd0_352m.
+ * So choose pll2_pfd2_396m as enfc_sel's parent.
+ */
+ clk_set_parent(clks[IMX6QDL_CLK_ENFC_SEL], clks[IMX6QDL_CLK_PLL2_PFD2_396M]);
+
return 0;
}
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 417c9ce96c..d04431fbc8 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
@@ -53,4 +53,12 @@ config MFD_TWL6030
select MFD_TWLCORE
bool "TWL6030 driver"
+config RAVE_SP_CORE
+ tristate "RAVE SP MCU core driver"
+ depends on SERIAL_DEV_BUS
+ select CRC_CCITT
+ help
+ Select this to get support for the Supervisory Processor
+ device found on several devices in RAVE line of hardware.
+
endmenu
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 041915a7c6..8b23a1023e 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -10,3 +10,4 @@ obj-$(CONFIG_MFD_SYSCON) += syscon.o
obj-$(CONFIG_MFD_TWLCORE) += twl-core.o
obj-$(CONFIG_MFD_TWL4030) += twl4030.o
obj-$(CONFIG_MFD_TWL6030) += twl6030.o
+obj-$(CONFIG_RAVE_SP_CORE) += rave-sp.o
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)
diff --git a/drivers/mfd/mc13xxx.c b/drivers/mfd/mc13xxx.c
index f6aa922425..a5877dbda1 100644
--- a/drivers/mfd/mc13xxx.c
+++ b/drivers/mfd/mc13xxx.c
@@ -40,10 +40,12 @@ struct mc13xxx {
struct spi_device *spi;
};
int revision;
+ int type;
};
struct mc13xxx_devtype {
int (*revision)(struct mc13xxx*);
+ int type;
};
#define to_mc13xxx(a) container_of(a, struct mc13xxx, cdev)
@@ -56,6 +58,12 @@ struct mc13xxx *mc13xxx_get(void)
}
EXPORT_SYMBOL(mc13xxx_get);
+int mc13xxx_type(struct mc13xxx *mc13xxx)
+{
+ return mc13xxx->type;
+}
+EXPORT_SYMBOL(mc13xxx_type);
+
int mc13xxx_revision(struct mc13xxx *mc13xxx)
{
return mc13xxx->revision;
@@ -347,6 +355,7 @@ static int __init mc13xxx_probe(struct device_d *dev)
}
mc_dev->revision = rev;
+ mc_dev->type = devtype->type;
ret = regmap_register_cdev(mc_dev->map, NULL);
if (ret)
@@ -355,19 +364,25 @@ static int __init mc13xxx_probe(struct device_d *dev)
if (mc13xxx_init_callback)
mc13xxx_init_callback(mc_dev);
+ if (of_property_read_bool(dev->device_node, "fsl,mc13xxx-uses-adc"))
+ mc13xxx_adc_probe(dev, mc_dev);
+
return 0;
}
static struct mc13xxx_devtype mc13783_devtype = {
.revision = mc13783_revision,
+ .type = MC13783_TYPE,
};
static struct mc13xxx_devtype mc13892_devtype = {
.revision = mc13892_revision,
+ .type = MC13892_TYPE,
};
static struct mc13xxx_devtype mc34708_devtype = {
.revision = mc34708_revision,
+ .type = MC34708_TYPE,
};
static struct platform_device_id mc13xxx_ids[] = {
diff --git a/drivers/mfd/rave-sp.c b/drivers/mfd/rave-sp.c
new file mode 100644
index 0000000000..d55e913ff7
--- /dev/null
+++ b/drivers/mfd/rave-sp.c
@@ -0,0 +1,758 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+/*
+ * Multifunction core driver for Zodiac Inflight Innovations RAVE
+ * Supervisory Processor(SP) MCU that is connected via dedicated UART
+ * port
+ *
+ * Copyright (C) 2017 Zodiac Inflight Innovations
+ */
+
+#include <common.h>
+#include <init.h>
+#include <of_device.h>
+
+#include <asm/unaligned.h>
+
+#include <linux/crc-ccitt.h>
+#include <linux/mfd/rave-sp.h>
+
+#define DUMP_PREFIX_NONE 0
+
+/*
+ * UART protocol using following entities:
+ * - message to MCU => ACK response
+ * - event from MCU => event ACK
+ *
+ * Frame structure:
+ * <STX> <DATA> <CHECKSUM> <ETX>
+ * Where:
+ * - STX - is start of transmission character
+ * - ETX - end of transmission
+ * - DATA - payload
+ * - CHECKSUM - checksum calculated on <DATA>
+ *
+ * If <DATA> or <CHECKSUM> contain one of control characters, then it is
+ * escaped using <DLE> control code. Added <DLE> does not participate in
+ * checksum calculation.
+ */
+#define RAVE_SP_STX 0x02
+#define RAVE_SP_ETX 0x03
+#define RAVE_SP_DLE 0x10
+
+#define RAVE_SP_MAX_DATA_SIZE 64
+#define RAVE_SP_CHECKSUM_SIZE 2 /* Worst case scenario on RDU2 */
+/*
+ * We don't store STX, ETX and unescaped bytes, so Rx is only
+ * DATA + CSUM
+ */
+#define RAVE_SP_RX_BUFFER_SIZE \
+ (RAVE_SP_MAX_DATA_SIZE + RAVE_SP_CHECKSUM_SIZE)
+
+#define RAVE_SP_STX_ETX_SIZE 2
+/*
+ * For Tx we have to have space for everything, STX, EXT and
+ * potentially stuffed DATA + CSUM data + csum
+ */
+#define RAVE_SP_TX_BUFFER_SIZE \
+ (RAVE_SP_STX_ETX_SIZE + 2 * RAVE_SP_RX_BUFFER_SIZE)
+
+#define RAVE_SP_BOOT_SOURCE_GET 0
+#define RAVE_SP_BOOT_SOURCE_SET 1
+
+#define RAVE_SP_RDU2_BOARD_TYPE_RMB 0
+#define RAVE_SP_RDU2_BOARD_TYPE_DEB 1
+
+#define RAVE_SP_BOOT_SOURCE_SD 0
+#define RAVE_SP_BOOT_SOURCE_EMMC 1
+#define RAVE_SP_BOOT_SOURCE_NOR 2
+
+/**
+ * enum rave_sp_deframer_state - Possible state for de-framer
+ *
+ * @RAVE_SP_EXPECT_SOF: Scanning input for start-of-frame marker
+ * @RAVE_SP_EXPECT_DATA: Got start of frame marker, collecting frame
+ * @RAVE_SP_EXPECT_ESCAPED_DATA: Got escape character, collecting escaped byte
+ */
+enum rave_sp_deframer_state {
+ RAVE_SP_EXPECT_SOF,
+ RAVE_SP_EXPECT_DATA,
+ RAVE_SP_EXPECT_ESCAPED_DATA,
+};
+
+/**
+ * struct rave_sp_deframer - Device protocol deframer
+ *
+ * @state: Current state of the deframer
+ * @data: Buffer used to collect deframed data
+ * @length: Number of bytes de-framed so far
+ */
+struct rave_sp_deframer {
+ enum rave_sp_deframer_state state;
+ unsigned char data[RAVE_SP_RX_BUFFER_SIZE];
+ size_t length;
+};
+
+/**
+ * struct rave_sp_reply - Reply as per RAVE device protocol
+ *
+ * @length: Expected reply length
+ * @data: Buffer to store reply payload in
+ * @code: Expected reply code
+ * @ackid: Expected reply ACK ID
+ * @completion: Successful reply reception completion
+ */
+struct rave_sp_reply {
+ size_t length;
+ void *data;
+ u8 code;
+ u8 ackid;
+
+ bool received;
+};
+
+/**
+ * struct rave_sp_checksum - Variant specific checksum implementation details
+ *
+ * @length: Caculated checksum length
+ * @subroutine: Utilized checksum algorithm implementation
+ */
+struct rave_sp_checksum {
+ size_t length;
+ void (*subroutine)(const u8 *, size_t, u8 *);
+};
+
+/**
+ * struct rave_sp_variant_cmds - Variant specific command routines
+ *
+ * @translate: Generic to variant specific command mapping routine
+ */
+struct rave_sp_variant_cmds {
+ int (*translate)(enum rave_sp_command);
+};
+
+/**
+ * struct rave_sp_variant - RAVE supervisory processor core variant
+ *
+ * @checksum: Variant specific checksum implementation
+ * @cmd: Variant specific command pointer table
+ *
+ */
+struct rave_sp_variant {
+ const struct rave_sp_checksum *checksum;
+ struct rave_sp_variant_cmds cmd;
+};
+
+/**
+ * struct rave_sp - RAVE supervisory processor core
+ *
+ * @serdev: Pointer to underlying serdev
+ * @deframer: Stored state of the protocol deframer
+ * @ackid: ACK ID used in last reply sent to the device
+ * @bus_lock: Lock to serialize access to the device
+ * @reply_lock: Lock protecting @reply
+ * @reply: Pointer to memory to store reply payload
+ *
+ * @variant: Device variant specific information
+ * @event_notifier_list: Input event notification chain
+ *
+ * @part_number_firmware: Firmware version
+ * @part_number_bootloader: Bootloader version
+ */
+struct rave_sp {
+ struct serdev_device *serdev;
+ struct rave_sp_deframer deframer;
+ unsigned int ackid;
+ struct rave_sp_reply *reply;
+
+ const struct rave_sp_variant *variant;
+
+ const char *part_number_firmware;
+ const char *part_number_bootloader;
+};
+
+struct rave_sp_version {
+ u8 hardware;
+ __le16 major;
+ u8 minor;
+ u8 letter[2];
+} __packed;
+
+struct rave_sp_status {
+ struct rave_sp_version bootloader_version;
+ struct rave_sp_version firmware_version;
+ u16 rdu_eeprom_flag;
+ u16 dds_eeprom_flag;
+ u8 pic_flag;
+ u8 orientation;
+ u32 etc;
+ s16 temp[2];
+ u8 backlight_current[3];
+ u8 dip_switch;
+ u8 host_interrupt;
+ u16 voltage_28;
+ u8 i2c_device_status;
+ u8 power_status;
+ u8 general_status;
+#define RAVE_SP_STATUS_GS_FIRMWARE_MODE BIT(1)
+
+ u8 deprecated1;
+ u8 power_led_status;
+ u8 deprecated2;
+ u8 periph_power_shutoff;
+} __packed;
+
+static bool rave_sp_id_is_event(u8 code)
+{
+ return (code & 0xF0) == RAVE_SP_EVNT_BASE;
+}
+
+static void csum_8b2c(const u8 *buf, size_t size, u8 *crc)
+{
+ *crc = *buf++;
+ size--;
+
+ while (size--)
+ *crc += *buf++;
+
+ *crc = 1 + ~(*crc);
+}
+
+static void csum_ccitt(const u8 *buf, size_t size, u8 *crc)
+{
+ const u16 calculated = crc_ccitt_false(0xffff, buf, size);
+
+ /*
+ * While the rest of the wire protocol is little-endian,
+ * CCITT-16 CRC in RDU2 device is sent out in big-endian order.
+ */
+ put_unaligned_be16(calculated, crc);
+}
+
+static void *stuff(unsigned char *dest, const unsigned char *src, size_t n)
+{
+ while (n--) {
+ const unsigned char byte = *src++;
+
+ switch (byte) {
+ case RAVE_SP_STX:
+ case RAVE_SP_ETX:
+ case RAVE_SP_DLE:
+ *dest++ = RAVE_SP_DLE;
+ /* FALLTHROUGH */
+ default:
+ *dest++ = byte;
+ }
+ }
+
+ return dest;
+}
+
+static int rave_sp_write(struct rave_sp *sp, const u8 *data, u8 data_size)
+{
+ const size_t checksum_length = sp->variant->checksum->length;
+ unsigned char frame[RAVE_SP_TX_BUFFER_SIZE];
+ unsigned char crc[RAVE_SP_CHECKSUM_SIZE];
+ unsigned char *dest = frame;
+ size_t length;
+
+ if (WARN_ON(checksum_length > sizeof(crc)))
+ return -ENOMEM;
+
+ if (WARN_ON(data_size > sizeof(frame)))
+ return -ENOMEM;
+
+ sp->variant->checksum->subroutine(data, data_size, crc);
+
+ *dest++ = RAVE_SP_STX;
+ dest = stuff(dest, data, data_size);
+ dest = stuff(dest, crc, checksum_length);
+ *dest++ = RAVE_SP_ETX;
+
+ length = dest - frame;
+
+ if (IS_ENABLED(DEBUG))
+ print_hex_dump(0, "rave-sp tx: ", DUMP_PREFIX_NONE,
+ 16, 1, frame, length, false);
+
+ return serdev_device_write(sp->serdev, frame, length, SECOND);
+}
+
+static u8 rave_sp_reply_code(u8 command)
+{
+ /*
+ * There isn't a single rule that describes command code ->
+ * ACK code transformation, but, going through various
+ * versions of ICDs, there appear to be three distinct groups
+ * that can be described by simple transformation.
+ */
+ switch (command) {
+ case 0xA0 ... 0xBE:
+ /*
+ * Commands implemented by firmware found in RDU1 and
+ * older devices all seem to obey the following rule
+ */
+ return command + 0x20;
+ case 0xE0 ... 0xEF:
+ /*
+ * Events emitted by all versions of the firmare use
+ * least significant bit to get an ACK code
+ */
+ return command | 0x01;
+ default:
+ /*
+ * Commands implemented by firmware found in RDU2 are
+ * similar to "old" commands, but they use slightly
+ * different offset
+ */
+ return command + 0x40;
+ }
+}
+
+int rave_sp_exec(struct rave_sp *sp,
+ void *__data, size_t data_size,
+ void *reply_data, size_t reply_data_size)
+{
+ struct device_d *dev = sp->serdev->dev;
+ struct rave_sp_reply reply = {
+ .data = reply_data,
+ .length = reply_data_size,
+ .received = false,
+ };
+ unsigned char *data = __data;
+ int command, ret = 0;
+ u8 ackid;
+ uint64_t start = get_time_ns();
+
+ command = sp->variant->cmd.translate(data[0]);
+ if (command < 0)
+ return command;
+
+ ackid = sp->ackid++;
+ reply.ackid = ackid;
+ reply.code = rave_sp_reply_code((u8)command),
+
+ sp->reply = &reply;
+
+ data[0] = command;
+ data[1] = ackid;
+
+ rave_sp_write(sp, data, data_size);
+ /*
+ * is_timeout will implicitly poll serdev via poller
+ * infrastructure
+ */
+ while (!is_timeout(start, SECOND) && !reply.received)
+ ;
+
+ if (!reply.received) {
+ dev_err(dev, "Command timeout\n");
+ ret = -ETIMEDOUT;
+ sp->reply = NULL;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(rave_sp_exec);
+
+static void rave_sp_receive_event(struct rave_sp *sp,
+ const unsigned char *data, size_t length)
+{
+ u8 cmd[] = {
+ [0] = rave_sp_reply_code(data[0]),
+ [1] = data[1],
+ };
+
+ /*
+ * The only thing we do for event, in case we get one, is to
+ * acknowledge it to prevent RAVE SP from spamming us
+ */
+ rave_sp_write(sp, cmd, sizeof(cmd));
+}
+
+static void rave_sp_receive_reply(struct rave_sp *sp,
+ const unsigned char *data, size_t length)
+{
+ struct device_d *dev = sp->serdev->dev;
+ struct rave_sp_reply *reply;
+ const size_t payload_length = length - 2;
+
+ reply = sp->reply;
+
+ if (reply) {
+ if (reply->code == data[0] && reply->ackid == data[1] &&
+ payload_length >= reply->length) {
+ /*
+ * We are relying on memcpy(dst, src, 0) to be a no-op
+ * when handling commands that have a no-payload reply
+ */
+ memcpy(reply->data, &data[2], reply->length);
+ reply->received = true;
+ sp->reply = NULL;
+ } else {
+ dev_err(dev, "Ignoring incorrect reply\n");
+ dev_dbg(dev, "Code: expected = 0x%08x received = 0x%08x\n",
+ reply->code, data[0]);
+ dev_dbg(dev, "ACK ID: expected = 0x%08x received = 0x%08x\n",
+ reply->ackid, data[1]);
+ dev_dbg(dev, "Length: expected = %zu received = %zu\n",
+ reply->length, payload_length);
+ }
+ }
+
+}
+
+static void rave_sp_receive_frame(struct rave_sp *sp,
+ const unsigned char *data,
+ size_t length)
+{
+ const size_t checksum_length = sp->variant->checksum->length;
+ const size_t payload_length = length - checksum_length;
+ const u8 *crc_reported = &data[payload_length];
+ struct device_d *dev = sp->serdev->dev;
+ u8 crc_calculated[checksum_length];
+
+ if (IS_ENABLED(DEBUG))
+ print_hex_dump(0, "rave-sp rx: ", DUMP_PREFIX_NONE,
+ 16, 1, data, length, false);
+
+ if (unlikely(length <= checksum_length)) {
+ dev_warn(dev, "Dropping short frame\n");
+ return;
+ }
+
+ sp->variant->checksum->subroutine(data, payload_length,
+ crc_calculated);
+
+ if (memcmp(crc_calculated, crc_reported, checksum_length)) {
+ dev_warn(dev, "Dropping bad frame\n");
+ return;
+ }
+
+ if (rave_sp_id_is_event(data[0]))
+ rave_sp_receive_event(sp, data, length);
+ else
+ rave_sp_receive_reply(sp, data, length);
+}
+
+static int rave_sp_receive_buf(struct serdev_device *serdev,
+ const unsigned char *buf, size_t size)
+{
+ struct device_d *dev = serdev->dev;
+ struct rave_sp *sp = dev->priv;
+ struct rave_sp_deframer *deframer = &sp->deframer;
+ const unsigned char *src = buf;
+ const unsigned char *end = buf + size;
+
+ while (src < end) {
+ const unsigned char byte = *src++;
+
+ switch (deframer->state) {
+ case RAVE_SP_EXPECT_SOF:
+ if (byte == RAVE_SP_STX)
+ deframer->state = RAVE_SP_EXPECT_DATA;
+ break;
+
+ case RAVE_SP_EXPECT_DATA:
+ /*
+ * Treat special byte values first
+ */
+ switch (byte) {
+ case RAVE_SP_ETX:
+ rave_sp_receive_frame(sp,
+ deframer->data,
+ deframer->length);
+ /*
+ * Once we extracted a complete frame
+ * out of a stream, we call it done
+ * and proceed to bailing out while
+ * resetting the framer to initial
+ * state, regardless if we've consumed
+ * all of the stream or not.
+ */
+ goto reset_framer;
+ case RAVE_SP_STX:
+ dev_warn(dev, "Bad frame: STX before ETX\n");
+ /*
+ * If we encounter second "start of
+ * the frame" marker before seeing
+ * corresponding "end of frame", we
+ * reset the framer and ignore both:
+ * frame started by first SOF and
+ * frame started by current SOF.
+ *
+ * NOTE: The above means that only the
+ * frame started by third SOF, sent
+ * after this one will have a chance
+ * to get throught.
+ */
+ goto reset_framer;
+ case RAVE_SP_DLE:
+ deframer->state = RAVE_SP_EXPECT_ESCAPED_DATA;
+ /*
+ * If we encounter escape sequence we
+ * need to skip it and collect the
+ * byte that follows. We do it by
+ * forcing the next iteration of the
+ * encompassing while loop.
+ */
+ continue;
+ }
+ /*
+ * For the rest of the bytes, that are not
+ * speical snoflakes, we do the same thing
+ * that we do to escaped data - collect it in
+ * deframer buffer
+ */
+
+ /* FALLTHROUGH */
+
+ case RAVE_SP_EXPECT_ESCAPED_DATA:
+ if (deframer->length == sizeof(deframer->data)) {
+ dev_warn(dev, "Bad frame: Too long\n");
+ /*
+ * If the amount of data we've
+ * accumulated for current frame so
+ * far starts to exceed the capacity
+ * of deframer's buffer, there's
+ * nothing else we can do but to
+ * discard that data and start
+ * assemblying a new frame again
+ */
+ goto reset_framer;
+ }
+
+ deframer->data[deframer->length++] = byte;
+
+ /*
+ * We've extracted out special byte, now we
+ * can go back to regular data collecting
+ */
+ deframer->state = RAVE_SP_EXPECT_DATA;
+ break;
+ }
+ }
+
+ /*
+ * The only way to get out of the above loop and end up here
+ * is throught consuming all of the supplied data, so here we
+ * report that we processed it all.
+ */
+ return size;
+
+reset_framer:
+ /*
+ * NOTE: A number of codepaths that will drop us here will do
+ * so before consuming all 'size' bytes of the data passed by
+ * serdev layer. We rely on the fact that serdev layer will
+ * re-execute this handler with the remainder of the Rx bytes
+ * once we report actual number of bytes that we processed.
+ */
+ deframer->state = RAVE_SP_EXPECT_SOF;
+ deframer->length = 0;
+
+ return src - buf;
+}
+
+static int rave_sp_rdu1_cmd_translate(enum rave_sp_command command)
+{
+ if (command >= RAVE_SP_CMD_STATUS &&
+ command <= RAVE_SP_CMD_CONTROL_EVENTS)
+ return command;
+
+ return -EINVAL;
+}
+
+static int rave_sp_rdu2_cmd_translate(enum rave_sp_command command)
+{
+ if (command >= RAVE_SP_CMD_GET_FIRMWARE_VERSION &&
+ command <= RAVE_SP_CMD_GET_GPIO_STATE)
+ return command;
+
+ if (command == RAVE_SP_CMD_REQ_COPPER_REV) {
+ /*
+ * As per RDU2 ICD 3.4.47 CMD_GET_COPPER_REV code is
+ * different from that for RDU1 and it is set to 0x28.
+ */
+ return 0x28;
+ }
+
+ return rave_sp_rdu1_cmd_translate(command);
+}
+
+static int rave_sp_default_cmd_translate(enum rave_sp_command command)
+{
+ /*
+ * All of the following command codes were taken from "Table :
+ * Communications Protocol Message Types" in section 3.3
+ * "MESSAGE TYPES" of Rave PIC24 ICD.
+ */
+ switch (command) {
+ case RAVE_SP_CMD_GET_FIRMWARE_VERSION:
+ return 0x11;
+ case RAVE_SP_CMD_GET_BOOTLOADER_VERSION:
+ return 0x12;
+ case RAVE_SP_CMD_BOOT_SOURCE:
+ return 0x14;
+ case RAVE_SP_CMD_SW_WDT:
+ return 0x1C;
+ case RAVE_SP_CMD_RESET:
+ return 0x1E;
+ case RAVE_SP_CMD_RESET_REASON:
+ return 0x1F;
+ case RAVE_SP_CMD_BOOTLOADER:
+ return 0x2A;
+ default:
+ return -EINVAL;
+ }
+}
+
+static const char *devm_rave_sp_version(struct device_d *dev,
+ struct rave_sp_version *version)
+{
+ /*
+ * NOTE: The format string below uses %02d to display u16
+ * intentionally for the sake of backwards compatibility with
+ * legacy software.
+ */
+ return basprintf("%02d%02d%02d.%c%c\n",
+ version->hardware,
+ le16_to_cpu(version->major),
+ version->minor,
+ version->letter[0],
+ version->letter[1]);
+}
+
+static int rave_sp_get_status(struct rave_sp *sp)
+{
+ struct device_d *dev = sp->serdev->dev;
+ u8 cmd[] = {
+ [0] = RAVE_SP_CMD_STATUS,
+ [1] = 0
+ };
+ struct rave_sp_status status;
+ const char *mode;
+ int ret;
+
+ ret = rave_sp_exec(sp, cmd, sizeof(cmd), &status, sizeof(status));
+ if (ret)
+ return ret;
+
+ if (status.general_status & RAVE_SP_STATUS_GS_FIRMWARE_MODE)
+ mode = "Application";
+ else
+ mode = "Bootloader";
+
+ dev_info(dev, "Device is in %s mode\n", mode);
+
+ sp->part_number_firmware = devm_rave_sp_version(dev,
+ &status.firmware_version);
+ sp->part_number_bootloader = devm_rave_sp_version(dev,
+ &status.bootloader_version);
+ return 0;
+}
+
+static const struct rave_sp_checksum rave_sp_checksum_8b2c = {
+ .length = 1,
+ .subroutine = csum_8b2c,
+};
+
+static const struct rave_sp_checksum rave_sp_checksum_ccitt = {
+ .length = 2,
+ .subroutine = csum_ccitt,
+};
+
+static const struct rave_sp_variant rave_sp_legacy = {
+ .checksum = &rave_sp_checksum_8b2c,
+ .cmd = {
+ .translate = rave_sp_default_cmd_translate,
+ },
+};
+
+static const struct rave_sp_variant rave_sp_rdu1 = {
+ .checksum = &rave_sp_checksum_8b2c,
+ .cmd = {
+ .translate = rave_sp_rdu1_cmd_translate,
+ },
+};
+
+static const struct rave_sp_variant rave_sp_rdu2 = {
+ .checksum = &rave_sp_checksum_ccitt,
+ .cmd = {
+ .translate = rave_sp_rdu2_cmd_translate,
+ },
+};
+
+static const struct of_device_id __maybe_unused rave_sp_dt_ids[] = {
+ { .compatible = "zii,rave-sp-niu", .data = &rave_sp_legacy },
+ { .compatible = "zii,rave-sp-mezz", .data = &rave_sp_legacy },
+ { .compatible = "zii,rave-sp-esb", .data = &rave_sp_legacy },
+ { .compatible = "zii,rave-sp-rdu1", .data = &rave_sp_rdu1 },
+ { .compatible = "zii,rave-sp-rdu2", .data = &rave_sp_rdu2 },
+ { /* sentinel */ }
+};
+
+static int rave_sp_probe(struct device_d *dev)
+{
+ struct serdev_device *serdev = to_serdev_device(dev->parent);
+ struct rave_sp *sp;
+ u32 baud;
+ int ret;
+
+ if (of_property_read_u32(dev->device_node, "current-speed", &baud)) {
+ dev_err(dev,
+ "'current-speed' is not specified in device node\n");
+ return -EINVAL;
+ }
+
+ sp = xzalloc(sizeof(*sp));
+ sp->serdev = serdev;
+ dev->priv = sp;
+ serdev->dev = dev;
+ serdev->receive_buf = rave_sp_receive_buf;
+ serdev->polling_interval = 500 * MSECOND;
+ /*
+ * We have to set polling window to 200ms initially in order
+ * to avoid timing out on get_status below when coming out of
+ * power-cycle induced reset. It's adjusted right after
+ */
+ serdev->polling_window = 200 * MSECOND;
+
+ sp->variant = of_device_get_match_data(dev);
+ if (!sp->variant)
+ return -ENODEV;
+
+ ret = serdev_device_open(serdev);
+ if (ret)
+ return ret;
+
+ serdev_device_set_baudrate(serdev, baud);
+
+ ret = rave_sp_get_status(sp);
+ if (ret) {
+ dev_warn(dev, "Failed to get firmware status: %d\n", ret);
+ return ret;
+ }
+ /*
+ * 10ms is just a setting that was arrived at empirically when
+ * trying to make sure that EEPROM or MAC address access
+ * commnads to not time out.
+ */
+ serdev->polling_window = 10 * MSECOND;
+
+ /*
+ * Those strings already have a \n embedded, so there's no
+ * need to have one in format string.
+ */
+ dev_info(dev, "Firmware version: %s", sp->part_number_firmware);
+ dev_info(dev, "Bootloader version: %s", sp->part_number_bootloader);
+
+ return of_platform_populate(dev->device_node, NULL, dev);
+}
+
+static struct driver_d rave_sp_drv = {
+ .name = "rave-sp",
+ .probe = rave_sp_probe,
+ .of_compatible = DRV_OF_COMPAT(rave_sp_dt_ids),
+};
+console_platform_driver(rave_sp_drv);
diff --git a/drivers/net/phy/micrel.c b/drivers/net/phy/micrel.c
index 0ca359b0ba..fe71bfd1a6 100644
--- a/drivers/net/phy/micrel.c
+++ b/drivers/net/phy/micrel.c
@@ -293,6 +293,7 @@ static int ksz9031_read_status(struct phy_device *phydev)
if ((regval & 0xff) == 0xff) {
phy_init_hw(phydev);
phydev->link = 0;
+ phy_wait_aneg_done(phydev);
}
return 0;
diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c
index 33d900b706..60e67ff1a2 100644
--- a/drivers/net/usb/usbnet.c
+++ b/drivers/net/usb/usbnet.c
@@ -125,7 +125,7 @@ static int usbnet_recv(struct eth_device *edev)
len = dev->rx_urb_size;
- ret = usb_bulk_msg(dev->udev, dev->in, rx_buf, len, &alen, 1);
+ ret = usb_bulk_msg(dev->udev, dev->in, rx_buf, len, &alen, 100);
if (ret)
return ret;
diff --git a/drivers/pinctrl/pinctrl-vf610.c b/drivers/pinctrl/pinctrl-vf610.c
index 4234263d37..a46b0e2ca0 100644
--- a/drivers/pinctrl/pinctrl-vf610.c
+++ b/drivers/pinctrl/pinctrl-vf610.c
@@ -29,9 +29,9 @@
enum {
PINCTRL_VF610_MUX_LINE_SIZE = 20,
- PINCTRL_VF610_IBE = 1 << 0,
- PINCTRL_VF610_OBE = 1 << 1,
- PINCTRL_VF610_xBE = 0b11,
+ PINCTRL_VF610_IBE = BIT(0),
+ PINCTRL_VF610_OBE = BIT(1),
+ PINCTRL_VF610_xBE = PINCTRL_VF610_OBE | PINCTRL_VF610_IBE,
};
struct pinctrl_vf610 {
diff --git a/drivers/serial/serial_lpuart.c b/drivers/serial/serial_lpuart.c
index 09ae187ecc..f28035a326 100644
--- a/drivers/serial/serial_lpuart.c
+++ b/drivers/serial/serial_lpuart.c
@@ -170,9 +170,7 @@ static int lpuart_serial_probe(struct device_d *dev)
cdev->linux_console_name = "ttyLP";
- lpuart_setup_with_fifo(lpuart->base,
- clk_get_rate(lpuart->clk),
- 15);
+ lpuart_setup(lpuart->base, clk_get_rate(lpuart->clk));
ret = console_register(cdev);
if (!ret) {
diff --git a/drivers/video/fb.c b/drivers/video/fb.c
index 5e829e832d..c9d184d502 100644
--- a/drivers/video/fb.c
+++ b/drivers/video/fb.c
@@ -124,6 +124,9 @@ static int fb_enable_set(struct param_d *param, void *priv)
struct fb_info *info = priv;
int enable;
+ if (!info->mode)
+ return -EINVAL;
+
enable = info->p_enable;
if (enable)
diff --git a/drivers/video/imx-ipu-v3/imx-ldb.c b/drivers/video/imx-ipu-v3/imx-ldb.c
index 33dbade870..9b4524274c 100644
--- a/drivers/video/imx-ipu-v3/imx-ldb.c
+++ b/drivers/video/imx-ipu-v3/imx-ldb.c
@@ -347,7 +347,7 @@ static int imx_ldb_probe(struct device_d *dev)
return -EINVAL;
if (dual && i > 0) {
- dev_warn(dev, "dual-channel mode, ignoring second output\n");
+ dev_info(dev, "dual-channel mode, ignoring second output\n");
continue;
}
diff --git a/drivers/video/tc358767.c b/drivers/video/tc358767.c
index d4749b15c7..125e8236c1 100644
--- a/drivers/video/tc358767.c
+++ b/drivers/video/tc358767.c
@@ -1413,7 +1413,6 @@ static int tc_probe(struct device_d *dev)
tc->adapter.master_xfer = tc_aux_i2c_xfer;
tc->adapter.nr = -1; /* any free */
tc->adapter.dev.parent = dev;
- tc->adapter.dev.device_node = dev->device_node;
/* Add I2C adapter */
ret = i2c_add_numbered_adapter(&tc->adapter);
if (ret < 0) {
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 1d6b15617c..27e9f6d8b4 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -70,4 +70,9 @@ config WATCHDOG_BCM2835
help
Add support for watchdog on the Broadcom BCM283X SoCs.
+config RAVE_SP_WATCHDOG
+ bool "RAVE SP Watchdog timer"
+ depends on RAVE_SP_CORE
+ help
+ Support for the watchdog on RAVE SP device.
endif
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index 2376401213..faf06110a3 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -8,3 +8,4 @@ obj-$(CONFIG_WATCHDOG_JZ4740) += jz4740.o
obj-$(CONFIG_WATCHDOG_IMX_RESET_SOURCE) += imxwd.o
obj-$(CONFIG_WATCHDOG_ORION) += orion_wdt.o
obj-$(CONFIG_ARCH_BCM283X) += bcm2835_wdt.o
+obj-$(CONFIG_RAVE_SP_WATCHDOG) += rave-sp-wdt.o
diff --git a/drivers/watchdog/omap_wdt.c b/drivers/watchdog/omap_wdt.c
index 27fdfd13a0..c8a7ccf607 100644
--- a/drivers/watchdog/omap_wdt.c
+++ b/drivers/watchdog/omap_wdt.c
@@ -160,6 +160,7 @@ static int omap_wdt_probe(struct device_d *dev)
wdev = xzalloc(sizeof(*wdev));
wdev->wdog.set_timeout = omap_wdt_set_timeout;
+ wdev->wdog.hwdev = dev;
wdev->wdt_trgr_pattern = 0x1234;
/* reserve static register mappings */
diff --git a/drivers/watchdog/rave-sp-wdt.c b/drivers/watchdog/rave-sp-wdt.c
new file mode 100644
index 0000000000..1643167851
--- /dev/null
+++ b/drivers/watchdog/rave-sp-wdt.c
@@ -0,0 +1,426 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+/*
+ * Driver for watchdog aspect of for Zodiac Inflight Innovations RAVE
+ * Supervisory Processor(SP) MCU
+ *
+ * Copyright (C) 2018 Zodiac Inflight Innovation
+ *
+ */
+
+#include <common.h>
+#include <init.h>
+#include <of_device.h>
+#include <restart.h>
+#include <watchdog.h>
+
+#include <debug_ll.h>
+
+#include <linux/mfd/rave-sp.h>
+#include <linux/nvmem-consumer.h>
+
+enum {
+ RAVE_SP_RESET_BYTE = 1,
+ RAVE_SP_RESET_REASON_NORMAL = 0,
+ RAVE_SP_RESET_REASON_HW_WATCHDOG = 1,
+ RAVE_SP_RESET_REASON_SW_WATCHDOG = 2,
+ RAVE_SP_RESET_REASON_VOLTAGE = 3,
+ RAVE_SP_RESET_REASON_HOST_REQUEST = 4,
+ RAVE_SP_RESET_REASON_TEMPERATURE = 5,
+ RAVE_SP_RESET_REASON_BUTTON_PRESS = 6,
+ RAVE_SP_RESET_REASON_PIC_CM = 7,
+ RAVE_SP_RESET_REASON_PIC_ILL_INST = 8,
+ RAVE_SP_RESET_REASON_PIC_TRAP = 9,
+ RAVE_SP_RESET_REASON_UKNOWN_REASON = 10,
+ RAVE_SP_RESET_REASON_THERMAL_SENSOR = 11,
+ RAVE_SP_RESET_REASON_SW_VOLTAGE = 12,
+ RAVE_SP_RESET_REASON_CP_REQUEST = 13,
+
+ RAVE_SP_RESET_DELAY_MS = 500,
+
+ RAVE_SP_BOOT_SOURCE_GET = 0,
+ RAVE_SP_BOOT_SOURCE_SET = 1,
+};
+
+/**
+ * struct rave_sp_wdt_variant - RAVE SP watchdog variant
+ *
+ * @max_timeout: Largest possible watchdog timeout setting
+ * @min_timeout: Smallest possible watchdog timeout setting
+ *
+ * @configure: Function to send configuration command
+ * @restart: Function to send "restart" command
+ */
+struct rave_sp_wdt_variant {
+ unsigned int max_timeout;
+ unsigned int min_timeout;
+
+ int (*configure)(struct watchdog *, bool);
+ int (*restart)(struct watchdog *);
+ int (*reset_reason)(struct watchdog *);
+};
+
+/**
+ * struct rave_sp_wdt - RAVE SP watchdog
+ *
+ * @wdd: Underlying watchdog device
+ * @sp: Pointer to parent RAVE SP device
+ * @variant: Device specific variant information
+ * @reboot_notifier: Reboot notifier implementing machine reset
+ */
+struct rave_sp_wdt {
+ struct watchdog wdd;
+ struct rave_sp *sp;
+ const struct rave_sp_wdt_variant *variant;
+ struct restart_handler restart;
+ unsigned int timeout;
+ unsigned int boot_source;
+ struct device_d dev;
+};
+
+static struct rave_sp_wdt *to_rave_sp_wdt(struct watchdog *wdd)
+{
+ return container_of(wdd, struct rave_sp_wdt, wdd);
+}
+
+static int rave_sp_wdt_exec(struct watchdog *wdd, void *data,
+ size_t data_size)
+{
+ return rave_sp_exec(to_rave_sp_wdt(wdd)->sp,
+ data, data_size, NULL, 0);
+}
+
+
+static int rave_sp_wdt_access_boot_source(struct rave_sp_wdt *sp_wd, u8 set_get)
+{
+ u8 cmd[] = {
+ [0] = RAVE_SP_CMD_BOOT_SOURCE,
+ [1] = 0,
+ [2] = set_get,
+ [3] = sp_wd->boot_source,
+ };
+ u8 response;
+ int ret;
+
+ ret = rave_sp_exec(sp_wd->sp, cmd, sizeof(cmd), &response,
+ sizeof(response));
+ if (ret)
+ return ret;
+
+ return response;
+}
+
+static int __rave_sp_wdt_rdu_reset_reason(struct watchdog *wdd,
+ uint8_t response[],
+ size_t response_len)
+{
+ u8 cmd[] = {
+ [0] = RAVE_SP_CMD_RESET_REASON,
+ [1] = 0,
+ };
+ int ret;
+
+ ret = rave_sp_exec(to_rave_sp_wdt(wdd)->sp, cmd, sizeof(cmd),
+ response, response_len);
+ if (ret)
+ return ret;
+ /*
+ * Non "legacy" watchdog variants return 2 bytes as response
+ * whereas "legacy" ones return only one. Both however send
+ * the data we need as a first byte of the response.
+ */
+ return response[0];
+}
+
+static int rave_sp_wdt_rdu_reset_reason(struct watchdog *wdd)
+{
+ u8 response[2];
+ return __rave_sp_wdt_rdu_reset_reason(wdd, response, sizeof(response));
+}
+
+static int rave_sp_wdt_legacy_reset_reason(struct watchdog *wdd)
+{
+ u8 response[1];
+ return __rave_sp_wdt_rdu_reset_reason(wdd, response, sizeof(response));
+}
+
+static int rave_sp_wdt_legacy_configure(struct watchdog *wdd, bool on)
+{
+ struct rave_sp_wdt *sp_wd = to_rave_sp_wdt(wdd);
+
+ u8 cmd[] = {
+ [0] = RAVE_SP_CMD_SW_WDT,
+ [1] = 0,
+ [2] = 0,
+ [3] = on,
+ [4] = on ? sp_wd->timeout : 0,
+ };
+
+ return rave_sp_wdt_exec(wdd, cmd, sizeof(cmd));
+}
+
+static int rave_sp_wdt_rdu_configure(struct watchdog *wdd, bool on)
+{
+ struct rave_sp_wdt *sp_wd = to_rave_sp_wdt(wdd);
+
+ u8 cmd[] = {
+ [0] = RAVE_SP_CMD_SW_WDT,
+ [1] = 0,
+ [2] = on,
+ [3] = sp_wd->timeout,
+ [4] = (u8)(sp_wd->timeout >> 8),
+ };
+
+ return rave_sp_wdt_exec(wdd, cmd, sizeof(cmd));
+}
+
+static int rave_sp_wdt_configure(struct watchdog *wdd, bool on)
+{
+ return to_rave_sp_wdt(wdd)->variant->configure(wdd, on);
+}
+
+static int rave_sp_wdt_legacy_restart(struct watchdog *wdd)
+{
+ u8 cmd[] = {
+ [0] = RAVE_SP_CMD_RESET,
+ [1] = 0,
+ [2] = RAVE_SP_RESET_BYTE
+ };
+
+ return rave_sp_wdt_exec(wdd, cmd, sizeof(cmd));
+}
+
+static int rave_sp_wdt_rdu_restart(struct watchdog *wdd)
+{
+ u8 cmd[] = {
+ [0] = RAVE_SP_CMD_RESET,
+ [1] = 0,
+ [2] = RAVE_SP_RESET_BYTE,
+ [3] = RAVE_SP_RESET_REASON_NORMAL
+ };
+
+ return rave_sp_wdt_exec(wdd, cmd, sizeof(cmd));
+}
+
+static void __noreturn rave_sp_wdt_restart_handler(struct restart_handler *rst)
+{
+ struct rave_sp_wdt *sp_wd =
+ container_of(rst, struct rave_sp_wdt, restart);
+
+ const int ret = sp_wd->variant->restart(&sp_wd->wdd);
+
+ if (ret < 0)
+ dev_err(&sp_wd->wdd.dev,
+ "Failed to issue restart command (%d)", ret);
+ /*
+ * The actual work was done by reboot notifier above. SP
+ * firmware waits 500 ms before issuing reset, so let's hang
+ * here for twice that delay and hopefuly we'd never reach
+ * the return statement.
+ */
+ mdelay(2 * RAVE_SP_RESET_DELAY_MS);
+ hang();
+}
+
+static int rave_sp_wdt_start(struct watchdog *wdd)
+{
+ return rave_sp_wdt_configure(wdd, true);
+}
+
+static int rave_sp_wdt_stop(struct watchdog *wdd)
+{
+ return rave_sp_wdt_configure(wdd, false);
+}
+
+static int rave_sp_wdt_set_timeout(struct watchdog *wdd,
+ unsigned int timeout)
+{
+ struct rave_sp_wdt *sp_wd = to_rave_sp_wdt(wdd);
+
+ if (!timeout)
+ return rave_sp_wdt_stop(wdd);
+
+ if (timeout < sp_wd->variant->min_timeout ||
+ timeout > sp_wd->variant->max_timeout)
+ return -EINVAL;
+
+ sp_wd->timeout = timeout;
+ return rave_sp_wdt_start(wdd);
+}
+
+static const struct rave_sp_wdt_variant rave_sp_wdt_legacy = {
+ .max_timeout = 255,
+ .min_timeout = 1,
+ .configure = rave_sp_wdt_legacy_configure,
+ .restart = rave_sp_wdt_legacy_restart,
+ .reset_reason = rave_sp_wdt_legacy_reset_reason,
+};
+
+static const struct rave_sp_wdt_variant rave_sp_wdt_rdu = {
+ .max_timeout = 180,
+ .min_timeout = 60,
+ .configure = rave_sp_wdt_rdu_configure,
+ .restart = rave_sp_wdt_rdu_restart,
+ .reset_reason = rave_sp_wdt_rdu_reset_reason,
+};
+
+static const struct of_device_id rave_sp_wdt_of_match[] = {
+ {
+ .compatible = "zii,rave-sp-watchdog-legacy",
+ .data = &rave_sp_wdt_legacy,
+ },
+ {
+ .compatible = "zii,rave-sp-watchdog",
+ .data = &rave_sp_wdt_rdu,
+ },
+ { /* sentinel */ }
+};
+
+static int rave_sp_wdt_set_boot_source(struct param_d *param, void *priv)
+{
+ int ret;
+
+ ret = rave_sp_wdt_access_boot_source(priv, RAVE_SP_BOOT_SOURCE_SET);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int rave_sp_wdt_get_boot_source(struct param_d *param, void *priv)
+{
+ struct rave_sp_wdt *sp_wd = priv;
+ int ret;
+
+ ret = rave_sp_wdt_access_boot_source(sp_wd, RAVE_SP_BOOT_SOURCE_GET);
+ if (ret < 0)
+ return ret;
+
+ sp_wd->boot_source = ret;
+ return 0;
+}
+
+static int rave_sp_wdt_probe(struct device_d *dev)
+{
+ struct rave_sp_wdt *sp_wd;
+ const char *reset_reason;
+ struct nvmem_cell *cell;
+ struct watchdog *wdd;
+ __le16 timeout = 60;
+ struct param_d *p;
+ int ret;
+
+ sp_wd = xzalloc(sizeof(*sp_wd));
+ sp_wd->variant = of_device_get_match_data(dev);
+ sp_wd->sp = dev->parent->priv;
+
+ cell = nvmem_cell_get(dev, "wdt-timeout");
+ if (!IS_ERR(cell)) {
+ size_t len;
+ void *value = nvmem_cell_read(cell, &len);
+
+ if (!IS_ERR(value)) {
+ memcpy(&timeout, value, min(len, sizeof(timeout)));
+ kfree(value);
+ }
+ nvmem_cell_put(cell);
+ }
+ sp_wd->timeout = le16_to_cpu(timeout);
+
+ wdd = &sp_wd->wdd;
+ wdd->hwdev = dev;
+ wdd->set_timeout = rave_sp_wdt_set_timeout;
+
+ ret = rave_sp_wdt_stop(wdd);
+ if (ret) {
+ dev_err(dev, "Failed to stop watchdog device\n");
+ return ret;
+ }
+
+ ret = watchdog_register(wdd);
+ if (ret) {
+ dev_err(dev, "Failed to register watchdog device\n");
+
+ return ret;
+ }
+
+ sp_wd->restart.name = "rave-sp-wdt";
+ sp_wd->restart.restart = rave_sp_wdt_restart_handler;
+ sp_wd->restart.priority = 200;
+
+ ret = restart_handler_register(&sp_wd->restart);
+ if (ret)
+ dev_warn(dev, "Cannot register restart handler\n");
+
+
+ p = dev_add_param_int(&wdd->dev, "boot_source",
+ rave_sp_wdt_set_boot_source,
+ rave_sp_wdt_get_boot_source,
+ &sp_wd->boot_source, "%u", sp_wd);
+ if (IS_ERR(p)) {
+ unregister_device(&sp_wd->dev);
+ return PTR_ERR(p);
+ }
+
+ ret = sp_wd->variant->reset_reason(wdd);
+ if (ret < 0) {
+ dev_warn(dev, "Failed to query reset reason\n");
+ return 0;
+ }
+
+ switch (ret) {
+ case RAVE_SP_RESET_REASON_NORMAL:
+ reset_reason = "Normal poweroff";
+ break;
+ case RAVE_SP_RESET_REASON_HW_WATCHDOG:
+ reset_reason = "PIC hardware watchdog";
+ break;
+ case RAVE_SP_RESET_REASON_SW_WATCHDOG:
+ reset_reason = "PIC software watchdog";
+ break;
+ case RAVE_SP_RESET_REASON_VOLTAGE:
+ reset_reason = "Input voltage out of range";
+ break;
+ case RAVE_SP_RESET_REASON_HOST_REQUEST:
+ reset_reason = "Host requested";
+ break;
+ case RAVE_SP_RESET_REASON_TEMPERATURE:
+ reset_reason = "Temperature out of range";
+ break;
+ case RAVE_SP_RESET_REASON_BUTTON_PRESS:
+ reset_reason = "User requested";
+ break;
+ case RAVE_SP_RESET_REASON_PIC_CM:
+ reset_reason = "Illegal configuration word";
+ break;
+ case RAVE_SP_RESET_REASON_PIC_ILL_INST:
+ reset_reason = "Illegal instruction";
+ break;
+ case RAVE_SP_RESET_REASON_PIC_TRAP:
+ reset_reason = "Illegal trap";
+ break;
+ default:
+ case RAVE_SP_RESET_REASON_UKNOWN_REASON:
+ reset_reason = "Unknown";
+ break;
+ case RAVE_SP_RESET_REASON_THERMAL_SENSOR:
+ reset_reason = "Thermal sensor";
+ break;
+ case RAVE_SP_RESET_REASON_SW_VOLTAGE:
+ reset_reason = "Software detected brownout";
+ break;
+ case RAVE_SP_RESET_REASON_CP_REQUEST:
+ reset_reason = "Command request";
+ break;
+ }
+
+ dev_info(dev, "Reset reason: %s\n", reset_reason);
+ return 0;
+}
+
+static struct driver_d rave_sp_wdt_driver = {
+ .name = "rave-sp-wdt",
+ .probe = rave_sp_wdt_probe,
+ .of_compatible = DRV_OF_COMPAT(rave_sp_wdt_of_match),
+};
+console_platform_driver(rave_sp_wdt_driver);
diff --git a/drivers/watchdog/wd_core.c b/drivers/watchdog/wd_core.c
index d3305443d0..56e8626cb1 100644
--- a/drivers/watchdog/wd_core.c
+++ b/drivers/watchdog/wd_core.c
@@ -112,10 +112,12 @@ static int watchdog_register_dev(struct watchdog *wd, const char *name, int id)
int watchdog_register(struct watchdog *wd)
{
struct param_d *p;
- const char *alias;
+ const char *alias = NULL;
int ret = 0;
- alias = of_alias_get(wd->hwdev->device_node);
+ if (wd->hwdev)
+ alias = of_alias_get(wd->hwdev->device_node);
+
if (alias)
ret = watchdog_register_dev(wd, alias, DEVICE_ID_SINGLE);