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