xref: /freebsd/sys/dev/xilinx/axi_quad_spi.c (revision e72dd8d78f768f0d7f05aa67b4752a1d7be722c2)
1*e72dd8d7SRuslan Bukin /*-
2*e72dd8d7SRuslan Bukin  * Copyright (c) 2016 Ruslan Bukin <br@bsdpad.com>
3*e72dd8d7SRuslan Bukin  * All rights reserved.
4*e72dd8d7SRuslan Bukin  *
5*e72dd8d7SRuslan Bukin  * Portions of this software were developed by SRI International and the
6*e72dd8d7SRuslan Bukin  * University of Cambridge Computer Laboratory under DARPA/AFRL contract
7*e72dd8d7SRuslan Bukin  * FA8750-10-C-0237 ("CTSRD"), as part of the DARPA CRASH research programme.
8*e72dd8d7SRuslan Bukin  *
9*e72dd8d7SRuslan Bukin  * Portions of this software were developed by the University of Cambridge
10*e72dd8d7SRuslan Bukin  * Computer Laboratory as part of the CTSRD Project, with support from the
11*e72dd8d7SRuslan Bukin  * UK Higher Education Innovation Fund (HEIF).
12*e72dd8d7SRuslan Bukin  *
13*e72dd8d7SRuslan Bukin  * Redistribution and use in source and binary forms, with or without
14*e72dd8d7SRuslan Bukin  * modification, are permitted provided that the following conditions
15*e72dd8d7SRuslan Bukin  * are met:
16*e72dd8d7SRuslan Bukin  * 1. Redistributions of source code must retain the above copyright
17*e72dd8d7SRuslan Bukin  *    notice, this list of conditions and the following disclaimer.
18*e72dd8d7SRuslan Bukin  * 2. Redistributions in binary form must reproduce the above copyright
19*e72dd8d7SRuslan Bukin  *    notice, this list of conditions and the following disclaimer in the
20*e72dd8d7SRuslan Bukin  *    documentation and/or other materials provided with the distribution.
21*e72dd8d7SRuslan Bukin  *
22*e72dd8d7SRuslan Bukin  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
23*e72dd8d7SRuslan Bukin  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24*e72dd8d7SRuslan Bukin  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25*e72dd8d7SRuslan Bukin  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
26*e72dd8d7SRuslan Bukin  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27*e72dd8d7SRuslan Bukin  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28*e72dd8d7SRuslan Bukin  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29*e72dd8d7SRuslan Bukin  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30*e72dd8d7SRuslan Bukin  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31*e72dd8d7SRuslan Bukin  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32*e72dd8d7SRuslan Bukin  * SUCH DAMAGE.
33*e72dd8d7SRuslan Bukin  */
34*e72dd8d7SRuslan Bukin 
35*e72dd8d7SRuslan Bukin /*
36*e72dd8d7SRuslan Bukin  * Xilinx AXI_QUAD_SPI
37*e72dd8d7SRuslan Bukin  */
38*e72dd8d7SRuslan Bukin 
39*e72dd8d7SRuslan Bukin #include <sys/cdefs.h>
40*e72dd8d7SRuslan Bukin __FBSDID("$FreeBSD$");
41*e72dd8d7SRuslan Bukin 
42*e72dd8d7SRuslan Bukin #include <sys/param.h>
43*e72dd8d7SRuslan Bukin #include <sys/systm.h>
44*e72dd8d7SRuslan Bukin #include <sys/bus.h>
45*e72dd8d7SRuslan Bukin #include <sys/kernel.h>
46*e72dd8d7SRuslan Bukin #include <sys/module.h>
47*e72dd8d7SRuslan Bukin #include <sys/malloc.h>
48*e72dd8d7SRuslan Bukin #include <sys/rman.h>
49*e72dd8d7SRuslan Bukin #include <sys/timeet.h>
50*e72dd8d7SRuslan Bukin #include <sys/timetc.h>
51*e72dd8d7SRuslan Bukin #include <sys/watchdog.h>
52*e72dd8d7SRuslan Bukin 
53*e72dd8d7SRuslan Bukin #include <dev/spibus/spi.h>
54*e72dd8d7SRuslan Bukin #include <dev/spibus/spibusvar.h>
55*e72dd8d7SRuslan Bukin 
56*e72dd8d7SRuslan Bukin #include "spibus_if.h"
57*e72dd8d7SRuslan Bukin 
58*e72dd8d7SRuslan Bukin #include <dev/fdt/fdt_common.h>
59*e72dd8d7SRuslan Bukin #include <dev/ofw/openfirm.h>
60*e72dd8d7SRuslan Bukin #include <dev/ofw/ofw_bus.h>
61*e72dd8d7SRuslan Bukin #include <dev/ofw/ofw_bus_subr.h>
62*e72dd8d7SRuslan Bukin 
63*e72dd8d7SRuslan Bukin #include <machine/bus.h>
64*e72dd8d7SRuslan Bukin #include <machine/cpu.h>
65*e72dd8d7SRuslan Bukin #include <machine/intr.h>
66*e72dd8d7SRuslan Bukin 
67*e72dd8d7SRuslan Bukin #define	READ4(_sc, _reg)	\
68*e72dd8d7SRuslan Bukin 	bus_space_read_4(_sc->bst, _sc->bsh, _reg)
69*e72dd8d7SRuslan Bukin #define	WRITE4(_sc, _reg, _val)	\
70*e72dd8d7SRuslan Bukin 	bus_space_write_4(_sc->bst, _sc->bsh, _reg, _val)
71*e72dd8d7SRuslan Bukin 
72*e72dd8d7SRuslan Bukin #define	SPI_SRR		0x40		/* Software reset register */
73*e72dd8d7SRuslan Bukin #define	 SRR_RESET	0x0A		/* The only reset value */
74*e72dd8d7SRuslan Bukin #define	SPI_CR		0x60		/* Control register */
75*e72dd8d7SRuslan Bukin #define	 CR_LSB_FIRST	(1 << 9)	/* LSB first */
76*e72dd8d7SRuslan Bukin #define	 CR_MASTER_TI	(1 << 8)	/* Master Transaction Inhibit */
77*e72dd8d7SRuslan Bukin #define	 CR_MSS		(1 << 7)	/* Manual Slave Select */
78*e72dd8d7SRuslan Bukin #define	 CR_RST_RX	(1 << 6)	/* RX FIFO Reset */
79*e72dd8d7SRuslan Bukin #define	 CR_RST_TX	(1 << 5)	/* TX FIFO Reset */
80*e72dd8d7SRuslan Bukin #define	 CR_CPHA	(1 << 4)	/* Clock phase */
81*e72dd8d7SRuslan Bukin #define	 CR_CPOL	(1 << 3)	/* Clock polarity */
82*e72dd8d7SRuslan Bukin #define	 CR_MASTER	(1 << 2)	/* Master (SPI master mode) */
83*e72dd8d7SRuslan Bukin #define	 CR_SPE		(1 << 1)	/* SPI system enable */
84*e72dd8d7SRuslan Bukin #define	 CR_LOOP	(1 << 0)	/* Local loopback mode */
85*e72dd8d7SRuslan Bukin #define	SPI_SR		0x64		/* Status register */
86*e72dd8d7SRuslan Bukin #define	 SR_TX_FULL	(1 << 3)	/* Transmit full */
87*e72dd8d7SRuslan Bukin #define	 SR_TX_EMPTY	(1 << 2)	/* Transmit empty */
88*e72dd8d7SRuslan Bukin #define	 SR_RX_FULL	(1 << 1)	/* Receive full */
89*e72dd8d7SRuslan Bukin #define	 SR_RX_EMPTY	(1 << 0)	/* Receive empty */
90*e72dd8d7SRuslan Bukin #define	SPI_DTR		0x68		/* Data transmit register */
91*e72dd8d7SRuslan Bukin #define	SPI_DRR		0x6C		/* Data receive register */
92*e72dd8d7SRuslan Bukin #define	SPI_SSR		0x70		/* Slave select register */
93*e72dd8d7SRuslan Bukin #define	SPI_TFOR	0x74		/* Transmit FIFO Occupancy Register */
94*e72dd8d7SRuslan Bukin #define	SPI_RFOR	0x78		/* Receive FIFO Occupancy Register */
95*e72dd8d7SRuslan Bukin #define	SPI_DGIER	0x1C		/* Device global interrupt enable register */
96*e72dd8d7SRuslan Bukin #define	SPI_IPISR	0x20		/* IP interrupt status register */
97*e72dd8d7SRuslan Bukin #define	SPI_IPIER	0x28		/* IP interrupt enable register */
98*e72dd8d7SRuslan Bukin 
99*e72dd8d7SRuslan Bukin struct spi_softc {
100*e72dd8d7SRuslan Bukin 	struct resource		*res[1];
101*e72dd8d7SRuslan Bukin 	bus_space_tag_t		bst;
102*e72dd8d7SRuslan Bukin 	bus_space_handle_t	bsh;
103*e72dd8d7SRuslan Bukin 	void			*ih;
104*e72dd8d7SRuslan Bukin };
105*e72dd8d7SRuslan Bukin 
106*e72dd8d7SRuslan Bukin static struct resource_spec spi_spec[] = {
107*e72dd8d7SRuslan Bukin 	{ SYS_RES_MEMORY,	0,	RF_ACTIVE },
108*e72dd8d7SRuslan Bukin 	{ -1, 0 }
109*e72dd8d7SRuslan Bukin };
110*e72dd8d7SRuslan Bukin 
111*e72dd8d7SRuslan Bukin static int
112*e72dd8d7SRuslan Bukin spi_probe(device_t dev)
113*e72dd8d7SRuslan Bukin {
114*e72dd8d7SRuslan Bukin 
115*e72dd8d7SRuslan Bukin 	if (!ofw_bus_status_okay(dev))
116*e72dd8d7SRuslan Bukin 		return (ENXIO);
117*e72dd8d7SRuslan Bukin 
118*e72dd8d7SRuslan Bukin 	if (!ofw_bus_is_compatible(dev, "xlnx,xps-spi-3.2"))
119*e72dd8d7SRuslan Bukin 		return (ENXIO);
120*e72dd8d7SRuslan Bukin 
121*e72dd8d7SRuslan Bukin 	device_set_desc(dev, "Xilinx Quad SPI");
122*e72dd8d7SRuslan Bukin 	return (BUS_PROBE_DEFAULT);
123*e72dd8d7SRuslan Bukin }
124*e72dd8d7SRuslan Bukin 
125*e72dd8d7SRuslan Bukin static int
126*e72dd8d7SRuslan Bukin spi_chip_select(device_t dev, device_t child)
127*e72dd8d7SRuslan Bukin {
128*e72dd8d7SRuslan Bukin 	struct spi_softc *sc;
129*e72dd8d7SRuslan Bukin 	uint32_t cs;
130*e72dd8d7SRuslan Bukin 
131*e72dd8d7SRuslan Bukin 	sc = device_get_softc(dev);
132*e72dd8d7SRuslan Bukin 
133*e72dd8d7SRuslan Bukin 	spibus_get_cs(child, &cs);
134*e72dd8d7SRuslan Bukin 
135*e72dd8d7SRuslan Bukin 	WRITE4(sc, SPI_SSR, ~(1 << cs));
136*e72dd8d7SRuslan Bukin 
137*e72dd8d7SRuslan Bukin 	return (0);
138*e72dd8d7SRuslan Bukin }
139*e72dd8d7SRuslan Bukin 
140*e72dd8d7SRuslan Bukin static int
141*e72dd8d7SRuslan Bukin spi_chip_deselect(device_t dev, device_t child)
142*e72dd8d7SRuslan Bukin {
143*e72dd8d7SRuslan Bukin 	struct spi_softc *sc;
144*e72dd8d7SRuslan Bukin 
145*e72dd8d7SRuslan Bukin 	sc = device_get_softc(dev);
146*e72dd8d7SRuslan Bukin 
147*e72dd8d7SRuslan Bukin 	WRITE4(sc, SPI_SSR, ~0);
148*e72dd8d7SRuslan Bukin 
149*e72dd8d7SRuslan Bukin 	return (0);
150*e72dd8d7SRuslan Bukin }
151*e72dd8d7SRuslan Bukin 
152*e72dd8d7SRuslan Bukin static int
153*e72dd8d7SRuslan Bukin spi_attach(device_t dev)
154*e72dd8d7SRuslan Bukin {
155*e72dd8d7SRuslan Bukin 	struct spi_softc *sc;
156*e72dd8d7SRuslan Bukin 	uint32_t reg;
157*e72dd8d7SRuslan Bukin 
158*e72dd8d7SRuslan Bukin 	sc = device_get_softc(dev);
159*e72dd8d7SRuslan Bukin 
160*e72dd8d7SRuslan Bukin 	if (bus_alloc_resources(dev, spi_spec, sc->res)) {
161*e72dd8d7SRuslan Bukin 		device_printf(dev, "could not allocate resources\n");
162*e72dd8d7SRuslan Bukin 		return (ENXIO);
163*e72dd8d7SRuslan Bukin 	}
164*e72dd8d7SRuslan Bukin 
165*e72dd8d7SRuslan Bukin 	/* Memory interface */
166*e72dd8d7SRuslan Bukin 	sc->bst = rman_get_bustag(sc->res[0]);
167*e72dd8d7SRuslan Bukin 	sc->bsh = rman_get_bushandle(sc->res[0]);
168*e72dd8d7SRuslan Bukin 
169*e72dd8d7SRuslan Bukin 	/* Reset */
170*e72dd8d7SRuslan Bukin 	WRITE4(sc, SPI_SRR, SRR_RESET);
171*e72dd8d7SRuslan Bukin 
172*e72dd8d7SRuslan Bukin 	DELAY(1000);
173*e72dd8d7SRuslan Bukin 
174*e72dd8d7SRuslan Bukin 	reg = (CR_MASTER | CR_MSS | CR_RST_RX | CR_RST_TX);
175*e72dd8d7SRuslan Bukin 	WRITE4(sc, SPI_CR, reg);
176*e72dd8d7SRuslan Bukin 	WRITE4(sc, SPI_DGIER, 0);	/* Disable interrupts */
177*e72dd8d7SRuslan Bukin 
178*e72dd8d7SRuslan Bukin 	reg = (CR_MASTER | CR_MSS | CR_SPE);
179*e72dd8d7SRuslan Bukin 	WRITE4(sc, SPI_CR, reg);
180*e72dd8d7SRuslan Bukin 
181*e72dd8d7SRuslan Bukin 	device_add_child(dev, "spibus", 0);
182*e72dd8d7SRuslan Bukin 	return (bus_generic_attach(dev));
183*e72dd8d7SRuslan Bukin }
184*e72dd8d7SRuslan Bukin 
185*e72dd8d7SRuslan Bukin static int
186*e72dd8d7SRuslan Bukin spi_txrx(struct spi_softc *sc, uint8_t *out_buf,
187*e72dd8d7SRuslan Bukin     uint8_t *in_buf, int bufsz, int cs)
188*e72dd8d7SRuslan Bukin {
189*e72dd8d7SRuslan Bukin 	uint32_t data;
190*e72dd8d7SRuslan Bukin 	uint32_t i;
191*e72dd8d7SRuslan Bukin 
192*e72dd8d7SRuslan Bukin 	for (i = 0; i < bufsz; i++) {
193*e72dd8d7SRuslan Bukin 		WRITE4(sc, SPI_DTR, out_buf[i]);
194*e72dd8d7SRuslan Bukin 
195*e72dd8d7SRuslan Bukin 		while(!(READ4(sc, SPI_SR) & SR_TX_EMPTY))
196*e72dd8d7SRuslan Bukin 			continue;
197*e72dd8d7SRuslan Bukin 
198*e72dd8d7SRuslan Bukin 		data = READ4(sc, SPI_DRR);
199*e72dd8d7SRuslan Bukin 		if (in_buf)
200*e72dd8d7SRuslan Bukin 			in_buf[i] = (data & 0xff);
201*e72dd8d7SRuslan Bukin 	}
202*e72dd8d7SRuslan Bukin 
203*e72dd8d7SRuslan Bukin 	return (0);
204*e72dd8d7SRuslan Bukin }
205*e72dd8d7SRuslan Bukin 
206*e72dd8d7SRuslan Bukin static int
207*e72dd8d7SRuslan Bukin spi_transfer(device_t dev, device_t child, struct spi_command *cmd)
208*e72dd8d7SRuslan Bukin {
209*e72dd8d7SRuslan Bukin 	struct spi_softc *sc;
210*e72dd8d7SRuslan Bukin 	uint32_t cs;
211*e72dd8d7SRuslan Bukin 
212*e72dd8d7SRuslan Bukin 	sc = device_get_softc(dev);
213*e72dd8d7SRuslan Bukin 
214*e72dd8d7SRuslan Bukin 	KASSERT(cmd->tx_cmd_sz == cmd->rx_cmd_sz,
215*e72dd8d7SRuslan Bukin 	    ("%s: TX/RX command sizes should be equal", __func__));
216*e72dd8d7SRuslan Bukin 	KASSERT(cmd->tx_data_sz == cmd->rx_data_sz,
217*e72dd8d7SRuslan Bukin 	    ("%s: TX/RX data sizes should be equal", __func__));
218*e72dd8d7SRuslan Bukin 
219*e72dd8d7SRuslan Bukin 	/* get the proper chip select */
220*e72dd8d7SRuslan Bukin 	spibus_get_cs(child, &cs);
221*e72dd8d7SRuslan Bukin 
222*e72dd8d7SRuslan Bukin 	/* Command */
223*e72dd8d7SRuslan Bukin 	spi_txrx(sc, cmd->tx_cmd, cmd->rx_cmd, cmd->tx_cmd_sz, cs);
224*e72dd8d7SRuslan Bukin 
225*e72dd8d7SRuslan Bukin 	/* Data */
226*e72dd8d7SRuslan Bukin 	spi_txrx(sc, cmd->tx_data, cmd->rx_data, cmd->tx_data_sz, cs);
227*e72dd8d7SRuslan Bukin 
228*e72dd8d7SRuslan Bukin 	return (0);
229*e72dd8d7SRuslan Bukin }
230*e72dd8d7SRuslan Bukin 
231*e72dd8d7SRuslan Bukin static device_method_t spi_methods[] = {
232*e72dd8d7SRuslan Bukin 	/* Device interface */
233*e72dd8d7SRuslan Bukin 	DEVMETHOD(device_probe,		spi_probe),
234*e72dd8d7SRuslan Bukin 	DEVMETHOD(device_attach,	spi_attach),
235*e72dd8d7SRuslan Bukin 
236*e72dd8d7SRuslan Bukin 	/* SPI interface */
237*e72dd8d7SRuslan Bukin 	DEVMETHOD(spibus_transfer,	spi_transfer),
238*e72dd8d7SRuslan Bukin 	DEVMETHOD(spibus_chip_select,	spi_chip_select),
239*e72dd8d7SRuslan Bukin 	DEVMETHOD(spibus_chip_deselect,	spi_chip_deselect),
240*e72dd8d7SRuslan Bukin 
241*e72dd8d7SRuslan Bukin 	DEVMETHOD_END
242*e72dd8d7SRuslan Bukin };
243*e72dd8d7SRuslan Bukin 
244*e72dd8d7SRuslan Bukin static driver_t spi_driver = {
245*e72dd8d7SRuslan Bukin 	"spi",
246*e72dd8d7SRuslan Bukin 	spi_methods,
247*e72dd8d7SRuslan Bukin 	sizeof(struct spi_softc),
248*e72dd8d7SRuslan Bukin };
249*e72dd8d7SRuslan Bukin 
250*e72dd8d7SRuslan Bukin static devclass_t spi_devclass;
251*e72dd8d7SRuslan Bukin 
252*e72dd8d7SRuslan Bukin DRIVER_MODULE(spi, simplebus, spi_driver, spi_devclass, 0, 0);
253