1155324Simp/*-
2236495Smarius * Copyright (c) 2006 M. Warner Losh.
3236495Smarius * Copyright (c) 2011-2012 Ian Lepore.
4236495Smarius * All rights reserved.
5155324Simp *
6155324Simp * Redistribution and use in source and binary forms, with or without
7155324Simp * modification, are permitted provided that the following conditions
8155324Simp * are met:
9155324Simp * 1. Redistributions of source code must retain the above copyright
10155324Simp *    notice, this list of conditions and the following disclaimer.
11155324Simp * 2. Redistributions in binary form must reproduce the above copyright
12155324Simp *    notice, this list of conditions and the following disclaimer in the
13155324Simp *    documentation and/or other materials provided with the distribution.
14155324Simp *
15185265Simp * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16185265Simp * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17185265Simp * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18185265Simp * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
19185265Simp * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20185265Simp * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21185265Simp * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22185265Simp * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23185265Simp * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24185265Simp * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25185265Simp * SUCH DAMAGE.
26155324Simp */
27155324Simp
28266196Sian#include "opt_platform.h"
29266196Sian
30155324Simp#include <sys/cdefs.h>
31155324Simp__FBSDID("$FreeBSD$");
32155324Simp
33155324Simp#include <sys/param.h>
34155324Simp#include <sys/systm.h>
35155324Simp#include <sys/bus.h>
36155324Simp#include <sys/conf.h>
37155324Simp#include <sys/kernel.h>
38236495Smarius#include <sys/lock.h>
39155324Simp#include <sys/mbuf.h>
40155324Simp#include <sys/malloc.h>
41155324Simp#include <sys/module.h>
42155324Simp#include <sys/rman.h>
43236495Smarius#include <sys/sx.h>
44236495Smarius
45155324Simp#include <machine/bus.h>
46155324Simp
47238955Simp#include <arm/at91/at91var.h>
48155324Simp#include <arm/at91/at91_spireg.h>
49160358Simp#include <arm/at91/at91_pdcreg.h>
50155324Simp
51160358Simp#include <dev/spibus/spi.h>
52239626Simp#include <dev/spibus/spibusvar.h>
53236495Smarius
54266196Sian#ifdef FDT
55266196Sian#include <dev/fdt/fdt_common.h>
56266196Sian#include <dev/ofw/ofw_bus.h>
57266196Sian#include <dev/ofw/ofw_bus_subr.h>
58266196Sian#endif
59266196Sian
60160358Simp#include "spibus_if.h"
61160358Simp
62155324Simpstruct at91_spi_softc
63155324Simp{
64155324Simp	device_t dev;			/* Myself */
65155324Simp	void *intrhand;			/* Interrupt handle */
66155324Simp	struct resource *irq_res;	/* IRQ resource */
67155324Simp	struct resource	*mem_res;	/* Memory resource */
68236495Smarius	bus_dma_tag_t dmatag;		/* bus dma tag for transfers */
69160358Simp	bus_dmamap_t map[4];		/* Maps for the transaction */
70236495Smarius	struct sx xfer_mtx;		/* Enforce one transfer at a time */
71236495Smarius	uint32_t xfer_done;		/* interrupt<->mainthread signaling */
72155324Simp};
73155324Simp
74236495Smarius#define CS_TO_MR(cs)	((~(1 << (cs)) & 0x0f) << 16)
75236495Smarius
76155324Simpstatic inline uint32_t
77155324SimpRD4(struct at91_spi_softc *sc, bus_size_t off)
78155324Simp{
79236495Smarius
80236495Smarius	return (bus_read_4(sc->mem_res, off));
81155324Simp}
82155324Simp
83155324Simpstatic inline void
84155324SimpWR4(struct at91_spi_softc *sc, bus_size_t off, uint32_t val)
85155324Simp{
86236495Smarius
87155324Simp	bus_write_4(sc->mem_res, off, val);
88155324Simp}
89155324Simp
90155324Simp/* bus entry points */
91155324Simpstatic int at91_spi_attach(device_t dev);
92155324Simpstatic int at91_spi_detach(device_t dev);
93236495Smariusstatic int at91_spi_probe(device_t dev);
94236495Smariusstatic int at91_spi_transfer(device_t dev, device_t child,
95236495Smarius    struct spi_command *cmd);
96155324Simp
97155324Simp/* helper routines */
98236495Smariusstatic void at91_getaddr(void *arg, bus_dma_segment_t *segs, int nsegs,
99236495Smarius    int error);
100155324Simpstatic int at91_spi_activate(device_t dev);
101155324Simpstatic void at91_spi_deactivate(device_t dev);
102163526Simpstatic void at91_spi_intr(void *arg);
103155324Simp
104155324Simpstatic int
105155324Simpat91_spi_probe(device_t dev)
106155324Simp{
107266196Sian#ifdef FDT
108266196Sian	if (!ofw_bus_is_compatible(dev, "atmel,at91rm9200-spi"))
109266196Sian		return (ENXIO);
110266196Sian#endif
111236495Smarius	device_set_desc(dev, "AT91 SPI");
112155324Simp	return (0);
113155324Simp}
114155324Simp
115155324Simpstatic int
116155324Simpat91_spi_attach(device_t dev)
117155324Simp{
118236495Smarius	struct at91_spi_softc *sc;
119236495Smarius	int err;
120236495Smarius	uint32_t csr;
121155324Simp
122236495Smarius	sc = device_get_softc(dev);
123236495Smarius
124155324Simp	sc->dev = dev;
125236495Smarius	sx_init(&sc->xfer_mtx, device_get_nameunit(dev));
126236495Smarius
127236495Smarius	/*
128236495Smarius	 * Allocate resources.
129236495Smarius	 */
130155324Simp	err = at91_spi_activate(dev);
131155324Simp	if (err)
132155324Simp		goto out;
133155324Simp
134266196Sian#ifdef FDT
135155324Simp	/*
136266196Sian	 * Disable devices need to hold their resources, so return now and not attach
137266196Sian	 * the spibus, setup interrupt handlers, etc.
138266196Sian	 */
139266196Sian	if (!ofw_bus_status_okay(dev))
140266196Sian		return 0;
141266196Sian#endif
142266196Sian
143266196Sian	/*
144236495Smarius	 * Set up the hardware.
145155324Simp	 */
146155324Simp
147155324Simp	WR4(sc, SPI_CR, SPI_CR_SWRST);
148236495Smarius	/* "Software Reset must be Written Twice" erratum */
149236495Smarius	WR4(sc, SPI_CR, SPI_CR_SWRST);
150163526Simp	WR4(sc, SPI_IDR, 0xffffffff);
151160358Simp
152160358Simp	WR4(sc, SPI_MR, (0xf << 24) | SPI_MR_MSTR | SPI_MR_MODFDIS |
153236495Smarius	    CS_TO_MR(0));
154160358Simp
155236495Smarius	/*
156236495Smarius	 * For now, run the bus at the slowest speed possible as otherwise we
157236495Smarius	 * may encounter data corruption on transmit as seen with ETHERNUT5
158236495Smarius	 * and AT45DB321D even though both board and slave device can take
159236495Smarius	 * more.
160236495Smarius	 * This also serves as a work-around for the "NPCSx rises if no data
161236495Smarius	 * data is to be transmitted" erratum.  The ideal workaround for the
162236495Smarius	 * latter is to take the chip select control away from the peripheral
163236495Smarius	 * and manage it directly as a GPIO line.  The easy solution is to
164236495Smarius	 * slow down the bus so dramatically that it just never gets starved
165236495Smarius	 * as may be seen when the OCHI controller is running and consuming
166236495Smarius	 * memory and APB bandwidth.
167236495Smarius	 * Also, currently we lack a way for lettting both the board and the
168236495Smarius	 * slave devices take their maximum supported SPI clocks into account.
169238955Simp	 * Also, we hard-wire SPI mode to 3.
170236495Smarius	 */
171236495Smarius	csr = SPI_CSR_CPOL | (4 << 16) | (0xff << 8);
172236495Smarius	WR4(sc, SPI_CSR0, csr);
173236495Smarius	WR4(sc, SPI_CSR1, csr);
174236495Smarius	WR4(sc, SPI_CSR2, csr);
175236495Smarius	WR4(sc, SPI_CSR3, csr);
176236495Smarius
177160358Simp	WR4(sc, SPI_CR, SPI_CR_SPIEN);
178160358Simp
179160358Simp	WR4(sc, PDC_PTCR, PDC_PTCR_TXTDIS);
180160358Simp	WR4(sc, PDC_PTCR, PDC_PTCR_RXTDIS);
181160358Simp	WR4(sc, PDC_RNPR, 0);
182160358Simp	WR4(sc, PDC_RNCR, 0);
183160358Simp	WR4(sc, PDC_TNPR, 0);
184160358Simp	WR4(sc, PDC_TNCR, 0);
185160358Simp	WR4(sc, PDC_RPR, 0);
186160358Simp	WR4(sc, PDC_RCR, 0);
187160358Simp	WR4(sc, PDC_TPR, 0);
188160358Simp	WR4(sc, PDC_TCR, 0);
189160358Simp	RD4(sc, SPI_RDR);
190160358Simp	RD4(sc, SPI_SR);
191160358Simp
192160358Simp	device_add_child(dev, "spibus", -1);
193160358Simp	bus_generic_attach(dev);
194225882Skevloout:
195155324Simp	if (err)
196155324Simp		at91_spi_deactivate(dev);
197155324Simp	return (err);
198155324Simp}
199155324Simp
200155324Simpstatic int
201155324Simpat91_spi_detach(device_t dev)
202155324Simp{
203236495Smarius
204155324Simp	return (EBUSY);	/* XXX */
205155324Simp}
206155324Simp
207155324Simpstatic int
208155324Simpat91_spi_activate(device_t dev)
209155324Simp{
210155324Simp	struct at91_spi_softc *sc;
211236495Smarius	int err, i, rid;
212155324Simp
213155324Simp	sc = device_get_softc(dev);
214236495Smarius	err = ENOMEM;
215236495Smarius
216155324Simp	rid = 0;
217155324Simp	sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
218155324Simp	    RF_ACTIVE);
219155324Simp	if (sc->mem_res == NULL)
220236495Smarius		goto out;
221236495Smarius
222155324Simp	rid = 0;
223155324Simp	sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
224155324Simp	    RF_ACTIVE);
225163526Simp	if (sc->irq_res == NULL)
226236495Smarius		goto out;
227163526Simp	err = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC | INTR_MPSAFE,
228166901Spiso	    NULL, at91_spi_intr, sc, &sc->intrhand);
229163526Simp	if (err != 0)
230236495Smarius		goto out;
231236495Smarius
232236495Smarius	err = bus_dma_tag_create(bus_get_dma_tag(dev), 1, 0,
233236495Smarius	    BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, 2048, 1,
234236495Smarius	    2048, BUS_DMA_ALLOCNOW, NULL, NULL, &sc->dmatag);
235236495Smarius	if (err != 0)
236236495Smarius		goto out;
237236495Smarius
238236495Smarius	for (i = 0; i < 4; i++) {
239236495Smarius		err = bus_dmamap_create(sc->dmatag, 0,  &sc->map[i]);
240236495Smarius		if (err != 0)
241236495Smarius			goto out;
242236495Smarius	}
243236495Smariusout:
244236495Smarius	if (err != 0)
245236495Smarius		at91_spi_deactivate(dev);
246163526Simp	return (err);
247155324Simp}
248155324Simp
249155324Simpstatic void
250155324Simpat91_spi_deactivate(device_t dev)
251155324Simp{
252155324Simp	struct at91_spi_softc *sc;
253236495Smarius	int i;
254155324Simp
255155324Simp	sc = device_get_softc(dev);
256236495Smarius	bus_generic_detach(dev);
257236495Smarius
258236495Smarius	for (i = 0; i < 4; i++)
259236495Smarius		if (sc->map[i])
260236495Smarius			bus_dmamap_destroy(sc->dmatag, sc->map[i]);
261236495Smarius
262236495Smarius	if (sc->dmatag)
263236495Smarius		bus_dma_tag_destroy(sc->dmatag);
264236495Smarius
265155324Simp	if (sc->intrhand)
266155324Simp		bus_teardown_intr(dev, sc->irq_res, sc->intrhand);
267236495Smarius	sc->intrhand = NULL;
268155324Simp	if (sc->irq_res)
269155324Simp		bus_release_resource(dev, SYS_RES_IRQ,
270155324Simp		    rman_get_rid(sc->irq_res), sc->irq_res);
271236495Smarius	sc->irq_res = NULL;
272236495Smarius
273236495Smarius	if (sc->mem_res)
274236495Smarius		bus_release_resource(dev, SYS_RES_MEMORY,
275236495Smarius		    rman_get_rid(sc->mem_res), sc->mem_res);
276236495Smarius	sc->mem_res = NULL;
277155324Simp}
278155324Simp
279155324Simpstatic void
280236495Smariusat91_getaddr(void *arg, bus_dma_segment_t *segs, int nsegs __unused,
281236495Smarius    int error)
282155324Simp{
283236495Smarius
284160358Simp	if (error != 0)
285155324Simp		return;
286160358Simp	*(bus_addr_t *)arg = segs[0].ds_addr;
287155324Simp}
288155324Simp
289155324Simpstatic int
290160358Simpat91_spi_transfer(device_t dev, device_t child, struct spi_command *cmd)
291155324Simp{
292155324Simp	struct at91_spi_softc *sc;
293160358Simp	bus_addr_t addr;
294239626Simp	int err, i, j, mode[4], cs;
295155324Simp
296236495Smarius	KASSERT(cmd->tx_cmd_sz == cmd->rx_cmd_sz,
297236495Smarius	    ("%s: TX/RX command sizes should be equal", __func__));
298236495Smarius	KASSERT(cmd->tx_data_sz == cmd->rx_data_sz,
299236495Smarius	    ("%s: TX/RX data sizes should be equal", __func__));
300236495Smarius
301239626Simp	/* get the proper chip select */
302239626Simp	spibus_get_cs(child, &cs);
303239626Simp
304160358Simp	sc = device_get_softc(dev);
305236495Smarius	i = 0;
306236495Smarius
307236495Smarius	sx_xlock(&sc->xfer_mtx);
308236495Smarius
309236495Smarius	/*
310236495Smarius	 * Disable transfers while we set things up.
311236495Smarius	 */
312160358Simp	WR4(sc, PDC_PTCR, PDC_PTCR_TXTDIS | PDC_PTCR_RXTDIS);
313236495Smarius
314238955Simp	/*
315238955Simp	 * PSCDEC = 0 has a range of 0..3 for chip select.  We
316238955Simp	 * don't support PSCDEC = 1 which has a range of 0..15.
317238955Simp	 */
318239626Simp	if (cs < 0 || cs > 3) {
319236495Smarius		device_printf(dev,
320239626Simp		    "Invalid chip select %d requested by %s\n", cs,
321236495Smarius		    device_get_nameunit(child));
322236495Smarius		err = EINVAL;
323160358Simp		goto out;
324236495Smarius	}
325238955Simp
326236495Smarius#ifdef SPI_CHIP_SELECT_HIGH_SUPPORT
327238955Simp	/*
328238955Simp	 * The AT91RM9200 couldn't do CS high for CS 0.  Other chips can, but we
329238955Simp	 * don't support that yet, or other spi modes.
330238955Simp	 */
331239626Simp	if (at91_is_rm92() && cs == 0 &&
332236495Smarius	    (cmd->flags & SPI_CHIP_SELECT_HIGH) != 0) {
333236495Smarius		device_printf(dev,
334238955Simp		    "Invalid chip select high requested by %s for cs 0.\n",
335236495Smarius		    device_get_nameunit(child));
336236495Smarius		err = EINVAL;
337236495Smarius		goto out;
338236495Smarius	}
339236495Smarius#endif
340239626Simp	err = (RD4(sc, SPI_MR) & ~0x000f0000) | CS_TO_MR(cs);
341238955Simp	WR4(sc, SPI_MR, err);
342236495Smarius
343236495Smarius	/*
344236495Smarius	 * Set up the TX side of the transfer.
345236495Smarius	 */
346236495Smarius	if ((err = bus_dmamap_load(sc->dmatag, sc->map[i], cmd->tx_cmd,
347236495Smarius	    cmd->tx_cmd_sz, at91_getaddr, &addr, 0)) != 0)
348236495Smarius		goto out;
349160358Simp	WR4(sc, PDC_TPR, addr);
350160358Simp	WR4(sc, PDC_TCR, cmd->tx_cmd_sz);
351160358Simp	bus_dmamap_sync(sc->dmatag, sc->map[i], BUS_DMASYNC_PREWRITE);
352163526Simp	mode[i++] = BUS_DMASYNC_POSTWRITE;
353163526Simp	if (cmd->tx_data_sz > 0) {
354236495Smarius		if ((err = bus_dmamap_load(sc->dmatag, sc->map[i],
355236495Smarius		    cmd->tx_data, cmd->tx_data_sz, at91_getaddr, &addr, 0)) !=
356236495Smarius		    0)
357163526Simp			goto out;
358163526Simp		WR4(sc, PDC_TNPR, addr);
359164741Simp		WR4(sc, PDC_TNCR, cmd->tx_data_sz);
360163526Simp		bus_dmamap_sync(sc->dmatag, sc->map[i], BUS_DMASYNC_PREWRITE);
361163526Simp		mode[i++] = BUS_DMASYNC_POSTWRITE;
362163526Simp	}
363236495Smarius
364236495Smarius	/*
365236495Smarius	 * Set up the RX side of the transfer.
366236495Smarius	 */
367236495Smarius	if ((err = bus_dmamap_load(sc->dmatag, sc->map[i], cmd->rx_cmd,
368236495Smarius	    cmd->rx_cmd_sz, at91_getaddr, &addr, 0)) != 0)
369160358Simp		goto out;
370160358Simp	WR4(sc, PDC_RPR, addr);
371236495Smarius	WR4(sc, PDC_RCR, cmd->rx_cmd_sz);
372160358Simp	bus_dmamap_sync(sc->dmatag, sc->map[i], BUS_DMASYNC_PREREAD);
373163526Simp	mode[i++] = BUS_DMASYNC_POSTREAD;
374164741Simp	if (cmd->rx_data_sz > 0) {
375236495Smarius		if ((err = bus_dmamap_load(sc->dmatag, sc->map[i],
376236495Smarius		    cmd->rx_data, cmd->rx_data_sz, at91_getaddr, &addr, 0)) !=
377236495Smarius		    0)
378163526Simp			goto out;
379163526Simp		WR4(sc, PDC_RNPR, addr);
380164741Simp		WR4(sc, PDC_RNCR, cmd->rx_data_sz);
381163526Simp		bus_dmamap_sync(sc->dmatag, sc->map[i], BUS_DMASYNC_PREREAD);
382163526Simp		mode[i++] = BUS_DMASYNC_POSTREAD;
383163526Simp	}
384236495Smarius
385236495Smarius	/*
386236495Smarius	 * Start the transfer, wait for it to complete.
387236495Smarius	 */
388236495Smarius	sc->xfer_done = 0;
389237239Smarius	WR4(sc, SPI_IER, SPI_SR_RXBUFF);
390160358Simp	WR4(sc, PDC_PTCR, PDC_PTCR_TXTEN | PDC_PTCR_RXTEN);
391236495Smarius	do
392236495Smarius		err = tsleep(&sc->xfer_done, PCATCH | PZERO, "at91_spi", hz);
393237239Smarius	while (sc->xfer_done == 0 && err != EINTR);
394155324Simp
395236495Smarius	/*
396236495Smarius	 * Stop the transfer and clean things up.
397236495Smarius	 */
398163526Simp	WR4(sc, PDC_PTCR, PDC_PTCR_TXTDIS | PDC_PTCR_RXTDIS);
399236495Smarius	if (err == 0)
400236495Smarius		for (j = 0; j < i; j++)
401163526Simp			bus_dmamap_sync(sc->dmatag, sc->map[j], mode[j]);
402236495Smariusout:
403163526Simp	for (j = 0; j < i; j++)
404163526Simp		bus_dmamap_unload(sc->dmatag, sc->map[j]);
405236495Smarius
406236495Smarius	sx_xunlock(&sc->xfer_mtx);
407236495Smarius
408163526Simp	return (err);
409155324Simp}
410155324Simp
411163526Simpstatic void
412163526Simpat91_spi_intr(void *arg)
413163526Simp{
414236495Smarius	struct at91_spi_softc *sc;
415237239Smarius	uint32_t sr;
416163526Simp
417236495Smarius	sc = (struct at91_spi_softc*)arg;
418236495Smarius
419163526Simp	sr = RD4(sc, SPI_SR) & RD4(sc, SPI_IMR);
420237239Smarius	if ((sr & SPI_SR_RXBUFF) != 0) {
421237239Smarius		sc->xfer_done = 1;
422237239Smarius		WR4(sc, SPI_IDR, SPI_SR_RXBUFF);
423236495Smarius		wakeup(&sc->xfer_done);
424163526Simp	}
425237239Smarius	if ((sr & ~SPI_SR_RXBUFF) != 0) {
426163526Simp		device_printf(sc->dev, "Unexpected ISR %#x\n", sr);
427237239Smarius		WR4(sc, SPI_IDR, sr & ~SPI_SR_RXBUFF);
428163526Simp	}
429163526Simp}
430163526Simp
431163526Simpstatic devclass_t at91_spi_devclass;
432163526Simp
433155324Simpstatic device_method_t at91_spi_methods[] = {
434155324Simp	/* Device interface */
435155324Simp	DEVMETHOD(device_probe,		at91_spi_probe),
436155324Simp	DEVMETHOD(device_attach,	at91_spi_attach),
437155324Simp	DEVMETHOD(device_detach,	at91_spi_detach),
438155324Simp
439160358Simp	/* spibus interface */
440160358Simp	DEVMETHOD(spibus_transfer,	at91_spi_transfer),
441236495Smarius
442236495Smarius	DEVMETHOD_END
443155324Simp};
444155324Simp
445155324Simpstatic driver_t at91_spi_driver = {
446192059Sgonzo	"spi",
447155324Simp	at91_spi_methods,
448155324Simp	sizeof(struct at91_spi_softc),
449155324Simp};
450155324Simp
451266196Sian#ifdef FDT
452266196SianDRIVER_MODULE(at91_spi, simplebus, at91_spi_driver, at91_spi_devclass, NULL,
453266196Sian    NULL);
454266196Sian#else
455236495SmariusDRIVER_MODULE(at91_spi, atmelarm, at91_spi_driver, at91_spi_devclass, NULL,
456236495Smarius    NULL);
457266196Sian#endif
458