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