1192357Sgonzo/*-
2192357Sgonzo * Copyright (c) 2009, Oleksandr Tymoshenko <gonzo@FreeBSD.org>
3192357Sgonzo * All rights reserved.
4192357Sgonzo *
5192357Sgonzo * Redistribution and use in source and binary forms, with or without
6192357Sgonzo * modification, are permitted provided that the following conditions
7192357Sgonzo * are met:
8192357Sgonzo * 1. Redistributions of source code must retain the above copyright
9192357Sgonzo *    notice unmodified, this list of conditions, and the following
10192357Sgonzo *    disclaimer.
11192357Sgonzo * 2. Redistributions in binary form must reproduce the above copyright
12192357Sgonzo *    notice, this list of conditions and the following disclaimer in the
13192357Sgonzo *    documentation and/or other materials provided with the distribution.
14192357Sgonzo *
15192357Sgonzo * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16192357Sgonzo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17192357Sgonzo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18192357Sgonzo * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19192357Sgonzo * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20192357Sgonzo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21192357Sgonzo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22192357Sgonzo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23192357Sgonzo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24192357Sgonzo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25192357Sgonzo * SUCH DAMAGE.
26192357Sgonzo */
27192357Sgonzo
28192357Sgonzo#include <sys/cdefs.h>
29192357Sgonzo__FBSDID("$FreeBSD$");
30192357Sgonzo
31192357Sgonzo#include <sys/param.h>
32192357Sgonzo#include <sys/systm.h>
33192357Sgonzo
34192357Sgonzo#include <sys/bus.h>
35192357Sgonzo#include <sys/interrupt.h>
36192357Sgonzo#include <sys/malloc.h>
37192357Sgonzo#include <sys/kernel.h>
38192357Sgonzo#include <sys/module.h>
39192357Sgonzo#include <sys/rman.h>
40192357Sgonzo
41192357Sgonzo#include <vm/vm.h>
42192357Sgonzo#include <vm/pmap.h>
43192357Sgonzo#include <vm/vm_extern.h>
44192357Sgonzo
45192357Sgonzo#include <machine/bus.h>
46192357Sgonzo#include <machine/cpu.h>
47192357Sgonzo#include <machine/pmap.h>
48192357Sgonzo
49192357Sgonzo#include <dev/spibus/spi.h>
50192357Sgonzo#include <dev/spibus/spibusvar.h>
51192357Sgonzo#include "spibus_if.h"
52192357Sgonzo
53192357Sgonzo#include <mips/atheros/ar71xxreg.h>
54192357Sgonzo
55192357Sgonzo#undef AR71XX_SPI_DEBUG
56192357Sgonzo#ifdef AR71XX_SPI_DEBUG
57192357Sgonzo#define dprintf printf
58192357Sgonzo#else
59192357Sgonzo#define dprintf(x, arg...)
60192357Sgonzo#endif
61192357Sgonzo
62192357Sgonzo/*
63192357Sgonzo * register space access macros
64192357Sgonzo */
65192357Sgonzo#define SPI_WRITE(sc, reg, val)	do {	\
66192357Sgonzo		bus_write_4(sc->sc_mem_res, (reg), (val)); \
67192357Sgonzo	} while (0)
68192357Sgonzo
69192357Sgonzo#define SPI_READ(sc, reg)	 bus_read_4(sc->sc_mem_res, (reg))
70192357Sgonzo
71192357Sgonzo#define SPI_SET_BITS(sc, reg, bits)	\
72192357Sgonzo	SPI_WRITE(sc, reg, SPI_READ(sc, (reg)) | (bits))
73192357Sgonzo
74192357Sgonzo#define SPI_CLEAR_BITS(sc, reg, bits)	\
75192357Sgonzo	SPI_WRITE(sc, reg, SPI_READ(sc, (reg)) & ~(bits))
76192357Sgonzo
77192357Sgonzostruct ar71xx_spi_softc {
78192357Sgonzo	device_t		sc_dev;
79192357Sgonzo	struct resource		*sc_mem_res;
80202723Sgonzo	uint32_t		sc_reg_ctrl;
81192357Sgonzo};
82192357Sgonzo
83192357Sgonzostatic int
84192357Sgonzoar71xx_spi_probe(device_t dev)
85192357Sgonzo{
86192357Sgonzo	device_set_desc(dev, "AR71XX SPI");
87192357Sgonzo	return (0);
88192357Sgonzo}
89192357Sgonzo
90192357Sgonzostatic int
91192357Sgonzoar71xx_spi_attach(device_t dev)
92192357Sgonzo{
93192357Sgonzo	struct ar71xx_spi_softc *sc = device_get_softc(dev);
94192357Sgonzo	int rid;
95192357Sgonzo
96192357Sgonzo	sc->sc_dev = dev;
97192357Sgonzo        rid = 0;
98192357Sgonzo	sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
99192357Sgonzo	    RF_ACTIVE);
100192357Sgonzo	if (!sc->sc_mem_res) {
101192357Sgonzo		device_printf(dev, "Could not map memory\n");
102192357Sgonzo		return (ENXIO);
103192357Sgonzo	}
104192357Sgonzo
105192357Sgonzo
106202723Sgonzo	SPI_WRITE(sc, AR71XX_SPI_FS, 1);
107202723Sgonzo	sc->sc_reg_ctrl  = SPI_READ(sc, AR71XX_SPI_CTRL);
108202723Sgonzo	SPI_WRITE(sc, AR71XX_SPI_CTRL, 0x43);
109202723Sgonzo	SPI_WRITE(sc, AR71XX_SPI_IO_CTRL, SPI_IO_CTRL_CSMASK);
110192357Sgonzo
111192357Sgonzo	device_add_child(dev, "spibus", 0);
112192357Sgonzo	return (bus_generic_attach(dev));
113192357Sgonzo}
114192357Sgonzo
115192357Sgonzostatic void
116192357Sgonzoar71xx_spi_chip_activate(struct ar71xx_spi_softc *sc, int cs)
117192357Sgonzo{
118202723Sgonzo	uint32_t ioctrl = SPI_IO_CTRL_CSMASK;
119192357Sgonzo	/*
120192357Sgonzo	 * Put respective CSx to low
121192357Sgonzo	 */
122192357Sgonzo	ioctrl &= ~(SPI_IO_CTRL_CS0 << cs);
123192357Sgonzo
124192357Sgonzo	SPI_WRITE(sc, AR71XX_SPI_IO_CTRL, ioctrl);
125192357Sgonzo}
126192357Sgonzo
127192357Sgonzostatic void
128192357Sgonzoar71xx_spi_chip_deactivate(struct ar71xx_spi_softc *sc, int cs)
129192357Sgonzo{
130192357Sgonzo	/*
131192357Sgonzo	 * Put all CSx to high
132192357Sgonzo	 */
133202723Sgonzo	SPI_WRITE(sc, AR71XX_SPI_IO_CTRL, SPI_IO_CTRL_CSMASK);
134192357Sgonzo}
135192357Sgonzo
136192357Sgonzostatic uint8_t
137202723Sgonzoar71xx_spi_txrx(struct ar71xx_spi_softc *sc, int cs, uint8_t data)
138192357Sgonzo{
139192357Sgonzo	int bit;
140192357Sgonzo	/* CS0 */
141202723Sgonzo	uint32_t ioctrl = SPI_IO_CTRL_CSMASK;
142202723Sgonzo	/*
143202723Sgonzo	 * low-level for selected CS
144202723Sgonzo	 */
145202723Sgonzo	ioctrl &= ~(SPI_IO_CTRL_CS0 << cs);
146192357Sgonzo
147192357Sgonzo	uint32_t iod, rds;
148192357Sgonzo	for (bit = 7; bit >=0; bit--) {
149192357Sgonzo		if (data & (1 << bit))
150192357Sgonzo			iod = ioctrl | SPI_IO_CTRL_DO;
151192357Sgonzo		else
152192357Sgonzo			iod = ioctrl & ~SPI_IO_CTRL_DO;
153192357Sgonzo		SPI_WRITE(sc, AR71XX_SPI_IO_CTRL, iod);
154192357Sgonzo		SPI_WRITE(sc, AR71XX_SPI_IO_CTRL, iod | SPI_IO_CTRL_CLK);
155192357Sgonzo	}
156192357Sgonzo
157202723Sgonzo	/*
158202723Sgonzo	 * Provide falling edge for connected device by clear clock bit.
159202723Sgonzo	 */
160202723Sgonzo	SPI_WRITE(sc, AR71XX_SPI_IO_CTRL, iod);
161192357Sgonzo	rds = SPI_READ(sc, AR71XX_SPI_RDS);
162192357Sgonzo
163192357Sgonzo	return (rds & 0xff);
164192357Sgonzo}
165192357Sgonzo
166192357Sgonzostatic int
167192357Sgonzoar71xx_spi_transfer(device_t dev, device_t child, struct spi_command *cmd)
168192357Sgonzo{
169192357Sgonzo	struct ar71xx_spi_softc *sc;
170192357Sgonzo	uint8_t *buf_in, *buf_out;
171192357Sgonzo	struct spibus_ivar *devi = SPIBUS_IVAR(child);
172192357Sgonzo	int i;
173192357Sgonzo
174192357Sgonzo	sc = device_get_softc(dev);
175192357Sgonzo
176192357Sgonzo	ar71xx_spi_chip_activate(sc, devi->cs);
177192357Sgonzo
178192357Sgonzo	KASSERT(cmd->tx_cmd_sz == cmd->rx_cmd_sz,
179192357Sgonzo	    ("TX/RX command sizes should be equal"));
180192357Sgonzo	KASSERT(cmd->tx_data_sz == cmd->rx_data_sz,
181192357Sgonzo	    ("TX/RX data sizes should be equal"));
182192357Sgonzo
183192357Sgonzo	/*
184192357Sgonzo	 * Transfer command
185192357Sgonzo	 */
186192357Sgonzo	buf_out = (uint8_t *)cmd->tx_cmd;
187192357Sgonzo	buf_in = (uint8_t *)cmd->rx_cmd;
188192357Sgonzo	for (i = 0; i < cmd->tx_cmd_sz; i++)
189202723Sgonzo		buf_in[i] = ar71xx_spi_txrx(sc, devi->cs, buf_out[i]);
190192357Sgonzo
191192357Sgonzo	/*
192192357Sgonzo	 * Receive/transmit data (depends on  command)
193192357Sgonzo	 */
194192357Sgonzo	buf_out = (uint8_t *)cmd->tx_data;
195192357Sgonzo	buf_in = (uint8_t *)cmd->rx_data;
196192357Sgonzo	for (i = 0; i < cmd->tx_data_sz; i++)
197202723Sgonzo		buf_in[i] = ar71xx_spi_txrx(sc, devi->cs, buf_out[i]);
198192357Sgonzo
199192357Sgonzo	ar71xx_spi_chip_deactivate(sc, devi->cs);
200192357Sgonzo
201192357Sgonzo	return (0);
202192357Sgonzo}
203192357Sgonzo
204192357Sgonzostatic int
205192357Sgonzoar71xx_spi_detach(device_t dev)
206192357Sgonzo{
207202723Sgonzo	struct ar71xx_spi_softc *sc = device_get_softc(dev);
208192357Sgonzo
209202723Sgonzo	SPI_WRITE(sc, AR71XX_SPI_CTRL, sc->sc_reg_ctrl);
210202723Sgonzo	SPI_WRITE(sc, AR71XX_SPI_FS, 0);
211202723Sgonzo
212202723Sgonzo	if (sc->sc_mem_res)
213202723Sgonzo		bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res);
214202723Sgonzo
215202723Sgonzo	return (0);
216192357Sgonzo}
217192357Sgonzo
218192357Sgonzostatic device_method_t ar71xx_spi_methods[] = {
219192357Sgonzo	/* Device interface */
220192357Sgonzo	DEVMETHOD(device_probe,		ar71xx_spi_probe),
221192357Sgonzo	DEVMETHOD(device_attach,	ar71xx_spi_attach),
222192357Sgonzo	DEVMETHOD(device_detach,	ar71xx_spi_detach),
223192357Sgonzo
224192357Sgonzo	DEVMETHOD(spibus_transfer,	ar71xx_spi_transfer),
225192357Sgonzo
226192357Sgonzo	{0, 0}
227192357Sgonzo};
228192357Sgonzo
229192357Sgonzostatic driver_t ar71xx_spi_driver = {
230192357Sgonzo	"spi",
231192357Sgonzo	ar71xx_spi_methods,
232192357Sgonzo	sizeof(struct ar71xx_spi_softc),
233192357Sgonzo};
234192357Sgonzo
235192357Sgonzostatic devclass_t ar71xx_spi_devclass;
236192357Sgonzo
237192357SgonzoDRIVER_MODULE(ar71xx_spi, nexus, ar71xx_spi_driver, ar71xx_spi_devclass, 0, 0);
238