summaryrefslogtreecommitdiffstats
path: root/sys/powerpc/ps3/ps3bus.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/powerpc/ps3/ps3bus.c')
-rw-r--r--sys/powerpc/ps3/ps3bus.c209
1 files changed, 185 insertions, 24 deletions
diff --git a/sys/powerpc/ps3/ps3bus.c b/sys/powerpc/ps3/ps3bus.c
index 6a5120a..90f0e87 100644
--- a/sys/powerpc/ps3/ps3bus.c
+++ b/sys/powerpc/ps3/ps3bus.c
@@ -1,5 +1,6 @@
/*-
* Copyright (C) 2010 Nathan Whitehorn
+ * Copyright (C) 2011 glevand (geoffrey.levand@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, int bus_index, int dev_index,
}
}
+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);
-
- cdev = device_add_child(self, NULL, -1);
- if (cdev == NULL) {
- device_printf(self,
- "device_add_child failed\n");
- free(dinfo, M_PS3BUS);
- continue;
+ /* OHCI 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, 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 child, int which, uintptr_t *result)
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);
}
@@ -481,9 +629,10 @@ ps3bus_get_dma_tag(device_t dev, device_t child)
{
struct ps3bus_devinfo *dinfo = device_get_ivars(child);
struct ps3bus_softc *sc = device_get_softc(dev);
- int i, err, flags;
+ int i, err, flags, pagesize;
- 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);
@@ -497,9 +646,13 @@ ps3bus_get_dma_tag(device_t dev, device_t child)
dinfo->devtype == PS3_DEVTYPE_USB)
flags = 2; /* 8-bit mode */
+ pagesize = 24; /* log_2(16 MB) */
+ if (dinfo->bustype == PS3_BUSTYPE_STORAGE)
+ pagesize = 12; /* 4 KB */
+
for (i = 0; i < sc->rcount; i++) {
err = lv1_allocate_device_dma_region(dinfo->bus, dinfo->dev,
- sc->regions[i].mr_size, 24 /* log_2(16 MB) */, flags,
+ sc->regions[i].mr_size, pagesize, flags,
&dinfo->dma_base[i]);
if (err != 0) {
device_printf(child,
@@ -523,7 +676,15 @@ ps3bus_get_dma_tag(device_t dev, device_t child)
NULL, NULL, BUS_SPACE_MAXSIZE, 0, BUS_SPACE_MAXSIZE,
0, NULL, NULL, &dinfo->dma_tag);
- bus_dma_tag_set_iommu(dinfo->dma_tag, dev, dinfo);
+ /*
+ * Note: storage devices have IOMMU mappings set up by the hypervisor,
+ * but use physical, non-translated addresses. The above IOMMU
+ * initialization is necessary for the hypervisor to be able to set up
+ * the mappings, but actual DMA mappings should not use the IOMMU
+ * routines.
+ */
+ if (dinfo->bustype != PS3_BUSTYPE_STORAGE)
+ bus_dma_tag_set_iommu(dinfo->dma_tag, dev, dinfo);
fail:
mtx_unlock(&dinfo->iommu_mtx);
OpenPOWER on IntegriCloud