exynos5_spi.c revision 331506
1/*-
2 * Copyright (c) 2014 Ruslan Bukin <br@bsdpad.com>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27/*
28 * Exynos 5 Serial Peripheral Interface (SPI)
29 */
30
31#include <sys/cdefs.h>
32__FBSDID("$FreeBSD: stable/11/sys/arm/samsung/exynos/exynos5_spi.c 331506 2018-03-24 23:23:31Z ian $");
33
34#include <sys/param.h>
35#include <sys/systm.h>
36#include <sys/bus.h>
37#include <sys/kernel.h>
38#include <sys/module.h>
39#include <sys/malloc.h>
40#include <sys/rman.h>
41#include <sys/timeet.h>
42#include <sys/timetc.h>
43#include <sys/watchdog.h>
44
45#include <dev/spibus/spi.h>
46#include <dev/spibus/spibusvar.h>
47
48#include "spibus_if.h"
49
50#include <dev/ofw/openfirm.h>
51#include <dev/ofw/ofw_bus.h>
52#include <dev/ofw/ofw_bus_subr.h>
53
54#include <machine/bus.h>
55#include <machine/cpu.h>
56#include <machine/intr.h>
57
58#include <arm/samsung/exynos/exynos5_common.h>
59
60#define	CH_CFG		0x00		/* SPI configuration */
61#define	 SW_RST		(1 << 5)	/* Reset */
62#define	 RX_CH_ON	(1 << 1)	/* SPI Rx Channel On */
63#define	 TX_CH_ON	(1 << 0)	/* SPI Tx Channel On */
64#define	MODE_CFG	0x08		/* FIFO control */
65#define	CS_REG		0x0C		/* slave selection control */
66#define	 NSSOUT		(1 << 0)
67#define	SPI_INT_EN	0x10		/* interrupt enable */
68#define	SPI_STATUS	0x14		/* SPI status */
69#define	 TX_FIFO_LVL_S	6
70#define	 TX_FIFO_LVL_M	0x1ff
71#define	 RX_FIFO_LVL_S	15
72#define	 RX_FIFO_LVL_M	0x1ff
73#define	SPI_TX_DATA	0x18		/* Tx data */
74#define	SPI_RX_DATA	0x1C		/* Rx data */
75#define	PACKET_CNT_REG	0x20		/* packet count */
76#define	PENDING_CLR_REG	0x24		/* interrupt pending clear */
77#define	SWAP_CFG	0x28		/* swap configuration */
78#define	FB_CLK_SEL	0x2C		/* feedback clock selection */
79#define	 FB_CLK_180	0x2		/* 180 degree phase lagging */
80
81struct spi_softc {
82	struct resource		*res[2];
83	bus_space_tag_t		bst;
84	bus_space_handle_t	bsh;
85	device_t		dev;
86};
87
88struct spi_softc *spi_sc;
89
90static struct resource_spec spi_spec[] = {
91	{ SYS_RES_MEMORY,	0,	RF_ACTIVE },
92	{ SYS_RES_IRQ,		0,	RF_ACTIVE },
93	{ -1, 0 }
94};
95
96static int
97spi_probe(device_t dev)
98{
99
100	if (!ofw_bus_status_okay(dev))
101		return (ENXIO);
102
103	if (!ofw_bus_is_compatible(dev, "samsung,exynos5-spi"))
104		return (ENXIO);
105
106	device_set_desc(dev, "Exynos 5 Serial Peripheral Interface (SPI)");
107	return (BUS_PROBE_DEFAULT);
108}
109
110static int
111spi_attach(device_t dev)
112{
113	struct spi_softc *sc;
114	int reg;
115
116	sc = device_get_softc(dev);
117	sc->dev = dev;
118
119	if (bus_alloc_resources(dev, spi_spec, sc->res)) {
120		device_printf(dev, "could not allocate resources\n");
121		return (ENXIO);
122	}
123
124	/* Memory interface */
125	sc->bst = rman_get_bustag(sc->res[0]);
126	sc->bsh = rman_get_bushandle(sc->res[0]);
127
128	spi_sc = sc;
129
130	WRITE4(sc, FB_CLK_SEL, FB_CLK_180);
131
132	reg = READ4(sc, CH_CFG);
133	reg |= (RX_CH_ON | TX_CH_ON);
134	WRITE4(sc, CH_CFG, reg);
135
136	device_add_child(dev, "spibus", 0);
137	return (bus_generic_attach(dev));
138}
139
140static int
141spi_txrx(struct spi_softc *sc, uint8_t *out_buf,
142    uint8_t *in_buf, int bufsz, int cs)
143{
144	uint32_t reg;
145	uint32_t i;
146
147	if (bufsz == 0) {
148		/* Nothing to transfer */
149		return (0);
150	}
151
152	/* Reset registers */
153	reg = READ4(sc, CH_CFG);
154	reg |= SW_RST;
155	WRITE4(sc, CH_CFG, reg);
156	reg &= ~SW_RST;
157	WRITE4(sc, CH_CFG, reg);
158
159	/* Assert CS */
160	reg = READ4(sc, CS_REG);
161	reg &= ~NSSOUT;
162	WRITE4(sc, CS_REG, reg);
163
164	for (i = 0; i < bufsz; i++) {
165
166		/* TODO: Implement FIFO operation */
167
168		/* Wait all the data shifted out */
169		while (READ4(sc, SPI_STATUS) & \
170		    (TX_FIFO_LVL_M << TX_FIFO_LVL_S))
171			continue;
172
173		WRITE1(sc, SPI_TX_DATA, out_buf[i]);
174
175		/* Wait until no data available */
176		while ((READ4(sc, SPI_STATUS) & \
177			(RX_FIFO_LVL_M << RX_FIFO_LVL_S)) == 0)
178			continue;
179
180		in_buf[i] = READ1(sc, SPI_RX_DATA);
181	}
182
183	/* Deassert CS */
184	reg = READ4(sc, CS_REG);
185	reg |= NSSOUT;
186	WRITE4(sc, CS_REG, reg);
187
188	return (0);
189}
190
191static int
192spi_transfer(device_t dev, device_t child, struct spi_command *cmd)
193{
194	struct spi_softc *sc;
195	uint32_t cs;
196
197	sc = device_get_softc(dev);
198
199	KASSERT(cmd->tx_cmd_sz == cmd->rx_cmd_sz,
200	    ("%s: TX/RX command sizes should be equal", __func__));
201	KASSERT(cmd->tx_data_sz == cmd->rx_data_sz,
202	    ("%s: TX/RX data sizes should be equal", __func__));
203
204	/* get the proper chip select */
205	spibus_get_cs(child, &cs);
206
207	cs &= ~SPIBUS_CS_HIGH;
208
209	/* Command */
210	spi_txrx(sc, cmd->tx_cmd, cmd->rx_cmd, cmd->tx_cmd_sz, cs);
211
212	/* Data */
213	spi_txrx(sc, cmd->tx_data, cmd->rx_data, cmd->tx_data_sz, cs);
214
215	return (0);
216}
217
218static device_method_t spi_methods[] = {
219	DEVMETHOD(device_probe,		spi_probe),
220	DEVMETHOD(device_attach,	spi_attach),
221
222	/* SPI interface */
223	DEVMETHOD(spibus_transfer,	spi_transfer),
224
225	{ 0, 0 }
226};
227
228static driver_t spi_driver = {
229	"spi",
230	spi_methods,
231	sizeof(struct spi_softc),
232};
233
234static devclass_t spi_devclass;
235
236DRIVER_MODULE(spi, simplebus, spi_driver, spi_devclass, 0, 0);
237