#include <lib/libsa/stand.h>
#include <lib/libkern/libkern.h>
#include <machine/emipsreg.h>
#include <sys/param.h>
#include <sys/disklabel.h>
#include <sys/endian.h>
#include "common.h"
#include "ace.h"
#include "start.h"
#define NSAC 2
#define SAC0 ((struct _Sac *)IDE_DEFAULT_ADDRESS)
#define SAC1 ((struct _Sac *)(IDE_DEFAULT_ADDRESS+256))
#define CF_SECBITS 9
#define CF_SECTOR_SIZE (1 << CF_SECBITS)
#define FAILED(x) (x < 0)
#define S_OK (0)
#define E_INVALID_PARAMETER (-1)
#define E_DISK_RESET_FAILED (-2)
#define E_NO_MEDIA_IN_DRIVE (-3)
#define E_TIMED_OUT (-4)
#if defined(DEBUG)
int acedebug = 2;
#define DBGME(lev,x) \
do \
if (lev >= acedebug) { \
x; \
} \
while (0)
#else
#define DBGME(lev,x)
#endif
#if defined(DEBUG)
typedef char *NAME;
typedef struct _REGDESC {
NAME RegisterName;
NAME BitNames[32];
} REGDESC, *PREGDESC;
static void SysacePrintRegister(const REGDESC *Desc, uint32_t Value);
static void SysacePrintRegister(const REGDESC *Desc, uint32_t Value)
{
int i;
printf("\t%s %x =", Desc->RegisterName, Value);
for (i = 31; i >= 0; i--) {
if (Value & (1 << i))
printf(" %s",
(Desc->BitNames[i]) ? Desc->BitNames[i] : "?");
}
printf("\n");
}
static void SysaceDumpRegisters(struct _Sac *Interface)
{
const REGDESC Control_Names =
{ "Control",
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
"RST",
"BUS8",
"BUS16",
"BUS32",
"IRQ",
"BRDY",
"IMSK0",
"IMSK1",
"TD0",
"TD1",
"TD2",
"TD3",
"BUFW8",
"BUFW16",
"BUFW32",
"DEBUG"}
};
const REGDESC STATUS_Names =
{ "STATUS",
{"CFGLOCK",
"MPULOCK",
"CFGERROR",
"CFCERROR",
"CFDETECT",
"DATABUFRDY",
"DATABUFWRITE",
"CFGDONE",
"RDYFORCFCMD",
"CFGMODEPIN",
0,0,0,
"CFGADDRPIN0",
"CFGADDRPIN1",
"CFGADDRPIN2",
0,
"CFBSY",
"CFRDY",
"CFDWF",
"CFDSC",
"CFDRQ",
"CFCORR",
"CFERR",
0,}
};
const REGDESC ERRORREG_Names =
{ "ERRORREG",
{"CARDRESETERR",
"CARDRDYERR",
"CARDREADERR",
"CARDWRITEERR",
"SECTORRDYERR",
"CFGADDRERR",
"CFGFAILED",
"CFGREADERR",
"CFGINSTRERR",
"CFGINITERR",
0,
"CFBBK",
"CFUNC",
"CFIDNF",
"CFABORT",
"CFAMNF",
0,}
};
const NAME CommandNames[8] =
{ "0",
"RESETMEMCARD",
"IDENTIFYMEMCARD",
"READMEMCARDDATA",
"WRITEMEMCARDDATA",
"5",
"ABORT",
"7"
};
const REGDESC CONTROLREG_Names =
{ "CONTROLREG",
{"FORCELOCKREQ",
"LOCKREQ",
"FORCECFGADDR",
"FORCECFGMODE",
"CFGMODE",
"CFGSTART",
"CFGSEL_MPU",
"CFGRESET",
"DATABUFRDYIRQ",
"ERRORIRQ",
"CFGDONEIRQ",
"RESETIRQ",
"CFGPROG",
"CFGADDR_B0",
"CFGADDR_B1",
"CFGADDR_B2",
0,}
};
const REGDESC FATSTATREG_Names =
{ "FATSTATREG",
{"MBRVALID",
"PBRVALID",
"MBRFAT12",
"PBRFAT12",
"MBRFAT16",
"PBRFAT16",
"CALCFAT12",
"CALCFAT16",
0, }
};
printf("Sysace@%p:\n", Interface);
printf("\tTag %x\n", Interface->Tag);
SysacePrintRegister(&Control_Names, Interface->Control);
printf("\tBUSMODEREG %x\n", Interface->BUSMODEREG);
SysacePrintRegister(&STATUS_Names, Interface->STATUS);
SysacePrintRegister(&ERRORREG_Names, Interface->ERRORREG);
printf("\tCFGLBAREG %x\n", Interface->CFGLBAREG);
printf("\tMPULBAREG %x\n", Interface->MPULBAREG);
printf("\tVERSIONREG %x\n", Interface->VERSIONREG);
printf("\tSECCNTCMDREG %x = %s cnt=%d\n", Interface->SECCNTCMDREG,
CommandNames[(Interface->SECCNTCMDREG >> 8)&7],
Interface->SECCNTCMDREG & SAC_SECCCNT);
SysacePrintRegister(&CONTROLREG_Names, Interface->CONTROLREG);
SysacePrintRegister(&FATSTATREG_Names, Interface->FATSTATREG);
}
#else
#define SysaceDumpRegisters(_c_)
#endif
static int SysaceInitialize(struct _Sac *Interface)
{
uint32_t BusMode, Control;
Interface->Control = SAC_RST;
Delay(200);
Interface->BUSMODEREG = SAC_MODE16 | (SAC_MODE16 << 8);
Delay(1);
Control = Interface->Control;
BusMode = Interface->BUSMODEREG;
if (BusMode & SAC_MODE16)
{
Interface->Control = Control | SAC_BUS16;
Interface->Control = Interface->Control & ~SAC_BUS8;
}
else
{
Interface->Control = Control | SAC_BUS8;
Interface->Control = Interface->Control & ~SAC_BUS16;
}
BusMode = Interface->BUSMODEREG;
Control = Interface->Control;
if (((BusMode & SAC_MODE16) == 0) && ((Control & SAC_BUS8) == 0)) return E_DISK_RESET_FAILED;
if (((BusMode & SAC_MODE16) > 0) && ((Control & SAC_BUS16) == 0)) return E_DISK_RESET_FAILED;
Interface->Control &= ~SAC_INTMASK;
#define SAC_INTERRUPTS (SAC_DATABUFRDYIRQ | SAC_ERRORIRQ )
Control = Interface->CONTROLREG;
Control = (Control & ~SAC_INTERRUPTS) | SAC_RESETIRQ | SAC_FORCECFGMODE;
Interface->CONTROLREG = Control;
Interface->CONTROLREG = Control & ~SAC_RESETIRQ;
Interface->MPULBAREG = 0;
return S_OK;
}
static int SysaceLock(struct _Sac *Interface)
{
uint32_t Status;
int i;
Status = Interface->STATUS;
if (Status & SAC_MPULOCK)
return TRUE;
Interface->CONTROLREG |= SAC_LOCKREQ;
for (i = 0; i < 200; i++) {
Status = Interface->STATUS;
if (Status & SAC_MPULOCK)
return TRUE;
Delay(100);
DBGME(0,printf("Sysace::Lock loops.. (st=%x)\n",Status));
}
DBGME(3,printf("Sysace::Lock timeout (st=%x)\n",Status));
SysaceDumpRegisters(Interface);
return FALSE;
}
static int SysaceUnlock(struct _Sac *Interface)
{
uint32_t Status;
int i;
Interface->CONTROLREG &= ~SAC_CFGRESET;
Status = Interface->STATUS;
if (0 == (Status & SAC_MPULOCK))
return TRUE;
Interface->CONTROLREG &= ~SAC_LOCKREQ;
for (i = 0; i < 200; i++) {
Status = Interface->STATUS;
if (0 == (Status & SAC_MPULOCK))
return TRUE;
Delay(100);
DBGME(0,printf("Sysace::Unlock loops.. (st=%x)\n",Status));
}
DBGME(3,printf("Sysace::Unlock timeout (st=%x)\n",Status));
SysaceDumpRegisters(Interface);
return FALSE;
}
#define SysaceReadyForCommand(_i_) ((_i_)->STATUS & SAC_RDYFORCFCMD)
#define SysaceBusyWithCommand(_i_) ((_i_)->STATUS & SAC_CFBSY)
#define SysaceInton(_i_) { \
(_i_)->CONTROLREG |= SAC_INTERRUPTS; \
(_i_)->Control |= SAC_INTMASK; \
}
#define SysaceIntoff(_i_) { \
(_i_)->CONTROLREG &= ~SAC_INTERRUPTS; \
(_i_)->Control &= ~SAC_INTMASK; \
}
static int SysaceStartCommand(struct _Sac *Interface,
uint32_t Command,
uint32_t Lba,
uint32_t nSectors)
{
int sc = E_DISK_RESET_FAILED;
if (!SysaceLock(Interface)) {
return sc;
}
if (! (Interface->STATUS & SAC_CFDETECT)) {
DBGME(2,printf("Sysace:: no media (st=%x)\n",Interface->STATUS));
return E_NO_MEDIA_IN_DRIVE;
}
if (!SysaceReadyForCommand(Interface)) {
DBGME(3,printf("Sysace:: not ready (st=%x)\n",Interface->STATUS));
SysaceDumpRegisters(Interface);
return sc;
}
Interface->MPULBAREG = Lba;
Interface->SECCNTCMDREG = (uint16_t)(Command | (nSectors & SAC_SECCCNT));
Interface->CONTROLREG |= SAC_CFGRESET;
return S_OK;
}
static void SysAce_isr ( struct _Sac *Interface )
{
uint32_t Control;
SysaceIntoff(Interface);
Control = Interface->CONTROLREG & (~(SAC_RESETIRQ|SAC_INTERRUPTS));
Interface->CONTROLREG = Control | SAC_RESETIRQ;
Interface->CONTROLREG = Control;
}
static int SysAce_wait(struct _Sac *Interface)
{
int i;
for (i = 0; i < 30000; i++) {
if (Interface->STATUS & SAC_DATABUFRDY) {
SysAce_isr(Interface);
return S_OK;
}
Delay(100);
}
return E_TIMED_OUT;
}
static int SysAce_read(struct _Sac * Interface, char *Buffer, uint32_t StartSector, uint32_t nBlocks)
{
int sc = S_OK;
uint32_t Size, SizeThisTime;
uint32_t Status = 0, SizeRead = 0;
unsigned int i, j;
Size = nBlocks << CF_SECBITS;
DBGME(1,printf("Sysace Read(%p %x %x)\n",
Buffer, StartSector, nBlocks));
while (sc == S_OK) {
SizeThisTime = Size;
if (SizeThisTime > CF_SECTOR_SIZE)
SizeThisTime = CF_SECTOR_SIZE;
SysaceInton(Interface);
sc = SysaceStartCommand(Interface,
SAC_CMD_READMEMCARDDATA,
StartSector,
1);
if (!FAILED(sc)) {
sc = SysAce_wait(Interface);
}
if (!FAILED(sc)) {
for (i = 0; i < SizeThisTime; i += 4) {
for (j = 0; j < 100; j++) {
Status = Interface->STATUS;
if (Status & SAC_DATABUFRDY)
break;
Delay(10);
}
if (Status & SAC_DATABUFRDY) {
uint32_t Data32;
Data32 = Interface->DATABUFREG[0];
Data32 = le32toh(Data32);
DBGME(0,printf(" %x", Data32));
if (0 == (0xf & (i+4))) DBGME(0,printf("\n"));
memcpy(Buffer+i, &Data32, 4);
}
else
{
DBGME(3,printf("Sysace::READ timeout\n"));
SysaceDumpRegisters(Interface);
sc = E_TIMED_OUT;
break;
}
}
if (!FAILED(sc)) {
StartSector += 1;
Buffer += SizeThisTime;
SizeRead += SizeThisTime;
Size -= SizeThisTime;
}
}
SysaceUnlock(Interface);
if (Size == 0)
break;
}
return sc;
}
struct ace_softc {
struct _Sac *sc_dp;
int sc_part;
struct disklabel sc_label;
};
#define RF_PROTECTED_SECTORS 64
int aceprobe(int unit)
{
struct _Sac *Sac;
if (unit == 0)
Sac = SAC0;
else if (unit == 1)
Sac = SAC1;
else
return E_INVALID_PARAMETER;
if ((Sac->Tag & SAC_TAG) != PMTTAG_SYSTEM_ACE) {
DBGME(3,printf("init_ace: bad tag (%x != %x) @x%p\n",
Sac->Tag, PMTTAG_SYSTEM_ACE, Sac));
return E_INVALID_PARAMETER;
}
return S_OK;
}
int
aceopen(struct open_file *f, ...)
{
int ctlr, unit, part;
struct _Sac *Sac;
struct ace_softc *sc;
struct disklabel *lp;
int i;
char *msg;
char buf[DEV_BSIZE];
size_t cnt;
va_list ap;
va_start(ap, f);
ctlr = va_arg(ap, int);
unit = va_arg(ap, int);
part = va_arg(ap, int);
va_end(ap);
if (ctlr != 0 || unit >= NSAC || part >= 8)
return (ENXIO);
Sac = (unit == 0) ? SAC0 : SAC1;
i = aceprobe(unit);
if (i < 0)
goto Bad;
if (SysaceInitialize(Sac) < 0) {
DBGME(3,printf("ace%d: no reset @x%p\n", unit, Sac));
Bad:
printf("open failed\n");
return (ENXIO);
}
sc = alloc(sizeof(struct ace_softc));
memset(sc, 0, sizeof(struct ace_softc));
f->f_devdata = (void *)sc;
sc->sc_dp = Sac;
sc->sc_part = part;
lp = &sc->sc_label;
lp->d_secsize = DEV_BSIZE;
lp->d_secpercyl = 1;
lp->d_npartitions = MAXPARTITIONS;
lp->d_partitions[part].p_offset = 0;
lp->d_partitions[part].p_size = 0x7fffffff;
i = acestrategy(sc, F_READ, (daddr_t)LABELSECTOR, DEV_BSIZE, buf, &cnt);
if (i || cnt != DEV_BSIZE) {
DBGME(3,printf("ace%d: error reading disk label\n", unit));
goto bad;
}
msg = getdisklabel(buf, lp);
if (msg) {
return (0);
}
if (part >= lp->d_npartitions || lp->d_partitions[part].p_size == 0) {
bad:
dealloc(sc, sizeof(struct ace_softc));
DBGME(3,printf("ace%d: bad part %d\n", unit, part));
return (ENXIO);
}
return (0);
}
#ifndef LIBSA_NO_DEV_CLOSE
int
aceclose(struct open_file *f)
{
dealloc(f->f_devdata, sizeof(struct ace_softc));
f->f_devdata = (void *)0;
return (0);
}
#endif
int
acestrategy(
void *devdata,
int rw,
daddr_t bn,
size_t reqcnt,
void *addr,
size_t *cnt)
{
struct ace_softc *sc = (struct ace_softc *)devdata;
int part = sc->sc_part;
struct partition *pp = &sc->sc_label.d_partitions[part];
int s;
uint32_t sector;
#if 0
if (rw != F_READ)
return (EINVAL);
#endif
if (reqcnt & (DEV_BSIZE - 1)) {
*cnt = 0;
return (EINVAL);
}
sector = bn;
sector += pp->p_offset;
if (pp->p_fstype == FS_RAID)
sector += RF_PROTECTED_SECTORS;
s = SysAce_read(sc->sc_dp,addr,sector,reqcnt >> CF_SECBITS);
if (s < 0)
return (EIO);
*cnt = reqcnt;
return (0);
}