aboutsummaryrefslogtreecommitdiff
path: root/sys/powerpc/ps3
diff options
context:
space:
mode:
Diffstat (limited to 'sys/powerpc/ps3')
-rw-r--r--sys/powerpc/ps3/ehci_ps3.c175
-rw-r--r--sys/powerpc/ps3/if_glc.c954
-rw-r--r--sys/powerpc/ps3/if_glcreg.h159
-rw-r--r--sys/powerpc/ps3/mmu_ps3.c288
-rw-r--r--sys/powerpc/ps3/ohci_ps3.c167
-rw-r--r--sys/powerpc/ps3/platform_ps3.c290
-rw-r--r--sys/powerpc/ps3/ps3-hv-asm.awk56
-rw-r--r--sys/powerpc/ps3/ps3-hv-header.awk40
-rw-r--r--sys/powerpc/ps3/ps3-hvcall.S1366
-rw-r--r--sys/powerpc/ps3/ps3-hvcall.h137
-rw-r--r--sys/powerpc/ps3/ps3-hvcall.master138
-rw-r--r--sys/powerpc/ps3/ps3_syscons.c237
-rw-r--r--sys/powerpc/ps3/ps3bus.c780
-rw-r--r--sys/powerpc/ps3/ps3bus.h69
-rw-r--r--sys/powerpc/ps3/ps3cdrom.c706
-rw-r--r--sys/powerpc/ps3/ps3disk.c712
-rw-r--r--sys/powerpc/ps3/ps3pic.c246
17 files changed, 6520 insertions, 0 deletions
diff --git a/sys/powerpc/ps3/ehci_ps3.c b/sys/powerpc/ps3/ehci_ps3.c
new file mode 100644
index 000000000000..d42d8e96d3f7
--- /dev/null
+++ b/sys/powerpc/ps3/ehci_ps3.c
@@ -0,0 +1,175 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (C) 2010 Nathan Whitehorn
+ * 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 TOOLS GMBH 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>
+#include <sys/stdint.h>
+#include <sys/stddef.h>
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <sys/module.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/condvar.h>
+#include <sys/sysctl.h>
+#include <sys/sx.h>
+#include <sys/unistd.h>
+#include <sys/callout.h>
+#include <sys/malloc.h>
+#include <sys/priv.h>
+
+#include <sys/rman.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_util.h>
+
+#include <dev/usb/usb_controller.h>
+#include <dev/usb/usb_bus.h>
+#include <dev/usb/controller/ehci.h>
+#include <dev/usb/controller/ehcireg.h>
+
+#include "ps3bus.h"
+
+static void
+ehci_ps3_post_reset(struct ehci_softc *ehci_softc)
+{
+ uint32_t usbmode;
+
+ /* Select big-endian mode */
+ usbmode = EOREAD4(ehci_softc, EHCI_USBMODE_NOLPM);
+ usbmode |= EHCI_UM_ES_BE;
+ EOWRITE4(ehci_softc, EHCI_USBMODE_NOLPM, usbmode);
+}
+
+static int
+ehci_ps3_probe(device_t dev)
+{
+ if (ps3bus_get_bustype(dev) != PS3_BUSTYPE_SYSBUS ||
+ ps3bus_get_devtype(dev) != PS3_DEVTYPE_USB)
+ return (ENXIO);
+
+ device_set_desc(dev, "Playstation 3 USB 2.0 controller");
+ return (BUS_PROBE_SPECIFIC);
+}
+
+static int
+ehci_ps3_attach(device_t dev)
+{
+ ehci_softc_t *sc = device_get_softc(dev);
+ int rid, err;
+
+ sc->sc_bus.parent = dev;
+ sc->sc_bus.devices = sc->sc_devices;
+ sc->sc_bus.devices_max = EHCI_MAX_DEVICES;
+ sc->sc_bus.dma_bits = 32;
+
+ /* get all DMA memory */
+ if (usb_bus_mem_alloc_all(&sc->sc_bus,
+ USB_GET_DMA_TAG(dev), &ehci_iterate_hw_softc))
+ return (ENOMEM);
+
+ rid = 1;
+ sc->sc_io_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
+ &rid, RF_ACTIVE);
+
+ if (!sc->sc_io_res) {
+ device_printf(dev, "Could not map memory\n");
+ goto error;
+ }
+
+ sc->sc_io_tag = rman_get_bustag(sc->sc_io_res);
+ sc->sc_io_hdl = rman_get_bushandle(sc->sc_io_res);
+ sc->sc_io_size = rman_get_size(sc->sc_io_res);
+
+ rid = 1;
+ sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
+ RF_SHAREABLE | RF_ACTIVE);
+
+ if (sc->sc_irq_res == NULL) {
+ device_printf(dev, "Could not allocate irq\n");
+ return (ENXIO);
+ }
+
+ sc->sc_bus.bdev = device_add_child(dev, "usbus", DEVICE_UNIT_ANY);
+ if (!sc->sc_bus.bdev) {
+ device_printf(dev, "Could not add USB device\n");
+ return (ENXIO);
+ }
+
+ device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus);
+
+ sprintf(sc->sc_vendor, "Sony");
+
+ err = bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE,
+ NULL, (driver_intr_t *)ehci_interrupt, sc, &sc->sc_intr_hdl);
+ if (err) {
+ device_printf(dev, "Could not setup error irq, %d\n", err);
+ goto error;
+ }
+
+ sc->sc_vendor_post_reset = ehci_ps3_post_reset;
+ err = ehci_init(sc);
+ if (err) {
+ device_printf(dev, "USB init failed err=%d\n", err);
+ goto error;
+ }
+
+ err = device_probe_and_attach(sc->sc_bus.bdev);
+ if (err == 0)
+ return (0);
+
+error:
+ return (ENXIO);
+}
+
+static device_method_t ehci_ps3_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, ehci_ps3_probe),
+ DEVMETHOD(device_attach, ehci_ps3_attach),
+ DEVMETHOD(device_resume, bus_generic_resume),
+ DEVMETHOD(device_suspend, bus_generic_suspend),
+ DEVMETHOD(device_shutdown, bus_generic_shutdown),
+
+ DEVMETHOD_END
+};
+
+static driver_t ehci_ps3_driver = {
+ .name = "ehci",
+ .methods = ehci_ps3_methods,
+ .size = sizeof(ehci_softc_t),
+};
+
+DRIVER_MODULE(ehci_ps3, ps3bus, ehci_ps3_driver, 0, 0);
+MODULE_DEPEND(ehci_ps3, usb, 1, 1, 1);
diff --git a/sys/powerpc/ps3/if_glc.c b/sys/powerpc/ps3/if_glc.c
new file mode 100644
index 000000000000..70a6633a03f4
--- /dev/null
+++ b/sys/powerpc/ps3/if_glc.c
@@ -0,0 +1,954 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (C) 2010 Nathan Whitehorn
+ * 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 TOOLS GMBH 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/param.h>
+#include <sys/systm.h>
+#include <sys/sockio.h>
+#include <sys/endian.h>
+#include <sys/lock.h>
+#include <sys/mbuf.h>
+#include <sys/module.h>
+#include <sys/malloc.h>
+#include <sys/mutex.h>
+#include <sys/kernel.h>
+#include <sys/socket.h>
+
+#include <vm/vm.h>
+#include <vm/pmap.h>
+
+#include <net/bpf.h>
+#include <net/if.h>
+#include <net/if_var.h>
+#include <net/ethernet.h>
+#include <net/if_media.h>
+#include <net/if_types.h>
+#include <net/if_dl.h>
+
+#include <machine/pio.h>
+#include <machine/bus.h>
+#include <machine/platform.h>
+#include <machine/resource.h>
+#include <sys/bus.h>
+#include <sys/rman.h>
+
+#include "ps3bus.h"
+#include "ps3-hvcall.h"
+#include "if_glcreg.h"
+
+static int glc_probe(device_t);
+static int glc_attach(device_t);
+static void glc_init(void *xsc);
+static void glc_start(if_t ifp);
+static int glc_ioctl(if_t ifp, u_long cmd, caddr_t data);
+static void glc_set_multicast(struct glc_softc *sc);
+static int glc_add_rxbuf(struct glc_softc *sc, int idx);
+static int glc_add_rxbuf_dma(struct glc_softc *sc, int idx);
+static int glc_encap(struct glc_softc *sc, struct mbuf **m_head,
+ bus_addr_t *pktdesc);
+static int glc_intr_filter(void *xsc);
+static void glc_intr(void *xsc);
+static void glc_tick(void *xsc);
+static void glc_media_status(if_t ifp, struct ifmediareq *ifmr);
+static int glc_media_change(if_t ifp);
+
+static MALLOC_DEFINE(M_GLC, "gelic", "PS3 GELIC ethernet");
+
+static device_method_t glc_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, glc_probe),
+ DEVMETHOD(device_attach, glc_attach),
+ { 0, 0 }
+};
+
+static driver_t glc_driver = {
+ "glc",
+ glc_methods,
+ sizeof(struct glc_softc)
+};
+
+DRIVER_MODULE(glc, ps3bus, glc_driver, 0, 0);
+
+static int
+glc_probe(device_t dev)
+{
+
+ if (ps3bus_get_bustype(dev) != PS3_BUSTYPE_SYSBUS ||
+ ps3bus_get_devtype(dev) != PS3_DEVTYPE_GELIC)
+ return (ENXIO);
+
+ device_set_desc(dev, "Playstation 3 GELIC Network Controller");
+ return (BUS_PROBE_SPECIFIC);
+}
+
+static void
+glc_getphys(void *xaddr, bus_dma_segment_t *segs, int nsegs, int error)
+{
+ if (error != 0)
+ return;
+
+ *(bus_addr_t *)xaddr = segs[0].ds_addr;
+}
+
+static int
+glc_attach(device_t dev)
+{
+ struct glc_softc *sc;
+ struct glc_txsoft *txs;
+ uint64_t mac64, val, junk;
+ int i, err;
+
+ sc = device_get_softc(dev);
+
+ sc->sc_bus = ps3bus_get_bus(dev);
+ sc->sc_dev = ps3bus_get_device(dev);
+ sc->sc_self = dev;
+
+ mtx_init(&sc->sc_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK,
+ MTX_DEF);
+ callout_init_mtx(&sc->sc_tick_ch, &sc->sc_mtx, 0);
+ sc->next_txdma_slot = 0;
+ sc->bsy_txdma_slots = 0;
+ sc->sc_next_rxdma_slot = 0;
+ sc->first_used_txdma_slot = -1;
+
+ /*
+ * Shut down existing tasks.
+ */
+
+ lv1_net_stop_tx_dma(sc->sc_bus, sc->sc_dev, 0);
+ lv1_net_stop_rx_dma(sc->sc_bus, sc->sc_dev, 0);
+
+ sc->sc_ifp = if_alloc(IFT_ETHER);
+ if_setsoftc(sc->sc_ifp, sc);
+
+ /*
+ * Get MAC address and VLAN id
+ */
+
+ lv1_net_control(sc->sc_bus, sc->sc_dev, GELIC_GET_MAC_ADDRESS,
+ 0, 0, 0, &mac64, &junk);
+ memcpy(sc->sc_enaddr, &((uint8_t *)&mac64)[2], sizeof(sc->sc_enaddr));
+ sc->sc_tx_vlan = sc->sc_rx_vlan = -1;
+ err = lv1_net_control(sc->sc_bus, sc->sc_dev, GELIC_GET_VLAN_ID,
+ GELIC_VLAN_TX_ETHERNET, 0, 0, &val, &junk);
+ if (err == 0)
+ sc->sc_tx_vlan = val;
+ err = lv1_net_control(sc->sc_bus, sc->sc_dev, GELIC_GET_VLAN_ID,
+ GELIC_VLAN_RX_ETHERNET, 0, 0, &val, &junk);
+ if (err == 0)
+ sc->sc_rx_vlan = val;
+
+ /*
+ * Set up 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 == NULL) {
+ device_printf(dev, "Could not allocate IRQ!\n");
+ mtx_destroy(&sc->sc_mtx);
+ return (ENXIO);
+ }
+
+ bus_setup_intr(dev, sc->sc_irq,
+ INTR_TYPE_NET | INTR_MPSAFE | INTR_ENTROPY,
+ glc_intr_filter, glc_intr, sc, &sc->sc_irqctx);
+ sc->sc_hwirq_status = (uint64_t *)contigmalloc(8, M_GLC, M_ZERO, 0,
+ BUS_SPACE_MAXADDR_32BIT, 8, PAGE_SIZE);
+ lv1_net_set_interrupt_status_indicator(sc->sc_bus, sc->sc_dev,
+ vtophys(sc->sc_hwirq_status), 0);
+ lv1_net_set_interrupt_mask(sc->sc_bus, sc->sc_dev,
+ GELIC_INT_RXDONE | GELIC_INT_RXFRAME | GELIC_INT_PHY |
+ GELIC_INT_TX_CHAIN_END, 0);
+
+ /*
+ * Set up DMA.
+ */
+
+ err = bus_dma_tag_create(bus_get_dma_tag(dev), 32, 0,
+ BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL,
+ 129*sizeof(struct glc_dmadesc), 1, 128*sizeof(struct glc_dmadesc),
+ 0, NULL,NULL, &sc->sc_dmadesc_tag);
+
+ err = bus_dmamem_alloc(sc->sc_dmadesc_tag, (void **)&sc->sc_txdmadesc,
+ BUS_DMA_WAITOK | BUS_DMA_COHERENT | BUS_DMA_ZERO,
+ &sc->sc_txdmadesc_map);
+ err = bus_dmamap_load(sc->sc_dmadesc_tag, sc->sc_txdmadesc_map,
+ sc->sc_txdmadesc, 128*sizeof(struct glc_dmadesc), glc_getphys,
+ &sc->sc_txdmadesc_phys, 0);
+ err = bus_dmamem_alloc(sc->sc_dmadesc_tag, (void **)&sc->sc_rxdmadesc,
+ BUS_DMA_WAITOK | BUS_DMA_COHERENT | BUS_DMA_ZERO,
+ &sc->sc_rxdmadesc_map);
+ err = bus_dmamap_load(sc->sc_dmadesc_tag, sc->sc_rxdmadesc_map,
+ sc->sc_rxdmadesc, 128*sizeof(struct glc_dmadesc), glc_getphys,
+ &sc->sc_rxdmadesc_phys, 0);
+
+ err = bus_dma_tag_create(bus_get_dma_tag(dev), 128, 0,
+ BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL,
+ BUS_SPACE_MAXSIZE_32BIT, 0, BUS_SPACE_MAXSIZE_32BIT, 0, NULL,NULL,
+ &sc->sc_rxdma_tag);
+ err = bus_dma_tag_create(bus_get_dma_tag(dev), 1, 0,
+ BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL,
+ BUS_SPACE_MAXSIZE_32BIT, 16, BUS_SPACE_MAXSIZE_32BIT, 0, NULL,NULL,
+ &sc->sc_txdma_tag);
+
+ /* init transmit descriptors */
+ STAILQ_INIT(&sc->sc_txfreeq);
+ STAILQ_INIT(&sc->sc_txdirtyq);
+
+ /* create TX DMA maps */
+ err = ENOMEM;
+ for (i = 0; i < GLC_MAX_TX_PACKETS; i++) {
+ txs = &sc->sc_txsoft[i];
+ txs->txs_mbuf = NULL;
+ err = bus_dmamap_create(sc->sc_txdma_tag, 0, &txs->txs_dmamap);
+ if (err) {
+ device_printf(dev,
+ "unable to create TX DMA map %d, error = %d\n",
+ i, err);
+ }
+ STAILQ_INSERT_TAIL(&sc->sc_txfreeq, txs, txs_q);
+ }
+
+ /* Create the receive buffer DMA maps. */
+ for (i = 0; i < GLC_MAX_RX_PACKETS; i++) {
+ err = bus_dmamap_create(sc->sc_rxdma_tag, 0,
+ &sc->sc_rxsoft[i].rxs_dmamap);
+ if (err) {
+ device_printf(dev,
+ "unable to create RX DMA map %d, error = %d\n",
+ i, err);
+ }
+ sc->sc_rxsoft[i].rxs_mbuf = NULL;
+ }
+
+ /*
+ * Attach to network stack
+ */
+
+ if_initname(sc->sc_ifp, device_get_name(dev), device_get_unit(dev));
+ if_setmtu(sc->sc_ifp, ETHERMTU);
+ if_setflags(sc->sc_ifp, IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST);
+ if_sethwassist(sc->sc_ifp, CSUM_TCP | CSUM_UDP);
+ if_setcapabilities(sc->sc_ifp, IFCAP_HWCSUM | IFCAP_RXCSUM);
+ if_setcapenable(sc->sc_ifp, IFCAP_HWCSUM | IFCAP_RXCSUM);
+ if_setstartfn(sc->sc_ifp, glc_start);
+ if_setioctlfn(sc->sc_ifp, glc_ioctl);
+ if_setinitfn(sc->sc_ifp, glc_init);
+
+ ifmedia_init(&sc->sc_media, IFM_IMASK, glc_media_change,
+ glc_media_status);
+ ifmedia_add(&sc->sc_media, IFM_ETHER | IFM_10_T, 0, NULL);
+ ifmedia_add(&sc->sc_media, IFM_ETHER | IFM_10_T | IFM_FDX, 0, NULL);
+ ifmedia_add(&sc->sc_media, IFM_ETHER | IFM_100_TX, 0, NULL);
+ ifmedia_add(&sc->sc_media, IFM_ETHER | IFM_100_TX | IFM_FDX, 0, NULL);
+ ifmedia_add(&sc->sc_media, IFM_ETHER | IFM_1000_T | IFM_FDX, 0, NULL);
+ ifmedia_add(&sc->sc_media, IFM_ETHER | IFM_AUTO, 0, NULL);
+ ifmedia_set(&sc->sc_media, IFM_ETHER | IFM_AUTO);
+
+ if_setsendqlen(sc->sc_ifp, GLC_MAX_TX_PACKETS);
+ if_setsendqready(sc->sc_ifp);
+
+ ether_ifattach(sc->sc_ifp, sc->sc_enaddr);
+ if_sethwassist(sc->sc_ifp, 0);
+
+ return (0);
+
+ mtx_destroy(&sc->sc_mtx);
+ if_free(sc->sc_ifp);
+ return (ENXIO);
+}
+
+static void
+glc_init_locked(struct glc_softc *sc)
+{
+ int i, error;
+ struct glc_rxsoft *rxs;
+ struct glc_txsoft *txs;
+
+ mtx_assert(&sc->sc_mtx, MA_OWNED);
+
+ lv1_net_stop_tx_dma(sc->sc_bus, sc->sc_dev, 0);
+ lv1_net_stop_rx_dma(sc->sc_bus, sc->sc_dev, 0);
+
+ glc_set_multicast(sc);
+
+ for (i = 0; i < GLC_MAX_RX_PACKETS; i++) {
+ rxs = &sc->sc_rxsoft[i];
+ rxs->rxs_desc_slot = i;
+
+ if (rxs->rxs_mbuf == NULL) {
+ glc_add_rxbuf(sc, i);
+
+ if (rxs->rxs_mbuf == NULL) {
+ rxs->rxs_desc_slot = -1;
+ break;
+ }
+ }
+
+ glc_add_rxbuf_dma(sc, i);
+ bus_dmamap_sync(sc->sc_dmadesc_tag, sc->sc_rxdmadesc_map,
+ BUS_DMASYNC_PREREAD);
+ }
+
+ /* Clear TX dirty queue */
+ while ((txs = STAILQ_FIRST(&sc->sc_txdirtyq)) != NULL) {
+ STAILQ_REMOVE_HEAD(&sc->sc_txdirtyq, txs_q);
+ bus_dmamap_unload(sc->sc_txdma_tag, txs->txs_dmamap);
+
+ if (txs->txs_mbuf != NULL) {
+ m_freem(txs->txs_mbuf);
+ txs->txs_mbuf = NULL;
+ }
+
+ STAILQ_INSERT_TAIL(&sc->sc_txfreeq, txs, txs_q);
+ }
+ sc->first_used_txdma_slot = -1;
+ sc->bsy_txdma_slots = 0;
+
+ error = lv1_net_start_rx_dma(sc->sc_bus, sc->sc_dev,
+ sc->sc_rxsoft[0].rxs_desc, 0);
+ if (error != 0)
+ device_printf(sc->sc_self,
+ "lv1_net_start_rx_dma error: %d\n", error);
+
+ if_setdrvflagbits(sc->sc_ifp, IFF_DRV_RUNNING, 0);
+ if_setdrvflagbits(sc->sc_ifp, 0, IFF_DRV_OACTIVE);
+ sc->sc_ifpflags = if_getflags(sc->sc_ifp);
+
+ sc->sc_wdog_timer = 0;
+ callout_reset(&sc->sc_tick_ch, hz, glc_tick, sc);
+}
+
+static void
+glc_stop(void *xsc)
+{
+ struct glc_softc *sc = xsc;
+
+ mtx_assert(&sc->sc_mtx, MA_OWNED);
+
+ lv1_net_stop_tx_dma(sc->sc_bus, sc->sc_dev, 0);
+ lv1_net_stop_rx_dma(sc->sc_bus, sc->sc_dev, 0);
+}
+
+static void
+glc_init(void *xsc)
+{
+ struct glc_softc *sc = xsc;
+
+ mtx_lock(&sc->sc_mtx);
+ glc_init_locked(sc);
+ mtx_unlock(&sc->sc_mtx);
+}
+
+static void
+glc_tick(void *xsc)
+{
+ struct glc_softc *sc = xsc;
+
+ mtx_assert(&sc->sc_mtx, MA_OWNED);
+
+ /*
+ * XXX: Sometimes the RX queue gets stuck. Poke it periodically until
+ * we figure out why. This will fail harmlessly if the RX queue is
+ * already running.
+ */
+ lv1_net_start_rx_dma(sc->sc_bus, sc->sc_dev,
+ sc->sc_rxsoft[sc->sc_next_rxdma_slot].rxs_desc, 0);
+
+ if (sc->sc_wdog_timer == 0 || --sc->sc_wdog_timer != 0) {
+ callout_reset(&sc->sc_tick_ch, hz, glc_tick, sc);
+ return;
+ }
+
+ /* Problems */
+ device_printf(sc->sc_self, "device timeout\n");
+
+ glc_init_locked(sc);
+}
+
+static void
+glc_start_locked(if_t ifp)
+{
+ struct glc_softc *sc = if_getsoftc(ifp);
+ bus_addr_t first, pktdesc;
+ int kickstart = 0;
+ int error;
+ struct mbuf *mb_head;
+
+ mtx_assert(&sc->sc_mtx, MA_OWNED);
+ first = 0;
+
+ if ((if_getdrvflags(ifp) & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) !=
+ IFF_DRV_RUNNING)
+ return;
+
+ if (STAILQ_EMPTY(&sc->sc_txdirtyq))
+ kickstart = 1;
+
+ while (!if_sendq_empty(ifp)) {
+ mb_head = if_dequeue(ifp);
+
+ if (mb_head == NULL)
+ break;
+
+ /* Check if the ring buffer is full */
+ if (sc->bsy_txdma_slots > 125) {
+ /* Put the packet back and stop */
+ if_setdrvflagbits(ifp, IFF_DRV_OACTIVE, 0);
+ if_sendq_prepend(ifp, mb_head);
+ break;
+ }
+
+ BPF_MTAP(ifp, mb_head);
+
+ if (sc->sc_tx_vlan >= 0)
+ mb_head = ether_vlanencap(mb_head, sc->sc_tx_vlan);
+
+ if (glc_encap(sc, &mb_head, &pktdesc)) {
+ if_setdrvflagbits(ifp, IFF_DRV_OACTIVE, 0);
+ break;
+ }
+
+ if (first == 0)
+ first = pktdesc;
+ }
+
+ if (kickstart && first != 0) {
+ error = lv1_net_start_tx_dma(sc->sc_bus, sc->sc_dev, first, 0);
+ if (error != 0)
+ device_printf(sc->sc_self,
+ "lv1_net_start_tx_dma error: %d\n", error);
+ sc->sc_wdog_timer = 5;
+ }
+}
+
+static void
+glc_start(if_t ifp)
+{
+ struct glc_softc *sc = if_getsoftc(ifp);
+
+ mtx_lock(&sc->sc_mtx);
+ glc_start_locked(ifp);
+ mtx_unlock(&sc->sc_mtx);
+}
+
+static int
+glc_ioctl(if_t ifp, u_long cmd, caddr_t data)
+{
+ struct glc_softc *sc = if_getsoftc(ifp);
+ struct ifreq *ifr = (struct ifreq *)data;
+ int err = 0;
+
+ switch (cmd) {
+ case SIOCSIFFLAGS:
+ mtx_lock(&sc->sc_mtx);
+ if ((if_getflags(ifp) & IFF_UP) != 0) {
+ if ((if_getdrvflags(ifp) & IFF_DRV_RUNNING) != 0 &&
+ ((if_getflags(ifp) ^ sc->sc_ifpflags) &
+ (IFF_ALLMULTI | IFF_PROMISC)) != 0)
+ glc_set_multicast(sc);
+ else
+ glc_init_locked(sc);
+ }
+ else if ((if_getdrvflags(ifp) & IFF_DRV_RUNNING) != 0)
+ glc_stop(sc);
+ sc->sc_ifpflags = if_getflags(ifp);
+ mtx_unlock(&sc->sc_mtx);
+ break;
+ case SIOCADDMULTI:
+ case SIOCDELMULTI:
+ mtx_lock(&sc->sc_mtx);
+ glc_set_multicast(sc);
+ mtx_unlock(&sc->sc_mtx);
+ break;
+ case SIOCGIFMEDIA:
+ case SIOCSIFMEDIA:
+ err = ifmedia_ioctl(ifp, ifr, &sc->sc_media, cmd);
+ break;
+ default:
+ err = ether_ioctl(ifp, cmd, data);
+ break;
+ }
+
+ return (err);
+}
+
+static u_int
+glc_add_maddr(void *arg, struct sockaddr_dl *sdl, u_int cnt)
+{
+ struct glc_softc *sc = arg;
+ uint64_t addr;
+
+ /*
+ * Filter can only hold 32 addresses, so fall back to
+ * the IFF_ALLMULTI case if we have too many. +1 is for
+ * broadcast.
+ */
+ if (cnt + 1 == 32)
+ return (0);
+
+ addr = 0;
+ memcpy(&((uint8_t *)(&addr))[2], LLADDR(sdl), ETHER_ADDR_LEN);
+ lv1_net_add_multicast_address(sc->sc_bus, sc->sc_dev, addr, 0);
+
+ return (1);
+}
+
+static void
+glc_set_multicast(struct glc_softc *sc)
+{
+ if_t ifp = sc->sc_ifp;
+ int naddrs;
+
+ /* Clear multicast filter */
+ lv1_net_remove_multicast_address(sc->sc_bus, sc->sc_dev, 0, 1);
+
+ /* Add broadcast */
+ lv1_net_add_multicast_address(sc->sc_bus, sc->sc_dev,
+ 0xffffffffffffL, 0);
+
+ if ((if_getflags(ifp) & IFF_ALLMULTI) != 0) {
+ lv1_net_add_multicast_address(sc->sc_bus, sc->sc_dev, 0, 1);
+ } else {
+ naddrs = if_foreach_llmaddr(ifp, glc_add_maddr, sc);
+ if (naddrs + 1 == 32)
+ lv1_net_add_multicast_address(sc->sc_bus,
+ sc->sc_dev, 0, 1);
+ }
+}
+
+static int
+glc_add_rxbuf(struct glc_softc *sc, int idx)
+{
+ struct glc_rxsoft *rxs = &sc->sc_rxsoft[idx];
+ struct mbuf *m;
+ bus_dma_segment_t segs[1];
+ int error, nsegs;
+
+ m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR);
+ if (m == NULL)
+ return (ENOBUFS);
+ m->m_len = m->m_pkthdr.len = m->m_ext.ext_size;
+
+ if (rxs->rxs_mbuf != NULL) {
+ bus_dmamap_sync(sc->sc_rxdma_tag, rxs->rxs_dmamap,
+ BUS_DMASYNC_POSTREAD);
+ bus_dmamap_unload(sc->sc_rxdma_tag, rxs->rxs_dmamap);
+ }
+
+ error = bus_dmamap_load_mbuf_sg(sc->sc_rxdma_tag, rxs->rxs_dmamap, m,
+ segs, &nsegs, BUS_DMA_NOWAIT);
+ if (error != 0) {
+ device_printf(sc->sc_self,
+ "cannot load RS DMA map %d, error = %d\n", idx, error);
+ m_freem(m);
+ return (error);
+ }
+ /* If nsegs is wrong then the stack is corrupt. */
+ KASSERT(nsegs == 1,
+ ("%s: too many DMA segments (%d)", __func__, nsegs));
+ rxs->rxs_mbuf = m;
+ rxs->segment = segs[0];
+
+ bus_dmamap_sync(sc->sc_rxdma_tag, rxs->rxs_dmamap, BUS_DMASYNC_PREREAD);
+
+ return (0);
+}
+
+static int
+glc_add_rxbuf_dma(struct glc_softc *sc, int idx)
+{
+ struct glc_rxsoft *rxs = &sc->sc_rxsoft[idx];
+
+ bzero(&sc->sc_rxdmadesc[idx], sizeof(sc->sc_rxdmadesc[idx]));
+ sc->sc_rxdmadesc[idx].paddr = rxs->segment.ds_addr;
+ sc->sc_rxdmadesc[idx].len = rxs->segment.ds_len;
+ sc->sc_rxdmadesc[idx].next = sc->sc_rxdmadesc_phys +
+ ((idx + 1) % GLC_MAX_RX_PACKETS)*sizeof(sc->sc_rxdmadesc[idx]);
+ sc->sc_rxdmadesc[idx].cmd_stat = GELIC_DESCR_OWNED;
+
+ rxs->rxs_desc_slot = idx;
+ rxs->rxs_desc = sc->sc_rxdmadesc_phys + idx*sizeof(struct glc_dmadesc);
+
+ return (0);
+}
+
+static int
+glc_encap(struct glc_softc *sc, struct mbuf **m_head, bus_addr_t *pktdesc)
+{
+ bus_dma_segment_t segs[16];
+ struct glc_txsoft *txs;
+ struct mbuf *m;
+ bus_addr_t firstslotphys;
+ int i, idx, nsegs, nsegs_max;
+ int err = 0;
+
+ /* Max number of segments is the number of free DMA slots */
+ nsegs_max = 128 - sc->bsy_txdma_slots;
+
+ if (nsegs_max > 16 || sc->first_used_txdma_slot < 0)
+ nsegs_max = 16;
+
+ /* Get a work queue entry. */
+ if ((txs = STAILQ_FIRST(&sc->sc_txfreeq)) == NULL) {
+ /* Ran out of descriptors. */
+ return (ENOBUFS);
+ }
+
+ nsegs = 0;
+ for (m = *m_head; m != NULL; m = m->m_next)
+ nsegs++;
+
+ if (nsegs > nsegs_max) {
+ m = m_collapse(*m_head, M_NOWAIT, nsegs_max);
+ if (m == NULL) {
+ m_freem(*m_head);
+ *m_head = NULL;
+ return (ENOBUFS);
+ }
+ *m_head = m;
+ }
+
+ err = bus_dmamap_load_mbuf_sg(sc->sc_txdma_tag, txs->txs_dmamap,
+ *m_head, segs, &nsegs, BUS_DMA_NOWAIT);
+ if (err != 0) {
+ m_freem(*m_head);
+ *m_head = NULL;
+ return (err);
+ }
+
+ KASSERT(nsegs <= 128 - sc->bsy_txdma_slots,
+ ("GLC: Mapped too many (%d) DMA segments with %d available",
+ nsegs, 128 - sc->bsy_txdma_slots));
+
+ if (nsegs == 0) {
+ m_freem(*m_head);
+ *m_head = NULL;
+ return (EIO);
+ }
+
+ txs->txs_ndescs = nsegs;
+ txs->txs_firstdesc = sc->next_txdma_slot;
+
+ idx = txs->txs_firstdesc;
+ firstslotphys = sc->sc_txdmadesc_phys +
+ txs->txs_firstdesc*sizeof(struct glc_dmadesc);
+
+ for (i = 0; i < nsegs; i++) {
+ bzero(&sc->sc_txdmadesc[idx], sizeof(sc->sc_txdmadesc[idx]));
+ sc->sc_txdmadesc[idx].paddr = segs[i].ds_addr;
+ sc->sc_txdmadesc[idx].len = segs[i].ds_len;
+ sc->sc_txdmadesc[idx].next = sc->sc_txdmadesc_phys +
+ ((idx + 1) % GLC_MAX_TX_PACKETS)*sizeof(struct glc_dmadesc);
+ sc->sc_txdmadesc[idx].cmd_stat |= GELIC_CMDSTAT_NOIPSEC;
+
+ if (i+1 == nsegs) {
+ txs->txs_lastdesc = idx;
+ sc->sc_txdmadesc[idx].next = 0;
+ sc->sc_txdmadesc[idx].cmd_stat |= GELIC_CMDSTAT_LAST;
+ }
+
+ if ((*m_head)->m_pkthdr.csum_flags & CSUM_TCP)
+ sc->sc_txdmadesc[idx].cmd_stat |= GELIC_CMDSTAT_CSUM_TCP;
+ if ((*m_head)->m_pkthdr.csum_flags & CSUM_UDP)
+ sc->sc_txdmadesc[idx].cmd_stat |= GELIC_CMDSTAT_CSUM_UDP;
+ sc->sc_txdmadesc[idx].cmd_stat |= GELIC_DESCR_OWNED;
+
+ idx = (idx + 1) % GLC_MAX_TX_PACKETS;
+ }
+ sc->next_txdma_slot = idx;
+ sc->bsy_txdma_slots += nsegs;
+ if (txs->txs_firstdesc != 0)
+ idx = txs->txs_firstdesc - 1;
+ else
+ idx = GLC_MAX_TX_PACKETS - 1;
+
+ if (sc->first_used_txdma_slot < 0)
+ sc->first_used_txdma_slot = txs->txs_firstdesc;
+
+ bus_dmamap_sync(sc->sc_txdma_tag, txs->txs_dmamap,
+ BUS_DMASYNC_PREWRITE);
+ sc->sc_txdmadesc[idx].next = firstslotphys;
+
+ STAILQ_REMOVE_HEAD(&sc->sc_txfreeq, txs_q);
+ STAILQ_INSERT_TAIL(&sc->sc_txdirtyq, txs, txs_q);
+ txs->txs_mbuf = *m_head;
+ *pktdesc = firstslotphys;
+
+ return (0);
+}
+
+static void
+glc_rxintr(struct glc_softc *sc)
+{
+ int i, restart_rxdma, error;
+ struct mbuf *m;
+ if_t ifp = sc->sc_ifp;
+
+ bus_dmamap_sync(sc->sc_dmadesc_tag, sc->sc_rxdmadesc_map,
+ BUS_DMASYNC_POSTREAD);
+
+ restart_rxdma = 0;
+ while ((sc->sc_rxdmadesc[sc->sc_next_rxdma_slot].cmd_stat &
+ GELIC_DESCR_OWNED) == 0) {
+ i = sc->sc_next_rxdma_slot;
+ sc->sc_next_rxdma_slot++;
+ if (sc->sc_next_rxdma_slot >= GLC_MAX_RX_PACKETS)
+ sc->sc_next_rxdma_slot = 0;
+
+ if (sc->sc_rxdmadesc[i].cmd_stat & GELIC_CMDSTAT_CHAIN_END)
+ restart_rxdma = 1;
+
+ if (sc->sc_rxdmadesc[i].rxerror & GELIC_RXERRORS) {
+ if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
+ goto requeue;
+ }
+
+ m = sc->sc_rxsoft[i].rxs_mbuf;
+ if (sc->sc_rxdmadesc[i].data_stat & GELIC_RX_IPCSUM) {
+ m->m_pkthdr.csum_flags |=
+ CSUM_IP_CHECKED | CSUM_IP_VALID;
+ }
+ if (sc->sc_rxdmadesc[i].data_stat & GELIC_RX_TCPUDPCSUM) {
+ m->m_pkthdr.csum_flags |=
+ CSUM_DATA_VALID | CSUM_PSEUDO_HDR;
+ m->m_pkthdr.csum_data = 0xffff;
+ }
+
+ if (glc_add_rxbuf(sc, i)) {
+ if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
+ goto requeue;
+ }
+
+ if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1);
+ m->m_pkthdr.rcvif = ifp;
+ m->m_len = sc->sc_rxdmadesc[i].valid_size;
+ m->m_pkthdr.len = m->m_len;
+
+ /*
+ * Remove VLAN tag. Even on early firmwares that do not allow
+ * multiple VLANs, the VLAN tag is still in place here.
+ */
+ m_adj(m, 2);
+
+ mtx_unlock(&sc->sc_mtx);
+ if_input(ifp, m);
+ mtx_lock(&sc->sc_mtx);
+
+ requeue:
+ glc_add_rxbuf_dma(sc, i);
+ }
+
+ bus_dmamap_sync(sc->sc_dmadesc_tag, sc->sc_rxdmadesc_map,
+ BUS_DMASYNC_PREWRITE);
+
+ if (restart_rxdma) {
+ error = lv1_net_start_rx_dma(sc->sc_bus, sc->sc_dev,
+ sc->sc_rxsoft[sc->sc_next_rxdma_slot].rxs_desc, 0);
+ if (error != 0)
+ device_printf(sc->sc_self,
+ "lv1_net_start_rx_dma error: %d\n", error);
+ }
+}
+
+static void
+glc_txintr(struct glc_softc *sc)
+{
+ if_t ifp = sc->sc_ifp;
+ struct glc_txsoft *txs;
+ int progress = 0, kickstart = 0, error;
+
+ bus_dmamap_sync(sc->sc_dmadesc_tag, sc->sc_txdmadesc_map,
+ BUS_DMASYNC_POSTREAD);
+
+ while ((txs = STAILQ_FIRST(&sc->sc_txdirtyq)) != NULL) {
+ if (sc->sc_txdmadesc[txs->txs_lastdesc].cmd_stat
+ & GELIC_DESCR_OWNED)
+ break;
+
+ STAILQ_REMOVE_HEAD(&sc->sc_txdirtyq, txs_q);
+ bus_dmamap_unload(sc->sc_txdma_tag, txs->txs_dmamap);
+ sc->bsy_txdma_slots -= txs->txs_ndescs;
+
+ if (txs->txs_mbuf != NULL) {
+ m_freem(txs->txs_mbuf);
+ txs->txs_mbuf = NULL;
+ }
+
+ if ((sc->sc_txdmadesc[txs->txs_lastdesc].cmd_stat & 0xf0000000)
+ != 0) {
+ lv1_net_stop_tx_dma(sc->sc_bus, sc->sc_dev, 0);
+ kickstart = 1;
+ if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
+ }
+
+ if (sc->sc_txdmadesc[txs->txs_lastdesc].cmd_stat &
+ GELIC_CMDSTAT_CHAIN_END)
+ kickstart = 1;
+
+ STAILQ_INSERT_TAIL(&sc->sc_txfreeq, txs, txs_q);
+ if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1);
+ progress = 1;
+ }
+
+ if (txs != NULL)
+ sc->first_used_txdma_slot = txs->txs_firstdesc;
+ else
+ sc->first_used_txdma_slot = -1;
+
+ if (kickstart || txs != NULL) {
+ /* Speculatively (or necessarily) start the TX queue again */
+ error = lv1_net_start_tx_dma(sc->sc_bus, sc->sc_dev,
+ sc->sc_txdmadesc_phys +
+ ((txs == NULL) ? 0 : txs->txs_firstdesc)*
+ sizeof(struct glc_dmadesc), 0);
+ if (error != 0)
+ device_printf(sc->sc_self,
+ "lv1_net_start_tx_dma error: %d\n", error);
+ }
+
+ if (progress) {
+ /*
+ * We freed some descriptors, so reset IFF_DRV_OACTIVE
+ * and restart.
+ */
+ if_setdrvflagbits(ifp, 0, IFF_DRV_OACTIVE);
+ sc->sc_wdog_timer = STAILQ_EMPTY(&sc->sc_txdirtyq) ? 0 : 5;
+
+ if ((if_getdrvflags(ifp) & IFF_DRV_RUNNING) &&
+ !if_sendq_empty(ifp))
+ glc_start_locked(ifp);
+ }
+}
+
+static int
+glc_intr_filter(void *xsc)
+{
+ struct glc_softc *sc = xsc;
+
+ powerpc_sync();
+ atomic_set_64(&sc->sc_interrupt_status, *sc->sc_hwirq_status);
+ return (FILTER_SCHEDULE_THREAD);
+}
+
+static void
+glc_intr(void *xsc)
+{
+ struct glc_softc *sc = xsc;
+ uint64_t status, linkstat, junk;
+
+ mtx_lock(&sc->sc_mtx);
+
+ status = atomic_readandclear_64(&sc->sc_interrupt_status);
+
+ if (status == 0) {
+ mtx_unlock(&sc->sc_mtx);
+ return;
+ }
+
+ if (status & (GELIC_INT_RXDONE | GELIC_INT_RXFRAME))
+ glc_rxintr(sc);
+
+ if (status & (GELIC_INT_TXDONE | GELIC_INT_TX_CHAIN_END))
+ glc_txintr(sc);
+
+ if (status & GELIC_INT_PHY) {
+ lv1_net_control(sc->sc_bus, sc->sc_dev, GELIC_GET_LINK_STATUS,
+ GELIC_VLAN_TX_ETHERNET, 0, 0, &linkstat, &junk);
+
+ linkstat = (linkstat & GELIC_LINK_UP) ?
+ LINK_STATE_UP : LINK_STATE_DOWN;
+ if_link_state_change(sc->sc_ifp, linkstat);
+ }
+
+ mtx_unlock(&sc->sc_mtx);
+}
+
+static void
+glc_media_status(if_t ifp, struct ifmediareq *ifmr)
+{
+ struct glc_softc *sc = if_getsoftc(ifp);
+ uint64_t status, junk;
+
+ ifmr->ifm_status = IFM_AVALID;
+ ifmr->ifm_active = IFM_ETHER;
+
+ lv1_net_control(sc->sc_bus, sc->sc_dev, GELIC_GET_LINK_STATUS,
+ GELIC_VLAN_TX_ETHERNET, 0, 0, &status, &junk);
+
+ if (status & GELIC_LINK_UP)
+ ifmr->ifm_status |= IFM_ACTIVE;
+
+ if (status & GELIC_SPEED_10)
+ ifmr->ifm_active |= IFM_10_T;
+ else if (status & GELIC_SPEED_100)
+ ifmr->ifm_active |= IFM_100_TX;
+ else if (status & GELIC_SPEED_1000)
+ ifmr->ifm_active |= IFM_1000_T;
+
+ if (status & GELIC_FULL_DUPLEX)
+ ifmr->ifm_active |= IFM_FDX;
+ else
+ ifmr->ifm_active |= IFM_HDX;
+}
+
+static int
+glc_media_change(if_t ifp)
+{
+ struct glc_softc *sc = if_getsoftc(ifp);
+ uint64_t mode, junk;
+ int result;
+
+ if (IFM_TYPE(sc->sc_media.ifm_media) != IFM_ETHER)
+ return (EINVAL);
+
+ switch (IFM_SUBTYPE(sc->sc_media.ifm_media)) {
+ case IFM_AUTO:
+ mode = GELIC_AUTO_NEG;
+ break;
+ case IFM_10_T:
+ mode = GELIC_SPEED_10;
+ break;
+ case IFM_100_TX:
+ mode = GELIC_SPEED_100;
+ break;
+ case IFM_1000_T:
+ mode = GELIC_SPEED_1000 | GELIC_FULL_DUPLEX;
+ break;
+ default:
+ return (EINVAL);
+ }
+
+ if (IFM_OPTIONS(sc->sc_media.ifm_media) & IFM_FDX)
+ mode |= GELIC_FULL_DUPLEX;
+
+ result = lv1_net_control(sc->sc_bus, sc->sc_dev, GELIC_SET_LINK_MODE,
+ GELIC_VLAN_TX_ETHERNET, mode, 0, &junk, &junk);
+
+ return (result ? EIO : 0);
+}
diff --git a/sys/powerpc/ps3/if_glcreg.h b/sys/powerpc/ps3/if_glcreg.h
new file mode 100644
index 000000000000..25d97b00a0cc
--- /dev/null
+++ b/sys/powerpc/ps3/if_glcreg.h
@@ -0,0 +1,159 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (C) 2010 Nathan Whitehorn
+ * 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 TOOLS GMBH 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.
+ */
+
+#ifndef _POWERPC_PS3_IF_GLCREG_H
+#define _POWERPC_PS3_IF_GLCREG_H
+
+#define GLC_MAX_TX_PACKETS 128
+#define GLC_MAX_RX_PACKETS 128
+
+struct glc_dmadesc;
+
+/*
+ * software state for transmit job mbufs (may be elements of mbuf chains)
+ */
+
+struct glc_txsoft {
+ struct mbuf *txs_mbuf; /* head of our mbuf chain */
+ bus_dmamap_t txs_dmamap; /* our DMA map */
+ int txs_firstdesc; /* first descriptor in packet */
+ int txs_lastdesc; /* last descriptor in packet */
+
+ int txs_ndescs; /* number of descriptors */
+ STAILQ_ENTRY(glc_txsoft) txs_q;
+};
+
+STAILQ_HEAD(glc_txsq, glc_txsoft);
+
+/*
+ * software state for receive jobs
+ */
+struct glc_rxsoft {
+ struct mbuf *rxs_mbuf; /* head of our mbuf chain */
+ bus_dmamap_t rxs_dmamap; /* our DMA map */
+
+ int rxs_desc_slot; /* DMA descriptor for this packet */
+ bus_addr_t rxs_desc;
+
+ bus_dma_segment_t segment;
+};
+
+struct glc_softc {
+ if_t sc_ifp;
+ device_t sc_self;
+ struct mtx sc_mtx;
+ u_char sc_enaddr[ETHER_ADDR_LEN];
+ int sc_tx_vlan, sc_rx_vlan;
+ int sc_ifpflags;
+
+ uint64_t sc_dma_base[5];
+ bus_dma_tag_t sc_dmadesc_tag;
+
+ int sc_irqid;
+ struct resource *sc_irq;
+ void *sc_irqctx;
+ uint64_t *sc_hwirq_status;
+ volatile uint64_t sc_interrupt_status;
+
+ struct ifmedia sc_media;
+
+ /* Transmission */
+
+ bus_dma_tag_t sc_txdma_tag;
+ struct glc_txsoft sc_txsoft[GLC_MAX_TX_PACKETS];
+ struct glc_dmadesc *sc_txdmadesc;
+ int next_txdma_slot, first_used_txdma_slot, bsy_txdma_slots;
+ bus_dmamap_t sc_txdmadesc_map;
+ bus_addr_t sc_txdmadesc_phys;
+
+ struct glc_txsq sc_txfreeq;
+ struct glc_txsq sc_txdirtyq;
+
+ /* Reception */
+
+ bus_dma_tag_t sc_rxdma_tag;
+ struct glc_rxsoft sc_rxsoft[GLC_MAX_RX_PACKETS];
+ struct glc_dmadesc *sc_rxdmadesc;
+ int sc_next_rxdma_slot;
+ bus_dmamap_t sc_rxdmadesc_map;
+ bus_addr_t sc_rxdmadesc_phys;
+
+ int sc_bus, sc_dev;
+ int sc_wdog_timer;
+ struct callout sc_tick_ch;
+};
+
+#define GELIC_GET_MAC_ADDRESS 0x0001
+#define GELIC_GET_LINK_STATUS 0x0002
+#define GELIC_SET_LINK_MODE 0x0003
+#define GELIC_LINK_UP 0x0001
+#define GELIC_FULL_DUPLEX 0x0002
+#define GELIC_AUTO_NEG 0x0004
+#define GELIC_SPEED_10 0x0010
+#define GELIC_SPEED_100 0x0020
+#define GELIC_SPEED_1000 0x0040
+#define GELIC_GET_VLAN_ID 0x0004
+#define GELIC_VLAN_TX_ETHERNET 0x0002
+#define GELIC_VLAN_RX_ETHERNET 0x0012
+#define GELIC_VLAN_TX_WIRELESS 0x0003
+#define GELIC_VLAN_RX_WIRELESS 0x0013
+
+/* Command status code */
+#define GELIC_DESCR_OWNED 0xa0000000
+#define GELIC_CMDSTAT_DMA_DONE 0x00000000
+#define GELIC_CMDSTAT_CHAIN_END 0x00000002
+#define GELIC_CMDSTAT_CSUM_TCP 0x00020000
+#define GELIC_CMDSTAT_CSUM_UDP 0x00030000
+#define GELIC_CMDSTAT_NOIPSEC 0x00080000
+#define GELIC_CMDSTAT_LAST 0x00040000
+#define GELIC_RXERRORS 0x7def8000
+
+/* RX Data Status codes */
+#define GELIC_RX_IPCSUM 0x20000000
+#define GELIC_RX_TCPUDPCSUM 0x10000000
+
+/* Interrupt options */
+#define GELIC_INT_RXDONE 0x0000000000004000UL
+#define GELIC_INT_RXFRAME 0x1000000000000000UL
+#define GELIC_INT_TXDONE 0x0080000000000000UL
+#define GELIC_INT_TX_CHAIN_END 0x0100000000000000UL
+#define GELIC_INT_PHY 0x0000000020000000UL
+
+/* Hardware DMA descriptor. Must be 32-byte aligned */
+
+struct glc_dmadesc {
+ uint32_t paddr; /* Must be 128 byte aligned for receive */
+ uint32_t len;
+ uint32_t next;
+ uint32_t cmd_stat;
+ uint32_t result_size;
+ uint32_t valid_size;
+ uint32_t data_stat;
+ uint32_t rxerror;
+};
+
+#endif /* _POWERPC_PS3_IF_GLCREG_H */
diff --git a/sys/powerpc/ps3/mmu_ps3.c b/sys/powerpc/ps3/mmu_ps3.c
new file mode 100644
index 000000000000..6aeadfab5f7a
--- /dev/null
+++ b/sys/powerpc/ps3/mmu_ps3.c
@@ -0,0 +1,288 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (C) 2010 Nathan Whitehorn
+ * 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 TOOLS GMBH 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/param.h>
+#include <sys/kernel.h>
+#include <sys/ktr.h>
+#include <sys/lock.h>
+#include <sys/msgbuf.h>
+#include <sys/mutex.h>
+#include <sys/proc.h>
+#include <sys/sysctl.h>
+#include <sys/systm.h>
+#include <sys/vmmeter.h>
+
+#include <vm/vm.h>
+#include <vm/vm_param.h>
+#include <vm/vm_kern.h>
+#include <vm/vm_page.h>
+#include <vm/vm_map.h>
+#include <vm/vm_object.h>
+#include <vm/vm_extern.h>
+#include <vm/vm_pageout.h>
+#include <vm/uma.h>
+
+#include <powerpc/aim/mmu_oea64.h>
+
+#include "ps3-hvcall.h"
+
+#define VSID_HASH_MASK 0x0000007fffffffffUL
+#define PTESYNC() __asm __volatile("ptesync")
+
+extern int ps3fb_remap(void);
+
+static uint64_t mps3_vas_id;
+
+/*
+ * Kernel MMU interface
+ */
+
+static void mps3_install(void);
+static void mps3_bootstrap(vm_offset_t kernelstart,
+ vm_offset_t kernelend);
+static void mps3_cpu_bootstrap(int ap);
+static int64_t mps3_pte_synch(struct pvo_entry *);
+static int64_t mps3_pte_clear(struct pvo_entry *, uint64_t ptebit);
+static int64_t mps3_pte_unset(struct pvo_entry *);
+static int64_t mps3_pte_insert(struct pvo_entry *);
+
+static struct pmap_funcs mps3_methods = {
+ .install = mps3_install,
+ .bootstrap = mps3_bootstrap,
+ .cpu_bootstrap = mps3_cpu_bootstrap,
+};
+
+static struct moea64_funcs mps3_funcs = {
+ .pte_synch = mps3_pte_synch,
+ .pte_clear = mps3_pte_clear,
+ .pte_unset = mps3_pte_unset,
+ .pte_insert = mps3_pte_insert,
+};
+
+MMU_DEF_INHERIT(ps3_mmu, "mmu_ps3", mps3_methods, oea64_mmu);
+
+static struct mtx mps3_table_lock;
+
+static void
+mps3_install(void)
+{
+ moea64_ops = &mps3_funcs;
+ moea64_install();
+}
+
+static void
+mps3_bootstrap(vm_offset_t kernelstart, vm_offset_t kernelend)
+{
+ uint64_t final_pteg_count;
+
+ mtx_init(&mps3_table_lock, "page table", NULL, MTX_DEF);
+
+ moea64_early_bootstrap(kernelstart, kernelend);
+
+ /* In case we had a page table already */
+ lv1_destruct_virtual_address_space(0);
+
+ /* Allocate new hardware page table */
+ lv1_construct_virtual_address_space(
+ 20 /* log_2(moea64_pteg_count) */, 2 /* n page sizes */,
+ (24UL << 56) | (16UL << 48) /* page sizes 16 MB + 64 KB */,
+ &mps3_vas_id, &final_pteg_count
+ );
+
+ lv1_select_virtual_address_space(mps3_vas_id);
+
+ moea64_pteg_count = final_pteg_count / sizeof(struct lpteg);
+
+ moea64_mid_bootstrap(kernelstart, kernelend);
+ moea64_late_bootstrap(kernelstart, kernelend);
+}
+
+static void
+mps3_cpu_bootstrap(int ap)
+{
+ struct slb *slb = PCPU_GET(aim.slb);
+ register_t seg0;
+ int i;
+
+ mtmsr(mfmsr() & ~PSL_DR & ~PSL_IR);
+
+ /*
+ * Select the page table we configured above and set up the FB mapping
+ * so we can have a console.
+ */
+ lv1_select_virtual_address_space(mps3_vas_id);
+
+ if (!ap)
+ ps3fb_remap();
+
+ /*
+ * Install kernel SLB entries
+ */
+
+ __asm __volatile ("slbia");
+ __asm __volatile ("slbmfee %0,%1; slbie %0;" : "=r"(seg0) : "r"(0));
+ for (i = 0; i < 64; i++) {
+ if (!(slb[i].slbe & SLBE_VALID))
+ continue;
+
+ __asm __volatile ("slbmte %0, %1" ::
+ "r"(slb[i].slbv), "r"(slb[i].slbe));
+ }
+}
+
+static int64_t
+mps3_pte_synch_locked(struct pvo_entry *pvo)
+{
+ uint64_t halfbucket[4], rcbits;
+
+ PTESYNC();
+ lv1_read_htab_entries(mps3_vas_id, pvo->pvo_pte.slot & ~0x3UL,
+ &halfbucket[0], &halfbucket[1], &halfbucket[2], &halfbucket[3],
+ &rcbits);
+
+ /* Check if present in page table */
+ if ((halfbucket[pvo->pvo_pte.slot & 0x3] & LPTE_AVPN_MASK) !=
+ ((pvo->pvo_vpn >> (ADDR_API_SHFT64 - ADDR_PIDX_SHFT)) &
+ LPTE_AVPN_MASK))
+ return (-1);
+ if (!(halfbucket[pvo->pvo_pte.slot & 0x3] & LPTE_VALID))
+ return (-1);
+
+ /*
+ * rcbits contains the low 12 bits of each PTE's 2nd part,
+ * spaced at 16-bit intervals
+ */
+
+ return ((rcbits >> ((3 - (pvo->pvo_pte.slot & 0x3))*16)) &
+ (LPTE_CHG | LPTE_REF));
+}
+
+static int64_t
+mps3_pte_synch(struct pvo_entry *pvo)
+{
+ int64_t retval;
+
+ mtx_lock(&mps3_table_lock);
+ retval = mps3_pte_synch_locked(pvo);
+ mtx_unlock(&mps3_table_lock);
+
+ return (retval);
+}
+
+static int64_t
+mps3_pte_clear(struct pvo_entry *pvo, uint64_t ptebit)
+{
+ int64_t refchg;
+ struct lpte pte;
+
+ mtx_lock(&mps3_table_lock);
+
+ refchg = mps3_pte_synch_locked(pvo);
+ if (refchg < 0) {
+ mtx_unlock(&mps3_table_lock);
+ return (refchg);
+ }
+
+ moea64_pte_from_pvo(pvo, &pte);
+
+ pte.pte_lo |= refchg;
+ pte.pte_lo &= ~ptebit;
+ /* XXX: race on RC bits between write and sync. Anything to do? */
+ lv1_write_htab_entry(mps3_vas_id, pvo->pvo_pte.slot, pte.pte_hi,
+ pte.pte_lo);
+ mtx_unlock(&mps3_table_lock);
+
+ return (refchg);
+}
+
+static int64_t
+mps3_pte_unset(struct pvo_entry *pvo)
+{
+ int64_t refchg;
+
+ mtx_lock(&mps3_table_lock);
+ refchg = mps3_pte_synch_locked(pvo);
+ if (refchg < 0) {
+ STAT_MOEA64(moea64_pte_overflow--);
+ mtx_unlock(&mps3_table_lock);
+ return (-1);
+ }
+ /* XXX: race on RC bits between unset and sync. Anything to do? */
+ lv1_write_htab_entry(mps3_vas_id, pvo->pvo_pte.slot, 0, 0);
+ mtx_unlock(&mps3_table_lock);
+ STAT_MOEA64(moea64_pte_valid--);
+
+ return (refchg & (LPTE_REF | LPTE_CHG));
+}
+
+static int64_t
+mps3_pte_insert(struct pvo_entry *pvo)
+{
+ int result;
+ struct lpte pte, evicted;
+ uint64_t index;
+
+ if (pvo->pvo_vaddr & PVO_HID) {
+ /* Hypercall needs primary PTEG */
+ pvo->pvo_vaddr &= ~PVO_HID;
+ pvo->pvo_pte.slot ^= (moea64_pteg_mask << 3);
+ }
+
+ pvo->pvo_pte.slot &= ~7UL;
+ moea64_pte_from_pvo(pvo, &pte);
+ evicted.pte_hi = 0;
+ PTESYNC();
+ mtx_lock(&mps3_table_lock);
+ result = lv1_insert_htab_entry(mps3_vas_id, pvo->pvo_pte.slot,
+ pte.pte_hi, pte.pte_lo, LPTE_LOCKED | LPTE_WIRED, 0,
+ &index, &evicted.pte_hi, &evicted.pte_lo);
+ mtx_unlock(&mps3_table_lock);
+
+ if (result != 0) {
+ /* No freeable slots in either PTEG? We're hosed. */
+ panic("mps3_pte_insert: overflow (%d)", result);
+ return (-1);
+ }
+
+ /*
+ * See where we ended up.
+ */
+ if ((index & ~7UL) != pvo->pvo_pte.slot)
+ pvo->pvo_vaddr |= PVO_HID;
+ pvo->pvo_pte.slot = index;
+
+ STAT_MOEA64(moea64_pte_valid++);
+
+ if (evicted.pte_hi) {
+ KASSERT((evicted.pte_hi & (LPTE_WIRED | LPTE_LOCKED)) == 0,
+ ("Evicted a wired PTE"));
+ STAT_MOEA64(moea64_pte_valid--);
+ STAT_MOEA64(moea64_pte_overflow++);
+ }
+
+ return (0);
+}
diff --git a/sys/powerpc/ps3/ohci_ps3.c b/sys/powerpc/ps3/ohci_ps3.c
new file mode 100644
index 000000000000..7920568ffb72
--- /dev/null
+++ b/sys/powerpc/ps3/ohci_ps3.c
@@ -0,0 +1,167 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (C) 2010 Nathan Whitehorn
+ * 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 TOOLS GMBH 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>
+#include <sys/stdint.h>
+#include <sys/stddef.h>
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <sys/linker_set.h>
+#include <sys/module.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/condvar.h>
+#include <sys/sysctl.h>
+#include <sys/sx.h>
+#include <sys/unistd.h>
+#include <sys/callout.h>
+#include <sys/malloc.h>
+#include <sys/priv.h>
+
+#include <sys/rman.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_util.h>
+
+#include <dev/usb/usb_controller.h>
+#include <dev/usb/usb_bus.h>
+#include <dev/usb/controller/ohci.h>
+#include <dev/usb/controller/ohcireg.h>
+
+#include "ps3bus.h"
+
+static int
+ohci_ps3_probe(device_t dev)
+{
+ if (ps3bus_get_bustype(dev) != PS3_BUSTYPE_SYSBUS ||
+ ps3bus_get_devtype(dev) != PS3_DEVTYPE_USB)
+ return (ENXIO);
+
+ device_set_desc(dev, "Playstation 3 USB 2.0 controller");
+ return (BUS_PROBE_SPECIFIC);
+}
+
+static int
+ohci_ps3_attach(device_t dev)
+{
+ ohci_softc_t *sc = device_get_softc(dev);
+ int rid, err;
+
+ sc->sc_bus.parent = dev;
+ sc->sc_bus.devices = sc->sc_devices;
+ sc->sc_bus.devices_max = OHCI_MAX_DEVICES;
+ sc->sc_bus.dma_bits = 32;
+
+ /* get all DMA memory */
+ if (usb_bus_mem_alloc_all(&sc->sc_bus,
+ USB_GET_DMA_TAG(dev), &ohci_iterate_hw_softc))
+ return (ENOMEM);
+
+ rid = 0;
+ sc->sc_io_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
+ &rid, RF_ACTIVE);
+
+ if (!sc->sc_io_res) {
+ device_printf(dev, "Could not map memory\n");
+ goto error;
+ }
+
+ sc->sc_io_tag = rman_get_bustag(sc->sc_io_res);
+ sc->sc_io_hdl = rman_get_bushandle(sc->sc_io_res);
+ sc->sc_io_size = rman_get_size(sc->sc_io_res);
+
+ rid = 0;
+ sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
+ RF_SHAREABLE | RF_ACTIVE);
+
+ if (sc->sc_irq_res == NULL) {
+ device_printf(dev, "Could not allocate irq\n");
+ return (ENXIO);
+ }
+
+ sc->sc_bus.bdev = device_add_child(dev, "usbus", DEVICE_UNIT_ANY);
+ if (!sc->sc_bus.bdev) {
+ device_printf(dev, "Could not add USB device\n");
+ return (ENXIO);
+ }
+
+ device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus);
+
+ sprintf(sc->sc_vendor, "Sony");
+
+ err = bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE,
+ NULL, (driver_intr_t *)ohci_interrupt, sc, &sc->sc_intr_hdl);
+ if (err) {
+ device_printf(dev, "Could not setup error irq, %d\n", err);
+ goto error;
+ }
+
+ //sc->sc_flags |= EHCI_SCFLG_BIGEMMIO;
+ bus_space_write_4(sc->sc_io_tag, sc->sc_io_hdl,
+ OHCI_CONTROL, 0);
+ err = ohci_init(sc);
+ if (err) {
+ device_printf(dev, "USB init failed err=%d\n", err);
+ goto error;
+ }
+
+ err = device_probe_and_attach(sc->sc_bus.bdev);
+ if (err == 0)
+ return (0);
+
+error:
+ return (ENXIO);
+}
+
+static device_method_t ohci_ps3_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, ohci_ps3_probe),
+ DEVMETHOD(device_attach, ohci_ps3_attach),
+ DEVMETHOD(device_resume, bus_generic_resume),
+ DEVMETHOD(device_suspend, bus_generic_suspend),
+ DEVMETHOD(device_shutdown, bus_generic_shutdown),
+
+ DEVMETHOD_END
+};
+
+static driver_t ohci_ps3_driver = {
+ .name = "ohci",
+ .methods = ohci_ps3_methods,
+ .size = sizeof(ohci_softc_t),
+};
+
+DRIVER_MODULE(ohci_ps3, ps3bus, ohci_ps3_driver, 0, 0);
+MODULE_DEPEND(ohci_ps3, usb, 1, 1, 1);
diff --git a/sys/powerpc/ps3/platform_ps3.c b/sys/powerpc/ps3/platform_ps3.c
new file mode 100644
index 000000000000..556f723a0134
--- /dev/null
+++ b/sys/powerpc/ps3/platform_ps3.c
@@ -0,0 +1,290 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2010 Nathan Whitehorn
+ * 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/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <sys/pcpu.h>
+#include <sys/proc.h>
+#include <sys/reboot.h>
+#include <sys/smp.h>
+
+#include <vm/vm.h>
+#include <vm/pmap.h>
+
+#include <machine/bus.h>
+#include <machine/cpu.h>
+#include <machine/hid.h>
+#include <machine/platform.h>
+#include <machine/platformvar.h>
+#include <machine/smp.h>
+#include <machine/spr.h>
+#include <machine/vmparam.h>
+
+#include <dev/ofw/openfirm.h>
+
+#include "platform_if.h"
+#include "ps3-hvcall.h"
+
+#ifdef SMP
+extern void *ap_pcpu;
+#endif
+
+static int ps3_probe(platform_t);
+static int ps3_attach(platform_t);
+static void ps3_mem_regions(platform_t, struct mem_region *phys, int *physsz,
+ struct mem_region *avail, int *availsz);
+static vm_offset_t ps3_real_maxaddr(platform_t);
+static u_long ps3_timebase_freq(platform_t, struct cpuref *cpuref);
+#ifdef SMP
+static int ps3_smp_first_cpu(platform_t, struct cpuref *cpuref);
+static int ps3_smp_next_cpu(platform_t, struct cpuref *cpuref);
+static int ps3_smp_get_bsp(platform_t, struct cpuref *cpuref);
+static int ps3_smp_start_cpu(platform_t, struct pcpu *cpu);
+static void ps3_smp_probe_threads(platform_t);
+static struct cpu_group *ps3_smp_topo(platform_t);
+#endif
+static void ps3_reset(platform_t);
+static void ps3_cpu_idle(sbintime_t);
+
+static platform_method_t ps3_methods[] = {
+ PLATFORMMETHOD(platform_probe, ps3_probe),
+ PLATFORMMETHOD(platform_attach, ps3_attach),
+ PLATFORMMETHOD(platform_mem_regions, ps3_mem_regions),
+ PLATFORMMETHOD(platform_real_maxaddr, ps3_real_maxaddr),
+ PLATFORMMETHOD(platform_timebase_freq, ps3_timebase_freq),
+
+#ifdef SMP
+ PLATFORMMETHOD(platform_smp_first_cpu, ps3_smp_first_cpu),
+ PLATFORMMETHOD(platform_smp_next_cpu, ps3_smp_next_cpu),
+ PLATFORMMETHOD(platform_smp_get_bsp, ps3_smp_get_bsp),
+ PLATFORMMETHOD(platform_smp_start_cpu, ps3_smp_start_cpu),
+ PLATFORMMETHOD(platform_smp_probe_threads, ps3_smp_probe_threads),
+ PLATFORMMETHOD(platform_smp_topo, ps3_smp_topo),
+#endif
+
+ PLATFORMMETHOD(platform_reset, ps3_reset),
+
+ PLATFORMMETHOD_END
+};
+
+static platform_def_t ps3_platform = {
+ "ps3",
+ ps3_methods,
+ 0
+};
+
+PLATFORM_DEF(ps3_platform);
+
+static int ps3_boot_pir = 0;
+
+static int
+ps3_probe(platform_t plat)
+{
+ phandle_t root;
+ char compatible[64];
+
+ root = OF_finddevice("/");
+ if (OF_getprop(root, "compatible", compatible, sizeof(compatible)) <= 0)
+ return (BUS_PROBE_NOWILDCARD);
+
+ if (strncmp(compatible, "sony,ps3", sizeof(compatible)) != 0)
+ return (BUS_PROBE_NOWILDCARD);
+
+ return (BUS_PROBE_SPECIFIC);
+}
+
+static int
+ps3_attach(platform_t plat)
+{
+
+ pmap_mmu_install("mmu_ps3", BUS_PROBE_SPECIFIC);
+ cpu_idle_hook = ps3_cpu_idle;
+
+ /* Record our PIR at boot for later */
+ ps3_boot_pir = mfspr(SPR_PIR);
+
+ return (0);
+}
+
+void
+ps3_mem_regions(platform_t plat, struct mem_region *phys, int *physsz,
+ struct mem_region *avail_regions, int *availsz)
+{
+ uint64_t lpar_id, junk;
+ int i;
+
+ /* Prefer device tree information if available */
+ if (OF_finddevice("/") != -1) {
+ ofw_mem_regions(phys, physsz, avail_regions, availsz);
+ } else {
+ /* Real mode memory region is first segment */
+ phys[0].mr_start = 0;
+ phys[0].mr_size = ps3_real_maxaddr(plat);
+ *physsz = *availsz = 1;
+ avail_regions[0] = phys[0];
+ }
+
+ /* Now get extended memory region */
+ lv1_get_logical_partition_id(&lpar_id);
+ lv1_get_repository_node_value(lpar_id,
+ lv1_repository_string("bi") >> 32,
+ lv1_repository_string("rgntotal"), 0, 0,
+ &phys[*physsz].mr_size, &junk);
+ for (i = 0; i < *physsz; i++)
+ phys[*physsz].mr_size -= phys[i].mr_size;
+
+ /* Convert to maximum amount we can allocate in 16 MB pages */
+ phys[*physsz].mr_size -= phys[*physsz].mr_size % (16*1024*1024);
+
+ /* Allocate extended memory region */
+ lv1_allocate_memory(phys[*physsz].mr_size, 24 /* 16 MB pages */,
+ 0, 0x04 /* any address */, &phys[*physsz].mr_start, &junk);
+ avail_regions[*availsz] = phys[*physsz];
+ (*physsz)++;
+ (*availsz)++;
+}
+
+static u_long
+ps3_timebase_freq(platform_t plat, struct cpuref *cpuref)
+{
+ uint64_t ticks, node_id, junk;
+
+ lv1_get_repository_node_value(PS3_LPAR_ID_PME,
+ lv1_repository_string("be") >> 32, 0, 0, 0, &node_id, &junk);
+ lv1_get_repository_node_value(PS3_LPAR_ID_PME,
+ lv1_repository_string("be") >> 32, node_id,
+ lv1_repository_string("clock"), 0, &ticks, &junk);
+
+ return (ticks);
+}
+
+#ifdef SMP
+static int
+ps3_smp_first_cpu(platform_t plat, struct cpuref *cpuref)
+{
+
+ cpuref->cr_cpuid = 0;
+ cpuref->cr_hwref = ps3_boot_pir;
+
+ return (0);
+}
+
+static int
+ps3_smp_next_cpu(platform_t plat, struct cpuref *cpuref)
+{
+
+ if (cpuref->cr_cpuid >= 1)
+ return (ENOENT);
+
+ cpuref->cr_cpuid++;
+ cpuref->cr_hwref = !ps3_boot_pir;
+
+ return (0);
+}
+
+static int
+ps3_smp_get_bsp(platform_t plat, struct cpuref *cpuref)
+{
+
+ cpuref->cr_cpuid = 0;
+ cpuref->cr_hwref = ps3_boot_pir;
+
+ return (0);
+}
+
+static int
+ps3_smp_start_cpu(platform_t plat, struct pcpu *pc)
+{
+ /* kernel is spinning on 0x40 == -1 right now */
+ volatile uint32_t *secondary_spin_sem =
+ (uint32_t *)PHYS_TO_DMAP((uintptr_t)0x40);
+ int remote_pir = pc->pc_hwref;
+ int timeout;
+
+ ap_pcpu = pc;
+
+ /* Try both PIR values, looping a few times: the HV likes moving us */
+ timeout = 10000;
+ while (!pc->pc_awake && timeout--) {
+ *secondary_spin_sem = remote_pir;
+ powerpc_sync();
+ DELAY(100);
+ remote_pir = !remote_pir;
+ }
+
+ return ((pc->pc_awake) ? 0 : EBUSY);
+}
+
+static void
+ps3_smp_probe_threads(platform_t plat)
+{
+ mp_ncores = 1;
+ smp_threads_per_core = 2;
+}
+
+static struct cpu_group *
+ps3_smp_topo(platform_t plat)
+{
+ return (smp_topo_1level(CG_SHARE_L1, 2, CG_FLAG_SMT));
+}
+#endif
+
+static void
+ps3_reset(platform_t plat)
+{
+ lv1_panic(1);
+}
+
+static vm_offset_t
+ps3_real_maxaddr(platform_t plat)
+{
+ uint64_t lpar_id, junk, ppe_id;
+ static uint64_t rm_maxaddr = 0;
+
+ if (rm_maxaddr == 0) {
+ /* Get real mode memory region */
+ lv1_get_logical_partition_id(&lpar_id);
+ lv1_get_logical_ppe_id(&ppe_id);
+
+ lv1_get_repository_node_value(lpar_id,
+ lv1_repository_string("bi") >> 32,
+ lv1_repository_string("pu"),
+ ppe_id, lv1_repository_string("rm_size"),
+ &rm_maxaddr, &junk);
+ }
+
+ return (rm_maxaddr);
+}
+
+static void
+ps3_cpu_idle(sbintime_t sbt)
+{
+ lv1_pause(0);
+}
diff --git a/sys/powerpc/ps3/ps3-hv-asm.awk b/sys/powerpc/ps3/ps3-hv-asm.awk
new file mode 100644
index 000000000000..d3364a0c2bd7
--- /dev/null
+++ b/sys/powerpc/ps3/ps3-hv-asm.awk
@@ -0,0 +1,56 @@
+# This script generates the PS3 hypervisor call stubs from an HV
+# interface definition file. The PS3 HV calling convention is very
+# similar to the PAPR one, except that the function token is passed in
+# r11 instead of r3.
+#
+# Invoke like so: awk -f ps3-hv-asm.awk < ps3-hvcall.master > ps3-hvcall.S
+#
+
+BEGIN {
+ printf("#include <machine/asm.h>\n\n");
+ printf("#define hc .long 0x44000022\n\n");
+}
+
+/HVCALL.*/ {
+ # Parameter save area
+ # 48 in elfv1, 32 in elfv2
+ stack_offset = 32;
+
+ code = $2;
+ ins = split($4, a, ",")
+ outs = split($5, a, ",")
+
+ printf("ASENTRY(%s)\n",$3);
+ printf("\tmflr %%r0\n");
+ printf("\tstd %%r0,16(%%r1)\n");
+ printf("\tstdu %%r1,-%d(%%r1)\n", stack_offset+8*outs);
+
+ if ($4 == "UNUSED")
+ ins = 0
+
+ # Save output reg addresses to the stack
+ for (i = 0; i < outs; i++) {
+ if (ins+i >= 8) {
+ printf("\tld %%r11,%d(%%r1)\n", stack_offset+8*outs + stack_offset + 8*(i+ins));
+ printf("\tstd %%r11,%d(%%r1)\n", stack_offset+8*i);
+ } else {
+ printf("\tstd %%r%d,%d(%%r1)\n", 3+ins+i, stack_offset+8*i);
+ }
+ }
+
+ printf("\tli %%r11,%d\n", code);
+ printf("\thc\n");
+ printf("\textsw %%r3,%%r3\n");
+
+ for (i = 0; i < outs; i++) {
+ printf("\tld %%r11,%d(%%r1)\n", stack_offset+8*i);
+ printf("\tstd %%r%d,0(%%r11)\n", 4+i);
+ }
+
+ printf("\tld %%r1,0(%%r1)\n");
+ printf("\tld %%r0,16(%%r1)\n");
+ printf("\tmtlr %%r0\n");
+ printf("\tblr\n");
+
+ printf("ASEND(%s)\n\n",$3);
+}
diff --git a/sys/powerpc/ps3/ps3-hv-header.awk b/sys/powerpc/ps3/ps3-hv-header.awk
new file mode 100644
index 000000000000..d7de9ed665ac
--- /dev/null
+++ b/sys/powerpc/ps3/ps3-hv-header.awk
@@ -0,0 +1,40 @@
+# This script generates the PS3 hypervisor call header from a hypervisor
+# interface definition file. All lines that do not begin with HVCALL
+# or a bare # for comments are copied to the output header so that
+# enums, constant, C comments and the like can be passed through into the
+# header.
+#
+# Invoke like so: awk -f ps3-hv-header.awk < ps3-hvcall.master > ps3-hv.h
+#
+
+!/HVCALL.*/ && (!/#.*/ || /#define.*/ || /#include.*/) {
+ print($0);
+}
+
+/HVCALL.*/ {
+ split($5, outs, ",")
+ if ($4 == "UNUSED")
+ split("", ins, ",")
+ else
+ split($4, ins, ",")
+
+ printf("int %s(",$3);
+ for (i = 1; i <= length(ins); i++) {
+ printf("uint64_t %s", ins[i]);
+ if (i < length(ins)) printf(", ");
+ }
+
+ if (length(outs) > 0 && length(ins) > 0)
+ printf(", ");
+
+ for (i = 1; i <= length(outs); i++) {
+ printf("uint64_t *%s", outs[i]);
+ if (i < length(outs)) printf(", ");
+ }
+
+ if (length(outs) == 0 && length(ins) == 0)
+ printf("void");
+
+ printf(");\n");
+}
+
diff --git a/sys/powerpc/ps3/ps3-hvcall.S b/sys/powerpc/ps3/ps3-hvcall.S
new file mode 100644
index 000000000000..59dfe639ee61
--- /dev/null
+++ b/sys/powerpc/ps3/ps3-hvcall.S
@@ -0,0 +1,1366 @@
+#include <machine/asm.h>
+
+#define hc .long 0x44000022
+
+ASENTRY(lv1_allocate_memory)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-48(%r1)
+ std %r7,32(%r1)
+ std %r8,40(%r1)
+ li %r11,0
+ hc
+ extsw %r3,%r3
+ ld %r11,32(%r1)
+ std %r4,0(%r11)
+ ld %r11,40(%r1)
+ std %r5,0(%r11)
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+ASEND(lv1_allocate_memory)
+
+ASENTRY(lv1_write_htab_entry)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-32(%r1)
+ li %r11,1
+ hc
+ extsw %r3,%r3
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+ASEND(lv1_write_htab_entry)
+
+ASENTRY(lv1_construct_virtual_address_space)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-48(%r1)
+ std %r6,32(%r1)
+ std %r7,40(%r1)
+ li %r11,2
+ hc
+ extsw %r3,%r3
+ ld %r11,32(%r1)
+ std %r4,0(%r11)
+ ld %r11,40(%r1)
+ std %r5,0(%r11)
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+ASEND(lv1_construct_virtual_address_space)
+
+ASENTRY(lv1_get_virtual_address_space_id_of_ppe)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-40(%r1)
+ std %r4,32(%r1)
+ li %r11,4
+ hc
+ extsw %r3,%r3
+ ld %r11,32(%r1)
+ std %r4,0(%r11)
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+ASEND(lv1_get_virtual_address_space_id_of_ppe)
+
+ASENTRY(lv1_query_logical_partition_address_region_info)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-72(%r1)
+ std %r4,32(%r1)
+ std %r5,40(%r1)
+ std %r6,48(%r1)
+ std %r7,56(%r1)
+ std %r8,64(%r1)
+ li %r11,6
+ hc
+ extsw %r3,%r3
+ ld %r11,32(%r1)
+ std %r4,0(%r11)
+ ld %r11,40(%r1)
+ std %r5,0(%r11)
+ ld %r11,48(%r1)
+ std %r6,0(%r11)
+ ld %r11,56(%r1)
+ std %r7,0(%r11)
+ ld %r11,64(%r1)
+ std %r8,0(%r11)
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+ASEND(lv1_query_logical_partition_address_region_info)
+
+ASENTRY(lv1_select_virtual_address_space)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-32(%r1)
+ li %r11,7
+ hc
+ extsw %r3,%r3
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+ASEND(lv1_select_virtual_address_space)
+
+ASENTRY(lv1_pause)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-32(%r1)
+ li %r11,9
+ hc
+ extsw %r3,%r3
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+ASEND(lv1_pause)
+
+ASENTRY(lv1_destruct_virtual_address_space)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-32(%r1)
+ li %r11,10
+ hc
+ extsw %r3,%r3
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+ASEND(lv1_destruct_virtual_address_space)
+
+ASENTRY(lv1_configure_irq_state_bitmap)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-32(%r1)
+ li %r11,11
+ hc
+ extsw %r3,%r3
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+ASEND(lv1_configure_irq_state_bitmap)
+
+ASENTRY(lv1_connect_irq_plug_ext)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-32(%r1)
+ li %r11,12
+ hc
+ extsw %r3,%r3
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+ASEND(lv1_connect_irq_plug_ext)
+
+ASENTRY(lv1_release_memory)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-32(%r1)
+ li %r11,13
+ hc
+ extsw %r3,%r3
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+ASEND(lv1_release_memory)
+
+ASENTRY(lv1_put_iopte)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-32(%r1)
+ li %r11,15
+ hc
+ extsw %r3,%r3
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+ASEND(lv1_put_iopte)
+
+ASENTRY(lv1_disconnect_irq_plug_ext)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-32(%r1)
+ li %r11,17
+ hc
+ extsw %r3,%r3
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+ASEND(lv1_disconnect_irq_plug_ext)
+
+ASENTRY(lv1_construct_event_receive_port)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-40(%r1)
+ std %r3,32(%r1)
+ li %r11,18
+ hc
+ extsw %r3,%r3
+ ld %r11,32(%r1)
+ std %r4,0(%r11)
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+ASEND(lv1_construct_event_receive_port)
+
+ASENTRY(lv1_destruct_event_receive_port)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-32(%r1)
+ li %r11,19
+ hc
+ extsw %r3,%r3
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+ASEND(lv1_destruct_event_receive_port)
+
+ASENTRY(lv1_send_event_locally)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-32(%r1)
+ li %r11,24
+ hc
+ extsw %r3,%r3
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+ASEND(lv1_send_event_locally)
+
+ASENTRY(lv1_end_of_interrupt)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-32(%r1)
+ li %r11,27
+ hc
+ extsw %r3,%r3
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+ASEND(lv1_end_of_interrupt)
+
+ASENTRY(lv1_connect_irq_plug)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-32(%r1)
+ li %r11,28
+ hc
+ extsw %r3,%r3
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+ASEND(lv1_connect_irq_plug)
+
+ASENTRY(lv1_disconnect_irq_plus)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-32(%r1)
+ li %r11,29
+ hc
+ extsw %r3,%r3
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+ASEND(lv1_disconnect_irq_plus)
+
+ASENTRY(lv1_end_of_interrupt_ext)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-32(%r1)
+ li %r11,30
+ hc
+ extsw %r3,%r3
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+ASEND(lv1_end_of_interrupt_ext)
+
+ASENTRY(lv1_did_update_interrupt_mask)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-32(%r1)
+ li %r11,31
+ hc
+ extsw %r3,%r3
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+ASEND(lv1_did_update_interrupt_mask)
+
+ASENTRY(lv1_shutdown_logical_partition)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-32(%r1)
+ li %r11,44
+ hc
+ extsw %r3,%r3
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+ASEND(lv1_shutdown_logical_partition)
+
+ASENTRY(lv1_destruct_logical_spe)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-32(%r1)
+ li %r11,54
+ hc
+ extsw %r3,%r3
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+ASEND(lv1_destruct_logical_spe)
+
+ASENTRY(lv1_construct_logical_spe)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-80(%r1)
+ std %r10,32(%r1)
+ ld %r11,176(%r1)
+ std %r11,40(%r1)
+ ld %r11,184(%r1)
+ std %r11,48(%r1)
+ ld %r11,192(%r1)
+ std %r11,56(%r1)
+ ld %r11,200(%r1)
+ std %r11,64(%r1)
+ ld %r11,208(%r1)
+ std %r11,72(%r1)
+ li %r11,57
+ hc
+ extsw %r3,%r3
+ ld %r11,32(%r1)
+ std %r4,0(%r11)
+ ld %r11,40(%r1)
+ std %r5,0(%r11)
+ ld %r11,48(%r1)
+ std %r6,0(%r11)
+ ld %r11,56(%r1)
+ std %r7,0(%r11)
+ ld %r11,64(%r1)
+ std %r8,0(%r11)
+ ld %r11,72(%r1)
+ std %r9,0(%r11)
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+ASEND(lv1_construct_logical_spe)
+
+ASENTRY(lv1_set_spe_interrupt_mask)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-32(%r1)
+ li %r11,61
+ hc
+ extsw %r3,%r3
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+ASEND(lv1_set_spe_interrupt_mask)
+
+ASENTRY(lv1_disable_logical_spe)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-32(%r1)
+ li %r11,65
+ hc
+ extsw %r3,%r3
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+ASEND(lv1_disable_logical_spe)
+
+ASENTRY(lv1_clear_spe_interrupt_status)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-32(%r1)
+ li %r11,66
+ hc
+ extsw %r3,%r3
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+ASEND(lv1_clear_spe_interrupt_status)
+
+ASENTRY(lv1_get_spe_interrupt_status)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-40(%r1)
+ std %r5,32(%r1)
+ li %r11,67
+ hc
+ extsw %r3,%r3
+ ld %r11,32(%r1)
+ std %r4,0(%r11)
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+ASEND(lv1_get_spe_interrupt_status)
+
+ASENTRY(lv1_get_logical_ppe_id)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-40(%r1)
+ std %r3,32(%r1)
+ li %r11,69
+ hc
+ extsw %r3,%r3
+ ld %r11,32(%r1)
+ std %r4,0(%r11)
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+ASEND(lv1_get_logical_ppe_id)
+
+ASENTRY(lv1_get_logical_partition_id)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-40(%r1)
+ std %r3,32(%r1)
+ li %r11,74
+ hc
+ extsw %r3,%r3
+ ld %r11,32(%r1)
+ std %r4,0(%r11)
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+ASEND(lv1_get_logical_partition_id)
+
+ASENTRY(lv1_get_spe_irq_outlet)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-40(%r1)
+ std %r5,32(%r1)
+ li %r11,78
+ hc
+ extsw %r3,%r3
+ ld %r11,32(%r1)
+ std %r4,0(%r11)
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+ASEND(lv1_get_spe_irq_outlet)
+
+ASENTRY(lv1_set_spe_privilege_state_area_1_register)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-32(%r1)
+ li %r11,79
+ hc
+ extsw %r3,%r3
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+ASEND(lv1_set_spe_privilege_state_area_1_register)
+
+ASENTRY(lv1_get_repository_node_value)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-48(%r1)
+ std %r8,32(%r1)
+ std %r9,40(%r1)
+ li %r11,91
+ hc
+ extsw %r3,%r3
+ ld %r11,32(%r1)
+ std %r4,0(%r11)
+ ld %r11,40(%r1)
+ std %r5,0(%r11)
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+ASEND(lv1_get_repository_node_value)
+
+ASENTRY(lv1_read_htab_entries)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-72(%r1)
+ std %r5,32(%r1)
+ std %r6,40(%r1)
+ std %r7,48(%r1)
+ std %r8,56(%r1)
+ std %r9,64(%r1)
+ li %r11,95
+ hc
+ extsw %r3,%r3
+ ld %r11,32(%r1)
+ std %r4,0(%r11)
+ ld %r11,40(%r1)
+ std %r5,0(%r11)
+ ld %r11,48(%r1)
+ std %r6,0(%r11)
+ ld %r11,56(%r1)
+ std %r7,0(%r11)
+ ld %r11,64(%r1)
+ std %r8,0(%r11)
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+ASEND(lv1_read_htab_entries)
+
+ASENTRY(lv1_set_dabr)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-32(%r1)
+ li %r11,96
+ hc
+ extsw %r3,%r3
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+ASEND(lv1_set_dabr)
+
+ASENTRY(lv1_allocate_io_segment)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-40(%r1)
+ std %r6,32(%r1)
+ li %r11,116
+ hc
+ extsw %r3,%r3
+ ld %r11,32(%r1)
+ std %r4,0(%r11)
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+ASEND(lv1_allocate_io_segment)
+
+ASENTRY(lv1_release_io_segment)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-32(%r1)
+ li %r11,117
+ hc
+ extsw %r3,%r3
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+ASEND(lv1_release_io_segment)
+
+ASENTRY(lv1_construct_io_irq_outlet)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-40(%r1)
+ std %r4,32(%r1)
+ li %r11,120
+ hc
+ extsw %r3,%r3
+ ld %r11,32(%r1)
+ std %r4,0(%r11)
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+ASEND(lv1_construct_io_irq_outlet)
+
+ASENTRY(lv1_destruct_io_irq_outlet)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-32(%r1)
+ li %r11,121
+ hc
+ extsw %r3,%r3
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+ASEND(lv1_destruct_io_irq_outlet)
+
+ASENTRY(lv1_map_htab)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-40(%r1)
+ std %r4,32(%r1)
+ li %r11,122
+ hc
+ extsw %r3,%r3
+ ld %r11,32(%r1)
+ std %r4,0(%r11)
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+ASEND(lv1_map_htab)
+
+ASENTRY(lv1_unmap_htab)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-32(%r1)
+ li %r11,123
+ hc
+ extsw %r3,%r3
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+ASEND(lv1_unmap_htab)
+
+ASENTRY(lv1_get_version_info)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-40(%r1)
+ std %r3,32(%r1)
+ li %r11,127
+ hc
+ extsw %r3,%r3
+ ld %r11,32(%r1)
+ std %r4,0(%r11)
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+ASEND(lv1_get_version_info)
+
+ASENTRY(lv1_insert_htab_entry)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-56(%r1)
+ std %r9,32(%r1)
+ std %r10,40(%r1)
+ ld %r11,152(%r1)
+ std %r11,48(%r1)
+ li %r11,158
+ hc
+ extsw %r3,%r3
+ ld %r11,32(%r1)
+ std %r4,0(%r11)
+ ld %r11,40(%r1)
+ std %r5,0(%r11)
+ ld %r11,48(%r1)
+ std %r6,0(%r11)
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+ASEND(lv1_insert_htab_entry)
+
+ASENTRY(lv1_read_virtual_uart)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-40(%r1)
+ std %r6,32(%r1)
+ li %r11,162
+ hc
+ extsw %r3,%r3
+ ld %r11,32(%r1)
+ std %r4,0(%r11)
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+ASEND(lv1_read_virtual_uart)
+
+ASENTRY(lv1_write_virtual_uart)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-40(%r1)
+ std %r6,32(%r1)
+ li %r11,163
+ hc
+ extsw %r3,%r3
+ ld %r11,32(%r1)
+ std %r4,0(%r11)
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+ASEND(lv1_write_virtual_uart)
+
+ASENTRY(lv1_set_virtual_uart_param)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-32(%r1)
+ li %r11,164
+ hc
+ extsw %r3,%r3
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+ASEND(lv1_set_virtual_uart_param)
+
+ASENTRY(lv1_get_virtual_uart_param)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-40(%r1)
+ std %r5,32(%r1)
+ li %r11,165
+ hc
+ extsw %r3,%r3
+ ld %r11,32(%r1)
+ std %r4,0(%r11)
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+ASEND(lv1_get_virtual_uart_param)
+
+ASENTRY(lv1_configure_virtual_uart)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-40(%r1)
+ std %r4,32(%r1)
+ li %r11,166
+ hc
+ extsw %r3,%r3
+ ld %r11,32(%r1)
+ std %r4,0(%r11)
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+ASEND(lv1_configure_virtual_uart)
+
+ASENTRY(lv1_open_device)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-32(%r1)
+ li %r11,170
+ hc
+ extsw %r3,%r3
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+ASEND(lv1_open_device)
+
+ASENTRY(lv1_close_device)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-32(%r1)
+ li %r11,171
+ hc
+ extsw %r3,%r3
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+ASEND(lv1_close_device)
+
+ASENTRY(lv1_map_device_mmio_region)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-40(%r1)
+ std %r8,32(%r1)
+ li %r11,172
+ hc
+ extsw %r3,%r3
+ ld %r11,32(%r1)
+ std %r4,0(%r11)
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+ASEND(lv1_map_device_mmio_region)
+
+ASENTRY(lv1_unmap_device_mmio_region)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-32(%r1)
+ li %r11,173
+ hc
+ extsw %r3,%r3
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+ASEND(lv1_unmap_device_mmio_region)
+
+ASENTRY(lv1_allocate_device_dma_region)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-40(%r1)
+ std %r8,32(%r1)
+ li %r11,174
+ hc
+ extsw %r3,%r3
+ ld %r11,32(%r1)
+ std %r4,0(%r11)
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+ASEND(lv1_allocate_device_dma_region)
+
+ASENTRY(lv1_free_device_dma_region)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-32(%r1)
+ li %r11,175
+ hc
+ extsw %r3,%r3
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+ASEND(lv1_free_device_dma_region)
+
+ASENTRY(lv1_map_device_dma_region)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-32(%r1)
+ li %r11,176
+ hc
+ extsw %r3,%r3
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+ASEND(lv1_map_device_dma_region)
+
+ASENTRY(lv1_unmap_device_dma_region)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-32(%r1)
+ li %r11,177
+ hc
+ extsw %r3,%r3
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+ASEND(lv1_unmap_device_dma_region)
+
+ASENTRY(lv1_read_pci_config)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-40(%r1)
+ std %r9,32(%r1)
+ li %r11,178
+ hc
+ extsw %r3,%r3
+ ld %r11,32(%r1)
+ std %r4,0(%r11)
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+ASEND(lv1_read_pci_config)
+
+ASENTRY(lv1_write_pci_config)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-32(%r1)
+ li %r11,179
+ hc
+ extsw %r3,%r3
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+ASEND(lv1_write_pci_config)
+
+ASENTRY(lv1_net_add_multicast_address)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-32(%r1)
+ li %r11,185
+ hc
+ extsw %r3,%r3
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+ASEND(lv1_net_add_multicast_address)
+
+ASENTRY(lv1_net_remove_multicast_address)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-32(%r1)
+ li %r11,186
+ hc
+ extsw %r3,%r3
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+ASEND(lv1_net_remove_multicast_address)
+
+ASENTRY(lv1_net_start_tx_dma)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-32(%r1)
+ li %r11,187
+ hc
+ extsw %r3,%r3
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+ASEND(lv1_net_start_tx_dma)
+
+ASENTRY(lv1_net_stop_tx_dma)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-32(%r1)
+ li %r11,188
+ hc
+ extsw %r3,%r3
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+ASEND(lv1_net_stop_tx_dma)
+
+ASENTRY(lv1_net_start_rx_dma)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-32(%r1)
+ li %r11,189
+ hc
+ extsw %r3,%r3
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+ASEND(lv1_net_start_rx_dma)
+
+ASENTRY(lv1_net_stop_rx_dma)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-32(%r1)
+ li %r11,190
+ hc
+ extsw %r3,%r3
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+ASEND(lv1_net_stop_rx_dma)
+
+ASENTRY(lv1_net_set_interrupt_status_indicator)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-32(%r1)
+ li %r11,191
+ hc
+ extsw %r3,%r3
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+ASEND(lv1_net_set_interrupt_status_indicator)
+
+ASENTRY(lv1_net_set_interrupt_mask)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-32(%r1)
+ li %r11,193
+ hc
+ extsw %r3,%r3
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+ASEND(lv1_net_set_interrupt_mask)
+
+ASENTRY(lv1_net_control)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-48(%r1)
+ std %r9,32(%r1)
+ std %r10,40(%r1)
+ li %r11,194
+ hc
+ extsw %r3,%r3
+ ld %r11,32(%r1)
+ std %r4,0(%r11)
+ ld %r11,40(%r1)
+ std %r5,0(%r11)
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+ASEND(lv1_net_control)
+
+ASENTRY(lv1_connect_interrupt_event_receive_port)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-32(%r1)
+ li %r11,197
+ hc
+ extsw %r3,%r3
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+ASEND(lv1_connect_interrupt_event_receive_port)
+
+ASENTRY(lv1_disconnect_interrupt_event_receive_port)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-32(%r1)
+ li %r11,198
+ hc
+ extsw %r3,%r3
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+ASEND(lv1_disconnect_interrupt_event_receive_port)
+
+ASENTRY(lv1_deconfigure_virtual_uart_irq)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-32(%r1)
+ li %r11,202
+ hc
+ extsw %r3,%r3
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+ASEND(lv1_deconfigure_virtual_uart_irq)
+
+ASENTRY(lv1_enable_logical_spe)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-32(%r1)
+ li %r11,207
+ hc
+ extsw %r3,%r3
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+ASEND(lv1_enable_logical_spe)
+
+ASENTRY(lv1_gpu_open)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-32(%r1)
+ li %r11,210
+ hc
+ extsw %r3,%r3
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+ASEND(lv1_gpu_open)
+
+ASENTRY(lv1_gpu_close)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-32(%r1)
+ li %r11,211
+ hc
+ extsw %r3,%r3
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+ASEND(lv1_gpu_close)
+
+ASENTRY(lv1_gpu_device_map)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-48(%r1)
+ std %r4,32(%r1)
+ std %r5,40(%r1)
+ li %r11,212
+ hc
+ extsw %r3,%r3
+ ld %r11,32(%r1)
+ std %r4,0(%r11)
+ ld %r11,40(%r1)
+ std %r5,0(%r11)
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+ASEND(lv1_gpu_device_map)
+
+ASENTRY(lv1_gpu_device_unmap)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-32(%r1)
+ li %r11,213
+ hc
+ extsw %r3,%r3
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+ASEND(lv1_gpu_device_unmap)
+
+ASENTRY(lv1_gpu_memory_allocate)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-48(%r1)
+ std %r8,32(%r1)
+ std %r9,40(%r1)
+ li %r11,214
+ hc
+ extsw %r3,%r3
+ ld %r11,32(%r1)
+ std %r4,0(%r11)
+ ld %r11,40(%r1)
+ std %r5,0(%r11)
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+ASEND(lv1_gpu_memory_allocate)
+
+ASENTRY(lv1_gpu_memory_free)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-32(%r1)
+ li %r11,216
+ hc
+ extsw %r3,%r3
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+ASEND(lv1_gpu_memory_free)
+
+ASENTRY(lv1_gpu_context_allocate)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-72(%r1)
+ std %r5,32(%r1)
+ std %r6,40(%r1)
+ std %r7,48(%r1)
+ std %r8,56(%r1)
+ std %r9,64(%r1)
+ li %r11,217
+ hc
+ extsw %r3,%r3
+ ld %r11,32(%r1)
+ std %r4,0(%r11)
+ ld %r11,40(%r1)
+ std %r5,0(%r11)
+ ld %r11,48(%r1)
+ std %r6,0(%r11)
+ ld %r11,56(%r1)
+ std %r7,0(%r11)
+ ld %r11,64(%r1)
+ std %r8,0(%r11)
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+ASEND(lv1_gpu_context_allocate)
+
+ASENTRY(lv1_gpu_context_free)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-32(%r1)
+ li %r11,218
+ hc
+ extsw %r3,%r3
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+ASEND(lv1_gpu_context_free)
+
+ASENTRY(lv1_gpu_context_iomap)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-32(%r1)
+ li %r11,221
+ hc
+ extsw %r3,%r3
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+ASEND(lv1_gpu_context_iomap)
+
+ASENTRY(lv1_gpu_context_attribute)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-32(%r1)
+ li %r11,225
+ hc
+ extsw %r3,%r3
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+ASEND(lv1_gpu_context_attribute)
+
+ASENTRY(lv1_gpu_context_intr)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-40(%r1)
+ std %r4,32(%r1)
+ li %r11,227
+ hc
+ extsw %r3,%r3
+ ld %r11,32(%r1)
+ std %r4,0(%r11)
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+ASEND(lv1_gpu_context_intr)
+
+ASENTRY(lv1_gpu_attribute)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-32(%r1)
+ li %r11,228
+ hc
+ extsw %r3,%r3
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+ASEND(lv1_gpu_attribute)
+
+ASENTRY(lv1_get_rtc)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-48(%r1)
+ std %r3,32(%r1)
+ std %r4,40(%r1)
+ li %r11,232
+ hc
+ extsw %r3,%r3
+ ld %r11,32(%r1)
+ std %r4,0(%r11)
+ ld %r11,40(%r1)
+ std %r5,0(%r11)
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+ASEND(lv1_get_rtc)
+
+ASENTRY(lv1_storage_read)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-40(%r1)
+ std %r9,32(%r1)
+ li %r11,245
+ hc
+ extsw %r3,%r3
+ ld %r11,32(%r1)
+ std %r4,0(%r11)
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+ASEND(lv1_storage_read)
+
+ASENTRY(lv1_storage_write)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-40(%r1)
+ std %r9,32(%r1)
+ li %r11,246
+ hc
+ extsw %r3,%r3
+ ld %r11,32(%r1)
+ std %r4,0(%r11)
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+ASEND(lv1_storage_write)
+
+ASENTRY(lv1_storage_send_device_command)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-40(%r1)
+ std %r9,32(%r1)
+ li %r11,248
+ hc
+ extsw %r3,%r3
+ ld %r11,32(%r1)
+ std %r4,0(%r11)
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+ASEND(lv1_storage_send_device_command)
+
+ASENTRY(lv1_storage_get_async_status)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-48(%r1)
+ std %r4,32(%r1)
+ std %r5,40(%r1)
+ li %r11,249
+ hc
+ extsw %r3,%r3
+ ld %r11,32(%r1)
+ std %r4,0(%r11)
+ ld %r11,40(%r1)
+ std %r5,0(%r11)
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+ASEND(lv1_storage_get_async_status)
+
+ASENTRY(lv1_storage_check_async_status)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-40(%r1)
+ std %r5,32(%r1)
+ li %r11,254
+ hc
+ extsw %r3,%r3
+ ld %r11,32(%r1)
+ std %r4,0(%r11)
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+ASEND(lv1_storage_check_async_status)
+
+ASENTRY(lv1_panic)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-32(%r1)
+ li %r11,255
+ hc
+ extsw %r3,%r3
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+ASEND(lv1_panic)
+
diff --git a/sys/powerpc/ps3/ps3-hvcall.h b/sys/powerpc/ps3/ps3-hvcall.h
new file mode 100644
index 000000000000..1cb187ef8e21
--- /dev/null
+++ b/sys/powerpc/ps3/ps3-hvcall.h
@@ -0,0 +1,137 @@
+/*
+ * Playstation 3 LV1 hypercall interface
+ */
+
+#include <sys/types.h>
+
+enum lpar_id {
+ PS3_LPAR_ID_CURRENT = 0x00,
+ PS3_LPAR_ID_PME = 0x01,
+};
+
+/* Return codes from hypercalls */
+#define LV1_SUCCESS 0
+#define LV1_RESOURCE_SHORTAGE -2
+#define LV1_NO_PRIVILEGE -3
+#define LV1_DENIED_BY_POLICY -4
+#define LV1_ACCESS_VIOLATION -5
+#define LV1_NO_ENTRY -6
+#define LV1_DUPLICATE_ENTRY -7
+#define LV1_TYPE_MISMATCH -8
+#define LV1_BUSY -9
+#define LV1_EMPTY -10
+#define LV1_WRONG_STATE -11
+#define LV1_NO_MATCH -13
+#define LV1_ALREADY_CONNECTED -14
+#define LV1_UNSUPPORTED_PARAMETER_VALUE -15
+#define LV1_CONDITION_NOT_SATISFIED -16
+#define LV1_ILLEGAL_PARAMETER_VALUE -17
+#define LV1_BAD_OPTION -18
+#define LV1_IMPLEMENTATION_LIMITATION -19
+#define LV1_NOT_IMPLEMENTED -20
+#define LV1_INVALID_CLASS_ID -21
+#define LV1_CONSTRAINT_NOT_SATISFIED -22
+#define LV1_ALIGNMENT_ERROR -23
+#define LV1_HARDWARE_ERROR -24
+#define LV1_INVALID_DATA_FORMAT -25
+#define LV1_INVALID_OPERATION -26
+#define LV1_INTERNAL_ERROR -32768
+
+static inline uint64_t
+lv1_repository_string(const char *str)
+{
+ uint64_t ret = 0;
+ strncpy((char *)&ret, str, sizeof(ret));
+ return (ret);
+}
+
+int lv1_allocate_memory(uint64_t size, uint64_t log_page_size, uint64_t zero, uint64_t flags, uint64_t *base_addr, uint64_t *muid);
+int lv1_write_htab_entry(uint64_t vas_id, uint64_t slot, uint64_t pte_hi, uint64_t pte_lo);
+int lv1_construct_virtual_address_space(uint64_t log_pteg_count, uint64_t n_sizes, uint64_t page_sizes, uint64_t *vas_id, uint64_t *hv_pteg_count);
+int lv1_get_virtual_address_space_id_of_ppe(uint64_t ppe_id, uint64_t *vas_id);
+int lv1_query_logical_partition_address_region_info(uint64_t lpar_id, uint64_t *base_addr, uint64_t *size, uint64_t *access_right, uint64_t *max_page_size, uint64_t *flags);
+int lv1_select_virtual_address_space(uint64_t vas_id);
+int lv1_pause(uint64_t mode);
+int lv1_destruct_virtual_address_space(uint64_t vas_id);
+int lv1_configure_irq_state_bitmap(uint64_t ppe_id, uint64_t cpu_id, uint64_t bitmap_addr);
+int lv1_connect_irq_plug_ext(uint64_t ppe_id, uint64_t cpu_id, uint64_t virq, uint64_t outlet, uint64_t zero);
+int lv1_release_memory(uint64_t base_addr);
+int lv1_put_iopte(uint64_t ioas_id, uint64_t ioif_addr, uint64_t lpar_addr, uint64_t io_id, uint64_t flags);
+int lv1_disconnect_irq_plug_ext(uint64_t ppe_id, uint64_t cpu_id, uint64_t virq);
+int lv1_construct_event_receive_port(uint64_t *outlet);
+int lv1_destruct_event_receive_port(uint64_t outlet);
+int lv1_send_event_locally(uint64_t outlet);
+int lv1_end_of_interrupt(uint64_t irq);
+int lv1_connect_irq_plug(uint64_t virq, uint64_t irq);
+int lv1_disconnect_irq_plus(uint64_t virq);
+int lv1_end_of_interrupt_ext(uint64_t ppe_id, uint64_t cpu_id, uint64_t virq);
+int lv1_did_update_interrupt_mask(uint64_t ppe_id, uint64_t cpu_id);
+int lv1_shutdown_logical_partition(uint64_t cmd);
+int lv1_destruct_logical_spe(uint64_t spe_id);
+int lv1_construct_logical_spe(uint64_t pshift1, uint64_t pshift2, uint64_t pshift3, uint64_t pshift4, uint64_t pshift5, uint64_t vas_id, uint64_t spe_type, uint64_t *priv2_addr, uint64_t *problem_phys, uint64_t *local_store_phys, uint64_t *unused, uint64_t *shadow_addr, uint64_t *spe_id);
+int lv1_set_spe_interrupt_mask(uint64_t spe_id, uint64_t class, uint64_t mask);
+int lv1_disable_logical_spe(uint64_t spe_id, uint64_t zero);
+int lv1_clear_spe_interrupt_status(uint64_t spe_id, uint64_t class, uint64_t stat, uint64_t zero);
+int lv1_get_spe_interrupt_status(uint64_t spe_id, uint64_t class, uint64_t *stat);
+int lv1_get_logical_ppe_id(uint64_t *ppe_id);
+int lv1_get_logical_partition_id(uint64_t *lpar_id);
+int lv1_get_spe_irq_outlet(uint64_t spe_id, uint64_t class, uint64_t *outlet);
+int lv1_set_spe_privilege_state_area_1_register(uint64_t spe_id, uint64_t offset, uint64_t value);
+int lv1_get_repository_node_value(uint64_t lpar_id, uint64_t n1, uint64_t n2, uint64_t n3, uint64_t n4, uint64_t *v1, uint64_t *v2);
+int lv1_read_htab_entries(uint64_t vas_id, uint64_t slot, uint64_t *hi1, uint64_t *hi2, uint64_t *hi3, uint64_t *hi4, uint64_t *rcbits);
+int lv1_set_dabr(uint64_t dabr, uint64_t flags);
+int lv1_allocate_io_segment(uint64_t ioas_id, uint64_t seg_size, uint64_t io_pagesize, uint64_t *ioif_addr);
+int lv1_release_io_segment(uint64_t ioas_id, uint64_t ioif_addr);
+int lv1_construct_io_irq_outlet(uint64_t interrupt_id, uint64_t *outlet);
+int lv1_destruct_io_irq_outlet(uint64_t outlet);
+int lv1_map_htab(uint64_t lpar_id, uint64_t *htab_addr);
+int lv1_unmap_htab(uint64_t htab_addr);
+int lv1_get_version_info(uint64_t *firm_vers);
+int lv1_insert_htab_entry(uint64_t vas_id, uint64_t pteg, uint64_t pte_hi, uint64_t pte_lo, uint64_t lockflags, uint64_t flags, uint64_t *index, uint64_t *evicted_hi, uint64_t *evicted_lo);
+int lv1_read_virtual_uart(uint64_t port, uint64_t buffer, uint64_t bytes, uint64_t *bytes_read);
+int lv1_write_virtual_uart(uint64_t port, uint64_t buffer, uint64_t bytes, uint64_t *bytes_written);
+int lv1_set_virtual_uart_param(uint64_t port, uint64_t param, uint64_t value);
+int lv1_get_virtual_uart_param(uint64_t port, uint64_t param, uint64_t *value);
+int lv1_configure_virtual_uart(uint64_t lpar_addr, uint64_t *outlet);
+int lv1_open_device(uint64_t bus, uint64_t dev, uint64_t zero);
+int lv1_close_device(uint64_t bus, uint64_t dev);
+int lv1_map_device_mmio_region(uint64_t bus, uint64_t dev, uint64_t bus_addr, uint64_t size, uint64_t page_size, uint64_t *lpar_addr);
+int lv1_unmap_device_mmio_region(uint64_t bus, uint64_t dev, uint64_t lpar_addr);
+int lv1_allocate_device_dma_region(uint64_t bus, uint64_t dev, uint64_t io_size, uint64_t io_pagesize, uint64_t flag, uint64_t *dma_region);
+int lv1_free_device_dma_region(uint64_t bus, uint64_t dev, uint64_t dma_region);
+int lv1_map_device_dma_region(uint64_t bus, uint64_t dev, uint64_t lpar_addr, uint64_t dma_region, uint64_t size, uint64_t flags);
+int lv1_unmap_device_dma_region(uint64_t bus, uint64_t dev, uint64_t dma_region, uint64_t size);
+int lv1_read_pci_config(uint64_t ps3bus, uint64_t bus, uint64_t dev, uint64_t func, uint64_t offset, uint64_t size, uint64_t *result);
+int lv1_write_pci_config(uint64_t ps3bus, uint64_t bus, uint64_t dev, uint64_t func, uint64_t offset, uint64_t size, uint64_t data);
+int lv1_net_add_multicast_address(uint64_t bus, uint64_t dev, uint64_t addr, uint64_t flags);
+int lv1_net_remove_multicast_address(uint64_t bus, uint64_t dev, uint64_t zero, uint64_t one);
+int lv1_net_start_tx_dma(uint64_t bus, uint64_t dev, uint64_t bus_addr, uint64_t zero);
+int lv1_net_stop_tx_dma(uint64_t bus, uint64_t dev, uint64_t zero);
+int lv1_net_start_rx_dma(uint64_t bus, uint64_t dev, uint64_t bus_addr, uint64_t zero);
+int lv1_net_stop_rx_dma(uint64_t bus, uint64_t dev, uint64_t zero);
+int lv1_net_set_interrupt_status_indicator(uint64_t bus, uint64_t dev, uint64_t irq_status_addr, uint64_t zero);
+int lv1_net_set_interrupt_mask(uint64_t bus, uint64_t dev, uint64_t mask, uint64_t zero);
+int lv1_net_control(uint64_t bus, uint64_t dev, uint64_t p1, uint64_t p2, uint64_t p3, uint64_t p4, uint64_t *v1, uint64_t *v2);
+int lv1_connect_interrupt_event_receive_port(uint64_t bus, uint64_t dev, uint64_t outlet, uint64_t irq);
+int lv1_disconnect_interrupt_event_receive_port(uint64_t bus, uint64_t dev, uint64_t outlet, uint64_t irq);
+int lv1_deconfigure_virtual_uart_irq(void);
+int lv1_enable_logical_spe(uint64_t spe_id, uint64_t resource_id);
+int lv1_gpu_open(uint64_t zero);
+int lv1_gpu_close(void);
+int lv1_gpu_device_map(uint64_t dev, uint64_t *lpar_addr, uint64_t *lpar_size);
+int lv1_gpu_device_unmap(uint64_t dev);
+int lv1_gpu_memory_allocate(uint64_t ddr_size, uint64_t zero1, uint64_t zero2, uint64_t zero3, uint64_t zero4, uint64_t *handle, uint64_t *ddr_lpar);
+int lv1_gpu_memory_free(uint64_t handle);
+int lv1_gpu_context_allocate(uint64_t handle, uint64_t flags, uint64_t *chandle, uint64_t *lpar_dma_control, uint64_t *lpar_driver_info, uint64_t *lpar_reports, uint64_t *lpar_reports_size);
+int lv1_gpu_context_free(uint64_t chandle);
+int lv1_gpu_context_iomap(uint64_t changle, uint64_t gpu_ioif, uint64_t xdr_lpar, uint64_t fbsize, uint64_t ioflags);
+int lv1_gpu_context_attribute(uint64_t chandle, uint64_t op, uint64_t p1, uint64_t p2, uint64_t p3, uint64_t p4);
+int lv1_gpu_context_intr(uint64_t chandle, uint64_t *v1);
+int lv1_gpu_attribute(uint64_t p1, uint64_t p2, uint64_t p3, uint64_t p4, uint64_t p5);
+int lv1_get_rtc(uint64_t *rtc_val, uint64_t *timebase);
+int lv1_storage_read(uint64_t dev, uint64_t region, uint64_t sector, uint64_t nsectors, uint64_t flags, uint64_t buf, uint64_t *dma_tag);
+int lv1_storage_write(uint64_t dev, uint64_t region, uint64_t sector, uint64_t nsectors, uint64_t flags, uint64_t buf, uint64_t *dma_tag);
+int lv1_storage_send_device_command(uint64_t dev, uint64_t cmd_id, uint64_t cmd_block, uint64_t cmd_size, uint64_t data_buf, uint64_t blocks, uint64_t *dma_tag);
+int lv1_storage_get_async_status(uint64_t dev, uint64_t *dma_tag, uint64_t *status);
+int lv1_storage_check_async_status(uint64_t dev, uint64_t dma_tag, uint64_t *status);
+int lv1_panic(uint64_t howto);
diff --git a/sys/powerpc/ps3/ps3-hvcall.master b/sys/powerpc/ps3/ps3-hvcall.master
new file mode 100644
index 000000000000..3b3835f0e055
--- /dev/null
+++ b/sys/powerpc/ps3/ps3-hvcall.master
@@ -0,0 +1,138 @@
+/*
+ * Playstation 3 LV1 hypercall interface
+ */
+
+#include <sys/types.h>
+
+enum lpar_id {
+ PS3_LPAR_ID_CURRENT = 0x00,
+ PS3_LPAR_ID_PME = 0x01,
+};
+
+/* Return codes from hypercalls */
+#define LV1_SUCCESS 0
+#define LV1_RESOURCE_SHORTAGE -2
+#define LV1_NO_PRIVILEGE -3
+#define LV1_DENIED_BY_POLICY -4
+#define LV1_ACCESS_VIOLATION -5
+#define LV1_NO_ENTRY -6
+#define LV1_DUPLICATE_ENTRY -7
+#define LV1_TYPE_MISMATCH -8
+#define LV1_BUSY -9
+#define LV1_EMPTY -10
+#define LV1_WRONG_STATE -11
+#define LV1_NO_MATCH -13
+#define LV1_ALREADY_CONNECTED -14
+#define LV1_UNSUPPORTED_PARAMETER_VALUE -15
+#define LV1_CONDITION_NOT_SATISFIED -16
+#define LV1_ILLEGAL_PARAMETER_VALUE -17
+#define LV1_BAD_OPTION -18
+#define LV1_IMPLEMENTATION_LIMITATION -19
+#define LV1_NOT_IMPLEMENTED -20
+#define LV1_INVALID_CLASS_ID -21
+#define LV1_CONSTRAINT_NOT_SATISFIED -22
+#define LV1_ALIGNMENT_ERROR -23
+#define LV1_HARDWARE_ERROR -24
+#define LV1_INVALID_DATA_FORMAT -25
+#define LV1_INVALID_OPERATION -26
+#define LV1_INTERNAL_ERROR -32768
+
+static inline uint64_t
+lv1_repository_string(const char *str)
+{
+ uint64_t ret = 0;
+ strncpy((char *)&ret, str, sizeof(ret));
+ return (ret);
+}
+
+# Code Name Inputs Outputs
+HVCALL 0 lv1_allocate_memory size,log_page_size,zero,flags base_addr,muid
+HVCALL 1 lv1_write_htab_entry vas_id,slot,pte_hi,pte_lo
+HVCALL 2 lv1_construct_virtual_address_space log_pteg_count,n_sizes,page_sizes vas_id,hv_pteg_count
+HVCALL 4 lv1_get_virtual_address_space_id_of_ppe ppe_id vas_id
+HVCALL 6 lv1_query_logical_partition_address_region_info lpar_id base_addr,size,access_right,max_page_size,flags
+HVCALL 7 lv1_select_virtual_address_space vas_id
+HVCALL 9 lv1_pause mode
+HVCALL 10 lv1_destruct_virtual_address_space vas_id
+HVCALL 11 lv1_configure_irq_state_bitmap ppe_id,cpu_id,bitmap_addr
+HVCALL 12 lv1_connect_irq_plug_ext ppe_id,cpu_id,virq,outlet,zero
+HVCALL 13 lv1_release_memory base_addr
+HVCALL 15 lv1_put_iopte ioas_id,ioif_addr,lpar_addr,io_id,flags
+HVCALL 17 lv1_disconnect_irq_plug_ext ppe_id,cpu_id,virq
+HVCALL 18 lv1_construct_event_receive_port UNUSED outlet
+HVCALL 19 lv1_destruct_event_receive_port outlet
+HVCALL 24 lv1_send_event_locally outlet
+HVCALL 27 lv1_end_of_interrupt irq
+HVCALL 28 lv1_connect_irq_plug virq,irq
+HVCALL 29 lv1_disconnect_irq_plus virq
+HVCALL 30 lv1_end_of_interrupt_ext ppe_id,cpu_id,virq
+HVCALL 31 lv1_did_update_interrupt_mask ppe_id,cpu_id
+HVCALL 44 lv1_shutdown_logical_partition cmd
+HVCALL 54 lv1_destruct_logical_spe spe_id
+HVCALL 57 lv1_construct_logical_spe pshift1,pshift2,pshift3,pshift4,pshift5,vas_id,spe_type priv2_addr,problem_phys,local_store_phys,unused,shadow_addr,spe_id
+HVCALL 61 lv1_set_spe_interrupt_mask spe_id,class,mask
+HVCALL 65 lv1_disable_logical_spe spe_id,zero
+HVCALL 66 lv1_clear_spe_interrupt_status spe_id,class,stat,zero
+HVCALL 67 lv1_get_spe_interrupt_status spe_id,class stat
+HVCALL 69 lv1_get_logical_ppe_id UNUSED ppe_id
+HVCALL 74 lv1_get_logical_partition_id UNUSED lpar_id
+HVCALL 78 lv1_get_spe_irq_outlet spe_id,class outlet
+HVCALL 79 lv1_set_spe_privilege_state_area_1_register spe_id,offset,value
+HVCALL 91 lv1_get_repository_node_value lpar_id,n1,n2,n3,n4 v1,v2
+HVCALL 95 lv1_read_htab_entries vas_id,slot hi1,hi2,hi3,hi4,rcbits
+HVCALL 96 lv1_set_dabr dabr,flags
+HVCALL 116 lv1_allocate_io_segment ioas_id,seg_size,io_pagesize ioif_addr
+HVCALL 117 lv1_release_io_segment ioas_id,ioif_addr
+HVCALL 120 lv1_construct_io_irq_outlet interrupt_id outlet
+HVCALL 121 lv1_destruct_io_irq_outlet outlet
+HVCALL 122 lv1_map_htab lpar_id htab_addr
+HVCALL 123 lv1_unmap_htab htab_addr
+HVCALL 127 lv1_get_version_info UNUSED firm_vers
+HVCALL 158 lv1_insert_htab_entry vas_id,pteg,pte_hi,pte_lo,lockflags,flags index,evicted_hi,evicted_lo
+HVCALL 162 lv1_read_virtual_uart port,buffer,bytes bytes_read
+HVCALL 163 lv1_write_virtual_uart port,buffer,bytes bytes_written
+HVCALL 164 lv1_set_virtual_uart_param port,param,value
+HVCALL 165 lv1_get_virtual_uart_param port,param value
+HVCALL 166 lv1_configure_virtual_uart lpar_addr outlet
+HVCALL 170 lv1_open_device bus,dev,zero
+HVCALL 171 lv1_close_device bus,dev
+HVCALL 172 lv1_map_device_mmio_region bus,dev,bus_addr,size,page_size lpar_addr
+HVCALL 173 lv1_unmap_device_mmio_region bus,dev,lpar_addr
+HVCALL 174 lv1_allocate_device_dma_region bus,dev,io_size,io_pagesize,flag dma_region
+HVCALL 175 lv1_free_device_dma_region bus,dev,dma_region
+HVCALL 176 lv1_map_device_dma_region bus,dev,lpar_addr,dma_region,size,flags
+HVCALL 177 lv1_unmap_device_dma_region bus,dev,dma_region,size
+HVCALL 178 lv1_read_pci_config ps3bus,bus,dev,func,offset,size result
+HVCALL 179 lv1_write_pci_config ps3bus,bus,dev,func,offset,size,data
+HVCALL 185 lv1_net_add_multicast_address bus,dev,addr,flags
+HVCALL 186 lv1_net_remove_multicast_address bus,dev,zero,one
+HVCALL 187 lv1_net_start_tx_dma bus,dev,bus_addr,zero
+HVCALL 188 lv1_net_stop_tx_dma bus,dev,zero
+HVCALL 189 lv1_net_start_rx_dma bus,dev,bus_addr,zero
+HVCALL 190 lv1_net_stop_rx_dma bus,dev,zero
+HVCALL 191 lv1_net_set_interrupt_status_indicator bus,dev,irq_status_addr,zero
+HVCALL 193 lv1_net_set_interrupt_mask bus,dev,mask,zero
+HVCALL 194 lv1_net_control bus,dev,p1,p2,p3,p4 v1,v2
+HVCALL 197 lv1_connect_interrupt_event_receive_port bus,dev,outlet,irq
+HVCALL 198 lv1_disconnect_interrupt_event_receive_port bus,dev,outlet,irq
+HVCALL 202 lv1_deconfigure_virtual_uart_irq
+HVCALL 207 lv1_enable_logical_spe spe_id,resource_id
+HVCALL 210 lv1_gpu_open zero
+HVCALL 211 lv1_gpu_close
+HVCALL 212 lv1_gpu_device_map dev lpar_addr,lpar_size
+HVCALL 213 lv1_gpu_device_unmap dev
+HVCALL 214 lv1_gpu_memory_allocate ddr_size,zero1,zero2,zero3,zero4 handle,ddr_lpar
+HVCALL 216 lv1_gpu_memory_free handle
+HVCALL 217 lv1_gpu_context_allocate handle,flags chandle,lpar_dma_control,lpar_driver_info,lpar_reports,lpar_reports_size
+HVCALL 218 lv1_gpu_context_free chandle
+HVCALL 221 lv1_gpu_context_iomap changle,gpu_ioif,xdr_lpar,fbsize,ioflags
+HVCALL 225 lv1_gpu_context_attribute chandle,op,p1,p2,p3,p4
+HVCALL 227 lv1_gpu_context_intr chandle v1
+HVCALL 228 lv1_gpu_attribute p1,p2,p3,p4,p5
+HVCALL 232 lv1_get_rtc UNUSED rtc_val,timebase
+HVCALL 245 lv1_storage_read dev,region,sector,nsectors,flags,buf dma_tag
+HVCALL 246 lv1_storage_write dev,region,sector,nsectors,flags,buf dma_tag
+HVCALL 248 lv1_storage_send_device_command dev,cmd_id,cmd_block,cmd_size,data_buf,blocks dma_tag
+HVCALL 249 lv1_storage_get_async_status dev dma_tag,status
+HVCALL 254 lv1_storage_check_async_status dev,dma_tag status
+HVCALL 255 lv1_panic howto
diff --git a/sys/powerpc/ps3/ps3_syscons.c b/sys/powerpc/ps3/ps3_syscons.c
new file mode 100644
index 000000000000..0e2f21056258
--- /dev/null
+++ b/sys/powerpc/ps3/ps3_syscons.c
@@ -0,0 +1,237 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2011-2014 Nathan Whitehorn
+ * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/sysctl.h>
+#include <sys/limits.h>
+#include <sys/conf.h>
+#include <sys/cons.h>
+#include <sys/fbio.h>
+
+#include <vm/vm.h>
+#include <vm/pmap.h>
+
+#include <machine/platform.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/vt/vt.h>
+#include <dev/vt/hw/fb/vt_fb.h>
+#include <dev/vt/colors/vt_termcolors.h>
+
+#include "ps3-hvcall.h"
+
+#define L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_MODE_SET 0x0100
+#define L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_SYNC 0x0101
+#define L1GPU_DISPLAY_SYNC_HSYNC 1
+#define L1GPU_DISPLAY_SYNC_VSYNC 2
+#define L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_FLIP 0x0102
+
+static vd_init_t ps3fb_init;
+static vd_probe_t ps3fb_probe;
+void ps3fb_remap(void);
+
+struct ps3fb_softc {
+ struct fb_info fb_info;
+
+ uint64_t sc_fbhandle;
+ uint64_t sc_fbcontext;
+ uint64_t sc_dma_control;
+ uint64_t sc_driver_info;
+ uint64_t sc_reports;
+ uint64_t sc_reports_size;
+};
+
+static struct vt_driver vt_ps3fb_driver = {
+ .vd_name = "ps3fb",
+ .vd_probe = ps3fb_probe,
+ .vd_init = ps3fb_init,
+ .vd_blank = vt_fb_blank,
+ .vd_bitblt_text = vt_fb_bitblt_text,
+ .vd_bitblt_bmp = vt_fb_bitblt_bitmap,
+ .vd_drawrect = vt_fb_drawrect,
+ .vd_setpixel = vt_fb_setpixel,
+ .vd_fb_ioctl = vt_fb_ioctl,
+ .vd_fb_mmap = vt_fb_mmap,
+ /* Better than VGA, but still generic driver. */
+ .vd_priority = VD_PRIORITY_GENERIC + 1,
+};
+
+VT_DRIVER_DECLARE(vt_ps3fb, vt_ps3fb_driver);
+static struct ps3fb_softc ps3fb_softc;
+
+static int
+ps3fb_probe(struct vt_device *vd)
+{
+ int disable;
+ char compatible[64];
+ phandle_t root;
+
+ disable = 0;
+ TUNABLE_INT_FETCH("hw.syscons.disable", &disable);
+ if (disable != 0)
+ return (0);
+
+ TUNABLE_STR_FETCH("hw.platform", compatible, sizeof(compatible));
+ if (strcmp(compatible, "ps3") == 0)
+ return (CN_INTERNAL);
+
+ root = OF_finddevice("/");
+ if (OF_getprop(root, "compatible", compatible, sizeof(compatible)) <= 0)
+ return (CN_DEAD);
+
+ if (strncmp(compatible, "sony,ps3", sizeof(compatible)) != 0)
+ return (CN_DEAD);
+
+ return (CN_INTERNAL);
+}
+
+void
+ps3fb_remap(void)
+{
+ struct ps3fb_softc *sc;
+ vm_offset_t va, fb_paddr;
+
+ sc = &ps3fb_softc;
+
+ lv1_gpu_close();
+ lv1_gpu_open(0);
+
+ lv1_gpu_context_attribute(0, L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_MODE_SET,
+ 0,0,0,0);
+ lv1_gpu_context_attribute(0, L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_MODE_SET,
+ 0,0,1,0);
+ lv1_gpu_context_attribute(0, L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_SYNC,
+ 0,L1GPU_DISPLAY_SYNC_VSYNC,0,0);
+ lv1_gpu_context_attribute(0, L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_SYNC,
+ 1,L1GPU_DISPLAY_SYNC_VSYNC,0,0);
+ lv1_gpu_memory_allocate(roundup2(sc->fb_info.fb_size, 1024*1024),
+ 0, 0, 0, 0, &sc->sc_fbhandle, &fb_paddr);
+ lv1_gpu_context_allocate(sc->sc_fbhandle, 0, &sc->sc_fbcontext,
+ &sc->sc_dma_control, &sc->sc_driver_info, &sc->sc_reports,
+ &sc->sc_reports_size);
+
+ lv1_gpu_context_attribute(sc->sc_fbcontext,
+ L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_FLIP, 0, 0, 0, 0);
+ lv1_gpu_context_attribute(sc->sc_fbcontext,
+ L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_FLIP, 1, 0, 0, 0);
+
+ sc->fb_info.fb_pbase = fb_paddr;
+ for (va = 0; va < sc->fb_info.fb_size; va += PAGE_SIZE)
+ pmap_kenter_attr(0x10000000 + va, fb_paddr + va,
+ VM_MEMATTR_WRITE_COMBINING);
+ sc->fb_info.fb_flags &= ~FB_FLAG_NOWRITE;
+}
+
+static int
+ps3fb_init(struct vt_device *vd)
+{
+ struct ps3fb_softc *sc;
+ char linux_video_mode[24];
+ int linux_video_mode_num = 0;
+
+ /* Init softc */
+ vd->vd_softc = sc = &ps3fb_softc;
+
+ sc->fb_info.fb_depth = 32;
+ sc->fb_info.fb_height = 1080;
+ sc->fb_info.fb_width = 1920;
+
+ /* See if the bootloader has passed a graphics mode to use */
+ bzero(linux_video_mode, sizeof(linux_video_mode));
+ TUNABLE_STR_FETCH("video", linux_video_mode, sizeof(linux_video_mode));
+ sscanf(linux_video_mode, "ps3fb:mode:%d", &linux_video_mode_num);
+
+ switch (linux_video_mode_num) {
+ case 1:
+ case 2:
+ sc->fb_info.fb_height = 480;
+ sc->fb_info.fb_width = 720;
+ break;
+ case 3:
+ case 8:
+ sc->fb_info.fb_height = 720;
+ sc->fb_info.fb_width = 1280;
+ break;
+ case 4:
+ case 5:
+ case 9:
+ case 10:
+ sc->fb_info.fb_height = 1080;
+ sc->fb_info.fb_width = 1920;
+ break;
+ case 6:
+ case 7:
+ sc->fb_info.fb_height = 576;
+ sc->fb_info.fb_width = 720;
+ break;
+ case 11:
+ sc->fb_info.fb_height = 768;
+ sc->fb_info.fb_width = 1024;
+ break;
+ case 12:
+ sc->fb_info.fb_height = 1024;
+ sc->fb_info.fb_width = 1280;
+ break;
+ case 13:
+ sc->fb_info.fb_height = 1200;
+ sc->fb_info.fb_width = 1920;
+ break;
+ }
+
+ /* Allow explicitly-specified values for us to override everything */
+ TUNABLE_INT_FETCH("hw.ps3fb.height", &sc->fb_info.fb_height);
+ TUNABLE_INT_FETCH("hw.ps3fb.width", &sc->fb_info.fb_width);
+
+ sc->fb_info.fb_stride = sc->fb_info.fb_width*4;
+ sc->fb_info.fb_size = sc->fb_info.fb_height * sc->fb_info.fb_stride;
+ sc->fb_info.fb_bpp = sc->fb_info.fb_stride / sc->fb_info.fb_width * 8;
+
+ /*
+ * Arbitrarily choose address for the framebuffer
+ */
+
+ sc->fb_info.fb_vbase = 0x10000000;
+ sc->fb_info.fb_flags |= FB_FLAG_NOWRITE; /* Not available yet */
+ sc->fb_info.fb_cmsize = 16;
+
+ /* 32-bit VGA palette */
+ vt_config_cons_colors(&sc->fb_info, COLOR_FORMAT_RGB,
+ 255, 16, 255, 8, 255, 0);
+
+ /* Set correct graphics context */
+ lv1_gpu_context_attribute(sc->sc_fbcontext,
+ L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_FLIP, 0, 0, 0, 0);
+ lv1_gpu_context_attribute(sc->sc_fbcontext,
+ L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_FLIP, 1, 0, 0, 0);
+
+ vt_fb_init(vd);
+
+ return (CN_INTERNAL);
+}
diff --git a/sys/powerpc/ps3/ps3bus.c b/sys/powerpc/ps3/ps3bus.c
new file mode 100644
index 000000000000..dc4026cb9a5e
--- /dev/null
+++ b/sys/powerpc/ps3/ps3bus.c
@@ -0,0 +1,780 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * 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
+ * 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 TOOLS GMBH 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/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/malloc.h>
+#include <sys/bus.h>
+#include <sys/clock.h>
+#include <sys/cpu.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/resource.h>
+#include <sys/rman.h>
+
+#include <vm/vm.h>
+#include <vm/pmap.h>
+
+#include <machine/bus.h>
+#include <machine/platform.h>
+#include <machine/resource.h>
+
+#include "ps3bus.h"
+#include "ps3-hvcall.h"
+#include "iommu_if.h"
+#include "clock_if.h"
+
+static void ps3bus_identify(driver_t *, device_t);
+static int ps3bus_probe(device_t);
+static int ps3bus_attach(device_t);
+static int ps3bus_print_child(device_t dev, device_t child);
+static int ps3bus_read_ivar(device_t bus, device_t child, int which,
+ uintptr_t *result);
+static struct rman *ps3bus_get_rman(device_t bus, int type, u_int flags);
+static struct resource *ps3bus_alloc_resource(device_t bus, device_t child,
+ int type, int *rid, rman_res_t start, rman_res_t end,
+ rman_res_t count, u_int flags);
+static int ps3bus_map_resource(device_t bus, device_t child,
+ struct resource *r, struct resource_map_request *argsp,
+ struct resource_map *map);
+static int ps3bus_unmap_resource(device_t bus, device_t child,
+ struct resource *r, struct resource_map *map);
+static bus_dma_tag_t ps3bus_get_dma_tag(device_t dev, device_t child);
+static int ps3_iommu_map(device_t dev, bus_dma_segment_t *segs, int *nsegs,
+ bus_addr_t min, bus_addr_t max, bus_size_t alignment,
+ bus_addr_t boundary, void *cookie);
+static int ps3_iommu_unmap(device_t dev, bus_dma_segment_t *segs,
+ int nsegs, void *cookie);
+static int ps3_gettime(device_t dev, struct timespec *ts);
+static int ps3_settime(device_t dev, struct timespec *ts);
+
+struct ps3bus_devinfo {
+ int bus;
+ int dev;
+ uint64_t bustype;
+ uint64_t devtype;
+ int busidx;
+ int devidx;
+
+ struct resource_list resources;
+ bus_dma_tag_t dma_tag;
+
+ struct mtx iommu_mtx;
+ bus_addr_t dma_base[4];
+};
+
+static MALLOC_DEFINE(M_PS3BUS, "ps3bus", "PS3 system bus device information");
+
+enum ps3bus_irq_type {
+ SB_IRQ = 2,
+ OHCI_IRQ = 3,
+ 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),
+ DEVMETHOD(device_probe, ps3bus_probe),
+ DEVMETHOD(device_attach, ps3bus_attach),
+
+ /* Bus interface */
+ DEVMETHOD(bus_add_child, bus_generic_add_child),
+ DEVMETHOD(bus_get_dma_tag, ps3bus_get_dma_tag),
+ DEVMETHOD(bus_print_child, ps3bus_print_child),
+ DEVMETHOD(bus_read_ivar, ps3bus_read_ivar),
+ DEVMETHOD(bus_get_rman, ps3bus_get_rman),
+ DEVMETHOD(bus_alloc_resource, ps3bus_alloc_resource),
+ DEVMETHOD(bus_adjust_resource, bus_generic_rman_adjust_resource),
+ DEVMETHOD(bus_activate_resource, bus_generic_rman_activate_resource),
+ DEVMETHOD(bus_deactivate_resource, bus_generic_rman_deactivate_resource),
+ DEVMETHOD(bus_map_resource, ps3bus_map_resource),
+ DEVMETHOD(bus_unmap_resource, ps3bus_unmap_resource),
+ DEVMETHOD(bus_release_resource, bus_generic_rman_release_resource),
+ DEVMETHOD(bus_setup_intr, bus_generic_setup_intr),
+ DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr),
+
+ /* IOMMU interface */
+ DEVMETHOD(iommu_map, ps3_iommu_map),
+ DEVMETHOD(iommu_unmap, ps3_iommu_unmap),
+
+ /* Clock interface */
+ DEVMETHOD(clock_gettime, ps3_gettime),
+ DEVMETHOD(clock_settime, ps3_settime),
+
+ DEVMETHOD_END
+};
+
+struct ps3bus_softc {
+ struct rman sc_mem_rman;
+ struct rman sc_intr_rman;
+ struct mem_region *regions;
+ int rcount;
+};
+
+static driver_t ps3bus_driver = {
+ "ps3bus",
+ ps3bus_methods,
+ sizeof(struct ps3bus_softc)
+};
+
+DRIVER_MODULE(ps3bus, nexus, ps3bus_driver, 0, 0);
+
+static void
+ps3bus_identify(driver_t *driver, device_t parent)
+{
+ if (strcmp(installed_platform(), "ps3") != 0)
+ return;
+
+ if (device_find_child(parent, "ps3bus", DEVICE_UNIT_ANY) == NULL)
+ BUS_ADD_CHILD(parent, 0, "ps3bus", 0);
+}
+
+static int
+ps3bus_probe(device_t dev)
+{
+ /* Do not attach to any OF nodes that may be present */
+
+ device_set_desc(dev, "Playstation 3 System Bus");
+
+ return (BUS_PROBE_NOWILDCARD);
+}
+
+static void
+ps3bus_resources_init(struct rman *rm, int bus_index, int dev_index,
+ 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;
+
+ switch (irq_type) {
+ case SB_IRQ:
+ lv1_construct_event_receive_port(&outlet);
+ lv1_connect_irq_plug_ext(ppe, thread, outlet, outlet,
+ 0);
+ lv1_connect_interrupt_event_receive_port(dinfo->bus,
+ dinfo->dev, outlet, irq);
+ break;
+ case OHCI_IRQ:
+ case EHCI_IRQ:
+ lv1_construct_io_irq_outlet(irq, &outlet);
+ lv1_connect_irq_plug_ext(ppe, thread, outlet, outlet,
+ 0);
+ break;
+ default:
+ printf("Unknown IRQ type %ld for device %d.%d\n",
+ irq_type, dinfo->bus, dinfo->dev);
+ break;
+ }
+
+ 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;
+
+ 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);
+ continue;
+ }
+
+ rman_manage_region(rm, paddr, paddr + len - 1);
+ resource_list_add(&dinfo->resources, SYS_RES_MEMORY, i,
+ paddr, paddr + len, len);
+ }
+}
+
+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)
+{
+ struct ps3bus_softc *sc;
+ struct ps3bus_devinfo *dinfo;
+ int bus_index, dev_index, result;
+ uint64_t bustype, bus, devs;
+ uint64_t dev, devtype;
+ uint64_t junk;
+ device_t cdev;
+
+ sc = device_get_softc(self);
+ sc->sc_mem_rman.rm_type = RMAN_ARRAY;
+ sc->sc_mem_rman.rm_descr = "PS3Bus Memory Mapped I/O";
+ sc->sc_intr_rman.rm_type = RMAN_ARRAY;
+ sc->sc_intr_rman.rm_descr = "PS3Bus Interrupts";
+ rman_init(&sc->sc_mem_rman);
+ rman_init(&sc->sc_intr_rman);
+ rman_manage_region(&sc->sc_intr_rman, 0, ~0);
+
+ /* Get memory regions for DMA */
+ mem_regions(&sc->regions, &sc->rcount, NULL, NULL);
+
+ /*
+ * Probe all the PS3's buses.
+ */
+
+ for (bus_index = 0; bus_index < 5; bus_index++) {
+ result = lv1_get_repository_node_value(PS3_LPAR_ID_PME,
+ (lv1_repository_string("bus") >> 32) | bus_index,
+ lv1_repository_string("type"), 0, 0, &bustype, &junk);
+
+ if (result != 0)
+ continue;
+
+ result = lv1_get_repository_node_value(PS3_LPAR_ID_PME,
+ (lv1_repository_string("bus") >> 32) | bus_index,
+ lv1_repository_string("id"), 0, 0, &bus, &junk);
+
+ if (result != 0)
+ continue;
+
+ result = lv1_get_repository_node_value(PS3_LPAR_ID_PME,
+ (lv1_repository_string("bus") >> 32) | bus_index,
+ lv1_repository_string("num_dev"), 0, 0, &devs, &junk);
+
+ for (dev_index = 0; dev_index < devs; dev_index++) {
+ 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("type"), 0, &devtype, &junk);
+
+ if (result != 0)
+ 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("id"), 0, &dev, &junk);
+
+ if (result != 0)
+ continue;
+
+ switch (devtype) {
+ case PS3_DEVTYPE_USB:
+ /* USB device has OHCI and EHCI USB host controllers */
+
+ lv1_open_device(bus, dev, 0);
+
+ /* 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", DEVICE_UNIT_ANY);
+ 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", DEVICE_UNIT_ANY);
+ 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, DEVICE_UNIT_ANY);
+ 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);
+ }
+ }
+ }
+
+ clock_register(self, 1000);
+
+ bus_attach_children(self);
+ return (0);
+}
+
+static int
+ps3bus_print_child(device_t dev, device_t child)
+{
+ struct ps3bus_devinfo *dinfo = device_get_ivars(child);
+ int retval = 0;
+
+ retval += bus_print_child_header(dev, child);
+ retval += resource_list_print_type(&dinfo->resources, "mem",
+ SYS_RES_MEMORY, "%#jx");
+ retval += resource_list_print_type(&dinfo->resources, "irq",
+ SYS_RES_IRQ, "%jd");
+
+ retval += bus_print_child_footer(dev, child);
+
+ return (retval);
+}
+
+static int
+ps3bus_read_ivar(device_t bus, device_t child, int which, uintptr_t *result)
+{
+ struct ps3bus_devinfo *dinfo = device_get_ivars(child);
+
+ switch (which) {
+ case PS3BUS_IVAR_BUS:
+ *result = dinfo->bus;
+ break;
+ case PS3BUS_IVAR_DEVICE:
+ *result = dinfo->dev;
+ break;
+ case PS3BUS_IVAR_BUSTYPE:
+ *result = dinfo->bustype;
+ break;
+ 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);
+ }
+
+ return (0);
+}
+
+static struct rman *
+ps3bus_get_rman(device_t bus, int type, u_int flags)
+{
+ struct ps3bus_softc *sc;
+
+ sc = device_get_softc(bus);
+ switch (type) {
+ case SYS_RES_MEMORY:
+ return (&sc->sc_mem_rman);
+ case SYS_RES_IRQ:
+ return (&sc->sc_intr_rman);
+ default:
+ return (NULL);
+ }
+}
+
+static struct resource *
+ps3bus_alloc_resource(device_t bus, device_t child, int type, int *rid,
+ rman_res_t start, rman_res_t end, rman_res_t count, u_int flags)
+{
+ struct ps3bus_devinfo *dinfo;
+ rman_res_t adjstart, adjend, adjcount;
+ struct resource_list_entry *rle;
+
+ dinfo = device_get_ivars(child);
+
+ switch (type) {
+ case SYS_RES_MEMORY:
+ rle = resource_list_find(&dinfo->resources, SYS_RES_MEMORY,
+ *rid);
+ if (rle == NULL) {
+ device_printf(bus, "no rle for %s memory %d\n",
+ device_get_nameunit(child), *rid);
+ return (NULL);
+ }
+
+ if (start < rle->start)
+ adjstart = rle->start;
+ else if (start > rle->end)
+ adjstart = rle->end;
+ else
+ adjstart = start;
+
+ if (end < rle->start)
+ adjend = rle->start;
+ else if (end > rle->end)
+ adjend = rle->end;
+ else
+ adjend = end;
+
+ adjcount = adjend - adjstart;
+ break;
+ case SYS_RES_IRQ:
+ rle = resource_list_find(&dinfo->resources, SYS_RES_IRQ,
+ *rid);
+ adjstart = rle->start;
+ adjcount = ulmax(count, rle->count);
+ adjend = ulmax(rle->end, rle->start + adjcount - 1);
+ break;
+ default:
+ device_printf(bus, "unknown resource request from %s\n",
+ device_get_nameunit(child));
+ return (NULL);
+ }
+
+ return (bus_generic_rman_alloc_resource(bus, child, type, rid, adjstart,
+ adjend, adjcount, flags));
+}
+
+static int
+ps3bus_map_resource(device_t bus, device_t child, struct resource *r,
+ struct resource_map_request *argsp, struct resource_map *map)
+{
+ struct resource_map_request args;
+ rman_res_t length, start;
+ int error;
+
+ /* Resources must be active to be mapped. */
+ if (!(rman_get_flags(r) & RF_ACTIVE))
+ return (ENXIO);
+
+ /* Mappings are only supported on memory resources. */
+ switch (rman_get_type(r)) {
+ case SYS_RES_MEMORY:
+ break;
+ default:
+ return (EINVAL);
+ }
+
+ resource_init_map_request(&args);
+ error = resource_validate_map_request(r, argsp, &args, &start, &length);
+ if (error)
+ return (error);
+
+ if (bootverbose)
+ printf("ps3 mapdev: start %jx, len %jd\n", start, length);
+
+ map->r_vaddr = pmap_mapdev_attr(start, length, args.memattr);
+ if (map->r_vaddr == NULL)
+ return (ENOMEM);
+ map->r_bustag = &bs_be_tag;
+ map->r_bushandle = (vm_offset_t)map->r_vaddr;
+ map->r_size = length;
+ return (0);
+}
+
+static int
+ps3bus_unmap_resource(device_t bus, device_t child, struct resource *r,
+ struct resource_map *map)
+{
+
+ switch (rman_get_type(r)) {
+ case SYS_RES_MEMORY:
+ pmap_unmapdev(map->r_vaddr, map->r_size);
+ return (0);
+ default:
+ return (EINVAL);
+ }
+}
+
+static bus_dma_tag_t
+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, pagesize;
+
+ if (dinfo->bustype != PS3_BUSTYPE_SYSBUS &&
+ dinfo->bustype != PS3_BUSTYPE_STORAGE)
+ return (bus_get_dma_tag(dev));
+
+ mtx_lock(&dinfo->iommu_mtx);
+ if (dinfo->dma_tag != NULL) {
+ mtx_unlock(&dinfo->iommu_mtx);
+ return (dinfo->dma_tag);
+ }
+
+ flags = 0; /* 32-bit mode */
+ if (dinfo->bustype == PS3_BUSTYPE_SYSBUS &&
+ 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, pagesize, flags,
+ &dinfo->dma_base[i]);
+ if (err != 0) {
+ device_printf(child,
+ "could not allocate DMA region %d: %d\n", i, err);
+ goto fail;
+ }
+
+ err = lv1_map_device_dma_region(dinfo->bus, dinfo->dev,
+ sc->regions[i].mr_start, dinfo->dma_base[i],
+ sc->regions[i].mr_size,
+ 0xf800000000000800UL /* Cell Handbook Figure 7.3.4.1 */);
+ if (err != 0) {
+ device_printf(child,
+ "could not map DMA region %d: %d\n", i, err);
+ goto fail;
+ }
+ }
+
+ err = bus_dma_tag_create(bus_get_dma_tag(dev),
+ 1, 0, BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR,
+ NULL, NULL, BUS_SPACE_MAXSIZE, 0, BUS_SPACE_MAXSIZE,
+ 0, NULL, NULL, &dinfo->dma_tag);
+
+ /*
+ * 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);
+
+ if (err)
+ return (NULL);
+
+ return (dinfo->dma_tag);
+}
+
+static int
+ps3_iommu_map(device_t dev, bus_dma_segment_t *segs, int *nsegs,
+ bus_addr_t min, bus_addr_t max, bus_size_t alignment, bus_addr_t boundary,
+ void *cookie)
+{
+ struct ps3bus_devinfo *dinfo = cookie;
+ struct ps3bus_softc *sc = device_get_softc(dev);
+ int i, j;
+
+ for (i = 0; i < *nsegs; i++) {
+ for (j = 0; j < sc->rcount; j++) {
+ if (segs[i].ds_addr >= sc->regions[j].mr_start &&
+ segs[i].ds_addr < sc->regions[j].mr_start +
+ sc->regions[j].mr_size)
+ break;
+ }
+ KASSERT(j < sc->rcount,
+ ("Trying to map address %#lx not in physical memory",
+ segs[i].ds_addr));
+
+ segs[i].ds_addr = dinfo->dma_base[j] +
+ (segs[i].ds_addr - sc->regions[j].mr_start);
+ }
+
+ return (0);
+}
+
+static int
+ps3_iommu_unmap(device_t dev, bus_dma_segment_t *segs, int nsegs, void *cookie)
+{
+
+ return (0);
+}
+
+#define Y2K 946684800
+
+static int
+ps3_gettime(device_t dev, struct timespec *ts)
+{
+ uint64_t rtc, tb;
+ int result;
+
+ result = lv1_get_rtc(&rtc, &tb);
+ if (result)
+ return (result);
+
+ ts->tv_sec = rtc + Y2K;
+ ts->tv_nsec = 0;
+ return (0);
+}
+
+static int
+ps3_settime(device_t dev, struct timespec *ts)
+{
+ return (-1);
+}
diff --git a/sys/powerpc/ps3/ps3bus.h b/sys/powerpc/ps3/ps3bus.h
new file mode 100644
index 000000000000..386e54065b35
--- /dev/null
+++ b/sys/powerpc/ps3/ps3bus.h
@@ -0,0 +1,69 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (C) 2010 Nathan Whitehorn
+ * 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 TOOLS GMBH 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.
+ */
+
+#ifndef _POWERPC_PS3_PS3BUS_H
+#define _POWERPC_PS3_PS3BUS_H
+
+enum {
+ PS3BUS_IVAR_BUS,
+ PS3BUS_IVAR_DEVICE,
+ PS3BUS_IVAR_BUSTYPE,
+ PS3BUS_IVAR_DEVTYPE,
+ PS3BUS_IVAR_BUSIDX,
+ PS3BUS_IVAR_DEVIDX,
+};
+
+#define PS3BUS_ACCESSOR(A, B, T) \
+ __BUS_ACCESSOR(ps3bus, A, PS3BUS, B, T)
+
+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 {
+ PS3_BUSTYPE_SYSBUS = 4,
+ PS3_BUSTYPE_STORAGE = 5,
+};
+
+/* Device types */
+enum {
+ /* System bus devices */
+ PS3_DEVTYPE_GELIC = 3,
+ PS3_DEVTYPE_USB = 4,
+ PS3_DEVTYPE_GPIO = 6,
+
+ /* Storage bus devices */
+ PS3_DEVTYPE_DISK = 0,
+ PS3_DEVTYPE_CDROM = 5,
+ PS3_DEVTYPE_FLASH = 14,
+};
+
+#endif /* _POWERPC_PS3_PS3BUS_H */
diff --git a/sys/powerpc/ps3/ps3cdrom.c b/sys/powerpc/ps3/ps3cdrom.c
new file mode 100644
index 000000000000..f40cb8710d5a
--- /dev/null
+++ b/sys/powerpc/ps3/ps3cdrom.c
@@ -0,0 +1,706 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * 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
+ * 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,
+ * without modification, immediately at the beginning of the file.
+ * 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/param.h>
+#include <sys/module.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/ata.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/kthread.h>
+#include <sys/lock.h>
+#include <sys/malloc.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/resource.h>
+#include <sys/bus.h>
+#include <sys/rman.h>
+
+#include <cam/cam.h>
+#include <cam/cam_ccb.h>
+#include <cam/cam_sim.h>
+#include <cam/cam_xpt_sim.h>
+#include <cam/cam_debug.h>
+#include <cam/scsi/scsi_all.h>
+
+#include "ps3bus.h"
+#include "ps3-hvcall.h"
+
+#define PS3CDROM_LOCK_INIT(_sc) \
+ mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->sc_dev), "ps3cdrom", \
+ MTX_DEF)
+#define PS3CDROM_LOCK_DESTROY(_sc) mtx_destroy(&_sc->sc_mtx);
+#define PS3CDROM_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx)
+#define PS3CDROM_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx)
+#define PS3CDROM_ASSERT_LOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_OWNED);
+#define PS3CDROM_ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_NOTOWNED);
+
+#define PS3CDROM_MAX_XFERS 3
+
+#define LV1_STORAGE_SEND_ATAPI_COMMAND 0x01
+
+struct ps3cdrom_softc;
+
+struct ps3cdrom_xfer {
+ TAILQ_ENTRY(ps3cdrom_xfer) x_queue;
+ struct ps3cdrom_softc *x_sc;
+ union ccb *x_ccb;
+ bus_dmamap_t x_dmamap;
+ uint64_t x_tag;
+};
+
+TAILQ_HEAD(ps3cdrom_xferq, ps3cdrom_xfer);
+
+struct ps3cdrom_softc {
+ device_t sc_dev;
+
+ struct mtx sc_mtx;
+
+ uint64_t sc_blksize;
+ uint64_t sc_nblocks;
+
+ int sc_irqid;
+ struct resource *sc_irq;
+ void *sc_irqctx;
+
+ bus_dma_tag_t sc_dmatag;
+
+ struct cam_sim *sc_sim;
+ struct cam_path *sc_path;
+
+ struct ps3cdrom_xfer sc_xfer[PS3CDROM_MAX_XFERS];
+ struct ps3cdrom_xferq sc_active_xferq;
+ struct ps3cdrom_xferq sc_free_xferq;
+};
+
+enum lv1_ata_proto {
+ NON_DATA_PROTO = 0x00,
+ PIO_DATA_IN_PROTO = 0x01,
+ PIO_DATA_OUT_PROTO = 0x02,
+ DMA_PROTO = 0x03
+};
+
+enum lv1_ata_in_out {
+ DIR_WRITE = 0x00,
+ DIR_READ = 0x01
+};
+
+struct lv1_atapi_cmd {
+ uint8_t pkt[32];
+ uint32_t pktlen;
+ uint32_t nblocks;
+ uint32_t blksize;
+ uint32_t proto; /* enum lv1_ata_proto */
+ uint32_t in_out; /* enum lv1_ata_in_out */
+ uint64_t buf;
+ uint32_t arglen;
+};
+
+static void ps3cdrom_action(struct cam_sim *sim, union ccb *ccb);
+static void ps3cdrom_poll(struct cam_sim *sim);
+static void ps3cdrom_async(void *callback_arg, u_int32_t code,
+ struct cam_path* path, void *arg);
+
+static void ps3cdrom_intr(void *arg);
+
+static void ps3cdrom_transfer(void *arg, bus_dma_segment_t *segs, int nsegs,
+ int error);
+
+static int ps3cdrom_decode_lv1_status(uint64_t status,
+ u_int8_t *sense_key, u_int8_t *asc, u_int8_t *ascq);
+
+static int
+ps3cdrom_probe(device_t dev)
+{
+ if (ps3bus_get_bustype(dev) != PS3_BUSTYPE_STORAGE ||
+ ps3bus_get_devtype(dev) != PS3_DEVTYPE_CDROM)
+ return (ENXIO);
+
+ device_set_desc(dev, "Playstation 3 CDROM");
+
+ return (BUS_PROBE_SPECIFIC);
+}
+
+static int
+ps3cdrom_attach(device_t dev)
+{
+ struct ps3cdrom_softc *sc = device_get_softc(dev);
+ struct cam_devq *devq;
+ struct ps3cdrom_xfer *xp;
+ struct ccb_setasync csa;
+ int i, err;
+
+ sc->sc_dev = dev;
+
+ PS3CDROM_LOCK_INIT(sc);
+
+ /* 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_destroy_lock;
+ }
+
+ err = bus_setup_intr(dev, sc->sc_irq,
+ INTR_TYPE_CAM | INTR_MPSAFE | INTR_ENTROPY,
+ NULL, ps3cdrom_intr, sc, &sc->sc_irqctx);
+ if (err) {
+ device_printf(dev, "Could not setup IRQ\n");
+ err = ENXIO;
+ goto fail_release_intr;
+ }
+
+ /* Setup DMA */
+
+ err = bus_dma_tag_create(bus_get_dma_tag(dev), 4096, 0,
+ BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL,
+ BUS_SPACE_UNRESTRICTED, 1, PAGE_SIZE, 0,
+ busdma_lock_mutex, &sc->sc_mtx, &sc->sc_dmatag);
+ if (err) {
+ device_printf(dev, "Could not create DMA tag\n");
+ err = ENXIO;
+ goto fail_teardown_intr;
+ }
+
+ /* Setup transfer queues */
+
+ TAILQ_INIT(&sc->sc_active_xferq);
+ TAILQ_INIT(&sc->sc_free_xferq);
+
+ for (i = 0; i < PS3CDROM_MAX_XFERS; i++) {
+ xp = &sc->sc_xfer[i];
+ xp->x_sc = sc;
+
+ err = bus_dmamap_create(sc->sc_dmatag, BUS_DMA_COHERENT,
+ &xp->x_dmamap);
+ if (err) {
+ device_printf(dev, "Could not create DMA map (%d)\n",
+ err);
+ goto fail_destroy_dmamap;
+ }
+
+ TAILQ_INSERT_TAIL(&sc->sc_free_xferq, xp, x_queue);
+ }
+
+ /* Setup CAM */
+
+ devq = cam_simq_alloc(PS3CDROM_MAX_XFERS - 1);
+ if (!devq) {
+ device_printf(dev, "Could not allocate SIM queue\n");
+ err = ENOMEM;
+ goto fail_destroy_dmatag;
+ }
+
+ sc->sc_sim = cam_sim_alloc(ps3cdrom_action, ps3cdrom_poll, "ps3cdrom",
+ sc, device_get_unit(dev), &sc->sc_mtx, PS3CDROM_MAX_XFERS - 1, 0,
+ devq);
+ if (!sc->sc_sim) {
+ device_printf(dev, "Could not allocate SIM\n");
+ cam_simq_free(devq);
+ err = ENOMEM;
+ goto fail_destroy_dmatag;
+ }
+
+ /* Setup XPT */
+
+ PS3CDROM_LOCK(sc);
+
+ err = xpt_bus_register(sc->sc_sim, dev, 0);
+ if (err != CAM_SUCCESS) {
+ device_printf(dev, "Could not register XPT bus\n");
+ err = ENXIO;
+ PS3CDROM_UNLOCK(sc);
+ goto fail_free_sim;
+ }
+
+ err = xpt_create_path(&sc->sc_path, NULL, cam_sim_path(sc->sc_sim),
+ CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD);
+ if (err != CAM_REQ_CMP) {
+ device_printf(dev, "Could not create XPT path\n");
+ err = ENOMEM;
+ PS3CDROM_UNLOCK(sc);
+ goto fail_unregister_xpt_bus;
+ }
+
+ memset(&csa, 0, sizeof(csa));
+ xpt_setup_ccb(&csa.ccb_h, sc->sc_path, 5);
+ csa.ccb_h.func_code = XPT_SASYNC_CB;
+ csa.event_enable = AC_LOST_DEVICE;
+ csa.callback = ps3cdrom_async;
+ csa.callback_arg = sc->sc_sim;
+ xpt_action((union ccb *) &csa);
+
+ CAM_DEBUG(sc->sc_path, CAM_DEBUG_TRACE,
+ ("registered SIM for ps3cdrom%d\n", device_get_unit(dev)));
+
+ PS3CDROM_UNLOCK(sc);
+
+ return (BUS_PROBE_SPECIFIC);
+
+fail_unregister_xpt_bus:
+
+ xpt_bus_deregister(cam_sim_path(sc->sc_sim));
+
+fail_free_sim:
+
+ cam_sim_free(sc->sc_sim, TRUE);
+
+fail_destroy_dmamap:
+
+ while ((xp = TAILQ_FIRST(&sc->sc_free_xferq))) {
+ TAILQ_REMOVE(&sc->sc_free_xferq, xp, x_queue);
+ bus_dmamap_destroy(sc->sc_dmatag, xp->x_dmamap);
+ }
+
+fail_destroy_dmatag:
+
+ bus_dma_tag_destroy(sc->sc_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_destroy_lock:
+
+ PS3CDROM_LOCK_DESTROY(sc);
+
+ return (err);
+}
+
+static int
+ps3cdrom_detach(device_t dev)
+{
+ struct ps3cdrom_softc *sc = device_get_softc(dev);
+ int i;
+
+ xpt_async(AC_LOST_DEVICE, sc->sc_path, NULL);
+ xpt_free_path(sc->sc_path);
+ xpt_bus_deregister(cam_sim_path(sc->sc_sim));
+ cam_sim_free(sc->sc_sim, TRUE);
+
+ for (i = 0; i < PS3CDROM_MAX_XFERS; i++)
+ bus_dmamap_destroy(sc->sc_dmatag, sc->sc_xfer[i].x_dmamap);
+
+ bus_dma_tag_destroy(sc->sc_dmatag);
+
+ bus_teardown_intr(dev, sc->sc_irq, sc->sc_irqctx);
+ bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irqid, sc->sc_irq);
+
+ PS3CDROM_LOCK_DESTROY(sc);
+
+ return (0);
+}
+
+static void
+ps3cdrom_action(struct cam_sim *sim, union ccb *ccb)
+{
+ struct ps3cdrom_softc *sc = (struct ps3cdrom_softc *)cam_sim_softc(sim);
+ device_t dev = sc->sc_dev;
+ struct ps3cdrom_xfer *xp;
+ int err;
+
+ PS3CDROM_ASSERT_LOCKED(sc);
+
+ CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_TRACE,
+ ("function code 0x%02x\n", ccb->ccb_h.func_code));
+
+ switch (ccb->ccb_h.func_code) {
+ case XPT_SCSI_IO:
+ if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_INPROG)
+ break;
+
+ if(ccb->ccb_h.target_id > 0) {
+ ccb->ccb_h.status = CAM_TID_INVALID;
+ break;
+ }
+
+ if(ccb->ccb_h.target_lun > 0) {
+ ccb->ccb_h.status = CAM_LUN_INVALID;
+ break;
+ }
+
+ xp = TAILQ_FIRST(&sc->sc_free_xferq);
+
+ KASSERT(xp != NULL, ("no free transfers"));
+
+ xp->x_ccb = ccb;
+
+ TAILQ_REMOVE(&sc->sc_free_xferq, xp, x_queue);
+
+ err = bus_dmamap_load_ccb(sc->sc_dmatag, xp->x_dmamap,
+ ccb, ps3cdrom_transfer, xp, 0);
+ if (err && err != EINPROGRESS) {
+ device_printf(dev, "Could not load DMA map (%d)\n",
+ err);
+
+ xp->x_ccb = NULL;
+ TAILQ_INSERT_TAIL(&sc->sc_free_xferq, xp, x_queue);
+ ccb->ccb_h.status = CAM_SCSI_STATUS_ERROR;
+ break;
+ }
+ return;
+ case XPT_SET_TRAN_SETTINGS:
+ ccb->ccb_h.status = CAM_FUNC_NOTAVAIL;
+ break;
+ case XPT_GET_TRAN_SETTINGS:
+ {
+ struct ccb_trans_settings *cts = &ccb->cts;
+
+ cts->protocol = PROTO_SCSI;
+ cts->protocol_version = SCSI_REV_2;
+ cts->transport = XPORT_SPI;
+ cts->transport_version = 2;
+ cts->proto_specific.valid = 0;
+ cts->xport_specific.valid = 0;
+ ccb->ccb_h.status = CAM_REQ_CMP;
+ break;
+ }
+ case XPT_RESET_BUS:
+ case XPT_RESET_DEV:
+ ccb->ccb_h.status = CAM_REQ_CMP;
+ break;
+ case XPT_CALC_GEOMETRY:
+ cam_calc_geometry(&ccb->ccg, 1);
+ break;
+ case XPT_PATH_INQ:
+ {
+ struct ccb_pathinq *cpi = &ccb->cpi;
+
+ cpi->version_num = 1;
+ cpi->hba_inquiry = 0;
+ cpi->target_sprt = 0;
+ cpi->hba_inquiry = PI_SDTR_ABLE;
+ cpi->hba_misc = PIM_NOBUSRESET | PIM_SEQSCAN | PIM_NO_6_BYTE;
+ cpi->hba_eng_cnt = 0;
+ bzero(cpi->vuhba_flags, sizeof(cpi->vuhba_flags));
+ cpi->max_target = 0;
+ cpi->max_lun = 0;
+ cpi->initiator_id = 7;
+ cpi->bus_id = cam_sim_bus(sim);
+ cpi->unit_number = cam_sim_unit(sim);
+ cpi->base_transfer_speed = 150000;
+ strlcpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN);
+ strlcpy(cpi->hba_vid, "Sony", HBA_IDLEN);
+ strlcpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN);
+ cpi->transport = XPORT_SPI;
+ cpi->transport_version = 2;
+ cpi->protocol = PROTO_SCSI;
+ cpi->protocol_version = SCSI_REV_2;
+ cpi->maxio = PAGE_SIZE;
+ cpi->ccb_h.status = CAM_REQ_CMP;
+ break;
+ }
+ default:
+ CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_TRACE,
+ ("unsupported function code 0x%02x\n",
+ ccb->ccb_h.func_code));
+ ccb->ccb_h.status = CAM_REQ_INVALID;
+ break;
+ }
+
+ xpt_done(ccb);
+}
+
+static void
+ps3cdrom_poll(struct cam_sim *sim)
+{
+ ps3cdrom_intr(cam_sim_softc(sim));
+}
+
+static void
+ps3cdrom_async(void *callback_arg, u_int32_t code,
+ struct cam_path* path, void *arg)
+{
+ switch (code) {
+ case AC_LOST_DEVICE:
+ xpt_print_path(path);
+ break;
+ default:
+ break;
+ }
+}
+
+static void
+ps3cdrom_intr(void *arg)
+{
+ struct ps3cdrom_softc *sc = (struct ps3cdrom_softc *) arg;
+ device_t dev = sc->sc_dev;
+ uint64_t devid = ps3bus_get_device(dev);
+ struct ps3cdrom_xfer *xp;
+ union ccb *ccb;
+ u_int8_t *cdb, sense_key, asc, ascq;
+ uint64_t tag, status;
+
+ if (lv1_storage_get_async_status(devid, &tag, &status) != 0)
+ return;
+
+ PS3CDROM_LOCK(sc);
+
+ /* Find transfer with the returned tag */
+
+ TAILQ_FOREACH(xp, &sc->sc_active_xferq, x_queue) {
+ if (xp->x_tag == tag)
+ break;
+ }
+
+ if (xp) {
+ ccb = xp->x_ccb;
+ cdb = (ccb->ccb_h.flags & CAM_CDB_POINTER) ?
+ ccb->csio.cdb_io.cdb_ptr :
+ ccb->csio.cdb_io.cdb_bytes;
+
+ CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_TRACE,
+ ("ATAPI command 0x%02x tag 0x%016lx completed (0x%016lx)\n",
+ cdb[0], tag, status));
+
+ if (!status) {
+ ccb->csio.scsi_status = SCSI_STATUS_OK;
+ ccb->csio.resid = 0;
+ ccb->ccb_h.status = CAM_REQ_CMP;
+ } else {
+ ccb->csio.scsi_status = SCSI_STATUS_CHECK_COND;
+ ccb->ccb_h.status = CAM_SCSI_STATUS_ERROR;
+
+ if (!ps3cdrom_decode_lv1_status(status, &sense_key,
+ &asc, &ascq)) {
+ CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_TRACE,
+ ("sense key 0x%02x asc 0x%02x ascq 0x%02x\n",
+ sense_key, asc, ascq));
+
+ scsi_set_sense_data(&ccb->csio.sense_data,
+ /*sense_format*/ SSD_TYPE_NONE,
+ /*current_error*/ 1,
+ sense_key,
+ asc,
+ ascq,
+ SSD_ELEM_NONE);
+ ccb->csio.sense_len = SSD_FULL_SIZE;
+ ccb->ccb_h.status = CAM_SCSI_STATUS_ERROR |
+ CAM_AUTOSNS_VALID;
+ }
+
+ if ((ccb->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE)
+ ccb->csio.resid = ccb->csio.dxfer_len;
+ }
+
+ if (ccb->ccb_h.flags & CAM_DIR_IN)
+ bus_dmamap_sync(sc->sc_dmatag, xp->x_dmamap,
+ BUS_DMASYNC_POSTREAD);
+
+ bus_dmamap_unload(sc->sc_dmatag, xp->x_dmamap);
+
+ xp->x_ccb = NULL;
+ TAILQ_REMOVE(&sc->sc_active_xferq, xp, x_queue);
+ TAILQ_INSERT_TAIL(&sc->sc_free_xferq, xp, x_queue);
+
+ xpt_done(ccb);
+ } else {
+ device_printf(dev,
+ "Could not find transfer with tag 0x%016lx\n", tag);
+ }
+
+ PS3CDROM_UNLOCK(sc);
+}
+
+static void
+ps3cdrom_transfer(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
+{
+ struct ps3cdrom_xfer *xp = (struct ps3cdrom_xfer *) arg;
+ struct ps3cdrom_softc *sc = xp->x_sc;
+ device_t dev = sc->sc_dev;
+ uint64_t devid = ps3bus_get_device(dev);
+ union ccb *ccb = xp->x_ccb;
+ u_int8_t *cdb;
+ uint64_t start_sector, block_count;
+ int err;
+
+ KASSERT(nsegs == 1 || nsegs == 0,
+ ("ps3cdrom_transfer: invalid number of DMA segments %d", nsegs));
+ KASSERT(error == 0, ("ps3cdrom_transfer: DMA error %d", error));
+
+ PS3CDROM_ASSERT_LOCKED(sc);
+
+ if (error) {
+ device_printf(dev, "Could not load DMA map (%d)\n", error);
+
+ xp->x_ccb = NULL;
+ TAILQ_INSERT_TAIL(&sc->sc_free_xferq, xp, x_queue);
+ ccb->ccb_h.status = CAM_SCSI_STATUS_ERROR;
+ xpt_done(ccb);
+ return;
+ }
+
+ cdb = (ccb->ccb_h.flags & CAM_CDB_POINTER) ?
+ ccb->csio.cdb_io.cdb_ptr :
+ ccb->csio.cdb_io.cdb_bytes;
+
+ CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_TRACE,
+ ("ATAPI command 0x%02x cdb_len %d dxfer_len %d\n ", cdb[0],
+ ccb->csio.cdb_len, ccb->csio.dxfer_len));
+
+ switch (cdb[0]) {
+ case READ_10:
+ KASSERT(nsegs == 1, ("ps3cdrom_transfer: no data to read"));
+ start_sector = (cdb[2] << 24) | (cdb[3] << 16) |
+ (cdb[4] << 8) | cdb[5];
+ block_count = (cdb[7] << 8) | cdb[8];
+
+ err = lv1_storage_read(devid, 0 /* region id */,
+ start_sector, block_count, 0 /* flags */, segs[0].ds_addr,
+ &xp->x_tag);
+ bus_dmamap_sync(sc->sc_dmatag, xp->x_dmamap,
+ BUS_DMASYNC_POSTREAD);
+ break;
+ case WRITE_10:
+ KASSERT(nsegs == 1, ("ps3cdrom_transfer: no data to write"));
+ start_sector = (cdb[2] << 24) | (cdb[3] << 16) |
+ (cdb[4] << 8) | cdb[5];
+ block_count = (cdb[7] << 8) | cdb[8];
+
+ bus_dmamap_sync(sc->sc_dmatag, xp->x_dmamap,
+ BUS_DMASYNC_PREWRITE);
+ err = lv1_storage_write(devid, 0 /* region id */,
+ start_sector, block_count, 0 /* flags */,
+ segs[0].ds_addr, &xp->x_tag);
+ break;
+ default:
+ {
+ struct lv1_atapi_cmd atapi_cmd;
+
+ bzero(&atapi_cmd, sizeof(atapi_cmd));
+ atapi_cmd.pktlen = 12;
+ bcopy(cdb, atapi_cmd.pkt, ccb->csio.cdb_len);
+
+ if (ccb->ccb_h.flags & CAM_DIR_IN) {
+ atapi_cmd.in_out = DIR_READ;
+ atapi_cmd.proto = (ccb->csio.dxfer_len >= 2048) ?
+ DMA_PROTO : PIO_DATA_IN_PROTO;
+ } else if (ccb->ccb_h.flags & CAM_DIR_OUT) {
+ atapi_cmd.in_out = DIR_WRITE;
+ atapi_cmd.proto = (ccb->csio.dxfer_len >= 2048) ?
+ DMA_PROTO : PIO_DATA_OUT_PROTO;
+ } else {
+ atapi_cmd.proto = NON_DATA_PROTO;
+ }
+
+ atapi_cmd.nblocks = atapi_cmd.arglen =
+ (nsegs == 0) ? 0 : segs[0].ds_len;
+ atapi_cmd.blksize = 1;
+ atapi_cmd.buf = (nsegs == 0) ? 0 : segs[0].ds_addr;
+
+ if (ccb->ccb_h.flags & CAM_DIR_OUT)
+ bus_dmamap_sync(sc->sc_dmatag, xp->x_dmamap,
+ BUS_DMASYNC_PREWRITE);
+
+ err = lv1_storage_send_device_command(devid,
+ LV1_STORAGE_SEND_ATAPI_COMMAND, vtophys(&atapi_cmd),
+ sizeof(atapi_cmd), atapi_cmd.buf, atapi_cmd.arglen,
+ &xp->x_tag);
+
+ break;
+ }
+ }
+
+ if (err) {
+ device_printf(dev, "ATAPI command 0x%02x failed (%d)\n",
+ cdb[0], err);
+
+ bus_dmamap_unload(sc->sc_dmatag, xp->x_dmamap);
+
+ xp->x_ccb = NULL;
+ TAILQ_INSERT_TAIL(&sc->sc_free_xferq, xp, x_queue);
+
+ bzero(&ccb->csio.sense_data, sizeof(ccb->csio.sense_data));
+ /* Invalid field in parameter list */
+ scsi_set_sense_data(&ccb->csio.sense_data,
+ /*sense_format*/ SSD_TYPE_NONE,
+ /*current_error*/ 1,
+ /*sense_key*/ SSD_KEY_ILLEGAL_REQUEST,
+ /*asc*/ 0x26,
+ /*ascq*/ 0x00,
+ SSD_ELEM_NONE);
+
+ ccb->csio.sense_len = SSD_FULL_SIZE;
+ ccb->csio.scsi_status = SCSI_STATUS_CHECK_COND;
+ ccb->ccb_h.status = CAM_SCSI_STATUS_ERROR | CAM_AUTOSNS_VALID;
+ xpt_done(ccb);
+ } else {
+ CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_TRACE,
+ ("ATAPI command 0x%02x tag 0x%016lx submitted\n ", cdb[0],
+ xp->x_tag));
+
+ TAILQ_INSERT_TAIL(&sc->sc_active_xferq, xp, x_queue);
+ ccb->ccb_h.status |= CAM_SIM_QUEUED;
+ }
+}
+
+static int
+ps3cdrom_decode_lv1_status(uint64_t status, u_int8_t *sense_key, u_int8_t *asc,
+ u_int8_t *ascq)
+{
+ if (((status >> 24) & 0xff) != SCSI_STATUS_CHECK_COND)
+ return -1;
+
+ *sense_key = (status >> 16) & 0xff;
+ *asc = (status >> 8) & 0xff;
+ *ascq = status & 0xff;
+
+ return (0);
+}
+
+static device_method_t ps3cdrom_methods[] = {
+ DEVMETHOD(device_probe, ps3cdrom_probe),
+ DEVMETHOD(device_attach, ps3cdrom_attach),
+ DEVMETHOD(device_detach, ps3cdrom_detach),
+ {0, 0},
+};
+
+static driver_t ps3cdrom_driver = {
+ "ps3cdrom",
+ ps3cdrom_methods,
+ sizeof(struct ps3cdrom_softc),
+};
+
+DRIVER_MODULE(ps3cdrom, ps3bus, ps3cdrom_driver, 0, 0);
+MODULE_DEPEND(ps3cdrom, cam, 1, 1, 1);
diff --git a/sys/powerpc/ps3/ps3disk.c b/sys/powerpc/ps3/ps3disk.c
new file mode 100644
index 000000000000..4d9976ecccee
--- /dev/null
+++ b/sys/powerpc/ps3/ps3disk.c
@@ -0,0 +1,712 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (C) 2011 glevand (geoffrey.levand@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/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/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
+
+static SYSCTL_NODE(_hw, OID_AUTO, ps3disk, CTLFLAG_RD | CTLFLAG_MPSAFE, 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 bio_queue_head sc_deferredq;
+ struct proc *sc_task;
+
+ bus_dma_tag_t sc_dmatag;
+
+ 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 void ps3disk_intr(void *arg);
+static int ps3disk_get_disk_geometry(struct ps3disk_softc *sc);
+static int ps3disk_enum_regions(struct ps3disk_softc *sc);
+static void ps3disk_transfer(void *arg, bus_dma_segment_t *segs, int nsegs,
+ int error);
+
+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;
+ uint64_t junk;
+ 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,
+ NULL, ps3disk_intr, sc, &sc->sc_irqctx);
+ if (err) {
+ device_printf(dev, "Could not setup IRQ\n");
+ err = ENXIO;
+ goto fail_release_intr;
+ }
+
+ /* Setup DMA */
+ err = bus_dma_tag_create(bus_get_dma_tag(dev), 4096, 0,
+ BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL,
+ BUS_SPACE_UNRESTRICTED, 1, PAGE_SIZE, 0,
+ busdma_lock_mutex, &sc->sc_mtx, &sc->sc_dmatag);
+ if (err) {
+ device_printf(dev, "Could not create DMA tag\n");
+ err = ENXIO;
+ goto fail_teardown_intr;
+ }
+
+ /* 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_teardown_intr;
+ }
+
+ for (i = 0; i < sc->sc_nregs; i++) {
+ struct ps3disk_region *rp = &sc->sc_reg[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 = PAGE_SIZE;
+ 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;
+ }
+
+ /* Test to see if we can read this region */
+ err = lv1_storage_read(ps3bus_get_device(dev), d->d_unit,
+ 0, 0, rp->r_flags, 0, &junk);
+ device_printf(dev, "region %d %ju%cB%s\n", i, mb, unit,
+ (err == LV1_DENIED_BY_POLICY) ? " (hypervisor protected)"
+ : "");
+
+ if (err != LV1_DENIED_BY_POLICY)
+ disk_create(d, DISK_VERSION);
+ }
+ err = 0;
+
+ bioq_init(&sc->sc_bioq);
+ bioq_init(&sc->sc_deferredq);
+ kproc_create(&ps3disk_task, sc, &sc->sc_task, 0, 0, "ps3disk");
+
+ ps3disk_sysctlattach(sc);
+ sc->sc_running = 1;
+ return (0);
+
+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;
+
+ for (i = 0; i < sc->sc_nregs; i++)
+ disk_destroy(sc->sc_disk[i]);
+
+ bus_dma_tag_destroy(sc->sc_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);
+}
+
+/* Process deferred blocks */
+static void
+ps3disk_task(void *arg)
+{
+ struct ps3disk_softc *sc = (struct ps3disk_softc *) arg;
+ struct bio *bp;
+
+ while (1) {
+ kproc_suspend_check(sc->sc_task);
+ tsleep(&sc->sc_deferredq, PRIBIO, "ps3disk", 10);
+
+ PS3DISK_LOCK(sc);
+ bp = bioq_takefirst(&sc->sc_deferredq);
+ PS3DISK_UNLOCK(sc);
+
+ if (bp == NULL)
+ continue;
+
+ if (bp->bio_driver1 != NULL) {
+ bus_dmamap_unload(sc->sc_dmatag, (bus_dmamap_t)
+ bp->bio_driver1);
+ bus_dmamap_destroy(sc->sc_dmatag, (bus_dmamap_t)
+ bp->bio_driver1);
+ }
+
+ ps3disk_strategy(bp);
+ }
+
+ kproc_exit(0);
+}
+
+static void
+ps3disk_strategy(struct bio *bp)
+{
+ struct ps3disk_softc *sc = (struct ps3disk_softc *)bp->bio_disk->d_drv1;
+ int err;
+
+ if (sc == NULL) {
+ bp->bio_flags |= BIO_ERROR;
+ bp->bio_error = EINVAL;
+ biodone(bp);
+ return;
+ }
+
+ PS3DISK_LOCK(sc);
+ bp->bio_resid = bp->bio_bcount;
+ bioq_insert_tail(&sc->sc_bioq, bp);
+
+ DPRINTF(sc, PS3DISK_DEBUG_TASK, "%s: bio_cmd 0x%02x\n",
+ __func__, bp->bio_cmd);
+
+ err = 0;
+ if (bp->bio_cmd == BIO_FLUSH) {
+ bp->bio_driver1 = 0;
+ err = lv1_storage_send_device_command(
+ ps3bus_get_device(sc->sc_dev), LV1_STORAGE_ATA_HDDOUT,
+ 0, 0, 0, 0, (uint64_t *)&bp->bio_driver2);
+ if (err == LV1_BUSY)
+ err = EAGAIN;
+ } else if (bp->bio_cmd == BIO_READ || bp->bio_cmd == BIO_WRITE) {
+ if (bp->bio_bcount % sc->sc_blksize != 0) {
+ err = EINVAL;
+ } else {
+ bus_dmamap_create(sc->sc_dmatag, BUS_DMA_COHERENT,
+ (bus_dmamap_t *)(&bp->bio_driver1));
+ err = bus_dmamap_load(sc->sc_dmatag,
+ (bus_dmamap_t)(bp->bio_driver1), bp->bio_data,
+ bp->bio_bcount, ps3disk_transfer, bp, 0);
+ if (err == EINPROGRESS)
+ err = 0;
+ }
+ } else {
+ err = EINVAL;
+ }
+
+ if (err == EAGAIN) {
+ bioq_remove(&sc->sc_bioq, bp);
+ bioq_insert_tail(&sc->sc_deferredq, bp);
+ } else if (err != 0) {
+ bp->bio_error = err;
+ bp->bio_flags |= BIO_ERROR;
+ bioq_remove(&sc->sc_bioq, bp);
+ disk_err(bp, "hard error", -1, 1);
+ biodone(bp);
+ }
+
+ PS3DISK_UNLOCK(sc);
+}
+
+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);
+ struct bio *bp;
+ uint64_t tag, status;
+
+ if (lv1_storage_get_async_status(devid, &tag, &status) != 0)
+ return;
+
+ PS3DISK_LOCK(sc);
+
+ DPRINTF(sc, PS3DISK_DEBUG_INTR, "%s: tag 0x%016lx "
+ "status 0x%016lx\n", __func__, tag, status);
+
+ /* Locate the matching request */
+ TAILQ_FOREACH(bp, &sc->sc_bioq.queue, bio_queue) {
+ if ((uint64_t)bp->bio_driver2 != tag)
+ continue;
+
+ if (status != 0) {
+ device_printf(sc->sc_dev, "%s error (%#lx)\n",
+ (bp->bio_cmd == BIO_READ) ? "Read" : "Write",
+ status);
+ bp->bio_error = EIO;
+ bp->bio_flags |= BIO_ERROR;
+ } else {
+ bp->bio_error = 0;
+ bp->bio_resid = 0;
+ bp->bio_flags |= BIO_DONE;
+ }
+
+ if (bp->bio_driver1 != NULL) {
+ if (bp->bio_cmd == BIO_READ)
+ bus_dmamap_sync(sc->sc_dmatag, (bus_dmamap_t)
+ bp->bio_driver1, BUS_DMASYNC_POSTREAD);
+ bus_dmamap_unload(sc->sc_dmatag, (bus_dmamap_t)
+ bp->bio_driver1);
+ bus_dmamap_destroy(sc->sc_dmatag, (bus_dmamap_t)
+ bp->bio_driver1);
+ }
+
+ bioq_remove(&sc->sc_bioq, bp);
+ biodone(bp);
+ break;
+ }
+
+ if (bioq_first(&sc->sc_deferredq) != NULL)
+ wakeup(&sc->sc_deferredq);
+
+ PS3DISK_UNLOCK(sc);
+}
+
+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);
+ return (ENXIO);
+ }
+
+ 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;
+ }
+
+ 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)
+ 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 void
+ps3disk_transfer(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
+{
+ struct bio *bp = (struct bio *)(arg);
+ struct ps3disk_softc *sc = (struct ps3disk_softc *)bp->bio_disk->d_drv1;
+ struct ps3disk_region *rp = &sc->sc_reg[bp->bio_disk->d_unit];
+ bus_dma_segment_t *seg = &segs[0];
+ uint64_t devid = ps3bus_get_device(sc->sc_dev);
+ uint64_t block, bio_length, sector_op_count;
+ int err;
+
+ /* Locks already held by busdma */
+ PS3DISK_ASSERT_LOCKED(sc);
+
+ if (error) {
+ bp->bio_error = error;
+ bp->bio_flags |= BIO_ERROR;
+ bioq_remove(&sc->sc_bioq, bp);
+ biodone(bp);
+ return;
+ }
+
+ /* supports only 1 segment */
+
+ KASSERT(nsegs == 1,
+ ("nsegs must be 1!, %d", nsegs));
+
+ block = bp->bio_pblkno;
+ bio_length = bp->bio_length;
+
+ /* ds_len always >= bio_length */
+
+ KASSERT((seg->ds_len % bio_length) == 0,
+ ("ds_len not bio_length multiples, %lu, %lu",
+ (uint64_t)seg->ds_len, bio_length));
+
+ KASSERT((bio_length % sc->sc_blksize) == 0,
+ ("bio_length not blocksize multiples, %lu, %lu",
+ bio_length, (uint64_t)sc->sc_blksize));
+
+ sector_op_count = bio_length / sc->sc_blksize;
+
+ if (bp->bio_cmd == BIO_READ) {
+ err = lv1_storage_read(devid, rp->r_id,
+ block, sector_op_count,
+ rp->r_flags, seg->ds_addr,
+ (uint64_t *)&bp->bio_driver2);
+ } else {
+ bus_dmamap_sync(sc->sc_dmatag,
+ (bus_dmamap_t)bp->bio_driver1,
+ BUS_DMASYNC_PREWRITE);
+
+ err = lv1_storage_write(devid, rp->r_id,
+ block, sector_op_count,
+ rp->r_flags, seg->ds_addr,
+ (uint64_t *)&bp->bio_driver2);
+ }
+
+ if (err) {
+ if (err == LV1_BUSY) {
+ bioq_remove(&sc->sc_bioq, bp);
+ bioq_insert_tail(&sc->sc_deferredq, bp);
+ } else {
+ bus_dmamap_unload(sc->sc_dmatag, (bus_dmamap_t)
+ bp->bio_driver1);
+ bus_dmamap_destroy(sc->sc_dmatag, (bus_dmamap_t)
+ bp->bio_driver1);
+ device_printf(sc->sc_dev, "Could not read "
+ "sectors (0x%08x)\n", err);
+ bp->bio_error = EINVAL;
+ bp->bio_flags |= BIO_ERROR;
+ bioq_remove(&sc->sc_bioq, bp);
+ biodone(bp);
+ }
+ }
+
+ DPRINTF(sc, PS3DISK_DEBUG_READ, "%s: tag 0x%016lx\n",
+ __func__, sc->sc_bounce_tag);
+}
+
+#ifdef PS3DISK_DEBUG
+static int
+ps3disk_sysctl_debug(SYSCTL_HANDLER_ARGS)
+{
+ struct ps3disk_softc *sc = arg1;
+ int debug, error;
+
+ debug = sc->sc_debug;
+
+ error = sysctl_handle_int(oidp, &debug, 0, req);
+ if (error || !req->newptr)
+ return error;
+
+ sc->sc_debug = debug;
+
+ return 0;
+}
+#endif
+
+static void
+ps3disk_sysctlattach(struct ps3disk_softc *sc)
+{
+#ifdef PS3DISK_DEBUG
+ struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->sc_dev);
+ struct sysctl_oid *tree = device_get_sysctl_tree(sc->sc_dev);
+
+ sc->sc_debug = ps3disk_debug;
+
+ SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+ "debug", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, sc, 0,
+ ps3disk_sysctl_debug, "I", "control debugging printfs");
+#endif
+}
+
+static device_method_t ps3disk_methods[] = {
+ DEVMETHOD(device_probe, ps3disk_probe),
+ DEVMETHOD(device_attach, ps3disk_attach),
+ DEVMETHOD(device_detach, ps3disk_detach),
+ {0, 0},
+};
+
+static driver_t ps3disk_driver = {
+ "ps3disk",
+ ps3disk_methods,
+ sizeof(struct ps3disk_softc),
+};
+
+DRIVER_MODULE(ps3disk, ps3bus, ps3disk_driver, 0, 0);
diff --git a/sys/powerpc/ps3/ps3pic.c b/sys/powerpc/ps3/ps3pic.c
new file mode 100644
index 000000000000..5463f6a6e3b6
--- /dev/null
+++ b/sys/powerpc/ps3/ps3pic.c
@@ -0,0 +1,246 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright 2010 Nathan Whitehorn
+ *
+ * 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/param.h>
+#include <sys/systm.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+
+#include <vm/vm.h>
+#include <vm/pmap.h>
+
+#include <machine/bus.h>
+#include <machine/intr_machdep.h>
+#include <machine/md_var.h>
+#include <machine/platform.h>
+
+#include "ps3-hvcall.h"
+#include "pic_if.h"
+
+static void ps3pic_identify(driver_t *driver, device_t parent);
+static int ps3pic_probe(device_t);
+static int ps3pic_attach(device_t);
+
+static void ps3pic_dispatch(device_t, struct trapframe *);
+static void ps3pic_enable(device_t, u_int, u_int, void **);
+static void ps3pic_eoi(device_t, u_int, void *);
+static void ps3pic_ipi(device_t, u_int);
+static void ps3pic_mask(device_t, u_int, void *);
+static void ps3pic_unmask(device_t, u_int, void *);
+
+struct ps3pic_softc {
+ volatile uint64_t *bitmap_thread0;
+ volatile uint64_t *mask_thread0;
+ volatile uint64_t *bitmap_thread1;
+ volatile uint64_t *mask_thread1;
+
+ uint64_t sc_ipi_outlet[2];
+ uint64_t sc_ipi_virq;
+ int sc_vector[64];
+};
+
+static device_method_t ps3pic_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_identify, ps3pic_identify),
+ DEVMETHOD(device_probe, ps3pic_probe),
+ DEVMETHOD(device_attach, ps3pic_attach),
+
+ /* PIC interface */
+ DEVMETHOD(pic_dispatch, ps3pic_dispatch),
+ DEVMETHOD(pic_enable, ps3pic_enable),
+ DEVMETHOD(pic_eoi, ps3pic_eoi),
+ DEVMETHOD(pic_ipi, ps3pic_ipi),
+ DEVMETHOD(pic_mask, ps3pic_mask),
+ DEVMETHOD(pic_unmask, ps3pic_unmask),
+
+ { 0, 0 },
+};
+
+static driver_t ps3pic_driver = {
+ "ps3pic",
+ ps3pic_methods,
+ sizeof(struct ps3pic_softc)
+};
+
+DRIVER_MODULE(ps3pic, nexus, ps3pic_driver, 0, 0);
+
+static MALLOC_DEFINE(M_PS3PIC, "ps3pic", "PS3 PIC");
+
+static void
+ps3pic_identify(driver_t *driver, device_t parent)
+{
+ if (strcmp(installed_platform(), "ps3") != 0)
+ return;
+
+ if (device_find_child(parent, "ps3pic", DEVICE_UNIT_ANY) == NULL)
+ BUS_ADD_CHILD(parent, 0, "ps3pic", 0);
+}
+
+static int
+ps3pic_probe(device_t dev)
+{
+ device_set_desc(dev, "Playstation 3 interrupt controller");
+ return (BUS_PROBE_NOWILDCARD);
+}
+
+static int
+ps3pic_attach(device_t dev)
+{
+ struct ps3pic_softc *sc;
+ uint64_t ppe;
+ int thread;
+
+ sc = device_get_softc(dev);
+
+ sc->bitmap_thread0 = contigmalloc(128 /* 512 bits * 2 */, M_PS3PIC,
+ M_NOWAIT | M_ZERO, 0, BUS_SPACE_MAXADDR, 64 /* alignment */,
+ PAGE_SIZE /* boundary */);
+ sc->mask_thread0 = sc->bitmap_thread0 + 4;
+ sc->bitmap_thread1 = sc->bitmap_thread0 + 8;
+ sc->mask_thread1 = sc->bitmap_thread0 + 12;
+
+ lv1_get_logical_ppe_id(&ppe);
+ thread = 32 - fls(mfctrl());
+ lv1_configure_irq_state_bitmap(ppe, thread,
+ vtophys(sc->bitmap_thread0));
+
+ sc->sc_ipi_virq = 63;
+
+#ifdef SMP
+ lv1_configure_irq_state_bitmap(ppe, !thread,
+ vtophys(sc->bitmap_thread1));
+
+ /* Map both IPIs to the same VIRQ to avoid changes in intr_machdep */
+ lv1_construct_event_receive_port(&sc->sc_ipi_outlet[0]);
+ lv1_connect_irq_plug_ext(ppe, thread, sc->sc_ipi_virq,
+ sc->sc_ipi_outlet[0], 0);
+ lv1_construct_event_receive_port(&sc->sc_ipi_outlet[1]);
+ lv1_connect_irq_plug_ext(ppe, !thread, sc->sc_ipi_virq,
+ sc->sc_ipi_outlet[1], 0);
+#endif
+
+ powerpc_register_pic(dev, 0, sc->sc_ipi_virq, 1, FALSE);
+ return (0);
+}
+
+/*
+ * PIC I/F methods.
+ */
+
+static void
+ps3pic_dispatch(device_t dev, struct trapframe *tf)
+{
+ uint64_t bitmap, mask;
+ int irq;
+ struct ps3pic_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ if (PCPU_GET(cpuid) == 0) {
+ bitmap = atomic_readandclear_64(&sc->bitmap_thread0[0]);
+ mask = sc->mask_thread0[0];
+ } else {
+ bitmap = atomic_readandclear_64(&sc->bitmap_thread1[0]);
+ mask = sc->mask_thread1[0];
+ }
+ powerpc_sync();
+
+ while ((irq = ffsl(bitmap & mask) - 1) != -1) {
+ bitmap &= ~(1UL << irq);
+ powerpc_dispatch_intr(sc->sc_vector[63 - irq], tf);
+ }
+}
+
+static void
+ps3pic_enable(device_t dev, u_int irq, u_int vector, void **priv)
+{
+ struct ps3pic_softc *sc;
+
+ sc = device_get_softc(dev);
+ sc->sc_vector[irq] = vector;
+
+ ps3pic_unmask(dev, irq, priv);
+}
+
+static void
+ps3pic_eoi(device_t dev, u_int irq, void *priv)
+{
+ uint64_t ppe;
+ int thread;
+
+ lv1_get_logical_ppe_id(&ppe);
+ thread = 32 - fls(mfctrl());
+
+ lv1_end_of_interrupt_ext(ppe, thread, irq);
+}
+
+static void
+ps3pic_ipi(device_t dev, u_int cpu)
+{
+ struct ps3pic_softc *sc;
+ sc = device_get_softc(dev);
+
+ lv1_send_event_locally(sc->sc_ipi_outlet[cpu]);
+}
+
+static void
+ps3pic_mask(device_t dev, u_int irq, void *priv)
+{
+ struct ps3pic_softc *sc;
+ uint64_t ppe;
+
+ sc = device_get_softc(dev);
+
+ /* Do not mask IPIs! */
+ if (irq == sc->sc_ipi_virq)
+ return;
+
+ atomic_clear_64(&sc->mask_thread0[0], 1UL << (63 - irq));
+ atomic_clear_64(&sc->mask_thread1[0], 1UL << (63 - irq));
+
+ lv1_get_logical_ppe_id(&ppe);
+ lv1_did_update_interrupt_mask(ppe, 0);
+ lv1_did_update_interrupt_mask(ppe, 1);
+}
+
+static void
+ps3pic_unmask(device_t dev, u_int irq, void *priv)
+{
+ struct ps3pic_softc *sc;
+ uint64_t ppe;
+
+ sc = device_get_softc(dev);
+ atomic_set_64(&sc->mask_thread0[0], 1UL << (63 - irq));
+ atomic_set_64(&sc->mask_thread1[0], 1UL << (63 - irq));
+
+ lv1_get_logical_ppe_id(&ppe);
+ lv1_did_update_interrupt_mask(ppe, 0);
+ lv1_did_update_interrupt_mask(ppe, 1);
+}