diff options
Diffstat (limited to 'sys/dev/hfa/fore_load.c')
-rw-r--r-- | sys/dev/hfa/fore_load.c | 1618 |
1 files changed, 1618 insertions, 0 deletions
diff --git a/sys/dev/hfa/fore_load.c b/sys/dev/hfa/fore_load.c new file mode 100644 index 0000000..4250ddc --- /dev/null +++ b/sys/dev/hfa/fore_load.c @@ -0,0 +1,1618 @@ +/* + * + * =================================== + * 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. + * + * @(#) $Id: fore_load.c,v 1.12 1998/06/29 21:42:14 mks Exp $ + * + */ + +/* + * FORE Systems 200-Series Adapter Support + * --------------------------------------- + * + * Loadable kernel module and device identification support + * + */ + +#ifndef lint +static char *RCSid = "@(#) $Id: fore_load.c,v 1.12 1998/06/29 21:42:14 mks Exp $"; +#endif + +#include <dev/hfa/fore_include.h> + + +/* + * Local functions + */ +static int fore_start __P((void)); +static int fore_stop __P((void)); +static int fore_doload __P((void)); +static int fore_dounload __P((void)); +#ifdef sun +static int fore_identify __P((char *)); +static int fore_attach __P((struct devinfo *)); +#endif +#ifdef __FreeBSD__ +static char * fore_pci_probe __P((pcici_t, pcidi_t)); +static void fore_pci_attach __P((pcici_t, int)); +#if BSD < 199506 +static int fore_pci_shutdown __P((struct kern_devconf *, int)); +#else +static void fore_pci_shutdown __P((int, void *)); +#endif +#endif +static void fore_unattach __P((Fore_unit *)); +static void fore_reset __P((Fore_unit *)); + + +/* + * Local variables + */ +static int fore_inited = 0; + +/* + * Driver entry points + */ +#ifdef sun +static struct dev_ops fore_ops = { + 1, /* revision */ + fore_identify, /* identify */ + fore_attach, /* attach */ + NULL, /* open */ + NULL, /* close */ + NULL, /* read */ + NULL, /* write */ + NULL, /* strategy */ + NULL, /* dump */ + NULL, /* psize */ + NULL, /* ioctl */ + NULL, /* reset */ + NULL /* mmap */ +}; +#endif + +#ifdef __FreeBSD__ +static u_long fore_pci_count = 0; + +static struct pci_device fore_pci_device = { + FORE_DEV_NAME, + fore_pci_probe, + fore_pci_attach, + &fore_pci_count, +#if BSD < 199506 + fore_pci_shutdown +#else + NULL +#endif +}; + +DATA_SET(pcidevice_set, fore_pci_device); +#endif + + +/* + * Initialize driver processing + * + * This will be called during module loading. Not much to do here, as + * we must wait for our identify/attach routines to get called before + * we know what we're in for. + * + * Arguments: + * none + * + * Returns: + * 0 startup was successful + * errno startup failed - reason indicated + * + */ +static int +fore_start() +{ + + /* + * Verify software version + */ + if (atm_version != ATM_VERSION) { + log(LOG_ERR, "version mismatch: fore=%d.%d kernel=%d.%d\n", + ATM_VERS_MAJ(ATM_VERSION), ATM_VERS_MIN(ATM_VERSION), + ATM_VERS_MAJ(atm_version), ATM_VERS_MIN(atm_version)); + return (EINVAL); + } + + /* + * Initialize DMA mapping + */ + DMA_INIT(); + + /* + * Start up watchdog timer + */ + atm_timeout(&fore_timer, ATM_HZ * FORE_TIME_TICK, fore_timeout); + + fore_inited = 1; + + return (0); +} + + +/* + * Halt driver processing + * + * This will be called just prior to unloading the module from memory. + * Everything we've setup since we've been loaded must be undone here. + * + * Arguments: + * none + * + * Returns: + * 0 shutdown was successful + * errno shutdown failed - reason indicated + * + */ +static int +fore_stop() +{ + int err = 0; + int s = splimp(); + int i; + + /* + * Stop the watchdog timer + */ + (void) atm_untimeout(&fore_timer); + + /* + * Clean up each device (if any) + */ + for ( i = 0; i < fore_nunits; i++ ) { + Fore_unit *fup = fore_units[i]; + + if (fup == NULL) + continue; + + /* + * Deregister device from kernel services + */ + if (err = atm_physif_deregister((Cmn_unit *)fup)) { + (void) splx(s); + return (err); + } + + /* + * Unattach the device from the system + */ + fore_unattach(fup); + + /* + * Free any Fore-specific device resources + */ + fore_interface_free(fup); + + /* + * Free the unit structure + */ + atm_dev_free(fup); + fore_units[i] = NULL; + } + + fore_nunits = 0; + + /* + * Now free our global resources + */ + + /* + * Release our storage pools + */ + atm_release_pool(&fore_vcc_pool); + atm_release_pool(&fore_nif_pool); + + /* + * Release all DMA mappings + */ + DMA_RELEASE(); + + fore_inited = 0; + + (void) splx(s); + + return (0); +} + + +#ifdef sun +/* + * Device identify routine + * + * Determine if this driver will support the named device. If we claim to + * support the device, our attach routine will (later) be called for the + * device. + * + * Arguments: + * name pointer to identifier string from device + * + * Returns: + * 1 driver claims support for this device + * 0 device not claimed by this driver + * + */ +static int +fore_identify(name) + char *name; +{ + int ret = 0; + int i = 0; + + /* + * Initialize driver stuff + */ + if (fore_inited == 0) { + if (fore_start()) + return (0); + } + + while (fore_devices[i].fd_name) { + if (strcmp(fore_devices[i].fd_name, name) == 0) { + + /* + * We support this device!! + */ + if (fore_nunits < FORE_MAX_UNITS) { + fore_nunits++; + ret = 1; + } else { + log(LOG_ERR, + "fore_identify: Too many devices\n"); + } + break; + } + i++; + } + return (ret); +} + + +/* + * Device attach routine + * + * Attach a device we've previously claimed to support. Walk through its + * register set and map, as required. Determine what level the device will + * be interrupting at and then register an interrupt handler for it. If we + * succeed, then reset the adapter and read useful info from its PROM. + * Last, register the interface with the kernel ATM services. + * + * Arguments: + * devinfo_p pointer to device information structure + * + * Returns: + * 0 attach was successful + * -1 attach failed + * + */ +static int +fore_attach(devinfo_p) + struct dev_info *devinfo_p; +{ + struct dev_reg *dev_reg_p; + struct dev_intr *dev_intr_p; + Fore_unit *fup; + Atm_config *fcp; + addr_t valp; + int val; + int i; + int err_count = BOOT_LOOPS; + static int unit = 0; + + /* + * Sanity check + */ + if (devinfo_p == NULL) + return (-1); + + /* + * Make sure this isn't a duplicate unit + */ + if (fore_units[unit] != NULL) + return (-1); + + /* + * Allocate a new unit structure + */ + fup = (Fore_unit *) atm_dev_alloc(sizeof(Fore_unit), sizeof(int), 0); + if (fup == NULL) + return (-1); + + /* + * Start initializing it + */ + fup->fu_unit = unit; + fup->fu_mtu = FORE_IFF_MTU; + fup->fu_devinfo = devinfo_p; + fup->fu_vcc_pool = &fore_vcc_pool; + fup->fu_nif_pool = &fore_nif_pool; + fup->fu_ioctl = fore_atm_ioctl; + fup->fu_instvcc = fore_instvcc; + fup->fu_openvcc = fore_openvcc; + fup->fu_closevcc = fore_closevcc; + fup->fu_output = fore_output; + + /* + * Consider this unit assigned + */ + fore_units[unit] = fup; + unit++; + + ATM_DEBUG1("fore_attach: fup=0x%x\n", (int)fup); + ATM_DEBUG2("\tfu_xmit_q=0x%x fu_xmit_head=0x%x\n", + (int)fup->fu_xmit_q, (int)&fup->fu_xmit_head); + ATM_DEBUG2("\tfu_recv_q=0x%x fu_recv_head=0x%x\n", + (int)fup->fu_recv_q, (int)&fup->fu_recv_head); + ATM_DEBUG2("\tfu_buf1s_q=0x%x fu_buf1s_head=0x%x\n", + (int)fup->fu_buf1s_q, (int)&fup->fu_buf1s_head); + ATM_DEBUG2("\tfu_buf1l_q=0x%x fu_buf1l_head=0x%x\n", + (int)fup->fu_buf1l_q, (int)&fup->fu_buf1l_head); + ATM_DEBUG2("\tfu_cmd_q=0x%x fu_cmd_head=0x%x\n", + (int)fup->fu_cmd_q, (int)&fup->fu_cmd_head); + ATM_DEBUG1("\tfu_stats=0x%x\n", + (int)&fup->fu_stats); + + /* + * Tell kernel our unit number + */ + devinfo_p->devi_unit = fup->fu_unit; + + /* + * Figure out what type of device we've got. This should always + * work since we've already done this at identify time! + */ + i = 0; + while (fore_devices[i].fd_name) { + if (strcmp(fore_devices[i].fd_name, devinfo_p->devi_name) == 0) + break; + i++; + } + if (fore_devices[i].fd_name == NULL) + return (-1); + + fup->fu_config.ac_device = fore_devices[i].fd_devtyp; + + /* + * Walk through the OPENPROM register information + * mapping register banks as they are found. + */ + for ( dev_reg_p = devinfo_p->devi_reg, i = 1; + i <= devinfo_p->devi_nreg; i++, ++dev_reg_p ) + { + if ( dev_reg_p == NULL ) + { + /* + * Can't happen... + */ + return ( -1 ); + } + + /* + * Each device type has different register sets + */ + switch (fup->fu_config.ac_device) { + +#ifdef FORE_SBUS + case DEV_FORE_SBA200E: + + switch ( i ) + { + /* + * Host Control Register (HCR) + */ + case 1: + if ( sizeof(Fore_reg) != dev_reg_p->reg_size ) + { + return ( -1 ); + } + fup->fu_ctlreg = (Fore_reg *) + map_regs ( dev_reg_p->reg_addr, + sizeof(Fore_reg), + dev_reg_p->reg_bustype ); + if ( fup->fu_ctlreg == NULL ) + { + return ( -1 ); + } + break; + + /* + * SBus Burst Transfer Configuration Register + */ + case 2: + /* + * Not used + */ + break; + + /* + * SBus Interrupt Level Select Register + */ + case 3: + if ( sizeof (Fore_reg) != dev_reg_p->reg_size ) + { + return ( -1 ); + } + fup->fu_intlvl = (Fore_reg *) + map_regs ( dev_reg_p->reg_addr, + sizeof(Fore_reg), + dev_reg_p->reg_bustype ); + if ( fup->fu_intlvl == NULL ) + { + return ( -1 ); + } + break; + + /* + * i960 RAM + */ + case 4: + fup->fu_ram = (Fore_mem *) + map_regs ( dev_reg_p->reg_addr, + dev_reg_p->reg_size, + dev_reg_p->reg_bustype ); + if ( fup->fu_ram == NULL ) + { + return ( -1 ); + } + fup->fu_ramsize = dev_reg_p->reg_size; + + /* + * Various versions of the Sun PROM mess with + * the reg_addr value in unpredictable (to me, + * at least) ways, so just use the "memoffset" + * property, which should give us the RAM + * offset directly. + */ + val = getprop(devinfo_p->devi_nodeid, + "memoffset", -1); + if (val == -1) { + return (-1); + } + fup->fu_config.ac_ram = val; + fup->fu_config.ac_ramsize = fup->fu_ramsize; + + /* + * Set monitor interface for initializing + */ + fup->fu_mon = (Mon960 *) + (fup->fu_ram + MON960_BASE); + break; + + default: + log(LOG_ERR, + "fore_attach: Too many registers\n"); + return ( -1 ); + } + break; + + case DEV_FORE_SBA200: + + switch ( i ) + { + /* + * Board Control Register (BCR) + */ + case 1: + if ( sizeof(Fore_reg) != dev_reg_p->reg_size ) + { + return ( -1 ); + } + fup->fu_ctlreg = (Fore_reg *) + map_regs ( dev_reg_p->reg_addr, + sizeof(Fore_reg), + dev_reg_p->reg_bustype ); + if ( fup->fu_ctlreg == NULL ) + { + return ( -1 ); + } + break; + + /* + * i960 RAM + */ + case 2: + fup->fu_ram = (Fore_mem *) + map_regs ( dev_reg_p->reg_addr, + dev_reg_p->reg_size, + dev_reg_p->reg_bustype ); + if ( fup->fu_ram == NULL ) + { + return ( -1 ); + } + fup->fu_ramsize = dev_reg_p->reg_size; + + /* + * Various versions of the Sun PROM mess with + * the reg_addr value in unpredictable (to me, + * at least) ways, so just use the "memoffset" + * property, which should give us the RAM + * offset directly. + */ + val = getprop(devinfo_p->devi_nodeid, + "memoffset", -1); + if (val == -1) { + return (-1); + } + fup->fu_config.ac_ram = val; + fup->fu_config.ac_ramsize = fup->fu_ramsize; + + /* + * Set monitor interface for initializing + */ + fup->fu_mon = (Mon960 *) + (fup->fu_ram + MON960_BASE); + break; + + default: + log(LOG_ERR, + "fore_attach: Too many registers\n"); + return ( -1 ); + } + break; +#endif /* FORE_SBUS */ + + default: + log(LOG_ERR, + "fore_attach: Unsupported device type %d\n", + fup->fu_config.ac_device); + return (-1); + } + } + + /* + * Install the device in the interrupt chain. + * + * dev_intr_p may be null IFF devi_nintr is zero. + */ + dev_intr_p = devinfo_p->devi_intr; + for ( i = devinfo_p->devi_nintr; i > 0; --i, ++dev_intr_p ) + { + + if ( dev_intr_p == NULL ) + { + /* + * Can't happen. + */ + return ( -1 ); + } + + /* + * Convert hardware ipl (0-15) into spl level. + */ + if ( ipltospl ( dev_intr_p->int_pri ) > fup->fu_intrpri ) + { + fup->fu_intrpri = ipltospl ( dev_intr_p->int_pri ); + + /* + * If SBA-200E card, set SBus interrupt level + * into board register + */ + if ( fup->fu_intlvl ) { +#if defined(sun4c) + *(fup->fu_intlvl) = dev_intr_p->int_pri; +#elif defined(sun4m) + extern int svimap[]; + + *(fup->fu_intlvl) = + svimap[dev_intr_p->int_pri & 0xf]; +#else + #error PORT ME; +#endif + } + } + + DEVICE_LOCK((Cmn_unit *)fup); + + /* + * Register our interrupt routine. + */ + (void) addintr ( dev_intr_p->int_pri, fore_poll, + devinfo_p->devi_name, devinfo_p->devi_unit ); + + /* + * If we can do DMA (we can), then DVMA routines need + * to know the highest IPL level we will interrupt at. + */ + adddma ( dev_intr_p->int_pri ); + + DEVICE_UNLOCK((Cmn_unit *)fup); + } + + /* + * Poke the hardware...boot the CP and prepare it for downloading + */ + fore_reset(fup); + + switch (fup->fu_config.ac_device) { + +#ifdef FORE_SBUS + case DEV_FORE_SBA200E: + /* + * Enable interrupts + */ + SBA200E_HCR_SET(*fup->fu_ctlreg, SBA200E_SBUS_ENA); + break; +#endif /* FORE_SBUS */ + } + + /* + * Wait for monitor to perform self-test + */ + while (CP_READ(fup->fu_mon->mon_bstat) != BOOT_MONREADY) { + if (CP_READ(fup->fu_mon->mon_bstat) == BOOT_FAILTEST) { + log(LOG_ERR, "fore_attach: Unit %d failed self-test\n", + fup->fu_unit); + return (-1); + + } else if ( --err_count == 0 ) { + log(LOG_ERR, "fore_attach: Unit %d unable to boot\n", + fup->fu_unit); + return (-1); + } + DELAY ( BOOT_DELAY ); + } + + /* + * Write a one line message to the console informing + * that we've attached the device. + */ + report_dev ( devinfo_p ); + + /* + * Get the mac address from the card PROM + */ + val = getprop ( devinfo_p->devi_nodeid, "macaddress1", -1 ); + if ( val != -1 ) { + fup->fu_pif.pif_macaddr.ma_data[0] = val & 0xff; + val = getprop ( devinfo_p->devi_nodeid, "macaddress2", -1 ); + fup->fu_pif.pif_macaddr.ma_data[1] = val & 0xff; + val = getprop ( devinfo_p->devi_nodeid, "macaddress3", -1 ); + fup->fu_pif.pif_macaddr.ma_data[2] = val & 0xff; + val = getprop ( devinfo_p->devi_nodeid, "macaddress4", -1 ); + fup->fu_pif.pif_macaddr.ma_data[3] = val & 0xff; + val = getprop ( devinfo_p->devi_nodeid, "macaddress5", -1 ); + fup->fu_pif.pif_macaddr.ma_data[4] = val & 0xff; + val = getprop ( devinfo_p->devi_nodeid, "macaddress6", -1 ); + fup->fu_pif.pif_macaddr.ma_data[5] = val & 0xff; + } else { + /* + * Newer PROM - mac addresses have been combined. Also, + * macaddrlo2 reflects the board serial number. + */ + val = htonl(getprop(devinfo_p->devi_nodeid, "macaddrlo2", -1)); + KM_COPY ( (caddr_t)&val, + (caddr_t)&fup->fu_pif.pif_macaddr.ma_data[2], + sizeof(val) ); + val = htonl(getprop(devinfo_p->devi_nodeid, "macaddrhi4", -1)); + KM_COPY ( (caddr_t)&val, + (caddr_t)fup->fu_pif.pif_macaddr.ma_data, + sizeof(val) ); + } + + /* + * Setup the adapter config info + */ + fcp = &fup->fu_config; + fcp->ac_vendor = VENDOR_FORE; + fcp->ac_vendapi = VENDAPI_FORE_1; + fcp->ac_macaddr = fup->fu_pif.pif_macaddr; + val = getprop ( devinfo_p->devi_nodeid, "promversion", -1 ); + if ( val == -1 ) { + val = getprop ( devinfo_p->devi_nodeid, "hw-version", -1 ); + } + if (val != -1) { + sprintf(fcp->ac_hard_vers, "%d.%d.%d", + (val >> 16) & 0xff, (val >> 8) & 0xff, val & 0xff); + } else + sprintf(fcp->ac_hard_vers, "Unknown"); + + val = getprop ( devinfo_p->devi_nodeid, "serialnumber", -1 ); + if ( val != -1 ) + fcp->ac_serial = val; + + valp = (addr_t)getlongprop ( devinfo_p->devi_nodeid, "model" ); + if ( valp ) + { + /* + * Media Type + */ + switch (fcp->ac_device) { + +#ifdef FORE_SBUS + case DEV_FORE_SBA200E: + fcp->ac_media = MEDIA_OC3C; + fup->fu_pif.pif_pcr = ATM_PCR_OC3C; + break; + + case DEV_FORE_SBA200: + /* + * Look at the /SSS trailer to determine 4B5B speed + * TAXI-100 = 125; TAXI-140 = 175 + * Assume that OC3 has no /SSS speed identifier. + */ + while (*valp && *valp != '/') + valp++; + if (*valp == NULL) { + fcp->ac_media = MEDIA_OC3C; + fup->fu_pif.pif_pcr = ATM_PCR_OC3C; + } else if (strcmp(valp, "/125") == 0) { + fcp->ac_media = MEDIA_TAXI_100; + fup->fu_pif.pif_pcr = ATM_PCR_TAXI100; + } else { + fcp->ac_media = MEDIA_TAXI_140; + fup->fu_pif.pif_pcr = ATM_PCR_TAXI140; + } + break; +#endif /* FORE_SBUS */ + } + + /* + * Free property space + */ + KM_FREE(valp, getproplen(devinfo_p->devi_nodeid, "model"), 0); + } + + /* + * Bus information + */ + fcp->ac_busslot = +#ifdef SBUS_SIZE + (long)(devinfo_p->devi_reg->reg_addr - SBUS_BASE) / SBUS_SIZE; +#else + sbusslot((u_long)devinfo_p->devi_reg->reg_addr); +#endif + + val = getprop(devinfo_p->devi_parent->devi_nodeid, "burst-sizes", 0); + if (val & SBUS_BURST32) + fcp->ac_bustype = BUS_SBUS_B32; + else + fcp->ac_bustype = BUS_SBUS_B16; + + /* + * Set device capabilities + */ + fup->fu_pif.pif_maxvpi = FORE_MAX_VPI; + fup->fu_pif.pif_maxvci = FORE_MAX_VCI; + + /* + * Register this interface with ATM core services + */ + if ( atm_physif_register + ((Cmn_unit *)fup, FORE_DEV_NAME, fore_services) != 0 ) + { + /* + * Registration failed - back everything out + */ + /* + * Modload calls UNLOAD if it get's a failure - don't + * call fore_unload() here. + */ + return ( -1 ); + } + + /* + * Initialize the CP microcode program. + */ + fore_initialize(fup); + + return (0); +} +#endif /* sun */ + + +#ifdef __FreeBSD__ +/* + * Device probe routine + * + * Determine if this driver will support the identified device. If we claim + * to support the device, our attach routine will (later) be called for the + * device. + * + * Arguments: + * config_id device's PCI configuration ID + * device_id device's PCI Vendor/Device ID + * + * Returns: + * name device identification string + * NULL device not claimed by this driver + * + */ +static char * +fore_pci_probe(config_id, device_id) + pcici_t config_id; + pcidi_t device_id; +{ + + /* + * Initialize driver stuff + */ + if (fore_inited == 0) { + if (fore_start()) + return (NULL); + } + + if ((device_id & 0xffff) != FORE_VENDOR_ID) + return (NULL); + + if (((device_id >> 16) & 0xffff) == FORE_PCA200E_ID) + return ("FORE Systems PCA-200E ATM"); + + return (NULL); +} + + +/* + * Device attach routine + * + * Attach a device we've previously claimed to support. Walk through its + * register set and map, as required. Determine what level the device will + * be interrupting at and then register an interrupt handler for it. If we + * succeed, then reset the adapter and initialize the microcode. + * Last, register the interface with the kernel ATM services. + * + * Arguments: + * config_id device's PCI configuration ID + * unit device unit number + * + * Returns: + * none + * + */ +static void +fore_pci_attach(config_id, unit) + pcici_t config_id; + int unit; +{ + Fore_unit *fup; + vm_offset_t va; + vm_offset_t pa; + pcidi_t device_id; + long val; + int err_count = BOOT_LOOPS; + + /* + * Just checking... + */ + if (unit >= FORE_MAX_UNITS) { + log(LOG_ERR, "%s%d: too many devices\n", + FORE_DEV_NAME, unit); + return; + } + + /* + * Make sure this isn't a duplicate unit + */ + if (fore_units[unit] != NULL) + return; + + /* + * Allocate a new unit structure + */ + fup = (Fore_unit *) atm_dev_alloc(sizeof(Fore_unit), sizeof(int), 0); + if (fup == NULL) + return; + + /* + * Start initializing it + */ + fup->fu_unit = unit; + fup->fu_mtu = FORE_IFF_MTU; + fup->fu_pcitag = config_id; + fup->fu_vcc_pool = &fore_vcc_pool; + fup->fu_nif_pool = &fore_nif_pool; + fup->fu_ioctl = fore_atm_ioctl; + fup->fu_instvcc = fore_instvcc; + fup->fu_openvcc = fore_openvcc; + fup->fu_closevcc = fore_closevcc; + fup->fu_output = fore_output; + callout_handle_init(&fup->fu_thandle); + + /* + * Get our device type + */ + device_id = pci_conf_read ( config_id, PCI_ID_REG ); + switch ((device_id >> 16) & 0xffff) { + + case FORE_PCA200E_ID: + fup->fu_config.ac_device = DEV_FORE_PCA200E; + break; + + default: + fup->fu_config.ac_device = DEV_UNKNOWN; + } + + /* + * Map RAM + */ + if ((pci_map_mem(config_id, PCA200E_PCI_MEMBASE, &va, &pa)) == 0) { + log(LOG_ERR, "%s%d: unable to map memory\n", + FORE_DEV_NAME, unit); + goto failed; + } + fup->fu_ram = (Fore_mem *)va; + fup->fu_ramsize = PCA200E_RAM_SIZE; + fup->fu_mon = (Mon960 *)(fup->fu_ram + MON960_BASE); + fup->fu_ctlreg = (Fore_reg *)(va + PCA200E_HCR_OFFSET); + fup->fu_imask = (Fore_reg *)(va + PCA200E_IMASK_OFFSET); + fup->fu_psr = (Fore_reg *)(va + PCA200E_PSR_OFFSET); + + /* + * Convert Endianess of Slave RAM accesses + */ + val = pci_conf_read(config_id, PCA200E_PCI_MCTL); + val |= PCA200E_MCTL_SWAP; + pci_conf_write(config_id, PCA200E_PCI_MCTL, val); + + /* + * Map interrupt in + */ + if ( !pci_map_int( config_id, fore_intr, fup, &net_imask ) ) { + log(LOG_ERR, "%s%d: unable to map interrupt\n", + FORE_DEV_NAME, unit); + goto failed; + } + + /* + * Poke the hardware - boot the CP and prepare it for downloading + */ + fore_reset(fup); + + /* + * Wait for the monitor to perform self-test + */ + while (CP_READ(fup->fu_mon->mon_bstat) != BOOT_MONREADY) { + if (CP_READ(fup->fu_mon->mon_bstat) == BOOT_FAILTEST) { + log(LOG_ERR, "%s%d: failed self-test\n", + FORE_DEV_NAME, unit); + goto failed; + } else if ( --err_count == 0 ) { + log(LOG_ERR, "%s%d: unable to boot - status=0x%x\n", + FORE_DEV_NAME, unit, + CP_READ(fup->fu_mon->mon_bstat)); + goto failed; + } + DELAY ( BOOT_DELAY ); + } + + /* + * Setup the adapter config info - at least as much as we can + */ + fup->fu_config.ac_vendor = VENDOR_FORE; + fup->fu_config.ac_vendapi = VENDAPI_FORE_1; + fup->fu_config.ac_media = MEDIA_OC3C; + fup->fu_pif.pif_pcr = ATM_PCR_OC3C; + fup->fu_config.ac_bustype = BUS_PCI; + fup->fu_config.ac_busslot = config_id->bus << 8 | config_id->slot; + + /* + * Save device ram info for user-level programs + */ + fup->fu_config.ac_ram = (long)fup->fu_ram; + fup->fu_config.ac_ramsize = fup->fu_ramsize; + + /* + * Set device capabilities + */ + fup->fu_pif.pif_maxvpi = FORE_MAX_VPI; + fup->fu_pif.pif_maxvci = FORE_MAX_VCI; + + /* + * Register this interface with ATM core services + */ + if ( atm_physif_register + ((Cmn_unit *)fup, FORE_DEV_NAME, fore_services) != 0 ) + { + /* + * Registration failed - back everything out + */ + goto failed; + } + + fore_units[unit] = fup; + fore_nunits++; + +#if BSD >= 199506 + /* + * Add hook to our shutdown function + */ + at_shutdown(fore_pci_shutdown, fup, SHUTDOWN_POST_SYNC); +#endif + + /* + * Initialize the CP microcode program. + */ + fore_initialize(fup); + + return; + +failed: + /* + * Unattach the device from the system + */ + fore_unattach(fup); + + /* + * Free any Fore-specific device resources + */ + fore_interface_free(fup); + + atm_dev_free(fup); + + return; +} + + +#if BSD < 199506 +/* + * Device shutdown routine + * + * Arguments: + * kdc pointer to device's configuration table + * force forced shutdown flag + * + * Returns: + * none + * + */ +static int +fore_pci_shutdown(kdc, force) + struct kern_devconf *kdc; + int force; +{ + Fore_unit *fup; + + if (kdc->kdc_unit < fore_nunits) { + + fup = fore_units[kdc->kdc_unit]; + if (fup != NULL) { + fore_reset(fup); + } + } + + (void) dev_detach(kdc); + return (0); +} +#else +/* + * Device shutdown routine + * + * Arguments: + * howto type of shutdown + * fup pointer to device unit structure + * + * Returns: + * none + * + */ +static void +fore_pci_shutdown(howto, fup) + int howto; + void *fup; +{ + + fore_reset((Fore_unit *) fup); + + return; +} +#endif /* BSD < 199506 */ +#endif /* __FreeBSD__ */ + + +/* + * Device unattach routine + * + * Reset the physical device, remove any pending timeouts, + * unmap any register sets, and unregister any interrupts. + * + * Arguments: + * fup pointer to device unit structure + * + * Returns: + * none + */ +static void +fore_unattach(fup) + Fore_unit *fup; +{ +#ifdef sun + struct dev_info *devinfo_p = fup->fu_devinfo; + struct dev_reg *dev_reg_p; + struct dev_intr *dev_intr_p; +#endif + int i; + + + /* + * Reset the board and return it to cold_start state. + * Hopefully, this will prevent use of resources as + * we're trying to free things up. + */ + fore_reset(fup); + + /* + * Lock out all device interrupts + */ + DEVICE_LOCK((Cmn_unit *)fup); + + /* + * Remove any pending timeout()'s + */ + (void)untimeout((KTimeout_ret(*) __P((void *)))fore_initialize, + (void *)fup, fup->fu_thandle); + +#ifdef sun + /* + * Remove any mappings of the device + */ + for ( dev_reg_p = devinfo_p->devi_reg, i = 1; + i <= devinfo_p->devi_nreg; i++, ++dev_reg_p ) + { + if ( dev_reg_p == NULL ) + { + /* + * Can't happen... + */ + break; + } + + /* + * Each device type has different register sets + */ + switch (fup->fu_config.ac_device) { + +#ifdef FORE_SBUS + case DEV_FORE_SBA200E: + + switch ( i ) + { + /* + * Host Control Register (HCR) + */ + case 1: + unmap_regs((addr_t)fup->fu_ctlreg, + sizeof(Fore_reg)); + break; + + /* + * SBus Burst Transfer Configuration Register + */ + case 2: + /* + * Not used + */ + break; + + /* + * SBus Interrupt Level Select Register + */ + case 3: + unmap_regs((addr_t)fup->fu_intlvl, + sizeof(Fore_reg)); + break; + + /* + * i960 RAM + */ + case 4: + unmap_regs((addr_t)fup->fu_ram, + fup->fu_ramsize); + break; + } + break; + + case DEV_FORE_SBA200: + + switch ( i ) + { + /* + * Board Control Register (BCR) + */ + case 1: + unmap_regs((addr_t)fup->fu_ctlreg, + sizeof(Fore_reg)); + break; + + /* + * i960 RAM + */ + case 2: + unmap_regs((addr_t)fup->fu_ram, + fup->fu_ramsize); + break; + } + break; +#endif /* FORE_SBUS */ + } + } + + /* + * Remove the interrupt vector(s) + */ + dev_intr_p = devinfo_p->devi_intr; + for ( i = devinfo_p->devi_nintr; i > 0; --i, ++dev_intr_p ) + { + if ( dev_intr_p == NULL ) + { + /* + * Can't happen... + */ + break; + } + (void) remintr ( dev_intr_p->int_pri, fore_poll ); + } +#endif /* sun */ + +#ifdef __FreeBSD__ + /* + * Unmap the device interrupt + */ + (void) pci_unmap_int(fup->fu_pcitag); + + /* + * Unmap memory + */ +#ifdef notdef + (void) pci_unmap_mem(fup->fu_pcitag, PCA200E_PCI_MEMBASE); +#endif +#endif /* __FreeBSD__ */ + + DEVICE_UNLOCK((Cmn_unit *)fup); +} + + +/* + * Device reset routine + * + * Reset the physical device + * + * Arguments: + * fup pointer to device unit structure + * + * Returns: + * none + */ +static void +fore_reset(fup) + Fore_unit *fup; +{ + int s = splimp(); + + /* + * Reset the board and return it to cold_start state + */ + if (fup->fu_mon) + fup->fu_mon->mon_bstat = CP_WRITE(BOOT_COLDSTART); + + if (fup->fu_ctlreg) { + + switch (fup->fu_config.ac_device) { + +#ifdef FORE_SBUS + case DEV_FORE_SBA200E: + /* + * Reset i960 by setting and clearing RESET + */ + SBA200E_HCR_INIT(*fup->fu_ctlreg, SBA200E_RESET); + SBA200E_HCR_CLR(*fup->fu_ctlreg, SBA200E_RESET); + break; + + case DEV_FORE_SBA200: + /* + * Reset i960 by setting and clearing RESET + * + * SBA200 will NOT reset if bit is OR'd in! + */ + *fup->fu_ctlreg = SBA200_RESET; + *fup->fu_ctlreg = SBA200_RESET_CLR; + break; +#endif /* FORE_SBUS */ +#ifdef FORE_PCI + case DEV_FORE_PCA200E: + /* + * Reset i960 by setting and clearing RESET + */ + PCA200E_HCR_INIT(*fup->fu_ctlreg, PCA200E_RESET); + DELAY(10000); + PCA200E_HCR_CLR(*fup->fu_ctlreg, PCA200E_RESET); + break; + +#endif + } + } + + (void) splx(s); + return; +} + + +#ifndef ATM_LINKED +/* + ******************************************************************* + * + * Loadable Module Support + * + ******************************************************************* + */ + +/* + * Generic module load processing + * + * This function is called by an OS-specific function when this + * module is being loaded. + * + * Arguments: + * none + * + * Returns: + * 0 load was successful + * errno load failed - reason indicated + * + */ +static int +fore_doload() +{ + int err = 0; + + /* + * Start us up + */ + err = fore_start(); + if (err) + /* Problems, clean up */ + (void)fore_stop(); + + return (err); +} + + +/* + * Generic module unload processing + * + * This function is called by an OS-specific function when this + * module is being unloaded. + * + * Arguments: + * none + * + * Returns: + * 0 unload was successful + * errno unload failed - reason indicated + * + */ +static int +fore_dounload() +{ + int err = 0; + + /* + * OK, try to clean up our mess + */ + err = fore_stop(); + + return (err); +} + + +#ifdef sun +/* + * Loadable driver description + */ +static struct vdldrv fore_drv = { + VDMAGIC_DRV, /* Device Driver */ + "fore_mod", /* name */ + &fore_ops, /* dev_ops */ + NULL, /* bdevsw */ + NULL, /* cdevsw */ + 0, /* blockmajor */ + 0 /* charmajor */ +}; + + +/* + * Loadable module support entry point + * + * This is the routine called by the vd driver for all loadable module + * functions for this pseudo driver. This routine name must be specified + * on the modload(1) command. This routine will be called whenever the + * modload(1), modunload(1) or modstat(1) commands are issued for this + * module. + * + * Arguments: + * cmd vd command code + * vdp pointer to vd driver's structure + * vdi pointer to command-specific vdioctl_* structure + * vds pointer to status structure (VDSTAT only) + * + * Returns: + * 0 command was successful + * errno command failed - reason indicated + * + */ +int +fore_mod(cmd, vdp, vdi, vds) + int cmd; + struct vddrv *vdp; + caddr_t vdi; + struct vdstat *vds; +{ + int err = 0; + + switch (cmd) { + + case VDLOAD: + /* + * Module Load + * + * We dont support any user configuration + */ + err = fore_doload(); + if (err == 0) + /* Let vd driver know about us */ + vdp->vdd_vdtab = (struct vdlinkage *)&fore_drv; + break; + + case VDUNLOAD: + /* + * Module Unload + */ + err = fore_dounload(); + break; + + case VDSTAT: + /* + * Module Status + */ + + /* Not much to say at the moment */ + + break; + + default: + log(LOG_ERR, "fore_mod: Unknown vd command 0x%x\n", cmd); + err = EINVAL; + } + + return (err); +} +#endif /* sun */ + +#ifdef __FreeBSD__ +#ifdef notdef + +/* + * Driver entry points + */ +static struct cdevsw fore_cdev = { + (d_open_t *)enodev, /* open */ + (d_close_t *)enodev, /* close */ + NULL, /* read */ + NULL, /* write */ + NULL, /* ioctl */ + NULL, /* stop */ + NULL, /* reset */ + NULL, /* devtotty */ + NULL, /* select */ + NULL, /* mmap */ + NULL /* strategy */ +}; + + +/* + * Loadable device driver module description + */ +#if BSD < 199506 +MOD_DEV("fore_mod", LM_DT_CHAR, -1, (void *)&fore_cdev); +#else +MOD_DEV(fore, LM_DT_CHAR, -1, (void *)&fore_cdev); +#endif + + +/* + * Loadable module support "load" entry point + * + * This is the routine called by the lkm driver whenever the + * modload(1) command is issued for this module. + * + * Arguments: + * lkmtp pointer to lkm drivers's structure + * cmd lkm command code + * + * Returns: + * 0 command was successful + * errno command failed - reason indicated + * + */ +static int +fore_load(lkmtp, cmd) + struct lkm_table *lkmtp; + int cmd; +{ + return(fore_doload()); +} + + +/* + * Loadable module support "unload" entry point + * + * This is the routine called by the lkm driver whenever the + * modunload(1) command is issued for this module. + * + * Arguments: + * lkmtp pointer to lkm drivers's structure + * cmd lkm command code + * + * Returns: + * 0 command was successful + * errno command failed - reason indicated + * + */ +static int +fore_unload(lkmtp, cmd) + struct lkm_table *lkmtp; + int cmd; +{ + return(fore_dounload()); +} + + +/* + * Loadable module support entry point + * + * This is the routine called by the lkm driver for all loadable module + * functions for this driver. This routine name must be specified + * on the modload(1) command. This routine will be called whenever the + * modload(1), modunload(1) or modstat(1) commands are issued for this + * module. + * + * Arguments: + * lkmtp pointer to lkm drivers's structure + * cmd lkm command code + * ver lkm version + * + * Returns: + * 0 command was successful + * errno command failed - reason indicated + * + */ +int +fore_mod(lkmtp, cmd, ver) + struct lkm_table *lkmtp; + int cmd; + int ver; +{ +#if BSD < 199506 + DISPATCH(lkmtp, cmd, ver, fore_load, fore_unload, nosys); +#else + DISPATCH(lkmtp, cmd, ver, fore_load, fore_unload, lkm_nullcmd); +#endif +} +#endif /* notdef */ +#endif /* __FreeBSD__ */ + +#endif /* ATM_LINKED */ + |