From bff18c2671621a93ce036109b456f214f7ee6622 Mon Sep 17 00:00:00 2001 From: Steffen Trumtrar Date: Mon, 17 Oct 2016 09:50:50 +0200 Subject: reset: import socfpga-reset driver from linux Port the linux v4.8-rc1 reset-socfpga driver to barebox. Signed-off-by: Steffen Trumtrar Signed-off-by: Sascha Hauer --- drivers/reset/Makefile | 1 + drivers/reset/reset-socfpga.c | 124 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 125 insertions(+) create mode 100644 drivers/reset/reset-socfpga.c (limited to 'drivers') diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile index 1e2d83f2b9..52b10cd480 100644 --- a/drivers/reset/Makefile +++ b/drivers/reset/Makefile @@ -1 +1,2 @@ obj-$(CONFIG_RESET_CONTROLLER) += core.o +obj-$(CONFIG_ARCH_SOCFPGA) += reset-socfpga.o diff --git a/drivers/reset/reset-socfpga.c b/drivers/reset/reset-socfpga.c new file mode 100644 index 0000000000..9214197e62 --- /dev/null +++ b/drivers/reset/reset-socfpga.c @@ -0,0 +1,124 @@ +/* + * Copyright 2014 Steffen Trumtrar + * + * based on + * Allwinner SoCs Reset Controller driver + * + * Copyright 2013 Maxime Ripard + * + * Maxime Ripard + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include + +#define NR_BANKS 4 + +struct socfpga_reset_data { + spinlock_t lock; + void __iomem *membase; + u32 modrst_offset; + struct reset_controller_dev rcdev; +}; + +static int socfpga_reset_assert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + struct socfpga_reset_data *data = container_of(rcdev, + struct socfpga_reset_data, + rcdev); + int bank = id / BITS_PER_LONG; + int offset = id % BITS_PER_LONG; + unsigned long flags; + u32 reg; + + spin_lock_irqsave(&data->lock, flags); + + reg = readl(data->membase + data->modrst_offset + (bank * NR_BANKS)); + writel(reg | BIT(offset), data->membase + data->modrst_offset + + (bank * NR_BANKS)); + spin_unlock_irqrestore(&data->lock, flags); + + return 0; +} + +static int socfpga_reset_deassert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + struct socfpga_reset_data *data = container_of(rcdev, + struct socfpga_reset_data, + rcdev); + + int bank = id / BITS_PER_LONG; + int offset = id % BITS_PER_LONG; + unsigned long flags; + u32 reg; + + spin_lock_irqsave(&data->lock, flags); + + reg = readl(data->membase + data->modrst_offset + (bank * NR_BANKS)); + writel(reg & ~BIT(offset), data->membase + data->modrst_offset + + (bank * NR_BANKS)); + + spin_unlock_irqrestore(&data->lock, flags); + + return 0; +} + +static struct reset_control_ops socfpga_reset_ops = { + .assert = socfpga_reset_assert, + .deassert = socfpga_reset_deassert, +}; + +static int socfpga_reset_probe(struct device_d *dev) +{ + struct socfpga_reset_data *data; + struct resource *res; + struct device_node *np = dev->device_node; + + data = xzalloc(sizeof(*data)); + + res = dev_request_mem_resource(dev, 0); + data->membase = IOMEM(res->start); + if (IS_ERR(data->membase)) + return PTR_ERR(data->membase); + + if (of_property_read_u32(np, "altr,modrst-offset", &data->modrst_offset)) { + dev_warn(dev, "missing altr,modrst-offset property, assuming 0x10!\n"); + data->modrst_offset = 0x10; + } + + spin_lock_init(&data->lock); + + data->rcdev.nr_resets = NR_BANKS * BITS_PER_LONG; + data->rcdev.ops = &socfpga_reset_ops; + data->rcdev.of_node = np; + + return reset_controller_register(&data->rcdev); +} + +static const struct of_device_id socfpga_reset_dt_ids[] = { + { .compatible = "altr,rst-mgr", }, + { /* sentinel */ }, +}; + +static struct driver_d socfpga_reset_driver = { + .probe = socfpga_reset_probe, + .of_compatible = DRV_OF_COMPAT(socfpga_reset_dt_ids), +}; + +static int socfpga_reset_init(void) +{ + return platform_driver_register(&socfpga_reset_driver); +} +postcore_initcall(socfpga_reset_init); -- cgit v1.2.3 From 9ec4f328f64c9872bce3cd076ab9a457cdfad925 Mon Sep 17 00:00:00 2001 From: Steffen Trumtrar Date: Mon, 17 Oct 2016 09:50:51 +0200 Subject: watchdog: add designware driver Port the linux v4.8-rc1 Synopsys DesignWare watchdog driver to barebox. Signed-off-by: Steffen Trumtrar Signed-off-by: Sascha Hauer --- drivers/watchdog/Kconfig | 6 ++ drivers/watchdog/Makefile | 1 + drivers/watchdog/dw_wdt.c | 193 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 200 insertions(+) create mode 100644 drivers/watchdog/dw_wdt.c (limited to 'drivers') diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 60a56bf4b0..63fb1a8c57 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -16,6 +16,12 @@ config WATCHDOG_DAVINCI help Add support for watchdog on the TI Davinci SoC. +config WATCHDOG_DW + bool "Synopsys DesignWare watchdog" + select RESET_CONTROLLER + help + Add support for the Synopsys DesignWare watchdog timer. + config WATCHDOG_MXS28 bool "i.MX28" depends on ARCH_IMX28 diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index e3afe1c27e..5fca4c368c 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -2,5 +2,6 @@ obj-$(CONFIG_WATCHDOG) += wd_core.o obj-$(CONFIG_WATCHDOG_DAVINCI) += davinci_wdt.o obj-$(CONFIG_WATCHDOG_OMAP) += omap_wdt.o obj-$(CONFIG_WATCHDOG_MXS28) += im28wd.o +obj-$(CONFIG_WATCHDOG_DW) += dw_wdt.o obj-$(CONFIG_WATCHDOG_JZ4740) += jz4740.o obj-$(CONFIG_WATCHDOG_IMX_RESET_SOURCE) += imxwd.o diff --git a/drivers/watchdog/dw_wdt.c b/drivers/watchdog/dw_wdt.c new file mode 100644 index 0000000000..8fd8c81e6c --- /dev/null +++ b/drivers/watchdog/dw_wdt.c @@ -0,0 +1,193 @@ +/* + * Copyright 2010-2011 Picochip Ltd., Jamie Iles + * http://www.picochip.com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * This file implements a driver for the Synopsys DesignWare watchdog device + * in the many subsystems. The watchdog has 16 different timeout periods + * and these are a function of the input clock frequency. + * + * The DesignWare watchdog cannot be stopped once it has been started so we + * do not implement a stop function. The watchdog core will continue to send + * heartbeat requests after the watchdog device has been closed. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define WDOG_CONTROL_REG_OFFSET 0x00 +#define WDOG_CONTROL_REG_WDT_EN_MASK 0x01 +#define WDOG_TIMEOUT_RANGE_REG_OFFSET 0x04 +#define WDOG_TIMEOUT_RANGE_TOPINIT_SHIFT 4 +#define WDOG_CURRENT_COUNT_REG_OFFSET 0x08 +#define WDOG_COUNTER_RESTART_REG_OFFSET 0x0c +#define WDOG_COUNTER_RESTART_KICK_VALUE 0x76 + +/* The maximum TOP (timeout period) value that can be set in the watchdog. */ +#define DW_WDT_MAX_TOP 15 + +#define DW_WDT_DEFAULT_SECONDS 30 + +struct dw_wdt { + void __iomem *regs; + struct clk *clk; + struct restart_handler restart; + struct watchdog wdd; + struct reset_control *rst; +}; + +#define to_dw_wdt(wdd) container_of(wdd, struct dw_wdt, wdd) + +static inline int dw_wdt_top_in_seconds(struct dw_wdt *dw_wdt, unsigned top) +{ + /* + * There are 16 possible timeout values in 0..15 where the number of + * cycles is 2 ^ (16 + i) and the watchdog counts down. + */ + return (1U << (16 + top)) / clk_get_rate(dw_wdt->clk); +} + +static int dw_wdt_start(struct watchdog *wdd) +{ + struct dw_wdt *dw_wdt = to_dw_wdt(wdd); + + writel(WDOG_CONTROL_REG_WDT_EN_MASK, + dw_wdt->regs + WDOG_CONTROL_REG_OFFSET); + + return 0; +} + +static int dw_wdt_stop(struct watchdog *wdd) +{ + struct dw_wdt *dw_wdt = to_dw_wdt(wdd); + + if (IS_ERR(dw_wdt->rst)) { + dev_warn(dw_wdt->wdd.dev, "No reset line. Will not stop.\n"); + return PTR_ERR(dw_wdt->rst); + } + + reset_control_assert(dw_wdt->rst); + reset_control_deassert(dw_wdt->rst); + + return 0; +} + +static int dw_wdt_set_timeout(struct watchdog *wdd, unsigned int top_s) +{ + struct dw_wdt *dw_wdt = to_dw_wdt(wdd); + int i, top_val = DW_WDT_MAX_TOP; + + if (top_s == 0) + return dw_wdt_stop(wdd); + + /* + * Iterate over the timeout values until we find the closest match. We + * always look for >=. + */ + for (i = 0; i <= DW_WDT_MAX_TOP; ++i) { + if (dw_wdt_top_in_seconds(dw_wdt, i) >= top_s) { + top_val = i; + break; + } + } + + /* + * Set the new value in the watchdog. Some versions of dw_wdt + * have have TOPINIT in the TIMEOUT_RANGE register (as per + * CP_WDT_DUAL_TOP in WDT_COMP_PARAMS_1). On those we + * effectively get a pat of the watchdog right here. + */ + writel(top_val | top_val << WDOG_TIMEOUT_RANGE_TOPINIT_SHIFT, + dw_wdt->regs + WDOG_TIMEOUT_RANGE_REG_OFFSET); + + dw_wdt_start(wdd); + + return 0; +} + +static void __noreturn dw_wdt_restart_handle(struct restart_handler *rst) +{ + struct dw_wdt *dw_wdt; + + dw_wdt = container_of(rst, struct dw_wdt, restart); + + dw_wdt->wdd.set_timeout(&dw_wdt->wdd, -1); + + mdelay(1000); + + hang(); +} + +static int dw_wdt_drv_probe(struct device_d *dev) +{ + struct watchdog *wdd; + struct dw_wdt *dw_wdt; + struct resource *mem; + int ret; + + dw_wdt = xzalloc(sizeof(*dw_wdt)); + + mem = dev_request_mem_resource(dev, 0); + dw_wdt->regs = IOMEM(mem->start); + if (IS_ERR(dw_wdt->regs)) + return PTR_ERR(dw_wdt->regs); + + dw_wdt->clk = clk_get(dev, NULL); + if (IS_ERR(dw_wdt->clk)) + return PTR_ERR(dw_wdt->clk); + + ret = clk_enable(dw_wdt->clk); + if (ret) + return ret; + + dw_wdt->rst = reset_control_get(dev, NULL); + if (IS_ERR(dw_wdt->rst)) + dev_warn(dev, "No reset lines. Will not be able to stop once started.\n"); + + wdd = &dw_wdt->wdd; + wdd->name = "dw_wdt"; + wdd->dev = dev; + wdd->set_timeout = dw_wdt_set_timeout; + + ret = watchdog_register(wdd); + if (ret) + goto out_disable_clk; + + dw_wdt->restart.name = "dw_wdt"; + dw_wdt->restart.restart = dw_wdt_restart_handle; + + ret = restart_handler_register(&dw_wdt->restart); + if (ret) + dev_warn(dev, "cannot register restart handler\n"); + + if (!IS_ERR(dw_wdt->rst)) + reset_control_deassert(dw_wdt->rst); + + return 0; + +out_disable_clk: + clk_disable(dw_wdt->clk); + return ret; +} + +static struct of_device_id dw_wdt_of_match[] = { + { .compatible = "snps,dw-wdt", }, + { /* sentinel */ } +}; + +static struct driver_d dw_wdt_driver = { + .probe = dw_wdt_drv_probe, + .of_compatible = DRV_OF_COMPAT(dw_wdt_of_match), +}; +device_platform_driver(dw_wdt_driver); -- cgit v1.2.3