xref: /openbmc/linux/drivers/memory/tegra/tegra20.c (revision 1079a66b)
1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2a8d502fdSDmitry Osipenko /*
3a8d502fdSDmitry Osipenko  * Copyright (C) 2012 NVIDIA CORPORATION.  All rights reserved.
4a8d502fdSDmitry Osipenko  */
5a8d502fdSDmitry Osipenko 
6fbd31f5aSDmitry Osipenko #include <linux/bitfield.h>
7fbd31f5aSDmitry Osipenko #include <linux/delay.h>
828947198SDmitry Osipenko #include <linux/mutex.h>
9d5ef16baSDmitry Osipenko #include <linux/of_device.h>
10d5ef16baSDmitry Osipenko #include <linux/slab.h>
11d5ef16baSDmitry Osipenko #include <linux/string.h>
12d5ef16baSDmitry Osipenko 
13cb557757SDmitry Osipenko #include <dt-bindings/memory/tegra20-mc.h>
14cb557757SDmitry Osipenko 
15a8d502fdSDmitry Osipenko #include "mc.h"
16a8d502fdSDmitry Osipenko 
17fbd31f5aSDmitry Osipenko #define MC_STAT_CONTROL				0x90
18fbd31f5aSDmitry Osipenko #define MC_STAT_EMC_CLOCK_LIMIT			0xa0
19fbd31f5aSDmitry Osipenko #define MC_STAT_EMC_CLOCKS			0xa4
20fbd31f5aSDmitry Osipenko #define MC_STAT_EMC_CONTROL_0			0xa8
21fbd31f5aSDmitry Osipenko #define MC_STAT_EMC_CONTROL_1			0xac
22fbd31f5aSDmitry Osipenko #define MC_STAT_EMC_COUNT_0			0xb8
23fbd31f5aSDmitry Osipenko #define MC_STAT_EMC_COUNT_1			0xbc
24fbd31f5aSDmitry Osipenko 
25fbd31f5aSDmitry Osipenko #define MC_STAT_CONTROL_CLIENT_ID		GENMASK(13,  8)
26fbd31f5aSDmitry Osipenko #define MC_STAT_CONTROL_EVENT			GENMASK(23, 16)
27fbd31f5aSDmitry Osipenko #define MC_STAT_CONTROL_PRI_EVENT		GENMASK(25, 24)
28fbd31f5aSDmitry Osipenko #define MC_STAT_CONTROL_FILTER_CLIENT_ENABLE	GENMASK(26, 26)
29fbd31f5aSDmitry Osipenko #define MC_STAT_CONTROL_FILTER_PRI		GENMASK(29, 28)
30fbd31f5aSDmitry Osipenko 
31fbd31f5aSDmitry Osipenko #define MC_STAT_CONTROL_PRI_EVENT_HP		0
32fbd31f5aSDmitry Osipenko #define MC_STAT_CONTROL_PRI_EVENT_TM		1
33fbd31f5aSDmitry Osipenko #define MC_STAT_CONTROL_PRI_EVENT_BW		2
34fbd31f5aSDmitry Osipenko 
35fbd31f5aSDmitry Osipenko #define MC_STAT_CONTROL_FILTER_PRI_DISABLE	0
36fbd31f5aSDmitry Osipenko #define MC_STAT_CONTROL_FILTER_PRI_NO		1
37fbd31f5aSDmitry Osipenko #define MC_STAT_CONTROL_FILTER_PRI_YES		2
38fbd31f5aSDmitry Osipenko 
39fbd31f5aSDmitry Osipenko #define MC_STAT_CONTROL_EVENT_QUALIFIED		0
40fbd31f5aSDmitry Osipenko #define MC_STAT_CONTROL_EVENT_ANY_READ		1
41fbd31f5aSDmitry Osipenko #define MC_STAT_CONTROL_EVENT_ANY_WRITE		2
42fbd31f5aSDmitry Osipenko #define MC_STAT_CONTROL_EVENT_RD_WR_CHANGE	3
43fbd31f5aSDmitry Osipenko #define MC_STAT_CONTROL_EVENT_SUCCESSIVE	4
44fbd31f5aSDmitry Osipenko #define MC_STAT_CONTROL_EVENT_ARB_BANK_AA	5
45fbd31f5aSDmitry Osipenko #define MC_STAT_CONTROL_EVENT_ARB_BANK_BB	6
46fbd31f5aSDmitry Osipenko #define MC_STAT_CONTROL_EVENT_PAGE_MISS		7
47fbd31f5aSDmitry Osipenko #define MC_STAT_CONTROL_EVENT_AUTO_PRECHARGE	8
48fbd31f5aSDmitry Osipenko 
49fbd31f5aSDmitry Osipenko #define EMC_GATHER_RST				(0 << 8)
50fbd31f5aSDmitry Osipenko #define EMC_GATHER_CLEAR			(1 << 8)
51fbd31f5aSDmitry Osipenko #define EMC_GATHER_DISABLE			(2 << 8)
52fbd31f5aSDmitry Osipenko #define EMC_GATHER_ENABLE			(3 << 8)
53fbd31f5aSDmitry Osipenko 
54fbd31f5aSDmitry Osipenko #define MC_STAT_SAMPLE_TIME_USEC		16000
55fbd31f5aSDmitry Osipenko 
56fbd31f5aSDmitry Osipenko /* we store collected statistics as a fixed point values */
57fbd31f5aSDmitry Osipenko #define MC_FX_FRAC_SCALE			100
58fbd31f5aSDmitry Osipenko 
5928947198SDmitry Osipenko static DEFINE_MUTEX(tegra20_mc_stat_lock);
6028947198SDmitry Osipenko 
61fbd31f5aSDmitry Osipenko struct tegra20_mc_stat_gather {
62fbd31f5aSDmitry Osipenko 	unsigned int pri_filter;
63fbd31f5aSDmitry Osipenko 	unsigned int pri_event;
64fbd31f5aSDmitry Osipenko 	unsigned int result;
65fbd31f5aSDmitry Osipenko 	unsigned int client;
66fbd31f5aSDmitry Osipenko 	unsigned int event;
67fbd31f5aSDmitry Osipenko 	bool client_enb;
68fbd31f5aSDmitry Osipenko };
69fbd31f5aSDmitry Osipenko 
70fbd31f5aSDmitry Osipenko struct tegra20_mc_stat {
71fbd31f5aSDmitry Osipenko 	struct tegra20_mc_stat_gather gather0;
72fbd31f5aSDmitry Osipenko 	struct tegra20_mc_stat_gather gather1;
73fbd31f5aSDmitry Osipenko 	unsigned int sample_time_usec;
74fbd31f5aSDmitry Osipenko 	const struct tegra_mc *mc;
75fbd31f5aSDmitry Osipenko };
76fbd31f5aSDmitry Osipenko 
77fbd31f5aSDmitry Osipenko struct tegra20_mc_client_stat {
78fbd31f5aSDmitry Osipenko 	unsigned int events;
79fbd31f5aSDmitry Osipenko 	unsigned int arb_high_prio;
80fbd31f5aSDmitry Osipenko 	unsigned int arb_timeout;
81fbd31f5aSDmitry Osipenko 	unsigned int arb_bandwidth;
82fbd31f5aSDmitry Osipenko 	unsigned int rd_wr_change;
83fbd31f5aSDmitry Osipenko 	unsigned int successive;
84fbd31f5aSDmitry Osipenko 	unsigned int page_miss;
85fbd31f5aSDmitry Osipenko 	unsigned int auto_precharge;
86fbd31f5aSDmitry Osipenko 	unsigned int arb_bank_aa;
87fbd31f5aSDmitry Osipenko 	unsigned int arb_bank_bb;
88fbd31f5aSDmitry Osipenko };
89fbd31f5aSDmitry Osipenko 
90a8d502fdSDmitry Osipenko static const struct tegra_mc_client tegra20_mc_clients[] = {
91a8d502fdSDmitry Osipenko 	{
92a8d502fdSDmitry Osipenko 		.id = 0x00,
93a8d502fdSDmitry Osipenko 		.name = "display0a",
94a8d502fdSDmitry Osipenko 	}, {
95a8d502fdSDmitry Osipenko 		.id = 0x01,
96a8d502fdSDmitry Osipenko 		.name = "display0ab",
97a8d502fdSDmitry Osipenko 	}, {
98a8d502fdSDmitry Osipenko 		.id = 0x02,
99a8d502fdSDmitry Osipenko 		.name = "display0b",
100a8d502fdSDmitry Osipenko 	}, {
101a8d502fdSDmitry Osipenko 		.id = 0x03,
102a8d502fdSDmitry Osipenko 		.name = "display0bb",
103a8d502fdSDmitry Osipenko 	}, {
104a8d502fdSDmitry Osipenko 		.id = 0x04,
105a8d502fdSDmitry Osipenko 		.name = "display0c",
106a8d502fdSDmitry Osipenko 	}, {
107a8d502fdSDmitry Osipenko 		.id = 0x05,
108a8d502fdSDmitry Osipenko 		.name = "display0cb",
109a8d502fdSDmitry Osipenko 	}, {
110a8d502fdSDmitry Osipenko 		.id = 0x06,
111a8d502fdSDmitry Osipenko 		.name = "display1b",
112a8d502fdSDmitry Osipenko 	}, {
113a8d502fdSDmitry Osipenko 		.id = 0x07,
114a8d502fdSDmitry Osipenko 		.name = "display1bb",
115a8d502fdSDmitry Osipenko 	}, {
116a8d502fdSDmitry Osipenko 		.id = 0x08,
117a8d502fdSDmitry Osipenko 		.name = "eppup",
118a8d502fdSDmitry Osipenko 	}, {
119a8d502fdSDmitry Osipenko 		.id = 0x09,
120a8d502fdSDmitry Osipenko 		.name = "g2pr",
121a8d502fdSDmitry Osipenko 	}, {
122a8d502fdSDmitry Osipenko 		.id = 0x0a,
123a8d502fdSDmitry Osipenko 		.name = "g2sr",
124a8d502fdSDmitry Osipenko 	}, {
125a8d502fdSDmitry Osipenko 		.id = 0x0b,
126a8d502fdSDmitry Osipenko 		.name = "mpeunifbr",
127a8d502fdSDmitry Osipenko 	}, {
128a8d502fdSDmitry Osipenko 		.id = 0x0c,
129a8d502fdSDmitry Osipenko 		.name = "viruv",
130a8d502fdSDmitry Osipenko 	}, {
131a8d502fdSDmitry Osipenko 		.id = 0x0d,
132a8d502fdSDmitry Osipenko 		.name = "avpcarm7r",
133a8d502fdSDmitry Osipenko 	}, {
134a8d502fdSDmitry Osipenko 		.id = 0x0e,
135a8d502fdSDmitry Osipenko 		.name = "displayhc",
136a8d502fdSDmitry Osipenko 	}, {
137a8d502fdSDmitry Osipenko 		.id = 0x0f,
138a8d502fdSDmitry Osipenko 		.name = "displayhcb",
139a8d502fdSDmitry Osipenko 	}, {
140a8d502fdSDmitry Osipenko 		.id = 0x10,
141a8d502fdSDmitry Osipenko 		.name = "fdcdrd",
142a8d502fdSDmitry Osipenko 	}, {
143a8d502fdSDmitry Osipenko 		.id = 0x11,
144a8d502fdSDmitry Osipenko 		.name = "g2dr",
145a8d502fdSDmitry Osipenko 	}, {
146a8d502fdSDmitry Osipenko 		.id = 0x12,
147a8d502fdSDmitry Osipenko 		.name = "host1xdmar",
148a8d502fdSDmitry Osipenko 	}, {
149a8d502fdSDmitry Osipenko 		.id = 0x13,
150a8d502fdSDmitry Osipenko 		.name = "host1xr",
151a8d502fdSDmitry Osipenko 	}, {
152a8d502fdSDmitry Osipenko 		.id = 0x14,
153a8d502fdSDmitry Osipenko 		.name = "idxsrd",
154a8d502fdSDmitry Osipenko 	}, {
155a8d502fdSDmitry Osipenko 		.id = 0x15,
156a8d502fdSDmitry Osipenko 		.name = "mpcorer",
157a8d502fdSDmitry Osipenko 	}, {
158a8d502fdSDmitry Osipenko 		.id = 0x16,
159a8d502fdSDmitry Osipenko 		.name = "mpe_ipred",
160a8d502fdSDmitry Osipenko 	}, {
161a8d502fdSDmitry Osipenko 		.id = 0x17,
162a8d502fdSDmitry Osipenko 		.name = "mpeamemrd",
163a8d502fdSDmitry Osipenko 	}, {
164a8d502fdSDmitry Osipenko 		.id = 0x18,
165a8d502fdSDmitry Osipenko 		.name = "mpecsrd",
166a8d502fdSDmitry Osipenko 	}, {
167a8d502fdSDmitry Osipenko 		.id = 0x19,
168a8d502fdSDmitry Osipenko 		.name = "ppcsahbdmar",
169a8d502fdSDmitry Osipenko 	}, {
170a8d502fdSDmitry Osipenko 		.id = 0x1a,
171a8d502fdSDmitry Osipenko 		.name = "ppcsahbslvr",
172a8d502fdSDmitry Osipenko 	}, {
173a8d502fdSDmitry Osipenko 		.id = 0x1b,
174a8d502fdSDmitry Osipenko 		.name = "texsrd",
175a8d502fdSDmitry Osipenko 	}, {
176a8d502fdSDmitry Osipenko 		.id = 0x1c,
177a8d502fdSDmitry Osipenko 		.name = "vdebsevr",
178a8d502fdSDmitry Osipenko 	}, {
179a8d502fdSDmitry Osipenko 		.id = 0x1d,
180a8d502fdSDmitry Osipenko 		.name = "vdember",
181a8d502fdSDmitry Osipenko 	}, {
182a8d502fdSDmitry Osipenko 		.id = 0x1e,
183a8d502fdSDmitry Osipenko 		.name = "vdemcer",
184a8d502fdSDmitry Osipenko 	}, {
185a8d502fdSDmitry Osipenko 		.id = 0x1f,
186a8d502fdSDmitry Osipenko 		.name = "vdetper",
187a8d502fdSDmitry Osipenko 	}, {
188a8d502fdSDmitry Osipenko 		.id = 0x20,
189a8d502fdSDmitry Osipenko 		.name = "eppu",
190a8d502fdSDmitry Osipenko 	}, {
191a8d502fdSDmitry Osipenko 		.id = 0x21,
192a8d502fdSDmitry Osipenko 		.name = "eppv",
193a8d502fdSDmitry Osipenko 	}, {
194a8d502fdSDmitry Osipenko 		.id = 0x22,
195a8d502fdSDmitry Osipenko 		.name = "eppy",
196a8d502fdSDmitry Osipenko 	}, {
197a8d502fdSDmitry Osipenko 		.id = 0x23,
198a8d502fdSDmitry Osipenko 		.name = "mpeunifbw",
199a8d502fdSDmitry Osipenko 	}, {
200a8d502fdSDmitry Osipenko 		.id = 0x24,
201a8d502fdSDmitry Osipenko 		.name = "viwsb",
202a8d502fdSDmitry Osipenko 	}, {
203a8d502fdSDmitry Osipenko 		.id = 0x25,
204a8d502fdSDmitry Osipenko 		.name = "viwu",
205a8d502fdSDmitry Osipenko 	}, {
206a8d502fdSDmitry Osipenko 		.id = 0x26,
207a8d502fdSDmitry Osipenko 		.name = "viwv",
208a8d502fdSDmitry Osipenko 	}, {
209a8d502fdSDmitry Osipenko 		.id = 0x27,
210a8d502fdSDmitry Osipenko 		.name = "viwy",
211a8d502fdSDmitry Osipenko 	}, {
212a8d502fdSDmitry Osipenko 		.id = 0x28,
213a8d502fdSDmitry Osipenko 		.name = "g2dw",
214a8d502fdSDmitry Osipenko 	}, {
215a8d502fdSDmitry Osipenko 		.id = 0x29,
216a8d502fdSDmitry Osipenko 		.name = "avpcarm7w",
217a8d502fdSDmitry Osipenko 	}, {
218a8d502fdSDmitry Osipenko 		.id = 0x2a,
219a8d502fdSDmitry Osipenko 		.name = "fdcdwr",
220a8d502fdSDmitry Osipenko 	}, {
221a8d502fdSDmitry Osipenko 		.id = 0x2b,
222a8d502fdSDmitry Osipenko 		.name = "host1xw",
223a8d502fdSDmitry Osipenko 	}, {
224a8d502fdSDmitry Osipenko 		.id = 0x2c,
225a8d502fdSDmitry Osipenko 		.name = "ispw",
226a8d502fdSDmitry Osipenko 	}, {
227a8d502fdSDmitry Osipenko 		.id = 0x2d,
228a8d502fdSDmitry Osipenko 		.name = "mpcorew",
229a8d502fdSDmitry Osipenko 	}, {
230a8d502fdSDmitry Osipenko 		.id = 0x2e,
231a8d502fdSDmitry Osipenko 		.name = "mpecswr",
232a8d502fdSDmitry Osipenko 	}, {
233a8d502fdSDmitry Osipenko 		.id = 0x2f,
234a8d502fdSDmitry Osipenko 		.name = "ppcsahbdmaw",
235a8d502fdSDmitry Osipenko 	}, {
236a8d502fdSDmitry Osipenko 		.id = 0x30,
237a8d502fdSDmitry Osipenko 		.name = "ppcsahbslvw",
238a8d502fdSDmitry Osipenko 	}, {
239a8d502fdSDmitry Osipenko 		.id = 0x31,
240a8d502fdSDmitry Osipenko 		.name = "vdebsevw",
241a8d502fdSDmitry Osipenko 	}, {
242a8d502fdSDmitry Osipenko 		.id = 0x32,
243a8d502fdSDmitry Osipenko 		.name = "vdembew",
244a8d502fdSDmitry Osipenko 	}, {
245a8d502fdSDmitry Osipenko 		.id = 0x33,
246a8d502fdSDmitry Osipenko 		.name = "vdetpmw",
247a8d502fdSDmitry Osipenko 	},
248a8d502fdSDmitry Osipenko };
249a8d502fdSDmitry Osipenko 
250cb557757SDmitry Osipenko #define TEGRA20_MC_RESET(_name, _control, _status, _reset, _bit)	\
251cb557757SDmitry Osipenko 	{								\
252cb557757SDmitry Osipenko 		.name = #_name,						\
253cb557757SDmitry Osipenko 		.id = TEGRA20_MC_RESET_##_name,				\
254cb557757SDmitry Osipenko 		.control = _control,					\
255cb557757SDmitry Osipenko 		.status = _status,					\
256cb557757SDmitry Osipenko 		.reset = _reset,					\
257cb557757SDmitry Osipenko 		.bit = _bit,						\
258cb557757SDmitry Osipenko 	}
259cb557757SDmitry Osipenko 
260cb557757SDmitry Osipenko static const struct tegra_mc_reset tegra20_mc_resets[] = {
261cb557757SDmitry Osipenko 	TEGRA20_MC_RESET(AVPC,   0x100, 0x140, 0x104,  0),
262cb557757SDmitry Osipenko 	TEGRA20_MC_RESET(DC,     0x100, 0x144, 0x104,  1),
263cb557757SDmitry Osipenko 	TEGRA20_MC_RESET(DCB,    0x100, 0x148, 0x104,  2),
264cb557757SDmitry Osipenko 	TEGRA20_MC_RESET(EPP,    0x100, 0x14c, 0x104,  3),
265cb557757SDmitry Osipenko 	TEGRA20_MC_RESET(2D,     0x100, 0x150, 0x104,  4),
266cb557757SDmitry Osipenko 	TEGRA20_MC_RESET(HC,     0x100, 0x154, 0x104,  5),
267cb557757SDmitry Osipenko 	TEGRA20_MC_RESET(ISP,    0x100, 0x158, 0x104,  6),
268cb557757SDmitry Osipenko 	TEGRA20_MC_RESET(MPCORE, 0x100, 0x15c, 0x104,  7),
269cb557757SDmitry Osipenko 	TEGRA20_MC_RESET(MPEA,   0x100, 0x160, 0x104,  8),
270cb557757SDmitry Osipenko 	TEGRA20_MC_RESET(MPEB,   0x100, 0x164, 0x104,  9),
271cb557757SDmitry Osipenko 	TEGRA20_MC_RESET(MPEC,   0x100, 0x168, 0x104, 10),
272cb557757SDmitry Osipenko 	TEGRA20_MC_RESET(3D,     0x100, 0x16c, 0x104, 11),
273cb557757SDmitry Osipenko 	TEGRA20_MC_RESET(PPCS,   0x100, 0x170, 0x104, 12),
274cb557757SDmitry Osipenko 	TEGRA20_MC_RESET(VDE,    0x100, 0x174, 0x104, 13),
275cb557757SDmitry Osipenko 	TEGRA20_MC_RESET(VI,     0x100, 0x178, 0x104, 14),
276cb557757SDmitry Osipenko };
277cb557757SDmitry Osipenko 
278cb2b5839SThierry Reding static int tegra20_mc_hotreset_assert(struct tegra_mc *mc,
279cb557757SDmitry Osipenko 				      const struct tegra_mc_reset *rst)
280cb557757SDmitry Osipenko {
281cb557757SDmitry Osipenko 	unsigned long flags;
282cb557757SDmitry Osipenko 	u32 value;
283cb557757SDmitry Osipenko 
284cb557757SDmitry Osipenko 	spin_lock_irqsave(&mc->lock, flags);
285cb557757SDmitry Osipenko 
286cb557757SDmitry Osipenko 	value = mc_readl(mc, rst->reset);
287cb557757SDmitry Osipenko 	mc_writel(mc, value & ~BIT(rst->bit), rst->reset);
288cb557757SDmitry Osipenko 
289cb557757SDmitry Osipenko 	spin_unlock_irqrestore(&mc->lock, flags);
290cb557757SDmitry Osipenko 
291cb557757SDmitry Osipenko 	return 0;
292cb557757SDmitry Osipenko }
293cb557757SDmitry Osipenko 
294cb2b5839SThierry Reding static int tegra20_mc_hotreset_deassert(struct tegra_mc *mc,
295cb557757SDmitry Osipenko 					const struct tegra_mc_reset *rst)
296cb557757SDmitry Osipenko {
297cb557757SDmitry Osipenko 	unsigned long flags;
298cb557757SDmitry Osipenko 	u32 value;
299cb557757SDmitry Osipenko 
300cb557757SDmitry Osipenko 	spin_lock_irqsave(&mc->lock, flags);
301cb557757SDmitry Osipenko 
302cb557757SDmitry Osipenko 	value = mc_readl(mc, rst->reset);
303cb557757SDmitry Osipenko 	mc_writel(mc, value | BIT(rst->bit), rst->reset);
304cb557757SDmitry Osipenko 
305cb557757SDmitry Osipenko 	spin_unlock_irqrestore(&mc->lock, flags);
306cb557757SDmitry Osipenko 
307cb557757SDmitry Osipenko 	return 0;
308cb557757SDmitry Osipenko }
309cb557757SDmitry Osipenko 
310cb2b5839SThierry Reding static int tegra20_mc_block_dma(struct tegra_mc *mc,
311cb557757SDmitry Osipenko 				const struct tegra_mc_reset *rst)
312cb557757SDmitry Osipenko {
313cb557757SDmitry Osipenko 	unsigned long flags;
314cb557757SDmitry Osipenko 	u32 value;
315cb557757SDmitry Osipenko 
316cb557757SDmitry Osipenko 	spin_lock_irqsave(&mc->lock, flags);
317cb557757SDmitry Osipenko 
318cb557757SDmitry Osipenko 	value = mc_readl(mc, rst->control) & ~BIT(rst->bit);
319cb557757SDmitry Osipenko 	mc_writel(mc, value, rst->control);
320cb557757SDmitry Osipenko 
321cb557757SDmitry Osipenko 	spin_unlock_irqrestore(&mc->lock, flags);
322cb557757SDmitry Osipenko 
323cb557757SDmitry Osipenko 	return 0;
324cb557757SDmitry Osipenko }
325cb557757SDmitry Osipenko 
326cb2b5839SThierry Reding static bool tegra20_mc_dma_idling(struct tegra_mc *mc,
327cb557757SDmitry Osipenko 				  const struct tegra_mc_reset *rst)
328cb557757SDmitry Osipenko {
329cb557757SDmitry Osipenko 	return mc_readl(mc, rst->status) == 0;
330cb557757SDmitry Osipenko }
331cb557757SDmitry Osipenko 
332cb2b5839SThierry Reding static int tegra20_mc_reset_status(struct tegra_mc *mc,
333cb557757SDmitry Osipenko 				   const struct tegra_mc_reset *rst)
334cb557757SDmitry Osipenko {
335cb557757SDmitry Osipenko 	return (mc_readl(mc, rst->reset) & BIT(rst->bit)) == 0;
336cb557757SDmitry Osipenko }
337cb557757SDmitry Osipenko 
338cb2b5839SThierry Reding static int tegra20_mc_unblock_dma(struct tegra_mc *mc,
339cb557757SDmitry Osipenko 				  const struct tegra_mc_reset *rst)
340cb557757SDmitry Osipenko {
341cb557757SDmitry Osipenko 	unsigned long flags;
342cb557757SDmitry Osipenko 	u32 value;
343cb557757SDmitry Osipenko 
344cb557757SDmitry Osipenko 	spin_lock_irqsave(&mc->lock, flags);
345cb557757SDmitry Osipenko 
346cb557757SDmitry Osipenko 	value = mc_readl(mc, rst->control) | BIT(rst->bit);
347cb557757SDmitry Osipenko 	mc_writel(mc, value, rst->control);
348cb557757SDmitry Osipenko 
349cb557757SDmitry Osipenko 	spin_unlock_irqrestore(&mc->lock, flags);
350cb557757SDmitry Osipenko 
351cb557757SDmitry Osipenko 	return 0;
352cb557757SDmitry Osipenko }
353cb557757SDmitry Osipenko 
354cb2b5839SThierry Reding static const struct tegra_mc_reset_ops tegra20_mc_reset_ops = {
355cb2b5839SThierry Reding 	.hotreset_assert = tegra20_mc_hotreset_assert,
356cb2b5839SThierry Reding 	.hotreset_deassert = tegra20_mc_hotreset_deassert,
357cb2b5839SThierry Reding 	.block_dma = tegra20_mc_block_dma,
358cb2b5839SThierry Reding 	.dma_idling = tegra20_mc_dma_idling,
359cb2b5839SThierry Reding 	.unblock_dma = tegra20_mc_unblock_dma,
360cb2b5839SThierry Reding 	.reset_status = tegra20_mc_reset_status,
361cb557757SDmitry Osipenko };
362cb557757SDmitry Osipenko 
363d5ef16baSDmitry Osipenko static int tegra20_mc_icc_set(struct icc_node *src, struct icc_node *dst)
364d5ef16baSDmitry Osipenko {
365d5ef16baSDmitry Osipenko 	/*
366d5ef16baSDmitry Osipenko 	 * It should be possible to tune arbitration knobs here, but the
367d5ef16baSDmitry Osipenko 	 * default values are known to work well on all devices. Hence
368d5ef16baSDmitry Osipenko 	 * nothing to do here so far.
369d5ef16baSDmitry Osipenko 	 */
370d5ef16baSDmitry Osipenko 	return 0;
371d5ef16baSDmitry Osipenko }
372d5ef16baSDmitry Osipenko 
373d5ef16baSDmitry Osipenko static int tegra20_mc_icc_aggreate(struct icc_node *node, u32 tag, u32 avg_bw,
374d5ef16baSDmitry Osipenko 				   u32 peak_bw, u32 *agg_avg, u32 *agg_peak)
375d5ef16baSDmitry Osipenko {
376d5ef16baSDmitry Osipenko 	/*
377d5ef16baSDmitry Osipenko 	 * ISO clients need to reserve extra bandwidth up-front because
378d5ef16baSDmitry Osipenko 	 * there could be high bandwidth pressure during initial filling
379d5ef16baSDmitry Osipenko 	 * of the client's FIFO buffers.  Secondly, we need to take into
380d5ef16baSDmitry Osipenko 	 * account impurities of the memory subsystem.
381d5ef16baSDmitry Osipenko 	 */
382d5ef16baSDmitry Osipenko 	if (tag & TEGRA_MC_ICC_TAG_ISO)
383d5ef16baSDmitry Osipenko 		peak_bw = tegra_mc_scale_percents(peak_bw, 300);
384d5ef16baSDmitry Osipenko 
385d5ef16baSDmitry Osipenko 	*agg_avg += avg_bw;
386d5ef16baSDmitry Osipenko 	*agg_peak = max(*agg_peak, peak_bw);
387d5ef16baSDmitry Osipenko 
388d5ef16baSDmitry Osipenko 	return 0;
389d5ef16baSDmitry Osipenko }
390d5ef16baSDmitry Osipenko 
391d5ef16baSDmitry Osipenko static struct icc_node_data *
392d5ef16baSDmitry Osipenko tegra20_mc_of_icc_xlate_extended(struct of_phandle_args *spec, void *data)
393d5ef16baSDmitry Osipenko {
394d5ef16baSDmitry Osipenko 	struct tegra_mc *mc = icc_provider_to_tegra_mc(data);
395d5ef16baSDmitry Osipenko 	unsigned int i, idx = spec->args[0];
396d5ef16baSDmitry Osipenko 	struct icc_node_data *ndata;
397d5ef16baSDmitry Osipenko 	struct icc_node *node;
398d5ef16baSDmitry Osipenko 
399d5ef16baSDmitry Osipenko 	list_for_each_entry(node, &mc->provider.nodes, node_list) {
400d5ef16baSDmitry Osipenko 		if (node->id != idx)
401d5ef16baSDmitry Osipenko 			continue;
402d5ef16baSDmitry Osipenko 
403d5ef16baSDmitry Osipenko 		ndata = kzalloc(sizeof(*ndata), GFP_KERNEL);
404d5ef16baSDmitry Osipenko 		if (!ndata)
405d5ef16baSDmitry Osipenko 			return ERR_PTR(-ENOMEM);
406d5ef16baSDmitry Osipenko 
407d5ef16baSDmitry Osipenko 		ndata->node = node;
408d5ef16baSDmitry Osipenko 
409d5ef16baSDmitry Osipenko 		/* these clients are isochronous by default */
410d5ef16baSDmitry Osipenko 		if (strstarts(node->name, "display") ||
411d5ef16baSDmitry Osipenko 		    strstarts(node->name, "vi"))
412d5ef16baSDmitry Osipenko 			ndata->tag = TEGRA_MC_ICC_TAG_ISO;
413d5ef16baSDmitry Osipenko 		else
414d5ef16baSDmitry Osipenko 			ndata->tag = TEGRA_MC_ICC_TAG_DEFAULT;
415d5ef16baSDmitry Osipenko 
416d5ef16baSDmitry Osipenko 		return ndata;
417d5ef16baSDmitry Osipenko 	}
418d5ef16baSDmitry Osipenko 
419d5ef16baSDmitry Osipenko 	for (i = 0; i < mc->soc->num_clients; i++) {
420d5ef16baSDmitry Osipenko 		if (mc->soc->clients[i].id == idx)
421d5ef16baSDmitry Osipenko 			return ERR_PTR(-EPROBE_DEFER);
422d5ef16baSDmitry Osipenko 	}
423d5ef16baSDmitry Osipenko 
424d5ef16baSDmitry Osipenko 	dev_err(mc->dev, "invalid ICC client ID %u\n", idx);
425d5ef16baSDmitry Osipenko 
426d5ef16baSDmitry Osipenko 	return ERR_PTR(-EINVAL);
427d5ef16baSDmitry Osipenko }
428d5ef16baSDmitry Osipenko 
429d5ef16baSDmitry Osipenko static const struct tegra_mc_icc_ops tegra20_mc_icc_ops = {
430d5ef16baSDmitry Osipenko 	.xlate_extended = tegra20_mc_of_icc_xlate_extended,
431d5ef16baSDmitry Osipenko 	.aggregate = tegra20_mc_icc_aggreate,
432d5ef16baSDmitry Osipenko 	.set = tegra20_mc_icc_set,
433d5ef16baSDmitry Osipenko };
434d5ef16baSDmitry Osipenko 
435fbd31f5aSDmitry Osipenko static u32 tegra20_mc_stat_gather_control(const struct tegra20_mc_stat_gather *g)
436fbd31f5aSDmitry Osipenko {
437fbd31f5aSDmitry Osipenko 	u32 control;
438fbd31f5aSDmitry Osipenko 
439fbd31f5aSDmitry Osipenko 	control  = FIELD_PREP(MC_STAT_CONTROL_EVENT, g->event);
440fbd31f5aSDmitry Osipenko 	control |= FIELD_PREP(MC_STAT_CONTROL_CLIENT_ID, g->client);
441fbd31f5aSDmitry Osipenko 	control |= FIELD_PREP(MC_STAT_CONTROL_PRI_EVENT, g->pri_event);
442fbd31f5aSDmitry Osipenko 	control |= FIELD_PREP(MC_STAT_CONTROL_FILTER_PRI, g->pri_filter);
443fbd31f5aSDmitry Osipenko 	control |= FIELD_PREP(MC_STAT_CONTROL_FILTER_CLIENT_ENABLE, g->client_enb);
444fbd31f5aSDmitry Osipenko 
445fbd31f5aSDmitry Osipenko 	return control;
446fbd31f5aSDmitry Osipenko }
447fbd31f5aSDmitry Osipenko 
448fbd31f5aSDmitry Osipenko static void tegra20_mc_stat_gather(struct tegra20_mc_stat *stat)
449fbd31f5aSDmitry Osipenko {
450fbd31f5aSDmitry Osipenko 	u32 clocks, count0, count1, control_0, control_1;
451fbd31f5aSDmitry Osipenko 	const struct tegra_mc *mc = stat->mc;
452fbd31f5aSDmitry Osipenko 
453fbd31f5aSDmitry Osipenko 	control_0 = tegra20_mc_stat_gather_control(&stat->gather0);
454fbd31f5aSDmitry Osipenko 	control_1 = tegra20_mc_stat_gather_control(&stat->gather1);
455fbd31f5aSDmitry Osipenko 
456fbd31f5aSDmitry Osipenko 	/*
4577ebb09dbSDmitry Osipenko 	 * Reset statistic gathers state, select statistics collection mode
4587ebb09dbSDmitry Osipenko 	 * and set clocks counter saturation limit to maximum.
459fbd31f5aSDmitry Osipenko 	 */
460fbd31f5aSDmitry Osipenko 	mc_writel(mc, 0x00000000, MC_STAT_CONTROL);
461fbd31f5aSDmitry Osipenko 	mc_writel(mc,  control_0, MC_STAT_EMC_CONTROL_0);
462fbd31f5aSDmitry Osipenko 	mc_writel(mc,  control_1, MC_STAT_EMC_CONTROL_1);
463fbd31f5aSDmitry Osipenko 	mc_writel(mc, 0xffffffff, MC_STAT_EMC_CLOCK_LIMIT);
464fbd31f5aSDmitry Osipenko 
465fbd31f5aSDmitry Osipenko 	mc_writel(mc, EMC_GATHER_ENABLE, MC_STAT_CONTROL);
466fbd31f5aSDmitry Osipenko 	fsleep(stat->sample_time_usec);
467fbd31f5aSDmitry Osipenko 	mc_writel(mc, EMC_GATHER_DISABLE, MC_STAT_CONTROL);
468fbd31f5aSDmitry Osipenko 
469fbd31f5aSDmitry Osipenko 	count0 = mc_readl(mc, MC_STAT_EMC_COUNT_0);
470fbd31f5aSDmitry Osipenko 	count1 = mc_readl(mc, MC_STAT_EMC_COUNT_1);
471fbd31f5aSDmitry Osipenko 	clocks = mc_readl(mc, MC_STAT_EMC_CLOCKS);
472fbd31f5aSDmitry Osipenko 	clocks = max(clocks / 100 / MC_FX_FRAC_SCALE, 1u);
473fbd31f5aSDmitry Osipenko 
474fbd31f5aSDmitry Osipenko 	stat->gather0.result = DIV_ROUND_UP(count0, clocks);
475fbd31f5aSDmitry Osipenko 	stat->gather1.result = DIV_ROUND_UP(count1, clocks);
476fbd31f5aSDmitry Osipenko }
477fbd31f5aSDmitry Osipenko 
478fbd31f5aSDmitry Osipenko static void tegra20_mc_stat_events(const struct tegra_mc *mc,
479fbd31f5aSDmitry Osipenko 				   const struct tegra_mc_client *client0,
480fbd31f5aSDmitry Osipenko 				   const struct tegra_mc_client *client1,
481fbd31f5aSDmitry Osipenko 				   unsigned int pri_filter,
482fbd31f5aSDmitry Osipenko 				   unsigned int pri_event,
483fbd31f5aSDmitry Osipenko 				   unsigned int event,
484fbd31f5aSDmitry Osipenko 				   unsigned int *result0,
485fbd31f5aSDmitry Osipenko 				   unsigned int *result1)
486fbd31f5aSDmitry Osipenko {
487fbd31f5aSDmitry Osipenko 	struct tegra20_mc_stat stat = {};
488fbd31f5aSDmitry Osipenko 
489fbd31f5aSDmitry Osipenko 	stat.gather0.client = client0 ? client0->id : 0;
490fbd31f5aSDmitry Osipenko 	stat.gather0.pri_filter = pri_filter;
491fbd31f5aSDmitry Osipenko 	stat.gather0.client_enb = !!client0;
492fbd31f5aSDmitry Osipenko 	stat.gather0.pri_event = pri_event;
493fbd31f5aSDmitry Osipenko 	stat.gather0.event = event;
494fbd31f5aSDmitry Osipenko 
495fbd31f5aSDmitry Osipenko 	stat.gather1.client = client1 ? client1->id : 0;
496fbd31f5aSDmitry Osipenko 	stat.gather1.pri_filter = pri_filter;
497fbd31f5aSDmitry Osipenko 	stat.gather1.client_enb = !!client1;
498fbd31f5aSDmitry Osipenko 	stat.gather1.pri_event = pri_event;
499fbd31f5aSDmitry Osipenko 	stat.gather1.event = event;
500fbd31f5aSDmitry Osipenko 
501fbd31f5aSDmitry Osipenko 	stat.sample_time_usec = MC_STAT_SAMPLE_TIME_USEC;
502fbd31f5aSDmitry Osipenko 	stat.mc = mc;
503fbd31f5aSDmitry Osipenko 
504fbd31f5aSDmitry Osipenko 	tegra20_mc_stat_gather(&stat);
505fbd31f5aSDmitry Osipenko 
506fbd31f5aSDmitry Osipenko 	*result0 = stat.gather0.result;
507fbd31f5aSDmitry Osipenko 	*result1 = stat.gather1.result;
508fbd31f5aSDmitry Osipenko }
509fbd31f5aSDmitry Osipenko 
510fbd31f5aSDmitry Osipenko static void tegra20_mc_collect_stats(const struct tegra_mc *mc,
511fbd31f5aSDmitry Osipenko 				     struct tegra20_mc_client_stat *stats)
512fbd31f5aSDmitry Osipenko {
513fbd31f5aSDmitry Osipenko 	const struct tegra_mc_client *client0, *client1;
514fbd31f5aSDmitry Osipenko 	unsigned int i;
515fbd31f5aSDmitry Osipenko 
516fbd31f5aSDmitry Osipenko 	/* collect memory controller utilization percent for each client */
517fbd31f5aSDmitry Osipenko 	for (i = 0; i < mc->soc->num_clients; i += 2) {
518fbd31f5aSDmitry Osipenko 		client0 = &mc->soc->clients[i];
519fbd31f5aSDmitry Osipenko 		client1 = &mc->soc->clients[i + 1];
520fbd31f5aSDmitry Osipenko 
521fbd31f5aSDmitry Osipenko 		if (i + 1 == mc->soc->num_clients)
522fbd31f5aSDmitry Osipenko 			client1 = NULL;
523fbd31f5aSDmitry Osipenko 
524fbd31f5aSDmitry Osipenko 		tegra20_mc_stat_events(mc, client0, client1,
525fbd31f5aSDmitry Osipenko 				       MC_STAT_CONTROL_FILTER_PRI_DISABLE,
526fbd31f5aSDmitry Osipenko 				       MC_STAT_CONTROL_PRI_EVENT_HP,
527fbd31f5aSDmitry Osipenko 				       MC_STAT_CONTROL_EVENT_QUALIFIED,
528fbd31f5aSDmitry Osipenko 				       &stats[i + 0].events,
529fbd31f5aSDmitry Osipenko 				       &stats[i + 1].events);
530fbd31f5aSDmitry Osipenko 	}
531fbd31f5aSDmitry Osipenko 
532fbd31f5aSDmitry Osipenko 	/* collect more info from active clients */
533fbd31f5aSDmitry Osipenko 	for (i = 0; i < mc->soc->num_clients; i++) {
534fbd31f5aSDmitry Osipenko 		unsigned int clienta, clientb = mc->soc->num_clients;
535fbd31f5aSDmitry Osipenko 
536fbd31f5aSDmitry Osipenko 		for (client0 = NULL; i < mc->soc->num_clients; i++) {
537fbd31f5aSDmitry Osipenko 			if (stats[i].events) {
538fbd31f5aSDmitry Osipenko 				client0 = &mc->soc->clients[i];
539fbd31f5aSDmitry Osipenko 				clienta = i++;
540fbd31f5aSDmitry Osipenko 				break;
541fbd31f5aSDmitry Osipenko 			}
542fbd31f5aSDmitry Osipenko 		}
543fbd31f5aSDmitry Osipenko 
544fbd31f5aSDmitry Osipenko 		for (client1 = NULL; i < mc->soc->num_clients; i++) {
545fbd31f5aSDmitry Osipenko 			if (stats[i].events) {
546fbd31f5aSDmitry Osipenko 				client1 = &mc->soc->clients[i];
547fbd31f5aSDmitry Osipenko 				clientb = i;
548fbd31f5aSDmitry Osipenko 				break;
549fbd31f5aSDmitry Osipenko 			}
550fbd31f5aSDmitry Osipenko 		}
551fbd31f5aSDmitry Osipenko 
552fbd31f5aSDmitry Osipenko 		if (!client0 && !client1)
553fbd31f5aSDmitry Osipenko 			break;
554fbd31f5aSDmitry Osipenko 
555fbd31f5aSDmitry Osipenko 		tegra20_mc_stat_events(mc, client0, client1,
556fbd31f5aSDmitry Osipenko 				       MC_STAT_CONTROL_FILTER_PRI_YES,
557fbd31f5aSDmitry Osipenko 				       MC_STAT_CONTROL_PRI_EVENT_HP,
558fbd31f5aSDmitry Osipenko 				       MC_STAT_CONTROL_EVENT_QUALIFIED,
559fbd31f5aSDmitry Osipenko 				       &stats[clienta].arb_high_prio,
560fbd31f5aSDmitry Osipenko 				       &stats[clientb].arb_high_prio);
561fbd31f5aSDmitry Osipenko 
562fbd31f5aSDmitry Osipenko 		tegra20_mc_stat_events(mc, client0, client1,
563fbd31f5aSDmitry Osipenko 				       MC_STAT_CONTROL_FILTER_PRI_YES,
564fbd31f5aSDmitry Osipenko 				       MC_STAT_CONTROL_PRI_EVENT_TM,
565fbd31f5aSDmitry Osipenko 				       MC_STAT_CONTROL_EVENT_QUALIFIED,
566fbd31f5aSDmitry Osipenko 				       &stats[clienta].arb_timeout,
567fbd31f5aSDmitry Osipenko 				       &stats[clientb].arb_timeout);
568fbd31f5aSDmitry Osipenko 
569fbd31f5aSDmitry Osipenko 		tegra20_mc_stat_events(mc, client0, client1,
570fbd31f5aSDmitry Osipenko 				       MC_STAT_CONTROL_FILTER_PRI_YES,
571fbd31f5aSDmitry Osipenko 				       MC_STAT_CONTROL_PRI_EVENT_BW,
572fbd31f5aSDmitry Osipenko 				       MC_STAT_CONTROL_EVENT_QUALIFIED,
573fbd31f5aSDmitry Osipenko 				       &stats[clienta].arb_bandwidth,
574fbd31f5aSDmitry Osipenko 				       &stats[clientb].arb_bandwidth);
575fbd31f5aSDmitry Osipenko 
576fbd31f5aSDmitry Osipenko 		tegra20_mc_stat_events(mc, client0, client1,
577fbd31f5aSDmitry Osipenko 				       MC_STAT_CONTROL_FILTER_PRI_DISABLE,
578fbd31f5aSDmitry Osipenko 				       MC_STAT_CONTROL_PRI_EVENT_HP,
579fbd31f5aSDmitry Osipenko 				       MC_STAT_CONTROL_EVENT_RD_WR_CHANGE,
580fbd31f5aSDmitry Osipenko 				       &stats[clienta].rd_wr_change,
581fbd31f5aSDmitry Osipenko 				       &stats[clientb].rd_wr_change);
582fbd31f5aSDmitry Osipenko 
583fbd31f5aSDmitry Osipenko 		tegra20_mc_stat_events(mc, client0, client1,
584fbd31f5aSDmitry Osipenko 				       MC_STAT_CONTROL_FILTER_PRI_DISABLE,
585fbd31f5aSDmitry Osipenko 				       MC_STAT_CONTROL_PRI_EVENT_HP,
586fbd31f5aSDmitry Osipenko 				       MC_STAT_CONTROL_EVENT_SUCCESSIVE,
587fbd31f5aSDmitry Osipenko 				       &stats[clienta].successive,
588fbd31f5aSDmitry Osipenko 				       &stats[clientb].successive);
589fbd31f5aSDmitry Osipenko 
590fbd31f5aSDmitry Osipenko 		tegra20_mc_stat_events(mc, client0, client1,
591fbd31f5aSDmitry Osipenko 				       MC_STAT_CONTROL_FILTER_PRI_DISABLE,
592fbd31f5aSDmitry Osipenko 				       MC_STAT_CONTROL_PRI_EVENT_HP,
593fbd31f5aSDmitry Osipenko 				       MC_STAT_CONTROL_EVENT_PAGE_MISS,
594fbd31f5aSDmitry Osipenko 				       &stats[clienta].page_miss,
595fbd31f5aSDmitry Osipenko 				       &stats[clientb].page_miss);
596fbd31f5aSDmitry Osipenko 	}
597fbd31f5aSDmitry Osipenko }
598fbd31f5aSDmitry Osipenko 
599fbd31f5aSDmitry Osipenko static void tegra20_mc_printf_percents(struct seq_file *s,
600fbd31f5aSDmitry Osipenko 				       const char *fmt,
601fbd31f5aSDmitry Osipenko 				       unsigned int percents_fx)
602fbd31f5aSDmitry Osipenko {
603fbd31f5aSDmitry Osipenko 	char percents_str[8];
604fbd31f5aSDmitry Osipenko 
605fbd31f5aSDmitry Osipenko 	snprintf(percents_str, ARRAY_SIZE(percents_str), "%3u.%02u%%",
606fbd31f5aSDmitry Osipenko 		 percents_fx / MC_FX_FRAC_SCALE, percents_fx % MC_FX_FRAC_SCALE);
607fbd31f5aSDmitry Osipenko 
608fbd31f5aSDmitry Osipenko 	seq_printf(s, fmt, percents_str);
609fbd31f5aSDmitry Osipenko }
610fbd31f5aSDmitry Osipenko 
611fbd31f5aSDmitry Osipenko static int tegra20_mc_stats_show(struct seq_file *s, void *unused)
612fbd31f5aSDmitry Osipenko {
613fbd31f5aSDmitry Osipenko 	const struct tegra_mc *mc = dev_get_drvdata(s->private);
614fbd31f5aSDmitry Osipenko 	struct tegra20_mc_client_stat *stats;
615fbd31f5aSDmitry Osipenko 	unsigned int i;
616fbd31f5aSDmitry Osipenko 
617fbd31f5aSDmitry Osipenko 	stats = kcalloc(mc->soc->num_clients + 1, sizeof(*stats), GFP_KERNEL);
618fbd31f5aSDmitry Osipenko 	if (!stats)
619fbd31f5aSDmitry Osipenko 		return -ENOMEM;
620fbd31f5aSDmitry Osipenko 
62128947198SDmitry Osipenko 	mutex_lock(&tegra20_mc_stat_lock);
62228947198SDmitry Osipenko 
623fbd31f5aSDmitry Osipenko 	tegra20_mc_collect_stats(mc, stats);
624fbd31f5aSDmitry Osipenko 
62528947198SDmitry Osipenko 	mutex_unlock(&tegra20_mc_stat_lock);
62628947198SDmitry Osipenko 
627fbd31f5aSDmitry Osipenko 	seq_puts(s, "Memory client   Events   Timeout   High priority   Bandwidth ARB   RW change   Successive   Page miss\n");
628fbd31f5aSDmitry Osipenko 	seq_puts(s, "-----------------------------------------------------------------------------------------------------\n");
629fbd31f5aSDmitry Osipenko 
630fbd31f5aSDmitry Osipenko 	for (i = 0; i < mc->soc->num_clients; i++) {
631fbd31f5aSDmitry Osipenko 		seq_printf(s, "%-14s  ", mc->soc->clients[i].name);
632fbd31f5aSDmitry Osipenko 
633fbd31f5aSDmitry Osipenko 		/* An event is generated when client performs R/W request. */
634fbd31f5aSDmitry Osipenko 		tegra20_mc_printf_percents(s,  "%-9s", stats[i].events);
635fbd31f5aSDmitry Osipenko 
636fbd31f5aSDmitry Osipenko 		/*
637fbd31f5aSDmitry Osipenko 		 * An event is generated based on the timeout (TM) signal
638fbd31f5aSDmitry Osipenko 		 * accompanying a request for arbitration.
639fbd31f5aSDmitry Osipenko 		 */
640fbd31f5aSDmitry Osipenko 		tegra20_mc_printf_percents(s, "%-10s", stats[i].arb_timeout);
641fbd31f5aSDmitry Osipenko 
642fbd31f5aSDmitry Osipenko 		/*
643fbd31f5aSDmitry Osipenko 		 * An event is generated based on the high-priority (HP) signal
644fbd31f5aSDmitry Osipenko 		 * accompanying a request for arbitration.
645fbd31f5aSDmitry Osipenko 		 */
646fbd31f5aSDmitry Osipenko 		tegra20_mc_printf_percents(s, "%-16s", stats[i].arb_high_prio);
647fbd31f5aSDmitry Osipenko 
648fbd31f5aSDmitry Osipenko 		/*
649fbd31f5aSDmitry Osipenko 		 * An event is generated based on the bandwidth (BW) signal
650fbd31f5aSDmitry Osipenko 		 * accompanying a request for arbitration.
651fbd31f5aSDmitry Osipenko 		 */
652fbd31f5aSDmitry Osipenko 		tegra20_mc_printf_percents(s, "%-16s", stats[i].arb_bandwidth);
653fbd31f5aSDmitry Osipenko 
654fbd31f5aSDmitry Osipenko 		/*
655fbd31f5aSDmitry Osipenko 		 * An event is generated when the memory controller switches
656fbd31f5aSDmitry Osipenko 		 * between making a read request to making a write request.
657fbd31f5aSDmitry Osipenko 		 */
658fbd31f5aSDmitry Osipenko 		tegra20_mc_printf_percents(s, "%-12s", stats[i].rd_wr_change);
659fbd31f5aSDmitry Osipenko 
660fbd31f5aSDmitry Osipenko 		/*
661fbd31f5aSDmitry Osipenko 		 * An even generated when the chosen client has wins arbitration
662fbd31f5aSDmitry Osipenko 		 * when it was also the winner at the previous request.  If a
663fbd31f5aSDmitry Osipenko 		 * client makes N requests in a row that are honored, SUCCESSIVE
664fbd31f5aSDmitry Osipenko 		 * will be counted (N-1) times.  Large values for this event
665fbd31f5aSDmitry Osipenko 		 * imply that if we were patient enough, all of those requests
666fbd31f5aSDmitry Osipenko 		 * could have been coalesced.
667fbd31f5aSDmitry Osipenko 		 */
668fbd31f5aSDmitry Osipenko 		tegra20_mc_printf_percents(s, "%-13s", stats[i].successive);
669fbd31f5aSDmitry Osipenko 
670fbd31f5aSDmitry Osipenko 		/*
671fbd31f5aSDmitry Osipenko 		 * An event is generated when the memory controller detects a
672fbd31f5aSDmitry Osipenko 		 * page miss for the current request.
673fbd31f5aSDmitry Osipenko 		 */
674fbd31f5aSDmitry Osipenko 		tegra20_mc_printf_percents(s, "%-12s\n", stats[i].page_miss);
675fbd31f5aSDmitry Osipenko 	}
676fbd31f5aSDmitry Osipenko 
677fbd31f5aSDmitry Osipenko 	kfree(stats);
678fbd31f5aSDmitry Osipenko 
679fbd31f5aSDmitry Osipenko 	return 0;
680fbd31f5aSDmitry Osipenko }
681fbd31f5aSDmitry Osipenko 
682c64738e9SThierry Reding static int tegra20_mc_probe(struct tegra_mc *mc)
683fbd31f5aSDmitry Osipenko {
684fbd31f5aSDmitry Osipenko 	debugfs_create_devm_seqfile(mc->dev, "stats", mc->debugfs.root,
685fbd31f5aSDmitry Osipenko 				    tegra20_mc_stats_show);
686fbd31f5aSDmitry Osipenko 
687fbd31f5aSDmitry Osipenko 	return 0;
688fbd31f5aSDmitry Osipenko }
689fbd31f5aSDmitry Osipenko 
6905c9016f0SThierry Reding static int tegra20_mc_suspend(struct tegra_mc *mc)
6915c9016f0SThierry Reding {
6925c9016f0SThierry Reding 	int err;
6935c9016f0SThierry Reding 
6945c9016f0SThierry Reding 	if (IS_ENABLED(CONFIG_TEGRA_IOMMU_GART) && mc->gart) {
6955c9016f0SThierry Reding 		err = tegra_gart_suspend(mc->gart);
6965c9016f0SThierry Reding 		if (err < 0)
6975c9016f0SThierry Reding 			return err;
6985c9016f0SThierry Reding 	}
6995c9016f0SThierry Reding 
7005c9016f0SThierry Reding 	return 0;
7015c9016f0SThierry Reding }
7025c9016f0SThierry Reding 
7035c9016f0SThierry Reding static int tegra20_mc_resume(struct tegra_mc *mc)
7045c9016f0SThierry Reding {
7055c9016f0SThierry Reding 	int err;
7065c9016f0SThierry Reding 
7075c9016f0SThierry Reding 	if (IS_ENABLED(CONFIG_TEGRA_IOMMU_GART) && mc->gart) {
7085c9016f0SThierry Reding 		err = tegra_gart_resume(mc->gart);
7095c9016f0SThierry Reding 		if (err < 0)
7105c9016f0SThierry Reding 			return err;
7115c9016f0SThierry Reding 	}
7125c9016f0SThierry Reding 
7135c9016f0SThierry Reding 	return 0;
7145c9016f0SThierry Reding }
7155c9016f0SThierry Reding 
716*1079a66bSThierry Reding static irqreturn_t tegra20_mc_handle_irq(int irq, void *data)
717*1079a66bSThierry Reding {
718*1079a66bSThierry Reding 	struct tegra_mc *mc = data;
719*1079a66bSThierry Reding 	unsigned long status;
720*1079a66bSThierry Reding 	unsigned int bit;
721*1079a66bSThierry Reding 
722*1079a66bSThierry Reding 	/* mask all interrupts to avoid flooding */
723*1079a66bSThierry Reding 	status = mc_readl(mc, MC_INTSTATUS) & mc->soc->intmask;
724*1079a66bSThierry Reding 	if (!status)
725*1079a66bSThierry Reding 		return IRQ_NONE;
726*1079a66bSThierry Reding 
727*1079a66bSThierry Reding 	for_each_set_bit(bit, &status, 32) {
728*1079a66bSThierry Reding 		const char *error = tegra_mc_status_names[bit];
729*1079a66bSThierry Reding 		const char *direction = "read", *secure = "";
730*1079a66bSThierry Reding 		const char *client, *desc;
731*1079a66bSThierry Reding 		phys_addr_t addr;
732*1079a66bSThierry Reding 		u32 value, reg;
733*1079a66bSThierry Reding 		u8 id, type;
734*1079a66bSThierry Reding 
735*1079a66bSThierry Reding 		switch (BIT(bit)) {
736*1079a66bSThierry Reding 		case MC_INT_DECERR_EMEM:
737*1079a66bSThierry Reding 			reg = MC_DECERR_EMEM_OTHERS_STATUS;
738*1079a66bSThierry Reding 			value = mc_readl(mc, reg);
739*1079a66bSThierry Reding 
740*1079a66bSThierry Reding 			id = value & mc->soc->client_id_mask;
741*1079a66bSThierry Reding 			desc = tegra_mc_error_names[2];
742*1079a66bSThierry Reding 
743*1079a66bSThierry Reding 			if (value & BIT(31))
744*1079a66bSThierry Reding 				direction = "write";
745*1079a66bSThierry Reding 			break;
746*1079a66bSThierry Reding 
747*1079a66bSThierry Reding 		case MC_INT_INVALID_GART_PAGE:
748*1079a66bSThierry Reding 			reg = MC_GART_ERROR_REQ;
749*1079a66bSThierry Reding 			value = mc_readl(mc, reg);
750*1079a66bSThierry Reding 
751*1079a66bSThierry Reding 			id = (value >> 1) & mc->soc->client_id_mask;
752*1079a66bSThierry Reding 			desc = tegra_mc_error_names[2];
753*1079a66bSThierry Reding 
754*1079a66bSThierry Reding 			if (value & BIT(0))
755*1079a66bSThierry Reding 				direction = "write";
756*1079a66bSThierry Reding 			break;
757*1079a66bSThierry Reding 
758*1079a66bSThierry Reding 		case MC_INT_SECURITY_VIOLATION:
759*1079a66bSThierry Reding 			reg = MC_SECURITY_VIOLATION_STATUS;
760*1079a66bSThierry Reding 			value = mc_readl(mc, reg);
761*1079a66bSThierry Reding 
762*1079a66bSThierry Reding 			id = value & mc->soc->client_id_mask;
763*1079a66bSThierry Reding 			type = (value & BIT(30)) ? 4 : 3;
764*1079a66bSThierry Reding 			desc = tegra_mc_error_names[type];
765*1079a66bSThierry Reding 			secure = "secure ";
766*1079a66bSThierry Reding 
767*1079a66bSThierry Reding 			if (value & BIT(31))
768*1079a66bSThierry Reding 				direction = "write";
769*1079a66bSThierry Reding 			break;
770*1079a66bSThierry Reding 
771*1079a66bSThierry Reding 		default:
772*1079a66bSThierry Reding 			continue;
773*1079a66bSThierry Reding 		}
774*1079a66bSThierry Reding 
775*1079a66bSThierry Reding 		client = mc->soc->clients[id].name;
776*1079a66bSThierry Reding 		addr = mc_readl(mc, reg + sizeof(u32));
777*1079a66bSThierry Reding 
778*1079a66bSThierry Reding 		dev_err_ratelimited(mc->dev, "%s: %s%s @%pa: %s (%s)\n",
779*1079a66bSThierry Reding 				    client, secure, direction, &addr, error,
780*1079a66bSThierry Reding 				    desc);
781*1079a66bSThierry Reding 	}
782*1079a66bSThierry Reding 
783*1079a66bSThierry Reding 	/* clear interrupts */
784*1079a66bSThierry Reding 	mc_writel(mc, status, MC_INTSTATUS);
785*1079a66bSThierry Reding 
786*1079a66bSThierry Reding 	return IRQ_HANDLED;
787*1079a66bSThierry Reding }
788*1079a66bSThierry Reding 
7896cc884c1SThierry Reding static const struct tegra_mc_ops tegra20_mc_ops = {
790c64738e9SThierry Reding 	.probe = tegra20_mc_probe,
7915c9016f0SThierry Reding 	.suspend = tegra20_mc_suspend,
7925c9016f0SThierry Reding 	.resume = tegra20_mc_resume,
793*1079a66bSThierry Reding 	.handle_irq = tegra20_mc_handle_irq,
7946cc884c1SThierry Reding };
7956cc884c1SThierry Reding 
796a8d502fdSDmitry Osipenko const struct tegra_mc_soc tegra20_mc_soc = {
797a8d502fdSDmitry Osipenko 	.clients = tegra20_mc_clients,
798a8d502fdSDmitry Osipenko 	.num_clients = ARRAY_SIZE(tegra20_mc_clients),
799a8d502fdSDmitry Osipenko 	.num_address_bits = 32,
800a8d502fdSDmitry Osipenko 	.client_id_mask = 0x3f,
801a8d502fdSDmitry Osipenko 	.intmask = MC_INT_SECURITY_VIOLATION | MC_INT_INVALID_GART_PAGE |
802a8d502fdSDmitry Osipenko 		   MC_INT_DECERR_EMEM,
803cb2b5839SThierry Reding 	.reset_ops = &tegra20_mc_reset_ops,
804cb557757SDmitry Osipenko 	.resets = tegra20_mc_resets,
805cb557757SDmitry Osipenko 	.num_resets = ARRAY_SIZE(tegra20_mc_resets),
806d5ef16baSDmitry Osipenko 	.icc_ops = &tegra20_mc_icc_ops,
8076cc884c1SThierry Reding 	.ops = &tegra20_mc_ops,
808a8d502fdSDmitry Osipenko };
809