freebsd-dev/sys/dev/hfa/fore_init.c
Hartmut Brandt 50b751a7d4 Make the driver to work with firmware version 4.1.12 (other 4.X.Y should
also do it). Three problems have been encountered:

1. The initialisation command does not work in interrupt mode. Whether
   this is a firmware bug or a feature is not clear. The original Fore
   drivers execute the initialize command always in polling mode, so
   it appears that this behaviour is expected. When we detect a 4.X.Y
   firmware do busy wait on the command status.

2. The command code of the GET_PROM command has changed. This is an
   unofficial command anyway. What was GET_PROM in 3.X.Y is CLEAR_STATS
   in 4.X.Y (although unimplemented in the firmware). We need to
   use the correct code depending on the firmware.

3. The 4.X.Y firmware can set the error flag in the command status
   without also setting the completion flag (as the documenation says).
   Check both variants.

An additional field in the per-card structure fu_ft4 is TRUE when we have
detected a 4.X.Y firmware. Otherwise it is false. The behaviour of the
driver when using a 3.X.Y firmware should be identical to the previous
behaviour.

This change will enable traffic shaping of (at least one) CBR channels.
2003-07-31 14:20:07 +00:00

381 lines
9.3 KiB
C

/*
*
* ===================================
* HARP | Host ATM Research Platform
* ===================================
*
*
* This Host ATM Research Platform ("HARP") file (the "Software") is
* made available by Network Computing Services, Inc. ("NetworkCS")
* "AS IS". NetworkCS does not provide maintenance, improvements or
* support of any kind.
*
* NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
* INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
* SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
* In no event shall NetworkCS be responsible for any damages, including
* but not limited to consequential damages, arising from or relating to
* any use of the Software or related support.
*
* Copyright 1994-1998 Network Computing Services, Inc.
*
* Copies of this Software may be made, however, the above copyright
* notice must be reproduced on all copies.
*
* @(#) $FreeBSD$
*
*/
/*
* FORE Systems 200-Series Adapter Support
* ---------------------------------------
*
* Cell Processor (CP) initialization routines
*
*/
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/systm.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/syslog.h>
#include <vm/vm.h>
#include <vm/pmap.h>
#include <net/if.h>
#include <netatm/port.h>
#include <netatm/queue.h>
#include <netatm/atm.h>
#include <netatm/atm_sys.h>
#include <netatm/atm_sap.h>
#include <netatm/atm_cm.h>
#include <netatm/atm_if.h>
#include <netatm/atm_stack.h>
#include <netatm/atm_pcb.h>
#include <netatm/atm_var.h>
#include <pci/pcivar.h>
#include <dev/hfa/fore.h>
#include <dev/hfa/fore_aali.h>
#include <dev/hfa/fore_slave.h>
#include <dev/hfa/fore_stats.h>
#include <dev/hfa/fore_var.h>
#include <dev/hfa/fore_include.h>
#ifndef lint
__RCSID("@(#) $FreeBSD$");
#endif
/*
* Local functions
*/
static void fore_get_prom(Fore_unit *);
/*
* Begin CP Initialization
*
* This function will poll for the successful downloading and starting of
* the CP microcode program. After the microcode is running, we will allocate
* any needed kernel memory (must do it in non-interrupt mode), build the CP
* queue configurations and issue an Initialize command to the CP.
*
* Arguments:
* fup pointer to device unit structure
*
* Returns:
* none
*/
void
fore_initialize(fup)
Fore_unit *fup;
{
Aali *aap;
Init_parms *inp;
caddr_t errmsg;
u_long vers;
u_int c, wait;
#define MAX_WAIT 100
/*
* Must wait until firmware has been downloaded and is running
*/
if (CP_READ(fup->fu_mon->mon_bstat) != BOOT_RUNNING) {
/*
* Try again later
*/
fup->fu_thandle =
timeout((KTimeout_ret(*)(void *))fore_initialize,
(void *)fup, hz);
return;
} else
callout_handle_init(&fup->fu_thandle);
/*
* Allocate queues and whatever else is needed
*/
if (fore_xmit_allocate(fup)) {
errmsg = "transmit queue allocation";
goto failed;
}
if (fore_recv_allocate(fup)) {
errmsg = "receive queue allocation";
goto failed;
}
if (fore_buf_allocate(fup)) {
errmsg = "buffer supply queue allocation";
goto failed;
}
if (fore_cmd_allocate(fup)) {
errmsg = "command queue allocation";
goto failed;
}
/*
* CP microcode is downloaded - locate shared memory interface
*/
aap = (Aali *)(fup->fu_ram + CP_READ(fup->fu_mon->mon_appl));
fup->fu_aali = aap;
/*
* Pick out any interesting info from the microcode
*/
vers = CP_READ(aap->aali_ucode_ver);
if (vers < FORE_MIN_UCODE) {
errmsg = "unsupported microcode version";
goto failed;
}
snprintf(fup->fu_config.ac_firm_vers,
sizeof(fup->fu_config.ac_firm_vers), "%ld.%ld.%ld",
(vers >> 16) & 0xff, (vers >> 8) & 0xff, vers & 0xff);
if (((vers >> 16) & 0xff) == 4)
fup->fu_ft4 = 1;
else
fup->fu_ft4 = 0;
#ifdef notdef
/*
* Turn on CP debugging
*/
aap->aali_hostlog = 1;
#endif
/*
* Build the initialization block
*/
inp = &aap->aali_init;
inp->init_numvcc = CP_WRITE(FORE_MAX_VCC);
inp->init_cmd_elem = CP_WRITE(CMD_QUELEN);
inp->init_xmit_elem = CP_WRITE(XMIT_QUELEN);
inp->init_recv_elem = CP_WRITE(RECV_QUELEN);
inp->init_recv_ext = CP_WRITE(RECV_EXTRA_SEGS);
inp->init_xmit_ext = CP_WRITE(XMIT_EXTRA_SEGS);
inp->init_buf1s.bfs_quelen = CP_WRITE(BUF1_SM_QUELEN);
inp->init_buf1s.bfs_bufsize = CP_WRITE(BUF1_SM_SIZE);
inp->init_buf1s.bfs_cppool = CP_WRITE(BUF1_SM_CPPOOL);
inp->init_buf1s.bfs_entsize = CP_WRITE(BUF1_SM_ENTSIZE);
inp->init_buf1l.bfs_quelen = CP_WRITE(BUF1_LG_QUELEN);
inp->init_buf1l.bfs_bufsize = CP_WRITE(BUF1_LG_SIZE);
inp->init_buf1l.bfs_cppool = CP_WRITE(BUF1_LG_CPPOOL);
inp->init_buf1l.bfs_entsize = CP_WRITE(BUF1_LG_ENTSIZE);
inp->init_buf2s.bfs_quelen = CP_WRITE(0);
inp->init_buf2s.bfs_bufsize = CP_WRITE(0);
inp->init_buf2s.bfs_cppool = CP_WRITE(0);
inp->init_buf2s.bfs_entsize = CP_WRITE(0);
inp->init_buf2l.bfs_quelen = CP_WRITE(0);
inp->init_buf2l.bfs_bufsize = CP_WRITE(0);
inp->init_buf2l.bfs_cppool = CP_WRITE(0);
inp->init_buf2l.bfs_entsize = CP_WRITE(0);
/*
* Enable device interrupts
*/
aap->aali_intr_ena = CP_WRITE(1);
/*
* Issue the Initialize command to the CP and wait for
* the CP to interrupt to signal completion
*/
inp->init_status = CP_WRITE(QSTAT_PENDING);
if (!fup->fu_ft4) {
inp->init_cmd = CP_WRITE(CMD_INIT | CMD_INTR_REQ);
return;
}
inp->init_cmd = CP_WRITE(CMD_INIT);
/*
* With the ForeThought 4.X image it appears that we need to
* busy wait on the initializisation command to complete.
* Otherwise the command queue address (the first word
* of the queue structure) will be mangled.
*/
c = 0;
for (wait = 0; wait < MAX_WAIT; wait++) {
c = CP_READ(inp->init_status);
if (c & QSTAT_COMPLETED)
break;
DELAY(1000);
}
if (c & QSTAT_ERROR) {
log(LOG_ERR, "fore initialization failed: intf=%s%d, "
"hbeat=0x%lx\n", fup->fu_pif.pif_name,
fup->fu_pif.pif_unit, (u_long)CP_READ(aap->aali_heartbeat));
fore_interface_free(fup);
return;
}
if (!(c & QSTAT_COMPLETED)) {
log(LOG_ERR, "fore initialization timed out: intf=%s%d, "
"hbeat=0x%lx\n", fup->fu_pif.pif_name, fup->fu_pif.pif_unit,
(u_long)CP_READ(aap->aali_heartbeat));
fore_interface_free(fup);
return;
}
fore_initialize_complete(fup);
return;
failed:
/*
* Initialization failure
*/
fore_interface_free(fup);
log(LOG_ERR, "fore initialization failed: intf=%s%d, err=%s\n",
fup->fu_pif.pif_name, fup->fu_pif.pif_unit, errmsg);
return;
}
/*
* Complete CP Initialization
*
* Called after the CP has successfully completed processing of the
* Initialize command. We will now finish off our part of the
* initialization process by setting up all the host-based queue
* management structures.
*
* Called at interrupt level.
*
* Arguments:
* fup pointer to device unit structure
*
* Returns:
* none
*/
void
fore_initialize_complete(fup)
Fore_unit *fup;
{
Aali *aap = fup->fu_aali;
/*
* Log an initialization failure
*/
if (CP_READ(aap->aali_init.init_status) & QSTAT_ERROR) {
log(LOG_ERR,
"fore initialization failed: intf=%s%d, hbeat=0x%lx\n",
fup->fu_pif.pif_name, fup->fu_pif.pif_unit,
(u_long)CP_READ(aap->aali_heartbeat));
return;
}
ATM_DEBUG1("heap=0x%lx\n", aap->aali_heap);
ATM_DEBUG1("heaplen=0x%lx\n", aap->aali_heaplen);
ATM_DEBUG1("cmd_q=0x%lx\n", aap->aali_cmd_q);
ATM_DEBUG1("xmit_q=0x%lx\n", aap->aali_xmit_q);
ATM_DEBUG1("recv_q=0x%lx\n", aap->aali_recv_q);
ATM_DEBUG1("buf1s_q=0x%lx\n", aap->aali_buf1s_q);
ATM_DEBUG1("buf1l_q=0x%lx\n", aap->aali_buf1l_q);
ATM_DEBUG1("buf2s_q=0x%lx\n", aap->aali_buf2s_q);
ATM_DEBUG1("buf2l_q=0x%lx\n", aap->aali_buf2l_q);
/*
* Initialize all of our queues
*/
fore_xmit_initialize(fup);
fore_recv_initialize(fup);
fore_buf_initialize(fup);
fore_cmd_initialize(fup);
/*
* Mark device initialization completed
*/
fup->fu_flags |= CUF_INITED;
fore_get_prom(fup);
return;
}
/*
* Get device PROM values from CP
*
* This function will issue a GET_PROM command to the CP in order to
* initiate the DMA transfer of the CP's PROM structure to the host.
* This will be called after CP initialization has completed.
* There is (currently) no retry if this fails.
*
* It took me some time to find out that FT3 and FT4 use different
* operation codes for GET_PROM.
*
* Called at interrupt level.
*
* Arguments:
* fup pointer to device unit structure
*
* Returns:
* none
*
*/
static void
fore_get_prom(fup)
Fore_unit *fup;
{
H_cmd_queue *hcp;
Cmd_queue *cqp;
/*
* Queue command at end of command queue
*/
hcp = fup->fu_cmd_tail;
if ((*hcp->hcq_status) & QSTAT_FREE) {
/*
* Queue entry available, so set our view of things up
*/
if (fup->fu_ft4)
hcp->hcq_code = CMD_GET_PROM4;
else
hcp->hcq_code = CMD_GET_PROM;
hcp->hcq_arg = NULL;
fup->fu_cmd_tail = hcp->hcq_next;
/*
* Now set the CP-resident queue entry - the CP will grab
* the command when the op-code is set.
*/
cqp = hcp->hcq_cpelem;
(*hcp->hcq_status) = QSTAT_PENDING;
fup->fu_promd = (Fore_prom *)vtophys(fup->fu_prom);
if (fup->fu_promd == NULL) {
fup->fu_stats->st_drv.drv_cm_nodma++;
return;
}
cqp->cmdq_prom.prom_buffer = (CP_dma) CP_WRITE(fup->fu_promd);
cqp->cmdq_prom.prom_cmd = CP_WRITE(hcp->hcq_code | CMD_INTR_REQ);
} else {
/*
* Command queue full
*/
fup->fu_stats->st_drv.drv_cm_full++;
}
return;
}