1/*-
2 * Copyright (c) 2017 Justin Hibbits <jhibbits@FreeBSD.org>
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#include <sys/cdefs.h>
28__FBSDID("$FreeBSD$");
29
30#include <sys/param.h>
31#include <sys/systm.h>
32#include <sys/bus.h>
33#include <sys/kernel.h>
34#include <sys/lock.h>
35#include <sys/malloc.h>
36#include <sys/module.h>
37#include <sys/mutex.h>
38
39#include <machine/bus.h>
40
41#include <dev/spibus/spi.h>
42#include <dev/spibus/spibusvar.h>
43
44#include <dev/ofw/ofw_bus.h>
45#include <dev/ofw/ofw_bus_subr.h>
46
47#include <powerpc/mpc85xx/mpc85xx.h>
48
49#include "spibus_if.h"
50
51/* TODO:
52 *
53 * Optimize FIFO reads and writes to do word-at-a-time instead of byte-at-a-time
54 */
55#define	ESPI_SPMODE	0x0
56#define	  ESPI_SPMODE_EN	  0x80000000
57#define	  ESPI_SPMODE_LOOP	  0x40000000
58#define	  ESPI_SPMODE_HO_ADJ_M	  0x00070000
59#define	  ESPI_SPMODE_TXTHR_M	  0x00003f00
60#define	  ESPI_SPMODE_TXTHR_S	  8
61#define	  ESPI_SPMODE_RXTHR_M	  0x0000001f
62#define	  ESPI_SPMODE_RXTHR_S	  0
63#define	ESPI_SPIE	0x4
64#define	  ESPI_SPIE_RXCNT_M	  0x3f000000
65#define	  ESPI_SPIE_RXCNT_S	  24
66#define	  ESPI_SPIE_TXCNT_M	  0x003f0000
67#define	  ESPI_SPIE_TXCNT_S	  16
68#define	  ESPI_SPIE_TXE		  0x00008000
69#define	  ESPI_SPIE_DON		  0x00004000
70#define	  ESPI_SPIE_RXT		  0x00002000
71#define	  ESPI_SPIE_RXF		  0x00001000
72#define	  ESPI_SPIE_TXT		  0x00000800
73#define	  ESPI_SPIE_RNE		  0x00000200
74#define	  ESPI_SPIE_TNF		  0x00000100
75#define	ESPI_SPIM	0x8
76#define	ESPI_SPCOM	0xc
77#define	  ESPI_SPCOM_CS_M	  0xc0000000
78#define	  ESPI_SPCOM_CS_S	  30
79#define	  ESPI_SPCOM_RXDELAY	  0x20000000
80#define	  ESPI_SPCOM_DO		  0x10000000
81#define	  ESPI_SPCOM_TO		  0x08000000
82#define	  ESPI_SPCOM_HLD	  0x04000000
83#define	  ESPI_SPCOM_RXSKIP_M	  0x00ff0000
84#define	  ESPI_SPCOM_TRANLEN_M	  0x0000ffff
85#define	ESPI_SPITF	0x10
86#define	ESPI_SPIRF	0x14
87#define	ESPI_SPMODE0	0x20
88#define	ESPI_SPMODE1	0x24
89#define	ESPI_SPMODE2	0x28
90#define	ESPI_SPMODE3	0x2c
91#define	  ESPI_CSMODE_CI	  0x80000000
92#define	  ESPI_CSMODE_CP	  0x40000000
93#define	  ESPI_CSMODE_REV	  0x20000000
94#define	  ESPI_CSMODE_DIV16	  0x10000000
95#define	  ESPI_CSMODE_PM_M	  0x0f000000
96#define	  ESPI_CSMODE_PM_S	  24
97#define	  ESPI_CSMODE_ODD	  0x00800000
98#define	  ESPI_CSMODE_POL	  0x00100000
99#define	  ESPI_CSMODE_LEN_M	  0x000f0000
100#define	    ESPI_CSMODE_LEN(x)	    (x << 16)
101#define	  ESPI_CSMODE_CSBEF_M	  0x0000f000
102#define	  ESPI_CSMODE_CSAFT_M	  0x00000f00
103#define	  ESPI_CSMODE_CSCG_M	  0x000000f8
104#define	    ESPI_CSMODE_CSCG(x)	    (x << 3)
105#define	ESPI_CSMODE(n)		(ESPI_SPMODE0 + n * 4)
106
107#define	FSL_ESPI_WRITE(sc,off,val)	bus_write_4(sc->sc_mem_res, off, val)
108#define	FSL_ESPI_READ(sc,off)		bus_read_4(sc->sc_mem_res, off)
109#define	FSL_ESPI_WRITE_FIFO(sc,off,val)	bus_write_1(sc->sc_mem_res, off, val)
110#define	FSL_ESPI_READ_FIFO(sc,off)	bus_read_1(sc->sc_mem_res, off)
111
112#define FSL_ESPI_LOCK(_sc)			\
113    mtx_lock(&(_sc)->sc_mtx)
114#define FSL_ESPI_UNLOCK(_sc)			\
115    mtx_unlock(&(_sc)->sc_mtx)
116
117struct fsl_espi_softc
118{
119	device_t		sc_dev;
120	struct resource		*sc_mem_res;
121	struct resource		*sc_irq_res;
122	struct mtx		sc_mtx;
123	int			sc_num_cs;
124	struct spi_command	*sc_cmd;
125	uint32_t		sc_len;
126	uint32_t		sc_read;
127	uint32_t		sc_flags;
128#define	  FSL_ESPI_BUSY		  0x00000001
129	uint32_t		sc_written;
130	void *			sc_intrhand;
131};
132
133static void fsl_espi_intr(void *);
134
135static int
136fsl_espi_probe(device_t dev)
137{
138
139	if (!ofw_bus_status_okay(dev))
140		return (ENXIO);
141
142	if (!ofw_bus_is_compatible(dev, "fsl,mpc8536-espi"))
143		return (ENXIO);
144
145	device_set_desc(dev, "Freescale eSPI controller");
146
147	return (BUS_PROBE_DEFAULT);
148}
149
150static int
151fsl_espi_attach(device_t dev)
152{
153	struct fsl_espi_softc *sc;
154	int rid;
155	phandle_t node;
156
157	sc = device_get_softc(dev);
158	sc->sc_dev = dev;
159	node = ofw_bus_get_node(dev);
160
161	rid = 0;
162	sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
163	    RF_ACTIVE);
164	if (!sc->sc_mem_res) {
165		device_printf(dev, "cannot allocate memory resource\n");
166		return (ENXIO);
167	}
168
169	rid = 0;
170	sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
171	    RF_ACTIVE);
172	if (!sc->sc_irq_res) {
173		bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res);
174		device_printf(dev, "cannot allocate interrupt\n");
175		return (ENXIO);
176	}
177
178	/* Hook up our interrupt handler. */
179	if (bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_MISC | INTR_MPSAFE,
180	    NULL, fsl_espi_intr, sc, &sc->sc_intrhand)) {
181		bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res);
182		bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res);
183		device_printf(dev, "cannot setup the interrupt handler\n");
184		return (ENXIO);
185	}
186	if (OF_getencprop(node, "fsl,espi-num-chipselects",
187	    &sc->sc_num_cs, sizeof(sc->sc_num_cs)) < 0 )
188		sc->sc_num_cs = 4;
189
190	mtx_init(&sc->sc_mtx, "fsl_espi", NULL, MTX_DEF);
191
192	/* Enable the SPI controller.  */
193	FSL_ESPI_WRITE(sc, ESPI_SPMODE, ESPI_SPMODE_EN |
194	    (16 << ESPI_SPMODE_TXTHR_S) | (15 << ESPI_SPMODE_RXTHR_S));
195
196	/* Disable all interrupts until we start transfers  */
197	FSL_ESPI_WRITE(sc, ESPI_SPIM, 0);
198
199	device_add_child(dev, "spibus", -1);
200
201	return (bus_generic_attach(dev));
202}
203
204static int
205fsl_espi_detach(device_t dev)
206{
207	struct fsl_espi_softc *sc;
208
209	bus_generic_detach(dev);
210
211	sc = device_get_softc(dev);
212	FSL_ESPI_WRITE(sc, ESPI_SPMODE, 0);
213
214	sc = device_get_softc(dev);
215	mtx_destroy(&sc->sc_mtx);
216	if (sc->sc_intrhand)
217		bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_intrhand);
218	if (sc->sc_irq_res)
219		bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res);
220	if (sc->sc_mem_res)
221		bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res);
222
223	return (0);
224}
225
226static void
227fsl_espi_fill_fifo(struct fsl_espi_softc *sc)
228{
229	struct spi_command *cmd;
230	uint32_t spier, written;
231	uint8_t *data;
232
233	cmd = sc->sc_cmd;
234	spier = FSL_ESPI_READ(sc, ESPI_SPIE);
235	while (sc->sc_written < sc->sc_len &&
236	    (spier & ESPI_SPIE_TNF)) {
237		data = (uint8_t *)cmd->tx_cmd;
238		written = sc->sc_written++;
239		if (written >= cmd->tx_cmd_sz) {
240			data = (uint8_t *)cmd->tx_data;
241			written -= cmd->tx_cmd_sz;
242		}
243		FSL_ESPI_WRITE_FIFO(sc, ESPI_SPITF, data[written]);
244		spier = FSL_ESPI_READ(sc, ESPI_SPIE);
245	}
246}
247
248static void
249fsl_espi_drain_fifo(struct fsl_espi_softc *sc)
250{
251	struct spi_command *cmd;
252	uint32_t spier, read;
253	uint8_t *data;
254	uint8_t r;
255
256	cmd = sc->sc_cmd;
257	spier = FSL_ESPI_READ(sc, ESPI_SPIE);
258	while (sc->sc_read < sc->sc_len && (spier & ESPI_SPIE_RNE)) {
259		data = (uint8_t *)cmd->rx_cmd;
260		read = sc->sc_read++;
261		if (read >= cmd->rx_cmd_sz) {
262			data = (uint8_t *)cmd->rx_data;
263			read -= cmd->rx_cmd_sz;
264		}
265		r = FSL_ESPI_READ_FIFO(sc, ESPI_SPIRF);
266		data[read] = r;
267		spier = FSL_ESPI_READ(sc, ESPI_SPIE);
268	}
269}
270
271static void
272fsl_espi_intr(void *arg)
273{
274	struct fsl_espi_softc *sc;
275	uint32_t spie;
276
277	sc = (struct fsl_espi_softc *)arg;
278	FSL_ESPI_LOCK(sc);
279
280	/* Filter stray interrupts. */
281	if ((sc->sc_flags & FSL_ESPI_BUSY) == 0) {
282		FSL_ESPI_UNLOCK(sc);
283		return;
284	}
285	spie = FSL_ESPI_READ(sc, ESPI_SPIE);
286	FSL_ESPI_WRITE(sc, ESPI_SPIE, spie);
287
288	/* TX - Fill up the FIFO. */
289	fsl_espi_fill_fifo(sc);
290
291	/* RX - Drain the FIFO. */
292	fsl_espi_drain_fifo(sc);
293
294	/* Check for end of transfer. */
295	if (spie & ESPI_SPIE_DON)
296		wakeup(sc->sc_dev);
297
298	FSL_ESPI_UNLOCK(sc);
299}
300
301static int
302fsl_espi_transfer(device_t dev, device_t child, struct spi_command *cmd)
303{
304	struct fsl_espi_softc *sc;
305	u_long plat_clk;
306	uint32_t csmode, spi_clk, spi_mode;
307	int cs, err, pm;
308
309	sc = device_get_softc(dev);
310
311	KASSERT(cmd->tx_cmd_sz == cmd->rx_cmd_sz,
312	    ("TX/RX command sizes should be equal"));
313	KASSERT(cmd->tx_data_sz == cmd->rx_data_sz,
314	    ("TX/RX data sizes should be equal"));
315
316	/* Restrict transmit length to command max length */
317	if (cmd->tx_cmd_sz + cmd->tx_data_sz > ESPI_SPCOM_TRANLEN_M + 1) {
318		return (EINVAL);
319	}
320
321	/* Get the proper chip select for this child. */
322	spibus_get_cs(child, &cs);
323	if (cs < 0 || cs > sc->sc_num_cs) {
324		device_printf(dev,
325		    "Invalid chip select %d requested by %s\n", cs,
326		    device_get_nameunit(child));
327		return (EINVAL);
328	}
329	spibus_get_clock(child, &spi_clk);
330	spibus_get_mode(child, &spi_mode);
331
332	FSL_ESPI_LOCK(sc);
333
334	/* If the controller is in use wait until it is available. */
335	while (sc->sc_flags & FSL_ESPI_BUSY)
336		mtx_sleep(dev, &sc->sc_mtx, 0, "fsl_espi", 0);
337
338	/* Now we have control over SPI controller. */
339	sc->sc_flags = FSL_ESPI_BUSY;
340
341	/* Save a pointer to the SPI command. */
342	sc->sc_cmd = cmd;
343	sc->sc_read = 0;
344	sc->sc_written = 0;
345	sc->sc_len = cmd->tx_cmd_sz + cmd->tx_data_sz;
346
347	plat_clk = mpc85xx_get_system_clock();
348	spi_clk = max(spi_clk, plat_clk / (16 * 16));
349	if (plat_clk == 0) {
350		device_printf(dev,
351		    "unable to get platform clock, giving up.\n");
352		return (EINVAL);
353	}
354	csmode = 0;
355	if (plat_clk > spi_clk * 16 * 2) {
356		csmode |= ESPI_CSMODE_DIV16;
357		plat_clk /= 16;
358	}
359	pm = howmany(plat_clk, spi_clk * 2) - 1;
360	if (pm < 0)
361		pm = 1;
362	if (pm > 15)
363		pm = 15;
364
365	csmode |= (pm << ESPI_CSMODE_PM_S);
366	csmode |= ESPI_CSMODE_REV;
367	if (spi_mode == SPIBUS_MODE_CPOL || spi_mode == SPIBUS_MODE_CPOL_CPHA)
368		csmode |= ESPI_CSMODE_CI;
369	if (spi_mode == SPIBUS_MODE_CPHA || spi_mode == SPIBUS_MODE_CPOL_CPHA)
370		csmode |= ESPI_CSMODE_CP;
371	if (!(cs & SPIBUS_CS_HIGH))
372		csmode |= ESPI_CSMODE_POL;
373	csmode |= ESPI_CSMODE_LEN(7);/* Only deal with 8-bit characters. */
374	csmode |= ESPI_CSMODE_CSCG(1); /* XXX: Make this configurable? */
375	/* Configure transaction */
376	FSL_ESPI_WRITE(sc, ESPI_SPCOM, (cs << ESPI_SPCOM_CS_S) | (sc->sc_len - 1));
377	FSL_ESPI_WRITE(sc, ESPI_CSMODE(cs), csmode);
378	/* Enable interrupts we need. */
379	FSL_ESPI_WRITE(sc, ESPI_SPIM,
380	    ESPI_SPIE_TXE | ESPI_SPIE_DON | ESPI_SPIE_RXF);
381
382	/* Wait for the transaction to complete. */
383	err = mtx_sleep(dev, &sc->sc_mtx, 0, "fsl_espi", hz * 2);
384	FSL_ESPI_WRITE(sc, ESPI_SPIM, 0);
385
386	/* Release the controller and wakeup the next thread waiting for it. */
387	sc->sc_flags = 0;
388	wakeup_one(dev);
389	FSL_ESPI_UNLOCK(sc);
390
391	/*
392	 * Check for transfer timeout.  The SPI controller doesn't
393	 * return errors.
394	 */
395	if (err == EWOULDBLOCK) {
396		device_printf(sc->sc_dev, "SPI error\n");
397		err = EIO;
398	}
399
400	return (err);
401}
402
403static phandle_t
404fsl_espi_get_node(device_t bus, device_t dev)
405{
406
407	/* We only have one child, the SPI bus, which needs our own node. */
408	return (ofw_bus_get_node(bus));
409}
410
411static device_method_t fsl_espi_methods[] = {
412	/* Device interface */
413	DEVMETHOD(device_probe,		fsl_espi_probe),
414	DEVMETHOD(device_attach,	fsl_espi_attach),
415	DEVMETHOD(device_detach,	fsl_espi_detach),
416
417	/* SPI interface */
418	DEVMETHOD(spibus_transfer,	fsl_espi_transfer),
419
420	/* ofw_bus interface */
421	DEVMETHOD(ofw_bus_get_node,	fsl_espi_get_node),
422
423	DEVMETHOD_END
424};
425
426static devclass_t fsl_espi_devclass;
427
428static driver_t fsl_espi_driver = {
429	"spi",
430	fsl_espi_methods,
431	sizeof(struct fsl_espi_softc),
432};
433
434DRIVER_MODULE(fsl_espi, simplebus, fsl_espi_driver, fsl_espi_devclass, 0, 0);
435