xref: /freebsd/sys/arm/xilinx/zy7_qspi.c (revision 64d1a02e4e1a5f561d03b2bbc9c20dad255e79e8)
12de9b4d3SEmmanuel Vadot /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
32de9b4d3SEmmanuel Vadot  *
42de9b4d3SEmmanuel Vadot  * Copyright (c) 2018 Thomas Skibo <thomasskibo@yahoo.com>
52de9b4d3SEmmanuel Vadot  * All rights reserved.
62de9b4d3SEmmanuel Vadot  *
72de9b4d3SEmmanuel Vadot  * Redistribution and use in source and binary forms, with or without
82de9b4d3SEmmanuel Vadot  * modification, are permitted provided that the following conditions
92de9b4d3SEmmanuel Vadot  * are met:
102de9b4d3SEmmanuel Vadot  * 1. Redistributions of source code must retain the above copyright
112de9b4d3SEmmanuel Vadot  *    notice, this list of conditions and the following disclaimer.
122de9b4d3SEmmanuel Vadot  * 2. Redistributions in binary form must reproduce the above copyright
132de9b4d3SEmmanuel Vadot  *    notice, this list of conditions and the following disclaimer in the
142de9b4d3SEmmanuel Vadot  *    documentation and/or other materials provided with the distribution.
152de9b4d3SEmmanuel Vadot  *
162de9b4d3SEmmanuel Vadot  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
172de9b4d3SEmmanuel Vadot  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
182de9b4d3SEmmanuel Vadot  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
192de9b4d3SEmmanuel Vadot  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
202de9b4d3SEmmanuel Vadot  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
212de9b4d3SEmmanuel Vadot  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
222de9b4d3SEmmanuel Vadot  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
232de9b4d3SEmmanuel Vadot  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
242de9b4d3SEmmanuel Vadot  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
252de9b4d3SEmmanuel Vadot  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
262de9b4d3SEmmanuel Vadot  * SUCH DAMAGE.
272de9b4d3SEmmanuel Vadot  */
282de9b4d3SEmmanuel Vadot 
292de9b4d3SEmmanuel Vadot #include <sys/cdefs.h>
302de9b4d3SEmmanuel Vadot /*
312de9b4d3SEmmanuel Vadot  * This is a driver for the Quad-SPI Flash Controller in the Xilinx
322de9b4d3SEmmanuel Vadot  * Zynq-7000 SoC.
332de9b4d3SEmmanuel Vadot  */
342de9b4d3SEmmanuel Vadot 
352de9b4d3SEmmanuel Vadot #include <sys/param.h>
362de9b4d3SEmmanuel Vadot #include <sys/systm.h>
372de9b4d3SEmmanuel Vadot #include <sys/conf.h>
382de9b4d3SEmmanuel Vadot #include <sys/kernel.h>
392de9b4d3SEmmanuel Vadot #include <sys/module.h>
402de9b4d3SEmmanuel Vadot #include <sys/sysctl.h>
412de9b4d3SEmmanuel Vadot #include <sys/lock.h>
422de9b4d3SEmmanuel Vadot #include <sys/mutex.h>
432de9b4d3SEmmanuel Vadot #include <sys/resource.h>
442de9b4d3SEmmanuel Vadot #include <sys/rman.h>
452de9b4d3SEmmanuel Vadot #include <sys/uio.h>
462de9b4d3SEmmanuel Vadot 
472de9b4d3SEmmanuel Vadot #include <machine/bus.h>
482de9b4d3SEmmanuel Vadot #include <machine/resource.h>
492de9b4d3SEmmanuel Vadot #include <machine/stdarg.h>
502de9b4d3SEmmanuel Vadot 
512de9b4d3SEmmanuel Vadot #include <dev/fdt/fdt_common.h>
522de9b4d3SEmmanuel Vadot #include <dev/ofw/ofw_bus.h>
532de9b4d3SEmmanuel Vadot #include <dev/ofw/ofw_bus_subr.h>
542de9b4d3SEmmanuel Vadot 
552de9b4d3SEmmanuel Vadot #include <dev/spibus/spi.h>
562de9b4d3SEmmanuel Vadot #include <dev/spibus/spibusvar.h>
572de9b4d3SEmmanuel Vadot 
582de9b4d3SEmmanuel Vadot #include <dev/flash/mx25lreg.h>
592de9b4d3SEmmanuel Vadot 
602de9b4d3SEmmanuel Vadot #include "spibus_if.h"
612de9b4d3SEmmanuel Vadot 
622de9b4d3SEmmanuel Vadot static struct ofw_compat_data compat_data[] = {
632de9b4d3SEmmanuel Vadot 	{"xlnx,zy7_qspi",		1},
642de9b4d3SEmmanuel Vadot 	{"xlnx,zynq-qspi-1.0",		1},
652de9b4d3SEmmanuel Vadot 	{NULL,				0}
662de9b4d3SEmmanuel Vadot };
672de9b4d3SEmmanuel Vadot 
682de9b4d3SEmmanuel Vadot struct zy7_qspi_softc {
692de9b4d3SEmmanuel Vadot 	device_t		dev;
702de9b4d3SEmmanuel Vadot 	device_t		child;
712de9b4d3SEmmanuel Vadot 	struct mtx		sc_mtx;
722de9b4d3SEmmanuel Vadot 	struct resource		*mem_res;
732de9b4d3SEmmanuel Vadot 	struct resource		*irq_res;
742de9b4d3SEmmanuel Vadot 	void			*intrhandle;
752de9b4d3SEmmanuel Vadot 
762de9b4d3SEmmanuel Vadot 	uint32_t		cfg_reg_shadow;
772de9b4d3SEmmanuel Vadot 	uint32_t		lqspi_cfg_shadow;
782de9b4d3SEmmanuel Vadot 	uint32_t		spi_clock;
792de9b4d3SEmmanuel Vadot 	uint32_t		ref_clock;
802de9b4d3SEmmanuel Vadot 	unsigned int		spi_clk_real_freq;
812de9b4d3SEmmanuel Vadot 	unsigned int		rx_overflows;
822de9b4d3SEmmanuel Vadot 	unsigned int		tx_underflows;
832de9b4d3SEmmanuel Vadot 	unsigned int		interrupts;
842de9b4d3SEmmanuel Vadot 	unsigned int		stray_ints;
852de9b4d3SEmmanuel Vadot 	struct spi_command	*cmd;
862de9b4d3SEmmanuel Vadot 	int			tx_bytes;	/* tx_cmd_sz + tx_data_sz */
872de9b4d3SEmmanuel Vadot 	int			tx_bytes_sent;
882de9b4d3SEmmanuel Vadot 	int			rx_bytes;	/* rx_cmd_sz + rx_data_sz */
892de9b4d3SEmmanuel Vadot 	int			rx_bytes_rcvd;
902de9b4d3SEmmanuel Vadot 	int			busy;
912de9b4d3SEmmanuel Vadot 	int			is_dual;
922de9b4d3SEmmanuel Vadot 	int			is_stacked;
932de9b4d3SEmmanuel Vadot 	int			is_dio;
942de9b4d3SEmmanuel Vadot };
952de9b4d3SEmmanuel Vadot 
962de9b4d3SEmmanuel Vadot #define ZY7_QSPI_DEFAULT_SPI_CLOCK	50000000
972de9b4d3SEmmanuel Vadot 
982de9b4d3SEmmanuel Vadot #define QSPI_SC_LOCK(sc)		mtx_lock(&(sc)->sc_mtx)
992de9b4d3SEmmanuel Vadot #define	QSPI_SC_UNLOCK(sc)		mtx_unlock(&(sc)->sc_mtx)
1002de9b4d3SEmmanuel Vadot #define QSPI_SC_LOCK_INIT(sc) \
1012de9b4d3SEmmanuel Vadot 	mtx_init(&(sc)->sc_mtx, device_get_nameunit((sc)->dev),	NULL, MTX_DEF)
1022de9b4d3SEmmanuel Vadot #define QSPI_SC_LOCK_DESTROY(sc)	mtx_destroy(&(sc)->sc_mtx)
1032de9b4d3SEmmanuel Vadot #define QSPI_SC_ASSERT_LOCKED(sc)	mtx_assert(&(sc)->sc_mtx, MA_OWNED)
1042de9b4d3SEmmanuel Vadot 
1052de9b4d3SEmmanuel Vadot #define RD4(sc, off)		(bus_read_4((sc)->mem_res, (off)))
1062de9b4d3SEmmanuel Vadot #define WR4(sc, off, val)	(bus_write_4((sc)->mem_res, (off), (val)))
1072de9b4d3SEmmanuel Vadot 
1082de9b4d3SEmmanuel Vadot /*
1092de9b4d3SEmmanuel Vadot  * QSPI device registers.
1102de9b4d3SEmmanuel Vadot  * Reference: Zynq-7000 All Programmable SoC Technical Reference Manual.
1112de9b4d3SEmmanuel Vadot  * (v1.12.2) July 1, 2018.  Xilinx doc UG585.
1122de9b4d3SEmmanuel Vadot  */
1132de9b4d3SEmmanuel Vadot #define ZY7_QSPI_CONFIG_REG		0x0000
1142de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_CONFIG_IFMODE		(1U << 31)
1152de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_CONFIG_ENDIAN		(1 << 26)
1162de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_CONFIG_HOLDB_DR		(1 << 19)
1172de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_CONFIG_RSVD1			(1 << 17) /* must be 1 */
1182de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_CONFIG_MANSTRT		(1 << 16)
1192de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_CONFIG_MANSTRTEN		(1 << 15)
1202de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_CONFIG_SSFORCE		(1 << 14)
1212de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_CONFIG_PCS			(1 << 10)
1222de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_CONFIG_REF_CLK		(1 << 8)
1232de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_CONFIG_FIFO_WIDTH_MASK	(3 << 6)
1242de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_CONFIG_FIFO_WIDTH32		(3 << 6)
1252de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_CONFIG_BAUD_RATE_DIV_MASK	(7 << 3)
1262de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_CONFIG_BAUD_RATE_DIV_SHIFT	3
1272de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_CONFIG_BAUD_RATE_DIV(x)	((x) << 3) /* divide by 2<<x */
1282de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_CONFIG_CLK_PH		(1 << 2)   /* clock phase */
1292de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_CONFIG_CLK_POL		(1 << 1)   /* clock polarity */
1302de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_CONFIG_MODE_SEL		(1 << 0)   /* master enable */
1312de9b4d3SEmmanuel Vadot 
1322de9b4d3SEmmanuel Vadot #define ZY7_QSPI_INTR_STAT_REG		0x0004
1332de9b4d3SEmmanuel Vadot #define ZY7_QSPI_INTR_EN_REG		0x0008
1342de9b4d3SEmmanuel Vadot #define ZY7_QSPI_INTR_DIS_REG		0x000c
1352de9b4d3SEmmanuel Vadot #define ZY7_QSPI_INTR_MASK_REG		0x0010
1362de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_INTR_TX_FIFO_UNDERFLOW	(1 << 6)
1372de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_INTR_RX_FIFO_FULL		(1 << 5)
1382de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_INTR_RX_FIFO_NOT_EMPTY	(1 << 4)
1392de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_INTR_TX_FIFO_FULL		(1 << 3)
1402de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_INTR_TX_FIFO_NOT_FULL	(1 << 2)
1412de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_INTR_RX_OVERFLOW		(1 << 0)
1422de9b4d3SEmmanuel Vadot 
1432de9b4d3SEmmanuel Vadot #define ZY7_QSPI_EN_REG			0x0014
1442de9b4d3SEmmanuel Vadot #define   ZY7_SPI_ENABLE			1
1452de9b4d3SEmmanuel Vadot 
1462de9b4d3SEmmanuel Vadot #define ZY7_QSPI_DELAY_REG		0x0018
1472de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_DELAY_NSS_MASK		(0xffU << 24)
1482de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_DELAY_NSS_SHIFT		24
1492de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_DELAY_NSS(x)			((x) << 24)
1502de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_DELAY_BTWN_MASK		(0xff << 16)
1512de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_DELAY_BTWN_SHIFT		16
1522de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_DELAY_BTWN(x)		((x) << 16)
1532de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_DELAY_AFTER_MASK		(0xff << 8)
1542de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_DELAY_AFTER_SHIFT		8
1552de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_DELAY_AFTER(x)		((x) << 8)
1562de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_DELAY_INIT_MASK		0xff
1572de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_DELAY_INIT_SHIFT		0
1582de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_DELAY_INIT(x)		(x)
1592de9b4d3SEmmanuel Vadot 
1602de9b4d3SEmmanuel Vadot #define ZY7_QSPI_TXD0_REG		0x001c
1612de9b4d3SEmmanuel Vadot #define ZY7_QSPI_RX_DATA_REG		0x0020
1622de9b4d3SEmmanuel Vadot 
1632de9b4d3SEmmanuel Vadot #define ZY7_QSPI_SLV_IDLE_CT_REG	0x0024
1642de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_SLV_IDLE_CT_MASK		0xff
1652de9b4d3SEmmanuel Vadot 
1662de9b4d3SEmmanuel Vadot #define ZY7_QSPI_TX_THRESH_REG		0x0028
1672de9b4d3SEmmanuel Vadot #define ZY7_QSPI_RX_THRESH_REG		0x002c
1682de9b4d3SEmmanuel Vadot 
1692de9b4d3SEmmanuel Vadot #define ZY7_QSPI_GPIO_REG		0x0030
1702de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_GPIO_WP_N			1
1712de9b4d3SEmmanuel Vadot 
1722de9b4d3SEmmanuel Vadot #define ZY7_QSPI_LPBK_DLY_ADJ_REG	0x0038
1732de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_LPBK_DLY_ADJ_LPBK_SEL	(1 << 8)
1742de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_LPBK_DLY_ADJ_LPBK_PH		(1 << 7)
1752de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_LPBK_DLY_ADJ_USE_LPBK	(1 << 5)
1762de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_LPBK_DLY_ADJ_DLY1_MASK	(3 << 3)
1772de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_LPBK_DLY_ADJ_DLY1_SHIFT	3
1782de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_LPBK_DLY_ADJ_DLY1(x)		((x) << 3)
1792de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_LPBK_DLY_ADJ_DLY0_MASK	7
1802de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_LPBK_DLY_ADJ_DLY0_SHIFT	0
1812de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_LPBK_DLY_ADJ_DLY0(x)		(x)
1822de9b4d3SEmmanuel Vadot 
1832de9b4d3SEmmanuel Vadot #define ZY7_QSPI_TXD1_REG		0x0080
1842de9b4d3SEmmanuel Vadot #define ZY7_QSPI_TXD2_REG		0x0084
1852de9b4d3SEmmanuel Vadot #define ZY7_QSPI_TXD3_REG		0x0088
1862de9b4d3SEmmanuel Vadot 
1872de9b4d3SEmmanuel Vadot #define ZY7_QSPI_LQSPI_CFG_REG		0x00a0
1882de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_LQSPI_CFG_LINEAR		(1U << 31)
1892de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_LQSPI_CFG_TWO_MEM		(1 << 30)
1902de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_LQSPI_CFG_SEP_BUS		(1 << 29)
1912de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_LQSPI_CFG_U_PAGE		(1 << 28)
1922de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_LQSPI_CFG_MODE_EN		(1 << 25)
1932de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_LQSPI_CFG_MODE_ON		(1 << 24)
1942de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_LQSPI_CFG_MODE_BITS_MASK	(0xff << 16)
1952de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_LQSPI_CFG_MODE_BITS_SHIFT	16
1962de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_LQSPI_CFG_MODE_BITS(x)	((x) << 16)
1972de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_LQSPI_CFG_DUMMY_BYTES_MASK	(7 << 8)
1982de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_LQSPI_CFG_DUMMY_BYTES_SHIFT	8
1992de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_LQSPI_CFG_DUMMY_BYTES(x)	((x) << 8)
2002de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_LQSPI_CFG_INST_CODE_MASK	0xff
2012de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_LQSPI_CFG_INST_CODE_SHIFT	0
2022de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_LQSPI_CFG_INST_CODE(x)	(x)
2032de9b4d3SEmmanuel Vadot 
2042de9b4d3SEmmanuel Vadot #define ZY7_QSPI_LQSPI_STS_REG		0x00a4
2052de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_LQSPI_STS_D_FSM_ERR		(1 << 2)
2062de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_LQSPI_STS_WR_RECVD		(1 << 1)
2072de9b4d3SEmmanuel Vadot 
2082de9b4d3SEmmanuel Vadot #define ZY7_QSPI_MOD_ID_REG		0x00fc
2092de9b4d3SEmmanuel Vadot 
2102de9b4d3SEmmanuel Vadot static int zy7_qspi_detach(device_t);
2112de9b4d3SEmmanuel Vadot 
2122de9b4d3SEmmanuel Vadot /* Fill hardware fifo with command and data bytes. */
2132de9b4d3SEmmanuel Vadot static void
zy7_qspi_write_fifo(struct zy7_qspi_softc * sc,int nbytes)2142de9b4d3SEmmanuel Vadot zy7_qspi_write_fifo(struct zy7_qspi_softc *sc, int nbytes)
2152de9b4d3SEmmanuel Vadot {
2162de9b4d3SEmmanuel Vadot 	int n, nvalid;
2172de9b4d3SEmmanuel Vadot 	uint32_t data;
2182de9b4d3SEmmanuel Vadot 
2192de9b4d3SEmmanuel Vadot 	while (nbytes > 0) {
2202de9b4d3SEmmanuel Vadot 		nvalid = MIN(4, nbytes);
2212de9b4d3SEmmanuel Vadot 		data = 0xffffffff;
2222de9b4d3SEmmanuel Vadot 
2232de9b4d3SEmmanuel Vadot 		/*
2242de9b4d3SEmmanuel Vadot 		 * A hardware bug forces us to wait until the tx fifo is
2252de9b4d3SEmmanuel Vadot 		 * empty before writing partial words.  We'll come back
2262de9b4d3SEmmanuel Vadot 		 * next tx interrupt.
2272de9b4d3SEmmanuel Vadot 		 */
2282de9b4d3SEmmanuel Vadot 		if (nvalid < 4 && (RD4(sc, ZY7_QSPI_INTR_STAT_REG) &
2292de9b4d3SEmmanuel Vadot 		    ZY7_QSPI_INTR_TX_FIFO_NOT_FULL) == 0)
2302de9b4d3SEmmanuel Vadot 			return;
2312de9b4d3SEmmanuel Vadot 
2322de9b4d3SEmmanuel Vadot 		if (sc->tx_bytes_sent < sc->cmd->tx_cmd_sz) {
2332de9b4d3SEmmanuel Vadot 			/* Writing command. */
2342de9b4d3SEmmanuel Vadot 			n = MIN(nvalid, sc->cmd->tx_cmd_sz -
2352de9b4d3SEmmanuel Vadot 			    sc->tx_bytes_sent);
2362de9b4d3SEmmanuel Vadot 			memcpy(&data, (uint8_t *)sc->cmd->tx_cmd +
2372de9b4d3SEmmanuel Vadot 			    sc->tx_bytes_sent, n);
2382de9b4d3SEmmanuel Vadot 
2392de9b4d3SEmmanuel Vadot 			if (nvalid > n) {
2402de9b4d3SEmmanuel Vadot 				/* Writing start of data. */
2412de9b4d3SEmmanuel Vadot 				memcpy((uint8_t *)&data + n,
2422de9b4d3SEmmanuel Vadot 				    sc->cmd->tx_data, nvalid - n);
2432de9b4d3SEmmanuel Vadot 			}
2442de9b4d3SEmmanuel Vadot 		} else
2452de9b4d3SEmmanuel Vadot 			/* Writing data. */
2462de9b4d3SEmmanuel Vadot 			memcpy(&data, (uint8_t *)sc->cmd->tx_data +
2472de9b4d3SEmmanuel Vadot 			    (sc->tx_bytes_sent - sc->cmd->tx_cmd_sz), nvalid);
2482de9b4d3SEmmanuel Vadot 
2492de9b4d3SEmmanuel Vadot 		switch (nvalid) {
2502de9b4d3SEmmanuel Vadot 		case 1:
2512de9b4d3SEmmanuel Vadot 			WR4(sc, ZY7_QSPI_TXD1_REG, data);
2522de9b4d3SEmmanuel Vadot 			break;
2532de9b4d3SEmmanuel Vadot 		case 2:
2542de9b4d3SEmmanuel Vadot 			WR4(sc, ZY7_QSPI_TXD2_REG, data);
2552de9b4d3SEmmanuel Vadot 			break;
2562de9b4d3SEmmanuel Vadot 		case 3:
2572de9b4d3SEmmanuel Vadot 			WR4(sc, ZY7_QSPI_TXD3_REG, data);
2582de9b4d3SEmmanuel Vadot 			break;
2592de9b4d3SEmmanuel Vadot 		case 4:
2602de9b4d3SEmmanuel Vadot 			WR4(sc, ZY7_QSPI_TXD0_REG, data);
2612de9b4d3SEmmanuel Vadot 			break;
2622de9b4d3SEmmanuel Vadot 		}
2632de9b4d3SEmmanuel Vadot 
2642de9b4d3SEmmanuel Vadot 		sc->tx_bytes_sent += nvalid;
2652de9b4d3SEmmanuel Vadot 		nbytes -= nvalid;
2662de9b4d3SEmmanuel Vadot 	}
2672de9b4d3SEmmanuel Vadot }
2682de9b4d3SEmmanuel Vadot 
2692de9b4d3SEmmanuel Vadot /* Read hardware fifo data into command response and data buffers. */
2702de9b4d3SEmmanuel Vadot static void
zy7_qspi_read_fifo(struct zy7_qspi_softc * sc)2712de9b4d3SEmmanuel Vadot zy7_qspi_read_fifo(struct zy7_qspi_softc *sc)
2722de9b4d3SEmmanuel Vadot {
2732de9b4d3SEmmanuel Vadot 	int n, nbytes;
2742de9b4d3SEmmanuel Vadot 	uint32_t data;
2752de9b4d3SEmmanuel Vadot 
2762de9b4d3SEmmanuel Vadot 	do {
2772de9b4d3SEmmanuel Vadot 		data = RD4(sc, ZY7_QSPI_RX_DATA_REG);
2782de9b4d3SEmmanuel Vadot 		nbytes = MIN(4, sc->rx_bytes - sc->rx_bytes_rcvd);
2792de9b4d3SEmmanuel Vadot 
2802de9b4d3SEmmanuel Vadot 		/*
2812de9b4d3SEmmanuel Vadot 		 * Last word in non-word-multiple transfer is packed
2822de9b4d3SEmmanuel Vadot 		 * non-intuitively.
2832de9b4d3SEmmanuel Vadot 		 */
2842de9b4d3SEmmanuel Vadot 		if (nbytes < 4)
2852de9b4d3SEmmanuel Vadot 			data >>= 8 * (4 - nbytes);
2862de9b4d3SEmmanuel Vadot 
2872de9b4d3SEmmanuel Vadot 		if (sc->rx_bytes_rcvd < sc->cmd->rx_cmd_sz) {
2882de9b4d3SEmmanuel Vadot 			/* Reading command. */
2892de9b4d3SEmmanuel Vadot 			n = MIN(nbytes, sc->cmd->rx_cmd_sz -
2902de9b4d3SEmmanuel Vadot 			    sc->rx_bytes_rcvd);
2912de9b4d3SEmmanuel Vadot 			memcpy((uint8_t *)sc->cmd->rx_cmd + sc->rx_bytes_rcvd,
2922de9b4d3SEmmanuel Vadot 			    &data, n);
2932de9b4d3SEmmanuel Vadot 			sc->rx_bytes_rcvd += n;
2942de9b4d3SEmmanuel Vadot 			nbytes -= n;
2952de9b4d3SEmmanuel Vadot 			data >>= 8 * n;
2962de9b4d3SEmmanuel Vadot 		}
2972de9b4d3SEmmanuel Vadot 
2982de9b4d3SEmmanuel Vadot 		if (nbytes > 0) {
2992de9b4d3SEmmanuel Vadot 			/* Reading data. */
3002de9b4d3SEmmanuel Vadot 			memcpy((uint8_t *)sc->cmd->rx_data +
3012de9b4d3SEmmanuel Vadot 			    (sc->rx_bytes_rcvd - sc->cmd->rx_cmd_sz),
3022de9b4d3SEmmanuel Vadot 			    &data, nbytes);
3032de9b4d3SEmmanuel Vadot 			sc->rx_bytes_rcvd += nbytes;
3042de9b4d3SEmmanuel Vadot 		}
3052de9b4d3SEmmanuel Vadot 
3062de9b4d3SEmmanuel Vadot 	} while (sc->rx_bytes_rcvd < sc->rx_bytes &&
3072de9b4d3SEmmanuel Vadot 		 (RD4(sc, ZY7_QSPI_INTR_STAT_REG) &
3082de9b4d3SEmmanuel Vadot 		  ZY7_QSPI_INTR_RX_FIFO_NOT_EMPTY) != 0);
3092de9b4d3SEmmanuel Vadot }
3102de9b4d3SEmmanuel Vadot 
3112de9b4d3SEmmanuel Vadot /* End a transfer early by draining rx fifo and disabling interrupts. */
3122de9b4d3SEmmanuel Vadot static void
zy7_qspi_abort_transfer(struct zy7_qspi_softc * sc)3132de9b4d3SEmmanuel Vadot zy7_qspi_abort_transfer(struct zy7_qspi_softc *sc)
3142de9b4d3SEmmanuel Vadot {
3152de9b4d3SEmmanuel Vadot 	/* Drain receive fifo. */
3162de9b4d3SEmmanuel Vadot 	while ((RD4(sc, ZY7_QSPI_INTR_STAT_REG) &
3172de9b4d3SEmmanuel Vadot 		ZY7_QSPI_INTR_RX_FIFO_NOT_EMPTY) != 0)
3182de9b4d3SEmmanuel Vadot 		(void)RD4(sc, ZY7_QSPI_RX_DATA_REG);
3192de9b4d3SEmmanuel Vadot 
3202de9b4d3SEmmanuel Vadot 	/* Shut down interrupts. */
3212de9b4d3SEmmanuel Vadot 	WR4(sc, ZY7_QSPI_INTR_DIS_REG,
3222de9b4d3SEmmanuel Vadot 	    ZY7_QSPI_INTR_RX_OVERFLOW |
3232de9b4d3SEmmanuel Vadot 	    ZY7_QSPI_INTR_RX_FIFO_NOT_EMPTY |
3242de9b4d3SEmmanuel Vadot 	    ZY7_QSPI_INTR_TX_FIFO_NOT_FULL);
3252de9b4d3SEmmanuel Vadot }
3262de9b4d3SEmmanuel Vadot 
3272de9b4d3SEmmanuel Vadot static void
zy7_qspi_intr(void * arg)3282de9b4d3SEmmanuel Vadot zy7_qspi_intr(void *arg)
3292de9b4d3SEmmanuel Vadot {
3302de9b4d3SEmmanuel Vadot 	struct zy7_qspi_softc *sc = (struct zy7_qspi_softc *)arg;
3312de9b4d3SEmmanuel Vadot 	uint32_t istatus;
3322de9b4d3SEmmanuel Vadot 
3332de9b4d3SEmmanuel Vadot 	QSPI_SC_LOCK(sc);
3342de9b4d3SEmmanuel Vadot 
3352de9b4d3SEmmanuel Vadot 	sc->interrupts++;
3362de9b4d3SEmmanuel Vadot 
3372de9b4d3SEmmanuel Vadot 	istatus = RD4(sc, ZY7_QSPI_INTR_STAT_REG);
3382de9b4d3SEmmanuel Vadot 
3392de9b4d3SEmmanuel Vadot 	/* Stray interrupts can happen if a transfer gets interrupted. */
3402de9b4d3SEmmanuel Vadot 	if (!sc->busy) {
3412de9b4d3SEmmanuel Vadot 		sc->stray_ints++;
3422de9b4d3SEmmanuel Vadot 		QSPI_SC_UNLOCK(sc);
3432de9b4d3SEmmanuel Vadot 		return;
3442de9b4d3SEmmanuel Vadot 	}
3452de9b4d3SEmmanuel Vadot 
3462de9b4d3SEmmanuel Vadot 	if ((istatus & ZY7_QSPI_INTR_RX_OVERFLOW) != 0) {
3472de9b4d3SEmmanuel Vadot 		device_printf(sc->dev, "rx fifo overflow!\n");
3482de9b4d3SEmmanuel Vadot 		sc->rx_overflows++;
3492de9b4d3SEmmanuel Vadot 
3502de9b4d3SEmmanuel Vadot 		/* Clear status bit. */
3512de9b4d3SEmmanuel Vadot 		WR4(sc, ZY7_QSPI_INTR_STAT_REG,
3522de9b4d3SEmmanuel Vadot 		    ZY7_QSPI_INTR_RX_OVERFLOW);
3532de9b4d3SEmmanuel Vadot 	}
3542de9b4d3SEmmanuel Vadot 
3552de9b4d3SEmmanuel Vadot 	/* Empty receive fifo before any more transmit data is sent. */
3562de9b4d3SEmmanuel Vadot 	if (sc->rx_bytes_rcvd < sc->rx_bytes &&
3572de9b4d3SEmmanuel Vadot 	    (istatus & ZY7_QSPI_INTR_RX_FIFO_NOT_EMPTY) != 0) {
3582de9b4d3SEmmanuel Vadot 		zy7_qspi_read_fifo(sc);
3592de9b4d3SEmmanuel Vadot 		if (sc->rx_bytes_rcvd == sc->rx_bytes)
3602de9b4d3SEmmanuel Vadot 			/* Disable receive interrupts. */
3612de9b4d3SEmmanuel Vadot 			WR4(sc, ZY7_QSPI_INTR_DIS_REG,
3622de9b4d3SEmmanuel Vadot 			    ZY7_QSPI_INTR_RX_FIFO_NOT_EMPTY |
3632de9b4d3SEmmanuel Vadot 			    ZY7_QSPI_INTR_RX_OVERFLOW);
3642de9b4d3SEmmanuel Vadot 	}
3652de9b4d3SEmmanuel Vadot 
3662de9b4d3SEmmanuel Vadot 	/*
3672de9b4d3SEmmanuel Vadot 	 * Transmit underflows aren't really a bug because a hardware
3682de9b4d3SEmmanuel Vadot 	 * bug forces us to allow the tx fifo to go empty between full
3692de9b4d3SEmmanuel Vadot 	 * and partial fifo writes.  Why bother counting?
3702de9b4d3SEmmanuel Vadot 	 */
3712de9b4d3SEmmanuel Vadot 	if ((istatus & ZY7_QSPI_INTR_TX_FIFO_UNDERFLOW) != 0) {
3722de9b4d3SEmmanuel Vadot 		sc->tx_underflows++;
3732de9b4d3SEmmanuel Vadot 
3742de9b4d3SEmmanuel Vadot 		/* Clear status bit. */
3752de9b4d3SEmmanuel Vadot 		WR4(sc, ZY7_QSPI_INTR_STAT_REG,
3762de9b4d3SEmmanuel Vadot 		    ZY7_QSPI_INTR_TX_FIFO_UNDERFLOW);
3772de9b4d3SEmmanuel Vadot 	}
3782de9b4d3SEmmanuel Vadot 
3792de9b4d3SEmmanuel Vadot 	/* Fill transmit fifo. */
3802de9b4d3SEmmanuel Vadot 	if (sc->tx_bytes_sent < sc->tx_bytes &&
3812de9b4d3SEmmanuel Vadot 	    (istatus & ZY7_QSPI_INTR_TX_FIFO_NOT_FULL) != 0) {
3822de9b4d3SEmmanuel Vadot 		zy7_qspi_write_fifo(sc, MIN(240, sc->tx_bytes -
3832de9b4d3SEmmanuel Vadot 			sc->tx_bytes_sent));
3842de9b4d3SEmmanuel Vadot 
3852de9b4d3SEmmanuel Vadot 		if (sc->tx_bytes_sent == sc->tx_bytes) {
3862de9b4d3SEmmanuel Vadot 			/*
3872de9b4d3SEmmanuel Vadot 			 * Disable transmit FIFO interrupt, enable receive
3882de9b4d3SEmmanuel Vadot 			 * FIFO interrupt.
3892de9b4d3SEmmanuel Vadot 			 */
3902de9b4d3SEmmanuel Vadot 			WR4(sc, ZY7_QSPI_INTR_DIS_REG,
3912de9b4d3SEmmanuel Vadot 			    ZY7_QSPI_INTR_TX_FIFO_NOT_FULL);
3922de9b4d3SEmmanuel Vadot 			WR4(sc, ZY7_QSPI_INTR_EN_REG,
3932de9b4d3SEmmanuel Vadot 			    ZY7_QSPI_INTR_RX_FIFO_NOT_EMPTY);
3942de9b4d3SEmmanuel Vadot 		}
3952de9b4d3SEmmanuel Vadot 	}
3962de9b4d3SEmmanuel Vadot 
3972de9b4d3SEmmanuel Vadot 	/* Finished with transfer? */
3982de9b4d3SEmmanuel Vadot 	if (sc->tx_bytes_sent == sc->tx_bytes &&
3992de9b4d3SEmmanuel Vadot 	    sc->rx_bytes_rcvd == sc->rx_bytes) {
4002de9b4d3SEmmanuel Vadot 		/* De-assert CS. */
4012de9b4d3SEmmanuel Vadot 		sc->cfg_reg_shadow |= ZY7_QSPI_CONFIG_PCS;
4022de9b4d3SEmmanuel Vadot 		WR4(sc, ZY7_QSPI_CONFIG_REG, sc->cfg_reg_shadow);
4032de9b4d3SEmmanuel Vadot 
4042de9b4d3SEmmanuel Vadot 		wakeup(sc->dev);
4052de9b4d3SEmmanuel Vadot 	}
4062de9b4d3SEmmanuel Vadot 
4072de9b4d3SEmmanuel Vadot 	QSPI_SC_UNLOCK(sc);
4082de9b4d3SEmmanuel Vadot }
4092de9b4d3SEmmanuel Vadot 
4102de9b4d3SEmmanuel Vadot /* Initialize hardware. */
4112de9b4d3SEmmanuel Vadot static int
zy7_qspi_init_hw(struct zy7_qspi_softc * sc)4122de9b4d3SEmmanuel Vadot zy7_qspi_init_hw(struct zy7_qspi_softc *sc)
4132de9b4d3SEmmanuel Vadot {
4142de9b4d3SEmmanuel Vadot 	uint32_t baud_div;
4152de9b4d3SEmmanuel Vadot 
4162de9b4d3SEmmanuel Vadot 	/* Configure LQSPI Config register.  Disable linear mode. */
4172de9b4d3SEmmanuel Vadot 	sc->lqspi_cfg_shadow = RD4(sc, ZY7_QSPI_LQSPI_CFG_REG);
4182de9b4d3SEmmanuel Vadot 	sc->lqspi_cfg_shadow &= ~(ZY7_QSPI_LQSPI_CFG_LINEAR |
4192de9b4d3SEmmanuel Vadot 				  ZY7_QSPI_LQSPI_CFG_TWO_MEM |
4202de9b4d3SEmmanuel Vadot 				  ZY7_QSPI_LQSPI_CFG_SEP_BUS);
4212de9b4d3SEmmanuel Vadot 	if (sc->is_dual) {
4222de9b4d3SEmmanuel Vadot 		sc->lqspi_cfg_shadow |= ZY7_QSPI_LQSPI_CFG_TWO_MEM;
4232de9b4d3SEmmanuel Vadot 		if (sc->is_stacked) {
4242de9b4d3SEmmanuel Vadot 			sc->lqspi_cfg_shadow &=
4252de9b4d3SEmmanuel Vadot 			    ~ZY7_QSPI_LQSPI_CFG_INST_CODE_MASK;
4262de9b4d3SEmmanuel Vadot 			sc->lqspi_cfg_shadow |=
4272de9b4d3SEmmanuel Vadot 			    ZY7_QSPI_LQSPI_CFG_INST_CODE(sc->is_dio ?
4282de9b4d3SEmmanuel Vadot 				CMD_READ_DUAL_IO : CMD_READ_QUAD_OUTPUT);
4292de9b4d3SEmmanuel Vadot 		} else
4302de9b4d3SEmmanuel Vadot 			sc->lqspi_cfg_shadow |= ZY7_QSPI_LQSPI_CFG_SEP_BUS;
4312de9b4d3SEmmanuel Vadot 	}
4322de9b4d3SEmmanuel Vadot 	WR4(sc, ZY7_QSPI_LQSPI_CFG_REG, sc->lqspi_cfg_shadow);
4332de9b4d3SEmmanuel Vadot 
4342de9b4d3SEmmanuel Vadot 	/* Find best clock divider. */
4352de9b4d3SEmmanuel Vadot 	baud_div = 0;
4362de9b4d3SEmmanuel Vadot 	while ((sc->ref_clock >> (baud_div + 1)) > sc->spi_clock &&
4372de9b4d3SEmmanuel Vadot 	       baud_div < 8)
4382de9b4d3SEmmanuel Vadot 		baud_div++;
4392de9b4d3SEmmanuel Vadot 	if (baud_div >= 8) {
4402de9b4d3SEmmanuel Vadot 		device_printf(sc->dev, "cannot configure clock divider: ref=%d"
4412de9b4d3SEmmanuel Vadot 		    " spi=%d.\n", sc->ref_clock, sc->spi_clock);
4422de9b4d3SEmmanuel Vadot 		return (EINVAL);
4432de9b4d3SEmmanuel Vadot 	}
4442de9b4d3SEmmanuel Vadot 	sc->spi_clk_real_freq = sc->ref_clock >> (baud_div + 1);
4452de9b4d3SEmmanuel Vadot 
4462de9b4d3SEmmanuel Vadot 	/*
4472de9b4d3SEmmanuel Vadot 	 * If divider is 2 (the max speed), use internal loopback master
4482de9b4d3SEmmanuel Vadot 	 * clock for read data.  (See section 12.3.1 in ref man.)
4492de9b4d3SEmmanuel Vadot 	 */
4502de9b4d3SEmmanuel Vadot 	if (baud_div == 0)
4512de9b4d3SEmmanuel Vadot 		WR4(sc, ZY7_QSPI_LPBK_DLY_ADJ_REG,
4522de9b4d3SEmmanuel Vadot 		    ZY7_QSPI_LPBK_DLY_ADJ_USE_LPBK |
4532de9b4d3SEmmanuel Vadot 		    ZY7_QSPI_LPBK_DLY_ADJ_DLY1(0) |
4542de9b4d3SEmmanuel Vadot 		    ZY7_QSPI_LPBK_DLY_ADJ_DLY0(0));
4552de9b4d3SEmmanuel Vadot 	else
4562de9b4d3SEmmanuel Vadot 		WR4(sc, ZY7_QSPI_LPBK_DLY_ADJ_REG, 0);
4572de9b4d3SEmmanuel Vadot 
4582de9b4d3SEmmanuel Vadot 	/* Set up configuration register. */
4592de9b4d3SEmmanuel Vadot 	sc->cfg_reg_shadow =
4602de9b4d3SEmmanuel Vadot 		ZY7_QSPI_CONFIG_IFMODE |
4612de9b4d3SEmmanuel Vadot 		ZY7_QSPI_CONFIG_HOLDB_DR |
4622de9b4d3SEmmanuel Vadot 		ZY7_QSPI_CONFIG_RSVD1 |
4632de9b4d3SEmmanuel Vadot 		ZY7_QSPI_CONFIG_SSFORCE |
4642de9b4d3SEmmanuel Vadot 		ZY7_QSPI_CONFIG_PCS |
4652de9b4d3SEmmanuel Vadot 		ZY7_QSPI_CONFIG_FIFO_WIDTH32 |
4662de9b4d3SEmmanuel Vadot 		ZY7_QSPI_CONFIG_BAUD_RATE_DIV(baud_div) |
4672de9b4d3SEmmanuel Vadot 		ZY7_QSPI_CONFIG_MODE_SEL;
4682de9b4d3SEmmanuel Vadot 	WR4(sc, ZY7_QSPI_CONFIG_REG, sc->cfg_reg_shadow);
4692de9b4d3SEmmanuel Vadot 
4702de9b4d3SEmmanuel Vadot 	/*
4712de9b4d3SEmmanuel Vadot 	 * Set thresholds.  We must use 1 for tx threshold because there
4722de9b4d3SEmmanuel Vadot 	 * is no fifo empty flag and we need one to implement a bug
4732de9b4d3SEmmanuel Vadot 	 * workaround.
4742de9b4d3SEmmanuel Vadot 	 */
4752de9b4d3SEmmanuel Vadot 	WR4(sc, ZY7_QSPI_TX_THRESH_REG, 1);
4762de9b4d3SEmmanuel Vadot 	WR4(sc, ZY7_QSPI_RX_THRESH_REG, 1);
4772de9b4d3SEmmanuel Vadot 
4782de9b4d3SEmmanuel Vadot 	/* Clear and disable all interrupts. */
4792de9b4d3SEmmanuel Vadot 	WR4(sc, ZY7_QSPI_INTR_STAT_REG, ~0);
4802de9b4d3SEmmanuel Vadot 	WR4(sc, ZY7_QSPI_INTR_DIS_REG, ~0);
4812de9b4d3SEmmanuel Vadot 
4822de9b4d3SEmmanuel Vadot 	/* Enable SPI. */
4832de9b4d3SEmmanuel Vadot 	WR4(sc, ZY7_QSPI_EN_REG, ZY7_SPI_ENABLE);
4842de9b4d3SEmmanuel Vadot 
4852de9b4d3SEmmanuel Vadot 	return (0);
4862de9b4d3SEmmanuel Vadot }
4872de9b4d3SEmmanuel Vadot 
4882de9b4d3SEmmanuel Vadot static void
zy7_qspi_add_sysctls(device_t dev)4892de9b4d3SEmmanuel Vadot zy7_qspi_add_sysctls(device_t dev)
4902de9b4d3SEmmanuel Vadot {
4912de9b4d3SEmmanuel Vadot 	struct zy7_qspi_softc *sc = device_get_softc(dev);
4922de9b4d3SEmmanuel Vadot 	struct sysctl_ctx_list *ctx;
4932de9b4d3SEmmanuel Vadot 	struct sysctl_oid_list *child;
4942de9b4d3SEmmanuel Vadot 
4952de9b4d3SEmmanuel Vadot 	ctx = device_get_sysctl_ctx(dev);
4962de9b4d3SEmmanuel Vadot 	child = SYSCTL_CHILDREN(device_get_sysctl_tree(dev));
4972de9b4d3SEmmanuel Vadot 
4982de9b4d3SEmmanuel Vadot 	SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "spi_clk_real_freq", CTLFLAG_RD,
4992de9b4d3SEmmanuel Vadot 	    &sc->spi_clk_real_freq, 0, "SPI clock real frequency");
5002de9b4d3SEmmanuel Vadot 
5012de9b4d3SEmmanuel Vadot 	SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "rx_overflows", CTLFLAG_RD,
5022de9b4d3SEmmanuel Vadot 	    &sc->rx_overflows, 0, "RX FIFO overflow events");
5032de9b4d3SEmmanuel Vadot 
5042de9b4d3SEmmanuel Vadot 	SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "tx_underflows", CTLFLAG_RD,
5052de9b4d3SEmmanuel Vadot 	    &sc->tx_underflows, 0, "TX FIFO underflow events");
5062de9b4d3SEmmanuel Vadot 
5072de9b4d3SEmmanuel Vadot 	SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "interrupts", CTLFLAG_RD,
5082de9b4d3SEmmanuel Vadot 	    &sc->interrupts, 0, "interrupt calls");
5092de9b4d3SEmmanuel Vadot 
5102de9b4d3SEmmanuel Vadot 	SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "stray_ints", CTLFLAG_RD,
5112de9b4d3SEmmanuel Vadot 	    &sc->stray_ints, 0, "stray interrupts");
5122de9b4d3SEmmanuel Vadot }
5132de9b4d3SEmmanuel Vadot 
5142de9b4d3SEmmanuel Vadot static int
zy7_qspi_probe(device_t dev)5152de9b4d3SEmmanuel Vadot zy7_qspi_probe(device_t dev)
5162de9b4d3SEmmanuel Vadot {
5172de9b4d3SEmmanuel Vadot 
5182de9b4d3SEmmanuel Vadot 	if (!ofw_bus_status_okay(dev))
5192de9b4d3SEmmanuel Vadot 		return (ENXIO);
5202de9b4d3SEmmanuel Vadot 
5212de9b4d3SEmmanuel Vadot 	if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
5222de9b4d3SEmmanuel Vadot 		return (ENXIO);
5232de9b4d3SEmmanuel Vadot 
5242de9b4d3SEmmanuel Vadot 	device_set_desc(dev, "Zynq Quad-SPI Flash Controller");
5252de9b4d3SEmmanuel Vadot 
5262de9b4d3SEmmanuel Vadot 	return (BUS_PROBE_DEFAULT);
5272de9b4d3SEmmanuel Vadot }
5282de9b4d3SEmmanuel Vadot 
5292de9b4d3SEmmanuel Vadot static int
zy7_qspi_attach(device_t dev)5302de9b4d3SEmmanuel Vadot zy7_qspi_attach(device_t dev)
5312de9b4d3SEmmanuel Vadot {
5322de9b4d3SEmmanuel Vadot 	struct zy7_qspi_softc *sc;
5332de9b4d3SEmmanuel Vadot 	int rid, err;
5342de9b4d3SEmmanuel Vadot 	phandle_t node;
5352de9b4d3SEmmanuel Vadot 	pcell_t cell;
5362de9b4d3SEmmanuel Vadot 
5372de9b4d3SEmmanuel Vadot 	sc = device_get_softc(dev);
5382de9b4d3SEmmanuel Vadot 	sc->dev = dev;
5392de9b4d3SEmmanuel Vadot 
5402de9b4d3SEmmanuel Vadot 	QSPI_SC_LOCK_INIT(sc);
5412de9b4d3SEmmanuel Vadot 
5422de9b4d3SEmmanuel Vadot 	/* Get ref-clock, spi-clock, and other properties. */
5432de9b4d3SEmmanuel Vadot 	node = ofw_bus_get_node(dev);
5442de9b4d3SEmmanuel Vadot 	if (OF_getprop(node, "ref-clock", &cell, sizeof(cell)) > 0)
5452de9b4d3SEmmanuel Vadot 		sc->ref_clock = fdt32_to_cpu(cell);
5462de9b4d3SEmmanuel Vadot 	else {
5472de9b4d3SEmmanuel Vadot 		device_printf(dev, "must have ref-clock property\n");
5482de9b4d3SEmmanuel Vadot 		return (ENXIO);
5492de9b4d3SEmmanuel Vadot 	}
5502de9b4d3SEmmanuel Vadot 	if (OF_getprop(node, "spi-clock", &cell, sizeof(cell)) > 0)
5512de9b4d3SEmmanuel Vadot 		sc->spi_clock = fdt32_to_cpu(cell);
5522de9b4d3SEmmanuel Vadot 	else
5532de9b4d3SEmmanuel Vadot 		sc->spi_clock = ZY7_QSPI_DEFAULT_SPI_CLOCK;
5542de9b4d3SEmmanuel Vadot 	if (OF_getprop(node, "is-stacked", &cell, sizeof(cell)) > 0 &&
5552de9b4d3SEmmanuel Vadot 	    fdt32_to_cpu(cell) != 0) {
5562de9b4d3SEmmanuel Vadot 		sc->is_dual = 1;
5572de9b4d3SEmmanuel Vadot 		sc->is_stacked = 1;
5582de9b4d3SEmmanuel Vadot 	} else if (OF_getprop(node, "is-dual", &cell, sizeof(cell)) > 0 &&
5592de9b4d3SEmmanuel Vadot 		   fdt32_to_cpu(cell) != 0)
5602de9b4d3SEmmanuel Vadot 		sc->is_dual = 1;
5612de9b4d3SEmmanuel Vadot 	if (OF_getprop(node, "is-dio", &cell, sizeof(cell)) > 0 &&
5622de9b4d3SEmmanuel Vadot 	    fdt32_to_cpu(cell) != 0)
5632de9b4d3SEmmanuel Vadot 		sc->is_dio = 1;
5642de9b4d3SEmmanuel Vadot 
5652de9b4d3SEmmanuel Vadot 	/* Get memory resource. */
5662de9b4d3SEmmanuel Vadot 	rid = 0;
5672de9b4d3SEmmanuel Vadot 	sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
5682de9b4d3SEmmanuel Vadot 	    RF_ACTIVE);
5692de9b4d3SEmmanuel Vadot 	if (sc->mem_res == NULL) {
5702de9b4d3SEmmanuel Vadot 		device_printf(dev, "could not allocate memory resources.\n");
5712de9b4d3SEmmanuel Vadot 		zy7_qspi_detach(dev);
5722de9b4d3SEmmanuel Vadot 		return (ENOMEM);
5732de9b4d3SEmmanuel Vadot 	}
5742de9b4d3SEmmanuel Vadot 
5752de9b4d3SEmmanuel Vadot 	/* Allocate IRQ. */
5762de9b4d3SEmmanuel Vadot 	rid = 0;
5772de9b4d3SEmmanuel Vadot 	sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
5782de9b4d3SEmmanuel Vadot 	    RF_ACTIVE);
5792de9b4d3SEmmanuel Vadot 	if (sc->irq_res == NULL) {
5802de9b4d3SEmmanuel Vadot 		device_printf(dev, "could not allocate IRQ resource.\n");
5812de9b4d3SEmmanuel Vadot 		zy7_qspi_detach(dev);
5822de9b4d3SEmmanuel Vadot 		return (ENOMEM);
5832de9b4d3SEmmanuel Vadot 	}
5842de9b4d3SEmmanuel Vadot 
5852de9b4d3SEmmanuel Vadot 	/* Activate the interrupt. */
5862de9b4d3SEmmanuel Vadot 	err = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC | INTR_MPSAFE,
5872de9b4d3SEmmanuel Vadot 	    NULL, zy7_qspi_intr, sc, &sc->intrhandle);
5882de9b4d3SEmmanuel Vadot 	if (err) {
5892de9b4d3SEmmanuel Vadot 		device_printf(dev, "could not setup IRQ.\n");
5902de9b4d3SEmmanuel Vadot 		zy7_qspi_detach(dev);
5912de9b4d3SEmmanuel Vadot 		return (err);
5922de9b4d3SEmmanuel Vadot 	}
5932de9b4d3SEmmanuel Vadot 
5942de9b4d3SEmmanuel Vadot 	/* Configure the device. */
5952de9b4d3SEmmanuel Vadot 	err = zy7_qspi_init_hw(sc);
5962de9b4d3SEmmanuel Vadot 	if (err) {
5972de9b4d3SEmmanuel Vadot 		zy7_qspi_detach(dev);
5982de9b4d3SEmmanuel Vadot 		return (err);
5992de9b4d3SEmmanuel Vadot 	}
6002de9b4d3SEmmanuel Vadot 
6015b56413dSWarner Losh 	sc->child = device_add_child(dev, "spibus", DEVICE_UNIT_ANY);
6022de9b4d3SEmmanuel Vadot 
6032de9b4d3SEmmanuel Vadot 	zy7_qspi_add_sysctls(dev);
6042de9b4d3SEmmanuel Vadot 
6052de9b4d3SEmmanuel Vadot 	/* Attach spibus driver as a child later when interrupts work. */
6065201deccSJohn Baldwin 	bus_delayed_attach_children(dev);
6072de9b4d3SEmmanuel Vadot 
6082de9b4d3SEmmanuel Vadot 	return (0);
6092de9b4d3SEmmanuel Vadot }
6102de9b4d3SEmmanuel Vadot 
6112de9b4d3SEmmanuel Vadot static int
zy7_qspi_detach(device_t dev)6122de9b4d3SEmmanuel Vadot zy7_qspi_detach(device_t dev)
6132de9b4d3SEmmanuel Vadot {
6142de9b4d3SEmmanuel Vadot 	struct zy7_qspi_softc *sc = device_get_softc(dev);
615*64d1a02eSJohn Baldwin 	int error;
6162de9b4d3SEmmanuel Vadot 
617*64d1a02eSJohn Baldwin 	error = bus_generic_detach(dev);
618*64d1a02eSJohn Baldwin 	if (error != 0)
619*64d1a02eSJohn Baldwin 		return (error);
6202de9b4d3SEmmanuel Vadot 
6212de9b4d3SEmmanuel Vadot 	/* Disable hardware. */
6222de9b4d3SEmmanuel Vadot 	if (sc->mem_res != NULL) {
6232de9b4d3SEmmanuel Vadot 		/* Disable SPI. */
6242de9b4d3SEmmanuel Vadot 		WR4(sc, ZY7_QSPI_EN_REG, 0);
6252de9b4d3SEmmanuel Vadot 
6262de9b4d3SEmmanuel Vadot 		/* Clear and disable all interrupts. */
6272de9b4d3SEmmanuel Vadot 		WR4(sc, ZY7_QSPI_INTR_STAT_REG, ~0);
6282de9b4d3SEmmanuel Vadot 		WR4(sc, ZY7_QSPI_INTR_DIS_REG, ~0);
6292de9b4d3SEmmanuel Vadot 	}
6302de9b4d3SEmmanuel Vadot 
6312de9b4d3SEmmanuel Vadot 	/* Teardown and release interrupt. */
6322de9b4d3SEmmanuel Vadot 	if (sc->irq_res != NULL) {
6332de9b4d3SEmmanuel Vadot 		if (sc->intrhandle)
6342de9b4d3SEmmanuel Vadot 			bus_teardown_intr(dev, sc->irq_res, sc->intrhandle);
6352de9b4d3SEmmanuel Vadot 		bus_release_resource(dev, SYS_RES_IRQ,
6362de9b4d3SEmmanuel Vadot 		    rman_get_rid(sc->irq_res), sc->irq_res);
6372de9b4d3SEmmanuel Vadot 	}
6382de9b4d3SEmmanuel Vadot 
6392de9b4d3SEmmanuel Vadot 	/* Release memory resource. */
6402de9b4d3SEmmanuel Vadot 	if (sc->mem_res != NULL)
6412de9b4d3SEmmanuel Vadot 		bus_release_resource(dev, SYS_RES_MEMORY,
6422de9b4d3SEmmanuel Vadot 		    rman_get_rid(sc->mem_res), sc->mem_res);
6432de9b4d3SEmmanuel Vadot 
6442de9b4d3SEmmanuel Vadot 	QSPI_SC_LOCK_DESTROY(sc);
6452de9b4d3SEmmanuel Vadot 
6462de9b4d3SEmmanuel Vadot 	return (0);
6472de9b4d3SEmmanuel Vadot }
6482de9b4d3SEmmanuel Vadot 
6492de9b4d3SEmmanuel Vadot static phandle_t
zy7_qspi_get_node(device_t bus,device_t dev)6502de9b4d3SEmmanuel Vadot zy7_qspi_get_node(device_t bus, device_t dev)
6512de9b4d3SEmmanuel Vadot {
6522de9b4d3SEmmanuel Vadot 
6532de9b4d3SEmmanuel Vadot 	return (ofw_bus_get_node(bus));
6542de9b4d3SEmmanuel Vadot }
6552de9b4d3SEmmanuel Vadot 
6562de9b4d3SEmmanuel Vadot static int
zy7_qspi_transfer(device_t dev,device_t child,struct spi_command * cmd)6572de9b4d3SEmmanuel Vadot zy7_qspi_transfer(device_t dev, device_t child, struct spi_command *cmd)
6582de9b4d3SEmmanuel Vadot {
6592de9b4d3SEmmanuel Vadot 	struct zy7_qspi_softc *sc = device_get_softc(dev);
6602de9b4d3SEmmanuel Vadot 	int err = 0;
6612de9b4d3SEmmanuel Vadot 
6622de9b4d3SEmmanuel Vadot 	KASSERT(cmd->tx_cmd_sz == cmd->rx_cmd_sz,
6632de9b4d3SEmmanuel Vadot 	    ("TX/RX command sizes should be equal"));
6642de9b4d3SEmmanuel Vadot 	KASSERT(cmd->tx_data_sz == cmd->rx_data_sz,
6652de9b4d3SEmmanuel Vadot 	    ("TX/RX data sizes should be equal"));
6662de9b4d3SEmmanuel Vadot 
6672de9b4d3SEmmanuel Vadot 	if (sc->is_dual && cmd->tx_data_sz % 2 != 0) {
6682de9b4d3SEmmanuel Vadot 		device_printf(dev, "driver does not support odd byte data "
6692de9b4d3SEmmanuel Vadot 		    "transfers in dual mode. (sz=%d)\n", cmd->tx_data_sz);
6702de9b4d3SEmmanuel Vadot 		return (EINVAL);
6712de9b4d3SEmmanuel Vadot 	}
6722de9b4d3SEmmanuel Vadot 
6732de9b4d3SEmmanuel Vadot 	QSPI_SC_LOCK(sc);
6742de9b4d3SEmmanuel Vadot 
6752de9b4d3SEmmanuel Vadot 	/* Wait for controller available. */
6762de9b4d3SEmmanuel Vadot 	while (sc->busy != 0) {
6772de9b4d3SEmmanuel Vadot 		err = mtx_sleep(dev, &sc->sc_mtx, 0, "zqspi0", 0);
6782de9b4d3SEmmanuel Vadot 		if (err) {
6792de9b4d3SEmmanuel Vadot 			QSPI_SC_UNLOCK(sc);
6802de9b4d3SEmmanuel Vadot 			return (err);
6812de9b4d3SEmmanuel Vadot 		}
6822de9b4d3SEmmanuel Vadot 	}
6832de9b4d3SEmmanuel Vadot 
6842de9b4d3SEmmanuel Vadot 	/* Start transfer. */
6852de9b4d3SEmmanuel Vadot 	sc->busy = 1;
6862de9b4d3SEmmanuel Vadot 	sc->cmd = cmd;
6872de9b4d3SEmmanuel Vadot 	sc->tx_bytes = sc->cmd->tx_cmd_sz + sc->cmd->tx_data_sz;
6882de9b4d3SEmmanuel Vadot 	sc->tx_bytes_sent = 0;
6892de9b4d3SEmmanuel Vadot 	sc->rx_bytes = sc->cmd->rx_cmd_sz + sc->cmd->rx_data_sz;
6902de9b4d3SEmmanuel Vadot 	sc->rx_bytes_rcvd = 0;
6912de9b4d3SEmmanuel Vadot 
6922de9b4d3SEmmanuel Vadot 	/* Enable interrupts.  zy7_qspi_intr() will handle transfer. */
6932de9b4d3SEmmanuel Vadot 	WR4(sc, ZY7_QSPI_INTR_EN_REG,
6942de9b4d3SEmmanuel Vadot 	    ZY7_QSPI_INTR_TX_FIFO_NOT_FULL |
6952de9b4d3SEmmanuel Vadot 	    ZY7_QSPI_INTR_RX_OVERFLOW);
6962de9b4d3SEmmanuel Vadot 
6972de9b4d3SEmmanuel Vadot #ifdef SPI_XFER_U_PAGE	/* XXX: future support for stacked memories. */
6982de9b4d3SEmmanuel Vadot 	if (sc->is_stacked) {
6992de9b4d3SEmmanuel Vadot 		if ((cmd->flags & SPI_XFER_U_PAGE) != 0)
7002de9b4d3SEmmanuel Vadot 			sc->lqspi_cfg_shadow |= ZY7_QSPI_LQSPI_CFG_U_PAGE;
7012de9b4d3SEmmanuel Vadot 		else
7022de9b4d3SEmmanuel Vadot 			sc->lqspi_cfg_shadow &= ~ZY7_QSPI_LQSPI_CFG_U_PAGE;
7032de9b4d3SEmmanuel Vadot 		WR4(sc, ZY7_QSPI_LQSPI_CFG_REG, sc->lqspi_cfg_shadow);
7042de9b4d3SEmmanuel Vadot 	}
7052de9b4d3SEmmanuel Vadot #endif
7062de9b4d3SEmmanuel Vadot 
7072de9b4d3SEmmanuel Vadot 	/* Assert CS. */
7082de9b4d3SEmmanuel Vadot 	sc->cfg_reg_shadow &= ~ZY7_QSPI_CONFIG_PCS;
7092de9b4d3SEmmanuel Vadot 	WR4(sc, ZY7_QSPI_CONFIG_REG, sc->cfg_reg_shadow);
7102de9b4d3SEmmanuel Vadot 
7112de9b4d3SEmmanuel Vadot 	/* Wait for completion. */
7122de9b4d3SEmmanuel Vadot 	err = mtx_sleep(dev, &sc->sc_mtx, 0, "zqspi1", hz * 2);
7132de9b4d3SEmmanuel Vadot 	if (err)
7142de9b4d3SEmmanuel Vadot 		zy7_qspi_abort_transfer(sc);
7152de9b4d3SEmmanuel Vadot 
7162de9b4d3SEmmanuel Vadot 	/* Release controller. */
7172de9b4d3SEmmanuel Vadot 	sc->busy = 0;
7182de9b4d3SEmmanuel Vadot 	wakeup_one(dev);
7192de9b4d3SEmmanuel Vadot 
7202de9b4d3SEmmanuel Vadot 	QSPI_SC_UNLOCK(sc);
7212de9b4d3SEmmanuel Vadot 
7222de9b4d3SEmmanuel Vadot 	return (err);
7232de9b4d3SEmmanuel Vadot }
7242de9b4d3SEmmanuel Vadot 
7252de9b4d3SEmmanuel Vadot static device_method_t zy7_qspi_methods[] = {
7262de9b4d3SEmmanuel Vadot 	/* Device interface */
7272de9b4d3SEmmanuel Vadot 	DEVMETHOD(device_probe,		zy7_qspi_probe),
7282de9b4d3SEmmanuel Vadot 	DEVMETHOD(device_attach,	zy7_qspi_attach),
7292de9b4d3SEmmanuel Vadot 	DEVMETHOD(device_detach,	zy7_qspi_detach),
7302de9b4d3SEmmanuel Vadot 
7312de9b4d3SEmmanuel Vadot 	/* SPI interface */
7322de9b4d3SEmmanuel Vadot 	DEVMETHOD(spibus_transfer,	zy7_qspi_transfer),
7332de9b4d3SEmmanuel Vadot 
7342de9b4d3SEmmanuel Vadot 	/* ofw_bus interface */
7352de9b4d3SEmmanuel Vadot 	DEVMETHOD(ofw_bus_get_node,	zy7_qspi_get_node),
7362de9b4d3SEmmanuel Vadot 
7372de9b4d3SEmmanuel Vadot 	DEVMETHOD_END
7382de9b4d3SEmmanuel Vadot };
7392de9b4d3SEmmanuel Vadot 
7402de9b4d3SEmmanuel Vadot static driver_t zy7_qspi_driver = {
7412de9b4d3SEmmanuel Vadot 	"zy7_qspi",
7422de9b4d3SEmmanuel Vadot 	zy7_qspi_methods,
7432de9b4d3SEmmanuel Vadot 	sizeof(struct zy7_qspi_softc),
7442de9b4d3SEmmanuel Vadot };
7452de9b4d3SEmmanuel Vadot 
746680ccae3SJohn Baldwin DRIVER_MODULE(zy7_qspi, simplebus, zy7_qspi_driver, 0, 0);
7475f31d14aSJohn Baldwin DRIVER_MODULE(ofw_spibus, zy7_qspi, ofw_spibus_driver, 0, 0);
7482de9b4d3SEmmanuel Vadot SIMPLEBUS_PNP_INFO(compat_data);
7492de9b4d3SEmmanuel Vadot MODULE_DEPEND(zy7_qspi, ofw_spibus, 1, 1, 1);
750