Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/riscv/sifive/sifive_spi.c
39478 views
1
/*-
2
* SPDX-License-Identifier: BSD-2-Clause
3
*
4
* Copyright (c) 2019 Axiado Corporation
5
* All rights reserved.
6
*
7
* This software was developed in part by Philip Paeps and Kristof Provost
8
* under contract for Axiado Corporation.
9
*
10
* Redistribution and use in source and binary forms, with or without
11
* modification, are permitted provided that the following conditions
12
* are met:
13
* 1. Redistributions of source code must retain the above copyright
14
* notice, this list of conditions and the following disclaimer.
15
* 2. Redistributions in binary form must reproduce the above copyright
16
* notice, this list of conditions and the following disclaimer in the
17
* documentation and/or other materials provided with the distribution.
18
*
19
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29
* SUCH DAMAGE.
30
*/
31
32
#include <sys/param.h>
33
#include <sys/systm.h>
34
#include <sys/bus.h>
35
#include <sys/kernel.h>
36
#include <sys/lock.h>
37
#include <sys/module.h>
38
#include <sys/mutex.h>
39
#include <sys/rman.h>
40
41
#include <machine/bus.h>
42
#include <machine/cpu.h>
43
44
#include <dev/clk/clk.h>
45
46
#include <dev/ofw/ofw_bus.h>
47
#include <dev/ofw/ofw_bus_subr.h>
48
#include <dev/ofw/openfirm.h>
49
50
#include <dev/spibus/spi.h>
51
#include <dev/spibus/spibusvar.h>
52
53
#include "spibus_if.h"
54
55
#if 1
56
#define DBGPRINT(dev, fmt, args...) \
57
device_printf(dev, "%s: " fmt "\n", __func__, ## args)
58
#else
59
#define DBGPRINT(dev, fmt, args...)
60
#endif
61
62
static struct resource_spec sfspi_spec[] = {
63
{ SYS_RES_MEMORY, 0, RF_ACTIVE },
64
RESOURCE_SPEC_END
65
};
66
67
struct sfspi_softc {
68
device_t dev;
69
device_t parent;
70
71
struct mtx mtx;
72
73
struct resource *res;
74
bus_space_tag_t bst;
75
bus_space_handle_t bsh;
76
77
void *ih;
78
79
clk_t clk;
80
uint64_t freq;
81
uint32_t cs_max;
82
};
83
84
#define SFSPI_LOCK(sc) mtx_lock(&(sc)->mtx)
85
#define SFSPI_UNLOCK(sc) mtx_unlock(&(sc)->mtx)
86
#define SFSPI_ASSERT_LOCKED(sc) mtx_assert(&(sc)->mtx, MA_OWNED);
87
#define SFSPI_ASSERT_UNLOCKED(sc) mtx_assert(&(sc)->mtx, MA_NOTOWNED);
88
89
/*
90
* Register offsets.
91
* From Sifive-Unleashed-FU540-C000-v1.0.pdf page 101.
92
*/
93
#define SFSPI_REG_SCKDIV 0x00 /* Serial clock divisor */
94
#define SFSPI_REG_SCKMODE 0x04 /* Serial clock mode */
95
#define SFSPI_REG_CSID 0x10 /* Chip select ID */
96
#define SFSPI_REG_CSDEF 0x14 /* Chip select default */
97
#define SFSPI_REG_CSMODE 0x18 /* Chip select mode */
98
#define SFSPI_REG_DELAY0 0x28 /* Delay control 0 */
99
#define SFSPI_REG_DELAY1 0x2C /* Delay control 1 */
100
#define SFSPI_REG_FMT 0x40 /* Frame format */
101
#define SFSPI_REG_TXDATA 0x48 /* Tx FIFO data */
102
#define SFSPI_REG_RXDATA 0x4C /* Rx FIFO data */
103
#define SFSPI_REG_TXMARK 0x50 /* Tx FIFO watermark */
104
#define SFSPI_REG_RXMARK 0x54 /* Rx FIFO watermark */
105
#define SFSPI_REG_FCTRL 0x60 /* SPI flash interface control* */
106
#define SFSPI_REG_FFMT 0x64 /* SPI flash instruction format* */
107
#define SFSPI_REG_IE 0x70 /* SPI interrupt enable */
108
#define SFSPI_REG_IP 0x74 /* SPI interrupt pending */
109
110
#define SFSPI_SCKDIV_MASK 0xfff
111
112
#define SFSPI_CSDEF_ALL ((1 << sc->cs_max)-1)
113
114
#define SFSPI_CSMODE_AUTO 0x0U
115
#define SFSPI_CSMODE_HOLD 0x2U
116
#define SFSPI_CSMODE_OFF 0x3U
117
118
#define SFSPI_TXDATA_DATA_MASK 0xff
119
#define SFSPI_TXDATA_FULL (1 << 31)
120
121
#define SFSPI_RXDATA_DATA_MASK 0xff
122
#define SFSPI_RXDATA_EMPTY (1 << 31)
123
124
#define SFSPI_SCKMODE_PHA (1 << 0)
125
#define SFSPI_SCKMODE_POL (1 << 1)
126
127
#define SFSPI_FMT_PROTO_SINGLE 0x0U
128
#define SFSPI_FMT_PROTO_DUAL 0x1U
129
#define SFSPI_FMT_PROTO_QUAD 0x2U
130
#define SFSPI_FMT_PROTO_MASK 0x3U
131
#define SFSPI_FMT_ENDIAN (1 << 2)
132
#define SFSPI_FMT_DIR (1 << 3)
133
#define SFSPI_FMT_LEN(x) ((uint32_t)(x) << 16)
134
#define SFSPI_FMT_LEN_MASK (0xfU << 16)
135
136
#define SFSPI_FIFO_DEPTH 8
137
138
#define SFSPI_READ(_sc, _reg) \
139
bus_space_read_4((_sc)->bst, (_sc)->bsh, (_reg))
140
#define SFSPI_WRITE(_sc, _reg, _val) \
141
bus_space_write_4((_sc)->bst, (_sc)->bsh, (_reg), (_val))
142
143
static void
144
sfspi_tx(struct sfspi_softc *sc, uint8_t *buf, uint32_t bufsiz)
145
{
146
uint32_t val;
147
uint8_t *p, *end;
148
149
KASSERT(buf != NULL, ("TX buffer cannot be NULL"));
150
151
end = buf + bufsiz;
152
for (p = buf; p < end; p++) {
153
do {
154
val = SFSPI_READ(sc, SFSPI_REG_TXDATA);
155
} while (val & SFSPI_TXDATA_FULL);
156
val = *p;
157
SFSPI_WRITE(sc, SFSPI_REG_TXDATA, val);
158
}
159
}
160
161
static void
162
sfspi_rx(struct sfspi_softc *sc, uint8_t *buf, uint32_t bufsiz)
163
{
164
uint32_t val;
165
uint8_t *p, *end;
166
167
KASSERT(buf != NULL, ("RX buffer cannot be NULL"));
168
KASSERT(bufsiz <= SFSPI_FIFO_DEPTH,
169
("Cannot receive more than %d bytes at a time\n",
170
SFSPI_FIFO_DEPTH));
171
172
end = buf + bufsiz;
173
for (p = buf; p < end; p++) {
174
do {
175
val = SFSPI_READ(sc, SFSPI_REG_RXDATA);
176
} while (val & SFSPI_RXDATA_EMPTY);
177
*p = val & SFSPI_RXDATA_DATA_MASK;
178
};
179
}
180
181
static int
182
sfspi_xfer_buf(struct sfspi_softc *sc, uint8_t *rxbuf, uint8_t *txbuf,
183
uint32_t txlen, uint32_t rxlen)
184
{
185
uint32_t bytes;
186
187
KASSERT(txlen == rxlen, ("TX and RX lengths must be equal"));
188
KASSERT(rxbuf != NULL, ("RX buffer cannot be NULL"));
189
KASSERT(txbuf != NULL, ("TX buffer cannot be NULL"));
190
191
while (txlen) {
192
bytes = (txlen > SFSPI_FIFO_DEPTH) ? SFSPI_FIFO_DEPTH : txlen;
193
sfspi_tx(sc, txbuf, bytes);
194
txbuf += bytes;
195
sfspi_rx(sc, rxbuf, bytes);
196
rxbuf += bytes;
197
txlen -= bytes;
198
}
199
200
return (0);
201
}
202
203
static int
204
sfspi_setup(struct sfspi_softc *sc, uint32_t cs, uint32_t mode,
205
uint32_t freq)
206
{
207
uint32_t csmode, fmt, sckdiv, sckmode;
208
209
SFSPI_ASSERT_LOCKED(sc);
210
211
/*
212
* Fsck = Fin / 2 * (div + 1)
213
* -> div = Fin / (2 * Fsck) - 1
214
*/
215
sckdiv = (howmany(sc->freq >> 1, freq) - 1) & SFSPI_SCKDIV_MASK;
216
SFSPI_WRITE(sc, SFSPI_REG_SCKDIV, sckdiv);
217
218
switch (mode) {
219
case SPIBUS_MODE_NONE:
220
sckmode = 0;
221
break;
222
case SPIBUS_MODE_CPHA:
223
sckmode = SFSPI_SCKMODE_PHA;
224
break;
225
case SPIBUS_MODE_CPOL:
226
sckmode = SFSPI_SCKMODE_POL;
227
break;
228
case SPIBUS_MODE_CPOL_CPHA:
229
sckmode = SFSPI_SCKMODE_PHA | SFSPI_SCKMODE_POL;
230
break;
231
default:
232
return (EINVAL);
233
}
234
SFSPI_WRITE(sc, SFSPI_REG_SCKMODE, sckmode);
235
236
csmode = SFSPI_CSMODE_HOLD;
237
if (cs & SPIBUS_CS_HIGH)
238
csmode = SFSPI_CSMODE_AUTO;
239
SFSPI_WRITE(sc, SFSPI_REG_CSMODE, csmode);
240
241
SFSPI_WRITE(sc, SFSPI_REG_CSID, cs & ~SPIBUS_CS_HIGH);
242
243
fmt = SFSPI_FMT_PROTO_SINGLE | SFSPI_FMT_LEN(8);
244
SFSPI_WRITE(sc, SFSPI_REG_FMT, fmt);
245
246
return (0);
247
}
248
249
static int
250
sfspi_transfer(device_t dev, device_t child, struct spi_command *cmd)
251
{
252
struct sfspi_softc *sc;
253
uint32_t clock, cs, csdef, mode;
254
int err;
255
256
KASSERT(cmd->tx_cmd_sz == cmd->rx_cmd_sz,
257
("TX and RX command sizes must be equal"));
258
KASSERT(cmd->tx_data_sz == cmd->rx_data_sz,
259
("TX and RX data sizes must be equal"));
260
261
sc = device_get_softc(dev);
262
spibus_get_cs(child, &cs);
263
spibus_get_clock(child, &clock);
264
spibus_get_mode(child, &mode);
265
266
if (cs > sc->cs_max) {
267
device_printf(sc->dev, "Invalid chip select %u\n", cs);
268
return (EINVAL);
269
}
270
271
SFSPI_LOCK(sc);
272
device_busy(sc->dev);
273
274
err = sfspi_setup(sc, cs, mode, clock);
275
if (err != 0) {
276
SFSPI_UNLOCK(sc);
277
return (err);
278
}
279
280
err = 0;
281
if (cmd->tx_cmd_sz > 0)
282
err = sfspi_xfer_buf(sc, cmd->rx_cmd, cmd->tx_cmd,
283
cmd->tx_cmd_sz, cmd->rx_cmd_sz);
284
if (cmd->tx_data_sz > 0 && err == 0)
285
err = sfspi_xfer_buf(sc, cmd->rx_data, cmd->tx_data,
286
cmd->tx_data_sz, cmd->rx_data_sz);
287
288
/* Deassert chip select. */
289
csdef = SFSPI_CSDEF_ALL & ~(1 << cs);
290
SFSPI_WRITE(sc, SFSPI_REG_CSDEF, csdef);
291
SFSPI_WRITE(sc, SFSPI_REG_CSDEF, SFSPI_CSDEF_ALL);
292
293
device_unbusy(sc->dev);
294
SFSPI_UNLOCK(sc);
295
296
return (err);
297
}
298
299
static int
300
sfspi_attach(device_t dev)
301
{
302
struct sfspi_softc *sc;
303
int error;
304
305
sc = device_get_softc(dev);
306
sc->dev = dev;
307
308
mtx_init(&sc->mtx, device_get_nameunit(sc->dev), NULL, MTX_DEF);
309
310
error = bus_alloc_resources(dev, sfspi_spec, &sc->res);
311
if (error) {
312
device_printf(dev, "Couldn't allocate resources\n");
313
goto fail;
314
}
315
sc->bst = rman_get_bustag(sc->res);
316
sc->bsh = rman_get_bushandle(sc->res);
317
318
error = clk_get_by_ofw_index(dev, 0, 0, &sc->clk);
319
if (error) {
320
device_printf(dev, "Couldn't allocate clock: %d\n", error);
321
goto fail;
322
}
323
error = clk_enable(sc->clk);
324
if (error) {
325
device_printf(dev, "Couldn't enable clock: %d\n", error);
326
goto fail;
327
}
328
329
error = clk_get_freq(sc->clk, &sc->freq);
330
if (error) {
331
device_printf(sc->dev, "Couldn't get frequency: %d\n", error);
332
goto fail;
333
}
334
335
/*
336
* From Sifive-Unleashed-FU540-C000-v1.0.pdf page 103:
337
* csdef is cs_width bits wide and all ones on reset.
338
*/
339
sc->cs_max = SFSPI_READ(sc, SFSPI_REG_CSDEF);
340
341
/*
342
* We don't support the direct-mapped flash interface.
343
* Disable it.
344
*/
345
SFSPI_WRITE(sc, SFSPI_REG_FCTRL, 0x0);
346
347
/* Probe and attach the spibus when interrupts are available. */
348
sc->parent = device_add_child(dev, "spibus", DEVICE_UNIT_ANY);
349
bus_delayed_attach_children(dev);
350
351
return (0);
352
353
fail:
354
bus_release_resources(dev, sfspi_spec, &sc->res);
355
mtx_destroy(&sc->mtx);
356
return (error);
357
}
358
359
static int
360
sfspi_probe(device_t dev)
361
{
362
363
if (!ofw_bus_status_okay(dev))
364
return (ENXIO);
365
366
if (!ofw_bus_is_compatible(dev, "sifive,spi0"))
367
return (ENXIO);
368
369
device_set_desc(dev, "SiFive SPI controller");
370
371
return (BUS_PROBE_DEFAULT);
372
}
373
374
static phandle_t
375
sfspi_get_node(device_t bus, device_t dev)
376
{
377
378
return (ofw_bus_get_node(bus));
379
}
380
381
static device_method_t sfspi_methods[] = {
382
DEVMETHOD(device_probe, sfspi_probe),
383
DEVMETHOD(device_attach, sfspi_attach),
384
385
DEVMETHOD(spibus_transfer, sfspi_transfer),
386
387
DEVMETHOD(ofw_bus_get_node, sfspi_get_node),
388
389
DEVMETHOD_END
390
};
391
392
static driver_t sfspi_driver = {
393
"sifive_spi",
394
sfspi_methods,
395
sizeof(struct sfspi_softc)
396
};
397
398
DRIVER_MODULE(sifive_spi, simplebus, sfspi_driver, 0, 0);
399
DRIVER_MODULE(ofw_spibus, sifive_spi, ofw_spibus_driver, 0, 0);
400
MODULE_DEPEND(sifive_spi, ofw_spibus, 1, 1, 1);
401
402