xref: /openbmc/linux/drivers/iommu/fsl_pamu.c (revision 1ac731c529cd4d6adbce134754b51ff7d822b145)
1d94d71cbSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2695093e3SVarun Sethi /*
3695093e3SVarun Sethi  *
4695093e3SVarun Sethi  * Copyright (C) 2013 Freescale Semiconductor, Inc.
5695093e3SVarun Sethi  */
6695093e3SVarun Sethi 
7695093e3SVarun Sethi #define pr_fmt(fmt)    "fsl-pamu: %s: " fmt, __func__
8695093e3SVarun Sethi 
9695093e3SVarun Sethi #include "fsl_pamu.h"
10695093e3SVarun Sethi 
1194848654SScott Wood #include <linux/fsl/guts.h>
12cd70d465SEmil Medve #include <linux/interrupt.h>
13cd70d465SEmil Medve #include <linux/genalloc.h>
14cae8d1f5SChristophe Leroy #include <linux/of_address.h>
15cae8d1f5SChristophe Leroy #include <linux/of_irq.h>
16cae8d1f5SChristophe Leroy #include <linux/platform_device.h>
17cd70d465SEmil Medve 
18cd70d465SEmil Medve #include <asm/mpc85xx.h>
19cd70d465SEmil Medve 
20695093e3SVarun Sethi /* define indexes for each operation mapping scenario */
21695093e3SVarun Sethi #define OMI_QMAN        0x00
22695093e3SVarun Sethi #define OMI_FMAN        0x01
23695093e3SVarun Sethi #define OMI_QMAN_PRIV   0x02
24695093e3SVarun Sethi #define OMI_CAAM        0x03
25695093e3SVarun Sethi 
26695093e3SVarun Sethi #define make64(high, low) (((u64)(high) << 32) | (low))
27695093e3SVarun Sethi 
28695093e3SVarun Sethi struct pamu_isr_data {
29695093e3SVarun Sethi 	void __iomem *pamu_reg_base;	/* Base address of PAMU regs */
30695093e3SVarun Sethi 	unsigned int count;		/* The number of PAMUs */
31695093e3SVarun Sethi };
32695093e3SVarun Sethi 
33695093e3SVarun Sethi static struct paace *ppaact;
34695093e3SVarun Sethi static struct paace *spaact;
35695093e3SVarun Sethi 
3607eb6fdfSJoerg Roedel static bool probed;			/* Has PAMU been probed? */
3707eb6fdfSJoerg Roedel 
38695093e3SVarun Sethi /*
39695093e3SVarun Sethi  * Table for matching compatible strings, for device tree
40695093e3SVarun Sethi  * guts node, for QorIQ SOCs.
41695093e3SVarun Sethi  * "fsl,qoriq-device-config-2.0" corresponds to T4 & B4
42695093e3SVarun Sethi  * SOCs. For the older SOCs "fsl,qoriq-device-config-1.0"
43695093e3SVarun Sethi  * string would be used.
44695093e3SVarun Sethi  */
4557fb907dSEmil Medve static const struct of_device_id guts_device_ids[] = {
46695093e3SVarun Sethi 	{ .compatible = "fsl,qoriq-device-config-1.0", },
47695093e3SVarun Sethi 	{ .compatible = "fsl,qoriq-device-config-2.0", },
48695093e3SVarun Sethi 	{}
49695093e3SVarun Sethi };
50695093e3SVarun Sethi 
51695093e3SVarun Sethi /*
52695093e3SVarun Sethi  * Table for matching compatible strings, for device tree
53695093e3SVarun Sethi  * L3 cache controller node.
54695093e3SVarun Sethi  * "fsl,t4240-l3-cache-controller" corresponds to T4,
55695093e3SVarun Sethi  * "fsl,b4860-l3-cache-controller" corresponds to B4 &
56695093e3SVarun Sethi  * "fsl,p4080-l3-cache-controller" corresponds to other,
57695093e3SVarun Sethi  * SOCs.
58695093e3SVarun Sethi  */
59695093e3SVarun Sethi static const struct of_device_id l3_device_ids[] = {
60695093e3SVarun Sethi 	{ .compatible = "fsl,t4240-l3-cache-controller", },
61695093e3SVarun Sethi 	{ .compatible = "fsl,b4860-l3-cache-controller", },
62695093e3SVarun Sethi 	{ .compatible = "fsl,p4080-l3-cache-controller", },
63695093e3SVarun Sethi 	{}
64695093e3SVarun Sethi };
65695093e3SVarun Sethi 
66695093e3SVarun Sethi /* maximum subwindows permitted per liodn */
67695093e3SVarun Sethi static u32 max_subwindow_count;
68695093e3SVarun Sethi 
69695093e3SVarun Sethi /**
70695093e3SVarun Sethi  * pamu_get_ppaace() - Return the primary PACCE
71695093e3SVarun Sethi  * @liodn: liodn PAACT index for desired PAACE
72695093e3SVarun Sethi  *
73695093e3SVarun Sethi  * Returns the ppace pointer upon success else return
74695093e3SVarun Sethi  * null.
75695093e3SVarun Sethi  */
pamu_get_ppaace(int liodn)76695093e3SVarun Sethi static struct paace *pamu_get_ppaace(int liodn)
77695093e3SVarun Sethi {
78695093e3SVarun Sethi 	if (!ppaact || liodn >= PAACE_NUMBER_ENTRIES) {
79695093e3SVarun Sethi 		pr_debug("PPAACT doesn't exist\n");
80695093e3SVarun Sethi 		return NULL;
81695093e3SVarun Sethi 	}
82695093e3SVarun Sethi 
83695093e3SVarun Sethi 	return &ppaact[liodn];
84695093e3SVarun Sethi }
85695093e3SVarun Sethi 
86695093e3SVarun Sethi /**
87695093e3SVarun Sethi  * pamu_enable_liodn() - Set valid bit of PACCE
88695093e3SVarun Sethi  * @liodn: liodn PAACT index for desired PAACE
89695093e3SVarun Sethi  *
90695093e3SVarun Sethi  * Returns 0 upon success else error code < 0 returned
91695093e3SVarun Sethi  */
pamu_enable_liodn(int liodn)92695093e3SVarun Sethi int pamu_enable_liodn(int liodn)
93695093e3SVarun Sethi {
94695093e3SVarun Sethi 	struct paace *ppaace;
95695093e3SVarun Sethi 
96695093e3SVarun Sethi 	ppaace = pamu_get_ppaace(liodn);
97695093e3SVarun Sethi 	if (!ppaace) {
98695093e3SVarun Sethi 		pr_debug("Invalid primary paace entry\n");
99695093e3SVarun Sethi 		return -ENOENT;
100695093e3SVarun Sethi 	}
101695093e3SVarun Sethi 
102695093e3SVarun Sethi 	if (!get_bf(ppaace->addr_bitfields, PPAACE_AF_WSE)) {
103695093e3SVarun Sethi 		pr_debug("liodn %d not configured\n", liodn);
104695093e3SVarun Sethi 		return -EINVAL;
105695093e3SVarun Sethi 	}
106695093e3SVarun Sethi 
107695093e3SVarun Sethi 	/* Ensure that all other stores to the ppaace complete first */
108695093e3SVarun Sethi 	mb();
109695093e3SVarun Sethi 
110695093e3SVarun Sethi 	set_bf(ppaace->addr_bitfields, PAACE_AF_V, PAACE_V_VALID);
111695093e3SVarun Sethi 	mb();
112695093e3SVarun Sethi 
113695093e3SVarun Sethi 	return 0;
114695093e3SVarun Sethi }
115695093e3SVarun Sethi 
116695093e3SVarun Sethi /**
117695093e3SVarun Sethi  * pamu_disable_liodn() - Clears valid bit of PACCE
118695093e3SVarun Sethi  * @liodn: liodn PAACT index for desired PAACE
119695093e3SVarun Sethi  *
120695093e3SVarun Sethi  * Returns 0 upon success else error code < 0 returned
121695093e3SVarun Sethi  */
pamu_disable_liodn(int liodn)122695093e3SVarun Sethi int pamu_disable_liodn(int liodn)
123695093e3SVarun Sethi {
124695093e3SVarun Sethi 	struct paace *ppaace;
125695093e3SVarun Sethi 
126695093e3SVarun Sethi 	ppaace = pamu_get_ppaace(liodn);
127695093e3SVarun Sethi 	if (!ppaace) {
128695093e3SVarun Sethi 		pr_debug("Invalid primary paace entry\n");
129695093e3SVarun Sethi 		return -ENOENT;
130695093e3SVarun Sethi 	}
131695093e3SVarun Sethi 
132695093e3SVarun Sethi 	set_bf(ppaace->addr_bitfields, PAACE_AF_V, PAACE_V_INVALID);
133695093e3SVarun Sethi 	mb();
134695093e3SVarun Sethi 
135695093e3SVarun Sethi 	return 0;
136695093e3SVarun Sethi }
137695093e3SVarun Sethi 
138695093e3SVarun Sethi /* Derive the window size encoding for a particular PAACE entry */
map_addrspace_size_to_wse(phys_addr_t addrspace_size)139695093e3SVarun Sethi static unsigned int map_addrspace_size_to_wse(phys_addr_t addrspace_size)
140695093e3SVarun Sethi {
141695093e3SVarun Sethi 	/* Bug if not a power of 2 */
142cd70d465SEmil Medve 	BUG_ON(addrspace_size & (addrspace_size - 1));
143695093e3SVarun Sethi 
144695093e3SVarun Sethi 	/* window size is 2^(WSE+1) bytes */
145d033f48fSVarun Sethi 	return fls64(addrspace_size) - 2;
146695093e3SVarun Sethi }
147695093e3SVarun Sethi 
148695093e3SVarun Sethi /*
149695093e3SVarun Sethi  * Set the PAACE type as primary and set the coherency required domain
150695093e3SVarun Sethi  * attribute
151695093e3SVarun Sethi  */
pamu_init_ppaace(struct paace * ppaace)152695093e3SVarun Sethi static void pamu_init_ppaace(struct paace *ppaace)
153695093e3SVarun Sethi {
154695093e3SVarun Sethi 	set_bf(ppaace->addr_bitfields, PAACE_AF_PT, PAACE_PT_PRIMARY);
155695093e3SVarun Sethi 
156695093e3SVarun Sethi 	set_bf(ppaace->domain_attr.to_host.coherency_required, PAACE_DA_HOST_CR,
157695093e3SVarun Sethi 	       PAACE_M_COHERENCE_REQ);
158695093e3SVarun Sethi }
159695093e3SVarun Sethi 
160695093e3SVarun Sethi /*
161695093e3SVarun Sethi  * Function used for updating stash destination for the coressponding
162695093e3SVarun Sethi  * LIODN.
163695093e3SVarun Sethi  */
pamu_update_paace_stash(int liodn,u32 value)164ba58d121SChristoph Hellwig int pamu_update_paace_stash(int liodn, u32 value)
165695093e3SVarun Sethi {
166695093e3SVarun Sethi 	struct paace *paace;
167695093e3SVarun Sethi 
168695093e3SVarun Sethi 	paace = pamu_get_ppaace(liodn);
169695093e3SVarun Sethi 	if (!paace) {
170695093e3SVarun Sethi 		pr_debug("Invalid liodn entry\n");
171695093e3SVarun Sethi 		return -ENOENT;
172695093e3SVarun Sethi 	}
173695093e3SVarun Sethi 	set_bf(paace->impl_attr, PAACE_IA_CID, value);
174695093e3SVarun Sethi 
175695093e3SVarun Sethi 	mb();
176695093e3SVarun Sethi 
177695093e3SVarun Sethi 	return 0;
178695093e3SVarun Sethi }
179695093e3SVarun Sethi 
180695093e3SVarun Sethi /**
181*829a7955SRandy Dunlap  * pamu_config_ppaace() - Sets up PPAACE entry for specified liodn
182695093e3SVarun Sethi  *
183695093e3SVarun Sethi  * @liodn: Logical IO device number
184695093e3SVarun Sethi  * @omi: Operation mapping index -- if ~omi == 0 then omi not defined
185695093e3SVarun Sethi  * @stashid: cache stash id for associated cpu -- if ~stashid == 0 then
186695093e3SVarun Sethi  *	     stashid not defined
187695093e3SVarun Sethi  * @prot: window permissions
188695093e3SVarun Sethi  *
189695093e3SVarun Sethi  * Returns 0 upon success else error code < 0 returned
190695093e3SVarun Sethi  */
pamu_config_ppaace(int liodn,u32 omi,u32 stashid,int prot)19157fa44beSChristoph Hellwig int pamu_config_ppaace(int liodn, u32 omi, u32 stashid, int prot)
192695093e3SVarun Sethi {
193695093e3SVarun Sethi 	struct paace *ppaace;
194695093e3SVarun Sethi 
195695093e3SVarun Sethi 	ppaace = pamu_get_ppaace(liodn);
196cd70d465SEmil Medve 	if (!ppaace)
197695093e3SVarun Sethi 		return -ENOENT;
198695093e3SVarun Sethi 
199695093e3SVarun Sethi 	/* window size is 2^(WSE+1) bytes */
200695093e3SVarun Sethi 	set_bf(ppaace->addr_bitfields, PPAACE_AF_WSE,
20157fa44beSChristoph Hellwig 	       map_addrspace_size_to_wse(1ULL << 36));
202695093e3SVarun Sethi 
203695093e3SVarun Sethi 	pamu_init_ppaace(ppaace);
204695093e3SVarun Sethi 
20557fa44beSChristoph Hellwig 	ppaace->wbah = 0;
20657fa44beSChristoph Hellwig 	set_bf(ppaace->addr_bitfields, PPAACE_AF_WBAL, 0);
207695093e3SVarun Sethi 
208695093e3SVarun Sethi 	/* set up operation mapping if it's configured */
209695093e3SVarun Sethi 	if (omi < OME_NUMBER_ENTRIES) {
210695093e3SVarun Sethi 		set_bf(ppaace->impl_attr, PAACE_IA_OTM, PAACE_OTM_INDEXED);
211695093e3SVarun Sethi 		ppaace->op_encode.index_ot.omi = omi;
212695093e3SVarun Sethi 	} else if (~omi != 0) {
213695093e3SVarun Sethi 		pr_debug("bad operation mapping index: %d\n", omi);
214bd7ebb77SNicolin Chen 		return -ENODEV;
215695093e3SVarun Sethi 	}
216695093e3SVarun Sethi 
217695093e3SVarun Sethi 	/* configure stash id */
218695093e3SVarun Sethi 	if (~stashid != 0)
219695093e3SVarun Sethi 		set_bf(ppaace->impl_attr, PAACE_IA_CID, stashid);
220695093e3SVarun Sethi 
221695093e3SVarun Sethi 	set_bf(ppaace->impl_attr, PAACE_IA_ATM, PAACE_ATM_WINDOW_XLATE);
222151f9414SChristoph Hellwig 	ppaace->twbah = 0;
223151f9414SChristoph Hellwig 	set_bf(ppaace->win_bitfields, PAACE_WIN_TWBAL, 0);
224695093e3SVarun Sethi 	set_bf(ppaace->addr_bitfields, PAACE_AF_AP, prot);
225695093e3SVarun Sethi 	set_bf(ppaace->impl_attr, PAACE_IA_WCE, 0);
226695093e3SVarun Sethi 	set_bf(ppaace->addr_bitfields, PPAACE_AF_MW, 0);
227695093e3SVarun Sethi 	mb();
228695093e3SVarun Sethi 
229695093e3SVarun Sethi 	return 0;
230695093e3SVarun Sethi }
231695093e3SVarun Sethi 
232695093e3SVarun Sethi /**
233695093e3SVarun Sethi  * get_ome_index() - Returns the index in the operation mapping table
234695093e3SVarun Sethi  *                   for device.
235*829a7955SRandy Dunlap  * @omi_index: pointer for storing the index value
236*829a7955SRandy Dunlap  * @dev: target device
237695093e3SVarun Sethi  *
238695093e3SVarun Sethi  */
get_ome_index(u32 * omi_index,struct device * dev)239695093e3SVarun Sethi void get_ome_index(u32 *omi_index, struct device *dev)
240695093e3SVarun Sethi {
241695093e3SVarun Sethi 	if (of_device_is_compatible(dev->of_node, "fsl,qman-portal"))
242695093e3SVarun Sethi 		*omi_index = OMI_QMAN;
243695093e3SVarun Sethi 	if (of_device_is_compatible(dev->of_node, "fsl,qman"))
244695093e3SVarun Sethi 		*omi_index = OMI_QMAN_PRIV;
245695093e3SVarun Sethi }
246695093e3SVarun Sethi 
247695093e3SVarun Sethi /**
248695093e3SVarun Sethi  * get_stash_id - Returns stash destination id corresponding to a
249695093e3SVarun Sethi  *                cache type and vcpu.
250695093e3SVarun Sethi  * @stash_dest_hint: L1, L2 or L3
251695093e3SVarun Sethi  * @vcpu: vpcu target for a particular cache type.
252695093e3SVarun Sethi  *
253695093e3SVarun Sethi  * Returs stash on success or ~(u32)0 on failure.
254695093e3SVarun Sethi  *
255695093e3SVarun Sethi  */
get_stash_id(u32 stash_dest_hint,u32 vcpu)256695093e3SVarun Sethi u32 get_stash_id(u32 stash_dest_hint, u32 vcpu)
257695093e3SVarun Sethi {
258695093e3SVarun Sethi 	const u32 *prop;
259695093e3SVarun Sethi 	struct device_node *node;
260695093e3SVarun Sethi 	u32 cache_level;
261695093e3SVarun Sethi 	int len, found = 0;
262695093e3SVarun Sethi 	int i;
263695093e3SVarun Sethi 
264695093e3SVarun Sethi 	/* Fastpath, exit early if L3/CPC cache is target for stashing */
265695093e3SVarun Sethi 	if (stash_dest_hint == PAMU_ATTR_CACHE_L3) {
266695093e3SVarun Sethi 		node = of_find_matching_node(NULL, l3_device_ids);
267695093e3SVarun Sethi 		if (node) {
268cd70d465SEmil Medve 			prop = of_get_property(node, "cache-stash-id", NULL);
269695093e3SVarun Sethi 			if (!prop) {
2706bd4f1c7SRob Herring 				pr_debug("missing cache-stash-id at %pOF\n",
2716bd4f1c7SRob Herring 					 node);
272695093e3SVarun Sethi 				of_node_put(node);
273695093e3SVarun Sethi 				return ~(u32)0;
274695093e3SVarun Sethi 			}
275695093e3SVarun Sethi 			of_node_put(node);
276695093e3SVarun Sethi 			return be32_to_cpup(prop);
277695093e3SVarun Sethi 		}
278695093e3SVarun Sethi 		return ~(u32)0;
279695093e3SVarun Sethi 	}
280695093e3SVarun Sethi 
281a9a455e8SRob Herring 	for_each_of_cpu_node(node) {
282695093e3SVarun Sethi 		prop = of_get_property(node, "reg", &len);
283695093e3SVarun Sethi 		for (i = 0; i < len / sizeof(u32); i++) {
284695093e3SVarun Sethi 			if (be32_to_cpup(&prop[i]) == vcpu) {
285695093e3SVarun Sethi 				found = 1;
286695093e3SVarun Sethi 				goto found_cpu_node;
287695093e3SVarun Sethi 			}
288695093e3SVarun Sethi 		}
289695093e3SVarun Sethi 	}
290695093e3SVarun Sethi found_cpu_node:
291695093e3SVarun Sethi 
292695093e3SVarun Sethi 	/* find the hwnode that represents the cache */
293695093e3SVarun Sethi 	for (cache_level = PAMU_ATTR_CACHE_L1; (cache_level < PAMU_ATTR_CACHE_L3) && found; cache_level++) {
294695093e3SVarun Sethi 		if (stash_dest_hint == cache_level) {
295cd70d465SEmil Medve 			prop = of_get_property(node, "cache-stash-id", NULL);
296695093e3SVarun Sethi 			if (!prop) {
2976bd4f1c7SRob Herring 				pr_debug("missing cache-stash-id at %pOF\n",
2986bd4f1c7SRob Herring 					 node);
299695093e3SVarun Sethi 				of_node_put(node);
300695093e3SVarun Sethi 				return ~(u32)0;
301695093e3SVarun Sethi 			}
302695093e3SVarun Sethi 			of_node_put(node);
303695093e3SVarun Sethi 			return be32_to_cpup(prop);
304695093e3SVarun Sethi 		}
305695093e3SVarun Sethi 
306cd70d465SEmil Medve 		prop = of_get_property(node, "next-level-cache", NULL);
307695093e3SVarun Sethi 		if (!prop) {
3086bd4f1c7SRob Herring 			pr_debug("can't find next-level-cache at %pOF\n", node);
309695093e3SVarun Sethi 			of_node_put(node);
310695093e3SVarun Sethi 			return ~(u32)0;  /* can't traverse any further */
311695093e3SVarun Sethi 		}
312695093e3SVarun Sethi 		of_node_put(node);
313695093e3SVarun Sethi 
314695093e3SVarun Sethi 		/* advance to next node in cache hierarchy */
315695093e3SVarun Sethi 		node = of_find_node_by_phandle(*prop);
316695093e3SVarun Sethi 		if (!node) {
317d6a71bf7SRickard Strandqvist 			pr_debug("Invalid node for cache hierarchy\n");
318695093e3SVarun Sethi 			return ~(u32)0;
319695093e3SVarun Sethi 		}
320695093e3SVarun Sethi 	}
321695093e3SVarun Sethi 
322695093e3SVarun Sethi 	pr_debug("stash dest not found for %d on vcpu %d\n",
323695093e3SVarun Sethi 		 stash_dest_hint, vcpu);
324695093e3SVarun Sethi 	return ~(u32)0;
325695093e3SVarun Sethi }
326695093e3SVarun Sethi 
327695093e3SVarun Sethi /* Identify if the PAACT table entry belongs to QMAN, BMAN or QMAN Portal */
328695093e3SVarun Sethi #define QMAN_PAACE 1
329695093e3SVarun Sethi #define QMAN_PORTAL_PAACE 2
330695093e3SVarun Sethi #define BMAN_PAACE 3
331695093e3SVarun Sethi 
332*829a7955SRandy Dunlap /*
333695093e3SVarun Sethi  * Setup operation mapping and stash destinations for QMAN and QMAN portal.
334695093e3SVarun Sethi  * Memory accesses to QMAN and BMAN private memory need not be coherent, so
335695093e3SVarun Sethi  * clear the PAACE entry coherency attribute for them.
336695093e3SVarun Sethi  */
setup_qbman_paace(struct paace * ppaace,int paace_type)33757fb907dSEmil Medve static void setup_qbman_paace(struct paace *ppaace, int  paace_type)
338695093e3SVarun Sethi {
339695093e3SVarun Sethi 	switch (paace_type) {
340695093e3SVarun Sethi 	case QMAN_PAACE:
341695093e3SVarun Sethi 		set_bf(ppaace->impl_attr, PAACE_IA_OTM, PAACE_OTM_INDEXED);
342695093e3SVarun Sethi 		ppaace->op_encode.index_ot.omi = OMI_QMAN_PRIV;
343695093e3SVarun Sethi 		/* setup QMAN Private data stashing for the L3 cache */
344695093e3SVarun Sethi 		set_bf(ppaace->impl_attr, PAACE_IA_CID, get_stash_id(PAMU_ATTR_CACHE_L3, 0));
345695093e3SVarun Sethi 		set_bf(ppaace->domain_attr.to_host.coherency_required, PAACE_DA_HOST_CR,
346695093e3SVarun Sethi 		       0);
347695093e3SVarun Sethi 		break;
348695093e3SVarun Sethi 	case QMAN_PORTAL_PAACE:
349695093e3SVarun Sethi 		set_bf(ppaace->impl_attr, PAACE_IA_OTM, PAACE_OTM_INDEXED);
350695093e3SVarun Sethi 		ppaace->op_encode.index_ot.omi = OMI_QMAN;
351695093e3SVarun Sethi 		/* Set DQRR and Frame stashing for the L3 cache */
352695093e3SVarun Sethi 		set_bf(ppaace->impl_attr, PAACE_IA_CID, get_stash_id(PAMU_ATTR_CACHE_L3, 0));
353695093e3SVarun Sethi 		break;
354695093e3SVarun Sethi 	case BMAN_PAACE:
355695093e3SVarun Sethi 		set_bf(ppaace->domain_attr.to_host.coherency_required, PAACE_DA_HOST_CR,
356695093e3SVarun Sethi 		       0);
357695093e3SVarun Sethi 		break;
358695093e3SVarun Sethi 	}
359695093e3SVarun Sethi }
360695093e3SVarun Sethi 
361*829a7955SRandy Dunlap /*
362695093e3SVarun Sethi  * Setup the operation mapping table for various devices. This is a static
363695093e3SVarun Sethi  * table where each table index corresponds to a particular device. PAMU uses
364695093e3SVarun Sethi  * this table to translate device transaction to appropriate corenet
365695093e3SVarun Sethi  * transaction.
366695093e3SVarun Sethi  */
setup_omt(struct ome * omt)36757fb907dSEmil Medve static void setup_omt(struct ome *omt)
368695093e3SVarun Sethi {
369695093e3SVarun Sethi 	struct ome *ome;
370695093e3SVarun Sethi 
371695093e3SVarun Sethi 	/* Configure OMI_QMAN */
372695093e3SVarun Sethi 	ome = &omt[OMI_QMAN];
373695093e3SVarun Sethi 
374695093e3SVarun Sethi 	ome->moe[IOE_READ_IDX] = EOE_VALID | EOE_READ;
375695093e3SVarun Sethi 	ome->moe[IOE_EREAD0_IDX] = EOE_VALID | EOE_RSA;
376695093e3SVarun Sethi 	ome->moe[IOE_WRITE_IDX] = EOE_VALID | EOE_WRITE;
377695093e3SVarun Sethi 	ome->moe[IOE_EWRITE0_IDX] = EOE_VALID | EOE_WWSAO;
378695093e3SVarun Sethi 
379695093e3SVarun Sethi 	ome->moe[IOE_DIRECT0_IDX] = EOE_VALID | EOE_LDEC;
380695093e3SVarun Sethi 	ome->moe[IOE_DIRECT1_IDX] = EOE_VALID | EOE_LDECPE;
381695093e3SVarun Sethi 
382695093e3SVarun Sethi 	/* Configure OMI_FMAN */
383695093e3SVarun Sethi 	ome = &omt[OMI_FMAN];
384695093e3SVarun Sethi 	ome->moe[IOE_READ_IDX]  = EOE_VALID | EOE_READI;
385695093e3SVarun Sethi 	ome->moe[IOE_WRITE_IDX] = EOE_VALID | EOE_WRITE;
386695093e3SVarun Sethi 
387695093e3SVarun Sethi 	/* Configure OMI_QMAN private */
388695093e3SVarun Sethi 	ome = &omt[OMI_QMAN_PRIV];
389695093e3SVarun Sethi 	ome->moe[IOE_READ_IDX]  = EOE_VALID | EOE_READ;
390695093e3SVarun Sethi 	ome->moe[IOE_WRITE_IDX] = EOE_VALID | EOE_WRITE;
391695093e3SVarun Sethi 	ome->moe[IOE_EREAD0_IDX] = EOE_VALID | EOE_RSA;
392695093e3SVarun Sethi 	ome->moe[IOE_EWRITE0_IDX] = EOE_VALID | EOE_WWSA;
393695093e3SVarun Sethi 
394695093e3SVarun Sethi 	/* Configure OMI_CAAM */
395695093e3SVarun Sethi 	ome = &omt[OMI_CAAM];
396695093e3SVarun Sethi 	ome->moe[IOE_READ_IDX]  = EOE_VALID | EOE_READI;
397695093e3SVarun Sethi 	ome->moe[IOE_WRITE_IDX] = EOE_VALID | EOE_WRITE;
398695093e3SVarun Sethi }
399695093e3SVarun Sethi 
400695093e3SVarun Sethi /*
401695093e3SVarun Sethi  * Get the maximum number of PAACT table entries
402695093e3SVarun Sethi  * and subwindows supported by PAMU
403695093e3SVarun Sethi  */
get_pamu_cap_values(unsigned long pamu_reg_base)40457fb907dSEmil Medve static void get_pamu_cap_values(unsigned long pamu_reg_base)
405695093e3SVarun Sethi {
406695093e3SVarun Sethi 	u32 pc_val;
407695093e3SVarun Sethi 
408695093e3SVarun Sethi 	pc_val = in_be32((u32 *)(pamu_reg_base + PAMU_PC3));
409695093e3SVarun Sethi 	/* Maximum number of subwindows per liodn */
410695093e3SVarun Sethi 	max_subwindow_count = 1 << (1 + PAMU_PC3_MWCE(pc_val));
411695093e3SVarun Sethi }
412695093e3SVarun Sethi 
413695093e3SVarun Sethi /* Setup PAMU registers pointing to PAACT, SPAACT and OMT */
setup_one_pamu(unsigned long pamu_reg_base,unsigned long pamu_reg_size,phys_addr_t ppaact_phys,phys_addr_t spaact_phys,phys_addr_t omt_phys)41457fb907dSEmil Medve static int setup_one_pamu(unsigned long pamu_reg_base, unsigned long pamu_reg_size,
415695093e3SVarun Sethi 			  phys_addr_t ppaact_phys, phys_addr_t spaact_phys,
416695093e3SVarun Sethi 			  phys_addr_t omt_phys)
417695093e3SVarun Sethi {
418695093e3SVarun Sethi 	u32 *pc;
419695093e3SVarun Sethi 	struct pamu_mmap_regs *pamu_regs;
420695093e3SVarun Sethi 
421695093e3SVarun Sethi 	pc = (u32 *) (pamu_reg_base + PAMU_PC);
422695093e3SVarun Sethi 	pamu_regs = (struct pamu_mmap_regs *)
423695093e3SVarun Sethi 		(pamu_reg_base + PAMU_MMAP_REGS_BASE);
424695093e3SVarun Sethi 
425695093e3SVarun Sethi 	/* set up pointers to corenet control blocks */
426695093e3SVarun Sethi 
427695093e3SVarun Sethi 	out_be32(&pamu_regs->ppbah, upper_32_bits(ppaact_phys));
428695093e3SVarun Sethi 	out_be32(&pamu_regs->ppbal, lower_32_bits(ppaact_phys));
429695093e3SVarun Sethi 	ppaact_phys = ppaact_phys + PAACT_SIZE;
430695093e3SVarun Sethi 	out_be32(&pamu_regs->pplah, upper_32_bits(ppaact_phys));
431695093e3SVarun Sethi 	out_be32(&pamu_regs->pplal, lower_32_bits(ppaact_phys));
432695093e3SVarun Sethi 
433695093e3SVarun Sethi 	out_be32(&pamu_regs->spbah, upper_32_bits(spaact_phys));
434695093e3SVarun Sethi 	out_be32(&pamu_regs->spbal, lower_32_bits(spaact_phys));
435695093e3SVarun Sethi 	spaact_phys = spaact_phys + SPAACT_SIZE;
436695093e3SVarun Sethi 	out_be32(&pamu_regs->splah, upper_32_bits(spaact_phys));
437695093e3SVarun Sethi 	out_be32(&pamu_regs->splal, lower_32_bits(spaact_phys));
438695093e3SVarun Sethi 
439695093e3SVarun Sethi 	out_be32(&pamu_regs->obah, upper_32_bits(omt_phys));
440695093e3SVarun Sethi 	out_be32(&pamu_regs->obal, lower_32_bits(omt_phys));
441695093e3SVarun Sethi 	omt_phys = omt_phys + OMT_SIZE;
442695093e3SVarun Sethi 	out_be32(&pamu_regs->olah, upper_32_bits(omt_phys));
443695093e3SVarun Sethi 	out_be32(&pamu_regs->olal, lower_32_bits(omt_phys));
444695093e3SVarun Sethi 
445695093e3SVarun Sethi 	/*
446695093e3SVarun Sethi 	 * set PAMU enable bit,
447695093e3SVarun Sethi 	 * allow ppaact & omt to be cached
448695093e3SVarun Sethi 	 * & enable PAMU access violation interrupts.
449695093e3SVarun Sethi 	 */
450695093e3SVarun Sethi 
451695093e3SVarun Sethi 	out_be32((u32 *)(pamu_reg_base + PAMU_PICS),
452695093e3SVarun Sethi 		 PAMU_ACCESS_VIOLATION_ENABLE);
453695093e3SVarun Sethi 	out_be32(pc, PAMU_PC_PE | PAMU_PC_OCE | PAMU_PC_SPCC | PAMU_PC_PPCC);
454695093e3SVarun Sethi 	return 0;
455695093e3SVarun Sethi }
456695093e3SVarun Sethi 
457695093e3SVarun Sethi /* Enable all device LIODNS */
setup_liodns(void)45857fb907dSEmil Medve static void setup_liodns(void)
459695093e3SVarun Sethi {
460695093e3SVarun Sethi 	int i, len;
461695093e3SVarun Sethi 	struct paace *ppaace;
462695093e3SVarun Sethi 	struct device_node *node = NULL;
463695093e3SVarun Sethi 	const u32 *prop;
464695093e3SVarun Sethi 
465695093e3SVarun Sethi 	for_each_node_with_property(node, "fsl,liodn") {
466695093e3SVarun Sethi 		prop = of_get_property(node, "fsl,liodn", &len);
467695093e3SVarun Sethi 		for (i = 0; i < len / sizeof(u32); i++) {
468695093e3SVarun Sethi 			int liodn;
469695093e3SVarun Sethi 
470695093e3SVarun Sethi 			liodn = be32_to_cpup(&prop[i]);
471695093e3SVarun Sethi 			if (liodn >= PAACE_NUMBER_ENTRIES) {
472695093e3SVarun Sethi 				pr_debug("Invalid LIODN value %d\n", liodn);
473695093e3SVarun Sethi 				continue;
474695093e3SVarun Sethi 			}
475695093e3SVarun Sethi 			ppaace = pamu_get_ppaace(liodn);
476695093e3SVarun Sethi 			pamu_init_ppaace(ppaace);
477695093e3SVarun Sethi 			/* window size is 2^(WSE+1) bytes */
478695093e3SVarun Sethi 			set_bf(ppaace->addr_bitfields, PPAACE_AF_WSE, 35);
479695093e3SVarun Sethi 			ppaace->wbah = 0;
480695093e3SVarun Sethi 			set_bf(ppaace->addr_bitfields, PPAACE_AF_WBAL, 0);
481695093e3SVarun Sethi 			set_bf(ppaace->impl_attr, PAACE_IA_ATM,
482695093e3SVarun Sethi 			       PAACE_ATM_NO_XLATE);
483695093e3SVarun Sethi 			set_bf(ppaace->addr_bitfields, PAACE_AF_AP,
484695093e3SVarun Sethi 			       PAACE_AP_PERMS_ALL);
485695093e3SVarun Sethi 			if (of_device_is_compatible(node, "fsl,qman-portal"))
486695093e3SVarun Sethi 				setup_qbman_paace(ppaace, QMAN_PORTAL_PAACE);
487695093e3SVarun Sethi 			if (of_device_is_compatible(node, "fsl,qman"))
488695093e3SVarun Sethi 				setup_qbman_paace(ppaace, QMAN_PAACE);
489695093e3SVarun Sethi 			if (of_device_is_compatible(node, "fsl,bman"))
490695093e3SVarun Sethi 				setup_qbman_paace(ppaace, BMAN_PAACE);
491695093e3SVarun Sethi 			mb();
492695093e3SVarun Sethi 			pamu_enable_liodn(liodn);
493695093e3SVarun Sethi 		}
494695093e3SVarun Sethi 	}
495695093e3SVarun Sethi }
496695093e3SVarun Sethi 
pamu_av_isr(int irq,void * arg)497cd70d465SEmil Medve static irqreturn_t pamu_av_isr(int irq, void *arg)
498695093e3SVarun Sethi {
499695093e3SVarun Sethi 	struct pamu_isr_data *data = arg;
500695093e3SVarun Sethi 	phys_addr_t phys;
501695093e3SVarun Sethi 	unsigned int i, j, ret;
502695093e3SVarun Sethi 
503634544bfSJoerg Roedel 	pr_emerg("access violation interrupt\n");
504695093e3SVarun Sethi 
505695093e3SVarun Sethi 	for (i = 0; i < data->count; i++) {
506695093e3SVarun Sethi 		void __iomem *p = data->pamu_reg_base + i * PAMU_OFFSET;
507695093e3SVarun Sethi 		u32 pics = in_be32(p + PAMU_PICS);
508695093e3SVarun Sethi 
509695093e3SVarun Sethi 		if (pics & PAMU_ACCESS_VIOLATION_STAT) {
510695093e3SVarun Sethi 			u32 avs1 = in_be32(p + PAMU_AVS1);
511695093e3SVarun Sethi 			struct paace *paace;
512695093e3SVarun Sethi 
513695093e3SVarun Sethi 			pr_emerg("POES1=%08x\n", in_be32(p + PAMU_POES1));
514695093e3SVarun Sethi 			pr_emerg("POES2=%08x\n", in_be32(p + PAMU_POES2));
515695093e3SVarun Sethi 			pr_emerg("AVS1=%08x\n", avs1);
516695093e3SVarun Sethi 			pr_emerg("AVS2=%08x\n", in_be32(p + PAMU_AVS2));
517cd70d465SEmil Medve 			pr_emerg("AVA=%016llx\n",
518cd70d465SEmil Medve 				 make64(in_be32(p + PAMU_AVAH),
519695093e3SVarun Sethi 					in_be32(p + PAMU_AVAL)));
520695093e3SVarun Sethi 			pr_emerg("UDAD=%08x\n", in_be32(p + PAMU_UDAD));
521cd70d465SEmil Medve 			pr_emerg("POEA=%016llx\n",
522cd70d465SEmil Medve 				 make64(in_be32(p + PAMU_POEAH),
523695093e3SVarun Sethi 					in_be32(p + PAMU_POEAL)));
524695093e3SVarun Sethi 
525695093e3SVarun Sethi 			phys = make64(in_be32(p + PAMU_POEAH),
526695093e3SVarun Sethi 				      in_be32(p + PAMU_POEAL));
527695093e3SVarun Sethi 
528695093e3SVarun Sethi 			/* Assume that POEA points to a PAACE */
529695093e3SVarun Sethi 			if (phys) {
530695093e3SVarun Sethi 				u32 *paace = phys_to_virt(phys);
531695093e3SVarun Sethi 
532695093e3SVarun Sethi 				/* Only the first four words are relevant */
533695093e3SVarun Sethi 				for (j = 0; j < 4; j++)
534cd70d465SEmil Medve 					pr_emerg("PAACE[%u]=%08x\n",
535cd70d465SEmil Medve 						 j, in_be32(paace + j));
536695093e3SVarun Sethi 			}
537695093e3SVarun Sethi 
538695093e3SVarun Sethi 			/* clear access violation condition */
539cd70d465SEmil Medve 			out_be32(p + PAMU_AVS1, avs1 & PAMU_AV_MASK);
540695093e3SVarun Sethi 			paace = pamu_get_ppaace(avs1 >> PAMU_AVS1_LIODN_SHIFT);
541695093e3SVarun Sethi 			BUG_ON(!paace);
542695093e3SVarun Sethi 			/* check if we got a violation for a disabled LIODN */
543695093e3SVarun Sethi 			if (!get_bf(paace->addr_bitfields, PAACE_AF_V)) {
544695093e3SVarun Sethi 				/*
545695093e3SVarun Sethi 				 * As per hardware erratum A-003638, access
546695093e3SVarun Sethi 				 * violation can be reported for a disabled
547695093e3SVarun Sethi 				 * LIODN. If we hit that condition, disable
548695093e3SVarun Sethi 				 * access violation reporting.
549695093e3SVarun Sethi 				 */
550695093e3SVarun Sethi 				pics &= ~PAMU_ACCESS_VIOLATION_ENABLE;
551695093e3SVarun Sethi 			} else {
552695093e3SVarun Sethi 				/* Disable the LIODN */
553695093e3SVarun Sethi 				ret = pamu_disable_liodn(avs1 >> PAMU_AVS1_LIODN_SHIFT);
554695093e3SVarun Sethi 				BUG_ON(ret);
555cd70d465SEmil Medve 				pr_emerg("Disabling liodn %x\n",
556cd70d465SEmil Medve 					 avs1 >> PAMU_AVS1_LIODN_SHIFT);
557695093e3SVarun Sethi 			}
558695093e3SVarun Sethi 			out_be32((p + PAMU_PICS), pics);
559695093e3SVarun Sethi 		}
560695093e3SVarun Sethi 	}
561695093e3SVarun Sethi 
562695093e3SVarun Sethi 	return IRQ_HANDLED;
563695093e3SVarun Sethi }
564695093e3SVarun Sethi 
565695093e3SVarun Sethi #define LAWAR_EN		0x80000000
566695093e3SVarun Sethi #define LAWAR_TARGET_MASK	0x0FF00000
567695093e3SVarun Sethi #define LAWAR_TARGET_SHIFT	20
568695093e3SVarun Sethi #define LAWAR_SIZE_MASK		0x0000003F
569695093e3SVarun Sethi #define LAWAR_CSDID_MASK	0x000FF000
570695093e3SVarun Sethi #define LAWAR_CSDID_SHIFT	12
571695093e3SVarun Sethi 
572695093e3SVarun Sethi #define LAW_SIZE_4K		0xb
573695093e3SVarun Sethi 
574695093e3SVarun Sethi struct ccsr_law {
575695093e3SVarun Sethi 	u32	lawbarh;	/* LAWn base address high */
576695093e3SVarun Sethi 	u32	lawbarl;	/* LAWn base address low */
577695093e3SVarun Sethi 	u32	lawar;		/* LAWn attributes */
578695093e3SVarun Sethi 	u32	reserved;
579695093e3SVarun Sethi };
580695093e3SVarun Sethi 
581695093e3SVarun Sethi /*
582695093e3SVarun Sethi  * Create a coherence subdomain for a given memory block.
583695093e3SVarun Sethi  */
create_csd(phys_addr_t phys,size_t size,u32 csd_port_id)58457fb907dSEmil Medve static int create_csd(phys_addr_t phys, size_t size, u32 csd_port_id)
585695093e3SVarun Sethi {
586695093e3SVarun Sethi 	struct device_node *np;
587695093e3SVarun Sethi 	const __be32 *iprop;
588695093e3SVarun Sethi 	void __iomem *lac = NULL;	/* Local Access Control registers */
589695093e3SVarun Sethi 	struct ccsr_law __iomem *law;
590695093e3SVarun Sethi 	void __iomem *ccm = NULL;
591695093e3SVarun Sethi 	u32 __iomem *csdids;
592695093e3SVarun Sethi 	unsigned int i, num_laws, num_csds;
593695093e3SVarun Sethi 	u32 law_target = 0;
594695093e3SVarun Sethi 	u32 csd_id = 0;
595695093e3SVarun Sethi 	int ret = 0;
596695093e3SVarun Sethi 
597695093e3SVarun Sethi 	np = of_find_compatible_node(NULL, NULL, "fsl,corenet-law");
598695093e3SVarun Sethi 	if (!np)
599695093e3SVarun Sethi 		return -ENODEV;
600695093e3SVarun Sethi 
601695093e3SVarun Sethi 	iprop = of_get_property(np, "fsl,num-laws", NULL);
602695093e3SVarun Sethi 	if (!iprop) {
603695093e3SVarun Sethi 		ret = -ENODEV;
604695093e3SVarun Sethi 		goto error;
605695093e3SVarun Sethi 	}
606695093e3SVarun Sethi 
607695093e3SVarun Sethi 	num_laws = be32_to_cpup(iprop);
608695093e3SVarun Sethi 	if (!num_laws) {
609695093e3SVarun Sethi 		ret = -ENODEV;
610695093e3SVarun Sethi 		goto error;
611695093e3SVarun Sethi 	}
612695093e3SVarun Sethi 
613695093e3SVarun Sethi 	lac = of_iomap(np, 0);
614695093e3SVarun Sethi 	if (!lac) {
615695093e3SVarun Sethi 		ret = -ENODEV;
616695093e3SVarun Sethi 		goto error;
617695093e3SVarun Sethi 	}
618695093e3SVarun Sethi 
619695093e3SVarun Sethi 	/* LAW registers are at offset 0xC00 */
620695093e3SVarun Sethi 	law = lac + 0xC00;
621695093e3SVarun Sethi 
622695093e3SVarun Sethi 	of_node_put(np);
623695093e3SVarun Sethi 
624695093e3SVarun Sethi 	np = of_find_compatible_node(NULL, NULL, "fsl,corenet-cf");
625695093e3SVarun Sethi 	if (!np) {
626695093e3SVarun Sethi 		ret = -ENODEV;
627695093e3SVarun Sethi 		goto error;
628695093e3SVarun Sethi 	}
629695093e3SVarun Sethi 
630695093e3SVarun Sethi 	iprop = of_get_property(np, "fsl,ccf-num-csdids", NULL);
631695093e3SVarun Sethi 	if (!iprop) {
632695093e3SVarun Sethi 		ret = -ENODEV;
633695093e3SVarun Sethi 		goto error;
634695093e3SVarun Sethi 	}
635695093e3SVarun Sethi 
636695093e3SVarun Sethi 	num_csds = be32_to_cpup(iprop);
637695093e3SVarun Sethi 	if (!num_csds) {
638695093e3SVarun Sethi 		ret = -ENODEV;
639695093e3SVarun Sethi 		goto error;
640695093e3SVarun Sethi 	}
641695093e3SVarun Sethi 
642695093e3SVarun Sethi 	ccm = of_iomap(np, 0);
643695093e3SVarun Sethi 	if (!ccm) {
644695093e3SVarun Sethi 		ret = -ENOMEM;
645695093e3SVarun Sethi 		goto error;
646695093e3SVarun Sethi 	}
647695093e3SVarun Sethi 
648695093e3SVarun Sethi 	/* The undocumented CSDID registers are at offset 0x600 */
649695093e3SVarun Sethi 	csdids = ccm + 0x600;
650695093e3SVarun Sethi 
651695093e3SVarun Sethi 	of_node_put(np);
652695093e3SVarun Sethi 	np = NULL;
653695093e3SVarun Sethi 
654695093e3SVarun Sethi 	/* Find an unused coherence subdomain ID */
655695093e3SVarun Sethi 	for (csd_id = 0; csd_id < num_csds; csd_id++) {
656695093e3SVarun Sethi 		if (!csdids[csd_id])
657695093e3SVarun Sethi 			break;
658695093e3SVarun Sethi 	}
659695093e3SVarun Sethi 
660695093e3SVarun Sethi 	/* Store the Port ID in the (undocumented) proper CIDMRxx register */
661695093e3SVarun Sethi 	csdids[csd_id] = csd_port_id;
662695093e3SVarun Sethi 
663695093e3SVarun Sethi 	/* Find the DDR LAW that maps to our buffer. */
664695093e3SVarun Sethi 	for (i = 0; i < num_laws; i++) {
665695093e3SVarun Sethi 		if (law[i].lawar & LAWAR_EN) {
666695093e3SVarun Sethi 			phys_addr_t law_start, law_end;
667695093e3SVarun Sethi 
668695093e3SVarun Sethi 			law_start = make64(law[i].lawbarh, law[i].lawbarl);
669695093e3SVarun Sethi 			law_end = law_start +
670695093e3SVarun Sethi 				(2ULL << (law[i].lawar & LAWAR_SIZE_MASK));
671695093e3SVarun Sethi 
672695093e3SVarun Sethi 			if (law_start <= phys && phys < law_end) {
673695093e3SVarun Sethi 				law_target = law[i].lawar & LAWAR_TARGET_MASK;
674695093e3SVarun Sethi 				break;
675695093e3SVarun Sethi 			}
676695093e3SVarun Sethi 		}
677695093e3SVarun Sethi 	}
678695093e3SVarun Sethi 
679695093e3SVarun Sethi 	if (i == 0 || i == num_laws) {
680695093e3SVarun Sethi 		/* This should never happen */
681695093e3SVarun Sethi 		ret = -ENOENT;
682695093e3SVarun Sethi 		goto error;
683695093e3SVarun Sethi 	}
684695093e3SVarun Sethi 
685695093e3SVarun Sethi 	/* Find a free LAW entry */
686695093e3SVarun Sethi 	while (law[--i].lawar & LAWAR_EN) {
687695093e3SVarun Sethi 		if (i == 0) {
688695093e3SVarun Sethi 			/* No higher priority LAW slots available */
689695093e3SVarun Sethi 			ret = -ENOENT;
690695093e3SVarun Sethi 			goto error;
691695093e3SVarun Sethi 		}
692695093e3SVarun Sethi 	}
693695093e3SVarun Sethi 
694695093e3SVarun Sethi 	law[i].lawbarh = upper_32_bits(phys);
695695093e3SVarun Sethi 	law[i].lawbarl = lower_32_bits(phys);
696695093e3SVarun Sethi 	wmb();
697695093e3SVarun Sethi 	law[i].lawar = LAWAR_EN | law_target | (csd_id << LAWAR_CSDID_SHIFT) |
698695093e3SVarun Sethi 		(LAW_SIZE_4K + get_order(size));
699695093e3SVarun Sethi 	wmb();
700695093e3SVarun Sethi 
701695093e3SVarun Sethi error:
702695093e3SVarun Sethi 	if (ccm)
703695093e3SVarun Sethi 		iounmap(ccm);
704695093e3SVarun Sethi 
705695093e3SVarun Sethi 	if (lac)
706695093e3SVarun Sethi 		iounmap(lac);
707695093e3SVarun Sethi 
708695093e3SVarun Sethi 	if (np)
709695093e3SVarun Sethi 		of_node_put(np);
710695093e3SVarun Sethi 
711695093e3SVarun Sethi 	return ret;
712695093e3SVarun Sethi }
713695093e3SVarun Sethi 
714695093e3SVarun Sethi /*
715695093e3SVarun Sethi  * Table of SVRs and the corresponding PORT_ID values. Port ID corresponds to a
716695093e3SVarun Sethi  * bit map of snoopers for a given range of memory mapped by a LAW.
717695093e3SVarun Sethi  *
718695093e3SVarun Sethi  * All future CoreNet-enabled SOCs will have this erratum(A-004510) fixed, so this
719695093e3SVarun Sethi  * table should never need to be updated.  SVRs are guaranteed to be unique, so
720695093e3SVarun Sethi  * there is no worry that a future SOC will inadvertently have one of these
721695093e3SVarun Sethi  * values.
722695093e3SVarun Sethi  */
723695093e3SVarun Sethi static const struct {
724695093e3SVarun Sethi 	u32 svr;
725695093e3SVarun Sethi 	u32 port_id;
72657fb907dSEmil Medve } port_id_map[] = {
727cd70d465SEmil Medve 	{(SVR_P2040 << 8) | 0x10, 0xFF000000},	/* P2040 1.0 */
728cd70d465SEmil Medve 	{(SVR_P2040 << 8) | 0x11, 0xFF000000},	/* P2040 1.1 */
729cd70d465SEmil Medve 	{(SVR_P2041 << 8) | 0x10, 0xFF000000},	/* P2041 1.0 */
730cd70d465SEmil Medve 	{(SVR_P2041 << 8) | 0x11, 0xFF000000},	/* P2041 1.1 */
731cd70d465SEmil Medve 	{(SVR_P3041 << 8) | 0x10, 0xFF000000},	/* P3041 1.0 */
732cd70d465SEmil Medve 	{(SVR_P3041 << 8) | 0x11, 0xFF000000},	/* P3041 1.1 */
733cd70d465SEmil Medve 	{(SVR_P4040 << 8) | 0x20, 0xFFF80000},	/* P4040 2.0 */
734cd70d465SEmil Medve 	{(SVR_P4080 << 8) | 0x20, 0xFFF80000},	/* P4080 2.0 */
735cd70d465SEmil Medve 	{(SVR_P5010 << 8) | 0x10, 0xFC000000},	/* P5010 1.0 */
736cd70d465SEmil Medve 	{(SVR_P5010 << 8) | 0x20, 0xFC000000},	/* P5010 2.0 */
737cd70d465SEmil Medve 	{(SVR_P5020 << 8) | 0x10, 0xFC000000},	/* P5020 1.0 */
738cd70d465SEmil Medve 	{(SVR_P5021 << 8) | 0x10, 0xFF800000},	/* P5021 1.0 */
739cd70d465SEmil Medve 	{(SVR_P5040 << 8) | 0x10, 0xFF800000},	/* P5040 1.0 */
740695093e3SVarun Sethi };
741695093e3SVarun Sethi 
742695093e3SVarun Sethi #define SVR_SECURITY	0x80000	/* The Security (E) bit */
743695093e3SVarun Sethi 
fsl_pamu_probe(struct platform_device * pdev)74457fb907dSEmil Medve static int fsl_pamu_probe(struct platform_device *pdev)
745695093e3SVarun Sethi {
746cd70d465SEmil Medve 	struct device *dev = &pdev->dev;
747695093e3SVarun Sethi 	void __iomem *pamu_regs = NULL;
748695093e3SVarun Sethi 	struct ccsr_guts __iomem *guts_regs = NULL;
749695093e3SVarun Sethi 	u32 pamubypenr, pamu_counter;
750695093e3SVarun Sethi 	unsigned long pamu_reg_off;
751695093e3SVarun Sethi 	unsigned long pamu_reg_base;
752695093e3SVarun Sethi 	struct pamu_isr_data *data = NULL;
753695093e3SVarun Sethi 	struct device_node *guts_node;
754695093e3SVarun Sethi 	u64 size;
755695093e3SVarun Sethi 	struct page *p;
756695093e3SVarun Sethi 	int ret = 0;
757695093e3SVarun Sethi 	int irq;
758695093e3SVarun Sethi 	phys_addr_t ppaact_phys;
759695093e3SVarun Sethi 	phys_addr_t spaact_phys;
76057fb907dSEmil Medve 	struct ome *omt;
761695093e3SVarun Sethi 	phys_addr_t omt_phys;
762695093e3SVarun Sethi 	size_t mem_size = 0;
763695093e3SVarun Sethi 	unsigned int order = 0;
764695093e3SVarun Sethi 	u32 csd_port_id = 0;
765695093e3SVarun Sethi 	unsigned i;
766695093e3SVarun Sethi 	/*
767695093e3SVarun Sethi 	 * enumerate all PAMUs and allocate and setup PAMU tables
768695093e3SVarun Sethi 	 * for each of them,
769695093e3SVarun Sethi 	 * NOTE : All PAMUs share the same LIODN tables.
770695093e3SVarun Sethi 	 */
771695093e3SVarun Sethi 
77207eb6fdfSJoerg Roedel 	if (WARN_ON(probed))
77307eb6fdfSJoerg Roedel 		return -EBUSY;
77407eb6fdfSJoerg Roedel 
775cd70d465SEmil Medve 	pamu_regs = of_iomap(dev->of_node, 0);
776695093e3SVarun Sethi 	if (!pamu_regs) {
777cd70d465SEmil Medve 		dev_err(dev, "ioremap of PAMU node failed\n");
778695093e3SVarun Sethi 		return -ENOMEM;
779695093e3SVarun Sethi 	}
780cd70d465SEmil Medve 	of_get_address(dev->of_node, 0, &size, NULL);
781695093e3SVarun Sethi 
782cd70d465SEmil Medve 	irq = irq_of_parse_and_map(dev->of_node, 0);
7838330b9ebSChristophe Leroy 	if (!irq) {
784cd70d465SEmil Medve 		dev_warn(dev, "no interrupts listed in PAMU node\n");
785695093e3SVarun Sethi 		goto error;
786695093e3SVarun Sethi 	}
787695093e3SVarun Sethi 
788cd70d465SEmil Medve 	data = kzalloc(sizeof(*data), GFP_KERNEL);
789695093e3SVarun Sethi 	if (!data) {
790695093e3SVarun Sethi 		ret = -ENOMEM;
791695093e3SVarun Sethi 		goto error;
792695093e3SVarun Sethi 	}
793695093e3SVarun Sethi 	data->pamu_reg_base = pamu_regs;
794695093e3SVarun Sethi 	data->count = size / PAMU_OFFSET;
795695093e3SVarun Sethi 
796695093e3SVarun Sethi 	/* The ISR needs access to the regs, so we won't iounmap them */
797695093e3SVarun Sethi 	ret = request_irq(irq, pamu_av_isr, 0, "pamu", data);
798695093e3SVarun Sethi 	if (ret < 0) {
799cd70d465SEmil Medve 		dev_err(dev, "error %i installing ISR for irq %i\n", ret, irq);
800695093e3SVarun Sethi 		goto error;
801695093e3SVarun Sethi 	}
802695093e3SVarun Sethi 
803695093e3SVarun Sethi 	guts_node = of_find_matching_node(NULL, guts_device_ids);
804695093e3SVarun Sethi 	if (!guts_node) {
8056bd4f1c7SRob Herring 		dev_err(dev, "could not find GUTS node %pOF\n", dev->of_node);
806695093e3SVarun Sethi 		ret = -ENODEV;
807695093e3SVarun Sethi 		goto error;
808695093e3SVarun Sethi 	}
809695093e3SVarun Sethi 
810695093e3SVarun Sethi 	guts_regs = of_iomap(guts_node, 0);
811695093e3SVarun Sethi 	of_node_put(guts_node);
812695093e3SVarun Sethi 	if (!guts_regs) {
813cd70d465SEmil Medve 		dev_err(dev, "ioremap of GUTS node failed\n");
814695093e3SVarun Sethi 		ret = -ENODEV;
815695093e3SVarun Sethi 		goto error;
816695093e3SVarun Sethi 	}
817695093e3SVarun Sethi 
818695093e3SVarun Sethi 	/* read in the PAMU capability registers */
819695093e3SVarun Sethi 	get_pamu_cap_values((unsigned long)pamu_regs);
820695093e3SVarun Sethi 	/*
821695093e3SVarun Sethi 	 * To simplify the allocation of a coherency domain, we allocate the
822695093e3SVarun Sethi 	 * PAACT and the OMT in the same memory buffer.  Unfortunately, this
823695093e3SVarun Sethi 	 * wastes more memory compared to allocating the buffers separately.
824695093e3SVarun Sethi 	 */
825695093e3SVarun Sethi 	/* Determine how much memory we need */
826695093e3SVarun Sethi 	mem_size = (PAGE_SIZE << get_order(PAACT_SIZE)) +
827695093e3SVarun Sethi 		(PAGE_SIZE << get_order(SPAACT_SIZE)) +
828695093e3SVarun Sethi 		(PAGE_SIZE << get_order(OMT_SIZE));
829695093e3SVarun Sethi 	order = get_order(mem_size);
830695093e3SVarun Sethi 
831695093e3SVarun Sethi 	p = alloc_pages(GFP_KERNEL | __GFP_ZERO, order);
832695093e3SVarun Sethi 	if (!p) {
833cd70d465SEmil Medve 		dev_err(dev, "unable to allocate PAACT/SPAACT/OMT block\n");
834695093e3SVarun Sethi 		ret = -ENOMEM;
835695093e3SVarun Sethi 		goto error;
836695093e3SVarun Sethi 	}
837695093e3SVarun Sethi 
838695093e3SVarun Sethi 	ppaact = page_address(p);
839695093e3SVarun Sethi 	ppaact_phys = page_to_phys(p);
840695093e3SVarun Sethi 
841695093e3SVarun Sethi 	/* Make sure the memory is naturally aligned */
842695093e3SVarun Sethi 	if (ppaact_phys & ((PAGE_SIZE << order) - 1)) {
843cd70d465SEmil Medve 		dev_err(dev, "PAACT/OMT block is unaligned\n");
844695093e3SVarun Sethi 		ret = -ENOMEM;
845695093e3SVarun Sethi 		goto error;
846695093e3SVarun Sethi 	}
847695093e3SVarun Sethi 
848695093e3SVarun Sethi 	spaact = (void *)ppaact + (PAGE_SIZE << get_order(PAACT_SIZE));
849695093e3SVarun Sethi 	omt = (void *)spaact + (PAGE_SIZE << get_order(SPAACT_SIZE));
850695093e3SVarun Sethi 
851cd70d465SEmil Medve 	dev_dbg(dev, "ppaact virt=%p phys=%pa\n", ppaact, &ppaact_phys);
852695093e3SVarun Sethi 
853695093e3SVarun Sethi 	/* Check to see if we need to implement the work-around on this SOC */
854695093e3SVarun Sethi 
855695093e3SVarun Sethi 	/* Determine the Port ID for our coherence subdomain */
856695093e3SVarun Sethi 	for (i = 0; i < ARRAY_SIZE(port_id_map); i++) {
857695093e3SVarun Sethi 		if (port_id_map[i].svr == (mfspr(SPRN_SVR) & ~SVR_SECURITY)) {
858695093e3SVarun Sethi 			csd_port_id = port_id_map[i].port_id;
859cd70d465SEmil Medve 			dev_dbg(dev, "found matching SVR %08x\n",
860695093e3SVarun Sethi 				port_id_map[i].svr);
861695093e3SVarun Sethi 			break;
862695093e3SVarun Sethi 		}
863695093e3SVarun Sethi 	}
864695093e3SVarun Sethi 
865695093e3SVarun Sethi 	if (csd_port_id) {
866cd70d465SEmil Medve 		dev_dbg(dev, "creating coherency subdomain at address %pa, size %zu, port id 0x%08x",
867cd70d465SEmil Medve 			&ppaact_phys, mem_size, csd_port_id);
868695093e3SVarun Sethi 
869695093e3SVarun Sethi 		ret = create_csd(ppaact_phys, mem_size, csd_port_id);
870695093e3SVarun Sethi 		if (ret) {
871cd70d465SEmil Medve 			dev_err(dev, "could not create coherence subdomain\n");
87273f5fc5fSYuan Can 			goto error;
873695093e3SVarun Sethi 		}
874695093e3SVarun Sethi 	}
875695093e3SVarun Sethi 
876695093e3SVarun Sethi 	spaact_phys = virt_to_phys(spaact);
877695093e3SVarun Sethi 	omt_phys = virt_to_phys(omt);
878695093e3SVarun Sethi 
879695093e3SVarun Sethi 	pamubypenr = in_be32(&guts_regs->pamubypenr);
880695093e3SVarun Sethi 
881695093e3SVarun Sethi 	for (pamu_reg_off = 0, pamu_counter = 0x80000000; pamu_reg_off < size;
882695093e3SVarun Sethi 	     pamu_reg_off += PAMU_OFFSET, pamu_counter >>= 1) {
883695093e3SVarun Sethi 
884695093e3SVarun Sethi 		pamu_reg_base = (unsigned long)pamu_regs + pamu_reg_off;
885695093e3SVarun Sethi 		setup_one_pamu(pamu_reg_base, pamu_reg_off, ppaact_phys,
886695093e3SVarun Sethi 			       spaact_phys, omt_phys);
887695093e3SVarun Sethi 		/* Disable PAMU bypass for this PAMU */
888695093e3SVarun Sethi 		pamubypenr &= ~pamu_counter;
889695093e3SVarun Sethi 	}
890695093e3SVarun Sethi 
891695093e3SVarun Sethi 	setup_omt(omt);
892695093e3SVarun Sethi 
893695093e3SVarun Sethi 	/* Enable all relevant PAMU(s) */
894695093e3SVarun Sethi 	out_be32(&guts_regs->pamubypenr, pamubypenr);
895695093e3SVarun Sethi 
896695093e3SVarun Sethi 	iounmap(guts_regs);
897695093e3SVarun Sethi 
898695093e3SVarun Sethi 	/* Enable DMA for the LIODNs in the device tree */
899695093e3SVarun Sethi 
900695093e3SVarun Sethi 	setup_liodns();
901695093e3SVarun Sethi 
90207eb6fdfSJoerg Roedel 	probed = true;
90307eb6fdfSJoerg Roedel 
904695093e3SVarun Sethi 	return 0;
905695093e3SVarun Sethi 
906695093e3SVarun Sethi error:
9078330b9ebSChristophe Leroy 	if (irq)
908695093e3SVarun Sethi 		free_irq(irq, data);
909695093e3SVarun Sethi 
910ce433d0fSAlex Dewar 	kfree_sensitive(data);
911695093e3SVarun Sethi 
912695093e3SVarun Sethi 	if (pamu_regs)
913695093e3SVarun Sethi 		iounmap(pamu_regs);
914695093e3SVarun Sethi 
915695093e3SVarun Sethi 	if (guts_regs)
916695093e3SVarun Sethi 		iounmap(guts_regs);
917695093e3SVarun Sethi 
918695093e3SVarun Sethi 	if (ppaact)
919695093e3SVarun Sethi 		free_pages((unsigned long)ppaact, order);
920695093e3SVarun Sethi 
921695093e3SVarun Sethi 	ppaact = NULL;
922695093e3SVarun Sethi 
923695093e3SVarun Sethi 	return ret;
924695093e3SVarun Sethi }
925695093e3SVarun Sethi 
92657fb907dSEmil Medve static struct platform_driver fsl_of_pamu_driver = {
927695093e3SVarun Sethi 	.driver = {
928695093e3SVarun Sethi 		.name = "fsl-of-pamu",
929695093e3SVarun Sethi 	},
930695093e3SVarun Sethi 	.probe = fsl_pamu_probe,
931695093e3SVarun Sethi };
932695093e3SVarun Sethi 
fsl_pamu_init(void)933695093e3SVarun Sethi static __init int fsl_pamu_init(void)
934695093e3SVarun Sethi {
935695093e3SVarun Sethi 	struct platform_device *pdev = NULL;
936695093e3SVarun Sethi 	struct device_node *np;
937695093e3SVarun Sethi 	int ret;
938695093e3SVarun Sethi 
939695093e3SVarun Sethi 	/*
940695093e3SVarun Sethi 	 * The normal OF process calls the probe function at some
941695093e3SVarun Sethi 	 * indeterminate later time, after most drivers have loaded.  This is
942695093e3SVarun Sethi 	 * too late for us, because PAMU clients (like the Qman driver)
943695093e3SVarun Sethi 	 * depend on PAMU being initialized early.
944695093e3SVarun Sethi 	 *
945695093e3SVarun Sethi 	 * So instead, we "manually" call our probe function by creating the
946695093e3SVarun Sethi 	 * platform devices ourselves.
947695093e3SVarun Sethi 	 */
948695093e3SVarun Sethi 
949695093e3SVarun Sethi 	/*
950695093e3SVarun Sethi 	 * We assume that there is only one PAMU node in the device tree.  A
951695093e3SVarun Sethi 	 * single PAMU node represents all of the PAMU devices in the SOC
952695093e3SVarun Sethi 	 * already.   Everything else already makes that assumption, and the
953695093e3SVarun Sethi 	 * binding for the PAMU nodes doesn't allow for any parent-child
954695093e3SVarun Sethi 	 * relationships anyway.  In other words, support for more than one
955695093e3SVarun Sethi 	 * PAMU node would require significant changes to a lot of code.
956695093e3SVarun Sethi 	 */
957695093e3SVarun Sethi 
958695093e3SVarun Sethi 	np = of_find_compatible_node(NULL, NULL, "fsl,pamu");
959695093e3SVarun Sethi 	if (!np) {
960634544bfSJoerg Roedel 		pr_err("could not find a PAMU node\n");
961695093e3SVarun Sethi 		return -ENODEV;
962695093e3SVarun Sethi 	}
963695093e3SVarun Sethi 
964695093e3SVarun Sethi 	ret = platform_driver_register(&fsl_of_pamu_driver);
965695093e3SVarun Sethi 	if (ret) {
966634544bfSJoerg Roedel 		pr_err("could not register driver (err=%i)\n", ret);
967695093e3SVarun Sethi 		goto error_driver_register;
968695093e3SVarun Sethi 	}
969695093e3SVarun Sethi 
970695093e3SVarun Sethi 	pdev = platform_device_alloc("fsl-of-pamu", 0);
971695093e3SVarun Sethi 	if (!pdev) {
9726bd4f1c7SRob Herring 		pr_err("could not allocate device %pOF\n", np);
973695093e3SVarun Sethi 		ret = -ENOMEM;
974695093e3SVarun Sethi 		goto error_device_alloc;
975695093e3SVarun Sethi 	}
976695093e3SVarun Sethi 	pdev->dev.of_node = of_node_get(np);
977695093e3SVarun Sethi 
978695093e3SVarun Sethi 	ret = pamu_domain_init();
979695093e3SVarun Sethi 	if (ret)
980695093e3SVarun Sethi 		goto error_device_add;
981695093e3SVarun Sethi 
982695093e3SVarun Sethi 	ret = platform_device_add(pdev);
983695093e3SVarun Sethi 	if (ret) {
9846bd4f1c7SRob Herring 		pr_err("could not add device %pOF (err=%i)\n", np, ret);
985695093e3SVarun Sethi 		goto error_device_add;
986695093e3SVarun Sethi 	}
987695093e3SVarun Sethi 
988695093e3SVarun Sethi 	return 0;
989695093e3SVarun Sethi 
990695093e3SVarun Sethi error_device_add:
991695093e3SVarun Sethi 	of_node_put(pdev->dev.of_node);
992695093e3SVarun Sethi 	pdev->dev.of_node = NULL;
993695093e3SVarun Sethi 
994695093e3SVarun Sethi 	platform_device_put(pdev);
995695093e3SVarun Sethi 
996695093e3SVarun Sethi error_device_alloc:
997695093e3SVarun Sethi 	platform_driver_unregister(&fsl_of_pamu_driver);
998695093e3SVarun Sethi 
999695093e3SVarun Sethi error_driver_register:
1000695093e3SVarun Sethi 	of_node_put(np);
1001695093e3SVarun Sethi 
1002695093e3SVarun Sethi 	return ret;
1003695093e3SVarun Sethi }
1004695093e3SVarun Sethi arch_initcall(fsl_pamu_init);
1005