root/sys/arch/amiga/amiga/gayle.c
/*      $NetBSD: gayle.c,v 1.8 2015/01/16 23:34:11 phx Exp $    */

/* public domain */

#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: gayle.c,v 1.8 2015/01/16 23:34:11 phx Exp $");

/*
 * Gayle management - provide functions for use in the drivers that utilise
 * the chip.
 *
 * These overly complicated routines try to deal with a few variants of
 * Gayle chip that exists. 
 */
#include <sys/bus.h>
#include <sys/systm.h>

#include <machine/cpu.h>

#include <amiga/amiga/gayle.h>
#include <amiga/dev/zbusvar.h>

/* #define GAYLE_DEBUG 1 */

struct gayle_tag {
        struct bus_space_tag gayle_io_bst;
        bus_space_tag_t gayle_io_t;
        bus_space_handle_t gayle_io_h;

        struct bus_space_tag gayle_isr_bst;
        bus_space_tag_t gayle_isr_t;
        bus_space_handle_t gayle_isr_h;
};

typedef struct gayle_tag *gayle_tag_t;

/*
 * Having these as static variables is ugly, but they don't fit into
 * driver's softc, as the chip might be utilised by many different drivers.
 * And since we can only have one Gayle per system it should be okay.
 */
static struct gayle_tag gayle;
static gayle_tag_t gayle_t = NULL;

/*
 * Any module that uses gayle should call gayle_init() before using anything
 *   related to gayle. gayle_init() can be called multiple times.
 */
void
gayle_init(void) {
        bus_addr_t gayle_vbase;

        if (gayle_t != NULL)
                return;

#ifdef GAYLE_DEBUG
        aprint_normal("gayle: doing init\n");
#endif /* GAYLE_DEBUG */

        gayle_t = &gayle;

        gayle_vbase = (bus_addr_t) __UNVOLATILE(ztwomap(GAYLE_REGS_BASE));

        gayle_t->gayle_io_bst.base = gayle_vbase;
        gayle_t->gayle_io_bst.absm = &amiga_bus_stride_0x1000;
        gayle_t->gayle_io_t = &(gayle_t->gayle_io_bst);

        bus_space_map(gayle_t->gayle_io_t, 0, 0x4, 0, &gayle_t->gayle_io_h);

        /* 
         * The A4000 variant of Gayle has interrupt status register at offset
         * +0x1000 from IDE registers.
         * XXX: in fact, on A4000 we should initialise only this part...
         */
        if (is_a4000()) {
                gayle_t->gayle_isr_bst.base = (bus_addr_t) __UNVOLATILE(ztwomap(
                    GAYLE_IDE_BASE_A4000+GAYLE_IDE_INTREQ_A4000));
                gayle_t->gayle_isr_bst.absm = &amiga_bus_stride_1;
                gayle_t->gayle_isr_t = &(gayle_t->gayle_isr_bst);

                bus_space_map(gayle_t->gayle_isr_t, 0, 0x1, 0,
                    &(gayle_t->gayle_isr_h));
        } else {
                bus_space_subregion(gayle_t->gayle_io_t, gayle_t->gayle_io_h, 
                    GAYLE_INTREQ, 0x1, &(gayle_t->gayle_isr_h));

                gayle_t->gayle_isr_bst = gayle_t->gayle_io_bst;
                gayle_t->gayle_isr_t = gayle_t->gayle_io_t;
        }

}

uint8_t 
gayle_intr_status(void)
{
        uint8_t rv;

        rv = bus_space_read_1(gayle_t->gayle_isr_t, gayle_t->gayle_isr_h, 0); 
#ifdef GAYLE_DEBUG
        aprint_normal("gayle: intr status %x\n", rv);
#endif /* GAYLE_DEBUG */

        return rv;
}

uint8_t
gayle_intr_enable_read(void)
{
        uint8_t rv;

        rv = bus_space_read_1(gayle_t->gayle_io_t, gayle_t->gayle_io_h, 
            GAYLE_INTENA);
#ifdef GAYLE_DEBUG
        aprint_normal("gayle: intr enable register read %x\n", rv);
#endif /* GAYLE_DEBUG */

        return rv;
}

void
gayle_intr_enable_write(uint8_t val)
{
#ifdef GAYLE_DEBUG
        aprint_normal("gayle: intr enable register write %x\n", val);
#endif /* GAYLE_DEBUG */
        bus_space_write_1(gayle_t->gayle_io_t, gayle_t->gayle_io_h, 
            GAYLE_INTENA, val);
}

void
gayle_intr_enable_set(uint8_t bits)
{
        uint8_t val;
        val = gayle_intr_enable_read();
        gayle_intr_enable_write(val | bits);
}

void 
gayle_intr_ack(uint8_t val)
{
#ifdef GAYLE_DEBUG
        aprint_normal("gayle: intr ack write %x\n", val);
#endif /* GAYLE_DEBUG */
        bus_space_write_1(gayle_t->gayle_io_t, gayle_t->gayle_io_h, 
            GAYLE_INTREQ, val);
}

uint8_t
gayle_pcmcia_status_read(void)
{
        uint8_t rv;

        rv = bus_space_read_1(gayle_t->gayle_io_t, gayle_t->gayle_io_h, 
            GAYLE_PCC_STATUS);
#ifdef GAYLE_DEBUG
        aprint_normal("gayle: pcmcia status read %x\n", rv);
#endif /* GAYLE_DEBUG */

        return rv;
}

void 
gayle_pcmcia_status_write(uint8_t val)
{
#ifdef GAYLE_DEBUG
        aprint_normal("gayle: pcmcia status write %x\n", val);
#endif /* GAYLE_DEBUG */
        bus_space_write_1(gayle_t->gayle_io_t, gayle_t->gayle_io_h, 
            GAYLE_PCC_STATUS, val);
}

void 
gayle_pcmcia_config_write(uint8_t val)
{
#ifdef GAYLE_DEBUG
        aprint_normal("gayle: pcmcia config write %x\n", val);
#endif /* GAYLE_DEBUG */
        bus_space_write_1(gayle_t->gayle_io_t, gayle_t->gayle_io_h, 
            GAYLE_PCC_CONFIG, val);
}

uint8_t
gayle_pcmcia_config_read(void)
{
        uint8_t rv;

        rv = bus_space_read_1(gayle_t->gayle_io_t, gayle_t->gayle_io_h, 
            GAYLE_PCC_CONFIG);
#ifdef GAYLE_DEBUG
        aprint_normal("gayle: pcmcia config read %x\n", rv);
#endif /* GAYLE_DEBUG */

        return rv;
}