xref: /openbmc/linux/drivers/memory/tegra/tegra20.c (revision 6a143a7c)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright (C) 2012 NVIDIA CORPORATION.  All rights reserved.
4  */
5 
6 #include <linux/of_device.h>
7 #include <linux/slab.h>
8 #include <linux/string.h>
9 
10 #include <dt-bindings/memory/tegra20-mc.h>
11 
12 #include "mc.h"
13 
14 static const struct tegra_mc_client tegra20_mc_clients[] = {
15 	{
16 		.id = 0x00,
17 		.name = "display0a",
18 	}, {
19 		.id = 0x01,
20 		.name = "display0ab",
21 	}, {
22 		.id = 0x02,
23 		.name = "display0b",
24 	}, {
25 		.id = 0x03,
26 		.name = "display0bb",
27 	}, {
28 		.id = 0x04,
29 		.name = "display0c",
30 	}, {
31 		.id = 0x05,
32 		.name = "display0cb",
33 	}, {
34 		.id = 0x06,
35 		.name = "display1b",
36 	}, {
37 		.id = 0x07,
38 		.name = "display1bb",
39 	}, {
40 		.id = 0x08,
41 		.name = "eppup",
42 	}, {
43 		.id = 0x09,
44 		.name = "g2pr",
45 	}, {
46 		.id = 0x0a,
47 		.name = "g2sr",
48 	}, {
49 		.id = 0x0b,
50 		.name = "mpeunifbr",
51 	}, {
52 		.id = 0x0c,
53 		.name = "viruv",
54 	}, {
55 		.id = 0x0d,
56 		.name = "avpcarm7r",
57 	}, {
58 		.id = 0x0e,
59 		.name = "displayhc",
60 	}, {
61 		.id = 0x0f,
62 		.name = "displayhcb",
63 	}, {
64 		.id = 0x10,
65 		.name = "fdcdrd",
66 	}, {
67 		.id = 0x11,
68 		.name = "g2dr",
69 	}, {
70 		.id = 0x12,
71 		.name = "host1xdmar",
72 	}, {
73 		.id = 0x13,
74 		.name = "host1xr",
75 	}, {
76 		.id = 0x14,
77 		.name = "idxsrd",
78 	}, {
79 		.id = 0x15,
80 		.name = "mpcorer",
81 	}, {
82 		.id = 0x16,
83 		.name = "mpe_ipred",
84 	}, {
85 		.id = 0x17,
86 		.name = "mpeamemrd",
87 	}, {
88 		.id = 0x18,
89 		.name = "mpecsrd",
90 	}, {
91 		.id = 0x19,
92 		.name = "ppcsahbdmar",
93 	}, {
94 		.id = 0x1a,
95 		.name = "ppcsahbslvr",
96 	}, {
97 		.id = 0x1b,
98 		.name = "texsrd",
99 	}, {
100 		.id = 0x1c,
101 		.name = "vdebsevr",
102 	}, {
103 		.id = 0x1d,
104 		.name = "vdember",
105 	}, {
106 		.id = 0x1e,
107 		.name = "vdemcer",
108 	}, {
109 		.id = 0x1f,
110 		.name = "vdetper",
111 	}, {
112 		.id = 0x20,
113 		.name = "eppu",
114 	}, {
115 		.id = 0x21,
116 		.name = "eppv",
117 	}, {
118 		.id = 0x22,
119 		.name = "eppy",
120 	}, {
121 		.id = 0x23,
122 		.name = "mpeunifbw",
123 	}, {
124 		.id = 0x24,
125 		.name = "viwsb",
126 	}, {
127 		.id = 0x25,
128 		.name = "viwu",
129 	}, {
130 		.id = 0x26,
131 		.name = "viwv",
132 	}, {
133 		.id = 0x27,
134 		.name = "viwy",
135 	}, {
136 		.id = 0x28,
137 		.name = "g2dw",
138 	}, {
139 		.id = 0x29,
140 		.name = "avpcarm7w",
141 	}, {
142 		.id = 0x2a,
143 		.name = "fdcdwr",
144 	}, {
145 		.id = 0x2b,
146 		.name = "host1xw",
147 	}, {
148 		.id = 0x2c,
149 		.name = "ispw",
150 	}, {
151 		.id = 0x2d,
152 		.name = "mpcorew",
153 	}, {
154 		.id = 0x2e,
155 		.name = "mpecswr",
156 	}, {
157 		.id = 0x2f,
158 		.name = "ppcsahbdmaw",
159 	}, {
160 		.id = 0x30,
161 		.name = "ppcsahbslvw",
162 	}, {
163 		.id = 0x31,
164 		.name = "vdebsevw",
165 	}, {
166 		.id = 0x32,
167 		.name = "vdembew",
168 	}, {
169 		.id = 0x33,
170 		.name = "vdetpmw",
171 	},
172 };
173 
174 #define TEGRA20_MC_RESET(_name, _control, _status, _reset, _bit)	\
175 	{								\
176 		.name = #_name,						\
177 		.id = TEGRA20_MC_RESET_##_name,				\
178 		.control = _control,					\
179 		.status = _status,					\
180 		.reset = _reset,					\
181 		.bit = _bit,						\
182 	}
183 
184 static const struct tegra_mc_reset tegra20_mc_resets[] = {
185 	TEGRA20_MC_RESET(AVPC,   0x100, 0x140, 0x104,  0),
186 	TEGRA20_MC_RESET(DC,     0x100, 0x144, 0x104,  1),
187 	TEGRA20_MC_RESET(DCB,    0x100, 0x148, 0x104,  2),
188 	TEGRA20_MC_RESET(EPP,    0x100, 0x14c, 0x104,  3),
189 	TEGRA20_MC_RESET(2D,     0x100, 0x150, 0x104,  4),
190 	TEGRA20_MC_RESET(HC,     0x100, 0x154, 0x104,  5),
191 	TEGRA20_MC_RESET(ISP,    0x100, 0x158, 0x104,  6),
192 	TEGRA20_MC_RESET(MPCORE, 0x100, 0x15c, 0x104,  7),
193 	TEGRA20_MC_RESET(MPEA,   0x100, 0x160, 0x104,  8),
194 	TEGRA20_MC_RESET(MPEB,   0x100, 0x164, 0x104,  9),
195 	TEGRA20_MC_RESET(MPEC,   0x100, 0x168, 0x104, 10),
196 	TEGRA20_MC_RESET(3D,     0x100, 0x16c, 0x104, 11),
197 	TEGRA20_MC_RESET(PPCS,   0x100, 0x170, 0x104, 12),
198 	TEGRA20_MC_RESET(VDE,    0x100, 0x174, 0x104, 13),
199 	TEGRA20_MC_RESET(VI,     0x100, 0x178, 0x104, 14),
200 };
201 
202 static int tegra20_mc_hotreset_assert(struct tegra_mc *mc,
203 				      const struct tegra_mc_reset *rst)
204 {
205 	unsigned long flags;
206 	u32 value;
207 
208 	spin_lock_irqsave(&mc->lock, flags);
209 
210 	value = mc_readl(mc, rst->reset);
211 	mc_writel(mc, value & ~BIT(rst->bit), rst->reset);
212 
213 	spin_unlock_irqrestore(&mc->lock, flags);
214 
215 	return 0;
216 }
217 
218 static int tegra20_mc_hotreset_deassert(struct tegra_mc *mc,
219 					const struct tegra_mc_reset *rst)
220 {
221 	unsigned long flags;
222 	u32 value;
223 
224 	spin_lock_irqsave(&mc->lock, flags);
225 
226 	value = mc_readl(mc, rst->reset);
227 	mc_writel(mc, value | BIT(rst->bit), rst->reset);
228 
229 	spin_unlock_irqrestore(&mc->lock, flags);
230 
231 	return 0;
232 }
233 
234 static int tegra20_mc_block_dma(struct tegra_mc *mc,
235 				const struct tegra_mc_reset *rst)
236 {
237 	unsigned long flags;
238 	u32 value;
239 
240 	spin_lock_irqsave(&mc->lock, flags);
241 
242 	value = mc_readl(mc, rst->control) & ~BIT(rst->bit);
243 	mc_writel(mc, value, rst->control);
244 
245 	spin_unlock_irqrestore(&mc->lock, flags);
246 
247 	return 0;
248 }
249 
250 static bool tegra20_mc_dma_idling(struct tegra_mc *mc,
251 				  const struct tegra_mc_reset *rst)
252 {
253 	return mc_readl(mc, rst->status) == 0;
254 }
255 
256 static int tegra20_mc_reset_status(struct tegra_mc *mc,
257 				   const struct tegra_mc_reset *rst)
258 {
259 	return (mc_readl(mc, rst->reset) & BIT(rst->bit)) == 0;
260 }
261 
262 static int tegra20_mc_unblock_dma(struct tegra_mc *mc,
263 				  const struct tegra_mc_reset *rst)
264 {
265 	unsigned long flags;
266 	u32 value;
267 
268 	spin_lock_irqsave(&mc->lock, flags);
269 
270 	value = mc_readl(mc, rst->control) | BIT(rst->bit);
271 	mc_writel(mc, value, rst->control);
272 
273 	spin_unlock_irqrestore(&mc->lock, flags);
274 
275 	return 0;
276 }
277 
278 static const struct tegra_mc_reset_ops tegra20_mc_reset_ops = {
279 	.hotreset_assert = tegra20_mc_hotreset_assert,
280 	.hotreset_deassert = tegra20_mc_hotreset_deassert,
281 	.block_dma = tegra20_mc_block_dma,
282 	.dma_idling = tegra20_mc_dma_idling,
283 	.unblock_dma = tegra20_mc_unblock_dma,
284 	.reset_status = tegra20_mc_reset_status,
285 };
286 
287 static int tegra20_mc_icc_set(struct icc_node *src, struct icc_node *dst)
288 {
289 	/*
290 	 * It should be possible to tune arbitration knobs here, but the
291 	 * default values are known to work well on all devices. Hence
292 	 * nothing to do here so far.
293 	 */
294 	return 0;
295 }
296 
297 static int tegra20_mc_icc_aggreate(struct icc_node *node, u32 tag, u32 avg_bw,
298 				   u32 peak_bw, u32 *agg_avg, u32 *agg_peak)
299 {
300 	/*
301 	 * ISO clients need to reserve extra bandwidth up-front because
302 	 * there could be high bandwidth pressure during initial filling
303 	 * of the client's FIFO buffers.  Secondly, we need to take into
304 	 * account impurities of the memory subsystem.
305 	 */
306 	if (tag & TEGRA_MC_ICC_TAG_ISO)
307 		peak_bw = tegra_mc_scale_percents(peak_bw, 300);
308 
309 	*agg_avg += avg_bw;
310 	*agg_peak = max(*agg_peak, peak_bw);
311 
312 	return 0;
313 }
314 
315 static struct icc_node_data *
316 tegra20_mc_of_icc_xlate_extended(struct of_phandle_args *spec, void *data)
317 {
318 	struct tegra_mc *mc = icc_provider_to_tegra_mc(data);
319 	unsigned int i, idx = spec->args[0];
320 	struct icc_node_data *ndata;
321 	struct icc_node *node;
322 
323 	list_for_each_entry(node, &mc->provider.nodes, node_list) {
324 		if (node->id != idx)
325 			continue;
326 
327 		ndata = kzalloc(sizeof(*ndata), GFP_KERNEL);
328 		if (!ndata)
329 			return ERR_PTR(-ENOMEM);
330 
331 		ndata->node = node;
332 
333 		/* these clients are isochronous by default */
334 		if (strstarts(node->name, "display") ||
335 		    strstarts(node->name, "vi"))
336 			ndata->tag = TEGRA_MC_ICC_TAG_ISO;
337 		else
338 			ndata->tag = TEGRA_MC_ICC_TAG_DEFAULT;
339 
340 		return ndata;
341 	}
342 
343 	for (i = 0; i < mc->soc->num_clients; i++) {
344 		if (mc->soc->clients[i].id == idx)
345 			return ERR_PTR(-EPROBE_DEFER);
346 	}
347 
348 	dev_err(mc->dev, "invalid ICC client ID %u\n", idx);
349 
350 	return ERR_PTR(-EINVAL);
351 }
352 
353 static const struct tegra_mc_icc_ops tegra20_mc_icc_ops = {
354 	.xlate_extended = tegra20_mc_of_icc_xlate_extended,
355 	.aggregate = tegra20_mc_icc_aggreate,
356 	.set = tegra20_mc_icc_set,
357 };
358 
359 const struct tegra_mc_soc tegra20_mc_soc = {
360 	.clients = tegra20_mc_clients,
361 	.num_clients = ARRAY_SIZE(tegra20_mc_clients),
362 	.num_address_bits = 32,
363 	.client_id_mask = 0x3f,
364 	.intmask = MC_INT_SECURITY_VIOLATION | MC_INT_INVALID_GART_PAGE |
365 		   MC_INT_DECERR_EMEM,
366 	.reset_ops = &tegra20_mc_reset_ops,
367 	.resets = tegra20_mc_resets,
368 	.num_resets = ARRAY_SIZE(tegra20_mc_resets),
369 	.icc_ops = &tegra20_mc_icc_ops,
370 };
371