]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/bhnd/cores/chipc/chipc_spi.c
Merge ACPICA 20170303.
[FreeBSD/FreeBSD.git] / sys / dev / bhnd / cores / chipc / chipc_spi.c
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
58 static int      chipc_spi_probe(device_t dev);
59 static int      chipc_spi_attach(device_t dev);
60 static int      chipc_spi_detach(device_t dev);
61 static int      chipc_spi_transfer(device_t dev, device_t child,
62                     struct spi_command *cmd);
63 static int      chipc_spi_txrx(struct chipc_spi_softc *sc, uint8_t in,
64                     uint8_t* out);
65 static int      chipc_spi_wait(struct chipc_spi_softc *sc);
66
67 static int
68 chipc_spi_probe(device_t dev)
69 {
70         device_set_desc(dev, "Broadcom ChipCommon SPI");
71         return (BUS_PROBE_NOWILDCARD);
72 }
73
74 static int
75 chipc_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
144 failed:
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
158 static int
159 chipc_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
175 static int
176 chipc_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_WARN_DEV(sc->sc_dev, "busy: CTL=0x%x DATA=0x%x",
188             SPI_READ(sc, CHIPC_SPI_FLASHCTL),
189             SPI_READ(sc, CHIPC_SPI_FLASHDATA));
190         return (-1);
191 }
192
193 static int
194 chipc_spi_txrx(struct chipc_spi_softc *sc, uint8_t out, uint8_t* in)
195 {
196         uint32_t ctl;
197
198         ctl = CHIPC_SPI_FLASHCTL_START | CHIPC_SPI_FLASHCTL_CSACTIVE | out;
199         SPI_BARRIER_WRITE(sc);
200         SPI_WRITE(sc, CHIPC_SPI_FLASHCTL, ctl);
201         SPI_BARRIER_WRITE(sc);
202
203         if (chipc_spi_wait(sc))
204                 return (-1);
205
206         *in = SPI_READ(sc, CHIPC_SPI_FLASHDATA) & 0xff;
207         return (0);
208 }
209
210 static int
211 chipc_spi_transfer(device_t dev, device_t child, struct spi_command *cmd)
212 {
213         struct chipc_spi_softc  *sc;
214         uint8_t         *buf_in;
215         uint8_t         *buf_out;
216         int              i;
217
218         sc = device_get_softc(dev);
219         KASSERT(cmd->tx_cmd_sz == cmd->rx_cmd_sz,
220             ("TX/RX command sizes should be equal"));
221         KASSERT(cmd->tx_data_sz == cmd->rx_data_sz,
222             ("TX/RX data sizes should be equal"));
223
224         if (cmd->tx_cmd_sz == 0) {
225                 BHND_DEBUG_DEV(child, "size of command is ZERO");
226                 return (EIO);
227         }
228
229         SPI_BARRIER_WRITE(sc);
230         SPI_WRITE(sc, CHIPC_SPI_FLASHADDR, 0);
231         SPI_BARRIER_WRITE(sc);
232
233         /*
234          * Transfer command
235          */
236         buf_out = (uint8_t *)cmd->tx_cmd;
237         buf_in = (uint8_t *)cmd->rx_cmd;
238         for (i = 0; i < cmd->tx_cmd_sz; i++)
239                  if (chipc_spi_txrx(sc, buf_out[i], &(buf_in[i])))
240                          return (EIO);
241
242         /*
243          * Receive/transmit data
244          */
245         buf_out = (uint8_t *)cmd->tx_data;
246         buf_in = (uint8_t *)cmd->rx_data;
247         for (i = 0; i < cmd->tx_data_sz; i++)
248                 if (chipc_spi_txrx(sc, buf_out[i], &(buf_in[i])))
249                         return (EIO);
250
251         /*
252          * Clear CS bit and whole control register
253          */
254         SPI_BARRIER_WRITE(sc);
255         SPI_WRITE(sc, CHIPC_SPI_FLASHCTL, 0);
256         SPI_BARRIER_WRITE(sc);
257
258         return (0);
259 }
260
261 static device_method_t chipc_spi_methods[] = {
262                 DEVMETHOD(device_probe,         chipc_spi_probe),
263                 DEVMETHOD(device_attach,        chipc_spi_attach),
264                 DEVMETHOD(device_detach,        chipc_spi_detach),
265
266                 /* SPI */
267                 DEVMETHOD(spibus_transfer,      chipc_spi_transfer),
268                 DEVMETHOD_END
269 };
270
271 static driver_t chipc_spi_driver = {
272         "spi",
273         chipc_spi_methods,
274         sizeof(struct chipc_spi_softc),
275 };
276
277 static devclass_t chipc_spi_devclass;
278
279 DRIVER_MODULE(chipc_spi, bhnd_chipc, chipc_spi_driver, chipc_spi_devclass,
280     0, 0);