/* $ZEL: sis1100_init_remote.c,v 1.11 2002/05/28 21:54:57 wuestner Exp $ */

#include "Copyright"

#include <linux/config.h>
#include <linux/module.h>
#include <linux/pci.h>

#include <dev/pci/sis1100var.h>

#define SIS_MODIFICATION

void
sis1100_synch_s_handler(void* data)
{
    struct SIS1100_softc* sc=(struct SIS1100_softc*)data;
    u_int32_t status;

/* reenable IRQs, but not sis1100irq_prot_end */
    sis1100_enable_irq(sc, 0, irq_synch_chg|irq_reset_req|irq_prot_l_err);
    status=sis1100readreg(sc, sr);

    printk(KERN_INFO "SIS1100[%d]: synch_s_handler: status=0x%08x\n",
        sc->unit, status);

    if ((status&sr_synch)==sr_synch) sis1100_init_remote(sc);
}

void
sis1100_synch_handler(unsigned long data)
{
    struct SIS1100_softc* sc=(struct SIS1100_softc*)data;
    int res;
/*
    printk(KERN_NOTICE "SIS1100[%d]: synch_handler called\n");
*/
    res=schedule_task(&sc->link_up_task);
    if (!res) printk(KERN_NOTICE
        "SIS1100[%d]: sis1100_synch_s_handler not scheduled\n", sc->unit);
}
/*
static u_int32_t read_sdram(struct SIS1100_softc* sc, loff_t addr)
{
    u_int32_t val;
    int res;
    res=sis1100_tmp_read(sc, addr, -1, 4, 6, &val);
    return val;
}

static void write_sdram(struct SIS1100_softc* sc, loff_t addr, u_int32_t val)
{
        int res;
        res=sis1100_tmp_write(sc, addr, -1, 4, 6, val);
        if (res)
                printk(KERN_INFO "SIS1100[%d]: write_sdram res=%d\n",
                                sc->unit,res);
}

static size_t sdram_size(struct SIS1100_softc* sc)
{
    loff_t addr=4;
    u_int32_t tval=0xa5a5a5a5;
    u_int32_t val_0, val_a, val_ax, val_0x;

    val_0=read_sdram(sc, 0);
    write_sdram(sc, 0, ~tval);
    do {
        addr<<=1;
        val_a=read_sdram(sc, addr);
        write_sdram(sc, addr, tval);
        val_ax=read_sdram(sc, addr);
        val_0x=read_sdram(sc, 0);
        write_sdram(sc, addr, val_a);
    } while ((val_ax==tval) && (val_0x!=tval));
    write_sdram(sc, 0, val_0);
    return addr;
}
*/
static int sharc_present(struct SIS1100_softc* sc)
{
    u_int32_t dsp_sc;
    int res;

    res=sis3100readreg(sc, dsp_sc, &dsp_sc, 0);
    if (res) {
        printk(KERN_INFO "SIS1100[%d]: read dsp_sc: res=%d\n", sc->unit, res);
        return 0;
    }
    return !!(dsp_sc&dsp_available);
}

char* rnames[]={"PCI", "VME", "CAMAC"};

void
sis1100_init_remote(struct SIS1100_softc* sc)
{
    u_int32_t ident, error, balance, typ, hv, fk, fv;;

    down(&sc->sem_hw);

    /*sis1100writereg(sc, cr, cr_rem_reset);*/ /* reset remote */
    flush_fifo(sc, "init_remote" , 0); /* clear local fifo */
    sis1100writereg(sc, p_balance, 0);
    sis1100readreg(sc, prot_error);

    ident=plxreadlocal0(sc, 0x800);
    error=sis1100readreg(sc, prot_error);
    balance=sis1100readreg(sc, p_balance);
    up(&sc->sem_hw);

    if (error || balance) {
        printk(KERN_ERR "SIS1100[%d]: error reading remote ident\n", sc->unit);
        printk(KERN_ERR "error=0x%x balance=%d\n", error, balance);
        flush_fifo(sc, "after reading ident" , 0); /* clear local fifo */
        /*dump_glink_status(sc, "init remote");*/
        return;
    }

    typ=ident&0xff;
    hv=(ident>>8)&0xff;
    fk=(ident>>16)&0xff;
    fv=(ident>>24)&0xff;
    printk(KERN_INFO "SIS1100[%d]:%s: remote ident: 0x%08x\n",
            sc->unit, sc->pcidev->slot_name, ident);
    if ((typ>0) && (typ<4))
        printk(KERN_INFO "SIS1100[%d]:%s: remote is %s\n",
                sc->unit, sc->pcidev->slot_name, rnames[typ-1]);
    else
        printk(KERN_ERR "SIS1100[%d]:%s: unknown remote type %d\n",
                sc->unit, sc->pcidev->slot_name, ident&0xff);
    printk(KERN_INFO "SIS1100[%d]: remote HW_ver %d FW_code %d FW_ver %d\n",
                sc->unit, hv, fk, fv);
    sc->remote_ident=ident;

#ifndef SIS_MODIFICATION
    if (sc->remote_ident!=0x03010102)  {
        printk(KERN_ERR "SIS1100[%d]: remote: wrong device\n", sc->unit);
        return;
    }
#endif

#ifdef SIS_MODIFICATION
    switch (sc->remote_ident&0xffff00) { /* HW version and FW code */
    	case 0x010100:
    	    switch ((sc->remote_ident>>24)&0xff) { /* FW version */
                case 1:
                case 2:
                        printk(KERN_ERR "SIS1100[%d]: remote: old Firmware Version !\n", sc->unit);
                        return;
                case 3:
                       printk(KERN_INFO "SIS1100[%d]: remote: Firmware Version not the newest !\n", sc->unit);
    	    	    break;
                case 4:
                case 5:
                       printk(KERN_INFO "SIS1100[%d]: remote: actual Firmware Version  !\n", sc->unit);
    	    	    break;
    	    	default:
                        printk(KERN_ERR "SIS1100[%d]: remote:  Firmware Version not known !\n", sc->unit);
                        return;
             }
    	    break;
    	default:
    	    printk(KERN_ERR "SIS1100[%d]: remote: Hard- or Firmware not known\n",
                        sc->unit);
            return;
    }

#endif





    if (init_sdram(sc)<0) {
        return;
    }
    /*sc->sdram_size=sdram_size(sc);*/
    printk(KERN_INFO "SIS1100[%d]: size of SDRAM: 0x%Lx (%Ld MByte)\n",
        sc->unit, sc->sdram_size, sc->sdram_size>>20);

    sc->sharc_present=sharc_present(sc);
    sc->sharc_size=sc->sharc_present?0x400000:0;
    printk(KERN_INFO "SIS1100[%d]: SHARC is %spresent\n",
        sc->unit, sc->sharc_present?"":"not ");

    down(&sc->sem_hw);
    plxwritelocal0(sc, 0x800+0x104, 0xfe01);
    up(&sc->sem_hw);
    sc->old_remote_ok=sc->remote_ok;
    sc->remote_ok=1;
    schedule_task(&sc->vme_irq_task);
}
