xref: /openbmc/linux/drivers/soc/fsl/qe/qe.c (revision c9492b4b)
17aa1aa6eSZhao Qiang /*
27aa1aa6eSZhao Qiang  * Copyright (C) 2006-2010 Freescale Semiconductor, Inc. All rights reserved.
37aa1aa6eSZhao Qiang  *
47aa1aa6eSZhao Qiang  * Authors: 	Shlomi Gridish <gridish@freescale.com>
57aa1aa6eSZhao Qiang  * 		Li Yang <leoli@freescale.com>
67aa1aa6eSZhao Qiang  * Based on cpm2_common.c from Dan Malek (dmalek@jlc.net)
77aa1aa6eSZhao Qiang  *
87aa1aa6eSZhao Qiang  * Description:
97aa1aa6eSZhao Qiang  * General Purpose functions for the global management of the
107aa1aa6eSZhao Qiang  * QUICC Engine (QE).
117aa1aa6eSZhao Qiang  *
127aa1aa6eSZhao Qiang  * This program is free software; you can redistribute  it and/or modify it
137aa1aa6eSZhao Qiang  * under  the terms of  the GNU General  Public License as published by the
147aa1aa6eSZhao Qiang  * Free Software Foundation;  either version 2 of the  License, or (at your
157aa1aa6eSZhao Qiang  * option) any later version.
167aa1aa6eSZhao Qiang  */
177aa1aa6eSZhao Qiang #include <linux/errno.h>
187aa1aa6eSZhao Qiang #include <linux/sched.h>
197aa1aa6eSZhao Qiang #include <linux/kernel.h>
207aa1aa6eSZhao Qiang #include <linux/param.h>
217aa1aa6eSZhao Qiang #include <linux/string.h>
227aa1aa6eSZhao Qiang #include <linux/spinlock.h>
237aa1aa6eSZhao Qiang #include <linux/mm.h>
247aa1aa6eSZhao Qiang #include <linux/interrupt.h>
257aa1aa6eSZhao Qiang #include <linux/module.h>
267aa1aa6eSZhao Qiang #include <linux/delay.h>
277aa1aa6eSZhao Qiang #include <linux/ioport.h>
287aa1aa6eSZhao Qiang #include <linux/crc32.h>
297aa1aa6eSZhao Qiang #include <linux/mod_devicetable.h>
307aa1aa6eSZhao Qiang #include <linux/of_platform.h>
317aa1aa6eSZhao Qiang #include <asm/irq.h>
327aa1aa6eSZhao Qiang #include <asm/page.h>
337aa1aa6eSZhao Qiang #include <asm/pgtable.h>
347aa1aa6eSZhao Qiang #include <soc/fsl/qe/immap_qe.h>
357aa1aa6eSZhao Qiang #include <soc/fsl/qe/qe.h>
367aa1aa6eSZhao Qiang #include <asm/prom.h>
377aa1aa6eSZhao Qiang #include <asm/rheap.h>
387aa1aa6eSZhao Qiang 
397aa1aa6eSZhao Qiang static void qe_snums_init(void);
407aa1aa6eSZhao Qiang static int qe_sdma_init(void);
417aa1aa6eSZhao Qiang 
427aa1aa6eSZhao Qiang static DEFINE_SPINLOCK(qe_lock);
437aa1aa6eSZhao Qiang DEFINE_SPINLOCK(cmxgcr_lock);
447aa1aa6eSZhao Qiang EXPORT_SYMBOL(cmxgcr_lock);
457aa1aa6eSZhao Qiang 
467aa1aa6eSZhao Qiang /* QE snum state */
477aa1aa6eSZhao Qiang enum qe_snum_state {
487aa1aa6eSZhao Qiang 	QE_SNUM_STATE_USED,
497aa1aa6eSZhao Qiang 	QE_SNUM_STATE_FREE
507aa1aa6eSZhao Qiang };
517aa1aa6eSZhao Qiang 
527aa1aa6eSZhao Qiang /* QE snum */
537aa1aa6eSZhao Qiang struct qe_snum {
547aa1aa6eSZhao Qiang 	u8 num;
557aa1aa6eSZhao Qiang 	enum qe_snum_state state;
567aa1aa6eSZhao Qiang };
577aa1aa6eSZhao Qiang 
587aa1aa6eSZhao Qiang /* We allocate this here because it is used almost exclusively for
597aa1aa6eSZhao Qiang  * the communication processor devices.
607aa1aa6eSZhao Qiang  */
617aa1aa6eSZhao Qiang struct qe_immap __iomem *qe_immr;
627aa1aa6eSZhao Qiang EXPORT_SYMBOL(qe_immr);
637aa1aa6eSZhao Qiang 
647aa1aa6eSZhao Qiang static struct qe_snum snums[QE_NUM_OF_SNUM];	/* Dynamically allocated SNUMs */
657aa1aa6eSZhao Qiang static unsigned int qe_num_of_snum;
667aa1aa6eSZhao Qiang 
677aa1aa6eSZhao Qiang static phys_addr_t qebase = -1;
687aa1aa6eSZhao Qiang 
697aa1aa6eSZhao Qiang 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 
787aa1aa6eSZhao Qiang 	qe = of_find_compatible_node(NULL, NULL, "fsl,qe");
797aa1aa6eSZhao Qiang 	if (!qe) {
807aa1aa6eSZhao Qiang 		qe = of_find_node_by_type(NULL, "qe");
817aa1aa6eSZhao Qiang 		if (!qe)
827aa1aa6eSZhao Qiang 			return qebase;
837aa1aa6eSZhao Qiang 	}
847aa1aa6eSZhao Qiang 
855066943aSZhao Qiang 	ret = of_address_to_resource(qe, 0, &res);
865066943aSZhao Qiang 	if (!ret)
875066943aSZhao Qiang 		qebase = res.start;
887aa1aa6eSZhao Qiang 	of_node_put(qe);
897aa1aa6eSZhao Qiang 
907aa1aa6eSZhao Qiang 	return qebase;
917aa1aa6eSZhao Qiang }
927aa1aa6eSZhao Qiang 
937aa1aa6eSZhao Qiang EXPORT_SYMBOL(get_qe_base);
947aa1aa6eSZhao Qiang 
957aa1aa6eSZhao Qiang void qe_reset(void)
967aa1aa6eSZhao Qiang {
977aa1aa6eSZhao Qiang 	if (qe_immr == NULL)
987aa1aa6eSZhao Qiang 		qe_immr = ioremap(get_qe_base(), QE_IMMAP_SIZE);
997aa1aa6eSZhao Qiang 
1007aa1aa6eSZhao Qiang 	qe_snums_init();
1017aa1aa6eSZhao Qiang 
1027aa1aa6eSZhao Qiang 	qe_issue_cmd(QE_RESET, QE_CR_SUBBLOCK_INVALID,
1037aa1aa6eSZhao Qiang 		     QE_CR_PROTOCOL_UNSPECIFIED, 0);
1047aa1aa6eSZhao Qiang 
1057aa1aa6eSZhao Qiang 	/* Reclaim the MURAM memory for our use. */
1067aa1aa6eSZhao Qiang 	qe_muram_init();
1077aa1aa6eSZhao Qiang 
1087aa1aa6eSZhao Qiang 	if (qe_sdma_init())
1097aa1aa6eSZhao Qiang 		panic("sdma init failed!");
1107aa1aa6eSZhao Qiang }
1117aa1aa6eSZhao Qiang 
1127aa1aa6eSZhao Qiang int qe_issue_cmd(u32 cmd, u32 device, u8 mcn_protocol, u32 cmd_input)
1137aa1aa6eSZhao Qiang {
1147aa1aa6eSZhao Qiang 	unsigned long flags;
1157aa1aa6eSZhao Qiang 	u8 mcn_shift = 0, dev_shift = 0;
1167aa1aa6eSZhao Qiang 	u32 ret;
1177aa1aa6eSZhao Qiang 
1187aa1aa6eSZhao Qiang 	spin_lock_irqsave(&qe_lock, flags);
1197aa1aa6eSZhao Qiang 	if (cmd == QE_RESET) {
1207aa1aa6eSZhao Qiang 		out_be32(&qe_immr->cp.cecr, (u32) (cmd | QE_CR_FLG));
1217aa1aa6eSZhao Qiang 	} else {
1227aa1aa6eSZhao Qiang 		if (cmd == QE_ASSIGN_PAGE) {
1237aa1aa6eSZhao Qiang 			/* Here device is the SNUM, not sub-block */
1247aa1aa6eSZhao Qiang 			dev_shift = QE_CR_SNUM_SHIFT;
1257aa1aa6eSZhao Qiang 		} else if (cmd == QE_ASSIGN_RISC) {
1267aa1aa6eSZhao Qiang 			/* Here device is the SNUM, and mcnProtocol is
1277aa1aa6eSZhao Qiang 			 * e_QeCmdRiscAssignment value */
1287aa1aa6eSZhao Qiang 			dev_shift = QE_CR_SNUM_SHIFT;
1297aa1aa6eSZhao Qiang 			mcn_shift = QE_CR_MCN_RISC_ASSIGN_SHIFT;
1307aa1aa6eSZhao Qiang 		} else {
1317aa1aa6eSZhao Qiang 			if (device == QE_CR_SUBBLOCK_USB)
1327aa1aa6eSZhao Qiang 				mcn_shift = QE_CR_MCN_USB_SHIFT;
1337aa1aa6eSZhao Qiang 			else
1347aa1aa6eSZhao Qiang 				mcn_shift = QE_CR_MCN_NORMAL_SHIFT;
1357aa1aa6eSZhao Qiang 		}
1367aa1aa6eSZhao Qiang 
1377aa1aa6eSZhao Qiang 		out_be32(&qe_immr->cp.cecdr, cmd_input);
1387aa1aa6eSZhao Qiang 		out_be32(&qe_immr->cp.cecr,
1397aa1aa6eSZhao Qiang 			 (cmd | QE_CR_FLG | ((u32) device << dev_shift) | (u32)
1407aa1aa6eSZhao Qiang 			  mcn_protocol << mcn_shift));
1417aa1aa6eSZhao Qiang 	}
1427aa1aa6eSZhao Qiang 
1437aa1aa6eSZhao Qiang 	/* wait for the QE_CR_FLG to clear */
1447aa1aa6eSZhao Qiang 	ret = spin_event_timeout((in_be32(&qe_immr->cp.cecr) & QE_CR_FLG) == 0,
1457aa1aa6eSZhao Qiang 			   100, 0);
1467aa1aa6eSZhao Qiang 	/* On timeout (e.g. failure), the expression will be false (ret == 0),
1477aa1aa6eSZhao Qiang 	   otherwise it will be true (ret == 1). */
1487aa1aa6eSZhao Qiang 	spin_unlock_irqrestore(&qe_lock, flags);
1497aa1aa6eSZhao Qiang 
1507aa1aa6eSZhao Qiang 	return ret == 1;
1517aa1aa6eSZhao Qiang }
1527aa1aa6eSZhao Qiang EXPORT_SYMBOL(qe_issue_cmd);
1537aa1aa6eSZhao Qiang 
1547aa1aa6eSZhao Qiang /* Set a baud rate generator. This needs lots of work. There are
1557aa1aa6eSZhao Qiang  * 16 BRGs, which can be connected to the QE channels or output
1567aa1aa6eSZhao Qiang  * as clocks. The BRGs are in two different block of internal
1577aa1aa6eSZhao Qiang  * memory mapped space.
1587aa1aa6eSZhao Qiang  * The BRG clock is the QE clock divided by 2.
1597aa1aa6eSZhao Qiang  * It was set up long ago during the initial boot phase and is
1607aa1aa6eSZhao Qiang  * is given to us.
1617aa1aa6eSZhao Qiang  * Baud rate clocks are zero-based in the driver code (as that maps
1627aa1aa6eSZhao Qiang  * to port numbers). Documentation uses 1-based numbering.
1637aa1aa6eSZhao Qiang  */
1647aa1aa6eSZhao Qiang static unsigned int brg_clk = 0;
1657aa1aa6eSZhao Qiang 
1667aa1aa6eSZhao Qiang unsigned int qe_get_brg_clk(void)
1677aa1aa6eSZhao Qiang {
1687aa1aa6eSZhao Qiang 	struct device_node *qe;
1697aa1aa6eSZhao Qiang 	int size;
1707aa1aa6eSZhao Qiang 	const u32 *prop;
1717aa1aa6eSZhao Qiang 
1727aa1aa6eSZhao Qiang 	if (brg_clk)
1737aa1aa6eSZhao Qiang 		return brg_clk;
1747aa1aa6eSZhao Qiang 
1757aa1aa6eSZhao Qiang 	qe = of_find_compatible_node(NULL, NULL, "fsl,qe");
1767aa1aa6eSZhao Qiang 	if (!qe) {
1777aa1aa6eSZhao Qiang 		qe = of_find_node_by_type(NULL, "qe");
1787aa1aa6eSZhao Qiang 		if (!qe)
1797aa1aa6eSZhao Qiang 			return brg_clk;
1807aa1aa6eSZhao Qiang 	}
1817aa1aa6eSZhao Qiang 
1827aa1aa6eSZhao Qiang 	prop = of_get_property(qe, "brg-frequency", &size);
1837aa1aa6eSZhao Qiang 	if (prop && size == sizeof(*prop))
1847aa1aa6eSZhao Qiang 		brg_clk = *prop;
1857aa1aa6eSZhao Qiang 
1867aa1aa6eSZhao Qiang 	of_node_put(qe);
1877aa1aa6eSZhao Qiang 
1887aa1aa6eSZhao Qiang 	return brg_clk;
1897aa1aa6eSZhao Qiang }
1907aa1aa6eSZhao Qiang EXPORT_SYMBOL(qe_get_brg_clk);
1917aa1aa6eSZhao Qiang 
1927aa1aa6eSZhao Qiang /* Program the BRG to the given sampling rate and multiplier
1937aa1aa6eSZhao Qiang  *
1947aa1aa6eSZhao Qiang  * @brg: the BRG, QE_BRG1 - QE_BRG16
1957aa1aa6eSZhao Qiang  * @rate: the desired sampling rate
1967aa1aa6eSZhao Qiang  * @multiplier: corresponds to the value programmed in GUMR_L[RDCR] or
1977aa1aa6eSZhao Qiang  * GUMR_L[TDCR].  E.g., if this BRG is the RX clock, and GUMR_L[RDCR]=01,
1987aa1aa6eSZhao Qiang  * then 'multiplier' should be 8.
1997aa1aa6eSZhao Qiang  */
2007aa1aa6eSZhao Qiang int qe_setbrg(enum qe_clock brg, unsigned int rate, unsigned int multiplier)
2017aa1aa6eSZhao Qiang {
2027aa1aa6eSZhao Qiang 	u32 divisor, tempval;
2037aa1aa6eSZhao Qiang 	u32 div16 = 0;
2047aa1aa6eSZhao Qiang 
2057aa1aa6eSZhao Qiang 	if ((brg < QE_BRG1) || (brg > QE_BRG16))
2067aa1aa6eSZhao Qiang 		return -EINVAL;
2077aa1aa6eSZhao Qiang 
2087aa1aa6eSZhao Qiang 	divisor = qe_get_brg_clk() / (rate * multiplier);
2097aa1aa6eSZhao Qiang 
2107aa1aa6eSZhao Qiang 	if (divisor > QE_BRGC_DIVISOR_MAX + 1) {
2117aa1aa6eSZhao Qiang 		div16 = QE_BRGC_DIV16;
2127aa1aa6eSZhao Qiang 		divisor /= 16;
2137aa1aa6eSZhao Qiang 	}
2147aa1aa6eSZhao Qiang 
2157aa1aa6eSZhao Qiang 	/* Errata QE_General4, which affects some MPC832x and MPC836x SOCs, says
2167aa1aa6eSZhao Qiang 	   that the BRG divisor must be even if you're not using divide-by-16
2177aa1aa6eSZhao Qiang 	   mode. */
2187aa1aa6eSZhao Qiang 	if (!div16 && (divisor & 1) && (divisor > 3))
2197aa1aa6eSZhao Qiang 		divisor++;
2207aa1aa6eSZhao Qiang 
2217aa1aa6eSZhao Qiang 	tempval = ((divisor - 1) << QE_BRGC_DIVISOR_SHIFT) |
2227aa1aa6eSZhao Qiang 		QE_BRGC_ENABLE | div16;
2237aa1aa6eSZhao Qiang 
2247aa1aa6eSZhao Qiang 	out_be32(&qe_immr->brg.brgc[brg - QE_BRG1], tempval);
2257aa1aa6eSZhao Qiang 
2267aa1aa6eSZhao Qiang 	return 0;
2277aa1aa6eSZhao Qiang }
2287aa1aa6eSZhao Qiang EXPORT_SYMBOL(qe_setbrg);
2297aa1aa6eSZhao Qiang 
2307aa1aa6eSZhao Qiang /* Convert a string to a QE clock source enum
2317aa1aa6eSZhao Qiang  *
2327aa1aa6eSZhao Qiang  * This function takes a string, typically from a property in the device
2337aa1aa6eSZhao Qiang  * tree, and returns the corresponding "enum qe_clock" value.
2347aa1aa6eSZhao Qiang */
2357aa1aa6eSZhao Qiang enum qe_clock qe_clock_source(const char *source)
2367aa1aa6eSZhao Qiang {
2377aa1aa6eSZhao Qiang 	unsigned int i;
2387aa1aa6eSZhao Qiang 
2397aa1aa6eSZhao Qiang 	if (strcasecmp(source, "none") == 0)
2407aa1aa6eSZhao Qiang 		return QE_CLK_NONE;
2417aa1aa6eSZhao Qiang 
24268f047e3SZhao Qiang 	if (strcmp(source, "tsync_pin") == 0)
24368f047e3SZhao Qiang 		return QE_TSYNC_PIN;
24468f047e3SZhao Qiang 
24568f047e3SZhao Qiang 	if (strcmp(source, "rsync_pin") == 0)
24668f047e3SZhao Qiang 		return QE_RSYNC_PIN;
24768f047e3SZhao Qiang 
2487aa1aa6eSZhao Qiang 	if (strncasecmp(source, "brg", 3) == 0) {
2497aa1aa6eSZhao Qiang 		i = simple_strtoul(source + 3, NULL, 10);
2507aa1aa6eSZhao Qiang 		if ((i >= 1) && (i <= 16))
2517aa1aa6eSZhao Qiang 			return (QE_BRG1 - 1) + i;
2527aa1aa6eSZhao Qiang 		else
2537aa1aa6eSZhao Qiang 			return QE_CLK_DUMMY;
2547aa1aa6eSZhao Qiang 	}
2557aa1aa6eSZhao Qiang 
2567aa1aa6eSZhao Qiang 	if (strncasecmp(source, "clk", 3) == 0) {
2577aa1aa6eSZhao Qiang 		i = simple_strtoul(source + 3, NULL, 10);
2587aa1aa6eSZhao Qiang 		if ((i >= 1) && (i <= 24))
2597aa1aa6eSZhao Qiang 			return (QE_CLK1 - 1) + i;
2607aa1aa6eSZhao Qiang 		else
2617aa1aa6eSZhao Qiang 			return QE_CLK_DUMMY;
2627aa1aa6eSZhao Qiang 	}
2637aa1aa6eSZhao Qiang 
2647aa1aa6eSZhao Qiang 	return QE_CLK_DUMMY;
2657aa1aa6eSZhao Qiang }
2667aa1aa6eSZhao Qiang EXPORT_SYMBOL(qe_clock_source);
2677aa1aa6eSZhao Qiang 
2687aa1aa6eSZhao Qiang /* Initialize SNUMs (thread serial numbers) according to
2697aa1aa6eSZhao Qiang  * QE Module Control chapter, SNUM table
2707aa1aa6eSZhao Qiang  */
2717aa1aa6eSZhao Qiang static void qe_snums_init(void)
2727aa1aa6eSZhao Qiang {
2737aa1aa6eSZhao Qiang 	int i;
2747aa1aa6eSZhao Qiang 	static const u8 snum_init_76[] = {
2757aa1aa6eSZhao Qiang 		0x04, 0x05, 0x0C, 0x0D, 0x14, 0x15, 0x1C, 0x1D,
2767aa1aa6eSZhao Qiang 		0x24, 0x25, 0x2C, 0x2D, 0x34, 0x35, 0x88, 0x89,
2777aa1aa6eSZhao Qiang 		0x98, 0x99, 0xA8, 0xA9, 0xB8, 0xB9, 0xC8, 0xC9,
2787aa1aa6eSZhao Qiang 		0xD8, 0xD9, 0xE8, 0xE9, 0x44, 0x45, 0x4C, 0x4D,
2797aa1aa6eSZhao Qiang 		0x54, 0x55, 0x5C, 0x5D, 0x64, 0x65, 0x6C, 0x6D,
2807aa1aa6eSZhao Qiang 		0x74, 0x75, 0x7C, 0x7D, 0x84, 0x85, 0x8C, 0x8D,
2817aa1aa6eSZhao Qiang 		0x94, 0x95, 0x9C, 0x9D, 0xA4, 0xA5, 0xAC, 0xAD,
2827aa1aa6eSZhao Qiang 		0xB4, 0xB5, 0xBC, 0xBD, 0xC4, 0xC5, 0xCC, 0xCD,
2837aa1aa6eSZhao Qiang 		0xD4, 0xD5, 0xDC, 0xDD, 0xE4, 0xE5, 0xEC, 0xED,
2847aa1aa6eSZhao Qiang 		0xF4, 0xF5, 0xFC, 0xFD,
2857aa1aa6eSZhao Qiang 	};
2867aa1aa6eSZhao Qiang 	static const u8 snum_init_46[] = {
2877aa1aa6eSZhao Qiang 		0x04, 0x05, 0x0C, 0x0D, 0x14, 0x15, 0x1C, 0x1D,
2887aa1aa6eSZhao Qiang 		0x24, 0x25, 0x2C, 0x2D, 0x34, 0x35, 0x88, 0x89,
2897aa1aa6eSZhao Qiang 		0x98, 0x99, 0xA8, 0xA9, 0xB8, 0xB9, 0xC8, 0xC9,
2907aa1aa6eSZhao Qiang 		0xD8, 0xD9, 0xE8, 0xE9, 0x08, 0x09, 0x18, 0x19,
2917aa1aa6eSZhao Qiang 		0x28, 0x29, 0x38, 0x39, 0x48, 0x49, 0x58, 0x59,
2927aa1aa6eSZhao Qiang 		0x68, 0x69, 0x78, 0x79, 0x80, 0x81,
2937aa1aa6eSZhao Qiang 	};
2947aa1aa6eSZhao Qiang 	static const u8 *snum_init;
2957aa1aa6eSZhao Qiang 
2967aa1aa6eSZhao Qiang 	qe_num_of_snum = qe_get_num_of_snums();
2977aa1aa6eSZhao Qiang 
2987aa1aa6eSZhao Qiang 	if (qe_num_of_snum == 76)
2997aa1aa6eSZhao Qiang 		snum_init = snum_init_76;
3007aa1aa6eSZhao Qiang 	else
3017aa1aa6eSZhao Qiang 		snum_init = snum_init_46;
3027aa1aa6eSZhao Qiang 
3037aa1aa6eSZhao Qiang 	for (i = 0; i < qe_num_of_snum; i++) {
3047aa1aa6eSZhao Qiang 		snums[i].num = snum_init[i];
3057aa1aa6eSZhao Qiang 		snums[i].state = QE_SNUM_STATE_FREE;
3067aa1aa6eSZhao Qiang 	}
3077aa1aa6eSZhao Qiang }
3087aa1aa6eSZhao Qiang 
3097aa1aa6eSZhao Qiang int qe_get_snum(void)
3107aa1aa6eSZhao Qiang {
3117aa1aa6eSZhao Qiang 	unsigned long flags;
3127aa1aa6eSZhao Qiang 	int snum = -EBUSY;
3137aa1aa6eSZhao Qiang 	int i;
3147aa1aa6eSZhao Qiang 
3157aa1aa6eSZhao Qiang 	spin_lock_irqsave(&qe_lock, flags);
3167aa1aa6eSZhao Qiang 	for (i = 0; i < qe_num_of_snum; i++) {
3177aa1aa6eSZhao Qiang 		if (snums[i].state == QE_SNUM_STATE_FREE) {
3187aa1aa6eSZhao Qiang 			snums[i].state = QE_SNUM_STATE_USED;
3197aa1aa6eSZhao Qiang 			snum = snums[i].num;
3207aa1aa6eSZhao Qiang 			break;
3217aa1aa6eSZhao Qiang 		}
3227aa1aa6eSZhao Qiang 	}
3237aa1aa6eSZhao Qiang 	spin_unlock_irqrestore(&qe_lock, flags);
3247aa1aa6eSZhao Qiang 
3257aa1aa6eSZhao Qiang 	return snum;
3267aa1aa6eSZhao Qiang }
3277aa1aa6eSZhao Qiang EXPORT_SYMBOL(qe_get_snum);
3287aa1aa6eSZhao Qiang 
3297aa1aa6eSZhao Qiang void qe_put_snum(u8 snum)
3307aa1aa6eSZhao Qiang {
3317aa1aa6eSZhao Qiang 	int i;
3327aa1aa6eSZhao Qiang 
3337aa1aa6eSZhao Qiang 	for (i = 0; i < qe_num_of_snum; i++) {
3347aa1aa6eSZhao Qiang 		if (snums[i].num == snum) {
3357aa1aa6eSZhao Qiang 			snums[i].state = QE_SNUM_STATE_FREE;
3367aa1aa6eSZhao Qiang 			break;
3377aa1aa6eSZhao Qiang 		}
3387aa1aa6eSZhao Qiang 	}
3397aa1aa6eSZhao Qiang }
3407aa1aa6eSZhao Qiang EXPORT_SYMBOL(qe_put_snum);
3417aa1aa6eSZhao Qiang 
3427aa1aa6eSZhao Qiang static int qe_sdma_init(void)
3437aa1aa6eSZhao Qiang {
3447aa1aa6eSZhao Qiang 	struct sdma __iomem *sdma = &qe_immr->sdma;
3457aa1aa6eSZhao Qiang 	static unsigned long sdma_buf_offset = (unsigned long)-ENOMEM;
3467aa1aa6eSZhao Qiang 
3477aa1aa6eSZhao Qiang 	if (!sdma)
3487aa1aa6eSZhao Qiang 		return -ENODEV;
3497aa1aa6eSZhao Qiang 
3507aa1aa6eSZhao Qiang 	/* allocate 2 internal temporary buffers (512 bytes size each) for
3517aa1aa6eSZhao Qiang 	 * the SDMA */
3527aa1aa6eSZhao Qiang 	if (IS_ERR_VALUE(sdma_buf_offset)) {
3537aa1aa6eSZhao Qiang 		sdma_buf_offset = qe_muram_alloc(512 * 2, 4096);
3547aa1aa6eSZhao Qiang 		if (IS_ERR_VALUE(sdma_buf_offset))
3557aa1aa6eSZhao Qiang 			return -ENOMEM;
3567aa1aa6eSZhao Qiang 	}
3577aa1aa6eSZhao Qiang 
3587aa1aa6eSZhao Qiang 	out_be32(&sdma->sdebcr, (u32) sdma_buf_offset & QE_SDEBCR_BA_MASK);
3597aa1aa6eSZhao Qiang  	out_be32(&sdma->sdmr, (QE_SDMR_GLB_1_MSK |
3607aa1aa6eSZhao Qiang  					(0x1 << QE_SDMR_CEN_SHIFT)));
3617aa1aa6eSZhao Qiang 
3627aa1aa6eSZhao Qiang 	return 0;
3637aa1aa6eSZhao Qiang }
3647aa1aa6eSZhao Qiang 
3657aa1aa6eSZhao Qiang /* The maximum number of RISCs we support */
3667aa1aa6eSZhao Qiang #define MAX_QE_RISC     4
3677aa1aa6eSZhao Qiang 
3687aa1aa6eSZhao Qiang /* Firmware information stored here for qe_get_firmware_info() */
3697aa1aa6eSZhao Qiang static struct qe_firmware_info qe_firmware_info;
3707aa1aa6eSZhao Qiang 
3717aa1aa6eSZhao Qiang /*
3727aa1aa6eSZhao Qiang  * Set to 1 if QE firmware has been uploaded, and therefore
3737aa1aa6eSZhao Qiang  * qe_firmware_info contains valid data.
3747aa1aa6eSZhao Qiang  */
3757aa1aa6eSZhao Qiang static int qe_firmware_uploaded;
3767aa1aa6eSZhao Qiang 
3777aa1aa6eSZhao Qiang /*
3787aa1aa6eSZhao Qiang  * Upload a QE microcode
3797aa1aa6eSZhao Qiang  *
3807aa1aa6eSZhao Qiang  * This function is a worker function for qe_upload_firmware().  It does
3817aa1aa6eSZhao Qiang  * the actual uploading of the microcode.
3827aa1aa6eSZhao Qiang  */
3837aa1aa6eSZhao Qiang static void qe_upload_microcode(const void *base,
3847aa1aa6eSZhao Qiang 	const struct qe_microcode *ucode)
3857aa1aa6eSZhao Qiang {
3867aa1aa6eSZhao Qiang 	const __be32 *code = base + be32_to_cpu(ucode->code_offset);
3877aa1aa6eSZhao Qiang 	unsigned int i;
3887aa1aa6eSZhao Qiang 
3897aa1aa6eSZhao Qiang 	if (ucode->major || ucode->minor || ucode->revision)
3907aa1aa6eSZhao Qiang 		printk(KERN_INFO "qe-firmware: "
3917aa1aa6eSZhao Qiang 			"uploading microcode '%s' version %u.%u.%u\n",
3927aa1aa6eSZhao Qiang 			ucode->id, ucode->major, ucode->minor, ucode->revision);
3937aa1aa6eSZhao Qiang 	else
3947aa1aa6eSZhao Qiang 		printk(KERN_INFO "qe-firmware: "
3957aa1aa6eSZhao Qiang 			"uploading microcode '%s'\n", ucode->id);
3967aa1aa6eSZhao Qiang 
3977aa1aa6eSZhao Qiang 	/* Use auto-increment */
3987aa1aa6eSZhao Qiang 	out_be32(&qe_immr->iram.iadd, be32_to_cpu(ucode->iram_offset) |
3997aa1aa6eSZhao Qiang 		QE_IRAM_IADD_AIE | QE_IRAM_IADD_BADDR);
4007aa1aa6eSZhao Qiang 
4017aa1aa6eSZhao Qiang 	for (i = 0; i < be32_to_cpu(ucode->count); i++)
4027aa1aa6eSZhao Qiang 		out_be32(&qe_immr->iram.idata, be32_to_cpu(code[i]));
4037aa1aa6eSZhao Qiang 
4047aa1aa6eSZhao Qiang 	/* Set I-RAM Ready Register */
4057aa1aa6eSZhao Qiang 	out_be32(&qe_immr->iram.iready, be32_to_cpu(QE_IRAM_READY));
4067aa1aa6eSZhao Qiang }
4077aa1aa6eSZhao Qiang 
4087aa1aa6eSZhao Qiang /*
4097aa1aa6eSZhao Qiang  * Upload a microcode to the I-RAM at a specific address.
4107aa1aa6eSZhao Qiang  *
4117aa1aa6eSZhao Qiang  * See Documentation/powerpc/qe_firmware.txt for information on QE microcode
4127aa1aa6eSZhao Qiang  * uploading.
4137aa1aa6eSZhao Qiang  *
4147aa1aa6eSZhao Qiang  * Currently, only version 1 is supported, so the 'version' field must be
4157aa1aa6eSZhao Qiang  * set to 1.
4167aa1aa6eSZhao Qiang  *
4177aa1aa6eSZhao Qiang  * The SOC model and revision are not validated, they are only displayed for
4187aa1aa6eSZhao Qiang  * informational purposes.
4197aa1aa6eSZhao Qiang  *
4207aa1aa6eSZhao Qiang  * 'calc_size' is the calculated size, in bytes, of the firmware structure and
4217aa1aa6eSZhao Qiang  * all of the microcode structures, minus the CRC.
4227aa1aa6eSZhao Qiang  *
4237aa1aa6eSZhao Qiang  * 'length' is the size that the structure says it is, including the CRC.
4247aa1aa6eSZhao Qiang  */
4257aa1aa6eSZhao Qiang int qe_upload_firmware(const struct qe_firmware *firmware)
4267aa1aa6eSZhao Qiang {
4277aa1aa6eSZhao Qiang 	unsigned int i;
4287aa1aa6eSZhao Qiang 	unsigned int j;
4297aa1aa6eSZhao Qiang 	u32 crc;
4307aa1aa6eSZhao Qiang 	size_t calc_size = sizeof(struct qe_firmware);
4317aa1aa6eSZhao Qiang 	size_t length;
4327aa1aa6eSZhao Qiang 	const struct qe_header *hdr;
4337aa1aa6eSZhao Qiang 
4347aa1aa6eSZhao Qiang 	if (!firmware) {
4357aa1aa6eSZhao Qiang 		printk(KERN_ERR "qe-firmware: invalid pointer\n");
4367aa1aa6eSZhao Qiang 		return -EINVAL;
4377aa1aa6eSZhao Qiang 	}
4387aa1aa6eSZhao Qiang 
4397aa1aa6eSZhao Qiang 	hdr = &firmware->header;
4407aa1aa6eSZhao Qiang 	length = be32_to_cpu(hdr->length);
4417aa1aa6eSZhao Qiang 
4427aa1aa6eSZhao Qiang 	/* Check the magic */
4437aa1aa6eSZhao Qiang 	if ((hdr->magic[0] != 'Q') || (hdr->magic[1] != 'E') ||
4447aa1aa6eSZhao Qiang 	    (hdr->magic[2] != 'F')) {
4457aa1aa6eSZhao Qiang 		printk(KERN_ERR "qe-firmware: not a microcode\n");
4467aa1aa6eSZhao Qiang 		return -EPERM;
4477aa1aa6eSZhao Qiang 	}
4487aa1aa6eSZhao Qiang 
4497aa1aa6eSZhao Qiang 	/* Check the version */
4507aa1aa6eSZhao Qiang 	if (hdr->version != 1) {
4517aa1aa6eSZhao Qiang 		printk(KERN_ERR "qe-firmware: unsupported version\n");
4527aa1aa6eSZhao Qiang 		return -EPERM;
4537aa1aa6eSZhao Qiang 	}
4547aa1aa6eSZhao Qiang 
4557aa1aa6eSZhao Qiang 	/* Validate some of the fields */
4567aa1aa6eSZhao Qiang 	if ((firmware->count < 1) || (firmware->count > MAX_QE_RISC)) {
4577aa1aa6eSZhao Qiang 		printk(KERN_ERR "qe-firmware: invalid data\n");
4587aa1aa6eSZhao Qiang 		return -EINVAL;
4597aa1aa6eSZhao Qiang 	}
4607aa1aa6eSZhao Qiang 
4617aa1aa6eSZhao Qiang 	/* Validate the length and check if there's a CRC */
4627aa1aa6eSZhao Qiang 	calc_size += (firmware->count - 1) * sizeof(struct qe_microcode);
4637aa1aa6eSZhao Qiang 
4647aa1aa6eSZhao Qiang 	for (i = 0; i < firmware->count; i++)
4657aa1aa6eSZhao Qiang 		/*
4667aa1aa6eSZhao Qiang 		 * For situations where the second RISC uses the same microcode
4677aa1aa6eSZhao Qiang 		 * as the first, the 'code_offset' and 'count' fields will be
4687aa1aa6eSZhao Qiang 		 * zero, so it's okay to add those.
4697aa1aa6eSZhao Qiang 		 */
4707aa1aa6eSZhao Qiang 		calc_size += sizeof(__be32) *
4717aa1aa6eSZhao Qiang 			be32_to_cpu(firmware->microcode[i].count);
4727aa1aa6eSZhao Qiang 
4737aa1aa6eSZhao Qiang 	/* Validate the length */
4747aa1aa6eSZhao Qiang 	if (length != calc_size + sizeof(__be32)) {
4757aa1aa6eSZhao Qiang 		printk(KERN_ERR "qe-firmware: invalid length\n");
4767aa1aa6eSZhao Qiang 		return -EPERM;
4777aa1aa6eSZhao Qiang 	}
4787aa1aa6eSZhao Qiang 
4797aa1aa6eSZhao Qiang 	/* Validate the CRC */
4807aa1aa6eSZhao Qiang 	crc = be32_to_cpu(*(__be32 *)((void *)firmware + calc_size));
4817aa1aa6eSZhao Qiang 	if (crc != crc32(0, firmware, calc_size)) {
4827aa1aa6eSZhao Qiang 		printk(KERN_ERR "qe-firmware: firmware CRC is invalid\n");
4837aa1aa6eSZhao Qiang 		return -EIO;
4847aa1aa6eSZhao Qiang 	}
4857aa1aa6eSZhao Qiang 
4867aa1aa6eSZhao Qiang 	/*
4877aa1aa6eSZhao Qiang 	 * If the microcode calls for it, split the I-RAM.
4887aa1aa6eSZhao Qiang 	 */
4897aa1aa6eSZhao Qiang 	if (!firmware->split)
4907aa1aa6eSZhao Qiang 		setbits16(&qe_immr->cp.cercr, QE_CP_CERCR_CIR);
4917aa1aa6eSZhao Qiang 
4927aa1aa6eSZhao Qiang 	if (firmware->soc.model)
4937aa1aa6eSZhao Qiang 		printk(KERN_INFO
4947aa1aa6eSZhao Qiang 			"qe-firmware: firmware '%s' for %u V%u.%u\n",
4957aa1aa6eSZhao Qiang 			firmware->id, be16_to_cpu(firmware->soc.model),
4967aa1aa6eSZhao Qiang 			firmware->soc.major, firmware->soc.minor);
4977aa1aa6eSZhao Qiang 	else
4987aa1aa6eSZhao Qiang 		printk(KERN_INFO "qe-firmware: firmware '%s'\n",
4997aa1aa6eSZhao Qiang 			firmware->id);
5007aa1aa6eSZhao Qiang 
5017aa1aa6eSZhao Qiang 	/*
5027aa1aa6eSZhao Qiang 	 * The QE only supports one microcode per RISC, so clear out all the
5037aa1aa6eSZhao Qiang 	 * saved microcode information and put in the new.
5047aa1aa6eSZhao Qiang 	 */
5057aa1aa6eSZhao Qiang 	memset(&qe_firmware_info, 0, sizeof(qe_firmware_info));
5067aa1aa6eSZhao Qiang 	strlcpy(qe_firmware_info.id, firmware->id, sizeof(qe_firmware_info.id));
5077aa1aa6eSZhao Qiang 	qe_firmware_info.extended_modes = firmware->extended_modes;
5087aa1aa6eSZhao Qiang 	memcpy(qe_firmware_info.vtraps, firmware->vtraps,
5097aa1aa6eSZhao Qiang 		sizeof(firmware->vtraps));
5107aa1aa6eSZhao Qiang 
5117aa1aa6eSZhao Qiang 	/* Loop through each microcode. */
5127aa1aa6eSZhao Qiang 	for (i = 0; i < firmware->count; i++) {
5137aa1aa6eSZhao Qiang 		const struct qe_microcode *ucode = &firmware->microcode[i];
5147aa1aa6eSZhao Qiang 
5157aa1aa6eSZhao Qiang 		/* Upload a microcode if it's present */
5167aa1aa6eSZhao Qiang 		if (ucode->code_offset)
5177aa1aa6eSZhao Qiang 			qe_upload_microcode(firmware, ucode);
5187aa1aa6eSZhao Qiang 
5197aa1aa6eSZhao Qiang 		/* Program the traps for this processor */
5207aa1aa6eSZhao Qiang 		for (j = 0; j < 16; j++) {
5217aa1aa6eSZhao Qiang 			u32 trap = be32_to_cpu(ucode->traps[j]);
5227aa1aa6eSZhao Qiang 
5237aa1aa6eSZhao Qiang 			if (trap)
5247aa1aa6eSZhao Qiang 				out_be32(&qe_immr->rsp[i].tibcr[j], trap);
5257aa1aa6eSZhao Qiang 		}
5267aa1aa6eSZhao Qiang 
5277aa1aa6eSZhao Qiang 		/* Enable traps */
5287aa1aa6eSZhao Qiang 		out_be32(&qe_immr->rsp[i].eccr, be32_to_cpu(ucode->eccr));
5297aa1aa6eSZhao Qiang 	}
5307aa1aa6eSZhao Qiang 
5317aa1aa6eSZhao Qiang 	qe_firmware_uploaded = 1;
5327aa1aa6eSZhao Qiang 
5337aa1aa6eSZhao Qiang 	return 0;
5347aa1aa6eSZhao Qiang }
5357aa1aa6eSZhao Qiang EXPORT_SYMBOL(qe_upload_firmware);
5367aa1aa6eSZhao Qiang 
5377aa1aa6eSZhao Qiang /*
5387aa1aa6eSZhao Qiang  * Get info on the currently-loaded firmware
5397aa1aa6eSZhao Qiang  *
5407aa1aa6eSZhao Qiang  * This function also checks the device tree to see if the boot loader has
5417aa1aa6eSZhao Qiang  * uploaded a firmware already.
5427aa1aa6eSZhao Qiang  */
5437aa1aa6eSZhao Qiang struct qe_firmware_info *qe_get_firmware_info(void)
5447aa1aa6eSZhao Qiang {
5457aa1aa6eSZhao Qiang 	static int initialized;
5467aa1aa6eSZhao Qiang 	struct property *prop;
5477aa1aa6eSZhao Qiang 	struct device_node *qe;
5487aa1aa6eSZhao Qiang 	struct device_node *fw = NULL;
5497aa1aa6eSZhao Qiang 	const char *sprop;
5507aa1aa6eSZhao Qiang 	unsigned int i;
5517aa1aa6eSZhao Qiang 
5527aa1aa6eSZhao Qiang 	/*
5537aa1aa6eSZhao Qiang 	 * If we haven't checked yet, and a driver hasn't uploaded a firmware
5547aa1aa6eSZhao Qiang 	 * yet, then check the device tree for information.
5557aa1aa6eSZhao Qiang 	 */
5567aa1aa6eSZhao Qiang 	if (qe_firmware_uploaded)
5577aa1aa6eSZhao Qiang 		return &qe_firmware_info;
5587aa1aa6eSZhao Qiang 
5597aa1aa6eSZhao Qiang 	if (initialized)
5607aa1aa6eSZhao Qiang 		return NULL;
5617aa1aa6eSZhao Qiang 
5627aa1aa6eSZhao Qiang 	initialized = 1;
5637aa1aa6eSZhao Qiang 
5647aa1aa6eSZhao Qiang 	/*
5657aa1aa6eSZhao Qiang 	 * Newer device trees have an "fsl,qe" compatible property for the QE
5667aa1aa6eSZhao Qiang 	 * node, but we still need to support older device trees.
5677aa1aa6eSZhao Qiang 	*/
5687aa1aa6eSZhao Qiang 	qe = of_find_compatible_node(NULL, NULL, "fsl,qe");
5697aa1aa6eSZhao Qiang 	if (!qe) {
5707aa1aa6eSZhao Qiang 		qe = of_find_node_by_type(NULL, "qe");
5717aa1aa6eSZhao Qiang 		if (!qe)
5727aa1aa6eSZhao Qiang 			return NULL;
5737aa1aa6eSZhao Qiang 	}
5747aa1aa6eSZhao Qiang 
5757aa1aa6eSZhao Qiang 	/* Find the 'firmware' child node */
5767aa1aa6eSZhao Qiang 	for_each_child_of_node(qe, fw) {
5777aa1aa6eSZhao Qiang 		if (strcmp(fw->name, "firmware") == 0)
5787aa1aa6eSZhao Qiang 			break;
5797aa1aa6eSZhao Qiang 	}
5807aa1aa6eSZhao Qiang 
5817aa1aa6eSZhao Qiang 	of_node_put(qe);
5827aa1aa6eSZhao Qiang 
5837aa1aa6eSZhao Qiang 	/* Did we find the 'firmware' node? */
5847aa1aa6eSZhao Qiang 	if (!fw)
5857aa1aa6eSZhao Qiang 		return NULL;
5867aa1aa6eSZhao Qiang 
5877aa1aa6eSZhao Qiang 	qe_firmware_uploaded = 1;
5887aa1aa6eSZhao Qiang 
5897aa1aa6eSZhao Qiang 	/* Copy the data into qe_firmware_info*/
5907aa1aa6eSZhao Qiang 	sprop = of_get_property(fw, "id", NULL);
5917aa1aa6eSZhao Qiang 	if (sprop)
5927aa1aa6eSZhao Qiang 		strlcpy(qe_firmware_info.id, sprop,
5937aa1aa6eSZhao Qiang 			sizeof(qe_firmware_info.id));
5947aa1aa6eSZhao Qiang 
5957aa1aa6eSZhao Qiang 	prop = of_find_property(fw, "extended-modes", NULL);
5967aa1aa6eSZhao Qiang 	if (prop && (prop->length == sizeof(u64))) {
5977aa1aa6eSZhao Qiang 		const u64 *iprop = prop->value;
5987aa1aa6eSZhao Qiang 
5997aa1aa6eSZhao Qiang 		qe_firmware_info.extended_modes = *iprop;
6007aa1aa6eSZhao Qiang 	}
6017aa1aa6eSZhao Qiang 
6027aa1aa6eSZhao Qiang 	prop = of_find_property(fw, "virtual-traps", NULL);
6037aa1aa6eSZhao Qiang 	if (prop && (prop->length == 32)) {
6047aa1aa6eSZhao Qiang 		const u32 *iprop = prop->value;
6057aa1aa6eSZhao Qiang 
6067aa1aa6eSZhao Qiang 		for (i = 0; i < ARRAY_SIZE(qe_firmware_info.vtraps); i++)
6077aa1aa6eSZhao Qiang 			qe_firmware_info.vtraps[i] = iprop[i];
6087aa1aa6eSZhao Qiang 	}
6097aa1aa6eSZhao Qiang 
6107aa1aa6eSZhao Qiang 	of_node_put(fw);
6117aa1aa6eSZhao Qiang 
6127aa1aa6eSZhao Qiang 	return &qe_firmware_info;
6137aa1aa6eSZhao Qiang }
6147aa1aa6eSZhao Qiang EXPORT_SYMBOL(qe_get_firmware_info);
6157aa1aa6eSZhao Qiang 
6167aa1aa6eSZhao Qiang unsigned int qe_get_num_of_risc(void)
6177aa1aa6eSZhao Qiang {
6187aa1aa6eSZhao Qiang 	struct device_node *qe;
6197aa1aa6eSZhao Qiang 	int size;
6207aa1aa6eSZhao Qiang 	unsigned int num_of_risc = 0;
6217aa1aa6eSZhao Qiang 	const u32 *prop;
6227aa1aa6eSZhao Qiang 
6237aa1aa6eSZhao Qiang 	qe = of_find_compatible_node(NULL, NULL, "fsl,qe");
6247aa1aa6eSZhao Qiang 	if (!qe) {
6257aa1aa6eSZhao Qiang 		/* Older devices trees did not have an "fsl,qe"
6267aa1aa6eSZhao Qiang 		 * compatible property, so we need to look for
6277aa1aa6eSZhao Qiang 		 * the QE node by name.
6287aa1aa6eSZhao Qiang 		 */
6297aa1aa6eSZhao Qiang 		qe = of_find_node_by_type(NULL, "qe");
6307aa1aa6eSZhao Qiang 		if (!qe)
6317aa1aa6eSZhao Qiang 			return num_of_risc;
6327aa1aa6eSZhao Qiang 	}
6337aa1aa6eSZhao Qiang 
6347aa1aa6eSZhao Qiang 	prop = of_get_property(qe, "fsl,qe-num-riscs", &size);
6357aa1aa6eSZhao Qiang 	if (prop && size == sizeof(*prop))
6367aa1aa6eSZhao Qiang 		num_of_risc = *prop;
6377aa1aa6eSZhao Qiang 
6387aa1aa6eSZhao Qiang 	of_node_put(qe);
6397aa1aa6eSZhao Qiang 
6407aa1aa6eSZhao Qiang 	return num_of_risc;
6417aa1aa6eSZhao Qiang }
6427aa1aa6eSZhao Qiang EXPORT_SYMBOL(qe_get_num_of_risc);
6437aa1aa6eSZhao Qiang 
6447aa1aa6eSZhao Qiang unsigned int qe_get_num_of_snums(void)
6457aa1aa6eSZhao Qiang {
6467aa1aa6eSZhao Qiang 	struct device_node *qe;
6477aa1aa6eSZhao Qiang 	int size;
6487aa1aa6eSZhao Qiang 	unsigned int num_of_snums;
6497aa1aa6eSZhao Qiang 	const u32 *prop;
6507aa1aa6eSZhao Qiang 
6517aa1aa6eSZhao Qiang 	num_of_snums = 28; /* The default number of snum for threads is 28 */
6527aa1aa6eSZhao Qiang 	qe = of_find_compatible_node(NULL, NULL, "fsl,qe");
6537aa1aa6eSZhao Qiang 	if (!qe) {
6547aa1aa6eSZhao Qiang 		/* Older devices trees did not have an "fsl,qe"
6557aa1aa6eSZhao Qiang 		 * compatible property, so we need to look for
6567aa1aa6eSZhao Qiang 		 * the QE node by name.
6577aa1aa6eSZhao Qiang 		 */
6587aa1aa6eSZhao Qiang 		qe = of_find_node_by_type(NULL, "qe");
6597aa1aa6eSZhao Qiang 		if (!qe)
6607aa1aa6eSZhao Qiang 			return num_of_snums;
6617aa1aa6eSZhao Qiang 	}
6627aa1aa6eSZhao Qiang 
6637aa1aa6eSZhao Qiang 	prop = of_get_property(qe, "fsl,qe-num-snums", &size);
6647aa1aa6eSZhao Qiang 	if (prop && size == sizeof(*prop)) {
6657aa1aa6eSZhao Qiang 		num_of_snums = *prop;
6667aa1aa6eSZhao Qiang 		if ((num_of_snums < 28) || (num_of_snums > QE_NUM_OF_SNUM)) {
6677aa1aa6eSZhao Qiang 			/* No QE ever has fewer than 28 SNUMs */
6687aa1aa6eSZhao Qiang 			pr_err("QE: number of snum is invalid\n");
6697aa1aa6eSZhao Qiang 			of_node_put(qe);
6707aa1aa6eSZhao Qiang 			return -EINVAL;
6717aa1aa6eSZhao Qiang 		}
6727aa1aa6eSZhao Qiang 	}
6737aa1aa6eSZhao Qiang 
6747aa1aa6eSZhao Qiang 	of_node_put(qe);
6757aa1aa6eSZhao Qiang 
6767aa1aa6eSZhao Qiang 	return num_of_snums;
6777aa1aa6eSZhao Qiang }
6787aa1aa6eSZhao Qiang EXPORT_SYMBOL(qe_get_num_of_snums);
6797aa1aa6eSZhao Qiang 
6807aa1aa6eSZhao Qiang static int __init qe_init(void)
6817aa1aa6eSZhao Qiang {
6827aa1aa6eSZhao Qiang 	struct device_node *np;
6837aa1aa6eSZhao Qiang 
6847aa1aa6eSZhao Qiang 	np = of_find_compatible_node(NULL, NULL, "fsl,qe");
6857aa1aa6eSZhao Qiang 	if (!np)
6867aa1aa6eSZhao Qiang 		return -ENODEV;
6877aa1aa6eSZhao Qiang 	qe_reset();
6887aa1aa6eSZhao Qiang 	of_node_put(np);
6897aa1aa6eSZhao Qiang 	return 0;
6907aa1aa6eSZhao Qiang }
6917aa1aa6eSZhao Qiang subsys_initcall(qe_init);
6927aa1aa6eSZhao Qiang 
6937aa1aa6eSZhao Qiang #if defined(CONFIG_SUSPEND) && defined(CONFIG_PPC_85xx)
6947aa1aa6eSZhao Qiang static int qe_resume(struct platform_device *ofdev)
6957aa1aa6eSZhao Qiang {
6967aa1aa6eSZhao Qiang 	if (!qe_alive_during_sleep())
6977aa1aa6eSZhao Qiang 		qe_reset();
6987aa1aa6eSZhao Qiang 	return 0;
6997aa1aa6eSZhao Qiang }
7007aa1aa6eSZhao Qiang 
7017aa1aa6eSZhao Qiang static int qe_probe(struct platform_device *ofdev)
7027aa1aa6eSZhao Qiang {
7037aa1aa6eSZhao Qiang 	return 0;
7047aa1aa6eSZhao Qiang }
7057aa1aa6eSZhao Qiang 
7067aa1aa6eSZhao Qiang static const struct of_device_id qe_ids[] = {
7077aa1aa6eSZhao Qiang 	{ .compatible = "fsl,qe", },
7087aa1aa6eSZhao Qiang 	{ },
7097aa1aa6eSZhao Qiang };
7107aa1aa6eSZhao Qiang 
7117aa1aa6eSZhao Qiang static struct platform_driver qe_driver = {
7127aa1aa6eSZhao Qiang 	.driver = {
7137aa1aa6eSZhao Qiang 		.name = "fsl-qe",
7147aa1aa6eSZhao Qiang 		.of_match_table = qe_ids,
7157aa1aa6eSZhao Qiang 	},
7167aa1aa6eSZhao Qiang 	.probe = qe_probe,
7177aa1aa6eSZhao Qiang 	.resume = qe_resume,
7187aa1aa6eSZhao Qiang };
7197aa1aa6eSZhao Qiang 
720c9492b4bSGeliang Tang builtin_platform_driver(qe_driver);
7217aa1aa6eSZhao Qiang #endif /* defined(CONFIG_SUSPEND) && defined(CONFIG_PPC_85xx) */
722