diff options
author | Theo de Raadt <deraadt@cvs.openbsd.org> | 1995-10-18 08:53:40 +0000 |
---|---|---|
committer | Theo de Raadt <deraadt@cvs.openbsd.org> | 1995-10-18 08:53:40 +0000 |
commit | d6583bb2a13f329cf0332ef2570eb8bb8fc0e39c (patch) | |
tree | ece253b876159b39c620e62b6c9b1174642e070e /sys/dev/ic/pdq.c |
initial import of NetBSD tree
Diffstat (limited to 'sys/dev/ic/pdq.c')
-rw-r--r-- | sys/dev/ic/pdq.c | 1549 |
1 files changed, 1549 insertions, 0 deletions
diff --git a/sys/dev/ic/pdq.c b/sys/dev/ic/pdq.c new file mode 100644 index 00000000000..27001b0890a --- /dev/null +++ b/sys/dev/ic/pdq.c @@ -0,0 +1,1549 @@ +/* $NetBSD: pdq.c,v 1.2 1995/08/19 04:35:18 cgd Exp $ */ + +/*- + * Copyright (c) 1995 Matt Thomas (matt@lkg.dec.com) + * 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. The name of the author may not be used to endorse or promote products + * derived from this software withough 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. + */ + +/* + * DEC PDQ FDDI Controller O/S independent code + * + * Written by Matt Thomas <matt@lkg.dec.com> + * + * This module should work any PDQ based board. Note that changes for + * MIPS and Alpha architectures (or any other architecture which requires + * a flushing of memory or write buffers and/or has incoherent caches) + * have yet to be made. + */ + +#define PDQ_HWSUPPORT /* for pdq.h */ + +#include "pdqreg.h" +#ifndef __NetBSD__ +#include "pdq_os.h" +#else +#include "pdqvar.h" +#endif + +#define PDQ_ROUNDUP(n, x) (((n) + ((x) - 1)) & ~((x) - 1)) +#define PDQ_CMD_RX_ALIGNMENT 16 + +#if defined(PDQTEST) && !defined(PDQ_NOPRINTF) +#define PDQ_PRINTF(x) printf x +#else +#define PDQ_PRINTF(x) do { } while (0) +#endif + +const char * const pdq_halt_codes[] = { + "Selftest Timeout", "Host Bus Parity Error", "Host Directed Fault", + "Software Fault", "Hardware Fault", "PC Trace Path Test", + "DMA Error", "Image CRC Error", "Adapter Processer Error" +}; + +const char * const pdq_adapter_states[] = { + "Reset", "Upgrade", "DMA Unavailable", "DMA Available", + "Link Available", "Link Unavailable", "Halted", "Ring Member" +}; + +/* + * The following are used in conjunction with + * unsolicited events + */ +const char * const pdq_entities[] = { + "Station", "Link", "Phy Port" +}; + +const char * const pdq_station_events[] = { + "Trace Received" +}; + +const char * const pdq_station_arguments[] = { + "Reason" +}; + +const char * const pdq_link_events[] = { + "Transmit Underrun", + "Transmit Failed", + "Block Check Error (CRC)", + "Frame Status Error", + "PDU Length Error", + NULL, + NULL, + "Receive Data Overrun", + NULL, + "No User Buffer", + "Ring Initialization Initiated", + "Ring Initialization Received", + "Ring Beacon Initiated", + "Duplicate Address Failure", + "Duplicate Token Detected", + "Ring Purger Error", + "FCI Strip Error", + "Trace Initiated", + "Directed Beacon Received", +}; + +const char * const pdq_link_arguments[] = { + "Reason", + "Data Link Header", + "Source", + "Upstream Neighbor" +}; + +const char * const pdq_phy_events[] = { + "LEM Error Monitor Reject", + "Elasticy Buffer Error", + "Link Confidence Test Reject" +}; + +const char * const pdq_phy_arguments[] = { + "Direction" +}; + +const char * const * const pdq_event_arguments[] = { + pdq_station_arguments, + pdq_link_arguments, + pdq_phy_arguments +}; + +const char * const * const pdq_event_codes[] = { + pdq_station_events, + pdq_link_events, + pdq_phy_events +}; + +const char * const pdq_station_types[] = { + "SAS", "DAC", "SAC", "NAC", "DAS" +}; + +const char * const pdq_smt_versions[] = { "", "V6.2", "V7.2", "V7.3" }; + +const char pdq_phy_types[] = "ABSM"; + +const char * const pdq_pmd_types0[] = { + "ANSI Multi-Mode", "ANSI Single-Mode Type 1", "ANSI Single-Mode Type 2", + "ANSI Sonet" +}; + +const char * const pdq_pmd_types100[] = { + "Low Power", "Thin Wire", "Shielded Twisted Pair", + "Unshielded Twisted Pair" +}; + +const char * const * const pdq_pmd_types[] = { + pdq_pmd_types0, pdq_pmd_types100 +}; + +const char * const pdq_descriptions[] = { + "DEFPA PCI", + "DEFEA EISA", +}; + +void +pdq_print_fddi_chars( + pdq_t *pdq, + const pdq_response_status_chars_get_t *rsp) +{ + const char hexchars[] = "0123456789abcdef"; + + printf( +#if !defined(__bsdi__) && !defined(__NetBSD__) + PDQ_OS_PREFIX +#else + ": " +#endif + "DEC %s FDDI %s Controller\n", +#if !defined(__bsdi__) && !defined(__NetBSD__) + PDQ_OS_PREFIX_ARGS, +#endif + pdq_descriptions[pdq->pdq_type], + pdq_station_types[rsp->status_chars_get.station_type]); + + printf(PDQ_OS_PREFIX "FDDI address %c%c:%c%c:%c%c:%c%c:%c%c:%c%c, FW=%c%c%c%c, HW=%c", + PDQ_OS_PREFIX_ARGS, + hexchars[pdq->pdq_hwaddr.lanaddr_bytes[0] >> 4], + hexchars[pdq->pdq_hwaddr.lanaddr_bytes[0] & 0x0F], + hexchars[pdq->pdq_hwaddr.lanaddr_bytes[1] >> 4], + hexchars[pdq->pdq_hwaddr.lanaddr_bytes[1] & 0x0F], + hexchars[pdq->pdq_hwaddr.lanaddr_bytes[2] >> 4], + hexchars[pdq->pdq_hwaddr.lanaddr_bytes[2] & 0x0F], + hexchars[pdq->pdq_hwaddr.lanaddr_bytes[3] >> 4], + hexchars[pdq->pdq_hwaddr.lanaddr_bytes[3] & 0x0F], + hexchars[pdq->pdq_hwaddr.lanaddr_bytes[4] >> 4], + hexchars[pdq->pdq_hwaddr.lanaddr_bytes[4] & 0x0F], + hexchars[pdq->pdq_hwaddr.lanaddr_bytes[5] >> 4], + hexchars[pdq->pdq_hwaddr.lanaddr_bytes[5] & 0x0F], + pdq->pdq_fwrev.fwrev_bytes[0], pdq->pdq_fwrev.fwrev_bytes[1], + pdq->pdq_fwrev.fwrev_bytes[2], pdq->pdq_fwrev.fwrev_bytes[3], + rsp->status_chars_get.module_rev.fwrev_bytes[0]); + + if (rsp->status_chars_get.smt_version_id < PDQ_ARRAY_SIZE(pdq_smt_versions)) { + printf(", SMT %s\n", pdq_smt_versions[rsp->status_chars_get.smt_version_id]); + } + + printf(PDQ_OS_PREFIX "FDDI Port%s = %c (PMD = %s)", + PDQ_OS_PREFIX_ARGS, + rsp->status_chars_get.station_type == PDQ_STATION_TYPE_DAS ? "[A]" : "", + pdq_phy_types[rsp->status_chars_get.phy_type[0]], + pdq_pmd_types[rsp->status_chars_get.pmd_type[0] / 100][rsp->status_chars_get.pmd_type[0] % 100]); + + if (rsp->status_chars_get.station_type == PDQ_STATION_TYPE_DAS) + printf(", FDDI Port[B] = %c (PMD = %s)", + pdq_phy_types[rsp->status_chars_get.phy_type[1]], + pdq_pmd_types[rsp->status_chars_get.pmd_type[1] / 100][rsp->status_chars_get.pmd_type[1] % 100]); + + printf("\n"); +} + +void +pdq_init_csrs( + pdq_csrs_t *csrs, + void *csr_va, + size_t csrsize) +{ + volatile pdq_uint32_t *csr_base = (volatile pdq_uint32_t *) csr_va; + + csrs->csr_port_reset = &csr_base[0 * csrsize]; + csrs->csr_host_data = &csr_base[1 * csrsize]; + csrs->csr_port_control = &csr_base[2 * csrsize]; + csrs->csr_port_data_a = &csr_base[3 * csrsize]; + csrs->csr_port_data_b = &csr_base[4 * csrsize]; + csrs->csr_port_status = &csr_base[5 * csrsize]; + csrs->csr_host_int_type_0 = &csr_base[6 * csrsize]; + csrs->csr_host_int_enable = &csr_base[7 * csrsize]; + csrs->csr_type_2_producer = &csr_base[8 * csrsize]; + csrs->csr_cmd_response_producer = &csr_base[10 * csrsize]; + csrs->csr_cmd_request_producer = &csr_base[11 * csrsize]; + csrs->csr_host_smt_producer = &csr_base[12 * csrsize]; + csrs->csr_unsolicited_producer = &csr_base[13 * csrsize]; +} + +void +pdq_init_pci_csrs( + pdq_pci_csrs_t *csrs, + void *csr_va, + size_t csrsize) +{ + volatile pdq_uint32_t *csr_base = (volatile pdq_uint32_t *) csr_va; + + csrs->csr_pfi_mode_control = &csr_base[16 * csrsize]; + csrs->csr_pfi_status = &csr_base[17 * csrsize]; + csrs->csr_fifo_write = &csr_base[18 * csrsize]; + csrs->csr_fifo_read = &csr_base[19 * csrsize]; +} + +void +pdq_flush_databuf_queue( + pdq_databuf_queue_t *q) +{ + PDQ_OS_DATABUF_T *pdu; + for (;;) { + PDQ_OS_DATABUF_DEQUEUE(q, pdu); + if (pdu == NULL) + return; + PDQ_OS_DATABUF_FREE(pdu); + } +} + +pdq_boolean_t +pdq_do_port_control( + const pdq_csrs_t * const csrs, + pdq_uint32_t cmd) +{ + int cnt = 0; + *csrs->csr_host_int_type_0 = PDQ_HOST_INT_CSR_CMD_DONE; + *csrs->csr_port_control = PDQ_PCTL_CMD_ERROR | cmd; + while ((*csrs->csr_host_int_type_0 & PDQ_HOST_INT_CSR_CMD_DONE) == 0 && cnt < 33000000) + cnt++; + PDQ_PRINTF(("CSR cmd spun %d times\n", cnt)); + if (*csrs->csr_host_int_type_0 & PDQ_HOST_INT_CSR_CMD_DONE) { + *csrs->csr_host_int_type_0 = PDQ_HOST_INT_CSR_CMD_DONE; + return (*csrs->csr_port_control & PDQ_PCTL_CMD_ERROR) ? PDQ_FALSE : PDQ_TRUE; + } + /* adapter failure */ + PDQ_ASSERT(0); + return PDQ_FALSE; +} + +void +pdq_read_mla( + const pdq_csrs_t * const csrs, + pdq_lanaddr_t *hwaddr) +{ + pdq_uint32_t data; + + *csrs->csr_port_data_a = 0; + pdq_do_port_control(csrs, PDQ_PCTL_MLA_READ); + data = *csrs->csr_host_data; + + hwaddr->lanaddr_bytes[0] = (data >> 0) & 0xFF; + hwaddr->lanaddr_bytes[1] = (data >> 8) & 0xFF; + hwaddr->lanaddr_bytes[2] = (data >> 16) & 0xFF; + hwaddr->lanaddr_bytes[3] = (data >> 24) & 0xFF; + + *csrs->csr_port_data_a = 1; + pdq_do_port_control(csrs, PDQ_PCTL_MLA_READ); + data = *csrs->csr_host_data; + + hwaddr->lanaddr_bytes[4] = (data >> 0) & 0xFF; + hwaddr->lanaddr_bytes[5] = (data >> 8) & 0xFF; +} + +void +pdq_read_fwrev( + const pdq_csrs_t * const csrs, + pdq_fwrev_t *fwrev) +{ + pdq_uint32_t data; + + pdq_do_port_control(csrs, PDQ_PCTL_FW_REV_READ); + data = *csrs->csr_host_data; + + fwrev->fwrev_bytes[3] = (data >> 0) & 0xFF; + fwrev->fwrev_bytes[2] = (data >> 8) & 0xFF; + fwrev->fwrev_bytes[1] = (data >> 16) & 0xFF; + fwrev->fwrev_bytes[0] = (data >> 24) & 0xFF; +} + +pdq_boolean_t +pdq_read_error_log( + pdq_t *pdq, + pdq_response_error_log_get_t *log_entry) +{ + const pdq_csrs_t * const csrs = &pdq->pdq_csrs; + pdq_uint32_t *ptr = (pdq_uint32_t *) log_entry; + + pdq_do_port_control(csrs, PDQ_PCTL_ERROR_LOG_START); + + while (pdq_do_port_control(csrs, PDQ_PCTL_FW_REV_READ) == PDQ_TRUE) { + *ptr++ = *csrs->csr_host_data; + if ((pdq_uint8_t *) ptr - (pdq_uint8_t *) log_entry == sizeof(*log_entry)) + break; + } + return (ptr == (pdq_uint32_t *) log_entry) ? PDQ_FALSE : PDQ_TRUE; +} + +pdq_chip_rev_t +pdq_read_chiprev( + const pdq_csrs_t * const csrs) +{ + pdq_uint32_t data; + + *csrs->csr_port_data_a = PDQ_SUB_CMD_PDQ_REV_GET; + pdq_do_port_control(csrs, PDQ_PCTL_SUB_CMD); + data = *csrs->csr_host_data; + + return (pdq_chip_rev_t) data; +} + +static const struct { + size_t cmd_len; + size_t rsp_len; + const char *cmd_name; +} pdq_cmd_info[] = { + { sizeof(pdq_cmd_generic_t), /* 0 - PDQC_START */ + sizeof(pdq_response_generic_t), + "Start" + }, + { sizeof(pdq_cmd_filter_set_t), /* 1 - PDQC_FILTER_SET */ + sizeof(pdq_response_generic_t), + "Filter Set" + }, + { sizeof(pdq_cmd_generic_t), /* 2 - PDQC_FILTER_GET */ + sizeof(pdq_response_filter_get_t), + "Filter Get" + }, + { sizeof(pdq_cmd_chars_set_t), /* 3 - PDQC_CHARS_SET */ + sizeof(pdq_response_generic_t), + "Chars Set" + }, + { sizeof(pdq_cmd_generic_t), /* 4 - PDQC_STATUS_CHARS_GET */ + sizeof(pdq_response_status_chars_get_t), + "Status Chars Get" + }, +#if 0 + { sizeof(pdq_cmd_generic_t), /* 5 - PDQC_COUNTERS_GET */ + sizeof(pdq_response_counters_get_t), + "Counters Get" + }, + { sizeof(pdq_cmd_counters_set_t), /* 6 - PDQC_COUNTERS_SET */ + sizeof(pdq_response_generic_t), + "Counters Set" + }, +#else + { 0, 0, "Counters Get" }, + { 0, 0, "Counters Set" }, +#endif + { sizeof(pdq_cmd_addr_filter_set_t), /* 7 - PDQC_ADDR_FILTER_SET */ + sizeof(pdq_response_generic_t), + "Addr Filter Set" + }, + { sizeof(pdq_cmd_generic_t), /* 8 - PDQC_ADDR_FILTER_GET */ + sizeof(pdq_response_addr_filter_get_t), + "Addr Filter Get" + }, +#if 0 + { sizeof(pdq_cmd_generic_t), /* 9 - PDQC_ERROR_LOG_CLEAR */ + sizeof(pdq_response_generic_t), + "Error Log Clear" + }, + { sizeof(pdq_cmd_generic_t), /* 10 - PDQC_ERROR_LOG_SET */ + sizeof(pdq_response_generic_t), + "Error Log Set" + }, + { sizeof(pdq_cmd_generic_t), /* 11 - PDQC_FDDI_MIB_GET */ + sizeof(pdq_response_generic_t), + "FDDI MIB Get" + }, + { sizeof(pdq_cmd_generic_t), /* 12 - PDQC_DEC_EXT_MIB_GET */ + sizeof(pdq_response_generic_t), + "DEC Ext MIB Get" + }, + { sizeof(pdq_cmd_generic_t), /* 13 - PDQC_DEC_SPECIFIC_GET */ + sizeof(pdq_response_generic_t), + "DEC Specific Get" + }, + { sizeof(pdq_cmd_generic_t), /* 14 - PDQC_SNMP_SET */ + sizeof(pdq_response_generic_t), + "SNMP Set" + }, + { 0, 0, "N/A" }, + { sizeof(pdq_cmd_generic_t), /* 16 - PDQC_SMT_MIB_GET */ + sizeof(pdq_response_generic_t), + "SMT MIB Get" + }, + { sizeof(pdq_cmd_generic_t), /* 17 - PDQC_SMT_MIB_SET */ + sizeof(pdq_response_generic_t), + "SMT MIB Set", + }, +#endif +}; + +void +pdq_queue_commands( + pdq_t *pdq) +{ + const pdq_csrs_t * const csrs = &pdq->pdq_csrs; + pdq_command_info_t * const ci = &pdq->pdq_command_info; + pdq_descriptor_block_t * const dbp = pdq->pdq_dbp; + pdq_cmd_code_t op; + pdq_uint32_t cmdlen, rsplen, mask; + + /* + * If there are commands or responses active or there aren't + * any pending commands, then don't queue any more. + */ + if (ci->ci_command_active || ci->ci_pending_commands == 0) + return; + + /* + * Determine which command needs to be queued. + */ + op = PDQC_SMT_MIB_SET; + for (mask = 1 << ((int) op); (mask & ci->ci_pending_commands) == 0; mask >>= 1) + op = (pdq_cmd_code_t) ((int) op - 1); + /* + * Obtain the sizes needed for the command and response. + * Round up to PDQ_CMD_RX_ALIGNMENT so the receive buffer is + * always properly aligned. + */ + cmdlen = PDQ_ROUNDUP(pdq_cmd_info[op].cmd_len, PDQ_CMD_RX_ALIGNMENT); + rsplen = PDQ_ROUNDUP(pdq_cmd_info[op].rsp_len, PDQ_CMD_RX_ALIGNMENT); + if (cmdlen < rsplen) + cmdlen = rsplen; + /* + * Since only one command at a time will be queued, there will always + * be enough space. + */ + + /* + * Obtain and fill in the descriptor for the command (descriptor is + * pre-initialized) + */ + dbp->pdqdb_command_requests[ci->ci_request_producer].txd_seg_len = cmdlen; + PDQ_ADVANCE(ci->ci_request_producer, 1, PDQ_RING_MASK(dbp->pdqdb_command_requests)); + + /* + * Obtain and fill in the descriptor for the response (descriptor is + * pre-initialized) + */ + dbp->pdqdb_command_responses[ci->ci_response_producer].rxd_seg_len_hi = cmdlen / 16; + PDQ_ADVANCE(ci->ci_response_producer, 1, PDQ_RING_MASK(dbp->pdqdb_command_responses)); + + /* + * Clear the command area, set the opcode, and the command from the pending + * mask. + */ + + PDQ_OS_MEMZERO(ci->ci_bufstart, cmdlen); + *(pdq_cmd_code_t *) ci->ci_bufstart = op; + ci->ci_pending_commands &= ~mask; + + /* + * Fill in the command area, if needed. + */ + switch (op) { + case PDQC_FILTER_SET: { + pdq_cmd_filter_set_t *filter_set = (pdq_cmd_filter_set_t *) ci->ci_bufstart; + unsigned idx = 0; + filter_set->filter_set_items[idx].item_code = PDQI_IND_GROUP_PROM; + filter_set->filter_set_items[idx].filter_state = (pdq->pdq_flags & PDQ_PROMISC ? PDQ_FILTER_PASS : PDQ_FILTER_BLOCK); + idx++; + filter_set->filter_set_items[idx].item_code = PDQI_GROUP_PROM; + filter_set->filter_set_items[idx].filter_state = (pdq->pdq_flags & PDQ_ALLMULTI ? PDQ_FILTER_PASS : PDQ_FILTER_BLOCK); + idx++; + filter_set->filter_set_items[idx].item_code = PDQI_SMT_PROM; + filter_set->filter_set_items[idx].filter_state = ((pdq->pdq_flags & (PDQ_PROMISC|PDQ_PASS_SMT)) == (PDQ_PROMISC|PDQ_PASS_SMT) ? PDQ_FILTER_PASS : PDQ_FILTER_BLOCK); + idx++; + filter_set->filter_set_items[idx].item_code = PDQI_SMT_USER; + filter_set->filter_set_items[idx].filter_state = (pdq->pdq_flags & PDQ_PASS_SMT ? PDQ_FILTER_PASS : PDQ_FILTER_BLOCK); + idx++; + filter_set->filter_set_items[idx].item_code = PDQI_EOL; + break; + } + case PDQC_ADDR_FILTER_SET: { + pdq_cmd_addr_filter_set_t *addr_filter_set = (pdq_cmd_addr_filter_set_t *) ci->ci_bufstart; + pdq_lanaddr_t *addr = addr_filter_set->addr_filter_set_addresses; + addr->lanaddr_bytes[0] = 0xFF; + addr->lanaddr_bytes[1] = 0xFF; + addr->lanaddr_bytes[2] = 0xFF; + addr->lanaddr_bytes[3] = 0xFF; + addr->lanaddr_bytes[4] = 0xFF; + addr->lanaddr_bytes[5] = 0xFF; + addr++; + pdq_os_addr_fill(pdq, addr, 61); + break; + } + } + /* + * At this point the command is done. All that needs to be done is to + * produce it to the PDQ. + */ + PDQ_PRINTF(("PDQ Queue Command Request: %s queued\n", + pdq_cmd_info[op].cmd_name)); + + ci->ci_command_active++; + *csrs->csr_cmd_response_producer = ci->ci_response_producer | (ci->ci_response_completion << 8); + *csrs->csr_cmd_request_producer = ci->ci_request_producer | (ci->ci_request_completion << 8); +} + +void +pdq_process_command_responses( + pdq_t * const pdq) +{ + const pdq_csrs_t * const csrs = &pdq->pdq_csrs; + pdq_command_info_t * const ci = &pdq->pdq_command_info; + volatile const pdq_consumer_block_t * const cbp = pdq->pdq_cbp; + pdq_descriptor_block_t * const dbp = pdq->pdq_dbp; + pdq_txdesc_t *txd; + pdq_rxdesc_t *rxd; + const pdq_response_generic_t *rspgen; + + /* + * We have to process the command and response in tandem so + * just wait for the response to be consumed. If it has been + * consumed then the command must have been as well. + */ + + if (cbp->pdqcb_command_response == ci->ci_response_completion) + return; + + PDQ_ASSERT (cbp->pdqcb_command_request != ci->ci_request_completion); + + txd = &dbp->pdqdb_command_requests[ci->ci_request_completion]; + rxd = &dbp->pdqdb_command_responses[ci->ci_response_completion]; + + rspgen = (const pdq_response_generic_t *) ci->ci_bufstart; + PDQ_ASSERT(rspgen->generic_status == PDQR_SUCCESS); + PDQ_PRINTF(("PDQ Process Command Response: %s completed\n", + pdq_cmd_info[rspgen->generic_op].cmd_name)); + + if (rspgen->generic_op == PDQC_STATUS_CHARS_GET && (pdq->pdq_flags & PDQ_PRINTCHARS)) { + pdq->pdq_flags &= ~PDQ_PRINTCHARS; + pdq_print_fddi_chars(pdq, (const pdq_response_status_chars_get_t *) rspgen); + } + + PDQ_ADVANCE(ci->ci_request_completion, 1, PDQ_RING_MASK(dbp->pdqdb_command_requests)); + PDQ_ADVANCE(ci->ci_response_completion, 1, PDQ_RING_MASK(dbp->pdqdb_command_responses)); + ci->ci_command_active = 0; + + if (ci->ci_pending_commands != 0) { + pdq_queue_commands(pdq); + } else { + *csrs->csr_cmd_response_producer = ci->ci_response_producer | (ci->ci_response_completion << 8); + *csrs->csr_cmd_request_producer = ci->ci_request_producer | (ci->ci_request_completion << 8); + } +} + +/* + * This following routine processes unsolicited events. + * In addition, it also fills the unsolicited queue with + * event buffers so it can be used to initialize the queue + * as well. + */ +void +pdq_process_unsolicited_events( + pdq_t *pdq) +{ + const pdq_csrs_t * const csrs = &pdq->pdq_csrs; + pdq_unsolicited_info_t *ui = &pdq->pdq_unsolicited_info; + volatile const pdq_consumer_block_t *cbp = pdq->pdq_cbp; + pdq_descriptor_block_t *dbp = pdq->pdq_dbp; + const pdq_unsolicited_event_t *event; + pdq_rxdesc_t *rxd; + + /* + * Process each unsolicited event (if any). + */ + + while (cbp->pdqcb_unsolicited_event != ui->ui_completion) { + rxd = &dbp->pdqdb_unsolicited_events[ui->ui_completion]; + event = &ui->ui_events[ui->ui_completion & (PDQ_NUM_UNSOLICITED_EVENTS-1)]; + + switch (event->event_type) { + case PDQ_UNSOLICITED_EVENT: { + printf(PDQ_OS_PREFIX "Unsolicited Event: %s: %s", + PDQ_OS_PREFIX_ARGS, + pdq_entities[event->event_entity], + pdq_event_codes[event->event_entity][event->event_code.value]); + if (event->event_type == PDQ_ENTITY_PHY_PORT) + printf("[%d]", event->event_index); + printf("\n"); + break; + } + case PDQ_UNSOLICITED_COUNTERS: { + break; + } + } + PDQ_ADVANCE(ui->ui_completion, 1, PDQ_RING_MASK(dbp->pdqdb_unsolicited_events)); + ui->ui_free++; + } + + /* + * Now give back the event buffers back to the PDQ. + */ + PDQ_ADVANCE(ui->ui_producer, ui->ui_free, PDQ_RING_MASK(dbp->pdqdb_unsolicited_events)); + ui->ui_free = 0; + + *csrs->csr_unsolicited_producer = ui->ui_producer | (ui->ui_completion << 8); +} + +void +pdq_process_received_data( + pdq_t *pdq, + pdq_rx_info_t *rx, + pdq_rxdesc_t *receives, + pdq_uint32_t completion_goal, + pdq_uint32_t ring_mask) +{ + pdq_uint32_t completion = rx->rx_completion; + pdq_uint32_t producer = rx->rx_producer; + PDQ_OS_DATABUF_T **buffers = (PDQ_OS_DATABUF_T **) rx->rx_buffers; + pdq_rxdesc_t *rxd; + pdq_uint32_t idx; + + while (completion != completion_goal) { + PDQ_OS_DATABUF_T *fpdu, *lpdu, *npdu; + pdq_uint8_t *dataptr; + pdq_uint32_t fc, datalen, pdulen, segcnt; + pdq_rxstatus_t status; + + fpdu = lpdu = buffers[completion]; + PDQ_ASSERT(fpdu != NULL); + + dataptr = PDQ_OS_DATABUF_PTR(fpdu); + status = *(pdq_rxstatus_t *) dataptr; + if ((status.rxs_status & 0x200000) == 0) { + datalen = status.rxs_status & 0x1FFF; + fc = dataptr[PDQ_RX_FC_OFFSET]; + switch (fc & (PDQ_FDDIFC_C|PDQ_FDDIFC_L|PDQ_FDDIFC_F)) { + case PDQ_FDDI_LLC_ASYNC: + case PDQ_FDDI_LLC_SYNC: + case PDQ_FDDI_IMP_ASYNC: + case PDQ_FDDI_IMP_SYNC: { + if (datalen > PDQ_FDDI_MAX || datalen < PDQ_FDDI_LLC_MIN) { + PDQ_PRINTF(("discard: bad length %d\n", datalen)); + goto discard_frame; + } + break; + } + case PDQ_FDDI_SMT: { + if (datalen > PDQ_FDDI_MAX || datalen < PDQ_FDDI_SMT_MIN) + goto discard_frame; + break; + } + default: { + PDQ_PRINTF(("discard: bad fc 0x%x\n", fc)); + goto discard_frame; + } + } + /* + * Update the lengths of the data buffers now that we know + * the real length. + */ + pdulen = datalen - 4 /* CRC */; + segcnt = (pdulen + PDQ_RX_FC_OFFSET + PDQ_OS_DATABUF_SIZE - 1) / PDQ_OS_DATABUF_SIZE; + PDQ_OS_DATABUF_ALLOC(npdu); + if (npdu == NULL) { + PDQ_PRINTF(("discard: no databuf #0\n")); + goto discard_frame; + } + buffers[completion] = npdu; + for (idx = 1; idx < segcnt; idx++) { + PDQ_OS_DATABUF_ALLOC(npdu); + if (npdu == NULL) { + PDQ_OS_DATABUF_NEXT_SET(lpdu, NULL); + PDQ_OS_DATABUF_FREE(fpdu); + goto discard_frame; + } + PDQ_OS_DATABUF_NEXT_SET(lpdu, buffers[(completion + idx) & ring_mask]); + lpdu = PDQ_OS_DATABUF_NEXT(lpdu); + buffers[(completion + idx) & ring_mask] = npdu; + } + PDQ_OS_DATABUF_NEXT_SET(lpdu, NULL); + for (idx = 0; idx < PDQ_RX_SEGCNT; idx++) { + buffers[(producer + idx) & ring_mask] = + buffers[(completion + idx) & ring_mask]; + buffers[(completion + idx) & ring_mask] = NULL; + } + PDQ_OS_DATABUF_ADJ(fpdu, PDQ_RX_FC_OFFSET); + if (segcnt == 1) { + PDQ_OS_DATABUF_LEN_SET(fpdu, pdulen); + } else { + PDQ_OS_DATABUF_LEN_SET(lpdu, pdulen + PDQ_RX_FC_OFFSET - (segcnt - 1) * PDQ_OS_DATABUF_SIZE); + } + pdq_os_receive_pdu(pdq, fpdu, pdulen); + rx->rx_free += PDQ_RX_SEGCNT; + PDQ_ADVANCE(producer, PDQ_RX_SEGCNT, ring_mask); + PDQ_ADVANCE(completion, PDQ_RX_SEGCNT, ring_mask); + continue; + } else { + PDQ_PRINTF(("discard: bad pdu 0x%x(%d.%d.%d.%d.%d)\n", status.rxs_status, + status.rxs_rcc_badpdu, status.rxs_rcc_badcrc, + status.rxs_rcc_reason, status.rxs_fsc, status.rxs_fsb_e)); + if (status.rxs_rcc_reason == 7) + goto discard_frame; + if (status.rxs_rcc_reason != 0) + /* hardware fault */ + if (status.rxs_rcc_badcrc) { + printf(PDQ_OS_PREFIX " MAC CRC error (source=%x-%x-%x-%x-%x-%x)\n", + PDQ_OS_PREFIX_ARGS, + dataptr[PDQ_RX_FC_OFFSET+1], + dataptr[PDQ_RX_FC_OFFSET+2], + dataptr[PDQ_RX_FC_OFFSET+3], + dataptr[PDQ_RX_FC_OFFSET+4], + dataptr[PDQ_RX_FC_OFFSET+5], + dataptr[PDQ_RX_FC_OFFSET+6]); + /* rx->rx_badcrc++; */ + } else if (status.rxs_fsc == 0 | status.rxs_fsb_e == 1) { + /* rx->rx_frame_status_errors++; */ + } else { + /* hardware fault */ + } + } + discard_frame: + /* + * Discarded frames go right back on the queue; therefore + * ring entries were freed. + */ + for (idx = 0; idx < PDQ_RX_SEGCNT; idx++) { + buffers[producer] = buffers[completion]; + buffers[completion] = NULL; + rxd = &receives[rx->rx_producer]; + if (idx == 0) { + rxd->rxd_sop = 1; rxd->rxd_seg_cnt = PDQ_RX_SEGCNT - 1; + } else { + rxd->rxd_sop = 0; rxd->rxd_seg_cnt = 0; + } + rxd->rxd_pa_hi = 0; + rxd->rxd_seg_len_hi = PDQ_OS_DATABUF_SIZE / 16; + rxd->rxd_pa_lo = PDQ_OS_VA_TO_PA(PDQ_OS_DATABUF_PTR(buffers[rx->rx_producer])); + PDQ_ADVANCE(rx->rx_producer, 1, ring_mask); + PDQ_ADVANCE(producer, 1, ring_mask); + PDQ_ADVANCE(completion, 1, ring_mask); + } + } + rx->rx_completion = completion; + + while (rx->rx_free > PDQ_RX_SEGCNT && rx->rx_free > rx->rx_target) { + PDQ_OS_DATABUF_T *pdu; + /* + * Allocate the needed number of data buffers. + * Try to obtain them from our free queue before + * asking the system for more. + */ + for (idx = 0; idx < PDQ_RX_SEGCNT; idx++) { + if ((pdu = buffers[(rx->rx_producer + idx) & ring_mask]) == NULL) { + PDQ_OS_DATABUF_ALLOC(pdu); + if (pdu == NULL) + break; + buffers[(rx->rx_producer + idx) & ring_mask] = pdu; + } + rxd = &receives[(rx->rx_producer + idx) & ring_mask]; + if (idx == 0) { + rxd->rxd_sop = 1; rxd->rxd_seg_cnt = PDQ_RX_SEGCNT - 1; + } else { + rxd->rxd_sop = 0; rxd->rxd_seg_cnt = 0; + } + rxd->rxd_pa_hi = 0; + rxd->rxd_seg_len_hi = PDQ_OS_DATABUF_SIZE / 16; + rxd->rxd_pa_lo = PDQ_OS_VA_TO_PA(PDQ_OS_DATABUF_PTR(pdu)); + } + if (idx < PDQ_RX_SEGCNT) { + /* + * We didn't get all databufs required to complete a new + * receive buffer. Keep the ones we got and retry a bit + * later for the rest. + */ + break; + } + PDQ_ADVANCE(rx->rx_producer, PDQ_RX_SEGCNT, ring_mask); + rx->rx_free -= PDQ_RX_SEGCNT; + } +} + +pdq_boolean_t +pdq_queue_transmit_data( + pdq_t *pdq, + PDQ_OS_DATABUF_T *pdu) +{ + pdq_tx_info_t *tx = &pdq->pdq_tx_info; + pdq_descriptor_block_t *dbp = pdq->pdq_dbp; + pdq_uint32_t producer = tx->tx_producer; + pdq_txdesc_t *eop = NULL; + PDQ_OS_DATABUF_T *pdu0; + pdq_uint32_t freecnt; + + if (tx->tx_free < 1) + return PDQ_FALSE; + + dbp->pdqdb_transmits[producer] = tx->tx_hdrdesc; + PDQ_ADVANCE(producer, 1, PDQ_RING_MASK(dbp->pdqdb_transmits)); + + for (freecnt = tx->tx_free - 1, pdu0 = pdu; pdu0 != NULL && freecnt > 0;) { + pdq_uint32_t fraglen, datalen = PDQ_OS_DATABUF_LEN(pdu0); + const pdq_uint8_t *dataptr = PDQ_OS_DATABUF_PTR(pdu0); + + /* + * The first segment is limited to the space remaining in + * page. All segments after that can be up to a full page + * in size. + */ + fraglen = PDQ_OS_PAGESIZE - ((dataptr - (pdq_uint8_t *) NULL) & (PDQ_OS_PAGESIZE-1)); + while (datalen > 0 && freecnt > 0) { + pdq_uint32_t seglen = (fraglen < datalen ? fraglen : datalen); + + /* + * Initialize the transmit descriptor + */ + eop = &dbp->pdqdb_transmits[producer]; + eop->txd_seg_len = seglen; + eop->txd_pa_lo = PDQ_OS_VA_TO_PA(dataptr); + eop->txd_sop = eop->txd_eop = eop->txd_pa_hi = 0; + + datalen -= seglen; + dataptr += seglen; + fraglen = PDQ_OS_PAGESIZE; + freecnt--; + PDQ_ADVANCE(producer, 1, PDQ_RING_MASK(dbp->pdqdb_transmits)); + } + pdu0 = PDQ_OS_DATABUF_NEXT(pdu0); + } + if (pdu0 != NULL) { + PDQ_ASSERT(freecnt == 0); + /* + * If we still have data to process then the ring was too full + * to store the PDU. Return FALSE so the caller will requeue + * the PDU for later. + */ + return PDQ_FALSE; + } + /* + * Everything went fine. Finish it up. + */ + tx->tx_descriptor_count[tx->tx_producer] = tx->tx_free - freecnt; + eop->txd_eop = 1; + PDQ_OS_DATABUF_ENQUEUE(&tx->tx_txq, pdu); + tx->tx_producer = producer; + tx->tx_free = freecnt; + PDQ_DO_TYPE2_PRODUCER(pdq); + return PDQ_TRUE; +} + +void +pdq_process_transmitted_data( + pdq_t *pdq) +{ + pdq_tx_info_t *tx = &pdq->pdq_tx_info; + volatile const pdq_consumer_block_t *cbp = pdq->pdq_cbp; + pdq_descriptor_block_t *dbp = pdq->pdq_dbp; + pdq_uint32_t completion = tx->tx_completion; + + while (completion != cbp->pdqcb_transmits) { + PDQ_OS_DATABUF_T *pdu; + pdq_uint32_t descriptor_count = tx->tx_descriptor_count[completion]; + PDQ_ASSERT(dbp->pdqdb_transmits[completion].txd_sop == 1); + PDQ_ASSERT(dbp->pdqdb_transmits[(completion + descriptor_count - 1) & PDQ_RING_MASK(dbp->pdqdb_transmits)].txd_eop == 1); + PDQ_OS_DATABUF_DEQUEUE(&tx->tx_txq, pdu); + pdq_os_transmit_done(pdq, pdu); + tx->tx_free += descriptor_count; + + PDQ_ADVANCE(completion, descriptor_count, PDQ_RING_MASK(dbp->pdqdb_transmits)); + } + if (tx->tx_completion != completion) { + tx->tx_completion = completion; + pdq_os_restart_transmitter(pdq); + } + PDQ_DO_TYPE2_PRODUCER(pdq); +} + +void +pdq_flush_transmitter( + pdq_t *pdq) +{ + volatile pdq_consumer_block_t *cbp = pdq->pdq_cbp; + pdq_tx_info_t *tx = &pdq->pdq_tx_info; + + for (;;) { + PDQ_OS_DATABUF_T *pdu; + PDQ_OS_DATABUF_DEQUEUE(&tx->tx_txq, pdu); + if (pdu == NULL) + break; + /* + * Don't call transmit done since the packet never made it + * out on the wire. + */ + PDQ_OS_DATABUF_FREE(pdu); + } + + tx->tx_free = PDQ_RING_MASK(pdq->pdq_dbp->pdqdb_transmits); + tx->tx_completion = cbp->pdqcb_transmits = tx->tx_producer; + + PDQ_DO_TYPE2_PRODUCER(pdq); +} + +void +pdq_hwreset( + pdq_t *pdq) +{ + const pdq_csrs_t * const csrs = &pdq->pdq_csrs; + pdq_state_t state; + int cnt; + + state = PDQ_PSTS_ADAPTER_STATE(*csrs->csr_port_status); + if (state == PDQS_DMA_UNAVAILABLE) + return; + *csrs->csr_port_data_a = (state == PDQS_HALTED) ? 0 : PDQ_PRESET_SKIP_SELFTEST; + *csrs->csr_port_reset = 1; + PDQ_OS_USEC_DELAY(100); + *csrs->csr_port_reset = 0; + for (cnt = 45000;;cnt--) { + PDQ_OS_USEC_DELAY(1000); + state = PDQ_PSTS_ADAPTER_STATE(*csrs->csr_port_status); + if (state == PDQS_DMA_UNAVAILABLE || cnt == 0) + break; + } + PDQ_PRINTF(("PDQ Reset spun %d cycles\n", 45000 - cnt)); + PDQ_OS_USEC_DELAY(10000); + state = PDQ_PSTS_ADAPTER_STATE(*csrs->csr_port_status); + PDQ_ASSERT(state == PDQS_DMA_UNAVAILABLE); + PDQ_ASSERT(cnt > 0); +} + +/* + * The following routine brings the PDQ from whatever state it is + * in to DMA_UNAVAILABLE (ie. like a RESET but without doing a RESET). + */ +pdq_state_t +pdq_stop( + pdq_t *pdq) +{ + pdq_state_t state; + const pdq_csrs_t * const csrs = &pdq->pdq_csrs; + int cnt, pass = 0, idx; + PDQ_OS_DATABUF_T **buffers; + + restart: + state = PDQ_PSTS_ADAPTER_STATE(*csrs->csr_port_status); + if (state != PDQS_DMA_UNAVAILABLE) { + pdq_hwreset(pdq); + state = PDQ_PSTS_ADAPTER_STATE(*csrs->csr_port_status); + PDQ_ASSERT(state == PDQS_DMA_UNAVAILABLE); + } +#if 0 + switch (state) { + case PDQS_RING_MEMBER: + case PDQS_LINK_UNAVAILABLE: + case PDQS_LINK_AVAILABLE: { + *csrs->csr_port_data_a = PDQ_SUB_CMD_LINK_UNINIT; + *csrs->csr_port_data_b = 0; + pdq_do_port_control(csrs, PDQ_PCTL_SUB_CMD); + state = PDQ_PSTS_ADAPTER_STATE(*csrs->csr_port_status); + PDQ_ASSERT(state == PDQS_DMA_AVAILABLE); + /* FALL THROUGH */ + } + case PDQS_DMA_AVAILABLE: { + *csrs->csr_port_data_a = 0; + *csrs->csr_port_data_b = 0; + pdq_do_port_control(csrs, PDQ_PCTL_DMA_UNINIT); + state = PDQ_PSTS_ADAPTER_STATE(*csrs->csr_port_status); + PDQ_ASSERT(state == PDQS_DMA_UNAVAILABLE); + /* FALL THROUGH */ + } + case PDQS_DMA_UNAVAILABLE: { + break; + } + } +#endif + /* + * Now we should be in DMA_UNAVAILABLE. So bring the PDQ into + * DMA_AVAILABLE. + */ + + /* + * Obtain the hardware address and firmware revisions + * (MLA = my long address which is FDDI speak for hardware address) + */ + pdq_read_mla(&pdq->pdq_csrs, &pdq->pdq_hwaddr); + pdq_read_fwrev(&pdq->pdq_csrs, &pdq->pdq_fwrev); + pdq->pdq_chip_rev = pdq_read_chiprev(&pdq->pdq_csrs); + + if (pdq->pdq_type == PDQ_DEFPA) { + /* + * Disable interrupts and DMA. + */ + *pdq->pdq_pci_csrs.csr_pfi_mode_control = 0; + *pdq->pdq_pci_csrs.csr_pfi_status = 0x10; + } + + /* + * Flush all the databuf queues. + */ + pdq_flush_databuf_queue(&pdq->pdq_tx_info.tx_txq); + buffers = (PDQ_OS_DATABUF_T **) pdq->pdq_rx_info.rx_buffers; + for (idx = 0; idx < PDQ_RING_SIZE(pdq->pdq_dbp->pdqdb_receives); idx++) { + if (buffers[idx] != NULL) { + PDQ_OS_DATABUF_FREE(buffers[idx]); + buffers[idx] = NULL; + } + } + pdq->pdq_rx_info.rx_free = PDQ_RING_MASK(pdq->pdq_dbp->pdqdb_receives); + buffers = (PDQ_OS_DATABUF_T **) pdq->pdq_host_smt_info.rx_buffers; + for (idx = 0; idx < PDQ_RING_SIZE(pdq->pdq_dbp->pdqdb_host_smt); idx++) { + if (buffers[idx] != NULL) { + PDQ_OS_DATABUF_FREE(buffers[idx]); + buffers[idx] = NULL; + } + } + pdq->pdq_host_smt_info.rx_free = PDQ_RING_MASK(pdq->pdq_dbp->pdqdb_host_smt); + + /* + * Reset the consumer indexes to 0. + */ + pdq->pdq_cbp->pdqcb_receives = 0; + pdq->pdq_cbp->pdqcb_transmits = 0; + pdq->pdq_cbp->pdqcb_host_smt = 0; + pdq->pdq_cbp->pdqcb_unsolicited_event = 0; + pdq->pdq_cbp->pdqcb_command_response = 0; + pdq->pdq_cbp->pdqcb_command_request = 0; + + /* + * Reset the producer and completion indexes to 0. + */ + pdq->pdq_command_info.ci_request_producer = 0; + pdq->pdq_command_info.ci_response_producer = 0; + pdq->pdq_command_info.ci_request_completion = 0; + pdq->pdq_command_info.ci_response_completion = 0; + pdq->pdq_unsolicited_info.ui_producer = 0; + pdq->pdq_unsolicited_info.ui_completion = 0; + pdq->pdq_rx_info.rx_producer = 0; + pdq->pdq_rx_info.rx_completion = 0; + pdq->pdq_tx_info.tx_producer = 0; + pdq->pdq_tx_info.tx_completion = 0; + pdq->pdq_host_smt_info.rx_producer = 0; + pdq->pdq_host_smt_info.rx_completion = 0; + + pdq->pdq_command_info.ci_command_active = 0; + pdq->pdq_unsolicited_info.ui_free = PDQ_NUM_UNSOLICITED_EVENTS; + pdq->pdq_tx_info.tx_free = PDQ_RING_MASK(pdq->pdq_dbp->pdqdb_transmits); + + /* + * Allow the DEFPA to do DMA. Then program the physical + * addresses of the consumer and descriptor blocks. + */ + if (pdq->pdq_type == PDQ_DEFPA) { +#ifdef PDQTEST + *pdq->pdq_pci_csrs.csr_pfi_mode_control = PDQ_PFI_MODE_DMA_ENABLE; +#else + *pdq->pdq_pci_csrs.csr_pfi_mode_control = PDQ_PFI_MODE_DMA_ENABLE + |PDQ_PFI_MODE_PFI_PCI_INTR|PDQ_PFI_MODE_PDQ_PCI_INTR; +#endif + } + + /* + * Make the unsolicited queue has events ... + */ + pdq_process_unsolicited_events(pdq); + + *csrs->csr_port_data_b = PDQ_DMA_BURST_8LW; + *csrs->csr_port_data_a = PDQ_SUB_CMD_DMA_BURST_SIZE_SET; + pdq_do_port_control(csrs, PDQ_PCTL_SUB_CMD); + + *csrs->csr_port_data_b = 0; + *csrs->csr_port_data_a = PDQ_OS_VA_TO_PA(pdq->pdq_cbp); + pdq_do_port_control(csrs, PDQ_PCTL_CONSUMER_BLOCK); + + *csrs->csr_port_data_b = 0; + *csrs->csr_port_data_a = PDQ_OS_VA_TO_PA(pdq->pdq_dbp) | PDQ_DMA_INIT_LW_BSWAP_DATA; + pdq_do_port_control(csrs, PDQ_PCTL_DMA_INIT); + + for (cnt = 0; cnt < 1000; cnt++) { + state = PDQ_PSTS_ADAPTER_STATE(*csrs->csr_port_status); + if (state == PDQS_HALTED) { + if (pass > 0) + return PDQS_HALTED; + pass = 1; + goto restart; + } + if (state == PDQS_DMA_AVAILABLE) { + PDQ_PRINTF(("Transition to DMA Available took %d spins\n", cnt)); + break; + } + PDQ_OS_USEC_DELAY(1000); + } + PDQ_ASSERT(state == PDQS_DMA_AVAILABLE); + + *csrs->csr_host_int_type_0 = 0xFF; + *csrs->csr_host_int_enable = 0 /* PDQ_HOST_INT_STATE_CHANGE + |PDQ_HOST_INT_FATAL_ERROR|PDQ_HOST_INT_CMD_RSP_ENABLE + |PDQ_HOST_INT_UNSOL_ENABLE */; + + /* + * Any other command but START should be valid. + */ + pdq->pdq_command_info.ci_pending_commands &= ~(PDQ_BITMASK(PDQC_START)); + if (pdq->pdq_flags & PDQ_PRINTCHARS) + pdq->pdq_command_info.ci_pending_commands |= PDQ_BITMASK(PDQC_STATUS_CHARS_GET); + pdq_queue_commands(pdq); + + if (pdq->pdq_flags & PDQ_PRINTCHARS) { + /* + * Now wait (up to 100ms) for the command(s) to finish. + */ + for (cnt = 0; cnt < 1000; cnt++) { + pdq_process_command_responses(pdq); + if (pdq->pdq_command_info.ci_response_producer == pdq->pdq_command_info.ci_response_completion) + break; + PDQ_OS_USEC_DELAY(1000); + } + state = PDQ_PSTS_ADAPTER_STATE(*csrs->csr_port_status); + } + + return state; +} + +void +pdq_run( + pdq_t *pdq) +{ + const pdq_csrs_t * const csrs = &pdq->pdq_csrs; + pdq_state_t state; + + state = PDQ_PSTS_ADAPTER_STATE(*csrs->csr_port_status); + PDQ_ASSERT(state != PDQS_DMA_UNAVAILABLE); + PDQ_ASSERT(state != PDQS_RESET); + PDQ_ASSERT(state != PDQS_HALTED); + PDQ_ASSERT(state != PDQS_UPGRADE); + PDQ_ASSERT(state != PDQS_RING_MEMBER); + switch (state) { + case PDQS_DMA_AVAILABLE: { + /* + * The PDQ after being reset screws up some of its state. + * So we need to clear all the errors/interrupts so the real + * ones will get through. + */ + *csrs->csr_host_int_type_0 = 0xFF; + *csrs->csr_host_int_enable = PDQ_HOST_INT_STATE_CHANGE|PDQ_HOST_INT_XMT_DATA_FLUSH + |PDQ_HOST_INT_FATAL_ERROR|PDQ_HOST_INT_CMD_RSP_ENABLE|PDQ_HOST_INT_UNSOL_ENABLE + |PDQ_HOST_INT_RX_ENABLE|PDQ_HOST_INT_TX_ENABLE|PDQ_HOST_INT_HOST_SMT_ENABLE; + /* + * Set the MAC and address filters and start up the PDQ. + */ + pdq_process_unsolicited_events(pdq); + pdq_process_received_data(pdq, &pdq->pdq_rx_info, + pdq->pdq_dbp->pdqdb_receives, + pdq->pdq_cbp->pdqcb_receives, + PDQ_RING_MASK(pdq->pdq_dbp->pdqdb_receives)); + PDQ_DO_TYPE2_PRODUCER(pdq); + if (pdq->pdq_flags & PDQ_PASS_SMT) { + pdq_process_received_data(pdq, &pdq->pdq_host_smt_info, + pdq->pdq_dbp->pdqdb_host_smt, + pdq->pdq_cbp->pdqcb_host_smt, + PDQ_RING_MASK(pdq->pdq_dbp->pdqdb_host_smt)); + *csrs->csr_host_smt_producer = pdq->pdq_host_smt_info.rx_producer | (pdq->pdq_host_smt_info.rx_completion << 8); + } + pdq->pdq_command_info.ci_pending_commands = PDQ_BITMASK(PDQC_FILTER_SET) + | PDQ_BITMASK(PDQC_ADDR_FILTER_SET) | PDQ_BITMASK(PDQC_START); + if (pdq->pdq_flags & PDQ_PRINTCHARS) + pdq->pdq_command_info.ci_pending_commands |= PDQ_BITMASK(PDQC_STATUS_CHARS_GET); + pdq_queue_commands(pdq); + break; + } + case PDQS_LINK_UNAVAILABLE: + case PDQS_LINK_AVAILABLE: { + pdq->pdq_command_info.ci_pending_commands = PDQ_BITMASK(PDQC_FILTER_SET) + | PDQ_BITMASK(PDQC_ADDR_FILTER_SET); + if (pdq->pdq_flags & PDQ_PRINTCHARS) + pdq->pdq_command_info.ci_pending_commands |= PDQ_BITMASK(PDQC_STATUS_CHARS_GET); + if (pdq->pdq_flags & PDQ_PASS_SMT) { + pdq_process_received_data(pdq, &pdq->pdq_host_smt_info, + pdq->pdq_dbp->pdqdb_host_smt, + pdq->pdq_cbp->pdqcb_host_smt, + PDQ_RING_MASK(pdq->pdq_dbp->pdqdb_host_smt)); + *csrs->csr_host_smt_producer = pdq->pdq_host_smt_info.rx_producer | (pdq->pdq_host_smt_info.rx_completion << 8); + } + pdq_process_unsolicited_events(pdq); + pdq_queue_commands(pdq); + break; + } + case PDQS_RING_MEMBER: { + } + } +} + +int +pdq_interrupt( + pdq_t *pdq) +{ + const pdq_csrs_t * const csrs = &pdq->pdq_csrs; + pdq_uint32_t data; + int progress = 0; + + if (pdq->pdq_type == PDQ_DEFPA) + if (*pdq->pdq_pci_csrs.csr_pfi_status & 0x10) + *pdq->pdq_pci_csrs.csr_pfi_status = 0x10; + + while ((data = *csrs->csr_port_status) & PDQ_PSTS_INTR_PENDING) { + progress = 1; + PDQ_PRINTF(("PDQ Interrupt: Status = 0x%08x\n", data)); + if (data & PDQ_PSTS_RCV_DATA_PENDING) { + pdq_process_received_data(pdq, &pdq->pdq_rx_info, + pdq->pdq_dbp->pdqdb_receives, + pdq->pdq_cbp->pdqcb_receives, + PDQ_RING_MASK(pdq->pdq_dbp->pdqdb_receives)); + PDQ_DO_TYPE2_PRODUCER(pdq); + } + if (data & PDQ_PSTS_HOST_SMT_PENDING) { + pdq_process_received_data(pdq, &pdq->pdq_host_smt_info, + pdq->pdq_dbp->pdqdb_host_smt, + pdq->pdq_cbp->pdqcb_host_smt, + PDQ_RING_MASK(pdq->pdq_dbp->pdqdb_host_smt)); + *csrs->csr_host_smt_producer = pdq->pdq_host_smt_info.rx_producer | (pdq->pdq_host_smt_info.rx_completion << 8); + } + if (data & PDQ_PSTS_XMT_DATA_PENDING) + pdq_process_transmitted_data(pdq); + if (data & PDQ_PSTS_UNSOL_PENDING) + pdq_process_unsolicited_events(pdq); + if (data & PDQ_PSTS_CMD_RSP_PENDING) + pdq_process_command_responses(pdq); + if (data & PDQ_PSTS_TYPE_0_PENDING) { + data = *csrs->csr_host_int_type_0; + if (data & PDQ_HOST_INT_STATE_CHANGE) { + pdq_state_t state = PDQ_PSTS_ADAPTER_STATE(*csrs->csr_port_status); + printf(PDQ_OS_PREFIX "%s", PDQ_OS_PREFIX_ARGS, pdq_adapter_states[state]); + if (state == PDQS_LINK_UNAVAILABLE) { + pdq->pdq_flags &= ~PDQ_TXOK; + } else if (state == PDQS_LINK_AVAILABLE) { + pdq->pdq_flags |= PDQ_TXOK; + pdq_os_restart_transmitter(pdq); + } else if (state == PDQS_HALTED) { + pdq_response_error_log_get_t log_entry; + pdq_halt_code_t halt_code = PDQ_PSTS_HALT_ID(*csrs->csr_port_status); + printf(": halt code = %d (%s)\n", + halt_code, pdq_halt_codes[halt_code]); + if (halt_code == PDQH_DMA_ERROR) { + PDQ_PRINTF(("\tPFI status = 0x%x, Host 0 Fatal Interrupt = 0x%x\n", + *pdq->pdq_pci_csrs.csr_pfi_status, + data & PDQ_HOST_INT_FATAL_ERROR)); + } + pdq_read_error_log(pdq, &log_entry); + pdq_stop(pdq); + if (pdq->pdq_flags & PDQ_RUNNING) + pdq_run(pdq); + return 1; + } + printf("\n"); + *csrs->csr_host_int_type_0 = PDQ_HOST_INT_STATE_CHANGE; + } + if (data & PDQ_HOST_INT_FATAL_ERROR) { + pdq_stop(pdq); + if (pdq->pdq_flags & PDQ_RUNNING) + pdq_run(pdq); + return 1; + } + if (data & PDQ_HOST_INT_XMT_DATA_FLUSH) { + printf(PDQ_OS_PREFIX "Flushing transmit queue\n", PDQ_OS_PREFIX_ARGS); + pdq->pdq_flags &= ~PDQ_TXOK; + pdq_flush_transmitter(pdq); + pdq_do_port_control(csrs, PDQ_PCTL_XMT_DATA_FLUSH_DONE); + *csrs->csr_host_int_type_0 = PDQ_HOST_INT_XMT_DATA_FLUSH; + } + } + if (pdq->pdq_type == PDQ_DEFPA) + if (*pdq->pdq_pci_csrs.csr_pfi_status & 0x10) + *pdq->pdq_pci_csrs.csr_pfi_status = 0x10; + } + return progress; +} + +pdq_t * +pdq_initialize( + void *csr_va, + const char *name, + int unit, + void *ctx, + pdq_type_t type) +{ + pdq_t *pdq; + pdq_state_t state; + const pdq_uint32_t contig_bytes = (sizeof(pdq_descriptor_block_t) * 2) - PDQ_OS_PAGESIZE; + pdq_uint8_t *p; + int idx; + + PDQ_ASSERT(sizeof(pdq_descriptor_block_t) == 8192); + PDQ_ASSERT(sizeof(pdq_consumer_block_t) == 64); + PDQ_ASSERT(sizeof(pdq_response_filter_get_t) == PDQ_SIZE_RESPONSE_FILTER_GET); + PDQ_ASSERT(sizeof(pdq_cmd_addr_filter_set_t) == PDQ_SIZE_CMD_ADDR_FILTER_SET); + PDQ_ASSERT(sizeof(pdq_response_addr_filter_get_t) == PDQ_SIZE_RESPONSE_ADDR_FILTER_GET); + PDQ_ASSERT(sizeof(pdq_response_status_chars_get_t) == PDQ_SIZE_RESPONSE_STATUS_CHARS_GET); + PDQ_ASSERT(sizeof(pdq_response_fddi_mib_get_t) == PDQ_SIZE_RESPONSE_FDDI_MIB_GET); + PDQ_ASSERT(sizeof(pdq_response_dec_ext_mib_get_t) == PDQ_SIZE_RESPONSE_DEC_EXT_MIB_GET); + PDQ_ASSERT(sizeof(pdq_unsolicited_event_t) == 512); + + pdq = (pdq_t *) PDQ_OS_MEMALLOC(sizeof(pdq_t)); + if (pdq == NULL) { + PDQ_PRINTF(("malloc(%d) failed\n", sizeof(*pdq))); + return NULL; + } + PDQ_OS_MEMZERO(pdq, sizeof(pdq_t)); + pdq->pdq_type = type; + pdq->pdq_unit = unit; + pdq->pdq_os_ctx = (void *) ctx; + pdq->pdq_os_name = name; + pdq->pdq_flags = PDQ_PRINTCHARS; + /* + * Allocate the additional data structures required by + * the PDQ driver. Allocate a contiguous region of memory + * for the descriptor block. We need to allocated enough + * to guarantee that we will a get 8KB block of memory aligned + * on a 8KB boundary. This turns to require that we allocate + * (N*2 - 1 page) pages of memory. On machine with less than + * a 8KB page size, it mean we will allocate more memory than + * we need. The extra will be used for the unsolicited event + * buffers (though on machines with 8KB pages we will to allocate + * them separately since there will be nothing left overs.) + */ + p = (pdq_uint8_t *) PDQ_OS_MEMALLOC_CONTIG(contig_bytes); + if (p != NULL) { + pdq_physaddr_t physaddr = PDQ_OS_VA_TO_PA(p) & 0x1FFF; + if (physaddr) { + pdq->pdq_unsolicited_info.ui_events = (pdq_unsolicited_event_t *) p; + pdq->pdq_dbp = (pdq_descriptor_block_t *) &p[0x2000 - physaddr]; + } else { + pdq->pdq_dbp = (pdq_descriptor_block_t *) p; + pdq->pdq_unsolicited_info.ui_events = (pdq_unsolicited_event_t *) &p[0x2000]; + } + } + if (contig_bytes == sizeof(pdq_descriptor_block_t)) { + pdq->pdq_unsolicited_info.ui_events = + (pdq_unsolicited_event_t *) PDQ_OS_MEMALLOC( + PDQ_NUM_UNSOLICITED_EVENTS * sizeof(pdq_unsolicited_event_t)); + } + + /* + * Make sure everything got allocated. If not, free what did + * get allocated and return. + */ + if (pdq->pdq_dbp == NULL || pdq->pdq_unsolicited_info.ui_events == NULL) { + cleanup_and_return: + if (pdq->pdq_dbp != NULL) + PDQ_OS_MEMFREE_CONTIG(pdq->pdq_dbp, contig_bytes); + if (contig_bytes == sizeof(pdq_descriptor_block_t) && pdq->pdq_unsolicited_info.ui_events != NULL) + PDQ_OS_MEMFREE(pdq->pdq_unsolicited_info.ui_events, + PDQ_NUM_UNSOLICITED_EVENTS * sizeof(pdq_unsolicited_event_t)); + PDQ_OS_MEMFREE(pdq, sizeof(pdq_t)); + return NULL; + } + + pdq->pdq_cbp = (volatile pdq_consumer_block_t *) &pdq->pdq_dbp->pdqdb_consumer; + pdq->pdq_command_info.ci_bufstart = (pdq_uint8_t *) pdq->pdq_dbp->pdqdb_command_pool; + pdq->pdq_rx_info.rx_buffers = (void *) pdq->pdq_dbp->pdqdb_receive_buffers; + + pdq->pdq_host_smt_info.rx_buffers = (void *) pdq->pdq_dbp->pdqdb_host_smt_buffers; + + PDQ_PRINTF(("PDQ Descriptor Block = %x\n", pdq->pdq_dbp)); + PDQ_PRINTF((" Recieve Queue = %x\n", pdq->pdq_dbp->pdqdb_receives)); + PDQ_PRINTF((" Transmit Queue = %x\n", pdq->pdq_dbp->pdqdb_transmits)); + PDQ_PRINTF((" Host SMT Queue = %x\n", pdq->pdq_dbp->pdqdb_host_smt)); + PDQ_PRINTF((" Command Response Queue = %x\n", pdq->pdq_dbp->pdqdb_command_responses)); + PDQ_PRINTF((" Command Request Queue = %x\n", pdq->pdq_dbp->pdqdb_command_requests)); + PDQ_PRINTF(("PDQ Consumer Block = %x\n", pdq->pdq_cbp)); + + /* + * Zero out the descriptor block. Not really required but + * it pays to be neat. This will also zero out the consumer + * block, command pool, and buffer pointers for the receive + * host_smt rings. + */ + PDQ_OS_MEMZERO(pdq->pdq_dbp, sizeof(*pdq->pdq_dbp)); + + /* + * Initialize the CSR references. + */ + pdq_init_csrs(&pdq->pdq_csrs, csr_va, 1); + switch (pdq->pdq_type) { + case PDQ_DEFPA: pdq_init_pci_csrs(&pdq->pdq_pci_csrs, csr_va, 1); break; +#ifdef PDQ_DO_EISA + case PDQ_DEFEA: pdq_init_esia_csrs(&pdq->pdq_eisa_csrs, csr_va, 1); break; +#endif + } + + PDQ_PRINTF(("PDQ CSRs:\n")); + PDQ_PRINTF((" Port Reset = %x [0x%08x]\n", + pdq->pdq_csrs.csr_port_reset, *pdq->pdq_csrs.csr_port_reset)); + PDQ_PRINTF((" Host Data = %x [0x%08x]\n", + pdq->pdq_csrs.csr_host_data, *pdq->pdq_csrs.csr_host_data)); + PDQ_PRINTF((" Port Control = %x [0x%08x]\n", + pdq->pdq_csrs.csr_port_control, *pdq->pdq_csrs.csr_port_control)); + PDQ_PRINTF((" Port Data A = %x [0x%08x]\n", + pdq->pdq_csrs.csr_port_data_a, *pdq->pdq_csrs.csr_port_data_a)); + PDQ_PRINTF((" Port Data B = %x [0x%08x]\n", + pdq->pdq_csrs.csr_port_data_b, *pdq->pdq_csrs.csr_port_data_b)); + PDQ_PRINTF((" Port Status = %x [0x%08x]\n", + pdq->pdq_csrs.csr_port_status, *pdq->pdq_csrs.csr_port_status)); + PDQ_PRINTF((" Host Int Type 0 = %x [0x%08x]\n", + pdq->pdq_csrs.csr_host_int_type_0, *pdq->pdq_csrs.csr_host_int_type_0)); + PDQ_PRINTF((" Host Int Enable = %x [0x%08x]\n", + pdq->pdq_csrs.csr_host_int_enable, *pdq->pdq_csrs.csr_host_int_enable)); + PDQ_PRINTF((" Type 2 Producer = %x [0x%08x]\n", + pdq->pdq_csrs.csr_type_2_producer, *pdq->pdq_csrs.csr_type_2_producer)); + PDQ_PRINTF((" Command Response Producer = %x [0x%08x]\n", + pdq->pdq_csrs.csr_cmd_response_producer, *pdq->pdq_csrs.csr_cmd_response_producer)); + PDQ_PRINTF((" Command Request Producer = %x [0x%08x]\n", + pdq->pdq_csrs.csr_cmd_request_producer, *pdq->pdq_csrs.csr_cmd_request_producer)); + PDQ_PRINTF((" Host SMT Producer = %x [0x%08x]\n", + pdq->pdq_csrs.csr_host_smt_producer, *pdq->pdq_csrs.csr_host_smt_producer)); + PDQ_PRINTF((" Unsolicited Producer = %x [0x%08x]\n", + pdq->pdq_csrs.csr_unsolicited_producer, *pdq->pdq_csrs.csr_unsolicited_producer)); + + /* + * Initialize the command information block + */ + pdq->pdq_command_info.ci_pa_bufstart = PDQ_OS_VA_TO_PA(pdq->pdq_command_info.ci_bufstart); + for (idx = 0; idx < sizeof(pdq->pdq_dbp->pdqdb_command_requests)/sizeof(pdq->pdq_dbp->pdqdb_command_requests[0]); idx++) { + pdq_txdesc_t *txd = &pdq->pdq_dbp->pdqdb_command_requests[idx]; + + txd->txd_pa_lo = pdq->pdq_command_info.ci_pa_bufstart; + txd->txd_eop = txd->txd_sop = 1; + txd->txd_pa_hi = 0; + } + for (idx = 0; idx < sizeof(pdq->pdq_dbp->pdqdb_command_responses)/sizeof(pdq->pdq_dbp->pdqdb_command_responses[0]); idx++) { + pdq_rxdesc_t *rxd = &pdq->pdq_dbp->pdqdb_command_responses[idx]; + + rxd->rxd_pa_lo = pdq->pdq_command_info.ci_pa_bufstart; + rxd->rxd_sop = 1; + rxd->rxd_seg_cnt = 0; + rxd->rxd_seg_len_lo = 0; + } + + /* + * Initialize the unsolicited event information block + */ + pdq->pdq_unsolicited_info.ui_free = PDQ_NUM_UNSOLICITED_EVENTS; + pdq->pdq_unsolicited_info.ui_pa_bufstart = PDQ_OS_VA_TO_PA(pdq->pdq_unsolicited_info.ui_events); + for (idx = 0; idx < sizeof(pdq->pdq_dbp->pdqdb_unsolicited_events)/sizeof(pdq->pdq_dbp->pdqdb_unsolicited_events[0]); idx++) { + pdq_rxdesc_t *rxd = &pdq->pdq_dbp->pdqdb_unsolicited_events[idx]; + pdq_unsolicited_event_t *event = &pdq->pdq_unsolicited_info.ui_events[idx & (PDQ_NUM_UNSOLICITED_EVENTS-1)]; + + rxd->rxd_sop = 1; + rxd->rxd_seg_cnt = 0; + rxd->rxd_seg_len_hi = sizeof(pdq_unsolicited_event_t) / 16; + rxd->rxd_pa_lo = pdq->pdq_unsolicited_info.ui_pa_bufstart + (const pdq_uint8_t *) event + - (const pdq_uint8_t *) pdq->pdq_unsolicited_info.ui_events; + rxd->rxd_pa_hi = 0; + } + /* + * Initialize the receive information blocks (normal and SMT). + */ + pdq->pdq_rx_info.rx_free = PDQ_RING_MASK(pdq->pdq_dbp->pdqdb_receives); + pdq->pdq_rx_info.rx_target = pdq->pdq_rx_info.rx_free - PDQ_RX_SEGCNT * 8; + + pdq->pdq_host_smt_info.rx_free = PDQ_RING_MASK(pdq->pdq_dbp->pdqdb_host_smt); + pdq->pdq_host_smt_info.rx_target = pdq->pdq_host_smt_info.rx_free - PDQ_RX_SEGCNT * 3; + + /* + * Initialize the transmit information block. + */ + pdq->pdq_tx_hdr[0] = PDQ_FDDI_PH0; + pdq->pdq_tx_hdr[1] = PDQ_FDDI_PH1; + pdq->pdq_tx_hdr[2] = PDQ_FDDI_PH2; + pdq->pdq_tx_info.tx_free = PDQ_RING_MASK(pdq->pdq_dbp->pdqdb_transmits); + pdq->pdq_tx_info.tx_hdrdesc.txd_seg_len = sizeof(pdq->pdq_tx_hdr); + pdq->pdq_tx_info.tx_hdrdesc.txd_sop = 1; + pdq->pdq_tx_info.tx_hdrdesc.txd_pa_lo = PDQ_OS_VA_TO_PA(pdq->pdq_tx_hdr); + + state = PDQ_PSTS_ADAPTER_STATE(*pdq->pdq_csrs.csr_port_status); + PDQ_PRINTF(("PDQ Adapter State = %s\n", pdq_adapter_states[state])); + + /* + * Stop the PDQ if it is running and put it into a known state. + */ + state = pdq_stop(pdq); + + /* state = PDQ_PSTS_ADAPTER_STATE(*pdq->pdq_csrs.csr_port_status); */ + PDQ_PRINTF(("PDQ Adapter State = %s\n", pdq_adapter_states[state])); + PDQ_ASSERT(state == PDQS_DMA_AVAILABLE); + /* + * If the adapter is not the state we expect, then the initialization + * failed. Cleanup and exit. + */ + if (state == PDQS_RESET || state == PDQS_HALTED || state == PDQS_UPGRADE) + goto cleanup_and_return; + + PDQ_PRINTF(("PDQ Hardware Address = %02x-%02x-%02x-%02x-%02x-%02x\n", + pdq->pdq_hwaddr.lanaddr_bytes[0], pdq->pdq_hwaddr.lanaddr_bytes[1], + pdq->pdq_hwaddr.lanaddr_bytes[2], pdq->pdq_hwaddr.lanaddr_bytes[3], + pdq->pdq_hwaddr.lanaddr_bytes[4], pdq->pdq_hwaddr.lanaddr_bytes[5])); + PDQ_PRINTF(("PDQ Firmware Revision = %c%c%c%c\n", + pdq->pdq_fwrev.fwrev_bytes[0], pdq->pdq_fwrev.fwrev_bytes[1], + pdq->pdq_fwrev.fwrev_bytes[2], pdq->pdq_fwrev.fwrev_bytes[3])); + PDQ_PRINTF(("PDQ Chip Revision = ")); + switch (pdq->pdq_chip_rev) { + case PDQ_CHIP_REV_A_B_OR_C: PDQ_PRINTF(("Rev C or below")); break; + case PDQ_CHIP_REV_D: PDQ_PRINTF(("Rev D")); break; + case PDQ_CHIP_REV_E: PDQ_PRINTF(("Rev E")); break; + default: PDQ_PRINTF(("Unknown Rev %d", (int) pdq->pdq_chip_rev)); + } + PDQ_PRINTF(("\n")); + + return pdq; +} |