1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Copyright (c) 2014 MundoReader S.L.
4 * Author: Heiko Stuebner <heiko@sntech.de>
5 */
6
7#include <linux/slab.h>
8#include <linux/io.h>
9#include <linux/reset-controller.h>
10#include <linux/spinlock.h>
11#include "clk.h"
12
13struct rockchip_softrst {
14	struct reset_controller_dev	rcdev;
15	void __iomem			*reg_base;
16	int				num_regs;
17	int				num_per_reg;
18	u8				flags;
19	spinlock_t			lock;
20};
21
22static int rockchip_softrst_assert(struct reset_controller_dev *rcdev,
23			      unsigned long id)
24{
25	struct rockchip_softrst *softrst = container_of(rcdev,
26						     struct rockchip_softrst,
27						     rcdev);
28	int bank = id / softrst->num_per_reg;
29	int offset = id % softrst->num_per_reg;
30
31	if (softrst->flags & ROCKCHIP_SOFTRST_HIWORD_MASK) {
32		writel(BIT(offset) | (BIT(offset) << 16),
33		       softrst->reg_base + (bank * 4));
34	} else {
35		unsigned long flags;
36		u32 reg;
37
38		spin_lock_irqsave(&softrst->lock, flags);
39
40		reg = readl(softrst->reg_base + (bank * 4));
41		writel(reg | BIT(offset), softrst->reg_base + (bank * 4));
42
43		spin_unlock_irqrestore(&softrst->lock, flags);
44	}
45
46	return 0;
47}
48
49static int rockchip_softrst_deassert(struct reset_controller_dev *rcdev,
50				unsigned long id)
51{
52	struct rockchip_softrst *softrst = container_of(rcdev,
53						     struct rockchip_softrst,
54						     rcdev);
55	int bank = id / softrst->num_per_reg;
56	int offset = id % softrst->num_per_reg;
57
58	if (softrst->flags & ROCKCHIP_SOFTRST_HIWORD_MASK) {
59		writel((BIT(offset) << 16), softrst->reg_base + (bank * 4));
60	} else {
61		unsigned long flags;
62		u32 reg;
63
64		spin_lock_irqsave(&softrst->lock, flags);
65
66		reg = readl(softrst->reg_base + (bank * 4));
67		writel(reg & ~BIT(offset), softrst->reg_base + (bank * 4));
68
69		spin_unlock_irqrestore(&softrst->lock, flags);
70	}
71
72	return 0;
73}
74
75static const struct reset_control_ops rockchip_softrst_ops = {
76	.assert		= rockchip_softrst_assert,
77	.deassert	= rockchip_softrst_deassert,
78};
79
80void rockchip_register_softrst(struct device_node *np,
81			       unsigned int num_regs,
82			       void __iomem *base, u8 flags)
83{
84	struct rockchip_softrst *softrst;
85	int ret;
86
87	softrst = kzalloc(sizeof(*softrst), GFP_KERNEL);
88	if (!softrst)
89		return;
90
91	spin_lock_init(&softrst->lock);
92
93	softrst->reg_base = base;
94	softrst->flags = flags;
95	softrst->num_regs = num_regs;
96	softrst->num_per_reg = (flags & ROCKCHIP_SOFTRST_HIWORD_MASK) ? 16
97								      : 32;
98
99	softrst->rcdev.owner = THIS_MODULE;
100	softrst->rcdev.nr_resets =  num_regs * softrst->num_per_reg;
101	softrst->rcdev.ops = &rockchip_softrst_ops;
102	softrst->rcdev.of_node = np;
103	ret = reset_controller_register(&softrst->rcdev);
104	if (ret) {
105		pr_err("%s: could not register reset controller, %d\n",
106		       __func__, ret);
107		kfree(softrst);
108	}
109};
110EXPORT_SYMBOL_GPL(rockchip_register_softrst);
111