26bdeaf2d3
Pointed out by: j@uriah.heep.sax.de (J Wunsch)
452 lines
11 KiB
C
452 lines
11 KiB
C
/* $NetBSD$ */
|
|
/*
|
|
* [NetBSD for NEC PC98 series]
|
|
* Copyright (c) 1994, 1995, 1996 NetBSD/pc98 porting staff.
|
|
* 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.
|
|
* 3. The name of the author may not be used to endorse or promote products
|
|
* derived from this software without specific prior written permission.
|
|
*
|
|
* 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.
|
|
*/
|
|
/*
|
|
* Copyright (c) 1994, 1995, 1996 Naofumi HONDA. All rights reserved.
|
|
*/
|
|
|
|
#ifdef __NetBSD__
|
|
#include <dev/isa/isadmareg.h>
|
|
#include <dev/isa/bs/bsif.h>
|
|
#include <dev/isa/bs/bshw.lst>
|
|
#endif
|
|
#ifdef __FreeBSD__
|
|
#include <i386/isa/ic/i8237.h>
|
|
#include <i386/isa/bs/bsif.h>
|
|
#include <i386/isa/bs/bshw.lst>
|
|
#include <machine/clock.h>
|
|
#include <i386/i386/cons.h>
|
|
#endif
|
|
|
|
static struct bs_softc *gbsc;
|
|
|
|
/**************************************************
|
|
* DECLARATION
|
|
**************************************************/
|
|
static void bshw_force_bsmode __P((struct bs_softc *));
|
|
|
|
/**************************************************
|
|
* STATIC VAL
|
|
**************************************************/
|
|
static int irq_tbl[] = { 3, 5, 6, 9, 12, 13 };
|
|
|
|
/**************************************************
|
|
* SCSI CMD BRANCH
|
|
**************************************************/
|
|
#define RS (BSSAT | BSSMIT | BSLINK | BSREAD)
|
|
#define WS (BSSAT | BSSMIT | BSLINK)
|
|
#define EOK (BSERROROK)
|
|
|
|
u_int8_t bshw_cmd[256] = {
|
|
/* 0 1 2 3 4 5 6 7 8 9 A B C E D F */
|
|
/*0*/0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,RS ,0 ,WS ,0 ,0 ,0 ,0 ,0 ,
|
|
/*1*/0 ,0 ,EOK,0 ,0 ,0 ,0 ,0 ,0 ,0 ,EOK,0 ,0 ,0 ,0 ,0 ,
|
|
/*2*/0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,RS ,0 ,WS ,0 ,0 ,0 ,0 ,0 ,
|
|
/*3*/0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,
|
|
/*4*/0 ,0 ,EOK,EOK,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,
|
|
/*5*/0 ,0 ,0 ,0 ,EOK,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,
|
|
/*6*/0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,
|
|
/*7*/0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,
|
|
/*8*/0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,
|
|
/*9*/0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,
|
|
/*A*/0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,
|
|
/*B*/0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,
|
|
/*C*/0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,
|
|
/*D*/0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,
|
|
/*E*/0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,
|
|
/*F*/0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,
|
|
};
|
|
|
|
#undef RS
|
|
#undef WS
|
|
#undef EOK
|
|
|
|
/**********************************************
|
|
* init
|
|
**********************************************/
|
|
static void
|
|
bshw_force_bsmode(bsc)
|
|
struct bs_softc *bsc;
|
|
{
|
|
|
|
if (bsc->sc_flags & BSBSMODE)
|
|
return;
|
|
bsc->sc_flags |= BSBSMODE;
|
|
|
|
/*
|
|
* If you have memory over 16M, some stupid boards always force to
|
|
* use the io polling mode. Check such a case and change mode into
|
|
* bus master DMA. However this depends heavily on the board's
|
|
* specifications!
|
|
*/
|
|
|
|
if (bsc->sc_hw->dma_init && ((*bsc->sc_hw->dma_init)(bsc)))
|
|
printf("%s change mode using external DMA (%x)\n",
|
|
bsc->sc_dvname, (u_int)read_wd33c93(bsc, 0x37));
|
|
}
|
|
|
|
#define RESET_DEFAULT 2000
|
|
|
|
int
|
|
bshw_chip_reset(bsc)
|
|
struct bs_softc *bsc;
|
|
{
|
|
int ct;
|
|
u_int8_t aux;
|
|
|
|
bshw_lock(bsc);
|
|
|
|
bshw_abort_cmd(bsc);
|
|
delay(10000);
|
|
|
|
bshw_get_auxstat(bsc);
|
|
bshw_get_busstat(bsc);
|
|
|
|
write_wd33c93(bsc, wd3s_oid, IDR_EHP | bsc->sc_cspeed | bsc->sc_hostid);
|
|
write_wd33c93(bsc, wd3s_cmd, WD3S_RESET);
|
|
|
|
for (ct = RESET_DEFAULT; ct > 0; ct--)
|
|
{
|
|
aux = bshw_get_auxstat(bsc);
|
|
if (aux != 0xff && (aux & STR_INT))
|
|
{
|
|
if (bshw_get_busstat(bsc) == 0)
|
|
break;
|
|
|
|
write_wd33c93(bsc, wd3s_cmd, WD3S_RESET);
|
|
}
|
|
delay(1);
|
|
}
|
|
|
|
if (ct == 0)
|
|
{
|
|
bshw_unlock(bsc);
|
|
return ENXIO;
|
|
}
|
|
|
|
bshw_force_bsmode(bsc);
|
|
|
|
write_wd33c93(bsc, wd3s_tout, BSHW_SEL_TIMEOUT);
|
|
write_wd33c93(bsc, wd3s_sid, SIDR_RESEL);
|
|
bsc->sc_flags |= BSDMATRANSFER;
|
|
write_wd33c93(bsc, wd3s_ctrl, CR_DEFAULT);
|
|
write_wd33c93(bsc, wd3s_synch, 0);
|
|
|
|
bshw_get_auxstat(bsc);
|
|
bsc->sc_busstat = bshw_get_busstat(bsc);
|
|
bshw_unlock(bsc);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* scsi bus hard reset */
|
|
#define TWIDDLEWAIT 10000
|
|
static int tw_pos;
|
|
static char tw_chars[] = "|/-\\";
|
|
|
|
/* this is some jokes */
|
|
static void
|
|
twiddle_wait(void)
|
|
{
|
|
|
|
cnputc('\b');
|
|
cnputc(tw_chars[tw_pos++]);
|
|
tw_pos %= (sizeof(tw_chars) - 1);
|
|
delay(TWIDDLEWAIT);
|
|
}
|
|
|
|
static void bshw_set_vsp __P((struct bs_softc *, u_int, u_int8_t));
|
|
|
|
static void
|
|
bshw_set_vsp(bsc, chan, spva)
|
|
struct bs_softc *bsc;
|
|
u_int chan;
|
|
u_int8_t spva;
|
|
{
|
|
struct bshw *hw = bsc->sc_hw;
|
|
|
|
if (hw->sregaddr == 0)
|
|
return;
|
|
|
|
write_wd33c93(bsc, hw->sregaddr + chan, spva);
|
|
if (hw->hw_flags & BSHW_DOUBLE_DMACHAN)
|
|
write_wd33c93(bsc, hw->sregaddr + chan + 8, spva);
|
|
}
|
|
|
|
void
|
|
bshw_bus_reset(bsc)
|
|
struct bs_softc *bsc;
|
|
{
|
|
struct targ_info *ti;
|
|
int i, lpc;
|
|
|
|
if (bsc->sc_RSTdelay == 0)
|
|
bsc->sc_RSTdelay = 6 * 1000 * 1000;
|
|
else
|
|
{
|
|
/* XXX:
|
|
* second time reset will be requested by hardware failuer.
|
|
*/
|
|
bsc->sc_RSTdelay = 12 * 1000 * 1000;
|
|
}
|
|
|
|
bshw_lock(bsc);
|
|
write_wd33c93(bsc, wd3s_mbank, (bsc->sc_membank | MBR_RST) & ~MBR_IEN);
|
|
delay(500000);
|
|
write_wd33c93(bsc, wd3s_mbank, (bsc->sc_membank) & ~MBR_IEN);
|
|
bshw_unlock(bsc);
|
|
|
|
for (lpc = 0; lpc < 2; lpc ++)
|
|
{
|
|
cnputc(' ');
|
|
for (i = 0; i <= bsc->sc_RSTdelay / TWIDDLEWAIT; i++)
|
|
twiddle_wait();
|
|
cnputc('\b');
|
|
|
|
(void) read_wd33c93(bsc, wd3s_auxc);
|
|
|
|
delay(10000);
|
|
|
|
if ((read_wd33c93(bsc, wd3s_auxc) & AUXCR_RRST) == 0)
|
|
break;
|
|
|
|
printf("\nreset state still continue, wait ...");
|
|
}
|
|
|
|
for (i = 0; i < NTARGETS; i++)
|
|
{
|
|
if (ti = bsc->sc_ti[i])
|
|
{
|
|
ti->ti_sync = 0;
|
|
bshw_set_vsp(bsc, i, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* probe */
|
|
int
|
|
bshw_board_probe(bsc, drq, irq)
|
|
struct bs_softc *bsc;
|
|
u_int *drq;
|
|
u_int *irq;
|
|
{
|
|
|
|
gbsc = bsc;
|
|
#ifdef SHOW_PORT
|
|
bshw_print_port(bsc);
|
|
#endif /* SHOW_PORT */
|
|
|
|
bsc->sc_hostid = (read_wd33c93(bsc, wd3s_auxc) & AUXCR_HIDM);
|
|
|
|
if ((*irq) == IRQUNK)
|
|
*irq = irq_tbl[(read_wd33c93(bsc, wd3s_auxc) >> 3) & 7];
|
|
|
|
if ((*drq) == DRQUNK)
|
|
*drq = BUS_IOR(cmd_port) & 3;
|
|
|
|
bsc->sc_dmachan = *drq;
|
|
bsc->sc_irqmasks = (1 << (*irq));
|
|
|
|
bsc->sc_membank = read_wd33c93(bsc, wd3s_mbank);
|
|
bsc->sc_membank &= ~MBR_RST;
|
|
bsc->sc_membank |= MBR_IEN;
|
|
|
|
bsc->sc_cspeed = (read_wd33c93(bsc, wd3s_oid) & (~IDR_IDM));
|
|
switch (BSC_CHIP_CLOCK(bsc->sc_cfgflags))
|
|
{
|
|
case 0:
|
|
break;
|
|
|
|
case 1:
|
|
bsc->sc_cspeed &= ~(IDR_FS_12_15 | IDR_FS_15_20);
|
|
break;
|
|
|
|
case 2:
|
|
bsc->sc_cspeed &= ~(IDR_FS_12_15 | IDR_FS_15_20);
|
|
bsc->sc_cspeed |= IDR_FS_12_15;
|
|
break;
|
|
|
|
case 3:
|
|
bsc->sc_cspeed &= ~(IDR_FS_12_15 | IDR_FS_15_20);
|
|
bsc->sc_cspeed |= IDR_FS_15_20;
|
|
break;
|
|
}
|
|
|
|
/* XXX: host id fixed(7) */
|
|
bsc->sc_hostid = 7;
|
|
|
|
if (bshw_chip_reset(bsc))
|
|
return ENXIO;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* XXX:
|
|
* Assume the board clock rate must be 20Mhz (always satisfied, maybe)!
|
|
* Only 10M/s 6.6M/s 5.0M/s 3.3M/s for synchronus transfer speed set.
|
|
*/
|
|
#define ILLEGAL_SYNCH
|
|
#ifdef ILLEGAL_SYNCH
|
|
/* A 10 6.6 5.0 4.0 3.3 2.8 2.5 2.0 M/s */
|
|
/* X 100 150 200 250 300 350 400 500 ns */
|
|
static u_int bshw_scsi_period[] =
|
|
{0, 25, 37, 50, 62, 75, 87, 100, 125};
|
|
static u_int8_t bshw_chip_pval[] =
|
|
{0, 0xa0, 0xb0, 0x20, 0xd0, 0x30, 0xf0, 0x40, 0x50};
|
|
#else /* !ILLEGAL_SYNCH */
|
|
/* A 10 6.6 5.0 3.3 2.5 M/s */
|
|
/* X 100 150 200 300 400 ns */
|
|
static u_int bshw_scsi_period[] =
|
|
{0, 25, 37, 50, 75, 100};
|
|
static u_int8_t bshw_chip_pval[] =
|
|
{0, 0xa0, 0xb0, 0x20, 0x30, 0x40};
|
|
#endif /* !ILLEGAL_SYNCH */
|
|
|
|
void
|
|
bshw_adj_syncdata(sdp)
|
|
struct syncdata *sdp;
|
|
{
|
|
int i;
|
|
|
|
if (sdp->offset == 0 || sdp->period < 25 || sdp->period > 100)
|
|
sdp->offset = sdp->period = 0;
|
|
else
|
|
{
|
|
for (i = 0; sdp->period > bshw_scsi_period[i] + 2; i ++)
|
|
;
|
|
sdp->period = bshw_scsi_period[i];
|
|
}
|
|
}
|
|
|
|
void
|
|
bshw_set_synchronous(bsc, ti)
|
|
struct bs_softc *bsc;
|
|
struct targ_info *ti;
|
|
{
|
|
struct bshw *hw = bsc->sc_hw;
|
|
struct syncdata sd;
|
|
int i;
|
|
|
|
sd = ti->ti_syncnow;
|
|
bshw_adj_syncdata(&sd);
|
|
for (i = 0; sd.period != bshw_scsi_period[i]; i++)
|
|
;
|
|
|
|
ti->ti_sync = ((sd.offset & 0x0f) | bshw_chip_pval[i]);
|
|
bshw_set_vsp(bsc, ti->ti_id, ti->ti_sync);
|
|
|
|
if (bsc->sc_nexus == ti)
|
|
bshw_set_sync_reg(bsc, ti->ti_sync);
|
|
}
|
|
|
|
/* ctrl reg */
|
|
void
|
|
bshw_setup_ctrl_reg(bsc, flags)
|
|
struct bs_softc *bsc;
|
|
u_int flags;
|
|
{
|
|
u_int8_t regval;
|
|
|
|
regval = (flags & BS_SCSI_NOPARITY) ? CR_DEFAULT : CR_DEFAULT_HP;
|
|
if (bsc->sc_flags & BSDMATRANSFER)
|
|
regval |= CR_DMA;
|
|
write_wd33c93(bsc, wd3s_ctrl, regval);
|
|
}
|
|
|
|
/* sat command */
|
|
void
|
|
bshw_issue_satcmd(bsc, cb, link)
|
|
struct bs_softc *bsc;
|
|
struct ccb *cb;
|
|
int link;
|
|
{
|
|
int i;
|
|
|
|
BUS_IOW(addr_port, wd3s_cdb);
|
|
for (i = 0; i < cb->cmdlen - 1; i++)
|
|
BUS_IOW(ctrl_port, cb->cmd[i]);
|
|
BUS_IOW(ctrl_port, cb->cmd[i] | (link ? 1 : 0));
|
|
}
|
|
|
|
/* lock */
|
|
void
|
|
bshw_lock(bsc)
|
|
struct bs_softc *bsc;
|
|
{
|
|
|
|
bsc->sc_hwlock++;
|
|
write_wd33c93(bsc, wd3s_mbank, bsc->sc_membank & (~MBR_IEN));
|
|
}
|
|
|
|
void
|
|
bshw_unlock(bsc)
|
|
struct bs_softc *bsc;
|
|
{
|
|
|
|
if ((--bsc->sc_hwlock) <= 0)
|
|
write_wd33c93(bsc, wd3s_mbank, bsc->sc_membank);
|
|
}
|
|
|
|
/**********************************************
|
|
* DMA OPERATIONS
|
|
**********************************************/
|
|
#ifdef __NetBSD__
|
|
#include <dev/isa/bs/bshw_dma.c>
|
|
#include <dev/isa/bs/bshw_pdma.c>
|
|
#endif
|
|
#ifdef __FreeBSD__
|
|
#include <i386/isa/bs/bshw_dma.c>
|
|
#include <i386/isa/bs/bshw_pdma.c>
|
|
#endif
|
|
|
|
/**********************************************
|
|
* DEBUG
|
|
**********************************************/
|
|
/* misc */
|
|
void
|
|
bshw_print_port(bsc)
|
|
struct bs_softc * bsc;
|
|
{
|
|
int i, j;
|
|
int port = 0x0;
|
|
|
|
if (bsc == NULL)
|
|
bsc = gbsc;
|
|
|
|
printf("\n");
|
|
for (j = 0; j <= 0x70; j += 0x10)
|
|
{
|
|
printf("port %x: ", port);
|
|
for (i = 0; i < 0x10; i++)
|
|
printf("%x ", (u_int) read_wd33c93(bsc, port++));
|
|
printf("\n");
|
|
}
|
|
}
|