ps3disk.c revision 223316
1/*-
2 * Copyright (C) 2011 glevand (geoffrey.levand@mail.ru)
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: head/sys/powerpc/ps3/ps3disk.c 223316 2011-06-20 01:43:18Z nwhitehorn $");
29
30#include <sys/param.h>
31#include <sys/systm.h>
32#include <sys/sysctl.h>
33#include <sys/disk.h>
34#include <sys/bio.h>
35#include <sys/bus.h>
36#include <sys/conf.h>
37#include <sys/kernel.h>
38#include <sys/kthread.h>
39#include <sys/lock.h>
40#include <sys/malloc.h>
41#include <sys/module.h>
42#include <sys/mutex.h>
43
44#include <vm/vm.h>
45#include <vm/pmap.h>
46
47#include <machine/pio.h>
48#include <machine/bus.h>
49#include <machine/platform.h>
50#include <machine/pmap.h>
51#include <machine/resource.h>
52#include <sys/bus.h>
53#include <sys/rman.h>
54
55#include <geom/geom_disk.h>
56
57#include "ps3bus.h"
58#include "ps3-hvcall.h"
59
60#define PS3DISK_LOCK_INIT(_sc)		\
61	mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->sc_dev), "ps3disk", MTX_DEF)
62#define PS3DISK_LOCK_DESTROY(_sc)	mtx_destroy(&_sc->sc_mtx);
63#define PS3DISK_LOCK(_sc)		mtx_lock(&(_sc)->sc_mtx)
64#define	PS3DISK_UNLOCK(_sc)		mtx_unlock(&(_sc)->sc_mtx)
65#define PS3DISK_ASSERT_LOCKED(_sc)	mtx_assert(&_sc->sc_mtx, MA_OWNED);
66#define PS3DISK_ASSERT_UNLOCKED(_sc)	mtx_assert(&_sc->sc_mtx, MA_NOTOWNED);
67
68#define LV1_STORAGE_ATA_HDDOUT 		0x23
69
70SYSCTL_NODE(_hw, OID_AUTO, ps3disk, CTLFLAG_RD, 0, "PS3 Disk driver parameters");
71
72#ifdef PS3DISK_DEBUG
73static int ps3disk_debug = 0;
74SYSCTL_INT(_hw_ps3disk, OID_AUTO, debug, CTLFLAG_RW, &ps3disk_debug,
75	0, "control debugging printfs");
76TUNABLE_INT("hw.ps3disk.debug", &ps3disk_debug);
77enum {
78	PS3DISK_DEBUG_INTR	= 0x00000001,
79	PS3DISK_DEBUG_TASK	= 0x00000002,
80	PS3DISK_DEBUG_READ	= 0x00000004,
81	PS3DISK_DEBUG_WRITE	= 0x00000008,
82	PS3DISK_DEBUG_FLUSH	= 0x00000010,
83	PS3DISK_DEBUG_ANY	= 0xffffffff
84};
85#define	DPRINTF(sc, m, fmt, ...)				\
86do {								\
87	if (sc->sc_debug & (m))					\
88		printf(fmt, __VA_ARGS__);			\
89} while (0)
90#else
91#define	DPRINTF(sc, m, fmt, ...)
92#endif
93
94struct ps3disk_region {
95	uint64_t r_id;
96	uint64_t r_start;
97	uint64_t r_size;
98	uint64_t r_flags;
99};
100
101struct ps3disk_softc {
102	device_t sc_dev;
103
104	struct mtx sc_mtx;
105
106	uint64_t sc_blksize;
107	uint64_t sc_nblocks;
108
109	uint64_t sc_nregs;
110	struct ps3disk_region *sc_reg;
111
112	int sc_irqid;
113	struct resource	*sc_irq;
114	void *sc_irqctx;
115
116	struct disk **sc_disk;
117
118	struct bio_queue_head sc_bioq;
119
120	struct proc *sc_task;
121
122	int sc_bounce_maxblocks;
123	bus_dma_tag_t sc_bounce_dmatag;
124	bus_dmamap_t sc_bounce_dmamap;
125	bus_addr_t sc_bounce_dmaphys;
126	char *sc_bounce;
127	uint64_t sc_bounce_lpar;
128	int sc_bounce_busy;
129	uint64_t sc_bounce_tag;
130	uint64_t sc_bounce_status;
131
132	int sc_running;
133
134	int sc_debug;
135};
136
137static int ps3disk_open(struct disk *dp);
138static int ps3disk_close(struct disk *dp);
139static void ps3disk_strategy(struct bio *bp);
140static void ps3disk_task(void *arg);
141
142static int ps3disk_intr_filter(void *arg);
143static void ps3disk_intr(void *arg);
144static void ps3disk_getphys(void *arg, bus_dma_segment_t *segs, int nsegs, int error);
145static int ps3disk_get_disk_geometry(struct ps3disk_softc *sc);
146static int ps3disk_enum_regions(struct ps3disk_softc *sc);
147static int ps3disk_read(struct ps3disk_softc *sc, int regidx,
148	uint64_t start_sector, uint64_t sector_count, char *data);
149static int ps3disk_write(struct ps3disk_softc *sc, int regidx,
150	uint64_t start_sector, uint64_t sector_count, char *data);
151static int ps3disk_flush(struct ps3disk_softc *sc);
152
153static void ps3disk_sysctlattach(struct ps3disk_softc *sc);
154
155static MALLOC_DEFINE(M_PS3DISK, "ps3disk", "PS3 Disk");
156
157static int
158ps3disk_probe(device_t dev)
159{
160	if (ps3bus_get_bustype(dev) != PS3_BUSTYPE_STORAGE ||
161	    ps3bus_get_devtype(dev) != PS3_DEVTYPE_DISK)
162		return (ENXIO);
163
164	device_set_desc(dev, "Playstation 3 Disk");
165
166	return (BUS_PROBE_SPECIFIC);
167}
168
169static int
170ps3disk_attach(device_t dev)
171{
172	struct ps3disk_softc *sc;
173	struct disk *d;
174	intmax_t mb;
175	char unit;
176	int i, err;
177
178	sc = device_get_softc(dev);
179	sc->sc_dev = dev;
180
181	PS3DISK_LOCK_INIT(sc);
182
183	err = ps3disk_get_disk_geometry(sc);
184	if (err) {
185		device_printf(dev, "Could not get disk geometry\n");
186		err = ENXIO;
187		goto fail_destroy_lock;
188	}
189
190	device_printf(dev, "block size %lu total blocks %lu\n",
191	    sc->sc_blksize, sc->sc_nblocks);
192
193	err = ps3disk_enum_regions(sc);
194	if (err) {
195		device_printf(dev, "Could not enumerate disk regions\n");
196		err = ENXIO;
197		goto fail_destroy_lock;
198	}
199
200	device_printf(dev, "Found %lu regions\n", sc->sc_nregs);
201
202	if (!sc->sc_nregs) {
203		err = ENXIO;
204		goto fail_destroy_lock;
205	}
206
207	/* Setup interrupt handler */
208
209	sc->sc_irqid = 0;
210	sc->sc_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->sc_irqid,
211	    RF_ACTIVE);
212	if (!sc->sc_irq) {
213		device_printf(dev, "Could not allocate IRQ\n");
214		err = ENXIO;
215		goto fail_free_regions;
216	}
217
218	err = bus_setup_intr(dev, sc->sc_irq,
219	    INTR_TYPE_BIO | INTR_MPSAFE | INTR_ENTROPY,
220	    ps3disk_intr_filter, ps3disk_intr, sc, &sc->sc_irqctx);
221	if (err) {
222		device_printf(dev, "Could not setup IRQ\n");
223		err = ENXIO;
224		goto fail_release_intr;
225	}
226
227	/* Setup DMA bounce buffer */
228
229	sc->sc_bounce_maxblocks = DFLTPHYS / sc->sc_blksize;
230
231	err = bus_dma_tag_create(bus_get_dma_tag(dev), 4096, 0,
232	    BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL,
233	    sc->sc_bounce_maxblocks * sc->sc_blksize, 1,
234	    sc->sc_bounce_maxblocks * sc->sc_blksize,
235	    0, NULL, NULL, &sc->sc_bounce_dmatag);
236	if (err) {
237		device_printf(dev, "Could not create DMA tag for bounce buffer\n");
238		err = ENXIO;
239		goto fail_teardown_intr;
240	}
241
242	err = bus_dmamem_alloc(sc->sc_bounce_dmatag, (void **) &sc->sc_bounce,
243	    BUS_DMA_WAITOK | BUS_DMA_COHERENT | BUS_DMA_ZERO,
244	    &sc->sc_bounce_dmamap);
245	if (err) {
246		device_printf(dev, "Could not allocate DMA memory for bounce buffer\n");
247		err = ENXIO;
248		goto fail_destroy_dmatag;
249	}
250
251	err = bus_dmamap_load(sc->sc_bounce_dmatag, sc->sc_bounce_dmamap,
252	    sc->sc_bounce, sc->sc_bounce_maxblocks * sc->sc_blksize,
253	    ps3disk_getphys, &sc->sc_bounce_dmaphys, 0);
254	if (err) {
255		device_printf(dev, "Could not load DMA map for bounce buffer\n");
256		err = ENXIO;
257		goto fail_free_dmamem;
258	}
259
260	sc->sc_bounce_lpar = vtophys(sc->sc_bounce);
261
262	if (bootverbose)
263		device_printf(dev, "bounce buffer lpar address 0x%016lx\n",
264		    sc->sc_bounce_lpar);
265
266	/* Setup disks */
267
268	sc->sc_disk = malloc(sc->sc_nregs * sizeof(struct disk *),
269	    M_PS3DISK, M_ZERO | M_WAITOK);
270	if (!sc->sc_disk) {
271		device_printf(dev, "Could not allocate disk(s)\n");
272		err = ENOMEM;
273		goto fail_unload_dmamem;
274	}
275
276	for (i = 0; i < sc->sc_nregs; i++) {
277		struct ps3disk_region *rp = &sc->sc_reg[i];
278
279		d = sc->sc_disk[i] = disk_alloc();
280		d->d_open = ps3disk_open;
281		d->d_close = ps3disk_close;
282		d->d_strategy = ps3disk_strategy;
283		d->d_name = "ps3disk";
284		d->d_drv1 = sc;
285		d->d_maxsize = DFLTPHYS;
286		d->d_sectorsize = sc->sc_blksize;
287		d->d_unit = i;
288		d->d_mediasize = sc->sc_reg[i].r_size * sc->sc_blksize;
289		d->d_flags |= DISKFLAG_CANFLUSHCACHE;
290
291		mb = d->d_mediasize >> 20;
292		unit = 'M';
293		if (mb >= 10240) {
294			unit = 'G';
295			mb /= 1024;
296		}
297
298		/* Test to see if we can read this region */
299		err = lv1_storage_read(ps3bus_get_device(dev), d->d_unit,
300		    0, 1, rp->r_flags, sc->sc_bounce_lpar, &sc->sc_bounce_tag);
301		device_printf(dev, "region %d %ju%cB%s\n", i, mb, unit,
302		     (err == 0) ? "" : " (hypervisor protected)");
303
304		if (err == 0)
305			disk_create(d, DISK_VERSION);
306	}
307	err = 0;
308
309	bioq_init(&sc->sc_bioq);
310
311	ps3disk_sysctlattach(sc);
312
313	sc->sc_running = 1;
314
315	kproc_create(&ps3disk_task, sc, &sc->sc_task, 0, 0, "task: ps3disk");
316
317	return (0);
318
319fail_unload_dmamem:
320
321	bus_dmamap_unload(sc->sc_bounce_dmatag, sc->sc_bounce_dmamap);
322
323fail_free_dmamem:
324
325	bus_dmamem_free(sc->sc_bounce_dmatag, sc->sc_bounce, sc->sc_bounce_dmamap);
326
327fail_destroy_dmatag:
328
329	bus_dma_tag_destroy(sc->sc_bounce_dmatag);
330
331fail_teardown_intr:
332
333	bus_teardown_intr(dev, sc->sc_irq, sc->sc_irqctx);
334
335fail_release_intr:
336
337	bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irqid, sc->sc_irq);
338
339fail_free_regions:
340
341	free(sc->sc_reg, M_PS3DISK);
342
343fail_destroy_lock:
344
345	PS3DISK_LOCK_DESTROY(sc);
346
347	return (err);
348}
349
350static int
351ps3disk_detach(device_t dev)
352{
353	struct ps3disk_softc *sc = device_get_softc(dev);
354	int i;
355
356	PS3DISK_LOCK(sc);
357	sc->sc_running = 0;
358	wakeup(sc);
359	PS3DISK_UNLOCK(sc);
360
361	PS3DISK_LOCK(sc);
362	while (sc->sc_running != -1)
363		msleep(sc, &sc->sc_mtx, PRIBIO, "detach", 0);
364	PS3DISK_UNLOCK(sc);
365
366	for (i = 0; i < sc->sc_nregs; i++)
367		disk_destroy(sc->sc_disk[i]);
368
369	bus_dmamap_unload(sc->sc_bounce_dmatag, sc->sc_bounce_dmamap);
370	bus_dmamem_free(sc->sc_bounce_dmatag, sc->sc_bounce, sc->sc_bounce_dmamap);
371	bus_dma_tag_destroy(sc->sc_bounce_dmatag);
372
373	bus_teardown_intr(dev, sc->sc_irq, sc->sc_irqctx);
374	bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irqid, sc->sc_irq);
375
376	free(sc->sc_disk, M_PS3DISK);
377
378	free(sc->sc_reg, M_PS3DISK);
379
380	PS3DISK_LOCK_DESTROY(sc);
381
382	return (0);
383}
384
385static int
386ps3disk_open(struct disk *dp)
387{
388	return (0);
389}
390
391static int
392ps3disk_close(struct disk *dp)
393{
394	return (0);
395}
396
397static void
398ps3disk_strategy(struct bio *bp)
399{
400	struct ps3disk_softc *sc = (struct ps3disk_softc *) bp->bio_disk->d_drv1;
401
402	if (!sc) {
403		bp->bio_flags |= BIO_ERROR;
404		bp->bio_error = EINVAL;
405		biodone(bp);
406		return;
407	}
408
409	PS3DISK_LOCK(sc);
410	bioq_disksort(&sc->sc_bioq, bp);
411	if (!sc->sc_bounce_busy)
412		wakeup(sc);
413	PS3DISK_UNLOCK(sc);
414}
415
416static void
417ps3disk_task(void *arg)
418{
419	struct ps3disk_softc *sc = (struct ps3disk_softc *) arg;
420	struct bio *bp;
421	daddr_t block, end;
422	u_long nblocks;
423	char *data;
424	int err;
425
426	while (sc->sc_running) {
427		PS3DISK_LOCK(sc);
428		do {
429			bp = bioq_first(&sc->sc_bioq);
430			if (bp == NULL)
431				msleep(sc, &sc->sc_mtx, PRIBIO, "jobqueue", 0);
432		} while (bp == NULL && sc->sc_running);
433		if (bp)
434			bioq_remove(&sc->sc_bioq, bp);
435		PS3DISK_UNLOCK(sc);
436
437		if (!sc->sc_running)
438			break;
439
440		DPRINTF(sc, PS3DISK_DEBUG_TASK, "%s: bio_cmd 0x%02x\n",
441	 	    __func__, bp->bio_cmd);
442
443		if (bp->bio_cmd == BIO_FLUSH) {
444			err = ps3disk_flush(sc);
445
446			if (err) {
447				bp->bio_error = EIO;
448				bp->bio_flags |= BIO_ERROR;
449			} else {
450				bp->bio_error = 0;
451				bp->bio_flags |= BIO_DONE;
452			}
453		} else if (bp->bio_cmd == BIO_READ || bp->bio_cmd == BIO_WRITE) {
454			end = bp->bio_pblkno + (bp->bio_bcount / sc->sc_blksize);
455
456			DPRINTF(sc, PS3DISK_DEBUG_TASK, "%s: bio_pblkno %ld bio_bcount %ld\n",
457	 		    __func__, bp->bio_pblkno, bp->bio_bcount);
458
459			for (block = bp->bio_pblkno; block < end;) {
460				data = bp->bio_data +
461				    (block - bp->bio_pblkno) * sc->sc_blksize;
462
463				nblocks = end - block;
464				if (nblocks > sc->sc_bounce_maxblocks)
465					nblocks = sc->sc_bounce_maxblocks;
466
467				DPRINTF(sc, PS3DISK_DEBUG_TASK, "%s: nblocks %lu\n",
468	 			    __func__, nblocks);
469
470				if (bp->bio_cmd == BIO_READ) {
471					err = ps3disk_read(sc, bp->bio_disk->d_unit,
472					    block, nblocks, data);
473				} else {
474					err = ps3disk_write(sc, bp->bio_disk->d_unit,
475					    block, nblocks, data);
476				}
477
478				if (err)
479					break;
480
481				block += nblocks;
482			}
483
484			bp->bio_resid = (end - block) * sc->sc_blksize;
485			if (bp->bio_resid) {
486				bp->bio_error = EIO;
487				bp->bio_flags |= BIO_ERROR;
488			} else {
489				bp->bio_error = 0;
490				bp->bio_flags |= BIO_DONE;
491			}
492
493			DPRINTF(sc, PS3DISK_DEBUG_TASK, "%s: bio_resid %ld\n",
494	 		    __func__, bp->bio_resid);
495		} else {
496			bp->bio_error = EINVAL;
497			bp->bio_flags |= BIO_ERROR;
498		}
499
500		if (bp->bio_flags & BIO_ERROR)
501			disk_err(bp, "hard error", -1, 1);
502
503		biodone(bp);
504	}
505
506	PS3DISK_LOCK(sc);
507	sc->sc_running = -1;
508	wakeup(sc);
509	PS3DISK_UNLOCK(sc);
510
511	kproc_exit(0);
512}
513
514static int
515ps3disk_intr_filter(void *arg)
516{
517	return (FILTER_SCHEDULE_THREAD);
518}
519
520static void
521ps3disk_intr(void *arg)
522{
523	struct ps3disk_softc *sc = (struct ps3disk_softc *) arg;
524	device_t dev = sc->sc_dev;
525	uint64_t devid = ps3bus_get_device(dev);
526	uint64_t tag, status;
527	int err;
528
529	PS3DISK_LOCK(sc);
530
531	err = lv1_storage_get_async_status(devid, &tag, &status);
532
533	DPRINTF(sc, PS3DISK_DEBUG_INTR, "%s: err %d tag 0x%016lx status 0x%016lx\n",
534	    __func__, err, tag, status);
535
536	if (err)
537		goto out;
538
539	if (!sc->sc_bounce_busy) {
540		device_printf(dev, "Got interrupt while no request pending\n");
541		goto out;
542	}
543
544	if (tag != sc->sc_bounce_tag)
545		device_printf(dev, "Tag mismatch, got 0x%016lx expected 0x%016lx\n",
546		    tag, sc->sc_bounce_tag);
547
548	if (status)
549		device_printf(dev, "Request completed with status 0x%016lx\n", status);
550
551	sc->sc_bounce_status = status;
552	sc->sc_bounce_busy = 0;
553
554	wakeup(sc);
555
556out:
557
558	PS3DISK_UNLOCK(sc);
559}
560
561static void
562ps3disk_getphys(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
563{
564	if (error != 0)
565		return;
566
567	*(bus_addr_t *) arg = segs[0].ds_addr;
568}
569
570static int
571ps3disk_get_disk_geometry(struct ps3disk_softc *sc)
572{
573	device_t dev = sc->sc_dev;
574	uint64_t bus_index = ps3bus_get_busidx(dev);
575	uint64_t dev_index = ps3bus_get_devidx(dev);
576	uint64_t junk;
577	int err;
578
579	err = lv1_get_repository_node_value(PS3_LPAR_ID_PME,
580	    (lv1_repository_string("bus") >> 32) | bus_index,
581	    lv1_repository_string("dev") | dev_index,
582	    lv1_repository_string("blk_size"), 0, &sc->sc_blksize, &junk);
583	if (err) {
584		device_printf(dev, "Could not get block size (0x%08x)\n", err);
585		err = ENXIO;
586		goto out;
587	}
588
589	err = lv1_get_repository_node_value(PS3_LPAR_ID_PME,
590	    (lv1_repository_string("bus") >> 32) | bus_index,
591	    lv1_repository_string("dev") | dev_index,
592	    lv1_repository_string("n_blocks"), 0, &sc->sc_nblocks, &junk);
593	if (err) {
594		device_printf(dev, "Could not get total number of blocks (0x%08x)\n",
595		    err);
596		err = ENXIO;
597		goto out;
598	}
599
600	err = 0;
601
602out:
603
604	return (err);
605}
606
607static int
608ps3disk_enum_regions(struct ps3disk_softc *sc)
609{
610	device_t dev = sc->sc_dev;
611	uint64_t bus_index = ps3bus_get_busidx(dev);
612	uint64_t dev_index = ps3bus_get_devidx(dev);
613	uint64_t junk;
614	int i, err;
615
616	/* Read number of regions */
617
618	err = lv1_get_repository_node_value(PS3_LPAR_ID_PME,
619	    (lv1_repository_string("bus") >> 32) | bus_index,
620	    lv1_repository_string("dev") | dev_index,
621	    lv1_repository_string("n_regs"), 0, &sc->sc_nregs, &junk);
622	if (err) {
623		device_printf(dev, "Could not get number of regions (0x%08x)\n",
624		    err);
625		err = ENXIO;
626		goto fail;
627	}
628
629	if (!sc->sc_nregs)
630		return 0;
631
632	sc->sc_reg = malloc(sc->sc_nregs * sizeof(struct ps3disk_region),
633	    M_PS3DISK, M_ZERO | M_WAITOK);
634	if (!sc->sc_reg) {
635		err = ENOMEM;
636		goto fail;
637	}
638
639	/* Setup regions */
640
641	for (i = 0; i < sc->sc_nregs; i++) {
642		err = lv1_get_repository_node_value(PS3_LPAR_ID_PME,
643		    (lv1_repository_string("bus") >> 32) | bus_index,
644		    lv1_repository_string("dev") | dev_index,
645		    lv1_repository_string("region") | i,
646		    lv1_repository_string("id"), &sc->sc_reg[i].r_id, &junk);
647		if (err) {
648			device_printf(dev, "Could not get region id (0x%08x)\n",
649			    err);
650			err = ENXIO;
651			goto fail;
652		}
653
654		err = lv1_get_repository_node_value(PS3_LPAR_ID_PME,
655		    (lv1_repository_string("bus") >> 32) | bus_index,
656		    lv1_repository_string("dev") | dev_index,
657		    lv1_repository_string("region") | i,
658		    lv1_repository_string("start"), &sc->sc_reg[i].r_start, &junk);
659		if (err) {
660			device_printf(dev, "Could not get region start (0x%08x)\n",
661			    err);
662			err = ENXIO;
663			goto fail;
664		}
665
666		err = lv1_get_repository_node_value(PS3_LPAR_ID_PME,
667		    (lv1_repository_string("bus") >> 32) | bus_index,
668		    lv1_repository_string("dev") | dev_index,
669		    lv1_repository_string("region") | i,
670		    lv1_repository_string("size"), &sc->sc_reg[i].r_size, &junk);
671		if (err) {
672			device_printf(dev, "Could not get region size (0x%08x)\n",
673			    err);
674			err = ENXIO;
675			goto fail;
676		}
677
678		if (i == 0)
679			/* disables HV access control and grants access to whole disk */
680			sc->sc_reg[i].r_flags = 0x2;
681		else
682			sc->sc_reg[i].r_flags = 0;
683	}
684
685	return (0);
686
687fail:
688
689	sc->sc_nregs = 0;
690	if (sc->sc_reg)
691		free(sc->sc_reg, M_PS3DISK);
692
693	return (err);
694}
695
696static int
697ps3disk_read(struct ps3disk_softc *sc, int regidx,
698	uint64_t start_sector, uint64_t sector_count, char *data)
699{
700	device_t dev = sc->sc_dev;
701	struct ps3disk_region *rp = &sc->sc_reg[regidx];
702	uint64_t devid = ps3bus_get_device(dev);
703	int err;
704
705	PS3DISK_LOCK(sc);
706
707	if (sc->sc_bounce_busy) {
708		device_printf(dev, "busy\n");
709		PS3DISK_UNLOCK(sc);
710		return EIO;
711	}
712
713	sc->sc_bounce_busy = 1;
714
715	err = lv1_storage_read(devid, rp->r_id,
716	    start_sector, sector_count, rp->r_flags,
717	    sc->sc_bounce_lpar, &sc->sc_bounce_tag);
718	if (err) {
719		device_printf(dev, "Could not read sectors (0x%08x)\n", err);
720		err = EIO;
721		goto out;
722	}
723
724	DPRINTF(sc, PS3DISK_DEBUG_READ, "%s: tag 0x%016lx\n",
725	    __func__, sc->sc_bounce_tag);
726
727	err = msleep(sc, &sc->sc_mtx, PRIBIO, "read", hz);
728	if (err) {
729		device_printf(dev, "Read request timed out\n");
730		err = EIO;
731		goto out;
732	}
733
734	if (sc->sc_bounce_busy || sc->sc_bounce_status) {
735		err = EIO;
736	} else {
737		bus_dmamap_sync(sc->sc_bounce_dmatag, sc->sc_bounce_dmamap,
738		    BUS_DMASYNC_POSTREAD);
739		memcpy(data, sc->sc_bounce, sector_count * sc->sc_blksize);
740		err = 0;
741	}
742
743out:
744
745	sc->sc_bounce_busy = 0;
746
747	PS3DISK_UNLOCK(sc);
748
749	return (err);
750}
751
752static int
753ps3disk_write(struct ps3disk_softc *sc, int regidx,
754	uint64_t start_sector, uint64_t sector_count, char *data)
755{
756	device_t dev = sc->sc_dev;
757	struct ps3disk_region *rp = &sc->sc_reg[regidx];
758	uint64_t devid = ps3bus_get_device(dev);
759	int err;
760
761	PS3DISK_LOCK(sc);
762
763	if (sc->sc_bounce_busy) {
764		device_printf(dev, "busy\n");
765		PS3DISK_UNLOCK(sc);
766		return EIO;
767	}
768
769	memcpy(sc->sc_bounce, data, sector_count * sc->sc_blksize);
770
771	bus_dmamap_sync(sc->sc_bounce_dmatag, sc->sc_bounce_dmamap,
772	    BUS_DMASYNC_PREWRITE);
773
774	sc->sc_bounce_busy = 1;
775
776	err = lv1_storage_write(devid, rp->r_id,
777	    start_sector, sector_count, rp->r_flags,
778	    sc->sc_bounce_lpar, &sc->sc_bounce_tag);
779	if (err) {
780		device_printf(dev, "Could not write sectors (0x%08x)\n", err);
781		err = EIO;
782		goto out;
783	}
784
785	DPRINTF(sc, PS3DISK_DEBUG_WRITE, "%s: tag 0x%016lx\n",
786	    __func__, sc->sc_bounce_tag);
787
788	err = msleep(sc, &sc->sc_mtx, PRIBIO, "write", hz);
789	if (err) {
790		device_printf(dev, "Write request timed out\n");
791		err = EIO;
792		goto out;
793	}
794
795	err = (sc->sc_bounce_busy || sc->sc_bounce_status) ? EIO : 0;
796
797out:
798
799	sc->sc_bounce_busy = 0;
800
801	PS3DISK_UNLOCK(sc);
802
803	return (err);
804}
805
806static int
807ps3disk_flush(struct ps3disk_softc *sc)
808{
809	device_t dev = sc->sc_dev;
810	uint64_t devid = ps3bus_get_device(dev);
811	int err;
812
813	PS3DISK_LOCK(sc);
814
815	if (sc->sc_bounce_busy) {
816		device_printf(dev, "busy\n");
817		PS3DISK_UNLOCK(sc);
818		return EIO;
819	}
820
821	sc->sc_bounce_busy = 1;
822
823	err = lv1_storage_send_device_command(devid, LV1_STORAGE_ATA_HDDOUT,
824	    0, 0, 0, 0, &sc->sc_bounce_tag);
825	if (err) {
826		device_printf(dev, "Could not flush (0x%08x)\n", err);
827		err = EIO;
828		goto out;
829	}
830
831	DPRINTF(sc, PS3DISK_DEBUG_FLUSH, "%s: tag 0x%016lx\n",
832	    __func__, sc->sc_bounce_tag);
833
834	err = msleep(sc, &sc->sc_mtx, PRIBIO, "flush", hz);
835	if (err) {
836		device_printf(dev, "Flush request timed out\n");
837		err = EIO;
838		goto out;
839	}
840
841	err = (sc->sc_bounce_busy || sc->sc_bounce_status) ? EIO : 0;
842
843out:
844
845	sc->sc_bounce_busy = 0;
846
847	PS3DISK_UNLOCK(sc);
848
849	return (err);
850}
851
852#ifdef PS3DISK_DEBUG
853static int
854ps3disk_sysctl_debug(SYSCTL_HANDLER_ARGS)
855{
856	struct ps3disk_softc *sc = arg1;
857	int debug, error;
858
859	debug = sc->sc_debug;
860
861	error = sysctl_handle_int(oidp, &debug, 0, req);
862	if (error || !req->newptr)
863		return error;
864
865	sc->sc_debug = debug;
866
867	return 0;
868}
869#endif
870
871static void
872ps3disk_sysctlattach(struct ps3disk_softc *sc)
873{
874#ifdef PS3DISK_DEBUG
875	struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->sc_dev);
876	struct sysctl_oid *tree = device_get_sysctl_tree(sc->sc_dev);
877
878	sc->sc_debug = ps3disk_debug;
879
880	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
881		"debug", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
882		ps3disk_sysctl_debug, "I", "control debugging printfs");
883#endif
884}
885
886static device_method_t ps3disk_methods[] = {
887	DEVMETHOD(device_probe,		ps3disk_probe),
888	DEVMETHOD(device_attach,	ps3disk_attach),
889	DEVMETHOD(device_detach,	ps3disk_detach),
890	{0, 0},
891};
892
893static driver_t ps3disk_driver = {
894	"ps3disk",
895	ps3disk_methods,
896	sizeof(struct ps3disk_softc),
897};
898
899static devclass_t ps3disk_devclass;
900
901DRIVER_MODULE(ps3disk, ps3bus, ps3disk_driver, ps3disk_devclass, 0, 0);
902