/* $NetBSD: eshconfig.c,v 1.10 2011/08/30 18:28:59 joerg Exp $ */ /* * Copyright (c) 1998 The NetBSD Foundation, Inc. * All rights reserved. * * This code contributed to The NetBSD Foundation by Kevin M. Lahey * of the Numerical Aerospace Simulation Facility, NASA Ames Research * Center. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``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 FOUNDATION OR CONTRIBUTORS * 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. */ #include #ifndef lint __RCSID("$NetBSD: eshconfig.c,v 1.10 2011/08/30 18:28:59 joerg Exp $"); #endif /* not lint */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * Create a simple pair of tables to map possible burst DMA values * to the values required by the RoadRunner. */ struct map_dma { int value; u_int32_t rr_value; }; static struct map_dma read_dma_map[] = {{0, RR_PS_READ_DISABLE}, {4, RR_PS_READ_4}, {16, RR_PS_READ_16}, {32, RR_PS_READ_32}, {64, RR_PS_READ_64}, {128, RR_PS_READ_128}, {256, RR_PS_READ_256}, {1024, RR_PS_READ_1024}, {-1, 0}}; static struct map_dma write_dma_map[] = {{0, RR_PS_WRITE_DISABLE}, {4, RR_PS_WRITE_4}, {16, RR_PS_WRITE_16}, {32, RR_PS_WRITE_32}, {64, RR_PS_WRITE_64}, {128, RR_PS_WRITE_128}, {256, RR_PS_WRITE_256}, {1024, RR_PS_WRITE_1024}, {-1, 0}}; /* * The RunCode is composed of separate segments, each of which has a * starting address in SRAM memory (for running) and in EEPROM * (for storage). */ struct rr_seg_descr { u_int32_t start_addr; u_int32_t length; u_int32_t ee_addr; }; static u_int32_t do_map(int, struct map_dma *); static void eeprom_upload(const char *); static void eeprom_download(const char *); static u_int32_t rr_checksum(const u_int32_t *, int); static void esh_tune(void); static void esh_tune_eeprom(void); static void esh_tuning_stats(void); static void esh_stats(int); static void esh_reset(void); static int drvspec_ioctl(char *, int, int, int, caddr_t); __dead static void usage(void); static char name[30] = "esh0"; static int s; #define RR_EE_SIZE 8192 static u_int32_t eeprom[RR_EE_SIZE]; static u_int32_t runcode[RR_EE_SIZE]; static struct ifdrv ifd; /* drvspec_ioctl * * We defined a driver-specific socket ioctl to allow us to tweak * the characteristics of network devices. This routine will * provide a shortcut to calling this routine, which would otherwise * require lots of costly and annoying setup. */ static int drvspec_ioctl(char *lname, int fd, int cmd, int len, caddr_t data) { strcpy(ifd.ifd_name, lname); ifd.ifd_cmd = cmd; ifd.ifd_len = len; ifd.ifd_data = data; return ioctl(fd, SIOCSDRVSPEC, (caddr_t) &ifd); } static void usage(void) { fprintf(stderr, "eshconfig -- configure Essential Communications " "HIPPI driver\n"); fprintf(stderr, "-b burst size for read\n"); fprintf(stderr, "-c burst size for write:\n"); fprintf(stderr, "\t0 (no limit), 5, 16, 32, 64, 128, 256, 1024\n"); fprintf(stderr, "-d download filename\n"); fprintf(stderr, "-e write data to EEPROM\n"); fprintf(stderr, "-m minimum bytes DMA per direction\n"); fprintf(stderr, "-r bytes before DMA starts for read\n"); fprintf(stderr, "-s show statistics (-ss to display only non-zero)\n"); fprintf(stderr, "-t show tuning parameters\n"); fprintf(stderr, "-u upload filename [not working]\n"); fprintf(stderr, "-w bytes before DMA starts for write\n"); fprintf(stderr, "-i interrupt delay in usecs\n"); fprintf(stderr, "-x reset interface\n"); exit(1); } /* do_map * * Map between values for burst DMA sizes and the values expected by * the RoadRunner chip. */ static u_int32_t do_map(int value, struct map_dma *map) { int i; for (i = 0; map[i].value != -1; i++) if (value == map[i].value) return map[i].rr_value; return -1; } /* do_map_dma * * Reverse the mapping. */ static int do_map_dma(uint32_t value, struct map_dma *map) { int i; for (i = 0; map[i].value != -1; i++) if (value == map[i].rr_value) return map[i].value; return 0; } static int dma_thresh_read = -1; static int dma_thresh_write = -1; static int dma_min_grab = -1; static int dma_max_read = -1; static int dma_max_write = -1; static int interrupt_delay = -1; static int get_stats = 0; static int get_tuning_stats = 0; static int eeprom_write = 0; static char *eeprom_download_filename = NULL; static char *eeprom_upload_filename = NULL; static int reset = 0; static struct rr_tuning rr_tune; struct rr_eeprom rr_eeprom; struct rr_stats rr_stats; int main(int argc, char *argv[]) { int ch; /* Parse command-line options */ while ((ch = getopt(argc, argv, "b:c:d:ei:m:r:stu:w:x")) != -1) { switch (ch) { case 'b': dma_max_read = atoi(optarg); break; case 'c': dma_max_write = atoi(optarg); break; case 'd': eeprom_download_filename = optarg; break; case 'e': eeprom_write++; break; case 'i': interrupt_delay = atoi(optarg); break; case 'm': dma_min_grab = atoi(optarg); break; case 'r': dma_thresh_read = atoi(optarg); break; case 's': get_stats++; break; case 't': get_tuning_stats++; break; case 'u': eeprom_upload_filename = optarg; break; case 'w': dma_thresh_write = atoi(optarg); break; case 'x': reset = 1; break; default: usage(); /* NOTREACHED */ } } argc -= optind; argv += optind; if (argc > 1) usage(); if (argc == 1) { (void) strncpy(name, argv[0], sizeof(name)); argc--; argv++; } s = socket(AF_INET, SOCK_DGRAM, 0); if (s < 0) err(1, "socket"); if (eeprom_upload_filename) eeprom_upload(eeprom_upload_filename); if (eeprom_download_filename) eeprom_download(eeprom_download_filename); if (get_stats) { esh_stats(get_stats); } if (drvspec_ioctl(name, s, EIOCGTUNE, sizeof(struct rr_tuning), (caddr_t) &rr_tune) < 0) { err(1, "ioctl(EIOCGTUNE)"); } if (get_tuning_stats) { if (get_stats) printf("\n"); esh_tuning_stats(); } if (eeprom_write || dma_thresh_read != -1 || dma_thresh_write != -1 || dma_min_grab != -1 || dma_max_read != -1 || dma_max_write != -1 || interrupt_delay != -1) { esh_tune(); } if (eeprom_write) esh_tune_eeprom(); if (reset) esh_reset(); exit(0); } static void esh_tune(void) { dma_max_read = do_map(dma_max_read, read_dma_map); if (dma_max_read != -1) { rr_tune.rt_pci_state &= ~RR_PS_READ_MASK; rr_tune.rt_pci_state |= dma_max_read; } dma_max_write = do_map(dma_max_write, write_dma_map); if (dma_max_write != -1) { rr_tune.rt_pci_state &= ~RR_PS_WRITE_MASK; rr_tune.rt_pci_state |= dma_max_write; } if (dma_min_grab != -1) { if ((dma_min_grab & (RR_PS_MIN_DMA_MASK >> RR_PS_MIN_DMA_SHIFT)) != dma_min_grab) usage(); rr_tune.rt_pci_state &= ~RR_PS_MIN_DMA_MASK; rr_tune.rt_pci_state |= (dma_min_grab << RR_PS_MIN_DMA_SHIFT); } if (dma_thresh_write != -1) { if (dma_thresh_write < 1 || dma_thresh_write > RR_DW_THRESHOLD_MAX) usage(); rr_tune.rt_dma_write_state &= ~RR_DW_THRESHOLD_MASK; rr_tune.rt_dma_write_state |= dma_thresh_write << RR_DW_THRESHOLD_SHIFT; } if (dma_thresh_read != -1) { if (dma_thresh_read < 1 || dma_thresh_read > RR_DR_THRESHOLD_MAX) usage(); rr_tune.rt_dma_read_state &= ~RR_DR_THRESHOLD_MASK; rr_tune.rt_dma_read_state |= dma_thresh_read << RR_DR_THRESHOLD_SHIFT; } rr_tune.rt_stats_timer = ESH_STATS_TIMER_DEFAULT; if (interrupt_delay != -1) rr_tune.rt_interrupt_timer = interrupt_delay; if (drvspec_ioctl(name, s, EIOCSTUNE, sizeof(struct rr_tuning), (caddr_t) &rr_tune) < 0) err(1, "EIOCSTUNE"); } /* esh_tune_eeprom * * Store the current tuning data into the eeprom. */ static void esh_tune_eeprom(void) { #define LAST (RR_EE_HEADER_CHECKSUM / RR_EE_WORD_LEN) #define FIRST (RR_EE_HEADER_CHECKSUM / RR_EE_WORD_LEN) u_int32_t tuning_data[LAST + 1]; rr_eeprom.ifr_buffer = tuning_data; rr_eeprom.ifr_length = sizeof(tuning_data); rr_eeprom.ifr_offset = 0; if (drvspec_ioctl(name, s, EIOCGEEPROM, sizeof(struct rr_eeprom), (caddr_t) &rr_eeprom) == -1) err(6, "ioctl to retrieve tuning information from EEPROM"); tuning_data[RR_EE_PCI_STATE / RR_EE_WORD_LEN] = rr_tune.rt_pci_state; tuning_data[RR_EE_DMA_WRITE_STATE / RR_EE_WORD_LEN] = rr_tune.rt_dma_write_state; tuning_data[RR_EE_DMA_READ_STATE / RR_EE_WORD_LEN] = rr_tune.rt_dma_read_state; tuning_data[RR_EE_INTERRUPT_TIMER / RR_EE_WORD_LEN] = rr_tune.rt_interrupt_timer; tuning_data[RR_EE_STATS_TIMER / RR_EE_WORD_LEN] = ESH_STATS_TIMER_DEFAULT; tuning_data[RR_EE_HEADER_CHECKSUM / RR_EE_WORD_LEN] = rr_checksum(&tuning_data[FIRST], LAST - FIRST); rr_eeprom.ifr_buffer = tuning_data; rr_eeprom.ifr_length = sizeof(tuning_data); rr_eeprom.ifr_offset = 0; if (drvspec_ioctl(name, s, EIOCSEEPROM, sizeof(struct rr_eeprom), (caddr_t) &rr_eeprom) == -1) err(7, "ioctl to set tuning information from EEPROM"); } /* eeprom_upload * * Upload the EEPROM from the card and store in the data file. */ static void eeprom_upload(const char *filename) { int fd; bzero(eeprom, sizeof(eeprom)); if ((fd = open(filename, O_WRONLY | O_CREAT, 0644)) < 0) err(4, "Couldn't open %s for output", filename); rr_eeprom.ifr_buffer = eeprom; rr_eeprom.ifr_length = sizeof(eeprom); rr_eeprom.ifr_offset = 0; if (drvspec_ioctl(name, s, EIOCGEEPROM, sizeof(struct rr_eeprom), (caddr_t) &rr_eeprom) == -1) err(5, "ioctl to retrieve all of EEPROM"); write(fd, eeprom, sizeof(eeprom)); close(fd); } /* eeprom_download * * Download into eeprom the contents of a file. The file is made up * of ASCII text; the first three characters can be ignored, the next * four hex characters define an address, the next two characters can * be ignored, and the final eight hex characters are the data. */ static void eeprom_download(const char *filename) { FILE *fp; struct rr_seg_descr *segd = NULL, *nsegd; char id[BUFSIZ]; char pad[BUFSIZ]; char buffer[BUFSIZ]; u_int32_t address = 0; u_int32_t last_address = 0; u_int32_t value; u_int32_t length = 0; int segment_start = 0; int seg_table_start; int seg_count_offset; int phase2_start; int phase2_checksum; int in_segment = 0; int segment = 0; int eof = 0; int line = 0; int zero_count = 0; int i; /* Clear out eeprom storage space, then read in the value on the card */ bzero(eeprom, sizeof(eeprom)); bzero(runcode, sizeof(runcode)); rr_eeprom.ifr_buffer = eeprom; rr_eeprom.ifr_length = sizeof(eeprom); rr_eeprom.ifr_offset = 0; if (drvspec_ioctl(name, s, EIOCGEEPROM, sizeof(struct rr_eeprom), (caddr_t) &rr_eeprom) == -1) err(5, "ioctl to retrieve EEPROM"); /* * Open the input file and proceed to read the data file, storing * the data and counting the number of segments. */ if ((fp = fopen(filename, "r")) == NULL) err(2, "fopen"); do { if (fgets(buffer, sizeof(buffer), fp) == NULL) errx(3, "premature, unmarked end of file, line %d", line); line++; if (!strncmp(buffer + 7, "01", 2)) { /* check for EOF marker... */ eof = 1; } else { sscanf(buffer, "%3s%4x%2s%8x%2s", id, &address, pad, &value, pad); if (strcmp(id, ":04") != 0) errx(3, "bad initial id on line %d", line); } /* * Check to see if we terminated a segment; this happens * when we are at end of file, or we hit a non-sequential * address value, or we see three or more zeroes in a row. */ if ((length == RR_EE_SEG_SIZE || eof || zero_count >= 3 || (last_address && last_address != address - 1)) && in_segment) { length -= zero_count; segment_start += length; segd[segment].length = length; printf("segment %d, %d words\n", segment, length); last_address = in_segment = zero_count = length = 0; segment++; } if (eof) break; /* Skip zero values starting a segment */ if (!in_segment && value == 0) continue; last_address = address; /* * If we haven't started a segment yet, do so now. * Store away the address at which this code should be placed * in memory and the address of the code in the EEPROM. */ if (!in_segment) { in_segment = 1; nsegd = realloc(segd, sizeof(struct rr_seg_descr) * (segment + 1)); if (nsegd == NULL) err(6, "couldn't realloc segment descriptor space"); segd = nsegd; segd[segment].start_addr = address * sizeof(u_int32_t); segd[segment].ee_addr = segment_start; } /* Keep track of consecutive zeroes */ if (in_segment && value == 0) zero_count++; else zero_count = 0; /* Store away the actual data */ runcode[segment_start + length++] = value; } while (!eof); fclose(fp); /* Now that we have a segment count, fill in the EEPROM image. */ seg_count_offset = eeprom[RR_EE_RUNCODE_SEGMENTS / RR_EE_WORD_LEN]; seg_count_offset = (seg_count_offset - RR_EE_OFFSET) / RR_EE_WORD_LEN; seg_table_start = seg_count_offset + 1; phase2_checksum = seg_table_start + 3 * segment; phase2_start = eeprom[RR_EE_PHASE2_EE_START / RR_EE_WORD_LEN]; phase2_start = (phase2_start - RR_EE_OFFSET) / RR_EE_WORD_LEN; printf("segment table start = %x, segments = %d\n", seg_table_start, eeprom[seg_count_offset]); /* We'll fill in anything after the segment count, so clear it */ bzero(eeprom + seg_count_offset, sizeof(eeprom) - seg_count_offset * sizeof(eeprom[0])); eeprom[seg_count_offset] = segment; for (i = 0; i < segment; i++) segd[i].ee_addr = RR_EE_OFFSET + (segd[i].ee_addr + phase2_checksum + 1) * RR_EE_WORD_LEN; bcopy(segd, &eeprom[seg_table_start], sizeof(struct rr_seg_descr) * segment); bcopy(runcode, &eeprom[phase2_checksum + 1], segment_start * sizeof(u_int32_t)); eeprom[phase2_checksum] = rr_checksum(&eeprom[phase2_start], phase2_checksum - phase2_start); eeprom[segment_start + phase2_checksum + 1] = rr_checksum(&eeprom[phase2_checksum + 1], segment_start); printf("phase2 checksum %x, runcode checksum %x\n", eeprom[phase2_checksum], eeprom[segment_start + phase2_checksum + 1]); rr_eeprom.ifr_buffer = eeprom; rr_eeprom.ifr_length = sizeof(eeprom); rr_eeprom.ifr_offset = 0; if (drvspec_ioctl(name, s, EIOCSEEPROM, sizeof(struct rr_eeprom), (caddr_t) &rr_eeprom) == -1) err(5, "ioctl to retrieve EEPROM"); } /* rr_checksum * * Perform checksum on RunCode. Length is in words. Ugh. */ static u_int32_t rr_checksum(const u_int32_t *data, int length) { u_int32_t checksum = 0; while (length--) checksum += *data++; checksum = 0 - checksum; return checksum; } static struct stats_values { int offset; const char *name; } stats_values[] = { {0x04, "receive rings created"}, {0x08, "receive rings deleted"}, {0x0c, "interrupts"}, {0x10, "event overflows"}, {0x14, "invalid commands"}, {0x18, "DMA read errors"}, {0x1c, "DMA write errors"}, {0x20, "stats updates per timer"}, {0x24, "stats updates per host"}, {0x28, "watchdog"}, {0x2c, "trace"}, {0x30, "link ready sync established"}, {0x34, "GLink errors"}, {0x38, "alternating flag errors"}, {0x3c, "overhead bit 8 synchronized"}, {0x40, "remote serial parity errors"}, {0x44, "remote parallel parity errors"}, {0x48, "remote loopback requested"}, {0x50, "transmit connections established"}, {0x54, "transmit connections rejected"}, {0x58, "transmit connections retried"}, {0x5c, "transmit connections timed out"}, {0x60, "transmit connections disconnected"}, {0x64, "transmit parity errors"}, {0x68, "packets sent"}, {0x74, "short first burst sent"}, {0x80, "transmit data not moving"}, {0x90, "receive connections accepted"}, {0x94, "receive connections rejected -- bad parity"}, {0x98, "receive connections rejected -- 64-bit width"}, {0x9c, "receive connections rejected -- buffers low"}, {0xa0, "receive connections disconnected"}, {0xa4, "receive connections with no data"}, {0xa8, "packets received"}, {0xb4, "short first burst received"}, {0xc0, "receive parity error"}, {0xc4, "receive LLRC error"}, {0xc8, "receive burst size error"}, {0xcc, "receive state error"}, {0xd0, "receive ready ULP"}, {0xd4, "receive invalid ULP"}, {0xd8, "receive packets flow control due to buffer space"}, {0xdc, "receive packets flow control due to descriptors"}, {0xe0, "receive ring fulls"}, {0xe4, "packet length errors"}, {0xe8, "packets with checksum error"}, {0xec, "packets dropped"}, {0xf0, "ring low on space"}, {0xf4, "data in ring at close"}, {0xf8, "receives to ring not moving data"}, {0xfc, "receiver idles"}, {0, 0}, }; static void esh_reset(void) { if (drvspec_ioctl(name, s, EIOCRESET, 0, 0) < 0) err(1, "ioctl(EIOCRESET)"); } static void esh_stats(int lget_stats) { u_int32_t *stats; long long value; int offset; if (drvspec_ioctl(name, s, EIOCGSTATS, sizeof(struct rr_stats), (caddr_t) &rr_stats) < 0) err(1, "ioctl(EIOCGTUNE)"); stats = rr_stats.rs_stats; value = (((long long) stats[0x78 / 4]) << 32) | stats[0x7c / 4]; if (lget_stats == 1 || value > 0) printf("%12lld bytes sent\n", value); value = ((long long) stats[0xb8 / 4] << 32) | stats[0xbc / 4]; if (lget_stats == 1 || value > 0) printf("%12lld bytes received\n", value); for (offset = 0; stats_values[offset].offset != 0; offset++) { if (lget_stats == 1 || stats[stats_values[offset].offset / 4] > 0) printf("%12d %s\n", stats[stats_values[offset].offset / 4], stats_values[offset].name); } } static void esh_tuning_stats(void) { printf("rt_mode_and_status = %x\n", rr_tune.rt_mode_and_status); printf("rt_conn_retry_count = %x\n", rr_tune.rt_conn_retry_count); printf("rt_conn_retry_timer = %x\n", rr_tune.rt_conn_retry_timer); printf("rt_conn_timeout = %x\n", rr_tune.rt_conn_timeout); printf("rt_stats_timer = %x\n", rr_tune.rt_stats_timer); printf("rt_interrupt_timer = %x\n", rr_tune.rt_interrupt_timer); printf("rt_tx_timeout = %x\n", rr_tune.rt_tx_timeout); printf("rt_rx_timeout = %x\n", rr_tune.rt_rx_timeout); printf("rt_pci_state = %x" " min DMA %x read max %x write max %x\n", rr_tune.rt_pci_state, (rr_tune.rt_pci_state & RR_PS_MIN_DMA_MASK) >> RR_PS_MIN_DMA_SHIFT, do_map_dma(rr_tune.rt_pci_state & RR_PS_READ_MASK, read_dma_map), do_map_dma(rr_tune.rt_pci_state & RR_PS_WRITE_MASK, write_dma_map)); printf("rt_dma_write_state = %x\n", rr_tune.rt_dma_write_state); printf("rt_dma_read_state = %x\n", rr_tune.rt_dma_read_state); printf("rt_driver_param = %x\n", rr_tune.rt_driver_param); }