diff options
author | nwhitehorn <nwhitehorn@FreeBSD.org> | 2011-01-06 04:12:29 +0000 |
---|---|---|
committer | nwhitehorn <nwhitehorn@FreeBSD.org> | 2011-01-06 04:12:29 +0000 |
commit | c2aa4fc0ebb33e3eeb047f84ad49d946887a72f2 (patch) | |
tree | 2f204ce78bf39efa5dd8a23b1af67430d676f4d8 /sys/powerpc/ps3 | |
parent | 0bfe906525518ea5724b945073ac8d422b5081d8 (diff) | |
download | FreeBSD-src-c2aa4fc0ebb33e3eeb047f84ad49d946887a72f2.zip FreeBSD-src-c2aa4fc0ebb33e3eeb047f84ad49d946887a72f2.tar.gz |
Import support for the Sony Playstation 3 using the OtherOS feature
available on firmwares 3.15 and earlier.
Caveats: Support for the internal SATA controller is currently missing,
as is support for framebuffer resolutions other than 720x480. These
deficiencies will be remedied soon.
Special thanks to Peter Grehan for providing the hardware that made this
port possible, and thanks to Geoff Levand of Sony Computer Entertainment
for advice on the LV1 hypervisor.
Diffstat (limited to 'sys/powerpc/ps3')
-rw-r--r-- | sys/powerpc/ps3/ehci_ps3.c | 173 | ||||
-rw-r--r-- | sys/powerpc/ps3/if_glc.c | 938 | ||||
-rw-r--r-- | sys/powerpc/ps3/if_glcreg.h | 160 | ||||
-rw-r--r-- | sys/powerpc/ps3/mmu_ps3.c | 311 | ||||
-rw-r--r-- | sys/powerpc/ps3/platform_ps3.c | 262 | ||||
-rw-r--r-- | sys/powerpc/ps3/ps3-hv-asm.awk | 52 | ||||
-rw-r--r-- | sys/powerpc/ps3/ps3-hv-header.awk | 42 | ||||
-rw-r--r-- | sys/powerpc/ps3/ps3-hvcall.S | 1254 | ||||
-rw-r--r-- | sys/powerpc/ps3/ps3-hvcall.h | 110 | ||||
-rw-r--r-- | sys/powerpc/ps3/ps3-hvcall.master | 111 | ||||
-rw-r--r-- | sys/powerpc/ps3/ps3_syscons.c | 757 | ||||
-rw-r--r-- | sys/powerpc/ps3/ps3bus.c | 561 | ||||
-rw-r--r-- | sys/powerpc/ps3/ps3bus.h | 65 | ||||
-rw-r--r-- | sys/powerpc/ps3/ps3pic.c | 254 |
14 files changed, 5050 insertions, 0 deletions
diff --git a/sys/powerpc/ps3/ehci_ps3.c b/sys/powerpc/ps3/ehci_ps3.c new file mode 100644 index 0000000..7808709 --- /dev/null +++ b/sys/powerpc/ps3/ehci_ps3.c @@ -0,0 +1,173 @@ +/*- + * 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. + * + * $FreeBSD$ + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#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/ehci.h> +#include <dev/usb/controller/ehcireg.h> + +#include "ps3bus.h" + +struct ps3_ehci_softc { + ehci_softc_t base; + struct bus_space tag; +}; + +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; + + 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", -1); + 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_flags |= EHCI_SCFLG_BIGEMMIO; + 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), + + /* Bus interface */ + DEVMETHOD(bus_print_child, bus_generic_print_child), + + {0, 0} +}; + +static driver_t ehci_ps3_driver = { + "ehci", + ehci_ps3_methods, + sizeof(ehci_softc_t), +}; + +static devclass_t ehci_ps3_devclass; + +DRIVER_MODULE(ehci_ps3, ps3bus, ehci_ps3_driver, ehci_ps3_devclass, 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 0000000..6901f44 --- /dev/null +++ b/sys/powerpc/ps3/if_glc.c @@ -0,0 +1,938 @@ +/*- + * 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. + * + * $FreeBSD$ + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/sockio.h> +#include <sys/endian.h> +#include <sys/mbuf.h> +#include <sys/module.h> +#include <sys/malloc.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_arp.h> +#include <net/ethernet.h> +#include <net/if_dl.h> +#include <net/if_media.h> +#include <net/if_types.h> +#include <net/if_vlan_var.h> + +#include <machine/pio.h> +#include <machine/bus.h> +#include <machine/platform.h> +#include <machine/pmap.h> +#include <machine/resource.h> +#include <sys/bus.h> +#include <sys/rman.h> + +#include "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(struct ifnet *ifp); +static int glc_ioctl(struct ifnet *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(struct ifnet *ifp, struct ifmediareq *ifmr); +static int glc_media_change(struct ifnet *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) +}; + +static devclass_t glc_devclass; + +DRIVER_MODULE(glc, ps3bus, glc_driver, glc_devclass, 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->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); + sc->sc_ifp->if_softc = 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_MISC | 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)); + sc->sc_ifp->if_mtu = ETHERMTU; + sc->sc_ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; + sc->sc_ifp->if_hwassist = CSUM_TCP | CSUM_UDP; + sc->sc_ifp->if_capabilities = IFCAP_HWCSUM | IFCAP_RXCSUM; + sc->sc_ifp->if_capenable = IFCAP_HWCSUM | IFCAP_RXCSUM; + sc->sc_ifp->if_start = glc_start; + sc->sc_ifp->if_ioctl = glc_ioctl; + sc->sc_ifp->if_init = 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); + + IFQ_SET_MAXLEN(&sc->sc_ifp->if_snd, GLC_MAX_TX_PACKETS); + sc->sc_ifp->if_snd.ifq_drv_maxlen = GLC_MAX_TX_PACKETS; + IFQ_SET_READY(&sc->sc_ifp->if_snd); + + ether_ifattach(sc->sc_ifp, sc->sc_enaddr); + sc->sc_ifp->if_hwassist = 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); + + sc->sc_ifp->if_drv_flags |= IFF_DRV_RUNNING; + sc->sc_ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; + sc->sc_ifpflags = sc->sc_ifp->if_flags; + + 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); + + 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(struct ifnet *ifp) +{ + struct glc_softc *sc = ifp->if_softc; + bus_addr_t first, pktdesc; + int kickstart = 0; + int error; + struct mbuf *mb_head; + + mtx_assert(&sc->sc_mtx, MA_OWNED); + first = 0; + + if ((ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) != + IFF_DRV_RUNNING) + return; + + if (STAILQ_EMPTY(&sc->sc_txdirtyq)) + kickstart = 1; + + while (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) { + IFQ_DRV_DEQUEUE(&ifp->if_snd, mb_head); + + if (mb_head == NULL) + break; + + /* Check if the ring buffer is full */ + if (sc->bsy_txdma_slots > 125) { + /* Put the packet back and stop */ + ifp->if_drv_flags |= IFF_DRV_OACTIVE; + IFQ_DRV_PREPEND(&ifp->if_snd, 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)) { + ifp->if_drv_flags |= IFF_DRV_OACTIVE; + 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(struct ifnet *ifp) +{ + struct glc_softc *sc = ifp->if_softc; + + mtx_lock(&sc->sc_mtx); + glc_start_locked(ifp); + mtx_unlock(&sc->sc_mtx); +} + +static int +glc_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) +{ + struct glc_softc *sc = ifp->if_softc; + struct ifreq *ifr = (struct ifreq *)data; + int err = 0; + + switch (cmd) { + case SIOCSIFFLAGS: + mtx_lock(&sc->sc_mtx); + if ((ifp->if_flags & IFF_UP) != 0) { + if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0 && + ((ifp->if_flags ^ sc->sc_ifpflags) & + (IFF_ALLMULTI | IFF_PROMISC)) != 0) + glc_set_multicast(sc); + else + glc_init_locked(sc); + } + else if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) + glc_stop(sc); + sc->sc_ifpflags = ifp->if_flags; + 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 void +glc_set_multicast(struct glc_softc *sc) +{ + struct ifnet *ifp = sc->sc_ifp; + struct ifmultiaddr *inm; + uint64_t addr; + 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 ((ifp->if_flags & IFF_ALLMULTI) != 0) { + lv1_net_add_multicast_address(sc->sc_bus, sc->sc_dev, 0, 1); + } else { + if_maddr_rlock(ifp); + naddrs = 1; /* Include broadcast */ + TAILQ_FOREACH(inm, &ifp->if_multiaddrs, ifma_link) { + if (inm->ifma_addr->sa_family != AF_LINK) + continue; + addr = 0; + memcpy(&((uint8_t *)(&addr))[2], + LLADDR((struct sockaddr_dl *)inm->ifma_addr), + ETHER_ADDR_LEN); + + lv1_net_add_multicast_address(sc->sc_bus, sc->sc_dev, + addr, 0); + + /* + * Filter can only hold 32 addresses, so fall back to + * the IFF_ALLMULTI case if we have too many. + */ + if (++naddrs >= 32) { + lv1_net_add_multicast_address(sc->sc_bus, + sc->sc_dev, 0, 1); + break; + } + } + if_maddr_runlock(ifp); + } +} + +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_DONTWAIT, 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_DONTWAIT, 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; + struct ifnet *ifp = sc->sc_ifp; + + bus_dmamap_sync(sc->sc_dmadesc_tag, sc->sc_rxdmadesc_map, + BUS_DMASYNC_PREWRITE); + + restart_rxdma = 0; + while ((sc->sc_rxdmadesc[sc->sc_next_rxdma_slot].cmd_stat & + GELIC_DESCR_OWNED) == 0) { + i = sc->sc_next_rxdma_slot; + if (sc->sc_rxdmadesc[i].rxerror & GELIC_RXERRORS) { + ifp->if_ierrors++; + 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)) { + ifp->if_ierrors++; + goto requeue; + } + + ifp->if_ipackets++; + m->m_pkthdr.rcvif = ifp; + m->m_len = sc->sc_rxdmadesc[i].valid_size; + m->m_pkthdr.len = m->m_len; + sc->sc_next_rxdma_slot++; + if (sc->sc_next_rxdma_slot >= GLC_MAX_RX_PACKETS) + sc->sc_next_rxdma_slot = 0; + + if (sc->sc_rx_vlan >= 0) + m_adj(m, 2); + + mtx_unlock(&sc->sc_mtx); + (*ifp->if_input)(ifp, m); + mtx_lock(&sc->sc_mtx); + + requeue: + if (sc->sc_rxdmadesc[i].cmd_stat & GELIC_CMDSTAT_CHAIN_END) + restart_rxdma = 1; + glc_add_rxbuf_dma(sc, i); + if (restart_rxdma) { + error = lv1_net_start_rx_dma(sc->sc_bus, sc->sc_dev, + sc->sc_rxsoft[i].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) +{ + struct ifnet *ifp = sc->sc_ifp; + struct glc_txsoft *txs; + int progress = 0, kickstart = 0, error; + + 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; + ifp->if_oerrors++; + } + + if (sc->sc_txdmadesc[txs->txs_lastdesc].cmd_stat & + GELIC_CMDSTAT_CHAIN_END) + kickstart = 1; + + STAILQ_INSERT_TAIL(&sc->sc_txfreeq, txs, txs_q); + ifp->if_opackets++; + progress = 1; + } + + if (txs != NULL) + sc->first_used_txdma_slot = txs->txs_firstdesc; + else + sc->first_used_txdma_slot = -1; + + if (kickstart && txs != NULL) { + error = lv1_net_start_tx_dma(sc->sc_bus, sc->sc_dev, + sc->sc_txdmadesc_phys + + 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. + */ + ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; + sc->sc_wdog_timer = STAILQ_EMPTY(&sc->sc_txdirtyq) ? 0 : 5; + + if ((ifp->if_drv_flags & IFF_DRV_RUNNING) && + !IFQ_DRV_IS_EMPTY(&ifp->if_snd)) + 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 (linkstat != sc->sc_ifp->if_link_state) + if_link_state_change(sc->sc_ifp, linkstat); + } + + mtx_unlock(&sc->sc_mtx); +} + +static void +glc_media_status(struct ifnet *ifp, struct ifmediareq *ifmr) +{ + struct glc_softc *sc = ifp->if_softc; + 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(struct ifnet *ifp) +{ + struct glc_softc *sc = ifp->if_softc; + 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 0000000..c56363c --- /dev/null +++ b/sys/powerpc/ps3/if_glcreg.h @@ -0,0 +1,160 @@ +/*- + * 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. + * + * $FreeBSD$ + */ + +#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 { + struct ifnet *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 0000000..5a7fe93 --- /dev/null +++ b/sys/powerpc/ps3/mmu_ps3.c @@ -0,0 +1,311 @@ +/*- + * 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> +__FBSDID("$FreeBSD$"); + +#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/vm_pager.h> +#include <vm/uma.h> + +#include <powerpc/aim/mmu_oea64.h> + +#include "mmu_if.h" +#include "moea64_if.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_bootstrap(mmu_t mmup, vm_offset_t kernelstart, + vm_offset_t kernelend); +static void mps3_cpu_bootstrap(mmu_t mmup, int ap); +static void mps3_pte_synch(mmu_t, uintptr_t pt, struct lpte *pvo_pt); +static void mps3_pte_clear(mmu_t, uintptr_t pt, struct lpte *pvo_pt, + uint64_t vpn, uint64_t ptebit); +static void mps3_pte_unset(mmu_t, uintptr_t pt, struct lpte *pvo_pt, + uint64_t vpn); +static void mps3_pte_change(mmu_t, uintptr_t pt, struct lpte *pvo_pt, + uint64_t vpn); +static int mps3_pte_insert(mmu_t, u_int ptegidx, struct lpte *pvo_pt); +static uintptr_t mps3_pvo_to_pte(mmu_t, const struct pvo_entry *pvo); + + +static mmu_method_t mps3_methods[] = { + MMUMETHOD(mmu_bootstrap, mps3_bootstrap), + MMUMETHOD(mmu_cpu_bootstrap, mps3_cpu_bootstrap), + + MMUMETHOD(moea64_pte_synch, mps3_pte_synch), + MMUMETHOD(moea64_pte_clear, mps3_pte_clear), + MMUMETHOD(moea64_pte_unset, mps3_pte_unset), + MMUMETHOD(moea64_pte_change, mps3_pte_change), + MMUMETHOD(moea64_pte_insert, mps3_pte_insert), + MMUMETHOD(moea64_pvo_to_pte, mps3_pvo_to_pte), + + { 0, 0 } +}; + +MMU_DEF_INHERIT(ps3_mmu, "mmu_ps3", mps3_methods, 0, oea64_mmu); + +static void +mps3_bootstrap(mmu_t mmup, vm_offset_t kernelstart, vm_offset_t kernelend) +{ + uint64_t final_pteg_count; + + moea64_early_bootstrap(mmup, kernelstart, kernelend); + + 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 + ); + + moea64_pteg_count = final_pteg_count / sizeof(struct lpteg); + + moea64_mid_bootstrap(mmup, kernelstart, kernelend); + moea64_late_bootstrap(mmup, kernelstart, kernelend); +} + +static void +mps3_cpu_bootstrap(mmu_t mmup, int ap) +{ + struct slb *slb = PCPU_GET(slb); + register_t seg0; + int i; + + mtmsr(mfmsr() & ~PSL_DR & ~PSL_IR); + + /* + * Destroy the loader's address space if we are coming up for + * the first time, and redo the FB mapping so we can continue + * having a console. + */ + + if (!ap) + lv1_destruct_virtual_address_space(0); + + 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 void +mps3_pte_synch(mmu_t mmu, uintptr_t slot, struct lpte *pvo_pt) +{ + uint64_t halfbucket[4], rcbits; + + PTESYNC(); + lv1_read_htab_entries(mps3_vas_id, slot & ~0x3UL, &halfbucket[0], + &halfbucket[1], &halfbucket[2], &halfbucket[3], &rcbits); + + /* + * rcbits contains the low 12 bits of each PTEs 2nd part, + * spaced at 16-bit intervals + */ + + KASSERT((halfbucket[slot & 0x3] & LPTE_AVPN_MASK) == + (pvo_pt->pte_hi & LPTE_AVPN_MASK), + ("PTE upper word %#lx != %#lx\n", + halfbucket[slot & 0x3], pvo_pt->pte_hi)); + + pvo_pt->pte_lo |= (rcbits >> ((3 - (slot & 0x3))*16)) & + (LPTE_CHG | LPTE_REF); +} + +static void +mps3_pte_clear(mmu_t mmu, uintptr_t slot, struct lpte *pvo_pt, uint64_t vpn, + u_int64_t ptebit) +{ + + lv1_write_htab_entry(mps3_vas_id, slot, pvo_pt->pte_hi, + pvo_pt->pte_lo & ~ptebit); +} + +static void +mps3_pte_unset(mmu_t mmu, uintptr_t slot, struct lpte *pvo_pt, uint64_t vpn) +{ + + mps3_pte_synch(mmu, slot, pvo_pt); + pvo_pt->pte_hi &= ~LPTE_VALID; + lv1_write_htab_entry(mps3_vas_id, slot, 0, 0); + moea64_pte_valid--; +} + +static void +mps3_pte_change(mmu_t mmu, uintptr_t slot, struct lpte *pvo_pt, uint64_t vpn) +{ + + mps3_pte_synch(mmu, slot, pvo_pt); + lv1_write_htab_entry(mps3_vas_id, slot, pvo_pt->pte_hi, + pvo_pt->pte_lo); +} + +static int +mps3_pte_insert(mmu_t mmu, u_int ptegidx, struct lpte *pvo_pt) +{ + int result; + struct lpte evicted; + struct pvo_entry *pvo; + uint64_t index; + + pvo_pt->pte_hi |= LPTE_VALID; + pvo_pt->pte_hi &= ~LPTE_HID; + evicted.pte_hi = 0; + PTESYNC(); + result = lv1_insert_htab_entry(mps3_vas_id, ptegidx << 3, + pvo_pt->pte_hi, pvo_pt->pte_lo, LPTE_LOCKED | LPTE_WIRED, 0, + &index, &evicted.pte_hi, &evicted.pte_lo); + + 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 >> 3 != ptegidx) + pvo_pt->pte_hi |= LPTE_HID; + + moea64_pte_valid++; + + if (!evicted.pte_hi) + return (index & 0x7); + + /* + * Synchronize the sacrifice PTE with its PVO, then mark both + * invalid. The PVO will be reused when/if the VM system comes + * here after a fault. + */ + + ptegidx = index >> 3; /* Where the sacrifice PTE was found */ + if (evicted.pte_hi & LPTE_HID) + ptegidx ^= moea64_pteg_mask; /* PTEs indexed by primary */ + + KASSERT((evicted.pte_hi & (LPTE_WIRED | LPTE_LOCKED)) == 0, + ("Evicted a wired PTE")); + + result = 0; + LIST_FOREACH(pvo, &moea64_pvo_table[ptegidx], pvo_olink) { + if (!PVO_PTEGIDX_ISSET(pvo)) + continue; + + if (pvo->pvo_pte.lpte.pte_hi == (evicted.pte_hi | LPTE_VALID)) { + KASSERT(pvo->pvo_pte.lpte.pte_hi & LPTE_VALID, + ("Invalid PVO for valid PTE!")); + pvo->pvo_pte.lpte.pte_hi &= ~LPTE_VALID; + pvo->pvo_pte.lpte.pte_lo |= + evicted.pte_lo & (LPTE_REF | LPTE_CHG); + PVO_PTEGIDX_CLR(pvo); + moea64_pte_valid--; + moea64_pte_overflow++; + result = 1; + break; + } + } + + KASSERT(result == 1, ("PVO for sacrifice PTE not found")); + + return (index & 0x7); +} + +static __inline u_int +va_to_pteg(uint64_t vsid, vm_offset_t addr, int large) +{ + uint64_t hash; + int shift; + + shift = large ? moea64_large_page_shift : ADDR_PIDX_SHFT; + hash = (vsid & VSID_HASH_MASK) ^ (((uint64_t)addr & ADDR_PIDX) >> + shift); + return (hash & moea64_pteg_mask); +} + +uintptr_t +mps3_pvo_to_pte(mmu_t mmu, const struct pvo_entry *pvo) +{ + uint64_t vsid; + u_int ptegidx; + + /* If the PTEG index is not set, then there is no page table entry */ + if (!PVO_PTEGIDX_ISSET(pvo)) + return (-1); + + vsid = PVO_VSID(pvo); + ptegidx = va_to_pteg(vsid, PVO_VADDR(pvo), pvo->pvo_vaddr & PVO_LARGE); + + /* + * We can find the actual pte entry without searching by grabbing + * the PTEG index from 3 unused bits in pvo_vaddr and by + * noticing the HID bit. + */ + if (pvo->pvo_pte.lpte.pte_hi & LPTE_HID) + ptegidx ^= moea64_pteg_mask; + + return ((ptegidx << 3) | PVO_PTEGIDX_GET(pvo)); +} + diff --git a/sys/powerpc/ps3/platform_ps3.c b/sys/powerpc/ps3/platform_ps3.c new file mode 100644 index 0000000..cb22cc3 --- /dev/null +++ b/sys/powerpc/ps3/platform_ps3.c @@ -0,0 +1,262 @@ +/*- + * 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/cdefs.h> +__FBSDID("$FreeBSD$"); + +#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/pmap.h> +#include <machine/smp.h> +#include <machine/spr.h> +#include <machine/vmparam.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 struct cpu_group *ps3_smp_topo(platform_t); +#endif +static void ps3_reset(platform_t); +static void ps3_cpu_idle(void); + +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_topo, ps3_smp_topo), +#endif + + PLATFORMMETHOD(platform_reset, ps3_reset), + + { 0, 0 } +}; + +static platform_def_t ps3_platform = { + "ps3", + ps3_methods, + 0 +}; + +PLATFORM_DEF(ps3_platform); + +static int +ps3_probe(platform_t plat) +{ + + return (BUS_PROBE_NOWILDCARD); +} + +#define MEM_REGIONS 2 +static struct mem_region avail_regions[MEM_REGIONS]; + +static int +ps3_attach(platform_t plat) +{ + uint64_t lpar_id, junk, ppe_id; + + /* Get real mode memory region */ + avail_regions[0].mr_start = 0; + 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"), + &avail_regions[0].mr_size, &junk); + + /* Now get extended memory region */ + lv1_get_repository_node_value(lpar_id, + lv1_repository_string("bi") >> 32, + lv1_repository_string("rgntotal"), 0, 0, + &avail_regions[1].mr_size, &junk); + + /* Convert to maximum amount we can allocate in 16 MB pages */ + avail_regions[1].mr_size -= avail_regions[0].mr_size; + avail_regions[1].mr_size -= avail_regions[1].mr_size % (16*1024*1024); + + lv1_allocate_memory(avail_regions[1].mr_size, 24 /* 16 MB pages */, + 0, 0x04 /* any address */, &avail_regions[1].mr_start, &junk); + + pmap_mmu_install("mmu_ps3", BUS_PROBE_SPECIFIC); + cpu_idle_hook = ps3_cpu_idle; + + /* Set a breakpoint to make NULL an invalid address */ + lv1_set_dabr(0x7 /* read and write, MMU on */, 2 /* kernel accesses */); + + return (0); +} + +void +ps3_mem_regions(platform_t plat, struct mem_region **phys, int *physsz, + struct mem_region **avail, int *availsz) +{ + + *phys = *avail = avail_regions; + *physsz = *availsz = MEM_REGIONS; +} + +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 = cpuref->cr_cpuid; + + 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 = cpuref->cr_cpuid; + + return (0); +} + +static int +ps3_smp_get_bsp(platform_t plat, struct cpuref *cpuref) +{ + + cpuref->cr_cpuid = 0; + cpuref->cr_hwref = cpuref->cr_cpuid; + + return (0); +} + +static int +ps3_smp_start_cpu(platform_t plat, struct pcpu *pc) +{ + /* loader(8) is spinning on 0x40 == 0 right now */ + uint32_t *secondary_spin_sem = (uint32_t *)(0x40); + int timeout; + + if (pc->pc_hwref != 1) + return (ENXIO); + + ap_pcpu = pc; + *secondary_spin_sem = 1; + powerpc_sync(); + DELAY(1); + + timeout = 10000; + while (!pc->pc_awake && timeout--) + DELAY(100); + + return ((pc->pc_awake) ? 0 : EBUSY); +} + +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) +{ + return (avail_regions[0].mr_start + avail_regions[0].mr_size); +} + +static void +ps3_cpu_idle(void) +{ + static volatile int pausing = 0; + + /* + * XXX: It appears that the PS3 can livelock if both threads + * call lv1_pause(0) simultaneously. + */ + if (!atomic_cmpset_int(&pausing, 0, 1)) + return; + + lv1_pause(0); + pausing = 0; +} + diff --git a/sys/powerpc/ps3/ps3-hv-asm.awk b/sys/powerpc/ps3/ps3-hv-asm.awk new file mode 100644 index 0000000..21241a8 --- /dev/null +++ b/sys/powerpc/ps3/ps3-hv-asm.awk @@ -0,0 +1,52 @@ +# 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 +# + +# $FreeBSD$ + +BEGIN { + printf("#include <machine/asm.h>\n\n"); + printf("#define hc .long 0x44000022\n\n"); +} + +/HVCALL.*/ { + 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", 48+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", 48+8*outs + 48 + 8*(i+ins)); + printf("\tstd %%r11,%d(%%r1)\n", 48+8*i); + } else { + printf("\tstd %%r%d,%d(%%r1)\n", 3+ins+i, 48+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", 48+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\n"); +} diff --git a/sys/powerpc/ps3/ps3-hv-header.awk b/sys/powerpc/ps3/ps3-hv-header.awk new file mode 100644 index 0000000..d00571a --- /dev/null +++ b/sys/powerpc/ps3/ps3-hv-header.awk @@ -0,0 +1,42 @@ +# 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 +# + +# $FreeBSD$ + +!/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 0000000..135c601 --- /dev/null +++ b/sys/powerpc/ps3/ps3-hvcall.S @@ -0,0 +1,1254 @@ +/* $FreeBSD$ */ + +#include <machine/asm.h> + +#define hc .long 0x44000022 + +ASENTRY(lv1_allocate_memory) + mflr %r0 + std %r0,16(%r1) + stdu %r1,-64(%r1) + std %r7,48(%r1) + std %r8,56(%r1) + li %r11,0 + hc + extsw %r3,%r3 + ld %r11,48(%r1) + std %r4,0(%r11) + ld %r11,56(%r1) + std %r5,0(%r11) + ld %r1,0(%r1) + ld %r0,16(%r1) + mtlr %r0 + blr + +ASENTRY(lv1_write_htab_entry) + mflr %r0 + std %r0,16(%r1) + stdu %r1,-48(%r1) + li %r11,1 + hc + extsw %r3,%r3 + ld %r1,0(%r1) + ld %r0,16(%r1) + mtlr %r0 + blr + +ASENTRY(lv1_construct_virtual_address_space) + mflr %r0 + std %r0,16(%r1) + stdu %r1,-64(%r1) + std %r6,48(%r1) + std %r7,56(%r1) + li %r11,2 + hc + extsw %r3,%r3 + ld %r11,48(%r1) + std %r4,0(%r11) + ld %r11,56(%r1) + std %r5,0(%r11) + ld %r1,0(%r1) + ld %r0,16(%r1) + mtlr %r0 + blr + +ASENTRY(lv1_get_virtual_address_space_id_of_ppe) + mflr %r0 + std %r0,16(%r1) + stdu %r1,-56(%r1) + std %r4,48(%r1) + li %r11,4 + hc + extsw %r3,%r3 + ld %r11,48(%r1) + std %r4,0(%r11) + ld %r1,0(%r1) + ld %r0,16(%r1) + mtlr %r0 + blr + +ASENTRY(lv1_query_logical_partition_address_region_info) + mflr %r0 + std %r0,16(%r1) + stdu %r1,-88(%r1) + std %r4,48(%r1) + std %r5,56(%r1) + std %r6,64(%r1) + std %r7,72(%r1) + std %r8,80(%r1) + li %r11,6 + hc + extsw %r3,%r3 + ld %r11,48(%r1) + std %r4,0(%r11) + ld %r11,56(%r1) + std %r5,0(%r11) + ld %r11,64(%r1) + std %r6,0(%r11) + ld %r11,72(%r1) + std %r7,0(%r11) + ld %r11,80(%r1) + std %r8,0(%r11) + ld %r1,0(%r1) + ld %r0,16(%r1) + mtlr %r0 + blr + +ASENTRY(lv1_select_virtual_address_space) + mflr %r0 + std %r0,16(%r1) + stdu %r1,-48(%r1) + li %r11,7 + hc + extsw %r3,%r3 + ld %r1,0(%r1) + ld %r0,16(%r1) + mtlr %r0 + blr + +ASENTRY(lv1_pause) + mflr %r0 + std %r0,16(%r1) + stdu %r1,-48(%r1) + li %r11,9 + hc + extsw %r3,%r3 + ld %r1,0(%r1) + ld %r0,16(%r1) + mtlr %r0 + blr + +ASENTRY(lv1_destruct_virtual_address_space) + mflr %r0 + std %r0,16(%r1) + stdu %r1,-48(%r1) + li %r11,10 + hc + extsw %r3,%r3 + ld %r1,0(%r1) + ld %r0,16(%r1) + mtlr %r0 + blr + +ASENTRY(lv1_configure_irq_state_bitmap) + mflr %r0 + std %r0,16(%r1) + stdu %r1,-48(%r1) + li %r11,11 + hc + extsw %r3,%r3 + ld %r1,0(%r1) + ld %r0,16(%r1) + mtlr %r0 + blr + +ASENTRY(lv1_connect_irq_plug_ext) + mflr %r0 + std %r0,16(%r1) + stdu %r1,-48(%r1) + li %r11,12 + hc + extsw %r3,%r3 + ld %r1,0(%r1) + ld %r0,16(%r1) + mtlr %r0 + blr + +ASENTRY(lv1_release_memory) + mflr %r0 + std %r0,16(%r1) + stdu %r1,-48(%r1) + li %r11,13 + hc + extsw %r3,%r3 + ld %r1,0(%r1) + ld %r0,16(%r1) + mtlr %r0 + blr + +ASENTRY(lv1_put_iopte) + mflr %r0 + std %r0,16(%r1) + stdu %r1,-48(%r1) + li %r11,15 + hc + extsw %r3,%r3 + ld %r1,0(%r1) + ld %r0,16(%r1) + mtlr %r0 + blr + +ASENTRY(lv1_disconnect_irq_plug_ext) + mflr %r0 + std %r0,16(%r1) + stdu %r1,-48(%r1) + li %r11,17 + hc + extsw %r3,%r3 + ld %r1,0(%r1) + ld %r0,16(%r1) + mtlr %r0 + blr + +ASENTRY(lv1_construct_event_receive_port) + mflr %r0 + std %r0,16(%r1) + stdu %r1,-56(%r1) + std %r3,48(%r1) + li %r11,18 + hc + extsw %r3,%r3 + ld %r11,48(%r1) + std %r4,0(%r11) + ld %r1,0(%r1) + ld %r0,16(%r1) + mtlr %r0 + blr + +ASENTRY(lv1_destruct_event_receive_port) + mflr %r0 + std %r0,16(%r1) + stdu %r1,-48(%r1) + li %r11,19 + hc + extsw %r3,%r3 + ld %r1,0(%r1) + ld %r0,16(%r1) + mtlr %r0 + blr + +ASENTRY(lv1_send_event_locally) + mflr %r0 + std %r0,16(%r1) + stdu %r1,-48(%r1) + li %r11,24 + hc + extsw %r3,%r3 + ld %r1,0(%r1) + ld %r0,16(%r1) + mtlr %r0 + blr + +ASENTRY(lv1_end_of_interrupt) + mflr %r0 + std %r0,16(%r1) + stdu %r1,-48(%r1) + li %r11,27 + hc + extsw %r3,%r3 + ld %r1,0(%r1) + ld %r0,16(%r1) + mtlr %r0 + blr + +ASENTRY(lv1_connect_irq_plug) + mflr %r0 + std %r0,16(%r1) + stdu %r1,-48(%r1) + li %r11,28 + hc + extsw %r3,%r3 + ld %r1,0(%r1) + ld %r0,16(%r1) + mtlr %r0 + blr + +ASENTRY(lv1_disconnect_irq_plus) + mflr %r0 + std %r0,16(%r1) + stdu %r1,-48(%r1) + li %r11,29 + hc + extsw %r3,%r3 + ld %r1,0(%r1) + ld %r0,16(%r1) + mtlr %r0 + blr + +ASENTRY(lv1_end_of_interrupt_ext) + mflr %r0 + std %r0,16(%r1) + stdu %r1,-48(%r1) + li %r11,30 + hc + extsw %r3,%r3 + ld %r1,0(%r1) + ld %r0,16(%r1) + mtlr %r0 + blr + +ASENTRY(lv1_did_update_interrupt_mask) + mflr %r0 + std %r0,16(%r1) + stdu %r1,-48(%r1) + li %r11,31 + hc + extsw %r3,%r3 + ld %r1,0(%r1) + ld %r0,16(%r1) + mtlr %r0 + blr + +ASENTRY(lv1_shutdown_logical_partition) + mflr %r0 + std %r0,16(%r1) + stdu %r1,-48(%r1) + li %r11,44 + hc + extsw %r3,%r3 + ld %r1,0(%r1) + ld %r0,16(%r1) + mtlr %r0 + blr + +ASENTRY(lv1_destruct_logical_spe) + mflr %r0 + std %r0,16(%r1) + stdu %r1,-48(%r1) + li %r11,54 + hc + extsw %r3,%r3 + ld %r1,0(%r1) + ld %r0,16(%r1) + mtlr %r0 + blr + +ASENTRY(lv1_construct_logical_spe) + mflr %r0 + std %r0,16(%r1) + stdu %r1,-96(%r1) + std %r10,48(%r1) + ld %r11,208(%r1) + std %r11,56(%r1) + ld %r11,216(%r1) + std %r11,64(%r1) + ld %r11,224(%r1) + std %r11,72(%r1) + ld %r11,232(%r1) + std %r11,80(%r1) + ld %r11,240(%r1) + std %r11,88(%r1) + li %r11,57 + hc + extsw %r3,%r3 + ld %r11,48(%r1) + std %r4,0(%r11) + ld %r11,56(%r1) + std %r5,0(%r11) + ld %r11,64(%r1) + std %r6,0(%r11) + ld %r11,72(%r1) + std %r7,0(%r11) + ld %r11,80(%r1) + std %r8,0(%r11) + ld %r11,88(%r1) + std %r9,0(%r11) + ld %r1,0(%r1) + ld %r0,16(%r1) + mtlr %r0 + blr + +ASENTRY(lv1_set_spe_interrupt_mask) + mflr %r0 + std %r0,16(%r1) + stdu %r1,-48(%r1) + li %r11,61 + hc + extsw %r3,%r3 + ld %r1,0(%r1) + ld %r0,16(%r1) + mtlr %r0 + blr + +ASENTRY(lv1_disable_logical_spe) + mflr %r0 + std %r0,16(%r1) + stdu %r1,-48(%r1) + li %r11,65 + hc + extsw %r3,%r3 + ld %r1,0(%r1) + ld %r0,16(%r1) + mtlr %r0 + blr + +ASENTRY(lv1_clear_spe_interrupt_status) + mflr %r0 + std %r0,16(%r1) + stdu %r1,-48(%r1) + li %r11,66 + hc + extsw %r3,%r3 + ld %r1,0(%r1) + ld %r0,16(%r1) + mtlr %r0 + blr + +ASENTRY(lv1_get_spe_interrupt_status) + mflr %r0 + std %r0,16(%r1) + stdu %r1,-56(%r1) + std %r5,48(%r1) + li %r11,67 + hc + extsw %r3,%r3 + ld %r11,48(%r1) + std %r4,0(%r11) + ld %r1,0(%r1) + ld %r0,16(%r1) + mtlr %r0 + blr + +ASENTRY(lv1_get_logical_ppe_id) + mflr %r0 + std %r0,16(%r1) + stdu %r1,-56(%r1) + std %r3,48(%r1) + li %r11,69 + hc + extsw %r3,%r3 + ld %r11,48(%r1) + std %r4,0(%r11) + ld %r1,0(%r1) + ld %r0,16(%r1) + mtlr %r0 + blr + +ASENTRY(lv1_get_logical_partition_id) + mflr %r0 + std %r0,16(%r1) + stdu %r1,-56(%r1) + std %r3,48(%r1) + li %r11,74 + hc + extsw %r3,%r3 + ld %r11,48(%r1) + std %r4,0(%r11) + ld %r1,0(%r1) + ld %r0,16(%r1) + mtlr %r0 + blr + +ASENTRY(lv1_get_spe_irq_outlet) + mflr %r0 + std %r0,16(%r1) + stdu %r1,-56(%r1) + std %r5,48(%r1) + li %r11,78 + hc + extsw %r3,%r3 + ld %r11,48(%r1) + std %r4,0(%r11) + ld %r1,0(%r1) + ld %r0,16(%r1) + mtlr %r0 + blr + +ASENTRY(lv1_set_spe_privilege_state_area_1_register) + mflr %r0 + std %r0,16(%r1) + stdu %r1,-48(%r1) + li %r11,79 + hc + extsw %r3,%r3 + ld %r1,0(%r1) + ld %r0,16(%r1) + mtlr %r0 + blr + +ASENTRY(lv1_get_repository_node_value) + mflr %r0 + std %r0,16(%r1) + stdu %r1,-64(%r1) + std %r8,48(%r1) + std %r9,56(%r1) + li %r11,91 + hc + extsw %r3,%r3 + ld %r11,48(%r1) + std %r4,0(%r11) + ld %r11,56(%r1) + std %r5,0(%r11) + ld %r1,0(%r1) + ld %r0,16(%r1) + mtlr %r0 + blr + +ASENTRY(lv1_read_htab_entries) + mflr %r0 + std %r0,16(%r1) + stdu %r1,-88(%r1) + std %r5,48(%r1) + std %r6,56(%r1) + std %r7,64(%r1) + std %r8,72(%r1) + std %r9,80(%r1) + li %r11,95 + hc + extsw %r3,%r3 + ld %r11,48(%r1) + std %r4,0(%r11) + ld %r11,56(%r1) + std %r5,0(%r11) + ld %r11,64(%r1) + std %r6,0(%r11) + ld %r11,72(%r1) + std %r7,0(%r11) + ld %r11,80(%r1) + std %r8,0(%r11) + ld %r1,0(%r1) + ld %r0,16(%r1) + mtlr %r0 + blr + +ASENTRY(lv1_set_dabr) + mflr %r0 + std %r0,16(%r1) + stdu %r1,-48(%r1) + li %r11,96 + hc + extsw %r3,%r3 + ld %r1,0(%r1) + ld %r0,16(%r1) + mtlr %r0 + blr + +ASENTRY(lv1_allocate_io_segment) + mflr %r0 + std %r0,16(%r1) + stdu %r1,-56(%r1) + std %r6,48(%r1) + li %r11,116 + hc + extsw %r3,%r3 + ld %r11,48(%r1) + std %r4,0(%r11) + ld %r1,0(%r1) + ld %r0,16(%r1) + mtlr %r0 + blr + +ASENTRY(lv1_release_io_segment) + mflr %r0 + std %r0,16(%r1) + stdu %r1,-48(%r1) + li %r11,117 + hc + extsw %r3,%r3 + ld %r1,0(%r1) + ld %r0,16(%r1) + mtlr %r0 + blr + +ASENTRY(lv1_construct_io_irq_outlet) + mflr %r0 + std %r0,16(%r1) + stdu %r1,-56(%r1) + std %r4,48(%r1) + li %r11,120 + hc + extsw %r3,%r3 + ld %r11,48(%r1) + std %r4,0(%r11) + ld %r1,0(%r1) + ld %r0,16(%r1) + mtlr %r0 + blr + +ASENTRY(lv1_destruct_io_irq_outlet) + mflr %r0 + std %r0,16(%r1) + stdu %r1,-48(%r1) + li %r11,121 + hc + extsw %r3,%r3 + ld %r1,0(%r1) + ld %r0,16(%r1) + mtlr %r0 + blr + +ASENTRY(lv1_map_htab) + mflr %r0 + std %r0,16(%r1) + stdu %r1,-56(%r1) + std %r4,48(%r1) + li %r11,122 + hc + extsw %r3,%r3 + ld %r11,48(%r1) + std %r4,0(%r11) + ld %r1,0(%r1) + ld %r0,16(%r1) + mtlr %r0 + blr + +ASENTRY(lv1_unmap_htab) + mflr %r0 + std %r0,16(%r1) + stdu %r1,-48(%r1) + li %r11,123 + hc + extsw %r3,%r3 + ld %r1,0(%r1) + ld %r0,16(%r1) + mtlr %r0 + blr + +ASENTRY(lv1_get_version_info) + mflr %r0 + std %r0,16(%r1) + stdu %r1,-56(%r1) + std %r3,48(%r1) + li %r11,127 + hc + extsw %r3,%r3 + ld %r11,48(%r1) + std %r4,0(%r11) + ld %r1,0(%r1) + ld %r0,16(%r1) + mtlr %r0 + blr + +ASENTRY(lv1_insert_htab_entry) + mflr %r0 + std %r0,16(%r1) + stdu %r1,-72(%r1) + std %r9,48(%r1) + std %r10,56(%r1) + ld %r11,184(%r1) + std %r11,64(%r1) + li %r11,158 + hc + extsw %r3,%r3 + ld %r11,48(%r1) + std %r4,0(%r11) + ld %r11,56(%r1) + std %r5,0(%r11) + ld %r11,64(%r1) + std %r6,0(%r11) + ld %r1,0(%r1) + ld %r0,16(%r1) + mtlr %r0 + blr + +ASENTRY(lv1_read_virtual_uart) + mflr %r0 + std %r0,16(%r1) + stdu %r1,-56(%r1) + std %r6,48(%r1) + li %r11,162 + hc + extsw %r3,%r3 + ld %r11,48(%r1) + std %r4,0(%r11) + ld %r1,0(%r1) + ld %r0,16(%r1) + mtlr %r0 + blr + +ASENTRY(lv1_write_virtual_uart) + mflr %r0 + std %r0,16(%r1) + stdu %r1,-56(%r1) + std %r6,48(%r1) + li %r11,163 + hc + extsw %r3,%r3 + ld %r11,48(%r1) + std %r4,0(%r11) + ld %r1,0(%r1) + ld %r0,16(%r1) + mtlr %r0 + blr + +ASENTRY(lv1_set_virtual_uart_param) + mflr %r0 + std %r0,16(%r1) + stdu %r1,-48(%r1) + li %r11,164 + hc + extsw %r3,%r3 + ld %r1,0(%r1) + ld %r0,16(%r1) + mtlr %r0 + blr + +ASENTRY(lv1_get_virtual_uart_param) + mflr %r0 + std %r0,16(%r1) + stdu %r1,-56(%r1) + std %r5,48(%r1) + li %r11,165 + hc + extsw %r3,%r3 + ld %r11,48(%r1) + std %r4,0(%r11) + ld %r1,0(%r1) + ld %r0,16(%r1) + mtlr %r0 + blr + +ASENTRY(lv1_configure_virtual_uart) + mflr %r0 + std %r0,16(%r1) + stdu %r1,-56(%r1) + std %r4,48(%r1) + li %r11,166 + hc + extsw %r3,%r3 + ld %r11,48(%r1) + std %r4,0(%r11) + ld %r1,0(%r1) + ld %r0,16(%r1) + mtlr %r0 + blr + +ASENTRY(lv1_open_device) + mflr %r0 + std %r0,16(%r1) + stdu %r1,-48(%r1) + li %r11,170 + hc + extsw %r3,%r3 + ld %r1,0(%r1) + ld %r0,16(%r1) + mtlr %r0 + blr + +ASENTRY(lv1_close_device) + mflr %r0 + std %r0,16(%r1) + stdu %r1,-48(%r1) + li %r11,171 + hc + extsw %r3,%r3 + ld %r1,0(%r1) + ld %r0,16(%r1) + mtlr %r0 + blr + +ASENTRY(lv1_map_device_mmio_region) + mflr %r0 + std %r0,16(%r1) + stdu %r1,-56(%r1) + std %r8,48(%r1) + li %r11,172 + hc + extsw %r3,%r3 + ld %r11,48(%r1) + std %r4,0(%r11) + ld %r1,0(%r1) + ld %r0,16(%r1) + mtlr %r0 + blr + +ASENTRY(lv1_unmap_device_mmio_region) + mflr %r0 + std %r0,16(%r1) + stdu %r1,-48(%r1) + li %r11,173 + hc + extsw %r3,%r3 + ld %r1,0(%r1) + ld %r0,16(%r1) + mtlr %r0 + blr + +ASENTRY(lv1_allocate_device_dma_region) + mflr %r0 + std %r0,16(%r1) + stdu %r1,-56(%r1) + std %r8,48(%r1) + li %r11,174 + hc + extsw %r3,%r3 + ld %r11,48(%r1) + std %r4,0(%r11) + ld %r1,0(%r1) + ld %r0,16(%r1) + mtlr %r0 + blr + +ASENTRY(lv1_free_device_dma_region) + mflr %r0 + std %r0,16(%r1) + stdu %r1,-48(%r1) + li %r11,175 + hc + extsw %r3,%r3 + ld %r1,0(%r1) + ld %r0,16(%r1) + mtlr %r0 + blr + +ASENTRY(lv1_map_device_dma_region) + mflr %r0 + std %r0,16(%r1) + stdu %r1,-48(%r1) + li %r11,176 + hc + extsw %r3,%r3 + ld %r1,0(%r1) + ld %r0,16(%r1) + mtlr %r0 + blr + +ASENTRY(lv1_unmap_device_dma_region) + mflr %r0 + std %r0,16(%r1) + stdu %r1,-48(%r1) + li %r11,177 + hc + extsw %r3,%r3 + ld %r1,0(%r1) + ld %r0,16(%r1) + mtlr %r0 + blr + +ASENTRY(lv1_read_pci_config) + mflr %r0 + std %r0,16(%r1) + stdu %r1,-56(%r1) + std %r9,48(%r1) + li %r11,178 + hc + extsw %r3,%r3 + ld %r11,48(%r1) + std %r4,0(%r11) + ld %r1,0(%r1) + ld %r0,16(%r1) + mtlr %r0 + blr + +ASENTRY(lv1_write_pci_config) + mflr %r0 + std %r0,16(%r1) + stdu %r1,-48(%r1) + li %r11,179 + hc + extsw %r3,%r3 + ld %r1,0(%r1) + ld %r0,16(%r1) + mtlr %r0 + blr + +ASENTRY(lv1_net_add_multicast_address) + mflr %r0 + std %r0,16(%r1) + stdu %r1,-48(%r1) + li %r11,185 + hc + extsw %r3,%r3 + ld %r1,0(%r1) + ld %r0,16(%r1) + mtlr %r0 + blr + +ASENTRY(lv1_net_remove_multicast_address) + mflr %r0 + std %r0,16(%r1) + stdu %r1,-48(%r1) + li %r11,186 + hc + extsw %r3,%r3 + ld %r1,0(%r1) + ld %r0,16(%r1) + mtlr %r0 + blr + +ASENTRY(lv1_net_start_tx_dma) + mflr %r0 + std %r0,16(%r1) + stdu %r1,-48(%r1) + li %r11,187 + hc + extsw %r3,%r3 + ld %r1,0(%r1) + ld %r0,16(%r1) + mtlr %r0 + blr + +ASENTRY(lv1_net_stop_tx_dma) + mflr %r0 + std %r0,16(%r1) + stdu %r1,-48(%r1) + li %r11,188 + hc + extsw %r3,%r3 + ld %r1,0(%r1) + ld %r0,16(%r1) + mtlr %r0 + blr + +ASENTRY(lv1_net_start_rx_dma) + mflr %r0 + std %r0,16(%r1) + stdu %r1,-48(%r1) + li %r11,189 + hc + extsw %r3,%r3 + ld %r1,0(%r1) + ld %r0,16(%r1) + mtlr %r0 + blr + +ASENTRY(lv1_net_stop_rx_dma) + mflr %r0 + std %r0,16(%r1) + stdu %r1,-48(%r1) + li %r11,190 + hc + extsw %r3,%r3 + ld %r1,0(%r1) + ld %r0,16(%r1) + mtlr %r0 + blr + +ASENTRY(lv1_net_set_interrupt_status_indicator) + mflr %r0 + std %r0,16(%r1) + stdu %r1,-48(%r1) + li %r11,191 + hc + extsw %r3,%r3 + ld %r1,0(%r1) + ld %r0,16(%r1) + mtlr %r0 + blr + +ASENTRY(lv1_net_set_interrupt_mask) + mflr %r0 + std %r0,16(%r1) + stdu %r1,-48(%r1) + li %r11,193 + hc + extsw %r3,%r3 + ld %r1,0(%r1) + ld %r0,16(%r1) + mtlr %r0 + blr + +ASENTRY(lv1_net_control) + mflr %r0 + std %r0,16(%r1) + stdu %r1,-64(%r1) + std %r9,48(%r1) + std %r10,56(%r1) + li %r11,194 + hc + extsw %r3,%r3 + ld %r11,48(%r1) + std %r4,0(%r11) + ld %r11,56(%r1) + std %r5,0(%r11) + ld %r1,0(%r1) + ld %r0,16(%r1) + mtlr %r0 + blr + +ASENTRY(lv1_connect_interrupt_event_receive_port) + mflr %r0 + std %r0,16(%r1) + stdu %r1,-48(%r1) + li %r11,197 + hc + extsw %r3,%r3 + ld %r1,0(%r1) + ld %r0,16(%r1) + mtlr %r0 + blr + +ASENTRY(lv1_disconnect_interrupt_event_receive_port) + mflr %r0 + std %r0,16(%r1) + stdu %r1,-48(%r1) + li %r11,198 + hc + extsw %r3,%r3 + ld %r1,0(%r1) + ld %r0,16(%r1) + mtlr %r0 + blr + +ASENTRY(lv1_deconfigure_virtual_uart_irq) + mflr %r0 + std %r0,16(%r1) + stdu %r1,-48(%r1) + li %r11,202 + hc + extsw %r3,%r3 + ld %r1,0(%r1) + ld %r0,16(%r1) + mtlr %r0 + blr + +ASENTRY(lv1_enable_logical_spe) + mflr %r0 + std %r0,16(%r1) + stdu %r1,-48(%r1) + li %r11,207 + hc + extsw %r3,%r3 + ld %r1,0(%r1) + ld %r0,16(%r1) + mtlr %r0 + blr + +ASENTRY(lv1_gpu_open) + mflr %r0 + std %r0,16(%r1) + stdu %r1,-48(%r1) + li %r11,210 + hc + extsw %r3,%r3 + ld %r1,0(%r1) + ld %r0,16(%r1) + mtlr %r0 + blr + +ASENTRY(lv1_gpu_close) + mflr %r0 + std %r0,16(%r1) + stdu %r1,-48(%r1) + li %r11,211 + hc + extsw %r3,%r3 + ld %r1,0(%r1) + ld %r0,16(%r1) + mtlr %r0 + blr + +ASENTRY(lv1_gpu_device_map) + mflr %r0 + std %r0,16(%r1) + stdu %r1,-64(%r1) + std %r4,48(%r1) + std %r5,56(%r1) + li %r11,212 + hc + extsw %r3,%r3 + ld %r11,48(%r1) + std %r4,0(%r11) + ld %r11,56(%r1) + std %r5,0(%r11) + ld %r1,0(%r1) + ld %r0,16(%r1) + mtlr %r0 + blr + +ASENTRY(lv1_gpu_device_unmap) + mflr %r0 + std %r0,16(%r1) + stdu %r1,-48(%r1) + li %r11,213 + hc + extsw %r3,%r3 + ld %r1,0(%r1) + ld %r0,16(%r1) + mtlr %r0 + blr + +ASENTRY(lv1_gpu_memory_allocate) + mflr %r0 + std %r0,16(%r1) + stdu %r1,-64(%r1) + std %r8,48(%r1) + std %r9,56(%r1) + li %r11,214 + hc + extsw %r3,%r3 + ld %r11,48(%r1) + std %r4,0(%r11) + ld %r11,56(%r1) + std %r5,0(%r11) + ld %r1,0(%r1) + ld %r0,16(%r1) + mtlr %r0 + blr + +ASENTRY(lv1_gpu_memory_free) + mflr %r0 + std %r0,16(%r1) + stdu %r1,-48(%r1) + li %r11,216 + hc + extsw %r3,%r3 + ld %r1,0(%r1) + ld %r0,16(%r1) + mtlr %r0 + blr + +ASENTRY(lv1_gpu_context_allocate) + mflr %r0 + std %r0,16(%r1) + stdu %r1,-56(%r1) + std %r5,48(%r1) + li %r11,217 + hc + extsw %r3,%r3 + ld %r11,48(%r1) + std %r4,0(%r11) + ld %r1,0(%r1) + ld %r0,16(%r1) + mtlr %r0 + blr + +ASENTRY(lv1_gpu_context_free) + mflr %r0 + std %r0,16(%r1) + stdu %r1,-48(%r1) + li %r11,218 + hc + extsw %r3,%r3 + ld %r1,0(%r1) + ld %r0,16(%r1) + mtlr %r0 + blr + +ASENTRY(lv1_gpu_context_iomap) + mflr %r0 + std %r0,16(%r1) + stdu %r1,-48(%r1) + li %r11,221 + hc + extsw %r3,%r3 + ld %r1,0(%r1) + ld %r0,16(%r1) + mtlr %r0 + blr + +ASENTRY(lv1_gpu_context_attribute) + mflr %r0 + std %r0,16(%r1) + stdu %r1,-48(%r1) + li %r11,225 + hc + extsw %r3,%r3 + ld %r1,0(%r1) + ld %r0,16(%r1) + mtlr %r0 + blr + +ASENTRY(lv1_gpu_context_intr) + mflr %r0 + std %r0,16(%r1) + stdu %r1,-56(%r1) + std %r4,48(%r1) + li %r11,227 + hc + extsw %r3,%r3 + ld %r11,48(%r1) + std %r4,0(%r11) + ld %r1,0(%r1) + ld %r0,16(%r1) + mtlr %r0 + blr + +ASENTRY(lv1_get_rtc) + mflr %r0 + std %r0,16(%r1) + stdu %r1,-64(%r1) + std %r3,48(%r1) + std %r4,56(%r1) + li %r11,232 + hc + extsw %r3,%r3 + ld %r11,48(%r1) + std %r4,0(%r11) + ld %r11,56(%r1) + std %r5,0(%r11) + ld %r1,0(%r1) + ld %r0,16(%r1) + mtlr %r0 + blr + +ASENTRY(lv1_storage_read) + mflr %r0 + std %r0,16(%r1) + stdu %r1,-56(%r1) + std %r9,48(%r1) + li %r11,245 + hc + extsw %r3,%r3 + ld %r11,48(%r1) + std %r4,0(%r11) + ld %r1,0(%r1) + ld %r0,16(%r1) + mtlr %r0 + blr + +ASENTRY(lv1_storage_write) + mflr %r0 + std %r0,16(%r1) + stdu %r1,-56(%r1) + std %r9,48(%r1) + li %r11,246 + hc + extsw %r3,%r3 + ld %r11,48(%r1) + std %r4,0(%r11) + ld %r1,0(%r1) + ld %r0,16(%r1) + mtlr %r0 + blr + +ASENTRY(lv1_storage_send_device_command) + mflr %r0 + std %r0,16(%r1) + stdu %r1,-56(%r1) + std %r9,48(%r1) + li %r11,248 + hc + extsw %r3,%r3 + ld %r11,48(%r1) + std %r4,0(%r11) + ld %r1,0(%r1) + ld %r0,16(%r1) + mtlr %r0 + blr + +ASENTRY(lv1_storage_get_async_status) + mflr %r0 + std %r0,16(%r1) + stdu %r1,-64(%r1) + std %r4,48(%r1) + std %r5,56(%r1) + li %r11,249 + hc + extsw %r3,%r3 + ld %r11,48(%r1) + std %r4,0(%r11) + ld %r11,56(%r1) + std %r5,0(%r11) + ld %r1,0(%r1) + ld %r0,16(%r1) + mtlr %r0 + blr + +ASENTRY(lv1_storage_check_async_status) + mflr %r0 + std %r0,16(%r1) + stdu %r1,-56(%r1) + std %r5,48(%r1) + li %r11,254 + hc + extsw %r3,%r3 + ld %r11,48(%r1) + std %r4,0(%r11) + ld %r1,0(%r1) + ld %r0,16(%r1) + mtlr %r0 + blr + +ASENTRY(lv1_panic) + mflr %r0 + std %r0,16(%r1) + stdu %r1,-48(%r1) + li %r11,255 + hc + extsw %r3,%r3 + ld %r1,0(%r1) + ld %r0,16(%r1) + mtlr %r0 + blr + diff --git a/sys/powerpc/ps3/ps3-hvcall.h b/sys/powerpc/ps3/ps3-hvcall.h new file mode 100644 index 0000000..912158b --- /dev/null +++ b/sys/powerpc/ps3/ps3-hvcall.h @@ -0,0 +1,110 @@ +/* + * Playstation 3 LV1 hypercall interface + * + * $FreeBSD$ + */ + +#include <sys/types.h> + +enum lpar_id { + PS3_LPAR_ID_CURRENT = 0x00, + PS3_LPAR_ID_PME = 0x01, +}; + +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 , uint64_t *zero); +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_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 0000000..d53a32e --- /dev/null +++ b/sys/powerpc/ps3/ps3-hvcall.master @@ -0,0 +1,111 @@ +/* + * Playstation 3 LV1 hypercall interface + * + * $FreeBSD$ + */ + +#include <sys/types.h> + +enum lpar_id { + PS3_LPAR_ID_CURRENT = 0x00, + PS3_LPAR_ID_PME = 0x01, +}; + +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, zero 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 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 0000000..e0e9cc8 --- /dev/null +++ b/sys/powerpc/ps3/ps3_syscons.c @@ -0,0 +1,757 @@ +/*- + * Copyright (c) 2003 Peter Grehan + * 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/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/module.h> +#include <sys/bus.h> +#include <sys/kernel.h> +#include <sys/sysctl.h> +#include <sys/limits.h> +#include <sys/conf.h> +#include <sys/cons.h> +#include <sys/proc.h> +#include <sys/fcntl.h> +#include <sys/malloc.h> +#include <sys/fbio.h> +#include <sys/consio.h> + +#include <vm/vm.h> +#include <vm/pmap.h> + +#include <machine/bus.h> +#include <machine/sc_machdep.h> +#include <machine/platform.h> +#include <machine/pmap.h> + +#include <sys/rman.h> + +#include <dev/fb/fbreg.h> +#include <dev/syscons/syscons.h> + +#include "ps3-hvcall.h" + +#define PS3FB_SIZE (4*1024*1024) + +#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 + +extern u_char dflt_font_16[]; +extern u_char dflt_font_14[]; +extern u_char dflt_font_8[]; + +static int ps3fb_configure(int flags); +void ps3fb_remap(void); + +static vi_probe_t ps3fb_probe; +static vi_init_t ps3fb_init; +static vi_get_info_t ps3fb_get_info; +static vi_query_mode_t ps3fb_query_mode; +static vi_set_mode_t ps3fb_set_mode; +static vi_save_font_t ps3fb_save_font; +static vi_load_font_t ps3fb_load_font; +static vi_show_font_t ps3fb_show_font; +static vi_save_palette_t ps3fb_save_palette; +static vi_load_palette_t ps3fb_load_palette; +static vi_set_border_t ps3fb_set_border; +static vi_save_state_t ps3fb_save_state; +static vi_load_state_t ps3fb_load_state; +static vi_set_win_org_t ps3fb_set_win_org; +static vi_read_hw_cursor_t ps3fb_read_hw_cursor; +static vi_set_hw_cursor_t ps3fb_set_hw_cursor; +static vi_set_hw_cursor_shape_t ps3fb_set_hw_cursor_shape; +static vi_blank_display_t ps3fb_blank_display; +static vi_mmap_t ps3fb_mmap; +static vi_ioctl_t ps3fb_ioctl; +static vi_clear_t ps3fb_clear; +static vi_fill_rect_t ps3fb_fill_rect; +static vi_bitblt_t ps3fb_bitblt; +static vi_diag_t ps3fb_diag; +static vi_save_cursor_palette_t ps3fb_save_cursor_palette; +static vi_load_cursor_palette_t ps3fb_load_cursor_palette; +static vi_copy_t ps3fb_copy; +static vi_putp_t ps3fb_putp; +static vi_putc_t ps3fb_putc; +static vi_puts_t ps3fb_puts; +static vi_putm_t ps3fb_putm; + +struct ps3fb_softc { + video_adapter_t sc_va; + struct cdev *sc_si; + int sc_console; + + intptr_t sc_addr; + int sc_height; + int sc_width; + int sc_stride; + int sc_ncol; + int sc_nrow; + + int sc_xmargin; + int sc_ymargin; + + u_char *sc_font; + int sc_font_height; +}; + +static video_switch_t ps3fbvidsw = { + .probe = ps3fb_probe, + .init = ps3fb_init, + .get_info = ps3fb_get_info, + .query_mode = ps3fb_query_mode, + .set_mode = ps3fb_set_mode, + .save_font = ps3fb_save_font, + .load_font = ps3fb_load_font, + .show_font = ps3fb_show_font, + .save_palette = ps3fb_save_palette, + .load_palette = ps3fb_load_palette, + .set_border = ps3fb_set_border, + .save_state = ps3fb_save_state, + .load_state = ps3fb_load_state, + .set_win_org = ps3fb_set_win_org, + .read_hw_cursor = ps3fb_read_hw_cursor, + .set_hw_cursor = ps3fb_set_hw_cursor, + .set_hw_cursor_shape = ps3fb_set_hw_cursor_shape, + .blank_display = ps3fb_blank_display, + .mmap = ps3fb_mmap, + .ioctl = ps3fb_ioctl, + .clear = ps3fb_clear, + .fill_rect = ps3fb_fill_rect, + .bitblt = ps3fb_bitblt, + .diag = ps3fb_diag, + .save_cursor_palette = ps3fb_save_cursor_palette, + .load_cursor_palette = ps3fb_load_cursor_palette, + .copy = ps3fb_copy, + .putp = ps3fb_putp, + .putc = ps3fb_putc, + .puts = ps3fb_puts, + .putm = ps3fb_putm, +}; + +VIDEO_DRIVER(ps3fb, ps3fbvidsw, ps3fb_configure); + +extern sc_rndr_sw_t txtrndrsw; +RENDERER(ps3fb, 0, txtrndrsw, gfb_set); + +RENDERER_MODULE(ps3fb, gfb_set); + +/* + * Define the iso6429-1983 colormap + */ +static struct { + uint8_t red; + uint8_t green; + uint8_t blue; +} ps3fb_cmap[16] = { /* # R G B Color */ + /* - - - - ----- */ + { 0x00, 0x00, 0x00 }, /* 0 0 0 0 Black */ + { 0x00, 0x00, 0xaa }, /* 1 0 0 2/3 Blue */ + { 0x00, 0xaa, 0x00 }, /* 2 0 2/3 0 Green */ + { 0x00, 0xaa, 0xaa }, /* 3 0 2/3 2/3 Cyan */ + { 0xaa, 0x00, 0x00 }, /* 4 2/3 0 0 Red */ + { 0xaa, 0x00, 0xaa }, /* 5 2/3 0 2/3 Magenta */ + { 0xaa, 0x55, 0x00 }, /* 6 2/3 1/3 0 Brown */ + { 0xaa, 0xaa, 0xaa }, /* 7 2/3 2/3 2/3 White */ + { 0x55, 0x55, 0x55 }, /* 8 1/3 1/3 1/3 Gray */ + { 0x55, 0x55, 0xff }, /* 9 1/3 1/3 1 Bright Blue */ + { 0x55, 0xff, 0x55 }, /* 10 1/3 1 1/3 Bright Green */ + { 0x55, 0xff, 0xff }, /* 11 1/3 1 1 Bright Cyan */ + { 0xff, 0x55, 0x55 }, /* 12 1 1/3 1/3 Bright Red */ + { 0xff, 0x55, 0xff }, /* 13 1 1/3 1 Bright Magenta */ + { 0xff, 0xff, 0x80 }, /* 14 1 1 1/3 Bright Yellow */ + { 0xff, 0xff, 0xff } /* 15 1 1 1 Bright White */ +}; + +#define TODO printf("%s: unimplemented\n", __func__) + +static u_int16_t ps3fb_static_window[ROW*COL]; + +static struct ps3fb_softc ps3fb_softc; + +static __inline int +ps3fb_background(uint8_t attr) +{ + return (attr >> 4); +} + +static __inline int +ps3fb_foreground(uint8_t attr) +{ + return (attr & 0x0f); +} + +static u_int +ps3fb_pix32(int attr) +{ + u_int retval; + + retval = (ps3fb_cmap[attr].red << 16) | + (ps3fb_cmap[attr].green << 8) | + ps3fb_cmap[attr].blue; + + return (retval); +} + +static int +ps3fb_configure(int flags) +{ + struct ps3fb_softc *sc; + int disable; + char compatible[64]; +#if 0 + phandle_t root; +#endif + static int done = 0; + + disable = 0; + TUNABLE_INT_FETCH("hw.syscons.disable", &disable); + if (disable != 0) + return (0); + + if (done != 0) + return (0); + done = 1; + + sc = &ps3fb_softc; + +#if 0 + root = OF_finddevice("/"); + if (OF_getprop(root, "compatible", compatible, sizeof(compatible)) <= 0) + return (0); + + if (strncmp(compatible, "sony,ps3", sizeof(compatible)) != 0) + return (0); +#else + TUNABLE_STR_FETCH("hw.platform", compatible, sizeof(compatible)); + if (strcmp(compatible, "ps3") != 0) + return (0); +#endif + + sc->sc_console = 1; + + /* XXX: get from HV repository */ + sc->sc_height = 480; + sc->sc_width = 720; + sc->sc_stride = sc->sc_width*4; + + /* + * The loader puts the FB at 0x10000000, so use that for now. + */ + + sc->sc_addr = 0x10000000; + ps3fb_init(0, &sc->sc_va, 0); + + return (0); +} + +void +ps3fb_remap(void) +{ + vm_offset_t va, fb_paddr; + uint64_t fbhandle, fbcontext; + + 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(PS3FB_SIZE, 0, 0, 0, 0, &fbhandle, &fb_paddr); + lv1_gpu_context_allocate(fbhandle, 0, &fbcontext); + + lv1_gpu_context_attribute(fbcontext, + L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_FLIP, 0, 0, 0, 0); + lv1_gpu_context_attribute(fbcontext, + L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_FLIP, 1, 0, 0, 0); + + for (va = 0; va < PS3FB_SIZE; va += PAGE_SIZE) + pmap_kenter_attr(0x10000000 + va, fb_paddr + va, + VM_MEMATTR_WRITE_COMBINING); +} + +static int +ps3fb_probe(int unit, video_adapter_t **adp, void *arg, int flags) +{ + TODO; + return (0); +} + +static int +ps3fb_init(int unit, video_adapter_t *adp, int flags) +{ + struct ps3fb_softc *sc; + video_info_t *vi; + int cxborder, cyborder; + int font_height; + + sc = (struct ps3fb_softc *)adp; + vi = &adp->va_info; + + vid_init_struct(adp, "ps3fb", -1, unit); + + /* The default font size can be overridden by loader */ + font_height = 16; + TUNABLE_INT_FETCH("hw.syscons.fsize", &font_height); + if (font_height == 8) { + sc->sc_font = dflt_font_8; + sc->sc_font_height = 8; + } else if (font_height == 14) { + sc->sc_font = dflt_font_14; + sc->sc_font_height = 14; + } else { + /* default is 8x16 */ + sc->sc_font = dflt_font_16; + sc->sc_font_height = 16; + } + + /* The user can set a border in chars - default is 1 char width */ + cxborder = 8; + cyborder = 2; + TUNABLE_INT_FETCH("hw.syscons.xborder", &cxborder); + TUNABLE_INT_FETCH("hw.syscons.yborder", &cyborder); + + vi->vi_cheight = sc->sc_font_height; + vi->vi_width = sc->sc_width/8 - 2*cxborder; + vi->vi_height = sc->sc_height/sc->sc_font_height - 2*cyborder; + vi->vi_cwidth = 8; + + /* + * Clamp width/height to syscons maximums + */ + if (vi->vi_width > COL) + vi->vi_width = COL; + if (vi->vi_height > ROW) + vi->vi_height = ROW; + + sc->sc_xmargin = (sc->sc_width - (vi->vi_width * vi->vi_cwidth)) / 2; + sc->sc_ymargin = (sc->sc_height - (vi->vi_height * vi->vi_cheight))/2; + + /* + * Avoid huge amounts of conditional code in syscons by + * defining a dummy h/w text display buffer. + */ + adp->va_window = (vm_offset_t) ps3fb_static_window; + + /* + * Enable future font-loading and flag color support, as well as + * adding V_ADP_MODECHANGE so that we ps3fb_set_mode() gets called + * when the X server shuts down. This enables us to get the console + * back when X disappears. + */ + adp->va_flags |= V_ADP_FONT | V_ADP_COLOR | V_ADP_MODECHANGE; + + ps3fb_set_mode(&sc->sc_va, 0); + vid_register(&sc->sc_va); + + return (0); +} + +static int +ps3fb_get_info(video_adapter_t *adp, int mode, video_info_t *info) +{ + bcopy(&adp->va_info, info, sizeof(*info)); + return (0); +} + +static int +ps3fb_query_mode(video_adapter_t *adp, video_info_t *info) +{ + TODO; + return (0); +} + +static int +ps3fb_set_mode(video_adapter_t *adp, int mode) +{ + struct ps3fb_softc *sc; + + sc = (struct ps3fb_softc *)adp; + + /* XXX: no real mode setting at the moment */ + + ps3fb_blank_display(&sc->sc_va, V_DISPLAY_ON); + + return (0); +} + +static int +ps3fb_save_font(video_adapter_t *adp, int page, int size, int width, + u_char *data, int c, int count) +{ + TODO; + return (0); +} + +static int +ps3fb_load_font(video_adapter_t *adp, int page, int size, int width, + u_char *data, int c, int count) +{ + struct ps3fb_softc *sc; + + sc = (struct ps3fb_softc *)adp; + + /* + * syscons code has already determined that current width/height + * are unchanged for this new font + */ + sc->sc_font = data; + return (0); +} + +static int +ps3fb_show_font(video_adapter_t *adp, int page) +{ + + return (0); +} + +static int +ps3fb_save_palette(video_adapter_t *adp, u_char *palette) +{ + /* TODO; */ + return (0); +} + +static int +ps3fb_load_palette(video_adapter_t *adp, u_char *palette) +{ + /* TODO; */ + return (0); +} + +static int +ps3fb_set_border(video_adapter_t *adp, int border) +{ + /* XXX Be lazy for now and blank entire screen */ + return (ps3fb_blank_display(adp, border)); +} + +static int +ps3fb_save_state(video_adapter_t *adp, void *p, size_t size) +{ + TODO; + return (0); +} + +static int +ps3fb_load_state(video_adapter_t *adp, void *p) +{ + TODO; + return (0); +} + +static int +ps3fb_set_win_org(video_adapter_t *adp, off_t offset) +{ + TODO; + return (0); +} + +static int +ps3fb_read_hw_cursor(video_adapter_t *adp, int *col, int *row) +{ + *col = 0; + *row = 0; + + return (0); +} + +static int +ps3fb_set_hw_cursor(video_adapter_t *adp, int col, int row) +{ + + return (0); +} + +static int +ps3fb_set_hw_cursor_shape(video_adapter_t *adp, int base, int height, + int celsize, int blink) +{ + return (0); +} + +static int +ps3fb_blank_display(video_adapter_t *adp, int mode) +{ + struct ps3fb_softc *sc; + int i; + uint32_t *addr; + + sc = (struct ps3fb_softc *)adp; + addr = (uint32_t *) sc->sc_addr; + + for (i = 0; i < (sc->sc_stride/4)*sc->sc_height; i++) + *(addr + i) = ps3fb_pix32(ps3fb_background(SC_NORM_ATTR)); + + return (0); +} + +static int +ps3fb_mmap(video_adapter_t *adp, vm_ooffset_t offset, vm_paddr_t *paddr, + int prot, vm_memattr_t *memattr) +{ + struct ps3fb_softc *sc; + + sc = (struct ps3fb_softc *)adp; + + /* + * This might be a legacy VGA mem request: if so, just point it at the + * framebuffer, since it shouldn't be touched + */ + if (offset < sc->sc_stride*sc->sc_height) { + *paddr = sc->sc_addr + offset; + return (0); + } + + return (EINVAL); +} + +static int +ps3fb_ioctl(video_adapter_t *adp, u_long cmd, caddr_t data) +{ + + return (0); +} + +static int +ps3fb_clear(video_adapter_t *adp) +{ + TODO; + return (0); +} + +static int +ps3fb_fill_rect(video_adapter_t *adp, int val, int x, int y, int cx, int cy) +{ + TODO; + return (0); +} + +static int +ps3fb_bitblt(video_adapter_t *adp, ...) +{ + TODO; + return (0); +} + +static int +ps3fb_diag(video_adapter_t *adp, int level) +{ + TODO; + return (0); +} + +static int +ps3fb_save_cursor_palette(video_adapter_t *adp, u_char *palette) +{ + TODO; + return (0); +} + +static int +ps3fb_load_cursor_palette(video_adapter_t *adp, u_char *palette) +{ + TODO; + return (0); +} + +static int +ps3fb_copy(video_adapter_t *adp, vm_offset_t src, vm_offset_t dst, int n) +{ + TODO; + return (0); +} + +static int +ps3fb_putp(video_adapter_t *adp, vm_offset_t off, uint32_t p, uint32_t a, + int size, int bpp, int bit_ltor, int byte_ltor) +{ + TODO; + return (0); +} + +static int +ps3fb_putc(video_adapter_t *adp, vm_offset_t off, uint8_t c, uint8_t a) +{ + struct ps3fb_softc *sc; + int row; + int col; + int i, j, k; + uint32_t *addr; + u_char *p; + + sc = (struct ps3fb_softc *)adp; + row = (off / adp->va_info.vi_width) * adp->va_info.vi_cheight; + col = (off % adp->va_info.vi_width) * adp->va_info.vi_cwidth; + p = sc->sc_font + c*sc->sc_font_height; + addr = (uint32_t *)sc->sc_addr + + (row + sc->sc_ymargin)*(sc->sc_stride/4) + + col + sc->sc_xmargin; + + for (i = 0; i < sc->sc_font_height; i++) { + for (j = 0, k = 7; j < 8; j++, k--) { + if ((p[i] & (1 << k)) == 0) + *(addr + j) = ps3fb_pix32(ps3fb_background(a)); + else + *(addr + j) = ps3fb_pix32(ps3fb_foreground(a)); + } + addr += (sc->sc_stride/4); + } + + return (0); +} + +static int +ps3fb_puts(video_adapter_t *adp, vm_offset_t off, u_int16_t *s, int len) +{ + int i; + + for (i = 0; i < len; i++) { + ps3fb_putc(adp, off + i, s[i] & 0xff, (s[i] & 0xff00) >> 8); + } + return (0); +} + +static int +ps3fb_putm(video_adapter_t *adp, int x, int y, uint8_t *pixel_image, + uint32_t pixel_mask, int size, int width) +{ + struct ps3fb_softc *sc; + int i, j, k; + uint32_t fg, bg; + uint32_t *addr; + + sc = (struct ps3fb_softc *)adp; + addr = (uint32_t *)sc->sc_addr + + (y + sc->sc_ymargin)*(sc->sc_stride/4) + + x + sc->sc_xmargin; + + fg = ps3fb_pix32(ps3fb_foreground(SC_NORM_ATTR)); + bg = ps3fb_pix32(ps3fb_background(SC_NORM_ATTR)); + + for (i = 0; i < size && i+y < sc->sc_height - 2*sc->sc_ymargin; i++) { + for (j = 0, k = width; j < 8; j++, k--) { + if (x + j >= sc->sc_width - 2*sc->sc_xmargin) + continue; + + if (pixel_image[i] & (1 << k)) + *(addr + j) = (*(addr + j) == fg) ? bg : fg; + } + addr += (sc->sc_stride/4); + } + + return (0); +} + +/* + * Define the syscons nexus device attachment + */ +static void +ps3fb_scidentify(driver_t *driver, device_t parent) +{ + device_t child; + + /* + * Add with a priority guaranteed to make it last on + * the device list + */ + if (strcmp(installed_platform(), "ps3") == 0) + child = BUS_ADD_CHILD(parent, INT_MAX, SC_DRIVER_NAME, 0); +} + +static int +ps3fb_scprobe(device_t dev) +{ + int error; + + if (strcmp(installed_platform(), "ps3") != 0) + return (ENXIO); + + device_set_desc(dev, "System console"); + + error = sc_probe_unit(device_get_unit(dev), + device_get_flags(dev) | SC_AUTODETECT_KBD); + if (error != 0) + return (error); + + /* This is a fake device, so make sure we added it ourselves */ + return (BUS_PROBE_NOWILDCARD); +} + +static int +ps3fb_scattach(device_t dev) +{ + return (sc_attach_unit(device_get_unit(dev), + device_get_flags(dev) | SC_AUTODETECT_KBD)); +} + +static device_method_t ps3fb_sc_methods[] = { + DEVMETHOD(device_identify, ps3fb_scidentify), + DEVMETHOD(device_probe, ps3fb_scprobe), + DEVMETHOD(device_attach, ps3fb_scattach), + { 0, 0 } +}; + +static driver_t ps3fb_sc_driver = { + SC_DRIVER_NAME, + ps3fb_sc_methods, + sizeof(sc_softc_t), +}; + +static devclass_t sc_devclass; + +DRIVER_MODULE(sc, nexus, ps3fb_sc_driver, sc_devclass, 0, 0); + +/* + * Define a stub keyboard driver in case one hasn't been + * compiled into the kernel + */ +#include <sys/kbio.h> +#include <dev/kbd/kbdreg.h> + +static int dummy_kbd_configure(int flags); + +keyboard_switch_t ps3dummysw; + +static int +dummy_kbd_configure(int flags) +{ + + return (0); +} +KEYBOARD_DRIVER(ps3dummy, ps3dummysw, dummy_kbd_configure); + diff --git a/sys/powerpc/ps3/ps3bus.c b/sys/powerpc/ps3/ps3bus.c new file mode 100644 index 0000000..11dbba5 --- /dev/null +++ b/sys/powerpc/ps3/ps3bus.c @@ -0,0 +1,561 @@ +/*- + * 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. + * + * $FreeBSD$ + */ + +#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/cpu.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/pmap.h> +#include <machine/resource.h> + +#include "ps3bus.h" +#include "ps3-hvcall.h" +#include "iommu_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 resource *ps3bus_alloc_resource(device_t bus, device_t child, + int type, int *rid, u_long start, u_long end, + u_long count, u_int flags); +static int ps3bus_activate_resource(device_t bus, device_t child, int type, + int rid, struct resource *res); +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_size_t boundary, void *cookie); +static int ps3_iommu_unmap(device_t dev, bus_dma_segment_t *segs, + int nsegs, void *cookie); + +struct ps3bus_devinfo { + int bus; + int dev; + uint64_t bustype; + uint64_t devtype; + + 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, +}; + +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_driver_added, bus_generic_driver_added), + 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_alloc_resource, ps3bus_alloc_resource), + DEVMETHOD(bus_activate_resource, ps3bus_activate_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), + + { 0, 0 } +}; + +struct ps3bus_softc { + struct rman sc_mem_rman; + struct mem_region *regions; + int rcount; +}; + +static driver_t ps3bus_driver = { + "ps3bus", + ps3bus_methods, + sizeof(struct ps3bus_softc) +}; + +static devclass_t ps3bus_devclass; + +DRIVER_MODULE(ps3bus, nexus, ps3bus_driver, ps3bus_devclass, 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", -1) == 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"), ®_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 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"; + rman_init(&sc->sc_mem_rman); + + /* Get memory regions for DMA */ + mem_regions(&sc->regions, &sc->rcount, &sc->regions, &sc->rcount); + + /* + * 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; + + dinfo = malloc(sizeof(*dinfo), M_PS3BUS, + M_WAITOK | M_ZERO); + + dinfo->bus = bus; + dinfo->dev = dev; + dinfo->bustype = bustype; + dinfo->devtype = devtype; + + if (dinfo->bustype == PS3_BUSTYPE_SYSBUS) + lv1_open_device(bus, dev, 0); + + ps3bus_resources_init(&sc->sc_mem_rman, bus_index, + dev_index, dinfo); + + cdev = device_add_child(self, NULL, -1); + if (cdev == NULL) { + device_printf(self, + "device_add_child failed\n"); + free(dinfo, M_PS3BUS); + continue; + } + + mtx_init(&dinfo->iommu_mtx, "iommu", NULL, MTX_DEF); + device_set_ivars(cdev, dinfo); + } + } + + return (bus_generic_attach(self)); +} + +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, "%#lx"); + retval += resource_list_print_type(&dinfo->resources, "irq", + SYS_RES_IRQ, "%ld"); + + 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; + default: + return (EINVAL); + } + + return (0); +} + +static struct resource * +ps3bus_alloc_resource(device_t bus, device_t child, int type, int *rid, + u_long start, u_long end, u_long count, u_int flags) +{ + struct ps3bus_devinfo *dinfo; + struct ps3bus_softc *sc; + int needactivate; + struct resource *rv; + struct rman *rm; + u_long adjstart, adjend, adjcount; + struct resource_list_entry *rle; + + sc = device_get_softc(bus); + dinfo = device_get_ivars(child); + needactivate = flags & RF_ACTIVE; + flags &= ~RF_ACTIVE; + + 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; + + rm = &sc->sc_mem_rman; + break; + case SYS_RES_IRQ: + return (resource_list_alloc(&dinfo->resources, bus, child, + type, rid, start, end, count, flags)); + default: + device_printf(bus, "unknown resource request from %s\n", + device_get_nameunit(child)); + return (NULL); + } + + rv = rman_reserve_resource(rm, adjstart, adjend, adjcount, flags, + child); + if (rv == NULL) { + device_printf(bus, + "failed to reserve resource %#lx - %#lx (%#lx)" + " for %s\n", adjstart, adjend, adjcount, + device_get_nameunit(child)); + return (NULL); + } + + rman_set_rid(rv, *rid); + + if (needactivate) { + if (bus_activate_resource(child, type, *rid, rv) != 0) { + device_printf(bus, + "failed to activate resource for %s\n", + device_get_nameunit(child)); + rman_release_resource(rv); + return (NULL); + } + } + + return (rv); +} + +static int +ps3bus_activate_resource(device_t bus, device_t child, int type, int rid, + struct resource *res) +{ + void *p; + + if (type == SYS_RES_IRQ) + return (bus_activate_resource(bus, type, rid, res)); + + if (type == SYS_RES_MEMORY) { + vm_offset_t start; + + start = (vm_offset_t) rman_get_start(res); + + if (bootverbose) + printf("ps3 mapdev: start %zx, len %ld\n", start, + rman_get_size(res)); + + p = pmap_mapdev(start, (vm_size_t) rman_get_size(res)); + if (p == NULL) + return (ENOMEM); + rman_set_virtual(res, p); + rman_set_bustag(res, &bs_be_tag); + rman_set_bushandle(res, (u_long)p); + } + + return (rman_activate_resource(res)); +} + +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; + + if (dinfo->bustype != PS3_BUSTYPE_SYSBUS) + 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 */ + + for (i = 0; i < sc->rcount; i++) { + err = lv1_allocate_device_dma_region(dinfo->bus, dinfo->dev, + sc->regions[i].mr_size, 24 /* log_2(16 MB) */, flags, + &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); + + 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_size_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); +} + diff --git a/sys/powerpc/ps3/ps3bus.h b/sys/powerpc/ps3/ps3bus.h new file mode 100644 index 0000000..b11ff8a --- /dev/null +++ b/sys/powerpc/ps3/ps3bus.h @@ -0,0 +1,65 @@ +/*- + * 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. + * + * $FreeBSD$ + */ + +#ifndef _POWERPC_PS3_PS3BUS_H +#define _POWERPC_PS3_PS3BUS_H + +enum { + PS3BUS_IVAR_BUS, + PS3BUS_IVAR_DEVICE, + PS3BUS_IVAR_BUSTYPE, + PS3BUS_IVAR_DEVTYPE +}; + +#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) + +/* 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/ps3pic.c b/sys/powerpc/ps3/ps3pic.c new file mode 100644 index 0000000..5d56d3e --- /dev/null +++ b/sys/powerpc/ps3/ps3pic.c @@ -0,0 +1,254 @@ +/*- + * 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. + * + * $FreeBSD$ + */ + +#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); +static void ps3pic_eoi(device_t, u_int); +static void ps3pic_ipi(device_t, u_int); +static void ps3pic_mask(device_t, u_int); +static void ps3pic_unmask(device_t, u_int); +static uint32_t ps3pic_id(device_t dev); + +struct ps3pic_softc { + uint64_t *bitmap_thread0; + uint64_t *mask_thread0; + uint64_t *bitmap_thread1; + uint64_t *mask_thread1; + + uint64_t sc_ipi_outlet[2]; + 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_id, ps3pic_id), + 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) +}; + +static devclass_t ps3pic_devclass; + +DRIVER_MODULE(ps3pic, nexus, ps3pic_driver, ps3pic_devclass, 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", -1) == 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)); +#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_outlet[0], + 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_outlet[0], + sc->sc_ipi_outlet[1], 0); +#endif + + powerpc_register_pic(dev, sc->sc_ipi_outlet[0]); + root_pic = dev; /* PS3s have only one PIC */ + + 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 = sc->bitmap_thread0[0]; + mask = sc->mask_thread0[0]; + } else { + bitmap = sc->bitmap_thread1[0]; + mask = sc->mask_thread1[0]; + } + + 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) +{ + struct ps3pic_softc *sc; + + sc = device_get_softc(dev); + sc->sc_vector[irq] = vector; + + ps3pic_unmask(dev, irq); +} + +static void +ps3pic_eoi(device_t dev, u_int irq) +{ + 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) +{ + struct ps3pic_softc *sc; + uint64_t ppe; + + sc = device_get_softc(dev); + + /* Do not mask IPIs! */ + if (irq == sc->sc_ipi_outlet[0]) + return; + + sc->mask_thread0[0] &= ~(1UL << (63 - irq)); + 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) +{ + struct ps3pic_softc *sc; + uint64_t ppe; + + sc = device_get_softc(dev); + sc->mask_thread0[0] |= (1UL << (63 - irq)); + 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 uint32_t +ps3pic_id(device_t dev) +{ + return (0); +} + |