162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Reset driver for NXP LPC18xx/43xx Reset Generation Unit (RGU).
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2015 Joachim Eastwood <manabian@gmail.com>
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/clk.h>
962306a36Sopenharmony_ci#include <linux/delay.h>
1062306a36Sopenharmony_ci#include <linux/err.h>
1162306a36Sopenharmony_ci#include <linux/io.h>
1262306a36Sopenharmony_ci#include <linux/init.h>
1362306a36Sopenharmony_ci#include <linux/of.h>
1462306a36Sopenharmony_ci#include <linux/platform_device.h>
1562306a36Sopenharmony_ci#include <linux/reboot.h>
1662306a36Sopenharmony_ci#include <linux/reset-controller.h>
1762306a36Sopenharmony_ci#include <linux/spinlock.h>
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci/* LPC18xx RGU registers */
2062306a36Sopenharmony_ci#define LPC18XX_RGU_CTRL0		0x100
2162306a36Sopenharmony_ci#define LPC18XX_RGU_CTRL1		0x104
2262306a36Sopenharmony_ci#define LPC18XX_RGU_ACTIVE_STATUS0	0x150
2362306a36Sopenharmony_ci#define LPC18XX_RGU_ACTIVE_STATUS1	0x154
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci#define LPC18XX_RGU_RESETS_PER_REG	32
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci/* Internal reset outputs */
2862306a36Sopenharmony_ci#define LPC18XX_RGU_CORE_RST	0
2962306a36Sopenharmony_ci#define LPC43XX_RGU_M0SUB_RST	12
3062306a36Sopenharmony_ci#define LPC43XX_RGU_M0APP_RST	56
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_cistruct lpc18xx_rgu_data {
3362306a36Sopenharmony_ci	struct reset_controller_dev rcdev;
3462306a36Sopenharmony_ci	struct notifier_block restart_nb;
3562306a36Sopenharmony_ci	struct clk *clk_delay;
3662306a36Sopenharmony_ci	struct clk *clk_reg;
3762306a36Sopenharmony_ci	void __iomem *base;
3862306a36Sopenharmony_ci	spinlock_t lock;
3962306a36Sopenharmony_ci	u32 delay_us;
4062306a36Sopenharmony_ci};
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci#define to_rgu_data(p) container_of(p, struct lpc18xx_rgu_data, rcdev)
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_cistatic int lpc18xx_rgu_restart(struct notifier_block *nb, unsigned long mode,
4562306a36Sopenharmony_ci			       void *cmd)
4662306a36Sopenharmony_ci{
4762306a36Sopenharmony_ci	struct lpc18xx_rgu_data *rc = container_of(nb, struct lpc18xx_rgu_data,
4862306a36Sopenharmony_ci						   restart_nb);
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci	writel(BIT(LPC18XX_RGU_CORE_RST), rc->base + LPC18XX_RGU_CTRL0);
5162306a36Sopenharmony_ci	mdelay(2000);
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	pr_emerg("%s: unable to restart system\n", __func__);
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	return NOTIFY_DONE;
5662306a36Sopenharmony_ci}
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci/*
5962306a36Sopenharmony_ci * The LPC18xx RGU has mostly self-deasserting resets except for the
6062306a36Sopenharmony_ci * two reset lines going to the internal Cortex-M0 cores.
6162306a36Sopenharmony_ci *
6262306a36Sopenharmony_ci * To prevent the M0 core resets from accidentally getting deasserted
6362306a36Sopenharmony_ci * status register must be check and bits in control register set to
6462306a36Sopenharmony_ci * preserve the state.
6562306a36Sopenharmony_ci */
6662306a36Sopenharmony_cistatic int lpc18xx_rgu_setclear_reset(struct reset_controller_dev *rcdev,
6762306a36Sopenharmony_ci				      unsigned long id, bool set)
6862306a36Sopenharmony_ci{
6962306a36Sopenharmony_ci	struct lpc18xx_rgu_data *rc = to_rgu_data(rcdev);
7062306a36Sopenharmony_ci	u32 stat_offset = LPC18XX_RGU_ACTIVE_STATUS0;
7162306a36Sopenharmony_ci	u32 ctrl_offset = LPC18XX_RGU_CTRL0;
7262306a36Sopenharmony_ci	unsigned long flags;
7362306a36Sopenharmony_ci	u32 stat, rst_bit;
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	stat_offset += (id / LPC18XX_RGU_RESETS_PER_REG) * sizeof(u32);
7662306a36Sopenharmony_ci	ctrl_offset += (id / LPC18XX_RGU_RESETS_PER_REG) * sizeof(u32);
7762306a36Sopenharmony_ci	rst_bit = 1 << (id % LPC18XX_RGU_RESETS_PER_REG);
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	spin_lock_irqsave(&rc->lock, flags);
8062306a36Sopenharmony_ci	stat = ~readl(rc->base + stat_offset);
8162306a36Sopenharmony_ci	if (set)
8262306a36Sopenharmony_ci		writel(stat | rst_bit, rc->base + ctrl_offset);
8362306a36Sopenharmony_ci	else
8462306a36Sopenharmony_ci		writel(stat & ~rst_bit, rc->base + ctrl_offset);
8562306a36Sopenharmony_ci	spin_unlock_irqrestore(&rc->lock, flags);
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	return 0;
8862306a36Sopenharmony_ci}
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_cistatic int lpc18xx_rgu_assert(struct reset_controller_dev *rcdev,
9162306a36Sopenharmony_ci			      unsigned long id)
9262306a36Sopenharmony_ci{
9362306a36Sopenharmony_ci	return lpc18xx_rgu_setclear_reset(rcdev, id, true);
9462306a36Sopenharmony_ci}
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_cistatic int lpc18xx_rgu_deassert(struct reset_controller_dev *rcdev,
9762306a36Sopenharmony_ci				unsigned long id)
9862306a36Sopenharmony_ci{
9962306a36Sopenharmony_ci	return lpc18xx_rgu_setclear_reset(rcdev, id, false);
10062306a36Sopenharmony_ci}
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci/* Only M0 cores require explicit reset deassert */
10362306a36Sopenharmony_cistatic int lpc18xx_rgu_reset(struct reset_controller_dev *rcdev,
10462306a36Sopenharmony_ci			     unsigned long id)
10562306a36Sopenharmony_ci{
10662306a36Sopenharmony_ci	struct lpc18xx_rgu_data *rc = to_rgu_data(rcdev);
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	lpc18xx_rgu_assert(rcdev, id);
10962306a36Sopenharmony_ci	udelay(rc->delay_us);
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	switch (id) {
11262306a36Sopenharmony_ci	case LPC43XX_RGU_M0SUB_RST:
11362306a36Sopenharmony_ci	case LPC43XX_RGU_M0APP_RST:
11462306a36Sopenharmony_ci		lpc18xx_rgu_setclear_reset(rcdev, id, false);
11562306a36Sopenharmony_ci	}
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	return 0;
11862306a36Sopenharmony_ci}
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_cistatic int lpc18xx_rgu_status(struct reset_controller_dev *rcdev,
12162306a36Sopenharmony_ci			      unsigned long id)
12262306a36Sopenharmony_ci{
12362306a36Sopenharmony_ci	struct lpc18xx_rgu_data *rc = to_rgu_data(rcdev);
12462306a36Sopenharmony_ci	u32 bit, offset = LPC18XX_RGU_ACTIVE_STATUS0;
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	offset += (id / LPC18XX_RGU_RESETS_PER_REG) * sizeof(u32);
12762306a36Sopenharmony_ci	bit = 1 << (id % LPC18XX_RGU_RESETS_PER_REG);
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	return !(readl(rc->base + offset) & bit);
13062306a36Sopenharmony_ci}
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_cistatic const struct reset_control_ops lpc18xx_rgu_ops = {
13362306a36Sopenharmony_ci	.reset		= lpc18xx_rgu_reset,
13462306a36Sopenharmony_ci	.assert		= lpc18xx_rgu_assert,
13562306a36Sopenharmony_ci	.deassert	= lpc18xx_rgu_deassert,
13662306a36Sopenharmony_ci	.status		= lpc18xx_rgu_status,
13762306a36Sopenharmony_ci};
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_cistatic int lpc18xx_rgu_probe(struct platform_device *pdev)
14062306a36Sopenharmony_ci{
14162306a36Sopenharmony_ci	struct lpc18xx_rgu_data *rc;
14262306a36Sopenharmony_ci	u32 fcclk, firc;
14362306a36Sopenharmony_ci	int ret;
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	rc = devm_kzalloc(&pdev->dev, sizeof(*rc), GFP_KERNEL);
14662306a36Sopenharmony_ci	if (!rc)
14762306a36Sopenharmony_ci		return -ENOMEM;
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	rc->base = devm_platform_ioremap_resource(pdev, 0);
15062306a36Sopenharmony_ci	if (IS_ERR(rc->base))
15162306a36Sopenharmony_ci		return PTR_ERR(rc->base);
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	rc->clk_reg = devm_clk_get(&pdev->dev, "reg");
15462306a36Sopenharmony_ci	if (IS_ERR(rc->clk_reg)) {
15562306a36Sopenharmony_ci		dev_err(&pdev->dev, "reg clock not found\n");
15662306a36Sopenharmony_ci		return PTR_ERR(rc->clk_reg);
15762306a36Sopenharmony_ci	}
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	rc->clk_delay = devm_clk_get(&pdev->dev, "delay");
16062306a36Sopenharmony_ci	if (IS_ERR(rc->clk_delay)) {
16162306a36Sopenharmony_ci		dev_err(&pdev->dev, "delay clock not found\n");
16262306a36Sopenharmony_ci		return PTR_ERR(rc->clk_delay);
16362306a36Sopenharmony_ci	}
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	ret = clk_prepare_enable(rc->clk_reg);
16662306a36Sopenharmony_ci	if (ret) {
16762306a36Sopenharmony_ci		dev_err(&pdev->dev, "unable to enable reg clock\n");
16862306a36Sopenharmony_ci		return ret;
16962306a36Sopenharmony_ci	}
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	ret = clk_prepare_enable(rc->clk_delay);
17262306a36Sopenharmony_ci	if (ret) {
17362306a36Sopenharmony_ci		dev_err(&pdev->dev, "unable to enable delay clock\n");
17462306a36Sopenharmony_ci		goto dis_clk_reg;
17562306a36Sopenharmony_ci	}
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	fcclk = clk_get_rate(rc->clk_reg) / USEC_PER_SEC;
17862306a36Sopenharmony_ci	firc = clk_get_rate(rc->clk_delay) / USEC_PER_SEC;
17962306a36Sopenharmony_ci	if (fcclk == 0 || firc == 0)
18062306a36Sopenharmony_ci		rc->delay_us = 2;
18162306a36Sopenharmony_ci	else
18262306a36Sopenharmony_ci		rc->delay_us = DIV_ROUND_UP(fcclk, firc * firc);
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	spin_lock_init(&rc->lock);
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	rc->rcdev.owner = THIS_MODULE;
18762306a36Sopenharmony_ci	rc->rcdev.nr_resets = 64;
18862306a36Sopenharmony_ci	rc->rcdev.ops = &lpc18xx_rgu_ops;
18962306a36Sopenharmony_ci	rc->rcdev.of_node = pdev->dev.of_node;
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	ret = reset_controller_register(&rc->rcdev);
19262306a36Sopenharmony_ci	if (ret) {
19362306a36Sopenharmony_ci		dev_err(&pdev->dev, "unable to register device\n");
19462306a36Sopenharmony_ci		goto dis_clks;
19562306a36Sopenharmony_ci	}
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	rc->restart_nb.priority = 192,
19862306a36Sopenharmony_ci	rc->restart_nb.notifier_call = lpc18xx_rgu_restart,
19962306a36Sopenharmony_ci	ret = register_restart_handler(&rc->restart_nb);
20062306a36Sopenharmony_ci	if (ret)
20162306a36Sopenharmony_ci		dev_warn(&pdev->dev, "failed to register restart handler\n");
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	return 0;
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_cidis_clks:
20662306a36Sopenharmony_ci	clk_disable_unprepare(rc->clk_delay);
20762306a36Sopenharmony_cidis_clk_reg:
20862306a36Sopenharmony_ci	clk_disable_unprepare(rc->clk_reg);
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	return ret;
21162306a36Sopenharmony_ci}
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_cistatic const struct of_device_id lpc18xx_rgu_match[] = {
21462306a36Sopenharmony_ci	{ .compatible = "nxp,lpc1850-rgu" },
21562306a36Sopenharmony_ci	{ }
21662306a36Sopenharmony_ci};
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_cistatic struct platform_driver lpc18xx_rgu_driver = {
21962306a36Sopenharmony_ci	.probe	= lpc18xx_rgu_probe,
22062306a36Sopenharmony_ci	.driver	= {
22162306a36Sopenharmony_ci		.name			= "lpc18xx-reset",
22262306a36Sopenharmony_ci		.of_match_table		= lpc18xx_rgu_match,
22362306a36Sopenharmony_ci		.suppress_bind_attrs	= true,
22462306a36Sopenharmony_ci	},
22562306a36Sopenharmony_ci};
22662306a36Sopenharmony_cibuiltin_platform_driver(lpc18xx_rgu_driver);
227