root/sys/arch/playstation2/playstation2/sifbios.c
/*      $NetBSD: sifbios.c,v 1.11 2014/03/31 11:25:49 martin Exp $      */

/*-
 * Copyright (c) 2001 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * This code is derived from software contributed to The NetBSD Foundation
 * by UCHIYAMA Yasushi.
 *
 * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
 */

/*
 * PlayStation 2 SIF BIOS Version 2.0 interface.
 */

#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: sifbios.c,v 1.11 2014/03/31 11:25:49 martin Exp $");

#include <sys/param.h>
#include <sys/systm.h>

#include <playstation2/playstation2/sifbios.h>
#include <playstation2/playstation2/interrupt.h>

#ifdef DEBUG
#define STATIC
#else
#define STATIC static
#endif

#define SIFBIOS_ENTRY_PTR       MIPS_PHYS_TO_KSEG0(0x00001000)
#define SIFBIOS_SIGNATURE_PTR   MIPS_PHYS_TO_KSEG1(0x00001004)
#define SIFBIOS_SIGNATURE       (('P' << 0)|('S' << 8)|('2' << 16)|('b' << 24))

STATIC int (*__sifbios_call)(int, void *);
#define CALL(t, n, a)   ((t)(*__sifbios_call)((n), (void *)(a)))

STATIC void sifbios_rpc_callback(void *, int);
STATIC int sifbios_rpc_call(int, void *, int *);

void
sifbios_init(void)
{
        /* check BIOS exits */
        if (*(u_int32_t *)SIFBIOS_SIGNATURE_PTR != SIFBIOS_SIGNATURE)
                panic("SIFBIOS not found");
        
        __sifbios_call = *((int (**)(int, void*))SIFBIOS_ENTRY_PTR);
}

int
sifbios_rpc_call(int callno, void *arg, int *result)
{
        volatile int done = 0;
        int retry;
        struct {
                int result;
                void *arg;
                void (*callback)(void *, int);
                volatile void *callback_arg;
        } __attribute__((__packed__, __aligned__(4))) sifbios_arg = {
                arg:            arg,
                callback:       sifbios_rpc_callback,
                callback_arg:   (volatile void *)&done,
        };
        
        /* call SIF BIOS */
        retry = 100;
        while (CALL(int, callno, &sifbios_arg) != 0 && --retry > 0)
                delay(20000);   /* .02 sec. for slow IOP */

        if (retry == 0) {
                printf("SIF BIOS call %d failed\n", callno);
                goto error;
        }

        /* wait IOP response (1 sec.) */
        _sif_call_start();
        retry = 10000;
        while (!done && --retry > 0)
                delay(100);
        _sif_call_end();

        if (retry == 0) {
                printf("IOP not respond (callno = %d)\n", callno);
                goto error;
        }
        
        *result = sifbios_arg.result;

        return (0);

 error:
        return (-1);
}

void
sifbios_rpc_callback(void *arg, int result)
{
        int *done = (int *)arg;
        
        *done = 1;
}

/*
 * System misc.
 */
int
sifbios_getver(void)
{

        return CALL(int, 0, 0);
}

void
sifbios_halt(int mode)
{
        int sifbios_arg = mode;

        CALL(void, 1, &sifbios_arg);
}

void
sifbios_setdve(int mode)
{
        int sifbios_arg = mode;

        CALL(void, 2, &sifbios_arg);
}

void
sifbios_putchar(int c)
{
        int sifbios_arg = c;

        CALL(void, 3, &sifbios_arg);
}

int
sifbios_getchar(void)
{

        return CALL(int, 4, 0);
}

/*
 * SIF DMA
 */
int
sifdma_init(void)
{

        return CALL(int, 16, 0);
}

void
sifdma_exit(void)
{

        CALL(void, 17, 0);
}

/* queue DMA request to SIFBIOS. returns queue identifier. */
sifdma_id_t
sifdma_queue(struct sifdma_transfer *arg, int n)
{
        struct {
                void *arg;      /* pointer to sifdma_transfer array */
                int n;          /* # of elements */
        } __attribute__((__packed__, __aligned__(4))) sifbios_arg = {
                arg:    arg,
                n:      n
        };

        return CALL(sifdma_id_t, 18, &sifbios_arg);
}

/* 
 * status of DMA 
 *      >0 ... queued. not kicked.
 *       0 ... DMA executing.
 *      <0 ... DMA done.
 */
int
sifdma_stat(sifdma_id_t id)
{
        u_int32_t sifbios_arg = id;

        return CALL(int, 19, &sifbios_arg);
}

/* reset DMA channel */
void
sifdma_reset(void)
{

        CALL(void, 20, 0);
}

/*
 * SIF CMD
 */
int
sifcmd_init(void)
{
        
        return CALL(int, 32, 0);
}

void
sifcmd_exit(void)
{

        CALL(void, 33, 0);
}

sifdma_id_t
sifcmd_queue(sifcmd_sw_t sw, vaddr_t cmd_pkt_addr, size_t cmd_pkt_sz,
    vaddr_t src_addr, vaddr_t dst_addr, vsize_t buf_sz)
{
        struct {
                sifcmd_sw_t sw;
                vaddr_t cmd_pkt_addr;   /* command buffer */
                size_t cmd_pkt_sz;
                vaddr_t src_addr;       /* data buffer */
                vaddr_t dst_addr;
                vsize_t buf_sz;
        } __attribute__((__packed__, __aligned__(4))) sifbios_arg = {
                sw:             sw,
                cmd_pkt_addr:   cmd_pkt_addr,
                cmd_pkt_sz:     cmd_pkt_sz,
                src_addr:       src_addr,
                dst_addr:       dst_addr,
                buf_sz:         buf_sz,
        };

        return CALL(sifdma_id_t, 34, &sifbios_arg);
}

/* interrupt handler of DMAC channel 5 (SIF0) */
int
sifcmd_intr(void *arg)
{

        CALL(void, 35, 0);

        return (1);
}

void
sifcmd_establish(sifcmd_sw_t sw, struct sifcmd_callback_holder *holder)
{
        struct {
                sifcmd_sw_t sw;
                sifcmd_callback_t func;
                void *arg;
        } __attribute__((__packed__, __aligned__(4))) sifbios_arg = {
                sw:     sw,
                func:   holder->func,
                arg:    holder->arg,
        };
        
        CALL(void, 36, &sifbios_arg);
}

void
sifcmd_disestablish(sifcmd_sw_t sw)
{
        u_int32_t sifbios_arg = sw;

        CALL(void, 37, &sifbios_arg);
}

struct sifcmd_callback_holder *
sifcmd_handler_init(struct sifcmd_callback_holder *holder, int n)
{
        struct {
                void *holder;
                int n;          /* # of slot */
        } __attribute__((__packed__, __aligned__(4))) sifbios_arg = {
                holder: holder,
                n:      n,
        };

        /* returns old holder */
        return CALL(struct sifcmd_callback_holder *, 38, &sifbios_arg);
}

/*
 * SIF RPC
 */
int
sifrpc_init(void)
{

        return CALL(int, 48, 0);
}

void
sifrpc_exit(void)
{

        CALL(void, 49, 0);
}

int
sifrpc_receive_buffer(struct sifrpc_receive *_cookie, void *src_iop,
    void *dst_ee, size_t sz, u_int32_t rpc_mode, void (*end_func)(void *),
    void *end_arg)
{
        struct {
                void *_cookie;
                void *src_iop;
                void *dst_ee;
                size_t sz;
                u_int32_t rpc_mode;
                sifrpc_endfunc_t end_func;
                void *end_arg;
        } __attribute__((__packed__, __aligned__(4))) sifbios_arg = {
                _cookie:        _cookie,
                src_iop:        src_iop,
                dst_ee:         dst_ee,
                sz:             sz,
                rpc_mode:       rpc_mode,
                end_func:       end_func,
                end_arg:        end_arg,
        };

        return CALL(int, 50, &sifbios_arg);
}

int
sifrpc_bind(struct sifrpc_client *_cookie, sifrpc_id_t rpc_id,
    u_int32_t rpc_mode, void (*end_func)(void *), void *end_arg)
{
        struct {
                void *_cookie;          /* filled by this call */
                sifrpc_id_t rpc_id;     /* specify server RPC id */
                u_int32_t rpc_mode;
                sifrpc_endfunc_t end_func;
                void *end_arg;
        } __attribute__((__packed__, __aligned__(4))) sifbios_arg = {
                _cookie:        _cookie,
                rpc_id:         rpc_id,
                rpc_mode:       rpc_mode,
                end_func:       end_func,
                end_arg:        end_arg,
        };

        return CALL(int, 51, &sifbios_arg);
}

int
sifrpc_call(struct sifrpc_client *_cookie, sifrpc_callno_t call_no,
    u_int32_t rpc_mode, void *sendbuf, size_t sendbuf_sz, void *recvbuf,
    size_t recvbuf_sz, void (*end_func)(void *), void *end_arg)
{
        struct {
                struct sifrpc_client *_cookie;  /* binded client cookie */
                sifrpc_callno_t call_no; /* passed to service function arg. */
                u_int32_t rpc_mode;
                void *sendbuf;
                size_t sendbuf_sz;
                void *recvbuf;
                size_t recvbuf_sz;
                sifrpc_endfunc_t end_func;
                void *end_arg;
        } __attribute__((__packed__, __aligned__(4))) sifbios_arg = {
                _cookie:        _cookie,
                call_no:        call_no,
                rpc_mode:       rpc_mode,
                sendbuf:        sendbuf,
                sendbuf_sz:     sendbuf_sz,
                recvbuf:        recvbuf,
                recvbuf_sz:     recvbuf_sz,
                end_func:       end_func,
                end_arg:        end_arg,
        };

        return CALL(int, 52, &sifbios_arg);
}

int
sifrpc_stat(struct sifrpc_header *_cookie)
{
        void *sifbios_arg = _cookie;

        return CALL(int, 53, &sifbios_arg);
}

void
sifrpc_establish(struct sifrpc_server_system *queue, void (*end_func)(void *),
    void *end_arg)
{
        struct {
                struct sifrpc_server_system *queue;
                sifrpc_endfunc_t end_func;
                void *end_arg;
        } __attribute__((__packed__, __aligned__(4))) sifbios_arg = {
                queue:          queue,
                end_func:       end_func,
                end_arg:        end_arg,
        };

        CALL(void, 54, &sifbios_arg);
}

void
sifrpc_register_service(struct sifrpc_server_system *queue,
    struct sifrpc_server *server, sifrpc_id_t rpc_id,
    void *(*service_func)(sifrpc_callno_t, void *, size_t), void *service_arg,
    void *(*cancel_func)(sifrpc_callno_t, void *, size_t), void *cancel_arg)
{
        struct {
                void *server;
                sifrpc_id_t rpc_id;
                sifrpc_rpcfunc_t service_func;
                void *service_arg;
                sifrpc_rpcfunc_t cancel_func;
                void *cancel_arg;
                void *receive_queue;
        } __attribute__((__packed__, __aligned__(4))) sifbios_arg = {
                server:         server,
                rpc_id:         rpc_id,
                service_func:   service_func,
                service_arg:    service_arg,
                cancel_func:    cancel_func,
                cancel_arg:     cancel_arg,
                receive_queue:  queue,
        };
        
        CALL(void, 55, &sifbios_arg);
}

void
sifrpc_unregister_service(struct sifrpc_server_system *queue,
    struct sifrpc_server *server)
{
        struct {
                void *server;
                void *receive_queue;
        } __attribute__((__packed__, __aligned__(4))) sifbios_arg = {
                server:         server,
                receive_queue:  queue,
        };

        CALL(void, 56, &sifbios_arg);
}

void
sifrpc_disestablish(struct sifrpc_server_system *queue)
{
        void *sifbios_arg = queue;

        CALL(void, 57, &sifbios_arg);
}

struct sifrpc_server *
sifrpc_dequeue(struct sifrpc_server_system *queue)
{
        void *sifbios_arg = queue;

        return CALL(struct sifrpc_server *, 58, &sifbios_arg);
}

void
sifrpc_dispatch_service(struct sifrpc_server *server)
{
        void *sifbios_arg = server;

        CALL(void, 59, &sifbios_arg);
}

/*
 * IOP memory
 */
int
iopmem_init(void)
{
        int result;

        sifbios_rpc_call(64, 0, &result);

        return (0);
}

paddr_t
iopmem_alloc(psize_t sz)
{
        int result, sifbios_arg = sz;

        if (sifbios_rpc_call(65, (void *)&sifbios_arg, &result) < 0)
                return (paddr_t)0;
        /* returns allocated IOP physical addr */
        return (paddr_t)result;
}

int
iopmem_free(paddr_t addr)
{
        int result, sifbios_arg = addr;

        sifbios_rpc_call(66, (void *)&sifbios_arg, &result);

        return (result);
}