ar71xx_spi.c revision 331722
1/*-
2 * Copyright (c) 2009, Oleksandr Tymoshenko <gonzo@FreeBSD.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice unmodified, this list of conditions, and the following
10 *    disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 */
27
28#include <sys/cdefs.h>
29__FBSDID("$FreeBSD: stable/11/sys/mips/atheros/ar71xx_spi.c 331722 2018-03-29 02:50:57Z eadler $");
30
31#include <sys/param.h>
32#include <sys/systm.h>
33
34#include <sys/bus.h>
35#include <sys/interrupt.h>
36#include <sys/malloc.h>
37#include <sys/kernel.h>
38#include <sys/module.h>
39#include <sys/rman.h>
40
41#include <vm/vm.h>
42#include <vm/pmap.h>
43#include <vm/vm_extern.h>
44
45#include <machine/bus.h>
46#include <machine/cpu.h>
47
48#include <dev/spibus/spi.h>
49#include <dev/spibus/spibusvar.h>
50#include "spibus_if.h"
51
52#include <mips/atheros/ar71xxreg.h>
53
54#undef AR71XX_SPI_DEBUG
55#ifdef AR71XX_SPI_DEBUG
56#define dprintf printf
57#else
58#define dprintf(x, arg...)
59#endif
60
61/*
62 * register space access macros
63 */
64
65#define	SPI_BARRIER_WRITE(sc)		bus_barrier((sc)->sc_mem_res, 0, 0, 	\
66					    BUS_SPACE_BARRIER_WRITE)
67#define	SPI_BARRIER_READ(sc)	bus_barrier((sc)->sc_mem_res, 0, 0, 	\
68					    BUS_SPACE_BARRIER_READ)
69#define	SPI_BARRIER_RW(sc)		bus_barrier((sc)->sc_mem_res, 0, 0, 	\
70					    BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE)
71
72#define SPI_WRITE(sc, reg, val)	do {				\
73		bus_write_4(sc->sc_mem_res, (reg), (val));	\
74	} while (0)
75
76#define SPI_READ(sc, reg)	 bus_read_4(sc->sc_mem_res, (reg))
77
78#define SPI_SET_BITS(sc, reg, bits)	\
79	SPI_WRITE(sc, reg, SPI_READ(sc, (reg)) | (bits))
80
81#define SPI_CLEAR_BITS(sc, reg, bits)	\
82	SPI_WRITE(sc, reg, SPI_READ(sc, (reg)) & ~(bits))
83
84struct ar71xx_spi_softc {
85	device_t		sc_dev;
86	struct resource		*sc_mem_res;
87	uint32_t		sc_reg_ctrl;
88};
89
90static int
91ar71xx_spi_probe(device_t dev)
92{
93	device_set_desc(dev, "AR71XX SPI");
94	return (BUS_PROBE_NOWILDCARD);
95}
96
97static int
98ar71xx_spi_attach(device_t dev)
99{
100	struct ar71xx_spi_softc *sc = device_get_softc(dev);
101	int rid;
102
103	sc->sc_dev = dev;
104        rid = 0;
105	sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
106	    RF_ACTIVE);
107	if (!sc->sc_mem_res) {
108		device_printf(dev, "Could not map memory\n");
109		return (ENXIO);
110	}
111
112	SPI_WRITE(sc, AR71XX_SPI_FS, 1);
113
114	/* Flush out read before reading the control register */
115	SPI_BARRIER_WRITE(sc);
116
117	sc->sc_reg_ctrl  = SPI_READ(sc, AR71XX_SPI_CTRL);
118
119	/*
120	 * XXX TODO: document what the SPI control register does.
121	 */
122	SPI_WRITE(sc, AR71XX_SPI_CTRL, 0x43);
123
124	/*
125	 * Ensure the config register write has gone out before configuring
126	 * the chip select mask.
127	 */
128	SPI_BARRIER_WRITE(sc);
129	SPI_WRITE(sc, AR71XX_SPI_IO_CTRL, SPI_IO_CTRL_CSMASK);
130
131	/*
132	 * .. and ensure the write has gone out before continuing.
133	 */
134	SPI_BARRIER_WRITE(sc);
135
136	device_add_child(dev, "spibus", -1);
137	return (bus_generic_attach(dev));
138}
139
140static void
141ar71xx_spi_chip_activate(struct ar71xx_spi_softc *sc, int cs)
142{
143	uint32_t ioctrl = SPI_IO_CTRL_CSMASK;
144	/*
145	 * Put respective CSx to low
146	 */
147	ioctrl &= ~(SPI_IO_CTRL_CS0 << cs);
148
149	/*
150	 * Make sure any other writes have gone out to the
151	 * device before changing the chip select line;
152	 * then ensure that it has made it out to the device
153	 * before continuing.
154	 */
155	SPI_BARRIER_WRITE(sc);
156	SPI_WRITE(sc, AR71XX_SPI_IO_CTRL, ioctrl);
157	SPI_BARRIER_WRITE(sc);
158}
159
160static void
161ar71xx_spi_chip_deactivate(struct ar71xx_spi_softc *sc, int cs)
162{
163	/*
164	 * Put all CSx to high
165	 */
166	SPI_WRITE(sc, AR71XX_SPI_IO_CTRL, SPI_IO_CTRL_CSMASK);
167}
168
169static uint8_t
170ar71xx_spi_txrx(struct ar71xx_spi_softc *sc, int cs, uint8_t data)
171{
172	int bit;
173	/* CS0 */
174	uint32_t ioctrl = SPI_IO_CTRL_CSMASK;
175	/*
176	 * low-level for selected CS
177	 */
178	ioctrl &= ~(SPI_IO_CTRL_CS0 << cs);
179
180	uint32_t iod, rds;
181	for (bit = 7; bit >=0; bit--) {
182		if (data & (1 << bit))
183			iod = ioctrl | SPI_IO_CTRL_DO;
184		else
185			iod = ioctrl & ~SPI_IO_CTRL_DO;
186		SPI_BARRIER_WRITE(sc);
187		SPI_WRITE(sc, AR71XX_SPI_IO_CTRL, iod);
188		SPI_BARRIER_WRITE(sc);
189		SPI_WRITE(sc, AR71XX_SPI_IO_CTRL, iod | SPI_IO_CTRL_CLK);
190	}
191
192	/*
193	 * Provide falling edge for connected device by clear clock bit.
194	 */
195	SPI_BARRIER_WRITE(sc);
196	SPI_WRITE(sc, AR71XX_SPI_IO_CTRL, iod);
197	SPI_BARRIER_WRITE(sc);
198	rds = SPI_READ(sc, AR71XX_SPI_RDS);
199
200	return (rds & 0xff);
201}
202
203static int
204ar71xx_spi_transfer(device_t dev, device_t child, struct spi_command *cmd)
205{
206	struct ar71xx_spi_softc *sc;
207	uint32_t cs;
208	uint8_t *buf_in, *buf_out;
209	int i;
210
211	sc = device_get_softc(dev);
212
213	spibus_get_cs(child, &cs);
214
215	cs &= ~SPIBUS_CS_HIGH;
216
217	ar71xx_spi_chip_activate(sc, cs);
218
219	KASSERT(cmd->tx_cmd_sz == cmd->rx_cmd_sz,
220	    ("TX/RX command sizes should be equal"));
221	KASSERT(cmd->tx_data_sz == cmd->rx_data_sz,
222	    ("TX/RX data sizes should be equal"));
223
224	/*
225	 * Transfer command
226	 */
227	buf_out = (uint8_t *)cmd->tx_cmd;
228	buf_in = (uint8_t *)cmd->rx_cmd;
229	for (i = 0; i < cmd->tx_cmd_sz; i++)
230		buf_in[i] = ar71xx_spi_txrx(sc, cs, buf_out[i]);
231
232	/*
233	 * Receive/transmit data (depends on  command)
234	 */
235	buf_out = (uint8_t *)cmd->tx_data;
236	buf_in = (uint8_t *)cmd->rx_data;
237	for (i = 0; i < cmd->tx_data_sz; i++)
238		buf_in[i] = ar71xx_spi_txrx(sc, cs, buf_out[i]);
239
240	ar71xx_spi_chip_deactivate(sc, cs);
241
242	return (0);
243}
244
245static int
246ar71xx_spi_detach(device_t dev)
247{
248	struct ar71xx_spi_softc *sc = device_get_softc(dev);
249
250	/*
251	 * Ensure any other writes to the device are finished
252	 * before we tear down the SPI device.
253	 */
254	SPI_BARRIER_WRITE(sc);
255
256	/*
257	 * Restore the control register; ensure it has hit the
258	 * hardware before continuing.
259	 */
260	SPI_WRITE(sc, AR71XX_SPI_CTRL, sc->sc_reg_ctrl);
261	SPI_BARRIER_WRITE(sc);
262
263	/*
264	 * And now, put the flash back into mapped IO mode and
265	 * ensure _that_ has completed before we finish up.
266	 */
267	SPI_WRITE(sc, AR71XX_SPI_FS, 0);
268	SPI_BARRIER_WRITE(sc);
269
270	if (sc->sc_mem_res)
271		bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res);
272
273	return (0);
274}
275
276static device_method_t ar71xx_spi_methods[] = {
277	/* Device interface */
278	DEVMETHOD(device_probe,		ar71xx_spi_probe),
279	DEVMETHOD(device_attach,	ar71xx_spi_attach),
280	DEVMETHOD(device_detach,	ar71xx_spi_detach),
281
282	DEVMETHOD(spibus_transfer,	ar71xx_spi_transfer),
283
284	{0, 0}
285};
286
287static driver_t ar71xx_spi_driver = {
288	"spi",
289	ar71xx_spi_methods,
290	sizeof(struct ar71xx_spi_softc),
291};
292
293static devclass_t ar71xx_spi_devclass;
294
295DRIVER_MODULE(ar71xx_spi, nexus, ar71xx_spi_driver, ar71xx_spi_devclass, 0, 0);
296