xref: /openbmc/linux/drivers/soc/fsl/qe/qe.c (revision 77d7676a)
12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
27aa1aa6eSZhao Qiang /*
37aa1aa6eSZhao Qiang  * Copyright (C) 2006-2010 Freescale Semiconductor, Inc. All rights reserved.
47aa1aa6eSZhao Qiang  *
57aa1aa6eSZhao Qiang  * Authors: 	Shlomi Gridish <gridish@freescale.com>
67aa1aa6eSZhao Qiang  * 		Li Yang <leoli@freescale.com>
77aa1aa6eSZhao Qiang  * Based on cpm2_common.c from Dan Malek (dmalek@jlc.net)
87aa1aa6eSZhao Qiang  *
97aa1aa6eSZhao Qiang  * Description:
107aa1aa6eSZhao Qiang  * General Purpose functions for the global management of the
117aa1aa6eSZhao Qiang  * QUICC Engine (QE).
127aa1aa6eSZhao Qiang  */
13875f2aabSRasmus Villemoes #include <linux/bitmap.h>
147aa1aa6eSZhao Qiang #include <linux/errno.h>
157aa1aa6eSZhao Qiang #include <linux/sched.h>
167aa1aa6eSZhao Qiang #include <linux/kernel.h>
177aa1aa6eSZhao Qiang #include <linux/param.h>
187aa1aa6eSZhao Qiang #include <linux/string.h>
197aa1aa6eSZhao Qiang #include <linux/spinlock.h>
207aa1aa6eSZhao Qiang #include <linux/mm.h>
217aa1aa6eSZhao Qiang #include <linux/interrupt.h>
227aa1aa6eSZhao Qiang #include <linux/module.h>
237aa1aa6eSZhao Qiang #include <linux/delay.h>
247aa1aa6eSZhao Qiang #include <linux/ioport.h>
257aa1aa6eSZhao Qiang #include <linux/crc32.h>
267aa1aa6eSZhao Qiang #include <linux/mod_devicetable.h>
277aa1aa6eSZhao Qiang #include <linux/of_platform.h>
287aa1aa6eSZhao Qiang #include <asm/irq.h>
297aa1aa6eSZhao Qiang #include <asm/page.h>
307aa1aa6eSZhao Qiang #include <asm/pgtable.h>
317aa1aa6eSZhao Qiang #include <soc/fsl/qe/immap_qe.h>
327aa1aa6eSZhao Qiang #include <soc/fsl/qe/qe.h>
337aa1aa6eSZhao Qiang #include <asm/prom.h>
347aa1aa6eSZhao Qiang #include <asm/rheap.h>
357aa1aa6eSZhao Qiang 
367aa1aa6eSZhao Qiang static void qe_snums_init(void);
377aa1aa6eSZhao Qiang static int qe_sdma_init(void);
387aa1aa6eSZhao Qiang 
397aa1aa6eSZhao Qiang static DEFINE_SPINLOCK(qe_lock);
407aa1aa6eSZhao Qiang DEFINE_SPINLOCK(cmxgcr_lock);
417aa1aa6eSZhao Qiang EXPORT_SYMBOL(cmxgcr_lock);
427aa1aa6eSZhao Qiang 
437aa1aa6eSZhao Qiang /* We allocate this here because it is used almost exclusively for
447aa1aa6eSZhao Qiang  * the communication processor devices.
457aa1aa6eSZhao Qiang  */
467aa1aa6eSZhao Qiang struct qe_immap __iomem *qe_immr;
477aa1aa6eSZhao Qiang EXPORT_SYMBOL(qe_immr);
487aa1aa6eSZhao Qiang 
49875f2aabSRasmus Villemoes static u8 snums[QE_NUM_OF_SNUM];	/* Dynamically allocated SNUMs */
50875f2aabSRasmus Villemoes static DECLARE_BITMAP(snum_state, QE_NUM_OF_SNUM);
517aa1aa6eSZhao Qiang static unsigned int qe_num_of_snum;
527aa1aa6eSZhao Qiang 
537aa1aa6eSZhao Qiang static phys_addr_t qebase = -1;
547aa1aa6eSZhao Qiang 
55d7fc5963SRasmus Villemoes static struct device_node *qe_get_device_node(void)
56d7fc5963SRasmus Villemoes {
57d7fc5963SRasmus Villemoes 	struct device_node *qe;
58d7fc5963SRasmus Villemoes 
59d7fc5963SRasmus Villemoes 	/*
60d7fc5963SRasmus Villemoes 	 * Newer device trees have an "fsl,qe" compatible property for the QE
61d7fc5963SRasmus Villemoes 	 * node, but we still need to support older device trees.
62d7fc5963SRasmus Villemoes 	 */
63d7fc5963SRasmus Villemoes 	qe = of_find_compatible_node(NULL, NULL, "fsl,qe");
64d7fc5963SRasmus Villemoes 	if (qe)
65d7fc5963SRasmus Villemoes 		return qe;
66d7fc5963SRasmus Villemoes 	return of_find_node_by_type(NULL, "qe");
67d7fc5963SRasmus Villemoes }
68d7fc5963SRasmus Villemoes 
69b54ea82fSChristophe Leroy static phys_addr_t get_qe_base(void)
707aa1aa6eSZhao Qiang {
717aa1aa6eSZhao Qiang 	struct device_node *qe;
725066943aSZhao Qiang 	int ret;
735066943aSZhao Qiang 	struct resource res;
747aa1aa6eSZhao Qiang 
757aa1aa6eSZhao Qiang 	if (qebase != -1)
767aa1aa6eSZhao Qiang 		return qebase;
777aa1aa6eSZhao Qiang 
78d7fc5963SRasmus Villemoes 	qe = qe_get_device_node();
797aa1aa6eSZhao Qiang 	if (!qe)
807aa1aa6eSZhao Qiang 		return qebase;
817aa1aa6eSZhao Qiang 
825066943aSZhao Qiang 	ret = of_address_to_resource(qe, 0, &res);
835066943aSZhao Qiang 	if (!ret)
845066943aSZhao Qiang 		qebase = res.start;
857aa1aa6eSZhao Qiang 	of_node_put(qe);
867aa1aa6eSZhao Qiang 
877aa1aa6eSZhao Qiang 	return qebase;
887aa1aa6eSZhao Qiang }
897aa1aa6eSZhao Qiang 
907aa1aa6eSZhao Qiang void qe_reset(void)
917aa1aa6eSZhao Qiang {
927aa1aa6eSZhao Qiang 	if (qe_immr == NULL)
937aa1aa6eSZhao Qiang 		qe_immr = ioremap(get_qe_base(), QE_IMMAP_SIZE);
947aa1aa6eSZhao Qiang 
957aa1aa6eSZhao Qiang 	qe_snums_init();
967aa1aa6eSZhao Qiang 
977aa1aa6eSZhao Qiang 	qe_issue_cmd(QE_RESET, QE_CR_SUBBLOCK_INVALID,
987aa1aa6eSZhao Qiang 		     QE_CR_PROTOCOL_UNSPECIFIED, 0);
997aa1aa6eSZhao Qiang 
1007aa1aa6eSZhao Qiang 	/* Reclaim the MURAM memory for our use. */
1017aa1aa6eSZhao Qiang 	qe_muram_init();
1027aa1aa6eSZhao Qiang 
1037aa1aa6eSZhao Qiang 	if (qe_sdma_init())
1047aa1aa6eSZhao Qiang 		panic("sdma init failed!");
1057aa1aa6eSZhao Qiang }
1067aa1aa6eSZhao Qiang 
1077aa1aa6eSZhao Qiang int qe_issue_cmd(u32 cmd, u32 device, u8 mcn_protocol, u32 cmd_input)
1087aa1aa6eSZhao Qiang {
1097aa1aa6eSZhao Qiang 	unsigned long flags;
1107aa1aa6eSZhao Qiang 	u8 mcn_shift = 0, dev_shift = 0;
1117aa1aa6eSZhao Qiang 	u32 ret;
1127aa1aa6eSZhao Qiang 
1137aa1aa6eSZhao Qiang 	spin_lock_irqsave(&qe_lock, flags);
1147aa1aa6eSZhao Qiang 	if (cmd == QE_RESET) {
11577d7676aSRasmus Villemoes 		qe_iowrite32be((u32)(cmd | QE_CR_FLG), &qe_immr->cp.cecr);
1167aa1aa6eSZhao Qiang 	} else {
1177aa1aa6eSZhao Qiang 		if (cmd == QE_ASSIGN_PAGE) {
1187aa1aa6eSZhao Qiang 			/* Here device is the SNUM, not sub-block */
1197aa1aa6eSZhao Qiang 			dev_shift = QE_CR_SNUM_SHIFT;
1207aa1aa6eSZhao Qiang 		} else if (cmd == QE_ASSIGN_RISC) {
1217aa1aa6eSZhao Qiang 			/* Here device is the SNUM, and mcnProtocol is
1227aa1aa6eSZhao Qiang 			 * e_QeCmdRiscAssignment value */
1237aa1aa6eSZhao Qiang 			dev_shift = QE_CR_SNUM_SHIFT;
1247aa1aa6eSZhao Qiang 			mcn_shift = QE_CR_MCN_RISC_ASSIGN_SHIFT;
1257aa1aa6eSZhao Qiang 		} else {
1267aa1aa6eSZhao Qiang 			if (device == QE_CR_SUBBLOCK_USB)
1277aa1aa6eSZhao Qiang 				mcn_shift = QE_CR_MCN_USB_SHIFT;
1287aa1aa6eSZhao Qiang 			else
1297aa1aa6eSZhao Qiang 				mcn_shift = QE_CR_MCN_NORMAL_SHIFT;
1307aa1aa6eSZhao Qiang 		}
1317aa1aa6eSZhao Qiang 
13277d7676aSRasmus Villemoes 		qe_iowrite32be(cmd_input, &qe_immr->cp.cecdr);
13377d7676aSRasmus Villemoes 		qe_iowrite32be((cmd | QE_CR_FLG | ((u32)device << dev_shift) | (u32)mcn_protocol << mcn_shift),
13477d7676aSRasmus Villemoes 			       &qe_immr->cp.cecr);
1357aa1aa6eSZhao Qiang 	}
1367aa1aa6eSZhao Qiang 
1377aa1aa6eSZhao Qiang 	/* wait for the QE_CR_FLG to clear */
13877d7676aSRasmus Villemoes 	ret = spin_event_timeout((qe_ioread32be(&qe_immr->cp.cecr) & QE_CR_FLG) == 0,
1397aa1aa6eSZhao Qiang 				 100, 0);
1407aa1aa6eSZhao Qiang 	/* On timeout (e.g. failure), the expression will be false (ret == 0),
1417aa1aa6eSZhao Qiang 	   otherwise it will be true (ret == 1). */
1427aa1aa6eSZhao Qiang 	spin_unlock_irqrestore(&qe_lock, flags);
1437aa1aa6eSZhao Qiang 
1447aa1aa6eSZhao Qiang 	return ret == 1;
1457aa1aa6eSZhao Qiang }
1467aa1aa6eSZhao Qiang EXPORT_SYMBOL(qe_issue_cmd);
1477aa1aa6eSZhao Qiang 
1487aa1aa6eSZhao Qiang /* Set a baud rate generator. This needs lots of work. There are
1497aa1aa6eSZhao Qiang  * 16 BRGs, which can be connected to the QE channels or output
1507aa1aa6eSZhao Qiang  * as clocks. The BRGs are in two different block of internal
1517aa1aa6eSZhao Qiang  * memory mapped space.
1527aa1aa6eSZhao Qiang  * The BRG clock is the QE clock divided by 2.
1537aa1aa6eSZhao Qiang  * It was set up long ago during the initial boot phase and is
1547aa1aa6eSZhao Qiang  * is given to us.
1557aa1aa6eSZhao Qiang  * Baud rate clocks are zero-based in the driver code (as that maps
1567aa1aa6eSZhao Qiang  * to port numbers). Documentation uses 1-based numbering.
1577aa1aa6eSZhao Qiang  */
1587aa1aa6eSZhao Qiang static unsigned int brg_clk = 0;
1597aa1aa6eSZhao Qiang 
1602ccf80b7SValentin Longchamp #define CLK_GRAN	(1000)
1612ccf80b7SValentin Longchamp #define CLK_GRAN_LIMIT	(5)
1622ccf80b7SValentin Longchamp 
1637aa1aa6eSZhao Qiang unsigned int qe_get_brg_clk(void)
1647aa1aa6eSZhao Qiang {
1657aa1aa6eSZhao Qiang 	struct device_node *qe;
1667aa1aa6eSZhao Qiang 	int size;
1677aa1aa6eSZhao Qiang 	const u32 *prop;
1682ccf80b7SValentin Longchamp 	unsigned int mod;
1697aa1aa6eSZhao Qiang 
1707aa1aa6eSZhao Qiang 	if (brg_clk)
1717aa1aa6eSZhao Qiang 		return brg_clk;
1727aa1aa6eSZhao Qiang 
173d7fc5963SRasmus Villemoes 	qe = qe_get_device_node();
1747aa1aa6eSZhao Qiang 	if (!qe)
1757aa1aa6eSZhao Qiang 		return brg_clk;
1767aa1aa6eSZhao Qiang 
1777aa1aa6eSZhao Qiang 	prop = of_get_property(qe, "brg-frequency", &size);
1787aa1aa6eSZhao Qiang 	if (prop && size == sizeof(*prop))
1797aa1aa6eSZhao Qiang 		brg_clk = *prop;
1807aa1aa6eSZhao Qiang 
1817aa1aa6eSZhao Qiang 	of_node_put(qe);
1827aa1aa6eSZhao Qiang 
1832ccf80b7SValentin Longchamp 	/* round this if near to a multiple of CLK_GRAN */
1842ccf80b7SValentin Longchamp 	mod = brg_clk % CLK_GRAN;
1852ccf80b7SValentin Longchamp 	if (mod) {
1862ccf80b7SValentin Longchamp 		if (mod < CLK_GRAN_LIMIT)
1872ccf80b7SValentin Longchamp 			brg_clk -= mod;
1882ccf80b7SValentin Longchamp 		else if (mod > (CLK_GRAN - CLK_GRAN_LIMIT))
1892ccf80b7SValentin Longchamp 			brg_clk += CLK_GRAN - mod;
1902ccf80b7SValentin Longchamp 	}
1912ccf80b7SValentin Longchamp 
1927aa1aa6eSZhao Qiang 	return brg_clk;
1937aa1aa6eSZhao Qiang }
1947aa1aa6eSZhao Qiang EXPORT_SYMBOL(qe_get_brg_clk);
1957aa1aa6eSZhao Qiang 
196e5c5c8d2SValentin Longchamp #define PVR_VER_836x	0x8083
197e5c5c8d2SValentin Longchamp #define PVR_VER_832x	0x8084
198e5c5c8d2SValentin Longchamp 
1997aa1aa6eSZhao Qiang /* Program the BRG to the given sampling rate and multiplier
2007aa1aa6eSZhao Qiang  *
2017aa1aa6eSZhao Qiang  * @brg: the BRG, QE_BRG1 - QE_BRG16
2027aa1aa6eSZhao Qiang  * @rate: the desired sampling rate
2037aa1aa6eSZhao Qiang  * @multiplier: corresponds to the value programmed in GUMR_L[RDCR] or
2047aa1aa6eSZhao Qiang  * GUMR_L[TDCR].  E.g., if this BRG is the RX clock, and GUMR_L[RDCR]=01,
2057aa1aa6eSZhao Qiang  * then 'multiplier' should be 8.
2067aa1aa6eSZhao Qiang  */
2077aa1aa6eSZhao Qiang int qe_setbrg(enum qe_clock brg, unsigned int rate, unsigned int multiplier)
2087aa1aa6eSZhao Qiang {
2097aa1aa6eSZhao Qiang 	u32 divisor, tempval;
2107aa1aa6eSZhao Qiang 	u32 div16 = 0;
2117aa1aa6eSZhao Qiang 
2127aa1aa6eSZhao Qiang 	if ((brg < QE_BRG1) || (brg > QE_BRG16))
2137aa1aa6eSZhao Qiang 		return -EINVAL;
2147aa1aa6eSZhao Qiang 
2157aa1aa6eSZhao Qiang 	divisor = qe_get_brg_clk() / (rate * multiplier);
2167aa1aa6eSZhao Qiang 
2177aa1aa6eSZhao Qiang 	if (divisor > QE_BRGC_DIVISOR_MAX + 1) {
2187aa1aa6eSZhao Qiang 		div16 = QE_BRGC_DIV16;
2197aa1aa6eSZhao Qiang 		divisor /= 16;
2207aa1aa6eSZhao Qiang 	}
2217aa1aa6eSZhao Qiang 
2227aa1aa6eSZhao Qiang 	/* Errata QE_General4, which affects some MPC832x and MPC836x SOCs, says
2237aa1aa6eSZhao Qiang 	   that the BRG divisor must be even if you're not using divide-by-16
2247aa1aa6eSZhao Qiang 	   mode. */
225e5c5c8d2SValentin Longchamp 	if (pvr_version_is(PVR_VER_836x) || pvr_version_is(PVR_VER_832x))
2267aa1aa6eSZhao Qiang 		if (!div16 && (divisor & 1) && (divisor > 3))
2277aa1aa6eSZhao Qiang 			divisor++;
2287aa1aa6eSZhao Qiang 
2297aa1aa6eSZhao Qiang 	tempval = ((divisor - 1) << QE_BRGC_DIVISOR_SHIFT) |
2307aa1aa6eSZhao Qiang 		QE_BRGC_ENABLE | div16;
2317aa1aa6eSZhao Qiang 
23277d7676aSRasmus Villemoes 	qe_iowrite32be(tempval, &qe_immr->brg.brgc[brg - QE_BRG1]);
2337aa1aa6eSZhao Qiang 
2347aa1aa6eSZhao Qiang 	return 0;
2357aa1aa6eSZhao Qiang }
2367aa1aa6eSZhao Qiang EXPORT_SYMBOL(qe_setbrg);
2377aa1aa6eSZhao Qiang 
2387aa1aa6eSZhao Qiang /* Convert a string to a QE clock source enum
2397aa1aa6eSZhao Qiang  *
2407aa1aa6eSZhao Qiang  * This function takes a string, typically from a property in the device
2417aa1aa6eSZhao Qiang  * tree, and returns the corresponding "enum qe_clock" value.
2427aa1aa6eSZhao Qiang */
2437aa1aa6eSZhao Qiang enum qe_clock qe_clock_source(const char *source)
2447aa1aa6eSZhao Qiang {
2457aa1aa6eSZhao Qiang 	unsigned int i;
2467aa1aa6eSZhao Qiang 
2477aa1aa6eSZhao Qiang 	if (strcasecmp(source, "none") == 0)
2487aa1aa6eSZhao Qiang 		return QE_CLK_NONE;
2497aa1aa6eSZhao Qiang 
25068f047e3SZhao Qiang 	if (strcmp(source, "tsync_pin") == 0)
25168f047e3SZhao Qiang 		return QE_TSYNC_PIN;
25268f047e3SZhao Qiang 
25368f047e3SZhao Qiang 	if (strcmp(source, "rsync_pin") == 0)
25468f047e3SZhao Qiang 		return QE_RSYNC_PIN;
25568f047e3SZhao Qiang 
2567aa1aa6eSZhao Qiang 	if (strncasecmp(source, "brg", 3) == 0) {
2577aa1aa6eSZhao Qiang 		i = simple_strtoul(source + 3, NULL, 10);
2587aa1aa6eSZhao Qiang 		if ((i >= 1) && (i <= 16))
2597aa1aa6eSZhao Qiang 			return (QE_BRG1 - 1) + i;
2607aa1aa6eSZhao Qiang 		else
2617aa1aa6eSZhao Qiang 			return QE_CLK_DUMMY;
2627aa1aa6eSZhao Qiang 	}
2637aa1aa6eSZhao Qiang 
2647aa1aa6eSZhao Qiang 	if (strncasecmp(source, "clk", 3) == 0) {
2657aa1aa6eSZhao Qiang 		i = simple_strtoul(source + 3, NULL, 10);
2667aa1aa6eSZhao Qiang 		if ((i >= 1) && (i <= 24))
2677aa1aa6eSZhao Qiang 			return (QE_CLK1 - 1) + i;
2687aa1aa6eSZhao Qiang 		else
2697aa1aa6eSZhao Qiang 			return QE_CLK_DUMMY;
2707aa1aa6eSZhao Qiang 	}
2717aa1aa6eSZhao Qiang 
2727aa1aa6eSZhao Qiang 	return QE_CLK_DUMMY;
2737aa1aa6eSZhao Qiang }
2747aa1aa6eSZhao Qiang EXPORT_SYMBOL(qe_clock_source);
2757aa1aa6eSZhao Qiang 
2767aa1aa6eSZhao Qiang /* Initialize SNUMs (thread serial numbers) according to
2777aa1aa6eSZhao Qiang  * QE Module Control chapter, SNUM table
2787aa1aa6eSZhao Qiang  */
2797aa1aa6eSZhao Qiang static void qe_snums_init(void)
2807aa1aa6eSZhao Qiang {
2817aa1aa6eSZhao Qiang 	static const u8 snum_init_76[] = {
2827aa1aa6eSZhao Qiang 		0x04, 0x05, 0x0C, 0x0D, 0x14, 0x15, 0x1C, 0x1D,
2837aa1aa6eSZhao Qiang 		0x24, 0x25, 0x2C, 0x2D, 0x34, 0x35, 0x88, 0x89,
2847aa1aa6eSZhao Qiang 		0x98, 0x99, 0xA8, 0xA9, 0xB8, 0xB9, 0xC8, 0xC9,
2857aa1aa6eSZhao Qiang 		0xD8, 0xD9, 0xE8, 0xE9, 0x44, 0x45, 0x4C, 0x4D,
2867aa1aa6eSZhao Qiang 		0x54, 0x55, 0x5C, 0x5D, 0x64, 0x65, 0x6C, 0x6D,
2877aa1aa6eSZhao Qiang 		0x74, 0x75, 0x7C, 0x7D, 0x84, 0x85, 0x8C, 0x8D,
2887aa1aa6eSZhao Qiang 		0x94, 0x95, 0x9C, 0x9D, 0xA4, 0xA5, 0xAC, 0xAD,
2897aa1aa6eSZhao Qiang 		0xB4, 0xB5, 0xBC, 0xBD, 0xC4, 0xC5, 0xCC, 0xCD,
2907aa1aa6eSZhao Qiang 		0xD4, 0xD5, 0xDC, 0xDD, 0xE4, 0xE5, 0xEC, 0xED,
2917aa1aa6eSZhao Qiang 		0xF4, 0xF5, 0xFC, 0xFD,
2927aa1aa6eSZhao Qiang 	};
2937aa1aa6eSZhao Qiang 	static const u8 snum_init_46[] = {
2947aa1aa6eSZhao Qiang 		0x04, 0x05, 0x0C, 0x0D, 0x14, 0x15, 0x1C, 0x1D,
2957aa1aa6eSZhao Qiang 		0x24, 0x25, 0x2C, 0x2D, 0x34, 0x35, 0x88, 0x89,
2967aa1aa6eSZhao Qiang 		0x98, 0x99, 0xA8, 0xA9, 0xB8, 0xB9, 0xC8, 0xC9,
2977aa1aa6eSZhao Qiang 		0xD8, 0xD9, 0xE8, 0xE9, 0x08, 0x09, 0x18, 0x19,
2987aa1aa6eSZhao Qiang 		0x28, 0x29, 0x38, 0x39, 0x48, 0x49, 0x58, 0x59,
2997aa1aa6eSZhao Qiang 		0x68, 0x69, 0x78, 0x79, 0x80, 0x81,
3007aa1aa6eSZhao Qiang 	};
3015cfca891SRasmus Villemoes 	struct device_node *qe;
302f03de74dSRasmus Villemoes 	const u8 *snum_init;
3035cfca891SRasmus Villemoes 	int i;
3047aa1aa6eSZhao Qiang 
3055cfca891SRasmus Villemoes 	bitmap_zero(snum_state, QE_NUM_OF_SNUM);
30621560067SRasmus Villemoes 	qe_num_of_snum = 28; /* The default number of snum for threads is 28 */
3075cfca891SRasmus Villemoes 	qe = qe_get_device_node();
3085cfca891SRasmus Villemoes 	if (qe) {
3095cfca891SRasmus Villemoes 		i = of_property_read_variable_u8_array(qe, "fsl,qe-snums",
3105cfca891SRasmus Villemoes 						       snums, 1, QE_NUM_OF_SNUM);
3115cfca891SRasmus Villemoes 		if (i > 0) {
31221560067SRasmus Villemoes 			of_node_put(qe);
3135cfca891SRasmus Villemoes 			qe_num_of_snum = i;
3145cfca891SRasmus Villemoes 			return;
3157aa1aa6eSZhao Qiang 		}
31621560067SRasmus Villemoes 		/*
31721560067SRasmus Villemoes 		 * Fall back to legacy binding of using the value of
31821560067SRasmus Villemoes 		 * fsl,qe-num-snums to choose one of the static arrays
31921560067SRasmus Villemoes 		 * above.
32021560067SRasmus Villemoes 		 */
32121560067SRasmus Villemoes 		of_property_read_u32(qe, "fsl,qe-num-snums", &qe_num_of_snum);
32221560067SRasmus Villemoes 		of_node_put(qe);
3235cfca891SRasmus Villemoes 	}
3247aa1aa6eSZhao Qiang 
32521560067SRasmus Villemoes 	if (qe_num_of_snum == 76) {
3267aa1aa6eSZhao Qiang 		snum_init = snum_init_76;
32721560067SRasmus Villemoes 	} else if (qe_num_of_snum == 28 || qe_num_of_snum == 46) {
3287aa1aa6eSZhao Qiang 		snum_init = snum_init_46;
32921560067SRasmus Villemoes 	} else {
33021560067SRasmus Villemoes 		pr_err("QE: unsupported value of fsl,qe-num-snums: %u\n", qe_num_of_snum);
33121560067SRasmus Villemoes 		return;
33221560067SRasmus Villemoes 	}
333875f2aabSRasmus Villemoes 	memcpy(snums, snum_init, qe_num_of_snum);
3347aa1aa6eSZhao Qiang }
3357aa1aa6eSZhao Qiang 
3367aa1aa6eSZhao Qiang int qe_get_snum(void)
3377aa1aa6eSZhao Qiang {
3387aa1aa6eSZhao Qiang 	unsigned long flags;
3397aa1aa6eSZhao Qiang 	int snum = -EBUSY;
3407aa1aa6eSZhao Qiang 	int i;
3417aa1aa6eSZhao Qiang 
3427aa1aa6eSZhao Qiang 	spin_lock_irqsave(&qe_lock, flags);
343875f2aabSRasmus Villemoes 	i = find_first_zero_bit(snum_state, qe_num_of_snum);
344875f2aabSRasmus Villemoes 	if (i < qe_num_of_snum) {
345875f2aabSRasmus Villemoes 		set_bit(i, snum_state);
346875f2aabSRasmus Villemoes 		snum = snums[i];
3477aa1aa6eSZhao Qiang 	}
3487aa1aa6eSZhao Qiang 	spin_unlock_irqrestore(&qe_lock, flags);
3497aa1aa6eSZhao Qiang 
3507aa1aa6eSZhao Qiang 	return snum;
3517aa1aa6eSZhao Qiang }
3527aa1aa6eSZhao Qiang EXPORT_SYMBOL(qe_get_snum);
3537aa1aa6eSZhao Qiang 
3547aa1aa6eSZhao Qiang void qe_put_snum(u8 snum)
3557aa1aa6eSZhao Qiang {
356875f2aabSRasmus Villemoes 	const u8 *p = memchr(snums, snum, qe_num_of_snum);
3577aa1aa6eSZhao Qiang 
358875f2aabSRasmus Villemoes 	if (p)
359875f2aabSRasmus Villemoes 		clear_bit(p - snums, snum_state);
3607aa1aa6eSZhao Qiang }
3617aa1aa6eSZhao Qiang EXPORT_SYMBOL(qe_put_snum);
3627aa1aa6eSZhao Qiang 
3637aa1aa6eSZhao Qiang static int qe_sdma_init(void)
3647aa1aa6eSZhao Qiang {
3657aa1aa6eSZhao Qiang 	struct sdma __iomem *sdma = &qe_immr->sdma;
3667aa1aa6eSZhao Qiang 	static unsigned long sdma_buf_offset = (unsigned long)-ENOMEM;
3677aa1aa6eSZhao Qiang 
3687aa1aa6eSZhao Qiang 	if (!sdma)
3697aa1aa6eSZhao Qiang 		return -ENODEV;
3707aa1aa6eSZhao Qiang 
3717aa1aa6eSZhao Qiang 	/* allocate 2 internal temporary buffers (512 bytes size each) for
3727aa1aa6eSZhao Qiang 	 * the SDMA */
3737aa1aa6eSZhao Qiang 	if (IS_ERR_VALUE(sdma_buf_offset)) {
3747aa1aa6eSZhao Qiang 		sdma_buf_offset = qe_muram_alloc(512 * 2, 4096);
3757aa1aa6eSZhao Qiang 		if (IS_ERR_VALUE(sdma_buf_offset))
3767aa1aa6eSZhao Qiang 			return -ENOMEM;
3777aa1aa6eSZhao Qiang 	}
3787aa1aa6eSZhao Qiang 
37977d7676aSRasmus Villemoes 	qe_iowrite32be((u32)sdma_buf_offset & QE_SDEBCR_BA_MASK,
38077d7676aSRasmus Villemoes 		       &sdma->sdebcr);
38177d7676aSRasmus Villemoes 	qe_iowrite32be((QE_SDMR_GLB_1_MSK | (0x1 << QE_SDMR_CEN_SHIFT)),
38277d7676aSRasmus Villemoes 		       &sdma->sdmr);
3837aa1aa6eSZhao Qiang 
3847aa1aa6eSZhao Qiang 	return 0;
3857aa1aa6eSZhao Qiang }
3867aa1aa6eSZhao Qiang 
3877aa1aa6eSZhao Qiang /* The maximum number of RISCs we support */
3887aa1aa6eSZhao Qiang #define MAX_QE_RISC     4
3897aa1aa6eSZhao Qiang 
3907aa1aa6eSZhao Qiang /* Firmware information stored here for qe_get_firmware_info() */
3917aa1aa6eSZhao Qiang static struct qe_firmware_info qe_firmware_info;
3927aa1aa6eSZhao Qiang 
3937aa1aa6eSZhao Qiang /*
3947aa1aa6eSZhao Qiang  * Set to 1 if QE firmware has been uploaded, and therefore
3957aa1aa6eSZhao Qiang  * qe_firmware_info contains valid data.
3967aa1aa6eSZhao Qiang  */
3977aa1aa6eSZhao Qiang static int qe_firmware_uploaded;
3987aa1aa6eSZhao Qiang 
3997aa1aa6eSZhao Qiang /*
4007aa1aa6eSZhao Qiang  * Upload a QE microcode
4017aa1aa6eSZhao Qiang  *
4027aa1aa6eSZhao Qiang  * This function is a worker function for qe_upload_firmware().  It does
4037aa1aa6eSZhao Qiang  * the actual uploading of the microcode.
4047aa1aa6eSZhao Qiang  */
4057aa1aa6eSZhao Qiang static void qe_upload_microcode(const void *base,
4067aa1aa6eSZhao Qiang 	const struct qe_microcode *ucode)
4077aa1aa6eSZhao Qiang {
4087aa1aa6eSZhao Qiang 	const __be32 *code = base + be32_to_cpu(ucode->code_offset);
4097aa1aa6eSZhao Qiang 	unsigned int i;
4107aa1aa6eSZhao Qiang 
4117aa1aa6eSZhao Qiang 	if (ucode->major || ucode->minor || ucode->revision)
4127aa1aa6eSZhao Qiang 		printk(KERN_INFO "qe-firmware: "
4137aa1aa6eSZhao Qiang 			"uploading microcode '%s' version %u.%u.%u\n",
4147aa1aa6eSZhao Qiang 			ucode->id, ucode->major, ucode->minor, ucode->revision);
4157aa1aa6eSZhao Qiang 	else
4167aa1aa6eSZhao Qiang 		printk(KERN_INFO "qe-firmware: "
4177aa1aa6eSZhao Qiang 			"uploading microcode '%s'\n", ucode->id);
4187aa1aa6eSZhao Qiang 
4197aa1aa6eSZhao Qiang 	/* Use auto-increment */
42077d7676aSRasmus Villemoes 	qe_iowrite32be(be32_to_cpu(ucode->iram_offset) | QE_IRAM_IADD_AIE | QE_IRAM_IADD_BADDR,
42177d7676aSRasmus Villemoes 		       &qe_immr->iram.iadd);
4227aa1aa6eSZhao Qiang 
4237aa1aa6eSZhao Qiang 	for (i = 0; i < be32_to_cpu(ucode->count); i++)
42477d7676aSRasmus Villemoes 		qe_iowrite32be(be32_to_cpu(code[i]), &qe_immr->iram.idata);
4257aa1aa6eSZhao Qiang 
4267aa1aa6eSZhao Qiang 	/* Set I-RAM Ready Register */
42777d7676aSRasmus Villemoes 	qe_iowrite32be(be32_to_cpu(QE_IRAM_READY), &qe_immr->iram.iready);
4287aa1aa6eSZhao Qiang }
4297aa1aa6eSZhao Qiang 
4307aa1aa6eSZhao Qiang /*
4317aa1aa6eSZhao Qiang  * Upload a microcode to the I-RAM at a specific address.
4327aa1aa6eSZhao Qiang  *
4334d2e26a3SMauro Carvalho Chehab  * See Documentation/powerpc/qe_firmware.rst for information on QE microcode
4347aa1aa6eSZhao Qiang  * uploading.
4357aa1aa6eSZhao Qiang  *
4367aa1aa6eSZhao Qiang  * Currently, only version 1 is supported, so the 'version' field must be
4377aa1aa6eSZhao Qiang  * set to 1.
4387aa1aa6eSZhao Qiang  *
4397aa1aa6eSZhao Qiang  * The SOC model and revision are not validated, they are only displayed for
4407aa1aa6eSZhao Qiang  * informational purposes.
4417aa1aa6eSZhao Qiang  *
4427aa1aa6eSZhao Qiang  * 'calc_size' is the calculated size, in bytes, of the firmware structure and
4437aa1aa6eSZhao Qiang  * all of the microcode structures, minus the CRC.
4447aa1aa6eSZhao Qiang  *
4457aa1aa6eSZhao Qiang  * 'length' is the size that the structure says it is, including the CRC.
4467aa1aa6eSZhao Qiang  */
4477aa1aa6eSZhao Qiang int qe_upload_firmware(const struct qe_firmware *firmware)
4487aa1aa6eSZhao Qiang {
4497aa1aa6eSZhao Qiang 	unsigned int i;
4507aa1aa6eSZhao Qiang 	unsigned int j;
4517aa1aa6eSZhao Qiang 	u32 crc;
4527aa1aa6eSZhao Qiang 	size_t calc_size = sizeof(struct qe_firmware);
4537aa1aa6eSZhao Qiang 	size_t length;
4547aa1aa6eSZhao Qiang 	const struct qe_header *hdr;
4557aa1aa6eSZhao Qiang 
4567aa1aa6eSZhao Qiang 	if (!firmware) {
4577aa1aa6eSZhao Qiang 		printk(KERN_ERR "qe-firmware: invalid pointer\n");
4587aa1aa6eSZhao Qiang 		return -EINVAL;
4597aa1aa6eSZhao Qiang 	}
4607aa1aa6eSZhao Qiang 
4617aa1aa6eSZhao Qiang 	hdr = &firmware->header;
4627aa1aa6eSZhao Qiang 	length = be32_to_cpu(hdr->length);
4637aa1aa6eSZhao Qiang 
4647aa1aa6eSZhao Qiang 	/* Check the magic */
4657aa1aa6eSZhao Qiang 	if ((hdr->magic[0] != 'Q') || (hdr->magic[1] != 'E') ||
4667aa1aa6eSZhao Qiang 	    (hdr->magic[2] != 'F')) {
4677aa1aa6eSZhao Qiang 		printk(KERN_ERR "qe-firmware: not a microcode\n");
4687aa1aa6eSZhao Qiang 		return -EPERM;
4697aa1aa6eSZhao Qiang 	}
4707aa1aa6eSZhao Qiang 
4717aa1aa6eSZhao Qiang 	/* Check the version */
4727aa1aa6eSZhao Qiang 	if (hdr->version != 1) {
4737aa1aa6eSZhao Qiang 		printk(KERN_ERR "qe-firmware: unsupported version\n");
4747aa1aa6eSZhao Qiang 		return -EPERM;
4757aa1aa6eSZhao Qiang 	}
4767aa1aa6eSZhao Qiang 
4777aa1aa6eSZhao Qiang 	/* Validate some of the fields */
4787aa1aa6eSZhao Qiang 	if ((firmware->count < 1) || (firmware->count > MAX_QE_RISC)) {
4797aa1aa6eSZhao Qiang 		printk(KERN_ERR "qe-firmware: invalid data\n");
4807aa1aa6eSZhao Qiang 		return -EINVAL;
4817aa1aa6eSZhao Qiang 	}
4827aa1aa6eSZhao Qiang 
4837aa1aa6eSZhao Qiang 	/* Validate the length and check if there's a CRC */
4847aa1aa6eSZhao Qiang 	calc_size += (firmware->count - 1) * sizeof(struct qe_microcode);
4857aa1aa6eSZhao Qiang 
4867aa1aa6eSZhao Qiang 	for (i = 0; i < firmware->count; i++)
4877aa1aa6eSZhao Qiang 		/*
4887aa1aa6eSZhao Qiang 		 * For situations where the second RISC uses the same microcode
4897aa1aa6eSZhao Qiang 		 * as the first, the 'code_offset' and 'count' fields will be
4907aa1aa6eSZhao Qiang 		 * zero, so it's okay to add those.
4917aa1aa6eSZhao Qiang 		 */
4927aa1aa6eSZhao Qiang 		calc_size += sizeof(__be32) *
4937aa1aa6eSZhao Qiang 			be32_to_cpu(firmware->microcode[i].count);
4947aa1aa6eSZhao Qiang 
4957aa1aa6eSZhao Qiang 	/* Validate the length */
4967aa1aa6eSZhao Qiang 	if (length != calc_size + sizeof(__be32)) {
4977aa1aa6eSZhao Qiang 		printk(KERN_ERR "qe-firmware: invalid length\n");
4987aa1aa6eSZhao Qiang 		return -EPERM;
4997aa1aa6eSZhao Qiang 	}
5007aa1aa6eSZhao Qiang 
5017aa1aa6eSZhao Qiang 	/* Validate the CRC */
5027aa1aa6eSZhao Qiang 	crc = be32_to_cpu(*(__be32 *)((void *)firmware + calc_size));
5037aa1aa6eSZhao Qiang 	if (crc != crc32(0, firmware, calc_size)) {
5047aa1aa6eSZhao Qiang 		printk(KERN_ERR "qe-firmware: firmware CRC is invalid\n");
5057aa1aa6eSZhao Qiang 		return -EIO;
5067aa1aa6eSZhao Qiang 	}
5077aa1aa6eSZhao Qiang 
5087aa1aa6eSZhao Qiang 	/*
5097aa1aa6eSZhao Qiang 	 * If the microcode calls for it, split the I-RAM.
5107aa1aa6eSZhao Qiang 	 */
5117aa1aa6eSZhao Qiang 	if (!firmware->split)
51277d7676aSRasmus Villemoes 		qe_setbits_be16(&qe_immr->cp.cercr, QE_CP_CERCR_CIR);
5137aa1aa6eSZhao Qiang 
5147aa1aa6eSZhao Qiang 	if (firmware->soc.model)
5157aa1aa6eSZhao Qiang 		printk(KERN_INFO
5167aa1aa6eSZhao Qiang 			"qe-firmware: firmware '%s' for %u V%u.%u\n",
5177aa1aa6eSZhao Qiang 			firmware->id, be16_to_cpu(firmware->soc.model),
5187aa1aa6eSZhao Qiang 			firmware->soc.major, firmware->soc.minor);
5197aa1aa6eSZhao Qiang 	else
5207aa1aa6eSZhao Qiang 		printk(KERN_INFO "qe-firmware: firmware '%s'\n",
5217aa1aa6eSZhao Qiang 			firmware->id);
5227aa1aa6eSZhao Qiang 
5237aa1aa6eSZhao Qiang 	/*
5247aa1aa6eSZhao Qiang 	 * The QE only supports one microcode per RISC, so clear out all the
5257aa1aa6eSZhao Qiang 	 * saved microcode information and put in the new.
5267aa1aa6eSZhao Qiang 	 */
5277aa1aa6eSZhao Qiang 	memset(&qe_firmware_info, 0, sizeof(qe_firmware_info));
5287aa1aa6eSZhao Qiang 	strlcpy(qe_firmware_info.id, firmware->id, sizeof(qe_firmware_info.id));
5297aa1aa6eSZhao Qiang 	qe_firmware_info.extended_modes = firmware->extended_modes;
5307aa1aa6eSZhao Qiang 	memcpy(qe_firmware_info.vtraps, firmware->vtraps,
5317aa1aa6eSZhao Qiang 		sizeof(firmware->vtraps));
5327aa1aa6eSZhao Qiang 
5337aa1aa6eSZhao Qiang 	/* Loop through each microcode. */
5347aa1aa6eSZhao Qiang 	for (i = 0; i < firmware->count; i++) {
5357aa1aa6eSZhao Qiang 		const struct qe_microcode *ucode = &firmware->microcode[i];
5367aa1aa6eSZhao Qiang 
5377aa1aa6eSZhao Qiang 		/* Upload a microcode if it's present */
5387aa1aa6eSZhao Qiang 		if (ucode->code_offset)
5397aa1aa6eSZhao Qiang 			qe_upload_microcode(firmware, ucode);
5407aa1aa6eSZhao Qiang 
5417aa1aa6eSZhao Qiang 		/* Program the traps for this processor */
5427aa1aa6eSZhao Qiang 		for (j = 0; j < 16; j++) {
5437aa1aa6eSZhao Qiang 			u32 trap = be32_to_cpu(ucode->traps[j]);
5447aa1aa6eSZhao Qiang 
5457aa1aa6eSZhao Qiang 			if (trap)
54677d7676aSRasmus Villemoes 				qe_iowrite32be(trap,
54777d7676aSRasmus Villemoes 					       &qe_immr->rsp[i].tibcr[j]);
5487aa1aa6eSZhao Qiang 		}
5497aa1aa6eSZhao Qiang 
5507aa1aa6eSZhao Qiang 		/* Enable traps */
55177d7676aSRasmus Villemoes 		qe_iowrite32be(be32_to_cpu(ucode->eccr),
55277d7676aSRasmus Villemoes 			       &qe_immr->rsp[i].eccr);
5537aa1aa6eSZhao Qiang 	}
5547aa1aa6eSZhao Qiang 
5557aa1aa6eSZhao Qiang 	qe_firmware_uploaded = 1;
5567aa1aa6eSZhao Qiang 
5577aa1aa6eSZhao Qiang 	return 0;
5587aa1aa6eSZhao Qiang }
5597aa1aa6eSZhao Qiang EXPORT_SYMBOL(qe_upload_firmware);
5607aa1aa6eSZhao Qiang 
5617aa1aa6eSZhao Qiang /*
5627aa1aa6eSZhao Qiang  * Get info on the currently-loaded firmware
5637aa1aa6eSZhao Qiang  *
5647aa1aa6eSZhao Qiang  * This function also checks the device tree to see if the boot loader has
5657aa1aa6eSZhao Qiang  * uploaded a firmware already.
5667aa1aa6eSZhao Qiang  */
5677aa1aa6eSZhao Qiang struct qe_firmware_info *qe_get_firmware_info(void)
5687aa1aa6eSZhao Qiang {
5697aa1aa6eSZhao Qiang 	static int initialized;
5707aa1aa6eSZhao Qiang 	struct property *prop;
5717aa1aa6eSZhao Qiang 	struct device_node *qe;
5727aa1aa6eSZhao Qiang 	struct device_node *fw = NULL;
5737aa1aa6eSZhao Qiang 	const char *sprop;
5747aa1aa6eSZhao Qiang 	unsigned int i;
5757aa1aa6eSZhao Qiang 
5767aa1aa6eSZhao Qiang 	/*
5777aa1aa6eSZhao Qiang 	 * If we haven't checked yet, and a driver hasn't uploaded a firmware
5787aa1aa6eSZhao Qiang 	 * yet, then check the device tree for information.
5797aa1aa6eSZhao Qiang 	 */
5807aa1aa6eSZhao Qiang 	if (qe_firmware_uploaded)
5817aa1aa6eSZhao Qiang 		return &qe_firmware_info;
5827aa1aa6eSZhao Qiang 
5837aa1aa6eSZhao Qiang 	if (initialized)
5847aa1aa6eSZhao Qiang 		return NULL;
5857aa1aa6eSZhao Qiang 
5867aa1aa6eSZhao Qiang 	initialized = 1;
5877aa1aa6eSZhao Qiang 
588d7fc5963SRasmus Villemoes 	qe = qe_get_device_node();
5897aa1aa6eSZhao Qiang 	if (!qe)
5907aa1aa6eSZhao Qiang 		return NULL;
5917aa1aa6eSZhao Qiang 
5927aa1aa6eSZhao Qiang 	/* Find the 'firmware' child node */
593f55f6122SRob Herring 	fw = of_get_child_by_name(qe, "firmware");
5947aa1aa6eSZhao Qiang 	of_node_put(qe);
5957aa1aa6eSZhao Qiang 
5967aa1aa6eSZhao Qiang 	/* Did we find the 'firmware' node? */
5977aa1aa6eSZhao Qiang 	if (!fw)
5987aa1aa6eSZhao Qiang 		return NULL;
5997aa1aa6eSZhao Qiang 
6007aa1aa6eSZhao Qiang 	qe_firmware_uploaded = 1;
6017aa1aa6eSZhao Qiang 
6027aa1aa6eSZhao Qiang 	/* Copy the data into qe_firmware_info*/
6037aa1aa6eSZhao Qiang 	sprop = of_get_property(fw, "id", NULL);
6047aa1aa6eSZhao Qiang 	if (sprop)
6057aa1aa6eSZhao Qiang 		strlcpy(qe_firmware_info.id, sprop,
6067aa1aa6eSZhao Qiang 			sizeof(qe_firmware_info.id));
6077aa1aa6eSZhao Qiang 
6087aa1aa6eSZhao Qiang 	prop = of_find_property(fw, "extended-modes", NULL);
6097aa1aa6eSZhao Qiang 	if (prop && (prop->length == sizeof(u64))) {
6107aa1aa6eSZhao Qiang 		const u64 *iprop = prop->value;
6117aa1aa6eSZhao Qiang 
6127aa1aa6eSZhao Qiang 		qe_firmware_info.extended_modes = *iprop;
6137aa1aa6eSZhao Qiang 	}
6147aa1aa6eSZhao Qiang 
6157aa1aa6eSZhao Qiang 	prop = of_find_property(fw, "virtual-traps", NULL);
6167aa1aa6eSZhao Qiang 	if (prop && (prop->length == 32)) {
6177aa1aa6eSZhao Qiang 		const u32 *iprop = prop->value;
6187aa1aa6eSZhao Qiang 
6197aa1aa6eSZhao Qiang 		for (i = 0; i < ARRAY_SIZE(qe_firmware_info.vtraps); i++)
6207aa1aa6eSZhao Qiang 			qe_firmware_info.vtraps[i] = iprop[i];
6217aa1aa6eSZhao Qiang 	}
6227aa1aa6eSZhao Qiang 
6237aa1aa6eSZhao Qiang 	of_node_put(fw);
6247aa1aa6eSZhao Qiang 
6257aa1aa6eSZhao Qiang 	return &qe_firmware_info;
6267aa1aa6eSZhao Qiang }
6277aa1aa6eSZhao Qiang EXPORT_SYMBOL(qe_get_firmware_info);
6287aa1aa6eSZhao Qiang 
6297aa1aa6eSZhao Qiang unsigned int qe_get_num_of_risc(void)
6307aa1aa6eSZhao Qiang {
6317aa1aa6eSZhao Qiang 	struct device_node *qe;
6327aa1aa6eSZhao Qiang 	int size;
6337aa1aa6eSZhao Qiang 	unsigned int num_of_risc = 0;
6347aa1aa6eSZhao Qiang 	const u32 *prop;
6357aa1aa6eSZhao Qiang 
636d7fc5963SRasmus Villemoes 	qe = qe_get_device_node();
6377aa1aa6eSZhao Qiang 	if (!qe)
6387aa1aa6eSZhao Qiang 		return num_of_risc;
6397aa1aa6eSZhao Qiang 
6407aa1aa6eSZhao Qiang 	prop = of_get_property(qe, "fsl,qe-num-riscs", &size);
6417aa1aa6eSZhao Qiang 	if (prop && size == sizeof(*prop))
6427aa1aa6eSZhao Qiang 		num_of_risc = *prop;
6437aa1aa6eSZhao Qiang 
6447aa1aa6eSZhao Qiang 	of_node_put(qe);
6457aa1aa6eSZhao Qiang 
6467aa1aa6eSZhao Qiang 	return num_of_risc;
6477aa1aa6eSZhao Qiang }
6487aa1aa6eSZhao Qiang EXPORT_SYMBOL(qe_get_num_of_risc);
6497aa1aa6eSZhao Qiang 
6507aa1aa6eSZhao Qiang unsigned int qe_get_num_of_snums(void)
6517aa1aa6eSZhao Qiang {
65221560067SRasmus Villemoes 	return qe_num_of_snum;
6537aa1aa6eSZhao Qiang }
6547aa1aa6eSZhao Qiang EXPORT_SYMBOL(qe_get_num_of_snums);
6557aa1aa6eSZhao Qiang 
6567aa1aa6eSZhao Qiang static int __init qe_init(void)
6577aa1aa6eSZhao Qiang {
6587aa1aa6eSZhao Qiang 	struct device_node *np;
6597aa1aa6eSZhao Qiang 
6607aa1aa6eSZhao Qiang 	np = of_find_compatible_node(NULL, NULL, "fsl,qe");
6617aa1aa6eSZhao Qiang 	if (!np)
6627aa1aa6eSZhao Qiang 		return -ENODEV;
6637aa1aa6eSZhao Qiang 	qe_reset();
6647aa1aa6eSZhao Qiang 	of_node_put(np);
6657aa1aa6eSZhao Qiang 	return 0;
6667aa1aa6eSZhao Qiang }
6677aa1aa6eSZhao Qiang subsys_initcall(qe_init);
6687aa1aa6eSZhao Qiang 
6697aa1aa6eSZhao Qiang #if defined(CONFIG_SUSPEND) && defined(CONFIG_PPC_85xx)
6707aa1aa6eSZhao Qiang static int qe_resume(struct platform_device *ofdev)
6717aa1aa6eSZhao Qiang {
6727aa1aa6eSZhao Qiang 	if (!qe_alive_during_sleep())
6737aa1aa6eSZhao Qiang 		qe_reset();
6747aa1aa6eSZhao Qiang 	return 0;
6757aa1aa6eSZhao Qiang }
6767aa1aa6eSZhao Qiang 
6777aa1aa6eSZhao Qiang static int qe_probe(struct platform_device *ofdev)
6787aa1aa6eSZhao Qiang {
6797aa1aa6eSZhao Qiang 	return 0;
6807aa1aa6eSZhao Qiang }
6817aa1aa6eSZhao Qiang 
6827aa1aa6eSZhao Qiang static const struct of_device_id qe_ids[] = {
6837aa1aa6eSZhao Qiang 	{ .compatible = "fsl,qe", },
6847aa1aa6eSZhao Qiang 	{ },
6857aa1aa6eSZhao Qiang };
6867aa1aa6eSZhao Qiang 
6877aa1aa6eSZhao Qiang static struct platform_driver qe_driver = {
6887aa1aa6eSZhao Qiang 	.driver = {
6897aa1aa6eSZhao Qiang 		.name = "fsl-qe",
6907aa1aa6eSZhao Qiang 		.of_match_table = qe_ids,
6917aa1aa6eSZhao Qiang 	},
6927aa1aa6eSZhao Qiang 	.probe = qe_probe,
6937aa1aa6eSZhao Qiang 	.resume = qe_resume,
6947aa1aa6eSZhao Qiang };
6957aa1aa6eSZhao Qiang 
696c9492b4bSGeliang Tang builtin_platform_driver(qe_driver);
6977aa1aa6eSZhao Qiang #endif /* defined(CONFIG_SUSPEND) && defined(CONFIG_PPC_85xx) */
698