summaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/ata/sata_mv.c111
-rw-r--r--drivers/base/driver.c30
-rw-r--r--drivers/block/efi-block-io.c42
-rw-r--r--drivers/clk/Makefile1
-rw-r--r--drivers/clk/clk-rpi.c85
-rw-r--r--drivers/clk/imx/clk-imx6.c4
-rw-r--r--drivers/clk/imx/clk-imx8mp.c1
-rw-r--r--drivers/firmware/zynqmp-fpga.c13
-rw-r--r--drivers/mfd/Kconfig6
-rw-r--r--drivers/mfd/Makefile1
-rw-r--r--drivers/mfd/rn5t568.c165
-rw-r--r--drivers/misc/Kconfig23
-rw-r--r--drivers/misc/Makefile1
-rw-r--r--drivers/misc/storage-by-uuid.c199
-rw-r--r--drivers/mtd/devices/mtdram.c35
-rw-r--r--drivers/of/base.c2
-rw-r--r--drivers/of/of_path.c2
-rw-r--r--drivers/of/platform.c3
-rw-r--r--drivers/power/reset/Kconfig3
-rw-r--r--drivers/serial/serial_cadence.c6
-rw-r--r--drivers/usb/core/usb.c1
-rw-r--r--drivers/watchdog/Kconfig6
-rw-r--r--drivers/watchdog/Makefile1
-rw-r--r--drivers/watchdog/rn5t568_wdt.c146
24 files changed, 834 insertions, 53 deletions
diff --git a/drivers/ata/sata_mv.c b/drivers/ata/sata_mv.c
index 3b55c71d67..05b27f1008 100644
--- a/drivers/ata/sata_mv.c
+++ b/drivers/ata/sata_mv.c
@@ -33,15 +33,22 @@ static void ata_ioports_init(struct ata_ioports *io,
/* io->alt_dev_addr is unused */
}
-#define REG_WINDOW_CONTROL(n) ((n) * 0x10 + 0x30)
-#define REG_WINDOW_BASE(n) ((n) * 0x10 + 0x34)
+#define REG_WINDOW_CONTROL(n) ((n) * 0x10 + 0x30)
+#define REG_WINDOW_BASE(n) ((n) * 0x10 + 0x34)
-#define REG_EDMA_COMMAND(n) ((n) * 0x2000 + 0x2028)
+#define REG_EDMA_COMMAND(n) ((n) * 0x2000 + 0x2028)
+#define EDMA_EN (1 << 0) /* enable EDMA */
+#define EDMA_DS (1 << 1) /* disable EDMA; self-negated */
#define REG_EDMA_COMMAND__EATARST 0x00000004
-
-#define REG_ATA_BASE 0x2100
-#define REG_SSTATUS(n) ((n) * 0x2000 + 0x2300)
-#define REG_SCONTROL(n) ((n) * 0x2000 + 0x2308)
+#define REG_EDMA_IORDY_TMOUT(n) ((n) * 0x2000 + 0x2034)
+#define REG_SATA_IFCFG(n) ((n) * 0x2000 + 0x2050)
+#define REG_SATA_IFCFG_GEN2EN (1 << 7)
+
+#define REG_ATA_BASE 0x2100
+#define REG_SSTATUS(n) ((n) * 0x2000 + 0x2300)
+#define REG_SERROR(n) ((n) * 0x2000 + 0x2304)
+#define REG_SERROR_MASK 0x03fe0000
+#define REG_SCONTROL(n) ((n) * 0x2000 + 0x2308)
#define REG_SCONTROL__DET 0x0000000f
#define REG_SCONTROL__DET__INIT 0x00000001
#define REG_SCONTROL__DET__PHYOK 0x00000002
@@ -49,13 +56,49 @@ static void ata_ioports_init(struct ata_ioports *io,
#define REG_SCONTROL__IPM__PARTIAL 0x00000100
#define REG_SCONTROL__IPM__SLUMBER 0x00000200
+#define PHY_MODE3 0x310
+#define PHY_MODE4 0x314 /* requires read-after-write */
+#define PHY_MODE9_GEN2 0x398
+#define PHY_MODE9_GEN1 0x39c
+
+static void mv_soc_65n_phy_errata(void __iomem *base)
+{
+ u32 reg;
+
+ reg = readl(base + PHY_MODE3);
+ reg &= ~(0x3 << 27); /* SELMUPF (bits 28:27) to 1 */
+ reg |= (0x1 << 27);
+ reg &= ~(0x3 << 29); /* SELMUPI (bits 30:29) to 1 */
+ reg |= (0x1 << 29);
+ writel(reg, base + PHY_MODE3);
+
+ reg = readl(base + PHY_MODE4);
+ reg &= ~0x1; /* SATU_OD8 (bit 0) to 0, reserved bit 16 must be set */
+ reg |= (0x1 << 16);
+ writel(reg, base + PHY_MODE4);
+
+ reg = readl(base + PHY_MODE9_GEN2);
+ reg &= ~0xf; /* TXAMP[3:0] (bits 3:0) to 8 */
+ reg |= 0x8;
+ reg &= ~(0x1 << 14); /* TXAMP[4] (bit 14) to 0 */
+ writel(reg, base + PHY_MODE9_GEN2);
+
+ reg = readl(base + PHY_MODE9_GEN1);
+ reg &= ~0xf; /* TXAMP[3:0] (bits 3:0) to 8 */
+ reg |= 0x8;
+ reg &= ~(0x1 << 14); /* TXAMP[4] (bit 14) to 0 */
+ writel(reg, base + PHY_MODE9_GEN1);
+}
+
static int mv_sata_probe(struct device_d *dev)
{
struct resource *iores;
void __iomem *base;
struct ide_port *ide;
+ u32 try_again = 0;
u32 scontrol;
int ret, i;
+ u32 tmp;
iores = dev_request_mem_resource(dev, 0);
if (IS_ERR(iores)) {
@@ -74,6 +117,31 @@ static int mv_sata_probe(struct device_d *dev)
writel(0x7fff0e01, base + REG_WINDOW_CONTROL(0));
writel(0, base + REG_WINDOW_BASE(0));
+again:
+ /* Clear SError */
+ writel(0x0, base + REG_SERROR(0));
+ /* disable EDMA */
+ writel(EDMA_DS, base + REG_EDMA_COMMAND(0));
+ /* Wait for the chip to confirm eDMA is off. */
+ ret = wait_on_timeout(10 * MSECOND,
+ (readl(base + REG_EDMA_COMMAND(0)) & EDMA_EN) == 0);
+ if (ret) {
+ dev_err(dev, "Failed to wait for eDMA off (sstatus=0x%08x)\n",
+ readl(base + REG_SSTATUS(0)));
+ return ret;
+ }
+
+ /* increase IORdy signal timeout */
+ writel(0x800, base + REG_EDMA_IORDY_TMOUT(0));
+ /* set GEN2i Speed */
+ tmp = readl(base + REG_SATA_IFCFG(0));
+ tmp |= REG_SATA_IFCFG_GEN2EN;
+ writel(tmp, base + REG_SATA_IFCFG(0));
+
+ mv_soc_65n_phy_errata(base);
+
+ /* strobe for hard-reset */
+ writel(REG_EDMA_COMMAND__EATARST, base + REG_EDMA_COMMAND(0));
writel(REG_EDMA_COMMAND__EATARST, base + REG_EDMA_COMMAND(0));
udelay(25);
writel(0x0, base + REG_EDMA_COMMAND(0));
@@ -104,10 +172,39 @@ static int mv_sata_probe(struct device_d *dev)
dev->priv = ide;
+ /* enable EDMA */
+ writel(EDMA_EN, base + REG_EDMA_COMMAND(0));
+
ret = ide_port_register(ide);
if (ret)
free(ide);
+ /*
+ * Under most conditions the above is enough and works as expected.
+ * With some specific hardware combinations, the setup fails however
+ * leading to an unusable SATA drive. From the error status bits it
+ * was not obvious what exactly went wrong.
+ * The ARMADA-XP datasheet advices to hard-reset the SATA core and
+ * drive and try again.
+ * When this happens, just try again multiple times, to give the drive
+ * some time to reach a stable state. If after 5 (randomly chosen) tries,
+ * the drive still doesn't work, just give up on it.
+ */
+ tmp = readl(base + REG_SERROR(0));
+ if (tmp & REG_SERROR_MASK) {
+ try_again++;
+ if (try_again > 5)
+ return -ENODEV;
+ dev_dbg(dev, "PHY layer error. Try again. (serror=0x%08x)\n", tmp);
+ if (ide->port.initialized) {
+ blockdevice_unregister(&ide->port.blk);
+ unregister_device(&ide->port.class_dev);
+ }
+
+ mdelay(100);
+ goto again;
+ }
+
return ret;
}
diff --git a/drivers/base/driver.c b/drivers/base/driver.c
index f54f4d0b37..2347b5c71f 100644
--- a/drivers/base/driver.c
+++ b/drivers/base/driver.c
@@ -270,6 +270,36 @@ int unregister_device(struct device_d *old_dev)
}
EXPORT_SYMBOL(unregister_device);
+/**
+ * free_device_res - free dynamically allocated device members
+ * @dev: The device
+ *
+ * This frees dynamically allocated resources allocated during device
+ * lifetime, but not the device itself.
+ */
+void free_device_res(struct device_d *dev)
+{
+ free(dev->name);
+ dev->name = NULL;
+ free(dev->unique_name);
+ dev->unique_name = NULL;
+}
+EXPORT_SYMBOL(free_device_res);
+
+/**
+ * free_device - free a device
+ * @dev: The device
+ *
+ * This frees dynamically allocated resources allocated during device
+ * lifetime and finally the device itself.
+ */
+void free_device(struct device_d *dev)
+{
+ free_device_res(dev);
+ free(dev);
+}
+EXPORT_SYMBOL(free_device);
+
/*
* Loop over list of deferred devices as long as at least one
* device is successfully probed. Devices that again request
diff --git a/drivers/block/efi-block-io.c b/drivers/block/efi-block-io.c
index ff0e467d2c..086afb378a 100644
--- a/drivers/block/efi-block-io.c
+++ b/drivers/block/efi-block-io.c
@@ -52,6 +52,7 @@ struct efi_bio_priv {
struct device_d *dev;
struct block_device blk;
u32 media_id;
+ void (*efi_info)(struct device_d *);
};
static int efi_bio_read(struct block_device *blk, void *buffer, sector_t block,
@@ -101,35 +102,40 @@ static struct block_device_ops efi_bio_ops = {
.flush = efi_bio_flush,
};
-static void efi_bio_print_info(struct efi_bio_priv *priv)
+static void efi_bio_print_info(struct device_d *dev)
{
+ struct efi_bio_priv *priv = dev->priv;
struct efi_block_io_media *media = priv->protocol->media;
u64 revision = priv->protocol->revision;
- dev_dbg(priv->dev, "revision: 0x%016llx\n", revision);
- dev_dbg(priv->dev, "media_id: 0x%08x\n", media->media_id);
- dev_dbg(priv->dev, "removable_media: %d\n", media->removable_media);
- dev_dbg(priv->dev, "media_present: %d\n", media->media_present);
- dev_dbg(priv->dev, "logical_partition: %d\n", media->logical_partition);
- dev_dbg(priv->dev, "read_only: %d\n", media->read_only);
- dev_dbg(priv->dev, "write_caching: %d\n", media->write_caching);
- dev_dbg(priv->dev, "block_size: 0x%08x\n", media->block_size);
- dev_dbg(priv->dev, "io_align: 0x%08x\n", media->io_align);
- dev_dbg(priv->dev, "last_block: 0x%016llx\n", media->last_block);
+ printf("Block I/O Media:\n");
+ printf(" revision: 0x%016llx\n", revision);
+ printf(" media_id: 0x%08x\n", media->media_id);
+ printf(" removable_media: %d\n", media->removable_media);
+ printf(" media_present: %d\n", media->media_present);
+ printf(" logical_partition: %d\n", media->logical_partition);
+ printf(" read_only: %d\n", media->read_only);
+ printf(" write_caching: %d\n", media->write_caching);
+ printf(" block_size: 0x%08x\n", media->block_size);
+ printf(" io_align: 0x%08x\n", media->io_align);
+ printf(" last_block: 0x%016llx\n", media->last_block);
if (revision < EFI_BLOCK_IO_PROTOCOL_REVISION2)
return;
- dev_dbg(priv->dev, "u64 lowest_aligned_lba: 0x%08llx\n",
+ printf(" lowest_aligned_lba: 0x%08llx\n",
media->lowest_aligned_lba);
- dev_dbg(priv->dev, "logical_blocks_per_physical_block: 0x%08x\n",
+ printf(" logical_blocks_per_physical_block: 0x%08x\n",
media->logical_blocks_per_physical_block);
if (revision < EFI_BLOCK_IO_PROTOCOL_REVISION3)
return;
- dev_dbg(priv->dev, "optimal_transfer_length_granularity: 0x%08x\n",
+ printf(" optimal_transfer_length_granularity: 0x%08x\n",
media->optimal_transfer_length_granularity);
+
+ if (priv->efi_info)
+ priv->efi_info(dev);
}
static bool is_bio_usbdev(struct efi_device *efidev)
@@ -143,6 +149,7 @@ static int efi_bio_probe(struct efi_device *efidev)
int instance;
struct efi_bio_priv *priv;
struct efi_block_io_media *media;
+ struct device_d *dev = &efidev->dev;
priv = xzalloc(sizeof(*priv));
@@ -151,8 +158,13 @@ static int efi_bio_probe(struct efi_device *efidev)
if (!priv->protocol)
return -ENODEV;
+ dev->priv = priv;
+ priv->efi_info = dev->info;
+ dev->info = efi_bio_print_info;
+
media = priv->protocol->media;
- efi_bio_print_info(priv);
+ if (__is_defined(DEBUG))
+ efi_bio_print_info(dev);
priv->dev = &efidev->dev;
if (is_bio_usbdev(efidev)) {
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index bab0498d7c..c60b38ff60 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -25,3 +25,4 @@ obj-y += analogbits/
obj-$(CONFIG_CLK_SIFIVE) += sifive/
obj-$(CONFIG_SOC_STARFIVE) += starfive/
obj-$(CONFIG_COMMON_CLK_STM32F) += clk-stm32f4.o
+obj-$(CONFIG_MACH_RPI_COMMON) += clk-rpi.o
diff --git a/drivers/clk/clk-rpi.c b/drivers/clk/clk-rpi.c
new file mode 100644
index 0000000000..59ae8e59ba
--- /dev/null
+++ b/drivers/clk/clk-rpi.c
@@ -0,0 +1,85 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <common.h>
+#include <init.h>
+#include <driver.h>
+#include <linux/clk.h>
+#include <io.h>
+#include <linux/clkdev.h>
+#include <linux/err.h>
+
+#include <mach/core.h>
+#include <mach/mbox.h>
+#include <mach/platform.h>
+#include <dt-bindings/clock/bcm2835.h>
+
+#define BCM2711_CLOCK_END (BCM2711_CLOCK_EMMC2 + 1)
+
+static struct clk *clks[BCM2711_CLOCK_END];
+static struct clk_onecell_data clk_data;
+
+struct msg_get_clock_rate {
+ struct bcm2835_mbox_hdr hdr;
+ struct bcm2835_mbox_tag_get_clock_rate get_clock_rate;
+ u32 end_tag;
+};
+
+static struct clk *rpi_register_firmware_clock(u32 clock_id, const char *name)
+{
+ BCM2835_MBOX_STACK_ALIGN(struct msg_get_clock_rate, msg);
+ int ret;
+
+ BCM2835_MBOX_INIT_HDR(msg);
+ BCM2835_MBOX_INIT_TAG(&msg->get_clock_rate, GET_CLOCK_RATE);
+ msg->get_clock_rate.body.req.clock_id = clock_id;
+
+ ret = bcm2835_mbox_call_prop(BCM2835_MBOX_PROP_CHAN, &msg->hdr);
+ if (ret)
+ return ERR_PTR(ret);
+
+ return clk_fixed(name, msg->get_clock_rate.body.resp.rate_hz);
+}
+
+static int bcm2835_cprman_probe(struct device_d *dev)
+{
+ struct clk *clk_cs;
+
+ clks[BCM2835_CLOCK_EMMC] =
+ rpi_register_firmware_clock(BCM2835_MBOX_CLOCK_ID_EMMC,
+ "bcm2835_mci0");
+ if (IS_ERR(clks[BCM2835_CLOCK_EMMC]))
+ return PTR_ERR(clks[BCM2835_CLOCK_EMMC]);
+
+ clks[BCM2835_CLOCK_VPU] =
+ rpi_register_firmware_clock(BCM2835_MBOX_CLOCK_ID_CORE,
+ "vpu");
+ if (IS_ERR(clks[BCM2835_CLOCK_VPU]))
+ return PTR_ERR(clks[BCM2835_CLOCK_VPU]);
+
+ clks[BCM2835_CLOCK_UART] = clk_fixed("uart0-pl0110", 48 * 1000 * 1000);
+ clk_register_clkdev(clks[BCM2835_CLOCK_UART], NULL, "uart0-pl0110");
+
+ clk_cs = clk_fixed("bcm2835-cs", 1 * 1000 * 1000);
+ clk_register_clkdev(clk_cs, NULL, "bcm2835-cs");
+
+ clk_data.clks = clks;
+ clk_data.clk_num = BCM2711_CLOCK_END;
+ of_clk_add_provider(dev->device_node, of_clk_src_onecell_get, &clk_data);
+
+ return 0;
+}
+
+static __maybe_unused struct of_device_id bcm2835_cprman_dt_ids[] = {
+ {
+ .compatible = "brcm,bcm2835-cprman",
+ }, {
+ /* sentinel */
+ }
+};
+
+static struct driver_d bcm2835_cprman_driver = {
+ .probe = bcm2835_cprman_probe,
+ .name = "bcm2835-cprman",
+ .of_compatible = DRV_OF_COMPAT(bcm2835_cprman_dt_ids),
+};
+core_platform_driver(bcm2835_cprman_driver);
diff --git a/drivers/clk/imx/clk-imx6.c b/drivers/clk/imx/clk-imx6.c
index 6935a8e852..e3afcf7858 100644
--- a/drivers/clk/imx/clk-imx6.c
+++ b/drivers/clk/imx/clk-imx6.c
@@ -60,8 +60,8 @@ static inline int cpu_mx6_is_plus(void)
/* Audio/Video PLL post dividers don't work on i.MX6q revision 1.0 */
static inline int cpu_has_working_video_pll_post_div(void) {
- return !((cpu_is_mx6q() || cpu_is_mx6d()) &&
- imx_silicon_revision() == IMX_CHIP_REV_1_0);
+ return !((cpu_mx6_is_mx6q() || cpu_mx6_is_mx6d()) &&
+ __imx6_cpu_revision() == IMX_CHIP_REV_1_0);
}
/* i.MX6 Quad/Dual/DualLite/Solo are all affected */
diff --git a/drivers/clk/imx/clk-imx8mp.c b/drivers/clk/imx/clk-imx8mp.c
index be395a6d55..b208ade9e6 100644
--- a/drivers/clk/imx/clk-imx8mp.c
+++ b/drivers/clk/imx/clk-imx8mp.c
@@ -543,7 +543,6 @@ static int imx8mp_clocks_init(struct device_node *ccm_np)
hws[IMX8MP_CLK_MIPI_DSI_ESC_RX] = imx8m_clk_hw_composite_bus("mipi_dsi_esc_rx", imx8mp_mipi_dsi_esc_rx_sels, ccm_base + 0x9200);
hws[IMX8MP_CLK_IPG_ROOT] = imx_clk_hw_divider2("ipg_root", "ahb_root", ccm_base + 0x9080, 0, 1);
- hws[IMX8MP_CLK_IPG_AUDIO_ROOT] = imx_clk_hw_divider2("ipg_audio_root", "audio_ahb", ccm_base + 0x9180, 0, 1);
hws[IMX8MP_CLK_DRAM_ALT] = imx8m_clk_hw_composite("dram_alt", imx8mp_dram_alt_sels, ccm_base + 0xa000);
hws[IMX8MP_CLK_DRAM_APB] = imx8m_clk_hw_composite_critical("dram_apb", imx8mp_dram_apb_sels, ccm_base + 0xa080);
diff --git a/drivers/firmware/zynqmp-fpga.c b/drivers/firmware/zynqmp-fpga.c
index a76600d4c9..63d7398fd4 100644
--- a/drivers/firmware/zynqmp-fpga.c
+++ b/drivers/firmware/zynqmp-fpga.c
@@ -252,13 +252,7 @@ static int fpgamgr_program_finish(struct firmware_handler *fh)
body_length / sizeof(u32));
else
memcpy((u32 *)buf_aligned, body, body_length);
-
- if (mgr->features & ZYNQMP_PM_FEATURE_SIZE_NOT_NEEDED) {
- buf_size = body_length;
- } else {
- buf_aligned[body_length / sizeof(*buf_aligned)] = body_length;
- buf_size = addr + body_length;
- }
+ buf_aligned[body_length / sizeof(*buf_aligned)] = body_length;
addr = dma_map_single(&mgr->dev, buf_aligned,
body_length + sizeof(buf_size), DMA_TO_DEVICE);
@@ -267,6 +261,11 @@ static int fpgamgr_program_finish(struct firmware_handler *fh)
goto err_free_dma;
}
+ if (mgr->features & ZYNQMP_PM_FEATURE_SIZE_NOT_NEEDED)
+ buf_size = body_length;
+ else
+ buf_size = addr + body_length;
+
status = mgr->eemi_ops->fpga_load((u64)addr, buf_size, flags);
dma_unmap_single(&mgr->dev, addr, body_length + sizeof(buf_size),
DMA_TO_DEVICE);
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 4285280602..9d4a82a9bb 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -68,6 +68,12 @@ config MFD_STPMIC1
help
Select this to support communication with the STPMIC1.
+config MFD_RN568PMIC
+ depends on I2C
+ bool "Ricoh RN5T568 MFD driver"
+ help
+ Select this to support communication with the Ricoh RN5T568 PMIC.
+
config MFD_SUPERIO
bool
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 2bcf90078a..50f54cfcf2 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -13,6 +13,7 @@ obj-$(CONFIG_MFD_TWL4030) += twl4030.o
obj-$(CONFIG_MFD_TWL6030) += twl6030.o
obj-$(CONFIG_RAVE_SP_CORE) += rave-sp.o
obj-$(CONFIG_MFD_STPMIC1) += stpmic1.o
+obj-$(CONFIG_MFD_RN568PMIC) += rn5t568.o
obj-$(CONFIG_MFD_SUPERIO) += superio.o
obj-$(CONFIG_FINTEK_SUPERIO) += fintek-superio.o
obj-$(CONFIG_SMSC_SUPERIO) += smsc-superio.o
diff --git a/drivers/mfd/rn5t568.c b/drivers/mfd/rn5t568.c
new file mode 100644
index 0000000000..c1c792cbec
--- /dev/null
+++ b/drivers/mfd/rn5t568.c
@@ -0,0 +1,165 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * MFD core driver for Ricoh RN5T618 PMIC
+ * Note: Manufacturer is now Nisshinbo Micro Devices Inc.
+ *
+ * Copyright (C) 2014 Beniamino Galvani <b.galvani@gmail.com>
+ * Copyright (C) 2016 Toradex AG
+ */
+
+#include <common.h>
+#include <driver.h>
+#include <errno.h>
+#include <i2c/i2c.h>
+#include <init.h>
+#include <of.h>
+#include <regmap.h>
+#include <reset_source.h>
+#include <restart.h>
+
+#define RN5T568_LSIVER 0x00
+#define RN5T568_OTPVER 0x01
+#define RN5T568_PONHIS 0x09
+# define RN5T568_PONHIS_ON_EXTINPON BIT(3)
+# define RN5T568_PONHIS_ON_REPWRPON BIT(1)
+# define RN5T568_PONHIS_ON_PWRONPON BIT(0)
+#define RN5T568_POFFHIS 0x0a
+# define RN5T568_POFFHIS_N_OEPOFF BIT(7)
+# define RN5T568_POFFHIS_DCLIMPOFF BIT(6)
+# define RN5T568_POFFHIS_WDGPOFF BIT(5)
+# define RN5T568_POFFHIS_CPUPOFF BIT(4)
+# define RN5T568_POFFHIS_IODETPOFF BIT(3)
+# define RN5T568_POFFHIS_VINDETPOFF BIT(2)
+# define RN5T568_POFFHIS_TSHUTPOFF BIT(1)
+# define RN5T568_POFFHIS_PWRONPOFF BIT(0)
+#define RN5T568_SLPCNT 0x0e
+# define RN5T568_SLPCNT_SWPPWROFF BIT(0)
+#define RN5T568_REPCNT 0x0f
+# define RN5T568_REPCNT_OFF_RESETO_16MS 0x30
+# define RN5T568_REPCNT_OFF_REPWRTIM_1000MS 0x06
+# define RN5T568_REPCNT_OFF_REPWRON BIT(0)
+#define RN5T568_MAX_REG 0xbc
+
+struct rn5t568 {
+ struct restart_handler restart;
+ struct regmap *regmap;
+};
+
+static void rn5t568_restart(struct restart_handler *rst)
+{
+ struct rn5t568 *rn5t568 = container_of(rst, struct rn5t568, restart);
+
+ regmap_write(rn5t568->regmap, RN5T568_SLPCNT, RN5T568_SLPCNT_SWPPWROFF);
+}
+
+static int rn5t568_reset_reason_detect(struct device_d *dev, struct regmap *regmap)
+{
+ unsigned int reg;
+ int ret;
+
+ ret = regmap_read(regmap, RN5T568_PONHIS, &reg);
+ if (ret)
+ return ret;
+
+ dev_dbg(dev, "Power-on history: %x\n", reg);
+
+ if (reg == 0) {
+ dev_info(dev, "No power-on reason available\n");
+ return 0;
+ }
+
+ if (reg & RN5T568_PONHIS_ON_EXTINPON) {
+ reset_source_set_device(dev, RESET_POR);
+ return 0;
+ } else if (reg & RN5T568_PONHIS_ON_PWRONPON) {
+ reset_source_set_device(dev, RESET_POR);
+ return 0;
+ } else if (!(reg & RN5T568_PONHIS_ON_REPWRPON))
+ return -EINVAL;
+
+ ret = regmap_read(regmap, RN5T568_POFFHIS, &reg);
+ if (ret)
+ return ret;
+
+ dev_dbg(dev, "Power-off history: %x\n", reg);
+
+ if (reg & RN5T568_POFFHIS_PWRONPOFF)
+ reset_source_set_device(dev, RESET_POR);
+ else if (reg & RN5T568_POFFHIS_TSHUTPOFF)
+ reset_source_set_device(dev, RESET_THERM);
+ else if (reg & RN5T568_POFFHIS_VINDETPOFF)
+ reset_source_set_device(dev, RESET_BROWNOUT);
+ else if (reg & RN5T568_POFFHIS_IODETPOFF)
+ reset_source_set_device(dev, RESET_UKWN);
+ else if (reg & RN5T568_POFFHIS_CPUPOFF)
+ reset_source_set_device(dev, RESET_RST);
+ else if (reg & RN5T568_POFFHIS_WDGPOFF)
+ reset_source_set_device(dev, RESET_WDG);
+ else if (reg & RN5T568_POFFHIS_DCLIMPOFF)
+ reset_source_set_device(dev, RESET_BROWNOUT);
+ else if (reg & RN5T568_POFFHIS_N_OEPOFF)
+ reset_source_set_device(dev, RESET_EXT);
+ else
+ return -EINVAL;
+
+ return 0;
+}
+
+static const struct regmap_config rn5t568_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = RN5T568_MAX_REG,
+};
+
+static int __init rn5t568_i2c_probe(struct device_d *dev)
+{
+ struct rn5t568 *pmic_instance;
+ unsigned char reg[2];
+ int ret;
+
+ pmic_instance = xzalloc(sizeof(struct rn5t568));
+ pmic_instance->regmap = regmap_init_i2c(to_i2c_client(dev), &rn5t568_regmap_config);
+ if (IS_ERR(pmic_instance->regmap))
+ return PTR_ERR(pmic_instance->regmap);
+
+ ret = regmap_register_cdev(pmic_instance->regmap, NULL);
+ if (ret)
+ return ret;
+
+ ret = regmap_bulk_read(pmic_instance->regmap, RN5T568_LSIVER, &reg, 2);
+ if (ret) {
+ dev_err(dev, "Failed to read PMIC version via I2C\n");
+ return ret;
+ }
+
+ dev_info(dev, "Found NMD RN5T568 LSI %x, OTP: %x\n", reg[0], reg[1]);
+
+ /* Settings used to trigger software reset and by a watchdog trigger */
+ regmap_write(pmic_instance->regmap, RN5T568_REPCNT, RN5T568_REPCNT_OFF_RESETO_16MS |
+ RN5T568_REPCNT_OFF_REPWRTIM_1000MS | RN5T568_REPCNT_OFF_REPWRON);
+
+ pmic_instance->restart.priority = of_get_restart_priority(dev->device_node);
+ pmic_instance->restart.name = "RN5T568";
+ pmic_instance->restart.restart = &rn5t568_restart;
+ restart_handler_register(&pmic_instance->restart);
+ dev_dbg(dev, "RN5t: Restart handler with priority %d registered\n", pmic_instance->restart.priority);
+
+ ret = rn5t568_reset_reason_detect(dev, pmic_instance->regmap);
+ if (ret)
+ dev_warn(dev, "Failed to query reset reason\n");
+
+ return of_platform_populate(dev->device_node, NULL, dev);
+}
+
+static __maybe_unused const struct of_device_id rn5t568_of_match[] = {
+ { .compatible = "ricoh,rn5t568", .data = NULL, },
+ { }
+};
+
+static struct driver_d rn5t568_i2c_driver = {
+ .name = "rn5t568-i2c",
+ .probe = rn5t568_i2c_probe,
+ .of_compatible = DRV_OF_COMPAT(rn5t568_of_match),
+};
+
+coredevice_i2c_driver(rn5t568_i2c_driver);
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 5ab0506cd9..78c9c193d8 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -47,4 +47,27 @@ config STARFIVE_PWRSEQ
be accessed over /dev/mem or used from kernels which still depend
on bootloader for initialization.
+config STORAGE_BY_UUID
+ bool "storage by UUID"
+ depends on OFDEVICE
+ help
+ This adds a driver which matches to a "barebox,storage-by-uuid"
+ compatible node. The driver looks for a storage device matching the
+ given UUID and when found registers a new cdev for the device.
+
+ This driver solved a very specific problem. On EFI the storage devices
+ are not connected to any device tree node. barebox-state however expects
+ a node to use as its backend. The obvious solution would be to create
+ a partition with a specific partuuid and use that for state, in our
+ special usecase though the storage device is partitioned with a MBR
+ which doesn't have any space left to create a new partition. As this
+ driver parses the of partition binding we can use that to create
+ a partition in an unallocated are of the disk which is then used for
+ state.
+
+ This driver has the problem that it depends on storage devices which
+ are not described in the device tree. This means it cannot work with
+ deep probe. This is not a problem on EFI though. It's a special purpose
+ driver, it's not recommended for general use.
+
endmenu
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 6326e784fc..986f7b1b38 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -9,3 +9,4 @@ obj-$(CONFIG_STATE_DRV) += state.o
obj-$(CONFIG_DEV_MEM) += mem.o
obj-$(CONFIG_UBOOTVAR) += ubootvar.o
obj-$(CONFIG_STARFIVE_PWRSEQ) += starfive-pwrseq.o
+obj-$(CONFIG_STORAGE_BY_UUID) += storage-by-uuid.o
diff --git a/drivers/misc/storage-by-uuid.c b/drivers/misc/storage-by-uuid.c
new file mode 100644
index 0000000000..12a06076a2
--- /dev/null
+++ b/drivers/misc/storage-by-uuid.c
@@ -0,0 +1,199 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#include <common.h>
+#include <init.h>
+#include <io.h>
+#include <of.h>
+#include <malloc.h>
+#include <partition.h>
+#include <envfs.h>
+#include <fs.h>
+
+static LIST_HEAD(sbu_list);
+
+struct sbu {
+ char *uuid;
+ struct device_d *dev;
+ struct cdev *rcdev;
+ struct cdev cdev;
+ struct list_head list;
+};
+
+void storage_by_uuid_check_exist(struct cdev *cdev);
+
+static ssize_t sbu_read(struct cdev *cdev, void *buf, size_t count, loff_t offset, ulong flags)
+{
+ struct sbu *sbu = cdev->priv;
+
+ return cdev_read(sbu->rcdev, buf, count, offset, flags);
+}
+
+static ssize_t sbu_write(struct cdev *cdev, const void *buf, size_t count, loff_t offset, ulong flags)
+{
+ struct sbu *sbu = cdev->priv;
+
+ return cdev_write(sbu->rcdev, buf, count, offset, flags);
+}
+
+static int sbu_ioctl(struct cdev *cdev, int request, void *buf)
+{
+ struct sbu *sbu = cdev->priv;
+
+ return cdev_ioctl(sbu->rcdev, request, buf);
+}
+
+static int sbu_open(struct cdev *cdev, unsigned long flags)
+{
+ struct sbu *sbu = cdev->priv;
+
+ return cdev_open(sbu->rcdev, flags);
+}
+
+static int sbu_close(struct cdev *cdev)
+{
+ struct sbu *sbu = cdev->priv;
+
+ cdev_close(sbu->rcdev);
+
+ return 0;
+}
+
+static int sbu_flush(struct cdev *cdev)
+{
+ struct sbu *sbu = cdev->priv;
+
+ return cdev_flush(sbu->rcdev);
+}
+
+static int sbu_erase(struct cdev *cdev, loff_t count, loff_t offset)
+{
+ struct sbu *sbu = cdev->priv;
+
+ return cdev_erase(sbu->rcdev, count, offset);
+}
+
+static int sbu_protect(struct cdev *cdev, size_t count, loff_t offset, int prot)
+{
+ struct sbu *sbu = cdev->priv;
+
+ return cdev_protect(sbu->rcdev, count, offset, prot);
+}
+
+static int sbu_discard_range(struct cdev *cdev, loff_t count, loff_t offset)
+{
+ struct sbu *sbu = cdev->priv;
+
+ return cdev_discard_range(sbu->rcdev, count, offset);
+}
+
+static int sbu_memmap(struct cdev *cdev, void **map, int flags)
+{
+ struct sbu *sbu = cdev->priv;
+
+ return cdev_memmap(sbu->rcdev, map, flags);
+}
+
+static int sbu_truncate(struct cdev *cdev, size_t size)
+{
+ struct sbu *sbu = cdev->priv;
+
+ return cdev_truncate(sbu->rcdev, size);
+}
+
+static struct cdev_operations sbu_ops = {
+ .read = sbu_read,
+ .write = sbu_write,
+ .ioctl = sbu_ioctl,
+ .open = sbu_open,
+ .close = sbu_close,
+ .flush = sbu_flush,
+ .erase = sbu_erase,
+ .protect = sbu_protect,
+ .discard_range = sbu_discard_range,
+ .memmap = sbu_memmap,
+ .truncate = sbu_truncate,
+};
+
+static void storage_by_uuid_add_partitions(struct sbu *sbu, struct cdev *rcdev)
+{
+ int ret;
+
+ if (sbu->rcdev)
+ return;
+
+ sbu->rcdev = rcdev;
+ sbu->cdev.name = sbu->uuid;
+ sbu->cdev.size = rcdev->size;
+ sbu->cdev.ops = &sbu_ops;
+ sbu->cdev.dev = sbu->dev;
+ sbu->cdev.priv = sbu;
+
+ ret = devfs_create(&sbu->cdev);
+ if (ret) {
+ dev_err(sbu->dev, "Failed to create cdev: %s\n", strerror(-ret));
+ return;
+ }
+
+ of_parse_partitions(&sbu->cdev, sbu->dev->device_node);
+}
+
+static void check_exist(struct sbu *sbu)
+{
+ struct cdev *cdev;
+
+ for_each_cdev(cdev) {
+ if (!strcmp(cdev->uuid, sbu->uuid)) {
+ dev_dbg(sbu->dev, "Found %s %s\n", cdev->name, cdev->uuid);
+ storage_by_uuid_add_partitions(sbu, cdev);
+ }
+ }
+}
+
+static int sbu_detect(struct device_d *dev)
+{
+ struct sbu *sbu = dev->priv;
+
+ dev_dbg(dev, "%s\n", __func__);
+
+ check_exist(sbu);
+
+ return 0;
+}
+
+static int storage_by_uuid_probe(struct device_d *dev)
+{
+ struct sbu *sbu;
+ int ret;
+ const char *uuid;
+
+ sbu = xzalloc(sizeof(*sbu));
+
+ ret = of_property_read_string(dev->device_node, "uuid", &uuid);
+ if (ret)
+ return ret;
+
+ sbu->dev = dev;
+ sbu->uuid = xstrdup(uuid);
+
+ list_add_tail(&sbu->list, &sbu_list);
+
+ check_exist(sbu);
+ dev->priv = sbu;
+ dev->detect = sbu_detect;
+
+ return 0;
+}
+
+static struct of_device_id storage_by_uuid_dt_ids[] = {
+ {
+ .compatible = "barebox,storage-by-uuid",
+ }, {
+ /* sentinel */
+ }
+};
+
+static struct driver_d storage_by_uuid_driver = {
+ .name = "storage-by-uuid",
+ .probe = storage_by_uuid_probe,
+ .of_compatible = storage_by_uuid_dt_ids,
+};
+device_platform_driver(storage_by_uuid_driver);
diff --git a/drivers/mtd/devices/mtdram.c b/drivers/mtd/devices/mtdram.c
index ee1cbf792d..abef07d9c0 100644
--- a/drivers/mtd/devices/mtdram.c
+++ b/drivers/mtd/devices/mtdram.c
@@ -16,11 +16,6 @@
#include <malloc.h>
#include <of.h>
-struct mtdram_priv_data {
- struct mtd_info mtd;
- void *base;
-};
-
static int ram_erase(struct mtd_info *mtd, struct erase_info *instr)
{
memset((char *)mtd->priv + instr->addr, 0xff, instr->len);
@@ -43,11 +38,10 @@ static int ram_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retle
static int mtdram_probe(struct device_d *dev)
{
+ long type;
struct resource *iores;
- void __iomem *base;
int device_id;
struct mtd_info *mtd;
- struct resource *res;
loff_t size;
int ret = 0;
@@ -60,9 +54,11 @@ static int mtdram_probe(struct device_d *dev)
mtd->name = xstrdup(alias);
}
+ type = (long)device_get_match_data(dev);
+
if (!mtd->name) {
device_id = DEVICE_ID_DYNAMIC;
- mtd->name = "mtdram";
+ mtd->name = type == MTD_RAM ? "mtdram" : "mtdrom";
}
iores = dev_request_mem_resource(dev, 0);
@@ -70,22 +66,23 @@ static int mtdram_probe(struct device_d *dev)
ret = PTR_ERR(iores);
goto nobase;
}
- base = IOMEM(iores->start);
- res = dev_get_resource(dev, IORESOURCE_MEM, 0);
- size = (unsigned long) resource_size(res);
- mtd->priv = base;
+ mtd->priv = IOMEM(iores->start);
+ size = (unsigned long) resource_size(iores);
- mtd->type = MTD_RAM;
+ mtd->type = type;
mtd->writesize = 1;
mtd->writebufsize = 64;
- mtd->flags = MTD_CAP_RAM;
mtd->size = size;
mtd->_read = ram_read;
- mtd->_write = ram_write;
- mtd->_erase = ram_erase;
- mtd->erasesize = 1;
+
+ if (type == MTD_RAM) {
+ mtd->flags = MTD_CAP_RAM;
+ mtd->_write = ram_write;
+ mtd->_erase = ram_erase;
+ mtd->erasesize = 1;
+ }
mtd->dev.parent = dev;
@@ -101,6 +98,10 @@ nobase:
static __maybe_unused struct of_device_id mtdram_dt_ids[] = {
{
.compatible = "mtd-ram",
+ .data = (void *)MTD_RAM
+ }, {
+ .compatible = "mtd-rom",
+ .data = (void *)MTD_ROM
}, {
/* sentinel */
}
diff --git a/drivers/of/base.c b/drivers/of/base.c
index 80465d6d50..2591610c3f 100644
--- a/drivers/of/base.c
+++ b/drivers/of/base.c
@@ -2383,7 +2383,7 @@ static void of_platform_device_create_root(struct device_node *np)
ret = platform_device_register(dev);
if (ret)
- free(dev);
+ free_device(dev);
}
static const struct of_device_id reserved_mem_matches[] = {
diff --git a/drivers/of/of_path.c b/drivers/of/of_path.c
index 40154bf5e9..05c28bf052 100644
--- a/drivers/of/of_path.c
+++ b/drivers/of/of_path.c
@@ -31,7 +31,7 @@ struct device_d *of_find_device_by_node_path(const char *path)
*
* @node: The node to find the cdev for, can be the device or a
* partition in the device
- * @part: Optionally, a description of a parition of @node. See of_find_path
+ * @part: Optionally, a description of a partition of @node. See of_find_path
* @outpath: if this function returns 0 outpath will contain the path belonging
* to the input path description. Must be freed with free().
* @flags: use OF_FIND_PATH_FLAGS_BB to return the .bb device if available
diff --git a/drivers/of/platform.c b/drivers/of/platform.c
index 3a82809cb3..0e718469db 100644
--- a/drivers/of/platform.c
+++ b/drivers/of/platform.c
@@ -155,7 +155,7 @@ struct device_d *of_platform_device_create(struct device_node *np,
np->dev = NULL;
- free(dev);
+ free_device(dev);
if (num_reg)
free(res);
return NULL;
@@ -278,6 +278,7 @@ static struct device_d *of_amba_device_create(struct device_node *np)
return &dev->dev;
amba_err_free:
+ free_device_res(&dev->dev);
free(dev);
return NULL;
}
diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig
index 968d4b8ba4..564b77ecba 100644
--- a/drivers/power/reset/Kconfig
+++ b/drivers/power/reset/Kconfig
@@ -12,7 +12,8 @@ config SYSCON_REBOOT_MODE
help
Say y here will enable reboot mode driver. This will
get reboot mode arguments and store it in SYSCON mapped
- register, then the bootloader can read it to take different
+ register, then the bootloader can read it and take different
+ action according to the mode.
config NVMEM_REBOOT_MODE
bool "Generic NVMEM reboot mode driver"
diff --git a/drivers/serial/serial_cadence.c b/drivers/serial/serial_cadence.c
index 6cf18aa25a..84dcd1b76b 100644
--- a/drivers/serial/serial_cadence.c
+++ b/drivers/serial/serial_cadence.c
@@ -231,6 +231,12 @@ static __maybe_unused struct of_device_id cadence_serial_dt_ids[] = {
.compatible = "xlnx,xuartps",
.data = &cadence_r1p08_data,
}, {
+ .compatible = "cdns,uart-r1p12",
+ .data = &cadence_r1p08_data,
+ }, {
+ .compatible = "xlnx,zynqmp-uart",
+ .data = &cadence_r1p08_data,
+ }, {
/* sentinel */
}
};
diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c
index 4eede13a11..34a0f004f7 100644
--- a/drivers/usb/core/usb.c
+++ b/drivers/usb/core/usb.c
@@ -534,6 +534,7 @@ void usb_free_device(struct usb_device *usbdev)
{
dma_free(usbdev->descriptor);
dma_free(usbdev->setup_packet);
+ free_device_res(&usbdev->dev);
free(usbdev);
}
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 2d194fab79..6f209e096e 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -109,6 +109,12 @@ config STPMIC1_WATCHDOG
help
Enable to support configuration of the stpmic1's built-in watchdog.
+config RN568_WATCHDOG
+ bool "Ricoh RN5t568 PMIC based Watchdog"
+ depends on MFD_RN568PMIC
+ help
+ Enable to support system control via the PMIC based watchdog.
+
config F71808E_WDT
bool "Fintek F718xx, F818xx Super I/O Watchdog"
depends on X86
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index 0d2f273f78..265ae179f1 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -16,6 +16,7 @@ obj-$(CONFIG_ARCH_BCM283X) += bcm2835_wdt.o
obj-$(CONFIG_RAVE_SP_WATCHDOG) += rave-sp-wdt.o
obj-$(CONFIG_STM32_IWDG_WATCHDOG) += stm32_iwdg.o
obj-$(CONFIG_STPMIC1_WATCHDOG) += stpmic1_wdt.o
+obj-$(CONFIG_RN568_WATCHDOG) += rn5t568_wdt.o
obj-$(CONFIG_F71808E_WDT) += f71808e_wdt.o
obj-$(CONFIG_GPIO_WATCHDOG) += gpio_wdt.o
obj-$(CONFIG_ITCO_WDT) += itco_wdt.o
diff --git a/drivers/watchdog/rn5t568_wdt.c b/drivers/watchdog/rn5t568_wdt.c
new file mode 100644
index 0000000000..a6443609e2
--- /dev/null
+++ b/drivers/watchdog/rn5t568_wdt.c
@@ -0,0 +1,146 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Watchdog driver for Ricoh RN5T618 PMIC
+ *
+ * Copyright (C) 2014 Beniamino Galvani <b.galvani@gmail.com>
+ */
+
+#include <common.h>
+#include <init.h>
+#include <watchdog.h>
+#include <regmap.h>
+#include <of.h>
+
+#define RN5T568_WATCHDOG 0x0b
+# define RN5T568_WATCHDOG_WDPWROFFEN BIT(2)
+# define RN5T568_WATCHDOG_WDOGTIM_M (BIT(0) | BIT(1))
+#define RN5T568_PWRIREN 0x12
+# define RN5T568_PWRIREN_EN_WDOG BIT(6)
+#define RN5T568_PWRIRQ 0x13
+# define RN5T568_PWRIRQ_IR_WDOG BIT(6)
+
+struct rn5t568_wdt {
+ struct watchdog wdd;
+ struct regmap *regmap;
+ unsigned int timeout;
+};
+
+struct rn5t568_wdt_tim {
+ u8 reg_val;
+ u8 time;
+};
+
+static const struct rn5t568_wdt_tim rn5t568_wdt_timeout[] = {
+ { .reg_val = 0, .time = 1, },
+ { .reg_val = 1, .time = 8, },
+ { .reg_val = 2, .time = 32, },
+ { .reg_val = 3, .time = 128, },
+};
+
+#define PMIC_WDT_MAX_TIMEOUT 128
+
+static int rn5t568_wdt_start(struct regmap *regmap, int idx)
+{
+ int ret;
+
+ ret = regmap_update_bits(regmap, RN5T568_WATCHDOG, RN5T568_WATCHDOG_WDOGTIM_M,
+ rn5t568_wdt_timeout[idx].reg_val);
+ if (ret)
+ return ret;
+
+ regmap_clear_bits(regmap, RN5T568_PWRIRQ, RN5T568_PWRIRQ_IR_WDOG);
+ regmap_set_bits(regmap, RN5T568_PWRIREN, RN5T568_PWRIREN_EN_WDOG);
+
+ pr_debug("RN5t: Starting the watchdog with %u seconds\n", rn5t568_wdt_timeout[idx].time);
+
+ return regmap_set_bits(regmap, RN5T568_WATCHDOG, RN5T568_WATCHDOG_WDPWROFFEN);
+}
+
+static int rn5t568_wdt_stop(struct regmap *regmap)
+{
+ int ret;
+
+ ret = regmap_clear_bits(regmap, RN5T568_PWRIREN, RN5T568_PWRIREN_EN_WDOG);
+ if (ret)
+ return ret;
+
+ return regmap_clear_bits(regmap, RN5T568_WATCHDOG, RN5T568_WATCHDOG_WDPWROFFEN);
+}
+
+static int rn5t568_wdt_ping(struct regmap *regmap)
+{
+ unsigned int val;
+ int ret;
+
+ ret = regmap_read(regmap, RN5T568_WATCHDOG, &val);
+ if (ret)
+ return ret;
+
+ return regmap_write(regmap, RN5T568_WATCHDOG, val);
+}
+
+static int rn5t568_wdt_set_timeout(struct watchdog *wdd, unsigned int timeout)
+{
+ struct rn5t568_wdt *wdt = container_of(wdd, struct rn5t568_wdt, wdd);
+ int ret, i;
+
+ if (!timeout)
+ return rn5t568_wdt_stop(wdt->regmap);
+
+ for (i = 0; i < ARRAY_SIZE(rn5t568_wdt_timeout); i++) {
+ if (timeout < rn5t568_wdt_timeout[i].time)
+ break;
+ }
+
+ if (i == ARRAY_SIZE(rn5t568_wdt_timeout))
+ return -EINVAL;
+
+ if (wdt->timeout == timeout)
+ return rn5t568_wdt_ping(wdt->regmap);
+
+ ret = rn5t568_wdt_start(wdt->regmap, i);
+ if (ret)
+ return ret;
+
+ wdt->timeout = rn5t568_wdt_timeout[i].time;
+
+ return ret;
+}
+
+static int rn5t568_wdt_probe(struct device_d *dev)
+{
+ struct rn5t568_wdt *wdt;
+ struct watchdog *wdd;
+ unsigned int val;
+ int ret;
+
+ wdt = xzalloc(sizeof(*wdt));
+
+ wdt->regmap = dev_get_regmap(dev->parent, NULL);
+ if (IS_ERR(wdt->regmap))
+ return PTR_ERR(wdt->regmap);
+
+ wdd = &wdt->wdd;
+ wdd->hwdev = dev;
+ wdd->set_timeout = rn5t568_wdt_set_timeout;
+ wdd->timeout_max = PMIC_WDT_MAX_TIMEOUT;
+
+ ret = regmap_read(wdt->regmap, RN5T568_WATCHDOG, &val);
+ if (ret == 0)
+ wdd->running = val & RN5T568_WATCHDOG_WDPWROFFEN ?
+ WDOG_HW_RUNNING : WDOG_HW_NOT_RUNNING;
+
+ return watchdog_register(wdd);
+}
+
+static __maybe_unused const struct of_device_id rn5t568_wdt_of_match[] = {
+ { .compatible = "ricoh,rn5t568-wdt" },
+ { /* sentinel */ }
+};
+
+static struct driver_d rn5t568_wdt_driver = {
+ .name = "rn5t568-wdt",
+ .probe = rn5t568_wdt_probe,
+ .of_compatible = DRV_OF_COMPAT(rn5t568_wdt_of_match),
+};
+device_platform_driver(rn5t568_wdt_driver);