1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2f59d23c2SChen Feng /*
3f59d23c2SChen Feng  * Hisilicon Hi6220 reset controller driver
4f59d23c2SChen Feng  *
58768a26cSChen Feng  * Copyright (c) 2016 Linaro Limited.
66eed6a4bSHao Fang  * Copyright (c) 2015-2016 HiSilicon Limited.
7f59d23c2SChen Feng  *
8f59d23c2SChen Feng  * Author: Feng Chen <puck.chen@hisilicon.com>
9f59d23c2SChen Feng  */
10f59d23c2SChen Feng 
11f59d23c2SChen Feng #include <linux/io.h>
12f59d23c2SChen Feng #include <linux/init.h>
1370b3590fSArnd Bergmann #include <linux/module.h>
14f59d23c2SChen Feng #include <linux/bitops.h>
15f59d23c2SChen Feng #include <linux/of.h>
168768a26cSChen Feng #include <linux/regmap.h>
178768a26cSChen Feng #include <linux/mfd/syscon.h>
18f59d23c2SChen Feng #include <linux/reset-controller.h>
19f59d23c2SChen Feng #include <linux/reset.h>
20f59d23c2SChen Feng #include <linux/platform_device.h>
21f59d23c2SChen Feng 
228768a26cSChen Feng #define PERIPH_ASSERT_OFFSET      0x300
238768a26cSChen Feng #define PERIPH_DEASSERT_OFFSET    0x304
248768a26cSChen Feng #define PERIPH_MAX_INDEX          0x509
25f59d23c2SChen Feng 
26ab52b599SXinliang Liu #define SC_MEDIA_RSTEN            0x052C
27ab52b599SXinliang Liu #define SC_MEDIA_RSTDIS           0x0530
28ab52b599SXinliang Liu #define MEDIA_MAX_INDEX           8
29ab52b599SXinliang Liu 
30f59d23c2SChen Feng #define to_reset_data(x) container_of(x, struct hi6220_reset_data, rc_dev)
31f59d23c2SChen Feng 
32ab52b599SXinliang Liu enum hi6220_reset_ctrl_type {
33ab52b599SXinliang Liu 	PERIPHERAL,
34ab52b599SXinliang Liu 	MEDIA,
35697fa27dSPeter Griffin 	AO,
36ab52b599SXinliang Liu };
37ab52b599SXinliang Liu 
38f59d23c2SChen Feng struct hi6220_reset_data {
39f59d23c2SChen Feng 	struct reset_controller_dev rc_dev;
408768a26cSChen Feng 	struct regmap *regmap;
41f59d23c2SChen Feng };
42f59d23c2SChen Feng 
hi6220_peripheral_assert(struct reset_controller_dev * rc_dev,unsigned long idx)438768a26cSChen Feng static int hi6220_peripheral_assert(struct reset_controller_dev *rc_dev,
44f59d23c2SChen Feng 				    unsigned long idx)
45f59d23c2SChen Feng {
46f59d23c2SChen Feng 	struct hi6220_reset_data *data = to_reset_data(rc_dev);
478768a26cSChen Feng 	struct regmap *regmap = data->regmap;
488768a26cSChen Feng 	u32 bank = idx >> 8;
498768a26cSChen Feng 	u32 offset = idx & 0xff;
508768a26cSChen Feng 	u32 reg = PERIPH_ASSERT_OFFSET + bank * 0x10;
51f59d23c2SChen Feng 
528768a26cSChen Feng 	return regmap_write(regmap, reg, BIT(offset));
53f59d23c2SChen Feng }
54f59d23c2SChen Feng 
hi6220_peripheral_deassert(struct reset_controller_dev * rc_dev,unsigned long idx)558768a26cSChen Feng static int hi6220_peripheral_deassert(struct reset_controller_dev *rc_dev,
56f59d23c2SChen Feng 				      unsigned long idx)
57f59d23c2SChen Feng {
58f59d23c2SChen Feng 	struct hi6220_reset_data *data = to_reset_data(rc_dev);
598768a26cSChen Feng 	struct regmap *regmap = data->regmap;
608768a26cSChen Feng 	u32 bank = idx >> 8;
618768a26cSChen Feng 	u32 offset = idx & 0xff;
628768a26cSChen Feng 	u32 reg = PERIPH_DEASSERT_OFFSET + bank * 0x10;
63f59d23c2SChen Feng 
648768a26cSChen Feng 	return regmap_write(regmap, reg, BIT(offset));
65f59d23c2SChen Feng }
66f59d23c2SChen Feng 
678768a26cSChen Feng static const struct reset_control_ops hi6220_peripheral_reset_ops = {
688768a26cSChen Feng 	.assert = hi6220_peripheral_assert,
698768a26cSChen Feng 	.deassert = hi6220_peripheral_deassert,
70f59d23c2SChen Feng };
71f59d23c2SChen Feng 
hi6220_media_assert(struct reset_controller_dev * rc_dev,unsigned long idx)72ab52b599SXinliang Liu static int hi6220_media_assert(struct reset_controller_dev *rc_dev,
73ab52b599SXinliang Liu 			       unsigned long idx)
74ab52b599SXinliang Liu {
75ab52b599SXinliang Liu 	struct hi6220_reset_data *data = to_reset_data(rc_dev);
76ab52b599SXinliang Liu 	struct regmap *regmap = data->regmap;
77ab52b599SXinliang Liu 
78ab52b599SXinliang Liu 	return regmap_write(regmap, SC_MEDIA_RSTEN, BIT(idx));
79ab52b599SXinliang Liu }
80ab52b599SXinliang Liu 
hi6220_media_deassert(struct reset_controller_dev * rc_dev,unsigned long idx)81ab52b599SXinliang Liu static int hi6220_media_deassert(struct reset_controller_dev *rc_dev,
82ab52b599SXinliang Liu 				 unsigned long idx)
83ab52b599SXinliang Liu {
84ab52b599SXinliang Liu 	struct hi6220_reset_data *data = to_reset_data(rc_dev);
85ab52b599SXinliang Liu 	struct regmap *regmap = data->regmap;
86ab52b599SXinliang Liu 
87ab52b599SXinliang Liu 	return regmap_write(regmap, SC_MEDIA_RSTDIS, BIT(idx));
88ab52b599SXinliang Liu }
89ab52b599SXinliang Liu 
90ab52b599SXinliang Liu static const struct reset_control_ops hi6220_media_reset_ops = {
91ab52b599SXinliang Liu 	.assert = hi6220_media_assert,
92ab52b599SXinliang Liu 	.deassert = hi6220_media_deassert,
93ab52b599SXinliang Liu };
94ab52b599SXinliang Liu 
95697fa27dSPeter Griffin #define AO_SCTRL_SC_PW_CLKEN0     0x800
96697fa27dSPeter Griffin #define AO_SCTRL_SC_PW_CLKDIS0    0x804
97697fa27dSPeter Griffin 
98697fa27dSPeter Griffin #define AO_SCTRL_SC_PW_RSTEN0     0x810
99697fa27dSPeter Griffin #define AO_SCTRL_SC_PW_RSTDIS0    0x814
100697fa27dSPeter Griffin 
101697fa27dSPeter Griffin #define AO_SCTRL_SC_PW_ISOEN0     0x820
102697fa27dSPeter Griffin #define AO_SCTRL_SC_PW_ISODIS0    0x824
103697fa27dSPeter Griffin #define AO_MAX_INDEX              12
104697fa27dSPeter Griffin 
hi6220_ao_assert(struct reset_controller_dev * rc_dev,unsigned long idx)105697fa27dSPeter Griffin static int hi6220_ao_assert(struct reset_controller_dev *rc_dev,
106697fa27dSPeter Griffin 			       unsigned long idx)
107697fa27dSPeter Griffin {
108697fa27dSPeter Griffin 	struct hi6220_reset_data *data = to_reset_data(rc_dev);
109697fa27dSPeter Griffin 	struct regmap *regmap = data->regmap;
110697fa27dSPeter Griffin 	int ret;
111697fa27dSPeter Griffin 
112697fa27dSPeter Griffin 	ret = regmap_write(regmap, AO_SCTRL_SC_PW_RSTEN0, BIT(idx));
113697fa27dSPeter Griffin 	if (ret)
114697fa27dSPeter Griffin 		return ret;
115697fa27dSPeter Griffin 
116697fa27dSPeter Griffin 	ret = regmap_write(regmap, AO_SCTRL_SC_PW_ISOEN0, BIT(idx));
117697fa27dSPeter Griffin 	if (ret)
118697fa27dSPeter Griffin 		return ret;
119697fa27dSPeter Griffin 
120697fa27dSPeter Griffin 	ret = regmap_write(regmap, AO_SCTRL_SC_PW_CLKDIS0, BIT(idx));
121697fa27dSPeter Griffin 	return ret;
122697fa27dSPeter Griffin }
123697fa27dSPeter Griffin 
hi6220_ao_deassert(struct reset_controller_dev * rc_dev,unsigned long idx)124697fa27dSPeter Griffin static int hi6220_ao_deassert(struct reset_controller_dev *rc_dev,
125697fa27dSPeter Griffin 				 unsigned long idx)
126697fa27dSPeter Griffin {
127697fa27dSPeter Griffin 	struct hi6220_reset_data *data = to_reset_data(rc_dev);
128697fa27dSPeter Griffin 	struct regmap *regmap = data->regmap;
129697fa27dSPeter Griffin 	int ret;
130697fa27dSPeter Griffin 
131697fa27dSPeter Griffin 	/*
132697fa27dSPeter Griffin 	 * It was suggested to disable isolation before enabling
133697fa27dSPeter Griffin 	 * the clocks and deasserting reset, to avoid glitches.
134697fa27dSPeter Griffin 	 * But this order is preserved to keep it matching the
135697fa27dSPeter Griffin 	 * vendor code.
136697fa27dSPeter Griffin 	 */
137697fa27dSPeter Griffin 	ret = regmap_write(regmap, AO_SCTRL_SC_PW_RSTDIS0, BIT(idx));
138697fa27dSPeter Griffin 	if (ret)
139697fa27dSPeter Griffin 		return ret;
140697fa27dSPeter Griffin 
141697fa27dSPeter Griffin 	ret = regmap_write(regmap, AO_SCTRL_SC_PW_ISODIS0, BIT(idx));
142697fa27dSPeter Griffin 	if (ret)
143697fa27dSPeter Griffin 		return ret;
144697fa27dSPeter Griffin 
145697fa27dSPeter Griffin 	ret = regmap_write(regmap, AO_SCTRL_SC_PW_CLKEN0, BIT(idx));
146697fa27dSPeter Griffin 	return ret;
147697fa27dSPeter Griffin }
148697fa27dSPeter Griffin 
149697fa27dSPeter Griffin static const struct reset_control_ops hi6220_ao_reset_ops = {
150697fa27dSPeter Griffin 	.assert = hi6220_ao_assert,
151697fa27dSPeter Griffin 	.deassert = hi6220_ao_deassert,
152697fa27dSPeter Griffin };
153697fa27dSPeter Griffin 
hi6220_reset_probe(struct platform_device * pdev)154f59d23c2SChen Feng static int hi6220_reset_probe(struct platform_device *pdev)
155f59d23c2SChen Feng {
1568768a26cSChen Feng 	struct device_node *np = pdev->dev.of_node;
1578768a26cSChen Feng 	struct device *dev = &pdev->dev;
158ab52b599SXinliang Liu 	enum hi6220_reset_ctrl_type type;
159f59d23c2SChen Feng 	struct hi6220_reset_data *data;
1608768a26cSChen Feng 	struct regmap *regmap;
161f59d23c2SChen Feng 
1628768a26cSChen Feng 	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
163f59d23c2SChen Feng 	if (!data)
164f59d23c2SChen Feng 		return -ENOMEM;
165f59d23c2SChen Feng 
166*88c38fd2SKrzysztof Kozlowski 	type = (uintptr_t)of_device_get_match_data(dev);
167ab52b599SXinliang Liu 
1688768a26cSChen Feng 	regmap = syscon_node_to_regmap(np);
1698768a26cSChen Feng 	if (IS_ERR(regmap)) {
1708768a26cSChen Feng 		dev_err(dev, "failed to get reset controller regmap\n");
1718768a26cSChen Feng 		return PTR_ERR(regmap);
1728768a26cSChen Feng 	}
173f59d23c2SChen Feng 
1748768a26cSChen Feng 	data->regmap = regmap;
1758768a26cSChen Feng 	data->rc_dev.of_node = np;
176ab52b599SXinliang Liu 	if (type == MEDIA) {
177ab52b599SXinliang Liu 		data->rc_dev.ops = &hi6220_media_reset_ops;
178ab52b599SXinliang Liu 		data->rc_dev.nr_resets = MEDIA_MAX_INDEX;
179697fa27dSPeter Griffin 	} else if (type == PERIPHERAL) {
1808768a26cSChen Feng 		data->rc_dev.ops = &hi6220_peripheral_reset_ops;
1818768a26cSChen Feng 		data->rc_dev.nr_resets = PERIPH_MAX_INDEX;
182697fa27dSPeter Griffin 	} else {
183697fa27dSPeter Griffin 		data->rc_dev.ops = &hi6220_ao_reset_ops;
184697fa27dSPeter Griffin 		data->rc_dev.nr_resets = AO_MAX_INDEX;
185ab52b599SXinliang Liu 	}
186f59d23c2SChen Feng 
187c41ef91fSMasahiro Yamada 	return reset_controller_register(&data->rc_dev);
188f59d23c2SChen Feng }
189f59d23c2SChen Feng 
190f59d23c2SChen Feng static const struct of_device_id hi6220_reset_match[] = {
1918768a26cSChen Feng 	{
1928768a26cSChen Feng 		.compatible = "hisilicon,hi6220-sysctrl",
193ab52b599SXinliang Liu 		.data = (void *)PERIPHERAL,
194ab52b599SXinliang Liu 	},
195ab52b599SXinliang Liu 	{
196ab52b599SXinliang Liu 		.compatible = "hisilicon,hi6220-mediactrl",
197ab52b599SXinliang Liu 		.data = (void *)MEDIA,
1988768a26cSChen Feng 	},
199697fa27dSPeter Griffin 	{
200697fa27dSPeter Griffin 		.compatible = "hisilicon,hi6220-aoctrl",
201697fa27dSPeter Griffin 		.data = (void *)AO,
202697fa27dSPeter Griffin 	},
2038768a26cSChen Feng 	{ /* sentinel */ },
204f59d23c2SChen Feng };
2058768a26cSChen Feng MODULE_DEVICE_TABLE(of, hi6220_reset_match);
206f59d23c2SChen Feng 
207f59d23c2SChen Feng static struct platform_driver hi6220_reset_driver = {
208f59d23c2SChen Feng 	.probe = hi6220_reset_probe,
209f59d23c2SChen Feng 	.driver = {
210f59d23c2SChen Feng 		.name = "reset-hi6220",
211f59d23c2SChen Feng 		.of_match_table = hi6220_reset_match,
212f59d23c2SChen Feng 	},
213f59d23c2SChen Feng };
214f59d23c2SChen Feng 
hi6220_reset_init(void)215f59d23c2SChen Feng static int __init hi6220_reset_init(void)
216f59d23c2SChen Feng {
217f59d23c2SChen Feng 	return platform_driver_register(&hi6220_reset_driver);
218f59d23c2SChen Feng }
219f59d23c2SChen Feng 
220f59d23c2SChen Feng postcore_initcall(hi6220_reset_init);
2214497a224SJeremy Linton 
2224497a224SJeremy Linton MODULE_LICENSE("GPL v2");
223