1/*-
2 * Copyright (c) 2016, Hiroki Mori
3 * Copyright (c) 2009, Oleksandr Tymoshenko <gonzo@FreeBSD.org>
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice unmodified, this list of conditions, and the following
11 *    disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29#include <sys/cdefs.h>
30__FBSDID("$FreeBSD$");
31
32#include <sys/param.h>
33#include <sys/systm.h>
34
35#include <sys/bus.h>
36#include <sys/interrupt.h>
37#include <sys/malloc.h>
38#include <sys/kernel.h>
39#include <sys/module.h>
40#include <sys/rman.h>
41#include <sys/sysctl.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#include <machine/pmap.h>
50
51#include <dev/spibus/spi.h>
52#include <dev/spibus/spibusvar.h>
53#include "spibus_if.h"
54
55#include <mips/atheros/ar531x/arspireg.h>
56#include <mips/atheros/ar531x/ar5315reg.h>
57
58#undef AR531X_SPI_DEBUG
59#ifdef AR531X_SPI_DEBUG
60#define dprintf printf
61#else
62#define dprintf(x, arg...)
63#endif
64
65/*
66 * register space access macros
67 */
68#define SPI_WRITE(sc, reg, val)	do {	\
69		bus_write_4(sc->sc_mem_res, (reg), (val)); \
70	} while (0)
71
72#define SPI_READ(sc, reg)	 bus_read_4(sc->sc_mem_res, (reg))
73
74#define SPI_SET_BITS(sc, reg, bits)	\
75	SPI_WRITE(sc, reg, SPI_READ(sc, (reg)) | (bits))
76
77#define SPI_CLEAR_BITS(sc, reg, bits)	\
78	SPI_WRITE(sc, reg, SPI_READ(sc, (reg)) & ~(bits))
79
80struct ar5315_spi_softc {
81	device_t		sc_dev;
82	struct resource		*sc_mem_res;
83	uint32_t		sc_reg_ctrl;
84	uint32_t		sc_debug;
85};
86
87static void
88ar5315_spi_attach_sysctl(device_t dev)
89{
90	struct ar5315_spi_softc *sc;
91	struct sysctl_ctx_list *ctx;
92	struct sysctl_oid *tree;
93
94	sc = device_get_softc(dev);
95	ctx = device_get_sysctl_ctx(dev);
96	tree = device_get_sysctl_tree(dev);
97
98	SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
99		"debug", CTLFLAG_RW, &sc->sc_debug, 0,
100		"ar5315_spi debugging flags");
101}
102
103static int
104ar5315_spi_probe(device_t dev)
105{
106	device_set_desc(dev, "AR5315 SPI");
107	return (0);
108}
109
110static int
111ar5315_spi_attach(device_t dev)
112{
113	struct ar5315_spi_softc *sc = device_get_softc(dev);
114	int rid;
115
116	sc->sc_dev = dev;
117        rid = 0;
118	sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
119	    RF_ACTIVE);
120	if (!sc->sc_mem_res) {
121		device_printf(dev, "Could not map memory\n");
122		return (ENXIO);
123	}
124
125	device_add_child(dev, "spibus", -1);
126	ar5315_spi_attach_sysctl(dev);
127
128	return (bus_generic_attach(dev));
129}
130
131static void
132ar5315_spi_chip_activate(struct ar5315_spi_softc *sc, int cs)
133{
134}
135
136static void
137ar5315_spi_chip_deactivate(struct ar5315_spi_softc *sc, int cs)
138{
139}
140
141static int
142ar5315_spi_get_block(off_t offset, caddr_t data, off_t count)
143{
144	int i;
145	for(i = 0; i < count / 4; ++i) {
146		*((uint32_t *)data + i) = ATH_READ_REG(AR5315_MEM1_BASE + offset + i * 4);
147	}
148//	printf("ar5315_spi_get_blockr: %x %x %x\n",
149//		(int)offset, (int)count, *(uint32_t *)data);
150	return (0);
151}
152
153static int
154ar5315_spi_transfer(device_t dev, device_t child, struct spi_command *cmd)
155{
156	struct ar5315_spi_softc *sc;
157	uint8_t *buf_in, *buf_out;
158	int lin, lout;
159	uint32_t ctl, cnt, op, rdat, cs;
160	int i, j;
161
162	sc = device_get_softc(dev);
163
164	if (sc->sc_debug & 0x8000)
165		printf("ar5315_spi_transfer: CMD ");
166
167	spibus_get_cs(child, &cs);
168
169	cs &= ~SPIBUS_CS_HIGH;
170
171	/* Open SPI controller interface */
172	ar5315_spi_chip_activate(sc, cs);
173
174	do {
175		ctl = SPI_READ(sc, ARSPI_REG_CTL);
176	} while (ctl & ARSPI_CTL_BUSY);
177
178	/*
179	 * Transfer command
180	 */
181	buf_out = (uint8_t *)cmd->tx_cmd;
182	op = buf_out[0];
183	if(op == 0x0b) {
184		int offset = buf_out[1] << 16 | buf_out[2] << 8 | buf_out[3];
185		ar5315_spi_get_block(offset, cmd->rx_data, cmd->rx_data_sz);
186		return (0);
187	}
188	do {
189		ctl = SPI_READ(sc, ARSPI_REG_CTL);
190	} while (ctl & ARSPI_CTL_BUSY);
191	if (sc->sc_debug & 0x8000) {
192		printf("%08x ", op);
193		printf("tx_cmd_sz=%d rx_cmd_sz=%d ", cmd->tx_cmd_sz,
194			cmd->rx_cmd_sz);
195		if(cmd->tx_cmd_sz != 1) {
196			printf("%08x ", *((uint32_t *)cmd->tx_cmd));
197			printf("%08x ", *((uint32_t *)cmd->tx_cmd + 1));
198		}
199	}
200	SPI_WRITE(sc, ARSPI_REG_OPCODE, op);
201
202	/* clear all of the tx and rx bits */
203	ctl &= ~(ARSPI_CTL_TXCNT_MASK | ARSPI_CTL_RXCNT_MASK);
204
205	/* now set txcnt */
206	cnt = 1;
207
208	ctl |= (cnt << ARSPI_CTL_TXCNT_SHIFT);
209
210	cnt = 24;
211	/* now set txcnt */
212	if(cmd->rx_cmd_sz < 24)
213		cnt = cmd->rx_cmd_sz;
214	ctl |= (cnt << ARSPI_CTL_RXCNT_SHIFT);
215
216	ctl |= ARSPI_CTL_START;
217
218	SPI_WRITE(sc, ARSPI_REG_CTL, ctl);
219
220	if(op == 0x0b)
221		SPI_WRITE(sc, ARSPI_REG_DATA, 0);
222	if (sc->sc_debug & 0x8000)
223		printf("\nDATA ");
224	/*
225	 * Receive/transmit data (depends on  command)
226	 */
227//	buf_out = (uint8_t *)cmd->tx_data;
228	buf_in = (uint8_t *)cmd->rx_cmd;
229//	lout = cmd->tx_data_sz;
230	lin = cmd->rx_cmd_sz;
231	if (sc->sc_debug & 0x8000)
232		printf("t%d r%d ", lout, lin);
233	for(i = 0; i <= (cnt - 1) / 4; ++i) {
234		do {
235			ctl = SPI_READ(sc, ARSPI_REG_CTL);
236		} while (ctl & ARSPI_CTL_BUSY);
237
238		rdat = SPI_READ(sc, ARSPI_REG_DATA);
239		if (sc->sc_debug & 0x8000)
240			printf("I%08x ", rdat);
241
242		for(j = 0; j < 4; ++j) {
243			buf_in[i * 4 + j + 1] = 0xff & (rdat >> (8 * j));
244			if(i * 4 + j  + 2 == cnt)
245				break;
246		}
247	}
248
249	ar5315_spi_chip_deactivate(sc, cs);
250	/*
251	 * Close SPI controller interface, restore flash memory mapped access.
252	 */
253	if (sc->sc_debug & 0x8000)
254		printf("\n");
255
256	return (0);
257}
258
259static int
260ar5315_spi_detach(device_t dev)
261{
262	struct ar5315_spi_softc *sc = device_get_softc(dev);
263
264	if (sc->sc_mem_res)
265		bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res);
266
267	return (0);
268}
269
270static device_method_t ar5315_spi_methods[] = {
271	/* Device interface */
272	DEVMETHOD(device_probe,		ar5315_spi_probe),
273	DEVMETHOD(device_attach,	ar5315_spi_attach),
274	DEVMETHOD(device_detach,	ar5315_spi_detach),
275
276	DEVMETHOD(spibus_transfer,	ar5315_spi_transfer),
277//	DEVMETHOD(spibus_get_block,	ar5315_spi_get_block),
278
279	DEVMETHOD_END
280};
281
282static driver_t ar5315_spi_driver = {
283	"spi",
284	ar5315_spi_methods,
285	sizeof(struct ar5315_spi_softc),
286};
287
288static devclass_t ar5315_spi_devclass;
289
290DRIVER_MODULE(ar5315_spi, nexus, ar5315_spi_driver, ar5315_spi_devclass, 0, 0);
291