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