1/*-
2 * Copyright (c) 2016 Michael Zhilin <mizhka@gmail.com>
3 * Copyright (c) 2016 Landon Fuller <landonf@FreeBSD.org>
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer,
11 *    without modification.
12 * 2. Redistributions in binary form must reproduce at minimum a disclaimer
13 *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
14 *    redistribution must be conditioned upon including a substantially
15 *    similar Disclaimer requirement for further binary redistribution.
16 *
17 * NO WARRANTY
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
21 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
22 * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
23 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
26 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
28 * THE POSSIBILITY OF SUCH DAMAGES.
29 */
30
31#include <sys/cdefs.h>
32__FBSDID("$FreeBSD$");
33
34#include <sys/param.h>
35#include <sys/systm.h>
36#include <sys/kernel.h>
37#include <sys/module.h>
38#include <sys/errno.h>
39#include <sys/rman.h>
40#include <sys/bus.h>
41
42#include <machine/bus.h>
43
44#include <dev/bhnd/bhndvar.h>
45
46#include <dev/spibus/spi.h>
47
48#include "bhnd_chipc_if.h"
49
50#include "spibus_if.h"
51
52#include "chipcreg.h"
53#include "chipcvar.h"
54#include "chipc_slicer.h"
55
56#include "chipc_spi.h"
57
58static int	chipc_spi_probe(device_t dev);
59static int	chipc_spi_attach(device_t dev);
60static int	chipc_spi_detach(device_t dev);
61static int	chipc_spi_transfer(device_t dev, device_t child,
62		    struct spi_command *cmd);
63static int	chipc_spi_txrx(struct chipc_spi_softc *sc, uint8_t in,
64		    uint8_t* out);
65static int	chipc_spi_wait(struct chipc_spi_softc *sc);
66
67static int
68chipc_spi_probe(device_t dev)
69{
70	device_set_desc(dev, "Broadcom ChipCommon SPI");
71	return (BUS_PROBE_NOWILDCARD);
72}
73
74static int
75chipc_spi_attach(device_t dev)
76{
77	struct chipc_spi_softc	*sc;
78	struct chipc_caps	*ccaps;
79	device_t		 flash_dev;
80	device_t		 spibus;
81	const char		*flash_name;
82	int			 error;
83
84	sc = device_get_softc(dev);
85
86	/* Allocate SPI controller registers */
87	sc->sc_rid = 1;
88	sc->sc_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->sc_rid,
89	    RF_ACTIVE);
90	if (sc->sc_res == NULL) {
91		device_printf(dev, "failed to allocate device registers\n");
92		return (ENXIO);
93	}
94
95	/* Allocate flash shadow region */
96	sc->sc_flash_rid = 0;
97	sc->sc_flash_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
98	    &sc->sc_flash_rid, RF_ACTIVE);
99	if (sc->sc_flash_res == NULL) {
100		device_printf(dev, "failed to allocate flash region\n");
101		error = ENXIO;
102		goto failed;
103	}
104
105	/*
106	 * Add flash device
107	 *
108	 * XXX: This should be replaced with a DEVICE_IDENTIFY implementation
109	 * in chipc-specific subclasses of the mx25l and at45d drivers.
110	 */
111	if ((spibus = device_add_child(dev, "spibus", -1)) == NULL) {
112		device_printf(dev, "failed to add spibus\n");
113		error = ENXIO;
114		goto failed;
115	}
116
117	/* Let spibus perform full attach before we try to call
118	 * BUS_ADD_CHILD() */
119	if ((error = bus_generic_attach(dev)))
120		goto failed;
121
122	/* Determine flash type and add the flash child */
123	ccaps = BHND_CHIPC_GET_CAPS(device_get_parent(dev));
124	flash_name = chipc_sflash_device_name(ccaps->flash_type);
125	if (flash_name != NULL) {
126		flash_dev = BUS_ADD_CHILD(spibus, 0, flash_name, -1);
127		if (flash_dev == NULL) {
128			device_printf(dev, "failed to add %s\n", flash_name);
129			error = ENXIO;
130			goto failed;
131		}
132
133		chipc_register_slicer(ccaps->flash_type);
134
135		if ((error = device_probe_and_attach(flash_dev))) {
136			device_printf(dev, "failed to attach %s: %d\n",
137			    flash_name, error);
138			goto failed;
139		}
140	}
141
142	return (0);
143
144failed:
145	device_delete_children(dev);
146
147	if (sc->sc_res != NULL)
148		bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_rid,
149		    sc->sc_res);
150
151	if (sc->sc_flash_res != NULL)
152		bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_flash_rid,
153		    sc->sc_flash_res);
154
155	return (error);
156}
157
158static int
159chipc_spi_detach(device_t dev)
160{
161	struct chipc_spi_softc	*sc;
162	int			 error;
163
164	sc = device_get_softc(dev);
165
166	if ((error = bus_generic_detach(dev)))
167		return (error);
168
169	bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_rid, sc->sc_res);
170	bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_flash_rid,
171	    sc->sc_flash_res);
172	return (0);
173}
174
175static int
176chipc_spi_wait(struct chipc_spi_softc *sc)
177{
178	int i;
179
180	for (i = CHIPC_SPI_MAXTRIES; i > 0; i--)
181		if (!(SPI_READ(sc, CHIPC_SPI_FLASHCTL) & CHIPC_SPI_FLASHCTL_START))
182			break;
183
184	if (i > 0)
185		return (0);
186
187	BHND_DEBUG_DEV(sc->sc_dev, "busy");
188	return (-1);
189}
190
191static int
192chipc_spi_txrx(struct chipc_spi_softc *sc, uint8_t out, uint8_t* in)
193{
194	uint32_t ctl;
195
196	ctl = CHIPC_SPI_FLASHCTL_START | CHIPC_SPI_FLASHCTL_CSACTIVE | out;
197	SPI_BARRIER_WRITE(sc);
198	SPI_WRITE(sc, CHIPC_SPI_FLASHCTL, ctl);
199	SPI_BARRIER_WRITE(sc);
200
201	if (chipc_spi_wait(sc))
202		return (-1);
203
204	*in = SPI_READ(sc, CHIPC_SPI_FLASHDATA) & 0xff;
205	return (0);
206}
207
208static int
209chipc_spi_transfer(device_t dev, device_t child, struct spi_command *cmd)
210{
211	struct chipc_spi_softc	*sc;
212	uint8_t		*buf_in;
213	uint8_t		*buf_out;
214	int		 i;
215
216	sc = device_get_softc(dev);
217	KASSERT(cmd->tx_cmd_sz == cmd->rx_cmd_sz,
218	    ("TX/RX command sizes should be equal"));
219	KASSERT(cmd->tx_data_sz == cmd->rx_data_sz,
220	    ("TX/RX data sizes should be equal"));
221
222	if (cmd->tx_cmd_sz == 0) {
223		BHND_DEBUG_DEV(child, "size of command is ZERO");
224		return (EIO);
225	}
226
227	SPI_BARRIER_WRITE(sc);
228	SPI_WRITE(sc, CHIPC_SPI_FLASHADDR, 0);
229	SPI_BARRIER_WRITE(sc);
230
231	/*
232	 * Transfer command
233	 */
234	buf_out = (uint8_t *)cmd->tx_cmd;
235	buf_in = (uint8_t *)cmd->rx_cmd;
236	for (i = 0; i < cmd->tx_cmd_sz; i++)
237		 if (chipc_spi_txrx(sc, buf_out[i], &(buf_in[i])))
238			 return (EIO);
239
240	/*
241	 * Receive/transmit data
242	 */
243	buf_out = (uint8_t *)cmd->tx_data;
244	buf_in = (uint8_t *)cmd->rx_data;
245	for (i = 0; i < cmd->tx_data_sz; i++)
246		if (chipc_spi_txrx(sc, buf_out[i], &(buf_in[i])))
247			return (EIO);
248
249	/*
250	 * Clear CS bit and whole control register
251	 */
252	SPI_BARRIER_WRITE(sc);
253	SPI_WRITE(sc, CHIPC_SPI_FLASHCTL, 0);
254	SPI_BARRIER_WRITE(sc);
255
256	return (0);
257}
258
259static device_method_t chipc_spi_methods[] = {
260		DEVMETHOD(device_probe,		chipc_spi_probe),
261		DEVMETHOD(device_attach,	chipc_spi_attach),
262		DEVMETHOD(device_detach,	chipc_spi_detach),
263
264		/* SPI */
265		DEVMETHOD(spibus_transfer,	chipc_spi_transfer),
266		DEVMETHOD_END
267};
268
269static driver_t chipc_spi_driver = {
270	"spi",
271	chipc_spi_methods,
272	sizeof(struct chipc_spi_softc),
273};
274
275static devclass_t chipc_spi_devclass;
276
277DRIVER_MODULE(chipc_spi, bhnd_chipc, chipc_spi_driver, chipc_spi_devclass,
278    0, 0);
279