]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/arm/mv/mv_spi.c
THIS BRANCH IS OBSOLETE, PLEASE READ:
[FreeBSD/FreeBSD.git] / sys / arm / mv / mv_spi.c
1 /*-
2  * Copyright (c) 2017-2018, Rubicon Communications, LLC (Netgate)
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 ``AS IS'' AND ANY EXPRESS OR
15  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  *
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
34 #include <sys/kernel.h>
35 #include <sys/lock.h>
36 #include <sys/module.h>
37 #include <sys/mutex.h>
38 #include <sys/rman.h>
39
40 #include <machine/bus.h>
41 #include <machine/resource.h>
42 #include <machine/intr.h>
43
44 #include <dev/ofw/ofw_bus.h>
45 #include <dev/ofw/ofw_bus_subr.h>
46 #include <dev/spibus/spi.h>
47 #include <dev/spibus/spibusvar.h>
48
49 #include <arm/mv/mvvar.h>
50
51 #include "spibus_if.h"
52
53 struct mv_spi_softc {
54         device_t                sc_dev;
55         struct mtx              sc_mtx;
56         struct resource         *sc_mem_res;
57         struct resource         *sc_irq_res;
58         struct spi_command      *sc_cmd;
59         bus_space_tag_t         sc_bst;
60         bus_space_handle_t      sc_bsh;
61         uint32_t                sc_len;
62         uint32_t                sc_read;
63         uint32_t                sc_flags;
64         uint32_t                sc_written;
65         void                    *sc_intrhand;
66 };
67
68 #define MV_SPI_BUSY             0x1
69 #define MV_SPI_WRITE(_sc, _off, _val)           \
70     bus_space_write_4((_sc)->sc_bst, (_sc)->sc_bsh, (_off), (_val))
71 #define MV_SPI_READ(_sc, _off)                  \
72     bus_space_read_4((_sc)->sc_bst, (_sc)->sc_bsh, (_off))
73 #define MV_SPI_LOCK(_sc)        mtx_lock(&(_sc)->sc_mtx)
74 #define MV_SPI_UNLOCK(_sc)      mtx_unlock(&(_sc)->sc_mtx)
75
76 #define MV_SPI_CONTROL          0
77 #define MV_SPI_CTRL_CS_MASK             7
78 #define MV_SPI_CTRL_CS_SHIFT            2
79 #define MV_SPI_CTRL_SMEMREADY           (1 << 1)
80 #define MV_SPI_CTRL_CS_ACTIVE           (1 << 0)
81 #define MV_SPI_CONF             0x4
82 #define MV_SPI_CONF_MODE_SHIFT          12
83 #define MV_SPI_CONF_MODE_MASK           (3 << MV_SPI_CONF_MODE_SHIFT)
84 #define MV_SPI_CONF_BYTELEN             (1 << 5)
85 #define MV_SPI_CONF_CLOCK_SPR_MASK      0xf
86 #define MV_SPI_CONF_CLOCK_SPPR_MASK     1
87 #define MV_SPI_CONF_CLOCK_SPPR_SHIFT    4
88 #define MV_SPI_CONF_CLOCK_SPPRHI_MASK   3
89 #define MV_SPI_CONF_CLOCK_SPPRHI_SHIFT  6
90 #define MV_SPI_CONF_CLOCK_MASK                                          \
91     ((MV_SPI_CONF_CLOCK_SPPRHI_MASK << MV_SPI_CONF_CLOCK_SPPRHI_SHIFT) | \
92     (MV_SPI_CONF_CLOCK_SPPR_MASK << MV_SPI_CONF_CLOCK_SPPR_SHIFT) |     \
93     MV_SPI_CONF_CLOCK_SPR_MASK)
94 #define MV_SPI_DATAOUT          0x8
95 #define MV_SPI_DATAIN           0xc
96 #define MV_SPI_INTR_STAT        0x10
97 #define MV_SPI_INTR_MASK        0x14
98 #define MV_SPI_INTR_SMEMREADY           (1 << 0)
99
100 static struct ofw_compat_data compat_data[] = {
101         {"marvell,armada-380-spi",      1},
102         {NULL,                          0}
103 };
104
105 static void mv_spi_intr(void *);
106
107 static int
108 mv_spi_probe(device_t dev)
109 {
110
111         if (!ofw_bus_status_okay(dev))
112                 return (ENXIO);
113         if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
114                 return (ENXIO);
115
116         device_set_desc(dev, "Marvell SPI controller");
117
118         return (BUS_PROBE_DEFAULT);
119 }
120
121 static int
122 mv_spi_attach(device_t dev)
123 {
124         struct mv_spi_softc *sc;
125         int rid;
126         uint32_t reg;
127
128         sc = device_get_softc(dev);
129         sc->sc_dev = dev;
130
131         rid = 0;
132         sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
133             RF_ACTIVE);
134         if (!sc->sc_mem_res) {
135                 device_printf(dev, "cannot allocate memory window\n");
136                 return (ENXIO);
137         }
138
139         sc->sc_bst = rman_get_bustag(sc->sc_mem_res);
140         sc->sc_bsh = rman_get_bushandle(sc->sc_mem_res);
141
142         rid = 0;
143         sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
144             RF_ACTIVE);
145         if (!sc->sc_irq_res) {
146                 bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res);
147                 device_printf(dev, "cannot allocate interrupt\n");
148                 return (ENXIO);
149         }
150
151         /* Deactivate the bus - just in case... */
152         reg = MV_SPI_READ(sc, MV_SPI_CONTROL);
153         MV_SPI_WRITE(sc, MV_SPI_CONTROL, reg & ~MV_SPI_CTRL_CS_ACTIVE);
154
155         /* Disable the two bytes FIFO. */
156         reg = MV_SPI_READ(sc, MV_SPI_CONF);
157         MV_SPI_WRITE(sc, MV_SPI_CONF, reg & ~MV_SPI_CONF_BYTELEN);
158
159         /* Clear and disable interrupts. */
160         MV_SPI_WRITE(sc, MV_SPI_INTR_MASK, 0);
161         MV_SPI_WRITE(sc, MV_SPI_INTR_STAT, 0);
162
163         /* Hook up our interrupt handler. */
164         if (bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_MISC | INTR_MPSAFE,
165             NULL, mv_spi_intr, sc, &sc->sc_intrhand)) {
166                 bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res);
167                 bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res);
168                 device_printf(dev, "cannot setup the interrupt handler\n");
169                 return (ENXIO);
170         }
171
172         mtx_init(&sc->sc_mtx, "mv_spi", NULL, MTX_DEF);
173
174         device_add_child(dev, "spibus", -1);
175
176         /* Probe and attach the spibus when interrupts are available. */
177         return (bus_delayed_attach_children(dev));
178 }
179
180 static int
181 mv_spi_detach(device_t dev)
182 {
183         struct mv_spi_softc *sc;
184
185         bus_generic_detach(dev);
186
187         sc = device_get_softc(dev);
188         mtx_destroy(&sc->sc_mtx);
189         if (sc->sc_intrhand)
190                 bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_intrhand);
191         if (sc->sc_irq_res)
192                 bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res);
193         if (sc->sc_mem_res)
194                 bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res);
195
196         return (0);
197 }
198
199 static __inline void
200 mv_spi_rx_byte(struct mv_spi_softc *sc)
201 {
202         struct spi_command *cmd;
203         uint32_t read;
204         uint8_t *p;
205
206         cmd = sc->sc_cmd; 
207         p = (uint8_t *)cmd->rx_cmd;
208         read = sc->sc_read++;
209         if (read >= cmd->rx_cmd_sz) {
210                 p = (uint8_t *)cmd->rx_data;
211                 read -= cmd->rx_cmd_sz;
212         }
213         p[read] = MV_SPI_READ(sc, MV_SPI_DATAIN) & 0xff;
214 }
215
216 static __inline void
217 mv_spi_tx_byte(struct mv_spi_softc *sc)
218 {
219         struct spi_command *cmd;
220         uint32_t written;
221         uint8_t *p;
222
223         cmd = sc->sc_cmd; 
224         p = (uint8_t *)cmd->tx_cmd;
225         written = sc->sc_written++;
226         if (written >= cmd->tx_cmd_sz) {
227                 p = (uint8_t *)cmd->tx_data;
228                 written -= cmd->tx_cmd_sz;
229         }
230         MV_SPI_WRITE(sc, MV_SPI_DATAOUT, p[written]);
231 }
232
233 static void
234 mv_spi_intr(void *arg)
235 {
236         struct mv_spi_softc *sc;
237
238         sc = (struct mv_spi_softc *)arg;
239         MV_SPI_LOCK(sc);
240
241         /* Filter stray interrupts. */
242         if ((sc->sc_flags & MV_SPI_BUSY) == 0) {
243                 MV_SPI_UNLOCK(sc);
244                 return;
245         }
246
247         /* RX */
248         mv_spi_rx_byte(sc);
249
250         /* TX */
251         mv_spi_tx_byte(sc);
252
253         /* Check for end of transfer. */
254         if (sc->sc_written == sc->sc_len && sc->sc_read == sc->sc_len)
255                 wakeup(sc->sc_dev);
256
257         MV_SPI_UNLOCK(sc);
258 }
259
260 static int
261 mv_spi_psc_calc(uint32_t clock, uint32_t *spr, uint32_t *sppr)
262 {
263         uint32_t divider, tclk;
264
265         tclk = get_tclk_armada38x();
266         for (*spr = 2; *spr <= 15; (*spr)++) {
267                 for (*sppr = 0; *sppr <= 7; (*sppr)++) {
268                         divider = *spr * (1 << *sppr);
269                         if (tclk / divider <= clock)
270                                 return (0);
271                 }
272         }
273
274         return (EINVAL);
275 }
276
277 static int
278 mv_spi_transfer(device_t dev, device_t child, struct spi_command *cmd)
279 {
280         struct mv_spi_softc *sc;
281         uint32_t clock, cs, mode, reg, spr, sppr;
282         int resid, timeout;
283
284         KASSERT(cmd->tx_cmd_sz == cmd->rx_cmd_sz,
285             ("TX/RX command sizes should be equal"));
286         KASSERT(cmd->tx_data_sz == cmd->rx_data_sz,
287             ("TX/RX data sizes should be equal"));
288
289         /* Get the proper chip select, mode and clock for this transfer. */
290         spibus_get_cs(child, &cs);
291         cs &= ~SPIBUS_CS_HIGH;
292         spibus_get_mode(child, &mode);
293         if (mode > 3) {
294                 device_printf(dev,
295                     "Invalid mode %u requested by %s\n", mode,
296                     device_get_nameunit(child));
297                 return (EINVAL);
298         }
299         spibus_get_clock(child, &clock);
300         if (clock == 0 || mv_spi_psc_calc(clock, &spr, &sppr) != 0) {
301                 device_printf(dev,
302                     "Invalid clock %uHz requested by %s\n", clock,
303                     device_get_nameunit(child));
304                 return (EINVAL);
305         }
306
307         sc = device_get_softc(dev);
308         MV_SPI_LOCK(sc);
309
310         /* Wait until the controller is free. */
311         while (sc->sc_flags & MV_SPI_BUSY)
312                 mtx_sleep(dev, &sc->sc_mtx, 0, "mv_spi", 0);
313
314         /* Now we have control over SPI controller. */
315         sc->sc_flags = MV_SPI_BUSY;
316
317         /* Save a pointer to the SPI command. */
318         sc->sc_cmd = cmd;
319         sc->sc_read = 0;
320         sc->sc_written = 0;
321         sc->sc_len = cmd->tx_cmd_sz + cmd->tx_data_sz;
322
323         /* Set SPI Mode and Clock. */
324         reg = MV_SPI_READ(sc, MV_SPI_CONF);
325         reg &= ~(MV_SPI_CONF_MODE_MASK | MV_SPI_CONF_CLOCK_MASK);
326         reg |= mode << MV_SPI_CONF_MODE_SHIFT;
327         reg |= spr & MV_SPI_CONF_CLOCK_SPR_MASK;
328         reg |= (sppr & MV_SPI_CONF_CLOCK_SPPR_MASK) <<
329             MV_SPI_CONF_CLOCK_SPPR_SHIFT;
330         reg |= (sppr & MV_SPI_CONF_CLOCK_SPPRHI_MASK) <<
331             MV_SPI_CONF_CLOCK_SPPRHI_SHIFT;
332         MV_SPI_WRITE(sc, MV_SPI_CONTROL, reg);
333
334         /* Set CS number and assert CS. */
335         reg = (cs & MV_SPI_CTRL_CS_MASK) << MV_SPI_CTRL_CS_SHIFT;
336         MV_SPI_WRITE(sc, MV_SPI_CONTROL, reg);
337         reg = MV_SPI_READ(sc, MV_SPI_CONTROL);
338         MV_SPI_WRITE(sc, MV_SPI_CONTROL, reg | MV_SPI_CTRL_CS_ACTIVE);
339
340         while ((resid = sc->sc_len - sc->sc_written) > 0) {
341                 MV_SPI_WRITE(sc, MV_SPI_INTR_STAT, 0);
342
343                 /*
344                  * Write to start the transmission and read the byte
345                  * back when ready.
346                  */
347                 mv_spi_tx_byte(sc);
348                 timeout = 1000;
349                 while (--timeout > 0) {
350                         reg = MV_SPI_READ(sc, MV_SPI_CONTROL);
351                         if (reg & MV_SPI_CTRL_SMEMREADY)
352                                 break;
353                         DELAY(1);
354                 }
355                 if (timeout == 0)
356                         break;
357                 mv_spi_rx_byte(sc);
358         }
359
360         /* Stop the controller. */
361         reg = MV_SPI_READ(sc, MV_SPI_CONTROL);
362         MV_SPI_WRITE(sc, MV_SPI_CONTROL, reg & ~MV_SPI_CTRL_CS_ACTIVE);
363         MV_SPI_WRITE(sc, MV_SPI_INTR_MASK, 0);
364         MV_SPI_WRITE(sc, MV_SPI_INTR_STAT, 0);
365
366         /* Release the controller and wakeup the next thread waiting for it. */
367         sc->sc_flags = 0;
368         wakeup_one(dev);
369         MV_SPI_UNLOCK(sc);
370
371         /*
372          * Check for transfer timeout.  The SPI controller doesn't
373          * return errors.
374          */
375         return ((timeout == 0) ? EIO : 0);
376 }
377
378 static phandle_t
379 mv_spi_get_node(device_t bus, device_t dev)
380 {
381
382         return (ofw_bus_get_node(bus));
383 }
384
385 static device_method_t mv_spi_methods[] = {
386         /* Device interface */
387         DEVMETHOD(device_probe,         mv_spi_probe),
388         DEVMETHOD(device_attach,        mv_spi_attach),
389         DEVMETHOD(device_detach,        mv_spi_detach),
390
391         /* SPI interface */
392         DEVMETHOD(spibus_transfer,      mv_spi_transfer),
393
394         /* ofw_bus interface */
395         DEVMETHOD(ofw_bus_get_node,     mv_spi_get_node),
396
397         DEVMETHOD_END
398 };
399
400 static devclass_t mv_spi_devclass;
401
402 static driver_t mv_spi_driver = {
403         "spi",
404         mv_spi_methods,
405         sizeof(struct mv_spi_softc),
406 };
407
408 DRIVER_MODULE(mv_spi, simplebus, mv_spi_driver, mv_spi_devclass, 0, 0);