rt305x_spi.c revision 310158
1/*-
2 * Copyright (c) 2009, Oleksandr Tymoshenko <gonzo@FreeBSD.org>
3 * Copyright (c) 2011, Aleksandr Rybalko <ray@FreeBSD.org>
4 * Copyright (c) 2013, Alexander A. Mityaev <sansan@adm.ua>
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/rt305x/rt305x_spi.c 310158 2016-12-16 15:45:09Z manu $");
32
33#include <sys/param.h>
34#include <sys/systm.h>
35#include <sys/bus.h>
36
37#include <sys/kernel.h>
38#include <sys/module.h>
39#include <sys/rman.h>
40#include <sys/lock.h>
41#include <sys/mutex.h>
42
43#include <machine/bus.h>
44#include <machine/cpu.h>
45//#include <machine/pmap.h>
46
47#include <dev/spibus/spi.h>
48#include <dev/spibus/spibusvar.h>
49#include "spibus_if.h"
50
51#include "opt_platform.h"
52
53#ifdef FDT
54#include <dev/ofw/openfirm.h>
55#include <dev/ofw/ofw_bus.h>
56#include <dev/ofw/ofw_bus_subr.h>
57#endif
58
59#include <mips/rt305x/rt305xreg.h>
60#include <dev/flash/mx25lreg.h>
61
62#undef RT305X_SPI_DEBUG
63#ifdef RT305X_SPI_DEBUG
64#define dprintf printf
65#else
66#define dprintf(x, arg...)
67#endif
68
69/*
70 * register space access macros
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 rt305x_spi_softc {
85	device_t		sc_dev;
86	struct resource		*sc_mem_res;
87};
88
89static int	rt305x_spi_probe(device_t);
90static int	rt305x_spi_attach(device_t);
91static int	rt305x_spi_detach(device_t);
92static int	rt305x_spi_wait(struct rt305x_spi_softc *);
93static void	rt305x_spi_chip_activate(struct rt305x_spi_softc *);
94static void	rt305x_spi_chip_deactivate(struct rt305x_spi_softc *);
95static uint8_t	rt305x_spi_txrx(struct rt305x_spi_softc *, uint8_t *, int);
96static int	rt305x_spi_transfer(device_t, device_t, struct spi_command *);
97#ifdef FDT
98static phandle_t rt305x_spi_get_node(device_t, device_t);
99#endif
100
101static int
102rt305x_spi_probe(device_t dev)
103{
104#ifdef FDT
105        if (!ofw_bus_is_compatible(dev, "ralink,rt305x-spi"))
106            return(ENXIO);
107#endif
108	device_set_desc(dev, "RT305X SPI");
109	return (0);
110}
111
112static int
113rt305x_spi_attach(device_t dev)
114{
115	struct rt305x_spi_softc *sc = device_get_softc(dev);
116	int rid;
117
118	sc->sc_dev = dev;
119        rid = 0;
120	sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
121	    RF_ACTIVE);
122	if (!sc->sc_mem_res) {
123		device_printf(dev, "Could not map memory\n");
124		return (ENXIO);
125	}
126
127	if (rt305x_spi_wait(sc)) {
128		bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res);
129		return (EBUSY);
130	}
131
132	SPI_WRITE(sc, RT305X_SPICFG, MSBFIRST | SPICLKPOL | TX_ON_CLK_FALL |
133	    SPI_CLK_DIV8); /* XXX: make it configurable */
134	    /*
135	     * W25Q64CV max 104MHz, bus 120-192 MHz, so divide by 2.
136	     * Update: divide by 4, DEV2 to fast for flash.
137	     */
138
139	device_add_child(dev, "spibus", 0);
140	return (bus_generic_attach(dev));
141}
142
143static int
144rt305x_spi_detach(device_t dev)
145{
146	struct rt305x_spi_softc *sc = device_get_softc(dev);
147
148	SPI_SET_BITS(sc, RT305X_SPICTL, HIZSMOSI | CS_HIGH);
149
150	if (sc->sc_mem_res)
151		bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res);
152
153	return (0);
154}
155
156static void
157rt305x_spi_chip_activate(struct rt305x_spi_softc *sc)
158{
159//        printf("%s\n", __func__);
160        rt305x_spi_wait(sc);
161	/*
162	 * Put all CSx to low
163	 */
164	SPI_CLEAR_BITS(sc, RT305X_SPICTL, CS_HIGH | HIZSMOSI);
165}
166
167static void
168rt305x_spi_chip_deactivate(struct rt305x_spi_softc *sc)
169{
170//        printf("%s\n", __func__);
171        rt305x_spi_wait(sc);
172	/*
173	 * Put all CSx to high
174	 */
175	SPI_SET_BITS(sc, RT305X_SPICTL, CS_HIGH | HIZSMOSI);
176}
177
178static int
179rt305x_spi_wait(struct rt305x_spi_softc *sc)
180{
181	int i = 1000;
182
183	while (i--) {
184		if (!SPI_READ(sc, RT305X_SPIBUSY))
185			break;
186	}
187	if (i == 0) {
188		printf("busy\n");
189		return (1);
190	}
191
192	return (0);
193}
194
195static uint8_t
196rt305x_spi_txrx(struct rt305x_spi_softc *sc, uint8_t *data, int write)
197{
198
199	if (rt305x_spi_wait(sc))
200		return (EBUSY);
201
202	if (write == RT305X_SPI_WRITE) {
203		SPI_WRITE(sc, RT305X_SPIDATA, *data);
204		SPI_SET_BITS(sc, RT305X_SPICTL, START_WRITE);
205		//printf("%s(W:%d)\n", __func__, *data);
206	} else {/* RT305X_SPI_READ */
207		SPI_SET_BITS(sc, RT305X_SPICTL, START_READ);
208		if (rt305x_spi_wait(sc))
209			return (EBUSY);
210
211		*data = SPI_READ(sc, RT305X_SPIDATA) & 0xff;
212		//printf("%s(R:%d)\n", __func__, *data);
213	}
214	return (0);
215}
216
217static int
218rt305x_spi_transfer(device_t dev, device_t child, struct spi_command *cmd)
219{
220	struct rt305x_spi_softc *sc;
221	uint32_t cs;
222	uint8_t *buf, byte, *tx_buf;
223	int i, sz, error = 0, write = 0;
224
225	sc = device_get_softc(dev);
226
227	spibus_get_cs(child, &cs);
228
229	if (cs != 0)
230		/* Only 1 CS */
231		return (ENXIO);
232
233        /* There is always a command to transfer. */
234        tx_buf = (uint8_t *)(cmd->tx_cmd);
235
236        /* Perform some fixup because RT305X dont support duplex SPI */
237        switch(tx_buf[0]) {
238                case CMD_READ_IDENT:
239                        cmd->tx_cmd_sz = 1;
240                        cmd->rx_cmd_sz = 3;
241                        break;
242                case CMD_WRITE_ENABLE:
243                case CMD_WRITE_DISABLE:
244                        cmd->tx_cmd_sz = 1;
245                        cmd->rx_cmd_sz = 0;
246                        break;
247                case CMD_READ_STATUS:
248                        cmd->tx_cmd_sz = 1;
249                        cmd->rx_cmd_sz = 1;
250                        break;
251                case CMD_READ:
252                        cmd->tx_cmd_sz = 4;
253                case CMD_FAST_READ:
254                        cmd->tx_cmd_sz = 5;
255                        cmd->rx_cmd_sz = cmd->tx_data_sz = 0;
256                        break;
257                case CMD_SECTOR_ERASE:
258                        cmd->tx_cmd_sz = 4;
259                        cmd->rx_cmd_sz = cmd->tx_data_sz = 0;
260                        break;
261                case CMD_PAGE_PROGRAM:
262                        cmd->tx_cmd_sz = 4;
263                        cmd->rx_cmd_sz = cmd->rx_data_sz = 0;
264                        break;
265        }
266
267	rt305x_spi_chip_activate(sc);
268
269	if (cmd->tx_cmd_sz + cmd->rx_cmd_sz) {
270		buf = (uint8_t *)(cmd->rx_cmd);
271		tx_buf = (uint8_t *)(cmd->tx_cmd);
272		sz = cmd->tx_cmd_sz + cmd->rx_cmd_sz;
273
274		for (i = 0; i < sz; i++) {
275                        if(i < cmd->tx_cmd_sz) {
276			        byte = tx_buf[i];
277        			error = rt305x_spi_txrx(sc, &byte,
278		        	    RT305X_SPI_WRITE);
279        			if (error)
280        				goto rt305x_spi_transfer_fail;
281                                continue;
282                        }
283                        error = rt305x_spi_txrx(sc, &byte,
284		            RT305X_SPI_READ);
285        		if (error)
286        			goto rt305x_spi_transfer_fail;
287			buf[i] = byte;
288		}
289	}
290
291	/*
292	 * Transfer/Receive data
293	 */
294
295	if (cmd->tx_data_sz + cmd->rx_data_sz) {
296		write = (cmd->tx_data_sz > 0)?1:0;
297		buf = (uint8_t *)(write ? cmd->tx_data : cmd->rx_data);
298		sz = write ? cmd->tx_data_sz : cmd->rx_data_sz;
299
300		for (i = 0; i < sz; i++) {
301			byte = buf[i];
302			error = rt305x_spi_txrx(sc, &byte,
303			    write?RT305X_SPI_WRITE:RT305X_SPI_READ);
304			if (error)
305				goto rt305x_spi_transfer_fail;
306			buf[i] = byte;
307		}
308	}
309rt305x_spi_transfer_fail:
310	rt305x_spi_chip_deactivate(sc);
311
312	return (error);
313}
314
315#ifdef FDT
316static phandle_t
317rt305x_spi_get_node(device_t bus, device_t dev)
318{
319
320	/* We only have one child, the SPI bus, which needs our own node. */
321	return (ofw_bus_get_node(bus));
322}
323#endif
324
325static device_method_t rt305x_spi_methods[] = {
326	/* Device interface */
327	DEVMETHOD(device_probe,		rt305x_spi_probe),
328	DEVMETHOD(device_attach,	rt305x_spi_attach),
329	DEVMETHOD(device_detach,	rt305x_spi_detach),
330
331	DEVMETHOD(spibus_transfer,	rt305x_spi_transfer),
332
333#ifdef FDT
334	/* ofw_bus interface */
335	DEVMETHOD(ofw_bus_get_node,	rt305x_spi_get_node),
336#endif
337
338	DEVMETHOD_END
339};
340
341static driver_t rt305x_spi_driver = {
342	.name = "spi",
343	.methods = rt305x_spi_methods,
344	.size = sizeof(struct rt305x_spi_softc),
345};
346
347static devclass_t rt305x_spi_devclass;
348
349DRIVER_MODULE(rt305x_spi, obio, rt305x_spi_driver, rt305x_spi_devclass, 0, 0);
350#ifdef FDT
351DRIVER_MODULE(rt305x_spi, simplebus, rt305x_spi_driver, rt305x_spi_devclass, 0, 0);
352#endif
353