From: Joachim Eastwood <manabian-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
To: p.zabel-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org
Cc: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org,
devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
Joachim Eastwood
<manabian-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
Subject: [PATCH v3 1/2] reset: add driver for lpc18xx rgu
Date: Wed, 6 May 2015 00:10:26 +0200 [thread overview]
Message-ID: <1430863827-21898-2-git-send-email-manabian@gmail.com> (raw)
In-Reply-To: <1430863827-21898-1-git-send-email-manabian-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
Add reset driver for the Reset Generation Unit (RGU) found on NXP
LPC18xx and LPC43xx devies. This reset controller features up to 64
reset lines connected to different blocks and peripheral in the SoC.
Most reset lines on the controller are self clearing except for
those dealing with the Cortex-M0 cores on LPC43xx devices.
This driver also registers a restart handler that can be used to
reset the entire device.
Signed-off-by: Joachim Eastwood <manabian-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
---
drivers/reset/Makefile | 1 +
drivers/reset/reset-lpc18xx.c | 258 ++++++++++++++++++++++++++++++++++++++++++
2 files changed, 259 insertions(+)
create mode 100644 drivers/reset/reset-lpc18xx.c
diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile
index 157d421f755b..1d41feeb2dce 100644
--- a/drivers/reset/Makefile
+++ b/drivers/reset/Makefile
@@ -1,4 +1,5 @@
obj-$(CONFIG_RESET_CONTROLLER) += core.o
+obj-$(CONFIG_ARCH_LPC18XX) += reset-lpc18xx.o
obj-$(CONFIG_ARCH_SOCFPGA) += reset-socfpga.o
obj-$(CONFIG_ARCH_BERLIN) += reset-berlin.o
obj-$(CONFIG_ARCH_SUNXI) += reset-sunxi.o
diff --git a/drivers/reset/reset-lpc18xx.c b/drivers/reset/reset-lpc18xx.c
new file mode 100644
index 000000000000..70922e9ac27f
--- /dev/null
+++ b/drivers/reset/reset-lpc18xx.c
@@ -0,0 +1,258 @@
+/*
+ * Reset driver for NXP LPC18xx/43xx Reset Generation Unit (RGU).
+ *
+ * Copyright (C) 2015 Joachim Eastwood <manabian-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
+ *
+ * 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.
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/reboot.h>
+#include <linux/reset-controller.h>
+#include <linux/spinlock.h>
+
+/* LPC18xx RGU registers */
+#define LPC18XX_RGU_CTRL0 0x100
+#define LPC18XX_RGU_CTRL1 0x104
+#define LPC18XX_RGU_ACTIVE_STATUS0 0x150
+#define LPC18XX_RGU_ACTIVE_STATUS1 0x154
+
+#define LPC18XX_RGU_RESETS_PER_REG 32
+
+/* Internal reset outputs */
+#define LPC18XX_RGU_CORE_RST 0
+#define LPC43XX_RGU_M0SUB_RST 12
+#define LPC43XX_RGU_M0APP_RST 56
+
+struct lpc18xx_rgu_data {
+ struct reset_controller_dev rcdev;
+ struct clk *clk_delay;
+ struct clk *clk_reg;
+ void __iomem *base;
+ spinlock_t lock;
+ u32 delay_us;
+};
+
+#define to_rgu_data(p) container_of(p, struct lpc18xx_rgu_data, rcdev)
+
+static void __iomem *rgu_base;
+
+static int lpc18xx_rgu_restart(struct notifier_block *this, unsigned long mode,
+ void *cmd)
+{
+ writel(BIT(LPC18XX_RGU_CORE_RST), rgu_base + LPC18XX_RGU_CTRL0);
+ mdelay(2000);
+
+ pr_emerg("%s: unable to restart system\n", __func__);
+
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block lpc18xx_rgu_restart_nb = {
+ .notifier_call = lpc18xx_rgu_restart,
+ .priority = 192,
+};
+
+/*
+ * The LPC18xx RGU has mostly self-deasserting resets except for the
+ * two reset lines going to the internal Cortex-M0 cores.
+ *
+ * To prevent the M0 core resets from accidentally getting deasserted
+ * status register must be check and bits in control register set to
+ * preserve the state.
+ */
+static int lpc18xx_rgu_setclear_reset(struct reset_controller_dev *rcdev,
+ unsigned long id, bool set)
+{
+ struct lpc18xx_rgu_data *rc = to_rgu_data(rcdev);
+ u32 stat_offset = LPC18XX_RGU_ACTIVE_STATUS0;
+ u32 ctrl_offset = LPC18XX_RGU_CTRL0;
+ unsigned long flags;
+ u32 stat, rst_bit;
+
+ stat_offset += (id / LPC18XX_RGU_RESETS_PER_REG) * sizeof(u32);
+ ctrl_offset += (id / LPC18XX_RGU_RESETS_PER_REG) * sizeof(u32);
+ rst_bit = 1 << (id % LPC18XX_RGU_RESETS_PER_REG);
+
+ spin_lock_irqsave(&rc->lock, flags);
+ stat = ~readl(rc->base + stat_offset);
+ if (set)
+ writel(stat | rst_bit, rc->base + ctrl_offset);
+ else
+ writel(stat & ~rst_bit, rc->base + ctrl_offset);
+ spin_unlock_irqrestore(&rc->lock, flags);
+
+ return 0;
+}
+
+static int lpc18xx_rgu_assert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ return lpc18xx_rgu_setclear_reset(rcdev, id, true);
+}
+
+static int lpc18xx_rgu_deassert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ return lpc18xx_rgu_setclear_reset(rcdev, id, false);
+}
+
+/* Only M0 cores require explicit reset deassert */
+static int lpc18xx_rgu_reset(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ struct lpc18xx_rgu_data *rc = to_rgu_data(rcdev);
+
+ lpc18xx_rgu_assert(rcdev, id);
+ udelay(rc->delay_us);
+
+ switch (id) {
+ case LPC43XX_RGU_M0SUB_RST:
+ case LPC43XX_RGU_M0APP_RST:
+ lpc18xx_rgu_setclear_reset(rcdev, id, false);
+ }
+
+ return 0;
+}
+
+static int lpc18xx_rgu_status(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ struct lpc18xx_rgu_data *rc = to_rgu_data(rcdev);
+ u32 bit, offset = LPC18XX_RGU_ACTIVE_STATUS0;
+
+ offset += (id / LPC18XX_RGU_RESETS_PER_REG) * sizeof(u32);
+ bit = 1 << (id % LPC18XX_RGU_RESETS_PER_REG);
+
+ return !(readl(rc->base + offset) & bit);
+}
+
+static struct reset_control_ops lpc18xx_rgu_ops = {
+ .reset = lpc18xx_rgu_reset,
+ .assert = lpc18xx_rgu_assert,
+ .deassert = lpc18xx_rgu_deassert,
+ .status = lpc18xx_rgu_status,
+};
+
+static int lpc18xx_rgu_probe(struct platform_device *pdev)
+{
+ struct lpc18xx_rgu_data *rc;
+ struct resource *res;
+ u32 fcclk, firc;
+ int ret;
+
+ rc = devm_kzalloc(&pdev->dev, sizeof(*rc), GFP_KERNEL);
+ if (!rc)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ rc->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(rc->base))
+ return PTR_ERR(rc->base);
+
+ rc->clk_reg = devm_clk_get(&pdev->dev, "reg");
+ if (IS_ERR(rc->clk_reg)) {
+ dev_err(&pdev->dev, "reg clock not found\n");
+ return PTR_ERR(rc->clk_reg);
+ }
+
+ rc->clk_delay = devm_clk_get(&pdev->dev, "delay");
+ if (IS_ERR(rc->clk_delay)) {
+ dev_err(&pdev->dev, "delay clock not found\n");
+ return PTR_ERR(rc->clk_delay);
+ }
+
+ ret = clk_prepare_enable(rc->clk_reg);
+ if (ret) {
+ dev_err(&pdev->dev, "unable to enable reg clock\n");
+ return ret;
+ }
+
+ ret = clk_prepare_enable(rc->clk_delay);
+ if (ret) {
+ dev_err(&pdev->dev, "unable to enable delay clock\n");
+ goto dis_clk_reg;
+ }
+
+ fcclk = clk_get_rate(rc->clk_reg) / USEC_PER_SEC;
+ firc = clk_get_rate(rc->clk_delay) / USEC_PER_SEC;
+ if (fcclk == 0 || firc == 0)
+ rc->delay_us = 2;
+ else
+ rc->delay_us = DIV_ROUND_UP(fcclk, firc * firc);
+
+ spin_lock_init(&rc->lock);
+
+ rc->rcdev.owner = THIS_MODULE;
+ rc->rcdev.nr_resets = 64;
+ rc->rcdev.ops = &lpc18xx_rgu_ops;
+ rc->rcdev.of_node = pdev->dev.of_node;
+
+ platform_set_drvdata(pdev, rc);
+
+ ret = reset_controller_register(&rc->rcdev);
+ if (ret) {
+ dev_err(&pdev->dev, "unable to register device\n");
+ goto dis_clks;
+ }
+
+ rgu_base = rc->base;
+ ret = register_restart_handler(&lpc18xx_rgu_restart_nb);
+ if (ret)
+ dev_warn(&pdev->dev, "failed to register restart handler\n");
+
+ return 0;
+
+dis_clks:
+ clk_disable_unprepare(rc->clk_delay);
+dis_clk_reg:
+ clk_disable_unprepare(rc->clk_reg);
+
+ return ret;
+}
+
+static int lpc18xx_rgu_remove(struct platform_device *pdev)
+{
+ struct lpc18xx_rgu_data *rc = platform_get_drvdata(pdev);
+ int ret;
+
+ ret = unregister_restart_handler(&lpc18xx_rgu_restart_nb);
+ if (ret)
+ dev_warn(&pdev->dev, "failed to unregister restart handler\n");
+
+ reset_controller_unregister(&rc->rcdev);
+
+ clk_disable_unprepare(rc->clk_delay);
+ clk_disable_unprepare(rc->clk_reg);
+
+ return 0;
+}
+
+static const struct of_device_id lpc18xx_rgu_match[] = {
+ { .compatible = "nxp,lpc1850-rgu" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, lpc18xx_rgu_match);
+
+static struct platform_driver lpc18xx_rgu_driver = {
+ .probe = lpc18xx_rgu_probe,
+ .remove = lpc18xx_rgu_remove,
+ .driver = {
+ .name = "lpc18xx-reset",
+ .of_match_table = lpc18xx_rgu_match,
+ },
+};
+module_platform_driver(lpc18xx_rgu_driver);
+
+MODULE_AUTHOR("Joachim Eastwood <manabian-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>");
+MODULE_DESCRIPTION("Reset driver for LPC18xx/43xx RGU");
+MODULE_LICENSE("GPL v2");
--
1.8.0
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
next prev parent reply other threads:[~2015-05-05 22:10 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2015-05-05 22:10 [PATCH v3 0/2] reset driver for NXP LPC18xx family Joachim Eastwood
[not found] ` <1430863827-21898-1-git-send-email-manabian-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
2015-05-05 22:10 ` Joachim Eastwood [this message]
2015-05-05 22:10 ` [PATCH v3 2/2] doc: dt: add documentation for lpc1850-rgu reset driver Joachim Eastwood
2015-05-07 13:01 ` [PATCH v3 0/2] reset driver for NXP LPC18xx family Philipp Zabel
[not found] ` <1431003665.3014.36.camel-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
2015-06-06 5:52 ` Ezequiel Garcia
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1430863827-21898-2-git-send-email-manabian@gmail.com \
--to=manabian-re5jqeeqqe8avxtiumwx3w@public.gmane.org \
--cc=devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org \
--cc=linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org \
--cc=p.zabel-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).