Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/powerpc/powernv/opal_flash.c
39535 views
1
/*-
2
* Copyright (c) 2019 Justin Hibbits
3
*
4
* Redistribution and use in source and binary forms, with or without
5
* modification, are permitted provided that the following conditions
6
* are met:
7
* 1. Redistributions of source code must retain the above copyright
8
* notice, this list of conditions and the following disclaimer.
9
* 2. Redistributions in binary form must reproduce the above copyright
10
* notice, this list of conditions and the following disclaimer in the
11
* documentation and/or other materials provided with the distribution.
12
*
13
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23
* SUCH DAMAGE.
24
*/
25
26
#include <sys/param.h>
27
#include <sys/systm.h>
28
#include <sys/bio.h>
29
#include <sys/bus.h>
30
#include <sys/kernel.h>
31
#include <sys/kthread.h>
32
#include <sys/lock.h>
33
#include <sys/module.h>
34
#include <sys/mutex.h>
35
#include <sys/proc.h>
36
#include <geom/geom_disk.h>
37
38
#include <vm/vm.h>
39
#include <vm/pmap.h>
40
41
#include <dev/ofw/ofw_bus.h>
42
#include <dev/ofw/ofw_bus_subr.h>
43
#include <dev/ofw/openfirm.h>
44
#include "opal.h"
45
46
/*
47
* OPAL System flash driver, using OPAL firmware calls to access the device.
48
*
49
* This just presents the base block interface. The fdt_slicer can be used on
50
* top to present the partitions listed in the fdt.
51
*
52
* There are three OPAL methods used: OPAL_FLASH_READ, OPAL_FLASH_WRITE, and
53
* OPAL_FLASH_ERASE. At the firmware layer, READ and WRITE can be on arbitrary
54
* boundaries, but ERASE is only at flash-block-size block alignments and sizes.
55
* To account for this, the following restrictions are in place:
56
*
57
* - Reads are on a 512-byte block boundary and size
58
* - Writes and Erases are aligned and sized on flash-block-size bytes.
59
*
60
* In order to support the fdt_slicer we present a type attribute of
61
* NAND::device.
62
*/
63
struct opalflash_softc {
64
device_t sc_dev;
65
struct mtx sc_mtx;
66
struct disk *sc_disk;
67
struct proc *sc_p;
68
struct bio_queue_head sc_bio_queue;
69
int sc_opal_id;
70
bool sc_erase; /* Erase is needed before write. */
71
};
72
73
#define OPALFLASH_LOCK(sc) mtx_lock(&(sc)->sc_mtx)
74
#define OPALFLASH_UNLOCK(sc) mtx_unlock(&(sc)->sc_mtx)
75
#define OPALFLASH_LOCK_INIT(sc) \
76
mtx_init(&(sc)->sc_mtx, device_get_nameunit((sc)->sc_dev), \
77
"opalflash", MTX_DEF)
78
79
#define FLASH_BLOCKSIZE 512
80
81
static int opalflash_probe(device_t);
82
static int opalflash_attach(device_t);
83
84
static device_method_t opalflash_methods[] = {
85
/* Device interface */
86
DEVMETHOD(device_probe, opalflash_probe),
87
DEVMETHOD(device_attach, opalflash_attach),
88
89
DEVMETHOD_END
90
};
91
92
static driver_t opalflash_driver = {
93
"opalflash",
94
opalflash_methods,
95
sizeof(struct opalflash_softc)
96
};
97
98
DRIVER_MODULE(opalflash, opal, opalflash_driver, 0, 0);
99
100
/* GEOM Disk interfaces. */
101
static int
102
opalflash_open(struct disk *dp)
103
{
104
105
return (0);
106
}
107
108
static int
109
opalflash_close(struct disk *dp)
110
{
111
112
return (0);
113
}
114
115
static int
116
opalflash_ioctl(struct disk *dp, u_long cmd, void *data, int fflag,
117
struct thread *td)
118
{
119
120
return (EINVAL);
121
}
122
123
/* Handle the one attribute we need to play nice with geom_flashmap. */
124
static int
125
opalflash_getattr(struct bio *bp)
126
{
127
struct opalflash_softc *sc;
128
device_t dev;
129
130
if (bp->bio_disk == NULL || bp->bio_disk->d_drv1 == NULL)
131
return (ENXIO);
132
133
sc = bp->bio_disk->d_drv1;
134
dev = sc->sc_dev;
135
136
if (strcmp(bp->bio_attribute, "NAND::device") == 0) {
137
if (bp->bio_length != sizeof(dev))
138
return (EFAULT);
139
bcopy(&dev, bp->bio_data, sizeof(dev));
140
} else
141
return (-1);
142
return (0);
143
}
144
145
static void
146
opalflash_strategy(struct bio *bp)
147
{
148
struct opalflash_softc *sc;
149
150
sc = (struct opalflash_softc *)bp->bio_disk->d_drv1;
151
OPALFLASH_LOCK(sc);
152
bioq_disksort(&sc->sc_bio_queue, bp);
153
wakeup(sc);
154
OPALFLASH_UNLOCK(sc);
155
}
156
157
static int
158
opalflash_read(struct opalflash_softc *sc, off_t off,
159
caddr_t data, off_t count)
160
{
161
struct opal_msg msg;
162
int rv, size, token;
163
164
/* Ensure we write aligned to a full block size. */
165
if (off % sc->sc_disk->d_sectorsize != 0 ||
166
count % sc->sc_disk->d_sectorsize != 0)
167
return (EIO);
168
169
token = opal_alloc_async_token();
170
171
/*
172
* Read one page at a time. It's not guaranteed that the buffer is
173
* physically contiguous.
174
*/
175
rv = 0;
176
while (count > 0) {
177
size = MIN(count, PAGE_SIZE);
178
size = MIN(size, PAGE_SIZE - ((u_long)data & PAGE_MASK));
179
rv = opal_call(OPAL_FLASH_READ, sc->sc_opal_id, off,
180
vtophys(data), size, token);
181
if (rv == OPAL_ASYNC_COMPLETION) {
182
rv = opal_wait_completion(&msg, sizeof(msg), token);
183
if (rv == OPAL_SUCCESS)
184
rv = msg.params[1];
185
}
186
if (rv != OPAL_SUCCESS)
187
break;
188
count -= size;
189
off += size;
190
data += size;
191
}
192
opal_free_async_token(token);
193
if (rv == OPAL_SUCCESS)
194
rv = 0;
195
else
196
rv = EIO;
197
198
return (rv);
199
}
200
201
static int
202
opalflash_erase(struct opalflash_softc *sc, off_t off, off_t count)
203
{
204
struct opal_msg msg;
205
int rv, token;
206
207
/* Ensure we write aligned to a full block size. */
208
if (off % sc->sc_disk->d_stripesize != 0 ||
209
count % sc->sc_disk->d_stripesize != 0)
210
return (EIO);
211
212
token = opal_alloc_async_token();
213
214
rv = opal_call(OPAL_FLASH_ERASE, sc->sc_opal_id, off, count, token);
215
if (rv == OPAL_ASYNC_COMPLETION) {
216
rv = opal_wait_completion(&msg, sizeof(msg), token);
217
if (rv == OPAL_SUCCESS)
218
rv = msg.params[1];
219
}
220
opal_free_async_token(token);
221
222
if (rv == OPAL_SUCCESS)
223
rv = 0;
224
else
225
rv = EIO;
226
227
return (rv);
228
}
229
230
static int
231
opalflash_write(struct opalflash_softc *sc, off_t off,
232
caddr_t data, off_t count)
233
{
234
struct opal_msg msg;
235
int rv, size, token;
236
237
/* Ensure we write aligned to a full block size. */
238
if (off % sc->sc_disk->d_sectorsize != 0 ||
239
count % sc->sc_disk->d_sectorsize != 0)
240
return (EIO);
241
242
if (sc->sc_erase) {
243
/* Erase the full block first, then write in page chunks. */
244
rv = opalflash_erase(sc, off, count);
245
if (rv != 0)
246
return (rv);
247
}
248
249
token = opal_alloc_async_token();
250
251
/*
252
* Write one page at a time. It's not guaranteed that the buffer is
253
* physically contiguous.
254
*/
255
while (count > 0) {
256
size = MIN(count, PAGE_SIZE);
257
size = MIN(size, PAGE_SIZE - ((u_long)data & PAGE_MASK));
258
rv = opal_call(OPAL_FLASH_WRITE, sc->sc_opal_id, off,
259
vtophys(data), size, token);
260
if (rv == OPAL_ASYNC_COMPLETION) {
261
rv = opal_wait_completion(&msg, sizeof(msg), token);
262
if (rv == OPAL_SUCCESS)
263
rv = msg.params[1];
264
}
265
if (rv != OPAL_SUCCESS)
266
break;
267
count -= size;
268
off += size;
269
data += size;
270
}
271
opal_free_async_token(token);
272
273
if (rv == OPAL_SUCCESS)
274
rv = 0;
275
else
276
rv = EIO;
277
278
return (rv);
279
}
280
281
/* Main flash handling task. */
282
static void
283
opalflash_task(void *arg)
284
{
285
struct opalflash_softc *sc;
286
struct bio *bp;
287
288
sc = arg;
289
290
for (;;) {
291
OPALFLASH_LOCK(sc);
292
do {
293
bp = bioq_first(&sc->sc_bio_queue);
294
if (bp == NULL)
295
msleep(sc, &sc->sc_mtx, PRIBIO, "opalflash", 0);
296
} while (bp == NULL);
297
bioq_remove(&sc->sc_bio_queue, bp);
298
OPALFLASH_UNLOCK(sc);
299
300
switch (bp->bio_cmd) {
301
case BIO_DELETE:
302
bp->bio_error = opalflash_erase(sc, bp->bio_offset,
303
bp->bio_bcount);
304
break;
305
case BIO_READ:
306
bp->bio_error = opalflash_read(sc, bp->bio_offset,
307
bp->bio_data, bp->bio_bcount);
308
break;
309
case BIO_WRITE:
310
bp->bio_error = opalflash_write(sc, bp->bio_offset,
311
bp->bio_data, bp->bio_bcount);
312
break;
313
default:
314
bp->bio_error = EINVAL;
315
}
316
biodone(bp);
317
}
318
}
319
320
/* Device driver interfaces. */
321
322
static int
323
opalflash_probe(device_t dev)
324
{
325
if (!ofw_bus_is_compatible(dev, "ibm,opal-flash"))
326
return (ENXIO);
327
328
device_set_desc(dev, "OPAL System Flash");
329
330
return (BUS_PROBE_GENERIC);
331
}
332
333
static int
334
opalflash_attach(device_t dev)
335
{
336
struct opalflash_softc *sc;
337
phandle_t node;
338
cell_t flash_blocksize, opal_id;
339
uint32_t regs[2];
340
341
sc = device_get_softc(dev);
342
sc->sc_dev = dev;
343
344
node = ofw_bus_get_node(dev);
345
OF_getencprop(node, "ibm,opal-id", &opal_id, sizeof(opal_id));
346
sc->sc_opal_id = opal_id;
347
348
if (OF_getencprop(node, "ibm,flash-block-size",
349
&flash_blocksize, sizeof(flash_blocksize)) < 0) {
350
device_printf(dev, "Cannot determine flash block size.\n");
351
return (ENXIO);
352
}
353
354
if (!OF_hasprop(node, "no-erase"))
355
sc->sc_erase = true;
356
357
OPALFLASH_LOCK_INIT(sc);
358
359
if (OF_getencprop(node, "reg", regs, sizeof(regs)) < 0) {
360
device_printf(dev, "Unable to get flash size.\n");
361
return (ENXIO);
362
}
363
364
sc->sc_disk = disk_alloc();
365
sc->sc_disk->d_name = "opalflash";
366
sc->sc_disk->d_open = opalflash_open;
367
sc->sc_disk->d_close = opalflash_close;
368
sc->sc_disk->d_strategy = opalflash_strategy;
369
sc->sc_disk->d_ioctl = opalflash_ioctl;
370
sc->sc_disk->d_getattr = opalflash_getattr;
371
sc->sc_disk->d_drv1 = sc;
372
sc->sc_disk->d_maxsize = DFLTPHYS;
373
sc->sc_disk->d_mediasize = regs[1];
374
sc->sc_disk->d_unit = device_get_unit(sc->sc_dev);
375
sc->sc_disk->d_sectorsize = FLASH_BLOCKSIZE;
376
sc->sc_disk->d_stripesize = flash_blocksize;
377
sc->sc_disk->d_dump = NULL;
378
379
disk_create(sc->sc_disk, DISK_VERSION);
380
bioq_init(&sc->sc_bio_queue);
381
382
kproc_create(&opalflash_task, sc, &sc->sc_p, 0, 0, "task: OPAL Flash");
383
384
return (0);
385
}
386
387