1// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Copyright (C) 2022 StarFive Technology Co., Ltd.
4 * Author:	Yanhong Wang <yanhong.wang@starfivetech.com>
5 *
6 */
7
8#include <dm.h>
9#include <dm/ofnode.h>
10#include <dt-bindings/reset/starfive,jh7110-crg.h>
11#include <errno.h>
12#include <linux/iopoll.h>
13#include <reset-uclass.h>
14
15struct jh7110_reset_priv {
16	void __iomem *reg;
17	u32	assert;
18	u32	status;
19	u32	resets;
20};
21
22struct reset_info {
23	const char *compat;
24	const u32 nr_resets;
25	const u32 assert_offset;
26	const u32 status_offset;
27};
28
29static const struct reset_info jh7110_rst_info[] = {
30	{
31		.compat = "starfive,jh7110-syscrg",
32		.nr_resets = JH7110_SYSRST_END,
33		.assert_offset = 0x2F8,
34		.status_offset = 0x308,
35	},
36	{
37		.compat = "starfive,jh7110-aoncrg",
38		.nr_resets = JH7110_AONRST_END,
39		.assert_offset = 0x38,
40		.status_offset = 0x3C,
41	},
42	{
43		.compat = "starfive,jh7110-stgcrg",
44		.nr_resets = JH7110_STGRST_END,
45		.assert_offset = 0x74,
46		.status_offset = 0x78,
47	}
48};
49
50static const struct reset_info *jh7110_reset_get_cfg(const char *compat)
51{
52	int i;
53
54	for (i = 0; i < ARRAY_SIZE(jh7110_rst_info); i++)
55		if (!strcmp(compat, jh7110_rst_info[i].compat))
56			return &jh7110_rst_info[i];
57
58	return NULL;
59}
60
61static int jh7110_reset_trigger(struct jh7110_reset_priv *priv,
62				unsigned long id, bool assert)
63{
64	ulong group;
65	u32 mask, value, done = 0;
66	ulong addr;
67
68	group = id / 32;
69	mask = BIT(id % 32);
70
71	if (!assert)
72		done ^= mask;
73
74	addr = (ulong)priv->reg + priv->assert + group * sizeof(u32);
75	value = readl((ulong *)addr);
76
77	if (assert)
78		value |= mask;
79	else
80		value &= ~mask;
81
82	writel(value, (ulong *)addr);
83	addr = (ulong)priv->reg + priv->status + group * sizeof(u32);
84
85	return readl_poll_timeout((ulong *)addr, value,
86						(value & mask) == done, 1000);
87}
88
89static int jh7110_reset_assert(struct reset_ctl *rst)
90{
91	struct jh7110_reset_priv *priv = dev_get_priv(rst->dev);
92
93	jh7110_reset_trigger(priv, rst->id, true);
94
95	return 0;
96}
97
98static int jh7110_reset_deassert(struct reset_ctl *rst)
99{
100	struct jh7110_reset_priv *priv = dev_get_priv(rst->dev);
101
102	jh7110_reset_trigger(priv, rst->id, false);
103
104	return 0;
105}
106
107static int jh7110_reset_free(struct reset_ctl *rst)
108{
109	return 0;
110}
111
112static int jh7110_reset_request(struct reset_ctl *rst)
113{
114	struct jh7110_reset_priv *priv = dev_get_priv(rst->dev);
115
116	if (rst->id >= priv->resets)
117		return -EINVAL;
118
119	return 0;
120}
121
122static int jh7110_reset_probe(struct udevice *dev)
123{
124	struct jh7110_reset_priv *priv = dev_get_priv(dev);
125	const struct reset_info *cfg;
126	const char *compat;
127
128	compat = ofnode_get_property(dev_ofnode(dev), "compatible", NULL);
129	if (!compat)
130		return -EINVAL;
131
132	cfg = jh7110_reset_get_cfg(compat);
133	if (!cfg)
134		return -EINVAL;
135
136	priv->assert = cfg->assert_offset;
137	priv->status = cfg->status_offset;
138	priv->resets = cfg->nr_resets;
139	priv->reg = (void __iomem *)dev_read_addr_index(dev, 0);
140
141	return 0;
142}
143
144const struct reset_ops jh7110_reset_reset_ops = {
145	.rfree = jh7110_reset_free,
146	.request = jh7110_reset_request,
147	.rst_assert = jh7110_reset_assert,
148	.rst_deassert = jh7110_reset_deassert,
149};
150
151U_BOOT_DRIVER(jh7110_reset) = {
152	.name = "jh7110_reset",
153	.id = UCLASS_RESET,
154	.ops = &jh7110_reset_reset_ops,
155	.probe = jh7110_reset_probe,
156	.priv_auto = sizeof(struct jh7110_reset_priv),
157};
158