1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Hisilicon Reset Controller Driver
4 *
5 * Copyright (c) 2015-2016 HiSilicon Technologies Co., Ltd.
6 */
7
8#include <linux/io.h>
9#include <linux/of_address.h>
10#include <linux/platform_device.h>
11#include <linux/reset-controller.h>
12#include <linux/slab.h>
13#include <linux/spinlock.h>
14#include "reset.h"
15
16#define	HISI_RESET_BIT_MASK	0x1f
17#define	HISI_RESET_OFFSET_SHIFT	8
18#define	HISI_RESET_OFFSET_MASK	0xffff00
19
20struct hisi_reset_controller {
21	spinlock_t	lock;
22	void __iomem	*membase;
23	struct reset_controller_dev	rcdev;
24};
25
26
27#define to_hisi_reset_controller(rcdev)  \
28	container_of(rcdev, struct hisi_reset_controller, rcdev)
29
30static int hisi_reset_of_xlate(struct reset_controller_dev *rcdev,
31			const struct of_phandle_args *reset_spec)
32{
33	u32 offset;
34	u8 bit;
35
36	offset = (reset_spec->args[0] << HISI_RESET_OFFSET_SHIFT)
37		& HISI_RESET_OFFSET_MASK;
38	bit = reset_spec->args[1] & HISI_RESET_BIT_MASK;
39
40	return (offset | bit);
41}
42
43static int hisi_reset_assert(struct reset_controller_dev *rcdev,
44			      unsigned long id)
45{
46	struct hisi_reset_controller *rstc = to_hisi_reset_controller(rcdev);
47	unsigned long flags;
48	u32 offset, reg;
49	u8 bit;
50
51	offset = (id & HISI_RESET_OFFSET_MASK) >> HISI_RESET_OFFSET_SHIFT;
52	bit = id & HISI_RESET_BIT_MASK;
53
54	spin_lock_irqsave(&rstc->lock, flags);
55
56	reg = readl(rstc->membase + offset);
57	writel(reg | BIT(bit), rstc->membase + offset);
58
59	spin_unlock_irqrestore(&rstc->lock, flags);
60
61	return 0;
62}
63
64static int hisi_reset_deassert(struct reset_controller_dev *rcdev,
65				unsigned long id)
66{
67	struct hisi_reset_controller *rstc = to_hisi_reset_controller(rcdev);
68	unsigned long flags;
69	u32 offset, reg;
70	u8 bit;
71
72	offset = (id & HISI_RESET_OFFSET_MASK) >> HISI_RESET_OFFSET_SHIFT;
73	bit = id & HISI_RESET_BIT_MASK;
74
75	spin_lock_irqsave(&rstc->lock, flags);
76
77	reg = readl(rstc->membase + offset);
78	writel(reg & ~BIT(bit), rstc->membase + offset);
79
80	spin_unlock_irqrestore(&rstc->lock, flags);
81
82	return 0;
83}
84
85static const struct reset_control_ops hisi_reset_ops = {
86	.assert		= hisi_reset_assert,
87	.deassert	= hisi_reset_deassert,
88};
89
90struct hisi_reset_controller *hisi_reset_init(struct platform_device *pdev)
91{
92	struct hisi_reset_controller *rstc;
93
94	rstc = devm_kmalloc(&pdev->dev, sizeof(*rstc), GFP_KERNEL);
95	if (!rstc)
96		return NULL;
97
98	rstc->membase = devm_platform_ioremap_resource(pdev, 0);
99	if (IS_ERR(rstc->membase))
100		return NULL;
101
102	spin_lock_init(&rstc->lock);
103	rstc->rcdev.owner = THIS_MODULE;
104	rstc->rcdev.ops = &hisi_reset_ops;
105	rstc->rcdev.of_node = pdev->dev.of_node;
106	rstc->rcdev.of_reset_n_cells = 2;
107	rstc->rcdev.of_xlate = hisi_reset_of_xlate;
108	reset_controller_register(&rstc->rcdev);
109
110	return rstc;
111}
112EXPORT_SYMBOL_GPL(hisi_reset_init);
113
114void hisi_reset_exit(struct hisi_reset_controller *rstc)
115{
116	reset_controller_unregister(&rstc->rcdev);
117}
118EXPORT_SYMBOL_GPL(hisi_reset_exit);
119