summaryrefslogtreecommitdiff
path: root/drivers/clk/ux500/reset-prcc.c
blob: fcd5d042806a689789dcbc20e4bfaaedac19e191 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Reset controller portions for the U8500 PRCC
 * Copyright (C) 2021 Linus Walleij <linus.walleij@linaro.org>
 */
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/slab.h>
#include <linux/io.h>
#include <linux/err.h>
#include <linux/types.h>
#include <linux/reset-controller.h>
#include <linux/bits.h>
#include <linux/delay.h>

#include "prcc.h"
#include "reset-prcc.h"

#define to_u8500_prcc_reset(p) container_of((p), struct u8500_prcc_reset, rcdev)

/* This macro flattens the 2-dimensional PRCC numberspace */
#define PRCC_RESET_LINE(prcc_num, bit) \
	(((prcc_num) * PRCC_PERIPHS_PER_CLUSTER) + (bit))

/*
 * Reset registers in each PRCC - the reset lines are active low
 * so what you need to do is write a bit for the peripheral you
 * want to put into reset into the CLEAR register, this will assert
 * the reset by pulling the line low. SET take the device out of
 * reset. The status reflects the actual state of the line.
 */
#define PRCC_K_SOFTRST_SET		0x018
#define PRCC_K_SOFTRST_CLEAR		0x01c
#define PRCC_K_RST_STATUS		0x020

static int prcc_num_to_index(unsigned int num)
{
	switch (num) {
	case 1:
		return CLKRST1_INDEX;
	case 2:
		return CLKRST2_INDEX;
	case 3:
		return CLKRST3_INDEX;
	case 5:
		return CLKRST5_INDEX;
	case 6:
		return CLKRST6_INDEX;
	}
	return -EINVAL;
}

static void __iomem *u8500_prcc_reset_base(struct u8500_prcc_reset *ur,
					   unsigned long id)
{
	unsigned int prcc_num, index;

	prcc_num = id / PRCC_PERIPHS_PER_CLUSTER;
	index = prcc_num_to_index(prcc_num);

	if (index > ARRAY_SIZE(ur->base))
		return NULL;

	return ur->base[index];
}

static int u8500_prcc_reset(struct reset_controller_dev *rcdev,
			    unsigned long id)
{
	struct u8500_prcc_reset *ur = to_u8500_prcc_reset(rcdev);
	void __iomem *base = u8500_prcc_reset_base(ur, id);
	unsigned int bit = id % PRCC_PERIPHS_PER_CLUSTER;

	pr_debug("PRCC cycle reset id %lu, bit %u\n", id, bit);

	/*
	 * Assert reset and then release it. The one microsecond
	 * delay is found in the vendor reference code.
	 */
	writel(BIT(bit), base + PRCC_K_SOFTRST_CLEAR);
	udelay(1);
	writel(BIT(bit), base + PRCC_K_SOFTRST_SET);
	udelay(1);

	return 0;
}

static int u8500_prcc_reset_assert(struct reset_controller_dev *rcdev,
				   unsigned long id)
{
	struct u8500_prcc_reset *ur = to_u8500_prcc_reset(rcdev);
	void __iomem *base = u8500_prcc_reset_base(ur, id);
	unsigned int bit = id % PRCC_PERIPHS_PER_CLUSTER;

	pr_debug("PRCC assert reset id %lu, bit %u\n", id, bit);
	writel(BIT(bit), base + PRCC_K_SOFTRST_CLEAR);

	return 0;
}

static int u8500_prcc_reset_deassert(struct reset_controller_dev *rcdev,
				     unsigned long id)
{
	struct u8500_prcc_reset *ur = to_u8500_prcc_reset(rcdev);
	void __iomem *base = u8500_prcc_reset_base(ur, id);
	unsigned int bit = id % PRCC_PERIPHS_PER_CLUSTER;

	pr_debug("PRCC deassert reset id %lu, bit %u\n", id, bit);
	writel(BIT(bit), base + PRCC_K_SOFTRST_SET);

	return 0;
}

static int u8500_prcc_reset_status(struct reset_controller_dev *rcdev,
				   unsigned long id)
{
	struct u8500_prcc_reset *ur = to_u8500_prcc_reset(rcdev);
	void __iomem *base = u8500_prcc_reset_base(ur, id);
	unsigned int bit = id % PRCC_PERIPHS_PER_CLUSTER;
	u32 val;

	pr_debug("PRCC check status on reset line id %lu, bit %u\n", id, bit);
	val = readl(base + PRCC_K_RST_STATUS);

	/* Active low so return the inverse value of the bit */
	return !(val & BIT(bit));
}

static const struct reset_control_ops u8500_prcc_reset_ops = {
	.reset = u8500_prcc_reset,
	.assert = u8500_prcc_reset_assert,
	.deassert = u8500_prcc_reset_deassert,
	.status = u8500_prcc_reset_status,
};

static int u8500_prcc_reset_xlate(struct reset_controller_dev *rcdev,
				  const struct of_phandle_args *reset_spec)
{
	unsigned int prcc_num, bit;

	if (reset_spec->args_count != 2)
		return -EINVAL;

	prcc_num = reset_spec->args[0];
	bit = reset_spec->args[1];

	if (prcc_num != 1 && prcc_num != 2 && prcc_num != 3 &&
	    prcc_num != 5 && prcc_num != 6) {
		pr_err("%s: invalid PRCC %d\n", __func__, prcc_num);
		return -EINVAL;
	}

	pr_debug("located reset line %d at PRCC %d bit %d\n",
		 PRCC_RESET_LINE(prcc_num, bit), prcc_num, bit);

	return PRCC_RESET_LINE(prcc_num, bit);
}

void u8500_prcc_reset_init(struct device_node *np, struct u8500_prcc_reset *ur)
{
	struct reset_controller_dev *rcdev = &ur->rcdev;
	int ret;
	int i;

	for (i = 0; i < CLKRST_MAX; i++) {
		ur->base[i] = ioremap(ur->phy_base[i], SZ_4K);
		if (!ur->base[i])
			pr_err("PRCC failed to remap for reset base %d (%08x)\n",
			       i, ur->phy_base[i]);
	}

	rcdev->owner = THIS_MODULE;
	rcdev->ops = &u8500_prcc_reset_ops;
	rcdev->of_node = np;
	rcdev->of_reset_n_cells = 2;
	rcdev->of_xlate = u8500_prcc_reset_xlate;

	ret = reset_controller_register(rcdev);
	if (ret)
		pr_err("PRCC failed to register reset controller\n");
}