1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Reset driver for the StarFive JH71X0 SoCs
4 *
5 * Copyright (C) 2021 Emil Renner Berthing <kernel@esmil.dk>
6 */
7
8#include <linux/bitmap.h>
9#include <linux/device.h>
10#include <linux/io.h>
11#include <linux/iopoll.h>
12#include <linux/reset-controller.h>
13#include <linux/spinlock.h>
14
15#include "reset-starfive-jh71x0.h"
16
17struct jh71x0_reset {
18	struct reset_controller_dev rcdev;
19	/* protect registers against concurrent read-modify-write */
20	spinlock_t lock;
21	void __iomem *assert;
22	void __iomem *status;
23	const u32 *asserted;
24};
25
26static inline struct jh71x0_reset *
27jh71x0_reset_from(struct reset_controller_dev *rcdev)
28{
29	return container_of(rcdev, struct jh71x0_reset, rcdev);
30}
31
32static int jh71x0_reset_update(struct reset_controller_dev *rcdev,
33			       unsigned long id, bool assert)
34{
35	struct jh71x0_reset *data = jh71x0_reset_from(rcdev);
36	unsigned long offset = id / 32;
37	u32 mask = BIT(id % 32);
38	void __iomem *reg_assert = data->assert + offset * sizeof(u32);
39	void __iomem *reg_status = data->status + offset * sizeof(u32);
40	u32 done = data->asserted ? data->asserted[offset] & mask : 0;
41	u32 value;
42	unsigned long flags;
43	int ret;
44
45	if (!assert)
46		done ^= mask;
47
48	spin_lock_irqsave(&data->lock, flags);
49
50	value = readl(reg_assert);
51	if (assert)
52		value |= mask;
53	else
54		value &= ~mask;
55	writel(value, reg_assert);
56
57	/* if the associated clock is gated, deasserting might otherwise hang forever */
58	ret = readl_poll_timeout_atomic(reg_status, value, (value & mask) == done, 0, 1000);
59
60	spin_unlock_irqrestore(&data->lock, flags);
61	return ret;
62}
63
64static int jh71x0_reset_assert(struct reset_controller_dev *rcdev,
65			       unsigned long id)
66{
67	return jh71x0_reset_update(rcdev, id, true);
68}
69
70static int jh71x0_reset_deassert(struct reset_controller_dev *rcdev,
71				 unsigned long id)
72{
73	return jh71x0_reset_update(rcdev, id, false);
74}
75
76static int jh71x0_reset_reset(struct reset_controller_dev *rcdev,
77			      unsigned long id)
78{
79	int ret;
80
81	ret = jh71x0_reset_assert(rcdev, id);
82	if (ret)
83		return ret;
84
85	return jh71x0_reset_deassert(rcdev, id);
86}
87
88static int jh71x0_reset_status(struct reset_controller_dev *rcdev,
89			       unsigned long id)
90{
91	struct jh71x0_reset *data = jh71x0_reset_from(rcdev);
92	unsigned long offset = id / 32;
93	u32 mask = BIT(id % 32);
94	void __iomem *reg_status = data->status + offset * sizeof(u32);
95	u32 value = readl(reg_status);
96
97	return !((value ^ data->asserted[offset]) & mask);
98}
99
100static const struct reset_control_ops jh71x0_reset_ops = {
101	.assert		= jh71x0_reset_assert,
102	.deassert	= jh71x0_reset_deassert,
103	.reset		= jh71x0_reset_reset,
104	.status		= jh71x0_reset_status,
105};
106
107int reset_starfive_jh71x0_register(struct device *dev, struct device_node *of_node,
108				   void __iomem *assert, void __iomem *status,
109				   const u32 *asserted, unsigned int nr_resets,
110				   struct module *owner)
111{
112	struct jh71x0_reset *data;
113
114	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
115	if (!data)
116		return -ENOMEM;
117
118	data->rcdev.ops = &jh71x0_reset_ops;
119	data->rcdev.owner = owner;
120	data->rcdev.nr_resets = nr_resets;
121	data->rcdev.dev = dev;
122	data->rcdev.of_node = of_node;
123
124	spin_lock_init(&data->lock);
125	data->assert = assert;
126	data->status = status;
127	data->asserted = asserted;
128
129	return devm_reset_controller_register(dev, &data->rcdev);
130}
131EXPORT_SYMBOL_GPL(reset_starfive_jh71x0_register);
132