Many internal structure changes for the FireWire driver. - Compute CRC in CROM parsing. - Add support for configuration ROM build. - Simplify dummy buffer handling. - busdma conversion - Use swi_taskqueue_giant for -current. Mark the interrupt routine as MPSAFE. - AR buffer handling. Don't reallocate AR buffer but just recycle it. Don't malloc and copy per packet in fwohci_arcv(). Pass packet to fw_rcv() using iovec. Application must prepare receiving buffer in advance. - Change fw_bind API so that application should pre-allocate xfer structure. - Add fw_xfer_unload() for recycling struct fw_xfer. - Add post_busreset hook - Remove unused 'sub' and 'act_type' in struct fw_xfer. - Remove npacket from struct fw_bulkxfer. - Don't call back handlers in fwochi_arcv() if the packet has not drained in AT queue - Make firewire works on big endian platform. - Use native endian for packet header and remove unnecessary ntohX/htonX. - Remove FWXFERQ_PACKET mode. We don't use it anymore. - Remove unnecessary restriction of FWSTMAXCHUNK. - Don't set root node for phy config packet if the root node is not cycle master capable but set myself for root node. We should be the root node after next bus reset. Spotted by: Yoshihiro Tabira <tabira@scd.mei.co.jp> - Improve self id handling Tested on: i386, sparc64 and i386 with forced bounce buffer
2847 lines
73 KiB
C
2847 lines
73 KiB
C
/*
|
|
* Copyright (c) 2003 Hidetoshi Shimokawa
|
|
* Copyright (c) 1998-2002 Katsushi Kobayashi and Hidetoshi Shimokawa
|
|
* 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. All advertising materials mentioning features or use of this software
|
|
* must display the acknowledgement as bellow:
|
|
*
|
|
* This product includes software developed by K. Kobayashi and H. Shimokawa
|
|
*
|
|
* 4. 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.
|
|
*
|
|
* $FreeBSD$
|
|
*
|
|
*/
|
|
|
|
#define ATRQ_CH 0
|
|
#define ATRS_CH 1
|
|
#define ARRQ_CH 2
|
|
#define ARRS_CH 3
|
|
#define ITX_CH 4
|
|
#define IRX_CH 0x24
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/proc.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/types.h>
|
|
#include <sys/mbuf.h>
|
|
#include <sys/mman.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/socketvar.h>
|
|
#include <sys/signalvar.h>
|
|
#include <sys/malloc.h>
|
|
#include <sys/sockio.h>
|
|
#include <sys/bus.h>
|
|
#include <sys/kernel.h>
|
|
#include <sys/conf.h>
|
|
#include <sys/endian.h>
|
|
|
|
#include <machine/bus.h>
|
|
#include <machine/resource.h>
|
|
#include <sys/rman.h>
|
|
|
|
#include <machine/cpufunc.h> /* for rdtsc proto for clock.h below */
|
|
#include <machine/clock.h>
|
|
#include <pci/pcivar.h>
|
|
#include <pci/pcireg.h>
|
|
|
|
#include <dev/firewire/firewire.h>
|
|
#include <dev/firewire/firewirereg.h>
|
|
#include <dev/firewire/fwdma.h>
|
|
#include <dev/firewire/fwohcireg.h>
|
|
#include <dev/firewire/fwohcivar.h>
|
|
#include <dev/firewire/firewire_phy.h>
|
|
|
|
#include <dev/firewire/iec68113.h>
|
|
|
|
#undef OHCI_DEBUG
|
|
|
|
static char dbcode[16][0x10]={"OUTM", "OUTL","INPM","INPL",
|
|
"STOR","LOAD","NOP ","STOP",};
|
|
|
|
static char dbkey[8][0x10]={"ST0", "ST1","ST2","ST3",
|
|
"UNDEF","REG","SYS","DEV"};
|
|
static char dbcond[4][0x10]={"NEV","C=1", "C=0", "ALL"};
|
|
char fwohcicode[32][0x20]={
|
|
"No stat","Undef","long","miss Ack err",
|
|
"underrun","overrun","desc err", "data read err",
|
|
"data write err","bus reset","timeout","tcode err",
|
|
"Undef","Undef","unknown event","flushed",
|
|
"Undef","ack complete","ack pend","Undef",
|
|
"ack busy_X","ack busy_A","ack busy_B","Undef",
|
|
"Undef","Undef","Undef","ack tardy",
|
|
"Undef","ack data_err","ack type_err",""};
|
|
|
|
#define MAX_SPEED 2
|
|
extern char linkspeed[MAX_SPEED+1][0x10];
|
|
u_int32_t tagbit[4] = { 1 << 28, 1 << 29, 1 << 30, 1 << 31};
|
|
|
|
static struct tcode_info tinfo[] = {
|
|
/* hdr_len block flag*/
|
|
/* 0 WREQQ */ {16, FWTI_REQ | FWTI_TLABEL},
|
|
/* 1 WREQB */ {16, FWTI_REQ | FWTI_TLABEL | FWTI_BLOCK_ASY},
|
|
/* 2 WRES */ {12, FWTI_RES},
|
|
/* 3 XXX */ { 0, 0},
|
|
/* 4 RREQQ */ {12, FWTI_REQ | FWTI_TLABEL},
|
|
/* 5 RREQB */ {16, FWTI_REQ | FWTI_TLABEL},
|
|
/* 6 RRESQ */ {16, FWTI_RES},
|
|
/* 7 RRESB */ {16, FWTI_RES | FWTI_BLOCK_ASY},
|
|
/* 8 CYCS */ { 0, 0},
|
|
/* 9 LREQ */ {16, FWTI_REQ | FWTI_TLABEL | FWTI_BLOCK_ASY},
|
|
/* a STREAM */ { 4, FWTI_REQ | FWTI_BLOCK_STR},
|
|
/* b LRES */ {16, FWTI_RES | FWTI_BLOCK_ASY},
|
|
/* c XXX */ { 0, 0},
|
|
/* d XXX */ { 0, 0},
|
|
/* e PHY */ {12, FWTI_REQ},
|
|
/* f XXX */ { 0, 0}
|
|
};
|
|
|
|
#define OHCI_WRITE_SIGMASK 0xffff0000
|
|
#define OHCI_READ_SIGMASK 0xffff0000
|
|
|
|
#define OWRITE(sc, r, x) bus_space_write_4((sc)->bst, (sc)->bsh, (r), (x))
|
|
#define OREAD(sc, r) bus_space_read_4((sc)->bst, (sc)->bsh, (r))
|
|
|
|
static void fwohci_ibr __P((struct firewire_comm *));
|
|
static void fwohci_db_init __P((struct fwohci_softc *, struct fwohci_dbch *));
|
|
static void fwohci_db_free __P((struct fwohci_dbch *));
|
|
static void fwohci_arcv __P((struct fwohci_softc *, struct fwohci_dbch *, int));
|
|
static void fwohci_txd __P((struct fwohci_softc *, struct fwohci_dbch *));
|
|
static void fwohci_start_atq __P((struct firewire_comm *));
|
|
static void fwohci_start_ats __P((struct firewire_comm *));
|
|
static void fwohci_start __P((struct fwohci_softc *, struct fwohci_dbch *));
|
|
static u_int32_t fwphy_wrdata __P(( struct fwohci_softc *, u_int32_t, u_int32_t));
|
|
static u_int32_t fwphy_rddata __P(( struct fwohci_softc *, u_int32_t));
|
|
static int fwohci_rx_enable __P((struct fwohci_softc *, struct fwohci_dbch *));
|
|
static int fwohci_tx_enable __P((struct fwohci_softc *, struct fwohci_dbch *));
|
|
static int fwohci_irx_enable __P((struct firewire_comm *, int));
|
|
static int fwohci_irx_disable __P((struct firewire_comm *, int));
|
|
#if BYTE_ORDER == BIG_ENDIAN
|
|
static void fwohci_irx_post __P((struct firewire_comm *, u_int32_t *));
|
|
#endif
|
|
static int fwohci_itxbuf_enable __P((struct firewire_comm *, int));
|
|
static int fwohci_itx_disable __P((struct firewire_comm *, int));
|
|
static void fwohci_timeout __P((void *));
|
|
static void fwohci_poll __P((struct firewire_comm *, int, int));
|
|
static void fwohci_set_intr __P((struct firewire_comm *, int));
|
|
|
|
static int fwohci_add_rx_buf __P((struct fwohci_dbch *, struct fwohcidb_tr *, int, struct fwdma_alloc *));
|
|
static int fwohci_add_tx_buf __P((struct fwohci_dbch *, struct fwohcidb_tr *, int));
|
|
static void dump_db __P((struct fwohci_softc *, u_int32_t));
|
|
static void print_db __P((struct fwohcidb_tr *, volatile struct fwohcidb *, u_int32_t , u_int32_t));
|
|
static void dump_dma __P((struct fwohci_softc *, u_int32_t));
|
|
static u_int32_t fwohci_cyctimer __P((struct firewire_comm *));
|
|
static void fwohci_rbuf_update __P((struct fwohci_softc *, int));
|
|
static void fwohci_tbuf_update __P((struct fwohci_softc *, int));
|
|
void fwohci_txbufdb __P((struct fwohci_softc *, int , struct fw_bulkxfer *));
|
|
#if FWOHCI_TASKQUEUE
|
|
static void fwohci_complete(void *, int);
|
|
#endif
|
|
|
|
/*
|
|
* memory allocated for DMA programs
|
|
*/
|
|
#define DMA_PROG_ALLOC (8 * PAGE_SIZE)
|
|
|
|
/* #define NDB 1024 */
|
|
#define NDB FWMAXQUEUE
|
|
#define NDVDB (DVBUF * NDB)
|
|
|
|
#define OHCI_VERSION 0x00
|
|
#define OHCI_ATRETRY 0x08
|
|
#define OHCI_CROMHDR 0x18
|
|
#define OHCI_BUS_OPT 0x20
|
|
#define OHCI_BUSIRMC (1 << 31)
|
|
#define OHCI_BUSCMC (1 << 30)
|
|
#define OHCI_BUSISC (1 << 29)
|
|
#define OHCI_BUSBMC (1 << 28)
|
|
#define OHCI_BUSPMC (1 << 27)
|
|
#define OHCI_BUSFNC OHCI_BUSIRMC | OHCI_BUSCMC | OHCI_BUSISC |\
|
|
OHCI_BUSBMC | OHCI_BUSPMC
|
|
|
|
#define OHCI_EUID_HI 0x24
|
|
#define OHCI_EUID_LO 0x28
|
|
|
|
#define OHCI_CROMPTR 0x34
|
|
#define OHCI_HCCCTL 0x50
|
|
#define OHCI_HCCCTLCLR 0x54
|
|
#define OHCI_AREQHI 0x100
|
|
#define OHCI_AREQHICLR 0x104
|
|
#define OHCI_AREQLO 0x108
|
|
#define OHCI_AREQLOCLR 0x10c
|
|
#define OHCI_PREQHI 0x110
|
|
#define OHCI_PREQHICLR 0x114
|
|
#define OHCI_PREQLO 0x118
|
|
#define OHCI_PREQLOCLR 0x11c
|
|
#define OHCI_PREQUPPER 0x120
|
|
|
|
#define OHCI_SID_BUF 0x64
|
|
#define OHCI_SID_CNT 0x68
|
|
#define OHCI_SID_ERR (1 << 31)
|
|
#define OHCI_SID_CNT_MASK 0xffc
|
|
|
|
#define OHCI_IT_STAT 0x90
|
|
#define OHCI_IT_STATCLR 0x94
|
|
#define OHCI_IT_MASK 0x98
|
|
#define OHCI_IT_MASKCLR 0x9c
|
|
|
|
#define OHCI_IR_STAT 0xa0
|
|
#define OHCI_IR_STATCLR 0xa4
|
|
#define OHCI_IR_MASK 0xa8
|
|
#define OHCI_IR_MASKCLR 0xac
|
|
|
|
#define OHCI_LNKCTL 0xe0
|
|
#define OHCI_LNKCTLCLR 0xe4
|
|
|
|
#define OHCI_PHYACCESS 0xec
|
|
#define OHCI_CYCLETIMER 0xf0
|
|
|
|
#define OHCI_DMACTL(off) (off)
|
|
#define OHCI_DMACTLCLR(off) (off + 4)
|
|
#define OHCI_DMACMD(off) (off + 0xc)
|
|
#define OHCI_DMAMATCH(off) (off + 0x10)
|
|
|
|
#define OHCI_ATQOFF 0x180
|
|
#define OHCI_ATQCTL OHCI_ATQOFF
|
|
#define OHCI_ATQCTLCLR (OHCI_ATQOFF + 4)
|
|
#define OHCI_ATQCMD (OHCI_ATQOFF + 0xc)
|
|
#define OHCI_ATQMATCH (OHCI_ATQOFF + 0x10)
|
|
|
|
#define OHCI_ATSOFF 0x1a0
|
|
#define OHCI_ATSCTL OHCI_ATSOFF
|
|
#define OHCI_ATSCTLCLR (OHCI_ATSOFF + 4)
|
|
#define OHCI_ATSCMD (OHCI_ATSOFF + 0xc)
|
|
#define OHCI_ATSMATCH (OHCI_ATSOFF + 0x10)
|
|
|
|
#define OHCI_ARQOFF 0x1c0
|
|
#define OHCI_ARQCTL OHCI_ARQOFF
|
|
#define OHCI_ARQCTLCLR (OHCI_ARQOFF + 4)
|
|
#define OHCI_ARQCMD (OHCI_ARQOFF + 0xc)
|
|
#define OHCI_ARQMATCH (OHCI_ARQOFF + 0x10)
|
|
|
|
#define OHCI_ARSOFF 0x1e0
|
|
#define OHCI_ARSCTL OHCI_ARSOFF
|
|
#define OHCI_ARSCTLCLR (OHCI_ARSOFF + 4)
|
|
#define OHCI_ARSCMD (OHCI_ARSOFF + 0xc)
|
|
#define OHCI_ARSMATCH (OHCI_ARSOFF + 0x10)
|
|
|
|
#define OHCI_ITOFF(CH) (0x200 + 0x10 * (CH))
|
|
#define OHCI_ITCTL(CH) (OHCI_ITOFF(CH))
|
|
#define OHCI_ITCTLCLR(CH) (OHCI_ITOFF(CH) + 4)
|
|
#define OHCI_ITCMD(CH) (OHCI_ITOFF(CH) + 0xc)
|
|
|
|
#define OHCI_IROFF(CH) (0x400 + 0x20 * (CH))
|
|
#define OHCI_IRCTL(CH) (OHCI_IROFF(CH))
|
|
#define OHCI_IRCTLCLR(CH) (OHCI_IROFF(CH) + 4)
|
|
#define OHCI_IRCMD(CH) (OHCI_IROFF(CH) + 0xc)
|
|
#define OHCI_IRMATCH(CH) (OHCI_IROFF(CH) + 0x10)
|
|
|
|
d_ioctl_t fwohci_ioctl;
|
|
|
|
/*
|
|
* Communication with PHY device
|
|
*/
|
|
static u_int32_t
|
|
fwphy_wrdata( struct fwohci_softc *sc, u_int32_t addr, u_int32_t data)
|
|
{
|
|
u_int32_t fun;
|
|
|
|
addr &= 0xf;
|
|
data &= 0xff;
|
|
|
|
fun = (PHYDEV_WRCMD | (addr << PHYDEV_REGADDR) | (data << PHYDEV_WRDATA));
|
|
OWRITE(sc, OHCI_PHYACCESS, fun);
|
|
DELAY(100);
|
|
|
|
return(fwphy_rddata( sc, addr));
|
|
}
|
|
|
|
static u_int32_t
|
|
fwohci_set_bus_manager(struct firewire_comm *fc, u_int node)
|
|
{
|
|
struct fwohci_softc *sc = (struct fwohci_softc *)fc;
|
|
int i;
|
|
u_int32_t bm;
|
|
|
|
#define OHCI_CSR_DATA 0x0c
|
|
#define OHCI_CSR_COMP 0x10
|
|
#define OHCI_CSR_CONT 0x14
|
|
#define OHCI_BUS_MANAGER_ID 0
|
|
|
|
OWRITE(sc, OHCI_CSR_DATA, node);
|
|
OWRITE(sc, OHCI_CSR_COMP, 0x3f);
|
|
OWRITE(sc, OHCI_CSR_CONT, OHCI_BUS_MANAGER_ID);
|
|
for (i = 0; !(OREAD(sc, OHCI_CSR_CONT) & (1<<31)) && (i < 1000); i++)
|
|
DELAY(10);
|
|
bm = OREAD(sc, OHCI_CSR_DATA);
|
|
if((bm & 0x3f) == 0x3f)
|
|
bm = node;
|
|
if (bootverbose)
|
|
device_printf(sc->fc.dev,
|
|
"fw_set_bus_manager: %d->%d (loop=%d)\n", bm, node, i);
|
|
|
|
return(bm);
|
|
}
|
|
|
|
static u_int32_t
|
|
fwphy_rddata(struct fwohci_softc *sc, u_int addr)
|
|
{
|
|
u_int32_t fun, stat;
|
|
u_int i, retry = 0;
|
|
|
|
addr &= 0xf;
|
|
#define MAX_RETRY 100
|
|
again:
|
|
OWRITE(sc, FWOHCI_INTSTATCLR, OHCI_INT_REG_FAIL);
|
|
fun = PHYDEV_RDCMD | (addr << PHYDEV_REGADDR);
|
|
OWRITE(sc, OHCI_PHYACCESS, fun);
|
|
for ( i = 0 ; i < MAX_RETRY ; i ++ ){
|
|
fun = OREAD(sc, OHCI_PHYACCESS);
|
|
if ((fun & PHYDEV_RDCMD) == 0 && (fun & PHYDEV_RDDONE) != 0)
|
|
break;
|
|
DELAY(100);
|
|
}
|
|
if(i >= MAX_RETRY) {
|
|
if (bootverbose)
|
|
device_printf(sc->fc.dev, "phy read failed(1).\n");
|
|
if (++retry < MAX_RETRY) {
|
|
DELAY(100);
|
|
goto again;
|
|
}
|
|
}
|
|
/* Make sure that SCLK is started */
|
|
stat = OREAD(sc, FWOHCI_INTSTAT);
|
|
if ((stat & OHCI_INT_REG_FAIL) != 0 ||
|
|
((fun >> PHYDEV_REGADDR) & 0xf) != addr) {
|
|
if (bootverbose)
|
|
device_printf(sc->fc.dev, "phy read failed(2).\n");
|
|
if (++retry < MAX_RETRY) {
|
|
DELAY(100);
|
|
goto again;
|
|
}
|
|
}
|
|
if (bootverbose || retry >= MAX_RETRY)
|
|
device_printf(sc->fc.dev,
|
|
"fwphy_rddata: loop=%d, retry=%d\n", i, retry);
|
|
#undef MAX_RETRY
|
|
return((fun >> PHYDEV_RDDATA )& 0xff);
|
|
}
|
|
/* Device specific ioctl. */
|
|
int
|
|
fwohci_ioctl (dev_t dev, u_long cmd, caddr_t data, int flag, fw_proc *td)
|
|
{
|
|
struct firewire_softc *sc;
|
|
struct fwohci_softc *fc;
|
|
int unit = DEV2UNIT(dev);
|
|
int err = 0;
|
|
struct fw_reg_req_t *reg = (struct fw_reg_req_t *) data;
|
|
u_int32_t *dmach = (u_int32_t *) data;
|
|
|
|
sc = devclass_get_softc(firewire_devclass, unit);
|
|
if(sc == NULL){
|
|
return(EINVAL);
|
|
}
|
|
fc = (struct fwohci_softc *)sc->fc;
|
|
|
|
if (!data)
|
|
return(EINVAL);
|
|
|
|
switch (cmd) {
|
|
case FWOHCI_WRREG:
|
|
#define OHCI_MAX_REG 0x800
|
|
if(reg->addr <= OHCI_MAX_REG){
|
|
OWRITE(fc, reg->addr, reg->data);
|
|
reg->data = OREAD(fc, reg->addr);
|
|
}else{
|
|
err = EINVAL;
|
|
}
|
|
break;
|
|
case FWOHCI_RDREG:
|
|
if(reg->addr <= OHCI_MAX_REG){
|
|
reg->data = OREAD(fc, reg->addr);
|
|
}else{
|
|
err = EINVAL;
|
|
}
|
|
break;
|
|
/* Read DMA descriptors for debug */
|
|
case DUMPDMA:
|
|
if(*dmach <= OHCI_MAX_DMA_CH ){
|
|
dump_dma(fc, *dmach);
|
|
dump_db(fc, *dmach);
|
|
}else{
|
|
err = EINVAL;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return err;
|
|
}
|
|
|
|
static int
|
|
fwohci_probe_phy(struct fwohci_softc *sc, device_t dev)
|
|
{
|
|
u_int32_t reg, reg2;
|
|
int e1394a = 1;
|
|
/*
|
|
* probe PHY parameters
|
|
* 0. to prove PHY version, whether compliance of 1394a.
|
|
* 1. to probe maximum speed supported by the PHY and
|
|
* number of port supported by core-logic.
|
|
* It is not actually available port on your PC .
|
|
*/
|
|
OWRITE(sc, OHCI_HCCCTL, OHCI_HCC_LPS);
|
|
reg = fwphy_rddata(sc, FW_PHY_SPD_REG);
|
|
|
|
if((reg >> 5) != 7 ){
|
|
sc->fc.mode &= ~FWPHYASYST;
|
|
sc->fc.nport = reg & FW_PHY_NP;
|
|
sc->fc.speed = reg & FW_PHY_SPD >> 6;
|
|
if (sc->fc.speed > MAX_SPEED) {
|
|
device_printf(dev, "invalid speed %d (fixed to %d).\n",
|
|
sc->fc.speed, MAX_SPEED);
|
|
sc->fc.speed = MAX_SPEED;
|
|
}
|
|
device_printf(dev,
|
|
"Phy 1394 only %s, %d ports.\n",
|
|
linkspeed[sc->fc.speed], sc->fc.nport);
|
|
}else{
|
|
reg2 = fwphy_rddata(sc, FW_PHY_ESPD_REG);
|
|
sc->fc.mode |= FWPHYASYST;
|
|
sc->fc.nport = reg & FW_PHY_NP;
|
|
sc->fc.speed = (reg2 & FW_PHY_ESPD) >> 5;
|
|
if (sc->fc.speed > MAX_SPEED) {
|
|
device_printf(dev, "invalid speed %d (fixed to %d).\n",
|
|
sc->fc.speed, MAX_SPEED);
|
|
sc->fc.speed = MAX_SPEED;
|
|
}
|
|
device_printf(dev,
|
|
"Phy 1394a available %s, %d ports.\n",
|
|
linkspeed[sc->fc.speed], sc->fc.nport);
|
|
|
|
/* check programPhyEnable */
|
|
reg2 = fwphy_rddata(sc, 5);
|
|
#if 0
|
|
if (e1394a && (OREAD(sc, OHCI_HCCCTL) & OHCI_HCC_PRPHY)) {
|
|
#else /* XXX force to enable 1394a */
|
|
if (e1394a) {
|
|
#endif
|
|
if (bootverbose)
|
|
device_printf(dev,
|
|
"Enable 1394a Enhancements\n");
|
|
/* enable EAA EMC */
|
|
reg2 |= 0x03;
|
|
/* set aPhyEnhanceEnable */
|
|
OWRITE(sc, OHCI_HCCCTL, OHCI_HCC_PHYEN);
|
|
OWRITE(sc, OHCI_HCCCTLCLR, OHCI_HCC_PRPHY);
|
|
} else {
|
|
/* for safe */
|
|
reg2 &= ~0x83;
|
|
}
|
|
reg2 = fwphy_wrdata(sc, 5, reg2);
|
|
}
|
|
|
|
reg = fwphy_rddata(sc, FW_PHY_SPD_REG);
|
|
if((reg >> 5) == 7 ){
|
|
reg = fwphy_rddata(sc, 4);
|
|
reg |= 1 << 6;
|
|
fwphy_wrdata(sc, 4, reg);
|
|
reg = fwphy_rddata(sc, 4);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
void
|
|
fwohci_reset(struct fwohci_softc *sc, device_t dev)
|
|
{
|
|
int i, max_rec, speed;
|
|
u_int32_t reg, reg2;
|
|
struct fwohcidb_tr *db_tr;
|
|
|
|
/* Disable interrupt */
|
|
OWRITE(sc, FWOHCI_INTMASKCLR, ~0);
|
|
|
|
/* Now stopping all DMA channel */
|
|
OWRITE(sc, OHCI_ARQCTLCLR, OHCI_CNTL_DMA_RUN);
|
|
OWRITE(sc, OHCI_ARSCTLCLR, OHCI_CNTL_DMA_RUN);
|
|
OWRITE(sc, OHCI_ATQCTLCLR, OHCI_CNTL_DMA_RUN);
|
|
OWRITE(sc, OHCI_ATSCTLCLR, OHCI_CNTL_DMA_RUN);
|
|
|
|
OWRITE(sc, OHCI_IR_MASKCLR, ~0);
|
|
for( i = 0 ; i < sc->fc.nisodma ; i ++ ){
|
|
OWRITE(sc, OHCI_IRCTLCLR(i), OHCI_CNTL_DMA_RUN);
|
|
OWRITE(sc, OHCI_ITCTLCLR(i), OHCI_CNTL_DMA_RUN);
|
|
}
|
|
|
|
/* FLUSH FIFO and reset Transmitter/Reciever */
|
|
OWRITE(sc, OHCI_HCCCTL, OHCI_HCC_RESET);
|
|
if (bootverbose)
|
|
device_printf(dev, "resetting OHCI...");
|
|
i = 0;
|
|
while(OREAD(sc, OHCI_HCCCTL) & OHCI_HCC_RESET) {
|
|
if (i++ > 100) break;
|
|
DELAY(1000);
|
|
}
|
|
if (bootverbose)
|
|
printf("done (loop=%d)\n", i);
|
|
|
|
/* Probe phy */
|
|
fwohci_probe_phy(sc, dev);
|
|
|
|
/* Probe link */
|
|
reg = OREAD(sc, OHCI_BUS_OPT);
|
|
reg2 = reg | OHCI_BUSFNC;
|
|
max_rec = (reg & 0x0000f000) >> 12;
|
|
speed = (reg & 0x00000007);
|
|
device_printf(dev, "Link %s, max_rec %d bytes.\n",
|
|
linkspeed[speed], MAXREC(max_rec));
|
|
/* XXX fix max_rec */
|
|
sc->fc.maxrec = sc->fc.speed + 8;
|
|
if (max_rec != sc->fc.maxrec) {
|
|
reg2 = (reg2 & 0xffff0fff) | (sc->fc.maxrec << 12);
|
|
device_printf(dev, "max_rec %d -> %d\n",
|
|
MAXREC(max_rec), MAXREC(sc->fc.maxrec));
|
|
}
|
|
if (bootverbose)
|
|
device_printf(dev, "BUS_OPT 0x%x -> 0x%x\n", reg, reg2);
|
|
OWRITE(sc, OHCI_BUS_OPT, reg2);
|
|
|
|
/* Initialize registers */
|
|
OWRITE(sc, OHCI_CROMHDR, sc->fc.config_rom[0]);
|
|
OWRITE(sc, OHCI_CROMPTR, sc->crom_dma.bus_addr);
|
|
OWRITE(sc, OHCI_HCCCTLCLR, OHCI_HCC_BIGEND);
|
|
OWRITE(sc, OHCI_HCCCTL, OHCI_HCC_POSTWR);
|
|
OWRITE(sc, OHCI_SID_BUF, sc->sid_dma.bus_addr);
|
|
OWRITE(sc, OHCI_LNKCTL, OHCI_CNTL_SID);
|
|
fw_busreset(&sc->fc);
|
|
|
|
/* Enable link */
|
|
OWRITE(sc, OHCI_HCCCTL, OHCI_HCC_LINKEN);
|
|
|
|
/* Force to start async RX DMA */
|
|
sc->arrq.xferq.flag &= ~FWXFERQ_RUNNING;
|
|
sc->arrs.xferq.flag &= ~FWXFERQ_RUNNING;
|
|
fwohci_rx_enable(sc, &sc->arrq);
|
|
fwohci_rx_enable(sc, &sc->arrs);
|
|
|
|
/* Initialize async TX */
|
|
OWRITE(sc, OHCI_ATQCTLCLR, OHCI_CNTL_DMA_RUN | OHCI_CNTL_DMA_DEAD);
|
|
OWRITE(sc, OHCI_ATSCTLCLR, OHCI_CNTL_DMA_RUN | OHCI_CNTL_DMA_DEAD);
|
|
/* AT Retries */
|
|
OWRITE(sc, FWOHCI_RETRY,
|
|
/* CycleLimit PhyRespRetries ATRespRetries ATReqRetries */
|
|
(0xffff << 16 ) | (0x0f << 8) | (0x0f << 4) | 0x0f) ;
|
|
for( i = 0, db_tr = sc->atrq.top; i < sc->atrq.ndb ;
|
|
i ++, db_tr = STAILQ_NEXT(db_tr, link)){
|
|
db_tr->xfer = NULL;
|
|
}
|
|
for( i = 0, db_tr = sc->atrs.top; i < sc->atrs.ndb ;
|
|
i ++, db_tr = STAILQ_NEXT(db_tr, link)){
|
|
db_tr->xfer = NULL;
|
|
}
|
|
|
|
|
|
/* Enable interrupt */
|
|
OWRITE(sc, FWOHCI_INTMASK,
|
|
OHCI_INT_ERR | OHCI_INT_PHY_SID
|
|
| OHCI_INT_DMA_ATRQ | OHCI_INT_DMA_ATRS
|
|
| OHCI_INT_DMA_PRRQ | OHCI_INT_DMA_PRRS
|
|
| OHCI_INT_PHY_BUS_R | OHCI_INT_PW_ERR);
|
|
fwohci_set_intr(&sc->fc, 1);
|
|
|
|
}
|
|
|
|
int
|
|
fwohci_init(struct fwohci_softc *sc, device_t dev)
|
|
{
|
|
int i;
|
|
u_int32_t reg;
|
|
u_int8_t ui[8];
|
|
|
|
#if FWOHCI_TASKQUEUE
|
|
TASK_INIT(&sc->fwohci_task_complete, 0, fwohci_complete, sc);
|
|
#endif
|
|
|
|
reg = OREAD(sc, OHCI_VERSION);
|
|
device_printf(dev, "OHCI version %x.%x (ROM=%d)\n",
|
|
(reg>>16) & 0xff, reg & 0xff, (reg>>24) & 1);
|
|
|
|
/* Available Isochrounous DMA channel probe */
|
|
OWRITE(sc, OHCI_IT_MASK, 0xffffffff);
|
|
OWRITE(sc, OHCI_IR_MASK, 0xffffffff);
|
|
reg = OREAD(sc, OHCI_IT_MASK) & OREAD(sc, OHCI_IR_MASK);
|
|
OWRITE(sc, OHCI_IT_MASKCLR, 0xffffffff);
|
|
OWRITE(sc, OHCI_IR_MASKCLR, 0xffffffff);
|
|
for (i = 0; i < 0x20; i++)
|
|
if ((reg & (1 << i)) == 0)
|
|
break;
|
|
sc->fc.nisodma = i;
|
|
device_printf(dev, "No. of Isochronous channel is %d.\n", i);
|
|
|
|
sc->fc.arq = &sc->arrq.xferq;
|
|
sc->fc.ars = &sc->arrs.xferq;
|
|
sc->fc.atq = &sc->atrq.xferq;
|
|
sc->fc.ats = &sc->atrs.xferq;
|
|
|
|
sc->arrq.xferq.psize = roundup2(FWPMAX_S400, PAGE_SIZE);
|
|
sc->arrs.xferq.psize = roundup2(FWPMAX_S400, PAGE_SIZE);
|
|
sc->atrq.xferq.psize = roundup2(FWPMAX_S400, PAGE_SIZE);
|
|
sc->atrs.xferq.psize = roundup2(FWPMAX_S400, PAGE_SIZE);
|
|
|
|
sc->arrq.xferq.start = NULL;
|
|
sc->arrs.xferq.start = NULL;
|
|
sc->atrq.xferq.start = fwohci_start_atq;
|
|
sc->atrs.xferq.start = fwohci_start_ats;
|
|
|
|
sc->arrq.xferq.buf = NULL;
|
|
sc->arrs.xferq.buf = NULL;
|
|
sc->atrq.xferq.buf = NULL;
|
|
sc->atrs.xferq.buf = NULL;
|
|
|
|
sc->arrq.ndesc = 1;
|
|
sc->arrs.ndesc = 1;
|
|
sc->atrq.ndesc = 8; /* equal to maximum of mbuf chains */
|
|
sc->atrs.ndesc = 2;
|
|
|
|
sc->arrq.ndb = NDB;
|
|
sc->arrs.ndb = NDB / 2;
|
|
sc->atrq.ndb = NDB;
|
|
sc->atrs.ndb = NDB / 2;
|
|
|
|
for( i = 0 ; i < sc->fc.nisodma ; i ++ ){
|
|
sc->fc.it[i] = &sc->it[i].xferq;
|
|
sc->fc.ir[i] = &sc->ir[i].xferq;
|
|
sc->it[i].ndb = 0;
|
|
sc->ir[i].ndb = 0;
|
|
}
|
|
|
|
sc->fc.tcode = tinfo;
|
|
sc->fc.dev = dev;
|
|
|
|
sc->fc.config_rom = fwdma_malloc(&sc->fc, CROMSIZE, CROMSIZE,
|
|
&sc->crom_dma, BUS_DMA_WAITOK);
|
|
if(sc->fc.config_rom == NULL){
|
|
device_printf(dev, "config_rom alloc failed.");
|
|
return ENOMEM;
|
|
}
|
|
|
|
#if 1
|
|
sc->fc.config_rom[1] = 0x31333934;
|
|
sc->fc.config_rom[2] = 0xf000a002;
|
|
sc->fc.config_rom[3] = OREAD(sc, OHCI_EUID_HI);
|
|
sc->fc.config_rom[4] = OREAD(sc, OHCI_EUID_LO);
|
|
sc->fc.config_rom[5] = 0;
|
|
sc->fc.config_rom[0] = (4 << 24) | (5 << 16);
|
|
|
|
sc->fc.config_rom[0] |= fw_crc16(&sc->fc.config_rom[1], 5*4);
|
|
#endif
|
|
|
|
|
|
/* SID recieve buffer must allign 2^11 */
|
|
#define OHCI_SIDSIZE (1 << 11)
|
|
sc->sid_buf = fwdma_malloc(&sc->fc, OHCI_SIDSIZE, OHCI_SIDSIZE,
|
|
&sc->sid_dma, BUS_DMA_WAITOK);
|
|
if (sc->sid_buf == NULL) {
|
|
device_printf(dev, "sid_buf alloc failed.");
|
|
return ENOMEM;
|
|
}
|
|
|
|
fwdma_malloc(&sc->fc, sizeof(u_int32_t), sizeof(u_int32_t),
|
|
&sc->dummy_dma, BUS_DMA_WAITOK);
|
|
|
|
if (sc->dummy_dma.v_addr == NULL) {
|
|
device_printf(dev, "dummy_dma alloc failed.");
|
|
return ENOMEM;
|
|
}
|
|
|
|
fwohci_db_init(sc, &sc->arrq);
|
|
if ((sc->arrq.flags & FWOHCI_DBCH_INIT) == 0)
|
|
return ENOMEM;
|
|
|
|
fwohci_db_init(sc, &sc->arrs);
|
|
if ((sc->arrs.flags & FWOHCI_DBCH_INIT) == 0)
|
|
return ENOMEM;
|
|
|
|
fwohci_db_init(sc, &sc->atrq);
|
|
if ((sc->atrq.flags & FWOHCI_DBCH_INIT) == 0)
|
|
return ENOMEM;
|
|
|
|
fwohci_db_init(sc, &sc->atrs);
|
|
if ((sc->atrs.flags & FWOHCI_DBCH_INIT) == 0)
|
|
return ENOMEM;
|
|
|
|
sc->fc.eui.hi = OREAD(sc, FWOHCIGUID_H);
|
|
sc->fc.eui.lo = OREAD(sc, FWOHCIGUID_L);
|
|
for( i = 0 ; i < 8 ; i ++)
|
|
ui[i] = FW_EUI64_BYTE(&sc->fc.eui,i);
|
|
device_printf(dev, "EUI64 %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n",
|
|
ui[0], ui[1], ui[2], ui[3], ui[4], ui[5], ui[6], ui[7]);
|
|
|
|
sc->fc.ioctl = fwohci_ioctl;
|
|
sc->fc.cyctimer = fwohci_cyctimer;
|
|
sc->fc.set_bmr = fwohci_set_bus_manager;
|
|
sc->fc.ibr = fwohci_ibr;
|
|
sc->fc.irx_enable = fwohci_irx_enable;
|
|
sc->fc.irx_disable = fwohci_irx_disable;
|
|
|
|
sc->fc.itx_enable = fwohci_itxbuf_enable;
|
|
sc->fc.itx_disable = fwohci_itx_disable;
|
|
#if BYTE_ORDER == BIG_ENDIAN
|
|
sc->fc.irx_post = fwohci_irx_post;
|
|
#else
|
|
sc->fc.irx_post = NULL;
|
|
#endif
|
|
sc->fc.itx_post = NULL;
|
|
sc->fc.timeout = fwohci_timeout;
|
|
sc->fc.poll = fwohci_poll;
|
|
sc->fc.set_intr = fwohci_set_intr;
|
|
|
|
sc->intmask = sc->irstat = sc->itstat = 0;
|
|
|
|
fw_init(&sc->fc);
|
|
fwohci_reset(sc, dev);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
fwohci_timeout(void *arg)
|
|
{
|
|
struct fwohci_softc *sc;
|
|
|
|
sc = (struct fwohci_softc *)arg;
|
|
}
|
|
|
|
u_int32_t
|
|
fwohci_cyctimer(struct firewire_comm *fc)
|
|
{
|
|
struct fwohci_softc *sc = (struct fwohci_softc *)fc;
|
|
return(OREAD(sc, OHCI_CYCLETIMER));
|
|
}
|
|
|
|
int
|
|
fwohci_detach(struct fwohci_softc *sc, device_t dev)
|
|
{
|
|
int i;
|
|
|
|
if (sc->sid_buf != NULL)
|
|
fwdma_free(&sc->fc, &sc->sid_dma);
|
|
if (sc->fc.config_rom != NULL)
|
|
fwdma_free(&sc->fc, &sc->crom_dma);
|
|
|
|
fwohci_db_free(&sc->arrq);
|
|
fwohci_db_free(&sc->arrs);
|
|
|
|
fwohci_db_free(&sc->atrq);
|
|
fwohci_db_free(&sc->atrs);
|
|
|
|
for( i = 0 ; i < sc->fc.nisodma ; i ++ ){
|
|
fwohci_db_free(&sc->it[i]);
|
|
fwohci_db_free(&sc->ir[i]);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#define LAST_DB(dbtr, db) do { \
|
|
struct fwohcidb_tr *_dbtr = (dbtr); \
|
|
int _cnt = _dbtr->dbcnt; \
|
|
db = &_dbtr->db[ (_cnt > 2) ? (_cnt -1) : 0]; \
|
|
} while (0)
|
|
|
|
static void
|
|
fwohci_execute_db(void *arg, bus_dma_segment_t *segs, int nseg, int error)
|
|
{
|
|
struct fwohcidb_tr *db_tr;
|
|
volatile struct fwohcidb *db;
|
|
bus_dma_segment_t *s;
|
|
int i;
|
|
|
|
db_tr = (struct fwohcidb_tr *)arg;
|
|
db = &db_tr->db[db_tr->dbcnt];
|
|
if (error) {
|
|
if (firewire_debug || error != EFBIG)
|
|
printf("fwohci_execute_db: error=%d\n", error);
|
|
return;
|
|
}
|
|
for (i = 0; i < nseg; i++) {
|
|
s = &segs[i];
|
|
FWOHCI_DMA_WRITE(db->db.desc.addr, s->ds_addr);
|
|
FWOHCI_DMA_WRITE(db->db.desc.cmd, s->ds_len);
|
|
FWOHCI_DMA_WRITE(db->db.desc.res, 0);
|
|
db++;
|
|
db_tr->dbcnt++;
|
|
}
|
|
}
|
|
|
|
static void
|
|
fwohci_execute_db2(void *arg, bus_dma_segment_t *segs, int nseg,
|
|
bus_size_t size, int error)
|
|
{
|
|
fwohci_execute_db(arg, segs, nseg, error);
|
|
}
|
|
|
|
static void
|
|
fwohci_start(struct fwohci_softc *sc, struct fwohci_dbch *dbch)
|
|
{
|
|
int i, s;
|
|
int tcode, hdr_len, pl_off, pl_len;
|
|
int fsegment = -1;
|
|
u_int32_t off;
|
|
struct fw_xfer *xfer;
|
|
struct fw_pkt *fp;
|
|
volatile struct fwohci_txpkthdr *ohcifp;
|
|
struct fwohcidb_tr *db_tr;
|
|
volatile struct fwohcidb *db;
|
|
struct tcode_info *info;
|
|
static int maxdesc=0;
|
|
|
|
if(&sc->atrq == dbch){
|
|
off = OHCI_ATQOFF;
|
|
}else if(&sc->atrs == dbch){
|
|
off = OHCI_ATSOFF;
|
|
}else{
|
|
return;
|
|
}
|
|
|
|
if (dbch->flags & FWOHCI_DBCH_FULL)
|
|
return;
|
|
|
|
s = splfw();
|
|
db_tr = dbch->top;
|
|
txloop:
|
|
xfer = STAILQ_FIRST(&dbch->xferq.q);
|
|
if(xfer == NULL){
|
|
goto kick;
|
|
}
|
|
if(dbch->xferq.queued == 0 ){
|
|
device_printf(sc->fc.dev, "TX queue empty\n");
|
|
}
|
|
STAILQ_REMOVE_HEAD(&dbch->xferq.q, link);
|
|
db_tr->xfer = xfer;
|
|
xfer->state = FWXF_START;
|
|
|
|
fp = (struct fw_pkt *)xfer->send.buf;
|
|
tcode = fp->mode.common.tcode;
|
|
|
|
ohcifp = (volatile struct fwohci_txpkthdr *) db_tr->db[1].db.immed;
|
|
info = &tinfo[tcode];
|
|
hdr_len = pl_off = info->hdr_len;
|
|
for( i = 0 ; i < pl_off ; i+= 4){
|
|
ohcifp->mode.ld[i/4] = fp->mode.ld[i/4];
|
|
}
|
|
ohcifp->mode.common.spd = xfer->spd;
|
|
if (tcode == FWTCODE_STREAM ){
|
|
hdr_len = 8;
|
|
ohcifp->mode.stream.len = fp->mode.stream.len;
|
|
} else if (tcode == FWTCODE_PHY) {
|
|
hdr_len = 12;
|
|
ohcifp->mode.ld[1] = fp->mode.ld[1];
|
|
ohcifp->mode.ld[2] = fp->mode.ld[2];
|
|
ohcifp->mode.common.spd = 0;
|
|
ohcifp->mode.common.tcode = FWOHCITCODE_PHY;
|
|
} else {
|
|
ohcifp->mode.asycomm.dst = fp->mode.hdr.dst;
|
|
ohcifp->mode.asycomm.srcbus = OHCI_ASYSRCBUS;
|
|
ohcifp->mode.asycomm.tlrt |= FWRETRY_X;
|
|
}
|
|
db = &db_tr->db[0];
|
|
FWOHCI_DMA_WRITE(db->db.desc.cmd,
|
|
OHCI_OUTPUT_MORE | OHCI_KEY_ST2 | hdr_len);
|
|
FWOHCI_DMA_WRITE(db->db.desc.res, 0);
|
|
/* Specify bound timer of asy. responce */
|
|
if(&sc->atrs == dbch){
|
|
FWOHCI_DMA_WRITE(db->db.desc.res,
|
|
(OREAD(sc, OHCI_CYCLETIMER) >> 12) + (1 << 13));
|
|
}
|
|
#if BYTE_ORDER == BIG_ENDIAN
|
|
if (tcode == FWTCODE_WREQQ || tcode == FWTCODE_RRESQ)
|
|
hdr_len = 12;
|
|
for (i = 0; i < hdr_len/4; i ++)
|
|
FWOHCI_DMA_WRITE(ohcifp->mode.ld[i], ohcifp->mode.ld[i]);
|
|
#endif
|
|
|
|
again:
|
|
db_tr->dbcnt = 2;
|
|
db = &db_tr->db[db_tr->dbcnt];
|
|
pl_len = xfer->send.len - pl_off;
|
|
if (pl_len > 0) {
|
|
int err;
|
|
/* handle payload */
|
|
if (xfer->mbuf == NULL) {
|
|
caddr_t pl_addr;
|
|
|
|
pl_addr = xfer->send.buf + pl_off;
|
|
err = bus_dmamap_load(dbch->dmat, db_tr->dma_map,
|
|
pl_addr, pl_len,
|
|
fwohci_execute_db, db_tr,
|
|
/*flags*/0);
|
|
} else {
|
|
/* XXX we can handle only 6 (=8-2) mbuf chains */
|
|
err = bus_dmamap_load_mbuf(dbch->dmat, db_tr->dma_map,
|
|
xfer->mbuf,
|
|
fwohci_execute_db2, db_tr,
|
|
/* flags */0);
|
|
if (err == EFBIG) {
|
|
struct mbuf *m0;
|
|
|
|
if (firewire_debug)
|
|
device_printf(sc->fc.dev, "EFBIG.\n");
|
|
m0 = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR);
|
|
if (m0 != NULL) {
|
|
m_copydata(xfer->mbuf, 0,
|
|
xfer->mbuf->m_pkthdr.len,
|
|
mtod(m0, caddr_t));
|
|
m0->m_len = m0->m_pkthdr.len =
|
|
xfer->mbuf->m_pkthdr.len;
|
|
m_freem(xfer->mbuf);
|
|
xfer->mbuf = m0;
|
|
goto again;
|
|
}
|
|
device_printf(sc->fc.dev, "m_getcl failed.\n");
|
|
}
|
|
}
|
|
if (err)
|
|
printf("dmamap_load: err=%d\n", err);
|
|
bus_dmamap_sync(dbch->dmat, db_tr->dma_map,
|
|
BUS_DMASYNC_PREWRITE);
|
|
#if 0 /* OHCI_OUTPUT_MODE == 0 */
|
|
for (i = 2; i < db_tr->dbcnt; i++)
|
|
FWOHCI_DMA_SET(db_tr->db[i].db.desc.cmd,
|
|
OHCI_OUTPUT_MORE);
|
|
#endif
|
|
}
|
|
if (maxdesc < db_tr->dbcnt) {
|
|
maxdesc = db_tr->dbcnt;
|
|
if (bootverbose)
|
|
device_printf(sc->fc.dev, "maxdesc: %d\n", maxdesc);
|
|
}
|
|
/* last db */
|
|
LAST_DB(db_tr, db);
|
|
FWOHCI_DMA_SET(db->db.desc.cmd,
|
|
OHCI_OUTPUT_LAST | OHCI_INTERRUPT_ALWAYS | OHCI_BRANCH_ALWAYS);
|
|
FWOHCI_DMA_WRITE(db->db.desc.depend,
|
|
STAILQ_NEXT(db_tr, link)->bus_addr);
|
|
|
|
if(fsegment == -1 )
|
|
fsegment = db_tr->dbcnt;
|
|
if (dbch->pdb_tr != NULL) {
|
|
LAST_DB(dbch->pdb_tr, db);
|
|
FWOHCI_DMA_SET(db->db.desc.depend, db_tr->dbcnt);
|
|
}
|
|
dbch->pdb_tr = db_tr;
|
|
db_tr = STAILQ_NEXT(db_tr, link);
|
|
if(db_tr != dbch->bottom){
|
|
goto txloop;
|
|
} else {
|
|
device_printf(sc->fc.dev, "fwohci_start: lack of db_trq\n");
|
|
dbch->flags |= FWOHCI_DBCH_FULL;
|
|
}
|
|
kick:
|
|
/* kick asy q */
|
|
fwdma_sync_multiseg_all(dbch->am, BUS_DMASYNC_PREREAD);
|
|
fwdma_sync_multiseg_all(dbch->am, BUS_DMASYNC_PREWRITE);
|
|
|
|
if(dbch->xferq.flag & FWXFERQ_RUNNING) {
|
|
OWRITE(sc, OHCI_DMACTL(off), OHCI_CNTL_DMA_WAKE);
|
|
} else {
|
|
if (bootverbose)
|
|
device_printf(sc->fc.dev, "start AT DMA status=%x\n",
|
|
OREAD(sc, OHCI_DMACTL(off)));
|
|
OWRITE(sc, OHCI_DMACMD(off), dbch->top->bus_addr | fsegment);
|
|
OWRITE(sc, OHCI_DMACTL(off), OHCI_CNTL_DMA_RUN);
|
|
dbch->xferq.flag |= FWXFERQ_RUNNING;
|
|
}
|
|
|
|
dbch->top = db_tr;
|
|
splx(s);
|
|
return;
|
|
}
|
|
|
|
static void
|
|
fwohci_start_atq(struct firewire_comm *fc)
|
|
{
|
|
struct fwohci_softc *sc = (struct fwohci_softc *)fc;
|
|
fwohci_start( sc, &(sc->atrq));
|
|
return;
|
|
}
|
|
|
|
static void
|
|
fwohci_start_ats(struct firewire_comm *fc)
|
|
{
|
|
struct fwohci_softc *sc = (struct fwohci_softc *)fc;
|
|
fwohci_start( sc, &(sc->atrs));
|
|
return;
|
|
}
|
|
|
|
void
|
|
fwohci_txd(struct fwohci_softc *sc, struct fwohci_dbch *dbch)
|
|
{
|
|
int s, ch, err = 0;
|
|
struct fwohcidb_tr *tr;
|
|
volatile struct fwohcidb *db;
|
|
struct fw_xfer *xfer;
|
|
u_int32_t off;
|
|
u_int stat, status;
|
|
int packets;
|
|
struct firewire_comm *fc = (struct firewire_comm *)sc;
|
|
|
|
if(&sc->atrq == dbch){
|
|
off = OHCI_ATQOFF;
|
|
ch = ATRQ_CH;
|
|
}else if(&sc->atrs == dbch){
|
|
off = OHCI_ATSOFF;
|
|
ch = ATRS_CH;
|
|
}else{
|
|
return;
|
|
}
|
|
s = splfw();
|
|
tr = dbch->bottom;
|
|
packets = 0;
|
|
fwdma_sync_multiseg_all(dbch->am, BUS_DMASYNC_POSTREAD);
|
|
fwdma_sync_multiseg_all(dbch->am, BUS_DMASYNC_POSTWRITE);
|
|
while(dbch->xferq.queued > 0){
|
|
LAST_DB(tr, db);
|
|
status = FWOHCI_DMA_READ(db->db.desc.res) >> OHCI_STATUS_SHIFT;
|
|
if(!(status & OHCI_CNTL_DMA_ACTIVE)){
|
|
if (fc->status != FWBUSRESET)
|
|
/* maybe out of order?? */
|
|
goto out;
|
|
}
|
|
bus_dmamap_sync(dbch->dmat, tr->dma_map,
|
|
BUS_DMASYNC_POSTWRITE);
|
|
bus_dmamap_unload(dbch->dmat, tr->dma_map);
|
|
#if 0
|
|
dump_db(sc, ch);
|
|
#endif
|
|
if(status & OHCI_CNTL_DMA_DEAD) {
|
|
/* Stop DMA */
|
|
OWRITE(sc, OHCI_DMACTLCLR(off), OHCI_CNTL_DMA_RUN);
|
|
device_printf(sc->fc.dev, "force reset AT FIFO\n");
|
|
OWRITE(sc, OHCI_HCCCTLCLR, OHCI_HCC_LINKEN);
|
|
OWRITE(sc, OHCI_HCCCTL, OHCI_HCC_LPS | OHCI_HCC_LINKEN);
|
|
OWRITE(sc, OHCI_DMACTLCLR(off), OHCI_CNTL_DMA_RUN);
|
|
}
|
|
stat = status & FWOHCIEV_MASK;
|
|
switch(stat){
|
|
case FWOHCIEV_ACKPEND:
|
|
case FWOHCIEV_ACKCOMPL:
|
|
err = 0;
|
|
break;
|
|
case FWOHCIEV_ACKBSA:
|
|
case FWOHCIEV_ACKBSB:
|
|
case FWOHCIEV_ACKBSX:
|
|
device_printf(sc->fc.dev, "txd err=%2x %s\n", stat, fwohcicode[stat]);
|
|
err = EBUSY;
|
|
break;
|
|
case FWOHCIEV_FLUSHED:
|
|
case FWOHCIEV_ACKTARD:
|
|
device_printf(sc->fc.dev, "txd err=%2x %s\n", stat, fwohcicode[stat]);
|
|
err = EAGAIN;
|
|
break;
|
|
case FWOHCIEV_MISSACK:
|
|
case FWOHCIEV_UNDRRUN:
|
|
case FWOHCIEV_OVRRUN:
|
|
case FWOHCIEV_DESCERR:
|
|
case FWOHCIEV_DTRDERR:
|
|
case FWOHCIEV_TIMEOUT:
|
|
case FWOHCIEV_TCODERR:
|
|
case FWOHCIEV_UNKNOWN:
|
|
case FWOHCIEV_ACKDERR:
|
|
case FWOHCIEV_ACKTERR:
|
|
default:
|
|
device_printf(sc->fc.dev, "txd err=%2x %s\n",
|
|
stat, fwohcicode[stat]);
|
|
err = EINVAL;
|
|
break;
|
|
}
|
|
if (tr->xfer != NULL) {
|
|
xfer = tr->xfer;
|
|
if (xfer->state == FWXF_RCVD) {
|
|
if (firewire_debug)
|
|
printf("already rcvd\n");
|
|
fw_xfer_done(xfer);
|
|
} else {
|
|
xfer->state = FWXF_SENT;
|
|
if (err == EBUSY && fc->status != FWBUSRESET) {
|
|
xfer->state = FWXF_BUSY;
|
|
xfer->resp = err;
|
|
if (xfer->retry_req != NULL)
|
|
xfer->retry_req(xfer);
|
|
else
|
|
fw_xfer_done(xfer);
|
|
} else if (stat != FWOHCIEV_ACKPEND) {
|
|
if (stat != FWOHCIEV_ACKCOMPL)
|
|
xfer->state = FWXF_SENTERR;
|
|
xfer->resp = err;
|
|
fw_xfer_done(xfer);
|
|
}
|
|
}
|
|
/*
|
|
* The watchdog timer takes care of split
|
|
* transcation timeout for ACKPEND case.
|
|
*/
|
|
} else {
|
|
printf("this shouldn't happen\n");
|
|
}
|
|
dbch->xferq.queued --;
|
|
tr->xfer = NULL;
|
|
|
|
packets ++;
|
|
tr = STAILQ_NEXT(tr, link);
|
|
dbch->bottom = tr;
|
|
if (dbch->bottom == dbch->top) {
|
|
/* we reaches the end of context program */
|
|
if (firewire_debug && dbch->xferq.queued > 0)
|
|
printf("queued > 0\n");
|
|
break;
|
|
}
|
|
}
|
|
out:
|
|
if ((dbch->flags & FWOHCI_DBCH_FULL) && packets > 0) {
|
|
printf("make free slot\n");
|
|
dbch->flags &= ~FWOHCI_DBCH_FULL;
|
|
fwohci_start(sc, dbch);
|
|
}
|
|
splx(s);
|
|
}
|
|
|
|
static void
|
|
fwohci_db_free(struct fwohci_dbch *dbch)
|
|
{
|
|
struct fwohcidb_tr *db_tr;
|
|
int idb;
|
|
|
|
if ((dbch->flags & FWOHCI_DBCH_INIT) == 0)
|
|
return;
|
|
|
|
for(db_tr = STAILQ_FIRST(&dbch->db_trq), idb = 0; idb < dbch->ndb;
|
|
db_tr = STAILQ_NEXT(db_tr, link), idb++){
|
|
if ((dbch->xferq.flag & FWXFERQ_EXTBUF) == 0 &&
|
|
db_tr->buf != NULL) {
|
|
fwdma_free_size(dbch->dmat, db_tr->dma_map,
|
|
db_tr->buf, dbch->xferq.psize);
|
|
db_tr->buf = NULL;
|
|
} else if (db_tr->dma_map != NULL)
|
|
bus_dmamap_destroy(dbch->dmat, db_tr->dma_map);
|
|
}
|
|
dbch->ndb = 0;
|
|
db_tr = STAILQ_FIRST(&dbch->db_trq);
|
|
fwdma_free_multiseg(dbch->am);
|
|
free(db_tr, M_FW);
|
|
STAILQ_INIT(&dbch->db_trq);
|
|
dbch->flags &= ~FWOHCI_DBCH_INIT;
|
|
}
|
|
|
|
static void
|
|
fwohci_db_init(struct fwohci_softc *sc, struct fwohci_dbch *dbch)
|
|
{
|
|
int idb;
|
|
struct fwohcidb_tr *db_tr;
|
|
|
|
if ((dbch->flags & FWOHCI_DBCH_INIT) != 0)
|
|
goto out;
|
|
|
|
/* create dma_tag for buffers */
|
|
#define MAX_REQCOUNT 0xffff
|
|
if (bus_dma_tag_create(/*parent*/ sc->fc.dmat,
|
|
/*alignment*/ 1, /*boundary*/ 0,
|
|
/*lowaddr*/ BUS_SPACE_MAXADDR_32BIT,
|
|
/*highaddr*/ BUS_SPACE_MAXADDR,
|
|
/*filter*/NULL, /*filterarg*/NULL,
|
|
/*maxsize*/ dbch->xferq.psize,
|
|
/*nsegments*/ dbch->ndesc > 3 ? dbch->ndesc - 2 : 1,
|
|
/*maxsegsz*/ MAX_REQCOUNT,
|
|
/*flags*/ 0, &dbch->dmat))
|
|
return;
|
|
|
|
/* allocate DB entries and attach one to each DMA channels */
|
|
/* DB entry must start at 16 bytes bounary. */
|
|
STAILQ_INIT(&dbch->db_trq);
|
|
db_tr = (struct fwohcidb_tr *)
|
|
malloc(sizeof(struct fwohcidb_tr) * dbch->ndb,
|
|
M_FW, M_WAITOK | M_ZERO);
|
|
if(db_tr == NULL){
|
|
printf("fwohci_db_init: malloc(1) failed\n");
|
|
return;
|
|
}
|
|
|
|
#define DB_SIZE(x) (sizeof(struct fwohcidb) * (x)->ndesc)
|
|
dbch->am = fwdma_malloc_multiseg(&sc->fc, DB_SIZE(dbch),
|
|
DB_SIZE(dbch), dbch->ndb, BUS_DMA_WAITOK);
|
|
if (dbch->am == NULL) {
|
|
printf("fwohci_db_init: fwdma_malloc_multiseg failed\n");
|
|
return;
|
|
}
|
|
/* Attach DB to DMA ch. */
|
|
for(idb = 0 ; idb < dbch->ndb ; idb++){
|
|
db_tr->dbcnt = 0;
|
|
db_tr->db = (struct fwohcidb *)fwdma_v_addr(dbch->am, idb);
|
|
db_tr->bus_addr = fwdma_bus_addr(dbch->am, idb);
|
|
/* create dmamap for buffers */
|
|
/* XXX do we need 4bytes alignment tag? */
|
|
/* XXX don't alloc dma_map for AR */
|
|
if (bus_dmamap_create(dbch->dmat, 0, &db_tr->dma_map) != 0) {
|
|
printf("bus_dmamap_create failed\n");
|
|
dbch->flags = FWOHCI_DBCH_INIT; /* XXX fake */
|
|
fwohci_db_free(dbch);
|
|
return;
|
|
}
|
|
STAILQ_INSERT_TAIL(&dbch->db_trq, db_tr, link);
|
|
if (dbch->xferq.flag & FWXFERQ_EXTBUF) {
|
|
if (idb % dbch->xferq.bnpacket == 0)
|
|
dbch->xferq.bulkxfer[idb / dbch->xferq.bnpacket
|
|
].start = (caddr_t)db_tr;
|
|
if ((idb + 1) % dbch->xferq.bnpacket == 0)
|
|
dbch->xferq.bulkxfer[idb / dbch->xferq.bnpacket
|
|
].end = (caddr_t)db_tr;
|
|
}
|
|
db_tr++;
|
|
}
|
|
STAILQ_LAST(&dbch->db_trq, fwohcidb_tr,link)->link.stqe_next
|
|
= STAILQ_FIRST(&dbch->db_trq);
|
|
out:
|
|
dbch->xferq.queued = 0;
|
|
dbch->pdb_tr = NULL;
|
|
dbch->top = STAILQ_FIRST(&dbch->db_trq);
|
|
dbch->bottom = dbch->top;
|
|
dbch->flags = FWOHCI_DBCH_INIT;
|
|
}
|
|
|
|
static int
|
|
fwohci_itx_disable(struct firewire_comm *fc, int dmach)
|
|
{
|
|
struct fwohci_softc *sc = (struct fwohci_softc *)fc;
|
|
int sleepch;
|
|
|
|
OWRITE(sc, OHCI_ITCTLCLR(dmach),
|
|
OHCI_CNTL_DMA_RUN | OHCI_CNTL_CYCMATCH_S);
|
|
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 *)&sleepch, FWPRI, "fwitxd", hz);
|
|
fwohci_db_free(&sc->it[dmach]);
|
|
sc->it[dmach].xferq.flag &= ~FWXFERQ_RUNNING;
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
fwohci_irx_disable(struct firewire_comm *fc, int dmach)
|
|
{
|
|
struct fwohci_softc *sc = (struct fwohci_softc *)fc;
|
|
int sleepch;
|
|
|
|
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 *)&sleepch, FWPRI, "fwirxd", hz);
|
|
fwohci_db_free(&sc->ir[dmach]);
|
|
sc->ir[dmach].xferq.flag &= ~FWXFERQ_RUNNING;
|
|
return 0;
|
|
}
|
|
|
|
#if BYTE_ORDER == BIG_ENDIAN
|
|
static void
|
|
fwohci_irx_post (struct firewire_comm *fc , u_int32_t *qld)
|
|
{
|
|
qld[0] = FWOHCI_DMA_READ(qld[0]);
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
static int
|
|
fwohci_tx_enable(struct fwohci_softc *sc, struct fwohci_dbch *dbch)
|
|
{
|
|
int err = 0;
|
|
int idb, z, i, dmach = 0, ldesc;
|
|
u_int32_t off = NULL;
|
|
struct fwohcidb_tr *db_tr;
|
|
volatile struct fwohcidb *db;
|
|
|
|
if(!(dbch->xferq.flag & FWXFERQ_EXTBUF)){
|
|
err = EINVAL;
|
|
return err;
|
|
}
|
|
z = dbch->ndesc;
|
|
for(dmach = 0 ; dmach < sc->fc.nisodma ; dmach++){
|
|
if( &sc->it[dmach] == dbch){
|
|
off = OHCI_ITOFF(dmach);
|
|
break;
|
|
}
|
|
}
|
|
if(off == NULL){
|
|
err = EINVAL;
|
|
return err;
|
|
}
|
|
if(dbch->xferq.flag & FWXFERQ_RUNNING)
|
|
return err;
|
|
dbch->xferq.flag |= FWXFERQ_RUNNING;
|
|
for( i = 0, dbch->bottom = dbch->top; i < (dbch->ndb - 1); i++){
|
|
dbch->bottom = STAILQ_NEXT(dbch->bottom, link);
|
|
}
|
|
db_tr = dbch->top;
|
|
for (idb = 0; idb < dbch->ndb; idb ++) {
|
|
fwohci_add_tx_buf(dbch, db_tr, idb);
|
|
if(STAILQ_NEXT(db_tr, link) == NULL){
|
|
break;
|
|
}
|
|
db = db_tr->db;
|
|
ldesc = db_tr->dbcnt - 1;
|
|
FWOHCI_DMA_WRITE(db[0].db.desc.depend,
|
|
STAILQ_NEXT(db_tr, link)->bus_addr | z);
|
|
db[ldesc].db.desc.depend = db[0].db.desc.depend;
|
|
if(dbch->xferq.flag & FWXFERQ_EXTBUF){
|
|
if(((idb + 1 ) % dbch->xferq.bnpacket) == 0){
|
|
FWOHCI_DMA_SET(
|
|
db[ldesc].db.desc.cmd,
|
|
OHCI_INTERRUPT_ALWAYS);
|
|
/* OHCI 1.1 and above */
|
|
FWOHCI_DMA_SET(
|
|
db[0].db.desc.cmd,
|
|
OHCI_INTERRUPT_ALWAYS);
|
|
}
|
|
}
|
|
db_tr = STAILQ_NEXT(db_tr, link);
|
|
}
|
|
FWOHCI_DMA_CLEAR(
|
|
dbch->bottom->db[dbch->bottom->dbcnt - 1].db.desc.depend, 0xf);
|
|
return err;
|
|
}
|
|
|
|
static int
|
|
fwohci_rx_enable(struct fwohci_softc *sc, struct fwohci_dbch *dbch)
|
|
{
|
|
int err = 0;
|
|
int idb, z, i, dmach = 0, ldesc;
|
|
u_int32_t off = NULL;
|
|
struct fwohcidb_tr *db_tr;
|
|
volatile struct fwohcidb *db;
|
|
|
|
z = dbch->ndesc;
|
|
if(&sc->arrq == dbch){
|
|
off = OHCI_ARQOFF;
|
|
}else if(&sc->arrs == dbch){
|
|
off = OHCI_ARSOFF;
|
|
}else{
|
|
for(dmach = 0 ; dmach < sc->fc.nisodma ; dmach++){
|
|
if( &sc->ir[dmach] == dbch){
|
|
off = OHCI_IROFF(dmach);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if(off == NULL){
|
|
err = EINVAL;
|
|
return err;
|
|
}
|
|
if(dbch->xferq.flag & FWXFERQ_STREAM){
|
|
if(dbch->xferq.flag & FWXFERQ_RUNNING)
|
|
return err;
|
|
}else{
|
|
if(dbch->xferq.flag & FWXFERQ_RUNNING){
|
|
err = EBUSY;
|
|
return err;
|
|
}
|
|
}
|
|
dbch->xferq.flag |= FWXFERQ_RUNNING;
|
|
dbch->top = STAILQ_FIRST(&dbch->db_trq);
|
|
for( i = 0, dbch->bottom = dbch->top; i < (dbch->ndb - 1); i++){
|
|
dbch->bottom = STAILQ_NEXT(dbch->bottom, link);
|
|
}
|
|
db_tr = dbch->top;
|
|
for (idb = 0; idb < dbch->ndb; idb ++) {
|
|
fwohci_add_rx_buf(dbch, db_tr, idb, &sc->dummy_dma);
|
|
if (STAILQ_NEXT(db_tr, link) == NULL)
|
|
break;
|
|
db = db_tr->db;
|
|
ldesc = db_tr->dbcnt - 1;
|
|
FWOHCI_DMA_WRITE(db[ldesc].db.desc.depend,
|
|
STAILQ_NEXT(db_tr, link)->bus_addr | z);
|
|
if(dbch->xferq.flag & FWXFERQ_EXTBUF){
|
|
if(((idb + 1 ) % dbch->xferq.bnpacket) == 0){
|
|
FWOHCI_DMA_SET(
|
|
db[ldesc].db.desc.cmd,
|
|
OHCI_INTERRUPT_ALWAYS);
|
|
FWOHCI_DMA_CLEAR(
|
|
db[ldesc].db.desc.depend,
|
|
0xf);
|
|
}
|
|
}
|
|
db_tr = STAILQ_NEXT(db_tr, link);
|
|
}
|
|
FWOHCI_DMA_CLEAR(
|
|
dbch->bottom->db[db_tr->dbcnt - 1].db.desc.depend, 0xf);
|
|
dbch->buf_offset = 0;
|
|
fwdma_sync_multiseg_all(dbch->am, BUS_DMASYNC_PREREAD);
|
|
fwdma_sync_multiseg_all(dbch->am, BUS_DMASYNC_PREWRITE);
|
|
if(dbch->xferq.flag & FWXFERQ_STREAM){
|
|
return err;
|
|
}else{
|
|
OWRITE(sc, OHCI_DMACMD(off), dbch->top->bus_addr | z);
|
|
}
|
|
OWRITE(sc, OHCI_DMACTL(off), OHCI_CNTL_DMA_RUN);
|
|
return err;
|
|
}
|
|
|
|
static int
|
|
fwohci_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
|
|
#if 1
|
|
#define CYCLE_DELAY 8 /* min delay to start DMA */
|
|
#else
|
|
#define CYCLE_DELAY 7000 /* min delay to start DMA */
|
|
#endif
|
|
cycle = cycle + CYCLE_DELAY;
|
|
if (cycle >= 8000) {
|
|
sec ++;
|
|
cycle -= 8000;
|
|
}
|
|
cycle = roundup2(cycle, 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)
|
|
{
|
|
struct fwohci_softc *sc = (struct fwohci_softc *)fc;
|
|
int err = 0;
|
|
unsigned short tag, ich;
|
|
struct fwohci_dbch *dbch;
|
|
int cycle_match, cycle_now, s, ldesc;
|
|
u_int32_t stat;
|
|
struct fw_bulkxfer *first, *chunk, *prev;
|
|
struct fw_xferq *it;
|
|
|
|
dbch = &sc->it[dmach];
|
|
it = &dbch->xferq;
|
|
|
|
tag = (it->flag >> 6) & 3;
|
|
ich = it->flag & 0x3f;
|
|
if ((dbch->flags & FWOHCI_DBCH_INIT) == 0) {
|
|
dbch->ndb = it->bnpacket * it->bnchunk;
|
|
dbch->ndesc = 3;
|
|
fwohci_db_init(sc, dbch);
|
|
if ((dbch->flags & FWOHCI_DBCH_INIT) == 0)
|
|
return ENOMEM;
|
|
err = fwohci_tx_enable(sc, dbch);
|
|
}
|
|
if(err)
|
|
return err;
|
|
|
|
ldesc = dbch->ndesc - 1;
|
|
s = splfw();
|
|
prev = STAILQ_LAST(&it->stdma, fw_bulkxfer, link);
|
|
while ((chunk = STAILQ_FIRST(&it->stvalid)) != NULL) {
|
|
volatile struct fwohcidb *db;
|
|
|
|
fwdma_sync_multiseg(it->buf, chunk->poffset, it->bnpacket,
|
|
BUS_DMASYNC_PREWRITE);
|
|
fwohci_txbufdb(sc, dmach, chunk);
|
|
if (prev != NULL) {
|
|
db = ((struct fwohcidb_tr *)(prev->end))->db;
|
|
#if 0 /* XXX necessary? */
|
|
FWOHCI_DMA_SET(db[ldesc].db.desc.cmd,
|
|
OHCI_BRANCH_ALWAYS);
|
|
#endif
|
|
#if 0 /* if bulkxfer->npacket changes */
|
|
db[ldesc].db.desc.depend = db[0].db.desc.depend =
|
|
((struct fwohcidb_tr *)
|
|
(chunk->start))->bus_addr | dbch->ndesc;
|
|
#else
|
|
FWOHCI_DMA_SET(db[0].db.desc.depend, dbch->ndesc);
|
|
FWOHCI_DMA_SET(db[ldesc].db.desc.depend, dbch->ndesc);
|
|
#endif
|
|
}
|
|
STAILQ_REMOVE_HEAD(&it->stvalid, link);
|
|
STAILQ_INSERT_TAIL(&it->stdma, chunk, link);
|
|
prev = chunk;
|
|
}
|
|
fwdma_sync_multiseg_all(dbch->am, BUS_DMASYNC_PREWRITE);
|
|
fwdma_sync_multiseg_all(dbch->am, BUS_DMASYNC_PREREAD);
|
|
splx(s);
|
|
stat = OREAD(sc, OHCI_ITCTL(dmach));
|
|
if (firewire_debug && (stat & OHCI_CNTL_CYCMATCH_S))
|
|
printf("stat 0x%x\n", stat);
|
|
|
|
if (stat & (OHCI_CNTL_DMA_ACTIVE | OHCI_CNTL_CYCMATCH_S))
|
|
return 0;
|
|
|
|
#if 0
|
|
OWRITE(sc, OHCI_ITCTLCLR(dmach), OHCI_CNTL_DMA_RUN);
|
|
#endif
|
|
OWRITE(sc, OHCI_IT_MASKCLR, 1 << dmach);
|
|
OWRITE(sc, OHCI_IT_STATCLR, 1 << dmach);
|
|
OWRITE(sc, OHCI_IT_MASK, 1 << dmach);
|
|
OWRITE(sc, FWOHCI_INTMASK, OHCI_INT_DMA_IT);
|
|
|
|
first = STAILQ_FIRST(&it->stdma);
|
|
OWRITE(sc, OHCI_ITCMD(dmach),
|
|
((struct fwohcidb_tr *)(first->start))->bus_addr | dbch->ndesc);
|
|
if (firewire_debug) {
|
|
printf("fwohci_itxbuf_enable: kick 0x%08x\n", stat);
|
|
#if 1
|
|
dump_dma(sc, ITX_CH + dmach);
|
|
#endif
|
|
}
|
|
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
|
|
#if 1
|
|
/* Clear cycle match counter bits */
|
|
OWRITE(sc, OHCI_ITCTLCLR(dmach), 0xffff0000);
|
|
|
|
/* 2bit second + 13bit cycle */
|
|
cycle_now = (fc->cyctimer(fc) >> 12) & 0x7fff;
|
|
cycle_match = fwohci_next_cycle(fc, cycle_now);
|
|
|
|
OWRITE(sc, OHCI_ITCTL(dmach),
|
|
OHCI_CNTL_CYCMATCH_S | (cycle_match << 16)
|
|
| OHCI_CNTL_DMA_RUN);
|
|
#else
|
|
OWRITE(sc, OHCI_ITCTL(dmach), OHCI_CNTL_DMA_RUN);
|
|
#endif
|
|
if (firewire_debug) {
|
|
printf("cycle_match: 0x%04x->0x%04x\n",
|
|
cycle_now, cycle_match);
|
|
dump_dma(sc, ITX_CH + dmach);
|
|
dump_db(sc, ITX_CH + dmach);
|
|
}
|
|
} else if ((stat & OHCI_CNTL_CYCMATCH_S) == 0) {
|
|
device_printf(sc->fc.dev,
|
|
"IT DMA underrun (0x%08x)\n", stat);
|
|
OWRITE(sc, OHCI_ITCTL(dmach), OHCI_CNTL_DMA_WAKE);
|
|
}
|
|
out:
|
|
return err;
|
|
}
|
|
|
|
static int
|
|
fwohci_irx_enable(struct firewire_comm *fc, int dmach)
|
|
{
|
|
struct fwohci_softc *sc = (struct fwohci_softc *)fc;
|
|
int err = 0, s, ldesc;
|
|
unsigned short tag, ich;
|
|
u_int32_t stat;
|
|
struct fwohci_dbch *dbch;
|
|
struct fwohcidb_tr *db_tr;
|
|
struct fw_bulkxfer *first, *prev, *chunk;
|
|
struct fw_xferq *ir;
|
|
|
|
dbch = &sc->ir[dmach];
|
|
ir = &dbch->xferq;
|
|
|
|
if ((ir->flag & FWXFERQ_RUNNING) == 0) {
|
|
tag = (ir->flag >> 6) & 3;
|
|
ich = ir->flag & 0x3f;
|
|
OWRITE(sc, OHCI_IRMATCH(dmach), tagbit[tag] | ich);
|
|
|
|
ir->queued = 0;
|
|
dbch->ndb = ir->bnpacket * ir->bnchunk;
|
|
dbch->ndesc = 2;
|
|
fwohci_db_init(sc, dbch);
|
|
if ((dbch->flags & FWOHCI_DBCH_INIT) == 0)
|
|
return ENOMEM;
|
|
err = fwohci_rx_enable(sc, dbch);
|
|
}
|
|
if(err)
|
|
return err;
|
|
|
|
first = STAILQ_FIRST(&ir->stfree);
|
|
if (first == NULL) {
|
|
device_printf(fc->dev, "IR DMA no free chunk\n");
|
|
return 0;
|
|
}
|
|
|
|
ldesc = dbch->ndesc - 1;
|
|
s = splfw();
|
|
prev = STAILQ_LAST(&ir->stdma, fw_bulkxfer, link);
|
|
while ((chunk = STAILQ_FIRST(&ir->stfree)) != NULL) {
|
|
volatile struct fwohcidb *db;
|
|
|
|
#if 1 /* XXX for if_fwe */
|
|
if (chunk->mbuf != NULL) {
|
|
db_tr = (struct fwohcidb_tr *)(chunk->start);
|
|
db_tr->dbcnt = 1;
|
|
err = bus_dmamap_load_mbuf(dbch->dmat, db_tr->dma_map,
|
|
chunk->mbuf, fwohci_execute_db2, db_tr,
|
|
/* flags */0);
|
|
FWOHCI_DMA_SET(db_tr->db[1].db.desc.cmd,
|
|
OHCI_UPDATE | OHCI_INPUT_LAST |
|
|
OHCI_INTERRUPT_ALWAYS | OHCI_BRANCH_ALWAYS);
|
|
}
|
|
#endif
|
|
db = ((struct fwohcidb_tr *)(chunk->end))->db;
|
|
FWOHCI_DMA_WRITE(db[ldesc].db.desc.res, 0);
|
|
FWOHCI_DMA_CLEAR(db[ldesc].db.desc.depend, 0xf);
|
|
if (prev != NULL) {
|
|
db = ((struct fwohcidb_tr *)(prev->end))->db;
|
|
FWOHCI_DMA_SET(db[ldesc].db.desc.depend, dbch->ndesc);
|
|
}
|
|
STAILQ_REMOVE_HEAD(&ir->stfree, link);
|
|
STAILQ_INSERT_TAIL(&ir->stdma, chunk, link);
|
|
prev = chunk;
|
|
}
|
|
fwdma_sync_multiseg_all(dbch->am, BUS_DMASYNC_PREWRITE);
|
|
fwdma_sync_multiseg_all(dbch->am, BUS_DMASYNC_PREREAD);
|
|
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);
|
|
}
|
|
|
|
if (firewire_debug)
|
|
printf("start IR DMA 0x%x\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),
|
|
((struct fwohcidb_tr *)(first->start))->bus_addr
|
|
| dbch->ndesc);
|
|
OWRITE(sc, OHCI_IRCTL(dmach), OHCI_CNTL_DMA_RUN);
|
|
OWRITE(sc, FWOHCI_INTMASK, OHCI_INT_DMA_IR);
|
|
#if 0
|
|
dump_db(sc, IRX_CH + dmach);
|
|
#endif
|
|
return err;
|
|
}
|
|
|
|
int
|
|
fwohci_stop(struct fwohci_softc *sc, device_t dev)
|
|
{
|
|
u_int i;
|
|
|
|
/* Now stopping all DMA channel */
|
|
OWRITE(sc, OHCI_ARQCTLCLR, OHCI_CNTL_DMA_RUN);
|
|
OWRITE(sc, OHCI_ARSCTLCLR, OHCI_CNTL_DMA_RUN);
|
|
OWRITE(sc, OHCI_ATQCTLCLR, OHCI_CNTL_DMA_RUN);
|
|
OWRITE(sc, OHCI_ATSCTLCLR, OHCI_CNTL_DMA_RUN);
|
|
|
|
for( i = 0 ; i < sc->fc.nisodma ; i ++ ){
|
|
OWRITE(sc, OHCI_IRCTLCLR(i), OHCI_CNTL_DMA_RUN);
|
|
OWRITE(sc, OHCI_ITCTLCLR(i), OHCI_CNTL_DMA_RUN);
|
|
}
|
|
|
|
/* FLUSH FIFO and reset Transmitter/Reciever */
|
|
OWRITE(sc, OHCI_HCCCTL, OHCI_HCC_RESET);
|
|
|
|
/* Stop interrupt */
|
|
OWRITE(sc, FWOHCI_INTMASKCLR,
|
|
OHCI_INT_EN | OHCI_INT_ERR | OHCI_INT_PHY_SID
|
|
| OHCI_INT_PHY_INT
|
|
| OHCI_INT_DMA_ATRQ | OHCI_INT_DMA_ATRS
|
|
| OHCI_INT_DMA_PRRQ | OHCI_INT_DMA_PRRS
|
|
| OHCI_INT_DMA_ARRQ | OHCI_INT_DMA_ARRS
|
|
| OHCI_INT_PHY_BUS_R);
|
|
/* XXX Link down? Bus reset? */
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
fwohci_resume(struct fwohci_softc *sc, device_t dev)
|
|
{
|
|
int i;
|
|
|
|
fwohci_reset(sc, dev);
|
|
/* XXX resume isochronus receive automatically. (how about TX?) */
|
|
for(i = 0; i < sc->fc.nisodma; i ++) {
|
|
if((sc->ir[i].xferq.flag & FWXFERQ_RUNNING) != 0) {
|
|
device_printf(sc->fc.dev,
|
|
"resume iso receive ch: %d\n", i);
|
|
sc->ir[i].xferq.flag &= ~FWXFERQ_RUNNING;
|
|
sc->fc.irx_enable(&sc->fc, i);
|
|
}
|
|
}
|
|
|
|
bus_generic_resume(dev);
|
|
sc->fc.ibr(&sc->fc);
|
|
return 0;
|
|
}
|
|
|
|
#define ACK_ALL
|
|
static void
|
|
fwohci_intr_body(struct fwohci_softc *sc, u_int32_t stat, int count)
|
|
{
|
|
u_int32_t irstat, itstat;
|
|
u_int i;
|
|
struct firewire_comm *fc = (struct firewire_comm *)sc;
|
|
|
|
#ifdef OHCI_DEBUG
|
|
if(stat & OREAD(sc, FWOHCI_INTMASK))
|
|
device_printf(fc->dev, "INTERRUPT < %s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s> 0x%08x, 0x%08x\n",
|
|
stat & OHCI_INT_EN ? "DMA_EN ":"",
|
|
stat & OHCI_INT_PHY_REG ? "PHY_REG ":"",
|
|
stat & OHCI_INT_CYC_LONG ? "CYC_LONG ":"",
|
|
stat & OHCI_INT_ERR ? "INT_ERR ":"",
|
|
stat & OHCI_INT_CYC_ERR ? "CYC_ERR ":"",
|
|
stat & OHCI_INT_CYC_LOST ? "CYC_LOST ":"",
|
|
stat & OHCI_INT_CYC_64SECOND ? "CYC_64SECOND ":"",
|
|
stat & OHCI_INT_CYC_START ? "CYC_START ":"",
|
|
stat & OHCI_INT_PHY_INT ? "PHY_INT ":"",
|
|
stat & OHCI_INT_PHY_BUS_R ? "BUS_RESET ":"",
|
|
stat & OHCI_INT_PHY_SID ? "SID ":"",
|
|
stat & OHCI_INT_LR_ERR ? "DMA_LR_ERR ":"",
|
|
stat & OHCI_INT_PW_ERR ? "DMA_PW_ERR ":"",
|
|
stat & OHCI_INT_DMA_IR ? "DMA_IR ":"",
|
|
stat & OHCI_INT_DMA_IT ? "DMA_IT " :"",
|
|
stat & OHCI_INT_DMA_PRRS ? "DMA_PRRS " :"",
|
|
stat & OHCI_INT_DMA_PRRQ ? "DMA_PRRQ " :"",
|
|
stat & OHCI_INT_DMA_ARRS ? "DMA_ARRS " :"",
|
|
stat & OHCI_INT_DMA_ARRQ ? "DMA_ARRQ " :"",
|
|
stat & OHCI_INT_DMA_ATRS ? "DMA_ATRS " :"",
|
|
stat & OHCI_INT_DMA_ATRQ ? "DMA_ATRQ " :"",
|
|
stat, OREAD(sc, FWOHCI_INTMASK)
|
|
);
|
|
#endif
|
|
/* Bus reset */
|
|
if(stat & OHCI_INT_PHY_BUS_R ){
|
|
if (fc->status == FWBUSRESET)
|
|
goto busresetout;
|
|
/* Disable bus reset interrupt until sid recv. */
|
|
OWRITE(sc, FWOHCI_INTMASKCLR, OHCI_INT_PHY_BUS_R);
|
|
|
|
device_printf(fc->dev, "BUS reset\n");
|
|
OWRITE(sc, FWOHCI_INTMASKCLR, OHCI_INT_CYC_LOST);
|
|
OWRITE(sc, OHCI_LNKCTLCLR, OHCI_CNTL_CYCSRC);
|
|
|
|
OWRITE(sc, OHCI_ATQCTLCLR, OHCI_CNTL_DMA_RUN);
|
|
sc->atrq.xferq.flag &= ~FWXFERQ_RUNNING;
|
|
OWRITE(sc, OHCI_ATSCTLCLR, OHCI_CNTL_DMA_RUN);
|
|
sc->atrs.xferq.flag &= ~FWXFERQ_RUNNING;
|
|
|
|
#ifndef ACK_ALL
|
|
OWRITE(sc, FWOHCI_INTSTATCLR, OHCI_INT_PHY_BUS_R);
|
|
#endif
|
|
fw_busreset(fc);
|
|
}
|
|
busresetout:
|
|
if((stat & OHCI_INT_DMA_IR )){
|
|
#ifndef ACK_ALL
|
|
OWRITE(sc, FWOHCI_INTSTATCLR, OHCI_INT_DMA_IR);
|
|
#endif
|
|
#if __FreeBSD_version >= 500000
|
|
irstat = atomic_readandclear_int(&sc->irstat);
|
|
#else
|
|
irstat = sc->irstat;
|
|
sc->irstat = 0;
|
|
#endif
|
|
for(i = 0; i < fc->nisodma ; i++){
|
|
struct fwohci_dbch *dbch;
|
|
|
|
if((irstat & (1 << i)) != 0){
|
|
dbch = &sc->ir[i];
|
|
if ((dbch->xferq.flag & FWXFERQ_OPEN) == 0) {
|
|
device_printf(sc->fc.dev,
|
|
"dma(%d) not active\n", i);
|
|
continue;
|
|
}
|
|
fwohci_rbuf_update(sc, i);
|
|
}
|
|
}
|
|
}
|
|
if((stat & OHCI_INT_DMA_IT )){
|
|
#ifndef ACK_ALL
|
|
OWRITE(sc, FWOHCI_INTSTATCLR, OHCI_INT_DMA_IT);
|
|
#endif
|
|
#if __FreeBSD_version >= 500000
|
|
itstat = atomic_readandclear_int(&sc->itstat);
|
|
#else
|
|
itstat = sc->itstat;
|
|
sc->itstat = 0;
|
|
#endif
|
|
for(i = 0; i < fc->nisodma ; i++){
|
|
if((itstat & (1 << i)) != 0){
|
|
fwohci_tbuf_update(sc, i);
|
|
}
|
|
}
|
|
}
|
|
if((stat & OHCI_INT_DMA_PRRS )){
|
|
#ifndef ACK_ALL
|
|
OWRITE(sc, FWOHCI_INTSTATCLR, OHCI_INT_DMA_PRRS);
|
|
#endif
|
|
#if 0
|
|
dump_dma(sc, ARRS_CH);
|
|
dump_db(sc, ARRS_CH);
|
|
#endif
|
|
fwohci_arcv(sc, &sc->arrs, count);
|
|
}
|
|
if((stat & OHCI_INT_DMA_PRRQ )){
|
|
#ifndef ACK_ALL
|
|
OWRITE(sc, FWOHCI_INTSTATCLR, OHCI_INT_DMA_PRRQ);
|
|
#endif
|
|
#if 0
|
|
dump_dma(sc, ARRQ_CH);
|
|
dump_db(sc, ARRQ_CH);
|
|
#endif
|
|
fwohci_arcv(sc, &sc->arrq, count);
|
|
}
|
|
if(stat & OHCI_INT_PHY_SID){
|
|
u_int32_t *buf, node_id;
|
|
int plen;
|
|
|
|
#ifndef ACK_ALL
|
|
OWRITE(sc, FWOHCI_INTSTATCLR, OHCI_INT_PHY_SID);
|
|
#endif
|
|
/* Enable bus reset interrupt */
|
|
OWRITE(sc, FWOHCI_INTMASK, OHCI_INT_PHY_BUS_R);
|
|
/* Allow async. request to us */
|
|
OWRITE(sc, OHCI_AREQHI, 1 << 31);
|
|
/* XXX insecure ?? */
|
|
OWRITE(sc, OHCI_PREQHI, 0x7fffffff);
|
|
OWRITE(sc, OHCI_PREQLO, 0xffffffff);
|
|
OWRITE(sc, OHCI_PREQUPPER, 0x10000);
|
|
/* Set ATRetries register */
|
|
OWRITE(sc, OHCI_ATRETRY, 1<<(13+16) | 0xfff);
|
|
/*
|
|
** Checking whether the node is root or not. If root, turn on
|
|
** cycle master.
|
|
*/
|
|
node_id = OREAD(sc, FWOHCI_NODEID);
|
|
plen = OREAD(sc, OHCI_SID_CNT);
|
|
|
|
device_printf(fc->dev, "node_id=0x%08x, gen=%d, ",
|
|
node_id, (plen >> 16) & 0xff);
|
|
if (!(node_id & OHCI_NODE_VALID)) {
|
|
printf("Bus reset failure\n");
|
|
goto sidout;
|
|
}
|
|
if (node_id & OHCI_NODE_ROOT) {
|
|
printf("CYCLEMASTER mode\n");
|
|
OWRITE(sc, OHCI_LNKCTL,
|
|
OHCI_CNTL_CYCMTR | OHCI_CNTL_CYCTIMER);
|
|
} else {
|
|
printf("non CYCLEMASTER mode\n");
|
|
OWRITE(sc, OHCI_LNKCTLCLR, OHCI_CNTL_CYCMTR);
|
|
OWRITE(sc, OHCI_LNKCTL, OHCI_CNTL_CYCTIMER);
|
|
}
|
|
fc->nodeid = node_id & 0x3f;
|
|
|
|
if (plen & OHCI_SID_ERR) {
|
|
device_printf(fc->dev, "SID Error\n");
|
|
goto sidout;
|
|
}
|
|
plen &= OHCI_SID_CNT_MASK;
|
|
if (plen < 4 || plen > OHCI_SIDSIZE) {
|
|
device_printf(fc->dev, "invalid SID len = %d\n", plen);
|
|
goto sidout;
|
|
}
|
|
plen -= 4; /* chop control info */
|
|
buf = (u_int32_t *)malloc(OHCI_SIDSIZE, M_FW, M_NOWAIT);
|
|
if (buf == NULL) {
|
|
device_printf(fc->dev, "malloc failed\n");
|
|
goto sidout;
|
|
}
|
|
for (i = 0; i < plen / 4; i ++)
|
|
buf[i] = FWOHCI_DMA_READ(sc->sid_buf[i+1]);
|
|
#if 1
|
|
/* pending all pre-bus_reset packets */
|
|
fwohci_txd(sc, &sc->atrq);
|
|
fwohci_txd(sc, &sc->atrs);
|
|
fwohci_arcv(sc, &sc->arrs, -1);
|
|
fwohci_arcv(sc, &sc->arrq, -1);
|
|
fw_drain_txq(fc);
|
|
#endif
|
|
fw_sidrcv(fc, buf, plen);
|
|
free(buf, M_FW);
|
|
}
|
|
sidout:
|
|
if((stat & OHCI_INT_DMA_ATRQ )){
|
|
#ifndef ACK_ALL
|
|
OWRITE(sc, FWOHCI_INTSTATCLR, OHCI_INT_DMA_ATRQ);
|
|
#endif
|
|
fwohci_txd(sc, &(sc->atrq));
|
|
}
|
|
if((stat & OHCI_INT_DMA_ATRS )){
|
|
#ifndef ACK_ALL
|
|
OWRITE(sc, FWOHCI_INTSTATCLR, OHCI_INT_DMA_ATRS);
|
|
#endif
|
|
fwohci_txd(sc, &(sc->atrs));
|
|
}
|
|
if((stat & OHCI_INT_PW_ERR )){
|
|
#ifndef ACK_ALL
|
|
OWRITE(sc, FWOHCI_INTSTATCLR, OHCI_INT_PW_ERR);
|
|
#endif
|
|
device_printf(fc->dev, "posted write error\n");
|
|
}
|
|
if((stat & OHCI_INT_ERR )){
|
|
#ifndef ACK_ALL
|
|
OWRITE(sc, FWOHCI_INTSTATCLR, OHCI_INT_ERR);
|
|
#endif
|
|
device_printf(fc->dev, "unrecoverable error\n");
|
|
}
|
|
if((stat & OHCI_INT_PHY_INT)) {
|
|
#ifndef ACK_ALL
|
|
OWRITE(sc, FWOHCI_INTSTATCLR, OHCI_INT_PHY_INT);
|
|
#endif
|
|
device_printf(fc->dev, "phy int\n");
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
#if FWOHCI_TASKQUEUE
|
|
static void
|
|
fwohci_complete(void *arg, int pending)
|
|
{
|
|
struct fwohci_softc *sc = (struct fwohci_softc *)arg;
|
|
u_int32_t stat;
|
|
|
|
again:
|
|
stat = atomic_readandclear_int(&sc->intstat);
|
|
if (stat)
|
|
fwohci_intr_body(sc, stat, -1);
|
|
else
|
|
return;
|
|
goto again;
|
|
}
|
|
#endif
|
|
|
|
static u_int32_t
|
|
fwochi_check_stat(struct fwohci_softc *sc)
|
|
{
|
|
u_int32_t stat, irstat, itstat;
|
|
|
|
stat = OREAD(sc, FWOHCI_INTSTAT);
|
|
if (stat == 0xffffffff) {
|
|
device_printf(sc->fc.dev,
|
|
"device physically ejected?\n");
|
|
return(stat);
|
|
}
|
|
#ifdef ACK_ALL
|
|
if (stat)
|
|
OWRITE(sc, FWOHCI_INTSTATCLR, stat);
|
|
#endif
|
|
if (stat & OHCI_INT_DMA_IR) {
|
|
irstat = OREAD(sc, OHCI_IR_STAT);
|
|
OWRITE(sc, OHCI_IR_STATCLR, irstat);
|
|
atomic_set_int(&sc->irstat, irstat);
|
|
}
|
|
if (stat & OHCI_INT_DMA_IT) {
|
|
itstat = OREAD(sc, OHCI_IT_STAT);
|
|
OWRITE(sc, OHCI_IT_STATCLR, itstat);
|
|
atomic_set_int(&sc->itstat, itstat);
|
|
}
|
|
return(stat);
|
|
}
|
|
|
|
void
|
|
fwohci_intr(void *arg)
|
|
{
|
|
struct fwohci_softc *sc = (struct fwohci_softc *)arg;
|
|
u_int32_t stat;
|
|
#if !FWOHCI_TASKQUEUE
|
|
u_int32_t bus_reset = 0;
|
|
#endif
|
|
|
|
if (!(sc->intmask & OHCI_INT_EN)) {
|
|
/* polling mode */
|
|
return;
|
|
}
|
|
|
|
#if !FWOHCI_TASKQUEUE
|
|
again:
|
|
#endif
|
|
stat = fwochi_check_stat(sc);
|
|
if (stat == 0 || stat == 0xffffffff)
|
|
return;
|
|
#if FWOHCI_TASKQUEUE
|
|
atomic_set_int(&sc->intstat, stat);
|
|
/* XXX mask bus reset intr. during bus reset phase */
|
|
if (stat)
|
|
taskqueue_enqueue(taskqueue_swi_giant, &sc->fwohci_task_complete);
|
|
#else
|
|
/* We cannot clear bus reset event during bus reset phase */
|
|
if ((stat & ~bus_reset) == 0)
|
|
return;
|
|
bus_reset = stat & OHCI_INT_PHY_BUS_R;
|
|
fwohci_intr_body(sc, stat, -1);
|
|
goto again;
|
|
#endif
|
|
}
|
|
|
|
static void
|
|
fwohci_poll(struct firewire_comm *fc, int quick, int count)
|
|
{
|
|
int s;
|
|
u_int32_t stat;
|
|
struct fwohci_softc *sc;
|
|
|
|
|
|
sc = (struct fwohci_softc *)fc;
|
|
stat = OHCI_INT_DMA_IR | OHCI_INT_DMA_IT |
|
|
OHCI_INT_DMA_PRRS | OHCI_INT_DMA_PRRQ |
|
|
OHCI_INT_DMA_ATRQ | OHCI_INT_DMA_ATRS;
|
|
#if 0
|
|
if (!quick) {
|
|
#else
|
|
if (1) {
|
|
#endif
|
|
stat = fwochi_check_stat(sc);
|
|
if (stat == 0 || stat == 0xffffffff)
|
|
return;
|
|
}
|
|
s = splfw();
|
|
fwohci_intr_body(sc, stat, count);
|
|
splx(s);
|
|
}
|
|
|
|
static void
|
|
fwohci_set_intr(struct firewire_comm *fc, int enable)
|
|
{
|
|
struct fwohci_softc *sc;
|
|
|
|
sc = (struct fwohci_softc *)fc;
|
|
if (bootverbose)
|
|
device_printf(sc->fc.dev, "fwohci_set_intr: %d\n", enable);
|
|
if (enable) {
|
|
sc->intmask |= OHCI_INT_EN;
|
|
OWRITE(sc, FWOHCI_INTMASK, OHCI_INT_EN);
|
|
} else {
|
|
sc->intmask &= ~OHCI_INT_EN;
|
|
OWRITE(sc, FWOHCI_INTMASKCLR, OHCI_INT_EN);
|
|
}
|
|
}
|
|
|
|
static void
|
|
fwohci_tbuf_update(struct fwohci_softc *sc, int dmach)
|
|
{
|
|
struct firewire_comm *fc = &sc->fc;
|
|
volatile struct fwohcidb *db;
|
|
struct fw_bulkxfer *chunk;
|
|
struct fw_xferq *it;
|
|
u_int32_t stat, count;
|
|
int s, w=0, ldesc;
|
|
|
|
it = fc->it[dmach];
|
|
ldesc = sc->it[dmach].ndesc - 1;
|
|
s = splfw(); /* unnecessary ? */
|
|
fwdma_sync_multiseg_all(sc->it[dmach].am, BUS_DMASYNC_POSTREAD);
|
|
while ((chunk = STAILQ_FIRST(&it->stdma)) != NULL) {
|
|
db = ((struct fwohcidb_tr *)(chunk->end))->db;
|
|
stat = FWOHCI_DMA_READ(db[ldesc].db.desc.res)
|
|
>> OHCI_STATUS_SHIFT;
|
|
db = ((struct fwohcidb_tr *)(chunk->start))->db;
|
|
count = FWOHCI_DMA_READ(db[ldesc].db.desc.res)
|
|
& OHCI_COUNT_MASK;
|
|
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
|
|
break;
|
|
default:
|
|
device_printf(fc->dev,
|
|
"Isochronous transmit err %02x(%s)\n",
|
|
stat, fwohcicode[stat & 0x1f]);
|
|
}
|
|
STAILQ_INSERT_TAIL(&it->stfree, chunk, link);
|
|
w++;
|
|
}
|
|
splx(s);
|
|
if (w)
|
|
wakeup(it);
|
|
}
|
|
|
|
static void
|
|
fwohci_rbuf_update(struct fwohci_softc *sc, int dmach)
|
|
{
|
|
struct firewire_comm *fc = &sc->fc;
|
|
volatile struct fwohcidb_tr *db_tr;
|
|
struct fw_bulkxfer *chunk;
|
|
struct fw_xferq *ir;
|
|
u_int32_t stat;
|
|
int s, w=0, ldesc;
|
|
|
|
ir = fc->ir[dmach];
|
|
ldesc = sc->ir[dmach].ndesc - 1;
|
|
#if 0
|
|
dump_db(sc, dmach);
|
|
#endif
|
|
s = splfw();
|
|
fwdma_sync_multiseg_all(sc->ir[dmach].am, BUS_DMASYNC_POSTREAD);
|
|
while ((chunk = STAILQ_FIRST(&ir->stdma)) != NULL) {
|
|
db_tr = (struct fwohcidb_tr *)chunk->end;
|
|
stat = FWOHCI_DMA_READ(db_tr->db[ldesc].db.desc.res)
|
|
>> OHCI_STATUS_SHIFT;
|
|
if (stat == 0)
|
|
break;
|
|
|
|
if (chunk->mbuf != NULL) {
|
|
bus_dmamap_sync(sc->ir[dmach].dmat, db_tr->dma_map,
|
|
BUS_DMASYNC_POSTREAD);
|
|
bus_dmamap_unload(sc->ir[dmach].dmat, db_tr->dma_map);
|
|
} else if (ir->buf != NULL) {
|
|
fwdma_sync_multiseg(ir->buf, chunk->poffset,
|
|
ir->bnpacket, BUS_DMASYNC_POSTREAD);
|
|
} else {
|
|
/* XXX */
|
|
printf("fwohci_rbuf_update: this shouldn't happend\n");
|
|
}
|
|
|
|
STAILQ_REMOVE_HEAD(&ir->stdma, link);
|
|
STAILQ_INSERT_TAIL(&ir->stvalid, chunk, link);
|
|
switch (stat & FWOHCIEV_MASK) {
|
|
case FWOHCIEV_ACKCOMPL:
|
|
chunk->resp = 0;
|
|
break;
|
|
default:
|
|
chunk->resp = EINVAL;
|
|
device_printf(fc->dev,
|
|
"Isochronous receive err %02x(%s)\n",
|
|
stat, fwohcicode[stat & 0x1f]);
|
|
}
|
|
w++;
|
|
}
|
|
splx(s);
|
|
if (w) {
|
|
if (ir->flag & FWXFERQ_HANDLER)
|
|
ir->hand(ir);
|
|
else
|
|
wakeup(ir);
|
|
}
|
|
}
|
|
|
|
void
|
|
dump_dma(struct fwohci_softc *sc, u_int32_t ch)
|
|
{
|
|
u_int32_t off, cntl, stat, cmd, match;
|
|
|
|
if(ch == 0){
|
|
off = OHCI_ATQOFF;
|
|
}else if(ch == 1){
|
|
off = OHCI_ATSOFF;
|
|
}else if(ch == 2){
|
|
off = OHCI_ARQOFF;
|
|
}else if(ch == 3){
|
|
off = OHCI_ARSOFF;
|
|
}else if(ch < IRX_CH){
|
|
off = OHCI_ITCTL(ch - ITX_CH);
|
|
}else{
|
|
off = OHCI_IRCTL(ch - IRX_CH);
|
|
}
|
|
cntl = stat = OREAD(sc, off);
|
|
cmd = OREAD(sc, off + 0xc);
|
|
match = OREAD(sc, off + 0x10);
|
|
|
|
device_printf(sc->fc.dev, "ch %1x cntl:0x%08x cmd:0x%08x match:0x%08x\n",
|
|
ch,
|
|
cntl,
|
|
cmd,
|
|
match);
|
|
stat &= 0xffff ;
|
|
if (stat) {
|
|
device_printf(sc->fc.dev, "dma %d ch:%s%s%s%s%s%s %s(%x)\n",
|
|
ch,
|
|
stat & OHCI_CNTL_DMA_RUN ? "RUN," : "",
|
|
stat & OHCI_CNTL_DMA_WAKE ? "WAKE," : "",
|
|
stat & OHCI_CNTL_DMA_DEAD ? "DEAD," : "",
|
|
stat & OHCI_CNTL_DMA_ACTIVE ? "ACTIVE," : "",
|
|
stat & OHCI_CNTL_DMA_BT ? "BRANCH," : "",
|
|
stat & OHCI_CNTL_DMA_BAD ? "BADDMA," : "",
|
|
fwohcicode[stat & 0x1f],
|
|
stat & 0x1f
|
|
);
|
|
}else{
|
|
device_printf(sc->fc.dev, "dma %d ch: Nostat\n", ch);
|
|
}
|
|
}
|
|
|
|
void
|
|
dump_db(struct fwohci_softc *sc, u_int32_t ch)
|
|
{
|
|
struct fwohci_dbch *dbch;
|
|
struct fwohcidb_tr *cp = NULL, *pp, *np = NULL;
|
|
volatile struct fwohcidb *curr = NULL, *prev, *next = NULL;
|
|
int idb, jdb;
|
|
u_int32_t cmd, off;
|
|
if(ch == 0){
|
|
off = OHCI_ATQOFF;
|
|
dbch = &sc->atrq;
|
|
}else if(ch == 1){
|
|
off = OHCI_ATSOFF;
|
|
dbch = &sc->atrs;
|
|
}else if(ch == 2){
|
|
off = OHCI_ARQOFF;
|
|
dbch = &sc->arrq;
|
|
}else if(ch == 3){
|
|
off = OHCI_ARSOFF;
|
|
dbch = &sc->arrs;
|
|
}else if(ch < IRX_CH){
|
|
off = OHCI_ITCTL(ch - ITX_CH);
|
|
dbch = &sc->it[ch - ITX_CH];
|
|
}else {
|
|
off = OHCI_IRCTL(ch - IRX_CH);
|
|
dbch = &sc->ir[ch - IRX_CH];
|
|
}
|
|
cmd = OREAD(sc, off + 0xc);
|
|
|
|
if( dbch->ndb == 0 ){
|
|
device_printf(sc->fc.dev, "No DB is attached ch=%d\n", ch);
|
|
return;
|
|
}
|
|
pp = dbch->top;
|
|
prev = pp->db;
|
|
for(idb = 0 ; idb < dbch->ndb ; idb ++ ){
|
|
if(pp == NULL){
|
|
curr = NULL;
|
|
goto outdb;
|
|
}
|
|
cp = STAILQ_NEXT(pp, link);
|
|
if(cp == NULL){
|
|
curr = NULL;
|
|
goto outdb;
|
|
}
|
|
np = STAILQ_NEXT(cp, link);
|
|
for(jdb = 0 ; jdb < dbch->ndesc ; jdb ++ ){
|
|
if ((cmd & 0xfffffff0) == cp->bus_addr) {
|
|
curr = cp->db;
|
|
if(np != NULL){
|
|
next = np->db;
|
|
}else{
|
|
next = NULL;
|
|
}
|
|
goto outdb;
|
|
}
|
|
}
|
|
pp = STAILQ_NEXT(pp, link);
|
|
prev = pp->db;
|
|
}
|
|
outdb:
|
|
if( curr != NULL){
|
|
#if 0
|
|
printf("Prev DB %d\n", ch);
|
|
print_db(pp, prev, ch, dbch->ndesc);
|
|
#endif
|
|
printf("Current DB %d\n", ch);
|
|
print_db(cp, curr, ch, dbch->ndesc);
|
|
#if 0
|
|
printf("Next DB %d\n", ch);
|
|
print_db(np, next, ch, dbch->ndesc);
|
|
#endif
|
|
}else{
|
|
printf("dbdump err ch = %d cmd = 0x%08x\n", ch, cmd);
|
|
}
|
|
return;
|
|
}
|
|
|
|
void
|
|
print_db(struct fwohcidb_tr *db_tr, volatile struct fwohcidb *db,
|
|
u_int32_t ch, u_int32_t max)
|
|
{
|
|
fwohcireg_t stat;
|
|
int i, key;
|
|
u_int32_t cmd, res;
|
|
|
|
if(db == NULL){
|
|
printf("No Descriptor is found\n");
|
|
return;
|
|
}
|
|
|
|
printf("ch = %d\n%8s %s %s %s %s %4s %8s %8s %4s:%4s\n",
|
|
ch,
|
|
"Current",
|
|
"OP ",
|
|
"KEY",
|
|
"INT",
|
|
"BR ",
|
|
"len",
|
|
"Addr",
|
|
"Depend",
|
|
"Stat",
|
|
"Cnt");
|
|
for( i = 0 ; i <= max ; i ++){
|
|
cmd = FWOHCI_DMA_READ(db[i].db.desc.cmd);
|
|
res = FWOHCI_DMA_READ(db[i].db.desc.res);
|
|
key = cmd & OHCI_KEY_MASK;
|
|
stat = res >> OHCI_STATUS_SHIFT;
|
|
#if __FreeBSD_version >= 500000
|
|
printf("%08tx %s %s %s %s %5d %08x %08x %04x:%04x",
|
|
#else
|
|
printf("%08x %s %s %s %s %5d %08x %08x %04x:%04x",
|
|
#endif
|
|
db_tr->bus_addr,
|
|
dbcode[(cmd >> 28) & 0xf],
|
|
dbkey[(cmd >> 24) & 0x7],
|
|
dbcond[(cmd >> 20) & 0x3],
|
|
dbcond[(cmd >> 18) & 0x3],
|
|
cmd & OHCI_COUNT_MASK,
|
|
FWOHCI_DMA_READ(db[i].db.desc.addr),
|
|
FWOHCI_DMA_READ(db[i].db.desc.depend),
|
|
stat,
|
|
res & OHCI_COUNT_MASK);
|
|
if(stat & 0xff00){
|
|
printf(" %s%s%s%s%s%s %s(%x)\n",
|
|
stat & OHCI_CNTL_DMA_RUN ? "RUN," : "",
|
|
stat & OHCI_CNTL_DMA_WAKE ? "WAKE," : "",
|
|
stat & OHCI_CNTL_DMA_DEAD ? "DEAD," : "",
|
|
stat & OHCI_CNTL_DMA_ACTIVE ? "ACTIVE," : "",
|
|
stat & OHCI_CNTL_DMA_BT ? "BRANCH," : "",
|
|
stat & OHCI_CNTL_DMA_BAD ? "BADDMA," : "",
|
|
fwohcicode[stat & 0x1f],
|
|
stat & 0x1f
|
|
);
|
|
}else{
|
|
printf(" Nostat\n");
|
|
}
|
|
if(key == OHCI_KEY_ST2 ){
|
|
printf("0x%08x 0x%08x 0x%08x 0x%08x\n",
|
|
FWOHCI_DMA_READ(db[i+1].db.immed[0]),
|
|
FWOHCI_DMA_READ(db[i+1].db.immed[1]),
|
|
FWOHCI_DMA_READ(db[i+1].db.immed[2]),
|
|
FWOHCI_DMA_READ(db[i+1].db.immed[3]));
|
|
}
|
|
if(key == OHCI_KEY_DEVICE){
|
|
return;
|
|
}
|
|
if((cmd & OHCI_BRANCH_MASK)
|
|
== OHCI_BRANCH_ALWAYS){
|
|
return;
|
|
}
|
|
if((cmd & OHCI_CMD_MASK)
|
|
== OHCI_OUTPUT_LAST){
|
|
return;
|
|
}
|
|
if((cmd & OHCI_CMD_MASK)
|
|
== OHCI_INPUT_LAST){
|
|
return;
|
|
}
|
|
if(key == OHCI_KEY_ST2 ){
|
|
i++;
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
void
|
|
fwohci_ibr(struct firewire_comm *fc)
|
|
{
|
|
struct fwohci_softc *sc;
|
|
u_int32_t fun;
|
|
|
|
device_printf(fc->dev, "Initiate bus reset\n");
|
|
sc = (struct fwohci_softc *)fc;
|
|
|
|
/*
|
|
* Set root hold-off bit so that non cyclemaster capable node
|
|
* shouldn't became the root node.
|
|
*/
|
|
#if 1
|
|
fun = fwphy_rddata(sc, FW_PHY_IBR_REG);
|
|
fun |= FW_PHY_IBR | FW_PHY_RHB;
|
|
fun = fwphy_wrdata(sc, FW_PHY_IBR_REG, fun);
|
|
#else /* Short bus reset */
|
|
fun = fwphy_rddata(sc, FW_PHY_ISBR_REG);
|
|
fun |= FW_PHY_ISBR | FW_PHY_RHB;
|
|
fun = fwphy_wrdata(sc, FW_PHY_ISBR_REG, fun);
|
|
#endif
|
|
}
|
|
|
|
void
|
|
fwohci_txbufdb(struct fwohci_softc *sc, int dmach, struct fw_bulkxfer *bulkxfer)
|
|
{
|
|
struct fwohcidb_tr *db_tr, *fdb_tr;
|
|
struct fwohci_dbch *dbch;
|
|
volatile struct fwohcidb *db;
|
|
struct fw_pkt *fp;
|
|
volatile struct fwohci_txpkthdr *ohcifp;
|
|
unsigned short chtag;
|
|
int idb;
|
|
|
|
dbch = &sc->it[dmach];
|
|
chtag = sc->it[dmach].xferq.flag & 0xff;
|
|
|
|
db_tr = (struct fwohcidb_tr *)(bulkxfer->start);
|
|
fdb_tr = (struct fwohcidb_tr *)(bulkxfer->end);
|
|
/*
|
|
device_printf(sc->fc.dev, "DB %08x %08x %08x\n", bulkxfer, db_tr->bus_addr, fdb_tr->bus_addr);
|
|
*/
|
|
for (idb = 0; idb < dbch->xferq.bnpacket; idb ++) {
|
|
db = db_tr->db;
|
|
fp = (struct fw_pkt *)db_tr->buf;
|
|
ohcifp = (volatile struct fwohci_txpkthdr *) db[1].db.immed;
|
|
ohcifp->mode.ld[0] = fp->mode.ld[0];
|
|
ohcifp->mode.stream.len = fp->mode.stream.len;
|
|
ohcifp->mode.stream.chtag = chtag;
|
|
ohcifp->mode.stream.tcode = 0xa;
|
|
ohcifp->mode.stream.spd = 0;
|
|
#if BYTE_ORDER == BIG_ENDIAN
|
|
FWOHCI_DMA_WRITE(db[1].db.immed[0], db[1].db.immed[0]);
|
|
FWOHCI_DMA_WRITE(db[1].db.immed[1], db[1].db.immed[1]);
|
|
#endif
|
|
|
|
FWOHCI_DMA_CLEAR(db[2].db.desc.cmd, OHCI_COUNT_MASK);
|
|
FWOHCI_DMA_SET(db[2].db.desc.cmd, fp->mode.stream.len);
|
|
FWOHCI_DMA_WRITE(db[2].db.desc.res, 0);
|
|
#if 0 /* if bulkxfer->npackets changes */
|
|
db[2].db.desc.cmd = OHCI_OUTPUT_LAST
|
|
| OHCI_UPDATE
|
|
| OHCI_BRANCH_ALWAYS;
|
|
db[0].db.desc.depend =
|
|
= db[dbch->ndesc - 1].db.desc.depend
|
|
= STAILQ_NEXT(db_tr, link)->bus_addr | dbch->ndesc;
|
|
#else
|
|
FWOHCI_DMA_SET(db[0].db.desc.depend, dbch->ndesc);
|
|
FWOHCI_DMA_SET(db[dbch->ndesc - 1].db.desc.depend, dbch->ndesc);
|
|
#endif
|
|
bulkxfer->end = (caddr_t)db_tr;
|
|
db_tr = STAILQ_NEXT(db_tr, link);
|
|
}
|
|
db = ((struct fwohcidb_tr *)bulkxfer->end)->db;
|
|
FWOHCI_DMA_CLEAR(db[0].db.desc.depend, 0xf);
|
|
FWOHCI_DMA_CLEAR(db[dbch->ndesc - 1].db.desc.depend, 0xf);
|
|
#if 0 /* if bulkxfer->npackets changes */
|
|
db[dbch->ndesc - 1].db.desc.control |= OHCI_INTERRUPT_ALWAYS;
|
|
/* OHCI 1.1 and above */
|
|
db[0].db.desc.control |= OHCI_INTERRUPT_ALWAYS;
|
|
#endif
|
|
/*
|
|
db_tr = (struct fwohcidb_tr *)bulkxfer->start;
|
|
fdb_tr = (struct fwohcidb_tr *)bulkxfer->end;
|
|
device_printf(sc->fc.dev, "DB %08x %3d %08x %08x\n", bulkxfer, bulkxfer->npacket, db_tr->bus_addr, fdb_tr->bus_addr);
|
|
*/
|
|
return;
|
|
}
|
|
|
|
static int
|
|
fwohci_add_tx_buf(struct fwohci_dbch *dbch, struct fwohcidb_tr *db_tr,
|
|
int poffset)
|
|
{
|
|
volatile struct fwohcidb *db = db_tr->db;
|
|
struct fw_xferq *it;
|
|
int err = 0;
|
|
|
|
it = &dbch->xferq;
|
|
if(it->buf == 0){
|
|
err = EINVAL;
|
|
return err;
|
|
}
|
|
db_tr->buf = fwdma_v_addr(it->buf, poffset);
|
|
db_tr->dbcnt = 3;
|
|
|
|
FWOHCI_DMA_WRITE(db[0].db.desc.cmd,
|
|
OHCI_OUTPUT_MORE | OHCI_KEY_ST2 | 8);
|
|
FWOHCI_DMA_WRITE(db[2].db.desc.addr,
|
|
fwdma_bus_addr(it->buf, poffset) + sizeof(u_int32_t));
|
|
|
|
FWOHCI_DMA_WRITE(db[2].db.desc.cmd,
|
|
OHCI_OUTPUT_LAST | OHCI_UPDATE | OHCI_BRANCH_ALWAYS);
|
|
#if 1
|
|
FWOHCI_DMA_WRITE(db[0].db.desc.res, 0);
|
|
FWOHCI_DMA_WRITE(db[2].db.desc.res, 0);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
fwohci_add_rx_buf(struct fwohci_dbch *dbch, struct fwohcidb_tr *db_tr,
|
|
int poffset, struct fwdma_alloc *dummy_dma)
|
|
{
|
|
volatile struct fwohcidb *db = db_tr->db;
|
|
struct fw_xferq *ir;
|
|
int i, ldesc;
|
|
bus_addr_t dbuf[2];
|
|
int dsiz[2];
|
|
|
|
ir = &dbch->xferq;
|
|
if (ir->buf == NULL && (dbch->xferq.flag & FWXFERQ_EXTBUF) == 0) {
|
|
db_tr->buf = fwdma_malloc_size(dbch->dmat, &db_tr->dma_map,
|
|
ir->psize, &dbuf[0], BUS_DMA_NOWAIT);
|
|
if (db_tr->buf == NULL)
|
|
return(ENOMEM);
|
|
db_tr->dbcnt = 1;
|
|
dsiz[0] = ir->psize;
|
|
bus_dmamap_sync(dbch->dmat, db_tr->dma_map,
|
|
BUS_DMASYNC_PREREAD);
|
|
} else {
|
|
db_tr->dbcnt = 0;
|
|
if (dummy_dma != NULL) {
|
|
dsiz[db_tr->dbcnt] = sizeof(u_int32_t);
|
|
dbuf[db_tr->dbcnt++] = dummy_dma->bus_addr;
|
|
}
|
|
dsiz[db_tr->dbcnt] = ir->psize;
|
|
if (ir->buf != NULL) {
|
|
db_tr->buf = fwdma_v_addr(ir->buf, poffset);
|
|
dbuf[db_tr->dbcnt] = fwdma_bus_addr( ir->buf, poffset);
|
|
}
|
|
db_tr->dbcnt++;
|
|
}
|
|
for(i = 0 ; i < db_tr->dbcnt ; i++){
|
|
FWOHCI_DMA_WRITE(db[i].db.desc.addr, dbuf[i]);
|
|
FWOHCI_DMA_WRITE(db[i].db.desc.cmd, OHCI_INPUT_MORE | dsiz[i]);
|
|
if (ir->flag & FWXFERQ_STREAM) {
|
|
FWOHCI_DMA_SET(db[i].db.desc.cmd, OHCI_UPDATE);
|
|
}
|
|
FWOHCI_DMA_WRITE(db[i].db.desc.res, dsiz[i]);
|
|
}
|
|
ldesc = db_tr->dbcnt - 1;
|
|
if (ir->flag & FWXFERQ_STREAM) {
|
|
FWOHCI_DMA_SET(db[ldesc].db.desc.cmd, OHCI_INPUT_LAST);
|
|
}
|
|
FWOHCI_DMA_SET(db[ldesc].db.desc.cmd, OHCI_BRANCH_ALWAYS);
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
fwohci_arcv_swap(struct fw_pkt *fp, int len)
|
|
{
|
|
struct fw_pkt *fp0;
|
|
u_int32_t ld0;
|
|
int slen;
|
|
#if BYTE_ORDER == BIG_ENDIAN
|
|
int i;
|
|
#endif
|
|
|
|
ld0 = FWOHCI_DMA_READ(fp->mode.ld[0]);
|
|
#if 0
|
|
printf("ld0: x%08x\n", ld0);
|
|
#endif
|
|
fp0 = (struct fw_pkt *)&ld0;
|
|
switch (fp0->mode.common.tcode) {
|
|
case FWTCODE_RREQQ:
|
|
case FWTCODE_WRES:
|
|
case FWTCODE_WREQQ:
|
|
case FWTCODE_RRESQ:
|
|
case FWOHCITCODE_PHY:
|
|
slen = 12;
|
|
break;
|
|
case FWTCODE_RREQB:
|
|
case FWTCODE_WREQB:
|
|
case FWTCODE_LREQ:
|
|
case FWTCODE_RRESB:
|
|
case FWTCODE_LRES:
|
|
slen = 16;
|
|
break;
|
|
default:
|
|
printf("Unknown tcode %d\n", fp0->mode.common.tcode);
|
|
return(0);
|
|
}
|
|
if (slen > len) {
|
|
if (firewire_debug)
|
|
printf("splitted header\n");
|
|
return(-slen);
|
|
}
|
|
#if BYTE_ORDER == BIG_ENDIAN
|
|
for(i = 0; i < slen/4; i ++)
|
|
fp->mode.ld[i] = FWOHCI_DMA_READ(fp->mode.ld[i]);
|
|
#endif
|
|
return(slen);
|
|
}
|
|
|
|
#define PLEN(x) roundup2(x, sizeof(u_int32_t))
|
|
static int
|
|
fwohci_get_plen(struct fwohci_softc *sc, struct fwohci_dbch *dbch, struct fw_pkt *fp)
|
|
{
|
|
int r;
|
|
|
|
switch(fp->mode.common.tcode){
|
|
case FWTCODE_RREQQ:
|
|
r = sizeof(fp->mode.rreqq) + sizeof(u_int32_t);
|
|
break;
|
|
case FWTCODE_WRES:
|
|
r = sizeof(fp->mode.wres) + sizeof(u_int32_t);
|
|
break;
|
|
case FWTCODE_WREQQ:
|
|
r = sizeof(fp->mode.wreqq) + sizeof(u_int32_t);
|
|
break;
|
|
case FWTCODE_RREQB:
|
|
r = sizeof(fp->mode.rreqb) + sizeof(u_int32_t);
|
|
break;
|
|
case FWTCODE_RRESQ:
|
|
r = sizeof(fp->mode.rresq) + sizeof(u_int32_t);
|
|
break;
|
|
case FWTCODE_WREQB:
|
|
r = sizeof(struct fw_asyhdr) + PLEN(fp->mode.wreqb.len)
|
|
+ sizeof(u_int32_t);
|
|
break;
|
|
case FWTCODE_LREQ:
|
|
r = sizeof(struct fw_asyhdr) + PLEN(fp->mode.lreq.len)
|
|
+ sizeof(u_int32_t);
|
|
break;
|
|
case FWTCODE_RRESB:
|
|
r = sizeof(struct fw_asyhdr) + PLEN(fp->mode.rresb.len)
|
|
+ sizeof(u_int32_t);
|
|
break;
|
|
case FWTCODE_LRES:
|
|
r = sizeof(struct fw_asyhdr) + PLEN(fp->mode.lres.len)
|
|
+ sizeof(u_int32_t);
|
|
break;
|
|
case FWOHCITCODE_PHY:
|
|
r = 16;
|
|
break;
|
|
default:
|
|
device_printf(sc->fc.dev, "Unknown tcode %d\n",
|
|
fp->mode.common.tcode);
|
|
r = 0;
|
|
}
|
|
if (r > dbch->xferq.psize) {
|
|
device_printf(sc->fc.dev, "Invalid packet length %d\n", r);
|
|
/* panic ? */
|
|
}
|
|
return r;
|
|
}
|
|
|
|
static void
|
|
fwohci_arcv_free_buf(struct fwohci_dbch *dbch, struct fwohcidb_tr *db_tr)
|
|
{
|
|
volatile struct fwohcidb *db = &db_tr->db[0];
|
|
|
|
FWOHCI_DMA_CLEAR(db->db.desc.depend, 0xf);
|
|
FWOHCI_DMA_WRITE(db->db.desc.res, dbch->xferq.psize);
|
|
FWOHCI_DMA_SET(dbch->bottom->db[0].db.desc.depend, 1);
|
|
fwdma_sync_multiseg_all(dbch->am, BUS_DMASYNC_PREWRITE);
|
|
dbch->bottom = db_tr;
|
|
}
|
|
|
|
static void
|
|
fwohci_arcv(struct fwohci_softc *sc, struct fwohci_dbch *dbch, int count)
|
|
{
|
|
struct fwohcidb_tr *db_tr;
|
|
struct iovec vec[2];
|
|
struct fw_pkt pktbuf;
|
|
int nvec;
|
|
struct fw_pkt *fp;
|
|
u_int8_t *ld;
|
|
u_int32_t stat, off, status;
|
|
u_int spd;
|
|
int len, plen, hlen, pcnt, offset;
|
|
int s;
|
|
caddr_t buf;
|
|
int resCount;
|
|
|
|
if(&sc->arrq == dbch){
|
|
off = OHCI_ARQOFF;
|
|
}else if(&sc->arrs == dbch){
|
|
off = OHCI_ARSOFF;
|
|
}else{
|
|
return;
|
|
}
|
|
|
|
s = splfw();
|
|
db_tr = dbch->top;
|
|
pcnt = 0;
|
|
/* XXX we cannot handle a packet which lies in more than two buf */
|
|
fwdma_sync_multiseg_all(dbch->am, BUS_DMASYNC_POSTREAD);
|
|
fwdma_sync_multiseg_all(dbch->am, BUS_DMASYNC_POSTWRITE);
|
|
status = FWOHCI_DMA_READ(db_tr->db[0].db.desc.res) >> OHCI_STATUS_SHIFT;
|
|
resCount = FWOHCI_DMA_READ(db_tr->db[0].db.desc.res) & OHCI_COUNT_MASK;
|
|
#if 0
|
|
printf("status 0x%04x, resCount 0x%04x\n", status, resCount);
|
|
#endif
|
|
while (status & OHCI_CNTL_DMA_ACTIVE) {
|
|
len = dbch->xferq.psize - resCount;
|
|
ld = (u_int8_t *)db_tr->buf;
|
|
if (dbch->pdb_tr == NULL) {
|
|
len -= dbch->buf_offset;
|
|
ld += dbch->buf_offset;
|
|
}
|
|
if (len > 0)
|
|
bus_dmamap_sync(dbch->dmat, db_tr->dma_map,
|
|
BUS_DMASYNC_POSTREAD);
|
|
while (len > 0 ) {
|
|
if (count >= 0 && count-- == 0)
|
|
goto out;
|
|
if(dbch->pdb_tr != NULL){
|
|
/* we have a fragment in previous buffer */
|
|
int rlen;
|
|
|
|
offset = dbch->buf_offset;
|
|
if (offset < 0)
|
|
offset = - offset;
|
|
buf = dbch->pdb_tr->buf + offset;
|
|
rlen = dbch->xferq.psize - offset;
|
|
if (firewire_debug)
|
|
printf("rlen=%d, offset=%d\n",
|
|
rlen, dbch->buf_offset);
|
|
if (dbch->buf_offset < 0) {
|
|
/* splitted in header, pull up */
|
|
char *p;
|
|
|
|
p = (char *)&pktbuf;
|
|
bcopy(buf, p, rlen);
|
|
p += rlen;
|
|
/* this must be too long but harmless */
|
|
rlen = sizeof(pktbuf) - rlen;
|
|
if (rlen < 0)
|
|
printf("why rlen < 0\n");
|
|
bcopy(db_tr->buf, p, rlen);
|
|
ld += rlen;
|
|
len -= rlen;
|
|
hlen = fwohci_arcv_swap(&pktbuf, sizeof(pktbuf));
|
|
if (hlen < 0) {
|
|
printf("hlen < 0 shouldn't happen");
|
|
}
|
|
offset = sizeof(pktbuf);
|
|
vec[0].iov_base = (char *)&pktbuf;
|
|
vec[0].iov_len = offset;
|
|
} else {
|
|
/* splitted in payload */
|
|
offset = rlen;
|
|
vec[0].iov_base = buf;
|
|
vec[0].iov_len = rlen;
|
|
}
|
|
fp=(struct fw_pkt *)vec[0].iov_base;
|
|
nvec = 1;
|
|
} else {
|
|
/* no fragment in previous buffer */
|
|
fp=(struct fw_pkt *)ld;
|
|
hlen = fwohci_arcv_swap(fp, len);
|
|
if (hlen == 0)
|
|
/* XXX need reset */
|
|
goto out;
|
|
if (hlen < 0) {
|
|
dbch->pdb_tr = db_tr;
|
|
dbch->buf_offset = - dbch->buf_offset;
|
|
/* sanity check */
|
|
if (resCount != 0)
|
|
printf("resCount != 0 !?\n");
|
|
goto out;
|
|
}
|
|
offset = 0;
|
|
nvec = 0;
|
|
}
|
|
plen = fwohci_get_plen(sc, dbch, fp) - offset;
|
|
if (plen < 0) {
|
|
/* minimum header size + trailer
|
|
= sizeof(fw_pkt) so this shouldn't happens */
|
|
printf("plen is negative! offset=%d\n", offset);
|
|
goto out;
|
|
}
|
|
if (plen > 0) {
|
|
len -= plen;
|
|
if (len < 0) {
|
|
dbch->pdb_tr = db_tr;
|
|
if (firewire_debug)
|
|
printf("splitted payload\n");
|
|
/* sanity check */
|
|
if (resCount != 0)
|
|
printf("resCount != 0 !?\n");
|
|
goto out;
|
|
}
|
|
vec[nvec].iov_base = ld;
|
|
vec[nvec].iov_len = plen;
|
|
nvec ++;
|
|
ld += plen;
|
|
}
|
|
dbch->buf_offset = ld - (u_int8_t *)db_tr->buf;
|
|
if (nvec == 0)
|
|
printf("nvec == 0\n");
|
|
|
|
/* DMA result-code will be written at the tail of packet */
|
|
#if BYTE_ORDER == BIG_ENDIAN
|
|
stat = FWOHCI_DMA_READ(((struct fwohci_trailer *)(ld - sizeof(struct fwohci_trailer)))->stat) >> 16;
|
|
#else
|
|
stat = ((struct fwohci_trailer *)(ld - sizeof(struct fwohci_trailer)))->stat;
|
|
#endif
|
|
#if 0
|
|
printf("plen: %d, stat %x\n", plen ,stat);
|
|
#endif
|
|
spd = (stat >> 5) & 0x3;
|
|
stat &= 0x1f;
|
|
switch(stat){
|
|
case FWOHCIEV_ACKPEND:
|
|
#if 0
|
|
printf("fwohci_arcv: ack pending tcode=0x%x..\n", fp->mode.common.tcode);
|
|
#endif
|
|
/* fall through */
|
|
case FWOHCIEV_ACKCOMPL:
|
|
if ((vec[nvec-1].iov_len -=
|
|
sizeof(struct fwohci_trailer)) == 0)
|
|
nvec--;
|
|
fw_rcv(&sc->fc, vec, nvec, 0, spd);
|
|
break;
|
|
case FWOHCIEV_BUSRST:
|
|
if (sc->fc.status != FWBUSRESET)
|
|
printf("got BUSRST packet!?\n");
|
|
break;
|
|
default:
|
|
device_printf(sc->fc.dev, "Async DMA Receive error err = %02x %s\n", stat, fwohcicode[stat]);
|
|
#if 0 /* XXX */
|
|
goto out;
|
|
#endif
|
|
break;
|
|
}
|
|
pcnt ++;
|
|
if (dbch->pdb_tr != NULL) {
|
|
fwohci_arcv_free_buf(dbch, dbch->pdb_tr);
|
|
dbch->pdb_tr = NULL;
|
|
}
|
|
|
|
}
|
|
out:
|
|
if (resCount == 0) {
|
|
/* done on this buffer */
|
|
if (dbch->pdb_tr == NULL) {
|
|
fwohci_arcv_free_buf(dbch, db_tr);
|
|
dbch->buf_offset = 0;
|
|
} else
|
|
if (dbch->pdb_tr != db_tr)
|
|
printf("pdb_tr != db_tr\n");
|
|
db_tr = STAILQ_NEXT(db_tr, link);
|
|
status = FWOHCI_DMA_READ(db_tr->db[0].db.desc.res)
|
|
>> OHCI_STATUS_SHIFT;
|
|
resCount = FWOHCI_DMA_READ(db_tr->db[0].db.desc.res)
|
|
& OHCI_COUNT_MASK;
|
|
/* XXX check buffer overrun */
|
|
dbch->top = db_tr;
|
|
} else {
|
|
dbch->buf_offset = dbch->xferq.psize - resCount;
|
|
break;
|
|
}
|
|
/* XXX make sure DMA is not dead */
|
|
}
|
|
#if 0
|
|
if (pcnt < 1)
|
|
printf("fwohci_arcv: no packets\n");
|
|
#endif
|
|
splx(s);
|
|
}
|