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