xref: /freebsd/sys/arm/xilinx/zy7_qspi.c (revision 2de9b4d3476945e773d19d3eb152ef0382f07963)
1*2de9b4d3SEmmanuel Vadot /*-
2*2de9b4d3SEmmanuel Vadot  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3*2de9b4d3SEmmanuel Vadot  *
4*2de9b4d3SEmmanuel Vadot  * Copyright (c) 2018 Thomas Skibo <thomasskibo@yahoo.com>
5*2de9b4d3SEmmanuel Vadot  * All rights reserved.
6*2de9b4d3SEmmanuel Vadot  *
7*2de9b4d3SEmmanuel Vadot  * Redistribution and use in source and binary forms, with or without
8*2de9b4d3SEmmanuel Vadot  * modification, are permitted provided that the following conditions
9*2de9b4d3SEmmanuel Vadot  * are met:
10*2de9b4d3SEmmanuel Vadot  * 1. Redistributions of source code must retain the above copyright
11*2de9b4d3SEmmanuel Vadot  *    notice, this list of conditions and the following disclaimer.
12*2de9b4d3SEmmanuel Vadot  * 2. Redistributions in binary form must reproduce the above copyright
13*2de9b4d3SEmmanuel Vadot  *    notice, this list of conditions and the following disclaimer in the
14*2de9b4d3SEmmanuel Vadot  *    documentation and/or other materials provided with the distribution.
15*2de9b4d3SEmmanuel Vadot  *
16*2de9b4d3SEmmanuel Vadot  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17*2de9b4d3SEmmanuel Vadot  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18*2de9b4d3SEmmanuel Vadot  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19*2de9b4d3SEmmanuel Vadot  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20*2de9b4d3SEmmanuel Vadot  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21*2de9b4d3SEmmanuel Vadot  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22*2de9b4d3SEmmanuel Vadot  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23*2de9b4d3SEmmanuel Vadot  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24*2de9b4d3SEmmanuel Vadot  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25*2de9b4d3SEmmanuel Vadot  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26*2de9b4d3SEmmanuel Vadot  * SUCH DAMAGE.
27*2de9b4d3SEmmanuel Vadot  */
28*2de9b4d3SEmmanuel Vadot 
29*2de9b4d3SEmmanuel Vadot #include <sys/cdefs.h>
30*2de9b4d3SEmmanuel Vadot __FBSDID("$FreeBSD$");
31*2de9b4d3SEmmanuel Vadot 
32*2de9b4d3SEmmanuel Vadot /*
33*2de9b4d3SEmmanuel Vadot  * This is a driver for the Quad-SPI Flash Controller in the Xilinx
34*2de9b4d3SEmmanuel Vadot  * Zynq-7000 SoC.
35*2de9b4d3SEmmanuel Vadot  */
36*2de9b4d3SEmmanuel Vadot 
37*2de9b4d3SEmmanuel Vadot #include <sys/param.h>
38*2de9b4d3SEmmanuel Vadot #include <sys/systm.h>
39*2de9b4d3SEmmanuel Vadot #include <sys/conf.h>
40*2de9b4d3SEmmanuel Vadot #include <sys/kernel.h>
41*2de9b4d3SEmmanuel Vadot #include <sys/module.h>
42*2de9b4d3SEmmanuel Vadot #include <sys/sysctl.h>
43*2de9b4d3SEmmanuel Vadot #include <sys/lock.h>
44*2de9b4d3SEmmanuel Vadot #include <sys/mutex.h>
45*2de9b4d3SEmmanuel Vadot #include <sys/resource.h>
46*2de9b4d3SEmmanuel Vadot #include <sys/rman.h>
47*2de9b4d3SEmmanuel Vadot #include <sys/uio.h>
48*2de9b4d3SEmmanuel Vadot 
49*2de9b4d3SEmmanuel Vadot #include <machine/bus.h>
50*2de9b4d3SEmmanuel Vadot #include <machine/resource.h>
51*2de9b4d3SEmmanuel Vadot #include <machine/stdarg.h>
52*2de9b4d3SEmmanuel Vadot 
53*2de9b4d3SEmmanuel Vadot #include <dev/fdt/fdt_common.h>
54*2de9b4d3SEmmanuel Vadot #include <dev/ofw/ofw_bus.h>
55*2de9b4d3SEmmanuel Vadot #include <dev/ofw/ofw_bus_subr.h>
56*2de9b4d3SEmmanuel Vadot 
57*2de9b4d3SEmmanuel Vadot #include <dev/spibus/spi.h>
58*2de9b4d3SEmmanuel Vadot #include <dev/spibus/spibusvar.h>
59*2de9b4d3SEmmanuel Vadot 
60*2de9b4d3SEmmanuel Vadot #include <dev/flash/mx25lreg.h>
61*2de9b4d3SEmmanuel Vadot 
62*2de9b4d3SEmmanuel Vadot #include "spibus_if.h"
63*2de9b4d3SEmmanuel Vadot 
64*2de9b4d3SEmmanuel Vadot static struct ofw_compat_data compat_data[] = {
65*2de9b4d3SEmmanuel Vadot 	{"xlnx,zy7_qspi",		1},
66*2de9b4d3SEmmanuel Vadot 	{"xlnx,zynq-qspi-1.0",		1},
67*2de9b4d3SEmmanuel Vadot 	{NULL,				0}
68*2de9b4d3SEmmanuel Vadot };
69*2de9b4d3SEmmanuel Vadot 
70*2de9b4d3SEmmanuel Vadot struct zy7_qspi_softc {
71*2de9b4d3SEmmanuel Vadot 	device_t		dev;
72*2de9b4d3SEmmanuel Vadot 	device_t		child;
73*2de9b4d3SEmmanuel Vadot 	struct mtx		sc_mtx;
74*2de9b4d3SEmmanuel Vadot 	struct resource		*mem_res;
75*2de9b4d3SEmmanuel Vadot 	struct resource		*irq_res;
76*2de9b4d3SEmmanuel Vadot 	void			*intrhandle;
77*2de9b4d3SEmmanuel Vadot 
78*2de9b4d3SEmmanuel Vadot 	uint32_t		cfg_reg_shadow;
79*2de9b4d3SEmmanuel Vadot 	uint32_t		lqspi_cfg_shadow;
80*2de9b4d3SEmmanuel Vadot 	uint32_t		spi_clock;
81*2de9b4d3SEmmanuel Vadot 	uint32_t		ref_clock;
82*2de9b4d3SEmmanuel Vadot 	unsigned int		spi_clk_real_freq;
83*2de9b4d3SEmmanuel Vadot 	unsigned int		rx_overflows;
84*2de9b4d3SEmmanuel Vadot 	unsigned int		tx_underflows;
85*2de9b4d3SEmmanuel Vadot 	unsigned int		interrupts;
86*2de9b4d3SEmmanuel Vadot 	unsigned int		stray_ints;
87*2de9b4d3SEmmanuel Vadot 	struct spi_command	*cmd;
88*2de9b4d3SEmmanuel Vadot 	int			tx_bytes;	/* tx_cmd_sz + tx_data_sz */
89*2de9b4d3SEmmanuel Vadot 	int			tx_bytes_sent;
90*2de9b4d3SEmmanuel Vadot 	int			rx_bytes;	/* rx_cmd_sz + rx_data_sz */
91*2de9b4d3SEmmanuel Vadot 	int			rx_bytes_rcvd;
92*2de9b4d3SEmmanuel Vadot 	int			busy;
93*2de9b4d3SEmmanuel Vadot 	int			is_dual;
94*2de9b4d3SEmmanuel Vadot 	int			is_stacked;
95*2de9b4d3SEmmanuel Vadot 	int			is_dio;
96*2de9b4d3SEmmanuel Vadot };
97*2de9b4d3SEmmanuel Vadot 
98*2de9b4d3SEmmanuel Vadot #define ZY7_QSPI_DEFAULT_SPI_CLOCK	50000000
99*2de9b4d3SEmmanuel Vadot 
100*2de9b4d3SEmmanuel Vadot #define QSPI_SC_LOCK(sc)		mtx_lock(&(sc)->sc_mtx)
101*2de9b4d3SEmmanuel Vadot #define	QSPI_SC_UNLOCK(sc)		mtx_unlock(&(sc)->sc_mtx)
102*2de9b4d3SEmmanuel Vadot #define QSPI_SC_LOCK_INIT(sc) \
103*2de9b4d3SEmmanuel Vadot 	mtx_init(&(sc)->sc_mtx, device_get_nameunit((sc)->dev),	NULL, MTX_DEF)
104*2de9b4d3SEmmanuel Vadot #define QSPI_SC_LOCK_DESTROY(sc)	mtx_destroy(&(sc)->sc_mtx)
105*2de9b4d3SEmmanuel Vadot #define QSPI_SC_ASSERT_LOCKED(sc)	mtx_assert(&(sc)->sc_mtx, MA_OWNED)
106*2de9b4d3SEmmanuel Vadot 
107*2de9b4d3SEmmanuel Vadot #define RD4(sc, off)		(bus_read_4((sc)->mem_res, (off)))
108*2de9b4d3SEmmanuel Vadot #define WR4(sc, off, val)	(bus_write_4((sc)->mem_res, (off), (val)))
109*2de9b4d3SEmmanuel Vadot 
110*2de9b4d3SEmmanuel Vadot /*
111*2de9b4d3SEmmanuel Vadot  * QSPI device registers.
112*2de9b4d3SEmmanuel Vadot  * Reference: Zynq-7000 All Programmable SoC Technical Reference Manual.
113*2de9b4d3SEmmanuel Vadot  * (v1.12.2) July 1, 2018.  Xilinx doc UG585.
114*2de9b4d3SEmmanuel Vadot  */
115*2de9b4d3SEmmanuel Vadot #define ZY7_QSPI_CONFIG_REG		0x0000
116*2de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_CONFIG_IFMODE		(1U << 31)
117*2de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_CONFIG_ENDIAN		(1 << 26)
118*2de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_CONFIG_HOLDB_DR		(1 << 19)
119*2de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_CONFIG_RSVD1			(1 << 17) /* must be 1 */
120*2de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_CONFIG_MANSTRT		(1 << 16)
121*2de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_CONFIG_MANSTRTEN		(1 << 15)
122*2de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_CONFIG_SSFORCE		(1 << 14)
123*2de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_CONFIG_PCS			(1 << 10)
124*2de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_CONFIG_REF_CLK		(1 << 8)
125*2de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_CONFIG_FIFO_WIDTH_MASK	(3 << 6)
126*2de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_CONFIG_FIFO_WIDTH32		(3 << 6)
127*2de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_CONFIG_BAUD_RATE_DIV_MASK	(7 << 3)
128*2de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_CONFIG_BAUD_RATE_DIV_SHIFT	3
129*2de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_CONFIG_BAUD_RATE_DIV(x)	((x) << 3) /* divide by 2<<x */
130*2de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_CONFIG_CLK_PH		(1 << 2)   /* clock phase */
131*2de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_CONFIG_CLK_POL		(1 << 1)   /* clock polarity */
132*2de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_CONFIG_MODE_SEL		(1 << 0)   /* master enable */
133*2de9b4d3SEmmanuel Vadot 
134*2de9b4d3SEmmanuel Vadot #define ZY7_QSPI_INTR_STAT_REG		0x0004
135*2de9b4d3SEmmanuel Vadot #define ZY7_QSPI_INTR_EN_REG		0x0008
136*2de9b4d3SEmmanuel Vadot #define ZY7_QSPI_INTR_DIS_REG		0x000c
137*2de9b4d3SEmmanuel Vadot #define ZY7_QSPI_INTR_MASK_REG		0x0010
138*2de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_INTR_TX_FIFO_UNDERFLOW	(1 << 6)
139*2de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_INTR_RX_FIFO_FULL		(1 << 5)
140*2de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_INTR_RX_FIFO_NOT_EMPTY	(1 << 4)
141*2de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_INTR_TX_FIFO_FULL		(1 << 3)
142*2de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_INTR_TX_FIFO_NOT_FULL	(1 << 2)
143*2de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_INTR_RX_OVERFLOW		(1 << 0)
144*2de9b4d3SEmmanuel Vadot 
145*2de9b4d3SEmmanuel Vadot #define ZY7_QSPI_EN_REG			0x0014
146*2de9b4d3SEmmanuel Vadot #define   ZY7_SPI_ENABLE			1
147*2de9b4d3SEmmanuel Vadot 
148*2de9b4d3SEmmanuel Vadot #define ZY7_QSPI_DELAY_REG		0x0018
149*2de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_DELAY_NSS_MASK		(0xffU << 24)
150*2de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_DELAY_NSS_SHIFT		24
151*2de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_DELAY_NSS(x)			((x) << 24)
152*2de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_DELAY_BTWN_MASK		(0xff << 16)
153*2de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_DELAY_BTWN_SHIFT		16
154*2de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_DELAY_BTWN(x)		((x) << 16)
155*2de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_DELAY_AFTER_MASK		(0xff << 8)
156*2de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_DELAY_AFTER_SHIFT		8
157*2de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_DELAY_AFTER(x)		((x) << 8)
158*2de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_DELAY_INIT_MASK		0xff
159*2de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_DELAY_INIT_SHIFT		0
160*2de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_DELAY_INIT(x)		(x)
161*2de9b4d3SEmmanuel Vadot 
162*2de9b4d3SEmmanuel Vadot #define ZY7_QSPI_TXD0_REG		0x001c
163*2de9b4d3SEmmanuel Vadot #define ZY7_QSPI_RX_DATA_REG		0x0020
164*2de9b4d3SEmmanuel Vadot 
165*2de9b4d3SEmmanuel Vadot #define ZY7_QSPI_SLV_IDLE_CT_REG	0x0024
166*2de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_SLV_IDLE_CT_MASK		0xff
167*2de9b4d3SEmmanuel Vadot 
168*2de9b4d3SEmmanuel Vadot #define ZY7_QSPI_TX_THRESH_REG		0x0028
169*2de9b4d3SEmmanuel Vadot #define ZY7_QSPI_RX_THRESH_REG		0x002c
170*2de9b4d3SEmmanuel Vadot 
171*2de9b4d3SEmmanuel Vadot #define ZY7_QSPI_GPIO_REG		0x0030
172*2de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_GPIO_WP_N			1
173*2de9b4d3SEmmanuel Vadot 
174*2de9b4d3SEmmanuel Vadot #define ZY7_QSPI_LPBK_DLY_ADJ_REG	0x0038
175*2de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_LPBK_DLY_ADJ_LPBK_SEL	(1 << 8)
176*2de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_LPBK_DLY_ADJ_LPBK_PH		(1 << 7)
177*2de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_LPBK_DLY_ADJ_USE_LPBK	(1 << 5)
178*2de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_LPBK_DLY_ADJ_DLY1_MASK	(3 << 3)
179*2de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_LPBK_DLY_ADJ_DLY1_SHIFT	3
180*2de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_LPBK_DLY_ADJ_DLY1(x)		((x) << 3)
181*2de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_LPBK_DLY_ADJ_DLY0_MASK	7
182*2de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_LPBK_DLY_ADJ_DLY0_SHIFT	0
183*2de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_LPBK_DLY_ADJ_DLY0(x)		(x)
184*2de9b4d3SEmmanuel Vadot 
185*2de9b4d3SEmmanuel Vadot #define ZY7_QSPI_TXD1_REG		0x0080
186*2de9b4d3SEmmanuel Vadot #define ZY7_QSPI_TXD2_REG		0x0084
187*2de9b4d3SEmmanuel Vadot #define ZY7_QSPI_TXD3_REG		0x0088
188*2de9b4d3SEmmanuel Vadot 
189*2de9b4d3SEmmanuel Vadot #define ZY7_QSPI_LQSPI_CFG_REG		0x00a0
190*2de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_LQSPI_CFG_LINEAR		(1U << 31)
191*2de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_LQSPI_CFG_TWO_MEM		(1 << 30)
192*2de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_LQSPI_CFG_SEP_BUS		(1 << 29)
193*2de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_LQSPI_CFG_U_PAGE		(1 << 28)
194*2de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_LQSPI_CFG_MODE_EN		(1 << 25)
195*2de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_LQSPI_CFG_MODE_ON		(1 << 24)
196*2de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_LQSPI_CFG_MODE_BITS_MASK	(0xff << 16)
197*2de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_LQSPI_CFG_MODE_BITS_SHIFT	16
198*2de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_LQSPI_CFG_MODE_BITS(x)	((x) << 16)
199*2de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_LQSPI_CFG_DUMMY_BYTES_MASK	(7 << 8)
200*2de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_LQSPI_CFG_DUMMY_BYTES_SHIFT	8
201*2de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_LQSPI_CFG_DUMMY_BYTES(x)	((x) << 8)
202*2de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_LQSPI_CFG_INST_CODE_MASK	0xff
203*2de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_LQSPI_CFG_INST_CODE_SHIFT	0
204*2de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_LQSPI_CFG_INST_CODE(x)	(x)
205*2de9b4d3SEmmanuel Vadot 
206*2de9b4d3SEmmanuel Vadot #define ZY7_QSPI_LQSPI_STS_REG		0x00a4
207*2de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_LQSPI_STS_D_FSM_ERR		(1 << 2)
208*2de9b4d3SEmmanuel Vadot #define   ZY7_QSPI_LQSPI_STS_WR_RECVD		(1 << 1)
209*2de9b4d3SEmmanuel Vadot 
210*2de9b4d3SEmmanuel Vadot #define ZY7_QSPI_MOD_ID_REG		0x00fc
211*2de9b4d3SEmmanuel Vadot 
212*2de9b4d3SEmmanuel Vadot static int zy7_qspi_detach(device_t);
213*2de9b4d3SEmmanuel Vadot 
214*2de9b4d3SEmmanuel Vadot /* Fill hardware fifo with command and data bytes. */
215*2de9b4d3SEmmanuel Vadot static void
216*2de9b4d3SEmmanuel Vadot zy7_qspi_write_fifo(struct zy7_qspi_softc *sc, int nbytes)
217*2de9b4d3SEmmanuel Vadot {
218*2de9b4d3SEmmanuel Vadot 	int n, nvalid;
219*2de9b4d3SEmmanuel Vadot 	uint32_t data;
220*2de9b4d3SEmmanuel Vadot 
221*2de9b4d3SEmmanuel Vadot 	while (nbytes > 0) {
222*2de9b4d3SEmmanuel Vadot 		nvalid = MIN(4, nbytes);
223*2de9b4d3SEmmanuel Vadot 		data = 0xffffffff;
224*2de9b4d3SEmmanuel Vadot 
225*2de9b4d3SEmmanuel Vadot 		/*
226*2de9b4d3SEmmanuel Vadot 		 * A hardware bug forces us to wait until the tx fifo is
227*2de9b4d3SEmmanuel Vadot 		 * empty before writing partial words.  We'll come back
228*2de9b4d3SEmmanuel Vadot 		 * next tx interrupt.
229*2de9b4d3SEmmanuel Vadot 		 */
230*2de9b4d3SEmmanuel Vadot 		if (nvalid < 4 && (RD4(sc, ZY7_QSPI_INTR_STAT_REG) &
231*2de9b4d3SEmmanuel Vadot 		    ZY7_QSPI_INTR_TX_FIFO_NOT_FULL) == 0)
232*2de9b4d3SEmmanuel Vadot 			return;
233*2de9b4d3SEmmanuel Vadot 
234*2de9b4d3SEmmanuel Vadot 		if (sc->tx_bytes_sent < sc->cmd->tx_cmd_sz) {
235*2de9b4d3SEmmanuel Vadot 			/* Writing command. */
236*2de9b4d3SEmmanuel Vadot 			n = MIN(nvalid, sc->cmd->tx_cmd_sz -
237*2de9b4d3SEmmanuel Vadot 			    sc->tx_bytes_sent);
238*2de9b4d3SEmmanuel Vadot 			memcpy(&data, (uint8_t *)sc->cmd->tx_cmd +
239*2de9b4d3SEmmanuel Vadot 			    sc->tx_bytes_sent, n);
240*2de9b4d3SEmmanuel Vadot 
241*2de9b4d3SEmmanuel Vadot 			if (nvalid > n) {
242*2de9b4d3SEmmanuel Vadot 				/* Writing start of data. */
243*2de9b4d3SEmmanuel Vadot 				memcpy((uint8_t *)&data + n,
244*2de9b4d3SEmmanuel Vadot 				    sc->cmd->tx_data, nvalid - n);
245*2de9b4d3SEmmanuel Vadot 			}
246*2de9b4d3SEmmanuel Vadot 		} else
247*2de9b4d3SEmmanuel Vadot 			/* Writing data. */
248*2de9b4d3SEmmanuel Vadot 			memcpy(&data, (uint8_t *)sc->cmd->tx_data +
249*2de9b4d3SEmmanuel Vadot 			    (sc->tx_bytes_sent - sc->cmd->tx_cmd_sz), nvalid);
250*2de9b4d3SEmmanuel Vadot 
251*2de9b4d3SEmmanuel Vadot 		switch (nvalid) {
252*2de9b4d3SEmmanuel Vadot 		case 1:
253*2de9b4d3SEmmanuel Vadot 			WR4(sc, ZY7_QSPI_TXD1_REG, data);
254*2de9b4d3SEmmanuel Vadot 			break;
255*2de9b4d3SEmmanuel Vadot 		case 2:
256*2de9b4d3SEmmanuel Vadot 			WR4(sc, ZY7_QSPI_TXD2_REG, data);
257*2de9b4d3SEmmanuel Vadot 			break;
258*2de9b4d3SEmmanuel Vadot 		case 3:
259*2de9b4d3SEmmanuel Vadot 			WR4(sc, ZY7_QSPI_TXD3_REG, data);
260*2de9b4d3SEmmanuel Vadot 			break;
261*2de9b4d3SEmmanuel Vadot 		case 4:
262*2de9b4d3SEmmanuel Vadot 			WR4(sc, ZY7_QSPI_TXD0_REG, data);
263*2de9b4d3SEmmanuel Vadot 			break;
264*2de9b4d3SEmmanuel Vadot 		}
265*2de9b4d3SEmmanuel Vadot 
266*2de9b4d3SEmmanuel Vadot 		sc->tx_bytes_sent += nvalid;
267*2de9b4d3SEmmanuel Vadot 		nbytes -= nvalid;
268*2de9b4d3SEmmanuel Vadot 	}
269*2de9b4d3SEmmanuel Vadot }
270*2de9b4d3SEmmanuel Vadot 
271*2de9b4d3SEmmanuel Vadot 
272*2de9b4d3SEmmanuel Vadot /* Read hardware fifo data into command response and data buffers. */
273*2de9b4d3SEmmanuel Vadot static void
274*2de9b4d3SEmmanuel Vadot zy7_qspi_read_fifo(struct zy7_qspi_softc *sc)
275*2de9b4d3SEmmanuel Vadot {
276*2de9b4d3SEmmanuel Vadot 	int n, nbytes;
277*2de9b4d3SEmmanuel Vadot 	uint32_t data;
278*2de9b4d3SEmmanuel Vadot 
279*2de9b4d3SEmmanuel Vadot 	do {
280*2de9b4d3SEmmanuel Vadot 		data = RD4(sc, ZY7_QSPI_RX_DATA_REG);
281*2de9b4d3SEmmanuel Vadot 		nbytes = MIN(4, sc->rx_bytes - sc->rx_bytes_rcvd);
282*2de9b4d3SEmmanuel Vadot 
283*2de9b4d3SEmmanuel Vadot 		/*
284*2de9b4d3SEmmanuel Vadot 		 * Last word in non-word-multiple transfer is packed
285*2de9b4d3SEmmanuel Vadot 		 * non-intuitively.
286*2de9b4d3SEmmanuel Vadot 		 */
287*2de9b4d3SEmmanuel Vadot 		if (nbytes < 4)
288*2de9b4d3SEmmanuel Vadot 			data >>= 8 * (4 - nbytes);
289*2de9b4d3SEmmanuel Vadot 
290*2de9b4d3SEmmanuel Vadot 		if (sc->rx_bytes_rcvd < sc->cmd->rx_cmd_sz) {
291*2de9b4d3SEmmanuel Vadot 			/* Reading command. */
292*2de9b4d3SEmmanuel Vadot 			n = MIN(nbytes, sc->cmd->rx_cmd_sz -
293*2de9b4d3SEmmanuel Vadot 			    sc->rx_bytes_rcvd);
294*2de9b4d3SEmmanuel Vadot 			memcpy((uint8_t *)sc->cmd->rx_cmd + sc->rx_bytes_rcvd,
295*2de9b4d3SEmmanuel Vadot 			    &data, n);
296*2de9b4d3SEmmanuel Vadot 			sc->rx_bytes_rcvd += n;
297*2de9b4d3SEmmanuel Vadot 			nbytes -= n;
298*2de9b4d3SEmmanuel Vadot 			data >>= 8 * n;
299*2de9b4d3SEmmanuel Vadot 		}
300*2de9b4d3SEmmanuel Vadot 
301*2de9b4d3SEmmanuel Vadot 		if (nbytes > 0) {
302*2de9b4d3SEmmanuel Vadot 			/* Reading data. */
303*2de9b4d3SEmmanuel Vadot 			memcpy((uint8_t *)sc->cmd->rx_data +
304*2de9b4d3SEmmanuel Vadot 			    (sc->rx_bytes_rcvd - sc->cmd->rx_cmd_sz),
305*2de9b4d3SEmmanuel Vadot 			    &data, nbytes);
306*2de9b4d3SEmmanuel Vadot 			sc->rx_bytes_rcvd += nbytes;
307*2de9b4d3SEmmanuel Vadot 		}
308*2de9b4d3SEmmanuel Vadot 
309*2de9b4d3SEmmanuel Vadot 	} while (sc->rx_bytes_rcvd < sc->rx_bytes &&
310*2de9b4d3SEmmanuel Vadot 		 (RD4(sc, ZY7_QSPI_INTR_STAT_REG) &
311*2de9b4d3SEmmanuel Vadot 		  ZY7_QSPI_INTR_RX_FIFO_NOT_EMPTY) != 0);
312*2de9b4d3SEmmanuel Vadot }
313*2de9b4d3SEmmanuel Vadot 
314*2de9b4d3SEmmanuel Vadot /* End a transfer early by draining rx fifo and disabling interrupts. */
315*2de9b4d3SEmmanuel Vadot static void
316*2de9b4d3SEmmanuel Vadot zy7_qspi_abort_transfer(struct zy7_qspi_softc *sc)
317*2de9b4d3SEmmanuel Vadot {
318*2de9b4d3SEmmanuel Vadot 	/* Drain receive fifo. */
319*2de9b4d3SEmmanuel Vadot 	while ((RD4(sc, ZY7_QSPI_INTR_STAT_REG) &
320*2de9b4d3SEmmanuel Vadot 		ZY7_QSPI_INTR_RX_FIFO_NOT_EMPTY) != 0)
321*2de9b4d3SEmmanuel Vadot 		(void)RD4(sc, ZY7_QSPI_RX_DATA_REG);
322*2de9b4d3SEmmanuel Vadot 
323*2de9b4d3SEmmanuel Vadot 	/* Shut down interrupts. */
324*2de9b4d3SEmmanuel Vadot 	WR4(sc, ZY7_QSPI_INTR_DIS_REG,
325*2de9b4d3SEmmanuel Vadot 	    ZY7_QSPI_INTR_RX_OVERFLOW |
326*2de9b4d3SEmmanuel Vadot 	    ZY7_QSPI_INTR_RX_FIFO_NOT_EMPTY |
327*2de9b4d3SEmmanuel Vadot 	    ZY7_QSPI_INTR_TX_FIFO_NOT_FULL);
328*2de9b4d3SEmmanuel Vadot }
329*2de9b4d3SEmmanuel Vadot 
330*2de9b4d3SEmmanuel Vadot 
331*2de9b4d3SEmmanuel Vadot static void
332*2de9b4d3SEmmanuel Vadot zy7_qspi_intr(void *arg)
333*2de9b4d3SEmmanuel Vadot {
334*2de9b4d3SEmmanuel Vadot 	struct zy7_qspi_softc *sc = (struct zy7_qspi_softc *)arg;
335*2de9b4d3SEmmanuel Vadot 	uint32_t istatus;
336*2de9b4d3SEmmanuel Vadot 
337*2de9b4d3SEmmanuel Vadot 	QSPI_SC_LOCK(sc);
338*2de9b4d3SEmmanuel Vadot 
339*2de9b4d3SEmmanuel Vadot 	sc->interrupts++;
340*2de9b4d3SEmmanuel Vadot 
341*2de9b4d3SEmmanuel Vadot 	istatus = RD4(sc, ZY7_QSPI_INTR_STAT_REG);
342*2de9b4d3SEmmanuel Vadot 
343*2de9b4d3SEmmanuel Vadot 	/* Stray interrupts can happen if a transfer gets interrupted. */
344*2de9b4d3SEmmanuel Vadot 	if (!sc->busy) {
345*2de9b4d3SEmmanuel Vadot 		sc->stray_ints++;
346*2de9b4d3SEmmanuel Vadot 		QSPI_SC_UNLOCK(sc);
347*2de9b4d3SEmmanuel Vadot 		return;
348*2de9b4d3SEmmanuel Vadot 	}
349*2de9b4d3SEmmanuel Vadot 
350*2de9b4d3SEmmanuel Vadot 	if ((istatus & ZY7_QSPI_INTR_RX_OVERFLOW) != 0) {
351*2de9b4d3SEmmanuel Vadot 		device_printf(sc->dev, "rx fifo overflow!\n");
352*2de9b4d3SEmmanuel Vadot 		sc->rx_overflows++;
353*2de9b4d3SEmmanuel Vadot 
354*2de9b4d3SEmmanuel Vadot 		/* Clear status bit. */
355*2de9b4d3SEmmanuel Vadot 		WR4(sc, ZY7_QSPI_INTR_STAT_REG,
356*2de9b4d3SEmmanuel Vadot 		    ZY7_QSPI_INTR_RX_OVERFLOW);
357*2de9b4d3SEmmanuel Vadot 	}
358*2de9b4d3SEmmanuel Vadot 
359*2de9b4d3SEmmanuel Vadot 	/* Empty receive fifo before any more transmit data is sent. */
360*2de9b4d3SEmmanuel Vadot 	if (sc->rx_bytes_rcvd < sc->rx_bytes &&
361*2de9b4d3SEmmanuel Vadot 	    (istatus & ZY7_QSPI_INTR_RX_FIFO_NOT_EMPTY) != 0) {
362*2de9b4d3SEmmanuel Vadot 		zy7_qspi_read_fifo(sc);
363*2de9b4d3SEmmanuel Vadot 		if (sc->rx_bytes_rcvd == sc->rx_bytes)
364*2de9b4d3SEmmanuel Vadot 			/* Disable receive interrupts. */
365*2de9b4d3SEmmanuel Vadot 			WR4(sc, ZY7_QSPI_INTR_DIS_REG,
366*2de9b4d3SEmmanuel Vadot 			    ZY7_QSPI_INTR_RX_FIFO_NOT_EMPTY |
367*2de9b4d3SEmmanuel Vadot 			    ZY7_QSPI_INTR_RX_OVERFLOW);
368*2de9b4d3SEmmanuel Vadot 	}
369*2de9b4d3SEmmanuel Vadot 
370*2de9b4d3SEmmanuel Vadot 	/*
371*2de9b4d3SEmmanuel Vadot 	 * Transmit underflows aren't really a bug because a hardware
372*2de9b4d3SEmmanuel Vadot 	 * bug forces us to allow the tx fifo to go empty between full
373*2de9b4d3SEmmanuel Vadot 	 * and partial fifo writes.  Why bother counting?
374*2de9b4d3SEmmanuel Vadot 	 */
375*2de9b4d3SEmmanuel Vadot 	if ((istatus & ZY7_QSPI_INTR_TX_FIFO_UNDERFLOW) != 0) {
376*2de9b4d3SEmmanuel Vadot 		sc->tx_underflows++;
377*2de9b4d3SEmmanuel Vadot 
378*2de9b4d3SEmmanuel Vadot 		/* Clear status bit. */
379*2de9b4d3SEmmanuel Vadot 		WR4(sc, ZY7_QSPI_INTR_STAT_REG,
380*2de9b4d3SEmmanuel Vadot 		    ZY7_QSPI_INTR_TX_FIFO_UNDERFLOW);
381*2de9b4d3SEmmanuel Vadot 	}
382*2de9b4d3SEmmanuel Vadot 
383*2de9b4d3SEmmanuel Vadot 	/* Fill transmit fifo. */
384*2de9b4d3SEmmanuel Vadot 	if (sc->tx_bytes_sent < sc->tx_bytes &&
385*2de9b4d3SEmmanuel Vadot 	    (istatus & ZY7_QSPI_INTR_TX_FIFO_NOT_FULL) != 0) {
386*2de9b4d3SEmmanuel Vadot 		zy7_qspi_write_fifo(sc, MIN(240, sc->tx_bytes -
387*2de9b4d3SEmmanuel Vadot 			sc->tx_bytes_sent));
388*2de9b4d3SEmmanuel Vadot 
389*2de9b4d3SEmmanuel Vadot 		if (sc->tx_bytes_sent == sc->tx_bytes) {
390*2de9b4d3SEmmanuel Vadot 			/*
391*2de9b4d3SEmmanuel Vadot 			 * Disable transmit FIFO interrupt, enable receive
392*2de9b4d3SEmmanuel Vadot 			 * FIFO interrupt.
393*2de9b4d3SEmmanuel Vadot 			 */
394*2de9b4d3SEmmanuel Vadot 			WR4(sc, ZY7_QSPI_INTR_DIS_REG,
395*2de9b4d3SEmmanuel Vadot 			    ZY7_QSPI_INTR_TX_FIFO_NOT_FULL);
396*2de9b4d3SEmmanuel Vadot 			WR4(sc, ZY7_QSPI_INTR_EN_REG,
397*2de9b4d3SEmmanuel Vadot 			    ZY7_QSPI_INTR_RX_FIFO_NOT_EMPTY);
398*2de9b4d3SEmmanuel Vadot 		}
399*2de9b4d3SEmmanuel Vadot 	}
400*2de9b4d3SEmmanuel Vadot 
401*2de9b4d3SEmmanuel Vadot 	/* Finished with transfer? */
402*2de9b4d3SEmmanuel Vadot 	if (sc->tx_bytes_sent == sc->tx_bytes &&
403*2de9b4d3SEmmanuel Vadot 	    sc->rx_bytes_rcvd == sc->rx_bytes) {
404*2de9b4d3SEmmanuel Vadot 
405*2de9b4d3SEmmanuel Vadot 		/* De-assert CS. */
406*2de9b4d3SEmmanuel Vadot 		sc->cfg_reg_shadow |= ZY7_QSPI_CONFIG_PCS;
407*2de9b4d3SEmmanuel Vadot 		WR4(sc, ZY7_QSPI_CONFIG_REG, sc->cfg_reg_shadow);
408*2de9b4d3SEmmanuel Vadot 
409*2de9b4d3SEmmanuel Vadot 		wakeup(sc->dev);
410*2de9b4d3SEmmanuel Vadot 	}
411*2de9b4d3SEmmanuel Vadot 
412*2de9b4d3SEmmanuel Vadot 	QSPI_SC_UNLOCK(sc);
413*2de9b4d3SEmmanuel Vadot }
414*2de9b4d3SEmmanuel Vadot 
415*2de9b4d3SEmmanuel Vadot /* Initialize hardware. */
416*2de9b4d3SEmmanuel Vadot static int
417*2de9b4d3SEmmanuel Vadot zy7_qspi_init_hw(struct zy7_qspi_softc *sc)
418*2de9b4d3SEmmanuel Vadot {
419*2de9b4d3SEmmanuel Vadot 	uint32_t baud_div;
420*2de9b4d3SEmmanuel Vadot 
421*2de9b4d3SEmmanuel Vadot 	/* Configure LQSPI Config register.  Disable linear mode. */
422*2de9b4d3SEmmanuel Vadot 	sc->lqspi_cfg_shadow = RD4(sc, ZY7_QSPI_LQSPI_CFG_REG);
423*2de9b4d3SEmmanuel Vadot 	sc->lqspi_cfg_shadow &= ~(ZY7_QSPI_LQSPI_CFG_LINEAR |
424*2de9b4d3SEmmanuel Vadot 				  ZY7_QSPI_LQSPI_CFG_TWO_MEM |
425*2de9b4d3SEmmanuel Vadot 				  ZY7_QSPI_LQSPI_CFG_SEP_BUS);
426*2de9b4d3SEmmanuel Vadot 	if (sc->is_dual) {
427*2de9b4d3SEmmanuel Vadot 		sc->lqspi_cfg_shadow |= ZY7_QSPI_LQSPI_CFG_TWO_MEM;
428*2de9b4d3SEmmanuel Vadot 		if (sc->is_stacked) {
429*2de9b4d3SEmmanuel Vadot 			sc->lqspi_cfg_shadow &=
430*2de9b4d3SEmmanuel Vadot 			    ~ZY7_QSPI_LQSPI_CFG_INST_CODE_MASK;
431*2de9b4d3SEmmanuel Vadot 			sc->lqspi_cfg_shadow |=
432*2de9b4d3SEmmanuel Vadot 			    ZY7_QSPI_LQSPI_CFG_INST_CODE(sc->is_dio ?
433*2de9b4d3SEmmanuel Vadot 				CMD_READ_DUAL_IO : CMD_READ_QUAD_OUTPUT);
434*2de9b4d3SEmmanuel Vadot 		} else
435*2de9b4d3SEmmanuel Vadot 			sc->lqspi_cfg_shadow |= ZY7_QSPI_LQSPI_CFG_SEP_BUS;
436*2de9b4d3SEmmanuel Vadot 	}
437*2de9b4d3SEmmanuel Vadot 	WR4(sc, ZY7_QSPI_LQSPI_CFG_REG, sc->lqspi_cfg_shadow);
438*2de9b4d3SEmmanuel Vadot 
439*2de9b4d3SEmmanuel Vadot 	/* Find best clock divider. */
440*2de9b4d3SEmmanuel Vadot 	baud_div = 0;
441*2de9b4d3SEmmanuel Vadot 	while ((sc->ref_clock >> (baud_div + 1)) > sc->spi_clock &&
442*2de9b4d3SEmmanuel Vadot 	       baud_div < 8)
443*2de9b4d3SEmmanuel Vadot 		baud_div++;
444*2de9b4d3SEmmanuel Vadot 	if (baud_div >= 8) {
445*2de9b4d3SEmmanuel Vadot 		device_printf(sc->dev, "cannot configure clock divider: ref=%d"
446*2de9b4d3SEmmanuel Vadot 		    " spi=%d.\n", sc->ref_clock, sc->spi_clock);
447*2de9b4d3SEmmanuel Vadot 		return (EINVAL);
448*2de9b4d3SEmmanuel Vadot 	}
449*2de9b4d3SEmmanuel Vadot 	sc->spi_clk_real_freq = sc->ref_clock >> (baud_div + 1);
450*2de9b4d3SEmmanuel Vadot 
451*2de9b4d3SEmmanuel Vadot 	/*
452*2de9b4d3SEmmanuel Vadot 	 * If divider is 2 (the max speed), use internal loopback master
453*2de9b4d3SEmmanuel Vadot 	 * clock for read data.  (See section 12.3.1 in ref man.)
454*2de9b4d3SEmmanuel Vadot 	 */
455*2de9b4d3SEmmanuel Vadot 	if (baud_div == 0)
456*2de9b4d3SEmmanuel Vadot 		WR4(sc, ZY7_QSPI_LPBK_DLY_ADJ_REG,
457*2de9b4d3SEmmanuel Vadot 		    ZY7_QSPI_LPBK_DLY_ADJ_USE_LPBK |
458*2de9b4d3SEmmanuel Vadot 		    ZY7_QSPI_LPBK_DLY_ADJ_DLY1(0) |
459*2de9b4d3SEmmanuel Vadot 		    ZY7_QSPI_LPBK_DLY_ADJ_DLY0(0));
460*2de9b4d3SEmmanuel Vadot 	else
461*2de9b4d3SEmmanuel Vadot 		WR4(sc, ZY7_QSPI_LPBK_DLY_ADJ_REG, 0);
462*2de9b4d3SEmmanuel Vadot 
463*2de9b4d3SEmmanuel Vadot 	/* Set up configuration register. */
464*2de9b4d3SEmmanuel Vadot 	sc->cfg_reg_shadow =
465*2de9b4d3SEmmanuel Vadot 		ZY7_QSPI_CONFIG_IFMODE |
466*2de9b4d3SEmmanuel Vadot 		ZY7_QSPI_CONFIG_HOLDB_DR |
467*2de9b4d3SEmmanuel Vadot 		ZY7_QSPI_CONFIG_RSVD1 |
468*2de9b4d3SEmmanuel Vadot 		ZY7_QSPI_CONFIG_SSFORCE |
469*2de9b4d3SEmmanuel Vadot 		ZY7_QSPI_CONFIG_PCS |
470*2de9b4d3SEmmanuel Vadot 		ZY7_QSPI_CONFIG_FIFO_WIDTH32 |
471*2de9b4d3SEmmanuel Vadot 		ZY7_QSPI_CONFIG_BAUD_RATE_DIV(baud_div) |
472*2de9b4d3SEmmanuel Vadot 		ZY7_QSPI_CONFIG_MODE_SEL;
473*2de9b4d3SEmmanuel Vadot 	WR4(sc, ZY7_QSPI_CONFIG_REG, sc->cfg_reg_shadow);
474*2de9b4d3SEmmanuel Vadot 
475*2de9b4d3SEmmanuel Vadot 	/*
476*2de9b4d3SEmmanuel Vadot 	 * Set thresholds.  We must use 1 for tx threshold because there
477*2de9b4d3SEmmanuel Vadot 	 * is no fifo empty flag and we need one to implement a bug
478*2de9b4d3SEmmanuel Vadot 	 * workaround.
479*2de9b4d3SEmmanuel Vadot 	 */
480*2de9b4d3SEmmanuel Vadot 	WR4(sc, ZY7_QSPI_TX_THRESH_REG, 1);
481*2de9b4d3SEmmanuel Vadot 	WR4(sc, ZY7_QSPI_RX_THRESH_REG, 1);
482*2de9b4d3SEmmanuel Vadot 
483*2de9b4d3SEmmanuel Vadot 	/* Clear and disable all interrupts. */
484*2de9b4d3SEmmanuel Vadot 	WR4(sc, ZY7_QSPI_INTR_STAT_REG, ~0);
485*2de9b4d3SEmmanuel Vadot 	WR4(sc, ZY7_QSPI_INTR_DIS_REG, ~0);
486*2de9b4d3SEmmanuel Vadot 
487*2de9b4d3SEmmanuel Vadot 	/* Enable SPI. */
488*2de9b4d3SEmmanuel Vadot 	WR4(sc, ZY7_QSPI_EN_REG, ZY7_SPI_ENABLE);
489*2de9b4d3SEmmanuel Vadot 
490*2de9b4d3SEmmanuel Vadot 	return (0);
491*2de9b4d3SEmmanuel Vadot }
492*2de9b4d3SEmmanuel Vadot 
493*2de9b4d3SEmmanuel Vadot 
494*2de9b4d3SEmmanuel Vadot static void
495*2de9b4d3SEmmanuel Vadot zy7_qspi_add_sysctls(device_t dev)
496*2de9b4d3SEmmanuel Vadot {
497*2de9b4d3SEmmanuel Vadot 	struct zy7_qspi_softc *sc = device_get_softc(dev);
498*2de9b4d3SEmmanuel Vadot 	struct sysctl_ctx_list *ctx;
499*2de9b4d3SEmmanuel Vadot 	struct sysctl_oid_list *child;
500*2de9b4d3SEmmanuel Vadot 
501*2de9b4d3SEmmanuel Vadot 	ctx = device_get_sysctl_ctx(dev);
502*2de9b4d3SEmmanuel Vadot 	child = SYSCTL_CHILDREN(device_get_sysctl_tree(dev));
503*2de9b4d3SEmmanuel Vadot 
504*2de9b4d3SEmmanuel Vadot 	SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "spi_clk_real_freq", CTLFLAG_RD,
505*2de9b4d3SEmmanuel Vadot 	    &sc->spi_clk_real_freq, 0, "SPI clock real frequency");
506*2de9b4d3SEmmanuel Vadot 
507*2de9b4d3SEmmanuel Vadot 	SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "rx_overflows", CTLFLAG_RD,
508*2de9b4d3SEmmanuel Vadot 	    &sc->rx_overflows, 0, "RX FIFO overflow events");
509*2de9b4d3SEmmanuel Vadot 
510*2de9b4d3SEmmanuel Vadot 	SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "tx_underflows", CTLFLAG_RD,
511*2de9b4d3SEmmanuel Vadot 	    &sc->tx_underflows, 0, "TX FIFO underflow events");
512*2de9b4d3SEmmanuel Vadot 
513*2de9b4d3SEmmanuel Vadot 	SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "interrupts", CTLFLAG_RD,
514*2de9b4d3SEmmanuel Vadot 	    &sc->interrupts, 0, "interrupt calls");
515*2de9b4d3SEmmanuel Vadot 
516*2de9b4d3SEmmanuel Vadot 	SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "stray_ints", CTLFLAG_RD,
517*2de9b4d3SEmmanuel Vadot 	    &sc->stray_ints, 0, "stray interrupts");
518*2de9b4d3SEmmanuel Vadot }
519*2de9b4d3SEmmanuel Vadot 
520*2de9b4d3SEmmanuel Vadot 
521*2de9b4d3SEmmanuel Vadot static int
522*2de9b4d3SEmmanuel Vadot zy7_qspi_probe(device_t dev)
523*2de9b4d3SEmmanuel Vadot {
524*2de9b4d3SEmmanuel Vadot 
525*2de9b4d3SEmmanuel Vadot 	if (!ofw_bus_status_okay(dev))
526*2de9b4d3SEmmanuel Vadot 		return (ENXIO);
527*2de9b4d3SEmmanuel Vadot 
528*2de9b4d3SEmmanuel Vadot 	if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
529*2de9b4d3SEmmanuel Vadot 		return (ENXIO);
530*2de9b4d3SEmmanuel Vadot 
531*2de9b4d3SEmmanuel Vadot 	device_set_desc(dev, "Zynq Quad-SPI Flash Controller");
532*2de9b4d3SEmmanuel Vadot 
533*2de9b4d3SEmmanuel Vadot 	return (BUS_PROBE_DEFAULT);
534*2de9b4d3SEmmanuel Vadot }
535*2de9b4d3SEmmanuel Vadot 
536*2de9b4d3SEmmanuel Vadot 
537*2de9b4d3SEmmanuel Vadot static int
538*2de9b4d3SEmmanuel Vadot zy7_qspi_attach(device_t dev)
539*2de9b4d3SEmmanuel Vadot {
540*2de9b4d3SEmmanuel Vadot 	struct zy7_qspi_softc *sc;
541*2de9b4d3SEmmanuel Vadot 	int rid, err;
542*2de9b4d3SEmmanuel Vadot 	phandle_t node;
543*2de9b4d3SEmmanuel Vadot 	pcell_t cell;
544*2de9b4d3SEmmanuel Vadot 
545*2de9b4d3SEmmanuel Vadot 	sc = device_get_softc(dev);
546*2de9b4d3SEmmanuel Vadot 	sc->dev = dev;
547*2de9b4d3SEmmanuel Vadot 
548*2de9b4d3SEmmanuel Vadot 	QSPI_SC_LOCK_INIT(sc);
549*2de9b4d3SEmmanuel Vadot 
550*2de9b4d3SEmmanuel Vadot 	/* Get ref-clock, spi-clock, and other properties. */
551*2de9b4d3SEmmanuel Vadot 	node = ofw_bus_get_node(dev);
552*2de9b4d3SEmmanuel Vadot 	if (OF_getprop(node, "ref-clock", &cell, sizeof(cell)) > 0)
553*2de9b4d3SEmmanuel Vadot 		sc->ref_clock = fdt32_to_cpu(cell);
554*2de9b4d3SEmmanuel Vadot 	else {
555*2de9b4d3SEmmanuel Vadot 		device_printf(dev, "must have ref-clock property\n");
556*2de9b4d3SEmmanuel Vadot 		return (ENXIO);
557*2de9b4d3SEmmanuel Vadot 	}
558*2de9b4d3SEmmanuel Vadot 	if (OF_getprop(node, "spi-clock", &cell, sizeof(cell)) > 0)
559*2de9b4d3SEmmanuel Vadot 		sc->spi_clock = fdt32_to_cpu(cell);
560*2de9b4d3SEmmanuel Vadot 	else
561*2de9b4d3SEmmanuel Vadot 		sc->spi_clock = ZY7_QSPI_DEFAULT_SPI_CLOCK;
562*2de9b4d3SEmmanuel Vadot 	if (OF_getprop(node, "is-stacked", &cell, sizeof(cell)) > 0 &&
563*2de9b4d3SEmmanuel Vadot 	    fdt32_to_cpu(cell) != 0) {
564*2de9b4d3SEmmanuel Vadot 		sc->is_dual = 1;
565*2de9b4d3SEmmanuel Vadot 		sc->is_stacked = 1;
566*2de9b4d3SEmmanuel Vadot 	} else if (OF_getprop(node, "is-dual", &cell, sizeof(cell)) > 0 &&
567*2de9b4d3SEmmanuel Vadot 		   fdt32_to_cpu(cell) != 0)
568*2de9b4d3SEmmanuel Vadot 		sc->is_dual = 1;
569*2de9b4d3SEmmanuel Vadot 	if (OF_getprop(node, "is-dio", &cell, sizeof(cell)) > 0 &&
570*2de9b4d3SEmmanuel Vadot 	    fdt32_to_cpu(cell) != 0)
571*2de9b4d3SEmmanuel Vadot 		sc->is_dio = 1;
572*2de9b4d3SEmmanuel Vadot 
573*2de9b4d3SEmmanuel Vadot 	/* Get memory resource. */
574*2de9b4d3SEmmanuel Vadot 	rid = 0;
575*2de9b4d3SEmmanuel Vadot 	sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
576*2de9b4d3SEmmanuel Vadot 	    RF_ACTIVE);
577*2de9b4d3SEmmanuel Vadot 	if (sc->mem_res == NULL) {
578*2de9b4d3SEmmanuel Vadot 		device_printf(dev, "could not allocate memory resources.\n");
579*2de9b4d3SEmmanuel Vadot 		zy7_qspi_detach(dev);
580*2de9b4d3SEmmanuel Vadot 		return (ENOMEM);
581*2de9b4d3SEmmanuel Vadot 	}
582*2de9b4d3SEmmanuel Vadot 
583*2de9b4d3SEmmanuel Vadot 	/* Allocate IRQ. */
584*2de9b4d3SEmmanuel Vadot 	rid = 0;
585*2de9b4d3SEmmanuel Vadot 	sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
586*2de9b4d3SEmmanuel Vadot 	    RF_ACTIVE);
587*2de9b4d3SEmmanuel Vadot 	if (sc->irq_res == NULL) {
588*2de9b4d3SEmmanuel Vadot 		device_printf(dev, "could not allocate IRQ resource.\n");
589*2de9b4d3SEmmanuel Vadot 		zy7_qspi_detach(dev);
590*2de9b4d3SEmmanuel Vadot 		return (ENOMEM);
591*2de9b4d3SEmmanuel Vadot 	}
592*2de9b4d3SEmmanuel Vadot 
593*2de9b4d3SEmmanuel Vadot 	/* Activate the interrupt. */
594*2de9b4d3SEmmanuel Vadot 	err = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC | INTR_MPSAFE,
595*2de9b4d3SEmmanuel Vadot 	    NULL, zy7_qspi_intr, sc, &sc->intrhandle);
596*2de9b4d3SEmmanuel Vadot 	if (err) {
597*2de9b4d3SEmmanuel Vadot 		device_printf(dev, "could not setup IRQ.\n");
598*2de9b4d3SEmmanuel Vadot 		zy7_qspi_detach(dev);
599*2de9b4d3SEmmanuel Vadot 		return (err);
600*2de9b4d3SEmmanuel Vadot 	}
601*2de9b4d3SEmmanuel Vadot 
602*2de9b4d3SEmmanuel Vadot 	/* Configure the device. */
603*2de9b4d3SEmmanuel Vadot 	err = zy7_qspi_init_hw(sc);
604*2de9b4d3SEmmanuel Vadot 	if (err) {
605*2de9b4d3SEmmanuel Vadot 		zy7_qspi_detach(dev);
606*2de9b4d3SEmmanuel Vadot 		return (err);
607*2de9b4d3SEmmanuel Vadot 	}
608*2de9b4d3SEmmanuel Vadot 
609*2de9b4d3SEmmanuel Vadot 	sc->child = device_add_child(dev, "spibus", -1);
610*2de9b4d3SEmmanuel Vadot 
611*2de9b4d3SEmmanuel Vadot 	zy7_qspi_add_sysctls(dev);
612*2de9b4d3SEmmanuel Vadot 
613*2de9b4d3SEmmanuel Vadot 	/* Attach spibus driver as a child later when interrupts work. */
614*2de9b4d3SEmmanuel Vadot 	config_intrhook_oneshot((ich_func_t)bus_generic_attach, dev);
615*2de9b4d3SEmmanuel Vadot 
616*2de9b4d3SEmmanuel Vadot 	return (0);
617*2de9b4d3SEmmanuel Vadot }
618*2de9b4d3SEmmanuel Vadot 
619*2de9b4d3SEmmanuel Vadot static int
620*2de9b4d3SEmmanuel Vadot zy7_qspi_detach(device_t dev)
621*2de9b4d3SEmmanuel Vadot {
622*2de9b4d3SEmmanuel Vadot 	struct zy7_qspi_softc *sc = device_get_softc(dev);
623*2de9b4d3SEmmanuel Vadot 
624*2de9b4d3SEmmanuel Vadot 	if (device_is_attached(dev))
625*2de9b4d3SEmmanuel Vadot 		bus_generic_detach(dev);
626*2de9b4d3SEmmanuel Vadot 
627*2de9b4d3SEmmanuel Vadot 	/* Delete child bus. */
628*2de9b4d3SEmmanuel Vadot 	if (sc->child)
629*2de9b4d3SEmmanuel Vadot 		device_delete_child(dev, sc->child);
630*2de9b4d3SEmmanuel Vadot 
631*2de9b4d3SEmmanuel Vadot 	/* Disable hardware. */
632*2de9b4d3SEmmanuel Vadot 	if (sc->mem_res != NULL) {
633*2de9b4d3SEmmanuel Vadot 		/* Disable SPI. */
634*2de9b4d3SEmmanuel Vadot 		WR4(sc, ZY7_QSPI_EN_REG, 0);
635*2de9b4d3SEmmanuel Vadot 
636*2de9b4d3SEmmanuel Vadot 		/* Clear and disable all interrupts. */
637*2de9b4d3SEmmanuel Vadot 		WR4(sc, ZY7_QSPI_INTR_STAT_REG, ~0);
638*2de9b4d3SEmmanuel Vadot 		WR4(sc, ZY7_QSPI_INTR_DIS_REG, ~0);
639*2de9b4d3SEmmanuel Vadot 	}
640*2de9b4d3SEmmanuel Vadot 
641*2de9b4d3SEmmanuel Vadot 	/* Teardown and release interrupt. */
642*2de9b4d3SEmmanuel Vadot 	if (sc->irq_res != NULL) {
643*2de9b4d3SEmmanuel Vadot 		if (sc->intrhandle)
644*2de9b4d3SEmmanuel Vadot 			bus_teardown_intr(dev, sc->irq_res, sc->intrhandle);
645*2de9b4d3SEmmanuel Vadot 		bus_release_resource(dev, SYS_RES_IRQ,
646*2de9b4d3SEmmanuel Vadot 		    rman_get_rid(sc->irq_res), sc->irq_res);
647*2de9b4d3SEmmanuel Vadot 	}
648*2de9b4d3SEmmanuel Vadot 
649*2de9b4d3SEmmanuel Vadot 	/* Release memory resource. */
650*2de9b4d3SEmmanuel Vadot 	if (sc->mem_res != NULL)
651*2de9b4d3SEmmanuel Vadot 		bus_release_resource(dev, SYS_RES_MEMORY,
652*2de9b4d3SEmmanuel Vadot 		    rman_get_rid(sc->mem_res), sc->mem_res);
653*2de9b4d3SEmmanuel Vadot 
654*2de9b4d3SEmmanuel Vadot 	QSPI_SC_LOCK_DESTROY(sc);
655*2de9b4d3SEmmanuel Vadot 
656*2de9b4d3SEmmanuel Vadot 	return (0);
657*2de9b4d3SEmmanuel Vadot }
658*2de9b4d3SEmmanuel Vadot 
659*2de9b4d3SEmmanuel Vadot 
660*2de9b4d3SEmmanuel Vadot static phandle_t
661*2de9b4d3SEmmanuel Vadot zy7_qspi_get_node(device_t bus, device_t dev)
662*2de9b4d3SEmmanuel Vadot {
663*2de9b4d3SEmmanuel Vadot 
664*2de9b4d3SEmmanuel Vadot 	return (ofw_bus_get_node(bus));
665*2de9b4d3SEmmanuel Vadot }
666*2de9b4d3SEmmanuel Vadot 
667*2de9b4d3SEmmanuel Vadot 
668*2de9b4d3SEmmanuel Vadot static int
669*2de9b4d3SEmmanuel Vadot zy7_qspi_transfer(device_t dev, device_t child, struct spi_command *cmd)
670*2de9b4d3SEmmanuel Vadot {
671*2de9b4d3SEmmanuel Vadot 	struct zy7_qspi_softc *sc = device_get_softc(dev);
672*2de9b4d3SEmmanuel Vadot 	int err = 0;
673*2de9b4d3SEmmanuel Vadot 
674*2de9b4d3SEmmanuel Vadot 	KASSERT(cmd->tx_cmd_sz == cmd->rx_cmd_sz,
675*2de9b4d3SEmmanuel Vadot 	    ("TX/RX command sizes should be equal"));
676*2de9b4d3SEmmanuel Vadot 	KASSERT(cmd->tx_data_sz == cmd->rx_data_sz,
677*2de9b4d3SEmmanuel Vadot 	    ("TX/RX data sizes should be equal"));
678*2de9b4d3SEmmanuel Vadot 
679*2de9b4d3SEmmanuel Vadot 	if (sc->is_dual && cmd->tx_data_sz % 2 != 0) {
680*2de9b4d3SEmmanuel Vadot 		device_printf(dev, "driver does not support odd byte data "
681*2de9b4d3SEmmanuel Vadot 		    "transfers in dual mode. (sz=%d)\n", cmd->tx_data_sz);
682*2de9b4d3SEmmanuel Vadot 		return (EINVAL);
683*2de9b4d3SEmmanuel Vadot 	}
684*2de9b4d3SEmmanuel Vadot 
685*2de9b4d3SEmmanuel Vadot 	QSPI_SC_LOCK(sc);
686*2de9b4d3SEmmanuel Vadot 
687*2de9b4d3SEmmanuel Vadot 	/* Wait for controller available. */
688*2de9b4d3SEmmanuel Vadot 	while (sc->busy != 0) {
689*2de9b4d3SEmmanuel Vadot 		err = mtx_sleep(dev, &sc->sc_mtx, 0, "zqspi0", 0);
690*2de9b4d3SEmmanuel Vadot 		if (err) {
691*2de9b4d3SEmmanuel Vadot 			QSPI_SC_UNLOCK(sc);
692*2de9b4d3SEmmanuel Vadot 			return (err);
693*2de9b4d3SEmmanuel Vadot 		}
694*2de9b4d3SEmmanuel Vadot 	}
695*2de9b4d3SEmmanuel Vadot 
696*2de9b4d3SEmmanuel Vadot 	/* Start transfer. */
697*2de9b4d3SEmmanuel Vadot 	sc->busy = 1;
698*2de9b4d3SEmmanuel Vadot 	sc->cmd = cmd;
699*2de9b4d3SEmmanuel Vadot 	sc->tx_bytes = sc->cmd->tx_cmd_sz + sc->cmd->tx_data_sz;
700*2de9b4d3SEmmanuel Vadot 	sc->tx_bytes_sent = 0;
701*2de9b4d3SEmmanuel Vadot 	sc->rx_bytes = sc->cmd->rx_cmd_sz + sc->cmd->rx_data_sz;
702*2de9b4d3SEmmanuel Vadot 	sc->rx_bytes_rcvd = 0;
703*2de9b4d3SEmmanuel Vadot 
704*2de9b4d3SEmmanuel Vadot 	/* Enable interrupts.  zy7_qspi_intr() will handle transfer. */
705*2de9b4d3SEmmanuel Vadot 	WR4(sc, ZY7_QSPI_INTR_EN_REG,
706*2de9b4d3SEmmanuel Vadot 	    ZY7_QSPI_INTR_TX_FIFO_NOT_FULL |
707*2de9b4d3SEmmanuel Vadot 	    ZY7_QSPI_INTR_RX_OVERFLOW);
708*2de9b4d3SEmmanuel Vadot 
709*2de9b4d3SEmmanuel Vadot #ifdef SPI_XFER_U_PAGE	/* XXX: future support for stacked memories. */
710*2de9b4d3SEmmanuel Vadot 	if (sc->is_stacked) {
711*2de9b4d3SEmmanuel Vadot 		if ((cmd->flags & SPI_XFER_U_PAGE) != 0)
712*2de9b4d3SEmmanuel Vadot 			sc->lqspi_cfg_shadow |= ZY7_QSPI_LQSPI_CFG_U_PAGE;
713*2de9b4d3SEmmanuel Vadot 		else
714*2de9b4d3SEmmanuel Vadot 			sc->lqspi_cfg_shadow &= ~ZY7_QSPI_LQSPI_CFG_U_PAGE;
715*2de9b4d3SEmmanuel Vadot 		WR4(sc, ZY7_QSPI_LQSPI_CFG_REG, sc->lqspi_cfg_shadow);
716*2de9b4d3SEmmanuel Vadot 	}
717*2de9b4d3SEmmanuel Vadot #endif
718*2de9b4d3SEmmanuel Vadot 
719*2de9b4d3SEmmanuel Vadot 	/* Assert CS. */
720*2de9b4d3SEmmanuel Vadot 	sc->cfg_reg_shadow &= ~ZY7_QSPI_CONFIG_PCS;
721*2de9b4d3SEmmanuel Vadot 	WR4(sc, ZY7_QSPI_CONFIG_REG, sc->cfg_reg_shadow);
722*2de9b4d3SEmmanuel Vadot 
723*2de9b4d3SEmmanuel Vadot 	/* Wait for completion. */
724*2de9b4d3SEmmanuel Vadot 	err = mtx_sleep(dev, &sc->sc_mtx, 0, "zqspi1", hz * 2);
725*2de9b4d3SEmmanuel Vadot 	if (err)
726*2de9b4d3SEmmanuel Vadot 		zy7_qspi_abort_transfer(sc);
727*2de9b4d3SEmmanuel Vadot 
728*2de9b4d3SEmmanuel Vadot 	/* Release controller. */
729*2de9b4d3SEmmanuel Vadot 	sc->busy = 0;
730*2de9b4d3SEmmanuel Vadot 	wakeup_one(dev);
731*2de9b4d3SEmmanuel Vadot 
732*2de9b4d3SEmmanuel Vadot 	QSPI_SC_UNLOCK(sc);
733*2de9b4d3SEmmanuel Vadot 
734*2de9b4d3SEmmanuel Vadot 	return (err);
735*2de9b4d3SEmmanuel Vadot }
736*2de9b4d3SEmmanuel Vadot 
737*2de9b4d3SEmmanuel Vadot static device_method_t zy7_qspi_methods[] = {
738*2de9b4d3SEmmanuel Vadot 	/* Device interface */
739*2de9b4d3SEmmanuel Vadot 	DEVMETHOD(device_probe,		zy7_qspi_probe),
740*2de9b4d3SEmmanuel Vadot 	DEVMETHOD(device_attach,	zy7_qspi_attach),
741*2de9b4d3SEmmanuel Vadot 	DEVMETHOD(device_detach,	zy7_qspi_detach),
742*2de9b4d3SEmmanuel Vadot 
743*2de9b4d3SEmmanuel Vadot 	/* SPI interface */
744*2de9b4d3SEmmanuel Vadot 	DEVMETHOD(spibus_transfer,	zy7_qspi_transfer),
745*2de9b4d3SEmmanuel Vadot 
746*2de9b4d3SEmmanuel Vadot 	/* ofw_bus interface */
747*2de9b4d3SEmmanuel Vadot 	DEVMETHOD(ofw_bus_get_node,	zy7_qspi_get_node),
748*2de9b4d3SEmmanuel Vadot 
749*2de9b4d3SEmmanuel Vadot 	DEVMETHOD_END
750*2de9b4d3SEmmanuel Vadot };
751*2de9b4d3SEmmanuel Vadot 
752*2de9b4d3SEmmanuel Vadot 
753*2de9b4d3SEmmanuel Vadot static driver_t zy7_qspi_driver = {
754*2de9b4d3SEmmanuel Vadot 	"zy7_qspi",
755*2de9b4d3SEmmanuel Vadot 	zy7_qspi_methods,
756*2de9b4d3SEmmanuel Vadot 	sizeof(struct zy7_qspi_softc),
757*2de9b4d3SEmmanuel Vadot };
758*2de9b4d3SEmmanuel Vadot static devclass_t zy7_qspi_devclass;
759*2de9b4d3SEmmanuel Vadot 
760*2de9b4d3SEmmanuel Vadot DRIVER_MODULE(zy7_qspi, simplebus, zy7_qspi_driver, zy7_qspi_devclass, 0, 0);
761*2de9b4d3SEmmanuel Vadot DRIVER_MODULE(ofw_spibus, zy7_qspi, ofw_spibus_driver, ofw_spibus_devclass, 0, 0);
762*2de9b4d3SEmmanuel Vadot SIMPLEBUS_PNP_INFO(compat_data);
763*2de9b4d3SEmmanuel Vadot MODULE_DEPEND(zy7_qspi, ofw_spibus, 1, 1, 1);
764