at91_spi.c revision 185265
1155324Simp/*-
2155324Simp * Copyright (c) 2006 M. Warner Losh.  All rights reserved.
3155324Simp *
4155324Simp * Redistribution and use in source and binary forms, with or without
5155324Simp * modification, are permitted provided that the following conditions
6155324Simp * are met:
7155324Simp * 1. Redistributions of source code must retain the above copyright
8155324Simp *    notice, this list of conditions and the following disclaimer.
9155324Simp * 2. Redistributions in binary form must reproduce the above copyright
10155324Simp *    notice, this list of conditions and the following disclaimer in the
11155324Simp *    documentation and/or other materials provided with the distribution.
12155324Simp *
13185265Simp * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14185265Simp * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15185265Simp * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16185265Simp * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
17185265Simp * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18185265Simp * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19185265Simp * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20185265Simp * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21185265Simp * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22185265Simp * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23185265Simp * SUCH DAMAGE.
24155324Simp */
25155324Simp
26155324Simp#include <sys/cdefs.h>
27155324Simp__FBSDID("$FreeBSD: head/sys/arm/at91/at91_spi.c 185265 2008-11-25 00:13:26Z imp $");
28155324Simp
29155324Simp#include <sys/param.h>
30155324Simp#include <sys/systm.h>
31155324Simp#include <sys/bus.h>
32155324Simp#include <sys/conf.h>
33155324Simp#include <sys/kernel.h>
34155324Simp#include <sys/mbuf.h>
35155324Simp#include <sys/malloc.h>
36155324Simp#include <sys/module.h>
37155324Simp#include <sys/mutex.h>
38155324Simp#include <sys/rman.h>
39155324Simp#include <machine/bus.h>
40155324Simp
41155324Simp#include <arm/at91/at91_spireg.h>
42160358Simp#include <arm/at91/at91_pdcreg.h>
43155324Simp
44160358Simp#include <dev/spibus/spi.h>
45160358Simp#include "spibus_if.h"
46160358Simp
47155324Simpstruct at91_spi_softc
48155324Simp{
49155324Simp	device_t dev;			/* Myself */
50155324Simp	void *intrhand;			/* Interrupt handle */
51155324Simp	struct resource *irq_res;	/* IRQ resource */
52155324Simp	struct resource	*mem_res;	/* Memory resource */
53160358Simp	bus_dma_tag_t dmatag;		/* bus dma tag for mbufs */
54160358Simp	bus_dmamap_t map[4];		/* Maps for the transaction */
55163526Simp	int rxdone;
56155324Simp};
57155324Simp
58155324Simpstatic inline uint32_t
59155324SimpRD4(struct at91_spi_softc *sc, bus_size_t off)
60155324Simp{
61155324Simp	return bus_read_4(sc->mem_res, off);
62155324Simp}
63155324Simp
64155324Simpstatic inline void
65155324SimpWR4(struct at91_spi_softc *sc, bus_size_t off, uint32_t val)
66155324Simp{
67155324Simp	bus_write_4(sc->mem_res, off, val);
68155324Simp}
69155324Simp
70155324Simp/* bus entry points */
71155324Simpstatic int at91_spi_probe(device_t dev);
72155324Simpstatic int at91_spi_attach(device_t dev);
73155324Simpstatic int at91_spi_detach(device_t dev);
74155324Simp
75155324Simp/* helper routines */
76155324Simpstatic int at91_spi_activate(device_t dev);
77155324Simpstatic void at91_spi_deactivate(device_t dev);
78163526Simpstatic void at91_spi_intr(void *arg);
79155324Simp
80155324Simpstatic int
81155324Simpat91_spi_probe(device_t dev)
82155324Simp{
83155324Simp	device_set_desc(dev, "SPI");
84155324Simp	return (0);
85155324Simp}
86155324Simp
87155324Simpstatic int
88155324Simpat91_spi_attach(device_t dev)
89155324Simp{
90155324Simp	struct at91_spi_softc *sc = device_get_softc(dev);
91160358Simp	int err, i;
92155324Simp
93155324Simp	sc->dev = dev;
94155324Simp	err = at91_spi_activate(dev);
95155324Simp	if (err)
96155324Simp		goto out;
97155324Simp
98155324Simp	/*
99160358Simp	 * Allocate DMA tags and maps
100155324Simp	 */
101183670Simp	err = bus_dma_tag_create(bus_get_dma_tag(dev), 1, 0,
102183670Simp	    BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, 2058, 1,
103183670Simp	    2048, BUS_DMA_ALLOCNOW, NULL, NULL, &sc->dmatag);
104160358Simp	if (err != 0)
105155324Simp		goto out;
106160358Simp	for (i = 0; i < 4; i++) {
107160358Simp		err = bus_dmamap_create(sc->dmatag, 0,  &sc->map[i]);
108160358Simp		if (err != 0)
109160358Simp			goto out;
110155324Simp	}
111155324Simp
112160358Simp	// reset the SPI
113155324Simp	WR4(sc, SPI_CR, SPI_CR_SWRST);
114163526Simp	WR4(sc, SPI_IDR, 0xffffffff);
115160358Simp
116160358Simp	WR4(sc, SPI_MR, (0xf << 24) | SPI_MR_MSTR | SPI_MR_MODFDIS |
117160358Simp	    (0xE << 16));
118160358Simp
119160358Simp	WR4(sc, SPI_CSR0, SPI_CSR_CPOL | (4 << 16) | (2 << 8));
120160358Simp	WR4(sc, SPI_CR, SPI_CR_SPIEN);
121160358Simp
122160358Simp	WR4(sc, PDC_PTCR, PDC_PTCR_TXTDIS);
123160358Simp	WR4(sc, PDC_PTCR, PDC_PTCR_RXTDIS);
124160358Simp	WR4(sc, PDC_RNPR, 0);
125160358Simp	WR4(sc, PDC_RNCR, 0);
126160358Simp	WR4(sc, PDC_TNPR, 0);
127160358Simp	WR4(sc, PDC_TNCR, 0);
128160358Simp	WR4(sc, PDC_RPR, 0);
129160358Simp	WR4(sc, PDC_RCR, 0);
130160358Simp	WR4(sc, PDC_TPR, 0);
131160358Simp	WR4(sc, PDC_TCR, 0);
132160358Simp	RD4(sc, SPI_RDR);
133160358Simp	RD4(sc, SPI_SR);
134160358Simp
135160358Simp	device_add_child(dev, "spibus", -1);
136160358Simp	bus_generic_attach(dev);
137155324Simpout:;
138155324Simp	if (err)
139155324Simp		at91_spi_deactivate(dev);
140155324Simp	return (err);
141155324Simp}
142155324Simp
143155324Simpstatic int
144155324Simpat91_spi_detach(device_t dev)
145155324Simp{
146155324Simp	return (EBUSY);	/* XXX */
147155324Simp}
148155324Simp
149155324Simpstatic int
150155324Simpat91_spi_activate(device_t dev)
151155324Simp{
152155324Simp	struct at91_spi_softc *sc;
153163526Simp	int rid, err = ENOMEM;
154155324Simp
155155324Simp	sc = device_get_softc(dev);
156155324Simp	rid = 0;
157155324Simp	sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
158155324Simp	    RF_ACTIVE);
159155324Simp	if (sc->mem_res == NULL)
160155324Simp		goto errout;
161155324Simp	rid = 0;
162155324Simp	sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
163155324Simp	    RF_ACTIVE);
164163526Simp	if (sc->irq_res == NULL)
165155324Simp		goto errout;
166163526Simp	err = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC | INTR_MPSAFE,
167166901Spiso	    NULL, at91_spi_intr, sc, &sc->intrhand);
168163526Simp	if (err != 0)
169163526Simp		goto errout;
170155324Simp	return (0);
171155324Simperrout:
172155324Simp	at91_spi_deactivate(dev);
173163526Simp	return (err);
174155324Simp}
175155324Simp
176155324Simpstatic void
177155324Simpat91_spi_deactivate(device_t dev)
178155324Simp{
179155324Simp	struct at91_spi_softc *sc;
180155324Simp
181155324Simp	sc = device_get_softc(dev);
182155324Simp	if (sc->intrhand)
183155324Simp		bus_teardown_intr(dev, sc->irq_res, sc->intrhand);
184155324Simp	sc->intrhand = 0;
185155324Simp	bus_generic_detach(sc->dev);
186155324Simp	if (sc->mem_res)
187155324Simp		bus_release_resource(dev, SYS_RES_IOPORT,
188155324Simp		    rman_get_rid(sc->mem_res), sc->mem_res);
189155324Simp	sc->mem_res = 0;
190155324Simp	if (sc->irq_res)
191155324Simp		bus_release_resource(dev, SYS_RES_IRQ,
192155324Simp		    rman_get_rid(sc->irq_res), sc->irq_res);
193155324Simp	sc->irq_res = 0;
194155324Simp	return;
195155324Simp}
196155324Simp
197155324Simpstatic void
198160358Simpat91_getaddr(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
199155324Simp{
200160358Simp	if (error != 0)
201155324Simp		return;
202160358Simp	*(bus_addr_t *)arg = segs[0].ds_addr;
203155324Simp}
204155324Simp
205155324Simpstatic int
206160358Simpat91_spi_transfer(device_t dev, device_t child, struct spi_command *cmd)
207155324Simp{
208155324Simp	struct at91_spi_softc *sc;
209163526Simp	int i, j, rxdone, err, mode[4];
210160358Simp	bus_addr_t addr;
211155324Simp
212160358Simp	sc = device_get_softc(dev);
213160358Simp	WR4(sc, PDC_PTCR, PDC_PTCR_TXTDIS | PDC_PTCR_RXTDIS);
214160358Simp	i = 0;
215160358Simp	if (bus_dmamap_load(sc->dmatag, sc->map[i], cmd->tx_cmd,
216160358Simp	    cmd->tx_cmd_sz, at91_getaddr, &addr, 0) != 0)
217160358Simp		goto out;
218160358Simp	WR4(sc, PDC_TPR, addr);
219160358Simp	WR4(sc, PDC_TCR, cmd->tx_cmd_sz);
220160358Simp	bus_dmamap_sync(sc->dmatag, sc->map[i], BUS_DMASYNC_PREWRITE);
221163526Simp	mode[i++] = BUS_DMASYNC_POSTWRITE;
222163526Simp	if (cmd->tx_data_sz > 0) {
223163526Simp		if (bus_dmamap_load(sc->dmatag, sc->map[i], cmd->tx_data,
224163526Simp			cmd->tx_data_sz, at91_getaddr, &addr, 0) != 0)
225163526Simp			goto out;
226163526Simp		WR4(sc, PDC_TNPR, addr);
227164741Simp		WR4(sc, PDC_TNCR, cmd->tx_data_sz);
228163526Simp		bus_dmamap_sync(sc->dmatag, sc->map[i], BUS_DMASYNC_PREWRITE);
229163526Simp		mode[i++] = BUS_DMASYNC_POSTWRITE;
230163526Simp	}
231160358Simp	if (bus_dmamap_load(sc->dmatag, sc->map[i], cmd->rx_cmd,
232160358Simp	    cmd->tx_cmd_sz, at91_getaddr, &addr, 0) != 0)
233160358Simp		goto out;
234160358Simp	WR4(sc, PDC_RPR, addr);
235160358Simp	WR4(sc, PDC_RCR, cmd->tx_cmd_sz);
236160358Simp	bus_dmamap_sync(sc->dmatag, sc->map[i], BUS_DMASYNC_PREREAD);
237163526Simp	mode[i++] = BUS_DMASYNC_POSTREAD;
238164741Simp	if (cmd->rx_data_sz > 0) {
239163526Simp		if (bus_dmamap_load(sc->dmatag, sc->map[i], cmd->rx_data,
240163526Simp			cmd->tx_data_sz, at91_getaddr, &addr, 0) != 0)
241163526Simp			goto out;
242163526Simp		WR4(sc, PDC_RNPR, addr);
243164741Simp		WR4(sc, PDC_RNCR, cmd->rx_data_sz);
244163526Simp		bus_dmamap_sync(sc->dmatag, sc->map[i], BUS_DMASYNC_PREREAD);
245163526Simp		mode[i++] = BUS_DMASYNC_POSTREAD;
246163526Simp	}
247163526Simp	WR4(sc, SPI_IER, SPI_SR_ENDRX);
248160358Simp	WR4(sc, PDC_PTCR, PDC_PTCR_TXTEN | PDC_PTCR_RXTEN);
249155324Simp
250163526Simp	rxdone = sc->rxdone;
251163526Simp	do {
252167082Sjhb		err = tsleep(&sc->rxdone, PCATCH | PZERO, "spi", hz);
253163526Simp	} while (rxdone == sc->rxdone && err != EINTR);
254163526Simp	WR4(sc, PDC_PTCR, PDC_PTCR_TXTDIS | PDC_PTCR_RXTDIS);
255163526Simp	if (err == 0) {
256163526Simp		for (j = 0; j < i; j++)
257163526Simp			bus_dmamap_sync(sc->dmatag, sc->map[j], mode[j]);
258163526Simp	}
259163526Simp	for (j = 0; j < i; j++)
260163526Simp		bus_dmamap_unload(sc->dmatag, sc->map[j]);
261163526Simp	return (err);
262160358Simpout:;
263163526Simp	for (j = 0; j < i; j++)
264163526Simp		bus_dmamap_unload(sc->dmatag, sc->map[j]);
265160358Simp	return (EIO);
266155324Simp}
267155324Simp
268163526Simpstatic void
269163526Simpat91_spi_intr(void *arg)
270163526Simp{
271163526Simp	struct at91_spi_softc *sc = (struct at91_spi_softc*)arg;
272163526Simp	uint32_t sr;
273163526Simp
274163526Simp	sr = RD4(sc, SPI_SR) & RD4(sc, SPI_IMR);
275163526Simp	if (sr & SPI_SR_ENDRX) {
276163526Simp		sc->rxdone++;
277163526Simp		WR4(sc, SPI_IDR, SPI_SR_ENDRX);
278163526Simp		wakeup(&sc->rxdone);
279163526Simp	}
280163526Simp	if (sr & ~SPI_SR_ENDRX) {
281163526Simp		device_printf(sc->dev, "Unexpected ISR %#x\n", sr);
282163526Simp		WR4(sc, SPI_IDR, sr & ~SPI_SR_ENDRX);
283163526Simp	}
284163526Simp}
285163526Simp
286163526Simpstatic devclass_t at91_spi_devclass;
287163526Simp
288155324Simpstatic device_method_t at91_spi_methods[] = {
289155324Simp	/* Device interface */
290155324Simp	DEVMETHOD(device_probe,		at91_spi_probe),
291155324Simp	DEVMETHOD(device_attach,	at91_spi_attach),
292155324Simp	DEVMETHOD(device_detach,	at91_spi_detach),
293155324Simp
294160358Simp	/* spibus interface */
295160358Simp	DEVMETHOD(spibus_transfer,	at91_spi_transfer),
296155324Simp	{ 0, 0 }
297155324Simp};
298155324Simp
299155324Simpstatic driver_t at91_spi_driver = {
300155324Simp	"at91_spi",
301155324Simp	at91_spi_methods,
302155324Simp	sizeof(struct at91_spi_softc),
303155324Simp};
304155324Simp
305155324SimpDRIVER_MODULE(at91_spi, atmelarm, at91_spi_driver, at91_spi_devclass, 0, 0);
306