Author: nwhitehorn
Date: Mon Jun 20 00:17:44 2011
New Revision: 223313
URL: http://svn.freebsd.org/changeset/base/223313

Log:
  Driver for PS3's internal hard disk. Hopefully this can be CAM-ified in
  the future, but presents a set of simple block devices for now. With
  (forthcoming) boot loader support or vfs.root.mountfrom, allows booting
  PS3s from disk.
  
  Submitted by: glevand <geoffrey.lev...@mail.ru>

Added:
  head/sys/powerpc/ps3/ps3disk.c   (contents, props changed)
Modified:
  head/sys/conf/files.powerpc
  head/sys/powerpc/ps3/ps3bus.c
  head/sys/powerpc/ps3/ps3bus.h

Modified: head/sys/conf/files.powerpc
==============================================================================
--- head/sys/conf/files.powerpc Sun Jun 19 23:54:01 2011        (r223312)
+++ head/sys/conf/files.powerpc Mon Jun 20 00:17:44 2011        (r223313)
@@ -208,6 +208,7 @@ powerpc/ps3/mmu_ps3.c               optional        ps3
 powerpc/ps3/platform_ps3.c     optional        ps3
 powerpc/ps3/ps3ata.c           optional        ps3 ps3ata
 powerpc/ps3/ps3bus.c           optional        ps3
+powerpc/ps3/ps3disk.c          optional        ps3
 powerpc/ps3/ps3pic.c           optional        ps3
 powerpc/ps3/ps3_syscons.c      optional        ps3 sc
 powerpc/ps3/ps3-hvcall.S       optional        ps3 sc

Modified: head/sys/powerpc/ps3/ps3bus.c
==============================================================================
--- head/sys/powerpc/ps3/ps3bus.c       Sun Jun 19 23:54:01 2011        
(r223312)
+++ head/sys/powerpc/ps3/ps3bus.c       Mon Jun 20 00:17:44 2011        
(r223313)
@@ -1,5 +1,6 @@
 /*-
  * Copyright (C) 2010 Nathan Whitehorn
+ * Copyright (C) 2011 glevand (geoffrey.lev...@mail.ru)
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -73,6 +74,8 @@ struct ps3bus_devinfo {
        int dev;
        uint64_t bustype;
        uint64_t devtype;
+       int busidx;
+       int devidx;
 
        struct resource_list resources;
        bus_dma_tag_t dma_tag;
@@ -89,6 +92,11 @@ enum ps3bus_irq_type {
        EHCI_IRQ = 4,
 };
 
+enum ps3bus_reg_type {
+       OHCI_REG = 3,
+       EHCI_REG = 4,
+};
+
 static device_method_t ps3bus_methods[] = {
        /* Device interface */
        DEVMETHOD(device_identify,      ps3bus_identify),
@@ -235,6 +243,77 @@ ps3bus_resources_init(struct rman *rm, i
        }
 }
 
+static void
+ps3bus_resources_init_by_type(struct rman *rm, int bus_index, int dev_index,
+    uint64_t irq_type, uint64_t reg_type, struct ps3bus_devinfo *dinfo)
+{
+       uint64_t _irq_type, irq, outlet;
+       uint64_t _reg_type, paddr, len;
+       uint64_t ppe, junk;
+       int i, result;
+       int thread;
+
+       resource_list_init(&dinfo->resources);
+
+       lv1_get_logical_ppe_id(&ppe);
+       thread = 32 - fls(mfctrl());
+
+       /* Scan for interrupts */
+       for (i = 0; i < 10; i++) {
+               result = lv1_get_repository_node_value(PS3_LPAR_ID_PME,
+                   (lv1_repository_string("bus") >> 32) | bus_index,
+                   lv1_repository_string("dev") | dev_index,
+                   lv1_repository_string("intr") | i, 0, &_irq_type, &irq);
+
+               if (result != 0)
+                       break;
+
+               if (_irq_type != irq_type)
+                       continue;
+
+               lv1_construct_io_irq_outlet(irq, &outlet);
+               lv1_connect_irq_plug_ext(ppe, thread, outlet, outlet,
+                   0);
+               resource_list_add(&dinfo->resources, SYS_RES_IRQ, i,
+                   outlet, outlet, 1);
+       }
+
+       /* Scan for registers */
+       for (i = 0; i < 10; i++) {
+               result = lv1_get_repository_node_value(PS3_LPAR_ID_PME,
+                   (lv1_repository_string("bus") >> 32) | bus_index,
+                   lv1_repository_string("dev") | dev_index,
+                   lv1_repository_string("reg") | i, 
+                   lv1_repository_string("type"), &_reg_type, &junk);
+
+               if (result != 0)
+                       break;
+
+               if (_reg_type != reg_type)
+                       continue;
+
+               result = lv1_get_repository_node_value(PS3_LPAR_ID_PME,
+                   (lv1_repository_string("bus") >> 32) | bus_index,
+                   lv1_repository_string("dev") | dev_index,
+                   lv1_repository_string("reg") | i, 
+                   lv1_repository_string("data"), &paddr, &len);
+
+               result = lv1_map_device_mmio_region(dinfo->bus, dinfo->dev,
+                   paddr, len, 12 /* log_2(4 KB) */, &paddr);
+
+               if (result != 0) {
+                       printf("Mapping registers failed for device "
+                           "%d.%d (%ld.%ld): %d\n", dinfo->bus, dinfo->dev,
+                           dinfo->bustype, dinfo->devtype, result);
+                       break;
+               }
+
+               rman_manage_region(rm, paddr, paddr + len - 1);
+               resource_list_add(&dinfo->resources, SYS_RES_MEMORY, i,
+                   paddr, paddr + len, len);
+       }
+}
+
 static int 
 ps3bus_attach(device_t self) 
 {
@@ -294,30 +373,93 @@ ps3bus_attach(device_t self) 
                        if (result != 0)
                                continue;
                        
-                       dinfo = malloc(sizeof(*dinfo), M_PS3BUS,
-                           M_WAITOK | M_ZERO);
-
-                       dinfo->bus = bus;
-                       dinfo->dev = dev;
-                       dinfo->bustype = bustype;
-                       dinfo->devtype = devtype;
+                       switch (devtype) {
+                       case PS3_DEVTYPE_USB:
+                               /* USB device has OHCI and EHCI USB host 
controllers */
 
-                       if (dinfo->bustype == PS3_BUSTYPE_SYSBUS)
                                lv1_open_device(bus, dev, 0);
 
-                       ps3bus_resources_init(&sc->sc_mem_rman, bus_index,
-                           dev_index, dinfo);
+                               /* OHCI host controller */
 
-                       cdev = device_add_child(self, NULL, -1);
-                       if (cdev == NULL) {
-                               device_printf(self,
-                                   "device_add_child failed\n");
-                               free(dinfo, M_PS3BUS);
-                               continue;
-                       }
+                               dinfo = malloc(sizeof(*dinfo), M_PS3BUS,
+                                   M_WAITOK | M_ZERO);
+
+                               dinfo->bus = bus;
+                               dinfo->dev = dev;
+                               dinfo->bustype = bustype;
+                               dinfo->devtype = devtype;
+                               dinfo->busidx = bus_index;
+                               dinfo->devidx = dev_index;
+
+                               ps3bus_resources_init_by_type(&sc->sc_mem_rman, 
bus_index,
+                                   dev_index, OHCI_IRQ, OHCI_REG, dinfo);
+
+                               cdev = device_add_child(self, "ohci", -1);
+                               if (cdev == NULL) {
+                                       device_printf(self,
+                                           "device_add_child failed\n");
+                                       free(dinfo, M_PS3BUS);
+                                       continue;
+                               }
+
+                               mtx_init(&dinfo->iommu_mtx, "iommu", NULL, 
MTX_DEF);
+                               device_set_ivars(cdev, dinfo);
+
+                               /* EHCI host controller */
+
+                               dinfo = malloc(sizeof(*dinfo), M_PS3BUS,
+                                   M_WAITOK | M_ZERO);
+
+                               dinfo->bus = bus;
+                               dinfo->dev = dev;
+                               dinfo->bustype = bustype;
+                               dinfo->devtype = devtype;
+                               dinfo->busidx = bus_index;
+                               dinfo->devidx = dev_index;
+
+                               ps3bus_resources_init_by_type(&sc->sc_mem_rman, 
bus_index,
+                                   dev_index, EHCI_IRQ, EHCI_REG, dinfo);
+
+                               cdev = device_add_child(self, "ehci", -1);
+                               if (cdev == NULL) {
+                                       device_printf(self,
+                                           "device_add_child failed\n");
+                                       free(dinfo, M_PS3BUS);
+                                       continue;
+                               }
+
+                               mtx_init(&dinfo->iommu_mtx, "iommu", NULL, 
MTX_DEF);
+                               device_set_ivars(cdev, dinfo);
+                               break;
+                       default:
+                               dinfo = malloc(sizeof(*dinfo), M_PS3BUS,
+                                   M_WAITOK | M_ZERO);
+
+                               dinfo->bus = bus;
+                               dinfo->dev = dev;
+                               dinfo->bustype = bustype;
+                               dinfo->devtype = devtype;
+                               dinfo->busidx = bus_index;
+                               dinfo->devidx = dev_index;
+
+                               if (dinfo->bustype == PS3_BUSTYPE_SYSBUS ||
+                                   dinfo->bustype == PS3_BUSTYPE_STORAGE)
+                                       lv1_open_device(bus, dev, 0);
+
+                               ps3bus_resources_init(&sc->sc_mem_rman, 
bus_index,
+                                   dev_index, dinfo);
+
+                               cdev = device_add_child(self, NULL, -1);
+                               if (cdev == NULL) {
+                                       device_printf(self,
+                                           "device_add_child failed\n");
+                                       free(dinfo, M_PS3BUS);
+                                       continue;
+                               }
 
-                       mtx_init(&dinfo->iommu_mtx, "iommu", NULL, MTX_DEF);
-                       device_set_ivars(cdev, dinfo);
+                               mtx_init(&dinfo->iommu_mtx, "iommu", NULL, 
MTX_DEF);
+                               device_set_ivars(cdev, dinfo);
+                       }
                }
        }
        
@@ -361,6 +503,12 @@ ps3bus_read_ivar(device_t bus, device_t 
        case PS3BUS_IVAR_DEVTYPE:
                *result = dinfo->devtype;
                break;
+       case PS3BUS_IVAR_BUSIDX:
+               *result = dinfo->busidx;
+               break;
+       case PS3BUS_IVAR_DEVIDX:
+               *result = dinfo->devidx;
+               break;
        default:
                return (EINVAL);
        }
@@ -483,7 +631,8 @@ ps3bus_get_dma_tag(device_t dev, device_
        struct ps3bus_softc *sc = device_get_softc(dev);
        int i, err, flags;
 
-       if (dinfo->bustype != PS3_BUSTYPE_SYSBUS)
+       if (dinfo->bustype != PS3_BUSTYPE_SYSBUS &&
+           dinfo->bustype != PS3_BUSTYPE_STORAGE)
                return (bus_get_dma_tag(dev));
 
        mtx_lock(&dinfo->iommu_mtx);

Modified: head/sys/powerpc/ps3/ps3bus.h
==============================================================================
--- head/sys/powerpc/ps3/ps3bus.h       Sun Jun 19 23:54:01 2011        
(r223312)
+++ head/sys/powerpc/ps3/ps3bus.h       Mon Jun 20 00:17:44 2011        
(r223313)
@@ -32,7 +32,9 @@ enum {
        PS3BUS_IVAR_BUS,
        PS3BUS_IVAR_DEVICE,
        PS3BUS_IVAR_BUSTYPE,
-       PS3BUS_IVAR_DEVTYPE
+       PS3BUS_IVAR_DEVTYPE,
+       PS3BUS_IVAR_BUSIDX,
+       PS3BUS_IVAR_DEVIDX,
 };
 
 #define PS3BUS_ACCESSOR(A, B, T) \
@@ -42,6 +44,8 @@ PS3BUS_ACCESSOR(bus,          BUS,            int)
 PS3BUS_ACCESSOR(device,                DEVICE,         int)
 PS3BUS_ACCESSOR(bustype,       BUSTYPE,        uint64_t)
 PS3BUS_ACCESSOR(devtype,       DEVTYPE,        uint64_t)
+PS3BUS_ACCESSOR(busidx,                BUSIDX,         int)
+PS3BUS_ACCESSOR(devidx,                DEVIDX,         int)
 
 /* Bus types */
 enum {

Added: head/sys/powerpc/ps3/ps3disk.c
==============================================================================
--- /dev/null   00:00:00 1970   (empty, because file is newly added)
+++ head/sys/powerpc/ps3/ps3disk.c      Mon Jun 20 00:17:44 2011        
(r223313)
@@ -0,0 +1,893 @@
+/*-
+ * Copyright (C) 2011 glevand (geoffrey.lev...@mail.ru)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/sysctl.h>
+#include <sys/disk.h>
+#include <sys/bio.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/kernel.h>
+#include <sys/kthread.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+
+#include <vm/vm.h>
+#include <vm/pmap.h>
+
+#include <machine/pio.h>
+#include <machine/bus.h>
+#include <machine/platform.h>
+#include <machine/pmap.h>
+#include <machine/resource.h>
+#include <sys/bus.h>
+#include <sys/rman.h>
+
+#include <geom/geom_disk.h>
+
+#include "ps3bus.h"
+#include "ps3-hvcall.h"
+
+#define PS3DISK_LOCK_INIT(_sc)         \
+       mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->sc_dev), "ps3disk", 
MTX_DEF)
+#define PS3DISK_LOCK_DESTROY(_sc)      mtx_destroy(&_sc->sc_mtx);
+#define PS3DISK_LOCK(_sc)              mtx_lock(&(_sc)->sc_mtx)
+#define        PS3DISK_UNLOCK(_sc)             mtx_unlock(&(_sc)->sc_mtx)
+#define PS3DISK_ASSERT_LOCKED(_sc)     mtx_assert(&_sc->sc_mtx, MA_OWNED);
+#define PS3DISK_ASSERT_UNLOCKED(_sc)   mtx_assert(&_sc->sc_mtx, MA_NOTOWNED);
+
+#define LV1_STORAGE_ATA_HDDOUT                 0x23
+
+SYSCTL_NODE(_hw, OID_AUTO, ps3disk, CTLFLAG_RD, 0, "PS3 Disk driver 
parameters");
+
+#ifdef PS3DISK_DEBUG
+static int ps3disk_debug = 0;
+SYSCTL_INT(_hw_ps3disk, OID_AUTO, debug, CTLFLAG_RW, &ps3disk_debug,
+       0, "control debugging printfs");
+TUNABLE_INT("hw.ps3disk.debug", &ps3disk_debug);
+enum {
+       PS3DISK_DEBUG_INTR      = 0x00000001,
+       PS3DISK_DEBUG_TASK      = 0x00000002,
+       PS3DISK_DEBUG_READ      = 0x00000004,
+       PS3DISK_DEBUG_WRITE     = 0x00000008,
+       PS3DISK_DEBUG_FLUSH     = 0x00000010,
+       PS3DISK_DEBUG_ANY       = 0xffffffff
+};
+#define        DPRINTF(sc, m, fmt, ...)                                \
+do {                                                           \
+       if (sc->sc_debug & (m))                                 \
+               printf(fmt, __VA_ARGS__);                       \
+} while (0)
+#else
+#define        DPRINTF(sc, m, fmt, ...)
+#endif
+
+struct ps3disk_region {
+       uint64_t r_id;
+       uint64_t r_start;
+       uint64_t r_size;
+       uint64_t r_flags;
+};
+
+struct ps3disk_softc {
+       device_t sc_dev;
+
+       struct mtx sc_mtx;
+
+       uint64_t sc_blksize;
+       uint64_t sc_nblocks;
+
+       uint64_t sc_nregs;
+       struct ps3disk_region *sc_reg;
+
+       int sc_irqid;
+       struct resource *sc_irq;
+       void *sc_irqctx;
+
+       struct disk **sc_disk;
+
+       struct bio_queue_head sc_bioq;
+
+       struct proc *sc_task;
+
+       int sc_bounce_maxblocks;
+       bus_dma_tag_t sc_bounce_dmatag;
+       bus_dmamap_t sc_bounce_dmamap;
+       bus_addr_t sc_bounce_dmaphys;
+       char *sc_bounce;
+       uint64_t sc_bounce_lpar;
+       int sc_bounce_busy;
+       uint64_t sc_bounce_tag;
+       uint64_t sc_bounce_status;
+
+       int sc_running;
+
+       int sc_debug;
+};
+
+static int ps3disk_open(struct disk *dp);
+static int ps3disk_close(struct disk *dp);
+static void ps3disk_strategy(struct bio *bp);
+static void ps3disk_task(void *arg);
+
+static int ps3disk_intr_filter(void *arg);
+static void ps3disk_intr(void *arg);
+static void ps3disk_getphys(void *arg, bus_dma_segment_t *segs, int nsegs, int 
error);
+static int ps3disk_get_disk_geometry(struct ps3disk_softc *sc);
+static int ps3disk_enum_regions(struct ps3disk_softc *sc);
+static int ps3disk_read(struct ps3disk_softc *sc, int regidx,
+       uint64_t start_sector, uint64_t sector_count, char *data);
+static int ps3disk_write(struct ps3disk_softc *sc, int regidx,
+       uint64_t start_sector, uint64_t sector_count, char *data);
+static int ps3disk_flush(struct ps3disk_softc *sc);
+
+static void ps3disk_sysctlattach(struct ps3disk_softc *sc);
+
+static MALLOC_DEFINE(M_PS3DISK, "ps3disk", "PS3 Disk");
+
+static int
+ps3disk_probe(device_t dev)
+{
+       if (ps3bus_get_bustype(dev) != PS3_BUSTYPE_STORAGE ||
+           ps3bus_get_devtype(dev) != PS3_DEVTYPE_DISK)
+               return (ENXIO);
+
+       device_set_desc(dev, "Playstation 3 Disk");
+
+       return (BUS_PROBE_SPECIFIC);
+}
+
+static int
+ps3disk_attach(device_t dev)
+{
+       struct ps3disk_softc *sc;
+       struct disk *d;
+       intmax_t mb;
+       char unit;
+       int i, err;
+
+       sc = device_get_softc(dev);
+       sc->sc_dev = dev;
+
+       PS3DISK_LOCK_INIT(sc);
+
+       err = ps3disk_get_disk_geometry(sc);
+       if (err) {
+               device_printf(dev, "Could not get disk geometry\n");
+               err = ENXIO;
+               goto fail_destroy_lock;
+       }
+
+       device_printf(dev, "block size %lu total blocks %lu\n",
+           sc->sc_blksize, sc->sc_nblocks);
+
+       err = ps3disk_enum_regions(sc);
+       if (err) {
+               device_printf(dev, "Could not enumerate disk regions\n");
+               err = ENXIO;
+               goto fail_destroy_lock;
+       }
+
+       device_printf(dev, "Found %lu regions\n", sc->sc_nregs);
+
+       if (!sc->sc_nregs) {
+               err = ENXIO;
+               goto fail_destroy_lock;
+       }
+
+       /* Setup interrupt handler */
+
+       sc->sc_irqid = 0;
+       sc->sc_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->sc_irqid,
+           RF_ACTIVE);
+       if (!sc->sc_irq) {
+               device_printf(dev, "Could not allocate IRQ\n");
+               err = ENXIO;
+               goto fail_free_regions;
+       }
+
+       err = bus_setup_intr(dev, sc->sc_irq,
+           INTR_TYPE_BIO | INTR_MPSAFE | INTR_ENTROPY,
+           ps3disk_intr_filter, ps3disk_intr, sc, &sc->sc_irqctx);
+       if (err) {
+               device_printf(dev, "Could not setup IRQ\n");
+               err = ENXIO;
+               goto fail_release_intr;
+       }
+
+       /* Setup DMA bounce buffer */
+
+       sc->sc_bounce_maxblocks = DFLTPHYS / sc->sc_blksize;
+
+       err = bus_dma_tag_create(bus_get_dma_tag(dev), 4096, 0,
+           BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL,
+           sc->sc_bounce_maxblocks * sc->sc_blksize, 1,
+           sc->sc_bounce_maxblocks * sc->sc_blksize,
+           0, NULL, NULL, &sc->sc_bounce_dmatag);
+       if (err) {
+               device_printf(dev, "Could not create DMA tag for bounce 
buffer\n");
+               err = ENXIO;
+               goto fail_teardown_intr;
+       }
+
+       err = bus_dmamem_alloc(sc->sc_bounce_dmatag, (void **) &sc->sc_bounce,
+           BUS_DMA_WAITOK | BUS_DMA_COHERENT | BUS_DMA_ZERO,
+           &sc->sc_bounce_dmamap);
+       if (err) {
+               device_printf(dev, "Could not allocate DMA memory for bounce 
buffer\n");
+               err = ENXIO;
+               goto fail_destroy_dmatag;
+       }
+
+       err = bus_dmamap_load(sc->sc_bounce_dmatag, sc->sc_bounce_dmamap,
+           sc->sc_bounce, sc->sc_bounce_maxblocks * sc->sc_blksize,
+           ps3disk_getphys, &sc->sc_bounce_dmaphys, 0);
+       if (err) {
+               device_printf(dev, "Could not load DMA map for bounce 
buffer\n");
+               err = ENXIO;
+               goto fail_free_dmamem;
+       }
+
+       sc->sc_bounce_lpar = vtophys(sc->sc_bounce);
+
+       if (bootverbose)
+               device_printf(dev, "bounce buffer lpar address 0x%016lx\n",
+                   sc->sc_bounce_lpar);
+
+       /* Setup disks */
+
+       sc->sc_disk = malloc(sc->sc_nregs * sizeof(struct disk *),
+           M_PS3DISK, M_ZERO | M_WAITOK);
+       if (!sc->sc_disk) {
+               device_printf(dev, "Could not allocate disk(s)\n");
+               err = ENOMEM;
+               goto fail_unload_dmamem;
+       }
+
+       for (i = 0; i < sc->sc_nregs; i++) {
+               d = sc->sc_disk[i] = disk_alloc();
+               d->d_open = ps3disk_open;
+               d->d_close = ps3disk_close;
+               d->d_strategy = ps3disk_strategy;
+               d->d_name = "ps3disk";
+               d->d_drv1 = sc;
+               d->d_maxsize = DFLTPHYS;
+               d->d_sectorsize = sc->sc_blksize;
+               d->d_unit = i;
+               d->d_mediasize = sc->sc_reg[i].r_size * sc->sc_blksize;
+               d->d_flags |= DISKFLAG_CANFLUSHCACHE;
+
+               mb = d->d_mediasize >> 20;
+               unit = 'M';
+               if (mb >= 10240) {
+                       unit = 'G';
+                       mb /= 1024;
+               }
+
+               device_printf(dev, "region %d %ju%cB\n", i, mb, unit);
+
+               disk_create(d, DISK_VERSION);
+       }
+
+       bioq_init(&sc->sc_bioq);
+
+       ps3disk_sysctlattach(sc);
+
+       sc->sc_running = 1;
+
+       kproc_create(&ps3disk_task, sc, &sc->sc_task, 0, 0, "task: ps3disk");
+
+       return (0);
+
+fail_unload_dmamem:
+
+       bus_dmamap_unload(sc->sc_bounce_dmatag, sc->sc_bounce_dmamap);
+
+fail_free_dmamem:
+
+       bus_dmamem_free(sc->sc_bounce_dmatag, sc->sc_bounce, 
sc->sc_bounce_dmamap);
+
+fail_destroy_dmatag:
+
+       bus_dma_tag_destroy(sc->sc_bounce_dmatag);
+
+fail_teardown_intr:
+
+       bus_teardown_intr(dev, sc->sc_irq, sc->sc_irqctx);
+
+fail_release_intr:
+
+       bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irqid, sc->sc_irq);
+
+fail_free_regions:
+
+       free(sc->sc_reg, M_PS3DISK);
+
+fail_destroy_lock:
+
+       PS3DISK_LOCK_DESTROY(sc);
+
+       return (err);
+}
+
+static int
+ps3disk_detach(device_t dev)
+{
+       struct ps3disk_softc *sc = device_get_softc(dev);
+       int i;
+
+       PS3DISK_LOCK(sc);
+       sc->sc_running = 0;
+       wakeup(sc);
+       PS3DISK_UNLOCK(sc);
+
+       PS3DISK_LOCK(sc);
+       while (sc->sc_running != -1)
+               msleep(sc, &sc->sc_mtx, PRIBIO, "detach", 0);
+       PS3DISK_UNLOCK(sc);
+
+       for (i = 0; i < sc->sc_nregs; i++)
+               disk_destroy(sc->sc_disk[i]);
+
+       bus_dmamap_unload(sc->sc_bounce_dmatag, sc->sc_bounce_dmamap);
+       bus_dmamem_free(sc->sc_bounce_dmatag, sc->sc_bounce, 
sc->sc_bounce_dmamap);
+       bus_dma_tag_destroy(sc->sc_bounce_dmatag);
+
+       bus_teardown_intr(dev, sc->sc_irq, sc->sc_irqctx);
+       bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irqid, sc->sc_irq);
+
+       free(sc->sc_disk, M_PS3DISK);
+
+       free(sc->sc_reg, M_PS3DISK);
+
+       PS3DISK_LOCK_DESTROY(sc);
+
+       return (0);
+}
+
+static int
+ps3disk_open(struct disk *dp)
+{
+       return (0);
+}
+
+static int
+ps3disk_close(struct disk *dp)
+{
+       return (0);
+}
+
+static void
+ps3disk_strategy(struct bio *bp)
+{
+       struct ps3disk_softc *sc = (struct ps3disk_softc *) 
bp->bio_disk->d_drv1;
+
+       if (!sc) {
+               bp->bio_flags |= BIO_ERROR;
+               bp->bio_error = EINVAL;
+               biodone(bp);
+               return;
+       }
+
+       PS3DISK_LOCK(sc);
+       bioq_disksort(&sc->sc_bioq, bp);
+       if (!sc->sc_bounce_busy)
+               wakeup(sc);
+       PS3DISK_UNLOCK(sc);
+}
+
+static void
+ps3disk_task(void *arg)
+{
+       struct ps3disk_softc *sc = (struct ps3disk_softc *) arg;
+       struct bio *bp;
+       daddr_t block, end;
+       u_long nblocks;
+       char *data;
+       int err;
+
+       while (sc->sc_running) {
+               PS3DISK_LOCK(sc);
+               do {
+                       bp = bioq_first(&sc->sc_bioq);
+                       if (bp == NULL)
+                               msleep(sc, &sc->sc_mtx, PRIBIO, "jobqueue", 0);
+               } while (bp == NULL && sc->sc_running);
+               if (bp)
+                       bioq_remove(&sc->sc_bioq, bp);
+               PS3DISK_UNLOCK(sc);
+
+               if (!sc->sc_running)
+                       break;
+
+               DPRINTF(sc, PS3DISK_DEBUG_TASK, "%s: bio_cmd 0x%02x\n",
+                   __func__, bp->bio_cmd);
+
+               if (bp->bio_cmd == BIO_FLUSH) {
+                       err = ps3disk_flush(sc);
+
+                       if (err) {
+                               bp->bio_error = EIO;
+                               bp->bio_flags |= BIO_ERROR;
+                       } else {
+                               bp->bio_error = 0;
+                               bp->bio_flags |= BIO_DONE;
+                       }
+               } else if (bp->bio_cmd == BIO_READ || bp->bio_cmd == BIO_WRITE) 
{
+                       end = bp->bio_pblkno + (bp->bio_bcount / 
sc->sc_blksize);
+
+                       DPRINTF(sc, PS3DISK_DEBUG_TASK, "%s: bio_pblkno %ld 
bio_bcount %ld\n",
+                           __func__, bp->bio_pblkno, bp->bio_bcount);
+
+                       for (block = bp->bio_pblkno; block < end;) {
+                               data = bp->bio_data + 
+                                   (block - bp->bio_pblkno) * sc->sc_blksize;
+
+                               nblocks = end - block;
+                               if (nblocks > sc->sc_bounce_maxblocks)
+                                       nblocks = sc->sc_bounce_maxblocks;
+
+                               DPRINTF(sc, PS3DISK_DEBUG_TASK, "%s: nblocks 
%lu\n",
+                                   __func__, nblocks);
+
+                               if (bp->bio_cmd == BIO_READ) {
+                                       err = ps3disk_read(sc, 
bp->bio_disk->d_unit,
+                                           block, nblocks, data);
+                               } else {
+                                       err = ps3disk_write(sc, 
bp->bio_disk->d_unit,
+                                           block, nblocks, data);
+                               }
+               
+                               if (err)
+                                       break;
+
+                               block += nblocks;
+                       }
+
+                       bp->bio_resid = (end - block) * sc->sc_blksize;
+                       if (bp->bio_resid) {
+                               bp->bio_error = EIO;
+                               bp->bio_flags |= BIO_ERROR;
+                       } else {
+                               bp->bio_error = 0;
+                               bp->bio_flags |= BIO_DONE;
+                       }
+
+                       DPRINTF(sc, PS3DISK_DEBUG_TASK, "%s: bio_resid %ld\n",
+                           __func__, bp->bio_resid);
+               } else {
+                       bp->bio_error = EINVAL;
+                       bp->bio_flags |= BIO_ERROR;
+               }
+
+               if (bp->bio_flags & BIO_ERROR)
+                       disk_err(bp, "hard error", -1, 1);
+
+               biodone(bp);
+       }
+
+       PS3DISK_LOCK(sc);
+       sc->sc_running = -1;
+       wakeup(sc);
+       PS3DISK_UNLOCK(sc);
+
+       kproc_exit(0);
+}
+
+static int
+ps3disk_intr_filter(void *arg)
+{
+       return (FILTER_SCHEDULE_THREAD);
+}
+
+static void
+ps3disk_intr(void *arg)
+{
+       struct ps3disk_softc *sc = (struct ps3disk_softc *) arg;
+       device_t dev = sc->sc_dev;
+       uint64_t devid = ps3bus_get_device(dev);
+       uint64_t tag, status;
+       int err;
+
+       PS3DISK_LOCK(sc);
+
+       err = lv1_storage_get_async_status(devid, &tag, &status);
+
+       DPRINTF(sc, PS3DISK_DEBUG_INTR, "%s: err %d tag 0x%016lx status 
0x%016lx\n",
+           __func__, err, tag, status);
+
+       if (err)
+               goto out;
+
+       if (!sc->sc_bounce_busy) {
+               device_printf(dev, "Got interrupt while no request pending\n");
+               goto out;
+       }
+
+       if (tag != sc->sc_bounce_tag)
+               device_printf(dev, "Tag mismatch, got 0x%016lx expected 
0x%016lx\n",
+                   tag, sc->sc_bounce_tag);
+
+       if (status)
+               device_printf(dev, "Request completed with status 0x%016lx\n", 
status);
+
+       sc->sc_bounce_status = status;
+       sc->sc_bounce_busy = 0;
+
+       wakeup(sc);
+
+out:
+
+       PS3DISK_UNLOCK(sc);
+}
+
+static void
+ps3disk_getphys(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
+{
+       if (error != 0)
+               return;
+
+       *(bus_addr_t *) arg = segs[0].ds_addr;
+}
+
+static int
+ps3disk_get_disk_geometry(struct ps3disk_softc *sc)
+{
+       device_t dev = sc->sc_dev;
+       uint64_t bus_index = ps3bus_get_busidx(dev);
+       uint64_t dev_index = ps3bus_get_devidx(dev);
+       uint64_t junk;
+       int err;
+
+       err = lv1_get_repository_node_value(PS3_LPAR_ID_PME,
+           (lv1_repository_string("bus") >> 32) | bus_index,
+           lv1_repository_string("dev") | dev_index,
+           lv1_repository_string("blk_size"), 0, &sc->sc_blksize, &junk);
+       if (err) {
+               device_printf(dev, "Could not get block size (0x%08x)\n", err);
+               err = ENXIO;
+               goto out;
+       }
+
+       err = lv1_get_repository_node_value(PS3_LPAR_ID_PME,
+           (lv1_repository_string("bus") >> 32) | bus_index,
+           lv1_repository_string("dev") | dev_index,
+           lv1_repository_string("n_blocks"), 0, &sc->sc_nblocks, &junk);
+       if (err) {
+               device_printf(dev, "Could not get total number of blocks 
(0x%08x)\n",
+                   err);
+               err = ENXIO;
+               goto out;
+       }
+
+       err = 0;
+
+out:
+
+       return (err);
+}
+
+static int
+ps3disk_enum_regions(struct ps3disk_softc *sc)
+{
+       device_t dev = sc->sc_dev;
+       uint64_t bus_index = ps3bus_get_busidx(dev);
+       uint64_t dev_index = ps3bus_get_devidx(dev);
+       uint64_t junk;
+       int i, err;
+
+       /* Read number of regions */
+
+       err = lv1_get_repository_node_value(PS3_LPAR_ID_PME,
+           (lv1_repository_string("bus") >> 32) | bus_index,
+           lv1_repository_string("dev") | dev_index,
+           lv1_repository_string("n_regs"), 0, &sc->sc_nregs, &junk);
+       if (err) {
+               device_printf(dev, "Could not get number of regions (0x%08x)\n",
+                   err);
+               err = ENXIO;
+               goto fail;
+       }
+
+       if (!sc->sc_nregs)
+               return 0;
+
+       sc->sc_reg = malloc(sc->sc_nregs * sizeof(struct ps3disk_region),
+           M_PS3DISK, M_ZERO | M_WAITOK);
+       if (!sc->sc_reg) {
+               err = ENOMEM;
+               goto fail;
+       }
+
+       /* Setup regions */
+
+       for (i = 0; i < sc->sc_nregs; i++) {
+               err = lv1_get_repository_node_value(PS3_LPAR_ID_PME,
+                   (lv1_repository_string("bus") >> 32) | bus_index,
+                   lv1_repository_string("dev") | dev_index,
+                   lv1_repository_string("region") | i,
+                   lv1_repository_string("id"), &sc->sc_reg[i].r_id, &junk);
+               if (err) {
+                       device_printf(dev, "Could not get region id (0x%08x)\n",
+                           err);
+                       err = ENXIO;
+                       goto fail;
+               }
+
+               err = lv1_get_repository_node_value(PS3_LPAR_ID_PME,
+                   (lv1_repository_string("bus") >> 32) | bus_index,
+                   lv1_repository_string("dev") | dev_index,
+                   lv1_repository_string("region") | i,
+                   lv1_repository_string("start"), &sc->sc_reg[i].r_start, 
&junk);
+               if (err) {
+                       device_printf(dev, "Could not get region start 
(0x%08x)\n",
+                           err);
+                       err = ENXIO;
+                       goto fail;
+               }
+
+               err = lv1_get_repository_node_value(PS3_LPAR_ID_PME,
+                   (lv1_repository_string("bus") >> 32) | bus_index,
+                   lv1_repository_string("dev") | dev_index,
+                   lv1_repository_string("region") | i,
+                   lv1_repository_string("size"), &sc->sc_reg[i].r_size, 
&junk);
+               if (err) {
+                       device_printf(dev, "Could not get region size 
(0x%08x)\n",
+                           err);
+                       err = ENXIO;
+                       goto fail;
+               }
+
+               if (i == 0)
+                       /* disables HV access control and grants access to 
whole disk */
+                       sc->sc_reg[i].r_flags = 0x2;
+               else
+                       sc->sc_reg[i].r_flags = 0;
+       }
+
+       return (0);
+
+fail:
+
+       sc->sc_nregs = 0;
+       if (sc->sc_reg)
+               free(sc->sc_reg, M_PS3DISK);
+
+       return (err);
+}
+
+static int
+ps3disk_read(struct ps3disk_softc *sc, int regidx,
+       uint64_t start_sector, uint64_t sector_count, char *data)
+{
+       device_t dev = sc->sc_dev;
+       struct ps3disk_region *rp = &sc->sc_reg[regidx];
+       uint64_t devid = ps3bus_get_device(dev);
+       int err;
+
+       PS3DISK_LOCK(sc);
+
+       if (sc->sc_bounce_busy) {
+               device_printf(dev, "busy\n");
+               PS3DISK_UNLOCK(sc);
+               return EIO;
+       }
+
+       sc->sc_bounce_busy = 1;
+
+       err = lv1_storage_read(devid, rp->r_id,
+           start_sector, sector_count, rp->r_flags,
+           sc->sc_bounce_lpar, &sc->sc_bounce_tag);
+       if (err) {
+               device_printf(dev, "Could not read sectors (0x%08x)\n", err);
+               err = EIO;
+               goto out;
+       }
+
+       DPRINTF(sc, PS3DISK_DEBUG_READ, "%s: tag 0x%016lx\n",
+           __func__, sc->sc_bounce_tag);
+
+       err = msleep(sc, &sc->sc_mtx, PRIBIO, "read", hz);
+       if (err) {
+               device_printf(dev, "Read request timed out\n");

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
_______________________________________________
svn-src-head@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to