- Improve IT/IR DMA queue management.

- Improve debug message for mbuf handling.
- Wait 1 sec for DMA stop in fwohci_i{t,r}x_disable() before freeing buffers.
This commit is contained in:
Hidetoshi Shimokawa 2003-01-26 15:39:04 +00:00
parent 8d48318bbb
commit 5a7ba74dc4
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=109890
4 changed files with 235 additions and 428 deletions

View File

@ -121,184 +121,6 @@ static driver_t firewire_driver = {
sizeof(struct firewire_softc),
};
/*
* transmitter buffer update.
*/
int
fw_tbuf_update(struct firewire_comm *fc, int sub, int flag){
struct fw_bulkxfer *bulkxfer, *bulkxfer2 = NULL;
struct fw_xferq *it;
int s, err = 0;
it = fc->it[sub];
s = splfw();
if(it->stdma == NULL){
bulkxfer = STAILQ_FIRST(&it->stvalid);
}else if(flag != 0){
bulkxfer = STAILQ_FIRST(&it->stvalid);
if(bulkxfer == it->stdma){
STAILQ_REMOVE_HEAD(&it->stvalid, link);
it->stdma->flag = 0;
STAILQ_INSERT_TAIL(&it->stfree, it->stdma, link);
#ifdef FWXFERQ_DV
if(!(it->flag & FWXFERQ_DV))
#endif
wakeup(it);
}
bulkxfer = STAILQ_FIRST(&it->stvalid);
}else{
bulkxfer = it->stdma;
}
if(bulkxfer != NULL){
bulkxfer2 = STAILQ_NEXT(bulkxfer, link);
#if 0
if(it->flag & FWXFERQ_DV && bulkxfer2 == NULL){
bulkxfer2 = STAILQ_FIRST(&it->stfree);
STAILQ_REMOVE_HEAD(&it->stfree, link);
bcopy(bulkxfer->buf, bulkxfer2->buf,
it->psize * it->btpacket);
STAILQ_INSERT_TAIL(&it->stvalid, bulkxfer2, link);
}
#endif
}
it->stdma = bulkxfer;
it->stdma2 = bulkxfer2;
#ifdef FWXFERQ_DV
if(it->flag & FWXFERQ_DV){
struct fw_dvbuf *dvbuf = NULL;
int i, j, chtag;
struct fw_pkt *fp;
u_int64_t cycle, dvsync;
chtag = it->flag & 0xff;
dvloop:
if(it->dvdma == NULL){
dvbuf = STAILQ_FIRST(&it->dvvalid);
if(dvbuf != NULL){
STAILQ_REMOVE_HEAD(&it->dvvalid, link);
it->dvdma = dvbuf;
it->queued = 0;
}
}
if(it->dvdma == NULL)
goto out;
it->stproc = STAILQ_FIRST(&it->stfree);
if(it->stproc != NULL){
STAILQ_REMOVE_HEAD(&it->stfree, link);
}else{
goto out;
}
#if DV_PAL
#define DVSEC 3
#define DVFRAC 75 /* PAL: 25 Hz (1875 = 25 * 3) */
#define DVDIFF 5 /* 125 = (8000/300 - 25) * 3 */
#else
#define DVSEC 100
#define DVFRAC 2997 /* NTSC: 29.97 Hz (2997 = 29.97 * 100) */
#define DVDIFF 203 /* 203 = (8000/250 - 29.97) * 100 */
#endif
#define CYCLEFRAC 0xc00
cycle = (u_int64_t) 8000 * DVSEC * it->dvsync;
/* least significant 12 bits */
dvsync = (cycle * CYCLEFRAC / DVFRAC) % CYCLEFRAC;
/* most significat 4 bits */
cycle = (cycle / DVFRAC + it->dvoffset) & 0xf;
fp = (struct fw_pkt *)(it->dvdma->buf);
#if 1
fp->mode.ld[2] = htonl(0x80000000 | (cycle << 12) | dvsync);
#else
fp->mode.ld[2] = htonl(0x80000000 | dvsync);
#endif
it->dvsync ++;
it->dvsync %= 2997;
for( i = 0, j = 0 ; i < it->dvpacket ; i++){
bcopy(it->dvdma->buf + it->queued * it->psize,
it->stproc->buf + j * it->psize, it->psize);
fp = (struct fw_pkt *)(it->stproc->buf + j * it->psize);
fp->mode.stream.len = htons(488);
fp->mode.stream.chtag = chtag;
fp->mode.stream.tcode = FWTCODE_STREAM;
fp->mode.ld[1] = htonl((fc->nodeid << 24) | 0x00780000 | it->dvdbc);
it->dvdbc++;
it->dvdbc %= 256;
it->queued ++;
j++;
it->dvdiff += DVDIFF;
if(it->dvdiff >= DVFRAC){
it->dvdiff %= DVFRAC;
fp = (struct fw_pkt *)(it->stproc->buf + j * it->psize);
fp->mode.stream.len = htons(0x8);
fp->mode.stream.chtag = chtag;
fp->mode.stream.tcode = FWTCODE_STREAM;
fp->mode.ld[1] = htonl((fc->nodeid << 24) |
0x00780000 | it->dvdbc);
j++;
}
}
it->stproc->npacket = j;
STAILQ_INSERT_TAIL(&it->stvalid, it->stproc, link);
if(it->queued >= it->dvpacket){
STAILQ_INSERT_TAIL(&it->dvfree, it->dvdma, link);
it->dvdma = NULL;
wakeup(it);
goto dvloop;
}
}
out:
#endif
splx(s);
return err;
}
/*
* receving buffer update.
*/
int
fw_rbuf_update(struct firewire_comm *fc, int sub, int flag){
struct fw_bulkxfer *bulkxfer, *bulkxfer2 = NULL;
struct fw_xferq *ir;
int s, err = 0;
ir = fc->ir[sub];
s = splfw();
if(ir->stdma != NULL){
if(flag != 0){
STAILQ_INSERT_TAIL(&ir->stvalid, ir->stdma, link);
}else{
ir->stdma->flag = 0;
STAILQ_INSERT_TAIL(&ir->stfree, ir->stdma, link);
}
}
if(ir->stdma2 != NULL){
bulkxfer = ir->stdma2;
bulkxfer2 = STAILQ_FIRST(&ir->stfree);
if(bulkxfer2 != NULL){
STAILQ_REMOVE_HEAD(&ir->stfree, link);
}
}else{
bulkxfer = STAILQ_FIRST(&ir->stfree);
if(bulkxfer != NULL){
STAILQ_REMOVE_HEAD(&ir->stfree, link);
bulkxfer2 = STAILQ_FIRST(&ir->stfree);
if(bulkxfer2 != NULL){
STAILQ_REMOVE_HEAD(&ir->stfree, link);
}
}else{
device_printf(fc->bdev, "no free chunk available\n");
bulkxfer = STAILQ_FIRST(&ir->stvalid);
STAILQ_REMOVE_HEAD(&ir->stvalid, link);
}
}
splx(s);
ir->stdma = bulkxfer;
ir->stdma2 = bulkxfer2;
return err;
}
/*
* To lookup node id. from EUI64.
*/

View File

@ -186,7 +186,7 @@ struct fw_xferq {
#define FWXFERQ_PACKET (1 << 10)
#define FWXFERQ_BULK (1 << 11)
#if 0
#if 0 /* BROKEN */
#define FWXFERQ_DV (1 << 12)
#endif
#define FWXFERQ_MODEMASK (7 << 10)
@ -213,10 +213,9 @@ struct fw_xferq {
struct fw_bulkxfer *bulkxfer;
STAILQ_HEAD(, fw_bulkxfer) stvalid;
STAILQ_HEAD(, fw_bulkxfer) stfree;
struct fw_bulkxfer *stdma;
struct fw_bulkxfer *stdma2;
STAILQ_HEAD(, fw_bulkxfer) stdma;
struct fw_bulkxfer *stproc;
u_int procptr;
#ifdef FWXFERQ_DV
int dvdbc, dvdiff, dvsync, dvoffset;
struct fw_dvbuf *dvbuf;
STAILQ_HEAD(, fw_dvbuf) dvvalid;
@ -225,14 +224,13 @@ struct fw_xferq {
struct fw_dvbuf *dvproc;
u_int dvptr;
u_int dvpacket;
u_int need_wakeup;
#endif
struct selinfo rsel;
caddr_t sc;
void (*hand) __P((struct fw_xferq *));
};
struct fw_bulkxfer{
u_int32_t flag;
caddr_t buf;
STAILQ_ENTRY(fw_bulkxfer) link;
caddr_t start;

View File

@ -173,7 +173,9 @@ fw_close (dev_t dev, int flags, int fmt, fw_proc *td)
sc->fc->it[sub]->buf = NULL;
free(sc->fc->it[sub]->bulkxfer, M_DEVBUF);
sc->fc->it[sub]->bulkxfer = NULL;
#ifdef FWXFERQ_DV
sc->fc->it[sub]->dvbuf = NULL;
#endif
sc->fc->it[sub]->flag &= ~FWXFERQ_EXTBUF;
sc->fc->it[sub]->psize = 0;
sc->fc->it[sub]->maxq = FWMAXQUEUE;
@ -284,24 +286,12 @@ fw_read (dev_t dev, struct uio *uio, int ioflag)
ir->queued ++;
if(ir->queued >= ir->bnpacket){
s = splfw();
ir->stproc->flag = 0;
STAILQ_INSERT_TAIL(&ir->stfree, ir->stproc, link);
splx(s);
sc->fc->irx_enable(sc->fc, sub);
ir->stproc = NULL;
}
}
#if 0
if(STAILQ_FIRST(&ir->q) == NULL &&
(ir->flag & FWXFERQ_RUNNING) && (ir->flag & FWXFERQ_PACKET)){
err = sc->fc->irx_enable(sc->fc, sub);
}
#endif
#if 0
if(STAILQ_FIRST(&ir->stvalid) == NULL &&
(ir->flag & FWXFERQ_RUNNING) && !(ir->flag & FWXFERQ_PACKET)){
err = sc->fc->irx_enable(sc->fc, sub);
}
#endif
return err;
}
@ -357,7 +347,6 @@ fw_write (dev_t dev, struct uio *uio, int ioflag)
/* Discard unsent buffered stream packet, when sending Asyrequrst */
if(xferq != NULL && it->stproc != NULL){
s = splfw();
it->stproc->flag = 0;
STAILQ_INSERT_TAIL(&it->stfree, it->stproc, link);
splx(s);
it->stproc = NULL;
@ -368,14 +357,14 @@ fw_write (dev_t dev, struct uio *uio, int ioflag)
if (xferq == NULL) {
#endif
isoloop:
if(it->stproc == NULL){
if (it->stproc == NULL) {
it->stproc = STAILQ_FIRST(&it->stfree);
if(it->stproc != NULL){
if (it->stproc != NULL) {
s = splfw();
STAILQ_REMOVE_HEAD(&it->stfree, link);
splx(s);
it->queued = 0;
}else if(slept == 0){
} else if (slept == 0) {
slept = 1;
err = sc->fc->itx_enable(sc->fc, sub);
if(err){
@ -391,10 +380,6 @@ fw_write (dev_t dev, struct uio *uio, int ioflag)
return err;
}
}
#if 0 /* What's this for? (overwritten by the following uiomove)*/
fp = (struct fw_pkt *)(it->stproc->buf + it->queued * it->psize);
fp->mode.stream.len = htons(uio->uio_resid - sizeof(u_int32_t));
#endif
err = uiomove(it->stproc->buf + it->queued * it->psize,
uio->uio_resid, uio);
it->queued ++;
@ -403,7 +388,6 @@ fw_write (dev_t dev, struct uio *uio, int ioflag)
STAILQ_INSERT_TAIL(&it->stvalid, it->stproc, link);
splx(s);
it->stproc = NULL;
fw_tbuf_update(sc->fc, sub, 0);
err = sc->fc->itx_enable(sc->fc, sub);
}
return err;
@ -684,22 +668,23 @@ fw_ioctl (dev_t dev, u_long cmd, caddr_t data, int flag, fw_proc *td)
it->bnpacket = ibufreq->tx.npacket;
it->btpacket = ibufreq->tx.npacket;
it->psize = (ibufreq->tx.psize + 3) & ~3;
ir->queued = 0;
it->queued = 0;
#ifdef FWXFERQ_DV
it->dvdbc = 0;
it->dvdiff = 0;
it->dvsync = 0;
it->dvoffset = 0;
#endif
STAILQ_INIT(&ir->stvalid);
STAILQ_INIT(&ir->stfree);
ir->stdma = NULL;
ir->stdma2 = NULL;
STAILQ_INIT(&ir->stdma);
ir->stproc = NULL;
STAILQ_INIT(&it->stvalid);
STAILQ_INIT(&it->stfree);
it->stdma = NULL;
it->stdma2 = NULL;
STAILQ_INIT(&it->stdma);
it->stproc = NULL;
for(i = 0 ; i < sc->fc->ir[sub]->bnchunk; i++){
@ -707,7 +692,6 @@ fw_ioctl (dev_t dev, u_long cmd, caddr_t data, int flag, fw_proc *td)
ir->buf +
i * sc->fc->ir[sub]->bnpacket *
sc->fc->ir[sub]->psize;
ir->bulkxfer[i].flag = 0;
STAILQ_INSERT_TAIL(&ir->stfree,
&ir->bulkxfer[i], link);
ir->bulkxfer[i].npacket = ir->bnpacket;
@ -717,7 +701,6 @@ fw_ioctl (dev_t dev, u_long cmd, caddr_t data, int flag, fw_proc *td)
it->buf +
i * sc->fc->it[sub]->bnpacket *
sc->fc->it[sub]->psize;
it->bulkxfer[i].flag = 0;
STAILQ_INSERT_TAIL(&it->stfree,
&it->bulkxfer[i], link);
it->bulkxfer[i].npacket = it->bnpacket;

View File

@ -42,6 +42,7 @@
#define IRX_CH 0x24
#include <sys/param.h>
#include <sys/proc.h>
#include <sys/systm.h>
#include <sys/types.h>
#include <sys/mbuf.h>
@ -847,17 +848,15 @@ fwohci_start(struct fwohci_softc *sc, struct fwohci_dbch *dbch)
db_tr->dbcnt++;
} else {
int mchain=0;
/* XXX we assume mbuf chain is shorter than ndesc */
for (m = xfer->mbuf; m != NULL; m = m->m_next) {
if (m->m_len == 0)
/* unrecoverable error could ocurre. */
/* unrecoverable error could occur. */
continue;
mchain++;
if (db_tr->dbcnt >= dbch->ndesc)
continue;
if (db_tr->dbcnt >= dbch->ndesc) {
device_printf(sc->fc.dev,
"dbch->ndesc is too small"
", trancated.\n");
break;
}
db->db.desc.addr
= vtophys(mtod(m, caddr_t));
db->db.desc.cmd = OHCI_OUTPUT_MORE | m->m_len;
@ -865,6 +864,11 @@ fwohci_start(struct fwohci_softc *sc, struct fwohci_dbch *dbch)
db++;
db_tr->dbcnt++;
}
if (mchain > dbch->ndesc - 2)
device_printf(sc->fc.dev,
"dbch->ndesc(%d) is too small for"
" mbuf chain(%d), trancated.\n",
dbch->ndesc, mchain);
}
}
if (maxdesc < db_tr->dbcnt) {
@ -1201,9 +1205,13 @@ static int
fwohci_itx_disable(struct firewire_comm *fc, int dmach)
{
struct fwohci_softc *sc = (struct fwohci_softc *)fc;
int dummy;
OWRITE(sc, OHCI_ITCTLCLR(dmach), OHCI_CNTL_DMA_RUN);
OWRITE(sc, OHCI_IT_MASKCLR, 1 << dmach);
OWRITE(sc, OHCI_IT_STATCLR, 1 << dmach);
/* XXX we cannot free buffers until the DMA really stops */
tsleep((void *)&dummy, FWPRI, "fwitxd", hz);
fwohci_db_free(&sc->it[dmach]);
sc->it[dmach].xferq.flag &= ~FWXFERQ_RUNNING;
return 0;
@ -1213,10 +1221,13 @@ static int
fwohci_irx_disable(struct firewire_comm *fc, int dmach)
{
struct fwohci_softc *sc = (struct fwohci_softc *)fc;
int dummy;
OWRITE(sc, OHCI_IRCTLCLR(dmach), OHCI_CNTL_DMA_RUN);
OWRITE(sc, OHCI_IR_MASKCLR, 1 << dmach);
OWRITE(sc, OHCI_IR_STATCLR, 1 << dmach);
/* XXX we cannot free buffers until the DMA really stops */
tsleep((void *)&dummy, FWPRI, "fwirxd", hz);
if(sc->ir[dmach].dummy != NULL){
free(sc->ir[dmach].dummy, M_DEVBUF);
}
@ -1415,6 +1426,33 @@ fwohci_rx_enable(struct fwohci_softc *sc, struct fwohci_dbch *dbch)
return err;
}
static int
fwochi_next_cycle(struct firewire_comm *fc, int cycle_now)
{
int sec, cycle, cycle_match;
cycle = cycle_now & 0x1fff;
sec = cycle_now >> 13;
#define CYCLE_MOD 0x10
#define CYCLE_DELAY 8 /* min delay to start DMA */
cycle = cycle + CYCLE_DELAY;
if (cycle >= 8000) {
sec ++;
cycle -= 8000;
}
cycle = ((cycle + CYCLE_MOD - 1) / CYCLE_MOD) * CYCLE_MOD;
if (cycle >= 8000) {
sec ++;
if (cycle == 8000)
cycle = 0;
else
cycle = CYCLE_MOD;
}
cycle_match = ((sec << 13) | cycle) & 0x7ffff;
return(cycle_match);
}
static int
fwohci_itxbuf_enable(struct firewire_comm *fc, int dmach)
{
@ -1422,15 +1460,18 @@ fwohci_itxbuf_enable(struct firewire_comm *fc, int dmach)
int err = 0;
unsigned short tag, ich;
struct fwohci_dbch *dbch;
int cycle_now, sec, cycle, cycle_match;
int cycle_match, cycle_now, s, ldesc;
u_int32_t stat;
struct fw_bulkxfer *first, *chunk, *prev;
struct fw_xferq *it;
tag = (sc->it[dmach].xferq.flag >> 6) & 3;
ich = sc->it[dmach].xferq.flag & 0x3f;
dbch = &sc->it[dmach];
it = &dbch->xferq;
tag = (it->flag >> 6) & 3;
ich = it->flag & 0x3f;
if ((dbch->flags & FWOHCI_DBCH_INIT) == 0) {
dbch->xferq.queued = 0;
dbch->ndb = dbch->xferq.bnpacket * dbch->xferq.bnchunk;
dbch->ndb = it->bnpacket * it->bnchunk;
dbch->ndesc = 3;
fwohci_db_init(dbch);
if ((dbch->flags & FWOHCI_DBCH_INIT) == 0)
@ -1439,56 +1480,53 @@ fwohci_itxbuf_enable(struct firewire_comm *fc, int dmach)
}
if(err)
return err;
stat = OREAD(sc, OHCI_ITCTL(dmach));
if (stat & OHCI_CNTL_DMA_ACTIVE) {
if(dbch->xferq.stdma2 != NULL){
fwohci_txbufdb(sc, dmach, dbch->xferq.stdma2);
((struct fwohcidb_tr *)
(dbch->xferq.stdma->end))->db[dbch->ndesc - 1].db.desc.cmd
|= OHCI_BRANCH_ALWAYS;
((struct fwohcidb_tr *)
(dbch->xferq.stdma->end))->db[dbch->ndesc - 1].db.desc.depend =
vtophys(((struct fwohcidb_tr *)(dbch->xferq.stdma2->start))->db) | dbch->ndesc;
((struct fwohcidb_tr *)(dbch->xferq.stdma->end))->db[0].db.desc.depend =
vtophys(((struct fwohcidb_tr *)(dbch->xferq.stdma2->start))->db) | dbch->ndesc;
((struct fwohcidb_tr *)(dbch->xferq.stdma2->end))->db[dbch->ndesc - 1].db.desc.depend &= ~0xf;
((struct fwohcidb_tr *)(dbch->xferq.stdma2->end))->db[0].db.desc.depend &= ~0xf;
} else {
device_printf(fc->dev,
"fwohci_itxbuf_enable: queue underrun\n");
s = splfw();
prev = STAILQ_LAST(&it->stdma, fw_bulkxfer, link);
while ((chunk = STAILQ_FIRST(&it->stvalid)) != NULL) {
volatile struct fwohcidb *db;
fwohci_txbufdb(sc, dmach, chunk);
ldesc = dbch->ndesc - 1;
db = ((struct fwohcidb_tr *)(chunk->end))->db;
db[ldesc].db.desc.status = db[0].db.desc.status = 0;
db[ldesc].db.desc.count = db[0].db.desc.count = 0;
db[ldesc].db.desc.depend &= ~0xf;
db[0].db.desc.depend &= ~0xf;
if (prev != NULL) {
db = ((struct fwohcidb_tr *)(prev->end))->db;
db[ldesc].db.desc.cmd |= OHCI_BRANCH_ALWAYS;
db[ldesc].db.desc.depend = db[0].db.desc.depend =
vtophys(((struct fwohcidb_tr *)
(chunk->start))->db) | dbch->ndesc;
}
return err;
}
if (firewire_debug)
printf("fwohci_itxbuf_enable: kick 0x%08x\n", stat);
fw_tbuf_update(&sc->fc, dmach, 0);
if(dbch->xferq.stdma == NULL){
return err;
}
if(dbch->xferq.stdma2 == NULL){
/* wait until 2 chunks buffered */
return err;
STAILQ_REMOVE_HEAD(&it->stvalid, link);
STAILQ_INSERT_TAIL(&it->stdma, chunk, link);
prev = chunk;
}
splx(s);
stat = OREAD(sc, OHCI_ITCTL(dmach));
if (stat & (OHCI_CNTL_DMA_ACTIVE | OHCI_CNTL_CYCMATCH_S))
return 0;
OWRITE(sc, OHCI_ITCTLCLR(dmach), OHCI_CNTL_DMA_RUN);
OWRITE(sc, OHCI_IT_MASKCLR, 1 << dmach);
OWRITE(sc, OHCI_IT_STATCLR, 1 << dmach);
OWRITE(sc, OHCI_IT_MASK, 1 << dmach);
fwohci_txbufdb(sc, dmach, dbch->xferq.stdma);
fwohci_txbufdb(sc, dmach, dbch->xferq.stdma2);
((struct fwohcidb_tr *)
(dbch->xferq.stdma->end))->db[dbch->ndesc - 1].db.desc.cmd
|= OHCI_BRANCH_ALWAYS;
((struct fwohcidb_tr *)(dbch->xferq.stdma->end))->db[dbch->ndesc - 1].db.desc.depend =
vtophys(((struct fwohcidb_tr *)(dbch->xferq.stdma2->start))->db) | dbch->ndesc;
((struct fwohcidb_tr *)(dbch->xferq.stdma->end))->db[0].db.desc.depend =
vtophys(((struct fwohcidb_tr *)(dbch->xferq.stdma2->start))->db) | dbch->ndesc;
((struct fwohcidb_tr *)(dbch->xferq.stdma2->end))->db[dbch->ndesc - 1].db.desc.depend &= ~0xf;
((struct fwohcidb_tr *) (dbch->xferq.stdma2->end))->db[0].db.desc.depend &= ~0xf;
OWRITE(sc, OHCI_ITCMD(dmach),
vtophys(((struct fwohcidb_tr *)
(dbch->xferq.stdma->start))->db) | dbch->ndesc);
#define CYCLE_OFFSET 1
first = STAILQ_FIRST(&it->stdma);
OWRITE(sc, OHCI_ITCMD(dmach), vtophys(((struct fwohcidb_tr *)
(first->start))->db) | dbch->ndesc);
if (firewire_debug)
printf("fwohci_itxbuf_enable: kick 0x%08x\n", stat);
if ((stat & OHCI_CNTL_DMA_RUN) == 0) {
#if 1
/* Don't start until all chunks are buffered */
if (STAILQ_FIRST(&it->stfree) != NULL)
goto out;
#endif
#ifdef FWXFERQ_DV
#define CYCLE_OFFSET 1
if(dbch->xferq.flag & FWXFERQ_DV){
struct fw_pkt *fp;
struct fwohcidb_tr *db_tr;
@ -1499,41 +1537,26 @@ fwohci_itxbuf_enable(struct firewire_comm *fc, int dmach)
fp->mode.ld[2] |= htonl(dbch->xferq.dvoffset << 12);
}
#endif
/* 2bit second + 13bit cycle */
cycle_now = (fc->cyctimer(fc) >> 12) & 0x7fff;
cycle = cycle_now & 0x1fff;
sec = cycle_now >> 13;
#define CYCLE_MOD 0x10
#define CYCLE_DELAY 8 /* min delay to start DMA */
cycle = cycle + CYCLE_DELAY;
if (cycle >= 8000) {
sec ++;
cycle -= 8000;
}
cycle = ((cycle + CYCLE_MOD - 1) / CYCLE_MOD) * CYCLE_MOD;
if (cycle >= 8000) {
sec ++;
if (cycle == 8000)
cycle = 0;
else
cycle = CYCLE_MOD;
}
cycle_match = ((sec << 13) | cycle) & 0x7ffff;
/* Clear cycle match counter bits */
OWRITE(sc, OHCI_ITCTLCLR(dmach), 0xffff0000);
OWRITE(sc, FWOHCI_INTMASK, OHCI_INT_DMA_IT);
/* 2bit second + 13bit cycle */
cycle_now = (fc->cyctimer(fc) >> 12) & 0x7fff;
cycle_match = fwochi_next_cycle(fc, cycle_now);
OWRITE(sc, OHCI_ITCTL(dmach),
OHCI_CNTL_CYCMATCH_S | (cycle_match << 16)
| OHCI_CNTL_DMA_RUN);
OWRITE(sc, FWOHCI_INTMASK, OHCI_INT_DMA_IT);
if (firewire_debug)
printf("cycle_match: 0x%04x->0x%04x\n",
cycle_now, cycle_match);
} else if ((stat & OHCI_CNTL_CYCMATCH_S) == 0) {
if (firewire_debug)
printf("fwohci_itxbuf_enable: restart 0x%08x\n", stat);
OWRITE(sc, OHCI_ITCTLCLR(dmach), OHCI_CNTL_DMA_RUN);
device_printf(sc->fc.dev,
"IT DMA underrun (0x%08x)\n", stat);
OWRITE(sc, OHCI_ITCTL(dmach), OHCI_CNTL_DMA_RUN);
}
out:
return err;
}
@ -1541,71 +1564,84 @@ static int
fwohci_irxbuf_enable(struct firewire_comm *fc, int dmach)
{
struct fwohci_softc *sc = (struct fwohci_softc *)fc;
int err = 0;
int err = 0, s, ldesc;
unsigned short tag, ich;
u_int32_t stat;
struct fwohci_dbch *dbch;
struct fw_bulkxfer *first, *prev, *chunk;
struct fw_xferq *ir;
if(!(sc->ir[dmach].xferq.flag & FWXFERQ_RUNNING)){
tag = (sc->ir[dmach].xferq.flag >> 6) & 3;
ich = sc->ir[dmach].xferq.flag & 0x3f;
dbch = &sc->ir[dmach];
ir = &dbch->xferq;
ldesc = dbch->ndesc - 1;
if ((ir->flag & FWXFERQ_RUNNING) == 0) {
tag = (ir->flag >> 6) & 3;
ich = ir->flag & 0x3f;
OWRITE(sc, OHCI_IRMATCH(dmach), tagbit[tag] | ich);
sc->ir[dmach].xferq.queued = 0;
sc->ir[dmach].ndb = sc->ir[dmach].xferq.bnpacket *
sc->ir[dmach].xferq.bnchunk;
sc->ir[dmach].dummy =
malloc(sizeof(u_int32_t) * sc->ir[dmach].ndb,
M_DEVBUF, M_NOWAIT);
if(sc->ir[dmach].dummy == NULL){
ir->queued = 0;
dbch->ndb = ir->bnpacket * ir->bnchunk;
dbch->dummy = malloc(sizeof(u_int32_t) * dbch->ndb,
M_DEVBUF, M_NOWAIT);
if (dbch->dummy == NULL) {
err = ENOMEM;
return err;
}
sc->ir[dmach].ndesc = 2;
fwohci_db_init(&sc->ir[dmach]);
if ((sc->ir[dmach].flags & FWOHCI_DBCH_INIT) == 0)
dbch->ndesc = 2;
fwohci_db_init(dbch);
if ((dbch->flags & FWOHCI_DBCH_INIT) == 0)
return ENOMEM;
err = fwohci_rx_enable(sc, &sc->ir[dmach]);
err = fwohci_rx_enable(sc, dbch);
}
if(err)
return err;
stat = OREAD(sc, OHCI_IRCTL(dmach));
if (stat & OHCI_CNTL_DMA_ACTIVE) {
if(sc->ir[dmach].xferq.stdma2 != NULL){
((struct fwohcidb_tr *)(sc->ir[dmach].xferq.stdma->end))->db[sc->ir[dmach].ndesc - 1].db.desc.depend =
vtophys(((struct fwohcidb_tr *)(sc->ir[dmach].xferq.stdma2->start))->db) | sc->ir[dmach].ndesc;
((struct fwohcidb_tr *)(sc->ir[dmach].xferq.stdma->end))->db[0].db.desc.depend =
vtophys(((struct fwohcidb_tr *)(sc->ir[dmach].xferq.stdma2->start))->db);
((struct fwohcidb_tr *)(sc->ir[dmach].xferq.stdma2->end))->db[sc->ir[dmach].ndesc - 1].db.desc.depend &= ~0xf;
((struct fwohcidb_tr *)(sc->ir[dmach].xferq.stdma2->end))->db[0].db.desc.depend &= ~0xf;
}
} else if (!(stat & OHCI_CNTL_DMA_ACTIVE)
&& !(sc->ir[dmach].xferq.flag & FWXFERQ_PACKET)) {
if (firewire_debug)
device_printf(sc->fc.dev, "IR DMA stat %x\n", stat);
fw_rbuf_update(&sc->fc, dmach, 0);
s = splfw();
OWRITE(sc, OHCI_IRCTLCLR(dmach), OHCI_CNTL_DMA_RUN);
OWRITE(sc, OHCI_IR_MASKCLR, 1 << dmach);
OWRITE(sc, OHCI_IR_STATCLR, 1 << dmach);
OWRITE(sc, OHCI_IR_MASK, 1 << dmach);
OWRITE(sc, OHCI_IRCTLCLR(dmach), 0xf0000000);
OWRITE(sc, OHCI_IRCTL(dmach), OHCI_CNTL_ISOHDR);
if(sc->ir[dmach].xferq.stdma2 != NULL){
((struct fwohcidb_tr *)(sc->ir[dmach].xferq.stdma->end))->db[sc->ir[dmach].ndesc - 1].db.desc.depend =
vtophys(((struct fwohcidb_tr *)(sc->ir[dmach].xferq.stdma2->start))->db) | sc->ir[dmach].ndesc;
((struct fwohcidb_tr *)(sc->ir[dmach].xferq.stdma->end))->db[0].db.desc.depend =
vtophys(((struct fwohcidb_tr *)(sc->ir[dmach].xferq.stdma2->start))->db);
((struct fwohcidb_tr *)(sc->ir[dmach].xferq.stdma2->end))->db[sc->ir[dmach].ndesc - 1].db.desc.depend &= ~0xf;
}else{
((struct fwohcidb_tr *)(sc->ir[dmach].xferq.stdma->end))->db[sc->ir[dmach].ndesc - 1].db.desc.depend &= ~0xf;
((struct fwohcidb_tr *)(sc->ir[dmach].xferq.stdma->end))->db[0].db.desc.depend &= ~0xf;
}
OWRITE(sc, OHCI_IRCMD(dmach),
vtophys(((struct fwohcidb_tr *)(sc->ir[dmach].xferq.stdma->start))->db) | sc->ir[dmach].ndesc);
OWRITE(sc, OHCI_IRCTL(dmach), OHCI_CNTL_DMA_RUN);
OWRITE(sc, FWOHCI_INTMASK, OHCI_INT_DMA_IR);
first = STAILQ_FIRST(&ir->stfree);
if (first == NULL) {
device_printf(fc->dev, "IR DMA no free chunk\n");
splx(s);
return 0;
}
prev = STAILQ_LAST(&ir->stdma, fw_bulkxfer, link);
while ((chunk = STAILQ_FIRST(&ir->stfree)) != NULL) {
volatile struct fwohcidb *db;
db = ((struct fwohcidb_tr *)(chunk->end))->db;
db[ldesc].db.desc.status = db[ldesc].db.desc.count = 0;
db[ldesc].db.desc.depend &= ~0xf;
if (prev != NULL) {
db = ((struct fwohcidb_tr *)(prev->end))->db;
db[ldesc].db.desc.depend =
vtophys(((struct fwohcidb_tr *)
(chunk->start))->db) | dbch->ndesc;
}
STAILQ_REMOVE_HEAD(&ir->stfree, link);
STAILQ_INSERT_TAIL(&ir->stdma, chunk, link);
prev = chunk;
}
splx(s);
stat = OREAD(sc, OHCI_IRCTL(dmach));
if (stat & OHCI_CNTL_DMA_ACTIVE)
return 0;
if (stat & OHCI_CNTL_DMA_RUN) {
OWRITE(sc, OHCI_IRCTLCLR(dmach), OHCI_CNTL_DMA_RUN);
device_printf(sc->fc.dev, "IR DMA overrun (0x%08x)\n", stat);
}
OWRITE(sc, OHCI_IR_MASKCLR, 1 << dmach);
OWRITE(sc, OHCI_IR_STATCLR, 1 << dmach);
OWRITE(sc, OHCI_IR_MASK, 1 << dmach);
OWRITE(sc, OHCI_IRCTLCLR(dmach), 0xf0000000);
OWRITE(sc, OHCI_IRCTL(dmach), OHCI_CNTL_ISOHDR);
OWRITE(sc, OHCI_IRCMD(dmach),
vtophys(((struct fwohcidb_tr *)(first->start))->db)
| dbch->ndesc);
OWRITE(sc, OHCI_IRCTL(dmach), OHCI_CNTL_DMA_RUN);
OWRITE(sc, FWOHCI_INTMASK, OHCI_INT_DMA_IR);
return err;
}
@ -1959,92 +1995,72 @@ fwohci_set_intr(struct firewire_comm *fc, int enable)
static void
fwohci_tbuf_update(struct fwohci_softc *sc, int dmach)
{
int stat;
struct firewire_comm *fc = &sc->fc;
struct fwohci_dbch *dbch;
struct fwohcidb_tr *db_tr;
volatile struct fwohcidb *db;
struct fw_bulkxfer *chunk;
struct fw_xferq *it;
u_int32_t stat, count;
int s, w=0;
dbch = &sc->it[dmach];
#if 0 /* XXX OHCI interrupt before the last packet is really on the wire */
if((dbch->xferq.flag & FWXFERQ_DV) && (dbch->xferq.stdma2 != NULL)){
db_tr = (struct fwohcidb_tr *)dbch->xferq.stdma2->start;
/*
* Overwrite highest significant 4 bits timestamp information
*/
fp = (struct fw_pkt *)db_tr->buf;
fp->mode.ld[2] &= htonl(0xffff0fff);
fp->mode.ld[2] |= htonl((fc->cyctimer(fc) + 0x4000) & 0xf000);
}
it = fc->it[dmach];
s = splfw(); /* unnecessary ? */
while ((chunk = STAILQ_FIRST(&it->stdma)) != NULL) {
db = ((struct fwohcidb_tr *)(chunk->end))->db;
stat = db[sc->it[dmach].ndesc - 1].db.desc.status;
db = ((struct fwohcidb_tr *)(chunk->start))->db;
count = db[sc->it[dmach].ndesc - 1].db.desc.count;
if (stat == 0)
break;
STAILQ_REMOVE_HEAD(&it->stdma, link);
switch (stat & FWOHCIEV_MASK){
case FWOHCIEV_ACKCOMPL:
#if 0
device_printf(fc->dev, "0x%08x\n", count);
#endif
/*
* XXX interrupt could be missed.
* We have to check more than one buffer/chunk
*/
if (firewire_debug && dbch->xferq.stdma2 != NULL) {
db_tr = (struct fwohcidb_tr *)dbch->xferq.stdma2->end;
stat = db_tr->db[2].db.desc.status;
if (stat)
break;
default:
device_printf(fc->dev,
"stdma2 already done stat:0x%x\n", stat);
"Isochronous transmit err %02x\n", stat);
}
STAILQ_INSERT_TAIL(&it->stfree, chunk, link);
w++;
}
stat = OREAD(sc, OHCI_ITCTL(dmach)) & 0x1f;
switch(stat){
case FWOHCIEV_ACKCOMPL:
#ifdef FWXFERQ_DV
if (dbch->xferq.flag & FWXFERQ_DV) {
struct ciphdr *ciph;
int timer, timestamp, cycl, diff;
static int last_timer=0;
struct fw_pkt *fp;
timer = (fc->cyctimer(fc) >> 12) & 0xffff;
db_tr = (struct fwohcidb_tr *)dbch->xferq.stdma->start;
fp = (struct fw_pkt *)db_tr->buf;
ciph = (struct ciphdr *) &fp->mode.ld[1];
timestamp = db_tr->db[2].db.desc.count & 0xffff;
cycl = ntohs(ciph->fdf.dv.cyc) >> 12;
diff = cycl - (timestamp & 0xf) - CYCLE_OFFSET;
if (diff < 0)
diff += 16;
if (diff > 8)
diff -= 16;
if (firewire_debug || diff != 0)
printf("dbc: %3d timer: 0x%04x packet: 0x%04x"
" cyc: 0x%x diff: %+1d\n",
ciph->dbc, last_timer, timestamp, cycl, diff);
last_timer = timer;
/* XXX adjust dbch->xferq.dvoffset if diff != 0 or 1 */
}
#endif
fw_tbuf_update(fc, dmach, 1);
break;
default:
device_printf(fc->dev, "Isochronous transmit err %02x\n", stat);
fw_tbuf_update(fc, dmach, 0);
break;
}
fwohci_itxbuf_enable(fc, dmach);
splx(s);
if (w)
wakeup(it);
}
static void
fwohci_rbuf_update(struct fwohci_softc *sc, int dmach)
{
struct firewire_comm *fc = &sc->fc;
int stat;
volatile struct fwohcidb *db;
struct fw_bulkxfer *chunk;
struct fw_xferq *ir;
u_int32_t stat;
int s, w=0;
stat = OREAD(sc, OHCI_IRCTL(dmach)) & 0x1f;
switch(stat){
case FWOHCIEV_ACKCOMPL:
fw_rbuf_update(fc, dmach, 1);
wakeup(fc->ir[dmach]);
fwohci_irx_enable(fc, dmach);
break;
default:
device_printf(fc->dev, "Isochronous receive err %02x\n",
stat);
break;
ir = fc->ir[dmach];
s = splfw();
while ((chunk = STAILQ_FIRST(&ir->stdma)) != NULL) {
db = ((struct fwohcidb_tr *)(chunk->end))->db;
stat = db[sc->ir[dmach].ndesc - 1].db.desc.status;
if (stat == 0)
break;
STAILQ_REMOVE_HEAD(&ir->stdma, link);
STAILQ_INSERT_TAIL(&ir->stvalid, chunk, link);
switch (stat & FWOHCIEV_MASK) {
case FWOHCIEV_ACKCOMPL:
break;
default:
device_printf(fc->dev,
"Isochronous receive err %02x\n", stat);
}
w++;
}
splx(s);
if (w)
wakeup(ir);
}
void
@ -2294,10 +2310,6 @@ fwohci_txbufdb(struct fwohci_softc *sc, int dmach, struct fw_bulkxfer *bulkxfer)
/*
device_printf(sc->fc.dev, "DB %08x %08x %08x\n", bulkxfer, vtophys(db_tr->db), vtophys(fdb_tr->db));
*/
if(bulkxfer->flag != 0){
return;
}
bulkxfer->flag = 1;
for( idb = 0 ; idb < bulkxfer->npacket ; idb ++){
db_tr->db[0].db.desc.cmd
= OHCI_OUTPUT_MORE | OHCI_KEY_ST2 | 8;
@ -2308,9 +2320,7 @@ device_printf(sc->fc.dev, "DB %08x %08x %08x\n", bulkxfer, vtophys(db_tr->db), v
ohcifp->mode.stream.len = ntohs(fp->mode.stream.len);
ohcifp->mode.stream.chtag = chtag;
ohcifp->mode.stream.tcode = 0xa;
ohcifp->mode.stream.spd = 4;
ohcifp->mode.ld[2] = ntohl(fp->mode.ld[1]);
ohcifp->mode.ld[3] = ntohl(fp->mode.ld[2]);
ohcifp->mode.stream.spd = 0;
db_tr->db[2].db.desc.cmd
= OHCI_OUTPUT_LAST
@ -2329,12 +2339,6 @@ device_printf(sc->fc.dev, "DB %08x %08x %08x\n", bulkxfer, vtophys(db_tr->db), v
db_tr = (struct fwohcidb_tr *)bulkxfer->end;
db_tr->db[0].db.desc.depend &= ~0xf;
db_tr->db[dbch->ndesc - 1].db.desc.depend &= ~0xf;
#if 0
/**/
db_tr->db[dbch->ndesc - 1].db.desc.cmd &= ~OHCI_BRANCH_ALWAYS;
db_tr->db[dbch->ndesc - 1].db.desc.cmd |= OHCI_BRANCH_NEVER;
/**/
#endif
db_tr->db[dbch->ndesc - 1].db.desc.cmd |= OHCI_INTERRUPT_ALWAYS;
/* OHCI 1.1 and above */
db_tr->db[0].db.desc.cmd |= OHCI_INTERRUPT_ALWAYS;